summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2012-10-05 09:36:26 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-10-05 09:36:26 -0300
commitbd0d10498826ed150da5e4c45baf8b9c7088fb71 (patch)
treecdee4371121a355d627a655c4eef5c0047b0462a
parent89de0f2cda8b784e51ebd6655fff7339e4ac552b (diff)
parent2425bb3d4016ed95ce83a90b53bd92c7f31091e4 (diff)
downloadlinux-bd0d10498826ed150da5e4c45baf8b9c7088fb71.tar.gz
linux-bd0d10498826ed150da5e4c45baf8b9c7088fb71.tar.bz2
linux-bd0d10498826ed150da5e4c45baf8b9c7088fb71.zip
Merge branch 'staging/for_v3.7' into v4l_for_linus
* staging/for_v3.7: (2891 commits) em28xx: regression fix: use DRX-K sync firmware requests on em28xx drxk: allow loading firmware synchrousnously em28xx: Make all em28xx extensions to be initialized asynchronously [media] tda18271: properly report read errors in tda18271_get_id [media] tda18271: delay IR & RF calibration until init() if delay_cal is set [media] MAINTAINERS: add Michael Krufky as tda827x maintainer [media] MAINTAINERS: add Michael Krufky as tda8290 maintainer [media] MAINTAINERS: add Michael Krufky as cxusb maintainer [media] MAINTAINERS: add Michael Krufky as lg2160 maintainer [media] MAINTAINERS: add Michael Krufky as lgdt3305 maintainer [media] MAINTAINERS: add Michael Krufky as mxl111sf maintainer [media] MAINTAINERS: add Michael Krufky as mxl5007t maintainer [media] MAINTAINERS: add Michael Krufky as tda18271 maintainer [media] s5p-tv: Report only multi-plane capabilities in vidioc_querycap [media] s5p-mfc: Fix misplaced return statement in s5p_mfc_suspend() [media] exynos-gsc: Add missing static storage class specifiers [media] exynos-gsc: Remove <linux/version.h> header file inclusion [media] s5p-fimc: Fix incorrect condition in fimc_lite_reqbufs() [media] s5p-tv: Fix potential NULL pointer dereference error [media] s5k6aa: Fix possible NULL pointer dereference ... Conflicts: drivers/media/platform/s5p-fimc/fimc-capture.c drivers/media/platform/s5p-fimc/fimc-lite.c
-rw-r--r--Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads5
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci12
-rw-r--r--Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb44
-rw-r--r--Documentation/ABI/testing/sysfs-platform-ideapad-laptop11
-rw-r--r--Documentation/DocBook/filesystems.tmpl4
-rw-r--r--Documentation/DocBook/media/Makefile2
-rw-r--r--Documentation/DocBook/media/dvb/audio.xml113
-rw-r--r--Documentation/DocBook/media/dvb/ca.xml353
-rw-r--r--Documentation/DocBook/media/dvb/demux.xml230
-rw-r--r--Documentation/DocBook/media/dvb/dvbapi.xml4
-rw-r--r--Documentation/DocBook/media/dvb/dvbproperty.xml113
-rw-r--r--Documentation/DocBook/media/dvb/frontend.xml71
-rw-r--r--Documentation/DocBook/media/dvb/intro.xml2
-rw-r--r--Documentation/DocBook/media/dvb/kdapi.xml2
-rw-r--r--Documentation/DocBook/media/dvb/net.xml127
-rw-r--r--Documentation/DocBook/media/dvb/video.xml333
-rw-r--r--Documentation/DocBook/media/v4l/biblio.xml52
-rw-r--r--Documentation/DocBook/media/v4l/common.xml30
-rw-r--r--Documentation/DocBook/media/v4l/compat.xml41
-rw-r--r--Documentation/DocBook/media/v4l/controls.xml614
-rw-r--r--Documentation/DocBook/media/v4l/dev-osd.xml7
-rw-r--r--Documentation/DocBook/media/v4l/dev-rds.xml2
-rw-r--r--Documentation/DocBook/media/v4l/dev-subdev.xml20
-rw-r--r--Documentation/DocBook/media/v4l/gen-errors.xml19
-rw-r--r--Documentation/DocBook/media/v4l/io.xml21
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml3
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt-yvu420m.xml154
-rw-r--r--Documentation/DocBook/media/v4l/pixfmt.xml1
-rw-r--r--Documentation/DocBook/media/v4l/selection-api.xml22
-rw-r--r--Documentation/DocBook/media/v4l/v4l2.xml15
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-cropcap.xml12
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml9
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enuminput.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enumoutput.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-enumstd.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-crop.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml9
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml13
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-fmt.xml13
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-parm.xml4
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-selection.xml9
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-std.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-g-tuner.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-qbuf.xml2
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml9
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml6
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-querycap.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-querystd.xml8
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-reqbufs.xml5
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml10
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-streamon.xml7
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml152
-rw-r--r--Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml8
-rw-r--r--Documentation/DocBook/media_api.tmpl9
-rw-r--r--Documentation/IRQ-domain.txt5
-rw-r--r--Documentation/block/00-INDEX10
-rw-r--r--Documentation/block/cfq-iosched.txt77
-rw-r--r--Documentation/block/queue-sysfs.txt71
-rw-r--r--Documentation/cgroups/hugetlb.txt45
-rw-r--r--Documentation/cgroups/memory.txt12
-rw-r--r--Documentation/device-mapper/dm-raid.txt26
-rw-r--r--Documentation/devicetree/bindings/arm/mrvl/intc.txt20
-rw-r--r--Documentation/devicetree/bindings/ata/marvell.txt16
-rw-r--r--Documentation/devicetree/bindings/gpio/mrvl-gpio.txt23
-rw-r--r--Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt8
-rw-r--r--Documentation/devicetree/bindings/regulator/tps6586x.txt12
-rw-r--r--Documentation/devicetree/bindings/watchdog/marvel.txt14
-rw-r--r--Documentation/dvb/README.dvb-usb2
-rwxr-xr-xDocumentation/dvb/get_dvb_firmware2
-rw-r--r--Documentation/feature-removal-schedule.txt53
-rw-r--r--Documentation/filesystems/Locking21
-rw-r--r--Documentation/filesystems/porting5
-rw-r--r--Documentation/filesystems/vfat.txt11
-rw-r--r--Documentation/filesystems/vfs.txt16
-rw-r--r--Documentation/i2c/busses/i2c-i8011
-rw-r--r--Documentation/ioctl/ioctl-number.txt6
-rw-r--r--Documentation/laptops/laptop-mode.txt12
-rw-r--r--Documentation/networking/netconsole.txt19
-rw-r--r--Documentation/pinctrl.txt6
-rw-r--r--Documentation/security/Yama.txt14
-rw-r--r--Documentation/sound/alsa/HD-Audio-Models.txt5
-rw-r--r--Documentation/sysctl/fs.txt42
-rw-r--r--Documentation/sysctl/vm.txt44
-rw-r--r--Documentation/vfio.txt314
-rw-r--r--Documentation/video4linux/CARDLIST.cx238851
-rw-r--r--Documentation/video4linux/CQcam.txt2
-rw-r--r--Documentation/video4linux/README.davinci-vpbe20
-rw-r--r--Documentation/video4linux/fimc.txt16
-rw-r--r--Documentation/video4linux/omap3isp.txt2
-rw-r--r--Documentation/video4linux/v4l2-controls.txt6
-rw-r--r--Documentation/video4linux/v4l2-framework.txt12
-rw-r--r--Documentation/video4linux/videobuf2
-rw-r--r--Documentation/vm/hugetlbpage.txt10
-rw-r--r--Documentation/w1/slaves/w1_therm2
-rw-r--r--Documentation/watchdog/src/watchdog-test.c2
-rw-r--r--MAINTAINERS501
-rw-r--r--Makefile4
-rw-r--r--arch/alpha/Kconfig2
-rw-r--r--arch/alpha/include/asm/atomic.h4
-rw-r--r--arch/alpha/include/asm/fpu.h2
-rw-r--r--arch/alpha/include/asm/ptrace.h5
-rw-r--r--arch/alpha/include/asm/socket.h2
-rw-r--r--arch/alpha/include/asm/uaccess.h34
-rw-r--r--arch/alpha/include/asm/unistd.h4
-rw-r--r--arch/alpha/include/asm/word-at-a-time.h55
-rw-r--r--arch/alpha/kernel/alpha_ksyms.c3
-rw-r--r--arch/alpha/kernel/entry.S161
-rw-r--r--arch/alpha/kernel/osf_sys.c49
-rw-r--r--arch/alpha/kernel/process.c19
-rw-r--r--arch/alpha/kernel/systbls.S4
-rw-r--r--arch/alpha/lib/Makefile2
-rw-r--r--arch/alpha/lib/ev6-strncpy_from_user.S424
-rw-r--r--arch/alpha/lib/ev67-strlen_user.S107
-rw-r--r--arch/alpha/lib/strlen_user.S91
-rw-r--r--arch/alpha/lib/strncpy_from_user.S339
-rw-r--r--arch/alpha/mm/fault.c36
-rw-r--r--arch/alpha/oprofile/common.c1
-rw-r--r--arch/arm/Kconfig10
-rw-r--r--arch/arm/Kconfig.debug6
-rw-r--r--arch/arm/Makefile4
-rw-r--r--arch/arm/boot/compressed/head.S5
-rw-r--r--arch/arm/boot/dts/am33xx.dtsi5
-rw-r--r--arch/arm/boot/dts/armada-xp.dtsi2
-rw-r--r--arch/arm/boot/dts/at91sam9260.dtsi3
-rw-r--r--arch/arm/boot/dts/at91sam9263.dtsi5
-rw-r--r--arch/arm/boot/dts/at91sam9g25ek.dts2
-rw-r--r--arch/arm/boot/dts/at91sam9g45.dtsi5
-rw-r--r--arch/arm/boot/dts/at91sam9n12.dtsi4
-rw-r--r--arch/arm/boot/dts/at91sam9x5.dtsi4
-rw-r--r--arch/arm/boot/dts/imx23.dtsi52
-rw-r--r--arch/arm/boot/dts/imx27-3ds.dts2
-rw-r--r--arch/arm/boot/dts/imx27.dtsi6
-rw-r--r--arch/arm/boot/dts/imx28.dtsi74
-rw-r--r--arch/arm/boot/dts/imx51-babbage.dts6
-rw-r--r--arch/arm/boot/dts/imx51.dtsi4
-rw-r--r--arch/arm/boot/dts/imx53-ard.dts22
-rw-r--r--arch/arm/boot/dts/imx53.dtsi7
-rw-r--r--arch/arm/boot/dts/imx6q-sabrelite.dts1
-rw-r--r--arch/arm/boot/dts/imx6q.dtsi7
-rw-r--r--arch/arm/boot/dts/kirkwood-dns320.dts62
-rw-r--r--arch/arm/boot/dts/kirkwood-dns325.dts68
-rw-r--r--arch/arm/boot/dts/kirkwood-dnskw.dtsi69
-rw-r--r--arch/arm/boot/dts/kirkwood-dreamplug.dts50
-rw-r--r--arch/arm/boot/dts/kirkwood-goflexnet.dts99
-rw-r--r--arch/arm/boot/dts/kirkwood-ib62x0.dts38
-rw-r--r--arch/arm/boot/dts/kirkwood-iconnect.dts46
-rw-r--r--arch/arm/boot/dts/kirkwood-lschlv2.dts20
-rw-r--r--arch/arm/boot/dts/kirkwood-lsxhl.dts20
-rw-r--r--arch/arm/boot/dts/kirkwood-lsxl.dtsi95
-rw-r--r--arch/arm/boot/dts/kirkwood-ts219-6281.dts21
-rw-r--r--arch/arm/boot/dts/kirkwood-ts219-6282.dts21
-rw-r--r--arch/arm/boot/dts/kirkwood-ts219.dtsi78
-rw-r--r--arch/arm/boot/dts/kirkwood.dtsi60
-rw-r--r--arch/arm/boot/dts/twl6030.dtsi3
-rw-r--r--arch/arm/configs/armadillo800eva_defconfig2
-rw-r--r--arch/arm/configs/imx_v6_v7_defconfig1
-rw-r--r--arch/arm/configs/mxs_defconfig1
-rw-r--r--arch/arm/configs/omap2plus_defconfig2
-rw-r--r--arch/arm/configs/tct_hammer_defconfig2
-rw-r--r--arch/arm/configs/u8500_defconfig1
-rw-r--r--arch/arm/include/asm/assembler.h8
-rw-r--r--arch/arm/include/asm/cacheflush.h8
-rw-r--r--arch/arm/include/asm/dma-mapping.h7
-rw-r--r--arch/arm/include/asm/memory.h3
-rw-r--r--arch/arm/include/asm/mutex.h119
-rw-r--r--arch/arm/include/asm/pgtable.h40
-rw-r--r--arch/arm/include/asm/sched_clock.h2
-rw-r--r--arch/arm/include/asm/setup.h4
-rw-r--r--arch/arm/include/asm/tlb.h4
-rw-r--r--arch/arm/include/asm/uaccess.h58
-rw-r--r--arch/arm/include/asm/unistd.h2
-rw-r--r--arch/arm/kernel/calls.S1
-rw-r--r--arch/arm/kernel/entry-armv.S111
-rw-r--r--arch/arm/kernel/entry-common.S24
-rw-r--r--arch/arm/kernel/ftrace.c17
-rw-r--r--arch/arm/kernel/hw_breakpoint.c62
-rw-r--r--arch/arm/kernel/process.c2
-rw-r--r--arch/arm/kernel/ptrace.c1
-rw-r--r--arch/arm/kernel/sched_clock.c24
-rw-r--r--arch/arm/kernel/setup.c6
-rw-r--r--arch/arm/kernel/signal.c114
-rw-r--r--arch/arm/kernel/signal.h2
-rw-r--r--arch/arm/kernel/smp.c3
-rw-r--r--arch/arm/kernel/smp_twd.c48
-rw-r--r--arch/arm/kernel/topology.c2
-rw-r--r--arch/arm/kernel/traps.c21
-rw-r--r--arch/arm/lib/Makefile23
-rw-r--r--arch/arm/lib/delay.c1
-rw-r--r--arch/arm/lib/getuser.S23
-rw-r--r--arch/arm/lib/io-readsw-armv3.S106
-rw-r--r--arch/arm/lib/io-writesw-armv3.S126
-rw-r--r--arch/arm/lib/putuser.S6
-rw-r--r--arch/arm/lib/uaccess.S564
-rw-r--r--arch/arm/mach-at91/at91rm9200_time.c2
-rw-r--r--arch/arm/mach-at91/at91sam9260_devices.c6
-rw-r--r--arch/arm/mach-at91/at91sam9261_devices.c6
-rw-r--r--arch/arm/mach-at91/at91sam9263_devices.c10
-rw-r--r--arch/arm/mach-at91/at91sam9g45_devices.c6
-rw-r--r--arch/arm/mach-at91/at91sam9rl_devices.c6
-rw-r--r--arch/arm/mach-at91/clock.c12
-rw-r--r--arch/arm/mach-davinci/board-neuros-osd2.c39
-rw-r--r--arch/arm/mach-davinci/devices-da8xx.c1
-rw-r--r--arch/arm/mach-dove/common.c3
-rw-r--r--arch/arm/mach-dove/irq.c58
-rw-r--r--arch/arm/mach-exynos/mach-origen.c7
-rw-r--r--arch/arm/mach-exynos/mach-smdkv310.c7
-rw-r--r--arch/arm/mach-exynos/pm_domains.c2
-rw-r--r--arch/arm/mach-gemini/irq.c1
-rw-r--r--arch/arm/mach-imx/Makefile10
-rw-r--r--arch/arm/mach-imx/clk-imx25.c8
-rw-r--r--arch/arm/mach-imx/clk-imx27.c12
-rw-r--r--arch/arm/mach-imx/clk-imx31.c2
-rw-r--r--arch/arm/mach-imx/clk-imx35.c6
-rw-r--r--arch/arm/mach-imx/clk-imx51-imx53.c1
-rw-r--r--arch/arm/mach-imx/clk-imx6q.c8
-rw-r--r--arch/arm/mach-imx/devices-imx27.h4
-rw-r--r--arch/arm/mach-imx/headsmp.S (renamed from arch/arm/mach-imx/head-v7.S)0
-rw-r--r--arch/arm/mach-imx/hotplug.c23
-rw-r--r--arch/arm/mach-imx/mach-armadillo5x0.c3
-rw-r--r--arch/arm/mach-imx/mach-imx27_visstrim_m10.c49
-rw-r--r--arch/arm/mach-imx/mach-imx6q.c4
-rw-r--r--arch/arm/mach-integrator/core.c1
-rw-r--r--arch/arm/mach-integrator/integrator_ap.c2
-rw-r--r--arch/arm/mach-kirkwood/Kconfig29
-rw-r--r--arch/arm/mach-kirkwood/Makefile3
-rw-r--r--arch/arm/mach-kirkwood/Makefile.boot5
-rw-r--r--arch/arm/mach-kirkwood/board-dnskw.c143
-rw-r--r--arch/arm/mach-kirkwood/board-dreamplug.c80
-rw-r--r--arch/arm/mach-kirkwood/board-dt.c29
-rw-r--r--arch/arm/mach-kirkwood/board-goflexnet.c71
-rw-r--r--arch/arm/mach-kirkwood/board-ib62x0.c72
-rw-r--r--arch/arm/mach-kirkwood/board-iconnect.c56
-rw-r--r--arch/arm/mach-kirkwood/board-lsxl.c135
-rw-r--r--arch/arm/mach-kirkwood/board-ts219.c82
-rw-r--r--arch/arm/mach-kirkwood/common.c13
-rw-r--r--arch/arm/mach-kirkwood/common.h17
-rw-r--r--arch/arm/mach-kirkwood/db88f6281-bp-setup.c1
-rw-r--r--arch/arm/mach-kirkwood/irq.c38
-rw-r--r--arch/arm/mach-mmp/gplugd.c1
-rw-r--r--arch/arm/mach-mmp/sram.c2
-rw-r--r--arch/arm/mach-mv78xx0/addr-map.c2
-rw-r--r--arch/arm/mach-mv78xx0/common.c6
-rw-r--r--arch/arm/mach-mv78xx0/irq.c22
-rw-r--r--arch/arm/mach-mxs/Kconfig6
-rw-r--r--arch/arm/mach-mxs/Makefile3
-rw-r--r--arch/arm/mach-mxs/mach-mxs.c2
-rw-r--r--arch/arm/mach-omap1/board-h2-mmc.c1
-rw-r--r--arch/arm/mach-omap1/board-h3-mmc.c1
-rw-r--r--arch/arm/mach-omap1/board-nokia770.c1
-rw-r--r--arch/arm/mach-omap1/board-palmz71.c3
-rw-r--r--arch/arm/mach-omap2/Kconfig5
-rw-r--r--arch/arm/mach-omap2/Makefile2
-rw-r--r--arch/arm/mach-omap2/board-igep0020.c2
-rw-r--r--arch/arm/mach-omap2/board-n8x0.c1
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c1
-rw-r--r--arch/arm/mach-omap2/board-rx51-peripherals.c30
-rw-r--r--arch/arm/mach-omap2/clock33xx_data.c14
-rw-r--r--arch/arm/mach-omap2/clockdomain2xxx_3xxx.c50
-rw-r--r--arch/arm/mach-omap2/cm-regbits-34xx.h1
-rw-r--r--arch/arm/mach-omap2/common-board-devices.c11
-rw-r--r--arch/arm/mach-omap2/common-board-devices.h1
-rw-r--r--arch/arm/mach-omap2/cpuidle44xx.c148
-rw-r--r--arch/arm/mach-omap2/display.c25
-rw-r--r--arch/arm/mach-omap2/hsmmc.c1
-rw-r--r--arch/arm/mach-omap2/mux.h1
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c2
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c1
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_3xxx_data.c15
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c12
-rw-r--r--arch/arm/mach-omap2/opp4xxx_data.c2
-rw-r--r--arch/arm/mach-omap2/pm34xx.c19
-rw-r--r--arch/arm/mach-omap2/sleep44xx.S8
-rw-r--r--arch/arm/mach-omap2/timer.c11
-rw-r--r--arch/arm/mach-omap2/twl-common.c1
-rw-r--r--arch/arm/mach-orion5x/common.c10
-rw-r--r--arch/arm/mach-orion5x/irq.c22
-rw-r--r--arch/arm/mach-prima2/timer.c6
-rw-r--r--arch/arm/mach-pxa/lubbock.c2
-rw-r--r--arch/arm/mach-pxa/magician.c5
-rw-r--r--arch/arm/mach-pxa/raumfeld.c2
-rw-r--r--arch/arm/mach-pxa/trizeps4.c4
-rw-r--r--arch/arm/mach-s3c24xx/Kconfig4
-rw-r--r--arch/arm/mach-s3c24xx/include/mach/dma.h3
-rw-r--r--arch/arm/mach-sa1100/leds-hackkit.c1
-rw-r--r--arch/arm/mach-shmobile/board-armadillo800eva.c13
-rw-r--r--arch/arm/mach-shmobile/board-kzm9g.c4
-rw-r--r--arch/arm/mach-shmobile/board-mackerel.c3
-rw-r--r--arch/arm/mach-shmobile/board-marzen.c2
-rw-r--r--arch/arm/mach-shmobile/intc-sh73a0.c4
-rw-r--r--arch/arm/mach-spear3xx/spear300.c26
-rw-r--r--arch/arm/mach-spear3xx/spear310.c26
-rw-r--r--arch/arm/mach-spear3xx/spear320.c26
-rw-r--r--arch/arm/mach-spear3xx/spear3xx.c3
-rw-r--r--arch/arm/mach-spear6xx/spear6xx.c51
-rw-r--r--arch/arm/mach-tegra/board-harmony-power.c32
-rw-r--r--arch/arm/mach-ux500/Kconfig1
-rw-r--r--arch/arm/mach-ux500/board-mop500-msp.c10
-rw-r--r--arch/arm/mach-ux500/board-mop500.c4
-rw-r--r--arch/arm/mm/context.c7
-rw-r--r--arch/arm/mm/dma-mapping.c128
-rw-r--r--arch/arm/mm/flush.c2
-rw-r--r--arch/arm/mm/mm.h3
-rw-r--r--arch/arm/mm/mmu.c8
-rw-r--r--arch/arm/mm/tlb-v7.S12
-rw-r--r--arch/arm/plat-mxc/devices/Kconfig6
-rw-r--r--arch/arm/plat-mxc/devices/Makefile1
-rw-r--r--arch/arm/plat-mxc/devices/platform-imx27-coda.c37
-rw-r--r--arch/arm/plat-mxc/include/mach/devices-common.h8
-rw-r--r--arch/arm/plat-mxc/include/mach/mx25.h1
-rw-r--r--arch/arm/plat-mxc/tzic.c1
-rw-r--r--arch/arm/plat-omap/dmtimer.c6
-rw-r--r--arch/arm/plat-omap/include/plat/cpu.h3
-rw-r--r--arch/arm/plat-omap/include/plat/mmc.h2
-rw-r--r--arch/arm/plat-omap/include/plat/multi.h9
-rw-r--r--arch/arm/plat-omap/include/plat/uncompress.h4
-rw-r--r--arch/arm/plat-omap/sram.c11
-rw-r--r--arch/arm/plat-orion/common.c9
-rw-r--r--arch/arm/plat-orion/gpio.c166
-rw-r--r--arch/arm/plat-orion/include/plat/common.h6
-rw-r--r--arch/arm/plat-orion/include/plat/gpio.h16
-rw-r--r--arch/arm/plat-orion/include/plat/irq.h3
-rw-r--r--arch/arm/plat-orion/irq.c40
-rw-r--r--arch/arm/plat-s3c24xx/dma.c2
-rw-r--r--arch/arm/plat-samsung/Kconfig3
-rw-r--r--arch/arm/plat-samsung/clock.c10
-rw-r--r--arch/arm/plat-samsung/devs.c29
-rw-r--r--arch/arm/plat-samsung/include/plat/hdmi.h16
-rw-r--r--arch/arm/plat-samsung/pm.c2
-rw-r--r--arch/arm/plat-spear/include/plat/pl080.h6
-rw-r--r--arch/arm/plat-spear/pl080.c10
-rw-r--r--arch/arm/vfp/entry.S16
-rw-r--r--arch/arm/vfp/vfphw.S19
-rw-r--r--arch/arm/vfp/vfpmodule.c10
-rw-r--r--arch/blackfin/Kconfig1
-rw-r--r--arch/blackfin/Makefile1
-rw-r--r--arch/blackfin/include/asm/smp.h2
-rw-r--r--arch/blackfin/kernel/setup.c1
-rw-r--r--arch/blackfin/mach-common/smp.c223
-rw-r--r--arch/c6x/Kconfig1
-rw-r--r--arch/c6x/include/asm/Kbuild1
-rw-r--r--arch/c6x/include/asm/barrier.h27
-rw-r--r--arch/c6x/include/asm/cache.h16
-rw-r--r--arch/ia64/configs/generic_defconfig1
-rw-r--r--arch/ia64/configs/gensparse_defconfig1
-rw-r--r--arch/ia64/kernel/acpi.c5
-rw-r--r--arch/ia64/kernel/irq_ia64.c1
-rw-r--r--arch/ia64/kernel/perfmon.c1
-rw-r--r--arch/m68k/Kconfig13
-rw-r--r--arch/m68k/Kconfig.cpu19
-rw-r--r--arch/m68k/apollo/config.c16
-rw-r--r--arch/m68k/include/asm/Kbuild25
-rw-r--r--arch/m68k/include/asm/MC68332.h152
-rw-r--r--arch/m68k/include/asm/apollodma.h248
-rw-r--r--arch/m68k/include/asm/apollohw.h2
-rw-r--r--arch/m68k/include/asm/bitsperlong.h1
-rw-r--r--arch/m68k/include/asm/cputime.h6
-rw-r--r--arch/m68k/include/asm/delay.h2
-rw-r--r--arch/m68k/include/asm/device.h7
-rw-r--r--arch/m68k/include/asm/emergency-restart.h6
-rw-r--r--arch/m68k/include/asm/errno.h6
-rw-r--r--arch/m68k/include/asm/futex.h6
-rw-r--r--arch/m68k/include/asm/ioctl.h1
-rw-r--r--arch/m68k/include/asm/ipcbuf.h1
-rw-r--r--arch/m68k/include/asm/irq_regs.h1
-rw-r--r--arch/m68k/include/asm/kdebug.h1
-rw-r--r--arch/m68k/include/asm/kmap_types.h6
-rw-r--r--arch/m68k/include/asm/kvm_para.h1
-rw-r--r--arch/m68k/include/asm/local.h6
-rw-r--r--arch/m68k/include/asm/local64.h1
-rw-r--r--arch/m68k/include/asm/mac_mouse.h23
-rw-r--r--arch/m68k/include/asm/mcfmbus.h77
-rw-r--r--arch/m68k/include/asm/mman.h1
-rw-r--r--arch/m68k/include/asm/mutex.h9
-rw-r--r--arch/m68k/include/asm/percpu.h6
-rw-r--r--arch/m68k/include/asm/resource.h6
-rw-r--r--arch/m68k/include/asm/sbus.h45
-rw-r--r--arch/m68k/include/asm/scatterlist.h6
-rw-r--r--arch/m68k/include/asm/sections.h8
-rw-r--r--arch/m68k/include/asm/shm.h31
-rw-r--r--arch/m68k/include/asm/siginfo.h6
-rw-r--r--arch/m68k/include/asm/statfs.h6
-rw-r--r--arch/m68k/include/asm/topology.h6
-rw-r--r--arch/m68k/include/asm/types.h22
-rw-r--r--arch/m68k/include/asm/unaligned.h4
-rw-r--r--arch/m68k/include/asm/xor.h1
-rw-r--r--arch/m68k/kernel/setup_no.c11
-rw-r--r--arch/m68k/kernel/sys_m68k.c8
-rw-r--r--arch/m68k/kernel/vmlinux-nommu.lds2
-rw-r--r--arch/m68k/kernel/vmlinux-std.lds2
-rw-r--r--arch/m68k/kernel/vmlinux-sun3.lds2
-rw-r--r--arch/m68k/lib/muldi3.c2
-rw-r--r--arch/m68k/mm/init_mm.c2
-rw-r--r--arch/m68k/mm/init_no.c2
-rw-r--r--arch/m68k/platform/68328/head-de2.S8
-rw-r--r--arch/m68k/platform/68328/head-pilot.S10
-rw-r--r--arch/m68k/platform/68328/head-ram.S4
-rw-r--r--arch/m68k/platform/68328/head-rom.S6
-rw-r--r--arch/m68k/platform/68360/head-ram.S6
-rw-r--r--arch/m68k/platform/68360/head-rom.S8
-rw-r--r--arch/m68k/platform/coldfire/clk.c6
-rw-r--r--arch/m68k/platform/coldfire/head.S10
-rw-r--r--arch/m68k/sun3/prom/init.c48
-rw-r--r--arch/microblaze/include/asm/sections.h4
-rw-r--r--arch/microblaze/kernel/microblaze_ksyms.c3
-rw-r--r--arch/microblaze/kernel/setup.c4
-rw-r--r--arch/microblaze/kernel/vmlinux.lds.S1
-rw-r--r--arch/mips/Kconfig3
-rw-r--r--arch/mips/alchemy/board-mtx1.c2
-rw-r--r--arch/mips/ath79/dev-usb.c2
-rw-r--r--arch/mips/ath79/gpio.c6
-rw-r--r--arch/mips/bcm63xx/dev-spi.c4
-rw-r--r--arch/mips/cavium-octeon/octeon-irq.c89
-rw-r--r--arch/mips/include/asm/clock.h11
-rw-r--r--arch/mips/include/asm/mach-ath79/ar71xx_regs.h3
-rw-r--r--arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h1
-rw-r--r--arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h2
-rw-r--r--arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h13
-rw-r--r--arch/mips/include/asm/mach-cavium-octeon/irq.h10
-rw-r--r--arch/mips/include/asm/mach-loongson/loongson.h1
-rw-r--r--arch/mips/include/asm/module.h1
-rw-r--r--arch/mips/include/asm/r4k-timer.h8
-rw-r--r--arch/mips/kernel/cpufreq/Makefile2
-rw-r--r--arch/mips/kernel/cpufreq/loongson2_clock.c171
-rw-r--r--arch/mips/kernel/cpufreq/loongson2_cpufreq.c21
-rw-r--r--arch/mips/kernel/module.c43
-rw-r--r--arch/mips/kernel/smp-cmp.c2
-rw-r--r--arch/mips/kernel/smp.c4
-rw-r--r--arch/mips/kernel/sync-r4k.c26
-rw-r--r--arch/mips/lantiq/clk.c5
-rw-r--r--arch/mips/lantiq/prom.c22
-rw-r--r--arch/mips/lantiq/xway/sysctrl.c49
-rw-r--r--arch/mips/loongson/Kconfig1
-rw-r--r--arch/mips/loongson/lemote-2f/Makefile2
-rw-r--r--arch/mips/loongson/lemote-2f/clock.c145
-rw-r--r--arch/mips/loongson1/Kconfig1
-rw-r--r--arch/mips/loongson1/common/clock.c16
-rw-r--r--arch/mips/mm/gup.c2
-rw-r--r--arch/mips/mti-malta/malta-int.c9
-rw-r--r--arch/mips/mti-malta/malta-pci.c13
-rw-r--r--arch/mips/mti-malta/malta-platform.c5
-rw-r--r--arch/mips/pci/pci-ar724x.c22
-rw-r--r--arch/mips/sgi-ip27/ip27-memory.c1
-rw-r--r--arch/mips/txx9/Kconfig1
-rw-r--r--arch/parisc/include/asm/atomic.h4
-rw-r--r--arch/parisc/kernel/process.c2
-rw-r--r--arch/parisc/kernel/sys_parisc.c8
-rw-r--r--arch/powerpc/boot/dts/fsl/p4080si-post.dtsi7
-rw-r--r--arch/powerpc/configs/85xx/p1023rds_defconfig31
-rw-r--r--arch/powerpc/configs/chroma_defconfig4
-rw-r--r--arch/powerpc/configs/corenet32_smp_defconfig29
-rw-r--r--arch/powerpc/configs/corenet64_smp_defconfig1
-rw-r--r--arch/powerpc/configs/g5_defconfig103
-rw-r--r--arch/powerpc/configs/mpc83xx_defconfig18
-rw-r--r--arch/powerpc/configs/mpc85xx_defconfig33
-rw-r--r--arch/powerpc/configs/mpc85xx_smp_defconfig32
-rw-r--r--arch/powerpc/include/asm/cputable.h2
-rw-r--r--arch/powerpc/include/asm/kvm_host.h1
-rw-r--r--arch/powerpc/include/asm/kvm_ppc.h12
-rw-r--r--arch/powerpc/include/asm/mpic_msgr.h1
-rw-r--r--arch/powerpc/include/asm/processor.h1
-rw-r--r--arch/powerpc/kernel/asm-offsets.c1
-rw-r--r--arch/powerpc/kernel/dbell.c2
-rw-r--r--arch/powerpc/kernel/dma-iommu.c9
-rw-r--r--arch/powerpc/kernel/entry_64.S23
-rw-r--r--arch/powerpc/kernel/exceptions-64s.S3
-rw-r--r--arch/powerpc/kernel/hw_breakpoint.c2
-rw-r--r--arch/powerpc/kernel/idle_power7.S2
-rw-r--r--arch/powerpc/kernel/kgdb.c27
-rw-r--r--arch/powerpc/kernel/process.c12
-rw-r--r--arch/powerpc/kernel/smp.c11
-rw-r--r--arch/powerpc/kernel/syscalls.c8
-rw-r--r--arch/powerpc/kernel/sysfs.c10
-rw-r--r--arch/powerpc/kernel/time.c9
-rw-r--r--arch/powerpc/kernel/traps.c3
-rw-r--r--arch/powerpc/kvm/book3s_32_mmu_host.c3
-rw-r--r--arch/powerpc/kvm/book3s_64_mmu_host.c2
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S12
-rw-r--r--arch/powerpc/kvm/e500_tlb.c11
-rw-r--r--arch/powerpc/lib/code-patching.c2
-rw-r--r--arch/powerpc/lib/copyuser_power7.S35
-rw-r--r--arch/powerpc/lib/memcpy_power7.S4
-rw-r--r--arch/powerpc/mm/mem.c1
-rw-r--r--arch/powerpc/mm/numa.c7
-rw-r--r--arch/powerpc/perf/core-book3s.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c77
-rw-r--r--arch/powerpc/platforms/cell/spufs/syscalls.c2
-rw-r--r--arch/powerpc/platforms/powernv/smp.c10
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c13
-rw-r--r--arch/powerpc/sysdev/mpic_msgr.c3
-rw-r--r--arch/powerpc/sysdev/xics/icp-hv.c8
-rw-r--r--arch/powerpc/sysdev/xics/icp-native.c2
-rw-r--r--arch/powerpc/sysdev/xics/xics-common.c3
-rw-r--r--arch/powerpc/xmon/xmon.c84
-rw-r--r--arch/s390/Kconfig1
-rw-r--r--arch/s390/defconfig2
-rw-r--r--arch/s390/include/asm/elf.h3
-rw-r--r--arch/s390/include/asm/hugetlb.h24
-rw-r--r--arch/s390/include/asm/posix_types.h3
-rw-r--r--arch/s390/include/asm/smp.h1
-rw-r--r--arch/s390/include/asm/sparsemem.h2
-rw-r--r--arch/s390/include/asm/syscall.h10
-rw-r--r--arch/s390/include/asm/tlbflush.h2
-rw-r--r--arch/s390/kernel/compat_linux.c2
-rw-r--r--arch/s390/kernel/compat_wrapper.S4
-rw-r--r--arch/s390/kernel/ptrace.c7
-rw-r--r--arch/s390/kernel/setup.c2
-rw-r--r--arch/s390/kernel/sys_s390.c9
-rw-r--r--arch/s390/lib/uaccess_pt.c142
-rw-r--r--arch/s390/oprofile/init.c10
-rw-r--r--arch/sh/boards/Kconfig13
-rw-r--r--arch/sh/boards/board-apsh4a3a.c10
-rw-r--r--arch/sh/boards/board-apsh4ad0a.c10
-rw-r--r--arch/sh/boards/board-magicpanelr2.c10
-rw-r--r--arch/sh/boards/board-polaris.c10
-rw-r--r--arch/sh/boards/board-sh2007.c12
-rw-r--r--arch/sh/boards/board-sh7757lcr.c14
-rw-r--r--arch/sh/boards/mach-ap325rxa/setup.c21
-rw-r--r--arch/sh/boards/mach-ecovec24/setup.c125
-rw-r--r--arch/sh/boards/mach-kfr2r09/setup.c12
-rw-r--r--arch/sh/boards/mach-migor/setup.c13
-rw-r--r--arch/sh/boards/mach-rsk/setup.c10
-rw-r--r--arch/sh/boards/mach-sdk7786/setup.c10
-rw-r--r--arch/sh/boards/mach-se/7724/setup.c15
-rw-r--r--arch/sh/configs/apsh4ad0a_defconfig2
-rw-r--r--arch/sh/configs/sdk7786_defconfig4
-rw-r--r--arch/sh/configs/se7206_defconfig2
-rw-r--r--arch/sh/configs/shx3_defconfig2
-rw-r--r--arch/sh/configs/urquell_defconfig4
-rw-r--r--arch/sh/drivers/dma/dma-sh.c2
-rw-r--r--arch/sh/include/asm/sections.h1
-rw-r--r--arch/sh/include/cpu-sh2a/cpu/sh7269.h36
-rw-r--r--arch/sh/include/cpu-sh4/cpu/sh7757.h2
-rw-r--r--arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c195
-rw-r--r--arch/sh/kernel/cpu/sh4a/clock-sh7724.c4
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7722.c1
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7757.c14
-rw-r--r--arch/sh/kernel/cpu/sh5/entry.S2
-rw-r--r--arch/sh/kernel/entry-common.S2
-rw-r--r--arch/sh/kernel/setup.c2
-rw-r--r--arch/sh/kernel/sh_ksyms_32.c1
-rw-r--r--arch/sh/kernel/vmlinux.lds.S1
-rw-r--r--arch/sh/lib/mcount.S8
-rw-r--r--arch/sh/mm/fault.c8
-rw-r--r--arch/sparc/kernel/ldc.c6
-rw-r--r--arch/sparc/kernel/module.c13
-rw-r--r--arch/sparc/kernel/sys_sparc_64.c10
-rw-r--r--arch/sparc/mm/init_64.c28
-rw-r--r--arch/tile/configs/tilegx_defconfig4
-rw-r--r--arch/tile/configs/tilepro_defconfig4
-rw-r--r--arch/tile/include/gxio/iorpc_trio.h24
-rw-r--r--arch/um/defconfig10
-rw-r--r--arch/um/drivers/chan_kern.c4
-rw-r--r--arch/um/drivers/line.c231
-rw-r--r--arch/um/drivers/line.h12
-rw-r--r--arch/um/drivers/mconsole_kern.c3
-rw-r--r--arch/um/drivers/port_kern.c6
-rw-r--r--arch/um/drivers/random.c3
-rw-r--r--arch/um/drivers/ssl.c42
-rw-r--r--arch/um/drivers/stdio_console.c21
-rw-r--r--arch/um/drivers/ubd_kern.c2
-rw-r--r--arch/um/drivers/xterm_kern.c3
-rw-r--r--arch/um/include/asm/processor-generic.h9
-rw-r--r--arch/um/include/asm/ptrace-generic.h2
-rw-r--r--arch/um/include/shared/as-layout.h3
-rw-r--r--arch/um/include/shared/common-offsets.h10
-rw-r--r--arch/um/include/shared/irq_user.h3
-rw-r--r--arch/um/include/shared/kern_util.h13
-rw-r--r--arch/um/include/shared/user.h11
-rw-r--r--arch/um/kernel/exec.c25
-rw-r--r--arch/um/kernel/irq.c2
-rw-r--r--arch/um/kernel/process.c21
-rw-r--r--arch/um/kernel/ptrace.c71
-rw-r--r--arch/um/kernel/sigio.c3
-rw-r--r--arch/um/kernel/signal.c6
-rw-r--r--arch/um/kernel/skas/syscall.c6
-rw-r--r--arch/um/kernel/syscall.c24
-rw-r--r--arch/um/kernel/time.c2
-rw-r--r--arch/um/kernel/trap.c39
-rw-r--r--arch/um/os-Linux/internal.h2
-rw-r--r--arch/um/os-Linux/signal.c26
-rw-r--r--arch/um/os-Linux/skas/process.c16
-rw-r--r--arch/um/os-Linux/time.c4
-rw-r--r--arch/um/scripts/Makefile.rules2
-rw-r--r--arch/x86/Kconfig10
-rw-r--r--arch/x86/Makefile6
-rw-r--r--arch/x86/boot/Makefile2
-rw-r--r--arch/x86/include/asm/mce.h8
-rw-r--r--arch/x86/include/asm/olpc.h19
-rw-r--r--arch/x86/include/asm/perf_event.h11
-rw-r--r--arch/x86/include/asm/spinlock.h3
-rw-r--r--arch/x86/include/asm/xen/page.h3
-rw-r--r--arch/x86/kernel/acpi/sleep.c4
-rw-r--r--arch/x86/kernel/acpi/sleep.h2
-rw-r--r--arch/x86/kernel/acpi/wakeup_32.S4
-rw-r--r--arch/x86/kernel/acpi/wakeup_64.S4
-rw-r--r--arch/x86/kernel/alternative.c4
-rw-r--r--arch/x86/kernel/apic/io_apic.c14
-rw-r--r--arch/x86/kernel/cpu/common.c2
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-severity.c7
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c43
-rw-r--r--arch/x86/kernel/cpu/perf_event.c89
-rw-r--r--arch/x86/kernel/cpu/perf_event.h22
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c16
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c35
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_ds.c21
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_lbr.c3
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.c259
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.h48
-rw-r--r--arch/x86/kernel/irq.c3
-rw-r--r--arch/x86/kernel/kdebugfs.c6
-rw-r--r--arch/x86/kernel/microcode_amd.c7
-rw-r--r--arch/x86/kernel/microcode_core.c3
-rw-r--r--arch/x86/kvm/emulate.c30
-rw-r--r--arch/x86/kvm/i8259.c17
-rw-r--r--arch/x86/kvm/mmu.c13
-rw-r--r--arch/x86/kvm/vmx.c43
-rw-r--r--arch/x86/kvm/x86.c22
-rw-r--r--arch/x86/mm/hugetlbpage.c21
-rw-r--r--arch/x86/mm/init.c2
-rw-r--r--arch/x86/mm/pageattr.c10
-rw-r--r--arch/x86/mm/srat.c15
-rw-r--r--arch/x86/platform/efi/efi.c30
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-pm.c16
-rw-r--r--arch/x86/platform/olpc/olpc-xo1-sci.c1
-rw-r--r--arch/x86/platform/olpc/olpc-xo15-sci.c1
-rw-r--r--arch/x86/platform/olpc/olpc.c190
-rw-r--r--arch/x86/realmode/rm/Makefile2
-rw-r--r--arch/x86/syscalls/syscall_64.tbl8
-rw-r--r--arch/x86/um/Kconfig1
-rw-r--r--arch/x86/um/asm/ptrace.h6
-rw-r--r--arch/x86/um/shared/sysdep/kernel-offsets.h3
-rw-r--r--arch/x86/um/shared/sysdep/syscalls.h2
-rw-r--r--arch/x86/um/signal.c6
-rw-r--r--arch/x86/um/sys_call_table_32.c2
-rw-r--r--arch/x86/um/syscalls_32.c27
-rw-r--r--arch/x86/um/syscalls_64.c23
-rw-r--r--arch/x86/xen/enlighten.c122
-rw-r--r--arch/x86/xen/mmu.c2
-rw-r--r--arch/x86/xen/p2m.c121
-rw-r--r--arch/x86/xen/setup.c13
-rw-r--r--arch/x86/xen/suspend.c2
-rw-r--r--arch/x86/xen/xen-ops.h2
-rw-r--r--arch/xtensa/Kconfig1
-rw-r--r--block/blk-cgroup.c139
-rw-r--r--block/blk-cgroup.h128
-rw-r--r--block/blk-core.c261
-rw-r--r--block/blk-ioc.c1
-rw-r--r--block/blk-lib.c41
-rw-r--r--block/blk-merge.c117
-rw-r--r--block/blk-settings.c3
-rw-r--r--block/blk-sysfs.c34
-rw-r--r--block/blk-throttle.c3
-rw-r--r--block/blk.h4
-rw-r--r--block/bsg-lib.c53
-rw-r--r--block/genhd.c22
-rw-r--r--block/ioctl.c61
-rw-r--r--block/partition-generic.c4
-rw-r--r--crypto/authenc.c4
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/ac.c4
-rw-r--r--drivers/acpi/acpica/achware.h12
-rw-r--r--drivers/acpi/acpica/hwesleep.c19
-rw-r--r--drivers/acpi/acpica/hwsleep.c20
-rw-r--r--drivers/acpi/acpica/hwxfsleep.c22
-rw-r--r--drivers/acpi/acpica/tbxface.c1
-rw-r--r--drivers/acpi/battery.c2
-rw-r--r--drivers/acpi/bus.c10
-rw-r--r--drivers/acpi/button.c4
-rw-r--r--drivers/acpi/fan.c4
-rw-r--r--drivers/acpi/numa.c12
-rw-r--r--drivers/acpi/pci_root.c11
-rw-r--r--drivers/acpi/power.c40
-rw-r--r--drivers/acpi/processor_driver.c2
-rw-r--r--drivers/acpi/sbs.c2
-rw-r--r--drivers/acpi/sleep.c75
-rw-r--r--drivers/acpi/sysfs.c4
-rw-r--r--drivers/acpi/thermal.c4
-rw-r--r--drivers/ata/Kconfig2
-rw-r--r--drivers/ata/ahci.c18
-rw-r--r--drivers/ata/ahci.h1
-rw-r--r--drivers/ata/ata_piix.c8
-rw-r--r--drivers/ata/libahci.c3
-rw-r--r--drivers/ata/libata-acpi.c15
-rw-r--r--drivers/ata/libata-core.c3
-rw-r--r--drivers/ata/pata_atiixp.c16
-rw-r--r--drivers/ata/sata_mv.c42
-rw-r--r--drivers/atm/iphase.c2
-rw-r--r--drivers/base/Kconfig1
-rw-r--r--drivers/base/core.c9
-rw-r--r--drivers/base/devtmpfs.c9
-rw-r--r--drivers/base/dma-contiguous.c2
-rw-r--r--drivers/base/power/clock_ops.c3
-rw-r--r--drivers/base/power/common.c4
-rw-r--r--drivers/base/power/runtime.c13
-rw-r--r--drivers/bcma/host_pci.c1
-rw-r--r--drivers/bcma/sprom.c4
-rw-r--r--drivers/block/aoe/aoecmd.c1
-rw-r--r--drivers/block/cciss_scsi.c12
-rw-r--r--drivers/block/drbd/drbd_actlog.c8
-rw-r--r--drivers/block/drbd/drbd_bitmap.c19
-rw-r--r--drivers/block/drbd/drbd_int.h45
-rw-r--r--drivers/block/drbd/drbd_main.c97
-rw-r--r--drivers/block/drbd/drbd_nl.c40
-rw-r--r--drivers/block/drbd/drbd_proc.c3
-rw-r--r--drivers/block/drbd/drbd_receiver.c38
-rw-r--r--drivers/block/drbd/drbd_req.c45
-rw-r--r--drivers/block/drbd/drbd_worker.c12
-rw-r--r--drivers/block/floppy.c24
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c38
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h10
-rw-r--r--drivers/block/nbd.c17
-rw-r--r--drivers/block/nvme.c153
-rw-r--r--drivers/block/rbd.c7
-rw-r--r--drivers/block/umem.c37
-rw-r--r--drivers/block/xen-blkback/blkback.c2
-rw-r--r--drivers/block/xen-blkfront.c5
-rw-r--r--drivers/bluetooth/ath3k.c4
-rw-r--r--drivers/bluetooth/btusb.c12
-rw-r--r--drivers/char/agp/intel-agp.h40
-rw-r--r--drivers/char/agp/intel-gtt.c107
-rw-r--r--drivers/char/hw_random/omap-rng.c2
-rw-r--r--drivers/char/random.c355
-rw-r--r--drivers/char/tpm/tpm_tis.c2
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-devres.c55
-rw-r--r--drivers/clk/clkdev.c45
-rw-r--r--drivers/clocksource/cs5535-clockevt.c4
-rw-r--r--drivers/cpufreq/omap-cpufreq.c4
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c1
-rw-r--r--drivers/cpufreq/powernow-k8.c63
-rw-r--r--drivers/cpuidle/coupled.c14
-rw-r--r--drivers/crypto/caam/jr.c10
-rw-r--r--drivers/crypto/caam/key_gen.c1
-rw-r--r--drivers/crypto/hifn_795x.c4
-rw-r--r--drivers/crypto/n2_core.c3
-rw-r--r--drivers/dma/Kconfig11
-rw-r--r--drivers/dma/Makefile2
-rw-r--r--drivers/dma/amba-pl08x.c941
-rw-r--r--drivers/dma/at_hdmac.c13
-rw-r--r--drivers/dma/imx-dma.c36
-rw-r--r--drivers/dma/omap-dma.c669
-rw-r--r--drivers/dma/pl330.c21
-rw-r--r--drivers/dma/sa11x0-dma.c388
-rw-r--r--drivers/dma/sh/shdma-base.c9
-rw-r--r--drivers/dma/sh/shdma.c12
-rw-r--r--drivers/dma/tegra20-apb-dma.c18
-rw-r--r--drivers/dma/virt-dma.c123
-rw-r--r--drivers/dma/virt-dma.h152
-rw-r--r--drivers/edac/edac_mc.c57
-rw-r--r--drivers/edac/i3200_edac.c2
-rw-r--r--drivers/edac/i5000_edac.c4
-rw-r--r--drivers/edac/sb_edac.c7
-rw-r--r--drivers/extcon/extcon-max77693.c19
-rw-r--r--drivers/extcon/extcon_gpio.c3
-rw-r--r--drivers/firmware/dmi_scan.c3
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpio/gpio-bt8xx.c2
-rw-r--r--drivers/gpio/gpio-em.c6
-rw-r--r--drivers/gpio/gpio-langwell.c7
-rw-r--r--drivers/gpio/gpio-lpc32xx.c5
-rw-r--r--drivers/gpio/gpio-msic.c2
-rw-r--r--drivers/gpio/gpio-mxc.c5
-rw-r--r--drivers/gpio/gpio-pxa.c30
-rw-r--r--drivers/gpio/gpio-rdc321x.c1
-rw-r--r--drivers/gpio/gpio-samsung.c14
-rw-r--r--drivers/gpio/gpio-sch.c3
-rw-r--r--drivers/gpio/gpiolib-of.c2
-rw-r--r--drivers/gpu/drm/Kconfig1
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c3
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c2
-rw-r--r--drivers/gpu/drm/cirrus/cirrus_drv.c3
-rw-r--r--drivers/gpu/drm/drm_crtc.c2
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/drm_edid_load.c8
-rw-r--r--drivers/gpu/drm/drm_modes.c3
-rw-r--r--drivers/gpu/drm/drm_proc.c4
-rw-r--r--drivers/gpu/drm/exynos/Kconfig2
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dmabuf.c7
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c52
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c4
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c11
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c6
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_device.c2
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_display.c3
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c3
-rw-r--r--drivers/gpu/drm/i810/i810_drv.c3
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c1
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c31
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c11
-rw-r--r--drivers/gpu/drm/i915/i915_gem_context.c1
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c20
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c10
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c3
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h1
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c12
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c36
-rw-r--r--drivers/gpu/drm/i915/intel_display.c42
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c25
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h22
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c2
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c10
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c8
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c31
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c46
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c24
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c48
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c21
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c4
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c3
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c12
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c7
-rw-r--r--drivers/gpu/drm/nouveau/nv50_gpio.c16
-rw-r--r--drivers/gpu/drm/nouveau/nv84_fifo.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_pm.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvd0_display.c6
-rw-r--r--drivers/gpu/drm/nouveau/nve0_fifo.c40
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c81
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c29
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c140
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c71
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c13
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h2
-rw-r--r--drivers/gpu/drm/radeon/ni.c14
-rw-r--r--drivers/gpu/drm/radeon/r100.c3
-rw-r--r--drivers/gpu/drm/radeon/r600.c20
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c196
-rw-r--r--drivers/gpu/drm/radeon/r600d.h28
-rw-r--r--drivers/gpu/drm/radeon/radeon.h27
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h10
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c51
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c56
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c138
-rw-r--r--drivers/gpu/drm/radeon/radeon_combios.c57
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c32
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c8
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c26
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c35
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c1
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r6009
-rw-r--r--drivers/gpu/drm/radeon/rv515.c13
-rw-r--r--drivers/gpu/drm/radeon/si.c35
-rw-r--r--drivers/gpu/drm/radeon/sid.h3
-rw-r--r--drivers/gpu/drm/savage/savage_drv.c3
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c3
-rw-r--r--drivers/gpu/drm/tdfx/tdfx_drv.c3
-rw-r--r--drivers/gpu/drm/udl/Kconfig1
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c7
-rw-r--r--drivers/gpu/drm/udl/udl_drv.c3
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c2
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c3
-rw-r--r--drivers/gpu/drm/via/via_drv.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/Kconfig8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c6
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c73
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c61
-rw-r--r--drivers/hid/hid-core.c8
-rw-r--r--drivers/hid/hid-lenovo-tpkbd.c2
-rw-r--r--drivers/hid/hid-logitech-dj.c49
-rw-r--r--drivers/hid/hid-logitech-dj.h1
-rw-r--r--drivers/hid/usbhid/hid-quirks.c1
-rw-r--r--drivers/hv/vmbus_drv.c3
-rw-r--r--drivers/hwmon/ad7314.c8
-rw-r--r--drivers/hwmon/ads7871.c9
-rw-r--r--drivers/hwmon/applesmc.c4
-rw-r--r--drivers/hwmon/asus_atk0110.c6
-rw-r--r--drivers/hwmon/coretemp.c7
-rw-r--r--drivers/hwmon/fam15h_power.c15
-rw-r--r--drivers/hwmon/ina2xx.c30
-rw-r--r--drivers/hwmon/twl4030-madc-hwmon.c9
-rw-r--r--drivers/hwmon/via-cputemp.c5
-rw-r--r--drivers/hwmon/w83627hf.c2
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c3
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c6
-rw-r--r--drivers/i2c/busses/Kconfig6
-rw-r--r--drivers/i2c/busses/Makefile5
-rw-r--r--drivers/i2c/busses/i2c-designware-core.c11
-rw-r--r--drivers/i2c/busses/i2c-diolan-u2c.c1
-rw-r--r--drivers/i2c/busses/i2c-i801.c3
-rw-r--r--drivers/i2c/busses/i2c-mxs.c13
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c28
-rw-r--r--drivers/i2c/busses/i2c-omap.c2
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c3
-rw-r--r--drivers/i2c/busses/i2c-pnx.c53
-rw-r--r--drivers/i2c/busses/i2c-tegra.c2
-rw-r--r--drivers/i2c/i2c-core.c22
-rw-r--r--drivers/ide/ide-pm.c4
-rw-r--r--drivers/idle/intel_idle.c3
-rw-r--r--drivers/iio/adc/at91_adc.c2
-rw-r--r--drivers/iio/frequency/adf4350.c24
-rw-r--r--drivers/iio/light/adjd_s311.c7
-rw-r--r--drivers/iio/light/lm3533-als.c4
-rw-r--r--drivers/infiniband/core/cma.c5
-rw-r--r--drivers/infiniband/core/ucma.c21
-rw-r--r--drivers/infiniband/hw/amso1100/c2_rnic.c2
-rw-r--r--drivers/infiniband/hw/cxgb3/iwch_cm.c2
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c2
-rw-r--r--drivers/infiniband/hw/mlx4/mad.c16
-rw-r--r--drivers/infiniband/hw/mlx4/main.c5
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c6
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_main.c16
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c12
-rw-r--r--drivers/infiniband/hw/qib/qib.h10
-rw-r--r--drivers/infiniband/hw/qib/qib_iba7322.c4
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c3
-rw-r--r--drivers/infiniband/hw/qib/qib_sd7220.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h59
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c19
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c641
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c55
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c87
-rw-r--r--drivers/infiniband/ulp/srpt/ib_srpt.c2
-rw-r--r--drivers/input/keyboard/imx_keypad.c3
-rw-r--r--drivers/input/misc/ab8500-ponkey.c4
-rw-r--r--drivers/input/mouse/sentelic.c11
-rw-r--r--drivers/input/serio/ambakmi.c6
-rw-r--r--drivers/input/serio/hp_sdc.c2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h20
-rw-r--r--drivers/input/tablet/wacom_wac.c6
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c11
-rw-r--r--drivers/input/touchscreen/eeti_ts.c21
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c40
-rw-r--r--drivers/iommu/amd_iommu.c31
-rw-r--r--drivers/iommu/amd_iommu_init.c8
-rw-r--r--drivers/iommu/exynos-iommu.c6
-rw-r--r--drivers/iommu/intel-iommu.c26
-rw-r--r--drivers/iommu/intel_irq_remapping.c18
-rw-r--r--drivers/iommu/tegra-smmu.c17
-rw-r--r--drivers/isdn/hardware/mISDN/avmfritz.c3
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c2
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNipac.c3
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNisar.c3
-rw-r--r--drivers/isdn/hardware/mISDN/netjet.c3
-rw-r--r--drivers/isdn/hardware/mISDN/w6692.c3
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c12
-rw-r--r--drivers/isdn/mISDN/hwchannel.c9
-rw-r--r--drivers/isdn/mISDN/layer2.c2
-rw-r--r--drivers/leds/led-triggers.c2
-rw-r--r--drivers/leds/leds-lp8788.c2
-rw-r--r--drivers/leds/leds-renesas-tpu.c2
-rw-r--r--drivers/md/Kconfig5
-rw-r--r--drivers/md/bitmap.c2
-rw-r--r--drivers/md/dm-mpath.c11
-rw-r--r--drivers/md/dm-raid.c95
-rw-r--r--drivers/md/dm-table.c61
-rw-r--r--drivers/md/dm-thin.c135
-rw-r--r--drivers/md/dm-verity.c8
-rw-r--r--drivers/md/dm.c71
-rw-r--r--drivers/md/dm.h1
-rw-r--r--drivers/md/md.c82
-rw-r--r--drivers/md/md.h11
-rw-r--r--drivers/md/raid1.c224
-rw-r--r--drivers/md/raid1.h30
-rw-r--r--drivers/md/raid10.c133
-rw-r--r--drivers/md/raid10.h25
-rw-r--r--drivers/md/raid5.c320
-rw-r--r--drivers/md/raid5.h3
-rw-r--r--drivers/media/Kconfig53
-rw-r--r--drivers/media/Makefile23
-rw-r--r--drivers/media/common/Kconfig12
-rw-r--r--drivers/media/common/Makefile7
-rw-r--r--drivers/media/common/b2c2/Kconfig28
-rw-r--r--drivers/media/common/b2c2/Makefile8
-rw-r--r--drivers/media/common/b2c2/flexcop-common.h (renamed from drivers/media/dvb/b2c2/flexcop-common.h)0
-rw-r--r--drivers/media/common/b2c2/flexcop-eeprom.c (renamed from drivers/media/dvb/b2c2/flexcop-eeprom.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop-fe-tuner.c (renamed from drivers/media/dvb/b2c2/flexcop-fe-tuner.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop-hw-filter.c (renamed from drivers/media/dvb/b2c2/flexcop-hw-filter.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c (renamed from drivers/media/dvb/b2c2/flexcop-i2c.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop-misc.c (renamed from drivers/media/dvb/b2c2/flexcop-misc.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop-reg.h (renamed from drivers/media/dvb/b2c2/flexcop-reg.h)0
-rw-r--r--drivers/media/common/b2c2/flexcop-sram.c (renamed from drivers/media/dvb/b2c2/flexcop-sram.c)0
-rw-r--r--drivers/media/common/b2c2/flexcop.c325
-rw-r--r--drivers/media/common/b2c2/flexcop.h (renamed from drivers/media/dvb/b2c2/flexcop.h)0
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_be.h (renamed from drivers/media/dvb/b2c2/flexcop_ibi_value_be.h)0
-rw-r--r--drivers/media/common/b2c2/flexcop_ibi_value_le.h (renamed from drivers/media/dvb/b2c2/flexcop_ibi_value_le.h)0
-rw-r--r--drivers/media/common/saa7146/Kconfig9
-rw-r--r--drivers/media/common/saa7146/Makefile5
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c592
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c664
-rw-r--r--drivers/media/common/saa7146/saa7146_hlp.c (renamed from drivers/media/common/saa7146_hlp.c)0
-rw-r--r--drivers/media/common/saa7146/saa7146_i2c.c (renamed from drivers/media/common/saa7146_i2c.c)0
-rw-r--r--drivers/media/common/saa7146/saa7146_vbi.c (renamed from drivers/media/common/saa7146_vbi.c)0
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c1332
-rw-r--r--drivers/media/common/saa7146_core.c600
-rw-r--r--drivers/media/common/saa7146_fops.c641
-rw-r--r--drivers/media/common/saa7146_video.c1332
-rw-r--r--drivers/media/common/siano/Kconfig17
-rw-r--r--drivers/media/common/siano/Makefile7
-rw-r--r--drivers/media/common/siano/sms-cards.c (renamed from drivers/media/dvb/siano/sms-cards.c)0
-rw-r--r--drivers/media/common/siano/sms-cards.h (renamed from drivers/media/dvb/siano/sms-cards.h)0
-rw-r--r--drivers/media/common/siano/smscoreapi.c (renamed from drivers/media/dvb/siano/smscoreapi.c)0
-rw-r--r--drivers/media/common/siano/smscoreapi.h (renamed from drivers/media/dvb/siano/smscoreapi.h)0
-rw-r--r--drivers/media/common/siano/smsdvb.c (renamed from drivers/media/dvb/siano/smsdvb.c)0
-rw-r--r--drivers/media/common/siano/smsendian.c (renamed from drivers/media/dvb/siano/smsendian.c)0
-rw-r--r--drivers/media/common/siano/smsendian.h (renamed from drivers/media/dvb/siano/smsendian.h)0
-rw-r--r--drivers/media/common/siano/smsir.c (renamed from drivers/media/dvb/siano/smsir.c)0
-rw-r--r--drivers/media/common/siano/smsir.h (renamed from drivers/media/dvb/siano/smsir.h)0
-rw-r--r--drivers/media/common/tuners/Kconfig243
-rw-r--r--drivers/media/common/tuners/Makefile37
-rw-r--r--drivers/media/common/tuners/mc44s803.c372
-rw-r--r--drivers/media/common/tuners/mxl5005s.c4109
-rw-r--r--drivers/media/common/tuners/qt1010.c480
-rw-r--r--drivers/media/common/tuners/tda18212.c326
-rw-r--r--drivers/media/common/tuners/tda18218.c342
-rw-r--r--drivers/media/common/tuners/tda18218_priv.h108
-rw-r--r--drivers/media/common/tuners/tda18271-common.c703
-rw-r--r--drivers/media/common/tuners/tda18271-fe.c1345
-rw-r--r--drivers/media/common/tuners/tda18271.h134
-rw-r--r--drivers/media/common/tuners/tua9001.c215
-rw-r--r--drivers/media/common/tuners/tua9001.h46
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c1510
-rw-r--r--drivers/media/common/tuners/xc4000.c1758
-rw-r--r--drivers/media/common/tuners/xc5000.c1265
-rw-r--r--drivers/media/dvb-core/Kconfig29
-rw-r--r--drivers/media/dvb-core/Makefile (renamed from drivers/media/dvb/dvb-core/Makefile)0
-rw-r--r--drivers/media/dvb-core/demux.h (renamed from drivers/media/dvb/dvb-core/demux.h)0
-rw-r--r--drivers/media/dvb-core/dmxdev.c1275
-rw-r--r--drivers/media/dvb-core/dmxdev.h (renamed from drivers/media/dvb/dvb-core/dmxdev.h)0
-rw-r--r--drivers/media/dvb-core/dvb-usb-ids.h364
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c (renamed from drivers/media/dvb/dvb-core/dvb_ca_en50221.c)0
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.h (renamed from drivers/media/dvb/dvb-core/dvb_ca_en50221.h)0
-rw-r--r--drivers/media/dvb-core/dvb_demux.c1318
-rw-r--r--drivers/media/dvb-core/dvb_demux.h (renamed from drivers/media/dvb/dvb-core/dvb_demux.h)0
-rw-r--r--drivers/media/dvb-core/dvb_filter.c (renamed from drivers/media/dvb/dvb-core/dvb_filter.c)0
-rw-r--r--drivers/media/dvb-core/dvb_filter.h (renamed from drivers/media/dvb/dvb-core/dvb_filter.h)0
-rw-r--r--drivers/media/dvb-core/dvb_frontend.c2617
-rw-r--r--drivers/media/dvb-core/dvb_frontend.h425
-rw-r--r--drivers/media/dvb-core/dvb_math.c (renamed from drivers/media/dvb/dvb-core/dvb_math.c)0
-rw-r--r--drivers/media/dvb-core/dvb_math.h (renamed from drivers/media/dvb/dvb-core/dvb_math.h)0
-rw-r--r--drivers/media/dvb-core/dvb_net.c (renamed from drivers/media/dvb/dvb-core/dvb_net.c)0
-rw-r--r--drivers/media/dvb-core/dvb_net.h (renamed from drivers/media/dvb/dvb-core/dvb_net.h)0
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c (renamed from drivers/media/dvb/dvb-core/dvb_ringbuffer.c)0
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.h (renamed from drivers/media/dvb/dvb-core/dvb_ringbuffer.h)0
-rw-r--r--drivers/media/dvb-core/dvbdev.c507
-rw-r--r--drivers/media/dvb-core/dvbdev.h146
-rw-r--r--drivers/media/dvb-frontends/Kconfig741
-rw-r--r--drivers/media/dvb-frontends/Makefile105
-rw-r--r--drivers/media/dvb-frontends/a8293.c (renamed from drivers/media/dvb/frontends/a8293.c)0
-rw-r--r--drivers/media/dvb-frontends/a8293.h (renamed from drivers/media/dvb/frontends/a8293.h)0
-rw-r--r--drivers/media/dvb-frontends/af9013.c1548
-rw-r--r--drivers/media/dvb-frontends/af9013.h118
-rw-r--r--drivers/media/dvb-frontends/af9013_priv.h909
-rw-r--r--drivers/media/dvb-frontends/af9033.c996
-rw-r--r--drivers/media/dvb-frontends/af9033.h76
-rw-r--r--drivers/media/dvb-frontends/af9033_priv.h507
-rw-r--r--drivers/media/dvb-frontends/atbm8830.c508
-rw-r--r--drivers/media/dvb-frontends/atbm8830.h (renamed from drivers/media/dvb/frontends/atbm8830.h)0
-rw-r--r--drivers/media/dvb-frontends/atbm8830_priv.h (renamed from drivers/media/dvb/frontends/atbm8830_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/au8522.h (renamed from drivers/media/dvb/frontends/au8522.h)0
-rw-r--r--drivers/media/dvb-frontends/au8522_common.c277
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c839
-rw-r--r--drivers/media/dvb-frontends/au8522_dig.c828
-rw-r--r--drivers/media/dvb-frontends/au8522_priv.h446
-rw-r--r--drivers/media/dvb-frontends/bcm3510.c (renamed from drivers/media/dvb/frontends/bcm3510.c)0
-rw-r--r--drivers/media/dvb-frontends/bcm3510.h (renamed from drivers/media/dvb/frontends/bcm3510.h)0
-rw-r--r--drivers/media/dvb-frontends/bcm3510_priv.h (renamed from drivers/media/dvb/frontends/bcm3510_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/bsbe1-d01a.h (renamed from drivers/media/dvb/frontends/bsbe1-d01a.h)0
-rw-r--r--drivers/media/dvb-frontends/bsbe1.h (renamed from drivers/media/dvb/frontends/bsbe1.h)0
-rw-r--r--drivers/media/dvb-frontends/bsru6.h (renamed from drivers/media/dvb/frontends/bsru6.h)0
-rw-r--r--drivers/media/dvb-frontends/cx22700.c (renamed from drivers/media/dvb/frontends/cx22700.c)0
-rw-r--r--drivers/media/dvb-frontends/cx22700.h (renamed from drivers/media/dvb/frontends/cx22700.h)0
-rw-r--r--drivers/media/dvb-frontends/cx22702.c (renamed from drivers/media/dvb/frontends/cx22702.c)0
-rw-r--r--drivers/media/dvb-frontends/cx22702.h (renamed from drivers/media/dvb/frontends/cx22702.h)0
-rw-r--r--drivers/media/dvb-frontends/cx24110.c (renamed from drivers/media/dvb/frontends/cx24110.c)0
-rw-r--r--drivers/media/dvb-frontends/cx24110.h (renamed from drivers/media/dvb/frontends/cx24110.h)0
-rw-r--r--drivers/media/dvb-frontends/cx24113.c (renamed from drivers/media/dvb/frontends/cx24113.c)0
-rw-r--r--drivers/media/dvb-frontends/cx24113.h (renamed from drivers/media/dvb/frontends/cx24113.h)0
-rw-r--r--drivers/media/dvb-frontends/cx24116.c (renamed from drivers/media/dvb/frontends/cx24116.c)0
-rw-r--r--drivers/media/dvb-frontends/cx24116.h (renamed from drivers/media/dvb/frontends/cx24116.h)0
-rw-r--r--drivers/media/dvb-frontends/cx24123.c (renamed from drivers/media/dvb/frontends/cx24123.c)0
-rw-r--r--drivers/media/dvb-frontends/cx24123.h (renamed from drivers/media/dvb/frontends/cx24123.h)0
-rw-r--r--drivers/media/dvb-frontends/cxd2820r.h88
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_c.c343
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c757
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_priv.h148
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t.c449
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_t2.c423
-rw-r--r--drivers/media/dvb-frontends/dib0070.c (renamed from drivers/media/dvb/frontends/dib0070.c)0
-rw-r--r--drivers/media/dvb-frontends/dib0070.h (renamed from drivers/media/dvb/frontends/dib0070.h)0
-rw-r--r--drivers/media/dvb-frontends/dib0090.c (renamed from drivers/media/dvb/frontends/dib0090.c)0
-rw-r--r--drivers/media/dvb-frontends/dib0090.h (renamed from drivers/media/dvb/frontends/dib0090.h)0
-rw-r--r--drivers/media/dvb-frontends/dib3000.h (renamed from drivers/media/dvb/frontends/dib3000.h)0
-rw-r--r--drivers/media/dvb-frontends/dib3000mb.c (renamed from drivers/media/dvb/frontends/dib3000mb.c)0
-rw-r--r--drivers/media/dvb-frontends/dib3000mb_priv.h (renamed from drivers/media/dvb/frontends/dib3000mb_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.c (renamed from drivers/media/dvb/frontends/dib3000mc.c)0
-rw-r--r--drivers/media/dvb-frontends/dib3000mc.h (renamed from drivers/media/dvb/frontends/dib3000mc.h)0
-rw-r--r--drivers/media/dvb-frontends/dib7000m.c (renamed from drivers/media/dvb/frontends/dib7000m.c)0
-rw-r--r--drivers/media/dvb-frontends/dib7000m.h (renamed from drivers/media/dvb/frontends/dib7000m.h)0
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c (renamed from drivers/media/dvb/frontends/dib7000p.c)0
-rw-r--r--drivers/media/dvb-frontends/dib7000p.h (renamed from drivers/media/dvb/frontends/dib7000p.h)0
-rw-r--r--drivers/media/dvb-frontends/dib8000.c (renamed from drivers/media/dvb/frontends/dib8000.c)0
-rw-r--r--drivers/media/dvb-frontends/dib8000.h (renamed from drivers/media/dvb/frontends/dib8000.h)0
-rw-r--r--drivers/media/dvb-frontends/dib9000.c (renamed from drivers/media/dvb/frontends/dib9000.c)0
-rw-r--r--drivers/media/dvb-frontends/dib9000.h (renamed from drivers/media/dvb/frontends/dib9000.h)0
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.c (renamed from drivers/media/dvb/frontends/dibx000_common.c)0
-rw-r--r--drivers/media/dvb-frontends/dibx000_common.h (renamed from drivers/media/dvb/frontends/dibx000_common.h)0
-rw-r--r--drivers/media/dvb-frontends/drxd.h (renamed from drivers/media/dvb/frontends/drxd.h)0
-rw-r--r--drivers/media/dvb-frontends/drxd_firm.c (renamed from drivers/media/dvb/frontends/drxd_firm.c)0
-rw-r--r--drivers/media/dvb-frontends/drxd_firm.h (renamed from drivers/media/dvb/frontends/drxd_firm.h)0
-rw-r--r--drivers/media/dvb-frontends/drxd_hard.c (renamed from drivers/media/dvb/frontends/drxd_hard.c)0
-rw-r--r--drivers/media/dvb-frontends/drxd_map_firm.h (renamed from drivers/media/dvb/frontends/drxd_map_firm.h)0
-rw-r--r--drivers/media/dvb-frontends/drxk.h68
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c6647
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.h (renamed from drivers/media/dvb/frontends/drxk_hard.h)0
-rw-r--r--drivers/media/dvb-frontends/drxk_map.h (renamed from drivers/media/dvb/frontends/drxk_map.h)0
-rw-r--r--drivers/media/dvb-frontends/ds3000.c (renamed from drivers/media/dvb/frontends/ds3000.c)0
-rw-r--r--drivers/media/dvb-frontends/ds3000.h (renamed from drivers/media/dvb/frontends/ds3000.h)0
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c820
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.h57
-rw-r--r--drivers/media/dvb-frontends/dvb_dummy_fe.c (renamed from drivers/media/dvb/frontends/dvb_dummy_fe.c)0
-rw-r--r--drivers/media/dvb-frontends/dvb_dummy_fe.h (renamed from drivers/media/dvb/frontends/dvb_dummy_fe.h)0
-rw-r--r--drivers/media/dvb-frontends/ec100.c345
-rw-r--r--drivers/media/dvb-frontends/ec100.h46
-rw-r--r--drivers/media/dvb-frontends/eds1547.h (renamed from drivers/media/dvb/frontends/eds1547.h)0
-rw-r--r--drivers/media/dvb-frontends/hd29l2.c866
-rw-r--r--drivers/media/dvb-frontends/hd29l2.h66
-rw-r--r--drivers/media/dvb-frontends/hd29l2_priv.h301
-rw-r--r--drivers/media/dvb-frontends/isl6405.c (renamed from drivers/media/dvb/frontends/isl6405.c)0
-rw-r--r--drivers/media/dvb-frontends/isl6405.h (renamed from drivers/media/dvb/frontends/isl6405.h)0
-rw-r--r--drivers/media/dvb-frontends/isl6421.c (renamed from drivers/media/dvb/frontends/isl6421.c)0
-rw-r--r--drivers/media/dvb-frontends/isl6421.h (renamed from drivers/media/dvb/frontends/isl6421.h)0
-rw-r--r--drivers/media/dvb-frontends/isl6423.c (renamed from drivers/media/dvb/frontends/isl6423.c)0
-rw-r--r--drivers/media/dvb-frontends/isl6423.h (renamed from drivers/media/dvb/frontends/isl6423.h)0
-rw-r--r--drivers/media/dvb-frontends/it913x-fe-priv.h (renamed from drivers/media/dvb/frontends/it913x-fe-priv.h)0
-rw-r--r--drivers/media/dvb-frontends/it913x-fe.c1045
-rw-r--r--drivers/media/dvb-frontends/it913x-fe.h (renamed from drivers/media/dvb/frontends/it913x-fe.h)0
-rw-r--r--drivers/media/dvb-frontends/itd1000.c (renamed from drivers/media/dvb/frontends/itd1000.c)0
-rw-r--r--drivers/media/dvb-frontends/itd1000.h (renamed from drivers/media/dvb/frontends/itd1000.h)0
-rw-r--r--drivers/media/dvb-frontends/itd1000_priv.h (renamed from drivers/media/dvb/frontends/itd1000_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/ix2505v.c (renamed from drivers/media/dvb/frontends/ix2505v.c)0
-rw-r--r--drivers/media/dvb-frontends/ix2505v.h (renamed from drivers/media/dvb/frontends/ix2505v.h)0
-rw-r--r--drivers/media/dvb-frontends/l64781.c (renamed from drivers/media/dvb/frontends/l64781.c)0
-rw-r--r--drivers/media/dvb-frontends/l64781.h (renamed from drivers/media/dvb/frontends/l64781.h)0
-rw-r--r--drivers/media/dvb-frontends/lg2160.c (renamed from drivers/media/dvb/frontends/lg2160.c)0
-rw-r--r--drivers/media/dvb-frontends/lg2160.h (renamed from drivers/media/dvb/frontends/lg2160.h)0
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.c (renamed from drivers/media/dvb/frontends/lgdt3305.c)0
-rw-r--r--drivers/media/dvb-frontends/lgdt3305.h (renamed from drivers/media/dvb/frontends/lgdt3305.h)0
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c (renamed from drivers/media/dvb/frontends/lgdt330x.c)0
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.h (renamed from drivers/media/dvb/frontends/lgdt330x.h)0
-rw-r--r--drivers/media/dvb-frontends/lgdt330x_priv.h (renamed from drivers/media/dvb/frontends/lgdt330x_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/lgs8gl5.c453
-rw-r--r--drivers/media/dvb-frontends/lgs8gl5.h (renamed from drivers/media/dvb/frontends/lgs8gl5.h)0
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.c1075
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx.h (renamed from drivers/media/dvb/frontends/lgs8gxx.h)0
-rw-r--r--drivers/media/dvb-frontends/lgs8gxx_priv.h (renamed from drivers/media/dvb/frontends/lgs8gxx_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/lnbh24.h (renamed from drivers/media/dvb/frontends/lnbh24.h)0
-rw-r--r--drivers/media/dvb-frontends/lnbp21.c (renamed from drivers/media/dvb/frontends/lnbp21.c)0
-rw-r--r--drivers/media/dvb-frontends/lnbp21.h (renamed from drivers/media/dvb/frontends/lnbp21.h)0
-rw-r--r--drivers/media/dvb-frontends/lnbp22.c (renamed from drivers/media/dvb/frontends/lnbp22.c)0
-rw-r--r--drivers/media/dvb-frontends/lnbp22.h (renamed from drivers/media/dvb/frontends/lnbp22.h)0
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.c919
-rw-r--r--drivers/media/dvb-frontends/m88rs2000.h (renamed from drivers/media/dvb/frontends/m88rs2000.h)0
-rw-r--r--drivers/media/dvb-frontends/mb86a16.c (renamed from drivers/media/dvb/frontends/mb86a16.c)0
-rw-r--r--drivers/media/dvb-frontends/mb86a16.h (renamed from drivers/media/dvb/frontends/mb86a16.h)0
-rw-r--r--drivers/media/dvb-frontends/mb86a16_priv.h (renamed from drivers/media/dvb/frontends/mb86a16_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.c (renamed from drivers/media/dvb/frontends/mb86a20s.c)0
-rw-r--r--drivers/media/dvb-frontends/mb86a20s.h (renamed from drivers/media/dvb/frontends/mb86a20s.h)0
-rw-r--r--drivers/media/dvb-frontends/mt312.c (renamed from drivers/media/dvb/frontends/mt312.c)0
-rw-r--r--drivers/media/dvb-frontends/mt312.h (renamed from drivers/media/dvb/frontends/mt312.h)0
-rw-r--r--drivers/media/dvb-frontends/mt312_priv.h (renamed from drivers/media/dvb/frontends/mt312_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/mt352.c (renamed from drivers/media/dvb/frontends/mt352.c)0
-rw-r--r--drivers/media/dvb-frontends/mt352.h (renamed from drivers/media/dvb/frontends/mt352.h)0
-rw-r--r--drivers/media/dvb-frontends/mt352_priv.h (renamed from drivers/media/dvb/frontends/mt352_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/nxt200x.c1242
-rw-r--r--drivers/media/dvb-frontends/nxt200x.h (renamed from drivers/media/dvb/frontends/nxt200x.h)0
-rw-r--r--drivers/media/dvb-frontends/nxt6000.c (renamed from drivers/media/dvb/frontends/nxt6000.c)0
-rw-r--r--drivers/media/dvb-frontends/nxt6000.h (renamed from drivers/media/dvb/frontends/nxt6000.h)0
-rw-r--r--drivers/media/dvb-frontends/nxt6000_priv.h (renamed from drivers/media/dvb/frontends/nxt6000_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/or51132.c (renamed from drivers/media/dvb/frontends/or51132.c)0
-rw-r--r--drivers/media/dvb-frontends/or51132.h (renamed from drivers/media/dvb/frontends/or51132.h)0
-rw-r--r--drivers/media/dvb-frontends/or51211.c (renamed from drivers/media/dvb/frontends/or51211.c)0
-rw-r--r--drivers/media/dvb-frontends/or51211.h (renamed from drivers/media/dvb/frontends/or51211.h)0
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c761
-rw-r--r--drivers/media/dvb-frontends/rtl2830.h90
-rw-r--r--drivers/media/dvb-frontends/rtl2830_priv.h45
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c932
-rw-r--r--drivers/media/dvb-frontends/rtl2832.h75
-rw-r--r--drivers/media/dvb-frontends/rtl2832_priv.h342
-rw-r--r--drivers/media/dvb-frontends/s5h1409.c (renamed from drivers/media/dvb/frontends/s5h1409.c)0
-rw-r--r--drivers/media/dvb-frontends/s5h1409.h (renamed from drivers/media/dvb/frontends/s5h1409.h)0
-rw-r--r--drivers/media/dvb-frontends/s5h1411.c (renamed from drivers/media/dvb/frontends/s5h1411.c)0
-rw-r--r--drivers/media/dvb-frontends/s5h1411.h (renamed from drivers/media/dvb/frontends/s5h1411.h)0
-rw-r--r--drivers/media/dvb-frontends/s5h1420.c (renamed from drivers/media/dvb/frontends/s5h1420.c)0
-rw-r--r--drivers/media/dvb-frontends/s5h1420.h (renamed from drivers/media/dvb/frontends/s5h1420.h)0
-rw-r--r--drivers/media/dvb-frontends/s5h1420_priv.h (renamed from drivers/media/dvb/frontends/s5h1420_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/s5h1432.c (renamed from drivers/media/dvb/frontends/s5h1432.c)0
-rw-r--r--drivers/media/dvb-frontends/s5h1432.h (renamed from drivers/media/dvb/frontends/s5h1432.h)0
-rw-r--r--drivers/media/dvb-frontends/s921.c (renamed from drivers/media/dvb/frontends/s921.c)0
-rw-r--r--drivers/media/dvb-frontends/s921.h (renamed from drivers/media/dvb/frontends/s921.h)0
-rw-r--r--drivers/media/dvb-frontends/si21xx.c (renamed from drivers/media/dvb/frontends/si21xx.c)0
-rw-r--r--drivers/media/dvb-frontends/si21xx.h (renamed from drivers/media/dvb/frontends/si21xx.h)0
-rw-r--r--drivers/media/dvb-frontends/sp8870.c (renamed from drivers/media/dvb/frontends/sp8870.c)0
-rw-r--r--drivers/media/dvb-frontends/sp8870.h (renamed from drivers/media/dvb/frontends/sp8870.h)0
-rw-r--r--drivers/media/dvb-frontends/sp887x.c (renamed from drivers/media/dvb/frontends/sp887x.c)0
-rw-r--r--drivers/media/dvb-frontends/sp887x.h (renamed from drivers/media/dvb/frontends/sp887x.h)0
-rw-r--r--drivers/media/dvb-frontends/stb0899_algo.c (renamed from drivers/media/dvb/frontends/stb0899_algo.c)0
-rw-r--r--drivers/media/dvb-frontends/stb0899_cfg.h (renamed from drivers/media/dvb/frontends/stb0899_cfg.h)0
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.c1652
-rw-r--r--drivers/media/dvb-frontends/stb0899_drv.h (renamed from drivers/media/dvb/frontends/stb0899_drv.h)0
-rw-r--r--drivers/media/dvb-frontends/stb0899_priv.h (renamed from drivers/media/dvb/frontends/stb0899_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/stb0899_reg.h (renamed from drivers/media/dvb/frontends/stb0899_reg.h)0
-rw-r--r--drivers/media/dvb-frontends/stb6000.c (renamed from drivers/media/dvb/frontends/stb6000.c)0
-rw-r--r--drivers/media/dvb-frontends/stb6000.h (renamed from drivers/media/dvb/frontends/stb6000.h)0
-rw-r--r--drivers/media/dvb-frontends/stb6100.c (renamed from drivers/media/dvb/frontends/stb6100.c)0
-rw-r--r--drivers/media/dvb-frontends/stb6100.h (renamed from drivers/media/dvb/frontends/stb6100.h)0
-rw-r--r--drivers/media/dvb-frontends/stb6100_cfg.h (renamed from drivers/media/dvb/frontends/stb6100_cfg.h)0
-rw-r--r--drivers/media/dvb-frontends/stb6100_proc.h (renamed from drivers/media/dvb/frontends/stb6100_proc.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0288.c (renamed from drivers/media/dvb/frontends/stv0288.c)0
-rw-r--r--drivers/media/dvb-frontends/stv0288.h (renamed from drivers/media/dvb/frontends/stv0288.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0297.c (renamed from drivers/media/dvb/frontends/stv0297.c)0
-rw-r--r--drivers/media/dvb-frontends/stv0297.h (renamed from drivers/media/dvb/frontends/stv0297.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0299.c (renamed from drivers/media/dvb/frontends/stv0299.c)0
-rw-r--r--drivers/media/dvb-frontends/stv0299.h (renamed from drivers/media/dvb/frontends/stv0299.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0367.c (renamed from drivers/media/dvb/frontends/stv0367.c)0
-rw-r--r--drivers/media/dvb-frontends/stv0367.h (renamed from drivers/media/dvb/frontends/stv0367.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0367_priv.h (renamed from drivers/media/dvb/frontends/stv0367_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0367_regs.h (renamed from drivers/media/dvb/frontends/stv0367_regs.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0900.h (renamed from drivers/media/dvb/frontends/stv0900.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c (renamed from drivers/media/dvb/frontends/stv0900_core.c)0
-rw-r--r--drivers/media/dvb-frontends/stv0900_init.h (renamed from drivers/media/dvb/frontends/stv0900_init.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0900_priv.h (renamed from drivers/media/dvb/frontends/stv0900_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0900_reg.h (renamed from drivers/media/dvb/frontends/stv0900_reg.h)0
-rw-r--r--drivers/media/dvb-frontends/stv0900_sw.c (renamed from drivers/media/dvb/frontends/stv0900_sw.c)0
-rw-r--r--drivers/media/dvb-frontends/stv090x.c4855
-rw-r--r--drivers/media/dvb-frontends/stv090x.h (renamed from drivers/media/dvb/frontends/stv090x.h)0
-rw-r--r--drivers/media/dvb-frontends/stv090x_priv.h (renamed from drivers/media/dvb/frontends/stv090x_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/stv090x_reg.h (renamed from drivers/media/dvb/frontends/stv090x_reg.h)0
-rw-r--r--drivers/media/dvb-frontends/stv6110.c (renamed from drivers/media/dvb/frontends/stv6110.c)0
-rw-r--r--drivers/media/dvb-frontends/stv6110.h (renamed from drivers/media/dvb/frontends/stv6110.h)0
-rw-r--r--drivers/media/dvb-frontends/stv6110x.c (renamed from drivers/media/dvb/frontends/stv6110x.c)0
-rw-r--r--drivers/media/dvb-frontends/stv6110x.h (renamed from drivers/media/dvb/frontends/stv6110x.h)0
-rw-r--r--drivers/media/dvb-frontends/stv6110x_priv.h (renamed from drivers/media/dvb/frontends/stv6110x_priv.h)0
-rw-r--r--drivers/media/dvb-frontends/stv6110x_reg.h (renamed from drivers/media/dvb/frontends/stv6110x_reg.h)0
-rw-r--r--drivers/media/dvb-frontends/tda10021.c (renamed from drivers/media/dvb/frontends/tda10021.c)0
-rw-r--r--drivers/media/dvb-frontends/tda10023.c (renamed from drivers/media/dvb/frontends/tda10023.c)0
-rw-r--r--drivers/media/dvb-frontends/tda1002x.h (renamed from drivers/media/dvb/frontends/tda1002x.h)0
-rw-r--r--drivers/media/dvb-frontends/tda10048.c (renamed from drivers/media/dvb/frontends/tda10048.c)0
-rw-r--r--drivers/media/dvb-frontends/tda10048.h (renamed from drivers/media/dvb/frontends/tda10048.h)0
-rw-r--r--drivers/media/dvb-frontends/tda1004x.c1385
-rw-r--r--drivers/media/dvb-frontends/tda1004x.h (renamed from drivers/media/dvb/frontends/tda1004x.h)0
-rw-r--r--drivers/media/dvb-frontends/tda10071.c1285
-rw-r--r--drivers/media/dvb-frontends/tda10071.h (renamed from drivers/media/dvb/frontends/tda10071.h)0
-rw-r--r--drivers/media/dvb-frontends/tda10071_priv.h109
-rw-r--r--drivers/media/dvb-frontends/tda10086.c (renamed from drivers/media/dvb/frontends/tda10086.c)0
-rw-r--r--drivers/media/dvb-frontends/tda10086.h (renamed from drivers/media/dvb/frontends/tda10086.h)0
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.c (renamed from drivers/media/dvb/frontends/tda18271c2dd.c)0
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd.h (renamed from drivers/media/dvb/frontends/tda18271c2dd.h)0
-rw-r--r--drivers/media/dvb-frontends/tda18271c2dd_maps.h (renamed from drivers/media/dvb/frontends/tda18271c2dd_maps.h)0
-rw-r--r--drivers/media/dvb-frontends/tda665x.c (renamed from drivers/media/dvb/frontends/tda665x.c)0
-rw-r--r--drivers/media/dvb-frontends/tda665x.h (renamed from drivers/media/dvb/frontends/tda665x.h)0
-rw-r--r--drivers/media/dvb-frontends/tda8083.c (renamed from drivers/media/dvb/frontends/tda8083.c)0
-rw-r--r--drivers/media/dvb-frontends/tda8083.h (renamed from drivers/media/dvb/frontends/tda8083.h)0
-rw-r--r--drivers/media/dvb-frontends/tda8261.c230
-rw-r--r--drivers/media/dvb-frontends/tda8261.h (renamed from drivers/media/dvb/frontends/tda8261.h)0
-rw-r--r--drivers/media/dvb-frontends/tda8261_cfg.h (renamed from drivers/media/dvb/frontends/tda8261_cfg.h)0
-rw-r--r--drivers/media/dvb-frontends/tda826x.c (renamed from drivers/media/dvb/frontends/tda826x.c)0
-rw-r--r--drivers/media/dvb-frontends/tda826x.h (renamed from drivers/media/dvb/frontends/tda826x.h)0
-rw-r--r--drivers/media/dvb-frontends/tdhd1.h (renamed from drivers/media/dvb/frontends/tdhd1.h)0
-rw-r--r--drivers/media/dvb-frontends/tua6100.c (renamed from drivers/media/dvb/frontends/tua6100.c)0
-rw-r--r--drivers/media/dvb-frontends/tua6100.h (renamed from drivers/media/dvb/frontends/tua6100.h)0
-rw-r--r--drivers/media/dvb-frontends/ves1820.c (renamed from drivers/media/dvb/frontends/ves1820.c)0
-rw-r--r--drivers/media/dvb-frontends/ves1820.h (renamed from drivers/media/dvb/frontends/ves1820.h)0
-rw-r--r--drivers/media/dvb-frontends/ves1x93.c (renamed from drivers/media/dvb/frontends/ves1x93.c)0
-rw-r--r--drivers/media/dvb-frontends/ves1x93.h (renamed from drivers/media/dvb/frontends/ves1x93.h)0
-rw-r--r--drivers/media/dvb-frontends/z0194a.h (renamed from drivers/media/dvb/frontends/z0194a.h)0
-rw-r--r--drivers/media/dvb-frontends/zl10036.c (renamed from drivers/media/dvb/frontends/zl10036.c)0
-rw-r--r--drivers/media/dvb-frontends/zl10036.h (renamed from drivers/media/dvb/frontends/zl10036.h)0
-rw-r--r--drivers/media/dvb-frontends/zl10039.c (renamed from drivers/media/dvb/frontends/zl10039.c)0
-rw-r--r--drivers/media/dvb-frontends/zl10039.h (renamed from drivers/media/dvb/frontends/zl10039.h)0
-rw-r--r--drivers/media/dvb-frontends/zl10353.c (renamed from drivers/media/dvb/frontends/zl10353.c)0
-rw-r--r--drivers/media/dvb-frontends/zl10353.h (renamed from drivers/media/dvb/frontends/zl10353.h)0
-rw-r--r--drivers/media/dvb-frontends/zl10353_priv.h (renamed from drivers/media/dvb/frontends/zl10353_priv.h)0
-rw-r--r--drivers/media/dvb/Kconfig91
-rw-r--r--drivers/media/dvb/Makefile21
-rw-r--r--drivers/media/dvb/b2c2/Kconfig45
-rw-r--r--drivers/media/dvb/b2c2/Makefile16
-rw-r--r--drivers/media/dvb/b2c2/flexcop-usb.c590
-rw-r--r--drivers/media/dvb/b2c2/flexcop.c324
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig22
-rw-r--r--drivers/media/dvb/bt8xx/Makefile6
-rw-r--r--drivers/media/dvb/bt8xx/dst_ca.c725
-rw-r--r--drivers/media/dvb/ddbridge/Kconfig18
-rw-r--r--drivers/media/dvb/ddbridge/Makefile14
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c1723
-rw-r--r--drivers/media/dvb/dm1105/Kconfig20
-rw-r--r--drivers/media/dvb/dm1105/Makefile3
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c1277
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.c1309
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c2563
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h423
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c507
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h172
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig440
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile121
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c1952
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h132
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.c1242
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.h113
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c1406
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.h332
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c252
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.h39
-rw-r--r--drivers/media/dvb/dvb-usb/az6007.c955
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.c324
-rw-r--r--drivers/media/dvb/dvb-usb/ce6230.h69
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c845
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c289
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h361
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h485
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.c435
-rw-r--r--drivers/media/dvb/dvb-usb/ec168.h73
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c217
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.h15
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c931
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c1407
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.c525
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c1835
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.h158
-rw-r--r--drivers/media/dvb/dvb-usb/pctv452e.c1064
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c1428
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.h264
-rw-r--r--drivers/media/dvb/dvb-usb/ttusb2.c820
-rw-r--r--drivers/media/dvb/firewire/Makefile6
-rw-r--r--drivers/media/dvb/frontends/Kconfig756
-rw-r--r--drivers/media/dvb/frontends/Makefile105
-rw-r--r--drivers/media/dvb/frontends/af9013.c1524
-rw-r--r--drivers/media/dvb/frontends/af9013.h118
-rw-r--r--drivers/media/dvb/frontends/af9013_priv.h922
-rw-r--r--drivers/media/dvb/frontends/af9033.c980
-rw-r--r--drivers/media/dvb/frontends/af9033.h75
-rw-r--r--drivers/media/dvb/frontends/af9033_priv.h470
-rw-r--r--drivers/media/dvb/frontends/atbm8830.c508
-rw-r--r--drivers/media/dvb/frontends/au8522_common.c259
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c842
-rw-r--r--drivers/media/dvb/frontends/au8522_dig.c826
-rw-r--r--drivers/media/dvb/frontends/au8522_priv.h423
-rw-r--r--drivers/media/dvb/frontends/cxd2820r.h94
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_c.c346
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_core.c646
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_priv.h156
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t.c453
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_t2.c426
-rw-r--r--drivers/media/dvb/frontends/drxk.h66
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c6637
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c794
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h56
-rw-r--r--drivers/media/dvb/frontends/ec100.c335
-rw-r--r--drivers/media/dvb/frontends/ec100.h46
-rw-r--r--drivers/media/dvb/frontends/ec100_priv.h39
-rw-r--r--drivers/media/dvb/frontends/hd29l2.c861
-rw-r--r--drivers/media/dvb/frontends/hd29l2.h66
-rw-r--r--drivers/media/dvb/frontends/hd29l2_priv.h314
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c1045
-rw-r--r--drivers/media/dvb/frontends/lgs8gl5.c453
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx.c1075
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.c919
-rw-r--r--drivers/media/dvb/frontends/nxt200x.c1240
-rw-r--r--drivers/media/dvb/frontends/rtl2830.c757
-rw-r--r--drivers/media/dvb/frontends/rtl2830.h97
-rw-r--r--drivers/media/dvb/frontends/rtl2830_priv.h58
-rw-r--r--drivers/media/dvb/frontends/rtl2832.c789
-rw-r--r--drivers/media/dvb/frontends/rtl2832.h74
-rw-r--r--drivers/media/dvb/frontends/rtl2832_priv.h260
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c1651
-rw-r--r--drivers/media/dvb/frontends/stv090x.c4823
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c1381
-rw-r--r--drivers/media/dvb/frontends/tda10071.c1284
-rw-r--r--drivers/media/dvb/frontends/tda10071_priv.h109
-rw-r--r--drivers/media/dvb/frontends/tda8261.c230
-rw-r--r--drivers/media/dvb/mantis/Kconfig38
-rw-r--r--drivers/media/dvb/mantis/Makefile28
-rw-r--r--drivers/media/dvb/mantis/mantis_cards.c307
-rw-r--r--drivers/media/dvb/mantis/mantis_core.c235
-rw-r--r--drivers/media/dvb/mantis/mantis_dvb.c299
-rw-r--r--drivers/media/dvb/ngene/Kconfig13
-rw-r--r--drivers/media/dvb/ngene/Makefile14
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c560
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c1713
-rw-r--r--drivers/media/dvb/pluto2/Makefile3
-rw-r--r--drivers/media/dvb/pt1/Makefile5
-rw-r--r--drivers/media/dvb/pt1/va1j5jf8007s.c735
-rw-r--r--drivers/media/dvb/siano/Kconfig34
-rw-r--r--drivers/media/dvb/siano/Makefile11
-rw-r--r--drivers/media/dvb/siano/smsusb.c568
-rw-r--r--drivers/media/dvb/ttpci/Kconfig159
-rw-r--r--drivers/media/dvb/ttpci/Makefile21
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c966
-rw-r--r--drivers/media/dvb/ttpci/budget.c811
-rw-r--r--drivers/media/dvb/ttusb-budget/Kconfig18
-rw-r--r--drivers/media/dvb/ttusb-budget/Makefile3
-rw-r--r--drivers/media/dvb/ttusb-dec/Makefile3
-rw-r--r--drivers/media/firewire/Kconfig (renamed from drivers/media/dvb/firewire/Kconfig)0
-rw-r--r--drivers/media/firewire/Makefile6
-rw-r--r--drivers/media/firewire/firedtv-avc.c (renamed from drivers/media/dvb/firewire/firedtv-avc.c)0
-rw-r--r--drivers/media/firewire/firedtv-ci.c (renamed from drivers/media/dvb/firewire/firedtv-ci.c)0
-rw-r--r--drivers/media/firewire/firedtv-dvb.c (renamed from drivers/media/dvb/firewire/firedtv-dvb.c)0
-rw-r--r--drivers/media/firewire/firedtv-fe.c (renamed from drivers/media/dvb/firewire/firedtv-fe.c)0
-rw-r--r--drivers/media/firewire/firedtv-fw.c (renamed from drivers/media/dvb/firewire/firedtv-fw.c)0
-rw-r--r--drivers/media/firewire/firedtv-rc.c (renamed from drivers/media/dvb/firewire/firedtv-rc.c)0
-rw-r--r--drivers/media/firewire/firedtv.h (renamed from drivers/media/dvb/firewire/firedtv.h)0
-rw-r--r--drivers/media/i2c/Kconfig591
-rw-r--r--drivers/media/i2c/Makefile67
-rw-r--r--drivers/media/i2c/ad9389b.c1328
-rw-r--r--drivers/media/i2c/adp1653.c487
-rw-r--r--drivers/media/i2c/adv7170.c (renamed from drivers/media/video/adv7170.c)0
-rw-r--r--drivers/media/i2c/adv7175.c (renamed from drivers/media/video/adv7175.c)0
-rw-r--r--drivers/media/i2c/adv7180.c (renamed from drivers/media/video/adv7180.c)0
-rw-r--r--drivers/media/i2c/adv7183.c (renamed from drivers/media/video/adv7183.c)0
-rw-r--r--drivers/media/i2c/adv7183_regs.h (renamed from drivers/media/video/adv7183_regs.h)0
-rw-r--r--drivers/media/i2c/adv7343.c (renamed from drivers/media/video/adv7343.c)0
-rw-r--r--drivers/media/i2c/adv7343_regs.h (renamed from drivers/media/video/adv7343_regs.h)0
-rw-r--r--drivers/media/i2c/adv7393.c (renamed from drivers/media/video/adv7393.c)0
-rw-r--r--drivers/media/i2c/adv7393_regs.h (renamed from drivers/media/video/adv7393_regs.h)0
-rw-r--r--drivers/media/i2c/adv7604.c1959
-rw-r--r--drivers/media/i2c/ak881x.c (renamed from drivers/media/video/ak881x.c)0
-rw-r--r--drivers/media/i2c/aptina-pll.c (renamed from drivers/media/video/aptina-pll.c)0
-rw-r--r--drivers/media/i2c/aptina-pll.h (renamed from drivers/media/video/aptina-pll.h)0
-rw-r--r--drivers/media/i2c/as3645a.c888
-rw-r--r--drivers/media/i2c/bt819.c (renamed from drivers/media/video/bt819.c)0
-rw-r--r--drivers/media/i2c/bt856.c (renamed from drivers/media/video/bt856.c)0
-rw-r--r--drivers/media/i2c/bt866.c (renamed from drivers/media/video/bt866.c)0
-rw-r--r--drivers/media/i2c/btcx-risc.c (renamed from drivers/media/video/btcx-risc.c)0
-rw-r--r--drivers/media/i2c/btcx-risc.h (renamed from drivers/media/video/btcx-risc.h)0
-rw-r--r--drivers/media/i2c/cs5345.c (renamed from drivers/media/video/cs5345.c)0
-rw-r--r--drivers/media/i2c/cs53l32a.c (renamed from drivers/media/video/cs53l32a.c)0
-rw-r--r--drivers/media/i2c/cx2341x.c (renamed from drivers/media/video/cx2341x.c)0
-rw-r--r--drivers/media/i2c/cx25840/Kconfig (renamed from drivers/media/video/cx25840/Kconfig)0
-rw-r--r--drivers/media/i2c/cx25840/Makefile6
-rw-r--r--drivers/media/i2c/cx25840/cx25840-audio.c (renamed from drivers/media/video/cx25840/cx25840-audio.c)0
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c (renamed from drivers/media/video/cx25840/cx25840-core.c)0
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.h (renamed from drivers/media/video/cx25840/cx25840-core.h)0
-rw-r--r--drivers/media/i2c/cx25840/cx25840-firmware.c175
-rw-r--r--drivers/media/i2c/cx25840/cx25840-ir.c (renamed from drivers/media/video/cx25840/cx25840-ir.c)0
-rw-r--r--drivers/media/i2c/cx25840/cx25840-vbi.c257
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c (renamed from drivers/media/video/ir-kbd-i2c.c)0
-rw-r--r--drivers/media/i2c/ks0127.c733
-rw-r--r--drivers/media/i2c/ks0127.h (renamed from drivers/media/video/ks0127.h)0
-rw-r--r--drivers/media/i2c/m52790.c (renamed from drivers/media/video/m52790.c)0
-rw-r--r--drivers/media/i2c/m5mols/Kconfig (renamed from drivers/media/video/m5mols/Kconfig)0
-rw-r--r--drivers/media/i2c/m5mols/Makefile (renamed from drivers/media/video/m5mols/Makefile)0
-rw-r--r--drivers/media/i2c/m5mols/m5mols.h338
-rw-r--r--drivers/media/i2c/m5mols/m5mols_capture.c (renamed from drivers/media/video/m5mols/m5mols_capture.c)0
-rw-r--r--drivers/media/i2c/m5mols/m5mols_controls.c (renamed from drivers/media/video/m5mols/m5mols_controls.c)0
-rw-r--r--drivers/media/i2c/m5mols/m5mols_core.c1010
-rw-r--r--drivers/media/i2c/m5mols/m5mols_reg.h (renamed from drivers/media/video/m5mols/m5mols_reg.h)0
-rw-r--r--drivers/media/i2c/msp3400-driver.c927
-rw-r--r--drivers/media/i2c/msp3400-driver.h (renamed from drivers/media/video/msp3400-driver.h)0
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c (renamed from drivers/media/video/msp3400-kthreads.c)0
-rw-r--r--drivers/media/i2c/mt9m032.c878
-rw-r--r--drivers/media/i2c/mt9p031.c1071
-rw-r--r--drivers/media/i2c/mt9t001.c (renamed from drivers/media/video/mt9t001.c)0
-rw-r--r--drivers/media/i2c/mt9v011.c (renamed from drivers/media/video/mt9v011.c)0
-rw-r--r--drivers/media/i2c/mt9v032.c853
-rw-r--r--drivers/media/i2c/noon010pc30.c (renamed from drivers/media/video/noon010pc30.c)0
-rw-r--r--drivers/media/i2c/ov7670.c (renamed from drivers/media/video/ov7670.c)0
-rw-r--r--drivers/media/i2c/s5k4ecgx.c1036
-rw-r--r--drivers/media/i2c/s5k6aa.c1664
-rw-r--r--drivers/media/i2c/saa6588.c (renamed from drivers/media/video/saa6588.c)0
-rw-r--r--drivers/media/i2c/saa7110.c (renamed from drivers/media/video/saa7110.c)0
-rw-r--r--drivers/media/i2c/saa7115.c1728
-rw-r--r--drivers/media/i2c/saa711x_regs.h (renamed from drivers/media/video/saa711x_regs.h)0
-rw-r--r--drivers/media/i2c/saa7127.c852
-rw-r--r--drivers/media/i2c/saa717x.c (renamed from drivers/media/video/saa717x.c)0
-rw-r--r--drivers/media/i2c/saa7185.c (renamed from drivers/media/video/saa7185.c)0
-rw-r--r--drivers/media/i2c/saa7191.c (renamed from drivers/media/video/saa7191.c)0
-rw-r--r--drivers/media/i2c/saa7191.h (renamed from drivers/media/video/saa7191.h)0
-rw-r--r--drivers/media/i2c/smiapp-pll.c418
-rw-r--r--drivers/media/i2c/smiapp-pll.h103
-rw-r--r--drivers/media/i2c/smiapp/Kconfig (renamed from drivers/media/video/smiapp/Kconfig)0
-rw-r--r--drivers/media/i2c/smiapp/Makefile5
-rw-r--r--drivers/media/i2c/smiapp/smiapp-core.c2898
-rw-r--r--drivers/media/i2c/smiapp/smiapp-limits.c132
-rw-r--r--drivers/media/i2c/smiapp/smiapp-limits.h128
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.c286
-rw-r--r--drivers/media/i2c/smiapp/smiapp-quirk.h83
-rw-r--r--drivers/media/i2c/smiapp/smiapp-reg-defs.h503
-rw-r--r--drivers/media/i2c/smiapp/smiapp-reg.h122
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.c273
-rw-r--r--drivers/media/i2c/smiapp/smiapp-regs.h (renamed from drivers/media/video/smiapp/smiapp-regs.h)0
-rw-r--r--drivers/media/i2c/smiapp/smiapp.h252
-rw-r--r--drivers/media/i2c/soc_camera/Kconfig89
-rw-r--r--drivers/media/i2c/soc_camera/Makefile14
-rw-r--r--drivers/media/i2c/soc_camera/imx074.c495
-rw-r--r--drivers/media/i2c/soc_camera/mt9m001.c757
-rw-r--r--drivers/media/i2c/soc_camera/mt9m111.c1046
-rw-r--r--drivers/media/i2c/soc_camera/mt9t031.c857
-rw-r--r--drivers/media/i2c/soc_camera/mt9t112.c1142
-rw-r--r--drivers/media/i2c/soc_camera/mt9v022.c919
-rw-r--r--drivers/media/i2c/soc_camera/ov2640.c1122
-rw-r--r--drivers/media/i2c/soc_camera/ov5642.c1088
-rw-r--r--drivers/media/i2c/soc_camera/ov6650.c1069
-rw-r--r--drivers/media/i2c/soc_camera/ov772x.c1137
-rw-r--r--drivers/media/i2c/soc_camera/ov9640.c763
-rw-r--r--drivers/media/i2c/soc_camera/ov9640.h (renamed from drivers/media/video/ov9640.h)0
-rw-r--r--drivers/media/i2c/soc_camera/ov9740.c1020
-rw-r--r--drivers/media/i2c/soc_camera/rj54n1cb0c.c1431
-rw-r--r--drivers/media/i2c/soc_camera/tw9910.c973
-rw-r--r--drivers/media/i2c/sr030pc30.c (renamed from drivers/media/video/sr030pc30.c)0
-rw-r--r--drivers/media/i2c/tcm825x.c937
-rw-r--r--drivers/media/i2c/tcm825x.h200
-rw-r--r--drivers/media/i2c/tda7432.c (renamed from drivers/media/video/tda7432.c)0
-rw-r--r--drivers/media/i2c/tda9840.c (renamed from drivers/media/video/tda9840.c)0
-rw-r--r--drivers/media/i2c/tea6415c.c187
-rw-r--r--drivers/media/i2c/tea6415c.h (renamed from drivers/media/video/tea6415c.h)0
-rw-r--r--drivers/media/i2c/tea6420.c (renamed from drivers/media/video/tea6420.c)0
-rw-r--r--drivers/media/i2c/tea6420.h (renamed from drivers/media/video/tea6420.h)0
-rw-r--r--drivers/media/i2c/ths7303.c (renamed from drivers/media/video/ths7303.c)0
-rw-r--r--drivers/media/i2c/tlv320aic23b.c (renamed from drivers/media/video/tlv320aic23b.c)0
-rw-r--r--drivers/media/i2c/tvaudio.c2131
-rw-r--r--drivers/media/i2c/tveeprom.c (renamed from drivers/media/video/tveeprom.c)0
-rw-r--r--drivers/media/i2c/tvp514x.c1166
-rw-r--r--drivers/media/i2c/tvp514x_regs.h287
-rw-r--r--drivers/media/i2c/tvp5150.c1274
-rw-r--r--drivers/media/i2c/tvp5150_reg.h (renamed from drivers/media/video/tvp5150_reg.h)0
-rw-r--r--drivers/media/i2c/tvp7002.c (renamed from drivers/media/video/tvp7002.c)0
-rw-r--r--drivers/media/i2c/tvp7002_reg.h (renamed from drivers/media/video/tvp7002_reg.h)0
-rw-r--r--drivers/media/i2c/upd64031a.c (renamed from drivers/media/video/upd64031a.c)0
-rw-r--r--drivers/media/i2c/upd64083.c (renamed from drivers/media/video/upd64083.c)0
-rw-r--r--drivers/media/i2c/vp27smpx.c (renamed from drivers/media/video/vp27smpx.c)0
-rw-r--r--drivers/media/i2c/vpx3220.c (renamed from drivers/media/video/vpx3220.c)0
-rw-r--r--drivers/media/i2c/vs6624.c (renamed from drivers/media/video/vs6624.c)0
-rw-r--r--drivers/media/i2c/vs6624_regs.h (renamed from drivers/media/video/vs6624_regs.h)0
-rw-r--r--drivers/media/i2c/wm8739.c (renamed from drivers/media/video/wm8739.c)0
-rw-r--r--drivers/media/i2c/wm8775.c (renamed from drivers/media/video/wm8775.c)0
-rw-r--r--drivers/media/media-device.c4
-rw-r--r--drivers/media/media-devnode.c14
-rw-r--r--drivers/media/mmc/Kconfig2
-rw-r--r--drivers/media/mmc/Makefile1
-rw-r--r--drivers/media/mmc/siano/Kconfig10
-rw-r--r--drivers/media/mmc/siano/Makefile6
-rw-r--r--drivers/media/mmc/siano/smssdio.c (renamed from drivers/media/dvb/siano/smssdio.c)0
-rw-r--r--drivers/media/parport/Kconfig52
-rw-r--r--drivers/media/parport/Makefile4
-rw-r--r--drivers/media/parport/bw-qcam.c (renamed from drivers/media/video/bw-qcam.c)0
-rw-r--r--drivers/media/parport/c-qcam.c (renamed from drivers/media/video/c-qcam.c)0
-rw-r--r--drivers/media/parport/pms.c (renamed from drivers/media/video/pms.c)0
-rw-r--r--drivers/media/parport/w9966.c (renamed from drivers/media/video/w9966.c)0
-rw-r--r--drivers/media/pci/Kconfig47
-rw-r--r--drivers/media/pci/Makefile26
-rw-r--r--drivers/media/pci/b2c2/Kconfig15
-rw-r--r--drivers/media/pci/b2c2/Makefile9
-rw-r--r--drivers/media/pci/b2c2/flexcop-dma.c (renamed from drivers/media/dvb/b2c2/flexcop-dma.c)0
-rw-r--r--drivers/media/pci/b2c2/flexcop-pci.c (renamed from drivers/media/dvb/b2c2/flexcop-pci.c)0
-rw-r--r--drivers/media/pci/bt8xx/Kconfig43
-rw-r--r--drivers/media/pci/bt8xx/Makefile11
-rw-r--r--drivers/media/pci/bt8xx/bt848.h (renamed from drivers/media/video/bt8xx/bt848.h)0
-rw-r--r--drivers/media/pci/bt8xx/bt878.c (renamed from drivers/media/dvb/bt8xx/bt878.c)0
-rw-r--r--drivers/media/pci/bt8xx/bt878.h (renamed from drivers/media/dvb/bt8xx/bt878.h)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.c (renamed from drivers/media/video/bt8xx/bttv-audio-hook.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-audio-hook.h (renamed from drivers/media/video/bt8xx/bttv-audio-hook.h)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-cards.c (renamed from drivers/media/video/bt8xx/bttv-cards.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c4630
-rw-r--r--drivers/media/pci/bt8xx/bttv-gpio.c (renamed from drivers/media/video/bt8xx/bttv-gpio.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-i2c.c (renamed from drivers/media/video/bt8xx/bttv-i2c.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-if.c (renamed from drivers/media/video/bt8xx/bttv-if.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-input.c (renamed from drivers/media/video/bt8xx/bttv-input.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-risc.c (renamed from drivers/media/video/bt8xx/bttv-risc.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv-vbi.c (renamed from drivers/media/video/bt8xx/bttv-vbi.c)0
-rw-r--r--drivers/media/pci/bt8xx/bttv.h (renamed from drivers/media/video/bt8xx/bttv.h)0
-rw-r--r--drivers/media/pci/bt8xx/bttvp.h (renamed from drivers/media/video/bt8xx/bttvp.h)0
-rw-r--r--drivers/media/pci/bt8xx/dst.c (renamed from drivers/media/dvb/bt8xx/dst.c)0
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.c726
-rw-r--r--drivers/media/pci/bt8xx/dst_ca.h (renamed from drivers/media/dvb/bt8xx/dst_ca.h)0
-rw-r--r--drivers/media/pci/bt8xx/dst_common.h (renamed from drivers/media/dvb/bt8xx/dst_common.h)0
-rw-r--r--drivers/media/pci/bt8xx/dst_priv.h (renamed from drivers/media/dvb/bt8xx/dst_priv.h)0
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.c (renamed from drivers/media/dvb/bt8xx/dvb-bt8xx.c)0
-rw-r--r--drivers/media/pci/bt8xx/dvb-bt8xx.h (renamed from drivers/media/dvb/bt8xx/dvb-bt8xx.h)0
-rw-r--r--drivers/media/pci/cx18/Kconfig35
-rw-r--r--drivers/media/pci/cx18/Makefile13
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-main.c (renamed from drivers/media/video/cx18/cx18-alsa-main.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.c (renamed from drivers/media/video/cx18/cx18-alsa-mixer.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-mixer.h (renamed from drivers/media/video/cx18/cx18-alsa-mixer.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.c (renamed from drivers/media/video/cx18/cx18-alsa-pcm.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-alsa-pcm.h (renamed from drivers/media/video/cx18/cx18-alsa-pcm.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-alsa.h (renamed from drivers/media/video/cx18/cx18-alsa.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-audio.c (renamed from drivers/media/video/cx18/cx18-audio.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-audio.h (renamed from drivers/media/video/cx18/cx18-audio.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-av-audio.c (renamed from drivers/media/video/cx18/cx18-av-audio.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c (renamed from drivers/media/video/cx18/cx18-av-core.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.h (renamed from drivers/media/video/cx18/cx18-av-core.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-av-firmware.c225
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c313
-rw-r--r--drivers/media/pci/cx18/cx18-cards.c (renamed from drivers/media/video/cx18/cx18-cards.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-cards.h (renamed from drivers/media/video/cx18/cx18-cards.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c (renamed from drivers/media/video/cx18/cx18-controls.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-controls.h (renamed from drivers/media/video/cx18/cx18-controls.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c1360
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h (renamed from drivers/media/video/cx18/cx18-driver.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.c609
-rw-r--r--drivers/media/pci/cx18/cx18-dvb.h (renamed from drivers/media/video/cx18/cx18-dvb.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c (renamed from drivers/media/video/cx18/cx18-fileops.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.h (renamed from drivers/media/video/cx18/cx18-fileops.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.c459
-rw-r--r--drivers/media/pci/cx18/cx18-firmware.h (renamed from drivers/media/video/cx18/cx18-firmware.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.c (renamed from drivers/media/video/cx18/cx18-gpio.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-gpio.h (renamed from drivers/media/video/cx18/cx18-gpio.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.c (renamed from drivers/media/video/cx18/cx18-i2c.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-i2c.h (renamed from drivers/media/video/cx18/cx18-i2c.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-io.c (renamed from drivers/media/video/cx18/cx18-io.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-io.h (renamed from drivers/media/video/cx18/cx18-io.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c1190
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.h (renamed from drivers/media/video/cx18/cx18-ioctl.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-irq.c (renamed from drivers/media/video/cx18/cx18-irq.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-irq.h (renamed from drivers/media/video/cx18/cx18-irq.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.c (renamed from drivers/media/video/cx18/cx18-mailbox.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-mailbox.h (renamed from drivers/media/video/cx18/cx18-mailbox.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-queue.c (renamed from drivers/media/video/cx18/cx18-queue.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-queue.h (renamed from drivers/media/video/cx18/cx18-queue.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-scb.c (renamed from drivers/media/video/cx18/cx18-scb.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-scb.h (renamed from drivers/media/video/cx18/cx18-scb.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-streams.c1059
-rw-r--r--drivers/media/pci/cx18/cx18-streams.h (renamed from drivers/media/video/cx18/cx18-streams.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.c (renamed from drivers/media/video/cx18/cx18-vbi.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-vbi.h (renamed from drivers/media/video/cx18/cx18-vbi.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-version.h (renamed from drivers/media/video/cx18/cx18-version.h)0
-rw-r--r--drivers/media/pci/cx18/cx18-video.c (renamed from drivers/media/video/cx18/cx18-video.c)0
-rw-r--r--drivers/media/pci/cx18/cx18-video.h (renamed from drivers/media/video/cx18/cx18-video.h)0
-rw-r--r--drivers/media/pci/cx18/cx23418.h (renamed from drivers/media/video/cx18/cx23418.h)0
-rw-r--r--drivers/media/pci/cx23885/Kconfig50
-rw-r--r--drivers/media/pci/cx23885/Makefile15
-rw-r--r--drivers/media/pci/cx23885/altera-ci.c839
-rw-r--r--drivers/media/pci/cx23885/altera-ci.h (renamed from drivers/media/video/cx23885/altera-ci.h)0
-rw-r--r--drivers/media/pci/cx23885/cimax2.c (renamed from drivers/media/video/cx23885/cimax2.c)0
-rw-r--r--drivers/media/pci/cx23885/cimax2.h (renamed from drivers/media/video/cx23885/cimax2.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-417.c1790
-rw-r--r--drivers/media/pci/cx23885/cx23885-alsa.c (renamed from drivers/media/video/cx23885/cx23885-alsa.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.c (renamed from drivers/media/video/cx23885/cx23885-av.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-av.h (renamed from drivers/media/video/cx23885/cx23885-av.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-cards.c1694
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c (renamed from drivers/media/video/cx23885/cx23885-core.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c1412
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.c (renamed from drivers/media/video/cx23885/cx23885-f300.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-f300.h (renamed from drivers/media/video/cx23885/cx23885-f300.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-i2c.c (renamed from drivers/media/video/cx23885/cx23885-i2c.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.c365
-rw-r--r--drivers/media/pci/cx23885/cx23885-input.h (renamed from drivers/media/video/cx23885/cx23885-input.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.c (renamed from drivers/media/video/cx23885/cx23885-ioctl.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-ioctl.h (renamed from drivers/media/video/cx23885/cx23885-ioctl.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.c (renamed from drivers/media/video/cx23885/cx23885-ir.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-ir.h (renamed from drivers/media/video/cx23885/cx23885-ir.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-reg.h (renamed from drivers/media/video/cx23885/cx23885-reg.h)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-vbi.c (renamed from drivers/media/video/cx23885/cx23885-vbi.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c1926
-rw-r--r--drivers/media/pci/cx23885/cx23885.h654
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.c (renamed from drivers/media/video/cx23885/cx23888-ir.c)0
-rw-r--r--drivers/media/pci/cx23885/cx23888-ir.h (renamed from drivers/media/video/cx23885/cx23888-ir.h)0
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.c (renamed from drivers/media/video/cx23885/netup-eeprom.c)0
-rw-r--r--drivers/media/pci/cx23885/netup-eeprom.h (renamed from drivers/media/video/cx23885/netup-eeprom.h)0
-rw-r--r--drivers/media/pci/cx23885/netup-init.c (renamed from drivers/media/video/cx23885/netup-init.c)0
-rw-r--r--drivers/media/pci/cx23885/netup-init.h (renamed from drivers/media/video/cx23885/netup-init.h)0
-rw-r--r--drivers/media/pci/cx25821/Kconfig (renamed from drivers/media/video/cx25821/Kconfig)0
-rw-r--r--drivers/media/pci/cx25821/Makefile13
-rw-r--r--drivers/media/pci/cx25821/cx25821-alsa.c (renamed from drivers/media/video/cx25821/cx25821-alsa.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio-upstream.c (renamed from drivers/media/video/cx25821/cx25821-audio-upstream.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio-upstream.h (renamed from drivers/media/video/cx25821/cx25821-audio-upstream.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-audio.h (renamed from drivers/media/video/cx25821/cx25821-audio.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-biffuncs.h (renamed from drivers/media/video/cx25821/cx25821-biffuncs.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-cards.c (renamed from drivers/media/video/cx25821/cx25821-cards.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-core.c (renamed from drivers/media/video/cx25821/cx25821-core.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-gpio.c (renamed from drivers/media/video/cx25821/cx25821-gpio.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-i2c.c (renamed from drivers/media/video/cx25821/cx25821-i2c.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-defines.h (renamed from drivers/media/video/cx25821/cx25821-medusa-defines.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-reg.h (renamed from drivers/media/video/cx25821/cx25821-medusa-reg.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.c (renamed from drivers/media/video/cx25821/cx25821-medusa-video.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-medusa-video.h (renamed from drivers/media/video/cx25821/cx25821-medusa-video.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-reg.h (renamed from drivers/media/video/cx25821/cx25821-reg.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-sram.h (renamed from drivers/media/video/cx25821/cx25821-sram.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c (renamed from drivers/media/video/cx25821/cx25821-video-upstream-ch2.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h (renamed from drivers/media/video/cx25821/cx25821-video-upstream-ch2.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream.c (renamed from drivers/media/video/cx25821/cx25821-video-upstream.c)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-video-upstream.h (renamed from drivers/media/video/cx25821/cx25821-video-upstream.h)0
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.c1990
-rw-r--r--drivers/media/pci/cx25821/cx25821-video.h186
-rw-r--r--drivers/media/pci/cx25821/cx25821.h (renamed from drivers/media/video/cx25821/cx25821.h)0
-rw-r--r--drivers/media/pci/cx88/Kconfig86
-rw-r--r--drivers/media/pci/cx88/Makefile16
-rw-r--r--drivers/media/pci/cx88/cx88-alsa.c975
-rw-r--r--drivers/media/pci/cx88/cx88-blackbird.c1299
-rw-r--r--drivers/media/pci/cx88/cx88-cards.c3811
-rw-r--r--drivers/media/pci/cx88/cx88-core.c1131
-rw-r--r--drivers/media/pci/cx88/cx88-dsp.c (renamed from drivers/media/video/cx88/cx88-dsp.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-dvb.c1778
-rw-r--r--drivers/media/pci/cx88/cx88-i2c.c (renamed from drivers/media/video/cx88/cx88-i2c.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-input.c (renamed from drivers/media/video/cx88/cx88-input.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c (renamed from drivers/media/video/cx88/cx88-mpeg.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-reg.h (renamed from drivers/media/video/cx88/cx88-reg.h)0
-rw-r--r--drivers/media/pci/cx88/cx88-tvaudio.c (renamed from drivers/media/video/cx88/cx88-tvaudio.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-vbi.c (renamed from drivers/media/video/cx88/cx88-vbi.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-video.c2075
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.c (renamed from drivers/media/video/cx88/cx88-vp3054-i2c.c)0
-rw-r--r--drivers/media/pci/cx88/cx88-vp3054-i2c.h (renamed from drivers/media/video/cx88/cx88-vp3054-i2c.h)0
-rw-r--r--drivers/media/pci/cx88/cx88.h748
-rw-r--r--drivers/media/pci/ddbridge/Kconfig18
-rw-r--r--drivers/media/pci/ddbridge/Makefile14
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-core.c1730
-rw-r--r--drivers/media/pci/ddbridge/ddbridge-regs.h (renamed from drivers/media/dvb/ddbridge/ddbridge-regs.h)0
-rw-r--r--drivers/media/pci/ddbridge/ddbridge.h (renamed from drivers/media/dvb/ddbridge/ddbridge.h)0
-rw-r--r--drivers/media/pci/dm1105/Kconfig20
-rw-r--r--drivers/media/pci/dm1105/Makefile3
-rw-r--r--drivers/media/pci/dm1105/dm1105.c (renamed from drivers/media/dvb/dm1105/dm1105.c)0
-rw-r--r--drivers/media/pci/ivtv/Kconfig61
-rw-r--r--drivers/media/pci/ivtv/Makefile16
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-main.c303
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.c175
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-mixer.h23
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.c356
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa-pcm.h27
-rw-r--r--drivers/media/pci/ivtv/ivtv-alsa.h75
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.c (renamed from drivers/media/video/ivtv/ivtv-cards.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-cards.h (renamed from drivers/media/video/ivtv/ivtv-cards.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c (renamed from drivers/media/video/ivtv/ivtv-controls.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.h (renamed from drivers/media/video/ivtv/ivtv-controls.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c1536
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.h850
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.c1073
-rw-r--r--drivers/media/pci/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.c402
-rw-r--r--drivers/media/pci/ivtv/ivtv-firmware.h (renamed from drivers/media/video/ivtv/ivtv-firmware.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.c (renamed from drivers/media/video/ivtv/ivtv-gpio.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-gpio.h (renamed from drivers/media/video/ivtv/ivtv-gpio.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.c (renamed from drivers/media/video/ivtv/ivtv-i2c.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-i2c.h (renamed from drivers/media/video/ivtv/ivtv-i2c.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c1927
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.h (renamed from drivers/media/video/ivtv/ivtv-ioctl.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.c1088
-rw-r--r--drivers/media/pci/ivtv/ivtv-irq.h (renamed from drivers/media/video/ivtv/ivtv-irq.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.c (renamed from drivers/media/video/ivtv/ivtv-mailbox.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-mailbox.h (renamed from drivers/media/video/ivtv/ivtv-mailbox.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.c (renamed from drivers/media/video/ivtv/ivtv-queue.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-queue.h (renamed from drivers/media/video/ivtv/ivtv-queue.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.c (renamed from drivers/media/video/ivtv/ivtv-routing.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-routing.h (renamed from drivers/media/video/ivtv/ivtv-routing.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.c1039
-rw-r--r--drivers/media/pci/ivtv/ivtv-streams.h (renamed from drivers/media/video/ivtv/ivtv-streams.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.c (renamed from drivers/media/video/ivtv/ivtv-udma.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-udma.h (renamed from drivers/media/video/ivtv/ivtv-udma.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.c (renamed from drivers/media/video/ivtv/ivtv-vbi.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-vbi.h (renamed from drivers/media/video/ivtv/ivtv-vbi.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-version.h (renamed from drivers/media/video/ivtv/ivtv-version.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.c (renamed from drivers/media/video/ivtv/ivtv-yuv.c)0
-rw-r--r--drivers/media/pci/ivtv/ivtv-yuv.h (renamed from drivers/media/video/ivtv/ivtv-yuv.h)0
-rw-r--r--drivers/media/pci/ivtv/ivtvfb.c (renamed from drivers/media/video/ivtv/ivtvfb.c)0
-rw-r--r--drivers/media/pci/mantis/Kconfig38
-rw-r--r--drivers/media/pci/mantis/Makefile28
-rw-r--r--drivers/media/pci/mantis/hopper_cards.c (renamed from drivers/media/dvb/mantis/hopper_cards.c)0
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.c (renamed from drivers/media/dvb/mantis/hopper_vp3028.c)0
-rw-r--r--drivers/media/pci/mantis/hopper_vp3028.h (renamed from drivers/media/dvb/mantis/hopper_vp3028.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_ca.c (renamed from drivers/media/dvb/mantis/mantis_ca.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_ca.h (renamed from drivers/media/dvb/mantis/mantis_ca.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_cards.c307
-rw-r--r--drivers/media/pci/mantis/mantis_common.h (renamed from drivers/media/dvb/mantis/mantis_common.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_core.c235
-rw-r--r--drivers/media/pci/mantis/mantis_core.h (renamed from drivers/media/dvb/mantis/mantis_core.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_dma.c (renamed from drivers/media/dvb/mantis/mantis_dma.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_dma.h (renamed from drivers/media/dvb/mantis/mantis_dma.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.c301
-rw-r--r--drivers/media/pci/mantis/mantis_dvb.h (renamed from drivers/media/dvb/mantis/mantis_dvb.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_evm.c (renamed from drivers/media/dvb/mantis/mantis_evm.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_hif.c (renamed from drivers/media/dvb/mantis/mantis_hif.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_hif.h (renamed from drivers/media/dvb/mantis/mantis_hif.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.c (renamed from drivers/media/dvb/mantis/mantis_i2c.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_i2c.h (renamed from drivers/media/dvb/mantis/mantis_i2c.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_input.c (renamed from drivers/media/dvb/mantis/mantis_input.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.c (renamed from drivers/media/dvb/mantis/mantis_ioc.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_ioc.h (renamed from drivers/media/dvb/mantis/mantis_ioc.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_link.h (renamed from drivers/media/dvb/mantis/mantis_link.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_pci.c (renamed from drivers/media/dvb/mantis/mantis_pci.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_pci.h (renamed from drivers/media/dvb/mantis/mantis_pci.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_pcmcia.c (renamed from drivers/media/dvb/mantis/mantis_pcmcia.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_reg.h (renamed from drivers/media/dvb/mantis/mantis_reg.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_uart.c (renamed from drivers/media/dvb/mantis/mantis_uart.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_uart.h (renamed from drivers/media/dvb/mantis/mantis_uart.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.c (renamed from drivers/media/dvb/mantis/mantis_vp1033.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1033.h (renamed from drivers/media/dvb/mantis/mantis_vp1033.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.c (renamed from drivers/media/dvb/mantis/mantis_vp1034.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1034.h (renamed from drivers/media/dvb/mantis/mantis_vp1034.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.c (renamed from drivers/media/dvb/mantis/mantis_vp1041.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp1041.h (renamed from drivers/media/dvb/mantis/mantis_vp1041.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.c (renamed from drivers/media/dvb/mantis/mantis_vp2033.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp2033.h (renamed from drivers/media/dvb/mantis/mantis_vp2033.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.c (renamed from drivers/media/dvb/mantis/mantis_vp2040.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp2040.h (renamed from drivers/media/dvb/mantis/mantis_vp2040.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.c (renamed from drivers/media/dvb/mantis/mantis_vp3028.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp3028.h (renamed from drivers/media/dvb/mantis/mantis_vp3028.h)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.c (renamed from drivers/media/dvb/mantis/mantis_vp3030.c)0
-rw-r--r--drivers/media/pci/mantis/mantis_vp3030.h (renamed from drivers/media/dvb/mantis/mantis_vp3030.h)0
-rw-r--r--drivers/media/pci/meye/Kconfig13
-rw-r--r--drivers/media/pci/meye/Makefile1
-rw-r--r--drivers/media/pci/meye/meye.c (renamed from drivers/media/video/meye.c)0
-rw-r--r--drivers/media/pci/meye/meye.h (renamed from drivers/media/video/meye.h)0
-rw-r--r--drivers/media/pci/ngene/Kconfig13
-rw-r--r--drivers/media/pci/ngene/Makefile14
-rw-r--r--drivers/media/pci/ngene/ngene-cards.c823
-rw-r--r--drivers/media/pci/ngene/ngene-core.c1707
-rw-r--r--drivers/media/pci/ngene/ngene-dvb.c (renamed from drivers/media/dvb/ngene/ngene-dvb.c)0
-rw-r--r--drivers/media/pci/ngene/ngene-i2c.c (renamed from drivers/media/dvb/ngene/ngene-i2c.c)0
-rw-r--r--drivers/media/pci/ngene/ngene.h (renamed from drivers/media/dvb/ngene/ngene.h)0
-rw-r--r--drivers/media/pci/pluto2/Kconfig (renamed from drivers/media/dvb/pluto2/Kconfig)0
-rw-r--r--drivers/media/pci/pluto2/Makefile3
-rw-r--r--drivers/media/pci/pluto2/pluto2.c (renamed from drivers/media/dvb/pluto2/pluto2.c)0
-rw-r--r--drivers/media/pci/pt1/Kconfig (renamed from drivers/media/dvb/pt1/Kconfig)0
-rw-r--r--drivers/media/pci/pt1/Makefile5
-rw-r--r--drivers/media/pci/pt1/pt1.c (renamed from drivers/media/dvb/pt1/pt1.c)0
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.c736
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007s.h (renamed from drivers/media/dvb/pt1/va1j5jf8007s.h)0
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.c (renamed from drivers/media/dvb/pt1/va1j5jf8007t.c)0
-rw-r--r--drivers/media/pci/pt1/va1j5jf8007t.h (renamed from drivers/media/dvb/pt1/va1j5jf8007t.h)0
-rw-r--r--drivers/media/pci/saa7134/Kconfig64
-rw-r--r--drivers/media/pci/saa7134/Makefile16
-rw-r--r--drivers/media/pci/saa7134/saa6752hs.c (renamed from drivers/media/video/saa7134/saa6752hs.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-alsa.c (renamed from drivers/media/video/saa7134/saa7134-alsa.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-cards.c (renamed from drivers/media/video/saa7134/saa7134-cards.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c (renamed from drivers/media/video/saa7134/saa7134-core.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-dvb.c1936
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c (renamed from drivers/media/video/saa7134/saa7134-empress.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-i2c.c (renamed from drivers/media/video/saa7134/saa7134-i2c.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c1041
-rw-r--r--drivers/media/pci/saa7134/saa7134-reg.h (renamed from drivers/media/video/saa7134/saa7134-reg.h)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-ts.c (renamed from drivers/media/video/saa7134/saa7134-ts.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-tvaudio.c (renamed from drivers/media/video/saa7134/saa7134-tvaudio.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-vbi.c (renamed from drivers/media/video/saa7134/saa7134-vbi.c)0
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c2661
-rw-r--r--drivers/media/pci/saa7134/saa7134.h855
-rw-r--r--drivers/media/pci/saa7146/Kconfig38
-rw-r--r--drivers/media/pci/saa7146/Makefile5
-rw-r--r--drivers/media/pci/saa7146/hexium_gemini.c (renamed from drivers/media/video/hexium_gemini.c)0
-rw-r--r--drivers/media/pci/saa7146/hexium_orion.c (renamed from drivers/media/video/hexium_orion.c)0
-rw-r--r--drivers/media/pci/saa7146/mxb.c886
-rw-r--r--drivers/media/pci/saa7164/Kconfig18
-rw-r--r--drivers/media/pci/saa7164/Makefile12
-rw-r--r--drivers/media/pci/saa7164/saa7164-api.c1524
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c (renamed from drivers/media/video/saa7164/saa7164-buffer.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-bus.c (renamed from drivers/media/video/saa7164/saa7164-bus.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-cards.c (renamed from drivers/media/video/saa7164/saa7164-cards.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c (renamed from drivers/media/video/saa7164/saa7164-cmd.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c1488
-rw-r--r--drivers/media/pci/saa7164/saa7164-dvb.c (renamed from drivers/media/video/saa7164/saa7164-dvb.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-encoder.c (renamed from drivers/media/video/saa7164/saa7164-encoder.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-fw.c (renamed from drivers/media/video/saa7164/saa7164-fw.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-i2c.c (renamed from drivers/media/video/saa7164/saa7164-i2c.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-reg.h (renamed from drivers/media/video/saa7164/saa7164-reg.h)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-types.h (renamed from drivers/media/video/saa7164/saa7164-types.h)0
-rw-r--r--drivers/media/pci/saa7164/saa7164-vbi.c (renamed from drivers/media/video/saa7164/saa7164-vbi.c)0
-rw-r--r--drivers/media/pci/saa7164/saa7164.h616
-rw-r--r--drivers/media/pci/sta2x11/Kconfig12
-rw-r--r--drivers/media/pci/sta2x11/Makefile1
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.c (renamed from drivers/media/video/sta2x11_vip.c)0
-rw-r--r--drivers/media/pci/sta2x11/sta2x11_vip.h (renamed from drivers/media/video/sta2x11_vip.h)0
-rw-r--r--drivers/media/pci/ttpci/Kconfig159
-rw-r--r--drivers/media/pci/ttpci/Makefile21
-rw-r--r--drivers/media/pci/ttpci/av7110.c (renamed from drivers/media/dvb/ttpci/av7110.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110.h (renamed from drivers/media/dvb/ttpci/av7110.h)0
-rw-r--r--drivers/media/pci/ttpci/av7110_av.c (renamed from drivers/media/dvb/ttpci/av7110_av.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110_av.h (renamed from drivers/media/dvb/ttpci/av7110_av.h)0
-rw-r--r--drivers/media/pci/ttpci/av7110_ca.c (renamed from drivers/media/dvb/ttpci/av7110_ca.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110_ca.h (renamed from drivers/media/dvb/ttpci/av7110_ca.h)0
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.c (renamed from drivers/media/dvb/ttpci/av7110_hw.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110_hw.h (renamed from drivers/media/dvb/ttpci/av7110_hw.h)0
-rw-r--r--drivers/media/pci/ttpci/av7110_ipack.c (renamed from drivers/media/dvb/ttpci/av7110_ipack.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110_ipack.h (renamed from drivers/media/dvb/ttpci/av7110_ipack.h)0
-rw-r--r--drivers/media/pci/ttpci/av7110_ir.c (renamed from drivers/media/dvb/ttpci/av7110_ir.c)0
-rw-r--r--drivers/media/pci/ttpci/av7110_v4l.c966
-rw-r--r--drivers/media/pci/ttpci/budget-av.c (renamed from drivers/media/dvb/ttpci/budget-av.c)0
-rw-r--r--drivers/media/pci/ttpci/budget-ci.c (renamed from drivers/media/dvb/ttpci/budget-ci.c)0
-rw-r--r--drivers/media/pci/ttpci/budget-core.c (renamed from drivers/media/dvb/ttpci/budget-core.c)0
-rw-r--r--drivers/media/pci/ttpci/budget-patch.c (renamed from drivers/media/dvb/ttpci/budget-patch.c)0
-rw-r--r--drivers/media/pci/ttpci/budget.c871
-rw-r--r--drivers/media/pci/ttpci/budget.h (renamed from drivers/media/dvb/ttpci/budget.h)0
-rw-r--r--drivers/media/pci/ttpci/ttpci-eeprom.c (renamed from drivers/media/dvb/ttpci/ttpci-eeprom.c)0
-rw-r--r--drivers/media/pci/ttpci/ttpci-eeprom.h (renamed from drivers/media/dvb/ttpci/ttpci-eeprom.h)0
-rw-r--r--drivers/media/pci/zoran/Kconfig74
-rw-r--r--drivers/media/pci/zoran/Makefile (renamed from drivers/media/video/zoran/Makefile)0
-rw-r--r--drivers/media/pci/zoran/videocodec.c (renamed from drivers/media/video/zoran/videocodec.c)0
-rw-r--r--drivers/media/pci/zoran/videocodec.h (renamed from drivers/media/video/zoran/videocodec.h)0
-rw-r--r--drivers/media/pci/zoran/zoran.h (renamed from drivers/media/video/zoran/zoran.h)0
-rw-r--r--drivers/media/pci/zoran/zoran_card.c1528
-rw-r--r--drivers/media/pci/zoran/zoran_card.h (renamed from drivers/media/video/zoran/zoran_card.h)0
-rw-r--r--drivers/media/pci/zoran/zoran_device.c (renamed from drivers/media/video/zoran/zoran_device.c)0
-rw-r--r--drivers/media/pci/zoran/zoran_device.h (renamed from drivers/media/video/zoran/zoran_device.h)0
-rw-r--r--drivers/media/pci/zoran/zoran_driver.c3090
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.c (renamed from drivers/media/video/zoran/zoran_procfs.c)0
-rw-r--r--drivers/media/pci/zoran/zoran_procfs.h (renamed from drivers/media/video/zoran/zoran_procfs.h)0
-rw-r--r--drivers/media/pci/zoran/zr36016.c (renamed from drivers/media/video/zoran/zr36016.c)0
-rw-r--r--drivers/media/pci/zoran/zr36016.h (renamed from drivers/media/video/zoran/zr36016.h)0
-rw-r--r--drivers/media/pci/zoran/zr36050.c (renamed from drivers/media/video/zoran/zr36050.c)0
-rw-r--r--drivers/media/pci/zoran/zr36050.h (renamed from drivers/media/video/zoran/zr36050.h)0
-rw-r--r--drivers/media/pci/zoran/zr36057.h (renamed from drivers/media/video/zoran/zr36057.h)0
-rw-r--r--drivers/media/pci/zoran/zr36060.c (renamed from drivers/media/video/zoran/zr36060.c)0
-rw-r--r--drivers/media/pci/zoran/zr36060.h (renamed from drivers/media/video/zoran/zr36060.h)0
-rw-r--r--drivers/media/platform/Kconfig223
-rw-r--r--drivers/media/platform/Makefile50
-rw-r--r--drivers/media/platform/arv.c (renamed from drivers/media/video/arv.c)0
-rw-r--r--drivers/media/platform/blackfin/Kconfig (renamed from drivers/media/video/blackfin/Kconfig)0
-rw-r--r--drivers/media/platform/blackfin/Makefile (renamed from drivers/media/video/blackfin/Makefile)0
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c1069
-rw-r--r--drivers/media/platform/blackfin/ppi.c (renamed from drivers/media/video/blackfin/ppi.c)0
-rw-r--r--drivers/media/platform/coda.c2049
-rw-r--r--drivers/media/platform/coda.h238
-rw-r--r--drivers/media/platform/davinci/Kconfig121
-rw-r--r--drivers/media/platform/davinci/Makefile (renamed from drivers/media/video/davinci/Makefile)0
-rw-r--r--drivers/media/platform/davinci/ccdc_hw_device.h (renamed from drivers/media/video/davinci/ccdc_hw_device.h)0
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc.c1072
-rw-r--r--drivers/media/platform/davinci/dm355_ccdc_regs.h (renamed from drivers/media/video/davinci/dm355_ccdc_regs.h)0
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc.c1081
-rw-r--r--drivers/media/platform/davinci/dm644x_ccdc_regs.h (renamed from drivers/media/video/davinci/dm644x_ccdc_regs.h)0
-rw-r--r--drivers/media/platform/davinci/isif.c1162
-rw-r--r--drivers/media/platform/davinci/isif_regs.h (renamed from drivers/media/video/davinci/isif_regs.h)0
-rw-r--r--drivers/media/platform/davinci/vpbe.c (renamed from drivers/media/video/davinci/vpbe.c)0
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c1839
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c (renamed from drivers/media/video/davinci/vpbe_osd.c)0
-rw-r--r--drivers/media/platform/davinci/vpbe_osd_regs.h (renamed from drivers/media/video/davinci/vpbe_osd_regs.h)0
-rw-r--r--drivers/media/platform/davinci/vpbe_venc.c (renamed from drivers/media/video/davinci/vpbe_venc.c)0
-rw-r--r--drivers/media/platform/davinci/vpbe_venc_regs.h (renamed from drivers/media/video/davinci/vpbe_venc_regs.h)0
-rw-r--r--drivers/media/platform/davinci/vpfe_capture.c2080
-rw-r--r--drivers/media/platform/davinci/vpif.c516
-rw-r--r--drivers/media/platform/davinci/vpif.h688
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c2380
-rw-r--r--drivers/media/platform/davinci/vpif_capture.h168
-rw-r--r--drivers/media/platform/davinci/vpif_display.c1933
-rw-r--r--drivers/media/platform/davinci/vpif_display.h179
-rw-r--r--drivers/media/platform/davinci/vpss.c482
-rw-r--r--drivers/media/platform/exynos-gsc/Makefile3
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c1252
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h527
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c770
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-regs.c425
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-regs.h172
-rw-r--r--drivers/media/platform/fsl-viu.c1690
-rw-r--r--drivers/media/platform/indycam.c (renamed from drivers/media/video/indycam.c)0
-rw-r--r--drivers/media/platform/indycam.h (renamed from drivers/media/video/indycam.h)0
-rw-r--r--drivers/media/platform/m2m-deinterlace.c1124
-rw-r--r--drivers/media/platform/marvell-ccic/Kconfig (renamed from drivers/media/video/marvell-ccic/Kconfig)0
-rw-r--r--drivers/media/platform/marvell-ccic/Makefile (renamed from drivers/media/video/marvell-ccic/Makefile)0
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c (renamed from drivers/media/video/marvell-ccic/cafe-driver.c)0
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c (renamed from drivers/media/video/marvell-ccic/mcam-core.c)0
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h (renamed from drivers/media/video/marvell-ccic/mcam-core.h)0
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c (renamed from drivers/media/video/marvell-ccic/mmp-driver.c)0
-rw-r--r--drivers/media/platform/mem2mem_testdev.c1130
-rw-r--r--drivers/media/platform/mx2_emmaprp.c1028
-rw-r--r--drivers/media/platform/omap/Kconfig (renamed from drivers/media/video/omap/Kconfig)0
-rw-r--r--drivers/media/platform/omap/Makefile8
-rw-r--r--drivers/media/platform/omap/omap_vout.c2290
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.c (renamed from drivers/media/video/omap/omap_vout_vrfb.c)0
-rw-r--r--drivers/media/platform/omap/omap_vout_vrfb.h (renamed from drivers/media/video/omap/omap_vout_vrfb.h)0
-rw-r--r--drivers/media/platform/omap/omap_voutdef.h (renamed from drivers/media/video/omap/omap_voutdef.h)0
-rw-r--r--drivers/media/platform/omap/omap_voutlib.c (renamed from drivers/media/video/omap/omap_voutlib.c)0
-rw-r--r--drivers/media/platform/omap/omap_voutlib.h (renamed from drivers/media/video/omap/omap_voutlib.h)0
-rw-r--r--drivers/media/platform/omap24xxcam-dma.c601
-rw-r--r--drivers/media/platform/omap24xxcam.c1881
-rw-r--r--drivers/media/platform/omap24xxcam.h593
-rw-r--r--drivers/media/platform/omap3isp/Makefile (renamed from drivers/media/video/omap3isp/Makefile)0
-rw-r--r--drivers/media/platform/omap3isp/cfa_coef_table.h61
-rw-r--r--drivers/media/platform/omap3isp/gamma_table.h (renamed from drivers/media/video/omap3isp/gamma_table.h)0
-rw-r--r--drivers/media/platform/omap3isp/isp.c2243
-rw-r--r--drivers/media/platform/omap3isp/isp.h352
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c2583
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.h172
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c (renamed from drivers/media/video/omap3isp/ispccp2.c)0
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.h (renamed from drivers/media/video/omap3isp/ispccp2.h)0
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c1328
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.h (renamed from drivers/media/video/omap3isp/ispcsi2.h)0
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.c (renamed from drivers/media/video/omap3isp/ispcsiphy.c)0
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.h (renamed from drivers/media/video/omap3isp/ispcsiphy.h)0
-rw-r--r--drivers/media/platform/omap3isp/isph3a.h (renamed from drivers/media/video/omap3isp/isph3a.h)0
-rw-r--r--drivers/media/platform/omap3isp/isph3a_aewb.c368
-rw-r--r--drivers/media/platform/omap3isp/isph3a_af.c423
-rw-r--r--drivers/media/platform/omap3isp/isphist.c518
-rw-r--r--drivers/media/platform/omap3isp/isphist.h (renamed from drivers/media/video/omap3isp/isphist.h)0
-rw-r--r--drivers/media/platform/omap3isp/isppreview.c2348
-rw-r--r--drivers/media/platform/omap3isp/isppreview.h174
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.c1158
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.h (renamed from drivers/media/video/omap3isp/ispqueue.h)0
-rw-r--r--drivers/media/platform/omap3isp/ispreg.h (renamed from drivers/media/video/omap3isp/ispreg.h)0
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.c1778
-rw-r--r--drivers/media/platform/omap3isp/ispresizer.h (renamed from drivers/media/video/omap3isp/ispresizer.h)0
-rw-r--r--drivers/media/platform/omap3isp/ispstat.c1102
-rw-r--r--drivers/media/platform/omap3isp/ispstat.h169
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c1404
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.h216
-rw-r--r--drivers/media/platform/omap3isp/luma_enhance_table.h (renamed from drivers/media/video/omap3isp/luma_enhance_table.h)0
-rw-r--r--drivers/media/platform/omap3isp/noise_filter_table.h (renamed from drivers/media/video/omap3isp/noise_filter_table.h)0
-rw-r--r--drivers/media/platform/s5p-fimc/Kconfig (renamed from drivers/media/video/s5p-fimc/Kconfig)0
-rw-r--r--drivers/media/platform/s5p-fimc/Makefile (renamed from drivers/media/video/s5p-fimc/Makefile)0
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-capture.c1734
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-core.c (renamed from drivers/media/video/s5p-fimc/fimc-core.c)0
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-core.h714
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-lite-reg.c300
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-lite-reg.h (renamed from drivers/media/video/s5p-fimc/fimc-lite-reg.h)0
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-lite.c1604
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-lite.h215
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-m2m.c839
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.c1050
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-mdevice.h112
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-reg.c (renamed from drivers/media/video/s5p-fimc/fimc-reg.c)0
-rw-r--r--drivers/media/platform/s5p-fimc/fimc-reg.h (renamed from drivers/media/video/s5p-fimc/fimc-reg.h)0
-rw-r--r--drivers/media/platform/s5p-fimc/mipi-csis.c842
-rw-r--r--drivers/media/platform/s5p-fimc/mipi-csis.h (renamed from drivers/media/video/s5p-fimc/mipi-csis.h)0
-rw-r--r--drivers/media/platform/s5p-g2d/Makefile (renamed from drivers/media/video/s5p-g2d/Makefile)0
-rw-r--r--drivers/media/platform/s5p-g2d/g2d-hw.c (renamed from drivers/media/video/s5p-g2d/g2d-hw.c)0
-rw-r--r--drivers/media/platform/s5p-g2d/g2d-regs.h (renamed from drivers/media/video/s5p-g2d/g2d-regs.h)0
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.c846
-rw-r--r--drivers/media/platform/s5p-g2d/g2d.h (renamed from drivers/media/video/s5p-g2d/g2d.h)0
-rw-r--r--drivers/media/platform/s5p-jpeg/Makefile (renamed from drivers/media/video/s5p-jpeg/Makefile)0
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c1528
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.h150
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-hw.h357
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-regs.h170
-rw-r--r--drivers/media/platform/s5p-mfc/Makefile (renamed from drivers/media/video/s5p-mfc/Makefile)0
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc.h (renamed from drivers/media/video/s5p-mfc/regs-mfc.h)0
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c1298
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c120
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h30
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h578
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c347
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_debug.h48
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c1030
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.h23
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c1882
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.h23
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_intr.c92
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_intr.h26
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.c1425
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h93
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_pm.c137
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_pm.h24
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_shm.c47
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_shm.h90
-rw-r--r--drivers/media/platform/s5p-tv/Kconfig86
-rw-r--r--drivers/media/platform/s5p-tv/Makefile19
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c1009
-rw-r--r--drivers/media/platform/s5p-tv/hdmiphy_drv.c (renamed from drivers/media/video/s5p-tv/hdmiphy_drv.c)0
-rw-r--r--drivers/media/platform/s5p-tv/mixer.h (renamed from drivers/media/video/s5p-tv/mixer.h)0
-rw-r--r--drivers/media/platform/s5p-tv/mixer_drv.c487
-rw-r--r--drivers/media/platform/s5p-tv/mixer_grp_layer.c (renamed from drivers/media/video/s5p-tv/mixer_grp_layer.c)0
-rw-r--r--drivers/media/platform/s5p-tv/mixer_reg.c (renamed from drivers/media/video/s5p-tv/mixer_reg.c)0
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c1127
-rw-r--r--drivers/media/platform/s5p-tv/mixer_vp_layer.c (renamed from drivers/media/video/s5p-tv/mixer_vp_layer.c)0
-rw-r--r--drivers/media/platform/s5p-tv/regs-hdmi.h (renamed from drivers/media/video/s5p-tv/regs-hdmi.h)0
-rw-r--r--drivers/media/platform/s5p-tv/regs-mixer.h (renamed from drivers/media/video/s5p-tv/regs-mixer.h)0
-rw-r--r--drivers/media/platform/s5p-tv/regs-sdo.h63
-rw-r--r--drivers/media/platform/s5p-tv/regs-vp.h (renamed from drivers/media/video/s5p-tv/regs-vp.h)0
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c448
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c413
-rw-r--r--drivers/media/platform/sh_vou.c1513
-rw-r--r--drivers/media/platform/soc_camera/Kconfig87
-rw-r--r--drivers/media/platform/soc_camera/Makefile14
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c (renamed from drivers/media/video/atmel-isi.c)0
-rw-r--r--drivers/media/platform/soc_camera/mx1_camera.c (renamed from drivers/media/video/mx1_camera.c)0
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c1863
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c1290
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c1723
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c (renamed from drivers/media/video/pxa_camera.c)0
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c2331
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c (renamed from drivers/media/video/sh_mobile_csi2.c)0
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c1605
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c206
-rw-r--r--drivers/media/platform/soc_camera/soc_mediabus.c (renamed from drivers/media/video/soc_mediabus.c)0
-rw-r--r--drivers/media/platform/timblogiw.c (renamed from drivers/media/video/timblogiw.c)0
-rw-r--r--drivers/media/platform/via-camera.c (renamed from drivers/media/video/via-camera.c)0
-rw-r--r--drivers/media/platform/via-camera.h (renamed from drivers/media/video/via-camera.h)0
-rw-r--r--drivers/media/platform/vino.c4349
-rw-r--r--drivers/media/platform/vino.h (renamed from drivers/media/video/vino.h)0
-rw-r--r--drivers/media/platform/vivi.c1411
-rw-r--r--drivers/media/radio/radio-keene.c2
-rw-r--r--drivers/media/radio/radio-miropcm20.c2
-rw-r--r--drivers/media/radio/radio-mr800.c5
-rw-r--r--drivers/media/radio/radio-sf16fmi.c2
-rw-r--r--drivers/media/radio/radio-shark.c44
-rw-r--r--drivers/media/radio/radio-shark2.c52
-rw-r--r--drivers/media/radio/radio-si4713.c11
-rw-r--r--drivers/media/radio/radio-tea5764.c15
-rw-r--r--drivers/media/radio/radio-tea5777.c205
-rw-r--r--drivers/media/radio/radio-tea5777.h3
-rw-r--r--drivers/media/radio/radio-timb.c10
-rw-r--r--drivers/media/radio/radio-wl1273.c32
-rw-r--r--drivers/media/radio/saa7706h.c15
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c7
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c23
-rw-r--r--drivers/media/radio/si4713-i2c.c4
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c47
-rw-r--r--drivers/media/rc/Kconfig32
-rw-r--r--drivers/media/rc/Makefile2
-rw-r--r--drivers/media/rc/ati_remote.c15
-rw-r--r--drivers/media/rc/fintek-cir.c11
-rw-r--r--drivers/media/rc/iguanair.c247
-rw-r--r--drivers/media/rc/ir-lirc-codec.c35
-rw-r--r--drivers/media/rc/ir-nec-decoder.c4
-rw-r--r--drivers/media/rc/ir-raw.c6
-rw-r--r--drivers/media/rc/ir-rx51.c498
-rw-r--r--drivers/media/rc/ite-cir.c2
-rw-r--r--drivers/media/rc/keymaps/rc-tt-1500.c2
-rw-r--r--drivers/media/rc/mceusb.c30
-rw-r--r--drivers/media/rc/rc-loopback.c12
-rw-r--r--drivers/media/rc/redrat3.c5
-rw-r--r--drivers/media/rc/ttusbir.c447
-rw-r--r--drivers/media/rc/winbond-cir.c49
-rw-r--r--drivers/media/tuners/Kconfig244
-rw-r--r--drivers/media/tuners/Makefile39
-rw-r--r--drivers/media/tuners/e4000.c409
-rw-r--r--drivers/media/tuners/e4000.h52
-rw-r--r--drivers/media/tuners/e4000_priv.h147
-rw-r--r--drivers/media/tuners/fc0011.c (renamed from drivers/media/common/tuners/fc0011.c)0
-rw-r--r--drivers/media/tuners/fc0011.h (renamed from drivers/media/common/tuners/fc0011.h)0
-rw-r--r--drivers/media/tuners/fc0012-priv.h (renamed from drivers/media/common/tuners/fc0012-priv.h)0
-rw-r--r--drivers/media/tuners/fc0012.c (renamed from drivers/media/common/tuners/fc0012.c)0
-rw-r--r--drivers/media/tuners/fc0012.h (renamed from drivers/media/common/tuners/fc0012.h)0
-rw-r--r--drivers/media/tuners/fc0013-priv.h (renamed from drivers/media/common/tuners/fc0013-priv.h)0
-rw-r--r--drivers/media/tuners/fc0013.c (renamed from drivers/media/common/tuners/fc0013.c)0
-rw-r--r--drivers/media/tuners/fc0013.h (renamed from drivers/media/common/tuners/fc0013.h)0
-rw-r--r--drivers/media/tuners/fc001x-common.h (renamed from drivers/media/common/tuners/fc001x-common.h)0
-rw-r--r--drivers/media/tuners/fc2580.c529
-rw-r--r--drivers/media/tuners/fc2580.h52
-rw-r--r--drivers/media/tuners/fc2580_priv.h134
-rw-r--r--drivers/media/tuners/max2165.c (renamed from drivers/media/common/tuners/max2165.c)0
-rw-r--r--drivers/media/tuners/max2165.h (renamed from drivers/media/common/tuners/max2165.h)0
-rw-r--r--drivers/media/tuners/max2165_priv.h (renamed from drivers/media/common/tuners/max2165_priv.h)0
-rw-r--r--drivers/media/tuners/mc44s803.c379
-rw-r--r--drivers/media/tuners/mc44s803.h (renamed from drivers/media/common/tuners/mc44s803.h)0
-rw-r--r--drivers/media/tuners/mc44s803_priv.h (renamed from drivers/media/common/tuners/mc44s803_priv.h)0
-rw-r--r--drivers/media/tuners/mt2060.c (renamed from drivers/media/common/tuners/mt2060.c)0
-rw-r--r--drivers/media/tuners/mt2060.h (renamed from drivers/media/common/tuners/mt2060.h)0
-rw-r--r--drivers/media/tuners/mt2060_priv.h (renamed from drivers/media/common/tuners/mt2060_priv.h)0
-rw-r--r--drivers/media/tuners/mt2063.c (renamed from drivers/media/common/tuners/mt2063.c)0
-rw-r--r--drivers/media/tuners/mt2063.h (renamed from drivers/media/common/tuners/mt2063.h)0
-rw-r--r--drivers/media/tuners/mt20xx.c (renamed from drivers/media/common/tuners/mt20xx.c)0
-rw-r--r--drivers/media/tuners/mt20xx.h (renamed from drivers/media/common/tuners/mt20xx.h)0
-rw-r--r--drivers/media/tuners/mt2131.c (renamed from drivers/media/common/tuners/mt2131.c)0
-rw-r--r--drivers/media/tuners/mt2131.h (renamed from drivers/media/common/tuners/mt2131.h)0
-rw-r--r--drivers/media/tuners/mt2131_priv.h (renamed from drivers/media/common/tuners/mt2131_priv.h)0
-rw-r--r--drivers/media/tuners/mt2266.c (renamed from drivers/media/common/tuners/mt2266.c)0
-rw-r--r--drivers/media/tuners/mt2266.h (renamed from drivers/media/common/tuners/mt2266.h)0
-rw-r--r--drivers/media/tuners/mxl5005s.c4120
-rw-r--r--drivers/media/tuners/mxl5005s.h (renamed from drivers/media/common/tuners/mxl5005s.h)0
-rw-r--r--drivers/media/tuners/mxl5007t.c (renamed from drivers/media/common/tuners/mxl5007t.c)0
-rw-r--r--drivers/media/tuners/mxl5007t.h (renamed from drivers/media/common/tuners/mxl5007t.h)0
-rw-r--r--drivers/media/tuners/qt1010.c456
-rw-r--r--drivers/media/tuners/qt1010.h (renamed from drivers/media/common/tuners/qt1010.h)0
-rw-r--r--drivers/media/tuners/qt1010_priv.h (renamed from drivers/media/common/tuners/qt1010_priv.h)0
-rw-r--r--drivers/media/tuners/tda18212.c319
-rw-r--r--drivers/media/tuners/tda18212.h (renamed from drivers/media/common/tuners/tda18212.h)0
-rw-r--r--drivers/media/tuners/tda18218.c340
-rw-r--r--drivers/media/tuners/tda18218.h (renamed from drivers/media/common/tuners/tda18218.h)0
-rw-r--r--drivers/media/tuners/tda18218_priv.h97
-rw-r--r--drivers/media/tuners/tda18271-common.c703
-rw-r--r--drivers/media/tuners/tda18271-fe.c1362
-rw-r--r--drivers/media/tuners/tda18271-maps.c (renamed from drivers/media/common/tuners/tda18271-maps.c)0
-rw-r--r--drivers/media/tuners/tda18271-priv.h (renamed from drivers/media/common/tuners/tda18271-priv.h)0
-rw-r--r--drivers/media/tuners/tda18271.h139
-rw-r--r--drivers/media/tuners/tda827x.c (renamed from drivers/media/common/tuners/tda827x.c)0
-rw-r--r--drivers/media/tuners/tda827x.h (renamed from drivers/media/common/tuners/tda827x.h)0
-rw-r--r--drivers/media/tuners/tda8290.c (renamed from drivers/media/common/tuners/tda8290.c)0
-rw-r--r--drivers/media/tuners/tda8290.h (renamed from drivers/media/common/tuners/tda8290.h)0
-rw-r--r--drivers/media/tuners/tda9887.c (renamed from drivers/media/common/tuners/tda9887.c)0
-rw-r--r--drivers/media/tuners/tda9887.h (renamed from drivers/media/common/tuners/tda9887.h)0
-rw-r--r--drivers/media/tuners/tea5761.c (renamed from drivers/media/common/tuners/tea5761.c)0
-rw-r--r--drivers/media/tuners/tea5761.h (renamed from drivers/media/common/tuners/tea5761.h)0
-rw-r--r--drivers/media/tuners/tea5767.c (renamed from drivers/media/common/tuners/tea5767.c)0
-rw-r--r--drivers/media/tuners/tea5767.h (renamed from drivers/media/common/tuners/tea5767.h)0
-rw-r--r--drivers/media/tuners/tua9001.c294
-rw-r--r--drivers/media/tuners/tua9001.h66
-rw-r--r--drivers/media/tuners/tua9001_priv.h (renamed from drivers/media/common/tuners/tua9001_priv.h)0
-rw-r--r--drivers/media/tuners/tuner-i2c.h (renamed from drivers/media/common/tuners/tuner-i2c.h)0
-rw-r--r--drivers/media/tuners/tuner-simple.c (renamed from drivers/media/common/tuners/tuner-simple.c)0
-rw-r--r--drivers/media/tuners/tuner-simple.h (renamed from drivers/media/common/tuners/tuner-simple.h)0
-rw-r--r--drivers/media/tuners/tuner-types.c (renamed from drivers/media/common/tuners/tuner-types.c)0
-rw-r--r--drivers/media/tuners/tuner-xc2028-types.h (renamed from drivers/media/common/tuners/tuner-xc2028-types.h)0
-rw-r--r--drivers/media/tuners/tuner-xc2028.c1509
-rw-r--r--drivers/media/tuners/tuner-xc2028.h (renamed from drivers/media/common/tuners/tuner-xc2028.h)0
-rw-r--r--drivers/media/tuners/xc4000.c1757
-rw-r--r--drivers/media/tuners/xc4000.h (renamed from drivers/media/common/tuners/xc4000.h)0
-rw-r--r--drivers/media/tuners/xc5000.c1366
-rw-r--r--drivers/media/tuners/xc5000.h (renamed from drivers/media/common/tuners/xc5000.h)0
-rw-r--r--drivers/media/usb/Kconfig54
-rw-r--r--drivers/media/usb/Makefile22
-rw-r--r--drivers/media/usb/au0828/Kconfig17
-rw-r--r--drivers/media/usb/au0828/Makefile9
-rw-r--r--drivers/media/usb/au0828/au0828-cards.c339
-rw-r--r--drivers/media/usb/au0828/au0828-cards.h (renamed from drivers/media/video/au0828/au0828-cards.h)0
-rw-r--r--drivers/media/usb/au0828/au0828-core.c282
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c500
-rw-r--r--drivers/media/usb/au0828/au0828-i2c.c401
-rw-r--r--drivers/media/usb/au0828/au0828-reg.h66
-rw-r--r--drivers/media/usb/au0828/au0828-vbi.c (renamed from drivers/media/video/au0828/au0828-vbi.c)0
-rw-r--r--drivers/media/usb/au0828/au0828-video.c2034
-rw-r--r--drivers/media/usb/au0828/au0828.h304
-rw-r--r--drivers/media/usb/b2c2/Kconfig15
-rw-r--r--drivers/media/usb/b2c2/Makefile5
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.c587
-rw-r--r--drivers/media/usb/b2c2/flexcop-usb.h (renamed from drivers/media/dvb/b2c2/flexcop-usb.h)0
-rw-r--r--drivers/media/usb/cpia2/Kconfig (renamed from drivers/media/video/cpia2/Kconfig)0
-rw-r--r--drivers/media/usb/cpia2/Makefile (renamed from drivers/media/video/cpia2/Makefile)0
-rw-r--r--drivers/media/usb/cpia2/cpia2.h (renamed from drivers/media/video/cpia2/cpia2.h)0
-rw-r--r--drivers/media/usb/cpia2/cpia2_core.c2417
-rw-r--r--drivers/media/usb/cpia2/cpia2_registers.h (renamed from drivers/media/video/cpia2/cpia2_registers.h)0
-rw-r--r--drivers/media/usb/cpia2/cpia2_usb.c (renamed from drivers/media/video/cpia2/cpia2_usb.c)0
-rw-r--r--drivers/media/usb/cpia2/cpia2_v4l.c1266
-rw-r--r--drivers/media/usb/cx231xx/Kconfig51
-rw-r--r--drivers/media/usb/cx231xx/Makefile15
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c2197
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c (renamed from drivers/media/video/cx231xx/cx231xx-audio.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-avcore.c (renamed from drivers/media/video/cx231xx/cx231xx-avcore.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c (renamed from drivers/media/video/cx231xx/cx231xx-cards.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-conf-reg.h (renamed from drivers/media/video/cx231xx/cx231xx-conf-reg.h)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-core.c (renamed from drivers/media/video/cx231xx/cx231xx-core.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dif.h (renamed from drivers/media/video/cx231xx/cx231xx-dif.h)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c (renamed from drivers/media/video/cx231xx/cx231xx-dvb.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-i2c.c (renamed from drivers/media/video/cx231xx/cx231xx-i2c.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-input.c (renamed from drivers/media/video/cx231xx/cx231xx-input.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c (renamed from drivers/media/video/cx231xx/cx231xx-pcb-cfg.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h (renamed from drivers/media/video/cx231xx/cx231xx-pcb-cfg.h)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-reg.h (renamed from drivers/media/video/cx231xx/cx231xx-reg.h)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.c (renamed from drivers/media/video/cx231xx/cx231xx-vbi.c)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-vbi.h (renamed from drivers/media/video/cx231xx/cx231xx-vbi.h)0
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c2670
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h (renamed from drivers/media/video/cx231xx/cx231xx.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig149
-rw-r--r--drivers/media/usb/dvb-usb-v2/Makefile49
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.c1454
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9015.h151
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.c1158
-rw-r--r--drivers/media/usb/dvb-usb-v2/af9035.h115
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.c1327
-rw-r--r--drivers/media/usb/dvb-usb-v2/anysee.h326
-rw-r--r--drivers/media/usb/dvb-usb-v2/au6610.c213
-rw-r--r--drivers/media/usb/dvb-usb-v2/au6610.h32
-rw-r--r--drivers/media/usb/dvb-usb-v2/az6007.c919
-rw-r--r--drivers/media/usb/dvb-usb-v2/ce6230.c292
-rw-r--r--drivers/media/usb/dvb-usb-v2/ce6230.h50
-rw-r--r--drivers/media/usb/dvb-usb-v2/cypress_firmware.c134
-rw-r--r--drivers/media/usb/dvb-usb-v2/cypress_firmware.h31
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h403
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_common.h35
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c1049
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c77
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.c385
-rw-r--r--drivers/media/usb/dvb-usb-v2/ec168.h53
-rw-r--r--drivers/media/usb/dvb-usb-v2/gl861.c177
-rw-r--r--drivers/media/usb/dvb-usb-v2/gl861.h12
-rw-r--r--drivers/media/usb/dvb-usb-v2/it913x.c799
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c1369
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.h (renamed from drivers/media/dvb/dvb-usb/lmedm04.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-demod.c)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-demod.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-gpio.c)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-gpio.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-i2c.c)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-i2c.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c (renamed from drivers/media/dvb/dvb-usb/mxl111sf-phy.c)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-phy.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-reg.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c527
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h (renamed from drivers/media/dvb/dvb-usb/mxl111sf-tuner.h)0
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.c1431
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf.h160
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.c1370
-rw-r--r--drivers/media/usb/dvb-usb-v2/rtl28xxu.h245
-rw-r--r--drivers/media/usb/dvb-usb-v2/usb_urb.c358
-rw-r--r--drivers/media/usb/dvb-usb/Kconfig313
-rw-r--r--drivers/media/usb/dvb-usb/Makefile83
-rw-r--r--drivers/media/usb/dvb-usb/a800.c (renamed from drivers/media/dvb/dvb-usb/a800.c)0
-rw-r--r--drivers/media/usb/dvb-usb/af9005-fe.c (renamed from drivers/media/dvb/dvb-usb/af9005-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/af9005-remote.c (renamed from drivers/media/dvb/dvb-usb/af9005-remote.c)0
-rw-r--r--drivers/media/usb/dvb-usb/af9005-script.h (renamed from drivers/media/dvb/dvb-usb/af9005-script.h)0
-rw-r--r--drivers/media/usb/dvb-usb/af9005.c (renamed from drivers/media/dvb/dvb-usb/af9005.c)0
-rw-r--r--drivers/media/usb/dvb-usb/af9005.h (renamed from drivers/media/dvb/dvb-usb/af9005.h)0
-rw-r--r--drivers/media/usb/dvb-usb/az6027.c (renamed from drivers/media/dvb/dvb-usb/az6027.c)0
-rw-r--r--drivers/media/usb/dvb-usb/az6027.h (renamed from drivers/media/dvb/dvb-usb/az6027.h)0
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-core.c (renamed from drivers/media/dvb/dvb-usb/cinergyT2-core.c)0
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2-fe.c (renamed from drivers/media/dvb/dvb-usb/cinergyT2-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/cinergyT2.h (renamed from drivers/media/dvb/dvb-usb/cinergyT2.h)0
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.c (renamed from drivers/media/dvb/dvb-usb/cxusb.c)0
-rw-r--r--drivers/media/usb/dvb-usb/cxusb.h (renamed from drivers/media/dvb/dvb-usb/cxusb.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dib0700.h (renamed from drivers/media/dvb/dvb-usb/dib0700.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c845
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c (renamed from drivers/media/dvb/dvb-usb/dib0700_devices.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dib07x0.h (renamed from drivers/media/dvb/dvb-usb/dib07x0.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-common.c (renamed from drivers/media/dvb/dvb-usb/dibusb-common.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mb.c (renamed from drivers/media/dvb/dvb-usb/dibusb-mb.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dibusb-mc.c (renamed from drivers/media/dvb/dvb-usb/dibusb-mc.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dibusb.h (renamed from drivers/media/dvb/dvb-usb/dibusb.h)0
-rw-r--r--drivers/media/usb/dvb-usb/digitv.c (renamed from drivers/media/dvb/dvb-usb/digitv.c)0
-rw-r--r--drivers/media/usb/dvb-usb/digitv.h (renamed from drivers/media/dvb/dvb-usb/digitv.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u-fe.c (renamed from drivers/media/dvb/dvb-usb/dtt200u-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.c (renamed from drivers/media/dvb/dvb-usb/dtt200u.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dtt200u.h (renamed from drivers/media/dvb/dvb-usb/dtt200u.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.c (renamed from drivers/media/dvb/dvb-usb/dtv5100.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.h (renamed from drivers/media/dvb/dvb-usb/dtv5100.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-common.h (renamed from drivers/media/dvb/dvb-usb/dvb-usb-common.h)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-dvb.c288
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-firmware.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-firmware.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-i2c.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-i2c.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-init.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-init.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-remote.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-remote.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-urb.c (renamed from drivers/media/dvb/dvb-usb/dvb-usb-urb.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb.h483
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.c (renamed from drivers/media/dvb/dvb-usb/dw2102.c)0
-rw-r--r--drivers/media/usb/dvb-usb/dw2102.h (renamed from drivers/media/dvb/dvb-usb/dw2102.h)0
-rw-r--r--drivers/media/usb/dvb-usb/friio-fe.c (renamed from drivers/media/dvb/dvb-usb/friio-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/friio.c (renamed from drivers/media/dvb/dvb-usb/friio.c)0
-rw-r--r--drivers/media/usb/dvb-usb/friio.h (renamed from drivers/media/dvb/dvb-usb/friio.h)0
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk-fe.c (renamed from drivers/media/dvb/dvb-usb/gp8psk-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.c (renamed from drivers/media/dvb/dvb-usb/gp8psk.c)0
-rw-r--r--drivers/media/usb/dvb-usb/gp8psk.h (renamed from drivers/media/dvb/dvb-usb/gp8psk.h)0
-rw-r--r--drivers/media/usb/dvb-usb/m920x.c (renamed from drivers/media/dvb/dvb-usb/m920x.c)0
-rw-r--r--drivers/media/usb/dvb-usb/m920x.h (renamed from drivers/media/dvb/dvb-usb/m920x.h)0
-rw-r--r--drivers/media/usb/dvb-usb/nova-t-usb2.c (renamed from drivers/media/dvb/dvb-usb/nova-t-usb2.c)0
-rw-r--r--drivers/media/usb/dvb-usb/opera1.c (renamed from drivers/media/dvb/dvb-usb/opera1.c)0
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c1063
-rw-r--r--drivers/media/usb/dvb-usb/technisat-usb2.c (renamed from drivers/media/dvb/dvb-usb/technisat-usb2.c)0
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.c820
-rw-r--r--drivers/media/usb/dvb-usb/ttusb2.h (renamed from drivers/media/dvb/dvb-usb/ttusb2.h)0
-rw-r--r--drivers/media/usb/dvb-usb/umt-010.c (renamed from drivers/media/dvb/dvb-usb/umt-010.c)0
-rw-r--r--drivers/media/usb/dvb-usb/usb-urb.c (renamed from drivers/media/dvb/dvb-usb/usb-urb.c)0
-rw-r--r--drivers/media/usb/dvb-usb/vp702x-fe.c (renamed from drivers/media/dvb/dvb-usb/vp702x-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.c (renamed from drivers/media/dvb/dvb-usb/vp702x.c)0
-rw-r--r--drivers/media/usb/dvb-usb/vp702x.h (renamed from drivers/media/dvb/dvb-usb/vp702x.h)0
-rw-r--r--drivers/media/usb/dvb-usb/vp7045-fe.c (renamed from drivers/media/dvb/dvb-usb/vp7045-fe.c)0
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.c (renamed from drivers/media/dvb/dvb-usb/vp7045.c)0
-rw-r--r--drivers/media/usb/dvb-usb/vp7045.h (renamed from drivers/media/dvb/dvb-usb/vp7045.h)0
-rw-r--r--drivers/media/usb/em28xx/Kconfig58
-rw-r--r--drivers/media/usb/em28xx/Makefile15
-rw-r--r--drivers/media/usb/em28xx/em28xx-audio.c751
-rw-r--r--drivers/media/usb/em28xx/em28xx-cards.c3415
-rw-r--r--drivers/media/usb/em28xx/em28xx-core.c1260
-rw-r--r--drivers/media/usb/em28xx/em28xx-dvb.c1245
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c (renamed from drivers/media/video/em28xx/em28xx-i2c.c)0
-rw-r--r--drivers/media/usb/em28xx/em28xx-input.c (renamed from drivers/media/video/em28xx/em28xx-input.c)0
-rw-r--r--drivers/media/usb/em28xx/em28xx-reg.h (renamed from drivers/media/video/em28xx/em28xx-reg.h)0
-rw-r--r--drivers/media/usb/em28xx/em28xx-vbi.c (renamed from drivers/media/video/em28xx/em28xx-vbi.c)0
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c2624
-rw-r--r--drivers/media/usb/em28xx/em28xx.h (renamed from drivers/media/video/em28xx/em28xx.h)0
-rw-r--r--drivers/media/usb/gspca/Kconfig425
-rw-r--r--drivers/media/usb/gspca/Makefile (renamed from drivers/media/video/gspca/Makefile)0
-rw-r--r--drivers/media/usb/gspca/autogain_functions.c (renamed from drivers/media/video/gspca/autogain_functions.c)0
-rw-r--r--drivers/media/usb/gspca/autogain_functions.h (renamed from drivers/media/video/gspca/autogain_functions.h)0
-rw-r--r--drivers/media/usb/gspca/benq.c (renamed from drivers/media/video/gspca/benq.c)0
-rw-r--r--drivers/media/usb/gspca/conex.c (renamed from drivers/media/video/gspca/conex.c)0
-rw-r--r--drivers/media/usb/gspca/cpia1.c1905
-rw-r--r--drivers/media/usb/gspca/etoms.c (renamed from drivers/media/video/gspca/etoms.c)0
-rw-r--r--drivers/media/usb/gspca/finepix.c310
-rw-r--r--drivers/media/usb/gspca/gl860/Kconfig (renamed from drivers/media/video/gspca/gl860/Kconfig)0
-rw-r--r--drivers/media/usb/gspca/gl860/Makefile10
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-mi1320.c (renamed from drivers/media/video/gspca/gl860/gl860-mi1320.c)0
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-mi2020.c (renamed from drivers/media/video/gspca/gl860/gl860-mi2020.c)0
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-ov2640.c (renamed from drivers/media/video/gspca/gl860/gl860-ov2640.c)0
-rw-r--r--drivers/media/usb/gspca/gl860/gl860-ov9655.c (renamed from drivers/media/video/gspca/gl860/gl860-ov9655.c)0
-rw-r--r--drivers/media/usb/gspca/gl860/gl860.c (renamed from drivers/media/video/gspca/gl860/gl860.c)0
-rw-r--r--drivers/media/usb/gspca/gl860/gl860.h (renamed from drivers/media/video/gspca/gl860/gl860.h)0
-rw-r--r--drivers/media/usb/gspca/gspca.c2460
-rw-r--r--drivers/media/usb/gspca/gspca.h261
-rw-r--r--drivers/media/usb/gspca/jeilinj.c548
-rw-r--r--drivers/media/usb/gspca/jl2005bcd.c555
-rw-r--r--drivers/media/usb/gspca/jpeg.h (renamed from drivers/media/video/gspca/jpeg.h)0
-rw-r--r--drivers/media/usb/gspca/kinect.c (renamed from drivers/media/video/gspca/kinect.c)0
-rw-r--r--drivers/media/usb/gspca/konica.c (renamed from drivers/media/video/gspca/konica.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/Kconfig (renamed from drivers/media/video/gspca/m5602/Kconfig)0
-rw-r--r--drivers/media/usb/gspca/m5602/Makefile11
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_bridge.h (renamed from drivers/media/video/gspca/m5602/m5602_bridge.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_core.c (renamed from drivers/media/video/gspca/m5602/m5602_core.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_mt9m111.c (renamed from drivers/media/video/gspca/m5602/m5602_mt9m111.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_mt9m111.h (renamed from drivers/media/video/gspca/m5602/m5602_mt9m111.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov7660.c (renamed from drivers/media/video/gspca/m5602/m5602_ov7660.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov7660.h (renamed from drivers/media/video/gspca/m5602/m5602_ov7660.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov9650.c (renamed from drivers/media/video/gspca/m5602/m5602_ov9650.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_ov9650.h (renamed from drivers/media/video/gspca/m5602/m5602_ov9650.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_po1030.c (renamed from drivers/media/video/gspca/m5602/m5602_po1030.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_po1030.h (renamed from drivers/media/video/gspca/m5602/m5602_po1030.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k4aa.c (renamed from drivers/media/video/gspca/m5602/m5602_s5k4aa.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k4aa.h (renamed from drivers/media/video/gspca/m5602/m5602_s5k4aa.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k83a.c (renamed from drivers/media/video/gspca/m5602/m5602_s5k83a.c)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_s5k83a.h (renamed from drivers/media/video/gspca/m5602/m5602_s5k83a.h)0
-rw-r--r--drivers/media/usb/gspca/m5602/m5602_sensor.h (renamed from drivers/media/video/gspca/m5602/m5602_sensor.h)0
-rw-r--r--drivers/media/usb/gspca/mars.c (renamed from drivers/media/video/gspca/mars.c)0
-rw-r--r--drivers/media/usb/gspca/mr97310a.c (renamed from drivers/media/video/gspca/mr97310a.c)0
-rw-r--r--drivers/media/usb/gspca/nw80x.c (renamed from drivers/media/video/gspca/nw80x.c)0
-rw-r--r--drivers/media/usb/gspca/ov519.c4991
-rw-r--r--drivers/media/usb/gspca/ov534.c (renamed from drivers/media/video/gspca/ov534.c)0
-rw-r--r--drivers/media/usb/gspca/ov534_9.c (renamed from drivers/media/video/gspca/ov534_9.c)0
-rw-r--r--drivers/media/usb/gspca/pac207.c (renamed from drivers/media/video/gspca/pac207.c)0
-rw-r--r--drivers/media/usb/gspca/pac7302.c957
-rw-r--r--drivers/media/usb/gspca/pac7311.c (renamed from drivers/media/video/gspca/pac7311.c)0
-rw-r--r--drivers/media/usb/gspca/pac_common.h (renamed from drivers/media/video/gspca/pac_common.h)0
-rw-r--r--drivers/media/usb/gspca/se401.c (renamed from drivers/media/video/gspca/se401.c)0
-rw-r--r--drivers/media/usb/gspca/se401.h (renamed from drivers/media/video/gspca/se401.h)0
-rw-r--r--drivers/media/usb/gspca/sn9c2028.c (renamed from drivers/media/video/gspca/sn9c2028.c)0
-rw-r--r--drivers/media/usb/gspca/sn9c2028.h (renamed from drivers/media/video/gspca/sn9c2028.h)0
-rw-r--r--drivers/media/usb/gspca/sn9c20x.c2430
-rw-r--r--drivers/media/usb/gspca/sonixb.c (renamed from drivers/media/video/gspca/sonixb.c)0
-rw-r--r--drivers/media/usb/gspca/sonixj.c3208
-rw-r--r--drivers/media/usb/gspca/spca1528.c (renamed from drivers/media/video/gspca/spca1528.c)0
-rw-r--r--drivers/media/usb/gspca/spca500.c (renamed from drivers/media/video/gspca/spca500.c)0
-rw-r--r--drivers/media/usb/gspca/spca501.c (renamed from drivers/media/video/gspca/spca501.c)0
-rw-r--r--drivers/media/usb/gspca/spca505.c (renamed from drivers/media/video/gspca/spca505.c)0
-rw-r--r--drivers/media/usb/gspca/spca506.c612
-rw-r--r--drivers/media/usb/gspca/spca508.c (renamed from drivers/media/video/gspca/spca508.c)0
-rw-r--r--drivers/media/usb/gspca/spca561.c (renamed from drivers/media/video/gspca/spca561.c)0
-rw-r--r--drivers/media/usb/gspca/sq905.c439
-rw-r--r--drivers/media/usb/gspca/sq905c.c342
-rw-r--r--drivers/media/usb/gspca/sq930x.c1164
-rw-r--r--drivers/media/usb/gspca/stk014.c (renamed from drivers/media/video/gspca/stk014.c)0
-rw-r--r--drivers/media/usb/gspca/stv0680.c (renamed from drivers/media/video/gspca/stv0680.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/Kconfig (renamed from drivers/media/video/gspca/stv06xx/Kconfig)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/Makefile10
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx.h)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_sensor.h)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_st6422.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_st6422.h)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c (renamed from drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c)0
-rw-r--r--drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h (renamed from drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h)0
-rw-r--r--drivers/media/usb/gspca/sunplus.c (renamed from drivers/media/video/gspca/sunplus.c)0
-rw-r--r--drivers/media/usb/gspca/t613.c (renamed from drivers/media/video/gspca/t613.c)0
-rw-r--r--drivers/media/usb/gspca/topro.c4969
-rw-r--r--drivers/media/usb/gspca/tv8532.c (renamed from drivers/media/video/gspca/tv8532.c)0
-rw-r--r--drivers/media/usb/gspca/vc032x.c3850
-rw-r--r--drivers/media/usb/gspca/vicam.c364
-rw-r--r--drivers/media/usb/gspca/w996Xcf.c (renamed from drivers/media/video/gspca/w996Xcf.c)0
-rw-r--r--drivers/media/usb/gspca/xirlink_cit.c3143
-rw-r--r--drivers/media/usb/gspca/zc3xx-reg.h (renamed from drivers/media/video/gspca/zc3xx-reg.h)0
-rw-r--r--drivers/media/usb/gspca/zc3xx.c7021
-rw-r--r--drivers/media/usb/hdpvr/Kconfig (renamed from drivers/media/video/hdpvr/Kconfig)0
-rw-r--r--drivers/media/usb/hdpvr/Makefile7
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-control.c (renamed from drivers/media/video/hdpvr/hdpvr-control.c)0
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c (renamed from drivers/media/video/hdpvr/hdpvr-core.c)0
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c (renamed from drivers/media/video/hdpvr/hdpvr-i2c.c)0
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c1289
-rw-r--r--drivers/media/usb/hdpvr/hdpvr.h (renamed from drivers/media/video/hdpvr/hdpvr.h)0
-rw-r--r--drivers/media/usb/pvrusb2/Kconfig65
-rw-r--r--drivers/media/usb/pvrusb2/Makefile22
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-audio.c (renamed from drivers/media/video/pvrusb2/pvrusb2-audio.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-audio.h (renamed from drivers/media/video/pvrusb2/pvrusb2-audio.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.c (renamed from drivers/media/video/pvrusb2/pvrusb2-context.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-context.h (renamed from drivers/media/video/pvrusb2/pvrusb2-context.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c (renamed from drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h (renamed from drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ctrl.c (renamed from drivers/media/video/pvrusb2/pvrusb2-ctrl.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ctrl.h (renamed from drivers/media/video/pvrusb2/pvrusb2-ctrl.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c (renamed from drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h (renamed from drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debug.h (renamed from drivers/media/video/pvrusb2/pvrusb2-debug.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debugifc.c (renamed from drivers/media/video/pvrusb2/pvrusb2-debugifc.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-debugifc.h (renamed from drivers/media/video/pvrusb2/pvrusb2-debugifc.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-devattr.c576
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-devattr.h (renamed from drivers/media/video/pvrusb2/pvrusb2-devattr.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-dvb.c (renamed from drivers/media/video/pvrusb2/pvrusb2-dvb.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-dvb.h (renamed from drivers/media/video/pvrusb2/pvrusb2-dvb.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-eeprom.c (renamed from drivers/media/video/pvrusb2/pvrusb2-eeprom.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-eeprom.h (renamed from drivers/media/video/pvrusb2/pvrusb2-eeprom.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-encoder.c (renamed from drivers/media/video/pvrusb2/pvrusb2-encoder.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-encoder.h (renamed from drivers/media/video/pvrusb2/pvrusb2-encoder.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h (renamed from drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.h (renamed from drivers/media/video/pvrusb2/pvrusb2-hdw.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c (renamed from drivers/media/video/pvrusb2/pvrusb2-i2c-core.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h (renamed from drivers/media/video/pvrusb2/pvrusb2-i2c-core.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.c (renamed from drivers/media/video/pvrusb2/pvrusb2-io.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-io.h (renamed from drivers/media/video/pvrusb2/pvrusb2-io.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ioread.c (renamed from drivers/media/video/pvrusb2/pvrusb2-ioread.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-ioread.h (renamed from drivers/media/video/pvrusb2/pvrusb2-ioread.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-main.c (renamed from drivers/media/video/pvrusb2/pvrusb2-main.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.c (renamed from drivers/media/video/pvrusb2/pvrusb2-std.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-std.h (renamed from drivers/media/video/pvrusb2/pvrusb2-std.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.c (renamed from drivers/media/video/pvrusb2/pvrusb2-sysfs.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-sysfs.h (renamed from drivers/media/video/pvrusb2/pvrusb2-sysfs.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-util.h (renamed from drivers/media/video/pvrusb2/pvrusb2-util.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.c1413
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-v4l2.h (renamed from drivers/media/video/pvrusb2/pvrusb2-v4l2.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c (renamed from drivers/media/video/pvrusb2/pvrusb2-video-v4l.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h (renamed from drivers/media/video/pvrusb2/pvrusb2-video-v4l.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-wm8775.c (renamed from drivers/media/video/pvrusb2/pvrusb2-wm8775.c)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-wm8775.h (renamed from drivers/media/video/pvrusb2/pvrusb2-wm8775.h)0
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2.h (renamed from drivers/media/video/pvrusb2/pvrusb2.h)0
-rw-r--r--drivers/media/usb/pwc/Kconfig (renamed from drivers/media/video/pwc/Kconfig)0
-rw-r--r--drivers/media/usb/pwc/Makefile4
-rw-r--r--drivers/media/usb/pwc/philips.txt (renamed from drivers/media/video/pwc/philips.txt)0
-rw-r--r--drivers/media/usb/pwc/pwc-ctrl.c (renamed from drivers/media/video/pwc/pwc-ctrl.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-dec1.c (renamed from drivers/media/video/pwc/pwc-dec1.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-dec1.h (renamed from drivers/media/video/pwc/pwc-dec1.h)0
-rw-r--r--drivers/media/usb/pwc/pwc-dec23.c (renamed from drivers/media/video/pwc/pwc-dec23.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-dec23.h (renamed from drivers/media/video/pwc/pwc-dec23.h)0
-rw-r--r--drivers/media/usb/pwc/pwc-if.c1164
-rw-r--r--drivers/media/usb/pwc/pwc-kiara.c (renamed from drivers/media/video/pwc/pwc-kiara.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-kiara.h (renamed from drivers/media/video/pwc/pwc-kiara.h)0
-rw-r--r--drivers/media/usb/pwc/pwc-misc.c (renamed from drivers/media/video/pwc/pwc-misc.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-nala.h (renamed from drivers/media/video/pwc/pwc-nala.h)0
-rw-r--r--drivers/media/usb/pwc/pwc-timon.c (renamed from drivers/media/video/pwc/pwc-timon.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-timon.h (renamed from drivers/media/video/pwc/pwc-timon.h)0
-rw-r--r--drivers/media/usb/pwc/pwc-uncompress.c (renamed from drivers/media/video/pwc/pwc-uncompress.c)0
-rw-r--r--drivers/media/usb/pwc/pwc-v4l.c (renamed from drivers/media/video/pwc/pwc-v4l.c)0
-rw-r--r--drivers/media/usb/pwc/pwc.h (renamed from drivers/media/video/pwc/pwc.h)0
-rw-r--r--drivers/media/usb/s2255/Kconfig9
-rw-r--r--drivers/media/usb/s2255/Makefile2
-rw-r--r--drivers/media/usb/s2255/s2255drv.c2690
-rw-r--r--drivers/media/usb/siano/Kconfig10
-rw-r--r--drivers/media/usb/siano/Makefile6
-rw-r--r--drivers/media/usb/siano/smsusb.c568
-rw-r--r--drivers/media/usb/sn9c102/Kconfig (renamed from drivers/media/video/sn9c102/Kconfig)0
-rw-r--r--drivers/media/usb/sn9c102/Makefile (renamed from drivers/media/video/sn9c102/Makefile)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102.h (renamed from drivers/media/video/sn9c102/sn9c102.h)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_config.h (renamed from drivers/media/video/sn9c102/sn9c102_config.h)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_core.c (renamed from drivers/media/video/sn9c102/sn9c102_core.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_devtable.h (renamed from drivers/media/video/sn9c102/sn9c102_devtable.h)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_hv7131d.c (renamed from drivers/media/video/sn9c102/sn9c102_hv7131d.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_hv7131r.c (renamed from drivers/media/video/sn9c102/sn9c102_hv7131r.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_mi0343.c (renamed from drivers/media/video/sn9c102/sn9c102_mi0343.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_mi0360.c (renamed from drivers/media/video/sn9c102/sn9c102_mi0360.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_mt9v111.c (renamed from drivers/media/video/sn9c102/sn9c102_mt9v111.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_ov7630.c (renamed from drivers/media/video/sn9c102/sn9c102_ov7630.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_ov7660.c (renamed from drivers/media/video/sn9c102/sn9c102_ov7660.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_pas106b.c (renamed from drivers/media/video/sn9c102/sn9c102_pas106b.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_pas202bcb.c (renamed from drivers/media/video/sn9c102/sn9c102_pas202bcb.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_sensor.h (renamed from drivers/media/video/sn9c102/sn9c102_sensor.h)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c (renamed from drivers/media/video/sn9c102/sn9c102_tas5110c1b.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_tas5110d.c (renamed from drivers/media/video/sn9c102/sn9c102_tas5110d.c)0
-rw-r--r--drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c (renamed from drivers/media/video/sn9c102/sn9c102_tas5130d1b.c)0
-rw-r--r--drivers/media/usb/stk1160/Kconfig20
-rw-r--r--drivers/media/usb/stk1160/Makefile11
-rw-r--r--drivers/media/usb/stk1160/stk1160-ac97.c152
-rw-r--r--drivers/media/usb/stk1160/stk1160-core.c430
-rw-r--r--drivers/media/usb/stk1160/stk1160-i2c.c294
-rw-r--r--drivers/media/usb/stk1160/stk1160-reg.h93
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c739
-rw-r--r--drivers/media/usb/stk1160/stk1160-video.c522
-rw-r--r--drivers/media/usb/stk1160/stk1160.h208
-rw-r--r--drivers/media/usb/stkwebcam/Kconfig13
-rw-r--r--drivers/media/usb/stkwebcam/Makefile4
-rw-r--r--drivers/media/usb/stkwebcam/stk-sensor.c (renamed from drivers/media/video/stk-sensor.c)0
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.c (renamed from drivers/media/video/stk-webcam.c)0
-rw-r--r--drivers/media/usb/stkwebcam/stk-webcam.h (renamed from drivers/media/video/stk-webcam.h)0
-rw-r--r--drivers/media/usb/tlg2300/Kconfig (renamed from drivers/media/video/tlg2300/Kconfig)0
-rw-r--r--drivers/media/usb/tlg2300/Makefile9
-rw-r--r--drivers/media/usb/tlg2300/pd-alsa.c336
-rw-r--r--drivers/media/usb/tlg2300/pd-common.h (renamed from drivers/media/video/tlg2300/pd-common.h)0
-rw-r--r--drivers/media/usb/tlg2300/pd-dvb.c (renamed from drivers/media/video/tlg2300/pd-dvb.c)0
-rw-r--r--drivers/media/usb/tlg2300/pd-main.c (renamed from drivers/media/video/tlg2300/pd-main.c)0
-rw-r--r--drivers/media/usb/tlg2300/pd-radio.c421
-rw-r--r--drivers/media/usb/tlg2300/pd-video.c1668
-rw-r--r--drivers/media/usb/tlg2300/vendorcmds.h (renamed from drivers/media/video/tlg2300/vendorcmds.h)0
-rw-r--r--drivers/media/usb/tm6000/Kconfig (renamed from drivers/media/video/tm6000/Kconfig)0
-rw-r--r--drivers/media/usb/tm6000/Makefile15
-rw-r--r--drivers/media/usb/tm6000/tm6000-alsa.c529
-rw-r--r--drivers/media/usb/tm6000/tm6000-cards.c (renamed from drivers/media/video/tm6000/tm6000-cards.c)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-core.c (renamed from drivers/media/video/tm6000/tm6000-core.c)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-dvb.c (renamed from drivers/media/video/tm6000/tm6000-dvb.c)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-i2c.c (renamed from drivers/media/video/tm6000/tm6000-i2c.c)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-input.c499
-rw-r--r--drivers/media/usb/tm6000/tm6000-regs.h (renamed from drivers/media/video/tm6000/tm6000-regs.h)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-stds.c (renamed from drivers/media/video/tm6000/tm6000-stds.c)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-usb-isoc.h (renamed from drivers/media/video/tm6000/tm6000-usb-isoc.h)0
-rw-r--r--drivers/media/usb/tm6000/tm6000-video.c1852
-rw-r--r--drivers/media/usb/tm6000/tm6000.h (renamed from drivers/media/video/tm6000/tm6000.h)0
-rw-r--r--drivers/media/usb/ttusb-budget/Kconfig18
-rw-r--r--drivers/media/usb/ttusb-budget/Makefile3
-rw-r--r--drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c (renamed from drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c)0
-rw-r--r--drivers/media/usb/ttusb-dec/Kconfig (renamed from drivers/media/dvb/ttusb-dec/Kconfig)0
-rw-r--r--drivers/media/usb/ttusb-dec/Makefile3
-rw-r--r--drivers/media/usb/ttusb-dec/ttusb_dec.c (renamed from drivers/media/dvb/ttusb-dec/ttusb_dec.c)0
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.c (renamed from drivers/media/dvb/ttusb-dec/ttusbdecfe.c)0
-rw-r--r--drivers/media/usb/ttusb-dec/ttusbdecfe.h (renamed from drivers/media/dvb/ttusb-dec/ttusbdecfe.h)0
-rw-r--r--drivers/media/usb/usbvision/Kconfig12
-rw-r--r--drivers/media/usb/usbvision/Makefile6
-rw-r--r--drivers/media/usb/usbvision/usbvision-cards.c (renamed from drivers/media/video/usbvision/usbvision-cards.c)0
-rw-r--r--drivers/media/usb/usbvision/usbvision-cards.h (renamed from drivers/media/video/usbvision/usbvision-cards.h)0
-rw-r--r--drivers/media/usb/usbvision/usbvision-core.c (renamed from drivers/media/video/usbvision/usbvision-core.c)0
-rw-r--r--drivers/media/usb/usbvision/usbvision-i2c.c (renamed from drivers/media/video/usbvision/usbvision-i2c.c)0
-rw-r--r--drivers/media/usb/usbvision/usbvision-video.c1720
-rw-r--r--drivers/media/usb/usbvision/usbvision.h (renamed from drivers/media/video/usbvision/usbvision.h)0
-rw-r--r--drivers/media/usb/uvc/Kconfig (renamed from drivers/media/video/uvc/Kconfig)0
-rw-r--r--drivers/media/usb/uvc/Makefile (renamed from drivers/media/video/uvc/Makefile)0
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c (renamed from drivers/media/video/uvc/uvc_ctrl.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_debugfs.c (renamed from drivers/media/video/uvc/uvc_debugfs.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c2462
-rw-r--r--drivers/media/usb/uvc/uvc_entity.c (renamed from drivers/media/video/uvc/uvc_entity.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_isight.c (renamed from drivers/media/video/uvc/uvc_isight.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c (renamed from drivers/media/video/uvc/uvc_queue.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_status.c (renamed from drivers/media/video/uvc/uvc_status.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c (renamed from drivers/media/video/uvc/uvc_v4l2.c)0
-rw-r--r--drivers/media/usb/uvc/uvc_video.c1879
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h718
-rw-r--r--drivers/media/usb/zr364xx/Kconfig14
-rw-r--r--drivers/media/usb/zr364xx/Makefile2
-rw-r--r--drivers/media/usb/zr364xx/zr364xx.c (renamed from drivers/media/video/zr364xx.c)0
-rw-r--r--drivers/media/v4l2-core/Kconfig81
-rw-r--r--drivers/media/v4l2-core/Makefile35
-rw-r--r--drivers/media/v4l2-core/tuner-core.c (renamed from drivers/media/video/tuner-core.c)0
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c981
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c1094
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls.c2742
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c1029
-rw-r--r--drivers/media/v4l2-core/v4l2-device.c280
-rw-r--r--drivers/media/v4l2-core/v4l2-event.c313
-rw-r--r--drivers/media/v4l2-core/v4l2-fh.c (renamed from drivers/media/video/v4l2-fh.c)0
-rw-r--r--drivers/media/v4l2-core/v4l2-int-device.c (renamed from drivers/media/video/v4l2-int-device.c)0
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c2358
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c647
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c476
-rw-r--r--drivers/media/v4l2-core/videobuf-core.c (renamed from drivers/media/video/videobuf-core.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-contig.c (renamed from drivers/media/video/videobuf-dma-contig.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf-dma-sg.c (renamed from drivers/media/video/videobuf-dma-sg.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf-dvb.c398
-rw-r--r--drivers/media/v4l2-core/videobuf-vmalloc.c (renamed from drivers/media/video/videobuf-vmalloc.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf2-core.c2383
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-contig.c (renamed from drivers/media/video/videobuf2-dma-contig.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf2-dma-sg.c (renamed from drivers/media/video/videobuf2-dma-sg.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf2-memops.c (renamed from drivers/media/video/videobuf2-memops.c)0
-rw-r--r--drivers/media/v4l2-core/videobuf2-vmalloc.c223
-rw-r--r--drivers/media/video/Kconfig1263
-rw-r--r--drivers/media/video/Makefile218
-rw-r--r--drivers/media/video/adp1653.c487
-rw-r--r--drivers/media/video/as3645a.c888
-rw-r--r--drivers/media/video/au0828/Kconfig18
-rw-r--r--drivers/media/video/au0828/Makefile9
-rw-r--r--drivers/media/video/au0828/au0828-cards.c339
-rw-r--r--drivers/media/video/au0828/au0828-core.c295
-rw-r--r--drivers/media/video/au0828/au0828-dvb.c458
-rw-r--r--drivers/media/video/au0828/au0828-i2c.c390
-rw-r--r--drivers/media/video/au0828/au0828-reg.h65
-rw-r--r--drivers/media/video/au0828/au0828-video.c1998
-rw-r--r--drivers/media/video/au0828/au0828.h302
-rw-r--r--drivers/media/video/blackfin/bfin_capture.c1063
-rw-r--r--drivers/media/video/bt8xx/Kconfig27
-rw-r--r--drivers/media/video/bt8xx/Makefile13
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c4630
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c2413
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c1250
-rw-r--r--drivers/media/video/cx18/Kconfig35
-rw-r--r--drivers/media/video/cx18/Makefile13
-rw-r--r--drivers/media/video/cx18/cx18-av-firmware.c223
-rw-r--r--drivers/media/video/cx18/cx18-av-vbi.c311
-rw-r--r--drivers/media/video/cx18/cx18-driver.c1359
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c605
-rw-r--r--drivers/media/video/cx18/cx18-firmware.c453
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c1194
-rw-r--r--drivers/media/video/cx18/cx18-streams.c1060
-rw-r--r--drivers/media/video/cx231xx/Kconfig51
-rw-r--r--drivers/media/video/cx231xx/Makefile16
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c2195
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c2645
-rw-r--r--drivers/media/video/cx23885/Kconfig46
-rw-r--r--drivers/media/video/cx23885/Makefile15
-rw-r--r--drivers/media/video/cx23885/altera-ci.c837
-rw-r--r--drivers/media/video/cx23885/cx23885-417.c1788
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c1680
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c1357
-rw-r--r--drivers/media/video/cx23885/cx23885-input.c356
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c1926
-rw-r--r--drivers/media/video/cx23885/cx23885.h653
-rw-r--r--drivers/media/video/cx25821/Makefile13
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c1990
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h186
-rw-r--r--drivers/media/video/cx25840/Makefile6
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c166
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c256
-rw-r--r--drivers/media/video/cx88/Kconfig86
-rw-r--r--drivers/media/video/cx88/Makefile16
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c975
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c1299
-rw-r--r--drivers/media/video/cx88/cx88-cards.c3811
-rw-r--r--drivers/media/video/cx88/cx88-core.c1131
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c1778
-rw-r--r--drivers/media/video/cx88/cx88-video.c2075
-rw-r--r--drivers/media/video/cx88/cx88.h748
-rw-r--r--drivers/media/video/davinci/Kconfig121
-rw-r--r--drivers/media/video/davinci/dm355_ccdc.c1072
-rw-r--r--drivers/media/video/davinci/dm644x_ccdc.c1081
-rw-r--r--drivers/media/video/davinci/isif.c1162
-rw-r--r--drivers/media/video/davinci/vpbe_display.c1830
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c2079
-rw-r--r--drivers/media/video/davinci/vpif.c514
-rw-r--r--drivers/media/video/davinci/vpif.h688
-rw-r--r--drivers/media/video/davinci/vpif_capture.c2444
-rw-r--r--drivers/media/video/davinci/vpif_capture.h170
-rw-r--r--drivers/media/video/davinci/vpif_display.c1998
-rw-r--r--drivers/media/video/davinci/vpif_display.h181
-rw-r--r--drivers/media/video/davinci/vpss.c482
-rw-r--r--drivers/media/video/em28xx/Kconfig58
-rw-r--r--drivers/media/video/em28xx/Makefile15
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c752
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c3417
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c1264
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c1197
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c2606
-rw-r--r--drivers/media/video/fsl-viu.c1673
-rw-r--r--drivers/media/video/gspca/Kconfig425
-rw-r--r--drivers/media/video/gspca/cpia1.c1905
-rw-r--r--drivers/media/video/gspca/finepix.c306
-rw-r--r--drivers/media/video/gspca/gl860/Makefile10
-rw-r--r--drivers/media/video/gspca/gspca.c2456
-rw-r--r--drivers/media/video/gspca/gspca.h259
-rw-r--r--drivers/media/video/gspca/jeilinj.c548
-rw-r--r--drivers/media/video/gspca/jl2005bcd.c557
-rw-r--r--drivers/media/video/gspca/m5602/Makefile11
-rw-r--r--drivers/media/video/gspca/ov519.c4991
-rw-r--r--drivers/media/video/gspca/pac7302.c932
-rw-r--r--drivers/media/video/gspca/sn9c20x.c2428
-rw-r--r--drivers/media/video/gspca/sonixj.c3206
-rw-r--r--drivers/media/video/gspca/spca506.c612
-rw-r--r--drivers/media/video/gspca/sq905.c440
-rw-r--r--drivers/media/video/gspca/sq905c.c347
-rw-r--r--drivers/media/video/gspca/sq930x.c1172
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile10
-rw-r--r--drivers/media/video/gspca/topro.c4969
-rw-r--r--drivers/media/video/gspca/vc032x.c3853
-rw-r--r--drivers/media/video/gspca/vicam.c365
-rw-r--r--drivers/media/video/gspca/xirlink_cit.c3145
-rw-r--r--drivers/media/video/gspca/zc3xx.c7024
-rw-r--r--drivers/media/video/hdpvr/Makefile7
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c1289
-rw-r--r--drivers/media/video/imx074.c475
-rw-r--r--drivers/media/video/ivtv/Kconfig45
-rw-r--r--drivers/media/video/ivtv/Makefile14
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c1498
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h839
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c1038
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.h44
-rw-r--r--drivers/media/video/ivtv/ivtv-firmware.c398
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c1899
-rw-r--r--drivers/media/video/ivtv/ivtv-irq.c1038
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c1018
-rw-r--r--drivers/media/video/ks0127.c724
-rw-r--r--drivers/media/video/m5mols/m5mols.h334
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c992
-rw-r--r--drivers/media/video/mem2mem_testdev.c1120
-rw-r--r--drivers/media/video/msp3400-driver.c899
-rw-r--r--drivers/media/video/mt9m001.c737
-rw-r--r--drivers/media/video/mt9m032.c878
-rw-r--r--drivers/media/video/mt9m111.c1014
-rw-r--r--drivers/media/video/mt9p031.c1071
-rw-r--r--drivers/media/video/mt9t031.c857
-rw-r--r--drivers/media/video/mt9t112.c1125
-rw-r--r--drivers/media/video/mt9v022.c879
-rw-r--r--drivers/media/video/mt9v032.c763
-rw-r--r--drivers/media/video/mx2_camera.c1884
-rw-r--r--drivers/media/video/mx2_emmaprp.c1016
-rw-r--r--drivers/media/video/mx3_camera.c1292
-rw-r--r--drivers/media/video/mxb.c886
-rw-r--r--drivers/media/video/omap/Makefile8
-rw-r--r--drivers/media/video/omap/omap_vout.c2289
-rw-r--r--drivers/media/video/omap1_camera.c1723
-rw-r--r--drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--drivers/media/video/omap24xxcam.c1881
-rw-r--r--drivers/media/video/omap24xxcam.h593
-rw-r--r--drivers/media/video/omap3isp/cfa_coef_table.h61
-rw-r--r--drivers/media/video/omap3isp/isp.c2230
-rw-r--r--drivers/media/video/omap3isp/isp.h351
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c2535
-rw-r--r--drivers/media/video/omap3isp/ispccdc.h209
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c1305
-rw-r--r--drivers/media/video/omap3isp/isph3a_aewb.c374
-rw-r--r--drivers/media/video/omap3isp/isph3a_af.c429
-rw-r--r--drivers/media/video/omap3isp/isphist.c520
-rw-r--r--drivers/media/video/omap3isp/isppreview.c2369
-rw-r--r--drivers/media/video/omap3isp/isppreview.h173
-rw-r--r--drivers/media/video/omap3isp/ispqueue.c1157
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c1776
-rw-r--r--drivers/media/video/omap3isp/ispstat.c1102
-rw-r--r--drivers/media/video/omap3isp/ispstat.h169
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c1392
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h214
-rw-r--r--drivers/media/video/ov2640.c1108
-rw-r--r--drivers/media/video/ov5642.c1073
-rw-r--r--drivers/media/video/ov6650.c1053
-rw-r--r--drivers/media/video/ov772x.c1126
-rw-r--r--drivers/media/video/ov9640.c746
-rw-r--r--drivers/media/video/ov9740.c1005
-rw-r--r--drivers/media/video/pvrusb2/Kconfig65
-rw-r--r--drivers/media/video/pvrusb2/Makefile22
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c569
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1413
-rw-r--r--drivers/media/video/pwc/Makefile4
-rw-r--r--drivers/media/video/pwc/pwc-if.c1165
-rw-r--r--drivers/media/video/rj54n1cb0c.c1414
-rw-r--r--drivers/media/video/s2255drv.c2689
-rw-r--r--drivers/media/video/s5k6aa.c1670
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c1731
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h713
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.c300
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.c1596
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.h213
-rw-r--r--drivers/media/video/s5p-fimc/fimc-m2m.c840
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c1040
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h118
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c722
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c832
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c1515
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.h150
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-hw.h357
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-regs.h170
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c1223
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.c120
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_cmd.h30
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_common.h570
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c343
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h29
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_debug.h48
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.c1044
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_dec.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c1834
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.h23
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.c92
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_intr.h26
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c1397
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.h93
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.c137
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.h24
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.c47
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_shm.h90
-rw-r--r--drivers/media/video/s5p-tv/Kconfig86
-rw-r--r--drivers/media/video/s5p-tv/Makefile19
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c1007
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c487
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c1112
-rw-r--r--drivers/media/video/s5p-tv/regs-sdo.h63
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c452
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c422
-rw-r--r--drivers/media/video/saa7115.c1727
-rw-r--r--drivers/media/video/saa7127.c855
-rw-r--r--drivers/media/video/saa7134/Kconfig64
-rw-r--r--drivers/media/video/saa7134/Makefile16
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c1936
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c1045
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2661
-rw-r--r--drivers/media/video/saa7134/saa7134.h856
-rw-r--r--drivers/media/video/saa7164/Kconfig18
-rw-r--r--drivers/media/video/saa7164/Makefile12
-rw-r--r--drivers/media/video/saa7164/saa7164-api.c1519
-rw-r--r--drivers/media/video/saa7164/saa7164-core.c1526
-rw-r--r--drivers/media/video/saa7164/saa7164.h617
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c2331
-rw-r--r--drivers/media/video/sh_vou.c1497
-rw-r--r--drivers/media/video/smiapp-pll.c418
-rw-r--r--drivers/media/video/smiapp-pll.h103
-rw-r--r--drivers/media/video/smiapp/Makefile5
-rw-r--r--drivers/media/video/smiapp/smiapp-core.c2895
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.c132
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.h128
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.c306
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.h83
-rw-r--r--drivers/media/video/smiapp/smiapp-reg-defs.h503
-rw-r--r--drivers/media/video/smiapp/smiapp-reg.h122
-rw-r--r--drivers/media/video/smiapp/smiapp-regs.c273
-rw-r--r--drivers/media/video/smiapp/smiapp.h252
-rw-r--r--drivers/media/video/soc_camera.c1546
-rw-r--r--drivers/media/video/soc_camera_platform.c197
-rw-r--r--drivers/media/video/tcm825x.c937
-rw-r--r--drivers/media/video/tcm825x.h200
-rw-r--r--drivers/media/video/tea6415c.c187
-rw-r--r--drivers/media/video/tlg2300/Makefile9
-rw-r--r--drivers/media/video/tlg2300/pd-alsa.c332
-rw-r--r--drivers/media/video/tlg2300/pd-radio.c421
-rw-r--r--drivers/media/video/tlg2300/pd-video.c1668
-rw-r--r--drivers/media/video/tm6000/Makefile15
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c528
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c498
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c1818
-rw-r--r--drivers/media/video/tvaudio.c2118
-rw-r--r--drivers/media/video/tvp514x.c1166
-rw-r--r--drivers/media/video/tvp514x_regs.h287
-rw-r--r--drivers/media/video/tvp5150.c1274
-rw-r--r--drivers/media/video/tw9910.c956
-rw-r--r--drivers/media/video/usbvision/Kconfig12
-rw-r--r--drivers/media/video/usbvision/Makefile6
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c1690
-rw-r--r--drivers/media/video/uvc/uvc_driver.c2448
-rw-r--r--drivers/media/video/uvc/uvc_video.c1861
-rw-r--r--drivers/media/video/uvc/uvcvideo.h709
-rw-r--r--drivers/media/video/v4l2-common.c623
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c1045
-rw-r--r--drivers/media/video/v4l2-ctrls.c2651
-rw-r--r--drivers/media/video/v4l2-dev.c1038
-rw-r--r--drivers/media/video/v4l2-device.c280
-rw-r--r--drivers/media/video/v4l2-event.c313
-rw-r--r--drivers/media/video/v4l2-ioctl.c2336
-rw-r--r--drivers/media/video/v4l2-mem2mem.c647
-rw-r--r--drivers/media/video/v4l2-subdev.c470
-rw-r--r--drivers/media/video/videobuf-dvb.c403
-rw-r--r--drivers/media/video/videobuf2-core.c2387
-rw-r--r--drivers/media/video/videobuf2-vmalloc.c222
-rw-r--r--drivers/media/video/vino.c4349
-rw-r--r--drivers/media/video/vivi.c1385
-rw-r--r--drivers/media/video/zoran/Kconfig74
-rw-r--r--drivers/media/video/zoran/zoran_card.c1524
-rw-r--r--drivers/media/video/zoran/zoran_driver.c3090
-rw-r--r--drivers/mfd/88pm800.c5
-rw-r--r--drivers/mfd/88pm805.c3
-rw-r--r--drivers/mfd/88pm860x-core.c21
-rw-r--r--drivers/mfd/Kconfig3
-rw-r--r--drivers/mfd/aat2870-core.c2
-rw-r--r--drivers/mfd/ab3100-core.c6
-rw-r--r--drivers/mfd/ab8500-core.c10
-rw-r--r--drivers/mfd/arizona-core.c6
-rw-r--r--drivers/mfd/asic3.c7
-rw-r--r--drivers/mfd/cs5535-mfd.c2
-rw-r--r--drivers/mfd/da9052-core.c2
-rw-r--r--drivers/mfd/davinci_voicecodec.c2
-rw-r--r--drivers/mfd/db8500-prcmu.c2
-rw-r--r--drivers/mfd/ezx-pcap.c2
-rw-r--r--drivers/mfd/htc-pasic3.c5
-rw-r--r--drivers/mfd/intel_msic.c4
-rw-r--r--drivers/mfd/janz-cmodio.c2
-rw-r--r--drivers/mfd/jz4740-adc.c3
-rw-r--r--drivers/mfd/lm3533-core.c7
-rw-r--r--drivers/mfd/lpc_ich.c24
-rw-r--r--drivers/mfd/lpc_sch.c6
-rw-r--r--drivers/mfd/max77686.c2
-rw-r--r--drivers/mfd/max77693-irq.c36
-rw-r--r--drivers/mfd/max77693.c16
-rw-r--r--drivers/mfd/max8925-core.c12
-rw-r--r--drivers/mfd/max8997.c2
-rw-r--r--drivers/mfd/max8998.c8
-rw-r--r--drivers/mfd/mc13xxx-core.c2
-rw-r--r--drivers/mfd/mfd-core.c12
-rw-r--r--drivers/mfd/palmas.c3
-rw-r--r--drivers/mfd/rc5t583.c2
-rw-r--r--drivers/mfd/rdc321x-southbridge.c3
-rw-r--r--drivers/mfd/sec-core.c8
-rw-r--r--drivers/mfd/sta2x11-mfd.c4
-rw-r--r--drivers/mfd/stmpe.c2
-rw-r--r--drivers/mfd/t7l66xb.c2
-rw-r--r--drivers/mfd/tc3589x.c8
-rw-r--r--drivers/mfd/tc6387xb.c2
-rw-r--r--drivers/mfd/tc6393xb.c4
-rw-r--r--drivers/mfd/ti-ssp.c2
-rw-r--r--drivers/mfd/timberdale.c12
-rw-r--r--drivers/mfd/tps6105x.c2
-rw-r--r--drivers/mfd/tps65010.c3
-rw-r--r--drivers/mfd/tps6507x.c2
-rw-r--r--drivers/mfd/tps65090.c2
-rw-r--r--drivers/mfd/tps65217.c130
-rw-r--r--drivers/mfd/tps6586x.c3
-rw-r--r--drivers/mfd/tps65910.c2
-rw-r--r--drivers/mfd/tps65912-core.c2
-rw-r--r--drivers/mfd/twl4030-audio.c2
-rw-r--r--drivers/mfd/twl6040-core.c2
-rw-r--r--drivers/mfd/vx855.c2
-rw-r--r--drivers/mfd/wl1273-core.c2
-rw-r--r--drivers/mfd/wm831x-core.c16
-rw-r--r--drivers/mfd/wm831x-otp.c8
-rw-r--r--drivers/mfd/wm8400-core.c2
-rw-r--r--drivers/mfd/wm8994-core.c4
-rw-r--r--drivers/misc/mei/interrupt.c2
-rw-r--r--drivers/misc/mei/main.c27
-rw-r--r--drivers/misc/sgi-xp/xpc_uv.c84
-rw-r--r--drivers/misc/ti-st/st_ll.c2
-rw-r--r--drivers/mmc/card/block.c26
-rw-r--r--drivers/mmc/host/atmel-mci.c6
-rw-r--r--drivers/mmc/host/bfin_sdh.c7
-rw-r--r--drivers/mmc/host/dw_mmc.c85
-rw-r--r--drivers/mmc/host/mxs-mmc.c14
-rw-r--r--drivers/mmc/host/omap.c382
-rw-r--r--drivers/mmc/host/omap_hsmmc.c204
-rw-r--r--drivers/mmc/host/sdhci-esdhc.h6
-rw-r--r--drivers/mtd/maps/uclinux.c5
-rw-r--r--drivers/mtd/mtdchar.c48
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/omap2.c105
-rw-r--r--drivers/mtd/ubi/vtbl.c4
-rw-r--r--drivers/net/appletalk/cops.c4
-rw-r--r--drivers/net/appletalk/ltpc.c4
-rw-r--r--drivers/net/bonding/bond_main.c12
-rw-r--r--drivers/net/can/janz-ican3.c4
-rw-r--r--drivers/net/can/mcp251x.c11
-rw-r--r--drivers/net/can/sja1000/sja1000_platform.c4
-rw-r--r--drivers/net/can/softing/softing_fw.c7
-rw-r--r--drivers/net/can/ti_hecc.c2
-rw-r--r--drivers/net/cris/eth_v10.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h5
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c16
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h15
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h25
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c10
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c18
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c122
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c15
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sge.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4vf/sge.c2
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c10
-rw-r--r--drivers/net/ethernet/emulex/benet/be_cmds.c6
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c4
-rw-r--r--drivers/net/ethernet/freescale/fs_enet/mii-fec.c8
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c2
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c1
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c4
-rw-r--r--drivers/net/ethernet/i825xx/znet.c13
-rw-r--r--drivers/net/ethernet/ibm/ibmveth.c26
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c11
-rw-r--r--drivers/net/ethernet/intel/e1000e/82571.c10
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c84
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_82575.c16
-rw-r--r--drivers/net/ethernet/intel/igb/e1000_regs.h8
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c28
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c21
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c3
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_rx.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_tx.c17
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c39
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h12
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mcg.c110
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4.h80
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mr.c27
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/profile.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/resource_tracker.c116
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/sense.c14
-rw-r--r--drivers/net/ethernet/nxp/lpc_eth.c13
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c4
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c4
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c4
-rw-r--r--drivers/net/ethernet/renesas/Kconfig4
-rw-r--r--drivers/net/ethernet/renesas/sh_eth.c11
-rw-r--r--drivers/net/ethernet/seeq/sgiseeq.c1
-rw-r--r--drivers/net/ethernet/sfc/efx.c6
-rw-r--r--drivers/net/ethernet/sfc/efx.h14
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c20
-rw-r--r--drivers/net/ethernet/sfc/tx.c19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/descs_com.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac100.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/mmc_core.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h4
-rw-r--r--drivers/net/ethernet/ti/davinci_cpdma.c3
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c4
-rw-r--r--drivers/net/ethernet/xscale/ixp4xx_eth.c1
-rw-r--r--drivers/net/fddi/skfp/pmf.c2
-rw-r--r--drivers/net/hyperv/netvsc.c7
-rw-r--r--drivers/net/hyperv/rndis_filter.c11
-rw-r--r--drivers/net/irda/bfin_sir.c8
-rw-r--r--drivers/net/irda/ks959-sir.c1
-rw-r--r--drivers/net/irda/ksdazzle-sir.c1
-rw-r--r--drivers/net/irda/sh_sir.c2
-rw-r--r--drivers/net/macvtap.c3
-rw-r--r--drivers/net/netconsole.c6
-rw-r--r--drivers/net/phy/bcm87xx.c2
-rw-r--r--drivers/net/phy/mdio-mux-gpio.c1
-rw-r--r--drivers/net/phy/mdio-mux.c2
-rw-r--r--drivers/net/phy/micrel.c45
-rw-r--r--drivers/net/phy/smsc.c28
-rw-r--r--drivers/net/ppp/pppoe.c2
-rw-r--r--drivers/net/ppp/pptp.c4
-rw-r--r--drivers/net/team/team.c60
-rw-r--r--drivers/net/tun.c1
-rw-r--r--drivers/net/usb/asix_devices.c4
-rw-r--r--drivers/net/usb/cdc-phonet.c3
-rw-r--r--drivers/net/usb/cdc_ncm.c20
-rw-r--r--drivers/net/usb/qmi_wwan.c271
-rw-r--r--drivers/net/usb/sierra_net.c54
-rw-r--r--drivers/net/usb/smsc75xx.c1
-rw-r--r--drivers/net/usb/usbnet.c18
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c2
-rw-r--r--drivers/net/wan/dscc4.c5
-rw-r--r--drivers/net/wan/ixp4xx_hss.c1
-rw-r--r--drivers/net/wimax/i2400m/fw.c4
-rw-r--r--drivers/net/wireless/at76c50x-usb.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c6
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.c2
-rw-r--r--drivers/net/wireless/ath/ath5k/eeprom.h1
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_paprd.c105
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/gpio.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h5
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c20
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c18
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c3
-rw-r--r--drivers/net/wireless/b43/main.c21
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c26
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c30
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c21
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c7
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c6
-rw-r--r--drivers/net/wireless/ipw2x00/ipw2100.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/debugfs.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rs.c13
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c31
-rw-r--r--drivers/net/wireless/libertas/cfg.c1
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c6
-rw-r--r--drivers/net/wireless/libertas/if_usb.c1
-rw-r--r--drivers/net/wireless/libertas/main.c5
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c15
-rw-r--r--drivers/net/wireless/p54/p54usb.c2
-rw-r--r--drivers/net/wireless/rndis_wlan.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c11
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.h17
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c69
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c80
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c22
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c12
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c9
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.h3
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187/dev.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/def.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c12
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/sw.c6
-rw-r--r--drivers/net/xen-netfront.c39
-rw-r--r--drivers/of/base.c27
-rw-r--r--drivers/pci/pci-acpi.c4
-rw-r--r--drivers/pci/pci-driver.c13
-rw-r--r--drivers/pci/pci-sysfs.c42
-rw-r--r--drivers/pci/pci.c1
-rw-r--r--drivers/pci/pcie/portdrv_pci.c14
-rw-r--r--drivers/pci/probe.c31
-rw-r--r--drivers/pinctrl/core.c13
-rw-r--r--drivers/pinctrl/pinctrl-imx23.c2
-rw-r--r--drivers/pinctrl/pinctrl-imx28.c2
-rw-r--r--drivers/pinctrl/pinctrl-imx51.c2
-rw-r--r--drivers/pinctrl/pinctrl-nomadik-db8500.c7
-rw-r--r--drivers/pinctrl/pinctrl-nomadik.c3
-rw-r--r--drivers/pinctrl/pinctrl-sirf.c1
-rw-r--r--drivers/pinctrl/pinctrl-u300.c8
-rw-r--r--drivers/platform/Makefile1
-rw-r--r--drivers/platform/olpc/Makefile4
-rw-r--r--drivers/platform/olpc/olpc-ec.c336
-rw-r--r--drivers/platform/x86/Kconfig6
-rw-r--r--drivers/platform/x86/acer-wmi.c2
-rw-r--r--drivers/platform/x86/apple-gmux.c432
-rw-r--r--drivers/platform/x86/asus-laptop.c10
-rw-r--r--drivers/platform/x86/asus-wmi.c25
-rw-r--r--drivers/platform/x86/asus-wmi.h1
-rw-r--r--drivers/platform/x86/classmate-laptop.c16
-rw-r--r--drivers/platform/x86/dell-laptop.c12
-rw-r--r--drivers/platform/x86/eeepc-laptop.c10
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c2
-rw-r--r--drivers/platform/x86/hdaps.c2
-rw-r--r--drivers/platform/x86/hp_accel.c2
-rw-r--r--drivers/platform/x86/ideapad-laptop.c110
-rw-r--r--drivers/platform/x86/msi-laptop.c4
-rw-r--r--drivers/platform/x86/panasonic-laptop.c4
-rw-r--r--drivers/platform/x86/samsung-laptop.c4
-rw-r--r--drivers/platform/x86/sony-laptop.c12
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c22
-rw-r--r--drivers/platform/x86/toshiba_acpi.c2
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c4
-rw-r--r--drivers/platform/x86/xo1-rfkill.c3
-rw-r--r--drivers/platform/x86/xo15-ebook.c2
-rw-r--r--drivers/power/olpc_battery.c1
-rw-r--r--drivers/power/pda_power.c6
-rw-r--r--drivers/pwm/Kconfig31
-rw-r--r--drivers/pwm/core.c12
-rw-r--r--drivers/pwm/pwm-samsung.c1
-rw-r--r--drivers/pwm/pwm-tegra.c4
-rw-r--r--drivers/pwm/pwm-tiecap.c11
-rw-r--r--drivers/pwm/pwm-tiehrpwm.c33
-rw-r--r--drivers/pwm/pwm-vt8500.c2
-rw-r--r--drivers/rapidio/devices/tsi721.c12
-rw-r--r--drivers/regulator/ab3100.c1
-rw-r--r--drivers/regulator/anatop-regulator.c5
-rw-r--r--drivers/regulator/core.c2
-rw-r--r--drivers/regulator/gpio-regulator.c38
-rw-r--r--drivers/regulator/palmas-regulator.c23
-rw-r--r--drivers/regulator/tps65217-regulator.c124
-rw-r--r--drivers/regulator/tps6586x-regulator.c8
-rw-r--r--drivers/regulator/twl-regulator.c5
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c6
-rw-r--r--drivers/rtc/interface.c2
-rw-r--r--drivers/rtc/rtc-88pm80x.c4
-rw-r--r--drivers/rtc/rtc-at91sam9.c22
-rw-r--r--drivers/rtc/rtc-cmos.c1
-rw-r--r--drivers/rtc/rtc-pcf2123.c2
-rw-r--r--drivers/rtc/rtc-rs5c348.c7
-rw-r--r--drivers/rtc/rtc-twl.c5
-rw-r--r--drivers/rtc/rtc-wm831x.c24
-rw-r--r--drivers/s390/block/dasd.c17
-rw-r--r--drivers/s390/block/dasd_alias.c27
-rw-r--r--drivers/s390/block/dasd_eckd.c34
-rw-r--r--drivers/s390/block/dasd_ioctl.c7
-rw-r--r--drivers/s390/char/sclp_sdias.c2
-rw-r--r--drivers/s390/cio/device.c7
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c3
-rw-r--r--drivers/scsi/hpsa.c3
-rw-r--r--drivers/scsi/megaraid/megaraid_sas_base.c3
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c20
-rw-r--r--drivers/scsi/scsi_error.c10
-rw-r--r--drivers/scsi/scsi_lib.c5
-rw-r--r--drivers/scsi/scsi_scan.c10
-rw-r--r--drivers/scsi/scsi_transport_fc.c38
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c2
-rw-r--r--drivers/scsi/virtio_scsi.c2
-rw-r--r--drivers/sh/intc/Kconfig4
-rw-r--r--drivers/sh/intc/Makefile2
-rw-r--r--drivers/sh/intc/core.c38
-rw-r--r--drivers/sh/intc/internals.h5
-rw-r--r--drivers/sh/intc/irqdomain.c68
-rw-r--r--drivers/sh/pfc/pinctrl.c39
-rw-r--r--drivers/spi/spi-bcm63xx.c35
-rw-r--r--drivers/spi/spi-coldfire-qspi.c5
-rw-r--r--drivers/spi/spi-omap2-mcspi.c235
-rw-r--r--drivers/spi/spi-pl022.c1
-rw-r--r--drivers/spi/spi-s3c64xx.c14
-rw-r--r--drivers/staging/android/android_alarm.h4
-rw-r--r--drivers/staging/bcm/Misc.c31
-rw-r--r--drivers/staging/comedi/drivers.c2
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c3
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1723.c2
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c2
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.c7
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236.c7
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc263.c7
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci224.c7
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci230.c7
-rw-r--r--drivers/staging/comedi/drivers/daqboard2000.c17
-rw-r--r--drivers/staging/comedi/drivers/das08.c11
-rw-r--r--drivers/staging/comedi/drivers/dt3000.c6
-rw-r--r--drivers/staging/comedi/drivers/rtd520.c26
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c4
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c4
-rw-r--r--drivers/staging/comedi/drivers/usbduxsigma.c4
-rw-r--r--drivers/staging/csr/Kconfig2
-rw-r--r--drivers/staging/gdm72xx/sdio_boot.c7
-rw-r--r--drivers/staging/gdm72xx/usb_boot.c22
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_ring.c4
-rw-r--r--drivers/staging/iio/adc/ad7192.c50
-rw-r--r--drivers/staging/iio/adc/ad7298_ring.c2
-rw-r--r--drivers/staging/iio/adc/ad7780.c10
-rw-r--r--drivers/staging/iio/adc/ad7793.c99
-rw-r--r--drivers/staging/iio/gyro/adis16260_core.c2
-rw-r--r--drivers/staging/iio/imu/adis16400_core.c2
-rw-r--r--drivers/staging/iio/meter/ade7753.c2
-rw-r--r--drivers/staging/iio/meter/ade7754.c2
-rw-r--r--drivers/staging/iio/meter/ade7759.c2
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/as102/Makefile2
-rw-r--r--drivers/staging/media/cxd2099/Makefile6
-rw-r--r--drivers/staging/media/cxd2099/cxd2099.c13
-rw-r--r--drivers/staging/media/dt3155v4l/dt3155v4l.c29
-rw-r--r--drivers/staging/media/easycap/Kconfig30
-rw-r--r--drivers/staging/media/easycap/Makefile10
-rw-r--r--drivers/staging/media/easycap/README141
-rw-r--r--drivers/staging/media/easycap/easycap.h567
-rw-r--r--drivers/staging/media/easycap/easycap_ioctl.c2443
-rw-r--r--drivers/staging/media/easycap/easycap_low.c968
-rw-r--r--drivers/staging/media/easycap/easycap_main.c4239
-rw-r--r--drivers/staging/media/easycap/easycap_settings.c696
-rw-r--r--drivers/staging/media/easycap/easycap_sound.c750
-rw-r--r--drivers/staging/media/easycap/easycap_testcard.c155
-rw-r--r--drivers/staging/media/go7007/Makefile6
-rw-r--r--drivers/staging/media/go7007/go7007-v4l2.c4
-rw-r--r--drivers/staging/media/lirc/Kconfig6
-rw-r--r--drivers/staging/media/lirc/Makefile1
-rw-r--r--drivers/staging/media/lirc/lirc_ene0100.h169
-rw-r--r--drivers/staging/media/lirc/lirc_igorplugusb.c4
-rw-r--r--drivers/staging/media/lirc/lirc_ttusbir.c376
-rw-r--r--drivers/staging/media/lirc/lirc_zilog.c3
-rw-r--r--drivers/staging/nvec/nvec.c2
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c1
-rw-r--r--drivers/staging/omapdrm/omap_connector.c41
-rw-r--r--drivers/staging/ozwpan/ozcdev.c3
-rw-r--r--drivers/staging/rtl8712/recv_linux.c7
-rw-r--r--drivers/staging/vt6656/dpc.c2
-rw-r--r--drivers/staging/vt6656/main_usb.c2
-rw-r--r--drivers/staging/vt6656/rxtx.c38
-rw-r--r--drivers/staging/winbond/wbusb.c2
-rw-r--r--drivers/staging/wlan-ng/cfg80211.c4
-rw-r--r--drivers/staging/zcache/zcache-main.c7
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c11
-rw-r--r--drivers/target/target_core_alua.c7
-rw-r--r--drivers/target/target_core_device.c7
-rw-r--r--drivers/target/target_core_file.c32
-rw-r--r--drivers/target/target_core_iblock.c17
-rw-r--r--drivers/target/target_core_pr.c8
-rw-r--r--drivers/target/target_core_pscsi.c36
-rw-r--r--drivers/target/target_core_spc.c35
-rw-r--r--drivers/target/target_core_transport.c161
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h1
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c8
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c4
-rw-r--r--drivers/tty/serial/Kconfig10
-rw-r--r--drivers/tty/serial/ifx6x60.c2
-rw-r--r--drivers/tty/serial/imx.c30
-rw-r--r--drivers/tty/serial/mxs-auart.c14
-rw-r--r--drivers/tty/serial/pmac_zilog.c12
-rw-r--r--drivers/tty/serial/sh-sci.c5
-rw-r--r--drivers/tty/serial/uartlite.c3
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/chipidea/Kconfig9
-rw-r--r--drivers/usb/chipidea/udc.c59
-rw-r--r--drivers/usb/class/cdc-acm.c3
-rw-r--r--drivers/usb/class/cdc-wdm.c12
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/hcd.c6
-rw-r--r--drivers/usb/core/hub.c9
-rw-r--r--drivers/usb/core/quirks.c4
-rw-r--r--drivers/usb/dwc3/core.c9
-rw-r--r--drivers/usb/dwc3/ep0.c1
-rw-r--r--drivers/usb/dwc3/gadget.c19
-rw-r--r--drivers/usb/early/ehci-dbgp.c2
-rw-r--r--drivers/usb/gadget/at91_udc.c6
-rw-r--r--drivers/usb/gadget/dummy_hcd.c41
-rw-r--r--drivers/usb/gadget/f_fs.c4
-rw-r--r--drivers/usb/gadget/f_phonet.c2
-rw-r--r--drivers/usb/gadget/goku_udc.c2
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c12
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c3
-rw-r--r--drivers/usb/gadget/storage_common.c12
-rw-r--r--drivers/usb/gadget/u_ether.c6
-rw-r--r--drivers/usb/gadget/u_serial.c4
-rw-r--r--drivers/usb/gadget/u_uac1.c6
-rw-r--r--drivers/usb/host/ehci-omap.c167
-rw-r--r--drivers/usb/host/ehci-q.c12
-rw-r--r--drivers/usb/host/ehci-sead3.c2
-rw-r--r--drivers/usb/host/ehci-tegra.c3
-rw-r--r--drivers/usb/host/isp1362-hcd.c8
-rw-r--r--drivers/usb/host/ohci-at91.c13
-rw-r--r--drivers/usb/host/ohci-omap.c2
-rw-r--r--drivers/usb/host/pci-quirks.c49
-rw-r--r--drivers/usb/host/pci-quirks.h2
-rw-r--r--drivers/usb/host/xhci-hub.c42
-rw-r--r--drivers/usb/host/xhci-pci.c10
-rw-r--r--drivers/usb/host/xhci-plat.c2
-rw-r--r--drivers/usb/host/xhci-ring.c40
-rw-r--r--drivers/usb/host/xhci.c129
-rw-r--r--drivers/usb/host/xhci.h9
-rw-r--r--drivers/usb/misc/emi62.c2
-rw-r--r--drivers/usb/musb/Kconfig4
-rw-r--r--drivers/usb/musb/musb_dsps.c19
-rw-r--r--drivers/usb/musb/musb_host.c2
-rw-r--r--drivers/usb/musb/musbhsdma.c2
-rw-r--r--drivers/usb/musb/tusb6010.c2
-rw-r--r--drivers/usb/otg/isp1301_omap.c1
-rw-r--r--drivers/usb/renesas_usbhs/common.c6
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c4
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c8
-rw-r--r--drivers/usb/serial/bus.c15
-rw-r--r--drivers/usb/serial/ftdi_sio.c23
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h36
-rw-r--r--drivers/usb/serial/ipw.c3
-rw-r--r--drivers/usb/serial/mos7840.c16
-rw-r--r--drivers/usb/serial/option.c287
-rw-r--r--drivers/usb/serial/qcserial.c47
-rw-r--r--drivers/usb/serial/usb-wwan.h3
-rw-r--r--drivers/usb/serial/usb_wwan.c68
-rw-r--r--drivers/vfio/Kconfig16
-rw-r--r--drivers/vfio/Makefile3
-rw-r--r--drivers/vfio/pci/Kconfig8
-rw-r--r--drivers/vfio/pci/Makefile4
-rw-r--r--drivers/vfio/pci/vfio_pci.c579
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c1540
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c776
-rw-r--r--drivers/vfio/pci/vfio_pci_private.h91
-rw-r--r--drivers/vfio/pci/vfio_pci_rdwr.c269
-rw-r--r--drivers/vfio/vfio.c1415
-rw-r--r--drivers/vfio/vfio_iommu_type1.c753
-rw-r--r--drivers/vhost/Kconfig3
-rw-r--r--drivers/vhost/Kconfig.tcm6
-rw-r--r--drivers/vhost/Makefile2
-rw-r--r--drivers/vhost/tcm_vhost.c1649
-rw-r--r--drivers/vhost/tcm_vhost.h103
-rw-r--r--drivers/video/aty/aty128fb.c180
-rw-r--r--drivers/video/auo_k190x.c2
-rw-r--r--drivers/video/console/bitblit.c2
-rw-r--r--drivers/video/console/fbcon.c11
-rw-r--r--drivers/video/da8xx-fb.c78
-rw-r--r--drivers/video/epson1355fb.c8
-rw-r--r--drivers/video/exynos/exynos_dp_core.c23
-rw-r--r--drivers/video/exynos/exynos_dp_core.h4
-rw-r--r--drivers/video/exynos/exynos_dp_reg.c4
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c2
-rw-r--r--drivers/video/exynos/s6e8ax0.h21
-rw-r--r--drivers/video/fb_defio.c2
-rw-r--r--drivers/video/fb_draw.h7
-rw-r--r--drivers/video/grvga.c47
-rw-r--r--drivers/video/mb862xx/mb862xxfbdrv.c2
-rw-r--r--drivers/video/mx3fb.c55
-rw-r--r--drivers/video/omap2/displays/panel-acx565akm.c10
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c179
-rw-r--r--drivers/video/omap2/displays/panel-lgphilips-lb035q02.c8
-rw-r--r--drivers/video/omap2/displays/panel-n8x0.c1
-rw-r--r--drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c9
-rw-r--r--drivers/video/omap2/displays/panel-picodlp.c9
-rw-r--r--drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c9
-rw-r--r--drivers/video/omap2/displays/panel-taal.c1
-rw-r--r--drivers/video/omap2/displays/panel-tfp410.c7
-rw-r--r--drivers/video/omap2/displays/panel-tpo-td043mtea1.c8
-rw-r--r--drivers/video/omap2/dss/Kconfig4
-rw-r--r--drivers/video/omap2/dss/apply.c91
-rw-r--r--drivers/video/omap2/dss/dispc.c494
-rw-r--r--drivers/video/omap2/dss/dispc.h28
-rw-r--r--drivers/video/omap2/dss/display.c40
-rw-r--r--drivers/video/omap2/dss/dpi.c64
-rw-r--r--drivers/video/omap2/dss/dsi.c152
-rw-r--r--drivers/video/omap2/dss/dss.c19
-rw-r--r--drivers/video/omap2/dss/dss.h54
-rw-r--r--drivers/video/omap2/dss/dss_features.h5
-rw-r--r--drivers/video/omap2/dss/hdmi.c246
-rw-r--r--drivers/video/omap2/dss/hdmi_panel.c9
-rw-r--r--drivers/video/omap2/dss/manager.c51
-rw-r--r--drivers/video/omap2/dss/overlay.c33
-rw-r--r--drivers/video/omap2/dss/rfbi.c40
-rw-r--r--drivers/video/omap2/dss/sdi.c56
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h21
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c26
-rw-r--r--drivers/video/omap2/dss/venc.c8
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c53
-rw-r--r--drivers/video/s3fb.c31
-rw-r--r--drivers/video/sh_mipi_dsi.c7
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1117
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h5
-rw-r--r--drivers/video/sh_mobile_meram.c235
-rw-r--r--drivers/video/smscufx.c2
-rw-r--r--drivers/video/w100fb.c12
-rw-r--r--drivers/w1/slaves/w1_therm.c9
-rw-r--r--drivers/w1/w1_family.h1
-rw-r--r--drivers/watchdog/booke_wdt.c7
-rw-r--r--drivers/watchdog/da9052_wdt.c1
-rw-r--r--drivers/watchdog/hpwdt.c3
-rw-r--r--drivers/watchdog/orion_wdt.c8
-rw-r--r--drivers/watchdog/watchdog_core.c3
-rw-r--r--drivers/xen/gntdev.c5
-rw-r--r--drivers/xen/grant-table.c6
-rw-r--r--drivers/xen/platform-pci.c15
-rw-r--r--drivers/xen/swiotlb-xen.c2
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c8
-rw-r--r--drivers/zorro/zorro.c2
-rw-r--r--fs/9p/vfs_file.c3
-rw-r--r--fs/autofs4/expire.c36
-rw-r--r--fs/bio.c13
-rw-r--r--fs/block_dev.c3
-rw-r--r--fs/btrfs/backref.c4
-rw-r--r--fs/btrfs/compression.c1
-rw-r--r--fs/btrfs/ctree.c9
-rw-r--r--fs/btrfs/ctree.h3
-rw-r--r--fs/btrfs/delayed-inode.c12
-rw-r--r--fs/btrfs/delayed-ref.c163
-rw-r--r--fs/btrfs/delayed-ref.h4
-rw-r--r--fs/btrfs/disk-io.c56
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/extent-tree.c123
-rw-r--r--fs/btrfs/extent_io.c17
-rw-r--r--fs/btrfs/file-item.c4
-rw-r--r--fs/btrfs/file.c3
-rw-r--r--fs/btrfs/inode.c335
-rw-r--r--fs/btrfs/ioctl.c12
-rw-r--r--fs/btrfs/locking.c2
-rw-r--r--fs/btrfs/ordered-data.c2
-rw-r--r--fs/btrfs/qgroup.c4
-rw-r--r--fs/btrfs/root-tree.c4
-rw-r--r--fs/btrfs/super.c19
-rw-r--r--fs/btrfs/transaction.c10
-rw-r--r--fs/btrfs/volumes.c37
-rw-r--r--fs/btrfs/volumes.h2
-rw-r--r--fs/buffer.c94
-rw-r--r--fs/ceph/addr.c3
-rw-r--r--fs/ceph/debugfs.c1
-rw-r--r--fs/ceph/dir.c38
-rw-r--r--fs/ceph/file.c62
-rw-r--r--fs/ceph/inode.c15
-rw-r--r--fs/ceph/ioctl.c3
-rw-r--r--fs/ceph/super.h6
-rw-r--r--fs/cifs/cifs_unicode.c2
-rw-r--r--fs/cifs/cifsglob.h10
-rw-r--r--fs/cifs/cifsproto.h11
-rw-r--r--fs/cifs/cifssmb.c42
-rw-r--r--fs/cifs/dir.c9
-rw-r--r--fs/cifs/file.c2
-rw-r--r--fs/cifs/inode.c324
-rw-r--r--fs/cifs/link.c2
-rw-r--r--fs/cifs/smb1ops.c24
-rw-r--r--fs/cifs/smb2inode.c39
-rw-r--r--fs/cifs/smb2misc.c16
-rw-r--r--fs/cifs/smb2ops.c3
-rw-r--r--fs/cifs/smb2pdu.h10
-rw-r--r--fs/cifs/smb2proto.h8
-rw-r--r--fs/cifs/transport.c9
-rw-r--r--fs/compat.c10
-rw-r--r--fs/dcache.c12
-rw-r--r--fs/debugfs/file.c76
-rw-r--r--fs/direct-io.c5
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h24
-rw-r--r--fs/ecryptfs/file.c100
-rw-r--r--fs/ecryptfs/inode.c100
-rw-r--r--fs/ecryptfs/main.c23
-rw-r--r--fs/ecryptfs/messaging.c136
-rw-r--r--fs/ecryptfs/miscdev.c98
-rw-r--r--fs/ecryptfs/mmap.c39
-rw-r--r--fs/eventpoll.c2
-rw-r--r--fs/exec.c19
-rw-r--r--fs/exofs/inode.c27
-rw-r--r--fs/exofs/ore.c14
-rw-r--r--fs/exofs/super.c11
-rw-r--r--fs/ext2/inode.c5
-rw-r--r--fs/ext2/super.c33
-rw-r--r--fs/ext3/inode.c25
-rw-r--r--fs/ext3/super.c11
-rw-r--r--fs/ext4/balloc.c62
-rw-r--r--fs/ext4/bitmap.c1
-rw-r--r--fs/ext4/extents.c1
-rw-r--r--fs/ext4/inode.c25
-rw-r--r--fs/ext4/mmp.c6
-rw-r--r--fs/ext4/super.c48
-rw-r--r--fs/fat/file.c15
-rw-r--r--fs/file_table.c4
-rw-r--r--fs/fs-writeback.c5
-rw-r--r--fs/fuse/control.c4
-rw-r--r--fs/fuse/cuse.c4
-rw-r--r--fs/fuse/dev.c1
-rw-r--r--fs/fuse/dir.c3
-rw-r--r--fs/fuse/file.c19
-rw-r--r--fs/fuse/fuse_i.h3
-rw-r--r--fs/fuse/inode.c44
-rw-r--r--fs/gfs2/file.c49
-rw-r--r--fs/gfs2/inode.c8
-rw-r--r--fs/gfs2/meta_io.c2
-rw-r--r--fs/gfs2/rgrp.c66
-rw-r--r--fs/gfs2/trans.c4
-rw-r--r--fs/hfs/mdb.c4
-rw-r--r--fs/hugetlbfs/inode.c4
-rw-r--r--fs/inode.c12
-rw-r--r--fs/internal.h4
-rw-r--r--fs/jbd/journal.c9
-rw-r--r--fs/jbd2/journal.c7
-rw-r--r--fs/lockd/clntproc.c14
-rw-r--r--fs/lockd/svc4proc.c1
-rw-r--r--fs/lockd/svclock.c4
-rw-r--r--fs/lockd/svcproc.c1
-rw-r--r--fs/locks.c6
-rw-r--r--fs/logfs/dev_bdev.c15
-rw-r--r--fs/logfs/inode.c18
-rw-r--r--fs/logfs/journal.c2
-rw-r--r--fs/logfs/readwrite.c1
-rw-r--r--fs/logfs/segment.c2
-rw-r--r--fs/namei.c323
-rw-r--r--fs/namespace.c107
-rw-r--r--fs/nfs/Kconfig8
-rw-r--r--fs/nfs/Makefile18
-rw-r--r--fs/nfs/client.c2
-rw-r--r--fs/nfs/direct.c82
-rw-r--r--fs/nfs/file.c32
-rw-r--r--fs/nfs/idmap.c62
-rw-r--r--fs/nfs/inode.c6
-rw-r--r--fs/nfs/internal.h7
-rw-r--r--fs/nfs/nfs3proc.c4
-rw-r--r--fs/nfs/nfs4_fs.h3
-rw-r--r--fs/nfs/nfs4client.c2
-rw-r--r--fs/nfs/nfs4file.c4
-rw-r--r--fs/nfs/nfs4proc.c127
-rw-r--r--fs/nfs/nfs4super.c15
-rw-r--r--fs/nfs/nfs4xdr.c41
-rw-r--r--fs/nfs/objlayout/objio_osd.c55
-rw-r--r--fs/nfs/pagelist.c6
-rw-r--r--fs/nfs/pnfs.c39
-rw-r--r--fs/nfs/pnfs.h2
-rw-r--r--fs/nfs/read.c6
-rw-r--r--fs/nfs/super.c41
-rw-r--r--fs/nfs/write.c104
-rw-r--r--fs/nfsd/nfs4callback.c4
-rw-r--r--fs/nfsd/nfs4recover.c9
-rw-r--r--fs/nfsd/nfsfh.c1
-rw-r--r--fs/nfsd/nfsproc.c9
-rw-r--r--fs/nfsd/state.h1
-rw-r--r--fs/nfsd/vfs.c79
-rw-r--r--fs/nfsd/vfs.h11
-rw-r--r--fs/nilfs2/file.c18
-rw-r--r--fs/nilfs2/ioctl.c2
-rw-r--r--fs/nilfs2/segment.c5
-rw-r--r--fs/nilfs2/super.c4
-rw-r--r--fs/nilfs2/the_nilfs.h2
-rw-r--r--fs/ntfs/file.c3
-rw-r--r--fs/ocfs2/file.c11
-rw-r--r--fs/ocfs2/ioctl.c14
-rw-r--r--fs/ocfs2/journal.c7
-rw-r--r--fs/ocfs2/mmap.c2
-rw-r--r--fs/ocfs2/refcounttree.c11
-rw-r--r--fs/open.c24
-rw-r--r--fs/pipe.c75
-rw-r--r--fs/proc/proc_sysctl.c5
-rw-r--r--fs/quota/dquot.c2
-rw-r--r--fs/reiserfs/bitmap.c2
-rw-r--r--fs/reiserfs/inode.c2
-rw-r--r--fs/splice.c3
-rw-r--r--fs/stat.c2
-rw-r--r--fs/super.c294
-rw-r--r--fs/sysfs/bin.c2
-rw-r--r--fs/ubifs/debug.h2
-rw-r--r--fs/ubifs/file.c10
-rw-r--r--fs/ubifs/lpt.c5
-rw-r--r--fs/ubifs/recovery.c2
-rw-r--r--fs/ubifs/replay.c3
-rw-r--r--fs/ubifs/super.c5
-rw-r--r--fs/udf/file.c35
-rw-r--r--fs/udf/inode.c5
-rw-r--r--fs/udf/super.c7
-rw-r--r--fs/xfs/xfs_aops.c18
-rw-r--r--fs/xfs/xfs_buf.c5
-rw-r--r--fs/xfs/xfs_buf.h41
-rw-r--r--fs/xfs/xfs_discard.c6
-rw-r--r--fs/xfs/xfs_file.c10
-rw-r--r--fs/xfs/xfs_ialloc.c17
-rw-r--r--fs/xfs/xfs_ioctl.c55
-rw-r--r--fs/xfs/xfs_ioctl32.c12
-rw-r--r--fs/xfs/xfs_iomap.c4
-rw-r--r--fs/xfs/xfs_mount.c2
-rw-r--r--fs/xfs/xfs_mount.h3
-rw-r--r--fs/xfs/xfs_rtalloc.c2
-rw-r--r--fs/xfs/xfs_super.c1
-rw-r--r--fs/xfs/xfs_sync.c2
-rw-r--r--fs/xfs/xfs_trans.c17
-rw-r--r--fs/xfs/xfs_trans.h2
-rw-r--r--include/acpi/acpixf.h4
-rw-r--r--include/acpi/actypes.h2
-rw-r--r--include/asm-generic/mutex-xchg.h11
-rw-r--r--include/asm-generic/unistd.h4
-rw-r--r--include/drm/drm_crtc.h5
-rw-r--r--include/drm/drm_fourcc.h6
-rw-r--r--include/drm/drm_mode.h5
-rw-r--r--include/drm/drm_pciids.h3
-rw-r--r--include/drm/radeon_drm.h2
-rw-r--r--include/linux/Kbuild2
-rw-r--r--include/linux/acpi.h2
-rw-r--r--include/linux/amba/pl08x.h156
-rw-r--r--include/linux/atmel-ssc.h1
-rw-r--r--include/linux/audit.h4
-rw-r--r--include/linux/backing-dev.h4
-rw-r--r--include/linux/bcma/bcma_driver_chipcommon.h6
-rw-r--r--include/linux/blk_types.h2
-rw-r--r--include/linux/blkdev.h75
-rw-r--r--include/linux/blkpg.h1
-rw-r--r--include/linux/bsg-lib.h1
-rw-r--r--include/linux/can.h25
-rw-r--r--include/linux/cgroup_subsys.h8
-rw-r--r--include/linux/compaction.h8
-rw-r--r--include/linux/compiler-gcc4.h7
-rw-r--r--include/linux/compiler.h4
-rw-r--r--include/linux/cpuidle.h4
-rw-r--r--include/linux/dcache.h2
-rw-r--r--include/linux/dvb/frontend.h61
-rw-r--r--include/linux/dvb/version.h2
-rw-r--r--include/linux/efi.h2
-rw-r--r--include/linux/fs.h166
-rw-r--r--include/linux/ftrace_event.h5
-rw-r--r--include/linux/fuse.h19
-rw-r--r--include/linux/genhd.h57
-rw-r--r--include/linux/gfp.h13
-rw-r--r--include/linux/hardirq.h2
-rw-r--r--include/linux/highmem.h7
-rw-r--r--include/linux/hugetlb.h50
-rw-r--r--include/linux/hugetlb_cgroup.h126
-rw-r--r--include/linux/i2c-pnx.h1
-rw-r--r--include/linux/if_team.h30
-rw-r--r--include/linux/iio/frequency/adf4350.h2
-rw-r--r--include/linux/input/eeti_ts.h1
-rw-r--r--include/linux/interrupt.h2
-rw-r--r--include/linux/iommu.h44
-rw-r--r--include/linux/ipv6.h1
-rw-r--r--include/linux/irq.h1
-rw-r--r--include/linux/irqdesc.h1
-rw-r--r--include/linux/irqdomain.h28
-rw-r--r--include/linux/jbd2.h1
-rw-r--r--include/linux/jiffies.h29
-rw-r--r--include/linux/kdb.h2
-rw-r--r--include/linux/kernel.h12
-rw-r--r--include/linux/kobject.h2
-rw-r--r--include/linux/kref.h18
-rw-r--r--include/linux/ktime.h7
-rw-r--r--include/linux/mISDNhw.h2
-rw-r--r--include/linux/memcontrol.h34
-rw-r--r--include/linux/memory.h2
-rw-r--r--include/linux/mempool.h3
-rw-r--r--include/linux/mfd/core.h4
-rw-r--r--include/linux/mfd/ezx-pcap.h1
-rw-r--r--include/linux/mfd/tps65217.h12
-rw-r--r--include/linux/micrel_phy.h19
-rw-r--r--include/linux/migrate.h4
-rw-r--r--include/linux/mlx4/device.h13
-rw-r--r--include/linux/mm.h32
-rw-r--r--include/linux/mm_types.h9
-rw-r--r--include/linux/mmc/card.h1
-rw-r--r--include/linux/mmzone.h26
-rw-r--r--include/linux/mv643xx_eth.h2
-rw-r--r--include/linux/namei.h1
-rw-r--r--include/linux/netdevice.h7
-rw-r--r--include/linux/netfilter/nf_conntrack_sip.h2
-rw-r--r--include/linux/netpoll.h42
-rw-r--r--include/linux/nfs_fs.h9
-rw-r--r--include/linux/nfs_page.h1
-rw-r--r--include/linux/nfs_xdr.h3
-rw-r--r--include/linux/nfsd/nfsfh.h1
-rw-r--r--include/linux/nvme.h2
-rw-r--r--include/linux/of.h22
-rw-r--r--include/linux/olpc-ec.h41
-rw-r--r--include/linux/omap-dma.h22
-rw-r--r--include/linux/omap3isp.h6
-rw-r--r--include/linux/oom.h21
-rw-r--r--include/linux/page-flags.h29
-rw-r--r--include/linux/page-isolation.h13
-rw-r--r--include/linux/page_cgroup.h10
-rw-r--r--include/linux/pagemap.h5
-rw-r--r--include/linux/pci_ids.h2
-rw-r--r--include/linux/perf_event.h9
-rw-r--r--include/linux/pinctrl/consumer.h1
-rw-r--r--include/linux/pipe_fs_i.h2
-rw-r--r--include/linux/random.h6
-rw-r--r--include/linux/sched.h18
-rw-r--r--include/linux/security.h3
-rw-r--r--include/linux/shdma-base.h2
-rw-r--r--include/linux/shrinker.h1
-rw-r--r--include/linux/skbuff.h80
-rw-r--r--include/linux/string.h2
-rw-r--r--include/linux/sunrpc/xprt.h6
-rw-r--r--include/linux/swap.h14
-rw-r--r--include/linux/time.h29
-rw-r--r--include/linux/timex.h2
-rw-r--r--include/linux/topology.h1
-rw-r--r--include/linux/v4l2-common.h8
-rw-r--r--include/linux/v4l2-controls.h761
-rw-r--r--include/linux/v4l2-subdev.h10
-rw-r--r--include/linux/vfio.h445
-rw-r--r--include/linux/videodev2.h726
-rw-r--r--include/linux/vm_event_item.h1
-rw-r--r--include/linux/vmstat.h5
-rw-r--r--include/linux/writeback.h6
-rw-r--r--include/linux/xfrm.h2
-rw-r--r--include/media/ad9389b.h49
-rw-r--r--include/media/adv7604.h153
-rw-r--r--include/media/ir-rx51.h10
-rw-r--r--include/media/mt9v032.h3
-rw-r--r--include/media/omap3isp.h14
-rw-r--r--include/media/s5k4ecgx.h37
-rw-r--r--include/media/s5p_fimc.h18
-rw-r--r--include/media/saa7146.h4
-rw-r--r--include/media/soc_camera.h16
-rw-r--r--include/media/v4l2-chip-ident.h6
-rw-r--r--include/media/v4l2-common.h17
-rw-r--r--include/media/v4l2-ctrls.h43
-rw-r--r--include/media/v4l2-dev.h12
-rw-r--r--include/media/v4l2-event.h4
-rw-r--r--include/media/v4l2-ioctl.h26
-rw-r--r--include/media/v4l2-mem2mem.h4
-rw-r--r--include/media/v4l2-subdev.h8
-rw-r--r--include/media/videobuf-dvb.h4
-rw-r--r--include/media/videobuf2-core.h2
-rw-r--r--include/net/bluetooth/smp.h2
-rw-r--r--include/net/cfg80211.h2
-rw-r--r--include/net/codel.h8
-rw-r--r--include/net/dst.h2
-rw-r--r--include/net/inet_connection_sock.h1
-rw-r--r--include/net/inet_sock.h9
-rw-r--r--include/net/ip.h2
-rw-r--r--include/net/ip6_fib.h5
-rw-r--r--include/net/llc.h2
-rw-r--r--include/net/net_namespace.h10
-rw-r--r--include/net/netfilter/nf_conntrack_ecache.h1
-rw-r--r--include/net/netns/ipv4.h1
-rw-r--r--include/net/route.h2
-rw-r--r--include/net/scm.h4
-rw-r--r--include/net/sock.h42
-rw-r--r--include/net/tcp.h1
-rw-r--r--include/net/xfrm.h9
-rw-r--r--include/sound/es1688.h1
-rw-r--r--include/sound/pcm.h3
-rw-r--r--include/sound/tea575x-tuner.h4
-rw-r--r--include/target/target_core_backend.h4
-rw-r--r--include/target/target_core_base.h3
-rw-r--r--include/trace/events/gfpflags.h1
-rw-r--r--include/trace/events/kmem.h4
-rw-r--r--include/trace/events/random.h134
-rw-r--r--include/trace/events/sched.h4
-rw-r--r--include/trace/ftrace.h6
-rw-r--r--include/video/da8xx-fb.h3
-rw-r--r--include/video/omapdss.h51
-rw-r--r--include/video/sh_mobile_lcdc.h7
-rw-r--r--include/video/sh_mobile_meram.h71
-rw-r--r--include/xen/events.h2
-rw-r--r--include/xen/grant_table.h3
-rw-r--r--init/Kconfig29
-rw-r--r--init/main.c10
-rw-r--r--ipc/mqueue.c61
-rw-r--r--kernel/audit.c21
-rw-r--r--kernel/audit_tree.c19
-rw-r--r--kernel/cpu.c2
-rw-r--r--kernel/debug/kdb/kdb_debugger.c4
-rw-r--r--kernel/debug/kdb/kdb_io.c11
-rw-r--r--kernel/debug/kdb/kdb_main.c15
-rw-r--r--kernel/events/callchain.c9
-rw-r--r--kernel/events/core.c94
-rw-r--r--kernel/events/hw_breakpoint.c11
-rw-r--r--kernel/events/internal.h3
-rw-r--r--kernel/fork.c10
-rw-r--r--kernel/futex.c17
-rw-r--r--kernel/irq/handle.c7
-rw-r--r--kernel/irq/irqdomain.c362
-rw-r--r--kernel/irq/manage.c32
-rw-r--r--kernel/pid_namespace.c6
-rw-r--r--kernel/power/suspend.c3
-rw-r--r--kernel/printk.c2
-rw-r--r--kernel/sched/core.c112
-rw-r--r--kernel/sched/cpupri.c10
-rw-r--r--kernel/sched/fair.c77
-rw-r--r--kernel/sched/rt.c14
-rw-r--r--kernel/sched/sched.h9
-rw-r--r--kernel/sched/stop_task.c22
-rw-r--r--kernel/softirq.c9
-rw-r--r--kernel/sysctl.c26
-rw-r--r--kernel/sysctl_binary.c2
-rw-r--r--kernel/task_work.c1
-rw-r--r--kernel/time/jiffies.c2
-rw-r--r--kernel/time/ntp.c2
-rw-r--r--kernel/time/tick-sched.c1
-rw-r--r--kernel/time/timekeeping.c455
-rw-r--r--kernel/timer.c9
-rw-r--r--kernel/trace/trace_event_perf.c2
-rw-r--r--kernel/trace/trace_kprobe.c6
-rw-r--r--kernel/trace/trace_syscalls.c8
-rw-r--r--kernel/trace/trace_uprobe.c2
-rw-r--r--kernel/watchdog.c21
-rw-r--r--kernel/workqueue.c147
-rw-r--r--lib/digsig.c6
-rw-r--r--lib/flex_proportions.c2
-rw-r--r--lib/percpu_counter.c14
-rw-r--r--mm/Kconfig5
-rw-r--r--mm/Makefile8
-rw-r--r--mm/backing-dev.c72
-rw-r--r--mm/compaction.c167
-rw-r--r--mm/fadvise.c18
-rw-r--r--mm/filemap.c38
-rw-r--r--mm/filemap_xip.c6
-rw-r--r--mm/highmem.c12
-rw-r--r--mm/huge_memory.c1
-rw-r--r--mm/hugetlb.c195
-rw-r--r--mm/hugetlb_cgroup.c418
-rw-r--r--mm/hwpoison-inject.c2
-rw-r--r--mm/internal.h9
-rw-r--r--mm/memblock.c37
-rw-r--r--mm/memcontrol.c390
-rw-r--r--mm/memory-failure.c17
-rw-r--r--mm/memory.c23
-rw-r--r--mm/memory_hotplug.c36
-rw-r--r--mm/mempolicy.c2
-rw-r--r--mm/mempool.c12
-rw-r--r--mm/migrate.c81
-rw-r--r--mm/mmap.c12
-rw-r--r--mm/mmu_notifier.c45
-rw-r--r--mm/mmzone.c2
-rw-r--r--mm/mremap.c2
-rw-r--r--mm/oom_kill.c223
-rw-r--r--mm/page-writeback.c1
-rw-r--r--mm/page_alloc.c338
-rw-r--r--mm/page_cgroup.c2
-rw-r--r--mm/page_io.c145
-rw-r--r--mm/page_isolation.c93
-rw-r--r--mm/shmem.c6
-rw-r--r--mm/slab.c217
-rw-r--r--mm/slub.c45
-rw-r--r--mm/sparse.c29
-rw-r--r--mm/swap.c52
-rw-r--r--mm/swap_state.c7
-rw-r--r--mm/swapfile.c145
-rw-r--r--mm/vmalloc.c16
-rw-r--r--mm/vmscan.c176
-rw-r--r--mm/vmstat.c1
-rw-r--r--net/8021q/vlan_dev.c52
-rw-r--r--net/atm/common.c1
-rw-r--r--net/atm/pvc.c1
-rw-r--r--net/batman-adv/bat_iv_ogm.c13
-rw-r--r--net/batman-adv/bitarray.h6
-rw-r--r--net/batman-adv/gateway_client.c6
-rw-r--r--net/batman-adv/soft-interface.c7
-rw-r--r--net/batman-adv/translation-table.c1
-rw-r--r--net/bluetooth/bnep/sock.c4
-rw-r--r--net/bluetooth/cmtp/sock.c4
-rw-r--r--net/bluetooth/hci_conn.c4
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_event.c28
-rw-r--r--net/bluetooth/hci_sock.c18
-rw-r--r--net/bluetooth/hidp/sock.c4
-rw-r--r--net/bluetooth/l2cap_core.c14
-rw-r--r--net/bluetooth/l2cap_sock.c5
-rw-r--r--net/bluetooth/mgmt.c16
-rw-r--r--net/bluetooth/rfcomm/sock.c2
-rw-r--r--net/bluetooth/rfcomm/tty.c2
-rw-r--r--net/bluetooth/sco.c19
-rw-r--r--net/bluetooth/smp.c15
-rw-r--r--net/bridge/br_device.c30
-rw-r--r--net/bridge/br_forward.c2
-rw-r--r--net/bridge/br_if.c6
-rw-r--r--net/bridge/br_private.h4
-rw-r--r--net/bridge/netfilter/ebt_log.c2
-rw-r--r--net/caif/caif_socket.c2
-rw-r--r--net/caif/cfsrvl.c5
-rw-r--r--net/caif/chnl_net.c4
-rw-r--r--net/ceph/ceph_common.c1
-rw-r--r--net/ceph/crypto.c1
-rw-r--r--net/ceph/crypto.h3
-rw-r--r--net/ceph/debugfs.c4
-rw-r--r--net/ceph/messenger.c16
-rw-r--r--net/ceph/mon_client.c51
-rw-r--r--net/core/dev.c100
-rw-r--r--net/core/dst.c10
-rw-r--r--net/core/filter.c8
-rw-r--r--net/core/netpoll.c99
-rw-r--r--net/core/netprio_cgroup.c30
-rw-r--r--net/core/pktgen.c2
-rw-r--r--net/core/rtnetlink.c1
-rw-r--r--net/core/scm.c4
-rw-r--r--net/core/skbuff.c128
-rw-r--r--net/core/sock.c72
-rw-r--r--net/dccp/ccid.h4
-rw-r--r--net/dccp/ccids/ccid3.c1
-rw-r--r--net/ipv4/Makefile2
-rw-r--r--net/ipv4/arp.c2
-rw-r--r--net/ipv4/devinet.c10
-rw-r--r--net/ipv4/fib_frontend.c20
-rw-r--r--net/ipv4/fib_rules.c2
-rw-r--r--net/ipv4/fib_trie.c8
-rw-r--r--net/ipv4/inet_connection_sock.c7
-rw-r--r--net/ipv4/inetpeer.c5
-rw-r--r--net/ipv4/ip_output.c10
-rw-r--r--net/ipv4/ipmr.c14
-rw-r--r--net/ipv4/netfilter/nf_nat_sip.c14
-rw-r--r--net/ipv4/raw.c14
-rw-r--r--net/ipv4/route.c54
-rw-r--r--net/ipv4/sysctl_net_ipv4.c4
-rw-r--r--net/ipv4/tcp.c27
-rw-r--r--net/ipv4/tcp_cong.c3
-rw-r--r--net/ipv4/tcp_input.c45
-rw-r--r--net/ipv4/tcp_ipv4.c25
-rw-r--r--net/ipv4/tcp_metrics.c12
-rw-r--r--net/ipv4/tcp_minisocks.c2
-rw-r--r--net/ipv4/tcp_output.c49
-rw-r--r--net/ipv4/tcp_timer.c6
-rw-r--r--net/ipv4/udp.c7
-rw-r--r--net/ipv6/addrconf.c4
-rw-r--r--net/ipv6/esp6.c6
-rw-r--r--net/ipv6/inet6_connection_sock.c23
-rw-r--r--net/ipv6/ip6_fib.c4
-rw-r--r--net/ipv6/mip6.c20
-rw-r--r--net/ipv6/proc.c4
-rw-r--r--net/ipv6/raw.c21
-rw-r--r--net/ipv6/route.c19
-rw-r--r--net/ipv6/tcp_ipv6.c44
-rw-r--r--net/ipv6/udp.c11
-rw-r--r--net/ipv6/xfrm6_policy.c8
-rw-r--r--net/l2tp/l2tp_core.c7
-rw-r--r--net/l2tp/l2tp_core.h1
-rw-r--r--net/l2tp/l2tp_eth.c2
-rw-r--r--net/l2tp/l2tp_ip6.c1
-rw-r--r--net/l2tp/l2tp_netlink.c12
-rw-r--r--net/llc/af_llc.c8
-rw-r--r--net/llc/llc_input.c21
-rw-r--r--net/llc/llc_station.c29
-rw-r--r--net/mac80211/cfg.c9
-rw-r--r--net/mac80211/mesh.c3
-rw-r--r--net/mac80211/mlme.c6
-rw-r--r--net/mac80211/scan.c3
-rw-r--r--net/mac80211/tx.c38
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c5
-rw-r--r--net/netfilter/nf_conntrack_core.c16
-rw-r--r--net/netfilter/nf_conntrack_expect.c29
-rw-r--r--net/netfilter/nf_conntrack_netlink.c10
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c29
-rw-r--r--net/netfilter/nf_conntrack_sip.c92
-rw-r--r--net/netfilter/nfnetlink_log.c20
-rw-r--r--net/netfilter/xt_LOG.c37
-rw-r--r--net/netfilter/xt_limit.c8
-rw-r--r--net/netlink/af_netlink.c6
-rw-r--r--net/netrom/af_netrom.c9
-rw-r--r--net/openvswitch/actions.c2
-rw-r--r--net/openvswitch/datapath.c6
-rw-r--r--net/openvswitch/flow.h8
-rw-r--r--net/packet/af_packet.c12
-rw-r--r--net/sched/act_gact.c14
-rw-r--r--net/sched/act_ipt.c7
-rw-r--r--net/sched/act_mirred.c11
-rw-r--r--net/sched/act_pedit.c5
-rw-r--r--net/sched/act_simple.c5
-rw-r--r--net/sched/sch_cbq.c5
-rw-r--r--net/sched/sch_fq_codel.c2
-rw-r--r--net/sched/sch_gred.c38
-rw-r--r--net/sched/sch_qfq.c100
-rw-r--r--net/sctp/output.c21
-rw-r--r--net/sctp/ulpevent.c3
-rw-r--r--net/socket.c5
-rw-r--r--net/sunrpc/Kconfig5
-rw-r--r--net/sunrpc/clnt.c9
-rw-r--r--net/sunrpc/sched.c7
-rw-r--r--net/sunrpc/svc_xprt.c10
-rw-r--r--net/sunrpc/svcsock.c2
-rw-r--r--net/sunrpc/xprt.c34
-rw-r--r--net/sunrpc/xprtrdma/transport.c1
-rw-r--r--net/sunrpc/xprtsock.c46
-rw-r--r--net/unix/af_unix.c97
-rw-r--r--net/wireless/core.c5
-rw-r--r--net/wireless/core.h1
-rw-r--r--net/wireless/nl80211.c4
-rw-r--r--net/wireless/reg.c31
-rw-r--r--net/wireless/util.c2
-rw-r--r--net/xfrm/xfrm_input.c2
-rw-r--r--net/xfrm/xfrm_policy.c5
-rw-r--r--net/xfrm/xfrm_replay.c15
-rw-r--r--net/xfrm/xfrm_state.c25
-rw-r--r--net/xfrm/xfrm_user.c57
-rw-r--r--scripts/Makefile.fwinst2
-rwxr-xr-xscripts/checkpatch.pl3
-rwxr-xr-xscripts/checksyscalls.sh2
-rwxr-xr-xscripts/decodecode2
-rwxr-xr-xscripts/kernel-doc1
-rw-r--r--scripts/link-vmlinux.sh9
-rw-r--r--security/selinux/avc.c2
-rw-r--r--security/selinux/include/xfrm.h1
-rw-r--r--security/yama/yama_lsm.c43
-rw-r--r--sound/arm/pxa2xx-ac97.c4
-rw-r--r--sound/atmel/abdac.c3
-rw-r--r--sound/atmel/ac97c.c14
-rw-r--r--sound/core/compress_offload.c8
-rw-r--r--sound/core/sgbuf.c2
-rw-r--r--sound/drivers/aloop.c2
-rw-r--r--sound/drivers/dummy.c2
-rw-r--r--sound/drivers/mpu401/mpu401_uart.c1
-rw-r--r--sound/drivers/pcsp/pcsp.c4
-rw-r--r--sound/i2c/other/tea575x-tuner.c205
-rw-r--r--sound/isa/als100.c2
-rw-r--r--sound/isa/es1688/es1688_lib.c34
-rw-r--r--sound/oss/sb_audio.c4
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c2
-rw-r--r--sound/pci/ctxfi/ctatc.c4
-rw-r--r--sound/pci/emu10k1/memory.c5
-rw-r--r--sound/pci/hda/hda_auto_parser.c5
-rw-r--r--sound/pci/hda/hda_beep.c29
-rw-r--r--sound/pci/hda/hda_codec.c85
-rw-r--r--sound/pci/hda/hda_codec.h2
-rw-r--r--sound/pci/hda/hda_intel.c11
-rw-r--r--sound/pci/hda/hda_proc.c2
-rw-r--r--sound/pci/hda/patch_ca0132.c174
-rw-r--r--sound/pci/hda/patch_conexant.c6
-rw-r--r--sound/pci/hda/patch_hdmi.c12
-rw-r--r--sound/pci/hda/patch_realtek.c30
-rw-r--r--sound/pci/hda/patch_sigmatel.c37
-rw-r--r--sound/pci/hda/patch_via.c15
-rw-r--r--sound/pci/ice1712/prodigy_hifi.c3
-rw-r--r--sound/pci/lx6464es/lx6464es.c2
-rw-r--r--sound/pci/rme9652/hdspm.c2
-rw-r--r--sound/pci/sis7019.c5
-rw-r--r--sound/ppc/powermac.c2
-rw-r--r--sound/ppc/snd_ps3.c1
-rw-r--r--sound/soc/blackfin/bf6xx-sport.c7
-rw-r--r--sound/soc/codecs/ab8500-codec.c4
-rw-r--r--sound/soc/codecs/ad1980.c1
-rw-r--r--sound/soc/codecs/arizona.c2
-rw-r--r--sound/soc/codecs/mc13783.c10
-rw-r--r--sound/soc/codecs/sgtl5000.c3
-rw-r--r--sound/soc/codecs/stac9766.c1
-rw-r--r--sound/soc/codecs/wm2000.c2
-rw-r--r--sound/soc/codecs/wm5102.c25
-rw-r--r--sound/soc/codecs/wm5110.c12
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8962.c18
-rw-r--r--sound/soc/codecs/wm8994.c17
-rw-r--r--sound/soc/codecs/wm9712.c22
-rw-r--r--sound/soc/codecs/wm9713.c1
-rw-r--r--sound/soc/davinci/davinci-mcasp.c10
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c2
-rw-r--r--sound/soc/fsl/imx-ssi.c5
-rw-r--r--sound/soc/mxs/Kconfig2
-rw-r--r--sound/soc/mxs/mxs-saif.c24
-rw-r--r--sound/soc/omap/am3517evm.c2
-rw-r--r--sound/soc/omap/mcbsp.c2
-rw-r--r--sound/soc/omap/omap-mcbsp.c1
-rw-r--r--sound/soc/omap/omap-pcm.c1
-rw-r--r--sound/soc/samsung/dma.c8
-rw-r--r--sound/soc/samsung/pcm.c2
-rw-r--r--sound/soc/soc-core.c12
-rw-r--r--sound/soc/soc-dapm.c5
-rw-r--r--sound/soc/soc-jack.c2
-rw-r--r--sound/soc/spear/spear_pcm.c2
-rw-r--r--sound/soc/tegra/tegra_alc5632.c3
-rw-r--r--sound/soc/tegra/tegra_pcm.c4
-rw-r--r--sound/soc/tegra/tegra_wm8903.c10
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c2
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c27
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h2
-rw-r--r--sound/sound_firmware.c8
-rw-r--r--sound/usb/card.c4
-rw-r--r--sound/usb/clock.c3
-rw-r--r--sound/usb/endpoint.c32
-rw-r--r--sound/usb/endpoint.h3
-rw-r--r--sound/usb/pcm.c67
-rw-r--r--tools/perf/Makefile7
-rw-r--r--tools/perf/builtin-record.c4
-rw-r--r--tools/perf/builtin-report.c5
-rw-r--r--tools/perf/builtin-test.c19
-rw-r--r--tools/perf/builtin-top.c23
-rw-r--r--tools/perf/util/event.h3
-rw-r--r--tools/perf/util/evlist.c7
-rw-r--r--tools/perf/util/evlist.h3
-rw-r--r--tools/perf/util/evsel.c15
-rw-r--r--tools/perf/util/evsel.h10
-rw-r--r--tools/perf/util/header.c9
-rw-r--r--tools/perf/util/intlist.c101
-rw-r--r--tools/perf/util/intlist.h75
-rw-r--r--tools/perf/util/parse-events-test.c12
-rw-r--r--tools/perf/util/parse-options.c3
-rw-r--r--tools/perf/util/python-ext-sources2
-rw-r--r--tools/perf/util/python.c6
-rw-r--r--tools/perf/util/rblist.c107
-rw-r--r--tools/perf/util/rblist.h47
-rw-r--r--tools/perf/util/session.c48
-rw-r--r--tools/perf/util/session.h24
-rw-r--r--tools/perf/util/strlist.c130
-rw-r--r--tools/perf/util/strlist.h11
-rw-r--r--tools/perf/util/symbol.c14
-rw-r--r--tools/perf/util/target.c2
-rw-r--r--tools/testing/fault-injection/failcmd.sh2
-rw-r--r--virt/kvm/kvm_main.c7
4168 files changed, 415821 insertions, 382537 deletions
diff --git a/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads
new file mode 100644
index 000000000000..b0b0eeb20fe3
--- /dev/null
+++ b/Documentation/ABI/obsolete/proc-sys-vm-nr_pdflush_threads
@@ -0,0 +1,5 @@
+What: /proc/sys/vm/nr_pdflush_threads
+Date: June 2012
+Contact: Wanpeng Li <liwp@linux.vnet.ibm.com>
+Description: Since pdflush is replaced by per-BDI flusher, the interface of old pdflush
+ exported in /proc/sys/vm/ should be removed.
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index 34f51100f029..dff1f48d252d 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -210,3 +210,15 @@ Users:
firmware assigned instance number of the PCI
device that can help in understanding the firmware
intended order of the PCI device.
+
+What: /sys/bus/pci/devices/.../d3cold_allowed
+Date: July 2012
+Contact: Huang Ying <ying.huang@intel.com>
+Description:
+ d3cold_allowed is bit to control whether the corresponding PCI
+ device can be put into D3Cold state. If it is cleared, the
+ device will never be put into D3Cold state. If it is set, the
+ device may be put into D3Cold state if other requirements are
+ satisfied too. Reading this attribute will show the current
+ value of d3cold_allowed bit. Writing this attribute will set
+ the value of d3cold_allowed bit.
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb
new file mode 100644
index 000000000000..2107082426da
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-sh_mobile_lcdc_fb
@@ -0,0 +1,44 @@
+What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_alpha
+Date: May 2012
+Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+ This file is only available on fb[0-9] devices corresponding
+ to overlay planes.
+
+ Stores the alpha blending value for the overlay. Values range
+ from 0 (transparent) to 255 (opaque). The value is ignored if
+ the mode is not set to Alpha Blending.
+
+What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_mode
+Date: May 2012
+Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+ This file is only available on fb[0-9] devices corresponding
+ to overlay planes.
+
+ Selects the composition mode for the overlay. Possible values
+ are
+
+ 0 - Alpha Blending
+ 1 - ROP3
+
+What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_position
+Date: May 2012
+Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+ This file is only available on fb[0-9] devices corresponding
+ to overlay planes.
+
+ Stores the x,y overlay position on the display in pixels. The
+ position format is `[0-9]+,[0-9]+'.
+
+What: /sys/devices/platform/sh_mobile_lcdc_fb.[0-3]/graphics/fb[0-9]/ovl_rop3
+Date: May 2012
+Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Description:
+ This file is only available on fb[0-9] devices corresponding
+ to overlay planes.
+
+ Stores the raster operation (ROP3) for the overlay. Values
+ range from 0 to 255. The value is ignored if the mode is not
+ set to ROP3.
diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
index 814b01354c41..b31e782bd985 100644
--- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
+++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
@@ -5,4 +5,15 @@ Contact: "Ike Panhc <ike.pan@canonical.com>"
Description:
Control the power of camera module. 1 means on, 0 means off.
+What: /sys/devices/platform/ideapad/fan_mode
+Date: June 2012
+KernelVersion: 3.6
+Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
+Description:
+ Change fan mode
+ There are four available modes:
+ * 0 -> Super Silent Mode
+ * 1 -> Standard Mode
+ * 2 -> Dust Cleaning
+ * 4 -> Efficient Thermal Dissipation Mode
diff --git a/Documentation/DocBook/filesystems.tmpl b/Documentation/DocBook/filesystems.tmpl
index 3fca32c41927..25b58efd955d 100644
--- a/Documentation/DocBook/filesystems.tmpl
+++ b/Documentation/DocBook/filesystems.tmpl
@@ -224,8 +224,8 @@ all your transactions.
</para>
<para>
-Then at umount time , in your put_super() (2.4) or write_super() (2.5)
-you can then call journal_destroy() to clean up your in-core journal object.
+Then at umount time , in your put_super() you can then call journal_destroy()
+to clean up your in-core journal object.
</para>
<para>
diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile
index 362520992ced..9b7e4c557928 100644
--- a/Documentation/DocBook/media/Makefile
+++ b/Documentation/DocBook/media/Makefile
@@ -300,7 +300,7 @@ $(MEDIA_OBJ_DIR)/media-entities.tmpl: $(MEDIA_OBJ_DIR)/v4l2.xml
@( \
for ident in $(IOCTLS) ; do \
entity=`echo $$ident | tr _ -` ; \
- id=`grep "<refname>$$ident" $(MEDIA_OBJ_DIR)/vidioc-*.xml | sed -r s,"^.*/(.*).xml.*","\1",` ; \
+ id=`grep "<refname>$$ident" $(MEDIA_OBJ_DIR)/vidioc-*.xml $(MEDIA_OBJ_DIR)/media-ioc-*.xml | sed -r s,"^.*/(.*).xml.*","\1",` ; \
echo "<!ENTITY $$entity \"<link" \
"linkend='$$id'><constant>$$ident</constant></link>\">" \
>>$@ ; \
diff --git a/Documentation/DocBook/media/dvb/audio.xml b/Documentation/DocBook/media/dvb/audio.xml
index d64386237207..a7ea56c71a27 100644
--- a/Documentation/DocBook/media/dvb/audio.xml
+++ b/Documentation/DocBook/media/dvb/audio.xml
@@ -1,12 +1,16 @@
<title>DVB Audio Device</title>
<para>The DVB audio device controls the MPEG2 audio decoder of the DVB hardware. It
can be accessed through <emphasis role="tt">/dev/dvb/adapter0/audio0</emphasis>. Data types and and
-ioctl definitions can be accessed by including <emphasis role="tt">linux/dvb/video.h</emphasis> in your
+ioctl definitions can be accessed by including <emphasis role="tt">linux/dvb/audio.h</emphasis> in your
application.
</para>
<para>Please note that some DVB cards don&#8217;t have their own MPEG decoder, which results in
the omission of the audio and video device.
</para>
+<para>
+These ioctls were also used by V4L2 to control MPEG decoders implemented in V4L2. The use
+of these ioctls for that purpose has been made obsolete and proper V4L2 ioctls or controls
+have been created to replace that functionality.</para>
<section id="audio_data_types">
<title>Audio Data Types</title>
@@ -558,6 +562,8 @@ role="subsection"><title>AUDIO_SELECT_SOURCE</title>
role="subsection"><title>AUDIO_SET_MUTE</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+&VIDIOC-DECODER-CMD; with the <constant>V4L2_DEC_CMD_START_MUTE_AUDIO</constant> flag instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call asks the audio device to mute the stream that is currently being
@@ -730,6 +736,8 @@ role="subsection"><title>AUDIO_SET_BYPASS_MODE</title>
role="subsection"><title>AUDIO_CHANNEL_SELECT</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+<constant>V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK</constant> control instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call asks the Audio Device to select the requested channel if possible.</para>
@@ -772,6 +780,109 @@ role="subsection"><title>AUDIO_CHANNEL_SELECT</title>
</row></tbody></tgroup></informaltable>
&return-value-dvb;
+</section><section id="AUDIO_BILINGUAL_CHANNEL_SELECT"
+role="subsection"><title>AUDIO_BILINGUAL_CHANNEL_SELECT</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. It has been replaced by
+the V4L2 <constant>V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK</constant> control
+for MPEG decoders controlled through V4L2.</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call asks the Audio Device to select the requested channel for bilingual streams if possible.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ AUDIO_BILINGUAL_CHANNEL_SELECT, audio_channel_select_t);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals AUDIO_BILINGUAL_CHANNEL_SELECT for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>audio_channel_select_t
+ch</para>
+</entry><entry
+ align="char">
+<para>Select the output format of the audio (mono left/right,
+ stereo).</para>
+</entry>
+ </row>
+</tbody></tgroup></informaltable>
+&return-value-dvb;
+
+</section><section id="AUDIO_GET_PTS"
+role="subsection"><title>AUDIO_GET_PTS</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. If you need this functionality,
+then please contact the linux-media mailing list (&v4l-ml;).</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call asks the Audio Device to return the current PTS timestamp.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ AUDIO_GET_PTS, __u64 *pts);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals AUDIO_GET_PTS for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u64 *pts
+</para>
+</entry><entry
+ align="char">
+<para>Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / ISO/IEC 13818-1.
+</para>
+<para>
+The PTS should belong to the currently played
+frame if possible, but may also be a value close to it
+like the PTS of the last decoded frame or the last PTS
+extracted by the PES parser.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
</section><section id="AUDIO_GET_STATUS"
role="subsection"><title>AUDIO_GET_STATUS</title>
<para>DESCRIPTION
diff --git a/Documentation/DocBook/media/dvb/ca.xml b/Documentation/DocBook/media/dvb/ca.xml
index 5c4adb44b1c1..85eaf4fe2931 100644
--- a/Documentation/DocBook/media/dvb/ca.xml
+++ b/Documentation/DocBook/media/dvb/ca.xml
@@ -226,4 +226,357 @@ typedef struct ca_pid {
</entry>
</row></tbody></tgroup></informaltable>
</section>
+
+<section id="CA_RESET"
+role="subsection"><title>CA_RESET</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_RESET);
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_RESET for this command.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_GET_CAP"
+role="subsection"><title>CA_GET_CAP</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_GET_CAP,
+ ca_caps_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_GET_CAP for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_caps_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_GET_SLOT_INFO"
+role="subsection"><title>CA_GET_SLOT_INFO</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_GET_SLOT_INFO,
+ ca_slot_info_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_GET_SLOT_INFO for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_slot_info_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_GET_DESCR_INFO"
+role="subsection"><title>CA_GET_DESCR_INFO</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_GET_DESCR_INFO,
+ ca_descr_info_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_GET_DESCR_INFO for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_descr_info_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_GET_MSG"
+role="subsection"><title>CA_GET_MSG</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_GET_MSG,
+ ca_msg_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_GET_MSG for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_msg_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_SEND_MSG"
+role="subsection"><title>CA_SEND_MSG</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_SEND_MSG,
+ ca_msg_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_SEND_MSG for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_msg_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_SET_DESCR"
+role="subsection"><title>CA_SET_DESCR</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_SET_DESCR,
+ ca_descr_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_SET_DESCR for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_descr_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="CA_SET_PID"
+role="subsection"><title>CA_SET_PID</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = CA_SET_PID,
+ ca_pid_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals CA_SET_PID for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>ca_pid_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
</section>
diff --git a/Documentation/DocBook/media/dvb/demux.xml b/Documentation/DocBook/media/dvb/demux.xml
index 37c17908aa40..86de89cfbd67 100644
--- a/Documentation/DocBook/media/dvb/demux.xml
+++ b/Documentation/DocBook/media/dvb/demux.xml
@@ -899,4 +899,232 @@ typedef enum {
<para>Invalid stc number.</para>
</entry>
</row></tbody></tgroup></informaltable>
- </section></section>
+ </section>
+
+<section id="DMX_GET_PES_PIDS"
+role="subsection"><title>DMX_GET_PES_PIDS</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = DMX_GET_PES_PIDS,
+ __u16[5]);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals DMX_GET_PES_PIDS for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u16[5]
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="DMX_GET_CAPS"
+role="subsection"><title>DMX_GET_CAPS</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = DMX_GET_CAPS,
+ dmx_caps_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals DMX_GET_CAPS for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>dmx_caps_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="DMX_SET_SOURCE"
+role="subsection"><title>DMX_SET_SOURCE</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = DMX_SET_SOURCE,
+ dmx_source_t *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals DMX_SET_SOURCE for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>dmx_source_t *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="DMX_ADD_PID"
+role="subsection"><title>DMX_ADD_PID</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = DMX_ADD_PID,
+ __u16 *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals DMX_ADD_PID for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u16 *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="DMX_REMOVE_PID"
+role="subsection"><title>DMX_REMOVE_PID</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = DMX_REMOVE_PID,
+ __u16 *);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals DMX_REMOVE_PID for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u16 *
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+
+</section>
diff --git a/Documentation/DocBook/media/dvb/dvbapi.xml b/Documentation/DocBook/media/dvb/dvbapi.xml
index 2ab6ddcfc4e0..757488b24f4f 100644
--- a/Documentation/DocBook/media/dvb/dvbapi.xml
+++ b/Documentation/DocBook/media/dvb/dvbapi.xml
@@ -28,7 +28,7 @@
<holder>Convergence GmbH</holder>
</copyright>
<copyright>
- <year>2009-2011</year>
+ <year>2009-2012</year>
<holder>Mauro Carvalho Chehab</holder>
</copyright>
@@ -84,7 +84,7 @@ Added ISDB-T test originally written by Patrick Boettcher
<title>LINUX DVB API</title>
-<subtitle>Version 5.2</subtitle>
+<subtitle>Version 5.8</subtitle>
<!-- ADD THE CHAPTERS HERE -->
<chapter id="dvb_introdution">
&sub-intro;
diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml
index e633c097a8d1..957e3acaae8e 100644
--- a/Documentation/DocBook/media/dvb/dvbproperty.xml
+++ b/Documentation/DocBook/media/dvb/dvbproperty.xml
@@ -194,6 +194,7 @@ get/set up to 64 properties. The actual meaning of each property is described on
APSK_16,
APSK_32,
DQPSK,
+ QAM_4_NR,
} fe_modulation_t;
</programlisting>
</section>
@@ -265,6 +266,7 @@ typedef enum fe_code_rate {
FEC_AUTO,
FEC_3_5,
FEC_9_10,
+ FEC_2_5,
} fe_code_rate_t;
</programlisting>
<para>which correspond to error correction rates of 1/2, 2/3, etc.,
@@ -351,7 +353,7 @@ typedef enum fe_delivery_system {
SYS_ISDBC,
SYS_ATSC,
SYS_ATSCMH,
- SYS_DMBTH,
+ SYS_DTMB,
SYS_CMMB,
SYS_DAB,
SYS_DVBT2,
@@ -567,28 +569,33 @@ typedef enum fe_delivery_system {
<title><constant>DTV_ATSCMH_RS_FRAME_MODE</constant></title>
<para>RS frame mode.</para>
<para>Possible values are:</para>
+ <para id="atscmh-rs-frame-mode">
<programlisting>
typedef enum atscmh_rs_frame_mode {
ATSCMH_RSFRAME_PRI_ONLY = 0,
ATSCMH_RSFRAME_PRI_SEC = 1,
} atscmh_rs_frame_mode_t;
</programlisting>
+ </para>
</section>
<section id="DTV-ATSCMH-RS-FRAME-ENSEMBLE">
<title><constant>DTV_ATSCMH_RS_FRAME_ENSEMBLE</constant></title>
<para>RS frame ensemble.</para>
<para>Possible values are:</para>
+ <para id="atscmh-rs-frame-ensemble">
<programlisting>
typedef enum atscmh_rs_frame_ensemble {
ATSCMH_RSFRAME_ENS_PRI = 0,
ATSCMH_RSFRAME_ENS_SEC = 1,
} atscmh_rs_frame_ensemble_t;
</programlisting>
+ </para>
</section>
<section id="DTV-ATSCMH-RS-CODE-MODE-PRI">
<title><constant>DTV_ATSCMH_RS_CODE_MODE_PRI</constant></title>
<para>RS code mode (primary).</para>
<para>Possible values are:</para>
+ <para id="atscmh-rs-code-mode">
<programlisting>
typedef enum atscmh_rs_code_mode {
ATSCMH_RSCODE_211_187 = 0,
@@ -596,6 +603,7 @@ typedef enum atscmh_rs_code_mode {
ATSCMH_RSCODE_235_187 = 2,
} atscmh_rs_code_mode_t;
</programlisting>
+ </para>
</section>
<section id="DTV-ATSCMH-RS-CODE-MODE-SEC">
<title><constant>DTV_ATSCMH_RS_CODE_MODE_SEC</constant></title>
@@ -613,23 +621,27 @@ typedef enum atscmh_rs_code_mode {
<title><constant>DTV_ATSCMH_SCCC_BLOCK_MODE</constant></title>
<para>Series Concatenated Convolutional Code Block Mode.</para>
<para>Possible values are:</para>
+ <para id="atscmh-sccc-block-mode">
<programlisting>
typedef enum atscmh_sccc_block_mode {
ATSCMH_SCCC_BLK_SEP = 0,
ATSCMH_SCCC_BLK_COMB = 1,
} atscmh_sccc_block_mode_t;
</programlisting>
+ </para>
</section>
<section id="DTV-ATSCMH-SCCC-CODE-MODE-A">
<title><constant>DTV_ATSCMH_SCCC_CODE_MODE_A</constant></title>
<para>Series Concatenated Convolutional Code Rate.</para>
<para>Possible values are:</para>
+ <para id="atscmh-sccc-code-mode">
<programlisting>
typedef enum atscmh_sccc_code_mode {
ATSCMH_SCCC_CODE_HLF = 0,
ATSCMH_SCCC_CODE_QTR = 1,
} atscmh_sccc_code_mode_t;
</programlisting>
+ </para>
</section>
<section id="DTV-ATSCMH-SCCC-CODE-MODE-B">
<title><constant>DTV_ATSCMH_SCCC_CODE_MODE_B</constant></title>
@@ -725,6 +737,9 @@ typedef enum fe_guard_interval {
GUARD_INTERVAL_1_128,
GUARD_INTERVAL_19_128,
GUARD_INTERVAL_19_256,
+ GUARD_INTERVAL_PN420,
+ GUARD_INTERVAL_PN595,
+ GUARD_INTERVAL_PN945,
} fe_guard_interval_t;
</programlisting>
@@ -733,6 +748,7 @@ typedef enum fe_guard_interval {
try to find the correct guard interval (if capable) and will use TMCC to fill
in the missing parameters.</para>
<para>2) Intervals 1/128, 19/128 and 19/256 are used only for DVB-T2 at present</para>
+ <para>3) DTMB specifies PN420, PN595 and PN945.</para>
</section>
<section id="DTV-TRANSMISSION-MODE">
<title><constant>DTV_TRANSMISSION_MODE</constant></title>
@@ -749,6 +765,8 @@ typedef enum fe_transmit_mode {
TRANSMISSION_MODE_1K,
TRANSMISSION_MODE_16K,
TRANSMISSION_MODE_32K,
+ TRANSMISSION_MODE_C1,
+ TRANSMISSION_MODE_C3780,
} fe_transmit_mode_t;
</programlisting>
<para>Notes:</para>
@@ -760,6 +778,7 @@ typedef enum fe_transmit_mode {
use TMCC to fill in the missing parameters.</para>
<para>3) DVB-T specifies 2K and 8K as valid sizes.</para>
<para>4) DVB-T2 specifies 1K, 2K, 4K, 8K, 16K and 32K.</para>
+ <para>5) DTMB specifies C1 and C3780.</para>
</section>
<section id="DTV-HIERARCHY">
<title><constant>DTV_HIERARCHY</constant></title>
@@ -774,17 +793,28 @@ typedef enum fe_hierarchy {
} fe_hierarchy_t;
</programlisting>
</section>
- <section id="DTV-ISDBS-TS-ID">
- <title><constant>DTV_ISDBS_TS_ID</constant></title>
- <para>Currently unused.</para>
+ <section id="DTV-STREAM-ID">
+ <title><constant>DTV_STREAM_ID</constant></title>
+ <para>DVB-S2, DVB-T2 and ISDB-S support the transmission of several
+ streams on a single transport stream.
+ This property enables the DVB driver to handle substream filtering,
+ when supported by the hardware.
+ By default, substream filtering is disabled.
+ </para><para>
+ For DVB-S2 and DVB-T2, the valid substream id range is from 0 to 255.
+ </para><para>
+ For ISDB, the valid substream id range is from 1 to 65535.
+ </para><para>
+ To disable it, you should use the special macro NO_STREAM_ID_FILTER.
+ </para><para>
+ Note: any value outside the id range also disables filtering.
+ </para>
</section>
- <section id="DTV-DVBT2-PLP-ID">
- <title><constant>DTV_DVBT2_PLP_ID</constant></title>
- <para>DVB-T2 supports Physical Layer Pipes (PLP) to allow transmission of
- many data types via a single multiplex. The API will soon support this
- at which point this section will be expanded.</para>
+ <section id="DTV-DVBT2-PLP-ID-LEGACY">
+ <title><constant>DTV_DVBT2_PLP_ID_LEGACY</constant></title>
+ <para>Obsolete, replaced with DTV_STREAM_ID.</para>
</section>
- <section id="DTV_ENUM_DELSYS">
+ <section id="DTV-ENUM-DELSYS">
<title><constant>DTV_ENUM_DELSYS</constant></title>
<para>A Multi standard frontend needs to advertise the delivery systems provided.
Applications need to enumerate the provided delivery systems, before using
@@ -796,6 +826,29 @@ typedef enum fe_hierarchy {
FE_GET_INFO. In the case of a legacy frontend, the result is just the same
as with FE_GET_INFO, but in a more structured format </para>
</section>
+ <section id="DTV-INTERLEAVING">
+ <title><constant>DTV_INTERLEAVING</constant></title>
+ <para id="fe-interleaving">Interleaving mode</para>
+ <programlisting>
+enum fe_interleaving {
+ INTERLEAVING_NONE,
+ INTERLEAVING_AUTO,
+ INTERLEAVING_240,
+ INTERLEAVING_720,
+};
+ </programlisting>
+ </section>
+ <section id="DTV-LNA">
+ <title><constant>DTV_LNA</constant></title>
+ <para>Low-noise amplifier.</para>
+ <para>Hardware might offer controllable LNA which can be set manually
+ using that parameter. Usually LNA could be found only from
+ terrestrial devices if at all.</para>
+ <para>Possible values: 0, 1, LNA_AUTO</para>
+ <para>0, LNA off</para>
+ <para>1, LNA on</para>
+ <para>use the special macro LNA_AUTO to set LNA auto</para>
+ </section>
</section>
<section id="frontend-property-terrestrial-systems">
<title>Properties used on terrestrial delivery systems</title>
@@ -816,6 +869,7 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
<listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
<listitem><para><link linkend="DTV-HIERARCHY"><constant>DTV_HIERARCHY</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-LNA"><constant>DTV_LNA</constant></link></para></listitem>
</itemizedlist>
</section>
<section id="dvbt2-params">
@@ -838,7 +892,8 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
<listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
<listitem><para><link linkend="DTV-HIERARCHY"><constant>DTV_HIERARCHY</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-DVBT2-PLP-ID"><constant>DTV_DVBT2_PLP_ID</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-STREAM-ID"><constant>DTV_STREAM_ID</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-LNA"><constant>DTV_LNA</constant></link></para></listitem>
</itemizedlist>
</section>
<section id="isdbt">
@@ -925,13 +980,32 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-ATSCMH-PRC"><constant>DTV_ATSCMH_PRC</constant></link></para></listitem>
<listitem><para><link linkend="DTV-ATSCMH-RS-FRAME-MODE"><constant>DTV_ATSCMH_RS_FRAME_MODE</constant></link></para></listitem>
<listitem><para><link linkend="DTV-ATSCMH-RS-FRAME-ENSEMBLE"><constant>DTV_ATSCMH_RS_FRAME_ENSEMBLE</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-CODE-MODE-PRI"><constant>DTV_ATSCMH_CODE_MODE_PRI</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-CODE-MODE-SEC"><constant>DTV_ATSCMH_CODE_MODE_SEC</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-RS-CODE-MODE-PRI"><constant>DTV_ATSCMH_RS_CODE_MODE_PRI</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-RS-CODE-MODE-SEC"><constant>DTV_ATSCMH_RS_CODE_MODE_SEC</constant></link></para></listitem>
<listitem><para><link linkend="DTV-ATSCMH-SCCC-BLOCK-MODE"><constant>DTV_ATSCMH_SCCC_BLOCK_MODE</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE_MODE-A"><constant>DTV_ATSCMH_SCCC_CODE_MODE_A</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE_MODE-B"><constant>DTV_ATSCMH_SCCC_CODE_MODE_B</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE_MODE-C"><constant>DTV_ATSCMH_SCCC_CODE_MODE_C</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE_MODE-D"><constant>DTV_ATSCMH_SCCC_CODE_MODE_D</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE-MODE-A"><constant>DTV_ATSCMH_SCCC_CODE_MODE_A</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE-MODE-B"><constant>DTV_ATSCMH_SCCC_CODE_MODE_B</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE-MODE-C"><constant>DTV_ATSCMH_SCCC_CODE_MODE_C</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-ATSCMH-SCCC-CODE-MODE-D"><constant>DTV_ATSCMH_SCCC_CODE_MODE_D</constant></link></para></listitem>
+ </itemizedlist>
+ </section>
+ <section id="dtmb-params">
+ <title>DTMB delivery system</title>
+ <para>The following parameters are valid for DTMB:</para>
+ <itemizedlist mark='opencircle'>
+ <listitem><para><link linkend="DTV-API-VERSION"><constant>DTV_API_VERSION</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-DELIVERY-SYSTEM"><constant>DTV_DELIVERY_SYSTEM</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-TUNE"><constant>DTV_TUNE</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-CLEAR"><constant>DTV_CLEAR</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-BANDWIDTH-HZ"><constant>DTV_BANDWIDTH_HZ</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-GUARD-INTERVAL"><constant>DTV_GUARD_INTERVAL</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-TRANSMISSION-MODE"><constant>DTV_TRANSMISSION_MODE</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-INTERLEAVING"><constant>DTV_INTERLEAVING</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-LNA"><constant>DTV_LNA</constant></link></para></listitem>
</itemizedlist>
</section>
</section>
@@ -952,6 +1026,7 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-LNA"><constant>DTV_LNA</constant></link></para></listitem>
</itemizedlist>
</section>
<section id="dvbc-annex-b-params">
@@ -966,6 +1041,7 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-FREQUENCY"><constant>DTV_FREQUENCY</constant></link></para></listitem>
<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
<listitem><para><link linkend="DTV-INVERSION"><constant>DTV_INVERSION</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-LNA"><constant>DTV_LNA</constant></link></para></listitem>
</itemizedlist>
</section>
</section>
@@ -999,6 +1075,7 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-MODULATION"><constant>DTV_MODULATION</constant></link></para></listitem>
<listitem><para><link linkend="DTV-PILOT"><constant>DTV_PILOT</constant></link></para></listitem>
<listitem><para><link linkend="DTV-ROLLOFF"><constant>DTV_ROLLOFF</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-STREAM-ID"><constant>DTV_STREAM_ID</constant></link></para></listitem>
</itemizedlist>
</section>
<section id="turbo-params">
@@ -1021,7 +1098,7 @@ typedef enum fe_hierarchy {
<listitem><para><link linkend="DTV-SYMBOL-RATE"><constant>DTV_SYMBOL_RATE</constant></link></para></listitem>
<listitem><para><link linkend="DTV-INNER-FEC"><constant>DTV_INNER_FEC</constant></link></para></listitem>
<listitem><para><link linkend="DTV-VOLTAGE"><constant>DTV_VOLTAGE</constant></link></para></listitem>
- <listitem><para><link linkend="DTV-ISDBS-TS-ID"><constant>DTV_ISDBS_TS_ID</constant></link></para></listitem>
+ <listitem><para><link linkend="DTV-STREAM-ID"><constant>DTV_STREAM_ID</constant></link></para></listitem>
</itemizedlist>
</section>
</section>
diff --git a/Documentation/DocBook/media/dvb/frontend.xml b/Documentation/DocBook/media/dvb/frontend.xml
index aeaed59d0f1f..426c2526a454 100644
--- a/Documentation/DocBook/media/dvb/frontend.xml
+++ b/Documentation/DocBook/media/dvb/frontend.xml
@@ -66,7 +66,7 @@ supported via the new <link linkend="FE_GET_SET_PROPERTY">FE_GET_PROPERTY/FE_GET
<para>The usage of this field is deprecated, as it doesn't report all supported standards, and
will provide an incomplete information for frontends that support multiple delivery systems.
-Please use <link linkend="DTV_ENUM_DELSYS">DTV_ENUM_DELSYS</link> instead.</para>
+Please use <link linkend="DTV-ENUM-DELSYS">DTV_ENUM_DELSYS</link> instead.</para>
</section>
<section id="fe-caps-t">
@@ -101,6 +101,7 @@ a specific frontend type.</para>
FE_CAN_8VSB = 0x200000,
FE_CAN_16VSB = 0x400000,
FE_HAS_EXTENDED_CAPS = 0x800000,
+ FE_CAN_MULTISTREAM = 0x4000000,
FE_CAN_TURBO_FEC = 0x8000000,
FE_CAN_2G_MODULATION = 0x10000000,
FE_NEEDS_BENDING = 0x20000000,
@@ -207,18 +208,44 @@ spec.</para>
<para>Several functions of the frontend device use the fe_status data type defined
by</para>
<programlisting>
- typedef enum fe_status {
- FE_HAS_SIGNAL = 0x01, /&#x22C6; found something above the noise level &#x22C6;/
- FE_HAS_CARRIER = 0x02, /&#x22C6; found a DVB signal &#x22C6;/
- FE_HAS_VITERBI = 0x04, /&#x22C6; FEC is stable &#x22C6;/
- FE_HAS_SYNC = 0x08, /&#x22C6; found sync bytes &#x22C6;/
- FE_HAS_LOCK = 0x10, /&#x22C6; everything's working... &#x22C6;/
- FE_TIMEDOUT = 0x20, /&#x22C6; no lock within the last ~2 seconds &#x22C6;/
- FE_REINIT = 0x40 /&#x22C6; frontend was reinitialized, &#x22C6;/
- } fe_status_t; /&#x22C6; application is recommned to reset &#x22C6;/
+typedef enum fe_status {
+ FE_HAS_SIGNAL = 0x01,
+ FE_HAS_CARRIER = 0x02,
+ FE_HAS_VITERBI = 0x04,
+ FE_HAS_SYNC = 0x08,
+ FE_HAS_LOCK = 0x10,
+ FE_TIMEDOUT = 0x20,
+ FE_REINIT = 0x40,
+} fe_status_t;
</programlisting>
-<para>to indicate the current state and/or state changes of the frontend hardware.
-</para>
+<para>to indicate the current state and/or state changes of the frontend hardware:
+</para>
+
+<informaltable><tgroup cols="2"><tbody>
+<row>
+<entry align="char">FE_HAS_SIGNAL</entry>
+<entry align="char">The frontend has found something above the noise level</entry>
+</row><row>
+<entry align="char">FE_HAS_CARRIER</entry>
+<entry align="char">The frontend has found a DVB signal</entry>
+</row><row>
+<entry align="char">FE_HAS_VITERBI</entry>
+<entry align="char">The frontend FEC code is stable</entry>
+</row><row>
+<entry align="char">FE_HAS_SYNC</entry>
+<entry align="char">Syncronization bytes was found</entry>
+</row><row>
+<entry align="char">FE_HAS_LOCK</entry>
+<entry align="char">The DVB were locked and everything is working</entry>
+</row><row>
+<entry align="char">FE_TIMEDOUT</entry>
+<entry align="char">no lock within the last about 2 seconds</entry>
+</row><row>
+<entry align="char">FE_REINIT</entry>
+<entry align="char">The frontend was reinitialized, application is
+recommended to reset DiSEqC, tone and parameters</entry>
+</row>
+</tbody></tgroup></informaltable>
</section>
@@ -238,7 +265,7 @@ and to add newer delivery systems.</para>
<constant>FE_GET_PROPERTY/FE_SET_PROPERTY</constant></link> instead, in
order to be able to support the newer System Delivery like DVB-S2, DVB-T2,
DVB-C2, ISDB, etc.</para>
-<para>All kinds of parameters are combined as an union in the FrontendParameters structure:</para>
+<para>All kinds of parameters are combined as an union in the FrontendParameters structure:
<programlisting>
struct dvb_frontend_parameters {
uint32_t frequency; /&#x22C6; (absolute) frequency in Hz for QAM/OFDM &#x22C6;/
@@ -251,12 +278,13 @@ struct dvb_frontend_parameters {
struct dvb_vsb_parameters vsb;
} u;
};
-</programlisting>
+</programlisting></para>
<para>In the case of QPSK frontends the <constant>frequency</constant> field specifies the intermediate
frequency, i.e. the offset which is effectively added to the local oscillator frequency (LOF) of
the LNB. The intermediate frequency has to be specified in units of kHz. For QAM and
OFDM frontends the <constant>frequency</constant> specifies the absolute frequency and is given in Hz.
</para>
+
<section id="dvb-qpsk-parameters">
<title>QPSK parameters</title>
<para>For satellite QPSK frontends you have to use the <constant>dvb_qpsk_parameters</constant> structure:</para>
@@ -321,8 +349,8 @@ itself.
<section id="fe-code-rate-t">
<title>frontend code rate</title>
<para>The possible values for the <constant>fec_inner</constant> field used on
-<link refend="dvb-qpsk-parameters"><constant>struct dvb_qpsk_parameters</constant></link> and
-<link refend="dvb-qam-parameters"><constant>struct dvb_qam_parameters</constant></link> are:
+<link linkend="dvb-qpsk-parameters"><constant>struct dvb_qpsk_parameters</constant></link> and
+<link linkend="dvb-qam-parameters"><constant>struct dvb_qam_parameters</constant></link> are:
</para>
<programlisting>
typedef enum fe_code_rate {
@@ -347,9 +375,9 @@ detection.
<section id="fe-modulation-t">
<title>frontend modulation type for QAM, OFDM and VSB</title>
<para>For cable and terrestrial frontends, e. g. for
-<link refend="dvb-qam-parameters"><constant>struct dvb_qpsk_parameters</constant></link>,
-<link refend="dvb-ofdm-parameters"><constant>struct dvb_qam_parameters</constant></link> and
-<link refend="dvb-vsb-parameters"><constant>struct dvb_qam_parameters</constant></link>,
+<link linkend="dvb-qam-parameters"><constant>struct dvb_qpsk_parameters</constant></link>,
+<link linkend="dvb-ofdm-parameters"><constant>struct dvb_qam_parameters</constant></link> and
+<link linkend="dvb-vsb-parameters"><constant>struct dvb_qam_parameters</constant></link>,
it needs to specify the quadrature modulation mode which can be one of the following:
</para>
<programlisting>
@@ -370,8 +398,8 @@ it needs to specify the quadrature modulation mode which can be one of the follo
} fe_modulation_t;
</programlisting>
</section>
-<para>Finally, there are several more parameters for OFDM:
-</para>
+<section>
+<title>More OFDM parameters</title>
<section id="fe-transmit-mode-t">
<title>Number of carriers per channel</title>
<programlisting>
@@ -427,6 +455,7 @@ typedef enum fe_hierarchy {
} fe_hierarchy_t;
</programlisting>
</section>
+</section>
</section>
diff --git a/Documentation/DocBook/media/dvb/intro.xml b/Documentation/DocBook/media/dvb/intro.xml
index 170064a3dc8f..2048b53d19b9 100644
--- a/Documentation/DocBook/media/dvb/intro.xml
+++ b/Documentation/DocBook/media/dvb/intro.xml
@@ -205,7 +205,7 @@ a partial path like:</para>
additional include file <emphasis
role="tt">linux/dvb/version.h</emphasis> exists, which defines the
constant <emphasis role="tt">DVB_API_VERSION</emphasis>. This document
-describes <emphasis role="tt">DVB_API_VERSION 5.4</emphasis>.
+describes <emphasis role="tt">DVB_API_VERSION 5.8</emphasis>.
</para>
</section>
diff --git a/Documentation/DocBook/media/dvb/kdapi.xml b/Documentation/DocBook/media/dvb/kdapi.xml
index 6c67481eaa4b..6c11ec52cbee 100644
--- a/Documentation/DocBook/media/dvb/kdapi.xml
+++ b/Documentation/DocBook/media/dvb/kdapi.xml
@@ -2,7 +2,7 @@
<para>The kernel demux API defines a driver-internal interface for registering low-level,
hardware specific driver to a hardware independent demux layer. It is only of interest for
DVB device driver writers. The header file for this API is named <emphasis role="tt">demux.h</emphasis> and located in
-<emphasis role="tt">drivers/media/dvb/dvb-core</emphasis>.
+<emphasis role="tt">drivers/media/dvb-core</emphasis>.
</para>
<para>Maintainer note: This section must be reviewed. It is probably out of date.
</para>
diff --git a/Documentation/DocBook/media/dvb/net.xml b/Documentation/DocBook/media/dvb/net.xml
index 67d37e5ce597..a193e86941b5 100644
--- a/Documentation/DocBook/media/dvb/net.xml
+++ b/Documentation/DocBook/media/dvb/net.xml
@@ -26,4 +26,131 @@ struct dvb_net_if {
<title>DVB net Function Calls</title>
<para>To be written&#x2026;
</para>
+
+<section id="NET_ADD_IF"
+role="subsection"><title>NET_ADD_IF</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = NET_ADD_IF,
+ struct dvb_net_if *if);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals NET_ADD_IF for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct dvb_net_if *if
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="NET_REMOVE_IF"
+role="subsection"><title>NET_REMOVE_IF</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = NET_REMOVE_IF);
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals NET_REMOVE_IF for this command.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
+
+<section id="NET_GET_IF"
+role="subsection"><title>NET_GET_IF</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl is undocumented. Documentation is welcome.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(fd, int request = NET_GET_IF,
+ struct dvb_net_if *if);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals NET_GET_IF for this command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct dvb_net_if *if
+</para>
+</entry><entry
+ align="char">
+<para>Undocumented.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+</section>
</section>
diff --git a/Documentation/DocBook/media/dvb/video.xml b/Documentation/DocBook/media/dvb/video.xml
index 25fb823226b4..3ea1ca7e785e 100644
--- a/Documentation/DocBook/media/dvb/video.xml
+++ b/Documentation/DocBook/media/dvb/video.xml
@@ -15,6 +15,10 @@ the audio and video device as well as the video4linux device.
<para>The ioctls that deal with SPUs (sub picture units) and navigation packets are only
supported on some MPEG decoders made for DVD playback.
</para>
+<para>
+These ioctls were also used by V4L2 to control MPEG decoders implemented in V4L2. The use
+of these ioctls for that purpose has been made obsolete and proper V4L2 ioctls or controls
+have been created to replace that functionality.</para>
<section id="video_types">
<title>Video Data Types</title>
@@ -55,7 +59,7 @@ typedef enum {
</section>
<section id="video-stream-source-t">
-<title>video stream source</title>
+<title>video_stream_source_t</title>
<para>The video stream source is set through the VIDEO_SELECT_SOURCE call and can take
the following values, depending on whether we are replaying from an internal (demuxer) or
external (user write) source.
@@ -76,7 +80,7 @@ call.
</section>
<section id="video-play-state-t">
-<title>video play state</title>
+<title>video_play_state_t</title>
<para>The following values can be returned by the VIDEO_GET_STATUS call representing the
state of video playback.
</para>
@@ -90,9 +94,9 @@ typedef enum {
</section>
<section id="video-command">
+<title>struct video_command</title>
<para>The structure must be zeroed before use by the application
This ensures it can be extended safely in the future.</para>
-<title>struct video-command</title>
<programlisting>
struct video_command {
__u32 cmd;
@@ -121,7 +125,7 @@ struct video_command {
</section>
<section id="video-size-t">
-<title>struct video_size-t</title>
+<title>video_size_t</title>
<programlisting>
typedef struct {
int w;
@@ -217,7 +221,7 @@ bits set according to the hardwares capabilities.
</section>
<section id="video-system">
-<title>video system</title>
+<title>video_system_t</title>
<para>A call to VIDEO_SET_SYSTEM sets the desired video system for TV output. The
following system types can be set:
</para>
@@ -263,7 +267,7 @@ call expects the following format for that information:
</section>
<section id="video-spu">
-<title>video SPU</title>
+<title>struct video_spu</title>
<para>Calling VIDEO_SET_SPU deactivates or activates SPU decoding, according to the
following format:
</para>
@@ -277,12 +281,12 @@ following format:
</section>
<section id="video-spu-palette">
-<title>video SPU palette</title>
+<title>struct video_spu_palette</title>
<para>The following structure is used to set the SPU palette by calling VIDEO_SPU_PALETTE:
</para>
<programlisting>
typedef
- struct video_spu_palette{
+ struct video_spu_palette {
int length;
uint8_t &#x22C6;palette;
} video_spu_palette_t;
@@ -290,13 +294,13 @@ following format:
</section>
<section id="video-navi-pack">
-<title>video NAVI pack</title>
+<title>struct video_navi_pack</title>
<para>In order to get the navigational data the following structure has to be passed to the ioctl
VIDEO_GET_NAVI:
</para>
<programlisting>
typedef
- struct video_navi_pack{
+ struct video_navi_pack {
int length; /&#x22C6; 0 ... 1024 &#x22C6;/
uint8_t data[1024];
} video_navi_pack_t;
@@ -305,7 +309,7 @@ VIDEO_GET_NAVI:
<section id="video-attributes-t">
-<title>video attributes</title>
+<title>video_attributes_t</title>
<para>The following attributes can be set by a call to VIDEO_SET_ATTRIBUTES:
</para>
<programlisting>
@@ -541,6 +545,8 @@ VIDEO_GET_NAVI:
role="subsection"><title>VIDEO_STOP</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+&VIDIOC-DECODER-CMD; instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call asks the Video Device to stop playing the current stream.
@@ -598,6 +604,8 @@ role="subsection"><title>VIDEO_STOP</title>
role="subsection"><title>VIDEO_PLAY</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+&VIDIOC-DECODER-CMD; instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call asks the Video Device to start playing a video stream from the
@@ -634,6 +642,8 @@ role="subsection"><title>VIDEO_PLAY</title>
role="subsection"><title>VIDEO_FREEZE</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+&VIDIOC-DECODER-CMD; instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call suspends the live video stream being played. Decoding
@@ -674,6 +684,8 @@ role="subsection"><title>VIDEO_FREEZE</title>
role="subsection"><title>VIDEO_CONTINUE</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To control a V4L2 decoder use the V4L2
+&VIDIOC-DECODER-CMD; instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call restarts decoding and playing processes of the video stream
@@ -710,6 +722,9 @@ role="subsection"><title>VIDEO_CONTINUE</title>
role="subsection"><title>VIDEO_SELECT_SOURCE</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. This ioctl was also supported by the
+V4L2 ivtv driver, but that has been replaced by the ivtv-specific
+<constant>IVTV_IOC_PASSTHROUGH_MODE</constant> ioctl.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call informs the video device which source shall be used for the input
@@ -845,10 +860,160 @@ role="subsection"><title>VIDEO_GET_STATUS</title>
</row></tbody></tgroup></informaltable>
&return-value-dvb;
+</section><section id="VIDEO_GET_FRAME_COUNT"
+role="subsection"><title>VIDEO_GET_FRAME_COUNT</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders this
+ioctl has been replaced by the <constant>V4L2_CID_MPEG_VIDEO_DEC_FRAME</constant> control.</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call asks the Video Device to return the number of displayed frames
+since the decoder was started.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_GET_FRAME_COUNT, __u64 *pts);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_GET_FRAME_COUNT for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u64 *pts
+</para>
+</entry><entry
+ align="char">
+<para>Returns the number of frames displayed since the decoder was started.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
+</section><section id="VIDEO_GET_PTS"
+role="subsection"><title>VIDEO_GET_PTS</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders this
+ioctl has been replaced by the <constant>V4L2_CID_MPEG_VIDEO_DEC_PTS</constant> control.</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call asks the Video Device to return the current PTS timestamp.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_GET_PTS, __u64 *pts);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_GET_PTS for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>__u64 *pts
+</para>
+</entry><entry
+ align="char">
+<para>Returns the 33-bit timestamp as defined in ITU T-REC-H.222.0 / ISO/IEC 13818-1.
+</para>
+<para>
+The PTS should belong to the currently played
+frame if possible, but may also be a value close to it
+like the PTS of the last decoded frame or the last PTS
+extracted by the PES parser.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
+</section><section id="VIDEO_GET_FRAME_RATE"
+role="subsection"><title>VIDEO_GET_FRAME_RATE</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl call asks the Video Device to return the current framerate.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_GET_FRAME_RATE, unsigned int *rate);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_GET_FRAME_RATE for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>unsigned int *rate
+</para>
+</entry><entry
+ align="char">
+<para>Returns the framerate in number of frames per 1000 seconds.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
</section><section id="VIDEO_GET_EVENT"
role="subsection"><title>VIDEO_GET_EVENT</title>
<para>DESCRIPTION
</para>
+<para>This ioctl is for DVB devices only. To get events from a V4L2 decoder use the V4L2
+&VIDIOC-DQEVENT; ioctl instead.</para>
<informaltable><tgroup cols="1"><tbody><row><entry
align="char">
<para>This ioctl call returns an event of type video_event if available. If an event is
@@ -914,6 +1079,152 @@ role="subsection"><title>VIDEO_GET_EVENT</title>
</entry>
</row></tbody></tgroup></informaltable>
+</section><section id="VIDEO_COMMAND"
+role="subsection"><title>VIDEO_COMMAND</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders this
+ioctl has been replaced by the &VIDIOC-DECODER-CMD; ioctl.</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl commands the decoder. The <constant>video_command</constant> struct
+is a subset of the <constant>v4l2_decoder_cmd</constant> struct, so refer to the
+&VIDIOC-DECODER-CMD; documentation for more information.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_COMMAND, struct video_command *cmd);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_COMMAND for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct video_command *cmd
+</para>
+</entry><entry
+ align="char">
+<para>Commands the decoder.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
+</section><section id="VIDEO_TRY_COMMAND"
+role="subsection"><title>VIDEO_TRY_COMMAND</title>
+<para>DESCRIPTION
+</para>
+<para>This ioctl is obsolete. Do not use in new drivers. For V4L2 decoders this
+ioctl has been replaced by the &VIDIOC-TRY-DECODER-CMD; ioctl.</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl tries a decoder command. The <constant>video_command</constant> struct
+is a subset of the <constant>v4l2_decoder_cmd</constant> struct, so refer to the
+&VIDIOC-TRY-DECODER-CMD; documentation for more information.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_TRY_COMMAND, struct video_command *cmd);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_TRY_COMMAND for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>struct video_command *cmd
+</para>
+</entry><entry
+ align="char">
+<para>Try a decoder command.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
+</section><section id="VIDEO_GET_SIZE"
+role="subsection"><title>VIDEO_GET_SIZE</title>
+<para>DESCRIPTION
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>This ioctl returns the size and aspect ratio.</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>SYNOPSIS
+</para>
+<informaltable><tgroup cols="1"><tbody><row><entry
+ align="char">
+<para>int ioctl(int fd, int request =
+ VIDEO_GET_SIZE, video_size_t *size);</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+<para>PARAMETERS
+</para>
+<informaltable><tgroup cols="2"><tbody><row><entry
+ align="char">
+<para>int fd</para>
+</entry><entry
+ align="char">
+<para>File descriptor returned by a previous call to open().</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>int request</para>
+</entry><entry
+ align="char">
+<para>Equals VIDEO_GET_SIZE for this
+ command.</para>
+</entry>
+ </row><row><entry
+ align="char">
+<para>video_size_t *size
+</para>
+</entry><entry
+ align="char">
+<para>Returns the size and aspect ratio.
+</para>
+</entry>
+ </row></tbody></tgroup></informaltable>
+&return-value-dvb;
+
</section><section id="VIDEO_SET_DISPLAY_FORMAT"
role="subsection"><title>VIDEO_SET_DISPLAY_FORMAT</title>
<para>DESCRIPTION
diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml
index 1078e45f189f..d2eb79e41a01 100644
--- a/Documentation/DocBook/media/v4l/biblio.xml
+++ b/Documentation/DocBook/media/v4l/biblio.xml
@@ -178,23 +178,23 @@ Signal - NTSC for Studio Applications"</title>
1125-Line High-Definition Production"</title>
</biblioentry>
- <biblioentry id="en50067">
- <abbrev>EN&nbsp;50067</abbrev>
+ <biblioentry id="iec62106">
+ <abbrev>IEC&nbsp;62106</abbrev>
<authorgroup>
- <corpauthor>European Committee for Electrotechnical Standardization
-(<ulink url="http://www.cenelec.eu">http://www.cenelec.eu</ulink>)</corpauthor>
+ <corpauthor>International Electrotechnical Commission
+(<ulink url="http://www.iec.ch">http://www.iec.ch</ulink>)</corpauthor>
</authorgroup>
<title>Specification of the radio data system (RDS) for VHF/FM sound broadcasting
in the frequency range from 87,5 to 108,0 MHz</title>
</biblioentry>
<biblioentry id="nrsc4">
- <abbrev>NRSC-4</abbrev>
+ <abbrev>NRSC-4-B</abbrev>
<authorgroup>
<corpauthor>National Radio Systems Committee
(<ulink url="http://www.nrscstandards.org">http://www.nrscstandards.org</ulink>)</corpauthor>
</authorgroup>
- <title>NRSC-4: United States RBDS Standard</title>
+ <title>NRSC-4-B: United States RBDS Standard</title>
</biblioentry>
<biblioentry id="iso12232">
@@ -226,4 +226,44 @@ in the frequency range from 87,5 to 108,0 MHz</title>
<title>VESA and Industry Standards and Guidelines for Computer Display Monitor Timing (DMT)</title>
</biblioentry>
+ <biblioentry id="vesaedid">
+ <abbrev>EDID</abbrev>
+ <authorgroup>
+ <corpauthor>Video Electronics Standards Association
+(<ulink url="http://www.vesa.org">http://www.vesa.org</ulink>)</corpauthor>
+ </authorgroup>
+ <title>VESA Enhanced Extended Display Identification Data Standard</title>
+ <subtitle>Release A, Revision 2</subtitle>
+ </biblioentry>
+
+ <biblioentry id="hdcp">
+ <abbrev>HDCP</abbrev>
+ <authorgroup>
+ <corpauthor>Digital Content Protection LLC
+(<ulink url="http://www.digital-cp.com">http://www.digital-cp.com</ulink>)</corpauthor>
+ </authorgroup>
+ <title>High-bandwidth Digital Content Protection System</title>
+ <subtitle>Revision 1.3</subtitle>
+ </biblioentry>
+
+ <biblioentry id="hdmi">
+ <abbrev>HDMI</abbrev>
+ <authorgroup>
+ <corpauthor>HDMI Licensing LLC
+(<ulink url="http://www.hdmi.org">http://www.hdmi.org</ulink>)</corpauthor>
+ </authorgroup>
+ <title>High-Definition Multimedia Interface</title>
+ <subtitle>Specification Version 1.4a</subtitle>
+ </biblioentry>
+
+ <biblioentry id="dp">
+ <abbrev>DP</abbrev>
+ <authorgroup>
+ <corpauthor>Video Electronics Standards Association
+(<ulink url="http://www.vesa.org">http://www.vesa.org</ulink>)</corpauthor>
+ </authorgroup>
+ <title>VESA DisplayPort Standard</title>
+ <subtitle>Version 1, Revision 2</subtitle>
+ </biblioentry>
+
</bibliography>
diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml
index b91d25313b63..73c6847436c9 100644
--- a/Documentation/DocBook/media/v4l/common.xml
+++ b/Documentation/DocBook/media/v4l/common.xml
@@ -564,7 +564,7 @@ automatically.</para>
<para>To query and select the standard used by the current video
input or output applications call the &VIDIOC-G-STD; and
&VIDIOC-S-STD; ioctl, respectively. The <emphasis>received</emphasis>
-standard can be sensed with the &VIDIOC-QUERYSTD; ioctl. Note parameter of all these ioctls is a pointer to a &v4l2-std-id; type (a standard set), <emphasis>not</emphasis> an index into the standard enumeration.<footnote>
+standard can be sensed with the &VIDIOC-QUERYSTD; ioctl. Note that the parameter of all these ioctls is a pointer to a &v4l2-std-id; type (a standard set), <emphasis>not</emphasis> an index into the standard enumeration.<footnote>
<para>An alternative to the current scheme is to use pointers
to indices as arguments of <constant>VIDIOC_G_STD</constant> and
<constant>VIDIOC_S_STD</constant>, the &v4l2-input; and
@@ -588,30 +588,28 @@ switch to a standard by &v4l2-std-id;.</para>
</footnote> Drivers must implement all video standard ioctls
when the device has one or more video inputs or outputs.</para>
- <para>Special rules apply to USB cameras where the notion of video
-standards makes little sense. More generally any capture device,
-output devices accordingly, which is <itemizedlist>
+ <para>Special rules apply to devices such as USB cameras where the notion of video
+standards makes little sense. More generally for any capture or output device
+which is: <itemizedlist>
<listitem>
<para>incapable of capturing fields or frames at the nominal
rate of the video standard, or</para>
</listitem>
<listitem>
- <para>where <link linkend="buffer">timestamps</link> refer
-to the instant the field or frame was received by the driver, not the
-capture time, or</para>
- </listitem>
- <listitem>
- <para>where <link linkend="buffer">sequence numbers</link>
-refer to the frames received by the driver, not the captured
-frames.</para>
+ <para>that does not support the video standard formats at all.</para>
</listitem>
</itemizedlist> Here the driver shall set the
<structfield>std</structfield> field of &v4l2-input; and &v4l2-output;
-to zero, the <constant>VIDIOC_G_STD</constant>,
+to zero and the <constant>VIDIOC_G_STD</constant>,
<constant>VIDIOC_S_STD</constant>,
<constant>VIDIOC_QUERYSTD</constant> and
<constant>VIDIOC_ENUMSTD</constant> ioctls shall return the
-&EINVAL;.<footnote>
+&ENOTTY;.<footnote>
+ <para>See <xref linkend="buffer" /> for a rationale.</para>
+ <para>Applications can make use of the <xref linkend="input-capabilities" /> and
+<xref linkend="output-capabilities"/> flags to determine whether the video standard ioctls
+are available for the device.</para>
+&ENOTTY;.
<para>See <xref linkend="buffer" /> for a rationale. Probably
even USB cameras follow some well known video standard. It might have
been better to explicitly indicate elsewhere if a device cannot live
@@ -626,9 +624,9 @@ up to normal expectations, instead of this exception.</para>
&v4l2-standard; standard;
if (-1 == ioctl (fd, &VIDIOC-G-STD;, &amp;std_id)) {
- /* Note when VIDIOC_ENUMSTD always returns EINVAL this
+ /* Note when VIDIOC_ENUMSTD always returns ENOTTY this
is no video device or it falls under the USB exception,
- and VIDIOC_G_STD returning EINVAL is no error. */
+ and VIDIOC_G_STD returning ENOTTY is no error. */
perror ("VIDIOC_G_STD");
exit (EXIT_FAILURE);
diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml
index faa0fd14666a..c6ae4c9d0e0c 100644
--- a/Documentation/DocBook/media/v4l/compat.xml
+++ b/Documentation/DocBook/media/v4l/compat.xml
@@ -1476,7 +1476,7 @@ follows.<informaltable>
</row>
<row>
<entry><constant>V4L2_BUF_TYPE_PRIVATE_BASE</constant></entry>
- <entry><constant>V4L2_BUF_TYPE_PRIVATE</constant></entry>
+ <entry><constant>V4L2_BUF_TYPE_PRIVATE</constant> (but this is deprecated)</entry>
</row>
</tbody>
</tgroup>
@@ -2468,21 +2468,9 @@ that used it. It was originally scheduled for removal in 2.6.35.
<structfield>reserved2</structfield> and removed
<constant>V4L2_BUF_FLAG_INPUT</constant>.</para>
</listitem>
- </orderedlist>
- </section>
-
- <section>
- <title>V4L2 in Linux 3.6</title>
- <orderedlist>
<listitem>
<para>Added V4L2_CAP_VIDEO_M2M and V4L2_CAP_VIDEO_M2M_MPLANE capabilities.</para>
</listitem>
- </orderedlist>
- </section>
-
- <section>
- <title>V4L2 in Linux 3.6</title>
- <orderedlist>
<listitem>
<para>Added support for frequency band enumerations: &VIDIOC-ENUM-FREQ-BANDS;.</para>
</listitem>
@@ -2567,29 +2555,6 @@ and may change in the future.</para>
<para>Video Output Overlay (OSD) Interface, <xref
linkend="osd" />.</para>
</listitem>
- <listitem>
- <para><constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY</constant>,
- &v4l2-buf-type;, <xref linkend="v4l2-buf-type" />.</para>
- </listitem>
- <listitem>
- <para><constant>V4L2_CAP_VIDEO_OUTPUT_OVERLAY</constant>,
-&VIDIOC-QUERYCAP; ioctl, <xref linkend="device-capabilities" />.</para>
- </listitem>
- <listitem>
- <para>&VIDIOC-ENUM-FRAMESIZES; and
-&VIDIOC-ENUM-FRAMEINTERVALS; ioctls.</para>
- </listitem>
- <listitem>
- <para>&VIDIOC-G-ENC-INDEX; ioctl.</para>
- </listitem>
- <listitem>
- <para>&VIDIOC-ENCODER-CMD; and &VIDIOC-TRY-ENCODER-CMD;
-ioctls.</para>
- </listitem>
- <listitem>
- <para>&VIDIOC-DECODER-CMD; and &VIDIOC-TRY-DECODER-CMD;
-ioctls.</para>
- </listitem>
<listitem>
<para>&VIDIOC-DBG-G-REGISTER; and &VIDIOC-DBG-S-REGISTER;
ioctls.</para>
@@ -2615,10 +2580,6 @@ ioctls.</para>
and &VIDIOC-SUBDEV-S-SELECTION; ioctls.</para>
</listitem>
<listitem>
- <para><link linkend="v4l2-auto-focus-area"><constant>
- V4L2_CID_AUTO_FOCUS_AREA</constant></link> control.</para>
- </listitem>
- <listitem>
<para>Support for frequency band enumeration: &VIDIOC-ENUM-FREQ-BANDS; ioctl.</para>
</listitem>
</itemizedlist>
diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml
index b0964fb4e834..272a5f718509 100644
--- a/Documentation/DocBook/media/v4l/controls.xml
+++ b/Documentation/DocBook/media/v4l/controls.xml
@@ -3505,7 +3505,7 @@ This encodes up to 31 pre-defined programme types.</entry>
</row>
<row><entry spanname="descr">Sets the Programme Service name (PS_NAME) for transmission.
It is intended for static display on a receiver. It is the primary aid to listeners in programme service
-identification and selection. In Annex E of <xref linkend="en50067" />, the RDS specification,
+identification and selection. In Annex E of <xref linkend="iec62106" />, the RDS specification,
there is a full description of the correct character encoding for Programme Service name strings.
Also from RDS specification, PS is usually a single eight character text. However, it is also possible
to find receivers which can scroll strings sized as 8 x N characters. So, this control must be configured
@@ -3519,7 +3519,7 @@ with steps of 8 characters. The result is it must always contain a string with s
what is being broadcasted. RDS Radio Text can be applied when broadcaster wishes to transmit longer PS names,
programme-related information or any other text. In these cases, RadioText should be used in addition to
<constant>V4L2_CID_RDS_TX_PS_NAME</constant>. The encoding for Radio Text strings is also fully described
-in Annex E of <xref linkend="en50067" />. The length of Radio Text strings depends on which RDS Block is being
+in Annex E of <xref linkend="iec62106" />. The length of Radio Text strings depends on which RDS Block is being
used to transmit it, either 32 (2A block) or 64 (2B block). However, it is also possible
to find receivers which can scroll strings sized as 32 x N or 64 x N characters. So, this control must be configured
with steps of 32 or 64 characters. The result is it must always contain a string with size multiple of 32 or 64. </entry>
@@ -3650,7 +3650,7 @@ manually or automatically if set to zero. Unit, range and step are driver-specif
</table>
<para>For more details about RDS specification, refer to
-<xref linkend="en50067" /> document, from CENELEC.</para>
+<xref linkend="iec62106" /> document, from CENELEC.</para>
</section>
<section id="flash-controls">
@@ -3717,232 +3717,231 @@ interface and may change in the future.</para>
use case involving camera or individually.
</para>
- </section>
+ <table pgwide="1" frame="none" id="flash-control-id">
+ <title>Flash Control IDs</title>
+
+ <tgroup cols="4">
+ <colspec colname="c1" colwidth="1*" />
+ <colspec colname="c2" colwidth="6*" />
+ <colspec colname="c3" colwidth="2*" />
+ <colspec colname="c4" colwidth="6*" />
+ <spanspec namest="c1" nameend="c2" spanname="id" />
+ <spanspec namest="c2" nameend="c4" spanname="descr" />
+ <thead>
+ <row>
+ <entry spanname="id" align="left">ID</entry>
+ <entry align="left">Type</entry>
+ </row><row rowsep="1"><entry spanname="descr" align="left">Description</entry>
+ </row>
+ </thead>
+ <tbody valign="top">
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_CLASS</constant></entry>
+ <entry>class</entry>
+ </row>
+ <row>
+ <entry spanname="descr">The FLASH class descriptor.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_LED_MODE</constant></entry>
+ <entry>menu</entry>
+ </row>
+ <row id="v4l2-flash-led-mode">
+ <entry spanname="descr">Defines the mode of the flash LED,
+ the high-power white LED attached to the flash controller.
+ Setting this control may not be possible in presence of
+ some faults. See V4L2_CID_FLASH_FAULT.</entry>
+ </row>
+ <row>
+ <entrytbl spanname="descr" cols="2">
+ <tbody valign="top">
+ <row>
+ <entry><constant>V4L2_FLASH_LED_MODE_NONE</constant></entry>
+ <entry>Off.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_LED_MODE_FLASH</constant></entry>
+ <entry>Flash mode.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_LED_MODE_TORCH</constant></entry>
+ <entry>Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY.</entry>
+ </row>
+ </tbody>
+ </entrytbl>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_SOURCE</constant></entry>
+ <entry>menu</entry>
+ </row>
+ <row id="v4l2-flash-strobe-source"><entry
+ spanname="descr">Defines the source of the flash LED
+ strobe.</entry>
+ </row>
+ <row>
+ <entrytbl spanname="descr" cols="2">
+ <tbody valign="top">
+ <row>
+ <entry><constant>V4L2_FLASH_STROBE_SOURCE_SOFTWARE</constant></entry>
+ <entry>The flash strobe is triggered by using
+ the V4L2_CID_FLASH_STROBE control.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_STROBE_SOURCE_EXTERNAL</constant></entry>
+ <entry>The flash strobe is triggered by an
+ external source. Typically this is a sensor,
+ which makes it possible to synchronises the
+ flash strobe start to exposure start.</entry>
+ </row>
+ </tbody>
+ </entrytbl>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE</constant></entry>
+ <entry>button</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Strobe flash. Valid when
+ V4L2_CID_FLASH_LED_MODE is set to
+ V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE
+ is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this
+ control may not be possible in presence of some faults.
+ See V4L2_CID_FLASH_FAULT.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STOP</constant></entry>
+ <entry>button</entry>
+ </row>
+ <row><entry spanname="descr">Stop flash strobe immediately.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STATUS</constant></entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Strobe status: whether the flash
+ is strobing at the moment or not. This is a read-only
+ control.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Hardware timeout for flash. The
+ flash strobe is stopped after this period of time has
+ passed from the start of the strobe.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_INTENSITY</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Intensity of the flash strobe when
+ the flash LED is in flash mode
+ (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps
+ (mA) if possible.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_TORCH_INTENSITY</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Intensity of the flash LED in
+ torch mode (V4L2_FLASH_LED_MODE_TORCH). The unit should be
+ milliamps (mA) if possible. Setting this control may not
+ be possible in presence of some faults. See
+ V4L2_CID_FLASH_FAULT.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_INDICATOR_INTENSITY</constant></entry>
+ <entry>integer</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Intensity of the indicator LED.
+ The indicator LED may be fully independent of the flash
+ LED. The unit should be microamps (uA) if possible.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_FAULT</constant></entry>
+ <entry>bitmask</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Faults related to the flash. The
+ faults tell about specific problems in the flash chip
+ itself or the LEDs attached to it. Faults may prevent
+ further use of some of the flash controls. In particular,
+ V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE
+ if the fault affects the flash LED. Exactly which faults
+ have such an effect is chip dependent. Reading the faults
+ resets the control and returns the chip to a usable state
+ if possible.</entry>
+ </row>
+ <row>
+ <entrytbl spanname="descr" cols="2">
+ <tbody valign="top">
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_OVER_VOLTAGE</constant></entry>
+ <entry>Flash controller voltage to the flash LED
+ has exceeded the limit specific to the flash
+ controller.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_TIMEOUT</constant></entry>
+ <entry>The flash strobe was still on when
+ the timeout set by the user ---
+ V4L2_CID_FLASH_TIMEOUT control --- has expired.
+ Not all flash controllers may set this in all
+ such conditions.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_OVER_TEMPERATURE</constant></entry>
+ <entry>The flash controller has overheated.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_SHORT_CIRCUIT</constant></entry>
+ <entry>The short circuit protection of the flash
+ controller has been triggered.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_OVER_CURRENT</constant></entry>
+ <entry>Current in the LED power supply has exceeded the limit
+ specific to the flash controller.</entry>
+ </row>
+ <row>
+ <entry><constant>V4L2_FLASH_FAULT_INDICATOR</constant></entry>
+ <entry>The flash controller has detected a short or open
+ circuit condition on the indicator LED.</entry>
+ </row>
+ </tbody>
+ </entrytbl>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_CHARGE</constant></entry>
+ <entry>boolean</entry>
+ </row>
+ <row><entry spanname="descr">Enable or disable charging of the xenon
+ flash capacitor.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_FLASH_READY</constant></entry>
+ <entry>boolean</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Is the flash ready to strobe?
+ Xenon flashes require their capacitors charged before
+ strobing. LED flashes often require a cooldown period
+ after strobe during which another strobe will not be
+ possible. This is a read-only control.</entry>
+ </row>
+ <row><entry></entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
</section>
-
- <table pgwide="1" frame="none" id="flash-control-id">
- <title>Flash Control IDs</title>
-
- <tgroup cols="4">
- <colspec colname="c1" colwidth="1*" />
- <colspec colname="c2" colwidth="6*" />
- <colspec colname="c3" colwidth="2*" />
- <colspec colname="c4" colwidth="6*" />
- <spanspec namest="c1" nameend="c2" spanname="id" />
- <spanspec namest="c2" nameend="c4" spanname="descr" />
- <thead>
- <row>
- <entry spanname="id" align="left">ID</entry>
- <entry align="left">Type</entry>
- </row><row rowsep="1"><entry spanname="descr" align="left">Description</entry>
- </row>
- </thead>
- <tbody valign="top">
- <row><entry></entry></row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_CLASS</constant></entry>
- <entry>class</entry>
- </row>
- <row>
- <entry spanname="descr">The FLASH class descriptor.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_LED_MODE</constant></entry>
- <entry>menu</entry>
- </row>
- <row id="v4l2-flash-led-mode">
- <entry spanname="descr">Defines the mode of the flash LED,
- the high-power white LED attached to the flash controller.
- Setting this control may not be possible in presence of
- some faults. See V4L2_CID_FLASH_FAULT.</entry>
- </row>
- <row>
- <entrytbl spanname="descr" cols="2">
- <tbody valign="top">
- <row>
- <entry><constant>V4L2_FLASH_LED_MODE_NONE</constant></entry>
- <entry>Off.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_LED_MODE_FLASH</constant></entry>
- <entry>Flash mode.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_LED_MODE_TORCH</constant></entry>
- <entry>Torch mode. See V4L2_CID_FLASH_TORCH_INTENSITY.</entry>
- </row>
- </tbody>
- </entrytbl>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_SOURCE</constant></entry>
- <entry>menu</entry>
- </row>
- <row id="v4l2-flash-strobe-source"><entry
- spanname="descr">Defines the source of the flash LED
- strobe.</entry>
- </row>
- <row>
- <entrytbl spanname="descr" cols="2">
- <tbody valign="top">
- <row>
- <entry><constant>V4L2_FLASH_STROBE_SOURCE_SOFTWARE</constant></entry>
- <entry>The flash strobe is triggered by using
- the V4L2_CID_FLASH_STROBE control.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_STROBE_SOURCE_EXTERNAL</constant></entry>
- <entry>The flash strobe is triggered by an
- external source. Typically this is a sensor,
- which makes it possible to synchronises the
- flash strobe start to exposure start.</entry>
- </row>
- </tbody>
- </entrytbl>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE</constant></entry>
- <entry>button</entry>
- </row>
- <row>
- <entry spanname="descr">Strobe flash. Valid when
- V4L2_CID_FLASH_LED_MODE is set to
- V4L2_FLASH_LED_MODE_FLASH and V4L2_CID_FLASH_STROBE_SOURCE
- is set to V4L2_FLASH_STROBE_SOURCE_SOFTWARE. Setting this
- control may not be possible in presence of some faults.
- See V4L2_CID_FLASH_FAULT.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STOP</constant></entry>
- <entry>button</entry>
- </row>
- <row><entry spanname="descr">Stop flash strobe immediately.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_STROBE_STATUS</constant></entry>
- <entry>boolean</entry>
- </row>
- <row>
- <entry spanname="descr">Strobe status: whether the flash
- is strobing at the moment or not. This is a read-only
- control.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_TIMEOUT</constant></entry>
- <entry>integer</entry>
- </row>
- <row>
- <entry spanname="descr">Hardware timeout for flash. The
- flash strobe is stopped after this period of time has
- passed from the start of the strobe.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_INTENSITY</constant></entry>
- <entry>integer</entry>
- </row>
- <row>
- <entry spanname="descr">Intensity of the flash strobe when
- the flash LED is in flash mode
- (V4L2_FLASH_LED_MODE_FLASH). The unit should be milliamps
- (mA) if possible.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_TORCH_INTENSITY</constant></entry>
- <entry>integer</entry>
- </row>
- <row>
- <entry spanname="descr">Intensity of the flash LED in
- torch mode (V4L2_FLASH_LED_MODE_TORCH). The unit should be
- milliamps (mA) if possible. Setting this control may not
- be possible in presence of some faults. See
- V4L2_CID_FLASH_FAULT.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_INDICATOR_INTENSITY</constant></entry>
- <entry>integer</entry>
- </row>
- <row>
- <entry spanname="descr">Intensity of the indicator LED.
- The indicator LED may be fully independent of the flash
- LED. The unit should be microamps (uA) if possible.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_FAULT</constant></entry>
- <entry>bitmask</entry>
- </row>
- <row>
- <entry spanname="descr">Faults related to the flash. The
- faults tell about specific problems in the flash chip
- itself or the LEDs attached to it. Faults may prevent
- further use of some of the flash controls. In particular,
- V4L2_CID_FLASH_LED_MODE is set to V4L2_FLASH_LED_MODE_NONE
- if the fault affects the flash LED. Exactly which faults
- have such an effect is chip dependent. Reading the faults
- resets the control and returns the chip to a usable state
- if possible.</entry>
- </row>
- <row>
- <entrytbl spanname="descr" cols="2">
- <tbody valign="top">
- <row>
- <entry><constant>V4L2_FLASH_FAULT_OVER_VOLTAGE</constant></entry>
- <entry>Flash controller voltage to the flash LED
- has exceeded the limit specific to the flash
- controller.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_FAULT_TIMEOUT</constant></entry>
- <entry>The flash strobe was still on when
- the timeout set by the user ---
- V4L2_CID_FLASH_TIMEOUT control --- has expired.
- Not all flash controllers may set this in all
- such conditions.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_FAULT_OVER_TEMPERATURE</constant></entry>
- <entry>The flash controller has overheated.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_FAULT_SHORT_CIRCUIT</constant></entry>
- <entry>The short circuit protection of the flash
- controller has been triggered.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_FAULT_OVER_CURRENT</constant></entry>
- <entry>Current in the LED power supply has exceeded the limit
- specific to the flash controller.</entry>
- </row>
- <row>
- <entry><constant>V4L2_FLASH_FAULT_INDICATOR</constant></entry>
- <entry>The flash controller has detected a short or open
- circuit condition on the indicator LED.</entry>
- </row>
- </tbody>
- </entrytbl>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_CHARGE</constant></entry>
- <entry>boolean</entry>
- </row>
- <row><entry spanname="descr">Enable or disable charging of the xenon
- flash capacitor.</entry>
- </row>
- <row>
- <entry spanname="id"><constant>V4L2_CID_FLASH_READY</constant></entry>
- <entry>boolean</entry>
- </row>
- <row>
- <entry spanname="descr">Is the flash ready to strobe?
- Xenon flashes require their capacitors charged before
- strobing. LED flashes often require a cooldown period
- after strobe during which another strobe will not be
- possible. This is a read-only control.</entry>
- </row>
- <row><entry></entry></row>
- </tbody>
- </tgroup>
- </table>
</section>
<section id="jpeg-controls">
@@ -4274,4 +4273,165 @@ interface and may change in the future.</para>
</table>
</section>
+
+ <section id="dv-controls">
+ <title>Digital Video Control Reference</title>
+
+ <note>
+ <title>Experimental</title>
+
+ <para>This is an <link
+ linkend="experimental">experimental</link> interface and may
+ change in the future.</para>
+ </note>
+
+ <para>
+ The Digital Video control class is intended to control receivers
+ and transmitters for <ulink url="http://en.wikipedia.org/wiki/Vga">VGA</ulink>,
+ <ulink url="http://en.wikipedia.org/wiki/Digital_Visual_Interface">DVI</ulink>
+ (Digital Visual Interface), HDMI (<xref linkend="hdmi" />) and DisplayPort (<xref linkend="dp" />).
+ These controls are generally expected to be private to the receiver or transmitter
+ subdevice that implements them, so they are only exposed on the
+ <filename>/dev/v4l-subdev*</filename> device node.
+ </para>
+
+ <para>Note that these devices can have multiple input or output pads which are
+ hooked up to e.g. HDMI connectors. Even though the subdevice will receive or
+ transmit video from/to only one of those pads, the other pads can still be
+ active when it comes to EDID (Extended Display Identification Data,
+ <xref linkend="vesaedid" />) and HDCP (High-bandwidth Digital Content
+ Protection System, <xref linkend="hdcp" />) processing, allowing the device
+ to do the fairly slow EDID/HDCP handling in advance. This allows for quick
+ switching between connectors.</para>
+
+ <para>These pads appear in several of the controls in this section as
+ bitmasks, one bit for each pad. Bit 0 corresponds to pad 0, bit 1 to pad 1,
+ etc. The maximum value of the control is the set of valid pads.</para>
+
+ <table pgwide="1" frame="none" id="dv-control-id">
+ <title>Digital Video Control IDs</title>
+
+ <tgroup cols="4">
+ <colspec colname="c1" colwidth="1*" />
+ <colspec colname="c2" colwidth="6*" />
+ <colspec colname="c3" colwidth="2*" />
+ <colspec colname="c4" colwidth="6*" />
+ <spanspec namest="c1" nameend="c2" spanname="id" />
+ <spanspec namest="c2" nameend="c4" spanname="descr" />
+ <thead>
+ <row>
+ <entry spanname="id" align="left">ID</entry>
+ <entry align="left">Type</entry>
+ </row><row rowsep="1"><entry spanname="descr" align="left">Description</entry>
+ </row>
+ </thead>
+ <tbody valign="top">
+ <row><entry></entry></row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_CLASS</constant></entry>
+ <entry>class</entry>
+ </row>
+ <row>
+ <entry spanname="descr">The Digital Video class descriptor.</entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_HOTPLUG</constant></entry>
+ <entry>bitmask</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Many connectors have a hotplug pin which is high
+ if EDID information is available from the source. This control shows the
+ state of the hotplug pin as seen by the transmitter.
+ Each bit corresponds to an output pad on the transmitter. If an output pad
+ does not have an associated hotplug pin, then the bit for that pad will be 0.
+ This read-only control is applicable to DVI-D, HDMI and DisplayPort connectors.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_RXSENSE</constant></entry>
+ <entry>bitmask</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Rx Sense is the detection of pull-ups on the TMDS
+ clock lines. This normally means that the sink has left/entered standby (i.e.
+ the transmitter can sense that the receiver is ready to receive video).
+ Each bit corresponds to an output pad on the transmitter. If an output pad
+ does not have an associated Rx Sense, then the bit for that pad will be 0.
+ This read-only control is applicable to DVI-D and HDMI devices.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_EDID_PRESENT</constant></entry>
+ <entry>bitmask</entry>
+ </row>
+ <row>
+ <entry spanname="descr">When the transmitter sees the hotplug signal from the
+ receiver it will attempt to read the EDID. If set, then the transmitter has read
+ at least the first block (= 128 bytes).
+ Each bit corresponds to an output pad on the transmitter. If an output pad
+ does not support EDIDs, then the bit for that pad will be 0.
+ This read-only control is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_MODE</constant></entry>
+ <entry id="v4l2-dv-tx-mode">enum v4l2_dv_tx_mode</entry>
+ </row>
+ <row>
+ <entry spanname="descr">HDMI transmitters can transmit in DVI-D mode (just video)
+ or in HDMI mode (video + audio + auxiliary data). This control selects which mode
+ to use: V4L2_DV_TX_MODE_DVI_D or V4L2_DV_TX_MODE_HDMI.
+ This control is applicable to HDMI connectors.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_TX_RGB_RANGE</constant></entry>
+ <entry id="v4l2-dv-rgb-range">enum v4l2_dv_rgb_range</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Select the quantization range for RGB output. V4L2_DV_RANGE_AUTO
+ follows the RGB quantization range specified in the standard for the video interface
+ (ie. <xref linkend="cea861" /> for HDMI). V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the standard
+ to be compatible with sinks that have not implemented the standard correctly
+ (unfortunately quite common for HDMI and DVI-D). Full range allows all possible values to be
+ used whereas limited range sets the range to (16 &lt;&lt; (N-8)) - (235 &lt;&lt; (N-8))
+ where N is the number of bits per component.
+ This control is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_RX_POWER_PRESENT</constant></entry>
+ <entry>bitmask</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Detects whether the receiver receives power from the source
+ (e.g. HDMI carries 5V on one of the pins). This is often used to power an eeprom
+ which contains EDID information, such that the source can read the EDID even if
+ the sink is in standby/power off.
+ Each bit corresponds to an input pad on the transmitter. If an input pad
+ cannot detect whether power is present, then the bit for that pad will be 0.
+ This read-only control is applicable to DVI-D, HDMI and DisplayPort connectors.
+ </entry>
+ </row>
+ <row>
+ <entry spanname="id"><constant>V4L2_CID_DV_RX_RGB_RANGE</constant></entry>
+ <entry>enum v4l2_dv_rgb_range</entry>
+ </row>
+ <row>
+ <entry spanname="descr">Select the quantization range for RGB input. V4L2_DV_RANGE_AUTO
+ follows the RGB quantization range specified in the standard for the video interface
+ (ie. <xref linkend="cea861" /> for HDMI). V4L2_DV_RANGE_LIMITED and V4L2_DV_RANGE_FULL override the standard
+ to be compatible with sources that have not implemented the standard correctly
+ (unfortunately quite common for HDMI and DVI-D). Full range allows all possible values to be
+ used whereas limited range sets the range to (16 &lt;&lt; (N-8)) - (235 &lt;&lt; (N-8))
+ where N is the number of bits per component.
+ This control is applicable to VGA, DVI-A/D, HDMI and DisplayPort connectors.
+ </entry>
+ </row>
+ <row><entry></entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </section>
</section>
diff --git a/Documentation/DocBook/media/v4l/dev-osd.xml b/Documentation/DocBook/media/v4l/dev-osd.xml
index 479d9433869a..dd91d6134e8c 100644
--- a/Documentation/DocBook/media/v4l/dev-osd.xml
+++ b/Documentation/DocBook/media/v4l/dev-osd.xml
@@ -1,13 +1,6 @@
<title>Video Output Overlay Interface</title>
<subtitle>Also known as On-Screen Display (OSD)</subtitle>
- <note>
- <title>Experimental</title>
-
- <para>This is an <link linkend="experimental">experimental</link>
-interface and may change in the future.</para>
- </note>
-
<para>Some video output devices can overlay a framebuffer image onto
the outgoing video signal. Applications can set up such an overlay
using this interface, which borrows structures and ioctls of the <link
diff --git a/Documentation/DocBook/media/v4l/dev-rds.xml b/Documentation/DocBook/media/v4l/dev-rds.xml
index 38883a419e65..be2f33737323 100644
--- a/Documentation/DocBook/media/v4l/dev-rds.xml
+++ b/Documentation/DocBook/media/v4l/dev-rds.xml
@@ -6,7 +6,7 @@ information, on an inaudible audio subcarrier of a radio program. This
interface is aimed at devices capable of receiving and/or transmitting RDS
information.</para>
- <para>For more information see the core RDS standard <xref linkend="en50067" />
+ <para>For more information see the core RDS standard <xref linkend="iec62106" />
and the RBDS standard <xref linkend="nrsc4" />.</para>
<para>Note that the RBDS standard as is used in the USA is almost identical
diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml
index a3d9dd093268..d15aaf83f56f 100644
--- a/Documentation/DocBook/media/v4l/dev-subdev.xml
+++ b/Documentation/DocBook/media/v4l/dev-subdev.xml
@@ -374,29 +374,29 @@
rectangle --- if it is supported by the hardware.</para>
<orderedlist>
- <listitem>Sink pad format. The user configures the sink pad
+ <listitem><para>Sink pad format. The user configures the sink pad
format. This format defines the parameters of the image the
- entity receives through the pad for further processing.</listitem>
+ entity receives through the pad for further processing.</para></listitem>
- <listitem>Sink pad actual crop selection. The sink pad crop
- defines the crop performed to the sink pad format.</listitem>
+ <listitem><para>Sink pad actual crop selection. The sink pad crop
+ defines the crop performed to the sink pad format.</para></listitem>
- <listitem>Sink pad actual compose selection. The size of the
+ <listitem><para>Sink pad actual compose selection. The size of the
sink pad compose rectangle defines the scaling ratio compared
to the size of the sink pad crop rectangle. The location of
the compose rectangle specifies the location of the actual
sink compose rectangle in the sink compose bounds
- rectangle.</listitem>
+ rectangle.</para></listitem>
- <listitem>Source pad actual crop selection. Crop on the source
+ <listitem><para>Source pad actual crop selection. Crop on the source
pad defines crop performed to the image in the sink compose
- bounds rectangle.</listitem>
+ bounds rectangle.</para></listitem>
- <listitem>Source pad format. The source pad format defines the
+ <listitem><para>Source pad format. The source pad format defines the
output pixel format of the subdev, as well as the other
parameters with the exception of the image width and height.
Width and height are defined by the size of the source pad
- actual crop selection.</listitem>
+ actual crop selection.</para></listitem>
</orderedlist>
<para>Accessing any of the above rectangles not supported by the
diff --git a/Documentation/DocBook/media/v4l/gen-errors.xml b/Documentation/DocBook/media/v4l/gen-errors.xml
index 5bbf3ce1973a..7e29a4e1f696 100644
--- a/Documentation/DocBook/media/v4l/gen-errors.xml
+++ b/Documentation/DocBook/media/v4l/gen-errors.xml
@@ -7,6 +7,15 @@
<tbody valign="top">
<!-- Keep it ordered alphabetically -->
<row>
+ <entry>EAGAIN (aka EWOULDBLOCK)</entry>
+ <entry>The ioctl can't be handled because the device is in state where
+ it can't perform it. This could happen for example in case where
+ device is sleeping and ioctl is performed to query statistics.
+ It is also returned when the ioctl would need to wait
+ for an event, but the device was opened in non-blocking mode.
+ </entry>
+ </row>
+ <row>
<entry>EBADF</entry>
<entry>The file descriptor is not a valid.</entry>
</row>
@@ -51,21 +60,11 @@
for periodic transfers (up to 80% of the USB bandwidth).</entry>
</row>
<row>
- <entry>ENOSYS or EOPNOTSUPP</entry>
- <entry>Function not available for this device (dvb API only. Will likely
- be replaced anytime soon by ENOTTY).</entry>
- </row>
- <row>
<entry>EPERM</entry>
<entry>Permission denied. Can be returned if the device needs write
permission, or some special capabilities is needed
(e. g. root)</entry>
</row>
- <row>
- <entry>EWOULDBLOCK</entry>
- <entry>Operation would block. Used when the ioctl would need to wait
- for an event, but the device was opened in non-blocking mode.</entry>
- </row>
</tbody>
</tgroup>
</table>
diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml
index 1885cc0755cb..97f785add841 100644
--- a/Documentation/DocBook/media/v4l/io.xml
+++ b/Documentation/DocBook/media/v4l/io.xml
@@ -613,8 +613,8 @@ field is independent of the <structfield>timestamp</structfield> and
<entry>__u32</entry>
<entry><structfield>sequence</structfield></entry>
<entry></entry>
- <entry>Set by the driver, counting the frames in the
-sequence.</entry>
+ <entry>Set by the driver, counting the frames (not fields!) in
+sequence. This field is set for both input and output devices.</entry>
</row>
<row>
<entry spanname="hspan"><para>In <link
@@ -685,18 +685,14 @@ memory, set by the application. See <xref linkend="userp" /> for details.
<entry>__u32</entry>
<entry><structfield>reserved2</structfield></entry>
<entry></entry>
- <entry>A place holder for future extensions and custom
-(driver defined) buffer types
-<constant>V4L2_BUF_TYPE_PRIVATE</constant> and higher. Applications
+ <entry>A place holder for future extensions. Applications
should set this to 0.</entry>
</row>
<row>
<entry>__u32</entry>
<entry><structfield>reserved</structfield></entry>
<entry></entry>
- <entry>A place holder for future extensions and custom
-(driver defined) buffer types
-<constant>V4L2_BUF_TYPE_PRIVATE</constant> and higher. Applications
+ <entry>A place holder for future extensions. Applications
should set this to 0.</entry>
</row>
</tbody>
@@ -827,14 +823,7 @@ should set this to 0.</entry>
<entry><constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY</constant></entry>
<entry>8</entry>
<entry>Buffer for video output overlay (OSD), see <xref
- linkend="osd" />. Status: <link
-linkend="experimental">Experimental</link>.</entry>
- </row>
- <row>
- <entry><constant>V4L2_BUF_TYPE_PRIVATE</constant></entry>
- <entry>0x80</entry>
- <entry>This and higher values are reserved for custom
-(driver defined) buffer types.</entry>
+ linkend="osd" />.</entry>
</row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml
index 8eace3e2e7d4..2d3f0b1aefe0 100644
--- a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml
@@ -22,8 +22,7 @@
with 10 bits per colour compressed to 8 bits each, using DPCM
compression. DPCM, differential pulse-code modulation, is lossy.
Each colour component consumes 8 bits of memory. In other respects
- this format is similar to <xref
- linkend="pixfmt-srggb10">.</xref></para>
+ this format is similar to <xref linkend="pixfmt-srggb10" />.</para>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-yvu420m.xml b/Documentation/DocBook/media/v4l/pixfmt-yvu420m.xml
new file mode 100644
index 000000000000..2330667907c7
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-yvu420m.xml
@@ -0,0 +1,154 @@
+ <refentry id="V4L2-PIX-FMT-YVU420M">
+ <refmeta>
+ <refentrytitle>V4L2_PIX_FMT_YVU420M ('YM21')</refentrytitle>
+ &manvol;
+ </refmeta>
+ <refnamediv>
+ <refname> <constant>V4L2_PIX_FMT_YVU420M</constant></refname>
+ <refpurpose>Variation of <constant>V4L2_PIX_FMT_YVU420</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 Cr data
+constitutes the second plane which is half the width and half
+the height of the Y plane (and of the image). Each Cr belongs to four
+pixels, a two-by-two square of the image. For example,
+Cr<subscript>0</subscript> belongs to Y'<subscript>00</subscript>,
+Y'<subscript>01</subscript>, Y'<subscript>10</subscript>, and
+Y'<subscript>11</subscript>. The Cb data, just like the Cr plane, constitutes
+the third plane. </para>
+
+ <para>If the Y plane has pad bytes after each row, then the Cr
+and Cb 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_YVU420M</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>Cr<subscript>00</subscript></entry>
+ <entry>Cr<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start1&nbsp;+&nbsp;2:</entry>
+ <entry>Cr<subscript>10</subscript></entry>
+ <entry>Cr<subscript>11</subscript></entry>
+ </row>
+ <row><entry></entry></row>
+ <row>
+ <entry>start2&nbsp;+&nbsp;0:</entry>
+ <entry>Cb<subscript>00</subscript></entry>
+ <entry>Cb<subscript>01</subscript></entry>
+ </row>
+ <row>
+ <entry>start2&nbsp;+&nbsp;2:</entry>
+ <entry>Cb<subscript>10</subscript></entry>
+ <entry>Cb<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>
diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml
index e58934c92895..1ddbfabe3195 100644
--- a/Documentation/DocBook/media/v4l/pixfmt.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt.xml
@@ -708,6 +708,7 @@ information.</para>
&sub-y41p;
&sub-yuv420;
&sub-yuv420m;
+ &sub-yvu420m;
&sub-yuv410;
&sub-yuv422p;
&sub-yuv411p;
diff --git a/Documentation/DocBook/media/v4l/selection-api.xml b/Documentation/DocBook/media/v4l/selection-api.xml
index e7ed5077834d..4c238ce068b0 100644
--- a/Documentation/DocBook/media/v4l/selection-api.xml
+++ b/Documentation/DocBook/media/v4l/selection-api.xml
@@ -40,6 +40,7 @@ cropping and composing rectangles have the same size.</para>
<section>
<title>Selection targets</title>
+ <para>
<figure id="sel-targets-capture">
<title>Cropping and composing targets</title>
<mediaobject>
@@ -52,12 +53,12 @@ cropping and composing rectangles have the same size.</para>
</textobject>
</mediaobject>
</figure>
+ </para>
+ <para>See <xref linkend="v4l2-selection-targets" /> for more
+ information.</para>
</section>
- See <xref linkend="v4l2-selection-targets" /> for more
- information.
-
<section>
<title>Configuration</title>
@@ -216,18 +217,17 @@ composing and cropping operations by setting the appropriate targets. The V4L2
API lacks any support for composing to and cropping from an image inside a
memory buffer. The application could configure a capture device to fill only a
part of an image by abusing V4L2 API. Cropping a smaller image from a larger
-one is achieved by setting the field <structfield>
-&v4l2-pix-format;::bytesperline </structfield>. Introducing an image offsets
-could be done by modifying field <structfield> &v4l2-buffer;::m:userptr
-</structfield> before calling <constant> VIDIOC_QBUF </constant>. Those
+one is achieved by setting the field
+&v4l2-pix-format;<structfield>::bytesperline</structfield>. Introducing an image offsets
+could be done by modifying field &v4l2-buffer;<structfield>::m_userptr</structfield>
+before calling <constant> VIDIOC_QBUF </constant>. Those
operations should be avoided because they are not portable (endianness), and do
not work for macroblock and Bayer formats and mmap buffers. The selection API
deals with configuration of buffer cropping/composing in a clear, intuitive and
portable way. Next, with the selection API the concepts of the padded target
-and constraints flags are introduced. Finally, <structname> &v4l2-crop;
-</structname> and <structname> &v4l2-cropcap; </structname> have no reserved
-fields. Therefore there is no way to extend their functionality. The new
-<structname> &v4l2-selection; </structname> provides a lot of place for future
+and constraints flags are introduced. Finally, &v4l2-crop; and &v4l2-cropcap;
+have no reserved fields. Therefore there is no way to extend their functionality.
+The new &v4l2-selection; provides a lot of place for future
extensions. Driver developers are encouraged to implement only selection API.
The former cropping API would be simulated using the new one. </para>
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index eee6908c749f..10ccde9d16d0 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -145,9 +145,12 @@ applications. -->
<authorinitials>hv</authorinitials>
<revremark>Added VIDIOC_ENUM_FREQ_BANDS.
</revremark>
+ </revision>
+
+ <revision>
<revnumber>3.5</revnumber>
<date>2012-05-07</date>
- <authorinitials>sa, sn</authorinitials>
+ <authorinitials>sa, sn, hv</authorinitials>
<revremark>Added V4L2_CTRL_TYPE_INTEGER_MENU and V4L2 subdev
selections API. Improved the description of V4L2_CID_COLORFX
control, added V4L2_CID_COLORFX_CBCR control.
@@ -158,11 +161,8 @@ applications. -->
V4L2_CID_3A_LOCK, V4L2_CID_AUTO_FOCUS_START,
V4L2_CID_AUTO_FOCUS_STOP, V4L2_CID_AUTO_FOCUS_STATUS
and V4L2_CID_AUTO_FOCUS_RANGE.
- </revremark>
- <date>2012-05-01</date>
- <authorinitials>hv</authorinitials>
- <revremark>Added VIDIOC_ENUM_DV_TIMINGS, VIDIOC_QUERY_DV_TIMINGS and
- VIDIOC_DV_TIMINGS_CAP.
+ Added VIDIOC_ENUM_DV_TIMINGS, VIDIOC_QUERY_DV_TIMINGS and
+ VIDIOC_DV_TIMINGS_CAP.
</revremark>
</revision>
@@ -472,7 +472,7 @@ and discussions on the V4L mailing list.</revremark>
</partinfo>
<title>Video for Linux Two API Specification</title>
- <subtitle>Revision 3.5</subtitle>
+ <subtitle>Revision 3.6</subtitle>
<chapter id="common">
&sub-common;
@@ -581,6 +581,7 @@ and discussions on the V4L mailing list.</revremark>
&sub-subdev-enum-frame-size;
&sub-subdev-enum-mbus-code;
&sub-subdev-g-crop;
+ &sub-subdev-g-edid;
&sub-subdev-g-fmt;
&sub-subdev-g-frame-interval;
&sub-subdev-g-selection;
diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
index f1bac2c6e978..bf7cc979fdfa 100644
--- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml
@@ -59,6 +59,9 @@ constant except when switching the video standard. Remember this
switch can occur implicit when switching the video input or
output.</para>
+ <para>This ioctl must be implemented for video capture or output devices that
+support cropping and/or scaling and/or have non-square pixels, and for overlay devices.</para>
+
<table pgwide="1" frame="none" id="v4l2-cropcap">
<title>struct <structname>v4l2_cropcap</structname></title>
<tgroup cols="3">
@@ -70,10 +73,10 @@ output.</para>
<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_OVERLAY</constant>, and custom (driver
-defined) types with code <constant>V4L2_BUF_TYPE_PRIVATE</constant>
-and higher. See <xref linkend="v4l2-buf-type" />.</entry>
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant> and
+<constant>V4L2_BUF_TYPE_VIDEO_OVERLAY</constant>. See <xref linkend="v4l2-buf-type" />.</entry>
</row>
<row>
<entry>struct <link linkend="v4l2-rect-crop">v4l2_rect</link></entry>
@@ -156,8 +159,7 @@ on 22 Oct 2002 subject "Re:[V4L][patches!] Re:v4l2/kernel-2.5" -->
<term><errorcode>EINVAL</errorcode></term>
<listitem>
<para>The &v4l2-cropcap; <structfield>type</structfield> is
-invalid. This is not permitted for video capture, output and overlay devices,
-which must support <constant>VIDIOC_CROPCAP</constant>.</para>
+invalid.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml b/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml
index 74b87f6e480a..9215627b04c7 100644
--- a/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-decoder-cmd.xml
@@ -49,13 +49,6 @@
<refsect1>
<title>Description</title>
- <note>
- <title>Experimental</title>
-
- <para>This is an <link linkend="experimental">experimental</link>
-interface and may change in the future.</para>
- </note>
-
<para>These ioctls control an audio/video (usually MPEG-) decoder.
<constant>VIDIOC_DECODER_CMD</constant> sends a command to the
decoder, <constant>VIDIOC_TRY_DECODER_CMD</constant> can be used to
diff --git a/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
index f431b3ba79bd..0619ca5d2d36 100644
--- a/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-encoder-cmd.xml
@@ -49,13 +49,6 @@
<refsect1>
<title>Description</title>
- <note>
- <title>Experimental</title>
-
- <para>This is an <link linkend="experimental">experimental</link>
-interface and may change in the future.</para>
- </note>
-
<para>These ioctls control an audio/video (usually MPEG-) encoder.
<constant>VIDIOC_ENCODER_CMD</constant> sends a command to the
encoder, <constant>VIDIOC_TRY_ENCODER_CMD</constant> can be used to
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml
index 509f0012d2a6..fced5fb0dbf0 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml
@@ -229,6 +229,12 @@ intended for the user.</entry>
is out of bounds.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video presets are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml
index 24c3bf4fd29a..b3e17c1dfaf5 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml
@@ -106,6 +106,12 @@ application.</entry>
is out of bounds.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video presets are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml
index 81ebe48317fe..f8dfeed34fca 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml
@@ -58,6 +58,9 @@ structure. Drivers fill the rest of the structure or return an
incrementing by one until <errorcode>EINVAL</errorcode> is
returned.</para>
+ <para>Note that after switching input or output the list of enumerated image
+formats may be different.</para>
+
<table pgwide="1" frame="none" id="v4l2-fmtdesc">
<title>struct <structname>v4l2_fmtdesc</structname></title>
<tgroup cols="3">
@@ -78,10 +81,8 @@ 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. See <xref linkend="v4l2-buf-type" />.</entry>
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE</constant> and
+<constant>V4L2_BUF_TYPE_VIDEO_OVERLAY</constant>. See <xref linkend="v4l2-buf-type" />.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml b/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
index f77a13f486d7..a78454b5abcd 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enum-framesizes.xml
@@ -50,13 +50,6 @@ and pixel format and receives a frame width and height.</para>
<refsect1>
<title>Description</title>
- <note>
- <title>Experimental</title>
-
- <para>This is an <link linkend="experimental">experimental</link>
-interface and may change in the future.</para>
- </note>
-
<para>This ioctl allows applications to enumerate all frame sizes
(&ie; width and height in pixels) that the device supports for the
given pixel format.</para>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml
index 46d5a044a537..3c9a81305ad4 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml
@@ -283,7 +283,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009.
<entry>This input supports setting DV presets by using VIDIOC_S_DV_PRESET.</entry>
</row>
<row>
- <entry><constant>V4L2_IN_CAP_CUSTOM_TIMINGS</constant></entry>
+ <entry><constant>V4L2_IN_CAP_DV_TIMINGS</constant></entry>
<entry>0x00000002</entry>
<entry>This input supports setting video timings by using VIDIOC_S_DV_TIMINGS.</entry>
</row>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml
index 428020000ef0..f4ab0798545d 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml
@@ -168,7 +168,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009.
<entry>This output supports setting DV presets by using VIDIOC_S_DV_PRESET.</entry>
</row>
<row>
- <entry><constant>V4L2_OUT_CAP_CUSTOM_TIMINGS</constant></entry>
+ <entry><constant>V4L2_OUT_CAP_DV_TIMINGS</constant></entry>
<entry>0x00000002</entry>
<entry>This output supports setting video timings by using VIDIOC_S_DV_TIMINGS.</entry>
</row>
diff --git a/Documentation/DocBook/media/v4l/vidioc-enumstd.xml b/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
index 3a5fc5405f96..8065099401d1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-enumstd.xml
@@ -378,6 +378,12 @@ system)</para></footnote></para></entry>
is out of bounds.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Standard video timings are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml
index c4ff3b1887fb..75c6a93de3c1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml
@@ -104,10 +104,8 @@ changed and <constant>VIDIOC_S_CROP</constant> returns the
<entry><structfield>type</structfield></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_OUTPUT</constant>,
-<constant>V4L2_BUF_TYPE_VIDEO_OVERLAY</constant>, and custom (driver
-defined) types with code <constant>V4L2_BUF_TYPE_PRIVATE</constant>
-and higher. See <xref linkend="v4l2-buf-type" />.</entry>
+<constant>V4L2_BUF_TYPE_VIDEO_OUTPUT</constant> and
+<constant>V4L2_BUF_TYPE_VIDEO_OVERLAY</constant>. See <xref linkend="v4l2-buf-type" />.</entry>
</row>
<row>
<entry>&v4l2-rect;</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
index 61be9fa3803a..b9ea37634f6c 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml
@@ -78,6 +78,12 @@ If the preset is not supported, it returns an &EINVAL; </para>
</listitem>
</varlistentry>
<varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video presets are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><errorcode>EBUSY</errorcode></term>
<listitem>
<para>The device is busy and therefore can not change the preset.</para>
@@ -104,7 +110,4 @@ If the preset is not supported, it returns an &EINVAL; </para>
</tgroup>
</table>
</refsect1>
- <refsect1>
- &return-value;
- </refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
index eda1a2991bbe..72369707bd77 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml
@@ -56,7 +56,9 @@ a pointer to the &v4l2-dv-timings; structure as argument. If the ioctl is not su
or the timing values are not correct, the driver returns &EINVAL;.</para>
<para>The <filename>linux/v4l2-dv-timings.h</filename> header can be used to get the
timings of the formats in the <xref linkend="cea861" /> and <xref linkend="vesadmt" />
-standards.</para>
+standards. If the current input or output does not support DV timings (e.g. if
+&VIDIOC-ENUMINPUT; does not set the <constant>V4L2_IN_CAP_DV_TIMINGS</constant> flag), then
+&ENODATA; is returned.</para>
</refsect1>
<refsect1>
@@ -71,6 +73,12 @@ standards.</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video timings are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><errorcode>EBUSY</errorcode></term>
<listitem>
<para>The device is busy and therefore can not change the timings.</para>
@@ -320,7 +328,4 @@ detected or used depends on the hardware.
</tgroup>
</table>
</refsect1>
- <refsect1>
- &return-value;
- </refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml b/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
index 2aef02c9044e..be25029a16f1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-enc-index.xml
@@ -48,13 +48,6 @@
<refsect1>
<title>Description</title>
- <note>
- <title>Experimental</title>
-
- <para>This is an <link linkend="experimental">experimental</link>
-interface and may change in the future.</para>
- </note>
-
<para>The <constant>VIDIOC_G_ENC_INDEX</constant> ioctl provides
meta data about a compressed video stream the same or another
application currently reads from the driver, which is useful for
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
index 52acff193a6f..ee8f56e1bac0 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml
@@ -81,7 +81,7 @@ the application calls the <constant>VIDIOC_S_FMT</constant> ioctl
with a pointer to a <structname>v4l2_format</structname> structure
the driver checks
and adjusts the parameters against hardware abilities. Drivers
-should not return an error code unless the input is ambiguous, this is
+should not return an error code unless the <structfield>type</structfield> field is invalid, this is
a mechanism to fathom device capabilities and to approach parameters
acceptable for both the application and driver. On success the driver
may program the hardware, allocate resources and generally prepare for
@@ -107,6 +107,10 @@ disabling I/O or possibly time consuming hardware preparations.
Although strongly recommended drivers are not required to implement
this ioctl.</para>
+ <para>The format as returned by <constant>VIDIOC_TRY_FMT</constant>
+must be identical to what <constant>VIDIOC_S_FMT</constant> returns for
+the same input or output.</para>
+
<table pgwide="1" frame="none" id="v4l2-format">
<title>struct <structname>v4l2_format</structname></title>
<tgroup cols="4">
@@ -170,9 +174,7 @@ capture and output devices.</entry>
<entry></entry>
<entry>__u8</entry>
<entry><structfield>raw_data</structfield>[200]</entry>
- <entry>Place holder for future extensions and custom
-(driver defined) formats with <structfield>type</structfield>
-<constant>V4L2_BUF_TYPE_PRIVATE</constant> and higher.</entry>
+ <entry>Place holder for future extensions.</entry>
</row>
</tbody>
</tgroup>
@@ -187,8 +189,7 @@ capture and output devices.</entry>
<term><errorcode>EINVAL</errorcode></term>
<listitem>
<para>The &v4l2-format; <structfield>type</structfield>
-field is invalid, the requested buffer type not supported, or the
-format is not supported with this buffer type.</para>
+field is invalid or the requested buffer type not supported.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml
index f83d2cdd1185..9058224d1bbf 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml
@@ -108,9 +108,7 @@ devices.</para>
<entry></entry>
<entry>__u8</entry>
<entry><structfield>raw_data</structfield>[200]</entry>
- <entry>A place holder for future extensions and custom
-(driver defined) buffer types <constant>V4L2_BUF_TYPE_PRIVATE</constant> and
-higher.</entry>
+ <entry>A place holder for future extensions.</entry>
</row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
index f76d8a6d9b92..b11ec75e21a1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-selection.xml
@@ -152,12 +152,10 @@ satisfactory parameters have been negotiated. If constraints flags have to be
violated at then ERANGE is returned. The error indicates that <emphasis> there
exist no rectangle </emphasis> that satisfies the constraints.</para>
- </refsect1>
-
<para>Selection targets and flags are documented in <xref
linkend="v4l2-selections-common"/>.</para>
- <section>
+ <para>
<figure id="sel-const-adjust">
<title>Size adjustments with constraint flags.</title>
<mediaobject>
@@ -170,9 +168,9 @@ exist no rectangle </emphasis> that satisfies the constraints.</para>
</textobject>
</mediaobject>
</figure>
- </section>
+ </para>
- <refsect1>
+ <para>
<table pgwide="1" frame="none" id="v4l2-selection">
<title>struct <structname>v4l2_selection</structname></title>
<tgroup cols="3">
@@ -208,6 +206,7 @@ exist no rectangle </emphasis> that satisfies the constraints.</para>
</tbody>
</tgroup>
</table>
+ </para>
</refsect1>
<refsect1>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-std.xml b/Documentation/DocBook/media/v4l/vidioc-g-std.xml
index 99ff1a016220..4a898417de28 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-std.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-std.xml
@@ -72,7 +72,9 @@ flags, being a write-only ioctl it does not return the actual new standard as
the current input does not support the requested standard the driver
returns an &EINVAL;. When the standard set is ambiguous drivers may
return <errorcode>EINVAL</errorcode> or choose any of the requested
-standards.</para>
+standards. If the current input or output does not support standard video timings (e.g. if
+&VIDIOC-ENUMINPUT; does not set the <constant>V4L2_IN_CAP_STD</constant> flag), then
+&ENODATA; is returned.</para>
</refsect1>
<refsect1>
@@ -85,6 +87,12 @@ standards.</para>
<para>The <constant>VIDIOC_S_STD</constant> parameter was unsuitable.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Standard video timings are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
index 701138f1209d..6cc82010c736 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
@@ -354,6 +354,12 @@ radio tuners.</entry>
<entry>The &VIDIOC-ENUM-FREQ-BANDS; ioctl can be used to enumerate
the available frequency bands.</entry>
</row>
+ <row>
+ <entry><constant>V4L2_TUNER_CAP_HWSEEK_PROG_LIM</constant></entry>
+ <entry>0x0800</entry>
+ <entry>The range to search when using the hardware seek functionality
+ is programmable, see &VIDIOC-S-HW-FREQ-SEEK; for details.</entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml
index 77ff5be0809d..6a821a65a5ae 100644
--- a/Documentation/DocBook/media/v4l/vidioc-qbuf.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-qbuf.xml
@@ -155,6 +155,8 @@ or no buffers have been allocated yet, or the
<structfield>userptr</structfield> or
<structfield>length</structfield> are invalid.</para>
</listitem>
+ </varlistentry>
+ <varlistentry>
<term><errorcode>EIO</errorcode></term>
<listitem>
<para><constant>VIDIOC_DQBUF</constant> failed due to an
diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
index 1bc8aeb3ff1f..68b49d09e245 100644
--- a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml
@@ -65,5 +65,14 @@ returned.</para>
<refsect1>
&return-value;
+
+ <variablelist>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video presets are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml
index 44935a0ffcf0..e185f149e0a1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml
@@ -78,6 +78,12 @@ capabilities in order to give more precise feedback to the user.
<variablelist>
<varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Digital video timings are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><errorcode>ENOLINK</errorcode></term>
<listitem>
<para>No timings could be detected because no signal was found.
diff --git a/Documentation/DocBook/media/v4l/vidioc-querycap.xml b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
index f33dd746b66b..4c70215ae03f 100644
--- a/Documentation/DocBook/media/v4l/vidioc-querycap.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querycap.xml
@@ -90,11 +90,13 @@ ambiguities.</entry>
<entry>__u8</entry>
<entry><structfield>bus_info</structfield>[32]</entry>
<entry>Location of the device in the system, a
-NUL-terminated ASCII string. For example: "PCI Slot 4". This
+NUL-terminated ASCII string. For example: "PCI:0000:05:06.0". This
information is intended for users, to distinguish multiple
-identical devices. If no such information is available the field may
-simply count the devices controlled by the driver, or contain the
-empty string (<structfield>bus_info</structfield>[0] = 0).<!-- XXX pci_dev->slot_name example --></entry>
+identical devices. If no such information is available the field must
+simply count the devices controlled by the driver ("platform:vivi-000").
+The bus_info must start with "PCI:" for PCI boards, "PCIe:" for PCI Express boards,
+"usb-" for USB devices, "I2C:" for i2c devices, "ISA:" for ISA devices,
+"parport" for parallel port devices and "platform:" for platform devices.</entry>
</row>
<row>
<entry>__u32</entry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-querystd.xml b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
index 4b79c7c04ed6..fe80a183d957 100644
--- a/Documentation/DocBook/media/v4l/vidioc-querystd.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-querystd.xml
@@ -62,5 +62,13 @@ current video input or output.</para>
<refsect1>
&return-value;
+ <variablelist>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>Standard video timings are not supported for this input or output.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
</refsect1>
</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
index d7c95057bc51..2b50ef2007f3 100644
--- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml
@@ -109,9 +109,8 @@ as the &v4l2-format; <structfield>type</structfield> field. See <xref
<row>
<entry>__u32</entry>
<entry><structfield>reserved</structfield>[2]</entry>
- <entry>A place holder for future extensions and custom
-(driver defined) buffer types <constant>V4L2_BUF_TYPE_PRIVATE</constant> and
-higher. This array should be zeroed by applications.</entry>
+ <entry>A place holder for future extensions. This array should
+be zeroed by applications.</entry>
</row>
</tbody>
</tgroup>
diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
index 3dd1bec6d3c7..5b379e752194 100644
--- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml
@@ -75,6 +75,9 @@ seek is started.</para>
<para>This ioctl is supported if the <constant>V4L2_CAP_HW_FREQ_SEEK</constant> capability is set.</para>
+ <para>If this ioctl is called from a non-blocking filehandle, then &EAGAIN; is
+ returned and no seek takes place.</para>
+
<table pgwide="1" frame="none" id="v4l2-hw-freq-seek">
<title>struct <structname>v4l2_hw_freq_seek</structname></title>
<tgroup cols="3">
@@ -158,6 +161,13 @@ fields is wrong.</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term><errorcode>EAGAIN</errorcode></term>
+ <listitem>
+ <para>Attempted to call <constant>VIDIOC_S_HW_FREQ_SEEK</constant>
+ with the filehandle in non-blocking mode.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><errorcode>ENODATA</errorcode></term>
<listitem>
<para>The hardware seek found no channels.</para>
diff --git a/Documentation/DocBook/media/v4l/vidioc-streamon.xml b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
index 81cca4569050..716ea15e54a1 100644
--- a/Documentation/DocBook/media/v4l/vidioc-streamon.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-streamon.xml
@@ -74,7 +74,12 @@ not transmitted yet. I/O returns to the same state as after calling
stream type. This is the same as &v4l2-requestbuffers;
<structfield>type</structfield>.</para>
- <para>Note applications can be preempted for unknown periods right
+ <para>If <constant>VIDIOC_STREAMON</constant> is called when streaming
+is already in progress, or if <constant>VIDIOC_STREAMOFF</constant> is called
+when streaming is already stopped, then the ioctl does nothing and 0 is
+returned.</para>
+
+ <para>Note that applications can be preempted for unknown periods right
before or after the <constant>VIDIOC_STREAMON</constant> or
<constant>VIDIOC_STREAMOFF</constant> calls, there is no notion of
starting or stopping "now". Buffer timestamps can be used to
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml
new file mode 100644
index 000000000000..bbd18f0e6ede
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-edid.xml
@@ -0,0 +1,152 @@
+<refentry id="vidioc-subdev-g-edid">
+ <refmeta>
+ <refentrytitle>ioctl VIDIOC_SUBDEV_G_EDID, VIDIOC_SUBDEV_S_EDID</refentrytitle>
+ &manvol;
+ </refmeta>
+
+ <refnamediv>
+ <refname>VIDIOC_SUBDEV_G_EDID</refname>
+ <refname>VIDIOC_SUBDEV_S_EDID</refname>
+ <refpurpose>Get or set the EDID of a video receiver/transmitter</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>ioctl</function></funcdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>int <parameter>request</parameter></paramdef>
+ <paramdef>struct v4l2_subdev_edid *<parameter>argp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>int <function>ioctl</function></funcdef>
+ <paramdef>int <parameter>fd</parameter></paramdef>
+ <paramdef>int <parameter>request</parameter></paramdef>
+ <paramdef>const struct v4l2_subdev_edid *<parameter>argp</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><parameter>fd</parameter></term>
+ <listitem>
+ <para>&fd;</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>request</parameter></term>
+ <listitem>
+ <para>VIDIOC_SUBDEV_G_EDID, VIDIOC_SUBDEV_S_EDID</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>argp</parameter></term>
+ <listitem>
+ <para></para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Description</title>
+ <para>These ioctls can be used to get or set an EDID associated with an input pad
+ from a receiver or an output pad of a transmitter subdevice.</para>
+
+ <para>To get the EDID data the application has to fill in the <structfield>pad</structfield>,
+ <structfield>start_block</structfield>, <structfield>blocks</structfield> and <structfield>edid</structfield>
+ fields and call <constant>VIDIOC_SUBDEV_G_EDID</constant>. The current EDID from block
+ <structfield>start_block</structfield> and of size <structfield>blocks</structfield>
+ will be placed in the memory <structfield>edid</structfield> points to. The <structfield>edid</structfield>
+ pointer must point to memory at least <structfield>blocks</structfield>&nbsp;*&nbsp;128 bytes
+ large (the size of one block is 128 bytes).</para>
+
+ <para>If there are fewer blocks than specified, then the driver will set <structfield>blocks</structfield>
+ to the actual number of blocks. If there are no EDID blocks available at all, then the error code
+ ENODATA is set.</para>
+
+ <para>If blocks have to be retrieved from the sink, then this call will block until they
+ have been read.</para>
+
+ <para>To set the EDID blocks of a receiver the application has to fill in the <structfield>pad</structfield>,
+ <structfield>blocks</structfield> and <structfield>edid</structfield> fields and set
+ <structfield>start_block</structfield> to 0. It is not possible to set part of an EDID,
+ it is always all or nothing. Setting the EDID data is only valid for receivers as it makes
+ no sense for a transmitter.</para>
+
+ <para>The driver assumes that the full EDID is passed in. If there are more EDID blocks than
+ the hardware can handle then the EDID is not written, but instead the error code E2BIG is set
+ and <structfield>blocks</structfield> is set to the maximum that the hardware supports.
+ If <structfield>start_block</structfield> is any
+ value other than 0 then the error code EINVAL is set.</para>
+
+ <para>To disable an EDID you set <structfield>blocks</structfield> to 0. Depending on the
+ hardware this will drive the hotplug pin low and/or block the source from reading the EDID
+ data in some way. In any case, the end result is the same: the EDID is no longer available.
+ </para>
+
+ <table pgwide="1" frame="none" id="v4l2-subdev-edid">
+ <title>struct <structname>v4l2_subdev_edid</structname></title>
+ <tgroup cols="3">
+ &cs-str;
+ <tbody valign="top">
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>pad</structfield></entry>
+ <entry>Pad for which to get/set the EDID blocks.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>start_block</structfield></entry>
+ <entry>Read the EDID from starting with this block. Must be 0 when setting
+ the EDID.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>blocks</structfield></entry>
+ <entry>The number of blocks to get or set. Must be less or equal to 256 (the
+ maximum number of blocks as defined by the standard). When you set the EDID and
+ <structfield>blocks</structfield> is 0, then the EDID is disabled or erased.</entry>
+ </row>
+ <row>
+ <entry>__u8&nbsp;*</entry>
+ <entry><structfield>edid</structfield></entry>
+ <entry>Pointer to memory that contains the EDID. The minimum size is
+ <structfield>blocks</structfield>&nbsp;*&nbsp;128.</entry>
+ </row>
+ <row>
+ <entry>__u32</entry>
+ <entry><structfield>reserved</structfield>[5]</entry>
+ <entry>Reserved for future extensions. Applications and drivers must
+ set the array to zero.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </refsect1>
+
+ <refsect1>
+ &return-value;
+
+ <variablelist>
+ <varlistentry>
+ <term><errorcode>ENODATA</errorcode></term>
+ <listitem>
+ <para>The EDID data is not available.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorcode>E2BIG</errorcode></term>
+ <listitem>
+ <para>The EDID data you provided is more than the hardware can handle.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml
index f33cc814a01d..1ba9e999af3f 100644
--- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml
@@ -69,23 +69,22 @@
more information on how each selection target affects the image
processing pipeline inside the subdevice.</para>
- <section>
+ <refsect2>
<title>Types of selection targets</title>
<para>There are two types of selection targets: actual and bounds. The
actual targets are the targets which configure the hardware. The BOUNDS
target will return a rectangle that contain all possible actual
rectangles.</para>
- </section>
+ </refsect2>
- <section>
+ <refsect2>
<title>Discovering supported features</title>
<para>To discover which targets are supported, the user can
perform <constant>VIDIOC_SUBDEV_G_SELECTION</constant> on them.
Any unsupported target will return
<constant>EINVAL</constant>.</para>
- </section>
<para>Selection targets and flags are documented in <xref
linkend="v4l2-selections-common"/>.</para>
@@ -132,6 +131,7 @@
</tbody>
</tgroup>
</table>
+ </refsect2>
</refsect1>
diff --git a/Documentation/DocBook/media_api.tmpl b/Documentation/DocBook/media_api.tmpl
index 4e8e8985cc17..f2413acfe241 100644
--- a/Documentation/DocBook/media_api.tmpl
+++ b/Documentation/DocBook/media_api.tmpl
@@ -29,7 +29,7 @@
<title>LINUX MEDIA INFRASTRUCTURE API</title>
<copyright>
- <year>2009-2011</year>
+ <year>2009-2012</year>
<holder>LinuxTV Developers</holder>
</copyright>
@@ -53,7 +53,7 @@ Foundation. A copy of the license is included in the chapter entitled
video and radio straming devices, including video cameras,
analog and digital TV receiver cards, AM/FM receiver cards,
streaming capture devices.</para>
- <para>It is divided into three parts.</para>
+ <para>It is divided into four parts.</para>
<para>The first part covers radio, capture,
cameras and analog TV devices.</para>
<para>The second part covers the
@@ -62,7 +62,8 @@ Foundation. A copy of the license is included in the chapter entitled
in fact it covers several different video standards including
DVB-T, DVB-S, DVB-C and ATSC. The API is currently being updated
to documment support also for DVB-S2, ISDB-T and ISDB-S.</para>
- <para>The third part covers Remote Controller API</para>
+ <para>The third part covers the Remote Controller API.</para>
+ <para>The fourth part covers the Media Controller API.</para>
<para>For additional information and for the latest development code,
see: <ulink url="http://linuxtv.org">http://linuxtv.org</ulink>.</para>
<para>For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: <ulink url="http://vger.kernel.org/vger-lists.html#linux-media">Linux Media Mailing List (LMML).</ulink>.</para>
@@ -87,7 +88,7 @@ Foundation. A copy of the license is included in the chapter entitled
</author>
</authorgroup>
<copyright>
- <year>2009-2011</year>
+ <year>2009-2012</year>
<holder>Mauro Carvalho Chehab</holder>
</copyright>
diff --git a/Documentation/IRQ-domain.txt b/Documentation/IRQ-domain.txt
index 27dcaabfb4db..1401cece745a 100644
--- a/Documentation/IRQ-domain.txt
+++ b/Documentation/IRQ-domain.txt
@@ -93,6 +93,7 @@ Linux IRQ number into the hardware.
Most drivers cannot use this mapping.
==== Legacy ====
+irq_domain_add_simple()
irq_domain_add_legacy()
irq_domain_add_legacy_isa()
@@ -115,3 +116,7 @@ The legacy map should only be used if fixed IRQ mappings must be
supported. For example, ISA controllers would use the legacy map for
mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ
numbers.
+
+Most users of legacy mappings should use irq_domain_add_simple() which
+will use a legacy domain only if an IRQ range is supplied by the
+system and will otherwise use a linear domain mapping.
diff --git a/Documentation/block/00-INDEX b/Documentation/block/00-INDEX
index d111e3b23db0..d18ecd827c40 100644
--- a/Documentation/block/00-INDEX
+++ b/Documentation/block/00-INDEX
@@ -3,15 +3,21 @@
biodoc.txt
- Notes on the Generic Block Layer Rewrite in Linux 2.5
capability.txt
- - Generic Block Device Capability (/sys/block/<disk>/capability)
+ - Generic Block Device Capability (/sys/block/<device>/capability)
+cfq-iosched.txt
+ - CFQ IO scheduler tunables
+data-integrity.txt
+ - Block data integrity
deadline-iosched.txt
- Deadline IO scheduler tunables
ioprio.txt
- Block io priorities (in CFQ scheduler)
+queue-sysfs.txt
+ - Queue's sysfs entries
request.txt
- The members of struct request (in include/linux/blkdev.h)
stat.txt
- - Block layer statistics in /sys/block/<dev>/stat
+ - Block layer statistics in /sys/block/<device>/stat
switching-sched.txt
- Switching I/O schedulers at runtime
writeback_cache_control.txt
diff --git a/Documentation/block/cfq-iosched.txt b/Documentation/block/cfq-iosched.txt
index 6d670f570451..d89b4fe724d7 100644
--- a/Documentation/block/cfq-iosched.txt
+++ b/Documentation/block/cfq-iosched.txt
@@ -1,3 +1,14 @@
+CFQ (Complete Fairness Queueing)
+===============================
+
+The main aim of CFQ scheduler is to provide a fair allocation of the disk
+I/O bandwidth for all the processes which requests an I/O operation.
+
+CFQ maintains the per process queue for the processes which request I/O
+operation(syncronous requests). In case of asynchronous requests, all the
+requests from all the processes are batched together according to their
+process's I/O priority.
+
CFQ ioscheduler tunables
========================
@@ -25,6 +36,72 @@ there are multiple spindles behind single LUN (Host based hardware RAID
controller or for storage arrays), setting slice_idle=0 might end up in better
throughput and acceptable latencies.
+back_seek_max
+-------------
+This specifies, given in Kbytes, the maximum "distance" for backward seeking.
+The distance is the amount of space from the current head location to the
+sectors that are backward in terms of distance.
+
+This parameter allows the scheduler to anticipate requests in the "backward"
+direction and consider them as being the "next" if they are within this
+distance from the current head location.
+
+back_seek_penalty
+-----------------
+This parameter is used to compute the cost of backward seeking. If the
+backward distance of request is just 1/back_seek_penalty from a "front"
+request, then the seeking cost of two requests is considered equivalent.
+
+So scheduler will not bias toward one or the other request (otherwise scheduler
+will bias toward front request). Default value of back_seek_penalty is 2.
+
+fifo_expire_async
+-----------------
+This parameter is used to set the timeout of asynchronous requests. Default
+value of this is 248ms.
+
+fifo_expire_sync
+----------------
+This parameter is used to set the timeout of synchronous requests. Default
+value of this is 124ms. In case to favor synchronous requests over asynchronous
+one, this value should be decreased relative to fifo_expire_async.
+
+slice_async
+-----------
+This parameter is same as of slice_sync but for asynchronous queue. The
+default value is 40ms.
+
+slice_async_rq
+--------------
+This parameter is used to limit the dispatching of asynchronous request to
+device request queue in queue's slice time. The maximum number of request that
+are allowed to be dispatched also depends upon the io priority. Default value
+for this is 2.
+
+slice_sync
+----------
+When a queue is selected for execution, the queues IO requests are only
+executed for a certain amount of time(time_slice) before switching to another
+queue. This parameter is used to calculate the time slice of synchronous
+queue.
+
+time_slice is computed using the below equation:-
+time_slice = slice_sync + (slice_sync/5 * (4 - prio)). To increase the
+time_slice of synchronous queue, increase the value of slice_sync. Default
+value is 100ms.
+
+quantum
+-------
+This specifies the number of request dispatched to the device queue. In a
+queue's time slice, a request will not be dispatched if the number of request
+in the device exceeds this parameter. This parameter is used for synchronous
+request.
+
+In case of storage with several disk, this setting can limit the parallel
+processing of request. Therefore, increasing the value can imporve the
+performace although this can cause the latency of some I/O to increase due
+to more number of requests.
+
CFQ IOPS Mode for group scheduling
===================================
Basic CFQ design is to provide priority based time slices. Higher priority
diff --git a/Documentation/block/queue-sysfs.txt b/Documentation/block/queue-sysfs.txt
index d8147b336c35..e54ac1d53403 100644
--- a/Documentation/block/queue-sysfs.txt
+++ b/Documentation/block/queue-sysfs.txt
@@ -9,20 +9,71 @@ These files are the ones found in the /sys/block/xxx/queue/ directory.
Files denoted with a RO postfix are readonly and the RW postfix means
read-write.
+add_random (RW)
+----------------
+This file allows to trun off the disk entropy contribution. Default
+value of this file is '1'(on).
+
+discard_granularity (RO)
+-----------------------
+This shows the size of internal allocation of the device in bytes, if
+reported by the device. A value of '0' means device does not support
+the discard functionality.
+
+discard_max_bytes (RO)
+----------------------
+Devices that support discard functionality may have internal limits on
+the number of bytes that can be trimmed or unmapped in a single operation.
+The discard_max_bytes parameter is set by the device driver to the maximum
+number of bytes that can be discarded in a single operation. Discard
+requests issued to the device must not exceed this limit. A discard_max_bytes
+value of 0 means that the device does not support discard functionality.
+
+discard_zeroes_data (RO)
+------------------------
+When read, this file will show if the discarded block are zeroed by the
+device or not. If its value is '1' the blocks are zeroed otherwise not.
+
hw_sector_size (RO)
-------------------
This is the hardware sector size of the device, in bytes.
+iostats (RW)
+-------------
+This file is used to control (on/off) the iostats accounting of the
+disk.
+
+logical_block_size (RO)
+-----------------------
+This is the logcal block size of the device, in bytes.
+
max_hw_sectors_kb (RO)
----------------------
This is the maximum number of kilobytes supported in a single data transfer.
+max_integrity_segments (RO)
+---------------------------
+When read, this file shows the max limit of integrity segments as
+set by block layer which a hardware controller can handle.
+
max_sectors_kb (RW)
-------------------
This is the maximum number of kilobytes that the block layer will allow
for a filesystem request. Must be smaller than or equal to the maximum
size allowed by the hardware.
+max_segments (RO)
+-----------------
+Maximum number of segments of the device.
+
+max_segment_size (RO)
+---------------------
+Maximum segment size of the device.
+
+minimum_io_size (RO)
+--------------------
+This is the smallest preferred io size reported by the device.
+
nomerges (RW)
-------------
This enables the user to disable the lookup logic involved with IO
@@ -38,11 +89,31 @@ read or write requests. Note that the total allocated number may be twice
this amount, since it applies only to reads or writes (not the accumulated
sum).
+To avoid priority inversion through request starvation, a request
+queue maintains a separate request pool per each cgroup when
+CONFIG_BLK_CGROUP is enabled, and this parameter applies to each such
+per-block-cgroup request pool. IOW, if there are N block cgroups,
+each request queue may have upto N request pools, each independently
+regulated by nr_requests.
+
+optimal_io_size (RO)
+--------------------
+This is the optimal io size reported by the device.
+
+physical_block_size (RO)
+------------------------
+This is the physical block size of device, in bytes.
+
read_ahead_kb (RW)
------------------
Maximum number of kilobytes to read-ahead for filesystems on this block
device.
+rotational (RW)
+---------------
+This file is used to stat if the device is of rotational type or
+non-rotational type.
+
rq_affinity (RW)
----------------
If this option is '1', the block layer will migrate request completions to the
diff --git a/Documentation/cgroups/hugetlb.txt b/Documentation/cgroups/hugetlb.txt
new file mode 100644
index 000000000000..a9faaca1f029
--- /dev/null
+++ b/Documentation/cgroups/hugetlb.txt
@@ -0,0 +1,45 @@
+HugeTLB Controller
+-------------------
+
+The HugeTLB controller allows to limit the HugeTLB usage per control group and
+enforces the controller limit during page fault. Since HugeTLB doesn't
+support page reclaim, enforcing the limit at page fault time implies that,
+the application will get SIGBUS signal if it tries to access HugeTLB pages
+beyond its limit. This requires the application to know beforehand how much
+HugeTLB pages it would require for its use.
+
+HugeTLB controller can be created by first mounting the cgroup filesystem.
+
+# mount -t cgroup -o hugetlb none /sys/fs/cgroup
+
+With the above step, the initial or the parent HugeTLB group becomes
+visible at /sys/fs/cgroup. At bootup, this group includes all the tasks in
+the system. /sys/fs/cgroup/tasks lists the tasks in this cgroup.
+
+New groups can be created under the parent group /sys/fs/cgroup.
+
+# cd /sys/fs/cgroup
+# mkdir g1
+# echo $$ > g1/tasks
+
+The above steps create a new group g1 and move the current shell
+process (bash) into it.
+
+Brief summary of control files
+
+ hugetlb.<hugepagesize>.limit_in_bytes # set/show limit of "hugepagesize" hugetlb usage
+ hugetlb.<hugepagesize>.max_usage_in_bytes # show max "hugepagesize" hugetlb usage recorded
+ hugetlb.<hugepagesize>.usage_in_bytes # show current res_counter usage for "hugepagesize" hugetlb
+ hugetlb.<hugepagesize>.failcnt # show the number of allocation failure due to HugeTLB limit
+
+For a system supporting two hugepage size (16M and 16G) the control
+files include:
+
+hugetlb.16GB.limit_in_bytes
+hugetlb.16GB.max_usage_in_bytes
+hugetlb.16GB.usage_in_bytes
+hugetlb.16GB.failcnt
+hugetlb.16MB.limit_in_bytes
+hugetlb.16MB.max_usage_in_bytes
+hugetlb.16MB.usage_in_bytes
+hugetlb.16MB.failcnt
diff --git a/Documentation/cgroups/memory.txt b/Documentation/cgroups/memory.txt
index dd88540bb995..4372e6b8a353 100644
--- a/Documentation/cgroups/memory.txt
+++ b/Documentation/cgroups/memory.txt
@@ -73,6 +73,8 @@ Brief summary of control files.
memory.kmem.tcp.limit_in_bytes # set/show hard limit for tcp buf memory
memory.kmem.tcp.usage_in_bytes # show current tcp buf memory allocation
+ memory.kmem.tcp.failcnt # show the number of tcp buf memory usage hits limits
+ memory.kmem.tcp.max_usage_in_bytes # show max tcp buf memory usage recorded
1. History
@@ -187,12 +189,12 @@ the cgroup that brought it in -- this will happen on memory pressure).
But see section 8.2: when moving a task to another cgroup, its pages may
be recharged to the new cgroup, if move_charge_at_immigrate has been chosen.
-Exception: If CONFIG_CGROUP_CGROUP_MEM_RES_CTLR_SWAP is not used.
+Exception: If CONFIG_CGROUP_CGROUP_MEMCG_SWAP is not used.
When you do swapoff and make swapped-out pages of shmem(tmpfs) to
be backed into memory in force, charges for pages are accounted against the
caller of swapoff rather than the users of shmem.
-2.4 Swap Extension (CONFIG_CGROUP_MEM_RES_CTLR_SWAP)
+2.4 Swap Extension (CONFIG_MEMCG_SWAP)
Swap Extension allows you to record charge for swap. A swapped-in page is
charged back to original page allocator if possible.
@@ -259,7 +261,7 @@ When oom event notifier is registered, event will be delivered.
per-zone-per-cgroup LRU (cgroup's private LRU) is just guarded by
zone->lru_lock, it has no lock of its own.
-2.7 Kernel Memory Extension (CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
+2.7 Kernel Memory Extension (CONFIG_MEMCG_KMEM)
With the Kernel memory extension, the Memory Controller is able to limit
the amount of kernel memory used by the system. Kernel memory is fundamentally
@@ -286,8 +288,8 @@ per cgroup, instead of globally.
a. Enable CONFIG_CGROUPS
b. Enable CONFIG_RESOURCE_COUNTERS
-c. Enable CONFIG_CGROUP_MEM_RES_CTLR
-d. Enable CONFIG_CGROUP_MEM_RES_CTLR_SWAP (to use swap extension)
+c. Enable CONFIG_MEMCG
+d. Enable CONFIG_MEMCG_SWAP (to use swap extension)
1. Prepare the cgroups (see cgroups.txt, Why are cgroups needed?)
# mount -t tmpfs none /sys/fs/cgroup
diff --git a/Documentation/device-mapper/dm-raid.txt b/Documentation/device-mapper/dm-raid.txt
index 946c73342cde..1c1844957166 100644
--- a/Documentation/device-mapper/dm-raid.txt
+++ b/Documentation/device-mapper/dm-raid.txt
@@ -27,6 +27,10 @@ The target is named "raid" and it accepts the following parameters:
- rotating parity N (right-to-left) with data restart
raid6_nc RAID6 N continue
- rotating parity N (right-to-left) with data continuation
+ raid10 Various RAID10 inspired algorithms chosen by additional params
+ - RAID10: Striped Mirrors (aka 'Striping on top of mirrors')
+ - RAID1E: Integrated Adjacent Stripe Mirroring
+ - and other similar RAID10 variants
Reference: Chapter 4 of
http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf
@@ -59,6 +63,28 @@ The target is named "raid" and it accepts the following parameters:
logical size of the array. The bitmap records the device
synchronisation state for each region.
+ [raid10_copies <# copies>]
+ [raid10_format near]
+ These two options are used to alter the default layout of
+ a RAID10 configuration. The number of copies is can be
+ specified, but the default is 2. There are other variations
+ to how the copies are laid down - the default and only current
+ option is "near". Near copies are what most people think of
+ with respect to mirroring. If these options are left
+ unspecified, or 'raid10_copies 2' and/or 'raid10_format near'
+ are given, then the layouts for 2, 3 and 4 devices are:
+ 2 drives 3 drives 4 drives
+ -------- ---------- --------------
+ A1 A1 A1 A1 A2 A1 A1 A2 A2
+ A2 A2 A2 A3 A3 A3 A3 A4 A4
+ A3 A3 A4 A4 A5 A5 A5 A6 A6
+ A4 A4 A5 A6 A6 A7 A7 A8 A8
+ .. .. .. .. .. .. .. .. ..
+ The 2-device layout is equivalent 2-way RAID1. The 4-device
+ layout is what a traditional RAID10 would look like. The
+ 3-device layout is what might be called a 'RAID1E - Integrated
+ Adjacent Stripe Mirroring'.
+
<#raid_devs>: The number of devices composing the array.
Each device consists of two entries. The first is the device
containing the metadata (if any); the second is the one containing the
diff --git a/Documentation/devicetree/bindings/arm/mrvl/intc.txt b/Documentation/devicetree/bindings/arm/mrvl/intc.txt
index 80b9a94d9a23..8b53273cb22f 100644
--- a/Documentation/devicetree/bindings/arm/mrvl/intc.txt
+++ b/Documentation/devicetree/bindings/arm/mrvl/intc.txt
@@ -38,3 +38,23 @@ Example:
reg-names = "mux status", "mux mask";
mrvl,intc-nr-irqs = <2>;
};
+
+* Marvell Orion Interrupt controller
+
+Required properties
+- compatible : Should be "marvell,orion-intc".
+- #interrupt-cells: Specifies the number of cells needed to encode an
+ interrupt source. Supported value is <1>.
+- interrupt-controller : Declare this node to be an interrupt controller.
+- reg : Interrupt mask address. A list of 4 byte ranges, one per controller.
+ One entry in the list represents 32 interrupts.
+
+Example:
+
+ intc: interrupt-controller {
+ compatible = "marvell,orion-intc", "marvell,intc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xfed20204 0x04>,
+ <0xfed20214 0x04>;
+ };
diff --git a/Documentation/devicetree/bindings/ata/marvell.txt b/Documentation/devicetree/bindings/ata/marvell.txt
new file mode 100644
index 000000000000..b5cdd20cde9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/ata/marvell.txt
@@ -0,0 +1,16 @@
+* Marvell Orion SATA
+
+Required Properties:
+- compatibility : "marvell,orion-sata"
+- reg : Address range of controller
+- interrupts : Interrupt controller is using
+- nr-ports : Number of SATA ports in use.
+
+Example:
+
+ sata@80000 {
+ compatible = "marvell,orion-sata";
+ reg = <0x80000 0x5000>;
+ interrupts = <21>;
+ nr-ports = <2>;
+ }
diff --git a/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt
index 05428f39d9ac..e13787498bcf 100644
--- a/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt
+++ b/Documentation/devicetree/bindings/gpio/mrvl-gpio.txt
@@ -27,3 +27,26 @@ Example:
interrupt-controller;
#interrupt-cells = <1>;
};
+
+* Marvell Orion GPIO Controller
+
+Required properties:
+- compatible : Should be "marvell,orion-gpio"
+- reg : Address and length of the register set for controller.
+- gpio-controller : So we know this is a gpio controller.
+- ngpio : How many gpios this controller has.
+- interrupts : Up to 4 Interrupts for the controller.
+
+Optional properties:
+- mask-offset : For SMP Orions, offset for Nth CPU
+
+Example:
+
+ gpio0: gpio@10100 {
+ compatible = "marvell,orion-gpio";
+ #gpio-cells = <2>;
+ gpio-controller;
+ reg = <0x10100 0x40>;
+ ngpio = <32>;
+ interrupts = <35>, <36>, <37>, <38>;
+ };
diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
index 70cd49b1caa8..1dd622546d06 100644
--- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
+++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt
@@ -10,8 +10,8 @@ Required properties:
- compatible : Should be "fsl,<chip>-esdhc"
Optional properties:
-- fsl,cd-internal : Indicate to use controller internal card detection
-- fsl,wp-internal : Indicate to use controller internal write protection
+- fsl,cd-controller : Indicate to use controller internal card detection
+- fsl,wp-controller : Indicate to use controller internal write protection
Examples:
@@ -19,8 +19,8 @@ esdhc@70004000 {
compatible = "fsl,imx51-esdhc";
reg = <0x70004000 0x4000>;
interrupts = <1>;
- fsl,cd-internal;
- fsl,wp-internal;
+ fsl,cd-controller;
+ fsl,wp-controller;
};
esdhc@70008000 {
diff --git a/Documentation/devicetree/bindings/regulator/tps6586x.txt b/Documentation/devicetree/bindings/regulator/tps6586x.txt
index d156e1b5db12..da80c2ae0915 100644
--- a/Documentation/devicetree/bindings/regulator/tps6586x.txt
+++ b/Documentation/devicetree/bindings/regulator/tps6586x.txt
@@ -9,9 +9,9 @@ Required properties:
- regulators: list of regulators provided by this controller, must have
property "regulator-compatible" to match their hardware counterparts:
sm[0-2], ldo[0-9] and ldo_rtc
-- sm0-supply: The input supply for the SM0.
-- sm1-supply: The input supply for the SM1.
-- sm2-supply: The input supply for the SM2.
+- vin-sm0-supply: The input supply for the SM0.
+- vin-sm1-supply: The input supply for the SM1.
+- vin-sm2-supply: The input supply for the SM2.
- vinldo01-supply: The input supply for the LDO1 and LDO2
- vinldo23-supply: The input supply for the LDO2 and LDO3
- vinldo4-supply: The input supply for the LDO4
@@ -30,9 +30,9 @@ Example:
#gpio-cells = <2>;
gpio-controller;
- sm0-supply = <&some_reg>;
- sm1-supply = <&some_reg>;
- sm2-supply = <&some_reg>;
+ vin-sm0-supply = <&some_reg>;
+ vin-sm1-supply = <&some_reg>;
+ vin-sm2-supply = <&some_reg>;
vinldo01-supply = <...>;
vinldo23-supply = <...>;
vinldo4-supply = <...>;
diff --git a/Documentation/devicetree/bindings/watchdog/marvel.txt b/Documentation/devicetree/bindings/watchdog/marvel.txt
new file mode 100644
index 000000000000..0b2503ab0a05
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/marvel.txt
@@ -0,0 +1,14 @@
+* Marvell Orion Watchdog Time
+
+Required Properties:
+
+- Compatibility : "marvell,orion-wdt"
+- reg : Address of the timer registers
+
+Example:
+
+ wdt@20300 {
+ compatible = "marvell,orion-wdt";
+ reg = <0x20300 0x28>;
+ status = "okay";
+ };
diff --git a/Documentation/dvb/README.dvb-usb b/Documentation/dvb/README.dvb-usb
index c4d963a67d6f..8eb92264ee04 100644
--- a/Documentation/dvb/README.dvb-usb
+++ b/Documentation/dvb/README.dvb-usb
@@ -30,7 +30,7 @@ with the device via the bus. The connection between the DVB-API-functionality
is done via callbacks, assigned in a static device-description (struct
dvb_usb_device) each device-driver has to have.
-For an example have a look in drivers/media/dvb/dvb-usb/vp7045*.
+For an example have a look in drivers/media/usb/dvb-usb/vp7045*.
Objective is to migrate all the usb-devices (dibusb, cinergyT2, maybe the
ttusb; flexcop-usb already benefits from the generic flexcop-device) to use
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index 12d3952e83d5..32bc56b13b1c 100755
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -116,7 +116,7 @@ sub tda10045 {
sub tda10046 {
my $sourcefile = "TT_PCI_2.19h_28_11_2006.zip";
- my $url = "http://www.tt-download.com/download/updates/219/$sourcefile";
+ my $url = "http://technotrend.com.ua/download/software/219/$sourcefile";
my $hash = "6a7e1e2f2644b162ff0502367553c72d";
my $outfile = "dvb-fe-tda10046.fw";
my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 09b132dd062d..28179cfdace6 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -6,6 +6,15 @@ be removed from this file. The suggested deprecation period is 3 releases.
---------------------------
+What: support for i.mx25 in mx2_camera.c
+When: v3.8
+Why: it's been broken for a year. Furthermore, i.MX25 video capture
+ HW doesn't have much in common with i.MX27. A separate driver
+ will be needed for it.
+Who: Javier Martin<javier.martin@vista-silicon.com>
+
+---------------------------
+
What: ddebug_query="query" boot cmdline param
When: v3.8
Why: obsoleted by dyndbg="query" and module.dyndbg="query"
@@ -13,6 +22,14 @@ Who: Jim Cromie <jim.cromie@gmail.com>, Jason Baron <jbaron@redhat.com>
---------------------------
+What: /proc/sys/vm/nr_pdflush_threads
+When: 2012
+Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/
+ should be removed.
+Who: Wanpeng Li <liwp@linux.vnet.ibm.com>
+
+---------------------------
+
What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle
When: 2012
Why: This optional sub-feature of APM is of dubious reliability,
@@ -70,20 +87,6 @@ Who: Luis R. Rodriguez <lrodriguez@atheros.com>
---------------------------
-What: IRQF_SAMPLE_RANDOM
-Check: IRQF_SAMPLE_RANDOM
-When: July 2009
-
-Why: Many of IRQF_SAMPLE_RANDOM users are technically bogus as entropy
- sources in the kernel's current entropy model. To resolve this, every
- input point to the kernel's entropy pool needs to better document the
- type of entropy source it actually is. This will be replaced with
- additional add_*_randomness functions in drivers/char/random.c
-
-Who: Robin Getz <rgetz@blackfin.uclinux.org> & Matt Mackall <mpm@selenic.com>
-
----------------------------
-
What: The ieee80211_regdom module parameter
When: March 2010 / desktop catchup
@@ -585,7 +588,7 @@ Why: KVM tracepoints provide mostly equivalent information in a much more
----------------------------
What: at91-mci driver ("CONFIG_MMC_AT91")
-When: 3.7
+When: 3.8
Why: There are two mci drivers: at91-mci and atmel-mci. The PDC support
was added to atmel-mci as a first step to support more chips.
Then at91-mci was kept only for old IP versions (on at91rm9200 and
@@ -632,3 +635,23 @@ Why: New drivers should use new V4L2_CAP_VIDEO_M2M capability flag
Who: Sylwester Nawrocki <s.nawrocki@samsung.com>
----------------------------
+
+What: OMAP private DMA implementation
+When: 2013
+Why: We have a DMA engine implementation; all users should be updated
+ to use this rather than persisting with the old APIs. The old APIs
+ block merging the old DMA engine implementation into the DMA
+ engine driver.
+Who: Russell King <linux@arm.linux.org.uk>,
+ Santosh Shilimkar <santosh.shilimkar@ti.com>
+
+----------------------------
+
+What: Remove deprecated DV timings capability defines V4L2_IN_CAP_CUSTOM_TIMINGS
+ and V4L2_OUT_CAP_CUSTOM_TIMINGS.
+When: 3.9
+Why: These defines have been replaced by V4L2_IN_CAP_TIMINGS and
+ V4L2_OUT_CAP_TIMINGS respectively.
+Who: Hans Verkuil <hans.verkuil@cisco.com>
+
+----------------------------
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index e0cce2a5f820..e540a24e5d06 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -114,7 +114,6 @@ prototypes:
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -136,10 +135,9 @@ write_inode:
drop_inode: !!!inode->i_lock!!!
evict_inode:
put_super: write
-write_super: read
sync_fs: read
-freeze_fs: read
-unfreeze_fs: read
+freeze_fs: write
+unfreeze_fs: write
statfs: maybe(read) (see below)
remount_fs: write
umount_begin: no
@@ -206,6 +204,8 @@ prototypes:
int (*launder_page)(struct page *);
int (*is_partially_uptodate)(struct page *, read_descriptor_t *, unsigned long);
int (*error_remove_page)(struct address_space *, struct page *);
+ int (*swap_activate)(struct file *);
+ int (*swap_deactivate)(struct file *);
locking rules:
All except set_page_dirty and freepage may block
@@ -229,6 +229,8 @@ migratepage: yes (both)
launder_page: yes
is_partially_uptodate: yes
error_remove_page: yes
+swap_activate: no
+swap_deactivate: no
->write_begin(), ->write_end(), ->sync_page() and ->readpage()
may be called from the request handler (/dev/loop).
@@ -330,6 +332,15 @@ cleaned, or an error value if not. Note that in order to prevent the page
getting mapped back in and redirtied, it needs to be kept locked
across the entire operation.
+ ->swap_activate will be called with a non-zero argument on
+files backing (non block device backed) swapfiles. A return value
+of zero indicates success, in which case this file can be used for
+backing swapspace. The swapspace operations will be proxied to the
+address space operations.
+
+ ->swap_deactivate() will be called in the sys_swapoff()
+path after ->swap_activate() returned success.
+
----------------------- file_lock_operations ------------------------------
prototypes:
void (*fl_copy_lock)(struct file_lock *, struct file_lock *);
@@ -346,7 +357,6 @@ prototypes:
int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
void (*lm_notify)(struct file_lock *); /* unblock callback */
int (*lm_grant)(struct file_lock *, struct file_lock *, int);
- void (*lm_release_private)(struct file_lock *);
void (*lm_break)(struct file_lock *); /* break_lease callback */
int (*lm_change)(struct file_lock **, int);
@@ -355,7 +365,6 @@ locking rules:
lm_compare_owner: yes no
lm_notify: yes no
lm_grant: no no
-lm_release_private: maybe no
lm_break: yes no
lm_change yes no
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 2bef2b3843d1..0742feebc6e2 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -94,9 +94,8 @@ protected.
---
[mandatory]
-BKL is also moved from around sb operations. ->write_super() Is now called
-without BKL held. BKL should have been shifted into individual fs sb_op
-functions. If you don't need it, remove it.
+BKL is also moved from around sb operations. BKL should have been shifted into
+individual fs sb_op functions. If you don't need it, remove it.
---
[informational]
diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt
index ead764b2728f..de1e6c4dccff 100644
--- a/Documentation/filesystems/vfat.txt
+++ b/Documentation/filesystems/vfat.txt
@@ -137,6 +137,17 @@ errors=panic|continue|remount-ro
without doing anything or remount the partition in
read-only mode (default behavior).
+discard -- If set, issues discard/TRIM commands to the block
+ device when blocks are freed. This is useful for SSD devices
+ and sparse/thinly-provisoned LUNs.
+
+nfs -- This option maintains an index (cache) of directory
+ inodes by i_logstart which is used by the nfs-related code to
+ improve look-ups.
+
+ Enable this only if you want to export the FAT filesystem
+ over NFS
+
<bool>: 0,1,yes,no,true,false
TODO
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index aa754e01464e..2ee133e030c3 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -216,7 +216,6 @@ struct super_operations {
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -273,9 +272,6 @@ or bottom half).
put_super: called when the VFS wishes to free the superblock
(i.e. unmount). This is called with the superblock lock held
- write_super: called when the VFS superblock needs to be written to
- disc. This method is optional
-
sync_fs: called when VFS is writing out all dirty data associated with
a superblock. The second parameter indicates whether the method
should wait until the write out has been completed. Optional.
@@ -592,6 +588,8 @@ struct address_space_operations {
int (*migratepage) (struct page *, struct page *);
int (*launder_page) (struct page *);
int (*error_remove_page) (struct mapping *mapping, struct page *page);
+ int (*swap_activate)(struct file *);
+ int (*swap_deactivate)(struct file *);
};
writepage: called by the VM to write a dirty page to backing store.
@@ -760,6 +758,16 @@ struct address_space_operations {
Setting this implies you deal with pages going away under you,
unless you have them locked or reference counts increased.
+ swap_activate: Called when swapon is used on a file to allocate
+ space if necessary and pin the block lookup information in
+ memory. A return value of zero indicates success,
+ in which case this file can be used to back swapspace. The
+ swapspace operations will be proxied to this address space's
+ ->swap_{out,in} methods.
+
+ swap_deactivate: Called during swapoff on files where swap_activate
+ was successful.
+
The File Object
===============
diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801
index 615142da4ef6..157416e78cc4 100644
--- a/Documentation/i2c/busses/i2c-i801
+++ b/Documentation/i2c/busses/i2c-i801
@@ -21,6 +21,7 @@ Supported adapters:
* Intel DH89xxCC (PCH)
* Intel Panther Point (PCH)
* Intel Lynx Point (PCH)
+ * Intel Lynx Point-LP (PCH)
Datasheets: Publicly available at the Intel website
On Intel Patsburg and later chipsets, both the normal host SMBus controller
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 915f28c470e9..2152b0e7237d 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -88,6 +88,7 @@ Code Seq#(hex) Include File Comments
and kernel/power/user.c
'8' all SNP8023 advanced NIC card
<mailto:mcr@solidum.com>
+';' 64-7F linux/vfio.h
'@' 00-0F linux/radeonfb.h conflict!
'@' 00-0F drivers/video/aty/aty128fb.c conflict!
'A' 00-1F linux/apm_bios.h conflict!
@@ -177,7 +178,6 @@ Code Seq#(hex) Include File Comments
'V' C0 linux/ivtv.h conflict!
'V' C0 media/davinci/vpfe_capture.h conflict!
'V' C0 media/si4713.h conflict!
-'V' C0-CF drivers/media/video/mxb.h conflict!
'W' 00-1F linux/watchdog.h conflict!
'W' 00-1F linux/wanrouter.h conflict!
'W' 00-3F sound/asound.h conflict!
@@ -203,8 +203,6 @@ Code Seq#(hex) Include File Comments
'c' A0-AF arch/x86/include/asm/msr.h conflict!
'd' 00-FF linux/char/drm/drm/h conflict!
'd' 02-40 pcmcia/ds.h conflict!
-'d' 10-3F drivers/media/video/dabusb.h conflict!
-'d' C0-CF drivers/media/video/saa7191.h conflict!
'd' F0-FF linux/digi1.h
'e' all linux/digi1.h conflict!
'e' 00-1F drivers/net/irda/irtty-sir.h conflict!
@@ -266,9 +264,7 @@ Code Seq#(hex) Include File Comments
'v' 00-1F linux/ext2_fs.h conflict!
'v' 00-1F linux/fs.h conflict!
'v' 00-0F linux/sonypi.h conflict!
-'v' C0-DF media/pwc-ioctl.h conflict!
'v' C0-FF linux/meye.h conflict!
-'v' D0-DF drivers/media/video/cpia2/cpia2dev.h conflict!
'w' all CERN SCI driver
'y' 00-1F packet based user level communications
<mailto:zapman@interlan.net>
diff --git a/Documentation/laptops/laptop-mode.txt b/Documentation/laptops/laptop-mode.txt
index 0bf25eebce94..4ebbfc3f1c6e 100644
--- a/Documentation/laptops/laptop-mode.txt
+++ b/Documentation/laptops/laptop-mode.txt
@@ -262,9 +262,9 @@ MINIMUM_BATTERY_MINUTES=10
#
# Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
-# exceeded, the kernel will wake pdflush which will then reduce the amount
-# of dirty memory to dirty_background_ratio. Set this nice and low, so once
-# some writeout has commenced, we do a lot of it.
+# exceeded, the kernel will wake flusher threads which will then reduce the
+# amount of dirty memory to dirty_background_ratio. Set this nice and low,
+# so once some writeout has commenced, we do a lot of it.
#
#DIRTY_BACKGROUND_RATIO=5
@@ -384,9 +384,9 @@ CPU_MAXFREQ=${CPU_MAXFREQ:-'slowest'}
#
# Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
-# exceeded, the kernel will wake pdflush which will then reduce the amount
-# of dirty memory to dirty_background_ratio. Set this nice and low, so once
-# some writeout has commenced, we do a lot of it.
+# exceeded, the kernel will wake flusher threads which will then reduce the
+# amount of dirty memory to dirty_background_ratio. Set this nice and low,
+# so once some writeout has commenced, we do a lot of it.
#
DIRTY_BACKGROUND_RATIO=${DIRTY_BACKGROUND_RATIO:-'5'}
diff --git a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt
index 8d022073e3ef..2e9e0ae2cd45 100644
--- a/Documentation/networking/netconsole.txt
+++ b/Documentation/networking/netconsole.txt
@@ -51,8 +51,23 @@ Built-in netconsole starts immediately after the TCP stack is
initialized and attempts to bring up the supplied dev at the supplied
address.
-The remote host can run either 'netcat -u -l -p <port>',
-'nc -l -u <port>' or syslogd.
+The remote host has several options to receive the kernel messages,
+for example:
+
+1) syslogd
+
+2) netcat
+
+ On distributions using a BSD-based netcat version (e.g. Fedora,
+ openSUSE and Ubuntu) the listening port must be specified without
+ the -p switch:
+
+ 'nc -u -l -p <port>' / 'nc -u -l <port>' or
+ 'netcat -u -l -p <port>' / 'netcat -u -l <port>'
+
+3) socat
+
+ 'socat udp-recv:<port> -'
Dynamic reconfiguration:
========================
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index e40f4b4e1977..1479aca23744 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -840,9 +840,9 @@ static unsigned long i2c_pin_configs[] = {
static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "i2c0"),
- PIN_MAP_MUX_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),
- PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),
- PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),
+ PIN_MAP_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),
+ PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),
+ PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),
};
Finally, some devices expect the mapping table to contain certain specific
diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt
index e369de2d48cd..dd908cf64ecf 100644
--- a/Documentation/security/Yama.txt
+++ b/Documentation/security/Yama.txt
@@ -46,14 +46,13 @@ restrictions, it can call prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, ...)
so that any otherwise allowed process (even those in external pid namespaces)
may attach.
-These restrictions do not change how ptrace via PTRACE_TRACEME operates.
-
-The sysctl settings are:
+The sysctl settings (writable only with CAP_SYS_PTRACE) are:
0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
process running under the same uid, as long as it is dumpable (i.e.
did not transition uids, start privileged, or have called
- prctl(PR_SET_DUMPABLE...) already).
+ prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is
+ unchanged.
1 - restricted ptrace: a process must have a predefined relationship
with the inferior it wants to call PTRACE_ATTACH on. By default,
@@ -61,12 +60,13 @@ The sysctl settings are:
classic criteria is also met. To change the relationship, an
inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
an allowed debugger PID to call PTRACE_ATTACH on the inferior.
+ Using PTRACE_TRACEME is unchanged.
2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
- with PTRACE_ATTACH.
+ with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.
-3 - no attach: no processes may use ptrace with PTRACE_ATTACH. Once set,
- this sysctl cannot be changed to a lower value.
+3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via
+ PTRACE_TRACEME. Once set, this sysctl value cannot be changed.
The original children-only logic was based on the restrictions in grsecurity.
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index 7456360e161c..a92bba816843 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -53,6 +53,7 @@ ALC882/883/885/888/889
acer-aspire-8930g Acer Aspire 8330G/6935G
acer-aspire Acer Aspire others
inv-dmic Inverted internal mic workaround
+ no-primary-hp VAIO Z workaround (for fixed speaker DAC)
ALC861/660
==========
@@ -273,6 +274,10 @@ STAC92HD83*
dell-s14 Dell laptop
dell-vostro-3500 Dell Vostro 3500 laptop
hp-dv7-4000 HP dv-7 4000
+ hp_cNB11_intquad HP CNB models with 4 speakers
+ hp-zephyr HP Zephyr
+ hp-led HP with broken BIOS for mute LED
+ hp-inv-led HP with broken BIOS for inverted mute LED
auto BIOS setup (default)
STAC9872
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 8c235b6e4246..88152f214f48 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs:
- nr_open
- overflowuid
- overflowgid
+- protected_hardlinks
+- protected_symlinks
- suid_dumpable
- super-max
- super-nr
@@ -157,6 +159,46 @@ The default is 65534.
==============================================================
+protected_hardlinks:
+
+A long-standing class of security issues is the hardlink-based
+time-of-check-time-of-use race, most commonly seen in world-writable
+directories like /tmp. The common method of exploitation of this flaw
+is to cross privilege boundaries when following a given hardlink (i.e. a
+root process follows a hardlink created by another user). Additionally,
+on systems without separated partitions, this stops unauthorized users
+from "pinning" vulnerable setuid/setgid files against being upgraded by
+the administrator, or linking to special files.
+
+When set to "0", hardlink creation behavior is unrestricted.
+
+When set to "1" hardlinks cannot be created by users if they do not
+already own the source file, or do not have read/write access to it.
+
+This protection is based on the restrictions in Openwall and grsecurity.
+
+==============================================================
+
+protected_symlinks:
+
+A long-standing class of security issues is the symlink-based
+time-of-check-time-of-use race, most commonly seen in world-writable
+directories like /tmp. The common method of exploitation of this flaw
+is to cross privilege boundaries when following a given symlink (i.e. a
+root process follows a symlink belonging to another user). For a likely
+incomplete list of hundreds of examples across the years, please see:
+http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=/tmp
+
+When set to "0", symlink following behavior is unrestricted.
+
+When set to "1" symlinks are permitted to be followed only when outside
+a sticky world-writable directory, or when the uid of the symlink and
+follower match, or when the directory owner matches the symlink's owner.
+
+This protection is based on the restrictions in Openwall and grsecurity.
+
+==============================================================
+
suid_dumpable:
This value can be used to query and set the core dump mode for setuid
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index 96f0ee825bed..078701fdbd4d 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -42,7 +42,6 @@ Currently, these files are in /proc/sys/vm:
- mmap_min_addr
- nr_hugepages
- nr_overcommit_hugepages
-- nr_pdflush_threads
- nr_trim_pages (only if CONFIG_MMU=n)
- numa_zonelist_order
- oom_dump_tasks
@@ -77,8 +76,8 @@ huge pages although processes will also directly compact memory as required.
dirty_background_bytes
-Contains the amount of dirty memory at which the pdflush background writeback
-daemon will start writeback.
+Contains the amount of dirty memory at which the background kernel
+flusher threads will start writeback.
Note: dirty_background_bytes is the counterpart of dirty_background_ratio. Only
one of them may be specified at a time. When one sysctl is written it is
@@ -90,7 +89,7 @@ other appears as 0 when read.
dirty_background_ratio
Contains, as a percentage of total system memory, the number of pages at which
-the pdflush background writeback daemon will start writing out dirty data.
+the background kernel flusher threads will start writing out dirty data.
==============================================================
@@ -113,9 +112,9 @@ retained.
dirty_expire_centisecs
This tunable is used to define when dirty data is old enough to be eligible
-for writeout by the pdflush daemons. It is expressed in 100'ths of a second.
-Data which has been dirty in-memory for longer than this interval will be
-written out next time a pdflush daemon wakes up.
+for writeout by the kernel flusher threads. It is expressed in 100'ths
+of a second. Data which has been dirty in-memory for longer than this
+interval will be written out next time a flusher thread wakes up.
==============================================================
@@ -129,7 +128,7 @@ data.
dirty_writeback_centisecs
-The pdflush writeback daemons will periodically wake up and write `old' data
+The kernel flusher threads will periodically wake up and write `old' data
out to disk. This tunable expresses the interval between those wakeups, in
100'ths of a second.
@@ -426,16 +425,6 @@ See Documentation/vm/hugetlbpage.txt
==============================================================
-nr_pdflush_threads
-
-The current number of pdflush threads. This value is read-only.
-The value changes according to the number of dirty pages in the system.
-
-When necessary, additional pdflush threads are created, one per second, up to
-nr_pdflush_threads_max.
-
-==============================================================
-
nr_trim_pages
This is available only on NOMMU kernels.
@@ -502,9 +491,10 @@ oom_dump_tasks
Enables a system-wide task dump (excluding kernel threads) to be
produced when the kernel performs an OOM-killing and includes such
-information as pid, uid, tgid, vm size, rss, cpu, oom_adj score, and
-name. This is helpful to determine why the OOM killer was invoked
-and to identify the rogue task that caused it.
+information as pid, uid, tgid, vm size, rss, nr_ptes, swapents,
+oom_score_adj score, and name. This is helpful to determine why the
+OOM killer was invoked, to identify the rogue task that caused it,
+and to determine why the OOM killer chose the task it did to kill.
If this is set to zero, this information is suppressed. On very
large systems with thousands of tasks it may not be feasible to dump
@@ -574,16 +564,24 @@ of physical RAM. See above.
page-cluster
-page-cluster controls the number of pages which are written to swap in
-a single attempt. The swap I/O size.
+page-cluster controls the number of pages up to which consecutive pages
+are read in from swap in a single attempt. This is the swap counterpart
+to page cache readahead.
+The mentioned consecutivity is not in terms of virtual/physical addresses,
+but consecutive on swap space - that means they were swapped out together.
It is a logarithmic value - setting it to zero means "1 page", setting
it to 1 means "2 pages", setting it to 2 means "4 pages", etc.
+Zero disables swap readahead completely.
The default value is three (eight pages at a time). There may be some
small benefits in tuning this to a different value if your workload is
swap-intensive.
+Lower values mean lower latencies for initial faults, but at the same time
+extra faults and I/O delays for following faults if they would have been part of
+that consecutive pages readahead would have brought in.
+
=============================================================
panic_on_oom
diff --git a/Documentation/vfio.txt b/Documentation/vfio.txt
new file mode 100644
index 000000000000..8eda3635a17d
--- /dev/null
+++ b/Documentation/vfio.txt
@@ -0,0 +1,314 @@
+VFIO - "Virtual Function I/O"[1]
+-------------------------------------------------------------------------------
+Many modern system now provide DMA and interrupt remapping facilities
+to help ensure I/O devices behave within the boundaries they've been
+allotted. This includes x86 hardware with AMD-Vi and Intel VT-d,
+POWER systems with Partitionable Endpoints (PEs) and embedded PowerPC
+systems such as Freescale PAMU. The VFIO driver is an IOMMU/device
+agnostic framework for exposing direct device access to userspace, in
+a secure, IOMMU protected environment. In other words, this allows
+safe[2], non-privileged, userspace drivers.
+
+Why do we want that? Virtual machines often make use of direct device
+access ("device assignment") when configured for the highest possible
+I/O performance. From a device and host perspective, this simply
+turns the VM into a userspace driver, with the benefits of
+significantly reduced latency, higher bandwidth, and direct use of
+bare-metal device drivers[3].
+
+Some applications, particularly in the high performance computing
+field, also benefit from low-overhead, direct device access from
+userspace. Examples include network adapters (often non-TCP/IP based)
+and compute accelerators. Prior to VFIO, these drivers had to either
+go through the full development cycle to become proper upstream
+driver, be maintained out of tree, or make use of the UIO framework,
+which has no notion of IOMMU protection, limited interrupt support,
+and requires root privileges to access things like PCI configuration
+space.
+
+The VFIO driver framework intends to unify these, replacing both the
+KVM PCI specific device assignment code as well as provide a more
+secure, more featureful userspace driver environment than UIO.
+
+Groups, Devices, and IOMMUs
+-------------------------------------------------------------------------------
+
+Devices are the main target of any I/O driver. Devices typically
+create a programming interface made up of I/O access, interrupts,
+and DMA. Without going into the details of each of these, DMA is
+by far the most critical aspect for maintaining a secure environment
+as allowing a device read-write access to system memory imposes the
+greatest risk to the overall system integrity.
+
+To help mitigate this risk, many modern IOMMUs now incorporate
+isolation properties into what was, in many cases, an interface only
+meant for translation (ie. solving the addressing problems of devices
+with limited address spaces). With this, devices can now be isolated
+from each other and from arbitrary memory access, thus allowing
+things like secure direct assignment of devices into virtual machines.
+
+This isolation is not always at the granularity of a single device
+though. Even when an IOMMU is capable of this, properties of devices,
+interconnects, and IOMMU topologies can each reduce this isolation.
+For instance, an individual device may be part of a larger multi-
+function enclosure. While the IOMMU may be able to distinguish
+between devices within the enclosure, the enclosure may not require
+transactions between devices to reach the IOMMU. Examples of this
+could be anything from a multi-function PCI device with backdoors
+between functions to a non-PCI-ACS (Access Control Services) capable
+bridge allowing redirection without reaching the IOMMU. Topology
+can also play a factor in terms of hiding devices. A PCIe-to-PCI
+bridge masks the devices behind it, making transaction appear as if
+from the bridge itself. Obviously IOMMU design plays a major factor
+as well.
+
+Therefore, while for the most part an IOMMU may have device level
+granularity, any system is susceptible to reduced granularity. The
+IOMMU API therefore supports a notion of IOMMU groups. A group is
+a set of devices which is isolatable from all other devices in the
+system. Groups are therefore the unit of ownership used by VFIO.
+
+While the group is the minimum granularity that must be used to
+ensure secure user access, it's not necessarily the preferred
+granularity. In IOMMUs which make use of page tables, it may be
+possible to share a set of page tables between different groups,
+reducing the overhead both to the platform (reduced TLB thrashing,
+reduced duplicate page tables), and to the user (programming only
+a single set of translations). For this reason, VFIO makes use of
+a container class, which may hold one or more groups. A container
+is created by simply opening the /dev/vfio/vfio character device.
+
+On its own, the container provides little functionality, with all
+but a couple version and extension query interfaces locked away.
+The user needs to add a group into the container for the next level
+of functionality. To do this, the user first needs to identify the
+group associated with the desired device. This can be done using
+the sysfs links described in the example below. By unbinding the
+device from the host driver and binding it to a VFIO driver, a new
+VFIO group will appear for the group as /dev/vfio/$GROUP, where
+$GROUP is the IOMMU group number of which the device is a member.
+If the IOMMU group contains multiple devices, each will need to
+be bound to a VFIO driver before operations on the VFIO group
+are allowed (it's also sufficient to only unbind the device from
+host drivers if a VFIO driver is unavailable; this will make the
+group available, but not that particular device). TBD - interface
+for disabling driver probing/locking a device.
+
+Once the group is ready, it may be added to the container by opening
+the VFIO group character device (/dev/vfio/$GROUP) and using the
+VFIO_GROUP_SET_CONTAINER ioctl, passing the file descriptor of the
+previously opened container file. If desired and if the IOMMU driver
+supports sharing the IOMMU context between groups, multiple groups may
+be set to the same container. If a group fails to set to a container
+with existing groups, a new empty container will need to be used
+instead.
+
+With a group (or groups) attached to a container, the remaining
+ioctls become available, enabling access to the VFIO IOMMU interfaces.
+Additionally, it now becomes possible to get file descriptors for each
+device within a group using an ioctl on the VFIO group file descriptor.
+
+The VFIO device API includes ioctls for describing the device, the I/O
+regions and their read/write/mmap offsets on the device descriptor, as
+well as mechanisms for describing and registering interrupt
+notifications.
+
+VFIO Usage Example
+-------------------------------------------------------------------------------
+
+Assume user wants to access PCI device 0000:06:0d.0
+
+$ readlink /sys/bus/pci/devices/0000:06:0d.0/iommu_group
+../../../../kernel/iommu_groups/26
+
+This device is therefore in IOMMU group 26. This device is on the
+pci bus, therefore the user will make use of vfio-pci to manage the
+group:
+
+# modprobe vfio-pci
+
+Binding this device to the vfio-pci driver creates the VFIO group
+character devices for this group:
+
+$ lspci -n -s 0000:06:0d.0
+06:0d.0 0401: 1102:0002 (rev 08)
+# echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
+# echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id
+
+Now we need to look at what other devices are in the group to free
+it for use by VFIO:
+
+$ ls -l /sys/bus/pci/devices/0000:06:0d.0/iommu_group/devices
+total 0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:00:1e.0 ->
+ ../../../../devices/pci0000:00/0000:00:1e.0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.0 ->
+ ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0
+lrwxrwxrwx. 1 root root 0 Apr 23 16:13 0000:06:0d.1 ->
+ ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1
+
+This device is behind a PCIe-to-PCI bridge[4], therefore we also
+need to add device 0000:06:0d.1 to the group following the same
+procedure as above. Device 0000:00:1e.0 is a bridge that does
+not currently have a host driver, therefore it's not required to
+bind this device to the vfio-pci driver (vfio-pci does not currently
+support PCI bridges).
+
+The final step is to provide the user with access to the group if
+unprivileged operation is desired (note that /dev/vfio/vfio provides
+no capabilities on its own and is therefore expected to be set to
+mode 0666 by the system).
+
+# chown user:user /dev/vfio/26
+
+The user now has full access to all the devices and the iommu for this
+group and can access them as follows:
+
+ int container, group, device, i;
+ struct vfio_group_status group_status =
+ { .argsz = sizeof(group_status) };
+ struct vfio_iommu_x86_info iommu_info = { .argsz = sizeof(iommu_info) };
+ struct vfio_iommu_x86_dma_map dma_map = { .argsz = sizeof(dma_map) };
+ struct vfio_device_info device_info = { .argsz = sizeof(device_info) };
+
+ /* Create a new container */
+ container = open("/dev/vfio/vfio, O_RDWR);
+
+ if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION)
+ /* Unknown API version */
+
+ if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_X86_IOMMU))
+ /* Doesn't support the IOMMU driver we want. */
+
+ /* Open the group */
+ group = open("/dev/vfio/26", O_RDWR);
+
+ /* Test the group is viable and available */
+ ioctl(group, VFIO_GROUP_GET_STATUS, &group_status);
+
+ if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE))
+ /* Group is not viable (ie, not all devices bound for vfio) */
+
+ /* Add the group to the container */
+ ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
+
+ /* Enable the IOMMU model we want */
+ ioctl(container, VFIO_SET_IOMMU, VFIO_X86_IOMMU)
+
+ /* Get addition IOMMU info */
+ ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info);
+
+ /* Allocate some space and setup a DMA mapping */
+ dma_map.vaddr = mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+ dma_map.size = 1024 * 1024;
+ dma_map.iova = 0; /* 1MB starting at 0x0 from device view */
+ dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE;
+
+ ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
+
+ /* Get a file descriptor for the device */
+ device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, "0000:06:0d.0");
+
+ /* Test and setup the device */
+ ioctl(device, VFIO_DEVICE_GET_INFO, &device_info);
+
+ for (i = 0; i < device_info.num_regions; i++) {
+ struct vfio_region_info reg = { .argsz = sizeof(reg) };
+
+ reg.index = i;
+
+ ioctl(device, VFIO_DEVICE_GET_REGION_INFO, &reg);
+
+ /* Setup mappings... read/write offsets, mmaps
+ * For PCI devices, config space is a region */
+ }
+
+ for (i = 0; i < device_info.num_irqs; i++) {
+ struct vfio_irq_info irq = { .argsz = sizeof(irq) };
+
+ irq.index = i;
+
+ ioctl(device, VFIO_DEVICE_GET_IRQ_INFO, &reg);
+
+ /* Setup IRQs... eventfds, VFIO_DEVICE_SET_IRQS */
+ }
+
+ /* Gratuitous device reset and go... */
+ ioctl(device, VFIO_DEVICE_RESET);
+
+VFIO User API
+-------------------------------------------------------------------------------
+
+Please see include/linux/vfio.h for complete API documentation.
+
+VFIO bus driver API
+-------------------------------------------------------------------------------
+
+VFIO bus drivers, such as vfio-pci make use of only a few interfaces
+into VFIO core. When devices are bound and unbound to the driver,
+the driver should call vfio_add_group_dev() and vfio_del_group_dev()
+respectively:
+
+extern int vfio_add_group_dev(struct iommu_group *iommu_group,
+ struct device *dev,
+ const struct vfio_device_ops *ops,
+ void *device_data);
+
+extern void *vfio_del_group_dev(struct device *dev);
+
+vfio_add_group_dev() indicates to the core to begin tracking the
+specified iommu_group and register the specified dev as owned by
+a VFIO bus driver. The driver provides an ops structure for callbacks
+similar to a file operations structure:
+
+struct vfio_device_ops {
+ int (*open)(void *device_data);
+ void (*release)(void *device_data);
+ ssize_t (*read)(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos);
+ ssize_t (*write)(void *device_data, const char __user *buf,
+ size_t size, loff_t *ppos);
+ long (*ioctl)(void *device_data, unsigned int cmd,
+ unsigned long arg);
+ int (*mmap)(void *device_data, struct vm_area_struct *vma);
+};
+
+Each function is passed the device_data that was originally registered
+in the vfio_add_group_dev() call above. This allows the bus driver
+an easy place to store its opaque, private data. The open/release
+callbacks are issued when a new file descriptor is created for a
+device (via VFIO_GROUP_GET_DEVICE_FD). The ioctl interface provides
+a direct pass through for VFIO_DEVICE_* ioctls. The read/write/mmap
+interfaces implement the device region access defined by the device's
+own VFIO_DEVICE_GET_REGION_INFO ioctl.
+
+-------------------------------------------------------------------------------
+
+[1] VFIO was originally an acronym for "Virtual Function I/O" in its
+initial implementation by Tom Lyon while as Cisco. We've since
+outgrown the acronym, but it's catchy.
+
+[2] "safe" also depends upon a device being "well behaved". It's
+possible for multi-function devices to have backdoors between
+functions and even for single function devices to have alternative
+access to things like PCI config space through MMIO registers. To
+guard against the former we can include additional precautions in the
+IOMMU driver to group multi-function PCI devices together
+(iommu=group_mf). The latter we can't prevent, but the IOMMU should
+still provide isolation. For PCI, SR-IOV Virtual Functions are the
+best indicator of "well behaved", as these are designed for
+virtualization usage models.
+
+[3] As always there are trade-offs to virtual machine device
+assignment that are beyond the scope of VFIO. It's expected that
+future IOMMU technologies will reduce some, but maybe not all, of
+these trade-offs.
+
+[4] In this case the device is below a PCI bridge, so transactions
+from either function of the device are indistinguishable to the iommu:
+
+-[0000:00]-+-1e.0-[06]--+-0d.0
+ \-0d.1
+
+00:1e.0 PCI bridge: Intel Corporation 82801 PCI Bridge (rev 90)
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 652aecd13199..1299b5e82d7f 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -35,3 +35,4 @@
34 -> TerraTec Cinergy T PCIe Dual [153b:117e]
35 -> TeVii S471 [d471:9022]
36 -> Hauppauge WinTV-HVR1255 [0070:2259]
+ 37 -> Prof Revolution DVB-S2 8000 [8000:3034]
diff --git a/Documentation/video4linux/CQcam.txt b/Documentation/video4linux/CQcam.txt
index 6e680fec1e9c..0b69e4ee8e31 100644
--- a/Documentation/video4linux/CQcam.txt
+++ b/Documentation/video4linux/CQcam.txt
@@ -18,7 +18,7 @@ Table of Contents
1.0 Introduction
- The file ../../drivers/media/video/c-qcam.c is a device driver for
+ The file ../../drivers/media/parport/c-qcam.c is a device driver for
the Logitech (nee Connectix) parallel port interface color CCD camera.
This is a fairly inexpensive device for capturing images. Logitech
does not currently provide information for developers, but many people
diff --git a/Documentation/video4linux/README.davinci-vpbe b/Documentation/video4linux/README.davinci-vpbe
index 7a460b0685bb..dc9a297f49c3 100644
--- a/Documentation/video4linux/README.davinci-vpbe
+++ b/Documentation/video4linux/README.davinci-vpbe
@@ -5,22 +5,22 @@
File partitioning
-----------------
V4L2 display device driver
- drivers/media/video/davinci/vpbe_display.c
- drivers/media/video/davinci/vpbe_display.h
+ drivers/media/platform/davinci/vpbe_display.c
+ drivers/media/platform/davinci/vpbe_display.h
VPBE display controller
- drivers/media/video/davinci/vpbe.c
- drivers/media/video/davinci/vpbe.h
+ drivers/media/platform/davinci/vpbe.c
+ drivers/media/platform/davinci/vpbe.h
VPBE venc sub device driver
- drivers/media/video/davinci/vpbe_venc.c
- drivers/media/video/davinci/vpbe_venc.h
- drivers/media/video/davinci/vpbe_venc_regs.h
+ drivers/media/platform/davinci/vpbe_venc.c
+ drivers/media/platform/davinci/vpbe_venc.h
+ drivers/media/platform/davinci/vpbe_venc_regs.h
VPBE osd driver
- drivers/media/video/davinci/vpbe_osd.c
- drivers/media/video/davinci/vpbe_osd.h
- drivers/media/video/davinci/vpbe_osd_regs.h
+ drivers/media/platform/davinci/vpbe_osd.c
+ drivers/media/platform/davinci/vpbe_osd.h
+ drivers/media/platform/davinci/vpbe_osd_regs.h
Functional partitioning
-----------------------
diff --git a/Documentation/video4linux/fimc.txt b/Documentation/video4linux/fimc.txt
index eb049708f3e4..fd02d9a4930a 100644
--- a/Documentation/video4linux/fimc.txt
+++ b/Documentation/video4linux/fimc.txt
@@ -10,7 +10,7 @@ data from LCD controller (FIMD) through the SoC internal writeback data
path. There are multiple FIMC instances in the SoCs (up to 4), having
slightly different capabilities, like pixel alignment constraints, rotator
availability, LCD writeback support, etc. The driver is located at
-drivers/media/video/s5p-fimc directory.
+drivers/media/platform/s5p-fimc directory.
1. Supported SoCs
=================
@@ -36,21 +36,21 @@ Not currently supported:
=====================
- media device driver
- drivers/media/video/s5p-fimc/fimc-mdevice.[ch]
+ drivers/media/platform/s5p-fimc/fimc-mdevice.[ch]
- camera capture video device driver
- drivers/media/video/s5p-fimc/fimc-capture.c
+ drivers/media/platform/s5p-fimc/fimc-capture.c
- MIPI-CSI2 receiver subdev
- drivers/media/video/s5p-fimc/mipi-csis.[ch]
+ drivers/media/platform/s5p-fimc/mipi-csis.[ch]
- video post-processor (mem-to-mem)
- drivers/media/video/s5p-fimc/fimc-core.c
+ drivers/media/platform/s5p-fimc/fimc-core.c
- common files
- drivers/media/video/s5p-fimc/fimc-core.h
- drivers/media/video/s5p-fimc/fimc-reg.h
- drivers/media/video/s5p-fimc/regs-fimc.h
+ drivers/media/platform/s5p-fimc/fimc-core.h
+ drivers/media/platform/s5p-fimc/fimc-reg.h
+ drivers/media/platform/s5p-fimc/regs-fimc.h
4. User space interfaces
========================
diff --git a/Documentation/video4linux/omap3isp.txt b/Documentation/video4linux/omap3isp.txt
index 5dd1439b61fd..b9a9f83b1587 100644
--- a/Documentation/video4linux/omap3isp.txt
+++ b/Documentation/video4linux/omap3isp.txt
@@ -12,7 +12,7 @@ Introduction
============
This file documents the Texas Instruments OMAP 3 Image Signal Processor (ISP)
-driver located under drivers/media/video/omap3isp. The original driver was
+driver located under drivers/media/platform/omap3isp. The original driver was
written by Texas Instruments but since that it has been rewritten (twice) at
Nokia.
diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt
index 43da22b89728..54270df99d5c 100644
--- a/Documentation/video4linux/v4l2-controls.txt
+++ b/Documentation/video4linux/v4l2-controls.txt
@@ -594,7 +594,11 @@ handler and finally add the first handler to the second. For example:
v4l2_ctrl_new_std(&radio_ctrl_handler, &radio_ops, V4L2_CID_AUDIO_MUTE, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_BRIGHTNESS, ...);
v4l2_ctrl_new_std(&video_ctrl_handler, &video_ops, V4L2_CID_CONTRAST, ...);
- v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler);
+ v4l2_ctrl_add_handler(&video_ctrl_handler, &radio_ctrl_handler, NULL);
+
+The last argument to v4l2_ctrl_add_handler() is a filter function that allows
+you to filter which controls will be added. Set it to NULL if you want to add
+all controls.
Or you can add specific controls to a handler:
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
index 89318be6c1d2..32bfe926e8d7 100644
--- a/Documentation/video4linux/v4l2-framework.txt
+++ b/Documentation/video4linux/v4l2-framework.txt
@@ -583,11 +583,19 @@ You should also set these fields:
- name: set to something descriptive and unique.
+- vfl_dir: set this to VFL_DIR_RX for capture devices (VFL_DIR_RX has value 0,
+ so this is normally already the default), set to VFL_DIR_TX for output
+ devices and VFL_DIR_M2M for mem2mem (codec) devices.
+
- fops: set to the v4l2_file_operations struct.
- ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
(highly recommended to use this and it might become compulsory in the
- future!), then set this to your v4l2_ioctl_ops struct.
+ future!), then set this to your v4l2_ioctl_ops struct. The vfl_type and
+ vfl_dir fields are used to disable ops that do not match the type/dir
+ combination. E.g. VBI ops are disabled for non-VBI nodes, and output ops
+ are disabled for a capture device. This makes it possible to provide
+ just one v4l2_ioctl_ops struct for both vbi and video nodes.
- lock: leave to NULL if you want to do all the locking in the driver.
Otherwise you give it a pointer to a struct mutex_lock and before the
@@ -1054,4 +1062,4 @@ The first event type in the class is reserved for future use, so the first
available event type is 'class base + 1'.
An example on how the V4L2 events may be used can be found in the OMAP
-3 ISP driver (drivers/media/video/omap3isp).
+3 ISP driver (drivers/media/platform/omap3isp).
diff --git a/Documentation/video4linux/videobuf b/Documentation/video4linux/videobuf
index 1d00d7f15b8f..3ffe9e960b6f 100644
--- a/Documentation/video4linux/videobuf
+++ b/Documentation/video4linux/videobuf
@@ -349,7 +349,7 @@ again.
Developers who are interested in more information can go into the relevant
header files; there are a few low-level functions declared there which have
not been talked about here. Also worthwhile is the vivi driver
-(drivers/media/video/vivi.c), which is maintained as an example of how V4L2
+(drivers/media/platform/vivi.c), which is maintained as an example of how V4L2
drivers should be written. Vivi only uses the vmalloc() API, but it's good
enough to get started with. Note also that all of these calls are exported
GPL-only, so they will not be available to non-GPL kernel modules.
diff --git a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt
index f8551b3879f8..4ac359b7aa17 100644
--- a/Documentation/vm/hugetlbpage.txt
+++ b/Documentation/vm/hugetlbpage.txt
@@ -299,11 +299,17 @@ map_hugetlb.c.
*******************************************************************
/*
- * hugepage-shm: see Documentation/vm/hugepage-shm.c
+ * map_hugetlb: see tools/testing/selftests/vm/map_hugetlb.c
*/
*******************************************************************
/*
- * hugepage-mmap: see Documentation/vm/hugepage-mmap.c
+ * hugepage-shm: see tools/testing/selftests/vm/hugepage-shm.c
+ */
+
+*******************************************************************
+
+/*
+ * hugepage-mmap: see tools/testing/selftests/vm/hugepage-mmap.c
*/
diff --git a/Documentation/w1/slaves/w1_therm b/Documentation/w1/slaves/w1_therm
index 0403aaaba878..874a8ca93feb 100644
--- a/Documentation/w1/slaves/w1_therm
+++ b/Documentation/w1/slaves/w1_therm
@@ -3,6 +3,7 @@ Kernel driver w1_therm
Supported chips:
* Maxim ds18*20 based temperature sensors.
+ * Maxim ds1825 based temperature sensors.
Author: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
@@ -15,6 +16,7 @@ supported family codes:
W1_THERM_DS18S20 0x10
W1_THERM_DS1822 0x22
W1_THERM_DS18B20 0x28
+W1_THERM_DS1825 0x3B
Support is provided through the sysfs w1_slave file. Each open and
read sequence will initiate a temperature conversion then provide two
diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
index 73ff5cc93e05..3da822967ee0 100644
--- a/Documentation/watchdog/src/watchdog-test.c
+++ b/Documentation/watchdog/src/watchdog-test.c
@@ -31,7 +31,7 @@ static void keep_alive(void)
* or "-e" to enable the card.
*/
-void term(int sig)
+static void term(int sig)
{
close(fd);
fprintf(stderr, "Stopping watchdog ticks...\n");
diff --git a/MAINTAINERS b/MAINTAINERS
index 5b44872b64ec..a21df13ffbcd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -184,6 +184,16 @@ S: Maintained
F: Documentation/filesystems/9p.txt
F: fs/9p/
+A8293 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/a8293*
+
AACRAID SCSI RAID DRIVER
M: Adaptec OEM Raid Solutions <aacraid@adaptec.com>
L: linux-scsi@vger.kernel.org
@@ -391,6 +401,26 @@ M: Riccardo Facchetti <fizban@tin.it>
S: Maintained
F: sound/oss/aedsp16.c
+AF9013 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/af9013*
+
+AF9033 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/af9033*
+
AFFS FILE SYSTEM
L: linux-fsdevel@vger.kernel.org
S: Orphan
@@ -827,24 +857,24 @@ F: arch/arm/mach-pxa/colibri-pxa270-income.c
ARM/INTEL IOP32X ARM ARCHITECTURE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IOP33X ARM ARCHITECTURE
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IOP13XX ARM ARCHITECTURE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IQ81342EX MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
@@ -869,7 +899,7 @@ F: drivers/pcmcia/pxa2xx_stargate2.c
ARM/INTEL XSC3 (MANZANO) ARM CORE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
@@ -925,14 +955,14 @@ S: Maintained
ARM/NOMADIK ARCHITECTURE
M: Alessandro Rubini <rubini@unipv.it>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
M: STEricsson <STEricsson_nomadik_linux@list.st.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-nomadik/
F: arch/arm/plat-nomadik/
F: drivers/i2c/busses/i2c-nomadik.c
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git
ARM/OPENMOKO NEO FREERUNNER (GTA02) MACHINE SUPPORT
M: Nelson Castillo <arhuaco@freaks-unidos.net>
@@ -1073,7 +1103,7 @@ L: linux-media@vger.kernel.org
S: Maintained
F: arch/arm/plat-s5p/dev-fimc*
F: arch/arm/plat-samsung/include/plat/*fimc*
-F: drivers/media/video/s5p-fimc/
+F: drivers/media/platform/s5p-fimc/
ARM/SAMSUNG S5P SERIES Multi Format Codec (MFC) SUPPORT
M: Kyungmin Park <kyungmin.park@samsung.com>
@@ -1083,7 +1113,7 @@ L: linux-arm-kernel@lists.infradead.org
L: linux-media@vger.kernel.org
S: Maintained
F: arch/arm/plat-s5p/dev-mfc.c
-F: drivers/media/video/s5p-mfc/
+F: drivers/media/platform/s5p-mfc/
ARM/SAMSUNG S5P SERIES TV SUBSYSTEM SUPPORT
M: Kyungmin Park <kyungmin.park@samsung.com>
@@ -1091,7 +1121,7 @@ M: Tomasz Stanislawski <t.stanislaws@samsung.com>
L: linux-arm-kernel@lists.infradead.org
L: linux-media@vger.kernel.org
S: Maintained
-F: drivers/media/video/s5p-tv/
+F: drivers/media/platform/s5p-tv/
ARM/SHMOBILE ARM ARCHITECTURE
M: Paul Mundt <lethal@linux-sh.org>
@@ -1146,7 +1176,7 @@ F: drivers/usb/host/ehci-w90x900.c
F: drivers/video/nuc900fb.c
ARM/U300 MACHINE SUPPORT
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: arch/arm/mach-u300/
@@ -1161,15 +1191,20 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
ARM/Ux500 ARM ARCHITECTURE
M: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-ux500/
+F: drivers/clocksource/clksrc-dbx500-prcmu.c
F: drivers/dma/ste_dma40*
+F: drivers/hwspinlock/u8500_hsem.c
F: drivers/mfd/abx500*
F: drivers/mfd/ab8500*
-F: drivers/mfd/stmpe*
+F: drivers/mfd/dbx500*
+F: drivers/mfd/db8500*
+F: drivers/pinctrl/pinctrl-nomadik*
F: drivers/rtc/rtc-ab8500.c
+F: drivers/rtc/rtc-pl031.c
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
ARM/VFP SUPPORT
@@ -1227,9 +1262,9 @@ S: Maintained
F: drivers/hwmon/asb100.c
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
W: http://sourceforge.net/projects/xscaleiop
-S: Supported
+S: Maintained
F: Documentation/crypto/async-tx-api.txt
F: crypto/async_tx/
F: drivers/dma/
@@ -1352,7 +1387,7 @@ ATMEL ISI DRIVER
M: Josh Wu <josh.wu@atmel.com>
L: linux-media@vger.kernel.org
S: Supported
-F: drivers/media/video/atmel-isi.c
+F: drivers/media/platform/atmel-isi.c
F: include/media/atmel-isi.h
ATMEL LCDFB DRIVER
@@ -1690,7 +1725,7 @@ W: http://linuxtv.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
F: Documentation/video4linux/bttv/
-F: drivers/media/video/bt8xx/bttv*
+F: drivers/media/pci/bt8xx/bttv*
C-MEDIA CMI8788 DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
@@ -1720,7 +1755,7 @@ L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
F: Documentation/video4linux/cafe_ccic
-F: drivers/media/video/marvell-ccic/
+F: drivers/media/platform/marvell-ccic/
CAIF NETWORK LAYER
M: Sjur Braendeland <sjur.brandeland@stericsson.com>
@@ -2109,7 +2144,17 @@ W: http://linuxtv.org
W: http://www.ivtvdriver.org/index.php/Cx18
S: Maintained
F: Documentation/video4linux/cx18.txt
-F: drivers/media/video/cx18/
+F: drivers/media/pci/cx18/
+
+CXD2820R MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/cxd2820r*
CXGB3 ETHERNET DRIVER (CXGB3)
M: Divy Le Ray <divy@chelsio.com>
@@ -2212,7 +2257,7 @@ S: Maintained
F: drivers/scsi/tmscsim.*
DC395x SCSI driver
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
M: Ali Akcaagac <aliakc@web.de>
M: Jamie Lenehan <lenehan@twibble.org>
W: http://twibble.org/dist/dc395x/
@@ -2359,7 +2404,7 @@ T: git git://git.linaro.org/people/sumitsemwal/linux-dma-buf.git
DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
M: Vinod Koul <vinod.koul@intel.com>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
S: Supported
F: drivers/dma/
F: include/linux/dma*
@@ -2464,6 +2509,117 @@ L: netdev@vger.kernel.org
S: Maintained
F: drivers/net/wan/dscc4.c
+DVB_USB_AF9015 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/af9015*
+
+DVB_USB_AF9035 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/af9035*
+
+DVB_USB_ANYSEE MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/anysee*
+
+DVB_USB_AU6610 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/au6610*
+
+DVB_USB_CE6230 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/ce6230*
+
+DVB_USB_CXUSB MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/cxusb*
+
+DVB_USB_CYPRESS_FIRMWARE MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/cypress_firmware*
+
+DVB_USB_EC168 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/ec168*
+
+DVB_USB_MXL111SF MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/mxl111sf.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/mxl111sf*
+
+DVB_USB_RTL28XXU MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/rtl28xxu*
+
+DVB_USB_V2 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/usb/dvb-usb-v2/dvb_usb*
+F: drivers/media/usb/dvb-usb-v2/usb_urb.c
+
DYNAMIC DEBUG
M: Jason Baron <jbaron@redhat.com>
S: Maintained
@@ -2475,6 +2631,16 @@ M: "Maciej W. Rozycki" <macro@linux-mips.org>
S: Maintained
F: drivers/tty/serial/dz.*
+E4000 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/e4000*
+
EATA-DMA SCSI DRIVER
M: Michael Neuffer <mike@i-Connect.Net>
L: linux-eata@i-connect.net
@@ -2503,6 +2669,16 @@ S: Maintained
F: include/linux/netfilter_bridge/ebt_*.h
F: net/bridge/netfilter/ebt*.c
+EC100 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/ec100*
+
ECRYPT FILE SYSTEM
M: Tyler Hicks <tyhicks@canonical.com>
M: Dustin Kirkland <dustin.kirkland@gazzang.com>
@@ -2773,8 +2949,18 @@ FC0011 TUNER DRIVER
M: Michael Buesch <m@bues.ch>
L: linux-media@vger.kernel.org
S: Maintained
-F: drivers/media/common/tuners/fc0011.h
-F: drivers/media/common/tuners/fc0011.c
+F: drivers/media/tuners/fc0011.h
+F: drivers/media/tuners/fc0011.c
+
+FC2580 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/fc2580*
FANOTIFY
M: Eric Paris <eparis@redhat.com>
@@ -3002,7 +3188,7 @@ M: Kyungmin Park <kyungmin.park@samsung.com>
M: Heungjun Kim <riverful.kim@samsung.com>
L: linux-media@vger.kernel.org
S: Maintained
-F: drivers/media/video/m5mols/
+F: drivers/media/i2c/m5mols/
F: include/media/m5mols.h
FUJITSU TABLET EXTRAS
@@ -3094,7 +3280,7 @@ F: include/linux/gigaset_dev.h
GPIO SUBSYSTEM
M: Grant Likely <grant.likely@secretlab.ca>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
T: git git://git.secretlab.ca/git/linux-2.6.git
F: Documentation/gpio.txt
@@ -3119,49 +3305,56 @@ M: Frank Zago <frank@zago.net>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/finepix.c
+F: drivers/media/usb/gspca/finepix.c
GSPCA GL860 SUBDRIVER
M: Olivier Lorin <o.lorin@laposte.net>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/gl860/
+F: drivers/media/usb/gspca/gl860/
GSPCA M5602 SUBDRIVER
M: Erik Andren <erik.andren@gmail.com>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/m5602/
+F: drivers/media/usb/gspca/m5602/
GSPCA PAC207 SONIXB SUBDRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/pac207.c
+F: drivers/media/usb/gspca/pac207.c
GSPCA SN9C20X SUBDRIVER
M: Brian Johnson <brijohn@gmail.com>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/sn9c20x.c
+F: drivers/media/usb/gspca/sn9c20x.c
GSPCA T613 SUBDRIVER
M: Leandro Costantino <lcostantino@gmail.com>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/t613.c
+F: drivers/media/usb/gspca/t613.c
GSPCA USB WEBCAM DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/gspca/
+F: drivers/media/usb/gspca/
+
+STK1160 USB VIDEO CAPTURE DRIVER
+M: Ezequiel Garcia <elezegarcia@gmail.com>
+L: linux-media@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
+S: Maintained
+F: drivers/media/usb/stk1160/
HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
M: Frank Seidel <frank@f-seidel.de>
@@ -3215,6 +3408,16 @@ L: linux-parisc@vger.kernel.org
S: Maintained
F: sound/parisc/harmony.*
+HD29L2 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/hd29l2*
+
HEWLETT-PACKARD SMART2 RAID DRIVER
M: Chirag Kantharia <chirag.kantharia@hp.com>
L: iss_storagedev@hp.com
@@ -3383,7 +3586,7 @@ M: "Wolfram Sang (embedded platforms)" <w.sang@pengutronix.de>
L: linux-i2c@vger.kernel.org
W: http://i2c.wiki.kernel.org/
T: quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-i2c/
-T: git git://git.fluff.org/bjdooks/linux.git
+T: git git://git.pengutronix.de/git/wsa/linux.git
S: Maintained
F: Documentation/i2c/
F: drivers/i2c/
@@ -3547,12 +3750,12 @@ K: \b(ABS|SYN)_MT_
INTEL C600 SERIES SAS CONTROLLER DRIVER
M: Intel SCU Linux support <intel-linux-scu@intel.com>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Lukasz Dorau <lukasz.dorau@intel.com>
+M: Maciej Patelczyk <maciej.patelczyk@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
-M: Ed Nadolski <edmund.nadolski@intel.com>
L: linux-scsi@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git
-S: Maintained
+T: git git://git.code.sf.net/p/intel-sas/isci
+S: Supported
F: drivers/scsi/isci/
F: firmware/isci/
@@ -3590,8 +3793,8 @@ F: arch/x86/kernel/microcode_core.c
F: arch/x86/kernel/microcode_intel.c
INTEL I/OAT DMA DRIVER
-M: Dan Williams <dan.j.williams@intel.com>
-S: Supported
+M: Dan Williams <djbw@fb.com>
+S: Maintained
F: drivers/dma/ioat*
INTEL IOMMU (VT-d)
@@ -3603,8 +3806,8 @@ F: drivers/iommu/intel-iommu.c
F: include/linux/intel-iommu.h
INTEL IOP-ADMA DMA DRIVER
-M: Dan Williams <dan.j.williams@intel.com>
-S: Maintained
+M: Dan Williams <djbw@fb.com>
+S: Odd fixes
F: drivers/dma/iop-adma.c
INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT
@@ -3662,11 +3865,12 @@ F: Documentation/networking/README.ipw2200
F: drivers/net/wireless/ipw2x00/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
-M: Joseph Cihula <joseph.cihula@intel.com>
+M: Richard L Maliszewski <richard.l.maliszewski@intel.com>
+M: Gang Wei <gang.wei@intel.com>
M: Shane Wang <shane.wang@intel.com>
L: tboot-devel@lists.sourceforge.net
W: http://tboot.sourceforge.net
-T: Mercurial http://www.bughost.org/repos.hg/tboot.hg
+T: hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot
S: Supported
F: Documentation/intel_txt.txt
F: include/linux/tboot.h
@@ -3861,7 +4065,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
W: http://www.ivtvdriver.org
S: Maintained
F: Documentation/video4linux/*.ivtv
-F: drivers/media/video/ivtv/
+F: drivers/media/pci/ivtv/
F: include/linux/ivtv*
JC42.4 TEMPERATURE SENSOR DRIVER
@@ -4155,6 +4359,26 @@ W: http://legousb.sourceforge.net/
S: Maintained
F: drivers/usb/misc/legousbtower.c
+LG2160 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/dvb-frontends/lg2160.*
+
+LGDT3305 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/dvb-frontends/lgdt3305.*
+
LGUEST
M: Rusty Russell <rusty@rustcorp.com.au>
L: lguest@lists.ozlabs.org
@@ -4533,7 +4757,7 @@ S: Supported
F: arch/microblaze/
MICROTEK X6 SCANNER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
S: Maintained
F: drivers/usb/image/microtek.*
@@ -4557,7 +4781,7 @@ MOTION EYE VAIO PICTUREBOOK CAMERA DRIVER
W: http://popies.net/meye/
S: Orphan
F: Documentation/video4linux/meye.txt
-F: drivers/media/video/meye.*
+F: drivers/media/pci/meye/
F: include/linux/meye.h
MOTOROLA IMX MMC/SD HOST CONTROLLER INTERFACE DRIVER
@@ -4621,6 +4845,16 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
S: Maintained
F: drivers/usb/musb/
+MXL5007T MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/mxl5007t.*
+
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
M: Andrew Gallatin <gallatin@myri.com>
L: netdev@vger.kernel.org
@@ -4960,7 +5194,7 @@ OMAP IMAGE SIGNAL PROCESSOR (ISP)
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
L: linux-media@vger.kernel.org
S: Maintained
-F: drivers/media/video/omap3isp/*
+F: drivers/media/platform/omap3isp/
OMAP USB SUPPORT
M: Felipe Balbi <balbi@ti.com>
@@ -5001,7 +5235,7 @@ M: Jonathan Corbet <corbet@lwn.net>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: drivers/media/video/ov7670.c
+F: drivers/media/i2c/ov7670.c
ONENAND FLASH DRIVER
M: Kyungmin Park <kyungmin.park@samsung.com>
@@ -5329,14 +5563,15 @@ PIN CONTROL SUBSYSTEM
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
F: drivers/pinctrl/
+F: include/linux/pinctrl/
PIN CONTROLLER - ST SPEAR
-M: Viresh Kumar <viresh.linux@gmail.com>
+M: Viresh Kumar <viresh.linux@gmail.com>
L: spear-devel@list.st.com
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://www.st.com/spear
S: Maintained
-F: driver/pinctrl/spear/
+F: drivers/pinctrl/spear/
PKTCDVD DRIVER
M: Peter Osterlund <petero2@telia.com>
@@ -5525,7 +5760,7 @@ W: http://www.isely.net/pvrusb2/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
F: Documentation/video4linux/README.pvrusb2
-F: drivers/media/video/pvrusb2/
+F: drivers/media/usb/pvrusb2/
PWM SUBSYSTEM
M: Thierry Reding <thierry.reding@avionic-design.de>
@@ -5538,6 +5773,8 @@ F: Documentation/devicetree/bindings/pwm/
F: include/linux/pwm.h
F: include/linux/of_pwm.h
F: drivers/pwm/
+F: drivers/video/backlight/pwm_bl.c
+F: include/linux/pwm_backlight.h
PXA2xx/PXA3xx SUPPORT
M: Eric Miao <eric.y.miao@gmail.com>
@@ -5633,6 +5870,16 @@ F: fs/qnx4/
F: include/linux/qnx4_fs.h
F: include/linux/qnxtypes.h
+QT1010 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/qt1010*
+
QUALCOMM HEXAGON ARCHITECTURE
M: Richard Kuo <rkuo@codeaurora.org>
L: linux-hexagon@vger.kernel.org
@@ -5682,7 +5929,7 @@ F: Documentation/blockdev/ramdisk.txt
F: drivers/block/brd.c
RANDOM NUMBER DRIVER
-M: Matt Mackall <mpm@selenic.com>
+M: Theodore Ts'o" <tytso@mit.edu>
S: Maintained
F: drivers/char/random.c
@@ -5799,6 +6046,16 @@ F: include/linux/rose.h
F: include/net/rose.h
F: net/rose/
+RTL2830 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/rtl2830*
+
RTL8180 WIRELESS DRIVER
M: "John W. Linville" <linville@tuxdriver.com>
L: linux-wireless@vger.kernel.org
@@ -5893,9 +6150,9 @@ L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
W: http://www.mihu.de/linux/saa7146
S: Maintained
-F: drivers/media/common/saa7146*
-F: drivers/media/video/*7146*
-F: include/media/*7146*
+F: drivers/media/common/saa7146/
+F: drivers/media/pci/saa7146/
+F: include/media/saa7146*
SAMSUNG LAPTOP DRIVER
M: Corentin Chary <corentincj@iksaif.net>
@@ -5956,7 +6213,7 @@ M: Huang Shijie <shijie8@gmail.com>
M: Kang Yong <kangyong@telegent.com>
M: Zhang Xiaobing <xbzhang@telegent.com>
S: Supported
-F: drivers/media/video/tlg2300
+F: drivers/media/usb/tlg2300
SC1200 WDT DRIVER
M: Zwane Mwaikambo <zwane@arm.linux.org.uk>
@@ -6344,8 +6601,9 @@ M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
S: Maintained
-F: include/media/v4l2*
-F: drivers/media/video/v4l2*
+F: include/media/soc*
+F: drivers/media/i2c/soc_camera/
+F: drivers/media/platform/soc_camera/
SOEKRIS NET48XX LED SUPPORT
M: Chris Boot <bootc@bootc.net>
@@ -6763,6 +7021,66 @@ W: http://tcp-lp-mod.sourceforge.net/
S: Maintained
F: net/ipv4/tcp_lp.c
+TDA10071 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/dvb-frontends/tda10071*
+
+TDA18212 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tda18212*
+
+TDA18218 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tda18218*
+
+TDA18271 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda18271*
+
+TDA827x MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda8290.*
+
+TDA8290 MEDIA DRIVER
+M: Michael Krufky <mkrufky@linuxtv.org>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://github.com/mkrufky
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/mkrufky/tuners.git
+S: Maintained
+F: drivers/media/tuners/tda8290.*
+
TEAM DRIVER
M: Jiri Pirko <jpirko@redhat.com>
L: netdev@vger.kernel.org
@@ -6953,6 +7271,16 @@ F: include/linux/serial_core.h
F: include/linux/serial.h
F: include/linux/tty.h
+TUA9001 MEDIA DRIVER
+M: Antti Palosaari <crope@iki.fi>
+L: linux-media@vger.kernel.org
+W: http://linuxtv.org/
+W: http://palosaari.fi/linux/
+Q: http://patchwork.linuxtv.org/project/linux-media/list/
+T: git git://linuxtv.org/anttip/media_tree.git
+S: Maintained
+F: drivers/media/tuners/tua9001*
+
TULIP NETWORK DRIVERS
M: Grant Grundler <grundler@parisc-linux.org>
L: netdev@vger.kernel.org
@@ -7071,7 +7399,7 @@ F: include/linux/mtd/ubi.h
F: include/mtd/ubi-user.h
USB ACM DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: Documentation/usb/acm.txt
@@ -7092,7 +7420,7 @@ S: Supported
F: drivers/block/ub.c
USB CDC ETHERNET DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/net/usb/cdc_*.c
@@ -7125,15 +7453,6 @@ S: Maintained
F: Documentation/usb/ehci.txt
F: drivers/usb/host/ehci*
-USB ET61X[12]51 DRIVER
-M: Luca Risolia <luca.risolia@studio.unibo.it>
-L: linux-usb@vger.kernel.org
-L: linux-media@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
-W: http://www.linux-projects.org
-S: Maintained
-F: drivers/media/video/et61x251/
-
USB GADGET/PERIPHERAL SUBSYSTEM
M: Felipe Balbi <balbi@ti.com>
L: linux-usb@vger.kernel.org
@@ -7165,7 +7484,7 @@ F: drivers/usb/host/isp116x*
F: include/linux/usb/isp116x.h
USB KAWASAKI LSI DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/serial/kl5kusb105.*
@@ -7283,6 +7602,12 @@ W: http://www.connecttech.com
S: Supported
F: drivers/usb/serial/whiteheat*
+USB SMSC75XX ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/smsc75xx.*
+
USB SMSC95XX ETHERNET DRIVER
M: Steve Glendinning <steve.glendinning@shawell.net>
L: netdev@vger.kernel.org
@@ -7297,7 +7622,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
W: http://www.linux-projects.org
S: Maintained
F: Documentation/video4linux/sn9c102.txt
-F: drivers/media/video/sn9c102/
+F: drivers/media/usb/sn9c102/
USB SUBSYSTEM
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
@@ -7332,17 +7657,7 @@ L: linux-media@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
W: http://www.ideasonboard.org/uvc/
S: Maintained
-F: drivers/media/video/uvc/
-
-USB W996[87]CF DRIVER
-M: Luca Risolia <luca.risolia@studio.unibo.it>
-L: linux-usb@vger.kernel.org
-L: linux-media@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
-W: http://www.linux-projects.org
-S: Maintained
-F: Documentation/video4linux/w9968cf.txt
-F: drivers/media/video/w996*
+F: drivers/media/usb/uvc/
USB WIRELESS RNDIS DRIVER (rndis_wlan)
M: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
@@ -7371,7 +7686,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git
W: http://royale.zerezo.com/zr364xx/
S: Maintained
F: Documentation/video4linux/zr364xx.txt
-F: drivers/media/video/zr364xx.c
+F: drivers/media/usb/zr364xx/
USER-MODE LINUX (UML)
M: Jeff Dike <jdike@addtoit.com>
@@ -7382,6 +7697,7 @@ W: http://user-mode-linux.sourceforge.net
S: Maintained
F: Documentation/virtual/uml/
F: arch/um/
+F: arch/x86/um/
F: fs/hostfs/
F: fs/hppfs/
@@ -7414,13 +7730,21 @@ S: Maintained
F: Documentation/filesystems/vfat.txt
F: fs/fat/
+VFIO DRIVER
+M: Alex Williamson <alex.williamson@redhat.com>
+L: kvm@vger.kernel.org
+S: Maintained
+F: Documentation/vfio.txt
+F: drivers/vfio/
+F: include/linux/vfio.h
+
VIDEOBUF2 FRAMEWORK
M: Pawel Osciak <pawel@osciak.com>
M: Marek Szyprowski <m.szyprowski@samsung.com>
M: Kyungmin Park <kyungmin.park@samsung.com>
L: linux-media@vger.kernel.org
S: Maintained
-F: drivers/media/video/videobuf2-*
+F: drivers/media/v4l2-core/videobuf2-*
F: include/media/videobuf2-*
VIRTIO CONSOLE DRIVER
@@ -7656,23 +7980,28 @@ S: Supported
F: Documentation/hwmon/wm83??
F: arch/arm/mach-s3c64xx/mach-crag6410*
F: drivers/clk/clk-wm83*.c
+F: drivers/extcon/extcon-arizona.c
F: drivers/leds/leds-wm83*.c
F: drivers/gpio/gpio-*wm*.c
+F: drivers/gpio/gpio-arizona.c
F: drivers/hwmon/wm83??-hwmon.c
F: drivers/input/misc/wm831x-on.c
F: drivers/input/touchscreen/wm831x-ts.c
F: drivers/input/touchscreen/wm97*.c
-F: drivers/mfd/wm8*.c
+F: drivers/mfd/arizona*
+F: drivers/mfd/wm*.c
F: drivers/power/wm83*.c
F: drivers/rtc/rtc-wm83*.c
F: drivers/regulator/wm8*.c
F: drivers/video/backlight/wm83*_bl.c
F: drivers/watchdog/wm83*_wdt.c
+F: include/linux/mfd/arizona/
F: include/linux/mfd/wm831x/
F: include/linux/mfd/wm8350/
F: include/linux/mfd/wm8400*
F: include/linux/wm97xx.h
F: include/sound/wm????.h
+F: sound/soc/codecs/arizona.?
F: sound/soc/codecs/wm*
WORKQUEUE
@@ -7817,7 +8146,7 @@ L: linux-media@vger.kernel.org
W: http://mjpeg.sourceforge.net/driver-zoran/
T: Mercurial http://linuxtv.org/hg/v4l-dvb
S: Odd Fixes
-F: drivers/media/video/zoran/
+F: drivers/media/pci/zoran/
ZS DECSTATION Z85C30 SERIAL DRIVER
M: "Maciej W. Rozycki" <macro@linux-mips.org>
diff --git a/Makefile b/Makefile
index 8e4c0a7d402b..bb9fff26f078 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
VERSION = 3
-PATCHLEVEL = 5
+PATCHLEVEL = 6
SUBLEVEL = 0
EXTRAVERSION =
-NAME = Saber-toothed Squirrel
+NAME = Terrified Chipmunk
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index d5b9b5e645cc..9944dedee5b1 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -18,6 +18,8 @@ config ALPHA
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_SMP_IDLE_THREAD
select GENERIC_CMOS_UPDATE
+ select GENERIC_STRNCPY_FROM_USER
+ select GENERIC_STRNLEN_USER
help
The Alpha is a 64-bit general-purpose processor designed and
marketed by the Digital Equipment Corporation of blessed memory,
diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h
index 3bb7ffeae3bc..c2cbe4fc391c 100644
--- a/arch/alpha/include/asm/atomic.h
+++ b/arch/alpha/include/asm/atomic.h
@@ -14,8 +14,8 @@
*/
-#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
-#define ATOMIC64_INIT(i) ( (atomic64_t) { (i) } )
+#define ATOMIC_INIT(i) { (i) }
+#define ATOMIC64_INIT(i) { (i) }
#define atomic_read(v) (*(volatile int *)&(v)->counter)
#define atomic64_read(v) (*(volatile long *)&(v)->counter)
diff --git a/arch/alpha/include/asm/fpu.h b/arch/alpha/include/asm/fpu.h
index db00f7885faa..e477bcd5b94a 100644
--- a/arch/alpha/include/asm/fpu.h
+++ b/arch/alpha/include/asm/fpu.h
@@ -1,7 +1,9 @@
#ifndef __ASM_ALPHA_FPU_H
#define __ASM_ALPHA_FPU_H
+#ifdef __KERNEL__
#include <asm/special_insns.h>
+#endif
/*
* Alpha floating-point control register defines:
diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index fd698a174f26..b87755a19554 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -76,7 +76,10 @@ struct switch_stack {
#define task_pt_regs(task) \
((struct pt_regs *) (task_stack_page(task) + 2*PAGE_SIZE) - 1)
-#define force_successful_syscall_return() (task_pt_regs(current)->r0 = 0)
+#define current_pt_regs() \
+ ((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1)
+
+#define force_successful_syscall_return() (current_pt_regs()->r0 = 0)
#endif
diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h
index dcb221a4b5be..7d2f75be932e 100644
--- a/arch/alpha/include/asm/socket.h
+++ b/arch/alpha/include/asm/socket.h
@@ -76,9 +76,11 @@
/* Instruct lower device to use last 4-bytes of skb data as FCS */
#define SO_NOFCS 43
+#ifdef __KERNEL__
/* O_NONBLOCK clashes with the bits used for socket types. Therefore we
* have to define SOCK_NONBLOCK to a different value here.
*/
#define SOCK_NONBLOCK 0x40000000
+#endif /* __KERNEL__ */
#endif /* _ASM_SOCKET_H */
diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h
index b49ec2f8d6e3..766fdfde2b7a 100644
--- a/arch/alpha/include/asm/uaccess.h
+++ b/arch/alpha/include/asm/uaccess.h
@@ -433,36 +433,12 @@ clear_user(void __user *to, long len)
#undef __module_address
#undef __module_call
-/* Returns: -EFAULT if exception before terminator, N if the entire
- buffer filled, else strlen. */
+#define user_addr_max() \
+ (segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL)
-extern long __strncpy_from_user(char *__to, const char __user *__from, long __to_len);
-
-extern inline long
-strncpy_from_user(char *to, const char __user *from, long n)
-{
- long ret = -EFAULT;
- if (__access_ok((unsigned long)from, 0, get_fs()))
- ret = __strncpy_from_user(to, from, n);
- return ret;
-}
-
-/* Returns: 0 if bad, string length+1 (memory size) of string if ok */
-extern long __strlen_user(const char __user *);
-
-extern inline long strlen_user(const char __user *str)
-{
- return access_ok(VERIFY_READ,str,0) ? __strlen_user(str) : 0;
-}
-
-/* Returns: 0 if exception before NUL or reaching the supplied limit (N),
- * a value greater than N if the limit would be exceeded, else strlen. */
-extern long __strnlen_user(const char __user *, long);
-
-extern inline long strnlen_user(const char __user *str, long n)
-{
- return access_ok(VERIFY_READ,str,0) ? __strnlen_user(str, n) : 0;
-}
+extern long strncpy_from_user(char *dest, const char __user *src, long count);
+extern __must_check long strlen_user(const char __user *str);
+extern __must_check long strnlen_user(const char __user *str, long n);
/*
* About the exception table:
diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h
index 633b23b0664a..a31a78eac9b9 100644
--- a/arch/alpha/include/asm/unistd.h
+++ b/arch/alpha/include/asm/unistd.h
@@ -465,10 +465,12 @@
#define __NR_setns 501
#define __NR_accept4 502
#define __NR_sendmmsg 503
+#define __NR_process_vm_readv 504
+#define __NR_process_vm_writev 505
#ifdef __KERNEL__
-#define NR_SYSCALLS 504
+#define NR_SYSCALLS 506
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_STAT64
diff --git a/arch/alpha/include/asm/word-at-a-time.h b/arch/alpha/include/asm/word-at-a-time.h
new file mode 100644
index 000000000000..6b340d0f1521
--- /dev/null
+++ b/arch/alpha/include/asm/word-at-a-time.h
@@ -0,0 +1,55 @@
+#ifndef _ASM_WORD_AT_A_TIME_H
+#define _ASM_WORD_AT_A_TIME_H
+
+#include <asm/compiler.h>
+
+/*
+ * word-at-a-time interface for Alpha.
+ */
+
+/*
+ * We do not use the word_at_a_time struct on Alpha, but it needs to be
+ * implemented to humour the generic code.
+ */
+struct word_at_a_time {
+ const unsigned long unused;
+};
+
+#define WORD_AT_A_TIME_CONSTANTS { 0 }
+
+/* Return nonzero if val has a zero */
+static inline unsigned long has_zero(unsigned long val, unsigned long *bits, const struct word_at_a_time *c)
+{
+ unsigned long zero_locations = __kernel_cmpbge(0, val);
+ *bits = zero_locations;
+ return zero_locations;
+}
+
+static inline unsigned long prep_zero_mask(unsigned long val, unsigned long bits, const struct word_at_a_time *c)
+{
+ return bits;
+}
+
+#define create_zero_mask(bits) (bits)
+
+static inline unsigned long find_zero(unsigned long bits)
+{
+#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67)
+ /* Simple if have CIX instructions */
+ return __kernel_cttz(bits);
+#else
+ unsigned long t1, t2, t3;
+ /* Retain lowest set bit only */
+ bits &= -bits;
+ /* Binary search for lowest set bit */
+ t1 = bits & 0xf0;
+ t2 = bits & 0xcc;
+ t3 = bits & 0xaa;
+ if (t1) t1 = 4;
+ if (t2) t2 = 2;
+ if (t3) t3 = 1;
+ return t1 + t2 + t3;
+#endif
+}
+
+#endif /* _ASM_WORD_AT_A_TIME_H */
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
index d96e742d4dc2..15fa821d09cd 100644
--- a/arch/alpha/kernel/alpha_ksyms.c
+++ b/arch/alpha/kernel/alpha_ksyms.c
@@ -52,7 +52,6 @@ EXPORT_SYMBOL(alpha_write_fp_reg_s);
/* entry.S */
EXPORT_SYMBOL(kernel_thread);
-EXPORT_SYMBOL(kernel_execve);
/* Networking helper routines. */
EXPORT_SYMBOL(csum_tcpudp_magic);
@@ -74,8 +73,6 @@ EXPORT_SYMBOL(alpha_fp_emul);
*/
EXPORT_SYMBOL(__copy_user);
EXPORT_SYMBOL(__do_clear_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__strnlen_user);
/*
* SMP-specific symbols.
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 6d159cee5f2f..ec0da0567ab5 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -663,58 +663,6 @@ kernel_thread:
br ret_to_kernel
.end kernel_thread
-/*
- * kernel_execve(path, argv, envp)
- */
- .align 4
- .globl kernel_execve
- .ent kernel_execve
-kernel_execve:
- /* We can be called from a module. */
- ldgp $gp, 0($27)
- lda $sp, -(32+SIZEOF_PT_REGS+8)($sp)
- .frame $sp, 32+SIZEOF_PT_REGS+8, $26, 0
- stq $26, 0($sp)
- stq $16, 8($sp)
- stq $17, 16($sp)
- stq $18, 24($sp)
- .prologue 1
-
- lda $16, 32($sp)
- lda $17, 0
- lda $18, SIZEOF_PT_REGS
- bsr $26, memset !samegp
-
- /* Avoid the HAE being gratuitously wrong, which would cause us
- to do the whole turn off interrupts thing and restore it. */
- ldq $2, alpha_mv+HAE_CACHE
- stq $2, 152+32($sp)
-
- ldq $16, 8($sp)
- ldq $17, 16($sp)
- ldq $18, 24($sp)
- lda $19, 32($sp)
- bsr $26, do_execve !samegp
-
- ldq $26, 0($sp)
- bne $0, 1f /* error! */
-
- /* Move the temporary pt_regs struct from its current location
- to the top of the kernel stack frame. See copy_thread for
- details for a normal process. */
- lda $16, 0x4000 - SIZEOF_PT_REGS($8)
- lda $17, 32($sp)
- lda $18, SIZEOF_PT_REGS
- bsr $26, memmove !samegp
-
- /* Take that over as our new stack frame and visit userland! */
- lda $sp, 0x4000 - SIZEOF_PT_REGS($8)
- br $31, ret_from_sys_call
-
-1: lda $sp, 32+SIZEOF_PT_REGS+8($sp)
- ret
-.end kernel_execve
-
/*
* Special system calls. Most of these are special in that they either
@@ -797,115 +745,6 @@ sys_rt_sigreturn:
.end sys_rt_sigreturn
.align 4
- .globl sys_sethae
- .ent sys_sethae
-sys_sethae:
- .prologue 0
- stq $16, 152($sp)
- ret
-.end sys_sethae
-
- .align 4
- .globl osf_getpriority
- .ent osf_getpriority
-osf_getpriority:
- lda $sp, -16($sp)
- stq $26, 0($sp)
- .prologue 0
-
- jsr $26, sys_getpriority
-
- ldq $26, 0($sp)
- blt $0, 1f
-
- /* Return value is the unbiased priority, i.e. 20 - prio.
- This does result in negative return values, so signal
- no error by writing into the R0 slot. */
- lda $1, 20
- stq $31, 16($sp)
- subl $1, $0, $0
- unop
-
-1: lda $sp, 16($sp)
- ret
-.end osf_getpriority
-
- .align 4
- .globl sys_getxuid
- .ent sys_getxuid
-sys_getxuid:
- .prologue 0
- ldq $2, TI_TASK($8)
- ldq $3, TASK_CRED($2)
- ldl $0, CRED_UID($3)
- ldl $1, CRED_EUID($3)
- stq $1, 80($sp)
- ret
-.end sys_getxuid
-
- .align 4
- .globl sys_getxgid
- .ent sys_getxgid
-sys_getxgid:
- .prologue 0
- ldq $2, TI_TASK($8)
- ldq $3, TASK_CRED($2)
- ldl $0, CRED_GID($3)
- ldl $1, CRED_EGID($3)
- stq $1, 80($sp)
- ret
-.end sys_getxgid
-
- .align 4
- .globl sys_getxpid
- .ent sys_getxpid
-sys_getxpid:
- .prologue 0
- ldq $2, TI_TASK($8)
-
- /* See linux/kernel/timer.c sys_getppid for discussion
- about this loop. */
- ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
- ldl $0, TASK_TGID($2)
-1: ldl $1, TASK_TGID($4)
-#ifdef CONFIG_SMP
- mov $4, $5
- mb
- ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
- cmpeq $4, $5, $5
- beq $5, 1b
-#endif
- stq $1, 80($sp)
- ret
-.end sys_getxpid
-
- .align 4
- .globl sys_alpha_pipe
- .ent sys_alpha_pipe
-sys_alpha_pipe:
- lda $sp, -16($sp)
- stq $26, 0($sp)
- .prologue 0
-
- mov $31, $17
- lda $16, 8($sp)
- jsr $26, do_pipe_flags
-
- ldq $26, 0($sp)
- bne $0, 1f
-
- /* The return values are in $0 and $20. */
- ldl $1, 12($sp)
- ldl $0, 8($sp)
-
- stq $1, 80+16($sp)
-1: lda $sp, 16($sp)
- ret
-.end sys_alpha_pipe
-
- .align 4
.globl sys_execve
.ent sys_execve
sys_execve:
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 98a103621af6..bc1acdda7a5e 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1404,3 +1404,52 @@ SYSCALL_DEFINE3(osf_writev, unsigned long, fd,
}
#endif
+
+SYSCALL_DEFINE2(osf_getpriority, int, which, int, who)
+{
+ int prio = sys_getpriority(which, who);
+ if (prio >= 0) {
+ /* Return value is the unbiased priority, i.e. 20 - prio.
+ This does result in negative return values, so signal
+ no error */
+ force_successful_syscall_return();
+ prio = 20 - prio;
+ }
+ return prio;
+}
+
+SYSCALL_DEFINE0(getxuid)
+{
+ current_pt_regs()->r20 = sys_geteuid();
+ return sys_getuid();
+}
+
+SYSCALL_DEFINE0(getxgid)
+{
+ current_pt_regs()->r20 = sys_getegid();
+ return sys_getgid();
+}
+
+SYSCALL_DEFINE0(getxpid)
+{
+ current_pt_regs()->r20 = sys_getppid();
+ return sys_getpid();
+}
+
+SYSCALL_DEFINE0(alpha_pipe)
+{
+ int fd[2];
+ int res = do_pipe_flags(fd, 0);
+ if (!res) {
+ /* The return values are in $0 and $20. */
+ current_pt_regs()->r20 = fd[1];
+ res = fd[0];
+ }
+ return res;
+}
+
+SYSCALL_DEFINE1(sethae, unsigned long, val)
+{
+ current_pt_regs()->hae = val;
+ return 0;
+}
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index 153d3fce3e8e..d6fde98b74b3 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -455,3 +455,22 @@ get_wchan(struct task_struct *p)
}
return pc;
}
+
+int kernel_execve(const char *path, const char *const argv[], const char *const envp[])
+{
+ /* Avoid the HAE being gratuitously wrong, which would cause us
+ to do the whole turn off interrupts thing and restore it. */
+ struct pt_regs regs = {.hae = alpha_mv.hae_cache};
+ int err = do_execve(path, argv, envp, &regs);
+ if (!err) {
+ struct pt_regs *p = current_pt_regs();
+ /* copy regs to normal position and off to userland we go... */
+ *p = regs;
+ __asm__ __volatile__ (
+ "mov %0, $sp;"
+ "br $31, ret_from_sys_call"
+ : : "r"(p));
+ }
+ return err;
+}
+EXPORT_SYMBOL(kernel_execve);
diff --git a/arch/alpha/kernel/systbls.S b/arch/alpha/kernel/systbls.S
index 87835235f114..2ac6b45c3e00 100644
--- a/arch/alpha/kernel/systbls.S
+++ b/arch/alpha/kernel/systbls.S
@@ -111,7 +111,7 @@ sys_call_table:
.quad sys_socket
.quad sys_connect
.quad sys_accept
- .quad osf_getpriority /* 100 */
+ .quad sys_osf_getpriority /* 100 */
.quad sys_send
.quad sys_recv
.quad sys_sigreturn
@@ -522,6 +522,8 @@ sys_call_table:
.quad sys_setns
.quad sys_accept4
.quad sys_sendmmsg
+ .quad sys_process_vm_readv
+ .quad sys_process_vm_writev /* 505 */
.size sys_call_table, . - sys_call_table
.type sys_call_table, @object
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
index c0a83ab62b78..59660743237c 100644
--- a/arch/alpha/lib/Makefile
+++ b/arch/alpha/lib/Makefile
@@ -31,8 +31,6 @@ lib-y = __divqu.o __remqu.o __divlu.o __remlu.o \
$(ev6-y)memchr.o \
$(ev6-y)copy_user.o \
$(ev6-y)clear_user.o \
- $(ev6-y)strncpy_from_user.o \
- $(ev67-y)strlen_user.o \
$(ev6-y)csum_ipv6_magic.o \
$(ev6-y)clear_page.o \
$(ev6-y)copy_page.o \
diff --git a/arch/alpha/lib/ev6-strncpy_from_user.S b/arch/alpha/lib/ev6-strncpy_from_user.S
deleted file mode 100644
index d2e28178cacc..000000000000
--- a/arch/alpha/lib/ev6-strncpy_from_user.S
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * arch/alpha/lib/ev6-strncpy_from_user.S
- * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
- *
- * Just like strncpy except in the return value:
- *
- * -EFAULT if an exception occurs before the terminator is copied.
- * N if the buffer filled.
- *
- * Otherwise the length of the string is returned.
- *
- * Much of the information about 21264 scheduling/coding comes from:
- * Compiler Writer's Guide for the Alpha 21264
- * abbreviated as 'CWG' in other comments here
- * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
- * Scheduling notation:
- * E - either cluster
- * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
- * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
- * A bunch of instructions got moved and temp registers were changed
- * to aid in scheduling. Control flow was also re-arranged to eliminate
- * branches, and to provide longer code sequences to enable better scheduling.
- * A total rewrite (using byte load/stores for start & tail sequences)
- * is desirable, but very difficult to do without a from-scratch rewrite.
- * Save that for the future.
- */
-
-
-#include <asm/errno.h>
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda $31, $exception-99b($0); \
- .previous
-
-
- .set noat
- .set noreorder
- .text
-
- .globl __strncpy_from_user
- .ent __strncpy_from_user
- .frame $30, 0, $26
- .prologue 0
-
- .align 4
-__strncpy_from_user:
- and a0, 7, t3 # E : find dest misalignment
- beq a2, $zerolength # U :
-
- /* Are source and destination co-aligned? */
- mov a0, v0 # E : save the string start
- xor a0, a1, t4 # E :
- EX( ldq_u t1, 0(a1) ) # L : Latency=3 load first quadword
- ldq_u t0, 0(a0) # L : load first (partial) aligned dest quadword
-
- addq a2, t3, a2 # E : bias count by dest misalignment
- subq a2, 1, a3 # E :
- addq zero, 1, t10 # E :
- and t4, 7, t4 # E : misalignment between the two
-
- and a3, 7, t6 # E : number of tail bytes
- sll t10, t6, t10 # E : t10 = bitmask of last count byte
- bne t4, $unaligned # U :
- lda t2, -1 # E : build a mask against false zero
-
- /*
- * We are co-aligned; take care of a partial first word.
- * On entry to this basic block:
- * t0 == the first destination word for masking back in
- * t1 == the first source word.
- */
-
- srl a3, 3, a2 # E : a2 = loop counter = (count - 1)/8
- addq a1, 8, a1 # E :
- mskqh t2, a1, t2 # U : detection in the src word
- nop
-
- /* Create the 1st output word and detect 0's in the 1st input word. */
- mskqh t1, a1, t3 # U :
- mskql t0, a1, t0 # U : assemble the first output word
- ornot t1, t2, t2 # E :
- nop
-
- cmpbge zero, t2, t8 # E : bits set iff null found
- or t0, t3, t0 # E :
- beq a2, $a_eoc # U :
- bne t8, $a_eos # U : 2nd branch in a quad. Bad.
-
- /* On entry to this basic block:
- * t0 == a source quad not containing a null.
- * a0 - current aligned destination address
- * a1 - current aligned source address
- * a2 - count of quadwords to move.
- * NOTE: Loop improvement - unrolling this is going to be
- * a huge win, since we're going to stall otherwise.
- * Fix this later. For _really_ large copies, look
- * at using wh64 on a look-ahead basis. See the code
- * in clear_user.S and copy_user.S.
- * Presumably, since (a0) and (a1) do not overlap (by C definition)
- * Lots of nops here:
- * - Separate loads from stores
- * - Keep it to 1 branch/quadpack so the branch predictor
- * can train.
- */
-$a_loop:
- stq_u t0, 0(a0) # L :
- addq a0, 8, a0 # E :
- nop
- subq a2, 1, a2 # E :
-
- EX( ldq_u t0, 0(a1) ) # L :
- addq a1, 8, a1 # E :
- cmpbge zero, t0, t8 # E : Stall 2 cycles on t0
- beq a2, $a_eoc # U :
-
- beq t8, $a_loop # U :
- nop
- nop
- nop
-
- /* Take care of the final (partial) word store. At this point
- * the end-of-count bit is set in t8 iff it applies.
- *
- * On entry to this basic block we have:
- * t0 == the source word containing the null
- * t8 == the cmpbge mask that found it.
- */
-$a_eos:
- negq t8, t12 # E : find low bit set
- and t8, t12, t12 # E :
-
- /* We're doing a partial word store and so need to combine
- our source and original destination words. */
- ldq_u t1, 0(a0) # L :
- subq t12, 1, t6 # E :
-
- or t12, t6, t8 # E :
- zapnot t0, t8, t0 # U : clear src bytes > null
- zap t1, t8, t1 # U : clear dst bytes <= null
- or t0, t1, t0 # E :
-
- stq_u t0, 0(a0) # L :
- br $finish_up # L0 :
- nop
- nop
-
- /* Add the end-of-count bit to the eos detection bitmask. */
- .align 4
-$a_eoc:
- or t10, t8, t8
- br $a_eos
- nop
- nop
-
-
-/* The source and destination are not co-aligned. Align the destination
- and cope. We have to be very careful about not reading too much and
- causing a SEGV. */
-
- .align 4
-$u_head:
- /* We know just enough now to be able to assemble the first
- full source word. We can still find a zero at the end of it
- that prevents us from outputting the whole thing.
-
- On entry to this basic block:
- t0 == the first dest word, unmasked
- t1 == the shifted low bits of the first source word
- t6 == bytemask that is -1 in dest word bytes */
-
- EX( ldq_u t2, 8(a1) ) # L : load second src word
- addq a1, 8, a1 # E :
- mskql t0, a0, t0 # U : mask trailing garbage in dst
- extqh t2, a1, t4 # U :
-
- or t1, t4, t1 # E : first aligned src word complete
- mskqh t1, a0, t1 # U : mask leading garbage in src
- or t0, t1, t0 # E : first output word complete
- or t0, t6, t6 # E : mask original data for zero test
-
- cmpbge zero, t6, t8 # E :
- beq a2, $u_eocfin # U :
- bne t8, $u_final # U : bad news - 2nd branch in a quad
- lda t6, -1 # E : mask out the bits we have
-
- mskql t6, a1, t6 # U : already seen
- stq_u t0, 0(a0) # L : store first output word
- or t6, t2, t2 # E :
- cmpbge zero, t2, t8 # E : find nulls in second partial
-
- addq a0, 8, a0 # E :
- subq a2, 1, a2 # E :
- bne t8, $u_late_head_exit # U :
- nop
-
- /* Finally, we've got all the stupid leading edge cases taken care
- of and we can set up to enter the main loop. */
-
- extql t2, a1, t1 # U : position hi-bits of lo word
- EX( ldq_u t2, 8(a1) ) # L : read next high-order source word
- addq a1, 8, a1 # E :
- cmpbge zero, t2, t8 # E :
-
- beq a2, $u_eoc # U :
- bne t8, $u_eos # U :
- nop
- nop
-
- /* Unaligned copy main loop. In order to avoid reading too much,
- the loop is structured to detect zeros in aligned source words.
- This has, unfortunately, effectively pulled half of a loop
- iteration out into the head and half into the tail, but it does
- prevent nastiness from accumulating in the very thing we want
- to run as fast as possible.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word
-
- We further know that t2 does not contain a null terminator. */
-
- /*
- * Extra nops here:
- * separate load quads from store quads
- * only one branch/quad to permit predictor training
- */
-
- .align 4
-$u_loop:
- extqh t2, a1, t0 # U : extract high bits for current word
- addq a1, 8, a1 # E :
- extql t2, a1, t3 # U : extract low bits for next time
- addq a0, 8, a0 # E :
-
- or t0, t1, t0 # E : current dst word now complete
- EX( ldq_u t2, 0(a1) ) # L : load high word for next time
- subq a2, 1, a2 # E :
- nop
-
- stq_u t0, -8(a0) # L : save the current word
- mov t3, t1 # E :
- cmpbge zero, t2, t8 # E : test new word for eos
- beq a2, $u_eoc # U :
-
- beq t8, $u_loop # U :
- nop
- nop
- nop
-
- /* We've found a zero somewhere in the source word we just read.
- If it resides in the lower half, we have one (probably partial)
- word to write out, and if it resides in the upper half, we
- have one full and one partial word left to write out.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word. */
- .align 4
-$u_eos:
- extqh t2, a1, t0 # U :
- or t0, t1, t0 # E : first (partial) source word complete
- cmpbge zero, t0, t8 # E : is the null in this first bit?
- nop
-
- bne t8, $u_final # U :
- stq_u t0, 0(a0) # L : the null was in the high-order bits
- addq a0, 8, a0 # E :
- subq a2, 1, a2 # E :
-
- .align 4
-$u_late_head_exit:
- extql t2, a1, t0 # U :
- cmpbge zero, t0, t8 # E :
- or t8, t10, t6 # E :
- cmoveq a2, t6, t8 # E :
-
- /* Take care of a final (probably partial) result word.
- On entry to this basic block:
- t0 == assembled source word
- t8 == cmpbge mask that found the null. */
- .align 4
-$u_final:
- negq t8, t6 # E : isolate low bit set
- and t6, t8, t12 # E :
- ldq_u t1, 0(a0) # L :
- subq t12, 1, t6 # E :
-
- or t6, t12, t8 # E :
- zapnot t0, t8, t0 # U : kill source bytes > null
- zap t1, t8, t1 # U : kill dest bytes <= null
- or t0, t1, t0 # E :
-
- stq_u t0, 0(a0) # E :
- br $finish_up # U :
- nop
- nop
-
- .align 4
-$u_eoc: # end-of-count
- extqh t2, a1, t0 # U :
- or t0, t1, t0 # E :
- cmpbge zero, t0, t8 # E :
- nop
-
- .align 4
-$u_eocfin: # end-of-count, final word
- or t10, t8, t8 # E :
- br $u_final # U :
- nop
- nop
-
- /* Unaligned copy entry point. */
- .align 4
-$unaligned:
-
- srl a3, 3, a2 # U : a2 = loop counter = (count - 1)/8
- and a0, 7, t4 # E : find dest misalignment
- and a1, 7, t5 # E : find src misalignment
- mov zero, t0 # E :
-
- /* Conditionally load the first destination word and a bytemask
- with 0xff indicating that the destination byte is sacrosanct. */
-
- mov zero, t6 # E :
- beq t4, 1f # U :
- ldq_u t0, 0(a0) # L :
- lda t6, -1 # E :
-
- mskql t6, a0, t6 # E :
- nop
- nop
- nop
-
- .align 4
-1:
- subq a1, t4, a1 # E : sub dest misalignment from src addr
- /* If source misalignment is larger than dest misalignment, we need
- extra startup checks to avoid SEGV. */
- cmplt t4, t5, t12 # E :
- extql t1, a1, t1 # U : shift src into place
- lda t2, -1 # E : for creating masks later
-
- beq t12, $u_head # U :
- mskqh t2, t5, t2 # U : begin src byte validity mask
- cmpbge zero, t1, t8 # E : is there a zero?
- nop
-
- extql t2, a1, t2 # U :
- or t8, t10, t5 # E : test for end-of-count too
- cmpbge zero, t2, t3 # E :
- cmoveq a2, t5, t8 # E : Latency=2, extra map slot
-
- nop # E : goes with cmov
- andnot t8, t3, t8 # E :
- beq t8, $u_head # U :
- nop
-
- /* At this point we've found a zero in the first partial word of
- the source. We need to isolate the valid source data and mask
- it into the original destination data. (Incidentally, we know
- that we'll need at least one byte of that original dest word.) */
-
- ldq_u t0, 0(a0) # L :
- negq t8, t6 # E : build bitmask of bytes <= zero
- mskqh t1, t4, t1 # U :
- and t6, t8, t12 # E :
-
- subq t12, 1, t6 # E :
- or t6, t12, t8 # E :
- zapnot t2, t8, t2 # U : prepare source word; mirror changes
- zapnot t1, t8, t1 # U : to source validity mask
-
- andnot t0, t2, t0 # E : zero place for source to reside
- or t0, t1, t0 # E : and put it there
- stq_u t0, 0(a0) # L :
- nop
-
- .align 4
-$finish_up:
- zapnot t0, t12, t4 # U : was last byte written null?
- and t12, 0xf0, t3 # E : binary search for the address of the
- cmovne t4, 1, t4 # E : Latency=2, extra map slot
- nop # E : with cmovne
-
- and t12, 0xcc, t2 # E : last byte written
- and t12, 0xaa, t1 # E :
- cmovne t3, 4, t3 # E : Latency=2, extra map slot
- nop # E : with cmovne
-
- bic a0, 7, t0
- cmovne t2, 2, t2 # E : Latency=2, extra map slot
- nop # E : with cmovne
- nop
-
- cmovne t1, 1, t1 # E : Latency=2, extra map slot
- nop # E : with cmovne
- addq t0, t3, t0 # E :
- addq t1, t2, t1 # E :
-
- addq t0, t1, t0 # E :
- addq t0, t4, t0 # add one if we filled the buffer
- subq t0, v0, v0 # find string length
- ret # L0 :
-
- .align 4
-$zerolength:
- nop
- nop
- nop
- clr v0
-
-$exception:
- nop
- nop
- nop
- ret
-
- .end __strncpy_from_user
diff --git a/arch/alpha/lib/ev67-strlen_user.S b/arch/alpha/lib/ev67-strlen_user.S
deleted file mode 100644
index 57e0d77b81a6..000000000000
--- a/arch/alpha/lib/ev67-strlen_user.S
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * arch/alpha/lib/ev67-strlen_user.S
- * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com>
- *
- * Return the length of the string including the NULL terminator
- * (strlen+1) or zero if an error occurred.
- *
- * In places where it is critical to limit the processing time,
- * and the data is not trusted, strnlen_user() should be used.
- * It will return a value greater than its second argument if
- * that limit would be exceeded. This implementation is allowed
- * to access memory beyond the limit, but will not cross a page
- * boundary when doing so.
- *
- * Much of the information about 21264 scheduling/coding comes from:
- * Compiler Writer's Guide for the Alpha 21264
- * abbreviated as 'CWG' in other comments here
- * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
- * Scheduling notation:
- * E - either cluster
- * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
- * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
- * Try not to change the actual algorithm if possible for consistency.
- */
-
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda v0, $exception-99b(zero); \
- .previous
-
-
- .set noreorder
- .set noat
- .text
-
- .globl __strlen_user
- .ent __strlen_user
- .frame sp, 0, ra
-
- .align 4
-__strlen_user:
- ldah a1, 32767(zero) # do not use plain strlen_user() for strings
- # that might be almost 2 GB long; you should
- # be using strnlen_user() instead
- nop
- nop
- nop
-
- .globl __strnlen_user
-
- .align 4
-__strnlen_user:
- .prologue 0
- EX( ldq_u t0, 0(a0) ) # L : load first quadword (a0 may be misaligned)
- lda t1, -1(zero) # E :
-
- insqh t1, a0, t1 # U :
- andnot a0, 7, v0 # E :
- or t1, t0, t0 # E :
- subq a0, 1, a0 # E : get our +1 for the return
-
- cmpbge zero, t0, t1 # E : t1 <- bitmask: bit i == 1 <==> i-th byte == 0
- subq a1, 7, t2 # E :
- subq a0, v0, t0 # E :
- bne t1, $found # U :
-
- addq t2, t0, t2 # E :
- addq a1, 1, a1 # E :
- nop # E :
- nop # E :
-
- .align 4
-$loop: ble t2, $limit # U :
- EX( ldq t0, 8(v0) ) # L :
- nop # E :
- nop # E :
-
- cmpbge zero, t0, t1 # E :
- subq t2, 8, t2 # E :
- addq v0, 8, v0 # E : addr += 8
- beq t1, $loop # U :
-
-$found: cttz t1, t2 # U0 :
- addq v0, t2, v0 # E :
- subq v0, a0, v0 # E :
- ret # L0 :
-
-$exception:
- nop
- nop
- nop
- ret
-
- .align 4 # currently redundant
-$limit:
- nop
- nop
- subq a1, t2, v0
- ret
-
- .end __strlen_user
diff --git a/arch/alpha/lib/strlen_user.S b/arch/alpha/lib/strlen_user.S
deleted file mode 100644
index 508a18e96479..000000000000
--- a/arch/alpha/lib/strlen_user.S
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * arch/alpha/lib/strlen_user.S
- *
- * Return the length of the string including the NUL terminator
- * (strlen+1) or zero if an error occurred.
- *
- * In places where it is critical to limit the processing time,
- * and the data is not trusted, strnlen_user() should be used.
- * It will return a value greater than its second argument if
- * that limit would be exceeded. This implementation is allowed
- * to access memory beyond the limit, but will not cross a page
- * boundary when doing so.
- */
-
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda v0, $exception-99b(zero); \
- .previous
-
-
- .set noreorder
- .set noat
- .text
-
- .globl __strlen_user
- .ent __strlen_user
- .frame sp, 0, ra
-
- .align 3
-__strlen_user:
- ldah a1, 32767(zero) # do not use plain strlen_user() for strings
- # that might be almost 2 GB long; you should
- # be using strnlen_user() instead
-
- .globl __strnlen_user
-
- .align 3
-__strnlen_user:
- .prologue 0
-
- EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned)
- lda t1, -1(zero)
- insqh t1, a0, t1
- andnot a0, 7, v0
- or t1, t0, t0
- subq a0, 1, a0 # get our +1 for the return
- cmpbge zero, t0, t1 # t1 <- bitmask: bit i == 1 <==> i-th byte == 0
- subq a1, 7, t2
- subq a0, v0, t0
- bne t1, $found
-
- addq t2, t0, t2
- addq a1, 1, a1
-
- .align 3
-$loop: ble t2, $limit
- EX( ldq t0, 8(v0) )
- subq t2, 8, t2
- addq v0, 8, v0 # addr += 8
- cmpbge zero, t0, t1
- beq t1, $loop
-
-$found: negq t1, t2 # clear all but least set bit
- and t1, t2, t1
-
- and t1, 0xf0, t2 # binary search for that set bit
- and t1, 0xcc, t3
- and t1, 0xaa, t4
- cmovne t2, 4, t2
- cmovne t3, 2, t3
- cmovne t4, 1, t4
- addq t2, t3, t2
- addq v0, t4, v0
- addq v0, t2, v0
- nop # dual issue next two on ev4 and ev5
- subq v0, a0, v0
-$exception:
- ret
-
- .align 3 # currently redundant
-$limit:
- subq a1, t2, v0
- ret
-
- .end __strlen_user
diff --git a/arch/alpha/lib/strncpy_from_user.S b/arch/alpha/lib/strncpy_from_user.S
deleted file mode 100644
index 73ee21160ff7..000000000000
--- a/arch/alpha/lib/strncpy_from_user.S
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * arch/alpha/lib/strncpy_from_user.S
- * Contributed by Richard Henderson (rth@tamu.edu)
- *
- * Just like strncpy except in the return value:
- *
- * -EFAULT if an exception occurs before the terminator is copied.
- * N if the buffer filled.
- *
- * Otherwise the length of the string is returned.
- */
-
-
-#include <asm/errno.h>
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda $31, $exception-99b($0); \
- .previous
-
-
- .set noat
- .set noreorder
- .text
-
- .globl __strncpy_from_user
- .ent __strncpy_from_user
- .frame $30, 0, $26
- .prologue 0
-
- .align 3
-$aligned:
- /* On entry to this basic block:
- t0 == the first destination word for masking back in
- t1 == the first source word. */
-
- /* Create the 1st output word and detect 0's in the 1st input word. */
- lda t2, -1 # e1 : build a mask against false zero
- mskqh t2, a1, t2 # e0 : detection in the src word
- mskqh t1, a1, t3 # e0 :
- ornot t1, t2, t2 # .. e1 :
- mskql t0, a1, t0 # e0 : assemble the first output word
- cmpbge zero, t2, t8 # .. e1 : bits set iff null found
- or t0, t3, t0 # e0 :
- beq a2, $a_eoc # .. e1 :
- bne t8, $a_eos # .. e1 :
-
- /* On entry to this basic block:
- t0 == a source word not containing a null. */
-
-$a_loop:
- stq_u t0, 0(a0) # e0 :
- addq a0, 8, a0 # .. e1 :
- EX( ldq_u t0, 0(a1) ) # e0 :
- addq a1, 8, a1 # .. e1 :
- subq a2, 1, a2 # e0 :
- cmpbge zero, t0, t8 # .. e1 (stall)
- beq a2, $a_eoc # e1 :
- beq t8, $a_loop # e1 :
-
- /* Take care of the final (partial) word store. At this point
- the end-of-count bit is set in t8 iff it applies.
-
- On entry to this basic block we have:
- t0 == the source word containing the null
- t8 == the cmpbge mask that found it. */
-
-$a_eos:
- negq t8, t12 # e0 : find low bit set
- and t8, t12, t12 # e1 (stall)
-
- /* For the sake of the cache, don't read a destination word
- if we're not going to need it. */
- and t12, 0x80, t6 # e0 :
- bne t6, 1f # .. e1 (zdb)
-
- /* We're doing a partial word store and so need to combine
- our source and original destination words. */
- ldq_u t1, 0(a0) # e0 :
- subq t12, 1, t6 # .. e1 :
- or t12, t6, t8 # e0 :
- unop #
- zapnot t0, t8, t0 # e0 : clear src bytes > null
- zap t1, t8, t1 # .. e1 : clear dst bytes <= null
- or t0, t1, t0 # e1 :
-
-1: stq_u t0, 0(a0)
- br $finish_up
-
- /* Add the end-of-count bit to the eos detection bitmask. */
-$a_eoc:
- or t10, t8, t8
- br $a_eos
-
- /*** The Function Entry Point ***/
- .align 3
-__strncpy_from_user:
- mov a0, v0 # save the string start
- beq a2, $zerolength
-
- /* Are source and destination co-aligned? */
- xor a0, a1, t1 # e0 :
- and a0, 7, t0 # .. e1 : find dest misalignment
- and t1, 7, t1 # e0 :
- addq a2, t0, a2 # .. e1 : bias count by dest misalignment
- subq a2, 1, a2 # e0 :
- and a2, 7, t2 # e1 :
- srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8
- addq zero, 1, t10 # .. e1 :
- sll t10, t2, t10 # e0 : t10 = bitmask of last count byte
- bne t1, $unaligned # .. e1 :
-
- /* We are co-aligned; take care of a partial first word. */
-
- EX( ldq_u t1, 0(a1) ) # e0 : load first src word
- addq a1, 8, a1 # .. e1 :
-
- beq t0, $aligned # avoid loading dest word if not needed
- ldq_u t0, 0(a0) # e0 :
- br $aligned # .. e1 :
-
-
-/* The source and destination are not co-aligned. Align the destination
- and cope. We have to be very careful about not reading too much and
- causing a SEGV. */
-
- .align 3
-$u_head:
- /* We know just enough now to be able to assemble the first
- full source word. We can still find a zero at the end of it
- that prevents us from outputting the whole thing.
-
- On entry to this basic block:
- t0 == the first dest word, unmasked
- t1 == the shifted low bits of the first source word
- t6 == bytemask that is -1 in dest word bytes */
-
- EX( ldq_u t2, 8(a1) ) # e0 : load second src word
- addq a1, 8, a1 # .. e1 :
- mskql t0, a0, t0 # e0 : mask trailing garbage in dst
- extqh t2, a1, t4 # e0 :
- or t1, t4, t1 # e1 : first aligned src word complete
- mskqh t1, a0, t1 # e0 : mask leading garbage in src
- or t0, t1, t0 # e0 : first output word complete
- or t0, t6, t6 # e1 : mask original data for zero test
- cmpbge zero, t6, t8 # e0 :
- beq a2, $u_eocfin # .. e1 :
- bne t8, $u_final # e1 :
-
- lda t6, -1 # e1 : mask out the bits we have
- mskql t6, a1, t6 # e0 : already seen
- stq_u t0, 0(a0) # e0 : store first output word
- or t6, t2, t2 # .. e1 :
- cmpbge zero, t2, t8 # e0 : find nulls in second partial
- addq a0, 8, a0 # .. e1 :
- subq a2, 1, a2 # e0 :
- bne t8, $u_late_head_exit # .. e1 :
-
- /* Finally, we've got all the stupid leading edge cases taken care
- of and we can set up to enter the main loop. */
-
- extql t2, a1, t1 # e0 : position hi-bits of lo word
- EX( ldq_u t2, 8(a1) ) # .. e1 : read next high-order source word
- addq a1, 8, a1 # e0 :
- cmpbge zero, t2, t8 # e1 (stall)
- beq a2, $u_eoc # e1 :
- bne t8, $u_eos # e1 :
-
- /* Unaligned copy main loop. In order to avoid reading too much,
- the loop is structured to detect zeros in aligned source words.
- This has, unfortunately, effectively pulled half of a loop
- iteration out into the head and half into the tail, but it does
- prevent nastiness from accumulating in the very thing we want
- to run as fast as possible.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word
-
- We further know that t2 does not contain a null terminator. */
-
- .align 3
-$u_loop:
- extqh t2, a1, t0 # e0 : extract high bits for current word
- addq a1, 8, a1 # .. e1 :
- extql t2, a1, t3 # e0 : extract low bits for next time
- addq a0, 8, a0 # .. e1 :
- or t0, t1, t0 # e0 : current dst word now complete
- EX( ldq_u t2, 0(a1) ) # .. e1 : load high word for next time
- stq_u t0, -8(a0) # e0 : save the current word
- mov t3, t1 # .. e1 :
- subq a2, 1, a2 # e0 :
- cmpbge zero, t2, t8 # .. e1 : test new word for eos
- beq a2, $u_eoc # e1 :
- beq t8, $u_loop # e1 :
-
- /* We've found a zero somewhere in the source word we just read.
- If it resides in the lower half, we have one (probably partial)
- word to write out, and if it resides in the upper half, we
- have one full and one partial word left to write out.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word. */
-$u_eos:
- extqh t2, a1, t0 # e0 :
- or t0, t1, t0 # e1 : first (partial) source word complete
-
- cmpbge zero, t0, t8 # e0 : is the null in this first bit?
- bne t8, $u_final # .. e1 (zdb)
-
- stq_u t0, 0(a0) # e0 : the null was in the high-order bits
- addq a0, 8, a0 # .. e1 :
- subq a2, 1, a2 # e1 :
-
-$u_late_head_exit:
- extql t2, a1, t0 # .. e0 :
- cmpbge zero, t0, t8 # e0 :
- or t8, t10, t6 # e1 :
- cmoveq a2, t6, t8 # e0 :
- nop # .. e1 :
-
- /* Take care of a final (probably partial) result word.
- On entry to this basic block:
- t0 == assembled source word
- t8 == cmpbge mask that found the null. */
-$u_final:
- negq t8, t6 # e0 : isolate low bit set
- and t6, t8, t12 # e1 :
-
- and t12, 0x80, t6 # e0 : avoid dest word load if we can
- bne t6, 1f # .. e1 (zdb)
-
- ldq_u t1, 0(a0) # e0 :
- subq t12, 1, t6 # .. e1 :
- or t6, t12, t8 # e0 :
- zapnot t0, t8, t0 # .. e1 : kill source bytes > null
- zap t1, t8, t1 # e0 : kill dest bytes <= null
- or t0, t1, t0 # e1 :
-
-1: stq_u t0, 0(a0) # e0 :
- br $finish_up
-
-$u_eoc: # end-of-count
- extqh t2, a1, t0
- or t0, t1, t0
- cmpbge zero, t0, t8
-
-$u_eocfin: # end-of-count, final word
- or t10, t8, t8
- br $u_final
-
- /* Unaligned copy entry point. */
- .align 3
-$unaligned:
-
- EX( ldq_u t1, 0(a1) ) # e0 : load first source word
-
- and a0, 7, t4 # .. e1 : find dest misalignment
- and a1, 7, t5 # e0 : find src misalignment
-
- /* Conditionally load the first destination word and a bytemask
- with 0xff indicating that the destination byte is sacrosanct. */
-
- mov zero, t0 # .. e1 :
- mov zero, t6 # e0 :
- beq t4, 1f # .. e1 :
- ldq_u t0, 0(a0) # e0 :
- lda t6, -1 # .. e1 :
- mskql t6, a0, t6 # e0 :
-1:
- subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr
-
- /* If source misalignment is larger than dest misalignment, we need
- extra startup checks to avoid SEGV. */
-
- cmplt t4, t5, t12 # e1 :
- extql t1, a1, t1 # .. e0 : shift src into place
- lda t2, -1 # e0 : for creating masks later
- beq t12, $u_head # e1 :
-
- mskqh t2, t5, t2 # e0 : begin src byte validity mask
- cmpbge zero, t1, t8 # .. e1 : is there a zero?
- extql t2, a1, t2 # e0 :
- or t8, t10, t5 # .. e1 : test for end-of-count too
- cmpbge zero, t2, t3 # e0 :
- cmoveq a2, t5, t8 # .. e1 :
- andnot t8, t3, t8 # e0 :
- beq t8, $u_head # .. e1 (zdb)
-
- /* At this point we've found a zero in the first partial word of
- the source. We need to isolate the valid source data and mask
- it into the original destination data. (Incidentally, we know
- that we'll need at least one byte of that original dest word.) */
-
- ldq_u t0, 0(a0) # e0 :
- negq t8, t6 # .. e1 : build bitmask of bytes <= zero
- mskqh t1, t4, t1 # e0 :
- and t6, t8, t12 # .. e1 :
- subq t12, 1, t6 # e0 :
- or t6, t12, t8 # e1 :
-
- zapnot t2, t8, t2 # e0 : prepare source word; mirror changes
- zapnot t1, t8, t1 # .. e1 : to source validity mask
-
- andnot t0, t2, t0 # e0 : zero place for source to reside
- or t0, t1, t0 # e1 : and put it there
- stq_u t0, 0(a0) # e0 :
-
-$finish_up:
- zapnot t0, t12, t4 # was last byte written null?
- cmovne t4, 1, t4
-
- and t12, 0xf0, t3 # binary search for the address of the
- and t12, 0xcc, t2 # last byte written
- and t12, 0xaa, t1
- bic a0, 7, t0
- cmovne t3, 4, t3
- cmovne t2, 2, t2
- cmovne t1, 1, t1
- addq t0, t3, t0
- addq t1, t2, t1
- addq t0, t1, t0
- addq t0, t4, t0 # add one if we filled the buffer
-
- subq t0, v0, v0 # find string length
- ret
-
-$zerolength:
- clr v0
-$exception:
- ret
-
- .end __strncpy_from_user
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index 5eecab1a84ef..0c4132dd3507 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -89,6 +89,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
const struct exception_table_entry *fixup;
int fault, si_code = SEGV_MAPERR;
siginfo_t info;
+ unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+ (cause > 0 ? FAULT_FLAG_WRITE : 0));
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
(or is suppressed by the PALcode). Support that for older CPUs
@@ -114,6 +116,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
goto vmalloc_fault;
#endif
+retry:
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
@@ -144,8 +147,11 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
/* If for any reason at all we couldn't handle the fault,
make sure we exit gracefully rather than endlessly redo
the fault. */
- fault = handle_mm_fault(mm, vma, address, cause > 0 ? FAULT_FLAG_WRITE : 0);
- up_read(&mm->mmap_sem);
+ fault = handle_mm_fault(mm, vma, address, flags);
+
+ if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+ return;
+
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM)
goto out_of_memory;
@@ -153,10 +159,26 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
goto do_sigbus;
BUG();
}
- if (fault & VM_FAULT_MAJOR)
- current->maj_flt++;
- else
- current->min_flt++;
+
+ if (flags & FAULT_FLAG_ALLOW_RETRY) {
+ if (fault & VM_FAULT_MAJOR)
+ current->maj_flt++;
+ else
+ current->min_flt++;
+ if (fault & VM_FAULT_RETRY) {
+ flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+ /* No need to up_read(&mm->mmap_sem) as we would
+ * have already released it in __lock_page_or_retry
+ * in mm/filemap.c.
+ */
+
+ goto retry;
+ }
+ }
+
+ up_read(&mm->mmap_sem);
+
return;
/* Something tried to access memory that isn't in our memory map.
@@ -186,12 +208,14 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
/* We ran out of memory, or some other thing happened to us that
made us unable to handle the page fault gracefully. */
out_of_memory:
+ up_read(&mm->mmap_sem);
if (!user_mode(regs))
goto no_context;
pagefault_out_of_memory();
return;
do_sigbus:
+ up_read(&mm->mmap_sem);
/* Send a sigbus, regardless of whether we were in kernel
or user mode. */
info.si_signo = SIGBUS;
diff --git a/arch/alpha/oprofile/common.c b/arch/alpha/oprofile/common.c
index a0a5d27aa215..b8ce18f485d3 100644
--- a/arch/alpha/oprofile/common.c
+++ b/arch/alpha/oprofile/common.c
@@ -12,6 +12,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <asm/ptrace.h>
+#include <asm/special_insns.h>
#include "op_impl.h"
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 7980873525b2..2f88d8d97701 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -6,7 +6,7 @@ config ARM
select HAVE_DMA_API_DEBUG
select HAVE_IDE if PCI || ISA || PCMCIA
select HAVE_DMA_ATTRS
- select HAVE_DMA_CONTIGUOUS if (CPU_V6 || CPU_V6K || CPU_V7)
+ select HAVE_DMA_CONTIGUOUS if MMU
select HAVE_MEMBLOCK
select RTC_LIB
select SYS_SUPPORTS_APM_EMULATION
@@ -38,7 +38,6 @@ config ARM
select HARDIRQS_SW_RESEND
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
- select GENERIC_IRQ_PROBE
select ARCH_WANT_IPC_PARSE_VERSION
select HARDIRQS_SW_RESEND
select CPU_PM if (SUSPEND || CPU_IDLE)
@@ -126,11 +125,6 @@ config TRACE_IRQFLAGS_SUPPORT
bool
default y
-config GENERIC_LOCKBREAK
- bool
- default y
- depends on SMP && PREEMPT
-
config RWSEM_GENERIC_SPINLOCK
bool
default y
@@ -1151,6 +1145,7 @@ config PLAT_ORION
bool
select CLKSRC_MMIO
select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
select COMMON_CLK
config PLAT_PXA
@@ -2149,6 +2144,7 @@ source "drivers/cpufreq/Kconfig"
config CPU_FREQ_IMX
tristate "CPUfreq driver for i.MX CPUs"
depends on ARCH_MXC && CPU_FREQ
+ select CPU_FREQ_TABLE
help
This enables the CPUfreq driver for i.MX CPUs.
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index f15f82bf3a50..e968a52e4881 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -356,15 +356,15 @@ choice
is nothing connected to read from the DCC.
config DEBUG_SEMIHOSTING
- bool "Kernel low-level debug output via semihosting I"
+ bool "Kernel low-level debug output via semihosting I/O"
help
Semihosting enables code running on an ARM target to use
the I/O facilities on a host debugger/emulator through a
- simple SVC calls. The host debugger or emulator must have
+ simple SVC call. The host debugger or emulator must have
semihosting enabled for the special svc call to be trapped
otherwise the kernel will crash.
- This is known to work with OpenOCD, as wellas
+ This is known to work with OpenOCD, as well as
ARM's Fast Models, or any other controlling environment
that implements semihosting.
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 30eae87ead6d..a051dfbdd7db 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -284,10 +284,10 @@ zImage Image xipImage bootpImage uImage: vmlinux
zinstall uinstall install: vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
-%.dtb:
+%.dtb: scripts
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
-dtbs:
+dtbs: scripts
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
# We use MRPROPER_FILES and CLEAN_FILES now
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index b8c64b80bafc..bc67cbff3944 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -653,16 +653,21 @@ __armv7_mmu_cache_on:
mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
#endif
mrc p15, 0, r0, c1, c0, 0 @ read control reg
+ bic r0, r0, #1 << 28 @ clear SCTLR.TRE
orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement
orr r0, r0, #0x003c @ write buffer
#ifdef CONFIG_MMU
#ifdef CONFIG_CPU_ENDIAN_BE8
orr r0, r0, #1 << 25 @ big-endian page tables
#endif
+ mrcne p15, 0, r6, c2, c0, 2 @ read ttb control reg
orrne r0, r0, #1 @ MMU enabled
movne r1, #0xfffffffd @ domain 0 = client
+ bic r6, r6, #1 << 31 @ 32-bit translation system
+ bic r6, r6, #3 << 0 @ use only ttbr0
mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer
mcrne p15, 0, r1, c3, c0, 0 @ load domain access control
+ mcrne p15, 0, r6, c2, c0, 2 @ load ttb control
#endif
mcr p15, 0, r0, c7, c5, 4 @ ISB
mcr p15, 0, r0, c1, c0, 0 @ load control register
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 59509c48d7e5..bd0cff3f808c 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -154,5 +154,10 @@
#size-cells = <0>;
ti,hwmods = "i2c3";
};
+
+ wdt2: wdt@44e35000 {
+ compatible = "ti,omap3-wdt";
+ ti,hwmods = "wd_timer2";
+ };
};
};
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index e1fa7e6edfe8..71d6b5d0daf1 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -12,7 +12,7 @@
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*
- * Contains definitions specific to the Armada 370 SoC that are not
+ * Contains definitions specific to the Armada XP SoC that are not
* common to all Armada SoCs.
*/
diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
index 66389c1c6f62..7c95f76398de 100644
--- a/arch/arm/boot/dts/at91sam9260.dtsi
+++ b/arch/arm/boot/dts/at91sam9260.dtsi
@@ -104,6 +104,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -113,6 +114,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -122,6 +124,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
index b460d6ce9eb5..195019b7ca0e 100644
--- a/arch/arm/boot/dts/at91sam9263.dtsi
+++ b/arch/arm/boot/dts/at91sam9263.dtsi
@@ -95,6 +95,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff400 {
@@ -104,6 +105,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff600 {
@@ -113,6 +115,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffff800 {
@@ -122,6 +125,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioE: gpio@fffffa00 {
@@ -131,6 +135,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@ffffee00 {
diff --git a/arch/arm/boot/dts/at91sam9g25ek.dts b/arch/arm/boot/dts/at91sam9g25ek.dts
index 7829a4d0cb22..96514c134e54 100644
--- a/arch/arm/boot/dts/at91sam9g25ek.dts
+++ b/arch/arm/boot/dts/at91sam9g25ek.dts
@@ -15,7 +15,7 @@
compatible = "atmel,at91sam9g25ek", "atmel,at91sam9x5ek", "atmel,at91sam9x5", "atmel,at91sam9";
chosen {
- bootargs = "128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=ubifs ubi.mtd=1 root=ubi0:rootfs";
+ bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=ubifs ubi.mtd=1 root=ubi0:rootfs";
};
ahb {
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index bafa8806fc17..63751b1e744b 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -113,6 +113,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff400 {
@@ -122,6 +123,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff600 {
@@ -131,6 +133,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffff800 {
@@ -140,6 +143,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioE: gpio@fffffa00 {
@@ -149,6 +153,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@ffffee00 {
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index bfac0dfc332c..ef9336ae9614 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -107,6 +107,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -116,6 +117,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -125,6 +127,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffffa00 {
@@ -134,6 +137,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 4a18c393b136..8a387a8d61b7 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -115,6 +115,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -124,6 +125,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -133,6 +135,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffffa00 {
@@ -142,6 +145,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index a874dbfb5ae6..e6138310e5ce 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -51,11 +51,11 @@
dma-apbh@80004000 {
compatible = "fsl,imx23-dma-apbh";
- reg = <0x80004000 2000>;
+ reg = <0x80004000 0x2000>;
};
ecc@80008000 {
- reg = <0x80008000 2000>;
+ reg = <0x80008000 0x2000>;
status = "disabled";
};
@@ -63,7 +63,7 @@
compatible = "fsl,imx23-gpmi-nand";
#address-cells = <1>;
#size-cells = <1>;
- reg = <0x8000c000 2000>, <0x8000a000 2000>;
+ reg = <0x8000c000 0x2000>, <0x8000a000 0x2000>;
reg-names = "gpmi-nand", "bch";
interrupts = <13>, <56>;
interrupt-names = "gpmi-dma", "bch";
@@ -72,14 +72,14 @@
};
ssp0: ssp@80010000 {
- reg = <0x80010000 2000>;
+ reg = <0x80010000 0x2000>;
interrupts = <15 14>;
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
etm@80014000 {
- reg = <0x80014000 2000>;
+ reg = <0x80014000 0x2000>;
status = "disabled";
};
@@ -87,7 +87,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx23-pinctrl", "simple-bus";
- reg = <0x80018000 2000>;
+ reg = <0x80018000 0x2000>;
gpio0: gpio@0 {
compatible = "fsl,imx23-gpio", "fsl,mxs-gpio";
@@ -273,32 +273,32 @@
};
emi@80020000 {
- reg = <0x80020000 2000>;
+ reg = <0x80020000 0x2000>;
status = "disabled";
};
dma-apbx@80024000 {
compatible = "fsl,imx23-dma-apbx";
- reg = <0x80024000 2000>;
+ reg = <0x80024000 0x2000>;
};
dcp@80028000 {
- reg = <0x80028000 2000>;
+ reg = <0x80028000 0x2000>;
status = "disabled";
};
pxp@8002a000 {
- reg = <0x8002a000 2000>;
+ reg = <0x8002a000 0x2000>;
status = "disabled";
};
ocotp@8002c000 {
- reg = <0x8002c000 2000>;
+ reg = <0x8002c000 0x2000>;
status = "disabled";
};
axi-ahb@8002e000 {
- reg = <0x8002e000 2000>;
+ reg = <0x8002e000 0x2000>;
status = "disabled";
};
@@ -310,14 +310,14 @@
};
ssp1: ssp@80034000 {
- reg = <0x80034000 2000>;
+ reg = <0x80034000 0x2000>;
interrupts = <2 20>;
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
tvenc@80038000 {
- reg = <0x80038000 2000>;
+ reg = <0x80038000 0x2000>;
status = "disabled";
};
};
@@ -330,37 +330,37 @@
ranges;
clkctl@80040000 {
- reg = <0x80040000 2000>;
+ reg = <0x80040000 0x2000>;
status = "disabled";
};
saif0: saif@80042000 {
- reg = <0x80042000 2000>;
+ reg = <0x80042000 0x2000>;
status = "disabled";
};
power@80044000 {
- reg = <0x80044000 2000>;
+ reg = <0x80044000 0x2000>;
status = "disabled";
};
saif1: saif@80046000 {
- reg = <0x80046000 2000>;
+ reg = <0x80046000 0x2000>;
status = "disabled";
};
audio-out@80048000 {
- reg = <0x80048000 2000>;
+ reg = <0x80048000 0x2000>;
status = "disabled";
};
audio-in@8004c000 {
- reg = <0x8004c000 2000>;
+ reg = <0x8004c000 0x2000>;
status = "disabled";
};
lradc@80050000 {
- reg = <0x80050000 2000>;
+ reg = <0x80050000 0x2000>;
status = "disabled";
};
@@ -370,26 +370,26 @@
};
i2c@80058000 {
- reg = <0x80058000 2000>;
+ reg = <0x80058000 0x2000>;
status = "disabled";
};
rtc@8005c000 {
compatible = "fsl,imx23-rtc", "fsl,stmp3xxx-rtc";
- reg = <0x8005c000 2000>;
+ reg = <0x8005c000 0x2000>;
interrupts = <22>;
};
pwm: pwm@80064000 {
compatible = "fsl,imx23-pwm";
- reg = <0x80064000 2000>;
+ reg = <0x80064000 0x2000>;
#pwm-cells = <2>;
fsl,pwm-number = <5>;
status = "disabled";
};
timrot@80068000 {
- reg = <0x80068000 2000>;
+ reg = <0x80068000 0x2000>;
status = "disabled";
};
@@ -429,7 +429,7 @@
ranges;
usbctrl@80080000 {
- reg = <0x80080000 0x10000>;
+ reg = <0x80080000 0x40000>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/imx27-3ds.dts b/arch/arm/boot/dts/imx27-3ds.dts
index d3f8296e19e0..0a8978a40ece 100644
--- a/arch/arm/boot/dts/imx27-3ds.dts
+++ b/arch/arm/boot/dts/imx27-3ds.dts
@@ -27,7 +27,7 @@
status = "okay";
};
- uart@1000a000 {
+ uart1: serial@1000a000 {
fsl,uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index 00bae3aad5ab..5303ab680a34 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -19,6 +19,12 @@
serial3 = &uart4;
serial4 = &uart5;
serial5 = &uart6;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
};
avic: avic-interrupt-controller@e0000000 {
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 787efac68da8..3fa6d190fab4 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -57,18 +57,18 @@
};
hsadc@80002000 {
- reg = <0x80002000 2000>;
+ reg = <0x80002000 0x2000>;
interrupts = <13 87>;
status = "disabled";
};
dma-apbh@80004000 {
compatible = "fsl,imx28-dma-apbh";
- reg = <0x80004000 2000>;
+ reg = <0x80004000 0x2000>;
};
perfmon@80006000 {
- reg = <0x80006000 800>;
+ reg = <0x80006000 0x800>;
interrupts = <27>;
status = "disabled";
};
@@ -77,7 +77,7 @@
compatible = "fsl,imx28-gpmi-nand";
#address-cells = <1>;
#size-cells = <1>;
- reg = <0x8000c000 2000>, <0x8000a000 2000>;
+ reg = <0x8000c000 0x2000>, <0x8000a000 0x2000>;
reg-names = "gpmi-nand", "bch";
interrupts = <88>, <41>;
interrupt-names = "gpmi-dma", "bch";
@@ -86,28 +86,28 @@
};
ssp0: ssp@80010000 {
- reg = <0x80010000 2000>;
+ reg = <0x80010000 0x2000>;
interrupts = <96 82>;
fsl,ssp-dma-channel = <0>;
status = "disabled";
};
ssp1: ssp@80012000 {
- reg = <0x80012000 2000>;
+ reg = <0x80012000 0x2000>;
interrupts = <97 83>;
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
ssp2: ssp@80014000 {
- reg = <0x80014000 2000>;
+ reg = <0x80014000 0x2000>;
interrupts = <98 84>;
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
ssp3: ssp@80016000 {
- reg = <0x80016000 2000>;
+ reg = <0x80016000 0x2000>;
interrupts = <99 85>;
fsl,ssp-dma-channel = <3>;
status = "disabled";
@@ -117,7 +117,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-pinctrl", "simple-bus";
- reg = <0x80018000 2000>;
+ reg = <0x80018000 0x2000>;
gpio0: gpio@0 {
compatible = "fsl,imx28-gpio", "fsl,mxs-gpio";
@@ -510,96 +510,96 @@
};
digctl@8001c000 {
- reg = <0x8001c000 2000>;
+ reg = <0x8001c000 0x2000>;
interrupts = <89>;
status = "disabled";
};
etm@80022000 {
- reg = <0x80022000 2000>;
+ reg = <0x80022000 0x2000>;
status = "disabled";
};
dma-apbx@80024000 {
compatible = "fsl,imx28-dma-apbx";
- reg = <0x80024000 2000>;
+ reg = <0x80024000 0x2000>;
};
dcp@80028000 {
- reg = <0x80028000 2000>;
+ reg = <0x80028000 0x2000>;
interrupts = <52 53 54>;
status = "disabled";
};
pxp@8002a000 {
- reg = <0x8002a000 2000>;
+ reg = <0x8002a000 0x2000>;
interrupts = <39>;
status = "disabled";
};
ocotp@8002c000 {
- reg = <0x8002c000 2000>;
+ reg = <0x8002c000 0x2000>;
status = "disabled";
};
axi-ahb@8002e000 {
- reg = <0x8002e000 2000>;
+ reg = <0x8002e000 0x2000>;
status = "disabled";
};
lcdif@80030000 {
compatible = "fsl,imx28-lcdif";
- reg = <0x80030000 2000>;
+ reg = <0x80030000 0x2000>;
interrupts = <38 86>;
status = "disabled";
};
can0: can@80032000 {
compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
- reg = <0x80032000 2000>;
+ reg = <0x80032000 0x2000>;
interrupts = <8>;
status = "disabled";
};
can1: can@80034000 {
compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
- reg = <0x80034000 2000>;
+ reg = <0x80034000 0x2000>;
interrupts = <9>;
status = "disabled";
};
simdbg@8003c000 {
- reg = <0x8003c000 200>;
+ reg = <0x8003c000 0x200>;
status = "disabled";
};
simgpmisel@8003c200 {
- reg = <0x8003c200 100>;
+ reg = <0x8003c200 0x100>;
status = "disabled";
};
simsspsel@8003c300 {
- reg = <0x8003c300 100>;
+ reg = <0x8003c300 0x100>;
status = "disabled";
};
simmemsel@8003c400 {
- reg = <0x8003c400 100>;
+ reg = <0x8003c400 0x100>;
status = "disabled";
};
gpiomon@8003c500 {
- reg = <0x8003c500 100>;
+ reg = <0x8003c500 0x100>;
status = "disabled";
};
simenet@8003c700 {
- reg = <0x8003c700 100>;
+ reg = <0x8003c700 0x100>;
status = "disabled";
};
armjtag@8003c800 {
- reg = <0x8003c800 100>;
+ reg = <0x8003c800 0x100>;
status = "disabled";
};
};
@@ -612,45 +612,45 @@
ranges;
clkctl@80040000 {
- reg = <0x80040000 2000>;
+ reg = <0x80040000 0x2000>;
status = "disabled";
};
saif0: saif@80042000 {
compatible = "fsl,imx28-saif";
- reg = <0x80042000 2000>;
+ reg = <0x80042000 0x2000>;
interrupts = <59 80>;
fsl,saif-dma-channel = <4>;
status = "disabled";
};
power@80044000 {
- reg = <0x80044000 2000>;
+ reg = <0x80044000 0x2000>;
status = "disabled";
};
saif1: saif@80046000 {
compatible = "fsl,imx28-saif";
- reg = <0x80046000 2000>;
+ reg = <0x80046000 0x2000>;
interrupts = <58 81>;
fsl,saif-dma-channel = <5>;
status = "disabled";
};
lradc@80050000 {
- reg = <0x80050000 2000>;
+ reg = <0x80050000 0x2000>;
status = "disabled";
};
spdif@80054000 {
- reg = <0x80054000 2000>;
+ reg = <0x80054000 0x2000>;
interrupts = <45 66>;
status = "disabled";
};
rtc@80056000 {
compatible = "fsl,imx28-rtc", "fsl,stmp3xxx-rtc";
- reg = <0x80056000 2000>;
+ reg = <0x80056000 0x2000>;
interrupts = <29>;
};
@@ -658,7 +658,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-i2c";
- reg = <0x80058000 2000>;
+ reg = <0x80058000 0x2000>;
interrupts = <111 68>;
clock-frequency = <100000>;
status = "disabled";
@@ -668,7 +668,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-i2c";
- reg = <0x8005a000 2000>;
+ reg = <0x8005a000 0x2000>;
interrupts = <110 69>;
clock-frequency = <100000>;
status = "disabled";
@@ -676,14 +676,14 @@
pwm: pwm@80064000 {
compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
- reg = <0x80064000 2000>;
+ reg = <0x80064000 0x2000>;
#pwm-cells = <2>;
fsl,pwm-number = <8>;
status = "disabled";
};
timrot@80068000 {
- reg = <0x80068000 2000>;
+ reg = <0x80068000 0x2000>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts
index de065b5976e6..59d9789e5508 100644
--- a/arch/arm/boot/dts/imx51-babbage.dts
+++ b/arch/arm/boot/dts/imx51-babbage.dts
@@ -25,8 +25,8 @@
aips@70000000 { /* aips-1 */
spba@70000000 {
esdhc@70004000 { /* ESDHC1 */
- fsl,cd-internal;
- fsl,wp-internal;
+ fsl,cd-controller;
+ fsl,wp-controller;
status = "okay";
};
@@ -53,7 +53,7 @@
spi-max-frequency = <6000000>;
reg = <0>;
interrupt-parent = <&gpio1>;
- interrupts = <8>;
+ interrupts = <8 0x4>;
regulators {
sw1_reg: sw1 {
diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi
index 53cbaa3d4f90..aba28dc87fc8 100644
--- a/arch/arm/boot/dts/imx51.dtsi
+++ b/arch/arm/boot/dts/imx51.dtsi
@@ -17,6 +17,10 @@
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
};
tzic: tz-interrupt-controller@e0000000 {
diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts
index 5b8eafcdbeec..da895e93a999 100644
--- a/arch/arm/boot/dts/imx53-ard.dts
+++ b/arch/arm/boot/dts/imx53-ard.dts
@@ -64,12 +64,32 @@
reg = <0xf4000000 0x2000000>;
phy-mode = "mii";
interrupt-parent = <&gpio2>;
- interrupts = <31>;
+ interrupts = <31 0x8>;
reg-io-width = <4>;
+ /*
+ * VDD33A and VDDVARIO of LAN9220 are supplied by
+ * SW4_3V3 of LTC3589. Before the regulator driver
+ * for this PMIC is available, we use a fixed dummy
+ * 3V3 regulator to get LAN9220 driver probing work.
+ */
+ vdd33a-supply = <&reg_3p3v>;
+ vddvario-supply = <&reg_3p3v>;
smsc,irq-push-pull;
};
};
+ regulators {
+ compatible = "simple-bus";
+
+ reg_3p3v: 3p3v {
+ compatible = "regulator-fixed";
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
gpio-keys {
compatible = "gpio-keys";
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index fc79cdc4b4e6..cd37165edce5 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -19,6 +19,13 @@
serial2 = &uart3;
serial3 = &uart4;
serial4 = &uart5;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
+ gpio6 = &gpio7;
};
tzic: tz-interrupt-controller@0fffc000 {
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index d42e851ceb97..72f30f3e6171 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -53,6 +53,7 @@
fsl,pins = <
144 0x80000000 /* MX6Q_PAD_EIM_D22__GPIO_3_22 */
121 0x80000000 /* MX6Q_PAD_EIM_D19__GPIO_3_19 */
+ 953 0x80000000 /* MX6Q_PAD_GPIO_0__CCM_CLKO */
>;
};
};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 3d3c64b014e6..fd57079f71a9 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -19,6 +19,13 @@
serial2 = &uart3;
serial3 = &uart4;
serial4 = &uart5;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
+ gpio6 = &gpio7;
};
cpus {
diff --git a/arch/arm/boot/dts/kirkwood-dns320.dts b/arch/arm/boot/dts/kirkwood-dns320.dts
index 9a33077130e8..5bb0bf39d3b8 100644
--- a/arch/arm/boot/dts/kirkwood-dns320.dts
+++ b/arch/arm/boot/dts/kirkwood-dns320.dts
@@ -1,6 +1,6 @@
/dts-v1/;
-/include/ "kirkwood.dtsi"
+/include/ "kirkwood-dnskw.dtsi"
/ {
model = "D-Link DNS-320 NAS (Rev A1)";
@@ -15,6 +15,31 @@
bootargs = "console=ttyS0,115200n8 earlyprintk";
};
+ gpio-leds {
+ compatible = "gpio-leds";
+ blue-power {
+ label = "dns320:blue:power";
+ gpios = <&gpio0 26 1>; /* GPIO 26 Active Low */
+ linux,default-trigger = "default-on";
+ };
+ blue-usb {
+ label = "dns320:blue:usb";
+ gpios = <&gpio1 11 1>; /* GPIO 43 Active Low */
+ };
+ orange-l_hdd {
+ label = "dns320:orange:l_hdd";
+ gpios = <&gpio0 28 1>; /* GPIO 28 Active Low */
+ };
+ orange-r_hdd {
+ label = "dns320:orange:r_hdd";
+ gpios = <&gpio0 27 1>; /* GPIO 27 Active Low */
+ };
+ orange-usb {
+ label = "dns320:orange:usb";
+ gpios = <&gpio1 3 1>; /* GPIO 35 Active Low */
+ };
+ };
+
ocp@f1000000 {
serial@12000 {
clock-frequency = <166666667>;
@@ -25,40 +50,5 @@
clock-frequency = <166666667>;
status = "okay";
};
-
- nand@3000000 {
- status = "okay";
-
- partition@0 {
- label = "u-boot";
- reg = <0x0000000 0x100000>;
- read-only;
- };
-
- partition@100000 {
- label = "uImage";
- reg = <0x0100000 0x500000>;
- };
-
- partition@600000 {
- label = "ramdisk";
- reg = <0x0600000 0x500000>;
- };
-
- partition@b00000 {
- label = "image";
- reg = <0x0b00000 0x6600000>;
- };
-
- partition@7100000 {
- label = "mini firmware";
- reg = <0x7100000 0xa00000>;
- };
-
- partition@7b00000 {
- label = "config";
- reg = <0x7b00000 0x500000>;
- };
- };
};
};
diff --git a/arch/arm/boot/dts/kirkwood-dns325.dts b/arch/arm/boot/dts/kirkwood-dns325.dts
index 16734c1b5dfe..d430713ea9b9 100644
--- a/arch/arm/boot/dts/kirkwood-dns325.dts
+++ b/arch/arm/boot/dts/kirkwood-dns325.dts
@@ -1,6 +1,6 @@
/dts-v1/;
-/include/ "kirkwood.dtsi"
+/include/ "kirkwood-dnskw.dtsi"
/ {
model = "D-Link DNS-325 NAS (Rev A1)";
@@ -15,45 +15,43 @@
bootargs = "console=ttyS0,115200n8 earlyprintk";
};
- ocp@f1000000 {
- serial@12000 {
- clock-frequency = <200000000>;
- status = "okay";
+ gpio-leds {
+ compatible = "gpio-leds";
+ white-power {
+ label = "dns325:white:power";
+ gpios = <&gpio0 26 1>; /* GPIO 26 Active Low */
+ linux,default-trigger = "default-on";
+ };
+ white-usb {
+ label = "dns325:white:usb";
+ gpios = <&gpio1 11 1>; /* GPIO 43 Active Low */
+ };
+ red-l_hdd {
+ label = "dns325:red:l_hdd";
+ gpios = <&gpio0 28 1>; /* GPIO 28 Active Low */
};
+ red-r_hdd {
+ label = "dns325:red:r_hdd";
+ gpios = <&gpio0 27 1>; /* GPIO 27 Active Low */
+ };
+ red-usb {
+ label = "dns325:red:usb";
+ gpios = <&gpio0 29 1>; /* GPIO 29 Active Low */
+ };
+ };
- nand@3000000 {
+ ocp@f1000000 {
+ i2c@11000 {
status = "okay";
- partition@0 {
- label = "u-boot";
- reg = <0x0000000 0x100000>;
- read-only;
- };
-
- partition@100000 {
- label = "uImage";
- reg = <0x0100000 0x500000>;
- };
-
- partition@600000 {
- label = "ramdisk";
- reg = <0x0600000 0x500000>;
- };
-
- partition@b00000 {
- label = "image";
- reg = <0x0b00000 0x6600000>;
- };
-
- partition@7100000 {
- label = "mini firmware";
- reg = <0x7100000 0xa00000>;
- };
-
- partition@7b00000 {
- label = "config";
- reg = <0x7b00000 0x500000>;
+ lm75: lm75@48 {
+ compatible = "national,lm75";
+ reg = <0x48>;
};
};
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
};
};
diff --git a/arch/arm/boot/dts/kirkwood-dnskw.dtsi b/arch/arm/boot/dts/kirkwood-dnskw.dtsi
new file mode 100644
index 000000000000..7408655f91b5
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-dnskw.dtsi
@@ -0,0 +1,69 @@
+/include/ "kirkwood.dtsi"
+
+/ {
+ model = "D-Link DNS NASes (kirkwood-based)";
+ compatible = "dlink,dns-kirkwood", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "Power button";
+ linux,code = <116>;
+ gpios = <&gpio1 2 1>;
+ };
+ button@2 {
+ label = "USB unmount button";
+ linux,code = <161>;
+ gpios = <&gpio1 15 1>;
+ };
+ button@3 {
+ label = "Reset button";
+ linux,code = <0x198>;
+ gpios = <&gpio1 16 1>;
+ };
+ };
+
+ ocp@f1000000 {
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+
+ nand@3000000 {
+ status = "okay";
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0000000 0x100000>;
+ read-only;
+ };
+
+ partition@100000 {
+ label = "uImage";
+ reg = <0x0100000 0x500000>;
+ };
+
+ partition@600000 {
+ label = "ramdisk";
+ reg = <0x0600000 0x500000>;
+ };
+
+ partition@b00000 {
+ label = "image";
+ reg = <0x0b00000 0x6600000>;
+ };
+
+ partition@7100000 {
+ label = "mini firmware";
+ reg = <0x7100000 0xa00000>;
+ };
+
+ partition@7b00000 {
+ label = "config";
+ reg = <0x7b00000 0x500000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-dreamplug.dts b/arch/arm/boot/dts/kirkwood-dreamplug.dts
index 78b0f06a09a2..26e281fbf6bc 100644
--- a/arch/arm/boot/dts/kirkwood-dreamplug.dts
+++ b/arch/arm/boot/dts/kirkwood-dreamplug.dts
@@ -20,5 +20,55 @@
clock-frequency = <200000000>;
status = "ok";
};
+
+ spi@10600 {
+ status = "okay";
+
+ m25p40@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "mx25l1606e";
+ reg = <0>;
+ spi-max-frequency = <50000000>;
+ mode = <0>;
+
+ partition@0 {
+ reg = <0x0 0x80000>;
+ label = "u-boot";
+ };
+
+ partition@100000 {
+ reg = <0x100000 0x10000>;
+ label = "u-boot env";
+ };
+
+ partition@180000 {
+ reg = <0x180000 0x10000>;
+ label = "dtb";
+ };
+ };
+ };
+
+ sata@80000 {
+ status = "okay";
+ nr-ports = <1>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ bluetooth {
+ label = "dreamplug:blue:bluetooth";
+ gpios = <&gpio1 15 1>;
+ };
+ wifi {
+ label = "dreamplug:green:wifi";
+ gpios = <&gpio1 16 1>;
+ };
+ wifi-ap {
+ label = "dreamplug:green:wifi_ap";
+ gpios = <&gpio1 17 1>;
+ };
};
};
diff --git a/arch/arm/boot/dts/kirkwood-goflexnet.dts b/arch/arm/boot/dts/kirkwood-goflexnet.dts
new file mode 100644
index 000000000000..7c8238fbb6f9
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-goflexnet.dts
@@ -0,0 +1,99 @@
+/dts-v1/;
+
+/include/ "kirkwood.dtsi"
+
+/ {
+ model = "Seagate GoFlex Net";
+ compatible = "seagate,goflexnet", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x8000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlyprintk root=/dev/sda1 rootdelay=10";
+ };
+
+ ocp@f1000000 {
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "ok";
+ };
+
+ nand@3000000 {
+ status = "okay";
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0000000 0x100000>;
+ read-only;
+ };
+
+ partition@100000 {
+ label = "uImage";
+ reg = <0x0100000 0x400000>;
+ };
+
+ partition@500000 {
+ label = "pogoplug";
+ reg = <0x0500000 0x2000000>;
+ };
+
+ partition@2500000 {
+ label = "root";
+ reg = <0x02500000 0xd800000>;
+ };
+ };
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+
+ };
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ health {
+ label = "status:green:health";
+ gpios = <&gpio1 14 1>;
+ linux,default-trigger = "default-on";
+ };
+ fault {
+ label = "status:orange:fault";
+ gpios = <&gpio1 15 1>;
+ };
+ left0 {
+ label = "status:white:left0";
+ gpios = <&gpio1 10 0>;
+ };
+ left1 {
+ label = "status:white:left1";
+ gpios = <&gpio1 11 0>;
+ };
+ left2 {
+ label = "status:white:left2";
+ gpios = <&gpio1 12 0>;
+ };
+ left3 {
+ label = "status:white:left3";
+ gpios = <&gpio1 13 0>;
+ };
+ right0 {
+ label = "status:white:right0";
+ gpios = <&gpio1 6 0>;
+ };
+ right1 {
+ label = "status:white:right1";
+ gpios = <&gpio1 7 0>;
+ };
+ right2 {
+ label = "status:white:right2";
+ gpios = <&gpio1 8 0>;
+ };
+ right3 {
+ label = "status:white:right3";
+ gpios = <&gpio1 9 0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-ib62x0.dts b/arch/arm/boot/dts/kirkwood-ib62x0.dts
index f59dcf6dc45f..66794ed75ff1 100644
--- a/arch/arm/boot/dts/kirkwood-ib62x0.dts
+++ b/arch/arm/boot/dts/kirkwood-ib62x0.dts
@@ -21,6 +21,11 @@
status = "okay";
};
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+
nand@3000000 {
status = "okay";
@@ -41,4 +46,37 @@
};
};
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "USB Copy";
+ linux,code = <133>;
+ gpios = <&gpio0 29 1>;
+ };
+ button@2 {
+ label = "Reset";
+ linux,code = <0x198>;
+ gpios = <&gpio0 28 1>;
+ };
+ };
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ green-os {
+ label = "ib62x0:green:os";
+ gpios = <&gpio0 25 0>;
+ linux,default-trigger = "default-on";
+ };
+ red-os {
+ label = "ib62x0:red:os";
+ gpios = <&gpio0 22 0>;
+ };
+ usb-copy {
+ label = "ib62x0:red:usb_copy";
+ gpios = <&gpio0 27 0>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/kirkwood-iconnect.dts b/arch/arm/boot/dts/kirkwood-iconnect.dts
index 026a1f82d813..f8ca6fa88192 100644
--- a/arch/arm/boot/dts/kirkwood-iconnect.dts
+++ b/arch/arm/boot/dts/kirkwood-iconnect.dts
@@ -18,9 +18,55 @@
};
ocp@f1000000 {
+ i2c@11000 {
+ status = "okay";
+
+ lm63: lm63@4c {
+ compatible = "national,lm63";
+ reg = <0x4c>;
+ };
+ };
serial@12000 {
clock-frequency = <200000000>;
status = "ok";
};
};
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ led-level {
+ label = "led_level";
+ gpios = <&gpio1 9 0>;
+ linux,default-trigger = "default-on";
+ };
+ power-blue {
+ label = "power:blue";
+ gpios = <&gpio1 10 0>;
+ linux,default-trigger = "timer";
+ };
+ power-red {
+ label = "power:red";
+ gpios = <&gpio1 11 0>;
+ };
+ usb1 {
+ label = "usb1:blue";
+ gpios = <&gpio1 12 0>;
+ };
+ usb2 {
+ label = "usb2:blue";
+ gpios = <&gpio1 13 0>;
+ };
+ usb3 {
+ label = "usb3:blue";
+ gpios = <&gpio1 14 0>;
+ };
+ usb4 {
+ label = "usb4:blue";
+ gpios = <&gpio1 15 0>;
+ };
+ otb {
+ label = "otb:blue";
+ gpios = <&gpio1 16 0>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/kirkwood-lschlv2.dts b/arch/arm/boot/dts/kirkwood-lschlv2.dts
new file mode 100644
index 000000000000..9510c9ea666c
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-lschlv2.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/include/ "kirkwood-lsxl.dtsi"
+
+/ {
+ model = "Buffalo Linkstation LS-CHLv2";
+ compatible = "buffalo,lschlv2", "buffalo,lsxl", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x4000000>;
+ };
+
+ ocp@f1000000 {
+ serial@12000 {
+ clock-frequency = <166666667>;
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-lsxhl.dts b/arch/arm/boot/dts/kirkwood-lsxhl.dts
new file mode 100644
index 000000000000..739019c4cba9
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-lsxhl.dts
@@ -0,0 +1,20 @@
+/dts-v1/;
+
+/include/ "kirkwood-lsxl.dtsi"
+
+/ {
+ model = "Buffalo Linkstation LS-XHL";
+ compatible = "buffalo,lsxhl", "buffalo,lsxl", "marvell,kirkwood-88f6281", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x10000000>;
+ };
+
+ ocp@f1000000 {
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-lsxl.dtsi b/arch/arm/boot/dts/kirkwood-lsxl.dtsi
new file mode 100644
index 000000000000..8ac51c08269d
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-lsxl.dtsi
@@ -0,0 +1,95 @@
+/include/ "kirkwood.dtsi"
+
+/ {
+ chosen {
+ bootargs = "console=ttyS0,115200n8 earlyprintk";
+ };
+
+ ocp@f1000000 {
+ sata@80000 {
+ status = "okay";
+ nr-ports = <1>;
+ };
+
+ spi@10600 {
+ status = "okay";
+
+ m25p40@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p40";
+ reg = <0>;
+ spi-max-frequency = <25000000>;
+ mode = <0>;
+
+ partition@0 {
+ reg = <0x0 0x60000>;
+ label = "uboot";
+ read-only;
+ };
+
+ partition@60000 {
+ reg = <0x60000 0x10000>;
+ label = "dtb";
+ read-only;
+ };
+
+ partition@70000 {
+ reg = <0x70000 0x10000>;
+ label = "uboot_env";
+ };
+ };
+ };
+ };
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "Function Button";
+ linux,code = <132>;
+ gpios = <&gpio1 9 1>;
+ };
+ button@2 {
+ label = "Power-on Switch";
+ linux,code = <116>;
+ gpios = <&gpio1 10 1>;
+ };
+ button@3 {
+ label = "Power-auto Switch";
+ linux,code = <142>;
+ gpios = <&gpio1 11 1>;
+ };
+ };
+
+ gpio_leds {
+ compatible = "gpio-leds";
+
+ led@1 {
+ label = "lschlv2:blue:func";
+ gpios = <&gpio1 4 1>;
+ };
+
+ led@2 {
+ label = "lschlv2:red:alarm";
+ gpios = <&gpio1 5 1>;
+ };
+
+ led@3 {
+ label = "lschlv2:amber:info";
+ gpios = <&gpio1 6 1>;
+ };
+
+ led@4 {
+ label = "lschlv2:blue:power";
+ gpios = <&gpio1 7 1>;
+ linux,default-trigger = "default-on";
+ };
+
+ led@5 {
+ label = "lschlv2:red:func";
+ gpios = <&gpio1 16 1>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood-ts219-6281.dts b/arch/arm/boot/dts/kirkwood-ts219-6281.dts
new file mode 100644
index 000000000000..ccbf32757800
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts219-6281.dts
@@ -0,0 +1,21 @@
+/dts-v1/;
+
+/include/ "kirkwood-ts219.dtsi"
+
+/ {
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "USB Copy";
+ linux,code = <133>;
+ gpios = <&gpio0 15 1>;
+ };
+ button@2 {
+ label = "Reset";
+ linux,code = <0x198>;
+ gpios = <&gpio0 16 1>;
+ };
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/boot/dts/kirkwood-ts219-6282.dts b/arch/arm/boot/dts/kirkwood-ts219-6282.dts
new file mode 100644
index 000000000000..fbe9932161a1
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts219-6282.dts
@@ -0,0 +1,21 @@
+/dts-v1/;
+
+/include/ "kirkwood-ts219.dtsi"
+
+/ {
+ gpio_keys {
+ compatible = "gpio-keys";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ button@1 {
+ label = "USB Copy";
+ linux,code = <133>;
+ gpios = <&gpio1 11 1>;
+ };
+ button@2 {
+ label = "Reset";
+ linux,code = <0x198>;
+ gpios = <&gpio1 5 1>;
+ };
+ };
+}; \ No newline at end of file
diff --git a/arch/arm/boot/dts/kirkwood-ts219.dtsi b/arch/arm/boot/dts/kirkwood-ts219.dtsi
new file mode 100644
index 000000000000..64ea27cb3298
--- /dev/null
+++ b/arch/arm/boot/dts/kirkwood-ts219.dtsi
@@ -0,0 +1,78 @@
+/include/ "kirkwood.dtsi"
+
+/ {
+ model = "QNAP TS219 family";
+ compatible = "qnap,ts219", "marvell,kirkwood";
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,115200n8";
+ };
+
+ ocp@f1000000 {
+ i2c@11000 {
+ status = "okay";
+ clock-frequency = <400000>;
+
+ s35390a: s35390a@30 {
+ compatible = "s35390a";
+ reg = <0x30>;
+ };
+ };
+ serial@12000 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+ serial@12100 {
+ clock-frequency = <200000000>;
+ status = "okay";
+ };
+ spi@10600 {
+ status = "okay";
+
+ m25p128@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "m25p128";
+ reg = <0>;
+ spi-max-frequency = <20000000>;
+ mode = <0>;
+
+ partition@0000000 {
+ reg = <0x00000000 0x00080000>;
+ label = "U-Boot";
+ };
+
+ partition@00200000 {
+ reg = <0x00200000 0x00200000>;
+ label = "Kernel";
+ };
+
+ partition@00400000 {
+ reg = <0x00400000 0x00900000>;
+ label = "RootFS1";
+ };
+ partition@00d00000 {
+ reg = <0x00d00000 0x00300000>;
+ label = "RootFS2";
+ };
+ partition@00040000 {
+ reg = <0x00080000 0x00040000>;
+ label = "U-Boot Config";
+ };
+ partition@000c0000 {
+ reg = <0x000c0000 0x00140000>;
+ label = "NAS Config";
+ };
+ };
+ };
+ sata@80000 {
+ status = "okay";
+ nr-ports = <2>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/kirkwood.dtsi b/arch/arm/boot/dts/kirkwood.dtsi
index f95dbc190ab6..cef9616f330a 100644
--- a/arch/arm/boot/dts/kirkwood.dtsi
+++ b/arch/arm/boot/dts/kirkwood.dtsi
@@ -2,6 +2,15 @@
/ {
compatible = "marvell,kirkwood";
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller {
+ compatible = "marvell,orion-intc", "marvell,intc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xf1020204 0x04>,
+ <0xf1020214 0x04>;
+ };
ocp@f1000000 {
compatible = "simple-bus";
@@ -9,6 +18,24 @@
#address-cells = <1>;
#size-cells = <1>;
+ gpio0: gpio@10100 {
+ compatible = "marvell,orion-gpio";
+ #gpio-cells = <2>;
+ gpio-controller;
+ reg = <0x10100 0x40>;
+ ngpio = <32>;
+ interrupts = <35>, <36>, <37>, <38>;
+ };
+
+ gpio1: gpio@10140 {
+ compatible = "marvell,orion-gpio";
+ #gpio-cells = <2>;
+ gpio-controller;
+ reg = <0x10140 0x40>;
+ ngpio = <18>;
+ interrupts = <39>, <40>, <41>;
+ };
+
serial@12000 {
compatible = "ns16550a";
reg = <0x12000 0x100>;
@@ -33,6 +60,29 @@
interrupts = <53>;
};
+ spi@10600 {
+ compatible = "marvell,orion-spi";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cell-index = <0>;
+ interrupts = <23>;
+ reg = <0x10600 0x28>;
+ status = "disabled";
+ };
+
+ wdt@20300 {
+ compatible = "marvell,orion-wdt";
+ reg = <0x20300 0x28>;
+ status = "okay";
+ };
+
+ sata@80000 {
+ compatible = "marvell,orion-sata";
+ reg = <0x80000 0x5000>;
+ interrupts = <21>;
+ status = "disabled";
+ };
+
nand@3000000 {
#address-cells = <1>;
#size-cells = <1>;
@@ -45,5 +95,15 @@
/* set partition map and/or chip-delay in board dts */
status = "disabled";
};
+
+ i2c@11000 {
+ compatible = "marvell,mv64xxx-i2c";
+ reg = <0x11000 0x20>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <29>;
+ clock-frequency = <100000>;
+ status = "disabled";
+ };
};
};
diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi
index 3b2f3510d7eb..d351b27d7213 100644
--- a/arch/arm/boot/dts/twl6030.dtsi
+++ b/arch/arm/boot/dts/twl6030.dtsi
@@ -66,6 +66,7 @@
vcxio: regulator@8 {
compatible = "ti,twl6030-vcxio";
+ regulator-always-on;
};
vusb: regulator@9 {
@@ -74,10 +75,12 @@
v1v8: regulator@10 {
compatible = "ti,twl6030-v1v8";
+ regulator-always-on;
};
v2v1: regulator@11 {
compatible = "ti,twl6030-v2v1";
+ regulator-always-on;
};
clk32kg: regulator@12 {
diff --git a/arch/arm/configs/armadillo800eva_defconfig b/arch/arm/configs/armadillo800eva_defconfig
index 7d8718468e0d..90610c7030f7 100644
--- a/arch/arm/configs/armadillo800eva_defconfig
+++ b/arch/arm/configs/armadillo800eva_defconfig
@@ -33,7 +33,7 @@ CONFIG_AEABI=y
CONFIG_FORCE_MAX_ZONEORDER=13
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="console=tty0 console=ttySC1,115200 earlyprintk=sh-sci.1,115200 ignore_loglevel root=/dev/nfs ip=dhcp nfsroot=,rsize=4096,wsize=4096"
+CONFIG_CMDLINE="console=tty0 console=ttySC1,115200 earlyprintk=sh-sci.1,115200 ignore_loglevel root=/dev/nfs ip=dhcp nfsroot=,rsize=4096,wsize=4096 rw"
CONFIG_CMDLINE_FORCE=y
CONFIG_KEXEC=y
CONFIG_VFP=y
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index f725b9637b33..3c9f32f9b6b4 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -192,6 +192,7 @@ CONFIG_RTC_DRV_MC13XXX=y
CONFIG_RTC_DRV_MXC=y
CONFIG_DMADEVICES=y
CONFIG_IMX_SDMA=y
+CONFIG_MXS_DMA=y
CONFIG_COMMON_CLK_DEBUG=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_EXT2_FS=y
diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig
index ccdb6357fb74..4edcfb4e4dee 100644
--- a/arch/arm/configs/mxs_defconfig
+++ b/arch/arm/configs/mxs_defconfig
@@ -34,7 +34,6 @@ CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_AEABI=y
-CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
CONFIG_AUTO_ZRELADDR=y
CONFIG_FPE_NWFPE=y
CONFIG_NET=y
diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
index b152de79fd95..e58edc36b406 100644
--- a/arch/arm/configs/omap2plus_defconfig
+++ b/arch/arm/configs/omap2plus_defconfig
@@ -193,6 +193,8 @@ CONFIG_MMC_OMAP_HS=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_TWL92330=y
CONFIG_RTC_DRV_TWL4030=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_OMAP=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
diff --git a/arch/arm/configs/tct_hammer_defconfig b/arch/arm/configs/tct_hammer_defconfig
index 1d24f8458bef..71277a1591ba 100644
--- a/arch/arm/configs/tct_hammer_defconfig
+++ b/arch/arm/configs/tct_hammer_defconfig
@@ -7,7 +7,7 @@ CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
# CONFIG_KALLSYMS is not set
-# CONFIG_BUG is not set
+# CONFIG_BUGVERBOSE is not set
# CONFIG_ELF_CORE is not set
# CONFIG_SHMEM is not set
CONFIG_SLOB=y
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index 2d4f661d1cf6..da6845493caa 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -86,6 +86,7 @@ CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_LM3530=y
CONFIG_LEDS_LP5521=y
+CONFIG_LEDS_GPIO=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_AB8500=y
CONFIG_RTC_DRV_PL031=y
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 03fb93621d0d..5c8b3bf4d825 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -320,4 +320,12 @@
.size \name , . - \name
.endm
+ .macro check_uaccess, addr:req, size:req, limit:req, tmp:req, bad:req
+#ifndef CONFIG_CPU_USE_DOMAINS
+ adds \tmp, \addr, #\size - 1
+ sbcccs \tmp, \tmp, \limit
+ bcs \bad
+#endif
+ .endm
+
#endif /* __ASM_ASSEMBLER_H__ */
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 004c1bc95d2b..e4448e16046d 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -215,7 +215,9 @@ static inline void vivt_flush_cache_mm(struct mm_struct *mm)
static inline void
vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
- if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm)))
+ struct mm_struct *mm = vma->vm_mm;
+
+ if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm)))
__cpuc_flush_user_range(start & PAGE_MASK, PAGE_ALIGN(end),
vma->vm_flags);
}
@@ -223,7 +225,9 @@ vivt_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned
static inline void
vivt_flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn)
{
- if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) {
+ struct mm_struct *mm = vma->vm_mm;
+
+ if (!mm || cpumask_test_cpu(smp_processor_id(), mm_cpumask(mm))) {
unsigned long addr = user_addr & PAGE_MASK;
__cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags);
}
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 2ae842df4551..5c44dcb0987b 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -203,6 +203,13 @@ static inline void dma_free_writecombine(struct device *dev, size_t size,
}
/*
+ * This can be called during early boot to increase the size of the atomic
+ * coherent DMA pool above the default value of 256KiB. It must be called
+ * before postcore_initcall.
+ */
+extern void __init init_dma_coherent_pool_size(unsigned long size);
+
+/*
* This can be called during boot to increase the size of the consistent
* DMA region above it's default value of 2MB. It must be called before the
* memory allocator is initialised, i.e. before any core_initcall.
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index e965f1b560f1..5f6ddcc56452 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -187,6 +187,7 @@ static inline unsigned long __phys_to_virt(unsigned long x)
#define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET)
#endif
#endif
+#endif /* __ASSEMBLY__ */
#ifndef PHYS_OFFSET
#ifdef PLAT_PHYS_OFFSET
@@ -196,6 +197,8 @@ static inline unsigned long __phys_to_virt(unsigned long x)
#endif
#endif
+#ifndef __ASSEMBLY__
+
/*
* PFNs are used to describe any physical page; this means
* PFN 0 == physical address 0.
diff --git a/arch/arm/include/asm/mutex.h b/arch/arm/include/asm/mutex.h
index 93226cf23ae0..b1479fd04a95 100644
--- a/arch/arm/include/asm/mutex.h
+++ b/arch/arm/include/asm/mutex.h
@@ -7,121 +7,10 @@
*/
#ifndef _ASM_MUTEX_H
#define _ASM_MUTEX_H
-
-#if __LINUX_ARM_ARCH__ < 6
-/* On pre-ARMv6 hardware the swp based implementation is the most efficient. */
-# include <asm-generic/mutex-xchg.h>
-#else
-
/*
- * Attempting to lock a mutex on ARMv6+ can be done with a bastardized
- * atomic decrement (it is not a reliable atomic decrement but it satisfies
- * the defined semantics for our purpose, while being smaller and faster
- * than a real atomic decrement or atomic swap. The idea is to attempt
- * decrementing the lock value only once. If once decremented it isn't zero,
- * or if its store-back fails due to a dispute on the exclusive store, we
- * simply bail out immediately through the slow path where the lock will be
- * reattempted until it succeeds.
+ * On pre-ARMv6 hardware this results in a swp-based implementation,
+ * which is the most efficient. For ARMv6+, we emit a pair of exclusive
+ * accesses instead.
*/
-static inline void
-__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
-{
- int __ex_flag, __res;
-
- __asm__ (
-
- "ldrex %0, [%2] \n\t"
- "sub %0, %0, #1 \n\t"
- "strex %1, %0, [%2] "
-
- : "=&r" (__res), "=&r" (__ex_flag)
- : "r" (&(count)->counter)
- : "cc","memory" );
-
- __res |= __ex_flag;
- if (unlikely(__res != 0))
- fail_fn(count);
-}
-
-static inline int
-__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
-{
- int __ex_flag, __res;
-
- __asm__ (
-
- "ldrex %0, [%2] \n\t"
- "sub %0, %0, #1 \n\t"
- "strex %1, %0, [%2] "
-
- : "=&r" (__res), "=&r" (__ex_flag)
- : "r" (&(count)->counter)
- : "cc","memory" );
-
- __res |= __ex_flag;
- if (unlikely(__res != 0))
- __res = fail_fn(count);
- return __res;
-}
-
-/*
- * Same trick is used for the unlock fast path. However the original value,
- * rather than the result, is used to test for success in order to have
- * better generated assembly.
- */
-static inline void
-__mutex_fastpath_unlock(atomic_t *count, void (*fail_fn)(atomic_t *))
-{
- int __ex_flag, __res, __orig;
-
- __asm__ (
-
- "ldrex %0, [%3] \n\t"
- "add %1, %0, #1 \n\t"
- "strex %2, %1, [%3] "
-
- : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag)
- : "r" (&(count)->counter)
- : "cc","memory" );
-
- __orig |= __ex_flag;
- if (unlikely(__orig != 0))
- fail_fn(count);
-}
-
-/*
- * If the unlock was done on a contended lock, or if the unlock simply fails
- * then the mutex remains locked.
- */
-#define __mutex_slowpath_needs_to_unlock() 1
-
-/*
- * For __mutex_fastpath_trylock we use another construct which could be
- * described as a "single value cmpxchg".
- *
- * This provides the needed trylock semantics like cmpxchg would, but it is
- * lighter and less generic than a true cmpxchg implementation.
- */
-static inline int
-__mutex_fastpath_trylock(atomic_t *count, int (*fail_fn)(atomic_t *))
-{
- int __ex_flag, __res, __orig;
-
- __asm__ (
-
- "1: ldrex %0, [%3] \n\t"
- "subs %1, %0, #1 \n\t"
- "strexeq %2, %1, [%3] \n\t"
- "movlt %0, #0 \n\t"
- "cmpeq %2, #0 \n\t"
- "bgt 1b "
-
- : "=&r" (__orig), "=&r" (__res), "=&r" (__ex_flag)
- : "r" (&count->counter)
- : "cc", "memory" );
-
- return __orig;
-}
-
-#endif
+#include <asm-generic/mutex-xchg.h>
#endif
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index f66626d71e7d..41dc31f834c3 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -195,6 +195,18 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)
+#define pte_none(pte) (!pte_val(pte))
+#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
+#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
+#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
+#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
+#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
+#define pte_special(pte) (0)
+
+#define pte_present_user(pte) \
+ ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
+ (L_PTE_PRESENT | L_PTE_USER))
+
#if __LINUX_ARM_ARCH__ < 6
static inline void __sync_icache_dcache(pte_t pteval)
{
@@ -206,25 +218,15 @@ extern void __sync_icache_dcache(pte_t pteval);
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pteval)
{
- if (addr >= TASK_SIZE)
- set_pte_ext(ptep, pteval, 0);
- else {
+ unsigned long ext = 0;
+
+ if (addr < TASK_SIZE && pte_present_user(pteval)) {
__sync_icache_dcache(pteval);
- set_pte_ext(ptep, pteval, PTE_EXT_NG);
+ ext |= PTE_EXT_NG;
}
-}
-#define pte_none(pte) (!pte_val(pte))
-#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
-#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
-#define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY)
-#define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG)
-#define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN))
-#define pte_special(pte) (0)
-
-#define pte_present_user(pte) \
- ((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
- (L_PTE_PRESENT | L_PTE_USER))
+ set_pte_ext(ptep, pteval, ext);
+}
#define PTE_BIT_FUNC(fn,op) \
static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
@@ -251,13 +253,13 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- * <--------------- offset --------------------> <- type --> 0 0 0
+ * <--------------- offset ----------------------> < type -> 0 0 0
*
- * This gives us up to 63 swap files and 32GB per swap file. Note that
+ * This gives us up to 31 swap files and 64GB per swap file. Note that
* the offset field is always non-zero.
*/
#define __SWP_TYPE_SHIFT 3
-#define __SWP_TYPE_BITS 6
+#define __SWP_TYPE_BITS 5
#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
index e3f757263438..05b8e82ec9f5 100644
--- a/arch/arm/include/asm/sched_clock.h
+++ b/arch/arm/include/asm/sched_clock.h
@@ -10,5 +10,7 @@
extern void sched_clock_postinit(void);
extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
+extern void setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
+ unsigned long rate);
#endif
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index 23ebc0c82a39..24d284a1bfc7 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -196,7 +196,7 @@ static const struct tagtable __tagtable_##fn __tag = { tag, fn }
struct membank {
phys_addr_t start;
- unsigned long size;
+ phys_addr_t size;
unsigned int highmem;
};
@@ -217,7 +217,7 @@ extern struct meminfo meminfo;
#define bank_phys_end(bank) ((bank)->start + (bank)->size)
#define bank_phys_size(bank) (bank)->size
-extern int arm_add_memory(phys_addr_t start, unsigned long size);
+extern int arm_add_memory(phys_addr_t start, phys_addr_t size);
extern void early_print(const char *str, ...);
extern void dump_machine_table(void);
diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h
index 314d4664eae7..99a19512ee26 100644
--- a/arch/arm/include/asm/tlb.h
+++ b/arch/arm/include/asm/tlb.h
@@ -199,6 +199,9 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
{
pgtable_page_dtor(pte);
+#ifdef CONFIG_ARM_LPAE
+ tlb_add_flush(tlb, addr);
+#else
/*
* With the classic ARM MMU, a pte page has two corresponding pmd
* entries, each covering 1MB.
@@ -206,6 +209,7 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
addr &= PMD_MASK;
tlb_add_flush(tlb, addr + SZ_1M - PAGE_SIZE);
tlb_add_flush(tlb, addr + SZ_1M);
+#endif
tlb_remove_page(tlb, pte);
}
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 479a6352e0b5..77bd79f2ffdb 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -101,28 +101,39 @@ extern int __get_user_1(void *);
extern int __get_user_2(void *);
extern int __get_user_4(void *);
-#define __get_user_x(__r2,__p,__e,__s,__i...) \
+#define __GUP_CLOBBER_1 "lr", "cc"
+#ifdef CONFIG_CPU_USE_DOMAINS
+#define __GUP_CLOBBER_2 "ip", "lr", "cc"
+#else
+#define __GUP_CLOBBER_2 "lr", "cc"
+#endif
+#define __GUP_CLOBBER_4 "lr", "cc"
+
+#define __get_user_x(__r2,__p,__e,__l,__s) \
__asm__ __volatile__ ( \
__asmeq("%0", "r0") __asmeq("%1", "r2") \
+ __asmeq("%3", "r1") \
"bl __get_user_" #__s \
: "=&r" (__e), "=r" (__r2) \
- : "0" (__p) \
- : __i, "cc")
+ : "0" (__p), "r" (__l) \
+ : __GUP_CLOBBER_##__s)
-#define get_user(x,p) \
+#define __get_user_check(x,p) \
({ \
+ unsigned long __limit = current_thread_info()->addr_limit - 1; \
register const typeof(*(p)) __user *__p asm("r0") = (p);\
register unsigned long __r2 asm("r2"); \
+ register unsigned long __l asm("r1") = __limit; \
register int __e asm("r0"); \
switch (sizeof(*(__p))) { \
case 1: \
- __get_user_x(__r2, __p, __e, 1, "lr"); \
- break; \
+ __get_user_x(__r2, __p, __e, __l, 1); \
+ break; \
case 2: \
- __get_user_x(__r2, __p, __e, 2, "r3", "lr"); \
+ __get_user_x(__r2, __p, __e, __l, 2); \
break; \
case 4: \
- __get_user_x(__r2, __p, __e, 4, "lr"); \
+ __get_user_x(__r2, __p, __e, __l, 4); \
break; \
default: __e = __get_user_bad(); break; \
} \
@@ -130,42 +141,57 @@ extern int __get_user_4(void *);
__e; \
})
+#define get_user(x,p) \
+ ({ \
+ might_fault(); \
+ __get_user_check(x,p); \
+ })
+
extern int __put_user_1(void *, unsigned int);
extern int __put_user_2(void *, unsigned int);
extern int __put_user_4(void *, unsigned int);
extern int __put_user_8(void *, unsigned long long);
-#define __put_user_x(__r2,__p,__e,__s) \
+#define __put_user_x(__r2,__p,__e,__l,__s) \
__asm__ __volatile__ ( \
__asmeq("%0", "r0") __asmeq("%2", "r2") \
+ __asmeq("%3", "r1") \
"bl __put_user_" #__s \
: "=&r" (__e) \
- : "0" (__p), "r" (__r2) \
+ : "0" (__p), "r" (__r2), "r" (__l) \
: "ip", "lr", "cc")
-#define put_user(x,p) \
+#define __put_user_check(x,p) \
({ \
+ unsigned long __limit = current_thread_info()->addr_limit - 1; \
register const typeof(*(p)) __r2 asm("r2") = (x); \
register const typeof(*(p)) __user *__p asm("r0") = (p);\
+ register unsigned long __l asm("r1") = __limit; \
register int __e asm("r0"); \
switch (sizeof(*(__p))) { \
case 1: \
- __put_user_x(__r2, __p, __e, 1); \
+ __put_user_x(__r2, __p, __e, __l, 1); \
break; \
case 2: \
- __put_user_x(__r2, __p, __e, 2); \
+ __put_user_x(__r2, __p, __e, __l, 2); \
break; \
case 4: \
- __put_user_x(__r2, __p, __e, 4); \
+ __put_user_x(__r2, __p, __e, __l, 4); \
break; \
case 8: \
- __put_user_x(__r2, __p, __e, 8); \
+ __put_user_x(__r2, __p, __e, __l, 8); \
break; \
default: __e = __put_user_bad(); break; \
} \
__e; \
})
+#define put_user(x,p) \
+ ({ \
+ might_fault(); \
+ __put_user_check(x,p); \
+ })
+
#else /* CONFIG_MMU */
/*
@@ -219,6 +245,7 @@ do { \
unsigned long __gu_addr = (unsigned long)(ptr); \
unsigned long __gu_val; \
__chk_user_ptr(ptr); \
+ might_fault(); \
switch (sizeof(*(ptr))) { \
case 1: __get_user_asm_byte(__gu_val,__gu_addr,err); break; \
case 2: __get_user_asm_half(__gu_val,__gu_addr,err); break; \
@@ -300,6 +327,7 @@ do { \
unsigned long __pu_addr = (unsigned long)(ptr); \
__typeof__(*(ptr)) __pu_val = (x); \
__chk_user_ptr(ptr); \
+ might_fault(); \
switch (sizeof(*(ptr))) { \
case 1: __put_user_asm_byte(__pu_val,__pu_addr,err); break; \
case 2: __put_user_asm_half(__pu_val,__pu_addr,err); break; \
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 0cab47d4a83f..2fde5fd1acce 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -404,6 +404,7 @@
#define __NR_setns (__NR_SYSCALL_BASE+375)
#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
+ /* 378 for kcmp */
/*
* The following SWIs are ARM private.
@@ -483,6 +484,7 @@
*/
#define __IGNORE_fadvise64_64
#define __IGNORE_migrate_pages
+#define __IGNORE_kcmp
#endif /* __KERNEL__ */
#endif /* __ASM_ARM_UNISTD_H */
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 463ff4a0ec8a..e337879595e5 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -387,6 +387,7 @@
/* 375 */ CALL(sys_setns)
CALL(sys_process_vm_readv)
CALL(sys_process_vm_writev)
+ CALL(sys_ni_syscall) /* reserved for sys_kcmp */
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 0d1851ca6eb9..0f82098c9bfe 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -244,6 +244,19 @@ svc_preempt:
b 1b
#endif
+__und_fault:
+ @ Correct the PC such that it is pointing at the instruction
+ @ which caused the fault. If the faulting instruction was ARM
+ @ the PC will be pointing at the next instruction, and have to
+ @ subtract 4. Otherwise, it is Thumb, and the PC will be
+ @ pointing at the second half of the Thumb instruction. We
+ @ have to subtract 2.
+ ldr r2, [r0, #S_PC]
+ sub r2, r2, r1
+ str r2, [r0, #S_PC]
+ b do_undefinstr
+ENDPROC(__und_fault)
+
.align 5
__und_svc:
#ifdef CONFIG_KPROBES
@@ -261,25 +274,32 @@ __und_svc:
@
@ r0 - instruction
@
-#ifndef CONFIG_THUMB2_KERNEL
+#ifndef CONFIG_THUMB2_KERNEL
ldr r0, [r4, #-4]
#else
+ mov r1, #2
ldrh r0, [r4, #-2] @ Thumb instruction at LR - 2
cmp r0, #0xe800 @ 32-bit instruction if xx >= 0
- ldrhhs r9, [r4] @ bottom 16 bits
- orrhs r0, r9, r0, lsl #16
+ blo __und_svc_fault
+ ldrh r9, [r4] @ bottom 16 bits
+ add r4, r4, #2
+ str r4, [sp, #S_PC]
+ orr r0, r9, r0, lsl #16
#endif
- adr r9, BSYM(1f)
+ adr r9, BSYM(__und_svc_finish)
mov r2, r4
bl call_fpe
+ mov r1, #4 @ PC correction to apply
+__und_svc_fault:
mov r0, sp @ struct pt_regs *regs
- bl do_undefinstr
+ bl __und_fault
@
@ IRQs off again before pulling preserved data off the stack
@
-1: disable_irq_notrace
+__und_svc_finish:
+ disable_irq_notrace
@
@ restore SPSR and restart the instruction
@@ -423,25 +443,33 @@ __und_usr:
mov r2, r4
mov r3, r5
+ @ r2 = regs->ARM_pc, which is either 2 or 4 bytes ahead of the
+ @ faulting instruction depending on Thumb mode.
+ @ r3 = regs->ARM_cpsr
@
- @ fall through to the emulation code, which returns using r9 if
- @ it has emulated the instruction, or the more conventional lr
- @ if we are to treat this as a real undefined instruction
- @
- @ r0 - instruction
+ @ The emulation code returns using r9 if it has emulated the
+ @ instruction, or the more conventional lr if we are to treat
+ @ this as a real undefined instruction
@
adr r9, BSYM(ret_from_exception)
- adr lr, BSYM(__und_usr_unknown)
+
tst r3, #PSR_T_BIT @ Thumb mode?
- itet eq @ explicit IT needed for the 1f label
- subeq r4, r2, #4 @ ARM instr at LR - 4
- subne r4, r2, #2 @ Thumb instr at LR - 2
-1: ldreqt r0, [r4]
+ bne __und_usr_thumb
+ sub r4, r2, #4 @ ARM instr at LR - 4
+1: ldrt r0, [r4]
#ifdef CONFIG_CPU_ENDIAN_BE8
- reveq r0, r0 @ little endian instruction
+ rev r0, r0 @ little endian instruction
#endif
- beq call_fpe
+ @ r0 = 32-bit ARM instruction which caused the exception
+ @ r2 = PC value for the following instruction (:= regs->ARM_pc)
+ @ r4 = PC value for the faulting instruction
+ @ lr = 32-bit undefined instruction function
+ adr lr, BSYM(__und_usr_fault_32)
+ b call_fpe
+
+__und_usr_thumb:
@ Thumb instruction
+ sub r4, r2, #2 @ First half of thumb instr at LR - 2
#if CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7
/*
* Thumb-2 instruction handling. Note that because pre-v6 and >= v6 platforms
@@ -455,7 +483,7 @@ __und_usr:
ldr r5, .LCcpu_architecture
ldr r5, [r5]
cmp r5, #CPU_ARCH_ARMv7
- blo __und_usr_unknown
+ blo __und_usr_fault_16 @ 16bit undefined instruction
/*
* The following code won't get run unless the running CPU really is v7, so
* coding round the lack of ldrht on older arches is pointless. Temporarily
@@ -463,15 +491,18 @@ __und_usr:
*/
.arch armv6t2
#endif
-2:
- ARM( ldrht r5, [r4], #2 )
- THUMB( ldrht r5, [r4] )
- THUMB( add r4, r4, #2 )
+2: ldrht r5, [r4]
cmp r5, #0xe800 @ 32bit instruction if xx != 0
- blo __und_usr_unknown
-3: ldrht r0, [r4]
+ blo __und_usr_fault_16 @ 16bit undefined instruction
+3: ldrht r0, [r2]
add r2, r2, #2 @ r2 is PC + 2, make it PC + 4
+ str r2, [sp, #S_PC] @ it's a 2x16bit instr, update
orr r0, r0, r5, lsl #16
+ adr lr, BSYM(__und_usr_fault_32)
+ @ r0 = the two 16-bit Thumb instructions which caused the exception
+ @ r2 = PC value for the following Thumb instruction (:= regs->ARM_pc)
+ @ r4 = PC value for the first 16-bit Thumb instruction
+ @ lr = 32bit undefined instruction function
#if __LINUX_ARM_ARCH__ < 7
/* If the target arch was overridden, change it back: */
@@ -482,17 +513,13 @@ __und_usr:
#endif
#endif /* __LINUX_ARM_ARCH__ < 7 */
#else /* !(CONFIG_ARM_THUMB && __LINUX_ARM_ARCH__ >= 6 && CONFIG_CPU_V7) */
- b __und_usr_unknown
+ b __und_usr_fault_16
#endif
- UNWIND(.fnend )
+ UNWIND(.fnend)
ENDPROC(__und_usr)
- @
- @ fallthrough to call_fpe
- @
-
/*
- * The out of line fixup for the ldrt above.
+ * The out of line fixup for the ldrt instructions above.
*/
.pushsection .fixup, "ax"
.align 2
@@ -524,11 +551,12 @@ ENDPROC(__und_usr)
* NEON handler code.
*
* Emulators may wish to make use of the following registers:
- * r0 = instruction opcode.
- * r2 = PC+4
+ * r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+ * r2 = PC value to resume execution after successful emulation
* r9 = normal "successful" return address
- * r10 = this threads thread_info structure.
+ * r10 = this threads thread_info structure
* lr = unrecognised instruction return address
+ * IRQs disabled, FIQs enabled.
*/
@
@ Fall-through from Thumb-2 __und_usr
@@ -659,12 +687,17 @@ ENTRY(no_fp)
mov pc, lr
ENDPROC(no_fp)
-__und_usr_unknown:
- enable_irq
+__und_usr_fault_32:
+ mov r1, #4
+ b 1f
+__und_usr_fault_16:
+ mov r1, #2
+1: enable_irq
mov r0, sp
adr lr, BSYM(ret_from_exception)
- b do_undefinstr
-ENDPROC(__und_usr_unknown)
+ b __und_fault
+ENDPROC(__und_usr_fault_32)
+ENDPROC(__und_usr_fault_16)
.align 5
__pabt_usr:
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 49d9f9305247..978eac57e04a 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -51,23 +51,15 @@ ret_fast_syscall:
fast_work_pending:
str r0, [sp, #S_R0+S_OFF]! @ returned r0
work_pending:
- tst r1, #_TIF_NEED_RESCHED
- bne work_resched
- /*
- * TIF_SIGPENDING or TIF_NOTIFY_RESUME must've been set if we got here
- */
- ldr r2, [sp, #S_PSR]
mov r0, sp @ 'regs'
- tst r2, #15 @ are we returning to user mode?
- bne no_work_pending @ no? just leave, then...
mov r2, why @ 'syscall'
- tst r1, #_TIF_SIGPENDING @ delivering a signal?
- movne why, #0 @ prevent further restarts
- bl do_notify_resume
- b ret_slow_syscall @ Check work again
+ bl do_work_pending
+ cmp r0, #0
+ beq no_work_pending
+ movlt scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE)
+ ldmia sp, {r0 - r6} @ have to reload r0 - r6
+ b local_restart @ ... and off we go
-work_resched:
- bl schedule
/*
* "slow" syscall return path. "why" tells us if this was a real syscall.
*/
@@ -409,6 +401,7 @@ ENTRY(vector_swi)
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
#endif
+local_restart:
ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing
stmdb sp!, {r4, r5} @ push fifth and sixth args
@@ -450,7 +443,8 @@ __sys_trace:
mov scno, r0 @ syscall number (possibly new)
add r1, sp, #S_R0 + S_OFF @ pointer to regs
cmp scno, #NR_syscalls @ check upper syscall limit
- ldmccia r1, {r0 - r3} @ have to reload r0 - r3
+ ldmccia r1, {r0 - r6} @ have to reload r0 - r6
+ stmccia sp, {r4, r5} @ and update the stack args
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
b 2b
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
index df0bf0c8cb79..34e56647dcee 100644
--- a/arch/arm/kernel/ftrace.c
+++ b/arch/arm/kernel/ftrace.c
@@ -179,19 +179,20 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
old = *parent;
*parent = return_hooker;
- err = ftrace_push_return_trace(old, self_addr, &trace.depth,
- frame_pointer);
- if (err == -EBUSY) {
- *parent = old;
- return;
- }
-
trace.func = self_addr;
+ trace.depth = current->curr_ret_stack + 1;
/* Only trace if the calling function expects to */
if (!ftrace_graph_entry(&trace)) {
- current->curr_ret_stack--;
*parent = old;
+ return;
+ }
+
+ err = ftrace_push_return_trace(old, self_addr, &trace.depth,
+ frame_pointer);
+ if (err == -EBUSY) {
+ *parent = old;
+ return;
}
}
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index ba386bd94107..281bf3301241 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -159,6 +159,12 @@ static int debug_arch_supported(void)
arch >= ARM_DEBUG_ARCH_V7_1;
}
+/* Can we determine the watchpoint access type from the fsr? */
+static int debug_exception_updates_fsr(void)
+{
+ return 0;
+}
+
/* Determine number of WRP registers available. */
static int get_num_wrp_resources(void)
{
@@ -604,13 +610,14 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
/* Aligned */
break;
case 1:
- /* Allow single byte watchpoint. */
- if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
- break;
case 2:
/* Allow halfword watchpoints and breakpoints. */
if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
break;
+ case 3:
+ /* Allow single byte watchpoint. */
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
+ break;
default:
ret = -EINVAL;
goto out;
@@ -619,18 +626,35 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
info->address &= ~alignment_mask;
info->ctrl.len <<= offset;
- /*
- * Currently we rely on an overflow handler to take
- * care of single-stepping the breakpoint when it fires.
- * In the case of userspace breakpoints on a core with V7 debug,
- * we can use the mismatch feature as a poor-man's hardware
- * single-step, but this only works for per-task breakpoints.
- */
- if (!bp->overflow_handler && (arch_check_bp_in_kernelspace(bp) ||
- !core_has_mismatch_brps() || !bp->hw.bp_target)) {
- pr_warning("overflow handler required but none found\n");
- ret = -EINVAL;
+ if (!bp->overflow_handler) {
+ /*
+ * Mismatch breakpoints are required for single-stepping
+ * breakpoints.
+ */
+ if (!core_has_mismatch_brps())
+ return -EINVAL;
+
+ /* We don't allow mismatch breakpoints in kernel space. */
+ if (arch_check_bp_in_kernelspace(bp))
+ return -EPERM;
+
+ /*
+ * Per-cpu breakpoints are not supported by our stepping
+ * mechanism.
+ */
+ if (!bp->hw.bp_target)
+ return -EINVAL;
+
+ /*
+ * We only support specific access types if the fsr
+ * reports them.
+ */
+ if (!debug_exception_updates_fsr() &&
+ (info->ctrl.type == ARM_BREAKPOINT_LOAD ||
+ info->ctrl.type == ARM_BREAKPOINT_STORE))
+ return -EINVAL;
}
+
out:
return ret;
}
@@ -706,10 +730,12 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr,
goto unlock;
/* Check that the access type matches. */
- access = (fsr & ARM_FSR_ACCESS_MASK) ? HW_BREAKPOINT_W :
- HW_BREAKPOINT_R;
- if (!(access & hw_breakpoint_type(wp)))
- goto unlock;
+ if (debug_exception_updates_fsr()) {
+ access = (fsr & ARM_FSR_ACCESS_MASK) ?
+ HW_BREAKPOINT_W : HW_BREAKPOINT_R;
+ if (!(access & hw_breakpoint_type(wp)))
+ goto unlock;
+ }
/* We have a winner. */
info->trigger = addr;
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 19c95ea65b2f..693b744fd572 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -247,6 +247,7 @@ void machine_shutdown(void)
void machine_halt(void)
{
machine_shutdown();
+ local_irq_disable();
while (1);
}
@@ -268,6 +269,7 @@ void machine_restart(char *cmd)
/* Whoops - the platform was unable to reboot. Tell the user! */
printk("Reboot failed -- System halted\n");
+ local_irq_disable();
while (1);
}
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index dab711e6e1ca..3e0fc5f7ed4b 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -25,6 +25,7 @@
#include <linux/regset.h>
#include <linux/audit.h>
#include <linux/tracehook.h>
+#include <linux/unistd.h>
#include <asm/pgtable.h>
#include <asm/traps.h>
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
index 27d186abbc06..f4515393248d 100644
--- a/arch/arm/kernel/sched_clock.c
+++ b/arch/arm/kernel/sched_clock.c
@@ -21,6 +21,8 @@ struct clock_data {
u32 epoch_cyc_copy;
u32 mult;
u32 shift;
+ bool suspended;
+ bool needs_suspend;
};
static void sched_clock_poll(unsigned long wrap_ticks);
@@ -49,6 +51,9 @@ static unsigned long long cyc_to_sched_clock(u32 cyc, u32 mask)
u64 epoch_ns;
u32 epoch_cyc;
+ if (cd.suspended)
+ return cd.epoch_ns;
+
/*
* Load the epoch_cyc and epoch_ns atomically. We do this by
* ensuring that we always write epoch_cyc, epoch_ns and
@@ -98,6 +103,13 @@ static void sched_clock_poll(unsigned long wrap_ticks)
update_sched_clock();
}
+void __init setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
+ unsigned long rate)
+{
+ setup_sched_clock(read, bits, rate);
+ cd.needs_suspend = true;
+}
+
void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
{
unsigned long r, w;
@@ -169,11 +181,23 @@ void __init sched_clock_postinit(void)
static int sched_clock_suspend(void)
{
sched_clock_poll(sched_clock_timer.data);
+ if (cd.needs_suspend)
+ cd.suspended = true;
return 0;
}
+static void sched_clock_resume(void)
+{
+ if (cd.needs_suspend) {
+ cd.epoch_cyc = read_sched_clock();
+ cd.epoch_cyc_copy = cd.epoch_cyc;
+ cd.suspended = false;
+ }
+}
+
static struct syscore_ops sched_clock_ops = {
.suspend = sched_clock_suspend,
+ .resume = sched_clock_resume,
};
static int __init sched_clock_syscore_init(void)
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index e15d83bb4ea3..a81dcecc7343 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -508,7 +508,7 @@ void __init dump_machine_table(void)
/* can't use cpu_relax() here as it may require MMU setup */;
}
-int __init arm_add_memory(phys_addr_t start, unsigned long size)
+int __init arm_add_memory(phys_addr_t start, phys_addr_t size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
@@ -538,7 +538,7 @@ int __init arm_add_memory(phys_addr_t start, unsigned long size)
}
#endif
- bank->size = size & PAGE_MASK;
+ bank->size = size & ~(phys_addr_t)(PAGE_SIZE - 1);
/*
* Check whether this memory region has non-zero size or
@@ -558,7 +558,7 @@ int __init arm_add_memory(phys_addr_t start, unsigned long size)
static int __init early_mem(char *p)
{
static int usermem __initdata = 0;
- unsigned long size;
+ phys_addr_t size;
phys_addr_t start;
char *endp;
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 536c5d6b340b..f27789e4e38a 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -27,7 +27,6 @@
*/
#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
-#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE)
/*
* With EABI, the syscall number has to be loaded into r7.
@@ -48,18 +47,6 @@ const unsigned long sigreturn_codes[7] = {
};
/*
- * Either we support OABI only, or we have EABI with the OABI
- * compat layer enabled. In the later case we don't know if
- * user space is EABI or not, and if not we must not clobber r7.
- * Always using the OABI syscall solves that issue and works for
- * all those cases.
- */
-const unsigned long syscall_restart_code[2] = {
- SWI_SYS_RESTART, /* swi __NR_restart_syscall */
- 0xe49df004, /* ldr pc, [sp], #4 */
-};
-
-/*
* atomically swap in the new signal mask, and wait for a signal.
*/
asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask)
@@ -582,12 +569,13 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
-static void do_signal(struct pt_regs *regs, int syscall)
+static int do_signal(struct pt_regs *regs, int syscall)
{
unsigned int retval = 0, continue_addr = 0, restart_addr = 0;
struct k_sigaction ka;
siginfo_t info;
int signr;
+ int restart = 0;
/*
* If we were from a system call, check for system call restarting...
@@ -602,15 +590,15 @@ static void do_signal(struct pt_regs *regs, int syscall)
* debugger will see the already changed PSW.
*/
switch (retval) {
+ case -ERESTART_RESTARTBLOCK:
+ restart -= 2;
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
+ restart++;
regs->ARM_r0 = regs->ARM_ORIG_r0;
regs->ARM_pc = restart_addr;
break;
- case -ERESTART_RESTARTBLOCK:
- regs->ARM_r0 = -EINTR;
- break;
}
}
@@ -619,14 +607,17 @@ static void do_signal(struct pt_regs *regs, int syscall)
* point the debugger may change all our registers ...
*/
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ /*
+ * Depending on the signal settings we may need to revert the
+ * decision to restart the system call. But skip this if a
+ * debugger has chosen to restart at a different PC.
+ */
+ if (regs->ARM_pc != restart_addr)
+ restart = 0;
if (signr > 0) {
- /*
- * Depending on the signal settings we may need to revert the
- * decision to restart the system call. But skip this if a
- * debugger has chosen to restart at a different PC.
- */
- if (regs->ARM_pc == restart_addr) {
- if (retval == -ERESTARTNOHAND
+ if (unlikely(restart)) {
+ if (retval == -ERESTARTNOHAND ||
+ retval == -ERESTART_RESTARTBLOCK
|| (retval == -ERESTARTSYS
&& !(ka.sa.sa_flags & SA_RESTART))) {
regs->ARM_r0 = -EINTR;
@@ -635,52 +626,43 @@ static void do_signal(struct pt_regs *regs, int syscall)
}
handle_signal(signr, &ka, &info, regs);
- return;
- }
-
- if (syscall) {
- /*
- * Handle restarting a different system call. As above,
- * if a debugger has chosen to restart at a different PC,
- * ignore the restart.
- */
- if (retval == -ERESTART_RESTARTBLOCK
- && regs->ARM_pc == continue_addr) {
- if (thumb_mode(regs)) {
- regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
- regs->ARM_pc -= 2;
- } else {
-#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
- regs->ARM_r7 = __NR_restart_syscall;
- regs->ARM_pc -= 4;
-#else
- u32 __user *usp;
-
- regs->ARM_sp -= 4;
- usp = (u32 __user *)regs->ARM_sp;
-
- if (put_user(regs->ARM_pc, usp) == 0) {
- regs->ARM_pc = KERN_RESTART_CODE;
- } else {
- regs->ARM_sp += 4;
- force_sigsegv(0, current);
- }
-#endif
- }
- }
+ return 0;
}
restore_saved_sigmask();
+ if (unlikely(restart))
+ regs->ARM_pc = continue_addr;
+ return restart;
}
-asmlinkage void
-do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
+asmlinkage int
+do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
- if (thread_flags & _TIF_SIGPENDING)
- do_signal(regs, syscall);
-
- if (thread_flags & _TIF_NOTIFY_RESUME) {
- clear_thread_flag(TIF_NOTIFY_RESUME);
- tracehook_notify_resume(regs);
- }
+ do {
+ if (likely(thread_flags & _TIF_NEED_RESCHED)) {
+ schedule();
+ } else {
+ if (unlikely(!user_mode(regs)))
+ return 0;
+ local_irq_enable();
+ if (thread_flags & _TIF_SIGPENDING) {
+ int restart = do_signal(regs, syscall);
+ if (unlikely(restart)) {
+ /*
+ * Restart without handlers.
+ * Deal with it without leaving
+ * the kernel space.
+ */
+ return restart;
+ }
+ syscall = 0;
+ } else {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
+ }
+ local_irq_disable();
+ thread_flags = current_thread_info()->flags;
+ } while (thread_flags & _TIF_WORK_MASK);
+ return 0;
}
diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h
index 6fcfe8398aa4..5ff067b7c752 100644
--- a/arch/arm/kernel/signal.h
+++ b/arch/arm/kernel/signal.h
@@ -8,7 +8,5 @@
* published by the Free Software Foundation.
*/
#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500)
-#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
extern const unsigned long sigreturn_codes[7];
-extern const unsigned long syscall_restart_code[2];
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index aea74f5bc34a..ebd8ad274d76 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -563,7 +563,8 @@ void smp_send_stop(void)
cpumask_copy(&mask, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &mask);
- smp_cross_call(&mask, IPI_CPU_STOP);
+ if (!cpumask_empty(&mask))
+ smp_cross_call(&mask, IPI_CPU_STOP);
/* Wait up to one second for other CPUs to stop */
timeout = USEC_PER_SEC;
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index fef42b21cecb..e1f906989bb8 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/clk.h>
-#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -96,7 +95,52 @@ static void twd_timer_stop(struct clock_event_device *clk)
disable_percpu_irq(clk->irq);
}
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_COMMON_CLK
+
+/*
+ * Updates clockevent frequency when the cpu frequency changes.
+ * Called on the cpu that is changing frequency with interrupts disabled.
+ */
+static void twd_update_frequency(void *new_rate)
+{
+ twd_timer_rate = *((unsigned long *) new_rate);
+
+ clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
+}
+
+static int twd_rate_change(struct notifier_block *nb,
+ unsigned long flags, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+
+ /*
+ * The twd clock events must be reprogrammed to account for the new
+ * frequency. The timer is local to a cpu, so cross-call to the
+ * changing cpu.
+ */
+ if (flags == POST_RATE_CHANGE)
+ smp_call_function(twd_update_frequency,
+ (void *)&cnd->new_rate, 1);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block twd_clk_nb = {
+ .notifier_call = twd_rate_change,
+};
+
+static int twd_clk_init(void)
+{
+ if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
+ return clk_notifier_register(twd_clk, &twd_clk_nb);
+
+ return 0;
+}
+core_initcall(twd_clk_init);
+
+#elif defined (CONFIG_CPU_FREQ)
+
+#include <linux/cpufreq.h>
/*
* Updates clockevent frequency when the cpu frequency changes.
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 198b08456e90..26c12c6440fc 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -321,7 +321,7 @@ void store_cpu_topology(unsigned int cpuid)
* init_cpu_topology is called at boot when only one cpu is running
* which prevent simultaneous write access to cpu_topology array
*/
-void init_cpu_topology(void)
+void __init init_cpu_topology(void)
{
unsigned int cpu;
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 8b97d739b17b..b0179b89a04c 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -402,18 +402,10 @@ static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
- unsigned int correction = thumb_mode(regs) ? 2 : 4;
unsigned int instr;
siginfo_t info;
void __user *pc;
- /*
- * According to the ARM ARM, PC is 2 or 4 bytes ahead,
- * depending whether we're in Thumb mode or not.
- * Correct this offset.
- */
- regs->ARM_pc -= correction;
-
pc = (void __user *)instruction_pointer(regs);
if (processor_mode(regs) == SVC_MODE) {
@@ -428,20 +420,23 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
#endif
instr = *(u32 *) pc;
} else if (thumb_mode(regs)) {
- get_user(instr, (u16 __user *)pc);
+ if (get_user(instr, (u16 __user *)pc))
+ goto die_sig;
if (is_wide_instruction(instr)) {
unsigned int instr2;
- get_user(instr2, (u16 __user *)pc+1);
+ if (get_user(instr2, (u16 __user *)pc+1))
+ goto die_sig;
instr <<= 16;
instr |= instr2;
}
- } else {
- get_user(instr, (u32 __user *)pc);
+ } else if (get_user(instr, (u32 __user *)pc)) {
+ goto die_sig;
}
if (call_undef_hook(regs, instr) == 0)
return;
+die_sig:
#ifdef CONFIG_DEBUG_USER
if (user_debug & UDBG_UNDEFINED) {
printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
@@ -852,8 +847,6 @@ void __init early_trap_init(void *vectors_base)
*/
memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
sigreturn_codes, sizeof(sigreturn_codes));
- memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
- syscall_restart_code, sizeof(syscall_restart_code));
flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 2473fd1fd51c..af72969820b4 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -16,13 +16,30 @@ lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \
call_with_stack.o
mmu-y := clear_user.o copy_page.o getuser.o putuser.o
-mmu-y += copy_from_user.o copy_to_user.o
+
+# the code in uaccess.S is not preemption safe and
+# probably faster on ARMv3 only
+ifeq ($(CONFIG_PREEMPT),y)
+ mmu-y += copy_from_user.o copy_to_user.o
+else
+ifneq ($(CONFIG_CPU_32v3),y)
+ mmu-y += copy_from_user.o copy_to_user.o
+else
+ mmu-y += uaccess.o
+endif
+endif
# using lib_ here won't override already available weak symbols
obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o
-lib-$(CONFIG_MMU) += $(mmu-y)
-lib-y += io-readsw-armv4.o io-writesw-armv4.o
+lib-$(CONFIG_MMU) += $(mmu-y)
+
+ifeq ($(CONFIG_CPU_32v3),y)
+ lib-y += io-readsw-armv3.o io-writesw-armv3.o
+else
+ lib-y += io-readsw-armv4.o io-writesw-armv4.o
+endif
+
lib-$(CONFIG_ARCH_RPC) += ecard.o io-acorn.o floppydma.o
lib-$(CONFIG_ARCH_SHARK) += io-shark.o
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
index d6dacc69254e..395d5fbb8fa2 100644
--- a/arch/arm/lib/delay.c
+++ b/arch/arm/lib/delay.c
@@ -59,6 +59,7 @@ void __init init_current_timer_delay(unsigned long freq)
{
pr_info("Switching to timer-based delay loop\n");
lpj_fine = freq / HZ;
+ loops_per_jiffy = lpj_fine;
arm_delay_ops.delay = __timer_delay;
arm_delay_ops.const_udelay = __timer_const_udelay;
arm_delay_ops.udelay = __timer_udelay;
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index 11093a7c3e32..9b06bb41fca6 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -16,8 +16,9 @@
* __get_user_X
*
* Inputs: r0 contains the address
+ * r1 contains the address limit, which must be preserved
* Outputs: r0 is the error code
- * r2, r3 contains the zero-extended value
+ * r2 contains the zero-extended value
* lr corrupted
*
* No other registers must be altered. (see <asm/uaccess.h>
@@ -27,33 +28,39 @@
* Note also that it is intended that __get_user_bad is not global.
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#include <asm/errno.h>
#include <asm/domain.h>
ENTRY(__get_user_1)
+ check_uaccess r0, 1, r1, r2, __get_user_bad
1: TUSER(ldrb) r2, [r0]
mov r0, #0
mov pc, lr
ENDPROC(__get_user_1)
ENTRY(__get_user_2)
-#ifdef CONFIG_THUMB2_KERNEL
-2: TUSER(ldrb) r2, [r0]
-3: TUSER(ldrb) r3, [r0, #1]
+ check_uaccess r0, 2, r1, r2, __get_user_bad
+#ifdef CONFIG_CPU_USE_DOMAINS
+rb .req ip
+2: ldrbt r2, [r0], #1
+3: ldrbt rb, [r0], #0
#else
-2: TUSER(ldrb) r2, [r0], #1
-3: TUSER(ldrb) r3, [r0]
+rb .req r0
+2: ldrb r2, [r0]
+3: ldrb rb, [r0, #1]
#endif
#ifndef __ARMEB__
- orr r2, r2, r3, lsl #8
+ orr r2, r2, rb, lsl #8
#else
- orr r2, r3, r2, lsl #8
+ orr r2, rb, r2, lsl #8
#endif
mov r0, #0
mov pc, lr
ENDPROC(__get_user_2)
ENTRY(__get_user_4)
+ check_uaccess r0, 4, r1, r2, __get_user_bad
4: TUSER(ldr) r2, [r0]
mov r0, #0
mov pc, lr
diff --git a/arch/arm/lib/io-readsw-armv3.S b/arch/arm/lib/io-readsw-armv3.S
new file mode 100644
index 000000000000..88487c8c4f23
--- /dev/null
+++ b/arch/arm/lib/io-readsw-armv3.S
@@ -0,0 +1,106 @@
+/*
+ * linux/arch/arm/lib/io-readsw-armv3.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * 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/linkage.h>
+#include <asm/assembler.h>
+
+.Linsw_bad_alignment:
+ adr r0, .Linsw_bad_align_msg
+ mov r2, lr
+ b panic
+.Linsw_bad_align_msg:
+ .asciz "insw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+ .align
+
+.Linsw_align: tst r1, #1
+ bne .Linsw_bad_alignment
+
+ ldr r3, [r0]
+ strb r3, [r1], #1
+ mov r3, r3, lsr #8
+ strb r3, [r1], #1
+
+ subs r2, r2, #1
+ moveq pc, lr
+
+ENTRY(__raw_readsw)
+ teq r2, #0 @ do we have to check for the zero len?
+ moveq pc, lr
+ tst r1, #3
+ bne .Linsw_align
+
+.Linsw_aligned: mov ip, #0xff
+ orr ip, ip, ip, lsl #8
+ stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .Lno_insw_8
+
+.Linsw_8_lp: ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ ldr r5, [r0]
+ and r5, r5, ip
+ ldr r6, [r0]
+ orr r5, r5, r6, lsl #16
+
+ ldr r6, [r0]
+ and r6, r6, ip
+ ldr lr, [r0]
+ orr r6, r6, lr, lsl #16
+
+ stmia r1!, {r3 - r6}
+
+ subs r2, r2, #8
+ bpl .Linsw_8_lp
+
+ tst r2, #7
+ ldmeqfd sp!, {r4, r5, r6, pc}
+
+.Lno_insw_8: tst r2, #4
+ beq .Lno_insw_4
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ stmia r1!, {r3, r4}
+
+.Lno_insw_4: tst r2, #2
+ beq .Lno_insw_2
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ str r3, [r1], #4
+
+.Lno_insw_2: tst r2, #1
+ ldrne r3, [r0]
+ strneb r3, [r1], #1
+ movne r3, r3, lsr #8
+ strneb r3, [r1]
+
+ ldmfd sp!, {r4, r5, r6, pc}
+
+
diff --git a/arch/arm/lib/io-writesw-armv3.S b/arch/arm/lib/io-writesw-armv3.S
new file mode 100644
index 000000000000..49b800419e32
--- /dev/null
+++ b/arch/arm/lib/io-writesw-armv3.S
@@ -0,0 +1,126 @@
+/*
+ * linux/arch/arm/lib/io-writesw-armv3.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * 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/linkage.h>
+#include <asm/assembler.h>
+
+.Loutsw_bad_alignment:
+ adr r0, .Loutsw_bad_align_msg
+ mov r2, lr
+ b panic
+.Loutsw_bad_align_msg:
+ .asciz "outsw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+ .align
+
+.Loutsw_align: tst r1, #1
+ bne .Loutsw_bad_alignment
+
+ add r1, r1, #2
+
+ ldr r3, [r1, #-4]
+ mov r3, r3, lsr #16
+ orr r3, r3, r3, lsl #16
+ str r3, [r0]
+ subs r2, r2, #1
+ moveq pc, lr
+
+ENTRY(__raw_writesw)
+ teq r2, #0 @ do we have to check for the zero len?
+ moveq pc, lr
+ tst r1, #3
+ bne .Loutsw_align
+
+ stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .Lno_outsw_8
+
+.Loutsw_8_lp: ldmia r1!, {r3, r4, r5, r6}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r5, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r5, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r6, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r6, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ subs r2, r2, #8
+ bpl .Loutsw_8_lp
+
+ tst r2, #7
+ ldmeqfd sp!, {r4, r5, r6, pc}
+
+.Lno_outsw_8: tst r2, #4
+ beq .Lno_outsw_4
+
+ ldmia r1!, {r3, r4}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.Lno_outsw_4: tst r2, #2
+ beq .Lno_outsw_2
+
+ ldr r3, [r1], #4
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.Lno_outsw_2: tst r2, #1
+
+ ldrne r3, [r1]
+
+ movne ip, r3, lsl #16
+ orrne ip, ip, ip, lsr #16
+ strne ip, [r0]
+
+ ldmfd sp!, {r4, r5, r6, pc}
diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S
index 7db25990c589..3d73dcb959b0 100644
--- a/arch/arm/lib/putuser.S
+++ b/arch/arm/lib/putuser.S
@@ -16,6 +16,7 @@
* __put_user_X
*
* Inputs: r0 contains the address
+ * r1 contains the address limit, which must be preserved
* r2, r3 contains the value
* Outputs: r0 is the error code
* lr corrupted
@@ -27,16 +28,19 @@
* Note also that it is intended that __put_user_bad is not global.
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
#include <asm/errno.h>
#include <asm/domain.h>
ENTRY(__put_user_1)
+ check_uaccess r0, 1, r1, ip, __put_user_bad
1: TUSER(strb) r2, [r0]
mov r0, #0
mov pc, lr
ENDPROC(__put_user_1)
ENTRY(__put_user_2)
+ check_uaccess r0, 2, r1, ip, __put_user_bad
mov ip, r2, lsr #8
#ifdef CONFIG_THUMB2_KERNEL
#ifndef __ARMEB__
@@ -60,12 +64,14 @@ ENTRY(__put_user_2)
ENDPROC(__put_user_2)
ENTRY(__put_user_4)
+ check_uaccess r0, 4, r1, ip, __put_user_bad
4: TUSER(str) r2, [r0]
mov r0, #0
mov pc, lr
ENDPROC(__put_user_4)
ENTRY(__put_user_8)
+ check_uaccess r0, 8, r1, ip, __put_user_bad
#ifdef CONFIG_THUMB2_KERNEL
5: TUSER(str) r2, [r0]
6: TUSER(str) r3, [r0, #4]
diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S
new file mode 100644
index 000000000000..5c908b1cb8ed
--- /dev/null
+++ b/arch/arm/lib/uaccess.S
@@ -0,0 +1,564 @@
+/*
+ * linux/arch/arm/lib/uaccess.S
+ *
+ * Copyright (C) 1995, 1996,1997,1998 Russell King
+ *
+ * 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.
+ *
+ * Routines to block copy data to/from user memory
+ * These are highly optimised both for the 4k page size
+ * and for various alignments.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+#include <asm/domain.h>
+
+ .text
+
+#define PAGE_SHIFT 12
+
+/* Prototype: int __copy_to_user(void *to, const char *from, size_t n)
+ * Purpose : copy a block to user memory from kernel memory
+ * Params : to - user memory
+ * : from - kernel memory
+ * : n - number of bytes to copy
+ * Returns : Number of bytes NOT copied.
+ */
+
+.Lc2u_dest_not_aligned:
+ rsb ip, ip, #4
+ cmp ip, #2
+ ldrb r3, [r1], #1
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #1
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ sub r2, r2, ip
+ b .Lc2u_dest_aligned
+
+ENTRY(__copy_to_user)
+ stmfd sp!, {r2, r4 - r7, lr}
+ cmp r2, #4
+ blt .Lc2u_not_enough
+ ands ip, r0, #3
+ bne .Lc2u_dest_not_aligned
+.Lc2u_dest_aligned:
+
+ ands ip, r1, #3
+ bne .Lc2u_src_not_aligned
+/*
+ * Seeing as there has to be at least 8 bytes to copy, we can
+ * copy one word, and force a user-mode page fault...
+ */
+
+.Lc2u_0fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_0nowords
+ ldr r3, [r1], #4
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_0fupi
+/*
+ * ip = max no. of bytes to copy before needing another "strt" insn
+ */
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #32
+ blt .Lc2u_0rem8lp
+
+.Lc2u_0cpy8lp: ldmia r1!, {r3 - r6}
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ ldmia r1!, {r3 - r6}
+ subs ip, ip, #32
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_0cpy8lp
+
+.Lc2u_0rem8lp: cmn ip, #16
+ ldmgeia r1!, {r3 - r6}
+ stmgeia r0!, {r3 - r6} @ Shouldnt fault
+ tst ip, #8
+ ldmneia r1!, {r3 - r4}
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ ldrne r3, [r1], #4
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_0fupi
+.Lc2u_0nowords: teq ip, #0
+ beq .Lc2u_finished
+.Lc2u_nowords: cmp ip, #2
+ ldrb r3, [r1], #1
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #1
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_not_enough:
+ movs ip, r2
+ bne .Lc2u_nowords
+.Lc2u_finished: mov r0, #0
+ ldmfd sp!, {r2, r4 - r7, pc}
+
+.Lc2u_src_not_aligned:
+ bic r1, r1, #3
+ ldr r7, [r1], #4
+ cmp ip, #2
+ bgt .Lc2u_3fupi
+ beq .Lc2u_2fupi
+.Lc2u_1fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_1nowords
+ mov r3, r7, pull #8
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #24
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_1fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_1rem8lp
+
+.Lc2u_1cpy8lp: mov r3, r7, pull #8
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #24
+ mov r4, r4, pull #8
+ orr r4, r4, r5, push #24
+ mov r5, r5, pull #8
+ orr r5, r5, r6, push #24
+ mov r6, r6, pull #8
+ orr r6, r6, r7, push #24
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_1cpy8lp
+
+.Lc2u_1rem8lp: tst ip, #8
+ movne r3, r7, pull #8
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #24
+ movne r4, r4, pull #8
+ orrne r4, r4, r7, push #24
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #8
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #24
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_1fupi
+.Lc2u_1nowords: mov r3, r7, get_byte_1
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ movge r3, r7, get_byte_2
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ movgt r3, r7, get_byte_3
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_2fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_2nowords
+ mov r3, r7, pull #16
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #16
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_2fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_2rem8lp
+
+.Lc2u_2cpy8lp: mov r3, r7, pull #16
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #16
+ mov r4, r4, pull #16
+ orr r4, r4, r5, push #16
+ mov r5, r5, pull #16
+ orr r5, r5, r6, push #16
+ mov r6, r6, pull #16
+ orr r6, r6, r7, push #16
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_2cpy8lp
+
+.Lc2u_2rem8lp: tst ip, #8
+ movne r3, r7, pull #16
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #16
+ movne r4, r4, pull #16
+ orrne r4, r4, r7, push #16
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #16
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #16
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_2fupi
+.Lc2u_2nowords: mov r3, r7, get_byte_2
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ movge r3, r7, get_byte_3
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #0
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_3fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_3nowords
+ mov r3, r7, pull #24
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #8
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_3fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_3rem8lp
+
+.Lc2u_3cpy8lp: mov r3, r7, pull #24
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #8
+ mov r4, r4, pull #24
+ orr r4, r4, r5, push #8
+ mov r5, r5, pull #24
+ orr r5, r5, r6, push #8
+ mov r6, r6, pull #24
+ orr r6, r6, r7, push #8
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_3cpy8lp
+
+.Lc2u_3rem8lp: tst ip, #8
+ movne r3, r7, pull #24
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #8
+ movne r4, r4, pull #24
+ orrne r4, r4, r7, push #8
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #24
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #8
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_3fupi
+.Lc2u_3nowords: mov r3, r7, get_byte_3
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #0
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+ENDPROC(__copy_to_user)
+
+ .pushsection .fixup,"ax"
+ .align 0
+9001: ldmfd sp!, {r0, r4 - r7, pc}
+ .popsection
+
+/* Prototype: unsigned long __copy_from_user(void *to,const void *from,unsigned long n);
+ * Purpose : copy a block from user memory to kernel memory
+ * Params : to - kernel memory
+ * : from - user memory
+ * : n - number of bytes to copy
+ * Returns : Number of bytes NOT copied.
+ */
+.Lcfu_dest_not_aligned:
+ rsb ip, ip, #4
+ cmp ip, #2
+USER( TUSER( ldrb) r3, [r1], #1) @ May fault
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ sub r2, r2, ip
+ b .Lcfu_dest_aligned
+
+ENTRY(__copy_from_user)
+ stmfd sp!, {r0, r2, r4 - r7, lr}
+ cmp r2, #4
+ blt .Lcfu_not_enough
+ ands ip, r0, #3
+ bne .Lcfu_dest_not_aligned
+.Lcfu_dest_aligned:
+ ands ip, r1, #3
+ bne .Lcfu_src_not_aligned
+
+/*
+ * Seeing as there has to be at least 8 bytes to copy, we can
+ * copy one word, and force a user-mode page fault...
+ */
+
+.Lcfu_0fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_0nowords
+USER( TUSER( ldr) r3, [r1], #4)
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_0fupi
+/*
+ * ip = max no. of bytes to copy before needing another "strt" insn
+ */
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #32
+ blt .Lcfu_0rem8lp
+
+.Lcfu_0cpy8lp: ldmia r1!, {r3 - r6} @ Shouldnt fault
+ stmia r0!, {r3 - r6}
+ ldmia r1!, {r3 - r6} @ Shouldnt fault
+ subs ip, ip, #32
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_0cpy8lp
+
+.Lcfu_0rem8lp: cmn ip, #16
+ ldmgeia r1!, {r3 - r6} @ Shouldnt fault
+ stmgeia r0!, {r3 - r6}
+ tst ip, #8
+ ldmneia r1!, {r3 - r4} @ Shouldnt fault
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ TUSER( ldrne) r3, [r1], #4 @ Shouldnt fault
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_0fupi
+.Lcfu_0nowords: teq ip, #0
+ beq .Lcfu_finished
+.Lcfu_nowords: cmp ip, #2
+USER( TUSER( ldrb) r3, [r1], #1) @ May fault
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_not_enough:
+ movs ip, r2
+ bne .Lcfu_nowords
+.Lcfu_finished: mov r0, #0
+ add sp, sp, #8
+ ldmfd sp!, {r4 - r7, pc}
+
+.Lcfu_src_not_aligned:
+ bic r1, r1, #3
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ cmp ip, #2
+ bgt .Lcfu_3fupi
+ beq .Lcfu_2fupi
+.Lcfu_1fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_1nowords
+ mov r3, r7, pull #8
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #24
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_1fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_1rem8lp
+
+.Lcfu_1cpy8lp: mov r3, r7, pull #8
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ subs ip, ip, #16
+ orr r3, r3, r4, push #24
+ mov r4, r4, pull #8
+ orr r4, r4, r5, push #24
+ mov r5, r5, pull #8
+ orr r5, r5, r6, push #24
+ mov r6, r6, pull #8
+ orr r6, r6, r7, push #24
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_1cpy8lp
+
+.Lcfu_1rem8lp: tst ip, #8
+ movne r3, r7, pull #8
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #24
+ movne r4, r4, pull #8
+ orrne r4, r4, r7, push #24
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #8
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #24
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_1fupi
+.Lcfu_1nowords: mov r3, r7, get_byte_1
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+ movge r3, r7, get_byte_2
+ strgeb r3, [r0], #1
+ movgt r3, r7, get_byte_3
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_2fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_2nowords
+ mov r3, r7, pull #16
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #16
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_2fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_2rem8lp
+
+
+.Lcfu_2cpy8lp: mov r3, r7, pull #16
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ subs ip, ip, #16
+ orr r3, r3, r4, push #16
+ mov r4, r4, pull #16
+ orr r4, r4, r5, push #16
+ mov r5, r5, pull #16
+ orr r5, r5, r6, push #16
+ mov r6, r6, pull #16
+ orr r6, r6, r7, push #16
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_2cpy8lp
+
+.Lcfu_2rem8lp: tst ip, #8
+ movne r3, r7, pull #16
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #16
+ movne r4, r4, pull #16
+ orrne r4, r4, r7, push #16
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #16
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #16
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_2fupi
+.Lcfu_2nowords: mov r3, r7, get_byte_2
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+ movge r3, r7, get_byte_3
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_3fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_3nowords
+ mov r3, r7, pull #24
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #8
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_3fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_3rem8lp
+
+.Lcfu_3cpy8lp: mov r3, r7, pull #24
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ orr r3, r3, r4, push #8
+ mov r4, r4, pull #24
+ orr r4, r4, r5, push #8
+ mov r5, r5, pull #24
+ orr r5, r5, r6, push #8
+ mov r6, r6, pull #24
+ orr r6, r6, r7, push #8
+ stmia r0!, {r3 - r6}
+ subs ip, ip, #16
+ bpl .Lcfu_3cpy8lp
+
+.Lcfu_3rem8lp: tst ip, #8
+ movne r3, r7, pull #24
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #8
+ movne r4, r4, pull #24
+ orrne r4, r4, r7, push #8
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #24
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #8
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_3fupi
+.Lcfu_3nowords: mov r3, r7, get_byte_3
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+ENDPROC(__copy_from_user)
+
+ .pushsection .fixup,"ax"
+ .align 0
+ /*
+ * We took an exception. r0 contains a pointer to
+ * the byte not copied.
+ */
+9001: ldr r2, [sp], #4 @ void *to
+ sub r2, r0, r2 @ bytes copied
+ ldr r1, [sp], #4 @ unsigned long count
+ subs r4, r1, r2 @ bytes left to copy
+ movne r1, r4
+ blne __memzero
+ mov r0, r4
+ ldmfd sp!, {r4 - r7, pc}
+ .popsection
+
diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c
index 104ca40d8d18..aaa443b48c91 100644
--- a/arch/arm/mach-at91/at91rm9200_time.c
+++ b/arch/arm/mach-at91/at91rm9200_time.c
@@ -197,7 +197,7 @@ void __init at91rm9200_timer_init(void)
at91_st_read(AT91_ST_SR);
/* Make IRQs happen for the system timer */
- setup_irq(AT91_ID_SYS, &at91rm9200_timer_irq);
+ setup_irq(NR_IRQS_LEGACY + AT91_ID_SYS, &at91rm9200_timer_irq);
/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used
* directly for the clocksource and all clockevents, after adjusting
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index 7b9c2ba396ed..bce572a530ef 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -726,6 +726,8 @@ static struct resource rtt_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
},
};
@@ -744,10 +746,12 @@ static void __init at91_add_device_rtt_rtc(void)
* The second resource is needed:
* GPBR will serve as the storage for RTC time offset
*/
- at91sam9260_rtt_device.num_resources = 2;
+ at91sam9260_rtt_device.num_resources = 3;
rtt_resources[1].start = AT91SAM9260_BASE_GPBR +
4 * CONFIG_RTC_DRV_AT91SAM9_GPBR;
rtt_resources[1].end = rtt_resources[1].start + 3;
+ rtt_resources[2].start = NR_IRQS_LEGACY + AT91_ID_SYS;
+ rtt_resources[2].end = NR_IRQS_LEGACY + AT91_ID_SYS;
}
#else
static void __init at91_add_device_rtt_rtc(void)
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 8df5c1bdff92..bc2590d712d0 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -609,6 +609,8 @@ static struct resource rtt_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
}
};
@@ -626,10 +628,12 @@ static void __init at91_add_device_rtt_rtc(void)
* The second resource is needed:
* GPBR will serve as the storage for RTC time offset
*/
- at91sam9261_rtt_device.num_resources = 2;
+ at91sam9261_rtt_device.num_resources = 3;
rtt_resources[1].start = AT91SAM9261_BASE_GPBR +
4 * CONFIG_RTC_DRV_AT91SAM9_GPBR;
rtt_resources[1].end = rtt_resources[1].start + 3;
+ rtt_resources[2].start = NR_IRQS_LEGACY + AT91_ID_SYS;
+ rtt_resources[2].end = NR_IRQS_LEGACY + AT91_ID_SYS;
}
#else
static void __init at91_add_device_rtt_rtc(void)
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index eb6bbf86fb9f..9b6ca734f1a9 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -990,6 +990,8 @@ static struct resource rtt0_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
}
};
@@ -1006,6 +1008,8 @@ static struct resource rtt1_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
}
};
@@ -1027,14 +1031,14 @@ static void __init at91_add_device_rtt_rtc(void)
* The second resource is needed only for the chosen RTT:
* GPBR will serve as the storage for RTC time offset
*/
- at91sam9263_rtt0_device.num_resources = 2;
+ at91sam9263_rtt0_device.num_resources = 3;
at91sam9263_rtt1_device.num_resources = 1;
pdev = &at91sam9263_rtt0_device;
r = rtt0_resources;
break;
case 1:
at91sam9263_rtt0_device.num_resources = 1;
- at91sam9263_rtt1_device.num_resources = 2;
+ at91sam9263_rtt1_device.num_resources = 3;
pdev = &at91sam9263_rtt1_device;
r = rtt1_resources;
break;
@@ -1047,6 +1051,8 @@ static void __init at91_add_device_rtt_rtc(void)
pdev->name = "rtc-at91sam9";
r[1].start = AT91SAM9263_BASE_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR;
r[1].end = r[1].start + 3;
+ r[2].start = NR_IRQS_LEGACY + AT91_ID_SYS;
+ r[2].end = NR_IRQS_LEGACY + AT91_ID_SYS;
}
#else
static void __init at91_add_device_rtt_rtc(void)
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index 06073996a382..1b47319ca00b 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1293,6 +1293,8 @@ static struct resource rtt_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
}
};
@@ -1310,10 +1312,12 @@ static void __init at91_add_device_rtt_rtc(void)
* The second resource is needed:
* GPBR will serve as the storage for RTC time offset
*/
- at91sam9g45_rtt_device.num_resources = 2;
+ at91sam9g45_rtt_device.num_resources = 3;
rtt_resources[1].start = AT91SAM9G45_BASE_GPBR +
4 * CONFIG_RTC_DRV_AT91SAM9_GPBR;
rtt_resources[1].end = rtt_resources[1].start + 3;
+ rtt_resources[2].start = NR_IRQS_LEGACY + AT91_ID_SYS;
+ rtt_resources[2].end = NR_IRQS_LEGACY + AT91_ID_SYS;
}
#else
static void __init at91_add_device_rtt_rtc(void)
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index f09fff932172..b3d365dadef5 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -688,6 +688,8 @@ static struct resource rtt_resources[] = {
.flags = IORESOURCE_MEM,
}, {
.flags = IORESOURCE_MEM,
+ }, {
+ .flags = IORESOURCE_IRQ,
}
};
@@ -705,10 +707,12 @@ static void __init at91_add_device_rtt_rtc(void)
* The second resource is needed:
* GPBR will serve as the storage for RTC time offset
*/
- at91sam9rl_rtt_device.num_resources = 2;
+ at91sam9rl_rtt_device.num_resources = 3;
rtt_resources[1].start = AT91SAM9RL_BASE_GPBR +
4 * CONFIG_RTC_DRV_AT91SAM9_GPBR;
rtt_resources[1].end = rtt_resources[1].start + 3;
+ rtt_resources[2].start = NR_IRQS_LEGACY + AT91_ID_SYS;
+ rtt_resources[2].end = NR_IRQS_LEGACY + AT91_ID_SYS;
}
#else
static void __init at91_add_device_rtt_rtc(void)
diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c
index de2ec6b8fea7..188c82971ebd 100644
--- a/arch/arm/mach-at91/clock.c
+++ b/arch/arm/mach-at91/clock.c
@@ -63,6 +63,12 @@ EXPORT_SYMBOL_GPL(at91_pmc_base);
#define cpu_has_300M_plla() (cpu_is_at91sam9g10())
+#define cpu_has_240M_plla() (cpu_is_at91sam9261() \
+ || cpu_is_at91sam9263() \
+ || cpu_is_at91sam9rl())
+
+#define cpu_has_210M_plla() (cpu_is_at91sam9260())
+
#define cpu_has_pllb() (!(cpu_is_at91sam9rl() \
|| cpu_is_at91sam9g45() \
|| cpu_is_at91sam9x5() \
@@ -706,6 +712,12 @@ static int __init at91_pmc_init(unsigned long main_clock)
} else if (cpu_has_800M_plla()) {
if (plla.rate_hz > 800000000)
pll_overclock = true;
+ } else if (cpu_has_240M_plla()) {
+ if (plla.rate_hz > 240000000)
+ pll_overclock = true;
+ } else if (cpu_has_210M_plla()) {
+ if (plla.rate_hz > 210000000)
+ pll_overclock = true;
} else {
if (plla.rate_hz > 209000000)
pll_overclock = true;
diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c
index 5de69f2fcca9..f6b9fc70161b 100644
--- a/arch/arm/mach-davinci/board-neuros-osd2.c
+++ b/arch/arm/mach-davinci/board-neuros-osd2.c
@@ -162,38 +162,6 @@ static void __init davinci_ntosd2_map_io(void)
dm644x_init();
}
-/*
- I2C initialization
-*/
-static struct davinci_i2c_platform_data ntosd2_i2c_pdata = {
- .bus_freq = 20 /* kHz */,
- .bus_delay = 100 /* usec */,
-};
-
-static struct i2c_board_info __initdata ntosd2_i2c_info[] = {
-};
-
-static int ntosd2_init_i2c(void)
-{
- int status;
-
- davinci_init_i2c(&ntosd2_i2c_pdata);
- status = gpio_request(NTOSD2_MSP430_IRQ, ntosd2_i2c_info[0].type);
- if (status == 0) {
- status = gpio_direction_input(NTOSD2_MSP430_IRQ);
- if (status == 0) {
- status = gpio_to_irq(NTOSD2_MSP430_IRQ);
- if (status > 0) {
- ntosd2_i2c_info[0].irq = status;
- i2c_register_board_info(1,
- ntosd2_i2c_info,
- ARRAY_SIZE(ntosd2_i2c_info));
- }
- }
- }
- return status;
-}
-
static struct davinci_mmc_config davinci_ntosd2_mmc_config = {
.wires = 4,
.version = MMC_CTLR_VERSION_1
@@ -218,7 +186,6 @@ static __init void davinci_ntosd2_init(void)
{
struct clk *aemif_clk;
struct davinci_soc_info *soc_info = &davinci_soc_info;
- int status;
aemif_clk = clk_get(NULL, "aemif");
clk_enable(aemif_clk);
@@ -242,12 +209,6 @@ static __init void davinci_ntosd2_init(void)
platform_add_devices(davinci_ntosd2_devices,
ARRAY_SIZE(davinci_ntosd2_devices));
- /* Initialize I2C interface specific for this board */
- status = ntosd2_init_i2c();
- if (status < 0)
- pr_warning("davinci_ntosd2_init: msp430 irq setup failed:"
- " %d\n", status);
-
davinci_serial_init(&uart_config);
dm644x_init_asp(&dm644x_ntosd2_snd_data);
diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index d1624a315c9a..783eab6845c4 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -546,6 +546,7 @@ static struct lcd_ctrl_config lcd_cfg = {
.sync_edge = 0,
.sync_ctrl = 1,
.raster_order = 0,
+ .fifo_th = 6,
};
struct da8xx_lcdc_platform_data sharp_lcd035q3dg01_pdata = {
diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c
index 4db5de54b6a7..6321567d8eaa 100644
--- a/arch/arm/mach-dove/common.c
+++ b/arch/arm/mach-dove/common.c
@@ -102,7 +102,8 @@ void __init dove_ehci1_init(void)
void __init dove_ge00_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge00_init(eth_data, DOVE_GE00_PHYS_BASE,
- IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR);
+ IRQ_DOVE_GE00_SUM, IRQ_DOVE_GE00_ERR,
+ 1600);
}
/*****************************************************************************
diff --git a/arch/arm/mach-dove/irq.c b/arch/arm/mach-dove/irq.c
index f07fd16e0c9b..9bc97a5baaa8 100644
--- a/arch/arm/mach-dove/irq.c
+++ b/arch/arm/mach-dove/irq.c
@@ -20,22 +20,6 @@
#include <mach/bridge-regs.h>
#include "common.h"
-static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- int irqoff;
- BUG_ON(irq < IRQ_DOVE_GPIO_0_7 || irq > IRQ_DOVE_HIGH_GPIO);
-
- irqoff = irq <= IRQ_DOVE_GPIO_16_23 ? irq - IRQ_DOVE_GPIO_0_7 :
- 3 + irq - IRQ_DOVE_GPIO_24_31;
-
- orion_gpio_irq_handler(irqoff << 3);
- if (irq == IRQ_DOVE_HIGH_GPIO) {
- orion_gpio_irq_handler(40);
- orion_gpio_irq_handler(48);
- orion_gpio_irq_handler(56);
- }
-}
-
static void pmu_irq_mask(struct irq_data *d)
{
int pin = irq_to_pmu(d->irq);
@@ -90,6 +74,27 @@ static void pmu_irq_handler(unsigned int irq, struct irq_desc *desc)
}
}
+static int __initdata gpio0_irqs[4] = {
+ IRQ_DOVE_GPIO_0_7,
+ IRQ_DOVE_GPIO_8_15,
+ IRQ_DOVE_GPIO_16_23,
+ IRQ_DOVE_GPIO_24_31,
+};
+
+static int __initdata gpio1_irqs[4] = {
+ IRQ_DOVE_HIGH_GPIO,
+ 0,
+ 0,
+ 0,
+};
+
+static int __initdata gpio2_irqs[4] = {
+ 0,
+ 0,
+ 0,
+ 0,
+};
+
void __init dove_init_irq(void)
{
int i;
@@ -100,19 +105,14 @@ void __init dove_init_irq(void)
/*
* Initialize gpiolib for GPIOs 0-71.
*/
- orion_gpio_init(0, 32, DOVE_GPIO_LO_VIRT_BASE, 0,
- IRQ_DOVE_GPIO_START);
- irq_set_chained_handler(IRQ_DOVE_GPIO_0_7, gpio_irq_handler);
- irq_set_chained_handler(IRQ_DOVE_GPIO_8_15, gpio_irq_handler);
- irq_set_chained_handler(IRQ_DOVE_GPIO_16_23, gpio_irq_handler);
- irq_set_chained_handler(IRQ_DOVE_GPIO_24_31, gpio_irq_handler);
-
- orion_gpio_init(32, 32, DOVE_GPIO_HI_VIRT_BASE, 0,
- IRQ_DOVE_GPIO_START + 32);
- irq_set_chained_handler(IRQ_DOVE_HIGH_GPIO, gpio_irq_handler);
-
- orion_gpio_init(64, 8, DOVE_GPIO2_VIRT_BASE, 0,
- IRQ_DOVE_GPIO_START + 64);
+ orion_gpio_init(NULL, 0, 32, (void __iomem *)DOVE_GPIO_LO_VIRT_BASE, 0,
+ IRQ_DOVE_GPIO_START, gpio0_irqs);
+
+ orion_gpio_init(NULL, 32, 32, (void __iomem *)DOVE_GPIO_HI_VIRT_BASE, 0,
+ IRQ_DOVE_GPIO_START + 32, gpio1_irqs);
+
+ orion_gpio_init(NULL, 64, 8, (void __iomem *)DOVE_GPIO2_VIRT_BASE, 0,
+ IRQ_DOVE_GPIO_START + 64, gpio2_irqs);
/*
* Mask and clear PMU interrupts
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index 5ca80307d6d7..4e574c24581c 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -42,6 +42,7 @@
#include <plat/backlight.h>
#include <plat/fb.h>
#include <plat/mfc.h>
+#include <plat/hdmi.h>
#include <mach/ohci.h>
#include <mach/map.h>
@@ -734,6 +735,11 @@ static void __init origen_bt_setup(void)
s3c_gpio_setpull(EXYNOS4_GPX2(2), S3C_GPIO_PULL_NONE);
}
+/* I2C module and id for HDMIPHY */
+static struct i2c_board_info hdmiphy_info = {
+ I2C_BOARD_INFO("hdmiphy-exynos4210", 0x38),
+};
+
static void s5p_tv_setup(void)
{
/* Direct HPD to HDMI chip */
@@ -781,6 +787,7 @@ static void __init origen_machine_init(void)
s5p_tv_setup();
s5p_i2c_hdmiphy_set_platdata(NULL);
+ s5p_hdmi_set_platdata(&hdmiphy_info, NULL, 0);
#ifdef CONFIG_DRM_EXYNOS
s5p_device_fimd0.dev.platform_data = &drm_fimd_pdata;
diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c
index 3cfa688d274a..73f2bce097e1 100644
--- a/arch/arm/mach-exynos/mach-smdkv310.c
+++ b/arch/arm/mach-exynos/mach-smdkv310.c
@@ -40,6 +40,7 @@
#include <plat/mfc.h>
#include <plat/ehci.h>
#include <plat/clock.h>
+#include <plat/hdmi.h>
#include <mach/map.h>
#include <mach/ohci.h>
@@ -354,6 +355,11 @@ static struct platform_pwm_backlight_data smdkv310_bl_data = {
.pwm_period_ns = 1000,
};
+/* I2C module and id for HDMIPHY */
+static struct i2c_board_info hdmiphy_info = {
+ I2C_BOARD_INFO("hdmiphy-exynos4210", 0x38),
+};
+
static void s5p_tv_setup(void)
{
/* direct HPD to HDMI chip */
@@ -388,6 +394,7 @@ static void __init smdkv310_machine_init(void)
s5p_tv_setup();
s5p_i2c_hdmiphy_set_platdata(NULL);
+ s5p_hdmi_set_platdata(&hdmiphy_info, NULL, 0);
samsung_keypad_set_platdata(&smdkv310_keypad_data);
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index 373c3c00d24c..c0bc83a7663e 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -115,7 +115,7 @@ static __init int exynos_pm_dt_parse_domains(void)
}
#endif /* CONFIG_OF */
-static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
+static __init __maybe_unused void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
struct exynos_pm_domain *pd)
{
if (pdev->dev.bus) {
diff --git a/arch/arm/mach-gemini/irq.c b/arch/arm/mach-gemini/irq.c
index ca70e5fcc7ac..020852d3bdd8 100644
--- a/arch/arm/mach-gemini/irq.c
+++ b/arch/arm/mach-gemini/irq.c
@@ -17,6 +17,7 @@
#include <linux/sched.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
+#include <asm/system_misc.h>
#include <mach/hardware.h>
#define IRQ_SOURCE(base_addr) (base_addr + 0x00)
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile
index 07f7c226e4cf..d004d37ad9d8 100644
--- a/arch/arm/mach-imx/Makefile
+++ b/arch/arm/mach-imx/Makefile
@@ -9,7 +9,8 @@ obj-$(CONFIG_SOC_IMX27) += clk-imx27.o mm-imx27.o ehci-imx27.o
obj-$(CONFIG_SOC_IMX31) += mm-imx3.o cpu-imx31.o clk-imx31.o iomux-imx31.o ehci-imx31.o pm-imx3.o
obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clk-imx35.o ehci-imx35.o pm-imx3.o
-obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clk-imx51-imx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o
+imx5-pm-$(CONFIG_PM) += pm-imx5.o
+obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clk-imx51-imx53.o ehci-imx5.o $(imx5-pm-y) cpu_op-mx51.o
obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \
clk-pfd.o clk-busy.o
@@ -70,14 +71,13 @@ obj-$(CONFIG_DEBUG_LL) += lluart.o
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
-obj-$(CONFIG_CPU_V7) += head-v7.o
-AFLAGS_head-v7.o :=-Wa,-march=armv7-a
-obj-$(CONFIG_SMP) += platsmp.o
+AFLAGS_headsmp.o :=-Wa,-march=armv7-a
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o
ifeq ($(CONFIG_PM),y)
-obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o
+obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
endif
# i.MX5 based machines
diff --git a/arch/arm/mach-imx/clk-imx25.c b/arch/arm/mach-imx/clk-imx25.c
index fdd8cc87c9fe..d20d4795f4ea 100644
--- a/arch/arm/mach-imx/clk-imx25.c
+++ b/arch/arm/mach-imx/clk-imx25.c
@@ -222,10 +222,8 @@ int __init mx25_clocks_init(void)
clk_register_clkdev(clk[lcdc_ipg], "ipg", "imx-fb.0");
clk_register_clkdev(clk[lcdc_ahb], "ahb", "imx-fb.0");
clk_register_clkdev(clk[wdt_ipg], NULL, "imx2-wdt.0");
- clk_register_clkdev(clk[ssi1_ipg_per], "per", "imx-ssi.0");
- clk_register_clkdev(clk[ssi1_ipg], "ipg", "imx-ssi.0");
- clk_register_clkdev(clk[ssi2_ipg_per], "per", "imx-ssi.1");
- clk_register_clkdev(clk[ssi2_ipg], "ipg", "imx-ssi.1");
+ clk_register_clkdev(clk[ssi1_ipg], NULL, "imx-ssi.0");
+ clk_register_clkdev(clk[ssi2_ipg], NULL, "imx-ssi.1");
clk_register_clkdev(clk[esdhc1_ipg_per], "per", "sdhci-esdhc-imx25.0");
clk_register_clkdev(clk[esdhc1_ipg], "ipg", "sdhci-esdhc-imx25.0");
clk_register_clkdev(clk[esdhc1_ahb], "ahb", "sdhci-esdhc-imx25.0");
@@ -243,6 +241,6 @@ int __init mx25_clocks_init(void)
clk_register_clkdev(clk[sdma_ahb], "ahb", "imx35-sdma");
clk_register_clkdev(clk[iim_ipg], "iim", NULL);
- mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54);
+ mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), MX25_INT_GPT1);
return 0;
}
diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c
index 7aa6313fb167..3b6b640eed24 100644
--- a/arch/arm/mach-imx/clk-imx27.c
+++ b/arch/arm/mach-imx/clk-imx27.c
@@ -223,7 +223,7 @@ int __init mx27_clocks_init(unsigned long fref)
clk_register_clkdev(clk[per3_gate], "per", "imx-fb.0");
clk_register_clkdev(clk[lcdc_ipg_gate], "ipg", "imx-fb.0");
clk_register_clkdev(clk[lcdc_ahb_gate], "ahb", "imx-fb.0");
- clk_register_clkdev(clk[csi_ahb_gate], NULL, "mx2-camera.0");
+ clk_register_clkdev(clk[csi_ahb_gate], "ahb", "mx2-camera.0");
clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc");
clk_register_clkdev(clk[usb_ipg_gate], "ipg", "fsl-usb2-udc");
clk_register_clkdev(clk[usb_ahb_gate], "ahb", "fsl-usb2-udc");
@@ -239,8 +239,8 @@ int __init mx27_clocks_init(unsigned long fref)
clk_register_clkdev(clk[ssi1_ipg_gate], NULL, "imx-ssi.0");
clk_register_clkdev(clk[ssi2_ipg_gate], NULL, "imx-ssi.1");
clk_register_clkdev(clk[nfc_baud_gate], NULL, "mxc_nand.0");
- clk_register_clkdev(clk[vpu_baud_gate], "per", "imx-vpu");
- clk_register_clkdev(clk[vpu_ahb_gate], "ahb", "imx-vpu");
+ clk_register_clkdev(clk[vpu_baud_gate], "per", "coda-imx27.0");
+ clk_register_clkdev(clk[vpu_ahb_gate], "ahb", "coda-imx27.0");
clk_register_clkdev(clk[dma_ahb_gate], "ahb", "imx-dma");
clk_register_clkdev(clk[dma_ipg_gate], "ipg", "imx-dma");
clk_register_clkdev(clk[fec_ipg_gate], "ipg", "imx27-fec.0");
@@ -250,8 +250,10 @@ int __init mx27_clocks_init(unsigned long fref)
clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx-i2c.1");
clk_register_clkdev(clk[owire_ipg_gate], NULL, "mxc_w1.0");
clk_register_clkdev(clk[kpp_ipg_gate], NULL, "imx-keypad");
- clk_register_clkdev(clk[emma_ahb_gate], "ahb", "imx-emma");
- clk_register_clkdev(clk[emma_ipg_gate], "ipg", "imx-emma");
+ clk_register_clkdev(clk[emma_ahb_gate], "emma-ahb", "mx2-camera.0");
+ clk_register_clkdev(clk[emma_ipg_gate], "emma-ipg", "mx2-camera.0");
+ clk_register_clkdev(clk[emma_ahb_gate], "ahb", "m2m-emmaprp.0");
+ clk_register_clkdev(clk[emma_ipg_gate], "ipg", "m2m-emmaprp.0");
clk_register_clkdev(clk[iim_ipg_gate], "iim", NULL);
clk_register_clkdev(clk[gpio_ipg_gate], "gpio", NULL);
clk_register_clkdev(clk[brom_ahb_gate], "brom", NULL);
diff --git a/arch/arm/mach-imx/clk-imx31.c b/arch/arm/mach-imx/clk-imx31.c
index 8e19e70f90f9..1253af2d9971 100644
--- a/arch/arm/mach-imx/clk-imx31.c
+++ b/arch/arm/mach-imx/clk-imx31.c
@@ -130,7 +130,7 @@ int __init mx31_clocks_init(unsigned long fref)
clk_register_clkdev(clk[nfc], NULL, "mxc_nand.0");
clk_register_clkdev(clk[ipu_gate], NULL, "ipu-core");
clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb");
- clk_register_clkdev(clk[kpp_gate], "kpp", NULL);
+ clk_register_clkdev(clk[kpp_gate], NULL, "imx-keypad");
clk_register_clkdev(clk[usb_div_post], "per", "mxc-ehci.0");
clk_register_clkdev(clk[usb_gate], "ahb", "mxc-ehci.0");
clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.0");
diff --git a/arch/arm/mach-imx/clk-imx35.c b/arch/arm/mach-imx/clk-imx35.c
index c6422fb10bae..65fb8bcd86cb 100644
--- a/arch/arm/mach-imx/clk-imx35.c
+++ b/arch/arm/mach-imx/clk-imx35.c
@@ -230,10 +230,8 @@ int __init mx35_clocks_init()
clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb");
clk_register_clkdev(clk[owire_gate], NULL, "mxc_w1");
clk_register_clkdev(clk[sdma_gate], NULL, "imx35-sdma");
- clk_register_clkdev(clk[ipg], "ipg", "imx-ssi.0");
- clk_register_clkdev(clk[ssi1_div_post], "per", "imx-ssi.0");
- clk_register_clkdev(clk[ipg], "ipg", "imx-ssi.1");
- clk_register_clkdev(clk[ssi2_div_post], "per", "imx-ssi.1");
+ clk_register_clkdev(clk[ssi1_gate], NULL, "imx-ssi.0");
+ clk_register_clkdev(clk[ssi2_gate], NULL, "imx-ssi.1");
/* i.mx35 has the i.mx21 type uart */
clk_register_clkdev(clk[uart1_gate], "per", "imx21-uart.0");
clk_register_clkdev(clk[ipg], "ipg", "imx21-uart.0");
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index f6086693ebd2..4bdcaa97bd98 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -303,6 +303,7 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk_prepare_enable(clk[aips_tz2]); /* fec */
clk_prepare_enable(clk[spba]);
clk_prepare_enable(clk[emi_fast_gate]); /* fec */
+ clk_prepare_enable(clk[emi_slow_gate]); /* eim */
clk_prepare_enable(clk[tmax1]);
clk_prepare_enable(clk[tmax2]); /* esdhc2, fec */
clk_prepare_enable(clk[tmax3]); /* esdhc1, esdhc4 */
diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
index ea89520b6e22..4233d9e3531d 100644
--- a/arch/arm/mach-imx/clk-imx6q.c
+++ b/arch/arm/mach-imx/clk-imx6q.c
@@ -152,7 +152,7 @@ enum mx6q_clks {
ssi2, ssi3, uart_ipg, uart_serial, usboh3, usdhc1, usdhc2, usdhc3,
usdhc4, vdo_axi, vpu_axi, cko1, pll1_sys, pll2_bus, pll3_usb_otg,
pll4_audio, pll5_video, pll6_mlb, pll7_usb_host, pll8_enet, ssi1_ipg,
- ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2,
+ ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
clk_max
};
@@ -288,8 +288,10 @@ int __init mx6q_clocks_init(void)
clk[gpu3d_shader] = imx_clk_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3);
clk[ipu1_podf] = imx_clk_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3);
clk[ipu2_podf] = imx_clk_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3);
- clk[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_sel", base + 0x20, 10, 1);
- clk[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_sel", base + 0x20, 11, 1);
+ clk[ldb_di0_div_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7);
+ clk[ldb_di0_podf] = imx_clk_divider("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1);
+ clk[ldb_di1_div_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
+ clk[ldb_di1_podf] = imx_clk_divider("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1);
clk[ipu1_di0_pre] = imx_clk_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3);
clk[ipu1_di1_pre] = imx_clk_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3);
clk[ipu2_di0_pre] = imx_clk_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3);
diff --git a/arch/arm/mach-imx/devices-imx27.h b/arch/arm/mach-imx/devices-imx27.h
index 436c5720fe6a..04822932cdd1 100644
--- a/arch/arm/mach-imx/devices-imx27.h
+++ b/arch/arm/mach-imx/devices-imx27.h
@@ -17,6 +17,10 @@ extern const struct imx_fsl_usb2_udc_data imx27_fsl_usb2_udc_data;
#define imx27_add_fsl_usb2_udc(pdata) \
imx_add_fsl_usb2_udc(&imx27_fsl_usb2_udc_data, pdata)
+extern const struct imx_imx27_coda_data imx27_coda_data;
+#define imx27_add_coda() \
+ imx_add_imx27_coda(&imx27_coda_data)
+
extern const struct imx_imx2_wdt_data imx27_imx2_wdt_data;
#define imx27_add_imx2_wdt() \
imx_add_imx2_wdt(&imx27_imx2_wdt_data)
diff --git a/arch/arm/mach-imx/head-v7.S b/arch/arm/mach-imx/headsmp.S
index 7e49deb128a4..7e49deb128a4 100644
--- a/arch/arm/mach-imx/head-v7.S
+++ b/arch/arm/mach-imx/headsmp.S
diff --git a/arch/arm/mach-imx/hotplug.c b/arch/arm/mach-imx/hotplug.c
index 20ed2d56c1af..f8f7437c83b8 100644
--- a/arch/arm/mach-imx/hotplug.c
+++ b/arch/arm/mach-imx/hotplug.c
@@ -42,22 +42,6 @@ static inline void cpu_enter_lowpower(void)
: "cc");
}
-static inline void cpu_leave_lowpower(void)
-{
- unsigned int v;
-
- asm volatile(
- "mrc p15, 0, %0, c1, c0, 0\n"
- " orr %0, %0, %1\n"
- " mcr p15, 0, %0, c1, c0, 0\n"
- " mrc p15, 0, %0, c1, c0, 1\n"
- " orr %0, %0, %2\n"
- " mcr p15, 0, %0, c1, c0, 1\n"
- : "=&r" (v)
- : "Ir" (CR_C), "Ir" (0x40)
- : "cc");
-}
-
/*
* platform-specific code to shutdown a CPU
*
@@ -67,11 +51,10 @@ void platform_cpu_die(unsigned int cpu)
{
cpu_enter_lowpower();
imx_enable_cpu(cpu, false);
- cpu_do_idle();
- cpu_leave_lowpower();
- /* We should never return from idle */
- panic("cpu %d unexpectedly exit from shutdown\n", cpu);
+ /* spin here until hardware takes it down */
+ while (1)
+ ;
}
int platform_cpu_disable(unsigned int cpu)
diff --git a/arch/arm/mach-imx/mach-armadillo5x0.c b/arch/arm/mach-imx/mach-armadillo5x0.c
index 2c6ab3273f9e..5985ed1b8c98 100644
--- a/arch/arm/mach-imx/mach-armadillo5x0.c
+++ b/arch/arm/mach-imx/mach-armadillo5x0.c
@@ -526,7 +526,8 @@ static void __init armadillo5x0_init(void)
imx31_add_mxc_nand(&armadillo5x0_nand_board_info);
/* set NAND page size to 2k if not configured via boot mode pins */
- __raw_writel(__raw_readl(MXC_CCM_RCSR) | (1 << 30), MXC_CCM_RCSR);
+ __raw_writel(__raw_readl(mx3_ccm_base + MXC_CCM_RCSR) |
+ (1 << 30), mx3_ccm_base + MXC_CCM_RCSR);
/* RTC */
/* Get RTC IRQ and register the chip */
diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
index f264ddddd47c..821d6aac411c 100644
--- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
+++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c
@@ -32,13 +32,13 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/leds.h>
-#include <linux/memblock.h>
#include <media/soc_camera.h>
#include <sound/tlv320aic32x4.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <asm/system_info.h>
+#include <asm/memblock.h>
#include <mach/common.h>
#include <mach/hardware.h>
#include <mach/iomux-mx27.h>
@@ -233,10 +233,8 @@ static void __init visstrim_camera_init(void)
static void __init visstrim_reserve(void)
{
/* reserve 4 MiB for mx2-camera */
- mx2_camera_base = memblock_alloc(MX2_CAMERA_BUF_SIZE,
+ mx2_camera_base = arm_memblock_steal(3 * MX2_CAMERA_BUF_SIZE,
MX2_CAMERA_BUF_SIZE);
- memblock_free(mx2_camera_base, MX2_CAMERA_BUF_SIZE);
- memblock_remove(mx2_camera_base, MX2_CAMERA_BUF_SIZE);
}
/* GPIOs used as events for applications */
@@ -405,6 +403,47 @@ static const struct imx_ssi_platform_data visstrim_m10_ssi_pdata __initconst = {
.flags = IMX_SSI_DMA | IMX_SSI_SYN,
};
+/* coda */
+
+static void __init visstrim_coda_init(void)
+{
+ struct platform_device *pdev;
+ int dma;
+
+ pdev = imx27_add_coda();
+ dma = dma_declare_coherent_memory(&pdev->dev,
+ mx2_camera_base + MX2_CAMERA_BUF_SIZE,
+ mx2_camera_base + MX2_CAMERA_BUF_SIZE,
+ MX2_CAMERA_BUF_SIZE,
+ DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE);
+ if (!(dma & DMA_MEMORY_MAP))
+ return;
+}
+
+/* DMA deinterlace */
+static struct platform_device visstrim_deinterlace = {
+ .name = "m2m-deinterlace",
+ .id = 0,
+};
+
+static void __init visstrim_deinterlace_init(void)
+{
+ int ret = -ENOMEM;
+ struct platform_device *pdev = &visstrim_deinterlace;
+ int dma;
+
+ ret = platform_device_register(pdev);
+
+ dma = dma_declare_coherent_memory(&pdev->dev,
+ mx2_camera_base + 2 * MX2_CAMERA_BUF_SIZE,
+ mx2_camera_base + 2 * MX2_CAMERA_BUF_SIZE,
+ MX2_CAMERA_BUF_SIZE,
+ DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE);
+ if (!(dma & DMA_MEMORY_MAP))
+ return;
+}
+
+
static void __init visstrim_m10_revision(void)
{
int exp_version = 0;
@@ -467,7 +506,9 @@ static void __init visstrim_m10_board_init(void)
platform_device_register_resndata(NULL, "soc-camera-pdrv", 0, NULL, 0,
&iclink_tvp5150, sizeof(iclink_tvp5150));
gpio_led_register_device(0, &visstrim_m10_led_data);
+ visstrim_deinterlace_init();
visstrim_camera_init();
+ visstrim_coda_init();
}
static void __init visstrim_m10_timer_init(void)
diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c
index 5ec0608f2a76..045b3f6a387d 100644
--- a/arch/arm/mach-imx/mach-imx6q.c
+++ b/arch/arm/mach-imx/mach-imx6q.c
@@ -71,7 +71,7 @@ soft:
/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */
static int ksz9021rn_phy_fixup(struct phy_device *phydev)
{
- if (IS_ENABLED(CONFIG_PHYLIB)) {
+ if (IS_BUILTIN(CONFIG_PHYLIB)) {
/* min rx data delay */
phy_write(phydev, 0x0b, 0x8105);
phy_write(phydev, 0x0c, 0x0000);
@@ -112,7 +112,7 @@ put_clk:
static void __init imx6q_sabrelite_init(void)
{
- if (IS_ENABLED(CONFIG_PHYLIB))
+ if (IS_BUILTIN(CONFIG_PHYLIB))
phy_register_fixup_for_uid(PHY_ID_KSZ9021, MICREL_PHY_ID_MASK,
ksz9021rn_phy_fixup);
imx6q_sabrelite_cko1_setup();
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
index ebf680bebdf2..3fa6c51390da 100644
--- a/arch/arm/mach-integrator/core.c
+++ b/arch/arm/mach-integrator/core.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
index 7b1055c8e0b9..3b2267529f5e 100644
--- a/arch/arm/mach-integrator/integrator_ap.c
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -456,7 +456,7 @@ static void __init ap_init_timer(void)
clk = clk_get_sys("ap_timer", NULL);
BUG_ON(IS_ERR(clk));
- clk_enable(clk);
+ clk_prepare_enable(clk);
rate = clk_get_rate(clk);
writel(0, TIMER0_VA_BASE + TIMER_CTRL);
diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig
index 199764fe0fb0..ca5c15a4e626 100644
--- a/arch/arm/mach-kirkwood/Kconfig
+++ b/arch/arm/mach-kirkwood/Kconfig
@@ -80,6 +80,35 @@ config MACH_IB62X0_DT
RaidSonic IB-NAS6210 & IB-NAS6220 devices, using
Flattened Device Tree.
+config MACH_TS219_DT
+ bool "Device Tree for QNAP TS-11X, TS-21X NAS"
+ select ARCH_KIRKWOOD_DT
+ select ARM_APPENDED_DTB
+ select ARM_ATAG_DTB_COMPAT
+ help
+ Say 'Y' here if you want your kernel to support the QNAP
+ TS-110, TS-119, TS-119P+, TS-210, TS-219, TS-219P and
+ TS-219P+ Turbo NAS devices using Fattened Device Tree.
+ There are two different Device Tree descriptions, depending
+ on if the device is based on an if the board uses the MV6281
+ or MV6282. If you have the wrong one, the buttons will not
+ work.
+
+config MACH_GOFLEXNET_DT
+ bool "Seagate GoFlex Net (Flattened Device Tree)"
+ select ARCH_KIRKWOOD_DT
+ help
+ Say 'Y' here if you want your kernel to support the
+ Seagate GoFlex Net (Flattened Device Tree).
+
+config MACH_LSXL_DT
+ bool "Buffalo Linkstation LS-XHL, LS-CHLv2 (Flattened Device Tree)"
+ select ARCH_KIRKWOOD_DT
+ help
+ Say 'Y' here if you want your kernel to support the
+ Buffalo Linkstation LS-XHL & LS-CHLv2 devices, using
+ Flattened Device Tree.
+
config MACH_TS219
bool "QNAP TS-110, TS-119, TS-119P+, TS-210, TS-219, TS-219P and TS-219P+ Turbo NAS"
help
diff --git a/arch/arm/mach-kirkwood/Makefile b/arch/arm/mach-kirkwood/Makefile
index d2b05907b10e..055c85a1cc46 100644
--- a/arch/arm/mach-kirkwood/Makefile
+++ b/arch/arm/mach-kirkwood/Makefile
@@ -25,3 +25,6 @@ obj-$(CONFIG_MACH_DREAMPLUG_DT) += board-dreamplug.o
obj-$(CONFIG_MACH_ICONNECT_DT) += board-iconnect.o
obj-$(CONFIG_MACH_DLINK_KIRKWOOD_DT) += board-dnskw.o
obj-$(CONFIG_MACH_IB62X0_DT) += board-ib62x0.o
+obj-$(CONFIG_MACH_TS219_DT) += board-ts219.o tsx1x-common.o
+obj-$(CONFIG_MACH_GOFLEXNET_DT) += board-goflexnet.o
+obj-$(CONFIG_MACH_LSXL_DT) += board-lsxl.o
diff --git a/arch/arm/mach-kirkwood/Makefile.boot b/arch/arm/mach-kirkwood/Makefile.boot
index 02edbdf5b065..a13299d758e1 100644
--- a/arch/arm/mach-kirkwood/Makefile.boot
+++ b/arch/arm/mach-kirkwood/Makefile.boot
@@ -7,3 +7,8 @@ dtb-$(CONFIG_MACH_DLINK_KIRKWOOD_DT) += kirkwood-dns320.dtb
dtb-$(CONFIG_MACH_DLINK_KIRKWOOD_DT) += kirkwood-dns325.dtb
dtb-$(CONFIG_MACH_ICONNECT_DT) += kirkwood-iconnect.dtb
dtb-$(CONFIG_MACH_IB62X0_DT) += kirkwood-ib62x0.dtb
+dtb-$(CONFIG_MACH_TS219_DT) += kirkwood-ts219-6281.dtb
+dtb-$(CONFIG_MACH_TS219_DT) += kirkwood-ts219-6282.dtb
+dtb-$(CONFIG_MACH_GOFLEXNET_DT) += kirkwood-goflexnet.dtb
+dtb-$(CONFIG_MACH_LSXL_DT) += kirkwood-lschlv2.dtb
+dtb-$(CONFIG_MACH_LSXL_DT) += kirkwood-lsxhl.dtb
diff --git a/arch/arm/mach-kirkwood/board-dnskw.c b/arch/arm/mach-kirkwood/board-dnskw.c
index 58c2d68f9443..4ab35065a144 100644
--- a/arch/arm/mach-kirkwood/board-dnskw.c
+++ b/arch/arm/mach-kirkwood/board-dnskw.c
@@ -14,13 +14,11 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/i2c.h>
#include <linux/ata_platform.h>
#include <linux/mv643xx_eth.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/input.h>
-#include <linux/gpio_keys.h>
#include <linux/gpio-fan.h>
#include <linux/leds.h>
#include <asm/mach-types.h>
@@ -35,10 +33,6 @@ static struct mv643xx_eth_platform_data dnskw_ge00_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(8),
};
-static struct mv_sata_platform_data dnskw_sata_data = {
- .n_ports = 2,
-};
-
static unsigned int dnskw_mpp_config[] __initdata = {
MPP13_UART1_TXD, /* Custom ... */
MPP14_UART1_RXD, /* ... Controller (DNS-320 only) */
@@ -73,132 +67,6 @@ static unsigned int dnskw_mpp_config[] __initdata = {
0
};
-static struct gpio_led dns325_led_pins[] = {
- {
- .name = "dns325:white:power",
- .gpio = 26,
- .active_low = 1,
- .default_trigger = "default-on",
- },
- {
- .name = "dns325:white:usb",
- .gpio = 43,
- .active_low = 1,
- },
- {
- .name = "dns325:red:l_hdd",
- .gpio = 28,
- .active_low = 1,
- },
- {
- .name = "dns325:red:r_hdd",
- .gpio = 27,
- .active_low = 1,
- },
- {
- .name = "dns325:red:usb",
- .gpio = 29,
- .active_low = 1,
- },
-};
-
-static struct gpio_led_platform_data dns325_led_data = {
- .num_leds = ARRAY_SIZE(dns325_led_pins),
- .leds = dns325_led_pins,
-};
-
-static struct platform_device dns325_led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &dns325_led_data,
- },
-};
-
-static struct gpio_led dns320_led_pins[] = {
- {
- .name = "dns320:blue:power",
- .gpio = 26,
- .active_low = 1,
- .default_trigger = "default-on",
- },
- {
- .name = "dns320:blue:usb",
- .gpio = 43,
- .active_low = 1,
- },
- {
- .name = "dns320:orange:l_hdd",
- .gpio = 28,
- .active_low = 1,
- },
- {
- .name = "dns320:orange:r_hdd",
- .gpio = 27,
- .active_low = 1,
- },
- {
- .name = "dns320:orange:usb",
- .gpio = 35,
- .active_low = 1,
- },
-};
-
-static struct gpio_led_platform_data dns320_led_data = {
- .num_leds = ARRAY_SIZE(dns320_led_pins),
- .leds = dns320_led_pins,
-};
-
-static struct platform_device dns320_led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &dns320_led_data,
- },
-};
-
-static struct i2c_board_info dns325_i2c_board_info[] __initdata = {
- {
- I2C_BOARD_INFO("lm75", 0x48),
- },
- /* Something at 0x0c also */
-};
-
-static struct gpio_keys_button dnskw_button_pins[] = {
- {
- .code = KEY_POWER,
- .gpio = 34,
- .desc = "Power button",
- .active_low = 1,
- },
- {
- .code = KEY_EJECTCD,
- .gpio = 47,
- .desc = "USB unmount button",
- .active_low = 1,
- },
- {
- .code = KEY_RESTART,
- .gpio = 48,
- .desc = "Reset button",
- .active_low = 1,
- },
-};
-
-static struct gpio_keys_platform_data dnskw_button_data = {
- .buttons = dnskw_button_pins,
- .nbuttons = ARRAY_SIZE(dnskw_button_pins),
-};
-
-static struct platform_device dnskw_button_device = {
- .name = "gpio-keys",
- .id = -1,
- .num_resources = 0,
- .dev = {
- .platform_data = &dnskw_button_data,
- }
-};
-
/* Fan: ADDA AD045HB-G73 40mm 6000rpm@5v */
static struct gpio_fan_speed dnskw_fan_speed[] = {
{ 0, 0 },
@@ -245,20 +113,9 @@ void __init dnskw_init(void)
kirkwood_ehci_init();
kirkwood_ge00_init(&dnskw_ge00_data);
- kirkwood_sata_init(&dnskw_sata_data);
- kirkwood_i2c_init();
- platform_device_register(&dnskw_button_device);
platform_device_register(&dnskw_fan_device);
- if (of_machine_is_compatible("dlink,dns-325")) {
- i2c_register_board_info(0, dns325_i2c_board_info,
- ARRAY_SIZE(dns325_i2c_board_info));
- platform_device_register(&dns325_led_device);
-
- } else if (of_machine_is_compatible("dlink,dns-320"))
- platform_device_register(&dns320_led_device);
-
/* Register power-off GPIO. */
if (gpio_request(36, "dnskw:power:off") == 0
&& gpio_direction_output(36, 0) == 0)
diff --git a/arch/arm/mach-kirkwood/board-dreamplug.c b/arch/arm/mach-kirkwood/board-dreamplug.c
index 55e357ab2923..aeb234d0d0e3 100644
--- a/arch/arm/mach-kirkwood/board-dreamplug.c
+++ b/arch/arm/mach-kirkwood/board-dreamplug.c
@@ -14,7 +14,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/mtd/partitions.h>
#include <linux/ata_platform.h>
#include <linux/mv643xx_eth.h>
#include <linux/of.h>
@@ -23,7 +22,6 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/gpio.h>
-#include <linux/leds.h>
#include <linux/mtd/physmap.h>
#include <linux/spi/flash.h>
#include <linux/spi/spi.h>
@@ -36,42 +34,6 @@
#include "common.h"
#include "mpp.h"
-struct mtd_partition dreamplug_partitions[] = {
- {
- .name = "u-boot",
- .size = SZ_512K,
- .offset = 0,
- },
- {
- .name = "u-boot env",
- .size = SZ_64K,
- .offset = SZ_512K + SZ_512K,
- },
- {
- .name = "dtb",
- .size = SZ_64K,
- .offset = SZ_512K + SZ_512K + SZ_512K,
- },
-};
-
-static const struct flash_platform_data dreamplug_spi_slave_data = {
- .type = "mx25l1606e",
- .name = "spi_flash",
- .parts = dreamplug_partitions,
- .nr_parts = ARRAY_SIZE(dreamplug_partitions),
-};
-
-static struct spi_board_info __initdata dreamplug_spi_slave_info[] = {
- {
- .modalias = "m25p80",
- .platform_data = &dreamplug_spi_slave_data,
- .irq = -1,
- .max_speed_hz = 50000000,
- .bus_num = 0,
- .chip_select = 0,
- },
-};
-
static struct mv643xx_eth_platform_data dreamplug_ge00_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(0),
};
@@ -80,45 +42,10 @@ static struct mv643xx_eth_platform_data dreamplug_ge01_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(1),
};
-static struct mv_sata_platform_data dreamplug_sata_data = {
- .n_ports = 1,
-};
-
static struct mvsdio_platform_data dreamplug_mvsdio_data = {
/* unfortunately the CD signal has not been connected */
};
-static struct gpio_led dreamplug_led_pins[] = {
- {
- .name = "dreamplug:blue:bluetooth",
- .gpio = 47,
- .active_low = 1,
- },
- {
- .name = "dreamplug:green:wifi",
- .gpio = 48,
- .active_low = 1,
- },
- {
- .name = "dreamplug:green:wifi_ap",
- .gpio = 49,
- .active_low = 1,
- },
-};
-
-static struct gpio_led_platform_data dreamplug_led_data = {
- .leds = dreamplug_led_pins,
- .num_leds = ARRAY_SIZE(dreamplug_led_pins),
-};
-
-static struct platform_device dreamplug_leds = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &dreamplug_led_data,
- }
-};
-
static unsigned int dreamplug_mpp_config[] __initdata = {
MPP0_SPI_SCn,
MPP1_SPI_MOSI,
@@ -137,15 +64,8 @@ void __init dreamplug_init(void)
*/
kirkwood_mpp_conf(dreamplug_mpp_config);
- spi_register_board_info(dreamplug_spi_slave_info,
- ARRAY_SIZE(dreamplug_spi_slave_info));
- kirkwood_spi_init();
-
kirkwood_ehci_init();
kirkwood_ge00_init(&dreamplug_ge00_data);
kirkwood_ge01_init(&dreamplug_ge01_data);
- kirkwood_sata_init(&dreamplug_sata_data);
kirkwood_sdio_init(&dreamplug_mvsdio_data);
-
- platform_device_register(&dreamplug_leds);
}
diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c
index edc3f8a9d45e..e4eb450de301 100644
--- a/arch/arm/mach-kirkwood/board-dt.c
+++ b/arch/arm/mach-kirkwood/board-dt.c
@@ -18,6 +18,7 @@
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/bridge-regs.h>
+#include <plat/irq.h>
#include "common.h"
static struct of_device_id kirkwood_dt_match_table[] __initdata = {
@@ -25,6 +26,16 @@ static struct of_device_id kirkwood_dt_match_table[] __initdata = {
{ }
};
+struct of_dev_auxdata kirkwood_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("marvell,orion-spi", 0xf1010600, "orion_spi.0", NULL),
+ OF_DEV_AUXDATA("marvell,mv64xxx-i2c", 0xf1011000, "mv64xxx_i2c.0",
+ NULL),
+ OF_DEV_AUXDATA("marvell,orion-wdt", 0xf1020300, "orion_wdt", NULL),
+ OF_DEV_AUXDATA("marvell,orion-sata", 0xf1080000, "sata_mv.0", NULL),
+ OF_DEV_AUXDATA("marvell,orion-nand", 0xf4000000, "orion_nand", NULL),
+ {},
+};
+
static void __init kirkwood_dt_init(void)
{
pr_info("Kirkwood: %s, TCLK=%d.\n", kirkwood_id(), kirkwood_tclk);
@@ -47,7 +58,6 @@ static void __init kirkwood_dt_init(void)
kirkwood_clk_init();
/* internal devices that every board has */
- kirkwood_wdt_init();
kirkwood_xor0_init();
kirkwood_xor1_init();
kirkwood_crypto_init();
@@ -68,7 +78,17 @@ static void __init kirkwood_dt_init(void)
if (of_machine_is_compatible("raidsonic,ib-nas62x0"))
ib62x0_init();
- of_platform_populate(NULL, kirkwood_dt_match_table, NULL, NULL);
+ if (of_machine_is_compatible("qnap,ts219"))
+ qnap_dt_ts219_init();
+
+ if (of_machine_is_compatible("seagate,goflexnet"))
+ goflexnet_init();
+
+ if (of_machine_is_compatible("buffalo,lsxl"))
+ lsxl_init();
+
+ of_platform_populate(NULL, kirkwood_dt_match_table,
+ kirkwood_auxdata_lookup, NULL);
}
static const char *kirkwood_dt_board_compat[] = {
@@ -77,6 +97,9 @@ static const char *kirkwood_dt_board_compat[] = {
"dlink,dns-325",
"iom,iconnect",
"raidsonic,ib-nas62x0",
+ "qnap,ts219",
+ "seagate,goflexnet",
+ "buffalo,lsxl",
NULL
};
@@ -84,7 +107,7 @@ DT_MACHINE_START(KIRKWOOD_DT, "Marvell Kirkwood (Flattened Device Tree)")
/* Maintainer: Jason Cooper <jason@lakedaemon.net> */
.map_io = kirkwood_map_io,
.init_early = kirkwood_init_early,
- .init_irq = kirkwood_init_irq,
+ .init_irq = orion_dt_init_irq,
.timer = &kirkwood_timer,
.init_machine = kirkwood_dt_init,
.restart = kirkwood_restart,
diff --git a/arch/arm/mach-kirkwood/board-goflexnet.c b/arch/arm/mach-kirkwood/board-goflexnet.c
new file mode 100644
index 000000000000..413e2c8ef5fe
--- /dev/null
+++ b/arch/arm/mach-kirkwood/board-goflexnet.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012 (C), Jason Cooper <jason@lakedaemon.net>
+ *
+ * arch/arm/mach-kirkwood/board-goflexnet.c
+ *
+ * Seagate GoFlext Net Board Init for drivers not converted to
+ * flattened device tree yet.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Copied and modified for Seagate GoFlex Net support by
+ * Joshua Coombs <josh.coombs@gmail.com> based on ArchLinux ARM's
+ * GoFlex kernel patches.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/ata_platform.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/kirkwood.h>
+#include <mach/bridge-regs.h>
+#include <plat/mvsdio.h>
+#include "common.h"
+#include "mpp.h"
+
+static struct mv643xx_eth_platform_data goflexnet_ge00_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(0),
+};
+
+static unsigned int goflexnet_mpp_config[] __initdata = {
+ MPP29_GPIO, /* USB Power Enable */
+ MPP47_GPIO, /* LED Orange */
+ MPP46_GPIO, /* LED Green */
+ MPP45_GPIO, /* LED Left Capacity 3 */
+ MPP44_GPIO, /* LED Left Capacity 2 */
+ MPP43_GPIO, /* LED Left Capacity 1 */
+ MPP42_GPIO, /* LED Left Capacity 0 */
+ MPP41_GPIO, /* LED Right Capacity 3 */
+ MPP40_GPIO, /* LED Right Capacity 2 */
+ MPP39_GPIO, /* LED Right Capacity 1 */
+ MPP38_GPIO, /* LED Right Capacity 0 */
+ 0
+};
+
+void __init goflexnet_init(void)
+{
+ /*
+ * Basic setup. Needs to be called early.
+ */
+ kirkwood_mpp_conf(goflexnet_mpp_config);
+
+ if (gpio_request(29, "USB Power Enable") != 0 ||
+ gpio_direction_output(29, 1) != 0)
+ pr_err("can't setup GPIO 29 (USB Power Enable)\n");
+ kirkwood_ehci_init();
+
+ kirkwood_ge00_init(&goflexnet_ge00_data);
+}
diff --git a/arch/arm/mach-kirkwood/board-ib62x0.c b/arch/arm/mach-kirkwood/board-ib62x0.c
index eddf1df8891f..cfc47f80e734 100644
--- a/arch/arm/mach-kirkwood/board-ib62x0.c
+++ b/arch/arm/mach-kirkwood/board-ib62x0.c
@@ -18,9 +18,7 @@
#include <linux/ata_platform.h>
#include <linux/mv643xx_eth.h>
#include <linux/gpio.h>
-#include <linux/gpio_keys.h>
#include <linux/input.h>
-#include <linux/leds.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <mach/kirkwood.h>
@@ -33,10 +31,6 @@ static struct mv643xx_eth_platform_data ib62x0_ge00_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(8),
};
-static struct mv_sata_platform_data ib62x0_sata_data = {
- .n_ports = 2,
-};
-
static unsigned int ib62x0_mpp_config[] __initdata = {
MPP0_NF_IO2,
MPP1_NF_IO3,
@@ -55,69 +49,6 @@ static unsigned int ib62x0_mpp_config[] __initdata = {
0
};
-static struct gpio_led ib62x0_led_pins[] = {
- {
- .name = "ib62x0:green:os",
- .default_trigger = "default-on",
- .gpio = 25,
- .active_low = 0,
- },
- {
- .name = "ib62x0:red:os",
- .default_trigger = "none",
- .gpio = 22,
- .active_low = 0,
- },
- {
- .name = "ib62x0:red:usb_copy",
- .default_trigger = "none",
- .gpio = 27,
- .active_low = 0,
- },
-};
-
-static struct gpio_led_platform_data ib62x0_led_data = {
- .leds = ib62x0_led_pins,
- .num_leds = ARRAY_SIZE(ib62x0_led_pins),
-};
-
-static struct platform_device ib62x0_led_device = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &ib62x0_led_data,
- }
-};
-
-static struct gpio_keys_button ib62x0_button_pins[] = {
- {
- .code = KEY_COPY,
- .gpio = 29,
- .desc = "USB Copy",
- .active_low = 1,
- },
- {
- .code = KEY_RESTART,
- .gpio = 28,
- .desc = "Reset",
- .active_low = 1,
- },
-};
-
-static struct gpio_keys_platform_data ib62x0_button_data = {
- .buttons = ib62x0_button_pins,
- .nbuttons = ARRAY_SIZE(ib62x0_button_pins),
-};
-
-static struct platform_device ib62x0_button_device = {
- .name = "gpio-keys",
- .id = -1,
- .num_resources = 0,
- .dev = {
- .platform_data = &ib62x0_button_data,
- }
-};
-
static void ib62x0_power_off(void)
{
gpio_set_value(IB62X0_GPIO_POWER_OFF, 1);
@@ -132,9 +63,6 @@ void __init ib62x0_init(void)
kirkwood_ehci_init();
kirkwood_ge00_init(&ib62x0_ge00_data);
- kirkwood_sata_init(&ib62x0_sata_data);
- platform_device_register(&ib62x0_led_device);
- platform_device_register(&ib62x0_button_device);
if (gpio_request(IB62X0_GPIO_POWER_OFF, "ib62x0:power:off") == 0 &&
gpio_direction_output(IB62X0_GPIO_POWER_OFF, 0) == 0)
pm_power_off = ib62x0_power_off;
diff --git a/arch/arm/mach-kirkwood/board-iconnect.c b/arch/arm/mach-kirkwood/board-iconnect.c
index b0d3cc49269d..d7a9198ed300 100644
--- a/arch/arm/mach-kirkwood/board-iconnect.c
+++ b/arch/arm/mach-kirkwood/board-iconnect.c
@@ -19,8 +19,6 @@
#include <linux/mtd/partitions.h>
#include <linux/mv643xx_eth.h>
#include <linux/gpio.h>
-#include <linux/leds.h>
-#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <asm/mach/arch.h>
@@ -32,50 +30,6 @@ static struct mv643xx_eth_platform_data iconnect_ge00_data = {
.phy_addr = MV643XX_ETH_PHY_ADDR(11),
};
-static struct gpio_led iconnect_led_pins[] = {
- {
- .name = "led_level",
- .gpio = 41,
- .default_trigger = "default-on",
- }, {
- .name = "power:blue",
- .gpio = 42,
- .default_trigger = "timer",
- }, {
- .name = "power:red",
- .gpio = 43,
- }, {
- .name = "usb1:blue",
- .gpio = 44,
- }, {
- .name = "usb2:blue",
- .gpio = 45,
- }, {
- .name = "usb3:blue",
- .gpio = 46,
- }, {
- .name = "usb4:blue",
- .gpio = 47,
- }, {
- .name = "otb:blue",
- .gpio = 48,
- },
-};
-
-static struct gpio_led_platform_data iconnect_led_data = {
- .leds = iconnect_led_pins,
- .num_leds = ARRAY_SIZE(iconnect_led_pins),
- .gpio_blink_set = orion_gpio_led_blink_set,
-};
-
-static struct platform_device iconnect_leds = {
- .name = "leds-gpio",
- .id = -1,
- .dev = {
- .platform_data = &iconnect_led_data,
- }
-};
-
static unsigned int iconnect_mpp_config[] __initdata = {
MPP12_GPIO,
MPP35_GPIO,
@@ -90,12 +44,6 @@ static unsigned int iconnect_mpp_config[] __initdata = {
0
};
-static struct i2c_board_info __initdata iconnect_board_info[] = {
- {
- I2C_BOARD_INFO("lm63", 0x4c),
- },
-};
-
static struct mtd_partition iconnect_nand_parts[] = {
{
.name = "flash",
@@ -142,15 +90,11 @@ void __init iconnect_init(void)
{
kirkwood_mpp_conf(iconnect_mpp_config);
kirkwood_nand_init(ARRAY_AND_SIZE(iconnect_nand_parts), 25);
- kirkwood_i2c_init();
- i2c_register_board_info(0, iconnect_board_info,
- ARRAY_SIZE(iconnect_board_info));
kirkwood_ehci_init();
kirkwood_ge00_init(&iconnect_ge00_data);
platform_device_register(&iconnect_button_device);
- platform_device_register(&iconnect_leds);
}
static int __init iconnect_pci_init(void)
diff --git a/arch/arm/mach-kirkwood/board-lsxl.c b/arch/arm/mach-kirkwood/board-lsxl.c
new file mode 100644
index 000000000000..83d8975592f8
--- /dev/null
+++ b/arch/arm/mach-kirkwood/board-lsxl.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2012 (C), Michael Walle <michael@walle.cc>
+ *
+ * arch/arm/mach-kirkwood/board-lsxl.c
+ *
+ * Buffalo Linkstation LS-XHL and LS-CHLv2 init for drivers not
+ * converted to flattened device tree yet.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+#include <linux/ata_platform.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/gpio.h>
+#include <linux/gpio-fan.h>
+#include <linux/input.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/kirkwood.h>
+#include "common.h"
+#include "mpp.h"
+
+static struct mv643xx_eth_platform_data lsxl_ge00_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(0),
+};
+
+static struct mv643xx_eth_platform_data lsxl_ge01_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+static unsigned int lsxl_mpp_config[] __initdata = {
+ MPP10_GPO, /* HDD Power Enable */
+ MPP11_GPIO, /* USB Vbus Enable */
+ MPP18_GPO, /* FAN High Enable# */
+ MPP19_GPO, /* FAN Low Enable# */
+ MPP36_GPIO, /* Function Blue LED */
+ MPP37_GPIO, /* Alarm LED */
+ MPP38_GPIO, /* Info LED */
+ MPP39_GPIO, /* Power LED */
+ MPP40_GPIO, /* Fan Lock */
+ MPP41_GPIO, /* Function Button */
+ MPP42_GPIO, /* Power Switch */
+ MPP43_GPIO, /* Power Auto Switch */
+ MPP48_GPIO, /* Function Red LED */
+ 0
+};
+
+#define LSXL_GPIO_FAN_HIGH 18
+#define LSXL_GPIO_FAN_LOW 19
+#define LSXL_GPIO_FAN_LOCK 40
+
+static struct gpio_fan_alarm lsxl_alarm = {
+ .gpio = LSXL_GPIO_FAN_LOCK,
+};
+
+static struct gpio_fan_speed lsxl_speeds[] = {
+ {
+ .rpm = 0,
+ .ctrl_val = 3,
+ }, {
+ .rpm = 1500,
+ .ctrl_val = 1,
+ }, {
+ .rpm = 3250,
+ .ctrl_val = 2,
+ }, {
+ .rpm = 5000,
+ .ctrl_val = 0,
+ }
+};
+
+static int lsxl_gpio_list[] = {
+ LSXL_GPIO_FAN_HIGH, LSXL_GPIO_FAN_LOW,
+};
+
+static struct gpio_fan_platform_data lsxl_fan_data = {
+ .num_ctrl = ARRAY_SIZE(lsxl_gpio_list),
+ .ctrl = lsxl_gpio_list,
+ .alarm = &lsxl_alarm,
+ .num_speed = ARRAY_SIZE(lsxl_speeds),
+ .speed = lsxl_speeds,
+};
+
+static struct platform_device lsxl_fan_device = {
+ .name = "gpio-fan",
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .platform_data = &lsxl_fan_data,
+ },
+};
+
+/*
+ * On the LS-XHL/LS-CHLv2, the shutdown process is following:
+ * - Userland monitors key events until the power switch goes to off position
+ * - The board reboots
+ * - U-boot starts and goes into an idle mode waiting for the user
+ * to move the switch to ON position
+ *
+ */
+static void lsxl_power_off(void)
+{
+ kirkwood_restart('h', NULL);
+}
+
+#define LSXL_GPIO_HDD_POWER 10
+#define LSXL_GPIO_USB_POWER 11
+
+void __init lsxl_init(void)
+{
+ /*
+ * Basic setup. Needs to be called early.
+ */
+ kirkwood_mpp_conf(lsxl_mpp_config);
+
+ /* usb and sata power on */
+ gpio_set_value(LSXL_GPIO_USB_POWER, 1);
+ gpio_set_value(LSXL_GPIO_HDD_POWER, 1);
+
+ kirkwood_ehci_init();
+ kirkwood_ge00_init(&lsxl_ge00_data);
+ kirkwood_ge01_init(&lsxl_ge01_data);
+ platform_device_register(&lsxl_fan_device);
+
+ /* register power-off method */
+ pm_power_off = lsxl_power_off;
+}
diff --git a/arch/arm/mach-kirkwood/board-ts219.c b/arch/arm/mach-kirkwood/board-ts219.c
new file mode 100644
index 000000000000..1750e68506c1
--- /dev/null
+++ b/arch/arm/mach-kirkwood/board-ts219.c
@@ -0,0 +1,82 @@
+/*
+ *
+ * QNAP TS-11x/TS-21x Turbo NAS Board Setup via DT
+ *
+ * Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
+ *
+ * Based on the board file ts219-setup.c:
+ *
+ * Copyright (C) 2009 Martin Michlmayr <tbm@cyrius.com>
+ * Copyright (C) 2008 Byron Bradley <byron.bbradley@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/ata_platform.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/kirkwood.h>
+#include "common.h"
+#include "mpp.h"
+#include "tsx1x-common.h"
+
+static struct mv643xx_eth_platform_data qnap_ts219_ge00_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+static unsigned int qnap_ts219_mpp_config[] __initdata = {
+ MPP0_SPI_SCn,
+ MPP1_SPI_MOSI,
+ MPP2_SPI_SCK,
+ MPP3_SPI_MISO,
+ MPP4_SATA1_ACTn,
+ MPP5_SATA0_ACTn,
+ MPP8_TW0_SDA,
+ MPP9_TW0_SCK,
+ MPP10_UART0_TXD,
+ MPP11_UART0_RXD,
+ MPP13_UART1_TXD, /* PIC controller */
+ MPP14_UART1_RXD, /* PIC controller */
+ MPP15_GPIO, /* USB Copy button (on devices with 88F6281) */
+ MPP16_GPIO, /* Reset button (on devices with 88F6281) */
+ MPP36_GPIO, /* RAM: 0: 256 MB, 1: 512 MB */
+ MPP37_GPIO, /* Reset button (on devices with 88F6282) */
+ MPP43_GPIO, /* USB Copy button (on devices with 88F6282) */
+ MPP44_GPIO, /* Board ID: 0: TS-11x, 1: TS-21x */
+ 0
+};
+
+void __init qnap_dt_ts219_init(void)
+{
+ u32 dev, rev;
+
+ kirkwood_mpp_conf(qnap_ts219_mpp_config);
+
+ kirkwood_pcie_id(&dev, &rev);
+ if (dev == MV88F6282_DEV_ID)
+ qnap_ts219_ge00_data.phy_addr = MV643XX_ETH_PHY_ADDR(0);
+
+ kirkwood_ge00_init(&qnap_ts219_ge00_data);
+ kirkwood_ehci_init();
+
+ pm_power_off = qnap_tsx1x_power_off;
+}
+
+/* FIXME: Will not work with DT. Maybe use MPP40_GPIO? */
+static int __init ts219_pci_init(void)
+{
+ if (machine_is_ts219())
+ kirkwood_pcie_init(KW_PCIE0);
+
+ return 0;
+}
+subsys_initcall(ts219_pci_init);
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index c9201539ffbd..1201191d7f1b 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -17,6 +17,7 @@
#include <linux/dma-mapping.h>
#include <linux/clk-provider.h>
#include <linux/spinlock.h>
+#include <linux/mv643xx_i2c.h>
#include <net/dsa.h>
#include <asm/page.h>
#include <asm/timex.h>
@@ -276,6 +277,7 @@ void __init kirkwood_clk_init(void)
orion_clkdev_add("0", "pcie", pex0);
orion_clkdev_add("1", "pcie", pex1);
orion_clkdev_add(NULL, "kirkwood-i2s", audio);
+ orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", runit);
/* Marvell says runit is used by SPI, UART, NAND, TWSI, ...,
* so should never be gated.
@@ -299,7 +301,7 @@ void __init kirkwood_ge00_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge00_init(eth_data,
GE00_PHYS_BASE, IRQ_KIRKWOOD_GE00_SUM,
- IRQ_KIRKWOOD_GE00_ERR);
+ IRQ_KIRKWOOD_GE00_ERR, 1600);
/* The interface forgets the MAC address assigned by u-boot if
the clock is turned off, so claim the clk now. */
clk_prepare_enable(ge0);
@@ -313,7 +315,7 @@ void __init kirkwood_ge01_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge01_init(eth_data,
GE01_PHYS_BASE, IRQ_KIRKWOOD_GE01_SUM,
- IRQ_KIRKWOOD_GE01_ERR);
+ IRQ_KIRKWOOD_GE01_ERR, 1600);
clk_prepare_enable(ge1);
}
@@ -515,6 +517,13 @@ void __init kirkwood_wdt_init(void)
void __init kirkwood_init_early(void)
{
orion_time_set_base(TIMER_VIRT_BASE);
+
+ /*
+ * Some Kirkwood devices allocate their coherent buffers from atomic
+ * context. Increase size of atomic coherent pool to make sure such
+ * the allocations won't fail.
+ */
+ init_dma_coherent_pool_size(SZ_1M);
}
int kirkwood_tclk;
diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h
index 9248fa2c165b..304dd1abfdca 100644
--- a/arch/arm/mach-kirkwood/common.h
+++ b/arch/arm/mach-kirkwood/common.h
@@ -58,6 +58,11 @@ void dreamplug_init(void);
#else
static inline void dreamplug_init(void) {};
#endif
+#ifdef CONFIG_MACH_TS219_DT
+void qnap_dt_ts219_init(void);
+#else
+static inline void qnap_dt_ts219_init(void) {};
+#endif
#ifdef CONFIG_MACH_DLINK_KIRKWOOD_DT
void dnskw_init(void);
@@ -77,6 +82,18 @@ void ib62x0_init(void);
static inline void ib62x0_init(void) {};
#endif
+#ifdef CONFIG_MACH_GOFLEXNET_DT
+void goflexnet_init(void);
+#else
+static inline void goflexnet_init(void) {};
+#endif
+
+#ifdef CONFIG_MACH_LSXL_DT
+void lsxl_init(void);
+#else
+static inline void lsxl_init(void) {};
+#endif
+
/* early init functions not converted to fdt yet */
char *kirkwood_id(void);
void kirkwood_l2_init(void);
diff --git a/arch/arm/mach-kirkwood/db88f6281-bp-setup.c b/arch/arm/mach-kirkwood/db88f6281-bp-setup.c
index d93359379598..be90b7d0e10b 100644
--- a/arch/arm/mach-kirkwood/db88f6281-bp-setup.c
+++ b/arch/arm/mach-kirkwood/db88f6281-bp-setup.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/sizes.h>
#include <linux/platform_device.h>
#include <linux/mtd/partitions.h>
#include <linux/ata_platform.h>
diff --git a/arch/arm/mach-kirkwood/irq.c b/arch/arm/mach-kirkwood/irq.c
index c4c68e5b94f1..720063ffa19d 100644
--- a/arch/arm/mach-kirkwood/irq.c
+++ b/arch/arm/mach-kirkwood/irq.c
@@ -9,20 +9,23 @@
*/
#include <linux/gpio.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/irq.h>
-#include <linux/io.h>
#include <mach/bridge-regs.h>
#include <plat/irq.h>
-#include "common.h"
-static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- BUG_ON(irq < IRQ_KIRKWOOD_GPIO_LOW_0_7);
- BUG_ON(irq > IRQ_KIRKWOOD_GPIO_HIGH_16_23);
+static int __initdata gpio0_irqs[4] = {
+ IRQ_KIRKWOOD_GPIO_LOW_0_7,
+ IRQ_KIRKWOOD_GPIO_LOW_8_15,
+ IRQ_KIRKWOOD_GPIO_LOW_16_23,
+ IRQ_KIRKWOOD_GPIO_LOW_24_31,
+};
- orion_gpio_irq_handler((irq - IRQ_KIRKWOOD_GPIO_LOW_0_7) << 3);
-}
+static int __initdata gpio1_irqs[4] = {
+ IRQ_KIRKWOOD_GPIO_HIGH_0_7,
+ IRQ_KIRKWOOD_GPIO_HIGH_8_15,
+ IRQ_KIRKWOOD_GPIO_HIGH_16_23,
+ 0,
+};
void __init kirkwood_init_irq(void)
{
@@ -32,17 +35,8 @@ void __init kirkwood_init_irq(void)
/*
* Initialize gpiolib for GPIOs 0-49.
*/
- orion_gpio_init(0, 32, GPIO_LOW_VIRT_BASE, 0,
- IRQ_KIRKWOOD_GPIO_START);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_0_7, gpio_irq_handler);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_8_15, gpio_irq_handler);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_16_23, gpio_irq_handler);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_LOW_24_31, gpio_irq_handler);
-
- orion_gpio_init(32, 18, GPIO_HIGH_VIRT_BASE, 0,
- IRQ_KIRKWOOD_GPIO_START + 32);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_0_7, gpio_irq_handler);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_8_15, gpio_irq_handler);
- irq_set_chained_handler(IRQ_KIRKWOOD_GPIO_HIGH_16_23,
- gpio_irq_handler);
+ orion_gpio_init(NULL, 0, 32, (void __iomem *)GPIO_LOW_VIRT_BASE, 0,
+ IRQ_KIRKWOOD_GPIO_START, gpio0_irqs);
+ orion_gpio_init(NULL, 32, 18, (void __iomem *)GPIO_HIGH_VIRT_BASE, 0,
+ IRQ_KIRKWOOD_GPIO_START + 32, gpio1_irqs);
}
diff --git a/arch/arm/mach-mmp/gplugd.c b/arch/arm/mach-mmp/gplugd.c
index f516e74ce0d5..5c3d61ee729a 100644
--- a/arch/arm/mach-mmp/gplugd.c
+++ b/arch/arm/mach-mmp/gplugd.c
@@ -14,6 +14,7 @@
#include <asm/mach/arch.h>
#include <asm/mach-types.h>
+#include <mach/irqs.h>
#include <mach/pxa168.h>
#include <mach/mfp-pxa168.h>
diff --git a/arch/arm/mach-mmp/sram.c b/arch/arm/mach-mmp/sram.c
index 4304f9519372..7e8a5a2e1ec7 100644
--- a/arch/arm/mach-mmp/sram.c
+++ b/arch/arm/mach-mmp/sram.c
@@ -68,7 +68,7 @@ static int __devinit sram_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
- if (!pdata && !pdata->pool_name)
+ if (!pdata || !pdata->pool_name)
return -ENODEV;
info = kzalloc(sizeof(*info), GFP_KERNEL);
diff --git a/arch/arm/mach-mv78xx0/addr-map.c b/arch/arm/mach-mv78xx0/addr-map.c
index 62b53d710efd..a9bc84180d21 100644
--- a/arch/arm/mach-mv78xx0/addr-map.c
+++ b/arch/arm/mach-mv78xx0/addr-map.c
@@ -37,7 +37,7 @@
#define WIN0_OFF(n) (BRIDGE_VIRT_BASE + 0x0000 + ((n) << 4))
#define WIN8_OFF(n) (BRIDGE_VIRT_BASE + 0x0900 + (((n) - 8) << 4))
-static void __init __iomem *win_cfg_base(int win)
+static void __init __iomem *win_cfg_base(const struct orion_addr_map_cfg *cfg, int win)
{
/*
* Find the control register base address for this window.
diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c
index b4c53b846c9c..3057f7d4329a 100644
--- a/arch/arm/mach-mv78xx0/common.c
+++ b/arch/arm/mach-mv78xx0/common.c
@@ -213,7 +213,8 @@ void __init mv78xx0_ge00_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge00_init(eth_data,
GE00_PHYS_BASE, IRQ_MV78XX0_GE00_SUM,
- IRQ_MV78XX0_GE_ERR);
+ IRQ_MV78XX0_GE_ERR,
+ MV643XX_TX_CSUM_DEFAULT_LIMIT);
}
@@ -224,7 +225,8 @@ void __init mv78xx0_ge01_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge01_init(eth_data,
GE01_PHYS_BASE, IRQ_MV78XX0_GE01_SUM,
- NO_IRQ);
+ NO_IRQ,
+ MV643XX_TX_CSUM_DEFAULT_LIMIT);
}
diff --git a/arch/arm/mach-mv78xx0/irq.c b/arch/arm/mach-mv78xx0/irq.c
index e421b701663b..eff9a750bbe2 100644
--- a/arch/arm/mach-mv78xx0/irq.c
+++ b/arch/arm/mach-mv78xx0/irq.c
@@ -9,19 +9,17 @@
*/
#include <linux/gpio.h>
#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/pci.h>
#include <linux/irq.h>
#include <mach/bridge-regs.h>
#include <plat/irq.h>
#include "common.h"
-static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- BUG_ON(irq < IRQ_MV78XX0_GPIO_0_7 || irq > IRQ_MV78XX0_GPIO_24_31);
-
- orion_gpio_irq_handler((irq - IRQ_MV78XX0_GPIO_0_7) << 3);
-}
+static int __initdata gpio0_irqs[4] = {
+ IRQ_MV78XX0_GPIO_0_7,
+ IRQ_MV78XX0_GPIO_8_15,
+ IRQ_MV78XX0_GPIO_16_23,
+ IRQ_MV78XX0_GPIO_24_31,
+};
void __init mv78xx0_init_irq(void)
{
@@ -34,11 +32,7 @@ void __init mv78xx0_init_irq(void)
* registers for core #1 are at an offset of 0x18 from those of
* core #0.)
*/
- orion_gpio_init(0, 32, GPIO_VIRT_BASE,
+ orion_gpio_init(NULL, 0, 32, (void __iomem *)GPIO_VIRT_BASE,
mv78xx0_core_index() ? 0x18 : 0,
- IRQ_MV78XX0_GPIO_START);
- irq_set_chained_handler(IRQ_MV78XX0_GPIO_0_7, gpio_irq_handler);
- irq_set_chained_handler(IRQ_MV78XX0_GPIO_8_15, gpio_irq_handler);
- irq_set_chained_handler(IRQ_MV78XX0_GPIO_16_23, gpio_irq_handler);
- irq_set_chained_handler(IRQ_MV78XX0_GPIO_24_31, gpio_irq_handler);
+ IRQ_MV78XX0_GPIO_START, gpio0_irqs);
}
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index ccdf83b17cf1..9a8bbda195b2 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -2,9 +2,6 @@ if ARCH_MXS
source "arch/arm/mach-mxs/devices/Kconfig"
-config MXS_OCOTP
- bool
-
config SOC_IMX23
bool
select ARM_AMBA
@@ -66,7 +63,6 @@ config MACH_MX28EVK
select MXS_HAVE_PLATFORM_MXS_SAIF
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_RTC_STMP3XXX
- select MXS_OCOTP
help
Include support for MX28EVK platform. This includes specific
configurations for the board and its peripherals.
@@ -94,7 +90,6 @@ config MODULE_M28
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_MXS_MMC
select MXS_HAVE_PLATFORM_MXSFB
- select MXS_OCOTP
config MODULE_APX4
bool
@@ -106,7 +101,6 @@ config MODULE_APX4
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_MXS_MMC
select MXS_HAVE_PLATFORM_MXS_SAIF
- select MXS_OCOTP
config MACH_TX28
bool "Ka-Ro TX28 module"
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index e41590ccb437..fed3695a1339 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -1,7 +1,6 @@
# Common support
-obj-y := devices.o icoll.o iomux.o system.o timer.o mm.o
+obj-y := devices.o icoll.o iomux.o ocotp.o system.o timer.o mm.o
-obj-$(CONFIG_MXS_OCOTP) += ocotp.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_MACH_MXS_DT) += mach-mxs.o
diff --git a/arch/arm/mach-mxs/mach-mxs.c b/arch/arm/mach-mxs/mach-mxs.c
index 8dabfe81d07c..ff886e01a0b0 100644
--- a/arch/arm/mach-mxs/mach-mxs.c
+++ b/arch/arm/mach-mxs/mach-mxs.c
@@ -261,7 +261,7 @@ static void __init apx4devkit_init(void)
enable_clk_enet_out();
if (IS_BUILTIN(CONFIG_PHYLIB))
- phy_register_fixup_for_uid(PHY_ID_KS8051, MICREL_PHY_ID_MASK,
+ phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK,
apx4devkit_phy_fixup);
mxsfb_pdata.mode_list = apx4devkit_video_modes;
diff --git a/arch/arm/mach-omap1/board-h2-mmc.c b/arch/arm/mach-omap1/board-h2-mmc.c
index da0e37d40823..e1362ce48497 100644
--- a/arch/arm/mach-omap1/board-h2-mmc.c
+++ b/arch/arm/mach-omap1/board-h2-mmc.c
@@ -54,7 +54,6 @@ static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 1,
.init = mmc_late_init,
.cleanup = mmc_cleanup,
- .dma_mask = 0xffffffff,
.slots[0] = {
.set_power = mmc_set_power,
.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-h3-mmc.c b/arch/arm/mach-omap1/board-h3-mmc.c
index f8242aa9b763..c74daace8cd6 100644
--- a/arch/arm/mach-omap1/board-h3-mmc.c
+++ b/arch/arm/mach-omap1/board-h3-mmc.c
@@ -36,7 +36,6 @@ static int mmc_set_power(struct device *dev, int slot, int power_on,
*/
static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 1,
- .dma_mask = 0xffffffff,
.slots[0] = {
.set_power = mmc_set_power,
.ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c
index 4007a372481b..2c0ca8fc3380 100644
--- a/arch/arm/mach-omap1/board-nokia770.c
+++ b/arch/arm/mach-omap1/board-nokia770.c
@@ -185,7 +185,6 @@ static int nokia770_mmc_get_cover_state(struct device *dev, int slot)
static struct omap_mmc_platform_data nokia770_mmc2_data = {
.nr_slots = 1,
- .dma_mask = 0xffffffff,
.max_freq = 12000000,
.slots[0] = {
.set_power = nokia770_mmc_set_power,
diff --git a/arch/arm/mach-omap1/board-palmz71.c b/arch/arm/mach-omap1/board-palmz71.c
index cc71a26723ef..355980321c2d 100644
--- a/arch/arm/mach-omap1/board-palmz71.c
+++ b/arch/arm/mach-omap1/board-palmz71.c
@@ -288,8 +288,7 @@ palmz71_gpio_setup(int early)
}
gpio_direction_input(PALMZ71_USBDETECT_GPIO);
if (request_irq(gpio_to_irq(PALMZ71_USBDETECT_GPIO),
- palmz71_powercable, IRQF_SAMPLE_RANDOM,
- "palmz71-cable", NULL))
+ palmz71_powercable, 0, "palmz71-cable", NULL))
printk(KERN_ERR
"IRQ request for power cable failed!\n");
palmz71_powercable(gpio_to_irq(PALMZ71_USBDETECT_GPIO), NULL);
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index dd0fbf76ac79..346fd26f3aa6 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -62,12 +62,14 @@ config ARCH_OMAP4
select PM_OPP if PM
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select ARM_CPU_SUSPEND if PM
+ select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
config SOC_OMAP5
bool "TI OMAP5"
select CPU_V7
select ARM_GIC
select HAVE_SMP
+ select ARM_CPU_SUSPEND if PM
comment "OMAP Core Type"
depends on ARCH_OMAP2
@@ -230,10 +232,11 @@ config MACH_OMAP3_PANDORA
select OMAP_PACKAGE_CBB
select REGULATOR_FIXED_VOLTAGE if REGULATOR
-config MACH_OMAP3_TOUCHBOOK
+config MACH_TOUCHBOOK
bool "OMAP3 Touch Book"
depends on ARCH_OMAP3
default y
+ select OMAP_PACKAGE_CBB
config MACH_OMAP_3430SDP
bool "OMAP 3430 SDP board"
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index f6a24b3f9c4f..34c2c7f59f0a 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -255,7 +255,7 @@ obj-$(CONFIG_MACH_OMAP_3630SDP) += board-zoom-display.o
obj-$(CONFIG_MACH_CM_T35) += board-cm-t35.o
obj-$(CONFIG_MACH_CM_T3517) += board-cm-t3517.o
obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o
-obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o
+obj-$(CONFIG_MACH_TOUCHBOOK) += board-omap3touchbook.o
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o
diff --git a/arch/arm/mach-omap2/board-igep0020.c b/arch/arm/mach-omap2/board-igep0020.c
index 74915295482e..28214483aaba 100644
--- a/arch/arm/mach-omap2/board-igep0020.c
+++ b/arch/arm/mach-omap2/board-igep0020.c
@@ -554,6 +554,8 @@ static const struct usbhs_omap_board_data igep3_usbhs_bdata __initconst = {
#ifdef CONFIG_OMAP_MUX
static struct omap_board_mux board_mux[] __initdata = {
+ /* SMSC9221 LAN Controller ETH IRQ (GPIO_176) */
+ OMAP3_MUX(MCSPI1_CS2, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),
{ .reg_offset = OMAP_MUX_TERMINATOR },
};
#endif
diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c
index 2c5d0ed75285..677357ff61ac 100644
--- a/arch/arm/mach-omap2/board-n8x0.c
+++ b/arch/arm/mach-omap2/board-n8x0.c
@@ -468,7 +468,6 @@ static struct omap_mmc_platform_data mmc1_data = {
.cleanup = n8x0_mmc_cleanup,
.shutdown = n8x0_mmc_shutdown,
.max_freq = 24000000,
- .dma_mask = 0xffffffff,
.slots[0] = {
.wires = 4,
.set_power = n8x0_mmc_set_power,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index ef230a0eb5eb..0d362e9f9cb9 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -58,6 +58,7 @@
#include "hsmmc.h"
#include "common-board-devices.h"
+#define OMAP3_EVM_TS_GPIO 175
#define OMAP3_EVM_EHCI_VBUS 22
#define OMAP3_EVM_EHCI_SELECT 61
diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index df2534de3361..ca07264bd3ae 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -34,6 +34,7 @@
#include <plat/gpmc.h>
#include <plat/onenand.h>
#include <plat/gpmc-smc91x.h>
+#include <plat/omap-pm.h>
#include <mach/board-rx51.h>
@@ -46,6 +47,10 @@
#include <../drivers/staging/iio/light/tsl2563.h>
#include <linux/lis3lv02d.h>
+#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
+#include <media/ir-rx51.h>
+#endif
+
#include "mux.h"
#include "hsmmc.h"
#include "common-board-devices.h"
@@ -1220,6 +1225,30 @@ static void __init rx51_init_tsc2005(void)
gpio_to_irq(RX51_TSC2005_IRQ_GPIO);
}
+#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
+static struct lirc_rx51_platform_data rx51_lirc_data = {
+ .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat,
+ .pwm_timer = 9, /* Use GPT 9 for CIR */
+};
+
+static struct platform_device rx51_lirc_device = {
+ .name = "lirc_rx51",
+ .id = -1,
+ .dev = {
+ .platform_data = &rx51_lirc_data,
+ },
+};
+
+static void __init rx51_init_lirc(void)
+{
+ platform_device_register(&rx51_lirc_device);
+}
+#else
+static void __init rx51_init_lirc(void)
+{
+}
+#endif
+
void __init rx51_peripherals_init(void)
{
rx51_i2c_init();
@@ -1230,6 +1259,7 @@ void __init rx51_peripherals_init(void)
rx51_init_wl1251();
rx51_init_tsc2005();
rx51_init_si4713();
+ rx51_init_lirc();
spi_register_board_info(rx51_peripherals_spi_board_info,
ARRAY_SIZE(rx51_peripherals_spi_board_info));
diff --git a/arch/arm/mach-omap2/clock33xx_data.c b/arch/arm/mach-omap2/clock33xx_data.c
index 25bbcc7ca4dc..ae27de8899a6 100644
--- a/arch/arm/mach-omap2/clock33xx_data.c
+++ b/arch/arm/mach-omap2/clock33xx_data.c
@@ -1036,13 +1036,13 @@ static struct omap_clk am33xx_clks[] = {
CLK(NULL, "mmu_fck", &mmu_fck, CK_AM33XX),
CLK(NULL, "smartreflex0_fck", &smartreflex0_fck, CK_AM33XX),
CLK(NULL, "smartreflex1_fck", &smartreflex1_fck, CK_AM33XX),
- CLK(NULL, "gpt1_fck", &timer1_fck, CK_AM33XX),
- CLK(NULL, "gpt2_fck", &timer2_fck, CK_AM33XX),
- CLK(NULL, "gpt3_fck", &timer3_fck, CK_AM33XX),
- CLK(NULL, "gpt4_fck", &timer4_fck, CK_AM33XX),
- CLK(NULL, "gpt5_fck", &timer5_fck, CK_AM33XX),
- CLK(NULL, "gpt6_fck", &timer6_fck, CK_AM33XX),
- CLK(NULL, "gpt7_fck", &timer7_fck, CK_AM33XX),
+ CLK(NULL, "timer1_fck", &timer1_fck, CK_AM33XX),
+ CLK(NULL, "timer2_fck", &timer2_fck, CK_AM33XX),
+ CLK(NULL, "timer3_fck", &timer3_fck, CK_AM33XX),
+ CLK(NULL, "timer4_fck", &timer4_fck, CK_AM33XX),
+ CLK(NULL, "timer5_fck", &timer5_fck, CK_AM33XX),
+ CLK(NULL, "timer6_fck", &timer6_fck, CK_AM33XX),
+ CLK(NULL, "timer7_fck", &timer7_fck, CK_AM33XX),
CLK(NULL, "usbotg_fck", &usbotg_fck, CK_AM33XX),
CLK(NULL, "ieee5000_fck", &ieee5000_fck, CK_AM33XX),
CLK(NULL, "wdt1_fck", &wdt1_fck, CK_AM33XX),
diff --git a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
index a0d68dbecfa3..f99e65cfb862 100644
--- a/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
+++ b/arch/arm/mach-omap2/clockdomain2xxx_3xxx.c
@@ -241,6 +241,52 @@ static void omap3_clkdm_deny_idle(struct clockdomain *clkdm)
_clkdm_del_autodeps(clkdm);
}
+static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
+{
+ bool hwsup = false;
+
+ if (!clkdm->clktrctrl_mask)
+ return 0;
+
+ hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+ clkdm->clktrctrl_mask);
+
+ if (hwsup) {
+ /* Disable HW transitions when we are changing deps */
+ _disable_hwsup(clkdm);
+ _clkdm_add_autodeps(clkdm);
+ _enable_hwsup(clkdm);
+ } else {
+ if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
+ omap3_clkdm_wakeup(clkdm);
+ }
+
+ return 0;
+}
+
+static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
+{
+ bool hwsup = false;
+
+ if (!clkdm->clktrctrl_mask)
+ return 0;
+
+ hwsup = omap2_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
+ clkdm->clktrctrl_mask);
+
+ if (hwsup) {
+ /* Disable HW transitions when we are changing deps */
+ _disable_hwsup(clkdm);
+ _clkdm_del_autodeps(clkdm);
+ _enable_hwsup(clkdm);
+ } else {
+ if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
+ omap3_clkdm_sleep(clkdm);
+ }
+
+ return 0;
+}
+
struct clkdm_ops omap2_clkdm_operations = {
.clkdm_add_wkdep = omap2_clkdm_add_wkdep,
.clkdm_del_wkdep = omap2_clkdm_del_wkdep,
@@ -267,6 +313,6 @@ struct clkdm_ops omap3_clkdm_operations = {
.clkdm_wakeup = omap3_clkdm_wakeup,
.clkdm_allow_idle = omap3_clkdm_allow_idle,
.clkdm_deny_idle = omap3_clkdm_deny_idle,
- .clkdm_clk_enable = omap2_clkdm_clk_enable,
- .clkdm_clk_disable = omap2_clkdm_clk_disable,
+ .clkdm_clk_enable = omap3xxx_clkdm_clk_enable,
+ .clkdm_clk_disable = omap3xxx_clkdm_clk_disable,
};
diff --git a/arch/arm/mach-omap2/cm-regbits-34xx.h b/arch/arm/mach-omap2/cm-regbits-34xx.h
index 766338fe4d34..975f6bda0e0b 100644
--- a/arch/arm/mach-omap2/cm-regbits-34xx.h
+++ b/arch/arm/mach-omap2/cm-regbits-34xx.h
@@ -67,6 +67,7 @@
#define OMAP3430_EN_IVA2_DPLL_MASK (0x7 << 0)
/* CM_IDLEST_IVA2 */
+#define OMAP3430_ST_IVA2_SHIFT 0
#define OMAP3430_ST_IVA2_MASK (1 << 0)
/* CM_IDLEST_PLL_IVA2 */
diff --git a/arch/arm/mach-omap2/common-board-devices.c b/arch/arm/mach-omap2/common-board-devices.c
index 14734746457c..c1875862679f 100644
--- a/arch/arm/mach-omap2/common-board-devices.c
+++ b/arch/arm/mach-omap2/common-board-devices.c
@@ -35,16 +35,6 @@ static struct omap2_mcspi_device_config ads7846_mcspi_config = {
.turbo_mode = 0,
};
-/*
- * ADS7846 driver maybe request a gpio according to the value
- * of pdata->get_pendown_state, but we have done this. So set
- * get_pendown_state to avoid twice gpio requesting.
- */
-static int omap3_get_pendown_state(void)
-{
- return !gpio_get_value(OMAP3_EVM_TS_GPIO);
-}
-
static struct ads7846_platform_data ads7846_config = {
.x_max = 0x0fff,
.y_max = 0x0fff,
@@ -55,7 +45,6 @@ static struct ads7846_platform_data ads7846_config = {
.debounce_rep = 1,
.gpio_pendown = -EINVAL,
.keep_vref_on = 1,
- .get_pendown_state = &omap3_get_pendown_state,
};
static struct spi_board_info ads7846_spi_board_info __initdata = {
diff --git a/arch/arm/mach-omap2/common-board-devices.h b/arch/arm/mach-omap2/common-board-devices.h
index 4c4ef6a6166b..a0b4a42836ab 100644
--- a/arch/arm/mach-omap2/common-board-devices.h
+++ b/arch/arm/mach-omap2/common-board-devices.h
@@ -4,7 +4,6 @@
#include "twl-common.h"
#define NAND_BLOCK_SIZE SZ_128K
-#define OMAP3_EVM_TS_GPIO 175
struct mtd_partition;
struct ads7846_platform_data;
diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c
index 02d15bbd4e35..288bee6cbb76 100644
--- a/arch/arm/mach-omap2/cpuidle44xx.c
+++ b/arch/arm/mach-omap2/cpuidle44xx.c
@@ -21,6 +21,7 @@
#include "common.h"
#include "pm.h"
#include "prm.h"
+#include "clockdomain.h"
/* Machine specific information */
struct omap4_idle_statedata {
@@ -47,10 +48,14 @@ static struct omap4_idle_statedata omap4_idle_data[] = {
},
};
-static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
+static struct powerdomain *mpu_pd, *cpu_pd[NR_CPUS];
+static struct clockdomain *cpu_clkdm[NR_CPUS];
+
+static atomic_t abort_barrier;
+static bool cpu_done[NR_CPUS];
/**
- * omap4_enter_idle - Programs OMAP4 to enter the specified state
+ * omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions
* @dev: cpuidle device
* @drv: cpuidle driver
* @index: the index of state to be entered
@@ -59,60 +64,84 @@ static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
* specified low power state selected by the governor.
* Returns the amount of time spent in the low power state.
*/
-static int omap4_enter_idle(struct cpuidle_device *dev,
+static int omap4_enter_idle_simple(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ local_fiq_disable();
+ omap_do_wfi();
+ local_fiq_enable();
+
+ return index;
+}
+
+static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct omap4_idle_statedata *cx = &omap4_idle_data[index];
- u32 cpu1_state;
int cpu_id = smp_processor_id();
local_fiq_disable();
/*
- * CPU0 has to stay ON (i.e in C1) until CPU1 is OFF state.
+ * CPU0 has to wait and stay ON until CPU1 is OFF state.
* This is necessary to honour hardware recommondation
* of triggeing all the possible low power modes once CPU1 is
* out of coherency and in OFF mode.
- * Update dev->last_state so that governor stats reflects right
- * data.
*/
- cpu1_state = pwrdm_read_pwrst(cpu1_pd);
- if (cpu1_state != PWRDM_POWER_OFF) {
- index = drv->safe_state_index;
- cx = &omap4_idle_data[index];
+ if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
+ while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) {
+ cpu_relax();
+
+ /*
+ * CPU1 could have already entered & exited idle
+ * without hitting off because of a wakeup
+ * or a failed attempt to hit off mode. Check for
+ * that here, otherwise we could spin forever
+ * waiting for CPU1 off.
+ */
+ if (cpu_done[1])
+ goto fail;
+
+ }
}
- if (index > 0)
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
/*
* Call idle CPU PM enter notifier chain so that
* VFP and per CPU interrupt context is saved.
*/
- if (cx->cpu_state == PWRDM_POWER_OFF)
- cpu_pm_enter();
-
- pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
- omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
-
- /*
- * Call idle CPU cluster PM enter notifier chain
- * to save GIC and wakeupgen context.
- */
- if ((cx->mpu_state == PWRDM_POWER_RET) &&
- (cx->mpu_logic_state == PWRDM_POWER_OFF))
- cpu_cluster_pm_enter();
+ cpu_pm_enter();
+
+ if (dev->cpu == 0) {
+ pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
+ omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
+
+ /*
+ * Call idle CPU cluster PM enter notifier chain
+ * to save GIC and wakeupgen context.
+ */
+ if ((cx->mpu_state == PWRDM_POWER_RET) &&
+ (cx->mpu_logic_state == PWRDM_POWER_OFF))
+ cpu_cluster_pm_enter();
+ }
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
+ cpu_done[dev->cpu] = true;
+
+ /* Wakeup CPU1 only if it is not offlined */
+ if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
+ clkdm_wakeup(cpu_clkdm[1]);
+ clkdm_allow_idle(cpu_clkdm[1]);
+ }
/*
* Call idle CPU PM exit notifier chain to restore
- * VFP and per CPU IRQ context. Only CPU0 state is
- * considered since CPU1 is managed by CPU hotplug.
+ * VFP and per CPU IRQ context.
*/
- if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
- cpu_pm_exit();
+ cpu_pm_exit();
/*
* Call idle CPU cluster PM exit notifier chain
@@ -121,8 +150,11 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
if (omap4_mpuss_read_prev_context_state())
cpu_cluster_pm_exit();
- if (index > 0)
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
+
+fail:
+ cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
+ cpu_done[dev->cpu] = false;
local_fiq_enable();
@@ -141,7 +173,7 @@ struct cpuidle_driver omap4_idle_driver = {
.exit_latency = 2 + 2,
.target_residency = 5,
.flags = CPUIDLE_FLAG_TIME_VALID,
- .enter = omap4_enter_idle,
+ .enter = omap4_enter_idle_simple,
.name = "C1",
.desc = "MPUSS ON"
},
@@ -149,8 +181,8 @@ struct cpuidle_driver omap4_idle_driver = {
/* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
.exit_latency = 328 + 440,
.target_residency = 960,
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .enter = omap4_enter_idle,
+ .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
+ .enter = omap4_enter_idle_coupled,
.name = "C2",
.desc = "MPUSS CSWR",
},
@@ -158,8 +190,8 @@ struct cpuidle_driver omap4_idle_driver = {
/* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
.exit_latency = 460 + 518,
.target_residency = 1100,
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .enter = omap4_enter_idle,
+ .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
+ .enter = omap4_enter_idle_coupled,
.name = "C3",
.desc = "MPUSS OSWR",
},
@@ -168,6 +200,16 @@ struct cpuidle_driver omap4_idle_driver = {
.safe_state_index = 0,
};
+/*
+ * For each cpu, setup the broadcast timer because local timers
+ * stops for the states above C1.
+ */
+static void omap_setup_broadcast_timer(void *arg)
+{
+ int cpu = smp_processor_id();
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
+}
+
/**
* omap4_idle_init - Init routine for OMAP4 idle
*
@@ -180,19 +222,31 @@ int __init omap4_idle_init(void)
unsigned int cpu_id = 0;
mpu_pd = pwrdm_lookup("mpu_pwrdm");
- cpu0_pd = pwrdm_lookup("cpu0_pwrdm");
- cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
- if ((!mpu_pd) || (!cpu0_pd) || (!cpu1_pd))
+ cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
+ cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
+ if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1]))
return -ENODEV;
- dev = &per_cpu(omap4_idle_dev, cpu_id);
- dev->cpu = cpu_id;
-
- cpuidle_register_driver(&omap4_idle_driver);
+ cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm");
+ cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm");
+ if (!cpu_clkdm[0] || !cpu_clkdm[1])
+ return -ENODEV;
- if (cpuidle_register_device(dev)) {
- pr_err("%s: CPUidle register device failed\n", __func__);
- return -EIO;
+ /* Configure the broadcast timer on each cpu */
+ on_each_cpu(omap_setup_broadcast_timer, NULL, 1);
+
+ for_each_cpu(cpu_id, cpu_online_mask) {
+ dev = &per_cpu(omap4_idle_dev, cpu_id);
+ dev->cpu = cpu_id;
+#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
+ dev->coupled_cpus = *cpu_online_mask;
+#endif
+ cpuidle_register_driver(&omap4_idle_driver);
+
+ if (cpuidle_register_device(dev)) {
+ pr_err("%s: CPUidle register failed\n", __func__);
+ return -EIO;
+ }
}
return 0;
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 5fb47a14f4ba..af1ed7d24a1f 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -37,6 +37,7 @@
#define DISPC_CONTROL 0x0040
#define DISPC_CONTROL2 0x0238
+#define DISPC_CONTROL3 0x0848
#define DISPC_IRQSTATUS 0x0018
#define DSS_SYSCONFIG 0x10
@@ -52,6 +53,7 @@
#define EVSYNC_EVEN_IRQ_SHIFT 2
#define EVSYNC_ODD_IRQ_SHIFT 3
#define FRAMEDONE2_IRQ_SHIFT 22
+#define FRAMEDONE3_IRQ_SHIFT 30
#define FRAMEDONETV_IRQ_SHIFT 24
/*
@@ -376,7 +378,7 @@ int __init omap_display_init(struct omap_dss_board_info *board_data)
static void dispc_disable_outputs(void)
{
u32 v, irq_mask = 0;
- bool lcd_en, digit_en, lcd2_en = false;
+ bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false;
int i;
struct omap_dss_dispc_dev_attr *da;
struct omap_hwmod *oh;
@@ -405,7 +407,13 @@ static void dispc_disable_outputs(void)
lcd2_en = v & LCD_EN_MASK;
}
- if (!(lcd_en | digit_en | lcd2_en))
+ /* store value of LCDENABLE for LCD3 */
+ if (da->manager_count > 3) {
+ v = omap_hwmod_read(oh, DISPC_CONTROL3);
+ lcd3_en = v & LCD_EN_MASK;
+ }
+
+ if (!(lcd_en | digit_en | lcd2_en | lcd3_en))
return; /* no managers currently enabled */
/*
@@ -426,10 +434,12 @@ static void dispc_disable_outputs(void)
if (lcd2_en)
irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT;
+ if (lcd3_en)
+ irq_mask |= 1 << FRAMEDONE3_IRQ_SHIFT;
/*
* clear any previous FRAMEDONE, FRAMEDONETV,
- * EVSYNC_EVEN/ODD or FRAMEDONE2 interrupts
+ * EVSYNC_EVEN/ODD, FRAMEDONE2 or FRAMEDONE3 interrupts
*/
omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS);
@@ -445,12 +455,19 @@ static void dispc_disable_outputs(void)
omap_hwmod_write(v, oh, DISPC_CONTROL2);
}
+ /* disable LCD3 manager */
+ if (da->manager_count > 3) {
+ v = omap_hwmod_read(oh, DISPC_CONTROL3);
+ v &= ~LCD_EN_MASK;
+ omap_hwmod_write(v, oh, DISPC_CONTROL3);
+ }
+
i = 0;
while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) !=
irq_mask) {
i++;
if (i > FRAMEDONE_IRQ_TIMEOUT) {
- pr_err("didn't get FRAMEDONE1/2 or TV interrupt\n");
+ pr_err("didn't get FRAMEDONE1/2/3 or TV interrupt\n");
break;
}
mdelay(1);
diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index be697d4e0843..a9675d8d1822 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -315,7 +315,6 @@ static int __init omap_hsmmc_pdata_init(struct omap2_hsmmc_info *c,
mmc->slots[0].caps = c->caps;
mmc->slots[0].pm_caps = c->pm_caps;
mmc->slots[0].internal_clock = !c->ext_clock;
- mmc->dma_mask = 0xffffffff;
mmc->max_freq = c->max_freq;
if (cpu_is_omap44xx())
mmc->reg_offset = OMAP4_MMC_REG_OFFSET;
diff --git a/arch/arm/mach-omap2/mux.h b/arch/arm/mach-omap2/mux.h
index 471e62a74a16..76f9b3c2f586 100644
--- a/arch/arm/mach-omap2/mux.h
+++ b/arch/arm/mach-omap2/mux.h
@@ -127,7 +127,6 @@ struct omap_mux_partition {
* @gpio: GPIO number
* @muxnames: available signal modes for a ball
* @balls: available balls on the package
- * @partition: mux partition
*/
struct omap_mux {
u16 reg_offset;
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
index 05fdebfaa195..330d4c6e746b 100644
--- a/arch/arm/mach-omap2/omap-wakeupgen.c
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -46,7 +46,7 @@
static void __iomem *wakeupgen_base;
static void __iomem *sar_base;
static DEFINE_SPINLOCK(wakeupgen_lock);
-static unsigned int irq_target_cpu[NR_IRQS];
+static unsigned int irq_target_cpu[MAX_IRQS];
static unsigned int irq_banks = MAX_NR_REG_BANKS;
static unsigned int max_irqs = MAX_IRQS;
static unsigned int omap_secure_apis;
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 6ca8e519968d..37afbd173c2c 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1889,6 +1889,7 @@ static int _enable(struct omap_hwmod *oh)
_enable_sysc(oh);
}
} else {
+ _omap4_disable_module(oh);
_disable_clocks(oh);
pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
oh->name, r);
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index c9e38200216b..ce7e6068768f 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -100,9 +100,9 @@ static struct omap_hwmod omap3xxx_mpu_hwmod = {
/* IVA2 (IVA2) */
static struct omap_hwmod_rst_info omap3xxx_iva_resets[] = {
- { .name = "logic", .rst_shift = 0 },
- { .name = "seq0", .rst_shift = 1 },
- { .name = "seq1", .rst_shift = 2 },
+ { .name = "logic", .rst_shift = 0, .st_shift = 8 },
+ { .name = "seq0", .rst_shift = 1, .st_shift = 9 },
+ { .name = "seq1", .rst_shift = 2, .st_shift = 10 },
};
static struct omap_hwmod omap3xxx_iva_hwmod = {
@@ -112,6 +112,15 @@ static struct omap_hwmod omap3xxx_iva_hwmod = {
.rst_lines = omap3xxx_iva_resets,
.rst_lines_cnt = ARRAY_SIZE(omap3xxx_iva_resets),
.main_clk = "iva2_ck",
+ .prcm = {
+ .omap2 = {
+ .module_offs = OMAP3430_IVA2_MOD,
+ .prcm_reg_id = 1,
+ .module_bit = OMAP3430_CM_FCLKEN_IVA2_EN_IVA2_SHIFT,
+ .idlest_reg_id = 1,
+ .idlest_idle_bit = OMAP3430_ST_IVA2_SHIFT,
+ }
+ },
};
/* timer class */
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 242aee498ceb..afb60917a948 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -4210,7 +4210,7 @@ static struct omap_hwmod_ocp_if omap44xx_dsp__iva = {
};
/* dsp -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_dsp__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_dsp__sl2if = {
.master = &omap44xx_dsp_hwmod,
.slave = &omap44xx_sl2if_hwmod,
.clk = "dpll_iva_m5x2_ck",
@@ -4828,7 +4828,7 @@ static struct omap_hwmod_ocp_if omap44xx_l3_main_2__iss = {
};
/* iva -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_iva__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_iva__sl2if = {
.master = &omap44xx_iva_hwmod,
.slave = &omap44xx_sl2if_hwmod,
.clk = "dpll_iva_m5x2_ck",
@@ -5362,7 +5362,7 @@ static struct omap_hwmod_ocp_if omap44xx_l4_wkup__scrm = {
};
/* l3_main_2 -> sl2if */
-static struct omap_hwmod_ocp_if omap44xx_l3_main_2__sl2if = {
+static struct omap_hwmod_ocp_if __maybe_unused omap44xx_l3_main_2__sl2if = {
.master = &omap44xx_l3_main_2_hwmod,
.slave = &omap44xx_sl2if_hwmod,
.clk = "l3_div_ck",
@@ -6032,7 +6032,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
&omap44xx_l4_abe__dmic,
&omap44xx_l4_abe__dmic_dma,
&omap44xx_dsp__iva,
- &omap44xx_dsp__sl2if,
+ /* &omap44xx_dsp__sl2if, */
&omap44xx_l4_cfg__dsp,
&omap44xx_l3_main_2__dss,
&omap44xx_l4_per__dss,
@@ -6068,7 +6068,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
&omap44xx_l4_per__i2c4,
&omap44xx_l3_main_2__ipu,
&omap44xx_l3_main_2__iss,
- &omap44xx_iva__sl2if,
+ /* &omap44xx_iva__sl2if, */
&omap44xx_l3_main_2__iva,
&omap44xx_l4_wkup__kbd,
&omap44xx_l4_cfg__mailbox,
@@ -6099,7 +6099,7 @@ static struct omap_hwmod_ocp_if *omap44xx_hwmod_ocp_ifs[] __initdata = {
&omap44xx_l4_cfg__cm_core,
&omap44xx_l4_wkup__prm,
&omap44xx_l4_wkup__scrm,
- &omap44xx_l3_main_2__sl2if,
+ /* &omap44xx_l3_main_2__sl2if, */
&omap44xx_l4_abe__slimbus1,
&omap44xx_l4_abe__slimbus1_dma,
&omap44xx_l4_per__slimbus2,
diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c
index 2293ba27101b..c95415da23c2 100644
--- a/arch/arm/mach-omap2/opp4xxx_data.c
+++ b/arch/arm/mach-omap2/opp4xxx_data.c
@@ -94,7 +94,7 @@ int __init omap4_opp_init(void)
{
int r = -ENODEV;
- if (!cpu_is_omap44xx())
+ if (!cpu_is_omap443x())
return r;
r = omap_init_opp_table(omap44xx_opp_def_list,
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index e4fc88c65dbd..05bd8f02723f 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -272,21 +272,16 @@ void omap_sram_idle(void)
per_next_state = pwrdm_read_next_pwrst(per_pwrdm);
core_next_state = pwrdm_read_next_pwrst(core_pwrdm);
- if (mpu_next_state < PWRDM_POWER_ON) {
- pwrdm_pre_transition(mpu_pwrdm);
- pwrdm_pre_transition(neon_pwrdm);
- }
+ pwrdm_pre_transition(NULL);
/* PER */
if (per_next_state < PWRDM_POWER_ON) {
- pwrdm_pre_transition(per_pwrdm);
per_going_off = (per_next_state == PWRDM_POWER_OFF) ? 1 : 0;
omap2_gpio_prepare_for_idle(per_going_off);
}
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {
- pwrdm_pre_transition(core_pwrdm);
if (core_next_state == PWRDM_POWER_OFF) {
omap3_core_save_context();
omap3_cm_save_context();
@@ -339,20 +334,14 @@ void omap_sram_idle(void)
omap2_prm_clear_mod_reg_bits(OMAP3430_AUTO_OFF_MASK,
OMAP3430_GR_MOD,
OMAP3_PRM_VOLTCTRL_OFFSET);
- pwrdm_post_transition(core_pwrdm);
}
omap3_intc_resume_idle();
+ pwrdm_post_transition(NULL);
+
/* PER */
- if (per_next_state < PWRDM_POWER_ON) {
+ if (per_next_state < PWRDM_POWER_ON)
omap2_gpio_resume_after_idle();
- pwrdm_post_transition(per_pwrdm);
- }
-
- if (mpu_next_state < PWRDM_POWER_ON) {
- pwrdm_post_transition(mpu_pwrdm);
- pwrdm_post_transition(neon_pwrdm);
- }
}
static void omap3_pm_idle(void)
diff --git a/arch/arm/mach-omap2/sleep44xx.S b/arch/arm/mach-omap2/sleep44xx.S
index 9f6b83d1b193..91e71d8f46f0 100644
--- a/arch/arm/mach-omap2/sleep44xx.S
+++ b/arch/arm/mach-omap2/sleep44xx.S
@@ -56,9 +56,13 @@ ppa_por_params:
* The restore function pointer is stored at CPUx_WAKEUP_NS_PA_ADDR_OFFSET.
* It returns to the caller for CPU INACTIVE and ON power states or in case
* CPU failed to transition to targeted OFF/DORMANT state.
+ *
+ * omap4_finish_suspend() calls v7_flush_dcache_all() which doesn't save
+ * stack frame and it expects the caller to take care of it. Hence the entire
+ * stack frame is saved to avoid possible stack corruption.
*/
ENTRY(omap4_finish_suspend)
- stmfd sp!, {lr}
+ stmfd sp!, {r4-r12, lr}
cmp r0, #0x0
beq do_WFI @ No lowpower state, jump to WFI
@@ -226,7 +230,7 @@ scu_gp_clear:
skip_scu_gp_clear:
isb
dsb
- ldmfd sp!, {pc}
+ ldmfd sp!, {r4-r12, pc}
ENDPROC(omap4_finish_suspend)
/*
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 13d20c8a283d..2ba4f57dda86 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -130,6 +130,7 @@ static struct clock_event_device clockevent_gpt = {
.name = "gp_timer",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.shift = 32,
+ .rating = 300,
.set_next_event = omap2_gp_timer_set_next_event,
.set_mode = omap2_gp_timer_set_mode,
};
@@ -223,7 +224,8 @@ static void __init omap2_gp_clockevent_init(int gptimer_id,
clockevent_delta2ns(3, &clockevent_gpt);
/* Timer internal resynch latency. */
- clockevent_gpt.cpumask = cpumask_of(0);
+ clockevent_gpt.cpumask = cpu_possible_mask;
+ clockevent_gpt.irq = omap_dm_timer_get_irq(&clkev);
clockevents_register_device(&clockevent_gpt);
pr_info("OMAP clockevent source: GPTIMER%d at %lu Hz\n",
@@ -258,6 +260,7 @@ static u32 notrace dmtimer_read_sched_clock(void)
return 0;
}
+#ifdef CONFIG_OMAP_32K_TIMER
/* Setup free-running counter for clocksource */
static int __init omap2_sync32k_clocksource_init(void)
{
@@ -297,6 +300,12 @@ static int __init omap2_sync32k_clocksource_init(void)
return ret;
}
+#else
+static inline int omap2_sync32k_clocksource_init(void)
+{
+ return -ENODEV;
+}
+#endif
static void __init omap2_gptimer_clocksource_init(int gptimer_id,
const char *fck_source)
diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c
index de47f170ba50..db5ff6642375 100644
--- a/arch/arm/mach-omap2/twl-common.c
+++ b/arch/arm/mach-omap2/twl-common.c
@@ -67,6 +67,7 @@ void __init omap_pmic_init(int bus, u32 clkrate,
const char *pmic_type, int pmic_irq,
struct twl4030_platform_data *pmic_data)
{
+ omap_mux_init_signal("sys_nirq", OMAP_PIN_INPUT_PULLUP | OMAP_PIN_OFF_WAKEUPENABLE);
strncpy(pmic_i2c_board_info.type, pmic_type,
sizeof(pmic_i2c_board_info.type));
pmic_i2c_board_info.irq = pmic_irq;
diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c
index 9148b229d0de..a6cd14ab1e4e 100644
--- a/arch/arm/mach-orion5x/common.c
+++ b/arch/arm/mach-orion5x/common.c
@@ -109,7 +109,8 @@ void __init orion5x_eth_init(struct mv643xx_eth_platform_data *eth_data)
{
orion_ge00_init(eth_data,
ORION5X_ETH_PHYS_BASE, IRQ_ORION5X_ETH_SUM,
- IRQ_ORION5X_ETH_ERR);
+ IRQ_ORION5X_ETH_ERR,
+ MV643XX_TX_CSUM_DEFAULT_LIMIT);
}
@@ -203,6 +204,13 @@ void __init orion5x_wdt_init(void)
void __init orion5x_init_early(void)
{
orion_time_set_base(TIMER_VIRT_BASE);
+
+ /*
+ * Some Orion5x devices allocate their coherent buffers from atomic
+ * context. Increase size of atomic coherent pool to make sure such
+ * the allocations won't fail.
+ */
+ init_dma_coherent_pool_size(SZ_1M);
}
int orion5x_tclk;
diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c
index b1b45fff776e..17da7091d310 100644
--- a/arch/arm/mach-orion5x/irq.c
+++ b/arch/arm/mach-orion5x/irq.c
@@ -11,19 +11,16 @@
*/
#include <linux/gpio.h>
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/irq.h>
-#include <linux/io.h>
#include <mach/bridge-regs.h>
#include <plat/irq.h>
-#include "common.h"
-static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- BUG_ON(irq < IRQ_ORION5X_GPIO_0_7 || irq > IRQ_ORION5X_GPIO_24_31);
-
- orion_gpio_irq_handler((irq - IRQ_ORION5X_GPIO_0_7) << 3);
-}
+static int __initdata gpio0_irqs[4] = {
+ IRQ_ORION5X_GPIO_0_7,
+ IRQ_ORION5X_GPIO_8_15,
+ IRQ_ORION5X_GPIO_16_23,
+ IRQ_ORION5X_GPIO_24_31,
+};
void __init orion5x_init_irq(void)
{
@@ -32,9 +29,6 @@ void __init orion5x_init_irq(void)
/*
* Initialize gpiolib for GPIOs 0-31.
*/
- orion_gpio_init(0, 32, GPIO_VIRT_BASE, 0, IRQ_ORION5X_GPIO_START);
- irq_set_chained_handler(IRQ_ORION5X_GPIO_0_7, gpio_irq_handler);
- irq_set_chained_handler(IRQ_ORION5X_GPIO_8_15, gpio_irq_handler);
- irq_set_chained_handler(IRQ_ORION5X_GPIO_16_23, gpio_irq_handler);
- irq_set_chained_handler(IRQ_ORION5X_GPIO_24_31, gpio_irq_handler);
+ orion_gpio_init(NULL, 0, 32, (void __iomem *)GPIO_VIRT_BASE, 0,
+ IRQ_ORION5X_GPIO_START, gpio0_irqs);
}
diff --git a/arch/arm/mach-prima2/timer.c b/arch/arm/mach-prima2/timer.c
index 0d024b1e916d..f224107de7bc 100644
--- a/arch/arm/mach-prima2/timer.c
+++ b/arch/arm/mach-prima2/timer.c
@@ -132,11 +132,11 @@ static void sirfsoc_clocksource_resume(struct clocksource *cs)
{
int i;
- for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
+ for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
- writel_relaxed(sirfsoc_timer_reg_val[i - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
- writel_relaxed(sirfsoc_timer_reg_val[i - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
}
static struct clock_event_device sirfsoc_clockevent = {
diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c
index 6bb3f47b1f14..0ca0db787903 100644
--- a/arch/arm/mach-pxa/lubbock.c
+++ b/arch/arm/mach-pxa/lubbock.c
@@ -456,7 +456,7 @@ static int lubbock_mci_init(struct device *dev,
init_timer(&mmc_timer);
mmc_timer.data = (unsigned long) data;
return request_irq(LUBBOCK_SD_IRQ, lubbock_detect_int,
- IRQF_SAMPLE_RANDOM, "lubbock-sd-detect", data);
+ 0, "lubbock-sd-detect", data);
}
static int lubbock_mci_get_ro(struct device *dev)
diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c
index 2db697cd2b4e..39561dcf65f2 100644
--- a/arch/arm/mach-pxa/magician.c
+++ b/arch/arm/mach-pxa/magician.c
@@ -633,9 +633,8 @@ static struct platform_device bq24022 = {
static int magician_mci_init(struct device *dev,
irq_handler_t detect_irq, void *data)
{
- return request_irq(IRQ_MAGICIAN_SD, detect_irq,
- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
- "mmc card detect", data);
+ return request_irq(IRQ_MAGICIAN_SD, detect_irq, IRQF_DISABLED,
+ "mmc card detect", data);
}
static void magician_mci_exit(struct device *dev, void *data)
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index 5905ed130e94..d89d87ae144c 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -953,12 +953,12 @@ static struct i2c_board_info raumfeld_connector_i2c_board_info __initdata = {
static struct eeti_ts_platform_data eeti_ts_pdata = {
.irq_active_high = 1,
+ .irq_gpio = GPIO_TOUCH_IRQ,
};
static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = {
.type = "eeti_ts",
.addr = 0x0a,
- .irq = PXA_GPIO_TO_IRQ(GPIO_TOUCH_IRQ),
.platform_data = &eeti_ts_pdata,
};
diff --git a/arch/arm/mach-pxa/trizeps4.c b/arch/arm/mach-pxa/trizeps4.c
index 2b6ac00b2cd9..166dd32cc1d3 100644
--- a/arch/arm/mach-pxa/trizeps4.c
+++ b/arch/arm/mach-pxa/trizeps4.c
@@ -332,8 +332,8 @@ static int trizeps4_mci_init(struct device *dev, irq_handler_t mci_detect_int,
int err;
err = request_irq(TRIZEPS4_MMC_IRQ, mci_detect_int,
- IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_SAMPLE_RANDOM,
- "MMC card detect", data);
+ IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ "MMC card detect", data);
if (err) {
printk(KERN_ERR "trizeps4_mci_init: MMC/SD: can't request"
"MMC card detect IRQ\n");
diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig
index e24961109b70..d56b0f7f2b20 100644
--- a/arch/arm/mach-s3c24xx/Kconfig
+++ b/arch/arm/mach-s3c24xx/Kconfig
@@ -483,7 +483,7 @@ config MACH_NEO1973_GTA02
select I2C
select POWER_SUPPLY
select MACH_NEO1973
- select S3C2410_PWM
+ select S3C24XX_PWM
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone
@@ -493,7 +493,7 @@ config MACH_RX1950
select S3C24XX_DCLK
select PM_H1940 if PM
select I2C
- select S3C2410_PWM
+ select S3C24XX_PWM
select S3C_DEV_NAND
select S3C2410_IOTIMING if S3C2440_CPUFREQ
select S3C2440_XTAL_16934400
diff --git a/arch/arm/mach-s3c24xx/include/mach/dma.h b/arch/arm/mach-s3c24xx/include/mach/dma.h
index 454831b66037..ee99fd56c043 100644
--- a/arch/arm/mach-s3c24xx/include/mach/dma.h
+++ b/arch/arm/mach-s3c24xx/include/mach/dma.h
@@ -24,7 +24,8 @@
*/
enum dma_ch {
- DMACH_XD0,
+ DMACH_DT_PROP = -1, /* not yet supported, do not use */
+ DMACH_XD0 = 0,
DMACH_XD1,
DMACH_SDI,
DMACH_SPI0,
diff --git a/arch/arm/mach-sa1100/leds-hackkit.c b/arch/arm/mach-sa1100/leds-hackkit.c
index 6a2352436e62..f8e47235babe 100644
--- a/arch/arm/mach-sa1100/leds-hackkit.c
+++ b/arch/arm/mach-sa1100/leds-hackkit.c
@@ -10,6 +10,7 @@
* as cpu led, the green one is used as timer led.
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/leds.h>
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c
index cf10f92856dc..453a6e50db8b 100644
--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
+++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
@@ -520,13 +520,14 @@ static struct platform_device hdmi_lcdc_device = {
};
/* GPIO KEY */
-#define GPIO_KEY(c, g, d) { .code = c, .gpio = g, .desc = d, .active_low = 1 }
+#define GPIO_KEY(c, g, d, ...) \
+ { .code = c, .gpio = g, .desc = d, .active_low = 1, __VA_ARGS__ }
static struct gpio_keys_button gpio_buttons[] = {
- GPIO_KEY(KEY_POWER, GPIO_PORT99, "SW1"),
- GPIO_KEY(KEY_BACK, GPIO_PORT100, "SW2"),
- GPIO_KEY(KEY_MENU, GPIO_PORT97, "SW3"),
- GPIO_KEY(KEY_HOME, GPIO_PORT98, "SW4"),
+ GPIO_KEY(KEY_POWER, GPIO_PORT99, "SW3", .wakeup = 1),
+ GPIO_KEY(KEY_BACK, GPIO_PORT100, "SW4"),
+ GPIO_KEY(KEY_MENU, GPIO_PORT97, "SW5"),
+ GPIO_KEY(KEY_HOME, GPIO_PORT98, "SW6"),
};
static struct gpio_keys_platform_data gpio_key_info = {
@@ -901,8 +902,8 @@ static struct platform_device *eva_devices[] __initdata = {
&camera_device,
&ceu0_device,
&fsi_device,
- &fsi_hdmi_device,
&fsi_wm8978_device,
+ &fsi_hdmi_device,
};
static void __init eva_clock_init(void)
diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c
index 53b7ea92c32c..3b8a0171c3cb 100644
--- a/arch/arm/mach-shmobile/board-kzm9g.c
+++ b/arch/arm/mach-shmobile/board-kzm9g.c
@@ -346,11 +346,11 @@ static struct resource sh_mmcif_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = gic_spi(141),
+ .start = gic_spi(140),
.flags = IORESOURCE_IRQ,
},
[2] = {
- .start = gic_spi(140),
+ .start = gic_spi(141),
.flags = IORESOURCE_IRQ,
},
};
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 7ea2b31e3199..c129542f6aed 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -695,6 +695,7 @@ static struct platform_device usbhs0_device = {
* - J30 "open"
* - modify usbhs1_get_id() USBHS_HOST -> USBHS_GADGET
* - add .get_vbus = usbhs_get_vbus in usbhs1_private
+ * - check usbhs0_device(pio)/usbhs1_device(irq) order in mackerel_devices.
*/
#define IRQ8 evt2irq(0x0300)
#define USB_PHY_MODE (1 << 4)
@@ -1325,8 +1326,8 @@ static struct platform_device *mackerel_devices[] __initdata = {
&nor_flash_device,
&smc911x_device,
&lcdc_device,
- &usbhs1_device,
&usbhs0_device,
+ &usbhs1_device,
&leds_device,
&fsi_device,
&fsi_ak4643_device,
diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c
index 3a528cf4366c..fcf5a47f4772 100644
--- a/arch/arm/mach-shmobile/board-marzen.c
+++ b/arch/arm/mach-shmobile/board-marzen.c
@@ -67,7 +67,7 @@ static struct smsc911x_platform_config smsc911x_platdata = {
static struct platform_device eth_device = {
.name = "smsc911x",
- .id = 0,
+ .id = -1,
.dev = {
.platform_data = &smsc911x_platdata,
},
diff --git a/arch/arm/mach-shmobile/intc-sh73a0.c b/arch/arm/mach-shmobile/intc-sh73a0.c
index ee447404c857..588555a67d9c 100644
--- a/arch/arm/mach-shmobile/intc-sh73a0.c
+++ b/arch/arm/mach-shmobile/intc-sh73a0.c
@@ -259,9 +259,9 @@ static int sh73a0_set_wake(struct irq_data *data, unsigned int on)
return 0; /* always allow wakeup */
}
-#define RELOC_BASE 0x1000
+#define RELOC_BASE 0x1200
-/* INTCA IRQ pins at INTCS + 0x1000 to make space for GIC+INTC handling */
+/* INTCA IRQ pins at INTCS + RELOC_BASE to make space for GIC+INTC handling */
#define INTCS_VECT_RELOC(n, vect) INTCS_VECT((n), (vect) + RELOC_BASE)
INTC_IRQ_PINS_32(intca_irq_pins, 0xe6900000,
diff --git a/arch/arm/mach-spear3xx/spear300.c b/arch/arm/mach-spear3xx/spear300.c
index 0f882ecb7d81..6ec300549960 100644
--- a/arch/arm/mach-spear3xx/spear300.c
+++ b/arch/arm/mach-spear3xx/spear300.c
@@ -120,182 +120,156 @@ struct pl08x_channel_data spear300_dma_info[] = {
.min_signal = 2,
.max_signal = 2,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart0_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "irda",
.min_signal = 12,
.max_signal = 12,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "adc",
.min_signal = 13,
.max_signal = 13,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "to_jpeg",
.min_signal = 14,
.max_signal = 14,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "from_jpeg",
.min_signal = 15,
.max_signal = 15,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras0_rx",
.min_signal = 0,
.max_signal = 0,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras0_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras1_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras1_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras2_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras2_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras3_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras3_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras4_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras4_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_rx",
.min_signal = 12,
.max_signal = 12,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_tx",
.min_signal = 13,
.max_signal = 13,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_rx",
.min_signal = 14,
.max_signal = 14,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_tx",
.min_signal = 15,
.max_signal = 15,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
},
};
diff --git a/arch/arm/mach-spear3xx/spear310.c b/arch/arm/mach-spear3xx/spear310.c
index bbcf4571d361..1d0e435b9045 100644
--- a/arch/arm/mach-spear3xx/spear310.c
+++ b/arch/arm/mach-spear3xx/spear310.c
@@ -205,182 +205,156 @@ struct pl08x_channel_data spear310_dma_info[] = {
.min_signal = 2,
.max_signal = 2,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart0_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "irda",
.min_signal = 12,
.max_signal = 12,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "adc",
.min_signal = 13,
.max_signal = 13,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "to_jpeg",
.min_signal = 14,
.max_signal = 14,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "from_jpeg",
.min_signal = 15,
.max_signal = 15,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart1_rx",
.min_signal = 0,
.max_signal = 0,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart1_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart2_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart2_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart3_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart3_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart4_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart4_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart5_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart5_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_rx",
.min_signal = 12,
.max_signal = 12,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_tx",
.min_signal = 13,
.max_signal = 13,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_rx",
.min_signal = 14,
.max_signal = 14,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_tx",
.min_signal = 15,
.max_signal = 15,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
},
};
diff --git a/arch/arm/mach-spear3xx/spear320.c b/arch/arm/mach-spear3xx/spear320.c
index 88d483bcd66a..fd823c624575 100644
--- a/arch/arm/mach-spear3xx/spear320.c
+++ b/arch/arm/mach-spear3xx/spear320.c
@@ -213,182 +213,156 @@ struct pl08x_channel_data spear320_dma_info[] = {
.min_signal = 2,
.max_signal = 2,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart0_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c0_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c0_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "irda",
.min_signal = 12,
.max_signal = 12,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "adc",
.min_signal = 13,
.max_signal = 13,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "to_jpeg",
.min_signal = 14,
.max_signal = 14,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "from_jpeg",
.min_signal = 15,
.max_signal = 15,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp1_rx",
.min_signal = 0,
.max_signal = 0,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ssp1_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ssp2_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ssp2_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "uart1_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "uart1_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "uart2_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "uart2_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2c1_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2c1_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2c2_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2c2_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2s_rx",
.min_signal = 12,
.max_signal = 12,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "i2s_tx",
.min_signal = 13,
.max_signal = 13,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "rs485_rx",
.min_signal = 14,
.max_signal = 14,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "rs485_tx",
.min_signal = 15,
.max_signal = 15,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
},
};
diff --git a/arch/arm/mach-spear3xx/spear3xx.c b/arch/arm/mach-spear3xx/spear3xx.c
index 66db5f13af84..98144baf8883 100644
--- a/arch/arm/mach-spear3xx/spear3xx.c
+++ b/arch/arm/mach-spear3xx/spear3xx.c
@@ -46,7 +46,8 @@ struct pl022_ssp_controller pl022_plat_data = {
struct pl08x_platform_data pl080_plat_data = {
.memcpy_channel = {
.bus_id = "memcpy",
- .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
+ .cctl_memcpy =
+ (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \
PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \
PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \
diff --git a/arch/arm/mach-spear6xx/spear6xx.c b/arch/arm/mach-spear6xx/spear6xx.c
index 9af67d003c62..5a5a52db252b 100644
--- a/arch/arm/mach-spear6xx/spear6xx.c
+++ b/arch/arm/mach-spear6xx/spear6xx.c
@@ -36,336 +36,288 @@ static struct pl08x_channel_data spear600_dma_info[] = {
.min_signal = 0,
.max_signal = 0,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp1_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart0_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart0_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart1_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "uart1_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp2_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ssp2_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ssp0_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ssp0_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "i2c_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "irda",
.min_signal = 12,
.max_signal = 12,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "adc",
.min_signal = 13,
.max_signal = 13,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "to_jpeg",
.min_signal = 14,
.max_signal = 14,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "from_jpeg",
.min_signal = 15,
.max_signal = 15,
.muxval = 0,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras0_rx",
.min_signal = 0,
.max_signal = 0,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras0_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras1_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras1_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras2_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras2_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras3_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras3_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras4_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras4_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras5_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_rx",
.min_signal = 12,
.max_signal = 12,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras6_tx",
.min_signal = 13,
.max_signal = 13,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_rx",
.min_signal = 14,
.max_signal = 14,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ras7_tx",
.min_signal = 15,
.max_signal = 15,
.muxval = 1,
- .cctl = 0,
.periph_buses = PL08X_AHB1,
}, {
.bus_id = "ext0_rx",
.min_signal = 0,
.max_signal = 0,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext0_tx",
.min_signal = 1,
.max_signal = 1,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext1_rx",
.min_signal = 2,
.max_signal = 2,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext1_tx",
.min_signal = 3,
.max_signal = 3,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext2_rx",
.min_signal = 4,
.max_signal = 4,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext2_tx",
.min_signal = 5,
.max_signal = 5,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext3_rx",
.min_signal = 6,
.max_signal = 6,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext3_tx",
.min_signal = 7,
.max_signal = 7,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext4_rx",
.min_signal = 8,
.max_signal = 8,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext4_tx",
.min_signal = 9,
.max_signal = 9,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext5_rx",
.min_signal = 10,
.max_signal = 10,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext5_tx",
.min_signal = 11,
.max_signal = 11,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext6_rx",
.min_signal = 12,
.max_signal = 12,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext6_tx",
.min_signal = 13,
.max_signal = 13,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext7_rx",
.min_signal = 14,
.max_signal = 14,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
}, {
.bus_id = "ext7_tx",
.min_signal = 15,
.max_signal = 15,
.muxval = 2,
- .cctl = 0,
.periph_buses = PL08X_AHB2,
},
};
@@ -373,7 +325,8 @@ static struct pl08x_channel_data spear600_dma_info[] = {
struct pl08x_platform_data pl080_plat_data = {
.memcpy_channel = {
.bus_id = "memcpy",
- .cctl = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
+ .cctl_memcpy =
+ (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT | \
PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT | \
PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | \
PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | \
diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c
index 8fd387bf31f0..b7344beec102 100644
--- a/arch/arm/mach-tegra/board-harmony-power.c
+++ b/arch/arm/mach-tegra/board-harmony-power.c
@@ -51,7 +51,7 @@ static struct regulator_init_data ldo0_data = {
.consumer_supplies = tps658621_ldo0_supply,
};
-#define HARMONY_REGULATOR_INIT(_id, _name, _supply, _minmv, _maxmv) \
+#define HARMONY_REGULATOR_INIT(_id, _name, _supply, _minmv, _maxmv, _on)\
static struct regulator_init_data _id##_data = { \
.supply_regulator = _supply, \
.constraints = { \
@@ -63,21 +63,22 @@ static struct regulator_init_data ldo0_data = {
.valid_ops_mask = (REGULATOR_CHANGE_MODE | \
REGULATOR_CHANGE_STATUS | \
REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _on, \
}, \
}
-HARMONY_REGULATOR_INIT(sm0, "vdd_sm0", "vdd_sys", 725, 1500);
-HARMONY_REGULATOR_INIT(sm1, "vdd_sm1", "vdd_sys", 725, 1500);
-HARMONY_REGULATOR_INIT(sm2, "vdd_sm2", "vdd_sys", 3000, 4550);
-HARMONY_REGULATOR_INIT(ldo1, "vdd_ldo1", "vdd_sm2", 725, 1500);
-HARMONY_REGULATOR_INIT(ldo2, "vdd_ldo2", "vdd_sm2", 725, 1500);
-HARMONY_REGULATOR_INIT(ldo3, "vdd_ldo3", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo4, "vdd_ldo4", "vdd_sm2", 1700, 2475);
-HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", NULL, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo6, "vdd_ldo6", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo7, "vdd_ldo7", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo8, "vdd_ldo8", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300);
+HARMONY_REGULATOR_INIT(sm0, "vdd_sm0", "vdd_sys", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(sm1, "vdd_sm1", "vdd_sys", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(sm2, "vdd_sm2", "vdd_sys", 3000, 4550, 1);
+HARMONY_REGULATOR_INIT(ldo1, "vdd_ldo1", "vdd_sm2", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(ldo2, "vdd_ldo2", "vdd_sm2", 725, 1500, 0);
+HARMONY_REGULATOR_INIT(ldo3, "vdd_ldo3", "vdd_sm2", 1250, 3300, 1);
+HARMONY_REGULATOR_INIT(ldo4, "vdd_ldo4", "vdd_sm2", 1700, 2475, 1);
+HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", NULL, 1250, 3300, 1);
+HARMONY_REGULATOR_INIT(ldo6, "vdd_ldo6", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo7, "vdd_ldo7", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo8, "vdd_ldo8", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300, 1);
#define TPS_REG(_id, _data) \
{ \
@@ -119,9 +120,10 @@ static struct i2c_board_info __initdata harmony_regulators[] = {
int __init harmony_regulator_init(void)
{
+ regulator_register_always_on(0, "vdd_sys",
+ NULL, 0, 5000000);
+
if (machine_is_harmony()) {
- regulator_register_always_on(0, "vdd_sys",
- NULL, 0, 5000000);
i2c_register_board_info(3, harmony_regulators, 1);
} else { /* Harmony, booted using device tree */
struct device_node *np;
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index c013bbf79cac..53d3d46dec12 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -41,7 +41,6 @@ config MACH_HREFV60
config MACH_SNOWBALL
bool "U8500 Snowball platform"
select MACH_MOP500
- select LEDS_GPIO
help
Include support for the snowball development platform.
diff --git a/arch/arm/mach-ux500/board-mop500-msp.c b/arch/arm/mach-ux500/board-mop500-msp.c
index 996048038743..df15646036aa 100644
--- a/arch/arm/mach-ux500/board-mop500-msp.c
+++ b/arch/arm/mach-ux500/board-mop500-msp.c
@@ -191,9 +191,9 @@ static struct platform_device *db8500_add_msp_i2s(struct device *parent,
return pdev;
}
-/* Platform device for ASoC U8500 machine */
-static struct platform_device snd_soc_u8500 = {
- .name = "snd-soc-u8500",
+/* Platform device for ASoC MOP500 machine */
+static struct platform_device snd_soc_mop500 = {
+ .name = "snd-soc-mop500",
.id = 0,
.dev = {
.platform_data = NULL,
@@ -227,8 +227,8 @@ int mop500_msp_init(struct device *parent)
{
struct platform_device *msp1;
- pr_info("%s: Register platform-device 'snd-soc-u8500'.\n", __func__);
- platform_device_register(&snd_soc_u8500);
+ pr_info("%s: Register platform-device 'snd-soc-mop500'.\n", __func__);
+ platform_device_register(&snd_soc_mop500);
pr_info("Initialize MSP I2S-devices.\n");
db8500_add_msp_i2s(parent, 0, U8500_MSP0_BASE, IRQ_DB8500_MSP0,
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 8674a890fd1c..a534d8880de1 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -797,6 +797,7 @@ static void __init u8500_init_machine(void)
ARRAY_SIZE(mop500_platform_devs));
mop500_sdi_init(parent);
+ mop500_msp_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
i2c_register_board_info(2, mop500_i2c2_devices,
@@ -804,6 +805,8 @@ static void __init u8500_init_machine(void)
mop500_uib_init();
+ } else if (of_machine_is_compatible("calaosystems,snowball-a9500")) {
+ mop500_msp_init(parent);
} else if (of_machine_is_compatible("st-ericsson,hrefv60+")) {
/*
* The HREFv60 board removed a GPIO expander and routed
@@ -815,6 +818,7 @@ static void __init u8500_init_machine(void)
ARRAY_SIZE(mop500_platform_devs));
hrefv60_sdi_init(parent);
+ mop500_msp_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index 119bc52ab93e..4e07eec1270d 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -63,10 +63,11 @@ static int contextidr_notifier(struct notifier_block *unused, unsigned long cmd,
pid = task_pid_nr(thread->task) << ASID_BITS;
asm volatile(
" mrc p15, 0, %0, c13, c0, 1\n"
- " bfi %1, %0, #0, %2\n"
- " mcr p15, 0, %1, c13, c0, 1\n"
+ " and %0, %0, %2\n"
+ " orr %0, %0, %1\n"
+ " mcr p15, 0, %0, c13, c0, 1\n"
: "=r" (contextidr), "+r" (pid)
- : "I" (ASID_BITS));
+ : "I" (~ASID_MASK));
isb();
return NOTIFY_OK;
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index c2cdf6500f75..13f555d62491 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -267,17 +267,19 @@ static void __dma_free_remap(void *cpu_addr, size_t size)
vunmap(cpu_addr);
}
+#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
+
struct dma_pool {
size_t size;
spinlock_t lock;
unsigned long *bitmap;
unsigned long nr_pages;
void *vaddr;
- struct page *page;
+ struct page **pages;
};
static struct dma_pool atomic_pool = {
- .size = SZ_256K,
+ .size = DEFAULT_DMA_COHERENT_POOL_SIZE,
};
static int __init early_coherent_pool(char *p)
@@ -287,6 +289,21 @@ static int __init early_coherent_pool(char *p)
}
early_param("coherent_pool", early_coherent_pool);
+void __init init_dma_coherent_pool_size(unsigned long size)
+{
+ /*
+ * Catch any attempt to set the pool size too late.
+ */
+ BUG_ON(atomic_pool.vaddr);
+
+ /*
+ * Set architecture specific coherent pool size only if
+ * it has not been changed by kernel command line parameter.
+ */
+ if (atomic_pool.size == DEFAULT_DMA_COHERENT_POOL_SIZE)
+ atomic_pool.size = size;
+}
+
/*
* Initialise the coherent pool for atomic allocations.
*/
@@ -297,6 +314,7 @@ static int __init atomic_pool_init(void)
unsigned long nr_pages = pool->size >> PAGE_SHIFT;
unsigned long *bitmap;
struct page *page;
+ struct page **pages;
void *ptr;
int bitmap_size = BITS_TO_LONGS(nr_pages) * sizeof(long);
@@ -304,21 +322,33 @@ static int __init atomic_pool_init(void)
if (!bitmap)
goto no_bitmap;
+ pages = kzalloc(nr_pages * sizeof(struct page *), GFP_KERNEL);
+ if (!pages)
+ goto no_pages;
+
if (IS_ENABLED(CONFIG_CMA))
ptr = __alloc_from_contiguous(NULL, pool->size, prot, &page);
else
ptr = __alloc_remap_buffer(NULL, pool->size, GFP_KERNEL, prot,
&page, NULL);
if (ptr) {
+ int i;
+
+ for (i = 0; i < nr_pages; i++)
+ pages[i] = page + i;
+
spin_lock_init(&pool->lock);
pool->vaddr = ptr;
- pool->page = page;
+ pool->pages = pages;
pool->bitmap = bitmap;
pool->nr_pages = nr_pages;
pr_info("DMA: preallocated %u KiB pool for atomic coherent allocations\n",
(unsigned)pool->size / 1024);
return 0;
}
+
+ kfree(pages);
+no_pages:
kfree(bitmap);
no_bitmap:
pr_err("DMA: failed to allocate %u KiB pool for atomic coherent allocation\n",
@@ -358,7 +388,7 @@ void __init dma_contiguous_remap(void)
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
if (start >= end)
- return;
+ continue;
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
@@ -423,7 +453,7 @@ static void *__alloc_from_pool(size_t size, struct page **ret_page)
unsigned int pageno;
unsigned long flags;
void *ptr = NULL;
- size_t align;
+ unsigned long align_mask;
if (!pool->vaddr) {
WARN(1, "coherent pool not initialised!\n");
@@ -435,35 +465,53 @@ static void *__alloc_from_pool(size_t size, struct page **ret_page)
* small, so align them to their order in pages, minimum is a page
* size. This helps reduce fragmentation of the DMA space.
*/
- align = PAGE_SIZE << get_order(size);
+ align_mask = (1 << get_order(size)) - 1;
spin_lock_irqsave(&pool->lock, flags);
pageno = bitmap_find_next_zero_area(pool->bitmap, pool->nr_pages,
- 0, count, (1 << align) - 1);
+ 0, count, align_mask);
if (pageno < pool->nr_pages) {
bitmap_set(pool->bitmap, pageno, count);
ptr = pool->vaddr + PAGE_SIZE * pageno;
- *ret_page = pool->page + pageno;
+ *ret_page = pool->pages[pageno];
+ } else {
+ pr_err_once("ERROR: %u KiB atomic DMA coherent pool is too small!\n"
+ "Please increase it with coherent_pool= kernel parameter!\n",
+ (unsigned)pool->size / 1024);
}
spin_unlock_irqrestore(&pool->lock, flags);
return ptr;
}
+static bool __in_atomic_pool(void *start, size_t size)
+{
+ struct dma_pool *pool = &atomic_pool;
+ void *end = start + size;
+ void *pool_start = pool->vaddr;
+ void *pool_end = pool->vaddr + pool->size;
+
+ if (start < pool_start || start >= pool_end)
+ return false;
+
+ if (end <= pool_end)
+ return true;
+
+ WARN(1, "Wrong coherent size(%p-%p) from atomic pool(%p-%p)\n",
+ start, end - 1, pool_start, pool_end - 1);
+
+ return false;
+}
+
static int __free_from_pool(void *start, size_t size)
{
struct dma_pool *pool = &atomic_pool;
unsigned long pageno, count;
unsigned long flags;
- if (start < pool->vaddr || start > pool->vaddr + pool->size)
+ if (!__in_atomic_pool(start, size))
return 0;
- if (start + size > pool->vaddr + pool->size) {
- WARN(1, "freeing wrong coherent size from pool\n");
- return 0;
- }
-
pageno = (start - pool->vaddr) >> PAGE_SHIFT;
count = size >> PAGE_SHIFT;
@@ -648,12 +696,12 @@ void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
if (arch_is_coherent() || nommu()) {
__dma_free_buffer(page, size);
+ } else if (__free_from_pool(cpu_addr, size)) {
+ return;
} else if (!IS_ENABLED(CONFIG_CMA)) {
__dma_free_remap(cpu_addr, size);
__dma_free_buffer(page, size);
} else {
- if (__free_from_pool(cpu_addr, size))
- return;
/*
* Non-atomic allocations cannot be freed with IRQs disabled
*/
@@ -1090,10 +1138,22 @@ static int __iommu_remove_mapping(struct device *dev, dma_addr_t iova, size_t si
return 0;
}
+static struct page **__atomic_get_pages(void *addr)
+{
+ struct dma_pool *pool = &atomic_pool;
+ struct page **pages = pool->pages;
+ int offs = (addr - pool->vaddr) >> PAGE_SHIFT;
+
+ return pages + offs;
+}
+
static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
{
struct vm_struct *area;
+ if (__in_atomic_pool(cpu_addr, PAGE_SIZE))
+ return __atomic_get_pages(cpu_addr);
+
if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
return cpu_addr;
@@ -1103,6 +1163,34 @@ static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
return NULL;
}
+static void *__iommu_alloc_atomic(struct device *dev, size_t size,
+ dma_addr_t *handle)
+{
+ struct page *page;
+ void *addr;
+
+ addr = __alloc_from_pool(size, &page);
+ if (!addr)
+ return NULL;
+
+ *handle = __iommu_create_mapping(dev, &page, size);
+ if (*handle == DMA_ERROR_CODE)
+ goto err_mapping;
+
+ return addr;
+
+err_mapping:
+ __free_from_pool(addr, size);
+ return NULL;
+}
+
+static void __iommu_free_atomic(struct device *dev, struct page **pages,
+ dma_addr_t handle, size_t size)
+{
+ __iommu_remove_mapping(dev, handle, size);
+ __free_from_pool(page_address(pages[0]), size);
+}
+
static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
{
@@ -1113,6 +1201,9 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
*handle = DMA_ERROR_CODE;
size = PAGE_ALIGN(size);
+ if (gfp & GFP_ATOMIC)
+ return __iommu_alloc_atomic(dev, size, handle);
+
pages = __iommu_alloc_buffer(dev, size, gfp);
if (!pages)
return NULL;
@@ -1179,6 +1270,11 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
return;
}
+ if (__in_atomic_pool(cpu_addr, size)) {
+ __iommu_free_atomic(dev, pages, handle, size);
+ return;
+ }
+
if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
unmap_kernel_range((unsigned long)cpu_addr, size);
vunmap(cpu_addr);
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 77458548e031..40ca11ed6e5f 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -231,8 +231,6 @@ void __sync_icache_dcache(pte_t pteval)
struct page *page;
struct address_space *mapping;
- if (!pte_present_user(pteval))
- return;
if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
/* only flush non-aliasing VIPT caches for exec mappings */
return;
diff --git a/arch/arm/mm/mm.h b/arch/arm/mm/mm.h
index 6776160618ef..a8ee92da3544 100644
--- a/arch/arm/mm/mm.h
+++ b/arch/arm/mm/mm.h
@@ -55,6 +55,9 @@ extern void __flush_dcache_page(struct address_space *mapping, struct page *page
/* permanent static mappings from iotable_init() */
#define VM_ARM_STATIC_MAPPING 0x40000000
+/* empty mapping */
+#define VM_ARM_EMPTY_MAPPING 0x20000000
+
/* mapping type (attributes) for permanent static mappings */
#define VM_ARM_MTYPE(mt) ((mt) << 20)
#define VM_ARM_MTYPE_MASK (0x1f << 20)
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 4c2d0451e84a..c2fa21d0103e 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -807,7 +807,7 @@ static void __init pmd_empty_section_gap(unsigned long addr)
vm = early_alloc_aligned(sizeof(*vm), __alignof__(*vm));
vm->addr = (void *)addr;
vm->size = SECTION_SIZE;
- vm->flags = VM_IOREMAP | VM_ARM_STATIC_MAPPING;
+ vm->flags = VM_IOREMAP | VM_ARM_EMPTY_MAPPING;
vm->caller = pmd_empty_section_gap;
vm_area_add_early(vm);
}
@@ -820,7 +820,7 @@ static void __init fill_pmd_gaps(void)
/* we're still single threaded hence no lock needed here */
for (vm = vmlist; vm; vm = vm->next) {
- if (!(vm->flags & VM_ARM_STATIC_MAPPING))
+ if (!(vm->flags & (VM_ARM_STATIC_MAPPING | VM_ARM_EMPTY_MAPPING)))
continue;
addr = (unsigned long)vm->addr;
if (addr < next)
@@ -961,8 +961,8 @@ void __init sanity_check_meminfo(void)
* Check whether this memory bank would partially overlap
* the vmalloc area.
*/
- if (__va(bank->start + bank->size) > vmalloc_min ||
- __va(bank->start + bank->size) < __va(bank->start)) {
+ if (__va(bank->start + bank->size - 1) >= vmalloc_min ||
+ __va(bank->start + bank->size - 1) <= __va(bank->start)) {
unsigned long newsize = vmalloc_min - __va(bank->start);
printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
"to -%.8llx (vmalloc region overlap).\n",
diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S
index 845f461f8ec1..ea94765acf9a 100644
--- a/arch/arm/mm/tlb-v7.S
+++ b/arch/arm/mm/tlb-v7.S
@@ -39,10 +39,18 @@ ENTRY(v7wbi_flush_user_tlb_range)
mov r0, r0, lsr #PAGE_SHIFT @ align address
mov r1, r1, lsr #PAGE_SHIFT
asid r3, r3 @ mask ASID
+#ifdef CONFIG_ARM_ERRATA_720789
+ ALT_SMP(W(mov) r3, #0 )
+ ALT_UP(W(nop) )
+#endif
orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA
mov r1, r1, lsl #PAGE_SHIFT
1:
+#ifdef CONFIG_ARM_ERRATA_720789
+ ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA all ASID (shareable)
+#else
ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable)
+#endif
ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA
add r0, r0, #PAGE_SZ
@@ -67,7 +75,11 @@ ENTRY(v7wbi_flush_kern_tlb_range)
mov r0, r0, lsl #PAGE_SHIFT
mov r1, r1, lsl #PAGE_SHIFT
1:
+#ifdef CONFIG_ARM_ERRATA_720789
+ ALT_SMP(mcr p15, 0, r0, c8, c3, 3) @ TLB invalidate U MVA all ASID (shareable)
+#else
ALT_SMP(mcr p15, 0, r0, c8, c3, 1) @ TLB invalidate U MVA (shareable)
+#endif
ALT_UP(mcr p15, 0, r0, c8, c7, 1) @ TLB invalidate U MVA
add r0, r0, #PAGE_SZ
cmp r0, r1
diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig
index cb3e3eef55c0..6b46cee2f9cd 100644
--- a/arch/arm/plat-mxc/devices/Kconfig
+++ b/arch/arm/plat-mxc/devices/Kconfig
@@ -15,7 +15,11 @@ config IMX_HAVE_PLATFORM_GPIO_KEYS
config IMX_HAVE_PLATFORM_IMX21_HCD
bool
-
+
+config IMX_HAVE_PLATFORM_IMX27_CODA
+ bool
+ default y if SOC_IMX27
+
config IMX_HAVE_PLATFORM_IMX2_WDT
bool
diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile
index c11ac8472beb..76f3195475d0 100644
--- a/arch/arm/plat-mxc/devices/Makefile
+++ b/arch/arm/plat-mxc/devices/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_FSL_USB2_UDC) += platform-fsl-usb2-udc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_GPIO_KEYS) += platform-gpio_keys.o
obj-y += platform-gpio-mxc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX21_HCD) += platform-imx21-hcd.o
+obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX27_CODA) += platform-imx27-coda.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX2_WDT) += platform-imx2-wdt.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_IMXDI_RTC) += platform-imxdi_rtc.o
obj-y += platform-imx-dma.o
diff --git a/arch/arm/plat-mxc/devices/platform-imx27-coda.c b/arch/arm/plat-mxc/devices/platform-imx27-coda.c
new file mode 100644
index 000000000000..8b12aacdf396
--- /dev/null
+++ b/arch/arm/plat-mxc/devices/platform-imx27-coda.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 Vista Silicon
+ * Javier Martin <javier.martin@vista-silicon.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.
+ */
+
+#include <mach/hardware.h>
+#include <mach/devices-common.h>
+
+#ifdef CONFIG_SOC_IMX27
+const struct imx_imx27_coda_data imx27_coda_data __initconst = {
+ .iobase = MX27_VPU_BASE_ADDR,
+ .iosize = SZ_512,
+ .irq = MX27_INT_VPU,
+};
+#endif
+
+struct platform_device *__init imx_add_imx27_coda(
+ const struct imx_imx27_coda_data *data)
+{
+ struct resource res[] = {
+ {
+ .start = data->iobase,
+ .end = data->iobase + data->iosize - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = data->irq,
+ .end = data->irq,
+ .flags = IORESOURCE_IRQ,
+ },
+ };
+ return imx_add_platform_device_dmamask("coda-imx27", 0, res, 2, NULL,
+ 0, DMA_BIT_MASK(32));
+}
diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h
index a7f5bb1084d7..762780cad1bd 100644
--- a/arch/arm/plat-mxc/include/mach/devices-common.h
+++ b/arch/arm/plat-mxc/include/mach/devices-common.h
@@ -83,6 +83,14 @@ struct platform_device *__init imx_add_imx21_hcd(
const struct imx_imx21_hcd_data *data,
const struct mx21_usbh_platform_data *pdata);
+struct imx_imx27_coda_data {
+ resource_size_t iobase;
+ resource_size_t iosize;
+ resource_size_t irq;
+};
+struct platform_device *__init imx_add_imx27_coda(
+ const struct imx_imx27_coda_data *data);
+
struct imx_imx2_wdt_data {
int id;
resource_size_t iobase;
diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h
index 627d94f1b010..ec466400a200 100644
--- a/arch/arm/plat-mxc/include/mach/mx25.h
+++ b/arch/arm/plat-mxc/include/mach/mx25.h
@@ -98,6 +98,7 @@
#define MX25_INT_UART1 (NR_IRQS_LEGACY + 45)
#define MX25_INT_GPIO2 (NR_IRQS_LEGACY + 51)
#define MX25_INT_GPIO1 (NR_IRQS_LEGACY + 52)
+#define MX25_INT_GPT1 (NR_IRQS_LEGACY + 54)
#define MX25_INT_FEC (NR_IRQS_LEGACY + 57)
#define MX25_DMA_REQ_SSI2_RX1 22
diff --git a/arch/arm/plat-mxc/tzic.c b/arch/arm/plat-mxc/tzic.c
index c2193178210b..3ed1adbc09f8 100644
--- a/arch/arm/plat-mxc/tzic.c
+++ b/arch/arm/plat-mxc/tzic.c
@@ -23,6 +23,7 @@
#include <mach/hardware.h>
#include <mach/common.h>
+#include <mach/irqs.h>
#include "irq-common.h"
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 626ad8cad7a9..938b50a33439 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -189,6 +189,7 @@ struct omap_dm_timer *omap_dm_timer_request(void)
timer->reserved = 1;
break;
}
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
if (timer) {
ret = omap_dm_timer_prepare(timer);
@@ -197,7 +198,6 @@ struct omap_dm_timer *omap_dm_timer_request(void)
timer = NULL;
}
}
- spin_unlock_irqrestore(&dm_timer_lock, flags);
if (!timer)
pr_debug("%s: timer request failed!\n", __func__);
@@ -220,6 +220,7 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)
break;
}
}
+ spin_unlock_irqrestore(&dm_timer_lock, flags);
if (timer) {
ret = omap_dm_timer_prepare(timer);
@@ -228,7 +229,6 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)
timer = NULL;
}
}
- spin_unlock_irqrestore(&dm_timer_lock, flags);
if (!timer)
pr_debug("%s: timer%d request failed!\n", __func__, id);
@@ -258,7 +258,7 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_enable);
void omap_dm_timer_disable(struct omap_dm_timer *timer)
{
- pm_runtime_put(&timer->pdev->dev);
+ pm_runtime_put_sync(&timer->pdev->dev);
}
EXPORT_SYMBOL_GPL(omap_dm_timer_disable);
diff --git a/arch/arm/plat-omap/include/plat/cpu.h b/arch/arm/plat-omap/include/plat/cpu.h
index 68b180edcfff..bb5d08a70dbc 100644
--- a/arch/arm/plat-omap/include/plat/cpu.h
+++ b/arch/arm/plat-omap/include/plat/cpu.h
@@ -372,7 +372,8 @@ IS_OMAP_TYPE(3430, 0x3430)
#define cpu_class_is_omap1() (cpu_is_omap7xx() || cpu_is_omap15xx() || \
cpu_is_omap16xx())
#define cpu_class_is_omap2() (cpu_is_omap24xx() || cpu_is_omap34xx() || \
- cpu_is_omap44xx() || soc_is_omap54xx())
+ cpu_is_omap44xx() || soc_is_omap54xx() || \
+ soc_is_am33xx())
/* Various silicon revisions for omap2 */
#define OMAP242X_CLASS 0x24200024
diff --git a/arch/arm/plat-omap/include/plat/mmc.h b/arch/arm/plat-omap/include/plat/mmc.h
index 5493bd95da5e..eb3e4d555343 100644
--- a/arch/arm/plat-omap/include/plat/mmc.h
+++ b/arch/arm/plat-omap/include/plat/mmc.h
@@ -81,8 +81,6 @@ struct omap_mmc_platform_data {
/* Return context loss count due to PM states changing */
int (*get_context_loss_count)(struct device *dev);
- u64 dma_mask;
-
/* Integrating attributes from the omap_hwmod layer */
u8 controller_flags;
diff --git a/arch/arm/plat-omap/include/plat/multi.h b/arch/arm/plat-omap/include/plat/multi.h
index 045e320f1067..324d31b14852 100644
--- a/arch/arm/plat-omap/include/plat/multi.h
+++ b/arch/arm/plat-omap/include/plat/multi.h
@@ -108,4 +108,13 @@
# endif
#endif
+#ifdef CONFIG_SOC_AM33XX
+# ifdef OMAP_NAME
+# undef MULTI_OMAP2
+# define MULTI_OMAP2
+# else
+# define OMAP_NAME am33xx
+# endif
+#endif
+
#endif /* __PLAT_OMAP_MULTI_H */
diff --git a/arch/arm/plat-omap/include/plat/uncompress.h b/arch/arm/plat-omap/include/plat/uncompress.h
index b8d19a136781..7f7b112acccb 100644
--- a/arch/arm/plat-omap/include/plat/uncompress.h
+++ b/arch/arm/plat-omap/include/plat/uncompress.h
@@ -110,7 +110,7 @@ static inline void flush(void)
_DEBUG_LL_ENTRY(mach, AM33XX_UART##p##_BASE, OMAP_PORT_SHIFT, \
AM33XXUART##p)
-static inline void __arch_decomp_setup(unsigned long arch_id)
+static inline void arch_decomp_setup(void)
{
int port = 0;
@@ -198,8 +198,6 @@ static inline void __arch_decomp_setup(unsigned long arch_id)
} while (0);
}
-#define arch_decomp_setup() __arch_decomp_setup(arch_id)
-
/*
* nothing to do
*/
diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c
index 766181cb5c95..024f3b08db29 100644
--- a/arch/arm/plat-omap/sram.c
+++ b/arch/arm/plat-omap/sram.c
@@ -68,6 +68,7 @@
static unsigned long omap_sram_start;
static void __iomem *omap_sram_base;
+static unsigned long omap_sram_skip;
static unsigned long omap_sram_size;
static void __iomem *omap_sram_ceil;
@@ -106,6 +107,7 @@ static int is_sram_locked(void)
*/
static void __init omap_detect_sram(void)
{
+ omap_sram_skip = SRAM_BOOTLOADER_SZ;
if (cpu_class_is_omap2()) {
if (is_sram_locked()) {
if (cpu_is_omap34xx()) {
@@ -113,6 +115,7 @@ static void __init omap_detect_sram(void)
if ((omap_type() == OMAP2_DEVICE_TYPE_EMU) ||
(omap_type() == OMAP2_DEVICE_TYPE_SEC)) {
omap_sram_size = 0x7000; /* 28K */
+ omap_sram_skip += SZ_16K;
} else {
omap_sram_size = 0x8000; /* 32K */
}
@@ -175,8 +178,10 @@ static void __init omap_map_sram(void)
return;
#ifdef CONFIG_OMAP4_ERRATA_I688
+ if (cpu_is_omap44xx()) {
omap_sram_start += PAGE_SIZE;
omap_sram_size -= SZ_16K;
+ }
#endif
if (cpu_is_omap34xx()) {
/*
@@ -203,8 +208,8 @@ static void __init omap_map_sram(void)
* Looks like we need to preserve some bootloader code at the
* beginning of SRAM for jumping to flash for reboot to work...
*/
- memset_io(omap_sram_base + SRAM_BOOTLOADER_SZ, 0,
- omap_sram_size - SRAM_BOOTLOADER_SZ);
+ memset_io(omap_sram_base + omap_sram_skip, 0,
+ omap_sram_size - omap_sram_skip);
}
/*
@@ -218,7 +223,7 @@ void *omap_sram_push_address(unsigned long size)
{
unsigned long available, new_ceil = (unsigned long)omap_sram_ceil;
- available = omap_sram_ceil - (omap_sram_base + SRAM_BOOTLOADER_SZ);
+ available = omap_sram_ceil - (omap_sram_base + omap_sram_skip);
if (size > available) {
pr_err("Not enough space in SRAM\n");
diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c
index c1793786aea9..b8b747a9d360 100644
--- a/arch/arm/plat-orion/common.c
+++ b/arch/arm/plat-orion/common.c
@@ -47,6 +47,7 @@ void __init orion_clkdev_init(struct clk *tclk)
orion_clkdev_add(NULL, MV643XX_ETH_NAME ".2", tclk);
orion_clkdev_add(NULL, MV643XX_ETH_NAME ".3", tclk);
orion_clkdev_add(NULL, "orion_wdt", tclk);
+ orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", tclk);
}
/* Fill in the resources structure and link it into the platform
@@ -290,10 +291,12 @@ static struct platform_device orion_ge00 = {
void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data,
unsigned long mapbase,
unsigned long irq,
- unsigned long irq_err)
+ unsigned long irq_err,
+ unsigned int tx_csum_limit)
{
fill_resources(&orion_ge00_shared, orion_ge00_shared_resources,
mapbase + 0x2000, SZ_16K - 1, irq_err);
+ orion_ge00_shared_data.tx_csum_limit = tx_csum_limit;
ge_complete(&orion_ge00_shared_data,
orion_ge00_resources, irq, &orion_ge00_shared,
eth_data, &orion_ge00);
@@ -342,10 +345,12 @@ static struct platform_device orion_ge01 = {
void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data,
unsigned long mapbase,
unsigned long irq,
- unsigned long irq_err)
+ unsigned long irq_err,
+ unsigned int tx_csum_limit)
{
fill_resources(&orion_ge01_shared, orion_ge01_shared_resources,
mapbase + 0x2000, SZ_16K - 1, irq_err);
+ orion_ge01_shared_data.tx_csum_limit = tx_csum_limit;
ge_complete(&orion_ge01_shared_data,
orion_ge01_resources, irq, &orion_ge01_shared,
eth_data, &orion_ge01);
diff --git a/arch/arm/plat-orion/gpio.c b/arch/arm/plat-orion/gpio.c
index af95af257301..dfda74fae6f2 100644
--- a/arch/arm/plat-orion/gpio.c
+++ b/arch/arm/plat-orion/gpio.c
@@ -8,15 +8,22 @@
* warranty of any kind, whether express or implied.
*/
+#define DEBUG
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/leds.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <plat/gpio.h>
/*
* GPIO unit register offsets.
@@ -38,6 +45,7 @@ struct orion_gpio_chip {
unsigned long valid_output;
int mask_offset;
int secondary_irq_base;
+ struct irq_domain *domain;
};
static void __iomem *GPIO_OUT(struct orion_gpio_chip *ochip)
@@ -222,10 +230,10 @@ static int orion_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
struct orion_gpio_chip *ochip =
container_of(chip, struct orion_gpio_chip, chip);
- return ochip->secondary_irq_base + pin;
+ return irq_create_mapping(ochip->domain,
+ ochip->secondary_irq_base + pin);
}
-
/*
* Orion-specific GPIO API extensions.
*/
@@ -353,12 +361,10 @@ static int gpio_irq_set_type(struct irq_data *d, u32 type)
int pin;
u32 u;
- pin = d->irq - gc->irq_base;
+ pin = d->hwirq - ochip->secondary_irq_base;
u = readl(GPIO_IO_CONF(ochip)) & (1 << pin);
if (!u) {
- printk(KERN_ERR "orion gpio_irq_set_type failed "
- "(irq %d, pin %d).\n", d->irq, pin);
return -EINVAL;
}
@@ -397,17 +403,53 @@ static int gpio_irq_set_type(struct irq_data *d, u32 type)
u &= ~(1 << pin); /* rising */
writel(u, GPIO_IN_POL(ochip));
}
-
return 0;
}
-void __init orion_gpio_init(int gpio_base, int ngpio,
- u32 base, int mask_offset, int secondary_irq_base)
+static void gpio_irq_handler(unsigned irq, struct irq_desc *desc)
+{
+ struct orion_gpio_chip *ochip = irq_get_handler_data(irq);
+ u32 cause, type;
+ int i;
+
+ if (ochip == NULL)
+ return;
+
+ cause = readl(GPIO_DATA_IN(ochip)) & readl(GPIO_LEVEL_MASK(ochip));
+ cause |= readl(GPIO_EDGE_CAUSE(ochip)) & readl(GPIO_EDGE_MASK(ochip));
+
+ for (i = 0; i < ochip->chip.ngpio; i++) {
+ int irq;
+
+ irq = ochip->secondary_irq_base + i;
+
+ if (!(cause & (1 << i)))
+ continue;
+
+ type = irqd_get_trigger_type(irq_get_irq_data(irq));
+ if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
+ /* Swap polarity (race with GPIO line) */
+ u32 polarity;
+
+ polarity = readl(GPIO_IN_POL(ochip));
+ polarity ^= 1 << i;
+ writel(polarity, GPIO_IN_POL(ochip));
+ }
+ generic_handle_irq(irq);
+ }
+}
+
+void __init orion_gpio_init(struct device_node *np,
+ int gpio_base, int ngpio,
+ void __iomem *base, int mask_offset,
+ int secondary_irq_base,
+ int irqs[4])
{
struct orion_gpio_chip *ochip;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
char gc_label[16];
+ int i;
if (orion_gpio_chip_count == ARRAY_SIZE(orion_gpio_chips))
return;
@@ -426,6 +468,10 @@ void __init orion_gpio_init(int gpio_base, int ngpio,
ochip->chip.base = gpio_base;
ochip->chip.ngpio = ngpio;
ochip->chip.can_sleep = 0;
+#ifdef CONFIG_OF
+ ochip->chip.of_node = np;
+#endif
+
spin_lock_init(&ochip->lock);
ochip->base = (void __iomem *)base;
ochip->valid_input = 0;
@@ -435,8 +481,6 @@ void __init orion_gpio_init(int gpio_base, int ngpio,
gpiochip_add(&ochip->chip);
- orion_gpio_chip_count++;
-
/*
* Mask and clear GPIO interrupts.
*/
@@ -444,16 +488,28 @@ void __init orion_gpio_init(int gpio_base, int ngpio,
writel(0, GPIO_EDGE_MASK(ochip));
writel(0, GPIO_LEVEL_MASK(ochip));
- gc = irq_alloc_generic_chip("orion_gpio_irq", 2, secondary_irq_base,
+ /* Setup the interrupt handlers. Each chip can have up to 4
+ * interrupt handlers, with each handler dealing with 8 GPIO
+ * pins. */
+
+ for (i = 0; i < 4; i++) {
+ if (irqs[i]) {
+ irq_set_handler_data(irqs[i], ochip);
+ irq_set_chained_handler(irqs[i], gpio_irq_handler);
+ }
+ }
+
+ gc = irq_alloc_generic_chip("orion_gpio_irq", 2,
+ secondary_irq_base,
ochip->base, handle_level_irq);
gc->private = ochip;
-
ct = gc->chip_types;
ct->regs.mask = ochip->mask_offset + GPIO_LEVEL_MASK_OFF;
ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_set_type = gpio_irq_set_type;
+ ct->chip.name = ochip->chip.label;
ct++;
ct->regs.mask = ochip->mask_offset + GPIO_EDGE_MASK_OFF;
@@ -464,41 +520,69 @@ void __init orion_gpio_init(int gpio_base, int ngpio,
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_set_type = gpio_irq_set_type;
ct->handler = handle_edge_irq;
+ ct->chip.name = ochip->chip.label;
irq_setup_generic_chip(gc, IRQ_MSK(ngpio), IRQ_GC_INIT_MASK_CACHE,
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
-}
-void orion_gpio_irq_handler(int pinoff)
-{
- struct orion_gpio_chip *ochip;
- u32 cause, type;
- int i;
-
- ochip = orion_gpio_chip_find(pinoff);
- if (ochip == NULL)
- return;
-
- cause = readl(GPIO_DATA_IN(ochip)) & readl(GPIO_LEVEL_MASK(ochip));
- cause |= readl(GPIO_EDGE_CAUSE(ochip)) & readl(GPIO_EDGE_MASK(ochip));
-
- for (i = 0; i < ochip->chip.ngpio; i++) {
- int irq;
+ /* Setup irq domain on top of the generic chip. */
+ ochip->domain = irq_domain_add_legacy(np,
+ ochip->chip.ngpio,
+ ochip->secondary_irq_base,
+ ochip->secondary_irq_base,
+ &irq_domain_simple_ops,
+ ochip);
+ if (!ochip->domain)
+ panic("%s: couldn't allocate irq domain (DT).\n",
+ ochip->chip.label);
- irq = ochip->secondary_irq_base + i;
+ orion_gpio_chip_count++;
+}
- if (!(cause & (1 << i)))
- continue;
+#ifdef CONFIG_OF
+static void __init orion_gpio_of_init_one(struct device_node *np,
+ int irq_gpio_base)
+{
+ int ngpio, gpio_base, mask_offset;
+ void __iomem *base;
+ int ret, i;
+ int irqs[4];
+ int secondary_irq_base;
+
+ ret = of_property_read_u32(np, "ngpio", &ngpio);
+ if (ret)
+ goto out;
+ ret = of_property_read_u32(np, "mask-offset", &mask_offset);
+ if (ret == -EINVAL)
+ mask_offset = 0;
+ else
+ goto out;
+ base = of_iomap(np, 0);
+ if (!base)
+ goto out;
+
+ secondary_irq_base = irq_gpio_base + (32 * orion_gpio_chip_count);
+ gpio_base = 32 * orion_gpio_chip_count;
+
+ /* Get the interrupt numbers. Each chip can have up to 4
+ * interrupt handlers, with each handler dealing with 8 GPIO
+ * pins. */
+
+ for (i = 0; i < 4; i++)
+ irqs[i] = irq_of_parse_and_map(np, i);
+
+ orion_gpio_init(np, gpio_base, ngpio, base, mask_offset,
+ secondary_irq_base, irqs);
+ return;
+out:
+ pr_err("%s: %s: missing mandatory property\n", __func__, np->name);
+}
- type = irqd_get_trigger_type(irq_get_irq_data(irq));
- if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
- /* Swap polarity (race with GPIO line) */
- u32 polarity;
+void __init orion_gpio_of_init(int irq_gpio_base)
+{
+ struct device_node *np;
- polarity = readl(GPIO_IN_POL(ochip));
- polarity ^= 1 << i;
- writel(polarity, GPIO_IN_POL(ochip));
- }
- generic_handle_irq(irq);
- }
+ for_each_compatible_node(np, NULL, "marvell,orion-gpio")
+ orion_gpio_of_init_one(np, irq_gpio_base);
}
+#endif
diff --git a/arch/arm/plat-orion/include/plat/common.h b/arch/arm/plat-orion/include/plat/common.h
index e00fdb213609..ae2377ef63e5 100644
--- a/arch/arm/plat-orion/include/plat/common.h
+++ b/arch/arm/plat-orion/include/plat/common.h
@@ -39,12 +39,14 @@ void __init orion_rtc_init(unsigned long mapbase,
void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data,
unsigned long mapbase,
unsigned long irq,
- unsigned long irq_err);
+ unsigned long irq_err,
+ unsigned int tx_csum_limit);
void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data,
unsigned long mapbase,
unsigned long irq,
- unsigned long irq_err);
+ unsigned long irq_err,
+ unsigned int tx_csum_limit);
void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data,
unsigned long mapbase,
diff --git a/arch/arm/plat-orion/include/plat/gpio.h b/arch/arm/plat-orion/include/plat/gpio.h
index bec0c98ce41f..81c6fc8a7b28 100644
--- a/arch/arm/plat-orion/include/plat/gpio.h
+++ b/arch/arm/plat-orion/include/plat/gpio.h
@@ -13,7 +13,7 @@
#include <linux/init.h>
#include <linux/types.h>
-
+#include <linux/irqdomain.h>
/*
* Orion-specific GPIO API extensions.
*/
@@ -27,13 +27,11 @@ int orion_gpio_led_blink_set(unsigned gpio, int state,
void orion_gpio_set_valid(unsigned pin, int mode);
/* Initialize gpiolib. */
-void __init orion_gpio_init(int gpio_base, int ngpio,
- u32 base, int mask_offset, int secondary_irq_base);
-
-/*
- * GPIO interrupt handling.
- */
-void orion_gpio_irq_handler(int irqoff);
-
+void __init orion_gpio_init(struct device_node *np,
+ int gpio_base, int ngpio,
+ void __iomem *base, int mask_offset,
+ int secondary_irq_base,
+ int irq[4]);
+void __init orion_gpio_of_init(int irq_gpio_base);
#endif
diff --git a/arch/arm/plat-orion/include/plat/irq.h b/arch/arm/plat-orion/include/plat/irq.h
index f05eeab94968..50547e417936 100644
--- a/arch/arm/plat-orion/include/plat/irq.h
+++ b/arch/arm/plat-orion/include/plat/irq.h
@@ -12,6 +12,5 @@
#define __PLAT_IRQ_H
void orion_irq_init(unsigned int irq_start, void __iomem *maskaddr);
-
-
+void __init orion_dt_init_irq(void);
#endif
diff --git a/arch/arm/plat-orion/irq.c b/arch/arm/plat-orion/irq.c
index 2d5b9c1ef389..d751964def4c 100644
--- a/arch/arm/plat-orion/irq.c
+++ b/arch/arm/plat-orion/irq.c
@@ -11,8 +11,12 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <plat/irq.h>
+#include <plat/gpio.h>
void __init orion_irq_init(unsigned int irq_start, void __iomem *maskaddr)
{
@@ -32,3 +36,39 @@ void __init orion_irq_init(unsigned int irq_start, void __iomem *maskaddr)
irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_MASK_CACHE,
IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE);
}
+
+#ifdef CONFIG_OF
+static int __init orion_add_irq_domain(struct device_node *np,
+ struct device_node *interrupt_parent)
+{
+ int i = 0, irq_gpio;
+ void __iomem *base;
+
+ do {
+ base = of_iomap(np, i);
+ if (base) {
+ orion_irq_init(i * 32, base);
+ i++;
+ }
+ } while (base);
+
+ irq_domain_add_legacy(np, i * 32, 0, 0,
+ &irq_domain_simple_ops, NULL);
+
+ irq_gpio = i * 32;
+ orion_gpio_of_init(irq_gpio);
+
+ return 0;
+}
+
+static const struct of_device_id orion_irq_match[] = {
+ { .compatible = "marvell,orion-intc",
+ .data = orion_add_irq_domain, },
+ {},
+};
+
+void __init orion_dt_init_irq(void)
+{
+ of_irq_init(orion_irq_match);
+}
+#endif
diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c
index 28f898f75380..db98e7021f0d 100644
--- a/arch/arm/plat-s3c24xx/dma.c
+++ b/arch/arm/plat-s3c24xx/dma.c
@@ -430,7 +430,7 @@ s3c2410_dma_canload(struct s3c2410_dma_chan *chan)
* when necessary.
*/
-int s3c2410_dma_enqueue(unsigned int channel, void *id,
+int s3c2410_dma_enqueue(enum dma_ch channel, void *id,
dma_addr_t data, int size)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 7aca31c1df1f..9c3b90c3538e 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -403,7 +403,8 @@ config S5P_DEV_USB_EHCI
config S3C24XX_PWM
bool "PWM device support"
- select HAVE_PWM
+ select PWM
+ select PWM_SAMSUNG
help
Support for exporting the PWM timer blocks via the pwm device
system
diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c
index 65c5eca475e7..d1116e2dfbea 100644
--- a/arch/arm/plat-samsung/clock.c
+++ b/arch/arm/plat-samsung/clock.c
@@ -144,6 +144,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
int clk_set_rate(struct clk *clk, unsigned long rate)
{
+ unsigned long flags;
int ret;
if (IS_ERR(clk))
@@ -159,9 +160,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
if (clk->ops == NULL || clk->ops->set_rate == NULL)
return -EINVAL;
- spin_lock(&clocks_lock);
+ spin_lock_irqsave(&clocks_lock, flags);
ret = (clk->ops->set_rate)(clk, rate);
- spin_unlock(&clocks_lock);
+ spin_unlock_irqrestore(&clocks_lock, flags);
return ret;
}
@@ -173,17 +174,18 @@ struct clk *clk_get_parent(struct clk *clk)
int clk_set_parent(struct clk *clk, struct clk *parent)
{
+ unsigned long flags;
int ret = 0;
if (IS_ERR(clk))
return -EINVAL;
- spin_lock(&clocks_lock);
+ spin_lock_irqsave(&clocks_lock, flags);
if (clk->ops && clk->ops->set_parent)
ret = (clk->ops->set_parent)(clk, parent);
- spin_unlock(&clocks_lock);
+ spin_unlock_irqrestore(&clocks_lock, flags);
return ret;
}
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index 74e31ce35538..fc49f3dabd76 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -32,6 +32,8 @@
#include <linux/platform_data/s3c-hsudc.h>
#include <linux/platform_data/s3c-hsotg.h>
+#include <media/s5p_hdmi.h>
+
#include <asm/irq.h>
#include <asm/pmu.h>
#include <asm/mach/arch.h>
@@ -748,7 +750,8 @@ void __init s5p_i2c_hdmiphy_set_platdata(struct s3c2410_platform_i2c *pd)
if (!pd) {
pd = &default_i2c_data;
- if (soc_is_exynos4210())
+ if (soc_is_exynos4210() ||
+ soc_is_exynos4212() || soc_is_exynos4412())
pd->bus_num = 8;
else if (soc_is_s5pv210())
pd->bus_num = 3;
@@ -759,6 +762,30 @@ void __init s5p_i2c_hdmiphy_set_platdata(struct s3c2410_platform_i2c *pd)
npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
&s5p_device_i2c_hdmiphy);
}
+
+struct s5p_hdmi_platform_data s5p_hdmi_def_platdata;
+
+void __init s5p_hdmi_set_platdata(struct i2c_board_info *hdmiphy_info,
+ struct i2c_board_info *mhl_info, int mhl_bus)
+{
+ struct s5p_hdmi_platform_data *pd = &s5p_hdmi_def_platdata;
+
+ if (soc_is_exynos4210() ||
+ soc_is_exynos4212() || soc_is_exynos4412())
+ pd->hdmiphy_bus = 8;
+ else if (soc_is_s5pv210())
+ pd->hdmiphy_bus = 3;
+ else
+ pd->hdmiphy_bus = 0;
+
+ pd->hdmiphy_info = hdmiphy_info;
+ pd->mhl_info = mhl_info;
+ pd->mhl_bus = mhl_bus;
+
+ s3c_set_platdata(pd, sizeof(struct s5p_hdmi_platform_data),
+ &s5p_device_hdmi);
+}
+
#endif /* CONFIG_S5P_DEV_I2C_HDMIPHY */
/* I2S */
diff --git a/arch/arm/plat-samsung/include/plat/hdmi.h b/arch/arm/plat-samsung/include/plat/hdmi.h
new file mode 100644
index 000000000000..331d046ac2c5
--- /dev/null
+++ b/arch/arm/plat-samsung/include/plat/hdmi.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the 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 __PLAT_SAMSUNG_HDMI_H
+#define __PLAT_SAMSUNG_HDMI_H __FILE__
+
+extern void s5p_hdmi_set_platdata(struct i2c_board_info *hdmiphy_info,
+ struct i2c_board_info *mhl_info, int mhl_bus);
+
+#endif /* __PLAT_SAMSUNG_HDMI_H */
diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c
index 64ab65f0fdbc..15070284343e 100644
--- a/arch/arm/plat-samsung/pm.c
+++ b/arch/arm/plat-samsung/pm.c
@@ -74,7 +74,7 @@ unsigned char pm_uart_udivslot;
#ifdef CONFIG_SAMSUNG_PM_DEBUG
-struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
+static struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
{
diff --git a/arch/arm/plat-spear/include/plat/pl080.h b/arch/arm/plat-spear/include/plat/pl080.h
index 2bc6b54460a8..eb6590ded40d 100644
--- a/arch/arm/plat-spear/include/plat/pl080.h
+++ b/arch/arm/plat-spear/include/plat/pl080.h
@@ -14,8 +14,8 @@
#ifndef __PLAT_PL080_H
#define __PLAT_PL080_H
-struct pl08x_dma_chan;
-int pl080_get_signal(struct pl08x_dma_chan *ch);
-void pl080_put_signal(struct pl08x_dma_chan *ch);
+struct pl08x_channel_data;
+int pl080_get_signal(const struct pl08x_channel_data *cd);
+void pl080_put_signal(const struct pl08x_channel_data *cd, int signal);
#endif /* __PLAT_PL080_H */
diff --git a/arch/arm/plat-spear/pl080.c b/arch/arm/plat-spear/pl080.c
index 12cf27f935f9..cfa1199d0f4a 100644
--- a/arch/arm/plat-spear/pl080.c
+++ b/arch/arm/plat-spear/pl080.c
@@ -27,9 +27,8 @@ struct {
unsigned char val;
} signals[16] = {{0, 0}, };
-int pl080_get_signal(struct pl08x_dma_chan *ch)
+int pl080_get_signal(const struct pl08x_channel_data *cd)
{
- const struct pl08x_channel_data *cd = ch->cd;
unsigned int signal = cd->min_signal, val;
unsigned long flags;
@@ -63,18 +62,17 @@ int pl080_get_signal(struct pl08x_dma_chan *ch)
return signal;
}
-void pl080_put_signal(struct pl08x_dma_chan *ch)
+void pl080_put_signal(const struct pl08x_channel_data *cd, int signal)
{
- const struct pl08x_channel_data *cd = ch->cd;
unsigned long flags;
spin_lock_irqsave(&lock, flags);
/* if signal is not used */
- if (!signals[cd->min_signal].busy)
+ if (!signals[signal].busy)
BUG();
- signals[cd->min_signal].busy--;
+ signals[signal].busy--;
spin_unlock_irqrestore(&lock, flags);
}
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S
index 4fa9903b83cf..cc926c985981 100644
--- a/arch/arm/vfp/entry.S
+++ b/arch/arm/vfp/entry.S
@@ -7,18 +7,20 @@
* 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.
- *
- * Basic entry code, called from the kernel's undefined instruction trap.
- * r0 = faulted instruction
- * r5 = faulted PC+4
- * r9 = successful return
- * r10 = thread_info structure
- * lr = failure return
*/
#include <asm/thread_info.h>
#include <asm/vfpmacros.h>
#include "../kernel/entry-header.S"
+@ VFP entry point.
+@
+@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@ r2 = PC value to resume execution after successful emulation
+@ r9 = normal "successful" return address
+@ r10 = this threads thread_info structure
+@ lr = unrecognised instruction return address
+@ IRQs disabled.
+@
ENTRY(do_vfp)
#ifdef CONFIG_PREEMPT
ldr r4, [r10, #TI_PREEMPT] @ get preempt count
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index d50f0e486cf2..ea0349f63586 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -62,13 +62,13 @@
@ VFP hardware support entry point.
@
-@ r0 = faulted instruction
-@ r2 = faulted PC+4
-@ r9 = successful return
+@ r0 = instruction opcode (32-bit ARM or two 16-bit Thumb)
+@ r2 = PC value to resume execution after successful emulation
+@ r9 = normal "successful" return address
@ r10 = vfp_state union
@ r11 = CPU number
-@ lr = failure return
-
+@ lr = unrecognised instruction return address
+@ IRQs enabled.
ENTRY(vfp_support_entry)
DBGSTR3 "instr %08x pc %08x state %p", r0, r2, r10
@@ -162,9 +162,12 @@ vfp_hw_state_valid:
@ exception before retrying branch
@ out before setting an FPEXC that
@ stops us reading stuff
- VFPFMXR FPEXC, r1 @ restore FPEXC last
- sub r2, r2, #4
- str r2, [sp, #S_PC] @ retry the instruction
+ VFPFMXR FPEXC, r1 @ Restore FPEXC last
+ sub r2, r2, #4 @ Retry current instruction - if Thumb
+ str r2, [sp, #S_PC] @ mode it's two 16-bit instructions,
+ @ else it's one 32-bit instruction, so
+ @ always subtract 4 from the following
+ @ instruction address.
#ifdef CONFIG_PREEMPT
get_thread_info r10
ldr r4, [r10, #TI_PREEMPT] @ get preempt count
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 586961929e96..c834b32af275 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -457,10 +457,16 @@ static int vfp_pm_suspend(void)
/* disable, just in case */
fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN);
+ } else if (vfp_current_hw_state[ti->cpu]) {
+#ifndef CONFIG_SMP
+ fmxr(FPEXC, fpexc | FPEXC_EN);
+ vfp_save_state(vfp_current_hw_state[ti->cpu], fpexc);
+ fmxr(FPEXC, fpexc);
+#endif
}
/* clear any information we had about last context state */
- memset(vfp_current_hw_state, 0, sizeof(vfp_current_hw_state));
+ vfp_current_hw_state[ti->cpu] = NULL;
return 0;
}
@@ -713,8 +719,10 @@ static int __init vfp_init(void)
if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100)
elf_hwcap |= HWCAP_NEON;
#endif
+#ifdef CONFIG_VFPv3
if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000)
elf_hwcap |= HWCAP_VFPv4;
+#endif
}
}
return 0;
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig
index f34861920634..c7092e6057c5 100644
--- a/arch/blackfin/Kconfig
+++ b/arch/blackfin/Kconfig
@@ -38,6 +38,7 @@ config BLACKFIN
select GENERIC_ATOMIC64
select GENERIC_IRQ_PROBE
select IRQ_PER_CPU if SMP
+ select USE_GENERIC_SMP_HELPERS if SMP
select HAVE_NMI_WATCHDOG if NMI_WATCHDOG
select GENERIC_SMP_IDLE_THREAD
select ARCH_USES_GETTIMEOFFSET if !GENERIC_CLOCKEVENTS
diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile
index d3d7e64ca96d..66cf00095b84 100644
--- a/arch/blackfin/Makefile
+++ b/arch/blackfin/Makefile
@@ -20,7 +20,6 @@ endif
KBUILD_AFLAGS += $(call cc-option,-mno-fdpic)
KBUILD_CFLAGS_MODULE += -mlong-calls
LDFLAGS += -m elf32bfin
-KALLSYMS += --symbol-prefix=_
KBUILD_DEFCONFIG := BF537-STAMP_defconfig
diff --git a/arch/blackfin/include/asm/smp.h b/arch/blackfin/include/asm/smp.h
index dc3d144b4bb5..9631598dcc5d 100644
--- a/arch/blackfin/include/asm/smp.h
+++ b/arch/blackfin/include/asm/smp.h
@@ -18,6 +18,8 @@
#define raw_smp_processor_id() blackfin_core_id()
extern void bfin_relocate_coreb_l1_mem(void);
+extern void arch_send_call_function_single_ipi(int cpu);
+extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
#if defined(CONFIG_SMP) && defined(CONFIG_ICACHE_FLUSH_L1)
asmlinkage void blackfin_icache_flush_range_l1(unsigned long *ptr);
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
index ada8f0fc71e4..fb96e607adcf 100644
--- a/arch/blackfin/kernel/setup.c
+++ b/arch/blackfin/kernel/setup.c
@@ -52,7 +52,6 @@ EXPORT_SYMBOL(reserved_mem_dcache_on);
#ifdef CONFIG_MTD_UCLINUX
extern struct map_info uclinux_ram_map;
unsigned long memory_mtd_end, memory_mtd_start, mtd_size;
-unsigned long _ebss;
EXPORT_SYMBOL(memory_mtd_end);
EXPORT_SYMBOL(memory_mtd_start);
EXPORT_SYMBOL(mtd_size);
diff --git a/arch/blackfin/mach-common/smp.c b/arch/blackfin/mach-common/smp.c
index 00bbe672b3b3..a40151306b77 100644
--- a/arch/blackfin/mach-common/smp.c
+++ b/arch/blackfin/mach-common/smp.c
@@ -48,10 +48,13 @@ unsigned long blackfin_iflush_l1_entry[NR_CPUS];
struct blackfin_initial_pda __cpuinitdata initial_pda_coreb;
-#define BFIN_IPI_TIMER 0
-#define BFIN_IPI_RESCHEDULE 1
-#define BFIN_IPI_CALL_FUNC 2
-#define BFIN_IPI_CPU_STOP 3
+enum ipi_message_type {
+ BFIN_IPI_TIMER,
+ BFIN_IPI_RESCHEDULE,
+ BFIN_IPI_CALL_FUNC,
+ BFIN_IPI_CALL_FUNC_SINGLE,
+ BFIN_IPI_CPU_STOP,
+};
struct blackfin_flush_data {
unsigned long start;
@@ -60,35 +63,20 @@ struct blackfin_flush_data {
void *secondary_stack;
-
-struct smp_call_struct {
- void (*func)(void *info);
- void *info;
- int wait;
- cpumask_t *waitmask;
-};
-
static struct blackfin_flush_data smp_flush_data;
static DEFINE_SPINLOCK(stop_lock);
-struct ipi_message {
- unsigned long type;
- struct smp_call_struct call_struct;
-};
-
/* A magic number - stress test shows this is safe for common cases */
#define BFIN_IPI_MSGQ_LEN 5
/* Simple FIFO buffer, overflow leads to panic */
-struct ipi_message_queue {
- spinlock_t lock;
+struct ipi_data {
unsigned long count;
- unsigned long head; /* head of the queue */
- struct ipi_message ipi_message[BFIN_IPI_MSGQ_LEN];
+ unsigned long bits;
};
-static DEFINE_PER_CPU(struct ipi_message_queue, ipi_msg_queue);
+static DEFINE_PER_CPU(struct ipi_data, bfin_ipi);
static void ipi_cpu_stop(unsigned int cpu)
{
@@ -129,28 +117,6 @@ static void ipi_flush_icache(void *info)
blackfin_icache_flush_range(fdata->start, fdata->end);
}
-static void ipi_call_function(unsigned int cpu, struct ipi_message *msg)
-{
- int wait;
- void (*func)(void *info);
- void *info;
- func = msg->call_struct.func;
- info = msg->call_struct.info;
- wait = msg->call_struct.wait;
- func(info);
- if (wait) {
-#ifdef __ARCH_SYNC_CORE_DCACHE
- /*
- * 'wait' usually means synchronization between CPUs.
- * Invalidate D cache in case shared data was changed
- * by func() to ensure cache coherence.
- */
- resync_core_dcache();
-#endif
- cpumask_clear_cpu(cpu, msg->call_struct.waitmask);
- }
-}
-
/* Use IRQ_SUPPLE_0 to request reschedule.
* When returning from interrupt to user space,
* there is chance to reschedule */
@@ -172,152 +138,95 @@ void ipi_timer(void)
static irqreturn_t ipi_handler_int1(int irq, void *dev_instance)
{
- struct ipi_message *msg;
- struct ipi_message_queue *msg_queue;
+ struct ipi_data *bfin_ipi_data;
unsigned int cpu = smp_processor_id();
- unsigned long flags;
+ unsigned long pending;
+ unsigned long msg;
platform_clear_ipi(cpu, IRQ_SUPPLE_1);
- msg_queue = &__get_cpu_var(ipi_msg_queue);
-
- spin_lock_irqsave(&msg_queue->lock, flags);
-
- while (msg_queue->count) {
- msg = &msg_queue->ipi_message[msg_queue->head];
- switch (msg->type) {
- case BFIN_IPI_TIMER:
- ipi_timer();
- break;
- case BFIN_IPI_RESCHEDULE:
- scheduler_ipi();
- break;
- case BFIN_IPI_CALL_FUNC:
- ipi_call_function(cpu, msg);
- break;
- case BFIN_IPI_CPU_STOP:
- ipi_cpu_stop(cpu);
- break;
- default:
- printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%lx\n",
- cpu, msg->type);
- break;
- }
- msg_queue->head++;
- msg_queue->head %= BFIN_IPI_MSGQ_LEN;
- msg_queue->count--;
+ bfin_ipi_data = &__get_cpu_var(bfin_ipi);
+
+ while ((pending = xchg(&bfin_ipi_data->bits, 0)) != 0) {
+ msg = 0;
+ do {
+ msg = find_next_bit(&pending, BITS_PER_LONG, msg + 1);
+ switch (msg) {
+ case BFIN_IPI_TIMER:
+ ipi_timer();
+ break;
+ case BFIN_IPI_RESCHEDULE:
+ scheduler_ipi();
+ break;
+ case BFIN_IPI_CALL_FUNC:
+ generic_smp_call_function_interrupt();
+ break;
+
+ case BFIN_IPI_CALL_FUNC_SINGLE:
+ generic_smp_call_function_single_interrupt();
+ break;
+
+ case BFIN_IPI_CPU_STOP:
+ ipi_cpu_stop(cpu);
+ break;
+ }
+ } while (msg < BITS_PER_LONG);
+
+ smp_mb();
}
- spin_unlock_irqrestore(&msg_queue->lock, flags);
return IRQ_HANDLED;
}
-static void ipi_queue_init(void)
+static void bfin_ipi_init(void)
{
unsigned int cpu;
- struct ipi_message_queue *msg_queue;
+ struct ipi_data *bfin_ipi_data;
for_each_possible_cpu(cpu) {
- msg_queue = &per_cpu(ipi_msg_queue, cpu);
- spin_lock_init(&msg_queue->lock);
- msg_queue->count = 0;
- msg_queue->head = 0;
+ bfin_ipi_data = &per_cpu(bfin_ipi, cpu);
+ bfin_ipi_data->bits = 0;
+ bfin_ipi_data->count = 0;
}
}
-static inline void smp_send_message(cpumask_t callmap, unsigned long type,
- void (*func) (void *info), void *info, int wait)
+void send_ipi(const struct cpumask *cpumask, enum ipi_message_type msg)
{
unsigned int cpu;
- struct ipi_message_queue *msg_queue;
- struct ipi_message *msg;
- unsigned long flags, next_msg;
- cpumask_t waitmask; /* waitmask is shared by all cpus */
-
- cpumask_copy(&waitmask, &callmap);
- for_each_cpu(cpu, &callmap) {
- msg_queue = &per_cpu(ipi_msg_queue, cpu);
- spin_lock_irqsave(&msg_queue->lock, flags);
- if (msg_queue->count < BFIN_IPI_MSGQ_LEN) {
- next_msg = (msg_queue->head + msg_queue->count)
- % BFIN_IPI_MSGQ_LEN;
- msg = &msg_queue->ipi_message[next_msg];
- msg->type = type;
- if (type == BFIN_IPI_CALL_FUNC) {
- msg->call_struct.func = func;
- msg->call_struct.info = info;
- msg->call_struct.wait = wait;
- msg->call_struct.waitmask = &waitmask;
- }
- msg_queue->count++;
- } else
- panic("IPI message queue overflow\n");
- spin_unlock_irqrestore(&msg_queue->lock, flags);
+ struct ipi_data *bfin_ipi_data;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ for_each_cpu(cpu, cpumask) {
+ bfin_ipi_data = &per_cpu(bfin_ipi, cpu);
+ smp_mb();
+ set_bit(msg, &bfin_ipi_data->bits);
+ bfin_ipi_data->count++;
platform_send_ipi_cpu(cpu, IRQ_SUPPLE_1);
}
- if (wait) {
- while (!cpumask_empty(&waitmask))
- blackfin_dcache_invalidate_range(
- (unsigned long)(&waitmask),
- (unsigned long)(&waitmask));
-#ifdef __ARCH_SYNC_CORE_DCACHE
- /*
- * Invalidate D cache in case shared data was changed by
- * other processors to ensure cache coherence.
- */
- resync_core_dcache();
-#endif
- }
+ local_irq_restore(flags);
}
-int smp_call_function(void (*func)(void *info), void *info, int wait)
+void arch_send_call_function_single_ipi(int cpu)
{
- cpumask_t callmap;
-
- preempt_disable();
- cpumask_copy(&callmap, cpu_online_mask);
- cpumask_clear_cpu(smp_processor_id(), &callmap);
- if (!cpumask_empty(&callmap))
- smp_send_message(callmap, BFIN_IPI_CALL_FUNC, func, info, wait);
-
- preempt_enable();
-
- return 0;
+ send_ipi(cpumask_of(cpu), BFIN_IPI_CALL_FUNC_SINGLE);
}
-EXPORT_SYMBOL_GPL(smp_call_function);
-int smp_call_function_single(int cpuid, void (*func) (void *info), void *info,
- int wait)
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
{
- unsigned int cpu = cpuid;
- cpumask_t callmap;
-
- if (cpu_is_offline(cpu))
- return 0;
- cpumask_clear(&callmap);
- cpumask_set_cpu(cpu, &callmap);
-
- smp_send_message(callmap, BFIN_IPI_CALL_FUNC, func, info, wait);
-
- return 0;
+ send_ipi(mask, BFIN_IPI_CALL_FUNC);
}
-EXPORT_SYMBOL_GPL(smp_call_function_single);
void smp_send_reschedule(int cpu)
{
- cpumask_t callmap;
- /* simply trigger an ipi */
-
- cpumask_clear(&callmap);
- cpumask_set_cpu(cpu, &callmap);
-
- smp_send_message(callmap, BFIN_IPI_RESCHEDULE, NULL, NULL, 0);
+ send_ipi(cpumask_of(cpu), BFIN_IPI_RESCHEDULE);
return;
}
void smp_send_msg(const struct cpumask *mask, unsigned long type)
{
- smp_send_message(*mask, type, NULL, NULL, 0);
+ send_ipi(mask, type);
}
void smp_timer_broadcast(const struct cpumask *mask)
@@ -333,7 +242,7 @@ void smp_send_stop(void)
cpumask_copy(&callmap, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &callmap);
if (!cpumask_empty(&callmap))
- smp_send_message(callmap, BFIN_IPI_CPU_STOP, NULL, NULL, 0);
+ send_ipi(&callmap, BFIN_IPI_CPU_STOP);
preempt_enable();
@@ -436,7 +345,7 @@ void __init smp_prepare_boot_cpu(void)
void __init smp_prepare_cpus(unsigned int max_cpus)
{
platform_prepare_cpus(max_cpus);
- ipi_queue_init();
+ bfin_ipi_init();
platform_request_ipi(IRQ_SUPPLE_0, ipi_handler_int0);
platform_request_ipi(IRQ_SUPPLE_1, ipi_handler_int1);
}
diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig
index 052f81a76239..983c859e40b7 100644
--- a/arch/c6x/Kconfig
+++ b/arch/c6x/Kconfig
@@ -6,6 +6,7 @@
config C6X
def_bool y
select CLKDEV_LOOKUP
+ select GENERIC_ATOMIC64
select GENERIC_IRQ_SHOW
select HAVE_ARCH_TRACEHOOK
select HAVE_DMA_API_DEBUG
diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild
index 3af601e31e66..f08e89183cda 100644
--- a/arch/c6x/include/asm/Kbuild
+++ b/arch/c6x/include/asm/Kbuild
@@ -2,6 +2,7 @@ include include/asm-generic/Kbuild.asm
generic-y += atomic.h
generic-y += auxvec.h
+generic-y += barrier.h
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += cputime.h
diff --git a/arch/c6x/include/asm/barrier.h b/arch/c6x/include/asm/barrier.h
deleted file mode 100644
index 538240e85909..000000000000
--- a/arch/c6x/include/asm/barrier.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Port on Texas Instruments TMS320C6x architecture
- *
- * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
- * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.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.
- */
-#ifndef _ASM_C6X_BARRIER_H
-#define _ASM_C6X_BARRIER_H
-
-#define nop() asm("NOP\n");
-
-#define mb() barrier()
-#define rmb() barrier()
-#define wmb() barrier()
-#define set_mb(var, value) do { var = value; mb(); } while (0)
-#define set_wmb(var, value) do { var = value; wmb(); } while (0)
-
-#define smp_mb() barrier()
-#define smp_rmb() barrier()
-#define smp_wmb() barrier()
-#define smp_read_barrier_depends() do { } while (0)
-
-#endif /* _ASM_C6X_BARRIER_H */
diff --git a/arch/c6x/include/asm/cache.h b/arch/c6x/include/asm/cache.h
index 6d521d96d941..09c5a0f5f4d1 100644
--- a/arch/c6x/include/asm/cache.h
+++ b/arch/c6x/include/asm/cache.h
@@ -1,7 +1,7 @@
/*
* Port on Texas Instruments TMS320C6x architecture
*
- * Copyright (C) 2005, 2006, 2009, 2010 Texas Instruments Incorporated
+ * Copyright (C) 2005, 2006, 2009, 2010, 2012 Texas Instruments Incorporated
* Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
*
* This program is free software; you can redistribute it and/or modify
@@ -16,9 +16,14 @@
/*
* Cache line size
*/
-#define L1D_CACHE_BYTES 64
-#define L1P_CACHE_BYTES 32
-#define L2_CACHE_BYTES 128
+#define L1D_CACHE_SHIFT 6
+#define L1D_CACHE_BYTES (1 << L1D_CACHE_SHIFT)
+
+#define L1P_CACHE_SHIFT 5
+#define L1P_CACHE_BYTES (1 << L1P_CACHE_SHIFT)
+
+#define L2_CACHE_SHIFT 7
+#define L2_CACHE_BYTES (1 << L2_CACHE_SHIFT)
/*
* L2 used as cache
@@ -29,7 +34,8 @@
* For practical reasons the L1_CACHE_BYTES defines should not be smaller than
* the L2 line size
*/
-#define L1_CACHE_BYTES L2_CACHE_BYTES
+#define L1_CACHE_SHIFT L2_CACHE_SHIFT
+#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
#define L2_CACHE_ALIGN_LOW(x) \
(((x) & ~(L2_CACHE_BYTES - 1)))
diff --git a/arch/ia64/configs/generic_defconfig b/arch/ia64/configs/generic_defconfig
index 954d81e2e837..7913695b2fcb 100644
--- a/arch/ia64/configs/generic_defconfig
+++ b/arch/ia64/configs/generic_defconfig
@@ -234,5 +234,4 @@ CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_MD5=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_T10DIF=y
-CONFIG_MISC_DEVICES=y
CONFIG_INTEL_IOMMU=y
diff --git a/arch/ia64/configs/gensparse_defconfig b/arch/ia64/configs/gensparse_defconfig
index 91c41ecfa6d9..f8e913365423 100644
--- a/arch/ia64/configs/gensparse_defconfig
+++ b/arch/ia64/configs/gensparse_defconfig
@@ -209,4 +209,3 @@ CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_CRYPTO_MD5=y
-CONFIG_MISC_DEVICES=y
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 6f38b6120d96..440578850ae5 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -497,7 +497,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
srat_num_cpus++;
}
-void __init
+int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
unsigned long paddr, size;
@@ -512,7 +512,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
/* Ignore disabled entries */
if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
- return;
+ return -1;
/* record this node in proximity bitmap */
pxm_bit_set(pxm);
@@ -531,6 +531,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
p->size = size;
p->nid = pxm;
num_node_memblks++;
+ return 0;
}
void __init acpi_numa_arch_fixup(void)
diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c
index 5c3e0888265a..1034884b77da 100644
--- a/arch/ia64/kernel/irq_ia64.c
+++ b/arch/ia64/kernel/irq_ia64.c
@@ -23,7 +23,6 @@
#include <linux/ioport.h>
#include <linux/kernel_stat.h>
#include <linux/ptrace.h>
-#include <linux/random.h> /* for rand_initialize_irq() */
#include <linux/signal.h>
#include <linux/smp.h>
#include <linux/threads.h>
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index d7f558c1e711..3fa4bc536953 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -2353,7 +2353,6 @@ pfm_smpl_buffer_alloc(struct task_struct *task, struct file *filp, pfm_context_t
*/
insert_vm_struct(mm, vma);
- mm->total_vm += size >> PAGE_SHIFT;
vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file,
vma_pages(vma));
up_write(&task->mm->mmap_sem);
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 0b0f8b8c4a26..b22df9410dce 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -5,6 +5,7 @@ config M68K
select HAVE_AOUT if MMU
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_SHOW
+ select GENERIC_ATOMIC64
select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
select GENERIC_CPU_DEVICES
select GENERIC_STRNCPY_FROM_USER if MMU
@@ -54,18 +55,6 @@ config ZONE_DMA
bool
default y
-config CPU_HAS_NO_BITFIELDS
- bool
-
-config CPU_HAS_NO_MULDIV64
- bool
-
-config CPU_HAS_ADDRESS_SPACES
- bool
-
-config FPU
- bool
-
config HZ
int
default 1000 if CLEOPATRA
diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu
index 43a9f8f1b8eb..c4eb79edecec 100644
--- a/arch/m68k/Kconfig.cpu
+++ b/arch/m68k/Kconfig.cpu
@@ -28,6 +28,7 @@ config COLDFIRE
select CPU_HAS_NO_BITFIELDS
select CPU_HAS_NO_MULDIV64
select GENERIC_CSUM
+ select HAVE_CLK
endchoice
@@ -37,6 +38,7 @@ config M68000
bool
select CPU_HAS_NO_BITFIELDS
select CPU_HAS_NO_MULDIV64
+ select CPU_HAS_NO_UNALIGNED
select GENERIC_CSUM
help
The Freescale (was Motorola) 68000 CPU is the first generation of
@@ -48,6 +50,7 @@ config M68000
config MCPU32
bool
select CPU_HAS_NO_BITFIELDS
+ select CPU_HAS_NO_UNALIGNED
help
The Freescale (was then Motorola) CPU32 is a CPU core that is
based on the 68020 processor. For the most part it is used in
@@ -56,7 +59,6 @@ config MCPU32
config M68020
bool "68020 support"
depends on MMU
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68020
@@ -67,7 +69,6 @@ config M68020
config M68030
bool "68030 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68030
@@ -77,7 +78,6 @@ config M68030
config M68040
bool "68040 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68LC040
@@ -88,7 +88,6 @@ config M68040
config M68060
bool "68060 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68060
@@ -376,6 +375,18 @@ config NODES_SHIFT
default "3"
depends on !SINGLE_MEMORY_CHUNK
+config CPU_HAS_NO_BITFIELDS
+ bool
+
+config CPU_HAS_NO_MULDIV64
+ bool
+
+config CPU_HAS_NO_UNALIGNED
+ bool
+
+config CPU_HAS_ADDRESS_SPACES
+ bool
+
config FPU
bool
diff --git a/arch/m68k/apollo/config.c b/arch/m68k/apollo/config.c
index 0a30406b9442..f5565d6eeb8e 100644
--- a/arch/m68k/apollo/config.c
+++ b/arch/m68k/apollo/config.c
@@ -177,8 +177,8 @@ irqreturn_t dn_timer_int(int irq, void *dev_id)
timer_handler(irq, dev_id);
- x=*(volatile unsigned char *)(timer+3);
- x=*(volatile unsigned char *)(timer+5);
+ x = *(volatile unsigned char *)(apollo_timer + 3);
+ x = *(volatile unsigned char *)(apollo_timer + 5);
return IRQ_HANDLED;
}
@@ -186,17 +186,17 @@ irqreturn_t dn_timer_int(int irq, void *dev_id)
void dn_sched_init(irq_handler_t timer_routine)
{
/* program timer 1 */
- *(volatile unsigned char *)(timer+3)=0x01;
- *(volatile unsigned char *)(timer+1)=0x40;
- *(volatile unsigned char *)(timer+5)=0x09;
- *(volatile unsigned char *)(timer+7)=0xc4;
+ *(volatile unsigned char *)(apollo_timer + 3) = 0x01;
+ *(volatile unsigned char *)(apollo_timer + 1) = 0x40;
+ *(volatile unsigned char *)(apollo_timer + 5) = 0x09;
+ *(volatile unsigned char *)(apollo_timer + 7) = 0xc4;
/* enable IRQ of PIC B */
*(volatile unsigned char *)(pica+1)&=(~8);
#if 0
- printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
- printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
+ printk("*(0x10803) %02x\n",*(volatile unsigned char *)(apollo_timer + 0x3));
+ printk("*(0x10803) %02x\n",*(volatile unsigned char *)(apollo_timer + 0x3));
#endif
if (request_irq(IRQ_APOLLO, dn_timer_int, 0, "time", timer_routine))
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index eafa2539a8ee..a74e5d95c384 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -1,4 +1,29 @@
include include/asm-generic/Kbuild.asm
header-y += cachectl.h
+generic-y += bitsperlong.h
+generic-y += cputime.h
+generic-y += device.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += futex.h
+generic-y += ioctl.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += kvm_para.h
+generic-y += local64.h
+generic-y += local.h
+generic-y += mman.h
+generic-y += mutex.h
+generic-y += percpu.h
+generic-y += resource.h
+generic-y += scatterlist.h
+generic-y += sections.h
+generic-y += siginfo.h
+generic-y += statfs.h
+generic-y += topology.h
+generic-y += types.h
generic-y += word-at-a-time.h
+generic-y += xor.h
diff --git a/arch/m68k/include/asm/MC68332.h b/arch/m68k/include/asm/MC68332.h
deleted file mode 100644
index 6bb8f02685a2..000000000000
--- a/arch/m68k/include/asm/MC68332.h
+++ /dev/null
@@ -1,152 +0,0 @@
-
-/* include/asm-m68knommu/MC68332.h: '332 control registers
- *
- * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
- *
- */
-
-#ifndef _MC68332_H_
-#define _MC68332_H_
-
-#define BYTE_REF(addr) (*((volatile unsigned char*)addr))
-#define WORD_REF(addr) (*((volatile unsigned short*)addr))
-
-#define PORTE_ADDR 0xfffa11
-#define PORTE BYTE_REF(PORTE_ADDR)
-#define DDRE_ADDR 0xfffa15
-#define DDRE BYTE_REF(DDRE_ADDR)
-#define PEPAR_ADDR 0xfffa17
-#define PEPAR BYTE_REF(PEPAR_ADDR)
-
-#define PORTF_ADDR 0xfffa19
-#define PORTF BYTE_REF(PORTF_ADDR)
-#define DDRF_ADDR 0xfffa1d
-#define DDRF BYTE_REF(DDRF_ADDR)
-#define PFPAR_ADDR 0xfffa1f
-#define PFPAR BYTE_REF(PFPAR_ADDR)
-
-#define PORTQS_ADDR 0xfffc15
-#define PORTQS BYTE_REF(PORTQS_ADDR)
-#define DDRQS_ADDR 0xfffc17
-#define DDRQS BYTE_REF(DDRQS_ADDR)
-#define PQSPAR_ADDR 0xfffc16
-#define PQSPAR BYTE_REF(PQSPAR_ADDR)
-
-#define CSPAR0_ADDR 0xFFFA44
-#define CSPAR0 WORD_REF(CSPAR0_ADDR)
-#define CSPAR1_ADDR 0xFFFA46
-#define CSPAR1 WORD_REF(CSPAR1_ADDR)
-#define CSARBT_ADDR 0xFFFA48
-#define CSARBT WORD_REF(CSARBT_ADDR)
-#define CSOPBT_ADDR 0xFFFA4A
-#define CSOPBT WORD_REF(CSOPBT_ADDR)
-#define CSBAR0_ADDR 0xFFFA4C
-#define CSBAR0 WORD_REF(CSBAR0_ADDR)
-#define CSOR0_ADDR 0xFFFA4E
-#define CSOR0 WORD_REF(CSOR0_ADDR)
-#define CSBAR1_ADDR 0xFFFA50
-#define CSBAR1 WORD_REF(CSBAR1_ADDR)
-#define CSOR1_ADDR 0xFFFA52
-#define CSOR1 WORD_REF(CSOR1_ADDR)
-#define CSBAR2_ADDR 0xFFFA54
-#define CSBAR2 WORD_REF(CSBAR2_ADDR)
-#define CSOR2_ADDR 0xFFFA56
-#define CSOR2 WORD_REF(CSOR2_ADDR)
-#define CSBAR3_ADDR 0xFFFA58
-#define CSBAR3 WORD_REF(CSBAR3_ADDR)
-#define CSOR3_ADDR 0xFFFA5A
-#define CSOR3 WORD_REF(CSOR3_ADDR)
-#define CSBAR4_ADDR 0xFFFA5C
-#define CSBAR4 WORD_REF(CSBAR4_ADDR)
-#define CSOR4_ADDR 0xFFFA5E
-#define CSOR4 WORD_REF(CSOR4_ADDR)
-#define CSBAR5_ADDR 0xFFFA60
-#define CSBAR5 WORD_REF(CSBAR5_ADDR)
-#define CSOR5_ADDR 0xFFFA62
-#define CSOR5 WORD_REF(CSOR5_ADDR)
-#define CSBAR6_ADDR 0xFFFA64
-#define CSBAR6 WORD_REF(CSBAR6_ADDR)
-#define CSOR6_ADDR 0xFFFA66
-#define CSOR6 WORD_REF(CSOR6_ADDR)
-#define CSBAR7_ADDR 0xFFFA68
-#define CSBAR7 WORD_REF(CSBAR7_ADDR)
-#define CSOR7_ADDR 0xFFFA6A
-#define CSOR7 WORD_REF(CSOR7_ADDR)
-#define CSBAR8_ADDR 0xFFFA6C
-#define CSBAR8 WORD_REF(CSBAR8_ADDR)
-#define CSOR8_ADDR 0xFFFA6E
-#define CSOR8 WORD_REF(CSOR8_ADDR)
-#define CSBAR9_ADDR 0xFFFA70
-#define CSBAR9 WORD_REF(CSBAR9_ADDR)
-#define CSOR9_ADDR 0xFFFA72
-#define CSOR9 WORD_REF(CSOR9_ADDR)
-#define CSBAR10_ADDR 0xFFFA74
-#define CSBAR10 WORD_REF(CSBAR10_ADDR)
-#define CSOR10_ADDR 0xFFFA76
-#define CSOR10 WORD_REF(CSOR10_ADDR)
-
-#define CSOR_MODE_ASYNC 0x0000
-#define CSOR_MODE_SYNC 0x8000
-#define CSOR_MODE_MASK 0x8000
-#define CSOR_BYTE_DISABLE 0x0000
-#define CSOR_BYTE_UPPER 0x4000
-#define CSOR_BYTE_LOWER 0x2000
-#define CSOR_BYTE_BOTH 0x6000
-#define CSOR_BYTE_MASK 0x6000
-#define CSOR_RW_RSVD 0x0000
-#define CSOR_RW_READ 0x0800
-#define CSOR_RW_WRITE 0x1000
-#define CSOR_RW_BOTH 0x1800
-#define CSOR_RW_MASK 0x1800
-#define CSOR_STROBE_DS 0x0400
-#define CSOR_STROBE_AS 0x0000
-#define CSOR_STROBE_MASK 0x0400
-#define CSOR_DSACK_WAIT(x) (wait << 6)
-#define CSOR_DSACK_FTERM (14 << 6)
-#define CSOR_DSACK_EXTERNAL (15 << 6)
-#define CSOR_DSACK_MASK 0x03c0
-#define CSOR_SPACE_CPU 0x0000
-#define CSOR_SPACE_USER 0x0010
-#define CSOR_SPACE_SU 0x0020
-#define CSOR_SPACE_BOTH 0x0030
-#define CSOR_SPACE_MASK 0x0030
-#define CSOR_IPL_ALL 0x0000
-#define CSOR_IPL_PRIORITY(x) (x << 1)
-#define CSOR_IPL_MASK 0x000e
-#define CSOR_AVEC_ON 0x0001
-#define CSOR_AVEC_OFF 0x0000
-#define CSOR_AVEC_MASK 0x0001
-
-#define CSBAR_ADDR(x) ((addr >> 11) << 3)
-#define CSBAR_ADDR_MASK 0xfff8
-#define CSBAR_BLKSIZE_2K 0x0000
-#define CSBAR_BLKSIZE_8K 0x0001
-#define CSBAR_BLKSIZE_16K 0x0002
-#define CSBAR_BLKSIZE_64K 0x0003
-#define CSBAR_BLKSIZE_128K 0x0004
-#define CSBAR_BLKSIZE_256K 0x0005
-#define CSBAR_BLKSIZE_512K 0x0006
-#define CSBAR_BLKSIZE_1M 0x0007
-#define CSBAR_BLKSIZE_MASK 0x0007
-
-#define CSPAR_DISC 0
-#define CSPAR_ALT 1
-#define CSPAR_CS8 2
-#define CSPAR_CS16 3
-#define CSPAR_MASK 3
-
-#define CSPAR0_CSBOOT(x) (x << 0)
-#define CSPAR0_CS0(x) (x << 2)
-#define CSPAR0_CS1(x) (x << 4)
-#define CSPAR0_CS2(x) (x << 6)
-#define CSPAR0_CS3(x) (x << 8)
-#define CSPAR0_CS4(x) (x << 10)
-#define CSPAR0_CS5(x) (x << 12)
-
-#define CSPAR1_CS6(x) (x << 0)
-#define CSPAR1_CS7(x) (x << 2)
-#define CSPAR1_CS8(x) (x << 4)
-#define CSPAR1_CS9(x) (x << 6)
-#define CSPAR1_CS10(x) (x << 8)
-
-#endif
diff --git a/arch/m68k/include/asm/apollodma.h b/arch/m68k/include/asm/apollodma.h
deleted file mode 100644
index 954adc851adb..000000000000
--- a/arch/m68k/include/asm/apollodma.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * linux/include/asm/dma.h: Defines for using and allocating dma channels.
- * Written by Hennus Bergman, 1992.
- * High DMA channel support & info by Hannu Savolainen
- * and John Boyd, Nov. 1992.
- */
-
-#ifndef _ASM_APOLLO_DMA_H
-#define _ASM_APOLLO_DMA_H
-
-#include <asm/apollohw.h> /* need byte IO */
-#include <linux/spinlock.h> /* And spinlocks */
-#include <linux/delay.h>
-
-
-#define dma_outb(val,addr) (*((volatile unsigned char *)(addr+IO_BASE)) = (val))
-#define dma_inb(addr) (*((volatile unsigned char *)(addr+IO_BASE)))
-
-/*
- * NOTES about DMA transfers:
- *
- * controller 1: channels 0-3, byte operations, ports 00-1F
- * controller 2: channels 4-7, word operations, ports C0-DF
- *
- * - ALL registers are 8 bits only, regardless of transfer size
- * - channel 4 is not used - cascades 1 into 2.
- * - channels 0-3 are byte - addresses/counts are for physical bytes
- * - channels 5-7 are word - addresses/counts are for physical words
- * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
- * - transfer count loaded to registers is 1 less than actual count
- * - controller 2 offsets are all even (2x offsets for controller 1)
- * - page registers for 5-7 don't use data bit 0, represent 128K pages
- * - page registers for 0-3 use bit 0, represent 64K pages
- *
- * DMA transfers are limited to the lower 16MB of _physical_ memory.
- * Note that addresses loaded into registers must be _physical_ addresses,
- * not logical addresses (which may differ if paging is active).
- *
- * Address mapping for channels 0-3:
- *
- * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses)
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * P7 ... P0 A7 ... A0 A7 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Address mapping for channels 5-7:
- *
- * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses)
- * | ... | \ \ ... \ \ \ ... \ \
- * | ... | \ \ ... \ \ \ ... \ (not used)
- * | ... | \ \ ... \ \ \ ... \
- * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
- * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
- * the hardware level, so odd-byte transfers aren't possible).
- *
- * Transfer count (_not # bytes_) is limited to 64K, represented as actual
- * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more,
- * and up to 128K bytes may be transferred on channels 5-7 in one operation.
- *
- */
-
-#define MAX_DMA_CHANNELS 8
-
-/* The maximum address that we can perform a DMA transfer to on this platform */#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x1000000)
-
-/* 8237 DMA controllers */
-#define IO_DMA1_BASE 0x10C00 /* 8 bit slave DMA, channels 0..3 */
-#define IO_DMA2_BASE 0x10D00 /* 16 bit master DMA, ch 4(=slave input)..7 */
-
-/* DMA controller registers */
-#define DMA1_CMD_REG (IO_DMA1_BASE+0x08) /* command register (w) */
-#define DMA1_STAT_REG (IO_DMA1_BASE+0x08) /* status register (r) */
-#define DMA1_REQ_REG (IO_DMA1_BASE+0x09) /* request register (w) */
-#define DMA1_MASK_REG (IO_DMA1_BASE+0x0A) /* single-channel mask (w) */
-#define DMA1_MODE_REG (IO_DMA1_BASE+0x0B) /* mode register (w) */
-#define DMA1_CLEAR_FF_REG (IO_DMA1_BASE+0x0C) /* clear pointer flip-flop (w) */
-#define DMA1_TEMP_REG (IO_DMA1_BASE+0x0D) /* Temporary Register (r) */
-#define DMA1_RESET_REG (IO_DMA1_BASE+0x0D) /* Master Clear (w) */
-#define DMA1_CLR_MASK_REG (IO_DMA1_BASE+0x0E) /* Clear Mask */
-#define DMA1_MASK_ALL_REG (IO_DMA1_BASE+0x0F) /* all-channels mask (w) */
-
-#define DMA2_CMD_REG (IO_DMA2_BASE+0x10) /* command register (w) */
-#define DMA2_STAT_REG (IO_DMA2_BASE+0x10) /* status register (r) */
-#define DMA2_REQ_REG (IO_DMA2_BASE+0x12) /* request register (w) */
-#define DMA2_MASK_REG (IO_DMA2_BASE+0x14) /* single-channel mask (w) */
-#define DMA2_MODE_REG (IO_DMA2_BASE+0x16) /* mode register (w) */
-#define DMA2_CLEAR_FF_REG (IO_DMA2_BASE+0x18) /* clear pointer flip-flop (w) */
-#define DMA2_TEMP_REG (IO_DMA2_BASE+0x1A) /* Temporary Register (r) */
-#define DMA2_RESET_REG (IO_DMA2_BASE+0x1A) /* Master Clear (w) */
-#define DMA2_CLR_MASK_REG (IO_DMA2_BASE+0x1C) /* Clear Mask */
-#define DMA2_MASK_ALL_REG (IO_DMA2_BASE+0x1E) /* all-channels mask (w) */
-
-#define DMA_ADDR_0 (IO_DMA1_BASE+0x00) /* DMA address registers */
-#define DMA_ADDR_1 (IO_DMA1_BASE+0x02)
-#define DMA_ADDR_2 (IO_DMA1_BASE+0x04)
-#define DMA_ADDR_3 (IO_DMA1_BASE+0x06)
-#define DMA_ADDR_4 (IO_DMA2_BASE+0x00)
-#define DMA_ADDR_5 (IO_DMA2_BASE+0x04)
-#define DMA_ADDR_6 (IO_DMA2_BASE+0x08)
-#define DMA_ADDR_7 (IO_DMA2_BASE+0x0C)
-
-#define DMA_CNT_0 (IO_DMA1_BASE+0x01) /* DMA count registers */
-#define DMA_CNT_1 (IO_DMA1_BASE+0x03)
-#define DMA_CNT_2 (IO_DMA1_BASE+0x05)
-#define DMA_CNT_3 (IO_DMA1_BASE+0x07)
-#define DMA_CNT_4 (IO_DMA2_BASE+0x02)
-#define DMA_CNT_5 (IO_DMA2_BASE+0x06)
-#define DMA_CNT_6 (IO_DMA2_BASE+0x0A)
-#define DMA_CNT_7 (IO_DMA2_BASE+0x0E)
-
-#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
-#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
-#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */
-
-#define DMA_AUTOINIT 0x10
-
-#define DMA_8BIT 0
-#define DMA_16BIT 1
-#define DMA_BUSMASTER 2
-
-extern spinlock_t dma_spin_lock;
-
-static __inline__ unsigned long claim_dma_lock(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&dma_spin_lock, flags);
- return flags;
-}
-
-static __inline__ void release_dma_lock(unsigned long flags)
-{
- spin_unlock_irqrestore(&dma_spin_lock, flags);
-}
-
-/* enable/disable a specific DMA channel */
-static __inline__ void enable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(dmanr, DMA1_MASK_REG);
- else
- dma_outb(dmanr & 3, DMA2_MASK_REG);
-}
-
-static __inline__ void disable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(dmanr | 4, DMA1_MASK_REG);
- else
- dma_outb((dmanr & 3) | 4, DMA2_MASK_REG);
-}
-
-/* Clear the 'DMA Pointer Flip Flop'.
- * Write 0 for LSB/MSB, 1 for MSB/LSB access.
- * Use this once to initialize the FF to a known state.
- * After that, keep track of it. :-)
- * --- In order to do that, the DMA routines below should ---
- * --- only be used while holding the DMA lock ! ---
- */
-static __inline__ void clear_dma_ff(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(0, DMA1_CLEAR_FF_REG);
- else
- dma_outb(0, DMA2_CLEAR_FF_REG);
-}
-
-/* set mode (above) for a specific DMA channel */
-static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
-{
- if (dmanr<=3)
- dma_outb(mode | dmanr, DMA1_MODE_REG);
- else
- dma_outb(mode | (dmanr&3), DMA2_MODE_REG);
-}
-
-/* Set transfer address & page bits for specific DMA channel.
- * Assumes dma flipflop is clear.
- */
-static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
-{
- if (dmanr <= 3) {
- dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
- dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
- } else {
- dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- }
-}
-
-
-/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
- * a specific DMA channel.
- * You must ensure the parameters are valid.
- * NOTE: from a manual: "the number of transfers is one more
- * than the initial word count"! This is taken into account.
- * Assumes dma flip-flop is clear.
- * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
- */
-static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
-{
- count--;
- if (dmanr <= 3) {
- dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- } else {
- dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- }
-}
-
-
-/* Get DMA residue count. After a DMA transfer, this
- * should return zero. Reading this while a DMA transfer is
- * still in progress will return unpredictable results.
- * If called before the channel has been used, it may return 1.
- * Otherwise, it returns the number of _bytes_ left to transfer.
- *
- * Assumes DMA flip-flop is clear.
- */
-static __inline__ int get_dma_residue(unsigned int dmanr)
-{
- unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
- : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
-
- /* using short to get 16-bit wrap around */
- unsigned short count;
-
- count = 1 + dma_inb(io_port);
- count += dma_inb(io_port) << 8;
-
- return (dmanr<=3)? count : (count<<1);
-}
-
-
-/* These are in kernel/dma.c: */
-extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */
-extern void free_dma(unsigned int dmanr); /* release it again */
-
-/* These are in arch/m68k/apollo/dma.c: */
-extern unsigned short dma_map_page(unsigned long phys_addr,int count,int type);
-extern void dma_unmap_page(unsigned short dma_addr);
-
-#endif /* _ASM_APOLLO_DMA_H */
diff --git a/arch/m68k/include/asm/apollohw.h b/arch/m68k/include/asm/apollohw.h
index a1373b9aa281..635ef4f89010 100644
--- a/arch/m68k/include/asm/apollohw.h
+++ b/arch/m68k/include/asm/apollohw.h
@@ -98,7 +98,7 @@ extern u_long timer_physaddr;
#define cpuctrl (*(volatile unsigned int *)(IO_BASE + cpuctrl_physaddr))
#define pica (IO_BASE + pica_physaddr)
#define picb (IO_BASE + picb_physaddr)
-#define timer (IO_BASE + timer_physaddr)
+#define apollo_timer (IO_BASE + timer_physaddr)
#define addr_xlat_map ((unsigned short *)(IO_BASE + 0x17000))
#define isaIO2mem(x) (((((x) & 0x3f8) << 7) | (((x) & 0xfc00) >> 6) | ((x) & 0x7)) + 0x40000 + IO_BASE)
diff --git a/arch/m68k/include/asm/bitsperlong.h b/arch/m68k/include/asm/bitsperlong.h
deleted file mode 100644
index 6dc0bb0c13b2..000000000000
--- a/arch/m68k/include/asm/bitsperlong.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bitsperlong.h>
diff --git a/arch/m68k/include/asm/cputime.h b/arch/m68k/include/asm/cputime.h
deleted file mode 100644
index c79c5e892305..000000000000
--- a/arch/m68k/include/asm/cputime.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __M68K_CPUTIME_H
-#define __M68K_CPUTIME_H
-
-#include <asm-generic/cputime.h>
-
-#endif /* __M68K_CPUTIME_H */
diff --git a/arch/m68k/include/asm/delay.h b/arch/m68k/include/asm/delay.h
index 9c09becfd4c9..12d8fe4f1d30 100644
--- a/arch/m68k/include/asm/delay.h
+++ b/arch/m68k/include/asm/delay.h
@@ -43,7 +43,7 @@ static inline void __delay(unsigned long loops)
extern void __bad_udelay(void);
-#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
+#ifdef CONFIG_CPU_HAS_NO_MULDIV64
/*
* The simpler m68k and ColdFire processors do not have a 32*32->64
* multiply instruction. So we need to handle them a little differently.
diff --git a/arch/m68k/include/asm/device.h b/arch/m68k/include/asm/device.h
deleted file mode 100644
index d8f9872b0e2d..000000000000
--- a/arch/m68k/include/asm/device.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Arch specific extensions to struct device
- *
- * This file is released under the GPLv2
- */
-#include <asm-generic/device.h>
-
diff --git a/arch/m68k/include/asm/emergency-restart.h b/arch/m68k/include/asm/emergency-restart.h
deleted file mode 100644
index 108d8c48e42e..000000000000
--- a/arch/m68k/include/asm/emergency-restart.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_EMERGENCY_RESTART_H
-#define _ASM_EMERGENCY_RESTART_H
-
-#include <asm-generic/emergency-restart.h>
-
-#endif /* _ASM_EMERGENCY_RESTART_H */
diff --git a/arch/m68k/include/asm/errno.h b/arch/m68k/include/asm/errno.h
deleted file mode 100644
index 0d4e188d6ef6..000000000000
--- a/arch/m68k/include/asm/errno.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_ERRNO_H
-#define _M68K_ERRNO_H
-
-#include <asm-generic/errno.h>
-
-#endif /* _M68K_ERRNO_H */
diff --git a/arch/m68k/include/asm/futex.h b/arch/m68k/include/asm/futex.h
deleted file mode 100644
index 6a332a9f099c..000000000000
--- a/arch/m68k/include/asm/futex.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_FUTEX_H
-#define _ASM_FUTEX_H
-
-#include <asm-generic/futex.h>
-
-#endif
diff --git a/arch/m68k/include/asm/ioctl.h b/arch/m68k/include/asm/ioctl.h
deleted file mode 100644
index b279fe06dfe5..000000000000
--- a/arch/m68k/include/asm/ioctl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctl.h>
diff --git a/arch/m68k/include/asm/ipcbuf.h b/arch/m68k/include/asm/ipcbuf.h
deleted file mode 100644
index 84c7e51cb6d0..000000000000
--- a/arch/m68k/include/asm/ipcbuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipcbuf.h>
diff --git a/arch/m68k/include/asm/irq_regs.h b/arch/m68k/include/asm/irq_regs.h
deleted file mode 100644
index 3dd9c0b70270..000000000000
--- a/arch/m68k/include/asm/irq_regs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/m68k/include/asm/kdebug.h b/arch/m68k/include/asm/kdebug.h
deleted file mode 100644
index 6ece1b037665..000000000000
--- a/arch/m68k/include/asm/kdebug.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kdebug.h>
diff --git a/arch/m68k/include/asm/kmap_types.h b/arch/m68k/include/asm/kmap_types.h
deleted file mode 100644
index 3413cc1390ec..000000000000
--- a/arch/m68k/include/asm/kmap_types.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_M68K_KMAP_TYPES_H
-#define __ASM_M68K_KMAP_TYPES_H
-
-#include <asm-generic/kmap_types.h>
-
-#endif /* __ASM_M68K_KMAP_TYPES_H */
diff --git a/arch/m68k/include/asm/kvm_para.h b/arch/m68k/include/asm/kvm_para.h
deleted file mode 100644
index 14fab8f0b957..000000000000
--- a/arch/m68k/include/asm/kvm_para.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kvm_para.h>
diff --git a/arch/m68k/include/asm/local.h b/arch/m68k/include/asm/local.h
deleted file mode 100644
index 6c259263e1f0..000000000000
--- a/arch/m68k/include/asm/local.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_M68K_LOCAL_H
-#define _ASM_M68K_LOCAL_H
-
-#include <asm-generic/local.h>
-
-#endif /* _ASM_M68K_LOCAL_H */
diff --git a/arch/m68k/include/asm/local64.h b/arch/m68k/include/asm/local64.h
deleted file mode 100644
index 36c93b5cc239..000000000000
--- a/arch/m68k/include/asm/local64.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
diff --git a/arch/m68k/include/asm/mac_mouse.h b/arch/m68k/include/asm/mac_mouse.h
deleted file mode 100644
index 39a5c292eaee..000000000000
--- a/arch/m68k/include/asm/mac_mouse.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _ASM_MAC_MOUSE_H
-#define _ASM_MAC_MOUSE_H
-
-/*
- * linux/include/asm-m68k/mac_mouse.h
- * header file for Macintosh ADB mouse driver
- * 27-10-97 Michael Schmitz
- * copied from:
- * header file for Atari Mouse driver
- * by Robert de Vries (robert@and.nl) on 19Jul93
- */
-
-struct mouse_status {
- char buttons;
- short dx;
- short dy;
- int ready;
- int active;
- wait_queue_head_t wait;
- struct fasync_struct *fasyncptr;
-};
-
-#endif
diff --git a/arch/m68k/include/asm/mcfmbus.h b/arch/m68k/include/asm/mcfmbus.h
deleted file mode 100644
index 319899c47a2c..000000000000
--- a/arch/m68k/include/asm/mcfmbus.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************/
-
-/*
- * mcfmbus.h -- Coldfire MBUS support defines.
- *
- * (C) Copyright 1999, Martin Floeer (mfloeer@axcent.de)
- */
-
-/****************************************************************************/
-
-
-#ifndef mcfmbus_h
-#define mcfmbus_h
-
-
-#define MCFMBUS_BASE 0x280
-#define MCFMBUS_IRQ_VECTOR 0x19
-#define MCFMBUS_IRQ 0x1
-#define MCFMBUS_CLK 0x3f
-#define MCFMBUS_IRQ_LEVEL 0x07 /*IRQ Level 1*/
-#define MCFMBUS_ADDRESS 0x01
-
-
-/*
-* Define the 5307 MBUS register set addresses
-*/
-
-#define MCFMBUS_MADR 0x00
-#define MCFMBUS_MFDR 0x04
-#define MCFMBUS_MBCR 0x08
-#define MCFMBUS_MBSR 0x0C
-#define MCFMBUS_MBDR 0x10
-
-
-#define MCFMBUS_MADR_ADDR(a) (((a)&0x7F)<<0x01) /*Slave Address*/
-
-#define MCFMBUS_MFDR_MBC(a) ((a)&0x3F) /*M-Bus Clock*/
-
-/*
-* Define bit flags in Control Register
-*/
-
-#define MCFMBUS_MBCR_MEN (0x80) /* M-Bus Enable */
-#define MCFMBUS_MBCR_MIEN (0x40) /* M-Bus Interrupt Enable */
-#define MCFMBUS_MBCR_MSTA (0x20) /* Master/Slave Mode Select Bit */
-#define MCFMBUS_MBCR_MTX (0x10) /* Transmit/Rcv Mode Select Bit */
-#define MCFMBUS_MBCR_TXAK (0x08) /* Transmit Acknowledge Enable */
-#define MCFMBUS_MBCR_RSTA (0x04) /* Repeat Start */
-
-/*
-* Define bit flags in Status Register
-*/
-
-#define MCFMBUS_MBSR_MCF (0x80) /* Data Transfer Complete */
-#define MCFMBUS_MBSR_MAAS (0x40) /* Addressed as a Slave */
-#define MCFMBUS_MBSR_MBB (0x20) /* Bus Busy */
-#define MCFMBUS_MBSR_MAL (0x10) /* Arbitration Lost */
-#define MCFMBUS_MBSR_SRW (0x04) /* Slave Transmit */
-#define MCFMBUS_MBSR_MIF (0x02) /* M-Bus Interrupt */
-#define MCFMBUS_MBSR_RXAK (0x01) /* No Acknowledge Received */
-
-/*
-* Define bit flags in DATA I/O Register
-*/
-
-#define MCFMBUS_MBDR_READ (0x01) /* 1=read 0=write MBUS */
-
-#define MBUSIOCSCLOCK 1
-#define MBUSIOCGCLOCK 2
-#define MBUSIOCSADDR 3
-#define MBUSIOCGADDR 4
-#define MBUSIOCSSLADDR 5
-#define MBUSIOCGSLADDR 6
-#define MBUSIOCSSUBADDR 7
-#define MBUSIOCGSUBADDR 8
-
-#endif
diff --git a/arch/m68k/include/asm/mman.h b/arch/m68k/include/asm/mman.h
deleted file mode 100644
index 8eebf89f5ab1..000000000000
--- a/arch/m68k/include/asm/mman.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/mman.h>
diff --git a/arch/m68k/include/asm/mutex.h b/arch/m68k/include/asm/mutex.h
deleted file mode 100644
index 458c1f7fbc18..000000000000
--- a/arch/m68k/include/asm/mutex.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * Pull in the generic implementation for the mutex fastpath.
- *
- * TODO: implement optimized primitives instead, or leave the generic
- * implementation in place, or pick the atomic_xchg() based generic
- * implementation. (see asm-generic/mutex-xchg.h for details)
- */
-
-#include <asm-generic/mutex-dec.h>
diff --git a/arch/m68k/include/asm/percpu.h b/arch/m68k/include/asm/percpu.h
deleted file mode 100644
index 0859d048faf5..000000000000
--- a/arch/m68k/include/asm/percpu.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_M68K_PERCPU_H
-#define __ASM_M68K_PERCPU_H
-
-#include <asm-generic/percpu.h>
-
-#endif /* __ASM_M68K_PERCPU_H */
diff --git a/arch/m68k/include/asm/resource.h b/arch/m68k/include/asm/resource.h
deleted file mode 100644
index e7d35019f337..000000000000
--- a/arch/m68k/include/asm/resource.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_RESOURCE_H
-#define _M68K_RESOURCE_H
-
-#include <asm-generic/resource.h>
-
-#endif /* _M68K_RESOURCE_H */
diff --git a/arch/m68k/include/asm/sbus.h b/arch/m68k/include/asm/sbus.h
deleted file mode 100644
index bfe3ba147f2e..000000000000
--- a/arch/m68k/include/asm/sbus.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * some sbus structures and macros to make usage of sbus drivers possible
- */
-
-#ifndef __M68K_SBUS_H
-#define __M68K_SBUS_H
-
-struct sbus_dev {
- struct {
- unsigned int which_io;
- unsigned int phys_addr;
- } reg_addrs[1];
-};
-
-/* sbus IO functions stolen from include/asm-sparc/io.h for the serial driver */
-/* No SBUS on the Sun3, kludge -- sam */
-
-static inline void _sbus_writeb(unsigned char val, unsigned long addr)
-{
- *(volatile unsigned char *)addr = val;
-}
-
-static inline unsigned char _sbus_readb(unsigned long addr)
-{
- return *(volatile unsigned char *)addr;
-}
-
-static inline void _sbus_writel(unsigned long val, unsigned long addr)
-{
- *(volatile unsigned long *)addr = val;
-
-}
-
-extern inline unsigned long _sbus_readl(unsigned long addr)
-{
- return *(volatile unsigned long *)addr;
-}
-
-
-#define sbus_readb(a) _sbus_readb((unsigned long)a)
-#define sbus_writeb(v, a) _sbus_writeb(v, (unsigned long)a)
-#define sbus_readl(a) _sbus_readl((unsigned long)a)
-#define sbus_writel(v, a) _sbus_writel(v, (unsigned long)a)
-
-#endif
diff --git a/arch/m68k/include/asm/scatterlist.h b/arch/m68k/include/asm/scatterlist.h
deleted file mode 100644
index 312505452a1e..000000000000
--- a/arch/m68k/include/asm/scatterlist.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_SCATTERLIST_H
-#define _M68K_SCATTERLIST_H
-
-#include <asm-generic/scatterlist.h>
-
-#endif /* !(_M68K_SCATTERLIST_H) */
diff --git a/arch/m68k/include/asm/sections.h b/arch/m68k/include/asm/sections.h
deleted file mode 100644
index 5277e52715ec..000000000000
--- a/arch/m68k/include/asm/sections.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _ASM_M68K_SECTIONS_H
-#define _ASM_M68K_SECTIONS_H
-
-#include <asm-generic/sections.h>
-
-extern char _sbss[], _ebss[];
-
-#endif /* _ASM_M68K_SECTIONS_H */
diff --git a/arch/m68k/include/asm/shm.h b/arch/m68k/include/asm/shm.h
deleted file mode 100644
index fa56ec84a126..000000000000
--- a/arch/m68k/include/asm/shm.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef _M68K_SHM_H
-#define _M68K_SHM_H
-
-
-/* format of page table entries that correspond to shared memory pages
- currently out in swap space (see also mm/swap.c):
- bits 0-1 (PAGE_PRESENT) is = 0
- bits 8..2 (SWP_TYPE) are = SHM_SWP_TYPE
- bits 31..9 are used like this:
- bits 15..9 (SHM_ID) the id of the shared memory segment
- bits 30..16 (SHM_IDX) the index of the page within the shared memory segment
- (actually only bits 25..16 get used since SHMMAX is so low)
- bit 31 (SHM_READ_ONLY) flag whether the page belongs to a read-only attach
-*/
-/* on the m68k both bits 0 and 1 must be zero */
-/* format on the sun3 is similar, but bits 30, 31 are set to zero and all
- others are reduced by 2. --m */
-
-#ifndef CONFIG_SUN3
-#define SHM_ID_SHIFT 9
-#else
-#define SHM_ID_SHIFT 7
-#endif
-#define _SHM_ID_BITS 7
-#define SHM_ID_MASK ((1<<_SHM_ID_BITS)-1)
-
-#define SHM_IDX_SHIFT (SHM_ID_SHIFT+_SHM_ID_BITS)
-#define _SHM_IDX_BITS 15
-#define SHM_IDX_MASK ((1<<_SHM_IDX_BITS)-1)
-
-#endif /* _M68K_SHM_H */
diff --git a/arch/m68k/include/asm/siginfo.h b/arch/m68k/include/asm/siginfo.h
deleted file mode 100644
index 851d3d784b53..000000000000
--- a/arch/m68k/include/asm/siginfo.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_SIGINFO_H
-#define _M68K_SIGINFO_H
-
-#include <asm-generic/siginfo.h>
-
-#endif
diff --git a/arch/m68k/include/asm/statfs.h b/arch/m68k/include/asm/statfs.h
deleted file mode 100644
index 08d93f14e061..000000000000
--- a/arch/m68k/include/asm/statfs.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_STATFS_H
-#define _M68K_STATFS_H
-
-#include <asm-generic/statfs.h>
-
-#endif /* _M68K_STATFS_H */
diff --git a/arch/m68k/include/asm/topology.h b/arch/m68k/include/asm/topology.h
deleted file mode 100644
index ca173e9f26ff..000000000000
--- a/arch/m68k/include/asm/topology.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_M68K_TOPOLOGY_H
-#define _ASM_M68K_TOPOLOGY_H
-
-#include <asm-generic/topology.h>
-
-#endif /* _ASM_M68K_TOPOLOGY_H */
diff --git a/arch/m68k/include/asm/types.h b/arch/m68k/include/asm/types.h
deleted file mode 100644
index 89705adcbd52..000000000000
--- a/arch/m68k/include/asm/types.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _M68K_TYPES_H
-#define _M68K_TYPES_H
-
-/*
- * This file is never included by application software unless
- * explicitly requested (e.g., via linux/types.h) in which case the
- * application is Linux specific so (user-) name space pollution is
- * not a major issue. However, for interoperability, libraries still
- * need to be careful to avoid a name clashes.
- */
-#include <asm-generic/int-ll64.h>
-
-/*
- * These aren't exported outside the kernel to avoid name space clashes
- */
-#ifdef __KERNEL__
-
-#define BITS_PER_LONG 32
-
-#endif /* __KERNEL__ */
-
-#endif /* _M68K_TYPES_H */
diff --git a/arch/m68k/include/asm/unaligned.h b/arch/m68k/include/asm/unaligned.h
index f4043ae63db1..2b3ca0bf7a0d 100644
--- a/arch/m68k/include/asm/unaligned.h
+++ b/arch/m68k/include/asm/unaligned.h
@@ -2,7 +2,7 @@
#define _ASM_M68K_UNALIGNED_H
-#if defined(CONFIG_COLDFIRE) || defined(CONFIG_M68000)
+#ifdef CONFIG_CPU_HAS_NO_UNALIGNED
#include <linux/unaligned/be_struct.h>
#include <linux/unaligned/le_byteshift.h>
#include <linux/unaligned/generic.h>
@@ -12,7 +12,7 @@
#else
/*
- * The m68k can do unaligned accesses itself.
+ * The m68k can do unaligned accesses itself.
*/
#include <linux/unaligned/access_ok.h>
#include <linux/unaligned/generic.h>
diff --git a/arch/m68k/include/asm/xor.h b/arch/m68k/include/asm/xor.h
deleted file mode 100644
index c82eb12a5b18..000000000000
--- a/arch/m68k/include/asm/xor.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/xor.h>
diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c
index 7dc186b7a85f..71fb29938dba 100644
--- a/arch/m68k/kernel/setup_no.c
+++ b/arch/m68k/kernel/setup_no.c
@@ -218,13 +218,10 @@ void __init setup_arch(char **cmdline_p)
printk(KERN_INFO "Motorola M5235EVB support (C)2005 Syn-tech Systems, Inc. (Jate Sujjavanich)\n");
#endif
- pr_debug("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x "
- "BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext,
- (int) &_sdata, (int) &_edata,
- (int) &_sbss, (int) &_ebss);
- pr_debug("MEMORY -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x\n ",
- (int) &_ebss, (int) memory_start,
- (int) memory_start, (int) memory_end);
+ pr_debug("KERNEL -> TEXT=0x%p-0x%p DATA=0x%p-0x%p BSS=0x%p-0x%p\n",
+ _stext, _etext, _sdata, _edata, __bss_start, __bss_stop);
+ pr_debug("MEMORY -> ROMFS=0x%p-0x%06lx MEM=0x%06lx-0x%06lx\n ",
+ __bss_stop, memory_start, memory_start, memory_end);
/* Keep a copy of command line */
*cmdline_p = &command_line[0];
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index 8623f8dc16f8..9a5932ec3689 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -479,9 +479,13 @@ sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5,
goto bad_access;
}
- mem_value = *mem;
+ /*
+ * No need to check for EFAULT; we know that the page is
+ * present and writable.
+ */
+ __get_user(mem_value, mem);
if (mem_value == oldval)
- *mem = newval;
+ __put_user(newval, mem);
pte_unmap_unlock(pte, ptl);
up_read(&mm->mmap_sem);
diff --git a/arch/m68k/kernel/vmlinux-nommu.lds b/arch/m68k/kernel/vmlinux-nommu.lds
index 40e02d9c38b4..06a763f49fd3 100644
--- a/arch/m68k/kernel/vmlinux-nommu.lds
+++ b/arch/m68k/kernel/vmlinux-nommu.lds
@@ -78,9 +78,7 @@ SECTIONS {
__init_end = .;
}
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_end = .;
diff --git a/arch/m68k/kernel/vmlinux-std.lds b/arch/m68k/kernel/vmlinux-std.lds
index 63407c836826..d0993594f558 100644
--- a/arch/m68k/kernel/vmlinux-std.lds
+++ b/arch/m68k/kernel/vmlinux-std.lds
@@ -31,9 +31,7 @@ SECTIONS
RW_DATA_SECTION(16, PAGE_SIZE, THREAD_SIZE)
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_edata = .; /* End of data section */
diff --git a/arch/m68k/kernel/vmlinux-sun3.lds b/arch/m68k/kernel/vmlinux-sun3.lds
index ad0f46d64c0b..8080469ee6c1 100644
--- a/arch/m68k/kernel/vmlinux-sun3.lds
+++ b/arch/m68k/kernel/vmlinux-sun3.lds
@@ -44,9 +44,7 @@ __init_begin = .;
. = ALIGN(PAGE_SIZE);
__init_end = .;
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_end = . ;
diff --git a/arch/m68k/lib/muldi3.c b/arch/m68k/lib/muldi3.c
index 79e928a525d0..ee5f0b1b5c5d 100644
--- a/arch/m68k/lib/muldi3.c
+++ b/arch/m68k/lib/muldi3.c
@@ -19,7 +19,7 @@ along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
+#ifdef CONFIG_CPU_HAS_NO_MULDIV64
#define SI_TYPE_SIZE 32
#define __BITS4 (SI_TYPE_SIZE / 4)
diff --git a/arch/m68k/mm/init_mm.c b/arch/m68k/mm/init_mm.c
index f77f258dce3a..282f9de68966 100644
--- a/arch/m68k/mm/init_mm.c
+++ b/arch/m68k/mm/init_mm.c
@@ -104,7 +104,7 @@ void __init print_memmap(void)
MLK_ROUNDUP(__init_begin, __init_end),
MLK_ROUNDUP(_stext, _etext),
MLK_ROUNDUP(_sdata, _edata),
- MLK_ROUNDUP(_sbss, _ebss));
+ MLK_ROUNDUP(__bss_start, __bss_stop));
}
void __init mem_init(void)
diff --git a/arch/m68k/mm/init_no.c b/arch/m68k/mm/init_no.c
index 345ec0d83e3d..688e3664aea0 100644
--- a/arch/m68k/mm/init_no.c
+++ b/arch/m68k/mm/init_no.c
@@ -91,7 +91,7 @@ void __init mem_init(void)
totalram_pages = free_all_bootmem();
codek = (_etext - _stext) >> 10;
- datak = (_ebss - _sdata) >> 10;
+ datak = (__bss_stop - _sdata) >> 10;
initk = (__init_begin - __init_end) >> 10;
tmp = nr_free_pages() << PAGE_SHIFT;
diff --git a/arch/m68k/platform/68328/head-de2.S b/arch/m68k/platform/68328/head-de2.S
index f632fdcb93e9..537d3245b539 100644
--- a/arch/m68k/platform/68328/head-de2.S
+++ b/arch/m68k/platform/68328/head-de2.S
@@ -60,8 +60,8 @@ _start:
* Move ROM filesystem above bss :-)
*/
- moveal #_sbss, %a0 /* romfs at the start of bss */
- moveal #_ebss, %a1 /* Set up destination */
+ moveal #__bss_start, %a0 /* romfs at the start of bss */
+ moveal #__bss_stop, %a1 /* Set up destination */
movel %a0, %a2 /* Copy of bss start */
movel 8(%a0), %d1 /* Get size of ROMFS */
@@ -84,8 +84,8 @@ _start:
* Initialize BSS segment to 0
*/
- lea _sbss, %a0
- lea _ebss, %a1
+ lea __bss_start, %a0
+ lea __bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
2: cmpal %a0, %a1
diff --git a/arch/m68k/platform/68328/head-pilot.S b/arch/m68k/platform/68328/head-pilot.S
index 2ebfd6420818..45a9dad29e3d 100644
--- a/arch/m68k/platform/68328/head-pilot.S
+++ b/arch/m68k/platform/68328/head-pilot.S
@@ -110,7 +110,7 @@ L0:
movel #CONFIG_VECTORBASE, %d7
addl #16, %d7
moveal %d7, %a0
- moveal #_ebss, %a1
+ moveal #__bss_stop, %a1
lea %a1@(512), %a2
DBG_PUTC('C')
@@ -138,8 +138,8 @@ LD1:
DBG_PUTC('E')
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -150,7 +150,7 @@ L1:
DBG_PUTC('F')
/* Copy command line from end of bss to command line */
- moveal #_ebss, %a0
+ moveal #__bss_stop, %a0
moveal #command_line, %a1
lea %a1@(512), %a2
@@ -165,7 +165,7 @@ L3:
movel #_sdata, %d0
movel %d0, _rambase
- movel #_ebss, %d0
+ movel #__bss_stop, %d0
movel %d0, _ramstart
movel %a4, %d0
diff --git a/arch/m68k/platform/68328/head-ram.S b/arch/m68k/platform/68328/head-ram.S
index 7f1aeeacb219..5189ef926098 100644
--- a/arch/m68k/platform/68328/head-ram.S
+++ b/arch/m68k/platform/68328/head-ram.S
@@ -76,8 +76,8 @@ pclp3:
beq pclp3
#endif /* DEBUG */
moveal #0x007ffff0, %ssp
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 >= %a1 */
L1:
diff --git a/arch/m68k/platform/68328/head-rom.S b/arch/m68k/platform/68328/head-rom.S
index a5ff96d0295f..3dff98ba2e97 100644
--- a/arch/m68k/platform/68328/head-rom.S
+++ b/arch/m68k/platform/68328/head-rom.S
@@ -59,8 +59,8 @@ _stext: movew #0x2700,%sr
cmpal %a1, %a2
bhi 1b
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
1:
@@ -70,7 +70,7 @@ _stext: movew #0x2700,%sr
movel #_sdata, %d0
movel %d0, _rambase
- movel #_ebss, %d0
+ movel #__bss_stop, %d0
movel %d0, _ramstart
movel #RAMEND-CONFIG_MEMORY_RESERVE*0x100000, %d0
movel %d0, _ramend
diff --git a/arch/m68k/platform/68360/head-ram.S b/arch/m68k/platform/68360/head-ram.S
index 8eb94fb6b971..acd213170d80 100644
--- a/arch/m68k/platform/68360/head-ram.S
+++ b/arch/m68k/platform/68360/head-ram.S
@@ -219,8 +219,8 @@ LD1:
cmp.l #_edata, %a1
blt LD1
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -234,7 +234,7 @@ load_quicc:
store_ram_size:
/* Set ram size information */
move.l #_sdata, _rambase
- move.l #_ebss, _ramstart
+ move.l #__bss_stop, _ramstart
move.l #RAMEND, %d0
sub.l #0x1000, %d0 /* Reserve 4K for stack space.*/
move.l %d0, _ramend /* Different from RAMEND.*/
diff --git a/arch/m68k/platform/68360/head-rom.S b/arch/m68k/platform/68360/head-rom.S
index 97510e55b802..dfc756d99886 100644
--- a/arch/m68k/platform/68360/head-rom.S
+++ b/arch/m68k/platform/68360/head-rom.S
@@ -13,7 +13,7 @@
*/
.global _stext
-.global _sbss
+.global __bss_start
.global _start
.global _rambase
@@ -229,8 +229,8 @@ LD1:
cmp.l #_edata, %a1
blt LD1
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -244,7 +244,7 @@ load_quicc:
store_ram_size:
/* Set ram size information */
move.l #_sdata, _rambase
- move.l #_ebss, _ramstart
+ move.l #__bss_stop, _ramstart
move.l #RAMEND, %d0
sub.l #0x1000, %d0 /* Reserve 4K for stack space.*/
move.l %d0, _ramend /* Different from RAMEND.*/
diff --git a/arch/m68k/platform/coldfire/clk.c b/arch/m68k/platform/coldfire/clk.c
index 75f9ee967ea7..9cd13b4ce42b 100644
--- a/arch/m68k/platform/coldfire/clk.c
+++ b/arch/m68k/platform/coldfire/clk.c
@@ -146,9 +146,3 @@ struct clk_ops clk_ops1 = {
};
#endif /* MCFPM_PPMCR1 */
#endif /* MCFPM_PPMCR0 */
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
- return NULL;
-}
-EXPORT_SYMBOL(devm_clk_get);
diff --git a/arch/m68k/platform/coldfire/head.S b/arch/m68k/platform/coldfire/head.S
index 4e0c9eb3bd1f..b88f5716f357 100644
--- a/arch/m68k/platform/coldfire/head.S
+++ b/arch/m68k/platform/coldfire/head.S
@@ -230,8 +230,8 @@ _vstart:
/*
* Move ROM filesystem above bss :-)
*/
- lea _sbss,%a0 /* get start of bss */
- lea _ebss,%a1 /* set up destination */
+ lea __bss_start,%a0 /* get start of bss */
+ lea __bss_stop,%a1 /* set up destination */
movel %a0,%a2 /* copy of bss start */
movel 8(%a0),%d0 /* get size of ROMFS */
@@ -249,7 +249,7 @@ _copy_romfs:
bne _copy_romfs
#else /* CONFIG_ROMFS_FS */
- lea _ebss,%a1
+ lea __bss_stop,%a1
movel %a1,_ramstart
#endif /* CONFIG_ROMFS_FS */
@@ -257,8 +257,8 @@ _copy_romfs:
/*
* Zero out the bss region.
*/
- lea _sbss,%a0 /* get start of bss */
- lea _ebss,%a1 /* get end of bss */
+ lea __bss_start,%a0 /* get start of bss */
+ lea __bss_stop,%a1 /* get end of bss */
clrl %d0 /* set value */
_clear_bss:
movel %d0,(%a0)+ /* clear each word */
diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c
index d8e6349336b4..eeba067d565f 100644
--- a/arch/m68k/sun3/prom/init.c
+++ b/arch/m68k/sun3/prom/init.c
@@ -22,57 +22,13 @@ int prom_root_node;
struct linux_nodeops *prom_nodeops;
/* You must call prom_init() before you attempt to use any of the
- * routines in the prom library. It returns 0 on success, 1 on
- * failure. It gets passed the pointer to the PROM vector.
+ * routines in the prom library.
+ * It gets passed the pointer to the PROM vector.
*/
-extern void prom_meminit(void);
-extern void prom_ranges_init(void);
-
void __init prom_init(struct linux_romvec *rp)
{
romvec = rp;
-#ifndef CONFIG_SUN3
- switch(romvec->pv_romvers) {
- case 0:
- prom_vers = PROM_V0;
- break;
- case 2:
- prom_vers = PROM_V2;
- break;
- case 3:
- prom_vers = PROM_V3;
- break;
- case 4:
- prom_vers = PROM_P1275;
- prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
- prom_halt();
- break;
- default:
- prom_printf("PROMLIB: Bad PROM version %d\n",
- romvec->pv_romvers);
- prom_halt();
- break;
- };
-
- prom_rev = romvec->pv_plugin_revision;
- prom_prev = romvec->pv_printrev;
- prom_nodeops = romvec->pv_nodeops;
-
- prom_root_node = prom_getsibling(0);
- if((prom_root_node == 0) || (prom_root_node == -1))
- prom_halt();
-
- if((((unsigned long) prom_nodeops) == 0) ||
- (((unsigned long) prom_nodeops) == -1))
- prom_halt();
-
- prom_meminit();
-
- prom_ranges_init();
-#endif
-// printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
-// romvec->pv_romvers, prom_rev);
/* Initialization successful. */
return;
diff --git a/arch/microblaze/include/asm/sections.h b/arch/microblaze/include/asm/sections.h
index 4487e150b455..c07ed5d2a820 100644
--- a/arch/microblaze/include/asm/sections.h
+++ b/arch/microblaze/include/asm/sections.h
@@ -18,10 +18,6 @@ extern char _ssbss[], _esbss[];
extern unsigned long __ivt_start[], __ivt_end[];
extern char _etext[], _stext[];
-# ifdef CONFIG_MTD_UCLINUX
-extern char *_ebss;
-# endif
-
extern u32 _fdt_start[], _fdt_end[];
# endif /* !__ASSEMBLY__ */
diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c
index bb4907c828dc..2b25bcf05c00 100644
--- a/arch/microblaze/kernel/microblaze_ksyms.c
+++ b/arch/microblaze/kernel/microblaze_ksyms.c
@@ -21,9 +21,6 @@
#include <linux/ftrace.h>
#include <linux/uaccess.h>
-extern char *_ebss;
-EXPORT_SYMBOL_GPL(_ebss);
-
#ifdef CONFIG_FUNCTION_TRACER
extern void _mcount(void);
EXPORT_SYMBOL(_mcount);
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c
index 16d8dfd9094b..4da971d4392f 100644
--- a/arch/microblaze/kernel/setup.c
+++ b/arch/microblaze/kernel/setup.c
@@ -121,7 +121,7 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,
/* Move ROMFS out of BSS before clearing it */
if (romfs_size > 0) {
- memmove(&_ebss, (int *)romfs_base, romfs_size);
+ memmove(&__bss_stop, (int *)romfs_base, romfs_size);
klimit += romfs_size;
}
#endif
@@ -165,7 +165,7 @@ void __init machine_early_init(const char *cmdline, unsigned int ram,
BUG_ON(romfs_size < 0); /* What else can we do? */
printk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n",
- romfs_size, romfs_base, (unsigned)&_ebss);
+ romfs_size, romfs_base, (unsigned)&__bss_stop);
printk("New klimit: 0x%08x\n", (unsigned)klimit);
#endif
diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S
index 109e9d86ade4..936d01a689d7 100644
--- a/arch/microblaze/kernel/vmlinux.lds.S
+++ b/arch/microblaze/kernel/vmlinux.lds.S
@@ -131,7 +131,6 @@ SECTIONS {
*(COMMON)
. = ALIGN (4) ;
__bss_stop = . ;
- _ebss = . ;
}
. = ALIGN(PAGE_SIZE);
_end = .;
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index e3efc06e6409..faf65286574e 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -77,6 +77,7 @@ config AR7
select SYS_SUPPORTS_ZBOOT_UART16550
select ARCH_REQUIRE_GPIOLIB
select VLYNQ
+ select HAVE_CLK
help
Support for the Texas Instruments AR7 System-on-a-Chip
family: TNETD7100, 7200 and 7300.
@@ -88,6 +89,7 @@ config ATH79
select CEVT_R4K
select CSRC_R4K
select DMA_NONCOHERENT
+ select HAVE_CLK
select IRQ_CPU
select MIPS_MACHINE
select SYS_HAS_CPU_MIPS32_R2
@@ -124,6 +126,7 @@ config BCM63XX
select SYS_HAS_EARLY_PRINTK
select SWAP_IO_SPACE
select ARCH_REQUIRE_GPIOLIB
+ select HAVE_CLK
help
Support for BCM63XX based boards
diff --git a/arch/mips/alchemy/board-mtx1.c b/arch/mips/alchemy/board-mtx1.c
index 99969484c475..a124c251c0c9 100644
--- a/arch/mips/alchemy/board-mtx1.c
+++ b/arch/mips/alchemy/board-mtx1.c
@@ -228,6 +228,8 @@ static int mtx1_pci_idsel(unsigned int devsel, int assert)
* adapter on the mtx-1 "singleboard" variant. It triggers a custom
* logic chip connected to EXT_IO3 (GPIO1) to suppress IDSEL signals.
*/
+ udelay(1);
+
if (assert && devsel != 0)
/* Suppress signal to Cardbus */
alchemy_gpio_set_value(1, 0); /* set EXT_IO3 OFF */
diff --git a/arch/mips/ath79/dev-usb.c b/arch/mips/ath79/dev-usb.c
index 36e9570e7bc4..b2a2311ec85b 100644
--- a/arch/mips/ath79/dev-usb.c
+++ b/arch/mips/ath79/dev-usb.c
@@ -145,6 +145,8 @@ static void __init ar7240_usb_setup(void)
ath79_ohci_resources[0].start = AR7240_OHCI_BASE;
ath79_ohci_resources[0].end = AR7240_OHCI_BASE + AR7240_OHCI_SIZE - 1;
+ ath79_ohci_resources[1].start = ATH79_CPU_IRQ_USB;
+ ath79_ohci_resources[1].end = ATH79_CPU_IRQ_USB;
platform_device_register(&ath79_ohci_device);
}
diff --git a/arch/mips/ath79/gpio.c b/arch/mips/ath79/gpio.c
index 29054f211832..48fe762d2526 100644
--- a/arch/mips/ath79/gpio.c
+++ b/arch/mips/ath79/gpio.c
@@ -188,8 +188,10 @@ void __init ath79_gpio_init(void)
if (soc_is_ar71xx())
ath79_gpio_count = AR71XX_GPIO_COUNT;
- else if (soc_is_ar724x())
- ath79_gpio_count = AR724X_GPIO_COUNT;
+ else if (soc_is_ar7240())
+ ath79_gpio_count = AR7240_GPIO_COUNT;
+ else if (soc_is_ar7241() || soc_is_ar7242())
+ ath79_gpio_count = AR7241_GPIO_COUNT;
else if (soc_is_ar913x())
ath79_gpio_count = AR913X_GPIO_COUNT;
else if (soc_is_ar933x())
diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c
index e39f73048d4f..f1c9c3e2f678 100644
--- a/arch/mips/bcm63xx/dev-spi.c
+++ b/arch/mips/bcm63xx/dev-spi.c
@@ -106,11 +106,15 @@ int __init bcm63xx_spi_register(void)
if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) {
spi_resources[0].end += BCM_6338_RSET_SPI_SIZE - 1;
spi_pdata.fifo_size = SPI_6338_MSG_DATA_SIZE;
+ spi_pdata.msg_type_shift = SPI_6338_MSG_TYPE_SHIFT;
+ spi_pdata.msg_ctl_width = SPI_6338_MSG_CTL_WIDTH;
}
if (BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
spi_resources[0].end += BCM_6358_RSET_SPI_SIZE - 1;
spi_pdata.fifo_size = SPI_6358_MSG_DATA_SIZE;
+ spi_pdata.msg_type_shift = SPI_6358_MSG_TYPE_SHIFT;
+ spi_pdata.msg_ctl_width = SPI_6358_MSG_CTL_WIDTH;
}
bcm63xx_spi_regs_init();
diff --git a/arch/mips/cavium-octeon/octeon-irq.c b/arch/mips/cavium-octeon/octeon-irq.c
index 7fb1f222b8a5..274cd4fad30c 100644
--- a/arch/mips/cavium-octeon/octeon-irq.c
+++ b/arch/mips/cavium-octeon/octeon-irq.c
@@ -61,6 +61,12 @@ static void octeon_irq_set_ciu_mapping(int irq, int line, int bit,
octeon_irq_ciu_to_irq[line][bit] = irq;
}
+static void octeon_irq_force_ciu_mapping(struct irq_domain *domain,
+ int irq, int line, int bit)
+{
+ irq_domain_associate(domain, irq, line << 6 | bit);
+}
+
static int octeon_coreid_for_cpu(int cpu)
{
#ifdef CONFIG_SMP
@@ -183,19 +189,9 @@ static void __init octeon_irq_init_core(void)
mutex_init(&cd->core_irq_mutex);
irq = OCTEON_IRQ_SW0 + i;
- switch (irq) {
- case OCTEON_IRQ_TIMER:
- case OCTEON_IRQ_SW0:
- case OCTEON_IRQ_SW1:
- case OCTEON_IRQ_5:
- case OCTEON_IRQ_PERF:
- irq_set_chip_data(irq, cd);
- irq_set_chip_and_handler(irq, &octeon_irq_chip_core,
- handle_percpu_irq);
- break;
- default:
- break;
- }
+ irq_set_chip_data(irq, cd);
+ irq_set_chip_and_handler(irq, &octeon_irq_chip_core,
+ handle_percpu_irq);
}
}
@@ -890,7 +886,6 @@ static int octeon_irq_gpio_xlat(struct irq_domain *d,
unsigned int type;
unsigned int pin;
unsigned int trigger;
- struct octeon_irq_gpio_domain_data *gpiod;
if (d->of_node != node)
return -EINVAL;
@@ -925,8 +920,7 @@ static int octeon_irq_gpio_xlat(struct irq_domain *d,
break;
}
*out_type = type;
- gpiod = d->host_data;
- *out_hwirq = gpiod->base_hwirq + pin;
+ *out_hwirq = pin;
return 0;
}
@@ -996,19 +990,21 @@ static int octeon_irq_ciu_map(struct irq_domain *d,
static int octeon_irq_gpio_map(struct irq_domain *d,
unsigned int virq, irq_hw_number_t hw)
{
- unsigned int line = hw >> 6;
- unsigned int bit = hw & 63;
+ struct octeon_irq_gpio_domain_data *gpiod = d->host_data;
+ unsigned int line, bit;
if (!octeon_irq_virq_in_range(virq))
return -EINVAL;
+ hw += gpiod->base_hwirq;
+ line = hw >> 6;
+ bit = hw & 63;
if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0)
return -EINVAL;
octeon_irq_set_ciu_mapping(virq, line, bit,
octeon_irq_gpio_chip,
octeon_irq_handle_gpio);
-
return 0;
}
@@ -1149,6 +1145,7 @@ static void __init octeon_irq_init_ciu(void)
struct irq_chip *chip_wd;
struct device_node *gpio_node;
struct device_node *ciu_node;
+ struct irq_domain *ciu_domain = NULL;
octeon_irq_init_ciu_percpu();
octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
@@ -1177,31 +1174,6 @@ static void __init octeon_irq_init_ciu(void)
/* Mips internal */
octeon_irq_init_core();
- /* CIU_0 */
- for (i = 0; i < 16; i++)
- octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq);
-
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
-
- for (i = 0; i < 4; i++)
- octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq);
- for (i = 0; i < 4; i++)
- octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq);
-
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq);
- for (i = 0; i < 4; i++)
- octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip, handle_edge_irq);
-
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq);
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq);
-
- /* CIU_1 */
- for (i = 0; i < 16; i++)
- octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);
-
- octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);
-
gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
if (gpio_node) {
struct octeon_irq_gpio_domain_data *gpiod;
@@ -1219,10 +1191,35 @@ static void __init octeon_irq_init_ciu(void)
ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
if (ciu_node) {
- irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
+ ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
of_node_put(ciu_node);
} else
- pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n");
+ panic("Cannot find device node for cavium,octeon-3860-ciu.");
+
+ /* CIU_0 */
+ for (i = 0; i < 16; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0);
+
+ octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq);
+ octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq);
+
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36);
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 0, i + 40);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_RML, 0, 46);
+ for (i = 0; i < 4; i++)
+ octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 0, i + 52);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 0, 56);
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_BOOTDMA, 0, 63);
+
+ /* CIU_1 */
+ for (i = 0; i < 16; i++)
+ octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);
+
+ octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17);
/* Enable the CIU lines */
set_c0_status(STATUSF_IP3 | STATUSF_IP2);
diff --git a/arch/mips/include/asm/clock.h b/arch/mips/include/asm/clock.h
index 83894aa7932c..c9456e7a7283 100644
--- a/arch/mips/include/asm/clock.h
+++ b/arch/mips/include/asm/clock.h
@@ -50,15 +50,4 @@ void clk_recalc_rate(struct clk *);
int clk_register(struct clk *);
void clk_unregister(struct clk *);
-/* the exported API, in addition to clk_set_rate */
-/**
- * clk_set_rate_ex - set the clock rate for a clock source, with additional parameter
- * @clk: clock source
- * @rate: desired clock rate in Hz
- * @algo_id: algorithm id to be passed down to ops->set_rate
- *
- * Returns success (0) or negative errno.
- */
-int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id);
-
#endif /* __ASM_MIPS_CLOCK_H */
diff --git a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
index 1caa78ad06d5..dde504477fac 100644
--- a/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
+++ b/arch/mips/include/asm/mach-ath79/ar71xx_regs.h
@@ -393,7 +393,8 @@
#define AR71XX_GPIO_REG_FUNC 0x28
#define AR71XX_GPIO_COUNT 16
-#define AR724X_GPIO_COUNT 18
+#define AR7240_GPIO_COUNT 18
+#define AR7241_GPIO_COUNT 20
#define AR913X_GPIO_COUNT 22
#define AR933X_GPIO_COUNT 30
#define AR934X_GPIO_COUNT 23
diff --git a/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h
index 4476fa03bf36..6ddae926bf79 100644
--- a/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h
+++ b/arch/mips/include/asm/mach-ath79/cpu-feature-overrides.h
@@ -42,7 +42,6 @@
#define cpu_has_mips64r1 0
#define cpu_has_mips64r2 0
-#define cpu_has_dsp 0
#define cpu_has_mipsmt 0
#define cpu_has_64bits 0
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
index 7d98dbe5d4b5..c9bae1362606 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
@@ -9,6 +9,8 @@ int __init bcm63xx_spi_register(void);
struct bcm63xx_spi_pdata {
unsigned int fifo_size;
+ unsigned int msg_type_shift;
+ unsigned int msg_ctl_width;
int bus_num;
int num_chipselect;
u32 speed_hz;
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 4ccc2a748aff..61f2a2a5099d 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -1054,7 +1054,8 @@
#define SPI_6338_FILL_BYTE 0x07
#define SPI_6338_MSG_TAIL 0x09
#define SPI_6338_RX_TAIL 0x0b
-#define SPI_6338_MSG_CTL 0x40
+#define SPI_6338_MSG_CTL 0x40 /* 8-bits register */
+#define SPI_6338_MSG_CTL_WIDTH 8
#define SPI_6338_MSG_DATA 0x41
#define SPI_6338_MSG_DATA_SIZE 0x3f
#define SPI_6338_RX_DATA 0x80
@@ -1070,7 +1071,8 @@
#define SPI_6348_FILL_BYTE 0x07
#define SPI_6348_MSG_TAIL 0x09
#define SPI_6348_RX_TAIL 0x0b
-#define SPI_6348_MSG_CTL 0x40
+#define SPI_6348_MSG_CTL 0x40 /* 8-bits register */
+#define SPI_6348_MSG_CTL_WIDTH 8
#define SPI_6348_MSG_DATA 0x41
#define SPI_6348_MSG_DATA_SIZE 0x3f
#define SPI_6348_RX_DATA 0x80
@@ -1078,6 +1080,7 @@
/* BCM 6358 SPI core */
#define SPI_6358_MSG_CTL 0x00 /* 16-bits register */
+#define SPI_6358_MSG_CTL_WIDTH 16
#define SPI_6358_MSG_DATA 0x02
#define SPI_6358_MSG_DATA_SIZE 0x21e
#define SPI_6358_RX_DATA 0x400
@@ -1094,6 +1097,7 @@
/* BCM 6358 SPI core */
#define SPI_6368_MSG_CTL 0x00 /* 16-bits register */
+#define SPI_6368_MSG_CTL_WIDTH 16
#define SPI_6368_MSG_DATA 0x02
#define SPI_6368_MSG_DATA_SIZE 0x21e
#define SPI_6368_RX_DATA 0x400
@@ -1115,7 +1119,10 @@
#define SPI_HD_W 0x01
#define SPI_HD_R 0x02
#define SPI_BYTE_CNT_SHIFT 0
-#define SPI_MSG_TYPE_SHIFT 14
+#define SPI_6338_MSG_TYPE_SHIFT 6
+#define SPI_6348_MSG_TYPE_SHIFT 6
+#define SPI_6358_MSG_TYPE_SHIFT 14
+#define SPI_6368_MSG_TYPE_SHIFT 14
/* Command */
#define SPI_CMD_NOOP 0x00
diff --git a/arch/mips/include/asm/mach-cavium-octeon/irq.h b/arch/mips/include/asm/mach-cavium-octeon/irq.h
index 418992042f6f..c22a3078bf11 100644
--- a/arch/mips/include/asm/mach-cavium-octeon/irq.h
+++ b/arch/mips/include/asm/mach-cavium-octeon/irq.h
@@ -21,14 +21,10 @@ enum octeon_irq {
OCTEON_IRQ_TIMER,
/* sources in CIU_INTX_EN0 */
OCTEON_IRQ_WORKQ0,
- OCTEON_IRQ_GPIO0 = OCTEON_IRQ_WORKQ0 + 16,
- OCTEON_IRQ_WDOG0 = OCTEON_IRQ_GPIO0 + 16,
+ OCTEON_IRQ_WDOG0 = OCTEON_IRQ_WORKQ0 + 16,
OCTEON_IRQ_WDOG15 = OCTEON_IRQ_WDOG0 + 15,
OCTEON_IRQ_MBOX0 = OCTEON_IRQ_WDOG0 + 16,
OCTEON_IRQ_MBOX1,
- OCTEON_IRQ_UART0,
- OCTEON_IRQ_UART1,
- OCTEON_IRQ_UART2,
OCTEON_IRQ_PCI_INT0,
OCTEON_IRQ_PCI_INT1,
OCTEON_IRQ_PCI_INT2,
@@ -38,8 +34,6 @@ enum octeon_irq {
OCTEON_IRQ_PCI_MSI2,
OCTEON_IRQ_PCI_MSI3,
- OCTEON_IRQ_TWSI,
- OCTEON_IRQ_TWSI2,
OCTEON_IRQ_RML,
OCTEON_IRQ_TIMER0,
OCTEON_IRQ_TIMER1,
@@ -47,8 +41,6 @@ enum octeon_irq {
OCTEON_IRQ_TIMER3,
OCTEON_IRQ_USB0,
OCTEON_IRQ_USB1,
- OCTEON_IRQ_MII0,
- OCTEON_IRQ_MII1,
OCTEON_IRQ_BOOTDMA,
#ifndef CONFIG_PCI_MSI
OCTEON_IRQ_LAST = 127
diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h
index 06367c37e1b2..5222a007bc21 100644
--- a/arch/mips/include/asm/mach-loongson/loongson.h
+++ b/arch/mips/include/asm/mach-loongson/loongson.h
@@ -245,7 +245,6 @@ static inline void do_perfcnt_IRQ(void)
#ifdef CONFIG_CPU_SUPPORTS_CPUFREQ
#include <linux/cpufreq.h>
-extern void loongson2_cpu_wait(void);
extern struct cpufreq_frequency_table loongson2_clockmod_table[];
/* Chip Config */
diff --git a/arch/mips/include/asm/module.h b/arch/mips/include/asm/module.h
index 7531ecd654d6..dca8bce8c7ab 100644
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -10,6 +10,7 @@ struct mod_arch_specific {
struct list_head dbe_list;
const struct exception_table_entry *dbe_start;
const struct exception_table_entry *dbe_end;
+ struct mips_hi16 *r_mips_hi16_list;
};
typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
diff --git a/arch/mips/include/asm/r4k-timer.h b/arch/mips/include/asm/r4k-timer.h
index a37d12b3b61c..afe9e0e03fe9 100644
--- a/arch/mips/include/asm/r4k-timer.h
+++ b/arch/mips/include/asm/r4k-timer.h
@@ -12,16 +12,16 @@
#ifdef CONFIG_SYNC_R4K
-extern void synchronise_count_master(void);
-extern void synchronise_count_slave(void);
+extern void synchronise_count_master(int cpu);
+extern void synchronise_count_slave(int cpu);
#else
-static inline void synchronise_count_master(void)
+static inline void synchronise_count_master(int cpu)
{
}
-static inline void synchronise_count_slave(void)
+static inline void synchronise_count_slave(int cpu)
{
}
diff --git a/arch/mips/kernel/cpufreq/Makefile b/arch/mips/kernel/cpufreq/Makefile
index c3479a432efe..05a5715ee38c 100644
--- a/arch/mips/kernel/cpufreq/Makefile
+++ b/arch/mips/kernel/cpufreq/Makefile
@@ -2,4 +2,4 @@
# Makefile for the Linux/MIPS cpufreq.
#
-obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o loongson2_clock.o
+obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o
diff --git a/arch/mips/kernel/cpufreq/loongson2_clock.c b/arch/mips/kernel/cpufreq/loongson2_clock.c
deleted file mode 100644
index 5426779d9fdb..000000000000
--- a/arch/mips/kernel/cpufreq/loongson2_clock.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
- * Author: Yanhua, yanh@lemote.com
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-
-#include <linux/module.h>
-#include <linux/cpufreq.h>
-#include <linux/platform_device.h>
-
-#include <asm/clock.h>
-
-#include <loongson.h>
-
-static LIST_HEAD(clock_list);
-static DEFINE_SPINLOCK(clock_lock);
-static DEFINE_MUTEX(clock_list_sem);
-
-/* Minimum CLK support */
-enum {
- DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
- DC_87PT, DC_DISABLE, DC_RESV
-};
-
-struct cpufreq_frequency_table loongson2_clockmod_table[] = {
- {DC_RESV, CPUFREQ_ENTRY_INVALID},
- {DC_ZERO, CPUFREQ_ENTRY_INVALID},
- {DC_25PT, 0},
- {DC_37PT, 0},
- {DC_50PT, 0},
- {DC_62PT, 0},
- {DC_75PT, 0},
- {DC_87PT, 0},
- {DC_DISABLE, 0},
- {DC_RESV, CPUFREQ_TABLE_END},
-};
-EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
-
-static struct clk cpu_clk = {
- .name = "cpu_clk",
- .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
- .rate = 800000000,
-};
-
-struct clk *clk_get(struct device *dev, const char *id)
-{
- return &cpu_clk;
-}
-EXPORT_SYMBOL(clk_get);
-
-static void propagate_rate(struct clk *clk)
-{
- struct clk *clkp;
-
- list_for_each_entry(clkp, &clock_list, node) {
- if (likely(clkp->parent != clk))
- continue;
- if (likely(clkp->ops && clkp->ops->recalc))
- clkp->ops->recalc(clkp);
- if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
- propagate_rate(clkp);
- }
-}
-
-int clk_enable(struct clk *clk)
-{
- return 0;
-}
-EXPORT_SYMBOL(clk_enable);
-
-void clk_disable(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_disable);
-
-unsigned long clk_get_rate(struct clk *clk)
-{
- return (unsigned long)clk->rate;
-}
-EXPORT_SYMBOL(clk_get_rate);
-
-void clk_put(struct clk *clk)
-{
-}
-EXPORT_SYMBOL(clk_put);
-
-int clk_set_rate(struct clk *clk, unsigned long rate)
-{
- return clk_set_rate_ex(clk, rate, 0);
-}
-EXPORT_SYMBOL_GPL(clk_set_rate);
-
-int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
-{
- int ret = 0;
- int regval;
- int i;
-
- if (likely(clk->ops && clk->ops->set_rate)) {
- unsigned long flags;
-
- spin_lock_irqsave(&clock_lock, flags);
- ret = clk->ops->set_rate(clk, rate, algo_id);
- spin_unlock_irqrestore(&clock_lock, flags);
- }
-
- if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
- propagate_rate(clk);
-
- for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
- i++) {
- if (loongson2_clockmod_table[i].frequency ==
- CPUFREQ_ENTRY_INVALID)
- continue;
- if (rate == loongson2_clockmod_table[i].frequency)
- break;
- }
- if (rate != loongson2_clockmod_table[i].frequency)
- return -ENOTSUPP;
-
- clk->rate = rate;
-
- regval = LOONGSON_CHIPCFG0;
- regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
- LOONGSON_CHIPCFG0 = regval;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(clk_set_rate_ex);
-
-long clk_round_rate(struct clk *clk, unsigned long rate)
-{
- if (likely(clk->ops && clk->ops->round_rate)) {
- unsigned long flags, rounded;
-
- spin_lock_irqsave(&clock_lock, flags);
- rounded = clk->ops->round_rate(clk, rate);
- spin_unlock_irqrestore(&clock_lock, flags);
-
- return rounded;
- }
-
- return rate;
-}
-EXPORT_SYMBOL_GPL(clk_round_rate);
-
-/*
- * This is the simple version of Loongson-2 wait, Maybe we need do this in
- * interrupt disabled content
- */
-
-DEFINE_SPINLOCK(loongson2_wait_lock);
-void loongson2_cpu_wait(void)
-{
- u32 cpu_freq;
- unsigned long flags;
-
- spin_lock_irqsave(&loongson2_wait_lock, flags);
- cpu_freq = LOONGSON_CHIPCFG0;
- LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */
- LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */
- spin_unlock_irqrestore(&loongson2_wait_lock, flags);
-}
-EXPORT_SYMBOL_GPL(loongson2_cpu_wait);
-
-MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
-MODULE_DESCRIPTION("cpufreq driver for Loongson 2F");
-MODULE_LICENSE("GPL");
diff --git a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c
index ae5db206347c..e7c98e2b78b6 100644
--- a/arch/mips/kernel/cpufreq/loongson2_cpufreq.c
+++ b/arch/mips/kernel/cpufreq/loongson2_cpufreq.c
@@ -19,7 +19,7 @@
#include <asm/clock.h>
-#include <loongson.h>
+#include <asm/mach-loongson/loongson.h>
static uint nowait;
@@ -181,6 +181,25 @@ static struct platform_driver platform_driver = {
.id_table = platform_device_ids,
};
+/*
+ * This is the simple version of Loongson-2 wait, Maybe we need do this in
+ * interrupt disabled context.
+ */
+
+static DEFINE_SPINLOCK(loongson2_wait_lock);
+
+static void loongson2_cpu_wait(void)
+{
+ unsigned long flags;
+ u32 cpu_freq;
+
+ spin_lock_irqsave(&loongson2_wait_lock, flags);
+ cpu_freq = LOONGSON_CHIPCFG0;
+ LOONGSON_CHIPCFG0 &= ~0x7; /* Put CPU into wait mode */
+ LOONGSON_CHIPCFG0 = cpu_freq; /* Restore CPU state */
+ spin_unlock_irqrestore(&loongson2_wait_lock, flags);
+}
+
static int __init cpufreq_init(void)
{
int ret;
diff --git a/arch/mips/kernel/module.c b/arch/mips/kernel/module.c
index a5066b1c3de3..4f8c3cba8c0c 100644
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -39,8 +39,6 @@ struct mips_hi16 {
Elf_Addr value;
};
-static struct mips_hi16 *mips_hi16_list;
-
static LIST_HEAD(dbe_list);
static DEFINE_SPINLOCK(dbe_lock);
@@ -128,8 +126,8 @@ static int apply_r_mips_hi16_rel(struct module *me, u32 *location, Elf_Addr v)
n->addr = (Elf_Addr *)location;
n->value = v;
- n->next = mips_hi16_list;
- mips_hi16_list = n;
+ n->next = me->arch.r_mips_hi16_list;
+ me->arch.r_mips_hi16_list = n;
return 0;
}
@@ -142,18 +140,28 @@ static int apply_r_mips_hi16_rela(struct module *me, u32 *location, Elf_Addr v)
return 0;
}
+static void free_relocation_chain(struct mips_hi16 *l)
+{
+ struct mips_hi16 *next;
+
+ while (l) {
+ next = l->next;
+ kfree(l);
+ l = next;
+ }
+}
+
static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
{
unsigned long insnlo = *location;
+ struct mips_hi16 *l;
Elf_Addr val, vallo;
/* Sign extend the addend we extract from the lo insn. */
vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
- if (mips_hi16_list != NULL) {
- struct mips_hi16 *l;
-
- l = mips_hi16_list;
+ if (me->arch.r_mips_hi16_list != NULL) {
+ l = me->arch.r_mips_hi16_list;
while (l != NULL) {
struct mips_hi16 *next;
unsigned long insn;
@@ -188,7 +196,7 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
l = next;
}
- mips_hi16_list = NULL;
+ me->arch.r_mips_hi16_list = NULL;
}
/*
@@ -201,6 +209,9 @@ static int apply_r_mips_lo16_rel(struct module *me, u32 *location, Elf_Addr v)
return 0;
out_danger:
+ free_relocation_chain(l);
+ me->arch.r_mips_hi16_list = NULL;
+
pr_err("module %s: dangerous R_MIPS_LO16 REL relocation\n", me->name);
return -ENOEXEC;
@@ -273,6 +284,7 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
pr_debug("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
+ me->arch.r_mips_hi16_list = NULL;
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
/* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
@@ -296,6 +308,19 @@ int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
return res;
}
+ /*
+ * Normally the hi16 list should be deallocated at this point. A
+ * malformed binary however could contain a series of R_MIPS_HI16
+ * relocations not followed by a R_MIPS_LO16 relocation. In that
+ * case, free up the list and return an error.
+ */
+ if (me->arch.r_mips_hi16_list) {
+ free_relocation_chain(me->arch.r_mips_hi16_list);
+ me->arch.r_mips_hi16_list = NULL;
+
+ return -ENOEXEC;
+ }
+
return 0;
}
diff --git a/arch/mips/kernel/smp-cmp.c b/arch/mips/kernel/smp-cmp.c
index e7e03ecf5495..afc379ca3753 100644
--- a/arch/mips/kernel/smp-cmp.c
+++ b/arch/mips/kernel/smp-cmp.c
@@ -102,7 +102,7 @@ static void cmp_init_secondary(void)
c->vpe_id = (read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE;
#endif
#ifdef CONFIG_MIPS_MT_SMTC
- c->tc_id = (read_c0_tcbind() >> TCBIND_CURTC_SHIFT) & TCBIND_CURTC;
+ c->tc_id = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT;
#endif
}
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 31637d8c8738..9005bf9fb859 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -130,7 +130,7 @@ asmlinkage __cpuinit void start_secondary(void)
cpu_set(cpu, cpu_callin_map);
- synchronise_count_slave();
+ synchronise_count_slave(cpu);
/*
* irq will be enabled in ->smp_finish(), enabling it too early
@@ -173,7 +173,6 @@ void smp_send_stop(void)
void __init smp_cpus_done(unsigned int max_cpus)
{
mp_ops->cpus_done();
- synchronise_count_master();
}
/* called from main before smp_init() */
@@ -206,6 +205,7 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *tidle)
while (!cpu_isset(cpu, cpu_callin_map))
udelay(100);
+ synchronise_count_master(cpu);
return 0;
}
diff --git a/arch/mips/kernel/sync-r4k.c b/arch/mips/kernel/sync-r4k.c
index 842d55e411fd..7f1eca3858de 100644
--- a/arch/mips/kernel/sync-r4k.c
+++ b/arch/mips/kernel/sync-r4k.c
@@ -28,12 +28,11 @@ static atomic_t __cpuinitdata count_reference = ATOMIC_INIT(0);
#define COUNTON 100
#define NR_LOOPS 5
-void __cpuinit synchronise_count_master(void)
+void __cpuinit synchronise_count_master(int cpu)
{
int i;
unsigned long flags;
unsigned int initcount;
- int nslaves;
#ifdef CONFIG_MIPS_MT_SMTC
/*
@@ -43,8 +42,7 @@ void __cpuinit synchronise_count_master(void)
return;
#endif
- printk(KERN_INFO "Synchronize counters across %u CPUs: ",
- num_online_cpus());
+ printk(KERN_INFO "Synchronize counters for CPU %u: ", cpu);
local_irq_save(flags);
@@ -52,7 +50,7 @@ void __cpuinit synchronise_count_master(void)
* Notify the slaves that it's time to start
*/
atomic_set(&count_reference, read_c0_count());
- atomic_set(&count_start_flag, 1);
+ atomic_set(&count_start_flag, cpu);
smp_wmb();
/* Count will be initialised to current timer for all CPU's */
@@ -69,10 +67,9 @@ void __cpuinit synchronise_count_master(void)
* two CPUs.
*/
- nslaves = num_online_cpus()-1;
for (i = 0; i < NR_LOOPS; i++) {
- /* slaves loop on '!= ncpus' */
- while (atomic_read(&count_count_start) != nslaves)
+ /* slaves loop on '!= 2' */
+ while (atomic_read(&count_count_start) != 1)
mb();
atomic_set(&count_count_stop, 0);
smp_wmb();
@@ -89,7 +86,7 @@ void __cpuinit synchronise_count_master(void)
/*
* Wait for all slaves to leave the synchronization point:
*/
- while (atomic_read(&count_count_stop) != nslaves)
+ while (atomic_read(&count_count_stop) != 1)
mb();
atomic_set(&count_count_start, 0);
smp_wmb();
@@ -97,6 +94,7 @@ void __cpuinit synchronise_count_master(void)
}
/* Arrange for an interrupt in a short while */
write_c0_compare(read_c0_count() + COUNTON);
+ atomic_set(&count_start_flag, 0);
local_irq_restore(flags);
@@ -108,11 +106,10 @@ void __cpuinit synchronise_count_master(void)
printk("done.\n");
}
-void __cpuinit synchronise_count_slave(void)
+void __cpuinit synchronise_count_slave(int cpu)
{
int i;
unsigned int initcount;
- int ncpus;
#ifdef CONFIG_MIPS_MT_SMTC
/*
@@ -127,16 +124,15 @@ void __cpuinit synchronise_count_slave(void)
* so we first wait for the master to say everyone is ready
*/
- while (!atomic_read(&count_start_flag))
+ while (atomic_read(&count_start_flag) != cpu)
mb();
/* Count will be initialised to next expire for all CPU's */
initcount = atomic_read(&count_reference);
- ncpus = num_online_cpus();
for (i = 0; i < NR_LOOPS; i++) {
atomic_inc(&count_count_start);
- while (atomic_read(&count_count_start) != ncpus)
+ while (atomic_read(&count_count_start) != 2)
mb();
/*
@@ -146,7 +142,7 @@ void __cpuinit synchronise_count_slave(void)
write_c0_count(initcount);
atomic_inc(&count_count_stop);
- while (atomic_read(&count_count_stop) != ncpus)
+ while (atomic_read(&count_count_stop) != 2)
mb();
}
/* Arrange for an interrupt in a short while */
diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c
index d3bcc33f4699..ce2f129b081f 100644
--- a/arch/mips/lantiq/clk.c
+++ b/arch/mips/lantiq/clk.c
@@ -135,6 +135,11 @@ void clk_deactivate(struct clk *clk)
}
EXPORT_SYMBOL(clk_deactivate);
+struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
+{
+ return NULL;
+}
+
static inline u32 get_counter_resolution(void)
{
u32 res;
diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c
index d185e8477fdf..6cfd6117fbfd 100644
--- a/arch/mips/lantiq/prom.c
+++ b/arch/mips/lantiq/prom.c
@@ -8,7 +8,10 @@
#include <linux/export.h>
#include <linux/clk.h>
+#include <linux/bootmem.h>
#include <linux/of_platform.h>
+#include <linux/of_fdt.h>
+
#include <asm/bootinfo.h>
#include <asm/time.h>
@@ -70,6 +73,25 @@ void __init plat_mem_setup(void)
__dt_setup_arch(&__dtb_start);
}
+void __init device_tree_init(void)
+{
+ unsigned long base, size;
+
+ if (!initial_boot_params)
+ return;
+
+ base = virt_to_phys((void *)initial_boot_params);
+ size = be32_to_cpu(initial_boot_params->totalsize);
+
+ /* Before we do anything, lets reserve the dt blob */
+ reserve_bootmem(base, size, BOOTMEM_DEFAULT);
+
+ unflatten_device_tree();
+
+ /* free the space reserved for the dt blob */
+ free_bootmem(base, size);
+}
+
void __init prom_init(void)
{
/* call the soc specific detetcion code and get it to fill soc_info */
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
index 83780f7c842b..befbb760ab76 100644
--- a/arch/mips/lantiq/xway/sysctrl.c
+++ b/arch/mips/lantiq/xway/sysctrl.c
@@ -20,10 +20,12 @@
/* clock control register */
#define CGU_IFCCR 0x0018
+#define CGU_IFCCR_VR9 0x0024
/* system clock register */
#define CGU_SYS 0x0010
/* pci control register */
#define CGU_PCICR 0x0034
+#define CGU_PCICR_VR9 0x0038
/* ephy configuration register */
#define CGU_EPHY 0x10
/* power control register */
@@ -80,6 +82,9 @@ static void __iomem *pmu_membase;
void __iomem *ltq_cgu_membase;
void __iomem *ltq_ebu_membase;
+static u32 ifccr = CGU_IFCCR;
+static u32 pcicr = CGU_PCICR;
+
/* legacy function kept alive to ease clkdev transition */
void ltq_pmu_enable(unsigned int module)
{
@@ -103,14 +108,14 @@ EXPORT_SYMBOL(ltq_pmu_disable);
/* enable a hw clock */
static int cgu_enable(struct clk *clk)
{
- ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR);
+ ltq_cgu_w32(ltq_cgu_r32(ifccr) | clk->bits, ifccr);
return 0;
}
/* disable a hw clock */
static void cgu_disable(struct clk *clk)
{
- ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR);
+ ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~clk->bits, ifccr);
}
/* enable a clock gate */
@@ -138,22 +143,22 @@ static void pmu_disable(struct clk *clk)
/* the pci enable helper */
static int pci_enable(struct clk *clk)
{
- unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+ unsigned int val = ltq_cgu_r32(ifccr);
/* set bus clock speed */
if (of_machine_is_compatible("lantiq,ar9")) {
- ifccr &= ~0x1f00000;
+ val &= ~0x1f00000;
if (clk->rate == CLOCK_33M)
- ifccr |= 0xe00000;
+ val |= 0xe00000;
else
- ifccr |= 0x700000; /* 62.5M */
+ val |= 0x700000; /* 62.5M */
} else {
- ifccr &= ~0xf00000;
+ val &= ~0xf00000;
if (clk->rate == CLOCK_33M)
- ifccr |= 0x800000;
+ val |= 0x800000;
else
- ifccr |= 0x400000; /* 62.5M */
+ val |= 0x400000; /* 62.5M */
}
- ltq_cgu_w32(ifccr, CGU_IFCCR);
+ ltq_cgu_w32(val, ifccr);
pmu_enable(clk);
return 0;
}
@@ -161,18 +166,16 @@ static int pci_enable(struct clk *clk)
/* enable the external clock as a source */
static int pci_ext_enable(struct clk *clk)
{
- ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16),
- CGU_IFCCR);
- ltq_cgu_w32((1 << 30), CGU_PCICR);
+ ltq_cgu_w32(ltq_cgu_r32(ifccr) & ~(1 << 16), ifccr);
+ ltq_cgu_w32((1 << 30), pcicr);
return 0;
}
/* disable the external clock as a source */
static void pci_ext_disable(struct clk *clk)
{
- ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16),
- CGU_IFCCR);
- ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR);
+ ltq_cgu_w32(ltq_cgu_r32(ifccr) | (1 << 16), ifccr);
+ ltq_cgu_w32((1 << 31) | (1 << 30), pcicr);
}
/* enable a clockout source */
@@ -184,11 +187,11 @@ static int clkout_enable(struct clk *clk)
for (i = 0; i < 4; i++) {
if (clk->rates[i] == clk->rate) {
int shift = 14 - (2 * clk->module);
- unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR);
+ unsigned int val = ltq_cgu_r32(ifccr);
- ifccr &= ~(3 << shift);
- ifccr |= i << shift;
- ltq_cgu_w32(ifccr, CGU_IFCCR);
+ val &= ~(3 << shift);
+ val |= i << shift;
+ ltq_cgu_w32(val, ifccr);
return 0;
}
}
@@ -336,8 +339,12 @@ void __init ltq_soc_init(void)
clkdev_add_clkout();
/* add the soc dependent clocks */
- if (!of_machine_is_compatible("lantiq,vr9"))
+ if (of_machine_is_compatible("lantiq,vr9")) {
+ ifccr = CGU_IFCCR_VR9;
+ pcicr = CGU_PCICR_VR9;
+ } else {
clkdev_add_pmu("1e180000.etop", NULL, 0, PMU_PPE);
+ }
if (!of_machine_is_compatible("lantiq,ase")) {
clkdev_add_pmu("1e100c00.serial", NULL, 0, PMU_ASC1);
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index aca93eed8779..263beb9322a8 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -41,6 +41,7 @@ config LEMOTE_MACH2F
select CSRC_R4K if ! MIPS_EXTERNAL_TIMER
select DMA_NONCOHERENT
select GENERIC_ISA_DMA_SUPPORT_BROKEN
+ select HAVE_CLK
select HW_HAS_PCI
select I8259
select IRQ_CPU
diff --git a/arch/mips/loongson/lemote-2f/Makefile b/arch/mips/loongson/lemote-2f/Makefile
index 8699a53f0477..4f9eaa328a16 100644
--- a/arch/mips/loongson/lemote-2f/Makefile
+++ b/arch/mips/loongson/lemote-2f/Makefile
@@ -2,7 +2,7 @@
# Makefile for lemote loongson2f family machines
#
-obj-y += machtype.o irq.o reset.o ec_kb3310b.o
+obj-y += clock.o machtype.o irq.o reset.o ec_kb3310b.o
#
# Suspend Support
diff --git a/arch/mips/loongson/lemote-2f/clock.c b/arch/mips/loongson/lemote-2f/clock.c
new file mode 100644
index 000000000000..bc739d4bab2e
--- /dev/null
+++ b/arch/mips/loongson/lemote-2f/clock.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2006 - 2008 Lemote Inc. & Insititute of Computing Technology
+ * Author: Yanhua, yanh@lemote.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include <asm/clock.h>
+#include <asm/mach-loongson/loongson.h>
+
+static LIST_HEAD(clock_list);
+static DEFINE_SPINLOCK(clock_lock);
+static DEFINE_MUTEX(clock_list_sem);
+
+/* Minimum CLK support */
+enum {
+ DC_ZERO, DC_25PT = 2, DC_37PT, DC_50PT, DC_62PT, DC_75PT,
+ DC_87PT, DC_DISABLE, DC_RESV
+};
+
+struct cpufreq_frequency_table loongson2_clockmod_table[] = {
+ {DC_RESV, CPUFREQ_ENTRY_INVALID},
+ {DC_ZERO, CPUFREQ_ENTRY_INVALID},
+ {DC_25PT, 0},
+ {DC_37PT, 0},
+ {DC_50PT, 0},
+ {DC_62PT, 0},
+ {DC_75PT, 0},
+ {DC_87PT, 0},
+ {DC_DISABLE, 0},
+ {DC_RESV, CPUFREQ_TABLE_END},
+};
+EXPORT_SYMBOL_GPL(loongson2_clockmod_table);
+
+static struct clk cpu_clk = {
+ .name = "cpu_clk",
+ .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES,
+ .rate = 800000000,
+};
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+ return &cpu_clk;
+}
+EXPORT_SYMBOL(clk_get);
+
+static void propagate_rate(struct clk *clk)
+{
+ struct clk *clkp;
+
+ list_for_each_entry(clkp, &clock_list, node) {
+ if (likely(clkp->parent != clk))
+ continue;
+ if (likely(clkp->ops && clkp->ops->recalc))
+ clkp->ops->recalc(clkp);
+ if (unlikely(clkp->flags & CLK_RATE_PROPAGATES))
+ propagate_rate(clkp);
+ }
+}
+
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return (unsigned long)clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ int ret = 0;
+ int regval;
+ int i;
+
+ if (likely(clk->ops && clk->ops->set_rate)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ ret = clk->ops->set_rate(clk, rate, 0);
+ spin_unlock_irqrestore(&clock_lock, flags);
+ }
+
+ if (unlikely(clk->flags & CLK_RATE_PROPAGATES))
+ propagate_rate(clk);
+
+ for (i = 0; loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END;
+ i++) {
+ if (loongson2_clockmod_table[i].frequency ==
+ CPUFREQ_ENTRY_INVALID)
+ continue;
+ if (rate == loongson2_clockmod_table[i].frequency)
+ break;
+ }
+ if (rate != loongson2_clockmod_table[i].frequency)
+ return -ENOTSUPP;
+
+ clk->rate = rate;
+
+ regval = LOONGSON_CHIPCFG0;
+ regval = (regval & ~0x7) | (loongson2_clockmod_table[i].index - 1);
+ LOONGSON_CHIPCFG0 = regval;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (likely(clk->ops && clk->ops->round_rate)) {
+ unsigned long flags, rounded;
+
+ spin_lock_irqsave(&clock_lock, flags);
+ rounded = clk->ops->round_rate(clk, rate);
+ spin_unlock_irqrestore(&clock_lock, flags);
+
+ return rounded;
+ }
+
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
diff --git a/arch/mips/loongson1/Kconfig b/arch/mips/loongson1/Kconfig
index 237fa214de9f..a9a14d6e81af 100644
--- a/arch/mips/loongson1/Kconfig
+++ b/arch/mips/loongson1/Kconfig
@@ -15,6 +15,7 @@ config LOONGSON1_LS1B
select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_HAS_EARLY_PRINTK
+ select HAVE_CLK
endchoice
diff --git a/arch/mips/loongson1/common/clock.c b/arch/mips/loongson1/common/clock.c
index 2d98fb030596..1bbbbec12085 100644
--- a/arch/mips/loongson1/common/clock.c
+++ b/arch/mips/loongson1/common/clock.c
@@ -38,12 +38,28 @@ struct clk *clk_get(struct device *dev, const char *name)
}
EXPORT_SYMBOL(clk_get);
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
+void clk_put(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_put);
+
static void pll_clk_init(struct clk *clk)
{
u32 pll;
diff --git a/arch/mips/mm/gup.c b/arch/mips/mm/gup.c
index 33aadbcf170b..dcfd573871c1 100644
--- a/arch/mips/mm/gup.c
+++ b/arch/mips/mm/gup.c
@@ -152,6 +152,8 @@ static int gup_huge_pud(pud_t pud, unsigned long addr, unsigned long end,
do {
VM_BUG_ON(compound_head(page) != head);
pages[*nr] = page;
+ if (PageTail(page))
+ get_huge_page_tail(page);
(*nr)++;
page++;
refs++;
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c
index 7b13a4caeea4..fea823f18479 100644
--- a/arch/mips/mti-malta/malta-int.c
+++ b/arch/mips/mti-malta/malta-int.c
@@ -273,16 +273,19 @@ asmlinkage void plat_irq_dispatch(void)
unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
int irq;
+ if (unlikely(!pending)) {
+ spurious_interrupt();
+ return;
+ }
+
irq = irq_ffs(pending);
if (irq == MIPSCPU_INT_I8259A)
malta_hw0_irqdispatch();
else if (gic_present && ((1 << irq) & ipi_map[smp_processor_id()]))
malta_ipi_irqdispatch();
- else if (irq >= 0)
- do_IRQ(MIPS_CPU_IRQ_BASE + irq);
else
- spurious_interrupt();
+ do_IRQ(MIPS_CPU_IRQ_BASE + irq);
}
#ifdef CONFIG_MIPS_MT_SMP
diff --git a/arch/mips/mti-malta/malta-pci.c b/arch/mips/mti-malta/malta-pci.c
index 284dea54faf5..2147cb34e705 100644
--- a/arch/mips/mti-malta/malta-pci.c
+++ b/arch/mips/mti-malta/malta-pci.c
@@ -252,16 +252,3 @@ void __init mips_pcibios_init(void)
register_pci_controller(controller);
}
-
-/* Enable PCI 2.1 compatibility in PIIX4 */
-static void __devinit quirk_dlcsetup(struct pci_dev *dev)
-{
- u8 odlc, ndlc;
- (void) pci_read_config_byte(dev, 0x82, &odlc);
- /* Enable passive releases and delayed transaction */
- ndlc = odlc | 7;
- (void) pci_write_config_byte(dev, 0x82, ndlc);
-}
-
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0,
- quirk_dlcsetup);
diff --git a/arch/mips/mti-malta/malta-platform.c b/arch/mips/mti-malta/malta-platform.c
index 4c35301720e7..80562b81f0f2 100644
--- a/arch/mips/mti-malta/malta-platform.c
+++ b/arch/mips/mti-malta/malta-platform.c
@@ -138,11 +138,6 @@ static int __init malta_add_devices(void)
if (err)
return err;
- /*
- * Set RTC to BCD mode to support current alarm code.
- */
- CMOS_WRITE(CMOS_READ(RTC_CONTROL) & ~RTC_DM_BINARY, RTC_CONTROL);
-
return 0;
}
diff --git a/arch/mips/pci/pci-ar724x.c b/arch/mips/pci/pci-ar724x.c
index 414a7459858d..86d77a666458 100644
--- a/arch/mips/pci/pci-ar724x.c
+++ b/arch/mips/pci/pci-ar724x.c
@@ -23,9 +23,12 @@
#define AR724X_PCI_MEM_BASE 0x10000000
#define AR724X_PCI_MEM_SIZE 0x08000000
+#define AR724X_PCI_REG_RESET 0x18
#define AR724X_PCI_REG_INT_STATUS 0x4c
#define AR724X_PCI_REG_INT_MASK 0x50
+#define AR724X_PCI_RESET_LINK_UP BIT(0)
+
#define AR724X_PCI_INT_DEV0 BIT(14)
#define AR724X_PCI_IRQ_COUNT 1
@@ -38,6 +41,15 @@ static void __iomem *ar724x_pci_ctrl_base;
static u32 ar724x_pci_bar0_value;
static bool ar724x_pci_bar0_is_cached;
+static bool ar724x_pci_link_up;
+
+static inline bool ar724x_pci_check_link(void)
+{
+ u32 reset;
+
+ reset = __raw_readl(ar724x_pci_ctrl_base + AR724X_PCI_REG_RESET);
+ return reset & AR724X_PCI_RESET_LINK_UP;
+}
static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
int size, uint32_t *value)
@@ -46,6 +58,9 @@ static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where,
void __iomem *base;
u32 data;
+ if (!ar724x_pci_link_up)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -96,6 +111,9 @@ static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where,
u32 data;
int s;
+ if (!ar724x_pci_link_up)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
if (devfn)
return PCIBIOS_DEVICE_NOT_FOUND;
@@ -280,6 +298,10 @@ int __init ar724x_pcibios_init(int irq)
if (ar724x_pci_ctrl_base == NULL)
goto err_unmap_devcfg;
+ ar724x_pci_link_up = ar724x_pci_check_link();
+ if (!ar724x_pci_link_up)
+ pr_warn("ar724x: PCIe link is down\n");
+
ar724x_pci_irq_init(irq);
register_pci_controller(&ar724x_pci_controller);
diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c
index b105eca3c020..cd8fcab6b054 100644
--- a/arch/mips/sgi-ip27/ip27-memory.c
+++ b/arch/mips/sgi-ip27/ip27-memory.c
@@ -401,6 +401,7 @@ static void __init node_mem_init(cnodeid_t node)
* Allocate the node data structures on the node first.
*/
__node_data[node] = __va(slot_freepfn << PAGE_SHIFT);
+ memset(__node_data[node], 0, PAGE_SIZE);
NODE_DATA(node)->bdata = &bootmem_node_data[node];
NODE_DATA(node)->node_start_pfn = start_pfn;
diff --git a/arch/mips/txx9/Kconfig b/arch/mips/txx9/Kconfig
index 852ae4bb7a85..6d40bc783459 100644
--- a/arch/mips/txx9/Kconfig
+++ b/arch/mips/txx9/Kconfig
@@ -20,6 +20,7 @@ config MACH_TXX9
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_BIG_ENDIAN
+ select HAVE_CLK
config TOSHIBA_JMR3927
bool "Toshiba JMR-TX3927 board"
diff --git a/arch/parisc/include/asm/atomic.h b/arch/parisc/include/asm/atomic.h
index 6c6defc24619..af9cf30ed474 100644
--- a/arch/parisc/include/asm/atomic.h
+++ b/arch/parisc/include/asm/atomic.h
@@ -141,7 +141,7 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
#define atomic_sub_and_test(i,v) (atomic_sub_return((i),(v)) == 0)
-#define ATOMIC_INIT(i) ((atomic_t) { (i) })
+#define ATOMIC_INIT(i) { (i) }
#define smp_mb__before_atomic_dec() smp_mb()
#define smp_mb__after_atomic_dec() smp_mb()
@@ -150,7 +150,7 @@ static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u)
#ifdef CONFIG_64BIT
-#define ATOMIC64_INIT(i) ((atomic64_t) { (i) })
+#define ATOMIC64_INIT(i) { (i) }
static __inline__ s64
__atomic64_add_return(s64 i, atomic64_t *v)
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index d4b94b395c16..2c05a9292a81 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -309,7 +309,7 @@ copy_thread(unsigned long clone_flags, unsigned long usp,
cregs->ksp = (unsigned long)stack
+ (pregs->gr[21] & (THREAD_SIZE - 1));
cregs->gr[30] = usp;
- if (p->personality == PER_HPUX) {
+ if (personality(p->personality) == PER_HPUX) {
#ifdef CONFIG_HPUX
cregs->kpc = (unsigned long) &hpux_child_return;
#else
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
index c9b932260f47..7426e40699bd 100644
--- a/arch/parisc/kernel/sys_parisc.c
+++ b/arch/parisc/kernel/sys_parisc.c
@@ -225,12 +225,12 @@ long parisc_personality(unsigned long personality)
long err;
if (personality(current->personality) == PER_LINUX32
- && personality == PER_LINUX)
- personality = PER_LINUX32;
+ && personality(personality) == PER_LINUX)
+ personality = (personality & ~PER_MASK) | PER_LINUX32;
err = sys_personality(personality);
- if (err == PER_LINUX32)
- err = PER_LINUX;
+ if (personality(err) == PER_LINUX32)
+ err = (err & ~PER_MASK) | PER_LINUX;
return err;
}
diff --git a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
index 8d35d2c1f694..4f9c9f682ecf 100644
--- a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi
@@ -345,6 +345,13 @@
/include/ "qoriq-duart-1.dtsi"
/include/ "qoriq-gpio-0.dtsi"
/include/ "qoriq-usb2-mph-0.dtsi"
+ usb@210000 {
+ compatible = "fsl-usb2-mph-v1.6", "fsl,mpc85xx-usb2-mph", "fsl-usb2-mph";
+ port0;
+ };
/include/ "qoriq-usb2-dr-0.dtsi"
+ usb@211000 {
+ compatible = "fsl-usb2-dr-v1.6", "fsl,mpc85xx-usb2-dr", "fsl-usb2-dr";
+ };
/include/ "qoriq-sec4.0-0.dtsi"
};
diff --git a/arch/powerpc/configs/85xx/p1023rds_defconfig b/arch/powerpc/configs/85xx/p1023rds_defconfig
index f4337bacd0e7..26e541c4662b 100644
--- a/arch/powerpc/configs/85xx/p1023rds_defconfig
+++ b/arch/powerpc/configs/85xx/p1023rds_defconfig
@@ -6,28 +6,27 @@ CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_AUDIT=y
-CONFIG_SPARSE_IRQ=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_KALLSYMS_ALL=y
-CONFIG_KALLSYMS_EXTRA_PASS=y
CONFIG_EMBEDDED=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
CONFIG_P1023_RDS=y
CONFIG_QUICC_ENGINE=y
CONFIG_QE_GPIO=y
CONFIG_CPM2=y
-CONFIG_GPIO_MPC8XXX=y
CONFIG_HIGHMEM=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m
CONFIG_MATH_EMULATION=y
@@ -63,11 +62,11 @@ CONFIG_INET_ESP=y
CONFIG_IPV6=y
CONFIG_IP_SCTP=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=131072
-CONFIG_MISC_DEVICES=y
CONFIG_EEPROM_LEGACY=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
@@ -80,15 +79,14 @@ CONFIG_SATA_FSL=y
CONFIG_SATA_SIL24=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
+CONFIG_FS_ENET=y
+CONFIG_FSL_PQ_MDIO=y
+CONFIG_E1000E=y
CONFIG_MARVELL_PHY=y
CONFIG_DAVICOM_PHY=y
CONFIG_CICADA_PHY=y
CONFIG_VITESSE_PHY=y
CONFIG_FIXED_PHY=y
-CONFIG_NET_ETHERNET=y
-CONFIG_FS_ENET=y
-CONFIG_E1000E=y
-CONFIG_FSL_PQ_MDIO=y
CONFIG_INPUT_FF_MEMLESS=m
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_KEYBOARD is not set
@@ -98,16 +96,15 @@ CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
-CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
CONFIG_SERIAL_QE=m
-CONFIG_HW_RANDOM=y
CONFIG_NVRAM=y
CONFIG_I2C=y
CONFIG_I2C_CPM=m
CONFIG_I2C_MPC=y
+CONFIG_GPIO_MPC8XXX=y
# CONFIG_HWMON is not set
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_SOUND=y
@@ -123,7 +120,6 @@ CONFIG_DMADEVICES=y
CONFIG_FSL_DMA=y
# CONFIG_NET_DMA is not set
CONFIG_STAGING=y
-# CONFIG_STAGING_EXCLUDE_BUILD is not set
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
@@ -150,22 +146,15 @@ CONFIG_QNX4FS_FS=m
CONFIG_SYSV_FS=m
CONFIG_UFS_FS=m
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_NFSD=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_MAC_PARTITION=y
CONFIG_CRC_T10DIF=y
CONFIG_FRAME_WARN=8092
CONFIG_DEBUG_FS=y
-CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_HUNG_TASK=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_DEBUG_INFO=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig
index b1f9597fe312..29bb11ec6c64 100644
--- a/arch/powerpc/configs/chroma_defconfig
+++ b/arch/powerpc/configs/chroma_defconfig
@@ -21,8 +21,8 @@ CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
CONFIG_NAMESPACES=y
CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
diff --git a/arch/powerpc/configs/corenet32_smp_defconfig b/arch/powerpc/configs/corenet32_smp_defconfig
index cbb98c1234fd..8b3d57c1ebe8 100644
--- a/arch/powerpc/configs/corenet32_smp_defconfig
+++ b/arch/powerpc/configs/corenet32_smp_defconfig
@@ -6,8 +6,8 @@ CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_AUDIT=y
-CONFIG_SPARSE_IRQ=y
-CONFIG_RCU_TRACE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
@@ -21,23 +21,22 @@ CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
CONFIG_P2041_RDB=y
CONFIG_P3041_DS=y
CONFIG_P4080_DS=y
CONFIG_P5020_DS=y
CONFIG_HIGHMEM=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_BINFMT_MISC=m
CONFIG_KEXEC=y
CONFIG_IRQ_ALL_CPUS=y
CONFIG_FORCE_MAX_ZONEORDER=13
-CONFIG_FSL_LBC=y
CONFIG_PCI=y
CONFIG_PCIEPORTBUS=y
-CONFIG_PCI_MSI=y
# CONFIG_PCIEASPM is not set
+CONFIG_PCI_MSI=y
CONFIG_RAPIDIO=y
CONFIG_FSL_RIO=y
CONFIG_NET=y
@@ -70,6 +69,7 @@ CONFIG_INET_IPCOMP=y
CONFIG_IPV6=y
CONFIG_IP_SCTP=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
@@ -77,17 +77,14 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_MTD_M25P80=y
CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_ECC=y
-CONFIG_MTD_NAND_IDS=y
-CONFIG_MTD_NAND_FSL_IFC=y
CONFIG_MTD_NAND_FSL_ELBC=y
-CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND_FSL_IFC=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=131072
-CONFIG_MISC_DEVICES=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
@@ -115,11 +112,9 @@ CONFIG_SERIO_LIBPS2=y
CONFIG_PPC_EPAPR_HV_BYTECHAN=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
-CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
-CONFIG_HW_RANDOM=y
CONFIG_NVRAM=y
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
@@ -132,7 +127,6 @@ CONFIG_SPI_FSL_ESPI=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_USB_HID=m
CONFIG_USB=y
-CONFIG_USB_DEVICEFS=y
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_FSL=y
@@ -142,8 +136,6 @@ CONFIG_USB_OHCI_HCD_PPC_OF_LE=y
CONFIG_USB_STORAGE=y
CONFIG_MMC=y
CONFIG_MMC_SDHCI=y
-CONFIG_MMC_SDHCI_OF=y
-CONFIG_MMC_SDHCI_OF_ESDHC=y
CONFIG_EDAC=y
CONFIG_EDAC_MM_EDAC=y
CONFIG_EDAC_MPC85XX=y
@@ -170,19 +162,16 @@ CONFIG_HUGETLBFS=y
CONFIG_JFFS2_FS=y
CONFIG_CRAMFS=y
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_NFSD=m
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_MAC_PARTITION=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_UTF8=m
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_SHIRQ=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_INFO=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_RCU_TRACE=y
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_MD4=y
diff --git a/arch/powerpc/configs/corenet64_smp_defconfig b/arch/powerpc/configs/corenet64_smp_defconfig
index dd89de8b0b7f..0516e22ca3de 100644
--- a/arch/powerpc/configs/corenet64_smp_defconfig
+++ b/arch/powerpc/configs/corenet64_smp_defconfig
@@ -56,6 +56,7 @@ CONFIG_INET_ESP=y
CONFIG_IPV6=y
CONFIG_IP_SCTP=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index 15130066e5e2..07b7f2af2dca 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -1,8 +1,10 @@
+CONFIG_PPC64=y
+CONFIG_ALTIVEC=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=4
CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_BLK_DEV_INITRD=y
@@ -13,15 +15,16 @@ CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_MAC_PARTITION=y
-CONFIG_SMP=y
-CONFIG_NR_CPUS=4
-CONFIG_KEXEC=y
-# CONFIG_RELOCATABLE is not set
+# CONFIG_PPC_PSERIES is not set
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_PMAC64=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_KEXEC=y
+CONFIG_IRQ_ALL_CPUS=y
+# CONFIG_MIGRATION is not set
CONFIG_PCI_MSI=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -49,6 +52,7 @@ CONFIG_NF_CT_NETLINK=m
CONFIG_NF_CONNTRACK_IPV4=m
CONFIG_IP_NF_QUEUE=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=m
CONFIG_BLK_DEV_RAM=y
@@ -56,6 +60,8 @@ CONFIG_BLK_DEV_RAM_SIZE=65536
CONFIG_CDROM_PKTCDVD=m
CONFIG_IDE=y
CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
@@ -79,24 +85,33 @@ CONFIG_DM_CRYPT=m
CONFIG_DM_SNAPSHOT=m
CONFIG_DM_MIRROR=m
CONFIG_DM_ZERO=m
-CONFIG_MACINTOSH_DRIVERS=y
+CONFIG_IEEE1394=y
+CONFIG_IEEE1394_OHCI1394=y
+CONFIG_IEEE1394_SBP2=m
+CONFIG_IEEE1394_ETH1394=m
+CONFIG_IEEE1394_RAWIO=y
+CONFIG_IEEE1394_VIDEO1394=m
+CONFIG_IEEE1394_DV1394=m
+CONFIG_ADB_PMU=y
+CONFIG_PMAC_SMU=y
CONFIG_MAC_EMUMOUSEBTN=y
+CONFIG_THERM_PM72=y
+CONFIG_WINDFARM=y
+CONFIG_WINDFARM_PM81=y
+CONFIG_WINDFARM_PM91=y
+CONFIG_WINDFARM_PM112=y
+CONFIG_WINDFARM_PM121=y
CONFIG_NETDEVICES=y
-CONFIG_BONDING=m
CONFIG_DUMMY=m
-CONFIG_MII=y
+CONFIG_BONDING=m
CONFIG_TUN=m
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_SUNGEM=y
CONFIG_ACENIC=m
CONFIG_ACENIC_OMIT_TIGON_I=y
-CONFIG_TIGON3=y
CONFIG_E1000=y
-CONFIG_SUNGEM=y
-CONFIG_PPP=m
-CONFIG_PPP_BSDCOMP=m
-CONFIG_PPP_DEFLATE=m
-CONFIG_PPPOE=m
-CONFIG_PPP_ASYNC=m
-CONFIG_PPP_SYNC_TTY=m
+CONFIG_TIGON3=y
CONFIG_USB_CATC=m
CONFIG_USB_KAWETH=m
CONFIG_USB_PEGASUS=m
@@ -106,24 +121,36 @@ CONFIG_USB_USBNET=m
# CONFIG_USB_NET_NET1080 is not set
# CONFIG_USB_NET_CDC_SUBSET is not set
# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_PPP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPPOE=m
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_JOYDEV=m
CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_MOUSE_PS2 is not set
+# CONFIG_SERIO_I8042 is not set
# CONFIG_SERIO_SERPORT is not set
-CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_HW_RANDOM is not set
CONFIG_GEN_RTC=y
CONFIG_RAW_DRIVER=y
CONFIG_I2C_CHARDEV=y
# CONFIG_HWMON is not set
-CONFIG_AGP=y
-CONFIG_DRM=y
-CONFIG_DRM_NOUVEAU=y
+CONFIG_AGP=m
+CONFIG_AGP_UNINORTH=m
CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_OF=y
+CONFIG_FB_NVIDIA=y
+CONFIG_FB_NVIDIA_I2C=y
CONFIG_FB_RADEON=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_LOGO=y
CONFIG_SOUND=m
CONFIG_SND=m
@@ -131,7 +158,15 @@ CONFIG_SND_SEQUENCER=m
CONFIG_SND_MIXER_OSS=m
CONFIG_SND_PCM_OSS=m
CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_POWERMAC=m
+CONFIG_SND_AOA=m
+CONFIG_SND_AOA_FABRIC_LAYOUT=m
+CONFIG_SND_AOA_ONYX=m
+CONFIG_SND_AOA_TAS=m
+CONFIG_SND_AOA_TOONIE=m
CONFIG_SND_USB_AUDIO=m
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
CONFIG_HID_GYRATION=y
CONFIG_LOGITECH_FF=y
CONFIG_HID_PANTHERLORD=y
@@ -139,12 +174,13 @@ CONFIG_HID_PETALYNX=y
CONFIG_HID_SAMSUNG=y
CONFIG_HID_SONY=y
CONFIG_HID_SUNPLUS=y
-CONFIG_HID_PID=y
-CONFIG_USB_HIDDEV=y
CONFIG_USB=y
+CONFIG_USB_DEVICEFS=y
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_HCD_PPC_OF is not set
CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PPC_OF_BE=y
CONFIG_USB_ACM=m
CONFIG_USB_PRINTER=y
CONFIG_USB_STORAGE=y
@@ -208,6 +244,8 @@ CONFIG_REISERFS_FS_POSIX_ACL=y
CONFIG_REISERFS_FS_SECURITY=y
CONFIG_XFS_FS=m
CONFIG_XFS_POSIX_ACL=y
+CONFIG_INOTIFY=y
+CONFIG_AUTOFS_FS=m
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
@@ -221,12 +259,14 @@ CONFIG_HFS_FS=m
CONFIG_HFSPLUS_FS=m
CONFIG_CRAMFS=y
CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_NFSD=y
CONFIG_NFSD_V3_ACL=y
CONFIG_NFSD_V4=y
CONFIG_CIFS=m
+CONFIG_PARTITION_ADVANCED=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_1250=y
CONFIG_NLS_CODEPAGE_1251=y
@@ -234,23 +274,29 @@ CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_15=y
CONFIG_NLS_UTF8=y
+CONFIG_CRC_T10DIF=y
+CONFIG_LIBCRC32C=m
CONFIG_MAGIC_SYSRQ=y
-# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
CONFIG_LATENCYTOP=y
-CONFIG_STRICT_DEVMEM=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_BOOTX_TEXT=y
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_SHA256=m
CONFIG_CRYPTO_SHA512=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_AES=m
CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
CONFIG_CRYPTO_BLOWFISH=m
CONFIG_CRYPTO_CAST5=m
CONFIG_CRYPTO_CAST6=m
@@ -260,6 +306,3 @@ CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_TWOFISH=m
# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
-# CONFIG_VIRTUALIZATION is not set
-CONFIG_CRC_T10DIF=y
-CONFIG_LIBCRC32C=m
diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig
index 5aac9a8bc53b..9352e4430c3b 100644
--- a/arch/powerpc/configs/mpc83xx_defconfig
+++ b/arch/powerpc/configs/mpc83xx_defconfig
@@ -2,12 +2,12 @@ CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_BLK_DEV_INITRD=y
-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_EXPERT=y
CONFIG_SLAB=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
# CONFIG_PPC_CHRP is not set
# CONFIG_PPC_PMAC is not set
CONFIG_PPC_83xx=y
@@ -25,7 +25,6 @@ CONFIG_ASP834x=y
CONFIG_QUICC_ENGINE=y
CONFIG_QE_GPIO=y
CONFIG_MATH_EMULATION=y
-CONFIG_SPARSE_IRQ=y
CONFIG_PCI=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -42,10 +41,9 @@ CONFIG_INET_ESP=y
# CONFIG_INET_LRO is not set
# CONFIG_IPV6 is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
-CONFIG_MTD_PARTITIONS=y
-CONFIG_MTD_OF_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
@@ -64,15 +62,14 @@ CONFIG_ATA=y
CONFIG_SATA_FSL=y
CONFIG_SATA_SIL=y
CONFIG_NETDEVICES=y
+CONFIG_MII=y
+CONFIG_UCC_GETH=y
+CONFIG_GIANFAR=y
CONFIG_MARVELL_PHY=y
CONFIG_DAVICOM_PHY=y
CONFIG_VITESSE_PHY=y
CONFIG_ICPLUS_PHY=y
CONFIG_FIXED_PHY=y
-CONFIG_NET_ETHERNET=y
-CONFIG_MII=y
-CONFIG_GIANFAR=y
-CONFIG_UCC_GETH=y
CONFIG_INPUT_FF_MEMLESS=m
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_KEYBOARD is not set
@@ -112,17 +109,12 @@ CONFIG_RTC_DRV_DS1374=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_INOTIFY=y
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
-CONFIG_PARTITION_ADVANCED=y
CONFIG_CRC_T10DIF=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
-CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_SHA256=y
diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig
index 03ee911c4577..8b5bda27d248 100644
--- a/arch/powerpc/configs/mpc85xx_defconfig
+++ b/arch/powerpc/configs/mpc85xx_defconfig
@@ -5,7 +5,9 @@ CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_AUDIT=y
-CONFIG_SPARSE_IRQ=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
@@ -17,6 +19,8 @@ CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
CONFIG_MPC8540_ADS=y
CONFIG_MPC8560_ADS=y
CONFIG_MPC85xx_CDS=y
@@ -40,8 +44,6 @@ CONFIG_SBC8548=y
CONFIG_QUICC_ENGINE=y
CONFIG_QE_GPIO=y
CONFIG_HIGHMEM=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_BINFMT_MISC=m
CONFIG_MATH_EMULATION=y
CONFIG_FORCE_MAX_ZONEORDER=12
@@ -74,36 +76,25 @@ CONFIG_INET_ESP=y
CONFIG_IPV6=y
CONFIG_IP_SCTP=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
CONFIG_FTL=y
-CONFIG_MTD_GEN_PROBE=y
-CONFIG_MTD_MAP_BANK_WIDTH_1=y
-CONFIG_MTD_MAP_BANK_WIDTH_2=y
-CONFIG_MTD_MAP_BANK_WIDTH_4=y
-CONFIG_MTD_CFI_I1=y
-CONFIG_MTD_CFI_I2=y
+CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
-CONFIG_MTD_CFI_UTIL=y
CONFIG_MTD_PHYSMAP_OF=y
-CONFIG_MTD_PARTITIONS=y
-CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_M25P80=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_MTD_NAND_FSL_IFC=y
-CONFIG_MTD_NAND_IDS=y
-CONFIG_MTD_NAND_ECC=y
-CONFIG_MTD_M25P80=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=131072
-CONFIG_MISC_DEVICES=y
CONFIG_EEPROM_LEGACY=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
@@ -115,6 +106,7 @@ CONFIG_ATA=y
CONFIG_SATA_AHCI=y
CONFIG_SATA_FSL=y
CONFIG_PATA_ALI=y
+CONFIG_PATA_VIA=y
CONFIG_NETDEVICES=y
CONFIG_DUMMY=y
CONFIG_FS_ENET=y
@@ -134,7 +126,6 @@ CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
-CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
@@ -183,7 +174,6 @@ CONFIG_HID_SAMSUNG=y
CONFIG_HID_SONY=y
CONFIG_HID_SUNPLUS=y
CONFIG_USB=y
-CONFIG_USB_DEVICEFS=y
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_FSL=y
@@ -229,18 +219,13 @@ CONFIG_QNX4FS_FS=m
CONFIG_SYSV_FS=m
CONFIG_UFS_FS=m
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_NFSD=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_MAC_PARTITION=y
CONFIG_CRC_T10DIF=y
CONFIG_DEBUG_FS=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_INFO=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig
index fdfa84dc908f..b0974e7e98ae 100644
--- a/arch/powerpc/configs/mpc85xx_smp_defconfig
+++ b/arch/powerpc/configs/mpc85xx_smp_defconfig
@@ -7,7 +7,9 @@ CONFIG_SYSVIPC=y
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_AUDIT=y
-CONFIG_SPARSE_IRQ=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
@@ -19,6 +21,8 @@ CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
CONFIG_MPC8540_ADS=y
CONFIG_MPC8560_ADS=y
CONFIG_MPC85xx_CDS=y
@@ -42,8 +46,6 @@ CONFIG_SBC8548=y
CONFIG_QUICC_ENGINE=y
CONFIG_QE_GPIO=y
CONFIG_HIGHMEM=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_BINFMT_MISC=m
CONFIG_MATH_EMULATION=y
CONFIG_IRQ_ALL_CPUS=y
@@ -77,36 +79,25 @@ CONFIG_INET_ESP=y
CONFIG_IPV6=y
CONFIG_IP_SCTP=m
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
CONFIG_MTD=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
-CONFIG_MTD_CFI=y
CONFIG_FTL=y
-CONFIG_MTD_GEN_PROBE=y
-CONFIG_MTD_MAP_BANK_WIDTH_1=y
-CONFIG_MTD_MAP_BANK_WIDTH_2=y
-CONFIG_MTD_MAP_BANK_WIDTH_4=y
-CONFIG_MTD_CFI_I1=y
-CONFIG_MTD_CFI_I2=y
+CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_INTELEXT=y
CONFIG_MTD_CFI_AMDSTD=y
-CONFIG_MTD_CFI_UTIL=y
CONFIG_MTD_PHYSMAP_OF=y
-CONFIG_MTD_PARTITIONS=y
-CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_M25P80=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_FSL_ELBC=y
CONFIG_MTD_NAND_FSL_IFC=y
-CONFIG_MTD_NAND_IDS=y
-CONFIG_MTD_NAND_ECC=y
-CONFIG_MTD_M25P80=y
CONFIG_PROC_DEVICETREE=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=131072
-CONFIG_MISC_DEVICES=y
CONFIG_EEPROM_LEGACY=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_ST=y
@@ -137,7 +128,6 @@ CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=2
CONFIG_SERIAL_8250_RUNTIME_UARTS=2
-CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
@@ -186,7 +176,6 @@ CONFIG_HID_SAMSUNG=y
CONFIG_HID_SONY=y
CONFIG_HID_SUNPLUS=y
CONFIG_USB=y
-CONFIG_USB_DEVICEFS=y
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_EHCI_FSL=y
@@ -232,18 +221,13 @@ CONFIG_QNX4FS_FS=m
CONFIG_SYSV_FS=m
CONFIG_UFS_FS=m
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
CONFIG_NFSD=y
-CONFIG_PARTITION_ADVANCED=y
-CONFIG_MAC_PARTITION=y
CONFIG_CRC_T10DIF=y
CONFIG_DEBUG_FS=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_INFO=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
-CONFIG_IRQ_DOMAIN_DEBUG=y
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h
index 50d82c8a037f..b3c083de17ad 100644
--- a/arch/powerpc/include/asm/cputable.h
+++ b/arch/powerpc/include/asm/cputable.h
@@ -553,9 +553,7 @@ static inline int cpu_has_feature(unsigned long feature)
& feature);
}
-#ifdef CONFIG_HAVE_HW_BREAKPOINT
#define HBP_NUM 1
-#endif /* CONFIG_HAVE_HW_BREAKPOINT */
#endif /* !__ASSEMBLY__ */
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 50ea12fd7bf5..a8bf5c673a3c 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -33,6 +33,7 @@
#include <asm/kvm_asm.h>
#include <asm/processor.h>
#include <asm/page.h>
+#include <asm/cacheflush.h>
#define KVM_MAX_VCPUS NR_CPUS
#define KVM_MAX_VCORES NR_CPUS
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 0124937a23b9..e006f0bdea95 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -219,4 +219,16 @@ void kvmppc_claim_lpid(long lpid);
void kvmppc_free_lpid(long lpid);
void kvmppc_init_lpid(unsigned long nr_lpids);
+static inline void kvmppc_mmu_flush_icache(pfn_t pfn)
+{
+ /* Clear i-cache for new pages */
+ struct page *page;
+ page = pfn_to_page(pfn);
+ if (!test_bit(PG_arch_1, &page->flags)) {
+ flush_dcache_icache_page(page);
+ set_bit(PG_arch_1, &page->flags);
+ }
+}
+
+
#endif /* __POWERPC_KVM_PPC_H__ */
diff --git a/arch/powerpc/include/asm/mpic_msgr.h b/arch/powerpc/include/asm/mpic_msgr.h
index 326d33ca55cd..d4f471fb1031 100644
--- a/arch/powerpc/include/asm/mpic_msgr.h
+++ b/arch/powerpc/include/asm/mpic_msgr.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <asm/smp.h>
+#include <asm/io.h>
struct mpic_msgr {
u32 __iomem *base;
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 53b6dfa83344..54b73a28c205 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -386,6 +386,7 @@ extern unsigned long cpuidle_disable;
enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
extern int powersave_nap; /* set if nap mode can be used in idle loop */
+extern void power7_nap(void);
#ifdef CONFIG_PSERIES_IDLE
extern void update_smt_snooze_delay(int snooze);
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 85b05c463fae..e8995727b1c1 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -76,6 +76,7 @@ int main(void)
DEFINE(SIGSEGV, SIGSEGV);
DEFINE(NMI_MASK, NMI_MASK);
DEFINE(THREAD_DSCR, offsetof(struct thread_struct, dscr));
+ DEFINE(THREAD_DSCR_INHERIT, offsetof(struct thread_struct, dscr_inherit));
#else
DEFINE(THREAD_INFO, offsetof(struct task_struct, stack));
#endif /* CONFIG_PPC64 */
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index 5b25c8060fd6..a892680668d8 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -28,6 +28,8 @@ void doorbell_setup_this_cpu(void)
void doorbell_cause_ipi(int cpu, unsigned long data)
{
+ /* Order previous accesses vs. msgsnd, which is treated as a store */
+ mb();
ppc_msgsnd(PPC_DBELL, 0, data);
}
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c
index 2d7bb8ced136..e4897523de41 100644
--- a/arch/powerpc/kernel/dma-iommu.c
+++ b/arch/powerpc/kernel/dma-iommu.c
@@ -83,11 +83,10 @@ static int dma_iommu_dma_supported(struct device *dev, u64 mask)
return 0;
}
- if ((tbl->it_offset + tbl->it_size) > (mask >> IOMMU_PAGE_SHIFT)) {
- dev_info(dev, "Warning: IOMMU window too big for device mask\n");
- dev_info(dev, "mask: 0x%08llx, table end: 0x%08lx\n",
- mask, (tbl->it_offset + tbl->it_size) <<
- IOMMU_PAGE_SHIFT);
+ if (tbl->it_offset > (mask >> IOMMU_PAGE_SHIFT)) {
+ dev_info(dev, "Warning: IOMMU offset too big for device mask\n");
+ dev_info(dev, "mask: 0x%08llx, table offset: 0x%08lx\n",
+ mask, tbl->it_offset << IOMMU_PAGE_SHIFT);
return 0;
} else
return 1;
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 4b01a25e29ef..b40e0b4815b3 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -370,6 +370,12 @@ _GLOBAL(ret_from_fork)
li r3,0
b syscall_exit
+ .section ".toc","aw"
+DSCR_DEFAULT:
+ .tc dscr_default[TC],dscr_default
+
+ .section ".text"
+
/*
* This routine switches between two different tasks. The process
* state of one is saved on its kernel stack. Then the state
@@ -509,9 +515,6 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
mr r1,r8 /* start using new stack pointer */
std r7,PACAKSAVE(r13)
- ld r6,_CCR(r1)
- mtcrf 0xFF,r6
-
#ifdef CONFIG_ALTIVEC
BEGIN_FTR_SECTION
ld r0,THREAD_VRSAVE(r4)
@@ -520,14 +523,22 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
#endif /* CONFIG_ALTIVEC */
#ifdef CONFIG_PPC64
BEGIN_FTR_SECTION
+ lwz r6,THREAD_DSCR_INHERIT(r4)
+ ld r7,DSCR_DEFAULT@toc(2)
ld r0,THREAD_DSCR(r4)
- cmpd r0,r25
- beq 1f
+ cmpwi r6,0
+ bne 1f
+ ld r0,0(r7)
+1: cmpd r0,r25
+ beq 2f
mtspr SPRN_DSCR,r0
-1:
+2:
END_FTR_SECTION_IFSET(CPU_FTR_DSCR)
#endif
+ ld r6,_CCR(r1)
+ mtcrf 0xFF,r6
+
/* r3-r13 are destroyed -- Cort */
REST_8GPRS(14, r1)
REST_10GPRS(22, r1)
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index e894515e77bb..39aa97d3ff88 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -186,7 +186,7 @@ hardware_interrupt_hv:
KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0x800)
MASKABLE_EXCEPTION_PSERIES(0x900, 0x900, decrementer)
- MASKABLE_EXCEPTION_HV(0x980, 0x982, decrementer)
+ STD_EXCEPTION_HV(0x980, 0x982, hdecrementer)
STD_EXCEPTION_PSERIES(0xa00, 0xa00, trap_0a)
KVM_HANDLER_PR(PACA_EXGEN, EXC_STD, 0xa00)
@@ -486,6 +486,7 @@ machine_check_common:
STD_EXCEPTION_COMMON_ASYNC(0x500, hardware_interrupt, do_IRQ)
STD_EXCEPTION_COMMON_ASYNC(0x900, decrementer, .timer_interrupt)
+ STD_EXCEPTION_COMMON(0x980, hdecrementer, .hdec_interrupt)
STD_EXCEPTION_COMMON(0xa00, trap_0a, .unknown_exception)
STD_EXCEPTION_COMMON(0xb00, trap_0b, .unknown_exception)
STD_EXCEPTION_COMMON(0xd00, single_step, .single_step_exception)
diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c
index f3a82dde61db..956a4c496de9 100644
--- a/arch/powerpc/kernel/hw_breakpoint.c
+++ b/arch/powerpc/kernel/hw_breakpoint.c
@@ -253,7 +253,7 @@ int __kprobes hw_breakpoint_handler(struct die_args *args)
/* Do not emulate user-space instructions, instead single-step them */
if (user_mode(regs)) {
- bp->ctx->task->thread.last_hit_ubp = bp;
+ current->thread.last_hit_ubp = bp;
regs->msr |= MSR_SE;
goto out;
}
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index 7140d838339e..e11863f4e595 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -28,7 +28,9 @@ _GLOBAL(power7_idle)
lwz r4,ADDROFF(powersave_nap)(r3)
cmpwi 0,r4,0
beqlr
+ /* fall through */
+_GLOBAL(power7_nap)
/* NAP is a state loss, we create a regs frame on the
* stack, fill it up with the state we care about and
* stick a pointer to it in PACAR1. We really only
diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c
index 782bd0a3c2f0..c470a40b29f5 100644
--- a/arch/powerpc/kernel/kgdb.c
+++ b/arch/powerpc/kernel/kgdb.c
@@ -25,6 +25,7 @@
#include <asm/processor.h>
#include <asm/machdep.h>
#include <asm/debug.h>
+#include <linux/slab.h>
/*
* This table contains the mapping between PowerPC hardware trap types, and
@@ -101,6 +102,21 @@ static int computeSignal(unsigned int tt)
return SIGHUP; /* default for things we don't know about */
}
+/**
+ *
+ * kgdb_skipexception - Bail out of KGDB when we've been triggered.
+ * @exception: Exception vector number
+ * @regs: Current &struct pt_regs.
+ *
+ * On some architectures we need to skip a breakpoint exception when
+ * it occurs after a breakpoint has been removed.
+ *
+ */
+int kgdb_skipexception(int exception, struct pt_regs *regs)
+{
+ return kgdb_isremovedbreak(regs->nip);
+}
+
static int kgdb_call_nmi_hook(struct pt_regs *regs)
{
kgdb_nmicallback(raw_smp_processor_id(), regs);
@@ -138,6 +154,8 @@ static int kgdb_handle_breakpoint(struct pt_regs *regs)
static int kgdb_singlestep(struct pt_regs *regs)
{
struct thread_info *thread_info, *exception_thread_info;
+ struct thread_info *backup_current_thread_info = \
+ (struct thread_info *)kmalloc(sizeof(struct thread_info), GFP_KERNEL);
if (user_mode(regs))
return 0;
@@ -155,13 +173,17 @@ static int kgdb_singlestep(struct pt_regs *regs)
thread_info = (struct thread_info *)(regs->gpr[1] & ~(THREAD_SIZE-1));
exception_thread_info = current_thread_info();
- if (thread_info != exception_thread_info)
+ if (thread_info != exception_thread_info) {
+ /* Save the original current_thread_info. */
+ memcpy(backup_current_thread_info, exception_thread_info, sizeof *thread_info);
memcpy(exception_thread_info, thread_info, sizeof *thread_info);
+ }
kgdb_handle_exception(0, SIGTRAP, 0, regs);
if (thread_info != exception_thread_info)
- memcpy(thread_info, exception_thread_info, sizeof *thread_info);
+ /* Restore current_thread_info lastly. */
+ memcpy(exception_thread_info, backup_current_thread_info, sizeof *thread_info);
return 1;
}
@@ -410,7 +432,6 @@ int kgdb_arch_handle_exception(int vector, int signo, int err_code,
#else
linux_regs->msr |= MSR_SE;
#endif
- kgdb_single_step = 1;
atomic_set(&kgdb_cpu_doing_single_step,
raw_smp_processor_id());
}
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 710f400476de..1a1f2ddfb581 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -802,16 +802,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
#endif /* CONFIG_PPC_STD_MMU_64 */
#ifdef CONFIG_PPC64
if (cpu_has_feature(CPU_FTR_DSCR)) {
- if (current->thread.dscr_inherit) {
- p->thread.dscr_inherit = 1;
- p->thread.dscr = current->thread.dscr;
- } else if (0 != dscr_default) {
- p->thread.dscr_inherit = 1;
- p->thread.dscr = dscr_default;
- } else {
- p->thread.dscr_inherit = 0;
- p->thread.dscr = 0;
- }
+ p->thread.dscr_inherit = current->thread.dscr_inherit;
+ p->thread.dscr = current->thread.dscr;
}
#endif
diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c
index 0321007086f7..8d4214afc21d 100644
--- a/arch/powerpc/kernel/smp.c
+++ b/arch/powerpc/kernel/smp.c
@@ -198,8 +198,15 @@ void smp_muxed_ipi_message_pass(int cpu, int msg)
struct cpu_messages *info = &per_cpu(ipi_message, cpu);
char *message = (char *)&info->messages;
+ /*
+ * Order previous accesses before accesses in the IPI handler.
+ */
+ smp_mb();
message[msg] = 1;
- mb();
+ /*
+ * cause_ipi functions are required to include a full barrier
+ * before doing whatever causes the IPI.
+ */
smp_ops->cause_ipi(cpu, info->data);
}
@@ -211,7 +218,7 @@ irqreturn_t smp_ipi_demux(void)
mb(); /* order any irq clear */
do {
- all = xchg_local(&info->messages, 0);
+ all = xchg(&info->messages, 0);
#ifdef __BIG_ENDIAN
if (all & (1 << (24 - 8 * PPC_MSG_CALL_FUNCTION)))
diff --git a/arch/powerpc/kernel/syscalls.c b/arch/powerpc/kernel/syscalls.c
index f2496f2faecc..4e3cc47f26b9 100644
--- a/arch/powerpc/kernel/syscalls.c
+++ b/arch/powerpc/kernel/syscalls.c
@@ -107,11 +107,11 @@ long ppc64_personality(unsigned long personality)
long ret;
if (personality(current->personality) == PER_LINUX32
- && personality == PER_LINUX)
- personality = PER_LINUX32;
+ && personality(personality) == PER_LINUX)
+ personality = (personality & ~PER_MASK) | PER_LINUX32;
ret = sys_personality(personality);
- if (ret == PER_LINUX32)
- ret = PER_LINUX;
+ if (personality(ret) == PER_LINUX32)
+ ret = (ret & ~PER_MASK) | PER_LINUX;
return ret;
}
#endif
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index 3529446c2abd..8302af649219 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -194,6 +194,14 @@ static ssize_t show_dscr_default(struct device *dev,
return sprintf(buf, "%lx\n", dscr_default);
}
+static void update_dscr(void *dummy)
+{
+ if (!current->thread.dscr_inherit) {
+ current->thread.dscr = dscr_default;
+ mtspr(SPRN_DSCR, dscr_default);
+ }
+}
+
static ssize_t __used store_dscr_default(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
@@ -206,6 +214,8 @@ static ssize_t __used store_dscr_default(struct device *dev,
return -EINVAL;
dscr_default = val;
+ on_each_cpu(update_dscr, NULL, 1);
+
return count;
}
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index be171ee73bf8..e49e93191b69 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -535,6 +535,15 @@ void timer_interrupt(struct pt_regs * regs)
trace_timer_interrupt_exit(regs);
}
+/*
+ * Hypervisor decrementer interrupts shouldn't occur but are sometimes
+ * left pending on exit from a KVM guest. We don't need to do anything
+ * to clear them, as they are edge-triggered.
+ */
+void hdec_interrupt(struct pt_regs *regs)
+{
+}
+
#ifdef CONFIG_SUSPEND
static void generic_suspend_disable_irqs(void)
{
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
index 158972341a2d..ae0843fa7a61 100644
--- a/arch/powerpc/kernel/traps.c
+++ b/arch/powerpc/kernel/traps.c
@@ -972,8 +972,9 @@ static int emulate_instruction(struct pt_regs *regs)
cpu_has_feature(CPU_FTR_DSCR)) {
PPC_WARN_EMULATED(mtdscr, regs);
rd = (instword >> 21) & 0x1f;
- mtspr(SPRN_DSCR, regs->gpr[rd]);
+ current->thread.dscr = regs->gpr[rd];
current->thread.dscr_inherit = 1;
+ mtspr(SPRN_DSCR, current->thread.dscr);
return 0;
}
#endif
diff --git a/arch/powerpc/kvm/book3s_32_mmu_host.c b/arch/powerpc/kvm/book3s_32_mmu_host.c
index f922c29bb234..837f13e7b6bf 100644
--- a/arch/powerpc/kvm/book3s_32_mmu_host.c
+++ b/arch/powerpc/kvm/book3s_32_mmu_host.c
@@ -211,6 +211,9 @@ next_pteg:
pteg1 |= PP_RWRX;
}
+ if (orig_pte->may_execute)
+ kvmppc_mmu_flush_icache(hpaddr >> PAGE_SHIFT);
+
local_irq_disable();
if (pteg[rr]) {
diff --git a/arch/powerpc/kvm/book3s_64_mmu_host.c b/arch/powerpc/kvm/book3s_64_mmu_host.c
index 10fc8ec9d2a8..0688b6b39585 100644
--- a/arch/powerpc/kvm/book3s_64_mmu_host.c
+++ b/arch/powerpc/kvm/book3s_64_mmu_host.c
@@ -126,6 +126,8 @@ int kvmppc_mmu_map_page(struct kvm_vcpu *vcpu, struct kvmppc_pte *orig_pte)
if (!orig_pte->may_execute)
rflags |= HPTE_R_N;
+ else
+ kvmppc_mmu_flush_icache(hpaddr >> PAGE_SHIFT);
hash = hpt_hash(va, PTE_SIZE, MMU_SEGSIZE_256M);
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 5a84c8d3d040..44b72feaff7d 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -1421,13 +1421,13 @@ _GLOBAL(kvmppc_h_cede)
sync /* order setting ceded vs. testing prodded */
lbz r5,VCPU_PRODDED(r3)
cmpwi r5,0
- bne 1f
+ bne kvm_cede_prodded
li r0,0 /* set trap to 0 to say hcall is handled */
stw r0,VCPU_TRAP(r3)
li r0,H_SUCCESS
std r0,VCPU_GPR(R3)(r3)
BEGIN_FTR_SECTION
- b 2f /* just send it up to host on 970 */
+ b kvm_cede_exit /* just send it up to host on 970 */
END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
/*
@@ -1446,7 +1446,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
or r4,r4,r0
PPC_POPCNTW(R7,R4)
cmpw r7,r8
- bge 2f
+ bge kvm_cede_exit
stwcx. r4,0,r6
bne 31b
li r0,1
@@ -1555,7 +1555,8 @@ kvm_end_cede:
b hcall_real_fallback
/* cede when already previously prodded case */
-1: li r0,0
+kvm_cede_prodded:
+ li r0,0
stb r0,VCPU_PRODDED(r3)
sync /* order testing prodded vs. clearing ceded */
stb r0,VCPU_CEDED(r3)
@@ -1563,7 +1564,8 @@ kvm_end_cede:
blr
/* we've ceded but we want to give control to the host */
-2: li r3,H_TOO_HARD
+kvm_cede_exit:
+ li r3,H_TOO_HARD
blr
secondary_too_late:
diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c
index c510fc961302..a2b66717813d 100644
--- a/arch/powerpc/kvm/e500_tlb.c
+++ b/arch/powerpc/kvm/e500_tlb.c
@@ -322,11 +322,11 @@ static inline void kvmppc_e500_ref_release(struct tlbe_ref *ref)
static void clear_tlb1_bitmap(struct kvmppc_vcpu_e500 *vcpu_e500)
{
if (vcpu_e500->g2h_tlb1_map)
- memset(vcpu_e500->g2h_tlb1_map,
- sizeof(u64) * vcpu_e500->gtlb_params[1].entries, 0);
+ memset(vcpu_e500->g2h_tlb1_map, 0,
+ sizeof(u64) * vcpu_e500->gtlb_params[1].entries);
if (vcpu_e500->h2g_tlb1_rmap)
- memset(vcpu_e500->h2g_tlb1_rmap,
- sizeof(unsigned int) * host_tlb_params[1].entries, 0);
+ memset(vcpu_e500->h2g_tlb1_rmap, 0,
+ sizeof(unsigned int) * host_tlb_params[1].entries);
}
static void clear_tlb_privs(struct kvmppc_vcpu_e500 *vcpu_e500)
@@ -539,6 +539,9 @@ static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
kvmppc_e500_setup_stlbe(&vcpu_e500->vcpu, gtlbe, tsize,
ref, gvaddr, stlbe);
+
+ /* Clear i-cache for new pages */
+ kvmppc_mmu_flush_icache(pfn);
}
/* XXX only map the one-one case, for now use TLB0 */
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index dd223b3eb333..17e5b2364312 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -20,7 +20,7 @@ int patch_instruction(unsigned int *addr, unsigned int instr)
{
int err;
- err = __put_user(instr, addr);
+ __put_user_size(instr, addr, 4, err);
if (err)
return err;
asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr));
diff --git a/arch/powerpc/lib/copyuser_power7.S b/arch/powerpc/lib/copyuser_power7.S
index f9ede7c6606e..0d24ff15f5f6 100644
--- a/arch/powerpc/lib/copyuser_power7.S
+++ b/arch/powerpc/lib/copyuser_power7.S
@@ -288,7 +288,7 @@ err1; stb r0,0(r3)
std r0,16(r1)
stdu r1,-STACKFRAMESIZE(r1)
bl .enter_vmx_usercopy
- cmpwi r3,0
+ cmpwi cr1,r3,0
ld r0,STACKFRAMESIZE+16(r1)
ld r3,STACKFRAMESIZE+48(r1)
ld r4,STACKFRAMESIZE+56(r1)
@@ -326,38 +326,7 @@ err1; stb r0,0(r3)
dcbt r0,r8,0b01010 /* GO */
.machine pop
- /*
- * We prefetch both the source and destination using enhanced touch
- * instructions. We use a stream ID of 0 for the load side and
- * 1 for the store side.
- */
- clrrdi r6,r4,7
- clrrdi r9,r3,7
- ori r9,r9,1 /* stream=1 */
-
- srdi r7,r5,7 /* length in cachelines, capped at 0x3FF */
- cmpldi cr1,r7,0x3FF
- ble cr1,1f
- li r7,0x3FF
-1: lis r0,0x0E00 /* depth=7 */
- sldi r7,r7,7
- or r7,r7,r0
- ori r10,r7,1 /* stream=1 */
-
- lis r8,0x8000 /* GO=1 */
- clrldi r8,r8,32
-
-.machine push
-.machine "power4"
- dcbt r0,r6,0b01000
- dcbt r0,r7,0b01010
- dcbtst r0,r9,0b01000
- dcbtst r0,r10,0b01010
- eieio
- dcbt r0,r8,0b01010 /* GO */
-.machine pop
-
- beq .Lunwind_stack_nonvmx_copy
+ beq cr1,.Lunwind_stack_nonvmx_copy
/*
* If source and destination are not relatively aligned we use a
diff --git a/arch/powerpc/lib/memcpy_power7.S b/arch/powerpc/lib/memcpy_power7.S
index 0efdc51bc716..7ba6c96de778 100644
--- a/arch/powerpc/lib/memcpy_power7.S
+++ b/arch/powerpc/lib/memcpy_power7.S
@@ -222,7 +222,7 @@ _GLOBAL(memcpy_power7)
std r0,16(r1)
stdu r1,-STACKFRAMESIZE(r1)
bl .enter_vmx_copy
- cmpwi r3,0
+ cmpwi cr1,r3,0
ld r0,STACKFRAMESIZE+16(r1)
ld r3,STACKFRAMESIZE+48(r1)
ld r4,STACKFRAMESIZE+56(r1)
@@ -260,7 +260,7 @@ _GLOBAL(memcpy_power7)
dcbt r0,r8,0b01010 /* GO */
.machine pop
- beq .Lunwind_stack_nonvmx_copy
+ beq cr1,.Lunwind_stack_nonvmx_copy
/*
* If source and destination are not relatively aligned we use a
diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c
index baaafde7d135..fbdad0e3929a 100644
--- a/arch/powerpc/mm/mem.c
+++ b/arch/powerpc/mm/mem.c
@@ -469,6 +469,7 @@ void flush_dcache_icache_page(struct page *page)
__flush_dcache_icache_phys(page_to_pfn(page) << PAGE_SHIFT);
#endif
}
+EXPORT_SYMBOL(flush_dcache_icache_page);
void clear_user_page(void *page, unsigned long vaddr, struct page *pg)
{
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index 39b159751c35..59213cfaeca9 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1436,11 +1436,11 @@ static long vphn_get_associativity(unsigned long cpu,
/*
* Update the node maps and sysfs entries for each cpu whose home node
- * has changed.
+ * has changed. Returns 1 when the topology has changed, and 0 otherwise.
*/
int arch_update_cpu_topology(void)
{
- int cpu, nid, old_nid;
+ int cpu, nid, old_nid, changed = 0;
unsigned int associativity[VPHN_ASSOC_BUFSIZE] = {0};
struct device *dev;
@@ -1466,9 +1466,10 @@ int arch_update_cpu_topology(void)
dev = get_cpu_device(cpu);
if (dev)
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+ changed = 1;
}
- return 1;
+ return changed;
}
static void topology_work_fn(struct work_struct *work)
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index 77b49ddda9d3..7cd2dbd6e4c4 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -1431,7 +1431,7 @@ static void perf_event_interrupt(struct pt_regs *regs)
if (!event->hw.idx || is_limited_pmc(event->hw.idx))
continue;
val = read_pmc(event->hw.idx);
- if ((int)val < 0) {
+ if (pmc_overflow(val)) {
/* event has overflowed */
found = 1;
record_and_restart(event, val, regs);
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index d544d7816df3..dba1ce235da5 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -186,10 +186,13 @@ static void spufs_prune_dir(struct dentry *dir)
static int spufs_rmdir(struct inode *parent, struct dentry *dir)
{
/* remove all entries */
+ int res;
spufs_prune_dir(dir);
d_drop(dir);
-
- return simple_rmdir(parent, dir);
+ res = simple_rmdir(parent, dir);
+ /* We have to give up the mm_struct */
+ spu_forget(SPUFS_I(dir->d_inode)->i_ctx);
+ return res;
}
static int spufs_fill_dir(struct dentry *dir,
@@ -245,9 +248,6 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
mutex_unlock(&parent->i_mutex);
WARN_ON(ret);
- /* We have to give up the mm_struct */
- spu_forget(ctx);
-
return dcache_dir_close(inode, file);
}
@@ -450,28 +450,24 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
struct spu_context *neighbor;
struct path path = {.mnt = mnt, .dentry = dentry};
- ret = -EPERM;
if ((flags & SPU_CREATE_NOSCHED) &&
!capable(CAP_SYS_NICE))
- goto out_unlock;
+ return -EPERM;
- ret = -EINVAL;
if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
== SPU_CREATE_ISOLATE)
- goto out_unlock;
+ return -EINVAL;
- ret = -ENODEV;
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
- goto out_unlock;
+ return -ENODEV;
gang = NULL;
neighbor = NULL;
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
if (affinity) {
gang = SPUFS_I(inode)->i_gang;
- ret = -EINVAL;
if (!gang)
- goto out_unlock;
+ return -EINVAL;
mutex_lock(&gang->aff_mutex);
neighbor = spufs_assert_affinity(flags, gang, aff_filp);
if (IS_ERR(neighbor)) {
@@ -492,22 +488,12 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
}
ret = spufs_context_open(&path);
- if (ret < 0) {
+ if (ret < 0)
WARN_ON(spufs_rmdir(inode, dentry));
- if (affinity)
- mutex_unlock(&gang->aff_mutex);
- mutex_unlock(&inode->i_mutex);
- spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
- goto out;
- }
out_aff_unlock:
if (affinity)
mutex_unlock(&gang->aff_mutex);
-out_unlock:
- mutex_unlock(&inode->i_mutex);
-out:
- dput(dentry);
return ret;
}
@@ -580,18 +566,13 @@ static int spufs_create_gang(struct inode *inode,
int ret;
ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
- if (ret)
- goto out;
-
- ret = spufs_gang_open(&path);
- if (ret < 0) {
- int err = simple_rmdir(inode, dentry);
- WARN_ON(err);
+ if (!ret) {
+ ret = spufs_gang_open(&path);
+ if (ret < 0) {
+ int err = simple_rmdir(inode, dentry);
+ WARN_ON(err);
+ }
}
-
-out:
- mutex_unlock(&inode->i_mutex);
- dput(dentry);
return ret;
}
@@ -601,40 +582,32 @@ static struct file_system_type spufs_type;
long spufs_create(struct path *path, struct dentry *dentry,
unsigned int flags, umode_t mode, struct file *filp)
{
+ struct inode *dir = path->dentry->d_inode;
int ret;
- ret = -EINVAL;
/* check if we are on spufs */
if (path->dentry->d_sb->s_type != &spufs_type)
- goto out;
+ return -EINVAL;
/* don't accept undefined flags */
if (flags & (~SPU_CREATE_FLAG_ALL))
- goto out;
+ return -EINVAL;
/* only threads can be underneath a gang */
- if (path->dentry != path->dentry->d_sb->s_root) {
- if ((flags & SPU_CREATE_GANG) ||
- !SPUFS_I(path->dentry->d_inode)->i_gang)
- goto out;
- }
+ if (path->dentry != path->dentry->d_sb->s_root)
+ if ((flags & SPU_CREATE_GANG) || !SPUFS_I(dir)->i_gang)
+ return -EINVAL;
mode &= ~current_umask();
if (flags & SPU_CREATE_GANG)
- ret = spufs_create_gang(path->dentry->d_inode,
- dentry, path->mnt, mode);
+ ret = spufs_create_gang(dir, dentry, path->mnt, mode);
else
- ret = spufs_create_context(path->dentry->d_inode,
- dentry, path->mnt, flags, mode,
+ ret = spufs_create_context(dir, dentry, path->mnt, flags, mode,
filp);
if (ret >= 0)
- fsnotify_mkdir(path->dentry->d_inode, dentry);
- return ret;
+ fsnotify_mkdir(dir, dentry);
-out:
- mutex_unlock(&path->dentry->d_inode->i_mutex);
- dput(dentry);
return ret;
}
diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c
index 5665dcc382c7..5b7d8ffbf890 100644
--- a/arch/powerpc/platforms/cell/spufs/syscalls.c
+++ b/arch/powerpc/platforms/cell/spufs/syscalls.c
@@ -70,7 +70,7 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
ret = PTR_ERR(dentry);
if (!IS_ERR(dentry)) {
ret = spufs_create(&path, dentry, flags, mode, neighbor);
- path_put(&path);
+ done_path_create(&path, dentry);
}
return ret;
diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c
index 3ef46254c35b..7698b6e13c57 100644
--- a/arch/powerpc/platforms/powernv/smp.c
+++ b/arch/powerpc/platforms/powernv/smp.c
@@ -106,14 +106,6 @@ static void pnv_smp_cpu_kill_self(void)
{
unsigned int cpu;
- /* If powersave_nap is enabled, use NAP mode, else just
- * spin aimlessly
- */
- if (!powersave_nap) {
- generic_mach_cpu_die();
- return;
- }
-
/* Standard hot unplug procedure */
local_irq_disable();
idle_task_exit();
@@ -128,7 +120,7 @@ static void pnv_smp_cpu_kill_self(void)
*/
mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1);
while (!generic_check_cpu_restart(cpu)) {
- power7_idle();
+ power7_nap();
if (!generic_check_cpu_restart(cpu)) {
DBG("CPU%d Unexpected exit while offline !\n", cpu);
/* We may be getting an IPI, so we re-enable
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index a7b2a600d0a4..c37f46136321 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -465,7 +465,7 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
iounmap(hose->cfg_data);
iounmap(hose->cfg_addr);
pcibios_free_controller(hose);
- return 0;
+ return -ENODEV;
}
setup_pci_cmd(hose);
@@ -827,6 +827,7 @@ struct device_node *fsl_pci_primary;
void __devinit fsl_pci_init(void)
{
+ int ret;
struct device_node *node;
struct pci_controller *hose;
dma_addr_t max = 0xffffffff;
@@ -855,10 +856,12 @@ void __devinit fsl_pci_init(void)
if (!fsl_pci_primary)
fsl_pci_primary = node;
- fsl_add_bridge(node, fsl_pci_primary == node);
- hose = pci_find_hose_for_OF_device(node);
- max = min(max, hose->dma_window_base_cur +
- hose->dma_window_size);
+ ret = fsl_add_bridge(node, fsl_pci_primary == node);
+ if (ret == 0) {
+ hose = pci_find_hose_for_OF_device(node);
+ max = min(max, hose->dma_window_base_cur +
+ hose->dma_window_size);
+ }
}
}
diff --git a/arch/powerpc/sysdev/mpic_msgr.c b/arch/powerpc/sysdev/mpic_msgr.c
index 483d8fa72e8b..e961f8c4a8f0 100644
--- a/arch/powerpc/sysdev/mpic_msgr.c
+++ b/arch/powerpc/sysdev/mpic_msgr.c
@@ -14,6 +14,9 @@
#include <linux/list.h>
#include <linux/of_platform.h>
#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
#include <asm/prom.h>
#include <asm/hw_irq.h>
#include <asm/ppc-pci.h>
diff --git a/arch/powerpc/sysdev/xics/icp-hv.c b/arch/powerpc/sysdev/xics/icp-hv.c
index 253dce98c16e..df0fc5821469 100644
--- a/arch/powerpc/sysdev/xics/icp-hv.c
+++ b/arch/powerpc/sysdev/xics/icp-hv.c
@@ -65,7 +65,11 @@ static inline void icp_hv_set_xirr(unsigned int value)
static inline void icp_hv_set_qirr(int n_cpu , u8 value)
{
int hw_cpu = get_hard_smp_processor_id(n_cpu);
- long rc = plpar_hcall_norets(H_IPI, hw_cpu, value);
+ long rc;
+
+ /* Make sure all previous accesses are ordered before IPI sending */
+ mb();
+ rc = plpar_hcall_norets(H_IPI, hw_cpu, value);
if (rc != H_SUCCESS) {
pr_err("%s: bad return code qirr cpu=%d hw_cpu=%d mfrr=0x%x "
"returned %ld\n", __func__, n_cpu, hw_cpu, value, rc);
@@ -111,7 +115,7 @@ static unsigned int icp_hv_get_irq(void)
if (vec == XICS_IRQ_SPURIOUS)
return NO_IRQ;
- irq = irq_radix_revmap_lookup(xics_host, vec);
+ irq = irq_find_mapping(xics_host, vec);
if (likely(irq != NO_IRQ)) {
xics_push_cppr(vec);
return irq;
diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c
index 4c79b6fbee1c..48861d3fcd07 100644
--- a/arch/powerpc/sysdev/xics/icp-native.c
+++ b/arch/powerpc/sysdev/xics/icp-native.c
@@ -119,7 +119,7 @@ static unsigned int icp_native_get_irq(void)
if (vec == XICS_IRQ_SPURIOUS)
return NO_IRQ;
- irq = irq_radix_revmap_lookup(xics_host, vec);
+ irq = irq_find_mapping(xics_host, vec);
if (likely(irq != NO_IRQ)) {
xics_push_cppr(vec);
return irq;
diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c
index cd1d18db92c6..9049d9f44485 100644
--- a/arch/powerpc/sysdev/xics/xics-common.c
+++ b/arch/powerpc/sysdev/xics/xics-common.c
@@ -329,9 +329,6 @@ static int xics_host_map(struct irq_domain *h, unsigned int virq,
pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw);
- /* Insert the interrupt mapping into the radix tree for fast lookup */
- irq_radix_revmap_insert(xics_host, virq, hw);
-
/* They aren't all level sensitive but we just don't really know */
irq_set_status_flags(virq, IRQ_LEVEL);
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index eab3492a45c5..9b49c65ee7a4 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -17,6 +17,7 @@
#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/kallsyms.h>
+#include <linux/kmsg_dump.h>
#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/sysrq.h>
@@ -894,13 +895,13 @@ cmds(struct pt_regs *excp)
#endif
default:
printf("Unrecognized command: ");
- do {
+ do {
if (' ' < cmd && cmd <= '~')
putchar(cmd);
else
printf("\\x%x", cmd);
cmd = inchar();
- } while (cmd != '\n');
+ } while (cmd != '\n');
printf(" (type ? for help)\n");
break;
}
@@ -1097,7 +1098,7 @@ static long check_bp_loc(unsigned long addr)
return 1;
}
-static char *breakpoint_help_string =
+static char *breakpoint_help_string =
"Breakpoint command usage:\n"
"b show breakpoints\n"
"b <addr> [cnt] set breakpoint at given instr addr\n"
@@ -1193,7 +1194,7 @@ bpt_cmds(void)
default:
termch = cmd;
- cmd = skipbl();
+ cmd = skipbl();
if (cmd == '?') {
printf(breakpoint_help_string);
break;
@@ -1359,7 +1360,7 @@ static void xmon_show_stack(unsigned long sp, unsigned long lr,
sp + REGS_OFFSET);
break;
}
- printf("--- Exception: %lx %s at ", regs.trap,
+ printf("--- Exception: %lx %s at ", regs.trap,
getvecname(TRAP(&regs)));
pc = regs.nip;
lr = regs.link;
@@ -1623,14 +1624,14 @@ static void super_regs(void)
cmd = skipbl();
if (cmd == '\n') {
- unsigned long sp, toc;
+ unsigned long sp, toc;
asm("mr %0,1" : "=r" (sp) :);
asm("mr %0,2" : "=r" (toc) :);
printf("msr = "REG" sprg0= "REG"\n",
mfmsr(), mfspr(SPRN_SPRG0));
printf("pvr = "REG" sprg1= "REG"\n",
- mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
+ mfspr(SPRN_PVR), mfspr(SPRN_SPRG1));
printf("dec = "REG" sprg2= "REG"\n",
mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
@@ -1783,7 +1784,7 @@ byterev(unsigned char *val, int size)
static int brev;
static int mnoread;
-static char *memex_help_string =
+static char *memex_help_string =
"Memory examine command usage:\n"
"m [addr] [flags] examine/change memory\n"
" addr is optional. will start where left off.\n"
@@ -1798,7 +1799,7 @@ static char *memex_help_string =
"NOTE: flags are saved as defaults\n"
"";
-static char *memex_subcmd_help_string =
+static char *memex_subcmd_help_string =
"Memory examine subcommands:\n"
" hexval write this val to current location\n"
" 'string' write chars from string to this location\n"
@@ -2064,7 +2065,7 @@ prdump(unsigned long adrs, long ndump)
nr = mread(adrs, temp, r);
adrs += nr;
for (m = 0; m < r; ++m) {
- if ((m & (sizeof(long) - 1)) == 0 && m > 0)
+ if ((m & (sizeof(long) - 1)) == 0 && m > 0)
putchar(' ');
if (m < nr)
printf("%.2x", temp[m]);
@@ -2072,7 +2073,7 @@ prdump(unsigned long adrs, long ndump)
printf("%s", fault_chars[fault_type]);
}
for (; m < 16; ++m) {
- if ((m & (sizeof(long) - 1)) == 0)
+ if ((m & (sizeof(long) - 1)) == 0)
putchar(' ');
printf(" ");
}
@@ -2148,45 +2149,28 @@ print_address(unsigned long addr)
void
dump_log_buf(void)
{
- const unsigned long size = 128;
- unsigned long end, addr;
- unsigned char buf[size + 1];
-
- addr = 0;
- buf[size] = '\0';
-
- if (setjmp(bus_error_jmp) != 0) {
- printf("Unable to lookup symbol __log_buf!\n");
- return;
- }
-
- catch_memory_errors = 1;
- sync();
- addr = kallsyms_lookup_name("__log_buf");
-
- if (! addr)
- printf("Symbol __log_buf not found!\n");
- else {
- end = addr + (1 << CONFIG_LOG_BUF_SHIFT);
- while (addr < end) {
- if (! mread(addr, buf, size)) {
- printf("Can't read memory at address 0x%lx\n", addr);
- break;
- }
-
- printf("%s", buf);
-
- if (strlen(buf) < size)
- break;
-
- addr += size;
- }
- }
-
- sync();
- /* wait a little while to see if we get a machine check */
- __delay(200);
- catch_memory_errors = 0;
+ struct kmsg_dumper dumper = { .active = 1 };
+ unsigned char buf[128];
+ size_t len;
+
+ if (setjmp(bus_error_jmp) != 0) {
+ printf("Error dumping printk buffer!\n");
+ return;
+ }
+
+ catch_memory_errors = 1;
+ sync();
+
+ kmsg_dump_rewind_nolock(&dumper);
+ while (kmsg_dump_get_line_nolock(&dumper, false, buf, sizeof(buf), &len)) {
+ buf[len] = '\0';
+ printf("%s", buf);
+ }
+
+ sync();
+ /* wait a little while to see if we get a machine check */
+ __delay(200);
+ catch_memory_errors = 0;
}
/*
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 76de6b68487c..107610e01a29 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -124,6 +124,7 @@ config S390
select GENERIC_TIME_VSYSCALL
select GENERIC_CLOCKEVENTS
select KTIME_SCALAR if 32BIT
+ select HAVE_ARCH_SECCOMP_FILTER
config SCHED_OMIT_FRAME_POINTER
def_bool y
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index 967923dea98d..f39cd710980b 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -16,7 +16,7 @@ CONFIG_CGROUPS=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index 32e8449640fa..9b94a160fe7f 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -180,7 +180,8 @@ extern char elf_platform[];
#define ELF_PLATFORM (elf_platform)
#ifndef CONFIG_64BIT
-#define SET_PERSONALITY(ex) set_personality(PER_LINUX)
+#define SET_PERSONALITY(ex) \
+ set_personality(PER_LINUX | (current->personality & (~PER_MASK)))
#else /* CONFIG_64BIT */
#define SET_PERSONALITY(ex) \
do { \
diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h
index 799ed0f1643d..2d6e6e380564 100644
--- a/arch/s390/include/asm/hugetlb.h
+++ b/arch/s390/include/asm/hugetlb.h
@@ -66,16 +66,6 @@ static inline pte_t huge_ptep_get(pte_t *ptep)
return pte;
}
-static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
-{
- pte_t pte = huge_ptep_get(ptep);
-
- mm->context.flush_mm = 1;
- pmd_clear((pmd_t *) ptep);
- return pte;
-}
-
static inline void __pmd_csp(pmd_t *pmdp)
{
register unsigned long reg2 asm("2") = pmd_val(*pmdp);
@@ -117,6 +107,15 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
__pmd_csp(pmdp);
}
+static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ pte_t pte = huge_ptep_get(ptep);
+
+ huge_ptep_invalidate(mm, addr, ptep);
+ return pte;
+}
+
#define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
({ \
int __changed = !pte_same(huge_ptep_get(__ptep), __entry); \
@@ -131,10 +130,7 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
({ \
pte_t __pte = huge_ptep_get(__ptep); \
if (pte_write(__pte)) { \
- (__mm)->context.flush_mm = 1; \
- if (atomic_read(&(__mm)->context.attach_count) > 1 || \
- (__mm) != current->active_mm) \
- huge_ptep_invalidate(__mm, __addr, __ptep); \
+ huge_ptep_invalidate(__mm, __addr, __ptep); \
set_huge_pte_at(__mm, __addr, __ptep, \
huge_pte_wrprotect(__pte)); \
} \
diff --git a/arch/s390/include/asm/posix_types.h b/arch/s390/include/asm/posix_types.h
index 7bcc14e395f0..bf2a2ad2f800 100644
--- a/arch/s390/include/asm/posix_types.h
+++ b/arch/s390/include/asm/posix_types.h
@@ -13,6 +13,7 @@
*/
typedef unsigned long __kernel_size_t;
+typedef long __kernel_ssize_t;
#define __kernel_size_t __kernel_size_t
typedef unsigned short __kernel_old_dev_t;
@@ -25,7 +26,6 @@ typedef unsigned short __kernel_mode_t;
typedef unsigned short __kernel_ipc_pid_t;
typedef unsigned short __kernel_uid_t;
typedef unsigned short __kernel_gid_t;
-typedef int __kernel_ssize_t;
typedef int __kernel_ptrdiff_t;
#else /* __s390x__ */
@@ -35,7 +35,6 @@ typedef unsigned int __kernel_mode_t;
typedef int __kernel_ipc_pid_t;
typedef unsigned int __kernel_uid_t;
typedef unsigned int __kernel_gid_t;
-typedef long __kernel_ssize_t;
typedef long __kernel_ptrdiff_t;
typedef unsigned long __kernel_sigset_t; /* at least 32 bits */
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index a0a8340daafa..ce26ac3cb162 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -44,6 +44,7 @@ static inline void smp_call_online_cpu(void (*func)(void *), void *data)
}
static inline int smp_find_processor_id(int address) { return 0; }
+static inline int smp_store_status(int cpu) { return 0; }
static inline int smp_vcpu_scheduled(int cpu) { return 1; }
static inline void smp_yield_cpu(int cpu) { }
static inline void smp_yield(void) { }
diff --git a/arch/s390/include/asm/sparsemem.h b/arch/s390/include/asm/sparsemem.h
index 0fb34027d3f6..a60d085ddb4d 100644
--- a/arch/s390/include/asm/sparsemem.h
+++ b/arch/s390/include/asm/sparsemem.h
@@ -4,13 +4,11 @@
#ifdef CONFIG_64BIT
#define SECTION_SIZE_BITS 28
-#define MAX_PHYSADDR_BITS 46
#define MAX_PHYSMEM_BITS 46
#else
#define SECTION_SIZE_BITS 25
-#define MAX_PHYSADDR_BITS 31
#define MAX_PHYSMEM_BITS 31
#endif /* CONFIG_64BIT */
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index fb214dd9b7e0..fe7b99759e12 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -12,6 +12,7 @@
#ifndef _ASM_SYSCALL_H
#define _ASM_SYSCALL_H 1
+#include <linux/audit.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <asm/ptrace.h>
@@ -87,4 +88,13 @@ static inline void syscall_set_arguments(struct task_struct *task,
regs->orig_gpr2 = args[0];
}
+static inline int syscall_get_arch(struct task_struct *task,
+ struct pt_regs *regs)
+{
+#ifdef CONFIG_COMPAT
+ if (test_tsk_thread_flag(task, TIF_31BIT))
+ return AUDIT_ARCH_S390;
+#endif
+ return sizeof(long) == 8 ? AUDIT_ARCH_S390X : AUDIT_ARCH_S390;
+}
#endif /* _ASM_SYSCALL_H */
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 9fde315f3a7c..1d8fe2b17ef6 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -90,12 +90,10 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
static inline void __tlb_flush_mm_cond(struct mm_struct * mm)
{
- spin_lock(&mm->page_table_lock);
if (mm->context.flush_mm) {
__tlb_flush_mm(mm);
mm->context.flush_mm = 0;
}
- spin_unlock(&mm->page_table_lock);
}
/*
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index d1225089a4bb..f606d935f495 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -620,7 +620,6 @@ asmlinkage unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg)
return -EFAULT;
if (a.offset & ~PAGE_MASK)
return -EINVAL;
- a.addr = (unsigned long) compat_ptr(a.addr);
return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
a.offset >> PAGE_SHIFT);
}
@@ -631,7 +630,6 @@ asmlinkage long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg)
if (copy_from_user(&a, arg, sizeof(a)))
return -EFAULT;
- a.addr = (unsigned long) compat_ptr(a.addr);
return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
}
diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S
index e835d6d5b7fd..2d82cfcbce5b 100644
--- a/arch/s390/kernel/compat_wrapper.S
+++ b/arch/s390/kernel/compat_wrapper.S
@@ -1635,7 +1635,7 @@ ENTRY(compat_sys_process_vm_readv_wrapper)
llgfr %r6,%r6 # unsigned long
llgf %r0,164(%r15) # unsigned long
stg %r0,160(%r15)
- jg sys_process_vm_readv
+ jg compat_sys_process_vm_readv
ENTRY(compat_sys_process_vm_writev_wrapper)
lgfr %r2,%r2 # compat_pid_t
@@ -1645,4 +1645,4 @@ ENTRY(compat_sys_process_vm_writev_wrapper)
llgfr %r6,%r6 # unsigned long
llgf %r0,164(%r15) # unsigned long
stg %r0,160(%r15)
- jg sys_process_vm_writev
+ jg compat_sys_process_vm_writev
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index f4eb37680b91..e4be113fbac6 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -719,7 +719,11 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
long ret = 0;
/* Do the secure computing check first. */
- secure_computing_strict(regs->gprs[2]);
+ if (secure_computing(regs->gprs[2])) {
+ /* seccomp failures shouldn't expose any additional code. */
+ ret = -1;
+ goto out;
+ }
/*
* The sysc_tracesys code in entry.S stored the system
@@ -745,6 +749,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
regs->gprs[2], regs->orig_gpr2,
regs->gprs[3], regs->gprs[4],
regs->gprs[5]);
+out:
return ret ?: regs->gprs[2];
}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index f86c81e13c37..40b57693de38 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -974,11 +974,13 @@ static void __init setup_hwcaps(void)
if (MACHINE_HAS_HPAGE)
elf_hwcap |= HWCAP_S390_HPAGE;
+#if defined(CONFIG_64BIT)
/*
* 64-bit register support for 31-bit processes
* HWCAP_S390_HIGH_GPRS is bit 9.
*/
elf_hwcap |= HWCAP_S390_HIGH_GPRS;
+#endif
get_cpu_id(&cpu_id);
switch (cpu_id.machine) {
diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c
index b4a29eee41b8..d0964d22adb5 100644
--- a/arch/s390/kernel/sys_s390.c
+++ b/arch/s390/kernel/sys_s390.c
@@ -81,11 +81,12 @@ SYSCALL_DEFINE1(s390_personality, unsigned int, personality)
{
unsigned int ret;
- if (current->personality == PER_LINUX32 && personality == PER_LINUX)
- personality = PER_LINUX32;
+ if (personality(current->personality) == PER_LINUX32 &&
+ personality(personality) == PER_LINUX)
+ personality |= PER_LINUX32;
ret = sys_personality(personality);
- if (ret == PER_LINUX32)
- ret = PER_LINUX;
+ if (personality(ret) == PER_LINUX32)
+ ret &= ~PER_LINUX32;
return ret;
}
diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c
index 60ee2b883797..2d37bb861faf 100644
--- a/arch/s390/lib/uaccess_pt.c
+++ b/arch/s390/lib/uaccess_pt.c
@@ -2,69 +2,82 @@
* User access functions based on page table walks for enhanced
* system layout without hardware support.
*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
*/
#include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/mm.h>
+#include <linux/hugetlb.h>
#include <asm/uaccess.h>
#include <asm/futex.h>
#include "uaccess.h"
-static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
+
+/*
+ * Returns kernel address for user virtual address. If the returned address is
+ * >= -4095 (IS_ERR_VALUE(x) returns true), a fault has occured and the address
+ * contains the (negative) exception code.
+ */
+static __always_inline unsigned long follow_table(struct mm_struct *mm,
+ unsigned long addr, int write)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
+ pte_t *ptep;
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- return (pte_t *) 0x3a;
+ return -0x3aUL;
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || unlikely(pud_bad(*pud)))
- return (pte_t *) 0x3b;
+ return -0x3bUL;
pmd = pmd_offset(pud, addr);
- if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- return (pte_t *) 0x10;
+ if (pmd_none(*pmd))
+ return -0x10UL;
+ if (pmd_huge(*pmd)) {
+ if (write && (pmd_val(*pmd) & _SEGMENT_ENTRY_RO))
+ return -0x04UL;
+ return (pmd_val(*pmd) & HPAGE_MASK) + (addr & ~HPAGE_MASK);
+ }
+ if (unlikely(pmd_bad(*pmd)))
+ return -0x10UL;
+
+ ptep = pte_offset_map(pmd, addr);
+ if (!pte_present(*ptep))
+ return -0x11UL;
+ if (write && !pte_write(*ptep))
+ return -0x04UL;
- return pte_offset_map(pmd, addr);
+ return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK);
}
static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
size_t n, int write_user)
{
struct mm_struct *mm = current->mm;
- unsigned long offset, pfn, done, size;
- pte_t *pte;
+ unsigned long offset, done, size, kaddr;
void *from, *to;
done = 0;
retry:
spin_lock(&mm->page_table_lock);
do {
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
+ kaddr = follow_table(mm, uaddr, write_user);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
- goto fault;
- } else if (write_user && !pte_write(*pte)) {
- pte = (pte_t *) 0x04;
- goto fault;
- }
- pfn = pte_pfn(*pte);
- offset = uaddr & (PAGE_SIZE - 1);
+ offset = uaddr & ~PAGE_MASK;
size = min(n - done, PAGE_SIZE - offset);
if (write_user) {
- to = (void *)((pfn << PAGE_SHIFT) + offset);
+ to = (void *) kaddr;
from = kptr + done;
} else {
- from = (void *)((pfn << PAGE_SHIFT) + offset);
+ from = (void *) kaddr;
to = kptr + done;
}
memcpy(to, from, size);
@@ -75,7 +88,7 @@ retry:
return n - done;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, (unsigned long) pte, write_user))
+ if (__handle_fault(uaddr, -kaddr, write_user))
return n - done;
goto retry;
}
@@ -84,27 +97,22 @@ fault:
* Do DAT for user address by page table walk, return kernel address.
* This function needs to be called with current->mm->page_table_lock held.
*/
-static __always_inline unsigned long __dat_user_addr(unsigned long uaddr)
+static __always_inline unsigned long __dat_user_addr(unsigned long uaddr,
+ int write)
{
struct mm_struct *mm = current->mm;
- unsigned long pfn;
- pte_t *pte;
+ unsigned long kaddr;
int rc;
retry:
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
- goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
+ kaddr = follow_table(mm, uaddr, write);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- }
- pfn = pte_pfn(*pte);
- return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
+ return kaddr;
fault:
spin_unlock(&mm->page_table_lock);
- rc = __handle_fault(uaddr, (unsigned long) pte, 0);
+ rc = __handle_fault(uaddr, -kaddr, write);
spin_lock(&mm->page_table_lock);
if (!rc)
goto retry;
@@ -159,11 +167,9 @@ static size_t clear_user_pt(size_t n, void __user *to)
static size_t strnlen_user_pt(size_t count, const char __user *src)
{
- char *addr;
unsigned long uaddr = (unsigned long) src;
struct mm_struct *mm = current->mm;
- unsigned long offset, pfn, done, len;
- pte_t *pte;
+ unsigned long offset, done, len, kaddr;
size_t len_str;
if (segment_eq(get_fs(), KERNEL_DS))
@@ -172,19 +178,13 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
retry:
spin_lock(&mm->page_table_lock);
do {
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
- goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
+ kaddr = follow_table(mm, uaddr, 0);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- }
- pfn = pte_pfn(*pte);
- offset = uaddr & (PAGE_SIZE-1);
- addr = (char *)(pfn << PAGE_SHIFT) + offset;
+ offset = uaddr & ~PAGE_MASK;
len = min(count - done, PAGE_SIZE - offset);
- len_str = strnlen(addr, len);
+ len_str = strnlen((char *) kaddr, len);
done += len_str;
uaddr += len_str;
} while ((len_str == len) && (done < count));
@@ -192,7 +192,7 @@ retry:
return done + 1;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, (unsigned long) pte, 0))
+ if (__handle_fault(uaddr, -kaddr, 0))
return 0;
goto retry;
}
@@ -225,11 +225,10 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
const void __user *from)
{
struct mm_struct *mm = current->mm;
- unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to,
- uaddr, done, size, error_code;
+ unsigned long offset_max, uaddr, done, size, error_code;
unsigned long uaddr_from = (unsigned long) from;
unsigned long uaddr_to = (unsigned long) to;
- pte_t *pte_from, *pte_to;
+ unsigned long kaddr_to, kaddr_from;
int write_user;
if (segment_eq(get_fs(), KERNEL_DS)) {
@@ -242,38 +241,23 @@ retry:
do {
write_user = 0;
uaddr = uaddr_from;
- pte_from = follow_table(mm, uaddr_from);
- error_code = (unsigned long) pte_from;
- if (error_code < 0x1000)
- goto fault;
- if (!pte_present(*pte_from)) {
- error_code = 0x11;
+ kaddr_from = follow_table(mm, uaddr_from, 0);
+ error_code = kaddr_from;
+ if (IS_ERR_VALUE(error_code))
goto fault;
- }
write_user = 1;
uaddr = uaddr_to;
- pte_to = follow_table(mm, uaddr_to);
- error_code = (unsigned long) pte_to;
- if (error_code < 0x1000)
- goto fault;
- if (!pte_present(*pte_to)) {
- error_code = 0x11;
+ kaddr_to = follow_table(mm, uaddr_to, 1);
+ error_code = (unsigned long) kaddr_to;
+ if (IS_ERR_VALUE(error_code))
goto fault;
- } else if (!pte_write(*pte_to)) {
- error_code = 0x04;
- goto fault;
- }
- pfn_from = pte_pfn(*pte_from);
- pfn_to = pte_pfn(*pte_to);
- offset_from = uaddr_from & (PAGE_SIZE-1);
- offset_to = uaddr_from & (PAGE_SIZE-1);
- offset_max = max(offset_from, offset_to);
+ offset_max = max(uaddr_from & ~PAGE_MASK,
+ uaddr_to & ~PAGE_MASK);
size = min(n - done, PAGE_SIZE - offset_max);
- memcpy((void *)(pfn_to << PAGE_SHIFT) + offset_to,
- (void *)(pfn_from << PAGE_SHIFT) + offset_from, size);
+ memcpy((void *) kaddr_to, (void *) kaddr_from, size);
done += size;
uaddr_from += size;
uaddr_to += size;
@@ -282,7 +266,7 @@ retry:
return n - done;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, error_code, write_user))
+ if (__handle_fault(uaddr, -error_code, write_user))
return n - done;
goto retry;
}
@@ -341,7 +325,7 @@ int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
return __futex_atomic_op_pt(op, uaddr, oparg, old);
spin_lock(&current->mm->page_table_lock);
uaddr = (u32 __force __user *)
- __dat_user_addr((__force unsigned long) uaddr);
+ __dat_user_addr((__force unsigned long) uaddr, 1);
if (!uaddr) {
spin_unlock(&current->mm->page_table_lock);
return -EFAULT;
@@ -378,7 +362,7 @@ int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
spin_lock(&current->mm->page_table_lock);
uaddr = (u32 __force __user *)
- __dat_user_addr((__force unsigned long) uaddr);
+ __dat_user_addr((__force unsigned long) uaddr, 1);
if (!uaddr) {
spin_unlock(&current->mm->page_table_lock);
return -EFAULT;
diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c
index a1e9d69a9c90..584b93674ea4 100644
--- a/arch/s390/oprofile/init.c
+++ b/arch/s390/oprofile/init.c
@@ -169,7 +169,7 @@ static ssize_t hw_interval_write(struct file *file, char const __user *buf,
if (*offset)
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
if (val < oprofile_min_interval)
oprofile_hw_interval = oprofile_min_interval;
@@ -212,7 +212,7 @@ static ssize_t hwsampler_zero_write(struct file *file, char const __user *buf,
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
if (val != 0)
return -EINVAL;
@@ -243,7 +243,7 @@ static ssize_t hwsampler_kernel_write(struct file *file, char const __user *buf,
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
if (val != 0 && val != 1)
@@ -278,7 +278,7 @@ static ssize_t hwsampler_user_write(struct file *file, char const __user *buf,
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
if (val != 0 && val != 1)
@@ -317,7 +317,7 @@ static ssize_t timer_enabled_write(struct file *file, char const __user *buf,
return -EINVAL;
retval = oprofilefs_ulong_from_user(&val, buf, count);
- if (retval)
+ if (retval <= 0)
return retval;
if (val != 0 && val != 1)
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index 7048c03490d9..fb5805745ace 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -57,6 +57,7 @@ config SH_7724_SOLUTION_ENGINE
depends on CPU_SUBTYPE_SH7724
select ARCH_REQUIRE_GPIOLIB
select SND_SOC_AK4642 if SND_SIMPLE_CARD
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select 7724 SolutionEngine if configuring for a Hitachi SH7724
evaluation board.
@@ -140,6 +141,7 @@ config SH_RSK
bool "Renesas Starter Kit"
depends on CPU_SUBTYPE_SH7201 || CPU_SUBTYPE_SH7203 || \
CPU_SUBTYPE_SH7264 || CPU_SUBTYPE_SH7269
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select this option if configuring for any of the RSK+ MCU
evaluation platforms.
@@ -159,6 +161,7 @@ config SH_SDK7786
select NO_IOPORT if !PCI
select ARCH_WANT_OPTIONAL_GPIOLIB
select HAVE_SRAM_POOL
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select SDK7786 if configuring for a Renesas Technology Europe
SH7786-65nm board.
@@ -173,6 +176,7 @@ config SH_SH7757LCR
bool "SH7757LCR"
depends on CPU_SUBTYPE_SH7757
select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
config SH_SH7785LCR
bool "SH7785LCR"
@@ -206,6 +210,7 @@ config SH_MIGOR
bool "Migo-R"
depends on CPU_SUBTYPE_SH7722
select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Migo-R if configuring for the SH7722 Migo-R platform
by Renesas System Solutions Asia Pte. Ltd.
@@ -214,6 +219,7 @@ config SH_AP325RXA
bool "AP-325RXA"
depends on CPU_SUBTYPE_SH7723
select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Renesas "AP-325RXA" support.
Compatible with ALGO SYSTEM CO.,LTD. "AP-320A"
@@ -222,6 +228,7 @@ config SH_KFR2R09
bool "KFR2R09"
depends on CPU_SUBTYPE_SH7724
select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
"Kit For R2R for 2009" support.
@@ -230,6 +237,7 @@ config SH_ECOVEC
depends on CPU_SUBTYPE_SH7724
select ARCH_REQUIRE_GPIOLIB
select SND_SOC_DA7210 if SND_SIMPLE_CARD
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Renesas "R0P7724LC0011/21RL (EcoVec)" support.
@@ -305,6 +313,7 @@ config SH_MAGIC_PANEL_R2
bool "Magic Panel R2"
depends on CPU_SUBTYPE_SH7720
select ARCH_REQUIRE_GPIOLIB
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
help
Select Magic Panel R2 if configuring for Magic Panel R2.
@@ -316,6 +325,7 @@ config SH_CAYMAN
config SH_POLARIS
bool "SMSC Polaris"
select CPU_HAS_IPR_IRQ
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
depends on CPU_SUBTYPE_SH7709
help
Select if configuring for an SMSC Polaris development board
@@ -323,6 +333,7 @@ config SH_POLARIS
config SH_SH2007
bool "SH-2007 board"
select NO_IOPORT
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
depends on CPU_SUBTYPE_SH7780
help
SH-2007 is a single-board computer based around SH7780 chip
@@ -334,6 +345,7 @@ config SH_SH2007
config SH_APSH4A3A
bool "AP-SH4A-3A"
select SH_ALPHA_BOARD
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
depends on CPU_SUBTYPE_SH7785
help
Select AP-SH4A-3A if configuring for an ALPHAPROJECT AP-SH4A-3A.
@@ -342,6 +354,7 @@ config SH_APSH4AD0A
bool "AP-SH4AD-0A"
select SH_ALPHA_BOARD
select SYS_SUPPORTS_PCI
+ select REGULATOR_FIXED_VOLTAGE if REGULATOR
depends on CPU_SUBTYPE_SH7786
help
Select AP-SH4AD-0A if configuring for an ALPHAPROJECT AP-SH4AD-0A.
diff --git a/arch/sh/boards/board-apsh4a3a.c b/arch/sh/boards/board-apsh4a3a.c
index 2823619c6006..0a39c241628a 100644
--- a/arch/sh/boards/board-apsh4a3a.c
+++ b/arch/sh/boards/board-apsh4a3a.c
@@ -13,6 +13,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/mtd/physmap.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/irq.h>
#include <linux/clk.h>
@@ -66,6 +68,12 @@ static struct platform_device nor_flash_device = {
.resource = nor_flash_resources,
};
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
static struct resource smsc911x_resources[] = {
[0] = {
.name = "smsc911x-memory",
@@ -105,6 +113,8 @@ static struct platform_device *apsh4a3a_devices[] __initdata = {
static int __init apsh4a3a_devices_setup(void)
{
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
return platform_add_devices(apsh4a3a_devices,
ARRAY_SIZE(apsh4a3a_devices));
}
diff --git a/arch/sh/boards/board-apsh4ad0a.c b/arch/sh/boards/board-apsh4ad0a.c
index b4d6292a9247..92eac3a99187 100644
--- a/arch/sh/boards/board-apsh4ad0a.c
+++ b/arch/sh/boards/board-apsh4ad0a.c
@@ -12,12 +12,20 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <asm/machvec.h>
#include <asm/sizes.h>
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
static struct resource smsc911x_resources[] = {
[0] = {
.name = "smsc911x-memory",
@@ -56,6 +64,8 @@ static struct platform_device *apsh4ad0a_devices[] __initdata = {
static int __init apsh4ad0a_devices_setup(void)
{
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
return platform_add_devices(apsh4ad0a_devices,
ARRAY_SIZE(apsh4ad0a_devices));
}
diff --git a/arch/sh/boards/board-magicpanelr2.c b/arch/sh/boards/board-magicpanelr2.c
index 90568f9de3a4..20500858b56c 100644
--- a/arch/sh/boards/board-magicpanelr2.c
+++ b/arch/sh/boards/board-magicpanelr2.c
@@ -14,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/gpio.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@@ -24,6 +26,12 @@
#include <asm/heartbeat.h>
#include <cpu/sh7720.h>
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
#define LAN9115_READY (__raw_readl(0xA8000084UL) & 0x00000001UL)
/* Wait until reset finished. Timeout is 100ms. */
@@ -348,6 +356,8 @@ static struct platform_device *mpr2_devices[] __initdata = {
static int __init mpr2_devices_setup(void)
{
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
return platform_add_devices(mpr2_devices, ARRAY_SIZE(mpr2_devices));
}
device_initcall(mpr2_devices_setup);
diff --git a/arch/sh/boards/board-polaris.c b/arch/sh/boards/board-polaris.c
index 0978ae2e4847..37a08d094727 100644
--- a/arch/sh/boards/board-polaris.c
+++ b/arch/sh/boards/board-polaris.c
@@ -9,6 +9,8 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/io.h>
#include <asm/irq.h>
@@ -22,6 +24,12 @@
#define AREA5_WAIT_CTRL (0x1C00)
#define WAIT_STATES_10 (0x7)
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x.0"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.0"),
+};
+
static struct resource smsc911x_resources[] = {
[0] = {
.name = "smsc911x-memory",
@@ -88,6 +96,8 @@ static int __init polaris_initialise(void)
printk(KERN_INFO "Configuring Polaris external bus\n");
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
/* Configure area 5 with 2 wait states */
wcr = __raw_readw(WCR2);
wcr &= (~AREA5_WAIT_CTRL);
diff --git a/arch/sh/boards/board-sh2007.c b/arch/sh/boards/board-sh2007.c
index b90b78f6a829..1980bb7e5780 100644
--- a/arch/sh/boards/board-sh2007.c
+++ b/arch/sh/boards/board-sh2007.c
@@ -6,6 +6,8 @@
*/
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/platform_device.h>
#include <linux/ata_platform.h>
@@ -13,6 +15,14 @@
#include <asm/machvec.h>
#include <mach/sh2007.h>
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x.0"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.0"),
+ REGULATOR_SUPPLY("vddvario", "smsc911x.1"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.1"),
+};
+
struct smsc911x_platform_config smc911x_info = {
.flags = SMSC911X_USE_32BIT,
.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
@@ -98,6 +108,8 @@ static struct platform_device *sh2007_devices[] __initdata = {
static int __init sh2007_io_init(void)
{
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
platform_add_devices(sh2007_devices, ARRAY_SIZE(sh2007_devices));
return 0;
}
diff --git a/arch/sh/boards/board-sh7757lcr.c b/arch/sh/boards/board-sh7757lcr.c
index 5087f8bb4cff..41f86702eb9f 100644
--- a/arch/sh/boards/board-sh7757lcr.c
+++ b/arch/sh/boards/board-sh7757lcr.c
@@ -12,6 +12,8 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/io.h>
@@ -199,6 +201,15 @@ static struct platform_device sh7757_eth_giga1_device = {
},
};
+/* Fixed 3.3V regulator to be used by SDHI0, MMCIF */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+};
+
/* SH_MMCIF */
static struct resource sh_mmcif_resources[] = {
[0] = {
@@ -329,6 +340,9 @@ static struct spi_board_info spi_board_info[] = {
static int __init sh7757lcr_devices_setup(void)
{
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+
/* RGMII (PTA) */
gpio_request(GPIO_FN_ET0_MDC, NULL);
gpio_request(GPIO_FN_ET0_MDIO, NULL);
diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c
index f33ebf447073..9e963c1d1447 100644
--- a/arch/sh/boards/mach-ap325rxa/setup.c
+++ b/arch/sh/boards/mach-ap325rxa/setup.c
@@ -20,6 +20,8 @@
#include <linux/mtd/sh_flctl.h>
#include <linux/delay.h>
#include <linux/i2c.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/gpio.h>
#include <linux/videodev2.h>
@@ -34,6 +36,12 @@
#include <asm/suspend.h>
#include <cpu/sh7723.h>
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
static struct smsc911x_platform_config smsc911x_config = {
.phy_interface = PHY_INTERFACE_MODE_MII,
.irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW,
@@ -423,6 +431,15 @@ static struct platform_device ceu_device = {
},
};
+/* Fixed 3.3V regulators to be used by SDHI0, SDHI1 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
static struct resource sdhi0_cn3_resources[] = {
[0] = {
.name = "SDHI0",
@@ -544,6 +561,10 @@ static int __init ap325rxa_devices_setup(void)
&ap325rxa_sdram_leave_start,
&ap325rxa_sdram_leave_end);
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+ regulator_register_fixed(1, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
/* LD3 and LD4 LEDs */
gpio_request(GPIO_PTX5, NULL); /* RUN */
gpio_direction_output(GPIO_PTX5, 1);
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index 4158d70c0dea..64559e8af14b 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -19,6 +19,8 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/usb/r8a66597.h>
#include <linux/usb/renesas_usbhs.h>
#include <linux/i2c.h>
@@ -242,9 +244,17 @@ static int usbhs_get_id(struct platform_device *pdev)
return gpio_get_value(GPIO_PTB3);
}
+static void usbhs_phy_reset(struct platform_device *pdev)
+{
+ /* enable vbus if HOST */
+ if (!gpio_get_value(GPIO_PTB3))
+ gpio_set_value(GPIO_PTB5, 1);
+}
+
static struct renesas_usbhs_platform_info usbhs_info = {
.platform_callback = {
.get_id = usbhs_get_id,
+ .phy_reset = usbhs_phy_reset,
},
.driver_param = {
.buswait_bwait = 4,
@@ -518,10 +528,86 @@ static struct i2c_board_info ts_i2c_clients = {
.irq = IRQ0,
};
+static struct regulator_consumer_supply cn12_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mmcif.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mmcif.0"),
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
+static struct regulator_init_data cn12_power_init_data = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cn12_power_consumers),
+ .consumer_supplies = cn12_power_consumers,
+};
+
+static struct fixed_voltage_config cn12_power_info = {
+ .supply_name = "CN12 SD/MMC Vdd",
+ .microvolts = 3300000,
+ .gpio = GPIO_PTB7,
+ .enable_high = 1,
+ .init_data = &cn12_power_init_data,
+};
+
+static struct platform_device cn12_power = {
+ .name = "reg-fixed-voltage",
+ .id = 0,
+ .dev = {
+ .platform_data = &cn12_power_info,
+ },
+};
+
#if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
/* SDHI0 */
+static struct regulator_consumer_supply sdhi0_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
+static struct regulator_init_data sdhi0_power_init_data = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(sdhi0_power_consumers),
+ .consumer_supplies = sdhi0_power_consumers,
+};
+
+static struct fixed_voltage_config sdhi0_power_info = {
+ .supply_name = "CN11 SD/MMC Vdd",
+ .microvolts = 3300000,
+ .gpio = GPIO_PTB6,
+ .enable_high = 1,
+ .init_data = &sdhi0_power_init_data,
+};
+
+static struct platform_device sdhi0_power = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &sdhi0_power_info,
+ },
+};
+
static void sdhi0_set_pwr(struct platform_device *pdev, int state)
{
+ static int power_gpio = -EINVAL;
+
+ if (power_gpio < 0) {
+ int ret = gpio_request(GPIO_PTB6, NULL);
+ if (!ret) {
+ power_gpio = GPIO_PTB6;
+ gpio_direction_output(power_gpio, 0);
+ }
+ }
+
+ /*
+ * Toggle the GPIO regardless, whether we managed to grab it above or
+ * the fixed regulator driver did.
+ */
gpio_set_value(GPIO_PTB6, state);
}
@@ -562,13 +648,27 @@ static struct platform_device sdhi0_device = {
},
};
-#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
-/* SDHI1 */
-static void sdhi1_set_pwr(struct platform_device *pdev, int state)
+static void cn12_set_pwr(struct platform_device *pdev, int state)
{
+ static int power_gpio = -EINVAL;
+
+ if (power_gpio < 0) {
+ int ret = gpio_request(GPIO_PTB7, NULL);
+ if (!ret) {
+ power_gpio = GPIO_PTB7;
+ gpio_direction_output(power_gpio, 0);
+ }
+ }
+
+ /*
+ * Toggle the GPIO regardless, whether we managed to grab it above or
+ * the fixed regulator driver did.
+ */
gpio_set_value(GPIO_PTB7, state);
}
+#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
+/* SDHI1 */
static int sdhi1_get_cd(struct platform_device *pdev)
{
return !gpio_get_value(GPIO_PTW7);
@@ -579,7 +679,7 @@ static struct sh_mobile_sdhi_info sdhi1_info = {
.dma_slave_rx = SHDMA_SLAVE_SDHI1_RX,
.tmio_caps = MMC_CAP_SDIO_IRQ | MMC_CAP_POWER_OFF_CARD |
MMC_CAP_NEEDS_POLL,
- .set_pwr = sdhi1_set_pwr,
+ .set_pwr = cn12_set_pwr,
.get_cd = sdhi1_get_cd,
};
@@ -899,14 +999,9 @@ static struct platform_device vou_device = {
#if defined(CONFIG_MMC_SH_MMCIF) || defined(CONFIG_MMC_SH_MMCIF_MODULE)
/* SH_MMCIF */
-static void mmcif_set_pwr(struct platform_device *pdev, int state)
-{
- gpio_set_value(GPIO_PTB7, state);
-}
-
static void mmcif_down_pwr(struct platform_device *pdev)
{
- gpio_set_value(GPIO_PTB7, 0);
+ cn12_set_pwr(pdev, 0);
}
static struct resource sh_mmcif_resources[] = {
@@ -929,7 +1024,7 @@ static struct resource sh_mmcif_resources[] = {
};
static struct sh_mmcif_plat_data sh_mmcif_plat = {
- .set_pwr = mmcif_set_pwr,
+ .set_pwr = cn12_set_pwr,
.down_pwr = mmcif_down_pwr,
.sup_pclk = 0, /* SH7724: Max Pclk/2 */
.caps = MMC_CAP_4_BIT_DATA |
@@ -960,7 +1055,9 @@ static struct platform_device *ecovec_devices[] __initdata = {
&ceu0_device,
&ceu1_device,
&keysc_device,
+ &cn12_power,
#if defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
+ &sdhi0_power,
&sdhi0_device,
#if !defined(CONFIG_MMC_SH_MMCIF) && !defined(CONFIG_MMC_SH_MMCIF_MODULE)
&sdhi1_device,
@@ -1258,8 +1355,6 @@ static int __init arch_setup(void)
gpio_request(GPIO_FN_SDHI0D2, NULL);
gpio_request(GPIO_FN_SDHI0D1, NULL);
gpio_request(GPIO_FN_SDHI0D0, NULL);
- gpio_request(GPIO_PTB6, NULL);
- gpio_direction_output(GPIO_PTB6, 0);
#else
/* enable MSIOF0 on CN11 (needs DS2.4 set to OFF) */
gpio_request(GPIO_FN_MSIOF0_TXD, NULL);
@@ -1288,8 +1383,6 @@ static int __init arch_setup(void)
gpio_request(GPIO_FN_MMC_D0, NULL);
gpio_request(GPIO_FN_MMC_CLK, NULL);
gpio_request(GPIO_FN_MMC_CMD, NULL);
- gpio_request(GPIO_PTB7, NULL);
- gpio_direction_output(GPIO_PTB7, 0);
cn12_enabled = true;
#elif defined(CONFIG_MMC_SDHI) || defined(CONFIG_MMC_SDHI_MODULE)
@@ -1301,8 +1394,6 @@ static int __init arch_setup(void)
gpio_request(GPIO_FN_SDHI1D2, NULL);
gpio_request(GPIO_FN_SDHI1D1, NULL);
gpio_request(GPIO_FN_SDHI1D0, NULL);
- gpio_request(GPIO_PTB7, NULL);
- gpio_direction_output(GPIO_PTB7, 0);
/* Card-detect, used on CN12 with SDHI1 */
gpio_request(GPIO_PTW7, NULL);
diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c
index 43a179ce9afc..f2a4304fbe23 100644
--- a/arch/sh/boards/mach-kfr2r09/setup.c
+++ b/arch/sh/boards/mach-kfr2r09/setup.c
@@ -21,6 +21,8 @@
#include <linux/input.h>
#include <linux/input/sh_keysc.h>
#include <linux/i2c.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/usb/r8a66597.h>
#include <linux/videodev2.h>
#include <linux/sh_intc.h>
@@ -341,6 +343,13 @@ static struct platform_device kfr2r09_camera = {
},
};
+/* Fixed 3.3V regulator to be used by SDHI0 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
static struct resource kfr2r09_sh_sdhi0_resources[] = {
[0] = {
.name = "SDHI0",
@@ -523,6 +532,9 @@ static int __init kfr2r09_devices_setup(void)
&kfr2r09_sdram_leave_start,
&kfr2r09_sdram_leave_end);
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+
/* enable SCIF1 serial port for YC401 console support */
gpio_request(GPIO_FN_SCIF1_RXD, NULL);
gpio_request(GPIO_FN_SCIF1_TXD, NULL);
diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
index a8a1ca741c85..8b73194ed2ce 100644
--- a/arch/sh/boards/mach-migor/setup.c
+++ b/arch/sh/boards/mach-migor/setup.c
@@ -17,6 +17,8 @@
#include <linux/mtd/physmap.h>
#include <linux/mtd/nand.h>
#include <linux/i2c.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smc91x.h>
#include <linux/delay.h>
#include <linux/clk.h>
@@ -386,6 +388,13 @@ static struct platform_device migor_ceu_device = {
},
};
+/* Fixed 3.3V regulator to be used by SDHI0 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
static struct resource sdhi_cn9_resources[] = {
[0] = {
.name = "SDHI",
@@ -498,6 +507,10 @@ static int __init migor_devices_setup(void)
&migor_sdram_enter_end,
&migor_sdram_leave_start,
&migor_sdram_leave_end);
+
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+
/* Let D11 LED show STATUS0 */
gpio_request(GPIO_FN_STATUS0, NULL);
diff --git a/arch/sh/boards/mach-rsk/setup.c b/arch/sh/boards/mach-rsk/setup.c
index 895f030070d3..2685ea03b064 100644
--- a/arch/sh/boards/mach-rsk/setup.c
+++ b/arch/sh/boards/mach-rsk/setup.c
@@ -16,9 +16,17 @@
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/map.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <asm/machvec.h>
#include <asm/io.h>
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
static const char *part_probes[] = { "cmdlinepart", NULL };
static struct mtd_partition rsk_partitions[] = {
@@ -67,6 +75,8 @@ static struct platform_device *rsk_devices[] __initdata = {
static int __init rsk_devices_setup(void)
{
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
return platform_add_devices(rsk_devices,
ARRAY_SIZE(rsk_devices));
}
diff --git a/arch/sh/boards/mach-sdk7786/setup.c b/arch/sh/boards/mach-sdk7786/setup.c
index 27a2314f50ac..c29268bfd34a 100644
--- a/arch/sh/boards/mach-sdk7786/setup.c
+++ b/arch/sh/boards/mach-sdk7786/setup.c
@@ -11,6 +11,8 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
#include <linux/i2c.h>
#include <linux/irq.h>
@@ -38,6 +40,12 @@ static struct platform_device heartbeat_device = {
.resource = &heartbeat_resource,
};
+/* Dummy supplies, where voltage doesn't matter */
+static struct regulator_consumer_supply dummy_supplies[] = {
+ REGULATOR_SUPPLY("vddvario", "smsc911x"),
+ REGULATOR_SUPPLY("vdd33a", "smsc911x"),
+};
+
static struct resource smsc911x_resources[] = {
[0] = {
.name = "smsc911x-memory",
@@ -236,6 +244,8 @@ static void __init sdk7786_setup(char **cmdline_p)
{
pr_info("Renesas Technology Europe SDK7786 support:\n");
+ regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+
sdk7786_fpga_init();
sdk7786_nmi_init();
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index ffbf5bc7366b..35f6efa3ac0e 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -18,6 +18,8 @@
#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mtd/physmap.h>
#include <linux/delay.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
#include <linux/smc91x.h>
#include <linux/gpio.h>
#include <linux/input.h>
@@ -454,6 +456,15 @@ static struct platform_device sh7724_usb1_gadget_device = {
.resource = sh7724_usb1_gadget_resources,
};
+/* Fixed 3.3V regulator to be used by SDHI0, SDHI1 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] =
+{
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.1"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.1"),
+};
+
static struct resource sdhi0_cn7_resources[] = {
[0] = {
.name = "SDHI0",
@@ -684,6 +695,10 @@ static int __init devices_setup(void)
&ms7724se_sdram_enter_end,
&ms7724se_sdram_leave_start,
&ms7724se_sdram_leave_end);
+
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+
/* Reset Release */
fpga_out = __raw_readw(FPGA_OUT);
/* bit4: NTSC_PDN, bit5: NTSC_RESET */
diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig
index e7583484cc07..95ae23fcfdd6 100644
--- a/arch/sh/configs/apsh4ad0a_defconfig
+++ b/arch/sh/configs/apsh4ad0a_defconfig
@@ -11,7 +11,7 @@ CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
CONFIG_BLK_CGROUP=y
CONFIG_NAMESPACES=y
CONFIG_BLK_DEV_INITRD=y
diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig
index 8a7dd7b59c5c..76a76a295d74 100644
--- a/arch/sh/configs/sdk7786_defconfig
+++ b/arch/sh/configs/sdk7786_defconfig
@@ -18,8 +18,8 @@ CONFIG_CPUSETS=y
# CONFIG_PROC_PID_CPUSET is not set
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig
index 72c3fad7383f..6bc30ab9fd18 100644
--- a/arch/sh/configs/se7206_defconfig
+++ b/arch/sh/configs/se7206_defconfig
@@ -11,7 +11,7 @@ CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
CONFIG_RELAY=y
CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y
diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig
index 6bb413036892..cd6c519f8fad 100644
--- a/arch/sh/configs/shx3_defconfig
+++ b/arch/sh/configs/shx3_defconfig
@@ -13,7 +13,7 @@ CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEMCG=y
CONFIG_RELAY=y
CONFIG_NAMESPACES=y
CONFIG_UTS_NS=y
diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig
index 8bfa4d056d7a..d7f89be9f474 100644
--- a/arch/sh/configs/urquell_defconfig
+++ b/arch/sh/configs/urquell_defconfig
@@ -15,8 +15,8 @@ CONFIG_CPUSETS=y
# CONFIG_PROC_PID_CPUSET is not set
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_DEV_INITRD=y
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index 4c171f13b0e8..b22565623142 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -335,7 +335,7 @@ static int dmae_irq_init(void)
for (n = 0; n < NR_DMAE; n++) {
int i = request_irq(get_dma_error_irq(n), dma_err,
- IRQF_SHARED, dmae_name[n], NULL);
+ IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
if (unlikely(i < 0)) {
printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
return i;
diff --git a/arch/sh/include/asm/sections.h b/arch/sh/include/asm/sections.h
index 4a5350037c8f..1b6199740e98 100644
--- a/arch/sh/include/asm/sections.h
+++ b/arch/sh/include/asm/sections.h
@@ -6,7 +6,6 @@
extern long __nosave_begin, __nosave_end;
extern long __machvec_start, __machvec_end;
extern char __uncached_start, __uncached_end;
-extern char _ebss[];
extern char __start_eh_frame[], __stop_eh_frame[];
#endif /* __ASM_SH_SECTIONS_H */
diff --git a/arch/sh/include/cpu-sh2a/cpu/sh7269.h b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
index 48d14498e774..2a0ca8780f0d 100644
--- a/arch/sh/include/cpu-sh2a/cpu/sh7269.h
+++ b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
@@ -183,18 +183,30 @@ enum {
GPIO_FN_DV_DATA1, GPIO_FN_DV_DATA0,
GPIO_FN_LCD_CLK, GPIO_FN_LCD_EXTCLK,
GPIO_FN_LCD_VSYNC, GPIO_FN_LCD_HSYNC, GPIO_FN_LCD_DE,
- GPIO_FN_LCD_DATA23, GPIO_FN_LCD_DATA22,
- GPIO_FN_LCD_DATA21, GPIO_FN_LCD_DATA20,
- GPIO_FN_LCD_DATA19, GPIO_FN_LCD_DATA18,
- GPIO_FN_LCD_DATA17, GPIO_FN_LCD_DATA16,
- GPIO_FN_LCD_DATA15, GPIO_FN_LCD_DATA14,
- GPIO_FN_LCD_DATA13, GPIO_FN_LCD_DATA12,
- GPIO_FN_LCD_DATA11, GPIO_FN_LCD_DATA10,
- GPIO_FN_LCD_DATA9, GPIO_FN_LCD_DATA8,
- GPIO_FN_LCD_DATA7, GPIO_FN_LCD_DATA6,
- GPIO_FN_LCD_DATA5, GPIO_FN_LCD_DATA4,
- GPIO_FN_LCD_DATA3, GPIO_FN_LCD_DATA2,
- GPIO_FN_LCD_DATA1, GPIO_FN_LCD_DATA0,
+ GPIO_FN_LCD_DATA23_PG23, GPIO_FN_LCD_DATA22_PG22,
+ GPIO_FN_LCD_DATA21_PG21, GPIO_FN_LCD_DATA20_PG20,
+ GPIO_FN_LCD_DATA19_PG19, GPIO_FN_LCD_DATA18_PG18,
+ GPIO_FN_LCD_DATA17_PG17, GPIO_FN_LCD_DATA16_PG16,
+ GPIO_FN_LCD_DATA15_PG15, GPIO_FN_LCD_DATA14_PG14,
+ GPIO_FN_LCD_DATA13_PG13, GPIO_FN_LCD_DATA12_PG12,
+ GPIO_FN_LCD_DATA11_PG11, GPIO_FN_LCD_DATA10_PG10,
+ GPIO_FN_LCD_DATA9_PG9, GPIO_FN_LCD_DATA8_PG8,
+ GPIO_FN_LCD_DATA7_PG7, GPIO_FN_LCD_DATA6_PG6,
+ GPIO_FN_LCD_DATA5_PG5, GPIO_FN_LCD_DATA4_PG4,
+ GPIO_FN_LCD_DATA3_PG3, GPIO_FN_LCD_DATA2_PG2,
+ GPIO_FN_LCD_DATA1_PG1, GPIO_FN_LCD_DATA0_PG0,
+ GPIO_FN_LCD_DATA23_PJ23, GPIO_FN_LCD_DATA22_PJ22,
+ GPIO_FN_LCD_DATA21_PJ21, GPIO_FN_LCD_DATA20_PJ20,
+ GPIO_FN_LCD_DATA19_PJ19, GPIO_FN_LCD_DATA18_PJ18,
+ GPIO_FN_LCD_DATA17_PJ17, GPIO_FN_LCD_DATA16_PJ16,
+ GPIO_FN_LCD_DATA15_PJ15, GPIO_FN_LCD_DATA14_PJ14,
+ GPIO_FN_LCD_DATA13_PJ13, GPIO_FN_LCD_DATA12_PJ12,
+ GPIO_FN_LCD_DATA11_PJ11, GPIO_FN_LCD_DATA10_PJ10,
+ GPIO_FN_LCD_DATA9_PJ9, GPIO_FN_LCD_DATA8_PJ8,
+ GPIO_FN_LCD_DATA7_PJ7, GPIO_FN_LCD_DATA6_PJ6,
+ GPIO_FN_LCD_DATA5_PJ5, GPIO_FN_LCD_DATA4_PJ4,
+ GPIO_FN_LCD_DATA3_PJ3, GPIO_FN_LCD_DATA2_PJ2,
+ GPIO_FN_LCD_DATA1_PJ1, GPIO_FN_LCD_DATA0_PJ0,
GPIO_FN_LCD_M_DISP,
};
diff --git a/arch/sh/include/cpu-sh4/cpu/sh7757.h b/arch/sh/include/cpu-sh4/cpu/sh7757.h
index 41f9f8b9db73..5340f3bc1863 100644
--- a/arch/sh/include/cpu-sh4/cpu/sh7757.h
+++ b/arch/sh/include/cpu-sh4/cpu/sh7757.h
@@ -283,5 +283,7 @@ enum {
SHDMA_SLAVE_RIIC8_RX,
SHDMA_SLAVE_RIIC9_TX,
SHDMA_SLAVE_RIIC9_RX,
+ SHDMA_SLAVE_RSPI_TX,
+ SHDMA_SLAVE_RSPI_RX,
};
#endif /* __ASM_SH7757_H__ */
diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
index f25127c46eca..039e4587dd9b 100644
--- a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
@@ -758,12 +758,22 @@ enum {
DV_DATA3_MARK, DV_DATA2_MARK, DV_DATA1_MARK, DV_DATA0_MARK,
LCD_CLK_MARK, LCD_EXTCLK_MARK,
LCD_VSYNC_MARK, LCD_HSYNC_MARK, LCD_DE_MARK,
- LCD_DATA23_MARK, LCD_DATA22_MARK, LCD_DATA21_MARK, LCD_DATA20_MARK,
- LCD_DATA19_MARK, LCD_DATA18_MARK, LCD_DATA17_MARK, LCD_DATA16_MARK,
- LCD_DATA15_MARK, LCD_DATA14_MARK, LCD_DATA13_MARK, LCD_DATA12_MARK,
- LCD_DATA11_MARK, LCD_DATA10_MARK, LCD_DATA9_MARK, LCD_DATA8_MARK,
- LCD_DATA7_MARK, LCD_DATA6_MARK, LCD_DATA5_MARK, LCD_DATA4_MARK,
- LCD_DATA3_MARK, LCD_DATA2_MARK, LCD_DATA1_MARK, LCD_DATA0_MARK,
+ LCD_DATA23_PG23_MARK, LCD_DATA22_PG22_MARK, LCD_DATA21_PG21_MARK,
+ LCD_DATA20_PG20_MARK, LCD_DATA19_PG19_MARK, LCD_DATA18_PG18_MARK,
+ LCD_DATA17_PG17_MARK, LCD_DATA16_PG16_MARK, LCD_DATA15_PG15_MARK,
+ LCD_DATA14_PG14_MARK, LCD_DATA13_PG13_MARK, LCD_DATA12_PG12_MARK,
+ LCD_DATA11_PG11_MARK, LCD_DATA10_PG10_MARK, LCD_DATA9_PG9_MARK,
+ LCD_DATA8_PG8_MARK, LCD_DATA7_PG7_MARK, LCD_DATA6_PG6_MARK,
+ LCD_DATA5_PG5_MARK, LCD_DATA4_PG4_MARK, LCD_DATA3_PG3_MARK,
+ LCD_DATA2_PG2_MARK, LCD_DATA1_PG1_MARK, LCD_DATA0_PG0_MARK,
+ LCD_DATA23_PJ23_MARK, LCD_DATA22_PJ22_MARK, LCD_DATA21_PJ21_MARK,
+ LCD_DATA20_PJ20_MARK, LCD_DATA19_PJ19_MARK, LCD_DATA18_PJ18_MARK,
+ LCD_DATA17_PJ17_MARK, LCD_DATA16_PJ16_MARK, LCD_DATA15_PJ15_MARK,
+ LCD_DATA14_PJ14_MARK, LCD_DATA13_PJ13_MARK, LCD_DATA12_PJ12_MARK,
+ LCD_DATA11_PJ11_MARK, LCD_DATA10_PJ10_MARK, LCD_DATA9_PJ9_MARK,
+ LCD_DATA8_PJ8_MARK, LCD_DATA7_PJ7_MARK, LCD_DATA6_PJ6_MARK,
+ LCD_DATA5_PJ5_MARK, LCD_DATA4_PJ4_MARK, LCD_DATA3_PJ3_MARK,
+ LCD_DATA2_PJ2_MARK, LCD_DATA1_PJ1_MARK, LCD_DATA0_PJ0_MARK,
LCD_TCON6_MARK, LCD_TCON5_MARK, LCD_TCON4_MARK,
LCD_TCON3_MARK, LCD_TCON2_MARK, LCD_TCON1_MARK, LCD_TCON0_MARK,
LCD_M_DISP_MARK,
@@ -1036,6 +1046,7 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PF1_DATA, PF1MD_000),
PINMUX_DATA(BACK_MARK, PF1MD_001),
+ PINMUX_DATA(SSL10_MARK, PF1MD_011),
PINMUX_DATA(TIOC4B_MARK, PF1MD_100),
PINMUX_DATA(DACK0_MARK, PF1MD_101),
@@ -1049,47 +1060,50 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PG27_DATA, PG27MD_00),
PINMUX_DATA(LCD_TCON2_MARK, PG27MD_10),
PINMUX_DATA(LCD_EXTCLK_MARK, PG27MD_11),
+ PINMUX_DATA(LCD_DE_MARK, PG27MD_11),
PINMUX_DATA(PG26_DATA, PG26MD_00),
PINMUX_DATA(LCD_TCON1_MARK, PG26MD_10),
+ PINMUX_DATA(LCD_HSYNC_MARK, PG26MD_10),
PINMUX_DATA(PG25_DATA, PG25MD_00),
PINMUX_DATA(LCD_TCON0_MARK, PG25MD_10),
+ PINMUX_DATA(LCD_VSYNC_MARK, PG25MD_10),
PINMUX_DATA(PG24_DATA, PG24MD_00),
PINMUX_DATA(LCD_CLK_MARK, PG24MD_10),
PINMUX_DATA(PG23_DATA, PG23MD_000),
- PINMUX_DATA(LCD_DATA23_MARK, PG23MD_010),
+ PINMUX_DATA(LCD_DATA23_PG23_MARK, PG23MD_010),
PINMUX_DATA(LCD_TCON6_MARK, PG23MD_011),
PINMUX_DATA(TXD5_MARK, PG23MD_100),
PINMUX_DATA(PG22_DATA, PG22MD_000),
- PINMUX_DATA(LCD_DATA22_MARK, PG22MD_010),
+ PINMUX_DATA(LCD_DATA22_PG22_MARK, PG22MD_010),
PINMUX_DATA(LCD_TCON5_MARK, PG22MD_011),
PINMUX_DATA(RXD5_MARK, PG22MD_100),
PINMUX_DATA(PG21_DATA, PG21MD_000),
PINMUX_DATA(DV_DATA7_MARK, PG21MD_001),
- PINMUX_DATA(LCD_DATA21_MARK, PG21MD_010),
+ PINMUX_DATA(LCD_DATA21_PG21_MARK, PG21MD_010),
PINMUX_DATA(LCD_TCON4_MARK, PG21MD_011),
PINMUX_DATA(TXD4_MARK, PG21MD_100),
PINMUX_DATA(PG20_DATA, PG20MD_000),
PINMUX_DATA(DV_DATA6_MARK, PG20MD_001),
- PINMUX_DATA(LCD_DATA20_MARK, PG21MD_010),
+ PINMUX_DATA(LCD_DATA20_PG20_MARK, PG21MD_010),
PINMUX_DATA(LCD_TCON3_MARK, PG20MD_011),
PINMUX_DATA(RXD4_MARK, PG20MD_100),
PINMUX_DATA(PG19_DATA, PG19MD_000),
PINMUX_DATA(DV_DATA5_MARK, PG19MD_001),
- PINMUX_DATA(LCD_DATA19_MARK, PG19MD_010),
+ PINMUX_DATA(LCD_DATA19_PG19_MARK, PG19MD_010),
PINMUX_DATA(SPDIF_OUT_MARK, PG19MD_011),
PINMUX_DATA(SCK5_MARK, PG19MD_100),
PINMUX_DATA(PG18_DATA, PG18MD_000),
PINMUX_DATA(DV_DATA4_MARK, PG18MD_001),
- PINMUX_DATA(LCD_DATA18_MARK, PG18MD_010),
+ PINMUX_DATA(LCD_DATA18_PG18_MARK, PG18MD_010),
PINMUX_DATA(SPDIF_IN_MARK, PG18MD_011),
PINMUX_DATA(SCK4_MARK, PG18MD_100),
@@ -1097,103 +1111,103 @@ static pinmux_enum_t pinmux_data[] = {
// we're going with 2 bits
PINMUX_DATA(PG17_DATA, PG17MD_00),
PINMUX_DATA(WE3ICIOWRAHDQMUU_MARK, PG17MD_01),
- PINMUX_DATA(LCD_DATA17_MARK, PG17MD_10),
+ PINMUX_DATA(LCD_DATA17_PG17_MARK, PG17MD_10),
// TODO hardware manual has PG16 3 bits wide in reg picture and 2 bits in description
// we're going with 2 bits
PINMUX_DATA(PG16_DATA, PG16MD_00),
PINMUX_DATA(WE2ICIORDDQMUL_MARK, PG16MD_01),
- PINMUX_DATA(LCD_DATA16_MARK, PG16MD_10),
+ PINMUX_DATA(LCD_DATA16_PG16_MARK, PG16MD_10),
PINMUX_DATA(PG15_DATA, PG15MD_00),
PINMUX_DATA(D31_MARK, PG15MD_01),
- PINMUX_DATA(LCD_DATA15_MARK, PG15MD_10),
+ PINMUX_DATA(LCD_DATA15_PG15_MARK, PG15MD_10),
PINMUX_DATA(PINT7_PG_MARK, PG15MD_11),
PINMUX_DATA(PG14_DATA, PG14MD_00),
PINMUX_DATA(D30_MARK, PG14MD_01),
- PINMUX_DATA(LCD_DATA14_MARK, PG14MD_10),
+ PINMUX_DATA(LCD_DATA14_PG14_MARK, PG14MD_10),
PINMUX_DATA(PINT6_PG_MARK, PG14MD_11),
PINMUX_DATA(PG13_DATA, PG13MD_00),
PINMUX_DATA(D29_MARK, PG13MD_01),
- PINMUX_DATA(LCD_DATA13_MARK, PG13MD_10),
+ PINMUX_DATA(LCD_DATA13_PG13_MARK, PG13MD_10),
PINMUX_DATA(PINT5_PG_MARK, PG13MD_11),
PINMUX_DATA(PG12_DATA, PG12MD_00),
PINMUX_DATA(D28_MARK, PG12MD_01),
- PINMUX_DATA(LCD_DATA12_MARK, PG12MD_10),
+ PINMUX_DATA(LCD_DATA12_PG12_MARK, PG12MD_10),
PINMUX_DATA(PINT4_PG_MARK, PG12MD_11),
PINMUX_DATA(PG11_DATA, PG11MD_000),
PINMUX_DATA(D27_MARK, PG11MD_001),
- PINMUX_DATA(LCD_DATA11_MARK, PG11MD_010),
+ PINMUX_DATA(LCD_DATA11_PG11_MARK, PG11MD_010),
PINMUX_DATA(PINT3_PG_MARK, PG11MD_011),
PINMUX_DATA(TIOC3D_MARK, PG11MD_100),
PINMUX_DATA(PG10_DATA, PG10MD_000),
PINMUX_DATA(D26_MARK, PG10MD_001),
- PINMUX_DATA(LCD_DATA10_MARK, PG10MD_010),
+ PINMUX_DATA(LCD_DATA10_PG10_MARK, PG10MD_010),
PINMUX_DATA(PINT2_PG_MARK, PG10MD_011),
PINMUX_DATA(TIOC3C_MARK, PG10MD_100),
PINMUX_DATA(PG9_DATA, PG9MD_000),
PINMUX_DATA(D25_MARK, PG9MD_001),
- PINMUX_DATA(LCD_DATA9_MARK, PG9MD_010),
+ PINMUX_DATA(LCD_DATA9_PG9_MARK, PG9MD_010),
PINMUX_DATA(PINT1_PG_MARK, PG9MD_011),
PINMUX_DATA(TIOC3B_MARK, PG9MD_100),
PINMUX_DATA(PG8_DATA, PG8MD_000),
PINMUX_DATA(D24_MARK, PG8MD_001),
- PINMUX_DATA(LCD_DATA8_MARK, PG8MD_010),
+ PINMUX_DATA(LCD_DATA8_PG8_MARK, PG8MD_010),
PINMUX_DATA(PINT0_PG_MARK, PG8MD_011),
PINMUX_DATA(TIOC3A_MARK, PG8MD_100),
PINMUX_DATA(PG7_DATA, PG7MD_000),
PINMUX_DATA(D23_MARK, PG7MD_001),
- PINMUX_DATA(LCD_DATA7_MARK, PG7MD_010),
+ PINMUX_DATA(LCD_DATA7_PG7_MARK, PG7MD_010),
PINMUX_DATA(IRQ7_PG_MARK, PG7MD_011),
PINMUX_DATA(TIOC2B_MARK, PG7MD_100),
PINMUX_DATA(PG6_DATA, PG6MD_000),
PINMUX_DATA(D22_MARK, PG6MD_001),
- PINMUX_DATA(LCD_DATA6_MARK, PG6MD_010),
+ PINMUX_DATA(LCD_DATA6_PG6_MARK, PG6MD_010),
PINMUX_DATA(IRQ6_PG_MARK, PG6MD_011),
PINMUX_DATA(TIOC2A_MARK, PG6MD_100),
PINMUX_DATA(PG5_DATA, PG5MD_000),
PINMUX_DATA(D21_MARK, PG5MD_001),
- PINMUX_DATA(LCD_DATA5_MARK, PG5MD_010),
+ PINMUX_DATA(LCD_DATA5_PG5_MARK, PG5MD_010),
PINMUX_DATA(IRQ5_PG_MARK, PG5MD_011),
PINMUX_DATA(TIOC1B_MARK, PG5MD_100),
PINMUX_DATA(PG4_DATA, PG4MD_000),
PINMUX_DATA(D20_MARK, PG4MD_001),
- PINMUX_DATA(LCD_DATA4_MARK, PG4MD_010),
+ PINMUX_DATA(LCD_DATA4_PG4_MARK, PG4MD_010),
PINMUX_DATA(IRQ4_PG_MARK, PG4MD_011),
PINMUX_DATA(TIOC1A_MARK, PG4MD_100),
PINMUX_DATA(PG3_DATA, PG3MD_000),
PINMUX_DATA(D19_MARK, PG3MD_001),
- PINMUX_DATA(LCD_DATA3_MARK, PG3MD_010),
+ PINMUX_DATA(LCD_DATA3_PG3_MARK, PG3MD_010),
PINMUX_DATA(IRQ3_PG_MARK, PG3MD_011),
PINMUX_DATA(TIOC0D_MARK, PG3MD_100),
PINMUX_DATA(PG2_DATA, PG2MD_000),
PINMUX_DATA(D18_MARK, PG2MD_001),
- PINMUX_DATA(LCD_DATA2_MARK, PG2MD_010),
+ PINMUX_DATA(LCD_DATA2_PG2_MARK, PG2MD_010),
PINMUX_DATA(IRQ2_PG_MARK, PG2MD_011),
PINMUX_DATA(TIOC0C_MARK, PG2MD_100),
PINMUX_DATA(PG1_DATA, PG1MD_000),
PINMUX_DATA(D17_MARK, PG1MD_001),
- PINMUX_DATA(LCD_DATA1_MARK, PG1MD_010),
+ PINMUX_DATA(LCD_DATA1_PG1_MARK, PG1MD_010),
PINMUX_DATA(IRQ1_PG_MARK, PG1MD_011),
PINMUX_DATA(TIOC0B_MARK, PG1MD_100),
PINMUX_DATA(PG0_DATA, PG0MD_000),
PINMUX_DATA(D16_MARK, PG0MD_001),
- PINMUX_DATA(LCD_DATA0_MARK, PG0MD_010),
+ PINMUX_DATA(LCD_DATA0_PG0_MARK, PG0MD_010),
PINMUX_DATA(IRQ0_PG_MARK, PG0MD_011),
PINMUX_DATA(TIOC0A_MARK, PG0MD_100),
@@ -1275,14 +1289,14 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PJ23_DATA, PJ23MD_000),
PINMUX_DATA(DV_DATA23_MARK, PJ23MD_001),
- PINMUX_DATA(LCD_DATA23_MARK, PJ23MD_010),
+ PINMUX_DATA(LCD_DATA23_PJ23_MARK, PJ23MD_010),
PINMUX_DATA(LCD_TCON6_MARK, PJ23MD_011),
PINMUX_DATA(IRQ3_PJ_MARK, PJ23MD_100),
PINMUX_DATA(CTX1_MARK, PJ23MD_101),
PINMUX_DATA(PJ22_DATA, PJ22MD_000),
PINMUX_DATA(DV_DATA22_MARK, PJ22MD_001),
- PINMUX_DATA(LCD_DATA22_MARK, PJ22MD_010),
+ PINMUX_DATA(LCD_DATA22_PJ22_MARK, PJ22MD_010),
PINMUX_DATA(LCD_TCON5_MARK, PJ22MD_011),
PINMUX_DATA(IRQ2_PJ_MARK, PJ22MD_100),
PINMUX_DATA(CRX1_MARK, PJ22MD_101),
@@ -1290,14 +1304,14 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PJ21_DATA, PJ21MD_000),
PINMUX_DATA(DV_DATA21_MARK, PJ21MD_001),
- PINMUX_DATA(LCD_DATA21_MARK, PJ21MD_010),
+ PINMUX_DATA(LCD_DATA21_PJ21_MARK, PJ21MD_010),
PINMUX_DATA(LCD_TCON4_MARK, PJ21MD_011),
PINMUX_DATA(IRQ1_PJ_MARK, PJ21MD_100),
PINMUX_DATA(CTX2_MARK, PJ21MD_101),
PINMUX_DATA(PJ20_DATA, PJ20MD_000),
PINMUX_DATA(DV_DATA20_MARK, PJ20MD_001),
- PINMUX_DATA(LCD_DATA20_MARK, PJ20MD_010),
+ PINMUX_DATA(LCD_DATA20_PJ20_MARK, PJ20MD_010),
PINMUX_DATA(LCD_TCON3_MARK, PJ20MD_011),
PINMUX_DATA(IRQ0_PJ_MARK, PJ20MD_100),
PINMUX_DATA(CRX2_MARK, PJ20MD_101),
@@ -1305,7 +1319,7 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PJ19_DATA, PJ19MD_000),
PINMUX_DATA(DV_DATA19_MARK, PJ19MD_001),
- PINMUX_DATA(LCD_DATA19_MARK, PJ19MD_010),
+ PINMUX_DATA(LCD_DATA19_PJ19_MARK, PJ19MD_010),
PINMUX_DATA(MISO0_PJ19_MARK, PJ19MD_011),
PINMUX_DATA(TIOC0D_MARK, PJ19MD_100),
PINMUX_DATA(SIOFRXD_MARK, PJ19MD_101),
@@ -1313,126 +1327,126 @@ static pinmux_enum_t pinmux_data[] = {
PINMUX_DATA(PJ18_DATA, PJ18MD_000),
PINMUX_DATA(DV_DATA18_MARK, PJ18MD_001),
- PINMUX_DATA(LCD_DATA18_MARK, PJ18MD_010),
+ PINMUX_DATA(LCD_DATA18_PJ18_MARK, PJ18MD_010),
PINMUX_DATA(MOSI0_PJ18_MARK, PJ18MD_011),
PINMUX_DATA(TIOC0C_MARK, PJ18MD_100),
PINMUX_DATA(SIOFTXD_MARK, PJ18MD_101),
PINMUX_DATA(PJ17_DATA, PJ17MD_000),
PINMUX_DATA(DV_DATA17_MARK, PJ17MD_001),
- PINMUX_DATA(LCD_DATA17_MARK, PJ17MD_010),
+ PINMUX_DATA(LCD_DATA17_PJ17_MARK, PJ17MD_010),
PINMUX_DATA(SSL00_PJ17_MARK, PJ17MD_011),
PINMUX_DATA(TIOC0B_MARK, PJ17MD_100),
PINMUX_DATA(SIOFSYNC_MARK, PJ17MD_101),
PINMUX_DATA(PJ16_DATA, PJ16MD_000),
PINMUX_DATA(DV_DATA16_MARK, PJ16MD_001),
- PINMUX_DATA(LCD_DATA16_MARK, PJ16MD_010),
+ PINMUX_DATA(LCD_DATA16_PJ16_MARK, PJ16MD_010),
PINMUX_DATA(RSPCK0_PJ16_MARK, PJ16MD_011),
PINMUX_DATA(TIOC0A_MARK, PJ16MD_100),
PINMUX_DATA(SIOFSCK_MARK, PJ16MD_101),
PINMUX_DATA(PJ15_DATA, PJ15MD_000),
PINMUX_DATA(DV_DATA15_MARK, PJ15MD_001),
- PINMUX_DATA(LCD_DATA15_MARK, PJ15MD_010),
+ PINMUX_DATA(LCD_DATA15_PJ15_MARK, PJ15MD_010),
PINMUX_DATA(PINT7_PJ_MARK, PJ15MD_011),
PINMUX_DATA(PWM2H_MARK, PJ15MD_100),
PINMUX_DATA(TXD7_MARK, PJ15MD_101),
PINMUX_DATA(PJ14_DATA, PJ14MD_000),
PINMUX_DATA(DV_DATA14_MARK, PJ14MD_001),
- PINMUX_DATA(LCD_DATA14_MARK, PJ14MD_010),
+ PINMUX_DATA(LCD_DATA14_PJ14_MARK, PJ14MD_010),
PINMUX_DATA(PINT6_PJ_MARK, PJ14MD_011),
PINMUX_DATA(PWM2G_MARK, PJ14MD_100),
PINMUX_DATA(TXD6_MARK, PJ14MD_101),
PINMUX_DATA(PJ13_DATA, PJ13MD_000),
PINMUX_DATA(DV_DATA13_MARK, PJ13MD_001),
- PINMUX_DATA(LCD_DATA13_MARK, PJ13MD_010),
+ PINMUX_DATA(LCD_DATA13_PJ13_MARK, PJ13MD_010),
PINMUX_DATA(PINT5_PJ_MARK, PJ13MD_011),
PINMUX_DATA(PWM2F_MARK, PJ13MD_100),
PINMUX_DATA(TXD5_MARK, PJ13MD_101),
PINMUX_DATA(PJ12_DATA, PJ12MD_000),
PINMUX_DATA(DV_DATA12_MARK, PJ12MD_001),
- PINMUX_DATA(LCD_DATA12_MARK, PJ12MD_010),
+ PINMUX_DATA(LCD_DATA12_PJ12_MARK, PJ12MD_010),
PINMUX_DATA(PINT4_PJ_MARK, PJ12MD_011),
PINMUX_DATA(PWM2E_MARK, PJ12MD_100),
PINMUX_DATA(SCK7_MARK, PJ12MD_101),
PINMUX_DATA(PJ11_DATA, PJ11MD_000),
PINMUX_DATA(DV_DATA11_MARK, PJ11MD_001),
- PINMUX_DATA(LCD_DATA11_MARK, PJ11MD_010),
+ PINMUX_DATA(LCD_DATA11_PJ11_MARK, PJ11MD_010),
PINMUX_DATA(PINT3_PJ_MARK, PJ11MD_011),
PINMUX_DATA(PWM2D_MARK, PJ11MD_100),
PINMUX_DATA(SCK6_MARK, PJ11MD_101),
PINMUX_DATA(PJ10_DATA, PJ10MD_000),
PINMUX_DATA(DV_DATA10_MARK, PJ10MD_001),
- PINMUX_DATA(LCD_DATA10_MARK, PJ10MD_010),
+ PINMUX_DATA(LCD_DATA10_PJ10_MARK, PJ10MD_010),
PINMUX_DATA(PINT2_PJ_MARK, PJ10MD_011),
PINMUX_DATA(PWM2C_MARK, PJ10MD_100),
PINMUX_DATA(SCK5_MARK, PJ10MD_101),
PINMUX_DATA(PJ9_DATA, PJ9MD_000),
PINMUX_DATA(DV_DATA9_MARK, PJ9MD_001),
- PINMUX_DATA(LCD_DATA9_MARK, PJ9MD_010),
+ PINMUX_DATA(LCD_DATA9_PJ9_MARK, PJ9MD_010),
PINMUX_DATA(PINT1_PJ_MARK, PJ9MD_011),
PINMUX_DATA(PWM2B_MARK, PJ9MD_100),
PINMUX_DATA(RTS5_MARK, PJ9MD_101),
PINMUX_DATA(PJ8_DATA, PJ8MD_000),
PINMUX_DATA(DV_DATA8_MARK, PJ8MD_001),
- PINMUX_DATA(LCD_DATA8_MARK, PJ8MD_010),
+ PINMUX_DATA(LCD_DATA8_PJ8_MARK, PJ8MD_010),
PINMUX_DATA(PINT0_PJ_MARK, PJ8MD_011),
PINMUX_DATA(PWM2A_MARK, PJ8MD_100),
PINMUX_DATA(CTS5_MARK, PJ8MD_101),
PINMUX_DATA(PJ7_DATA, PJ7MD_000),
PINMUX_DATA(DV_DATA7_MARK, PJ7MD_001),
- PINMUX_DATA(LCD_DATA7_MARK, PJ7MD_010),
+ PINMUX_DATA(LCD_DATA7_PJ7_MARK, PJ7MD_010),
PINMUX_DATA(SD_D2_MARK, PJ7MD_011),
PINMUX_DATA(PWM1H_MARK, PJ7MD_100),
PINMUX_DATA(PJ6_DATA, PJ6MD_000),
PINMUX_DATA(DV_DATA6_MARK, PJ6MD_001),
- PINMUX_DATA(LCD_DATA6_MARK, PJ6MD_010),
+ PINMUX_DATA(LCD_DATA6_PJ6_MARK, PJ6MD_010),
PINMUX_DATA(SD_D3_MARK, PJ6MD_011),
PINMUX_DATA(PWM1G_MARK, PJ6MD_100),
PINMUX_DATA(PJ5_DATA, PJ5MD_000),
PINMUX_DATA(DV_DATA5_MARK, PJ5MD_001),
- PINMUX_DATA(LCD_DATA5_MARK, PJ5MD_010),
+ PINMUX_DATA(LCD_DATA5_PJ5_MARK, PJ5MD_010),
PINMUX_DATA(SD_CMD_MARK, PJ5MD_011),
PINMUX_DATA(PWM1F_MARK, PJ5MD_100),
PINMUX_DATA(PJ4_DATA, PJ4MD_000),
PINMUX_DATA(DV_DATA4_MARK, PJ4MD_001),
- PINMUX_DATA(LCD_DATA4_MARK, PJ4MD_010),
+ PINMUX_DATA(LCD_DATA4_PJ4_MARK, PJ4MD_010),
PINMUX_DATA(SD_CLK_MARK, PJ4MD_011),
PINMUX_DATA(PWM1E_MARK, PJ4MD_100),
PINMUX_DATA(PJ3_DATA, PJ3MD_000),
PINMUX_DATA(DV_DATA3_MARK, PJ3MD_001),
- PINMUX_DATA(LCD_DATA3_MARK, PJ3MD_010),
+ PINMUX_DATA(LCD_DATA3_PJ3_MARK, PJ3MD_010),
PINMUX_DATA(SD_D0_MARK, PJ3MD_011),
PINMUX_DATA(PWM1D_MARK, PJ3MD_100),
PINMUX_DATA(PJ2_DATA, PJ2MD_000),
PINMUX_DATA(DV_DATA2_MARK, PJ2MD_001),
- PINMUX_DATA(LCD_DATA2_MARK, PJ2MD_010),
+ PINMUX_DATA(LCD_DATA2_PJ2_MARK, PJ2MD_010),
PINMUX_DATA(SD_D1_MARK, PJ2MD_011),
PINMUX_DATA(PWM1C_MARK, PJ2MD_100),
PINMUX_DATA(PJ1_DATA, PJ1MD_000),
PINMUX_DATA(DV_DATA1_MARK, PJ1MD_001),
- PINMUX_DATA(LCD_DATA1_MARK, PJ1MD_010),
+ PINMUX_DATA(LCD_DATA1_PJ1_MARK, PJ1MD_010),
PINMUX_DATA(SD_WP_MARK, PJ1MD_011),
PINMUX_DATA(PWM1B_MARK, PJ1MD_100),
PINMUX_DATA(PJ0_DATA, PJ0MD_000),
PINMUX_DATA(DV_DATA0_MARK, PJ0MD_001),
- PINMUX_DATA(LCD_DATA0_MARK, PJ0MD_010),
+ PINMUX_DATA(LCD_DATA0_PJ0_MARK, PJ0MD_010),
PINMUX_DATA(SD_CD_MARK, PJ0MD_011),
PINMUX_DATA(PWM1A_MARK, PJ0MD_100),
};
@@ -1877,30 +1891,55 @@ static struct pinmux_gpio pinmux_gpios[] = {
PINMUX_GPIO(GPIO_FN_LCD_HSYNC, LCD_HSYNC_MARK),
PINMUX_GPIO(GPIO_FN_LCD_DE, LCD_DE_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA23, LCD_DATA23_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA22, LCD_DATA22_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA21, LCD_DATA21_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA20, LCD_DATA20_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA19, LCD_DATA19_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA18, LCD_DATA18_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA17, LCD_DATA17_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA16, LCD_DATA16_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA15, LCD_DATA15_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA14, LCD_DATA14_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA13, LCD_DATA13_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA12, LCD_DATA12_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA11, LCD_DATA11_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA10, LCD_DATA10_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA9, LCD_DATA9_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA8, LCD_DATA8_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA7, LCD_DATA7_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA6, LCD_DATA6_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA5, LCD_DATA5_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA4, LCD_DATA4_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA3, LCD_DATA3_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA2, LCD_DATA2_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA1, LCD_DATA1_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA0, LCD_DATA0_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA23_PG23, LCD_DATA23_PG23_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA22_PG22, LCD_DATA22_PG22_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA21_PG21, LCD_DATA21_PG21_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA20_PG20, LCD_DATA20_PG20_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA19_PG19, LCD_DATA19_PG19_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA18_PG18, LCD_DATA18_PG18_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA17_PG17, LCD_DATA17_PG17_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA16_PG16, LCD_DATA16_PG16_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA15_PG15, LCD_DATA15_PG15_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA14_PG14, LCD_DATA14_PG14_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA13_PG13, LCD_DATA13_PG13_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA12_PG12, LCD_DATA12_PG12_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA11_PG11, LCD_DATA11_PG11_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA10_PG10, LCD_DATA10_PG10_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA9_PG9, LCD_DATA9_PG9_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA8_PG8, LCD_DATA8_PG8_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA7_PG7, LCD_DATA7_PG7_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA6_PG6, LCD_DATA6_PG6_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA5_PG5, LCD_DATA5_PG5_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA4_PG4, LCD_DATA4_PG4_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA3_PG3, LCD_DATA3_PG3_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA2_PG2, LCD_DATA2_PG2_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA1_PG1, LCD_DATA1_PG1_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA0_PG0, LCD_DATA0_PG0_MARK),
+
+ PINMUX_GPIO(GPIO_FN_LCD_DATA23_PJ23, LCD_DATA23_PJ23_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA22_PJ22, LCD_DATA22_PJ22_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA21_PJ21, LCD_DATA21_PJ21_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA20_PJ20, LCD_DATA20_PJ20_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA19_PJ19, LCD_DATA19_PJ19_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA18_PJ18, LCD_DATA18_PJ18_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA17_PJ17, LCD_DATA17_PJ17_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA16_PJ16, LCD_DATA16_PJ16_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA15_PJ15, LCD_DATA15_PJ15_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA14_PJ14, LCD_DATA14_PJ14_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA13_PJ13, LCD_DATA13_PJ13_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA12_PJ12, LCD_DATA12_PJ12_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA11_PJ11, LCD_DATA11_PJ11_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA10_PJ10, LCD_DATA10_PJ10_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA9_PJ9, LCD_DATA9_PJ9_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA8_PJ8, LCD_DATA8_PJ8_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA7_PJ7, LCD_DATA7_PJ7_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA6_PJ6, LCD_DATA6_PJ6_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA5_PJ5, LCD_DATA5_PJ5_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA4_PJ4, LCD_DATA4_PJ4_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA3_PJ3, LCD_DATA3_PJ3_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA2_PJ2, LCD_DATA2_PJ2_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA1_PJ1, LCD_DATA1_PJ1_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA0_PJ0, LCD_DATA0_PJ0_MARK),
PINMUX_GPIO(GPIO_FN_LCD_M_DISP, LCD_M_DISP_MARK),
};
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
index c87e78f73234..5f30f805d2f2 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7724.c
@@ -334,8 +334,8 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("tpu0", &mstp_clks[HWBLK_TPU]),
CLKDEV_CON_ID("irda0", &mstp_clks[HWBLK_IRDA]),
CLKDEV_CON_ID("tsif0", &mstp_clks[HWBLK_TSIF]),
- CLKDEV_CON_ID("usb1", &mstp_clks[HWBLK_USB1]),
- CLKDEV_CON_ID("usb0", &mstp_clks[HWBLK_USB0]),
+ CLKDEV_DEV_ID("renesas_usbhs.1", &mstp_clks[HWBLK_USB1]),
+ CLKDEV_DEV_ID("renesas_usbhs.0", &mstp_clks[HWBLK_USB0]),
CLKDEV_CON_ID("2dg0", &mstp_clks[HWBLK_2DG]),
CLKDEV_DEV_ID("sh_mobile_sdhi.0", &mstp_clks[HWBLK_SDHI0]),
CLKDEV_DEV_ID("sh_mobile_sdhi.1", &mstp_clks[HWBLK_SDHI1]),
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
index 65786c7f5ded..6a868b091c2d 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_sci.h>
+#include <linux/sh_dma.h>
#include <linux/sh_timer.h>
#include <linux/sh_intc.h>
#include <linux/uio_driver.h>
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
index a7708425afa9..4a2f357f4df8 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c
@@ -216,6 +216,20 @@ static const struct sh_dmae_slave_config sh7757_dmae1_slaves[] = {
TS_INDEX2VAL(XMIT_SZ_8BIT),
.mid_rid = 0x42,
},
+ {
+ .slave_id = SHDMA_SLAVE_RSPI_TX,
+ .addr = 0xfe480004,
+ .chcr = SM_INC | 0x800 | 0x40000000 |
+ TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xc1,
+ },
+ {
+ .slave_id = SHDMA_SLAVE_RSPI_RX,
+ .addr = 0xfe480004,
+ .chcr = DM_INC | 0x800 | 0x40000000 |
+ TS_INDEX2VAL(XMIT_SZ_16BIT),
+ .mid_rid = 0xc2,
+ },
};
static const struct sh_dmae_slave_config sh7757_dmae2_slaves[] = {
diff --git a/arch/sh/kernel/cpu/sh5/entry.S b/arch/sh/kernel/cpu/sh5/entry.S
index b7cf6a547f11..7e605b95592a 100644
--- a/arch/sh/kernel/cpu/sh5/entry.S
+++ b/arch/sh/kernel/cpu/sh5/entry.S
@@ -933,7 +933,7 @@ ret_with_reschedule:
pta restore_all, tr1
- movi _TIF_SIGPENDING, r8
+ movi (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), r8
and r8, r7, r8
pta work_notifysig, tr0
bne r8, ZERO, tr0
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index f67601cb3f1f..b96489d8b27d 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -139,7 +139,7 @@ work_pending:
! r8: current_thread_info
! t: result of "tst #_TIF_NEED_RESCHED, r0"
bf/s work_resched
- tst #_TIF_SIGPENDING, r0
+ tst #(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME), r0
work_notifysig:
bt/s __restore_all
mov r15, r4
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 7b57bf1dc855..ebe7a7d97215 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -273,7 +273,7 @@ void __init setup_arch(char **cmdline_p)
data_resource.start = virt_to_phys(_etext);
data_resource.end = virt_to_phys(_edata)-1;
bss_resource.start = virt_to_phys(__bss_start);
- bss_resource.end = virt_to_phys(_ebss)-1;
+ bss_resource.end = virt_to_phys(__bss_stop)-1;
#ifdef CONFIG_CMDLINE_OVERWRITE
strlcpy(command_line, CONFIG_CMDLINE, sizeof(command_line));
diff --git a/arch/sh/kernel/sh_ksyms_32.c b/arch/sh/kernel/sh_ksyms_32.c
index 3896f26efa4a..2a0a596ebf67 100644
--- a/arch/sh/kernel/sh_ksyms_32.c
+++ b/arch/sh/kernel/sh_ksyms_32.c
@@ -19,7 +19,6 @@ EXPORT_SYMBOL(csum_partial);
EXPORT_SYMBOL(csum_partial_copy_generic);
EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(_ebss);
EXPORT_SYMBOL(empty_zero_page);
#define DECLARE_EXPORT(name) \
diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S
index c98905f71e28..db88cbf9eafd 100644
--- a/arch/sh/kernel/vmlinux.lds.S
+++ b/arch/sh/kernel/vmlinux.lds.S
@@ -78,7 +78,6 @@ SECTIONS
. = ALIGN(PAGE_SIZE);
__init_end = .;
BSS_SECTION(0, PAGE_SIZE, 4)
- _ebss = .; /* uClinux MTD sucks */
_end = . ;
STABS_DEBUG
diff --git a/arch/sh/lib/mcount.S b/arch/sh/lib/mcount.S
index 84a57761f17e..60164e65d665 100644
--- a/arch/sh/lib/mcount.S
+++ b/arch/sh/lib/mcount.S
@@ -39,7 +39,7 @@
*
* Make sure the stack pointer contains a valid address. Valid
* addresses for kernel stacks are anywhere after the bss
- * (after _ebss) and anywhere in init_thread_union (init_stack).
+ * (after __bss_stop) and anywhere in init_thread_union (init_stack).
*/
#define STACK_CHECK() \
mov #(THREAD_SIZE >> 10), r0; \
@@ -60,7 +60,7 @@
cmp/hi r2, r1; \
bf stack_panic; \
\
- /* If sp > _ebss then we're OK. */ \
+ /* If sp > __bss_stop then we're OK. */ \
mov.l .L_ebss, r1; \
cmp/hi r1, r15; \
bt 1f; \
@@ -70,7 +70,7 @@
cmp/hs r1, r15; \
bf stack_panic; \
\
- /* If sp > init_stack && sp < _ebss, not OK. */ \
+ /* If sp > init_stack && sp < __bss_stop, not OK. */ \
add r0, r1; \
cmp/hs r1, r15; \
bt stack_panic; \
@@ -292,8 +292,6 @@ stack_panic:
nop
.align 2
-.L_ebss:
- .long _ebss
.L_init_thread_union:
.long init_thread_union
.Lpanic:
diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c
index 1fc25d85e515..3bdc1ad9a341 100644
--- a/arch/sh/mm/fault.c
+++ b/arch/sh/mm/fault.c
@@ -58,11 +58,15 @@ static void show_pte(struct mm_struct *mm, unsigned long addr)
{
pgd_t *pgd;
- if (mm)
+ if (mm) {
pgd = mm->pgd;
- else
+ } else {
pgd = get_TTB();
+ if (unlikely(!pgd))
+ pgd = swapper_pg_dir;
+ }
+
printk(KERN_ALERT "pgd = %p\n", pgd);
pgd += pgd_index(addr);
printk(KERN_ALERT "[%08lx] *pgd=%0*Lx", addr,
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index 435e406fdec3..81d92fc9983b 100644
--- a/arch/sparc/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
@@ -1250,14 +1250,12 @@ int ldc_bind(struct ldc_channel *lp, const char *name)
snprintf(lp->rx_irq_name, LDC_IRQ_NAME_MAX, "%s RX", name);
snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
- err = request_irq(lp->cfg.rx_irq, ldc_rx,
- IRQF_SAMPLE_RANDOM | IRQF_DISABLED,
+ err = request_irq(lp->cfg.rx_irq, ldc_rx, IRQF_DISABLED,
lp->rx_irq_name, lp);
if (err)
return err;
- err = request_irq(lp->cfg.tx_irq, ldc_tx,
- IRQF_SAMPLE_RANDOM | IRQF_DISABLED,
+ err = request_irq(lp->cfg.tx_irq, ldc_tx, IRQF_DISABLED,
lp->tx_irq_name, lp);
if (err) {
free_irq(lp->cfg.rx_irq, lp);
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index 15e0a1693976..f1ddc0d23679 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -48,9 +48,7 @@ void *module_alloc(unsigned long size)
return NULL;
ret = module_map(size);
- if (!ret)
- ret = ERR_PTR(-ENOMEM);
- else
+ if (ret)
memset(ret, 0, size);
return ret;
@@ -116,6 +114,10 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
v = sym->st_value + rel[i].r_addend;
switch (ELF_R_TYPE(rel[i].r_info) & 0xff) {
+ case R_SPARC_DISP32:
+ v -= (Elf_Addr) location;
+ *loc32 = v;
+ break;
#ifdef CONFIG_SPARC64
case R_SPARC_64:
location[0] = v >> 56;
@@ -128,11 +130,6 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
location[7] = v >> 0;
break;
- case R_SPARC_DISP32:
- v -= (Elf_Addr) location;
- *loc32 = v;
- break;
-
case R_SPARC_WDISP19:
v -= (Elf_Addr) location;
*loc32 = (*loc32 & ~0x7ffff) |
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 0dc1f5786081..11c6c9603e71 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -502,12 +502,12 @@ SYSCALL_DEFINE1(sparc64_personality, unsigned long, personality)
{
int ret;
- if (current->personality == PER_LINUX32 &&
- personality == PER_LINUX)
- personality = PER_LINUX32;
+ if (personality(current->personality) == PER_LINUX32 &&
+ personality(personality) == PER_LINUX)
+ personality |= PER_LINUX32;
ret = sys_personality(personality);
- if (ret == PER_LINUX32)
- ret = PER_LINUX;
+ if (personality(ret) == PER_LINUX32)
+ ret &= ~PER_LINUX32;
return ret;
}
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 6026fdd1b2ed..d58edf5fefdb 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2020,6 +2020,9 @@ EXPORT_SYMBOL(_PAGE_CACHE);
#ifdef CONFIG_SPARSEMEM_VMEMMAP
unsigned long vmemmap_table[VMEMMAP_SIZE];
+static long __meminitdata addr_start, addr_end;
+static int __meminitdata node_start;
+
int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
{
unsigned long vstart = (unsigned long) start;
@@ -2050,15 +2053,30 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
*vmem_pp = pte_base | __pa(block);
- printk(KERN_INFO "[%p-%p] page_structs=%lu "
- "node=%d entry=%lu/%lu\n", start, block, nr,
- node,
- addr >> VMEMMAP_CHUNK_SHIFT,
- VMEMMAP_SIZE);
+ /* check to see if we have contiguous blocks */
+ if (addr_end != addr || node_start != node) {
+ if (addr_start)
+ printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
+ addr_start, addr_end-1, node_start);
+ addr_start = addr;
+ node_start = node;
+ }
+ addr_end = addr + VMEMMAP_CHUNK;
}
}
return 0;
}
+
+void __meminit vmemmap_populate_print_last(void)
+{
+ if (addr_start) {
+ printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
+ addr_start, addr_end-1, node_start);
+ addr_start = 0;
+ addr_end = 0;
+ node_start = 0;
+ }
+}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
static void prot_init_common(unsigned long page_none,
diff --git a/arch/tile/configs/tilegx_defconfig b/arch/tile/configs/tilegx_defconfig
index b8d99aca5431..0270620a1692 100644
--- a/arch/tile/configs/tilegx_defconfig
+++ b/arch/tile/configs/tilegx_defconfig
@@ -18,8 +18,8 @@ CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
diff --git a/arch/tile/configs/tilepro_defconfig b/arch/tile/configs/tilepro_defconfig
index 2b1fd31894f1..c11de27a9bcb 100644
--- a/arch/tile/configs/tilepro_defconfig
+++ b/arch/tile/configs/tilepro_defconfig
@@ -17,8 +17,8 @@ CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
diff --git a/arch/tile/include/gxio/iorpc_trio.h b/arch/tile/include/gxio/iorpc_trio.h
index 15fb77992083..58105c31228b 100644
--- a/arch/tile/include/gxio/iorpc_trio.h
+++ b/arch/tile/include/gxio/iorpc_trio.h
@@ -25,21 +25,23 @@
#include <linux/module.h>
#include <asm/pgtable.h>
-#define GXIO_TRIO_OP_ALLOC_ASIDS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_DEALLOC_ASID IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_ALLOC_ASIDS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1401)
-#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1402)
+#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1404)
-#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140e)
-#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140f)
+#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1412)
-#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1417)
-#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1418)
-#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1419)
-#define GXIO_TRIO_OP_CONFIG_MSI_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x141a)
+#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1414)
-#define GXIO_TRIO_OP_SET_MPS_MRS IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141c)
-#define GXIO_TRIO_OP_FORCE_RC_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141d)
-#define GXIO_TRIO_OP_FORCE_EP_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141f)
+#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1420)
+#define GXIO_TRIO_OP_CONFIG_MSI_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1421)
+
+#define GXIO_TRIO_OP_SET_MPS_MRS IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1423)
+#define GXIO_TRIO_OP_FORCE_RC_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1424)
+#define GXIO_TRIO_OP_FORCE_EP_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1425)
#define GXIO_TRIO_OP_GET_MMIO_BASE IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000)
#define GXIO_TRIO_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001)
diff --git a/arch/um/defconfig b/arch/um/defconfig
index 7823ab12e6a4..08107a795062 100644
--- a/arch/um/defconfig
+++ b/arch/um/defconfig
@@ -155,15 +155,15 @@ CONFIG_CPUSETS=y
CONFIG_PROC_PID_CPUSET=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEM_RES_CTLR=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
-# CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED is not set
-# CONFIG_CGROUP_MEM_RES_CTLR_KMEM is not set
+CONFIG_CGROUP_MEMCG=y
+CONFIG_CGROUP_MEMCG_SWAP=y
+# CONFIG_CGROUP_MEMCG_SWAP_ENABLED is not set
+# CONFIG_CGROUP_MEMCG_KMEM is not set
CONFIG_CGROUP_SCHED=y
CONFIG_FAIR_GROUP_SCHED=y
# CONFIG_CFS_BANDWIDTH is not set
# CONFIG_RT_GROUP_SCHED is not set
-CONFIG_BLK_CGROUP=m
+CONFIG_BLK_CGROUP=y
# CONFIG_DEBUG_BLK_CGROUP is not set
# CONFIG_CHECKPOINT_RESTORE is not set
CONFIG_NAMESPACES=y
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c
index 45e248c2f43c..87eebfe03c61 100644
--- a/arch/um/drivers/chan_kern.c
+++ b/arch/um/drivers/chan_kern.c
@@ -150,9 +150,11 @@ void chan_enable_winch(struct chan *chan, struct tty_struct *tty)
static void line_timer_cb(struct work_struct *work)
{
struct line *line = container_of(work, struct line, task.work);
+ struct tty_struct *tty = tty_port_tty_get(&line->port);
if (!line->throttled)
- chan_interrupt(line, line->tty, line->driver->read_irq);
+ chan_interrupt(line, tty, line->driver->read_irq);
+ tty_kref_put(tty);
}
int enable_chan(struct line *line)
diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c
index acfd0e0fd0c9..bbaf2c59830a 100644
--- a/arch/um/drivers/line.c
+++ b/arch/um/drivers/line.c
@@ -19,9 +19,11 @@ static irqreturn_t line_interrupt(int irq, void *data)
{
struct chan *chan = data;
struct line *line = chan->line;
+ struct tty_struct *tty = tty_port_tty_get(&line->port);
if (line)
- chan_interrupt(line, line->tty, irq);
+ chan_interrupt(line, tty, irq);
+ tty_kref_put(tty);
return IRQ_HANDLED;
}
@@ -219,92 +221,6 @@ void line_set_termios(struct tty_struct *tty, struct ktermios * old)
/* nothing */
}
-static const struct {
- int cmd;
- char *level;
- char *name;
-} tty_ioctls[] = {
- /* don't print these, they flood the log ... */
- { TCGETS, NULL, "TCGETS" },
- { TCSETS, NULL, "TCSETS" },
- { TCSETSW, NULL, "TCSETSW" },
- { TCFLSH, NULL, "TCFLSH" },
- { TCSBRK, NULL, "TCSBRK" },
-
- /* general tty stuff */
- { TCSETSF, KERN_DEBUG, "TCSETSF" },
- { TCGETA, KERN_DEBUG, "TCGETA" },
- { TIOCMGET, KERN_DEBUG, "TIOCMGET" },
- { TCSBRKP, KERN_DEBUG, "TCSBRKP" },
- { TIOCMSET, KERN_DEBUG, "TIOCMSET" },
-
- /* linux-specific ones */
- { TIOCLINUX, KERN_INFO, "TIOCLINUX" },
- { KDGKBMODE, KERN_INFO, "KDGKBMODE" },
- { KDGKBTYPE, KERN_INFO, "KDGKBTYPE" },
- { KDSIGACCEPT, KERN_INFO, "KDSIGACCEPT" },
-};
-
-int line_ioctl(struct tty_struct *tty, unsigned int cmd,
- unsigned long arg)
-{
- int ret;
- int i;
-
- ret = 0;
- switch(cmd) {
-#ifdef TIOCGETP
- case TIOCGETP:
- case TIOCSETP:
- case TIOCSETN:
-#endif
-#ifdef TIOCGETC
- case TIOCGETC:
- case TIOCSETC:
-#endif
-#ifdef TIOCGLTC
- case TIOCGLTC:
- case TIOCSLTC:
-#endif
- /* Note: these are out of date as we now have TCGETS2 etc but this
- whole lot should probably go away */
- case TCGETS:
- case TCSETSF:
- case TCSETSW:
- case TCSETS:
- case TCGETA:
- case TCSETAF:
- case TCSETAW:
- case TCSETA:
- case TCXONC:
- case TCFLSH:
- case TIOCOUTQ:
- case TIOCINQ:
- case TIOCGLCKTRMIOS:
- case TIOCSLCKTRMIOS:
- case TIOCPKT:
- case TIOCGSOFTCAR:
- case TIOCSSOFTCAR:
- return -ENOIOCTLCMD;
-#if 0
- case TCwhatever:
- /* do something */
- break;
-#endif
- default:
- for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++)
- if (cmd == tty_ioctls[i].cmd)
- break;
- if (i == ARRAY_SIZE(tty_ioctls)) {
- printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n",
- __func__, tty->name, cmd);
- }
- ret = -ENOIOCTLCMD;
- break;
- }
- return ret;
-}
-
void line_throttle(struct tty_struct *tty)
{
struct line *line = tty->driver_data;
@@ -333,7 +249,7 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
{
struct chan *chan = data;
struct line *line = chan->line;
- struct tty_struct *tty = line->tty;
+ struct tty_struct *tty;
int err;
/*
@@ -352,68 +268,42 @@ static irqreturn_t line_write_interrupt(int irq, void *data)
}
spin_unlock(&line->lock);
+ tty = tty_port_tty_get(&line->port);
if (tty == NULL)
return IRQ_NONE;
tty_wakeup(tty);
+ tty_kref_put(tty);
+
return IRQ_HANDLED;
}
int line_setup_irq(int fd, int input, int output, struct line *line, void *data)
{
const struct line_driver *driver = line->driver;
- int err = 0, flags = IRQF_SHARED | IRQF_SAMPLE_RANDOM;
+ int err = 0;
if (input)
err = um_request_irq(driver->read_irq, fd, IRQ_READ,
- line_interrupt, flags,
- driver->read_irq_name, data);
+ line_interrupt, IRQF_SHARED,
+ driver->read_irq_name, data);
if (err)
return err;
if (output)
err = um_request_irq(driver->write_irq, fd, IRQ_WRITE,
- line_write_interrupt, flags,
- driver->write_irq_name, data);
+ line_write_interrupt, IRQF_SHARED,
+ driver->write_irq_name, data);
return err;
}
-/*
- * Normally, a driver like this can rely mostly on the tty layer
- * locking, particularly when it comes to the driver structure.
- * However, in this case, mconsole requests can come in "from the
- * side", and race with opens and closes.
- *
- * mconsole config requests will want to be sure the device isn't in
- * use, and get_config, open, and close will want a stable
- * configuration. The checking and modification of the configuration
- * is done under a spinlock. Checking whether the device is in use is
- * line->tty->count > 1, also under the spinlock.
- *
- * line->count serves to decide whether the device should be enabled or
- * disabled on the host. If it's equal to 0, then we are doing the
- * first open or last close. Otherwise, open and close just return.
- */
-
-int line_open(struct line *lines, struct tty_struct *tty)
+static int line_activate(struct tty_port *port, struct tty_struct *tty)
{
- struct line *line = &lines[tty->index];
- int err = -ENODEV;
-
- mutex_lock(&line->count_lock);
- if (!line->valid)
- goto out_unlock;
-
- err = 0;
- if (line->count++)
- goto out_unlock;
-
- BUG_ON(tty->driver_data);
- tty->driver_data = line;
- line->tty = tty;
+ int ret;
+ struct line *line = tty->driver_data;
- err = enable_chan(line);
- if (err) /* line_close() will be called by our caller */
- goto out_unlock;
+ ret = enable_chan(line);
+ if (ret)
+ return ret;
if (!line->sigio) {
chan_enable_winch(line->chan_out, tty);
@@ -421,44 +311,60 @@ int line_open(struct line *lines, struct tty_struct *tty)
}
chan_window_size(line, &tty->winsize.ws_row,
- &tty->winsize.ws_col);
-out_unlock:
- mutex_unlock(&line->count_lock);
- return err;
+ &tty->winsize.ws_col);
+
+ return 0;
}
-static void unregister_winch(struct tty_struct *tty);
+static const struct tty_port_operations line_port_ops = {
+ .activate = line_activate,
+};
-void line_close(struct tty_struct *tty, struct file * filp)
+int line_open(struct tty_struct *tty, struct file *filp)
{
struct line *line = tty->driver_data;
- /*
- * If line_open fails (and tty->driver_data is never set),
- * tty_open will call line_close. So just return in this case.
- */
- if (line == NULL)
- return;
+ return tty_port_open(&line->port, tty, filp);
+}
- /* We ignore the error anyway! */
- flush_buffer(line);
+int line_install(struct tty_driver *driver, struct tty_struct *tty,
+ struct line *line)
+{
+ int ret;
- mutex_lock(&line->count_lock);
- BUG_ON(!line->valid);
+ ret = tty_standard_install(driver, tty);
+ if (ret)
+ return ret;
- if (--line->count)
- goto out_unlock;
+ tty->driver_data = line;
- line->tty = NULL;
- tty->driver_data = NULL;
+ return 0;
+}
+
+static void unregister_winch(struct tty_struct *tty);
+
+void line_cleanup(struct tty_struct *tty)
+{
+ struct line *line = tty->driver_data;
if (line->sigio) {
unregister_winch(tty);
line->sigio = 0;
}
+}
+
+void line_close(struct tty_struct *tty, struct file * filp)
+{
+ struct line *line = tty->driver_data;
-out_unlock:
- mutex_unlock(&line->count_lock);
+ tty_port_close(&line->port, tty, filp);
+}
+
+void line_hangup(struct tty_struct *tty)
+{
+ struct line *line = tty->driver_data;
+
+ tty_port_hangup(&line->port);
}
void close_lines(struct line *lines, int nlines)
@@ -476,9 +382,7 @@ int setup_one_line(struct line *lines, int n, char *init,
struct tty_driver *driver = line->driver->driver;
int err = -EINVAL;
- mutex_lock(&line->count_lock);
-
- if (line->count) {
+ if (line->port.count) {
*error_out = "Device is already open";
goto out;
}
@@ -519,7 +423,6 @@ int setup_one_line(struct line *lines, int n, char *init,
}
}
out:
- mutex_unlock(&line->count_lock);
return err;
}
@@ -607,13 +510,17 @@ int line_get_config(char *name, struct line *lines, unsigned int num, char *str,
line = &lines[dev];
- mutex_lock(&line->count_lock);
if (!line->valid)
CONFIG_CHUNK(str, size, n, "none", 1);
- else if (line->tty == NULL)
- CONFIG_CHUNK(str, size, n, line->init_str, 1);
- else n = chan_config_string(line, str, size, error_out);
- mutex_unlock(&line->count_lock);
+ else {
+ struct tty_struct *tty = tty_port_tty_get(&line->port);
+ if (tty == NULL) {
+ CONFIG_CHUNK(str, size, n, line->init_str, 1);
+ } else {
+ n = chan_config_string(line, str, size, error_out);
+ tty_kref_put(tty);
+ }
+ }
return n;
}
@@ -663,8 +570,9 @@ int register_lines(struct line_driver *line_driver,
driver->init_termios = tty_std_termios;
for (i = 0; i < nlines; i++) {
+ tty_port_init(&lines[i].port);
+ lines[i].port.ops = &line_port_ops;
spin_lock_init(&lines[i].lock);
- mutex_init(&lines[i].count_lock);
lines[i].driver = line_driver;
INIT_LIST_HEAD(&lines[i].chan_list);
}
@@ -779,8 +687,7 @@ void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty,
.stack = stack });
if (um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- "winch", winch) < 0) {
+ IRQF_SHARED, "winch", winch) < 0) {
printk(KERN_ERR "register_winch_irq - failed to register "
"IRQ\n");
goto out_free;
diff --git a/arch/um/drivers/line.h b/arch/um/drivers/line.h
index 0a1834719dba..bae95611e7ab 100644
--- a/arch/um/drivers/line.h
+++ b/arch/um/drivers/line.h
@@ -32,9 +32,7 @@ struct line_driver {
};
struct line {
- struct tty_struct *tty;
- struct mutex count_lock;
- unsigned long count;
+ struct tty_port port;
int valid;
char *init_str;
@@ -59,7 +57,11 @@ struct line {
};
extern void line_close(struct tty_struct *tty, struct file * filp);
-extern int line_open(struct line *lines, struct tty_struct *tty);
+extern int line_open(struct tty_struct *tty, struct file *filp);
+extern int line_install(struct tty_driver *driver, struct tty_struct *tty,
+ struct line *line);
+extern void line_cleanup(struct tty_struct *tty);
+extern void line_hangup(struct tty_struct *tty);
extern int line_setup(char **conf, unsigned nlines, char **def,
char *init, char *name);
extern int line_write(struct tty_struct *tty, const unsigned char *buf,
@@ -70,8 +72,6 @@ extern int line_chars_in_buffer(struct tty_struct *tty);
extern void line_flush_buffer(struct tty_struct *tty);
extern void line_flush_chars(struct tty_struct *tty);
extern int line_write_room(struct tty_struct *tty);
-extern int line_ioctl(struct tty_struct *tty, unsigned int cmd,
- unsigned long arg);
extern void line_throttle(struct tty_struct *tty);
extern void line_unthrottle(struct tty_struct *tty);
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 43b39d61b538..664a60e8dfb4 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -774,8 +774,7 @@ static int __init mconsole_init(void)
register_reboot_notifier(&reboot_notifier);
err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- "mconsole", (void *)sock);
+ IRQF_SHARED, "mconsole", (void *)sock);
if (err) {
printk(KERN_ERR "Failed to get IRQ for management console\n");
goto out;
diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c
index 11866ffd45a9..1d83d50236e1 100644
--- a/arch/um/drivers/port_kern.c
+++ b/arch/um/drivers/port_kern.c
@@ -100,8 +100,7 @@ static int port_accept(struct port_list *port)
.port = port });
if (um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- "telnetd", conn)) {
+ IRQF_SHARED, "telnetd", conn)) {
printk(KERN_ERR "port_accept : failed to get IRQ for "
"telnetd\n");
goto out_free;
@@ -184,8 +183,7 @@ void *port_data(int port_num)
}
if (um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- "port", port)) {
+ IRQF_SHARED, "port", port)) {
printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
goto out_close;
}
diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c
index b25296e6218a..e32c6aa6396f 100644
--- a/arch/um/drivers/random.c
+++ b/arch/um/drivers/random.c
@@ -131,8 +131,7 @@ static int __init rng_init (void)
random_fd = err;
err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
- IRQF_SAMPLE_RANDOM, "random",
- NULL);
+ 0, "random", NULL);
if (err)
goto err_out_cleanup_hw;
diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c
index e09801a1327b..7e86f0070123 100644
--- a/arch/um/drivers/ssl.c
+++ b/arch/um/drivers/ssl.c
@@ -87,40 +87,13 @@ static int ssl_remove(int n, char **error_out)
error_out);
}
-static int ssl_open(struct tty_struct *tty, struct file *filp)
-{
- int err = line_open(serial_lines, tty);
-
- if (err)
- printk(KERN_ERR "Failed to open serial line %d, err = %d\n",
- tty->index, err);
-
- return err;
-}
-
-#if 0
-static void ssl_flush_buffer(struct tty_struct *tty)
-{
- return;
-}
-
-static void ssl_stop(struct tty_struct *tty)
-{
- printk(KERN_ERR "Someone should implement ssl_stop\n");
-}
-
-static void ssl_start(struct tty_struct *tty)
-{
- printk(KERN_ERR "Someone should implement ssl_start\n");
-}
-
-void ssl_hangup(struct tty_struct *tty)
+static int ssl_install(struct tty_driver *driver, struct tty_struct *tty)
{
+ return line_install(driver, tty, &serial_lines[tty->index]);
}
-#endif
static const struct tty_operations ssl_ops = {
- .open = ssl_open,
+ .open = line_open,
.close = line_close,
.write = line_write,
.put_char = line_put_char,
@@ -129,14 +102,11 @@ static const struct tty_operations ssl_ops = {
.flush_buffer = line_flush_buffer,
.flush_chars = line_flush_chars,
.set_termios = line_set_termios,
- .ioctl = line_ioctl,
.throttle = line_throttle,
.unthrottle = line_unthrottle,
-#if 0
- .stop = ssl_stop,
- .start = ssl_start,
- .hangup = ssl_hangup,
-#endif
+ .install = ssl_install,
+ .cleanup = line_cleanup,
+ .hangup = line_hangup,
};
/* Changed by ssl_init and referenced by ssl_exit, which are both serialized
diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c
index 7663541c372e..929b99a261f3 100644
--- a/arch/um/drivers/stdio_console.c
+++ b/arch/um/drivers/stdio_console.c
@@ -89,21 +89,17 @@ static int con_remove(int n, char **error_out)
return line_remove(vts, ARRAY_SIZE(vts), n, error_out);
}
-static int con_open(struct tty_struct *tty, struct file *filp)
-{
- int err = line_open(vts, tty);
- if (err)
- printk(KERN_ERR "Failed to open console %d, err = %d\n",
- tty->index, err);
-
- return err;
-}
-
/* Set in an initcall, checked in an exitcall */
static int con_init_done = 0;
+static int con_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ return line_install(driver, tty, &vts[tty->index]);
+}
+
static const struct tty_operations console_ops = {
- .open = con_open,
+ .open = line_open,
+ .install = con_install,
.close = line_close,
.write = line_write,
.put_char = line_put_char,
@@ -112,9 +108,10 @@ static const struct tty_operations console_ops = {
.flush_buffer = line_flush_buffer,
.flush_chars = line_flush_chars,
.set_termios = line_set_termios,
- .ioctl = line_ioctl,
.throttle = line_throttle,
.unthrottle = line_unthrottle,
+ .cleanup = line_cleanup,
+ .hangup = line_hangup,
};
static void uml_console_write(struct console *console, const char *string,
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
index 20505cafa299..0643e5bc9f41 100644
--- a/arch/um/drivers/ubd_kern.c
+++ b/arch/um/drivers/ubd_kern.c
@@ -514,7 +514,7 @@ static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out)
goto out;
}
- fd = os_open_file(ubd_dev->file, global_openflags, 0);
+ fd = os_open_file(ubd_dev->file, of_read(OPENFLAGS()), 0);
if (fd < 0)
return fd;
diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c
index b68bbe269e01..e3031e69445d 100644
--- a/arch/um/drivers/xterm_kern.c
+++ b/arch/um/drivers/xterm_kern.c
@@ -50,8 +50,7 @@ int xterm_fd(int socket, int *pid_out)
init_completion(&data->ready);
err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- "xterm", data);
+ IRQF_SHARED, "xterm", data);
if (err) {
printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, "
"err = %d\n", err);
diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h
index 69f1c57a8d0d..33a6a2423bd2 100644
--- a/arch/um/include/asm/processor-generic.h
+++ b/arch/um/include/asm/processor-generic.h
@@ -20,14 +20,6 @@ struct mm_struct;
struct thread_struct {
struct task_struct *saved_task;
- /*
- * This flag is set to 1 before calling do_fork (and analyzed in
- * copy_thread) to mark that we are begin called from userspace (fork /
- * vfork / clone), and reset to 0 after. It is left to 0 when called
- * from kernelspace (i.e. kernel_thread() or fork_idle(),
- * as of 2.6.11).
- */
- int forking;
struct pt_regs regs;
int singlestep_syscall;
void *fault_addr;
@@ -58,7 +50,6 @@ struct thread_struct {
#define INIT_THREAD \
{ \
- .forking = 0, \
.regs = EMPTY_REGS, \
.fault_addr = NULL, \
.prev_sched = NULL, \
diff --git a/arch/um/include/asm/ptrace-generic.h b/arch/um/include/asm/ptrace-generic.h
index e786a6a3ec5e..442f1d025dc2 100644
--- a/arch/um/include/asm/ptrace-generic.h
+++ b/arch/um/include/asm/ptrace-generic.h
@@ -37,6 +37,8 @@ extern int putreg(struct task_struct *child, int regno, unsigned long value);
extern int arch_copy_tls(struct task_struct *new);
extern void clear_flushed_tls(struct task_struct *task);
+extern void syscall_trace_enter(struct pt_regs *regs);
+extern void syscall_trace_leave(struct pt_regs *regs);
#endif
diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h
index 896e16602176..86daa5461815 100644
--- a/arch/um/include/shared/as-layout.h
+++ b/arch/um/include/shared/as-layout.h
@@ -60,7 +60,8 @@ extern unsigned long host_task_size;
extern int linux_main(int argc, char **argv);
-extern void (*sig_info[])(int, struct uml_pt_regs *);
+struct siginfo;
+extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *);
#endif
diff --git a/arch/um/include/shared/common-offsets.h b/arch/um/include/shared/common-offsets.h
index 40db8f71deae..2df313b6a586 100644
--- a/arch/um/include/shared/common-offsets.h
+++ b/arch/um/include/shared/common-offsets.h
@@ -7,16 +7,6 @@ DEFINE(UM_KERN_PAGE_MASK, PAGE_MASK);
DEFINE(UM_KERN_PAGE_SHIFT, PAGE_SHIFT);
DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC);
-DEFINE_STR(UM_KERN_EMERG, KERN_EMERG);
-DEFINE_STR(UM_KERN_ALERT, KERN_ALERT);
-DEFINE_STR(UM_KERN_CRIT, KERN_CRIT);
-DEFINE_STR(UM_KERN_ERR, KERN_ERR);
-DEFINE_STR(UM_KERN_WARNING, KERN_WARNING);
-DEFINE_STR(UM_KERN_NOTICE, KERN_NOTICE);
-DEFINE_STR(UM_KERN_INFO, KERN_INFO);
-DEFINE_STR(UM_KERN_DEBUG, KERN_DEBUG);
-DEFINE_STR(UM_KERN_CONT, KERN_CONT);
-
DEFINE(UM_ELF_CLASS, ELF_CLASS);
DEFINE(UM_ELFCLASS32, ELFCLASS32);
DEFINE(UM_ELFCLASS64, ELFCLASS64);
diff --git a/arch/um/include/shared/irq_user.h b/arch/um/include/shared/irq_user.h
index c6c784df2673..2b6d703925b5 100644
--- a/arch/um/include/shared/irq_user.h
+++ b/arch/um/include/shared/irq_user.h
@@ -20,7 +20,8 @@ struct irq_fd {
enum { IRQ_READ, IRQ_WRITE };
-extern void sigio_handler(int sig, struct uml_pt_regs *regs);
+struct siginfo;
+extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void free_irq_by_fd(int fd);
extern void reactivate_fd(int fd, int irqnum);
extern void deactivate_fd(int fd, int irqnum);
diff --git a/arch/um/include/shared/kern_util.h b/arch/um/include/shared/kern_util.h
index 00965d06d2ca..af6b6dc868ba 100644
--- a/arch/um/include/shared/kern_util.h
+++ b/arch/um/include/shared/kern_util.h
@@ -9,6 +9,8 @@
#include "sysdep/ptrace.h"
#include "sysdep/faultinfo.h"
+struct siginfo;
+
extern int uml_exitcode;
extern int ncpus;
@@ -22,7 +24,7 @@ extern void free_stack(unsigned long stack, int order);
extern int do_signal(void);
extern void interrupt_end(void);
-extern void relay_signal(int sig, struct uml_pt_regs *regs);
+extern void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs);
extern unsigned long segv(struct faultinfo fi, unsigned long ip,
int is_user, struct uml_pt_regs *regs);
@@ -33,9 +35,8 @@ extern unsigned int do_IRQ(int irq, struct uml_pt_regs *regs);
extern int smp_sigio_handler(void);
extern void initial_thread_cb(void (*proc)(void *), void *arg);
extern int is_syscall(unsigned long addr);
-extern void timer_handler(int sig, struct uml_pt_regs *regs);
-extern void timer_handler(int sig, struct uml_pt_regs *regs);
+extern void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern int start_uml(void);
extern void paging_init(void);
@@ -59,9 +60,9 @@ extern unsigned long from_irq_stack(int nested);
extern void syscall_trace(struct uml_pt_regs *regs, int entryexit);
extern int singlestepping(void *t);
-extern void segv_handler(int sig, struct uml_pt_regs *regs);
-extern void bus_handler(int sig, struct uml_pt_regs *regs);
-extern void winch(int sig, struct uml_pt_regs *regs);
+extern void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
+extern void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs);
+extern void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
extern void fatal_sigsegv(void) __attribute__ ((noreturn));
diff --git a/arch/um/include/shared/user.h b/arch/um/include/shared/user.h
index 4fa82c055aab..cef068563336 100644
--- a/arch/um/include/shared/user.h
+++ b/arch/um/include/shared/user.h
@@ -26,6 +26,17 @@
extern void panic(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
+/* Requires preincluding include/linux/kern_levels.h */
+#define UM_KERN_EMERG KERN_EMERG
+#define UM_KERN_ALERT KERN_ALERT
+#define UM_KERN_CRIT KERN_CRIT
+#define UM_KERN_ERR KERN_ERR
+#define UM_KERN_WARNING KERN_WARNING
+#define UM_KERN_NOTICE KERN_NOTICE
+#define UM_KERN_INFO KERN_INFO
+#define UM_KERN_DEBUG KERN_DEBUG
+#define UM_KERN_CONT KERN_CONT
+
#ifdef UML_CONFIG_PRINTK
extern int printk(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c
index 6cade9366364..8c82786da823 100644
--- a/arch/um/kernel/exec.c
+++ b/arch/um/kernel/exec.c
@@ -39,34 +39,21 @@ void flush_thread(void)
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
{
+ get_safe_registers(regs->regs.gp, regs->regs.fp);
PT_REGS_IP(regs) = eip;
PT_REGS_SP(regs) = esp;
-}
-EXPORT_SYMBOL(start_thread);
-
-static long execve1(const char *file,
- const char __user *const __user *argv,
- const char __user *const __user *env)
-{
- long error;
-
- error = do_execve(file, argv, env, &current->thread.regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
+ current->ptrace &= ~PT_DTRACE;
#ifdef SUBARCH_EXECVE1
- SUBARCH_EXECVE1(&current->thread.regs.regs);
+ SUBARCH_EXECVE1(regs->regs);
#endif
- task_unlock(current);
- }
- return error;
}
+EXPORT_SYMBOL(start_thread);
long um_execve(const char *file, const char __user *const __user *argv, const char __user *const __user *env)
{
long err;
- err = execve1(file, argv, env);
+ err = do_execve(file, argv, env, &current->thread.regs);
if (!err)
UML_LONGJMP(current->thread.exec_buf, 1);
return err;
@@ -81,7 +68,7 @@ long sys_execve(const char __user *file, const char __user *const __user *argv,
filename = getname(file);
error = PTR_ERR(filename);
if (IS_ERR(filename)) goto out;
- error = execve1(filename, argv, env);
+ error = do_execve(filename, argv, env, &current->thread.regs);
putname(filename);
out:
return error;
diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c
index 00506c3d5d6e..9883026f0730 100644
--- a/arch/um/kernel/irq.c
+++ b/arch/um/kernel/irq.c
@@ -30,7 +30,7 @@ static struct irq_fd **last_irq_ptr = &active_fds;
extern void free_irqs(void);
-void sigio_handler(int sig, struct uml_pt_regs *regs)
+void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
struct irq_fd *irq_fd;
int n;
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index ccb9a9d283f1..c5f5afa50745 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -151,12 +151,10 @@ void new_thread_handler(void)
* 0 if it just exits
*/
n = run_kernel_thread(fn, arg, &current->thread.exec_buf);
- if (n == 1) {
- /* Handle any immediate reschedules or signals */
- interrupt_end();
+ if (n == 1)
userspace(&current->thread.regs.regs);
- }
- else do_exit(0);
+ else
+ do_exit(0);
}
/* Called magically, see new_thread_handler above */
@@ -175,9 +173,6 @@ void fork_handler(void)
current->thread.prev_sched = NULL;
- /* Handle any immediate reschedules or signals */
- interrupt_end();
-
userspace(&current->thread.regs.regs);
}
@@ -186,22 +181,22 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
struct pt_regs *regs)
{
void (*handler)(void);
+ int kthread = current->flags & PF_KTHREAD;
int ret = 0;
p->thread = (struct thread_struct) INIT_THREAD;
- if (current->thread.forking) {
+ if (!kthread) {
memcpy(&p->thread.regs.regs, &regs->regs,
sizeof(p->thread.regs.regs));
- UPT_SET_SYSCALL_RETURN(&p->thread.regs.regs, 0);
+ PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0);
if (sp != 0)
REGS_SP(p->thread.regs.regs.gp) = sp;
handler = fork_handler;
arch_copy_thread(&current->thread.arch, &p->thread.arch);
- }
- else {
+ } else {
get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
p->thread.request.u.thread = current->thread.request.u.thread;
handler = new_thread_handler;
@@ -209,7 +204,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
new_thread(task_stack_page(p), &p->thread.switch_buf, handler);
- if (current->thread.forking) {
+ if (!kthread) {
clear_flushed_tls(p);
/*
diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c
index 06b190390505..694d551c8899 100644
--- a/arch/um/kernel/ptrace.c
+++ b/arch/um/kernel/ptrace.c
@@ -3,11 +3,12 @@
* Licensed under the GPL
*/
-#include "linux/audit.h"
-#include "linux/ptrace.h"
-#include "linux/sched.h"
-#include "asm/uaccess.h"
-#include "skas_ptrace.h"
+#include <linux/audit.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/tracehook.h>
+#include <asm/uaccess.h>
+#include <skas_ptrace.h>
@@ -162,48 +163,36 @@ static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
* XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
* PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
*/
-void syscall_trace(struct uml_pt_regs *regs, int entryexit)
+void syscall_trace_enter(struct pt_regs *regs)
{
- int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
- int tracesysgood;
-
- if (!entryexit)
- audit_syscall_entry(HOST_AUDIT_ARCH,
- UPT_SYSCALL_NR(regs),
- UPT_SYSCALL_ARG1(regs),
- UPT_SYSCALL_ARG2(regs),
- UPT_SYSCALL_ARG3(regs),
- UPT_SYSCALL_ARG4(regs));
- else
- audit_syscall_exit(regs);
-
- /* Fake a debug trap */
- if (is_singlestep)
- send_sigtrap(current, regs, 0);
+ audit_syscall_entry(HOST_AUDIT_ARCH,
+ UPT_SYSCALL_NR(&regs->regs),
+ UPT_SYSCALL_ARG1(&regs->regs),
+ UPT_SYSCALL_ARG2(&regs->regs),
+ UPT_SYSCALL_ARG3(&regs->regs),
+ UPT_SYSCALL_ARG4(&regs->regs));
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
- if (!(current->ptrace & PT_PTRACED))
- return;
+ tracehook_report_syscall_entry(regs);
+}
- /*
- * the 0x80 provides a way for the tracing parent to distinguish
- * between a syscall stop and SIGTRAP delivery
- */
- tracesysgood = (current->ptrace & PT_TRACESYSGOOD);
- ptrace_notify(SIGTRAP | (tracesysgood ? 0x80 : 0));
+void syscall_trace_leave(struct pt_regs *regs)
+{
+ int ptraced = current->ptrace;
- if (entryexit) /* force do_signal() --> is_syscall() */
- set_thread_flag(TIF_SIGPENDING);
+ audit_syscall_exit(regs);
- /*
- * this isn't the same as continuing with a signal, but it will do
- * for normal use. strace only continues with a signal if the
- * stopping signal is not SIGTRAP. -brl
- */
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
- }
+ /* Fake a debug trap */
+ if (ptraced & PT_DTRACE)
+ send_sigtrap(current, &regs->regs, 0);
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return;
+
+ tracehook_report_syscall_exit(regs, 0);
+ /* force do_signal() --> is_syscall() */
+ if (ptraced & PT_PTRACED)
+ set_thread_flag(TIF_SIGPENDING);
}
diff --git a/arch/um/kernel/sigio.c b/arch/um/kernel/sigio.c
index 2a1639255763..c88211139a51 100644
--- a/arch/um/kernel/sigio.c
+++ b/arch/um/kernel/sigio.c
@@ -25,8 +25,7 @@ int write_sigio_irq(int fd)
int err;
err = um_request_irq(SIGIO_WRITE_IRQ, fd, IRQ_READ, sigio_interrupt,
- IRQF_SAMPLE_RANDOM, "write sigio",
- NULL);
+ 0, "write sigio", NULL);
if (err) {
printk(KERN_ERR "write_sigio_irq : um_request_irq failed, "
"err = %d\n", err);
diff --git a/arch/um/kernel/signal.c b/arch/um/kernel/signal.c
index 7362d58efc29..cc9c2350e417 100644
--- a/arch/um/kernel/signal.c
+++ b/arch/um/kernel/signal.c
@@ -22,9 +22,13 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
struct k_sigaction *ka, siginfo_t *info)
{
sigset_t *oldset = sigmask_to_save();
+ int singlestep = 0;
unsigned long sp;
int err;
+ if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+ singlestep = 1;
+
/* Did we come from a system call? */
if (PT_REGS_SYSCALL_NR(regs) >= 0) {
/* If so, check system call restarting.. */
@@ -61,7 +65,7 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
if (err)
force_sigsegv(signr, current);
else
- signal_delivered(signr, info, ka, regs, 0);
+ signal_delivered(signr, info, ka, regs, singlestep);
}
static int kern_do_signal(struct pt_regs *regs)
diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c
index 05fbeb480e0b..86368a025a96 100644
--- a/arch/um/kernel/skas/syscall.c
+++ b/arch/um/kernel/skas/syscall.c
@@ -18,7 +18,7 @@ void handle_syscall(struct uml_pt_regs *r)
long result;
int syscall;
- syscall_trace(r, 0);
+ syscall_trace_enter(regs);
/*
* This should go in the declaration of syscall, but when I do that,
@@ -34,7 +34,7 @@ void handle_syscall(struct uml_pt_regs *r)
result = -ENOSYS;
else result = EXECUTE_SYSCALL(syscall, regs);
- UPT_SET_SYSCALL_RETURN(r, result);
+ PT_REGS_SET_SYSCALL_RETURN(regs, result);
- syscall_trace(r, 1);
+ syscall_trace_leave(regs);
}
diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c
index f958cb876ee3..a4c6d8eee74c 100644
--- a/arch/um/kernel/syscall.c
+++ b/arch/um/kernel/syscall.c
@@ -17,25 +17,25 @@
long sys_fork(void)
{
- long ret;
-
- current->thread.forking = 1;
- ret = do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
+ return do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
- current->thread.forking = 0;
- return ret;
}
long sys_vfork(void)
{
- long ret;
-
- current->thread.forking = 1;
- ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
- current->thread.forking = 0;
- return ret;
+}
+
+long sys_clone(unsigned long clone_flags, unsigned long newsp,
+ void __user *parent_tid, void __user *child_tid)
+{
+ if (!newsp)
+ newsp = UPT_SP(&current->thread.regs.regs);
+
+ return do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
+ child_tid);
}
long old_mmap(unsigned long addr, unsigned long len,
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index d1a23fb3190d..5f76d4ba151c 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -13,7 +13,7 @@
#include "kern_util.h"
#include "os.h"
-void timer_handler(int sig, struct uml_pt_regs *regs)
+void timer_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
unsigned long flags;
diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c
index 3be60765c0e2..0353b98ae35a 100644
--- a/arch/um/kernel/trap.c
+++ b/arch/um/kernel/trap.c
@@ -172,7 +172,7 @@ void fatal_sigsegv(void)
os_dump_core();
}
-void segv_handler(int sig, struct uml_pt_regs *regs)
+void segv_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
struct faultinfo * fi = UPT_FAULTINFO(regs);
@@ -258,8 +258,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
return 0;
}
-void relay_signal(int sig, struct uml_pt_regs *regs)
+void relay_signal(int sig, struct siginfo *si, struct uml_pt_regs *regs)
{
+ struct faultinfo *fi;
+ struct siginfo clean_si;
+
if (!UPT_IS_USER(regs)) {
if (sig == SIGBUS)
printk(KERN_ERR "Bus error - the host /dev/shm or /tmp "
@@ -269,18 +272,40 @@ void relay_signal(int sig, struct uml_pt_regs *regs)
arch_examine_signal(sig, regs);
- current->thread.arch.faultinfo = *UPT_FAULTINFO(regs);
- force_sig(sig, current);
+ memset(&clean_si, 0, sizeof(clean_si));
+ clean_si.si_signo = si->si_signo;
+ clean_si.si_errno = si->si_errno;
+ clean_si.si_code = si->si_code;
+ switch (sig) {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ case SIGTRAP:
+ fi = UPT_FAULTINFO(regs);
+ clean_si.si_addr = (void __user *) FAULT_ADDRESS(*fi);
+ current->thread.arch.faultinfo = *fi;
+#ifdef __ARCH_SI_TRAPNO
+ clean_si.si_trapno = si->si_trapno;
+#endif
+ break;
+ default:
+ printk(KERN_ERR "Attempted to relay unknown signal %d (si_code = %d)\n",
+ sig, si->si_code);
+ }
+
+ force_sig_info(sig, &clean_si, current);
}
-void bus_handler(int sig, struct uml_pt_regs *regs)
+void bus_handler(int sig, struct siginfo *si, struct uml_pt_regs *regs)
{
if (current->thread.fault_catcher != NULL)
UML_LONGJMP(current->thread.fault_catcher, 1);
- else relay_signal(sig, regs);
+ else
+ relay_signal(sig, si, regs);
}
-void winch(int sig, struct uml_pt_regs *regs)
+void winch(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
{
do_IRQ(WINCH_IRQ, regs);
}
diff --git a/arch/um/os-Linux/internal.h b/arch/um/os-Linux/internal.h
index 2c3c3ecd8c01..0dc2c9f135f6 100644
--- a/arch/um/os-Linux/internal.h
+++ b/arch/um/os-Linux/internal.h
@@ -1 +1 @@
-void alarm_handler(int, mcontext_t *);
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc);
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c
index 2d22f1fcd8e2..6366ce904b9b 100644
--- a/arch/um/os-Linux/signal.c
+++ b/arch/um/os-Linux/signal.c
@@ -13,8 +13,9 @@
#include "kern_util.h"
#include "os.h"
#include "sysdep/mcontext.h"
+#include "internal.h"
-void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
+void (*sig_info[NSIG])(int, siginfo_t *, struct uml_pt_regs *) = {
[SIGTRAP] = relay_signal,
[SIGFPE] = relay_signal,
[SIGILL] = relay_signal,
@@ -24,7 +25,7 @@ void (*sig_info[NSIG])(int, struct uml_pt_regs *) = {
[SIGIO] = sigio_handler,
[SIGVTALRM] = timer_handler };
-static void sig_handler_common(int sig, mcontext_t *mc)
+static void sig_handler_common(int sig, siginfo_t *si, mcontext_t *mc)
{
struct uml_pt_regs r;
int save_errno = errno;
@@ -40,7 +41,7 @@ static void sig_handler_common(int sig, mcontext_t *mc)
if ((sig != SIGIO) && (sig != SIGWINCH) && (sig != SIGVTALRM))
unblock_signals();
- (*sig_info[sig])(sig, &r);
+ (*sig_info[sig])(sig, si, &r);
errno = save_errno;
}
@@ -60,7 +61,7 @@ static void sig_handler_common(int sig, mcontext_t *mc)
static int signals_enabled;
static unsigned int signals_pending;
-void sig_handler(int sig, mcontext_t *mc)
+void sig_handler(int sig, siginfo_t *si, mcontext_t *mc)
{
int enabled;
@@ -72,7 +73,7 @@ void sig_handler(int sig, mcontext_t *mc)
block_signals();
- sig_handler_common(sig, mc);
+ sig_handler_common(sig, si, mc);
set_signals(enabled);
}
@@ -85,10 +86,10 @@ static void real_alarm_handler(mcontext_t *mc)
get_regs_from_mc(&regs, mc);
regs.is_user = 0;
unblock_signals();
- timer_handler(SIGVTALRM, &regs);
+ timer_handler(SIGVTALRM, NULL, &regs);
}
-void alarm_handler(int sig, mcontext_t *mc)
+void alarm_handler(int sig, struct siginfo *unused_si, mcontext_t *mc)
{
int enabled;
@@ -119,7 +120,7 @@ void set_sigstack(void *sig_stack, int size)
panic("enabling signal stack failed, errno = %d\n", errno);
}
-static void (*handlers[_NSIG])(int sig, mcontext_t *mc) = {
+static void (*handlers[_NSIG])(int sig, siginfo_t *si, mcontext_t *mc) = {
[SIGSEGV] = sig_handler,
[SIGBUS] = sig_handler,
[SIGILL] = sig_handler,
@@ -132,7 +133,7 @@ static void (*handlers[_NSIG])(int sig, mcontext_t *mc) = {
};
-static void hard_handler(int sig, siginfo_t *info, void *p)
+static void hard_handler(int sig, siginfo_t *si, void *p)
{
struct ucontext *uc = p;
mcontext_t *mc = &uc->uc_mcontext;
@@ -161,7 +162,7 @@ static void hard_handler(int sig, siginfo_t *info, void *p)
while ((sig = ffs(pending)) != 0){
sig--;
pending &= ~(1 << sig);
- (*handlers[sig])(sig, mc);
+ (*handlers[sig])(sig, si, mc);
}
/*
@@ -273,9 +274,12 @@ void unblock_signals(void)
* Deal with SIGIO first because the alarm handler might
* schedule, leaving the pending SIGIO stranded until we come
* back here.
+ *
+ * SIGIO's handler doesn't use siginfo or mcontext,
+ * so they can be NULL.
*/
if (save_pending & SIGIO_MASK)
- sig_handler_common(SIGIO, NULL);
+ sig_handler_common(SIGIO, NULL, NULL);
if (save_pending & SIGVTALRM_MASK)
real_alarm_handler(NULL);
diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c
index cd65727854eb..d93bb40499f7 100644
--- a/arch/um/os-Linux/skas/process.c
+++ b/arch/um/os-Linux/skas/process.c
@@ -346,6 +346,10 @@ void userspace(struct uml_pt_regs *regs)
int err, status, op, pid = userspace_pid[0];
/* To prevent races if using_sysemu changes under us.*/
int local_using_sysemu;
+ siginfo_t si;
+
+ /* Handle any immediate reschedules or signals */
+ interrupt_end();
if (getitimer(ITIMER_VIRTUAL, &timer))
printk(UM_KERN_ERR "Failed to get itimer, errno = %d\n", errno);
@@ -404,13 +408,17 @@ void userspace(struct uml_pt_regs *regs)
if (WIFSTOPPED(status)) {
int sig = WSTOPSIG(status);
+
+ ptrace(PTRACE_GETSIGINFO, pid, 0, &si);
+
switch (sig) {
case SIGSEGV:
if (PTRACE_FULL_FAULTINFO ||
!ptrace_faultinfo) {
get_skas_faultinfo(pid,
&regs->faultinfo);
- (*sig_info[SIGSEGV])(SIGSEGV, regs);
+ (*sig_info[SIGSEGV])(SIGSEGV, &si,
+ regs);
}
else handle_segv(pid, regs);
break;
@@ -418,14 +426,14 @@ void userspace(struct uml_pt_regs *regs)
handle_trap(pid, regs, local_using_sysemu);
break;
case SIGTRAP:
- relay_signal(SIGTRAP, regs);
+ relay_signal(SIGTRAP, &si, regs);
break;
case SIGVTALRM:
now = os_nsecs();
if (now < nsecs)
break;
block_signals();
- (*sig_info[sig])(sig, regs);
+ (*sig_info[sig])(sig, &si, regs);
unblock_signals();
nsecs = timer.it_value.tv_sec *
UM_NSEC_PER_SEC +
@@ -439,7 +447,7 @@ void userspace(struct uml_pt_regs *regs)
case SIGFPE:
case SIGWINCH:
block_signals();
- (*sig_info[sig])(sig, regs);
+ (*sig_info[sig])(sig, &si, regs);
unblock_signals();
break;
default:
diff --git a/arch/um/os-Linux/time.c b/arch/um/os-Linux/time.c
index 910499d76a67..0748fe0c8a73 100644
--- a/arch/um/os-Linux/time.c
+++ b/arch/um/os-Linux/time.c
@@ -87,7 +87,7 @@ static int after_sleep_interval(struct timespec *ts)
static void deliver_alarm(void)
{
- alarm_handler(SIGVTALRM, NULL);
+ alarm_handler(SIGVTALRM, NULL, NULL);
}
static unsigned long long sleep_time(unsigned long long nsecs)
@@ -114,7 +114,7 @@ static void deliver_alarm(void)
skew += this_tick - last_tick;
while (skew >= one_tick) {
- alarm_handler(SIGVTALRM, NULL);
+ alarm_handler(SIGVTALRM, NULL, NULL);
skew -= one_tick;
}
diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules
index d50270d26b42..15889df9b466 100644
--- a/arch/um/scripts/Makefile.rules
+++ b/arch/um/scripts/Makefile.rules
@@ -8,7 +8,7 @@ USER_OBJS += $(filter %_user.o,$(obj-y) $(obj-m) $(USER_SINGLE_OBJS))
USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
$(USER_OBJS:.o=.%): \
- c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include user.h $(CFLAGS_$(basetarget).o)
+ c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include $(srctree)/include/linux/kern_levels.h -include user.h $(CFLAGS_$(basetarget).o)
# These are like USER_OBJS but filter USER_CFLAGS through unprofile instead of
# using it directly.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index ba2657c49217..50a1d1f9b6d3 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -746,10 +746,10 @@ config SWIOTLB
def_bool y if X86_64
---help---
Support for software bounce buffers used on x86-64 systems
- which don't have a hardware IOMMU (e.g. the current generation
- of Intel's x86-64 CPUs). Using this PCI devices which can only
- access 32-bits of memory can be used on systems with more than
- 3 GB of memory. If unsure, say Y.
+ which don't have a hardware IOMMU. Using this PCI devices
+ which can only access 32-bits of memory can be used on systems
+ with more than 3 GB of memory.
+ If unsure, say Y.
config IOMMU_HELPER
def_bool (CALGARY_IOMMU || GART_IOMMU || SWIOTLB || AMD_IOMMU)
@@ -1527,7 +1527,7 @@ config SECCOMP
If unsure, say Y. Only embedded should say N here.
config CC_STACKPROTECTOR
- bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
+ bool "Enable -fstack-protector buffer overflow detection"
---help---
This option turns on the -fstack-protector GCC feature. This
feature puts, at the beginning of functions, a canary value on
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index b0c5276861ec..474ca35b1bce 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -27,6 +27,10 @@ ifeq ($(CONFIG_X86_32),y)
KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return
+ # Never want PIC in a 32-bit kernel, prevent breakage with GCC built
+ # with nonstandard options
+ KBUILD_CFLAGS += -fno-pic
+
# prevent gcc from keeping the stack 16 byte aligned
KBUILD_CFLAGS += $(call cc-option,-mpreferred-stack-boundary=2)
@@ -138,7 +142,7 @@ KBUILD_CFLAGS += $(call cc-option,-mno-avx,)
KBUILD_CFLAGS += $(mflags-y)
KBUILD_AFLAGS += $(mflags-y)
-archscripts:
+archscripts: scripts_basic
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
###
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 5a747dd884db..f7535bedc33f 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -57,7 +57,7 @@ KBUILD_CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \
-Wall -Wstrict-prototypes \
-march=i386 -mregparm=3 \
-include $(srctree)/$(src)/code16gcc.h \
- -fno-strict-aliasing -fomit-frame-pointer \
+ -fno-strict-aliasing -fomit-frame-pointer -fno-pic \
$(call cc-option, -ffreestanding) \
$(call cc-option, -fno-toplevel-reorder,\
$(call cc-option, -fno-unit-at-a-time)) \
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 441520e4174f..a3ac52b29cbf 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -33,6 +33,14 @@
#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */
#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */
#define MCI_STATUS_AR (1ULL<<55) /* Action required */
+#define MCACOD 0xffff /* MCA Error Code */
+
+/* Architecturally defined codes from SDM Vol. 3B Chapter 15 */
+#define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */
+#define MCACOD_SCRUBMSK 0xfff0
+#define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */
+#define MCACOD_DATA 0x0134 /* Data Load */
+#define MCACOD_INSTR 0x0150 /* Instruction Fetch */
/* MCi_MISC register defines */
#define MCI_MISC_ADDR_LSB(m) ((m) & 0x3f)
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h
index 87bdbca72f94..72f9adf6eca4 100644
--- a/arch/x86/include/asm/olpc.h
+++ b/arch/x86/include/asm/olpc.h
@@ -100,25 +100,6 @@ extern void olpc_xo1_pm_wakeup_clear(u16 value);
extern int pci_olpc_init(void);
-/* EC related functions */
-
-extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
- unsigned char *outbuf, size_t outlen);
-
-/* EC commands */
-
-#define EC_FIRMWARE_REV 0x08
-#define EC_WRITE_SCI_MASK 0x1b
-#define EC_WAKE_UP_WLAN 0x24
-#define EC_WLAN_LEAVE_RESET 0x25
-#define EC_READ_EB_MODE 0x2a
-#define EC_SET_SCI_INHIBIT 0x32
-#define EC_SET_SCI_INHIBIT_RELEASE 0x34
-#define EC_WLAN_ENTER_RESET 0x35
-#define EC_WRITE_EXT_SCI_MASK 0x38
-#define EC_SCI_QUERY 0x84
-#define EC_EXT_SCI_QUERY 0x85
-
/* SCI source values */
#define EC_SCI_SRC_EMPTY 0x00
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index dab39350e51e..cb4e43bce98a 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -196,11 +196,16 @@ static inline u32 get_ibs_caps(void) { return 0; }
extern void perf_events_lapic_init(void);
/*
- * Abuse bit 3 of the cpu eflags register to indicate proper PEBS IP fixups.
- * This flag is otherwise unused and ABI specified to be 0, so nobody should
- * care what we do with it.
+ * Abuse bits {3,5} of the cpu eflags register. These flags are otherwise
+ * unused and ABI specified to be 0, so nobody should care what we do with
+ * them.
+ *
+ * EXACT - the IP points to the exact instruction that triggered the
+ * event (HW bugs exempt).
+ * VM - original X86_VM_MASK; see set_linear_ip().
*/
#define PERF_EFLAGS_EXACT (1UL << 3)
+#define PERF_EFLAGS_VM (1UL << 5)
struct pt_regs;
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h
index b315a33867f2..33692eaabab5 100644
--- a/arch/x86/include/asm/spinlock.h
+++ b/arch/x86/include/asm/spinlock.h
@@ -12,8 +12,7 @@
* Simple spin lock operations. There are two variants, one clears IRQ's
* on the local processor, one does not.
*
- * These are fair FIFO ticket locks, which are currently limited to 256
- * CPUs.
+ * These are fair FIFO ticket locks, which support up to 2^16 CPUs.
*
* (the type definitions are in asm/spinlock_types.h)
*/
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
index 93971e841dd5..472b9b783019 100644
--- a/arch/x86/include/asm/xen/page.h
+++ b/arch/x86/include/asm/xen/page.h
@@ -51,7 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,
extern int m2p_add_override(unsigned long mfn, struct page *page,
struct gnttab_map_grant_ref *kmap_op);
-extern int m2p_remove_override(struct page *page, bool clear_pte);
+extern int m2p_remove_override(struct page *page,
+ struct gnttab_map_grant_ref *kmap_op);
extern struct page *m2p_find_override(unsigned long mfn);
extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index 95bf99de9058..1b8e5a03d942 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -25,10 +25,6 @@ unsigned long acpi_realmode_flags;
static char temp_stack[4096];
#endif
-asmlinkage void acpi_enter_s3(void)
-{
- acpi_enter_sleep_state(3, wake_sleep_flags);
-}
/**
* acpi_suspend_lowlevel - save kernel state
*
diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h
index 5653a5791ec9..67f59f8c6956 100644
--- a/arch/x86/kernel/acpi/sleep.h
+++ b/arch/x86/kernel/acpi/sleep.h
@@ -2,7 +2,6 @@
* Variables and functions used by the code in sleep.c
*/
-#include <linux/linkage.h>
#include <asm/realmode.h>
extern unsigned long saved_video_mode;
@@ -11,7 +10,6 @@ extern long saved_magic;
extern int wakeup_pmode_return;
extern u8 wake_sleep_flags;
-extern asmlinkage void acpi_enter_s3(void);
extern unsigned long acpi_copy_wakeup_routine(unsigned long);
extern void wakeup_long64(void);
diff --git a/arch/x86/kernel/acpi/wakeup_32.S b/arch/x86/kernel/acpi/wakeup_32.S
index 72610839f03b..13ab720573e3 100644
--- a/arch/x86/kernel/acpi/wakeup_32.S
+++ b/arch/x86/kernel/acpi/wakeup_32.S
@@ -74,7 +74,9 @@ restore_registers:
ENTRY(do_suspend_lowlevel)
call save_processor_state
call save_registers
- call acpi_enter_s3
+ pushl $3
+ call acpi_enter_sleep_state
+ addl $4, %esp
# In case of S3 failure, we'll emerge here. Jump
# to ret_point to recover
diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S
index 014d1d28c397..8ea5164cbd04 100644
--- a/arch/x86/kernel/acpi/wakeup_64.S
+++ b/arch/x86/kernel/acpi/wakeup_64.S
@@ -71,7 +71,9 @@ ENTRY(do_suspend_lowlevel)
movq %rsi, saved_rsi
addq $8, %rsp
- call acpi_enter_s3
+ movl $3, %edi
+ xorl %eax, %eax
+ call acpi_enter_sleep_state
/* in case something went wrong, restore the machine status and go on */
jmp resume_point
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 931280ff8299..ced4534baed5 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -165,7 +165,7 @@ static const unsigned char * const k7_nops[ASM_NOP_MAX+2] =
#endif
#ifdef P6_NOP1
-static const unsigned char __initconst_or_module p6nops[] =
+static const unsigned char p6nops[] =
{
P6_NOP1,
P6_NOP2,
@@ -224,7 +224,7 @@ void __init arch_init_ideal_nops(void)
ideal_nops = intel_nops;
#endif
}
-
+ break;
default:
#ifdef CONFIG_X86_64
ideal_nops = k8_nops;
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 406eee784684..c265593ec2cd 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1204,7 +1204,7 @@ static void __clear_irq_vector(int irq, struct irq_cfg *cfg)
BUG_ON(!cfg->vector);
vector = cfg->vector;
- for_each_cpu(cpu, cfg->domain)
+ for_each_cpu_and(cpu, cfg->domain, cpu_online_mask)
per_cpu(vector_irq, cpu)[vector] = -1;
cfg->vector = 0;
@@ -1212,7 +1212,7 @@ static void __clear_irq_vector(int irq, struct irq_cfg *cfg)
if (likely(!cfg->move_in_progress))
return;
- for_each_cpu(cpu, cfg->old_domain) {
+ for_each_cpu_and(cpu, cfg->old_domain, cpu_online_mask) {
for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS;
vector++) {
if (per_cpu(vector_irq, cpu)[vector] != irq)
@@ -1356,6 +1356,16 @@ static void setup_ioapic_irq(unsigned int irq, struct irq_cfg *cfg,
if (!IO_APIC_IRQ(irq))
return;
+ /*
+ * For legacy irqs, cfg->domain starts with cpu 0. Now that IO-APIC
+ * can handle this irq and the apic driver is finialized at this point,
+ * update the cfg->domain.
+ */
+ if (irq < legacy_pic->nr_legacy_irqs &&
+ cpumask_equal(cfg->domain, cpumask_of(0)))
+ apic->vector_allocation_domain(0, cfg->domain,
+ apic->target_cpus());
+
if (assign_irq_vector(irq, cfg, apic->target_cpus()))
return;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 46d8786d655e..a5fbc3c5fccc 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -144,6 +144,8 @@ static int __init x86_xsave_setup(char *s)
{
setup_clear_cpu_cap(X86_FEATURE_XSAVE);
setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+ setup_clear_cpu_cap(X86_FEATURE_AVX);
+ setup_clear_cpu_cap(X86_FEATURE_AVX2);
return 1;
}
__setup("noxsave", x86_xsave_setup);
diff --git a/arch/x86/kernel/cpu/mcheck/mce-severity.c b/arch/x86/kernel/cpu/mcheck/mce-severity.c
index 413c2ced887c..13017626f9a8 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-severity.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-severity.c
@@ -55,13 +55,6 @@ static struct severity {
#define MCI_UC_S (MCI_STATUS_UC|MCI_STATUS_S)
#define MCI_UC_SAR (MCI_STATUS_UC|MCI_STATUS_S|MCI_STATUS_AR)
#define MCI_ADDR (MCI_STATUS_ADDRV|MCI_STATUS_MISCV)
-#define MCACOD 0xffff
-/* Architecturally defined codes from SDM Vol. 3B Chapter 15 */
-#define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */
-#define MCACOD_SCRUBMSK 0xfff0
-#define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */
-#define MCACOD_DATA 0x0134 /* Data Load */
-#define MCACOD_INSTR 0x0150 /* Instruction Fetch */
MCESEV(
NO, "Invalid",
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 5e095f873e3e..292d0258311c 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -103,6 +103,8 @@ DEFINE_PER_CPU(mce_banks_t, mce_poll_banks) = {
static DEFINE_PER_CPU(struct work_struct, mce_work);
+static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs);
+
/*
* CPU/chipset specific EDAC code can register a notifier call here to print
* MCE errors in a human-readable form.
@@ -650,14 +652,18 @@ EXPORT_SYMBOL_GPL(machine_check_poll);
* Do a quick check if any of the events requires a panic.
* This decides if we keep the events around or clear them.
*/
-static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp)
+static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp,
+ struct pt_regs *regs)
{
int i, ret = 0;
for (i = 0; i < banks; i++) {
m->status = mce_rdmsrl(MSR_IA32_MCx_STATUS(i));
- if (m->status & MCI_STATUS_VAL)
+ if (m->status & MCI_STATUS_VAL) {
__set_bit(i, validp);
+ if (quirk_no_way_out)
+ quirk_no_way_out(i, m, regs);
+ }
if (mce_severity(m, tolerant, msg) >= MCE_PANIC_SEVERITY)
ret = 1;
}
@@ -1040,7 +1046,7 @@ void do_machine_check(struct pt_regs *regs, long error_code)
*final = m;
memset(valid_banks, 0, sizeof(valid_banks));
- no_way_out = mce_no_way_out(&m, &msg, valid_banks);
+ no_way_out = mce_no_way_out(&m, &msg, valid_banks, regs);
barrier();
@@ -1418,6 +1424,34 @@ static void __mcheck_cpu_init_generic(void)
}
}
+/*
+ * During IFU recovery Sandy Bridge -EP4S processors set the RIPV and
+ * EIPV bits in MCG_STATUS to zero on the affected logical processor (SDM
+ * Vol 3B Table 15-20). But this confuses both the code that determines
+ * whether the machine check occurred in kernel or user mode, and also
+ * the severity assessment code. Pretend that EIPV was set, and take the
+ * ip/cs values from the pt_regs that mce_gather_info() ignored earlier.
+ */
+static void quirk_sandybridge_ifu(int bank, struct mce *m, struct pt_regs *regs)
+{
+ if (bank != 0)
+ return;
+ if ((m->mcgstatus & (MCG_STATUS_EIPV|MCG_STATUS_RIPV)) != 0)
+ return;
+ if ((m->status & (MCI_STATUS_OVER|MCI_STATUS_UC|
+ MCI_STATUS_EN|MCI_STATUS_MISCV|MCI_STATUS_ADDRV|
+ MCI_STATUS_PCC|MCI_STATUS_S|MCI_STATUS_AR|
+ MCACOD)) !=
+ (MCI_STATUS_UC|MCI_STATUS_EN|
+ MCI_STATUS_MISCV|MCI_STATUS_ADDRV|MCI_STATUS_S|
+ MCI_STATUS_AR|MCACOD_INSTR))
+ return;
+
+ m->mcgstatus |= MCG_STATUS_EIPV;
+ m->ip = regs->ip;
+ m->cs = regs->cs;
+}
+
/* Add per CPU specific workarounds here */
static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
{
@@ -1515,6 +1549,9 @@ static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
*/
if (c->x86 == 6 && c->x86_model <= 13 && mce_bootlog < 0)
mce_bootlog = 0;
+
+ if (c->x86 == 6 && c->x86_model == 45)
+ quirk_no_way_out = quirk_sandybridge_ifu;
}
if (monarch_timeout < 0)
monarch_timeout = 0;
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 29557aa06dda..915b876edd1e 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -32,6 +32,8 @@
#include <asm/smp.h>
#include <asm/alternative.h>
#include <asm/timer.h>
+#include <asm/desc.h>
+#include <asm/ldt.h>
#include "perf_event.h"
@@ -1738,6 +1740,29 @@ valid_user_frame(const void __user *fp, unsigned long size)
return (__range_not_ok(fp, size, TASK_SIZE) == 0);
}
+static unsigned long get_segment_base(unsigned int segment)
+{
+ struct desc_struct *desc;
+ int idx = segment >> 3;
+
+ if ((segment & SEGMENT_TI_MASK) == SEGMENT_LDT) {
+ if (idx > LDT_ENTRIES)
+ return 0;
+
+ if (idx > current->active_mm->context.size)
+ return 0;
+
+ desc = current->active_mm->context.ldt;
+ } else {
+ if (idx > GDT_ENTRIES)
+ return 0;
+
+ desc = __this_cpu_ptr(&gdt_page.gdt[0]);
+ }
+
+ return get_desc_base(desc + idx);
+}
+
#ifdef CONFIG_COMPAT
#include <asm/compat.h>
@@ -1746,13 +1771,17 @@ static inline int
perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
{
/* 32-bit process in 64-bit kernel. */
+ unsigned long ss_base, cs_base;
struct stack_frame_ia32 frame;
const void __user *fp;
if (!test_thread_flag(TIF_IA32))
return 0;
- fp = compat_ptr(regs->bp);
+ cs_base = get_segment_base(regs->cs);
+ ss_base = get_segment_base(regs->ss);
+
+ fp = compat_ptr(ss_base + regs->bp);
while (entry->nr < PERF_MAX_STACK_DEPTH) {
unsigned long bytes;
frame.next_frame = 0;
@@ -1765,8 +1794,8 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
if (!valid_user_frame(fp, sizeof(frame)))
break;
- perf_callchain_store(entry, frame.return_address);
- fp = compat_ptr(frame.next_frame);
+ perf_callchain_store(entry, cs_base + frame.return_address);
+ fp = compat_ptr(ss_base + frame.next_frame);
}
return 1;
}
@@ -1789,6 +1818,12 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
return;
}
+ /*
+ * We don't know what to do with VM86 stacks.. ignore them for now.
+ */
+ if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
+ return;
+
fp = (void __user *)regs->bp;
perf_callchain_store(entry, regs->ip);
@@ -1816,16 +1851,50 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
}
}
-unsigned long perf_instruction_pointer(struct pt_regs *regs)
+/*
+ * Deal with code segment offsets for the various execution modes:
+ *
+ * VM86 - the good olde 16 bit days, where the linear address is
+ * 20 bits and we use regs->ip + 0x10 * regs->cs.
+ *
+ * IA32 - Where we need to look at GDT/LDT segment descriptor tables
+ * to figure out what the 32bit base address is.
+ *
+ * X32 - has TIF_X32 set, but is running in x86_64
+ *
+ * X86_64 - CS,DS,SS,ES are all zero based.
+ */
+static unsigned long code_segment_base(struct pt_regs *regs)
{
- unsigned long ip;
+ /*
+ * If we are in VM86 mode, add the segment offset to convert to a
+ * linear address.
+ */
+ if (regs->flags & X86_VM_MASK)
+ return 0x10 * regs->cs;
+
+ /*
+ * For IA32 we look at the GDT/LDT segment base to convert the
+ * effective IP to a linear address.
+ */
+#ifdef CONFIG_X86_32
+ if (user_mode(regs) && regs->cs != __USER_CS)
+ return get_segment_base(regs->cs);
+#else
+ if (test_thread_flag(TIF_IA32)) {
+ if (user_mode(regs) && regs->cs != __USER32_CS)
+ return get_segment_base(regs->cs);
+ }
+#endif
+ return 0;
+}
+unsigned long perf_instruction_pointer(struct pt_regs *regs)
+{
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
- ip = perf_guest_cbs->get_guest_ip();
- else
- ip = instruction_pointer(regs);
+ return perf_guest_cbs->get_guest_ip();
- return ip;
+ return regs->ip + code_segment_base(regs);
}
unsigned long perf_misc_flags(struct pt_regs *regs)
@@ -1838,7 +1907,7 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
else
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else {
- if (!kernel_ip(regs->ip))
+ if (user_mode(regs))
misc |= PERF_RECORD_MISC_USER;
else
misc |= PERF_RECORD_MISC_KERNEL;
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 821d53b696d1..8b6defe7eefc 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -516,6 +516,26 @@ static inline bool kernel_ip(unsigned long ip)
#endif
}
+/*
+ * Not all PMUs provide the right context information to place the reported IP
+ * into full context. Specifically segment registers are typically not
+ * supplied.
+ *
+ * Assuming the address is a linear address (it is for IBS), we fake the CS and
+ * vm86 mode using the known zero-based code segment and 'fix up' the registers
+ * to reflect this.
+ *
+ * Intel PEBS/LBR appear to typically provide the effective address, nothing
+ * much we can do about that but pray and treat it like a linear address.
+ */
+static inline void set_linear_ip(struct pt_regs *regs, unsigned long ip)
+{
+ regs->cs = kernel_ip(ip) ? __KERNEL_CS : __USER_CS;
+ if (regs->flags & X86_VM_MASK)
+ regs->flags ^= (PERF_EFLAGS_VM | X86_VM_MASK);
+ regs->ip = ip;
+}
+
#ifdef CONFIG_CPU_SUP_AMD
int amd_pmu_init(void);
@@ -566,6 +586,8 @@ extern struct event_constraint intel_westmere_pebs_event_constraints[];
extern struct event_constraint intel_snb_pebs_event_constraints[];
+extern struct event_constraint intel_ivb_pebs_event_constraints[];
+
struct event_constraint *intel_pebs_constraints(struct perf_event *event);
void intel_pmu_pebs_enable(struct perf_event *event);
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index da9bcdcd9856..eebd5ffe1bba 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -13,6 +13,8 @@
#include <asm/apic.h>
+#include "perf_event.h"
+
static u32 ibs_caps;
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
@@ -207,6 +209,15 @@ static int perf_ibs_precise_event(struct perf_event *event, u64 *config)
return -EOPNOTSUPP;
}
+static const struct perf_event_attr ibs_notsupp = {
+ .exclude_user = 1,
+ .exclude_kernel = 1,
+ .exclude_hv = 1,
+ .exclude_idle = 1,
+ .exclude_host = 1,
+ .exclude_guest = 1,
+};
+
static int perf_ibs_init(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -227,6 +238,9 @@ static int perf_ibs_init(struct perf_event *event)
if (event->pmu != &perf_ibs->pmu)
return -ENOENT;
+ if (perf_flags(&event->attr) & perf_flags(&ibs_notsupp))
+ return -EINVAL;
+
if (config & ~perf_ibs->config_mask)
return -EINVAL;
@@ -536,7 +550,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
if (check_rip && (ibs_data.regs[2] & IBS_RIP_INVALID)) {
regs.flags &= ~PERF_EFLAGS_EXACT;
} else {
- instruction_pointer_set(&regs, ibs_data.regs[1]);
+ set_linear_ip(&regs, ibs_data.regs[1]);
regs.flags |= PERF_EFLAGS_EXACT;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 382366977d4c..6bca492b8547 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1522,8 +1522,16 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr)
arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL;
arr[0].host = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask;
arr[0].guest = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_host_mask;
+ /*
+ * If PMU counter has PEBS enabled it is not enough to disable counter
+ * on a guest entry since PEBS memory write can overshoot guest entry
+ * and corrupt guest memory. Disabling PEBS solves the problem.
+ */
+ arr[1].msr = MSR_IA32_PEBS_ENABLE;
+ arr[1].host = cpuc->pebs_enabled;
+ arr[1].guest = 0;
- *nr = 1;
+ *nr = 2;
return arr;
}
@@ -2000,6 +2008,7 @@ __init int intel_pmu_init(void)
break;
case 28: /* Atom */
+ case 54: /* Cedariew */
memcpy(hw_cache_event_ids, atom_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
@@ -2039,7 +2048,6 @@ __init int intel_pmu_init(void)
case 42: /* SandyBridge */
case 45: /* SandyBridge, "Romely-EP" */
x86_add_quirk(intel_sandybridge_quirk);
- case 58: /* IvyBridge */
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
@@ -2064,6 +2072,29 @@ __init int intel_pmu_init(void)
pr_cont("SandyBridge events, ");
break;
+ case 58: /* IvyBridge */
+ memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
+ sizeof(hw_cache_event_ids));
+ memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
+ sizeof(hw_cache_extra_regs));
+
+ intel_pmu_lbr_init_snb();
+
+ x86_pmu.event_constraints = intel_snb_event_constraints;
+ x86_pmu.pebs_constraints = intel_ivb_pebs_event_constraints;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.extra_regs = intel_snb_extra_regs;
+ /* all extra regs are per-cpu when HT is on */
+ x86_pmu.er_flags |= ERF_HAS_RSP_1;
+ x86_pmu.er_flags |= ERF_NO_HT_SHARING;
+
+ /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */
+ intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =
+ X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1);
+
+ pr_cont("IvyBridge events, ");
+ break;
+
default:
switch (x86_pmu.version) {
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index 629ae0b7ad90..826054a4f2ee 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -407,6 +407,20 @@ struct event_constraint intel_snb_pebs_event_constraints[] = {
EVENT_CONSTRAINT_END
};
+struct event_constraint intel_ivb_pebs_event_constraints[] = {
+ INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
+ INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
+ INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
+ INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
+ EVENT_CONSTRAINT_END
+};
+
struct event_constraint *intel_pebs_constraints(struct perf_event *event)
{
struct event_constraint *c;
@@ -499,7 +513,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
* We sampled a branch insn, rewind using the LBR stack
*/
if (ip == to) {
- regs->ip = from;
+ set_linear_ip(regs, from);
return 1;
}
@@ -529,7 +543,7 @@ static int intel_pmu_pebs_fixup_ip(struct pt_regs *regs)
} while (to < ip);
if (to == ip) {
- regs->ip = old_to;
+ set_linear_ip(regs, old_to);
return 1;
}
@@ -569,7 +583,8 @@ static void __intel_pmu_pebs_event(struct perf_event *event,
* A possible PERF_SAMPLE_REGS will have to transfer all regs.
*/
regs = *iregs;
- regs.ip = pebs->ip;
+ regs.flags = pebs->flags;
+ set_linear_ip(&regs, pebs->ip);
regs.bp = pebs->bp;
regs.sp = pebs->sp;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index 520b4265fcd2..da02e9cc3754 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -686,7 +686,8 @@ void intel_pmu_lbr_init_atom(void)
* to have an operational LBR which can freeze
* on PMU interrupt
*/
- if (boot_cpu_data.x86_mask < 10) {
+ if (boot_cpu_data.x86_model == 28
+ && boot_cpu_data.x86_mask < 10) {
pr_cont("LBR disabled due to erratum");
return;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index 7563fda9f033..38e4894165b9 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -661,6 +661,11 @@ static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
}
}
+static struct uncore_event_desc snb_uncore_events[] = {
+ INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
+ { /* end: all zeroes */ },
+};
+
static struct attribute *snb_uncore_formats_attr[] = {
&format_attr_event.attr,
&format_attr_umask.attr,
@@ -704,6 +709,7 @@ static struct intel_uncore_type snb_uncore_cbox = {
.constraints = snb_uncore_cbox_constraints,
.ops = &snb_uncore_msr_ops,
.format_group = &snb_uncore_format_group,
+ .event_descs = snb_uncore_events,
};
static struct intel_uncore_type *snb_msr_uncores[] = {
@@ -796,7 +802,6 @@ static struct intel_uncore_type *nhm_msr_uncores[] = {
DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5");
DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7");
-DEFINE_UNCORE_FORMAT_ATTR(mm_cfg, mm_cfg, "config:63");
DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63");
DEFINE_UNCORE_FORMAT_ATTR(mask, mask, "config2:0-63");
@@ -902,16 +907,21 @@ static struct attribute_group nhmex_uncore_cbox_format_group = {
.attrs = nhmex_uncore_cbox_formats_attr,
};
+/* msr offset for each instance of cbox */
+static unsigned nhmex_cbox_msr_offsets[] = {
+ 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x240, 0x2c0,
+};
+
static struct intel_uncore_type nhmex_uncore_cbox = {
.name = "cbox",
.num_counters = 6,
- .num_boxes = 8,
+ .num_boxes = 10,
.perf_ctr_bits = 48,
.event_ctl = NHMEX_C0_MSR_PMON_EV_SEL0,
.perf_ctr = NHMEX_C0_MSR_PMON_CTR0,
.event_mask = NHMEX_PMON_RAW_EVENT_MASK,
.box_ctl = NHMEX_C0_MSR_PMON_GLOBAL_CTL,
- .msr_offset = NHMEX_C_MSR_OFFSET,
+ .msr_offsets = nhmex_cbox_msr_offsets,
.pair_ctr_ctl = 1,
.ops = &nhmex_uncore_ops,
.format_group = &nhmex_uncore_cbox_format_group
@@ -1032,24 +1042,22 @@ static struct intel_uncore_type nhmex_uncore_bbox = {
static int nhmex_sbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
{
- struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
- struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+ struct hw_perf_event *hwc = &event->hw;
+ struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+ struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- if (event->attr.config & NHMEX_S_PMON_MM_CFG_EN) {
- reg1->config = event->attr.config1;
- reg2->config = event->attr.config2;
- } else {
- reg1->config = ~0ULL;
- reg2->config = ~0ULL;
- }
+ /* only TO_R_PROG_EV event uses the match/mask register */
+ if ((hwc->config & NHMEX_PMON_CTL_EV_SEL_MASK) !=
+ NHMEX_S_EVENT_TO_R_PROG_EV)
+ return 0;
if (box->pmu->pmu_idx == 0)
reg1->reg = NHMEX_S0_MSR_MM_CFG;
else
reg1->reg = NHMEX_S1_MSR_MM_CFG;
-
reg1->idx = 0;
-
+ reg1->config = event->attr.config1;
+ reg2->config = event->attr.config2;
return 0;
}
@@ -1059,8 +1067,8 @@ static void nhmex_sbox_msr_enable_event(struct intel_uncore_box *box, struct per
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- wrmsrl(reg1->reg, 0);
- if (reg1->config != ~0ULL || reg2->config != ~0ULL) {
+ if (reg1->idx != EXTRA_REG_NONE) {
+ wrmsrl(reg1->reg, 0);
wrmsrl(reg1->reg + 1, reg1->config);
wrmsrl(reg1->reg + 2, reg2->config);
wrmsrl(reg1->reg, NHMEX_S_PMON_MM_CFG_EN);
@@ -1074,7 +1082,6 @@ static struct attribute *nhmex_uncore_sbox_formats_attr[] = {
&format_attr_edge.attr,
&format_attr_inv.attr,
&format_attr_thresh8.attr,
- &format_attr_mm_cfg.attr,
&format_attr_match.attr,
&format_attr_mask.attr,
NULL,
@@ -1142,6 +1149,9 @@ static struct extra_reg nhmex_uncore_mbox_extra_regs[] = {
EVENT_EXTRA_END
};
+/* Nehalem-EX or Westmere-EX ? */
+bool uncore_nhmex;
+
static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config)
{
struct intel_uncore_extra_reg *er;
@@ -1171,18 +1181,29 @@ static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64
return false;
/* mask of the shared fields */
- mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK;
+ if (uncore_nhmex)
+ mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK;
+ else
+ mask = WSMEX_M_PMON_ZDP_CTL_FVC_MASK;
er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC];
raw_spin_lock_irqsave(&er->lock, flags);
/* add mask of the non-shared field if it's in use */
- if (__BITS_VALUE(atomic_read(&er->ref), idx, 8))
- mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (__BITS_VALUE(atomic_read(&er->ref), idx, 8)) {
+ if (uncore_nhmex)
+ mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ mask |= WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ }
if (!atomic_read(&er->ref) || !((er->config ^ config) & mask)) {
atomic_add(1 << (idx * 8), &er->ref);
- mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK |
- NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (uncore_nhmex)
+ mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK |
+ NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ mask = WSMEX_M_PMON_ZDP_CTL_FVC_MASK |
+ WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
er->config &= ~mask;
er->config |= (config & mask);
ret = true;
@@ -1216,7 +1237,10 @@ u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify)
/* get the non-shared control bits and shift them */
idx = orig_idx - EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
- config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (uncore_nhmex)
+ config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ config &= WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
if (new_idx > orig_idx) {
idx = new_idx - orig_idx;
config <<= 3 * idx;
@@ -1226,6 +1250,10 @@ u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify)
}
/* add the shared control bits back */
+ if (uncore_nhmex)
+ config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
+ else
+ config |= WSMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
if (modify) {
/* adjust the main event selector */
@@ -1264,7 +1292,8 @@ again:
}
/* for the match/mask registers */
- if ((uncore_box_is_fake(box) || !reg2->alloc) &&
+ if (reg2->idx != EXTRA_REG_NONE &&
+ (uncore_box_is_fake(box) || !reg2->alloc) &&
!nhmex_mbox_get_shared_reg(box, reg2->idx, reg2->config))
goto fail;
@@ -1278,7 +1307,8 @@ again:
if (idx[0] != 0xff && idx[0] != __BITS_VALUE(reg1->idx, 0, 8))
nhmex_mbox_alter_er(event, idx[0], true);
reg1->alloc |= alloc;
- reg2->alloc = 1;
+ if (reg2->idx != EXTRA_REG_NONE)
+ reg2->alloc = 1;
}
return NULL;
fail:
@@ -1342,9 +1372,6 @@ static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event
struct extra_reg *er;
unsigned msr;
int reg_idx = 0;
-
- if (WARN_ON_ONCE(reg1->idx != -1))
- return -EINVAL;
/*
* The mbox events may require 2 extra MSRs at the most. But only
* the lower 32 bits in these MSRs are significant, so we can use
@@ -1355,11 +1382,6 @@ static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event
continue;
if (event->attr.config1 & ~er->valid_mask)
return -EINVAL;
- if (er->idx == __BITS_VALUE(reg1->idx, 0, 8) ||
- er->idx == __BITS_VALUE(reg1->idx, 1, 8))
- continue;
- if (WARN_ON_ONCE(reg_idx >= 2))
- return -EINVAL;
msr = er->msr + type->msr_offset * box->pmu->pmu_idx;
if (WARN_ON_ONCE(msr >= 0xffff || er->idx >= 0xff))
@@ -1368,6 +1390,8 @@ static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event
/* always use the 32~63 bits to pass the PLD config */
if (er->idx == EXTRA_REG_NHMEX_M_PLD)
reg_idx = 1;
+ else if (WARN_ON_ONCE(reg_idx > 0))
+ return -EINVAL;
reg1->idx &= ~(0xff << (reg_idx * 8));
reg1->reg &= ~(0xffff << (reg_idx * 16));
@@ -1376,17 +1400,21 @@ static int nhmex_mbox_hw_config(struct intel_uncore_box *box, struct perf_event
reg1->config = event->attr.config1;
reg_idx++;
}
- /* use config2 to pass the filter config */
- reg2->idx = EXTRA_REG_NHMEX_M_FILTER;
- if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN)
- reg2->config = event->attr.config2;
- else
- reg2->config = ~0ULL;
- if (box->pmu->pmu_idx == 0)
- reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG;
- else
- reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG;
-
+ /*
+ * The mbox only provides ability to perform address matching
+ * for the PLD events.
+ */
+ if (reg_idx == 2) {
+ reg2->idx = EXTRA_REG_NHMEX_M_FILTER;
+ if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN)
+ reg2->config = event->attr.config2;
+ else
+ reg2->config = ~0ULL;
+ if (box->pmu->pmu_idx == 0)
+ reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG;
+ else
+ reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG;
+ }
return 0;
}
@@ -1422,34 +1450,36 @@ static void nhmex_mbox_msr_enable_event(struct intel_uncore_box *box, struct per
wrmsrl(__BITS_VALUE(reg1->reg, 1, 16),
nhmex_mbox_shared_reg_config(box, idx));
- wrmsrl(reg2->reg, 0);
- if (reg2->config != ~0ULL) {
- wrmsrl(reg2->reg + 1,
- reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
- wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
- (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
- wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
+ if (reg2->idx != EXTRA_REG_NONE) {
+ wrmsrl(reg2->reg, 0);
+ if (reg2->config != ~0ULL) {
+ wrmsrl(reg2->reg + 1,
+ reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
+ wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
+ (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
+ wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
+ }
}
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
}
-DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
-DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode, "config:4-5");
-DEFINE_UNCORE_FORMAT_ATTR(wrap_mode, wrap_mode, "config:6");
-DEFINE_UNCORE_FORMAT_ATTR(flag_mode, flag_mode, "config:7");
-DEFINE_UNCORE_FORMAT_ATTR(inc_sel, inc_sel, "config:9-13");
-DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel, set_flag_sel, "config:19-21");
-DEFINE_UNCORE_FORMAT_ATTR(filter_cfg, filter_cfg, "config2:63");
-DEFINE_UNCORE_FORMAT_ATTR(filter_match, filter_match, "config2:0-33");
-DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask, "config2:34-61");
-DEFINE_UNCORE_FORMAT_ATTR(dsp, dsp, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(thr, thr, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(fvc, fvc, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(pgt, pgt, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(map, map, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(iss, iss, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(pld, pld, "config1:32-63");
+DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
+DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode, "config:4-5");
+DEFINE_UNCORE_FORMAT_ATTR(wrap_mode, wrap_mode, "config:6");
+DEFINE_UNCORE_FORMAT_ATTR(flag_mode, flag_mode, "config:7");
+DEFINE_UNCORE_FORMAT_ATTR(inc_sel, inc_sel, "config:9-13");
+DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel, set_flag_sel, "config:19-21");
+DEFINE_UNCORE_FORMAT_ATTR(filter_cfg_en, filter_cfg_en, "config2:63");
+DEFINE_UNCORE_FORMAT_ATTR(filter_match, filter_match, "config2:0-33");
+DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask, "config2:34-61");
+DEFINE_UNCORE_FORMAT_ATTR(dsp, dsp, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(thr, thr, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(fvc, fvc, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pgt, pgt, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(map, map, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(iss, iss, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pld, pld, "config1:32-63");
static struct attribute *nhmex_uncore_mbox_formats_attr[] = {
&format_attr_count_mode.attr,
@@ -1458,7 +1488,7 @@ static struct attribute *nhmex_uncore_mbox_formats_attr[] = {
&format_attr_flag_mode.attr,
&format_attr_inc_sel.attr,
&format_attr_set_flag_sel.attr,
- &format_attr_filter_cfg.attr,
+ &format_attr_filter_cfg_en.attr,
&format_attr_filter_match.attr,
&format_attr_filter_mask.attr,
&format_attr_dsp.attr,
@@ -1482,6 +1512,12 @@ static struct uncore_event_desc nhmex_uncore_mbox_events[] = {
{ /* end: all zeroes */ },
};
+static struct uncore_event_desc wsmex_uncore_mbox_events[] = {
+ INTEL_UNCORE_EVENT_DESC(bbox_cmds_read, "inc_sel=0xd,fvc=0x5000"),
+ INTEL_UNCORE_EVENT_DESC(bbox_cmds_write, "inc_sel=0xd,fvc=0x5040"),
+ { /* end: all zeroes */ },
+};
+
static struct intel_uncore_ops nhmex_uncore_mbox_ops = {
NHMEX_UNCORE_OPS_COMMON_INIT(),
.enable_event = nhmex_mbox_msr_enable_event,
@@ -1513,7 +1549,7 @@ void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event)
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
int port;
- /* adjust the main event selector */
+ /* adjust the main event selector and extra register index */
if (reg1->idx % 2) {
reg1->idx--;
hwc->config -= 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
@@ -1522,29 +1558,17 @@ void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event)
hwc->config += 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
}
- /* adjust address or config of extra register */
+ /* adjust extra register config */
port = reg1->idx / 6 + box->pmu->pmu_idx * 4;
switch (reg1->idx % 6) {
- case 0:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
- break;
- case 1:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
- break;
case 2:
- /* the 8~15 bits to the 0~7 bits */
+ /* shift the 8~15 bits to the 0~7 bits */
reg1->config >>= 8;
break;
case 3:
- /* the 0~7 bits to the 8~15 bits */
+ /* shift the 0~7 bits to the 8~15 bits */
reg1->config <<= 8;
break;
- case 4:
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
- break;
- case 5:
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
- break;
};
}
@@ -1671,7 +1695,7 @@ static int nhmex_rbox_hw_config(struct intel_uncore_box *box, struct perf_event
struct hw_perf_event *hwc = &event->hw;
struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
- int port, idx;
+ int idx;
idx = (event->hw.config & NHMEX_R_PMON_CTL_EV_SEL_MASK) >>
NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
@@ -1681,27 +1705,11 @@ static int nhmex_rbox_hw_config(struct intel_uncore_box *box, struct perf_event
reg1->idx = idx;
reg1->config = event->attr.config1;
- port = idx / 6 + box->pmu->pmu_idx * 4;
- idx %= 6;
- switch (idx) {
- case 0:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
- break;
- case 1:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
- break;
- case 2:
- case 3:
- reg1->reg = NHMEX_R_MSR_PORTN_QLX_CFG(port);
- break;
+ switch (idx % 6) {
case 4:
case 5:
- if (idx == 4)
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
- else
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
- reg2->config = event->attr.config2;
hwc->config |= event->attr.config & (~0ULL << 32);
+ reg2->config = event->attr.config2;
break;
};
return 0;
@@ -1727,28 +1735,34 @@ static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct per
struct hw_perf_event *hwc = &event->hw;
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- int idx, er_idx;
+ int idx, port;
- idx = reg1->idx % 6;
- er_idx = idx;
- if (er_idx > 2)
- er_idx--;
- er_idx += (reg1->idx / 6) * 5;
+ idx = reg1->idx;
+ port = idx / 6 + box->pmu->pmu_idx * 4;
- switch (idx) {
+ switch (idx % 6) {
case 0:
+ wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG0(port), reg1->config);
+ break;
case 1:
- wrmsrl(reg1->reg, reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG1(port), reg1->config);
break;
case 2:
case 3:
- wrmsrl(reg1->reg, nhmex_rbox_shared_reg_config(box, er_idx));
+ wrmsrl(NHMEX_R_MSR_PORTN_QLX_CFG(port),
+ nhmex_rbox_shared_reg_config(box, 2 + (idx / 6) * 5));
break;
case 4:
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port),
+ hwc->config >> 32);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(port), reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MASK(port), reg2->config);
+ break;
case 5:
- wrmsrl(reg1->reg, reg1->config);
- wrmsrl(reg1->reg + 1, hwc->config >> 32);
- wrmsrl(reg1->reg + 2, reg2->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port),
+ hwc->config >> 32);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(port), reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MASK(port), reg2->config);
break;
};
@@ -1756,8 +1770,8 @@ static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct per
(hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK));
}
-DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config:32-63");
-DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config1:0-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config:32-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config1:0-63");
DEFINE_UNCORE_FORMAT_ATTR(xbr_mask, xbr_mask, "config2:0-63");
DEFINE_UNCORE_FORMAT_ATTR(qlx_cfg, qlx_cfg, "config1:0-15");
DEFINE_UNCORE_FORMAT_ATTR(iperf_cfg, iperf_cfg, "config1:0-31");
@@ -2303,6 +2317,7 @@ int uncore_pmu_event_init(struct perf_event *event)
event->hw.idx = -1;
event->hw.last_tag = ~0ULL;
event->hw.extra_reg.idx = EXTRA_REG_NONE;
+ event->hw.branch_reg.idx = EXTRA_REG_NONE;
if (event->attr.config == UNCORE_FIXED_EVENT) {
/* no fixed counter */
@@ -2373,7 +2388,7 @@ static void __init uncore_type_exit(struct intel_uncore_type *type)
type->attr_groups[1] = NULL;
}
-static void uncore_types_exit(struct intel_uncore_type **types)
+static void __init uncore_types_exit(struct intel_uncore_type **types)
{
int i;
for (i = 0; types[i]; i++)
@@ -2814,7 +2829,13 @@ static int __init uncore_cpu_init(void)
snbep_uncore_cbox.num_boxes = max_cores;
msr_uncores = snbep_msr_uncores;
break;
- case 46:
+ case 46: /* Nehalem-EX */
+ uncore_nhmex = true;
+ case 47: /* Westmere-EX aka. Xeon E7 */
+ if (!uncore_nhmex)
+ nhmex_uncore_mbox.event_descs = wsmex_uncore_mbox_events;
+ if (nhmex_uncore_cbox.num_boxes > max_cores)
+ nhmex_uncore_cbox.num_boxes = max_cores;
msr_uncores = nhmex_msr_uncores;
break;
default:
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
index f3851892e077..5b81c1856aac 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
@@ -5,7 +5,7 @@
#include "perf_event.h"
#define UNCORE_PMU_NAME_LEN 32
-#define UNCORE_PMU_HRTIMER_INTERVAL (60 * NSEC_PER_SEC)
+#define UNCORE_PMU_HRTIMER_INTERVAL (60LL * NSEC_PER_SEC)
#define UNCORE_FIXED_EVENT 0xff
#define UNCORE_PMC_IDX_MAX_GENERIC 8
@@ -230,6 +230,7 @@
#define NHMEX_S1_MSR_MASK 0xe5a
#define NHMEX_S_PMON_MM_CFG_EN (0x1ULL << 63)
+#define NHMEX_S_EVENT_TO_R_PROG_EV 0
/* NHM-EX Mbox */
#define NHMEX_M0_MSR_GLOBAL_CTL 0xca0
@@ -275,18 +276,12 @@
NHMEX_M_PMON_CTL_INC_SEL_MASK | \
NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK)
-
-#define NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK 0x1f
-#define NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK (0x7 << 5)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK (0x7 << 8)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR (1 << 23)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK \
- (NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 11) - 1) | (1 << 23))
#define NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (11 + 3 * (n)))
+#define WSMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 12) - 1) | (1 << 24))
+#define WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (12 + 3 * (n)))
+
/*
* use the 9~13 bits to select event If the 7th bit is not set,
* otherwise use the 19~21 bits to select event.
@@ -368,6 +363,7 @@ struct intel_uncore_type {
unsigned num_shared_regs:8;
unsigned single_fixed:1;
unsigned pair_ctr_ctl:1;
+ unsigned *msr_offsets;
struct event_constraint unconstrainted;
struct event_constraint *constraints;
struct intel_uncore_pmu *pmus;
@@ -485,29 +481,31 @@ unsigned uncore_pci_perf_ctr(struct intel_uncore_box *box, int idx)
return idx * 8 + box->pmu->type->perf_ctr;
}
-static inline
-unsigned uncore_msr_box_ctl(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_box_offset(struct intel_uncore_box *box)
+{
+ struct intel_uncore_pmu *pmu = box->pmu;
+ return pmu->type->msr_offsets ?
+ pmu->type->msr_offsets[pmu->pmu_idx] :
+ pmu->type->msr_offset * pmu->pmu_idx;
+}
+
+static inline unsigned uncore_msr_box_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->box_ctl)
return 0;
- return box->pmu->type->box_ctl +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->box_ctl + uncore_msr_box_offset(box);
}
-static inline
-unsigned uncore_msr_fixed_ctl(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_fixed_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->fixed_ctl)
return 0;
- return box->pmu->type->fixed_ctl +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->fixed_ctl + uncore_msr_box_offset(box);
}
-static inline
-unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
{
- return box->pmu->type->fixed_ctr +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->fixed_ctr + uncore_msr_box_offset(box);
}
static inline
@@ -515,7 +513,7 @@ unsigned uncore_msr_event_ctl(struct intel_uncore_box *box, int idx)
{
return box->pmu->type->event_ctl +
(box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ uncore_msr_box_offset(box);
}
static inline
@@ -523,7 +521,7 @@ unsigned uncore_msr_perf_ctr(struct intel_uncore_box *box, int idx)
{
return box->pmu->type->perf_ctr +
(box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ uncore_msr_box_offset(box);
}
static inline
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 1f5f1d5d2a02..d44f7829968e 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -270,7 +270,7 @@ void fixup_irqs(void)
if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
break_affinity = 1;
- affinity = cpu_all_mask;
+ affinity = cpu_online_mask;
}
chip = irq_data_get_irq_chip(data);
@@ -328,6 +328,7 @@ void fixup_irqs(void)
chip->irq_retrigger(data);
raw_spin_unlock(&desc->lock);
}
+ __this_cpu_write(vector_irq[vector], -1);
}
}
#endif
diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c
index 1d5d31ea686b..dc1404bf8e4b 100644
--- a/arch/x86/kernel/kdebugfs.c
+++ b/arch/x86/kernel/kdebugfs.c
@@ -107,7 +107,7 @@ static int __init create_setup_data_nodes(struct dentry *parent)
{
struct setup_data_node *node;
struct setup_data *data;
- int error = -ENOMEM;
+ int error;
struct dentry *d;
struct page *pg;
u64 pa_data;
@@ -121,8 +121,10 @@ static int __init create_setup_data_nodes(struct dentry *parent)
while (pa_data) {
node = kmalloc(sizeof(*node), GFP_KERNEL);
- if (!node)
+ if (!node) {
+ error = -ENOMEM;
goto err_dir;
+ }
pg = pfn_to_page((pa_data+sizeof(*data)-1) >> PAGE_SHIFT);
if (PageHighMem(pg)) {
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 8a2ce8fd41c0..82746f942cd8 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -143,11 +143,12 @@ static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
unsigned int *current_size)
{
struct microcode_header_amd *mc_hdr;
- unsigned int actual_size;
+ unsigned int actual_size, patch_size;
u16 equiv_cpu_id;
/* size of the current patch we're staring at */
- *current_size = *(u32 *)(ucode_ptr + 4) + SECTION_HDR_SIZE;
+ patch_size = *(u32 *)(ucode_ptr + 4);
+ *current_size = patch_size + SECTION_HDR_SIZE;
equiv_cpu_id = find_equiv_id();
if (!equiv_cpu_id)
@@ -174,7 +175,7 @@ static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
/*
* now that the header looks sane, verify its size
*/
- actual_size = verify_ucode_size(cpu, *current_size, leftover_size);
+ actual_size = verify_ucode_size(cpu, patch_size, leftover_size);
if (!actual_size)
return 0;
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index 4873e62db6a1..9e5bcf1e2376 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -225,6 +225,9 @@ static ssize_t microcode_write(struct file *file, const char __user *buf,
if (do_microcode_update(buf, len) == 0)
ret = (ssize_t)len;
+ if (ret > 0)
+ perf_check_microcode();
+
mutex_unlock(&microcode_mutex);
put_online_cpus();
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 97d9a9914ba8..a3b57a27be88 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -475,13 +475,26 @@ register_address(struct x86_emulate_ctxt *ctxt, unsigned long reg)
return address_mask(ctxt, reg);
}
+static void masked_increment(ulong *reg, ulong mask, int inc)
+{
+ assign_masked(reg, *reg + inc, mask);
+}
+
static inline void
register_address_increment(struct x86_emulate_ctxt *ctxt, unsigned long *reg, int inc)
{
+ ulong mask;
+
if (ctxt->ad_bytes == sizeof(unsigned long))
- *reg += inc;
+ mask = ~0UL;
else
- *reg = (*reg & ~ad_mask(ctxt)) | ((*reg + inc) & ad_mask(ctxt));
+ mask = ad_mask(ctxt);
+ masked_increment(reg, mask, inc);
+}
+
+static void rsp_increment(struct x86_emulate_ctxt *ctxt, int inc)
+{
+ masked_increment(&ctxt->regs[VCPU_REGS_RSP], stack_mask(ctxt), inc);
}
static inline void jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
@@ -1522,8 +1535,8 @@ static int push(struct x86_emulate_ctxt *ctxt, void *data, int bytes)
{
struct segmented_address addr;
- register_address_increment(ctxt, &ctxt->regs[VCPU_REGS_RSP], -bytes);
- addr.ea = register_address(ctxt, ctxt->regs[VCPU_REGS_RSP]);
+ rsp_increment(ctxt, -bytes);
+ addr.ea = ctxt->regs[VCPU_REGS_RSP] & stack_mask(ctxt);
addr.seg = VCPU_SREG_SS;
return segmented_write(ctxt, addr, data, bytes);
@@ -1542,13 +1555,13 @@ static int emulate_pop(struct x86_emulate_ctxt *ctxt,
int rc;
struct segmented_address addr;
- addr.ea = register_address(ctxt, ctxt->regs[VCPU_REGS_RSP]);
+ addr.ea = ctxt->regs[VCPU_REGS_RSP] & stack_mask(ctxt);
addr.seg = VCPU_SREG_SS;
rc = segmented_read(ctxt, addr, dest, len);
if (rc != X86EMUL_CONTINUE)
return rc;
- register_address_increment(ctxt, &ctxt->regs[VCPU_REGS_RSP], len);
+ rsp_increment(ctxt, len);
return rc;
}
@@ -1688,8 +1701,7 @@ static int em_popa(struct x86_emulate_ctxt *ctxt)
while (reg >= VCPU_REGS_RAX) {
if (reg == VCPU_REGS_RSP) {
- register_address_increment(ctxt, &ctxt->regs[VCPU_REGS_RSP],
- ctxt->op_bytes);
+ rsp_increment(ctxt, ctxt->op_bytes);
--reg;
}
@@ -2825,7 +2837,7 @@ static int em_ret_near_imm(struct x86_emulate_ctxt *ctxt)
rc = emulate_pop(ctxt, &ctxt->dst.val, ctxt->op_bytes);
if (rc != X86EMUL_CONTINUE)
return rc;
- register_address_increment(ctxt, &ctxt->regs[VCPU_REGS_RSP], ctxt->src.val);
+ rsp_increment(ctxt, ctxt->src.val);
return X86EMUL_CONTINUE;
}
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index 1df8fb9e1d5d..9fc9aa7ac703 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -316,6 +316,11 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
addr &= 1;
if (addr == 0) {
if (val & 0x10) {
+ u8 edge_irr = s->irr & ~s->elcr;
+ int i;
+ bool found = false;
+ struct kvm_vcpu *vcpu;
+
s->init4 = val & 1;
s->last_irr = 0;
s->irr &= s->elcr;
@@ -333,6 +338,18 @@ static void pic_ioport_write(void *opaque, u32 addr, u32 val)
if (val & 0x08)
pr_pic_unimpl(
"level sensitive irq not supported");
+
+ kvm_for_each_vcpu(i, vcpu, s->pics_state->kvm)
+ if (kvm_apic_accept_pic_intr(vcpu)) {
+ found = true;
+ break;
+ }
+
+
+ if (found)
+ for (irq = 0; irq < PIC_NUM_PINS/2; irq++)
+ if (edge_irr & (1 << irq))
+ pic_clear_isr(s, irq);
} else if (val & 0x08) {
if (val & 0x04)
s->poll = 1;
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 01ca00423938..7fbd0d273ea8 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -4113,16 +4113,21 @@ static int mmu_shrink(struct shrinker *shrink, struct shrink_control *sc)
LIST_HEAD(invalid_list);
/*
+ * Never scan more than sc->nr_to_scan VM instances.
+ * Will not hit this condition practically since we do not try
+ * to shrink more than one VM and it is very unlikely to see
+ * !n_used_mmu_pages so many times.
+ */
+ if (!nr_to_scan--)
+ break;
+ /*
* n_used_mmu_pages is accessed without holding kvm->mmu_lock
* here. We may skip a VM instance errorneosly, but we do not
* want to shrink a VM that only started to populate its MMU
* anyway.
*/
- if (kvm->arch.n_used_mmu_pages > 0) {
- if (!nr_to_scan--)
- break;
+ if (!kvm->arch.n_used_mmu_pages)
continue;
- }
idx = srcu_read_lock(&kvm->srcu);
spin_lock(&kvm->mmu_lock);
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index c39b60707e02..b1eb202ee76a 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1488,13 +1488,6 @@ static void __vmx_load_host_state(struct vcpu_vmx *vmx)
loadsegment(ds, vmx->host_state.ds_sel);
loadsegment(es, vmx->host_state.es_sel);
}
-#else
- /*
- * The sysexit path does not restore ds/es, so we must set them to
- * a reasonable value ourselves.
- */
- loadsegment(ds, __USER_DS);
- loadsegment(es, __USER_DS);
#endif
reload_tss();
#ifdef CONFIG_X86_64
@@ -3626,6 +3619,7 @@ static void seg_setup(int seg)
static int alloc_apic_access_page(struct kvm *kvm)
{
+ struct page *page;
struct kvm_userspace_memory_region kvm_userspace_mem;
int r = 0;
@@ -3640,7 +3634,13 @@ static int alloc_apic_access_page(struct kvm *kvm)
if (r)
goto out;
- kvm->arch.apic_access_page = gfn_to_page(kvm, 0xfee00);
+ page = gfn_to_page(kvm, 0xfee00);
+ if (is_error_page(page)) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ kvm->arch.apic_access_page = page;
out:
mutex_unlock(&kvm->slots_lock);
return r;
@@ -3648,6 +3648,7 @@ out:
static int alloc_identity_pagetable(struct kvm *kvm)
{
+ struct page *page;
struct kvm_userspace_memory_region kvm_userspace_mem;
int r = 0;
@@ -3663,8 +3664,13 @@ static int alloc_identity_pagetable(struct kvm *kvm)
if (r)
goto out;
- kvm->arch.ept_identity_pagetable = gfn_to_page(kvm,
- kvm->arch.ept_identity_map_addr >> PAGE_SHIFT);
+ page = gfn_to_page(kvm, kvm->arch.ept_identity_map_addr >> PAGE_SHIFT);
+ if (is_error_page(page)) {
+ r = -EFAULT;
+ goto out;
+ }
+
+ kvm->arch.ept_identity_pagetable = page;
out:
mutex_unlock(&kvm->slots_lock);
return r;
@@ -6370,6 +6376,19 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
#endif
);
+#ifndef CONFIG_X86_64
+ /*
+ * The sysexit path does not restore ds/es, so we must set them to
+ * a reasonable value ourselves.
+ *
+ * We can't defer this to vmx_load_host_state() since that function
+ * may be executed in interrupt context, which saves and restore segments
+ * around it, nullifying its effect.
+ */
+ loadsegment(ds, __USER_DS);
+ loadsegment(es, __USER_DS);
+#endif
+
vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
| (1 << VCPU_EXREG_RFLAGS)
| (1 << VCPU_EXREG_CPL)
@@ -6569,7 +6588,7 @@ static void vmx_cpuid_update(struct kvm_vcpu *vcpu)
/* Exposing INVPCID only when PCID is exposed */
best = kvm_find_cpuid_entry(vcpu, 0x7, 0);
if (vmx_invpcid_supported() &&
- best && (best->ecx & bit(X86_FEATURE_INVPCID)) &&
+ best && (best->ebx & bit(X86_FEATURE_INVPCID)) &&
guest_cpuid_has_pcid(vcpu)) {
exec_control |= SECONDARY_EXEC_ENABLE_INVPCID;
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
@@ -6579,7 +6598,7 @@ static void vmx_cpuid_update(struct kvm_vcpu *vcpu)
vmcs_write32(SECONDARY_VM_EXEC_CONTROL,
exec_control);
if (best)
- best->ecx &= ~bit(X86_FEATURE_INVPCID);
+ best->ebx &= ~bit(X86_FEATURE_INVPCID);
}
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 59b59508ff07..2966c847d489 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -806,7 +806,7 @@ EXPORT_SYMBOL_GPL(kvm_rdpmc);
* kvm-specific. Those are put in the beginning of the list.
*/
-#define KVM_SAVE_MSRS_BEGIN 9
+#define KVM_SAVE_MSRS_BEGIN 10
static u32 msrs_to_save[] = {
MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW,
@@ -925,6 +925,10 @@ static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock)
*/
getboottime(&boot);
+ if (kvm->arch.kvmclock_offset) {
+ struct timespec ts = ns_to_timespec(kvm->arch.kvmclock_offset);
+ boot = timespec_sub(boot, ts);
+ }
wc.sec = boot.tv_sec;
wc.nsec = boot.tv_nsec;
wc.version = version;
@@ -1996,6 +2000,9 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case MSR_KVM_STEAL_TIME:
data = vcpu->arch.st.msr_val;
break;
+ case MSR_KVM_PV_EOI_EN:
+ data = vcpu->arch.pv_eoi.msr_val;
+ break;
case MSR_IA32_P5_MC_ADDR:
case MSR_IA32_P5_MC_TYPE:
case MSR_IA32_MCG_CAP:
@@ -5106,17 +5113,20 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu)
!kvm_event_needs_reinjection(vcpu);
}
-static void vapic_enter(struct kvm_vcpu *vcpu)
+static int vapic_enter(struct kvm_vcpu *vcpu)
{
struct kvm_lapic *apic = vcpu->arch.apic;
struct page *page;
if (!apic || !apic->vapic_addr)
- return;
+ return 0;
page = gfn_to_page(vcpu->kvm, apic->vapic_addr >> PAGE_SHIFT);
+ if (is_error_page(page))
+ return -EFAULT;
vcpu->arch.apic->vapic_page = page;
+ return 0;
}
static void vapic_exit(struct kvm_vcpu *vcpu)
@@ -5423,7 +5433,11 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
}
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
- vapic_enter(vcpu);
+ r = vapic_enter(vcpu);
+ if (r) {
+ srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
+ return r;
+ }
r = 1;
while (r > 0) {
diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
index f6679a7fb8ca..b91e48512425 100644
--- a/arch/x86/mm/hugetlbpage.c
+++ b/arch/x86/mm/hugetlbpage.c
@@ -56,9 +56,16 @@ static int vma_shareable(struct vm_area_struct *vma, unsigned long addr)
}
/*
- * search for a shareable pmd page for hugetlb.
+ * Search for a shareable pmd page for hugetlb. In any case calls pmd_alloc()
+ * and returns the corresponding pte. While this is not necessary for the
+ * !shared pmd case because we can allocate the pmd later as well, it makes the
+ * code much cleaner. pmd allocation is essential for the shared case because
+ * pud has to be populated inside the same i_mmap_mutex section - otherwise
+ * racing tasks could either miss the sharing (see huge_pte_offset) or select a
+ * bad pmd for sharing.
*/
-static void huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
+static pte_t *
+huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
{
struct vm_area_struct *vma = find_vma(mm, addr);
struct address_space *mapping = vma->vm_file->f_mapping;
@@ -68,9 +75,10 @@ static void huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
struct vm_area_struct *svma;
unsigned long saddr;
pte_t *spte = NULL;
+ pte_t *pte;
if (!vma_shareable(vma, addr))
- return;
+ return (pte_t *)pmd_alloc(mm, pud, addr);
mutex_lock(&mapping->i_mmap_mutex);
vma_prio_tree_foreach(svma, &iter, &mapping->i_mmap, idx, idx) {
@@ -97,7 +105,9 @@ static void huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
put_page(virt_to_page(spte));
spin_unlock(&mm->page_table_lock);
out:
+ pte = (pte_t *)pmd_alloc(mm, pud, addr);
mutex_unlock(&mapping->i_mmap_mutex);
+ return pte;
}
/*
@@ -142,8 +152,9 @@ pte_t *huge_pte_alloc(struct mm_struct *mm,
} else {
BUG_ON(sz != PMD_SIZE);
if (pud_none(*pud))
- huge_pmd_share(mm, addr, pud);
- pte = (pte_t *) pmd_alloc(mm, pud, addr);
+ pte = huge_pmd_share(mm, addr, pud);
+ else
+ pte = (pte_t *)pmd_alloc(mm, pud, addr);
}
}
BUG_ON(pte && !pte_none(*pte) && !pte_huge(*pte));
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index e0e6990723e9..ab1f6a93b527 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -319,7 +319,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
*/
int devmem_is_allowed(unsigned long pagenr)
{
- if (pagenr <= 256)
+ if (pagenr < 256)
return 1;
if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
return 0;
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 931930a96160..a718e0d23503 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -919,13 +919,11 @@ static int change_page_attr_set_clr(unsigned long *addr, int numpages,
/*
* On success we use clflush, when the CPU supports it to
- * avoid the wbindv. If the CPU does not support it, in the
- * error case, and during early boot (for EFI) we fall back
- * to cpa_flush_all (which uses wbinvd):
+ * avoid the wbindv. If the CPU does not support it and in the
+ * error case we fall back to cpa_flush_all (which uses
+ * wbindv):
*/
- if (early_boot_irqs_disabled)
- __cpa_flush_all((void *)(long)cache);
- else if (!ret && cpu_has_clflush) {
+ if (!ret && cpu_has_clflush) {
if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) {
cpa_flush_array(addr, numpages, cache,
cpa.flags, pages);
diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c
index 4599c3e8bcb6..4ddf497ca65b 100644
--- a/arch/x86/mm/srat.c
+++ b/arch/x86/mm/srat.c
@@ -142,23 +142,23 @@ static inline int save_add_info(void) {return 0;}
#endif
/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
-void __init
+int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
u64 start, end;
int node, pxm;
if (srat_disabled())
- return;
+ return -1;
if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
bad_srat();
- return;
+ return -1;
}
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
- return;
+ return -1;
if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info())
- return;
+ return -1;
start = ma->base_address;
end = start + ma->length;
pxm = ma->proximity_domain;
@@ -168,12 +168,12 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
if (node < 0) {
printk(KERN_ERR "SRAT: Too many proximity domains.\n");
bad_srat();
- return;
+ return -1;
}
if (numa_add_memblk(node, start, end) < 0) {
bad_srat();
- return;
+ return -1;
}
node_set(node, numa_nodes_parsed);
@@ -181,6 +181,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n",
node, pxm,
(unsigned long long) start, (unsigned long long) end - 1);
+ return 0;
}
void __init acpi_numa_arch_fixup(void) {}
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 2dc29f51e75a..92660edaa1e7 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -234,7 +234,22 @@ static efi_status_t __init phys_efi_set_virtual_address_map(
return status;
}
-static int efi_set_rtc_mmss(unsigned long nowtime)
+static efi_status_t __init phys_efi_get_time(efi_time_t *tm,
+ efi_time_cap_t *tc)
+{
+ unsigned long flags;
+ efi_status_t status;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ efi_call_phys_prelog();
+ status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm),
+ virt_to_phys(tc));
+ efi_call_phys_epilog();
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return status;
+}
+
+int efi_set_rtc_mmss(unsigned long nowtime)
{
int real_seconds, real_minutes;
efi_status_t status;
@@ -263,7 +278,7 @@ static int efi_set_rtc_mmss(unsigned long nowtime)
return 0;
}
-static unsigned long efi_get_time(void)
+unsigned long efi_get_time(void)
{
efi_status_t status;
efi_time_t eft;
@@ -606,13 +621,18 @@ static int __init efi_runtime_init(void)
}
/*
* We will only need *early* access to the following
- * EFI runtime service before set_virtual_address_map
+ * two EFI runtime services before set_virtual_address_map
* is invoked.
*/
+ efi_phys.get_time = (efi_get_time_t *)runtime->get_time;
efi_phys.set_virtual_address_map =
(efi_set_virtual_address_map_t *)
runtime->set_virtual_address_map;
-
+ /*
+ * Make efi_get_time can be called before entering
+ * virtual mode.
+ */
+ efi.get_time = phys_efi_get_time;
early_iounmap(runtime, sizeof(efi_runtime_services_t));
return 0;
@@ -700,10 +720,12 @@ void __init efi_init(void)
efi_enabled = 0;
return;
}
+#ifdef CONFIG_X86_32
if (efi_native) {
x86_platform.get_wallclock = efi_get_time;
x86_platform.set_wallclock = efi_set_rtc_mmss;
}
+#endif
#if EFI_DEBUG
print_efi_memmap();
diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c
index 0ce8616c88ae..d75582d1aa55 100644
--- a/arch/x86/platform/olpc/olpc-xo1-pm.c
+++ b/arch/x86/platform/olpc/olpc-xo1-pm.c
@@ -18,6 +18,7 @@
#include <linux/pm.h>
#include <linux/mfd/core.h>
#include <linux/suspend.h>
+#include <linux/olpc-ec.h>
#include <asm/io.h>
#include <asm/olpc.h>
@@ -51,16 +52,11 @@ EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
static int xo1_power_state_enter(suspend_state_t pm_state)
{
unsigned long saved_sci_mask;
- int r;
/* Only STR is supported */
if (pm_state != PM_SUSPEND_MEM)
return -EINVAL;
- r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
- if (r)
- return r;
-
/*
* Save SCI mask (this gets lost since PM1_EN is used as a mask for
* wakeup events, which is not necessarily the same event set)
@@ -76,16 +72,6 @@ static int xo1_power_state_enter(suspend_state_t pm_state)
/* Restore SCI mask (using dword access to CS5536_PM1_EN) */
outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
- /* Tell the EC to stop inhibiting SCIs */
- olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
-
- /*
- * Tell the wireless module to restart USB communication.
- * Must be done twice.
- */
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
- olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
-
return 0;
}
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c
index 04b8c73659c5..63d4aa40956e 100644
--- a/arch/x86/platform/olpc/olpc-xo1-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo1-sci.c
@@ -23,6 +23,7 @@
#include <linux/power_supply.h>
#include <linux/suspend.h>
#include <linux/workqueue.h>
+#include <linux/olpc-ec.h>
#include <asm/io.h>
#include <asm/msr.h>
diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c
index 599be499fdf7..2fdca25905ae 100644
--- a/arch/x86/platform/olpc/olpc-xo15-sci.c
+++ b/arch/x86/platform/olpc/olpc-xo15-sci.c
@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/power_supply.h>
+#include <linux/olpc-ec.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
index a4bee53c2e54..27376081ddec 100644
--- a/arch/x86/platform/olpc/olpc.c
+++ b/arch/x86/platform/olpc/olpc.c
@@ -14,14 +14,13 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/syscore_ops.h>
-#include <linux/debugfs.h>
#include <linux/mutex.h>
+#include <linux/olpc-ec.h>
#include <asm/geode.h>
#include <asm/setup.h>
@@ -31,17 +30,6 @@
struct olpc_platform_t olpc_platform_info;
EXPORT_SYMBOL_GPL(olpc_platform_info);
-static DEFINE_SPINLOCK(ec_lock);
-
-/* debugfs interface to EC commands */
-#define EC_MAX_CMD_ARGS (5 + 1) /* cmd byte + 5 args */
-#define EC_MAX_CMD_REPLY (8)
-
-static struct dentry *ec_debugfs_dir;
-static DEFINE_MUTEX(ec_debugfs_cmd_lock);
-static unsigned char ec_debugfs_resp[EC_MAX_CMD_REPLY];
-static unsigned int ec_debugfs_resp_bytes;
-
/* EC event mask to be applied during suspend (defining wakeup sources). */
static u16 ec_wakeup_mask;
@@ -125,16 +113,13 @@ static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
* <http://wiki.laptop.org/go/Ec_specification>. Unfortunately, while
* OpenFirmware's source is available, the EC's is not.
*/
-int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
- unsigned char *outbuf, size_t outlen)
+static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
+ size_t outlen, void *arg)
{
- unsigned long flags;
int ret = -EIO;
int i;
int restarts = 0;
- spin_lock_irqsave(&ec_lock, flags);
-
/* Clear OBF */
for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
inb(0x68);
@@ -198,10 +183,8 @@ restart:
ret = 0;
err:
- spin_unlock_irqrestore(&ec_lock, flags);
return ret;
}
-EXPORT_SYMBOL_GPL(olpc_ec_cmd);
void olpc_ec_wakeup_set(u16 value)
{
@@ -280,96 +263,6 @@ int olpc_ec_sci_query(u16 *sci_value)
}
EXPORT_SYMBOL_GPL(olpc_ec_sci_query);
-static ssize_t ec_debugfs_cmd_write(struct file *file, const char __user *buf,
- size_t size, loff_t *ppos)
-{
- int i, m;
- unsigned char ec_cmd[EC_MAX_CMD_ARGS];
- unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
- char cmdbuf[64];
- int ec_cmd_bytes;
-
- mutex_lock(&ec_debugfs_cmd_lock);
-
- size = simple_write_to_buffer(cmdbuf, sizeof(cmdbuf), ppos, buf, size);
-
- m = sscanf(cmdbuf, "%x:%u %x %x %x %x %x", &ec_cmd_int[0],
- &ec_debugfs_resp_bytes,
- &ec_cmd_int[1], &ec_cmd_int[2], &ec_cmd_int[3],
- &ec_cmd_int[4], &ec_cmd_int[5]);
- if (m < 2 || ec_debugfs_resp_bytes > EC_MAX_CMD_REPLY) {
- /* reset to prevent overflow on read */
- ec_debugfs_resp_bytes = 0;
-
- printk(KERN_DEBUG "olpc-ec: bad ec cmd: "
- "cmd:response-count [arg1 [arg2 ...]]\n");
- size = -EINVAL;
- goto out;
- }
-
- /* convert scanf'd ints to char */
- ec_cmd_bytes = m - 2;
- for (i = 0; i <= ec_cmd_bytes; i++)
- ec_cmd[i] = ec_cmd_int[i];
-
- printk(KERN_DEBUG "olpc-ec: debugfs cmd 0x%02x with %d args "
- "%02x %02x %02x %02x %02x, want %d returns\n",
- ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2], ec_cmd[3],
- ec_cmd[4], ec_cmd[5], ec_debugfs_resp_bytes);
-
- olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
- ec_cmd_bytes, ec_debugfs_resp, ec_debugfs_resp_bytes);
-
- printk(KERN_DEBUG "olpc-ec: response "
- "%02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
- ec_debugfs_resp[0], ec_debugfs_resp[1], ec_debugfs_resp[2],
- ec_debugfs_resp[3], ec_debugfs_resp[4], ec_debugfs_resp[5],
- ec_debugfs_resp[6], ec_debugfs_resp[7], ec_debugfs_resp_bytes);
-
-out:
- mutex_unlock(&ec_debugfs_cmd_lock);
- return size;
-}
-
-static ssize_t ec_debugfs_cmd_read(struct file *file, char __user *buf,
- size_t size, loff_t *ppos)
-{
- unsigned int i, r;
- char *rp;
- char respbuf[64];
-
- mutex_lock(&ec_debugfs_cmd_lock);
- rp = respbuf;
- rp += sprintf(rp, "%02x", ec_debugfs_resp[0]);
- for (i = 1; i < ec_debugfs_resp_bytes; i++)
- rp += sprintf(rp, ", %02x", ec_debugfs_resp[i]);
- mutex_unlock(&ec_debugfs_cmd_lock);
- rp += sprintf(rp, "\n");
-
- r = rp - respbuf;
- return simple_read_from_buffer(buf, size, ppos, respbuf, r);
-}
-
-static const struct file_operations ec_debugfs_genops = {
- .write = ec_debugfs_cmd_write,
- .read = ec_debugfs_cmd_read,
-};
-
-static void setup_debugfs(void)
-{
- ec_debugfs_dir = debugfs_create_dir("olpc-ec", 0);
- if (ec_debugfs_dir == ERR_PTR(-ENODEV))
- return;
-
- debugfs_create_file("cmd", 0600, ec_debugfs_dir, NULL,
- &ec_debugfs_genops);
-}
-
-static int olpc_ec_suspend(void)
-{
- return olpc_ec_mask_write(ec_wakeup_mask);
-}
-
static bool __init check_ofw_architecture(struct device_node *root)
{
const char *olpc_arch;
@@ -424,8 +317,59 @@ static int __init add_xo1_platform_devices(void)
return 0;
}
-static struct syscore_ops olpc_syscore_ops = {
- .suspend = olpc_ec_suspend,
+static int olpc_xo1_ec_probe(struct platform_device *pdev)
+{
+ /* get the EC revision */
+ olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
+ (unsigned char *) &olpc_platform_info.ecver, 1);
+
+ /* EC version 0x5f adds support for wide SCI mask */
+ if (olpc_platform_info.ecver >= 0x5f)
+ olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
+
+ pr_info("OLPC board revision %s%X (EC=%x)\n",
+ ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
+ olpc_platform_info.boardrev >> 4,
+ olpc_platform_info.ecver);
+
+ return 0;
+}
+static int olpc_xo1_ec_suspend(struct platform_device *pdev)
+{
+ olpc_ec_mask_write(ec_wakeup_mask);
+
+ /*
+ * Squelch SCIs while suspended. This is a fix for
+ * <http://dev.laptop.org/ticket/1835>.
+ */
+ return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
+}
+
+static int olpc_xo1_ec_resume(struct platform_device *pdev)
+{
+ /* Tell the EC to stop inhibiting SCIs */
+ olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
+
+ /*
+ * Tell the wireless module to restart USB communication.
+ * Must be done twice.
+ */
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+ olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
+
+ return 0;
+}
+
+static struct olpc_ec_driver ec_xo1_driver = {
+ .probe = olpc_xo1_ec_probe,
+ .suspend = olpc_xo1_ec_suspend,
+ .resume = olpc_xo1_ec_resume,
+ .ec_cmd = olpc_xo1_ec_cmd,
+};
+
+static struct olpc_ec_driver ec_xo1_5_driver = {
+ .probe = olpc_xo1_ec_probe,
+ .ec_cmd = olpc_xo1_ec_cmd,
};
static int __init olpc_init(void)
@@ -435,16 +379,17 @@ static int __init olpc_init(void)
if (!olpc_ofw_present() || !platform_detect())
return 0;
- spin_lock_init(&ec_lock);
+ /* register the XO-1 and 1.5-specific EC handler */
+ if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
+ olpc_ec_driver_register(&ec_xo1_driver, NULL);
+ else
+ olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
+ platform_device_register_simple("olpc-ec", -1, NULL, 0);
/* assume B1 and above models always have a DCON */
if (olpc_board_at_least(olpc_board(0xb1)))
olpc_platform_info.flags |= OLPC_F_DCON;
- /* get the EC revision */
- olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
- (unsigned char *) &olpc_platform_info.ecver, 1);
-
#ifdef CONFIG_PCI_OLPC
/* If the VSA exists let it emulate PCI, if not emulate in kernel.
* XO-1 only. */
@@ -452,14 +397,6 @@ static int __init olpc_init(void)
!cs5535_has_vsa2())
x86_init.pci.arch_init = pci_olpc_init;
#endif
- /* EC version 0x5f adds support for wide SCI mask */
- if (olpc_platform_info.ecver >= 0x5f)
- olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI;
-
- printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
- ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
- olpc_platform_info.boardrev >> 4,
- olpc_platform_info.ecver);
if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
r = add_xo1_platform_devices();
@@ -467,9 +404,6 @@ static int __init olpc_init(void)
return r;
}
- register_syscore_ops(&olpc_syscore_ops);
- setup_debugfs();
-
return 0;
}
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index b2d534cab25f..88692871823f 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -72,7 +72,7 @@ KBUILD_CFLAGS := $(LINUXINCLUDE) -m32 -g -Os -D_SETUP -D__KERNEL__ -D_WAKEUP \
-Wall -Wstrict-prototypes \
-march=i386 -mregparm=3 \
-include $(srctree)/$(src)/../../boot/code16gcc.h \
- -fno-strict-aliasing -fomit-frame-pointer \
+ -fno-strict-aliasing -fomit-frame-pointer -fno-pic \
$(call cc-option, -ffreestanding) \
$(call cc-option, -fno-toplevel-reorder,\
$(call cc-option, -fno-unit-at-a-time)) \
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index 51171aeff0dc..a582bfed95bb 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -60,8 +60,8 @@
51 common getsockname sys_getsockname
52 common getpeername sys_getpeername
53 common socketpair sys_socketpair
-54 common setsockopt sys_setsockopt
-55 common getsockopt sys_getsockopt
+54 64 setsockopt sys_setsockopt
+55 64 getsockopt sys_getsockopt
56 common clone stub_clone
57 common fork stub_fork
58 common vfork stub_vfork
@@ -318,7 +318,7 @@
309 common getcpu sys_getcpu
310 64 process_vm_readv sys_process_vm_readv
311 64 process_vm_writev sys_process_vm_writev
-312 64 kcmp sys_kcmp
+312 common kcmp sys_kcmp
#
# x32-specific system call numbers start at 512 to avoid cache impact
@@ -353,3 +353,5 @@
538 x32 sendmmsg compat_sys_sendmmsg
539 x32 process_vm_readv compat_sys_process_vm_readv
540 x32 process_vm_writev compat_sys_process_vm_writev
+541 x32 setsockopt compat_sys_setsockopt
+542 x32 getsockopt compat_sys_getsockopt
diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig
index 9926e11a772d..aeaff8bef2f1 100644
--- a/arch/x86/um/Kconfig
+++ b/arch/x86/um/Kconfig
@@ -21,6 +21,7 @@ config 64BIT
config X86_32
def_bool !64BIT
select HAVE_AOUT
+ select ARCH_WANT_IPC_PARSE_VERSION
config X86_64
def_bool 64BIT
diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h
index 950dfb7b8417..e72cd0df5ba3 100644
--- a/arch/x86/um/asm/ptrace.h
+++ b/arch/x86/um/asm/ptrace.h
@@ -30,10 +30,10 @@
#define profile_pc(regs) PT_REGS_IP(regs)
#define UPT_RESTART_SYSCALL(r) (UPT_IP(r) -= 2)
-#define UPT_SET_SYSCALL_RETURN(r, res) (UPT_AX(r) = (res))
+#define PT_REGS_SET_SYSCALL_RETURN(r, res) (PT_REGS_AX(r) = (res))
-static inline long regs_return_value(struct uml_pt_regs *regs)
+static inline long regs_return_value(struct pt_regs *regs)
{
- return UPT_AX(regs);
+ return PT_REGS_AX(regs);
}
#endif /* __UM_X86_PTRACE_H */
diff --git a/arch/x86/um/shared/sysdep/kernel-offsets.h b/arch/x86/um/shared/sysdep/kernel-offsets.h
index 5868526b5eef..46a9df99f3c5 100644
--- a/arch/x86/um/shared/sysdep/kernel-offsets.h
+++ b/arch/x86/um/shared/sysdep/kernel-offsets.h
@@ -7,9 +7,6 @@
#define DEFINE(sym, val) \
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
-#define STR(x) #x
-#define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " STR(val) " " #val: : )
-
#define BLANK() asm volatile("\n->" : : )
#define OFFSET(sym, str, mem) \
diff --git a/arch/x86/um/shared/sysdep/syscalls.h b/arch/x86/um/shared/sysdep/syscalls.h
index bd9a89b67e41..ca255a805ed9 100644
--- a/arch/x86/um/shared/sysdep/syscalls.h
+++ b/arch/x86/um/shared/sysdep/syscalls.h
@@ -1,3 +1,5 @@
+extern long sys_clone(unsigned long clone_flags, unsigned long newsp,
+ void __user *parent_tid, void __user *child_tid);
#ifdef __i386__
#include "syscalls_32.h"
#else
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index a508cea13503..ba7363ecf896 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -416,9 +416,6 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig,
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) 0;
PT_REGS_CX(regs) = (unsigned long) 0;
-
- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
- ptrace_notify(SIGTRAP);
return 0;
}
@@ -466,9 +463,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) &frame->info;
PT_REGS_CX(regs) = (unsigned long) &frame->uc;
-
- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
- ptrace_notify(SIGTRAP);
return 0;
}
diff --git a/arch/x86/um/sys_call_table_32.c b/arch/x86/um/sys_call_table_32.c
index 68d1dc91b37b..b5408cecac6c 100644
--- a/arch/x86/um/sys_call_table_32.c
+++ b/arch/x86/um/sys_call_table_32.c
@@ -28,7 +28,7 @@
#define ptregs_execve sys_execve
#define ptregs_iopl sys_iopl
#define ptregs_vm86old sys_vm86old
-#define ptregs_clone sys_clone
+#define ptregs_clone i386_clone
#define ptregs_vm86 sys_vm86
#define ptregs_sigaltstack sys_sigaltstack
#define ptregs_vfork sys_vfork
diff --git a/arch/x86/um/syscalls_32.c b/arch/x86/um/syscalls_32.c
index b853e8600b9d..db444c7218fe 100644
--- a/arch/x86/um/syscalls_32.c
+++ b/arch/x86/um/syscalls_32.c
@@ -3,37 +3,24 @@
* Licensed under the GPL
*/
-#include "linux/sched.h"
-#include "linux/shm.h"
-#include "linux/ipc.h"
-#include "linux/syscalls.h"
-#include "asm/mman.h"
-#include "asm/uaccess.h"
-#include "asm/unistd.h"
+#include <linux/syscalls.h>
+#include <sysdep/syscalls.h>
/*
* The prototype on i386 is:
*
- * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr)
+ * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls
*
* and the "newtls" arg. on i386 is read by copy_thread directly from the
* register saved on the stack.
*/
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
- int __user *parent_tid, void *newtls, int __user *child_tid)
+long i386_clone(unsigned long clone_flags, unsigned long newsp,
+ int __user *parent_tid, void *newtls, int __user *child_tid)
{
- long ret;
-
- if (!newsp)
- newsp = UPT_SP(&current->thread.regs.regs);
-
- current->thread.forking = 1;
- ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
- child_tid);
- current->thread.forking = 0;
- return ret;
+ return sys_clone(clone_flags, newsp, parent_tid, child_tid);
}
+
long sys_sigaction(int sig, const struct old_sigaction __user *act,
struct old_sigaction __user *oact)
{
diff --git a/arch/x86/um/syscalls_64.c b/arch/x86/um/syscalls_64.c
index f3d82bb6e15a..adb08eb5c22a 100644
--- a/arch/x86/um/syscalls_64.c
+++ b/arch/x86/um/syscalls_64.c
@@ -5,12 +5,9 @@
* Licensed under the GPL
*/
-#include "linux/linkage.h"
-#include "linux/personality.h"
-#include "linux/utsname.h"
-#include "asm/prctl.h" /* XXX This should get the constants from libc */
-#include "asm/uaccess.h"
-#include "os.h"
+#include <linux/sched.h>
+#include <asm/prctl.h> /* XXX This should get the constants from libc */
+#include <os.h>
long arch_prctl(struct task_struct *task, int code, unsigned long __user *addr)
{
@@ -79,20 +76,6 @@ long sys_arch_prctl(int code, unsigned long addr)
return arch_prctl(current, code, (unsigned long __user *) addr);
}
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
- void __user *parent_tid, void __user *child_tid)
-{
- long ret;
-
- if (!newsp)
- newsp = UPT_SP(&current->thread.regs.regs);
- current->thread.forking = 1;
- ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
- child_tid);
- current->thread.forking = 0;
- return ret;
-}
-
void arch_switch_to(struct task_struct *to)
{
if ((to->thread.arch.fs == 0) || (to->mm == NULL))
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index bf4bda6d3e9a..1fbe75a95f15 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -31,7 +31,6 @@
#include <linux/pci.h>
#include <linux/gfp.h>
#include <linux/memblock.h>
-#include <linux/syscore_ops.h>
#include <xen/xen.h>
#include <xen/interface/xen.h>
@@ -1453,6 +1452,10 @@ asmlinkage void __init xen_start_kernel(void)
pci_request_acs();
xen_acpi_sleep_register();
+
+ /* Avoid searching for BIOS MP tables */
+ x86_init.mpparse.find_smp_config = x86_init_noop;
+ x86_init.mpparse.get_smp_config = x86_init_uint_noop;
}
#ifdef CONFIG_PCI
/* PCI BIOS service won't work from a PV guest. */
@@ -1470,130 +1473,38 @@ asmlinkage void __init xen_start_kernel(void)
#endif
}
-#ifdef CONFIG_XEN_PVHVM
-/*
- * The pfn containing the shared_info is located somewhere in RAM. This
- * will cause trouble if the current kernel is doing a kexec boot into a
- * new kernel. The new kernel (and its startup code) can not know where
- * the pfn is, so it can not reserve the page. The hypervisor will
- * continue to update the pfn, and as a result memory corruption occours
- * in the new kernel.
- *
- * One way to work around this issue is to allocate a page in the
- * xen-platform pci device's BAR memory range. But pci init is done very
- * late and the shared_info page is already in use very early to read
- * the pvclock. So moving the pfn from RAM to MMIO is racy because some
- * code paths on other vcpus could access the pfn during the small
- * window when the old pfn is moved to the new pfn. There is even a
- * small window were the old pfn is not backed by a mfn, and during that
- * time all reads return -1.
- *
- * Because it is not known upfront where the MMIO region is located it
- * can not be used right from the start in xen_hvm_init_shared_info.
- *
- * To minimise trouble the move of the pfn is done shortly before kexec.
- * This does not eliminate the race because all vcpus are still online
- * when the syscore_ops will be called. But hopefully there is no work
- * pending at this point in time. Also the syscore_op is run last which
- * reduces the risk further.
- */
-
-static struct shared_info *xen_hvm_shared_info;
-
-static void xen_hvm_connect_shared_info(unsigned long pfn)
+void __ref xen_hvm_init_shared_info(void)
{
+ int cpu;
struct xen_add_to_physmap xatp;
+ static struct shared_info *shared_info_page = 0;
+ if (!shared_info_page)
+ shared_info_page = (struct shared_info *)
+ extend_brk(PAGE_SIZE, PAGE_SIZE);
xatp.domid = DOMID_SELF;
xatp.idx = 0;
xatp.space = XENMAPSPACE_shared_info;
- xatp.gpfn = pfn;
+ xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT;
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
BUG();
-}
-static void xen_hvm_set_shared_info(struct shared_info *sip)
-{
- int cpu;
-
- HYPERVISOR_shared_info = sip;
+ HYPERVISOR_shared_info = (struct shared_info *)shared_info_page;
/* xen_vcpu is a pointer to the vcpu_info struct in the shared_info
* page, we use it in the event channel upcall and in some pvclock
* related functions. We don't need the vcpu_info placement
* optimizations because we don't use any pv_mmu or pv_irq op on
* HVM.
- * When xen_hvm_set_shared_info is run at boot time only vcpu 0 is
- * online but xen_hvm_set_shared_info is run at resume time too and
+ * When xen_hvm_init_shared_info is run at boot time only vcpu 0 is
+ * online but xen_hvm_init_shared_info is run at resume time too and
* in that case multiple vcpus might be online. */
for_each_online_cpu(cpu) {
per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu];
}
}
-/* Reconnect the shared_info pfn to a mfn */
-void xen_hvm_resume_shared_info(void)
-{
- xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT);
-}
-
-#ifdef CONFIG_KEXEC
-static struct shared_info *xen_hvm_shared_info_kexec;
-static unsigned long xen_hvm_shared_info_pfn_kexec;
-
-/* Remember a pfn in MMIO space for kexec reboot */
-void __devinit xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn)
-{
- xen_hvm_shared_info_kexec = sip;
- xen_hvm_shared_info_pfn_kexec = pfn;
-}
-
-static void xen_hvm_syscore_shutdown(void)
-{
- struct xen_memory_reservation reservation = {
- .domid = DOMID_SELF,
- .nr_extents = 1,
- };
- unsigned long prev_pfn;
- int rc;
-
- if (!xen_hvm_shared_info_kexec)
- return;
-
- prev_pfn = __pa(xen_hvm_shared_info) >> PAGE_SHIFT;
- set_xen_guest_handle(reservation.extent_start, &prev_pfn);
-
- /* Move pfn to MMIO, disconnects previous pfn from mfn */
- xen_hvm_connect_shared_info(xen_hvm_shared_info_pfn_kexec);
-
- /* Update pointers, following hypercall is also a memory barrier */
- xen_hvm_set_shared_info(xen_hvm_shared_info_kexec);
-
- /* Allocate new mfn for previous pfn */
- do {
- rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation);
- if (rc == 0)
- msleep(123);
- } while (rc == 0);
-
- /* Make sure the previous pfn is really connected to a (new) mfn */
- BUG_ON(rc != 1);
-}
-
-static struct syscore_ops xen_hvm_syscore_ops = {
- .shutdown = xen_hvm_syscore_shutdown,
-};
-#endif
-
-/* Use a pfn in RAM, may move to MMIO before kexec. */
-static void __init xen_hvm_init_shared_info(void)
-{
- /* Remember pointer for resume */
- xen_hvm_shared_info = extend_brk(PAGE_SIZE, PAGE_SIZE);
- xen_hvm_connect_shared_info(__pa(xen_hvm_shared_info) >> PAGE_SHIFT);
- xen_hvm_set_shared_info(xen_hvm_shared_info);
-}
-
+#ifdef CONFIG_XEN_PVHVM
static void __init init_hvm_pv_info(void)
{
int major, minor;
@@ -1644,9 +1555,6 @@ static void __init xen_hvm_guest_init(void)
init_hvm_pv_info();
xen_hvm_init_shared_info();
-#ifdef CONFIG_KEXEC
- register_syscore_ops(&xen_hvm_syscore_ops);
-#endif
if (xen_feature(XENFEAT_hvm_callback_vector))
xen_have_vector_callback = 1;
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index b65a76133f4f..5141d808e751 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1283,7 +1283,7 @@ static void xen_flush_tlb_others(const struct cpumask *cpus,
cpumask_clear_cpu(smp_processor_id(), to_cpumask(args->mask));
args->op.cmd = MMUEXT_TLB_FLUSH_MULTI;
- if (start != TLB_FLUSH_ALL && (end - start) <= PAGE_SIZE) {
+ if (end != TLB_FLUSH_ALL && (end - start) <= PAGE_SIZE) {
args->op.cmd = MMUEXT_INVLPG_MULTI;
args->op.arg1.linear_addr = start;
}
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 64effdc6da94..72213da605f5 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -194,6 +194,13 @@ RESERVE_BRK(p2m_mid_mfn, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID
* boundary violation will require three middle nodes. */
RESERVE_BRK(p2m_mid_identity, PAGE_SIZE * 2 * 3);
+/* When we populate back during bootup, the amount of pages can vary. The
+ * max we have is seen is 395979, but that does not mean it can't be more.
+ * Some machines can have 3GB I/O holes even. With early_can_reuse_p2m_middle
+ * it can re-use Xen provided mfn_list array, so we only need to allocate at
+ * most three P2M top nodes. */
+RESERVE_BRK(p2m_populated, PAGE_SIZE * 3);
+
static inline unsigned p2m_top_index(unsigned long pfn)
{
BUG_ON(pfn >= MAX_P2M_PFN);
@@ -570,12 +577,99 @@ static bool __init early_alloc_p2m(unsigned long pfn)
}
return true;
}
+
+/*
+ * Skim over the P2M tree looking at pages that are either filled with
+ * INVALID_P2M_ENTRY or with 1:1 PFNs. If found, re-use that page and
+ * replace the P2M leaf with a p2m_missing or p2m_identity.
+ * Stick the old page in the new P2M tree location.
+ */
+bool __init early_can_reuse_p2m_middle(unsigned long set_pfn, unsigned long set_mfn)
+{
+ unsigned topidx;
+ unsigned mididx;
+ unsigned ident_pfns;
+ unsigned inv_pfns;
+ unsigned long *p2m;
+ unsigned long *mid_mfn_p;
+ unsigned idx;
+ unsigned long pfn;
+
+ /* We only look when this entails a P2M middle layer */
+ if (p2m_index(set_pfn))
+ return false;
+
+ for (pfn = 0; pfn < MAX_DOMAIN_PAGES; pfn += P2M_PER_PAGE) {
+ topidx = p2m_top_index(pfn);
+
+ if (!p2m_top[topidx])
+ continue;
+
+ if (p2m_top[topidx] == p2m_mid_missing)
+ continue;
+
+ mididx = p2m_mid_index(pfn);
+ p2m = p2m_top[topidx][mididx];
+ if (!p2m)
+ continue;
+
+ if ((p2m == p2m_missing) || (p2m == p2m_identity))
+ continue;
+
+ if ((unsigned long)p2m == INVALID_P2M_ENTRY)
+ continue;
+
+ ident_pfns = 0;
+ inv_pfns = 0;
+ for (idx = 0; idx < P2M_PER_PAGE; idx++) {
+ /* IDENTITY_PFNs are 1:1 */
+ if (p2m[idx] == IDENTITY_FRAME(pfn + idx))
+ ident_pfns++;
+ else if (p2m[idx] == INVALID_P2M_ENTRY)
+ inv_pfns++;
+ else
+ break;
+ }
+ if ((ident_pfns == P2M_PER_PAGE) || (inv_pfns == P2M_PER_PAGE))
+ goto found;
+ }
+ return false;
+found:
+ /* Found one, replace old with p2m_identity or p2m_missing */
+ p2m_top[topidx][mididx] = (ident_pfns ? p2m_identity : p2m_missing);
+ /* And the other for save/restore.. */
+ mid_mfn_p = p2m_top_mfn_p[topidx];
+ /* NOTE: Even if it is a p2m_identity it should still be point to
+ * a page filled with INVALID_P2M_ENTRY entries. */
+ mid_mfn_p[mididx] = virt_to_mfn(p2m_missing);
+
+ /* Reset where we want to stick the old page in. */
+ topidx = p2m_top_index(set_pfn);
+ mididx = p2m_mid_index(set_pfn);
+
+ /* This shouldn't happen */
+ if (WARN_ON(p2m_top[topidx] == p2m_mid_missing))
+ early_alloc_p2m(set_pfn);
+
+ if (WARN_ON(p2m_top[topidx][mididx] != p2m_missing))
+ return false;
+
+ p2m_init(p2m);
+ p2m_top[topidx][mididx] = p2m;
+ mid_mfn_p = p2m_top_mfn_p[topidx];
+ mid_mfn_p[mididx] = virt_to_mfn(p2m);
+
+ return true;
+}
bool __init early_set_phys_to_machine(unsigned long pfn, unsigned long mfn)
{
if (unlikely(!__set_phys_to_machine(pfn, mfn))) {
if (!early_alloc_p2m(pfn))
return false;
+ if (early_can_reuse_p2m_middle(pfn, mfn))
+ return __set_phys_to_machine(pfn, mfn);
+
if (!early_alloc_p2m_middle(pfn, false /* boundary crossover OK!*/))
return false;
@@ -734,9 +828,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
xen_mc_issue(PARAVIRT_LAZY_MMU);
}
- /* let's use dev_bus_addr to record the old mfn instead */
- kmap_op->dev_bus_addr = page->index;
- page->index = (unsigned long) kmap_op;
}
spin_lock_irqsave(&m2p_override_lock, flags);
list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]);
@@ -763,7 +854,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
return 0;
}
EXPORT_SYMBOL_GPL(m2p_add_override);
-int m2p_remove_override(struct page *page, bool clear_pte)
+int m2p_remove_override(struct page *page,
+ struct gnttab_map_grant_ref *kmap_op)
{
unsigned long flags;
unsigned long mfn;
@@ -793,10 +885,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
WARN_ON(!PagePrivate(page));
ClearPagePrivate(page);
- if (clear_pte) {
- struct gnttab_map_grant_ref *map_op =
- (struct gnttab_map_grant_ref *) page->index;
- set_phys_to_machine(pfn, map_op->dev_bus_addr);
+ set_phys_to_machine(pfn, page->index);
+ if (kmap_op != NULL) {
if (!PageHighMem(page)) {
struct multicall_space mcs;
struct gnttab_unmap_grant_ref *unmap_op;
@@ -808,13 +898,13 @@ int m2p_remove_override(struct page *page, bool clear_pte)
* issued. In this case handle is going to -1 because
* it hasn't been modified yet.
*/
- if (map_op->handle == -1)
+ if (kmap_op->handle == -1)
xen_mc_flush();
/*
- * Now if map_op->handle is negative it means that the
+ * Now if kmap_op->handle is negative it means that the
* hypercall actually returned an error.
*/
- if (map_op->handle == GNTST_general_error) {
+ if (kmap_op->handle == GNTST_general_error) {
printk(KERN_WARNING "m2p_remove_override: "
"pfn %lx mfn %lx, failed to modify kernel mappings",
pfn, mfn);
@@ -824,8 +914,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
mcs = xen_mc_entry(
sizeof(struct gnttab_unmap_grant_ref));
unmap_op = mcs.args;
- unmap_op->host_addr = map_op->host_addr;
- unmap_op->handle = map_op->handle;
+ unmap_op->host_addr = kmap_op->host_addr;
+ unmap_op->handle = kmap_op->handle;
unmap_op->dev_bus_addr = 0;
MULTI_grant_table_op(mcs.mc,
@@ -836,10 +926,9 @@ int m2p_remove_override(struct page *page, bool clear_pte)
set_pte_at(&init_mm, address, ptep,
pfn_pte(pfn, PAGE_KERNEL));
__flush_tlb_single(address);
- map_op->host_addr = 0;
+ kmap_op->host_addr = 0;
}
- } else
- set_phys_to_machine(pfn, page->index);
+ }
/* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
* somewhere in this domain, even before being added to the
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c
index ead85576d54a..e2d62d697b5d 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -17,6 +17,7 @@
#include <asm/e820.h>
#include <asm/setup.h>
#include <asm/acpi.h>
+#include <asm/numa.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
@@ -78,9 +79,16 @@ static void __init xen_add_extra_mem(u64 start, u64 size)
memblock_reserve(start, size);
xen_max_p2m_pfn = PFN_DOWN(start + size);
+ for (pfn = PFN_DOWN(start); pfn < xen_max_p2m_pfn; pfn++) {
+ unsigned long mfn = pfn_to_mfn(pfn);
+
+ if (WARN(mfn == pfn, "Trying to over-write 1-1 mapping (pfn: %lx)\n", pfn))
+ continue;
+ WARN(mfn != INVALID_P2M_ENTRY, "Trying to remove %lx which has %lx mfn!\n",
+ pfn, mfn);
- for (pfn = PFN_DOWN(start); pfn <= xen_max_p2m_pfn; pfn++)
__set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
+ }
}
static unsigned long __init xen_do_chunk(unsigned long start,
@@ -537,4 +545,7 @@ void __init xen_arch_setup(void)
disable_cpufreq();
WARN_ON(set_pm_idle_to_default());
fiddle_vdso();
+#ifdef CONFIG_NUMA
+ numa_off = 1;
+#endif
}
diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c
index ae8a00c39de4..45329c8c226e 100644
--- a/arch/x86/xen/suspend.c
+++ b/arch/x86/xen/suspend.c
@@ -30,7 +30,7 @@ void xen_arch_hvm_post_suspend(int suspend_cancelled)
{
#ifdef CONFIG_XEN_PVHVM
int cpu;
- xen_hvm_resume_shared_info();
+ xen_hvm_init_shared_info();
xen_callback_vector();
xen_unplug_emulated_devices();
if (xen_feature(XENFEAT_hvm_safe_pvclock)) {
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h
index 1e4329e04e0f..202d4c150154 100644
--- a/arch/x86/xen/xen-ops.h
+++ b/arch/x86/xen/xen-ops.h
@@ -41,7 +41,7 @@ void xen_enable_syscall(void);
void xen_vcpu_restore(void);
void xen_callback_vector(void);
-void xen_hvm_resume_shared_info(void);
+void xen_hvm_init_shared_info(void);
void xen_unplug_emulated_devices(void);
void __init xen_build_dynamic_phys_to_machine(void);
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 8a3f8351f438..8ed64cfae4ff 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -7,6 +7,7 @@ config ZONE_DMA
config XTENSA
def_bool y
select HAVE_IDE
+ select GENERIC_ATOMIC64
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_SHOW
select GENERIC_CPU_DEVICES
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index e7dee617358e..f3b44a65fc7a 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -31,27 +31,6 @@ EXPORT_SYMBOL_GPL(blkcg_root);
static struct blkcg_policy *blkcg_policy[BLKCG_MAX_POLS];
-struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup)
-{
- return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
- struct blkcg, css);
-}
-EXPORT_SYMBOL_GPL(cgroup_to_blkcg);
-
-static struct blkcg *task_blkcg(struct task_struct *tsk)
-{
- return container_of(task_subsys_state(tsk, blkio_subsys_id),
- struct blkcg, css);
-}
-
-struct blkcg *bio_blkcg(struct bio *bio)
-{
- if (bio && bio->bi_css)
- return container_of(bio->bi_css, struct blkcg, css);
- return task_blkcg(current);
-}
-EXPORT_SYMBOL_GPL(bio_blkcg);
-
static bool blkcg_policy_enabled(struct request_queue *q,
const struct blkcg_policy *pol)
{
@@ -84,6 +63,7 @@ static void blkg_free(struct blkcg_gq *blkg)
kfree(pd);
}
+ blk_exit_rl(&blkg->rl);
kfree(blkg);
}
@@ -91,16 +71,18 @@ static void blkg_free(struct blkcg_gq *blkg)
* blkg_alloc - allocate a blkg
* @blkcg: block cgroup the new blkg is associated with
* @q: request_queue the new blkg is associated with
+ * @gfp_mask: allocation mask to use
*
* Allocate a new blkg assocating @blkcg and @q.
*/
-static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
+static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q,
+ gfp_t gfp_mask)
{
struct blkcg_gq *blkg;
int i;
/* alloc and init base part */
- blkg = kzalloc_node(sizeof(*blkg), GFP_ATOMIC, q->node);
+ blkg = kzalloc_node(sizeof(*blkg), gfp_mask, q->node);
if (!blkg)
return NULL;
@@ -109,6 +91,13 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
blkg->blkcg = blkcg;
blkg->refcnt = 1;
+ /* root blkg uses @q->root_rl, init rl only for !root blkgs */
+ if (blkcg != &blkcg_root) {
+ if (blk_init_rl(&blkg->rl, q, gfp_mask))
+ goto err_free;
+ blkg->rl.blkg = blkg;
+ }
+
for (i = 0; i < BLKCG_MAX_POLS; i++) {
struct blkcg_policy *pol = blkcg_policy[i];
struct blkg_policy_data *pd;
@@ -117,11 +106,9 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
continue;
/* alloc per-policy data and attach it to blkg */
- pd = kzalloc_node(pol->pd_size, GFP_ATOMIC, q->node);
- if (!pd) {
- blkg_free(blkg);
- return NULL;
- }
+ pd = kzalloc_node(pol->pd_size, gfp_mask, q->node);
+ if (!pd)
+ goto err_free;
blkg->pd[i] = pd;
pd->blkg = blkg;
@@ -132,6 +119,10 @@ static struct blkcg_gq *blkg_alloc(struct blkcg *blkcg, struct request_queue *q)
}
return blkg;
+
+err_free:
+ blkg_free(blkg);
+ return NULL;
}
static struct blkcg_gq *__blkg_lookup(struct blkcg *blkcg,
@@ -175,9 +166,13 @@ struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q)
}
EXPORT_SYMBOL_GPL(blkg_lookup);
+/*
+ * If @new_blkg is %NULL, this function tries to allocate a new one as
+ * necessary using %GFP_ATOMIC. @new_blkg is always consumed on return.
+ */
static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
- struct request_queue *q)
- __releases(q->queue_lock) __acquires(q->queue_lock)
+ struct request_queue *q,
+ struct blkcg_gq *new_blkg)
{
struct blkcg_gq *blkg;
int ret;
@@ -189,24 +184,26 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
blkg = __blkg_lookup(blkcg, q);
if (blkg) {
rcu_assign_pointer(blkcg->blkg_hint, blkg);
- return blkg;
+ goto out_free;
}
/* blkg holds a reference to blkcg */
- if (!css_tryget(&blkcg->css))
- return ERR_PTR(-EINVAL);
+ if (!css_tryget(&blkcg->css)) {
+ blkg = ERR_PTR(-EINVAL);
+ goto out_free;
+ }
/* allocate */
- ret = -ENOMEM;
- blkg = blkg_alloc(blkcg, q);
- if (unlikely(!blkg))
- goto err_put;
+ if (!new_blkg) {
+ new_blkg = blkg_alloc(blkcg, q, GFP_ATOMIC);
+ if (unlikely(!new_blkg)) {
+ blkg = ERR_PTR(-ENOMEM);
+ goto out_put;
+ }
+ }
+ blkg = new_blkg;
/* insert */
- ret = radix_tree_preload(GFP_ATOMIC);
- if (ret)
- goto err_free;
-
spin_lock(&blkcg->lock);
ret = radix_tree_insert(&blkcg->blkg_tree, q->id, blkg);
if (likely(!ret)) {
@@ -215,15 +212,15 @@ static struct blkcg_gq *__blkg_lookup_create(struct blkcg *blkcg,
}
spin_unlock(&blkcg->lock);
- radix_tree_preload_end();
-
if (!ret)
return blkg;
-err_free:
- blkg_free(blkg);
-err_put:
+
+ blkg = ERR_PTR(ret);
+out_put:
css_put(&blkcg->css);
- return ERR_PTR(ret);
+out_free:
+ blkg_free(new_blkg);
+ return blkg;
}
struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
@@ -235,7 +232,7 @@ struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
*/
if (unlikely(blk_queue_bypass(q)))
return ERR_PTR(blk_queue_dead(q) ? -EINVAL : -EBUSY);
- return __blkg_lookup_create(blkcg, q);
+ return __blkg_lookup_create(blkcg, q, NULL);
}
EXPORT_SYMBOL_GPL(blkg_lookup_create);
@@ -313,6 +310,38 @@ void __blkg_release(struct blkcg_gq *blkg)
}
EXPORT_SYMBOL_GPL(__blkg_release);
+/*
+ * The next function used by blk_queue_for_each_rl(). It's a bit tricky
+ * because the root blkg uses @q->root_rl instead of its own rl.
+ */
+struct request_list *__blk_queue_next_rl(struct request_list *rl,
+ struct request_queue *q)
+{
+ struct list_head *ent;
+ struct blkcg_gq *blkg;
+
+ /*
+ * Determine the current blkg list_head. The first entry is
+ * root_rl which is off @q->blkg_list and mapped to the head.
+ */
+ if (rl == &q->root_rl) {
+ ent = &q->blkg_list;
+ } else {
+ blkg = container_of(rl, struct blkcg_gq, rl);
+ ent = &blkg->q_node;
+ }
+
+ /* walk to the next list_head, skip root blkcg */
+ ent = ent->next;
+ if (ent == &q->root_blkg->q_node)
+ ent = ent->next;
+ if (ent == &q->blkg_list)
+ return NULL;
+
+ blkg = container_of(ent, struct blkcg_gq, q_node);
+ return &blkg->rl;
+}
+
static int blkcg_reset_stats(struct cgroup *cgroup, struct cftype *cftype,
u64 val)
{
@@ -734,24 +763,36 @@ int blkcg_activate_policy(struct request_queue *q,
struct blkcg_gq *blkg;
struct blkg_policy_data *pd, *n;
int cnt = 0, ret;
+ bool preloaded;
if (blkcg_policy_enabled(q, pol))
return 0;
+ /* preallocations for root blkg */
+ blkg = blkg_alloc(&blkcg_root, q, GFP_KERNEL);
+ if (!blkg)
+ return -ENOMEM;
+
+ preloaded = !radix_tree_preload(GFP_KERNEL);
+
blk_queue_bypass_start(q);
/* make sure the root blkg exists and count the existing blkgs */
spin_lock_irq(q->queue_lock);
rcu_read_lock();
- blkg = __blkg_lookup_create(&blkcg_root, q);
+ blkg = __blkg_lookup_create(&blkcg_root, q, blkg);
rcu_read_unlock();
+ if (preloaded)
+ radix_tree_preload_end();
+
if (IS_ERR(blkg)) {
ret = PTR_ERR(blkg);
goto out_unlock;
}
q->root_blkg = blkg;
+ q->root_rl.blkg = blkg;
list_for_each_entry(blkg, &q->blkg_list, q_node)
cnt++;
diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h
index 8ac457ce7783..24597309e23d 100644
--- a/block/blk-cgroup.h
+++ b/block/blk-cgroup.h
@@ -17,6 +17,7 @@
#include <linux/u64_stats_sync.h>
#include <linux/seq_file.h>
#include <linux/radix-tree.h>
+#include <linux/blkdev.h>
/* Max limits for throttle policy */
#define THROTL_IOPS_MAX UINT_MAX
@@ -93,6 +94,8 @@ struct blkcg_gq {
struct list_head q_node;
struct hlist_node blkcg_node;
struct blkcg *blkcg;
+ /* request allocation list for this blkcg-q pair */
+ struct request_list rl;
/* reference count */
int refcnt;
@@ -120,8 +123,6 @@ struct blkcg_policy {
extern struct blkcg blkcg_root;
-struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup);
-struct blkcg *bio_blkcg(struct bio *bio);
struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, struct request_queue *q);
struct blkcg_gq *blkg_lookup_create(struct blkcg *blkcg,
struct request_queue *q);
@@ -160,6 +161,25 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
void blkg_conf_finish(struct blkg_conf_ctx *ctx);
+static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup)
+{
+ return container_of(cgroup_subsys_state(cgroup, blkio_subsys_id),
+ struct blkcg, css);
+}
+
+static inline struct blkcg *task_blkcg(struct task_struct *tsk)
+{
+ return container_of(task_subsys_state(tsk, blkio_subsys_id),
+ struct blkcg, css);
+}
+
+static inline struct blkcg *bio_blkcg(struct bio *bio)
+{
+ if (bio && bio->bi_css)
+ return container_of(bio->bi_css, struct blkcg, css);
+ return task_blkcg(current);
+}
+
/**
* blkg_to_pdata - get policy private data
* @blkg: blkg of interest
@@ -234,6 +254,95 @@ static inline void blkg_put(struct blkcg_gq *blkg)
}
/**
+ * blk_get_rl - get request_list to use
+ * @q: request_queue of interest
+ * @bio: bio which will be attached to the allocated request (may be %NULL)
+ *
+ * The caller wants to allocate a request from @q to use for @bio. Find
+ * the request_list to use and obtain a reference on it. Should be called
+ * under queue_lock. This function is guaranteed to return non-%NULL
+ * request_list.
+ */
+static inline struct request_list *blk_get_rl(struct request_queue *q,
+ struct bio *bio)
+{
+ struct blkcg *blkcg;
+ struct blkcg_gq *blkg;
+
+ rcu_read_lock();
+
+ blkcg = bio_blkcg(bio);
+
+ /* bypass blkg lookup and use @q->root_rl directly for root */
+ if (blkcg == &blkcg_root)
+ goto root_rl;
+
+ /*
+ * Try to use blkg->rl. blkg lookup may fail under memory pressure
+ * or if either the blkcg or queue is going away. Fall back to
+ * root_rl in such cases.
+ */
+ blkg = blkg_lookup_create(blkcg, q);
+ if (unlikely(IS_ERR(blkg)))
+ goto root_rl;
+
+ blkg_get(blkg);
+ rcu_read_unlock();
+ return &blkg->rl;
+root_rl:
+ rcu_read_unlock();
+ return &q->root_rl;
+}
+
+/**
+ * blk_put_rl - put request_list
+ * @rl: request_list to put
+ *
+ * Put the reference acquired by blk_get_rl(). Should be called under
+ * queue_lock.
+ */
+static inline void blk_put_rl(struct request_list *rl)
+{
+ /* root_rl may not have blkg set */
+ if (rl->blkg && rl->blkg->blkcg != &blkcg_root)
+ blkg_put(rl->blkg);
+}
+
+/**
+ * blk_rq_set_rl - associate a request with a request_list
+ * @rq: request of interest
+ * @rl: target request_list
+ *
+ * Associate @rq with @rl so that accounting and freeing can know the
+ * request_list @rq came from.
+ */
+static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl)
+{
+ rq->rl = rl;
+}
+
+/**
+ * blk_rq_rl - return the request_list a request came from
+ * @rq: request of interest
+ *
+ * Return the request_list @rq is allocated from.
+ */
+static inline struct request_list *blk_rq_rl(struct request *rq)
+{
+ return rq->rl;
+}
+
+struct request_list *__blk_queue_next_rl(struct request_list *rl,
+ struct request_queue *q);
+/**
+ * blk_queue_for_each_rl - iterate through all request_lists of a request_queue
+ *
+ * Should be used under queue_lock.
+ */
+#define blk_queue_for_each_rl(rl, q) \
+ for ((rl) = &(q)->root_rl; (rl); (rl) = __blk_queue_next_rl((rl), (q)))
+
+/**
* blkg_stat_add - add a value to a blkg_stat
* @stat: target blkg_stat
* @val: value to add
@@ -351,6 +460,7 @@ static inline void blkg_rwstat_reset(struct blkg_rwstat *rwstat)
#else /* CONFIG_BLK_CGROUP */
struct cgroup;
+struct blkcg;
struct blkg_policy_data {
};
@@ -361,8 +471,6 @@ struct blkcg_gq {
struct blkcg_policy {
};
-static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; }
-static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; }
static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; }
static inline int blkcg_init_queue(struct request_queue *q) { return 0; }
static inline void blkcg_drain_queue(struct request_queue *q) { }
@@ -374,6 +482,9 @@ static inline int blkcg_activate_policy(struct request_queue *q,
static inline void blkcg_deactivate_policy(struct request_queue *q,
const struct blkcg_policy *pol) { }
+static inline struct blkcg *cgroup_to_blkcg(struct cgroup *cgroup) { return NULL; }
+static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; }
+
static inline struct blkg_policy_data *blkg_to_pd(struct blkcg_gq *blkg,
struct blkcg_policy *pol) { return NULL; }
static inline struct blkcg_gq *pd_to_blkg(struct blkg_policy_data *pd) { return NULL; }
@@ -381,5 +492,14 @@ static inline char *blkg_path(struct blkcg_gq *blkg) { return NULL; }
static inline void blkg_get(struct blkcg_gq *blkg) { }
static inline void blkg_put(struct blkcg_gq *blkg) { }
+static inline struct request_list *blk_get_rl(struct request_queue *q,
+ struct bio *bio) { return &q->root_rl; }
+static inline void blk_put_rl(struct request_list *rl) { }
+static inline void blk_rq_set_rl(struct request *rq, struct request_list *rl) { }
+static inline struct request_list *blk_rq_rl(struct request *rq) { return &rq->q->root_rl; }
+
+#define blk_queue_for_each_rl(rl, q) \
+ for ((rl) = &(q)->root_rl; (rl); (rl) = NULL)
+
#endif /* CONFIG_BLK_CGROUP */
#endif /* _BLK_CGROUP_H */
diff --git a/block/blk-core.c b/block/blk-core.c
index 93eb3e4f88ce..ee3cb3a5e278 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -387,7 +387,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
if (!list_empty(&q->queue_head) && q->request_fn)
__blk_run_queue(q);
- drain |= q->rq.elvpriv;
+ drain |= q->nr_rqs_elvpriv;
/*
* Unfortunately, requests are queued at and tracked from
@@ -397,7 +397,7 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
if (drain_all) {
drain |= !list_empty(&q->queue_head);
for (i = 0; i < 2; i++) {
- drain |= q->rq.count[i];
+ drain |= q->nr_rqs[i];
drain |= q->in_flight[i];
drain |= !list_empty(&q->flush_queue[i]);
}
@@ -416,9 +416,14 @@ void blk_drain_queue(struct request_queue *q, bool drain_all)
* left with hung waiters. We need to wake up those waiters.
*/
if (q->request_fn) {
+ struct request_list *rl;
+
spin_lock_irq(q->queue_lock);
- for (i = 0; i < ARRAY_SIZE(q->rq.wait); i++)
- wake_up_all(&q->rq.wait[i]);
+
+ blk_queue_for_each_rl(rl, q)
+ for (i = 0; i < ARRAY_SIZE(rl->wait); i++)
+ wake_up_all(&rl->wait[i]);
+
spin_unlock_irq(q->queue_lock);
}
}
@@ -517,28 +522,33 @@ void blk_cleanup_queue(struct request_queue *q)
}
EXPORT_SYMBOL(blk_cleanup_queue);
-static int blk_init_free_list(struct request_queue *q)
+int blk_init_rl(struct request_list *rl, struct request_queue *q,
+ gfp_t gfp_mask)
{
- struct request_list *rl = &q->rq;
-
if (unlikely(rl->rq_pool))
return 0;
+ rl->q = q;
rl->count[BLK_RW_SYNC] = rl->count[BLK_RW_ASYNC] = 0;
rl->starved[BLK_RW_SYNC] = rl->starved[BLK_RW_ASYNC] = 0;
- rl->elvpriv = 0;
init_waitqueue_head(&rl->wait[BLK_RW_SYNC]);
init_waitqueue_head(&rl->wait[BLK_RW_ASYNC]);
rl->rq_pool = mempool_create_node(BLKDEV_MIN_RQ, mempool_alloc_slab,
- mempool_free_slab, request_cachep, q->node);
-
+ mempool_free_slab, request_cachep,
+ gfp_mask, q->node);
if (!rl->rq_pool)
return -ENOMEM;
return 0;
}
+void blk_exit_rl(struct request_list *rl)
+{
+ if (rl->rq_pool)
+ mempool_destroy(rl->rq_pool);
+}
+
struct request_queue *blk_alloc_queue(gfp_t gfp_mask)
{
return blk_alloc_queue_node(gfp_mask, -1);
@@ -680,7 +690,7 @@ blk_init_allocated_queue(struct request_queue *q, request_fn_proc *rfn,
if (!q)
return NULL;
- if (blk_init_free_list(q))
+ if (blk_init_rl(&q->root_rl, q, GFP_KERNEL))
return NULL;
q->request_fn = rfn;
@@ -722,15 +732,15 @@ bool blk_get_queue(struct request_queue *q)
}
EXPORT_SYMBOL(blk_get_queue);
-static inline void blk_free_request(struct request_queue *q, struct request *rq)
+static inline void blk_free_request(struct request_list *rl, struct request *rq)
{
if (rq->cmd_flags & REQ_ELVPRIV) {
- elv_put_request(q, rq);
+ elv_put_request(rl->q, rq);
if (rq->elv.icq)
put_io_context(rq->elv.icq->ioc);
}
- mempool_free(rq, q->rq.rq_pool);
+ mempool_free(rq, rl->rq_pool);
}
/*
@@ -767,18 +777,23 @@ static void ioc_set_batching(struct request_queue *q, struct io_context *ioc)
ioc->last_waited = jiffies;
}
-static void __freed_request(struct request_queue *q, int sync)
+static void __freed_request(struct request_list *rl, int sync)
{
- struct request_list *rl = &q->rq;
+ struct request_queue *q = rl->q;
- if (rl->count[sync] < queue_congestion_off_threshold(q))
+ /*
+ * bdi isn't aware of blkcg yet. As all async IOs end up root
+ * blkcg anyway, just use root blkcg state.
+ */
+ if (rl == &q->root_rl &&
+ rl->count[sync] < queue_congestion_off_threshold(q))
blk_clear_queue_congested(q, sync);
if (rl->count[sync] + 1 <= q->nr_requests) {
if (waitqueue_active(&rl->wait[sync]))
wake_up(&rl->wait[sync]);
- blk_clear_queue_full(q, sync);
+ blk_clear_rl_full(rl, sync);
}
}
@@ -786,19 +801,20 @@ static void __freed_request(struct request_queue *q, int sync)
* A request has just been released. Account for it, update the full and
* congestion status, wake up any waiters. Called under q->queue_lock.
*/
-static void freed_request(struct request_queue *q, unsigned int flags)
+static void freed_request(struct request_list *rl, unsigned int flags)
{
- struct request_list *rl = &q->rq;
+ struct request_queue *q = rl->q;
int sync = rw_is_sync(flags);
+ q->nr_rqs[sync]--;
rl->count[sync]--;
if (flags & REQ_ELVPRIV)
- rl->elvpriv--;
+ q->nr_rqs_elvpriv--;
- __freed_request(q, sync);
+ __freed_request(rl, sync);
if (unlikely(rl->starved[sync ^ 1]))
- __freed_request(q, sync ^ 1);
+ __freed_request(rl, sync ^ 1);
}
/*
@@ -837,8 +853,8 @@ static struct io_context *rq_ioc(struct bio *bio)
}
/**
- * get_request - get a free request
- * @q: request_queue to allocate request from
+ * __get_request - get a free request
+ * @rl: request list to allocate from
* @rw_flags: RW and SYNC flags
* @bio: bio to allocate request for (can be %NULL)
* @gfp_mask: allocation mask
@@ -850,20 +866,16 @@ static struct io_context *rq_ioc(struct bio *bio)
* Returns %NULL on failure, with @q->queue_lock held.
* Returns !%NULL on success, with @q->queue_lock *not held*.
*/
-static struct request *get_request(struct request_queue *q, int rw_flags,
- struct bio *bio, gfp_t gfp_mask)
+static struct request *__get_request(struct request_list *rl, int rw_flags,
+ struct bio *bio, gfp_t gfp_mask)
{
+ struct request_queue *q = rl->q;
struct request *rq;
- struct request_list *rl = &q->rq;
- struct elevator_type *et;
- struct io_context *ioc;
+ struct elevator_type *et = q->elevator->type;
+ struct io_context *ioc = rq_ioc(bio);
struct io_cq *icq = NULL;
const bool is_sync = rw_is_sync(rw_flags) != 0;
- bool retried = false;
int may_queue;
-retry:
- et = q->elevator->type;
- ioc = rq_ioc(bio);
if (unlikely(blk_queue_dead(q)))
return NULL;
@@ -875,28 +887,14 @@ retry:
if (rl->count[is_sync]+1 >= queue_congestion_on_threshold(q)) {
if (rl->count[is_sync]+1 >= q->nr_requests) {
/*
- * We want ioc to record batching state. If it's
- * not already there, creating a new one requires
- * dropping queue_lock, which in turn requires
- * retesting conditions to avoid queue hang.
- */
- if (!ioc && !retried) {
- spin_unlock_irq(q->queue_lock);
- create_io_context(gfp_mask, q->node);
- spin_lock_irq(q->queue_lock);
- retried = true;
- goto retry;
- }
-
- /*
* The queue will fill after this allocation, so set
* it as full, and mark this process as "batching".
* This process will be allowed to complete a batch of
* requests, others will be blocked.
*/
- if (!blk_queue_full(q, is_sync)) {
+ if (!blk_rl_full(rl, is_sync)) {
ioc_set_batching(q, ioc);
- blk_set_queue_full(q, is_sync);
+ blk_set_rl_full(rl, is_sync);
} else {
if (may_queue != ELV_MQUEUE_MUST
&& !ioc_batching(q, ioc)) {
@@ -909,7 +907,12 @@ retry:
}
}
}
- blk_set_queue_congested(q, is_sync);
+ /*
+ * bdi isn't aware of blkcg yet. As all async IOs end up
+ * root blkcg anyway, just use root blkcg state.
+ */
+ if (rl == &q->root_rl)
+ blk_set_queue_congested(q, is_sync);
}
/*
@@ -920,6 +923,7 @@ retry:
if (rl->count[is_sync] >= (3 * q->nr_requests / 2))
return NULL;
+ q->nr_rqs[is_sync]++;
rl->count[is_sync]++;
rl->starved[is_sync] = 0;
@@ -935,7 +939,7 @@ retry:
*/
if (blk_rq_should_init_elevator(bio) && !blk_queue_bypass(q)) {
rw_flags |= REQ_ELVPRIV;
- rl->elvpriv++;
+ q->nr_rqs_elvpriv++;
if (et->icq_cache && ioc)
icq = ioc_lookup_icq(ioc, q);
}
@@ -945,22 +949,19 @@ retry:
spin_unlock_irq(q->queue_lock);
/* allocate and init request */
- rq = mempool_alloc(q->rq.rq_pool, gfp_mask);
+ rq = mempool_alloc(rl->rq_pool, gfp_mask);
if (!rq)
goto fail_alloc;
blk_rq_init(q, rq);
+ blk_rq_set_rl(rq, rl);
rq->cmd_flags = rw_flags | REQ_ALLOCED;
/* init elvpriv */
if (rw_flags & REQ_ELVPRIV) {
if (unlikely(et->icq_cache && !icq)) {
- create_io_context(gfp_mask, q->node);
- ioc = rq_ioc(bio);
- if (!ioc)
- goto fail_elvpriv;
-
- icq = ioc_create_icq(ioc, q, gfp_mask);
+ if (ioc)
+ icq = ioc_create_icq(ioc, q, gfp_mask);
if (!icq)
goto fail_elvpriv;
}
@@ -1000,7 +1001,7 @@ fail_elvpriv:
rq->elv.icq = NULL;
spin_lock_irq(q->queue_lock);
- rl->elvpriv--;
+ q->nr_rqs_elvpriv--;
spin_unlock_irq(q->queue_lock);
goto out;
@@ -1013,7 +1014,7 @@ fail_alloc:
* queue, but this is pretty rare.
*/
spin_lock_irq(q->queue_lock);
- freed_request(q, rw_flags);
+ freed_request(rl, rw_flags);
/*
* in the very unlikely event that allocation failed and no
@@ -1029,56 +1030,58 @@ rq_starved:
}
/**
- * get_request_wait - get a free request with retry
+ * get_request - get a free request
* @q: request_queue to allocate request from
* @rw_flags: RW and SYNC flags
* @bio: bio to allocate request for (can be %NULL)
+ * @gfp_mask: allocation mask
*
- * Get a free request from @q. This function keeps retrying under memory
- * pressure and fails iff @q is dead.
+ * Get a free request from @q. If %__GFP_WAIT is set in @gfp_mask, this
+ * function keeps retrying under memory pressure and fails iff @q is dead.
*
* Must be callled with @q->queue_lock held and,
* Returns %NULL on failure, with @q->queue_lock held.
* Returns !%NULL on success, with @q->queue_lock *not held*.
*/
-static struct request *get_request_wait(struct request_queue *q, int rw_flags,
- struct bio *bio)
+static struct request *get_request(struct request_queue *q, int rw_flags,
+ struct bio *bio, gfp_t gfp_mask)
{
const bool is_sync = rw_is_sync(rw_flags) != 0;
+ DEFINE_WAIT(wait);
+ struct request_list *rl;
struct request *rq;
- rq = get_request(q, rw_flags, bio, GFP_NOIO);
- while (!rq) {
- DEFINE_WAIT(wait);
- struct request_list *rl = &q->rq;
-
- if (unlikely(blk_queue_dead(q)))
- return NULL;
+ rl = blk_get_rl(q, bio); /* transferred to @rq on success */
+retry:
+ rq = __get_request(rl, rw_flags, bio, gfp_mask);
+ if (rq)
+ return rq;
- prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
- TASK_UNINTERRUPTIBLE);
+ if (!(gfp_mask & __GFP_WAIT) || unlikely(blk_queue_dead(q))) {
+ blk_put_rl(rl);
+ return NULL;
+ }
- trace_block_sleeprq(q, bio, rw_flags & 1);
+ /* wait on @rl and retry */
+ prepare_to_wait_exclusive(&rl->wait[is_sync], &wait,
+ TASK_UNINTERRUPTIBLE);
- spin_unlock_irq(q->queue_lock);
- io_schedule();
+ trace_block_sleeprq(q, bio, rw_flags & 1);
- /*
- * After sleeping, we become a "batching" process and
- * will be able to allocate at least one request, and
- * up to a big batch of them for a small period time.
- * See ioc_batching, ioc_set_batching
- */
- create_io_context(GFP_NOIO, q->node);
- ioc_set_batching(q, current->io_context);
+ spin_unlock_irq(q->queue_lock);
+ io_schedule();
- spin_lock_irq(q->queue_lock);
- finish_wait(&rl->wait[is_sync], &wait);
+ /*
+ * After sleeping, we become a "batching" process and will be able
+ * to allocate at least one request, and up to a big batch of them
+ * for a small period time. See ioc_batching, ioc_set_batching
+ */
+ ioc_set_batching(q, current->io_context);
- rq = get_request(q, rw_flags, bio, GFP_NOIO);
- };
+ spin_lock_irq(q->queue_lock);
+ finish_wait(&rl->wait[is_sync], &wait);
- return rq;
+ goto retry;
}
struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
@@ -1087,11 +1090,11 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask)
BUG_ON(rw != READ && rw != WRITE);
+ /* create ioc upfront */
+ create_io_context(gfp_mask, q->node);
+
spin_lock_irq(q->queue_lock);
- if (gfp_mask & __GFP_WAIT)
- rq = get_request_wait(q, rw, NULL);
- else
- rq = get_request(q, rw, NULL, gfp_mask);
+ rq = get_request(q, rw, NULL, gfp_mask);
if (!rq)
spin_unlock_irq(q->queue_lock);
/* q->queue_lock is unlocked at this point */
@@ -1248,12 +1251,14 @@ void __blk_put_request(struct request_queue *q, struct request *req)
*/
if (req->cmd_flags & REQ_ALLOCED) {
unsigned int flags = req->cmd_flags;
+ struct request_list *rl = blk_rq_rl(req);
BUG_ON(!list_empty(&req->queuelist));
BUG_ON(!hlist_unhashed(&req->hash));
- blk_free_request(q, req);
- freed_request(q, flags);
+ blk_free_request(rl, req);
+ freed_request(rl, flags);
+ blk_put_rl(rl);
}
}
EXPORT_SYMBOL_GPL(__blk_put_request);
@@ -1481,7 +1486,7 @@ get_rq:
* Grab a free request. This is might sleep but can not fail.
* Returns with the queue unlocked.
*/
- req = get_request_wait(q, rw_flags, bio);
+ req = get_request(q, rw_flags, bio, GFP_NOIO);
if (unlikely(!req)) {
bio_endio(bio, -ENODEV); /* @q is dead */
goto out_unlock;
@@ -1702,6 +1707,14 @@ generic_make_request_checks(struct bio *bio)
goto end_io;
}
+ /*
+ * Various block parts want %current->io_context and lazy ioc
+ * allocation ends up trading a lot of pain for a small amount of
+ * memory. Just allocate it upfront. This may fail and block
+ * layer knows how to live with it.
+ */
+ create_io_context(GFP_ATOMIC, q->node);
+
if (blk_throtl_bio(q, bio))
return false; /* throttled, will be resubmitted later */
@@ -2241,9 +2254,11 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
error_type = "I/O";
break;
}
- printk(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
- error_type, req->rq_disk ? req->rq_disk->disk_name : "?",
- (unsigned long long)blk_rq_pos(req));
+ printk_ratelimited(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
+ error_type, req->rq_disk ?
+ req->rq_disk->disk_name : "?",
+ (unsigned long long)blk_rq_pos(req));
+
}
blk_account_io_completion(req, nr_bytes);
@@ -2896,23 +2911,47 @@ static void queue_unplugged(struct request_queue *q, unsigned int depth,
}
-static void flush_plug_callbacks(struct blk_plug *plug)
+static void flush_plug_callbacks(struct blk_plug *plug, bool from_schedule)
{
LIST_HEAD(callbacks);
- if (list_empty(&plug->cb_list))
- return;
+ while (!list_empty(&plug->cb_list)) {
+ list_splice_init(&plug->cb_list, &callbacks);
- list_splice_init(&plug->cb_list, &callbacks);
-
- while (!list_empty(&callbacks)) {
- struct blk_plug_cb *cb = list_first_entry(&callbacks,
+ while (!list_empty(&callbacks)) {
+ struct blk_plug_cb *cb = list_first_entry(&callbacks,
struct blk_plug_cb,
list);
- list_del(&cb->list);
- cb->callback(cb);
+ list_del(&cb->list);
+ cb->callback(cb, from_schedule);
+ }
+ }
+}
+
+struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug, void *data,
+ int size)
+{
+ struct blk_plug *plug = current->plug;
+ struct blk_plug_cb *cb;
+
+ if (!plug)
+ return NULL;
+
+ list_for_each_entry(cb, &plug->cb_list, list)
+ if (cb->callback == unplug && cb->data == data)
+ return cb;
+
+ /* Not currently on the callback list */
+ BUG_ON(size < sizeof(*cb));
+ cb = kzalloc(size, GFP_ATOMIC);
+ if (cb) {
+ cb->data = data;
+ cb->callback = unplug;
+ list_add(&cb->list, &plug->cb_list);
}
+ return cb;
}
+EXPORT_SYMBOL(blk_check_plugged);
void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
{
@@ -2924,7 +2963,7 @@ void blk_flush_plug_list(struct blk_plug *plug, bool from_schedule)
BUG_ON(plug->magic != PLUG_MAGIC);
- flush_plug_callbacks(plug);
+ flush_plug_callbacks(plug, from_schedule);
if (list_empty(&plug->list))
return;
diff --git a/block/blk-ioc.c b/block/blk-ioc.c
index 893b8007c657..fab4cdd3f7bb 100644
--- a/block/blk-ioc.c
+++ b/block/blk-ioc.c
@@ -244,6 +244,7 @@ int create_task_io_context(struct task_struct *task, gfp_t gfp_flags, int node)
/* initialize */
atomic_long_set(&ioc->refcount, 1);
+ atomic_set(&ioc->nr_tasks, 1);
atomic_set(&ioc->active_ref, 1);
spin_lock_init(&ioc->lock);
INIT_RADIX_TREE(&ioc->icq_tree, GFP_ATOMIC | __GFP_HIGH);
diff --git a/block/blk-lib.c b/block/blk-lib.c
index 2b461b496a78..19cc761cacb2 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -44,6 +44,7 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
struct request_queue *q = bdev_get_queue(bdev);
int type = REQ_WRITE | REQ_DISCARD;
unsigned int max_discard_sectors;
+ unsigned int granularity, alignment, mask;
struct bio_batch bb;
struct bio *bio;
int ret = 0;
@@ -54,18 +55,20 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
if (!blk_queue_discard(q))
return -EOPNOTSUPP;
+ /* Zero-sector (unknown) and one-sector granularities are the same. */
+ granularity = max(q->limits.discard_granularity >> 9, 1U);
+ mask = granularity - 1;
+ alignment = (bdev_discard_alignment(bdev) >> 9) & mask;
+
/*
* Ensure that max_discard_sectors is of the proper
- * granularity
+ * granularity, so that requests stay aligned after a split.
*/
max_discard_sectors = min(q->limits.max_discard_sectors, UINT_MAX >> 9);
+ max_discard_sectors = round_down(max_discard_sectors, granularity);
if (unlikely(!max_discard_sectors)) {
/* Avoid infinite loop below. Being cautious never hurts. */
return -EOPNOTSUPP;
- } else if (q->limits.discard_granularity) {
- unsigned int disc_sects = q->limits.discard_granularity >> 9;
-
- max_discard_sectors &= ~(disc_sects - 1);
}
if (flags & BLKDEV_DISCARD_SECURE) {
@@ -79,25 +82,37 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
bb.wait = &wait;
while (nr_sects) {
+ unsigned int req_sects;
+ sector_t end_sect;
+
bio = bio_alloc(gfp_mask, 1);
if (!bio) {
ret = -ENOMEM;
break;
}
+ req_sects = min_t(sector_t, nr_sects, max_discard_sectors);
+
+ /*
+ * If splitting a request, and the next starting sector would be
+ * misaligned, stop the discard at the previous aligned sector.
+ */
+ end_sect = sector + req_sects;
+ if (req_sects < nr_sects && (end_sect & mask) != alignment) {
+ end_sect =
+ round_down(end_sect - alignment, granularity)
+ + alignment;
+ req_sects = end_sect - sector;
+ }
+
bio->bi_sector = sector;
bio->bi_end_io = bio_batch_end_io;
bio->bi_bdev = bdev;
bio->bi_private = &bb;
- if (nr_sects > max_discard_sectors) {
- bio->bi_size = max_discard_sectors << 9;
- nr_sects -= max_discard_sectors;
- sector += max_discard_sectors;
- } else {
- bio->bi_size = nr_sects << 9;
- nr_sects = 0;
- }
+ bio->bi_size = req_sects << 9;
+ nr_sects -= req_sects;
+ sector = end_sect;
atomic_inc(&bb.done);
submit_bio(type, bio);
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 160035f54882..e76279e41162 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -110,6 +110,49 @@ static int blk_phys_contig_segment(struct request_queue *q, struct bio *bio,
return 0;
}
+static void
+__blk_segment_map_sg(struct request_queue *q, struct bio_vec *bvec,
+ struct scatterlist *sglist, struct bio_vec **bvprv,
+ struct scatterlist **sg, int *nsegs, int *cluster)
+{
+
+ int nbytes = bvec->bv_len;
+
+ if (*bvprv && *cluster) {
+ if ((*sg)->length + nbytes > queue_max_segment_size(q))
+ goto new_segment;
+
+ if (!BIOVEC_PHYS_MERGEABLE(*bvprv, bvec))
+ goto new_segment;
+ if (!BIOVEC_SEG_BOUNDARY(q, *bvprv, bvec))
+ goto new_segment;
+
+ (*sg)->length += nbytes;
+ } else {
+new_segment:
+ if (!*sg)
+ *sg = sglist;
+ else {
+ /*
+ * If the driver previously mapped a shorter
+ * list, we could see a termination bit
+ * prematurely unless it fully inits the sg
+ * table on each mapping. We KNOW that there
+ * must be more entries here or the driver
+ * would be buggy, so force clear the
+ * termination bit to avoid doing a full
+ * sg_init_table() in drivers for each command.
+ */
+ (*sg)->page_link &= ~0x02;
+ *sg = sg_next(*sg);
+ }
+
+ sg_set_page(*sg, bvec->bv_page, nbytes, bvec->bv_offset);
+ (*nsegs)++;
+ }
+ *bvprv = bvec;
+}
+
/*
* map a request to scatterlist, return number of sg entries setup. Caller
* must make sure sg can hold rq->nr_phys_segments entries
@@ -131,41 +174,8 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq,
bvprv = NULL;
sg = NULL;
rq_for_each_segment(bvec, rq, iter) {
- int nbytes = bvec->bv_len;
-
- if (bvprv && cluster) {
- if (sg->length + nbytes > queue_max_segment_size(q))
- goto new_segment;
-
- if (!BIOVEC_PHYS_MERGEABLE(bvprv, bvec))
- goto new_segment;
- if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bvec))
- goto new_segment;
-
- sg->length += nbytes;
- } else {
-new_segment:
- if (!sg)
- sg = sglist;
- else {
- /*
- * If the driver previously mapped a shorter
- * list, we could see a termination bit
- * prematurely unless it fully inits the sg
- * table on each mapping. We KNOW that there
- * must be more entries here or the driver
- * would be buggy, so force clear the
- * termination bit to avoid doing a full
- * sg_init_table() in drivers for each command.
- */
- sg->page_link &= ~0x02;
- sg = sg_next(sg);
- }
-
- sg_set_page(sg, bvec->bv_page, nbytes, bvec->bv_offset);
- nsegs++;
- }
- bvprv = bvec;
+ __blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg,
+ &nsegs, &cluster);
} /* segments in rq */
@@ -199,6 +209,43 @@ new_segment:
}
EXPORT_SYMBOL(blk_rq_map_sg);
+/**
+ * blk_bio_map_sg - map a bio to a scatterlist
+ * @q: request_queue in question
+ * @bio: bio being mapped
+ * @sglist: scatterlist being mapped
+ *
+ * Note:
+ * Caller must make sure sg can hold bio->bi_phys_segments entries
+ *
+ * Will return the number of sg entries setup
+ */
+int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
+ struct scatterlist *sglist)
+{
+ struct bio_vec *bvec, *bvprv;
+ struct scatterlist *sg;
+ int nsegs, cluster;
+ unsigned long i;
+
+ nsegs = 0;
+ cluster = blk_queue_cluster(q);
+
+ bvprv = NULL;
+ sg = NULL;
+ bio_for_each_segment(bvec, bio, i) {
+ __blk_segment_map_sg(q, bvec, sglist, &bvprv, &sg,
+ &nsegs, &cluster);
+ } /* segments in bio */
+
+ if (sg)
+ sg_mark_end(sg);
+
+ BUG_ON(bio->bi_phys_segments && nsegs > bio->bi_phys_segments);
+ return nsegs;
+}
+EXPORT_SYMBOL(blk_bio_map_sg);
+
static inline int ll_new_hw_segment(struct request_queue *q,
struct request *req,
struct bio *bio)
diff --git a/block/blk-settings.c b/block/blk-settings.c
index d3234fc494ad..565a6786032f 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -143,8 +143,7 @@ void blk_set_stacking_limits(struct queue_limits *lim)
lim->discard_zeroes_data = 1;
lim->max_segments = USHRT_MAX;
lim->max_hw_sectors = UINT_MAX;
-
- lim->max_sectors = BLK_DEF_MAX_SECTORS;
+ lim->max_sectors = UINT_MAX;
}
EXPORT_SYMBOL(blk_set_stacking_limits);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index aa41b47c22d2..9628b291f960 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -40,7 +40,7 @@ static ssize_t queue_requests_show(struct request_queue *q, char *page)
static ssize_t
queue_requests_store(struct request_queue *q, const char *page, size_t count)
{
- struct request_list *rl = &q->rq;
+ struct request_list *rl;
unsigned long nr;
int ret;
@@ -55,6 +55,9 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
q->nr_requests = nr;
blk_queue_congestion_threshold(q);
+ /* congestion isn't cgroup aware and follows root blkcg for now */
+ rl = &q->root_rl;
+
if (rl->count[BLK_RW_SYNC] >= queue_congestion_on_threshold(q))
blk_set_queue_congested(q, BLK_RW_SYNC);
else if (rl->count[BLK_RW_SYNC] < queue_congestion_off_threshold(q))
@@ -65,19 +68,22 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
else if (rl->count[BLK_RW_ASYNC] < queue_congestion_off_threshold(q))
blk_clear_queue_congested(q, BLK_RW_ASYNC);
- if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
- blk_set_queue_full(q, BLK_RW_SYNC);
- } else {
- blk_clear_queue_full(q, BLK_RW_SYNC);
- wake_up(&rl->wait[BLK_RW_SYNC]);
+ blk_queue_for_each_rl(rl, q) {
+ if (rl->count[BLK_RW_SYNC] >= q->nr_requests) {
+ blk_set_rl_full(rl, BLK_RW_SYNC);
+ } else {
+ blk_clear_rl_full(rl, BLK_RW_SYNC);
+ wake_up(&rl->wait[BLK_RW_SYNC]);
+ }
+
+ if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
+ blk_set_rl_full(rl, BLK_RW_ASYNC);
+ } else {
+ blk_clear_rl_full(rl, BLK_RW_ASYNC);
+ wake_up(&rl->wait[BLK_RW_ASYNC]);
+ }
}
- if (rl->count[BLK_RW_ASYNC] >= q->nr_requests) {
- blk_set_queue_full(q, BLK_RW_ASYNC);
- } else {
- blk_clear_queue_full(q, BLK_RW_ASYNC);
- wake_up(&rl->wait[BLK_RW_ASYNC]);
- }
spin_unlock_irq(q->queue_lock);
return ret;
}
@@ -476,7 +482,6 @@ static void blk_release_queue(struct kobject *kobj)
{
struct request_queue *q =
container_of(kobj, struct request_queue, kobj);
- struct request_list *rl = &q->rq;
blk_sync_queue(q);
@@ -489,8 +494,7 @@ static void blk_release_queue(struct kobject *kobj)
elevator_exit(q->elevator);
}
- if (rl->rq_pool)
- mempool_destroy(rl->rq_pool);
+ blk_exit_rl(&q->root_rl);
if (q->queue_tags)
__blk_queue_free_tags(q);
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 5b0659512047..e287c19908c8 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -1123,9 +1123,6 @@ bool blk_throtl_bio(struct request_queue *q, struct bio *bio)
goto out;
}
- /* bio_associate_current() needs ioc, try creating */
- create_io_context(GFP_ATOMIC, q->node);
-
/*
* A throtl_grp pointer retrieved under rcu can be used to access
* basic fields like stats and io rates. If a group has no rules,
diff --git a/block/blk.h b/block/blk.h
index 85f6ae42f7d3..2a0ea32d249f 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -18,6 +18,9 @@ static inline void __blk_get_queue(struct request_queue *q)
kobject_get(&q->kobj);
}
+int blk_init_rl(struct request_list *rl, struct request_queue *q,
+ gfp_t gfp_mask);
+void blk_exit_rl(struct request_list *rl);
void init_request_from_bio(struct request *req, struct bio *bio);
void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
struct bio *bio);
@@ -33,7 +36,6 @@ bool __blk_end_bidi_request(struct request *rq, int error,
void blk_rq_timed_out_timer(unsigned long data);
void blk_delete_timer(struct request *);
void blk_add_timer(struct request *);
-void __generic_unplug_device(struct request_queue *);
/*
* Internal atomic flags for request handling
diff --git a/block/bsg-lib.c b/block/bsg-lib.c
index 7ad49c88f6b1..deee61fbb741 100644
--- a/block/bsg-lib.c
+++ b/block/bsg-lib.c
@@ -243,56 +243,3 @@ int bsg_setup_queue(struct device *dev, struct request_queue *q,
return 0;
}
EXPORT_SYMBOL_GPL(bsg_setup_queue);
-
-/**
- * bsg_remove_queue - Deletes the bsg dev from the q
- * @q: the request_queue that is to be torn down.
- *
- * Notes:
- * Before unregistering the queue empty any requests that are blocked
- */
-void bsg_remove_queue(struct request_queue *q)
-{
- struct request *req; /* block request */
- int counts; /* totals for request_list count and starved */
-
- if (!q)
- return;
-
- /* Stop taking in new requests */
- spin_lock_irq(q->queue_lock);
- blk_stop_queue(q);
-
- /* drain all requests in the queue */
- while (1) {
- /* need the lock to fetch a request
- * this may fetch the same reqeust as the previous pass
- */
- req = blk_fetch_request(q);
- /* save requests in use and starved */
- counts = q->rq.count[0] + q->rq.count[1] +
- q->rq.starved[0] + q->rq.starved[1];
- spin_unlock_irq(q->queue_lock);
- /* any requests still outstanding? */
- if (counts == 0)
- break;
-
- /* This may be the same req as the previous iteration,
- * always send the blk_end_request_all after a prefetch.
- * It is not okay to not end the request because the
- * prefetch started the request.
- */
- if (req) {
- /* return -ENXIO to indicate that this queue is
- * going away
- */
- req->errors = -ENXIO;
- blk_end_request_all(req, -ENXIO);
- }
-
- msleep(200); /* allow bsg to possibly finish */
- spin_lock_irq(q->queue_lock);
- }
- bsg_unregister_queue(q);
-}
-EXPORT_SYMBOL_GPL(bsg_remove_queue);
diff --git a/block/genhd.c b/block/genhd.c
index 9cf5583c90ff..d839723303c8 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -154,7 +154,7 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter)
part = rcu_dereference(ptbl->part[piter->idx]);
if (!part)
continue;
- if (!part->nr_sects &&
+ if (!part_nr_sects_read(part) &&
!(piter->flags & DISK_PITER_INCL_EMPTY) &&
!(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
piter->idx == 0))
@@ -191,7 +191,7 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit);
static inline int sector_in_part(struct hd_struct *part, sector_t sector)
{
return part->start_sect <= sector &&
- sector < part->start_sect + part->nr_sects;
+ sector < part->start_sect + part_nr_sects_read(part);
}
/**
@@ -769,8 +769,8 @@ void __init printk_all_partitions(void)
printk("%s%s %10llu %s %s", is_part0 ? "" : " ",
bdevt_str(part_devt(part), devt_buf),
- (unsigned long long)part->nr_sects >> 1,
- disk_name(disk, part->partno, name_buf),
+ (unsigned long long)part_nr_sects_read(part) >> 1
+ , disk_name(disk, part->partno, name_buf),
uuid_buf);
if (is_part0) {
if (disk->driverfs_dev != NULL &&
@@ -835,7 +835,7 @@ static void disk_seqf_stop(struct seq_file *seqf, void *v)
static void *show_partition_start(struct seq_file *seqf, loff_t *pos)
{
- static void *p;
+ void *p;
p = disk_seqf_start(seqf, pos);
if (!IS_ERR_OR_NULL(p) && !*pos)
@@ -862,7 +862,7 @@ static int show_partition(struct seq_file *seqf, void *v)
while ((part = disk_part_iter_next(&piter)))
seq_printf(seqf, "%4d %7d %10llu %s\n",
MAJOR(part_devt(part)), MINOR(part_devt(part)),
- (unsigned long long)part->nr_sects >> 1,
+ (unsigned long long)part_nr_sects_read(part) >> 1,
disk_name(sgp, part->partno, buf));
disk_part_iter_exit(&piter);
@@ -1268,6 +1268,16 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
}
disk->part_tbl->part[0] = &disk->part0;
+ /*
+ * set_capacity() and get_capacity() currently don't use
+ * seqcounter to read/update the part0->nr_sects. Still init
+ * the counter as we can read the sectors in IO submission
+ * patch using seqence counters.
+ *
+ * TODO: Ideally set_capacity() and get_capacity() should be
+ * converted to make use of bd_mutex and sequence counters.
+ */
+ seqcount_init(&disk->part0.nr_sects_seq);
hd_ref_init(&disk->part0);
disk->minors = minors;
diff --git a/block/ioctl.c b/block/ioctl.c
index ba15b2dbfb98..4a85096f5410 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -13,7 +13,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
{
struct block_device *bdevp;
struct gendisk *disk;
- struct hd_struct *part;
+ struct hd_struct *part, *lpart;
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
struct disk_part_iter piter;
@@ -36,12 +36,12 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
case BLKPG_ADD_PARTITION:
start = p.start >> 9;
length = p.length >> 9;
- /* check for fit in a hd_struct */
- if (sizeof(sector_t) == sizeof(long) &&
+ /* check for fit in a hd_struct */
+ if (sizeof(sector_t) == sizeof(long) &&
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
- || pstart < 0 || plength < 0)
+ || pstart < 0 || plength < 0 || partno > 65535)
return -EINVAL;
}
@@ -92,6 +92,59 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
bdput(bdevp);
return 0;
+ case BLKPG_RESIZE_PARTITION:
+ start = p.start >> 9;
+ /* new length of partition in bytes */
+ length = p.length >> 9;
+ /* check for fit in a hd_struct */
+ if (sizeof(sector_t) == sizeof(long) &&
+ sizeof(long long) > sizeof(long)) {
+ long pstart = start, plength = length;
+ if (pstart != start || plength != length
+ || pstart < 0 || plength < 0)
+ return -EINVAL;
+ }
+ part = disk_get_part(disk, partno);
+ if (!part)
+ return -ENXIO;
+ bdevp = bdget(part_devt(part));
+ if (!bdevp) {
+ disk_put_part(part);
+ return -ENOMEM;
+ }
+ mutex_lock(&bdevp->bd_mutex);
+ mutex_lock_nested(&bdev->bd_mutex, 1);
+ if (start != part->start_sect) {
+ mutex_unlock(&bdevp->bd_mutex);
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdevp);
+ disk_put_part(part);
+ return -EINVAL;
+ }
+ /* overlap? */
+ disk_part_iter_init(&piter, disk,
+ DISK_PITER_INCL_EMPTY);
+ while ((lpart = disk_part_iter_next(&piter))) {
+ if (lpart->partno != partno &&
+ !(start + length <= lpart->start_sect ||
+ start >= lpart->start_sect + lpart->nr_sects)
+ ) {
+ disk_part_iter_exit(&piter);
+ mutex_unlock(&bdevp->bd_mutex);
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdevp);
+ disk_put_part(part);
+ return -EBUSY;
+ }
+ }
+ disk_part_iter_exit(&piter);
+ part_nr_sects_write(part, (sector_t)length);
+ i_size_write(bdevp->bd_inode, p.length);
+ mutex_unlock(&bdevp->bd_mutex);
+ mutex_unlock(&bdev->bd_mutex);
+ bdput(bdevp);
+ disk_put_part(part);
+ return 0;
default:
return -EINVAL;
}
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 6df5d6928a44..f1d14519cc04 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -84,7 +84,7 @@ ssize_t part_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hd_struct *p = dev_to_part(dev);
- return sprintf(buf, "%llu\n",(unsigned long long)p->nr_sects);
+ return sprintf(buf, "%llu\n",(unsigned long long)part_nr_sects_read(p));
}
static ssize_t part_ro_show(struct device *dev,
@@ -294,6 +294,8 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno,
err = -ENOMEM;
goto out_free;
}
+
+ seqcount_init(&p->nr_sects_seq);
pdev = part_to_dev(p);
p->start_sect = start;
diff --git a/crypto/authenc.c b/crypto/authenc.c
index 5ef7ba6b6a76..d0583a4489e6 100644
--- a/crypto/authenc.c
+++ b/crypto/authenc.c
@@ -336,7 +336,7 @@ static int crypto_authenc_genicv(struct aead_request *req, u8 *iv,
cryptlen += ivsize;
}
- if (sg_is_last(assoc)) {
+ if (req->assoclen && sg_is_last(assoc)) {
authenc_ahash_fn = crypto_authenc_ahash;
sg_init_table(asg, 2);
sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
@@ -490,7 +490,7 @@ static int crypto_authenc_iverify(struct aead_request *req, u8 *iv,
cryptlen += ivsize;
}
- if (sg_is_last(assoc)) {
+ if (req->assoclen && sg_is_last(assoc)) {
authenc_ahash_fn = crypto_authenc_ahash;
sg_init_table(asg, 2);
sg_set_page(asg, sg_page(assoc), assoc->length, assoc->offset);
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 805c432c9439..ece958d3762e 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -112,6 +112,8 @@ source "drivers/auxdisplay/Kconfig"
source "drivers/uio/Kconfig"
+source "drivers/vfio/Kconfig"
+
source "drivers/vlynq/Kconfig"
source "drivers/virtio/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index bd36f09f2246..5b421840c48d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_FUSION) += message/
obj-y += firewire/
obj-$(CONFIG_UIO) += uio/
+obj-$(CONFIG_VFIO) += vfio/
obj-y += cdrom/
obj-y += auxdisplay/
obj-$(CONFIG_PCCARD) += pcmcia/
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index ac7034129f3f..d5fdd36190cc 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -69,7 +69,9 @@ static const struct acpi_device_id ac_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, ac_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_ac_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
static struct acpi_driver acpi_ac_driver = {
@@ -313,6 +315,7 @@ static int acpi_ac_add(struct acpi_device *device)
return result;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_ac_resume(struct device *dev)
{
struct acpi_ac *ac;
@@ -332,6 +335,7 @@ static int acpi_ac_resume(struct device *dev)
kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
return 0;
}
+#endif
static int acpi_ac_remove(struct acpi_device *device, int type)
{
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 5ccb99ae3a6f..5de4ec72766d 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -83,22 +83,22 @@ acpi_status acpi_hw_clear_acpi_status(void);
/*
* hwsleep - sleep/wake support (Legacy sleep registers)
*/
-acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state);
-acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state);
-acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_wake(u8 sleep_state);
/*
* hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
*/
void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument);
-acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_sleep(u8 sleep_state);
-acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state);
-acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_wake(u8 sleep_state);
/*
* hwvalid - Port I/O with validation
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
index 48518dac5342..94996f9ae3ad 100644
--- a/drivers/acpi/acpica/hwesleep.c
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -90,7 +90,6 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument)
* FUNCTION: acpi_hw_extended_sleep
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -100,7 +99,7 @@ void acpi_hw_execute_sleep_method(char *method_pathname, u32 integer_argument)
*
******************************************************************************/
-acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_sleep(u8 sleep_state)
{
acpi_status status;
u8 sleep_type_value;
@@ -125,12 +124,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
acpi_gbl_system_awake_and_running = FALSE;
- /* Optionally execute _GTS (Going To Sleep) */
-
- if (flags & ACPI_EXECUTE_GTS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
- }
-
/* Flush caches, as per ACPI specification */
ACPI_FLUSH_CPU_CACHE();
@@ -172,7 +165,6 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
* FUNCTION: acpi_hw_extended_wake_prep
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - ACPI_EXECUTE_BFS to run optional method
*
* RETURN: Status
*
@@ -181,7 +173,7 @@ acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
*
******************************************************************************/
-acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state)
{
acpi_status status;
u8 sleep_type_value;
@@ -200,11 +192,6 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
&acpi_gbl_FADT.sleep_control);
}
- /* Optionally execute _BFS (Back From Sleep) */
-
- if (flags & ACPI_EXECUTE_BFS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
- }
return_ACPI_STATUS(AE_OK);
}
@@ -222,7 +209,7 @@ acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
*
******************************************************************************/
-acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_wake(u8 sleep_state)
{
ACPI_FUNCTION_TRACE(hw_extended_wake);
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index 9960fe9ef533..3fddde056a5e 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -56,7 +56,6 @@ ACPI_MODULE_NAME("hwsleep")
* FUNCTION: acpi_hw_legacy_sleep
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -64,7 +63,7 @@ ACPI_MODULE_NAME("hwsleep")
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
-acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
{
struct acpi_bit_register_info *sleep_type_reg_info;
struct acpi_bit_register_info *sleep_enable_reg_info;
@@ -110,12 +109,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
return_ACPI_STATUS(status);
}
- /* Optionally execute _GTS (Going To Sleep) */
-
- if (flags & ACPI_EXECUTE_GTS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
- }
-
/* Get current value of PM1A control */
status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
@@ -214,7 +207,6 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
* FUNCTION: acpi_hw_legacy_wake_prep
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - ACPI_EXECUTE_BFS to run optional method
*
* RETURN: Status
*
@@ -224,7 +216,7 @@ acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
*
******************************************************************************/
-acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state)
{
acpi_status status;
struct acpi_bit_register_info *sleep_type_reg_info;
@@ -275,11 +267,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
}
}
- /* Optionally execute _BFS (Back From Sleep) */
-
- if (flags & ACPI_EXECUTE_BFS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
- }
return_ACPI_STATUS(status);
}
@@ -288,7 +275,6 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
* FUNCTION: acpi_hw_legacy_wake
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - Reserved, set to zero
*
* RETURN: Status
*
@@ -297,7 +283,7 @@ acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
*
******************************************************************************/
-acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_wake(u8 sleep_state)
{
acpi_status status;
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
index f8684bfe7907..1f165a750ae2 100644
--- a/drivers/acpi/acpica/hwxfsleep.c
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -50,7 +50,7 @@ ACPI_MODULE_NAME("hwxfsleep")
/* Local prototypes */
static acpi_status
-acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id);
+acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id);
/*
* Dispatch table used to efficiently branch to the various sleep
@@ -235,7 +235,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios)
*
******************************************************************************/
static acpi_status
-acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
+acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id)
{
acpi_status status;
struct acpi_sleep_functions *sleep_functions =
@@ -248,11 +248,11 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
* use the extended sleep registers
*/
if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) {
- status = sleep_functions->extended_function(sleep_state, flags);
+ status = sleep_functions->extended_function(sleep_state);
} else {
/* Legacy sleep */
- status = sleep_functions->legacy_function(sleep_state, flags);
+ status = sleep_functions->legacy_function(sleep_state);
}
return (status);
@@ -262,7 +262,7 @@ acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
* For the case where reduced-hardware-only code is being generated,
* we know that only the extended sleep registers are available
*/
- status = sleep_functions->extended_function(sleep_state, flags);
+ status = sleep_functions->extended_function(sleep_state);
return (status);
#endif /* !ACPI_REDUCED_HARDWARE */
@@ -349,7 +349,6 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
* FUNCTION: acpi_enter_sleep_state
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -357,7 +356,7 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags)
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
{
acpi_status status;
@@ -371,7 +370,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags)
}
status =
- acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID);
+ acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID);
return_ACPI_STATUS(status);
}
@@ -391,14 +390,14 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state)
* Called with interrupts DISABLED.
*
******************************************************************************/
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
status =
- acpi_hw_sleep_dispatch(sleep_state, flags,
+ acpi_hw_sleep_dispatch(sleep_state,
ACPI_WAKE_PREP_FUNCTION_ID);
return_ACPI_STATUS(status);
}
@@ -423,8 +422,7 @@ acpi_status acpi_leave_sleep_state(u8 sleep_state)
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
-
- status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID);
+ status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index ea4c6d52605a..29e51bc01383 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -387,6 +387,7 @@ acpi_get_table_with_size(char *signature,
return (AE_NOT_FOUND);
}
+ACPI_EXPORT_SYMBOL(acpi_get_table_with_size)
acpi_status
acpi_get_table(char *signature,
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index ff2c876ec412..45e3e1759fb8 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -1052,6 +1052,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
/* this is needed to learn about changes made in suspended state */
static int acpi_battery_resume(struct device *dev)
{
@@ -1068,6 +1069,7 @@ static int acpi_battery_resume(struct device *dev)
acpi_battery_update(battery);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 9628652e080c..e0596954290b 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -237,6 +237,16 @@ static int __acpi_bus_get_power(struct acpi_device *device, int *state)
} else if (result == ACPI_STATE_D3_HOT) {
result = ACPI_STATE_D3;
}
+
+ /*
+ * If we were unsure about the device parent's power state up to this
+ * point, the fact that the device is in D0 implies that the parent has
+ * to be in D0 too.
+ */
+ if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN
+ && result == ACPI_STATE_D0)
+ device->parent->power.state = ACPI_STATE_D0;
+
*state = result;
out:
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 79d4c22f7a6d..314a3b84bbc7 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -78,7 +78,9 @@ static int acpi_button_add(struct acpi_device *device);
static int acpi_button_remove(struct acpi_device *device, int type);
static void acpi_button_notify(struct acpi_device *device, u32 event);
+#ifdef CONFIG_PM_SLEEP
static int acpi_button_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_button_pm, NULL, acpi_button_resume);
static struct acpi_driver acpi_button_driver = {
@@ -310,6 +312,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
}
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_button_resume(struct device *dev)
{
struct acpi_device *device = to_acpi_device(dev);
@@ -319,6 +322,7 @@ static int acpi_button_resume(struct device *dev)
return acpi_lid_send_state(device);
return 0;
}
+#endif
static int acpi_button_add(struct acpi_device *device)
{
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 669d9ee80d16..bc36a476f1ab 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -53,8 +53,10 @@ static const struct acpi_device_id fan_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, fan_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_fan_suspend(struct device *dev);
static int acpi_fan_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume);
static struct acpi_driver acpi_fan_driver = {
@@ -184,6 +186,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_fan_suspend(struct device *dev)
{
if (!dev)
@@ -207,6 +210,7 @@ static int acpi_fan_resume(struct device *dev)
return result;
}
+#endif
static int __init acpi_fan_init(void)
{
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index e56f3be7b07d..cb31298ca684 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -237,6 +237,8 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header,
return 0;
}
+static int __initdata parsed_numa_memblks;
+
static int __init
acpi_parse_memory_affinity(struct acpi_subtable_header * header,
const unsigned long end)
@@ -250,8 +252,8 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header,
acpi_table_print_srat_entry(header);
/* let architecture-dependent part to do it */
- acpi_numa_memory_affinity_init(memory_affinity);
-
+ if (!acpi_numa_memory_affinity_init(memory_affinity))
+ parsed_numa_memblks++;
return 0;
}
@@ -304,8 +306,10 @@ int __init acpi_numa_init(void)
acpi_numa_arch_fixup();
- if (cnt <= 0)
- return cnt ?: -ENOENT;
+ if (cnt < 0)
+ return cnt;
+ else if (!parsed_numa_memblks)
+ return -ENOENT;
return 0;
}
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ec54014c321c..72a2c98bc429 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -573,8 +573,15 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
OSC_CLOCK_PWR_CAPABILITY_SUPPORT;
if (pci_msi_enabled())
flags |= OSC_MSI_SUPPORT;
- if (flags != base_flags)
- acpi_pci_osc_support(root, flags);
+ if (flags != base_flags) {
+ status = acpi_pci_osc_support(root, flags);
+ if (ACPI_FAILURE(status)) {
+ dev_info(root->bus->bridge, "ACPI _OSC support "
+ "notification failed, disabling PCIe ASPM\n");
+ pcie_no_aspm();
+ flags = base_flags;
+ }
+ }
if (!pcie_ports_disabled
&& (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) {
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 215ecd097408..40e38a06ba85 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -67,7 +67,9 @@ static const struct acpi_device_id power_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, power_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_power_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_power_pm, NULL, acpi_power_resume);
static struct acpi_driver acpi_power_driver = {
@@ -105,6 +107,7 @@ struct acpi_power_resource {
/* List of devices relying on this power resource */
struct acpi_power_resource_device *devices;
+ struct mutex devices_lock;
};
static struct list_head acpi_power_resource_list;
@@ -223,7 +226,6 @@ static void acpi_power_on_device(struct acpi_power_managed_device *device)
static int __acpi_power_on(struct acpi_power_resource *resource)
{
- struct acpi_power_resource_device *device_list = resource->devices;
acpi_status status = AE_OK;
status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
@@ -236,19 +238,15 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
resource->name));
- while (device_list) {
- acpi_power_on_device(device_list->device);
-
- device_list = device_list->next;
- }
-
return 0;
}
static int acpi_power_on(acpi_handle handle)
{
int result = 0;
+ bool resume_device = false;
struct acpi_power_resource *resource = NULL;
+ struct acpi_power_resource_device *device_list;
result = acpi_power_get_context(handle, &resource);
if (result)
@@ -264,10 +262,25 @@ static int acpi_power_on(acpi_handle handle)
result = __acpi_power_on(resource);
if (result)
resource->ref_count--;
+ else
+ resume_device = true;
}
mutex_unlock(&resource->resource_lock);
+ if (!resume_device)
+ return result;
+
+ mutex_lock(&resource->devices_lock);
+
+ device_list = resource->devices;
+ while (device_list) {
+ acpi_power_on_device(device_list->device);
+ device_list = device_list->next;
+ }
+
+ mutex_unlock(&resource->devices_lock);
+
return result;
}
@@ -353,7 +366,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev,
if (acpi_power_get_context(res_handle, &resource))
return;
- mutex_lock(&resource->resource_lock);
+ mutex_lock(&resource->devices_lock);
prev = NULL;
curr = resource->devices;
while (curr) {
@@ -370,7 +383,7 @@ static void __acpi_power_resource_unregister_device(struct device *dev,
prev = curr;
curr = curr->next;
}
- mutex_unlock(&resource->resource_lock);
+ mutex_unlock(&resource->devices_lock);
}
/* Unlink dev from all power resources in _PR0 */
@@ -412,10 +425,10 @@ static int __acpi_power_resource_register_device(
power_resource_device->device = powered_device;
- mutex_lock(&resource->resource_lock);
+ mutex_lock(&resource->devices_lock);
power_resource_device->next = resource->devices;
resource->devices = power_resource_device;
- mutex_unlock(&resource->resource_lock);
+ mutex_unlock(&resource->devices_lock);
return 0;
}
@@ -460,7 +473,7 @@ int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
return ret;
no_power_resource:
- printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
+ printk(KERN_DEBUG PREFIX "Invalid Power Resource to register!");
return -ENODEV;
}
EXPORT_SYMBOL_GPL(acpi_power_resource_register_device);
@@ -719,6 +732,7 @@ static int acpi_power_add(struct acpi_device *device)
resource->device = device;
mutex_init(&resource->resource_lock);
+ mutex_init(&resource->devices_lock);
strcpy(resource->name, device->pnp.bus_id);
strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
@@ -775,6 +789,7 @@ static int acpi_power_remove(struct acpi_device *device, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_power_resume(struct device *dev)
{
int result = 0, state;
@@ -803,6 +818,7 @@ static int acpi_power_resume(struct device *dev)
return result;
}
+#endif
int __init acpi_power_init(void)
{
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index ff8e04f2fab4..bfc31cb0dd3e 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -437,7 +437,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
/* Normal CPU soft online event */
} else {
acpi_processor_ppc_has_changed(pr, 0);
- acpi_processor_cst_has_changed(pr);
+ acpi_processor_hotplug(pr);
acpi_processor_reevaluate_tstate(pr, action);
acpi_processor_tstate_has_changed(pr);
}
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index c0b9aa5faf4c..ff0740e0a9c2 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -988,6 +988,7 @@ static void acpi_sbs_rmdirs(void)
#endif
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_sbs_resume(struct device *dev)
{
struct acpi_sbs *sbs;
@@ -997,6 +998,7 @@ static int acpi_sbs_resume(struct device *dev)
acpi_sbs_callback(sbs);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 7a7a9c929247..fdcdbb652915 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -28,36 +28,7 @@
#include "internal.h"
#include "sleep.h"
-u8 wake_sleep_flags = ACPI_NO_OPTIONAL_METHODS;
-static unsigned int gts, bfs;
-static int set_param_wake_flag(const char *val, struct kernel_param *kp)
-{
- int ret = param_set_int(val, kp);
-
- if (ret)
- return ret;
-
- if (kp->arg == (const char *)&gts) {
- if (gts)
- wake_sleep_flags |= ACPI_EXECUTE_GTS;
- else
- wake_sleep_flags &= ~ACPI_EXECUTE_GTS;
- }
- if (kp->arg == (const char *)&bfs) {
- if (bfs)
- wake_sleep_flags |= ACPI_EXECUTE_BFS;
- else
- wake_sleep_flags &= ~ACPI_EXECUTE_BFS;
- }
- return ret;
-}
-module_param_call(gts, set_param_wake_flag, param_get_int, &gts, 0644);
-module_param_call(bfs, set_param_wake_flag, param_get_int, &bfs, 0644);
-MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
-MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
-
static u8 sleep_states[ACPI_S_STATE_COUNT];
-static bool pwr_btn_event_pending;
static void acpi_sleep_tts_switch(u32 acpi_state)
{
@@ -110,6 +81,7 @@ static int acpi_sleep_prepare(u32 acpi_state)
#ifdef CONFIG_ACPI_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+static bool pwr_btn_event_pending;
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
@@ -305,7 +277,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
switch (acpi_state) {
case ACPI_STATE_S1:
barrier();
- status = acpi_enter_sleep_state(acpi_state, wake_sleep_flags);
+ status = acpi_enter_sleep_state(acpi_state);
break;
case ACPI_STATE_S3:
@@ -319,8 +291,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
/* This violates the spec but is required for bug compatibility. */
acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(acpi_state, wake_sleep_flags);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(acpi_state);
/* ACPI 3.0 specs (P62) says that it's the responsibility
* of the OSPM to clear the status bit [ implying that the
@@ -603,9 +575,9 @@ static int acpi_hibernation_enter(void)
ACPI_FLUSH_CPU_CACHE();
/* This shouldn't return. If it returns, we have a problem */
- status = acpi_enter_sleep_state(ACPI_STATE_S4, wake_sleep_flags);
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags);
+ status = acpi_enter_sleep_state(ACPI_STATE_S4);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
@@ -617,8 +589,8 @@ static void acpi_hibernation_leave(void)
* enable it here.
*/
acpi_enable();
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
/* Check the hardware signature */
if (facs && s4_hardware_signature != facs->hardware_signature) {
printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
@@ -892,33 +864,7 @@ static void acpi_power_off(void)
/* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
printk(KERN_DEBUG "%s called\n", __func__);
local_irq_disable();
- acpi_enter_sleep_state(ACPI_STATE_S5, wake_sleep_flags);
-}
-
-/*
- * ACPI 2.0 created the optional _GTS and _BFS,
- * but industry adoption has been neither rapid nor broad.
- *
- * Linux gets into trouble when it executes poorly validated
- * paths through the BIOS, so disable _GTS and _BFS by default,
- * but do speak up and offer the option to enable them.
- */
-static void __init acpi_gts_bfs_check(void)
-{
- acpi_handle dummy;
-
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy)))
- {
- printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n");
- printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, "
- "please notify linux-acpi@vger.kernel.org\n");
- }
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy)))
- {
- printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n");
- printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, "
- "please notify linux-acpi@vger.kernel.org\n");
- }
+ acpi_enter_sleep_state(ACPI_STATE_S5);
}
int __init acpi_sleep_init(void)
@@ -979,6 +925,5 @@ int __init acpi_sleep_init(void)
* object can also be evaluated when the system enters S5.
*/
register_reboot_notifier(&tts_notifier);
- acpi_gts_bfs_check();
return 0;
}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 240a24400976..7c3f98ba4afe 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -173,7 +173,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp)
{
int result = 0;
- if (!strncmp(val, "enable", strlen("enable"))) {
+ if (!strncmp(val, "enable", sizeof("enable") - 1)) {
result = acpi_debug_trace(trace_method_name, trace_debug_level,
trace_debug_layer, 0);
if (result)
@@ -181,7 +181,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp)
goto exit;
}
- if (!strncmp(val, "disable", strlen("disable"))) {
+ if (!strncmp(val, "disable", sizeof("disable") - 1)) {
int name = 0;
result = acpi_debug_trace((char *)&name, trace_debug_level,
trace_debug_layer, 0);
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 9fe90e9fecb5..edda74a43406 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -106,7 +106,9 @@ static const struct acpi_device_id thermal_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_thermal_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume);
static struct acpi_driver acpi_thermal_driver = {
@@ -1041,6 +1043,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_thermal_resume(struct device *dev)
{
struct acpi_thermal *tz;
@@ -1075,6 +1078,7 @@ static int acpi_thermal_resume(struct device *dev)
return AE_OK;
}
+#endif
static int thermal_act(const struct dmi_system_id *d) {
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 2be8ef1d3093..27cecd313e75 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -115,7 +115,7 @@ config SATA_SIL24
If unsure, say N.
config ATA_SFF
- bool "ATA SFF support"
+ bool "ATA SFF support (for legacy IDE and PATA)"
default y
help
This option adds support for ATA controllers with SFF
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 062e6a1a248f..7862d17976b7 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -256,10 +256,21 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */
{ PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x9c02), board_ahci }, /* Lynx Point-LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c03), board_ahci }, /* Lynx Point-LP AHCI */
+ { PCI_VDEVICE(INTEL, 0x9c04), board_ahci }, /* Lynx Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c05), board_ahci }, /* Lynx Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c06), board_ahci }, /* Lynx Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c07), board_ahci }, /* Lynx Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c0e), board_ahci }, /* Lynx Point-LP RAID */
+ { PCI_VDEVICE(INTEL, 0x9c0f), board_ahci }, /* Lynx Point-LP RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr },
+ /* JMicron 362B and 362C have an AHCI function with IDE class code */
+ { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr },
+ { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr },
/* ATI */
{ PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */
@@ -385,6 +396,8 @@ static const struct pci_device_id ahci_pci_tbl[] = {
.driver_data = board_ahci_yes_fbs }, /* 88se9125 */
{ PCI_DEVICE(0x1b4b, 0x917a),
.driver_data = board_ahci_yes_fbs }, /* 88se9172 */
+ { PCI_DEVICE(0x1b4b, 0x9192),
+ .driver_data = board_ahci_yes_fbs }, /* 88se9172 on some Gigabyte */
{ PCI_DEVICE(0x1b4b, 0x91a3),
.driver_data = board_ahci_yes_fbs },
@@ -392,7 +405,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(PROMISE, 0x3f20), board_ahci }, /* PDC42819 */
/* Asmedia */
- { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1061 */
+ { PCI_VDEVICE(ASMEDIA, 0x0601), board_ahci }, /* ASM1060 */
+ { PCI_VDEVICE(ASMEDIA, 0x0602), board_ahci }, /* ASM1060 */
+ { PCI_VDEVICE(ASMEDIA, 0x0611), board_ahci }, /* ASM1061 */
+ { PCI_VDEVICE(ASMEDIA, 0x0612), board_ahci }, /* ASM1062 */
/* Generic, PCI class code for AHCI */
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index c2594ddf25b0..57eb1c212a4c 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -320,6 +320,7 @@ extern struct device_attribute *ahci_sdev_attrs[];
extern struct ata_port_operations ahci_ops;
extern struct ata_port_operations ahci_pmp_retry_srst_ops;
+unsigned int ahci_dev_classify(struct ata_port *ap);
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts);
void ahci_save_initial_config(struct device *dev,
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index 3c809bfbccf5..ef773e12af79 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -329,6 +329,14 @@ static const struct pci_device_id piix_pci_tbl[] = {
{ 0x8086, 0x8c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (Lynx Point) */
{ 0x8086, 0x8c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Lynx Point-LP) */
+ { 0x8086, 0x9c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
+ /* SATA Controller IDE (Lynx Point-LP) */
+ { 0x8086, 0x9c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
+ /* SATA Controller IDE (Lynx Point-LP) */
+ { 0x8086, 0x9c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Lynx Point-LP) */
+ { 0x8086, 0x9c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (DH89xxCC) */
{ 0x8086, 0x2326, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
{ } /* terminate list */
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index f9eaa82311a9..555c07afa05b 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1139,7 +1139,7 @@ static void ahci_dev_config(struct ata_device *dev)
}
}
-static unsigned int ahci_dev_classify(struct ata_port *ap)
+unsigned int ahci_dev_classify(struct ata_port *ap)
{
void __iomem *port_mmio = ahci_port_base(ap);
struct ata_taskfile tf;
@@ -1153,6 +1153,7 @@ static unsigned int ahci_dev_classify(struct ata_port *ap)
return ata_dev_classify(&tf);
}
+EXPORT_SYMBOL_GPL(ahci_dev_classify);
void ahci_fill_cmd_slot(struct ahci_port_priv *pp, unsigned int tag,
u32 opts)
diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c
index 902b5a457170..fd9ecf74e631 100644
--- a/drivers/ata/libata-acpi.c
+++ b/drivers/ata/libata-acpi.c
@@ -60,17 +60,7 @@ acpi_handle ata_ap_acpi_handle(struct ata_port *ap)
if (ap->flags & ATA_FLAG_ACPI_SATA)
return NULL;
- /*
- * If acpi bind operation has already happened, we can get the handle
- * for the port by checking the corresponding scsi_host device's
- * firmware node, otherwise we will need to find out the handle from
- * its parent's acpi node.
- */
- if (ap->scsi_host)
- return DEVICE_ACPI_HANDLE(&ap->scsi_host->shost_gendev);
- else
- return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev),
- ap->port_no);
+ return acpi_get_child(DEVICE_ACPI_HANDLE(ap->host->dev), ap->port_no);
}
EXPORT_SYMBOL(ata_ap_acpi_handle);
@@ -1101,6 +1091,9 @@ static int ata_acpi_bind_host(struct ata_port *ap, acpi_handle *handle)
if (!*handle)
return -ENODEV;
+ if (ata_acpi_gtm(ap, &ap->__acpi_init_gtm) == 0)
+ ap->pflags |= ATA_PFLAG_INIT_GTM_VALID;
+
return 0;
}
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index fadd5866d40f..8e1039c8e159 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4062,7 +4062,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
{ "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA },
{ "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA },
{ "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA },
- { "2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
+ { " 2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
/* Odd clown on sil3726/4726 PMPs */
{ "Config Disk", NULL, ATA_HORKAGE_DISABLE },
@@ -4128,6 +4128,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
/* Devices that do not need bridging limits applied */
{ "MTRON MSP-SATA*", NULL, ATA_HORKAGE_BRIDGE_OK, },
+ { "BUFFALO HD-QSU2/R5", NULL, ATA_HORKAGE_BRIDGE_OK, },
/* Devices which aren't very happy with higher link speeds */
{ "WD My Book", NULL, ATA_HORKAGE_1_5_GBPS, },
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 361c75cea57b..24e51056ac26 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
+#include <linux/dmi.h>
#define DRV_NAME "pata_atiixp"
#define DRV_VERSION "0.4.6"
@@ -33,11 +34,26 @@ enum {
ATIIXP_IDE_UDMA_MODE = 0x56
};
+static const struct dmi_system_id attixp_cable_override_dmi_table[] = {
+ {
+ /* Board has onboard PATA<->SATA converters */
+ .ident = "MSI E350DM-E33",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "E350DM-E33(MS-7720)"),
+ },
+ },
+ { }
+};
+
static int atiixp_cable_detect(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 udma;
+ if (dmi_check_system(attixp_cable_override_dmi_table))
+ return ATA_CBL_PATA40_SHORT;
+
/* Hack from drivers/ide/pci. Really we want to know how to do the
raw detection not play follow the bios mode guess */
pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma);
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 24712adf69df..311be18d3f03 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -65,6 +65,8 @@
#include <linux/mbus.h>
#include <linux/bitops.h>
#include <linux/gfp.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
@@ -4026,7 +4028,7 @@ static int mv_platform_probe(struct platform_device *pdev)
struct ata_host *host;
struct mv_host_priv *hpriv;
struct resource *res;
- int n_ports = 0;
+ int n_ports = 0, irq = 0;
int rc;
#if defined(CONFIG_HAVE_CLK)
int port;
@@ -4050,8 +4052,14 @@ static int mv_platform_probe(struct platform_device *pdev)
return -EINVAL;
/* allocate host */
- mv_platform_data = pdev->dev.platform_data;
- n_ports = mv_platform_data->n_ports;
+ if (pdev->dev.of_node) {
+ of_property_read_u32(pdev->dev.of_node, "nr-ports", &n_ports);
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ } else {
+ mv_platform_data = pdev->dev.platform_data;
+ n_ports = mv_platform_data->n_ports;
+ irq = platform_get_irq(pdev, 0);
+ }
host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports);
hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
@@ -4109,8 +4117,7 @@ static int mv_platform_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "slots %u ports %d\n",
(unsigned)MV_MAX_Q_DEPTH, host->n_ports);
- rc = ata_host_activate(host, platform_get_irq(pdev, 0), mv_interrupt,
- IRQF_SHARED, &mv6_sht);
+ rc = ata_host_activate(host, irq, mv_interrupt, IRQF_SHARED, &mv6_sht);
if (!rc)
return 0;
@@ -4205,15 +4212,24 @@ static int mv_platform_resume(struct platform_device *pdev)
#define mv_platform_resume NULL
#endif
+#ifdef CONFIG_OF
+static struct of_device_id mv_sata_dt_ids[] __devinitdata = {
+ { .compatible = "marvell,orion-sata", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mv_sata_dt_ids);
+#endif
+
static struct platform_driver mv_platform_driver = {
- .probe = mv_platform_probe,
- .remove = __devexit_p(mv_platform_remove),
- .suspend = mv_platform_suspend,
- .resume = mv_platform_resume,
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- },
+ .probe = mv_platform_probe,
+ .remove = __devexit_p(mv_platform_remove),
+ .suspend = mv_platform_suspend,
+ .resume = mv_platform_resume,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mv_sata_dt_ids),
+ },
};
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index d4386019af5d..96cce6d53195 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -2362,7 +2362,7 @@ static int __devinit ia_init(struct atm_dev *dev)
{
printk(DEV_LABEL " (itf %d): can't set up page mapping\n",
dev->number);
- return error;
+ return -ENOMEM;
}
IF_INIT(printk(DEV_LABEL " (itf %d): rev.%d,base=%p,irq=%d\n",
dev->number, iadev->pci->revision, base, iadev->irq);)
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 9b21469482ae..08b4c5209384 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -196,6 +196,7 @@ config CMA
bool "Contiguous Memory Allocator (EXPERIMENTAL)"
depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL
select MIGRATION
+ select MEMORY_ISOLATION
help
This enables the Contiguous Memory Allocator which allows drivers
to allocate big physically-contiguous blocks of memory for use with
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f338037a4f3d..5e6e00bc1652 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1865,6 +1865,7 @@ int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
char dict[128];
+ const char *level_extra = "";
size_t dictlen = 0;
const char *subsys;
@@ -1911,10 +1912,14 @@ int __dev_printk(const char *level, const struct device *dev,
"DEVICE=+%s:%s", subsys, dev_name(dev));
}
skip:
+ if (level[2])
+ level_extra = &level[2]; /* skip past KERN_SOH "L" */
+
return printk_emit(0, level[1] - '0',
dictlen ? dict : NULL, dictlen,
- "%s %s: %pV",
- dev_driver_string(dev), dev_name(dev), vaf);
+ "%s %s: %s%pV",
+ dev_driver_string(dev), dev_name(dev),
+ level_extra, vaf);
}
EXPORT_SYMBOL(__dev_printk);
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index d91a3a0b2325..deb4a456cf83 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -156,9 +156,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (!err)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
@@ -218,10 +216,7 @@ static int handle_create(const char *nodename, umode_t mode, struct device *dev)
/* mark as kernel-created inode */
dentry->d_inode->i_private = &thread;
}
- dput(dentry);
-
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ done_path_create(&path, dentry);
return err;
}
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index 78efb0306a44..34d94c762a1e 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -250,7 +250,7 @@ int __init dma_declare_contiguous(struct device *dev, unsigned long size,
return -EINVAL;
/* Sanitise input arguments */
- alignment = PAGE_SIZE << max(MAX_ORDER, pageblock_order);
+ alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
base = ALIGN(base, alignment);
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 869d7ff2227f..eb78e9640c4a 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -169,8 +169,7 @@ void pm_clk_init(struct device *dev)
*/
int pm_clk_create(struct device *dev)
{
- int ret = dev_pm_get_subsys_data(dev);
- return ret < 0 ? ret : 0;
+ return dev_pm_get_subsys_data(dev);
}
/**
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index a14085cc613f..39c32529b833 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -24,7 +24,6 @@
int dev_pm_get_subsys_data(struct device *dev)
{
struct pm_subsys_data *psd;
- int ret = 0;
psd = kzalloc(sizeof(*psd), GFP_KERNEL);
if (!psd)
@@ -40,7 +39,6 @@ int dev_pm_get_subsys_data(struct device *dev)
dev->power.subsys_data = psd;
pm_clk_init(dev);
psd = NULL;
- ret = 1;
}
spin_unlock_irq(&dev->power.lock);
@@ -48,7 +46,7 @@ int dev_pm_get_subsys_data(struct device *dev)
/* kfree() verifies that its argument is nonzero. */
kfree(psd);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 59894873a3b3..7d9c1cb1c39a 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -147,6 +147,8 @@ static int rpm_check_suspend_allowed(struct device *dev)
|| (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
+ else if (__dev_pm_qos_read_value(dev) < 0)
+ retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
@@ -388,7 +390,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto repeat;
}
- dev->power.deferred_resume = false;
if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */
@@ -403,12 +404,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
goto out;
}
- if (__dev_pm_qos_read_value(dev) < 0) {
- /* Negative PM QoS constraint means "never suspend". */
- retval = -EPERM;
- goto out;
- }
-
__update_runtime_status(dev, RPM_SUSPENDING);
if (dev->pm_domain)
@@ -440,6 +435,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
wake_up_all(&dev->power.wait_queue);
if (dev->power.deferred_resume) {
+ dev->power.deferred_resume = false;
rpm_resume(dev, 0);
retval = -EAGAIN;
goto out;
@@ -584,6 +580,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
|| dev->parent->power.runtime_status == RPM_ACTIVE) {
atomic_inc(&dev->parent->power.child_count);
spin_unlock(&dev->parent->power.lock);
+ retval = 1;
goto no_callback; /* Assume success. */
}
spin_unlock(&dev->parent->power.lock);
@@ -664,7 +661,7 @@ static int rpm_resume(struct device *dev, int rpmflags)
}
wake_up_all(&dev->power.wait_queue);
- if (!retval)
+ if (retval >= 0)
rpm_idle(dev, RPM_ASYNC);
out:
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 11b32d2642df..a6e5672c67e7 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -272,6 +272,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
{ 0, },
};
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 26823d97fd9f..9ea4627dc0c2 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
/* for these chips OTP is always available */
present = true;
break;
-
+ case BCMA_CHIP_ID_BCM43228:
+ present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
+ break;
default:
present = false;
break;
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index de0435e63b02..887f68f6d79a 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -35,6 +35,7 @@ new_skb(ulong len)
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->protocol = __constant_htons(ETH_P_AOE);
+ skb_checksum_none_assert(skb);
}
return skb;
}
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index acda773b3720..da3311129a0c 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -763,16 +763,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout,
{
case CMD_TARGET_STATUS:
/* Pass it up to the upper layers... */
- if( ei->ScsiStatus)
- {
-#if 0
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- c, ei->ScsiStatus);
-#endif
- cmd->result |= (ei->ScsiStatus << 1);
- }
- else { /* scsi status is zero??? How??? */
+ if (!ei->ScsiStatus) {
/* Ordinarily, this case should never happen, but there is a bug
in some released firmware revisions that allows it to happen
@@ -804,6 +795,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout,
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev,
"%p has protocol error\n", c);
break;
diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c
index e54e31b02b88..3fbef018ce55 100644
--- a/drivers/block/drbd/drbd_actlog.c
+++ b/drivers/block/drbd/drbd_actlog.c
@@ -411,7 +411,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
+ mdev->ldev->md.al_offset + mdev->al_tr_pos;
if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE))
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
if (++mdev->al_tr_pos >
div_ceil(mdev->act_log->nr_elements, AL_EXTENTS_PT))
@@ -876,7 +876,11 @@ int __drbd_set_out_of_sync(struct drbd_conf *mdev, sector_t sector, int size,
unsigned int enr, count = 0;
struct lc_element *e;
- if (size <= 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
+ /* this should be an empty REQ_FLUSH */
+ if (size == 0)
+ return 0;
+
+ if (size < 0 || (size & 0x1ff) != 0 || size > DRBD_MAX_BIO_SIZE) {
dev_err(DEV, "sector: %llus, size: %d\n",
(unsigned long long)sector, size);
return 0;
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index fcb956bb4b4c..d84566496746 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -889,6 +889,7 @@ struct bm_aio_ctx {
unsigned int done;
unsigned flags;
#define BM_AIO_COPY_PAGES 1
+#define BM_WRITE_ALL_PAGES 2
int error;
struct kref kref;
};
@@ -1059,7 +1060,8 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_w
if (lazy_writeout_upper_idx && i == lazy_writeout_upper_idx)
break;
if (rw & WRITE) {
- if (bm_test_page_unchanged(b->bm_pages[i])) {
+ if (!(flags & BM_WRITE_ALL_PAGES) &&
+ bm_test_page_unchanged(b->bm_pages[i])) {
dynamic_dev_dbg(DEV, "skipped bm write for idx %u\n", i);
continue;
}
@@ -1096,7 +1098,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_w
if (ctx->error) {
dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
err = -EIO; /* ctx->error ? */
}
@@ -1141,6 +1143,17 @@ int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
}
/**
+ * drbd_bm_write_all() - Write the whole bitmap to its on disk location.
+ * @mdev: DRBD device.
+ *
+ * Will write all pages.
+ */
+int drbd_bm_write_all(struct drbd_conf *mdev) __must_hold(local)
+{
+ return bm_rw(mdev, WRITE, BM_WRITE_ALL_PAGES, 0);
+}
+
+/**
* drbd_bm_lazy_write_out() - Write bitmap pages 0 to @upper_idx-1, if they have changed.
* @mdev: DRBD device.
* @upper_idx: 0: write all changed pages; +ve: page index to stop scanning for changed pages
@@ -1212,7 +1225,7 @@ int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(loc
wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
if (ctx->error)
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
/* that should force detach, so the in memory bitmap will be
* gone in a moment as well. */
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h
index 02f013a073a7..b953cc7c9c00 100644
--- a/drivers/block/drbd/drbd_int.h
+++ b/drivers/block/drbd/drbd_int.h
@@ -813,7 +813,6 @@ enum {
SIGNAL_ASENDER, /* whether asender wants to be interrupted */
SEND_PING, /* whether asender should send a ping asap */
- UNPLUG_QUEUED, /* only relevant with kernel 2.4 */
UNPLUG_REMOTE, /* sending a "UnplugRemote" could help */
MD_DIRTY, /* current uuids and flags not yet on disk */
DISCARD_CONCURRENT, /* Set on one node, cleared on the peer! */
@@ -824,7 +823,6 @@ enum {
CRASHED_PRIMARY, /* This node was a crashed primary.
* Gets cleared when the state.conn
* goes into C_CONNECTED state. */
- NO_BARRIER_SUPP, /* underlying block device doesn't implement barriers */
CONSIDER_RESYNC,
MD_NO_FUA, /* Users wants us to not use FUA/FLUSH on meta data dev */
@@ -834,6 +832,7 @@ enum {
BITMAP_IO_QUEUED, /* Started bitmap IO */
GO_DISKLESS, /* Disk is being detached, on io-error or admin request. */
WAS_IO_ERROR, /* Local disk failed returned IO error */
+ FORCE_DETACH, /* Force-detach from local disk, aborting any pending local IO */
RESYNC_AFTER_NEG, /* Resync after online grow after the attach&negotiate finished. */
NET_CONGESTED, /* The data socket is congested */
@@ -851,6 +850,13 @@ enum {
AL_SUSPENDED, /* Activity logging is currently suspended. */
AHEAD_TO_SYNC_SOURCE, /* Ahead -> SyncSource queued */
STATE_SENT, /* Do not change state/UUIDs while this is set */
+
+ CALLBACK_PENDING, /* Whether we have a call_usermodehelper(, UMH_WAIT_PROC)
+ * pending, from drbd worker context.
+ * If set, bdi_write_congested() returns true,
+ * so shrink_page_list() would not recurse into,
+ * and potentially deadlock on, this drbd worker.
+ */
};
struct drbd_bitmap; /* opaque for drbd_conf */
@@ -1130,8 +1136,8 @@ struct drbd_conf {
int rs_in_flight; /* resync sectors in flight (to proxy, in proxy and from proxy) */
int rs_planed; /* resync sectors already planned */
atomic_t ap_in_flight; /* App sectors in flight (waiting for ack) */
- int peer_max_bio_size;
- int local_max_bio_size;
+ unsigned int peer_max_bio_size;
+ unsigned int local_max_bio_size;
};
static inline struct drbd_conf *minor_to_mdev(unsigned int minor)
@@ -1435,9 +1441,9 @@ struct bm_extent {
* hash table. */
#define HT_SHIFT 8
#define DRBD_MAX_BIO_SIZE (1U<<(9+HT_SHIFT))
-#define DRBD_MAX_BIO_SIZE_SAFE (1 << 12) /* Works always = 4k */
+#define DRBD_MAX_BIO_SIZE_SAFE (1U << 12) /* Works always = 4k */
-#define DRBD_MAX_SIZE_H80_PACKET (1 << 15) /* The old header only allows packets up to 32Kib data */
+#define DRBD_MAX_SIZE_H80_PACKET (1U << 15) /* The old header only allows packets up to 32Kib data */
/* Number of elements in the app_reads_hash */
#define APP_R_HSIZE 15
@@ -1463,6 +1469,7 @@ extern int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr);
extern int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(local);
extern int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local);
extern int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local);
+extern int drbd_bm_write_all(struct drbd_conf *mdev) __must_hold(local);
extern int drbd_bm_write_copy_pages(struct drbd_conf *mdev) __must_hold(local);
extern unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev,
unsigned long al_enr);
@@ -1840,12 +1847,20 @@ static inline int drbd_request_state(struct drbd_conf *mdev,
return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED);
}
+enum drbd_force_detach_flags {
+ DRBD_IO_ERROR,
+ DRBD_META_IO_ERROR,
+ DRBD_FORCE_DETACH,
+};
+
#define __drbd_chk_io_error(m,f) __drbd_chk_io_error_(m,f, __func__)
-static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach, const char *where)
+static inline void __drbd_chk_io_error_(struct drbd_conf *mdev,
+ enum drbd_force_detach_flags forcedetach,
+ const char *where)
{
switch (mdev->ldev->dc.on_io_error) {
case EP_PASS_ON:
- if (!forcedetach) {
+ if (forcedetach == DRBD_IO_ERROR) {
if (__ratelimit(&drbd_ratelimit_state))
dev_err(DEV, "Local IO failed in %s.\n", where);
if (mdev->state.disk > D_INCONSISTENT)
@@ -1856,6 +1871,8 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
case EP_DETACH:
case EP_CALL_HELPER:
set_bit(WAS_IO_ERROR, &mdev->flags);
+ if (forcedetach == DRBD_FORCE_DETACH)
+ set_bit(FORCE_DETACH, &mdev->flags);
if (mdev->state.disk > D_FAILED) {
_drbd_set_state(_NS(mdev, disk, D_FAILED), CS_HARD, NULL);
dev_err(DEV,
@@ -1875,7 +1892,7 @@ static inline void __drbd_chk_io_error_(struct drbd_conf *mdev, int forcedetach,
*/
#define drbd_chk_io_error(m,e,f) drbd_chk_io_error_(m,e,f, __func__)
static inline void drbd_chk_io_error_(struct drbd_conf *mdev,
- int error, int forcedetach, const char *where)
+ int error, enum drbd_force_detach_flags forcedetach, const char *where)
{
if (error) {
unsigned long flags;
@@ -2405,15 +2422,17 @@ static inline void dec_ap_bio(struct drbd_conf *mdev)
int ap_bio = atomic_dec_return(&mdev->ap_bio_cnt);
D_ASSERT(ap_bio >= 0);
+
+ if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
+ if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
+ drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
+ }
+
/* this currently does wake_up for every dec_ap_bio!
* maybe rather introduce some type of hysteresis?
* e.g. (ap_bio == mxb/2 || ap_bio == 0) ? */
if (ap_bio < mxb)
wake_up(&mdev->misc_wait);
- if (ap_bio == 0 && test_bit(BITMAP_IO, &mdev->flags)) {
- if (!test_and_set_bit(BITMAP_IO_QUEUED, &mdev->flags))
- drbd_queue_work(&mdev->data.work, &mdev->bm_io_work.w);
- }
}
static inline int drbd_set_ed_uuid(struct drbd_conf *mdev, u64 val)
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 920ede2829d6..f93a0320e952 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -79,6 +79,7 @@ static int w_md_sync(struct drbd_conf *mdev, struct drbd_work *w, int unused);
static void md_sync_timer_fn(unsigned long data);
static int w_bitmap_io(struct drbd_conf *mdev, struct drbd_work *w, int unused);
static int w_go_diskless(struct drbd_conf *mdev, struct drbd_work *w, int unused);
+static void _tl_clear(struct drbd_conf *mdev);
MODULE_AUTHOR("Philipp Reisner <phil@linbit.com>, "
"Lars Ellenberg <lars@linbit.com>");
@@ -432,19 +433,10 @@ static void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
/* Actions operating on the disk state, also want to work on
requests that got barrier acked. */
- switch (what) {
- case fail_frozen_disk_io:
- case restart_frozen_disk_io:
- list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
- req = list_entry(le, struct drbd_request, tl_requests);
- _req_mod(req, what);
- }
- case connection_lost_while_pending:
- case resend:
- break;
- default:
- dev_err(DEV, "what = %d in _tl_restart()\n", what);
+ list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
+ req = list_entry(le, struct drbd_request, tl_requests);
+ _req_mod(req, what);
}
}
@@ -459,11 +451,16 @@ static void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
*/
void tl_clear(struct drbd_conf *mdev)
{
+ spin_lock_irq(&mdev->req_lock);
+ _tl_clear(mdev);
+ spin_unlock_irq(&mdev->req_lock);
+}
+
+static void _tl_clear(struct drbd_conf *mdev)
+{
struct list_head *le, *tle;
struct drbd_request *r;
- spin_lock_irq(&mdev->req_lock);
-
_tl_restart(mdev, connection_lost_while_pending);
/* we expect this list to be empty. */
@@ -482,7 +479,6 @@ void tl_clear(struct drbd_conf *mdev)
memset(mdev->app_reads_hash, 0, APP_R_HSIZE*sizeof(void *));
- spin_unlock_irq(&mdev->req_lock);
}
void tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
@@ -1476,12 +1472,12 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
if (ns.susp_fen) {
/* case1: The outdate peer handler is successful: */
if (os.pdsk > D_OUTDATED && ns.pdsk <= D_OUTDATED) {
- tl_clear(mdev);
if (test_bit(NEW_CUR_UUID, &mdev->flags)) {
drbd_uuid_new_current(mdev);
clear_bit(NEW_CUR_UUID, &mdev->flags);
}
spin_lock_irq(&mdev->req_lock);
+ _tl_clear(mdev);
_drbd_set_state(_NS(mdev, susp_fen, 0), CS_VERBOSE, NULL);
spin_unlock_irq(&mdev->req_lock);
}
@@ -1514,6 +1510,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
/* Do not change the order of the if above and the two below... */
if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */
+ /* we probably will start a resync soon.
+ * make sure those things are properly reset. */
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+ drbd_rs_cancel_all(mdev);
+
drbd_send_uuids(mdev);
drbd_send_state(mdev, ns);
}
@@ -1630,9 +1633,24 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
eh = mdev->ldev->dc.on_io_error;
was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
- /* Immediately allow completion of all application IO, that waits
- for completion from the local disk. */
- tl_abort_disk_io(mdev);
+ if (was_io_error && eh == EP_CALL_HELPER)
+ drbd_khelper(mdev, "local-io-error");
+
+ /* Immediately allow completion of all application IO,
+ * that waits for completion from the local disk,
+ * if this was a force-detach due to disk_timeout
+ * or administrator request (drbdsetup detach --force).
+ * Do NOT abort otherwise.
+ * Aborting local requests may cause serious problems,
+ * if requests are completed to upper layers already,
+ * and then later the already submitted local bio completes.
+ * This can cause DMA into former bio pages that meanwhile
+ * have been re-used for other things.
+ * So aborting local requests may cause crashes,
+ * or even worse, silent data corruption.
+ */
+ if (test_and_clear_bit(FORCE_DETACH, &mdev->flags))
+ tl_abort_disk_io(mdev);
/* current state still has to be D_FAILED,
* there is only one way out: to D_DISKLESS,
@@ -1653,9 +1671,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
drbd_md_sync(mdev);
}
put_ldev(mdev);
-
- if (was_io_error && eh == EP_CALL_HELPER)
- drbd_khelper(mdev, "local-io-error");
}
/* second half of local IO error, failure to attach,
@@ -1669,10 +1684,6 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
"ASSERT FAILED: disk is %s while going diskless\n",
drbd_disk_str(mdev->state.disk));
- mdev->rs_total = 0;
- mdev->rs_failed = 0;
- atomic_set(&mdev->rs_pending_cnt, 0);
-
if (ns.conn >= C_CONNECTED)
drbd_send_state(mdev, ns);
@@ -2194,7 +2205,8 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
{
struct p_sizes p;
sector_t d_size, u_size;
- int q_order_type, max_bio_size;
+ int q_order_type;
+ unsigned int max_bio_size;
int ok;
if (get_ldev_if_state(mdev, D_NEGOTIATING)) {
@@ -2203,7 +2215,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
u_size = mdev->ldev->dc.disk_size;
q_order_type = drbd_queue_order_type(mdev);
max_bio_size = queue_max_hw_sectors(mdev->ldev->backing_bdev->bd_disk->queue) << 9;
- max_bio_size = min_t(int, max_bio_size, DRBD_MAX_BIO_SIZE);
+ max_bio_size = min(max_bio_size, DRBD_MAX_BIO_SIZE);
put_ldev(mdev);
} else {
d_size = 0;
@@ -2214,7 +2226,7 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
/* Never allow old drbd (up to 8.3.7) to see more than 32KiB */
if (mdev->agreed_pro_version <= 94)
- max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+ max_bio_size = min(max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
p.d_size = cpu_to_be64(d_size);
p.u_size = cpu_to_be64(u_size);
@@ -3521,9 +3533,9 @@ static void drbd_cleanup(void)
}
/**
- * drbd_congested() - Callback for pdflush
+ * drbd_congested() - Callback for the flusher thread
* @congested_data: User data
- * @bdi_bits: Bits pdflush is currently interested in
+ * @bdi_bits: Bits the BDI flusher thread is currently interested in
*
* Returns 1<<BDI_async_congested and/or 1<<BDI_sync_congested if we are congested.
*/
@@ -3541,6 +3553,22 @@ static int drbd_congested(void *congested_data, int bdi_bits)
goto out;
}
+ if (test_bit(CALLBACK_PENDING, &mdev->flags)) {
+ r |= (1 << BDI_async_congested);
+ /* Without good local data, we would need to read from remote,
+ * and that would need the worker thread as well, which is
+ * currently blocked waiting for that usermode helper to
+ * finish.
+ */
+ if (!get_ldev_if_state(mdev, D_UP_TO_DATE))
+ r |= (1 << BDI_sync_congested);
+ else
+ put_ldev(mdev);
+ r &= bdi_bits;
+ reason = 'c';
+ goto out;
+ }
+
if (get_ldev(mdev)) {
q = bdev_get_queue(mdev->ldev->backing_bdev);
r = bdi_congested(&q->backing_dev_info, bdi_bits);
@@ -3604,6 +3632,7 @@ struct drbd_conf *drbd_new_device(unsigned int minor)
q->backing_dev_info.congested_data = mdev;
blk_queue_make_request(q, drbd_make_request);
+ blk_queue_flush(q, REQ_FLUSH | REQ_FUA);
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
@@ -3870,7 +3899,7 @@ void drbd_md_sync(struct drbd_conf *mdev)
if (!drbd_md_sync_page_io(mdev, mdev->ldev, sector, WRITE)) {
/* this was a try anyways ... */
dev_err(DEV, "meta data update failed!\n");
- drbd_chk_io_error(mdev, 1, true);
+ drbd_chk_io_error(mdev, 1, DRBD_META_IO_ERROR);
}
/* Update mdev->ldev->md.la_size_sect,
@@ -3950,9 +3979,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
spin_lock_irq(&mdev->req_lock);
if (mdev->state.conn < C_CONNECTED) {
- int peer;
+ unsigned int peer;
peer = be32_to_cpu(buffer->la_peer_max_bio_size);
- peer = max_t(int, peer, DRBD_MAX_BIO_SIZE_SAFE);
+ peer = max(peer, DRBD_MAX_BIO_SIZE_SAFE);
mdev->peer_max_bio_size = peer;
}
spin_unlock_irq(&mdev->req_lock);
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 6d4de6a72e80..edb490aad8b4 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -147,6 +147,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
char *argv[] = {usermode_helper, cmd, mb, NULL };
int ret;
+ if (current == mdev->worker.task)
+ set_bit(CALLBACK_PENDING, &mdev->flags);
+
snprintf(mb, 12, "minor-%d", mdev_to_minor(mdev));
if (get_net_conf(mdev)) {
@@ -189,6 +192,9 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
usermode_helper, cmd, mb,
(ret >> 8) & 0xff, ret);
+ if (current == mdev->worker.task)
+ clear_bit(CALLBACK_PENDING, &mdev->flags);
+
if (ret < 0) /* Ignore any ERRNOs we got. */
ret = 0;
@@ -668,8 +674,8 @@ enum determine_dev_size drbd_determine_dev_size(struct drbd_conf *mdev, enum dds
la_size_changed && md_moved ? "size changed and md moved" :
la_size_changed ? "size changed" : "md moved");
/* next line implicitly does drbd_suspend_io()+drbd_resume_io() */
- err = drbd_bitmap_io(mdev, &drbd_bm_write,
- "size changed", BM_LOCKED_MASK);
+ err = drbd_bitmap_io(mdev, md_moved ? &drbd_bm_write_all : &drbd_bm_write,
+ "size changed", BM_LOCKED_MASK);
if (err) {
rv = dev_size_error;
goto out;
@@ -795,8 +801,8 @@ static int drbd_check_al_size(struct drbd_conf *mdev)
static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_size)
{
struct request_queue * const q = mdev->rq_queue;
- int max_hw_sectors = max_bio_size >> 9;
- int max_segments = 0;
+ unsigned int max_hw_sectors = max_bio_size >> 9;
+ unsigned int max_segments = 0;
if (get_ldev_if_state(mdev, D_ATTACHING)) {
struct request_queue * const b = mdev->ldev->backing_bdev->bd_disk->queue;
@@ -829,7 +835,7 @@ static void drbd_setup_queue_param(struct drbd_conf *mdev, unsigned int max_bio_
void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
{
- int now, new, local, peer;
+ unsigned int now, new, local, peer;
now = queue_max_hw_sectors(mdev->rq_queue) << 9;
local = mdev->local_max_bio_size; /* Eventually last known value, from volatile memory */
@@ -840,13 +846,14 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
mdev->local_max_bio_size = local;
put_ldev(mdev);
}
+ local = min(local, DRBD_MAX_BIO_SIZE);
/* We may ignore peer limits if the peer is modern enough.
Because new from 8.3.8 onwards the peer can use multiple
BIOs for a single peer_request */
if (mdev->state.conn >= C_CONNECTED) {
if (mdev->agreed_pro_version < 94) {
- peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
+ peer = min(mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
/* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
} else if (mdev->agreed_pro_version == 94)
peer = DRBD_MAX_SIZE_H80_PACKET;
@@ -854,10 +861,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
peer = DRBD_MAX_BIO_SIZE;
}
- new = min_t(int, local, peer);
+ new = min(local, peer);
if (mdev->state.role == R_PRIMARY && new < now)
- dev_err(DEV, "ASSERT FAILED new < now; (%d < %d)\n", new, now);
+ dev_err(DEV, "ASSERT FAILED new < now; (%u < %u)\n", new, now);
if (new != now)
dev_info(DEV, "max BIO size = %u\n", new);
@@ -950,6 +957,14 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
* to realize a "hot spare" feature (not that I'd recommend that) */
wait_event(mdev->misc_wait, !atomic_read(&mdev->local_cnt));
+ /* make sure there is no leftover from previous force-detach attempts */
+ clear_bit(FORCE_DETACH, &mdev->flags);
+
+ /* and no leftover from previously aborted resync or verify, either */
+ mdev->rs_total = 0;
+ mdev->rs_failed = 0;
+ atomic_set(&mdev->rs_pending_cnt, 0);
+
/* allocation not in the IO path, cqueue thread context */
nbc = kzalloc(sizeof(struct drbd_backing_dev), GFP_KERNEL);
if (!nbc) {
@@ -1345,6 +1360,7 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
}
if (dt.detach_force) {
+ set_bit(FORCE_DETACH, &mdev->flags);
drbd_force_state(mdev, NS(disk, D_FAILED));
reply->ret_code = SS_SUCCESS;
goto out;
@@ -1962,9 +1978,11 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
int retcode;
/* If there is still bitmap IO pending, probably because of a previous
- * resync just being finished, wait for it before requesting a new resync. */
+ * resync just being finished, wait for it before requesting a new resync.
+ * Also wait for it's after_state_ch(). */
drbd_suspend_io(mdev);
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+ drbd_flush_workqueue(mdev);
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
@@ -2003,9 +2021,11 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
int retcode;
/* If there is still bitmap IO pending, probably because of a previous
- * resync just being finished, wait for it before requesting a new resync. */
+ * resync just being finished, wait for it before requesting a new resync.
+ * Also wait for it's after_state_ch(). */
drbd_suspend_io(mdev);
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
+ drbd_flush_workqueue(mdev);
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
diff --git a/drivers/block/drbd/drbd_proc.c b/drivers/block/drbd/drbd_proc.c
index 869bada2ed06..5496104f90b9 100644
--- a/drivers/block/drbd/drbd_proc.c
+++ b/drivers/block/drbd/drbd_proc.c
@@ -245,6 +245,9 @@ static int drbd_seq_show(struct seq_file *seq, void *v)
mdev->state.role == R_SECONDARY) {
seq_printf(seq, "%2d: cs:Unconfigured\n", i);
} else {
+ /* reset mdev->congestion_reason */
+ bdi_rw_congested(&mdev->rq_queue->backing_dev_info);
+
seq_printf(seq,
"%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n"
" ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index ea4836e0ae98..c74ca2df7431 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -277,6 +277,9 @@ static void drbd_pp_free(struct drbd_conf *mdev, struct page *page, int is_net)
atomic_t *a = is_net ? &mdev->pp_in_use_by_net : &mdev->pp_in_use;
int i;
+ if (page == NULL)
+ return;
+
if (drbd_pp_vacant > (DRBD_MAX_BIO_SIZE/PAGE_SIZE)*minor_count)
i = page_chain_free(page);
else {
@@ -316,7 +319,7 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
gfp_t gfp_mask) __must_hold(local)
{
struct drbd_epoch_entry *e;
- struct page *page;
+ struct page *page = NULL;
unsigned nr_pages = (data_size + PAGE_SIZE -1) >> PAGE_SHIFT;
if (drbd_insert_fault(mdev, DRBD_FAULT_AL_EE))
@@ -329,9 +332,11 @@ struct drbd_epoch_entry *drbd_alloc_ee(struct drbd_conf *mdev,
return NULL;
}
- page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
- if (!page)
- goto fail;
+ if (data_size) {
+ page = drbd_pp_alloc(mdev, nr_pages, (gfp_mask & __GFP_WAIT));
+ if (!page)
+ goto fail;
+ }
INIT_HLIST_NODE(&e->collision);
e->epoch = NULL;
@@ -1270,7 +1275,6 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
data_size -= dgs;
- ERR_IF(data_size == 0) return NULL;
ERR_IF(data_size & 0x1ff) return NULL;
ERR_IF(data_size > DRBD_MAX_BIO_SIZE) return NULL;
@@ -1291,6 +1295,9 @@ read_in_block(struct drbd_conf *mdev, u64 id, sector_t sector, int data_size) __
if (!e)
return NULL;
+ if (!data_size)
+ return e;
+
ds = data_size;
page = e->pages;
page_chain_for_each(page) {
@@ -1715,6 +1722,10 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
dp_flags = be32_to_cpu(p->dp_flags);
rw |= wire_flags_to_bio(mdev, dp_flags);
+ if (e->pages == NULL) {
+ D_ASSERT(e->size == 0);
+ D_ASSERT(dp_flags & DP_FLUSH);
+ }
if (dp_flags & DP_MAY_SET_IN_SYNC)
e->flags |= EE_MAY_SET_IN_SYNC;
@@ -3801,11 +3812,18 @@ void drbd_free_tl_hash(struct drbd_conf *mdev)
mdev->ee_hash = NULL;
mdev->ee_hash_s = 0;
- /* paranoia code */
- for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++)
- if (h->first)
- dev_err(DEV, "ASSERT FAILED tl_hash[%u] == %p, expected NULL\n",
- (int)(h - mdev->tl_hash), h->first);
+ /* We may not have had the chance to wait for all locally pending
+ * application requests. The hlist_add_fake() prevents access after
+ * free on master bio completion. */
+ for (h = mdev->tl_hash; h < mdev->tl_hash + mdev->tl_hash_s; h++) {
+ struct drbd_request *req;
+ struct hlist_node *pos, *n;
+ hlist_for_each_entry_safe(req, pos, n, h, collision) {
+ hlist_del_init(&req->collision);
+ hlist_add_fake(&req->collision);
+ }
+ }
+
kfree(mdev->tl_hash);
mdev->tl_hash = NULL;
mdev->tl_hash_s = 0;
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index 8e93a6ac9bb6..01b2ac641c7b 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -455,7 +455,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
req->rq_state |= RQ_LOCAL_COMPLETED;
req->rq_state &= ~RQ_LOCAL_PENDING;
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
_req_may_be_done_not_susp(req, m);
break;
@@ -477,7 +477,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
break;
}
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
goto_queue_for_net_read:
@@ -695,6 +695,12 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
break;
case resend:
+ /* Simply complete (local only) READs. */
+ if (!(req->rq_state & RQ_WRITE) && !req->w.cb) {
+ _req_may_be_done(req, m);
+ break;
+ }
+
/* If RQ_NET_OK is already set, we got a P_WRITE_ACK or P_RECV_ACK
before the connection loss (B&C only); only P_BARRIER_ACK was missing.
Trowing them out of the TL here by pretending we got a BARRIER_ACK
@@ -834,7 +840,15 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
req->private_bio = NULL;
}
if (rw == WRITE) {
- remote = 1;
+ /* Need to replicate writes. Unless it is an empty flush,
+ * which is better mapped to a DRBD P_BARRIER packet,
+ * also for drbd wire protocol compatibility reasons. */
+ if (unlikely(size == 0)) {
+ /* The only size==0 bios we expect are empty flushes. */
+ D_ASSERT(bio->bi_rw & REQ_FLUSH);
+ remote = 0;
+ } else
+ remote = 1;
} else {
/* READ || READA */
if (local) {
@@ -870,8 +884,11 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
* extent. This waits for any resync activity in the corresponding
* resync extent to finish, and, if necessary, pulls in the target
* extent into the activity log, which involves further disk io because
- * of transactional on-disk meta data updates. */
- if (rw == WRITE && local && !test_bit(AL_SUSPENDED, &mdev->flags)) {
+ * of transactional on-disk meta data updates.
+ * Empty flushes don't need to go into the activity log, they can only
+ * flush data for pending writes which are already in there. */
+ if (rw == WRITE && local && size
+ && !test_bit(AL_SUSPENDED, &mdev->flags)) {
req->rq_state |= RQ_IN_ACT_LOG;
drbd_al_begin_io(mdev, sector);
}
@@ -994,7 +1011,10 @@ allocate_barrier:
if (rw == WRITE && _req_conflicts(req))
goto fail_conflicting;
- list_add_tail(&req->tl_requests, &mdev->newest_tle->requests);
+ /* no point in adding empty flushes to the transfer log,
+ * they are mapped to drbd barriers already. */
+ if (likely(size!=0))
+ list_add_tail(&req->tl_requests, &mdev->newest_tle->requests);
/* NOTE remote first: to get the concurrent write detection right,
* we must register the request before start of local IO. */
@@ -1014,6 +1034,14 @@ allocate_barrier:
mdev->net_conf->on_congestion != OC_BLOCK && mdev->agreed_pro_version >= 96)
maybe_pull_ahead(mdev);
+ /* If this was a flush, queue a drbd barrier/start a new epoch.
+ * Unless the current epoch was empty anyways, or we are not currently
+ * replicating, in which case there is no point. */
+ if (unlikely(bio->bi_rw & REQ_FLUSH)
+ && mdev->newest_tle->n_writes
+ && drbd_should_do_remote(mdev->state))
+ queue_barrier(mdev);
+
spin_unlock_irq(&mdev->req_lock);
kfree(b); /* if someone else has beaten us to it... */
@@ -1111,13 +1139,12 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
/*
* what we "blindly" assume:
*/
- D_ASSERT(bio->bi_size > 0);
D_ASSERT((bio->bi_size & 0x1ff) == 0);
/* to make some things easier, force alignment of requests within the
* granularity of our hash tables */
s_enr = bio->bi_sector >> HT_SHIFT;
- e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
+ e_enr = bio->bi_size ? (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT : s_enr;
if (likely(s_enr == e_enr)) {
do {
@@ -1275,7 +1302,7 @@ void request_timer_fn(unsigned long data)
time_after(now, req->start_time + dt) &&
!time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) {
dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n");
- __drbd_chk_io_error(mdev, 1);
+ __drbd_chk_io_error(mdev, DRBD_FORCE_DETACH);
}
nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
spin_unlock_irq(&mdev->req_lock);
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c
index 620c70ff2231..6bce2cc179d4 100644
--- a/drivers/block/drbd/drbd_worker.c
+++ b/drivers/block/drbd/drbd_worker.c
@@ -111,7 +111,7 @@ void drbd_endio_read_sec_final(struct drbd_epoch_entry *e) __releases(local)
if (list_empty(&mdev->read_ee))
wake_up(&mdev->ee_wait);
if (test_bit(__EE_WAS_ERROR, &e->flags))
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
spin_unlock_irqrestore(&mdev->req_lock, flags);
drbd_queue_work(&mdev->data.work, &e->w);
@@ -154,7 +154,7 @@ static void drbd_endio_write_sec_final(struct drbd_epoch_entry *e) __releases(lo
: list_empty(&mdev->active_ee);
if (test_bit(__EE_WAS_ERROR, &e->flags))
- __drbd_chk_io_error(mdev, false);
+ __drbd_chk_io_error(mdev, DRBD_IO_ERROR);
spin_unlock_irqrestore(&mdev->req_lock, flags);
if (is_syncer_req)
@@ -1501,14 +1501,6 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
return;
}
- if (mdev->state.conn < C_AHEAD) {
- /* In case a previous resync run was aborted by an IO error/detach on the peer. */
- drbd_rs_cancel_all(mdev);
- /* This should be done when we abort the resync. We definitely do not
- want to have this for connections going back and forth between
- Ahead/Behind and SyncSource/SyncTarget */
- }
-
if (side == C_SYNC_TARGET) {
/* Since application IO was locked out during C_WF_BITMAP_T and
C_WF_SYNC_UUID we are still unmodified. Before going to C_SYNC_TARGET
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 553f43a90953..a7d6347aaa79 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -191,6 +191,7 @@ static int print_unex = 1;
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/uaccess.h>
+#include <linux/async.h>
/*
* PS/2 floppies have much slower step rates than regular floppies.
@@ -2516,8 +2517,7 @@ static int make_raw_rw_request(void)
set_fdc((long)current_req->rq_disk->private_data);
raw_cmd = &default_raw_cmd;
- raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
- FD_RAW_NEED_SEEK;
+ raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
raw_cmd->cmd_count = NR_RW;
if (rq_data_dir(current_req) == READ) {
raw_cmd->flags |= FD_RAW_READ;
@@ -4123,7 +4123,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
return get_disk(disks[drive]);
}
-static int __init floppy_init(void)
+static int __init do_floppy_init(void)
{
int i, unit, drive;
int err, dr;
@@ -4338,6 +4338,24 @@ out_put_disk:
return err;
}
+#ifndef MODULE
+static __init void floppy_async_init(void *data, async_cookie_t cookie)
+{
+ do_floppy_init();
+}
+#endif
+
+static int __init floppy_init(void)
+{
+#ifdef MODULE
+ return do_floppy_init();
+#else
+ /* Don't hold up the bootup by the floppy initialization */
+ async_schedule(floppy_async_init, NULL);
+ return 0;
+#endif
+}
+
static const struct io_region {
int offset;
int size;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index a8fddeb3d638..f946d31d6917 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -1148,11 +1148,15 @@ static bool mtip_pause_ncq(struct mtip_port *port,
reply = port->rxfis + RX_FIS_D2H_REG;
task_file_data = readl(port->mmio+PORT_TFDATA);
- if ((task_file_data & 1) || (fis->command == ATA_CMD_SEC_ERASE_UNIT))
+ if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
+ clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+
+ if ((task_file_data & 1))
return false;
if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
port->ic_pause_timer = jiffies;
return true;
} else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1900,7 +1904,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
int rv = 0, xfer_sz = command[3];
if (xfer_sz) {
- if (user_buffer)
+ if (!user_buffer)
return -EFAULT;
buf = dmam_alloc_coherent(&port->dd->pdev->dev,
@@ -2043,7 +2047,7 @@ static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
*timeout = 240000; /* 4 minutes */
break;
case ATA_CMD_STANDBYNOW1:
- *timeout = 10000; /* 10 seconds */
+ *timeout = 120000; /* 2 minutes */
break;
case 0xF7:
case 0xFA:
@@ -2588,9 +2592,6 @@ static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "H/ S ACTive : [ 0x");
for (n = dd->slot_groups-1; n >= 0; n--)
@@ -2660,9 +2661,6 @@ static ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n",
dd->port->flags);
size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n",
@@ -3214,8 +3212,8 @@ static int mtip_hw_init(struct driver_data *dd)
"Unable to check write protect progress\n");
else
dev_info(&dd->pdev->dev,
- "Write protect progress: %d%% (%d blocks)\n",
- attr242.cur, attr242.data);
+ "Write protect progress: %u%% (%u blocks)\n",
+ attr242.cur, le32_to_cpu(attr242.data));
return rv;
out3:
@@ -3619,6 +3617,10 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
bio_endio(bio, -ENODATA);
return;
}
+ if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))) {
+ bio_endio(bio, -ENODATA);
+ return;
+ }
}
if (unlikely(!bio_has_data(bio))) {
@@ -4168,7 +4170,13 @@ static void mtip_pci_shutdown(struct pci_dev *pdev)
/* Table of device ids supported by this driver. */
static DEFINE_PCI_DEVICE_TABLE(mtip_pci_tbl) = {
- { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) },
{ 0 }
};
@@ -4199,12 +4207,12 @@ static int __init mtip_init(void)
{
int error;
- printk(KERN_INFO MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
+ pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
/* Allocate a major block device number to use with this driver. */
error = register_blkdev(0, MTIP_DRV_NAME);
if (error <= 0) {
- printk(KERN_ERR "Unable to register block device (%d)\n",
+ pr_err("Unable to register block device (%d)\n",
error);
return -EBUSY;
}
@@ -4213,7 +4221,7 @@ static int __init mtip_init(void)
if (!dfs_parent) {
dfs_parent = debugfs_create_dir("rssd", NULL);
if (IS_ERR_OR_NULL(dfs_parent)) {
- printk(KERN_WARNING "Error creating debugfs parent\n");
+ pr_warn("Error creating debugfs parent\n");
dfs_parent = NULL;
}
}
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index f51fc23d17bb..18627a1d04c5 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -76,7 +76,13 @@
/* Micron Vendor ID & P320x SSD Device ID */
#define PCI_VENDOR_ID_MICRON 0x1344
-#define P320_DEVICE_ID 0x5150
+#define P320H_DEVICE_ID 0x5150
+#define P320M_DEVICE_ID 0x5151
+#define P320S_DEVICE_ID 0x5152
+#define P325M_DEVICE_ID 0x5153
+#define P420H_DEVICE_ID 0x5160
+#define P420M_DEVICE_ID 0x5161
+#define P425M_DEVICE_ID 0x5163
/* Driver name and version strings */
#define MTIP_DRV_NAME "mtip32xx"
@@ -131,10 +137,12 @@ enum {
MTIP_PF_SVC_THD_STOP_BIT = 8,
/* below are bit numbers in 'dd_flag' defined in driver_data */
+ MTIP_DDF_SEC_LOCK_BIT = 0,
MTIP_DDF_REMOVE_PENDING_BIT = 1,
MTIP_DDF_OVER_TEMP_BIT = 2,
MTIP_DDF_WRITE_PROTECT_BIT = 3,
MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+ (1 << MTIP_DDF_SEC_LOCK_BIT) | \
(1 << MTIP_DDF_OVER_TEMP_BIT) | \
(1 << MTIP_DDF_WRITE_PROTECT_BIT)),
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 061427a75d37..0c03411c59eb 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -154,6 +154,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
struct msghdr msg;
struct kvec iov;
sigset_t blocked, oldset;
+ unsigned long pflags = current->flags;
if (unlikely(!sock)) {
dev_err(disk_to_dev(nbd->disk),
@@ -167,8 +168,9 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
siginitsetinv(&blocked, sigmask(SIGKILL));
sigprocmask(SIG_SETMASK, &blocked, &oldset);
+ current->flags |= PF_MEMALLOC;
do {
- sock->sk->sk_allocation = GFP_NOIO;
+ sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
iov.iov_base = buf;
iov.iov_len = size;
msg.msg_name = NULL;
@@ -214,6 +216,7 @@ static int sock_xmit(struct nbd_device *nbd, int send, void *buf, int size,
} while (size > 0);
sigprocmask(SIG_SETMASK, &oldset, NULL);
+ tsk_restore_flags(current, pflags, PF_MEMALLOC);
return result;
}
@@ -405,6 +408,7 @@ static int nbd_do_it(struct nbd_device *nbd)
BUG_ON(nbd->magic != NBD_MAGIC);
+ sk_set_memalloc(nbd->sock->sk);
nbd->pid = task_pid_nr(current);
ret = device_create_file(disk_to_dev(nbd->disk), &pid_attr);
if (ret) {
@@ -445,6 +449,14 @@ static void nbd_clear_que(struct nbd_device *nbd)
req->errors++;
nbd_end_request(req);
}
+
+ while (!list_empty(&nbd->waiting_queue)) {
+ req = list_entry(nbd->waiting_queue.next, struct request,
+ queuelist);
+ list_del_init(&req->queuelist);
+ req->errors++;
+ nbd_end_request(req);
+ }
}
@@ -481,7 +493,7 @@ static void nbd_handle_req(struct nbd_device *nbd, struct request *req)
nbd_end_request(req);
} else {
spin_lock(&nbd->queue_lock);
- list_add(&req->queuelist, &nbd->queue_head);
+ list_add_tail(&req->queuelist, &nbd->queue_head);
spin_unlock(&nbd->queue_lock);
}
@@ -594,6 +606,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
nbd->file = NULL;
nbd_clear_que(nbd);
BUG_ON(!list_empty(&nbd->queue_head));
+ BUG_ON(!list_empty(&nbd->waiting_queue));
if (file)
fput(file);
return 0;
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c
index 38a2d0631882..ad16c68c8645 100644
--- a/drivers/block/nvme.c
+++ b/drivers/block/nvme.c
@@ -79,6 +79,7 @@ struct nvme_dev {
char serial[20];
char model[40];
char firmware_rev[8];
+ u32 max_hw_sectors;
};
/*
@@ -835,15 +836,15 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,
}
static int nvme_get_features(struct nvme_dev *dev, unsigned fid,
- unsigned dword11, dma_addr_t dma_addr)
+ unsigned nsid, dma_addr_t dma_addr)
{
struct nvme_command c;
memset(&c, 0, sizeof(c));
c.features.opcode = nvme_admin_get_features;
+ c.features.nsid = cpu_to_le32(nsid);
c.features.prp1 = cpu_to_le64(dma_addr);
c.features.fid = cpu_to_le32(fid);
- c.features.dword11 = cpu_to_le32(dword11);
return nvme_submit_admin_cmd(dev, &c, NULL);
}
@@ -862,11 +863,51 @@ static int nvme_set_features(struct nvme_dev *dev, unsigned fid,
return nvme_submit_admin_cmd(dev, &c, result);
}
+/**
+ * nvme_cancel_ios - Cancel outstanding I/Os
+ * @queue: The queue to cancel I/Os on
+ * @timeout: True to only cancel I/Os which have timed out
+ */
+static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
+{
+ int depth = nvmeq->q_depth - 1;
+ struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+ unsigned long now = jiffies;
+ int cmdid;
+
+ for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
+ void *ctx;
+ nvme_completion_fn fn;
+ static struct nvme_completion cqe = {
+ .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1,
+ };
+
+ if (timeout && !time_after(now, info[cmdid].timeout))
+ continue;
+ dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d\n", cmdid);
+ ctx = cancel_cmdid(nvmeq, cmdid, &fn);
+ fn(nvmeq->dev, ctx, &cqe);
+ }
+}
+
+static void nvme_free_queue_mem(struct nvme_queue *nvmeq)
+{
+ dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
+ (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
+ dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
+ nvmeq->sq_cmds, nvmeq->sq_dma_addr);
+ kfree(nvmeq);
+}
+
static void nvme_free_queue(struct nvme_dev *dev, int qid)
{
struct nvme_queue *nvmeq = dev->queues[qid];
int vector = dev->entry[nvmeq->cq_vector].vector;
+ spin_lock_irq(&nvmeq->q_lock);
+ nvme_cancel_ios(nvmeq, false);
+ spin_unlock_irq(&nvmeq->q_lock);
+
irq_set_affinity_hint(vector, NULL);
free_irq(vector, nvmeq);
@@ -876,18 +917,15 @@ static void nvme_free_queue(struct nvme_dev *dev, int qid)
adapter_delete_cq(dev, qid);
}
- dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
- (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
- dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
- nvmeq->sq_cmds, nvmeq->sq_dma_addr);
- kfree(nvmeq);
+ nvme_free_queue_mem(nvmeq);
}
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
int depth, int vector)
{
struct device *dmadev = &dev->pci_dev->dev;
- unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info));
+ unsigned extra = DIV_ROUND_UP(depth, 8) + (depth *
+ sizeof(struct nvme_cmd_info));
struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);
if (!nvmeq)
return NULL;
@@ -975,7 +1013,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev,
static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
{
- int result;
+ int result = 0;
u32 aqa;
u64 cap;
unsigned long timeout;
@@ -1005,17 +1043,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
dev->db_stride = NVME_CAP_STRIDE(cap);
- while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
+ while (!result && !(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
msleep(100);
if (fatal_signal_pending(current))
- return -EINTR;
+ result = -EINTR;
if (time_after(jiffies, timeout)) {
dev_err(&dev->pci_dev->dev,
"Device not ready; aborting initialisation\n");
- return -ENODEV;
+ result = -ENODEV;
}
}
+ if (result) {
+ nvme_free_queue_mem(nvmeq);
+ return result;
+ }
+
result = queue_request_irq(dev, nvmeq, "nvme admin");
dev->queues[0] = nvmeq;
return result;
@@ -1037,6 +1080,8 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
offset = offset_in_page(addr);
count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
err = get_user_pages_fast(addr, count, 1, pages);
if (err < count) {
@@ -1146,14 +1191,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
return status;
}
-static int nvme_user_admin_cmd(struct nvme_ns *ns,
+static int nvme_user_admin_cmd(struct nvme_dev *dev,
struct nvme_admin_cmd __user *ucmd)
{
- struct nvme_dev *dev = ns->dev;
struct nvme_admin_cmd cmd;
struct nvme_command c;
int status, length;
- struct nvme_iod *iod;
+ struct nvme_iod *uninitialized_var(iod);
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -1204,7 +1248,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
case NVME_IOCTL_ID:
return ns->ns_id;
case NVME_IOCTL_ADMIN_CMD:
- return nvme_user_admin_cmd(ns, (void __user *)arg);
+ return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
case NVME_IOCTL_SUBMIT_IO:
return nvme_submit_io(ns, (void __user *)arg);
default:
@@ -1218,26 +1262,6 @@ static const struct block_device_operations nvme_fops = {
.compat_ioctl = nvme_ioctl,
};
-static void nvme_timeout_ios(struct nvme_queue *nvmeq)
-{
- int depth = nvmeq->q_depth - 1;
- struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
- unsigned long now = jiffies;
- int cmdid;
-
- for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
- void *ctx;
- nvme_completion_fn fn;
- static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, };
-
- if (!time_after(now, info[cmdid].timeout))
- continue;
- dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid);
- ctx = cancel_cmdid(nvmeq, cmdid, &fn);
- fn(nvmeq->dev, ctx, &cqe);
- }
-}
-
static void nvme_resubmit_bios(struct nvme_queue *nvmeq)
{
while (bio_list_peek(&nvmeq->sq_cong)) {
@@ -1269,7 +1293,7 @@ static int nvme_kthread(void *data)
spin_lock_irq(&nvmeq->q_lock);
if (nvme_process_cq(nvmeq))
printk("process_cq did something\n");
- nvme_timeout_ios(nvmeq);
+ nvme_cancel_ios(nvmeq, true);
nvme_resubmit_bios(nvmeq);
spin_unlock_irq(&nvmeq->q_lock);
}
@@ -1339,6 +1363,9 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid,
ns->disk = disk;
lbaf = id->flbas & 0xf;
ns->lba_shift = id->lbaf[lbaf].ds;
+ blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
+ if (dev->max_hw_sectors)
+ blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
disk->major = nvme_major;
disk->minors = NVME_MINORS;
@@ -1383,7 +1410,7 @@ static int set_queue_count(struct nvme_dev *dev, int count)
static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
{
- int result, cpu, i, nr_io_queues, db_bar_size;
+ int result, cpu, i, nr_io_queues, db_bar_size, q_depth;
nr_io_queues = num_online_cpus();
result = set_queue_count(dev, nr_io_queues);
@@ -1429,9 +1456,10 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
cpu = cpumask_next(cpu, cpu_online_mask);
}
+ q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1,
+ NVME_Q_DEPTH);
for (i = 0; i < nr_io_queues; i++) {
- dev->queues[i + 1] = nvme_create_queue(dev, i + 1,
- NVME_Q_DEPTH, i);
+ dev->queues[i + 1] = nvme_create_queue(dev, i + 1, q_depth, i);
if (IS_ERR(dev->queues[i + 1]))
return PTR_ERR(dev->queues[i + 1]);
dev->queue_count++;
@@ -1480,6 +1508,10 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev)
memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));
memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn));
memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));
+ if (ctrl->mdts) {
+ int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
+ dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
+ }
id_ns = mem;
for (i = 1; i <= nn; i++) {
@@ -1523,8 +1555,6 @@ static int nvme_dev_remove(struct nvme_dev *dev)
list_del(&dev->node);
spin_unlock(&dev_list_lock);
- /* TODO: wait all I/O finished or cancel them */
-
list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
list_del(&ns->list);
del_gendisk(ns->disk);
@@ -1560,15 +1590,33 @@ static void nvme_release_prp_pools(struct nvme_dev *dev)
dma_pool_destroy(dev->prp_small_pool);
}
-/* XXX: Use an ida or something to let remove / add work correctly */
-static void nvme_set_instance(struct nvme_dev *dev)
+static DEFINE_IDA(nvme_instance_ida);
+
+static int nvme_set_instance(struct nvme_dev *dev)
{
- static int instance;
- dev->instance = instance++;
+ int instance, error;
+
+ do {
+ if (!ida_pre_get(&nvme_instance_ida, GFP_KERNEL))
+ return -ENODEV;
+
+ spin_lock(&dev_list_lock);
+ error = ida_get_new(&nvme_instance_ida, &instance);
+ spin_unlock(&dev_list_lock);
+ } while (error == -EAGAIN);
+
+ if (error)
+ return -ENODEV;
+
+ dev->instance = instance;
+ return 0;
}
static void nvme_release_instance(struct nvme_dev *dev)
{
+ spin_lock(&dev_list_lock);
+ ida_remove(&nvme_instance_ida, dev->instance);
+ spin_unlock(&dev_list_lock);
}
static int __devinit nvme_probe(struct pci_dev *pdev,
@@ -1601,7 +1649,10 @@ static int __devinit nvme_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, dev);
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- nvme_set_instance(dev);
+ result = nvme_set_instance(dev);
+ if (result)
+ goto disable;
+
dev->entry[0].vector = pdev->irq;
result = nvme_setup_prp_pools(dev);
@@ -1704,15 +1755,17 @@ static struct pci_driver nvme_driver = {
static int __init nvme_init(void)
{
- int result = -EBUSY;
+ int result;
nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
if (IS_ERR(nvme_thread))
return PTR_ERR(nvme_thread);
- nvme_major = register_blkdev(nvme_major, "nvme");
- if (nvme_major <= 0)
+ result = register_blkdev(nvme_major, "nvme");
+ if (result < 0)
goto kill_kthread;
+ else if (result > 0)
+ nvme_major = result;
result = pci_register_driver(&nvme_driver);
if (result)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 9917943a3572..54a55f03115d 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -246,13 +246,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
{
struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
- rbd_get_dev(rbd_dev);
-
- set_device_ro(bdev, rbd_dev->read_only);
-
if ((mode & FMODE_WRITE) && rbd_dev->read_only)
return -EROFS;
+ rbd_get_dev(rbd_dev);
+ set_device_ro(bdev, rbd_dev->read_only);
+
return 0;
}
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 9a72277a31df..eb0d8216f557 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -513,42 +513,19 @@ static void process_page(unsigned long data)
}
}
-struct mm_plug_cb {
- struct blk_plug_cb cb;
- struct cardinfo *card;
-};
-
-static void mm_unplug(struct blk_plug_cb *cb)
+static void mm_unplug(struct blk_plug_cb *cb, bool from_schedule)
{
- struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb);
+ struct cardinfo *card = cb->data;
- spin_lock_irq(&mmcb->card->lock);
- activate(mmcb->card);
- spin_unlock_irq(&mmcb->card->lock);
- kfree(mmcb);
+ spin_lock_irq(&card->lock);
+ activate(card);
+ spin_unlock_irq(&card->lock);
+ kfree(cb);
}
static int mm_check_plugged(struct cardinfo *card)
{
- struct blk_plug *plug = current->plug;
- struct mm_plug_cb *mmcb;
-
- if (!plug)
- return 0;
-
- list_for_each_entry(mmcb, &plug->cb_list, cb.list) {
- if (mmcb->cb.callback == mm_unplug && mmcb->card == card)
- return 1;
- }
- /* Not currently on the callback list */
- mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC);
- if (!mmcb)
- return 0;
-
- mmcb->card = card;
- mmcb->cb.callback = mm_unplug;
- list_add(&mmcb->cb.list, &plug->cb_list);
- return 1;
+ return !!blk_check_plugged(mm_unplug, card, sizeof(struct blk_plug_cb));
}
static void mm_make_request(struct request_queue *q, struct bio *bio)
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 73f196ca713f..c6decb901e5e 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -337,7 +337,7 @@ static void xen_blkbk_unmap(struct pending_req *req)
invcount++;
}
- ret = gnttab_unmap_refs(unmap, pages, invcount, false);
+ ret = gnttab_unmap_refs(unmap, NULL, pages, invcount);
BUG_ON(ret);
}
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index e4fb3374dcd2..2c2d2e5c1597 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -888,9 +888,8 @@ static int setup_blkring(struct xenbus_device *dev,
if (err)
goto fail;
- err = bind_evtchn_to_irqhandler(info->evtchn,
- blkif_interrupt,
- IRQF_SAMPLE_RANDOM, "blkif", info);
+ err = bind_evtchn_to_irqhandler(info->evtchn, blkif_interrupt, 0,
+ "blkif", info);
if (err <= 0) {
xenbus_dev_fatal(dev, err,
"bind_evtchn_to_irqhandler failed");
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 10308cd8a7ed..fc2de5528dcc 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -79,12 +79,14 @@ static struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0930, 0x0219) },
+ { USB_DEVICE(0x0489, 0xe057) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C) },
+ { USB_DEVICE(0x0489, 0xE036) },
{ } /* Terminating entry */
};
@@ -104,9 +106,11 @@ static struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
{ } /* Terminating entry */
};
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index e27221411036..654e248763ef 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -52,6 +52,9 @@ static struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+ /* Apple-specific (Broadcom) devices */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) },
+
/* Broadcom SoftSailing reporting vendor specific */
{ USB_DEVICE(0x0a5c, 0x21e1) },
@@ -94,15 +97,14 @@ static struct usb_device_id btusb_table[] = {
/* Broadcom BCM20702A0 */
{ USB_DEVICE(0x0489, 0xe042) },
- { USB_DEVICE(0x0a5c, 0x21e3) },
- { USB_DEVICE(0x0a5c, 0x21e6) },
- { USB_DEVICE(0x0a5c, 0x21e8) },
- { USB_DEVICE(0x0a5c, 0x21f3) },
{ USB_DEVICE(0x413c, 0x8197) },
/* Foxconn - Hon Hai */
{ USB_DEVICE(0x0489, 0xe033) },
+ /*Broadcom devices with vendor specific id */
+ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
+
{ } /* Terminating entry */
};
@@ -133,12 +135,14 @@ static struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 57226424690c..6ec0fff79bc2 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -64,6 +64,7 @@
#define I830_PTE_SYSTEM_CACHED 0x00000006
/* GT PTE cache control fields */
#define GEN6_PTE_UNCACHED 0x00000002
+#define HSW_PTE_UNCACHED 0x00000000
#define GEN6_PTE_LLC 0x00000004
#define GEN6_PTE_LLC_MLC 0x00000006
#define GEN6_PTE_GFDT 0x00000008
@@ -239,16 +240,45 @@
#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG 0x016A
#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_HB 0x0F00 /* VLV1 */
#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG 0x0F30
-#define PCI_DEVICE_ID_INTEL_HASWELL_HB 0x0400 /* Desktop */
+#define PCI_DEVICE_ID_INTEL_HASWELL_HB 0x0400 /* Desktop */
#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG 0x0402
#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG 0x0412
-#define PCI_DEVICE_ID_INTEL_HASWELL_M_HB 0x0404 /* Mobile */
+#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG 0x0422
+#define PCI_DEVICE_ID_INTEL_HASWELL_M_HB 0x0404 /* Mobile */
#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG 0x0406
#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG 0x0416
-#define PCI_DEVICE_ID_INTEL_HASWELL_S_HB 0x0408 /* Server */
+#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG 0x0426
+#define PCI_DEVICE_ID_INTEL_HASWELL_S_HB 0x0408 /* Server */
#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG 0x040a
#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG 0x041a
-#define PCI_DEVICE_ID_INTEL_HASWELL_SDV 0x0c16 /* SDV */
-#define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04
+#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG 0x042a
+#define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG 0x0C02
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG 0x0C12
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG 0x0C22
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG 0x0C06
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG 0x0C16
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG 0x0C26
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG 0x0C0A
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG 0x0C1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG 0x0C2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG 0x0A02
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG 0x0A12
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG 0x0A22
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG 0x0A06
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG 0x0A16
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG 0x0A26
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG 0x0A0A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG 0x0A1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG 0x0A2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG 0x0D12
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG 0x0D22
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG 0x0D32
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG 0x0D16
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG 0x0D26
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG 0x0D36
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG 0x0D1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG 0x0D2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG 0x0D3A
#endif
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 9ed92ef5829b..58e32f7c3229 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -1156,6 +1156,30 @@ static bool gen6_check_flags(unsigned int flags)
return true;
}
+static void haswell_write_entry(dma_addr_t addr, unsigned int entry,
+ unsigned int flags)
+{
+ unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT;
+ unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT;
+ u32 pte_flags;
+
+ if (type_mask == AGP_USER_MEMORY)
+ pte_flags = HSW_PTE_UNCACHED | I810_PTE_VALID;
+ else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) {
+ pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
+ } else { /* set 'normal'/'cached' to LLC by default */
+ pte_flags = GEN6_PTE_LLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
+ }
+
+ /* gen6 has bit11-4 for physical addr bit39-32 */
+ addr |= (addr >> 28) & 0xff0;
+ writel(addr | pte_flags, intel_private.gtt + entry);
+}
+
static void gen6_write_entry(dma_addr_t addr, unsigned int entry,
unsigned int flags)
{
@@ -1382,6 +1406,15 @@ static const struct intel_gtt_driver sandybridge_gtt_driver = {
.check_flags = gen6_check_flags,
.chipset_flush = i9xx_chipset_flush,
};
+static const struct intel_gtt_driver haswell_gtt_driver = {
+ .gen = 6,
+ .setup = i9xx_setup,
+ .cleanup = gen6_cleanup,
+ .write_entry = haswell_write_entry,
+ .dma_mask_size = 40,
+ .check_flags = gen6_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
static const struct intel_gtt_driver valleyview_gtt_driver = {
.gen = 7,
.setup = i9xx_setup,
@@ -1499,19 +1532,77 @@ static const struct intel_gtt_driver_description {
{ PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG,
"ValleyView", &valleyview_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
- { PCI_DEVICE_ID_INTEL_HASWELL_SDV,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ 0, NULL, NULL }
};
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index d706bd0e9e80..4fbdceb6f773 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -160,7 +160,7 @@ static int __exit omap_rng_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int omap_rng_suspend(struct device *dev)
{
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 4ec04a754733..b86eae9b77df 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -125,21 +125,26 @@
* The current exported interfaces for gathering environmental noise
* from the devices are:
*
+ * void add_device_randomness(const void *buf, unsigned int size);
* void add_input_randomness(unsigned int type, unsigned int code,
* unsigned int value);
- * void add_interrupt_randomness(int irq);
+ * void add_interrupt_randomness(int irq, int irq_flags);
* void add_disk_randomness(struct gendisk *disk);
*
+ * add_device_randomness() is for adding data to the random pool that
+ * is likely to differ between two devices (or possibly even per boot).
+ * This would be things like MAC addresses or serial numbers, or the
+ * read-out of the RTC. This does *not* add any actual entropy to the
+ * pool, but it initializes the pool to different values for devices
+ * that might otherwise be identical and have very little entropy
+ * available to them (particularly common in the embedded world).
+ *
* add_input_randomness() uses the input layer interrupt timing, as well as
* the event type information from the hardware.
*
- * add_interrupt_randomness() uses the inter-interrupt timing as random
- * inputs to the entropy pool. Note that not all interrupts are good
- * sources of randomness! For example, the timer interrupts is not a
- * good choice, because the periodicity of the interrupts is too
- * regular, and hence predictable to an attacker. Network Interface
- * Controller interrupts are a better measure, since the timing of the
- * NIC interrupts are more unpredictable.
+ * add_interrupt_randomness() uses the interrupt timing as random
+ * inputs to the entropy pool. Using the cycle counters and the irq source
+ * as inputs, it feeds the randomness roughly once a second.
*
* add_disk_randomness() uses what amounts to the seek time of block
* layer request events, on a per-disk_devt basis, as input to the
@@ -248,6 +253,8 @@
#include <linux/percpu.h>
#include <linux/cryptohash.h>
#include <linux/fips.h>
+#include <linux/ptrace.h>
+#include <linux/kmemcheck.h>
#ifdef CONFIG_GENERIC_HARDIRQS
# include <linux/irq.h>
@@ -256,8 +263,12 @@
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
+#include <asm/irq_regs.h>
#include <asm/io.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/random.h>
+
/*
* Configuration information
*/
@@ -266,6 +277,8 @@
#define SEC_XFER_SIZE 512
#define EXTRACT_SIZE 10
+#define LONGS(x) (((x) + sizeof(unsigned long) - 1)/sizeof(unsigned long))
+
/*
* The minimum number of bits of entropy before we wake up a read on
* /dev/random. Should be enough to do a significant reseed.
@@ -420,8 +433,10 @@ struct entropy_store {
/* read-write data: */
spinlock_t lock;
unsigned add_ptr;
+ unsigned input_rotate;
int entropy_count;
- int input_rotate;
+ int entropy_total;
+ unsigned int initialized:1;
__u8 last_data[EXTRACT_SIZE];
};
@@ -454,6 +469,10 @@ static struct entropy_store nonblocking_pool = {
.pool = nonblocking_pool_data
};
+static __u32 const twist_table[8] = {
+ 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+
/*
* This function adds bytes into the entropy "pool". It does not
* update the entropy estimate. The caller should call
@@ -464,29 +483,24 @@ static struct entropy_store nonblocking_pool = {
* it's cheap to do so and helps slightly in the expected case where
* the entropy is concentrated in the low-order bits.
*/
-static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
- int nbytes, __u8 out[64])
+static void _mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
{
- static __u32 const twist_table[8] = {
- 0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
- 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
unsigned long i, j, tap1, tap2, tap3, tap4, tap5;
int input_rotate;
int wordmask = r->poolinfo->poolwords - 1;
const char *bytes = in;
__u32 w;
- unsigned long flags;
- /* Taps are constant, so we can load them without holding r->lock. */
tap1 = r->poolinfo->tap1;
tap2 = r->poolinfo->tap2;
tap3 = r->poolinfo->tap3;
tap4 = r->poolinfo->tap4;
tap5 = r->poolinfo->tap5;
- spin_lock_irqsave(&r->lock, flags);
- input_rotate = r->input_rotate;
- i = r->add_ptr;
+ smp_rmb();
+ input_rotate = ACCESS_ONCE(r->input_rotate);
+ i = ACCESS_ONCE(r->add_ptr);
/* mix one byte at a time to simplify size handling and churn faster */
while (nbytes--) {
@@ -513,19 +527,61 @@ static void mix_pool_bytes_extract(struct entropy_store *r, const void *in,
input_rotate += i ? 7 : 14;
}
- r->input_rotate = input_rotate;
- r->add_ptr = i;
+ ACCESS_ONCE(r->input_rotate) = input_rotate;
+ ACCESS_ONCE(r->add_ptr) = i;
+ smp_wmb();
if (out)
for (j = 0; j < 16; j++)
((__u32 *)out)[j] = r->pool[(i - j) & wordmask];
+}
+
+static void __mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
+{
+ trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_);
+ _mix_pool_bytes(r, in, nbytes, out);
+}
+
+static void mix_pool_bytes(struct entropy_store *r, const void *in,
+ int nbytes, __u8 out[64])
+{
+ unsigned long flags;
+ trace_mix_pool_bytes(r->name, nbytes, _RET_IP_);
+ spin_lock_irqsave(&r->lock, flags);
+ _mix_pool_bytes(r, in, nbytes, out);
spin_unlock_irqrestore(&r->lock, flags);
}
-static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
+struct fast_pool {
+ __u32 pool[4];
+ unsigned long last;
+ unsigned short count;
+ unsigned char rotate;
+ unsigned char last_timer_intr;
+};
+
+/*
+ * This is a fast mixing routine used by the interrupt randomness
+ * collector. It's hardcoded for an 128 bit pool and assumes that any
+ * locks that might be needed are taken by the caller.
+ */
+static void fast_mix(struct fast_pool *f, const void *in, int nbytes)
{
- mix_pool_bytes_extract(r, in, bytes, NULL);
+ const char *bytes = in;
+ __u32 w;
+ unsigned i = f->count;
+ unsigned input_rotate = f->rotate;
+
+ while (nbytes--) {
+ w = rol32(*bytes++, input_rotate & 31) ^ f->pool[i & 3] ^
+ f->pool[(i + 1) & 3];
+ f->pool[i & 3] = (w >> 3) ^ twist_table[w & 7];
+ input_rotate += (i++ & 3) ? 7 : 14;
+ }
+ f->count = i;
+ f->rotate = input_rotate;
}
/*
@@ -533,30 +589,38 @@ static void mix_pool_bytes(struct entropy_store *r, const void *in, int bytes)
*/
static void credit_entropy_bits(struct entropy_store *r, int nbits)
{
- unsigned long flags;
- int entropy_count;
+ int entropy_count, orig;
if (!nbits)
return;
- spin_lock_irqsave(&r->lock, flags);
-
DEBUG_ENT("added %d entropy credits to %s\n", nbits, r->name);
- entropy_count = r->entropy_count;
+retry:
+ entropy_count = orig = ACCESS_ONCE(r->entropy_count);
entropy_count += nbits;
+
if (entropy_count < 0) {
DEBUG_ENT("negative entropy/overflow\n");
entropy_count = 0;
} else if (entropy_count > r->poolinfo->POOLBITS)
entropy_count = r->poolinfo->POOLBITS;
- r->entropy_count = entropy_count;
+ if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
+ goto retry;
+
+ if (!r->initialized && nbits > 0) {
+ r->entropy_total += nbits;
+ if (r->entropy_total > 128)
+ r->initialized = 1;
+ }
+
+ trace_credit_entropy_bits(r->name, nbits, entropy_count,
+ r->entropy_total, _RET_IP_);
/* should we wake readers? */
if (r == &input_pool && entropy_count >= random_read_wakeup_thresh) {
wake_up_interruptible(&random_read_wait);
kill_fasync(&fasync, SIGIO, POLL_IN);
}
- spin_unlock_irqrestore(&r->lock, flags);
}
/*********************************************************************
@@ -572,42 +636,24 @@ struct timer_rand_state {
unsigned dont_count_entropy:1;
};
-#ifndef CONFIG_GENERIC_HARDIRQS
-
-static struct timer_rand_state *irq_timer_state[NR_IRQS];
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
- return irq_timer_state[irq];
-}
-
-static void set_timer_rand_state(unsigned int irq,
- struct timer_rand_state *state)
-{
- irq_timer_state[irq] = state;
-}
-
-#else
-
-static struct timer_rand_state *get_timer_rand_state(unsigned int irq)
-{
- struct irq_desc *desc;
-
- desc = irq_to_desc(irq);
-
- return desc->timer_rand_state;
-}
-
-static void set_timer_rand_state(unsigned int irq,
- struct timer_rand_state *state)
+/*
+ * Add device- or boot-specific data to the input and nonblocking
+ * pools to help initialize them to unique values.
+ *
+ * None of this adds any entropy, it is meant to avoid the
+ * problem of the nonblocking pool having similar initial state
+ * across largely identical devices.
+ */
+void add_device_randomness(const void *buf, unsigned int size)
{
- struct irq_desc *desc;
+ unsigned long time = get_cycles() ^ jiffies;
- desc = irq_to_desc(irq);
-
- desc->timer_rand_state = state;
+ mix_pool_bytes(&input_pool, buf, size, NULL);
+ mix_pool_bytes(&input_pool, &time, sizeof(time), NULL);
+ mix_pool_bytes(&nonblocking_pool, buf, size, NULL);
+ mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL);
}
-#endif
+EXPORT_SYMBOL(add_device_randomness);
static struct timer_rand_state input_timer_state;
@@ -637,13 +683,9 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
goto out;
sample.jiffies = jiffies;
-
- /* Use arch random value, fall back to cycles */
- if (!arch_get_random_int(&sample.cycles))
- sample.cycles = get_cycles();
-
+ sample.cycles = get_cycles();
sample.num = num;
- mix_pool_bytes(&input_pool, &sample, sizeof(sample));
+ mix_pool_bytes(&input_pool, &sample, sizeof(sample), NULL);
/*
* Calculate number of bits of randomness we probably added.
@@ -700,17 +742,48 @@ void add_input_randomness(unsigned int type, unsigned int code,
}
EXPORT_SYMBOL_GPL(add_input_randomness);
-void add_interrupt_randomness(int irq)
+static DEFINE_PER_CPU(struct fast_pool, irq_randomness);
+
+void add_interrupt_randomness(int irq, int irq_flags)
{
- struct timer_rand_state *state;
+ struct entropy_store *r;
+ struct fast_pool *fast_pool = &__get_cpu_var(irq_randomness);
+ struct pt_regs *regs = get_irq_regs();
+ unsigned long now = jiffies;
+ __u32 input[4], cycles = get_cycles();
+
+ input[0] = cycles ^ jiffies;
+ input[1] = irq;
+ if (regs) {
+ __u64 ip = instruction_pointer(regs);
+ input[2] = ip;
+ input[3] = ip >> 32;
+ }
- state = get_timer_rand_state(irq);
+ fast_mix(fast_pool, input, sizeof(input));
- if (state == NULL)
+ if ((fast_pool->count & 1023) &&
+ !time_after(now, fast_pool->last + HZ))
return;
- DEBUG_ENT("irq event %d\n", irq);
- add_timer_randomness(state, 0x100 + irq);
+ fast_pool->last = now;
+
+ r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool;
+ __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL);
+ /*
+ * If we don't have a valid cycle counter, and we see
+ * back-to-back timer interrupts, then skip giving credit for
+ * any entropy.
+ */
+ if (cycles == 0) {
+ if (irq_flags & __IRQF_TIMER) {
+ if (fast_pool->last_timer_intr)
+ return;
+ fast_pool->last_timer_intr = 1;
+ } else
+ fast_pool->last_timer_intr = 0;
+ }
+ credit_entropy_bits(r, 1);
}
#ifdef CONFIG_BLOCK
@@ -742,7 +815,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
*/
static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
{
- __u32 tmp[OUTPUT_POOL_WORDS];
+ __u32 tmp[OUTPUT_POOL_WORDS];
if (r->pull && r->entropy_count < nbytes * 8 &&
r->entropy_count < r->poolinfo->POOLBITS) {
@@ -761,7 +834,7 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes)
bytes = extract_entropy(r->pull, tmp, bytes,
random_read_wakeup_thresh / 8, rsvd);
- mix_pool_bytes(r, tmp, bytes);
+ mix_pool_bytes(r, tmp, bytes, NULL);
credit_entropy_bits(r, bytes*8);
}
}
@@ -820,13 +893,19 @@ static size_t account(struct entropy_store *r, size_t nbytes, int min,
static void extract_buf(struct entropy_store *r, __u8 *out)
{
int i;
- __u32 hash[5], workspace[SHA_WORKSPACE_WORDS];
+ union {
+ __u32 w[5];
+ unsigned long l[LONGS(EXTRACT_SIZE)];
+ } hash;
+ __u32 workspace[SHA_WORKSPACE_WORDS];
__u8 extract[64];
+ unsigned long flags;
/* Generate a hash across the pool, 16 words (512 bits) at a time */
- sha_init(hash);
+ sha_init(hash.w);
+ spin_lock_irqsave(&r->lock, flags);
for (i = 0; i < r->poolinfo->poolwords; i += 16)
- sha_transform(hash, (__u8 *)(r->pool + i), workspace);
+ sha_transform(hash.w, (__u8 *)(r->pool + i), workspace);
/*
* We mix the hash back into the pool to prevent backtracking
@@ -837,13 +916,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
* brute-forcing the feedback as hard as brute-forcing the
* hash.
*/
- mix_pool_bytes_extract(r, hash, sizeof(hash), extract);
+ __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract);
+ spin_unlock_irqrestore(&r->lock, flags);
/*
* To avoid duplicates, we atomically extract a portion of the
* pool while mixing, and hash one final time.
*/
- sha_transform(hash, extract, workspace);
+ sha_transform(hash.w, extract, workspace);
memset(extract, 0, sizeof(extract));
memset(workspace, 0, sizeof(workspace));
@@ -852,20 +932,32 @@ static void extract_buf(struct entropy_store *r, __u8 *out)
* pattern, we fold it in half. Thus, we always feed back
* twice as much data as we output.
*/
- hash[0] ^= hash[3];
- hash[1] ^= hash[4];
- hash[2] ^= rol32(hash[2], 16);
- memcpy(out, hash, EXTRACT_SIZE);
- memset(hash, 0, sizeof(hash));
+ hash.w[0] ^= hash.w[3];
+ hash.w[1] ^= hash.w[4];
+ hash.w[2] ^= rol32(hash.w[2], 16);
+
+ /*
+ * If we have a architectural hardware random number
+ * generator, mix that in, too.
+ */
+ for (i = 0; i < LONGS(EXTRACT_SIZE); i++) {
+ unsigned long v;
+ if (!arch_get_random_long(&v))
+ break;
+ hash.l[i] ^= v;
+ }
+
+ memcpy(out, &hash, EXTRACT_SIZE);
+ memset(&hash, 0, sizeof(hash));
}
static ssize_t extract_entropy(struct entropy_store *r, void *buf,
- size_t nbytes, int min, int reserved)
+ size_t nbytes, int min, int reserved)
{
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
- unsigned long flags;
+ trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, min, reserved);
@@ -873,6 +965,8 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
extract_buf(r, tmp);
if (fips_enabled) {
+ unsigned long flags;
+
spin_lock_irqsave(&r->lock, flags);
if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
panic("Hardware RNG duplicated output!\n");
@@ -898,6 +992,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
ssize_t ret = 0, i;
__u8 tmp[EXTRACT_SIZE];
+ trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
xfer_secondary_pool(r, nbytes);
nbytes = account(r, nbytes, 0, 0);
@@ -931,17 +1026,35 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
/*
* This function is the exported kernel interface. It returns some
- * number of good random numbers, suitable for seeding TCP sequence
- * numbers, etc.
+ * number of good random numbers, suitable for key generation, seeding
+ * TCP sequence numbers, etc. It does not use the hw random number
+ * generator, if available; use get_random_bytes_arch() for that.
*/
void get_random_bytes(void *buf, int nbytes)
{
+ extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
+}
+EXPORT_SYMBOL(get_random_bytes);
+
+/*
+ * This function will use the architecture-specific hardware random
+ * number generator if it is available. The arch-specific hw RNG will
+ * almost certainly be faster than what we can do in software, but it
+ * is impossible to verify that it is implemented securely (as
+ * opposed, to, say, the AES encryption of a sequence number using a
+ * key known by the NSA). So it's useful if we need the speed, but
+ * only if we're willing to trust the hardware manufacturer not to
+ * have put in a back door.
+ */
+void get_random_bytes_arch(void *buf, int nbytes)
+{
char *p = buf;
+ trace_get_random_bytes(nbytes, _RET_IP_);
while (nbytes) {
unsigned long v;
int chunk = min(nbytes, (int)sizeof(unsigned long));
-
+
if (!arch_get_random_long(&v))
break;
@@ -950,9 +1063,11 @@ void get_random_bytes(void *buf, int nbytes)
nbytes -= chunk;
}
- extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
+ if (nbytes)
+ extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
}
-EXPORT_SYMBOL(get_random_bytes);
+EXPORT_SYMBOL(get_random_bytes_arch);
+
/*
* init_std_data - initialize pool with system data
@@ -966,23 +1081,30 @@ EXPORT_SYMBOL(get_random_bytes);
static void init_std_data(struct entropy_store *r)
{
int i;
- ktime_t now;
- unsigned long flags;
+ ktime_t now = ktime_get_real();
+ unsigned long rv;
- spin_lock_irqsave(&r->lock, flags);
r->entropy_count = 0;
- spin_unlock_irqrestore(&r->lock, flags);
-
- now = ktime_get_real();
- mix_pool_bytes(r, &now, sizeof(now));
- for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof flags) {
- if (!arch_get_random_long(&flags))
+ r->entropy_total = 0;
+ mix_pool_bytes(r, &now, sizeof(now), NULL);
+ for (i = r->poolinfo->POOLBYTES; i > 0; i -= sizeof(rv)) {
+ if (!arch_get_random_long(&rv))
break;
- mix_pool_bytes(r, &flags, sizeof(flags));
+ mix_pool_bytes(r, &rv, sizeof(rv), NULL);
}
- mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
+ mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL);
}
+/*
+ * Note that setup_arch() may call add_device_randomness()
+ * long before we get here. This allows seeding of the pools
+ * with some platform dependent data very early in the boot
+ * process. But it limits our options here. We must use
+ * statically allocated structures that already have all
+ * initializations complete at compile time. We should also
+ * take care not to overwrite the precious per platform data
+ * we were given.
+ */
static int rand_initialize(void)
{
init_std_data(&input_pool);
@@ -992,24 +1114,6 @@ static int rand_initialize(void)
}
module_init(rand_initialize);
-void rand_initialize_irq(int irq)
-{
- struct timer_rand_state *state;
-
- state = get_timer_rand_state(irq);
-
- if (state)
- return;
-
- /*
- * If kzalloc returns null, we just won't use that entropy
- * source.
- */
- state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state)
- set_timer_rand_state(irq, state);
-}
-
#ifdef CONFIG_BLOCK
void rand_initialize_disk(struct gendisk *disk)
{
@@ -1117,7 +1221,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count)
count -= bytes;
p += bytes;
- mix_pool_bytes(r, buf, bytes);
+ mix_pool_bytes(r, buf, bytes, NULL);
cond_resched();
}
@@ -1279,6 +1383,7 @@ static int proc_do_uuid(ctl_table *table, int write,
}
static int sysctl_poolsize = INPUT_POOL_WORDS * 32;
+extern ctl_table random_table[];
ctl_table random_table[] = {
{
.procname = "poolsize",
@@ -1344,7 +1449,7 @@ late_initcall(random_int_secret_init);
* value is not cryptographically secure but for several uses the cost of
* depleting entropy is too high
*/
-DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
+static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
unsigned int get_random_int(void)
{
__u32 *hash;
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 89682fa8801e..c4be3519a587 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -807,6 +807,7 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
#endif
+#ifdef CONFIG_PM_SLEEP
static int tpm_tis_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
@@ -816,6 +817,7 @@ static int tpm_tis_resume(struct device *dev)
return tpm_pm_resume(dev);
}
+#endif
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5869ea387054..72ce247a0e8d 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,4 +1,5 @@
# common clock types
+obj-$(CONFIG_HAVE_CLK) += clk-devres.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
clk-mux.o clk-divider.o clk-fixed-factor.o
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
new file mode 100644
index 000000000000..8f571548870f
--- /dev/null
+++ b/drivers/clk/clk-devres.c
@@ -0,0 +1,55 @@
+/*
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+ clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+ struct clk **ptr, *clk;
+
+ ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ clk = clk_get(dev, id);
+ if (!IS_ERR(clk)) {
+ *ptr = clk;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+ struct clk **c = res;
+ if (!c || !*c) {
+ WARN_ON(!c || !*c);
+ return 0;
+ }
+ return *c == data;
+}
+
+void devm_clk_put(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
+
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_clk_put);
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index d423c9bdd71a..442a31363873 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -171,51 +171,6 @@ void clk_put(struct clk *clk)
}
EXPORT_SYMBOL(clk_put);
-static void devm_clk_release(struct device *dev, void *res)
-{
- clk_put(*(struct clk **)res);
-}
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_get(dev, id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
-
- return clk;
-}
-EXPORT_SYMBOL(devm_clk_get);
-
-static int devm_clk_match(struct device *dev, void *res, void *data)
-{
- struct clk **c = res;
- if (!c || !*c) {
- WARN_ON(!c || !*c);
- return 0;
- }
- return *c == data;
-}
-
-void devm_clk_put(struct device *dev, struct clk *clk)
-{
- int ret;
-
- ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk);
-
- WARN_ON(ret);
-}
-EXPORT_SYMBOL(devm_clk_put);
-
void clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index 540795cd0760..d9279385304d 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -53,7 +53,7 @@ static struct cs5535_mfgpt_timer *cs5535_event_clock;
#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
/*
- * The MFPGT timers on the CS5536 provide us with suitable timers to use
+ * The MFGPT timers on the CS5536 provide us with suitable timers to use
* as clock event sources - not as good as a HPET or APIC, but certainly
* better than the PIT. This isn't a general purpose MFGPT driver, but
* a simplified one designed specifically to act as a clock event source.
@@ -144,7 +144,7 @@ static int __init cs5535_mfgpt_init(void)
timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
if (!timer) {
- printk(KERN_ERR DRV_NAME ": Could not allocate MFPGT timer\n");
+ printk(KERN_ERR DRV_NAME ": Could not allocate MFGPT timer\n");
return -ENODEV;
}
cs5535_event_clock = timer;
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 17fa04d08be9..b47034e650a5 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -218,7 +218,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
- if (atomic_inc_return(&freq_table_users) == 1)
+ if (!freq_table)
result = opp_init_cpufreq_table(mpu_dev, &freq_table);
if (result) {
@@ -227,6 +227,8 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
goto fail_ck;
}
+ atomic_inc_return(&freq_table_users);
+
result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
if (result)
goto fail_table;
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index cdc02ac8f41a..503996a94a6a 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -454,6 +454,7 @@ static int __init pcc_cpufreq_probe(void)
mem_resource->address_length);
if (pcch_virt_addr == NULL) {
pr_debug("probe: could not map shared mem region\n");
+ ret = -ENOMEM;
goto out_free;
}
pcch_hdr = pcch_virt_addr;
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index c0e816468e30..1a40935c85fd 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -35,7 +35,6 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/cpumask.h>
-#include <linux/sched.h> /* for current / set_cpus_allowed() */
#include <linux/io.h>
#include <linux/delay.h>
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
return res;
}
-/* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol,
- unsigned targfreq, unsigned relation)
+struct powernowk8_target_arg {
+ struct cpufreq_policy *pol;
+ unsigned targfreq;
+ unsigned relation;
+};
+
+static long powernowk8_target_fn(void *arg)
{
- cpumask_var_t oldmask;
+ struct powernowk8_target_arg *pta = arg;
+ struct cpufreq_policy *pol = pta->pol;
+ unsigned targfreq = pta->targfreq;
+ unsigned relation = pta->relation;
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
u32 checkfid;
u32 checkvid;
unsigned int newstate;
- int ret = -EIO;
+ int ret;
if (!data)
return -EINVAL;
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol,
checkfid = data->currfid;
checkvid = data->currvid;
- /* only run on specific CPU from here on. */
- /* This is poor form: use a workqueue or smp_call_function_single */
- if (!alloc_cpumask_var(&oldmask, GFP_KERNEL))
- return -ENOMEM;
-
- cpumask_copy(oldmask, tsk_cpus_allowed(current));
- set_cpus_allowed_ptr(current, cpumask_of(pol->cpu));
-
- if (smp_processor_id() != pol->cpu) {
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
- goto err_out;
- }
-
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing targ, change pending bit set\n");
- goto err_out;
+ return -EIO;
}
pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
pol->cpu, targfreq, pol->min, pol->max, relation);
if (query_current_values_with_pending_wait(data))
- goto err_out;
+ return -EIO;
if (cpu_family != CPU_HW_PSTATE) {
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
if (cpufreq_frequency_table_target(pol, data->powernow_table,
targfreq, relation, &newstate))
- goto err_out;
+ return -EIO;
mutex_lock(&fidvid_mutex);
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
ret = transition_frequency_fidvid(data, newstate);
if (ret) {
printk(KERN_ERR PFX "transition frequency failed\n");
- ret = 1;
mutex_unlock(&fidvid_mutex);
- goto err_out;
+ return 1;
}
mutex_unlock(&fidvid_mutex);
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol,
data->powernow_table[newstate].index);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
- ret = 0;
-err_out:
- set_cpus_allowed_ptr(current, oldmask);
- free_cpumask_var(oldmask);
- return ret;
+ return 0;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol,
+ unsigned targfreq, unsigned relation)
+{
+ struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq,
+ .relation = relation };
+
+ /*
+ * Must run on @pol->cpu. cpufreq core is responsible for ensuring
+ * that we're bound to the current CPU and pol->cpu stays online.
+ */
+ if (smp_processor_id() == pol->cpu)
+ return powernowk8_target_fn(&pta);
+ else
+ return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta);
}
/* Driver entry point to verify the policy and range of frequencies */
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index 2c9bf2692232..3265844839bf 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -678,10 +678,22 @@ static int cpuidle_coupled_cpu_notify(struct notifier_block *nb,
int cpu = (unsigned long)hcpu;
struct cpuidle_device *dev;
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_UP_PREPARE:
+ case CPU_DOWN_PREPARE:
+ case CPU_ONLINE:
+ case CPU_DEAD:
+ case CPU_UP_CANCELED:
+ case CPU_DOWN_FAILED:
+ break;
+ default:
+ return NOTIFY_OK;
+ }
+
mutex_lock(&cpuidle_lock);
dev = per_cpu(cpuidle_devices, cpu);
- if (!dev->coupled)
+ if (!dev || !dev->coupled)
goto out;
switch (action & ~CPU_TASKS_FROZEN) {
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 53c8c51d5881..93d14070141a 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -63,7 +63,7 @@ static void caam_jr_dequeue(unsigned long devarg)
head = ACCESS_ONCE(jrp->head);
- spin_lock_bh(&jrp->outlock);
+ spin_lock(&jrp->outlock);
sw_idx = tail = jrp->tail;
hw_idx = jrp->out_ring_read_index;
@@ -115,7 +115,7 @@ static void caam_jr_dequeue(unsigned long devarg)
jrp->tail = tail;
}
- spin_unlock_bh(&jrp->outlock);
+ spin_unlock(&jrp->outlock);
/* Finally, execute user's callback */
usercall(dev, userdesc, userstatus, userarg);
@@ -236,14 +236,14 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
return -EIO;
}
- spin_lock(&jrp->inplock);
+ spin_lock_bh(&jrp->inplock);
head = jrp->head;
tail = ACCESS_ONCE(jrp->tail);
if (!rd_reg32(&jrp->rregs->inpring_avail) ||
CIRC_SPACE(head, tail, JOBR_DEPTH) <= 0) {
- spin_unlock(&jrp->inplock);
+ spin_unlock_bh(&jrp->inplock);
dma_unmap_single(dev, desc_dma, desc_size, DMA_TO_DEVICE);
return -EBUSY;
}
@@ -265,7 +265,7 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
wr_reg32(&jrp->rregs->inpring_jobadd, 1);
- spin_unlock(&jrp->inplock);
+ spin_unlock_bh(&jrp->inplock);
return 0;
}
diff --git a/drivers/crypto/caam/key_gen.c b/drivers/crypto/caam/key_gen.c
index 002888185f17..d216cd3cc569 100644
--- a/drivers/crypto/caam/key_gen.c
+++ b/drivers/crypto/caam/key_gen.c
@@ -120,3 +120,4 @@ u32 gen_split_key(struct device *jrdev, u8 *key_out, int split_key_len,
return ret;
}
+EXPORT_SYMBOL(gen_split_key);
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index c9c4befb5a8d..df14358d7fa1 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -821,8 +821,8 @@ static int hifn_register_rng(struct hifn_device *dev)
/*
* We must wait at least 256 Pk_clk cycles between two reads of the rng.
*/
- dev->rng_wait_time = DIV_ROUND_UP(NSEC_PER_SEC, dev->pk_clk_freq) *
- 256;
+ dev->rng_wait_time = DIV_ROUND_UP_ULL(NSEC_PER_SEC,
+ dev->pk_clk_freq) * 256;
dev->rng.name = dev->name;
dev->rng.data_present = hifn_rng_data_present,
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index 67b97c5fd859..a8bd0310f8fe 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1610,8 +1610,7 @@ static int spu_map_ino(struct platform_device *dev, struct spu_mdesc_info *ip,
sprintf(p->irq_name, "%s-%d", irq_name, index);
- return request_irq(p->irq, handler, IRQF_SAMPLE_RANDOM,
- p->irq_name, p);
+ return request_irq(p->irq, handler, 0, p->irq_name, p);
}
static struct kmem_cache *queue_cache[2];
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index d45cf1bcbde5..d06ea2950dd9 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -53,6 +53,7 @@ config AMBA_PL08X
bool "ARM PrimeCell PL080 or PL081 support"
depends on ARM_AMBA && EXPERIMENTAL
select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
help
Platform has a PL08x DMAC device
which can provide DMA engine support
@@ -269,6 +270,7 @@ config DMA_SA11X0
tristate "SA-11x0 DMA support"
depends on ARCH_SA1100
select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
help
Support the DMA engine found on Intel StrongARM SA-1100 and
SA-1110 SoCs. This DMA engine can only be used with on-chip
@@ -284,9 +286,18 @@ config MMP_TDMA
Say Y here if you enabled MMP ADMA, otherwise say N.
+config DMA_OMAP
+ tristate "OMAP DMA support"
+ depends on ARCH_OMAP
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
config DMA_ENGINE
bool
+config DMA_VIRTUAL_CHANNELS
+ tristate
+
comment "DMA Clients"
depends on DMA_ENGINE
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 640356add0a3..4cf6b128ab9a 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG
ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_VIRTUAL_CHANNELS) += virt-dma.o
obj-$(CONFIG_NET_DMA) += iovlock.o
obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
obj-$(CONFIG_DMATEST) += dmatest.o
@@ -30,3 +31,4 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
+obj-$(CONFIG_DMA_OMAP) += omap-dma.o
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 49ecbbb8932d..6fbeebb9486f 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -86,10 +86,12 @@
#include <asm/hardware/pl080.h>
#include "dmaengine.h"
+#include "virt-dma.h"
#define DRIVER_NAME "pl08xdmac"
static struct amba_driver pl08x_amba_driver;
+struct pl08x_driver_data;
/**
* struct vendor_data - vendor-specific config parameters for PL08x derivatives
@@ -119,6 +121,123 @@ struct pl08x_lli {
};
/**
+ * struct pl08x_bus_data - information of source or destination
+ * busses for a transfer
+ * @addr: current address
+ * @maxwidth: the maximum width of a transfer on this bus
+ * @buswidth: the width of this bus in bytes: 1, 2 or 4
+ */
+struct pl08x_bus_data {
+ dma_addr_t addr;
+ u8 maxwidth;
+ u8 buswidth;
+};
+
+/**
+ * struct pl08x_phy_chan - holder for the physical channels
+ * @id: physical index to this channel
+ * @lock: a lock to use when altering an instance of this struct
+ * @serving: the virtual channel currently being served by this physical
+ * channel
+ * @locked: channel unavailable for the system, e.g. dedicated to secure
+ * world
+ */
+struct pl08x_phy_chan {
+ unsigned int id;
+ void __iomem *base;
+ spinlock_t lock;
+ struct pl08x_dma_chan *serving;
+ bool locked;
+};
+
+/**
+ * struct pl08x_sg - structure containing data per sg
+ * @src_addr: src address of sg
+ * @dst_addr: dst address of sg
+ * @len: transfer len in bytes
+ * @node: node for txd's dsg_list
+ */
+struct pl08x_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ size_t len;
+ struct list_head node;
+};
+
+/**
+ * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
+ * @vd: virtual DMA descriptor
+ * @dsg_list: list of children sg's
+ * @llis_bus: DMA memory address (physical) start for the LLIs
+ * @llis_va: virtual memory address start for the LLIs
+ * @cctl: control reg values for current txd
+ * @ccfg: config reg values for current txd
+ * @done: this marks completed descriptors, which should not have their
+ * mux released.
+ */
+struct pl08x_txd {
+ struct virt_dma_desc vd;
+ struct list_head dsg_list;
+ dma_addr_t llis_bus;
+ struct pl08x_lli *llis_va;
+ /* Default cctl value for LLIs */
+ u32 cctl;
+ /*
+ * Settings to be put into the physical channel when we
+ * trigger this txd. Other registers are in llis_va[0].
+ */
+ u32 ccfg;
+ bool done;
+};
+
+/**
+ * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel
+ * states
+ * @PL08X_CHAN_IDLE: the channel is idle
+ * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
+ * channel and is running a transfer on it
+ * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
+ * channel, but the transfer is currently paused
+ * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
+ * channel to become available (only pertains to memcpy channels)
+ */
+enum pl08x_dma_chan_state {
+ PL08X_CHAN_IDLE,
+ PL08X_CHAN_RUNNING,
+ PL08X_CHAN_PAUSED,
+ PL08X_CHAN_WAITING,
+};
+
+/**
+ * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
+ * @vc: wrappped virtual channel
+ * @phychan: the physical channel utilized by this channel, if there is one
+ * @name: name of channel
+ * @cd: channel platform data
+ * @runtime_addr: address for RX/TX according to the runtime config
+ * @at: active transaction on this channel
+ * @lock: a lock for this channel data
+ * @host: a pointer to the host (internal use)
+ * @state: whether the channel is idle, paused, running etc
+ * @slave: whether this channel is a device (slave) or for memcpy
+ * @signal: the physical DMA request signal which this channel is using
+ * @mux_use: count of descriptors using this DMA request signal setting
+ */
+struct pl08x_dma_chan {
+ struct virt_dma_chan vc;
+ struct pl08x_phy_chan *phychan;
+ const char *name;
+ const struct pl08x_channel_data *cd;
+ struct dma_slave_config cfg;
+ struct pl08x_txd *at;
+ struct pl08x_driver_data *host;
+ enum pl08x_dma_chan_state state;
+ bool slave;
+ int signal;
+ unsigned mux_use;
+};
+
+/**
* struct pl08x_driver_data - the local state holder for the PL08x
* @slave: slave engine for this instance
* @memcpy: memcpy engine for this instance
@@ -128,7 +247,6 @@ struct pl08x_lli {
* @pd: platform data passed in from the platform/machine
* @phy_chans: array of data for the physical channels
* @pool: a pool for the LLI descriptors
- * @pool_ctr: counter of LLIs in the pool
* @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI
* fetches
* @mem_buses: set to indicate memory transfers on AHB2.
@@ -143,10 +261,8 @@ struct pl08x_driver_data {
struct pl08x_platform_data *pd;
struct pl08x_phy_chan *phy_chans;
struct dma_pool *pool;
- int pool_ctr;
u8 lli_buses;
u8 mem_buses;
- spinlock_t lock;
};
/*
@@ -162,12 +278,51 @@ struct pl08x_driver_data {
static inline struct pl08x_dma_chan *to_pl08x_chan(struct dma_chan *chan)
{
- return container_of(chan, struct pl08x_dma_chan, chan);
+ return container_of(chan, struct pl08x_dma_chan, vc.chan);
}
static inline struct pl08x_txd *to_pl08x_txd(struct dma_async_tx_descriptor *tx)
{
- return container_of(tx, struct pl08x_txd, tx);
+ return container_of(tx, struct pl08x_txd, vd.tx);
+}
+
+/*
+ * Mux handling.
+ *
+ * This gives us the DMA request input to the PL08x primecell which the
+ * peripheral described by the channel data will be routed to, possibly
+ * via a board/SoC specific external MUX. One important point to note
+ * here is that this does not depend on the physical channel.
+ */
+static int pl08x_request_mux(struct pl08x_dma_chan *plchan)
+{
+ const struct pl08x_platform_data *pd = plchan->host->pd;
+ int ret;
+
+ if (plchan->mux_use++ == 0 && pd->get_signal) {
+ ret = pd->get_signal(plchan->cd);
+ if (ret < 0) {
+ plchan->mux_use = 0;
+ return ret;
+ }
+
+ plchan->signal = ret;
+ }
+ return 0;
+}
+
+static void pl08x_release_mux(struct pl08x_dma_chan *plchan)
+{
+ const struct pl08x_platform_data *pd = plchan->host->pd;
+
+ if (plchan->signal >= 0) {
+ WARN_ON(plchan->mux_use == 0);
+
+ if (--plchan->mux_use == 0 && pd->put_signal) {
+ pd->put_signal(plchan->cd, plchan->signal);
+ plchan->signal = -1;
+ }
+ }
}
/*
@@ -189,20 +344,25 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
* been set when the LLIs were constructed. Poke them into the hardware
* and start the transfer.
*/
-static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
+static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
{
struct pl08x_driver_data *pl08x = plchan->host;
struct pl08x_phy_chan *phychan = plchan->phychan;
- struct pl08x_lli *lli = &txd->llis_va[0];
+ struct virt_dma_desc *vd = vchan_next_desc(&plchan->vc);
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_lli *lli;
u32 val;
+ list_del(&txd->vd.node);
+
plchan->at = txd;
/* Wait for channel inactive */
while (pl08x_phy_channel_busy(phychan))
cpu_relax();
+ lli = &txd->llis_va[0];
+
dev_vdbg(&pl08x->adev->dev,
"WRITE channel %d: csrc=0x%08x, cdst=0x%08x, "
"clli=0x%08x, cctl=0x%08x, ccfg=0x%08x\n",
@@ -311,10 +471,8 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
{
struct pl08x_phy_chan *ch;
struct pl08x_txd *txd;
- unsigned long flags;
size_t bytes = 0;
- spin_lock_irqsave(&plchan->lock, flags);
ch = plchan->phychan;
txd = plchan->at;
@@ -354,18 +512,6 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
}
}
- /* Sum up all queued transactions */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *txdi;
- list_for_each_entry(txdi, &plchan->pend_list, node) {
- struct pl08x_sg *dsg;
- list_for_each_entry(dsg, &txd->dsg_list, node)
- bytes += dsg->len;
- }
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
return bytes;
}
@@ -391,7 +537,6 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
if (!ch->locked && !ch->serving) {
ch->serving = virt_chan;
- ch->signal = -1;
spin_unlock_irqrestore(&ch->lock, flags);
break;
}
@@ -404,25 +549,114 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x,
return NULL;
}
- pm_runtime_get_sync(&pl08x->adev->dev);
return ch;
}
+/* Mark the physical channel as free. Note, this write is atomic. */
static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x,
struct pl08x_phy_chan *ch)
{
- unsigned long flags;
+ ch->serving = NULL;
+}
+
+/*
+ * Try to allocate a physical channel. When successful, assign it to
+ * this virtual channel, and initiate the next descriptor. The
+ * virtual channel lock must be held at this point.
+ */
+static void pl08x_phy_alloc_and_start(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_phy_chan *ch;
- spin_lock_irqsave(&ch->lock, flags);
+ ch = pl08x_get_phy_channel(pl08x, plchan);
+ if (!ch) {
+ dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
+ plchan->state = PL08X_CHAN_WAITING;
+ return;
+ }
- /* Stop the channel and clear its interrupts */
- pl08x_terminate_phy_chan(pl08x, ch);
+ dev_dbg(&pl08x->adev->dev, "allocated physical channel %d for xfer on %s\n",
+ ch->id, plchan->name);
- pm_runtime_put(&pl08x->adev->dev);
+ plchan->phychan = ch;
+ plchan->state = PL08X_CHAN_RUNNING;
+ pl08x_start_next_txd(plchan);
+}
- /* Mark it as free */
- ch->serving = NULL;
- spin_unlock_irqrestore(&ch->lock, flags);
+static void pl08x_phy_reassign_start(struct pl08x_phy_chan *ch,
+ struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+
+ dev_dbg(&pl08x->adev->dev, "reassigned physical channel %d for xfer on %s\n",
+ ch->id, plchan->name);
+
+ /*
+ * We do this without taking the lock; we're really only concerned
+ * about whether this pointer is NULL or not, and we're guaranteed
+ * that this will only be called when it _already_ is non-NULL.
+ */
+ ch->serving = plchan;
+ plchan->phychan = ch;
+ plchan->state = PL08X_CHAN_RUNNING;
+ pl08x_start_next_txd(plchan);
+}
+
+/*
+ * Free a physical DMA channel, potentially reallocating it to another
+ * virtual channel if we have any pending.
+ */
+static void pl08x_phy_free(struct pl08x_dma_chan *plchan)
+{
+ struct pl08x_driver_data *pl08x = plchan->host;
+ struct pl08x_dma_chan *p, *next;
+
+ retry:
+ next = NULL;
+
+ /* Find a waiting virtual channel for the next transfer. */
+ list_for_each_entry(p, &pl08x->memcpy.channels, vc.chan.device_node)
+ if (p->state == PL08X_CHAN_WAITING) {
+ next = p;
+ break;
+ }
+
+ if (!next) {
+ list_for_each_entry(p, &pl08x->slave.channels, vc.chan.device_node)
+ if (p->state == PL08X_CHAN_WAITING) {
+ next = p;
+ break;
+ }
+ }
+
+ /* Ensure that the physical channel is stopped */
+ pl08x_terminate_phy_chan(pl08x, plchan->phychan);
+
+ if (next) {
+ bool success;
+
+ /*
+ * Eww. We know this isn't going to deadlock
+ * but lockdep probably doesn't.
+ */
+ spin_lock(&next->vc.lock);
+ /* Re-check the state now that we have the lock */
+ success = next->state == PL08X_CHAN_WAITING;
+ if (success)
+ pl08x_phy_reassign_start(plchan->phychan, next);
+ spin_unlock(&next->vc.lock);
+
+ /* If the state changed, try to find another channel */
+ if (!success)
+ goto retry;
+ } else {
+ /* No more jobs, so free up the physical channel */
+ pl08x_put_phy_channel(pl08x, plchan->phychan);
+ }
+
+ plchan->phychan = NULL;
+ plchan->state = PL08X_CHAN_IDLE;
}
/*
@@ -585,8 +819,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
return 0;
}
- pl08x->pool_ctr++;
-
bd.txd = txd;
bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
cctl = txd->cctl;
@@ -802,18 +1034,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
return num_llis;
}
-/* You should call this with the struct pl08x lock held */
static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
struct pl08x_txd *txd)
{
struct pl08x_sg *dsg, *_dsg;
- /* Free the LLI */
if (txd->llis_va)
dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus);
- pl08x->pool_ctr--;
-
list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) {
list_del(&dsg->node);
kfree(dsg);
@@ -822,133 +1050,75 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x,
kfree(txd);
}
-static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
- struct pl08x_dma_chan *plchan)
+static void pl08x_unmap_buffers(struct pl08x_txd *txd)
{
- struct pl08x_txd *txdi = NULL;
- struct pl08x_txd *next;
-
- if (!list_empty(&plchan->pend_list)) {
- list_for_each_entry_safe(txdi,
- next, &plchan->pend_list, node) {
- list_del(&txdi->node);
- pl08x_free_txd(pl08x, txdi);
+ struct device *dev = txd->vd.tx.chan->device->dev;
+ struct pl08x_sg *dsg;
+
+ if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
+ if (txd->vd.tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
+ else {
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->src_addr, dsg->len,
+ DMA_TO_DEVICE);
}
}
+ if (!(txd->vd.tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
+ if (txd->vd.tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_single(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
+ else
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ dma_unmap_page(dev, dsg->dst_addr, dsg->len,
+ DMA_FROM_DEVICE);
+ }
}
-/*
- * The DMA ENGINE API
- */
-static int pl08x_alloc_chan_resources(struct dma_chan *chan)
+static void pl08x_desc_free(struct virt_dma_desc *vd)
{
- return 0;
-}
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(vd->tx.chan);
-static void pl08x_free_chan_resources(struct dma_chan *chan)
-{
+ if (!plchan->slave)
+ pl08x_unmap_buffers(txd);
+
+ if (!txd->done)
+ pl08x_release_mux(plchan);
+
+ pl08x_free_txd(plchan->host, txd);
}
-/*
- * This should be called with the channel plchan->lock held
- */
-static int prep_phy_channel(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
+static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x,
+ struct pl08x_dma_chan *plchan)
{
- struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_phy_chan *ch;
- int ret;
-
- /* Check if we already have a channel */
- if (plchan->phychan) {
- ch = plchan->phychan;
- goto got_channel;
- }
+ LIST_HEAD(head);
+ struct pl08x_txd *txd;
- ch = pl08x_get_phy_channel(pl08x, plchan);
- if (!ch) {
- /* No physical channel available, cope with it */
- dev_dbg(&pl08x->adev->dev, "no physical channel available for xfer on %s\n", plchan->name);
- return -EBUSY;
- }
+ vchan_get_all_descriptors(&plchan->vc, &head);
- /*
- * OK we have a physical channel: for memcpy() this is all we
- * need, but for slaves the physical signals may be muxed!
- * Can the platform allow us to use this channel?
- */
- if (plchan->slave && pl08x->pd->get_signal) {
- ret = pl08x->pd->get_signal(plchan);
- if (ret < 0) {
- dev_dbg(&pl08x->adev->dev,
- "unable to use physical channel %d for transfer on %s due to platform restrictions\n",
- ch->id, plchan->name);
- /* Release physical channel & return */
- pl08x_put_phy_channel(pl08x, ch);
- return -EBUSY;
- }
- ch->signal = ret;
+ while (!list_empty(&head)) {
+ txd = list_first_entry(&head, struct pl08x_txd, vd.node);
+ list_del(&txd->vd.node);
+ pl08x_desc_free(&txd->vd);
}
-
- plchan->phychan = ch;
- dev_dbg(&pl08x->adev->dev, "allocated physical channel %d and signal %d for xfer on %s\n",
- ch->id,
- ch->signal,
- plchan->name);
-
-got_channel:
- /* Assign the flow control signal to this channel */
- if (txd->direction == DMA_MEM_TO_DEV)
- txd->ccfg |= ch->signal << PL080_CONFIG_DST_SEL_SHIFT;
- else if (txd->direction == DMA_DEV_TO_MEM)
- txd->ccfg |= ch->signal << PL080_CONFIG_SRC_SEL_SHIFT;
-
- plchan->phychan_hold++;
-
- return 0;
}
-static void release_phy_channel(struct pl08x_dma_chan *plchan)
+/*
+ * The DMA ENGINE API
+ */
+static int pl08x_alloc_chan_resources(struct dma_chan *chan)
{
- struct pl08x_driver_data *pl08x = plchan->host;
-
- if ((plchan->phychan->signal >= 0) && pl08x->pd->put_signal) {
- pl08x->pd->put_signal(plchan);
- plchan->phychan->signal = -1;
- }
- pl08x_put_phy_channel(pl08x, plchan->phychan);
- plchan->phychan = NULL;
+ return 0;
}
-static dma_cookie_t pl08x_tx_submit(struct dma_async_tx_descriptor *tx)
+static void pl08x_free_chan_resources(struct dma_chan *chan)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(tx->chan);
- struct pl08x_txd *txd = to_pl08x_txd(tx);
- unsigned long flags;
- dma_cookie_t cookie;
-
- spin_lock_irqsave(&plchan->lock, flags);
- cookie = dma_cookie_assign(tx);
-
- /* Put this onto the pending list */
- list_add_tail(&txd->node, &plchan->pend_list);
-
- /*
- * If there was no physical channel available for this memcpy,
- * stack the request up and indicate that the channel is waiting
- * for a free physical channel.
- */
- if (!plchan->slave && !plchan->phychan) {
- /* Do this memcpy whenever there is a channel ready */
- plchan->state = PL08X_CHAN_WAITING;
- plchan->waiting = txd;
- } else {
- plchan->phychan_hold--;
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- return cookie;
+ /* Ensure all queued descriptors are freed */
+ vchan_free_chan_resources(to_virt_chan(chan));
}
static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt(
@@ -968,23 +1138,53 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+ struct virt_dma_desc *vd;
+ unsigned long flags;
enum dma_status ret;
+ size_t bytes = 0;
ret = dma_cookie_status(chan, cookie, txstate);
if (ret == DMA_SUCCESS)
return ret;
/*
+ * There's no point calculating the residue if there's
+ * no txstate to store the value.
+ */
+ if (!txstate) {
+ if (plchan->state == PL08X_CHAN_PAUSED)
+ ret = DMA_PAUSED;
+ return ret;
+ }
+
+ spin_lock_irqsave(&plchan->vc.lock, flags);
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret != DMA_SUCCESS) {
+ vd = vchan_find_desc(&plchan->vc, cookie);
+ if (vd) {
+ /* On the issued list, so hasn't been processed yet */
+ struct pl08x_txd *txd = to_pl08x_txd(&vd->tx);
+ struct pl08x_sg *dsg;
+
+ list_for_each_entry(dsg, &txd->dsg_list, node)
+ bytes += dsg->len;
+ } else {
+ bytes = pl08x_getbytes_chan(plchan);
+ }
+ }
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
+
+ /*
* This cookie not complete yet
* Get number of bytes left in the active transactions and queue
*/
- dma_set_residue(txstate, pl08x_getbytes_chan(plchan));
+ dma_set_residue(txstate, bytes);
- if (plchan->state == PL08X_CHAN_PAUSED)
- return DMA_PAUSED;
+ if (plchan->state == PL08X_CHAN_PAUSED && ret == DMA_IN_PROGRESS)
+ ret = DMA_PAUSED;
/* Whether waiting or running, we're in progress */
- return DMA_IN_PROGRESS;
+ return ret;
}
/* PrimeCell DMA extension */
@@ -1080,38 +1280,14 @@ static u32 pl08x_burst(u32 maxburst)
return burst_sizes[i].reg;
}
-static int dma_set_runtime_config(struct dma_chan *chan,
- struct dma_slave_config *config)
+static u32 pl08x_get_cctl(struct pl08x_dma_chan *plchan,
+ enum dma_slave_buswidth addr_width, u32 maxburst)
{
- struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- struct pl08x_driver_data *pl08x = plchan->host;
- enum dma_slave_buswidth addr_width;
- u32 width, burst, maxburst;
- u32 cctl = 0;
-
- if (!plchan->slave)
- return -EINVAL;
-
- /* Transfer direction */
- plchan->runtime_direction = config->direction;
- if (config->direction == DMA_MEM_TO_DEV) {
- addr_width = config->dst_addr_width;
- maxburst = config->dst_maxburst;
- } else if (config->direction == DMA_DEV_TO_MEM) {
- addr_width = config->src_addr_width;
- maxburst = config->src_maxburst;
- } else {
- dev_err(&pl08x->adev->dev,
- "bad runtime_config: alien transfer direction\n");
- return -EINVAL;
- }
+ u32 width, burst, cctl = 0;
width = pl08x_width(addr_width);
- if (width == ~0) {
- dev_err(&pl08x->adev->dev,
- "bad runtime_config: alien address width\n");
- return -EINVAL;
- }
+ if (width == ~0)
+ return ~0;
cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
@@ -1128,28 +1304,23 @@ static int dma_set_runtime_config(struct dma_chan *chan,
cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
- plchan->device_fc = config->device_fc;
+ return pl08x_cctl(cctl);
+}
- if (plchan->runtime_direction == DMA_DEV_TO_MEM) {
- plchan->src_addr = config->src_addr;
- plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
- pl08x_select_bus(plchan->cd->periph_buses,
- pl08x->mem_buses);
- } else {
- plchan->dst_addr = config->dst_addr;
- plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
- pl08x_select_bus(pl08x->mem_buses,
- plchan->cd->periph_buses);
- }
+static int dma_set_runtime_config(struct dma_chan *chan,
+ struct dma_slave_config *config)
+{
+ struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
- dev_dbg(&pl08x->adev->dev,
- "configured channel %s (%s) for %s, data width %d, "
- "maxburst %d words, LE, CCTL=0x%08x\n",
- dma_chan_name(chan), plchan->name,
- (config->direction == DMA_DEV_TO_MEM) ? "RX" : "TX",
- addr_width,
- maxburst,
- cctl);
+ if (!plchan->slave)
+ return -EINVAL;
+
+ /* Reject definitely invalid configurations */
+ if (config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+ config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+ return -EINVAL;
+
+ plchan->cfg = *config;
return 0;
}
@@ -1163,95 +1334,19 @@ static void pl08x_issue_pending(struct dma_chan *chan)
struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
unsigned long flags;
- spin_lock_irqsave(&plchan->lock, flags);
- /* Something is already active, or we're waiting for a channel... */
- if (plchan->at || plchan->state == PL08X_CHAN_WAITING) {
- spin_unlock_irqrestore(&plchan->lock, flags);
- return;
- }
-
- /* Take the first element in the queue and execute it */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *next;
-
- next = list_first_entry(&plchan->pend_list,
- struct pl08x_txd,
- node);
- list_del(&next->node);
- plchan->state = PL08X_CHAN_RUNNING;
-
- pl08x_start_txd(plchan, next);
+ spin_lock_irqsave(&plchan->vc.lock, flags);
+ if (vchan_issue_pending(&plchan->vc)) {
+ if (!plchan->phychan && plchan->state != PL08X_CHAN_WAITING)
+ pl08x_phy_alloc_and_start(plchan);
}
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-}
-
-static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
- struct pl08x_txd *txd)
-{
- struct pl08x_driver_data *pl08x = plchan->host;
- unsigned long flags;
- int num_llis, ret;
-
- num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
- if (!num_llis) {
- spin_lock_irqsave(&plchan->lock, flags);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&plchan->lock, flags);
-
- /*
- * See if we already have a physical channel allocated,
- * else this is the time to try to get one.
- */
- ret = prep_phy_channel(plchan, txd);
- if (ret) {
- /*
- * No physical channel was available.
- *
- * memcpy transfers can be sorted out at submission time.
- *
- * Slave transfers may have been denied due to platform
- * channel muxing restrictions. Since there is no guarantee
- * that this will ever be resolved, and the signal must be
- * acquired AFTER acquiring the physical channel, we will let
- * them be NACK:ed with -EBUSY here. The drivers can retry
- * the prep() call if they are eager on doing this using DMA.
- */
- if (plchan->slave) {
- pl08x_free_txd_list(pl08x, plchan);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
- return -EBUSY;
- }
- } else
- /*
- * Else we're all set, paused and ready to roll, status
- * will switch to PL08X_CHAN_RUNNING when we call
- * issue_pending(). If there is something running on the
- * channel already we don't change its state.
- */
- if (plchan->state == PL08X_CHAN_IDLE)
- plchan->state = PL08X_CHAN_PAUSED;
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- return 0;
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
}
-static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
- unsigned long flags)
+static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
{
struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
if (txd) {
- dma_async_tx_descriptor_init(&txd->tx, &plchan->chan);
- txd->tx.flags = flags;
- txd->tx.tx_submit = pl08x_tx_submit;
- INIT_LIST_HEAD(&txd->node);
INIT_LIST_HEAD(&txd->dsg_list);
/* Always enable error and terminal interrupts */
@@ -1274,7 +1369,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
struct pl08x_sg *dsg;
int ret;
- txd = pl08x_get_txd(plchan, flags);
+ txd = pl08x_get_txd(plchan);
if (!txd) {
dev_err(&pl08x->adev->dev,
"%s no memory for descriptor\n", __func__);
@@ -1290,14 +1385,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
}
list_add_tail(&dsg->node, &txd->dsg_list);
- txd->direction = DMA_NONE;
dsg->src_addr = src;
dsg->dst_addr = dest;
dsg->len = len;
/* Set platform data for m2m */
txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
- txd->cctl = pl08x->pd->memcpy_channel.cctl &
+ txd->cctl = pl08x->pd->memcpy_channel.cctl_memcpy &
~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
/* Both to be incremented or the code will break */
@@ -1307,11 +1401,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
pl08x->mem_buses);
- ret = pl08x_prep_channel_resources(plchan, txd);
- if (ret)
+ ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+ if (!ret) {
+ pl08x_free_txd(pl08x, txd);
return NULL;
+ }
- return &txd->tx;
+ return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
}
static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
@@ -1324,36 +1420,40 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
struct pl08x_txd *txd;
struct pl08x_sg *dsg;
struct scatterlist *sg;
+ enum dma_slave_buswidth addr_width;
dma_addr_t slave_addr;
int ret, tmp;
+ u8 src_buses, dst_buses;
+ u32 maxburst, cctl;
dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n",
__func__, sg_dma_len(sgl), plchan->name);
- txd = pl08x_get_txd(plchan, flags);
+ txd = pl08x_get_txd(plchan);
if (!txd) {
dev_err(&pl08x->adev->dev, "%s no txd\n", __func__);
return NULL;
}
- if (direction != plchan->runtime_direction)
- dev_err(&pl08x->adev->dev, "%s DMA setup does not match "
- "the direction configured for the PrimeCell\n",
- __func__);
-
/*
* Set up addresses, the PrimeCell configured address
* will take precedence since this may configure the
* channel target address dynamically at runtime.
*/
- txd->direction = direction;
-
if (direction == DMA_MEM_TO_DEV) {
- txd->cctl = plchan->dst_cctl;
- slave_addr = plchan->dst_addr;
+ cctl = PL080_CONTROL_SRC_INCR;
+ slave_addr = plchan->cfg.dst_addr;
+ addr_width = plchan->cfg.dst_addr_width;
+ maxburst = plchan->cfg.dst_maxburst;
+ src_buses = pl08x->mem_buses;
+ dst_buses = plchan->cd->periph_buses;
} else if (direction == DMA_DEV_TO_MEM) {
- txd->cctl = plchan->src_cctl;
- slave_addr = plchan->src_addr;
+ cctl = PL080_CONTROL_DST_INCR;
+ slave_addr = plchan->cfg.src_addr;
+ addr_width = plchan->cfg.src_addr_width;
+ maxburst = plchan->cfg.src_maxburst;
+ src_buses = plchan->cd->periph_buses;
+ dst_buses = pl08x->mem_buses;
} else {
pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev,
@@ -1361,7 +1461,17 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
return NULL;
}
- if (plchan->device_fc)
+ cctl |= pl08x_get_cctl(plchan, addr_width, maxburst);
+ if (cctl == ~0) {
+ pl08x_free_txd(pl08x, txd);
+ dev_err(&pl08x->adev->dev,
+ "DMA slave configuration botched?\n");
+ return NULL;
+ }
+
+ txd->cctl = cctl | pl08x_select_bus(src_buses, dst_buses);
+
+ if (plchan->cfg.device_fc)
tmp = (direction == DMA_MEM_TO_DEV) ? PL080_FLOW_MEM2PER_PER :
PL080_FLOW_PER2MEM_PER;
else
@@ -1370,9 +1480,28 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+ ret = pl08x_request_mux(plchan);
+ if (ret < 0) {
+ pl08x_free_txd(pl08x, txd);
+ dev_dbg(&pl08x->adev->dev,
+ "unable to mux for transfer on %s due to platform restrictions\n",
+ plchan->name);
+ return NULL;
+ }
+
+ dev_dbg(&pl08x->adev->dev, "allocated DMA request signal %d for xfer on %s\n",
+ plchan->signal, plchan->name);
+
+ /* Assign the flow control signal to this channel */
+ if (direction == DMA_MEM_TO_DEV)
+ txd->ccfg |= plchan->signal << PL080_CONFIG_DST_SEL_SHIFT;
+ else
+ txd->ccfg |= plchan->signal << PL080_CONFIG_SRC_SEL_SHIFT;
+
for_each_sg(sgl, sg, sg_len, tmp) {
dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT);
if (!dsg) {
+ pl08x_release_mux(plchan);
pl08x_free_txd(pl08x, txd);
dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n",
__func__);
@@ -1390,11 +1519,14 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
}
}
- ret = pl08x_prep_channel_resources(plchan, txd);
- if (ret)
+ ret = pl08x_fill_llis_for_desc(plchan->host, txd);
+ if (!ret) {
+ pl08x_release_mux(plchan);
+ pl08x_free_txd(pl08x, txd);
return NULL;
+ }
- return &txd->tx;
+ return vchan_tx_prep(&plchan->vc, &txd->vd, flags);
}
static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -1415,9 +1547,9 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
* Anything succeeds on channels with no physical allocation and
* no queued transfers.
*/
- spin_lock_irqsave(&plchan->lock, flags);
+ spin_lock_irqsave(&plchan->vc.lock, flags);
if (!plchan->phychan && !plchan->at) {
- spin_unlock_irqrestore(&plchan->lock, flags);
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
return 0;
}
@@ -1426,18 +1558,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
plchan->state = PL08X_CHAN_IDLE;
if (plchan->phychan) {
- pl08x_terminate_phy_chan(pl08x, plchan->phychan);
-
/*
* Mark physical channel as free and free any slave
* signal
*/
- release_phy_channel(plchan);
- plchan->phychan_hold = 0;
+ pl08x_phy_free(plchan);
}
/* Dequeue jobs and free LLIs */
if (plchan->at) {
- pl08x_free_txd(pl08x, plchan->at);
+ pl08x_desc_free(&plchan->at->vd);
plchan->at = NULL;
}
/* Dequeue jobs not yet fired as well */
@@ -1457,7 +1586,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
break;
}
- spin_unlock_irqrestore(&plchan->lock, flags);
+ spin_unlock_irqrestore(&plchan->vc.lock, flags);
return ret;
}
@@ -1494,123 +1623,6 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG);
}
-static void pl08x_unmap_buffers(struct pl08x_txd *txd)
-{
- struct device *dev = txd->tx.chan->device->dev;
- struct pl08x_sg *dsg;
-
- if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) {
- if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE)
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_single(dev, dsg->src_addr, dsg->len,
- DMA_TO_DEVICE);
- else {
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_page(dev, dsg->src_addr, dsg->len,
- DMA_TO_DEVICE);
- }
- }
- if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) {
- if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE)
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_single(dev, dsg->dst_addr, dsg->len,
- DMA_FROM_DEVICE);
- else
- list_for_each_entry(dsg, &txd->dsg_list, node)
- dma_unmap_page(dev, dsg->dst_addr, dsg->len,
- DMA_FROM_DEVICE);
- }
-}
-
-static void pl08x_tasklet(unsigned long data)
-{
- struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
- struct pl08x_driver_data *pl08x = plchan->host;
- struct pl08x_txd *txd;
- unsigned long flags;
-
- spin_lock_irqsave(&plchan->lock, flags);
-
- txd = plchan->at;
- plchan->at = NULL;
-
- if (txd) {
- /* Update last completed */
- dma_cookie_complete(&txd->tx);
- }
-
- /* If a new descriptor is queued, set it up plchan->at is NULL here */
- if (!list_empty(&plchan->pend_list)) {
- struct pl08x_txd *next;
-
- next = list_first_entry(&plchan->pend_list,
- struct pl08x_txd,
- node);
- list_del(&next->node);
-
- pl08x_start_txd(plchan, next);
- } else if (plchan->phychan_hold) {
- /*
- * This channel is still in use - we have a new txd being
- * prepared and will soon be queued. Don't give up the
- * physical channel.
- */
- } else {
- struct pl08x_dma_chan *waiting = NULL;
-
- /*
- * No more jobs, so free up the physical channel
- * Free any allocated signal on slave transfers too
- */
- release_phy_channel(plchan);
- plchan->state = PL08X_CHAN_IDLE;
-
- /*
- * And NOW before anyone else can grab that free:d up
- * physical channel, see if there is some memcpy pending
- * that seriously needs to start because of being stacked
- * up while we were choking the physical channels with data.
- */
- list_for_each_entry(waiting, &pl08x->memcpy.channels,
- chan.device_node) {
- if (waiting->state == PL08X_CHAN_WAITING &&
- waiting->waiting != NULL) {
- int ret;
-
- /* This should REALLY not fail now */
- ret = prep_phy_channel(waiting,
- waiting->waiting);
- BUG_ON(ret);
- waiting->phychan_hold--;
- waiting->state = PL08X_CHAN_RUNNING;
- waiting->waiting = NULL;
- pl08x_issue_pending(&waiting->chan);
- break;
- }
- }
- }
-
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- if (txd) {
- dma_async_tx_callback callback = txd->tx.callback;
- void *callback_param = txd->tx.callback_param;
-
- /* Don't try to unmap buffers on slave channels */
- if (!plchan->slave)
- pl08x_unmap_buffers(txd);
-
- /* Free the descriptor */
- spin_lock_irqsave(&plchan->lock, flags);
- pl08x_free_txd(pl08x, txd);
- spin_unlock_irqrestore(&plchan->lock, flags);
-
- /* Callback to signal completion */
- if (callback)
- callback(callback_param);
- }
-}
-
static irqreturn_t pl08x_irq(int irq, void *dev)
{
struct pl08x_driver_data *pl08x = dev;
@@ -1635,6 +1647,7 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
/* Locate physical channel */
struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i];
struct pl08x_dma_chan *plchan = phychan->serving;
+ struct pl08x_txd *tx;
if (!plchan) {
dev_err(&pl08x->adev->dev,
@@ -1643,8 +1656,29 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
continue;
}
- /* Schedule tasklet on this channel */
- tasklet_schedule(&plchan->tasklet);
+ spin_lock(&plchan->vc.lock);
+ tx = plchan->at;
+ if (tx) {
+ plchan->at = NULL;
+ /*
+ * This descriptor is done, release its mux
+ * reservation.
+ */
+ pl08x_release_mux(plchan);
+ tx->done = true;
+ vchan_cookie_complete(&tx->vd);
+
+ /*
+ * And start the next descriptor (if any),
+ * otherwise free this channel.
+ */
+ if (vchan_next_desc(&plchan->vc))
+ pl08x_start_next_txd(plchan);
+ else
+ pl08x_phy_free(plchan);
+ }
+ spin_unlock(&plchan->vc.lock);
+
mask |= (1 << i);
}
}
@@ -1654,16 +1688,10 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
{
- u32 cctl = pl08x_cctl(chan->cd->cctl);
-
chan->slave = true;
chan->name = chan->cd->bus_id;
- chan->src_addr = chan->cd->addr;
- chan->dst_addr = chan->cd->addr;
- chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
- pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
- chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
- pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+ chan->cfg.src_addr = chan->cd->addr;
+ chan->cfg.dst_addr = chan->cd->addr;
}
/*
@@ -1693,6 +1721,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan->host = pl08x;
chan->state = PL08X_CHAN_IDLE;
+ chan->signal = -1;
if (slave) {
chan->cd = &pl08x->pd->slave_channels[i];
@@ -1705,26 +1734,12 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
return -ENOMEM;
}
}
- if (chan->cd->circular_buffer) {
- dev_err(&pl08x->adev->dev,
- "channel %s: circular buffers not supported\n",
- chan->name);
- kfree(chan);
- continue;
- }
dev_dbg(&pl08x->adev->dev,
"initialize virtual channel \"%s\"\n",
chan->name);
- chan->chan.device = dmadev;
- dma_cookie_init(&chan->chan);
-
- spin_lock_init(&chan->lock);
- INIT_LIST_HEAD(&chan->pend_list);
- tasklet_init(&chan->tasklet, pl08x_tasklet,
- (unsigned long) chan);
-
- list_add_tail(&chan->chan.device_node, &dmadev->channels);
+ chan->vc.desc_free = pl08x_desc_free;
+ vchan_init(&chan->vc, dmadev);
}
dev_info(&pl08x->adev->dev, "initialized %d virtual %s channels\n",
i, slave ? "slave" : "memcpy");
@@ -1737,8 +1752,8 @@ static void pl08x_free_virtual_channels(struct dma_device *dmadev)
struct pl08x_dma_chan *next;
list_for_each_entry_safe(chan,
- next, &dmadev->channels, chan.device_node) {
- list_del(&chan->chan.device_node);
+ next, &dmadev->channels, vc.chan.device_node) {
+ list_del(&chan->vc.chan.device_node);
kfree(chan);
}
}
@@ -1791,7 +1806,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "\nPL08x virtual memcpy channels:\n");
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
- list_for_each_entry(chan, &pl08x->memcpy.channels, chan.device_node) {
+ list_for_each_entry(chan, &pl08x->memcpy.channels, vc.chan.device_node) {
seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1799,7 +1814,7 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
seq_printf(s, "\nPL08x virtual slave channels:\n");
seq_printf(s, "CHANNEL:\tSTATE:\n");
seq_printf(s, "--------\t------\n");
- list_for_each_entry(chan, &pl08x->slave.channels, chan.device_node) {
+ list_for_each_entry(chan, &pl08x->slave.channels, vc.chan.device_node) {
seq_printf(s, "%s\t\t%s\n", chan->name,
pl08x_state_str(chan->state));
}
@@ -1851,9 +1866,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto out_no_pl08x;
}
- pm_runtime_set_active(&adev->dev);
- pm_runtime_enable(&adev->dev);
-
/* Initialize memcpy engine */
dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask);
pl08x->memcpy.dev = &adev->dev;
@@ -1903,8 +1915,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto out_no_lli_pool;
}
- spin_lock_init(&pl08x->lock);
-
pl08x->base = ioremap(adev->res.start, resource_size(&adev->res));
if (!pl08x->base) {
ret = -ENOMEM;
@@ -1942,7 +1952,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
ch->id = i;
ch->base = pl08x->base + PL080_Cx_BASE(i);
spin_lock_init(&ch->lock);
- ch->signal = -1;
/*
* Nomadik variants can have channels that are locked
@@ -2007,7 +2016,6 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
amba_part(adev), amba_rev(adev),
(unsigned long long)adev->res.start, adev->irq[0]);
- pm_runtime_put(&adev->dev);
return 0;
out_no_slave_reg:
@@ -2026,9 +2034,6 @@ out_no_ioremap:
dma_pool_destroy(pl08x->pool);
out_no_lli_pool:
out_no_platdata:
- pm_runtime_put(&adev->dev);
- pm_runtime_disable(&adev->dev);
-
kfree(pl08x);
out_no_pl08x:
amba_release_regions(adev);
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 3934fcc4e00b..7ab6e26664a7 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -661,7 +661,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
flags);
if (unlikely(!atslave || !sg_len)) {
- dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
+ dev_dbg(chan2dev(chan), "prep_slave_sg: sg length is zero!\n");
return NULL;
}
@@ -689,6 +689,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -724,6 +729,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -757,6 +767,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
err_desc_get:
dev_err(chan2dev(chan), "not enough descriptors available\n");
+err:
atc_desc_put(atchan, first);
return NULL;
}
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index fcfeb3cd8d31..5084975d793c 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -172,7 +172,8 @@ struct imxdma_engine {
struct device_dma_parameters dma_parms;
struct dma_device dma_device;
void __iomem *base;
- struct clk *dma_clk;
+ struct clk *dma_ahb;
+ struct clk *dma_ipg;
spinlock_t lock;
struct imx_dma_2d_config slots_2d[IMX_DMA_2D_SLOTS];
struct imxdma_channel channel[IMX_DMA_CHANNELS];
@@ -976,10 +977,20 @@ static int __init imxdma_probe(struct platform_device *pdev)
return 0;
}
- imxdma->dma_clk = clk_get(NULL, "dma");
- if (IS_ERR(imxdma->dma_clk))
- return PTR_ERR(imxdma->dma_clk);
- clk_enable(imxdma->dma_clk);
+ imxdma->dma_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(imxdma->dma_ipg)) {
+ ret = PTR_ERR(imxdma->dma_ipg);
+ goto err_clk;
+ }
+
+ imxdma->dma_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(imxdma->dma_ahb)) {
+ ret = PTR_ERR(imxdma->dma_ahb);
+ goto err_clk;
+ }
+
+ clk_prepare_enable(imxdma->dma_ipg);
+ clk_prepare_enable(imxdma->dma_ahb);
/* reset DMA module */
imx_dmav1_writel(imxdma, DCR_DRST, DMA_DCR);
@@ -988,16 +999,14 @@ static int __init imxdma_probe(struct platform_device *pdev)
ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma);
if (ret) {
dev_warn(imxdma->dev, "Can't register IRQ for DMA\n");
- kfree(imxdma);
- return ret;
+ goto err_enable;
}
ret = request_irq(MX1_DMA_ERR, imxdma_err_handler, 0, "DMA", imxdma);
if (ret) {
dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n");
free_irq(MX1_DMA_INT, NULL);
- kfree(imxdma);
- return ret;
+ goto err_enable;
}
}
@@ -1094,7 +1103,10 @@ err_init:
free_irq(MX1_DMA_INT, NULL);
free_irq(MX1_DMA_ERR, NULL);
}
-
+err_enable:
+ clk_disable_unprepare(imxdma->dma_ipg);
+ clk_disable_unprepare(imxdma->dma_ahb);
+err_clk:
kfree(imxdma);
return ret;
}
@@ -1114,7 +1126,9 @@ static int __exit imxdma_remove(struct platform_device *pdev)
free_irq(MX1_DMA_ERR, NULL);
}
- kfree(imxdma);
+ clk_disable_unprepare(imxdma->dma_ipg);
+ clk_disable_unprepare(imxdma->dma_ahb);
+ kfree(imxdma);
return 0;
}
diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c
new file mode 100644
index 000000000000..ae0561826137
--- /dev/null
+++ b/drivers/dma/omap-dma.c
@@ -0,0 +1,669 @@
+/*
+ * OMAP DMAengine support
+ *
+ * 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/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/omap-dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include <plat/dma.h>
+
+struct omap_dmadev {
+ struct dma_device ddev;
+ spinlock_t lock;
+ struct tasklet_struct task;
+ struct list_head pending;
+};
+
+struct omap_chan {
+ struct virt_dma_chan vc;
+ struct list_head node;
+
+ struct dma_slave_config cfg;
+ unsigned dma_sig;
+ bool cyclic;
+
+ int dma_ch;
+ struct omap_desc *desc;
+ unsigned sgidx;
+};
+
+struct omap_sg {
+ dma_addr_t addr;
+ uint32_t en; /* number of elements (24-bit) */
+ uint32_t fn; /* number of frames (16-bit) */
+};
+
+struct omap_desc {
+ struct virt_dma_desc vd;
+ enum dma_transfer_direction dir;
+ dma_addr_t dev_addr;
+
+ int16_t fi; /* for OMAP_DMA_SYNC_PACKET */
+ uint8_t es; /* OMAP_DMA_DATA_TYPE_xxx */
+ uint8_t sync_mode; /* OMAP_DMA_SYNC_xxx */
+ uint8_t sync_type; /* OMAP_DMA_xxx_SYNC* */
+ uint8_t periph_port; /* Peripheral port */
+
+ unsigned sglen;
+ struct omap_sg sg[0];
+};
+
+static const unsigned es_bytes[] = {
+ [OMAP_DMA_DATA_TYPE_S8] = 1,
+ [OMAP_DMA_DATA_TYPE_S16] = 2,
+ [OMAP_DMA_DATA_TYPE_S32] = 4,
+};
+
+static inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
+{
+ return container_of(d, struct omap_dmadev, ddev);
+}
+
+static inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct omap_chan, vc.chan);
+}
+
+static inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
+{
+ return container_of(t, struct omap_desc, vd.tx);
+}
+
+static void omap_dma_desc_free(struct virt_dma_desc *vd)
+{
+ kfree(container_of(vd, struct omap_desc, vd));
+}
+
+static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d,
+ unsigned idx)
+{
+ struct omap_sg *sg = d->sg + idx;
+
+ if (d->dir == DMA_DEV_TO_MEM)
+ omap_set_dma_dest_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+ else
+ omap_set_dma_src_params(c->dma_ch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC, sg->addr, 0, 0);
+
+ omap_set_dma_transfer_params(c->dma_ch, d->es, sg->en, sg->fn,
+ d->sync_mode, c->dma_sig, d->sync_type);
+
+ omap_start_dma(c->dma_ch);
+}
+
+static void omap_dma_start_desc(struct omap_chan *c)
+{
+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+ struct omap_desc *d;
+
+ if (!vd) {
+ c->desc = NULL;
+ return;
+ }
+
+ list_del(&vd->node);
+
+ c->desc = d = to_omap_dma_desc(&vd->tx);
+ c->sgidx = 0;
+
+ if (d->dir == DMA_DEV_TO_MEM)
+ omap_set_dma_src_params(c->dma_ch, d->periph_port,
+ OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+ else
+ omap_set_dma_dest_params(c->dma_ch, d->periph_port,
+ OMAP_DMA_AMODE_CONSTANT, d->dev_addr, 0, d->fi);
+
+ omap_dma_start_sg(c, d, 0);
+}
+
+static void omap_dma_callback(int ch, u16 status, void *data)
+{
+ struct omap_chan *c = data;
+ struct omap_desc *d;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ d = c->desc;
+ if (d) {
+ if (!c->cyclic) {
+ if (++c->sgidx < d->sglen) {
+ omap_dma_start_sg(c, d, c->sgidx);
+ } else {
+ omap_dma_start_desc(c);
+ vchan_cookie_complete(&d->vd);
+ }
+ } else {
+ vchan_cyclic_callback(&d->vd);
+ }
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+/*
+ * This callback schedules all pending channels. We could be more
+ * clever here by postponing allocation of the real DMA channels to
+ * this point, and freeing them when our virtual channel becomes idle.
+ *
+ * We would then need to deal with 'all channels in-use'
+ */
+static void omap_dma_sched(unsigned long data)
+{
+ struct omap_dmadev *d = (struct omap_dmadev *)data;
+ LIST_HEAD(head);
+
+ spin_lock_irq(&d->lock);
+ list_splice_tail_init(&d->pending, &head);
+ spin_unlock_irq(&d->lock);
+
+ while (!list_empty(&head)) {
+ struct omap_chan *c = list_first_entry(&head,
+ struct omap_chan, node);
+
+ spin_lock_irq(&c->vc.lock);
+ list_del_init(&c->node);
+ omap_dma_start_desc(c);
+ spin_unlock_irq(&c->vc.lock);
+ }
+}
+
+static int omap_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+
+ dev_info(c->vc.chan.device->dev, "allocating channel for %u\n", c->dma_sig);
+
+ return omap_request_dma(c->dma_sig, "DMA engine",
+ omap_dma_callback, c, &c->dma_ch);
+}
+
+static void omap_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+
+ vchan_free_chan_resources(&c->vc);
+ omap_free_dma(c->dma_ch);
+
+ dev_info(c->vc.chan.device->dev, "freeing channel for %u\n", c->dma_sig);
+}
+
+static size_t omap_dma_sg_size(struct omap_sg *sg)
+{
+ return sg->en * sg->fn;
+}
+
+static size_t omap_dma_desc_size(struct omap_desc *d)
+{
+ unsigned i;
+ size_t size;
+
+ for (size = i = 0; i < d->sglen; i++)
+ size += omap_dma_sg_size(&d->sg[i]);
+
+ return size * es_bytes[d->es];
+}
+
+static size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
+{
+ unsigned i;
+ size_t size, es_size = es_bytes[d->es];
+
+ for (size = i = 0; i < d->sglen; i++) {
+ size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size;
+
+ if (size)
+ size += this_size;
+ else if (addr >= d->sg[i].addr &&
+ addr < d->sg[i].addr + this_size)
+ size += d->sg[i].addr + this_size - addr;
+ }
+ return size;
+}
+
+static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ struct virt_dma_desc *vd;
+ enum dma_status ret;
+ unsigned long flags;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_SUCCESS || !txstate)
+ return ret;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ vd = vchan_find_desc(&c->vc, cookie);
+ if (vd) {
+ txstate->residue = omap_dma_desc_size(to_omap_dma_desc(&vd->tx));
+ } else if (c->desc && c->desc->vd.tx.cookie == cookie) {
+ struct omap_desc *d = c->desc;
+ dma_addr_t pos;
+
+ if (d->dir == DMA_MEM_TO_DEV)
+ pos = omap_get_dma_src_pos(c->dma_ch);
+ else if (d->dir == DMA_DEV_TO_MEM)
+ pos = omap_get_dma_dst_pos(c->dma_ch);
+ else
+ pos = 0;
+
+ txstate->residue = omap_dma_desc_size_pos(d, pos);
+ } else {
+ txstate->residue = 0;
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+
+ return ret;
+}
+
+static void omap_dma_issue_pending(struct dma_chan *chan)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc) && !c->desc) {
+ struct omap_dmadev *d = to_omap_dma_dev(chan->device);
+ spin_lock(&d->lock);
+ if (list_empty(&c->node))
+ list_add_tail(&c->node, &d->pending);
+ spin_unlock(&d->lock);
+ tasklet_schedule(&d->task);
+ }
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
+ enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct scatterlist *sgent;
+ struct omap_desc *d;
+ dma_addr_t dev_addr;
+ unsigned i, j = 0, es, en, frame_bytes, sync_type;
+ u32 burst;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_addr = c->cfg.src_addr;
+ dev_width = c->cfg.src_addr_width;
+ burst = c->cfg.src_maxburst;
+ sync_type = OMAP_DMA_SRC_SYNC;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_addr = c->cfg.dst_addr;
+ dev_width = c->cfg.dst_addr_width;
+ burst = c->cfg.dst_maxburst;
+ sync_type = OMAP_DMA_DST_SYNC;
+ } else {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ /* Bus width translates to the element size (ES) */
+ switch (dev_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ es = OMAP_DMA_DATA_TYPE_S8;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S32;
+ break;
+ default: /* not reached */
+ return NULL;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ d->dir = dir;
+ d->dev_addr = dev_addr;
+ d->es = es;
+ d->sync_mode = OMAP_DMA_SYNC_FRAME;
+ d->sync_type = sync_type;
+ d->periph_port = OMAP_DMA_PORT_TIPB;
+
+ /*
+ * Build our scatterlist entries: each contains the address,
+ * the number of elements (EN) in each frame, and the number of
+ * frames (FN). Number of bytes for this entry = ES * EN * FN.
+ *
+ * Burst size translates to number of elements with frame sync.
+ * Note: DMA engine defines burst to be the number of dev-width
+ * transfers.
+ */
+ en = burst;
+ frame_bytes = es_bytes[es] * en;
+ for_each_sg(sgl, sgent, sglen, i) {
+ d->sg[j].addr = sg_dma_address(sgent);
+ d->sg[j].en = en;
+ d->sg[j].fn = sg_dma_len(sgent) / frame_bytes;
+ j++;
+ }
+
+ d->sglen = j;
+
+ return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
+}
+
+static struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction dir, void *context)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct omap_desc *d;
+ dma_addr_t dev_addr;
+ unsigned es, sync_type;
+ u32 burst;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_addr = c->cfg.src_addr;
+ dev_width = c->cfg.src_addr_width;
+ burst = c->cfg.src_maxburst;
+ sync_type = OMAP_DMA_SRC_SYNC;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_addr = c->cfg.dst_addr;
+ dev_width = c->cfg.dst_addr_width;
+ burst = c->cfg.dst_maxburst;
+ sync_type = OMAP_DMA_DST_SYNC;
+ } else {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ /* Bus width translates to the element size (ES) */
+ switch (dev_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ es = OMAP_DMA_DATA_TYPE_S8;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S16;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ es = OMAP_DMA_DATA_TYPE_S32;
+ break;
+ default: /* not reached */
+ return NULL;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
+ if (!d)
+ return NULL;
+
+ d->dir = dir;
+ d->dev_addr = dev_addr;
+ d->fi = burst;
+ d->es = es;
+ d->sync_mode = OMAP_DMA_SYNC_PACKET;
+ d->sync_type = sync_type;
+ d->periph_port = OMAP_DMA_PORT_MPUI;
+ d->sg[0].addr = buf_addr;
+ d->sg[0].en = period_len / es_bytes[es];
+ d->sg[0].fn = buf_len / period_len;
+ d->sglen = 1;
+
+ if (!c->cyclic) {
+ c->cyclic = true;
+ omap_dma_link_lch(c->dma_ch, c->dma_ch);
+ omap_enable_dma_irq(c->dma_ch, OMAP_DMA_FRAME_IRQ);
+ omap_disable_dma_irq(c->dma_ch, OMAP_DMA_BLOCK_IRQ);
+ }
+
+ if (!cpu_class_is_omap1()) {
+ omap_set_dma_src_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(c->dma_ch, OMAP_DMA_DATA_BURST_16);
+ }
+
+ return vchan_tx_prep(&c->vc, &d->vd, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+}
+
+static int omap_dma_slave_config(struct omap_chan *c, struct dma_slave_config *cfg)
+{
+ if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
+ cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+ return -EINVAL;
+
+ memcpy(&c->cfg, cfg, sizeof(c->cfg));
+
+ return 0;
+}
+
+static int omap_dma_terminate_all(struct omap_chan *c)
+{
+ struct omap_dmadev *d = to_omap_dma_dev(c->vc.chan.device);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&c->vc.lock, flags);
+
+ /* Prevent this channel being scheduled */
+ spin_lock(&d->lock);
+ list_del_init(&c->node);
+ spin_unlock(&d->lock);
+
+ /*
+ * Stop DMA activity: we assume the callback will not be called
+ * after omap_stop_dma() returns (even if it does, it will see
+ * c->desc is NULL and exit.)
+ */
+ if (c->desc) {
+ c->desc = NULL;
+ omap_stop_dma(c->dma_ch);
+ }
+
+ if (c->cyclic) {
+ c->cyclic = false;
+ omap_dma_unlink_lch(c->dma_ch, c->dma_ch);
+ }
+
+ vchan_get_all_descriptors(&c->vc, &head);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_dma_desc_free_list(&c->vc, &head);
+
+ return 0;
+}
+
+static int omap_dma_pause(struct omap_chan *c)
+{
+ /* FIXME: not supported by platform private API */
+ return -EINVAL;
+}
+
+static int omap_dma_resume(struct omap_chan *c)
+{
+ /* FIXME: not supported by platform private API */
+ return -EINVAL;
+}
+
+static int omap_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ int ret;
+
+ switch (cmd) {
+ case DMA_SLAVE_CONFIG:
+ ret = omap_dma_slave_config(c, (struct dma_slave_config *)arg);
+ break;
+
+ case DMA_TERMINATE_ALL:
+ ret = omap_dma_terminate_all(c);
+ break;
+
+ case DMA_PAUSE:
+ ret = omap_dma_pause(c);
+ break;
+
+ case DMA_RESUME:
+ ret = omap_dma_resume(c);
+ break;
+
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+static int omap_dma_chan_init(struct omap_dmadev *od, int dma_sig)
+{
+ struct omap_chan *c;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ c->dma_sig = dma_sig;
+ c->vc.desc_free = omap_dma_desc_free;
+ vchan_init(&c->vc, &od->ddev);
+ INIT_LIST_HEAD(&c->node);
+
+ od->ddev.chancnt++;
+
+ return 0;
+}
+
+static void omap_dma_free(struct omap_dmadev *od)
+{
+ tasklet_kill(&od->task);
+ while (!list_empty(&od->ddev.channels)) {
+ struct omap_chan *c = list_first_entry(&od->ddev.channels,
+ struct omap_chan, vc.chan.device_node);
+
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
+ kfree(c);
+ }
+ kfree(od);
+}
+
+static int omap_dma_probe(struct platform_device *pdev)
+{
+ struct omap_dmadev *od;
+ int rc, i;
+
+ od = kzalloc(sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;
+
+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
+ od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
+ od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
+ od->ddev.device_tx_status = omap_dma_tx_status;
+ od->ddev.device_issue_pending = omap_dma_issue_pending;
+ od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
+ od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
+ od->ddev.device_control = omap_dma_control;
+ od->ddev.dev = &pdev->dev;
+ INIT_LIST_HEAD(&od->ddev.channels);
+ INIT_LIST_HEAD(&od->pending);
+ spin_lock_init(&od->lock);
+
+ tasklet_init(&od->task, omap_dma_sched, (unsigned long)od);
+
+ for (i = 0; i < 127; i++) {
+ rc = omap_dma_chan_init(od, i);
+ if (rc) {
+ omap_dma_free(od);
+ return rc;
+ }
+ }
+
+ rc = dma_async_device_register(&od->ddev);
+ if (rc) {
+ pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
+ rc);
+ omap_dma_free(od);
+ } else {
+ platform_set_drvdata(pdev, od);
+ }
+
+ dev_info(&pdev->dev, "OMAP DMA engine driver\n");
+
+ return rc;
+}
+
+static int omap_dma_remove(struct platform_device *pdev)
+{
+ struct omap_dmadev *od = platform_get_drvdata(pdev);
+
+ dma_async_device_unregister(&od->ddev);
+ omap_dma_free(od);
+
+ return 0;
+}
+
+static struct platform_driver omap_dma_driver = {
+ .probe = omap_dma_probe,
+ .remove = omap_dma_remove,
+ .driver = {
+ .name = "omap-dma-engine",
+ .owner = THIS_MODULE,
+ },
+};
+
+bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ if (chan->device->dev->driver == &omap_dma_driver.driver) {
+ struct omap_chan *c = to_omap_dma_chan(chan);
+ unsigned req = *(unsigned *)param;
+
+ return req == c->dma_sig;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(omap_dma_filter_fn);
+
+static struct platform_device *pdev;
+
+static const struct platform_device_info omap_dma_dev_info = {
+ .name = "omap-dma-engine",
+ .id = -1,
+ .dma_mask = DMA_BIT_MASK(32),
+};
+
+static int omap_dma_init(void)
+{
+ int rc = platform_driver_register(&omap_dma_driver);
+
+ if (rc == 0) {
+ pdev = platform_device_register_full(&omap_dma_dev_info);
+ if (IS_ERR(pdev)) {
+ platform_driver_unregister(&omap_dma_driver);
+ rc = PTR_ERR(pdev);
+ }
+ }
+ return rc;
+}
+subsys_initcall(omap_dma_init);
+
+static void __exit omap_dma_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&omap_dma_driver);
+}
+module_exit(omap_dma_exit);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index e4feba6b03c0..f5843bc80baa 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1567,17 +1567,19 @@ static int pl330_submit_req(void *ch_id, struct pl330_req *r)
goto xfer_exit;
}
- /* Prefer Secure Channel */
- if (!_manager_ns(thrd))
- r->cfg->nonsecure = 0;
- else
- r->cfg->nonsecure = 1;
/* Use last settings, if not provided */
- if (r->cfg)
+ if (r->cfg) {
+ /* Prefer Secure Channel */
+ if (!_manager_ns(thrd))
+ r->cfg->nonsecure = 0;
+ else
+ r->cfg->nonsecure = 1;
+
ccr = _prepare_ccr(r->cfg);
- else
+ } else {
ccr = readl(regs + CC(thrd->id));
+ }
/* If this req doesn't have valid xfer settings */
if (!_is_valid(ccr)) {
@@ -2928,6 +2930,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan);
pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
+ if (!pdmac->peripherals) {
+ ret = -ENOMEM;
+ dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n");
+ goto probe_err5;
+ }
for (i = 0; i < num_chan; i++) {
pch = &pdmac->peripherals[i];
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
index ec78ccef9132..f5a73606217e 100644
--- a/drivers/dma/sa11x0-dma.c
+++ b/drivers/dma/sa11x0-dma.c
@@ -21,6 +21,8 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include "virt-dma.h"
+
#define NR_PHY_CHAN 6
#define DMA_ALIGN 3
#define DMA_MAX_SIZE 0x1fff
@@ -72,12 +74,13 @@ struct sa11x0_dma_sg {
};
struct sa11x0_dma_desc {
- struct dma_async_tx_descriptor tx;
+ struct virt_dma_desc vd;
+
u32 ddar;
size_t size;
+ unsigned period;
+ bool cyclic;
- /* maybe protected by c->lock */
- struct list_head node;
unsigned sglen;
struct sa11x0_dma_sg sg[0];
};
@@ -85,15 +88,11 @@ struct sa11x0_dma_desc {
struct sa11x0_dma_phy;
struct sa11x0_dma_chan {
- struct dma_chan chan;
- spinlock_t lock;
- dma_cookie_t lc;
+ struct virt_dma_chan vc;
- /* protected by c->lock */
+ /* protected by c->vc.lock */
struct sa11x0_dma_phy *phy;
enum dma_status status;
- struct list_head desc_submitted;
- struct list_head desc_issued;
/* protected by d->lock */
struct list_head node;
@@ -109,7 +108,7 @@ struct sa11x0_dma_phy {
struct sa11x0_dma_chan *vchan;
- /* Protected by c->lock */
+ /* Protected by c->vc.lock */
unsigned sg_load;
struct sa11x0_dma_desc *txd_load;
unsigned sg_done;
@@ -127,13 +126,12 @@ struct sa11x0_dma_dev {
spinlock_t lock;
struct tasklet_struct task;
struct list_head chan_pending;
- struct list_head desc_complete;
struct sa11x0_dma_phy phy[NR_PHY_CHAN];
};
static struct sa11x0_dma_chan *to_sa11x0_dma_chan(struct dma_chan *chan)
{
- return container_of(chan, struct sa11x0_dma_chan, chan);
+ return container_of(chan, struct sa11x0_dma_chan, vc.chan);
}
static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
@@ -141,27 +139,26 @@ static struct sa11x0_dma_dev *to_sa11x0_dma(struct dma_device *dmadev)
return container_of(dmadev, struct sa11x0_dma_dev, slave);
}
-static struct sa11x0_dma_desc *to_sa11x0_dma_tx(struct dma_async_tx_descriptor *tx)
+static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
{
- return container_of(tx, struct sa11x0_dma_desc, tx);
+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
+
+ return vd ? container_of(vd, struct sa11x0_dma_desc, vd) : NULL;
}
-static struct sa11x0_dma_desc *sa11x0_dma_next_desc(struct sa11x0_dma_chan *c)
+static void sa11x0_dma_free_desc(struct virt_dma_desc *vd)
{
- if (list_empty(&c->desc_issued))
- return NULL;
-
- return list_first_entry(&c->desc_issued, struct sa11x0_dma_desc, node);
+ kfree(container_of(vd, struct sa11x0_dma_desc, vd));
}
static void sa11x0_dma_start_desc(struct sa11x0_dma_phy *p, struct sa11x0_dma_desc *txd)
{
- list_del(&txd->node);
+ list_del(&txd->vd.node);
p->txd_load = txd;
p->sg_load = 0;
dev_vdbg(p->dev->slave.dev, "pchan %u: txd %p[%x]: starting: DDAR:%x\n",
- p->num, txd, txd->tx.cookie, txd->ddar);
+ p->num, &txd->vd, txd->vd.tx.cookie, txd->ddar);
}
static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
@@ -183,19 +180,24 @@ static void noinline sa11x0_dma_start_sg(struct sa11x0_dma_phy *p,
return;
if (p->sg_load == txd->sglen) {
- struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
+ if (!txd->cyclic) {
+ struct sa11x0_dma_desc *txn = sa11x0_dma_next_desc(c);
- /*
- * We have reached the end of the current descriptor.
- * Peek at the next descriptor, and if compatible with
- * the current, start processing it.
- */
- if (txn && txn->ddar == txd->ddar) {
- txd = txn;
- sa11x0_dma_start_desc(p, txn);
+ /*
+ * We have reached the end of the current descriptor.
+ * Peek at the next descriptor, and if compatible with
+ * the current, start processing it.
+ */
+ if (txn && txn->ddar == txd->ddar) {
+ txd = txn;
+ sa11x0_dma_start_desc(p, txn);
+ } else {
+ p->txd_load = NULL;
+ return;
+ }
} else {
- p->txd_load = NULL;
- return;
+ /* Cyclic: reset back to beginning */
+ p->sg_load = 0;
}
}
@@ -229,21 +231,21 @@ static void noinline sa11x0_dma_complete(struct sa11x0_dma_phy *p,
struct sa11x0_dma_desc *txd = p->txd_done;
if (++p->sg_done == txd->sglen) {
- struct sa11x0_dma_dev *d = p->dev;
-
- dev_vdbg(d->slave.dev, "pchan %u: txd %p[%x]: completed\n",
- p->num, p->txd_done, p->txd_done->tx.cookie);
-
- c->lc = txd->tx.cookie;
+ if (!txd->cyclic) {
+ vchan_cookie_complete(&txd->vd);
- spin_lock(&d->lock);
- list_add_tail(&txd->node, &d->desc_complete);
- spin_unlock(&d->lock);
+ p->sg_done = 0;
+ p->txd_done = p->txd_load;
- p->sg_done = 0;
- p->txd_done = p->txd_load;
+ if (!p->txd_done)
+ tasklet_schedule(&p->dev->task);
+ } else {
+ if ((p->sg_done % txd->period) == 0)
+ vchan_cyclic_callback(&txd->vd);
- tasklet_schedule(&d->task);
+ /* Cyclic: reset back to beginning */
+ p->sg_done = 0;
+ }
}
sa11x0_dma_start_sg(p, c);
@@ -280,7 +282,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
if (c) {
unsigned long flags;
- spin_lock_irqsave(&c->lock, flags);
+ spin_lock_irqsave(&c->vc.lock, flags);
/*
* Now that we're holding the lock, check that the vchan
* really is associated with this pchan before touching the
@@ -294,7 +296,7 @@ static irqreturn_t sa11x0_dma_irq(int irq, void *dev_id)
if (dcsr & DCSR_DONEB)
sa11x0_dma_complete(p, c);
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
}
return IRQ_HANDLED;
@@ -332,28 +334,15 @@ static void sa11x0_dma_tasklet(unsigned long arg)
struct sa11x0_dma_dev *d = (struct sa11x0_dma_dev *)arg;
struct sa11x0_dma_phy *p;
struct sa11x0_dma_chan *c;
- struct sa11x0_dma_desc *txd, *txn;
- LIST_HEAD(head);
unsigned pch, pch_alloc = 0;
dev_dbg(d->slave.dev, "tasklet enter\n");
- /* Get the completed tx descriptors */
- spin_lock_irq(&d->lock);
- list_splice_init(&d->desc_complete, &head);
- spin_unlock_irq(&d->lock);
-
- list_for_each_entry(txd, &head, node) {
- c = to_sa11x0_dma_chan(txd->tx.chan);
-
- dev_dbg(d->slave.dev, "vchan %p: txd %p[%x] completed\n",
- c, txd, txd->tx.cookie);
-
- spin_lock_irq(&c->lock);
+ list_for_each_entry(c, &d->slave.channels, vc.chan.device_node) {
+ spin_lock_irq(&c->vc.lock);
p = c->phy;
- if (p) {
- if (!p->txd_done)
- sa11x0_dma_start_txd(c);
+ if (p && !p->txd_done) {
+ sa11x0_dma_start_txd(c);
if (!p->txd_done) {
/* No current txd associated with this channel */
dev_dbg(d->slave.dev, "pchan %u: free\n", p->num);
@@ -363,7 +352,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
p->vchan = NULL;
}
}
- spin_unlock_irq(&c->lock);
+ spin_unlock_irq(&c->vc.lock);
}
spin_lock_irq(&d->lock);
@@ -380,7 +369,7 @@ static void sa11x0_dma_tasklet(unsigned long arg)
/* Mark this channel allocated */
p->vchan = c;
- dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, c);
+ dev_dbg(d->slave.dev, "pchan %u: alloc vchan %p\n", pch, &c->vc);
}
}
spin_unlock_irq(&d->lock);
@@ -390,42 +379,18 @@ static void sa11x0_dma_tasklet(unsigned long arg)
p = &d->phy[pch];
c = p->vchan;
- spin_lock_irq(&c->lock);
+ spin_lock_irq(&c->vc.lock);
c->phy = p;
sa11x0_dma_start_txd(c);
- spin_unlock_irq(&c->lock);
+ spin_unlock_irq(&c->vc.lock);
}
}
- /* Now free the completed tx descriptor, and call their callbacks */
- list_for_each_entry_safe(txd, txn, &head, node) {
- dma_async_tx_callback callback = txd->tx.callback;
- void *callback_param = txd->tx.callback_param;
-
- dev_dbg(d->slave.dev, "txd %p[%x]: callback and free\n",
- txd, txd->tx.cookie);
-
- kfree(txd);
-
- if (callback)
- callback(callback_param);
- }
-
dev_dbg(d->slave.dev, "tasklet exit\n");
}
-static void sa11x0_dma_desc_free(struct sa11x0_dma_dev *d, struct list_head *head)
-{
- struct sa11x0_dma_desc *txd, *txn;
-
- list_for_each_entry_safe(txd, txn, head, node) {
- dev_dbg(d->slave.dev, "txd %p: freeing\n", txd);
- kfree(txd);
- }
-}
-
static int sa11x0_dma_alloc_chan_resources(struct dma_chan *chan)
{
return 0;
@@ -436,18 +401,12 @@ static void sa11x0_dma_free_chan_resources(struct dma_chan *chan)
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
unsigned long flags;
- LIST_HEAD(head);
- spin_lock_irqsave(&c->lock, flags);
- spin_lock(&d->lock);
+ spin_lock_irqsave(&d->lock, flags);
list_del_init(&c->node);
- spin_unlock(&d->lock);
-
- list_splice_tail_init(&c->desc_submitted, &head);
- list_splice_tail_init(&c->desc_issued, &head);
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&d->lock, flags);
- sa11x0_dma_desc_free(d, &head);
+ vchan_free_chan_resources(&c->vc);
}
static dma_addr_t sa11x0_dma_pos(struct sa11x0_dma_phy *p)
@@ -472,33 +431,47 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
struct sa11x0_dma_phy *p;
- struct sa11x0_dma_desc *txd;
- dma_cookie_t last_used, last_complete;
+ struct virt_dma_desc *vd;
unsigned long flags;
enum dma_status ret;
- size_t bytes = 0;
-
- last_used = c->chan.cookie;
- last_complete = c->lc;
- ret = dma_async_is_complete(cookie, last_complete, last_used);
- if (ret == DMA_SUCCESS) {
- dma_set_tx_state(state, last_complete, last_used, 0);
+ ret = dma_cookie_status(&c->vc.chan, cookie, state);
+ if (ret == DMA_SUCCESS)
return ret;
- }
- spin_lock_irqsave(&c->lock, flags);
+ if (!state)
+ return c->status;
+
+ spin_lock_irqsave(&c->vc.lock, flags);
p = c->phy;
- ret = c->status;
- if (p) {
- dma_addr_t addr = sa11x0_dma_pos(p);
- dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+ /*
+ * If the cookie is on our issue queue, then the residue is
+ * its total size.
+ */
+ vd = vchan_find_desc(&c->vc, cookie);
+ if (vd) {
+ state->residue = container_of(vd, struct sa11x0_dma_desc, vd)->size;
+ } else if (!p) {
+ state->residue = 0;
+ } else {
+ struct sa11x0_dma_desc *txd;
+ size_t bytes = 0;
- txd = p->txd_done;
+ if (p->txd_done && p->txd_done->vd.tx.cookie == cookie)
+ txd = p->txd_done;
+ else if (p->txd_load && p->txd_load->vd.tx.cookie == cookie)
+ txd = p->txd_load;
+ else
+ txd = NULL;
+
+ ret = c->status;
if (txd) {
+ dma_addr_t addr = sa11x0_dma_pos(p);
unsigned i;
+ dev_vdbg(d->slave.dev, "tx_status: addr:%x\n", addr);
+
for (i = 0; i < txd->sglen; i++) {
dev_vdbg(d->slave.dev, "tx_status: [%u] %x+%x\n",
i, txd->sg[i].addr, txd->sg[i].len);
@@ -521,17 +494,11 @@ static enum dma_status sa11x0_dma_tx_status(struct dma_chan *chan,
bytes += txd->sg[i].len;
}
}
- if (txd != p->txd_load && p->txd_load)
- bytes += p->txd_load->size;
- }
- list_for_each_entry(txd, &c->desc_issued, node) {
- bytes += txd->size;
+ state->residue = bytes;
}
- spin_unlock_irqrestore(&c->lock, flags);
-
- dma_set_tx_state(state, last_complete, last_used, bytes);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
- dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", bytes);
+ dev_vdbg(d->slave.dev, "tx_status: bytes 0x%zx\n", state->residue);
return ret;
}
@@ -547,40 +514,20 @@ static void sa11x0_dma_issue_pending(struct dma_chan *chan)
struct sa11x0_dma_dev *d = to_sa11x0_dma(chan->device);
unsigned long flags;
- spin_lock_irqsave(&c->lock, flags);
- list_splice_tail_init(&c->desc_submitted, &c->desc_issued);
- if (!list_empty(&c->desc_issued)) {
- spin_lock(&d->lock);
- if (!c->phy && list_empty(&c->node)) {
- list_add_tail(&c->node, &d->chan_pending);
- tasklet_schedule(&d->task);
- dev_dbg(d->slave.dev, "vchan %p: issued\n", c);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ if (vchan_issue_pending(&c->vc)) {
+ if (!c->phy) {
+ spin_lock(&d->lock);
+ if (list_empty(&c->node)) {
+ list_add_tail(&c->node, &d->chan_pending);
+ tasklet_schedule(&d->task);
+ dev_dbg(d->slave.dev, "vchan %p: issued\n", &c->vc);
+ }
+ spin_unlock(&d->lock);
}
- spin_unlock(&d->lock);
} else
- dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", c);
- spin_unlock_irqrestore(&c->lock, flags);
-}
-
-static dma_cookie_t sa11x0_dma_tx_submit(struct dma_async_tx_descriptor *tx)
-{
- struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(tx->chan);
- struct sa11x0_dma_desc *txd = to_sa11x0_dma_tx(tx);
- unsigned long flags;
-
- spin_lock_irqsave(&c->lock, flags);
- c->chan.cookie += 1;
- if (c->chan.cookie < 0)
- c->chan.cookie = 1;
- txd->tx.cookie = c->chan.cookie;
-
- list_add_tail(&txd->node, &c->desc_submitted);
- spin_unlock_irqrestore(&c->lock, flags);
-
- dev_dbg(tx->chan->device->dev, "vchan %p: txd %p[%x]: submitted\n",
- c, txd, txd->tx.cookie);
-
- return txd->tx.cookie;
+ dev_dbg(d->slave.dev, "vchan %p: nothing to issue\n", &c->vc);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
}
static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
@@ -596,7 +543,7 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
/* SA11x0 channels can only operate in their native direction */
if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
- c, c->ddar, dir);
+ &c->vc, c->ddar, dir);
return NULL;
}
@@ -612,14 +559,14 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
j += DIV_ROUND_UP(len, DMA_MAX_SIZE & ~DMA_ALIGN) - 1;
if (addr & DMA_ALIGN) {
dev_dbg(chan->device->dev, "vchan %p: bad buffer alignment: %08x\n",
- c, addr);
+ &c->vc, addr);
return NULL;
}
}
txd = kzalloc(sizeof(*txd) + j * sizeof(txd->sg[0]), GFP_ATOMIC);
if (!txd) {
- dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", c);
+ dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
return NULL;
}
@@ -655,17 +602,73 @@ static struct dma_async_tx_descriptor *sa11x0_dma_prep_slave_sg(
} while (len);
}
- dma_async_tx_descriptor_init(&txd->tx, &c->chan);
- txd->tx.flags = flags;
- txd->tx.tx_submit = sa11x0_dma_tx_submit;
txd->ddar = c->ddar;
txd->size = size;
txd->sglen = j;
dev_dbg(chan->device->dev, "vchan %p: txd %p: size %u nr %u\n",
- c, txd, txd->size, txd->sglen);
+ &c->vc, &txd->vd, txd->size, txd->sglen);
- return &txd->tx;
+ return vchan_tx_prep(&c->vc, &txd->vd, flags);
+}
+
+static struct dma_async_tx_descriptor *sa11x0_dma_prep_dma_cyclic(
+ struct dma_chan *chan, dma_addr_t addr, size_t size, size_t period,
+ enum dma_transfer_direction dir, void *context)
+{
+ struct sa11x0_dma_chan *c = to_sa11x0_dma_chan(chan);
+ struct sa11x0_dma_desc *txd;
+ unsigned i, j, k, sglen, sgperiod;
+
+ /* SA11x0 channels can only operate in their native direction */
+ if (dir != (c->ddar & DDAR_RW ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV)) {
+ dev_err(chan->device->dev, "vchan %p: bad DMA direction: DDAR:%08x dir:%u\n",
+ &c->vc, c->ddar, dir);
+ return NULL;
+ }
+
+ sgperiod = DIV_ROUND_UP(period, DMA_MAX_SIZE & ~DMA_ALIGN);
+ sglen = size * sgperiod / period;
+
+ /* Do not allow zero-sized txds */
+ if (sglen == 0)
+ return NULL;
+
+ txd = kzalloc(sizeof(*txd) + sglen * sizeof(txd->sg[0]), GFP_ATOMIC);
+ if (!txd) {
+ dev_dbg(chan->device->dev, "vchan %p: kzalloc failed\n", &c->vc);
+ return NULL;
+ }
+
+ for (i = k = 0; i < size / period; i++) {
+ size_t tlen, len = period;
+
+ for (j = 0; j < sgperiod; j++, k++) {
+ tlen = len;
+
+ if (tlen > DMA_MAX_SIZE) {
+ unsigned mult = DIV_ROUND_UP(tlen, DMA_MAX_SIZE & ~DMA_ALIGN);
+ tlen = (tlen / mult) & ~DMA_ALIGN;
+ }
+
+ txd->sg[k].addr = addr;
+ txd->sg[k].len = tlen;
+ addr += tlen;
+ len -= tlen;
+ }
+
+ WARN_ON(len != 0);
+ }
+
+ WARN_ON(k != sglen);
+
+ txd->ddar = c->ddar;
+ txd->size = size;
+ txd->sglen = sglen;
+ txd->cyclic = 1;
+ txd->period = sgperiod;
+
+ return vchan_tx_prep(&c->vc, &txd->vd, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_config *cfg)
@@ -695,8 +698,8 @@ static int sa11x0_dma_slave_config(struct sa11x0_dma_chan *c, struct dma_slave_c
if (maxburst == 8)
ddar |= DDAR_BS;
- dev_dbg(c->chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
- c, addr, width, maxburst);
+ dev_dbg(c->vc.chan.device->dev, "vchan %p: dma_slave_config addr %x width %u burst %u\n",
+ &c->vc, addr, width, maxburst);
c->ddar = ddar | (addr & 0xf0000000) | (addr & 0x003ffffc) << 6;
@@ -718,16 +721,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
return sa11x0_dma_slave_config(c, (struct dma_slave_config *)arg);
case DMA_TERMINATE_ALL:
- dev_dbg(d->slave.dev, "vchan %p: terminate all\n", c);
+ dev_dbg(d->slave.dev, "vchan %p: terminate all\n", &c->vc);
/* Clear the tx descriptor lists */
- spin_lock_irqsave(&c->lock, flags);
- list_splice_tail_init(&c->desc_submitted, &head);
- list_splice_tail_init(&c->desc_issued, &head);
+ spin_lock_irqsave(&c->vc.lock, flags);
+ vchan_get_all_descriptors(&c->vc, &head);
p = c->phy;
if (p) {
- struct sa11x0_dma_desc *txd, *txn;
-
dev_dbg(d->slave.dev, "pchan %u: terminating\n", p->num);
/* vchan is assigned to a pchan - stop the channel */
writel(DCSR_RUN | DCSR_IE |
@@ -735,17 +735,13 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
DCSR_STRTB | DCSR_DONEB,
p->base + DMA_DCSR_C);
- list_for_each_entry_safe(txd, txn, &d->desc_complete, node)
- if (txd->tx.chan == &c->chan)
- list_move(&txd->node, &head);
-
if (p->txd_load) {
if (p->txd_load != p->txd_done)
- list_add_tail(&p->txd_load->node, &head);
+ list_add_tail(&p->txd_load->vd.node, &head);
p->txd_load = NULL;
}
if (p->txd_done) {
- list_add_tail(&p->txd_done->node, &head);
+ list_add_tail(&p->txd_done->vd.node, &head);
p->txd_done = NULL;
}
c->phy = NULL;
@@ -754,14 +750,14 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
spin_unlock(&d->lock);
tasklet_schedule(&d->task);
}
- spin_unlock_irqrestore(&c->lock, flags);
- sa11x0_dma_desc_free(d, &head);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
+ vchan_dma_desc_free_list(&c->vc, &head);
ret = 0;
break;
case DMA_PAUSE:
- dev_dbg(d->slave.dev, "vchan %p: pause\n", c);
- spin_lock_irqsave(&c->lock, flags);
+ dev_dbg(d->slave.dev, "vchan %p: pause\n", &c->vc);
+ spin_lock_irqsave(&c->vc.lock, flags);
if (c->status == DMA_IN_PROGRESS) {
c->status = DMA_PAUSED;
@@ -774,26 +770,26 @@ static int sa11x0_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
spin_unlock(&d->lock);
}
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
ret = 0;
break;
case DMA_RESUME:
- dev_dbg(d->slave.dev, "vchan %p: resume\n", c);
- spin_lock_irqsave(&c->lock, flags);
+ dev_dbg(d->slave.dev, "vchan %p: resume\n", &c->vc);
+ spin_lock_irqsave(&c->vc.lock, flags);
if (c->status == DMA_PAUSED) {
c->status = DMA_IN_PROGRESS;
p = c->phy;
if (p) {
writel(DCSR_RUN | DCSR_IE, p->base + DMA_DCSR_S);
- } else if (!list_empty(&c->desc_issued)) {
+ } else if (!list_empty(&c->vc.desc_issued)) {
spin_lock(&d->lock);
list_add_tail(&c->node, &d->chan_pending);
spin_unlock(&d->lock);
}
}
- spin_unlock_irqrestore(&c->lock, flags);
+ spin_unlock_irqrestore(&c->vc.lock, flags);
ret = 0;
break;
@@ -853,15 +849,13 @@ static int __devinit sa11x0_dma_init_dmadev(struct dma_device *dmadev,
return -ENOMEM;
}
- c->chan.device = dmadev;
c->status = DMA_IN_PROGRESS;
c->ddar = chan_desc[i].ddar;
c->name = chan_desc[i].name;
- spin_lock_init(&c->lock);
- INIT_LIST_HEAD(&c->desc_submitted);
- INIT_LIST_HEAD(&c->desc_issued);
INIT_LIST_HEAD(&c->node);
- list_add_tail(&c->chan.device_node, &dmadev->channels);
+
+ c->vc.desc_free = sa11x0_dma_free_desc;
+ vchan_init(&c->vc, dmadev);
}
return dma_async_device_register(dmadev);
@@ -890,8 +884,9 @@ static void sa11x0_dma_free_channels(struct dma_device *dmadev)
{
struct sa11x0_dma_chan *c, *cn;
- list_for_each_entry_safe(c, cn, &dmadev->channels, chan.device_node) {
- list_del(&c->chan.device_node);
+ list_for_each_entry_safe(c, cn, &dmadev->channels, vc.chan.device_node) {
+ list_del(&c->vc.chan.device_node);
+ tasklet_kill(&c->vc.task);
kfree(c);
}
}
@@ -915,7 +910,6 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
spin_lock_init(&d->lock);
INIT_LIST_HEAD(&d->chan_pending);
- INIT_LIST_HEAD(&d->desc_complete);
d->base = ioremap(res->start, resource_size(res));
if (!d->base) {
@@ -947,7 +941,9 @@ static int __devinit sa11x0_dma_probe(struct platform_device *pdev)
}
dma_cap_set(DMA_SLAVE, d->slave.cap_mask);
+ dma_cap_set(DMA_CYCLIC, d->slave.cap_mask);
d->slave.device_prep_slave_sg = sa11x0_dma_prep_slave_sg;
+ d->slave.device_prep_dma_cyclic = sa11x0_dma_prep_dma_cyclic;
ret = sa11x0_dma_init_dmadev(&d->slave, &pdev->dev);
if (ret) {
dev_warn(d->slave.dev, "failed to register slave async device: %d\n",
diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c
index 27f5c781fd73..f4cd946d259d 100644
--- a/drivers/dma/sh/shdma-base.c
+++ b/drivers/dma/sh/shdma-base.c
@@ -483,6 +483,7 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan,
new->mark = DESC_PREPARED;
new->async_tx.flags = flags;
new->direction = direction;
+ new->partial = 0;
*len -= copy_size;
if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
@@ -644,6 +645,14 @@ static int shdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
case DMA_TERMINATE_ALL:
spin_lock_irqsave(&schan->chan_lock, flags);
ops->halt_channel(schan);
+
+ if (ops->get_partial && !list_empty(&schan->ld_queue)) {
+ /* Record partial transfer */
+ struct shdma_desc *desc = list_first_entry(&schan->ld_queue,
+ struct shdma_desc, node);
+ desc->partial = ops->get_partial(schan, desc);
+ }
+
spin_unlock_irqrestore(&schan->chan_lock, flags);
shdma_chan_ld_cleanup(schan, true);
diff --git a/drivers/dma/sh/shdma.c b/drivers/dma/sh/shdma.c
index 027c9be97654..f41bcc5267fd 100644
--- a/drivers/dma/sh/shdma.c
+++ b/drivers/dma/sh/shdma.c
@@ -381,6 +381,17 @@ static bool sh_dmae_chan_irq(struct shdma_chan *schan, int irq)
return true;
}
+static size_t sh_dmae_get_partial(struct shdma_chan *schan,
+ struct shdma_desc *sdesc)
+{
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ shdma_chan);
+ struct sh_dmae_desc *sh_desc = container_of(sdesc,
+ struct sh_dmae_desc, shdma_desc);
+ return (sh_desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
+ sh_chan->xmit_shift;
+}
+
/* Called from error IRQ or NMI */
static bool sh_dmae_reset(struct sh_dmae_device *shdev)
{
@@ -632,6 +643,7 @@ static const struct shdma_ops sh_dmae_shdma_ops = {
.start_xfer = sh_dmae_start_xfer,
.embedded_desc = sh_dmae_embedded_desc,
.chan_irq = sh_dmae_chan_irq,
+ .get_partial = sh_dmae_get_partial,
};
static int __devinit sh_dmae_probe(struct platform_device *pdev)
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index d52dbc6c54ab..24acd711e032 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -1119,15 +1119,21 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic(
static int tegra_dma_alloc_chan_resources(struct dma_chan *dc)
{
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+ struct tegra_dma *tdma = tdc->tdma;
+ int ret;
dma_cookie_init(&tdc->dma_chan);
tdc->config_init = false;
- return 0;
+ ret = clk_prepare_enable(tdma->dma_clk);
+ if (ret < 0)
+ dev_err(tdc2dev(tdc), "clk_prepare_enable failed: %d\n", ret);
+ return ret;
}
static void tegra_dma_free_chan_resources(struct dma_chan *dc)
{
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+ struct tegra_dma *tdma = tdc->tdma;
struct tegra_dma_desc *dma_desc;
struct tegra_dma_sg_req *sg_req;
@@ -1163,6 +1169,7 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
list_del(&sg_req->node);
kfree(sg_req);
}
+ clk_disable_unprepare(tdma->dma_clk);
}
/* Tegra20 specific DMA controller information */
@@ -1255,6 +1262,13 @@ static int __devinit tegra_dma_probe(struct platform_device *pdev)
}
}
+ /* Enable clock before accessing registers */
+ ret = clk_prepare_enable(tdma->dma_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+ goto err_pm_disable;
+ }
+
/* Reset DMA controller */
tegra_periph_reset_assert(tdma->dma_clk);
udelay(2);
@@ -1265,6 +1279,8 @@ static int __devinit tegra_dma_probe(struct platform_device *pdev)
tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0);
tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul);
+ clk_disable_unprepare(tdma->dma_clk);
+
INIT_LIST_HEAD(&tdma->dma_dev.channels);
for (i = 0; i < cdata->nr_channels; i++) {
struct tegra_dma_channel *tdc = &tdma->channels[i];
diff --git a/drivers/dma/virt-dma.c b/drivers/dma/virt-dma.c
new file mode 100644
index 000000000000..6f80432a3f0a
--- /dev/null
+++ b/drivers/dma/virt-dma.c
@@ -0,0 +1,123 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * 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/device.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+
+static struct virt_dma_desc *to_virt_desc(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct virt_dma_desc, tx);
+}
+
+dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct virt_dma_chan *vc = to_virt_chan(tx->chan);
+ struct virt_dma_desc *vd = to_virt_desc(tx);
+ unsigned long flags;
+ dma_cookie_t cookie;
+
+ spin_lock_irqsave(&vc->lock, flags);
+ cookie = dma_cookie_assign(tx);
+
+ list_add_tail(&vd->node, &vc->desc_submitted);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ dev_dbg(vc->chan.device->dev, "vchan %p: txd %p[%x]: submitted\n",
+ vc, vd, cookie);
+
+ return cookie;
+}
+EXPORT_SYMBOL_GPL(vchan_tx_submit);
+
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *vc,
+ dma_cookie_t cookie)
+{
+ struct virt_dma_desc *vd;
+
+ list_for_each_entry(vd, &vc->desc_issued, node)
+ if (vd->tx.cookie == cookie)
+ return vd;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(vchan_find_desc);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+static void vchan_complete(unsigned long arg)
+{
+ struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+ struct virt_dma_desc *vd;
+ dma_async_tx_callback cb = NULL;
+ void *cb_data = NULL;
+ LIST_HEAD(head);
+
+ spin_lock_irq(&vc->lock);
+ list_splice_tail_init(&vc->desc_completed, &head);
+ vd = vc->cyclic;
+ if (vd) {
+ vc->cyclic = NULL;
+ cb = vd->tx.callback;
+ cb_data = vd->tx.callback_param;
+ }
+ spin_unlock_irq(&vc->lock);
+
+ if (cb)
+ cb(cb_data);
+
+ while (!list_empty(&head)) {
+ vd = list_first_entry(&head, struct virt_dma_desc, node);
+ cb = vd->tx.callback;
+ cb_data = vd->tx.callback_param;
+
+ list_del(&vd->node);
+
+ vc->desc_free(vd);
+
+ if (cb)
+ cb(cb_data);
+ }
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head)
+{
+ while (!list_empty(head)) {
+ struct virt_dma_desc *vd = list_first_entry(head,
+ struct virt_dma_desc, node);
+ list_del(&vd->node);
+ dev_dbg(vc->chan.device->dev, "txd %p: freeing\n", vd);
+ vc->desc_free(vd);
+ }
+}
+EXPORT_SYMBOL_GPL(vchan_dma_desc_free_list);
+
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev)
+{
+ dma_cookie_init(&vc->chan);
+
+ spin_lock_init(&vc->lock);
+ INIT_LIST_HEAD(&vc->desc_submitted);
+ INIT_LIST_HEAD(&vc->desc_issued);
+ INIT_LIST_HEAD(&vc->desc_completed);
+
+ tasklet_init(&vc->task, vchan_complete, (unsigned long)vc);
+
+ vc->chan.device = dmadev;
+ list_add_tail(&vc->chan.device_node, &dmadev->channels);
+}
+EXPORT_SYMBOL_GPL(vchan_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/virt-dma.h b/drivers/dma/virt-dma.h
new file mode 100644
index 000000000000..85c19d63f9fb
--- /dev/null
+++ b/drivers/dma/virt-dma.h
@@ -0,0 +1,152 @@
+/*
+ * Virtual DMA channel support for DMAengine
+ *
+ * Copyright (C) 2012 Russell King
+ *
+ * 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.
+ */
+#ifndef VIRT_DMA_H
+#define VIRT_DMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+
+#include "dmaengine.h"
+
+struct virt_dma_desc {
+ struct dma_async_tx_descriptor tx;
+ /* protected by vc.lock */
+ struct list_head node;
+};
+
+struct virt_dma_chan {
+ struct dma_chan chan;
+ struct tasklet_struct task;
+ void (*desc_free)(struct virt_dma_desc *);
+
+ spinlock_t lock;
+
+ /* protected by vc.lock */
+ struct list_head desc_submitted;
+ struct list_head desc_issued;
+ struct list_head desc_completed;
+
+ struct virt_dma_desc *cyclic;
+};
+
+static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct virt_dma_chan, chan);
+}
+
+void vchan_dma_desc_free_list(struct virt_dma_chan *vc, struct list_head *head);
+void vchan_init(struct virt_dma_chan *vc, struct dma_device *dmadev);
+struct virt_dma_desc *vchan_find_desc(struct virt_dma_chan *, dma_cookie_t);
+
+/**
+ * vchan_tx_prep - prepare a descriptor
+ * vc: virtual channel allocating this descriptor
+ * vd: virtual descriptor to prepare
+ * tx_flags: flags argument passed in to prepare function
+ */
+static inline struct dma_async_tx_descriptor *vchan_tx_prep(struct virt_dma_chan *vc,
+ struct virt_dma_desc *vd, unsigned long tx_flags)
+{
+ extern dma_cookie_t vchan_tx_submit(struct dma_async_tx_descriptor *);
+
+ dma_async_tx_descriptor_init(&vd->tx, &vc->chan);
+ vd->tx.flags = tx_flags;
+ vd->tx.tx_submit = vchan_tx_submit;
+
+ return &vd->tx;
+}
+
+/**
+ * vchan_issue_pending - move submitted descriptors to issued list
+ * vc: virtual channel to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline bool vchan_issue_pending(struct virt_dma_chan *vc)
+{
+ list_splice_tail_init(&vc->desc_submitted, &vc->desc_issued);
+ return !list_empty(&vc->desc_issued);
+}
+
+/**
+ * vchan_cookie_complete - report completion of a descriptor
+ * vd: virtual descriptor to update
+ *
+ * vc.lock must be held by caller
+ */
+static inline void vchan_cookie_complete(struct virt_dma_desc *vd)
+{
+ struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+ dma_cookie_complete(&vd->tx);
+ dev_vdbg(vc->chan.device->dev, "txd %p[%x]: marked complete\n",
+ vd, vd->tx.cookie);
+ list_add_tail(&vd->node, &vc->desc_completed);
+
+ tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_cyclic_callback - report the completion of a period
+ * vd: virtual descriptor
+ */
+static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
+{
+ struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+ vc->cyclic = vd;
+ tasklet_schedule(&vc->task);
+}
+
+/**
+ * vchan_next_desc - peek at the next descriptor to be processed
+ * vc: virtual channel to obtain descriptor from
+ *
+ * vc.lock must be held by caller
+ */
+static inline struct virt_dma_desc *vchan_next_desc(struct virt_dma_chan *vc)
+{
+ if (list_empty(&vc->desc_issued))
+ return NULL;
+
+ return list_first_entry(&vc->desc_issued, struct virt_dma_desc, node);
+}
+
+/**
+ * vchan_get_all_descriptors - obtain all submitted and issued descriptors
+ * vc: virtual channel to get descriptors from
+ * head: list of descriptors found
+ *
+ * vc.lock must be held by caller
+ *
+ * Removes all submitted and issued descriptors from internal lists, and
+ * provides a list of all descriptors found
+ */
+static inline void vchan_get_all_descriptors(struct virt_dma_chan *vc,
+ struct list_head *head)
+{
+ list_splice_tail_init(&vc->desc_submitted, head);
+ list_splice_tail_init(&vc->desc_issued, head);
+ list_splice_tail_init(&vc->desc_completed, head);
+}
+
+static inline void vchan_free_chan_resources(struct virt_dma_chan *vc)
+{
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&vc->lock, flags);
+ vchan_get_all_descriptors(vc, &head);
+ spin_unlock_irqrestore(&vc->lock, flags);
+
+ vchan_dma_desc_free_list(vc, &head);
+}
+
+#endif
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 616d90bcb3a4..d5dc9da7f99f 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -199,6 +199,36 @@ void *edac_align_ptr(void **p, unsigned size, int n_elems)
return (void *)(((unsigned long)ptr) + align - r);
}
+static void _edac_mc_free(struct mem_ctl_info *mci)
+{
+ int i, chn, row;
+ struct csrow_info *csr;
+ const unsigned int tot_dimms = mci->tot_dimms;
+ const unsigned int tot_channels = mci->num_cschannel;
+ const unsigned int tot_csrows = mci->nr_csrows;
+
+ if (mci->dimms) {
+ for (i = 0; i < tot_dimms; i++)
+ kfree(mci->dimms[i]);
+ kfree(mci->dimms);
+ }
+ if (mci->csrows) {
+ for (row = 0; row < tot_csrows; row++) {
+ csr = mci->csrows[row];
+ if (csr) {
+ if (csr->channels) {
+ for (chn = 0; chn < tot_channels; chn++)
+ kfree(csr->channels[chn]);
+ kfree(csr->channels);
+ }
+ kfree(csr);
+ }
+ }
+ kfree(mci->csrows);
+ }
+ kfree(mci);
+}
+
/**
* edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
* @mc_num: Memory controller number
@@ -413,24 +443,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
return mci;
error:
- if (mci->dimms) {
- for (i = 0; i < tot_dimms; i++)
- kfree(mci->dimms[i]);
- kfree(mci->dimms);
- }
- if (mci->csrows) {
- for (chn = 0; chn < tot_channels; chn++) {
- csr = mci->csrows[chn];
- if (csr) {
- for (chn = 0; chn < tot_channels; chn++)
- kfree(csr->channels[chn]);
- kfree(csr);
- }
- kfree(mci->csrows[i]);
- }
- kfree(mci->csrows);
- }
- kfree(mci);
+ _edac_mc_free(mci);
return NULL;
}
@@ -445,6 +458,14 @@ void edac_mc_free(struct mem_ctl_info *mci)
{
edac_dbg(1, "\n");
+ /* If we're not yet registered with sysfs free only what was allocated
+ * in edac_mc_alloc().
+ */
+ if (!device_is_registered(&mci->dev)) {
+ _edac_mc_free(mci);
+ return;
+ }
+
/* the mci instance is freed here, when the sysfs object is dropped */
edac_unregister_sysfs(mci);
}
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 47180a08edad..b6653a6fc5d5 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -391,7 +391,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
for (j = 0; j < nr_channels; j++) {
struct dimm_info *dimm = csrow->channels[j]->dimm;
- dimm->nr_pages = nr_pages / nr_channels;
+ dimm->nr_pages = nr_pages;
dimm->grain = nr_pages << PAGE_SHIFT;
dimm->mtype = MEM_DDR2;
dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 39c63757c2a1..6a49dd00b81b 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1012,6 +1012,10 @@ static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
/* add the number of COLUMN bits */
addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
+ /* Dual-rank memories have twice the size */
+ if (dinfo->dual_rank)
+ addrBits++;
+
addrBits += 6; /* add 64 bits per DIMM */
addrBits -= 20; /* divide by 2^^20 */
addrBits -= 3; /* 8 bits per bytes */
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index f3b1f9fafa4b..5715b7c2c517 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -513,7 +513,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
{
struct sbridge_pvt *pvt = mci->pvt_info;
struct dimm_info *dimm;
- int i, j, banks, ranks, rows, cols, size, npages;
+ unsigned i, j, banks, ranks, rows, cols, npages;
+ u64 size;
u32 reg;
enum edac_type mode;
enum mem_type mtype;
@@ -585,10 +586,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
cols = numcol(mtr);
/* DDR3 has 8 I/O banks */
- size = (rows * cols * banks * ranks) >> (20 - 3);
+ size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %Ld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
pvt->sbridge_dev->mc, i, j,
size, npages,
banks, ranks, rows, cols);
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 920a609b2c35..38f9e52f358b 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -669,13 +669,18 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
}
info->dev = &pdev->dev;
info->max77693 = max77693;
- info->max77693->regmap_muic = regmap_init_i2c(info->max77693->muic,
- &max77693_muic_regmap_config);
- if (IS_ERR(info->max77693->regmap_muic)) {
- ret = PTR_ERR(info->max77693->regmap_muic);
- dev_err(max77693->dev,
- "failed to allocate register map: %d\n", ret);
- goto err_regmap;
+ if (info->max77693->regmap_muic)
+ dev_dbg(&pdev->dev, "allocate register map\n");
+ else {
+ info->max77693->regmap_muic = devm_regmap_init_i2c(
+ info->max77693->muic,
+ &max77693_muic_regmap_config);
+ if (IS_ERR(info->max77693->regmap_muic)) {
+ ret = PTR_ERR(info->max77693->regmap_muic);
+ dev_err(max77693->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
}
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c
index fe3db45fa83c..3cc152e690b0 100644
--- a/drivers/extcon/extcon_gpio.c
+++ b/drivers/extcon/extcon_gpio.c
@@ -107,7 +107,8 @@ static int __devinit gpio_extcon_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name);
+ ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
+ pdev->name);
if (ret < 0)
goto err;
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index 153980be4ee6..b298158cb922 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -6,6 +6,7 @@
#include <linux/dmi.h>
#include <linux/efi.h>
#include <linux/bootmem.h>
+#include <linux/random.h>
#include <asm/dmi.h>
/*
@@ -111,6 +112,8 @@ static int __init dmi_walk_early(void (*decode)(const struct dmi_header *,
dmi_table(buf, dmi_len, dmi_num, decode, NULL);
+ add_device_randomness(buf, dmi_len);
+
dmi_iounmap(buf, dmi_len);
return 0;
}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index b16c8a72a2e2..ba7926f5c099 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -294,7 +294,7 @@ config GPIO_MAX732X_IRQ
config GPIO_MC9S08DZ60
bool "MX35 3DS BOARD MC9S08DZ60 GPIO functions"
- depends on I2C && MACH_MX35_3DS
+ depends on I2C=y && MACH_MX35_3DS
help
Select this to enable the MC9S08DZ60 GPIO driver
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
index e4cc7eb69bb2..92d555d5c25a 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -50,7 +50,7 @@
#include <linux/slab.h>
/* Steal the hardware definitions from the bttv driver. */
-#include "../media/video/bt8xx/bt848.h"
+#include "../media/pci/bt8xx/bt848.h"
#define BT8XXGPIO_NR_GPIOS 24 /* We have 24 GPIO pins */
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index 150d9768811d..ec48ed512628 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -247,9 +247,9 @@ static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
p->irq_base = irq_alloc_descs(pdata->irq_base, 0,
pdata->number_of_pins, numa_node_id());
- if (IS_ERR_VALUE(p->irq_base)) {
+ if (p->irq_base < 0) {
dev_err(&pdev->dev, "cannot get irq_desc\n");
- return -ENXIO;
+ return p->irq_base;
}
pr_debug("gio: hw base = %d, nr = %d, sw base = %d\n",
pdata->gpio_base, pdata->number_of_pins, p->irq_base);
@@ -266,7 +266,7 @@ static int __devinit em_gio_irq_domain_init(struct em_gio_priv *p)
return 0;
}
-static void __devexit em_gio_irq_domain_cleanup(struct em_gio_priv *p)
+static void em_gio_irq_domain_cleanup(struct em_gio_priv *p)
{
struct gpio_em_config *pdata = p->pdev->dev.platform_data;
diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c
index a1c8754f52cf..202a99207b7d 100644
--- a/drivers/gpio/gpio-langwell.c
+++ b/drivers/gpio/gpio-langwell.c
@@ -339,7 +339,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
resource_size_t start, len;
struct lnw_gpio *lnw;
u32 gpio_base;
- int retval = 0;
+ int retval;
int ngpio = id->driver_data;
retval = pci_enable_device(pdev);
@@ -357,6 +357,7 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
base = ioremap_nocache(start, len);
if (!base) {
dev_err(&pdev->dev, "error mapping bar1\n");
+ retval = -EFAULT;
goto err3;
}
gpio_base = *((u32 *)base + 1);
@@ -381,8 +382,10 @@ static int __devinit lnw_gpio_probe(struct pci_dev *pdev,
lnw->domain = irq_domain_add_linear(pdev->dev.of_node, ngpio,
&lnw_gpio_irq_ops, lnw);
- if (!lnw->domain)
+ if (!lnw->domain) {
+ retval = -ENOMEM;
goto err3;
+ }
lnw->reg_base = base;
lnw->chip.label = dev_name(&pdev->dev);
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 8a420f13905e..ed94b4ea72e9 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -308,6 +308,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p012(group, pin, value);
__set_gpio_dir_p012(group, pin, 0);
return 0;
@@ -318,6 +319,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p3(group, pin, value);
__set_gpio_dir_p3(group, pin, 0);
return 0;
@@ -326,6 +328,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
int value)
{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
return 0;
}
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
index 71a838f44501..b38986285868 100644
--- a/drivers/gpio/gpio-msic.c
+++ b/drivers/gpio/gpio-msic.c
@@ -99,7 +99,7 @@ static int msic_gpio_to_oreg(unsigned offset)
if (offset < 20)
return INTEL_MSIC_GPIO0HV0CTLO - offset + 16;
- return INTEL_MSIC_GPIO1HV0CTLO + offset + 20;
+ return INTEL_MSIC_GPIO1HV0CTLO - offset + 20;
}
static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 4db460b6ecf7..80f44bb64a87 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -465,9 +465,8 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev)
goto out_iounmap;
port->bgc.gc.to_irq = mxc_gpio_to_irq;
- port->bgc.gc.base = pdev->id * 32;
- port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir);
- port->bgc.data = port->bgc.read_reg(port->bgc.reg_set);
+ port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
+ pdev->id * 32;
err = gpiochip_add(&port->bgc.gc);
if (err)
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 58a6a63a6ece..9cac88a65f78 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -62,6 +62,7 @@ int pxa_last_gpio;
#ifdef CONFIG_OF
static struct irq_domain *domain;
+static struct device_node *pxa_gpio_of_node;
#endif
struct pxa_gpio_chip {
@@ -277,6 +278,24 @@ static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
(value ? GPSR_OFFSET : GPCR_OFFSET));
}
+#ifdef CONFIG_OF_GPIO
+static int pxa_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ if (gpiospec->args[0] > pxa_last_gpio)
+ return -EINVAL;
+
+ if (gc != &pxa_gpio_chips[gpiospec->args[0] / 32].chip)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] % 32;
+}
+#endif
+
static int __devinit pxa_init_gpio_chip(int gpio_end,
int (*set_wake)(unsigned int, unsigned int))
{
@@ -304,6 +323,11 @@ static int __devinit pxa_init_gpio_chip(int gpio_end,
c->get = pxa_gpio_get;
c->set = pxa_gpio_set;
c->to_irq = pxa_gpio_to_irq;
+#ifdef CONFIG_OF_GPIO
+ c->of_node = pxa_gpio_of_node;
+ c->of_xlate = pxa_gpio_of_xlate;
+ c->of_gpio_n_cells = 2;
+#endif
/* number of GPIOs on last bank may be less than 32 */
c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32;
@@ -488,6 +512,7 @@ static int pxa_gpio_nums(void)
return count;
}
+#ifdef CONFIG_OF
static struct of_device_id pxa_gpio_dt_ids[] = {
{ .compatible = "mrvl,pxa-gpio" },
{ .compatible = "mrvl,mmp-gpio", .data = (void *)MMP_GPIO },
@@ -505,9 +530,9 @@ static int pxa_irq_domain_map(struct irq_domain *d, unsigned int irq,
const struct irq_domain_ops pxa_irq_domain_ops = {
.map = pxa_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
};
-#ifdef CONFIG_OF
static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev)
{
int ret, nr_banks, nr_gpios, irq_base;
@@ -545,6 +570,7 @@ static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev)
}
domain = irq_domain_add_legacy(np, nr_gpios, irq_base, 0,
&pxa_irq_domain_ops, NULL);
+ pxa_gpio_of_node = np;
return 0;
err:
iounmap(gpio_reg_base);
@@ -653,7 +679,7 @@ static struct platform_driver pxa_gpio_driver = {
.probe = pxa_gpio_probe,
.driver = {
.name = "pxa-gpio",
- .of_match_table = pxa_gpio_dt_ids,
+ .of_match_table = of_match_ptr(pxa_gpio_dt_ids),
},
};
diff --git a/drivers/gpio/gpio-rdc321x.c b/drivers/gpio/gpio-rdc321x.c
index e97016af6443..b62d443e9a59 100644
--- a/drivers/gpio/gpio-rdc321x.c
+++ b/drivers/gpio/gpio-rdc321x.c
@@ -170,6 +170,7 @@ static int __devinit rdc321x_gpio_probe(struct platform_device *pdev)
rdc321x_gpio_dev->reg2_data_base = r->start + 0x4;
rdc321x_gpio_dev->chip.label = "rdc321x-gpio";
+ rdc321x_gpio_dev->chip.owner = THIS_MODULE;
rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input;
rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config;
rdc321x_gpio_dev->chip.get = rdc_gpio_get_value;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 92f7b2bb79d4..ba126cc04073 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -2454,12 +2454,6 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = {
},
}, {
.chip = {
- .base = EXYNOS5_GPC4(0),
- .ngpio = EXYNOS5_GPIO_C4_NR,
- .label = "GPC4",
- },
- }, {
- .chip = {
.base = EXYNOS5_GPD0(0),
.ngpio = EXYNOS5_GPIO_D0_NR,
.label = "GPD0",
@@ -2513,6 +2507,12 @@ static struct samsung_gpio_chip exynos5_gpios_1[] = {
.label = "GPY6",
},
}, {
+ .chip = {
+ .base = EXYNOS5_GPC4(0),
+ .ngpio = EXYNOS5_GPIO_C4_NR,
+ .label = "GPC4",
+ },
+ }, {
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(0),
.chip = {
@@ -2836,7 +2836,7 @@ static __init void exynos5_gpiolib_init(void)
}
/* need to set base address for gpc4 */
- exynos5_gpios_1[11].base = gpio_base1 + 0x2E0;
+ exynos5_gpios_1[20].base = gpio_base1 + 0x2E0;
/* need to set base address for gpx */
chip = &exynos5_gpios_1[21];
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index 424dce8e3f30..8707d4572a06 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -241,7 +241,8 @@ static int __devinit sch_gpio_probe(struct platform_device *pdev)
break;
default:
- return -ENODEV;
+ err = -ENODEV;
+ goto err_sch_gpio_core;
}
sch_gpio_core.dev = &pdev->dev;
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index a18c4aa68b1e..f1a45997aea8 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -82,7 +82,7 @@ int of_get_named_gpio_flags(struct device_node *np, const char *propname,
gpiochip_find(&gg_data, of_gpiochip_find_and_xlate);
of_node_put(gg_data.gpiospec.np);
- pr_debug("%s exited with status %d\n", __func__, ret);
+ pr_debug("%s exited with status %d\n", __func__, gg_data.out_gpio);
return gg_data.out_gpio;
}
EXPORT_SYMBOL(of_get_named_gpio_flags);
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 23120c00a881..90e28081712d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -22,6 +22,7 @@ menuconfig DRM
config DRM_USB
tristate
depends on DRM
+ depends on USB_ARCH_HAS_HCD
select USB
config DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index d0c4574ef49c..36164806b9d4 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -193,6 +193,9 @@ static const struct file_operations ast_fops = {
.mmap = ast_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.read = drm_read,
};
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 7282c081fb53..a712cafcfa1d 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -841,7 +841,7 @@ int ast_cursor_init(struct drm_device *dev)
ast->cursor_cache = obj;
ast->cursor_cache_gpu_addr = gpu_addr;
- DRM_ERROR("pinned cursor cache at %llx\n", ast->cursor_cache_gpu_addr);
+ DRM_DEBUG_KMS("pinned cursor cache at %llx\n", ast->cursor_cache_gpu_addr);
return 0;
fail:
return ret;
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 7053140c6596..b83a2d7ddd1a 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -74,6 +74,9 @@ static const struct file_operations cirrus_driver_fops = {
.unlocked_ioctl = drm_ioctl,
.mmap = cirrus_mmap,
.poll = drm_poll,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.fasync = drm_fasync,
};
static struct drm_driver driver = {
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 08a7aa722d6b..6fbfc244748f 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1981,7 +1981,7 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- if (!req->flags)
+ if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
return -EINVAL;
mutex_lock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a8743c399e83..b7ee230572b7 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -87,6 +87,9 @@ static struct edid_quirk {
int product_id;
u32 quirks;
} edid_quirk_list[] = {
+ /* ASUS VW222S */
+ { "ACI", 0x22a2, EDID_QUIRK_FORCE_REDUCED_BLANKING },
+
/* Acer AL1706 */
{ "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 },
/* Acer F51 */
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
index 66d4a28ad5a2..0303935d10e2 100644
--- a/drivers/gpu/drm/drm_edid_load.c
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -119,7 +119,7 @@ static int edid_load(struct drm_connector *connector, char *name,
{
const struct firmware *fw;
struct platform_device *pdev;
- u8 *fwdata = NULL, *edid;
+ u8 *fwdata = NULL, *edid, *new_edid;
int fwsize, expected;
int builtin = 0, err = 0;
int i, valid_extensions = 0;
@@ -195,12 +195,14 @@ static int edid_load(struct drm_connector *connector, char *name,
"\"%s\" for connector \"%s\"\n", valid_extensions,
edid[0x7e], name, connector_name);
edid[0x7e] = valid_extensions;
- edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
+ new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
GFP_KERNEL);
- if (edid == NULL) {
+ if (new_edid == NULL) {
err = -ENOMEM;
+ kfree(edid);
goto relfw_out;
}
+ edid = new_edid;
}
connector->display_info.raw_edid = edid;
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index b7adb4a967fd..28637c181b15 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -706,9 +706,6 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
-
- p->crtc_hadjusted = false;
- p->crtc_vadjusted = false;
}
EXPORT_SYMBOL(drm_mode_set_crtcinfo);
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index 371c695322d9..da457b18eaaf 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -89,7 +89,7 @@ static const struct file_operations drm_proc_fops = {
* Create a given set of proc files represented by an array of
* gdm_proc_lists in the given root directory.
*/
-int drm_proc_create_files(struct drm_info_list *files, int count,
+static int drm_proc_create_files(struct drm_info_list *files, int count,
struct proc_dir_entry *root, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
@@ -172,7 +172,7 @@ int drm_proc_init(struct drm_minor *minor, int minor_id,
return 0;
}
-int drm_proc_remove_files(struct drm_info_list *files, int count,
+static int drm_proc_remove_files(struct drm_info_list *files, int count,
struct drm_minor *minor)
{
struct list_head *pos, *q;
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 7f5096763b7d..59a26e577b57 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -36,6 +36,6 @@ config DRM_EXYNOS_VIDI
config DRM_EXYNOS_G2D
bool "Exynos DRM G2D"
- depends on DRM_EXYNOS
+ depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D
help
Choose this option if you want to use Exynos G2D for DRM.
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
index 613bf8a5d9b2..ae13febe0eaa 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
@@ -163,6 +163,12 @@ static void exynos_gem_dmabuf_kunmap(struct dma_buf *dma_buf,
/* TODO */
}
+static int exynos_gem_dmabuf_mmap(struct dma_buf *dma_buf,
+ struct vm_area_struct *vma)
+{
+ return -ENOTTY;
+}
+
static struct dma_buf_ops exynos_dmabuf_ops = {
.map_dma_buf = exynos_gem_map_dma_buf,
.unmap_dma_buf = exynos_gem_unmap_dma_buf,
@@ -170,6 +176,7 @@ static struct dma_buf_ops exynos_dmabuf_ops = {
.kmap_atomic = exynos_gem_dmabuf_kmap_atomic,
.kunmap = exynos_gem_dmabuf_kunmap,
.kunmap_atomic = exynos_gem_dmabuf_kunmap_atomic,
+ .mmap = exynos_gem_dmabuf_mmap,
.release = exynos_dmabuf_release,
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index ebacec6f1e48..d07071937453 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -160,7 +160,6 @@ static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
if (!file_priv)
return -ENOMEM;
- drm_prime_init_file_private(&file->prime);
file->driver_priv = file_priv;
return exynos_drm_subdrv_open(dev, file);
@@ -184,7 +183,6 @@ static void exynos_drm_preclose(struct drm_device *dev,
e->base.destroy(&e->base);
}
}
- drm_prime_destroy_file_private(&file->prime);
spin_unlock_irqrestore(&dev->event_lock, flags);
exynos_drm_subdrv_close(dev, file);
@@ -241,6 +239,9 @@ static const struct file_operations exynos_drm_driver_fops = {
.poll = drm_poll,
.read = drm_read,
.unlocked_ioctl = drm_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.release = drm_release,
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index a68d2b313f03..b19cd93e7047 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -831,11 +831,6 @@ static int __devinit fimd_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "failed to find registers\n");
- ret = -ENOENT;
- goto err_clk;
- }
ctx->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!ctx->regs) {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index d2d88f22a037..1065e90d0919 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -129,7 +129,6 @@ struct g2d_runqueue_node {
struct g2d_data {
struct device *dev;
struct clk *gate_clk;
- struct resource *regs_res;
void __iomem *regs;
int irq;
struct workqueue_struct *g2d_workq;
@@ -751,7 +750,7 @@ static int __devinit g2d_probe(struct platform_device *pdev)
struct exynos_drm_subdrv *subdrv;
int ret;
- g2d = kzalloc(sizeof(*g2d), GFP_KERNEL);
+ g2d = devm_kzalloc(&pdev->dev, sizeof(*g2d), GFP_KERNEL);
if (!g2d) {
dev_err(dev, "failed to allocate driver data\n");
return -ENOMEM;
@@ -759,10 +758,8 @@ static int __devinit g2d_probe(struct platform_device *pdev)
g2d->runqueue_slab = kmem_cache_create("g2d_runqueue_slab",
sizeof(struct g2d_runqueue_node), 0, 0, NULL);
- if (!g2d->runqueue_slab) {
- ret = -ENOMEM;
- goto err_free_mem;
- }
+ if (!g2d->runqueue_slab)
+ return -ENOMEM;
g2d->dev = dev;
@@ -794,38 +791,26 @@ static int __devinit g2d_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "failed to get I/O memory\n");
- ret = -ENOENT;
- goto err_put_clk;
- }
- g2d->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!g2d->regs_res) {
- dev_err(dev, "failed to request I/O memory\n");
- ret = -ENOENT;
- goto err_put_clk;
- }
-
- g2d->regs = ioremap(res->start, resource_size(res));
+ g2d->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!g2d->regs) {
dev_err(dev, "failed to remap I/O memory\n");
ret = -ENXIO;
- goto err_release_res;
+ goto err_put_clk;
}
g2d->irq = platform_get_irq(pdev, 0);
if (g2d->irq < 0) {
dev_err(dev, "failed to get irq\n");
ret = g2d->irq;
- goto err_unmap_base;
+ goto err_put_clk;
}
- ret = request_irq(g2d->irq, g2d_irq_handler, 0, "drm_g2d", g2d);
+ ret = devm_request_irq(&pdev->dev, g2d->irq, g2d_irq_handler, 0,
+ "drm_g2d", g2d);
if (ret < 0) {
dev_err(dev, "irq request failed\n");
- goto err_unmap_base;
+ goto err_put_clk;
}
platform_set_drvdata(pdev, g2d);
@@ -838,7 +823,7 @@ static int __devinit g2d_probe(struct platform_device *pdev)
ret = exynos_drm_subdrv_register(subdrv);
if (ret < 0) {
dev_err(dev, "failed to register drm g2d device\n");
- goto err_free_irq;
+ goto err_put_clk;
}
dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n",
@@ -846,13 +831,6 @@ static int __devinit g2d_probe(struct platform_device *pdev)
return 0;
-err_free_irq:
- free_irq(g2d->irq, g2d);
-err_unmap_base:
- iounmap(g2d->regs);
-err_release_res:
- release_resource(g2d->regs_res);
- kfree(g2d->regs_res);
err_put_clk:
pm_runtime_disable(dev);
clk_put(g2d->gate_clk);
@@ -862,8 +840,6 @@ err_destroy_workqueue:
destroy_workqueue(g2d->g2d_workq);
err_destroy_slab:
kmem_cache_destroy(g2d->runqueue_slab);
-err_free_mem:
- kfree(g2d);
return ret;
}
@@ -873,24 +849,18 @@ static int __devexit g2d_remove(struct platform_device *pdev)
cancel_work_sync(&g2d->runqueue_work);
exynos_drm_subdrv_unregister(&g2d->subdrv);
- free_irq(g2d->irq, g2d);
while (g2d->runqueue_node) {
g2d_free_runqueue_node(g2d, g2d->runqueue_node);
g2d->runqueue_node = g2d_get_runqueue_node(g2d);
}
- iounmap(g2d->regs);
- release_resource(g2d->regs_res);
- kfree(g2d->regs_res);
-
pm_runtime_disable(&pdev->dev);
clk_put(g2d->gate_clk);
g2d_fini_cmdlist(g2d);
destroy_workqueue(g2d->g2d_workq);
kmem_cache_destroy(g2d->runqueue_slab);
- kfree(g2d);
return 0;
}
@@ -924,7 +894,7 @@ static int g2d_resume(struct device *dev)
}
#endif
-SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume);
+static SIMPLE_DEV_PM_OPS(g2d_pm_ops, g2d_suspend, g2d_resume);
struct platform_driver g2d_driver = {
.probe = g2d_probe,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index f9efde40c097..a38051c95ec4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -122,7 +122,7 @@ fail:
__free_page(pages[i]);
drm_free_large(pages);
- return ERR_PTR(PTR_ERR(p));
+ return ERR_CAST(p);
}
static void exynos_gem_put_pages(struct drm_gem_object *obj,
@@ -662,7 +662,7 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
*/
args->pitch = args->width * ((args->bpp + 7) / 8);
- args->size = PAGE_ALIGN(args->pitch * args->height);
+ args->size = args->pitch * args->height;
exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
index 8ffcdf8b9e22..3fdf0b65f47e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -345,7 +345,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
DRM_DEBUG_KMS("%s\n", __FILE__);
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
DRM_LOG_KMS("failed to alloc common hdmi context.\n");
return -ENOMEM;
@@ -371,7 +371,6 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_subdrv_unregister(&ctx->subdrv);
- kfree(ctx);
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index b89829e5043a..e1f94b746bd7 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -29,7 +29,6 @@ static const uint32_t formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_NV12,
- DRM_FORMAT_NV12M,
DRM_FORMAT_NV12MT,
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index bb1550c4dd57..537027a74fd5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -633,7 +633,7 @@ static int __devinit vidi_probe(struct platform_device *pdev)
DRM_DEBUG_KMS("%s\n", __FILE__);
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
@@ -673,8 +673,6 @@ static int __devexit vidi_remove(struct platform_device *pdev)
ctx->raw_edid = NULL;
}
- kfree(ctx);
-
return 0;
}
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 409e2ec1207c..a6aea6f3ea1a 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -2172,7 +2172,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
DRM_DEBUG_KMS("HDMI resource init\n");
- memset(res, 0, sizeof *res);
+ memset(res, 0, sizeof(*res));
/* get clocks, power */
res->hdmi = clk_get(dev, "hdmi");
@@ -2204,7 +2204,7 @@ static int __devinit hdmi_resources_init(struct hdmi_context *hdata)
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
- sizeof res->regul_bulk[0], GFP_KERNEL);
+ sizeof(res->regul_bulk[0]), GFP_KERNEL);
if (!res->regul_bulk) {
DRM_ERROR("failed to get memory for regulators\n");
goto fail;
@@ -2243,7 +2243,7 @@ static int hdmi_resources_cleanup(struct hdmi_context *hdata)
clk_put(res->sclk_hdmi);
if (!IS_ERR_OR_NULL(res->hdmi))
clk_put(res->hdmi);
- memset(res, 0, sizeof *res);
+ memset(res, 0, sizeof(*res));
return 0;
}
@@ -2312,11 +2312,6 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- DRM_ERROR("failed to find registers\n");
- ret = -ENOENT;
- goto err_resource;
- }
hdata->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hdata->regs) {
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 30fcc12f81dd..25b97d5e5fcb 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -236,11 +236,11 @@ static inline void vp_filter_set(struct mixer_resources *res,
static void vp_default_filter(struct mixer_resources *res)
{
vp_filter_set(res, VP_POLY8_Y0_LL,
- filter_y_horiz_tap8, sizeof filter_y_horiz_tap8);
+ filter_y_horiz_tap8, sizeof(filter_y_horiz_tap8));
vp_filter_set(res, VP_POLY4_Y0_LL,
- filter_y_vert_tap4, sizeof filter_y_vert_tap4);
+ filter_y_vert_tap4, sizeof(filter_y_vert_tap4));
vp_filter_set(res, VP_POLY4_C0_LL,
- filter_cr_horiz_tap4, sizeof filter_cr_horiz_tap4);
+ filter_cr_horiz_tap4, sizeof(filter_cr_horiz_tap4));
}
static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c
index 0f9b7db80f6b..cf49ba5a54bf 100644
--- a/drivers/gpu/drm/gma500/oaktrail_device.c
+++ b/drivers/gpu/drm/gma500/oaktrail_device.c
@@ -476,6 +476,7 @@ static const struct psb_offset oaktrail_regmap[2] = {
.pos = DSPAPOS,
.surf = DSPASURF,
.addr = MRST_DSPABASE,
+ .base = MRST_DSPABASE,
.status = PIPEASTAT,
.linoff = DSPALINOFF,
.tileoff = DSPATILEOFF,
@@ -499,6 +500,7 @@ static const struct psb_offset oaktrail_regmap[2] = {
.pos = DSPBPOS,
.surf = DSPBSURF,
.addr = DSPBBASE,
+ .base = DSPBBASE,
.status = PIPEBSTAT,
.linoff = DSPBLINOFF,
.tileoff = DSPBTILEOFF,
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index 30dc22a7156c..8033526bb53b 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -1362,6 +1362,9 @@ void psb_intel_crtc_init(struct drm_device *dev, int pipe,
(struct drm_connector **) (psb_intel_crtc + 1);
psb_intel_crtc->mode_set.num_connectors = 0;
psb_intel_cursor_init(dev, psb_intel_crtc);
+
+ /* Set to true so that the pipe is forced off on initial config. */
+ psb_intel_crtc->active = true;
}
int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index 57d892eaaa6e..463ec6871fe9 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -115,6 +115,9 @@ static const struct file_operations i810_buffer_fops = {
.unlocked_ioctl = drm_ioctl,
.mmap = i810_mmap_buffers,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
index f9924ad04d09..48cfcca2b350 100644
--- a/drivers/gpu/drm/i810/i810_drv.c
+++ b/drivers/gpu/drm/i810/i810_drv.c
@@ -51,6 +51,9 @@ static const struct file_operations i810_driver_fops = {
.mmap = drm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 9cf7dfe022b9..914c0dfabe60 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1587,6 +1587,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&dev_priv->irq_lock);
spin_lock_init(&dev_priv->error_lock);
spin_lock_init(&dev_priv->rps_lock);
+ spin_lock_init(&dev_priv->dpio_lock);
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
dev_priv->num_pipe = 3;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ed22612bc847..a24ffbe97c01 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -346,11 +346,40 @@ static const struct pci_device_id pciidlist[] = { /* aka */
INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */
INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */
INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */
+ INTEL_VGA_DEVICE(0x0422, &intel_haswell_d_info), /* GT2 desktop */
INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */
INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */
+ INTEL_VGA_DEVICE(0x042a, &intel_haswell_d_info), /* GT2 server */
INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */
INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */
- INTEL_VGA_DEVICE(0x0c16, &intel_haswell_d_info), /* SDV */
+ INTEL_VGA_DEVICE(0x0426, &intel_haswell_m_info), /* GT2 mobile */
+ INTEL_VGA_DEVICE(0x0C02, &intel_haswell_d_info), /* SDV GT1 desktop */
+ INTEL_VGA_DEVICE(0x0C12, &intel_haswell_d_info), /* SDV GT2 desktop */
+ INTEL_VGA_DEVICE(0x0C22, &intel_haswell_d_info), /* SDV GT2 desktop */
+ INTEL_VGA_DEVICE(0x0C0A, &intel_haswell_d_info), /* SDV GT1 server */
+ INTEL_VGA_DEVICE(0x0C1A, &intel_haswell_d_info), /* SDV GT2 server */
+ INTEL_VGA_DEVICE(0x0C2A, &intel_haswell_d_info), /* SDV GT2 server */
+ INTEL_VGA_DEVICE(0x0C06, &intel_haswell_m_info), /* SDV GT1 mobile */
+ INTEL_VGA_DEVICE(0x0C16, &intel_haswell_m_info), /* SDV GT2 mobile */
+ INTEL_VGA_DEVICE(0x0C26, &intel_haswell_m_info), /* SDV GT2 mobile */
+ INTEL_VGA_DEVICE(0x0A02, &intel_haswell_d_info), /* ULT GT1 desktop */
+ INTEL_VGA_DEVICE(0x0A12, &intel_haswell_d_info), /* ULT GT2 desktop */
+ INTEL_VGA_DEVICE(0x0A22, &intel_haswell_d_info), /* ULT GT2 desktop */
+ INTEL_VGA_DEVICE(0x0A0A, &intel_haswell_d_info), /* ULT GT1 server */
+ INTEL_VGA_DEVICE(0x0A1A, &intel_haswell_d_info), /* ULT GT2 server */
+ INTEL_VGA_DEVICE(0x0A2A, &intel_haswell_d_info), /* ULT GT2 server */
+ INTEL_VGA_DEVICE(0x0A06, &intel_haswell_m_info), /* ULT GT1 mobile */
+ INTEL_VGA_DEVICE(0x0A16, &intel_haswell_m_info), /* ULT GT2 mobile */
+ INTEL_VGA_DEVICE(0x0A26, &intel_haswell_m_info), /* ULT GT2 mobile */
+ INTEL_VGA_DEVICE(0x0D12, &intel_haswell_d_info), /* CRW GT1 desktop */
+ INTEL_VGA_DEVICE(0x0D22, &intel_haswell_d_info), /* CRW GT2 desktop */
+ INTEL_VGA_DEVICE(0x0D32, &intel_haswell_d_info), /* CRW GT2 desktop */
+ INTEL_VGA_DEVICE(0x0D1A, &intel_haswell_d_info), /* CRW GT1 server */
+ INTEL_VGA_DEVICE(0x0D2A, &intel_haswell_d_info), /* CRW GT2 server */
+ INTEL_VGA_DEVICE(0x0D3A, &intel_haswell_d_info), /* CRW GT2 server */
+ INTEL_VGA_DEVICE(0x0D16, &intel_haswell_m_info), /* CRW GT1 mobile */
+ INTEL_VGA_DEVICE(0x0D26, &intel_haswell_m_info), /* CRW GT2 mobile */
+ INTEL_VGA_DEVICE(0x0D36, &intel_haswell_m_info), /* CRW GT2 mobile */
INTEL_VGA_DEVICE(0x0f30, &intel_valleyview_m_info),
INTEL_VGA_DEVICE(0x0157, &intel_valleyview_m_info),
INTEL_VGA_DEVICE(0x0155, &intel_valleyview_d_info),
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 5c4657a54f97..274d25de521e 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2365,6 +2365,10 @@ int i915_gpu_idle(struct drm_device *dev)
/* Flush everything onto the inactive list. */
for_each_ring(ring, dev_priv, i) {
+ ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
+ if (ret)
+ return ret;
+
ret = i915_ring_idle(ring);
if (ret)
return ret;
@@ -2372,10 +2376,6 @@ int i915_gpu_idle(struct drm_device *dev)
/* Is the device fubar? */
if (WARN_ON(!list_empty(&ring->gpu_write_list)))
return -EBUSY;
-
- ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
- if (ret)
- return ret;
}
return 0;
@@ -3242,7 +3242,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
{
int ret;
- BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
+ if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
+ return -EBUSY;
if (obj->gtt_space != NULL) {
if ((alignment && obj->gtt_offset & (alignment - 1)) ||
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index da8b01fb1bf8..a9d58d72bb4d 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -451,7 +451,6 @@ int i915_switch_context(struct intel_ring_buffer *ring,
struct drm_i915_file_private *file_priv = NULL;
struct i915_hw_context *to;
struct drm_i915_gem_object *from_obj = ring->last_context_obj;
- int ret;
if (dev_priv->hw_contexts_disabled)
return 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 5af631e788c8..ff2819ea0813 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -291,6 +291,16 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
target_i915_obj = to_intel_bo(target_obj);
target_offset = target_i915_obj->gtt_offset;
+ /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and
+ * pipe_control writes because the gpu doesn't properly redirect them
+ * through the ppgtt for non_secure batchbuffers. */
+ if (unlikely(IS_GEN6(dev) &&
+ reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
+ !target_i915_obj->has_global_gtt_mapping)) {
+ i915_gem_gtt_bind_object(target_i915_obj,
+ target_i915_obj->cache_level);
+ }
+
/* The target buffer should have appeared before us in the
* exec_object list, so it should have a GTT space bound by now.
*/
@@ -399,16 +409,6 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
io_mapping_unmap_atomic(reloc_page);
}
- /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and
- * pipe_control writes because the gpu doesn't properly redirect them
- * through the ppgtt for non_secure batchbuffers. */
- if (unlikely(IS_GEN6(dev) &&
- reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
- !target_i915_obj->has_global_gtt_mapping)) {
- i915_gem_gtt_bind_object(target_i915_obj,
- target_i915_obj->cache_level);
- }
-
/* and update the user's relocation entry */
reloc->presumed_offset = target_offset;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 9fd25a435536..60815b861ec2 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -72,7 +72,7 @@ int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
/* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
* entries. For aliasing ppgtt support we just steal them at the end for
* now. */
- first_pd_entry_in_global_pt = 512*1024 - I915_PPGTT_PD_ENTRIES;
+ first_pd_entry_in_global_pt = dev_priv->mm.gtt->gtt_total_entries - I915_PPGTT_PD_ENTRIES;
ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
if (!ppgtt)
@@ -261,7 +261,10 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
pte_flags |= GEN6_PTE_CACHE_LLC;
break;
case I915_CACHE_NONE:
- pte_flags |= GEN6_PTE_UNCACHED;
+ if (IS_HASWELL(dev))
+ pte_flags |= HSW_PTE_UNCACHED;
+ else
+ pte_flags |= GEN6_PTE_UNCACHED;
break;
default:
BUG();
@@ -361,7 +364,8 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (dev_priv->mm.gtt->needs_dmar)
+ /* don't map imported dma buf objects */
+ if (dev_priv->mm.gtt->needs_dmar && !obj->sg_table)
return intel_gtt_map_memory(obj->pages,
obj->base.size >> PAGE_SHIFT,
&obj->sg_list,
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 8a3828528b9d..5249640cce13 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -2700,9 +2700,6 @@ void intel_irq_init(struct drm_device *dev)
dev->driver->irq_handler = i8xx_irq_handler;
dev->driver->irq_uninstall = i8xx_irq_uninstall;
} else if (INTEL_INFO(dev)->gen == 3) {
- /* IIR "flip pending" means done if this bit is set */
- I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE));
-
dev->driver->irq_preinstall = i915_irq_preinstall;
dev->driver->irq_postinstall = i915_irq_postinstall;
dev->driver->irq_uninstall = i915_irq_uninstall;
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index acc99b21e0b6..28725ce5b82c 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -115,6 +115,7 @@
#define GEN6_PTE_VALID (1 << 0)
#define GEN6_PTE_UNCACHED (1 << 1)
+#define HSW_PTE_UNCACHED (0)
#define GEN6_PTE_CACHE_LLC (2 << 1)
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
#define GEN6_PTE_CACHE_BITS (3 << 1)
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 2f5388af8df9..7631807a2788 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -32,6 +32,7 @@
#include "intel_drv.h"
#include "i915_drv.h"
+#ifdef CONFIG_PM
static u32 calc_residency(struct drm_device *dev, const u32 reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -224,3 +225,14 @@ void i915_teardown_sysfs(struct drm_device *dev)
device_remove_bin_file(&dev->primary->kdev, &dpf_attrs);
sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group);
}
+#else
+void i915_setup_sysfs(struct drm_device *dev)
+{
+ return;
+}
+
+void i915_teardown_sysfs(struct drm_device *dev)
+{
+ return;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 7ed4a41c3965..23bdc8cd1458 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -326,6 +326,36 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
return ret;
}
+static struct edid *intel_crt_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *i2c)
+{
+ struct edid *edid;
+
+ edid = drm_get_edid(connector, i2c);
+
+ if (!edid && !intel_gmbus_is_forced_bit(i2c)) {
+ DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n");
+ intel_gmbus_force_bit(i2c, true);
+ edid = drm_get_edid(connector, i2c);
+ intel_gmbus_force_bit(i2c, false);
+ }
+
+ return edid;
+}
+
+/* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */
+static int intel_crt_ddc_get_modes(struct drm_connector *connector,
+ struct i2c_adapter *adapter)
+{
+ struct edid *edid;
+
+ edid = intel_crt_get_edid(connector, adapter);
+ if (!edid)
+ return 0;
+
+ return intel_connector_update_modes(connector, edid);
+}
+
static bool intel_crt_detect_ddc(struct drm_connector *connector)
{
struct intel_crt *crt = intel_attached_crt(connector);
@@ -336,7 +366,7 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
- edid = drm_get_edid(connector, i2c);
+ edid = intel_crt_get_edid(connector, i2c);
if (edid) {
bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
@@ -544,13 +574,13 @@ static int intel_crt_get_modes(struct drm_connector *connector)
struct i2c_adapter *i2c;
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
- ret = intel_ddc_get_modes(connector, i2c);
+ ret = intel_crt_ddc_get_modes(connector, i2c);
if (ret || !IS_G4X(dev))
return ret;
/* Try to probe digital port for output in DVI-I -> VGA mode. */
i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB);
- return intel_ddc_get_modes(connector, i2c);
+ return intel_crt_ddc_get_modes(connector, i2c);
}
static int intel_crt_set_property(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f6159765f1eb..c040aee1341c 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -869,6 +869,7 @@ intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc,
unsigned long bestppm, ppm, absppm;
int dotclk, flag;
+ flag = 0;
dotclk = target * 1000;
bestppm = 1000000;
ppm = absppm = 0;
@@ -1375,7 +1376,8 @@ static void assert_pch_dp_disabled(struct drm_i915_private *dev_priv,
"PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
- WARN(HAS_PCH_IBX(dev_priv->dev) && (val & SDVO_PIPE_B_SELECT),
+ WARN(HAS_PCH_IBX(dev_priv->dev) && (val & DP_PORT_EN) == 0
+ && (val & DP_PIPEB_SELECT),
"IBX PCH dp port still using transcoder B\n");
}
@@ -1383,11 +1385,12 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv,
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- WARN(hdmi_pipe_enabled(dev_priv, val, pipe),
+ WARN(hdmi_pipe_enabled(dev_priv, pipe, val),
"PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
- WARN(HAS_PCH_IBX(dev_priv->dev) && (val & SDVO_PIPE_B_SELECT),
+ WARN(HAS_PCH_IBX(dev_priv->dev) && (val & PORT_ENABLE) == 0
+ && (val & SDVO_PIPE_B_SELECT),
"IBX PCH hdmi port still using transcoder B\n");
}
@@ -1403,13 +1406,13 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
reg = PCH_ADPA;
val = I915_READ(reg);
- WARN(adpa_pipe_enabled(dev_priv, val, pipe),
+ WARN(adpa_pipe_enabled(dev_priv, pipe, val),
"PCH VGA enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
reg = PCH_LVDS;
val = I915_READ(reg);
- WARN(lvds_pipe_enabled(dev_priv, val, pipe),
+ WARN(lvds_pipe_enabled(dev_priv, pipe, val),
"PCH LVDS enabled on transcoder %c, should be disabled\n",
pipe_name(pipe));
@@ -1871,7 +1874,7 @@ static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
enum pipe pipe, int reg)
{
u32 val = I915_READ(reg);
- if (hdmi_pipe_enabled(dev_priv, val, pipe)) {
+ if (hdmi_pipe_enabled(dev_priv, pipe, val)) {
DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
reg, pipe);
I915_WRITE(reg, val & ~PORT_ENABLE);
@@ -1893,12 +1896,12 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
reg = PCH_ADPA;
val = I915_READ(reg);
- if (adpa_pipe_enabled(dev_priv, val, pipe))
+ if (adpa_pipe_enabled(dev_priv, pipe, val))
I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
reg = PCH_LVDS;
val = I915_READ(reg);
- if (lvds_pipe_enabled(dev_priv, val, pipe)) {
+ if (lvds_pipe_enabled(dev_priv, pipe, val)) {
DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val);
I915_WRITE(reg, val & ~LVDS_PORT_EN);
POSTING_READ(reg);
@@ -3753,17 +3756,6 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
continue;
}
- if (intel_encoder->type == INTEL_OUTPUT_EDP) {
- /* Use VBT settings if we have an eDP panel */
- unsigned int edp_bpc = dev_priv->edp.bpp / 3;
-
- if (edp_bpc < display_bpc) {
- DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
- display_bpc = edp_bpc;
- }
- continue;
- }
-
/* Not one of the known troublemakers, check the EDID */
list_for_each_entry(connector, &dev->mode_config.connector_list,
head) {
@@ -4199,12 +4191,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
POSTING_READ(DPLL(pipe));
udelay(150);
- I915_WRITE(DPLL(pipe), dpll);
-
- /* Wait for the clocks to stabilize. */
- POSTING_READ(DPLL(pipe));
- udelay(150);
-
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn't turn
* things on.
@@ -4212,6 +4198,12 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
intel_update_lvds(crtc, clock, adjusted_mode);
+ I915_WRITE(DPLL(pipe), dpll);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
/* The pixel multiplier can only be updated once the
* DPLL is enabled and the clocks are stable.
*
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 0a56b9ab0f58..ace757af9133 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1174,10 +1174,14 @@ static void ironlake_edp_panel_off(struct intel_dp *intel_dp)
WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
pp = ironlake_get_pp_control(dev_priv);
- pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_BLC_ENABLE);
+ /* We need to switch off panel power _and_ force vdd, for otherwise some
+ * panels get very unhappy and cease to work. */
+ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE);
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+ intel_dp->want_panel_vdd = false;
+
ironlake_wait_panel_off(intel_dp);
}
@@ -1287,11 +1291,9 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
* ensure that we have vdd while we switch off the panel. */
ironlake_edp_panel_vdd_on(intel_dp);
ironlake_edp_backlight_off(intel_dp);
- ironlake_edp_panel_off(intel_dp);
-
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+ ironlake_edp_panel_off(intel_dp);
intel_dp_link_down(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, false);
}
static void intel_dp_commit(struct drm_encoder *encoder)
@@ -1326,11 +1328,9 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
/* Switching the panel off requires vdd. */
ironlake_edp_panel_vdd_on(intel_dp);
ironlake_edp_backlight_off(intel_dp);
- ironlake_edp_panel_off(intel_dp);
-
intel_dp_sink_dpms(intel_dp, mode);
+ ironlake_edp_panel_off(intel_dp);
intel_dp_link_down(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, false);
if (is_cpu_edp(intel_dp))
ironlake_edp_pll_off(encoder);
@@ -2533,14 +2533,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
break;
}
- intel_dp_i2c_init(intel_dp, intel_connector, name);
-
/* Cache some DPCD data in the eDP case */
if (is_edp(intel_dp)) {
- bool ret;
struct edp_power_seq cur, vbt;
u32 pp_on, pp_off, pp_div;
- struct edid *edid;
pp_on = I915_READ(PCH_PP_ON_DELAYS);
pp_off = I915_READ(PCH_PP_OFF_DELAYS);
@@ -2591,6 +2587,13 @@ intel_dp_init(struct drm_device *dev, int output_reg)
DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n",
intel_dp->backlight_on_delay, intel_dp->backlight_off_delay);
+ }
+
+ intel_dp_i2c_init(intel_dp, intel_connector, name);
+
+ if (is_edp(intel_dp)) {
+ bool ret;
+ struct edid *edid;
ironlake_edp_panel_vdd_on(intel_dp);
ret = intel_dp_get_dpcd(intel_dp);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 84353559441c..cd54cf88a28f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -46,15 +46,16 @@
})
#define wait_for_atomic_us(COND, US) ({ \
- int i, ret__ = -ETIMEDOUT; \
- for (i = 0; i < (US); i++) { \
- if ((COND)) { \
- ret__ = 0; \
- break; \
- } \
- udelay(1); \
- } \
- ret__; \
+ unsigned long timeout__ = jiffies + usecs_to_jiffies(US); \
+ int ret__ = 0; \
+ while (!(COND)) { \
+ if (time_after(jiffies, timeout__)) { \
+ ret__ = -ETIMEDOUT; \
+ break; \
+ } \
+ cpu_relax(); \
+ } \
+ ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS, 1)
@@ -341,6 +342,8 @@ struct intel_fbc_work {
int interval;
};
+int intel_connector_update_modes(struct drm_connector *connector,
+ struct edid *edid);
int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
extern void intel_attach_force_audio_property(struct drm_connector *connector);
@@ -380,7 +383,6 @@ extern void intel_pch_panel_fitting(struct drm_device *dev,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
-extern u32 intel_panel_get_backlight(struct drm_device *dev);
extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
extern int intel_panel_setup_backlight(struct drm_device *dev);
extern void intel_panel_enable_backlight(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 98f602427eb8..12dc3308ab8c 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -609,7 +609,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
u32 temp;
u32 enable_bits = SDVO_ENABLE;
- if (intel_hdmi->has_audio)
+ if (intel_hdmi->has_audio || mode != DRM_MODE_DPMS_ON)
enable_bits |= SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->sdvox_reg);
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 1991a4408cf9..b9755f6378d8 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -486,9 +486,6 @@ int intel_setup_gmbus(struct drm_device *dev)
bus->dev_priv = dev_priv;
bus->adapter.algo = &gmbus_algorithm;
- ret = i2c_add_adapter(&bus->adapter);
- if (ret)
- goto err;
/* By default use a conservative clock rate */
bus->reg0 = port | GMBUS_RATE_100KHZ;
@@ -498,6 +495,10 @@ int intel_setup_gmbus(struct drm_device *dev)
bus->force_bit = true;
intel_gpio_setup(bus, port);
+
+ ret = i2c_add_adapter(&bus->adapter);
+ if (ret)
+ goto err;
}
intel_i2c_reset(dev_priv->dev);
@@ -540,9 +541,6 @@ void intel_teardown_gmbus(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- if (dev_priv->gmbus == NULL)
- return;
-
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
i2c_del_adapter(&bus->adapter);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index e05c0d3e3440..e9a6f6aaed85 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -780,6 +780,14 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_BOARD_NAME, "ZBOXSD-ID12/ID13"),
},
},
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Gigabyte GA-D525TUD",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
+ },
+ },
{ } /* terminating entry */
};
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 45848b9b670b..29b72593fbb2 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -33,6 +33,25 @@
#include "i915_drv.h"
/**
+ * intel_connector_update_modes - update connector from edid
+ * @connector: DRM connector device to use
+ * @edid: previously read EDID information
+ */
+int intel_connector_update_modes(struct drm_connector *connector,
+ struct edid *edid)
+{
+ int ret;
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+
+ return ret;
+}
+
+/**
* intel_ddc_get_modes - get modelist from monitor
* @connector: DRM connector device to use
* @adapter: i2c adapter
@@ -43,18 +62,12 @@ int intel_ddc_get_modes(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
struct edid *edid;
- int ret = 0;
edid = drm_get_edid(connector, adapter);
- if (edid) {
- drm_mode_connector_update_edid_property(connector, edid);
- ret = drm_add_edid_modes(connector, edid);
- drm_edid_to_eld(connector, edid);
- connector->display_info.raw_edid = NULL;
- kfree(edid);
- }
+ if (!edid)
+ return 0;
- return ret;
+ return intel_connector_update_modes(connector, edid);
}
static const struct drm_prop_enum_list force_audio_names[] = {
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 10c7d39034e1..e019b2369861 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -162,19 +162,12 @@ static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
return val;
}
-u32 intel_panel_get_max_backlight(struct drm_device *dev)
+static u32 _intel_panel_get_max_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 max;
max = i915_read_blc_pwm_ctl(dev_priv);
- if (max == 0) {
- /* XXX add code here to query mode clock or hardware clock
- * and program max PWM appropriately.
- */
- pr_warn_once("fixme: max PWM is zero\n");
- return 1;
- }
if (HAS_PCH_SPLIT(dev)) {
max >>= 16;
@@ -188,6 +181,22 @@ u32 intel_panel_get_max_backlight(struct drm_device *dev)
max *= 0xff;
}
+ return max;
+}
+
+u32 intel_panel_get_max_backlight(struct drm_device *dev)
+{
+ u32 max;
+
+ max = _intel_panel_get_max_backlight(dev);
+ if (max == 0) {
+ /* XXX add code here to query mode clock or hardware clock
+ * and program max PWM appropriately.
+ */
+ pr_warn_once("fixme: max PWM is zero\n");
+ return 1;
+ }
+
DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
return max;
}
@@ -213,7 +222,7 @@ static u32 intel_panel_compute_brightness(struct drm_device *dev, u32 val)
return val;
}
-u32 intel_panel_get_backlight(struct drm_device *dev)
+static u32 intel_panel_get_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
@@ -311,9 +320,6 @@ void intel_panel_enable_backlight(struct drm_device *dev,
if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
- dev_priv->backlight_enabled = true;
- intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
-
if (INTEL_INFO(dev)->gen >= 4) {
uint32_t reg, tmp;
@@ -326,7 +332,7 @@ void intel_panel_enable_backlight(struct drm_device *dev,
* we don't track the backlight dpms state, hence check whether
* we have to do anything first. */
if (tmp & BLM_PWM_ENABLE)
- return;
+ goto set_level;
if (dev_priv->num_pipe == 3)
tmp &= ~BLM_PIPE_SELECT_IVB;
@@ -347,6 +353,14 @@ void intel_panel_enable_backlight(struct drm_device *dev,
I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
}
}
+
+set_level:
+ /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
+ * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
+ * registers are set.
+ */
+ dev_priv->backlight_enabled = true;
+ intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
}
static void intel_panel_init_backlight(struct drm_device *dev)
@@ -419,7 +433,11 @@ int intel_panel_setup_backlight(struct drm_device *dev)
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
- props.max_brightness = intel_panel_get_max_backlight(dev);
+ props.max_brightness = _intel_panel_get_max_backlight(dev);
+ if (props.max_brightness == 0) {
+ DRM_ERROR("Failed to get maximum backlight value\n");
+ return -ENODEV;
+ }
dev_priv->backlight =
backlight_device_register("intel_backlight",
&connector->kdev, dev,
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 94aabcaa3a67..ba8a27b1757a 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2441,17 +2441,10 @@ static void gen6_enable_rps(struct drm_device *dev)
dev_priv->max_delay << 24 |
dev_priv->min_delay << 16);
- if (IS_HASWELL(dev)) {
- I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
- I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
- I915_WRITE(GEN6_RP_UP_EI, 66000);
- I915_WRITE(GEN6_RP_DOWN_EI, 350000);
- } else {
- I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
- I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
- I915_WRITE(GEN6_RP_UP_EI, 100000);
- I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
- }
+ I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
+ I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
+ I915_WRITE(GEN6_RP_UP_EI, 66000);
+ I915_WRITE(GEN6_RP_DOWN_EI, 350000);
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
I915_WRITE(GEN6_RP_CONTROL,
@@ -3679,6 +3672,9 @@ static void gen3_init_clock_gating(struct drm_device *dev)
if (IS_PINEVIEW(dev))
I915_WRITE(ECOSKPD, _MASKED_BIT_ENABLE(ECO_GATING_CX_ONLY));
+
+ /* IIR "flip pending" means done if this bit is set */
+ I915_WRITE(ECOSKPD, _MASKED_BIT_DISABLE(ECO_FLIP_DONE));
}
static void i85x_init_clock_gating(struct drm_device *dev)
@@ -3963,6 +3959,7 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
DRM_ERROR("Force wake wait timed out\n");
I915_WRITE_NOTRACE(FORCEWAKE, 1);
+ POSTING_READ(FORCEWAKE);
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
DRM_ERROR("Force wake wait timed out\n");
@@ -3983,6 +3980,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
DRM_ERROR("Force wake wait timed out\n");
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1));
+ POSTING_READ(FORCEWAKE_MT);
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
DRM_ERROR("Force wake wait timed out\n");
@@ -4018,14 +4016,14 @@ void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE, 0);
- /* The below doubles as a POSTING_READ */
+ POSTING_READ(FORCEWAKE);
gen6_gt_check_fifodbg(dev_priv);
}
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1));
- /* The below doubles as a POSTING_READ */
+ POSTING_READ(FORCEWAKE_MT);
gen6_gt_check_fifodbg(dev_priv);
}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index bf0195a96d53..e2a73b38abe9 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -227,31 +227,36 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring,
* number of bits based on the write domains has little performance
* impact.
*/
- flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
- flags |= PIPE_CONTROL_TLB_INVALIDATE;
- flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
- flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
- /*
- * Ensure that any following seqno writes only happen when the render
- * cache is indeed flushed (but only if the caller actually wants that).
- */
- if (flush_domains)
+ if (flush_domains) {
+ flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+ flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+ /*
+ * Ensure that any following seqno writes only happen
+ * when the render cache is indeed flushed.
+ */
flags |= PIPE_CONTROL_CS_STALL;
+ }
+ if (invalidate_domains) {
+ flags |= PIPE_CONTROL_TLB_INVALIDATE;
+ flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+ /*
+ * TLB invalidate requires a post-sync write.
+ */
+ flags |= PIPE_CONTROL_QW_WRITE;
+ }
- ret = intel_ring_begin(ring, 6);
+ ret = intel_ring_begin(ring, 4);
if (ret)
return ret;
- intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
intel_ring_emit(ring, flags);
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
- intel_ring_emit(ring, 0); /* lower dword */
- intel_ring_emit(ring, 0); /* uppwer dword */
- intel_ring_emit(ring, MI_NOOP);
+ intel_ring_emit(ring, 0);
intel_ring_advance(ring);
return 0;
@@ -289,8 +294,6 @@ static int init_ring_common(struct intel_ring_buffer *ring)
I915_WRITE_HEAD(ring, 0);
ring->write_tail(ring, 0);
- /* Initialize the ring. */
- I915_WRITE_START(ring, obj->gtt_offset);
head = I915_READ_HEAD(ring) & HEAD_ADDR;
/* G45 ring initialization fails to reset head to zero */
@@ -316,6 +319,11 @@ static int init_ring_common(struct intel_ring_buffer *ring)
}
}
+ /* Initialize the ring. This must happen _after_ we've cleared the ring
+ * registers with the above sequence (the readback of the HEAD registers
+ * also enforces ordering), otherwise the hw might lose the new ring
+ * register values. */
+ I915_WRITE_START(ring, obj->gtt_offset);
I915_WRITE_CTL(ring,
((ring->size - PAGE_SIZE) & RING_NR_PAGES)
| RING_VALID);
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 26a6a4d0d078..123afd357611 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -444,13 +444,16 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
struct i2c_msg *msgs;
int i, ret = true;
+ /* Would be simpler to allocate both in one go ? */
buf = (u8 *)kzalloc(args_len * 2 + 2, GFP_KERNEL);
if (!buf)
return false;
msgs = kcalloc(args_len + 3, sizeof(*msgs), GFP_KERNEL);
- if (!msgs)
+ if (!msgs) {
+ kfree(buf);
return false;
+ }
intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
@@ -1689,6 +1692,7 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector)
edid = intel_sdvo_get_edid(connector);
if (edid != NULL && edid->input & DRM_EDID_INPUT_DIGITAL)
has_audio = drm_detect_monitor_audio(edid);
+ kfree(edid);
return has_audio;
}
@@ -2569,7 +2573,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
hotplug_mask = intel_sdvo->is_sdvob ?
SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915;
}
- dev_priv->hotplug_supported_mask |= hotplug_mask;
drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
@@ -2577,14 +2580,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
goto err;
- /* Set up hotplug command - note paranoia about contents of reply.
- * We assume that the hardware is in a sane state, and only touch
- * the bits we think we understand.
- */
- intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG,
- &intel_sdvo->hotplug_active, 2);
- intel_sdvo->hotplug_active[0] &= ~0x3;
-
if (intel_sdvo_output_setup(intel_sdvo,
intel_sdvo->caps.output_flags) != true) {
DRM_DEBUG_KMS("SDVO output failed to setup on %s\n",
@@ -2592,6 +2587,12 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
goto err;
}
+ /* Only enable the hotplug irq if we need it, to work around noisy
+ * hotplug lines.
+ */
+ if (intel_sdvo->hotplug_active[0])
+ dev_priv->hotplug_supported_mask |= hotplug_mask;
+
intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
/* Set the input timing to the screen. Assume always input 0. */
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index cc8df4de2d92..7644f31a3778 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -60,11 +60,11 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb,
switch (fb->pixel_format) {
case DRM_FORMAT_XBGR8888:
- sprctl |= SPRITE_FORMAT_RGBX888;
+ sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
pixel_size = 4;
break;
case DRM_FORMAT_XRGB8888:
- sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX;
+ sprctl |= SPRITE_FORMAT_RGBX888;
pixel_size = 4;
break;
case DRM_FORMAT_YUYV:
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index ea1024d79974..e5f145d2cb3b 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -84,6 +84,9 @@ static const struct file_operations mgag200_driver_fops = {
.mmap = mgag200_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.read = drm_read,
};
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index a4d7c500c97b..b69642d5d850 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -468,10 +468,11 @@ static int mga_g200er_set_plls(struct mga_device *mdev, long clock)
{
unsigned int vcomax, vcomin, pllreffreq;
unsigned int delta, tmpdelta;
- unsigned int testr, testn, testm, testo;
+ int testr, testn, testm, testo;
unsigned int p, m, n;
- unsigned int computed;
+ unsigned int computed, vco;
int tmp;
+ const unsigned int m_div_val[] = { 1, 2, 4, 8 };
m = n = p = 0;
vcomax = 1488000;
@@ -490,12 +491,13 @@ static int mga_g200er_set_plls(struct mga_device *mdev, long clock)
if (delta == 0)
break;
for (testo = 5; testo < 33; testo++) {
- computed = pllreffreq * (testn + 1) /
+ vco = pllreffreq * (testn + 1) /
(testr + 1);
- if (computed < vcomin)
+ if (vco < vcomin)
continue;
- if (computed > vcomax)
+ if (vco > vcomax)
continue;
+ computed = vco / (m_div_val[testm] * (testo + 1));
if (computed > clock)
tmpdelta = computed - clock;
else
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index ff23d88880e5..3ca240b4413d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -179,7 +179,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
return 0;
} else
if (init->class == 0x906e) {
- NV_ERROR(dev, "906e not supported yet\n");
+ NV_DEBUG(dev, "906e not supported yet\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index fc841e87b343..26ebffebe710 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -211,11 +211,6 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
}
-static int nouveau_dsm_init(void)
-{
- return 0;
-}
-
static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
{
/* easy option one - intel vendor ID means Integrated */
@@ -232,7 +227,6 @@ static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
static struct vga_switcheroo_handler nouveau_dsm_handler = {
.switchto = nouveau_dsm_switchto,
.power_state = nouveau_dsm_power_state,
- .init = nouveau_dsm_init,
.get_client_id = nouveau_dsm_get_client_id,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 69688ef5cf46..7e16dc5e6467 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -598,7 +598,7 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
args->size = args->pitch * args->height;
args->size = roundup(args->size, PAGE_SIZE);
- ret = nouveau_gem_new(dev, args->size, 0, TTM_PL_FLAG_VRAM, 0, 0, &bo);
+ ret = nouveau_gem_new(dev, args->size, 0, NOUVEAU_GEM_DOMAIN_VRAM, 0, 0, &bo);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 77e564667b5c..240cf962c999 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -229,7 +229,7 @@ nouveau_i2c_init(struct drm_device *dev)
}
break;
case 6: /* NV50- DP AUX */
- port->drive = entry[0];
+ port->drive = entry[0] & 0x0f;
port->sense = port->drive;
port->adapter.algo = &nouveau_dp_i2c_algo;
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 1cdfd6e757ce..c61014442aa9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -731,15 +731,16 @@ nouveau_card_init(struct drm_device *dev)
case 0xa3:
case 0xa5:
case 0xa8:
- case 0xaf:
nva3_copy_create(dev);
break;
}
break;
case NV_C0:
- nvc0_copy_create(dev, 1);
+ if (!(nv_rd32(dev, 0x022500) & 0x00000200))
+ nvc0_copy_create(dev, 1);
case NV_D0:
- nvc0_copy_create(dev, 0);
+ if (!(nv_rd32(dev, 0x022500) & 0x00000100))
+ nvc0_copy_create(dev, 0);
break;
default:
break;
diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
index f429e6a8ca7a..c399d510b27a 100644
--- a/drivers/gpu/drm/nouveau/nv50_gpio.c
+++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <linux/dmi.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
@@ -110,11 +111,26 @@ nv50_gpio_isr(struct drm_device *dev)
nv_wr32(dev, 0xe074, intr1);
}
+static struct dmi_system_id gpio_reset_ids[] = {
+ {
+ .ident = "Apple Macbook 10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+ }
+ },
+ { }
+};
+
int
nv50_gpio_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ /* initialise gpios and routing to vbios defaults */
+ if (dmi_check_system(gpio_reset_ids))
+ nouveau_gpio_reset(dev);
+
/* disable, and ack any pending gpio interrupts */
nv_wr32(dev, 0xe050, 0x00000000);
nv_wr32(dev, 0xe054, 0xffffffff);
diff --git a/drivers/gpu/drm/nouveau/nv84_fifo.c b/drivers/gpu/drm/nouveau/nv84_fifo.c
index cc82d799fc3b..c564c5e4c30a 100644
--- a/drivers/gpu/drm/nouveau/nv84_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv84_fifo.c
@@ -117,17 +117,22 @@ nv84_fifo_context_del(struct nouveau_channel *chan, int engine)
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
unsigned long flags;
+ u32 save;
/* remove channel from playlist, will context switch if active */
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000);
nv50_fifo_playlist_update(dev);
+ save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
+
/* tell any engines on this channel to unload their contexts */
nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12);
if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff))
NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id);
+ nv_wr32(dev, 0x002520, save);
+
nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
@@ -184,10 +189,13 @@ nv84_fifo_fini(struct drm_device *dev, int engine, bool suspend)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv84_fifo_priv *priv = nv_engine(dev, engine);
int i;
+ u32 save;
/* set playlist length to zero, fifo will unload context */
nv_wr32(dev, 0x0032ec, 0);
+ save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
+
/* tell all connected engines to unload their contexts */
for (i = 0; i < priv->base.channels; i++) {
struct nouveau_channel *chan = dev_priv->channels.ptr[i];
@@ -199,6 +207,7 @@ nv84_fifo_fini(struct drm_device *dev, int engine, bool suspend)
}
}
+ nv_wr32(dev, 0x002520, save);
nv_wr32(dev, 0x002140, 0);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
index f704e942372e..f376c39310df 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fb.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -124,6 +124,7 @@ nvc0_fb_init(struct drm_device *dev)
priv = dev_priv->engine.fb.priv;
nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
+ nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index 7d85553d518c..cd39eb99f5b1 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -373,7 +373,8 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nvc0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
index 7c95c44e2887..4e712b10ebdb 100644
--- a/drivers/gpu/drm/nouveau/nvc0_pm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -557,7 +557,7 @@ prog_mem(struct drm_device *dev, struct nvc0_pm_state *info)
nouveau_mem_exec(&exec, info->perflvl);
if (dev_priv->chipset < 0xd0)
- nv_wr32(dev, 0x611200, 0x00003300);
+ nv_wr32(dev, 0x611200, 0x00003330);
else
nv_wr32(dev, 0x62c000, 0x03030300);
}
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index d0d60e1e7f95..8a2fc89b7763 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -790,7 +790,7 @@ nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ch = EVO_CURS(nv_crtc->index);
- evo_piow(crtc->dev, ch, 0x0084, (y << 16) | x);
+ evo_piow(crtc->dev, ch, 0x0084, (y << 16) | (x & 0xffff));
evo_piow(crtc->dev, ch, 0x0080, 0x00000000);
return 0;
}
@@ -1510,10 +1510,10 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
case OUTPUT_DP:
if (nv_connector->base.display_info.bpc == 6) {
nv_encoder->dp.datarate = mode->clock * 18 / 8;
- syncs |= 0x00000140;
+ syncs |= 0x00000002 << 6;
} else {
nv_encoder->dp.datarate = mode->clock * 24 / 8;
- syncs |= 0x00000180;
+ syncs |= 0x00000005 << 6;
}
if (nv_encoder->dcb->sorconf.link & 1)
diff --git a/drivers/gpu/drm/nouveau/nve0_fifo.c b/drivers/gpu/drm/nouveau/nve0_fifo.c
index 1855ecbd843b..281bece751b6 100644
--- a/drivers/gpu/drm/nouveau/nve0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nve0_fifo.c
@@ -294,6 +294,25 @@ nve0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
printk(" on channel 0x%010llx\n", (u64)inst << 12);
}
+static int
+nve0_fifo_page_flip(struct drm_device *dev, u32 chid)
+{
+ struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = NULL;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->channels.lock, flags);
+ if (likely(chid >= 0 && chid < priv->base.channels)) {
+ chan = dev_priv->channels.ptr[chid];
+ if (likely(chan))
+ ret = nouveau_finish_page_flip(chan, NULL);
+ }
+ spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+ return ret;
+}
+
static void
nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
{
@@ -303,11 +322,21 @@ nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f;
u32 subc = (addr & 0x00070000);
u32 mthd = (addr & 0x00003ffc);
+ u32 show = stat;
+
+ if (stat & 0x00200000) {
+ if (mthd == 0x0054) {
+ if (!nve0_fifo_page_flip(dev, chid))
+ show &= ~0x00200000;
+ }
+ }
- NV_INFO(dev, "PSUBFIFO %d:", unit);
- nouveau_bitfield_print(nve0_fifo_subfifo_intr, stat);
- NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
- unit, chid, subc, mthd, data);
+ if (show) {
+ NV_INFO(dev, "PFIFO%d:", unit);
+ nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
+ NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
+ unit, chid, subc, mthd, data);
+ }
nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
@@ -316,7 +345,8 @@ nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nve0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 9e6f76fec527..2817101fb167 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -258,8 +258,7 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
radeon_crtc->enabled = true;
/* adjust pm to dpms changes BEFORE enabling crtcs */
radeon_pm_compute_clocks(rdev);
- /* disable crtc pair power gating before programming */
- if (ASIC_IS_DCE6(rdev))
+ if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set)
atombios_powergate_crtc(crtc, ATOM_DISABLE);
atombios_enable_crtc(crtc, ATOM_ENABLE);
if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
@@ -278,25 +277,8 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
atombios_enable_crtc_memreq(crtc, ATOM_DISABLE);
atombios_enable_crtc(crtc, ATOM_DISABLE);
radeon_crtc->enabled = false;
- /* power gating is per-pair */
- if (ASIC_IS_DCE6(rdev)) {
- struct drm_crtc *other_crtc;
- struct radeon_crtc *other_radeon_crtc;
- list_for_each_entry(other_crtc, &rdev->ddev->mode_config.crtc_list, head) {
- other_radeon_crtc = to_radeon_crtc(other_crtc);
- if (((radeon_crtc->crtc_id == 0) && (other_radeon_crtc->crtc_id == 1)) ||
- ((radeon_crtc->crtc_id == 1) && (other_radeon_crtc->crtc_id == 0)) ||
- ((radeon_crtc->crtc_id == 2) && (other_radeon_crtc->crtc_id == 3)) ||
- ((radeon_crtc->crtc_id == 3) && (other_radeon_crtc->crtc_id == 2)) ||
- ((radeon_crtc->crtc_id == 4) && (other_radeon_crtc->crtc_id == 5)) ||
- ((radeon_crtc->crtc_id == 5) && (other_radeon_crtc->crtc_id == 4))) {
- /* if both crtcs in the pair are off, enable power gating */
- if (other_radeon_crtc->enabled == false)
- atombios_powergate_crtc(crtc, ATOM_ENABLE);
- break;
- }
- }
- }
+ if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set)
+ atombios_powergate_crtc(crtc, ATOM_ENABLE);
/* adjust pm to dpms changes AFTER disabling crtcs */
radeon_pm_compute_clocks(rdev);
break;
@@ -444,11 +426,28 @@ union atom_enable_ss {
static void atombios_crtc_program_ss(struct radeon_device *rdev,
int enable,
int pll_id,
+ int crtc_id,
struct radeon_atom_ss *ss)
{
+ unsigned i;
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
union atom_enable_ss args;
+ if (!enable) {
+ for (i = 0; i < rdev->num_crtc; i++) {
+ if (rdev->mode_info.crtcs[i] &&
+ rdev->mode_info.crtcs[i]->enabled &&
+ i != crtc_id &&
+ pll_id == rdev->mode_info.crtcs[i]->pll_id) {
+ /* one other crtc is using this pll don't turn
+ * off spread spectrum as it might turn off
+ * display on active crtc
+ */
+ return;
+ }
+ }
+ }
+
memset(&args, 0, sizeof(args));
if (ASIC_IS_DCE5(rdev)) {
@@ -1028,7 +1027,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
&ref_div, &post_div);
- atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
encoder_mode, radeon_encoder->encoder_id, mode->clock,
@@ -1051,7 +1050,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode
ss.step = step_size;
}
- atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
}
}
@@ -1531,12 +1530,12 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
* crtc virtual pixel clock.
*/
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- if (ASIC_IS_DCE5(rdev))
- return ATOM_DCPLL;
+ if (rdev->clock.dp_extclk)
+ return ATOM_PPLL_INVALID;
else if (ASIC_IS_DCE6(rdev))
return ATOM_PPLL0;
- else if (rdev->clock.dp_extclk)
- return ATOM_PPLL_INVALID;
+ else if (ASIC_IS_DCE5(rdev))
+ return ATOM_DCPLL;
}
}
}
@@ -1572,11 +1571,11 @@ void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev)
ASIC_INTERNAL_SS_ON_DCPLL,
rdev->clock.default_dispclk);
if (ss_enabled)
- atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, -1, &ss);
/* XXX: DCE5, make sure voltage, dispclk is high enough */
atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
if (ss_enabled)
- atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, -1, &ss);
}
}
@@ -1635,18 +1634,28 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
static void atombios_crtc_prepare(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ radeon_crtc->in_mode_set = true;
/* pick pll */
radeon_crtc->pll_id = radeon_atom_pick_pll(crtc);
+ /* disable crtc pair power gating before programming */
+ if (ASIC_IS_DCE6(rdev))
+ atombios_powergate_crtc(crtc, ATOM_DISABLE);
+
atombios_lock_crtc(crtc, ATOM_ENABLE);
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void atombios_crtc_commit(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
atombios_lock_crtc(crtc, ATOM_DISABLE);
+ radeon_crtc->in_mode_set = false;
}
static void atombios_crtc_disable(struct drm_crtc *crtc)
@@ -1655,9 +1664,22 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_atom_ss ss;
+ int i;
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+ for (i = 0; i < rdev->num_crtc; i++) {
+ if (rdev->mode_info.crtcs[i] &&
+ rdev->mode_info.crtcs[i]->enabled &&
+ i != radeon_crtc->crtc_id &&
+ radeon_crtc->pll_id == rdev->mode_info.crtcs[i]->pll_id) {
+ /* one other crtc is using this pll don't turn
+ * off the pll
+ */
+ goto done;
+ }
+ }
+
switch (radeon_crtc->pll_id) {
case ATOM_PPLL1:
case ATOM_PPLL2:
@@ -1674,6 +1696,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
default:
break;
}
+done:
radeon_crtc->pll_id = -1;
}
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 7712cf5ab33b..3623b98ed3fe 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -577,30 +577,25 @@ int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
+ u16 dp_bridge = radeon_connector_encoder_get_dp_bridge_encoder_id(connector);
+ u8 tmp;
if (!ASIC_IS_DCE4(rdev))
return panel_mode;
- if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
- ENCODER_OBJECT_ID_NUTMEG)
- panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
- else if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) ==
- ENCODER_OBJECT_ID_TRAVIS) {
- u8 id[6];
- int i;
- for (i = 0; i < 6; i++)
- id[i] = radeon_read_dpcd_reg(radeon_connector, 0x503 + i);
- if (id[0] == 0x73 &&
- id[1] == 0x69 &&
- id[2] == 0x76 &&
- id[3] == 0x61 &&
- id[4] == 0x72 &&
- id[5] == 0x54)
+ if (dp_bridge != ENCODER_OBJECT_ID_NONE) {
+ /* DP bridge chips */
+ tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
+ if (tmp & 1)
+ panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
+ else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) ||
+ (dp_bridge == ENCODER_OBJECT_ID_TRAVIS))
panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
else
- panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
+ panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
} else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
- u8 tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
+ /* eDP */
+ tmp = radeon_read_dpcd_reg(radeon_connector, DP_EDP_CONFIGURATION_CAP);
if (tmp & 1)
panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
}
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index f9bc27fe269a..6e8803a1170c 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -1379,6 +1379,8 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
struct radeon_connector *radeon_connector = NULL;
struct radeon_connector_atom_dig *radeon_dig_connector = NULL;
@@ -1390,19 +1392,37 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
switch (mode) {
case DRM_MODE_DPMS_ON:
- /* some early dce3.2 boards have a bug in their transmitter control table */
- if ((rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV730) ||
- ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
- if (ASIC_IS_DCE6(rdev)) {
- /* It seems we need to call ATOM_ENCODER_CMD_SETUP again
- * before reenabling encoder on DPMS ON, otherwise we never
- * get picture
- */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
+ if (!connector)
+ dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
+ else
+ dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector);
+
+ /* setup and enable the encoder */
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
+ atombios_dig_encoder_setup(encoder,
+ ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
+ dig->panel_mode);
+ if (ext_encoder) {
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))
+ atombios_external_encoder_setup(encoder, ext_encoder,
+ EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
}
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- } else {
+ } else if (ASIC_IS_DCE4(rdev)) {
+ /* setup and enable the encoder */
+ atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
+ /* enable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
+ } else {
+ /* setup and enable the encoder and transmitter */
+ atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ /* some early dce3.2 boards have a bug in their transmitter control table */
+ if ((rdev->family != CHIP_RV710) || (rdev->family != CHIP_RV730))
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
}
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
@@ -1420,10 +1440,19 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev))
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
+ /* disable the transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- else
+ } else if (ASIC_IS_DCE4(rdev)) {
+ /* disable the transmitter */
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ } else {
+ /* disable the encoder and transmitter */
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
+ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
+ }
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector) {
if (ASIC_IS_DCE4(rdev))
atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
@@ -1740,13 +1769,34 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
struct drm_encoder *test_encoder;
- struct radeon_encoder_atom_dig *dig;
+ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
uint32_t dig_enc_in_use = 0;
- /* DCE4/5 */
- if (ASIC_IS_DCE4(rdev)) {
- dig = radeon_encoder->enc_priv;
- if (ASIC_IS_DCE41(rdev)) {
+ if (ASIC_IS_DCE6(rdev)) {
+ /* DCE6 */
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ if (dig->linkb)
+ return 1;
+ else
+ return 0;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ if (dig->linkb)
+ return 3;
+ else
+ return 2;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ if (dig->linkb)
+ return 5;
+ else
+ return 4;
+ break;
+ }
+ } else if (ASIC_IS_DCE4(rdev)) {
+ /* DCE4/5 */
+ if (ASIC_IS_DCE41(rdev) && !ASIC_IS_DCE61(rdev)) {
/* ontario follows DCE4 */
if (rdev->family == CHIP_PALM) {
if (dig->linkb)
@@ -1848,10 +1898,12 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct drm_encoder *ext_encoder = radeon_get_external_encoder(encoder);
radeon_encoder->pixel_clock = adjusted_mode->clock;
+ /* need to call this here rather than in prepare() since we need some crtc info */
+ radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE4(rdev)) {
if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT))
atombios_yuv_setup(encoder, true);
@@ -1870,38 +1922,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
- struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
-
- if (!connector)
- dig->panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
- else
- dig->panel_mode = radeon_dp_get_panel_mode(encoder, connector);
-
- /* setup and enable the encoder */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
- atombios_dig_encoder_setup(encoder,
- ATOM_ENCODER_CMD_SETUP_PANEL_MODE,
- dig->panel_mode);
- } else if (ASIC_IS_DCE4(rdev)) {
- /* disable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- /* setup and enable the encoder */
- atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_SETUP, 0);
-
- /* enable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- } else {
- /* disable the encoder and transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
-
- /* setup and enable the encoder and transmitter */
- atombios_dig_encoder_setup(encoder, ATOM_ENABLE, 0);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
- }
+ /* handled in dpms */
break;
case ENCODER_OBJECT_ID_INTERNAL_DDI:
case ENCODER_OBJECT_ID_INTERNAL_DVO1:
@@ -1922,14 +1943,6 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
break;
}
- if (ext_encoder) {
- if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))
- atombios_external_encoder_setup(encoder, ext_encoder,
- EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
- else
- atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE);
- }
-
atombios_apply_encoder_quirks(encoder, adjusted_mode);
if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) {
@@ -2116,7 +2129,6 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
}
radeon_atom_output_lock(encoder, true);
- radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
if (connector) {
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -2137,6 +2149,7 @@ static void radeon_atom_encoder_prepare(struct drm_encoder *encoder)
static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
{
+ /* need to call this here as we need the crtc set up */
radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
radeon_atom_output_lock(encoder, false);
}
@@ -2177,14 +2190,7 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
- if (ASIC_IS_DCE4(rdev))
- /* disable the transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- else {
- /* disable the encoder and transmitter */
- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
- atombios_dig_encoder_setup(encoder, ATOM_DISABLE, 0);
- }
+ /* handled in dpms */
break;
case ENCODER_OBJECT_ID_INTERNAL_DDI:
case ENCODER_OBJECT_ID_INTERNAL_DVO1:
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index e585a3b947eb..e93b80a6d4e9 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1229,24 +1229,8 @@ void evergreen_agp_enable(struct radeon_device *rdev)
void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
{
- save->vga_control[0] = RREG32(D1VGA_CONTROL);
- save->vga_control[1] = RREG32(D2VGA_CONTROL);
save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
- save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
- save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
- if (rdev->num_crtc >= 4) {
- save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
- save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
- save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
- save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
- }
- if (rdev->num_crtc >= 6) {
- save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
- save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
- save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
- save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
- }
/* Stop all video */
WREG32(VGA_RENDER_CONTROL, 0);
@@ -1357,47 +1341,6 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
/* Unlock host access */
WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
mdelay(1);
- /* Restore video state */
- WREG32(D1VGA_CONTROL, save->vga_control[0]);
- WREG32(D2VGA_CONTROL, save->vga_control[1]);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
- WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
- WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
- }
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
- }
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
- }
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
- }
WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
}
@@ -1986,10 +1929,18 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
if (rdev->flags & RADEON_IS_IGP)
rdev->config.evergreen.tile_config |= 1 << 4;
else {
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.evergreen.tile_config |= 1 << 4;
- else
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.evergreen.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.evergreen.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.evergreen.tile_config |= 2 << 4;
+ break;
+ }
}
rdev->config.evergreen.tile_config |= 0 << 8;
rdev->config.evergreen.tile_config |=
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index c16554122ccd..e44a62a07fe3 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -788,6 +788,13 @@ static int evergreen_cs_track_validate_texture(struct radeon_cs_parser *p,
case V_030000_SQ_TEX_DIM_1D_ARRAY:
case V_030000_SQ_TEX_DIM_2D_ARRAY:
depth = 1;
+ break;
+ case V_030000_SQ_TEX_DIM_2D_MSAA:
+ case V_030000_SQ_TEX_DIM_2D_ARRAY_MSAA:
+ surf.nsamples = 1 << llevel;
+ llevel = 0;
+ depth = 1;
+ break;
case V_030000_SQ_TEX_DIM_3D:
break;
default:
@@ -961,13 +968,15 @@ static int evergreen_cs_track_check(struct radeon_cs_parser *p)
if (track->db_dirty) {
/* Check stencil buffer */
- if (G_028800_STENCIL_ENABLE(track->db_depth_control)) {
+ if (G_028044_FORMAT(track->db_s_info) != V_028044_STENCIL_INVALID &&
+ G_028800_STENCIL_ENABLE(track->db_depth_control)) {
r = evergreen_cs_track_validate_stencil(p);
if (r)
return r;
}
/* Check depth buffer */
- if (G_028800_Z_ENABLE(track->db_depth_control)) {
+ if (G_028040_FORMAT(track->db_z_info) != V_028040_Z_INVALID &&
+ G_028800_Z_ENABLE(track->db_depth_control)) {
r = evergreen_cs_track_validate_depth(p);
if (r)
return r;
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index d3bd098e4e19..79347855d9bf 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -1277,6 +1277,8 @@
#define S_028044_FORMAT(x) (((x) & 0x1) << 0)
#define G_028044_FORMAT(x) (((x) >> 0) & 0x1)
#define C_028044_FORMAT 0xFFFFFFFE
+#define V_028044_STENCIL_INVALID 0
+#define V_028044_STENCIL_8 1
#define G_028044_TILE_SPLIT(x) (((x) >> 8) & 0x7)
#define DB_Z_READ_BASE 0x28048
#define DB_STENCIL_READ_BASE 0x2804c
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 9945d86d9001..853800e8582f 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -574,10 +574,18 @@ static void cayman_gpu_init(struct radeon_device *rdev)
if (rdev->flags & RADEON_IS_IGP)
rdev->config.cayman.tile_config |= 1 << 4;
else {
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.cayman.tile_config |= 1 << 4;
- else
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.cayman.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.cayman.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.cayman.tile_config |= 2 << 4;
+ break;
+ }
}
rdev->config.cayman.tile_config |=
((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 8acb34fd3fd5..8d7e33a0b243 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -1182,7 +1182,8 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
ring->ready = true;
radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size);
- if (radeon_ring_supports_scratch_reg(rdev, ring)) {
+ if (!ring->rptr_save_reg /* not resuming from suspend */
+ && radeon_ring_supports_scratch_reg(rdev, ring)) {
r = radeon_scratch_get(rdev, &ring->rptr_save_reg);
if (r) {
DRM_ERROR("failed to get scratch reg for rptr save (%d).\n", r);
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 637280f541a3..d79c639ae739 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -3789,3 +3789,23 @@ static void r600_pcie_gen2_enable(struct radeon_device *rdev)
WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
}
}
+
+/**
+ * r600_get_gpu_clock - return GPU clock counter snapshot
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Fetches a GPU clock counter snapshot (R6xx-cayman).
+ * Returns the 64 bit clock counter snapshot.
+ */
+uint64_t r600_get_gpu_clock(struct radeon_device *rdev)
+{
+ uint64_t clock;
+
+ mutex_lock(&rdev->gpu_clock_mutex);
+ WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1);
+ clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) |
+ ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL);
+ mutex_unlock(&rdev->gpu_clock_mutex);
+ return clock;
+}
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index ca87f7afaf23..f37676d7f217 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -47,18 +47,23 @@ struct r600_cs_track {
u32 npipes;
/* value we track */
u32 sq_config;
+ u32 log_nsamples;
u32 nsamples;
u32 cb_color_base_last[8];
struct radeon_bo *cb_color_bo[8];
u64 cb_color_bo_mc[8];
- u32 cb_color_bo_offset[8];
- struct radeon_bo *cb_color_frag_bo[8]; /* unused */
- struct radeon_bo *cb_color_tile_bo[8]; /* unused */
+ u64 cb_color_bo_offset[8];
+ struct radeon_bo *cb_color_frag_bo[8];
+ u64 cb_color_frag_offset[8];
+ struct radeon_bo *cb_color_tile_bo[8];
+ u64 cb_color_tile_offset[8];
+ u32 cb_color_mask[8];
u32 cb_color_info[8];
u32 cb_color_view[8];
u32 cb_color_size_idx[8]; /* unused */
u32 cb_target_mask;
u32 cb_shader_mask; /* unused */
+ bool is_resolve;
u32 cb_color_size[8];
u32 vgt_strmout_en;
u32 vgt_strmout_buffer_en;
@@ -311,7 +316,15 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->cb_color_bo[i] = NULL;
track->cb_color_bo_offset[i] = 0xFFFFFFFF;
track->cb_color_bo_mc[i] = 0xFFFFFFFF;
- }
+ track->cb_color_frag_bo[i] = NULL;
+ track->cb_color_frag_offset[i] = 0xFFFFFFFF;
+ track->cb_color_tile_bo[i] = NULL;
+ track->cb_color_tile_offset[i] = 0xFFFFFFFF;
+ track->cb_color_mask[i] = 0xFFFFFFFF;
+ }
+ track->is_resolve = false;
+ track->nsamples = 16;
+ track->log_nsamples = 4;
track->cb_target_mask = 0xFFFFFFFF;
track->cb_shader_mask = 0xFFFFFFFF;
track->cb_dirty = true;
@@ -348,11 +361,9 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
volatile u32 *ib = p->ib.ptr;
unsigned array_mode;
u32 format;
+ /* When resolve is used, the second colorbuffer has always 1 sample. */
+ unsigned nsamples = track->is_resolve && i == 1 ? 1 : track->nsamples;
- if (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
- dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
- return -EINVAL;
- }
size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i];
format = G_0280A0_FORMAT(track->cb_color_info[i]);
if (!r600_fmt_is_valid_color(format)) {
@@ -375,7 +386,7 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
array_check.group_size = track->group_size;
array_check.nbanks = track->nbanks;
array_check.npipes = track->npipes;
- array_check.nsamples = track->nsamples;
+ array_check.nsamples = nsamples;
array_check.blocksize = r600_fmt_get_blocksize(format);
if (r600_get_array_mode_alignment(&array_check,
&pitch_align, &height_align, &depth_align, &base_align)) {
@@ -420,7 +431,8 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
}
/* check offset */
- tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) * r600_fmt_get_blocksize(format);
+ tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) *
+ r600_fmt_get_blocksize(format) * nsamples;
switch (array_mode) {
default:
case V_0280A0_ARRAY_LINEAR_GENERAL:
@@ -441,7 +453,7 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
* broken userspace.
*/
} else {
- dev_warn(p->dev, "%s offset[%d] %d %d %d %lu too big (%d %d) (%d %d %d)\n",
+ dev_warn(p->dev, "%s offset[%d] %d %llu %d %lu too big (%d %d) (%d %d %d)\n",
__func__, i, array_mode,
track->cb_color_bo_offset[i], tmp,
radeon_bo_size(track->cb_color_bo[i]),
@@ -458,6 +470,51 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
tmp = S_028060_PITCH_TILE_MAX((pitch / 8) - 1) |
S_028060_SLICE_TILE_MAX(slice_tile_max - 1);
ib[track->cb_color_size_idx[i]] = tmp;
+
+ /* FMASK/CMASK */
+ switch (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
+ case V_0280A0_TILE_DISABLE:
+ break;
+ case V_0280A0_FRAG_ENABLE:
+ if (track->nsamples > 1) {
+ uint32_t tile_max = G_028100_FMASK_TILE_MAX(track->cb_color_mask[i]);
+ /* the tile size is 8x8, but the size is in units of bits.
+ * for bytes, do just * 8. */
+ uint32_t bytes = track->nsamples * track->log_nsamples * 8 * (tile_max + 1);
+
+ if (bytes + track->cb_color_frag_offset[i] >
+ radeon_bo_size(track->cb_color_frag_bo[i])) {
+ dev_warn(p->dev, "%s FMASK_TILE_MAX too large "
+ "(tile_max=%u, bytes=%u, offset=%llu, bo_size=%lu)\n",
+ __func__, tile_max, bytes,
+ track->cb_color_frag_offset[i],
+ radeon_bo_size(track->cb_color_frag_bo[i]));
+ return -EINVAL;
+ }
+ }
+ /* fall through */
+ case V_0280A0_CLEAR_ENABLE:
+ {
+ uint32_t block_max = G_028100_CMASK_BLOCK_MAX(track->cb_color_mask[i]);
+ /* One block = 128x128 pixels, one 8x8 tile has 4 bits..
+ * (128*128) / (8*8) / 2 = 128 bytes per block. */
+ uint32_t bytes = (block_max + 1) * 128;
+
+ if (bytes + track->cb_color_tile_offset[i] >
+ radeon_bo_size(track->cb_color_tile_bo[i])) {
+ dev_warn(p->dev, "%s CMASK_BLOCK_MAX too large "
+ "(block_max=%u, bytes=%u, offset=%llu, bo_size=%lu)\n",
+ __func__, block_max, bytes,
+ track->cb_color_tile_offset[i],
+ radeon_bo_size(track->cb_color_tile_bo[i]));
+ return -EINVAL;
+ }
+ break;
+ }
+ default:
+ dev_warn(p->dev, "%s invalid tile mode\n", __func__);
+ return -EINVAL;
+ }
return 0;
}
@@ -566,7 +623,7 @@ static int r600_cs_track_validate_db(struct radeon_cs_parser *p)
ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
- tmp = ntiles * bpe * 64 * nviews;
+ tmp = ntiles * bpe * 64 * nviews * track->nsamples;
if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
array_mode,
@@ -746,6 +803,12 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
*/
if (track->cb_dirty) {
tmp = track->cb_target_mask;
+
+ /* We must check both colorbuffers for RESOLVE. */
+ if (track->is_resolve) {
+ tmp |= 0xff;
+ }
+
for (i = 0; i < 8; i++) {
if ((tmp >> (i * 4)) & 0xF) {
/* at least one component is enabled */
@@ -764,8 +827,10 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
}
/* Check depth buffer */
- if (track->db_dirty && (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
- G_028800_Z_ENABLE(track->db_depth_control))) {
+ if (track->db_dirty &&
+ G_028010_FORMAT(track->db_depth_info) != V_028010_DEPTH_INVALID &&
+ (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
+ G_028800_Z_ENABLE(track->db_depth_control))) {
r = r600_cs_track_validate_db(p);
if (r)
return r;
@@ -1229,9 +1294,15 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case R_028C04_PA_SC_AA_CONFIG:
tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx));
+ track->log_nsamples = tmp;
track->nsamples = 1 << tmp;
track->cb_dirty = true;
break;
+ case R_028808_CB_COLOR_CONTROL:
+ tmp = G_028808_SPECIAL_OP(radeon_get_ib_value(p, idx));
+ track->is_resolve = tmp == V_028808_SPECIAL_RESOLVE_BOX;
+ track->cb_dirty = true;
+ break;
case R_0280A0_CB_COLOR0_INFO:
case R_0280A4_CB_COLOR1_INFO:
case R_0280A8_CB_COLOR2_INFO:
@@ -1310,16 +1381,21 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
return -EINVAL;
}
- ib[idx] = track->cb_color_base_last[tmp];
track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp];
+ track->cb_color_frag_offset[tmp] = track->cb_color_bo_offset[tmp];
+ ib[idx] = track->cb_color_base_last[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_frag_bo[tmp] = reloc->robj;
+ track->cb_color_frag_offset[tmp] = (u64)ib[idx] << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ }
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
}
break;
case R_0280C0_CB_COLOR0_TILE:
@@ -1336,16 +1412,35 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
return -EINVAL;
}
- ib[idx] = track->cb_color_base_last[tmp];
track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp];
+ track->cb_color_tile_offset[tmp] = track->cb_color_bo_offset[tmp];
+ ib[idx] = track->cb_color_base_last[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_tile_bo[tmp] = reloc->robj;
+ track->cb_color_tile_offset[tmp] = (u64)ib[idx] << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ }
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
+ }
+ break;
+ case R_028100_CB_COLOR0_MASK:
+ case R_028104_CB_COLOR1_MASK:
+ case R_028108_CB_COLOR2_MASK:
+ case R_02810C_CB_COLOR3_MASK:
+ case R_028110_CB_COLOR4_MASK:
+ case R_028114_CB_COLOR5_MASK:
+ case R_028118_CB_COLOR6_MASK:
+ case R_02811C_CB_COLOR7_MASK:
+ tmp = (reg - R_028100_CB_COLOR0_MASK) / 4;
+ track->cb_color_mask[tmp] = radeon_get_ib_value(p, idx);
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
}
break;
case CB_COLOR0_BASE:
@@ -1490,7 +1585,7 @@ unsigned r600_mip_minify(unsigned size, unsigned level)
}
static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel,
- unsigned w0, unsigned h0, unsigned d0, unsigned format,
+ unsigned w0, unsigned h0, unsigned d0, unsigned nsamples, unsigned format,
unsigned block_align, unsigned height_align, unsigned base_align,
unsigned *l0_size, unsigned *mipmap_size)
{
@@ -1518,7 +1613,7 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel,
depth = r600_mip_minify(d0, i);
- size = nbx * nby * blocksize;
+ size = nbx * nby * blocksize * nsamples;
if (nfaces)
size *= nfaces;
else
@@ -1557,13 +1652,14 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
u32 tiling_flags)
{
struct r600_cs_track *track = p->track;
- u32 nfaces, llevel, blevel, w0, h0, d0;
- u32 word0, word1, l0_size, mipmap_size, word2, word3;
+ u32 dim, nfaces, llevel, blevel, w0, h0, d0;
+ u32 word0, word1, l0_size, mipmap_size, word2, word3, word4, word5;
u32 height_align, pitch, pitch_align, depth_align;
- u32 array, barray, larray;
+ u32 barray, larray;
u64 base_align;
struct array_mode_checker array_check;
u32 format;
+ bool is_array;
/* on legacy kernel we don't perform advanced check */
if (p->rdev == NULL)
@@ -1581,12 +1677,28 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
}
word1 = radeon_get_ib_value(p, idx + 1);
+ word2 = radeon_get_ib_value(p, idx + 2) << 8;
+ word3 = radeon_get_ib_value(p, idx + 3) << 8;
+ word4 = radeon_get_ib_value(p, idx + 4);
+ word5 = radeon_get_ib_value(p, idx + 5);
+ dim = G_038000_DIM(word0);
w0 = G_038000_TEX_WIDTH(word0) + 1;
+ pitch = (G_038000_PITCH(word0) + 1) * 8;
h0 = G_038004_TEX_HEIGHT(word1) + 1;
d0 = G_038004_TEX_DEPTH(word1);
+ format = G_038004_DATA_FORMAT(word1);
+ blevel = G_038010_BASE_LEVEL(word4);
+ llevel = G_038014_LAST_LEVEL(word5);
+ /* pitch in texels */
+ array_check.array_mode = G_038000_TILE_MODE(word0);
+ array_check.group_size = track->group_size;
+ array_check.nbanks = track->nbanks;
+ array_check.npipes = track->npipes;
+ array_check.nsamples = 1;
+ array_check.blocksize = r600_fmt_get_blocksize(format);
nfaces = 1;
- array = 0;
- switch (G_038000_DIM(word0)) {
+ is_array = false;
+ switch (dim) {
case V_038000_SQ_TEX_DIM_1D:
case V_038000_SQ_TEX_DIM_2D:
case V_038000_SQ_TEX_DIM_3D:
@@ -1599,29 +1711,25 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
break;
case V_038000_SQ_TEX_DIM_1D_ARRAY:
case V_038000_SQ_TEX_DIM_2D_ARRAY:
- array = 1;
+ is_array = true;
break;
- case V_038000_SQ_TEX_DIM_2D_MSAA:
case V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA:
+ is_array = true;
+ /* fall through */
+ case V_038000_SQ_TEX_DIM_2D_MSAA:
+ array_check.nsamples = 1 << llevel;
+ llevel = 0;
+ break;
default:
dev_warn(p->dev, "this kernel doesn't support %d texture dim\n", G_038000_DIM(word0));
return -EINVAL;
}
- format = G_038004_DATA_FORMAT(word1);
if (!r600_fmt_is_valid_texture(format, p->family)) {
dev_warn(p->dev, "%s:%d texture invalid format %d\n",
__func__, __LINE__, format);
return -EINVAL;
}
- /* pitch in texels */
- pitch = (G_038000_PITCH(word0) + 1) * 8;
- array_check.array_mode = G_038000_TILE_MODE(word0);
- array_check.group_size = track->group_size;
- array_check.nbanks = track->nbanks;
- array_check.npipes = track->npipes;
- array_check.nsamples = 1;
- array_check.blocksize = r600_fmt_get_blocksize(format);
if (r600_get_array_mode_alignment(&array_check,
&pitch_align, &height_align, &depth_align, &base_align)) {
dev_warn(p->dev, "%s:%d tex array mode (%d) invalid\n",
@@ -1647,24 +1755,17 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
return -EINVAL;
}
- word2 = radeon_get_ib_value(p, idx + 2) << 8;
- word3 = radeon_get_ib_value(p, idx + 3) << 8;
-
- word0 = radeon_get_ib_value(p, idx + 4);
- word1 = radeon_get_ib_value(p, idx + 5);
- blevel = G_038010_BASE_LEVEL(word0);
- llevel = G_038014_LAST_LEVEL(word1);
if (blevel > llevel) {
dev_warn(p->dev, "texture blevel %d > llevel %d\n",
blevel, llevel);
}
- if (array == 1) {
- barray = G_038014_BASE_ARRAY(word1);
- larray = G_038014_LAST_ARRAY(word1);
+ if (is_array) {
+ barray = G_038014_BASE_ARRAY(word5);
+ larray = G_038014_LAST_ARRAY(word5);
nfaces = larray - barray + 1;
}
- r600_texture_size(nfaces, blevel, llevel, w0, h0, d0, format,
+ r600_texture_size(nfaces, blevel, llevel, w0, h0, d0, array_check.nsamples, format,
pitch_align, height_align, base_align,
&l0_size, &mipmap_size);
/* using get ib will give us the offset into the texture bo */
@@ -1677,7 +1778,6 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
return -EINVAL;
}
/* using get ib will give us the offset into the mipmap bo */
- word3 = radeon_get_ib_value(p, idx + 3) << 8;
if ((mipmap_size + word3) > radeon_bo_size(mipmap)) {
/*dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
w0, h0, format, blevel, nlevels, word3, mipmap_size, radeon_bo_size(texture));*/
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 4b116ae75fc2..fa6f37099ba9 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -66,6 +66,14 @@
#define CC_RB_BACKEND_DISABLE 0x98F4
#define BACKEND_DISABLE(x) ((x) << 16)
+#define R_028808_CB_COLOR_CONTROL 0x28808
+#define S_028808_SPECIAL_OP(x) (((x) & 0x7) << 4)
+#define G_028808_SPECIAL_OP(x) (((x) >> 4) & 0x7)
+#define C_028808_SPECIAL_OP 0xFFFFFF8F
+#define V_028808_SPECIAL_NORMAL 0x00
+#define V_028808_SPECIAL_DISABLE 0x01
+#define V_028808_SPECIAL_RESOLVE_BOX 0x07
+
#define CB_COLOR0_BASE 0x28040
#define CB_COLOR1_BASE 0x28044
#define CB_COLOR2_BASE 0x28048
@@ -92,6 +100,20 @@
#define R_028094_CB_COLOR5_VIEW 0x028094
#define R_028098_CB_COLOR6_VIEW 0x028098
#define R_02809C_CB_COLOR7_VIEW 0x02809C
+#define R_028100_CB_COLOR0_MASK 0x028100
+#define S_028100_CMASK_BLOCK_MAX(x) (((x) & 0xFFF) << 0)
+#define G_028100_CMASK_BLOCK_MAX(x) (((x) >> 0) & 0xFFF)
+#define C_028100_CMASK_BLOCK_MAX 0xFFFFF000
+#define S_028100_FMASK_TILE_MAX(x) (((x) & 0xFFFFF) << 12)
+#define G_028100_FMASK_TILE_MAX(x) (((x) >> 12) & 0xFFFFF)
+#define C_028100_FMASK_TILE_MAX 0x00000FFF
+#define R_028104_CB_COLOR1_MASK 0x028104
+#define R_028108_CB_COLOR2_MASK 0x028108
+#define R_02810C_CB_COLOR3_MASK 0x02810C
+#define R_028110_CB_COLOR4_MASK 0x028110
+#define R_028114_CB_COLOR5_MASK 0x028114
+#define R_028118_CB_COLOR6_MASK 0x028118
+#define R_02811C_CB_COLOR7_MASK 0x02811C
#define CB_COLOR0_INFO 0x280a0
# define CB_FORMAT(x) ((x) << 2)
# define CB_ARRAY_MODE(x) ((x) << 8)
@@ -602,6 +624,9 @@
#define RLC_HB_WPTR 0x3f1c
#define RLC_HB_WPTR_LSB_ADDR 0x3f14
#define RLC_HB_WPTR_MSB_ADDR 0x3f18
+#define RLC_GPU_CLOCK_COUNT_LSB 0x3f38
+#define RLC_GPU_CLOCK_COUNT_MSB 0x3f3c
+#define RLC_CAPTURE_GPU_CLOCK_COUNT 0x3f40
#define RLC_MC_CNTL 0x3f44
#define RLC_UCODE_CNTL 0x3f48
#define RLC_UCODE_ADDR 0x3f2c
@@ -1397,6 +1422,9 @@
#define S_0280A0_TILE_MODE(x) (((x) & 0x3) << 18)
#define G_0280A0_TILE_MODE(x) (((x) >> 18) & 0x3)
#define C_0280A0_TILE_MODE 0xFFF3FFFF
+#define V_0280A0_TILE_DISABLE 0
+#define V_0280A0_CLEAR_ENABLE 1
+#define V_0280A0_FRAG_ENABLE 2
#define S_0280A0_BLEND_CLAMP(x) (((x) & 0x1) << 20)
#define G_0280A0_BLEND_CLAMP(x) (((x) >> 20) & 0x1)
#define C_0280A0_BLEND_CLAMP 0xFFEFFFFF
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 5431af292408..59a15315ae9f 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -142,21 +142,6 @@ struct radeon_device;
/*
* BIOS.
*/
-#define ATRM_BIOS_PAGE 4096
-
-#if defined(CONFIG_VGA_SWITCHEROO)
-bool radeon_atrm_supported(struct pci_dev *pdev);
-int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len);
-#else
-static inline bool radeon_atrm_supported(struct pci_dev *pdev)
-{
- return false;
-}
-
-static inline int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len){
- return -EINVAL;
-}
-#endif
bool radeon_get_bios(struct radeon_device *rdev);
/*
@@ -300,6 +285,7 @@ struct radeon_bo_va {
uint64_t soffset;
uint64_t eoffset;
uint32_t flags;
+ struct radeon_fence *fence;
bool valid;
};
@@ -1533,6 +1519,7 @@ struct radeon_device {
unsigned debugfs_count;
/* virtual memory */
struct radeon_vm_manager vm_manager;
+ struct mutex gpu_clock_mutex;
};
int radeon_device_init(struct radeon_device *rdev,
@@ -1733,11 +1720,11 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev))
#define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev))
#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev))
-#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pflip.pre_page_flip((rdev), (crtc))
-#define radeon_page_flip(rdev, crtc, base) rdev->asic->pflip.page_flip((rdev), (crtc), (base))
-#define radeon_post_page_flip(rdev, crtc) rdev->asic->pflip.post_page_flip((rdev), (crtc))
-#define radeon_wait_for_vblank(rdev, crtc) rdev->asic->display.wait_for_vblank((rdev), (crtc))
-#define radeon_mc_wait_for_idle(rdev) rdev->asic->mc_wait_for_idle((rdev))
+#define radeon_pre_page_flip(rdev, crtc) (rdev)->asic->pflip.pre_page_flip((rdev), (crtc))
+#define radeon_page_flip(rdev, crtc, base) (rdev)->asic->pflip.page_flip((rdev), (crtc), (base))
+#define radeon_post_page_flip(rdev, crtc) (rdev)->asic->pflip.post_page_flip((rdev), (crtc))
+#define radeon_wait_for_vblank(rdev, crtc) (rdev)->asic->display.wait_for_vblank((rdev), (crtc))
+#define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
/* Common functions */
/* AGP */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index f4af24310438..18c38d14c8cd 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -255,13 +255,10 @@ extern int rs690_mc_wait_for_idle(struct radeon_device *rdev);
* rv515
*/
struct rv515_mc_save {
- u32 d1vga_control;
- u32 d2vga_control;
u32 vga_render_control;
u32 vga_hdp_control;
- u32 d1crtc_control;
- u32 d2crtc_control;
};
+
int rv515_init(struct radeon_device *rdev);
void rv515_fini(struct radeon_device *rdev);
uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg);
@@ -371,6 +368,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
unsigned num_gpu_pages,
struct radeon_sa_bo *vb);
int r600_mc_wait_for_idle(struct radeon_device *rdev);
+uint64_t r600_get_gpu_clock(struct radeon_device *rdev);
/*
* rv770,rv730,rv710,rv740
@@ -389,11 +387,10 @@ void r700_cp_fini(struct radeon_device *rdev);
* evergreen
*/
struct evergreen_mc_save {
- u32 vga_control[6];
u32 vga_render_control;
u32 vga_hdp_control;
- u32 crtc_control[6];
};
+
void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev);
int evergreen_init(struct radeon_device *rdev);
void evergreen_fini(struct radeon_device *rdev);
@@ -472,5 +469,6 @@ int si_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm, int id);
void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm);
void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm);
int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
+uint64_t si_get_gpu_clock(struct radeon_device *rdev);
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index b1e3820df363..d67d4f3eb6f4 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -452,7 +452,7 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
}
/* Fujitsu D3003-S2 board lists DVI-I as DVI-D and VGA */
- if ((dev->pdev->device == 0x9802) &&
+ if (((dev->pdev->device == 0x9802) || (dev->pdev->device == 0x9806)) &&
(dev->pdev->subsystem_vendor == 0x1734) &&
(dev->pdev->subsystem_device == 0x11bd)) {
if (*connector_type == DRM_MODE_CONNECTOR_VGA) {
@@ -1263,6 +1263,8 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
};
bool radeon_atombios_sideport_present(struct radeon_device *rdev)
@@ -1390,27 +1392,50 @@ static void radeon_atombios_get_igp_ss_overrides(struct radeon_device *rdev,
struct radeon_mode_info *mode_info = &rdev->mode_info;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
u16 data_offset, size;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 *igp_info;
+ union igp_info *igp_info;
u8 frev, crev;
u16 percentage = 0, rate = 0;
/* get any igp specific overrides */
if (atom_parse_data_header(mode_info->atom_context, index, &size,
&frev, &crev, &data_offset)) {
- igp_info = (struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 *)
+ igp_info = (union igp_info *)
(mode_info->atom_context->bios + data_offset);
- switch (id) {
- case ASIC_INTERNAL_SS_ON_TMDS:
- percentage = le16_to_cpu(igp_info->usDVISSPercentage);
- rate = le16_to_cpu(igp_info->usDVISSpreadRateIn10Hz);
+ switch (crev) {
+ case 6:
+ switch (id) {
+ case ASIC_INTERNAL_SS_ON_TMDS:
+ percentage = le16_to_cpu(igp_info->info_6.usDVISSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usDVISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_HDMI:
+ percentage = le16_to_cpu(igp_info->info_6.usHDMISSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usHDMISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_LVDS:
+ percentage = le16_to_cpu(igp_info->info_6.usLvdsSSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usLvdsSSpreadRateIn10Hz);
+ break;
+ }
break;
- case ASIC_INTERNAL_SS_ON_HDMI:
- percentage = le16_to_cpu(igp_info->usHDMISSPercentage);
- rate = le16_to_cpu(igp_info->usHDMISSpreadRateIn10Hz);
+ case 7:
+ switch (id) {
+ case ASIC_INTERNAL_SS_ON_TMDS:
+ percentage = le16_to_cpu(igp_info->info_7.usDVISSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usDVISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_HDMI:
+ percentage = le16_to_cpu(igp_info->info_7.usHDMISSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usHDMISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_LVDS:
+ percentage = le16_to_cpu(igp_info->info_7.usLvdsSSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usLvdsSSpreadRateIn10Hz);
+ break;
+ }
break;
- case ASIC_INTERNAL_SS_ON_LVDS:
- percentage = le16_to_cpu(igp_info->usLvdsSSPercentage);
- rate = le16_to_cpu(igp_info->usLvdsSSpreadRateIn10Hz);
+ default:
+ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
break;
}
if (percentage)
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index 98724fcb0088..2a2cf0b88a28 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -30,57 +30,8 @@ static struct radeon_atpx_priv {
/* handle for device - and atpx */
acpi_handle dhandle;
acpi_handle atpx_handle;
- acpi_handle atrm_handle;
} radeon_atpx_priv;
-/* retrieve the ROM in 4k blocks */
-static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
- int offset, int len)
-{
- acpi_status status;
- union acpi_object atrm_arg_elements[2], *obj;
- struct acpi_object_list atrm_arg;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
-
- atrm_arg.count = 2;
- atrm_arg.pointer = &atrm_arg_elements[0];
-
- atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
- atrm_arg_elements[0].integer.value = offset;
-
- atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
- atrm_arg_elements[1].integer.value = len;
-
- status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
- if (ACPI_FAILURE(status)) {
- printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
- return -ENODEV;
- }
-
- obj = (union acpi_object *)buffer.pointer;
- memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
- len = obj->buffer.length;
- kfree(buffer.pointer);
- return len;
-}
-
-bool radeon_atrm_supported(struct pci_dev *pdev)
-{
- /* get the discrete ROM only via ATRM */
- if (!radeon_atpx_priv.atpx_detected)
- return false;
-
- if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
- return false;
- return true;
-}
-
-
-int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len)
-{
- return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len);
-}
-
static int radeon_atpx_get_version(acpi_handle handle)
{
acpi_status status;
@@ -198,7 +149,7 @@ static int radeon_atpx_power_state(enum vga_switcheroo_client_id id,
static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
{
- acpi_handle dhandle, atpx_handle, atrm_handle;
+ acpi_handle dhandle, atpx_handle;
acpi_status status;
dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
@@ -209,13 +160,8 @@ static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
if (ACPI_FAILURE(status))
return false;
- status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
- if (ACPI_FAILURE(status))
- return false;
-
radeon_atpx_priv.dhandle = dhandle;
radeon_atpx_priv.atpx_handle = atpx_handle;
- radeon_atpx_priv.atrm_handle = atrm_handle;
return true;
}
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 501f4881e5aa..d306cc8fdeaa 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -32,6 +32,7 @@
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
/*
* BIOS.
*/
@@ -98,16 +99,81 @@ static bool radeon_read_bios(struct radeon_device *rdev)
return true;
}
+#ifdef CONFIG_ACPI
/* ATRM is used to get the BIOS on the discrete cards in
* dual-gpu systems.
*/
+/* retrieve the ROM in 4k blocks */
+#define ATRM_BIOS_PAGE 4096
+/**
+ * radeon_atrm_call - fetch a chunk of the vbios
+ *
+ * @atrm_handle: acpi ATRM handle
+ * @bios: vbios image pointer
+ * @offset: offset of vbios image data to fetch
+ * @len: length of vbios image data to fetch
+ *
+ * Executes ATRM to fetch a chunk of the discrete
+ * vbios image on PX systems (all asics).
+ * Returns the length of the buffer fetched.
+ */
+static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
+ int offset, int len)
+{
+ acpi_status status;
+ union acpi_object atrm_arg_elements[2], *obj;
+ struct acpi_object_list atrm_arg;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
+
+ atrm_arg.count = 2;
+ atrm_arg.pointer = &atrm_arg_elements[0];
+
+ atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
+ atrm_arg_elements[0].integer.value = offset;
+
+ atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
+ atrm_arg_elements[1].integer.value = len;
+
+ status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ obj = (union acpi_object *)buffer.pointer;
+ memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
+ len = obj->buffer.length;
+ kfree(buffer.pointer);
+ return len;
+}
+
static bool radeon_atrm_get_bios(struct radeon_device *rdev)
{
int ret;
int size = 256 * 1024;
int i;
+ struct pci_dev *pdev = NULL;
+ acpi_handle dhandle, atrm_handle;
+ acpi_status status;
+ bool found = false;
+
+ /* ATRM is for the discrete card only */
+ if (rdev->flags & RADEON_IS_IGP)
+ return false;
+
+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+ dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+ if (!dhandle)
+ continue;
+
+ status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
+ if (!ACPI_FAILURE(status)) {
+ found = true;
+ break;
+ }
+ }
- if (!radeon_atrm_supported(rdev->pdev))
+ if (!found)
return false;
rdev->bios = kmalloc(size, GFP_KERNEL);
@@ -117,9 +183,10 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev)
}
for (i = 0; i < size / ATRM_BIOS_PAGE; i++) {
- ret = radeon_atrm_get_bios_chunk(rdev->bios,
- (i * ATRM_BIOS_PAGE),
- ATRM_BIOS_PAGE);
+ ret = radeon_atrm_call(atrm_handle,
+ rdev->bios,
+ (i * ATRM_BIOS_PAGE),
+ ATRM_BIOS_PAGE);
if (ret < ATRM_BIOS_PAGE)
break;
}
@@ -130,6 +197,12 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev)
}
return true;
}
+#else
+static inline bool radeon_atrm_get_bios(struct radeon_device *rdev)
+{
+ return false;
+}
+#endif
static bool ni_read_disabled_bios(struct radeon_device *rdev)
{
@@ -476,6 +549,61 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev)
return legacy_read_disabled_bios(rdev);
}
+#ifdef CONFIG_ACPI
+static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
+{
+ bool ret = false;
+ struct acpi_table_header *hdr;
+ acpi_size tbl_size;
+ UEFI_ACPI_VFCT *vfct;
+ GOP_VBIOS_CONTENT *vbios;
+ VFCT_IMAGE_HEADER *vhdr;
+
+ if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size)))
+ return false;
+ if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
+ DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n");
+ goto out_unmap;
+ }
+
+ vfct = (UEFI_ACPI_VFCT *)hdr;
+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) {
+ DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
+ goto out_unmap;
+ }
+
+ vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset);
+ vhdr = &vbios->VbiosHeader;
+ DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n",
+ vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction,
+ vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength);
+
+ if (vhdr->PCIBus != rdev->pdev->bus->number ||
+ vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) ||
+ vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) ||
+ vhdr->VendorID != rdev->pdev->vendor ||
+ vhdr->DeviceID != rdev->pdev->device) {
+ DRM_INFO("ACPI VFCT table is not for this card\n");
+ goto out_unmap;
+ };
+
+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) {
+ DRM_ERROR("ACPI VFCT image truncated\n");
+ goto out_unmap;
+ }
+
+ rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL);
+ ret = !!rdev->bios;
+
+out_unmap:
+ return ret;
+}
+#else
+static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
+{
+ return false;
+}
+#endif
bool radeon_get_bios(struct radeon_device *rdev)
{
@@ -484,6 +612,8 @@ bool radeon_get_bios(struct radeon_device *rdev)
r = radeon_atrm_get_bios(rdev);
if (r == false)
+ r = radeon_acpi_vfct_bios(rdev);
+ if (r == false)
r = igp_read_bios_from_vram(rdev);
if (r == false)
r = radeon_read_bios(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 576f4f6919f2..f75247d42ffd 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -719,6 +719,34 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde
return i2c;
}
+static struct radeon_i2c_bus_rec radeon_combios_get_i2c_info_from_table(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct radeon_i2c_bus_rec i2c;
+ u16 offset;
+ u8 id, blocks, clk, data;
+ int i;
+
+ i2c.valid = false;
+
+ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
+ if (offset) {
+ blocks = RBIOS8(offset + 2);
+ for (i = 0; i < blocks; i++) {
+ id = RBIOS8(offset + 3 + (i * 5) + 0);
+ if (id == 136) {
+ clk = RBIOS8(offset + 3 + (i * 5) + 3);
+ data = RBIOS8(offset + 3 + (i * 5) + 4);
+ /* gpiopad */
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
+ (1 << clk), (1 << data));
+ break;
+ }
+ }
+ }
+ return i2c;
+}
+
void radeon_combios_i2c_init(struct radeon_device *rdev)
{
struct drm_device *dev = rdev->ddev;
@@ -755,30 +783,14 @@ void radeon_combios_i2c_init(struct radeon_device *rdev)
} else if (rdev->family == CHIP_RS300 ||
rdev->family == CHIP_RS400 ||
rdev->family == CHIP_RS480) {
- u16 offset;
- u8 id, blocks, clk, data;
- int i;
-
/* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
- offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
- if (offset) {
- blocks = RBIOS8(offset + 2);
- for (i = 0; i < blocks; i++) {
- id = RBIOS8(offset + 3 + (i * 5) + 0);
- if (id == 136) {
- clk = RBIOS8(offset + 3 + (i * 5) + 3);
- data = RBIOS8(offset + 3 + (i * 5) + 4);
- /* gpiopad */
- i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
- (1 << clk), (1 << data));
- rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
- break;
- }
- }
- }
+ /* gpiopad */
+ i2c = radeon_combios_get_i2c_info_from_table(rdev);
+ if (i2c.valid)
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
} else if ((rdev->family == CHIP_R200) ||
(rdev->family >= CHIP_R300)) {
/* 0x68 */
@@ -2321,7 +2333,10 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
connector = (tmp >> 12) & 0xf;
ddc_type = (tmp >> 8) & 0xf;
- ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
+ if (ddc_type == 5)
+ ddc_i2c = radeon_combios_get_i2c_info_from_table(rdev);
+ else
+ ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
switch (connector) {
case CONNECTOR_PROPRIETARY_LEGACY:
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 8a4c49ef0cc4..b4a0db24f4dd 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -278,6 +278,30 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
return 0;
}
+static void radeon_bo_vm_fence_va(struct radeon_cs_parser *parser,
+ struct radeon_fence *fence)
+{
+ struct radeon_fpriv *fpriv = parser->filp->driver_priv;
+ struct radeon_vm *vm = &fpriv->vm;
+ struct radeon_bo_list *lobj;
+
+ if (parser->chunk_ib_idx == -1) {
+ return;
+ }
+ if ((parser->cs_flags & RADEON_CS_USE_VM) == 0) {
+ return;
+ }
+
+ list_for_each_entry(lobj, &parser->validated, tv.head) {
+ struct radeon_bo_va *bo_va;
+ struct radeon_bo *rbo = lobj->bo;
+
+ bo_va = radeon_bo_va(rbo, vm);
+ radeon_fence_unref(&bo_va->fence);
+ bo_va->fence = radeon_fence_ref(fence);
+ }
+}
+
/**
* cs_parser_fini() - clean parser states
* @parser: parser structure holding parsing context.
@@ -290,11 +314,14 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
{
unsigned i;
- if (!error)
+ if (!error) {
+ /* fence all bo va before ttm_eu_fence_buffer_objects so bo are still reserved */
+ radeon_bo_vm_fence_va(parser, parser->ib.fence);
ttm_eu_fence_buffer_objects(&parser->validated,
parser->ib.fence);
- else
+ } else {
ttm_eu_backoff_reservation(&parser->validated);
+ }
if (parser->relocs != NULL) {
for (i = 0; i < parser->nrelocs; i++) {
@@ -388,7 +415,6 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
if (parser->chunk_ib_idx == -1)
return 0;
-
if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 711e95ad39bf..8794744cdf1a 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -67,7 +67,8 @@ static void radeon_hide_cursor(struct drm_crtc *crtc)
if (ASIC_IS_DCE4(rdev)) {
WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
- WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+ WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+ EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
} else if (ASIC_IS_AVIVO(rdev)) {
WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
@@ -94,7 +95,8 @@ static void radeon_show_cursor(struct drm_crtc *crtc)
if (ASIC_IS_DCE4(rdev)) {
WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN |
- EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+ EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+ EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
} else if (ASIC_IS_AVIVO(rdev)) {
WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN |
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 742af8244e89..7a3daebd732d 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1009,6 +1009,7 @@ int radeon_device_init(struct radeon_device *rdev,
atomic_set(&rdev->ih.lock, 0);
mutex_init(&rdev->gem.mutex);
mutex_init(&rdev->pm.mutex);
+ mutex_init(&rdev->gpu_clock_mutex);
init_rwsem(&rdev->pm.mclk_lock);
init_rwsem(&rdev->exclusive_lock);
init_waitqueue_head(&rdev->irq.vblank_queue);
@@ -1050,7 +1051,7 @@ int radeon_device_init(struct radeon_device *rdev,
if (rdev->flags & RADEON_IS_AGP)
rdev->need_dma32 = true;
if ((rdev->flags & RADEON_IS_PCI) &&
- (rdev->family < CHIP_RS400))
+ (rdev->family <= CHIP_RS740))
rdev->need_dma32 = true;
dma_bits = rdev->need_dma32 ? 32 : 40;
@@ -1345,12 +1346,15 @@ retry:
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
radeon_ring_restore(rdev, &rdev->ring[i],
ring_sizes[i], ring_data[i]);
+ ring_sizes[i] = 0;
+ ring_data[i] = NULL;
}
r = radeon_ib_ring_tests(rdev);
if (r) {
dev_err(rdev->dev, "ib ring test failed (%d).\n", r);
if (saved) {
+ saved = false;
radeon_suspend(rdev);
goto retry;
}
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index dcea6f01ae4e..8c593ea82c41 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -59,9 +59,14 @@
* 2.15.0 - add max_pipes query
* 2.16.0 - fix evergreen 2D tiled surface calculation
* 2.17.0 - add STRMOUT_BASE_UPDATE for r7xx
+ * 2.18.0 - r600-eg: allow "invalid" DB formats
+ * 2.19.0 - r600-eg: MSAA textures
+ * 2.20.0 - r600-si: RADEON_INFO_TIMESTAMP query
+ * 2.21.0 - r600-r700: FMASK and CMASK
+ * 2.22.0 - r600 only: RESOLVE_BOX allowed
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 17
+#define KMS_DRIVER_MINOR 22
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 7b737b9339ad..2a59375dbe52 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -131,7 +131,7 @@ int radeon_fence_emit(struct radeon_device *rdev,
*/
void radeon_fence_process(struct radeon_device *rdev, int ring)
{
- uint64_t seq, last_seq;
+ uint64_t seq, last_seq, last_emitted;
unsigned count_loop = 0;
bool wake = false;
@@ -158,13 +158,15 @@ void radeon_fence_process(struct radeon_device *rdev, int ring)
*/
last_seq = atomic64_read(&rdev->fence_drv[ring].last_seq);
do {
+ last_emitted = rdev->fence_drv[ring].sync_seq[ring];
seq = radeon_fence_read(rdev, ring);
seq |= last_seq & 0xffffffff00000000LL;
if (seq < last_seq) {
- seq += 0x100000000LL;
+ seq &= 0xffffffff;
+ seq |= last_emitted & 0xffffffff00000000LL;
}
- if (seq == last_seq) {
+ if (seq <= last_seq || seq > last_emitted) {
break;
}
/* If we loop over we don't want to return without
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index b3720054614d..bb3b7fe05ccd 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -814,7 +814,7 @@ int radeon_vm_bo_update_pte(struct radeon_device *rdev,
return -EINVAL;
}
- if (bo_va->valid)
+ if (bo_va->valid && mem)
return 0;
ngpu_pages = radeon_bo_ngpu_pages(bo);
@@ -859,11 +859,27 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev,
struct radeon_bo *bo)
{
struct radeon_bo_va *bo_va;
+ int r;
bo_va = radeon_bo_va(bo, vm);
if (bo_va == NULL)
return 0;
+ /* wait for va use to end */
+ while (bo_va->fence) {
+ r = radeon_fence_wait(bo_va->fence, false);
+ if (r) {
+ DRM_ERROR("error while waiting for fence: %d\n", r);
+ }
+ if (r == -EDEADLK) {
+ r = radeon_gpu_reset(rdev);
+ if (!r)
+ continue;
+ }
+ break;
+ }
+ radeon_fence_unref(&bo_va->fence);
+
mutex_lock(&rdev->vm_manager.lock);
mutex_lock(&vm->mutex);
radeon_vm_bo_update_pte(rdev, vm, bo, NULL);
@@ -934,7 +950,7 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm)
}
/**
- * radeon_vm_init - tear down a vm instance
+ * radeon_vm_fini - tear down a vm instance
*
* @rdev: radeon_device pointer
* @vm: requested vm
@@ -952,12 +968,15 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
radeon_vm_unbind_locked(rdev, vm);
mutex_unlock(&rdev->vm_manager.lock);
- /* remove all bo */
+ /* remove all bo at this point non are busy any more because unbind
+ * waited for the last vm fence to signal
+ */
r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false);
if (!r) {
bo_va = radeon_bo_va(rdev->ring_tmp_bo.bo, vm);
list_del_init(&bo_va->bo_list);
list_del_init(&bo_va->vm_list);
+ radeon_fence_unref(&bo_va->fence);
radeon_bo_unreserve(rdev->ring_tmp_bo.bo);
kfree(bo_va);
}
@@ -969,6 +988,7 @@ void radeon_vm_fini(struct radeon_device *rdev, struct radeon_vm *vm)
r = radeon_bo_reserve(bo_va->bo, false);
if (!r) {
list_del_init(&bo_va->bo_list);
+ radeon_fence_unref(&bo_va->fence);
radeon_bo_unreserve(bo_va->bo);
kfree(bo_va);
}
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 84d045245739..1b57b0058ad6 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -134,25 +134,16 @@ void radeon_gem_object_close(struct drm_gem_object *obj,
struct radeon_device *rdev = rbo->rdev;
struct radeon_fpriv *fpriv = file_priv->driver_priv;
struct radeon_vm *vm = &fpriv->vm;
- struct radeon_bo_va *bo_va, *tmp;
if (rdev->family < CHIP_CAYMAN) {
return;
}
if (radeon_bo_reserve(rbo, false)) {
+ dev_err(rdev->dev, "leaking bo va because we fail to reserve bo\n");
return;
}
- list_for_each_entry_safe(bo_va, tmp, &rbo->va, bo_list) {
- if (bo_va->vm == vm) {
- /* remove from this vm address space */
- mutex_lock(&vm->mutex);
- list_del(&bo_va->vm_list);
- mutex_unlock(&vm->mutex);
- list_del(&bo_va->bo_list);
- kfree(bo_va);
- }
- }
+ radeon_vm_bo_rmv(rdev, vm, rbo);
radeon_bo_unreserve(rbo);
}
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 1d73f16b5d97..414b4acf6947 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -29,6 +29,7 @@
#include "drm_sarea.h"
#include "radeon.h"
#include "radeon_drm.h"
+#include "radeon_asic.h"
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
@@ -167,17 +168,39 @@ static void radeon_set_filp_rights(struct drm_device *dev,
int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct radeon_device *rdev = dev->dev_private;
- struct drm_radeon_info *info;
+ struct drm_radeon_info *info = data;
struct radeon_mode_info *minfo = &rdev->mode_info;
- uint32_t *value_ptr;
- uint32_t value;
+ uint32_t value, *value_ptr;
+ uint64_t value64, *value_ptr64;
struct drm_crtc *crtc;
int i, found;
- info = data;
+ /* TIMESTAMP is a 64-bit value, needs special handling. */
+ if (info->request == RADEON_INFO_TIMESTAMP) {
+ if (rdev->family >= CHIP_R600) {
+ value_ptr64 = (uint64_t*)((unsigned long)info->value);
+ if (rdev->family >= CHIP_TAHITI) {
+ value64 = si_get_gpu_clock(rdev);
+ } else {
+ value64 = r600_get_gpu_clock(rdev);
+ }
+
+ if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) {
+ DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ return 0;
+ } else {
+ DRM_DEBUG_KMS("timestamp is r6xx+ only!\n");
+ return -EINVAL;
+ }
+ }
+
value_ptr = (uint32_t *)((unsigned long)info->value);
- if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value)))
+ if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) {
+ DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__);
return -EFAULT;
+ }
switch (info->request) {
case RADEON_INFO_DEVICE_ID:
@@ -337,7 +360,7 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) {
- DRM_ERROR("copy_to_user\n");
+ DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__);
return -EFAULT;
}
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index d5fd615897ec..94b4a1c12893 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -1025,9 +1025,11 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
static void radeon_crtc_prepare(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct drm_crtc *crtci;
+ radeon_crtc->in_mode_set = true;
/*
* The hardware wedges sometimes if you reconfigure one CRTC
* whilst another is running (see fdo bug #24611).
@@ -1038,6 +1040,7 @@ static void radeon_crtc_prepare(struct drm_crtc *crtc)
static void radeon_crtc_commit(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct drm_crtc *crtci;
@@ -1048,6 +1051,7 @@ static void radeon_crtc_commit(struct drm_crtc *crtc)
if (crtci->enabled)
radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
}
+ radeon_crtc->in_mode_set = false;
}
static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index f380d59c5763..d56978949f34 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -275,6 +275,7 @@ struct radeon_crtc {
u16 lut_r[256], lut_g[256], lut_b[256];
bool enabled;
bool can_tile;
+ bool in_mode_set;
uint32_t crtc_offset;
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 1f1a4c803c1d..9024e7222839 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -52,11 +52,7 @@ void radeon_bo_clear_va(struct radeon_bo *bo)
list_for_each_entry_safe(bo_va, tmp, &bo->va, bo_list) {
/* remove from all vm address space */
- mutex_lock(&bo_va->vm->mutex);
- list_del(&bo_va->vm_list);
- mutex_unlock(&bo_va->vm->mutex);
- list_del(&bo_va->bo_list);
- kfree(bo_va);
+ radeon_vm_bo_rmv(bo->rdev, bo_va->vm, bo);
}
}
@@ -136,6 +132,7 @@ int radeon_bo_create(struct radeon_device *rdev,
acc_size = ttm_bo_dma_acc_size(&rdev->mman.bdev, size,
sizeof(struct radeon_bo));
+retry:
bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
if (bo == NULL)
return -ENOMEM;
@@ -149,8 +146,6 @@ int radeon_bo_create(struct radeon_device *rdev,
bo->surface_reg = -1;
INIT_LIST_HEAD(&bo->list);
INIT_LIST_HEAD(&bo->va);
-
-retry:
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
down_read(&rdev->pm.mclk_lock);
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index ec79b3750430..43c431a2686d 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -706,6 +706,7 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig
if (radeon_debugfs_ring_init(rdev, ring)) {
DRM_ERROR("Failed to register debugfs file for rings !\n");
}
+ radeon_ring_lockup_update(ring);
return 0;
}
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index 5e659b034d9a..20bfbda7b3f1 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -744,15 +744,6 @@ r600 0x9400
0x00028C38 CB_CLRCMP_DST
0x00028C3C CB_CLRCMP_MSK
0x00028C34 CB_CLRCMP_SRC
-0x00028100 CB_COLOR0_MASK
-0x00028104 CB_COLOR1_MASK
-0x00028108 CB_COLOR2_MASK
-0x0002810C CB_COLOR3_MASK
-0x00028110 CB_COLOR4_MASK
-0x00028114 CB_COLOR5_MASK
-0x00028118 CB_COLOR6_MASK
-0x0002811C CB_COLOR7_MASK
-0x00028808 CB_COLOR_CONTROL
0x0002842C CB_FOG_BLUE
0x00028428 CB_FOG_GREEN
0x00028424 CB_FOG_RED
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index a12fbcc8ccb6..aa8ef491ef3c 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -281,12 +281,8 @@ int rv515_debugfs_ga_info_init(struct radeon_device *rdev)
void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save)
{
- save->d1vga_control = RREG32(R_000330_D1VGA_CONTROL);
- save->d2vga_control = RREG32(R_000338_D2VGA_CONTROL);
save->vga_render_control = RREG32(R_000300_VGA_RENDER_CONTROL);
save->vga_hdp_control = RREG32(R_000328_VGA_HDP_CONTROL);
- save->d1crtc_control = RREG32(R_006080_D1CRTC_CONTROL);
- save->d2crtc_control = RREG32(R_006880_D2CRTC_CONTROL);
/* Stop all video */
WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0);
@@ -311,15 +307,6 @@ void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save)
/* Unlock host access */
WREG32(R_000328_VGA_HDP_CONTROL, save->vga_hdp_control);
mdelay(1);
- /* Restore video state */
- WREG32(R_000330_D1VGA_CONTROL, save->d1vga_control);
- WREG32(R_000338_D2VGA_CONTROL, save->d2vga_control);
- WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 1);
- WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 1);
- WREG32(R_006080_D1CRTC_CONTROL, save->d1crtc_control);
- WREG32(R_006880_D2CRTC_CONTROL, save->d2crtc_control);
- WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 0);
- WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0);
WREG32(R_000300_VGA_RENDER_CONTROL, save->vga_render_control);
}
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index c053f8193771..0139e227e3c7 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -1639,11 +1639,19 @@ static void si_gpu_init(struct radeon_device *rdev)
/* XXX what about 12? */
rdev->config.si.tile_config |= (3 << 0);
break;
- }
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.si.tile_config |= 1 << 4;
- else
+ }
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.si.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.si.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.si.tile_config |= 2 << 4;
+ break;
+ }
rdev->config.si.tile_config |=
((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
rdev->config.si.tile_config |=
@@ -3960,3 +3968,22 @@ void si_fini(struct radeon_device *rdev)
rdev->bios = NULL;
}
+/**
+ * si_get_gpu_clock - return GPU clock counter snapshot
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Fetches a GPU clock counter snapshot (SI).
+ * Returns the 64 bit clock counter snapshot.
+ */
+uint64_t si_get_gpu_clock(struct radeon_device *rdev)
+{
+ uint64_t clock;
+
+ mutex_lock(&rdev->gpu_clock_mutex);
+ WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1);
+ clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) |
+ ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL);
+ mutex_unlock(&rdev->gpu_clock_mutex);
+ return clock;
+}
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 7869089e8761..ef4815c27b1c 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -698,6 +698,9 @@
#define RLC_UCODE_ADDR 0xC32C
#define RLC_UCODE_DATA 0xC330
+#define RLC_GPU_CLOCK_COUNT_LSB 0xC338
+#define RLC_GPU_CLOCK_COUNT_MSB 0xC33C
+#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC340
#define RLC_MC_CNTL 0xC344
#define RLC_UCODE_CNTL 0xC348
diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
index d31d4cca9a4c..c5a164337bd5 100644
--- a/drivers/gpu/drm/savage/savage_drv.c
+++ b/drivers/gpu/drm/savage/savage_drv.c
@@ -43,6 +43,9 @@ static const struct file_operations savage_driver_fops = {
.mmap = drm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 7f119870147c..867dc03000e6 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -74,6 +74,9 @@ static const struct file_operations sis_driver_fops = {
.mmap = drm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
index 90f6b13acfac..a7f4d6bd1330 100644
--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
+++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
@@ -49,6 +49,9 @@ static const struct file_operations tdfx_driver_fops = {
.mmap = drm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig
index 0b5e096d39a6..56e0bf31d425 100644
--- a/drivers/gpu/drm/udl/Kconfig
+++ b/drivers/gpu/drm/udl/Kconfig
@@ -1,6 +1,7 @@
config DRM_UDL
tristate "DisplayLink"
depends on DRM && EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
select DRM_USB
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index ba055e9ca007..8d9dc44f1f94 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -69,6 +69,13 @@ static int udl_get_modes(struct drm_connector *connector)
static int udl_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
+ struct udl_device *udl = connector->dev->dev_private;
+ if (!udl->sku_pixel_limit)
+ return 0;
+
+ if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit)
+ return MODE_VIRTUAL_Y;
+
return 0;
}
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 6e52069894b3..9f84128505bb 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -66,6 +66,9 @@ static const struct file_operations udl_driver_fops = {
.unlocked_ioctl = drm_ioctl,
.release = drm_release,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index 7bd65bdd15a8..291ecc145585 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -308,7 +308,7 @@ struct drm_gem_object *udl_gem_prime_import(struct drm_device *dev,
/* need to attach */
attach = dma_buf_attach(dma_buf, dev->dev);
if (IS_ERR(attach))
- return ERR_PTR(PTR_ERR(attach));
+ return ERR_CAST(attach);
sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR(sg)) {
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index f5dd89e891de..9159d48d1dfd 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -354,8 +354,7 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc,
static void udl_crtc_disable(struct drm_crtc *crtc)
{
-
-
+ udl_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void udl_crtc_destroy(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index e927b4c052f5..af1b914b17e3 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -65,6 +65,9 @@ static const struct file_operations via_driver_fops = {
.mmap = drm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.llseek = noop_llseek,
};
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
index 794ff67c5701..b71bcd0bfbbf 100644
--- a/drivers/gpu/drm/vmwgfx/Kconfig
+++ b/drivers/gpu/drm/vmwgfx/Kconfig
@@ -12,3 +12,11 @@ config DRM_VMWGFX
This is a KMS enabled DRM driver for the VMware SVGA2
virtual hardware.
The compiled module will be called "vmwgfx.ko".
+
+config DRM_VMWGFX_FBCON
+ depends on DRM_VMWGFX
+ bool "Enable framebuffer console under vmwgfx by default"
+ help
+ Choose this option if you are shipping a new vmwgfx
+ userspace driver that supports using the kernel driver.
+
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 4d9edead01ac..ba2c35dbf10e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -182,8 +182,9 @@ static struct pci_device_id vmw_pci_id_list[] = {
{0x15ad, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VMWGFX_CHIP_SVGAII},
{0, 0, 0}
};
+MODULE_DEVICE_TABLE(pci, vmw_pci_id_list);
-static int enable_fbdev;
+static int enable_fbdev = IS_ENABLED(CONFIG_DRM_VMWGFX_FBCON);
static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
static void vmw_master_init(struct vmw_master *);
@@ -1154,6 +1155,11 @@ static struct drm_driver driver = {
.open = vmw_driver_open,
.preclose = vmw_preclose,
.postclose = vmw_postclose,
+
+ .dumb_create = vmw_dumb_create,
+ .dumb_map_offset = vmw_dumb_map_offset,
+ .dumb_destroy = vmw_dumb_destroy,
+
.fops = &vmwgfx_driver_fops,
.name = VMWGFX_DRIVER_NAME,
.desc = VMWGFX_DRIVER_DESC,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index d0f2c079ee27..29c984ff7f23 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -645,6 +645,16 @@ int vmw_kms_readback(struct vmw_private *dev_priv,
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+int vmw_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+
+int vmw_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset);
+int vmw_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle);
/**
* Overlay control - vmwgfx_overlay.c
*/
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index f2fb8f15e2f1..7e0743358dff 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -1018,7 +1018,7 @@ int vmw_event_fence_action_create(struct drm_file *file_priv,
}
- event = kzalloc(sizeof(event->event), GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
if (unlikely(event == NULL)) {
DRM_ERROR("Failed to allocate an event.\n");
ret = -ENOMEM;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 6b0078ffa763..c50724bd30f6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1688,15 +1688,19 @@ int vmw_du_page_flip(struct drm_crtc *crtc,
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct drm_framebuffer *old_fb = crtc->fb;
struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb);
- struct drm_file *file_priv = event->base.file_priv;
+ struct drm_file *file_priv ;
struct vmw_fence_obj *fence = NULL;
struct drm_clip_rect clips;
int ret;
+ if (event == NULL)
+ return -EINVAL;
+
/* require ScreenObject support for page flipping */
if (!dev_priv->sou_priv)
return -ENOSYS;
+ file_priv = event->base.file_priv;
if (!vmw_kms_screen_object_flippable(dev_priv, crtc))
return -EINVAL;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 22bf9a21ec71..2c6ffe0e2c07 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -1917,3 +1917,76 @@ err_ref:
vmw_resource_unreference(&res);
return ret;
}
+
+
+int vmw_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct vmw_master *vmaster = vmw_master(file_priv->master);
+ struct vmw_user_dma_buffer *vmw_user_bo;
+ struct ttm_buffer_object *tmp;
+ int ret;
+
+ args->pitch = args->width * ((args->bpp + 7) / 8);
+ args->size = args->pitch * args->height;
+
+ vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL);
+ if (vmw_user_bo == NULL)
+ return -ENOMEM;
+
+ ret = ttm_read_lock(&vmaster->lock, true);
+ if (ret != 0) {
+ kfree(vmw_user_bo);
+ return ret;
+ }
+
+ ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, args->size,
+ &vmw_vram_sys_placement, true,
+ &vmw_user_dmabuf_destroy);
+ if (ret != 0)
+ goto out_no_dmabuf;
+
+ tmp = ttm_bo_reference(&vmw_user_bo->dma.base);
+ ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile,
+ &vmw_user_bo->base,
+ false,
+ ttm_buffer_type,
+ &vmw_user_dmabuf_release, NULL);
+ if (unlikely(ret != 0))
+ goto out_no_base_object;
+
+ args->handle = vmw_user_bo->base.hash.key;
+
+out_no_base_object:
+ ttm_bo_unref(&tmp);
+out_no_dmabuf:
+ ttm_read_unlock(&vmaster->lock);
+ return ret;
+}
+
+int vmw_dumb_map_offset(struct drm_file *file_priv,
+ struct drm_device *dev, uint32_t handle,
+ uint64_t *offset)
+{
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_dma_buffer *out_buf;
+ int ret;
+
+ ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf);
+ if (ret != 0)
+ return -EINVAL;
+
+ *offset = out_buf->base.addr_space_offset;
+ vmw_dmabuf_unreference(&out_buf);
+ return 0;
+}
+
+int vmw_dumb_destroy(struct drm_file *file_priv,
+ struct drm_device *dev,
+ uint32_t handle)
+{
+ return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
+ handle, TTM_REF_USAGE);
+}
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 5b3c7d135dc9..e25cf31faab2 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -70,27 +70,12 @@ static struct vgasr_priv vgasr_priv = {
.clients = LIST_HEAD_INIT(vgasr_priv.clients),
};
-int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
-{
- mutex_lock(&vgasr_mutex);
- if (vgasr_priv.handler) {
- mutex_unlock(&vgasr_mutex);
- return -EINVAL;
- }
-
- vgasr_priv.handler = handler;
- mutex_unlock(&vgasr_mutex);
- return 0;
-}
-EXPORT_SYMBOL(vga_switcheroo_register_handler);
-
-void vga_switcheroo_unregister_handler(void)
+static bool vga_switcheroo_ready(void)
{
- mutex_lock(&vgasr_mutex);
- vgasr_priv.handler = NULL;
- mutex_unlock(&vgasr_mutex);
+ /* we're ready if we get two clients + handler */
+ return !vgasr_priv.active &&
+ vgasr_priv.registered_clients == 2 && vgasr_priv.handler;
}
-EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
static void vga_switcheroo_enable(void)
{
@@ -98,7 +83,8 @@ static void vga_switcheroo_enable(void)
struct vga_switcheroo_client *client;
/* call the handler to init */
- vgasr_priv.handler->init();
+ if (vgasr_priv.handler->init)
+ vgasr_priv.handler->init();
list_for_each_entry(client, &vgasr_priv.clients, list) {
if (client->id != -1)
@@ -113,6 +99,37 @@ static void vga_switcheroo_enable(void)
vgasr_priv.active = true;
}
+int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
+{
+ mutex_lock(&vgasr_mutex);
+ if (vgasr_priv.handler) {
+ mutex_unlock(&vgasr_mutex);
+ return -EINVAL;
+ }
+
+ vgasr_priv.handler = handler;
+ if (vga_switcheroo_ready()) {
+ printk(KERN_INFO "vga_switcheroo: enabled\n");
+ vga_switcheroo_enable();
+ }
+ mutex_unlock(&vgasr_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(vga_switcheroo_register_handler);
+
+void vga_switcheroo_unregister_handler(void)
+{
+ mutex_lock(&vgasr_mutex);
+ vgasr_priv.handler = NULL;
+ if (vgasr_priv.active) {
+ pr_info("vga_switcheroo: disabled\n");
+ vga_switcheroo_debugfs_fini(&vgasr_priv);
+ vgasr_priv.active = false;
+ }
+ mutex_unlock(&vgasr_mutex);
+}
+EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
+
static int register_client(struct pci_dev *pdev,
const struct vga_switcheroo_client_ops *ops,
int id, bool active)
@@ -134,9 +151,7 @@ static int register_client(struct pci_dev *pdev,
if (client_is_vga(client))
vgasr_priv.registered_clients++;
- /* if we get two clients + handler */
- if (!vgasr_priv.active &&
- vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
+ if (vga_switcheroo_ready()) {
printk(KERN_INFO "vga_switcheroo: enabled\n");
vga_switcheroo_enable();
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 60ea284407ce..8bcd168fffae 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -996,7 +996,8 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
struct hid_driver *hdrv = hid->driver;
int ret;
- hid_dump_input(hid, usage, value);
+ if (!list_empty(&hid->debug_list))
+ hid_dump_input(hid, usage, value);
if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
ret = hdrv->event(hid, field, usage, value);
@@ -1558,7 +1559,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
- { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
+#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD)
+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
@@ -1624,7 +1627,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
- { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c
index 77d2df04c97b..60c4e1e85913 100644
--- a/drivers/hid/hid-lenovo-tpkbd.c
+++ b/drivers/hid/hid-lenovo-tpkbd.c
@@ -519,6 +519,8 @@ static void tpkbd_remove_tp(struct hid_device *hdev)
led_classdev_unregister(&data_pointer->led_mute);
hid_set_drvdata(hdev, NULL);
+ kfree(data_pointer->led_micmute.name);
+ kfree(data_pointer->led_mute.name);
kfree(data_pointer);
}
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 0f9c146fc00d..9500f2f3f8fe 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -193,6 +193,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
size_t count,
unsigned char report_type);
+static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
@@ -233,6 +234,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
SPFUNCTION_DEVICE_LIST_EMPTY) {
dbg_hid("%s: device list is empty\n", __func__);
+ djrcv_dev->querying_devices = false;
return;
}
@@ -243,6 +245,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
return;
}
+ if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+ /* The device is already known. No need to reallocate it. */
+ dbg_hid("%s: device is already known\n", __func__);
+ return;
+ }
+
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -306,6 +314,7 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_report dj_report;
unsigned long flags;
int count;
+ int retval;
dbg_hid("%s\n", __func__);
@@ -338,6 +347,25 @@ static void delayedwork_callback(struct work_struct *work)
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
break;
default:
+ /* A normal report (i. e. not belonging to a pair/unpair notification)
+ * arriving here, means that the report arrived but we did not have a
+ * paired dj_device associated to the report's device_index, this
+ * means that the original "device paired" notification corresponding
+ * to this dj_device never arrived to this driver. The reason is that
+ * hid-core discards all packets coming from a device while probe() is
+ * executing. */
+ if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
+ /* ok, we don't know the device, just re-ask the
+ * receiver for the list of connected devices. */
+ retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+ if (!retval) {
+ /* everything went fine, so just leave */
+ break;
+ }
+ dev_err(&djrcv_dev->hdev->dev,
+ "%s:logi_dj_recv_query_paired_devices "
+ "error:%d\n", __func__, retval);
+ }
dbg_hid("%s: unexpected report type\n", __func__);
}
}
@@ -368,6 +396,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
if (!djdev) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -398,6 +432,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
if (dj_device == NULL) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -439,7 +479,11 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
struct dj_report *dj_report;
int retval;
- dj_report = kzalloc(sizeof(dj_report), GFP_KERNEL);
+ /* no need to protect djrcv_dev->querying_devices */
+ if (djrcv_dev->querying_devices)
+ return 0;
+
+ dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
dj_report->report_id = REPORT_ID_DJ_SHORT;
@@ -450,13 +494,14 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
return retval;
}
+
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
struct dj_report *dj_report;
int retval;
- dj_report = kzalloc(sizeof(dj_report), GFP_KERNEL);
+ dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
dj_report->report_id = REPORT_ID_DJ_SHORT;
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index fd28a5e0ca3b..4a4000340ce1 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
+ bool querying_devices;
};
struct dj_device {
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 903eef3d3e10..991e85c7325c 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -70,6 +70,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index a220e5746d67..4748086eaaf2 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -545,8 +545,7 @@ static int vmbus_bus_init(int irq)
if (ret)
goto err_cleanup;
- ret = request_irq(irq, vmbus_isr, IRQF_SAMPLE_RANDOM,
- driver_name, hv_acpi_dev);
+ ret = request_irq(irq, vmbus_isr, 0, driver_name, hv_acpi_dev);
if (ret != 0) {
pr_err("Unable to request IRQ %d\n",
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index cfec802cf9ca..f915eb1c29f7 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -87,10 +87,18 @@ static ssize_t ad7314_show_temperature(struct device *dev,
}
}
+static ssize_t ad7314_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
ad7314_show_temperature, NULL, 0);
static struct attribute *ad7314_attributes[] = {
+ &dev_attr_name.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index e65c6e45d36b..7bf4ce3d405e 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -139,6 +139,12 @@ static ssize_t show_voltage(struct device *dev,
}
}
+static ssize_t ads7871_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2);
@@ -148,6 +154,8 @@ static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7);
+static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL);
+
static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
@@ -157,6 +165,7 @@ static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
+ &dev_attr_name.attr,
NULL
};
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 282708860517..8f3f6f2c45fd 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -53,10 +53,10 @@
#define APPLESMC_MAX_DATA_LENGTH 32
-/* wait up to 32 ms for a status change. */
+/* wait up to 128 ms for a status change. */
#define APPLESMC_MIN_WAIT 0x0010
#define APPLESMC_RETRY_WAIT 0x0100
-#define APPLESMC_MAX_WAIT 0x8000
+#define APPLESMC_MAX_WAIT 0x20000
#define APPLESMC_READ_CMD 0x10
#define APPLESMC_WRITE_CMD 0x11
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index 351d1f4593e7..4ee578948723 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -34,6 +34,12 @@ static const struct dmi_system_id __initconst atk_force_new_if[] = {
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "SABERTOOTH X58")
}
+ }, {
+ /* Old interface reads the same sensor for fan0 and fan1 */
+ .ident = "Asus M5A78L",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "M5A78L")
+ }
},
{ }
};
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index faa16f80db9c..984a3f13923b 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -196,7 +196,7 @@ struct tjmax {
int tjmax;
};
-static struct tjmax __cpuinitconst tjmax_table[] = {
+static const struct tjmax __cpuinitconst tjmax_table[] = {
{ "CPU D410", 100000 },
{ "CPU D425", 100000 },
{ "CPU D510", 100000 },
@@ -815,17 +815,20 @@ static int __init coretemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i)
get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&coretemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -840,6 +843,7 @@ static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -848,6 +852,7 @@ static void __exit coretemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&coretemp_driver);
}
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 2764b78a784b..af69073b3fe8 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -129,12 +129,12 @@ static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
* counter saturations resulting in bogus power readings.
* We correct this value ourselves to cope with older BIOSes.
*/
-static DEFINE_PCI_DEVICE_TABLE(affected_device) = {
+static const struct pci_device_id affected_device[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ 0 }
};
-static void __devinit tweak_runavg_range(struct pci_dev *pdev)
+static void tweak_runavg_range(struct pci_dev *pdev)
{
u32 val;
@@ -158,6 +158,16 @@ static void __devinit tweak_runavg_range(struct pci_dev *pdev)
REG_TDP_RUNNING_AVERAGE, val);
}
+#ifdef CONFIG_PM
+static int fam15h_power_resume(struct pci_dev *pdev)
+{
+ tweak_runavg_range(pdev);
+ return 0;
+}
+#else
+#define fam15h_power_resume NULL
+#endif
+
static void __devinit fam15h_power_init_data(struct pci_dev *f4,
struct fam15h_power_data *data)
{
@@ -256,6 +266,7 @@ static struct pci_driver fam15h_power_driver = {
.id_table = fam15h_power_id_table,
.probe = fam15h_power_probe,
.remove = __devexit_p(fam15h_power_remove),
+ .resume = fam15h_power_resume,
};
module_pci_driver(fam15h_power_driver);
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 7f3f4a385729..602148299f68 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -69,22 +69,6 @@ struct ina2xx_data {
u16 regs[INA2XX_MAX_REGISTERS];
};
-int ina2xx_read_word(struct i2c_client *client, int reg)
-{
- int val = i2c_smbus_read_word_data(client, reg);
- if (unlikely(val < 0)) {
- dev_dbg(&client->dev,
- "Failed to read register: %d\n", reg);
- return val;
- }
- return be16_to_cpu(val);
-}
-
-void ina2xx_write_word(struct i2c_client *client, int reg, int data)
-{
- i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
-}
-
static struct ina2xx_data *ina2xx_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -102,7 +86,7 @@ static struct ina2xx_data *ina2xx_update_device(struct device *dev)
/* Read all registers */
for (i = 0; i < data->registers; i++) {
- int rv = ina2xx_read_word(client, i);
+ int rv = i2c_smbus_read_word_swapped(client, i);
if (rv < 0) {
ret = ERR_PTR(rv);
goto abort;
@@ -279,22 +263,26 @@ static int ina2xx_probe(struct i2c_client *client,
switch (data->kind) {
case ina219:
/* device configuration */
- ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT);
+ i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+ INA219_CONFIG_DEFAULT);
/* set current LSB to 1mA, shunt is in uOhms */
/* (equation 13 in datasheet) */
- ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt);
+ i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+ 40960000 / shunt);
dev_info(&client->dev,
"power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
data->registers = INA219_REGISTERS;
break;
case ina226:
/* device configuration */
- ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT);
+ i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+ INA226_CONFIG_DEFAULT);
/* set current LSB to 1mA, shunt is in uOhms */
/* (equation 1 in datasheet)*/
- ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt);
+ i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+ 5120000 / shunt);
dev_info(&client->dev,
"power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
data->registers = INA226_REGISTERS;
diff --git a/drivers/hwmon/twl4030-madc-hwmon.c b/drivers/hwmon/twl4030-madc-hwmon.c
index 0018c7dd0097..1a174f0a3cde 100644
--- a/drivers/hwmon/twl4030-madc-hwmon.c
+++ b/drivers/hwmon/twl4030-madc-hwmon.c
@@ -44,12 +44,13 @@ static ssize_t madc_read(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct twl4030_madc_request req;
+ struct twl4030_madc_request req = {
+ .channels = 1 << attr->index,
+ .method = TWL4030_MADC_SW2,
+ .type = TWL4030_MADC_WAIT,
+ };
long val;
- req.channels = (1 << attr->index);
- req.method = TWL4030_MADC_SW2;
- req.func_cb = NULL;
val = twl4030_madc_conversion(&req);
if (val < 0)
return val;
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index ee4ebc198a94..2e56c6ce9fb6 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -328,6 +328,7 @@ static int __init via_cputemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &cpu_data(i);
@@ -347,12 +348,14 @@ static int __init via_cputemp_init(void)
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -367,6 +370,7 @@ static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -375,6 +379,7 @@ static void __exit via_cputemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&via_cputemp_driver);
}
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index ab4825205a9d..5b1a6a666441 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -1206,7 +1206,7 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr,
int err = -ENODEV;
u16 val;
- static const __initdata char *names[] = {
+ static __initconst char *const names[] = {
"W83627HF",
"W83627THF",
"W83697HF",
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 1201a15784c3..db713c0dfba4 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -552,7 +552,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
*/
int hwspin_lock_free(struct hwspinlock *hwlock)
{
- struct device *dev = hwlock->bank->dev;
+ struct device *dev;
struct hwspinlock *tmp;
int ret;
@@ -561,6 +561,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
return -EINVAL;
}
+ dev = hwlock->bank->dev;
mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is used */
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index 73133b1063f0..6f5f98d69af7 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -476,17 +476,17 @@ static int pca_init(struct i2c_adapter *adap)
/* To avoid integer overflow, use clock/100 for calculations */
clock = pca_clock(pca_data) / 100;
- if (pca_data->i2c_clock > 10000) {
+ if (pca_data->i2c_clock > 1000000) {
mode = I2C_PCA_MODE_TURBO;
min_tlow = 14;
min_thi = 5;
raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
- } else if (pca_data->i2c_clock > 4000) {
+ } else if (pca_data->i2c_clock > 400000) {
mode = I2C_PCA_MODE_FASTP;
min_tlow = 17;
min_thi = 9;
raise_fall_time = 22; /* Raise 11e-8s, Fall 11e-8s */
- } else if (pca_data->i2c_clock > 1000) {
+ } else if (pca_data->i2c_clock > 100000) {
mode = I2C_PCA_MODE_FAST;
min_tlow = 44;
min_thi = 20;
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index b4aaa1bd6728..970a1612e795 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -104,6 +104,7 @@ config I2C_I801
DH89xxCC (PCH)
Panther Point (PCH)
Lynx Point (PCH)
+ Lynx Point-LP (PCH)
This driver can also be built as a module. If so, the module
will be called i2c-i801.
@@ -354,9 +355,13 @@ config I2C_DAVINCI
devices such as DaVinci NIC.
For details please see http://www.ti.com/davinci
+config I2C_DESIGNWARE_CORE
+ tristate
+
config I2C_DESIGNWARE_PLATFORM
tristate "Synopsys DesignWare Platform"
depends on HAVE_CLK
+ select I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.
@@ -367,6 +372,7 @@ config I2C_DESIGNWARE_PLATFORM
config I2C_DESIGNWARE_PCI
tristate "Synopsys DesignWare PCI"
depends on PCI
+ select I2C_DESIGNWARE_CORE
help
If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ce3c2be7fb40..37c4182cc98b 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -33,10 +33,11 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
+obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
-i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
+i2c-designware-platform-objs := i2c-designware-platdrv.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
-i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
+i2c-designware-pci-objs := i2c-designware-pcidrv.o
obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 1e48bec80edf..7b8ebbefb581 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -25,6 +25,7 @@
* ----------------------------------------------------------------------------
*
*/
+#include <linux/export.h>
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -316,6 +317,7 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
dw_writel(dev, dev->master_cfg , DW_IC_CON);
return 0;
}
+EXPORT_SYMBOL_GPL(i2c_dw_init);
/*
* Waiting for bus not busy
@@ -568,12 +570,14 @@ done:
return ret;
}
+EXPORT_SYMBOL_GPL(i2c_dw_xfer);
u32 i2c_dw_func(struct i2c_adapter *adap)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
return dev->functionality;
}
+EXPORT_SYMBOL_GPL(i2c_dw_func);
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
@@ -678,17 +682,20 @@ tx_aborted:
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_GPL(i2c_dw_isr);
void i2c_dw_enable(struct dw_i2c_dev *dev)
{
/* Enable the adapter */
dw_writel(dev, 1, DW_IC_ENABLE);
}
+EXPORT_SYMBOL_GPL(i2c_dw_enable);
u32 i2c_dw_is_enabled(struct dw_i2c_dev *dev)
{
return dw_readl(dev, DW_IC_ENABLE);
}
+EXPORT_SYMBOL_GPL(i2c_dw_is_enabled);
void i2c_dw_disable(struct dw_i2c_dev *dev)
{
@@ -699,18 +706,22 @@ void i2c_dw_disable(struct dw_i2c_dev *dev)
dw_writel(dev, 0, DW_IC_INTR_MASK);
dw_readl(dev, DW_IC_CLR_INTR);
}
+EXPORT_SYMBOL_GPL(i2c_dw_disable);
void i2c_dw_clear_int(struct dw_i2c_dev *dev)
{
dw_readl(dev, DW_IC_CLR_INTR);
}
+EXPORT_SYMBOL_GPL(i2c_dw_clear_int);
void i2c_dw_disable_int(struct dw_i2c_dev *dev)
{
dw_writel(dev, 0, DW_IC_INTR_MASK);
}
+EXPORT_SYMBOL_GPL(i2c_dw_disable_int);
u32 i2c_dw_read_comp_param(struct dw_i2c_dev *dev)
{
return dw_readl(dev, DW_IC_COMP_PARAM_1);
}
+EXPORT_SYMBOL_GPL(i2c_dw_read_comp_param);
diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c
index aedb94f34bf7..dae3ddfe7619 100644
--- a/drivers/i2c/busses/i2c-diolan-u2c.c
+++ b/drivers/i2c/busses/i2c-diolan-u2c.c
@@ -405,6 +405,7 @@ static int diolan_usb_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
}
}
}
+ ret = num;
abort:
sret = diolan_i2c_stop(dev);
if (sret < 0 && ret >= 0)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 898dcf9c7ade..33e9b0c09af2 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -52,6 +52,7 @@
DH89xxCC (PCH) 0x2330 32 hard yes yes yes
Panther Point (PCH) 0x1e22 32 hard yes yes yes
Lynx Point (PCH) 0x8c22 32 hard yes yes yes
+ Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes
Features supported by this driver:
Software PEC no
@@ -155,6 +156,7 @@
#define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330
#define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22
struct i801_priv {
struct i2c_adapter adapter;
@@ -771,6 +773,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 088c5c1ed17d..51f05b8520ed 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -365,10 +365,6 @@ static int mxs_i2c_get_ofdata(struct mxs_i2c_dev *i2c)
struct device_node *node = dev->of_node;
int ret;
- if (!node)
- return -EINVAL;
-
- i2c->speed = &mxs_i2c_95kHz_config;
ret = of_property_read_u32(node, "clock-frequency", &speed);
if (ret)
dev_warn(dev, "No I2C speed selected, using 100kHz\n");
@@ -419,10 +415,13 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev)
return err;
i2c->dev = dev;
+ i2c->speed = &mxs_i2c_95kHz_config;
- err = mxs_i2c_get_ofdata(i2c);
- if (err)
- return err;
+ if (dev->of_node) {
+ err = mxs_i2c_get_ofdata(i2c);
+ if (err)
+ return err;
+ }
platform_set_drvdata(pdev, i2c);
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 5e6f1eed4f83..61b00edacb08 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -350,10 +350,6 @@ static void setup_i2c_controller(struct nmk_i2c_dev *dev)
i2c_clk = clk_get_rate(dev->clk);
- /* fallback to std. mode if machine has not provided it */
- if (dev->cfg.clk_freq == 0)
- dev->cfg.clk_freq = 100000;
-
/*
* The spec says, in case of std. mode the divider is
* 2 whereas it is 3 for fast and fastplus mode of
@@ -911,20 +907,32 @@ static const struct i2c_algorithm nmk_i2c_algo = {
.functionality = nmk_i2c_functionality
};
+static struct nmk_i2c_controller u8500_i2c = {
+ /*
+ * Slave data setup time; 250ns, 100ns, and 10ns, which
+ * is 14, 6 and 2 respectively for a 48Mhz i2c clock.
+ */
+ .slsu = 0xe,
+ .tft = 1, /* Tx FIFO threshold */
+ .rft = 8, /* Rx FIFO threshold */
+ .clk_freq = 400000, /* fast mode operation */
+ .timeout = 200, /* Slave response timeout(ms) */
+ .sm = I2C_FREQ_MODE_FAST,
+};
+
static atomic_t adapter_id = ATOMIC_INIT(0);
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
- struct nmk_i2c_controller *pdata =
- adev->dev.platform_data;
+ struct nmk_i2c_controller *pdata = adev->dev.platform_data;
struct nmk_i2c_dev *dev;
struct i2c_adapter *adap;
- if (!pdata) {
- dev_warn(&adev->dev, "no platform data\n");
- return -ENODEV;
- }
+ if (!pdata)
+ /* No i2c configuration found, using the default. */
+ pdata = &u8500_i2c;
+
dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
if (!dev) {
dev_err(&adev->dev, "cannot allocate memory\n");
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 6849635b268a..5d19a49803c1 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -584,7 +584,7 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
r = pm_runtime_get_sync(dev->dev);
if (IS_ERR_VALUE(r))
- return r;
+ goto out;
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index 07b7447ecbc9..3d71395ae1f7 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -306,8 +306,7 @@ static int __devinit pmcmsptwi_probe(struct platform_device *pldev)
pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
if (pmcmsptwi_data.irq) {
rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM,
- pldev->name, &pmcmsptwi_data);
+ IRQF_SHARED, pldev->name, &pmcmsptwi_data);
if (rc == 0) {
/*
* Enable 'DONE' interrupt only.
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c
index 5d54416770b0..8488bddfe465 100644
--- a/drivers/i2c/busses/i2c-pnx.c
+++ b/drivers/i2c/busses/i2c-pnx.c
@@ -48,8 +48,9 @@ enum {
mcntrl_afie = 0x00000002,
mcntrl_naie = 0x00000004,
mcntrl_drmie = 0x00000008,
- mcntrl_daie = 0x00000020,
- mcntrl_rffie = 0x00000040,
+ mcntrl_drsie = 0x00000010,
+ mcntrl_rffie = 0x00000020,
+ mcntrl_daie = 0x00000040,
mcntrl_tffie = 0x00000080,
mcntrl_reset = 0x00000100,
mcntrl_cdbmode = 0x00000400,
@@ -290,31 +291,37 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
* or we didn't 'ask' for it yet.
*/
if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
- dev_dbg(&alg_data->adapter.dev,
- "%s(): Write dummy data to fill Rx-fifo...\n",
- __func__);
+ /* 'Asking' is done asynchronously, e.g. dummy TX of several
+ * bytes is done before the first actual RX arrives in FIFO.
+ * Therefore, ordered bytes (via TX) are counted separately.
+ */
+ if (alg_data->mif.order) {
+ dev_dbg(&alg_data->adapter.dev,
+ "%s(): Write dummy data to fill Rx-fifo...\n",
+ __func__);
- if (alg_data->mif.len == 1) {
- /* Last byte, do not acknowledge next rcv. */
- val |= stop_bit;
+ if (alg_data->mif.order == 1) {
+ /* Last byte, do not acknowledge next rcv. */
+ val |= stop_bit;
+
+ /*
+ * Enable interrupt RFDAIE (data in Rx fifo),
+ * and disable DRMIE (need data for Tx)
+ */
+ ctl = ioread32(I2C_REG_CTL(alg_data));
+ ctl |= mcntrl_rffie | mcntrl_daie;
+ ctl &= ~mcntrl_drmie;
+ iowrite32(ctl, I2C_REG_CTL(alg_data));
+ }
/*
- * Enable interrupt RFDAIE (data in Rx fifo),
- * and disable DRMIE (need data for Tx)
+ * Now we'll 'ask' for data:
+ * For each byte we want to receive, we must
+ * write a (dummy) byte to the Tx-FIFO.
*/
- ctl = ioread32(I2C_REG_CTL(alg_data));
- ctl |= mcntrl_rffie | mcntrl_daie;
- ctl &= ~mcntrl_drmie;
- iowrite32(ctl, I2C_REG_CTL(alg_data));
+ iowrite32(val, I2C_REG_TX(alg_data));
+ alg_data->mif.order--;
}
-
- /*
- * Now we'll 'ask' for data:
- * For each byte we want to receive, we must
- * write a (dummy) byte to the Tx-FIFO.
- */
- iowrite32(val, I2C_REG_TX(alg_data));
-
return 0;
}
@@ -514,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
alg_data->mif.buf = pmsg->buf;
alg_data->mif.len = pmsg->len;
+ alg_data->mif.order = pmsg->len;
alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?
I2C_SMBUS_READ : I2C_SMBUS_WRITE;
alg_data->mif.ret = 0;
@@ -566,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/* Cleanup to be sure... */
alg_data->mif.buf = NULL;
alg_data->mif.len = 0;
+ alg_data->mif.order = 0;
dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",
__func__, ioread32(I2C_REG_STS(alg_data)));
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 66eb53fac202..9a08c57bc936 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -712,7 +712,7 @@ static int __devexit tegra_i2c_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int tegra_i2c_suspend(struct device *dev)
{
struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 2efa56c5ff2c..2091ae8f539a 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -637,6 +637,22 @@ static void i2c_adapter_dev_release(struct device *dev)
}
/*
+ * This function is only needed for mutex_lock_nested, so it is never
+ * called unless locking correctness checking is enabled. Thus we
+ * make it inline to avoid a compiler warning. That's what gcc ends up
+ * doing anyway.
+ */
+static inline unsigned int i2c_adapter_depth(struct i2c_adapter *adapter)
+{
+ unsigned int depth = 0;
+
+ while ((adapter = i2c_parent_is_i2c_adapter(adapter)))
+ depth++;
+
+ return depth;
+}
+
+/*
* Let users instantiate I2C devices through sysfs. This can be used when
* platform initialization code doesn't contain the proper data for
* whatever reason. Also useful for drivers that do device detection and
@@ -726,7 +742,8 @@ i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
/* Make sure the device was added through sysfs */
res = -ENOENT;
- mutex_lock(&adap->userspace_clients_lock);
+ mutex_lock_nested(&adap->userspace_clients_lock,
+ i2c_adapter_depth(adap));
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
if (client->addr == addr) {
@@ -1073,7 +1090,8 @@ int i2c_del_adapter(struct i2c_adapter *adap)
return res;
/* Remove devices instantiated from sysfs */
- mutex_lock(&adap->userspace_clients_lock);
+ mutex_lock_nested(&adap->userspace_clients_lock,
+ i2c_adapter_depth(adap));
list_for_each_entry_safe(client, next, &adap->userspace_clients,
detected) {
dev_dbg(&adap->dev, "Removing %s at 0x%x\n", client->name,
diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c
index 92406097efeb..8d1e32d7cd97 100644
--- a/drivers/ide/ide-pm.c
+++ b/drivers/ide/ide-pm.c
@@ -4,7 +4,7 @@
int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{
- ide_drive_t *drive = dev_get_drvdata(dev);
+ ide_drive_t *drive = to_ide_device(dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
@@ -40,7 +40,7 @@ int generic_ide_suspend(struct device *dev, pm_message_t mesg)
int generic_ide_resume(struct device *dev)
{
- ide_drive_t *drive = dev_get_drvdata(dev);
+ ide_drive_t *drive = to_ide_device(dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
ide_hwif_t *hwif = drive->hwif;
struct request *rq;
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index f559088869f6..e8726177d103 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -606,8 +606,9 @@ static int __init intel_idle_init(void)
intel_idle_cpuidle_driver_init();
retval = cpuidle_register_driver(&intel_idle_driver);
if (retval) {
+ struct cpuidle_driver *drv = cpuidle_get_driver();
printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
- cpuidle_get_driver()->name);
+ drv ? drv->name : "none");
return retval;
}
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index f61780a02374..3bd5540238a7 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -617,7 +617,7 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
if (IS_ERR(st->adc_clk)) {
dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
- ret = PTR_ERR(st->clk);
+ ret = PTR_ERR(st->adc_clk);
goto error_disable_clk;
}
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 59fbb3ae40e7..e35bb8f6fe75 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -129,7 +129,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
{
struct adf4350_platform_data *pdata = st->pdata;
u64 tmp;
- u32 div_gcd, prescaler;
+ u32 div_gcd, prescaler, chspc;
u16 mdiv, r_cnt = 0;
u8 band_sel_div;
@@ -158,14 +158,20 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
if (pdata->ref_div_factor)
r_cnt = pdata->ref_div_factor - 1;
- do {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ chspc = st->chspc;
- st->r1_mod = st->fpfd / st->chspc;
- while (st->r1_mod > ADF4350_MAX_MODULUS) {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
- st->r1_mod = st->fpfd / st->chspc;
- }
+ do {
+ do {
+ do {
+ r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ st->r1_mod = st->fpfd / chspc;
+ if (r_cnt > ADF4350_MAX_R_CNT) {
+ /* try higher spacing values */
+ chspc++;
+ r_cnt = 0;
+ }
+ } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
+ } while (r_cnt == 0);
tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
@@ -194,7 +200,7 @@ static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
ADF4350_REG0_FRACT(st->r0_fract);
- st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) |
+ st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
ADF4350_REG1_MOD(st->r1_mod) |
prescaler;
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index 1cbb449b319a..9a99f43094f0 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -271,9 +271,10 @@ static int adjd_s311_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct adjd_s311_data *data = iio_priv(indio_dev);
- data->buffer = krealloc(data->buffer, indio_dev->scan_bytes,
- GFP_KERNEL);
- if (!data->buffer)
+
+ kfree(data->buffer);
+ data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data->buffer == NULL)
return -ENOMEM;
return 0;
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index c3e7bac13123..e45712a921ce 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -404,7 +404,7 @@ out:
return ret;
}
-static int show_thresh_either_en(struct device *dev,
+static ssize_t show_thresh_either_en(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@@ -424,7 +424,7 @@ static int show_thresh_either_en(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
}
-static int store_thresh_either_en(struct device *dev,
+static ssize_t store_thresh_either_en(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 5a335b5447c6..7172559ce0c1 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -3064,10 +3064,7 @@ static int cma_join_ib_multicast(struct rdma_id_private *id_priv,
id_priv->id.port_num, &rec,
comp_mask, GFP_KERNEL,
cma_ib_mc_handler, mc);
- if (IS_ERR(mc->multicast.ib))
- return PTR_ERR(mc->multicast.ib);
-
- return 0;
+ return PTR_RET(mc->multicast.ib);
}
static void iboe_mcast_work_handler(struct work_struct *work)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 893cb879462c..055ed59838dc 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -267,6 +267,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
if (!uevent)
return event->event == RDMA_CM_EVENT_CONNECT_REQUEST;
+ mutex_lock(&ctx->file->mut);
uevent->cm_id = cm_id;
ucma_set_event_context(ctx, event, uevent);
uevent->resp.event = event->event;
@@ -277,7 +278,6 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
ucma_copy_conn_event(&uevent->resp.param.conn,
&event->param.conn);
- mutex_lock(&ctx->file->mut);
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
if (!ctx->backlog) {
ret = -ENOMEM;
@@ -1002,23 +1002,18 @@ static ssize_t ucma_set_option(struct ucma_file *file, const char __user *inbuf,
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- optval = kmalloc(cmd.optlen, GFP_KERNEL);
- if (!optval) {
- ret = -ENOMEM;
- goto out1;
- }
-
- if (copy_from_user(optval, (void __user *) (unsigned long) cmd.optval,
- cmd.optlen)) {
- ret = -EFAULT;
- goto out2;
+ optval = memdup_user((void __user *) (unsigned long) cmd.optval,
+ cmd.optlen);
+ if (IS_ERR(optval)) {
+ ret = PTR_ERR(optval);
+ goto out;
}
ret = ucma_set_option_level(ctx, cmd.level, cmd.optname, optval,
cmd.optlen);
-out2:
kfree(optval);
-out1:
+
+out:
ucma_put_ctx(ctx);
return ret;
}
diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c
index 8c81992fa6db..e4a73158fc7f 100644
--- a/drivers/infiniband/hw/amso1100/c2_rnic.c
+++ b/drivers/infiniband/hw/amso1100/c2_rnic.c
@@ -439,7 +439,7 @@ static int c2_rnic_close(struct c2_dev *c2dev)
/*
* Called by c2_probe to initialize the RNIC. This principally
- * involves initalizing the various limits and resouce pools that
+ * involves initializing the various limits and resource pools that
* comprise the RNIC instance.
*/
int __devinit c2_rnic_init(struct c2_dev *c2dev)
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 77b6b182778a..aaf88ef9409c 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1680,7 +1680,7 @@ static int close_con_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
* T3A does 3 things when a TERM is received:
* 1) send up a CPL_RDMA_TERMINATE message with the TERM packet
* 2) generate an async event on the QP with the TERMINATE opcode
- * 3) post a TERMINATE opcde cqe into the associated CQ.
+ * 3) post a TERMINATE opcode cqe into the associated CQ.
*
* For (1), we save the message in the qp for later consumer consumption.
* For (2), we move the QP into TERMINATE, post a QP event and disconnect.
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 51f42061dae9..6cfd4d8fd0bd 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1361,11 +1361,11 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct tid_info *t = dev->rdev.lldi.tids;
ep = lookup_tid(t, tid);
- PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
if (!ep) {
printk(KERN_WARNING MOD "Abort rpl to freed endpoint\n");
return 0;
}
+ PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case ABORTING:
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index c27141fef1ab..9c2ae7efd00f 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -125,6 +125,7 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl)
{
struct ib_ah *new_ah;
struct ib_ah_attr ah_attr;
+ unsigned long flags;
if (!dev->send_agent[port_num - 1][0])
return;
@@ -139,11 +140,11 @@ static void update_sm_ah(struct mlx4_ib_dev *dev, u8 port_num, u16 lid, u8 sl)
if (IS_ERR(new_ah))
return;
- spin_lock(&dev->sm_lock);
+ spin_lock_irqsave(&dev->sm_lock, flags);
if (dev->sm_ah[port_num - 1])
ib_destroy_ah(dev->sm_ah[port_num - 1]);
dev->sm_ah[port_num - 1] = new_ah;
- spin_unlock(&dev->sm_lock);
+ spin_unlock_irqrestore(&dev->sm_lock, flags);
}
/*
@@ -197,13 +198,15 @@ static void smp_snoop(struct ib_device *ibdev, u8 port_num, struct ib_mad *mad,
static void node_desc_override(struct ib_device *dev,
struct ib_mad *mad)
{
+ unsigned long flags;
+
if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
mad->mad_hdr.method == IB_MGMT_METHOD_GET_RESP &&
mad->mad_hdr.attr_id == IB_SMP_ATTR_NODE_DESC) {
- spin_lock(&to_mdev(dev)->sm_lock);
+ spin_lock_irqsave(&to_mdev(dev)->sm_lock, flags);
memcpy(((struct ib_smp *) mad)->data, dev->node_desc, 64);
- spin_unlock(&to_mdev(dev)->sm_lock);
+ spin_unlock_irqrestore(&to_mdev(dev)->sm_lock, flags);
}
}
@@ -213,6 +216,7 @@ static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *ma
struct ib_mad_send_buf *send_buf;
struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn];
int ret;
+ unsigned long flags;
if (agent) {
send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
@@ -225,13 +229,13 @@ static void forward_trap(struct mlx4_ib_dev *dev, u8 port_num, struct ib_mad *ma
* wrong following the IB spec strictly, but we know
* it's OK for our devices).
*/
- spin_lock(&dev->sm_lock);
+ spin_lock_irqsave(&dev->sm_lock, flags);
memcpy(send_buf->mad, mad, sizeof *mad);
if ((send_buf->ah = dev->sm_ah[port_num - 1]))
ret = ib_post_send_mad(send_buf, NULL);
else
ret = -EINVAL;
- spin_unlock(&dev->sm_lock);
+ spin_unlock_irqrestore(&dev->sm_lock, flags);
if (ret)
ib_free_send_mad(send_buf);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index fe2088cfa6ee..cc05579ebce7 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -423,6 +423,7 @@ static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask,
struct ib_device_modify *props)
{
struct mlx4_cmd_mailbox *mailbox;
+ unsigned long flags;
if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
return -EOPNOTSUPP;
@@ -430,9 +431,9 @@ static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask,
if (!(mask & IB_DEVICE_MODIFY_NODE_DESC))
return 0;
- spin_lock(&to_mdev(ibdev)->sm_lock);
+ spin_lock_irqsave(&to_mdev(ibdev)->sm_lock, flags);
memcpy(ibdev->node_desc, props->node_desc, 64);
- spin_unlock(&to_mdev(ibdev)->sm_lock);
+ spin_unlock_irqrestore(&to_mdev(ibdev)->sm_lock, flags);
/*
* If possible, pass node desc to FW, so it can generate
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index a6d8ea060ea8..f585eddef4b7 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1407,6 +1407,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
struct mlx4_wqe_mlx_seg *mlx = wqe;
struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx;
struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah);
+ struct net_device *ndev;
union ib_gid sgid;
u16 pkey;
int send_size;
@@ -1483,7 +1484,10 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
memcpy(sqp->ud_header.eth.dmac_h, ah->av.eth.mac, 6);
/* FIXME: cache smac value? */
- smac = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1]->dev_addr;
+ ndev = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1];
+ if (!ndev)
+ return -ENODEV;
+ smac = ndev->dev_addr;
memcpy(sqp->ud_header.eth.smac_h, smac, 6);
if (!memcmp(sqp->ud_header.eth.smac_h, sqp->ud_header.eth.dmac_h, 6))
mlx->flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 5a044526e4f4..c4e0131f1b57 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -161,7 +161,7 @@ static void ocrdma_add_default_sgid(struct ocrdma_dev *dev)
ocrdma_get_guid(dev, &sgid->raw[8]);
}
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
static void ocrdma_add_vlan_sgids(struct ocrdma_dev *dev)
{
struct net_device *netdev, *tmp;
@@ -202,14 +202,13 @@ static int ocrdma_build_sgid_tbl(struct ocrdma_dev *dev)
return 0;
}
-#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_VLAN_8021Q)
+#if IS_ENABLED(CONFIG_IPV6)
static int ocrdma_inet6addr_event(struct notifier_block *notifier,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
- struct net_device *event_netdev = ifa->idev->dev;
- struct net_device *netdev = NULL;
+ struct net_device *netdev = ifa->idev->dev;
struct ib_event gid_event;
struct ocrdma_dev *dev;
bool found = false;
@@ -217,11 +216,12 @@ static int ocrdma_inet6addr_event(struct notifier_block *notifier,
bool is_vlan = false;
u16 vid = 0;
- netdev = vlan_dev_real_dev(event_netdev);
- if (netdev != event_netdev) {
- is_vlan = true;
- vid = vlan_dev_vlan_id(event_netdev);
+ is_vlan = netdev->priv_flags & IFF_802_1Q_VLAN;
+ if (is_vlan) {
+ vid = vlan_dev_vlan_id(netdev);
+ netdev = vlan_dev_real_dev(netdev);
}
+
rcu_read_lock();
list_for_each_entry_rcu(dev, &ocrdma_dev_list, entry) {
if (dev->nic_info.netdev == netdev) {
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index b2f9784beb4a..b29a4246ef41 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -893,7 +893,9 @@ static int ocrdma_check_qp_params(struct ib_pd *ibpd, struct ocrdma_dev *dev,
/* verify consumer QPs are not trying to use GSI QP's CQ */
if ((attrs->qp_type != IB_QPT_GSI) && (dev->gsi_qp_created)) {
if ((dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq)) ||
- (dev->gsi_sqcq == get_ocrdma_cq(attrs->send_cq))) {
+ (dev->gsi_sqcq == get_ocrdma_cq(attrs->recv_cq)) ||
+ (dev->gsi_rqcq == get_ocrdma_cq(attrs->send_cq)) ||
+ (dev->gsi_rqcq == get_ocrdma_cq(attrs->recv_cq))) {
ocrdma_err("%s(%d) Consumer QP cannot use GSI CQs.\n",
__func__, dev->id);
return -EINVAL;
@@ -2217,7 +2219,6 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
u32 wqe_idx;
if (!qp->wqe_wr_id_tbl[tail].signaled) {
- expand = true; /* CQE cannot be consumed yet */
*polled = false; /* WC cannot be consumed yet */
} else {
ibwc->status = IB_WC_SUCCESS;
@@ -2225,10 +2226,11 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
ibwc->qp = &qp->ibqp;
ocrdma_update_wc(qp, ibwc, tail);
*polled = true;
- wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
- if (tail != wqe_idx)
- expand = true; /* Coalesced CQE can't be consumed yet */
}
+ wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
+ if (tail != wqe_idx)
+ expand = true; /* Coalesced CQE can't be consumed yet */
+
ocrdma_hwq_inc_tail(&qp->sq);
return expand;
}
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 6e19ec844d99..7b1b86690024 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -656,6 +656,11 @@ struct qib_pportdata {
/* 16 congestion entries with each entry corresponding to a SL */
struct ib_cc_congestion_entry_shadow *congestion_entries;
+ /* Maximum number of congestion control entries that the agent expects
+ * the manager to send.
+ */
+ u16 cc_supported_table_entries;
+
/* Total number of congestion control table entries */
u16 total_cct_entry;
@@ -667,11 +672,6 @@ struct qib_pportdata {
/* CA's max number of 64 entry units in the congestion control table */
u8 cc_max_table_entries;
-
- /* Maximum number of congestion control entries that the agent expects
- * the manager to send.
- */
- u8 cc_supported_table_entries;
};
/* Observers. Not to be taken lightly, possibly not to ship. */
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 0d7280af99bc..3f6b21e9dc11 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -6346,8 +6346,10 @@ static int qib_init_7322_variables(struct qib_devdata *dd)
dd->piobcnt4k * dd->align4k;
dd->piovl15base = ioremap_nocache(vl15off,
NUM_VL15_BUFS * dd->align4k);
- if (!dd->piovl15base)
+ if (!dd->piovl15base) {
+ ret = -ENOMEM;
goto bail;
+ }
}
qib_7322_set_baseaddrs(dd); /* set chip access pointers now */
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index 19f1e6c45fb6..ccb119143d20 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -471,9 +471,10 @@ static int subn_get_portinfo(struct ib_smp *smp, struct ib_device *ibdev,
if (port_num != port) {
ibp = to_iport(ibdev, port_num);
ret = check_mkey(ibp, smp, 0);
- if (ret)
+ if (ret) {
ret = IB_MAD_RESULT_FAILURE;
goto bail;
+ }
}
}
diff --git a/drivers/infiniband/hw/qib/qib_sd7220.c b/drivers/infiniband/hw/qib/qib_sd7220.c
index a322d5171a2c..50a8a0d4fe67 100644
--- a/drivers/infiniband/hw/qib/qib_sd7220.c
+++ b/drivers/infiniband/hw/qib/qib_sd7220.c
@@ -372,7 +372,7 @@ static void qib_sd_trimdone_monitor(struct qib_devdata *dd,
/* Read CTRL reg for each channel to check TRIMDONE */
if (baduns & (1 << chn)) {
qib_dev_err(dd,
- "Reseting TRIMDONE on chn %d (%s)\n",
+ "Resetting TRIMDONE on chn %d (%s)\n",
chn, where);
ret = qib_sd7220_reg_mod(dd, IB_7220_SERDES,
IB_CTRL2(chn), 0x10, 0x10);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index 86df632ea612..0af216d21f87 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -92,6 +92,8 @@ enum {
IPOIB_STOP_REAPER = 7,
IPOIB_FLAG_ADMIN_CM = 9,
IPOIB_FLAG_UMCAST = 10,
+ IPOIB_STOP_NEIGH_GC = 11,
+ IPOIB_NEIGH_TBL_FLUSH = 12,
IPOIB_MAX_BACKOFF_SECONDS = 16,
@@ -260,6 +262,23 @@ struct ipoib_ethtool_st {
u16 max_coalesced_frames;
};
+struct ipoib_neigh_table;
+
+struct ipoib_neigh_hash {
+ struct ipoib_neigh_table *ntbl;
+ struct ipoib_neigh __rcu **buckets;
+ struct rcu_head rcu;
+ u32 mask;
+ u32 size;
+};
+
+struct ipoib_neigh_table {
+ struct ipoib_neigh_hash __rcu *htbl;
+ atomic_t entries;
+ struct completion flushed;
+ struct completion deleted;
+};
+
/*
* Device private locking: network stack tx_lock protects members used
* in TX fast path, lock protects everything else. lock nests inside
@@ -279,6 +298,8 @@ struct ipoib_dev_priv {
struct rb_root path_tree;
struct list_head path_list;
+ struct ipoib_neigh_table ntbl;
+
struct ipoib_mcast *broadcast;
struct list_head multicast_list;
struct rb_root multicast_tree;
@@ -291,7 +312,7 @@ struct ipoib_dev_priv {
struct work_struct flush_heavy;
struct work_struct restart_task;
struct delayed_work ah_reap_task;
-
+ struct delayed_work neigh_reap_task;
struct ib_device *ca;
u8 port;
u16 pkey;
@@ -377,13 +398,16 @@ struct ipoib_neigh {
#ifdef CONFIG_INFINIBAND_IPOIB_CM
struct ipoib_cm_tx *cm;
#endif
- union ib_gid dgid;
+ u8 daddr[INFINIBAND_ALEN];
struct sk_buff_head queue;
- struct neighbour *neighbour;
struct net_device *dev;
struct list_head list;
+ struct ipoib_neigh __rcu *hnext;
+ struct rcu_head rcu;
+ atomic_t refcnt;
+ unsigned long alive;
};
#define IPOIB_UD_MTU(ib_mtu) (ib_mtu - IPOIB_ENCAP_LEN)
@@ -394,21 +418,17 @@ static inline int ipoib_ud_need_sg(unsigned int ib_mtu)
return IPOIB_UD_BUF_SIZE(ib_mtu) > PAGE_SIZE;
}
-/*
- * We stash a pointer to our private neighbour information after our
- * hardware address in neigh->ha. The ALIGN() expression here makes
- * sure that this pointer is stored aligned so that an unaligned
- * load is not needed to dereference it.
- */
-static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh);
+static inline void ipoib_neigh_put(struct ipoib_neigh *neigh)
{
- return (void*) neigh + ALIGN(offsetof(struct neighbour, ha) +
- INFINIBAND_ALEN, sizeof(void *));
+ if (atomic_dec_and_test(&neigh->refcnt))
+ ipoib_neigh_dtor(neigh);
}
-
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh,
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr);
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
struct net_device *dev);
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh);
+void ipoib_neigh_free(struct ipoib_neigh *neigh);
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid);
extern struct workqueue_struct *ipoib_workqueue;
@@ -425,7 +445,6 @@ static inline void ipoib_put_ah(struct ipoib_ah *ah)
{
kref_put(&ah->ref, ipoib_free_ah);
}
-
int ipoib_open(struct net_device *dev);
int ipoib_add_pkey_attr(struct net_device *dev);
int ipoib_add_umcast_attr(struct net_device *dev);
@@ -455,7 +474,7 @@ void ipoib_dev_cleanup(struct net_device *dev);
void ipoib_mcast_join_task(struct work_struct *work);
void ipoib_mcast_carrier_on_task(struct work_struct *work);
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb);
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb);
void ipoib_mcast_restart_task(struct work_struct *work);
int ipoib_mcast_start_thread(struct net_device *dev);
@@ -517,10 +536,10 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
}
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- return IPOIB_CM_SUPPORTED(n->ha) &&
+ return IPOIB_CM_SUPPORTED(hwaddr) &&
test_bit(IPOIB_FLAG_ADMIN_CM, &priv->flags);
}
@@ -575,7 +594,7 @@ static inline int ipoib_cm_admin_enabled(struct net_device *dev)
{
return 0;
}
-static inline int ipoib_cm_enabled(struct net_device *dev, struct neighbour *n)
+static inline int ipoib_cm_enabled(struct net_device *dev, u8 *hwaddr)
{
return 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 6d66ab0dd92a..24683fda8e21 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -811,9 +811,7 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc)
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
tx->neigh = NULL;
}
@@ -1230,9 +1228,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id,
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
tx->neigh = NULL;
}
@@ -1275,12 +1271,15 @@ struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path
void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
{
struct ipoib_dev_priv *priv = netdev_priv(tx->dev);
+ unsigned long flags;
if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
+ spin_lock_irqsave(&priv->lock, flags);
list_move(&tx->list, &priv->cm.reap_list);
queue_work(ipoib_workqueue, &priv->cm.reap_task);
ipoib_dbg(priv, "Reap connection for gid %pI6\n",
- tx->neigh->dgid.raw);
+ tx->neigh->daddr + 4);
tx->neigh = NULL;
+ spin_unlock_irqrestore(&priv->lock, flags);
}
}
@@ -1304,7 +1303,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
p = list_entry(priv->cm.start_list.next, typeof(*p), list);
list_del_init(&p->list);
neigh = p->neigh;
- qpn = IPOIB_QPN(neigh->neighbour->ha);
+ qpn = IPOIB_QPN(neigh->daddr);
memcpy(&pathrec, &p->path->pathrec, sizeof pathrec);
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1320,9 +1319,7 @@ static void ipoib_cm_tx_start(struct work_struct *work)
if (neigh) {
neigh->cm = NULL;
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
}
list_del(&p->list);
kfree(p);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index bbee4b2d7a13..1e19b5ae7c47 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -46,7 +46,8 @@
#include <linux/ip.h>
#include <linux/in.h>
-#include <net/dst.h>
+#include <linux/jhash.h>
+#include <net/arp.h>
MODULE_AUTHOR("Roland Dreier");
MODULE_DESCRIPTION("IP-over-InfiniBand net driver");
@@ -84,6 +85,7 @@ struct ib_sa_client ipoib_sa_client;
static void ipoib_add_one(struct ib_device *device);
static void ipoib_remove_one(struct ib_device *device);
+static void ipoib_neigh_reclaim(struct rcu_head *rp);
static struct ib_client ipoib_client = {
.name = "ipoib",
@@ -264,30 +266,15 @@ static int __path_add(struct net_device *dev, struct ipoib_path *path)
static void path_free(struct net_device *dev, struct ipoib_path *path)
{
- struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct ipoib_neigh *neigh, *tn;
struct sk_buff *skb;
- unsigned long flags;
while ((skb = __skb_dequeue(&path->queue)))
dev_kfree_skb_irq(skb);
- spin_lock_irqsave(&priv->lock, flags);
-
- list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) {
- /*
- * It's safe to call ipoib_put_ah() inside priv->lock
- * here, because we know that path->ah will always
- * hold one more reference, so ipoib_put_ah() will
- * never do more than decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
-
- ipoib_neigh_free(dev, neigh);
- }
+ ipoib_dbg(netdev_priv(dev), "path_free\n");
- spin_unlock_irqrestore(&priv->lock, flags);
+ /* remove all neigh connected to this path */
+ ipoib_del_neighs_by_gid(dev, path->pathrec.dgid.raw);
if (path->ah)
ipoib_put_ah(path->ah);
@@ -458,19 +445,15 @@ static void path_rec_completion(int status,
}
kref_get(&path->ah->ref);
neigh->ah = path->ah;
- memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
- sizeof(union ib_gid));
- if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+ if (ipoib_cm_enabled(dev, neigh->daddr)) {
if (!ipoib_cm_get(neigh))
ipoib_cm_set(neigh, ipoib_cm_create_tx(dev,
path,
neigh));
if (!ipoib_cm_get(neigh)) {
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
continue;
}
}
@@ -555,26 +538,26 @@ static int path_rec_start(struct net_device *dev,
return 0;
}
-/* called with rcu_read_lock */
-static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
+static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
+ struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_path *path;
struct ipoib_neigh *neigh;
unsigned long flags;
- neigh = ipoib_neigh_alloc(n, skb->dev);
+ spin_lock_irqsave(&priv->lock, flags);
+ neigh = ipoib_neigh_alloc(daddr, dev);
if (!neigh) {
+ spin_unlock_irqrestore(&priv->lock, flags);
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
return;
}
- spin_lock_irqsave(&priv->lock, flags);
-
- path = __path_find(dev, n->ha + 4);
+ path = __path_find(dev, daddr + 4);
if (!path) {
- path = path_rec_create(dev, n->ha + 4);
+ path = path_rec_create(dev, daddr + 4);
if (!path)
goto err_path;
@@ -586,17 +569,13 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
if (path->ah) {
kref_get(&path->ah->ref);
neigh->ah = path->ah;
- memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
- sizeof(union ib_gid));
- if (ipoib_cm_enabled(dev, neigh->neighbour)) {
+ if (ipoib_cm_enabled(dev, neigh->daddr)) {
if (!ipoib_cm_get(neigh))
ipoib_cm_set(neigh, ipoib_cm_create_tx(dev, path, neigh));
if (!ipoib_cm_get(neigh)) {
list_del(&neigh->list);
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
goto err_drop;
}
if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
@@ -608,7 +587,8 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
}
} else {
spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_send(dev, skb, path->ah, IPOIB_QPN(n->ha));
+ ipoib_send(dev, skb, path->ah, IPOIB_QPN(daddr));
+ ipoib_neigh_put(neigh);
return;
}
} else {
@@ -621,35 +601,20 @@ static void neigh_add_path(struct sk_buff *skb, struct neighbour *n, struct net_
}
spin_unlock_irqrestore(&priv->lock, flags);
+ ipoib_neigh_put(neigh);
return;
err_list:
list_del(&neigh->list);
err_path:
- ipoib_neigh_free(dev, neigh);
+ ipoib_neigh_free(neigh);
err_drop:
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-/* called with rcu_read_lock */
-static void ipoib_path_lookup(struct sk_buff *skb, struct neighbour *n, struct net_device *dev)
-{
- struct ipoib_dev_priv *priv = netdev_priv(skb->dev);
-
- /* Look up path record for unicasts */
- if (n->ha[4] != 0xff) {
- neigh_add_path(skb, n, dev);
- return;
- }
-
- /* Add in the P_Key for multicasts */
- n->ha[8] = (priv->pkey >> 8) & 0xff;
- n->ha[9] = priv->pkey & 0xff;
- ipoib_mcast_send(dev, n->ha + 4, skb);
+ ipoib_neigh_put(neigh);
}
static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
@@ -710,96 +675,80 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_neigh *neigh;
- struct neighbour *n = NULL;
+ struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
+ struct ipoib_header *header;
unsigned long flags;
- rcu_read_lock();
- if (likely(skb_dst(skb))) {
- n = dst_neigh_lookup_skb(skb_dst(skb), skb);
- if (!n) {
+ header = (struct ipoib_header *) skb->data;
+
+ if (unlikely(cb->hwaddr[4] == 0xff)) {
+ /* multicast, arrange "if" according to probability */
+ if ((header->proto != htons(ETH_P_IP)) &&
+ (header->proto != htons(ETH_P_IPV6)) &&
+ (header->proto != htons(ETH_P_ARP)) &&
+ (header->proto != htons(ETH_P_RARP))) {
+ /* ethertype not supported by IPoIB */
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
- goto unlock;
+ return NETDEV_TX_OK;
}
+ /* Add in the P_Key for multicast*/
+ cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
+ cb->hwaddr[9] = priv->pkey & 0xff;
+
+ neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ if (likely(neigh))
+ goto send_using_neigh;
+ ipoib_mcast_send(dev, cb->hwaddr, skb);
+ return NETDEV_TX_OK;
}
- if (likely(n)) {
- if (unlikely(!*to_ipoib_neigh(n))) {
- ipoib_path_lookup(skb, n, dev);
- goto unlock;
- }
- neigh = *to_ipoib_neigh(n);
-
- if (unlikely((memcmp(&neigh->dgid.raw,
- n->ha + 4,
- sizeof(union ib_gid))) ||
- (neigh->dev != dev))) {
- spin_lock_irqsave(&priv->lock, flags);
- /*
- * It's safe to call ipoib_put_ah() inside
- * priv->lock here, because we know that
- * path->ah will always hold one more reference,
- * so ipoib_put_ah() will never do more than
- * decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- list_del(&neigh->list);
- ipoib_neigh_free(dev, neigh);
- spin_unlock_irqrestore(&priv->lock, flags);
- ipoib_path_lookup(skb, n, dev);
- goto unlock;
+ /* unicast, arrange "switch" according to probability */
+ switch (header->proto) {
+ case htons(ETH_P_IP):
+ case htons(ETH_P_IPV6):
+ neigh = ipoib_neigh_get(dev, cb->hwaddr);
+ if (unlikely(!neigh)) {
+ neigh_add_path(skb, cb->hwaddr, dev);
+ return NETDEV_TX_OK;
}
+ break;
+ case htons(ETH_P_ARP):
+ case htons(ETH_P_RARP):
+ /* for unicast ARP and RARP should always perform path find */
+ unicast_arp_send(skb, dev, cb);
+ return NETDEV_TX_OK;
+ default:
+ /* ethertype not supported by IPoIB */
+ ++dev->stats.tx_dropped;
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
- if (ipoib_cm_get(neigh)) {
- if (ipoib_cm_up(neigh)) {
- ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
- goto unlock;
- }
- } else if (neigh->ah) {
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
- goto unlock;
+send_using_neigh:
+ /* note we now hold a ref to neigh */
+ if (ipoib_cm_get(neigh)) {
+ if (ipoib_cm_up(neigh)) {
+ ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
+ goto unref;
}
+ } else if (neigh->ah) {
+ ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(cb->hwaddr));
+ goto unref;
+ }
- if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
- spin_lock_irqsave(&priv->lock, flags);
- __skb_queue_tail(&neigh->queue, skb);
- spin_unlock_irqrestore(&priv->lock, flags);
- } else {
- ++dev->stats.tx_dropped;
- dev_kfree_skb_any(skb);
- }
+ if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+ spin_lock_irqsave(&priv->lock, flags);
+ __skb_queue_tail(&neigh->queue, skb);
+ spin_unlock_irqrestore(&priv->lock, flags);
} else {
- struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
-
- if (cb->hwaddr[4] == 0xff) {
- /* Add in the P_Key for multicast*/
- cb->hwaddr[8] = (priv->pkey >> 8) & 0xff;
- cb->hwaddr[9] = priv->pkey & 0xff;
+ ++dev->stats.tx_dropped;
+ dev_kfree_skb_any(skb);
+ }
- ipoib_mcast_send(dev, cb->hwaddr + 4, skb);
- } else {
- /* unicast GID -- should be ARP or RARP reply */
-
- if ((be16_to_cpup((__be16 *) skb->data) != ETH_P_ARP) &&
- (be16_to_cpup((__be16 *) skb->data) != ETH_P_RARP)) {
- ipoib_warn(priv, "Unicast, no %s: type %04x, QPN %06x %pI6\n",
- skb_dst(skb) ? "neigh" : "dst",
- be16_to_cpup((__be16 *) skb->data),
- IPOIB_QPN(cb->hwaddr),
- cb->hwaddr + 4);
- dev_kfree_skb_any(skb);
- ++dev->stats.tx_dropped;
- goto unlock;
- }
+unref:
+ ipoib_neigh_put(neigh);
- unicast_arp_send(skb, dev, cb);
- }
- }
-unlock:
- if (n)
- neigh_release(n);
- rcu_read_unlock();
return NETDEV_TX_OK;
}
@@ -821,6 +770,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
const void *daddr, const void *saddr, unsigned len)
{
struct ipoib_header *header;
+ struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -828,14 +778,11 @@ static int ipoib_hard_header(struct sk_buff *skb,
header->reserved = 0;
/*
- * If we don't have a dst_entry structure, stuff the
+ * we don't rely on dst_entry structure, always stuff the
* destination address into skb->cb so we can figure out where
* to send the packet later.
*/
- if (!skb_dst(skb)) {
- struct ipoib_cb *cb = (struct ipoib_cb *) skb->cb;
- memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
- }
+ memcpy(cb->hwaddr, daddr, INFINIBAND_ALEN);
return 0;
}
@@ -852,86 +799,433 @@ static void ipoib_set_mcast_list(struct net_device *dev)
queue_work(ipoib_workqueue, &priv->restart_task);
}
-static void ipoib_neigh_cleanup(struct neighbour *n)
+static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr)
{
- struct ipoib_neigh *neigh;
- struct ipoib_dev_priv *priv = netdev_priv(n->dev);
+ /*
+ * Use only the address parts that contributes to spreading
+ * The subnet prefix is not used as one can not connect to
+ * same remote port (GUID) using the same remote QPN via two
+ * different subnets.
+ */
+ /* qpn octets[1:4) & port GUID octets[12:20) */
+ u32 *daddr_32 = (u32 *) daddr;
+ u32 hv;
+
+ hv = jhash_3words(daddr_32[3], daddr_32[4], 0xFFFFFF & daddr_32[0], 0);
+ return hv & htbl->mask;
+}
+
+struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh *neigh = NULL;
+ u32 hash_val;
+
+ rcu_read_lock_bh();
+
+ htbl = rcu_dereference_bh(ntbl->htbl);
+
+ if (!htbl)
+ goto out_unlock;
+
+ hash_val = ipoib_addr_hash(htbl, daddr);
+ for (neigh = rcu_dereference_bh(htbl->buckets[hash_val]);
+ neigh != NULL;
+ neigh = rcu_dereference_bh(neigh->hnext)) {
+ if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+ /* found, take one ref on behalf of the caller */
+ if (!atomic_inc_not_zero(&neigh->refcnt)) {
+ /* deleted */
+ neigh = NULL;
+ goto out_unlock;
+ }
+ neigh->alive = jiffies;
+ goto out_unlock;
+ }
+ }
+
+out_unlock:
+ rcu_read_unlock_bh();
+ return neigh;
+}
+
+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
+{
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long neigh_obsolete;
+ unsigned long dt;
unsigned long flags;
- struct ipoib_ah *ah = NULL;
+ int i;
- neigh = *to_ipoib_neigh(n);
- if (neigh)
- priv = netdev_priv(neigh->dev);
- else
+ if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
return;
- ipoib_dbg(priv,
- "neigh_cleanup for %06x %pI6\n",
- IPOIB_QPN(n->ha),
- n->ha + 4);
spin_lock_irqsave(&priv->lock, flags);
- if (neigh->ah)
- ah = neigh->ah;
- list_del(&neigh->list);
- ipoib_neigh_free(n->dev, neigh);
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&priv->lock));
+
+ if (!htbl)
+ goto out_unlock;
+
+ /* neigh is obsolete if it was idle for two GC periods */
+ dt = 2 * arp_tbl.gc_interval;
+ neigh_obsolete = jiffies - dt;
+ /* handle possible race condition */
+ if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+ goto out_unlock;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&priv->lock))) != NULL) {
+ /* was the neigh idle for two GC periods */
+ if (time_after(neigh_obsolete, neigh->alive)) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&priv->lock)));
+ /* remove from path/mc list */
+ list_del(&neigh->list);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ } else {
+ np = &neigh->hnext;
+ }
+ }
+ }
+
+out_unlock:
spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipoib_reap_neigh(struct work_struct *work)
+{
+ struct ipoib_dev_priv *priv =
+ container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
+
+ __ipoib_reap_neigh(priv);
- if (ah)
- ipoib_put_ah(ah);
+ if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
+ queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+ arp_tbl.gc_interval);
}
-struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour,
+
+static struct ipoib_neigh *ipoib_neigh_ctor(u8 *daddr,
struct net_device *dev)
{
struct ipoib_neigh *neigh;
- neigh = kmalloc(sizeof *neigh, GFP_ATOMIC);
+ neigh = kzalloc(sizeof *neigh, GFP_ATOMIC);
if (!neigh)
return NULL;
- neigh->neighbour = neighbour;
neigh->dev = dev;
- memset(&neigh->dgid.raw, 0, sizeof (union ib_gid));
- *to_ipoib_neigh(neighbour) = neigh;
+ memcpy(&neigh->daddr, daddr, sizeof(neigh->daddr));
skb_queue_head_init(&neigh->queue);
+ INIT_LIST_HEAD(&neigh->list);
ipoib_cm_set(neigh, NULL);
+ /* one ref on behalf of the caller */
+ atomic_set(&neigh->refcnt, 1);
+
+ return neigh;
+}
+
+struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
+ struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh *neigh;
+ u32 hash_val;
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&priv->lock));
+ if (!htbl) {
+ neigh = NULL;
+ goto out_unlock;
+ }
+
+ /* need to add a new neigh, but maybe some other thread succeeded?
+ * recalc hash, maybe hash resize took place so we do a search
+ */
+ hash_val = ipoib_addr_hash(htbl, daddr);
+ for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
+ lockdep_is_held(&priv->lock));
+ neigh != NULL;
+ neigh = rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&priv->lock))) {
+ if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
+ /* found, take one ref on behalf of the caller */
+ if (!atomic_inc_not_zero(&neigh->refcnt)) {
+ /* deleted */
+ neigh = NULL;
+ break;
+ }
+ neigh->alive = jiffies;
+ goto out_unlock;
+ }
+ }
+
+ neigh = ipoib_neigh_ctor(daddr, dev);
+ if (!neigh)
+ goto out_unlock;
+
+ /* one ref on behalf of the hash table */
+ atomic_inc(&neigh->refcnt);
+ neigh->alive = jiffies;
+ /* put in hash */
+ rcu_assign_pointer(neigh->hnext,
+ rcu_dereference_protected(htbl->buckets[hash_val],
+ lockdep_is_held(&priv->lock)));
+ rcu_assign_pointer(htbl->buckets[hash_val], neigh);
+ atomic_inc(&ntbl->entries);
+
+out_unlock:
return neigh;
}
-void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh)
+void ipoib_neigh_dtor(struct ipoib_neigh *neigh)
{
+ /* neigh reference count was dropprd to zero */
+ struct net_device *dev = neigh->dev;
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
struct sk_buff *skb;
- *to_ipoib_neigh(neigh->neighbour) = NULL;
+ if (neigh->ah)
+ ipoib_put_ah(neigh->ah);
while ((skb = __skb_dequeue(&neigh->queue))) {
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
}
if (ipoib_cm_get(neigh))
ipoib_cm_destroy_tx(ipoib_cm_get(neigh));
+ ipoib_dbg(netdev_priv(dev),
+ "neigh free for %06x %pI6\n",
+ IPOIB_QPN(neigh->daddr),
+ neigh->daddr + 4);
kfree(neigh);
+ if (atomic_dec_and_test(&priv->ntbl.entries)) {
+ if (test_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags))
+ complete(&priv->ntbl.flushed);
+ }
+}
+
+static void ipoib_neigh_reclaim(struct rcu_head *rp)
+{
+ /* Called as a result of removal from hash table */
+ struct ipoib_neigh *neigh = container_of(rp, struct ipoib_neigh, rcu);
+ /* note TX context may hold another ref */
+ ipoib_neigh_put(neigh);
+}
+
+void ipoib_neigh_free(struct ipoib_neigh *neigh)
+{
+ struct net_device *dev = neigh->dev;
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh __rcu **np;
+ struct ipoib_neigh *n;
+ u32 hash_val;
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&priv->lock));
+ if (!htbl)
+ return;
+
+ hash_val = ipoib_addr_hash(htbl, neigh->daddr);
+ np = &htbl->buckets[hash_val];
+ for (n = rcu_dereference_protected(*np,
+ lockdep_is_held(&priv->lock));
+ n != NULL;
+ n = rcu_dereference_protected(*np,
+ lockdep_is_held(&priv->lock))) {
+ if (n == neigh) {
+ /* found */
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&priv->lock)));
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ return;
+ } else {
+ np = &n->hnext;
+ }
+ }
}
-static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms)
+static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
{
- parms->neigh_cleanup = ipoib_neigh_cleanup;
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ struct ipoib_neigh **buckets;
+ u32 size;
+
+ clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+ ntbl->htbl = NULL;
+ htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
+ if (!htbl)
+ return -ENOMEM;
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ size = roundup_pow_of_two(arp_tbl.gc_thresh3);
+ buckets = kzalloc(size * sizeof(*buckets), GFP_KERNEL);
+ if (!buckets) {
+ kfree(htbl);
+ return -ENOMEM;
+ }
+ htbl->size = size;
+ htbl->mask = (size - 1);
+ htbl->buckets = buckets;
+ ntbl->htbl = htbl;
+ htbl->ntbl = ntbl;
+ atomic_set(&ntbl->entries, 0);
+
+ /* start garbage collection */
+ clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task,
+ arp_tbl.gc_interval);
return 0;
}
+static void neigh_hash_free_rcu(struct rcu_head *head)
+{
+ struct ipoib_neigh_hash *htbl = container_of(head,
+ struct ipoib_neigh_hash,
+ rcu);
+ struct ipoib_neigh __rcu **buckets = htbl->buckets;
+ struct ipoib_neigh_table *ntbl = htbl->ntbl;
+
+ kfree(buckets);
+ kfree(htbl);
+ complete(&ntbl->deleted);
+}
+
+void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long flags;
+ int i;
+
+ /* remove all neigh connected to a given path or mcast */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&priv->lock));
+
+ if (!htbl)
+ goto out_unlock;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&priv->lock))) != NULL) {
+ /* delete neighs belong to this parent */
+ if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&priv->lock)));
+ /* remove from parent list */
+ list_del(&neigh->list);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ } else {
+ np = &neigh->hnext;
+ }
+
+ }
+ }
+out_unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
+{
+ struct ipoib_neigh_table *ntbl = &priv->ntbl;
+ struct ipoib_neigh_hash *htbl;
+ unsigned long flags;
+ int i, wait_flushed = 0;
+
+ init_completion(&priv->ntbl.flushed);
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ htbl = rcu_dereference_protected(ntbl->htbl,
+ lockdep_is_held(&priv->lock));
+ if (!htbl)
+ goto out_unlock;
+
+ wait_flushed = atomic_read(&priv->ntbl.entries);
+ if (!wait_flushed)
+ goto free_htbl;
+
+ for (i = 0; i < htbl->size; i++) {
+ struct ipoib_neigh *neigh;
+ struct ipoib_neigh __rcu **np = &htbl->buckets[i];
+
+ while ((neigh = rcu_dereference_protected(*np,
+ lockdep_is_held(&priv->lock))) != NULL) {
+ rcu_assign_pointer(*np,
+ rcu_dereference_protected(neigh->hnext,
+ lockdep_is_held(&priv->lock)));
+ /* remove from path/mc list */
+ list_del(&neigh->list);
+ call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
+ }
+ }
+
+free_htbl:
+ rcu_assign_pointer(ntbl->htbl, NULL);
+ call_rcu(&htbl->rcu, neigh_hash_free_rcu);
+
+out_unlock:
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (wait_flushed)
+ wait_for_completion(&priv->ntbl.flushed);
+}
+
+static void ipoib_neigh_hash_uninit(struct net_device *dev)
+{
+ struct ipoib_dev_priv *priv = netdev_priv(dev);
+ int stopped;
+
+ ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
+ init_completion(&priv->ntbl.deleted);
+ set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
+
+ /* Stop GC if called at init fail need to cancel work */
+ stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ if (!stopped)
+ cancel_delayed_work(&priv->neigh_reap_task);
+
+ ipoib_flush_neighs(priv);
+
+ wait_for_completion(&priv->ntbl.deleted);
+}
+
+
int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ if (ipoib_neigh_hash_init(priv) < 0)
+ goto out;
/* Allocate RX/TX "rings" to hold queued skbs */
priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring,
GFP_KERNEL);
if (!priv->rx_ring) {
printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n",
ca->name, ipoib_recvq_size);
- goto out;
+ goto out_neigh_hash_cleanup;
}
priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring);
@@ -954,6 +1248,8 @@ out_tx_ring_cleanup:
out_rx_ring_cleanup:
kfree(priv->rx_ring);
+out_neigh_hash_cleanup:
+ ipoib_neigh_hash_uninit(dev);
out:
return -ENOMEM;
}
@@ -966,6 +1262,9 @@ void ipoib_dev_cleanup(struct net_device *dev)
/* Delete any child interfaces first */
list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
+ /* Stop GC on child */
+ set_bit(IPOIB_STOP_NEIGH_GC, &cpriv->flags);
+ cancel_delayed_work(&cpriv->neigh_reap_task);
unregister_netdev(cpriv->dev);
ipoib_dev_cleanup(cpriv->dev);
free_netdev(cpriv->dev);
@@ -978,6 +1277,8 @@ void ipoib_dev_cleanup(struct net_device *dev)
priv->rx_ring = NULL;
priv->tx_ring = NULL;
+
+ ipoib_neigh_hash_uninit(dev);
}
static const struct header_ops ipoib_header_ops = {
@@ -992,7 +1293,6 @@ static const struct net_device_ops ipoib_netdev_ops = {
.ndo_start_xmit = ipoib_start_xmit,
.ndo_tx_timeout = ipoib_timeout,
.ndo_set_rx_mode = ipoib_set_mcast_list,
- .ndo_neigh_setup = ipoib_neigh_setup_dev,
};
static void ipoib_setup(struct net_device *dev)
@@ -1041,6 +1341,7 @@ static void ipoib_setup(struct net_device *dev)
INIT_WORK(&priv->flush_heavy, ipoib_ib_dev_flush_heavy);
INIT_WORK(&priv->restart_task, ipoib_mcast_restart_task);
INIT_DELAYED_WORK(&priv->ah_reap_task, ipoib_reap_ah);
+ INIT_DELAYED_WORK(&priv->neigh_reap_task, ipoib_reap_neigh);
}
struct ipoib_dev_priv *ipoib_intf_alloc(const char *name)
@@ -1281,6 +1582,9 @@ sysfs_failed:
register_failed:
ib_unregister_event_handler(&priv->event_handler);
+ /* Stop GC if started before flush */
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(ipoib_workqueue);
event_failed:
@@ -1347,6 +1651,9 @@ static void ipoib_remove_one(struct ib_device *device)
dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
rtnl_unlock();
+ /* Stop GC */
+ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags);
+ cancel_delayed_work(&priv->neigh_reap_task);
flush_workqueue(ipoib_workqueue);
unregister_netdev(priv->dev);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 7cecb16d3d48..75367249f447 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -69,28 +69,13 @@ struct ipoib_mcast_iter {
static void ipoib_mcast_free(struct ipoib_mcast *mcast)
{
struct net_device *dev = mcast->dev;
- struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct ipoib_neigh *neigh, *tmp;
int tx_dropped = 0;
ipoib_dbg_mcast(netdev_priv(dev), "deleting multicast group %pI6\n",
mcast->mcmember.mgid.raw);
- spin_lock_irq(&priv->lock);
-
- list_for_each_entry_safe(neigh, tmp, &mcast->neigh_list, list) {
- /*
- * It's safe to call ipoib_put_ah() inside priv->lock
- * here, because we know that mcast->ah will always
- * hold one more reference, so ipoib_put_ah() will
- * never do more than decrement the ref count.
- */
- if (neigh->ah)
- ipoib_put_ah(neigh->ah);
- ipoib_neigh_free(dev, neigh);
- }
-
- spin_unlock_irq(&priv->lock);
+ /* remove all neigh connected to this mcast */
+ ipoib_del_neighs_by_gid(dev, mcast->mcmember.mgid.raw);
if (mcast->ah)
ipoib_put_ah(mcast->ah);
@@ -655,17 +640,12 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast)
return 0;
}
-void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
+void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
- struct dst_entry *dst = skb_dst(skb);
struct ipoib_mcast *mcast;
- struct neighbour *n;
unsigned long flags;
-
- n = NULL;
- if (dst)
- n = dst_neigh_lookup_skb(dst, skb);
+ void *mgid = daddr + 4;
spin_lock_irqsave(&priv->lock, flags);
@@ -721,28 +701,27 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb)
out:
if (mcast && mcast->ah) {
- if (n) {
- if (!*to_ipoib_neigh(n)) {
- struct ipoib_neigh *neigh;
-
- neigh = ipoib_neigh_alloc(n, skb->dev);
- if (neigh) {
- kref_get(&mcast->ah->ref);
- neigh->ah = mcast->ah;
- list_add_tail(&neigh->list,
- &mcast->neigh_list);
- }
+ struct ipoib_neigh *neigh;
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+ neigh = ipoib_neigh_get(dev, daddr);
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!neigh) {
+ neigh = ipoib_neigh_alloc(daddr, dev);
+ if (neigh) {
+ kref_get(&mcast->ah->ref);
+ neigh->ah = mcast->ah;
+ list_add_tail(&neigh->list, &mcast->neigh_list);
}
- neigh_release(n);
}
spin_unlock_irqrestore(&priv->lock, flags);
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
+ if (neigh)
+ ipoib_neigh_put(neigh);
return;
}
unlock:
- if (n)
- neigh_release(n);
spin_unlock_irqrestore(&priv->lock, flags);
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index bcbf22ee0aa7..1b5b0c730054 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -586,24 +586,62 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
scmnd->sc_data_direction);
}
-static void srp_remove_req(struct srp_target_port *target,
- struct srp_request *req, s32 req_lim_delta)
+/**
+ * srp_claim_req - Take ownership of the scmnd associated with a request.
+ * @target: SRP target port.
+ * @req: SRP request.
+ * @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take
+ * ownership of @req->scmnd if it equals @scmnd.
+ *
+ * Return value:
+ * Either NULL or a pointer to the SCSI command the caller became owner of.
+ */
+static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target,
+ struct srp_request *req,
+ struct scsi_cmnd *scmnd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&target->lock, flags);
+ if (!scmnd) {
+ scmnd = req->scmnd;
+ req->scmnd = NULL;
+ } else if (req->scmnd == scmnd) {
+ req->scmnd = NULL;
+ } else {
+ scmnd = NULL;
+ }
+ spin_unlock_irqrestore(&target->lock, flags);
+
+ return scmnd;
+}
+
+/**
+ * srp_free_req() - Unmap data and add request to the free request list.
+ */
+static void srp_free_req(struct srp_target_port *target,
+ struct srp_request *req, struct scsi_cmnd *scmnd,
+ s32 req_lim_delta)
{
unsigned long flags;
- srp_unmap_data(req->scmnd, target, req);
+ srp_unmap_data(scmnd, target, req);
+
spin_lock_irqsave(&target->lock, flags);
target->req_lim += req_lim_delta;
- req->scmnd = NULL;
list_add_tail(&req->list, &target->free_reqs);
spin_unlock_irqrestore(&target->lock, flags);
}
static void srp_reset_req(struct srp_target_port *target, struct srp_request *req)
{
- req->scmnd->result = DID_RESET << 16;
- req->scmnd->scsi_done(req->scmnd);
- srp_remove_req(target, req, 0);
+ struct scsi_cmnd *scmnd = srp_claim_req(target, req, NULL);
+
+ if (scmnd) {
+ scmnd->result = DID_RESET << 16;
+ scmnd->scsi_done(scmnd);
+ srp_free_req(target, req, scmnd, 0);
+ }
}
static int srp_reconnect_target(struct srp_target_port *target)
@@ -1073,11 +1111,18 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
complete(&target->tsk_mgmt_done);
} else {
req = &target->req_ring[rsp->tag];
- scmnd = req->scmnd;
- if (!scmnd)
+ scmnd = srp_claim_req(target, req, NULL);
+ if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
"Null scmnd for RSP w/tag %016llx\n",
(unsigned long long) rsp->tag);
+
+ spin_lock_irqsave(&target->lock, flags);
+ target->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ spin_unlock_irqrestore(&target->lock, flags);
+
+ return;
+ }
scmnd->result = rsp->status;
if (rsp->flags & SRP_RSP_FLAG_SNSVALID) {
@@ -1092,7 +1137,9 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp)
else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER))
scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt));
- srp_remove_req(target, req, be32_to_cpu(rsp->req_lim_delta));
+ srp_free_req(target, req, scmnd,
+ be32_to_cpu(rsp->req_lim_delta));
+
scmnd->host_scribble = NULL;
scmnd->scsi_done(scmnd);
}
@@ -1631,25 +1678,17 @@ static int srp_abort(struct scsi_cmnd *scmnd)
{
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
- int ret = SUCCESS;
shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
- if (!req || target->qp_in_error)
+ if (!req || target->qp_in_error || !srp_claim_req(target, req, scmnd))
return FAILED;
- if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
- SRP_TSK_ABORT_TASK))
- return FAILED;
-
- if (req->scmnd) {
- if (!target->tsk_mgmt_status) {
- srp_remove_req(target, req, 0);
- scmnd->result = DID_ABORT << 16;
- } else
- ret = FAILED;
- }
+ srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
+ SRP_TSK_ABORT_TASK);
+ srp_free_req(target, req, scmnd, 0);
+ scmnd->result = DID_ABORT << 16;
- return ret;
+ return SUCCESS;
}
static int srp_reset_device(struct scsi_cmnd *scmnd)
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 7a0ce8d42887..9e1449f8c6a2 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1469,7 +1469,7 @@ static void srpt_handle_send_comp(struct srpt_rdma_ch *ch,
*
* XXX: what is now target_execute_cmd used to be asynchronous, and unmapping
* the data that has been transferred via IB RDMA had to be postponed until the
- * check_stop_free() callback. None of this is nessecary anymore and needs to
+ * check_stop_free() callback. None of this is necessary anymore and needs to
* be cleaned up.
*/
static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch,
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index ff4c0a87a25f..cdc252612c0b 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -358,6 +358,7 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad)
/* Inhibit KDI and KRI interrupts. */
reg_val = readw(keypad->mmio_base + KPSR);
reg_val &= ~(KBD_STAT_KRIE | KBD_STAT_KDIE);
+ reg_val |= KBD_STAT_KPKR | KBD_STAT_KPKD;
writew(reg_val, keypad->mmio_base + KPSR);
/* Colums as open drain and disable all rows */
@@ -515,7 +516,9 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */
+ clk_prepare_enable(keypad->clk);
imx_keypad_inhibit(keypad);
+ clk_disable_unprepare(keypad->clk);
error = request_irq(irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
index f06231b7cab1..84ec691c05aa 100644
--- a/drivers/input/misc/ab8500-ponkey.c
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
ponkey->idev = input;
ponkey->ab8500 = ab8500;
- ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
- ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
+ ponkey->irq_dbf = irq_dbf;
+ ponkey->irq_dbr = irq_dbr;
input->name = "AB8500 POn(PowerOn) Key";
input->dev.parent = &pdev->dev;
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 3f5649f19082..a261d8576919 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -721,6 +721,17 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
case FSP_PKT_TYPE_ABS:
+
+ if ((packet[0] == 0x48 || packet[0] == 0x49) &&
+ packet[1] == 0 && packet[2] == 0) {
+ /*
+ * Ignore coordinate noise when finger leaving the
+ * surface, otherwise cursor may jump to upper-left
+ * corner.
+ */
+ packet[3] &= 0xf0;
+ }
+
abs_x = GET_ABS_X(packet);
abs_y = GET_ABS_Y(packet);
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 2ffd110bd5bc..2e77246c2e5a 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -72,7 +72,7 @@ static int amba_kmi_open(struct serio *io)
unsigned int divisor;
int ret;
- ret = clk_enable(kmi->clk);
+ ret = clk_prepare_enable(kmi->clk);
if (ret)
goto out;
@@ -92,7 +92,7 @@ static int amba_kmi_open(struct serio *io)
return 0;
clk_disable:
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
out:
return ret;
}
@@ -104,7 +104,7 @@ static void amba_kmi_close(struct serio *io)
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
}
static int __devinit amba_kmi_probe(struct amba_device *dev,
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 09a089996ded..d7a7e54f6465 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -878,7 +878,7 @@ static int __init hp_sdc_init(void)
#endif
errstr = "IRQ not available for";
- if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED|IRQF_SAMPLE_RANDOM,
+ if (request_irq(hp_sdc.irq, &hp_sdc_isr, IRQF_SHARED,
"HP SDC", &hp_sdc))
goto err1;
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 5ec774d6c82b..d6cc77a53c7e 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -177,6 +177,20 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
},
},
{
+ /* Gigabyte T1005 - defines wrong chassis type ("Other") */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T1005"),
+ },
+ },
+ {
+ /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"),
+ },
+ },
+ {
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
@@ -321,6 +335,12 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
},
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 002041975de9..532d067a9e07 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1848,7 +1848,10 @@ static const struct wacom_features wacom_features_0x2A =
{ "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047,
63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
static const struct wacom_features wacom_features_0xF4 =
- { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047,
+ { "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047,
+ 63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
+static const struct wacom_features wacom_features_0xF8 =
+ { "Wacom Cintiq 24HD touch", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047,
63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
static const struct wacom_features wacom_features_0x3F =
{ "Wacom Cintiq 21UX", WACOM_PKGLEN_INTUOS, 87200, 65600, 1023,
@@ -2091,6 +2094,7 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xEF) },
{ USB_DEVICE_WACOM(0x47) },
{ USB_DEVICE_WACOM(0xF4) },
+ { USB_DEVICE_WACOM(0xF8) },
{ USB_DEVICE_WACOM(0xFA) },
{ USB_DEVICE_LENOVO(0x6004) },
{ }
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index 9afc777a40a7..64957770b522 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -566,9 +566,12 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
}
read = min_t(size_t, count, tsdata->raw_bufsize - *off);
- error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
- if (!error)
- *off += read;
+ if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ *off += read;
out:
mutex_unlock(&tsdata->mutex);
return error ?: read;
@@ -602,6 +605,7 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
if (tsdata->debug_dir)
debugfs_remove_recursive(tsdata->debug_dir);
+ kfree(tsdata->raw_buffer);
}
#else
@@ -843,7 +847,6 @@ static int __devexit edt_ft5x06_ts_remove(struct i2c_client *client)
if (gpio_is_valid(pdata->reset_pin))
gpio_free(pdata->reset_pin);
- kfree(tsdata->raw_buffer);
kfree(tsdata);
return 0;
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 503c7096ed36..908407efc672 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -48,7 +48,7 @@ struct eeti_ts_priv {
struct input_dev *input;
struct work_struct work;
struct mutex mutex;
- int irq, irq_active_high;
+ int irq_gpio, irq, irq_active_high;
};
#define EETI_TS_BITDEPTH (11)
@@ -62,7 +62,7 @@ struct eeti_ts_priv {
static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
{
- return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high;
+ return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;
}
static void eeti_ts_read(struct work_struct *work)
@@ -157,7 +157,7 @@ static void eeti_ts_close(struct input_dev *dev)
static int __devinit eeti_ts_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
- struct eeti_ts_platform_data *pdata;
+ struct eeti_ts_platform_data *pdata = client->dev.platform_data;
struct eeti_ts_priv *priv;
struct input_dev *input;
unsigned int irq_flags;
@@ -199,9 +199,12 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,
priv->client = client;
priv->input = input;
- priv->irq = client->irq;
+ priv->irq_gpio = pdata->irq_gpio;
+ priv->irq = gpio_to_irq(pdata->irq_gpio);
- pdata = client->dev.platform_data;
+ err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
+ if (err < 0)
+ goto err1;
if (pdata)
priv->irq_active_high = pdata->irq_active_high;
@@ -215,13 +218,13 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,
err = input_register_device(input);
if (err)
- goto err1;
+ goto err2;
err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
client->name, priv);
if (err) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
- goto err2;
+ goto err3;
}
/*
@@ -233,9 +236,11 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,
device_init_wakeup(&client->dev, 0);
return 0;
-err2:
+err3:
input_unregister_device(input);
input = NULL; /* so we dont try to free it below */
+err2:
+ gpio_free(pdata->irq_gpio);
err1:
input_free_device(input);
kfree(priv);
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index e32709e0dd65..721fdb3597ca 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -304,6 +304,45 @@ static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#define EGALAX_PKT_TYPE_REPT 0x80
#define EGALAX_PKT_TYPE_DIAG 0x0A
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ unsigned char *buf;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ /*
+ * An eGalax diagnostic packet kicks the device into using the right
+ * protocol. We send a "check active" packet. The response will be
+ * read later and ignored.
+ */
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = EGALAX_PKT_TYPE_DIAG;
+ buf[1] = 1; /* length */
+ buf[2] = 'A'; /* command - check active */
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 3,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret >= 0) {
+ ret = 0;
+ break;
+ }
+ if (ret != -EPIPE)
+ break;
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
@@ -1056,6 +1095,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.process_pkt = usbtouch_process_multi,
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
+ .init = egalax_init,
},
#endif
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 6d1cbdfc9b2a..e89daf1b21b4 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -266,7 +266,7 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
static int iommu_init_device(struct device *dev)
{
- struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev);
+ struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
struct iommu_dev_data *dev_data;
struct iommu_group *group;
u16 alias;
@@ -293,11 +293,18 @@ static int iommu_init_device(struct device *dev)
dev_data->alias_data = alias_data;
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
- } else
+ }
+
+ if (dma_pdev == NULL)
dma_pdev = pci_dev_get(pdev);
+ /* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
+ /*
+ * If it's a multifunction device that does not support our
+ * required ACS flags, add to the same group as function 0.
+ */
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
swap_pci_ref(&dma_pdev,
@@ -305,14 +312,28 @@ static int iommu_init_device(struct device *dev)
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
0)));
+ /*
+ * Devices on the root bus go through the iommu. If that's not us,
+ * find the next upstream device and test ACS up to the root bus.
+ * Finding the next device may require skipping virtual buses.
+ */
while (!pci_is_root_bus(dma_pdev->bus)) {
- if (pci_acs_path_enabled(dma_pdev->bus->self,
- NULL, REQ_ACS_FLAGS))
+ struct pci_bus *bus = dma_pdev->bus;
+
+ while (!bus->self) {
+ if (!pci_is_root_bus(bus))
+ bus = bus->parent;
+ else
+ goto root_bus;
+ }
+
+ if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
- swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self));
+ swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
+root_bus:
group = iommu_group_get(&dma_pdev->dev);
pci_dev_put(dma_pdev);
if (!group) {
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 500e7f15f5c2..18a89b760aaa 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1111,7 +1111,7 @@ static void print_iommu_info(void)
if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
pr_info("AMD-Vi: Extended features: ");
- for (i = 0; ARRAY_SIZE(feat_str); ++i) {
+ for (i = 0; i < ARRAY_SIZE(feat_str); ++i) {
if (iommu_feature(iommu, (1ULL << i)))
pr_cont(" %s", feat_str[i]);
}
@@ -1131,9 +1131,6 @@ static int __init amd_iommu_init_pci(void)
break;
}
- /* Make sure ACS will be enabled */
- pci_request_acs();
-
ret = amd_iommu_init_devices();
print_iommu_info();
@@ -1652,6 +1649,9 @@ static bool detect_ivrs(void)
early_acpi_os_unmap_memory((char __iomem *)ivrs_base, ivrs_size);
+ /* Make sure ACS will be enabled during PCI probe */
+ pci_request_acs();
+
return true;
}
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 45350ff5e93c..80bad32aa463 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -732,9 +732,9 @@ static int exynos_iommu_domain_init(struct iommu_domain *domain)
spin_lock_init(&priv->pgtablelock);
INIT_LIST_HEAD(&priv->clients);
- dom->geometry.aperture_start = 0;
- dom->geometry.aperture_end = ~0UL;
- dom->geometry.force_aperture = true;
+ domain->geometry.aperture_start = 0;
+ domain->geometry.aperture_end = ~0UL;
+ domain->geometry.force_aperture = true;
domain->priv = priv;
return 0;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 7469b5346643..2297ec193eb4 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2008,6 +2008,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw)
if (!drhd) {
printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n",
pci_name(pdev));
+ free_domain_mem(domain);
return NULL;
}
iommu = drhd->iommu;
@@ -4124,8 +4125,13 @@ static int intel_iommu_add_device(struct device *dev)
} else
dma_pdev = pci_dev_get(pdev);
+ /* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
+ /*
+ * If it's a multifunction device that does not support our
+ * required ACS flags, add to the same group as function 0.
+ */
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
swap_pci_ref(&dma_pdev,
@@ -4133,14 +4139,28 @@ static int intel_iommu_add_device(struct device *dev)
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
0)));
+ /*
+ * Devices on the root bus go through the iommu. If that's not us,
+ * find the next upstream device and test ACS up to the root bus.
+ * Finding the next device may require skipping virtual buses.
+ */
while (!pci_is_root_bus(dma_pdev->bus)) {
- if (pci_acs_path_enabled(dma_pdev->bus->self,
- NULL, REQ_ACS_FLAGS))
+ struct pci_bus *bus = dma_pdev->bus;
+
+ while (!bus->self) {
+ if (!pci_is_root_bus(bus))
+ bus = bus->parent;
+ else
+ goto root_bus;
+ }
+
+ if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
- swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self));
+ swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
+root_bus:
group = iommu_group_get(&dma_pdev->dev);
pci_dev_put(dma_pdev);
if (!group) {
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index e0b18f3ae9a8..af8904de1d44 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -736,6 +736,7 @@ int __init parse_ioapics_under_ir(void)
{
struct dmar_drhd_unit *drhd;
int ir_supported = 0;
+ int ioapic_idx;
for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu;
@@ -748,13 +749,20 @@ int __init parse_ioapics_under_ir(void)
}
}
- if (ir_supported && ir_ioapic_num != nr_ioapics) {
- printk(KERN_WARNING
- "Not all IO-APIC's listed under remapping hardware\n");
- return -1;
+ if (!ir_supported)
+ return 0;
+
+ for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
+ int ioapic_id = mpc_ioapic_id(ioapic_idx);
+ if (!map_ioapic_to_ir(ioapic_id)) {
+ pr_err(FW_BUG "ioapic %d has no mapping iommu, "
+ "interrupt remapping will be disabled\n",
+ ioapic_id);
+ return -1;
+ }
}
- return ir_supported;
+ return 1;
}
int __init ir_dev_scope_init(void)
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 4ba325ab6262..2a4bb36bc688 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -799,14 +799,14 @@ static void smmu_iommu_detach_dev(struct iommu_domain *domain,
goto out;
}
}
- dev_err(smmu->dev, "Couldn't find %s\n", dev_name(c->dev));
+ dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev));
out:
spin_unlock(&as->client_lock);
}
static int smmu_iommu_domain_init(struct iommu_domain *domain)
{
- int i, err = -ENODEV;
+ int i, err = -EAGAIN;
unsigned long flags;
struct smmu_as *as;
struct smmu_device *smmu = smmu_handle;
@@ -814,11 +814,14 @@ static int smmu_iommu_domain_init(struct iommu_domain *domain)
/* Look for a free AS with lock held */
for (i = 0; i < smmu->num_as; i++) {
as = &smmu->as[i];
- if (!as->pdir_page) {
- err = alloc_pdir(as);
- if (!err)
- goto found;
- }
+
+ if (as->pdir_page)
+ continue;
+
+ err = alloc_pdir(as);
+ if (!err)
+ goto found;
+
if (err != -EAGAIN)
break;
}
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
index fa6ca4733725..dceaec821b0e 100644
--- a/drivers/isdn/hardware/mISDN/avmfritz.c
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -857,8 +857,9 @@ avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ cancel_work_sync(&bch->workq);
spin_lock_irqsave(&fc->lock, flags);
- mISDN_freebchannel(bch);
+ mISDN_clear_bchannel(bch);
modehdlc(bch, ISDN_P_NONE);
spin_unlock_irqrestore(&fc->lock, flags);
ch->protocol = ISDN_P_NONE;
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index 5e402cf2e795..f02794203bb1 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -5059,6 +5059,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev,
printk(KERN_INFO
"HFC-E1 #%d has overlapping B-channels on fragment #%d\n",
E1_cnt + 1, pt);
+ kfree(hc);
return -EINVAL;
}
maskcheck |= hc->bmask[pt];
@@ -5086,6 +5087,7 @@ hfcmulti_init(struct hm_map *m, struct pci_dev *pdev,
if ((poll >> 1) > sizeof(hc->silence_data)) {
printk(KERN_ERR "HFCMULTI error: silence_data too small, "
"please fix\n");
+ kfree(hc);
return -EINVAL;
}
for (i = 0; i < (poll >> 1); i++)
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
index 752e0825591f..ccd7d851be26 100644
--- a/drivers/isdn/hardware/mISDN/mISDNipac.c
+++ b/drivers/isdn/hardware/mISDN/mISDNipac.c
@@ -1406,8 +1406,9 @@ hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ cancel_work_sync(&bch->workq);
spin_lock_irqsave(hx->ip->hwlock, flags);
- mISDN_freebchannel(bch);
+ mISDN_clear_bchannel(bch);
hscx_mode(hx, ISDN_P_NONE);
spin_unlock_irqrestore(hx->ip->hwlock, flags);
ch->protocol = ISDN_P_NONE;
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
index be5973ded6d6..182ecf0626c2 100644
--- a/drivers/isdn/hardware/mISDN/mISDNisar.c
+++ b/drivers/isdn/hardware/mISDN/mISDNisar.c
@@ -1588,8 +1588,9 @@ isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ cancel_work_sync(&bch->workq);
spin_lock_irqsave(ich->is->hwlock, flags);
- mISDN_freebchannel(bch);
+ mISDN_clear_bchannel(bch);
modeisar(ich, ISDN_P_NONE);
spin_unlock_irqrestore(ich->is->hwlock, flags);
ch->protocol = ISDN_P_NONE;
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
index c3e3e7686273..9bcade59eb73 100644
--- a/drivers/isdn/hardware/mISDN/netjet.c
+++ b/drivers/isdn/hardware/mISDN/netjet.c
@@ -812,8 +812,9 @@ nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ cancel_work_sync(&bch->workq);
spin_lock_irqsave(&card->lock, flags);
- mISDN_freebchannel(bch);
+ mISDN_clear_bchannel(bch);
mode_tiger(bc, ISDN_P_NONE);
spin_unlock_irqrestore(&card->lock, flags);
ch->protocol = ISDN_P_NONE;
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
index 26a86b846099..335fe6455002 100644
--- a/drivers/isdn/hardware/mISDN/w6692.c
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -1054,8 +1054,9 @@ w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ cancel_work_sync(&bch->workq);
spin_lock_irqsave(&card->lock, flags);
- mISDN_freebchannel(bch);
+ mISDN_clear_bchannel(bch);
w6692_mode(bc, ISDN_P_NONE);
spin_unlock_irqrestore(&card->lock, flags);
ch->protocol = ISDN_P_NONE;
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index 5405ec644db3..baf2686aa8eb 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -16,7 +16,6 @@
#include <linux/sched.h>
#include "isdnloop.h"
-static char *revision = "$Revision: 1.11.6.7 $";
static char *isdnloop_id = "loop0";
MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
@@ -1494,17 +1493,6 @@ isdnloop_addcard(char *id1)
static int __init
isdnloop_init(void)
{
- char *p;
- char rev[10];
-
- if ((p = strchr(revision, ':'))) {
- strcpy(rev, p + 1);
- p = strchr(rev, '$');
- *p = 0;
- } else
- strcpy(rev, " ??? ");
- printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
-
if (isdnloop_id)
return (isdnloop_addcard(isdnloop_id));
diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
index ef34fd40867c..2602be23f341 100644
--- a/drivers/isdn/mISDN/hwchannel.c
+++ b/drivers/isdn/mISDN/hwchannel.c
@@ -148,17 +148,16 @@ mISDN_clear_bchannel(struct bchannel *ch)
ch->next_minlen = ch->init_minlen;
ch->maxlen = ch->init_maxlen;
ch->next_maxlen = ch->init_maxlen;
+ skb_queue_purge(&ch->rqueue);
+ ch->rcount = 0;
}
EXPORT_SYMBOL(mISDN_clear_bchannel);
-int
+void
mISDN_freebchannel(struct bchannel *ch)
{
+ cancel_work_sync(&ch->workq);
mISDN_clear_bchannel(ch);
- skb_queue_purge(&ch->rqueue);
- ch->rcount = 0;
- flush_work_sync(&ch->workq);
- return 0;
}
EXPORT_SYMBOL(mISDN_freebchannel);
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
index 0dc8abca1407..949cabb88f1c 100644
--- a/drivers/isdn/mISDN/layer2.c
+++ b/drivers/isdn/mISDN/layer2.c
@@ -2222,7 +2222,7 @@ create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, int tei,
InitWin(l2);
l2->l2m.fsm = &l2fsm;
if (test_bit(FLG_LAPB, &l2->flag) ||
- test_bit(FLG_PTP, &l2->flag) ||
+ test_bit(FLG_FIXED_TEI, &l2->flag) ||
test_bit(FLG_LAPD_NET, &l2->flag))
l2->l2m.state = ST_L2_4;
else
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 6157cbbf4113..363975b3c925 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -224,7 +224,7 @@ void led_trigger_event(struct led_trigger *trig,
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
- led_set_brightness(led_cdev, brightness);
+ __led_set_brightness(led_cdev, brightness);
}
read_unlock(&trig->leddev_list_lock);
}
diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c
index 53bd136f1ef0..0ade6ebfc914 100644
--- a/drivers/leds/leds-lp8788.c
+++ b/drivers/leds/leds-lp8788.c
@@ -63,7 +63,7 @@ static int lp8788_led_init_device(struct lp8788_led *led,
/* scale configuration */
addr = LP8788_ISINK_CTRL;
mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
- val = cfg->scale << cfg->num;
+ val = cfg->scale << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
ret = lp8788_update_bits(led->lp, addr, mask, val);
if (ret)
return ret;
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
index 9ee12c28059a..771ea067e680 100644
--- a/drivers/leds/leds-renesas-tpu.c
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -247,7 +247,7 @@ static int __devinit r_tpu_probe(struct platform_device *pdev)
if (!cfg) {
dev_err(&pdev->dev, "missing platform data\n");
- goto err0;
+ return -ENODEV;
}
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 1eee45b69b71..d949b781f6f8 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -268,13 +268,14 @@ config DM_MIRROR
needed for live data migration tools such as 'pvmove'.
config DM_RAID
- tristate "RAID 1/4/5/6 target"
+ tristate "RAID 1/4/5/6/10 target"
depends on BLK_DEV_DM
select MD_RAID1
+ select MD_RAID10
select MD_RAID456
select BLK_DEV_MD
---help---
- A dm target that supports RAID1, RAID4, RAID5 and RAID6 mappings
+ A dm target that supports RAID1, RAID10, RAID4, RAID5 and RAID6 mappings
A RAID-5 set of N drives with a capacity of C MB per drive provides
the capacity of C * (N - 1) MB, and protects against a failure
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 15dbe03117e4..94e7f6ba2e11 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -1305,7 +1305,7 @@ int bitmap_startwrite(struct bitmap *bitmap, sector_t offset, unsigned long sect
prepare_to_wait(&bitmap->overflow_wait, &__wait,
TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&bitmap->counts.lock);
- io_schedule();
+ schedule();
finish_wait(&bitmap->overflow_wait, &__wait);
continue;
}
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index d8abb90a6c2f..034233eefc82 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1555,6 +1555,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
unsigned long arg)
{
struct multipath *m = ti->private;
+ struct pgpath *pgpath;
struct block_device *bdev;
fmode_t mode;
unsigned long flags;
@@ -1570,12 +1571,14 @@ again:
if (!m->current_pgpath)
__choose_pgpath(m, 0);
- if (m->current_pgpath) {
- bdev = m->current_pgpath->path.dev->bdev;
- mode = m->current_pgpath->path.dev->mode;
+ pgpath = m->current_pgpath;
+
+ if (pgpath) {
+ bdev = pgpath->path.dev->bdev;
+ mode = pgpath->path.dev->mode;
}
- if (m->queue_io)
+ if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -EAGAIN;
else if (!bdev)
r = -EIO;
diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c
index f2f29c526544..982e3e390c45 100644
--- a/drivers/md/dm-raid.c
+++ b/drivers/md/dm-raid.c
@@ -11,6 +11,7 @@
#include "md.h"
#include "raid1.h"
#include "raid5.h"
+#include "raid10.h"
#include "bitmap.h"
#include <linux/device-mapper.h>
@@ -52,7 +53,10 @@ struct raid_dev {
#define DMPF_MAX_RECOVERY_RATE 0x20
#define DMPF_MAX_WRITE_BEHIND 0x40
#define DMPF_STRIPE_CACHE 0x80
-#define DMPF_REGION_SIZE 0X100
+#define DMPF_REGION_SIZE 0x100
+#define DMPF_RAID10_COPIES 0x200
+#define DMPF_RAID10_FORMAT 0x400
+
struct raid_set {
struct dm_target *ti;
@@ -76,6 +80,7 @@ static struct raid_type {
const unsigned algorithm; /* RAID algorithm. */
} raid_types[] = {
{"raid1", "RAID1 (mirroring)", 0, 2, 1, 0 /* NONE */},
+ {"raid10", "RAID10 (striped mirrors)", 0, 2, 10, UINT_MAX /* Varies */},
{"raid4", "RAID4 (dedicated parity disk)", 1, 2, 5, ALGORITHM_PARITY_0},
{"raid5_la", "RAID5 (left asymmetric)", 1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
{"raid5_ra", "RAID5 (right asymmetric)", 1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -86,6 +91,17 @@ static struct raid_type {
{"raid6_nc", "RAID6 (N continue)", 2, 4, 6, ALGORITHM_ROTATING_N_CONTINUE}
};
+static unsigned raid10_md_layout_to_copies(int layout)
+{
+ return layout & 0xFF;
+}
+
+static int raid10_format_to_md_layout(char *format, unsigned copies)
+{
+ /* 1 "far" copy, and 'copies' "near" copies */
+ return (1 << 8) | (copies & 0xFF);
+}
+
static struct raid_type *get_raid_type(char *name)
{
int i;
@@ -339,10 +355,16 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size)
* [max_write_behind <sectors>] See '-write-behind=' (man mdadm)
* [stripe_cache <sectors>] Stripe cache size for higher RAIDs
* [region_size <sectors>] Defines granularity of bitmap
+ *
+ * RAID10-only options:
+ * [raid10_copies <# copies>] Number of copies. (Default: 2)
+ * [raid10_format <near>] Layout algorithm. (Default: near)
*/
static int parse_raid_params(struct raid_set *rs, char **argv,
unsigned num_raid_params)
{
+ char *raid10_format = "near";
+ unsigned raid10_copies = 2;
unsigned i, rebuild_cnt = 0;
unsigned long value, region_size = 0;
sector_t sectors_per_dev = rs->ti->len;
@@ -416,11 +438,28 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
}
key = argv[i++];
+
+ /* Parameters that take a string value are checked here. */
+ if (!strcasecmp(key, "raid10_format")) {
+ if (rs->raid_type->level != 10) {
+ rs->ti->error = "'raid10_format' is an invalid parameter for this RAID type";
+ return -EINVAL;
+ }
+ if (strcmp("near", argv[i])) {
+ rs->ti->error = "Invalid 'raid10_format' value given";
+ return -EINVAL;
+ }
+ raid10_format = argv[i];
+ rs->print_flags |= DMPF_RAID10_FORMAT;
+ continue;
+ }
+
if (strict_strtoul(argv[i], 10, &value) < 0) {
rs->ti->error = "Bad numerical argument given in raid params";
return -EINVAL;
}
+ /* Parameters that take a numeric value are checked here */
if (!strcasecmp(key, "rebuild")) {
rebuild_cnt++;
@@ -439,6 +478,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
return -EINVAL;
}
break;
+ case 10:
default:
DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name);
rs->ti->error = "Rebuild not supported for this RAID type";
@@ -495,7 +535,8 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
*/
value /= 2;
- if (rs->raid_type->level < 5) {
+ if ((rs->raid_type->level != 5) &&
+ (rs->raid_type->level != 6)) {
rs->ti->error = "Inappropriate argument: stripe_cache";
return -EINVAL;
}
@@ -520,6 +561,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
} else if (!strcasecmp(key, "region_size")) {
rs->print_flags |= DMPF_REGION_SIZE;
region_size = value;
+ } else if (!strcasecmp(key, "raid10_copies") &&
+ (rs->raid_type->level == 10)) {
+ if ((value < 2) || (value > 0xFF)) {
+ rs->ti->error = "Bad value for 'raid10_copies'";
+ return -EINVAL;
+ }
+ rs->print_flags |= DMPF_RAID10_COPIES;
+ raid10_copies = value;
} else {
DMERR("Unable to parse RAID parameter: %s", key);
rs->ti->error = "Unable to parse RAID parameters";
@@ -538,8 +587,22 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
if (dm_set_target_max_io_len(rs->ti, max_io_len))
return -EINVAL;
- if ((rs->raid_type->level > 1) &&
- sector_div(sectors_per_dev, (rs->md.raid_disks - rs->raid_type->parity_devs))) {
+ if (rs->raid_type->level == 10) {
+ if (raid10_copies > rs->md.raid_disks) {
+ rs->ti->error = "Not enough devices to satisfy specification";
+ return -EINVAL;
+ }
+
+ /* (Len * #mirrors) / #devices */
+ sectors_per_dev = rs->ti->len * raid10_copies;
+ sector_div(sectors_per_dev, rs->md.raid_disks);
+
+ rs->md.layout = raid10_format_to_md_layout(raid10_format,
+ raid10_copies);
+ rs->md.new_layout = rs->md.layout;
+ } else if ((rs->raid_type->level > 1) &&
+ sector_div(sectors_per_dev,
+ (rs->md.raid_disks - rs->raid_type->parity_devs))) {
rs->ti->error = "Target length not divisible by number of data devices";
return -EINVAL;
}
@@ -566,6 +629,9 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
if (rs->raid_type->level == 1)
return md_raid1_congested(&rs->md, bits);
+ if (rs->raid_type->level == 10)
+ return md_raid10_congested(&rs->md, bits);
+
return md_raid5_congested(&rs->md, bits);
}
@@ -884,6 +950,9 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
case 6:
redundancy = rs->raid_type->parity_devs;
break;
+ case 10:
+ redundancy = raid10_md_layout_to_copies(mddev->layout) - 1;
+ break;
default:
ti->error = "Unknown RAID type";
return -EINVAL;
@@ -1049,12 +1118,19 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
goto bad;
}
+ if (ti->len != rs->md.array_sectors) {
+ ti->error = "Array size does not match requested target length";
+ ret = -EINVAL;
+ goto size_mismatch;
+ }
rs->callbacks.congested_fn = raid_is_congested;
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
mddev_suspend(&rs->md);
return 0;
+size_mismatch:
+ md_stop(&rs->md);
bad:
context_free(rs);
@@ -1203,6 +1279,13 @@ static int raid_status(struct dm_target *ti, status_type_t type,
DMEMIT(" region_size %lu",
rs->md.bitmap_info.chunksize >> 9);
+ if (rs->print_flags & DMPF_RAID10_COPIES)
+ DMEMIT(" raid10_copies %u",
+ raid10_md_layout_to_copies(rs->md.layout));
+
+ if (rs->print_flags & DMPF_RAID10_FORMAT)
+ DMEMIT(" raid10_format near");
+
DMEMIT(" %d", rs->md.raid_disks);
for (i = 0; i < rs->md.raid_disks; i++) {
if (rs->dev[i].meta_dev)
@@ -1277,7 +1360,7 @@ static void raid_resume(struct dm_target *ti)
static struct target_type raid_target = {
.name = "raid",
- .version = {1, 2, 0},
+ .version = {1, 3, 0},
.module = THIS_MODULE,
.ctr = raid_ctr,
.dtr = raid_dtr,
@@ -1304,6 +1387,8 @@ module_init(dm_raid_init);
module_exit(dm_raid_exit);
MODULE_DESCRIPTION(DM_NAME " raid4/5/6 target");
+MODULE_ALIAS("dm-raid1");
+MODULE_ALIAS("dm-raid10");
MODULE_ALIAS("dm-raid4");
MODULE_ALIAS("dm-raid5");
MODULE_ALIAS("dm-raid6");
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index f90069029aae..100368eb7991 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1212,6 +1212,41 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
return &t->targets[(KEYS_PER_NODE * n) + k];
}
+static int count_device(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ unsigned *num_devices = data;
+
+ (*num_devices)++;
+
+ return 0;
+}
+
+/*
+ * Check whether a table has no data devices attached using each
+ * target's iterate_devices method.
+ * Returns false if the result is unknown because a target doesn't
+ * support iterate_devices.
+ */
+bool dm_table_has_no_data_devices(struct dm_table *table)
+{
+ struct dm_target *uninitialized_var(ti);
+ unsigned i = 0, num_devices = 0;
+
+ while (i < dm_table_get_num_targets(table)) {
+ ti = dm_table_get_target(table, i++);
+
+ if (!ti->type->iterate_devices)
+ return false;
+
+ ti->type->iterate_devices(ti, count_device, &num_devices);
+ if (num_devices)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Establish the new table's queue_limits and validate them.
*/
@@ -1354,17 +1389,25 @@ static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
return q && blk_queue_nonrot(q);
}
-static bool dm_table_is_nonrot(struct dm_table *t)
+static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && !blk_queue_add_random(q);
+}
+
+static bool dm_table_all_devices_attribute(struct dm_table *t,
+ iterate_devices_callout_fn func)
{
struct dm_target *ti;
unsigned i = 0;
- /* Ensure that all underlying device are non-rotational. */
while (i < dm_table_get_num_targets(t)) {
ti = dm_table_get_target(t, i++);
if (!ti->type->iterate_devices ||
- !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+ !ti->type->iterate_devices(ti, func, NULL))
return 0;
}
@@ -1396,7 +1439,8 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_discard_zeroes_data(t))
q->limits.discard_zeroes_data = 0;
- if (dm_table_is_nonrot(t))
+ /* Ensure that all underlying devices are non-rotational. */
+ if (dm_table_all_devices_attribute(t, device_is_nonrot))
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
else
queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
@@ -1404,6 +1448,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
dm_table_set_integrity(t);
/*
+ * Determine whether or not this queue's I/O timings contribute
+ * to the entropy pool, Only request-based targets use this.
+ * Clear QUEUE_FLAG_ADD_RANDOM if any underlying device does not
+ * have it set.
+ */
+ if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
+ queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+
+ /*
* QUEUE_FLAG_STACKABLE must be set after all queue settings are
* visible to other CPUs because, once the flag is set, incoming bios
* are processed by request-based dm, which refers to the queue
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index af1fc3b2c2ad..c29410af1e22 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -509,9 +509,9 @@ enum pool_mode {
struct pool_features {
enum pool_mode mode;
- unsigned zero_new_blocks:1;
- unsigned discard_enabled:1;
- unsigned discard_passdown:1;
+ bool zero_new_blocks:1;
+ bool discard_enabled:1;
+ bool discard_passdown:1;
};
struct thin_c;
@@ -580,7 +580,8 @@ struct pool_c {
struct dm_target_callbacks callbacks;
dm_block_t low_water_blocks;
- struct pool_features pf;
+ struct pool_features requested_pf; /* Features requested during table load */
+ struct pool_features adjusted_pf; /* Features used after adjusting for constituent devices */
};
/*
@@ -1839,6 +1840,47 @@ static void __requeue_bios(struct pool *pool)
/*----------------------------------------------------------------
* Binding of control targets to a pool object
*--------------------------------------------------------------*/
+static bool data_dev_supports_discard(struct pool_c *pt)
+{
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+ return q && blk_queue_discard(q);
+}
+
+/*
+ * If discard_passdown was enabled verify that the data device
+ * supports discards. Disable discard_passdown if not.
+ */
+static void disable_passdown_if_not_supported(struct pool_c *pt)
+{
+ struct pool *pool = pt->pool;
+ struct block_device *data_bdev = pt->data_dev->bdev;
+ struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
+ sector_t block_size = pool->sectors_per_block << SECTOR_SHIFT;
+ const char *reason = NULL;
+ char buf[BDEVNAME_SIZE];
+
+ if (!pt->adjusted_pf.discard_passdown)
+ return;
+
+ if (!data_dev_supports_discard(pt))
+ reason = "discard unsupported";
+
+ else if (data_limits->max_discard_sectors < pool->sectors_per_block)
+ reason = "max discard sectors smaller than a block";
+
+ else if (data_limits->discard_granularity > block_size)
+ reason = "discard granularity larger than a block";
+
+ else if (block_size & (data_limits->discard_granularity - 1))
+ reason = "discard granularity not a factor of block size";
+
+ if (reason) {
+ DMWARN("Data device (%s) %s: Disabling discard passdown.", bdevname(data_bdev, buf), reason);
+ pt->adjusted_pf.discard_passdown = false;
+ }
+}
+
static int bind_control_target(struct pool *pool, struct dm_target *ti)
{
struct pool_c *pt = ti->private;
@@ -1847,31 +1889,16 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
* We want to make sure that degraded pools are never upgraded.
*/
enum pool_mode old_mode = pool->pf.mode;
- enum pool_mode new_mode = pt->pf.mode;
+ enum pool_mode new_mode = pt->adjusted_pf.mode;
if (old_mode > new_mode)
new_mode = old_mode;
pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
- pool->pf = pt->pf;
- set_pool_mode(pool, new_mode);
+ pool->pf = pt->adjusted_pf;
- /*
- * If discard_passdown was enabled verify that the data device
- * supports discards. Disable discard_passdown if not; otherwise
- * -EOPNOTSUPP will be returned.
- */
- /* FIXME: pull this out into a sep fn. */
- if (pt->pf.discard_passdown) {
- struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
- if (!q || !blk_queue_discard(q)) {
- char buf[BDEVNAME_SIZE];
- DMWARN("Discard unsupported by data device (%s): Disabling discard passdown.",
- bdevname(pt->data_dev->bdev, buf));
- pool->pf.discard_passdown = 0;
- }
- }
+ set_pool_mode(pool, new_mode);
return 0;
}
@@ -1889,9 +1916,9 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
static void pool_features_init(struct pool_features *pf)
{
pf->mode = PM_WRITE;
- pf->zero_new_blocks = 1;
- pf->discard_enabled = 1;
- pf->discard_passdown = 1;
+ pf->zero_new_blocks = true;
+ pf->discard_enabled = true;
+ pf->discard_passdown = true;
}
static void __pool_destroy(struct pool *pool)
@@ -2119,13 +2146,13 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
argc--;
if (!strcasecmp(arg_name, "skip_block_zeroing"))
- pf->zero_new_blocks = 0;
+ pf->zero_new_blocks = false;
else if (!strcasecmp(arg_name, "ignore_discard"))
- pf->discard_enabled = 0;
+ pf->discard_enabled = false;
else if (!strcasecmp(arg_name, "no_discard_passdown"))
- pf->discard_passdown = 0;
+ pf->discard_passdown = false;
else if (!strcasecmp(arg_name, "read_only"))
pf->mode = PM_READ_ONLY;
@@ -2259,8 +2286,9 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks;
- pt->pf = pf;
+ pt->adjusted_pf = pt->requested_pf = pf;
ti->num_flush_requests = 1;
+
/*
* Only need to enable discards if the pool should pass
* them down to the data device. The thin device's discard
@@ -2268,12 +2296,14 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
*/
if (pf.discard_enabled && pf.discard_passdown) {
ti->num_discard_requests = 1;
+
/*
* Setting 'discards_supported' circumvents the normal
* stacking of discard limits (this keeps the pool and
* thin devices' discard limits consistent).
*/
ti->discards_supported = true;
+ ti->discard_zeroes_data_unsupported = true;
}
ti->private = pt;
@@ -2703,7 +2733,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);
- emit_flags(&pt->pf, result, sz, maxlen);
+ emit_flags(&pt->requested_pf, result, sz, maxlen);
break;
}
@@ -2732,20 +2762,21 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
-static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
+static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
{
- /*
- * FIXME: these limits may be incompatible with the pool's data device
- */
+ struct pool *pool = pt->pool;
+ struct queue_limits *data_limits;
+
limits->max_discard_sectors = pool->sectors_per_block;
/*
- * This is just a hint, and not enforced. We have to cope with
- * bios that cover a block partially. A discard that spans a block
- * boundary is not sent to this target.
+ * discard_granularity is just a hint, and not enforced.
*/
- limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
- limits->discard_zeroes_data = pool->pf.zero_new_blocks;
+ if (pt->adjusted_pf.discard_passdown) {
+ data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
+ limits->discard_granularity = data_limits->discard_granularity;
+ } else
+ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
}
static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -2755,15 +2786,25 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- if (pool->pf.discard_enabled)
- set_discard_limits(pool, limits);
+
+ /*
+ * pt->adjusted_pf is a staging area for the actual features to use.
+ * They get transferred to the live pool in bind_control_target()
+ * called from pool_preresume().
+ */
+ if (!pt->adjusted_pf.discard_enabled)
+ return;
+
+ disable_passdown_if_not_supported(pt);
+
+ set_discard_limits(pt, limits);
}
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -3042,19 +3083,19 @@ static int thin_iterate_devices(struct dm_target *ti,
return 0;
}
+/*
+ * A thin device always inherits its queue limits from its pool.
+ */
static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct thin_c *tc = ti->private;
- struct pool *pool = tc->pool;
- blk_limits_io_min(limits, 0);
- blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- set_discard_limits(pool, limits);
+ *limits = bdev_get_queue(tc->pool_dev->bdev)->limits;
}
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 254d19268ad2..892ae2766aa6 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -718,8 +718,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
v->hash_dev_block_bits = ffs(num) - 1;
if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->data_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+ >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid data blocks";
r = -EINVAL;
goto bad;
@@ -733,8 +733,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
+ >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid hash start";
r = -EINVAL;
goto bad;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4e09b6ff5b49..67ffa391edcf 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -865,10 +865,14 @@ static void dm_done(struct request *clone, int error, bool mapped)
{
int r = error;
struct dm_rq_target_io *tio = clone->end_io_data;
- dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
+ dm_request_endio_fn rq_end_io = NULL;
- if (mapped && rq_end_io)
- r = rq_end_io(tio->ti, clone, error, &tio->info);
+ if (tio->ti) {
+ rq_end_io = tio->ti->type->rq_end_io;
+
+ if (mapped && rq_end_io)
+ r = rq_end_io(tio->ti, clone, error, &tio->info);
+ }
if (r <= 0)
/* The target wants to complete the I/O */
@@ -1588,15 +1592,6 @@ static int map_request(struct dm_target *ti, struct request *clone,
int r, requeued = 0;
struct dm_rq_target_io *tio = clone->end_io_data;
- /*
- * Hold the md reference here for the in-flight I/O.
- * We can't rely on the reference count by device opener,
- * because the device may be closed during the request completion
- * when all bios are completed.
- * See the comment in rq_completed() too.
- */
- dm_get(md);
-
tio->ti = ti;
r = ti->type->map_rq(ti, clone, &tio->info);
switch (r) {
@@ -1628,6 +1623,26 @@ static int map_request(struct dm_target *ti, struct request *clone,
return requeued;
}
+static struct request *dm_start_request(struct mapped_device *md, struct request *orig)
+{
+ struct request *clone;
+
+ blk_start_request(orig);
+ clone = orig->special;
+ atomic_inc(&md->pending[rq_data_dir(clone)]);
+
+ /*
+ * Hold the md reference here for the in-flight I/O.
+ * We can't rely on the reference count by device opener,
+ * because the device may be closed during the request completion
+ * when all bios are completed.
+ * See the comment in rq_completed() too.
+ */
+ dm_get(md);
+
+ return clone;
+}
+
/*
* q->request_fn for request-based dm.
* Called with the queue lock held.
@@ -1657,14 +1672,21 @@ static void dm_request_fn(struct request_queue *q)
pos = blk_rq_pos(rq);
ti = dm_table_find_target(map, pos);
- BUG_ON(!dm_target_is_valid(ti));
+ if (!dm_target_is_valid(ti)) {
+ /*
+ * Must perform setup, that dm_done() requires,
+ * before calling dm_kill_unmapped_request
+ */
+ DMERR_LIMIT("request attempted access beyond the end of device");
+ clone = dm_start_request(md, rq);
+ dm_kill_unmapped_request(clone, -EIO);
+ continue;
+ }
if (ti->type->busy && ti->type->busy(ti))
goto delay_and_out;
- blk_start_request(rq);
- clone = rq->special;
- atomic_inc(&md->pending[rq_data_dir(clone)]);
+ clone = dm_start_request(md, rq);
spin_unlock(q->queue_lock);
if (map_request(ti, clone, md))
@@ -1684,8 +1706,6 @@ delay_and_out:
blk_delay_queue(q, HZ / 10);
out:
dm_table_put(map);
-
- return;
}
int dm_underlying_device_busy(struct request_queue *q)
@@ -2409,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)
*/
struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
{
- struct dm_table *map = ERR_PTR(-EINVAL);
+ struct dm_table *live_map, *map = ERR_PTR(-EINVAL);
struct queue_limits limits;
int r;
@@ -2419,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
if (!dm_suspended_md(md))
goto out;
+ /*
+ * If the new table has no data devices, retain the existing limits.
+ * This helps multipath with queue_if_no_path if all paths disappear,
+ * then new I/O is queued based on these limits, and then some paths
+ * reappear.
+ */
+ if (dm_table_has_no_data_devices(table)) {
+ live_map = dm_get_live_table(md);
+ if (live_map)
+ limits = md->queue->limits;
+ dm_table_put(live_map);
+ }
+
r = dm_calculate_queue_limits(table, &limits);
if (r) {
map = ERR_PTR(r);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 52eef493d266..6a99fefaa743 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+bool dm_table_has_no_data_devices(struct dm_table *table);
int dm_calculate_queue_limits(struct dm_table *table,
struct queue_limits *limits);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d5ab4493c8be..308e87b417e0 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -498,61 +498,13 @@ void md_flush_request(struct mddev *mddev, struct bio *bio)
}
EXPORT_SYMBOL(md_flush_request);
-/* Support for plugging.
- * This mirrors the plugging support in request_queue, but does not
- * require having a whole queue or request structures.
- * We allocate an md_plug_cb for each md device and each thread it gets
- * plugged on. This links tot the private plug_handle structure in the
- * personality data where we keep a count of the number of outstanding
- * plugs so other code can see if a plug is active.
- */
-struct md_plug_cb {
- struct blk_plug_cb cb;
- struct mddev *mddev;
-};
-
-static void plugger_unplug(struct blk_plug_cb *cb)
-{
- struct md_plug_cb *mdcb = container_of(cb, struct md_plug_cb, cb);
- if (atomic_dec_and_test(&mdcb->mddev->plug_cnt))
- md_wakeup_thread(mdcb->mddev->thread);
- kfree(mdcb);
-}
-
-/* Check that an unplug wakeup will come shortly.
- * If not, wakeup the md thread immediately
- */
-int mddev_check_plugged(struct mddev *mddev)
+void md_unplug(struct blk_plug_cb *cb, bool from_schedule)
{
- struct blk_plug *plug = current->plug;
- struct md_plug_cb *mdcb;
-
- if (!plug)
- return 0;
-
- list_for_each_entry(mdcb, &plug->cb_list, cb.list) {
- if (mdcb->cb.callback == plugger_unplug &&
- mdcb->mddev == mddev) {
- /* Already on the list, move to top */
- if (mdcb != list_first_entry(&plug->cb_list,
- struct md_plug_cb,
- cb.list))
- list_move(&mdcb->cb.list, &plug->cb_list);
- return 1;
- }
- }
- /* Not currently on the callback list */
- mdcb = kmalloc(sizeof(*mdcb), GFP_ATOMIC);
- if (!mdcb)
- return 0;
-
- mdcb->mddev = mddev;
- mdcb->cb.callback = plugger_unplug;
- atomic_inc(&mddev->plug_cnt);
- list_add(&mdcb->cb.list, &plug->cb_list);
- return 1;
+ struct mddev *mddev = cb->data;
+ md_wakeup_thread(mddev->thread);
+ kfree(cb);
}
-EXPORT_SYMBOL_GPL(mddev_check_plugged);
+EXPORT_SYMBOL(md_unplug);
static inline struct mddev *mddev_get(struct mddev *mddev)
{
@@ -602,7 +554,6 @@ void mddev_init(struct mddev *mddev)
atomic_set(&mddev->active, 1);
atomic_set(&mddev->openers, 0);
atomic_set(&mddev->active_io, 0);
- atomic_set(&mddev->plug_cnt, 0);
spin_lock_init(&mddev->write_lock);
atomic_set(&mddev->flush_pending, 0);
init_waitqueue_head(&mddev->sb_wait);
@@ -1157,8 +1108,11 @@ static int super_90_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor
ret = 0;
}
rdev->sectors = rdev->sb_start;
- /* Limit to 4TB as metadata cannot record more than that */
- if (rdev->sectors >= (2ULL << 32))
+ /* Limit to 4TB as metadata cannot record more than that.
+ * (not needed for Linear and RAID0 as metadata doesn't
+ * record this size)
+ */
+ if (rdev->sectors >= (2ULL << 32) && sb->level >= 1)
rdev->sectors = (2ULL << 32) - 2;
if (rdev->sectors < ((sector_t)sb->size) * 2 && sb->level >= 1)
@@ -1449,7 +1403,7 @@ super_90_rdev_size_change(struct md_rdev *rdev, sector_t num_sectors)
/* Limit to 4TB as metadata cannot record more than that.
* 4TB == 2^32 KB, or 2*2^32 sectors.
*/
- if (num_sectors >= (2ULL << 32))
+ if (num_sectors >= (2ULL << 32) && rdev->mddev->level >= 1)
num_sectors = (2ULL << 32) - 2;
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
rdev->sb_page);
@@ -3942,17 +3896,13 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
break;
case clear:
/* stopping an active array */
- if (atomic_read(&mddev->openers) > 0)
- return -EBUSY;
err = do_md_stop(mddev, 0, NULL);
break;
case inactive:
/* stopping an active array */
- if (mddev->pers) {
- if (atomic_read(&mddev->openers) > 0)
- return -EBUSY;
+ if (mddev->pers)
err = do_md_stop(mddev, 2, NULL);
- } else
+ else
err = 0; /* already inactive */
break;
case suspended:
@@ -7669,6 +7619,8 @@ static int remove_and_add_spares(struct mddev *mddev)
}
}
}
+ if (removed)
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
return spares;
}
@@ -7682,9 +7634,11 @@ static void reap_sync_thread(struct mddev *mddev)
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* success...*/
/* activate any spares */
- if (mddev->pers->spare_active(mddev))
+ if (mddev->pers->spare_active(mddev)) {
sysfs_notify(&mddev->kobj, NULL,
"degraded");
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ }
}
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
mddev->pers->finish_reshape)
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 7b4a3c318cae..f385b038589d 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -266,9 +266,6 @@ struct mddev {
int new_chunk_sectors;
int reshape_backwards;
- atomic_t plug_cnt; /* If device is expecting
- * more bios soon.
- */
struct md_thread *thread; /* management thread */
struct md_thread *sync_thread; /* doing resync or reconstruct */
sector_t curr_resync; /* last block scheduled */
@@ -630,6 +627,12 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
struct mddev *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
struct mddev *mddev);
-extern int mddev_check_plugged(struct mddev *mddev);
extern void md_trim_bio(struct bio *bio, int offset, int size);
+
+extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule);
+static inline int mddev_check_plugged(struct mddev *mddev)
+{
+ return !!blk_check_plugged(md_unplug, mddev,
+ sizeof(struct blk_plug_cb));
+}
#endif /* _MD_MD_H */
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index cacd008d6864..611b5f797618 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -46,6 +46,20 @@
*/
#define NR_RAID1_BIOS 256
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error. To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
/* When there are this many requests queue to be written by
* the raid1 thread, we become 'congested' to provide back-pressure
* for writeback.
@@ -483,12 +497,14 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
const sector_t this_sector = r1_bio->sector;
int sectors;
int best_good_sectors;
- int start_disk;
- int best_disk;
- int i;
+ int best_disk, best_dist_disk, best_pending_disk;
+ int has_nonrot_disk;
+ int disk;
sector_t best_dist;
+ unsigned int min_pending;
struct md_rdev *rdev;
int choose_first;
+ int choose_next_idle;
rcu_read_lock();
/*
@@ -499,26 +515,26 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
retry:
sectors = r1_bio->sectors;
best_disk = -1;
+ best_dist_disk = -1;
best_dist = MaxSector;
+ best_pending_disk = -1;
+ min_pending = UINT_MAX;
best_good_sectors = 0;
+ has_nonrot_disk = 0;
+ choose_next_idle = 0;
if (conf->mddev->recovery_cp < MaxSector &&
- (this_sector + sectors >= conf->next_resync)) {
+ (this_sector + sectors >= conf->next_resync))
choose_first = 1;
- start_disk = 0;
- } else {
+ else
choose_first = 0;
- start_disk = conf->last_used;
- }
- for (i = 0 ; i < conf->raid_disks * 2 ; i++) {
+ for (disk = 0 ; disk < conf->raid_disks * 2 ; disk++) {
sector_t dist;
sector_t first_bad;
int bad_sectors;
-
- int disk = start_disk + i;
- if (disk >= conf->raid_disks * 2)
- disk -= conf->raid_disks * 2;
+ unsigned int pending;
+ bool nonrot;
rdev = rcu_dereference(conf->mirrors[disk].rdev);
if (r1_bio->bios[disk] == IO_BLOCKED
@@ -577,22 +593,77 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
} else
best_good_sectors = sectors;
+ nonrot = blk_queue_nonrot(bdev_get_queue(rdev->bdev));
+ has_nonrot_disk |= nonrot;
+ pending = atomic_read(&rdev->nr_pending);
dist = abs(this_sector - conf->mirrors[disk].head_position);
- if (choose_first
- /* Don't change to another disk for sequential reads */
- || conf->next_seq_sect == this_sector
- || dist == 0
- /* If device is idle, use it */
- || atomic_read(&rdev->nr_pending) == 0) {
+ if (choose_first) {
+ best_disk = disk;
+ break;
+ }
+ /* Don't change to another disk for sequential reads */
+ if (conf->mirrors[disk].next_seq_sect == this_sector
+ || dist == 0) {
+ int opt_iosize = bdev_io_opt(rdev->bdev) >> 9;
+ struct raid1_info *mirror = &conf->mirrors[disk];
+
+ best_disk = disk;
+ /*
+ * If buffered sequential IO size exceeds optimal
+ * iosize, check if there is idle disk. If yes, choose
+ * the idle disk. read_balance could already choose an
+ * idle disk before noticing it's a sequential IO in
+ * this disk. This doesn't matter because this disk
+ * will idle, next time it will be utilized after the
+ * first disk has IO size exceeds optimal iosize. In
+ * this way, iosize of the first disk will be optimal
+ * iosize at least. iosize of the second disk might be
+ * small, but not a big deal since when the second disk
+ * starts IO, the first disk is likely still busy.
+ */
+ if (nonrot && opt_iosize > 0 &&
+ mirror->seq_start != MaxSector &&
+ mirror->next_seq_sect > opt_iosize &&
+ mirror->next_seq_sect - opt_iosize >=
+ mirror->seq_start) {
+ choose_next_idle = 1;
+ continue;
+ }
+ break;
+ }
+ /* If device is idle, use it */
+ if (pending == 0) {
best_disk = disk;
break;
}
+
+ if (choose_next_idle)
+ continue;
+
+ if (min_pending > pending) {
+ min_pending = pending;
+ best_pending_disk = disk;
+ }
+
if (dist < best_dist) {
best_dist = dist;
- best_disk = disk;
+ best_dist_disk = disk;
}
}
+ /*
+ * If all disks are rotational, choose the closest disk. If any disk is
+ * non-rotational, choose the disk with less pending request even the
+ * disk is rotational, which might/might not be optimal for raids with
+ * mixed ratation/non-rotational disks depending on workload.
+ */
+ if (best_disk == -1) {
+ if (has_nonrot_disk)
+ best_disk = best_pending_disk;
+ else
+ best_disk = best_dist_disk;
+ }
+
if (best_disk >= 0) {
rdev = rcu_dereference(conf->mirrors[best_disk].rdev);
if (!rdev)
@@ -606,8 +677,11 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
goto retry;
}
sectors = best_good_sectors;
- conf->next_seq_sect = this_sector + sectors;
- conf->last_used = best_disk;
+
+ if (conf->mirrors[best_disk].next_seq_sect != this_sector)
+ conf->mirrors[best_disk].seq_start = this_sector;
+
+ conf->mirrors[best_disk].next_seq_sect = this_sector + sectors;
}
rcu_read_unlock();
*max_sectors = sectors;
@@ -870,10 +944,48 @@ do_sync_io:
pr_debug("%dB behind alloc failed, doing sync I/O\n", bio->bi_size);
}
+struct raid1_plug_cb {
+ struct blk_plug_cb cb;
+ struct bio_list pending;
+ int pending_cnt;
+};
+
+static void raid1_unplug(struct blk_plug_cb *cb, bool from_schedule)
+{
+ struct raid1_plug_cb *plug = container_of(cb, struct raid1_plug_cb,
+ cb);
+ struct mddev *mddev = plug->cb.data;
+ struct r1conf *conf = mddev->private;
+ struct bio *bio;
+
+ if (from_schedule) {
+ spin_lock_irq(&conf->device_lock);
+ bio_list_merge(&conf->pending_bio_list, &plug->pending);
+ conf->pending_count += plug->pending_cnt;
+ spin_unlock_irq(&conf->device_lock);
+ md_wakeup_thread(mddev->thread);
+ kfree(plug);
+ return;
+ }
+
+ /* we aren't scheduling, so we can do the write-out directly. */
+ bio = bio_list_get(&plug->pending);
+ bitmap_unplug(mddev->bitmap);
+ wake_up(&conf->wait_barrier);
+
+ while (bio) { /* submit pending writes */
+ struct bio *next = bio->bi_next;
+ bio->bi_next = NULL;
+ generic_make_request(bio);
+ bio = next;
+ }
+ kfree(plug);
+}
+
static void make_request(struct mddev *mddev, struct bio * bio)
{
struct r1conf *conf = mddev->private;
- struct mirror_info *mirror;
+ struct raid1_info *mirror;
struct r1bio *r1_bio;
struct bio *read_bio;
int i, disks;
@@ -883,6 +995,8 @@ static void make_request(struct mddev *mddev, struct bio * bio)
const unsigned long do_sync = (bio->bi_rw & REQ_SYNC);
const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA));
struct md_rdev *blocked_rdev;
+ struct blk_plug_cb *cb;
+ struct raid1_plug_cb *plug = NULL;
int first_clone;
int sectors_handled;
int max_sectors;
@@ -1185,11 +1299,22 @@ read_again:
mbio->bi_private = r1_bio;
atomic_inc(&r1_bio->remaining);
+
+ cb = blk_check_plugged(raid1_unplug, mddev, sizeof(*plug));
+ if (cb)
+ plug = container_of(cb, struct raid1_plug_cb, cb);
+ else
+ plug = NULL;
spin_lock_irqsave(&conf->device_lock, flags);
- bio_list_add(&conf->pending_bio_list, mbio);
- conf->pending_count++;
+ if (plug) {
+ bio_list_add(&plug->pending, mbio);
+ plug->pending_cnt++;
+ } else {
+ bio_list_add(&conf->pending_bio_list, mbio);
+ conf->pending_count++;
+ }
spin_unlock_irqrestore(&conf->device_lock, flags);
- if (!mddev_check_plugged(mddev))
+ if (!plug)
md_wakeup_thread(mddev->thread);
}
/* Mustn't call r1_bio_write_done before this next test,
@@ -1364,7 +1489,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
struct r1conf *conf = mddev->private;
int err = -EEXIST;
int mirror = 0;
- struct mirror_info *p;
+ struct raid1_info *p;
int first = 0;
int last = conf->raid_disks - 1;
struct request_queue *q = bdev_get_queue(rdev->bdev);
@@ -1433,7 +1558,7 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct r1conf *conf = mddev->private;
int err = 0;
int number = rdev->raid_disk;
- struct mirror_info *p = conf->mirrors+ number;
+ struct raid1_info *p = conf->mirrors + number;
if (rdev != p->rdev)
p = conf->mirrors + conf->raid_disks + number;
@@ -2173,8 +2298,7 @@ static void raid1d(struct mddev *mddev)
blk_start_plug(&plug);
for (;;) {
- if (atomic_read(&mddev->plug_cnt) == 0)
- flush_pending_writes(conf);
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
@@ -2371,6 +2495,18 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
bio->bi_rw = READ;
bio->bi_end_io = end_sync_read;
read_targets++;
+ } else if (!test_bit(WriteErrorSeen, &rdev->flags) &&
+ test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
+ !test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) {
+ /*
+ * The device is suitable for reading (InSync),
+ * but has bad block(s) here. Let's try to correct them,
+ * if we are doing resync or repair. Otherwise, leave
+ * this device alone for this sync request.
+ */
+ bio->bi_rw = WRITE;
+ bio->bi_end_io = end_sync_write;
+ write_targets++;
}
}
if (bio->bi_end_io) {
@@ -2428,7 +2564,10 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, int *skipp
/* There is nowhere to write, so all non-sync
* drives must be failed - so we are finished
*/
- sector_t rv = max_sector - sector_nr;
+ sector_t rv;
+ if (min_bad > 0)
+ max_sector = sector_nr + min_bad;
+ rv = max_sector - sector_nr;
*skipped = 1;
put_buf(r1_bio);
return rv;
@@ -2521,7 +2660,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
{
struct r1conf *conf;
int i;
- struct mirror_info *disk;
+ struct raid1_info *disk;
struct md_rdev *rdev;
int err = -ENOMEM;
@@ -2529,7 +2668,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
if (!conf)
goto abort;
- conf->mirrors = kzalloc(sizeof(struct mirror_info)
+ conf->mirrors = kzalloc(sizeof(struct raid1_info)
* mddev->raid_disks * 2,
GFP_KERNEL);
if (!conf->mirrors)
@@ -2572,6 +2711,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
mddev->merge_check_needed = 1;
disk->head_position = 0;
+ disk->seq_start = MaxSector;
}
conf->raid_disks = mddev->raid_disks;
conf->mddev = mddev;
@@ -2585,7 +2725,6 @@ static struct r1conf *setup_conf(struct mddev *mddev)
conf->recovery_disabled = mddev->recovery_disabled - 1;
err = -EIO;
- conf->last_used = -1;
for (i = 0; i < conf->raid_disks * 2; i++) {
disk = conf->mirrors + i;
@@ -2611,19 +2750,9 @@ static struct r1conf *setup_conf(struct mddev *mddev)
if (disk->rdev &&
(disk->rdev->saved_raid_disk < 0))
conf->fullsync = 1;
- } else if (conf->last_used < 0)
- /*
- * The first working device is used as a
- * starting point to read balancing.
- */
- conf->last_used = i;
+ }
}
- if (conf->last_used < 0) {
- printk(KERN_ERR "md/raid1:%s: no operational mirrors\n",
- mdname(mddev));
- goto abort;
- }
err = -ENOMEM;
conf->thread = md_register_thread(raid1d, mddev, "raid1");
if (!conf->thread) {
@@ -2798,7 +2927,7 @@ static int raid1_reshape(struct mddev *mddev)
*/
mempool_t *newpool, *oldpool;
struct pool_info *newpoolinfo;
- struct mirror_info *newmirrors;
+ struct raid1_info *newmirrors;
struct r1conf *conf = mddev->private;
int cnt, raid_disks;
unsigned long flags;
@@ -2841,7 +2970,7 @@ static int raid1_reshape(struct mddev *mddev)
kfree(newpoolinfo);
return -ENOMEM;
}
- newmirrors = kzalloc(sizeof(struct mirror_info) * raid_disks * 2,
+ newmirrors = kzalloc(sizeof(struct raid1_info) * raid_disks * 2,
GFP_KERNEL);
if (!newmirrors) {
kfree(newpoolinfo);
@@ -2880,7 +3009,6 @@ static int raid1_reshape(struct mddev *mddev)
conf->raid_disks = mddev->raid_disks = raid_disks;
mddev->delta_disks = 0;
- conf->last_used = 0; /* just make sure it is in-range */
lower_barrier(conf);
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
diff --git a/drivers/md/raid1.h b/drivers/md/raid1.h
index 80ded139314c..0ff3715fb7eb 100644
--- a/drivers/md/raid1.h
+++ b/drivers/md/raid1.h
@@ -1,9 +1,15 @@
#ifndef _RAID1_H
#define _RAID1_H
-struct mirror_info {
+struct raid1_info {
struct md_rdev *rdev;
sector_t head_position;
+
+ /* When choose the best device for a read (read_balance())
+ * we try to keep sequential reads one the same device
+ */
+ sector_t next_seq_sect;
+ sector_t seq_start;
};
/*
@@ -24,17 +30,11 @@ struct pool_info {
struct r1conf {
struct mddev *mddev;
- struct mirror_info *mirrors; /* twice 'raid_disks' to
+ struct raid1_info *mirrors; /* twice 'raid_disks' to
* allow for replacements.
*/
int raid_disks;
- /* When choose the best device for a read (read_balance())
- * we try to keep sequential reads one the same device
- * using 'last_used' and 'next_seq_sect'
- */
- int last_used;
- sector_t next_seq_sect;
/* During resync, read_balancing is only allowed on the part
* of the array that has been resynced. 'next_resync' tells us
* where that is.
@@ -135,20 +135,6 @@ struct r1bio {
/* DO NOT PUT ANY NEW FIELDS HERE - bios array is contiguously alloced*/
};
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error. To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio *)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context. So we record
- * the success by setting bios[n] to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
/* bits for r1bio.state */
#define R1BIO_Uptodate 0
#define R1BIO_IsSync 1
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 8da6282254c3..0138a727c1f3 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -60,7 +60,21 @@
*/
#define NR_RAID10_BIOS 256
-/* When there are this many requests queue to be written by
+/* when we get a read error on a read-only array, we redirect to another
+ * device without failing the first device, or trying to over-write to
+ * correct the read error. To keep track of bad blocks on a per-bio
+ * level, we store IO_BLOCKED in the appropriate 'bios' pointer
+ */
+#define IO_BLOCKED ((struct bio *)1)
+/* When we successfully write to a known bad-block, we need to remove the
+ * bad-block marking which must be done from process context. So we record
+ * the success by setting devs[n].bio to IO_MADE_GOOD
+ */
+#define IO_MADE_GOOD ((struct bio *)2)
+
+#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
+
+/* When there are this many requests queued to be written by
* the raid10 thread, we become 'congested' to provide back-pressure
* for writeback.
*/
@@ -645,7 +659,11 @@ static int raid10_mergeable_bvec(struct request_queue *q,
max = biovec->bv_len;
if (mddev->merge_check_needed) {
- struct r10bio r10_bio;
+ struct {
+ struct r10bio r10_bio;
+ struct r10dev devs[conf->copies];
+ } on_stack;
+ struct r10bio *r10_bio = &on_stack.r10_bio;
int s;
if (conf->reshape_progress != MaxSector) {
/* Cannot give any guidance during reshape */
@@ -653,18 +671,18 @@ static int raid10_mergeable_bvec(struct request_queue *q,
return biovec->bv_len;
return 0;
}
- r10_bio.sector = sector;
- raid10_find_phys(conf, &r10_bio);
+ r10_bio->sector = sector;
+ raid10_find_phys(conf, r10_bio);
rcu_read_lock();
for (s = 0; s < conf->copies; s++) {
- int disk = r10_bio.devs[s].devnum;
+ int disk = r10_bio->devs[s].devnum;
struct md_rdev *rdev = rcu_dereference(
conf->mirrors[disk].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags)) {
struct request_queue *q =
bdev_get_queue(rdev->bdev);
if (q->merge_bvec_fn) {
- bvm->bi_sector = r10_bio.devs[s].addr
+ bvm->bi_sector = r10_bio->devs[s].addr
+ rdev->data_offset;
bvm->bi_bdev = rdev->bdev;
max = min(max, q->merge_bvec_fn(
@@ -676,7 +694,7 @@ static int raid10_mergeable_bvec(struct request_queue *q,
struct request_queue *q =
bdev_get_queue(rdev->bdev);
if (q->merge_bvec_fn) {
- bvm->bi_sector = r10_bio.devs[s].addr
+ bvm->bi_sector = r10_bio->devs[s].addr
+ rdev->data_offset;
bvm->bi_bdev = rdev->bdev;
max = min(max, q->merge_bvec_fn(
@@ -717,7 +735,7 @@ static struct md_rdev *read_balance(struct r10conf *conf,
int sectors = r10_bio->sectors;
int best_good_sectors;
sector_t new_distance, best_dist;
- struct md_rdev *rdev, *best_rdev;
+ struct md_rdev *best_rdev, *rdev = NULL;
int do_balance;
int best_slot;
struct geom *geo = &conf->geo;
@@ -839,9 +857,8 @@ retry:
return rdev;
}
-static int raid10_congested(void *data, int bits)
+int md_raid10_congested(struct mddev *mddev, int bits)
{
- struct mddev *mddev = data;
struct r10conf *conf = mddev->private;
int i, ret = 0;
@@ -849,8 +866,6 @@ static int raid10_congested(void *data, int bits)
conf->pending_count >= max_queued_requests)
return 1;
- if (mddev_congested(mddev, bits))
- return 1;
rcu_read_lock();
for (i = 0;
(i < conf->geo.raid_disks || i < conf->prev.raid_disks)
@@ -866,6 +881,15 @@ static int raid10_congested(void *data, int bits)
rcu_read_unlock();
return ret;
}
+EXPORT_SYMBOL_GPL(md_raid10_congested);
+
+static int raid10_congested(void *data, int bits)
+{
+ struct mddev *mddev = data;
+
+ return mddev_congested(mddev, bits) ||
+ md_raid10_congested(mddev, bits);
+}
static void flush_pending_writes(struct r10conf *conf)
{
@@ -1488,14 +1512,16 @@ static int _enough(struct r10conf *conf, struct geom *geo, int ignore)
do {
int n = conf->copies;
int cnt = 0;
+ int this = first;
while (n--) {
- if (conf->mirrors[first].rdev &&
- first != ignore)
+ if (conf->mirrors[this].rdev &&
+ this != ignore)
cnt++;
- first = (first+1) % geo->raid_disks;
+ this = (this+1) % geo->raid_disks;
}
if (cnt == 0)
return 0;
+ first = (first + geo->near_copies) % geo->raid_disks;
} while (first != 0);
return 1;
}
@@ -1546,7 +1572,7 @@ static void error(struct mddev *mddev, struct md_rdev *rdev)
static void print_conf(struct r10conf *conf)
{
int i;
- struct mirror_info *tmp;
+ struct raid10_info *tmp;
printk(KERN_DEBUG "RAID10 conf printout:\n");
if (!conf) {
@@ -1580,7 +1606,7 @@ static int raid10_spare_active(struct mddev *mddev)
{
int i;
struct r10conf *conf = mddev->private;
- struct mirror_info *tmp;
+ struct raid10_info *tmp;
int count = 0;
unsigned long flags;
@@ -1655,7 +1681,7 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
else
mirror = first;
for ( ; mirror <= last ; mirror++) {
- struct mirror_info *p = &conf->mirrors[mirror];
+ struct raid10_info *p = &conf->mirrors[mirror];
if (p->recovery_disabled == mddev->recovery_disabled)
continue;
if (p->rdev) {
@@ -1709,7 +1735,7 @@ static int raid10_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
int err = 0;
int number = rdev->raid_disk;
struct md_rdev **rdevp;
- struct mirror_info *p = conf->mirrors + number;
+ struct raid10_info *p = conf->mirrors + number;
print_conf(conf);
if (rdev == p->rdev)
@@ -2660,8 +2686,7 @@ static void raid10d(struct mddev *mddev)
blk_start_plug(&plug);
for (;;) {
- if (atomic_read(&mddev->plug_cnt) == 0)
- flush_pending_writes(conf);
+ flush_pending_writes(conf);
spin_lock_irqsave(&conf->device_lock, flags);
if (list_empty(head)) {
@@ -2876,7 +2901,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr,
sector_t sect;
int must_sync;
int any_working;
- struct mirror_info *mirror = &conf->mirrors[i];
+ struct raid10_info *mirror = &conf->mirrors[i];
if ((mirror->rdev == NULL ||
test_bit(In_sync, &mirror->rdev->flags))
@@ -3388,7 +3413,7 @@ static struct r10conf *setup_conf(struct mddev *mddev)
goto out;
/* FIXME calc properly */
- conf->mirrors = kzalloc(sizeof(struct mirror_info)*(mddev->raid_disks +
+ conf->mirrors = kzalloc(sizeof(struct raid10_info)*(mddev->raid_disks +
max(0,mddev->delta_disks)),
GFP_KERNEL);
if (!conf->mirrors)
@@ -3452,7 +3477,7 @@ static int run(struct mddev *mddev)
{
struct r10conf *conf;
int i, disk_idx, chunk_size;
- struct mirror_info *disk;
+ struct raid10_info *disk;
struct md_rdev *rdev;
sector_t size;
sector_t min_offset_diff = 0;
@@ -3472,12 +3497,14 @@ static int run(struct mddev *mddev)
conf->thread = NULL;
chunk_size = mddev->chunk_sectors << 9;
- blk_queue_io_min(mddev->queue, chunk_size);
- if (conf->geo.raid_disks % conf->geo.near_copies)
- blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
- else
- blk_queue_io_opt(mddev->queue, chunk_size *
- (conf->geo.raid_disks / conf->geo.near_copies));
+ if (mddev->queue) {
+ blk_queue_io_min(mddev->queue, chunk_size);
+ if (conf->geo.raid_disks % conf->geo.near_copies)
+ blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks);
+ else
+ blk_queue_io_opt(mddev->queue, chunk_size *
+ (conf->geo.raid_disks / conf->geo.near_copies));
+ }
rdev_for_each(rdev, mddev) {
long long diff;
@@ -3511,8 +3538,9 @@ static int run(struct mddev *mddev)
if (first || diff < min_offset_diff)
min_offset_diff = diff;
- disk_stack_limits(mddev->gendisk, rdev->bdev,
- rdev->data_offset << 9);
+ if (mddev->gendisk)
+ disk_stack_limits(mddev->gendisk, rdev->bdev,
+ rdev->data_offset << 9);
disk->head_position = 0;
}
@@ -3575,22 +3603,22 @@ static int run(struct mddev *mddev)
md_set_array_sectors(mddev, size);
mddev->resync_max_sectors = size;
- mddev->queue->backing_dev_info.congested_fn = raid10_congested;
- mddev->queue->backing_dev_info.congested_data = mddev;
-
- /* Calculate max read-ahead size.
- * We need to readahead at least twice a whole stripe....
- * maybe...
- */
- {
+ if (mddev->queue) {
int stripe = conf->geo.raid_disks *
((mddev->chunk_sectors << 9) / PAGE_SIZE);
+ mddev->queue->backing_dev_info.congested_fn = raid10_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
+
+ /* Calculate max read-ahead size.
+ * We need to readahead at least twice a whole stripe....
+ * maybe...
+ */
stripe /= conf->geo.near_copies;
if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
+ blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
}
- blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
if (md_integrity_register(mddev))
goto out_free_conf;
@@ -3641,7 +3669,10 @@ static int stop(struct mddev *mddev)
lower_barrier(conf);
md_unregister_thread(&mddev->thread);
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
+ if (mddev->queue)
+ /* the unplug fn references 'conf'*/
+ blk_sync_queue(mddev->queue);
+
if (conf->r10bio_pool)
mempool_destroy(conf->r10bio_pool);
kfree(conf->mirrors);
@@ -3805,7 +3836,7 @@ static int raid10_check_reshape(struct mddev *mddev)
if (mddev->delta_disks > 0) {
/* allocate new 'mirrors' list */
conf->mirrors_new = kzalloc(
- sizeof(struct mirror_info)
+ sizeof(struct raid10_info)
*(mddev->raid_disks +
mddev->delta_disks),
GFP_KERNEL);
@@ -3930,7 +3961,7 @@ static int raid10_start_reshape(struct mddev *mddev)
spin_lock_irq(&conf->device_lock);
if (conf->mirrors_new) {
memcpy(conf->mirrors_new, conf->mirrors,
- sizeof(struct mirror_info)*conf->prev.raid_disks);
+ sizeof(struct raid10_info)*conf->prev.raid_disks);
smp_mb();
kfree(conf->mirrors_old); /* FIXME and elsewhere */
conf->mirrors_old = conf->mirrors;
@@ -4389,14 +4420,18 @@ static int handle_reshape_read_error(struct mddev *mddev,
{
/* Use sync reads to get the blocks from somewhere else */
int sectors = r10_bio->sectors;
- struct r10bio r10b;
struct r10conf *conf = mddev->private;
+ struct {
+ struct r10bio r10_bio;
+ struct r10dev devs[conf->copies];
+ } on_stack;
+ struct r10bio *r10b = &on_stack.r10_bio;
int slot = 0;
int idx = 0;
struct bio_vec *bvec = r10_bio->master_bio->bi_io_vec;
- r10b.sector = r10_bio->sector;
- __raid10_find_phys(&conf->prev, &r10b);
+ r10b->sector = r10_bio->sector;
+ __raid10_find_phys(&conf->prev, r10b);
while (sectors) {
int s = sectors;
@@ -4407,7 +4442,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
s = PAGE_SIZE >> 9;
while (!success) {
- int d = r10b.devs[slot].devnum;
+ int d = r10b->devs[slot].devnum;
struct md_rdev *rdev = conf->mirrors[d].rdev;
sector_t addr;
if (rdev == NULL ||
@@ -4415,7 +4450,7 @@ static int handle_reshape_read_error(struct mddev *mddev,
!test_bit(In_sync, &rdev->flags))
goto failed;
- addr = r10b.devs[slot].addr + idx * PAGE_SIZE;
+ addr = r10b->devs[slot].addr + idx * PAGE_SIZE;
success = sync_page_io(rdev,
addr,
s << 9,
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 135b1b0a1554..1054cf602345 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -1,7 +1,7 @@
#ifndef _RAID10_H
#define _RAID10_H
-struct mirror_info {
+struct raid10_info {
struct md_rdev *rdev, *replacement;
sector_t head_position;
int recovery_disabled; /* matches
@@ -13,8 +13,8 @@ struct mirror_info {
struct r10conf {
struct mddev *mddev;
- struct mirror_info *mirrors;
- struct mirror_info *mirrors_new, *mirrors_old;
+ struct raid10_info *mirrors;
+ struct raid10_info *mirrors_new, *mirrors_old;
spinlock_t device_lock;
/* geometry */
@@ -110,7 +110,7 @@ struct r10bio {
* We choose the number when they are allocated.
* We sometimes need an extra bio to write to the replacement.
*/
- struct {
+ struct r10dev {
struct bio *bio;
union {
struct bio *repl_bio; /* used for resync and
@@ -123,20 +123,6 @@ struct r10bio {
} devs[0];
};
-/* when we get a read error on a read-only array, we redirect to another
- * device without failing the first device, or trying to over-write to
- * correct the read error. To keep track of bad blocks on a per-bio
- * level, we store IO_BLOCKED in the appropriate 'bios' pointer
- */
-#define IO_BLOCKED ((struct bio*)1)
-/* When we successfully write to a known bad-block, we need to remove the
- * bad-block marking which must be done from process context. So we record
- * the success by setting devs[n].bio to IO_MADE_GOOD
- */
-#define IO_MADE_GOOD ((struct bio *)2)
-
-#define BIO_SPECIAL(bio) ((unsigned long)bio <= 2)
-
/* bits for r10bio.state */
enum r10bio_state {
R10BIO_Uptodate,
@@ -159,4 +145,7 @@ enum r10bio_state {
*/
R10BIO_Previous,
};
+
+extern int md_raid10_congested(struct mddev *mddev, int bits);
+
#endif
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 04348d76bb30..0689173fd9f5 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -99,34 +99,40 @@ static inline struct bio *r5_next_bio(struct bio *bio, sector_t sector)
* We maintain a biased count of active stripes in the bottom 16 bits of
* bi_phys_segments, and a count of processed stripes in the upper 16 bits
*/
-static inline int raid5_bi_phys_segments(struct bio *bio)
+static inline int raid5_bi_processed_stripes(struct bio *bio)
{
- return bio->bi_phys_segments & 0xffff;
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ return (atomic_read(segments) >> 16) & 0xffff;
}
-static inline int raid5_bi_hw_segments(struct bio *bio)
+static inline int raid5_dec_bi_active_stripes(struct bio *bio)
{
- return (bio->bi_phys_segments >> 16) & 0xffff;
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ return atomic_sub_return(1, segments) & 0xffff;
}
-static inline int raid5_dec_bi_phys_segments(struct bio *bio)
+static inline void raid5_inc_bi_active_stripes(struct bio *bio)
{
- --bio->bi_phys_segments;
- return raid5_bi_phys_segments(bio);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ atomic_inc(segments);
}
-static inline int raid5_dec_bi_hw_segments(struct bio *bio)
+static inline void raid5_set_bi_processed_stripes(struct bio *bio,
+ unsigned int cnt)
{
- unsigned short val = raid5_bi_hw_segments(bio);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ int old, new;
- --val;
- bio->bi_phys_segments = (val << 16) | raid5_bi_phys_segments(bio);
- return val;
+ do {
+ old = atomic_read(segments);
+ new = (old & 0xffff) | (cnt << 16);
+ } while (atomic_cmpxchg(segments, old, new) != old);
}
-static inline void raid5_set_bi_hw_segments(struct bio *bio, unsigned int cnt)
+static inline void raid5_set_bi_stripes(struct bio *bio, unsigned int cnt)
{
- bio->bi_phys_segments = raid5_bi_phys_segments(bio) | (cnt << 16);
+ atomic_t *segments = (atomic_t *)&bio->bi_phys_segments;
+ atomic_set(segments, cnt);
}
/* Find first data disk in a raid6 stripe */
@@ -190,49 +196,56 @@ static int stripe_operations_active(struct stripe_head *sh)
test_bit(STRIPE_COMPUTE_RUN, &sh->state);
}
-static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh)
{
- if (atomic_dec_and_test(&sh->count)) {
- BUG_ON(!list_empty(&sh->lru));
- BUG_ON(atomic_read(&conf->active_stripes)==0);
- if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state) &&
- !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- list_add_tail(&sh->lru, &conf->delayed_list);
- else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- sh->bm_seq - conf->seq_write > 0)
- list_add_tail(&sh->lru, &conf->bitmap_list);
- else {
- clear_bit(STRIPE_DELAYED, &sh->state);
- clear_bit(STRIPE_BIT_DELAY, &sh->state);
- list_add_tail(&sh->lru, &conf->handle_list);
- }
- md_wakeup_thread(conf->mddev->thread);
- } else {
- BUG_ON(stripe_operations_active(sh));
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- if (atomic_dec_return(&conf->preread_active_stripes)
- < IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- atomic_dec(&conf->active_stripes);
- if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
- list_add_tail(&sh->lru, &conf->inactive_list);
- wake_up(&conf->wait_for_stripe);
- if (conf->retry_read_aligned)
- md_wakeup_thread(conf->mddev->thread);
- }
+ BUG_ON(!list_empty(&sh->lru));
+ BUG_ON(atomic_read(&conf->active_stripes)==0);
+ if (test_bit(STRIPE_HANDLE, &sh->state)) {
+ if (test_bit(STRIPE_DELAYED, &sh->state) &&
+ !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ list_add_tail(&sh->lru, &conf->delayed_list);
+ else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
+ sh->bm_seq - conf->seq_write > 0)
+ list_add_tail(&sh->lru, &conf->bitmap_list);
+ else {
+ clear_bit(STRIPE_DELAYED, &sh->state);
+ clear_bit(STRIPE_BIT_DELAY, &sh->state);
+ list_add_tail(&sh->lru, &conf->handle_list);
+ }
+ md_wakeup_thread(conf->mddev->thread);
+ } else {
+ BUG_ON(stripe_operations_active(sh));
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ if (atomic_dec_return(&conf->preread_active_stripes)
+ < IO_THRESHOLD)
+ md_wakeup_thread(conf->mddev->thread);
+ atomic_dec(&conf->active_stripes);
+ if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
+ list_add_tail(&sh->lru, &conf->inactive_list);
+ wake_up(&conf->wait_for_stripe);
+ if (conf->retry_read_aligned)
+ md_wakeup_thread(conf->mddev->thread);
}
}
}
+static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
+{
+ if (atomic_dec_and_test(&sh->count))
+ do_release_stripe(conf, sh);
+}
+
static void release_stripe(struct stripe_head *sh)
{
struct r5conf *conf = sh->raid_conf;
unsigned long flags;
- spin_lock_irqsave(&conf->device_lock, flags);
- __release_stripe(conf, sh);
- spin_unlock_irqrestore(&conf->device_lock, flags);
+ local_irq_save(flags);
+ if (atomic_dec_and_lock(&sh->count, &conf->device_lock)) {
+ do_release_stripe(conf, sh);
+ spin_unlock(&conf->device_lock);
+ }
+ local_irq_restore(flags);
}
static inline void remove_hash(struct stripe_head *sh)
@@ -380,6 +393,8 @@ static int calc_degraded(struct r5conf *conf)
degraded = 0;
for (i = 0; i < conf->previous_raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded++;
else if (test_bit(In_sync, &rdev->flags))
@@ -404,6 +419,8 @@ static int calc_degraded(struct r5conf *conf)
degraded2 = 0;
for (i = 0; i < conf->raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded2++;
else if (test_bit(In_sync, &rdev->flags))
@@ -471,7 +488,8 @@ get_active_stripe(struct r5conf *conf, sector_t sector,
} else {
if (atomic_read(&sh->count)) {
BUG_ON(!list_empty(&sh->lru)
- && !test_bit(STRIPE_EXPANDING, &sh->state));
+ && !test_bit(STRIPE_EXPANDING, &sh->state)
+ && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state));
} else {
if (!test_bit(STRIPE_HANDLE, &sh->state))
atomic_inc(&conf->active_stripes);
@@ -640,6 +658,9 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
else
bi->bi_sector = (sh->sector
+ rdev->data_offset);
+ if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+ bi->bi_rw |= REQ_FLUSH;
+
bi->bi_flags = 1 << BIO_UPTODATE;
bi->bi_idx = 0;
bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -749,14 +770,12 @@ static void ops_complete_biofill(void *stripe_head_ref)
{
struct stripe_head *sh = stripe_head_ref;
struct bio *return_bi = NULL;
- struct r5conf *conf = sh->raid_conf;
int i;
pr_debug("%s: stripe %llu\n", __func__,
(unsigned long long)sh->sector);
/* clear completed biofills */
- spin_lock_irq(&conf->device_lock);
for (i = sh->disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
@@ -774,7 +793,7 @@ static void ops_complete_biofill(void *stripe_head_ref)
while (rbi && rbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
rbi2 = r5_next_bio(rbi, dev->sector);
- if (!raid5_dec_bi_phys_segments(rbi)) {
+ if (!raid5_dec_bi_active_stripes(rbi)) {
rbi->bi_next = return_bi;
return_bi = rbi;
}
@@ -782,7 +801,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
}
}
}
- spin_unlock_irq(&conf->device_lock);
clear_bit(STRIPE_BIOFILL_RUN, &sh->state);
return_io(return_bi);
@@ -794,7 +812,6 @@ static void ops_complete_biofill(void *stripe_head_ref)
static void ops_run_biofill(struct stripe_head *sh)
{
struct dma_async_tx_descriptor *tx = NULL;
- struct r5conf *conf = sh->raid_conf;
struct async_submit_ctl submit;
int i;
@@ -805,10 +822,10 @@ static void ops_run_biofill(struct stripe_head *sh)
struct r5dev *dev = &sh->dev[i];
if (test_bit(R5_Wantfill, &dev->flags)) {
struct bio *rbi;
- spin_lock_irq(&conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
dev->read = rbi = dev->toread;
dev->toread = NULL;
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
while (rbi && rbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
tx = async_copy_data(0, rbi, dev->page,
@@ -1144,12 +1161,12 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) {
struct bio *wbi;
- spin_lock_irq(&sh->raid_conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
chosen = dev->towrite;
dev->towrite = NULL;
BUG_ON(dev->written);
wbi = dev->written = chosen;
- spin_unlock_irq(&sh->raid_conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
@@ -1454,6 +1471,8 @@ static int grow_one_stripe(struct r5conf *conf)
init_waitqueue_head(&sh->ops.wait_for_ops);
#endif
+ spin_lock_init(&sh->stripe_lock);
+
if (grow_buffers(sh)) {
shrink_buffers(sh);
kmem_cache_free(conf->slab_cache, sh);
@@ -1572,6 +1591,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
#ifdef CONFIG_MULTICORE_RAID456
init_waitqueue_head(&nsh->ops.wait_for_ops);
#endif
+ spin_lock_init(&nsh->stripe_lock);
list_add(&nsh->lru, &newstripes);
}
@@ -1739,7 +1759,9 @@ static void raid5_end_read_request(struct bio * bi, int error)
atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
clear_bit(R5_ReadError, &sh->dev[i].flags);
clear_bit(R5_ReWrite, &sh->dev[i].flags);
- }
+ } else if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags))
+ clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+
if (atomic_read(&rdev->read_errors))
atomic_set(&rdev->read_errors, 0);
} else {
@@ -1784,7 +1806,11 @@ static void raid5_end_read_request(struct bio * bi, int error)
else
retry = 1;
if (retry)
- set_bit(R5_ReadError, &sh->dev[i].flags);
+ if (test_bit(R5_ReadNoMerge, &sh->dev[i].flags)) {
+ set_bit(R5_ReadError, &sh->dev[i].flags);
+ clear_bit(R5_ReadNoMerge, &sh->dev[i].flags);
+ } else
+ set_bit(R5_ReadNoMerge, &sh->dev[i].flags);
else {
clear_bit(R5_ReadError, &sh->dev[i].flags);
clear_bit(R5_ReWrite, &sh->dev[i].flags);
@@ -2340,11 +2366,18 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
(unsigned long long)bi->bi_sector,
(unsigned long long)sh->sector);
-
- spin_lock_irq(&conf->device_lock);
+ /*
+ * If several bio share a stripe. The bio bi_phys_segments acts as a
+ * reference count to avoid race. The reference count should already be
+ * increased before this function is called (for example, in
+ * make_request()), so other bio sharing this stripe will not free the
+ * stripe. If a stripe is owned by one stripe, the stripe lock will
+ * protect it.
+ */
+ spin_lock_irq(&sh->stripe_lock);
if (forwrite) {
bip = &sh->dev[dd_idx].towrite;
- if (*bip == NULL && sh->dev[dd_idx].written == NULL)
+ if (*bip == NULL)
firstwrite = 1;
} else
bip = &sh->dev[dd_idx].toread;
@@ -2360,7 +2393,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
if (*bip)
bi->bi_next = *bip;
*bip = bi;
- bi->bi_phys_segments++;
+ raid5_inc_bi_active_stripes(bi);
if (forwrite) {
/* check if page is covered */
@@ -2375,7 +2408,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
}
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n",
(unsigned long long)(*bip)->bi_sector,
@@ -2391,7 +2424,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
overlap:
set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
- spin_unlock_irq(&conf->device_lock);
+ spin_unlock_irq(&sh->stripe_lock);
return 0;
}
@@ -2441,10 +2474,11 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
rdev_dec_pending(rdev, conf->mddev);
}
}
- spin_lock_irq(&conf->device_lock);
+ spin_lock_irq(&sh->stripe_lock);
/* fail all writes first */
bi = sh->dev[i].towrite;
sh->dev[i].towrite = NULL;
+ spin_unlock_irq(&sh->stripe_lock);
if (bi) {
s->to_write--;
bitmap_end = 1;
@@ -2457,13 +2491,17 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
sh->dev[i].sector + STRIPE_SECTORS) {
struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
md_write_end(conf->mddev);
bi->bi_next = *return_bi;
*return_bi = bi;
}
bi = nextbi;
}
+ if (bitmap_end)
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS, 0, 0);
+ bitmap_end = 0;
/* and fail all 'written' */
bi = sh->dev[i].written;
sh->dev[i].written = NULL;
@@ -2472,7 +2510,7 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
sh->dev[i].sector + STRIPE_SECTORS) {
struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
md_write_end(conf->mddev);
bi->bi_next = *return_bi;
*return_bi = bi;
@@ -2496,14 +2534,13 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
struct bio *nextbi =
r5_next_bio(bi, sh->dev[i].sector);
clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (!raid5_dec_bi_phys_segments(bi)) {
+ if (!raid5_dec_bi_active_stripes(bi)) {
bi->bi_next = *return_bi;
*return_bi = bi;
}
bi = nextbi;
}
}
- spin_unlock_irq(&conf->device_lock);
if (bitmap_end)
bitmap_endwrite(conf->mddev->bitmap, sh->sector,
STRIPE_SECTORS, 0, 0);
@@ -2707,30 +2744,23 @@ static void handle_stripe_clean_event(struct r5conf *conf,
test_bit(R5_UPTODATE, &dev->flags)) {
/* We can return any write requests */
struct bio *wbi, *wbi2;
- int bitmap_end = 0;
pr_debug("Return write for disc %d\n", i);
- spin_lock_irq(&conf->device_lock);
wbi = dev->written;
dev->written = NULL;
while (wbi && wbi->bi_sector <
dev->sector + STRIPE_SECTORS) {
wbi2 = r5_next_bio(wbi, dev->sector);
- if (!raid5_dec_bi_phys_segments(wbi)) {
+ if (!raid5_dec_bi_active_stripes(wbi)) {
md_write_end(conf->mddev);
wbi->bi_next = *return_bi;
*return_bi = wbi;
}
wbi = wbi2;
}
- if (dev->towrite == NULL)
- bitmap_end = 1;
- spin_unlock_irq(&conf->device_lock);
- if (bitmap_end)
- bitmap_endwrite(conf->mddev->bitmap,
- sh->sector,
- STRIPE_SECTORS,
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS,
!test_bit(STRIPE_DEGRADED, &sh->state),
- 0);
+ 0);
}
}
@@ -3182,7 +3212,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
/* Now to look around and see what can be done */
rcu_read_lock();
- spin_lock_irq(&conf->device_lock);
for (i=disks; i--; ) {
struct md_rdev *rdev;
sector_t first_bad;
@@ -3328,7 +3357,6 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
do_recovery = 1;
}
}
- spin_unlock_irq(&conf->device_lock);
if (test_bit(STRIPE_SYNCING, &sh->state)) {
/* If there is a failed device being replaced,
* we must be recovering.
@@ -3791,7 +3819,7 @@ static struct bio *remove_bio_from_retry(struct r5conf *conf)
* this sets the active strip count to 1 and the processed
* strip count to zero (upper 8 bits)
*/
- bi->bi_phys_segments = 1; /* biased count of active stripes */
+ raid5_set_bi_stripes(bi, 1); /* biased count of active stripes */
}
return bi;
@@ -3988,6 +4016,62 @@ static struct stripe_head *__get_priority_stripe(struct r5conf *conf)
return sh;
}
+struct raid5_plug_cb {
+ struct blk_plug_cb cb;
+ struct list_head list;
+};
+
+static void raid5_unplug(struct blk_plug_cb *blk_cb, bool from_schedule)
+{
+ struct raid5_plug_cb *cb = container_of(
+ blk_cb, struct raid5_plug_cb, cb);
+ struct stripe_head *sh;
+ struct mddev *mddev = cb->cb.data;
+ struct r5conf *conf = mddev->private;
+
+ if (cb->list.next && !list_empty(&cb->list)) {
+ spin_lock_irq(&conf->device_lock);
+ while (!list_empty(&cb->list)) {
+ sh = list_first_entry(&cb->list, struct stripe_head, lru);
+ list_del_init(&sh->lru);
+ /*
+ * avoid race release_stripe_plug() sees
+ * STRIPE_ON_UNPLUG_LIST clear but the stripe
+ * is still in our list
+ */
+ smp_mb__before_clear_bit();
+ clear_bit(STRIPE_ON_UNPLUG_LIST, &sh->state);
+ __release_stripe(conf, sh);
+ }
+ spin_unlock_irq(&conf->device_lock);
+ }
+ kfree(cb);
+}
+
+static void release_stripe_plug(struct mddev *mddev,
+ struct stripe_head *sh)
+{
+ struct blk_plug_cb *blk_cb = blk_check_plugged(
+ raid5_unplug, mddev,
+ sizeof(struct raid5_plug_cb));
+ struct raid5_plug_cb *cb;
+
+ if (!blk_cb) {
+ release_stripe(sh);
+ return;
+ }
+
+ cb = container_of(blk_cb, struct raid5_plug_cb, cb);
+
+ if (cb->list.next == NULL)
+ INIT_LIST_HEAD(&cb->list);
+
+ if (!test_and_set_bit(STRIPE_ON_UNPLUG_LIST, &sh->state))
+ list_add_tail(&sh->lru, &cb->list);
+ else
+ release_stripe(sh);
+}
+
static void make_request(struct mddev *mddev, struct bio * bi)
{
struct r5conf *conf = mddev->private;
@@ -4116,8 +4200,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
if ((bi->bi_rw & REQ_SYNC) &&
!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
atomic_inc(&conf->preread_active_stripes);
- mddev_check_plugged(mddev);
- release_stripe(sh);
+ release_stripe_plug(mddev, sh);
} else {
/* cannot get stripe for read-ahead, just give-up */
clear_bit(BIO_UPTODATE, &bi->bi_flags);
@@ -4126,9 +4209,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
}
}
- spin_lock_irq(&conf->device_lock);
- remaining = raid5_dec_bi_phys_segments(bi);
- spin_unlock_irq(&conf->device_lock);
+ remaining = raid5_dec_bi_active_stripes(bi);
if (remaining == 0) {
if ( rw == WRITE )
@@ -4484,7 +4565,7 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
sector += STRIPE_SECTORS,
scnt++) {
- if (scnt < raid5_bi_hw_segments(raid_bio))
+ if (scnt < raid5_bi_processed_stripes(raid_bio))
/* already done this stripe */
continue;
@@ -4492,25 +4573,24 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
if (!sh) {
/* failed to get a stripe - must wait */
- raid5_set_bi_hw_segments(raid_bio, scnt);
+ raid5_set_bi_processed_stripes(raid_bio, scnt);
conf->retry_read_aligned = raid_bio;
return handled;
}
if (!add_stripe_bio(sh, raid_bio, dd_idx, 0)) {
release_stripe(sh);
- raid5_set_bi_hw_segments(raid_bio, scnt);
+ raid5_set_bi_processed_stripes(raid_bio, scnt);
conf->retry_read_aligned = raid_bio;
return handled;
}
+ set_bit(R5_ReadNoMerge, &sh->dev[dd_idx].flags);
handle_stripe(sh);
release_stripe(sh);
handled++;
}
- spin_lock_irq(&conf->device_lock);
- remaining = raid5_dec_bi_phys_segments(raid_bio);
- spin_unlock_irq(&conf->device_lock);
+ remaining = raid5_dec_bi_active_stripes(raid_bio);
if (remaining == 0)
bio_endio(raid_bio, 0);
if (atomic_dec_and_test(&conf->active_aligned_reads))
@@ -4518,6 +4598,30 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
return handled;
}
+#define MAX_STRIPE_BATCH 8
+static int handle_active_stripes(struct r5conf *conf)
+{
+ struct stripe_head *batch[MAX_STRIPE_BATCH], *sh;
+ int i, batch_size = 0;
+
+ while (batch_size < MAX_STRIPE_BATCH &&
+ (sh = __get_priority_stripe(conf)) != NULL)
+ batch[batch_size++] = sh;
+
+ if (batch_size == 0)
+ return batch_size;
+ spin_unlock_irq(&conf->device_lock);
+
+ for (i = 0; i < batch_size; i++)
+ handle_stripe(batch[i]);
+
+ cond_resched();
+
+ spin_lock_irq(&conf->device_lock);
+ for (i = 0; i < batch_size; i++)
+ __release_stripe(conf, batch[i]);
+ return batch_size;
+}
/*
* This is our raid5 kernel thread.
@@ -4528,7 +4632,6 @@ static int retry_aligned_read(struct r5conf *conf, struct bio *raid_bio)
*/
static void raid5d(struct mddev *mddev)
{
- struct stripe_head *sh;
struct r5conf *conf = mddev->private;
int handled;
struct blk_plug plug;
@@ -4542,8 +4645,9 @@ static void raid5d(struct mddev *mddev)
spin_lock_irq(&conf->device_lock);
while (1) {
struct bio *bio;
+ int batch_size;
- if (atomic_read(&mddev->plug_cnt) == 0 &&
+ if (
!list_empty(&conf->bitmap_list)) {
/* Now is a good time to flush some bitmap updates */
conf->seq_flush++;
@@ -4553,8 +4657,7 @@ static void raid5d(struct mddev *mddev)
conf->seq_write = conf->seq_flush;
activate_bit_delay(conf);
}
- if (atomic_read(&mddev->plug_cnt) == 0)
- raid5_activate_delayed(conf);
+ raid5_activate_delayed(conf);
while ((bio = remove_bio_from_retry(conf))) {
int ok;
@@ -4566,21 +4669,16 @@ static void raid5d(struct mddev *mddev)
handled++;
}
- sh = __get_priority_stripe(conf);
-
- if (!sh)
+ batch_size = handle_active_stripes(conf);
+ if (!batch_size)
break;
- spin_unlock_irq(&conf->device_lock);
-
- handled++;
- handle_stripe(sh);
- release_stripe(sh);
- cond_resched();
+ handled += batch_size;
- if (mddev->flags & ~(1<<MD_CHANGE_PENDING))
+ if (mddev->flags & ~(1<<MD_CHANGE_PENDING)) {
+ spin_unlock_irq(&conf->device_lock);
md_check_recovery(mddev);
-
- spin_lock_irq(&conf->device_lock);
+ spin_lock_irq(&conf->device_lock);
+ }
}
pr_debug("%d stripes handled\n", handled);
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 2164021f3b5f..a9fc24901eda 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -210,6 +210,7 @@ struct stripe_head {
int disks; /* disks in stripe */
enum check_states check_state;
enum reconstruct_states reconstruct_state;
+ spinlock_t stripe_lock;
/**
* struct stripe_operations
* @target - STRIPE_OP_COMPUTE_BLK target
@@ -273,6 +274,7 @@ enum r5dev_flags {
R5_Wantwrite,
R5_Overlap, /* There is a pending overlapping request
* on this block */
+ R5_ReadNoMerge, /* prevent bio from merging in block-layer */
R5_ReadError, /* seen a read error here recently */
R5_ReWrite, /* have tried to over-write the readerror */
@@ -319,6 +321,7 @@ enum {
STRIPE_BIOFILL_RUN,
STRIPE_COMPUTE_RUN,
STRIPE_OPS_REQ_PENDING,
+ STRIPE_ON_UNPLUG_LIST,
};
/*
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index d941581ab921..dd13e3a4c272 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -99,11 +99,6 @@ config VIDEO_DEV
depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT
default y
-config VIDEO_V4L2_COMMON
- tristate
- depends on (I2C || I2C=n) && VIDEO_DEV
- default (I2C || I2C=n) && VIDEO_DEV
-
config VIDEO_V4L2_SUBDEV_API
bool "V4L2 sub-device userspace API (EXPERIMENTAL)"
depends on VIDEO_DEV && MEDIA_CONTROLLER && EXPERIMENTAL
@@ -113,6 +108,8 @@ config VIDEO_V4L2_SUBDEV_API
This API is mostly used by camera interfaces in embedded platforms.
+source "drivers/media/v4l2-core/Kconfig"
+
#
# DVB Core
# Only enables if one of DTV is selected
@@ -138,28 +135,54 @@ config DVB_NET
You may want to disable the network support on embedded devices. If
unsure say Y.
+source "drivers/media/dvb-core/Kconfig"
+
comment "Media drivers"
-source "drivers/media/common/Kconfig"
source "drivers/media/rc/Kconfig"
#
-# Tuner drivers for DVB and V4L
+# V4L platform/mem2mem drivers
#
-source "drivers/media/common/tuners/Kconfig"
+source "drivers/media/usb/Kconfig"
+source "drivers/media/pci/Kconfig"
+source "drivers/media/platform/Kconfig"
+source "drivers/media/mmc/Kconfig"
+source "drivers/media/parport/Kconfig"
+source "drivers/media/radio/Kconfig"
+
+comment "Supported FireWire (IEEE 1394) Adapters"
+ depends on DVB_CORE && FIREWIRE
+source "drivers/media/firewire/Kconfig"
+
+# Common driver options
+source "drivers/media/common/Kconfig"
#
-# Video/Radio/Hybrid adapters
+# Ancillary drivers (tuners, i2c, frontends)
#
-source "drivers/media/video/Kconfig"
+config MEDIA_SUBDRV_AUTOSELECT
+ bool "Autoselect analog and hybrid tuner modules to build"
+ depends on MEDIA_TUNER
+ default y
+ help
+ By default, a TV driver auto-selects all possible tuners
+ thar could be used by the driver.
-source "drivers/media/radio/Kconfig"
+ This is generally the right thing to do, except when there
+ are strict constraints with regards to the kernel size.
-#
-# DVB adapters
-#
+ Use this option with care, as deselecting tuner drivers which
+ are in fact necessary will result in TV devices which cannot
+ be tuned due to lack of the tuning driver.
+
+ If unsure say Y.
+
+comment "Media ancillary drivers (tuners, sensors, i2c, frontends)"
-source "drivers/media/dvb/Kconfig"
+source "drivers/media/i2c/Kconfig"
+source "drivers/media/tuners/Kconfig"
+source "drivers/media/dvb-frontends/Kconfig"
endif # MEDIA_SUPPORT
diff --git a/drivers/media/Makefile b/drivers/media/Makefile
index 64755c99ded2..620f275a45c9 100644
--- a/drivers/media/Makefile
+++ b/drivers/media/Makefile
@@ -4,11 +4,30 @@
media-objs := media-device.o media-devnode.o media-entity.o
+#
+# I2C drivers should come before other drivers, otherwise they'll fail
+# when compiled as builtin drivers
+#
+obj-y += i2c/ tuners/
+obj-$(CONFIG_DVB_CORE) += dvb-frontends/
+
+#
+# Now, let's link-in the media core
+#
ifeq ($(CONFIG_MEDIA_CONTROLLER),y)
obj-$(CONFIG_MEDIA_SUPPORT) += media.o
endif
-obj-y += common/ rc/ video/
+obj-$(CONFIG_VIDEO_DEV) += v4l2-core/
+obj-$(CONFIG_DVB_CORE) += dvb-core/
+# There are both core and drivers at RC subtree - merge before drivers
+obj-y += rc/
+
+#
+# Finally, merge the drivers that require the core
+#
+
+obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/
obj-$(CONFIG_VIDEO_DEV) += radio/
-obj-$(CONFIG_DVB_CORE) += dvb/
+
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 769c6f8142d2..121b0110af3c 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -1,9 +1,3 @@
-config VIDEO_SAA7146
- tristate
- depends on I2C && PCI
-
-config VIDEO_SAA7146_VV
- tristate
- depends on VIDEO_V4L2
- select VIDEOBUF_DMA_SG
- select VIDEO_SAA7146
+source "drivers/media/common/b2c2/Kconfig"
+source "drivers/media/common/saa7146/Kconfig"
+source "drivers/media/common/siano/Kconfig"
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index e3ec9639321b..b8e2e3a33a31 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,6 +1 @@
-saa7146-objs := saa7146_i2c.o saa7146_core.o
-saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
-
-obj-y += tuners/
-obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
-obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
+obj-y += b2c2/ saa7146/ siano/
diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig
new file mode 100644
index 000000000000..1df9e578daa5
--- /dev/null
+++ b/drivers/media/common/b2c2/Kconfig
@@ -0,0 +1,28 @@
+config DVB_B2C2_FLEXCOP
+ tristate
+ depends on DVB_CORE && I2C
+ depends on DVB_B2C2_FLEXCOP_PCI || DVB_B2C2_FLEXCOP_USB
+ default y
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_BCM3510 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_ITD1000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_CX24113 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the digital TV receiver chip made by B2C2 Inc. included in
+ Technisats PCI cards and USB boxes.
+
+ Say Y if you own such a device and want to use it.
+
+# Selected via the PCI or USB flexcop drivers
+config DVB_B2C2_FLEXCOP_DEBUG
+ bool
diff --git a/drivers/media/common/b2c2/Makefile b/drivers/media/common/b2c2/Makefile
new file mode 100644
index 000000000000..24993a5b38ba
--- /dev/null
+++ b/drivers/media/common/b2c2/Makefile
@@ -0,0 +1,8 @@
+b2c2-flexcop-objs += flexcop.o flexcop-fe-tuner.o flexcop-i2c.o
+b2c2-flexcop-objs += flexcop-sram.o flexcop-eeprom.o flexcop-misc.o
+b2c2-flexcop-objs += flexcop-hw-filter.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/common/b2c2/flexcop-common.h
index 437912e49824..437912e49824 100644
--- a/drivers/media/dvb/b2c2/flexcop-common.h
+++ b/drivers/media/common/b2c2/flexcop-common.h
diff --git a/drivers/media/dvb/b2c2/flexcop-eeprom.c b/drivers/media/common/b2c2/flexcop-eeprom.c
index a25373a9bd84..a25373a9bd84 100644
--- a/drivers/media/dvb/b2c2/flexcop-eeprom.c
+++ b/drivers/media/common/b2c2/flexcop-eeprom.c
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/common/b2c2/flexcop-fe-tuner.c
index 850a6c606750..850a6c606750 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/common/b2c2/flexcop-fe-tuner.c
diff --git a/drivers/media/dvb/b2c2/flexcop-hw-filter.c b/drivers/media/common/b2c2/flexcop-hw-filter.c
index 77e45475f4c7..77e45475f4c7 100644
--- a/drivers/media/dvb/b2c2/flexcop-hw-filter.c
+++ b/drivers/media/common/b2c2/flexcop-hw-filter.c
diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
index 965d5eb33752..965d5eb33752 100644
--- a/drivers/media/dvb/b2c2/flexcop-i2c.c
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
diff --git a/drivers/media/dvb/b2c2/flexcop-misc.c b/drivers/media/common/b2c2/flexcop-misc.c
index f06f3a9070f5..f06f3a9070f5 100644
--- a/drivers/media/dvb/b2c2/flexcop-misc.c
+++ b/drivers/media/common/b2c2/flexcop-misc.c
diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/common/b2c2/flexcop-reg.h
index dc4528dcbb98..dc4528dcbb98 100644
--- a/drivers/media/dvb/b2c2/flexcop-reg.h
+++ b/drivers/media/common/b2c2/flexcop-reg.h
diff --git a/drivers/media/dvb/b2c2/flexcop-sram.c b/drivers/media/common/b2c2/flexcop-sram.c
index f2199e43e803..f2199e43e803 100644
--- a/drivers/media/dvb/b2c2/flexcop-sram.c
+++ b/drivers/media/common/b2c2/flexcop-sram.c
diff --git a/drivers/media/common/b2c2/flexcop.c b/drivers/media/common/b2c2/flexcop.c
new file mode 100644
index 000000000000..412c5daf2b48
--- /dev/null
+++ b/drivers/media/common/b2c2/flexcop.c
@@ -0,0 +1,325 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop.c - main module part
+ * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
+ * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * Acknowledgements:
+ * John Jurrius from BBTI, Inc. for extensive support
+ * with code examples and data books
+ * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
+ *
+ * Contributions to the skystar2-driver have been done by
+ * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
+ * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
+ * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
+ * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
+ * filtering)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "flexcop.h"
+
+#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
+
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define DEBSTATUS ""
+#else
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+int b2c2_flexcop_debug;
+EXPORT_SYMBOL_GPL(b2c2_flexcop_debug);
+module_param_named(debug, b2c2_flexcop_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "set debug level (1=info,2=tuner,4=i2c,8=ts,"
+ "16=sram,32=reg (|-able))."
+ DEBSTATUS);
+#undef DEBSTATUS
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* global zero for ibi values */
+flexcop_ibi_value ibi_zero;
+
+static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+ return flexcop_pid_feed_control(fc, dvbdmxfeed, 1);
+}
+
+static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+ return flexcop_pid_feed_control(fc, dvbdmxfeed, 0);
+}
+
+static int flexcop_dvb_init(struct flexcop_device *fc)
+{
+ int ret = dvb_register_adapter(&fc->dvb_adapter,
+ "FlexCop Digital TV device", fc->owner,
+ fc->dev, adapter_nr);
+ if (ret < 0) {
+ err("error registering DVB adapter");
+ return ret;
+ }
+ fc->dvb_adapter.priv = fc;
+
+ fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING
+ | DMX_MEMORY_BASED_FILTERING);
+ fc->demux.priv = fc;
+ fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
+ fc->demux.start_feed = flexcop_dvb_start_feed;
+ fc->demux.stop_feed = flexcop_dvb_stop_feed;
+ fc->demux.write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(&fc->demux);
+ if (ret < 0) {
+ err("dvb_dmx failed: error %d", ret);
+ goto err_dmx;
+ }
+
+ fc->hw_frontend.source = DMX_FRONTEND_0;
+
+ fc->dmxdev.filternum = fc->demux.feednum;
+ fc->dmxdev.demux = &fc->demux.dmx;
+ fc->dmxdev.capabilities = 0;
+ ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter);
+ if (ret < 0) {
+ err("dvb_dmxdev_init failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend);
+ if (ret < 0) {
+ err("adding hw_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_hw_frontend;
+ }
+
+ fc->mem_frontend.source = DMX_MEMORY_FE;
+ ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend);
+ if (ret < 0) {
+ err("adding mem_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_mem_frontend;
+ }
+
+ ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend);
+ if (ret < 0) {
+ err("connect frontend failed: error %d", ret);
+ goto err_connect_frontend;
+ }
+
+ ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
+ if (ret < 0) {
+ err("dvb_net_init failed: error %d", ret);
+ goto err_net;
+ }
+
+ fc->init_state |= FC_STATE_DVB_INIT;
+ return 0;
+
+err_net:
+ fc->demux.dmx.disconnect_frontend(&fc->demux.dmx);
+err_connect_frontend:
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend);
+err_dmx_add_mem_frontend:
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend);
+err_dmx_add_hw_frontend:
+ dvb_dmxdev_release(&fc->dmxdev);
+err_dmx_dev:
+ dvb_dmx_release(&fc->demux);
+err_dmx:
+ dvb_unregister_adapter(&fc->dvb_adapter);
+ return ret;
+}
+
+static void flexcop_dvb_exit(struct flexcop_device *fc)
+{
+ if (fc->init_state & FC_STATE_DVB_INIT) {
+ dvb_net_release(&fc->dvbnet);
+
+ fc->demux.dmx.close(&fc->demux.dmx);
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
+ &fc->mem_frontend);
+ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
+ &fc->hw_frontend);
+ dvb_dmxdev_release(&fc->dmxdev);
+ dvb_dmx_release(&fc->demux);
+ dvb_unregister_adapter(&fc->dvb_adapter);
+ deb_info("deinitialized dvb stuff\n");
+ }
+ fc->init_state &= ~FC_STATE_DVB_INIT;
+}
+
+/* these methods are necessary to achieve the long-term-goal of hiding the
+ * struct flexcop_device from the bus-parts */
+void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
+{
+ dvb_dmx_swfilter(&fc->demux, buf, len);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_data);
+
+void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
+{
+ dvb_dmx_swfilter_packets(&fc->demux, buf, no);
+}
+EXPORT_SYMBOL(flexcop_pass_dmx_packets);
+
+static void flexcop_reset(struct flexcop_device *fc)
+{
+ flexcop_ibi_value v210, v204;
+
+ /* reset the flexcop itself */
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.raw = 0;
+ v210.sw_reset_210.reset_block_000 = 1;
+ v210.sw_reset_210.reset_block_100 = 1;
+ v210.sw_reset_210.reset_block_200 = 1;
+ v210.sw_reset_210.reset_block_300 = 1;
+ v210.sw_reset_210.reset_block_400 = 1;
+ v210.sw_reset_210.reset_block_500 = 1;
+ v210.sw_reset_210.reset_block_600 = 1;
+ v210.sw_reset_210.reset_block_700 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+ v210.sw_reset_210.Special_controls = 0xc259;
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+ msleep(1);
+
+ /* reset the periphical devices */
+
+ v204 = fc->read_ibi_reg(fc,misc_204);
+ v204.misc_204.Per_reset_sig = 0;
+ fc->write_ibi_reg(fc,misc_204,v204);
+ msleep(1);
+ v204.misc_204.Per_reset_sig = 1;
+ fc->write_ibi_reg(fc,misc_204,v204);
+}
+
+void flexcop_reset_block_300(struct flexcop_device *fc)
+{
+ flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208),
+ v210 = fc->read_ibi_reg(fc, sw_reset_210);
+
+ deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.sw_reset_210.reset_block_300 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+ fc->write_ibi_reg(fc,ctrl_208,v208_save);
+}
+
+struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
+{
+ void *bus;
+ struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device),
+ GFP_KERNEL);
+ if (!fc) {
+ err("no memory");
+ return NULL;
+ }
+
+ bus = kzalloc(bus_specific_len, GFP_KERNEL);
+ if (!bus) {
+ err("no memory");
+ kfree(fc);
+ return NULL;
+ }
+
+ fc->bus_specific = bus;
+
+ return fc;
+}
+EXPORT_SYMBOL(flexcop_device_kmalloc);
+
+void flexcop_device_kfree(struct flexcop_device *fc)
+{
+ kfree(fc->bus_specific);
+ kfree(fc);
+}
+EXPORT_SYMBOL(flexcop_device_kfree);
+
+int flexcop_device_initialize(struct flexcop_device *fc)
+{
+ int ret;
+ ibi_zero.raw = 0;
+
+ flexcop_reset(fc);
+ flexcop_determine_revision(fc);
+ flexcop_sram_init(fc);
+ flexcop_hw_filter_init(fc);
+ flexcop_smc_ctrl(fc, 0);
+
+ ret = flexcop_dvb_init(fc);
+ if (ret)
+ goto error;
+
+ /* i2c has to be done before doing EEProm stuff -
+ * because the EEProm is accessed via i2c */
+ ret = flexcop_i2c_init(fc);
+ if (ret)
+ goto error;
+
+ /* do the MAC address reading after initializing the dvb_adapter */
+ if (fc->get_mac_addr(fc, 0) == 0) {
+ u8 *b = fc->dvb_adapter.proposed_mac;
+ info("MAC address = %pM", b);
+ flexcop_set_mac_filter(fc,b);
+ flexcop_mac_filter_ctrl(fc,1);
+ } else
+ warn("reading of MAC address failed.\n");
+
+ ret = flexcop_frontend_init(fc);
+ if (ret)
+ goto error;
+
+ flexcop_device_name(fc,"initialization of","complete");
+ return 0;
+
+error:
+ flexcop_device_exit(fc);
+ return ret;
+}
+EXPORT_SYMBOL(flexcop_device_initialize);
+
+void flexcop_device_exit(struct flexcop_device *fc)
+{
+ flexcop_frontend_exit(fc);
+ flexcop_i2c_exit(fc);
+ flexcop_dvb_exit(fc);
+}
+EXPORT_SYMBOL(flexcop_device_exit);
+
+static int flexcop_module_init(void)
+{
+ info(DRIVER_NAME " loaded successfully");
+ return 0;
+}
+
+static void flexcop_module_cleanup(void)
+{
+ info(DRIVER_NAME " unloaded successfully");
+}
+
+module_init(flexcop_module_init);
+module_exit(flexcop_module_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop.h b/drivers/media/common/b2c2/flexcop.h
index 897b10c85ad9..897b10c85ad9 100644
--- a/drivers/media/dvb/b2c2/flexcop.h
+++ b/drivers/media/common/b2c2/flexcop.h
diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
index 8f64bdbd72bb..8f64bdbd72bb 100644
--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_be.h
diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
index c75830d7d942..c75830d7d942 100644
--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h
+++ b/drivers/media/common/b2c2/flexcop_ibi_value_le.h
diff --git a/drivers/media/common/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig
new file mode 100644
index 000000000000..769c6f8142d2
--- /dev/null
+++ b/drivers/media/common/saa7146/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_SAA7146
+ tristate
+ depends on I2C && PCI
+
+config VIDEO_SAA7146_VV
+ tristate
+ depends on VIDEO_V4L2
+ select VIDEOBUF_DMA_SG
+ select VIDEO_SAA7146
diff --git a/drivers/media/common/saa7146/Makefile b/drivers/media/common/saa7146/Makefile
new file mode 100644
index 000000000000..3219b00a8771
--- /dev/null
+++ b/drivers/media/common/saa7146/Makefile
@@ -0,0 +1,5 @@
+saa7146-objs := saa7146_i2c.o saa7146_core.o
+saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
+obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
new file mode 100644
index 000000000000..bb6ee5191eb1
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -0,0 +1,592 @@
+/*
+ saa7146.o - driver for generic saa7146-based hardware
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146.h>
+#include <linux/module.h>
+
+static int saa7146_num;
+
+unsigned int saa7146_debug;
+
+module_param(saa7146_debug, uint, 0644);
+MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
+
+#if 0
+static void dump_registers(struct saa7146_dev* dev)
+{
+ int i = 0;
+
+ pr_info(" @ %li jiffies:\n", jiffies);
+ for (i = 0; i <= 0x148; i += 4)
+ pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
+}
+#endif
+
+/****************************************************************************
+ * gpio and debi helper functions
+ ****************************************************************************/
+
+void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
+{
+ u32 value = 0;
+
+ BUG_ON(port > 3);
+
+ value = saa7146_read(dev, GPIO_CTRL);
+ value &= ~(0xff << (8*port));
+ value |= (data << (8*port));
+ saa7146_write(dev, GPIO_CTRL, value);
+}
+
+/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
+static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long timeout;
+ int err;
+
+ /* wait for registers to be programmed */
+ timeout = jiffies + usecs_to_jiffies(us1);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (err) {
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ /* wait for transfer to complete */
+ timeout = jiffies + usecs_to_jiffies(us2);
+ while (1) {
+ err = time_after(jiffies, timeout);
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (err) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
+ unsigned long us1, unsigned long us2)
+{
+ unsigned long loops;
+
+ /* wait for registers to be programmed */
+ loops = us1;
+ while (1) {
+ if (saa7146_read(dev, MC2) & 2)
+ break;
+ if (!loops--) {
+ pr_err("%s: %s timed out while waiting for registers getting programmed\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ /* wait for transfer to complete */
+ loops = us2 / 5;
+ while (1) {
+ if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
+ break;
+ saa7146_read(dev, MC2);
+ if (!loops--) {
+ DEB_S("%s: %s timed out while waiting for transfer completion\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ udelay(5);
+ }
+
+ return 0;
+}
+
+int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
+{
+ if (nobusyloop)
+ return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
+ else
+ return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
+}
+
+/****************************************************************************
+ * general helper functions
+ ****************************************************************************/
+
+/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
+ make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
+ may be triggered on highmem machines */
+static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
+{
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+
+ sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
+ if (NULL == sglist)
+ return NULL;
+ sg_init_table(sglist, nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
+ pg = vmalloc_to_page(virt);
+ if (NULL == pg)
+ goto err;
+ BUG_ON(PageHighMem(pg));
+ sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
+ }
+ return sglist;
+
+ err:
+ kfree(sglist);
+ return NULL;
+}
+
+/********************************************************************************/
+/* common page table functions */
+
+void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
+{
+ int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
+ void *mem = vmalloc_32(length);
+ int slen = 0;
+
+ if (NULL == mem)
+ goto err_null;
+
+ if (!(pt->slist = vmalloc_to_sg(mem, pages)))
+ goto err_free_mem;
+
+ if (saa7146_pgtable_alloc(pci, pt))
+ goto err_free_slist;
+
+ pt->nents = pages;
+ slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
+ if (0 == slen)
+ goto err_free_pgtable;
+
+ if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
+ goto err_unmap_sg;
+
+ return mem;
+
+err_unmap_sg:
+ pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+err_free_pgtable:
+ saa7146_pgtable_free(pci, pt);
+err_free_slist:
+ kfree(pt->slist);
+ pt->slist = NULL;
+err_free_mem:
+ vfree(mem);
+err_null:
+ return NULL;
+}
+
+void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
+{
+ pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
+ saa7146_pgtable_free(pci, pt);
+ kfree(pt->slist);
+ pt->slist = NULL;
+ vfree(mem);
+}
+
+void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ if (NULL == pt->cpu)
+ return;
+ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
+ pt->cpu = NULL;
+}
+
+int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
+{
+ __le32 *cpu;
+ dma_addr_t dma_addr = 0;
+
+ cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
+ if (NULL == cpu) {
+ return -ENOMEM;
+ }
+ pt->size = PAGE_SIZE;
+ pt->cpu = cpu;
+ pt->dma = dma_addr;
+
+ return 0;
+}
+
+int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
+ struct scatterlist *list, int sglen )
+{
+ __le32 *ptr, fill;
+ int nr_pages = 0;
+ int i,p;
+
+ BUG_ON(0 == sglen);
+ BUG_ON(list->offset > PAGE_SIZE);
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt->offset = list->offset;
+
+ ptr = pt->cpu;
+ for (i = 0; i < sglen; i++, list++) {
+/*
+ pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
+ i, sg_dma_address(list), sg_dma_len(list),
+ list->offset);
+*/
+ for (p = 0; p * 4096 < list->length; p++, ptr++) {
+ *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
+ nr_pages++;
+ }
+ }
+
+
+ /* safety; fill the page table up with the last valid page */
+ fill = *(ptr-1);
+ for(i=nr_pages;i<1024;i++) {
+ *ptr++ = fill;
+ }
+
+/*
+ ptr = pt->cpu;
+ pr_debug("offset: %d\n", pt->offset);
+ for(i=0;i<5;i++) {
+ pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
+ }
+*/
+ return 0;
+}
+
+/********************************************************************************/
+/* interrupt handler */
+static irqreturn_t interrupt_hw(int irq, void *dev_id)
+{
+ struct saa7146_dev *dev = dev_id;
+ u32 isr;
+ u32 ack_isr;
+
+ /* read out the interrupt status register */
+ ack_isr = isr = saa7146_read(dev, ISR);
+
+ /* is this our interrupt? */
+ if ( 0 == isr ) {
+ /* nope, some other device */
+ return IRQ_NONE;
+ }
+
+ if (dev->ext) {
+ if (dev->ext->irq_mask & isr) {
+ if (dev->ext->irq_func)
+ dev->ext->irq_func(dev, &isr);
+ isr &= ~dev->ext->irq_mask;
+ }
+ }
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_27;
+ }
+ if (0 != (isr & (MASK_28))) {
+ if (dev->vv_data && dev->vv_callback)
+ dev->vv_callback(dev,isr);
+ isr &= ~MASK_28;
+ }
+ if (0 != (isr & (MASK_16|MASK_17))) {
+ SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
+ /* only wake up if we expect something */
+ if (0 != dev->i2c_op) {
+ dev->i2c_op = 0;
+ wake_up(&dev->i2c_wq);
+ } else {
+ u32 psr = saa7146_read(dev, PSR);
+ u32 ssr = saa7146_read(dev, SSR);
+ pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
+ dev->name, isr, psr, ssr);
+ }
+ isr &= ~(MASK_16|MASK_17);
+ }
+ if( 0 != isr ) {
+ ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
+ isr);
+ ERR("disabling interrupt source(s)!\n");
+ SAA7146_IER_DISABLE(dev,isr);
+ }
+ saa7146_write(dev, ISR, ack_isr);
+ return IRQ_HANDLED;
+}
+
+/*********************************************************************************/
+/* configuration-functions */
+
+static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
+{
+ struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
+ struct saa7146_extension *ext = pci_ext->ext;
+ struct saa7146_dev *dev;
+ int err = -ENOMEM;
+
+ /* clear out mem for sure */
+ dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
+ if (!dev) {
+ ERR("out of memory\n");
+ goto out;
+ }
+
+ DEB_EE("pci:%p\n", pci);
+
+ err = pci_enable_device(pci);
+ if (err < 0) {
+ ERR("pci_enable_device() failed\n");
+ goto err_free;
+ }
+
+ /* enable bus-mastering */
+ pci_set_master(pci);
+
+ dev->pci = pci;
+
+ /* get chip-revision; this is needed to enable bug-fixes */
+ dev->revision = pci->revision;
+
+ /* remap the memory from virtual to physical address */
+
+ err = pci_request_region(pci, 0, "saa7146");
+ if (err < 0)
+ goto err_disable;
+
+ dev->mem = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ if (!dev->mem) {
+ ERR("ioremap() failed\n");
+ err = -ENODEV;
+ goto err_release;
+ }
+
+ /* we don't do a master reset here anymore, it screws up
+ some boards that don't have an i2c-eeprom for configuration
+ values */
+/*
+ saa7146_write(dev, MC1, MASK_31);
+*/
+
+ /* disable all irqs */
+ saa7146_write(dev, IER, 0);
+
+ /* shut down all dma transfers and rps tasks */
+ saa7146_write(dev, MC1, 0x30ff0000);
+
+ /* clear out any rps-signals pending */
+ saa7146_write(dev, MC2, 0xf8000000);
+
+ /* request an interrupt for the saa7146 */
+ err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
+ dev->name, dev);
+ if (err < 0) {
+ ERR("request_irq() failed\n");
+ goto err_unmap;
+ }
+
+ err = -ENOMEM;
+
+ /* get memory for various stuff */
+ dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps0.dma_handle);
+ if (!dev->d_rps0.cpu_addr)
+ goto err_free_irq;
+ memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_rps1.dma_handle);
+ if (!dev->d_rps1.cpu_addr)
+ goto err_free_rps0;
+ memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
+ &dev->d_i2c.dma_handle);
+ if (!dev->d_i2c.cpu_addr)
+ goto err_free_rps1;
+ memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
+
+ /* the rest + print status message */
+
+ /* create a nice device name */
+ sprintf(dev->name, "saa7146 (%d)", saa7146_num);
+
+ pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
+ dev->mem, dev->revision, pci->irq,
+ pci->subsystem_vendor, pci->subsystem_device);
+ dev->ext = ext;
+
+ mutex_init(&dev->v4l2_lock);
+ spin_lock_init(&dev->int_slock);
+ spin_lock_init(&dev->slock);
+
+ mutex_init(&dev->i2c_lock);
+
+ dev->module = THIS_MODULE;
+ init_waitqueue_head(&dev->i2c_wq);
+
+ /* set some sane pci arbitrition values */
+ saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
+
+ /* TODO: use the status code of the callback */
+
+ err = -ENODEV;
+
+ if (ext->probe && ext->probe(dev)) {
+ DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+
+ if (ext->attach(dev, pci_ext)) {
+ DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
+ goto err_free_i2c;
+ }
+ /* V4L extensions will set the pci drvdata to the v4l2_device in the
+ attach() above. So for those cards that do not use V4L we have to
+ set it explicitly. */
+ pci_set_drvdata(pci, &dev->v4l2_dev);
+
+ saa7146_num++;
+
+ err = 0;
+out:
+ return err;
+
+err_free_i2c:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
+err_free_rps1:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
+ dev->d_rps1.dma_handle);
+err_free_rps0:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
+ dev->d_rps0.dma_handle);
+err_free_irq:
+ free_irq(pci->irq, (void *)dev);
+err_unmap:
+ iounmap(dev->mem);
+err_release:
+ pci_release_region(pci, 0);
+err_disable:
+ pci_disable_device(pci);
+err_free:
+ kfree(dev);
+ goto out;
+}
+
+static void saa7146_remove_one(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+ struct {
+ void *addr;
+ dma_addr_t dma;
+ } dev_map[] = {
+ { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
+ { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
+ { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
+ { NULL, 0 }
+ }, *p;
+
+ DEB_EE("dev:%p\n", dev);
+
+ dev->ext->detach(dev);
+ /* Zero the PCI drvdata after use. */
+ pci_set_drvdata(pdev, NULL);
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+
+ /* disable all irqs, release irq-routine */
+ saa7146_write(dev, IER, 0);
+
+ free_irq(pdev->irq, dev);
+
+ for (p = dev_map; p->addr; p++)
+ pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
+
+ iounmap(dev->mem);
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+ kfree(dev);
+
+ saa7146_num--;
+}
+
+/*********************************************************************************/
+/* extension handling functions */
+
+int saa7146_register_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+
+ ext->driver.name = ext->name;
+ ext->driver.id_table = ext->pci_tbl;
+ ext->driver.probe = saa7146_init_one;
+ ext->driver.remove = saa7146_remove_one;
+
+ pr_info("register extension '%s'\n", ext->name);
+ return pci_register_driver(&ext->driver);
+}
+
+int saa7146_unregister_extension(struct saa7146_extension* ext)
+{
+ DEB_EE("ext:%p\n", ext);
+ pr_info("unregister extension '%s'\n", ext->name);
+ pci_unregister_driver(&ext->driver);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(saa7146_register_extension);
+EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
+
+/* misc functions used by extension modules */
+EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
+EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
+EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
+EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
+
+EXPORT_SYMBOL_GPL(saa7146_setgpio);
+
+EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
+
+EXPORT_SYMBOL_GPL(saa7146_debug);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
new file mode 100644
index 000000000000..b3890bd49df6
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -0,0 +1,664 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146_vv.h>
+#include <linux/module.h>
+
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (fh->resources & bit) {
+ DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
+ bit, vv->resources);
+ /* have it already allocated */
+ return 1;
+ }
+
+ /* is it free? */
+ if (vv->resources & bit) {
+ DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
+ vv->resources, bit);
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ vv->resources |= bit;
+ DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
+ return 1;
+}
+
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ vv->resources &= ~bits;
+ DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
+}
+
+
+/********************************************************************************/
+/* common dma functions */
+
+void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
+ struct saa7146_buf *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ DEB_EE("dev:%p, buf:%p\n", dev, buf);
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+
+/********************************************************************************/
+/* common buffer functions */
+
+int saa7146_buffer_queue(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ struct saa7146_buf *buf)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
+
+ BUG_ON(!q);
+
+ if (NULL == q->curr) {
+ q->curr = buf;
+ DEB_D("immediately activating buffer %p\n", buf);
+ buf->activate(dev,buf,NULL);
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queue);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ DEB_D("adding buffer %p to queue. (active buffer present)\n",
+ buf);
+ }
+ return 0;
+}
+
+void saa7146_buffer_finish(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q,
+ int state)
+{
+ assert_spin_locked(&dev->slock);
+ DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
+ DEB_EE("q->curr:%p\n", q->curr);
+
+ BUG_ON(!q->curr);
+
+ /* finish current buffer */
+ if (NULL == q->curr) {
+ DEB_D("aiii. no current buffer\n");
+ return;
+ }
+
+ q->curr->vb.state = state;
+ do_gettimeofday(&q->curr->vb.ts);
+ wake_up(&q->curr->vb.done);
+
+ q->curr = NULL;
+}
+
+void saa7146_buffer_next(struct saa7146_dev *dev,
+ struct saa7146_dmaqueue *q, int vbi)
+{
+ struct saa7146_buf *buf,*next = NULL;
+
+ BUG_ON(!q);
+
+ DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
+
+ assert_spin_locked(&dev->slock);
+ if (!list_empty(&q->queue)) {
+ /* activate next one from queue */
+ buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
+ list_del(&buf->vb.queue);
+ if (!list_empty(&q->queue))
+ next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
+ q->curr = buf;
+ DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
+ buf, q->queue.prev, q->queue.next);
+ buf->activate(dev,buf,next);
+ } else {
+ DEB_INT("no next buffer. stopping.\n");
+ if( 0 != vbi ) {
+ /* turn off video-dma3 */
+ saa7146_write(dev,MC1, MASK_20);
+ } else {
+ /* nothing to do -- just prevent next video-dma1 transfer
+ by lowering the protection address */
+
+ // fixme: fix this for vflip != 0
+
+ saa7146_write(dev, PROT_ADDR1, 0);
+ saa7146_write(dev, MC2, (MASK_02|MASK_18));
+
+ /* write the address of the rps-program */
+ saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
+ /* turn on rps */
+ saa7146_write(dev, MC1, (MASK_12 | MASK_28));
+
+/*
+ printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
+ printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
+ printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
+ printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
+ printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
+ printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
+*/
+ }
+ del_timer(&q->timeout);
+ }
+}
+
+void saa7146_buffer_timeout(unsigned long data)
+{
+ struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
+ struct saa7146_dev *dev = q->dev;
+ unsigned long flags;
+
+ DEB_EE("dev:%p, dmaq:%p\n", dev, q);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ if (q->curr) {
+ DEB_D("timeout on %p\n", q->curr);
+ saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
+ }
+
+ /* we don't restart the transfer here like other drivers do. when
+ a streaming capture is disabled, the timeout function will be
+ called for the current buffer. if we activate the next buffer now,
+ we mess up our capture logic. if a timeout occurs on another buffer,
+ then something is seriously broken before, so no need to buffer the
+ next capture IMHO... */
+/*
+ saa7146_buffer_next(dev,q);
+*/
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+/********************************************************************************/
+/* file operations */
+
+static int fops_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = video_drvdata(file);
+ struct saa7146_fh *fh = NULL;
+ int result = 0;
+
+ DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+
+ DEB_D("using: %p\n", dev);
+
+ /* check if an extension is registered */
+ if( NULL == dev->ext ) {
+ DEB_S("no extension registered for this device\n");
+ result = -ENODEV;
+ goto out;
+ }
+
+ /* allocate per open data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ DEB_S("cannot allocate memory for per open data\n");
+ result = -ENOMEM;
+ goto out;
+ }
+
+ v4l2_fh_init(&fh->fh, vdev);
+
+ file->private_data = &fh->fh;
+ fh->dev = dev;
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ DEB_S("initializing vbi...\n");
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ result = saa7146_vbi_uops.open(dev,file);
+ if (dev->ext_vv_data->vbi_fops.open)
+ dev->ext_vv_data->vbi_fops.open(file);
+ } else {
+ DEB_S("initializing video...\n");
+ result = saa7146_video_uops.open(dev,file);
+ }
+
+ if (0 != result) {
+ goto out;
+ }
+
+ if( 0 == try_module_get(dev->ext->module)) {
+ result = -EINVAL;
+ goto out;
+ }
+
+ result = 0;
+ v4l2_fh_add(&fh->fh);
+out:
+ if (fh && result != 0) {
+ kfree(fh);
+ file->private_data = NULL;
+ }
+ mutex_unlock(vdev->lock);
+ return result;
+}
+
+static int fops_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+
+ DEB_EE("file:%p\n", file);
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.release(dev,file);
+ if (dev->ext_vv_data->vbi_fops.release)
+ dev->ext_vv_data->vbi_fops.release(file);
+ } else {
+ saa7146_video_uops.release(dev,file);
+ }
+
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
+ module_put(dev->ext->module);
+ file->private_data = NULL;
+ kfree(fh);
+
+ mutex_unlock(vdev->lock);
+
+ return 0;
+}
+
+static int fops_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_queue *q;
+ int res;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER: {
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ q = &fh->video_q;
+ break;
+ }
+ case VFL_TYPE_VBI: {
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
+ file, vma);
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return -ENODEV;
+ q = &fh->vbi_q;
+ break;
+ }
+ default:
+ BUG();
+ return 0;
+ }
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ res = videobuf_mmap_mapper(q, vma);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static unsigned int __fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ struct videobuf_queue *q;
+ unsigned int res = v4l2_ctrl_poll(file, wait);
+
+ DEB_EE("file:%p, poll:%p\n", file, wait);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return res | POLLOUT | POLLWRNORM;
+ if( 0 == fh->vbi_q.streaming )
+ return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
+ q = &fh->vbi_q;
+ } else {
+ DEB_D("using video queue\n");
+ q = &fh->video_q;
+ }
+
+ if (!list_empty(&q->stream))
+ buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
+
+ if (!buf) {
+ DEB_D("buf == NULL!\n");
+ return res | POLLERR;
+ }
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
+ DEB_D("poll succeeded!\n");
+ return res | POLLIN | POLLRDNORM;
+ }
+
+ DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
+ return res;
+}
+
+static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ unsigned int res;
+
+ mutex_lock(vdev->lock);
+ res = __fops_poll(file, wait);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
+ file, data, (unsigned long)count);
+*/
+ return saa7146_video_uops.read(file,data,count,ppos);
+ case VFL_TYPE_VBI:
+/*
+ DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
+ file, data, (unsigned long)count);
+*/
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = saa7146_vbi_uops.read(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_fh *fh = file->private_data;
+ int ret;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ return -EINVAL;
+ case VFL_TYPE_VBI:
+ if (fh->dev->ext_vv_data->vbi_fops.write) {
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
+ mutex_unlock(vdev->lock);
+ return ret;
+ }
+ return -EINVAL;
+ default:
+ BUG();
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = fops_open,
+ .release = fops_release,
+ .read = fops_read,
+ .write = fops_write,
+ .poll = fops_poll,
+ .mmap = fops_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+{
+ u32 isr = status;
+
+ DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
+
+ if (0 != (isr & (MASK_27))) {
+ DEB_INT("irq: RPS0 (0x%08x)\n", isr);
+ saa7146_video_uops.irq_done(dev,isr);
+ }
+
+ if (0 != (isr & (MASK_28))) {
+ u32 mc2 = saa7146_read(dev, MC2);
+ if( 0 != (mc2 & MASK_15)) {
+ DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
+ wake_up(&dev->vv_data->vbi_wq);
+ saa7146_write(dev,MC2, MASK_31);
+ return;
+ }
+ DEB_INT("irq: RPS1 (0x%08x)\n", isr);
+ saa7146_vbi_uops.irq_done(dev,isr);
+ }
+}
+
+static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
+ .s_ctrl = saa7146_s_ctrl,
+};
+
+int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct v4l2_pix_format *fmt;
+ struct v4l2_vbi_format *vbi;
+ struct saa7146_vv *vv;
+ int err;
+
+ err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
+ if (err)
+ return err;
+
+ v4l2_ctrl_handler_init(hdl, 6);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hdl->error) {
+ err = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
+ if (vv == NULL) {
+ ERR("out of memory. aborting.\n");
+ v4l2_ctrl_handler_free(hdl);
+ return -ENOMEM;
+ }
+ ext_vv->vid_ops = saa7146_video_ioctl_ops;
+ ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
+ ext_vv->core_ops = &saa7146_video_ioctl_ops;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* set default values for video parts of the saa7146 */
+ saa7146_write(dev, BCS_CTRL, 0x80400040);
+
+ /* enable video-port pins */
+ saa7146_write(dev, MC1, (MASK_10 | MASK_26));
+
+ /* save per-device extension data (one extension can
+ handle different devices that might need different
+ configuration data) */
+ dev->ext_vv_data = ext_vv;
+
+ vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
+ if( NULL == vv->d_clipping.cpu_addr ) {
+ ERR("out of memory. aborting.\n");
+ kfree(vv);
+ v4l2_ctrl_handler_free(hdl);
+ return -1;
+ }
+ memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
+
+ saa7146_video_uops.init(dev,vv);
+ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
+ saa7146_vbi_uops.init(dev,vv);
+
+ fmt = &vv->ov_fb.fmt;
+ fmt->width = vv->standard->h_max_out;
+ fmt->height = vv->standard->v_max_out;
+ fmt->pixelformat = V4L2_PIX_FMT_RGB565;
+ fmt->bytesperline = 2 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt = &vv->video_fmt;
+ fmt->width = 384;
+ fmt->height = 288;
+ fmt->pixelformat = V4L2_PIX_FMT_BGR24;
+ fmt->field = V4L2_FIELD_ANY;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->bytesperline = 3 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+
+ vbi = &vv->vbi_fmt;
+ vbi->sampling_rate = 27000000;
+ vbi->offset = 248; /* todo */
+ vbi->samples_per_line = 720 * 2;
+ vbi->sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ vbi->start[0] = 5;
+ vbi->count[0] = 16;
+ vbi->start[1] = 312;
+ vbi->count[1] = 16;
+
+ init_timer(&vv->vbi_read_timeout);
+
+ vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
+ dev->vv_data = vv;
+ dev->vv_callback = &vv_callback;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_init);
+
+int saa7146_vv_release(struct saa7146_dev* dev)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p\n", dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(vv);
+ dev->vv_data = NULL;
+ dev->vv_callback = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_vv_release);
+
+int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
+ char *name, int type)
+{
+ struct video_device *vfd;
+ int err;
+ int i;
+
+ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
+
+ // released by vfd->release
+ vfd = video_device_alloc();
+ if (vfd == NULL)
+ return -ENOMEM;
+
+ vfd->fops = &video_fops;
+ if (type == VFL_TYPE_GRABBER)
+ vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
+ else
+ vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
+ vfd->release = video_device_release;
+ vfd->lock = &dev->v4l2_lock;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->tvnorms = 0;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
+ strlcpy(vfd->name, name, sizeof(vfd->name));
+ video_set_drvdata(vfd, dev);
+
+ err = video_register_device(vfd, type, -1);
+ if (err < 0) {
+ ERR("cannot register v4l2 device. skipping.\n");
+ video_device_release(vfd);
+ return err;
+ }
+
+ pr_info("%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(vfd));
+
+ *vid = vfd;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_register_device);
+
+int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
+{
+ DEB_EE("dev:%p\n", dev);
+
+ video_unregister_device(*vid);
+ *vid = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_unregister_device);
+
+static int __init saa7146_vv_init_module(void)
+{
+ return 0;
+}
+
+
+static void __exit saa7146_vv_cleanup_module(void)
+{
+}
+
+module_init(saa7146_vv_init_module);
+module_exit(saa7146_vv_cleanup_module);
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146/saa7146_hlp.c
index be746d1aee9a..be746d1aee9a 100644
--- a/drivers/media/common/saa7146_hlp.c
+++ b/drivers/media/common/saa7146/saa7146_hlp.c
diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146/saa7146_i2c.c
index 22027198129d..22027198129d 100644
--- a/drivers/media/common/saa7146_i2c.c
+++ b/drivers/media/common/saa7146/saa7146_i2c.c
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c
index 1e71e374bbfe..1e71e374bbfe 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146/saa7146_vbi.c
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
new file mode 100644
index 000000000000..4143d61f79b1
--- /dev/null
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -0,0 +1,1332 @@
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <media/saa7146_vv.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <linux/module.h>
+
+static int max_memory = 32;
+
+module_param(max_memory, int, 0644);
+MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
+
+#define IS_CAPTURE_ACTIVE(fh) \
+ (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
+
+#define IS_OVERLAY_ACTIVE(fh) \
+ (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
+
+/* format descriptions for capture and preview */
+static struct saa7146_format formats[] = {
+ {
+ .name = "RGB-8 (3-3-2)",
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .trans = RGB08_COMPOSED,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "RGB-16 (5/B-6/G-5/R)",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .trans = RGB16_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }, {
+ .name = "RGB-24 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR24,
+ .trans = RGB24_COMPOSED,
+ .depth = 24,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (B-G-R)",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ }, {
+ .name = "RGB-32 (R-G-B)",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .trans = RGB32_COMPOSED,
+ .depth = 32,
+ .flags = 0,
+ .swap = 0x2,
+ }, {
+ .name = "Greyscale-8",
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .trans = Y8,
+ .depth = 8,
+ .flags = 0,
+ }, {
+ .name = "YUV 4:2:2 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .trans = YUV422_DECOMPOSED,
+ .depth = 16,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YVU 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:0 planar (Y-Cb-Cr)",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .trans = YUV420_DECOMPOSED,
+ .depth = 12,
+ .flags = FORMAT_IS_PLANAR,
+ }, {
+ .name = "YUV 4:2:2 (U-Y-V-Y)",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .trans = YUV422_COMPOSED,
+ .depth = 16,
+ .flags = 0,
+ }
+};
+
+/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
+ due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
+ (like V4L2_PIX_FMT_YUYV) ... 8-( */
+
+static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
+
+struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+{
+ int i, j = NUM_FORMATS;
+
+ for (i = 0; i < j; i++) {
+ if (formats[i].pixelformat == fourcc) {
+ return formats+i;
+ }
+ }
+
+ DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
+
+int saa7146_start_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_format fmt;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if we have overlay information */
+ if (vv->ov.fh == NULL) {
+ DEB_D("no overlay data available. try S_FMT first.\n");
+ return -EAGAIN;
+ }
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_D("overlay is already active\n");
+ return 0;
+ }
+ DEB_D("overlay is already active in another open\n");
+ return -EBUSY;
+ }
+
+ if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+ DEB_D("cannot get necessary overlay resources\n");
+ return -EBUSY;
+ }
+
+ fmt.fmt.win = vv->ov.win;
+ err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
+ if (0 != err) {
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
+ }
+ vv->ov.win = fmt.fmt.win;
+
+ DEB_D("%dx%d+%d+%d %s field=%s\n",
+ vv->ov.win.w.width, vv->ov.win.w.height,
+ vv->ov.win.w.left, vv->ov.win.w.top,
+ vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]);
+
+ if (0 != (ret = saa7146_enable_overlay(fh))) {
+ DEB_D("enabling overlay failed: %d\n", ret);
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return ret;
+ }
+
+ vv->video_status = STATUS_OVERLAY;
+ vv->video_fh = fh;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_start_preview);
+
+int saa7146_stop_preview(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ /* check if streaming capture is running */
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_D("streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ /* check if overlay is running at all */
+ if ((vv->video_status & STATUS_OVERLAY) == 0) {
+ DEB_D("no active overlay\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_D("overlay is active, but in another open\n");
+ return -EBUSY;
+ }
+
+ vv->video_status = 0;
+ vv->video_fh = NULL;
+
+ saa7146_disable_overlay(fh);
+
+ saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7146_stop_preview);
+
+/********************************************************************************/
+/* common pagetable functions */
+
+static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ struct pci_dev *pci = dev->pci;
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ struct scatterlist *list = dma->sglist;
+ int length = dma->sglen;
+ struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
+
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ struct saa7146_pgtable *pt1 = &buf->pt[0];
+ struct saa7146_pgtable *pt2 = &buf->pt[1];
+ struct saa7146_pgtable *pt3 = &buf->pt[2];
+ __le32 *ptr1, *ptr2, *ptr3;
+ __le32 fill;
+
+ int size = buf->fmt->width*buf->fmt->height;
+ int i,p,m1,m2,m3,o1,o2;
+
+ switch( sfmt->depth ) {
+ case 12: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/4))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ case 16: {
+ /* create some offsets inside the page table */
+ m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
+ m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
+ m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
+ o1 = size%PAGE_SIZE;
+ o2 = (size+(size/2))%PAGE_SIZE;
+ DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
+ size, m1, m2, m3, o1, o2);
+ break;
+ }
+ default: {
+ return -1;
+ }
+ }
+
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+
+ /* walk all pages, copy all page addresses to ptr1 */
+ for (i = 0; i < length; i++, list++) {
+ for (p = 0; p * 4096 < list->length; p++, ptr1++) {
+ *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
+ }
+ }
+/*
+ ptr1 = pt1->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+*/
+
+ /* if we have a user buffer, the first page may not be
+ aligned to a page boundary. */
+ pt1->offset = dma->sglist->offset;
+ pt2->offset = pt1->offset+o1;
+ pt3->offset = pt1->offset+o2;
+
+ /* create video-dma2 page table */
+ ptr1 = pt1->cpu;
+ for(i = m1; i <= m2 ; i++, ptr2++) {
+ *ptr2 = ptr1[i];
+ }
+ fill = *(ptr2-1);
+ for(;i<1024;i++,ptr2++) {
+ *ptr2 = fill;
+ }
+ /* create video-dma3 page table */
+ ptr1 = pt1->cpu;
+ for(i = m2; i <= m3; i++,ptr3++) {
+ *ptr3 = ptr1[i];
+ }
+ fill = *(ptr3-1);
+ for(;i<1024;i++,ptr3++) {
+ *ptr3 = fill;
+ }
+ /* finally: finish up video-dma1 page table */
+ ptr1 = pt1->cpu+m1;
+ fill = pt1->cpu[m1];
+ for(i=m1;i<1024;i++,ptr1++) {
+ *ptr1 = fill;
+ }
+/*
+ ptr1 = pt1->cpu;
+ ptr2 = pt2->cpu;
+ ptr3 = pt3->cpu;
+ for(j=0;j<40;j++) {
+ printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
+ }
+ for(j=0;j<40;j++) {
+ printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
+ }
+*/
+ } else {
+ struct saa7146_pgtable *pt = &buf->pt[0];
+ return saa7146_pgtable_build_single(pci, pt, list, length);
+ }
+
+ return 0;
+}
+
+
+/********************************************************************************/
+/* file operations */
+
+static int video_begin(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned int resource;
+ int ret = 0, err = 0;
+
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return 0;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ DEB_S("warning: suspending overlay video for streaming capture\n");
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ }
+
+ ret = saa7146_res_get(fh, resource);
+ if (0 == ret) {
+ DEB_S("cannot get capture resource %d\n", resource);
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+ return -EBUSY;
+ }
+
+ /* clear out beginning of streaming bit (rps register 0)*/
+ saa7146_write(dev, MC2, MASK_27 );
+
+ /* enable rps0 irqs */
+ SAA7146_IER_ENABLE(dev, MASK_27);
+
+ vv->video_fh = fh;
+ vv->video_status = STATUS_CAPTURE;
+
+ return 0;
+}
+
+static int video_end(struct saa7146_fh *fh, struct file *file)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt = NULL;
+ unsigned long flags;
+ unsigned int resource;
+ u32 dmas = 0;
+ DEB_EE("dev:%p, fh:%p\n", dev, fh);
+
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
+ /* we need to have a valid format set here */
+ BUG_ON(NULL == fmt);
+
+ if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+ resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+ dmas = MASK_22 | MASK_21 | MASK_20;
+ } else {
+ resource = RESOURCE_DMA1_HPS;
+ dmas = MASK_22;
+ }
+ spin_lock_irqsave(&dev->slock,flags);
+
+ /* disable rps0 */
+ saa7146_write(dev, MC1, MASK_28);
+
+ /* disable rps0 irqs */
+ SAA7146_IER_DISABLE(dev, MASK_27);
+
+ /* shut down all used video dma transfers */
+ saa7146_write(dev, MC1, dmas);
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ vv->video_fh = NULL;
+ vv->video_status = 0;
+
+ saa7146_res_free(fh, resource);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ strcpy((char *)cap->driver, "saa7146 v4l2");
+ strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+ sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->device_caps =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->device_caps |= dev->ext_vv_data->capabilities;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ if (vdev->vfl_type == VFL_TYPE_GRABBER)
+ cap->device_caps &=
+ ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
+ else
+ cap->device_caps &=
+ ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *fb = vv->ov_fb;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ fb->flags = V4L2_FBUF_FLAG_PRIMARY;
+ return 0;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+
+ DEB_EE("VIDIOC_S_FBUF\n");
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* planar formats are not allowed for overlay video, clipping and video dma would clash */
+ if (fmt->flags & FORMAT_IS_PLANAR)
+ DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
+ (char *)&fmt->pixelformat);
+
+ /* check if overlay is running */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if (vv->video_fh != fh) {
+ DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
+ return -EBUSY;
+ }
+ }
+
+ /* ok, accept it */
+ vv->ov_fb = *fb;
+ vv->ov_fmt = fmt;
+
+ if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
+ vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
+ DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
+ }
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+ strlcpy((char *)f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].pixelformat;
+ return 0;
+}
+
+int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct saa7146_vv *vv = dev->vv_data;
+ u32 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0x00ffffff;
+ val |= (ctrl->val << 24);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xff00ffff;
+ val |= (ctrl->val << 16);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_SATURATION:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xffffff00;
+ val |= (ctrl->val << 0);
+ saa7146_write(dev, BCS_CTRL, val);
+ saa7146_write(dev, MC2, MASK_22 | MASK_06);
+ break;
+
+ case V4L2_CID_HFLIP:
+ /* fixme: we can support changing VFLIP and HFLIP here... */
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if ((vv->video_status & STATUS_CAPTURE))
+ return -EBUSY;
+ vv->vflip = ctrl->val;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
+ struct saa7146_fh *fh = vv->video_fh;
+
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ parm->parm.capture.readbuffers = 1;
+ v4l2_video_std_frame_period(vv->standard->id,
+ &parm->parm.capture.timeperframe);
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.pix = vv->video_fmt;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.win = vv->ov.win;
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.vbi = vv->vbi_fmt;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_format *fmt;
+ enum v4l2_field field;
+ int maxw, maxh;
+ int calc_bpl;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+
+ fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_ALTERNATE:
+ vv->last_field = V4L2_FIELD_TOP;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ vv->last_field = V4L2_FIELD_INTERLACED;
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+
+ calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+ if (f->fmt.pix.bytesperline < calc_bpl)
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
+ f->fmt.pix.bytesperline = calc_bpl;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+ DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
+ f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+
+ return 0;
+}
+
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct v4l2_window *win = &f->fmt.win;
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ DEB_EE("dev:%p\n", dev);
+
+ if (NULL == vv->ov_fb.base) {
+ DEB_D("no fb base set\n");
+ return -EINVAL;
+ }
+ if (NULL == vv->ov_fmt) {
+ DEB_D("no fb fmt set\n");
+ return -EINVAL;
+ }
+ if (win->w.width < 48 || win->w.height < 32) {
+ DEB_D("min width/height. (%d,%d)\n",
+ win->w.width, win->w.height);
+ return -EINVAL;
+ }
+ if (win->clipcount > 16) {
+ DEB_D("clipcount too big\n");
+ return -EINVAL;
+ }
+
+ field = win->field;
+ maxw = vv->standard->h_max_out;
+ maxh = vv->standard->v_max_out;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh / 2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ DEB_D("no known field mode '%d'\n", field);
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
+ if (IS_CAPTURE_ACTIVE(fh) != 0) {
+ DEB_EE("streaming capture is active\n");
+ return -EBUSY;
+ }
+ err = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->video_fmt = f->fmt.pix;
+ DEB_EE("set to pixelformat '%4.4s'\n",
+ (char *)&vv->video_fmt.pixelformat);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
+ err = vidioc_try_fmt_vid_overlay(file, fh, f);
+ if (0 != err)
+ return err;
+ vv->ov.win = f->fmt.win;
+ vv->ov.nclips = f->fmt.win.clipcount;
+ if (vv->ov.nclips > 16)
+ vv->ov.nclips = 16;
+ if (copy_from_user(vv->ov.clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip) * vv->ov.nclips)) {
+ return -EFAULT;
+ }
+
+ /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
+ vv->ov.fh = fh;
+
+ /* check if our current overlay is active */
+ if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ saa7146_stop_preview(fh);
+ saa7146_start_preview(fh);
+ }
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ *norm = vv->standard->id;
+ return 0;
+}
+
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
+/*
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+ if (e->index < 0 )
+ return -EINVAL;
+ if( e->index < dev->ext_vv_data->num_stds ) {
+ DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
+ v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
+ return 0;
+ }
+ return -EINVAL;
+ }
+ */
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int found = 0;
+ int err, i;
+
+ DEB_EE("VIDIOC_S_STD\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+ DEB_D("cannot change video standard while streaming capture is active\n");
+ return -EBUSY;
+ }
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (0 != err) {
+ DEB_D("suspending video failed. aborting\n");
+ return err;
+ }
+ }
+
+ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+ if (*id & dev->ext_vv_data->stds[i].id)
+ break;
+ if (i != dev->ext_vv_data->num_stds) {
+ vv->standard = &dev->ext_vv_data->stds[i];
+ if (NULL != dev->ext_vv_data->std_callback)
+ dev->ext_vv_data->std_callback(dev, vv->standard);
+ found = 1;
+ }
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ if (!found) {
+ DEB_EE("VIDIOC_S_STD: standard not found\n");
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
+{
+ int err;
+
+ DEB_D("VIDIOC_OVERLAY on:%d\n", on);
+ if (on)
+ err = saa7146_start_preview(fh);
+ else
+ err = saa7146_stop_preview(fh);
+ return err;
+}
+
+static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->video_q, b);
+ if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_reqbufs(&fh->vbi_q, b);
+ return -EINVAL;
+}
+
+static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_querybuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->video_q, buf);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_qbuf(&fh->vbi_q, buf);
+ return -EINVAL;
+}
+
+static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct saa7146_fh *fh = __fh;
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
+ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ int err;
+
+ DEB_D("VIDIOC_STREAMON, type:%d\n", type);
+
+ err = video_begin(fh);
+ if (err)
+ return err;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_streamon(&fh->video_q);
+ if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return videobuf_streamon(&fh->vbi_q);
+ return -EINVAL;
+}
+
+static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ int err;
+
+ DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
+
+ /* ugly: we need to copy some checks from video_end(),
+ because videobuf_streamoff() relies on the capture running.
+ check and fix this */
+ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+ DEB_S("not capturing\n");
+ return 0;
+ }
+
+ if (vv->video_fh != fh) {
+ DEB_S("capturing, but in another open\n");
+ return -EBUSY;
+ }
+
+ err = -EINVAL;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ err = videobuf_streamoff(&fh->video_q);
+ else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ err = videobuf_streamoff(&fh->vbi_q);
+ if (0 != err) {
+ DEB_D("warning: videobuf_streamoff() failed\n");
+ video_end(fh, file);
+ } else {
+ err = video_end(fh, file);
+ }
+ return err;
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *__fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa7146_fh *fh = __fh;
+ struct saa7146_dev *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(&chip->match))
+ chip->ident = V4L2_IDENT_SAA7146;
+ return 0;
+ }
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+ core, g_chip_ident, chip);
+}
+
+const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+
+ .vidioc_overlay = vidioc_overlay,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/*********************************************************************************/
+/* buffer handling functions */
+
+static int buffer_activate (struct saa7146_dev *dev,
+ struct saa7146_buf *buf,
+ struct saa7146_buf *next)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ saa7146_set_capture(dev,buf,next);
+
+ mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
+{
+ saa7146_pgtable_free(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_free(dev->pci, &buf->pt[2]);
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+ int size,err = 0;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ /* sanity checks */
+ if (vv->video_fmt.width < 48 ||
+ vv->video_fmt.height < 32 ||
+ vv->video_fmt.width > vv->standard->h_max_out ||
+ vv->video_fmt.height > vv->standard->v_max_out) {
+ DEB_D("w (%d) / h (%d) out of bounds\n",
+ vv->video_fmt.width, vv->video_fmt.height);
+ return -EINVAL;
+ }
+
+ size = vv->video_fmt.sizeimage;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size) {
+ DEB_D("size mismatch\n");
+ return -EINVAL;
+ }
+
+ DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
+ vv->video_fmt.width, vv->video_fmt.height,
+ size, v4l2_field_names[vv->video_fmt.field]);
+ if (buf->vb.width != vv->video_fmt.width ||
+ buf->vb.bytesperline != vv->video_fmt.bytesperline ||
+ buf->vb.height != vv->video_fmt.height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->vb.field != vv->video_fmt.field ||
+ buf->fmt != &vv->video_fmt) {
+ saa7146_dma_free(dev,q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct saa7146_format *sfmt;
+
+ buf->vb.bytesperline = vv->video_fmt.bytesperline;
+ buf->vb.width = vv->video_fmt.width;
+ buf->vb.height = vv->video_fmt.height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = &vv->video_fmt;
+ buf->vb.field = vv->video_fmt.field;
+
+ sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
+
+ release_all_pagetables(dev, buf);
+ if( 0 != IS_PLANAR(sfmt->trans)) {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
+ } else {
+ saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
+ }
+
+ err = videobuf_iolock(q,&buf->vb, &vv->ov_fb);
+ if (err)
+ goto oops;
+ err = saa7146_pgtable_build(dev,buf);
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+
+ return 0;
+
+ oops:
+ DEB_D("error out\n");
+ saa7146_dma_free(dev,q,buf);
+
+ return err;
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
+
+ if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
+ *count = MAX_SAA7146_CAPTURE_BUFFERS;
+
+ *size = vv->video_fmt.sizeimage;
+
+ /* check if we exceed the "max_memory" parameter */
+ if( (*count * *size) > (max_memory*1048576) ) {
+ *count = (max_memory*1048576) / *size;
+ }
+
+ DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
+
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+ saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct file *file = q->priv_data;
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_buf *buf = (struct saa7146_buf *)vb;
+
+ DEB_CAP("vbuf:%p\n", vb);
+
+ saa7146_dma_free(dev,q,buf);
+
+ release_all_pagetables(dev, buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************************************************************************/
+/* file operations */
+
+static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
+{
+ INIT_LIST_HEAD(&vv->video_dmaq.queue);
+
+ init_timer(&vv->video_dmaq.timeout);
+ vv->video_dmaq.timeout.function = saa7146_buffer_timeout;
+ vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq);
+ vv->video_dmaq.dev = dev;
+
+ /* set some default values */
+ vv->standard = &dev->ext_vv_data->stds[0];
+
+ /* FIXME: what's this? */
+ vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
+ vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
+}
+
+
+static int video_open(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+
+ videobuf_queue_sg_init(&fh->video_q, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7146_buf),
+ file, &dev->v4l2_lock);
+
+ return 0;
+}
+
+
+static void video_close(struct saa7146_dev *dev, struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct videobuf_queue *q = &fh->video_q;
+
+ if (IS_CAPTURE_ACTIVE(fh) != 0)
+ video_end(fh, file);
+ else if (IS_OVERLAY_ACTIVE(fh) != 0)
+ saa7146_stop_preview(fh);
+
+ videobuf_stop(q);
+ /* hmm, why is this function declared void? */
+}
+
+
+static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
+{
+ struct saa7146_vv *vv = dev->vv_data;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
+
+ spin_lock(&dev->slock);
+ DEB_CAP("called\n");
+
+ /* only finish the buffer if we have one... */
+ if( NULL != q->curr ) {
+ saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
+ }
+ saa7146_buffer_next(dev,q,0);
+
+ spin_unlock(&dev->slock);
+}
+
+static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ ssize_t ret = 0;
+
+ DEB_EE("called\n");
+
+ if ((vv->video_status & STATUS_CAPTURE) != 0) {
+ /* fixme: should we allow read() captures while streaming capture? */
+ if (vv->video_fh == fh) {
+ DEB_S("already capturing\n");
+ return -EBUSY;
+ }
+ DEB_S("already capturing in another open\n");
+ return -EBUSY;
+ }
+
+ ret = video_begin(fh);
+ if( 0 != ret) {
+ goto out;
+ }
+
+ ret = videobuf_read_one(&fh->video_q , data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (ret != 0) {
+ video_end(fh, file);
+ } else {
+ ret = video_end(fh, file);
+ }
+out:
+ /* restart overlay if it was active before */
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return ret;
+}
+
+struct saa7146_use_ops saa7146_video_uops = {
+ .init = video_init,
+ .open = video_open,
+ .release = video_close,
+ .irq_done = video_irq_done,
+ .read = video_read,
+};
diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
deleted file mode 100644
index d6b1cf66042d..000000000000
--- a/drivers/media/common/saa7146_core.c
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- saa7146.o - driver for generic saa7146-based hardware
-
- Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <media/saa7146.h>
-#include <linux/module.h>
-
-LIST_HEAD(saa7146_devices);
-DEFINE_MUTEX(saa7146_devices_lock);
-
-static int saa7146_num;
-
-unsigned int saa7146_debug;
-
-module_param(saa7146_debug, uint, 0644);
-MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
-
-#if 0
-static void dump_registers(struct saa7146_dev* dev)
-{
- int i = 0;
-
- pr_info(" @ %li jiffies:\n", jiffies);
- for (i = 0; i <= 0x148; i += 4)
- pr_info("0x%03x: 0x%08x\n", i, saa7146_read(dev, i));
-}
-#endif
-
-/****************************************************************************
- * gpio and debi helper functions
- ****************************************************************************/
-
-void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
-{
- u32 value = 0;
-
- BUG_ON(port > 3);
-
- value = saa7146_read(dev, GPIO_CTRL);
- value &= ~(0xff << (8*port));
- value |= (data << (8*port));
- saa7146_write(dev, GPIO_CTRL, value);
-}
-
-/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
-static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
- unsigned long us1, unsigned long us2)
-{
- unsigned long timeout;
- int err;
-
- /* wait for registers to be programmed */
- timeout = jiffies + usecs_to_jiffies(us1);
- while (1) {
- err = time_after(jiffies, timeout);
- if (saa7146_read(dev, MC2) & 2)
- break;
- if (err) {
- pr_err("%s: %s timed out while waiting for registers getting programmed\n",
- dev->name, __func__);
- return -ETIMEDOUT;
- }
- msleep(1);
- }
-
- /* wait for transfer to complete */
- timeout = jiffies + usecs_to_jiffies(us2);
- while (1) {
- err = time_after(jiffies, timeout);
- if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
- break;
- saa7146_read(dev, MC2);
- if (err) {
- DEB_S("%s: %s timed out while waiting for transfer completion\n",
- dev->name, __func__);
- return -ETIMEDOUT;
- }
- msleep(1);
- }
-
- return 0;
-}
-
-static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
- unsigned long us1, unsigned long us2)
-{
- unsigned long loops;
-
- /* wait for registers to be programmed */
- loops = us1;
- while (1) {
- if (saa7146_read(dev, MC2) & 2)
- break;
- if (!loops--) {
- pr_err("%s: %s timed out while waiting for registers getting programmed\n",
- dev->name, __func__);
- return -ETIMEDOUT;
- }
- udelay(1);
- }
-
- /* wait for transfer to complete */
- loops = us2 / 5;
- while (1) {
- if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
- break;
- saa7146_read(dev, MC2);
- if (!loops--) {
- DEB_S("%s: %s timed out while waiting for transfer completion\n",
- dev->name, __func__);
- return -ETIMEDOUT;
- }
- udelay(5);
- }
-
- return 0;
-}
-
-int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
-{
- if (nobusyloop)
- return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
- else
- return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
-}
-
-/****************************************************************************
- * general helper functions
- ****************************************************************************/
-
-/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.c
- make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
- may be triggered on highmem machines */
-static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
-{
- struct scatterlist *sglist;
- struct page *pg;
- int i;
-
- sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
- if (NULL == sglist)
- return NULL;
- sg_init_table(sglist, nr_pages);
- for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
- pg = vmalloc_to_page(virt);
- if (NULL == pg)
- goto err;
- BUG_ON(PageHighMem(pg));
- sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
- }
- return sglist;
-
- err:
- kfree(sglist);
- return NULL;
-}
-
-/********************************************************************************/
-/* common page table functions */
-
-void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
-{
- int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
- void *mem = vmalloc_32(length);
- int slen = 0;
-
- if (NULL == mem)
- goto err_null;
-
- if (!(pt->slist = vmalloc_to_sg(mem, pages)))
- goto err_free_mem;
-
- if (saa7146_pgtable_alloc(pci, pt))
- goto err_free_slist;
-
- pt->nents = pages;
- slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
- if (0 == slen)
- goto err_free_pgtable;
-
- if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
- goto err_unmap_sg;
-
- return mem;
-
-err_unmap_sg:
- pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
-err_free_pgtable:
- saa7146_pgtable_free(pci, pt);
-err_free_slist:
- kfree(pt->slist);
- pt->slist = NULL;
-err_free_mem:
- vfree(mem);
-err_null:
- return NULL;
-}
-
-void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
-{
- pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
- saa7146_pgtable_free(pci, pt);
- kfree(pt->slist);
- pt->slist = NULL;
- vfree(mem);
-}
-
-void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
-{
- if (NULL == pt->cpu)
- return;
- pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
- pt->cpu = NULL;
-}
-
-int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
-{
- __le32 *cpu;
- dma_addr_t dma_addr = 0;
-
- cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
- if (NULL == cpu) {
- return -ENOMEM;
- }
- pt->size = PAGE_SIZE;
- pt->cpu = cpu;
- pt->dma = dma_addr;
-
- return 0;
-}
-
-int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
- struct scatterlist *list, int sglen )
-{
- __le32 *ptr, fill;
- int nr_pages = 0;
- int i,p;
-
- BUG_ON(0 == sglen);
- BUG_ON(list->offset > PAGE_SIZE);
-
- /* if we have a user buffer, the first page may not be
- aligned to a page boundary. */
- pt->offset = list->offset;
-
- ptr = pt->cpu;
- for (i = 0; i < sglen; i++, list++) {
-/*
- pr_debug("i:%d, adr:0x%08x, len:%d, offset:%d\n",
- i, sg_dma_address(list), sg_dma_len(list),
- list->offset);
-*/
- for (p = 0; p * 4096 < list->length; p++, ptr++) {
- *ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
- nr_pages++;
- }
- }
-
-
- /* safety; fill the page table up with the last valid page */
- fill = *(ptr-1);
- for(i=nr_pages;i<1024;i++) {
- *ptr++ = fill;
- }
-
-/*
- ptr = pt->cpu;
- pr_debug("offset: %d\n", pt->offset);
- for(i=0;i<5;i++) {
- pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
- }
-*/
- return 0;
-}
-
-/********************************************************************************/
-/* interrupt handler */
-static irqreturn_t interrupt_hw(int irq, void *dev_id)
-{
- struct saa7146_dev *dev = dev_id;
- u32 isr;
- u32 ack_isr;
-
- /* read out the interrupt status register */
- ack_isr = isr = saa7146_read(dev, ISR);
-
- /* is this our interrupt? */
- if ( 0 == isr ) {
- /* nope, some other device */
- return IRQ_NONE;
- }
-
- if (dev->ext) {
- if (dev->ext->irq_mask & isr) {
- if (dev->ext->irq_func)
- dev->ext->irq_func(dev, &isr);
- isr &= ~dev->ext->irq_mask;
- }
- }
- if (0 != (isr & (MASK_27))) {
- DEB_INT("irq: RPS0 (0x%08x)\n", isr);
- if (dev->vv_data && dev->vv_callback)
- dev->vv_callback(dev,isr);
- isr &= ~MASK_27;
- }
- if (0 != (isr & (MASK_28))) {
- if (dev->vv_data && dev->vv_callback)
- dev->vv_callback(dev,isr);
- isr &= ~MASK_28;
- }
- if (0 != (isr & (MASK_16|MASK_17))) {
- SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
- /* only wake up if we expect something */
- if (0 != dev->i2c_op) {
- dev->i2c_op = 0;
- wake_up(&dev->i2c_wq);
- } else {
- u32 psr = saa7146_read(dev, PSR);
- u32 ssr = saa7146_read(dev, SSR);
- pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
- dev->name, isr, psr, ssr);
- }
- isr &= ~(MASK_16|MASK_17);
- }
- if( 0 != isr ) {
- ERR("warning: interrupt enabled, but not handled properly.(0x%08x)\n",
- isr);
- ERR("disabling interrupt source(s)!\n");
- SAA7146_IER_DISABLE(dev,isr);
- }
- saa7146_write(dev, ISR, ack_isr);
- return IRQ_HANDLED;
-}
-
-/*********************************************************************************/
-/* configuration-functions */
-
-static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
-{
- struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
- struct saa7146_extension *ext = pci_ext->ext;
- struct saa7146_dev *dev;
- int err = -ENOMEM;
-
- /* clear out mem for sure */
- dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
- if (!dev) {
- ERR("out of memory\n");
- goto out;
- }
-
- DEB_EE("pci:%p\n", pci);
-
- err = pci_enable_device(pci);
- if (err < 0) {
- ERR("pci_enable_device() failed\n");
- goto err_free;
- }
-
- /* enable bus-mastering */
- pci_set_master(pci);
-
- dev->pci = pci;
-
- /* get chip-revision; this is needed to enable bug-fixes */
- dev->revision = pci->revision;
-
- /* remap the memory from virtual to physical address */
-
- err = pci_request_region(pci, 0, "saa7146");
- if (err < 0)
- goto err_disable;
-
- dev->mem = ioremap(pci_resource_start(pci, 0),
- pci_resource_len(pci, 0));
- if (!dev->mem) {
- ERR("ioremap() failed\n");
- err = -ENODEV;
- goto err_release;
- }
-
- /* we don't do a master reset here anymore, it screws up
- some boards that don't have an i2c-eeprom for configuration
- values */
-/*
- saa7146_write(dev, MC1, MASK_31);
-*/
-
- /* disable all irqs */
- saa7146_write(dev, IER, 0);
-
- /* shut down all dma transfers and rps tasks */
- saa7146_write(dev, MC1, 0x30ff0000);
-
- /* clear out any rps-signals pending */
- saa7146_write(dev, MC2, 0xf8000000);
-
- /* request an interrupt for the saa7146 */
- err = request_irq(pci->irq, interrupt_hw, IRQF_SHARED | IRQF_DISABLED,
- dev->name, dev);
- if (err < 0) {
- ERR("request_irq() failed\n");
- goto err_unmap;
- }
-
- err = -ENOMEM;
-
- /* get memory for various stuff */
- dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_rps0.dma_handle);
- if (!dev->d_rps0.cpu_addr)
- goto err_free_irq;
- memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
-
- dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_rps1.dma_handle);
- if (!dev->d_rps1.cpu_addr)
- goto err_free_rps0;
- memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
-
- dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
- &dev->d_i2c.dma_handle);
- if (!dev->d_i2c.cpu_addr)
- goto err_free_rps1;
- memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
-
- /* the rest + print status message */
-
- /* create a nice device name */
- sprintf(dev->name, "saa7146 (%d)", saa7146_num);
-
- pr_info("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x)\n",
- dev->mem, dev->revision, pci->irq,
- pci->subsystem_vendor, pci->subsystem_device);
- dev->ext = ext;
-
- mutex_init(&dev->v4l2_lock);
- spin_lock_init(&dev->int_slock);
- spin_lock_init(&dev->slock);
-
- mutex_init(&dev->i2c_lock);
-
- dev->module = THIS_MODULE;
- init_waitqueue_head(&dev->i2c_wq);
-
- /* set some sane pci arbitrition values */
- saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
-
- /* TODO: use the status code of the callback */
-
- err = -ENODEV;
-
- if (ext->probe && ext->probe(dev)) {
- DEB_D("ext->probe() failed for %p. skipping device.\n", dev);
- goto err_free_i2c;
- }
-
- if (ext->attach(dev, pci_ext)) {
- DEB_D("ext->attach() failed for %p. skipping device.\n", dev);
- goto err_free_i2c;
- }
- /* V4L extensions will set the pci drvdata to the v4l2_device in the
- attach() above. So for those cards that do not use V4L we have to
- set it explicitly. */
- pci_set_drvdata(pci, &dev->v4l2_dev);
-
- INIT_LIST_HEAD(&dev->item);
- list_add_tail(&dev->item,&saa7146_devices);
- saa7146_num++;
-
- err = 0;
-out:
- return err;
-
-err_free_i2c:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
- dev->d_i2c.dma_handle);
-err_free_rps1:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
- dev->d_rps1.dma_handle);
-err_free_rps0:
- pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
- dev->d_rps0.dma_handle);
-err_free_irq:
- free_irq(pci->irq, (void *)dev);
-err_unmap:
- iounmap(dev->mem);
-err_release:
- pci_release_region(pci, 0);
-err_disable:
- pci_disable_device(pci);
-err_free:
- kfree(dev);
- goto out;
-}
-
-static void saa7146_remove_one(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
- struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
- struct {
- void *addr;
- dma_addr_t dma;
- } dev_map[] = {
- { dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
- { dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
- { dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
- { NULL, 0 }
- }, *p;
-
- DEB_EE("dev:%p\n", dev);
-
- dev->ext->detach(dev);
- /* Zero the PCI drvdata after use. */
- pci_set_drvdata(pdev, NULL);
-
- /* shut down all video dma transfers */
- saa7146_write(dev, MC1, 0x00ff0000);
-
- /* disable all irqs, release irq-routine */
- saa7146_write(dev, IER, 0);
-
- free_irq(pdev->irq, dev);
-
- for (p = dev_map; p->addr; p++)
- pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
-
- iounmap(dev->mem);
- pci_release_region(pdev, 0);
- list_del(&dev->item);
- pci_disable_device(pdev);
- kfree(dev);
-
- saa7146_num--;
-}
-
-/*********************************************************************************/
-/* extension handling functions */
-
-int saa7146_register_extension(struct saa7146_extension* ext)
-{
- DEB_EE("ext:%p\n", ext);
-
- ext->driver.name = ext->name;
- ext->driver.id_table = ext->pci_tbl;
- ext->driver.probe = saa7146_init_one;
- ext->driver.remove = saa7146_remove_one;
-
- pr_info("register extension '%s'\n", ext->name);
- return pci_register_driver(&ext->driver);
-}
-
-int saa7146_unregister_extension(struct saa7146_extension* ext)
-{
- DEB_EE("ext:%p\n", ext);
- pr_info("unregister extension '%s'\n", ext->name);
- pci_unregister_driver(&ext->driver);
- return 0;
-}
-
-EXPORT_SYMBOL_GPL(saa7146_register_extension);
-EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
-
-/* misc functions used by extension modules */
-EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
-EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
-EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
-EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
-EXPORT_SYMBOL_GPL(saa7146_vfree_destroy_pgtable);
-EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
-
-EXPORT_SYMBOL_GPL(saa7146_setgpio);
-
-EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
-
-EXPORT_SYMBOL_GPL(saa7146_debug);
-EXPORT_SYMBOL_GPL(saa7146_devices);
-EXPORT_SYMBOL_GPL(saa7146_devices_lock);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
deleted file mode 100644
index 0cdbd742974a..000000000000
--- a/drivers/media/common/saa7146_fops.c
+++ /dev/null
@@ -1,641 +0,0 @@
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <media/saa7146_vv.h>
-#include <linux/module.h>
-
-/****************************************************************************/
-/* resource management functions, shamelessly stolen from saa7134 driver */
-
-int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- if (fh->resources & bit) {
- DEB_D("already allocated! want: 0x%02x, cur:0x%02x\n",
- bit, vv->resources);
- /* have it already allocated */
- return 1;
- }
-
- /* is it free? */
- if (vv->resources & bit) {
- DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
- vv->resources, bit);
- /* no, someone else uses it */
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- vv->resources |= bit;
- DEB_D("res: get 0x%02x, cur:0x%02x\n", bit, vv->resources);
- return 1;
-}
-
-void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- BUG_ON((fh->resources & bits) != bits);
-
- fh->resources &= ~bits;
- vv->resources &= ~bits;
- DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
-}
-
-
-/********************************************************************************/
-/* common dma functions */
-
-void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
- struct saa7146_buf *buf)
-{
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- DEB_EE("dev:%p, buf:%p\n", dev, buf);
-
- BUG_ON(in_interrupt());
-
- videobuf_waiton(q, &buf->vb, 0, 0);
- videobuf_dma_unmap(q->dev, dma);
- videobuf_dma_free(dma);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-
-/********************************************************************************/
-/* common buffer functions */
-
-int saa7146_buffer_queue(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q,
- struct saa7146_buf *buf)
-{
- assert_spin_locked(&dev->slock);
- DEB_EE("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf);
-
- BUG_ON(!q);
-
- if (NULL == q->curr) {
- q->curr = buf;
- DEB_D("immediately activating buffer %p\n", buf);
- buf->activate(dev,buf,NULL);
- } else {
- list_add_tail(&buf->vb.queue,&q->queue);
- buf->vb.state = VIDEOBUF_QUEUED;
- DEB_D("adding buffer %p to queue. (active buffer present)\n",
- buf);
- }
- return 0;
-}
-
-void saa7146_buffer_finish(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q,
- int state)
-{
- assert_spin_locked(&dev->slock);
- DEB_EE("dev:%p, dmaq:%p, state:%d\n", dev, q, state);
- DEB_EE("q->curr:%p\n", q->curr);
-
- BUG_ON(!q->curr);
-
- /* finish current buffer */
- if (NULL == q->curr) {
- DEB_D("aiii. no current buffer\n");
- return;
- }
-
- q->curr->vb.state = state;
- do_gettimeofday(&q->curr->vb.ts);
- wake_up(&q->curr->vb.done);
-
- q->curr = NULL;
-}
-
-void saa7146_buffer_next(struct saa7146_dev *dev,
- struct saa7146_dmaqueue *q, int vbi)
-{
- struct saa7146_buf *buf,*next = NULL;
-
- BUG_ON(!q);
-
- DEB_INT("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi);
-
- assert_spin_locked(&dev->slock);
- if (!list_empty(&q->queue)) {
- /* activate next one from queue */
- buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
- list_del(&buf->vb.queue);
- if (!list_empty(&q->queue))
- next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
- q->curr = buf;
- DEB_INT("next buffer: buf:%p, prev:%p, next:%p\n",
- buf, q->queue.prev, q->queue.next);
- buf->activate(dev,buf,next);
- } else {
- DEB_INT("no next buffer. stopping.\n");
- if( 0 != vbi ) {
- /* turn off video-dma3 */
- saa7146_write(dev,MC1, MASK_20);
- } else {
- /* nothing to do -- just prevent next video-dma1 transfer
- by lowering the protection address */
-
- // fixme: fix this for vflip != 0
-
- saa7146_write(dev, PROT_ADDR1, 0);
- saa7146_write(dev, MC2, (MASK_02|MASK_18));
-
- /* write the address of the rps-program */
- saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
- /* turn on rps */
- saa7146_write(dev, MC1, (MASK_12 | MASK_28));
-
-/*
- printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
- printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
- printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
- printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
- printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
- printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
-*/
- }
- del_timer(&q->timeout);
- }
-}
-
-void saa7146_buffer_timeout(unsigned long data)
-{
- struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
- struct saa7146_dev *dev = q->dev;
- unsigned long flags;
-
- DEB_EE("dev:%p, dmaq:%p\n", dev, q);
-
- spin_lock_irqsave(&dev->slock,flags);
- if (q->curr) {
- DEB_D("timeout on %p\n", q->curr);
- saa7146_buffer_finish(dev,q,VIDEOBUF_ERROR);
- }
-
- /* we don't restart the transfer here like other drivers do. when
- a streaming capture is disabled, the timeout function will be
- called for the current buffer. if we activate the next buffer now,
- we mess up our capture logic. if a timeout occurs on another buffer,
- then something is seriously broken before, so no need to buffer the
- next capture IMHO... */
-/*
- saa7146_buffer_next(dev,q);
-*/
- spin_unlock_irqrestore(&dev->slock,flags);
-}
-
-/********************************************************************************/
-/* file operations */
-
-static int fops_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_dev *dev = video_drvdata(file);
- struct saa7146_fh *fh = NULL;
- int result = 0;
-
- DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
-
- if (mutex_lock_interruptible(&saa7146_devices_lock))
- return -ERESTARTSYS;
-
- DEB_D("using: %p\n", dev);
-
- /* check if an extension is registered */
- if( NULL == dev->ext ) {
- DEB_S("no extension registered for this device\n");
- result = -ENODEV;
- goto out;
- }
-
- /* allocate per open data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh) {
- DEB_S("cannot allocate memory for per open data\n");
- result = -ENOMEM;
- goto out;
- }
-
- v4l2_fh_init(&fh->fh, vdev);
-
- file->private_data = &fh->fh;
- fh->dev = dev;
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- DEB_S("initializing vbi...\n");
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- result = saa7146_vbi_uops.open(dev,file);
- if (dev->ext_vv_data->vbi_fops.open)
- dev->ext_vv_data->vbi_fops.open(file);
- } else {
- DEB_S("initializing video...\n");
- result = saa7146_video_uops.open(dev,file);
- }
-
- if (0 != result) {
- goto out;
- }
-
- if( 0 == try_module_get(dev->ext->module)) {
- result = -EINVAL;
- goto out;
- }
-
- result = 0;
- v4l2_fh_add(&fh->fh);
-out:
- if (fh && result != 0) {
- kfree(fh);
- file->private_data = NULL;
- }
- mutex_unlock(&saa7146_devices_lock);
- return result;
-}
-
-static int fops_release(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
-
- DEB_EE("file:%p\n", file);
-
- if (mutex_lock_interruptible(&saa7146_devices_lock))
- return -ERESTARTSYS;
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- saa7146_vbi_uops.release(dev,file);
- if (dev->ext_vv_data->vbi_fops.release)
- dev->ext_vv_data->vbi_fops.release(file);
- } else {
- saa7146_video_uops.release(dev,file);
- }
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- module_put(dev->ext->module);
- file->private_data = NULL;
- kfree(fh);
-
- mutex_unlock(&saa7146_devices_lock);
-
- return 0;
-}
-
-static int fops_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct videobuf_queue *q;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER: {
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
- file, vma);
- q = &fh->video_q;
- break;
- }
- case VFL_TYPE_VBI: {
- DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
- file, vma);
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
- return -ENODEV;
- q = &fh->vbi_q;
- break;
- }
- default:
- BUG();
- return 0;
- }
-
- return videobuf_mmap_mapper(q,vma);
-}
-
-static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
- struct videobuf_buffer *buf = NULL;
- struct videobuf_queue *q;
- unsigned int res = v4l2_ctrl_poll(file, wait);
-
- DEB_EE("file:%p, poll:%p\n", file, wait);
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
- return res | POLLOUT | POLLWRNORM;
- if( 0 == fh->vbi_q.streaming )
- return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
- q = &fh->vbi_q;
- } else {
- DEB_D("using video queue\n");
- q = &fh->video_q;
- }
-
- if (!list_empty(&q->stream))
- buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
-
- if (!buf) {
- DEB_D("buf == NULL!\n");
- return res | POLLERR;
- }
-
- poll_wait(file, &buf->done, wait);
- if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
- DEB_D("poll succeeded!\n");
- return res | POLLIN | POLLRDNORM;
- }
-
- DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
- return res;
-}
-
-static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
-/*
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
- file, data, (unsigned long)count);
-*/
- return saa7146_video_uops.read(file,data,count,ppos);
- case VFL_TYPE_VBI:
-/*
- DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
- file, data, (unsigned long)count);
-*/
- if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- return saa7146_vbi_uops.read(file,data,count,ppos);
- return -EINVAL;
- default:
- BUG();
- return 0;
- }
-}
-
-static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return -EINVAL;
- case VFL_TYPE_VBI:
- if (fh->dev->ext_vv_data->vbi_fops.write)
- return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
- else
- return -EINVAL;
- default:
- BUG();
- return -EINVAL;
- }
-}
-
-static const struct v4l2_file_operations video_fops =
-{
- .owner = THIS_MODULE,
- .open = fops_open,
- .release = fops_release,
- .read = fops_read,
- .write = fops_write,
- .poll = fops_poll,
- .mmap = fops_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static void vv_callback(struct saa7146_dev *dev, unsigned long status)
-{
- u32 isr = status;
-
- DEB_INT("dev:%p, isr:0x%08x\n", dev, (u32)status);
-
- if (0 != (isr & (MASK_27))) {
- DEB_INT("irq: RPS0 (0x%08x)\n", isr);
- saa7146_video_uops.irq_done(dev,isr);
- }
-
- if (0 != (isr & (MASK_28))) {
- u32 mc2 = saa7146_read(dev, MC2);
- if( 0 != (mc2 & MASK_15)) {
- DEB_INT("irq: RPS1 vbi workaround (0x%08x)\n", isr);
- wake_up(&dev->vv_data->vbi_wq);
- saa7146_write(dev,MC2, MASK_31);
- return;
- }
- DEB_INT("irq: RPS1 (0x%08x)\n", isr);
- saa7146_vbi_uops.irq_done(dev,isr);
- }
-}
-
-static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
- .s_ctrl = saa7146_s_ctrl,
-};
-
-int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
-{
- struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
- struct v4l2_pix_format *fmt;
- struct v4l2_vbi_format *vbi;
- struct saa7146_vv *vv;
- int err;
-
- err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
- if (err)
- return err;
-
- v4l2_ctrl_handler_init(hdl, 6);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_SATURATION, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- if (hdl->error) {
- err = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- return err;
- }
- dev->v4l2_dev.ctrl_handler = hdl;
-
- vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
- if (vv == NULL) {
- ERR("out of memory. aborting.\n");
- v4l2_ctrl_handler_free(hdl);
- return -ENOMEM;
- }
- ext_vv->vid_ops = saa7146_video_ioctl_ops;
- ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
- ext_vv->core_ops = &saa7146_video_ioctl_ops;
-
- DEB_EE("dev:%p\n", dev);
-
- /* set default values for video parts of the saa7146 */
- saa7146_write(dev, BCS_CTRL, 0x80400040);
-
- /* enable video-port pins */
- saa7146_write(dev, MC1, (MASK_10 | MASK_26));
-
- /* save per-device extension data (one extension can
- handle different devices that might need different
- configuration data) */
- dev->ext_vv_data = ext_vv;
-
- vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
- if( NULL == vv->d_clipping.cpu_addr ) {
- ERR("out of memory. aborting.\n");
- kfree(vv);
- v4l2_ctrl_handler_free(hdl);
- return -1;
- }
- memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
-
- saa7146_video_uops.init(dev,vv);
- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
- saa7146_vbi_uops.init(dev,vv);
-
- fmt = &vv->ov_fb.fmt;
- fmt->width = vv->standard->h_max_out;
- fmt->height = vv->standard->v_max_out;
- fmt->pixelformat = V4L2_PIX_FMT_RGB565;
- fmt->bytesperline = 2 * fmt->width;
- fmt->sizeimage = fmt->bytesperline * fmt->height;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
-
- fmt = &vv->video_fmt;
- fmt->width = 384;
- fmt->height = 288;
- fmt->pixelformat = V4L2_PIX_FMT_BGR24;
- fmt->field = V4L2_FIELD_ANY;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- fmt->bytesperline = 3 * fmt->width;
- fmt->sizeimage = fmt->bytesperline * fmt->height;
-
- vbi = &vv->vbi_fmt;
- vbi->sampling_rate = 27000000;
- vbi->offset = 248; /* todo */
- vbi->samples_per_line = 720 * 2;
- vbi->sample_format = V4L2_PIX_FMT_GREY;
-
- /* fixme: this only works for PAL */
- vbi->start[0] = 5;
- vbi->count[0] = 16;
- vbi->start[1] = 312;
- vbi->count[1] = 16;
-
- init_timer(&vv->vbi_read_timeout);
-
- vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
- dev->vv_data = vv;
- dev->vv_callback = &vv_callback;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_vv_init);
-
-int saa7146_vv_release(struct saa7146_dev* dev)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- DEB_EE("dev:%p\n", dev);
-
- v4l2_device_unregister(&dev->v4l2_dev);
- pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- kfree(vv);
- dev->vv_data = NULL;
- dev->vv_callback = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_vv_release);
-
-int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
- char *name, int type)
-{
- struct video_device *vfd;
- int err;
- int i;
-
- DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type);
-
- // released by vfd->release
- vfd = video_device_alloc();
- if (vfd == NULL)
- return -ENOMEM;
-
- vfd->fops = &video_fops;
- if (type == VFL_TYPE_GRABBER)
- vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
- else
- vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
- vfd->release = video_device_release;
- /* Locking in file operations other than ioctl should be done by
- the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->lock = &dev->v4l2_lock;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->tvnorms = 0;
- set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- for (i = 0; i < dev->ext_vv_data->num_stds; i++)
- vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
- strlcpy(vfd->name, name, sizeof(vfd->name));
- video_set_drvdata(vfd, dev);
-
- err = video_register_device(vfd, type, -1);
- if (err < 0) {
- ERR("cannot register v4l2 device. skipping.\n");
- video_device_release(vfd);
- return err;
- }
-
- pr_info("%s: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(vfd));
-
- *vid = vfd;
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_register_device);
-
-int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
-{
- DEB_EE("dev:%p\n", dev);
-
- video_unregister_device(*vid);
- *vid = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_unregister_device);
-
-static int __init saa7146_vv_init_module(void)
-{
- return 0;
-}
-
-
-static void __exit saa7146_vv_cleanup_module(void)
-{
-}
-
-module_init(saa7146_vv_init_module);
-module_exit(saa7146_vv_cleanup_module);
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
deleted file mode 100644
index 6d14785d4747..000000000000
--- a/drivers/media/common/saa7146_video.c
+++ /dev/null
@@ -1,1332 +0,0 @@
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <media/saa7146_vv.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-ctrls.h>
-#include <linux/module.h>
-
-static int max_memory = 32;
-
-module_param(max_memory, int, 0644);
-MODULE_PARM_DESC(max_memory, "maximum memory usage for capture buffers (default: 32Mb)");
-
-#define IS_CAPTURE_ACTIVE(fh) \
- (((vv->video_status & STATUS_CAPTURE) != 0) && (vv->video_fh == fh))
-
-#define IS_OVERLAY_ACTIVE(fh) \
- (((vv->video_status & STATUS_OVERLAY) != 0) && (vv->video_fh == fh))
-
-/* format descriptions for capture and preview */
-static struct saa7146_format formats[] = {
- {
- .name = "RGB-8 (3-3-2)",
- .pixelformat = V4L2_PIX_FMT_RGB332,
- .trans = RGB08_COMPOSED,
- .depth = 8,
- .flags = 0,
- }, {
- .name = "RGB-16 (5/B-6/G-5/R)",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .trans = RGB16_COMPOSED,
- .depth = 16,
- .flags = 0,
- }, {
- .name = "RGB-24 (B-G-R)",
- .pixelformat = V4L2_PIX_FMT_BGR24,
- .trans = RGB24_COMPOSED,
- .depth = 24,
- .flags = 0,
- }, {
- .name = "RGB-32 (B-G-R)",
- .pixelformat = V4L2_PIX_FMT_BGR32,
- .trans = RGB32_COMPOSED,
- .depth = 32,
- .flags = 0,
- }, {
- .name = "RGB-32 (R-G-B)",
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .trans = RGB32_COMPOSED,
- .depth = 32,
- .flags = 0,
- .swap = 0x2,
- }, {
- .name = "Greyscale-8",
- .pixelformat = V4L2_PIX_FMT_GREY,
- .trans = Y8,
- .depth = 8,
- .flags = 0,
- }, {
- .name = "YUV 4:2:2 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YUV422P,
- .trans = YUV422_DECOMPOSED,
- .depth = 16,
- .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
- }, {
- .name = "YVU 4:2:0 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YVU420,
- .trans = YUV420_DECOMPOSED,
- .depth = 12,
- .flags = FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
- }, {
- .name = "YUV 4:2:0 planar (Y-Cb-Cr)",
- .pixelformat = V4L2_PIX_FMT_YUV420,
- .trans = YUV420_DECOMPOSED,
- .depth = 12,
- .flags = FORMAT_IS_PLANAR,
- }, {
- .name = "YUV 4:2:2 (U-Y-V-Y)",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .trans = YUV422_COMPOSED,
- .depth = 16,
- .flags = 0,
- }
-};
-
-/* unfortunately, the saa7146 contains a bug which prevents it from doing on-the-fly byte swaps.
- due to this, it's impossible to provide additional *packed* formats, which are simply byte swapped
- (like V4L2_PIX_FMT_YUYV) ... 8-( */
-
-static int NUM_FORMATS = sizeof(formats)/sizeof(struct saa7146_format);
-
-struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc)
-{
- int i, j = NUM_FORMATS;
-
- for (i = 0; i < j; i++) {
- if (formats[i].pixelformat == fourcc) {
- return formats+i;
- }
- }
-
- DEB_D("unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
- return NULL;
-}
-
-static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
-
-int saa7146_start_preview(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct v4l2_format fmt;
- int ret = 0, err = 0;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- /* check if we have overlay information */
- if (vv->ov.fh == NULL) {
- DEB_D("no overlay data available. try S_FMT first.\n");
- return -EAGAIN;
- }
-
- /* check if streaming capture is running */
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("streaming capture is active\n");
- return -EBUSY;
- }
-
- /* check if overlay is running */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- if (vv->video_fh == fh) {
- DEB_D("overlay is already active\n");
- return 0;
- }
- DEB_D("overlay is already active in another open\n");
- return -EBUSY;
- }
-
- if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
- DEB_D("cannot get necessary overlay resources\n");
- return -EBUSY;
- }
-
- fmt.fmt.win = vv->ov.win;
- err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
- if (0 != err) {
- saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- return -EBUSY;
- }
- vv->ov.win = fmt.fmt.win;
-
- DEB_D("%dx%d+%d+%d %s field=%s\n",
- vv->ov.win.w.width, vv->ov.win.w.height,
- vv->ov.win.w.left, vv->ov.win.w.top,
- vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]);
-
- if (0 != (ret = saa7146_enable_overlay(fh))) {
- DEB_D("enabling overlay failed: %d\n", ret);
- saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
- return ret;
- }
-
- vv->video_status = STATUS_OVERLAY;
- vv->video_fh = fh;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_start_preview);
-
-int saa7146_stop_preview(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- /* check if streaming capture is running */
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("streaming capture is active\n");
- return -EBUSY;
- }
-
- /* check if overlay is running at all */
- if ((vv->video_status & STATUS_OVERLAY) == 0) {
- DEB_D("no active overlay\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_D("overlay is active, but in another open\n");
- return -EBUSY;
- }
-
- vv->video_status = 0;
- vv->video_fh = NULL;
-
- saa7146_disable_overlay(fh);
-
- saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7146_stop_preview);
-
-/********************************************************************************/
-/* common pagetable functions */
-
-static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
-{
- struct pci_dev *pci = dev->pci;
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- struct scatterlist *list = dma->sglist;
- int length = dma->sglen;
- struct saa7146_format *sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- DEB_EE("dev:%p, buf:%p, sg_len:%d\n", dev, buf, length);
-
- if( 0 != IS_PLANAR(sfmt->trans)) {
- struct saa7146_pgtable *pt1 = &buf->pt[0];
- struct saa7146_pgtable *pt2 = &buf->pt[1];
- struct saa7146_pgtable *pt3 = &buf->pt[2];
- __le32 *ptr1, *ptr2, *ptr3;
- __le32 fill;
-
- int size = buf->fmt->width*buf->fmt->height;
- int i,p,m1,m2,m3,o1,o2;
-
- switch( sfmt->depth ) {
- case 12: {
- /* create some offsets inside the page table */
- m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
- m2 = ((size+(size/4)+PAGE_SIZE)/PAGE_SIZE)-1;
- m3 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
- o1 = size%PAGE_SIZE;
- o2 = (size+(size/4))%PAGE_SIZE;
- DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
- size, m1, m2, m3, o1, o2);
- break;
- }
- case 16: {
- /* create some offsets inside the page table */
- m1 = ((size+PAGE_SIZE)/PAGE_SIZE)-1;
- m2 = ((size+(size/2)+PAGE_SIZE)/PAGE_SIZE)-1;
- m3 = ((2*size+PAGE_SIZE)/PAGE_SIZE)-1;
- o1 = size%PAGE_SIZE;
- o2 = (size+(size/2))%PAGE_SIZE;
- DEB_CAP("size:%d, m1:%d, m2:%d, m3:%d, o1:%d, o2:%d\n",
- size, m1, m2, m3, o1, o2);
- break;
- }
- default: {
- return -1;
- }
- }
-
- ptr1 = pt1->cpu;
- ptr2 = pt2->cpu;
- ptr3 = pt3->cpu;
-
- /* walk all pages, copy all page addresses to ptr1 */
- for (i = 0; i < length; i++, list++) {
- for (p = 0; p * 4096 < list->length; p++, ptr1++) {
- *ptr1 = cpu_to_le32(sg_dma_address(list) - list->offset);
- }
- }
-/*
- ptr1 = pt1->cpu;
- for(j=0;j<40;j++) {
- printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
- }
-*/
-
- /* if we have a user buffer, the first page may not be
- aligned to a page boundary. */
- pt1->offset = dma->sglist->offset;
- pt2->offset = pt1->offset+o1;
- pt3->offset = pt1->offset+o2;
-
- /* create video-dma2 page table */
- ptr1 = pt1->cpu;
- for(i = m1; i <= m2 ; i++, ptr2++) {
- *ptr2 = ptr1[i];
- }
- fill = *(ptr2-1);
- for(;i<1024;i++,ptr2++) {
- *ptr2 = fill;
- }
- /* create video-dma3 page table */
- ptr1 = pt1->cpu;
- for(i = m2; i <= m3; i++,ptr3++) {
- *ptr3 = ptr1[i];
- }
- fill = *(ptr3-1);
- for(;i<1024;i++,ptr3++) {
- *ptr3 = fill;
- }
- /* finally: finish up video-dma1 page table */
- ptr1 = pt1->cpu+m1;
- fill = pt1->cpu[m1];
- for(i=m1;i<1024;i++,ptr1++) {
- *ptr1 = fill;
- }
-/*
- ptr1 = pt1->cpu;
- ptr2 = pt2->cpu;
- ptr3 = pt3->cpu;
- for(j=0;j<40;j++) {
- printk("ptr1 %d: 0x%08x\n",j,ptr1[j]);
- }
- for(j=0;j<40;j++) {
- printk("ptr2 %d: 0x%08x\n",j,ptr2[j]);
- }
- for(j=0;j<40;j++) {
- printk("ptr3 %d: 0x%08x\n",j,ptr3[j]);
- }
-*/
- } else {
- struct saa7146_pgtable *pt = &buf->pt[0];
- return saa7146_pgtable_build_single(pci, pt, list, length);
- }
-
- return 0;
-}
-
-
-/********************************************************************************/
-/* file operations */
-
-static int video_begin(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt = NULL;
- unsigned int resource;
- int ret = 0, err = 0;
-
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- if ((vv->video_status & STATUS_CAPTURE) != 0) {
- if (vv->video_fh == fh) {
- DEB_S("already capturing\n");
- return 0;
- }
- DEB_S("already capturing in another open\n");
- return -EBUSY;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) {
- DEB_S("warning: suspending overlay video for streaming capture\n");
- vv->ov_suspend = vv->video_fh;
- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
- if (0 != err) {
- DEB_D("suspending video failed. aborting\n");
- return err;
- }
- }
-
- fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
- /* we need to have a valid format set here */
- BUG_ON(NULL == fmt);
-
- if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
- resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
- } else {
- resource = RESOURCE_DMA1_HPS;
- }
-
- ret = saa7146_res_get(fh, resource);
- if (0 == ret) {
- DEB_S("cannot get capture resource %d\n", resource);
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
- return -EBUSY;
- }
-
- /* clear out beginning of streaming bit (rps register 0)*/
- saa7146_write(dev, MC2, MASK_27 );
-
- /* enable rps0 irqs */
- SAA7146_IER_ENABLE(dev, MASK_27);
-
- vv->video_fh = fh;
- vv->video_status = STATUS_CAPTURE;
-
- return 0;
-}
-
-static int video_end(struct saa7146_fh *fh, struct file *file)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt = NULL;
- unsigned long flags;
- unsigned int resource;
- u32 dmas = 0;
- DEB_EE("dev:%p, fh:%p\n", dev, fh);
-
- if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S("not capturing\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_S("capturing, but in another open\n");
- return -EBUSY;
- }
-
- fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
- /* we need to have a valid format set here */
- BUG_ON(NULL == fmt);
-
- if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
- resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
- dmas = MASK_22 | MASK_21 | MASK_20;
- } else {
- resource = RESOURCE_DMA1_HPS;
- dmas = MASK_22;
- }
- spin_lock_irqsave(&dev->slock,flags);
-
- /* disable rps0 */
- saa7146_write(dev, MC1, MASK_28);
-
- /* disable rps0 irqs */
- SAA7146_IER_DISABLE(dev, MASK_27);
-
- /* shut down all used video dma transfers */
- saa7146_write(dev, MC1, dmas);
-
- spin_unlock_irqrestore(&dev->slock, flags);
-
- vv->video_fh = NULL;
- vv->video_status = 0;
-
- saa7146_res_free(fh, resource);
-
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
-
- strcpy((char *)cap->driver, "saa7146 v4l2");
- strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
- sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->device_caps =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OVERLAY |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- cap->device_caps |= dev->ext_vv_data->capabilities;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- if (vdev->vfl_type == VFL_TYPE_GRABBER)
- cap->device_caps &=
- ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
- else
- cap->device_caps &=
- ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
- return 0;
-}
-
-static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- *fb = vv->ov_fb;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- fb->flags = V4L2_FBUF_FLAG_PRIMARY;
- return 0;
-}
-
-static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt;
-
- DEB_EE("VIDIOC_S_FBUF\n");
-
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* check args */
- fmt = saa7146_format_by_fourcc(dev, fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- /* planar formats are not allowed for overlay video, clipping and video dma would clash */
- if (fmt->flags & FORMAT_IS_PLANAR)
- DEB_S("planar pixelformat '%4.4s' not allowed for overlay\n",
- (char *)&fmt->pixelformat);
-
- /* check if overlay is running */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- if (vv->video_fh != fh) {
- DEB_D("refusing to change framebuffer informations while overlay is active in another open\n");
- return -EBUSY;
- }
- }
-
- /* ok, accept it */
- vv->ov_fb = *fb;
- vv->ov_fmt = fmt;
-
- if (vv->ov_fb.fmt.bytesperline < vv->ov_fb.fmt.width) {
- vv->ov_fb.fmt.bytesperline = vv->ov_fb.fmt.width * fmt->depth / 8;
- DEB_D("setting bytesperline to %d\n", vv->ov_fb.fmt.bytesperline);
- }
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
-{
- if (f->index >= NUM_FORMATS)
- return -EINVAL;
- strlcpy((char *)f->description, formats[f->index].name,
- sizeof(f->description));
- f->pixelformat = formats[f->index].pixelformat;
- return 0;
-}
-
-int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct saa7146_dev *dev = container_of(ctrl->handler,
- struct saa7146_dev, ctrl_handler);
- struct saa7146_vv *vv = dev->vv_data;
- u32 val;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0x00ffffff;
- val |= (ctrl->val << 24);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_CONTRAST:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0xff00ffff;
- val |= (ctrl->val << 16);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_SATURATION:
- val = saa7146_read(dev, BCS_CTRL);
- val &= 0xffffff00;
- val |= (ctrl->val << 0);
- saa7146_write(dev, BCS_CTRL, val);
- saa7146_write(dev, MC2, MASK_22 | MASK_06);
- break;
-
- case V4L2_CID_HFLIP:
- /* fixme: we can support changing VFLIP and HFLIP here... */
- if ((vv->video_status & STATUS_CAPTURE))
- return -EBUSY;
- vv->hflip = ctrl->val;
- break;
-
- case V4L2_CID_VFLIP:
- if ((vv->video_status & STATUS_CAPTURE))
- return -EBUSY;
- vv->vflip = ctrl->val;
- break;
-
- default:
- return -EINVAL;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
- struct saa7146_fh *fh = vv->video_fh;
-
- saa7146_stop_preview(fh);
- saa7146_start_preview(fh);
- }
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *parm)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- parm->parm.capture.readbuffers = 1;
- v4l2_video_std_frame_period(vv->standard->id,
- &parm->parm.capture.timeperframe);
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.pix = vv->video_fmt;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.win = vv->ov.win;
- return 0;
-}
-
-static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- f->fmt.vbi = vv->vbi_fmt;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_format *fmt;
- enum v4l2_field field;
- int maxw, maxh;
- int calc_bpl;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
-
- fmt = saa7146_format_by_fourcc(dev, f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = vv->standard->h_max_out;
- maxh = vv->standard->v_max_out;
-
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh / 2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- switch (field) {
- case V4L2_FIELD_ALTERNATE:
- vv->last_field = V4L2_FIELD_TOP;
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- vv->last_field = V4L2_FIELD_INTERLACED;
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- vv->last_field = V4L2_FIELD_INTERLACED;
- break;
- default:
- DEB_D("no known field mode '%d'\n", field);
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
-
- calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
-
- if (f->fmt.pix.bytesperline < calc_bpl)
- f->fmt.pix.bytesperline = calc_bpl;
-
- if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
- f->fmt.pix.bytesperline = calc_bpl;
-
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
- DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
- f->fmt.pix.width, f->fmt.pix.height,
- f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
-
- return 0;
-}
-
-
-static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct v4l2_window *win = &f->fmt.win;
- enum v4l2_field field;
- int maxw, maxh;
-
- DEB_EE("dev:%p\n", dev);
-
- if (NULL == vv->ov_fb.base) {
- DEB_D("no fb base set\n");
- return -EINVAL;
- }
- if (NULL == vv->ov_fmt) {
- DEB_D("no fb fmt set\n");
- return -EINVAL;
- }
- if (win->w.width < 48 || win->w.height < 32) {
- DEB_D("min width/height. (%d,%d)\n",
- win->w.width, win->w.height);
- return -EINVAL;
- }
- if (win->clipcount > 16) {
- DEB_D("clipcount too big\n");
- return -EINVAL;
- }
-
- field = win->field;
- maxw = vv->standard->h_max_out;
- maxh = vv->standard->v_max_out;
-
- if (V4L2_FIELD_ANY == field) {
- field = (win->w.height > maxh / 2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_TOP;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- DEB_D("no known field mode '%d'\n", field);
- return -EINVAL;
- }
-
- win->field = field;
- if (win->w.width > maxw)
- win->w.width = maxw;
- if (win->w.height > maxh)
- win->w.height = maxh;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh);
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_EE("streaming capture is active\n");
- return -EBUSY;
- }
- err = vidioc_try_fmt_vid_cap(file, fh, f);
- if (0 != err)
- return err;
- vv->video_fmt = f->fmt.pix;
- DEB_EE("set to pixelformat '%4.4s'\n",
- (char *)&vv->video_fmt.pixelformat);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_EE("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh);
- err = vidioc_try_fmt_vid_overlay(file, fh, f);
- if (0 != err)
- return err;
- vv->ov.win = f->fmt.win;
- vv->ov.nclips = f->fmt.win.clipcount;
- if (vv->ov.nclips > 16)
- vv->ov.nclips = 16;
- if (copy_from_user(vv->ov.clips, f->fmt.win.clips,
- sizeof(struct v4l2_clip) * vv->ov.nclips)) {
- return -EFAULT;
- }
-
- /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
- vv->ov.fh = fh;
-
- /* check if our current overlay is active */
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
- saa7146_stop_preview(fh);
- saa7146_start_preview(fh);
- }
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
-
- *norm = vv->standard->id;
- return 0;
-}
-
- /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
- PAL / NTSC / SECAM. if your hardware does not (or does more)
- -- override this function in your extension */
-/*
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *e = arg;
- if (e->index < 0 )
- return -EINVAL;
- if( e->index < dev->ext_vv_data->num_stds ) {
- DEB_EE("VIDIOC_ENUMSTD: index:%d\n", e->index);
- v4l2_video_std_construct(e, dev->ext_vv_data->stds[e->index].id, dev->ext_vv_data->stds[e->index].name);
- return 0;
- }
- return -EINVAL;
- }
- */
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int found = 0;
- int err, i;
-
- DEB_EE("VIDIOC_S_STD\n");
-
- if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
- DEB_D("cannot change video standard while streaming capture is active\n");
- return -EBUSY;
- }
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) {
- vv->ov_suspend = vv->video_fh;
- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
- if (0 != err) {
- DEB_D("suspending video failed. aborting\n");
- return err;
- }
- }
-
- for (i = 0; i < dev->ext_vv_data->num_stds; i++)
- if (*id & dev->ext_vv_data->stds[i].id)
- break;
- if (i != dev->ext_vv_data->num_stds) {
- vv->standard = &dev->ext_vv_data->stds[i];
- if (NULL != dev->ext_vv_data->std_callback)
- dev->ext_vv_data->std_callback(dev, vv->standard);
- found = 1;
- }
-
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- if (!found) {
- DEB_EE("VIDIOC_S_STD: standard not found\n");
- return -EINVAL;
- }
-
- DEB_EE("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name);
- return 0;
-}
-
-static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
-{
- int err;
-
- DEB_D("VIDIOC_OVERLAY on:%d\n", on);
- if (on)
- err = saa7146_start_preview(fh);
- else
- err = saa7146_stop_preview(fh);
- return err;
-}
-
-static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
-{
- struct saa7146_fh *fh = __fh;
-
- if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_reqbufs(&fh->video_q, b);
- if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_reqbufs(&fh->vbi_q, b);
- return -EINVAL;
-}
-
-static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_querybuf(&fh->video_q, buf);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_querybuf(&fh->vbi_q, buf);
- return -EINVAL;
-}
-
-static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_qbuf(&fh->video_q, buf);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_qbuf(&fh->vbi_q, buf);
- return -EINVAL;
-}
-
-static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct saa7146_fh *fh = __fh;
-
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
- if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
- return -EINVAL;
-}
-
-static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct saa7146_fh *fh = __fh;
- int err;
-
- DEB_D("VIDIOC_STREAMON, type:%d\n", type);
-
- err = video_begin(fh);
- if (err)
- return err;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_streamon(&fh->video_q);
- if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return videobuf_streamon(&fh->vbi_q);
- return -EINVAL;
-}
-
-static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- int err;
-
- DEB_D("VIDIOC_STREAMOFF, type:%d\n", type);
-
- /* ugly: we need to copy some checks from video_end(),
- because videobuf_streamoff() relies on the capture running.
- check and fix this */
- if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
- DEB_S("not capturing\n");
- return 0;
- }
-
- if (vv->video_fh != fh) {
- DEB_S("capturing, but in another open\n");
- return -EBUSY;
- }
-
- err = -EINVAL;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- err = videobuf_streamoff(&fh->video_q);
- else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
- err = videobuf_streamoff(&fh->vbi_q);
- if (0 != err) {
- DEB_D("warning: videobuf_streamoff() failed\n");
- video_end(fh, file);
- } else {
- err = video_end(fh, file);
- }
- return err;
-}
-
-static int vidioc_g_chip_ident(struct file *file, void *__fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7146_fh *fh = __fh;
- struct saa7146_dev *dev = fh->dev;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = V4L2_IDENT_SAA7146;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
- core, g_chip_ident, chip);
-}
-
-const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-
- .vidioc_overlay = vidioc_overlay,
- .vidioc_g_fbuf = vidioc_g_fbuf,
- .vidioc_s_fbuf = vidioc_s_fbuf,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/*********************************************************************************/
-/* buffer handling functions */
-
-static int buffer_activate (struct saa7146_dev *dev,
- struct saa7146_buf *buf,
- struct saa7146_buf *next)
-{
- struct saa7146_vv *vv = dev->vv_data;
-
- buf->vb.state = VIDEOBUF_ACTIVE;
- saa7146_set_capture(dev,buf,next);
-
- mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
-}
-
-static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf)
-{
- saa7146_pgtable_free(dev->pci, &buf->pt[0]);
- saa7146_pgtable_free(dev->pci, &buf->pt[1]);
- saa7146_pgtable_free(dev->pci, &buf->pt[2]);
-}
-
-static int buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
- int size,err = 0;
-
- DEB_CAP("vbuf:%p\n", vb);
-
- /* sanity checks */
- if (vv->video_fmt.width < 48 ||
- vv->video_fmt.height < 32 ||
- vv->video_fmt.width > vv->standard->h_max_out ||
- vv->video_fmt.height > vv->standard->v_max_out) {
- DEB_D("w (%d) / h (%d) out of bounds\n",
- vv->video_fmt.width, vv->video_fmt.height);
- return -EINVAL;
- }
-
- size = vv->video_fmt.sizeimage;
- if (0 != buf->vb.baddr && buf->vb.bsize < size) {
- DEB_D("size mismatch\n");
- return -EINVAL;
- }
-
- DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
- vv->video_fmt.width, vv->video_fmt.height,
- size, v4l2_field_names[vv->video_fmt.field]);
- if (buf->vb.width != vv->video_fmt.width ||
- buf->vb.bytesperline != vv->video_fmt.bytesperline ||
- buf->vb.height != vv->video_fmt.height ||
- buf->vb.size != size ||
- buf->vb.field != field ||
- buf->vb.field != vv->video_fmt.field ||
- buf->fmt != &vv->video_fmt) {
- saa7146_dma_free(dev,q,buf);
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- struct saa7146_format *sfmt;
-
- buf->vb.bytesperline = vv->video_fmt.bytesperline;
- buf->vb.width = vv->video_fmt.width;
- buf->vb.height = vv->video_fmt.height;
- buf->vb.size = size;
- buf->vb.field = field;
- buf->fmt = &vv->video_fmt;
- buf->vb.field = vv->video_fmt.field;
-
- sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
-
- release_all_pagetables(dev, buf);
- if( 0 != IS_PLANAR(sfmt->trans)) {
- saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
- saa7146_pgtable_alloc(dev->pci, &buf->pt[1]);
- saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
- } else {
- saa7146_pgtable_alloc(dev->pci, &buf->pt[0]);
- }
-
- err = videobuf_iolock(q,&buf->vb, &vv->ov_fb);
- if (err)
- goto oops;
- err = saa7146_pgtable_build(dev,buf);
- if (err)
- goto oops;
- }
- buf->vb.state = VIDEOBUF_PREPARED;
- buf->activate = buffer_activate;
-
- return 0;
-
- oops:
- DEB_D("error out\n");
- saa7146_dma_free(dev,q,buf);
-
- return err;
-}
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = fh->dev->vv_data;
-
- if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
- *count = MAX_SAA7146_CAPTURE_BUFFERS;
-
- *size = vv->video_fmt.sizeimage;
-
- /* check if we exceed the "max_memory" parameter */
- if( (*count * *size) > (max_memory*1048576) ) {
- *count = (max_memory*1048576) / *size;
- }
-
- DEB_CAP("%d buffers, %d bytes each\n", *count, *size);
-
- return 0;
-}
-
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_CAP("vbuf:%p\n", vb);
- saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct file *file = q->priv_data;
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_buf *buf = (struct saa7146_buf *)vb;
-
- DEB_CAP("vbuf:%p\n", vb);
-
- saa7146_dma_free(dev,q,buf);
-
- release_all_pagetables(dev, buf);
-}
-
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/********************************************************************************/
-/* file operations */
-
-static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
-{
- INIT_LIST_HEAD(&vv->video_dmaq.queue);
-
- init_timer(&vv->video_dmaq.timeout);
- vv->video_dmaq.timeout.function = saa7146_buffer_timeout;
- vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq);
- vv->video_dmaq.dev = dev;
-
- /* set some default values */
- vv->standard = &dev->ext_vv_data->stds[0];
-
- /* FIXME: what's this? */
- vv->current_hps_source = SAA7146_HPS_SOURCE_PORT_A;
- vv->current_hps_sync = SAA7146_HPS_SYNC_PORT_A;
-}
-
-
-static int video_open(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
-
- videobuf_queue_sg_init(&fh->video_q, &video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct saa7146_buf),
- file, &dev->v4l2_lock);
-
- return 0;
-}
-
-
-static void video_close(struct saa7146_dev *dev, struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_vv *vv = dev->vv_data;
- struct videobuf_queue *q = &fh->video_q;
-
- if (IS_CAPTURE_ACTIVE(fh) != 0)
- video_end(fh, file);
- else if (IS_OVERLAY_ACTIVE(fh) != 0)
- saa7146_stop_preview(fh);
-
- videobuf_stop(q);
- /* hmm, why is this function declared void? */
-}
-
-
-static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
-{
- struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_dmaqueue *q = &vv->video_dmaq;
-
- spin_lock(&dev->slock);
- DEB_CAP("called\n");
-
- /* only finish the buffer if we have one... */
- if( NULL != q->curr ) {
- saa7146_buffer_finish(dev,q,VIDEOBUF_DONE);
- }
- saa7146_buffer_next(dev,q,0);
-
- spin_unlock(&dev->slock);
-}
-
-static ssize_t video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- ssize_t ret = 0;
-
- DEB_EE("called\n");
-
- if ((vv->video_status & STATUS_CAPTURE) != 0) {
- /* fixme: should we allow read() captures while streaming capture? */
- if (vv->video_fh == fh) {
- DEB_S("already capturing\n");
- return -EBUSY;
- }
- DEB_S("already capturing in another open\n");
- return -EBUSY;
- }
-
- ret = video_begin(fh);
- if( 0 != ret) {
- goto out;
- }
-
- ret = videobuf_read_one(&fh->video_q , data, count, ppos,
- file->f_flags & O_NONBLOCK);
- if (ret != 0) {
- video_end(fh, file);
- } else {
- ret = video_end(fh, file);
- }
-out:
- /* restart overlay if it was active before */
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- return ret;
-}
-
-struct saa7146_use_ops saa7146_video_uops = {
- .init = video_init,
- .open = video_open,
- .release = video_close,
- .irq_done = video_irq_done,
- .read = video_read,
-};
diff --git a/drivers/media/common/siano/Kconfig b/drivers/media/common/siano/Kconfig
new file mode 100644
index 000000000000..425aeadfb49d
--- /dev/null
+++ b/drivers/media/common/siano/Kconfig
@@ -0,0 +1,17 @@
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config SMS_SIANO_MDTV
+ tristate
+ depends on DVB_CORE && RC_CORE && HAS_DMA
+ depends on SMS_USB_DRV || SMS_SDIO_DRV
+ default y
+ ---help---
+ Choose Y or M here if you have MDTV receiver with a Siano chipset.
+
+ To compile this driver as a module, choose M here
+ (The module will be called smsmdtv).
+
+ Further documentation on this driver can be found on the WWW
+ at http://www.siano-ms.com/
diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile
new file mode 100644
index 000000000000..2a09279e0648
--- /dev/null
+++ b/drivers/media/common/siano/Makefile
@@ -0,0 +1,7 @@
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
+
+obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
+
diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/common/siano/sms-cards.c
index 680c781c8dd6..680c781c8dd6 100644
--- a/drivers/media/dvb/siano/sms-cards.c
+++ b/drivers/media/common/siano/sms-cards.c
diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/common/siano/sms-cards.h
index d8cdf756f7cf..d8cdf756f7cf 100644
--- a/drivers/media/dvb/siano/sms-cards.h
+++ b/drivers/media/common/siano/sms-cards.h
diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/common/siano/smscoreapi.c
index 9cc55546cc30..9cc55546cc30 100644
--- a/drivers/media/dvb/siano/smscoreapi.c
+++ b/drivers/media/common/siano/smscoreapi.c
diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h
index c592ae090397..c592ae090397 100644
--- a/drivers/media/dvb/siano/smscoreapi.h
+++ b/drivers/media/common/siano/smscoreapi.h
diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c
index aa77e54a8fae..aa77e54a8fae 100644
--- a/drivers/media/dvb/siano/smsdvb.c
+++ b/drivers/media/common/siano/smsdvb.c
diff --git a/drivers/media/dvb/siano/smsendian.c b/drivers/media/common/siano/smsendian.c
index e2657c2f0109..e2657c2f0109 100644
--- a/drivers/media/dvb/siano/smsendian.c
+++ b/drivers/media/common/siano/smsendian.c
diff --git a/drivers/media/dvb/siano/smsendian.h b/drivers/media/common/siano/smsendian.h
index 1624d6fd367b..1624d6fd367b 100644
--- a/drivers/media/dvb/siano/smsendian.h
+++ b/drivers/media/common/siano/smsendian.h
diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/common/siano/smsir.c
index 37bc5c4b8ad8..37bc5c4b8ad8 100644
--- a/drivers/media/dvb/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
diff --git a/drivers/media/dvb/siano/smsir.h b/drivers/media/common/siano/smsir.h
index ae92b3a8587e..ae92b3a8587e 100644
--- a/drivers/media/dvb/siano/smsir.h
+++ b/drivers/media/common/siano/smsir.h
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
deleted file mode 100644
index 94c6ff7a5da3..000000000000
--- a/drivers/media/common/tuners/Kconfig
+++ /dev/null
@@ -1,243 +0,0 @@
-config MEDIA_ATTACH
- bool "Load and attach frontend and tuner driver modules as needed"
- depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
- depends on MODULES
- default y if !EXPERT
- help
- Remove the static dependency of DVB card drivers on all
- frontend modules for all possible card variants. Instead,
- allow the card drivers to only load the frontend modules
- they require.
-
- Also, tuner module will automatically load a tuner driver
- when needed, for analog mode.
-
- This saves several KBytes of memory.
-
- Note: You will need module-init-tools v3.2 or later for this feature.
-
- If unsure say Y.
-
-config MEDIA_TUNER
- tristate
- depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C
- default y
- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT && EXPERIMENTAL
- select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE && MEDIA_RADIO_SUPPORT
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
-
-config MEDIA_TUNER_CUSTOMISE
- bool "Customize analog and hybrid tuner modules to build"
- depends on MEDIA_TUNER
- default y if EXPERT
- help
- This allows the user to deselect tuner drivers unnecessary
- for their hardware from the build. Use this option with care
- as deselecting tuner drivers which are in fact necessary will
- result in V4L/DVB devices which cannot be tuned due to lack of
- driver support
-
- If unsure say N.
-
-menu "Customize TV tuners"
- visible if MEDIA_TUNER_CUSTOMISE
- depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
-
-config MEDIA_TUNER_SIMPLE
- tristate "Simple tuner support"
- depends on MEDIA_SUPPORT && I2C
- select MEDIA_TUNER_TDA9887
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for various simple tuners.
-
-config MEDIA_TUNER_TDA8290
- tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
- depends on MEDIA_SUPPORT && I2C
- select MEDIA_TUNER_TDA827X
- select MEDIA_TUNER_TDA18271
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for Philips TDA8290+8275(a) tuner.
-
-config MEDIA_TUNER_TDA827X
- tristate "Philips TDA827X silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A DVB-T silicon tuner module. Say Y when you want to support this tuner.
-
-config MEDIA_TUNER_TDA18271
- tristate "NXP TDA18271 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A silicon tuner module. Say Y when you want to support this tuner.
-
-config MEDIA_TUNER_TDA9887
- tristate "TDA 9885/6/7 analog IF demodulator"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for Philips TDA9885/6/7
- analog IF demodulator.
-
-config MEDIA_TUNER_TEA5761
- tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
- depends on MEDIA_SUPPORT && I2C
- depends on EXPERIMENTAL
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the Philips TEA5761 radio tuner.
-
-config MEDIA_TUNER_TEA5767
- tristate "TEA 5767 radio tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the Philips TEA5767 radio tuner.
-
-config MEDIA_TUNER_MT20XX
- tristate "Microtune 2032 / 2050 tuners"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the MT2032 / MT2050 tuner.
-
-config MEDIA_TUNER_MT2060
- tristate "Microtune MT2060 silicon IF tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon IF tuner MT2060 from Microtune.
-
-config MEDIA_TUNER_MT2063
- tristate "Microtune MT2063 silicon IF tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon IF tuner MT2063 from Microtune.
-
-config MEDIA_TUNER_MT2266
- tristate "Microtune MT2266 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon baseband tuner MT2266 from Microtune.
-
-config MEDIA_TUNER_MT2131
- tristate "Microtune MT2131 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon baseband tuner MT2131 from Microtune.
-
-config MEDIA_TUNER_QT1010
- tristate "Quantek QT1010 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner QT1010 from Quantek.
-
-config MEDIA_TUNER_XC2028
- tristate "XCeive xc2028/xc3028 tuners"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to include support for the xc2028/xc3028 tuners.
-
-config MEDIA_TUNER_XC5000
- tristate "Xceive XC5000 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner XC5000 from Xceive.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-config MEDIA_TUNER_XC4000
- tristate "Xceive XC4000 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner XC4000 from Xceive.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-config MEDIA_TUNER_MXL5005S
- tristate "MaxLinear MSL5005S silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MXL5005S from MaxLinear.
-
-config MEDIA_TUNER_MXL5007T
- tristate "MaxLinear MxL5007T silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MxL5007T from MaxLinear.
-
-config MEDIA_TUNER_MC44S803
- tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Freescale MC44S803 based tuners
-
-config MEDIA_TUNER_MAX2165
- tristate "Maxim MAX2165 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- A driver for the silicon tuner MAX2165 from Maxim.
-
-config MEDIA_TUNER_TDA18218
- tristate "NXP TDA18218 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- NXP TDA18218 silicon tuner driver.
-
-config MEDIA_TUNER_FC0011
- tristate "Fitipower FC0011 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0011 silicon tuner driver.
-
-config MEDIA_TUNER_FC0012
- tristate "Fitipower FC0012 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0012 silicon tuner driver.
-
-config MEDIA_TUNER_FC0013
- tristate "Fitipower FC0013 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Fitipower FC0013 silicon tuner driver.
-
-config MEDIA_TUNER_TDA18212
- tristate "NXP TDA18212 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- NXP TDA18212 silicon tuner driver.
-
-config MEDIA_TUNER_TUA9001
- tristate "Infineon TUA 9001 silicon tuner"
- depends on MEDIA_SUPPORT && I2C
- default m if MEDIA_TUNER_CUSTOMISE
- help
- Infineon TUA 9001 silicon tuner driver.
-endmenu
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
deleted file mode 100644
index 891b80e60808..000000000000
--- a/drivers/media/common/tuners/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Makefile for common V4L/DVB tuners
-#
-
-tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
-
-obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o
-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o
-# tuner-types will be merged into tuner-simple, in the future
-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o
-obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o
-obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o
-obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o
-obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o
-obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
-obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
-obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
-obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
-obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
-obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o
-obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
-obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
-obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
-obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
-obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
-obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
-obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
-obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
-obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
-obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
-obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
-
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/mc44s803.c b/drivers/media/common/tuners/mc44s803.c
deleted file mode 100644
index 5ddce7e326f7..000000000000
--- a/drivers/media/common/tuners/mc44s803.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
- *
- * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/dvb/frontend.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-
-#include "dvb_frontend.h"
-
-#include "mc44s803.h"
-#include "mc44s803_priv.h"
-
-#define mc_printk(level, format, arg...) \
- printk(level "mc44s803: " format , ## arg)
-
-/* Writes a single register */
-static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
-{
- u8 buf[3];
- struct i2c_msg msg = {
- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
- };
-
- buf[0] = (val & 0xff0000) >> 16;
- buf[1] = (val & 0xff00) >> 8;
- buf[2] = (val & 0xff);
-
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
- mc_printk(KERN_WARNING, "I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-/* Reads a single register */
-static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
-{
- u32 wval;
- u8 buf[3];
- int ret;
- struct i2c_msg msg[] = {
- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
- .buf = buf, .len = 3 },
- };
-
- wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
- MC44S803_REG_SM(reg, MC44S803_D);
-
- ret = mc44s803_writereg(priv, wval);
- if (ret)
- return ret;
-
- if (i2c_transfer(priv->i2c, msg, 1) != 1) {
- mc_printk(KERN_WARNING, "I2C read failed\n");
- return -EREMOTEIO;
- }
-
- *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
-
- return 0;
-}
-
-static int mc44s803_release(struct dvb_frontend *fe)
-{
- struct mc44s803_priv *priv = fe->tuner_priv;
-
- fe->tuner_priv = NULL;
- kfree(priv);
-
- return 0;
-}
-
-static int mc44s803_init(struct dvb_frontend *fe)
-{
- struct mc44s803_priv *priv = fe->tuner_priv;
- u32 val;
- int err;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
-/* Reset chip */
- val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_RS);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
-/* Power Up and Start Osc */
-
- val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
- MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
- MC44S803_REG_SM(1, MC44S803_OSCSEL);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
- MC44S803_REG_SM(0x200, MC44S803_POWER);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- msleep(10);
-
- val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
- MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
- MC44S803_REG_SM(1, MC44S803_OSCSEL);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- msleep(20);
-
-/* Setup Mixer */
-
- val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
- MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
-/* Setup Cirquit Adjust */
-
- val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_G1) |
- MC44S803_REG_SM(1, MC44S803_G3) |
- MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
- MC44S803_REG_SM(1, MC44S803_G6) |
- MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
- MC44S803_REG_SM(0x3, MC44S803_LP) |
- MC44S803_REG_SM(1, MC44S803_CLRF) |
- MC44S803_REG_SM(1, MC44S803_CLIF);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_G1) |
- MC44S803_REG_SM(1, MC44S803_G3) |
- MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
- MC44S803_REG_SM(1, MC44S803_G6) |
- MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
- MC44S803_REG_SM(0x3, MC44S803_LP);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
-/* Setup Digtune */
-
- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
- MC44S803_REG_SM(3, MC44S803_XOD);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
-/* Setup AGC */
-
- val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_AT1) |
- MC44S803_REG_SM(1, MC44S803_AT2) |
- MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
- MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
- MC44S803_REG_SM(1, MC44S803_LNA0);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- return 0;
-
-exit:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- mc_printk(KERN_WARNING, "I/O Error\n");
- return err;
-}
-
-static int mc44s803_set_params(struct dvb_frontend *fe)
-{
- struct mc44s803_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 r1, r2, n1, n2, lo1, lo2, freq, val;
- int err;
-
- priv->frequency = c->frequency;
-
- r1 = MC44S803_OSC / 1000000;
- r2 = MC44S803_OSC / 100000;
-
- n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000;
- freq = MC44S803_OSC / r1 * n1;
- lo1 = ((60 * n1) + (r1 / 2)) / r1;
- freq = freq - c->frequency;
-
- n2 = (freq - MC44S803_IF2 + 50000) / 100000;
- lo2 = ((60 * n2) + (r2 / 2)) / r2;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
- MC44S803_REG_SM(r1-1, MC44S803_R1) |
- MC44S803_REG_SM(r2-1, MC44S803_R2) |
- MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
- MC44S803_REG_SM(n1-2, MC44S803_LO1);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
- MC44S803_REG_SM(n2-2, MC44S803_LO2);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
- MC44S803_REG_SM(1, MC44S803_DA) |
- MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
- MC44S803_REG_SM(1, MC44S803_AT);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
- MC44S803_REG_SM(2, MC44S803_DA) |
- MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
- MC44S803_REG_SM(1, MC44S803_AT);
-
- err = mc44s803_writereg(priv, val);
- if (err)
- goto exit;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return 0;
-
-exit:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- mc_printk(KERN_WARNING, "I/O Error\n");
- return err;
-}
-
-static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct mc44s803_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static const struct dvb_tuner_ops mc44s803_tuner_ops = {
- .info = {
- .name = "Freescale MC44S803",
- .frequency_min = 48000000,
- .frequency_max = 1000000000,
- .frequency_step = 100000,
- },
-
- .release = mc44s803_release,
- .init = mc44s803_init,
- .set_params = mc44s803_set_params,
- .get_frequency = mc44s803_get_frequency
-};
-
-/* This functions tries to identify a MC44S803 tuner by reading the ID
- register. This is hasty. */
-struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct mc44s803_config *cfg)
-{
- struct mc44s803_priv *priv;
- u32 reg;
- u8 id;
- int ret;
-
- reg = 0;
-
- priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
- priv->fe = fe;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
- if (ret)
- goto error;
-
- id = MC44S803_REG_MS(reg, MC44S803_ID);
-
- if (id != 0x14) {
- mc_printk(KERN_ERR, "unsupported ID "
- "(%x should be 0x14)\n", id);
- goto error;
- }
-
- mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
- memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = priv;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return fe;
-
-error:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(mc44s803_attach);
-
-MODULE_AUTHOR("Jochen Friedrich");
-MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
deleted file mode 100644
index 6133315fb0e3..000000000000
--- a/drivers/media/common/tuners/mxl5005s.c
+++ /dev/null
@@ -1,4109 +0,0 @@
-/*
- MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
-
- Copyright (C) 2008 MaxLinear
- Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
- Functions:
- mxl5005s_reset()
- mxl5005s_writereg()
- mxl5005s_writeregs()
- mxl5005s_init()
- mxl5005s_reconfigure()
- mxl5005s_AssignTunerMode()
- mxl5005s_set_params()
- mxl5005s_get_frequency()
- mxl5005s_get_bandwidth()
- mxl5005s_release()
- mxl5005s_attach()
-
- Copyright (C) 2008 Realtek
- Copyright (C) 2008 Jan Hoogenraad
- Functions:
- mxl5005s_SetRfFreqHz()
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/*
- History of this driver (Steven Toth):
- I was given a public release of a linux driver that included
- support for the MaxLinear MXL5005S silicon tuner. Analysis of
- the tuner driver showed clearly three things.
-
- 1. The tuner driver didn't support the LinuxTV tuner API
- so the code Realtek added had to be removed.
-
- 2. A significant amount of the driver is reference driver code
- from MaxLinear, I felt it was important to identify and
- preserve this.
-
- 3. New code has to be added to interface correctly with the
- LinuxTV API, as a regular kernel module.
-
- Other than the reference driver enum's, I've clearly marked
- sections of the code and retained the copyright of the
- respective owners.
-*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include "dvb_frontend.h"
-#include "mxl5005s.h"
-
-static int debug;
-
-#define dprintk(level, arg...) do { \
- if (level <= debug) \
- printk(arg); \
- } while (0)
-
-#define TUNER_REGS_NUM 104
-#define INITCTRL_NUM 40
-
-#ifdef _MXL_PRODUCTION
-#define CHCTRL_NUM 39
-#else
-#define CHCTRL_NUM 36
-#endif
-
-#define MXLCTRL_NUM 189
-#define MASTER_CONTROL_ADDR 9
-
-/* Enumeration of Master Control Register State */
-enum master_control_state {
- MC_LOAD_START = 1,
- MC_POWER_DOWN,
- MC_SYNTH_RESET,
- MC_SEQ_OFF
-};
-
-/* Enumeration of MXL5005 Tuner Modulation Type */
-enum {
- MXL_DEFAULT_MODULATION = 0,
- MXL_DVBT,
- MXL_ATSC,
- MXL_QAM,
- MXL_ANALOG_CABLE,
- MXL_ANALOG_OTA
-};
-
-/* MXL5005 Tuner Register Struct */
-struct TunerReg {
- u16 Reg_Num; /* Tuner Register Address */
- u16 Reg_Val; /* Current sw programmed value waiting to be written */
-};
-
-enum {
- /* Initialization Control Names */
- DN_IQTN_AMP_CUT = 1, /* 1 */
- BB_MODE, /* 2 */
- BB_BUF, /* 3 */
- BB_BUF_OA, /* 4 */
- BB_ALPF_BANDSELECT, /* 5 */
- BB_IQSWAP, /* 6 */
- BB_DLPF_BANDSEL, /* 7 */
- RFSYN_CHP_GAIN, /* 8 */
- RFSYN_EN_CHP_HIGAIN, /* 9 */
- AGC_IF, /* 10 */
- AGC_RF, /* 11 */
- IF_DIVVAL, /* 12 */
- IF_VCO_BIAS, /* 13 */
- CHCAL_INT_MOD_IF, /* 14 */
- CHCAL_FRAC_MOD_IF, /* 15 */
- DRV_RES_SEL, /* 16 */
- I_DRIVER, /* 17 */
- EN_AAF, /* 18 */
- EN_3P, /* 19 */
- EN_AUX_3P, /* 20 */
- SEL_AAF_BAND, /* 21 */
- SEQ_ENCLK16_CLK_OUT, /* 22 */
- SEQ_SEL4_16B, /* 23 */
- XTAL_CAPSELECT, /* 24 */
- IF_SEL_DBL, /* 25 */
- RFSYN_R_DIV, /* 26 */
- SEQ_EXTSYNTHCALIF, /* 27 */
- SEQ_EXTDCCAL, /* 28 */
- AGC_EN_RSSI, /* 29 */
- RFA_ENCLKRFAGC, /* 30 */
- RFA_RSSI_REFH, /* 31 */
- RFA_RSSI_REF, /* 32 */
- RFA_RSSI_REFL, /* 33 */
- RFA_FLR, /* 34 */
- RFA_CEIL, /* 35 */
- SEQ_EXTIQFSMPULSE, /* 36 */
- OVERRIDE_1, /* 37 */
- BB_INITSTATE_DLPF_TUNE, /* 38 */
- TG_R_DIV, /* 39 */
- EN_CHP_LIN_B, /* 40 */
-
- /* Channel Change Control Names */
- DN_POLY = 51, /* 51 */
- DN_RFGAIN, /* 52 */
- DN_CAP_RFLPF, /* 53 */
- DN_EN_VHFUHFBAR, /* 54 */
- DN_GAIN_ADJUST, /* 55 */
- DN_IQTNBUF_AMP, /* 56 */
- DN_IQTNGNBFBIAS_BST, /* 57 */
- RFSYN_EN_OUTMUX, /* 58 */
- RFSYN_SEL_VCO_OUT, /* 59 */
- RFSYN_SEL_VCO_HI, /* 60 */
- RFSYN_SEL_DIVM, /* 61 */
- RFSYN_RF_DIV_BIAS, /* 62 */
- DN_SEL_FREQ, /* 63 */
- RFSYN_VCO_BIAS, /* 64 */
- CHCAL_INT_MOD_RF, /* 65 */
- CHCAL_FRAC_MOD_RF, /* 66 */
- RFSYN_LPF_R, /* 67 */
- CHCAL_EN_INT_RF, /* 68 */
- TG_LO_DIVVAL, /* 69 */
- TG_LO_SELVAL, /* 70 */
- TG_DIV_VAL, /* 71 */
- TG_VCO_BIAS, /* 72 */
- SEQ_EXTPOWERUP, /* 73 */
- OVERRIDE_2, /* 74 */
- OVERRIDE_3, /* 75 */
- OVERRIDE_4, /* 76 */
- SEQ_FSM_PULSE, /* 77 */
- GPIO_4B, /* 78 */
- GPIO_3B, /* 79 */
- GPIO_4, /* 80 */
- GPIO_3, /* 81 */
- GPIO_1B, /* 82 */
- DAC_A_ENABLE, /* 83 */
- DAC_B_ENABLE, /* 84 */
- DAC_DIN_A, /* 85 */
- DAC_DIN_B, /* 86 */
-#ifdef _MXL_PRODUCTION
- RFSYN_EN_DIV, /* 87 */
- RFSYN_DIVM, /* 88 */
- DN_BYPASS_AGC_I2C /* 89 */
-#endif
-};
-
-/*
- * The following context is source code provided by MaxLinear.
- * MaxLinear source code - Common_MXL.h (?)
- */
-
-/* Constants */
-#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104
-#define MXL5005S_LATCH_BYTE 0xfe
-
-/* Register address, MSB, and LSB */
-#define MXL5005S_BB_IQSWAP_ADDR 59
-#define MXL5005S_BB_IQSWAP_MSB 0
-#define MXL5005S_BB_IQSWAP_LSB 0
-
-#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53
-#define MXL5005S_BB_DLPF_BANDSEL_MSB 4
-#define MXL5005S_BB_DLPF_BANDSEL_LSB 3
-
-/* Standard modes */
-enum {
- MXL5005S_STANDARD_DVBT,
- MXL5005S_STANDARD_ATSC,
-};
-#define MXL5005S_STANDARD_MODE_NUM 2
-
-/* Bandwidth modes */
-enum {
- MXL5005S_BANDWIDTH_6MHZ = 6000000,
- MXL5005S_BANDWIDTH_7MHZ = 7000000,
- MXL5005S_BANDWIDTH_8MHZ = 8000000,
-};
-#define MXL5005S_BANDWIDTH_MODE_NUM 3
-
-/* MXL5005 Tuner Control Struct */
-struct TunerControl {
- u16 Ctrl_Num; /* Control Number */
- u16 size; /* Number of bits to represent Value */
- u16 addr[25]; /* Array of Tuner Register Address for each bit pos */
- u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */
- u16 val[25]; /* Binary representation of Value */
-};
-
-/* MXL5005 Tuner Struct */
-struct mxl5005s_state {
- u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */
- u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */
- u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */
- u32 IF_OUT; /* Desired IF Out Frequency */
- u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */
- u32 RF_IN; /* RF Input Frequency */
- u32 Fxtal; /* XTAL Frequency */
- u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */
- u16 TOP; /* Value: take over point */
- u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */
- u8 DIV_OUT; /* 4MHz or 16MHz */
- u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */
- u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */
-
- /* Modulation Type; */
- /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */
- u8 Mod_Type;
-
- /* Tracking Filter Type */
- /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */
- u8 TF_Type;
-
- /* Calculated Settings */
- u32 RF_LO; /* Synth RF LO Frequency */
- u32 IF_LO; /* Synth IF LO Frequency */
- u32 TG_LO; /* Synth TG_LO Frequency */
-
- /* Pointers to ControlName Arrays */
- u16 Init_Ctrl_Num; /* Number of INIT Control Names */
- struct TunerControl
- Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */
-
- u16 CH_Ctrl_Num; /* Number of CH Control Names */
- struct TunerControl
- CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */
-
- u16 MXL_Ctrl_Num; /* Number of MXL Control Names */
- struct TunerControl
- MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */
-
- /* Pointer to Tuner Register Array */
- u16 TunerRegs_Num; /* Number of Tuner Registers */
- struct TunerReg
- TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */
-
- /* Linux driver framework specific */
- struct mxl5005s_config *config;
- struct dvb_frontend *frontend;
- struct i2c_adapter *i2c;
-
- /* Cache values */
- u32 current_mode;
-
-};
-
-static u16 MXL_GetMasterControl(u8 *MasterReg, int state);
-static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value);
-static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value);
-static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit,
- u8 bitVal);
-static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum,
- u8 *RegVal, int *count);
-static u32 MXL_Ceiling(u32 value, u32 resolution);
-static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal);
-static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
- u32 value, u16 controlGroup);
-static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val);
-static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
- u8 *RegVal, int *count);
-static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
-static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
-static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
-static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
- u8 *RegVal, int *count);
-static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable,
- u8 *datatable, u8 len);
-static u16 MXL_IFSynthInit(struct dvb_frontend *fe);
-static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type,
- u32 bandwidth);
-static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type,
- u32 bandwidth);
-
-/* ----------------------------------------------------------------
- * Begin: Custom code salvaged from the Realtek driver.
- * Copyright (C) 2008 Realtek
- * Copyright (C) 2008 Jan Hoogenraad
- * This code is placed under the terms of the GNU General Public License
- *
- * Released by Realtek under GPLv2.
- * Thanks to Realtek for a lot of support we received !
- *
- * Revision: 080314 - original version
- */
-
-static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
- unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
- int TableLen;
-
- u32 IfDivval = 0;
- unsigned char MasterControlByte;
-
- dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz);
-
- /* Set MxL5005S tuner RF frequency according to example code. */
-
- /* Tuner RF frequency setting stage 0 */
- MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
- AddrTable[0] = MASTER_CONTROL_ADDR;
- ByteTable[0] |= state->config->AgcMasterByte;
-
- mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
-
- /* Tuner RF frequency setting stage 1 */
- MXL_TuneRF(fe, RfFreqHz);
-
- MXL_ControlRead(fe, IF_DIVVAL, &IfDivval);
-
- MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0);
- MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1);
- MXL_ControlWrite(fe, IF_DIVVAL, 8);
- MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen);
-
- MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START);
- AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
- ByteTable[TableLen] = MasterControlByte |
- state->config->AgcMasterByte;
- TableLen += 1;
-
- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
-
- /* Wait 30 ms. */
- msleep(150);
-
- /* Tuner RF frequency setting stage 2 */
- MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1);
- MXL_ControlWrite(fe, IF_DIVVAL, IfDivval);
- MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen);
-
- MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START);
- AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
- ByteTable[TableLen] = MasterControlByte |
- state->config->AgcMasterByte ;
- TableLen += 1;
-
- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
-
- msleep(100);
-
- return 0;
-}
-/* End: Custom code taken from the Realtek driver */
-
-/* ----------------------------------------------------------------
- * Begin: Reference driver code found in the Realtek driver.
- * Copyright (C) 2008 MaxLinear
- */
-static u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- state->TunerRegs_Num = TUNER_REGS_NUM ;
-
- state->TunerRegs[0].Reg_Num = 9 ;
- state->TunerRegs[0].Reg_Val = 0x40 ;
-
- state->TunerRegs[1].Reg_Num = 11 ;
- state->TunerRegs[1].Reg_Val = 0x19 ;
-
- state->TunerRegs[2].Reg_Num = 12 ;
- state->TunerRegs[2].Reg_Val = 0x60 ;
-
- state->TunerRegs[3].Reg_Num = 13 ;
- state->TunerRegs[3].Reg_Val = 0x00 ;
-
- state->TunerRegs[4].Reg_Num = 14 ;
- state->TunerRegs[4].Reg_Val = 0x00 ;
-
- state->TunerRegs[5].Reg_Num = 15 ;
- state->TunerRegs[5].Reg_Val = 0xC0 ;
-
- state->TunerRegs[6].Reg_Num = 16 ;
- state->TunerRegs[6].Reg_Val = 0x00 ;
-
- state->TunerRegs[7].Reg_Num = 17 ;
- state->TunerRegs[7].Reg_Val = 0x00 ;
-
- state->TunerRegs[8].Reg_Num = 18 ;
- state->TunerRegs[8].Reg_Val = 0x00 ;
-
- state->TunerRegs[9].Reg_Num = 19 ;
- state->TunerRegs[9].Reg_Val = 0x34 ;
-
- state->TunerRegs[10].Reg_Num = 21 ;
- state->TunerRegs[10].Reg_Val = 0x00 ;
-
- state->TunerRegs[11].Reg_Num = 22 ;
- state->TunerRegs[11].Reg_Val = 0x6B ;
-
- state->TunerRegs[12].Reg_Num = 23 ;
- state->TunerRegs[12].Reg_Val = 0x35 ;
-
- state->TunerRegs[13].Reg_Num = 24 ;
- state->TunerRegs[13].Reg_Val = 0x70 ;
-
- state->TunerRegs[14].Reg_Num = 25 ;
- state->TunerRegs[14].Reg_Val = 0x3E ;
-
- state->TunerRegs[15].Reg_Num = 26 ;
- state->TunerRegs[15].Reg_Val = 0x82 ;
-
- state->TunerRegs[16].Reg_Num = 31 ;
- state->TunerRegs[16].Reg_Val = 0x00 ;
-
- state->TunerRegs[17].Reg_Num = 32 ;
- state->TunerRegs[17].Reg_Val = 0x40 ;
-
- state->TunerRegs[18].Reg_Num = 33 ;
- state->TunerRegs[18].Reg_Val = 0x53 ;
-
- state->TunerRegs[19].Reg_Num = 34 ;
- state->TunerRegs[19].Reg_Val = 0x81 ;
-
- state->TunerRegs[20].Reg_Num = 35 ;
- state->TunerRegs[20].Reg_Val = 0xC9 ;
-
- state->TunerRegs[21].Reg_Num = 36 ;
- state->TunerRegs[21].Reg_Val = 0x01 ;
-
- state->TunerRegs[22].Reg_Num = 37 ;
- state->TunerRegs[22].Reg_Val = 0x00 ;
-
- state->TunerRegs[23].Reg_Num = 41 ;
- state->TunerRegs[23].Reg_Val = 0x00 ;
-
- state->TunerRegs[24].Reg_Num = 42 ;
- state->TunerRegs[24].Reg_Val = 0xF8 ;
-
- state->TunerRegs[25].Reg_Num = 43 ;
- state->TunerRegs[25].Reg_Val = 0x43 ;
-
- state->TunerRegs[26].Reg_Num = 44 ;
- state->TunerRegs[26].Reg_Val = 0x20 ;
-
- state->TunerRegs[27].Reg_Num = 45 ;
- state->TunerRegs[27].Reg_Val = 0x80 ;
-
- state->TunerRegs[28].Reg_Num = 46 ;
- state->TunerRegs[28].Reg_Val = 0x88 ;
-
- state->TunerRegs[29].Reg_Num = 47 ;
- state->TunerRegs[29].Reg_Val = 0x86 ;
-
- state->TunerRegs[30].Reg_Num = 48 ;
- state->TunerRegs[30].Reg_Val = 0x00 ;
-
- state->TunerRegs[31].Reg_Num = 49 ;
- state->TunerRegs[31].Reg_Val = 0x00 ;
-
- state->TunerRegs[32].Reg_Num = 53 ;
- state->TunerRegs[32].Reg_Val = 0x94 ;
-
- state->TunerRegs[33].Reg_Num = 54 ;
- state->TunerRegs[33].Reg_Val = 0xFA ;
-
- state->TunerRegs[34].Reg_Num = 55 ;
- state->TunerRegs[34].Reg_Val = 0x92 ;
-
- state->TunerRegs[35].Reg_Num = 56 ;
- state->TunerRegs[35].Reg_Val = 0x80 ;
-
- state->TunerRegs[36].Reg_Num = 57 ;
- state->TunerRegs[36].Reg_Val = 0x41 ;
-
- state->TunerRegs[37].Reg_Num = 58 ;
- state->TunerRegs[37].Reg_Val = 0xDB ;
-
- state->TunerRegs[38].Reg_Num = 59 ;
- state->TunerRegs[38].Reg_Val = 0x00 ;
-
- state->TunerRegs[39].Reg_Num = 60 ;
- state->TunerRegs[39].Reg_Val = 0x00 ;
-
- state->TunerRegs[40].Reg_Num = 61 ;
- state->TunerRegs[40].Reg_Val = 0x00 ;
-
- state->TunerRegs[41].Reg_Num = 62 ;
- state->TunerRegs[41].Reg_Val = 0x00 ;
-
- state->TunerRegs[42].Reg_Num = 65 ;
- state->TunerRegs[42].Reg_Val = 0xF8 ;
-
- state->TunerRegs[43].Reg_Num = 66 ;
- state->TunerRegs[43].Reg_Val = 0xE4 ;
-
- state->TunerRegs[44].Reg_Num = 67 ;
- state->TunerRegs[44].Reg_Val = 0x90 ;
-
- state->TunerRegs[45].Reg_Num = 68 ;
- state->TunerRegs[45].Reg_Val = 0xC0 ;
-
- state->TunerRegs[46].Reg_Num = 69 ;
- state->TunerRegs[46].Reg_Val = 0x01 ;
-
- state->TunerRegs[47].Reg_Num = 70 ;
- state->TunerRegs[47].Reg_Val = 0x50 ;
-
- state->TunerRegs[48].Reg_Num = 71 ;
- state->TunerRegs[48].Reg_Val = 0x06 ;
-
- state->TunerRegs[49].Reg_Num = 72 ;
- state->TunerRegs[49].Reg_Val = 0x00 ;
-
- state->TunerRegs[50].Reg_Num = 73 ;
- state->TunerRegs[50].Reg_Val = 0x20 ;
-
- state->TunerRegs[51].Reg_Num = 76 ;
- state->TunerRegs[51].Reg_Val = 0xBB ;
-
- state->TunerRegs[52].Reg_Num = 77 ;
- state->TunerRegs[52].Reg_Val = 0x13 ;
-
- state->TunerRegs[53].Reg_Num = 81 ;
- state->TunerRegs[53].Reg_Val = 0x04 ;
-
- state->TunerRegs[54].Reg_Num = 82 ;
- state->TunerRegs[54].Reg_Val = 0x75 ;
-
- state->TunerRegs[55].Reg_Num = 83 ;
- state->TunerRegs[55].Reg_Val = 0x00 ;
-
- state->TunerRegs[56].Reg_Num = 84 ;
- state->TunerRegs[56].Reg_Val = 0x00 ;
-
- state->TunerRegs[57].Reg_Num = 85 ;
- state->TunerRegs[57].Reg_Val = 0x00 ;
-
- state->TunerRegs[58].Reg_Num = 91 ;
- state->TunerRegs[58].Reg_Val = 0x70 ;
-
- state->TunerRegs[59].Reg_Num = 92 ;
- state->TunerRegs[59].Reg_Val = 0x00 ;
-
- state->TunerRegs[60].Reg_Num = 93 ;
- state->TunerRegs[60].Reg_Val = 0x00 ;
-
- state->TunerRegs[61].Reg_Num = 94 ;
- state->TunerRegs[61].Reg_Val = 0x00 ;
-
- state->TunerRegs[62].Reg_Num = 95 ;
- state->TunerRegs[62].Reg_Val = 0x0C ;
-
- state->TunerRegs[63].Reg_Num = 96 ;
- state->TunerRegs[63].Reg_Val = 0x00 ;
-
- state->TunerRegs[64].Reg_Num = 97 ;
- state->TunerRegs[64].Reg_Val = 0x00 ;
-
- state->TunerRegs[65].Reg_Num = 98 ;
- state->TunerRegs[65].Reg_Val = 0xE2 ;
-
- state->TunerRegs[66].Reg_Num = 99 ;
- state->TunerRegs[66].Reg_Val = 0x00 ;
-
- state->TunerRegs[67].Reg_Num = 100 ;
- state->TunerRegs[67].Reg_Val = 0x00 ;
-
- state->TunerRegs[68].Reg_Num = 101 ;
- state->TunerRegs[68].Reg_Val = 0x12 ;
-
- state->TunerRegs[69].Reg_Num = 102 ;
- state->TunerRegs[69].Reg_Val = 0x80 ;
-
- state->TunerRegs[70].Reg_Num = 103 ;
- state->TunerRegs[70].Reg_Val = 0x32 ;
-
- state->TunerRegs[71].Reg_Num = 104 ;
- state->TunerRegs[71].Reg_Val = 0xB4 ;
-
- state->TunerRegs[72].Reg_Num = 105 ;
- state->TunerRegs[72].Reg_Val = 0x60 ;
-
- state->TunerRegs[73].Reg_Num = 106 ;
- state->TunerRegs[73].Reg_Val = 0x83 ;
-
- state->TunerRegs[74].Reg_Num = 107 ;
- state->TunerRegs[74].Reg_Val = 0x84 ;
-
- state->TunerRegs[75].Reg_Num = 108 ;
- state->TunerRegs[75].Reg_Val = 0x9C ;
-
- state->TunerRegs[76].Reg_Num = 109 ;
- state->TunerRegs[76].Reg_Val = 0x02 ;
-
- state->TunerRegs[77].Reg_Num = 110 ;
- state->TunerRegs[77].Reg_Val = 0x81 ;
-
- state->TunerRegs[78].Reg_Num = 111 ;
- state->TunerRegs[78].Reg_Val = 0xC0 ;
-
- state->TunerRegs[79].Reg_Num = 112 ;
- state->TunerRegs[79].Reg_Val = 0x10 ;
-
- state->TunerRegs[80].Reg_Num = 131 ;
- state->TunerRegs[80].Reg_Val = 0x8A ;
-
- state->TunerRegs[81].Reg_Num = 132 ;
- state->TunerRegs[81].Reg_Val = 0x10 ;
-
- state->TunerRegs[82].Reg_Num = 133 ;
- state->TunerRegs[82].Reg_Val = 0x24 ;
-
- state->TunerRegs[83].Reg_Num = 134 ;
- state->TunerRegs[83].Reg_Val = 0x00 ;
-
- state->TunerRegs[84].Reg_Num = 135 ;
- state->TunerRegs[84].Reg_Val = 0x00 ;
-
- state->TunerRegs[85].Reg_Num = 136 ;
- state->TunerRegs[85].Reg_Val = 0x7E ;
-
- state->TunerRegs[86].Reg_Num = 137 ;
- state->TunerRegs[86].Reg_Val = 0x40 ;
-
- state->TunerRegs[87].Reg_Num = 138 ;
- state->TunerRegs[87].Reg_Val = 0x38 ;
-
- state->TunerRegs[88].Reg_Num = 146 ;
- state->TunerRegs[88].Reg_Val = 0xF6 ;
-
- state->TunerRegs[89].Reg_Num = 147 ;
- state->TunerRegs[89].Reg_Val = 0x1A ;
-
- state->TunerRegs[90].Reg_Num = 148 ;
- state->TunerRegs[90].Reg_Val = 0x62 ;
-
- state->TunerRegs[91].Reg_Num = 149 ;
- state->TunerRegs[91].Reg_Val = 0x33 ;
-
- state->TunerRegs[92].Reg_Num = 150 ;
- state->TunerRegs[92].Reg_Val = 0x80 ;
-
- state->TunerRegs[93].Reg_Num = 156 ;
- state->TunerRegs[93].Reg_Val = 0x56 ;
-
- state->TunerRegs[94].Reg_Num = 157 ;
- state->TunerRegs[94].Reg_Val = 0x17 ;
-
- state->TunerRegs[95].Reg_Num = 158 ;
- state->TunerRegs[95].Reg_Val = 0xA9 ;
-
- state->TunerRegs[96].Reg_Num = 159 ;
- state->TunerRegs[96].Reg_Val = 0x00 ;
-
- state->TunerRegs[97].Reg_Num = 160 ;
- state->TunerRegs[97].Reg_Val = 0x00 ;
-
- state->TunerRegs[98].Reg_Num = 161 ;
- state->TunerRegs[98].Reg_Val = 0x00 ;
-
- state->TunerRegs[99].Reg_Num = 162 ;
- state->TunerRegs[99].Reg_Val = 0x40 ;
-
- state->TunerRegs[100].Reg_Num = 166 ;
- state->TunerRegs[100].Reg_Val = 0xAE ;
-
- state->TunerRegs[101].Reg_Num = 167 ;
- state->TunerRegs[101].Reg_Val = 0x1B ;
-
- state->TunerRegs[102].Reg_Num = 168 ;
- state->TunerRegs[102].Reg_Val = 0xF2 ;
-
- state->TunerRegs[103].Reg_Num = 195 ;
- state->TunerRegs[103].Reg_Val = 0x00 ;
-
- return 0 ;
-}
-
-static u16 MXL5005_ControlInit(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- state->Init_Ctrl_Num = INITCTRL_NUM;
-
- state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ;
- state->Init_Ctrl[0].size = 1 ;
- state->Init_Ctrl[0].addr[0] = 73;
- state->Init_Ctrl[0].bit[0] = 7;
- state->Init_Ctrl[0].val[0] = 0;
-
- state->Init_Ctrl[1].Ctrl_Num = BB_MODE ;
- state->Init_Ctrl[1].size = 1 ;
- state->Init_Ctrl[1].addr[0] = 53;
- state->Init_Ctrl[1].bit[0] = 2;
- state->Init_Ctrl[1].val[0] = 1;
-
- state->Init_Ctrl[2].Ctrl_Num = BB_BUF ;
- state->Init_Ctrl[2].size = 2 ;
- state->Init_Ctrl[2].addr[0] = 53;
- state->Init_Ctrl[2].bit[0] = 1;
- state->Init_Ctrl[2].val[0] = 0;
- state->Init_Ctrl[2].addr[1] = 57;
- state->Init_Ctrl[2].bit[1] = 0;
- state->Init_Ctrl[2].val[1] = 1;
-
- state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ;
- state->Init_Ctrl[3].size = 1 ;
- state->Init_Ctrl[3].addr[0] = 53;
- state->Init_Ctrl[3].bit[0] = 0;
- state->Init_Ctrl[3].val[0] = 0;
-
- state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ;
- state->Init_Ctrl[4].size = 3 ;
- state->Init_Ctrl[4].addr[0] = 53;
- state->Init_Ctrl[4].bit[0] = 5;
- state->Init_Ctrl[4].val[0] = 0;
- state->Init_Ctrl[4].addr[1] = 53;
- state->Init_Ctrl[4].bit[1] = 6;
- state->Init_Ctrl[4].val[1] = 0;
- state->Init_Ctrl[4].addr[2] = 53;
- state->Init_Ctrl[4].bit[2] = 7;
- state->Init_Ctrl[4].val[2] = 1;
-
- state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ;
- state->Init_Ctrl[5].size = 1 ;
- state->Init_Ctrl[5].addr[0] = 59;
- state->Init_Ctrl[5].bit[0] = 0;
- state->Init_Ctrl[5].val[0] = 0;
-
- state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ;
- state->Init_Ctrl[6].size = 2 ;
- state->Init_Ctrl[6].addr[0] = 53;
- state->Init_Ctrl[6].bit[0] = 3;
- state->Init_Ctrl[6].val[0] = 0;
- state->Init_Ctrl[6].addr[1] = 53;
- state->Init_Ctrl[6].bit[1] = 4;
- state->Init_Ctrl[6].val[1] = 1;
-
- state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ;
- state->Init_Ctrl[7].size = 4 ;
- state->Init_Ctrl[7].addr[0] = 22;
- state->Init_Ctrl[7].bit[0] = 4;
- state->Init_Ctrl[7].val[0] = 0;
- state->Init_Ctrl[7].addr[1] = 22;
- state->Init_Ctrl[7].bit[1] = 5;
- state->Init_Ctrl[7].val[1] = 1;
- state->Init_Ctrl[7].addr[2] = 22;
- state->Init_Ctrl[7].bit[2] = 6;
- state->Init_Ctrl[7].val[2] = 1;
- state->Init_Ctrl[7].addr[3] = 22;
- state->Init_Ctrl[7].bit[3] = 7;
- state->Init_Ctrl[7].val[3] = 0;
-
- state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ;
- state->Init_Ctrl[8].size = 1 ;
- state->Init_Ctrl[8].addr[0] = 22;
- state->Init_Ctrl[8].bit[0] = 2;
- state->Init_Ctrl[8].val[0] = 0;
-
- state->Init_Ctrl[9].Ctrl_Num = AGC_IF ;
- state->Init_Ctrl[9].size = 4 ;
- state->Init_Ctrl[9].addr[0] = 76;
- state->Init_Ctrl[9].bit[0] = 0;
- state->Init_Ctrl[9].val[0] = 1;
- state->Init_Ctrl[9].addr[1] = 76;
- state->Init_Ctrl[9].bit[1] = 1;
- state->Init_Ctrl[9].val[1] = 1;
- state->Init_Ctrl[9].addr[2] = 76;
- state->Init_Ctrl[9].bit[2] = 2;
- state->Init_Ctrl[9].val[2] = 0;
- state->Init_Ctrl[9].addr[3] = 76;
- state->Init_Ctrl[9].bit[3] = 3;
- state->Init_Ctrl[9].val[3] = 1;
-
- state->Init_Ctrl[10].Ctrl_Num = AGC_RF ;
- state->Init_Ctrl[10].size = 4 ;
- state->Init_Ctrl[10].addr[0] = 76;
- state->Init_Ctrl[10].bit[0] = 4;
- state->Init_Ctrl[10].val[0] = 1;
- state->Init_Ctrl[10].addr[1] = 76;
- state->Init_Ctrl[10].bit[1] = 5;
- state->Init_Ctrl[10].val[1] = 1;
- state->Init_Ctrl[10].addr[2] = 76;
- state->Init_Ctrl[10].bit[2] = 6;
- state->Init_Ctrl[10].val[2] = 0;
- state->Init_Ctrl[10].addr[3] = 76;
- state->Init_Ctrl[10].bit[3] = 7;
- state->Init_Ctrl[10].val[3] = 1;
-
- state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ;
- state->Init_Ctrl[11].size = 5 ;
- state->Init_Ctrl[11].addr[0] = 43;
- state->Init_Ctrl[11].bit[0] = 3;
- state->Init_Ctrl[11].val[0] = 0;
- state->Init_Ctrl[11].addr[1] = 43;
- state->Init_Ctrl[11].bit[1] = 4;
- state->Init_Ctrl[11].val[1] = 0;
- state->Init_Ctrl[11].addr[2] = 43;
- state->Init_Ctrl[11].bit[2] = 5;
- state->Init_Ctrl[11].val[2] = 0;
- state->Init_Ctrl[11].addr[3] = 43;
- state->Init_Ctrl[11].bit[3] = 6;
- state->Init_Ctrl[11].val[3] = 1;
- state->Init_Ctrl[11].addr[4] = 43;
- state->Init_Ctrl[11].bit[4] = 7;
- state->Init_Ctrl[11].val[4] = 0;
-
- state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ;
- state->Init_Ctrl[12].size = 6 ;
- state->Init_Ctrl[12].addr[0] = 44;
- state->Init_Ctrl[12].bit[0] = 2;
- state->Init_Ctrl[12].val[0] = 0;
- state->Init_Ctrl[12].addr[1] = 44;
- state->Init_Ctrl[12].bit[1] = 3;
- state->Init_Ctrl[12].val[1] = 0;
- state->Init_Ctrl[12].addr[2] = 44;
- state->Init_Ctrl[12].bit[2] = 4;
- state->Init_Ctrl[12].val[2] = 0;
- state->Init_Ctrl[12].addr[3] = 44;
- state->Init_Ctrl[12].bit[3] = 5;
- state->Init_Ctrl[12].val[3] = 1;
- state->Init_Ctrl[12].addr[4] = 44;
- state->Init_Ctrl[12].bit[4] = 6;
- state->Init_Ctrl[12].val[4] = 0;
- state->Init_Ctrl[12].addr[5] = 44;
- state->Init_Ctrl[12].bit[5] = 7;
- state->Init_Ctrl[12].val[5] = 0;
-
- state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ;
- state->Init_Ctrl[13].size = 7 ;
- state->Init_Ctrl[13].addr[0] = 11;
- state->Init_Ctrl[13].bit[0] = 0;
- state->Init_Ctrl[13].val[0] = 1;
- state->Init_Ctrl[13].addr[1] = 11;
- state->Init_Ctrl[13].bit[1] = 1;
- state->Init_Ctrl[13].val[1] = 0;
- state->Init_Ctrl[13].addr[2] = 11;
- state->Init_Ctrl[13].bit[2] = 2;
- state->Init_Ctrl[13].val[2] = 0;
- state->Init_Ctrl[13].addr[3] = 11;
- state->Init_Ctrl[13].bit[3] = 3;
- state->Init_Ctrl[13].val[3] = 1;
- state->Init_Ctrl[13].addr[4] = 11;
- state->Init_Ctrl[13].bit[4] = 4;
- state->Init_Ctrl[13].val[4] = 1;
- state->Init_Ctrl[13].addr[5] = 11;
- state->Init_Ctrl[13].bit[5] = 5;
- state->Init_Ctrl[13].val[5] = 0;
- state->Init_Ctrl[13].addr[6] = 11;
- state->Init_Ctrl[13].bit[6] = 6;
- state->Init_Ctrl[13].val[6] = 0;
-
- state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ;
- state->Init_Ctrl[14].size = 16 ;
- state->Init_Ctrl[14].addr[0] = 13;
- state->Init_Ctrl[14].bit[0] = 0;
- state->Init_Ctrl[14].val[0] = 0;
- state->Init_Ctrl[14].addr[1] = 13;
- state->Init_Ctrl[14].bit[1] = 1;
- state->Init_Ctrl[14].val[1] = 0;
- state->Init_Ctrl[14].addr[2] = 13;
- state->Init_Ctrl[14].bit[2] = 2;
- state->Init_Ctrl[14].val[2] = 0;
- state->Init_Ctrl[14].addr[3] = 13;
- state->Init_Ctrl[14].bit[3] = 3;
- state->Init_Ctrl[14].val[3] = 0;
- state->Init_Ctrl[14].addr[4] = 13;
- state->Init_Ctrl[14].bit[4] = 4;
- state->Init_Ctrl[14].val[4] = 0;
- state->Init_Ctrl[14].addr[5] = 13;
- state->Init_Ctrl[14].bit[5] = 5;
- state->Init_Ctrl[14].val[5] = 0;
- state->Init_Ctrl[14].addr[6] = 13;
- state->Init_Ctrl[14].bit[6] = 6;
- state->Init_Ctrl[14].val[6] = 0;
- state->Init_Ctrl[14].addr[7] = 13;
- state->Init_Ctrl[14].bit[7] = 7;
- state->Init_Ctrl[14].val[7] = 0;
- state->Init_Ctrl[14].addr[8] = 12;
- state->Init_Ctrl[14].bit[8] = 0;
- state->Init_Ctrl[14].val[8] = 0;
- state->Init_Ctrl[14].addr[9] = 12;
- state->Init_Ctrl[14].bit[9] = 1;
- state->Init_Ctrl[14].val[9] = 0;
- state->Init_Ctrl[14].addr[10] = 12;
- state->Init_Ctrl[14].bit[10] = 2;
- state->Init_Ctrl[14].val[10] = 0;
- state->Init_Ctrl[14].addr[11] = 12;
- state->Init_Ctrl[14].bit[11] = 3;
- state->Init_Ctrl[14].val[11] = 0;
- state->Init_Ctrl[14].addr[12] = 12;
- state->Init_Ctrl[14].bit[12] = 4;
- state->Init_Ctrl[14].val[12] = 0;
- state->Init_Ctrl[14].addr[13] = 12;
- state->Init_Ctrl[14].bit[13] = 5;
- state->Init_Ctrl[14].val[13] = 1;
- state->Init_Ctrl[14].addr[14] = 12;
- state->Init_Ctrl[14].bit[14] = 6;
- state->Init_Ctrl[14].val[14] = 1;
- state->Init_Ctrl[14].addr[15] = 12;
- state->Init_Ctrl[14].bit[15] = 7;
- state->Init_Ctrl[14].val[15] = 0;
-
- state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ;
- state->Init_Ctrl[15].size = 3 ;
- state->Init_Ctrl[15].addr[0] = 147;
- state->Init_Ctrl[15].bit[0] = 2;
- state->Init_Ctrl[15].val[0] = 0;
- state->Init_Ctrl[15].addr[1] = 147;
- state->Init_Ctrl[15].bit[1] = 3;
- state->Init_Ctrl[15].val[1] = 1;
- state->Init_Ctrl[15].addr[2] = 147;
- state->Init_Ctrl[15].bit[2] = 4;
- state->Init_Ctrl[15].val[2] = 1;
-
- state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ;
- state->Init_Ctrl[16].size = 2 ;
- state->Init_Ctrl[16].addr[0] = 147;
- state->Init_Ctrl[16].bit[0] = 0;
- state->Init_Ctrl[16].val[0] = 0;
- state->Init_Ctrl[16].addr[1] = 147;
- state->Init_Ctrl[16].bit[1] = 1;
- state->Init_Ctrl[16].val[1] = 1;
-
- state->Init_Ctrl[17].Ctrl_Num = EN_AAF ;
- state->Init_Ctrl[17].size = 1 ;
- state->Init_Ctrl[17].addr[0] = 147;
- state->Init_Ctrl[17].bit[0] = 7;
- state->Init_Ctrl[17].val[0] = 0;
-
- state->Init_Ctrl[18].Ctrl_Num = EN_3P ;
- state->Init_Ctrl[18].size = 1 ;
- state->Init_Ctrl[18].addr[0] = 147;
- state->Init_Ctrl[18].bit[0] = 6;
- state->Init_Ctrl[18].val[0] = 0;
-
- state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ;
- state->Init_Ctrl[19].size = 1 ;
- state->Init_Ctrl[19].addr[0] = 156;
- state->Init_Ctrl[19].bit[0] = 0;
- state->Init_Ctrl[19].val[0] = 0;
-
- state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ;
- state->Init_Ctrl[20].size = 1 ;
- state->Init_Ctrl[20].addr[0] = 147;
- state->Init_Ctrl[20].bit[0] = 5;
- state->Init_Ctrl[20].val[0] = 0;
-
- state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ;
- state->Init_Ctrl[21].size = 1 ;
- state->Init_Ctrl[21].addr[0] = 137;
- state->Init_Ctrl[21].bit[0] = 4;
- state->Init_Ctrl[21].val[0] = 0;
-
- state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ;
- state->Init_Ctrl[22].size = 1 ;
- state->Init_Ctrl[22].addr[0] = 137;
- state->Init_Ctrl[22].bit[0] = 7;
- state->Init_Ctrl[22].val[0] = 0;
-
- state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ;
- state->Init_Ctrl[23].size = 1 ;
- state->Init_Ctrl[23].addr[0] = 91;
- state->Init_Ctrl[23].bit[0] = 5;
- state->Init_Ctrl[23].val[0] = 1;
-
- state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ;
- state->Init_Ctrl[24].size = 1 ;
- state->Init_Ctrl[24].addr[0] = 43;
- state->Init_Ctrl[24].bit[0] = 0;
- state->Init_Ctrl[24].val[0] = 1;
-
- state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ;
- state->Init_Ctrl[25].size = 2 ;
- state->Init_Ctrl[25].addr[0] = 22;
- state->Init_Ctrl[25].bit[0] = 0;
- state->Init_Ctrl[25].val[0] = 1;
- state->Init_Ctrl[25].addr[1] = 22;
- state->Init_Ctrl[25].bit[1] = 1;
- state->Init_Ctrl[25].val[1] = 1;
-
- state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ;
- state->Init_Ctrl[26].size = 1 ;
- state->Init_Ctrl[26].addr[0] = 134;
- state->Init_Ctrl[26].bit[0] = 2;
- state->Init_Ctrl[26].val[0] = 0;
-
- state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ;
- state->Init_Ctrl[27].size = 1 ;
- state->Init_Ctrl[27].addr[0] = 137;
- state->Init_Ctrl[27].bit[0] = 3;
- state->Init_Ctrl[27].val[0] = 0;
-
- state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ;
- state->Init_Ctrl[28].size = 1 ;
- state->Init_Ctrl[28].addr[0] = 77;
- state->Init_Ctrl[28].bit[0] = 7;
- state->Init_Ctrl[28].val[0] = 0;
-
- state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ;
- state->Init_Ctrl[29].size = 1 ;
- state->Init_Ctrl[29].addr[0] = 166;
- state->Init_Ctrl[29].bit[0] = 7;
- state->Init_Ctrl[29].val[0] = 1;
-
- state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ;
- state->Init_Ctrl[30].size = 3 ;
- state->Init_Ctrl[30].addr[0] = 166;
- state->Init_Ctrl[30].bit[0] = 0;
- state->Init_Ctrl[30].val[0] = 0;
- state->Init_Ctrl[30].addr[1] = 166;
- state->Init_Ctrl[30].bit[1] = 1;
- state->Init_Ctrl[30].val[1] = 1;
- state->Init_Ctrl[30].addr[2] = 166;
- state->Init_Ctrl[30].bit[2] = 2;
- state->Init_Ctrl[30].val[2] = 1;
-
- state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ;
- state->Init_Ctrl[31].size = 3 ;
- state->Init_Ctrl[31].addr[0] = 166;
- state->Init_Ctrl[31].bit[0] = 3;
- state->Init_Ctrl[31].val[0] = 1;
- state->Init_Ctrl[31].addr[1] = 166;
- state->Init_Ctrl[31].bit[1] = 4;
- state->Init_Ctrl[31].val[1] = 0;
- state->Init_Ctrl[31].addr[2] = 166;
- state->Init_Ctrl[31].bit[2] = 5;
- state->Init_Ctrl[31].val[2] = 1;
-
- state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ;
- state->Init_Ctrl[32].size = 3 ;
- state->Init_Ctrl[32].addr[0] = 167;
- state->Init_Ctrl[32].bit[0] = 0;
- state->Init_Ctrl[32].val[0] = 1;
- state->Init_Ctrl[32].addr[1] = 167;
- state->Init_Ctrl[32].bit[1] = 1;
- state->Init_Ctrl[32].val[1] = 1;
- state->Init_Ctrl[32].addr[2] = 167;
- state->Init_Ctrl[32].bit[2] = 2;
- state->Init_Ctrl[32].val[2] = 0;
-
- state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ;
- state->Init_Ctrl[33].size = 4 ;
- state->Init_Ctrl[33].addr[0] = 168;
- state->Init_Ctrl[33].bit[0] = 0;
- state->Init_Ctrl[33].val[0] = 0;
- state->Init_Ctrl[33].addr[1] = 168;
- state->Init_Ctrl[33].bit[1] = 1;
- state->Init_Ctrl[33].val[1] = 1;
- state->Init_Ctrl[33].addr[2] = 168;
- state->Init_Ctrl[33].bit[2] = 2;
- state->Init_Ctrl[33].val[2] = 0;
- state->Init_Ctrl[33].addr[3] = 168;
- state->Init_Ctrl[33].bit[3] = 3;
- state->Init_Ctrl[33].val[3] = 0;
-
- state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ;
- state->Init_Ctrl[34].size = 4 ;
- state->Init_Ctrl[34].addr[0] = 168;
- state->Init_Ctrl[34].bit[0] = 4;
- state->Init_Ctrl[34].val[0] = 1;
- state->Init_Ctrl[34].addr[1] = 168;
- state->Init_Ctrl[34].bit[1] = 5;
- state->Init_Ctrl[34].val[1] = 1;
- state->Init_Ctrl[34].addr[2] = 168;
- state->Init_Ctrl[34].bit[2] = 6;
- state->Init_Ctrl[34].val[2] = 1;
- state->Init_Ctrl[34].addr[3] = 168;
- state->Init_Ctrl[34].bit[3] = 7;
- state->Init_Ctrl[34].val[3] = 1;
-
- state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ;
- state->Init_Ctrl[35].size = 1 ;
- state->Init_Ctrl[35].addr[0] = 135;
- state->Init_Ctrl[35].bit[0] = 0;
- state->Init_Ctrl[35].val[0] = 0;
-
- state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ;
- state->Init_Ctrl[36].size = 1 ;
- state->Init_Ctrl[36].addr[0] = 56;
- state->Init_Ctrl[36].bit[0] = 3;
- state->Init_Ctrl[36].val[0] = 0;
-
- state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ;
- state->Init_Ctrl[37].size = 7 ;
- state->Init_Ctrl[37].addr[0] = 59;
- state->Init_Ctrl[37].bit[0] = 1;
- state->Init_Ctrl[37].val[0] = 0;
- state->Init_Ctrl[37].addr[1] = 59;
- state->Init_Ctrl[37].bit[1] = 2;
- state->Init_Ctrl[37].val[1] = 0;
- state->Init_Ctrl[37].addr[2] = 59;
- state->Init_Ctrl[37].bit[2] = 3;
- state->Init_Ctrl[37].val[2] = 0;
- state->Init_Ctrl[37].addr[3] = 59;
- state->Init_Ctrl[37].bit[3] = 4;
- state->Init_Ctrl[37].val[3] = 0;
- state->Init_Ctrl[37].addr[4] = 59;
- state->Init_Ctrl[37].bit[4] = 5;
- state->Init_Ctrl[37].val[4] = 0;
- state->Init_Ctrl[37].addr[5] = 59;
- state->Init_Ctrl[37].bit[5] = 6;
- state->Init_Ctrl[37].val[5] = 0;
- state->Init_Ctrl[37].addr[6] = 59;
- state->Init_Ctrl[37].bit[6] = 7;
- state->Init_Ctrl[37].val[6] = 0;
-
- state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ;
- state->Init_Ctrl[38].size = 6 ;
- state->Init_Ctrl[38].addr[0] = 32;
- state->Init_Ctrl[38].bit[0] = 2;
- state->Init_Ctrl[38].val[0] = 0;
- state->Init_Ctrl[38].addr[1] = 32;
- state->Init_Ctrl[38].bit[1] = 3;
- state->Init_Ctrl[38].val[1] = 0;
- state->Init_Ctrl[38].addr[2] = 32;
- state->Init_Ctrl[38].bit[2] = 4;
- state->Init_Ctrl[38].val[2] = 0;
- state->Init_Ctrl[38].addr[3] = 32;
- state->Init_Ctrl[38].bit[3] = 5;
- state->Init_Ctrl[38].val[3] = 0;
- state->Init_Ctrl[38].addr[4] = 32;
- state->Init_Ctrl[38].bit[4] = 6;
- state->Init_Ctrl[38].val[4] = 1;
- state->Init_Ctrl[38].addr[5] = 32;
- state->Init_Ctrl[38].bit[5] = 7;
- state->Init_Ctrl[38].val[5] = 0;
-
- state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ;
- state->Init_Ctrl[39].size = 1 ;
- state->Init_Ctrl[39].addr[0] = 25;
- state->Init_Ctrl[39].bit[0] = 3;
- state->Init_Ctrl[39].val[0] = 1;
-
-
- state->CH_Ctrl_Num = CHCTRL_NUM ;
-
- state->CH_Ctrl[0].Ctrl_Num = DN_POLY ;
- state->CH_Ctrl[0].size = 2 ;
- state->CH_Ctrl[0].addr[0] = 68;
- state->CH_Ctrl[0].bit[0] = 6;
- state->CH_Ctrl[0].val[0] = 1;
- state->CH_Ctrl[0].addr[1] = 68;
- state->CH_Ctrl[0].bit[1] = 7;
- state->CH_Ctrl[0].val[1] = 1;
-
- state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ;
- state->CH_Ctrl[1].size = 2 ;
- state->CH_Ctrl[1].addr[0] = 70;
- state->CH_Ctrl[1].bit[0] = 6;
- state->CH_Ctrl[1].val[0] = 1;
- state->CH_Ctrl[1].addr[1] = 70;
- state->CH_Ctrl[1].bit[1] = 7;
- state->CH_Ctrl[1].val[1] = 0;
-
- state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ;
- state->CH_Ctrl[2].size = 9 ;
- state->CH_Ctrl[2].addr[0] = 69;
- state->CH_Ctrl[2].bit[0] = 5;
- state->CH_Ctrl[2].val[0] = 0;
- state->CH_Ctrl[2].addr[1] = 69;
- state->CH_Ctrl[2].bit[1] = 6;
- state->CH_Ctrl[2].val[1] = 0;
- state->CH_Ctrl[2].addr[2] = 69;
- state->CH_Ctrl[2].bit[2] = 7;
- state->CH_Ctrl[2].val[2] = 0;
- state->CH_Ctrl[2].addr[3] = 68;
- state->CH_Ctrl[2].bit[3] = 0;
- state->CH_Ctrl[2].val[3] = 0;
- state->CH_Ctrl[2].addr[4] = 68;
- state->CH_Ctrl[2].bit[4] = 1;
- state->CH_Ctrl[2].val[4] = 0;
- state->CH_Ctrl[2].addr[5] = 68;
- state->CH_Ctrl[2].bit[5] = 2;
- state->CH_Ctrl[2].val[5] = 0;
- state->CH_Ctrl[2].addr[6] = 68;
- state->CH_Ctrl[2].bit[6] = 3;
- state->CH_Ctrl[2].val[6] = 0;
- state->CH_Ctrl[2].addr[7] = 68;
- state->CH_Ctrl[2].bit[7] = 4;
- state->CH_Ctrl[2].val[7] = 0;
- state->CH_Ctrl[2].addr[8] = 68;
- state->CH_Ctrl[2].bit[8] = 5;
- state->CH_Ctrl[2].val[8] = 0;
-
- state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ;
- state->CH_Ctrl[3].size = 1 ;
- state->CH_Ctrl[3].addr[0] = 70;
- state->CH_Ctrl[3].bit[0] = 5;
- state->CH_Ctrl[3].val[0] = 0;
-
- state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ;
- state->CH_Ctrl[4].size = 3 ;
- state->CH_Ctrl[4].addr[0] = 73;
- state->CH_Ctrl[4].bit[0] = 4;
- state->CH_Ctrl[4].val[0] = 0;
- state->CH_Ctrl[4].addr[1] = 73;
- state->CH_Ctrl[4].bit[1] = 5;
- state->CH_Ctrl[4].val[1] = 1;
- state->CH_Ctrl[4].addr[2] = 73;
- state->CH_Ctrl[4].bit[2] = 6;
- state->CH_Ctrl[4].val[2] = 0;
-
- state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ;
- state->CH_Ctrl[5].size = 4 ;
- state->CH_Ctrl[5].addr[0] = 70;
- state->CH_Ctrl[5].bit[0] = 0;
- state->CH_Ctrl[5].val[0] = 0;
- state->CH_Ctrl[5].addr[1] = 70;
- state->CH_Ctrl[5].bit[1] = 1;
- state->CH_Ctrl[5].val[1] = 0;
- state->CH_Ctrl[5].addr[2] = 70;
- state->CH_Ctrl[5].bit[2] = 2;
- state->CH_Ctrl[5].val[2] = 0;
- state->CH_Ctrl[5].addr[3] = 70;
- state->CH_Ctrl[5].bit[3] = 3;
- state->CH_Ctrl[5].val[3] = 0;
-
- state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ;
- state->CH_Ctrl[6].size = 1 ;
- state->CH_Ctrl[6].addr[0] = 70;
- state->CH_Ctrl[6].bit[0] = 4;
- state->CH_Ctrl[6].val[0] = 1;
-
- state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ;
- state->CH_Ctrl[7].size = 1 ;
- state->CH_Ctrl[7].addr[0] = 111;
- state->CH_Ctrl[7].bit[0] = 4;
- state->CH_Ctrl[7].val[0] = 0;
-
- state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ;
- state->CH_Ctrl[8].size = 1 ;
- state->CH_Ctrl[8].addr[0] = 111;
- state->CH_Ctrl[8].bit[0] = 7;
- state->CH_Ctrl[8].val[0] = 1;
-
- state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ;
- state->CH_Ctrl[9].size = 1 ;
- state->CH_Ctrl[9].addr[0] = 111;
- state->CH_Ctrl[9].bit[0] = 6;
- state->CH_Ctrl[9].val[0] = 1;
-
- state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ;
- state->CH_Ctrl[10].size = 1 ;
- state->CH_Ctrl[10].addr[0] = 111;
- state->CH_Ctrl[10].bit[0] = 5;
- state->CH_Ctrl[10].val[0] = 0;
-
- state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ;
- state->CH_Ctrl[11].size = 2 ;
- state->CH_Ctrl[11].addr[0] = 110;
- state->CH_Ctrl[11].bit[0] = 0;
- state->CH_Ctrl[11].val[0] = 1;
- state->CH_Ctrl[11].addr[1] = 110;
- state->CH_Ctrl[11].bit[1] = 1;
- state->CH_Ctrl[11].val[1] = 0;
-
- state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ;
- state->CH_Ctrl[12].size = 3 ;
- state->CH_Ctrl[12].addr[0] = 69;
- state->CH_Ctrl[12].bit[0] = 2;
- state->CH_Ctrl[12].val[0] = 0;
- state->CH_Ctrl[12].addr[1] = 69;
- state->CH_Ctrl[12].bit[1] = 3;
- state->CH_Ctrl[12].val[1] = 0;
- state->CH_Ctrl[12].addr[2] = 69;
- state->CH_Ctrl[12].bit[2] = 4;
- state->CH_Ctrl[12].val[2] = 0;
-
- state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ;
- state->CH_Ctrl[13].size = 6 ;
- state->CH_Ctrl[13].addr[0] = 110;
- state->CH_Ctrl[13].bit[0] = 2;
- state->CH_Ctrl[13].val[0] = 0;
- state->CH_Ctrl[13].addr[1] = 110;
- state->CH_Ctrl[13].bit[1] = 3;
- state->CH_Ctrl[13].val[1] = 0;
- state->CH_Ctrl[13].addr[2] = 110;
- state->CH_Ctrl[13].bit[2] = 4;
- state->CH_Ctrl[13].val[2] = 0;
- state->CH_Ctrl[13].addr[3] = 110;
- state->CH_Ctrl[13].bit[3] = 5;
- state->CH_Ctrl[13].val[3] = 0;
- state->CH_Ctrl[13].addr[4] = 110;
- state->CH_Ctrl[13].bit[4] = 6;
- state->CH_Ctrl[13].val[4] = 0;
- state->CH_Ctrl[13].addr[5] = 110;
- state->CH_Ctrl[13].bit[5] = 7;
- state->CH_Ctrl[13].val[5] = 1;
-
- state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ;
- state->CH_Ctrl[14].size = 7 ;
- state->CH_Ctrl[14].addr[0] = 14;
- state->CH_Ctrl[14].bit[0] = 0;
- state->CH_Ctrl[14].val[0] = 0;
- state->CH_Ctrl[14].addr[1] = 14;
- state->CH_Ctrl[14].bit[1] = 1;
- state->CH_Ctrl[14].val[1] = 0;
- state->CH_Ctrl[14].addr[2] = 14;
- state->CH_Ctrl[14].bit[2] = 2;
- state->CH_Ctrl[14].val[2] = 0;
- state->CH_Ctrl[14].addr[3] = 14;
- state->CH_Ctrl[14].bit[3] = 3;
- state->CH_Ctrl[14].val[3] = 0;
- state->CH_Ctrl[14].addr[4] = 14;
- state->CH_Ctrl[14].bit[4] = 4;
- state->CH_Ctrl[14].val[4] = 0;
- state->CH_Ctrl[14].addr[5] = 14;
- state->CH_Ctrl[14].bit[5] = 5;
- state->CH_Ctrl[14].val[5] = 0;
- state->CH_Ctrl[14].addr[6] = 14;
- state->CH_Ctrl[14].bit[6] = 6;
- state->CH_Ctrl[14].val[6] = 0;
-
- state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ;
- state->CH_Ctrl[15].size = 18 ;
- state->CH_Ctrl[15].addr[0] = 17;
- state->CH_Ctrl[15].bit[0] = 6;
- state->CH_Ctrl[15].val[0] = 0;
- state->CH_Ctrl[15].addr[1] = 17;
- state->CH_Ctrl[15].bit[1] = 7;
- state->CH_Ctrl[15].val[1] = 0;
- state->CH_Ctrl[15].addr[2] = 16;
- state->CH_Ctrl[15].bit[2] = 0;
- state->CH_Ctrl[15].val[2] = 0;
- state->CH_Ctrl[15].addr[3] = 16;
- state->CH_Ctrl[15].bit[3] = 1;
- state->CH_Ctrl[15].val[3] = 0;
- state->CH_Ctrl[15].addr[4] = 16;
- state->CH_Ctrl[15].bit[4] = 2;
- state->CH_Ctrl[15].val[4] = 0;
- state->CH_Ctrl[15].addr[5] = 16;
- state->CH_Ctrl[15].bit[5] = 3;
- state->CH_Ctrl[15].val[5] = 0;
- state->CH_Ctrl[15].addr[6] = 16;
- state->CH_Ctrl[15].bit[6] = 4;
- state->CH_Ctrl[15].val[6] = 0;
- state->CH_Ctrl[15].addr[7] = 16;
- state->CH_Ctrl[15].bit[7] = 5;
- state->CH_Ctrl[15].val[7] = 0;
- state->CH_Ctrl[15].addr[8] = 16;
- state->CH_Ctrl[15].bit[8] = 6;
- state->CH_Ctrl[15].val[8] = 0;
- state->CH_Ctrl[15].addr[9] = 16;
- state->CH_Ctrl[15].bit[9] = 7;
- state->CH_Ctrl[15].val[9] = 0;
- state->CH_Ctrl[15].addr[10] = 15;
- state->CH_Ctrl[15].bit[10] = 0;
- state->CH_Ctrl[15].val[10] = 0;
- state->CH_Ctrl[15].addr[11] = 15;
- state->CH_Ctrl[15].bit[11] = 1;
- state->CH_Ctrl[15].val[11] = 0;
- state->CH_Ctrl[15].addr[12] = 15;
- state->CH_Ctrl[15].bit[12] = 2;
- state->CH_Ctrl[15].val[12] = 0;
- state->CH_Ctrl[15].addr[13] = 15;
- state->CH_Ctrl[15].bit[13] = 3;
- state->CH_Ctrl[15].val[13] = 0;
- state->CH_Ctrl[15].addr[14] = 15;
- state->CH_Ctrl[15].bit[14] = 4;
- state->CH_Ctrl[15].val[14] = 0;
- state->CH_Ctrl[15].addr[15] = 15;
- state->CH_Ctrl[15].bit[15] = 5;
- state->CH_Ctrl[15].val[15] = 0;
- state->CH_Ctrl[15].addr[16] = 15;
- state->CH_Ctrl[15].bit[16] = 6;
- state->CH_Ctrl[15].val[16] = 1;
- state->CH_Ctrl[15].addr[17] = 15;
- state->CH_Ctrl[15].bit[17] = 7;
- state->CH_Ctrl[15].val[17] = 1;
-
- state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ;
- state->CH_Ctrl[16].size = 5 ;
- state->CH_Ctrl[16].addr[0] = 112;
- state->CH_Ctrl[16].bit[0] = 0;
- state->CH_Ctrl[16].val[0] = 0;
- state->CH_Ctrl[16].addr[1] = 112;
- state->CH_Ctrl[16].bit[1] = 1;
- state->CH_Ctrl[16].val[1] = 0;
- state->CH_Ctrl[16].addr[2] = 112;
- state->CH_Ctrl[16].bit[2] = 2;
- state->CH_Ctrl[16].val[2] = 0;
- state->CH_Ctrl[16].addr[3] = 112;
- state->CH_Ctrl[16].bit[3] = 3;
- state->CH_Ctrl[16].val[3] = 0;
- state->CH_Ctrl[16].addr[4] = 112;
- state->CH_Ctrl[16].bit[4] = 4;
- state->CH_Ctrl[16].val[4] = 1;
-
- state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ;
- state->CH_Ctrl[17].size = 1 ;
- state->CH_Ctrl[17].addr[0] = 14;
- state->CH_Ctrl[17].bit[0] = 7;
- state->CH_Ctrl[17].val[0] = 0;
-
- state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ;
- state->CH_Ctrl[18].size = 4 ;
- state->CH_Ctrl[18].addr[0] = 107;
- state->CH_Ctrl[18].bit[0] = 3;
- state->CH_Ctrl[18].val[0] = 0;
- state->CH_Ctrl[18].addr[1] = 107;
- state->CH_Ctrl[18].bit[1] = 4;
- state->CH_Ctrl[18].val[1] = 0;
- state->CH_Ctrl[18].addr[2] = 107;
- state->CH_Ctrl[18].bit[2] = 5;
- state->CH_Ctrl[18].val[2] = 0;
- state->CH_Ctrl[18].addr[3] = 107;
- state->CH_Ctrl[18].bit[3] = 6;
- state->CH_Ctrl[18].val[3] = 0;
-
- state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ;
- state->CH_Ctrl[19].size = 3 ;
- state->CH_Ctrl[19].addr[0] = 107;
- state->CH_Ctrl[19].bit[0] = 7;
- state->CH_Ctrl[19].val[0] = 1;
- state->CH_Ctrl[19].addr[1] = 106;
- state->CH_Ctrl[19].bit[1] = 0;
- state->CH_Ctrl[19].val[1] = 1;
- state->CH_Ctrl[19].addr[2] = 106;
- state->CH_Ctrl[19].bit[2] = 1;
- state->CH_Ctrl[19].val[2] = 1;
-
- state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ;
- state->CH_Ctrl[20].size = 11 ;
- state->CH_Ctrl[20].addr[0] = 109;
- state->CH_Ctrl[20].bit[0] = 2;
- state->CH_Ctrl[20].val[0] = 0;
- state->CH_Ctrl[20].addr[1] = 109;
- state->CH_Ctrl[20].bit[1] = 3;
- state->CH_Ctrl[20].val[1] = 0;
- state->CH_Ctrl[20].addr[2] = 109;
- state->CH_Ctrl[20].bit[2] = 4;
- state->CH_Ctrl[20].val[2] = 0;
- state->CH_Ctrl[20].addr[3] = 109;
- state->CH_Ctrl[20].bit[3] = 5;
- state->CH_Ctrl[20].val[3] = 0;
- state->CH_Ctrl[20].addr[4] = 109;
- state->CH_Ctrl[20].bit[4] = 6;
- state->CH_Ctrl[20].val[4] = 0;
- state->CH_Ctrl[20].addr[5] = 109;
- state->CH_Ctrl[20].bit[5] = 7;
- state->CH_Ctrl[20].val[5] = 0;
- state->CH_Ctrl[20].addr[6] = 108;
- state->CH_Ctrl[20].bit[6] = 0;
- state->CH_Ctrl[20].val[6] = 0;
- state->CH_Ctrl[20].addr[7] = 108;
- state->CH_Ctrl[20].bit[7] = 1;
- state->CH_Ctrl[20].val[7] = 0;
- state->CH_Ctrl[20].addr[8] = 108;
- state->CH_Ctrl[20].bit[8] = 2;
- state->CH_Ctrl[20].val[8] = 1;
- state->CH_Ctrl[20].addr[9] = 108;
- state->CH_Ctrl[20].bit[9] = 3;
- state->CH_Ctrl[20].val[9] = 1;
- state->CH_Ctrl[20].addr[10] = 108;
- state->CH_Ctrl[20].bit[10] = 4;
- state->CH_Ctrl[20].val[10] = 1;
-
- state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ;
- state->CH_Ctrl[21].size = 6 ;
- state->CH_Ctrl[21].addr[0] = 106;
- state->CH_Ctrl[21].bit[0] = 2;
- state->CH_Ctrl[21].val[0] = 0;
- state->CH_Ctrl[21].addr[1] = 106;
- state->CH_Ctrl[21].bit[1] = 3;
- state->CH_Ctrl[21].val[1] = 0;
- state->CH_Ctrl[21].addr[2] = 106;
- state->CH_Ctrl[21].bit[2] = 4;
- state->CH_Ctrl[21].val[2] = 0;
- state->CH_Ctrl[21].addr[3] = 106;
- state->CH_Ctrl[21].bit[3] = 5;
- state->CH_Ctrl[21].val[3] = 0;
- state->CH_Ctrl[21].addr[4] = 106;
- state->CH_Ctrl[21].bit[4] = 6;
- state->CH_Ctrl[21].val[4] = 0;
- state->CH_Ctrl[21].addr[5] = 106;
- state->CH_Ctrl[21].bit[5] = 7;
- state->CH_Ctrl[21].val[5] = 1;
-
- state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ;
- state->CH_Ctrl[22].size = 1 ;
- state->CH_Ctrl[22].addr[0] = 138;
- state->CH_Ctrl[22].bit[0] = 4;
- state->CH_Ctrl[22].val[0] = 1;
-
- state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ;
- state->CH_Ctrl[23].size = 1 ;
- state->CH_Ctrl[23].addr[0] = 17;
- state->CH_Ctrl[23].bit[0] = 5;
- state->CH_Ctrl[23].val[0] = 0;
-
- state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ;
- state->CH_Ctrl[24].size = 1 ;
- state->CH_Ctrl[24].addr[0] = 111;
- state->CH_Ctrl[24].bit[0] = 3;
- state->CH_Ctrl[24].val[0] = 0;
-
- state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ;
- state->CH_Ctrl[25].size = 1 ;
- state->CH_Ctrl[25].addr[0] = 112;
- state->CH_Ctrl[25].bit[0] = 7;
- state->CH_Ctrl[25].val[0] = 0;
-
- state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ;
- state->CH_Ctrl[26].size = 1 ;
- state->CH_Ctrl[26].addr[0] = 136;
- state->CH_Ctrl[26].bit[0] = 7;
- state->CH_Ctrl[26].val[0] = 0;
-
- state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ;
- state->CH_Ctrl[27].size = 1 ;
- state->CH_Ctrl[27].addr[0] = 149;
- state->CH_Ctrl[27].bit[0] = 7;
- state->CH_Ctrl[27].val[0] = 0;
-
- state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ;
- state->CH_Ctrl[28].size = 1 ;
- state->CH_Ctrl[28].addr[0] = 149;
- state->CH_Ctrl[28].bit[0] = 6;
- state->CH_Ctrl[28].val[0] = 0;
-
- state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ;
- state->CH_Ctrl[29].size = 1 ;
- state->CH_Ctrl[29].addr[0] = 149;
- state->CH_Ctrl[29].bit[0] = 5;
- state->CH_Ctrl[29].val[0] = 1;
-
- state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ;
- state->CH_Ctrl[30].size = 1 ;
- state->CH_Ctrl[30].addr[0] = 149;
- state->CH_Ctrl[30].bit[0] = 4;
- state->CH_Ctrl[30].val[0] = 1;
-
- state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ;
- state->CH_Ctrl[31].size = 1 ;
- state->CH_Ctrl[31].addr[0] = 149;
- state->CH_Ctrl[31].bit[0] = 3;
- state->CH_Ctrl[31].val[0] = 0;
-
- state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ;
- state->CH_Ctrl[32].size = 1 ;
- state->CH_Ctrl[32].addr[0] = 93;
- state->CH_Ctrl[32].bit[0] = 1;
- state->CH_Ctrl[32].val[0] = 0;
-
- state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ;
- state->CH_Ctrl[33].size = 1 ;
- state->CH_Ctrl[33].addr[0] = 93;
- state->CH_Ctrl[33].bit[0] = 0;
- state->CH_Ctrl[33].val[0] = 0;
-
- state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ;
- state->CH_Ctrl[34].size = 6 ;
- state->CH_Ctrl[34].addr[0] = 92;
- state->CH_Ctrl[34].bit[0] = 2;
- state->CH_Ctrl[34].val[0] = 0;
- state->CH_Ctrl[34].addr[1] = 92;
- state->CH_Ctrl[34].bit[1] = 3;
- state->CH_Ctrl[34].val[1] = 0;
- state->CH_Ctrl[34].addr[2] = 92;
- state->CH_Ctrl[34].bit[2] = 4;
- state->CH_Ctrl[34].val[2] = 0;
- state->CH_Ctrl[34].addr[3] = 92;
- state->CH_Ctrl[34].bit[3] = 5;
- state->CH_Ctrl[34].val[3] = 0;
- state->CH_Ctrl[34].addr[4] = 92;
- state->CH_Ctrl[34].bit[4] = 6;
- state->CH_Ctrl[34].val[4] = 0;
- state->CH_Ctrl[34].addr[5] = 92;
- state->CH_Ctrl[34].bit[5] = 7;
- state->CH_Ctrl[34].val[5] = 0;
-
- state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ;
- state->CH_Ctrl[35].size = 6 ;
- state->CH_Ctrl[35].addr[0] = 93;
- state->CH_Ctrl[35].bit[0] = 2;
- state->CH_Ctrl[35].val[0] = 0;
- state->CH_Ctrl[35].addr[1] = 93;
- state->CH_Ctrl[35].bit[1] = 3;
- state->CH_Ctrl[35].val[1] = 0;
- state->CH_Ctrl[35].addr[2] = 93;
- state->CH_Ctrl[35].bit[2] = 4;
- state->CH_Ctrl[35].val[2] = 0;
- state->CH_Ctrl[35].addr[3] = 93;
- state->CH_Ctrl[35].bit[3] = 5;
- state->CH_Ctrl[35].val[3] = 0;
- state->CH_Ctrl[35].addr[4] = 93;
- state->CH_Ctrl[35].bit[4] = 6;
- state->CH_Ctrl[35].val[4] = 0;
- state->CH_Ctrl[35].addr[5] = 93;
- state->CH_Ctrl[35].bit[5] = 7;
- state->CH_Ctrl[35].val[5] = 0;
-
-#ifdef _MXL_PRODUCTION
- state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ;
- state->CH_Ctrl[36].size = 1 ;
- state->CH_Ctrl[36].addr[0] = 109;
- state->CH_Ctrl[36].bit[0] = 1;
- state->CH_Ctrl[36].val[0] = 1;
-
- state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ;
- state->CH_Ctrl[37].size = 2 ;
- state->CH_Ctrl[37].addr[0] = 112;
- state->CH_Ctrl[37].bit[0] = 5;
- state->CH_Ctrl[37].val[0] = 0;
- state->CH_Ctrl[37].addr[1] = 112;
- state->CH_Ctrl[37].bit[1] = 6;
- state->CH_Ctrl[37].val[1] = 0;
-
- state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ;
- state->CH_Ctrl[38].size = 1 ;
- state->CH_Ctrl[38].addr[0] = 65;
- state->CH_Ctrl[38].bit[0] = 1;
- state->CH_Ctrl[38].val[0] = 0;
-#endif
-
- return 0 ;
-}
-
-static void InitTunerControls(struct dvb_frontend *fe)
-{
- MXL5005_RegisterInit(fe);
- MXL5005_ControlInit(fe);
-#ifdef _MXL_INTERNAL
- MXL5005_MXLControlInit(fe);
-#endif
-}
-
-static u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
- u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */
- u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */
- u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */
- u32 IF_out, /* Desired IF Out Frequency */
- u32 Fxtal, /* XTAL Frequency */
- u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */
- u16 TOP, /* 0: Dual AGC; Value: take over point */
- u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */
- u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */
- u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */
- u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */
- u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */
-
- /* Modulation Type; */
- /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */
- u8 Mod_Type,
-
- /* Tracking Filter */
- /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */
- u8 TF_Type
- )
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0;
-
- state->Mode = Mode;
- state->IF_Mode = IF_mode;
- state->Chan_Bandwidth = Bandwidth;
- state->IF_OUT = IF_out;
- state->Fxtal = Fxtal;
- state->AGC_Mode = AGC_Mode;
- state->TOP = TOP;
- state->IF_OUT_LOAD = IF_OUT_LOAD;
- state->CLOCK_OUT = CLOCK_OUT;
- state->DIV_OUT = DIV_OUT;
- state->CAPSELECT = CAPSELECT;
- state->EN_RSSI = EN_RSSI;
- state->Mod_Type = Mod_Type;
- state->TF_Type = TF_Type;
-
- /* Initialize all the controls and registers */
- InitTunerControls(fe);
-
- /* Synthesizer LO frequency calculation */
- MXL_SynthIFLO_Calc(fe);
-
- return status;
-}
-
-static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- if (state->Mode == 1) /* Digital Mode */
- state->IF_LO = state->IF_OUT;
- else /* Analog Mode */ {
- if (state->IF_Mode == 0) /* Analog Zero IF mode */
- state->IF_LO = state->IF_OUT + 400000;
- else /* Analog Low IF mode */
- state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2;
- }
-}
-
-static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
-
- if (state->Mode == 1) /* Digital Mode */ {
- /* remove 20.48MHz setting for 2.6.10 */
- state->RF_LO = state->RF_IN;
- /* change for 2.6.6 */
- state->TG_LO = state->RF_IN - 750000;
- } else /* Analog Mode */ {
- if (state->IF_Mode == 0) /* Analog Zero IF mode */ {
- state->RF_LO = state->RF_IN - 400000;
- state->TG_LO = state->RF_IN - 1750000;
- } else /* Analog Low IF mode */ {
- state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2;
- state->TG_LO = state->RF_IN -
- state->Chan_Bandwidth + 500000;
- }
- }
-}
-
-static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
-{
- u16 status = 0;
-
- status += MXL_ControlWrite(fe, OVERRIDE_1, 1);
- status += MXL_ControlWrite(fe, OVERRIDE_2, 1);
- status += MXL_ControlWrite(fe, OVERRIDE_3, 1);
- status += MXL_ControlWrite(fe, OVERRIDE_4, 1);
-
- return status;
-}
-
-static u16 MXL_BlockInit(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0;
-
- status += MXL_OverwriteICDefault(fe);
-
- /* Downconverter Control Dig Ana */
- status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0);
-
- /* Filter Control Dig Ana */
- status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1);
- status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2);
- status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0);
- status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1);
- status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0);
-
- /* Initialize Low-Pass Filter */
- if (state->Mode) { /* Digital Mode */
- switch (state->Chan_Bandwidth) {
- case 8000000:
- status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0);
- break;
- case 7000000:
- status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2);
- break;
- case 6000000:
- status += MXL_ControlWrite(fe,
- BB_DLPF_BANDSEL, 3);
- break;
- }
- } else { /* Analog Mode */
- switch (state->Chan_Bandwidth) {
- case 8000000: /* Low Zero */
- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
- (state->IF_Mode ? 0 : 3));
- break;
- case 7000000:
- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
- (state->IF_Mode ? 1 : 4));
- break;
- case 6000000:
- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
- (state->IF_Mode ? 2 : 5));
- break;
- }
- }
-
- /* Charge Pump Control Dig Ana */
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8);
- status += MXL_ControlWrite(fe,
- RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1);
- status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0);
-
- /* AGC TOP Control */
- if (state->AGC_Mode == 0) /* Dual AGC */ {
- status += MXL_ControlWrite(fe, AGC_IF, 15);
- status += MXL_ControlWrite(fe, AGC_RF, 15);
- } else /* Single AGC Mode Dig Ana */
- status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12);
-
- if (state->TOP == 55) /* TOP == 5.5 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x0);
-
- if (state->TOP == 72) /* TOP == 7.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x1);
-
- if (state->TOP == 92) /* TOP == 9.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x2);
-
- if (state->TOP == 110) /* TOP == 11.0 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x3);
-
- if (state->TOP == 129) /* TOP == 12.9 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x4);
-
- if (state->TOP == 147) /* TOP == 14.7 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x5);
-
- if (state->TOP == 168) /* TOP == 16.8 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x6);
-
- if (state->TOP == 194) /* TOP == 19.4 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x7);
-
- if (state->TOP == 212) /* TOP == 21.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0x9);
-
- if (state->TOP == 232) /* TOP == 23.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xA);
-
- if (state->TOP == 252) /* TOP == 25.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xB);
-
- if (state->TOP == 271) /* TOP == 27.1 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xC);
-
- if (state->TOP == 292) /* TOP == 29.2 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xD);
-
- if (state->TOP == 317) /* TOP == 31.7 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xE);
-
- if (state->TOP == 349) /* TOP == 34.9 */
- status += MXL_ControlWrite(fe, AGC_IF, 0xF);
-
- /* IF Synthesizer Control */
- status += MXL_IFSynthInit(fe);
-
- /* IF UpConverter Control */
- if (state->IF_OUT_LOAD == 200) {
- status += MXL_ControlWrite(fe, DRV_RES_SEL, 6);
- status += MXL_ControlWrite(fe, I_DRIVER, 2);
- }
- if (state->IF_OUT_LOAD == 300) {
- status += MXL_ControlWrite(fe, DRV_RES_SEL, 4);
- status += MXL_ControlWrite(fe, I_DRIVER, 1);
- }
-
- /* Anti-Alias Filtering Control
- * initialise Anti-Aliasing Filter
- */
- if (state->Mode) { /* Digital Mode */
- if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) {
- status += MXL_ControlWrite(fe, EN_AAF, 1);
- status += MXL_ControlWrite(fe, EN_3P, 1);
- status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
- }
- if ((state->IF_OUT == 36125000UL) ||
- (state->IF_OUT == 36150000UL)) {
- status += MXL_ControlWrite(fe, EN_AAF, 1);
- status += MXL_ControlWrite(fe, EN_3P, 1);
- status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1);
- }
- if (state->IF_OUT > 36150000UL) {
- status += MXL_ControlWrite(fe, EN_AAF, 0);
- status += MXL_ControlWrite(fe, EN_3P, 1);
- status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1);
- }
- } else { /* Analog Mode */
- if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) {
- status += MXL_ControlWrite(fe, EN_AAF, 1);
- status += MXL_ControlWrite(fe, EN_3P, 1);
- status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
- }
- if (state->IF_OUT > 5000000UL) {
- status += MXL_ControlWrite(fe, EN_AAF, 0);
- status += MXL_ControlWrite(fe, EN_3P, 0);
- status += MXL_ControlWrite(fe, EN_AUX_3P, 0);
- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
- }
- }
-
- /* Demod Clock Out */
- if (state->CLOCK_OUT)
- status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1);
- else
- status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0);
-
- if (state->DIV_OUT == 1)
- status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1);
- if (state->DIV_OUT == 0)
- status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0);
-
- /* Crystal Control */
- if (state->CAPSELECT)
- status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1);
- else
- status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0);
-
- if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL)
- status += MXL_ControlWrite(fe, IF_SEL_DBL, 1);
- if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL)
- status += MXL_ControlWrite(fe, IF_SEL_DBL, 0);
-
- if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL)
- status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3);
- if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL)
- status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0);
-
- /* Misc Controls */
- if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */
- status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0);
- else
- status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1);
-
- /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */
-
- /* Set TG_R_DIV */
- status += MXL_ControlWrite(fe, TG_R_DIV,
- MXL_Ceiling(state->Fxtal, 1000000));
-
- /* Apply Default value to BB_INITSTATE_DLPF_TUNE */
-
- /* RSSI Control */
- if (state->EN_RSSI) {
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
-
- /* TOP point */
- status += MXL_ControlWrite(fe, RFA_FLR, 0);
- status += MXL_ControlWrite(fe, RFA_CEIL, 12);
- }
-
- /* Modulation type bit settings
- * Override the control values preset
- */
- if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ {
- state->AGC_Mode = 1; /* Single AGC Mode */
-
- /* Enable RSSI */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
-
- /* TOP point */
- status += MXL_ControlWrite(fe, RFA_FLR, 2);
- status += MXL_ControlWrite(fe, RFA_CEIL, 13);
- if (state->IF_OUT <= 6280000UL) /* Low IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
- else /* High IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
-
- }
- if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ {
- state->AGC_Mode = 1; /* Single AGC Mode */
-
- /* Enable RSSI */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
-
- /* TOP point */
- status += MXL_ControlWrite(fe, RFA_FLR, 2);
- status += MXL_ControlWrite(fe, RFA_CEIL, 13);
- status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1);
- /* Low Zero */
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5);
-
- if (state->IF_OUT <= 6280000UL) /* Low IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
- else /* High IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
- }
- if (state->Mod_Type == MXL_QAM) /* QAM Mode */ {
- state->Mode = MXL_DIGITAL_MODE;
-
- /* state->AGC_Mode = 1; */ /* Single AGC Mode */
-
- /* Disable RSSI */ /* change here for v2.6.5 */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
- /* change here for v2.6.5 */
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
-
- if (state->IF_OUT <= 6280000UL) /* Low IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
- else /* High IF */
- status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
-
- }
- if (state->Mod_Type == MXL_ANALOG_CABLE) {
- /* Analog Cable Mode */
- /* state->Mode = MXL_DIGITAL_MODE; */
-
- state->AGC_Mode = 1; /* Single AGC Mode */
-
- /* Disable RSSI */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
- /* change for 2.6.3 */
- status += MXL_ControlWrite(fe, AGC_IF, 1);
- status += MXL_ControlWrite(fe, AGC_RF, 15);
- status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
- }
-
- if (state->Mod_Type == MXL_ANALOG_OTA) {
- /* Analog OTA Terrestrial mode add for 2.6.7 */
- /* state->Mode = MXL_ANALOG_MODE; */
-
- /* Enable RSSI */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
- status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
- }
-
- /* RSSI disable */
- if (state->EN_RSSI == 0) {
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
- }
-
- return status;
-}
-
-static u16 MXL_IFSynthInit(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0 ;
- u32 Fref = 0 ;
- u32 Kdbl, intModVal ;
- u32 fracModVal ;
- Kdbl = 2 ;
-
- if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL)
- Kdbl = 2 ;
- if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL)
- Kdbl = 1 ;
-
- /* IF Synthesizer Control */
- if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ {
- if (state->IF_LO == 41000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 328000000UL ;
- }
- if (state->IF_LO == 47000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 376000000UL ;
- }
- if (state->IF_LO == 54000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 324000000UL ;
- }
- if (state->IF_LO == 60000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 39250000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 314000000UL ;
- }
- if (state->IF_LO == 39650000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 317200000UL ;
- }
- if (state->IF_LO == 40150000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 321200000UL ;
- }
- if (state->IF_LO == 40650000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 325200000UL ;
- }
- }
-
- if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) {
- if (state->IF_LO == 57000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 342000000UL ;
- }
- if (state->IF_LO == 44000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 352000000UL ;
- }
- if (state->IF_LO == 43750000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 350000000UL ;
- }
- if (state->IF_LO == 36650000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 366500000UL ;
- }
- if (state->IF_LO == 36150000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 361500000UL ;
- }
- if (state->IF_LO == 36000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 35250000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 352500000UL ;
- }
- if (state->IF_LO == 34750000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 347500000UL ;
- }
- if (state->IF_LO == 6280000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 376800000UL ;
- }
- if (state->IF_LO == 5000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 4500000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 4570000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 365600000UL ;
- }
- if (state->IF_LO == 4000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 57400000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 344400000UL ;
- }
- if (state->IF_LO == 44400000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 355200000UL ;
- }
- if (state->IF_LO == 44150000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 353200000UL ;
- }
- if (state->IF_LO == 37050000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 370500000UL ;
- }
- if (state->IF_LO == 36550000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 365500000UL ;
- }
- if (state->IF_LO == 36125000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 361250000UL ;
- }
- if (state->IF_LO == 6000000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 360000000UL ;
- }
- if (state->IF_LO == 5400000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 324000000UL ;
- }
- if (state->IF_LO == 5380000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
- Fref = 322800000UL ;
- }
- if (state->IF_LO == 5200000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 374400000UL ;
- }
- if (state->IF_LO == 4900000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 352800000UL ;
- }
- if (state->IF_LO == 4400000UL) {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 352000000UL ;
- }
- if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ {
- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05);
- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
- Fref = 365670000UL ;
- }
- }
- /* CHCAL_INT_MOD_IF */
- /* CHCAL_FRAC_MOD_IF */
- intModVal = Fref / (state->Fxtal * Kdbl/2);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal);
-
- fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) *
- intModVal);
-
- fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000);
- status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal);
-
- return status ;
-}
-
-static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0;
- u32 divider_val, E3, E4, E5, E5A;
- u32 Fmax, Fmin, FmaxBin, FminBin;
- u32 Kdbl_RF = 2;
- u32 tg_divval;
- u32 tg_lo;
-
- u32 Fref_TG;
- u32 Fvco;
-
- state->RF_IN = RF_Freq;
-
- MXL_SynthRFTGLO_Calc(fe);
-
- if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL)
- Kdbl_RF = 2;
- if (state->Fxtal > 22000000 && state->Fxtal <= 32000000)
- Kdbl_RF = 1;
-
- /* Downconverter Controls
- * Look-Up Table Implementation for:
- * DN_POLY
- * DN_RFGAIN
- * DN_CAP_RFLPF
- * DN_EN_VHFUHFBAR
- * DN_GAIN_ADJUST
- * Change the boundary reference from RF_IN to RF_LO
- */
- if (state->RF_LO < 40000000UL)
- return -1;
-
- if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 2);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1);
- }
- if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1);
- }
- if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2);
- }
- if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2);
- }
- if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
- }
- if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 1);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
- }
- if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) {
- status += MXL_ControlWrite(fe, DN_POLY, 3);
- status += MXL_ControlWrite(fe, DN_RFGAIN, 2);
- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0);
- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
- }
- if (state->RF_LO > 900000000UL)
- return -1;
-
- /* DN_IQTNBUF_AMP */
- /* DN_IQTNGNBFBIAS_BST */
- if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
- }
- if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1);
- }
- if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) {
- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10);
- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1);
- }
-
- /*
- * Set RF Synth and LO Path Control
- *
- * Look-Up table implementation for:
- * RFSYN_EN_OUTMUX
- * RFSYN_SEL_VCO_OUT
- * RFSYN_SEL_VCO_HI
- * RFSYN_SEL_DIVM
- * RFSYN_RF_DIV_BIAS
- * DN_SEL_FREQ
- *
- * Set divider_val, Fmax, Fmix to use in Equations
- */
- FminBin = 28000000UL ;
- FmaxBin = 42500000UL ;
- if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
- divider_val = 64 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 42500000UL ;
- FmaxBin = 56000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
- divider_val = 64 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 56000000UL ;
- FmaxBin = 85000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
- divider_val = 32 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 85000000UL ;
- FmaxBin = 112000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
- divider_val = 32 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 112000000UL ;
- FmaxBin = 170000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2);
- divider_val = 16 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 170000000UL ;
- FmaxBin = 225000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2);
- divider_val = 16 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 225000000UL ;
- FmaxBin = 300000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4);
- divider_val = 8 ;
- Fmax = 340000000UL ;
- Fmin = FminBin ;
- }
- FminBin = 300000000UL ;
- FmaxBin = 340000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- divider_val = 8 ;
- Fmax = FmaxBin ;
- Fmin = 225000000UL ;
- }
- FminBin = 340000000UL ;
- FmaxBin = 450000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- divider_val = 8 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 450000000UL ;
- FmaxBin = 680000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- divider_val = 4 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 680000000UL ;
- FmaxBin = 900000000UL ;
- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- divider_val = 4 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
-
- /* CHCAL_INT_MOD_RF
- * CHCAL_FRAC_MOD_RF
- * RFSYN_LPF_R
- * CHCAL_EN_INT_RF
- */
- /* Equation E3 RFSYN_VCO_BIAS */
- E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ;
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3);
-
- /* Equation E4 CHCAL_INT_MOD_RF */
- E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000);
- MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4);
-
- /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */
- E5 = ((2<<17)*(state->RF_LO/10000*divider_val -
- (E4*(2*state->Fxtal*Kdbl_RF)/10000))) /
- (2*state->Fxtal*Kdbl_RF/10000);
-
- status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5);
-
- /* Equation E5A RFSYN_LPF_R */
- E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ;
- status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A);
-
- /* Euqation E5B CHCAL_EN_INIT_RF */
- status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0));
- /*if (E5 == 0)
- * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1);
- *else
- * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5);
- */
-
- /*
- * Set TG Synth
- *
- * Look-Up table implementation for:
- * TG_LO_DIVVAL
- * TG_LO_SELVAL
- *
- * Set divider_val, Fmax, Fmix to use in Equations
- */
- if (state->TG_LO < 33000000UL)
- return -1;
-
- FminBin = 33000000UL ;
- FmaxBin = 50000000UL ;
- if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0);
- divider_val = 36 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 50000000UL ;
- FmaxBin = 67000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0);
- divider_val = 24 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 67000000UL ;
- FmaxBin = 100000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
- divider_val = 18 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 100000000UL ;
- FmaxBin = 150000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
- divider_val = 12 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 150000000UL ;
- FmaxBin = 200000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
- divider_val = 8 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 200000000UL ;
- FmaxBin = 300000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3);
- divider_val = 6 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 300000000UL ;
- FmaxBin = 400000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3);
- divider_val = 4 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 400000000UL ;
- FmaxBin = 600000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7);
- divider_val = 3 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
- FminBin = 600000000UL ;
- FmaxBin = 900000000UL ;
- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7);
- divider_val = 2 ;
- Fmax = FmaxBin ;
- Fmin = FminBin ;
- }
-
- /* TG_DIV_VAL */
- tg_divval = (state->TG_LO*divider_val/100000) *
- (MXL_Ceiling(state->Fxtal, 1000000) * 100) /
- (state->Fxtal/1000);
-
- status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval);
-
- if (state->TG_LO > 600000000UL)
- status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1);
-
- Fmax = 1800000000UL ;
- Fmin = 1200000000UL ;
-
- /* prevent overflow of 32 bit unsigned integer, use
- * following equation. Edit for v2.6.4
- */
- /* Fref_TF = Fref_TG * 1000 */
- Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000);
-
- /* Fvco = Fvco/10 */
- Fvco = (state->TG_LO/10000) * divider_val * Fref_TG;
-
- tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8;
-
- /* below equation is same as above but much harder to debug.
- *
- * static u32 MXL_GetXtalInt(u32 Xtal_Freq)
- * {
- * if ((Xtal_Freq % 1000000) == 0)
- * return (Xtal_Freq / 10000);
- * else
- * return (((Xtal_Freq / 1000000) + 1)*100);
- * }
- *
- * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal);
- * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) -
- * ((state->TG_LO/10000)*divider_val *
- * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 *
- * Xtal_Int/100) + 8;
- */
-
- status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo);
-
- /* add for 2.6.5 Special setting for QAM */
- if (state->Mod_Type == MXL_QAM) {
- if (state->config->qam_gain != 0)
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN,
- state->config->qam_gain);
- else if (state->RF_IN < 680000000)
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
- else
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
- }
-
- /* Off Chip Tracking Filter Control */
- if (state->TF_Type == MXL_TF_OFF) {
- /* Tracking Filter Off State; turn off all the banks */
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */
- status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */
- status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */
- }
-
- if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- }
- if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- }
- if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- }
- if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 0);
- }
- if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 29);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 0);
- }
- if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 0);
- }
- if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 16);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- }
- if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 7);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- }
- if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_C_H) {
-
- /* Tracking Filter type C-H for Hauppauge only */
- status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- }
- if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- }
- if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- }
- if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- }
- if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- }
- if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- }
- if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- }
- if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- }
- if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */
-
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_D_L) {
-
- /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */
- status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
-
- /* if UHF and terrestrial => Turn off Tracking Filter */
- if (state->RF_IN >= 471000000 &&
- (state->RF_IN - 471000000)%6000000 != 0) {
- /* Turn off all the banks */
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_ControlWrite(fe, AGC_IF, 10);
- } else {
- /* if VHF or cable => Turn on Tracking Filter */
- if (state->RF_IN >= 43000000 &&
- state->RF_IN < 140000000) {
-
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 140000000 &&
- state->RF_IN < 240000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 240000000 &&
- state->RF_IN < 340000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 340000000 &&
- state->RF_IN < 430000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 430000000 &&
- state->RF_IN < 470000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 470000000 &&
- state->RF_IN < 570000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 570000000 &&
- state->RF_IN < 620000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 620000000 &&
- state->RF_IN < 760000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 760000000 &&
- state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
- }
-
- if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ {
-
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_F) {
-
- /* Tracking Filter type F */
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_E_2) {
-
- /* Tracking Filter type E_2 */
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_G) {
-
- /* Tracking Filter type G add for v2.6.8 */
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) {
-
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
-
- if (state->TF_Type == MXL_TF_E_NA) {
-
- /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */
- status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
-
- /* if UHF and terrestrial=> Turn off Tracking Filter */
- if (state->RF_IN >= 471000000 &&
- (state->RF_IN - 471000000)%6000000 != 0) {
-
- /* Turn off all the banks */
- status += MXL_SetGPIO(fe, 3, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
-
- /* 2.6.12 Turn on RSSI */
- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
-
- /* RSSI reference point */
- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
-
- /* following parameter is from analog OTA mode,
- * can be change to seek better performance */
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
- } else {
- /* if VHF or Cable => Turn on Tracking Filter */
-
- /* 2.6.12 Turn off RSSI */
- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
-
- /* change back from above condition */
- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5);
-
-
- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
-
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 0);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 0);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 0);
- }
- if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) {
- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
- status += MXL_SetGPIO(fe, 4, 1);
- status += MXL_SetGPIO(fe, 1, 1);
- status += MXL_SetGPIO(fe, 3, 1);
- }
- }
- }
- return status ;
-}
-
-static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
-{
- u16 status = 0;
-
- if (GPIO_Num == 1)
- status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1);
-
- /* GPIO2 is not available */
-
- if (GPIO_Num == 3) {
- if (GPIO_Val == 1) {
- status += MXL_ControlWrite(fe, GPIO_3, 0);
- status += MXL_ControlWrite(fe, GPIO_3B, 0);
- }
- if (GPIO_Val == 0) {
- status += MXL_ControlWrite(fe, GPIO_3, 1);
- status += MXL_ControlWrite(fe, GPIO_3B, 1);
- }
- if (GPIO_Val == 3) { /* tri-state */
- status += MXL_ControlWrite(fe, GPIO_3, 0);
- status += MXL_ControlWrite(fe, GPIO_3B, 1);
- }
- }
- if (GPIO_Num == 4) {
- if (GPIO_Val == 1) {
- status += MXL_ControlWrite(fe, GPIO_4, 0);
- status += MXL_ControlWrite(fe, GPIO_4B, 0);
- }
- if (GPIO_Val == 0) {
- status += MXL_ControlWrite(fe, GPIO_4, 1);
- status += MXL_ControlWrite(fe, GPIO_4B, 1);
- }
- if (GPIO_Val == 3) { /* tri-state */
- status += MXL_ControlWrite(fe, GPIO_4, 0);
- status += MXL_ControlWrite(fe, GPIO_4B, 1);
- }
- }
-
- return status;
-}
-
-static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
-{
- u16 status = 0;
-
- /* Will write ALL Matching Control Name */
- /* Write Matching INIT Control */
- status += MXL_ControlWrite_Group(fe, ControlNum, value, 1);
- /* Write Matching CH Control */
- status += MXL_ControlWrite_Group(fe, ControlNum, value, 2);
-#ifdef _MXL_INTERNAL
- /* Write Matching MXL Control */
- status += MXL_ControlWrite_Group(fe, ControlNum, value, 3);
-#endif
- return status;
-}
-
-static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
- u32 value, u16 controlGroup)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 i, j, k;
- u32 highLimit;
- u32 ctrlVal;
-
- if (controlGroup == 1) /* Initial Control */ {
-
- for (i = 0; i < state->Init_Ctrl_Num; i++) {
-
- if (controlNum == state->Init_Ctrl[i].Ctrl_Num) {
-
- highLimit = 1 << state->Init_Ctrl[i].size;
- if (value < highLimit) {
- for (j = 0; j < state->Init_Ctrl[i].size; j++) {
- state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
- MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]),
- (u8)(state->Init_Ctrl[i].bit[j]),
- (u8)((value>>j) & 0x01));
- }
- ctrlVal = 0;
- for (k = 0; k < state->Init_Ctrl[i].size; k++)
- ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k);
- } else
- return -1;
- }
- }
- }
- if (controlGroup == 2) /* Chan change Control */ {
-
- for (i = 0; i < state->CH_Ctrl_Num; i++) {
-
- if (controlNum == state->CH_Ctrl[i].Ctrl_Num) {
-
- highLimit = 1 << state->CH_Ctrl[i].size;
- if (value < highLimit) {
- for (j = 0; j < state->CH_Ctrl[i].size; j++) {
- state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
- MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]),
- (u8)(state->CH_Ctrl[i].bit[j]),
- (u8)((value>>j) & 0x01));
- }
- ctrlVal = 0;
- for (k = 0; k < state->CH_Ctrl[i].size; k++)
- ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k);
- } else
- return -1;
- }
- }
- }
-#ifdef _MXL_INTERNAL
- if (controlGroup == 3) /* Maxlinear Control */ {
-
- for (i = 0; i < state->MXL_Ctrl_Num; i++) {
-
- if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) {
-
- highLimit = (1 << state->MXL_Ctrl[i].size);
- if (value < highLimit) {
- for (j = 0; j < state->MXL_Ctrl[i].size; j++) {
- state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
- MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]),
- (u8)(state->MXL_Ctrl[i].bit[j]),
- (u8)((value>>j) & 0x01));
- }
- ctrlVal = 0;
- for (k = 0; k < state->MXL_Ctrl[i].size; k++)
- ctrlVal += state->
- MXL_Ctrl[i].val[k] *
- (1 << k);
- } else
- return -1;
- }
- }
- }
-#endif
- return 0 ; /* successful return */
-}
-
-static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- int i ;
-
- for (i = 0; i < 104; i++) {
- if (RegNum == state->TunerRegs[i].Reg_Num) {
- *RegVal = (u8)(state->TunerRegs[i].Reg_Val);
- return 0;
- }
- }
-
- return 1;
-}
-
-static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u32 ctrlVal ;
- u16 i, k ;
-
- for (i = 0; i < state->Init_Ctrl_Num ; i++) {
-
- if (controlNum == state->Init_Ctrl[i].Ctrl_Num) {
-
- ctrlVal = 0;
- for (k = 0; k < state->Init_Ctrl[i].size; k++)
- ctrlVal += state->Init_Ctrl[i].val[k] * (1<<k);
- *value = ctrlVal;
- return 0;
- }
- }
-
- for (i = 0; i < state->CH_Ctrl_Num ; i++) {
-
- if (controlNum == state->CH_Ctrl[i].Ctrl_Num) {
-
- ctrlVal = 0;
- for (k = 0; k < state->CH_Ctrl[i].size; k++)
- ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k);
- *value = ctrlVal;
- return 0;
-
- }
- }
-
-#ifdef _MXL_INTERNAL
- for (i = 0; i < state->MXL_Ctrl_Num ; i++) {
-
- if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) {
-
- ctrlVal = 0;
- for (k = 0; k < state->MXL_Ctrl[i].size; k++)
- ctrlVal += state->MXL_Ctrl[i].val[k] * (1<<k);
- *value = ctrlVal;
- return 0;
-
- }
- }
-#endif
- return 1;
-}
-
-static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit,
- u8 bitVal)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- int i ;
-
- const u8 AND_MAP[8] = {
- 0xFE, 0xFD, 0xFB, 0xF7,
- 0xEF, 0xDF, 0xBF, 0x7F } ;
-
- const u8 OR_MAP[8] = {
- 0x01, 0x02, 0x04, 0x08,
- 0x10, 0x20, 0x40, 0x80 } ;
-
- for (i = 0; i < state->TunerRegs_Num; i++) {
- if (state->TunerRegs[i].Reg_Num == address) {
- if (bitVal)
- state->TunerRegs[i].Reg_Val |= OR_MAP[bit];
- else
- state->TunerRegs[i].Reg_Val &= AND_MAP[bit];
- break ;
- }
- }
-}
-
-static u32 MXL_Ceiling(u32 value, u32 resolution)
-{
- return value / resolution + (value % resolution > 0 ? 1 : 0);
-}
-
-/* Retrieve the Initialzation Registers */
-static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
- u8 *RegVal, int *count)
-{
- u16 status = 0;
- int i ;
-
- u8 RegAddr[] = {
- 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73,
- 76, 77, 91, 134, 135, 137, 147,
- 156, 166, 167, 168, 25 };
-
- *count = ARRAY_SIZE(RegAddr);
-
- status += MXL_BlockInit(fe);
-
- for (i = 0 ; i < *count; i++) {
- RegNum[i] = RegAddr[i];
- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
- }
-
- return status;
-}
-
-static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal,
- int *count)
-{
- u16 status = 0;
- int i ;
-
-/* add 77, 166, 167, 168 register for 2.6.12 */
-#ifdef _MXL_PRODUCTION
- u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106,
- 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ;
-#else
- u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106,
- 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ;
- /*
- u8 RegAddr[171];
- for (i = 0; i <= 170; i++)
- RegAddr[i] = i;
- */
-#endif
-
- *count = ARRAY_SIZE(RegAddr);
-
- for (i = 0 ; i < *count; i++) {
- RegNum[i] = RegAddr[i];
- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
- }
-
- return status;
-}
-
-static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
- u8 *RegVal, int *count)
-{
- u16 status = 0;
- int i;
-
- u8 RegAddr[] = {43, 136};
-
- *count = ARRAY_SIZE(RegAddr);
-
- for (i = 0; i < *count; i++) {
- RegNum[i] = RegAddr[i];
- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
- }
-
- return status;
-}
-
-static u16 MXL_GetMasterControl(u8 *MasterReg, int state)
-{
- if (state == 1) /* Load_Start */
- *MasterReg = 0xF3;
- if (state == 2) /* Power_Down */
- *MasterReg = 0x41;
- if (state == 3) /* Synth_Reset */
- *MasterReg = 0xB1;
- if (state == 4) /* Seq_Off */
- *MasterReg = 0xF1;
-
- return 0;
-}
-
-#ifdef _MXL_PRODUCTION
-static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0 ;
-
- if (VCO_Range == 1) {
- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- if (state->Mode == 0 && state->IF_Mode == 1) {
- /* Analog Low IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 180224);
- }
- if (state->Mode == 0 && state->IF_Mode == 0) {
- /* Analog Zero IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 222822);
- }
- if (state->Mode == 1) /* Digital Mode */ {
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 229376);
- }
- }
-
- if (VCO_Range == 2) {
- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41);
- if (state->Mode == 0 && state->IF_Mode == 1) {
- /* Analog Low IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 206438);
- }
- if (state->Mode == 0 && state->IF_Mode == 0) {
- /* Analog Zero IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 206438);
- }
- if (state->Mode == 1) /* Digital Mode */ {
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 16384);
- }
- }
-
- if (VCO_Range == 3) {
- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
- if (state->Mode == 0 && state->IF_Mode == 1) {
- /* Analog Low IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 173670);
- }
- if (state->Mode == 0 && state->IF_Mode == 0) {
- /* Analog Zero IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 173670);
- }
- if (state->Mode == 1) /* Digital Mode */ {
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 245760);
- }
- }
-
- if (VCO_Range == 4) {
- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
- if (state->Mode == 0 && state->IF_Mode == 1) {
- /* Analog Low IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 206438);
- }
- if (state->Mode == 0 && state->IF_Mode == 0) {
- /* Analog Zero IF Mode */
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 206438);
- }
- if (state->Mode == 1) /* Digital Mode */ {
- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
- status += MXL_ControlWrite(fe,
- CHCAL_FRAC_MOD_RF, 212992);
- }
- }
-
- return status;
-}
-
-static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u16 status = 0;
-
- if (Hystersis == 1)
- status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1);
-
- return status;
-}
-#endif
-/* End: Reference driver code found in the Realtek driver that
- * is copyright MaxLinear */
-
-/* ----------------------------------------------------------------
- * Begin: Everything after here is new code to adapt the
- * proprietary Realtek driver into a Linux API tuner.
- * Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
- */
-static int mxl5005s_reset(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- int ret = 0;
-
- u8 buf[2] = { 0xff, 0x00 };
- struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
- .buf = buf, .len = 2 };
-
- dprintk(2, "%s()\n", __func__);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- if (i2c_transfer(state->i2c, &msg, 1) != 1) {
- printk(KERN_WARNING "mxl5005s I2C reset failed\n");
- ret = -EREMOTEIO;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return ret;
-}
-
-/* Write a single byte to a single reg, latch the value if required by
- * following the transaction with the latch byte.
- */
-static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE };
- struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
- .buf = buf, .len = 3 };
-
- if (latch == 0)
- msg.len = 2;
-
- dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr);
-
- if (i2c_transfer(state->i2c, &msg, 1) != 1) {
- printk(KERN_WARNING "mxl5005s I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable,
- u8 *datatable, u8 len)
-{
- int ret = 0, i;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- for (i = 0 ; i < len-1; i++) {
- ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0);
- if (ret < 0)
- break;
- }
-
- ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return ret;
-}
-
-static int mxl5005s_init(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
-
- dprintk(1, "%s()\n", __func__);
- state->current_mode = MXL_QAM;
- return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ);
-}
-
-static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type,
- u32 bandwidth)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
-
- u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
- u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
- int TableLen;
-
- dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth);
-
- mxl5005s_reset(fe);
-
- /* Tuner initialization stage 0 */
- MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
- AddrTable[0] = MASTER_CONTROL_ADDR;
- ByteTable[0] |= state->config->AgcMasterByte;
-
- mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
-
- mxl5005s_AssignTunerMode(fe, mod_type, bandwidth);
-
- /* Tuner initialization stage 1 */
- MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen);
-
- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
-
- return 0;
-}
-
-static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type,
- u32 bandwidth)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- struct mxl5005s_config *c = state->config;
-
- InitTunerControls(fe);
-
- /* Set MxL5005S parameters. */
- MXL5005_TunerConfig(
- fe,
- c->mod_mode,
- c->if_mode,
- bandwidth,
- c->if_freq,
- c->xtal_freq,
- c->agc_mode,
- c->top,
- c->output_load,
- c->clock_out,
- c->div_out,
- c->cap_select,
- c->rssi_enable,
- mod_type,
- c->tracking_filter);
-
- return 0;
-}
-
-static int mxl5005s_set_params(struct dvb_frontend *fe)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- u32 bw = c->bandwidth_hz;
- u32 req_mode, req_bw = 0;
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- switch (delsys) {
- case SYS_ATSC:
- req_mode = MXL_ATSC;
- req_bw = MXL5005S_BANDWIDTH_6MHZ;
- break;
- case SYS_DVBC_ANNEX_B:
- req_mode = MXL_QAM;
- req_bw = MXL5005S_BANDWIDTH_6MHZ;
- break;
- default: /* Assume DVB-T */
- req_mode = MXL_DVBT;
- switch (bw) {
- case 6000000:
- req_bw = MXL5005S_BANDWIDTH_6MHZ;
- break;
- case 7000000:
- req_bw = MXL5005S_BANDWIDTH_7MHZ;
- break;
- case 8000000:
- case 0:
- req_bw = MXL5005S_BANDWIDTH_8MHZ;
- break;
- default:
- return -EINVAL;
- }
- }
-
- /* Change tuner for new modulation type if reqd */
- if (req_mode != state->current_mode ||
- req_bw != state->Chan_Bandwidth) {
- state->current_mode = req_mode;
- ret = mxl5005s_reconfigure(fe, req_mode, req_bw);
-
- } else
- ret = 0;
-
- if (ret == 0) {
- dprintk(1, "%s() freq=%d\n", __func__, c->frequency);
- ret = mxl5005s_SetRfFreqHz(fe, c->frequency);
- }
-
- return ret;
-}
-
-static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- *frequency = state->RF_IN;
-
- return 0;
-}
-
-static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
- struct mxl5005s_state *state = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- *bandwidth = state->Chan_Bandwidth;
-
- return 0;
-}
-
-static int mxl5005s_release(struct dvb_frontend *fe)
-{
- dprintk(1, "%s()\n", __func__);
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static const struct dvb_tuner_ops mxl5005s_tuner_ops = {
- .info = {
- .name = "MaxLinear MXL5005S",
- .frequency_min = 48000000,
- .frequency_max = 860000000,
- .frequency_step = 50000,
- },
-
- .release = mxl5005s_release,
- .init = mxl5005s_init,
-
- .set_params = mxl5005s_set_params,
- .get_frequency = mxl5005s_get_frequency,
- .get_bandwidth = mxl5005s_get_bandwidth,
-};
-
-struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct mxl5005s_config *config)
-{
- struct mxl5005s_state *state = NULL;
- dprintk(1, "%s()\n", __func__);
-
- state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL);
- if (state == NULL)
- return NULL;
-
- state->frontend = fe;
- state->config = config;
- state->i2c = i2c;
-
- printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n",
- config->i2c_address);
-
- memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = state;
- return fe;
-}
-EXPORT_SYMBOL(mxl5005s_attach);
-
-MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver");
-MODULE_AUTHOR("Steven Toth");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c
deleted file mode 100644
index 2d79b1f5d5eb..000000000000
--- a/drivers/media/common/tuners/qt1010.c
+++ /dev/null
@@ -1,480 +0,0 @@
-/*
- * Driver for Quantek QT1010 silicon tuner
- *
- * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
- * Aapo Tahkola <aet@rasterburn.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#include "qt1010.h"
-#include "qt1010_priv.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-#define dprintk(args...) \
- do { \
- if (debug) printk(KERN_DEBUG "QT1010: " args); \
- } while (0)
-
-/* read single register */
-static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val)
-{
- struct i2c_msg msg[2] = {
- { .addr = priv->cfg->i2c_address,
- .flags = 0, .buf = &reg, .len = 1 },
- { .addr = priv->cfg->i2c_address,
- .flags = I2C_M_RD, .buf = val, .len = 1 },
- };
-
- if (i2c_transfer(priv->i2c, msg, 2) != 2) {
- printk(KERN_WARNING "qt1010 I2C read failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-/* write single register */
-static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
-{
- u8 buf[2] = { reg, val };
- struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
- .flags = 0, .buf = buf, .len = 2 };
-
- if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
- printk(KERN_WARNING "qt1010 I2C write failed\n");
- return -EREMOTEIO;
- }
- return 0;
-}
-
-/* dump all registers */
-static void qt1010_dump_regs(struct qt1010_priv *priv)
-{
- u8 reg, val;
-
- for (reg = 0; ; reg++) {
- if (reg % 16 == 0) {
- if (reg)
- printk(KERN_CONT "\n");
- printk(KERN_DEBUG "%02x:", reg);
- }
- if (qt1010_readreg(priv, reg, &val) == 0)
- printk(KERN_CONT " %02x", val);
- else
- printk(KERN_CONT " --");
- if (reg == 0x2f)
- break;
- }
- printk(KERN_CONT "\n");
-}
-
-static int qt1010_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct qt1010_priv *priv;
- int err;
- u32 freq, div, mod1, mod2;
- u8 i, tmpval, reg05;
- qt1010_i2c_oper_t rd[48] = {
- { QT1010_WR, 0x01, 0x80 },
- { QT1010_WR, 0x02, 0x3f },
- { QT1010_WR, 0x05, 0xff }, /* 02 c write */
- { QT1010_WR, 0x06, 0x44 },
- { QT1010_WR, 0x07, 0xff }, /* 04 c write */
- { QT1010_WR, 0x08, 0x08 },
- { QT1010_WR, 0x09, 0xff }, /* 06 c write */
- { QT1010_WR, 0x0a, 0xff }, /* 07 c write */
- { QT1010_WR, 0x0b, 0xff }, /* 08 c write */
- { QT1010_WR, 0x0c, 0xe1 },
- { QT1010_WR, 0x1a, 0xff }, /* 10 c write */
- { QT1010_WR, 0x1b, 0x00 },
- { QT1010_WR, 0x1c, 0x89 },
- { QT1010_WR, 0x11, 0xff }, /* 13 c write */
- { QT1010_WR, 0x12, 0xff }, /* 14 c write */
- { QT1010_WR, 0x22, 0xff }, /* 15 c write */
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x1e, 0xd0 },
- { QT1010_RD, 0x22, 0xff }, /* 16 c read */
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_RD, 0x05, 0xff }, /* 20 c read */
- { QT1010_RD, 0x22, 0xff }, /* 21 c read */
- { QT1010_WR, 0x23, 0xd0 },
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x1e, 0xe0 },
- { QT1010_RD, 0x23, 0xff }, /* 25 c read */
- { QT1010_RD, 0x23, 0xff }, /* 26 c read */
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x24, 0xd0 },
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x1e, 0xf0 },
- { QT1010_RD, 0x24, 0xff }, /* 31 c read */
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x14, 0x7f },
- { QT1010_WR, 0x15, 0x7f },
- { QT1010_WR, 0x05, 0xff }, /* 35 c write */
- { QT1010_WR, 0x06, 0x00 },
- { QT1010_WR, 0x15, 0x1f },
- { QT1010_WR, 0x16, 0xff },
- { QT1010_WR, 0x18, 0xff },
- { QT1010_WR, 0x1f, 0xff }, /* 40 c write */
- { QT1010_WR, 0x20, 0xff }, /* 41 c write */
- { QT1010_WR, 0x21, 0x53 },
- { QT1010_WR, 0x25, 0xff }, /* 43 c write */
- { QT1010_WR, 0x26, 0x15 },
- { QT1010_WR, 0x00, 0xff }, /* 45 c write */
- { QT1010_WR, 0x02, 0x00 },
- { QT1010_WR, 0x01, 0x00 }
- };
-
-#define FREQ1 32000000 /* 32 MHz */
-#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */
-
- priv = fe->tuner_priv;
- freq = c->frequency;
- div = (freq + QT1010_OFFSET) / QT1010_STEP;
- freq = (div * QT1010_STEP) - QT1010_OFFSET;
- mod1 = (freq + QT1010_OFFSET) % FREQ1;
- mod2 = (freq + QT1010_OFFSET) % FREQ2;
- priv->frequency = freq;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- /* reg 05 base value */
- if (freq < 290000000) reg05 = 0x14; /* 290 MHz */
- else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */
- else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */
- else reg05 = 0x74;
-
- /* 0x5 */
- rd[2].val = reg05;
-
- /* 07 - set frequency: 32 MHz scale */
- rd[4].val = (freq + QT1010_OFFSET) / FREQ1;
-
- /* 09 - changes every 8/24 MHz */
- if (mod1 < 8000000) rd[6].val = 0x1d;
- else rd[6].val = 0x1c;
-
- /* 0a - set frequency: 4 MHz scale (max 28 MHz) */
- if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */
- else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */
- else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */
- else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */
- else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */
- else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */
- else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */
- else rd[7].val = 0x0a; /* +28 MHz */
-
- /* 0b - changes every 2/2 MHz */
- if (mod2 < 2000000) rd[8].val = 0x45;
- else rd[8].val = 0x44;
-
- /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/
- tmpval = 0x78; /* byte, overflows intentionally */
- rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08);
-
- /* 11 */
- rd[13].val = 0xfd; /* TODO: correct value calculation */
-
- /* 12 */
- rd[14].val = 0x91; /* TODO: correct value calculation */
-
- /* 22 */
- if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */
- else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */
- else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */
- else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */
- else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */
- else rd[15].val = 0xd0;
-
- /* 05 */
- rd[35].val = (reg05 & 0xf0);
-
- /* 1f */
- if (mod1 < 8000000) tmpval = 0x00;
- else if (mod1 < 12000000) tmpval = 0x01;
- else if (mod1 < 16000000) tmpval = 0x02;
- else if (mod1 < 24000000) tmpval = 0x03;
- else if (mod1 < 28000000) tmpval = 0x04;
- else tmpval = 0x05;
- rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval);
-
- /* 20 */
- if (mod1 < 8000000) tmpval = 0x00;
- else if (mod1 < 12000000) tmpval = 0x01;
- else if (mod1 < 20000000) tmpval = 0x02;
- else if (mod1 < 24000000) tmpval = 0x03;
- else if (mod1 < 28000000) tmpval = 0x04;
- else tmpval = 0x05;
- rd[41].val = (priv->reg20_init_val + 0x0d + tmpval);
-
- /* 25 */
- rd[43].val = priv->reg25_init_val;
-
- /* 00 */
- rd[45].val = 0x92; /* TODO: correct value calculation */
-
- dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \
- "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \
- "20:%02x 25:%02x 00:%02x", \
- freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \
- rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \
- rd[40].val, rd[41].val, rd[43].val, rd[45].val);
-
- for (i = 0; i < ARRAY_SIZE(rd); i++) {
- if (rd[i].oper == QT1010_WR) {
- err = qt1010_writereg(priv, rd[i].reg, rd[i].val);
- } else { /* read is required to proper locking */
- err = qt1010_readreg(priv, rd[i].reg, &tmpval);
- }
- if (err) return err;
- }
-
- if (debug)
- qt1010_dump_regs(priv);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- return 0;
-}
-
-static int qt1010_init_meas1(struct qt1010_priv *priv,
- u8 oper, u8 reg, u8 reg_init_val, u8 *retval)
-{
- u8 i, val1, val2;
- int err;
-
- qt1010_i2c_oper_t i2c_data[] = {
- { QT1010_WR, reg, reg_init_val },
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x1e, oper },
- { QT1010_RD, reg, 0xff }
- };
-
- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
- if (i2c_data[i].oper == QT1010_WR) {
- err = qt1010_writereg(priv, i2c_data[i].reg,
- i2c_data[i].val);
- } else {
- err = qt1010_readreg(priv, i2c_data[i].reg, &val2);
- }
- if (err) return err;
- }
-
- do {
- val1 = val2;
- err = qt1010_readreg(priv, reg, &val2);
- if (err) return err;
- dprintk("compare reg:%02x %02x %02x", reg, val1, val2);
- } while (val1 != val2);
- *retval = val1;
-
- return qt1010_writereg(priv, 0x1e, 0x00);
-}
-
-static u8 qt1010_init_meas2(struct qt1010_priv *priv,
- u8 reg_init_val, u8 *retval)
-{
- u8 i, val;
- int err;
- qt1010_i2c_oper_t i2c_data[] = {
- { QT1010_WR, 0x07, reg_init_val },
- { QT1010_WR, 0x22, 0xd0 },
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x1e, 0xd0 },
- { QT1010_RD, 0x22, 0xff },
- { QT1010_WR, 0x1e, 0x00 },
- { QT1010_WR, 0x22, 0xff }
- };
- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
- if (i2c_data[i].oper == QT1010_WR) {
- err = qt1010_writereg(priv, i2c_data[i].reg,
- i2c_data[i].val);
- } else {
- err = qt1010_readreg(priv, i2c_data[i].reg, &val);
- }
- if (err) return err;
- }
- *retval = val;
- return 0;
-}
-
-static int qt1010_init(struct dvb_frontend *fe)
-{
- struct qt1010_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int err = 0;
- u8 i, tmpval, *valptr = NULL;
-
- qt1010_i2c_oper_t i2c_data[] = {
- { QT1010_WR, 0x01, 0x80 },
- { QT1010_WR, 0x0d, 0x84 },
- { QT1010_WR, 0x0e, 0xb7 },
- { QT1010_WR, 0x2a, 0x23 },
- { QT1010_WR, 0x2c, 0xdc },
- { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */
- { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */
- { QT1010_WR, 0x2b, 0x70 },
- { QT1010_WR, 0x2a, 0x23 },
- { QT1010_M1, 0x26, 0x08 },
- { QT1010_M1, 0x82, 0xff },
- { QT1010_WR, 0x05, 0x14 },
- { QT1010_WR, 0x06, 0x44 },
- { QT1010_WR, 0x07, 0x28 },
- { QT1010_WR, 0x08, 0x0b },
- { QT1010_WR, 0x11, 0xfd },
- { QT1010_M1, 0x22, 0x0d },
- { QT1010_M1, 0xd0, 0xff },
- { QT1010_WR, 0x06, 0x40 },
- { QT1010_WR, 0x16, 0xf0 },
- { QT1010_WR, 0x02, 0x38 },
- { QT1010_WR, 0x03, 0x18 },
- { QT1010_WR, 0x20, 0xe0 },
- { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */
- { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */
- { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */
- { QT1010_WR, 0x03, 0x19 },
- { QT1010_WR, 0x02, 0x3f },
- { QT1010_WR, 0x21, 0x53 },
- { QT1010_RD, 0x21, 0xff },
- { QT1010_WR, 0x11, 0xfd },
- { QT1010_WR, 0x05, 0x34 },
- { QT1010_WR, 0x06, 0x44 },
- { QT1010_WR, 0x08, 0x08 }
- };
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
- switch (i2c_data[i].oper) {
- case QT1010_WR:
- err = qt1010_writereg(priv, i2c_data[i].reg,
- i2c_data[i].val);
- break;
- case QT1010_RD:
- if (i2c_data[i].val == 0x20)
- valptr = &priv->reg20_init_val;
- else
- valptr = &tmpval;
- err = qt1010_readreg(priv, i2c_data[i].reg, valptr);
- break;
- case QT1010_M1:
- if (i2c_data[i].val == 0x25)
- valptr = &priv->reg25_init_val;
- else if (i2c_data[i].val == 0x1f)
- valptr = &priv->reg1f_init_val;
- else
- valptr = &tmpval;
- err = qt1010_init_meas1(priv, i2c_data[i+1].reg,
- i2c_data[i].reg,
- i2c_data[i].val, valptr);
- i++;
- break;
- }
- if (err) return err;
- }
-
- for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */
- if ((err = qt1010_init_meas2(priv, i, &tmpval)))
- return err;
-
- c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */
- /* MSI Megasky 580 GL861 533000000 */
- return qt1010_set_params(fe);
-}
-
-static int qt1010_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct qt1010_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- *frequency = 36125000;
- return 0;
-}
-
-static const struct dvb_tuner_ops qt1010_tuner_ops = {
- .info = {
- .name = "Quantek QT1010",
- .frequency_min = QT1010_MIN_FREQ,
- .frequency_max = QT1010_MAX_FREQ,
- .frequency_step = QT1010_STEP,
- },
-
- .release = qt1010_release,
- .init = qt1010_init,
- /* TODO: implement sleep */
-
- .set_params = qt1010_set_params,
- .get_frequency = qt1010_get_frequency,
- .get_if_frequency = qt1010_get_if_frequency,
-};
-
-struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct qt1010_config *cfg)
-{
- struct qt1010_priv *priv = NULL;
- u8 id;
-
- priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
-
-
- /* Try to detect tuner chip. Probably this is not correct register. */
- if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) {
- kfree(priv);
- return NULL;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
-
- printk(KERN_INFO "Quantek QT1010 successfully identified.\n");
- memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = priv;
- return fe;
-}
-EXPORT_SYMBOL(qt1010_attach);
-
-MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
-MODULE_VERSION("0.1");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c
deleted file mode 100644
index 602c2e392b17..000000000000
--- a/drivers/media/common/tuners/tda18212.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * NXP TDA18212HN silicon tuner driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "tda18212.h"
-
-struct tda18212_priv {
- struct tda18212_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 if_frequency;
-};
-
-#define dbg(fmt, arg...) \
-do { \
- if (debug) \
- pr_info("%s: " fmt, __func__, ##arg); \
-} while (0)
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-/* write multiple registers */
-static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len+1];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n",
- ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg->i2c_address,
- .flags = I2C_M_RD,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, buf, len);
- ret = 0;
- } else {
- pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n",
- ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write single register */
-static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
-{
- return tda18212_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
-{
- return tda18212_rd_regs(priv, reg, val, 1);
-}
-
-#if 0 /* keep, useful when developing driver */
-static void tda18212_dump_regs(struct tda18212_priv *priv)
-{
- int i;
- u8 buf[256];
-
- #define TDA18212_RD_LEN 32
- for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
- tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
-
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
- sizeof(buf), true);
-
- return;
-}
-#endif
-
-static int tda18212_set_params(struct dvb_frontend *fe)
-{
- struct tda18212_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u32 if_khz;
- u8 buf[9];
- #define DVBT_6 0
- #define DVBT_7 1
- #define DVBT_8 2
- #define DVBT2_6 3
- #define DVBT2_7 4
- #define DVBT2_8 5
- #define DVBC_6 6
- #define DVBC_8 7
- static const u8 bw_params[][3] = {
- /* reg: 0f 13 23 */
- [DVBT_6] = { 0xb3, 0x20, 0x03 },
- [DVBT_7] = { 0xb3, 0x31, 0x01 },
- [DVBT_8] = { 0xb3, 0x22, 0x01 },
- [DVBT2_6] = { 0xbc, 0x20, 0x03 },
- [DVBT2_7] = { 0xbc, 0x72, 0x03 },
- [DVBT2_8] = { 0xbc, 0x22, 0x01 },
- [DVBC_6] = { 0x92, 0x50, 0x03 },
- [DVBC_8] = { 0x92, 0x53, 0x03 },
- };
-
- dbg("delsys=%d RF=%d BW=%d\n",
- c->delivery_system, c->frequency, c->bandwidth_hz);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- switch (c->bandwidth_hz) {
- case 6000000:
- if_khz = priv->cfg->if_dvbt_6;
- i = DVBT_6;
- break;
- case 7000000:
- if_khz = priv->cfg->if_dvbt_7;
- i = DVBT_7;
- break;
- case 8000000:
- if_khz = priv->cfg->if_dvbt_8;
- i = DVBT_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
- break;
- case SYS_DVBT2:
- switch (c->bandwidth_hz) {
- case 6000000:
- if_khz = priv->cfg->if_dvbt2_6;
- i = DVBT2_6;
- break;
- case 7000000:
- if_khz = priv->cfg->if_dvbt2_7;
- i = DVBT2_7;
- break;
- case 8000000:
- if_khz = priv->cfg->if_dvbt2_8;
- i = DVBT2_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
- break;
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- if_khz = priv->cfg->if_dvbc;
- i = DVBC_8;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
-
- ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
- if (ret)
- goto error;
-
- ret = tda18212_wr_reg(priv, 0x06, 0x00);
- if (ret)
- goto error;
-
- ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
- if (ret)
- goto error;
-
- buf[0] = 0x02;
- buf[1] = bw_params[i][1];
- buf[2] = 0x03; /* default value */
- buf[3] = DIV_ROUND_CLOSEST(if_khz, 50);
- buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
- buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
- buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
- buf[7] = 0xc1;
- buf[8] = 0x01;
- ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
- if (ret)
- goto error;
-
- /* actual IF rounded as it is on register */
- priv->if_frequency = buf[3] * 50 * 1000;
-
-exit:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- return ret;
-
-error:
- dbg("failed:%d\n", ret);
- goto exit;
-}
-
-static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda18212_priv *priv = fe->tuner_priv;
-
- *frequency = priv->if_frequency;
-
- return 0;
-}
-
-static int tda18212_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static const struct dvb_tuner_ops tda18212_tuner_ops = {
- .info = {
- .name = "NXP TDA18212",
-
- .frequency_min = 48000000,
- .frequency_max = 864000000,
- .frequency_step = 1000,
- },
-
- .release = tda18212_release,
-
- .set_params = tda18212_set_params,
- .get_if_frequency = tda18212_get_if_frequency,
-};
-
-struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18212_config *cfg)
-{
- struct tda18212_priv *priv = NULL;
- int ret;
- u8 val;
-
- priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
- fe->tuner_priv = priv;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- /* check if the tuner is there */
- ret = tda18212_rd_reg(priv, 0x00, &val);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- dbg("ret:%d chip ID:%02x\n", ret, val);
- if (ret || val != 0xc7) {
- kfree(priv);
- return NULL;
- }
-
- pr_info("NXP TDA18212HN successfully identified\n");
-
- memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- return fe;
-}
-EXPORT_SYMBOL(tda18212_attach);
-
-MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18218.c b/drivers/media/common/tuners/tda18218.c
deleted file mode 100644
index dfb3a831df45..000000000000
--- a/drivers/media/common/tuners/tda18218.c
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * NXP TDA18218HN silicon tuner driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "tda18218.h"
-#include "tda18218_priv.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-/* write multiple registers */
-static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
-{
- int ret = 0;
- u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .buf = buf,
- }
- };
-
- msg_len_max = priv->cfg->i2c_wr_max - 1;
- quotient = len / msg_len_max;
- remainder = len % msg_len_max;
- msg_len = msg_len_max;
- for (i = 0; (i <= quotient && remainder); i++) {
- if (i == quotient) /* set len of the last msg */
- msg_len = remainder;
-
- msg[0].len = msg_len + 1;
- buf[0] = reg + i * msg_len_max;
- memcpy(&buf[1], &val[i * msg_len_max], msg_len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret != 1)
- break;
- }
-
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* read multiple registers */
-static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
-{
- int ret;
- u8 buf[reg+len]; /* we must start read always from reg 0x00 */
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg->i2c_address,
- .flags = 0,
- .len = 1,
- .buf = "\x00",
- }, {
- .addr = priv->cfg->i2c_address,
- .flags = I2C_M_RD,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, &buf[reg], len);
- ret = 0;
- } else {
- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write single register */
-static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
-{
- return tda18218_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-
-static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
-{
- return tda18218_rd_regs(priv, reg, val, 1);
-}
-
-static int tda18218_set_params(struct dvb_frontend *fe)
-{
- struct tda18218_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 bw = c->bandwidth_hz;
- int ret;
- u8 buf[3], i, BP_Filter, LP_Fc;
- u32 LO_Frac;
- /* TODO: find out correct AGC algorithm */
- u8 agc[][2] = {
- { R20_AGC11, 0x60 },
- { R23_AGC21, 0x02 },
- { R20_AGC11, 0xa0 },
- { R23_AGC21, 0x09 },
- { R20_AGC11, 0xe0 },
- { R23_AGC21, 0x0c },
- { R20_AGC11, 0x40 },
- { R23_AGC21, 0x01 },
- { R20_AGC11, 0x80 },
- { R23_AGC21, 0x08 },
- { R20_AGC11, 0xc0 },
- { R23_AGC21, 0x0b },
- { R24_AGC22, 0x1c },
- { R24_AGC22, 0x0c },
- };
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- /* low-pass filter cut-off frequency */
- if (bw <= 6000000) {
- LP_Fc = 0;
- priv->if_frequency = 3000000;
- } else if (bw <= 7000000) {
- LP_Fc = 1;
- priv->if_frequency = 3500000;
- } else {
- LP_Fc = 2;
- priv->if_frequency = 4000000;
- }
-
- LO_Frac = c->frequency + priv->if_frequency;
-
- /* band-pass filter */
- if (LO_Frac < 188000000)
- BP_Filter = 3;
- else if (LO_Frac < 253000000)
- BP_Filter = 4;
- else if (LO_Frac < 343000000)
- BP_Filter = 5;
- else
- BP_Filter = 6;
-
- buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
- buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
- buf[2] = priv->regs[R1C_AGC2B];
- ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
- if (ret)
- goto error;
-
- buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
- buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
- buf[2] = (LO_Frac / 1000) << 4 |
- (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
- ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
- if (ret)
- goto error;
-
- buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
- ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
- if (ret)
- goto error;
-
- buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
- ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
- if (ret)
- goto error;
-
- /* trigger AGC */
- for (i = 0; i < ARRAY_SIZE(agc); i++) {
- ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
- if (ret)
- goto error;
- }
-
-error:
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- if (ret)
- dbg("%s: failed ret:%d", __func__, ret);
-
- return ret;
-}
-
-static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda18218_priv *priv = fe->tuner_priv;
- *frequency = priv->if_frequency;
- dbg("%s: if=%d", __func__, *frequency);
- return 0;
-}
-
-static int tda18218_sleep(struct dvb_frontend *fe)
-{
- struct tda18218_priv *priv = fe->tuner_priv;
- int ret;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- /* standby */
- ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- if (ret)
- dbg("%s: failed ret:%d", __func__, ret);
-
- return ret;
-}
-
-static int tda18218_init(struct dvb_frontend *fe)
-{
- struct tda18218_priv *priv = fe->tuner_priv;
- int ret;
-
- /* TODO: calibrations */
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- if (ret)
- dbg("%s: failed ret:%d", __func__, ret);
-
- return ret;
-}
-
-static int tda18218_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static const struct dvb_tuner_ops tda18218_tuner_ops = {
- .info = {
- .name = "NXP TDA18218",
-
- .frequency_min = 174000000,
- .frequency_max = 864000000,
- .frequency_step = 1000,
- },
-
- .release = tda18218_release,
- .init = tda18218_init,
- .sleep = tda18218_sleep,
-
- .set_params = tda18218_set_params,
-
- .get_if_frequency = tda18218_get_if_frequency,
-};
-
-struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tda18218_config *cfg)
-{
- struct tda18218_priv *priv = NULL;
- u8 val;
- int ret;
- /* chip default registers values */
- static u8 def_regs[] = {
- 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
- 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
- 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
- 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
- 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
- 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
- };
-
- priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
- fe->tuner_priv = priv;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
-
- /* check if the tuner is there */
- ret = tda18218_rd_reg(priv, R00_ID, &val);
- dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);
- if (ret || val != def_regs[R00_ID]) {
- kfree(priv);
- return NULL;
- }
-
- info("NXP TDA18218HN successfully identified.");
-
- memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
- sizeof(struct dvb_tuner_ops));
- memcpy(priv->regs, def_regs, sizeof(def_regs));
-
- /* loop-through enabled chip default register values */
- if (priv->cfg->loop_through) {
- priv->regs[R17_PD1] = 0xb0;
- priv->regs[R18_PD2] = 0x59;
- }
-
- /* standby */
- ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
- if (ret)
- dbg("%s: failed ret:%d", __func__, ret);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
-
- return fe;
-}
-EXPORT_SYMBOL(tda18218_attach);
-
-MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18218_priv.h b/drivers/media/common/tuners/tda18218_priv.h
deleted file mode 100644
index dc52b72e1407..000000000000
--- a/drivers/media/common/tuners/tda18218_priv.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * NXP TDA18218HN silicon tuner driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 TDA18218_PRIV_H
-#define TDA18218_PRIV_H
-
-#define LOG_PREFIX "tda18218"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (debug) \
- printk(KERN_DEBUG LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-#define R00_ID 0x00 /* ID byte */
-#define R01_R1 0x01 /* Read byte 1 */
-#define R02_R2 0x02 /* Read byte 2 */
-#define R03_R3 0x03 /* Read byte 3 */
-#define R04_R4 0x04 /* Read byte 4 */
-#define R05_R5 0x05 /* Read byte 5 */
-#define R06_R6 0x06 /* Read byte 6 */
-#define R07_MD1 0x07 /* Main divider byte 1 */
-#define R08_PSM1 0x08 /* PSM byte 1 */
-#define R09_MD2 0x09 /* Main divider byte 2 */
-#define R0A_MD3 0x0a /* Main divider byte 1 */
-#define R0B_MD4 0x0b /* Main divider byte 4 */
-#define R0C_MD5 0x0c /* Main divider byte 5 */
-#define R0D_MD6 0x0d /* Main divider byte 6 */
-#define R0E_MD7 0x0e /* Main divider byte 7 */
-#define R0F_MD8 0x0f /* Main divider byte 8 */
-#define R10_CD1 0x10 /* Call divider byte 1 */
-#define R11_CD2 0x11 /* Call divider byte 2 */
-#define R12_CD3 0x12 /* Call divider byte 3 */
-#define R13_CD4 0x13 /* Call divider byte 4 */
-#define R14_CD5 0x14 /* Call divider byte 5 */
-#define R15_CD6 0x15 /* Call divider byte 6 */
-#define R16_CD7 0x16 /* Call divider byte 7 */
-#define R17_PD1 0x17 /* Power-down byte 1 */
-#define R18_PD2 0x18 /* Power-down byte 2 */
-#define R19_XTOUT 0x19 /* XTOUT byte */
-#define R1A_IF1 0x1a /* IF byte 1 */
-#define R1B_IF2 0x1b /* IF byte 2 */
-#define R1C_AGC2B 0x1c /* AGC2b byte */
-#define R1D_PSM2 0x1d /* PSM byte 2 */
-#define R1E_PSM3 0x1e /* PSM byte 3 */
-#define R1F_PSM4 0x1f /* PSM byte 4 */
-#define R20_AGC11 0x20 /* AGC1 byte 1 */
-#define R21_AGC12 0x21 /* AGC1 byte 2 */
-#define R22_AGC13 0x22 /* AGC1 byte 3 */
-#define R23_AGC21 0x23 /* AGC2 byte 1 */
-#define R24_AGC22 0x24 /* AGC2 byte 2 */
-#define R25_AAGC 0x25 /* Analog AGC byte */
-#define R26_RC 0x26 /* RC byte */
-#define R27_RSSI 0x27 /* RSSI byte */
-#define R28_IRCAL1 0x28 /* IR CAL byte 1 */
-#define R29_IRCAL2 0x29 /* IR CAL byte 2 */
-#define R2A_IRCAL3 0x2a /* IR CAL byte 3 */
-#define R2B_IRCAL4 0x2b /* IR CAL byte 4 */
-#define R2C_RFCAL1 0x2c /* RF CAL byte 1 */
-#define R2D_RFCAL2 0x2d /* RF CAL byte 2 */
-#define R2E_RFCAL3 0x2e /* RF CAL byte 3 */
-#define R2F_RFCAL4 0x2f /* RF CAL byte 4 */
-#define R30_RFCAL5 0x30 /* RF CAL byte 5 */
-#define R31_RFCAL6 0x31 /* RF CAL byte 6 */
-#define R32_RFCAL7 0x32 /* RF CAL byte 7 */
-#define R33_RFCAL8 0x33 /* RF CAL byte 8 */
-#define R34_RFCAL9 0x34 /* RF CAL byte 9 */
-#define R35_RFCAL10 0x35 /* RF CAL byte 10 */
-#define R36_RFCALRAM1 0x36 /* RF CAL RAM byte 1 */
-#define R37_RFCALRAM2 0x37 /* RF CAL RAM byte 2 */
-#define R38_MARGIN 0x38 /* Margin byte */
-#define R39_FMAX1 0x39 /* Fmax byte 1 */
-#define R3A_FMAX2 0x3a /* Fmax byte 2 */
-
-#define TDA18218_NUM_REGS 59
-
-struct tda18218_priv {
- struct tda18218_config *cfg;
- struct i2c_adapter *i2c;
-
- u32 if_frequency;
-
- u8 regs[TDA18218_NUM_REGS];
-};
-
-#endif
diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
deleted file mode 100644
index 39c645787b62..000000000000
--- a/drivers/media/common/tuners/tda18271-common.c
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner
-
- Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "tda18271-priv.h"
-
-static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- enum tda18271_i2c_gate gate;
- int ret = 0;
-
- switch (priv->gate) {
- case TDA18271_GATE_DIGITAL:
- case TDA18271_GATE_ANALOG:
- gate = priv->gate;
- break;
- case TDA18271_GATE_AUTO:
- default:
- switch (priv->mode) {
- case TDA18271_DIGITAL:
- gate = TDA18271_GATE_DIGITAL;
- break;
- case TDA18271_ANALOG:
- default:
- gate = TDA18271_GATE_ANALOG;
- break;
- }
- }
-
- switch (gate) {
- case TDA18271_GATE_ANALOG:
- if (fe->ops.analog_ops.i2c_gate_ctrl)
- ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable);
- break;
- case TDA18271_GATE_DIGITAL:
- if (fe->ops.i2c_gate_ctrl)
- ret = fe->ops.i2c_gate_ctrl(fe, enable);
- break;
- default:
- ret = -EINVAL;
- break;
- }
-
- return ret;
-};
-
-/*---------------------------------------------------------------------*/
-
-static void tda18271_dump_regs(struct dvb_frontend *fe, int extended)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
-
- tda_reg("=== TDA18271 REG DUMP ===\n");
- tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]);
- tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]);
- tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]);
- tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]);
- tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]);
- tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]);
- tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]);
- tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]);
- tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]);
- tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]);
- tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]);
- tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]);
- tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]);
- tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]);
- tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]);
- tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]);
-
- /* only dump extended regs if DBG_ADV is set */
- if (!(tda18271_debug & DBG_ADV))
- return;
-
- /* W indicates write-only registers.
- * Register dump for write-only registers shows last value written. */
-
- tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]);
- tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]);
- tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]);
- tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]);
- tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]);
- tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]);
- tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]);
- tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]);
- tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]);
- tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]);
- tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]);
- tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]);
- tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]);
- tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]);
- tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]);
- tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]);
- tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]);
- tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]);
- tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]);
- tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]);
- tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]);
- tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]);
- tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]);
-}
-
-int tda18271_read_regs(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- unsigned char buf = 0x00;
- int ret;
- struct i2c_msg msg[] = {
- { .addr = priv->i2c_props.addr, .flags = 0,
- .buf = &buf, .len = 1 },
- { .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
- .buf = regs, .len = 16 }
- };
-
- tda18271_i2c_gate_ctrl(fe, 1);
-
- /* read all registers */
- ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
-
- tda18271_i2c_gate_ctrl(fe, 0);
-
- if (ret != 2)
- tda_err("ERROR: i2c_transfer returned: %d\n", ret);
-
- if (tda18271_debug & DBG_REG)
- tda18271_dump_regs(fe, 0);
-
- return (ret == 2 ? 0 : ret);
-}
-
-int tda18271_read_extended(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- unsigned char regdump[TDA18271_NUM_REGS];
- unsigned char buf = 0x00;
- int ret, i;
- struct i2c_msg msg[] = {
- { .addr = priv->i2c_props.addr, .flags = 0,
- .buf = &buf, .len = 1 },
- { .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
- .buf = regdump, .len = TDA18271_NUM_REGS }
- };
-
- tda18271_i2c_gate_ctrl(fe, 1);
-
- /* read all registers */
- ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
-
- tda18271_i2c_gate_ctrl(fe, 0);
-
- if (ret != 2)
- tda_err("ERROR: i2c_transfer returned: %d\n", ret);
-
- for (i = 0; i < TDA18271_NUM_REGS; i++) {
- /* don't update write-only registers */
- if ((i != R_EB9) &&
- (i != R_EB16) &&
- (i != R_EB17) &&
- (i != R_EB19) &&
- (i != R_EB20))
- regs[i] = regdump[i];
- }
-
- if (tda18271_debug & DBG_REG)
- tda18271_dump_regs(fe, 1);
-
- return (ret == 2 ? 0 : ret);
-}
-
-int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- unsigned char buf[TDA18271_NUM_REGS + 1];
- struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0,
- .buf = buf };
- int i, ret = 1, max;
-
- BUG_ON((len == 0) || (idx + len > sizeof(buf)));
-
-
- switch (priv->small_i2c) {
- case TDA18271_03_BYTE_CHUNK_INIT:
- max = 3;
- break;
- case TDA18271_08_BYTE_CHUNK_INIT:
- max = 8;
- break;
- case TDA18271_16_BYTE_CHUNK_INIT:
- max = 16;
- break;
- case TDA18271_39_BYTE_CHUNK_INIT:
- default:
- max = 39;
- }
-
- tda18271_i2c_gate_ctrl(fe, 1);
- while (len) {
- if (max > len)
- max = len;
-
- buf[0] = idx;
- for (i = 1; i <= max; i++)
- buf[i] = regs[idx - 1 + i];
-
- msg.len = max + 1;
-
- /* write registers */
- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
- if (ret != 1)
- break;
-
- idx += max;
- len -= max;
- }
- tda18271_i2c_gate_ctrl(fe, 0);
-
- if (ret != 1)
- tda_err("ERROR: idx = 0x%x, len = %d, "
- "i2c_transfer returned: %d\n", idx, max, ret);
-
- return (ret == 1 ? 0 : ret);
-}
-
-/*---------------------------------------------------------------------*/
-
-int tda18271_charge_pump_source(struct dvb_frontend *fe,
- enum tda18271_pll pll, int force)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
-
- int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4;
-
- regs[r_cp] &= ~0x20;
- regs[r_cp] |= ((force & 1) << 5);
-
- return tda18271_write_regs(fe, r_cp, 1);
-}
-
-int tda18271_init_regs(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
-
- tda_dbg("initializing registers for device @ %d-%04x\n",
- i2c_adapter_id(priv->i2c_props.adap),
- priv->i2c_props.addr);
-
- /* initialize registers */
- switch (priv->id) {
- case TDA18271HDC1:
- regs[R_ID] = 0x83;
- break;
- case TDA18271HDC2:
- regs[R_ID] = 0x84;
- break;
- };
-
- regs[R_TM] = 0x08;
- regs[R_PL] = 0x80;
- regs[R_EP1] = 0xc6;
- regs[R_EP2] = 0xdf;
- regs[R_EP3] = 0x16;
- regs[R_EP4] = 0x60;
- regs[R_EP5] = 0x80;
- regs[R_CPD] = 0x80;
- regs[R_CD1] = 0x00;
- regs[R_CD2] = 0x00;
- regs[R_CD3] = 0x00;
- regs[R_MPD] = 0x00;
- regs[R_MD1] = 0x00;
- regs[R_MD2] = 0x00;
- regs[R_MD3] = 0x00;
-
- switch (priv->id) {
- case TDA18271HDC1:
- regs[R_EB1] = 0xff;
- break;
- case TDA18271HDC2:
- regs[R_EB1] = 0xfc;
- break;
- };
-
- regs[R_EB2] = 0x01;
- regs[R_EB3] = 0x84;
- regs[R_EB4] = 0x41;
- regs[R_EB5] = 0x01;
- regs[R_EB6] = 0x84;
- regs[R_EB7] = 0x40;
- regs[R_EB8] = 0x07;
- regs[R_EB9] = 0x00;
- regs[R_EB10] = 0x00;
- regs[R_EB11] = 0x96;
-
- switch (priv->id) {
- case TDA18271HDC1:
- regs[R_EB12] = 0x0f;
- break;
- case TDA18271HDC2:
- regs[R_EB12] = 0x33;
- break;
- };
-
- regs[R_EB13] = 0xc1;
- regs[R_EB14] = 0x00;
- regs[R_EB15] = 0x8f;
- regs[R_EB16] = 0x00;
- regs[R_EB17] = 0x00;
-
- switch (priv->id) {
- case TDA18271HDC1:
- regs[R_EB18] = 0x00;
- break;
- case TDA18271HDC2:
- regs[R_EB18] = 0x8c;
- break;
- };
-
- regs[R_EB19] = 0x00;
- regs[R_EB20] = 0x20;
-
- switch (priv->id) {
- case TDA18271HDC1:
- regs[R_EB21] = 0x33;
- break;
- case TDA18271HDC2:
- regs[R_EB21] = 0xb3;
- break;
- };
-
- regs[R_EB22] = 0x48;
- regs[R_EB23] = 0xb0;
-
- tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
-
- /* setup agc1 gain */
- regs[R_EB17] = 0x00;
- tda18271_write_regs(fe, R_EB17, 1);
- regs[R_EB17] = 0x03;
- tda18271_write_regs(fe, R_EB17, 1);
- regs[R_EB17] = 0x43;
- tda18271_write_regs(fe, R_EB17, 1);
- regs[R_EB17] = 0x4c;
- tda18271_write_regs(fe, R_EB17, 1);
-
- /* setup agc2 gain */
- if ((priv->id) == TDA18271HDC1) {
- regs[R_EB20] = 0xa0;
- tda18271_write_regs(fe, R_EB20, 1);
- regs[R_EB20] = 0xa7;
- tda18271_write_regs(fe, R_EB20, 1);
- regs[R_EB20] = 0xe7;
- tda18271_write_regs(fe, R_EB20, 1);
- regs[R_EB20] = 0xec;
- tda18271_write_regs(fe, R_EB20, 1);
- }
-
- /* image rejection calibration */
-
- /* low-band */
- regs[R_EP3] = 0x1f;
- regs[R_EP4] = 0x66;
- regs[R_EP5] = 0x81;
- regs[R_CPD] = 0xcc;
- regs[R_CD1] = 0x6c;
- regs[R_CD2] = 0x00;
- regs[R_CD3] = 0x00;
- regs[R_MPD] = 0xcd;
- regs[R_MD1] = 0x77;
- regs[R_MD2] = 0x08;
- regs[R_MD3] = 0x00;
-
- tda18271_write_regs(fe, R_EP3, 11);
-
- if ((priv->id) == TDA18271HDC2) {
- /* main pll cp source on */
- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
- msleep(1);
-
- /* main pll cp source off */
- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
- }
-
- msleep(5); /* pll locking */
-
- /* launch detector */
- tda18271_write_regs(fe, R_EP1, 1);
- msleep(5); /* wanted low measurement */
-
- regs[R_EP5] = 0x85;
- regs[R_CPD] = 0xcb;
- regs[R_CD1] = 0x66;
- regs[R_CD2] = 0x70;
-
- tda18271_write_regs(fe, R_EP3, 7);
- msleep(5); /* pll locking */
-
- /* launch optimization algorithm */
- tda18271_write_regs(fe, R_EP2, 1);
- msleep(30); /* image low optimization completion */
-
- /* mid-band */
- regs[R_EP5] = 0x82;
- regs[R_CPD] = 0xa8;
- regs[R_CD2] = 0x00;
- regs[R_MPD] = 0xa9;
- regs[R_MD1] = 0x73;
- regs[R_MD2] = 0x1a;
-
- tda18271_write_regs(fe, R_EP3, 11);
- msleep(5); /* pll locking */
-
- /* launch detector */
- tda18271_write_regs(fe, R_EP1, 1);
- msleep(5); /* wanted mid measurement */
-
- regs[R_EP5] = 0x86;
- regs[R_CPD] = 0xa8;
- regs[R_CD1] = 0x66;
- regs[R_CD2] = 0xa0;
-
- tda18271_write_regs(fe, R_EP3, 7);
- msleep(5); /* pll locking */
-
- /* launch optimization algorithm */
- tda18271_write_regs(fe, R_EP2, 1);
- msleep(30); /* image mid optimization completion */
-
- /* high-band */
- regs[R_EP5] = 0x83;
- regs[R_CPD] = 0x98;
- regs[R_CD1] = 0x65;
- regs[R_CD2] = 0x00;
- regs[R_MPD] = 0x99;
- regs[R_MD1] = 0x71;
- regs[R_MD2] = 0xcd;
-
- tda18271_write_regs(fe, R_EP3, 11);
- msleep(5); /* pll locking */
-
- /* launch detector */
- tda18271_write_regs(fe, R_EP1, 1);
- msleep(5); /* wanted high measurement */
-
- regs[R_EP5] = 0x87;
- regs[R_CD1] = 0x65;
- regs[R_CD2] = 0x50;
-
- tda18271_write_regs(fe, R_EP3, 7);
- msleep(5); /* pll locking */
-
- /* launch optimization algorithm */
- tda18271_write_regs(fe, R_EP2, 1);
- msleep(30); /* image high optimization completion */
-
- /* return to normal mode */
- regs[R_EP4] = 0x64;
- tda18271_write_regs(fe, R_EP4, 1);
-
- /* synchronize */
- tda18271_write_regs(fe, R_EP1, 1);
-
- return 0;
-}
-
-/*---------------------------------------------------------------------*/
-
-/*
- * Standby modes, EP3 [7:5]
- *
- * | SM || SM_LT || SM_XT || mode description
- * |=====\\=======\\=======\\===================================
- * | 0 || 0 || 0 || normal mode
- * |-----||-------||-------||-----------------------------------
- * | || || || standby mode w/ slave tuner output
- * | 1 || 0 || 0 || & loop thru & xtal oscillator on
- * |-----||-------||-------||-----------------------------------
- * | 1 || 1 || 0 || standby mode w/ xtal oscillator on
- * |-----||-------||-------||-----------------------------------
- * | 1 || 1 || 1 || power off
- *
- */
-
-int tda18271_set_standby_mode(struct dvb_frontend *fe,
- int sm, int sm_lt, int sm_xt)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
-
- if (tda18271_debug & DBG_ADV)
- tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt);
-
- regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */
- regs[R_EP3] |= (sm ? (1 << 7) : 0) |
- (sm_lt ? (1 << 6) : 0) |
- (sm_xt ? (1 << 5) : 0);
-
- return tda18271_write_regs(fe, R_EP3, 1);
-}
-
-/*---------------------------------------------------------------------*/
-
-int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
-{
- /* sets main post divider & divider bytes, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 d, pd;
- u32 div;
-
- int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_MPD] = (0x7f & pd);
-
- div = ((d * (freq / 1000)) << 7) / 125;
-
- regs[R_MD1] = 0x7f & (div >> 16);
- regs[R_MD2] = 0xff & (div >> 8);
- regs[R_MD3] = 0xff & div;
-fail:
- return ret;
-}
-
-int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq)
-{
- /* sets cal post divider & divider bytes, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 d, pd;
- u32 div;
-
- int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_CPD] = pd;
-
- div = ((d * (freq / 1000)) << 7) / 125;
-
- regs[R_CD1] = 0x7f & (div >> 16);
- regs[R_CD2] = 0xff & (div >> 8);
- regs[R_CD3] = 0xff & div;
-fail:
- return ret;
-}
-
-/*---------------------------------------------------------------------*/
-
-int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets bp filter bits, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EP1] &= ~0x07; /* clear bp filter bits */
- regs[R_EP1] |= (0x07 & val);
-fail:
- return ret;
-}
-
-int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets K & M bits, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EB13] &= ~0x7c; /* clear k & m bits */
- regs[R_EB13] |= (0x7c & val);
-fail:
- return ret;
-}
-
-int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets rf band bits, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EP2] &= ~0xe0; /* clear rf band bits */
- regs[R_EP2] |= (0xe0 & (val << 5));
-fail:
- return ret;
-}
-
-int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets gain taper bits, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EP2] &= ~0x1f; /* clear gain taper bits */
- regs[R_EP2] |= (0x1f & val);
-fail:
- return ret;
-}
-
-int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets IR Meas bits, but does not write them */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EP5] &= ~0x07;
- regs[R_EP5] |= (0x07 & val);
-fail:
- return ret;
-}
-
-int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq)
-{
- /* sets rf cal byte (RFC_Cprog), but does not write it */
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u8 val;
-
- int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val);
- /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range
- * for frequencies above 61.1 MHz. In these cases, the internal RF
- * tracking filters calibration mechanism is used.
- *
- * There is no need to warn the user about this.
- */
- if (ret < 0)
- goto fail;
-
- regs[R_EB14] = val;
-fail:
- return ret;
-}
-
-int _tda_printk(struct tda18271_priv *state, const char *level,
- const char *func, const char *fmt, ...)
-{
- struct va_format vaf;
- va_list args;
- int rtn;
-
- va_start(args, fmt);
-
- vaf.fmt = fmt;
- vaf.va = &args;
-
- if (state)
- rtn = printk("%s%s: [%d-%04x|%c] %pV",
- level, func, i2c_adapter_id(state->i2c_props.adap),
- state->i2c_props.addr,
- (state->role == TDA18271_MASTER) ? 'M' : 'S',
- &vaf);
- else
- rtn = printk("%s%s: %pV", level, func, &vaf);
-
- va_end(args);
-
- return rtn;
-}
diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
deleted file mode 100644
index 2e67f4459904..000000000000
--- a/drivers/media/common/tuners/tda18271-fe.c
+++ /dev/null
@@ -1,1345 +0,0 @@
-/*
- tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner
-
- Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include "tda18271-priv.h"
-
-int tda18271_debug;
-module_param_named(debug, tda18271_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debug level "
- "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
-
-static int tda18271_cal_on_startup = -1;
-module_param_named(cal, tda18271_cal_on_startup, int, 0644);
-MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
-
-static DEFINE_MUTEX(tda18271_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-/*---------------------------------------------------------------------*/
-
-static int tda18271_toggle_output(struct dvb_frontend *fe, int standby)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
-
- int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0,
- priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0,
- priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0);
-
- if (tda_fail(ret))
- goto fail;
-
- tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n",
- standby ? "standby" : "active",
- priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",
- priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");
-fail:
- return ret;
-}
-
-/*---------------------------------------------------------------------*/
-
-static inline int charge_pump_source(struct dvb_frontend *fe, int force)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- return tda18271_charge_pump_source(fe,
- (priv->role == TDA18271_SLAVE) ?
- TDA18271_CAL_PLL :
- TDA18271_MAIN_PLL, force);
-}
-
-static inline void tda18271_set_if_notch(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
-
- switch (priv->mode) {
- case TDA18271_ANALOG:
- regs[R_MPD] &= ~0x80; /* IF notch = 0 */
- break;
- case TDA18271_DIGITAL:
- regs[R_MPD] |= 0x80; /* IF notch = 1 */
- break;
- }
-}
-
-static int tda18271_channel_configuration(struct dvb_frontend *fe,
- struct tda18271_std_map_item *map,
- u32 freq, u32 bw)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
- u32 N;
-
- /* update TV broadcast parameters */
-
- /* set standard */
- regs[R_EP3] &= ~0x1f; /* clear std bits */
- regs[R_EP3] |= (map->agc_mode << 3) | map->std;
-
- if (priv->id == TDA18271HDC2) {
- /* set rfagc to high speed mode */
- regs[R_EP3] &= ~0x04;
- }
-
- /* set cal mode to normal */
- regs[R_EP4] &= ~0x03;
-
- /* update IF output level */
- regs[R_EP4] &= ~0x1c; /* clear if level bits */
- regs[R_EP4] |= (map->if_lvl << 2);
-
- /* update FM_RFn */
- regs[R_EP4] &= ~0x80;
- regs[R_EP4] |= map->fm_rfn << 7;
-
- /* update rf top / if top */
- regs[R_EB22] = 0x00;
- regs[R_EB22] |= map->rfagc_top;
- ret = tda18271_write_regs(fe, R_EB22, 1);
- if (tda_fail(ret))
- goto fail;
-
- /* --------------------------------------------------------------- */
-
- /* disable Power Level Indicator */
- regs[R_EP1] |= 0x40;
-
- /* make sure thermometer is off */
- regs[R_TM] &= ~0x10;
-
- /* frequency dependent parameters */
-
- tda18271_calc_ir_measure(fe, &freq);
-
- tda18271_calc_bp_filter(fe, &freq);
-
- tda18271_calc_rf_band(fe, &freq);
-
- tda18271_calc_gain_taper(fe, &freq);
-
- /* --------------------------------------------------------------- */
-
- /* dual tuner and agc1 extra configuration */
-
- switch (priv->role) {
- case TDA18271_MASTER:
- regs[R_EB1] |= 0x04; /* main vco */
- break;
- case TDA18271_SLAVE:
- regs[R_EB1] &= ~0x04; /* cal vco */
- break;
- }
-
- /* agc1 always active */
- regs[R_EB1] &= ~0x02;
-
- /* agc1 has priority on agc2 */
- regs[R_EB1] &= ~0x01;
-
- ret = tda18271_write_regs(fe, R_EB1, 1);
- if (tda_fail(ret))
- goto fail;
-
- /* --------------------------------------------------------------- */
-
- N = map->if_freq * 1000 + freq;
-
- switch (priv->role) {
- case TDA18271_MASTER:
- tda18271_calc_main_pll(fe, N);
- tda18271_set_if_notch(fe);
- tda18271_write_regs(fe, R_MPD, 4);
- break;
- case TDA18271_SLAVE:
- tda18271_calc_cal_pll(fe, N);
- tda18271_write_regs(fe, R_CPD, 4);
-
- regs[R_MPD] = regs[R_CPD] & 0x7f;
- tda18271_set_if_notch(fe);
- tda18271_write_regs(fe, R_MPD, 1);
- break;
- }
-
- ret = tda18271_write_regs(fe, R_TM, 7);
- if (tda_fail(ret))
- goto fail;
-
- /* force charge pump source */
- charge_pump_source(fe, 1);
-
- msleep(1);
-
- /* return pll to normal operation */
- charge_pump_source(fe, 0);
-
- msleep(20);
-
- if (priv->id == TDA18271HDC2) {
- /* set rfagc to normal speed mode */
- if (map->fm_rfn)
- regs[R_EP3] &= ~0x04;
- else
- regs[R_EP3] |= 0x04;
- ret = tda18271_write_regs(fe, R_EP3, 1);
- }
-fail:
- return ret;
-}
-
-static int tda18271_read_thermometer(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int tm;
-
- /* switch thermometer on */
- regs[R_TM] |= 0x10;
- tda18271_write_regs(fe, R_TM, 1);
-
- /* read thermometer info */
- tda18271_read_regs(fe);
-
- if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||
- (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {
-
- if ((regs[R_TM] & 0x20) == 0x20)
- regs[R_TM] &= ~0x20;
- else
- regs[R_TM] |= 0x20;
-
- tda18271_write_regs(fe, R_TM, 1);
-
- msleep(10); /* temperature sensing */
-
- /* read thermometer info */
- tda18271_read_regs(fe);
- }
-
- tm = tda18271_lookup_thermometer(fe);
-
- /* switch thermometer off */
- regs[R_TM] &= ~0x10;
- tda18271_write_regs(fe, R_TM, 1);
-
- /* set CAL mode to normal */
- regs[R_EP4] &= ~0x03;
- tda18271_write_regs(fe, R_EP4, 1);
-
- return tm;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
- u32 freq)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
- unsigned char *regs = priv->tda18271_regs;
- int i, ret;
- u8 tm_current, dc_over_dt, rf_tab;
- s32 rfcal_comp, approx;
-
- /* power up */
- ret = tda18271_set_standby_mode(fe, 0, 0, 0);
- if (tda_fail(ret))
- goto fail;
-
- /* read die current temperature */
- tm_current = tda18271_read_thermometer(fe);
-
- /* frequency dependent parameters */
-
- tda18271_calc_rf_cal(fe, &freq);
- rf_tab = regs[R_EB14];
-
- i = tda18271_lookup_rf_band(fe, &freq, NULL);
- if (tda_fail(i))
- return i;
-
- if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
- approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +
- map[i].rf_b1 + rf_tab;
- } else {
- approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +
- map[i].rf_b2 + rf_tab;
- }
-
- if (approx < 0)
- approx = 0;
- if (approx > 255)
- approx = 255;
-
- tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
-
- /* calculate temperature compensation */
- rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;
-
- regs[R_EB14] = (unsigned char)(approx + rfcal_comp);
- ret = tda18271_write_regs(fe, R_EB14, 1);
-fail:
- return ret;
-}
-
-static int tda18271_por(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
-
- /* power up detector 1 */
- regs[R_EB12] &= ~0x20;
- ret = tda18271_write_regs(fe, R_EB12, 1);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EB18] &= ~0x80; /* turn agc1 loop on */
- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
- ret = tda18271_write_regs(fe, R_EB18, 1);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */
-
- /* POR mode */
- ret = tda18271_set_standby_mode(fe, 1, 0, 0);
- if (tda_fail(ret))
- goto fail;
-
- /* disable 1.5 MHz low pass filter */
- regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */
- regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */
- ret = tda18271_write_regs(fe, R_EB21, 3);
-fail:
- return ret;
-}
-
-static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- u32 N;
-
- /* set CAL mode to normal */
- regs[R_EP4] &= ~0x03;
- tda18271_write_regs(fe, R_EP4, 1);
-
- /* switch off agc1 */
- regs[R_EP3] |= 0x40; /* sm_lt = 1 */
-
- regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */
- tda18271_write_regs(fe, R_EB18, 1);
-
- /* frequency dependent parameters */
-
- tda18271_calc_bp_filter(fe, &freq);
- tda18271_calc_gain_taper(fe, &freq);
- tda18271_calc_rf_band(fe, &freq);
- tda18271_calc_km(fe, &freq);
-
- tda18271_write_regs(fe, R_EP1, 3);
- tda18271_write_regs(fe, R_EB13, 1);
-
- /* main pll charge pump source */
- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
-
- /* cal pll charge pump source */
- tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1);
-
- /* force dcdc converter to 0 V */
- regs[R_EB14] = 0x00;
- tda18271_write_regs(fe, R_EB14, 1);
-
- /* disable plls lock */
- regs[R_EB20] &= ~0x20;
- tda18271_write_regs(fe, R_EB20, 1);
-
- /* set CAL mode to RF tracking filter calibration */
- regs[R_EP4] |= 0x03;
- tda18271_write_regs(fe, R_EP4, 2);
-
- /* --------------------------------------------------------------- */
-
- /* set the internal calibration signal */
- N = freq;
-
- tda18271_calc_cal_pll(fe, N);
- tda18271_write_regs(fe, R_CPD, 4);
-
- /* downconvert internal calibration */
- N += 1000000;
-
- tda18271_calc_main_pll(fe, N);
- tda18271_write_regs(fe, R_MPD, 4);
-
- msleep(5);
-
- tda18271_write_regs(fe, R_EP2, 1);
- tda18271_write_regs(fe, R_EP1, 1);
- tda18271_write_regs(fe, R_EP2, 1);
- tda18271_write_regs(fe, R_EP1, 1);
-
- /* --------------------------------------------------------------- */
-
- /* normal operation for the main pll */
- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
-
- /* normal operation for the cal pll */
- tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0);
-
- msleep(10); /* plls locking */
-
- /* launch the rf tracking filters calibration */
- regs[R_EB20] |= 0x20;
- tda18271_write_regs(fe, R_EB20, 1);
-
- msleep(60); /* calibration */
-
- /* --------------------------------------------------------------- */
-
- /* set CAL mode to normal */
- regs[R_EP4] &= ~0x03;
-
- /* switch on agc1 */
- regs[R_EP3] &= ~0x40; /* sm_lt = 0 */
-
- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
- tda18271_write_regs(fe, R_EB18, 1);
-
- tda18271_write_regs(fe, R_EP3, 2);
-
- /* synchronization */
- tda18271_write_regs(fe, R_EP1, 1);
-
- /* get calibration result */
- tda18271_read_extended(fe);
-
- return regs[R_EB14];
-}
-
-static int tda18271_powerscan(struct dvb_frontend *fe,
- u32 *freq_in, u32 *freq_out)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int sgn, bcal, count, wait, ret;
- u8 cid_target;
- u16 count_limit;
- u32 freq;
-
- freq = *freq_in;
-
- tda18271_calc_rf_band(fe, &freq);
- tda18271_calc_rf_cal(fe, &freq);
- tda18271_calc_gain_taper(fe, &freq);
- tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);
-
- tda18271_write_regs(fe, R_EP2, 1);
- tda18271_write_regs(fe, R_EB14, 1);
-
- /* downconvert frequency */
- freq += 1000000;
-
- tda18271_calc_main_pll(fe, freq);
- tda18271_write_regs(fe, R_MPD, 4);
-
- msleep(5); /* pll locking */
-
- /* detection mode */
- regs[R_EP4] &= ~0x03;
- regs[R_EP4] |= 0x01;
- tda18271_write_regs(fe, R_EP4, 1);
-
- /* launch power detection measurement */
- tda18271_write_regs(fe, R_EP2, 1);
-
- /* read power detection info, stored in EB10 */
- ret = tda18271_read_extended(fe);
- if (tda_fail(ret))
- return ret;
-
- /* algorithm initialization */
- sgn = 1;
- *freq_out = *freq_in;
- bcal = 0;
- count = 0;
- wait = false;
-
- while ((regs[R_EB10] & 0x3f) < cid_target) {
- /* downconvert updated freq to 1 MHz */
- freq = *freq_in + (sgn * count) + 1000000;
-
- tda18271_calc_main_pll(fe, freq);
- tda18271_write_regs(fe, R_MPD, 4);
-
- if (wait) {
- msleep(5); /* pll locking */
- wait = false;
- } else
- udelay(100); /* pll locking */
-
- /* launch power detection measurement */
- tda18271_write_regs(fe, R_EP2, 1);
-
- /* read power detection info, stored in EB10 */
- ret = tda18271_read_extended(fe);
- if (tda_fail(ret))
- return ret;
-
- count += 200;
-
- if (count <= count_limit)
- continue;
-
- if (sgn <= 0)
- break;
-
- sgn = -1 * sgn;
- count = 200;
- wait = true;
- }
-
- if ((regs[R_EB10] & 0x3f) >= cid_target) {
- bcal = 1;
- *freq_out = freq - 1000000;
- } else
- bcal = 0;
-
- tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",
- bcal, *freq_in, *freq_out, freq);
-
- return bcal;
-}
-
-static int tda18271_powerscan_init(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
-
- /* set standard to digital */
- regs[R_EP3] &= ~0x1f; /* clear std bits */
- regs[R_EP3] |= 0x12;
-
- /* set cal mode to normal */
- regs[R_EP4] &= ~0x03;
-
- /* update IF output level */
- regs[R_EP4] &= ~0x1c; /* clear if level bits */
-
- ret = tda18271_write_regs(fe, R_EP3, 2);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
- ret = tda18271_write_regs(fe, R_EB18, 1);
- if (tda_fail(ret))
- goto fail;
-
- regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */
-
- /* 1.5 MHz low pass filter */
- regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */
- regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */
-
- ret = tda18271_write_regs(fe, R_EB21, 3);
-fail:
- return ret;
-}
-
-static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
- unsigned char *regs = priv->tda18271_regs;
- int bcal, rf, i;
- s32 divisor, dividend;
-#define RF1 0
-#define RF2 1
-#define RF3 2
- u32 rf_default[3];
- u32 rf_freq[3];
- s32 prog_cal[3];
- s32 prog_tab[3];
-
- i = tda18271_lookup_rf_band(fe, &freq, NULL);
-
- if (tda_fail(i))
- return i;
-
- rf_default[RF1] = 1000 * map[i].rf1_def;
- rf_default[RF2] = 1000 * map[i].rf2_def;
- rf_default[RF3] = 1000 * map[i].rf3_def;
-
- for (rf = RF1; rf <= RF3; rf++) {
- if (0 == rf_default[rf])
- return 0;
- tda_cal("freq = %d, rf = %d\n", freq, rf);
-
- /* look for optimized calibration frequency */
- bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);
- if (tda_fail(bcal))
- return bcal;
-
- tda18271_calc_rf_cal(fe, &rf_freq[rf]);
- prog_tab[rf] = (s32)regs[R_EB14];
-
- if (1 == bcal)
- prog_cal[rf] =
- (s32)tda18271_calibrate_rf(fe, rf_freq[rf]);
- else
- prog_cal[rf] = prog_tab[rf];
-
- switch (rf) {
- case RF1:
- map[i].rf_a1 = 0;
- map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);
- map[i].rf1 = rf_freq[RF1] / 1000;
- break;
- case RF2:
- dividend = (prog_cal[RF2] - prog_tab[RF2] -
- prog_cal[RF1] + prog_tab[RF1]);
- divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
- map[i].rf_a1 = (dividend / divisor);
- map[i].rf2 = rf_freq[RF2] / 1000;
- break;
- case RF3:
- dividend = (prog_cal[RF3] - prog_tab[RF3] -
- prog_cal[RF2] + prog_tab[RF2]);
- divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
- map[i].rf_a2 = (dividend / divisor);
- map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);
- map[i].rf3 = rf_freq[RF3] / 1000;
- break;
- default:
- BUG();
- }
- }
-
- return 0;
-}
-
-static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned int i;
- int ret;
-
- tda_info("tda18271: performing RF tracking filter calibration\n");
-
- /* wait for die temperature stabilization */
- msleep(200);
-
- ret = tda18271_powerscan_init(fe);
- if (tda_fail(ret))
- goto fail;
-
- /* rf band calibration */
- for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) {
- ret =
- tda18271_rf_tracking_filters_init(fe, 1000 *
- priv->rf_cal_state[i].rfmax);
- if (tda_fail(ret))
- goto fail;
- }
-
- priv->tm_rfcal = tda18271_read_thermometer(fe);
-fail:
- return ret;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda18271c2_rf_cal_init(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
-
- /* test RF_CAL_OK to see if we need init */
- if ((regs[R_EP1] & 0x10) == 0)
- priv->cal_initialized = false;
-
- if (priv->cal_initialized)
- return 0;
-
- ret = tda18271_calc_rf_filter_curve(fe);
- if (tda_fail(ret))
- goto fail;
-
- ret = tda18271_por(fe);
- if (tda_fail(ret))
- goto fail;
-
- tda_info("tda18271: RF tracking filter calibration complete\n");
-
- priv->cal_initialized = true;
- goto end;
-fail:
- tda_info("tda18271: RF tracking filter calibration failed!\n");
-end:
- return ret;
-}
-
-static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe,
- u32 freq, u32 bw)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
- u32 N = 0;
-
- /* calculate bp filter */
- tda18271_calc_bp_filter(fe, &freq);
- tda18271_write_regs(fe, R_EP1, 1);
-
- regs[R_EB4] &= 0x07;
- regs[R_EB4] |= 0x60;
- tda18271_write_regs(fe, R_EB4, 1);
-
- regs[R_EB7] = 0x60;
- tda18271_write_regs(fe, R_EB7, 1);
-
- regs[R_EB14] = 0x00;
- tda18271_write_regs(fe, R_EB14, 1);
-
- regs[R_EB20] = 0xcc;
- tda18271_write_regs(fe, R_EB20, 1);
-
- /* set cal mode to RF tracking filter calibration */
- regs[R_EP4] |= 0x03;
-
- /* calculate cal pll */
-
- switch (priv->mode) {
- case TDA18271_ANALOG:
- N = freq - 1250000;
- break;
- case TDA18271_DIGITAL:
- N = freq + bw / 2;
- break;
- }
-
- tda18271_calc_cal_pll(fe, N);
-
- /* calculate main pll */
-
- switch (priv->mode) {
- case TDA18271_ANALOG:
- N = freq - 250000;
- break;
- case TDA18271_DIGITAL:
- N = freq + bw / 2 + 1000000;
- break;
- }
-
- tda18271_calc_main_pll(fe, N);
-
- ret = tda18271_write_regs(fe, R_EP3, 11);
- if (tda_fail(ret))
- return ret;
-
- msleep(5); /* RF tracking filter calibration initialization */
-
- /* search for K,M,CO for RF calibration */
- tda18271_calc_km(fe, &freq);
- tda18271_write_regs(fe, R_EB13, 1);
-
- /* search for rf band */
- tda18271_calc_rf_band(fe, &freq);
-
- /* search for gain taper */
- tda18271_calc_gain_taper(fe, &freq);
-
- tda18271_write_regs(fe, R_EP2, 1);
- tda18271_write_regs(fe, R_EP1, 1);
- tda18271_write_regs(fe, R_EP2, 1);
- tda18271_write_regs(fe, R_EP1, 1);
-
- regs[R_EB4] &= 0x07;
- regs[R_EB4] |= 0x40;
- tda18271_write_regs(fe, R_EB4, 1);
-
- regs[R_EB7] = 0x40;
- tda18271_write_regs(fe, R_EB7, 1);
- msleep(10); /* pll locking */
-
- regs[R_EB20] = 0xec;
- tda18271_write_regs(fe, R_EB20, 1);
- msleep(60); /* RF tracking filter calibration completion */
-
- regs[R_EP4] &= ~0x03; /* set cal mode to normal */
- tda18271_write_regs(fe, R_EP4, 1);
-
- tda18271_write_regs(fe, R_EP1, 1);
-
- /* RF tracking filter correction for VHF_Low band */
- if (0 == tda18271_calc_rf_cal(fe, &freq))
- tda18271_write_regs(fe, R_EB14, 1);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda18271_ir_cal_init(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- int ret;
-
- ret = tda18271_read_regs(fe);
- if (tda_fail(ret))
- goto fail;
-
- /* test IR_CAL_OK to see if we need init */
- if ((regs[R_EP1] & 0x08) == 0)
- ret = tda18271_init_regs(fe);
-fail:
- return ret;
-}
-
-static int tda18271_init(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- int ret;
-
- mutex_lock(&priv->lock);
-
- /* full power up */
- ret = tda18271_set_standby_mode(fe, 0, 0, 0);
- if (tda_fail(ret))
- goto fail;
-
- /* initialization */
- ret = tda18271_ir_cal_init(fe);
- if (tda_fail(ret))
- goto fail;
-
- if (priv->id == TDA18271HDC2)
- tda18271c2_rf_cal_init(fe);
-fail:
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-static int tda18271_sleep(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- int ret;
-
- mutex_lock(&priv->lock);
-
- /* enter standby mode, with required output features enabled */
- ret = tda18271_toggle_output(fe, 1);
-
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda18271_agc(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- int ret = 0;
-
- switch (priv->config) {
- case 0:
- /* no external agc configuration required */
- if (tda18271_debug & DBG_ADV)
- tda_dbg("no agc configuration provided\n");
- break;
- case 3:
- /* switch with GPIO of saa713x */
- tda_dbg("invoking callback\n");
- if (fe->callback)
- ret = fe->callback(priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER,
- TDA18271_CALLBACK_CMD_AGC_ENABLE,
- priv->mode);
- break;
- case 1:
- case 2:
- default:
- /* n/a - currently not supported */
- tda_err("unsupported configuration: %d\n", priv->config);
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int tda18271_tune(struct dvb_frontend *fe,
- struct tda18271_std_map_item *map, u32 freq, u32 bw)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- int ret;
-
- tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",
- freq, map->if_freq, bw, map->agc_mode, map->std);
-
- ret = tda18271_agc(fe);
- if (tda_fail(ret))
- tda_warn("failed to configure agc\n");
-
- ret = tda18271_init(fe);
- if (tda_fail(ret))
- goto fail;
-
- mutex_lock(&priv->lock);
-
- switch (priv->id) {
- case TDA18271HDC1:
- tda18271c1_rf_tracking_filter_calibration(fe, freq, bw);
- break;
- case TDA18271HDC2:
- tda18271c2_rf_tracking_filters_correction(fe, freq);
- break;
- }
- ret = tda18271_channel_configuration(fe, map, freq, bw);
-
- mutex_unlock(&priv->lock);
-fail:
- return ret;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int tda18271_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- u32 bw = c->bandwidth_hz;
- u32 freq = c->frequency;
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_std_map *std_map = &priv->std;
- struct tda18271_std_map_item *map;
- int ret;
-
- priv->mode = TDA18271_DIGITAL;
-
- switch (delsys) {
- case SYS_ATSC:
- map = &std_map->atsc_6;
- bw = 6000000;
- break;
- case SYS_ISDBT:
- case SYS_DVBT:
- case SYS_DVBT2:
- if (bw <= 6000000) {
- map = &std_map->dvbt_6;
- } else if (bw <= 7000000) {
- map = &std_map->dvbt_7;
- } else {
- map = &std_map->dvbt_8;
- }
- break;
- case SYS_DVBC_ANNEX_B:
- bw = 6000000;
- /* falltrough */
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- if (bw <= 6000000) {
- map = &std_map->qam_6;
- } else if (bw <= 7000000) {
- map = &std_map->qam_7;
- } else {
- map = &std_map->qam_8;
- }
- break;
- default:
- tda_warn("modulation type not supported!\n");
- return -EINVAL;
- }
-
- /* When tuning digital, the analog demod must be tri-stated */
- if (fe->ops.analog_ops.standby)
- fe->ops.analog_ops.standby(fe);
-
- ret = tda18271_tune(fe, map, freq, bw);
-
- if (tda_fail(ret))
- goto fail;
-
- priv->if_freq = map->if_freq;
- priv->frequency = freq;
- priv->bandwidth = bw;
-fail:
- return ret;
-}
-
-static int tda18271_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_std_map *std_map = &priv->std;
- struct tda18271_std_map_item *map;
- char *mode;
- int ret;
- u32 freq = params->frequency * 125 *
- ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2;
-
- priv->mode = TDA18271_ANALOG;
-
- if (params->mode == V4L2_TUNER_RADIO) {
- map = &std_map->fm_radio;
- mode = "fm";
- } else if (params->std & V4L2_STD_MN) {
- map = &std_map->atv_mn;
- mode = "MN";
- } else if (params->std & V4L2_STD_B) {
- map = &std_map->atv_b;
- mode = "B";
- } else if (params->std & V4L2_STD_GH) {
- map = &std_map->atv_gh;
- mode = "GH";
- } else if (params->std & V4L2_STD_PAL_I) {
- map = &std_map->atv_i;
- mode = "I";
- } else if (params->std & V4L2_STD_DK) {
- map = &std_map->atv_dk;
- mode = "DK";
- } else if (params->std & V4L2_STD_SECAM_L) {
- map = &std_map->atv_l;
- mode = "L";
- } else if (params->std & V4L2_STD_SECAM_LC) {
- map = &std_map->atv_lc;
- mode = "L'";
- } else {
- map = &std_map->atv_i;
- mode = "xx";
- }
-
- tda_dbg("setting tda18271 to system %s\n", mode);
-
- ret = tda18271_tune(fe, map, freq, 0);
-
- if (tda_fail(ret))
- goto fail;
-
- priv->if_freq = map->if_freq;
- priv->frequency = freq;
- priv->bandwidth = 0;
-fail:
- return ret;
-}
-
-static int tda18271_release(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
-
- mutex_lock(&tda18271_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&tda18271_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- *bandwidth = priv->bandwidth;
- return 0;
-}
-
-static int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- *frequency = (u32)priv->if_freq * 1000;
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-#define tda18271_update_std(std_cfg, name) do { \
- if (map->std_cfg.if_freq + \
- map->std_cfg.agc_mode + map->std_cfg.std + \
- map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \
- tda_dbg("Using custom std config for %s\n", name); \
- memcpy(&std->std_cfg, &map->std_cfg, \
- sizeof(struct tda18271_std_map_item)); \
- } } while (0)
-
-#define tda18271_dump_std_item(std_cfg, name) do { \
- tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \
- "if_lvl = %d, rfagc_top = 0x%02x\n", \
- name, std->std_cfg.if_freq, \
- std->std_cfg.agc_mode, std->std_cfg.std, \
- std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \
- } while (0)
-
-static int tda18271_dump_std_map(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_std_map *std = &priv->std;
-
- tda_dbg("========== STANDARD MAP SETTINGS ==========\n");
- tda18271_dump_std_item(fm_radio, " fm ");
- tda18271_dump_std_item(atv_b, "atv b ");
- tda18271_dump_std_item(atv_dk, "atv dk");
- tda18271_dump_std_item(atv_gh, "atv gh");
- tda18271_dump_std_item(atv_i, "atv i ");
- tda18271_dump_std_item(atv_l, "atv l ");
- tda18271_dump_std_item(atv_lc, "atv l'");
- tda18271_dump_std_item(atv_mn, "atv mn");
- tda18271_dump_std_item(atsc_6, "atsc 6");
- tda18271_dump_std_item(dvbt_6, "dvbt 6");
- tda18271_dump_std_item(dvbt_7, "dvbt 7");
- tda18271_dump_std_item(dvbt_8, "dvbt 8");
- tda18271_dump_std_item(qam_6, "qam 6 ");
- tda18271_dump_std_item(qam_8, "qam 8 ");
-
- return 0;
-}
-
-static int tda18271_update_std_map(struct dvb_frontend *fe,
- struct tda18271_std_map *map)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- struct tda18271_std_map *std = &priv->std;
-
- if (!map)
- return -EINVAL;
-
- tda18271_update_std(fm_radio, "fm");
- tda18271_update_std(atv_b, "atv b");
- tda18271_update_std(atv_dk, "atv dk");
- tda18271_update_std(atv_gh, "atv gh");
- tda18271_update_std(atv_i, "atv i");
- tda18271_update_std(atv_l, "atv l");
- tda18271_update_std(atv_lc, "atv l'");
- tda18271_update_std(atv_mn, "atv mn");
- tda18271_update_std(atsc_6, "atsc 6");
- tda18271_update_std(dvbt_6, "dvbt 6");
- tda18271_update_std(dvbt_7, "dvbt 7");
- tda18271_update_std(dvbt_8, "dvbt 8");
- tda18271_update_std(qam_6, "qam 6");
- tda18271_update_std(qam_8, "qam 8");
-
- return 0;
-}
-
-static int tda18271_get_id(struct dvb_frontend *fe)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
- unsigned char *regs = priv->tda18271_regs;
- char *name;
-
- mutex_lock(&priv->lock);
- tda18271_read_regs(fe);
- mutex_unlock(&priv->lock);
-
- switch (regs[R_ID] & 0x7f) {
- case 3:
- name = "TDA18271HD/C1";
- priv->id = TDA18271HDC1;
- break;
- case 4:
- name = "TDA18271HD/C2";
- priv->id = TDA18271HDC2;
- break;
- default:
- tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n",
- regs[R_ID], i2c_adapter_id(priv->i2c_props.adap),
- priv->i2c_props.addr);
- return -EINVAL;
- }
-
- tda_info("%s detected @ %d-%04x\n", name,
- i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr);
-
- return 0;
-}
-
-static int tda18271_setup_configuration(struct dvb_frontend *fe,
- struct tda18271_config *cfg)
-{
- struct tda18271_priv *priv = fe->tuner_priv;
-
- priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
- priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
- priv->config = (cfg) ? cfg->config : 0;
- priv->small_i2c = (cfg) ?
- cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;
- priv->output_opt = (cfg) ?
- cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
-
- return 0;
-}
-
-static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)
-{
- /* tda18271_cal_on_startup == -1 when cal module option is unset */
- return ((tda18271_cal_on_startup == -1) ?
- /* honor configuration setting */
- ((cfg) && (cfg->rf_cal_on_startup)) :
- /* module option overrides configuration setting */
- (tda18271_cal_on_startup)) ? 1 : 0;
-}
-
-static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;
-
- tda18271_setup_configuration(fe, cfg);
-
- if (tda18271_need_cal_on_startup(cfg))
- tda18271_init(fe);
-
- /* override default std map with values in config struct */
- if ((cfg) && (cfg->std_map))
- tda18271_update_std_map(fe, cfg->std_map);
-
- return 0;
-}
-
-static const struct dvb_tuner_ops tda18271_tuner_ops = {
- .info = {
- .name = "NXP TDA18271HD",
- .frequency_min = 45000000,
- .frequency_max = 864000000,
- .frequency_step = 62500
- },
- .init = tda18271_init,
- .sleep = tda18271_sleep,
- .set_params = tda18271_set_params,
- .set_analog_params = tda18271_set_analog_params,
- .release = tda18271_release,
- .set_config = tda18271_set_config,
- .get_frequency = tda18271_get_frequency,
- .get_bandwidth = tda18271_get_bandwidth,
- .get_if_frequency = tda18271_get_if_frequency,
-};
-
-struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
- struct i2c_adapter *i2c,
- struct tda18271_config *cfg)
-{
- struct tda18271_priv *priv = NULL;
- int instance, ret;
-
- mutex_lock(&tda18271_list_mutex);
-
- instance = hybrid_tuner_request_state(struct tda18271_priv, priv,
- hybrid_tuner_instance_list,
- i2c, addr, "tda18271");
- switch (instance) {
- case 0:
- goto fail;
- case 1:
- /* new tuner instance */
- fe->tuner_priv = priv;
-
- tda18271_setup_configuration(fe, cfg);
-
- priv->cal_initialized = false;
- mutex_init(&priv->lock);
-
- ret = tda18271_get_id(fe);
- if (tda_fail(ret))
- goto fail;
-
- ret = tda18271_assign_map_layout(fe);
- if (tda_fail(ret))
- goto fail;
-
- mutex_lock(&priv->lock);
- tda18271_init_regs(fe);
-
- if ((tda18271_need_cal_on_startup(cfg)) &&
- (priv->id == TDA18271HDC2))
- tda18271c2_rf_cal_init(fe);
-
- mutex_unlock(&priv->lock);
- break;
- default:
- /* existing tuner instance */
- fe->tuner_priv = priv;
-
- /* allow dvb driver to override configuration settings */
- if (cfg) {
- if (cfg->gate != TDA18271_GATE_ANALOG)
- priv->gate = cfg->gate;
- if (cfg->role)
- priv->role = cfg->role;
- if (cfg->config)
- priv->config = cfg->config;
- if (cfg->small_i2c)
- priv->small_i2c = cfg->small_i2c;
- if (cfg->output_opt)
- priv->output_opt = cfg->output_opt;
- if (cfg->std_map)
- tda18271_update_std_map(fe, cfg->std_map);
- }
- if (tda18271_need_cal_on_startup(cfg))
- tda18271_init(fe);
- break;
- }
-
- /* override default std map with values in config struct */
- if ((cfg) && (cfg->std_map))
- tda18271_update_std_map(fe, cfg->std_map);
-
- mutex_unlock(&tda18271_list_mutex);
-
- memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- if (tda18271_debug & (DBG_MAP | DBG_ADV))
- tda18271_dump_std_map(fe);
-
- return fe;
-fail:
- mutex_unlock(&tda18271_list_mutex);
-
- tda18271_release(fe);
- return NULL;
-}
-EXPORT_SYMBOL_GPL(tda18271_attach);
-MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
-MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.4");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h
deleted file mode 100644
index 640bae4e6a5a..000000000000
--- a/drivers/media/common/tuners/tda18271.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
-
- Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#ifndef __TDA18271_H__
-#define __TDA18271_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-struct tda18271_std_map_item {
- u16 if_freq;
-
- /* EP3[4:3] */
- unsigned int agc_mode:2;
- /* EP3[2:0] */
- unsigned int std:3;
- /* EP4[7] */
- unsigned int fm_rfn:1;
- /* EP4[4:2] */
- unsigned int if_lvl:3;
- /* EB22[6:0] */
- unsigned int rfagc_top:7;
-};
-
-struct tda18271_std_map {
- struct tda18271_std_map_item fm_radio;
- struct tda18271_std_map_item atv_b;
- struct tda18271_std_map_item atv_dk;
- struct tda18271_std_map_item atv_gh;
- struct tda18271_std_map_item atv_i;
- struct tda18271_std_map_item atv_l;
- struct tda18271_std_map_item atv_lc;
- struct tda18271_std_map_item atv_mn;
- struct tda18271_std_map_item atsc_6;
- struct tda18271_std_map_item dvbt_6;
- struct tda18271_std_map_item dvbt_7;
- struct tda18271_std_map_item dvbt_8;
- struct tda18271_std_map_item qam_6;
- struct tda18271_std_map_item qam_7;
- struct tda18271_std_map_item qam_8;
-};
-
-enum tda18271_role {
- TDA18271_MASTER = 0,
- TDA18271_SLAVE,
-};
-
-enum tda18271_i2c_gate {
- TDA18271_GATE_AUTO = 0,
- TDA18271_GATE_ANALOG,
- TDA18271_GATE_DIGITAL,
-};
-
-enum tda18271_output_options {
- /* slave tuner output & loop thru & xtal oscillator always on */
- TDA18271_OUTPUT_LT_XT_ON = 0,
-
- /* slave tuner output loop thru off */
- TDA18271_OUTPUT_LT_OFF = 1,
-
- /* xtal oscillator off */
- TDA18271_OUTPUT_XT_OFF = 2,
-};
-
-enum tda18271_small_i2c {
- TDA18271_39_BYTE_CHUNK_INIT = 0,
- TDA18271_16_BYTE_CHUNK_INIT = 16,
- TDA18271_08_BYTE_CHUNK_INIT = 8,
- TDA18271_03_BYTE_CHUNK_INIT = 3,
-};
-
-struct tda18271_config {
- /* override default if freq / std settings (optional) */
- struct tda18271_std_map *std_map;
-
- /* master / slave tuner: master uses main pll, slave uses cal pll */
- enum tda18271_role role;
-
- /* use i2c gate provided by analog or digital demod */
- enum tda18271_i2c_gate gate;
-
- /* output options that can be disabled */
- enum tda18271_output_options output_opt;
-
- /* some i2c providers can't write all 39 registers at once */
- enum tda18271_small_i2c small_i2c;
-
- /* force rf tracking filter calibration on startup */
- unsigned int rf_cal_on_startup:1;
-
- /* interface to saa713x / tda829x */
- unsigned int config;
-};
-
-#define TDA18271_CALLBACK_CMD_AGC_ENABLE 0
-
-enum tda18271_mode {
- TDA18271_ANALOG = 0,
- TDA18271_DIGITAL,
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
- struct i2c_adapter *i2c,
- struct tda18271_config *cfg);
-#else
-static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
- u8 addr,
- struct i2c_adapter *i2c,
- struct tda18271_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* __TDA18271_H__ */
diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c
deleted file mode 100644
index de2607084672..000000000000
--- a/drivers/media/common/tuners/tua9001.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Infineon TUA 9001 silicon tuner driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tua9001.h"
-#include "tua9001_priv.h"
-
-/* write register */
-static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
-{
- int ret;
- u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg->i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n",
- __func__, ret, reg);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-static int tua9001_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int tua9001_init(struct dvb_frontend *fe)
-{
- struct tua9001_priv *priv = fe->tuner_priv;
- int ret = 0;
- u8 i;
- struct reg_val data[] = {
- { 0x1e, 0x6512 },
- { 0x25, 0xb888 },
- { 0x39, 0x5460 },
- { 0x3b, 0x00c0 },
- { 0x3a, 0xf000 },
- { 0x08, 0x0000 },
- { 0x32, 0x0030 },
- { 0x41, 0x703a },
- { 0x40, 0x1c78 },
- { 0x2c, 0x1c00 },
- { 0x36, 0xc013 },
- { 0x37, 0x6f18 },
- { 0x27, 0x0008 },
- { 0x2a, 0x0001 },
- { 0x34, 0x0a40 },
- };
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
-
- for (i = 0; i < ARRAY_SIZE(data); i++) {
- ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
- if (ret)
- break;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
-
- if (ret < 0)
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int tua9001_set_params(struct dvb_frontend *fe)
-{
- struct tua9001_priv *priv = fe->tuner_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u16 val;
- u32 frequency;
- struct reg_val data[2];
-
- pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
- __func__, c->delivery_system, c->frequency,
- c->bandwidth_hz);
-
- switch (c->delivery_system) {
- case SYS_DVBT:
- switch (c->bandwidth_hz) {
- case 8000000:
- val = 0x0000;
- break;
- case 7000000:
- val = 0x1000;
- break;
- case 6000000:
- val = 0x2000;
- break;
- case 5000000:
- val = 0x3000;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- data[0].reg = 0x04;
- data[0].val = val;
-
- frequency = (c->frequency - 150000000);
- frequency /= 100;
- frequency *= 48;
- frequency /= 10000;
-
- data[1].reg = 0x1f;
- data[1].val = frequency;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
-
- for (i = 0; i < ARRAY_SIZE(data); i++) {
- ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
- if (ret < 0)
- break;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
-
-err:
- if (ret < 0)
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- *frequency = 0; /* Zero-IF */
-
- return 0;
-}
-
-static const struct dvb_tuner_ops tua9001_tuner_ops = {
- .info = {
- .name = "Infineon TUA 9001",
-
- .frequency_min = 170000000,
- .frequency_max = 862000000,
- .frequency_step = 0,
- },
-
- .release = tua9001_release,
-
- .init = tua9001_init,
- .set_params = tua9001_set_params,
-
- .get_if_frequency = tua9001_get_if_frequency,
-};
-
-struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg)
-{
- struct tua9001_priv *priv = NULL;
-
- priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->cfg = cfg;
- priv->i2c = i2c;
-
- printk(KERN_INFO "Infineon TUA 9001 successfully attached.");
-
- memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = priv;
- return fe;
-}
-EXPORT_SYMBOL(tua9001_attach);
-
-MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h
deleted file mode 100644
index 38d6ae76b1d6..000000000000
--- a/drivers/media/common/tuners/tua9001.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Infineon TUA 9001 silicon tuner driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef TUA9001_H
-#define TUA9001_H
-
-#include "dvb_frontend.h"
-
-struct tua9001_config {
- /*
- * I2C address
- */
- u8 i2c_addr;
-};
-
-#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \
- (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE))
-extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg);
-#else
-static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c, struct tua9001_config *cfg)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
deleted file mode 100644
index ea0550eafe7d..000000000000
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ /dev/null
@@ -1,1510 +0,0 @@
-/* tuner-xc2028
- *
- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
- *
- * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
- * - frontend interface
- *
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#include <linux/i2c.h>
-#include <asm/div64.h>
-#include <linux/firmware.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <media/tuner.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-#include "tuner-i2c.h"
-#include "tuner-xc2028.h"
-#include "tuner-xc2028-types.h"
-
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-/* Registers (Write-only) */
-#define XREG_INIT 0x00
-#define XREG_RF_FREQ 0x02
-#define XREG_POWER_DOWN 0x08
-
-/* Registers (Read-only) */
-#define XREG_FREQ_ERROR 0x01
-#define XREG_LOCK 0x02
-#define XREG_VERSION 0x04
-#define XREG_PRODUCT_ID 0x08
-#define XREG_HSYNC_FREQ 0x10
-#define XREG_FRAME_LINES 0x20
-#define XREG_SNR 0x40
-
-#define XREG_ADC_ENV 0x0100
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-static int no_poweroff;
-module_param(no_poweroff, int, 0644);
-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
- "1 keep device energized and with tuner ready all the times.\n"
- " Faster, but consumes more power and keeps the device hotter\n");
-
-static char audio_std[8];
-module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
-MODULE_PARM_DESC(audio_std,
- "Audio standard. XC3028 audio decoder explicitly "
- "needs to know what audio\n"
- "standard is needed for some video standards with audio A2 or NICAM.\n"
- "The valid values are:\n"
- "A2\n"
- "A2/A\n"
- "A2/B\n"
- "NICAM\n"
- "NICAM/A\n"
- "NICAM/B\n");
-
-static char firmware_name[30];
-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
- "default firmware name\n");
-
-static LIST_HEAD(hybrid_tuner_instance_list);
-static DEFINE_MUTEX(xc2028_list_mutex);
-
-/* struct for storing firmware table */
-struct firmware_description {
- unsigned int type;
- v4l2_std_id id;
- __u16 int_freq;
- unsigned char *ptr;
- unsigned int size;
-};
-
-struct firmware_properties {
- unsigned int type;
- v4l2_std_id id;
- v4l2_std_id std_req;
- __u16 int_freq;
- unsigned int scode_table;
- int scode_nr;
-};
-
-enum xc2028_state {
- XC2028_NO_FIRMWARE = 0,
- XC2028_WAITING_FIRMWARE,
- XC2028_ACTIVE,
- XC2028_SLEEP,
- XC2028_NODEV,
-};
-
-struct xc2028_data {
- struct list_head hybrid_tuner_instance_list;
- struct tuner_i2c_props i2c_props;
- __u32 frequency;
-
- enum xc2028_state state;
- const char *fname;
-
- struct firmware_description *firm;
- int firm_size;
- __u16 firm_version;
-
- __u16 hwmodel;
- __u16 hwvers;
-
- struct xc2028_ctrl ctrl;
-
- struct firmware_properties cur_fw;
-
- struct mutex lock;
-};
-
-#define i2c_send(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_info("i2c output error: rc = %d (should be %d)\n",\
- _rc, (int)size); \
- if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-#define i2c_rcv(priv, buf, size) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
- if (size != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)size); \
- _rc; \
-})
-
-#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
- int _rc; \
- _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
- ibuf, isize); \
- if (isize != _rc) \
- tuner_err("i2c input error: rc = %d (should be %d)\n", \
- _rc, (int)isize); \
- if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-#define send_seq(priv, data...) ({ \
- static u8 _val[] = data; \
- int _rc; \
- if (sizeof(_val) != \
- (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
- _val, sizeof(_val)))) { \
- tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
- } else if (priv->ctrl.msleep) \
- msleep(priv->ctrl.msleep); \
- _rc; \
-})
-
-static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
-{
- unsigned char buf[2];
- unsigned char ibuf[2];
-
- tuner_dbg("%s %04x called\n", __func__, reg);
-
- buf[0] = reg >> 8;
- buf[1] = (unsigned char) reg;
-
- if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
- return -EIO;
-
- *val = (ibuf[1]) | (ibuf[0] << 8);
- return 0;
-}
-
-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
-{
- if (type & BASE)
- printk("BASE ");
- if (type & INIT1)
- printk("INIT1 ");
- if (type & F8MHZ)
- printk("F8MHZ ");
- if (type & MTS)
- printk("MTS ");
- if (type & D2620)
- printk("D2620 ");
- if (type & D2633)
- printk("D2633 ");
- if (type & DTV6)
- printk("DTV6 ");
- if (type & QAM)
- printk("QAM ");
- if (type & DTV7)
- printk("DTV7 ");
- if (type & DTV78)
- printk("DTV78 ");
- if (type & DTV8)
- printk("DTV8 ");
- if (type & FM)
- printk("FM ");
- if (type & INPUT1)
- printk("INPUT1 ");
- if (type & LCD)
- printk("LCD ");
- if (type & NOGD)
- printk("NOGD ");
- if (type & MONO)
- printk("MONO ");
- if (type & ATSC)
- printk("ATSC ");
- if (type & IF)
- printk("IF ");
- if (type & LG60)
- printk("LG60 ");
- if (type & ATI638)
- printk("ATI638 ");
- if (type & OREN538)
- printk("OREN538 ");
- if (type & OREN36)
- printk("OREN36 ");
- if (type & TOYOTA388)
- printk("TOYOTA388 ");
- if (type & TOYOTA794)
- printk("TOYOTA794 ");
- if (type & DIBCOM52)
- printk("DIBCOM52 ");
- if (type & ZARLINK456)
- printk("ZARLINK456 ");
- if (type & CHINA)
- printk("CHINA ");
- if (type & F6MHZ)
- printk("F6MHZ ");
- if (type & INPUT2)
- printk("INPUT2 ");
- if (type & SCODE)
- printk("SCODE ");
- if (type & HAS_IF)
- printk("HAS_IF_%d ", int_freq);
-}
-
-static v4l2_std_id parse_audio_std_option(void)
-{
- if (strcasecmp(audio_std, "A2") == 0)
- return V4L2_STD_A2;
- if (strcasecmp(audio_std, "A2/A") == 0)
- return V4L2_STD_A2_A;
- if (strcasecmp(audio_std, "A2/B") == 0)
- return V4L2_STD_A2_B;
- if (strcasecmp(audio_std, "NICAM") == 0)
- return V4L2_STD_NICAM;
- if (strcasecmp(audio_std, "NICAM/A") == 0)
- return V4L2_STD_NICAM_A;
- if (strcasecmp(audio_std, "NICAM/B") == 0)
- return V4L2_STD_NICAM_B;
-
- return 0;
-}
-
-static int check_device_status(struct xc2028_data *priv)
-{
- switch (priv->state) {
- case XC2028_NO_FIRMWARE:
- case XC2028_WAITING_FIRMWARE:
- return -EAGAIN;
- case XC2028_ACTIVE:
- case XC2028_SLEEP:
- return 0;
- case XC2028_NODEV:
- return -ENODEV;
- }
- return 0;
-}
-
-static void free_firmware(struct xc2028_data *priv)
-{
- int i;
- tuner_dbg("%s called\n", __func__);
-
- if (!priv->firm)
- return;
-
- for (i = 0; i < priv->firm_size; i++)
- kfree(priv->firm[i].ptr);
-
- kfree(priv->firm);
-
- priv->firm = NULL;
- priv->firm_size = 0;
- priv->state = XC2028_NO_FIRMWARE;
-
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-}
-
-static int load_all_firmwares(struct dvb_frontend *fe,
- const struct firmware *fw)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- const unsigned char *p, *endp;
- int rc = 0;
- int n, n_array;
- char name[33];
-
- tuner_dbg("%s called\n", __func__);
-
- p = fw->data;
- endp = p + fw->size;
-
- if (fw->size < sizeof(name) - 1 + 2 + 2) {
- tuner_err("Error: firmware file %s has invalid size!\n",
- priv->fname);
- goto corrupt;
- }
-
- memcpy(name, p, sizeof(name) - 1);
- name[sizeof(name) - 1] = 0;
- p += sizeof(name) - 1;
-
- priv->firm_version = get_unaligned_le16(p);
- p += 2;
-
- n_array = get_unaligned_le16(p);
- p += 2;
-
- tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
- n_array, priv->fname, name,
- priv->firm_version >> 8, priv->firm_version & 0xff);
-
- priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
- if (priv->firm == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- priv->firm_size = n_array;
-
- n = -1;
- while (p < endp) {
- __u32 type, size;
- v4l2_std_id id;
- __u16 int_freq = 0;
-
- n++;
- if (n >= n_array) {
- tuner_err("More firmware images in file than "
- "were expected!\n");
- goto corrupt;
- }
-
- /* Checks if there's enough bytes to read */
- if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
- goto header;
-
- type = get_unaligned_le32(p);
- p += sizeof(type);
-
- id = get_unaligned_le64(p);
- p += sizeof(id);
-
- if (type & HAS_IF) {
- int_freq = get_unaligned_le16(p);
- p += sizeof(int_freq);
- if (endp - p < sizeof(size))
- goto header;
- }
-
- size = get_unaligned_le32(p);
- p += sizeof(size);
-
- if (!size || size > endp - p) {
- tuner_err("Firmware type ");
- dump_firm_type(type);
- printk("(%x), id %llx is corrupted "
- "(size=%d, expected %d)\n",
- type, (unsigned long long)id,
- (unsigned)(endp - p), size);
- goto corrupt;
- }
-
- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
- if (priv->firm[n].ptr == NULL) {
- tuner_err("Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto err;
- }
- tuner_dbg("Reading firmware type ");
- if (debug) {
- dump_firm_type_and_int_freq(type, int_freq);
- printk("(%x), id %llx, size=%d.\n",
- type, (unsigned long long)id, size);
- }
-
- memcpy(priv->firm[n].ptr, p, size);
- priv->firm[n].type = type;
- priv->firm[n].id = id;
- priv->firm[n].size = size;
- priv->firm[n].int_freq = int_freq;
-
- p += size;
- }
-
- if (n + 1 != priv->firm_size) {
- tuner_err("Firmware file is incomplete!\n");
- goto corrupt;
- }
-
- goto done;
-
-header:
- tuner_err("Firmware header is incomplete!\n");
-corrupt:
- rc = -EINVAL;
- tuner_err("Error: firmware file is corrupted!\n");
-
-err:
- tuner_info("Releasing partially loaded firmware file.\n");
- free_firmware(priv);
-
-done:
- if (rc == 0)
- tuner_dbg("Firmware files loaded.\n");
- else
- priv->state = XC2028_NODEV;
-
- return rc;
-}
-
-static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int i, best_i = -1, best_nr_matches = 0;
- unsigned int type_mask = 0;
-
- tuner_dbg("%s called, want type=", __func__);
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
-
- if (!priv->firm) {
- tuner_err("Error! firmware not loaded\n");
- return -EINVAL;
- }
-
- if (((type & ~SCODE) == 0) && (*id == 0))
- *id = V4L2_STD_PAL;
-
- if (type & BASE)
- type_mask = BASE_TYPES;
- else if (type & SCODE) {
- type &= SCODE_TYPES;
- type_mask = SCODE_TYPES & ~HAS_IF;
- } else if (type & DTV_TYPES)
- type_mask = DTV_TYPES;
- else if (type & STD_SPECIFIC_TYPES)
- type_mask = STD_SPECIFIC_TYPES;
-
- type &= type_mask;
-
- if (!(type & SCODE))
- type_mask = ~0;
-
- /* Seek for exact match */
- for (i = 0; i < priv->firm_size; i++) {
- if ((type == (priv->firm[i].type & type_mask)) &&
- (*id == priv->firm[i].id))
- goto found;
- }
-
- /* Seek for generic video standard match */
- for (i = 0; i < priv->firm_size; i++) {
- v4l2_std_id match_mask;
- int nr_matches;
-
- if (type != (priv->firm[i].type & type_mask))
- continue;
-
- match_mask = *id & priv->firm[i].id;
- if (!match_mask)
- continue;
-
- if ((*id & match_mask) == *id)
- goto found; /* Supports all the requested standards */
-
- nr_matches = hweight64(match_mask);
- if (nr_matches > best_nr_matches) {
- best_nr_matches = nr_matches;
- best_i = i;
- }
- }
-
- if (best_nr_matches > 0) {
- tuner_dbg("Selecting best matching firmware (%d bits) for "
- "type=", best_nr_matches);
- dump_firm_type(type);
- printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
- i = best_i;
- goto found;
- }
-
- /*FIXME: Would make sense to seek for type "hint" match ? */
-
- i = -ENOENT;
- goto ret;
-
-found:
- *id = priv->firm[i].id;
-
-ret:
- tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
- if (debug) {
- dump_firm_type(type);
- printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
- return i;
-}
-
-static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- /* analog side (tuner-core) uses i2c_adap->algo_data.
- * digital side is not guaranteed to have algo_data defined.
- *
- * digital side will always have fe->dvb defined.
- * analog side (tuner-core) doesn't (yet) define fe->dvb.
- */
-
- return (!fe->callback) ? -EINVAL :
- fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv : priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
-}
-
-static int load_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p, *endp, buf[priv->ctrl.max_len];
-
- tuner_dbg("%s called\n", __func__);
-
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
-
- tuner_info("Loading firmware for type=");
- dump_firm_type(priv->firm[pos].type);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- p = priv->firm[pos].ptr;
- endp = p + priv->firm[pos].size;
-
- while (p < endp) {
- __u16 size;
-
- /* Checks if there's enough bytes to read */
- if (p + sizeof(size) > endp) {
- tuner_err("Firmware chunk size is wrong\n");
- return -EINVAL;
- }
-
- size = le16_to_cpu(*(__u16 *) p);
- p += sizeof(size);
-
- if (size == 0xffff)
- return 0;
-
- if (!size) {
- /* Special callback command received */
- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- continue;
- }
- if (size >= 0xff00) {
- switch (size) {
- case 0xff00:
- rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
- if (rc < 0) {
- tuner_err("Error at RESET code %d\n",
- (*p) & 0x7f);
- return -EINVAL;
- }
- break;
- default:
- tuner_info("Invalid RESET code %d\n",
- size & 0x7f);
- return -EINVAL;
-
- }
- continue;
- }
-
- /* Checks for a sleep command */
- if (size & 0x8000) {
- msleep(size & 0x7fff);
- continue;
- }
-
- if ((size + p > endp)) {
- tuner_err("missing bytes: need %d, have %d\n",
- size, (int)(endp - p));
- return -EINVAL;
- }
-
- buf[0] = *p;
- p++;
- size--;
-
- /* Sends message chunks */
- while (size > 0) {
- int len = (size < priv->ctrl.max_len - 1) ?
- size : priv->ctrl.max_len - 1;
-
- memcpy(buf + 1, p, len);
-
- rc = i2c_send(priv, buf, len + 1);
- if (rc < 0) {
- tuner_err("%d returned from send\n", rc);
- return -EINVAL;
- }
-
- p += len;
- size -= len;
- }
-
- /* silently fail if the frontend doesn't support I2C flush */
- rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
- if ((rc < 0) && (rc != -EINVAL)) {
- tuner_err("error executing flush: %d\n", rc);
- return rc;
- }
- }
- return 0;
-}
-
-static int load_scode(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id, __u16 int_freq, int scode)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p;
-
- tuner_dbg("%s called\n", __func__);
-
- if (!int_freq) {
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
- } else {
- for (pos = 0; pos < priv->firm_size; pos++) {
- if ((priv->firm[pos].int_freq == int_freq) &&
- (priv->firm[pos].type & HAS_IF))
- break;
- }
- if (pos == priv->firm_size)
- return -ENOENT;
- }
-
- p = priv->firm[pos].ptr;
-
- if (priv->firm[pos].type & HAS_IF) {
- if (priv->firm[pos].size != 12 * 16 || scode >= 16)
- return -EINVAL;
- p += 12 * scode;
- } else {
- /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
- * has a 2-byte size header in the firmware format. */
- if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
- le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
- return -EINVAL;
- p += 14 * scode + 2;
- }
-
- tuner_info("Loading SCODE for type=");
- dump_firm_type_and_int_freq(priv->firm[pos].type,
- priv->firm[pos].int_freq);
- printk("(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
-
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
- else
- rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
- if (rc < 0)
- return -EIO;
-
- rc = i2c_send(priv, p, 12);
- if (rc < 0)
- return -EIO;
-
- rc = send_seq(priv, {0x00, 0x8c});
- if (rc < 0)
- return -EIO;
-
- return 0;
-}
-
-static int check_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id std, __u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct firmware_properties new_fw;
- int rc, retry_count = 0;
- u16 version, hwmodel;
- v4l2_std_id std0;
-
- tuner_dbg("%s called\n", __func__);
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- if (priv->ctrl.mts && !(type & FM))
- type |= MTS;
-
-retry:
- new_fw.type = type;
- new_fw.id = std;
- new_fw.std_req = std;
- new_fw.scode_table = SCODE | priv->ctrl.scode_table;
- new_fw.scode_nr = 0;
- new_fw.int_freq = int_freq;
-
- tuner_dbg("checking firmware, user requested type=");
- if (debug) {
- dump_firm_type(new_fw.type);
- printk("(%x), id %016llx, ", new_fw.type,
- (unsigned long long)new_fw.std_req);
- if (!int_freq) {
- printk("scode_tbl ");
- dump_firm_type(priv->ctrl.scode_table);
- printk("(%x), ", priv->ctrl.scode_table);
- } else
- printk("int_freq %d, ", new_fw.int_freq);
- printk("scode_nr %d\n", new_fw.scode_nr);
- }
-
- /*
- * No need to reload base firmware if it matches and if the tuner
- * is not at sleep mode
- */
- if ((priv->state == XC2028_ACTIVE) &&
- (((BASE | new_fw.type) & BASE_TYPES) ==
- (priv->cur_fw.type & BASE_TYPES))) {
- tuner_dbg("BASE firmware not changed.\n");
- goto skip_base;
- }
-
- /* Updating BASE - forget about all currently loaded firmware */
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-
- /* Reset is needed before loading firmware */
- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
- if (rc < 0)
- goto fail;
-
- /* BASE firmwares are all std0 */
- std0 = 0;
- rc = load_firmware(fe, BASE | new_fw.type, &std0);
- if (rc < 0) {
- tuner_err("Error %d while loading base firmware\n",
- rc);
- goto fail;
- }
-
- /* Load INIT1, if needed */
- tuner_dbg("Load init1 firmware, if exists\n");
-
- rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
- if (rc == -ENOENT)
- rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
- &std0);
- if (rc < 0 && rc != -ENOENT) {
- tuner_err("Error %d while loading init1 firmware\n",
- rc);
- goto fail;
- }
-
-skip_base:
- /*
- * No need to reload standard specific firmware if base firmware
- * was not reloaded and requested video standards have not changed.
- */
- if (priv->cur_fw.type == (BASE | new_fw.type) &&
- priv->cur_fw.std_req == std) {
- tuner_dbg("Std-specific firmware already loaded.\n");
- goto skip_std_specific;
- }
-
- /* Reloading std-specific firmware forces a SCODE update */
- priv->cur_fw.scode_table = 0;
-
- rc = load_firmware(fe, new_fw.type, &new_fw.id);
- if (rc == -ENOENT)
- rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
-
- if (rc < 0)
- goto fail;
-
-skip_std_specific:
- if (priv->cur_fw.scode_table == new_fw.scode_table &&
- priv->cur_fw.scode_nr == new_fw.scode_nr) {
- tuner_dbg("SCODE firmware already loaded.\n");
- goto check_device;
- }
-
- if (new_fw.type & FM)
- goto check_device;
-
- /* Load SCODE firmware, if exists */
- tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
-
- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
- new_fw.int_freq, new_fw.scode_nr);
-
-check_device:
- if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
- xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
- tuner_err("Unable to read tuner registers.\n");
- goto fail;
- }
-
- tuner_dbg("Device is Xceive %d version %d.%d, "
- "firmware version %d.%d\n",
- hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
- (version & 0xf0) >> 4, version & 0xf);
-
-
- if (priv->ctrl.read_not_reliable)
- goto read_not_reliable;
-
- /* Check firmware version against what we downloaded. */
- if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
- if (!priv->ctrl.read_not_reliable) {
- tuner_err("Incorrect readback of firmware version.\n");
- goto fail;
- } else {
- tuner_err("Returned an incorrect version. However, "
- "read is not reliable enough. Ignoring it.\n");
- hwmodel = 3028;
- }
- }
-
- /* Check that the tuner hardware model remains consistent over time. */
- if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
- priv->hwmodel = hwmodel;
- priv->hwvers = version & 0xff00;
- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
- priv->hwvers != (version & 0xff00)) {
- tuner_err("Read invalid device hardware information - tuner "
- "hung?\n");
- goto fail;
- }
-
-read_not_reliable:
- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
-
- /*
- * By setting BASE in cur_fw.type only after successfully loading all
- * firmwares, we can:
- * 1. Identify that BASE firmware with type=0 has been loaded;
- * 2. Tell whether BASE firmware was just changed the next time through.
- */
- priv->cur_fw.type |= BASE;
- priv->state = XC2028_ACTIVE;
-
- return 0;
-
-fail:
- priv->state = XC2028_SLEEP;
-
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
- if (retry_count < 8) {
- msleep(50);
- retry_count++;
- tuner_dbg("Retrying firmware load\n");
- goto retry;
- }
-
- if (rc == -ENOENT)
- rc = -EINVAL;
- return rc;
-}
-
-static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- u16 frq_lock, signal = 0;
- int rc, i;
-
- tuner_dbg("%s called\n", __func__);
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- mutex_lock(&priv->lock);
-
- /* Sync Lock Indicator */
- for (i = 0; i < 3; i++) {
- rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
- if (rc < 0)
- goto ret;
-
- if (frq_lock)
- break;
- msleep(6);
- }
-
- /* Frequency didn't lock */
- if (frq_lock == 2)
- goto ret;
-
- /* Get SNR of the video signal */
- rc = xc2028_get_reg(priv, XREG_SNR, &signal);
- if (rc < 0)
- goto ret;
-
- /* Signal level is 3 bits only */
-
- signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
-
-ret:
- mutex_unlock(&priv->lock);
-
- *strength = signal;
-
- tuner_dbg("signal strength is %d\n", signal);
-
- return rc;
-}
-
-static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int i, rc;
- u16 frq_lock = 0;
- s16 afc_reg = 0;
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- mutex_lock(&priv->lock);
-
- /* Sync Lock Indicator */
- for (i = 0; i < 3; i++) {
- rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
- if (rc < 0)
- goto ret;
-
- if (frq_lock)
- break;
- msleep(6);
- }
-
- /* Frequency didn't lock */
- if (frq_lock == 2)
- goto ret;
-
- /* Get AFC */
- rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
- if (rc < 0)
- goto ret;
-
- *afc = afc_reg * 15625; /* Hz */
-
- tuner_dbg("AFC is %d Hz\n", *afc);
-
-ret:
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-#define DIV 15625
-
-static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
- enum v4l2_tuner_type new_type,
- unsigned int type,
- v4l2_std_id std,
- u16 int_freq)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc = -EINVAL;
- unsigned char buf[4];
- u32 div, offset = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- tuner_dbg("should set frequency %d kHz\n", freq / 1000);
-
- if (check_firmware(fe, type, std, int_freq) < 0)
- goto ret;
-
- /* On some cases xc2028 can disable video output, if
- * very weak signals are received. By sending a soft
- * reset, this is re-enabled. So, it is better to always
- * send a soft reset before changing channels, to be sure
- * that xc2028 will be in a safe state.
- * Maybe this might also be needed for DTV.
- */
- switch (new_type) {
- case V4L2_TUNER_ANALOG_TV:
- rc = send_seq(priv, {0x00, 0x00});
-
- /* Analog mode requires offset = 0 */
- break;
- case V4L2_TUNER_RADIO:
- /* Radio mode requires offset = 0 */
- break;
- case V4L2_TUNER_DIGITAL_TV:
- /*
- * Digital modes require an offset to adjust to the
- * proper frequency. The offset depends on what
- * firmware version is used.
- */
-
- /*
- * Adjust to the center frequency. This is calculated by the
- * formula: offset = 1.25MHz - BW/2
- * For DTV 7/8, the firmware uses BW = 8000, so it needs a
- * further adjustment to get the frequency center on VHF
- */
-
- /*
- * The firmware DTV78 used to work fine in UHF band (8 MHz
- * bandwidth) but not at all in VHF band (7 MHz bandwidth).
- * The real problem was connected to the formula used to
- * calculate the center frequency offset in VHF band.
- * In fact, removing the 500KHz adjustment fixed the problem.
- * This is coherent to what was implemented for the DTV7
- * firmware.
- * In the end, now the center frequency is the same for all 3
- * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel
- * bandwidth.
- */
-
- if (priv->cur_fw.type & DTV6)
- offset = 1750000;
- else /* DTV7 or DTV8 or DTV78 */
- offset = 2750000;
-
- /*
- * xc3028 additional "magic"
- * Depending on the firmware version, it needs some adjustments
- * to properly centralize the frequency. This seems to be
- * needed to compensate the SCODE table adjustments made by
- * newer firmwares
- */
-
- /*
- * The proper adjustment would be to do it at s-code table.
- * However, this didn't work, as reported by
- * Robert Lowery <rglowery@exemail.com.au>
- */
-
-#if 0
- /*
- * Still need tests for XC3028L (firmware 3.2 or upper)
- * So, for now, let's just comment the per-firmware
- * version of this change. Reports with xc3028l working
- * with and without the lines bellow are welcome
- */
-
- if (priv->firm_version < 0x0302) {
- if (priv->cur_fw.type & DTV7)
- offset += 500000;
- } else {
- if (priv->cur_fw.type & DTV7)
- offset -= 300000;
- else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
- offset += 200000;
- }
-#endif
- }
-
- div = (freq - offset + DIV / 2) / DIV;
-
- /* CMD= Set frequency */
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00});
- else
- rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00});
- if (rc < 0)
- goto ret;
-
- /* Return code shouldn't be checked.
- The reset CLK is needed only with tm6000.
- Driver should work fine even if this fails.
- */
- if (priv->ctrl.msleep)
- msleep(priv->ctrl.msleep);
- do_tuner_callback(fe, XC2028_RESET_CLK, 1);
-
- msleep(10);
-
- buf[0] = 0xff & (div >> 24);
- buf[1] = 0xff & (div >> 16);
- buf[2] = 0xff & (div >> 8);
- buf[3] = 0xff & (div);
-
- rc = i2c_send(priv, buf, sizeof(buf));
- if (rc < 0)
- goto ret;
- msleep(100);
-
- priv->frequency = freq;
-
- tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n",
- buf[0], buf[1], buf[2], buf[3],
- freq / 1000000, (freq % 1000000) / 1000);
-
- rc = 0;
-
-ret:
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static int xc2028_set_analog_freq(struct dvb_frontend *fe,
- struct analog_parameters *p)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- unsigned int type=0;
-
- tuner_dbg("%s called\n", __func__);
-
- if (p->mode == V4L2_TUNER_RADIO) {
- type |= FM;
- if (priv->ctrl.input1)
- type |= INPUT1;
- return generic_set_freq(fe, (625l * p->frequency) / 10,
- V4L2_TUNER_RADIO, type, 0, 0);
- }
-
- /* if std is not defined, choose one */
- if (!p->std)
- p->std = V4L2_STD_MN;
-
- /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
- if (!(p->std & V4L2_STD_MN))
- type |= F8MHZ;
-
- /* Add audio hack to std mask */
- p->std |= parse_audio_std_option();
-
- return generic_set_freq(fe, 62500l * p->frequency,
- V4L2_TUNER_ANALOG_TV, type, p->std, 0);
-}
-
-static int xc2028_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- u32 bw = c->bandwidth_hz;
- struct xc2028_data *priv = fe->tuner_priv;
- int rc;
- unsigned int type = 0;
- u16 demod = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- switch (delsys) {
- case SYS_DVBT:
- case SYS_DVBT2:
- /*
- * The only countries with 6MHz seem to be Taiwan/Uruguay.
- * Both seem to require QAM firmware for OFDM decoding
- * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com>
- */
- if (bw <= 6000000)
- type |= QAM;
-
- switch (priv->ctrl.type) {
- case XC2028_D2633:
- type |= D2633;
- break;
- case XC2028_D2620:
- type |= D2620;
- break;
- case XC2028_AUTO:
- default:
- /* Zarlink seems to need D2633 */
- if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
- type |= D2633;
- else
- type |= D2620;
- }
- break;
- case SYS_ATSC:
- /* The only ATSC firmware (at least on v2.7) is D2633 */
- type |= ATSC | D2633;
- break;
- /* DVB-S and pure QAM (FE_QAM) are not supported */
- default:
- return -EINVAL;
- }
-
- if (bw <= 6000000) {
- type |= DTV6;
- priv->ctrl.vhfbw7 = 0;
- priv->ctrl.uhfbw8 = 0;
- } else if (bw <= 7000000) {
- if (c->frequency < 470000000)
- priv->ctrl.vhfbw7 = 1;
- else
- priv->ctrl.uhfbw8 = 0;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
- type |= F8MHZ;
- } else {
- if (c->frequency < 470000000)
- priv->ctrl.vhfbw7 = 0;
- else
- priv->ctrl.uhfbw8 = 1;
- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
- type |= F8MHZ;
- }
-
- /* All S-code tables need a 200kHz shift */
- if (priv->ctrl.demod) {
- demod = priv->ctrl.demod;
-
- /*
- * Newer firmwares require a 200 kHz offset only for ATSC
- */
- if (type == ATSC || priv->firm_version < 0x0302)
- demod += 200;
- /*
- * The DTV7 S-code table needs a 700 kHz shift.
- *
- * DTV7 is only used in Australia. Germany or Italy may also
- * use this firmware after initialization, but a tune to a UHF
- * channel should then cause DTV78 to be used.
- *
- * Unfortunately, on real-field tests, the s-code offset
- * didn't work as expected, as reported by
- * Robert Lowery <rglowery@exemail.com.au>
- */
- }
-
- return generic_set_freq(fe, c->frequency,
- V4L2_TUNER_DIGITAL_TV, type, 0, demod);
-}
-
-static int xc2028_sleep(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc;
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- /* Avoid firmware reload on slow devices or if PM disabled */
- if (no_poweroff || priv->ctrl.disable_power_mgmt)
- return 0;
-
- tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
- if (debug > 1) {
- tuner_dbg("Printing sleep stack trace:\n");
- dump_stack();
- }
-
- mutex_lock(&priv->lock);
-
- if (priv->firm_version < 0x0202)
- rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00});
- else
- rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
-
- priv->state = XC2028_SLEEP;
-
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static int xc2028_dvb_release(struct dvb_frontend *fe)
-{
- struct xc2028_data *priv = fe->tuner_priv;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&xc2028_list_mutex);
-
- /* only perform final cleanup if this is the last instance */
- if (hybrid_tuner_report_instance_count(priv) == 1) {
- free_firmware(priv);
- kfree(priv->ctrl.fname);
- priv->ctrl.fname = NULL;
- }
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&xc2028_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- int rc;
-
- tuner_dbg("%s called\n", __func__);
-
- rc = check_device_status(priv);
- if (rc < 0)
- return rc;
-
- *frequency = priv->frequency;
-
- return 0;
-}
-
-static void load_firmware_cb(const struct firmware *fw,
- void *context)
-{
- struct dvb_frontend *fe = context;
- struct xc2028_data *priv = fe->tuner_priv;
- int rc;
-
- tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
- if (!fw) {
- tuner_err("Could not load firmware %s.\n", priv->fname);
- priv->state = XC2028_NODEV;
- return;
- }
-
- rc = load_all_firmwares(fe, fw);
-
- release_firmware(fw);
-
- if (rc < 0)
- return;
- priv->state = XC2028_SLEEP;
-}
-
-static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct xc2028_data *priv = fe->tuner_priv;
- struct xc2028_ctrl *p = priv_cfg;
- int rc = 0;
-
- tuner_dbg("%s called\n", __func__);
-
- mutex_lock(&priv->lock);
-
- /*
- * Copy the config data.
- * For the firmware name, keep a local copy of the string,
- * in order to avoid troubles during device release.
- */
- if (priv->ctrl.fname)
- kfree(priv->ctrl.fname);
- memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
- if (p->fname) {
- priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
- if (priv->ctrl.fname == NULL)
- rc = -ENOMEM;
- }
-
- /*
- * If firmware name changed, frees firmware. As free_firmware will
- * reset the status to NO_FIRMWARE, this forces a new request_firmware
- */
- if (!firmware_name[0] && p->fname &&
- priv->fname && strcmp(p->fname, priv->fname))
- free_firmware(priv);
-
- if (priv->ctrl.max_len < 9)
- priv->ctrl.max_len = 13;
-
- if (priv->state == XC2028_NO_FIRMWARE) {
- if (!firmware_name[0])
- priv->fname = priv->ctrl.fname;
- else
- priv->fname = firmware_name;
-
- rc = request_firmware_nowait(THIS_MODULE, 1,
- priv->fname,
- priv->i2c_props.adap->dev.parent,
- GFP_KERNEL,
- fe, load_firmware_cb);
- if (rc < 0) {
- tuner_err("Failed to request firmware %s\n",
- priv->fname);
- priv->state = XC2028_NODEV;
- }
- priv->state = XC2028_WAITING_FIRMWARE;
- }
- mutex_unlock(&priv->lock);
-
- return rc;
-}
-
-static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
- .info = {
- .name = "Xceive XC3028",
- .frequency_min = 42000000,
- .frequency_max = 864000000,
- .frequency_step = 50000,
- },
-
- .set_config = xc2028_set_config,
- .set_analog_params = xc2028_set_analog_freq,
- .release = xc2028_dvb_release,
- .get_frequency = xc2028_get_frequency,
- .get_rf_strength = xc2028_signal,
- .get_afc = xc2028_get_afc,
- .set_params = xc2028_set_params,
- .sleep = xc2028_sleep,
-};
-
-struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
- struct xc2028_config *cfg)
-{
- struct xc2028_data *priv;
- int instance;
-
- if (debug)
- printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
-
- if (NULL == cfg)
- return NULL;
-
- if (!fe) {
- printk(KERN_ERR "xc2028: No frontend!\n");
- return NULL;
- }
-
- mutex_lock(&xc2028_list_mutex);
-
- instance = hybrid_tuner_request_state(struct xc2028_data, priv,
- hybrid_tuner_instance_list,
- cfg->i2c_adap, cfg->i2c_addr,
- "xc2028");
- switch (instance) {
- case 0:
- /* memory allocation failure */
- goto fail;
- break;
- case 1:
- /* new tuner instance */
- priv->ctrl.max_len = 13;
-
- mutex_init(&priv->lock);
-
- fe->tuner_priv = priv;
- break;
- case 2:
- /* existing tuner instance */
- fe->tuner_priv = priv;
- break;
- }
-
- memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
- sizeof(xc2028_dvb_tuner_ops));
-
- tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
-
- if (cfg->ctrl)
- xc2028_set_config(fe, cfg->ctrl);
-
- mutex_unlock(&xc2028_list_mutex);
-
- return fe;
-fail:
- mutex_unlock(&xc2028_list_mutex);
-
- xc2028_dvb_release(fe);
- return NULL;
-}
-
-EXPORT_SYMBOL(xc2028_attach);
-
-MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
-MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
-MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c
deleted file mode 100644
index 68397110b7d9..000000000000
--- a/drivers/media/common/tuners/xc4000.c
+++ /dev/null
@@ -1,1758 +0,0 @@
-/*
- * Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Xceive Corporation
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
- * Copyright (c) 2009 Davide Ferri <d.ferri@zero11.it>
- * Copyright (c) 2010 Istvan Varga <istvan_v@mailbox.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <linux/dvb/frontend.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-#include <asm/unaligned.h>
-
-#include "dvb_frontend.h"
-
-#include "xc4000.h"
-#include "tuner-i2c.h"
-#include "tuner-xc2028-types.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off)).");
-
-static int no_poweroff;
-module_param(no_poweroff, int, 0644);
-MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, "
- "0 (default): use device-specific default mode).");
-
-static int audio_std;
-module_param(audio_std, int, 0644);
-MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly "
- "needs to know what audio standard is needed for some video standards "
- "with audio A2 or NICAM. The valid settings are a sum of:\n"
- " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n"
- " 2: use A2 instead of NICAM or BTSC\n"
- " 4: use SECAM/K3 instead of K1\n"
- " 8: use PAL-D/K audio for SECAM-D/K\n"
- "16: use FM radio input 1 instead of input 2\n"
- "32: use mono audio (the lower three bits are ignored)");
-
-static char firmware_name[30];
-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
- "default firmware name.");
-
-static DEFINE_MUTEX(xc4000_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_INFO "%s: " fmt, "xc4000", ## arg)
-
-/* struct for storing firmware table */
-struct firmware_description {
- unsigned int type;
- v4l2_std_id id;
- __u16 int_freq;
- unsigned char *ptr;
- unsigned int size;
-};
-
-struct firmware_properties {
- unsigned int type;
- v4l2_std_id id;
- v4l2_std_id std_req;
- __u16 int_freq;
- unsigned int scode_table;
- int scode_nr;
-};
-
-struct xc4000_priv {
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
- struct firmware_description *firm;
- int firm_size;
- u32 if_khz;
- u32 freq_hz;
- u32 bandwidth;
- u8 video_standard;
- u8 rf_mode;
- u8 default_pm;
- u8 dvb_amplitude;
- u8 set_smoothedcvbs;
- u8 ignore_i2c_write_errors;
- __u16 firm_version;
- struct firmware_properties cur_fw;
- __u16 hwmodel;
- __u16 hwvers;
- struct mutex lock;
-};
-
-#define XC4000_AUDIO_STD_B 1
-#define XC4000_AUDIO_STD_A2 2
-#define XC4000_AUDIO_STD_K3 4
-#define XC4000_AUDIO_STD_L 8
-#define XC4000_AUDIO_STD_INPUT1 16
-#define XC4000_AUDIO_STD_MONO 32
-
-#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw"
-
-/* Misc Defines */
-#define MAX_TV_STANDARD 24
-#define XC_MAX_I2C_WRITE_LENGTH 64
-#define XC_POWERED_DOWN 0x80000000U
-
-/* Signal Types */
-#define XC_RF_MODE_AIR 0
-#define XC_RF_MODE_CABLE 1
-
-/* Product id */
-#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
-#define XC_PRODUCT_ID_XC4000 0x0FA0
-#define XC_PRODUCT_ID_XC4100 0x1004
-
-/* Registers (Write-only) */
-#define XREG_INIT 0x00
-#define XREG_VIDEO_MODE 0x01
-#define XREG_AUDIO_MODE 0x02
-#define XREG_RF_FREQ 0x03
-#define XREG_D_CODE 0x04
-#define XREG_DIRECTSITTING_MODE 0x05
-#define XREG_SEEK_MODE 0x06
-#define XREG_POWER_DOWN 0x08
-#define XREG_SIGNALSOURCE 0x0A
-#define XREG_SMOOTHEDCVBS 0x0E
-#define XREG_AMPLITUDE 0x10
-
-/* Registers (Read-only) */
-#define XREG_ADC_ENV 0x00
-#define XREG_QUALITY 0x01
-#define XREG_FRAME_LINES 0x02
-#define XREG_HSYNC_FREQ 0x03
-#define XREG_LOCK 0x04
-#define XREG_FREQ_ERROR 0x05
-#define XREG_SNR 0x06
-#define XREG_VERSION 0x07
-#define XREG_PRODUCT_ID 0x08
-#define XREG_SIGNAL_LEVEL 0x0A
-#define XREG_NOISE_LEVEL 0x0B
-
-/*
- Basic firmware description. This will remain with
- the driver for documentation purposes.
-
- This represents an I2C firmware file encoded as a
- string of unsigned char. Format is as follows:
-
- char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
- char[1 ]=len0_LSB -> length of first write transaction
- char[2 ]=data0 -> first byte to be sent
- char[3 ]=data1
- char[4 ]=data2
- char[ ]=...
- char[M ]=dataN -> last byte to be sent
- char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
- char[M+2]=len1_LSB -> length of second write transaction
- char[M+3]=data0
- char[M+4]=data1
- ...
- etc.
-
- The [len] value should be interpreted as follows:
-
- len= len_MSB _ len_LSB
- len=1111_1111_1111_1111 : End of I2C_SEQUENCE
- len=0000_0000_0000_0000 : Reset command: Do hardware reset
- len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
- len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
-
- For the RESET and WAIT commands, the two following bytes will contain
- immediately the length of the following transaction.
-*/
-
-struct XC_TV_STANDARD {
- const char *Name;
- u16 audio_mode;
- u16 video_mode;
- u16 int_freq;
-};
-
-/* Tuner standards */
-#define XC4000_MN_NTSC_PAL_BTSC 0
-#define XC4000_MN_NTSC_PAL_A2 1
-#define XC4000_MN_NTSC_PAL_EIAJ 2
-#define XC4000_MN_NTSC_PAL_Mono 3
-#define XC4000_BG_PAL_A2 4
-#define XC4000_BG_PAL_NICAM 5
-#define XC4000_BG_PAL_MONO 6
-#define XC4000_I_PAL_NICAM 7
-#define XC4000_I_PAL_NICAM_MONO 8
-#define XC4000_DK_PAL_A2 9
-#define XC4000_DK_PAL_NICAM 10
-#define XC4000_DK_PAL_MONO 11
-#define XC4000_DK_SECAM_A2DK1 12
-#define XC4000_DK_SECAM_A2LDK3 13
-#define XC4000_DK_SECAM_A2MONO 14
-#define XC4000_DK_SECAM_NICAM 15
-#define XC4000_L_SECAM_NICAM 16
-#define XC4000_LC_SECAM_NICAM 17
-#define XC4000_DTV6 18
-#define XC4000_DTV8 19
-#define XC4000_DTV7_8 20
-#define XC4000_DTV7 21
-#define XC4000_FM_Radio_INPUT2 22
-#define XC4000_FM_Radio_INPUT1 23
-
-static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = {
- {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500},
- {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600},
- {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500},
- {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500},
- {"B/G-PAL-A2", 0x0000, 0x8159, 5640},
- {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740},
- {"B/G-PAL-MONO", 0x0078, 0x8159, 5500},
- {"I-PAL-NICAM", 0x0080, 0x8049, 6240},
- {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000},
- {"D/K-PAL-A2", 0x0000, 0x8049, 6380},
- {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200},
- {"D/K-PAL-MONO", 0x0078, 0x8049, 6500},
- {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340},
- {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000},
- {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500},
- {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200},
- {"L-SECAM-NICAM", 0x8080, 0x0009, 6200},
- {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200},
- {"DTV6", 0x00C0, 0x8002, 0},
- {"DTV8", 0x00C0, 0x800B, 0},
- {"DTV7/8", 0x00C0, 0x801B, 0},
- {"DTV7", 0x00C0, 0x8007, 0},
- {"FM Radio-INPUT2", 0x0008, 0x9800, 10700},
- {"FM Radio-INPUT1", 0x0008, 0x9000, 10700}
-};
-
-static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val);
-static int xc4000_tuner_reset(struct dvb_frontend *fe);
-static void xc_debug_dump(struct xc4000_priv *priv);
-
-static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
-{
- struct i2c_msg msg = { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = buf, .len = len };
- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
- if (priv->ignore_i2c_write_errors == 0) {
- printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n",
- len);
- if (len == 4) {
- printk(KERN_ERR "bytes %02x %02x %02x %02x\n", buf[0],
- buf[1], buf[2], buf[3]);
- }
- return -EREMOTEIO;
- }
- }
- return 0;
-}
-
-static int xc4000_tuner_reset(struct dvb_frontend *fe)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- if (fe->callback) {
- ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv :
- priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER,
- XC4000_TUNER_RESET, 0);
- if (ret) {
- printk(KERN_ERR "xc4000: reset failed\n");
- return -EREMOTEIO;
- }
- } else {
- printk(KERN_ERR "xc4000: no tuner reset callback function, "
- "fatal\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
-{
- u8 buf[4];
- int result;
-
- buf[0] = (regAddr >> 8) & 0xFF;
- buf[1] = regAddr & 0xFF;
- buf[2] = (i2cData >> 8) & 0xFF;
- buf[3] = i2cData & 0xFF;
- result = xc_send_i2c_data(priv, buf, 4);
-
- return result;
-}
-
-static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
-
- int i, nbytes_to_send, result;
- unsigned int len, pos, index;
- u8 buf[XC_MAX_I2C_WRITE_LENGTH];
-
- index = 0;
- while ((i2c_sequence[index] != 0xFF) ||
- (i2c_sequence[index + 1] != 0xFF)) {
- len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
- if (len == 0x0000) {
- /* RESET command */
- /* NOTE: this is ignored, as the reset callback was */
- /* already called by check_firmware() */
- index += 2;
- } else if (len & 0x8000) {
- /* WAIT command */
- msleep(len & 0x7FFF);
- index += 2;
- } else {
- /* Send i2c data whilst ensuring individual transactions
- * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
- */
- index += 2;
- buf[0] = i2c_sequence[index];
- buf[1] = i2c_sequence[index + 1];
- pos = 2;
- while (pos < len) {
- if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
- nbytes_to_send =
- XC_MAX_I2C_WRITE_LENGTH;
- else
- nbytes_to_send = (len - pos + 2);
- for (i = 2; i < nbytes_to_send; i++) {
- buf[i] = i2c_sequence[index + pos +
- i - 2];
- }
- result = xc_send_i2c_data(priv, buf,
- nbytes_to_send);
-
- if (result != 0)
- return result;
-
- pos += nbytes_to_send - 2;
- }
- index += len;
- }
- }
- return 0;
-}
-
-static int xc_set_tv_standard(struct xc4000_priv *priv,
- u16 video_mode, u16 audio_mode)
-{
- int ret;
- dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode);
- dprintk(1, "%s() Standard = %s\n",
- __func__,
- xc4000_standard[priv->video_standard].Name);
-
- /* Don't complain when the request fails because of i2c stretching */
- priv->ignore_i2c_write_errors = 1;
-
- ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode);
- if (ret == 0)
- ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);
-
- priv->ignore_i2c_write_errors = 0;
-
- return ret;
-}
-
-static int xc_set_signal_source(struct xc4000_priv *priv, u16 rf_mode)
-{
- dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
- rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
-
- if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
- rf_mode = XC_RF_MODE_CABLE;
- printk(KERN_ERR
- "%s(), Invalid mode, defaulting to CABLE",
- __func__);
- }
- return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
-}
-
-static const struct dvb_tuner_ops xc4000_tuner_ops;
-
-static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz)
-{
- u16 freq_code;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- if ((freq_hz > xc4000_tuner_ops.info.frequency_max) ||
- (freq_hz < xc4000_tuner_ops.info.frequency_min))
- return -EINVAL;
-
- freq_code = (u16)(freq_hz / 15625);
-
- /* WAS: Starting in firmware version 1.1.44, Xceive recommends using the
- FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
- only be used for fast scanning for channel lock) */
- /* WAS: XREG_FINERFREQ */
- return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
-}
-
-static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope)
-{
- return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope);
-}
-
-static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz)
-{
- int result;
- u16 regData;
- u32 tmp;
-
- result = xc4000_readreg(priv, XREG_FREQ_ERROR, &regData);
- if (result != 0)
- return result;
-
- tmp = (u32)regData & 0xFFFFU;
- tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp);
- (*freq_error_hz) = tmp * 15625;
- return result;
-}
-
-static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status)
-{
- return xc4000_readreg(priv, XREG_LOCK, lock_status);
-}
-
-static int xc_get_version(struct xc4000_priv *priv,
- u8 *hw_majorversion, u8 *hw_minorversion,
- u8 *fw_majorversion, u8 *fw_minorversion)
-{
- u16 data;
- int result;
-
- result = xc4000_readreg(priv, XREG_VERSION, &data);
- if (result != 0)
- return result;
-
- (*hw_majorversion) = (data >> 12) & 0x0F;
- (*hw_minorversion) = (data >> 8) & 0x0F;
- (*fw_majorversion) = (data >> 4) & 0x0F;
- (*fw_minorversion) = data & 0x0F;
-
- return 0;
-}
-
-static int xc_get_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz)
-{
- u16 regData;
- int result;
-
- result = xc4000_readreg(priv, XREG_HSYNC_FREQ, &regData);
- if (result != 0)
- return result;
-
- (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
- return result;
-}
-
-static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines)
-{
- return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines);
-}
-
-static int xc_get_quality(struct xc4000_priv *priv, u16 *quality)
-{
- return xc4000_readreg(priv, XREG_QUALITY, quality);
-}
-
-static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal)
-{
- return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal);
-}
-
-static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise)
-{
- return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise);
-}
-
-static u16 xc_wait_for_lock(struct xc4000_priv *priv)
-{
- u16 lock_state = 0;
- int watchdog_count = 40;
-
- while ((lock_state == 0) && (watchdog_count > 0)) {
- xc_get_lock_status(priv, &lock_state);
- if (lock_state != 1) {
- msleep(5);
- watchdog_count--;
- }
- }
- return lock_state;
-}
-
-static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz)
-{
- int found = 1;
- int result;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- /* Don't complain when the request fails because of i2c stretching */
- priv->ignore_i2c_write_errors = 1;
- result = xc_set_rf_frequency(priv, freq_hz);
- priv->ignore_i2c_write_errors = 0;
-
- if (result != 0)
- return 0;
-
- /* wait for lock only in analog TV mode */
- if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) {
- if (xc_wait_for_lock(priv) != 1)
- found = 0;
- }
-
- /* Wait for stats to stabilize.
- * Frame Lines needs two frame times after initial lock
- * before it is valid.
- */
- msleep(debug ? 100 : 10);
-
- if (debug)
- xc_debug_dump(priv);
-
- return found;
-}
-
-static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
-{
- u8 buf[2] = { reg >> 8, reg & 0xff };
- u8 bval[2] = { 0, 0 };
- struct i2c_msg msg[2] = {
- { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = &buf[0], .len = 2 },
- { .addr = priv->i2c_props.addr,
- .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
- };
-
- if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
- printk(KERN_ERR "xc4000: I2C read failed\n");
- return -EREMOTEIO;
- }
-
- *val = (bval[0] << 8) | bval[1];
- return 0;
-}
-
-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
-{
- if (type & BASE)
- printk(KERN_CONT "BASE ");
- if (type & INIT1)
- printk(KERN_CONT "INIT1 ");
- if (type & F8MHZ)
- printk(KERN_CONT "F8MHZ ");
- if (type & MTS)
- printk(KERN_CONT "MTS ");
- if (type & D2620)
- printk(KERN_CONT "D2620 ");
- if (type & D2633)
- printk(KERN_CONT "D2633 ");
- if (type & DTV6)
- printk(KERN_CONT "DTV6 ");
- if (type & QAM)
- printk(KERN_CONT "QAM ");
- if (type & DTV7)
- printk(KERN_CONT "DTV7 ");
- if (type & DTV78)
- printk(KERN_CONT "DTV78 ");
- if (type & DTV8)
- printk(KERN_CONT "DTV8 ");
- if (type & FM)
- printk(KERN_CONT "FM ");
- if (type & INPUT1)
- printk(KERN_CONT "INPUT1 ");
- if (type & LCD)
- printk(KERN_CONT "LCD ");
- if (type & NOGD)
- printk(KERN_CONT "NOGD ");
- if (type & MONO)
- printk(KERN_CONT "MONO ");
- if (type & ATSC)
- printk(KERN_CONT "ATSC ");
- if (type & IF)
- printk(KERN_CONT "IF ");
- if (type & LG60)
- printk(KERN_CONT "LG60 ");
- if (type & ATI638)
- printk(KERN_CONT "ATI638 ");
- if (type & OREN538)
- printk(KERN_CONT "OREN538 ");
- if (type & OREN36)
- printk(KERN_CONT "OREN36 ");
- if (type & TOYOTA388)
- printk(KERN_CONT "TOYOTA388 ");
- if (type & TOYOTA794)
- printk(KERN_CONT "TOYOTA794 ");
- if (type & DIBCOM52)
- printk(KERN_CONT "DIBCOM52 ");
- if (type & ZARLINK456)
- printk(KERN_CONT "ZARLINK456 ");
- if (type & CHINA)
- printk(KERN_CONT "CHINA ");
- if (type & F6MHZ)
- printk(KERN_CONT "F6MHZ ");
- if (type & INPUT2)
- printk(KERN_CONT "INPUT2 ");
- if (type & SCODE)
- printk(KERN_CONT "SCODE ");
- if (type & HAS_IF)
- printk(KERN_CONT "HAS_IF_%d ", int_freq);
-}
-
-static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- int i, best_i = -1;
- unsigned int best_nr_diffs = 255U;
-
- if (!priv->firm) {
- printk(KERN_ERR "Error! firmware not loaded\n");
- return -EINVAL;
- }
-
- if (((type & ~SCODE) == 0) && (*id == 0))
- *id = V4L2_STD_PAL;
-
- /* Seek for generic video standard match */
- for (i = 0; i < priv->firm_size; i++) {
- v4l2_std_id id_diff_mask =
- (priv->firm[i].id ^ (*id)) & (*id);
- unsigned int type_diff_mask =
- (priv->firm[i].type ^ type)
- & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE);
- unsigned int nr_diffs;
-
- if (type_diff_mask
- & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE))
- continue;
-
- nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask);
- if (!nr_diffs) /* Supports all the requested standards */
- goto found;
-
- if (nr_diffs < best_nr_diffs) {
- best_nr_diffs = nr_diffs;
- best_i = i;
- }
- }
-
- /* FIXME: Would make sense to seek for type "hint" match ? */
- if (best_i < 0) {
- i = -ENOENT;
- goto ret;
- }
-
- if (best_nr_diffs > 0U) {
- printk(KERN_WARNING
- "Selecting best matching firmware (%u bits differ) for "
- "type=(%x), id %016llx:\n",
- best_nr_diffs, type, (unsigned long long)*id);
- i = best_i;
- }
-
-found:
- *id = priv->firm[i].id;
-
-ret:
- if (debug) {
- printk(KERN_DEBUG "%s firmware for type=",
- (i < 0) ? "Can't find" : "Found");
- dump_firm_type(type);
- printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id);
- }
- return i;
-}
-
-static int load_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p;
-
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
-
- p = priv->firm[pos].ptr;
-
- /* Don't complain when the request fails because of i2c stretching */
- priv->ignore_i2c_write_errors = 1;
-
- rc = xc_load_i2c_sequence(fe, p);
-
- priv->ignore_i2c_write_errors = 0;
-
- return rc;
-}
-
-static int xc4000_fwupload(struct dvb_frontend *fe)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- const struct firmware *fw = NULL;
- const unsigned char *p, *endp;
- int rc = 0;
- int n, n_array;
- char name[33];
- const char *fname;
-
- if (firmware_name[0] != '\0')
- fname = firmware_name;
- else
- fname = XC4000_DEFAULT_FIRMWARE;
-
- dprintk(1, "Reading firmware %s\n", fname);
- rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
- if (rc < 0) {
- if (rc == -ENOENT)
- printk(KERN_ERR "Error: firmware %s not found.\n", fname);
- else
- printk(KERN_ERR "Error %d while requesting firmware %s\n",
- rc, fname);
-
- return rc;
- }
- p = fw->data;
- endp = p + fw->size;
-
- if (fw->size < sizeof(name) - 1 + 2 + 2) {
- printk(KERN_ERR "Error: firmware file %s has invalid size!\n",
- fname);
- goto corrupt;
- }
-
- memcpy(name, p, sizeof(name) - 1);
- name[sizeof(name) - 1] = '\0';
- p += sizeof(name) - 1;
-
- priv->firm_version = get_unaligned_le16(p);
- p += 2;
-
- n_array = get_unaligned_le16(p);
- p += 2;
-
- dprintk(1, "Loading %d firmware images from %s, type: %s, ver %d.%d\n",
- n_array, fname, name,
- priv->firm_version >> 8, priv->firm_version & 0xff);
-
- priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
- if (priv->firm == NULL) {
- printk(KERN_ERR "Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto done;
- }
- priv->firm_size = n_array;
-
- n = -1;
- while (p < endp) {
- __u32 type, size;
- v4l2_std_id id;
- __u16 int_freq = 0;
-
- n++;
- if (n >= n_array) {
- printk(KERN_ERR "More firmware images in file than "
- "were expected!\n");
- goto corrupt;
- }
-
- /* Checks if there's enough bytes to read */
- if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
- goto header;
-
- type = get_unaligned_le32(p);
- p += sizeof(type);
-
- id = get_unaligned_le64(p);
- p += sizeof(id);
-
- if (type & HAS_IF) {
- int_freq = get_unaligned_le16(p);
- p += sizeof(int_freq);
- if (endp - p < sizeof(size))
- goto header;
- }
-
- size = get_unaligned_le32(p);
- p += sizeof(size);
-
- if (!size || size > endp - p) {
- printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n",
- type, (unsigned long long)id,
- (unsigned)(endp - p), size);
- goto corrupt;
- }
-
- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
- if (priv->firm[n].ptr == NULL) {
- printk(KERN_ERR "Not enough memory to load firmware file.\n");
- rc = -ENOMEM;
- goto done;
- }
-
- if (debug) {
- printk(KERN_DEBUG "Reading firmware type ");
- dump_firm_type_and_int_freq(type, int_freq);
- printk(KERN_DEBUG "(%x), id %llx, size=%d.\n",
- type, (unsigned long long)id, size);
- }
-
- memcpy(priv->firm[n].ptr, p, size);
- priv->firm[n].type = type;
- priv->firm[n].id = id;
- priv->firm[n].size = size;
- priv->firm[n].int_freq = int_freq;
-
- p += size;
- }
-
- if (n + 1 != priv->firm_size) {
- printk(KERN_ERR "Firmware file is incomplete!\n");
- goto corrupt;
- }
-
- goto done;
-
-header:
- printk(KERN_ERR "Firmware header is incomplete!\n");
-corrupt:
- rc = -EINVAL;
- printk(KERN_ERR "Error: firmware file is corrupted!\n");
-
-done:
- release_firmware(fw);
- if (rc == 0)
- dprintk(1, "Firmware files loaded.\n");
-
- return rc;
-}
-
-static int load_scode(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id *id, __u16 int_freq, int scode)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- int pos, rc;
- unsigned char *p;
- u8 scode_buf[13];
- u8 indirect_mode[5];
-
- dprintk(1, "%s called int_freq=%d\n", __func__, int_freq);
-
- if (!int_freq) {
- pos = seek_firmware(fe, type, id);
- if (pos < 0)
- return pos;
- } else {
- for (pos = 0; pos < priv->firm_size; pos++) {
- if ((priv->firm[pos].int_freq == int_freq) &&
- (priv->firm[pos].type & HAS_IF))
- break;
- }
- if (pos == priv->firm_size)
- return -ENOENT;
- }
-
- p = priv->firm[pos].ptr;
-
- if (priv->firm[pos].size != 12 * 16 || scode >= 16)
- return -EINVAL;
- p += 12 * scode;
-
- if (debug) {
- tuner_info("Loading SCODE for type=");
- dump_firm_type_and_int_freq(priv->firm[pos].type,
- priv->firm[pos].int_freq);
- printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
- (unsigned long long)*id);
- }
-
- scode_buf[0] = 0x00;
- memcpy(&scode_buf[1], p, 12);
-
- /* Enter direct-mode */
- rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0);
- if (rc < 0) {
- printk(KERN_ERR "failed to put device into direct mode!\n");
- return -EIO;
- }
-
- rc = xc_send_i2c_data(priv, scode_buf, 13);
- if (rc != 0) {
- /* Even if the send failed, make sure we set back to indirect
- mode */
- printk(KERN_ERR "Failed to set scode %d\n", rc);
- }
-
- /* Switch back to indirect-mode */
- memset(indirect_mode, 0, sizeof(indirect_mode));
- indirect_mode[4] = 0x88;
- xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode));
- msleep(10);
-
- return 0;
-}
-
-static int check_firmware(struct dvb_frontend *fe, unsigned int type,
- v4l2_std_id std, __u16 int_freq)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- struct firmware_properties new_fw;
- int rc = 0, is_retry = 0;
- u16 hwmodel;
- v4l2_std_id std0;
- u8 hw_major, hw_minor, fw_major, fw_minor;
-
- dprintk(1, "%s called\n", __func__);
-
- if (!priv->firm) {
- rc = xc4000_fwupload(fe);
- if (rc < 0)
- return rc;
- }
-
-retry:
- new_fw.type = type;
- new_fw.id = std;
- new_fw.std_req = std;
- new_fw.scode_table = SCODE;
- new_fw.scode_nr = 0;
- new_fw.int_freq = int_freq;
-
- dprintk(1, "checking firmware, user requested type=");
- if (debug) {
- dump_firm_type(new_fw.type);
- printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
- (unsigned long long)new_fw.std_req);
- if (!int_freq)
- printk(KERN_CONT "scode_tbl ");
- else
- printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
- printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
- }
-
- /* No need to reload base firmware if it matches */
- if (priv->cur_fw.type & BASE) {
- dprintk(1, "BASE firmware not changed.\n");
- goto skip_base;
- }
-
- /* Updating BASE - forget about all currently loaded firmware */
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
-
- /* Reset is needed before loading firmware */
- rc = xc4000_tuner_reset(fe);
- if (rc < 0)
- goto fail;
-
- /* BASE firmwares are all std0 */
- std0 = 0;
- rc = load_firmware(fe, BASE, &std0);
- if (rc < 0) {
- printk(KERN_ERR "Error %d while loading base firmware\n", rc);
- goto fail;
- }
-
- /* Load INIT1, if needed */
- dprintk(1, "Load init1 firmware, if exists\n");
-
- rc = load_firmware(fe, BASE | INIT1, &std0);
- if (rc == -ENOENT)
- rc = load_firmware(fe, BASE | INIT1, &std0);
- if (rc < 0 && rc != -ENOENT) {
- tuner_err("Error %d while loading init1 firmware\n",
- rc);
- goto fail;
- }
-
-skip_base:
- /*
- * No need to reload standard specific firmware if base firmware
- * was not reloaded and requested video standards have not changed.
- */
- if (priv->cur_fw.type == (BASE | new_fw.type) &&
- priv->cur_fw.std_req == std) {
- dprintk(1, "Std-specific firmware already loaded.\n");
- goto skip_std_specific;
- }
-
- /* Reloading std-specific firmware forces a SCODE update */
- priv->cur_fw.scode_table = 0;
-
- /* Load the standard firmware */
- rc = load_firmware(fe, new_fw.type, &new_fw.id);
-
- if (rc < 0)
- goto fail;
-
-skip_std_specific:
- if (priv->cur_fw.scode_table == new_fw.scode_table &&
- priv->cur_fw.scode_nr == new_fw.scode_nr) {
- dprintk(1, "SCODE firmware already loaded.\n");
- goto check_device;
- }
-
- /* Load SCODE firmware, if exists */
- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
- new_fw.int_freq, new_fw.scode_nr);
- if (rc != 0)
- dprintk(1, "load scode failed %d\n", rc);
-
-check_device:
- rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel);
-
- if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major,
- &fw_minor) != 0) {
- printk(KERN_ERR "Unable to read tuner registers.\n");
- goto fail;
- }
-
- dprintk(1, "Device is Xceive %d version %d.%d, "
- "firmware version %d.%d\n",
- hwmodel, hw_major, hw_minor, fw_major, fw_minor);
-
- /* Check firmware version against what we downloaded. */
- if (priv->firm_version != ((fw_major << 8) | fw_minor)) {
- printk(KERN_WARNING
- "Incorrect readback of firmware version %d.%d.\n",
- fw_major, fw_minor);
- goto fail;
- }
-
- /* Check that the tuner hardware model remains consistent over time. */
- if (priv->hwmodel == 0 &&
- (hwmodel == XC_PRODUCT_ID_XC4000 ||
- hwmodel == XC_PRODUCT_ID_XC4100)) {
- priv->hwmodel = hwmodel;
- priv->hwvers = (hw_major << 8) | hw_minor;
- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
- priv->hwvers != ((hw_major << 8) | hw_minor)) {
- printk(KERN_WARNING
- "Read invalid device hardware information - tuner "
- "hung?\n");
- goto fail;
- }
-
- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
-
- /*
- * By setting BASE in cur_fw.type only after successfully loading all
- * firmwares, we can:
- * 1. Identify that BASE firmware with type=0 has been loaded;
- * 2. Tell whether BASE firmware was just changed the next time through.
- */
- priv->cur_fw.type |= BASE;
-
- return 0;
-
-fail:
- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
- if (!is_retry) {
- msleep(50);
- is_retry = 1;
- dprintk(1, "Retrying firmware load\n");
- goto retry;
- }
-
- if (rc == -ENOENT)
- rc = -EINVAL;
- return rc;
-}
-
-static void xc_debug_dump(struct xc4000_priv *priv)
-{
- u16 adc_envelope;
- u32 freq_error_hz = 0;
- u16 lock_status;
- u32 hsync_freq_hz = 0;
- u16 frame_lines;
- u16 quality;
- u16 signal = 0;
- u16 noise = 0;
- u8 hw_majorversion = 0, hw_minorversion = 0;
- u8 fw_majorversion = 0, fw_minorversion = 0;
-
- xc_get_adc_envelope(priv, &adc_envelope);
- dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
-
- xc_get_frequency_error(priv, &freq_error_hz);
- dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
-
- xc_get_lock_status(priv, &lock_status);
- dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
- lock_status);
-
- xc_get_version(priv, &hw_majorversion, &hw_minorversion,
- &fw_majorversion, &fw_minorversion);
- dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
- hw_majorversion, hw_minorversion,
- fw_majorversion, fw_minorversion);
-
- if (priv->video_standard < XC4000_DTV6) {
- xc_get_hsync_freq(priv, &hsync_freq_hz);
- dprintk(1, "*** Horizontal sync frequency = %d Hz\n",
- hsync_freq_hz);
-
- xc_get_frame_lines(priv, &frame_lines);
- dprintk(1, "*** Frame lines = %d\n", frame_lines);
- }
-
- xc_get_quality(priv, &quality);
- dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
-
- xc_get_signal_level(priv, &signal);
- dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal);
-
- xc_get_noise_level(priv, &noise);
- dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise);
-}
-
-static int xc4000_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- u32 bw = c->bandwidth_hz;
- struct xc4000_priv *priv = fe->tuner_priv;
- unsigned int type;
- int ret = -EREMOTEIO;
-
- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency);
-
- mutex_lock(&priv->lock);
-
- switch (delsys) {
- case SYS_ATSC:
- dprintk(1, "%s() VSB modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_AIR;
- priv->freq_hz = c->frequency - 1750000;
- priv->video_standard = XC4000_DTV6;
- type = DTV6;
- break;
- case SYS_DVBC_ANNEX_B:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- priv->freq_hz = c->frequency - 1750000;
- priv->video_standard = XC4000_DTV6;
- type = DTV6;
- break;
- case SYS_DVBT:
- case SYS_DVBT2:
- dprintk(1, "%s() OFDM\n", __func__);
- if (bw == 0) {
- if (c->frequency < 400000000) {
- priv->freq_hz = c->frequency - 2250000;
- } else {
- priv->freq_hz = c->frequency - 2750000;
- }
- priv->video_standard = XC4000_DTV7_8;
- type = DTV78;
- } else if (bw <= 6000000) {
- priv->video_standard = XC4000_DTV6;
- priv->freq_hz = c->frequency - 1750000;
- type = DTV6;
- } else if (bw <= 7000000) {
- priv->video_standard = XC4000_DTV7;
- priv->freq_hz = c->frequency - 2250000;
- type = DTV7;
- } else {
- priv->video_standard = XC4000_DTV8;
- priv->freq_hz = c->frequency - 2750000;
- type = DTV8;
- }
- priv->rf_mode = XC_RF_MODE_AIR;
- break;
- default:
- printk(KERN_ERR "xc4000 delivery system not supported!\n");
- ret = -EINVAL;
- goto fail;
- }
-
- dprintk(1, "%s() frequency=%d (compensated)\n",
- __func__, priv->freq_hz);
-
- /* Make sure the correct firmware type is loaded */
- if (check_firmware(fe, type, 0, priv->if_khz) != 0)
- goto fail;
-
- priv->bandwidth = c->bandwidth_hz;
-
- ret = xc_set_signal_source(priv, priv->rf_mode);
- if (ret != 0) {
- printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n",
- priv->rf_mode);
- goto fail;
- } else {
- u16 video_mode, audio_mode;
- video_mode = xc4000_standard[priv->video_standard].video_mode;
- audio_mode = xc4000_standard[priv->video_standard].audio_mode;
- if (type == DTV6 && priv->firm_version != 0x0102)
- video_mode |= 0x0001;
- ret = xc_set_tv_standard(priv, video_mode, audio_mode);
- if (ret != 0) {
- printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
- /* DJH - do not return when it fails... */
- /* goto fail; */
- }
- }
-
- if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
- ret = 0;
- if (priv->dvb_amplitude != 0) {
- if (xc_write_reg(priv, XREG_AMPLITUDE,
- (priv->firm_version != 0x0102 ||
- priv->dvb_amplitude != 134 ?
- priv->dvb_amplitude : 132)) != 0)
- ret = -EREMOTEIO;
- }
- if (priv->set_smoothedcvbs != 0) {
- if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
- ret = -EREMOTEIO;
- }
- if (ret != 0) {
- printk(KERN_ERR "xc4000: setting registers failed\n");
- /* goto fail; */
- }
-
- xc_tune_channel(priv, priv->freq_hz);
-
- ret = 0;
-
-fail:
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-static int xc4000_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- unsigned int type = 0;
- int ret = -EREMOTEIO;
-
- if (params->mode == V4L2_TUNER_RADIO) {
- dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n",
- __func__, params->frequency);
-
- mutex_lock(&priv->lock);
-
- params->std = 0;
- priv->freq_hz = params->frequency * 125L / 2;
-
- if (audio_std & XC4000_AUDIO_STD_INPUT1) {
- priv->video_standard = XC4000_FM_Radio_INPUT1;
- type = FM | INPUT1;
- } else {
- priv->video_standard = XC4000_FM_Radio_INPUT2;
- type = FM | INPUT2;
- }
-
- goto tune_channel;
- }
-
- dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
- __func__, params->frequency);
-
- mutex_lock(&priv->lock);
-
- /* params->frequency is in units of 62.5khz */
- priv->freq_hz = params->frequency * 62500;
-
- params->std &= V4L2_STD_ALL;
- /* if std is not defined, choose one */
- if (!params->std)
- params->std = V4L2_STD_PAL_BG;
-
- if (audio_std & XC4000_AUDIO_STD_MONO)
- type = MONO;
-
- if (params->std & V4L2_STD_MN) {
- params->std = V4L2_STD_MN;
- if (audio_std & XC4000_AUDIO_STD_MONO) {
- priv->video_standard = XC4000_MN_NTSC_PAL_Mono;
- } else if (audio_std & XC4000_AUDIO_STD_A2) {
- params->std |= V4L2_STD_A2;
- priv->video_standard = XC4000_MN_NTSC_PAL_A2;
- } else {
- params->std |= V4L2_STD_BTSC;
- priv->video_standard = XC4000_MN_NTSC_PAL_BTSC;
- }
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_BG) {
- params->std = V4L2_STD_PAL_BG;
- if (audio_std & XC4000_AUDIO_STD_MONO) {
- priv->video_standard = XC4000_BG_PAL_MONO;
- } else if (!(audio_std & XC4000_AUDIO_STD_A2)) {
- if (!(audio_std & XC4000_AUDIO_STD_B)) {
- params->std |= V4L2_STD_NICAM_A;
- priv->video_standard = XC4000_BG_PAL_NICAM;
- } else {
- params->std |= V4L2_STD_NICAM_B;
- priv->video_standard = XC4000_BG_PAL_NICAM;
- }
- } else {
- if (!(audio_std & XC4000_AUDIO_STD_B)) {
- params->std |= V4L2_STD_A2_A;
- priv->video_standard = XC4000_BG_PAL_A2;
- } else {
- params->std |= V4L2_STD_A2_B;
- priv->video_standard = XC4000_BG_PAL_A2;
- }
- }
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_I) {
- /* default to NICAM audio standard */
- params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM;
- if (audio_std & XC4000_AUDIO_STD_MONO)
- priv->video_standard = XC4000_I_PAL_NICAM_MONO;
- else
- priv->video_standard = XC4000_I_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_DK) {
- params->std = V4L2_STD_PAL_DK;
- if (audio_std & XC4000_AUDIO_STD_MONO) {
- priv->video_standard = XC4000_DK_PAL_MONO;
- } else if (audio_std & XC4000_AUDIO_STD_A2) {
- params->std |= V4L2_STD_A2;
- priv->video_standard = XC4000_DK_PAL_A2;
- } else {
- params->std |= V4L2_STD_NICAM;
- priv->video_standard = XC4000_DK_PAL_NICAM;
- }
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_DK) {
- /* default to A2 audio standard */
- params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2;
- if (audio_std & XC4000_AUDIO_STD_L) {
- type = 0;
- priv->video_standard = XC4000_DK_SECAM_NICAM;
- } else if (audio_std & XC4000_AUDIO_STD_MONO) {
- priv->video_standard = XC4000_DK_SECAM_A2MONO;
- } else if (audio_std & XC4000_AUDIO_STD_K3) {
- params->std |= V4L2_STD_SECAM_K3;
- priv->video_standard = XC4000_DK_SECAM_A2LDK3;
- } else {
- priv->video_standard = XC4000_DK_SECAM_A2DK1;
- }
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_L) {
- /* default to NICAM audio standard */
- type = 0;
- params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM;
- priv->video_standard = XC4000_L_SECAM_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_LC) {
- /* default to NICAM audio standard */
- type = 0;
- params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM;
- priv->video_standard = XC4000_LC_SECAM_NICAM;
- goto tune_channel;
- }
-
-tune_channel:
- /* FIXME: it could be air. */
- priv->rf_mode = XC_RF_MODE_CABLE;
-
- if (check_firmware(fe, type, params->std,
- xc4000_standard[priv->video_standard].int_freq) != 0)
- goto fail;
-
- ret = xc_set_signal_source(priv, priv->rf_mode);
- if (ret != 0) {
- printk(KERN_ERR
- "xc4000: xc_set_signal_source(%d) failed\n",
- priv->rf_mode);
- goto fail;
- } else {
- u16 video_mode, audio_mode;
- video_mode = xc4000_standard[priv->video_standard].video_mode;
- audio_mode = xc4000_standard[priv->video_standard].audio_mode;
- if (priv->video_standard < XC4000_BG_PAL_A2) {
- if (type & NOGD)
- video_mode &= 0xFF7F;
- } else if (priv->video_standard < XC4000_I_PAL_NICAM) {
- if (priv->firm_version == 0x0102)
- video_mode &= 0xFEFF;
- if (audio_std & XC4000_AUDIO_STD_B)
- video_mode |= 0x0080;
- }
- ret = xc_set_tv_standard(priv, video_mode, audio_mode);
- if (ret != 0) {
- printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
- goto fail;
- }
- }
-
- if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
- ret = 0;
- if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0)
- ret = -EREMOTEIO;
- if (priv->set_smoothedcvbs != 0) {
- if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
- ret = -EREMOTEIO;
- }
- if (ret != 0) {
- printk(KERN_ERR "xc4000: setting registers failed\n");
- goto fail;
- }
-
- xc_tune_channel(priv, priv->freq_hz);
-
- ret = 0;
-
-fail:
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- u16 value = 0;
- int rc;
-
- mutex_lock(&priv->lock);
- rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value);
- mutex_unlock(&priv->lock);
-
- if (rc < 0)
- goto ret;
-
- /* Informations from real testing of DVB-T and radio part,
- coeficient for one dB is 0xff.
- */
- tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value);
-
- /* all known digital modes */
- if ((priv->video_standard == XC4000_DTV6) ||
- (priv->video_standard == XC4000_DTV7) ||
- (priv->video_standard == XC4000_DTV7_8) ||
- (priv->video_standard == XC4000_DTV8))
- goto digital;
-
- /* Analog mode has NOISE LEVEL important, signal
- depends only on gain of antenna and amplifiers,
- but it doesn't tell anything about real quality
- of reception.
- */
- mutex_lock(&priv->lock);
- rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value);
- mutex_unlock(&priv->lock);
-
- tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value);
-
- /* highest noise level: 32dB */
- if (value >= 0x2000) {
- value = 0;
- } else {
- value = ~value << 3;
- }
-
- goto ret;
-
- /* Digital mode has SIGNAL LEVEL important and real
- noise level is stored in demodulator registers.
- */
-digital:
- /* best signal: -50dB */
- if (value <= 0x3200) {
- value = 0xffff;
- /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */
- } else if (value >= 0x713A) {
- value = 0;
- } else {
- value = ~(value - 0x3200) << 2;
- }
-
-ret:
- *strength = value;
-
- return rc;
-}
-
-static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
-
- *freq = priv->freq_hz;
-
- if (debug) {
- mutex_lock(&priv->lock);
- if ((priv->cur_fw.type
- & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
- u16 snr = 0;
- if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
- mutex_unlock(&priv->lock);
- dprintk(1, "%s() freq = %u, SNR = %d\n",
- __func__, *freq, snr);
- return 0;
- }
- }
- mutex_unlock(&priv->lock);
- }
-
- dprintk(1, "%s()\n", __func__);
-
- return 0;
-}
-
-static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- *bw = priv->bandwidth;
- return 0;
-}
-
-static int xc4000_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- u16 lock_status = 0;
-
- mutex_lock(&priv->lock);
-
- if (priv->cur_fw.type & BASE)
- xc_get_lock_status(priv, &lock_status);
-
- *status = (lock_status == 1 ?
- TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0);
- if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8))
- *status &= (~TUNER_STATUS_STEREO);
-
- mutex_unlock(&priv->lock);
-
- dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
-
- return 0;
-}
-
-static int xc4000_sleep(struct dvb_frontend *fe)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
- int ret = 0;
-
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&priv->lock);
-
- /* Avoid firmware reload on slow devices */
- if ((no_poweroff == 2 ||
- (no_poweroff == 0 && priv->default_pm != 0)) &&
- (priv->cur_fw.type & BASE) != 0) {
- /* force reset and firmware reload */
- priv->cur_fw.type = XC_POWERED_DOWN;
-
- if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) {
- printk(KERN_ERR
- "xc4000: %s() unable to shutdown tuner\n",
- __func__);
- ret = -EREMOTEIO;
- }
- msleep(20);
- }
-
- mutex_unlock(&priv->lock);
-
- return ret;
-}
-
-static int xc4000_init(struct dvb_frontend *fe)
-{
- dprintk(1, "%s()\n", __func__);
-
- return 0;
-}
-
-static int xc4000_release(struct dvb_frontend *fe)
-{
- struct xc4000_priv *priv = fe->tuner_priv;
-
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&xc4000_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&xc4000_list_mutex);
-
- fe->tuner_priv = NULL;
-
- return 0;
-}
-
-static const struct dvb_tuner_ops xc4000_tuner_ops = {
- .info = {
- .name = "Xceive XC4000",
- .frequency_min = 1000000,
- .frequency_max = 1023000000,
- .frequency_step = 50000,
- },
-
- .release = xc4000_release,
- .init = xc4000_init,
- .sleep = xc4000_sleep,
-
- .set_params = xc4000_set_params,
- .set_analog_params = xc4000_set_analog_params,
- .get_frequency = xc4000_get_frequency,
- .get_rf_strength = xc4000_get_signal,
- .get_bandwidth = xc4000_get_bandwidth,
- .get_status = xc4000_get_status
-};
-
-struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- struct xc4000_config *cfg)
-{
- struct xc4000_priv *priv = NULL;
- int instance;
- u16 id = 0;
-
- dprintk(1, "%s(%d-%04x)\n", __func__,
- i2c ? i2c_adapter_id(i2c) : -1,
- cfg ? cfg->i2c_address : -1);
-
- mutex_lock(&xc4000_list_mutex);
-
- instance = hybrid_tuner_request_state(struct xc4000_priv, priv,
- hybrid_tuner_instance_list,
- i2c, cfg->i2c_address, "xc4000");
- switch (instance) {
- case 0:
- goto fail;
- break;
- case 1:
- /* new tuner instance */
- priv->bandwidth = 6000000;
- /* set default configuration */
- priv->if_khz = 4560;
- priv->default_pm = 0;
- priv->dvb_amplitude = 134;
- priv->set_smoothedcvbs = 1;
- mutex_init(&priv->lock);
- fe->tuner_priv = priv;
- break;
- default:
- /* existing tuner instance */
- fe->tuner_priv = priv;
- break;
- }
-
- if (cfg->if_khz != 0) {
- /* copy configuration if provided by the caller */
- priv->if_khz = cfg->if_khz;
- priv->default_pm = cfg->default_pm;
- priv->dvb_amplitude = cfg->dvb_amplitude;
- priv->set_smoothedcvbs = cfg->set_smoothedcvbs;
- }
-
- /* Check if firmware has been loaded. It is possible that another
- instance of the driver has loaded the firmware.
- */
-
- if (instance == 1) {
- if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
- goto fail;
- } else {
- id = ((priv->cur_fw.type & BASE) != 0 ?
- priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED);
- }
-
- switch (id) {
- case XC_PRODUCT_ID_XC4000:
- case XC_PRODUCT_ID_XC4100:
- printk(KERN_INFO
- "xc4000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc4000: Firmware has been loaded previously\n");
- break;
- case XC_PRODUCT_ID_FW_NOT_LOADED:
- printk(KERN_INFO
- "xc4000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc4000: Firmware has not been loaded previously\n");
- break;
- default:
- printk(KERN_ERR
- "xc4000: Device not found at addr 0x%02x (0x%x)\n",
- cfg->i2c_address, id);
- goto fail;
- }
-
- mutex_unlock(&xc4000_list_mutex);
-
- memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- if (instance == 1) {
- int ret;
- mutex_lock(&priv->lock);
- ret = xc4000_fwupload(fe);
- mutex_unlock(&priv->lock);
- if (ret != 0)
- goto fail2;
- }
-
- return fe;
-fail:
- mutex_unlock(&xc4000_list_mutex);
-fail2:
- xc4000_release(fe);
- return NULL;
-}
-EXPORT_SYMBOL(xc4000_attach);
-
-MODULE_AUTHOR("Steven Toth, Davide Ferri");
-MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
deleted file mode 100644
index 362a8d7c9738..000000000000
--- a/drivers/media/common/tuners/xc5000.c
+++ /dev/null
@@ -1,1265 +0,0 @@
-/*
- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
- *
- * Copyright (c) 2007 Xceive Corporation
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <linux/dvb/frontend.h>
-#include <linux/i2c.h>
-
-#include "dvb_frontend.h"
-
-#include "xc5000.h"
-#include "tuner-i2c.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-static int no_poweroff;
-module_param(no_poweroff, int, 0644);
-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
- "\t\t1 keep device energized and with tuner ready all the times.\n"
- "\t\tFaster, but consumes more power and keeps the device hotter");
-
-static DEFINE_MUTEX(xc5000_list_mutex);
-static LIST_HEAD(hybrid_tuner_instance_list);
-
-#define dprintk(level, fmt, arg...) if (debug >= level) \
- printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
-
-struct xc5000_priv {
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
-
- u32 if_khz;
- u16 xtal_khz;
- u32 freq_hz;
- u32 bandwidth;
- u8 video_standard;
- u8 rf_mode;
- u8 radio_input;
-
- int chip_id;
-};
-
-/* Misc Defines */
-#define MAX_TV_STANDARD 24
-#define XC_MAX_I2C_WRITE_LENGTH 64
-
-/* Signal Types */
-#define XC_RF_MODE_AIR 0
-#define XC_RF_MODE_CABLE 1
-
-/* Result codes */
-#define XC_RESULT_SUCCESS 0
-#define XC_RESULT_RESET_FAILURE 1
-#define XC_RESULT_I2C_WRITE_FAILURE 2
-#define XC_RESULT_I2C_READ_FAILURE 3
-#define XC_RESULT_OUT_OF_RANGE 5
-
-/* Product id */
-#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
-#define XC_PRODUCT_ID_FW_LOADED 0x1388
-
-/* Registers */
-#define XREG_INIT 0x00
-#define XREG_VIDEO_MODE 0x01
-#define XREG_AUDIO_MODE 0x02
-#define XREG_RF_FREQ 0x03
-#define XREG_D_CODE 0x04
-#define XREG_IF_OUT 0x05
-#define XREG_SEEK_MODE 0x07
-#define XREG_POWER_DOWN 0x0A /* 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
-#define XREG_FINERFREQ 0x10
-#define XREG_DDIMODE 0x11
-
-#define XREG_ADC_ENV 0x00
-#define XREG_QUALITY 0x01
-#define XREG_FRAME_LINES 0x02
-#define XREG_HSYNC_FREQ 0x03
-#define XREG_LOCK 0x04
-#define XREG_FREQ_ERROR 0x05
-#define XREG_SNR 0x06
-#define XREG_VERSION 0x07
-#define XREG_PRODUCT_ID 0x08
-#define XREG_BUSY 0x09
-#define XREG_BUILD 0x0D
-
-/*
- Basic firmware description. This will remain with
- the driver for documentation purposes.
-
- This represents an I2C firmware file encoded as a
- string of unsigned char. Format is as follows:
-
- char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
- char[1 ]=len0_LSB -> length of first write transaction
- char[2 ]=data0 -> first byte to be sent
- char[3 ]=data1
- char[4 ]=data2
- char[ ]=...
- char[M ]=dataN -> last byte to be sent
- char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
- char[M+2]=len1_LSB -> length of second write transaction
- char[M+3]=data0
- char[M+4]=data1
- ...
- etc.
-
- The [len] value should be interpreted as follows:
-
- len= len_MSB _ len_LSB
- len=1111_1111_1111_1111 : End of I2C_SEQUENCE
- len=0000_0000_0000_0000 : Reset command: Do hardware reset
- len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
- len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
-
- For the RESET and WAIT commands, the two following bytes will contain
- immediately the length of the following transaction.
-
-*/
-struct XC_TV_STANDARD {
- char *Name;
- u16 AudioMode;
- u16 VideoMode;
-};
-
-/* Tuner standards */
-#define MN_NTSC_PAL_BTSC 0
-#define MN_NTSC_PAL_A2 1
-#define MN_NTSC_PAL_EIAJ 2
-#define MN_NTSC_PAL_Mono 3
-#define BG_PAL_A2 4
-#define BG_PAL_NICAM 5
-#define BG_PAL_MONO 6
-#define I_PAL_NICAM 7
-#define I_PAL_NICAM_MONO 8
-#define DK_PAL_A2 9
-#define DK_PAL_NICAM 10
-#define DK_PAL_MONO 11
-#define DK_SECAM_A2DK1 12
-#define DK_SECAM_A2LDK3 13
-#define DK_SECAM_A2MONO 14
-#define L_SECAM_NICAM 15
-#define LC_SECAM_NICAM 16
-#define DTV6 17
-#define DTV8 18
-#define DTV7_8 19
-#define DTV7 20
-#define FM_Radio_INPUT2 21
-#define FM_Radio_INPUT1 22
-#define FM_Radio_INPUT1_MONO 23
-
-static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
- {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
- {"M/N-NTSC/PAL-A2", 0x0600, 0x8020},
- {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
- {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020},
- {"B/G-PAL-A2", 0x0A00, 0x8049},
- {"B/G-PAL-NICAM", 0x0C04, 0x8049},
- {"B/G-PAL-MONO", 0x0878, 0x8059},
- {"I-PAL-NICAM", 0x1080, 0x8009},
- {"I-PAL-NICAM-MONO", 0x0E78, 0x8009},
- {"D/K-PAL-A2", 0x1600, 0x8009},
- {"D/K-PAL-NICAM", 0x0E80, 0x8009},
- {"D/K-PAL-MONO", 0x1478, 0x8009},
- {"D/K-SECAM-A2 DK1", 0x1200, 0x8009},
- {"D/K-SECAM-A2 L/DK3", 0x0E00, 0x8009},
- {"D/K-SECAM-A2 MONO", 0x1478, 0x8009},
- {"L-SECAM-NICAM", 0x8E82, 0x0009},
- {"L'-SECAM-NICAM", 0x8E82, 0x4009},
- {"DTV6", 0x00C0, 0x8002},
- {"DTV8", 0x00C0, 0x800B},
- {"DTV7/8", 0x00C0, 0x801B},
- {"DTV7", 0x00C0, 0x8007},
- {"FM Radio-INPUT2", 0x9802, 0x9002},
- {"FM Radio-INPUT1", 0x0208, 0x9002},
- {"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
-};
-
-
-struct xc5000_fw_cfg {
- char *name;
- u16 size;
-};
-
-#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
-static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
- .name = XC5000A_FIRMWARE,
- .size = 12401,
-};
-
-#define XC5000C_FIRMWARE "dvb-fe-xc5000c-41.024.5.fw"
-static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
- .name = XC5000C_FIRMWARE,
- .size = 16497,
-};
-
-static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
-{
- switch (chip_id) {
- default:
- case XC5000A:
- return &xc5000a_1_6_114;
- case XC5000C:
- return &xc5000c_41_024_5;
- }
-}
-
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
-static int xc5000_TunerReset(struct dvb_frontend *fe);
-
-static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
-{
- struct i2c_msg msg = { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = buf, .len = len };
-
- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
- printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len);
- return XC_RESULT_I2C_WRITE_FAILURE;
- }
- return XC_RESULT_SUCCESS;
-}
-
-#if 0
-/* This routine is never used because the only time we read data from the
- i2c bus is when we read registers, and we want that to be an atomic i2c
- transaction in case we are on a multi-master bus */
-static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
-{
- struct i2c_msg msg = { .addr = priv->i2c_props.addr,
- .flags = I2C_M_RD, .buf = buf, .len = len };
-
- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
- printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n", len);
- return -EREMOTEIO;
- }
- return 0;
-}
-#endif
-
-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
-{
- u8 buf[2] = { reg >> 8, reg & 0xff };
- u8 bval[2] = { 0, 0 };
- struct i2c_msg msg[2] = {
- { .addr = priv->i2c_props.addr,
- .flags = 0, .buf = &buf[0], .len = 2 },
- { .addr = priv->i2c_props.addr,
- .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
- };
-
- if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
- printk(KERN_WARNING "xc5000: I2C read failed\n");
- return -EREMOTEIO;
- }
-
- *val = (bval[0] << 8) | bval[1];
- return XC_RESULT_SUCCESS;
-}
-
-static void xc_wait(int wait_ms)
-{
- msleep(wait_ms);
-}
-
-static int xc5000_TunerReset(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- if (fe->callback) {
- ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
- fe->dvb->priv :
- priv->i2c_props.adap->algo_data,
- DVB_FRONTEND_COMPONENT_TUNER,
- XC5000_TUNER_RESET, 0);
- if (ret) {
- printk(KERN_ERR "xc5000: reset failed\n");
- return XC_RESULT_RESET_FAILURE;
- }
- } else {
- printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
- return XC_RESULT_RESET_FAILURE;
- }
- return XC_RESULT_SUCCESS;
-}
-
-static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
-{
- u8 buf[4];
- int WatchDogTimer = 100;
- int result;
-
- buf[0] = (regAddr >> 8) & 0xFF;
- buf[1] = regAddr & 0xFF;
- buf[2] = (i2cData >> 8) & 0xFF;
- buf[3] = i2cData & 0xFF;
- result = xc_send_i2c_data(priv, buf, 4);
- if (result == XC_RESULT_SUCCESS) {
- /* wait for busy flag to clear */
- while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
- result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf);
- if (result == XC_RESULT_SUCCESS) {
- if ((buf[0] == 0) && (buf[1] == 0)) {
- /* busy flag cleared */
- break;
- } else {
- xc_wait(5); /* wait 5 ms */
- WatchDogTimer--;
- }
- }
- }
- }
- if (WatchDogTimer < 0)
- result = XC_RESULT_I2C_WRITE_FAILURE;
-
- return result;
-}
-
-static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
-
- int i, nbytes_to_send, result;
- unsigned int len, pos, index;
- u8 buf[XC_MAX_I2C_WRITE_LENGTH];
-
- index = 0;
- while ((i2c_sequence[index] != 0xFF) ||
- (i2c_sequence[index + 1] != 0xFF)) {
- len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
- if (len == 0x0000) {
- /* RESET command */
- result = xc5000_TunerReset(fe);
- index += 2;
- if (result != XC_RESULT_SUCCESS)
- return result;
- } else if (len & 0x8000) {
- /* WAIT command */
- xc_wait(len & 0x7FFF);
- index += 2;
- } else {
- /* Send i2c data whilst ensuring individual transactions
- * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
- */
- index += 2;
- buf[0] = i2c_sequence[index];
- buf[1] = i2c_sequence[index + 1];
- pos = 2;
- while (pos < len) {
- if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
- nbytes_to_send =
- XC_MAX_I2C_WRITE_LENGTH;
- else
- nbytes_to_send = (len - pos + 2);
- for (i = 2; i < nbytes_to_send; i++) {
- buf[i] = i2c_sequence[index + pos +
- i - 2];
- }
- result = xc_send_i2c_data(priv, buf,
- nbytes_to_send);
-
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- pos += nbytes_to_send - 2;
- }
- index += len;
- }
- }
- return XC_RESULT_SUCCESS;
-}
-
-static int xc_initialize(struct xc5000_priv *priv)
-{
- dprintk(1, "%s()\n", __func__);
- return xc_write_reg(priv, XREG_INIT, 0);
-}
-
-static int xc_SetTVStandard(struct xc5000_priv *priv,
- u16 VideoMode, u16 AudioMode)
-{
- int ret;
- dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode);
- dprintk(1, "%s() Standard = %s\n",
- __func__,
- XC5000_Standard[priv->video_standard].Name);
-
- ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
- if (ret == XC_RESULT_SUCCESS)
- ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
-
- return ret;
-}
-
-static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
-{
- dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
- rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
-
- if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
- rf_mode = XC_RF_MODE_CABLE;
- printk(KERN_ERR
- "%s(), Invalid mode, defaulting to CABLE",
- __func__);
- }
- return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
-}
-
-static const struct dvb_tuner_ops xc5000_tuner_ops;
-
-static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
-{
- u16 freq_code;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
- (freq_hz < xc5000_tuner_ops.info.frequency_min))
- return XC_RESULT_OUT_OF_RANGE;
-
- freq_code = (u16)(freq_hz / 15625);
-
- /* Starting in firmware version 1.1.44, Xceive recommends using the
- FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
- only be used for fast scanning for channel lock) */
- return xc_write_reg(priv, XREG_FINERFREQ, freq_code);
-}
-
-
-static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
-{
- u32 freq_code = (freq_khz * 1024)/1000;
- dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n",
- __func__, freq_khz, freq_code);
-
- return xc_write_reg(priv, XREG_IF_OUT, freq_code);
-}
-
-
-static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
-{
- return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope);
-}
-
-static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
-{
- int result;
- u16 regData;
- u32 tmp;
-
- result = xc5000_readreg(priv, XREG_FREQ_ERROR, &regData);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- tmp = (u32)regData;
- (*freq_error_hz) = (tmp * 15625) / 1000;
- return result;
-}
-
-static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status)
-{
- return xc5000_readreg(priv, XREG_LOCK, lock_status);
-}
-
-static int xc_get_version(struct xc5000_priv *priv,
- u8 *hw_majorversion, u8 *hw_minorversion,
- u8 *fw_majorversion, u8 *fw_minorversion)
-{
- u16 data;
- int result;
-
- result = xc5000_readreg(priv, XREG_VERSION, &data);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- (*hw_majorversion) = (data >> 12) & 0x0F;
- (*hw_minorversion) = (data >> 8) & 0x0F;
- (*fw_majorversion) = (data >> 4) & 0x0F;
- (*fw_minorversion) = data & 0x0F;
-
- return 0;
-}
-
-static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev)
-{
- return xc5000_readreg(priv, XREG_BUILD, buildrev);
-}
-
-static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
-{
- u16 regData;
- int result;
-
- result = xc5000_readreg(priv, XREG_HSYNC_FREQ, &regData);
- if (result != XC_RESULT_SUCCESS)
- return result;
-
- (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
- return result;
-}
-
-static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines)
-{
- return xc5000_readreg(priv, XREG_FRAME_LINES, frame_lines);
-}
-
-static int xc_get_quality(struct xc5000_priv *priv, u16 *quality)
-{
- return xc5000_readreg(priv, XREG_QUALITY, quality);
-}
-
-static u16 WaitForLock(struct xc5000_priv *priv)
-{
- u16 lockState = 0;
- int watchDogCount = 40;
-
- while ((lockState == 0) && (watchDogCount > 0)) {
- xc_get_lock_status(priv, &lockState);
- if (lockState != 1) {
- xc_wait(5);
- watchDogCount--;
- }
- }
- return lockState;
-}
-
-#define XC_TUNE_ANALOG 0
-#define XC_TUNE_DIGITAL 1
-static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
-{
- int found = 0;
-
- dprintk(1, "%s(%u)\n", __func__, freq_hz);
-
- if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
- return 0;
-
- if (mode == XC_TUNE_ANALOG) {
- if (WaitForLock(priv) == 1)
- found = 1;
- }
-
- return found;
-}
-
-static int xc_set_xtal(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = XC_RESULT_SUCCESS;
-
- switch (priv->chip_id) {
- default:
- case XC5000A:
- /* 32.000 MHz xtal is default */
- break;
- case XC5000C:
- switch (priv->xtal_khz) {
- default:
- case 32000:
- /* 32.000 MHz xtal is default */
- break;
- case 31875:
- /* 31.875 MHz xtal configuration */
- ret = xc_write_reg(priv, 0x000f, 0x8081);
- break;
- }
- break;
- }
- return ret;
-}
-
-static int xc5000_fwupload(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- const struct firmware *fw;
- int ret;
- const struct xc5000_fw_cfg *desired_fw =
- xc5000_assign_firmware(priv->chip_id);
-
- /* request the firmware, this will block and timeout */
- printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
- desired_fw->name);
-
- ret = request_firmware(&fw, desired_fw->name,
- priv->i2c_props.adap->dev.parent);
- if (ret) {
- printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
- ret = XC_RESULT_RESET_FAILURE;
- goto out;
- } else {
- printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",
- fw->size);
- ret = XC_RESULT_SUCCESS;
- }
-
- if (fw->size != desired_fw->size) {
- printk(KERN_ERR "xc5000: firmware incorrect size\n");
- ret = XC_RESULT_RESET_FAILURE;
- } else {
- printk(KERN_INFO "xc5000: firmware uploading...\n");
- ret = xc_load_i2c_sequence(fe, fw->data);
- if (XC_RESULT_SUCCESS == ret)
- ret = xc_set_xtal(fe);
- if (XC_RESULT_SUCCESS == ret)
- printk(KERN_INFO "xc5000: firmware upload complete...\n");
- else
- printk(KERN_ERR "xc5000: firmware upload failed...\n");
- }
-
-out:
- release_firmware(fw);
- return ret;
-}
-
-static void xc_debug_dump(struct xc5000_priv *priv)
-{
- u16 adc_envelope;
- u32 freq_error_hz = 0;
- u16 lock_status;
- u32 hsync_freq_hz = 0;
- u16 frame_lines;
- u16 quality;
- u8 hw_majorversion = 0, hw_minorversion = 0;
- u8 fw_majorversion = 0, fw_minorversion = 0;
- u16 fw_buildversion = 0;
-
- /* Wait for stats to stabilize.
- * Frame Lines needs two frame times after initial lock
- * before it is valid.
- */
- xc_wait(100);
-
- xc_get_ADC_Envelope(priv, &adc_envelope);
- dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
-
- xc_get_frequency_error(priv, &freq_error_hz);
- dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
-
- xc_get_lock_status(priv, &lock_status);
- dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
- lock_status);
-
- xc_get_version(priv, &hw_majorversion, &hw_minorversion,
- &fw_majorversion, &fw_minorversion);
- xc_get_buildversion(priv, &fw_buildversion);
- dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x.%04x\n",
- hw_majorversion, hw_minorversion,
- fw_majorversion, fw_minorversion, fw_buildversion);
-
- xc_get_hsync_freq(priv, &hsync_freq_hz);
- dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz);
-
- xc_get_frame_lines(priv, &frame_lines);
- dprintk(1, "*** Frame lines = %d\n", frame_lines);
-
- xc_get_quality(priv, &quality);
- dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
-}
-
-static int xc5000_set_params(struct dvb_frontend *fe)
-{
- int ret, b;
- struct xc5000_priv *priv = fe->tuner_priv;
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- u32 freq = fe->dtv_property_cache.frequency;
- u32 delsys = fe->dtv_property_cache.delivery_system;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- dprintk(1, "Unable to load firmware and init tuner\n");
- return -EINVAL;
- }
- }
-
- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, freq);
-
- switch (delsys) {
- case SYS_ATSC:
- dprintk(1, "%s() VSB modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_AIR;
- priv->freq_hz = freq - 1750000;
- priv->video_standard = DTV6;
- break;
- case SYS_DVBC_ANNEX_B:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- priv->freq_hz = freq - 1750000;
- priv->video_standard = DTV6;
- break;
- case SYS_ISDBT:
- /* All ISDB-T are currently for 6 MHz bw */
- if (!bw)
- bw = 6000000;
- /* fall to OFDM handling */
- case SYS_DMBTH:
- case SYS_DVBT:
- case SYS_DVBT2:
- dprintk(1, "%s() OFDM\n", __func__);
- switch (bw) {
- case 6000000:
- priv->video_standard = DTV6;
- priv->freq_hz = freq - 1750000;
- break;
- case 7000000:
- priv->video_standard = DTV7;
- priv->freq_hz = freq - 2250000;
- break;
- case 8000000:
- priv->video_standard = DTV8;
- priv->freq_hz = freq - 2750000;
- break;
- default:
- printk(KERN_ERR "xc5000 bandwidth not set!\n");
- return -EINVAL;
- }
- priv->rf_mode = XC_RF_MODE_AIR;
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- if (bw <= 6000000) {
- priv->video_standard = DTV6;
- priv->freq_hz = freq - 1750000;
- b = 6;
- } else if (bw <= 7000000) {
- priv->video_standard = DTV7;
- priv->freq_hz = freq - 2250000;
- b = 7;
- } else {
- priv->video_standard = DTV7_8;
- priv->freq_hz = freq - 2750000;
- b = 8;
- }
- dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__,
- b, bw);
- break;
- default:
- printk(KERN_ERR "xc5000: delivery system is not supported!\n");
- return -EINVAL;
- }
-
- dprintk(1, "%s() frequency=%d (compensated to %d)\n",
- __func__, freq, priv->freq_hz);
-
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- ret = xc_SetTVStandard(priv,
- XC5000_Standard[priv->video_standard].VideoMode,
- XC5000_Standard[priv->video_standard].AudioMode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- ret = xc_set_IF_frequency(priv, priv->if_khz);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
- priv->if_khz);
- return -EIO;
- }
-
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
-
- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
-
- if (debug)
- xc_debug_dump(priv);
-
- priv->bandwidth = bw;
-
- return 0;
-}
-
-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
- u16 id;
-
- ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id);
- if (ret == XC_RESULT_SUCCESS) {
- if (id == XC_PRODUCT_ID_FW_NOT_LOADED)
- ret = XC_RESULT_RESET_FAILURE;
- else
- ret = XC_RESULT_SUCCESS;
- }
-
- dprintk(1, "%s() returns %s id = 0x%x\n", __func__,
- ret == XC_RESULT_SUCCESS ? "True" : "False", id);
- return ret;
-}
-
-static int xc5000_set_tv_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret;
-
- dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
- __func__, params->frequency);
-
- /* Fix me: it could be air. */
- priv->rf_mode = params->mode;
- if (params->mode > XC_RF_MODE_CABLE)
- priv->rf_mode = XC_RF_MODE_CABLE;
-
- /* params->frequency is in units of 62.5khz */
- priv->freq_hz = params->frequency * 62500;
-
- /* FIX ME: Some video standards may have several possible audio
- standards. We simply default to one of them here.
- */
- if (params->std & V4L2_STD_MN) {
- /* default to BTSC audio standard */
- priv->video_standard = MN_NTSC_PAL_BTSC;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_BG) {
- /* default to NICAM audio standard */
- priv->video_standard = BG_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_I) {
- /* default to NICAM audio standard */
- priv->video_standard = I_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_PAL_DK) {
- /* default to NICAM audio standard */
- priv->video_standard = DK_PAL_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_DK) {
- /* default to A2 DK1 audio standard */
- priv->video_standard = DK_SECAM_A2DK1;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_L) {
- priv->video_standard = L_SECAM_NICAM;
- goto tune_channel;
- }
-
- if (params->std & V4L2_STD_SECAM_LC) {
- priv->video_standard = LC_SECAM_NICAM;
- goto tune_channel;
- }
-
-tune_channel:
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- ret = xc_SetTVStandard(priv,
- XC5000_Standard[priv->video_standard].VideoMode,
- XC5000_Standard[priv->video_standard].AudioMode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
-
- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
-
- if (debug)
- xc_debug_dump(priv);
-
- return 0;
-}
-
-static int xc5000_set_radio_freq(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
- u8 radio_input;
-
- dprintk(1, "%s() frequency=%d (in units of khz)\n",
- __func__, params->frequency);
-
- if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
- dprintk(1, "%s() radio input not configured\n", __func__);
- return -EINVAL;
- }
-
- if (priv->radio_input == XC5000_RADIO_FM1)
- 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);
- return -EINVAL;
- }
-
- priv->freq_hz = params->frequency * 125 / 2;
-
- priv->rf_mode = XC_RF_MODE_AIR;
-
- ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
- XC5000_Standard[radio_input].AudioMode);
-
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
- return -EREMOTEIO;
- }
-
- ret = xc_SetSignalSource(priv, priv->rf_mode);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: xc_SetSignalSource(%d) failed\n",
- priv->rf_mode);
- return -EREMOTEIO;
- }
-
- 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;
-}
-
-static int xc5000_set_analog_params(struct dvb_frontend *fe,
- struct analog_parameters *params)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = -EINVAL;
-
- if (priv->i2c_props.adap == NULL)
- return -EINVAL;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- dprintk(1, "Unable to load firmware and init tuner\n");
- return -EINVAL;
- }
- }
-
- switch (params->mode) {
- case V4L2_TUNER_RADIO:
- ret = xc5000_set_radio_freq(fe, params);
- break;
- case V4L2_TUNER_ANALOG_TV:
- case V4L2_TUNER_DIGITAL_TV:
- ret = xc5000_set_tv_freq(fe, params);
- break;
- }
-
- return ret;
-}
-
-
-static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
- *freq = priv->freq_hz;
- return 0;
-}
-
-static int xc5000_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
- *freq = priv->if_khz * 1000;
- return 0;
-}
-
-static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- *bw = priv->bandwidth;
- return 0;
-}
-
-static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- u16 lock_status = 0;
-
- xc_get_lock_status(priv, &lock_status);
-
- dprintk(1, "%s() lock_status = 0x%08x\n", __func__, lock_status);
-
- *status = lock_status;
-
- return 0;
-}
-
-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- int ret = 0;
-
- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
- ret = xc5000_fwupload(fe);
- if (ret != XC_RESULT_SUCCESS)
- return ret;
- }
-
- /* Start the tuner self-calibration process */
- ret |= xc_initialize(priv);
-
- /* Wait for calibration to complete.
- * We could continue but XC5000 will clock stretch subsequent
- * I2C transactions until calibration is complete. This way we
- * don't have to rely on clock stretching working.
- */
- xc_wait(100);
-
- /* Default to "CABLE" mode */
- ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
-
- return ret;
-}
-
-static int xc5000_sleep(struct dvb_frontend *fe)
-{
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- /* Avoid firmware reload on slow devices */
- if (no_poweroff)
- return 0;
-
- /* According to Xceive technical support, the "powerdown" register
- was removed in newer versions of the firmware. The "supported"
- way to sleep the tuner is to pull the reset pin low for 10ms */
- ret = xc5000_TunerReset(fe);
- if (ret != XC_RESULT_SUCCESS) {
- printk(KERN_ERR
- "xc5000: %s() unable to shutdown tuner\n",
- __func__);
- return -EREMOTEIO;
- } else
- return XC_RESULT_SUCCESS;
-}
-
-static int xc5000_init(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
- dprintk(1, "%s()\n", __func__);
-
- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) {
- printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
- return -EREMOTEIO;
- }
-
- if (debug)
- xc_debug_dump(priv);
-
- return 0;
-}
-
-static int xc5000_release(struct dvb_frontend *fe)
-{
- struct xc5000_priv *priv = fe->tuner_priv;
-
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&xc5000_list_mutex);
-
- if (priv)
- hybrid_tuner_release_state(priv);
-
- mutex_unlock(&xc5000_list_mutex);
-
- fe->tuner_priv = NULL;
-
- 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",
- .frequency_min = 1000000,
- .frequency_max = 1023000000,
- .frequency_step = 50000,
- },
-
- .release = xc5000_release,
- .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,
- .get_if_frequency = xc5000_get_if_frequency,
- .get_bandwidth = xc5000_get_bandwidth,
- .get_status = xc5000_get_status
-};
-
-struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
- struct i2c_adapter *i2c,
- const struct xc5000_config *cfg)
-{
- struct xc5000_priv *priv = NULL;
- int instance;
- u16 id = 0;
-
- dprintk(1, "%s(%d-%04x)\n", __func__,
- i2c ? i2c_adapter_id(i2c) : -1,
- cfg ? cfg->i2c_address : -1);
-
- mutex_lock(&xc5000_list_mutex);
-
- instance = hybrid_tuner_request_state(struct xc5000_priv, priv,
- hybrid_tuner_instance_list,
- i2c, cfg->i2c_address, "xc5000");
- switch (instance) {
- case 0:
- goto fail;
- break;
- case 1:
- /* new tuner instance */
- priv->bandwidth = 6000000;
- fe->tuner_priv = priv;
- break;
- default:
- /* existing tuner instance */
- fe->tuner_priv = priv;
- break;
- }
-
- if (priv->if_khz == 0) {
- /* If the IF hasn't been set yet, use the value provided by
- the caller (occurs in hybrid devices where the analog
- call to xc5000_attach occurs before the digital side) */
- priv->if_khz = cfg->if_khz;
- }
-
- if (priv->xtal_khz == 0)
- priv->xtal_khz = cfg->xtal_khz;
-
- if (priv->radio_input == 0)
- priv->radio_input = cfg->radio_input;
-
- /* don't override chip id if it's already been set
- unless explicitly specified */
- if ((priv->chip_id == 0) || (cfg->chip_id))
- /* use default chip id if none specified, set to 0 so
- it can be overridden if this is a hybrid driver */
- priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
-
- /* Check if firmware has been loaded. It is possible that another
- instance of the driver has loaded the firmware.
- */
- if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
- goto fail;
-
- switch (id) {
- case XC_PRODUCT_ID_FW_LOADED:
- printk(KERN_INFO
- "xc5000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc5000: Firmware has been loaded previously\n");
- break;
- case XC_PRODUCT_ID_FW_NOT_LOADED:
- printk(KERN_INFO
- "xc5000: Successfully identified at address 0x%02x\n",
- cfg->i2c_address);
- printk(KERN_INFO
- "xc5000: Firmware has not been loaded previously\n");
- break;
- default:
- printk(KERN_ERR
- "xc5000: Device not found at addr 0x%02x (0x%x)\n",
- cfg->i2c_address, id);
- goto fail;
- }
-
- mutex_unlock(&xc5000_list_mutex);
-
- memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- return fe;
-fail:
- mutex_unlock(&xc5000_list_mutex);
-
- xc5000_release(fe);
- return NULL;
-}
-EXPORT_SYMBOL(xc5000_attach);
-
-MODULE_AUTHOR("Steven Toth");
-MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(XC5000A_FIRMWARE);
-MODULE_FIRMWARE(XC5000C_FIRMWARE);
diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig
new file mode 100644
index 000000000000..fa7a2490ed5f
--- /dev/null
+++ b/drivers/media/dvb-core/Kconfig
@@ -0,0 +1,29 @@
+#
+# DVB device configuration
+#
+
+config DVB_MAX_ADAPTERS
+ int "maximum number of DVB/ATSC adapters"
+ depends on DVB_CORE
+ default 8
+ range 1 255
+ help
+ Maximum number of DVB/ATSC adapters. Increasing this number
+ increases the memory consumption of the DVB subsystem even
+ if a much lower number of DVB/ATSC adapters is present.
+ Only values in the range 4-32 are tested.
+
+ If you are unsure about this, use the default value 8
+
+config DVB_DYNAMIC_MINORS
+ bool "Dynamic DVB minor allocation"
+ depends on DVB_CORE
+ default n
+ help
+ If you say Y here, the DVB subsystem will use dynamic minor
+ allocation for any device that uses the DVB major number.
+ This means that you can have more than 4 of a single type
+ of device (like demuxes and frontends) per adapter, but udev
+ will be required to manage the device nodes.
+
+ If you are unsure about this, say N here.
diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb-core/Makefile
index 8f22bcd7c1f9..8f22bcd7c1f9 100644
--- a/drivers/media/dvb/dvb-core/Makefile
+++ b/drivers/media/dvb-core/Makefile
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb-core/demux.h
index eb91fd808c16..eb91fd808c16 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb-core/demux.h
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
new file mode 100644
index 000000000000..889c9c16c6df
--- /dev/null
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -0,0 +1,1275 @@
+/*
+ * dmxdev.c - DVB demultiplexer device
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ * for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/ioctl.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include "dmxdev.h"
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk if (debug) printk
+
+static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
+ const u8 *src, size_t len)
+{
+ ssize_t free;
+
+ if (!len)
+ return 0;
+ if (!buf->data)
+ return 0;
+
+ free = dvb_ringbuffer_free(buf);
+ if (len > free) {
+ dprintk("dmxdev: buffer overflow\n");
+ return -EOVERFLOW;
+ }
+
+ return dvb_ringbuffer_write(buf, src, len);
+}
+
+static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
+ int non_blocking, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ size_t todo;
+ ssize_t avail;
+ ssize_t ret = 0;
+
+ if (!src->data)
+ return 0;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ return ret;
+ }
+
+ for (todo = count; todo > 0; todo -= ret) {
+ if (non_blocking && dvb_ringbuffer_empty(src)) {
+ ret = -EWOULDBLOCK;
+ break;
+ }
+
+ ret = wait_event_interruptible(src->queue,
+ !dvb_ringbuffer_empty(src) ||
+ (src->error != 0));
+ if (ret < 0)
+ break;
+
+ if (src->error) {
+ ret = src->error;
+ dvb_ringbuffer_flush(src);
+ break;
+ }
+
+ avail = dvb_ringbuffer_avail(src);
+ if (avail > todo)
+ avail = todo;
+
+ ret = dvb_ringbuffer_read_user(src, buf, avail);
+ if (ret < 0)
+ break;
+
+ buf += ret;
+ }
+
+ return (count - todo) ? (count - todo) : ret;
+}
+
+static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type)
+{
+ struct list_head *head, *pos;
+
+ head = demux->get_frontends(demux);
+ if (!head)
+ return NULL;
+ list_for_each(pos, head)
+ if (DMX_FE_ENTRY(pos)->source == type)
+ return DMX_FE_ENTRY(pos);
+
+ return NULL;
+}
+
+static int dvb_dvr_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ struct dmx_frontend *front;
+
+ dprintk("function : %s\n", __func__);
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_RDWR) {
+ if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ void *mem;
+ if (!dvbdev->readers) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EBUSY;
+ }
+ mem = vmalloc(DVR_BUFFER_SIZE);
+ if (!mem) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENOMEM;
+ }
+ dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
+ dvbdev->readers--;
+ }
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
+
+ if (!dmxdev->demux->write) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EOPNOTSUPP;
+ }
+
+ front = get_fe(dmxdev->demux, DMX_MEMORY_FE);
+
+ if (!front) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EINVAL;
+ }
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux, front);
+ }
+ dvbdev->users++;
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dvr_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ mutex_lock(&dmxdev->mutex);
+
+ if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
+ dmxdev->demux->disconnect_frontend(dmxdev->demux);
+ dmxdev->demux->connect_frontend(dmxdev->demux,
+ dmxdev->dvr_orig_fe);
+ }
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ dvbdev->readers++;
+ if (dmxdev->dvr_buffer.data) {
+ void *mem = dmxdev->dvr_buffer.data;
+ mb();
+ spin_lock_irq(&dmxdev->lock);
+ dmxdev->dvr_buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+ }
+ /* TODO */
+ dvbdev->users--;
+ if (dvbdev->users == 1 && dmxdev->exit == 1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return 0;
+}
+
+static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int ret;
+
+ if (!dmxdev->demux->write)
+ return -EOPNOTSUPP;
+ if ((file->f_flags & O_ACCMODE) != O_WRONLY)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdev->exit) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ENODEV;
+ }
+ ret = dmxdev->demux->write(dmxdev->demux, buf, count);
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+
+ if (dmxdev->exit)
+ return -ENODEV;
+
+ return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+}
+
+static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
+ void *newmem;
+ void *oldmem;
+
+ dprintk("function : %s\n", __func__);
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdev->lock);
+
+ vfree(oldmem);
+
+ return 0;
+}
+
+static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
+ *dmxdevfilter, int state)
+{
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state = state;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+}
+
+static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
+ unsigned long size)
+{
+ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+ void *newmem;
+ void *oldmem;
+
+ if (buf->size == size)
+ return 0;
+ if (!size)
+ return -EINVAL;
+ if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+ return -EBUSY;
+
+ newmem = vmalloc(size);
+ if (!newmem)
+ return -ENOMEM;
+
+ oldmem = buf->data;
+
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ buf->data = newmem;
+ buf->size = size;
+
+ /* reset and not flush in case the buffer shrinks */
+ dvb_ringbuffer_reset(buf);
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+ vfree(oldmem);
+
+ return 0;
+}
+
+static void dvb_dmxdev_filter_timeout(unsigned long data)
+{
+ struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
+
+ dmxdevfilter->buffer.error = -ETIMEDOUT;
+ spin_lock_irq(&dmxdevfilter->dev->lock);
+ dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
+ spin_unlock_irq(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+}
+
+static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec;
+
+ del_timer(&dmxdevfilter->timer);
+ if (para->timeout) {
+ dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout;
+ dmxdevfilter->timer.data = (unsigned long)dmxdevfilter;
+ dmxdevfilter->timer.expires =
+ jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;
+ add_timer(&dmxdevfilter->timer);
+ }
+}
+
+static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_section_filter *filter,
+ enum dmx_success success)
+{
+ struct dmxdev_filter *dmxdevfilter = filter->priv;
+ int ret;
+
+ if (dmxdevfilter->buffer.error) {
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+ }
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->state != DMXDEV_STATE_GO) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+ del_timer(&dmxdevfilter->timer);
+ dprintk("dmxdev: section callback %*ph\n", 6, buffer1);
+ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
+ buffer1_len);
+ if (ret == buffer1_len) {
+ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
+ buffer2_len);
+ }
+ if (ret < 0) {
+ dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ dmxdevfilter->buffer.error = ret;
+ }
+ if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
+ dmxdevfilter->state = DMXDEV_STATE_DONE;
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&dmxdevfilter->buffer.queue);
+ return 0;
+}
+
+static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
+ const u8 *buffer2, size_t buffer2_len,
+ struct dmx_ts_feed *feed,
+ enum dmx_success success)
+{
+ struct dmxdev_filter *dmxdevfilter = feed->priv;
+ struct dvb_ringbuffer *buffer;
+ int ret;
+
+ spin_lock(&dmxdevfilter->dev->lock);
+ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ return 0;
+ }
+
+ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
+ || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
+ buffer = &dmxdevfilter->buffer;
+ else
+ buffer = &dmxdevfilter->dev->dvr_buffer;
+ if (buffer->error) {
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+ }
+ ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
+ if (ret == buffer1_len)
+ ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
+ if (ret < 0) {
+ dvb_ringbuffer_flush(buffer);
+ buffer->error = ret;
+ }
+ spin_unlock(&dmxdevfilter->dev->lock);
+ wake_up(&buffer->queue);
+ return 0;
+}
+
+/* stop feed but only mark the specified filter as stopped (state set) */
+static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed;
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ del_timer(&dmxdevfilter->timer);
+ dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
+ break;
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
+ feed->ts->stop_filtering(feed->ts);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* start feed associated with the specified filter */
+static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev_feed *feed;
+ int ret;
+
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ return filter->feed.sec->start_filtering(filter->feed.sec);
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = feed->ts->start_filtering(feed->ts);
+ if (ret < 0) {
+ dvb_dmxdev_feed_stop(filter);
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* restart section feed if it has filters left associated with it,
+ otherwise release the feed */
+static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
+{
+ int i;
+ struct dmxdev *dmxdev = filter->dev;
+ u16 pid = filter->params.sec.pid;
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+ dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].params.sec.pid == pid) {
+ dvb_dmxdev_feed_start(&dmxdev->filter[i]);
+ return 0;
+ }
+
+ filter->dev->demux->release_section_feed(dmxdev->demux,
+ filter->feed.sec);
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed;
+ struct dmx_demux *demux;
+
+ if (dmxdevfilter->state < DMXDEV_STATE_GO)
+ return 0;
+
+ switch (dmxdevfilter->type) {
+ case DMXDEV_TYPE_SEC:
+ if (!dmxdevfilter->feed.sec)
+ break;
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ if (dmxdevfilter->filter.sec)
+ dmxdevfilter->feed.sec->
+ release_filter(dmxdevfilter->feed.sec,
+ dmxdevfilter->filter.sec);
+ dvb_dmxdev_feed_restart(dmxdevfilter);
+ dmxdevfilter->feed.sec = NULL;
+ break;
+ case DMXDEV_TYPE_PES:
+ dvb_dmxdev_feed_stop(dmxdevfilter);
+ demux = dmxdevfilter->dev->demux;
+ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
+ demux->release_ts_feed(demux, feed->ts);
+ feed->ts = NULL;
+ }
+ break;
+ default:
+ if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
+ return 0;
+ return -EINVAL;
+ }
+
+ dvb_ringbuffer_flush(&dmxdevfilter->buffer);
+ return 0;
+}
+
+static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ /* delete all PIDs */
+ list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
+ list_del(&feed->next);
+ kfree(feed);
+ }
+
+ BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
+}
+
+static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
+{
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ return 0;
+
+ if (dmxdevfilter->type == DMXDEV_TYPE_PES)
+ dvb_dmxdev_delete_pids(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ return 0;
+}
+
+static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter,
+ struct dmxdev_feed *feed)
+{
+ struct timespec timeout = { 0 };
+ struct dmx_pes_filter_params *para = &filter->params.pes;
+ dmx_output_t otype;
+ int ret;
+ int ts_type;
+ dmx_pes_type_t ts_pes;
+ struct dmx_ts_feed *tsfeed;
+
+ feed->ts = NULL;
+ otype = para->output;
+
+ ts_pes = para->pes_type;
+
+ if (ts_pes < DMX_PES_OTHER)
+ ts_type = TS_DECODER;
+ else
+ ts_type = 0;
+
+ if (otype == DMX_OUT_TS_TAP)
+ ts_type |= TS_PACKET;
+ else if (otype == DMX_OUT_TSDEMUX_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX;
+ else if (otype == DMX_OUT_TAP)
+ ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
+
+ ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
+ dvb_dmxdev_ts_callback);
+ if (ret < 0)
+ return ret;
+
+ tsfeed = feed->ts;
+ tsfeed->priv = filter;
+
+ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ ret = tsfeed->start_filtering(tsfeed);
+ if (ret < 0) {
+ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
+{
+ struct dmxdev *dmxdev = filter->dev;
+ struct dmxdev_feed *feed;
+ void *mem;
+ int ret, i;
+
+ if (filter->state < DMXDEV_STATE_SET)
+ return -EINVAL;
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ dvb_dmxdev_filter_stop(filter);
+
+ if (!filter->buffer.data) {
+ mem = vmalloc(filter->buffer.size);
+ if (!mem)
+ return -ENOMEM;
+ spin_lock_irq(&filter->dev->lock);
+ filter->buffer.data = mem;
+ spin_unlock_irq(&filter->dev->lock);
+ }
+
+ dvb_ringbuffer_flush(&filter->buffer);
+
+ switch (filter->type) {
+ case DMXDEV_TYPE_SEC:
+ {
+ struct dmx_sct_filter_params *para = &filter->params.sec;
+ struct dmx_section_filter **secfilter = &filter->filter.sec;
+ struct dmx_section_feed **secfeed = &filter->feed.sec;
+
+ *secfilter = NULL;
+ *secfeed = NULL;
+
+
+ /* find active filter/feed with same PID */
+ for (i = 0; i < dmxdev->filternum; i++) {
+ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
+ dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
+ dmxdev->filter[i].params.sec.pid == para->pid) {
+ *secfeed = dmxdev->filter[i].feed.sec;
+ break;
+ }
+ }
+
+ /* if no feed found, try to allocate new one */
+ if (!*secfeed) {
+ ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
+ secfeed,
+ dvb_dmxdev_section_callback);
+ if (ret < 0) {
+ printk("DVB (%s): could not alloc feed\n",
+ __func__);
+ return ret;
+ }
+
+ ret = (*secfeed)->set(*secfeed, para->pid, 32768,
+ (para->flags & DMX_CHECK_CRC) ? 1 : 0);
+ if (ret < 0) {
+ printk("DVB (%s): could not set feed\n",
+ __func__);
+ dvb_dmxdev_feed_restart(filter);
+ return ret;
+ }
+ } else {
+ dvb_dmxdev_feed_stop(filter);
+ }
+
+ ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
+ if (ret < 0) {
+ dvb_dmxdev_feed_restart(filter);
+ filter->feed.sec->start_filtering(*secfeed);
+ dprintk("could not get filter\n");
+ return ret;
+ }
+
+ (*secfilter)->priv = filter;
+
+ memcpy(&((*secfilter)->filter_value[3]),
+ &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
+ memcpy(&(*secfilter)->filter_mask[3],
+ &para->filter.mask[1], DMX_FILTER_SIZE - 1);
+ memcpy(&(*secfilter)->filter_mode[3],
+ &para->filter.mode[1], DMX_FILTER_SIZE - 1);
+
+ (*secfilter)->filter_value[0] = para->filter.filter[0];
+ (*secfilter)->filter_mask[0] = para->filter.mask[0];
+ (*secfilter)->filter_mode[0] = para->filter.mode[0];
+ (*secfilter)->filter_mask[1] = 0;
+ (*secfilter)->filter_mask[2] = 0;
+
+ filter->todo = 0;
+
+ ret = filter->feed.sec->start_filtering(filter->feed.sec);
+ if (ret < 0)
+ return ret;
+
+ dvb_dmxdev_filter_timer(filter);
+ break;
+ }
+ case DMXDEV_TYPE_PES:
+ list_for_each_entry(feed, &filter->feed.ts, next) {
+ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
+ if (ret < 0) {
+ dvb_dmxdev_filter_stop(filter);
+ return ret;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
+ return 0;
+}
+
+static int dvb_demux_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ int i;
+ struct dmxdev_filter *dmxdevfilter;
+
+ if (!dmxdev->filter)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ for (i = 0; i < dmxdev->filternum; i++)
+ if (dmxdev->filter[i].state == DMXDEV_STATE_FREE)
+ break;
+
+ if (i == dmxdev->filternum) {
+ mutex_unlock(&dmxdev->mutex);
+ return -EMFILE;
+ }
+
+ dmxdevfilter = &dmxdev->filter[i];
+ mutex_init(&dmxdevfilter->mutex);
+ file->private_data = dmxdevfilter;
+
+ dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
+ dmxdevfilter->type = DMXDEV_TYPE_NONE;
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
+ init_timer(&dmxdevfilter->timer);
+
+ dvbdev->users++;
+
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter)
+{
+ mutex_lock(&dmxdev->mutex);
+ mutex_lock(&dmxdevfilter->mutex);
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if (dmxdevfilter->buffer.data) {
+ void *mem = dmxdevfilter->buffer.data;
+
+ spin_lock_irq(&dmxdev->lock);
+ dmxdevfilter->buffer.data = NULL;
+ spin_unlock_irq(&dmxdev->lock);
+ vfree(mem);
+ }
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
+ wake_up(&dmxdevfilter->buffer.queue);
+ mutex_unlock(&dmxdevfilter->mutex);
+ mutex_unlock(&dmxdev->mutex);
+ return 0;
+}
+
+static inline void invert_mode(dmx_filter_t *filter)
+{
+ int i;
+
+ for (i = 0; i < DMX_FILTER_SIZE; i++)
+ filter->mode[i] ^= 0xff;
+}
+
+static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ /* only TS packet filters may have multiple PIDs */
+ if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
+ (!list_empty(&filter->feed.ts)))
+ return -EINVAL;
+
+ feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
+ if (feed == NULL)
+ return -ENOMEM;
+
+ feed->pid = pid;
+ list_add(&feed->next, &filter->feed.ts);
+
+ if (filter->state >= DMXDEV_STATE_GO)
+ return dvb_dmxdev_start_feed(dmxdev, filter, feed);
+
+ return 0;
+}
+
+static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
+ struct dmxdev_filter *filter, u16 pid)
+{
+ struct dmxdev_feed *feed, *tmp;
+
+ if ((filter->type != DMXDEV_TYPE_PES) ||
+ (filter->state < DMXDEV_STATE_SET))
+ return -EINVAL;
+
+ list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
+ if ((feed->pid == pid) && (feed->ts != NULL)) {
+ feed->ts->stop_filtering(feed->ts);
+ filter->dev->demux->release_ts_feed(filter->dev->demux,
+ feed->ts);
+ list_del(&feed->next);
+ kfree(feed);
+ }
+ }
+
+ return 0;
+}
+
+static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_sct_filter_params *params)
+{
+ dprintk("function : %s\n", __func__);
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+
+ dmxdevfilter->type = DMXDEV_TYPE_SEC;
+ memcpy(&dmxdevfilter->params.sec,
+ params, sizeof(struct dmx_sct_filter_params));
+ invert_mode(&dmxdevfilter->params.sec.filter);
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ if (params->flags & DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
+ struct dmxdev_filter *dmxdevfilter,
+ struct dmx_pes_filter_params *params)
+{
+ int ret;
+
+ dvb_dmxdev_filter_stop(dmxdevfilter);
+ dvb_dmxdev_filter_reset(dmxdevfilter);
+
+ if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
+ return -EINVAL;
+
+ dmxdevfilter->type = DMXDEV_TYPE_PES;
+ memcpy(&dmxdevfilter->params, params,
+ sizeof(struct dmx_pes_filter_params));
+ INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
+
+ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
+
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
+ dmxdevfilter->params.pes.pid);
+ if (ret < 0)
+ return ret;
+
+ if (params->flags & DMX_IMMEDIATE_START)
+ return dvb_dmxdev_filter_start(dmxdevfilter);
+
+ return 0;
+}
+
+static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
+ struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int result, hcount;
+ int done = 0;
+
+ if (dfil->todo <= 0) {
+ hcount = 3 + dfil->todo;
+ if (hcount > count)
+ hcount = count;
+ result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, hcount, ppos);
+ if (result < 0) {
+ dfil->todo = 0;
+ return result;
+ }
+ if (copy_from_user(dfil->secheader - dfil->todo, buf, result))
+ return -EFAULT;
+ buf += result;
+ done = result;
+ count -= result;
+ dfil->todo -= result;
+ if (dfil->todo > -3)
+ return done;
+ dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff;
+ if (!count)
+ return done;
+ }
+ if (count > dfil->todo)
+ count = dfil->todo;
+ result = dvb_dmxdev_buffer_read(&dfil->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+ if (result < 0)
+ return result;
+ dfil->todo -= result;
+ return (result + done);
+}
+
+static ssize_t
+dvb_demux_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ int ret;
+
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex))
+ return -ERESTARTSYS;
+
+ if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
+ ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
+ else
+ ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
+ file->f_flags & O_NONBLOCK,
+ buf, count, ppos);
+
+ mutex_unlock(&dmxdevfilter->mutex);
+ return ret;
+}
+
+static int dvb_demux_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+ unsigned long arg = (unsigned long)parg;
+ int ret = 0;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_START:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ if (dmxdevfilter->state < DMXDEV_STATE_SET)
+ ret = -EINVAL;
+ else
+ ret = dvb_dmxdev_filter_start(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_STOP:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_stop(dmxdevfilter);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_PES_FILTER:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_SET_BUFFER_SIZE:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ mutex_unlock(&dmxdev->mutex);
+ return -ERESTARTSYS;
+ }
+ ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_GET_PES_PIDS:
+ if (!dmxdev->demux->get_pes_pids) {
+ ret = -EINVAL;
+ break;
+ }
+ dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
+ break;
+
+ case DMX_GET_CAPS:
+ if (!dmxdev->demux->get_caps) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->get_caps(dmxdev->demux, parg);
+ break;
+
+ case DMX_SET_SOURCE:
+ if (!dmxdev->demux->set_source) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->set_source(dmxdev->demux, parg);
+ break;
+
+ case DMX_GET_STC:
+ if (!dmxdev->demux->get_stc) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = dmxdev->demux->get_stc(dmxdev->demux,
+ ((struct dmx_stc *)parg)->num,
+ &((struct dmx_stc *)parg)->stc,
+ &((struct dmx_stc *)parg)->base);
+ break;
+
+ case DMX_ADD_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ case DMX_REMOVE_PID:
+ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
+ mutex_unlock(&dmxdevfilter->mutex);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
+}
+
+static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ unsigned int mask = 0;
+
+ if (!dmxdevfilter)
+ return -EINVAL;
+
+ poll_wait(file, &dmxdevfilter->buffer.queue, wait);
+
+ if (dmxdevfilter->state != DMXDEV_STATE_GO &&
+ dmxdevfilter->state != DMXDEV_STATE_DONE &&
+ dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
+ return 0;
+
+ if (dmxdevfilter->buffer.error)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+
+ return mask;
+}
+
+static int dvb_demux_release(struct inode *inode, struct file *file)
+{
+ struct dmxdev_filter *dmxdevfilter = file->private_data;
+ struct dmxdev *dmxdev = dmxdevfilter->dev;
+
+ int ret;
+
+ ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
+
+ mutex_lock(&dmxdev->mutex);
+ dmxdev->dvbdev->users--;
+ if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ mutex_unlock(&dmxdev->mutex);
+ wake_up(&dmxdev->dvbdev->wait_queue);
+ } else
+ mutex_unlock(&dmxdev->mutex);
+
+ return ret;
+}
+
+static const struct file_operations dvb_demux_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_demux_read,
+ .unlocked_ioctl = dvb_demux_ioctl,
+ .open = dvb_demux_open,
+ .release = dvb_demux_release,
+ .poll = dvb_demux_poll,
+ .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_demux = {
+ .priv = NULL,
+ .users = 1,
+ .writers = 1,
+ .fops = &dvb_demux_fops
+};
+
+static int dvb_dvr_do_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned long arg = (unsigned long)parg;
+ int ret;
+
+ if (mutex_lock_interruptible(&dmxdev->mutex))
+ return -ERESTARTSYS;
+
+ switch (cmd) {
+ case DMX_SET_BUFFER_SIZE:
+ ret = dvb_dvr_set_buffer_size(dmxdev, arg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&dmxdev->mutex);
+ return ret;
+}
+
+static long dvb_dvr_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
+}
+
+static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dmxdev *dmxdev = dvbdev->priv;
+ unsigned int mask = 0;
+
+ dprintk("function : %s\n", __func__);
+
+ poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ if (dmxdev->dvr_buffer.error)
+ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
+
+ if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
+ mask |= (POLLIN | POLLRDNORM | POLLPRI);
+ } else
+ mask |= (POLLOUT | POLLWRNORM | POLLPRI);
+
+ return mask;
+}
+
+static const struct file_operations dvb_dvr_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_dvr_read,
+ .write = dvb_dvr_write,
+ .unlocked_ioctl = dvb_dvr_ioctl,
+ .open = dvb_dvr_open,
+ .release = dvb_dvr_release,
+ .poll = dvb_dvr_poll,
+ .llseek = default_llseek,
+};
+
+static struct dvb_device dvbdev_dvr = {
+ .priv = NULL,
+ .readers = 1,
+ .users = 1,
+ .fops = &dvb_dvr_fops
+};
+
+int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
+{
+ int i;
+
+ if (dmxdev->demux->open(dmxdev->demux) < 0)
+ return -EUSERS;
+
+ dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter));
+ if (!dmxdev->filter)
+ return -ENOMEM;
+
+ mutex_init(&dmxdev->mutex);
+ spin_lock_init(&dmxdev->lock);
+ for (i = 0; i < dmxdev->filternum; i++) {
+ dmxdev->filter[i].dev = dmxdev;
+ dmxdev->filter[i].buffer.data = NULL;
+ dvb_dmxdev_filter_state_set(&dmxdev->filter[i],
+ DMXDEV_STATE_FREE);
+ }
+
+ dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
+ DVB_DEVICE_DEMUX);
+ dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
+ dmxdev, DVB_DEVICE_DVR);
+
+ dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_init);
+
+void dvb_dmxdev_release(struct dmxdev *dmxdev)
+{
+ dmxdev->exit=1;
+ if (dmxdev->dvbdev->users > 1) {
+ wait_event(dmxdev->dvbdev->wait_queue,
+ dmxdev->dvbdev->users==1);
+ }
+ if (dmxdev->dvr_dvbdev->users > 1) {
+ wait_event(dmxdev->dvr_dvbdev->wait_queue,
+ dmxdev->dvr_dvbdev->users==1);
+ }
+
+ dvb_unregister_device(dmxdev->dvbdev);
+ dvb_unregister_device(dmxdev->dvr_dvbdev);
+
+ vfree(dmxdev->filter);
+ dmxdev->filter = NULL;
+ dmxdev->demux->close(dmxdev->demux);
+}
+
+EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb-core/dmxdev.h
index 02ebe28f830d..02ebe28f830d 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb-core/dmxdev.h
diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h
new file mode 100644
index 000000000000..58e0220447c0
--- /dev/null
+++ b/drivers/media/dvb-core/dvb-usb-ids.h
@@ -0,0 +1,364 @@
+/* dvb-usb-ids.h is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see
+ * dvb-usb-init.c for copyright information.
+ *
+ * a header file containing define's for the USB device supported by the
+ * various drivers.
+ */
+#ifndef _DVB_USB_IDS_H_
+#define _DVB_USB_IDS_H_
+
+/* Vendor IDs */
+#define USB_VID_ADSTECH 0x06e1
+#define USB_VID_AFATECH 0x15a4
+#define USB_VID_ALCOR_MICRO 0x058f
+#define USB_VID_ALINK 0x05e3
+#define USB_VID_AMT 0x1c73
+#define USB_VID_ANCHOR 0x0547
+#define USB_VID_ANSONIC 0x10b9
+#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
+#define USB_VID_ASUS 0x0b05
+#define USB_VID_AVERMEDIA 0x07ca
+#define USB_VID_COMPRO 0x185b
+#define USB_VID_COMPRO_UNK 0x145f
+#define USB_VID_CONEXANT 0x0572
+#define USB_VID_CYPRESS 0x04b4
+#define USB_VID_DEXATEK 0x1d19
+#define USB_VID_DIBCOM 0x10b8
+#define USB_VID_DPOSH 0x1498
+#define USB_VID_DVICO 0x0fe9
+#define USB_VID_E3C 0x18b4
+#define USB_VID_ELGATO 0x0fd9
+#define USB_VID_EMPIA 0xeb1a
+#define USB_VID_GENPIX 0x09c0
+#define USB_VID_GRANDTEC 0x5032
+#define USB_VID_GTEK 0x1f4d
+#define USB_VID_HANFTEK 0x15f4
+#define USB_VID_HAUPPAUGE 0x2040
+#define USB_VID_HYPER_PALTEK 0x1025
+#define USB_VID_INTEL 0x8086
+#define USB_VID_ITETECH 0x048d
+#define USB_VID_KWORLD 0xeb2a
+#define USB_VID_KWORLD_2 0x1b80
+#define USB_VID_KYE 0x0458
+#define USB_VID_LEADTEK 0x0413
+#define USB_VID_LITEON 0x04ca
+#define USB_VID_MEDION 0x1660
+#define USB_VID_MIGLIA 0x18f3
+#define USB_VID_MSI 0x0db0
+#define USB_VID_MSI_2 0x1462
+#define USB_VID_OPERA1 0x695c
+#define USB_VID_PINNACLE 0x2304
+#define USB_VID_PCTV 0x2013
+#define USB_VID_PIXELVIEW 0x1554
+#define USB_VID_REALTEK 0x0bda
+#define USB_VID_TECHNOTREND 0x0b48
+#define USB_VID_TERRATEC 0x0ccd
+#define USB_VID_TELESTAR 0x10b9
+#define USB_VID_VISIONPLUS 0x13d3
+#define USB_VID_SONY 0x1415
+#define USB_VID_TWINHAN 0x1822
+#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+#define USB_VID_UNIWILL 0x1584
+#define USB_VID_WIDEVIEW 0x14aa
+#define USB_VID_GIGABYTE 0x1044
+#define USB_VID_YUAN 0x1164
+#define USB_VID_XTENSIONS 0x1ae7
+#define USB_VID_HUMAX_COEX 0x10b9
+#define USB_VID_774 0x7a69
+#define USB_VID_EVOLUTEPC 0x1e59
+#define USB_VID_AZUREWAVE 0x13d3
+#define USB_VID_TECHNISAT 0x14f7
+
+/* Product IDs */
+#define USB_PID_ADSTECH_USB2_COLD 0xa333
+#define USB_PID_ADSTECH_USB2_WARM 0xa334
+#define USB_PID_AFATECH_AF9005 0x9020
+#define USB_PID_AFATECH_AF9015_9015 0x9015
+#define USB_PID_AFATECH_AF9015_9016 0x9016
+#define USB_PID_AFATECH_AF9035_1000 0x1000
+#define USB_PID_AFATECH_AF9035_1001 0x1001
+#define USB_PID_AFATECH_AF9035_1002 0x1002
+#define USB_PID_AFATECH_AF9035_1003 0x1003
+#define USB_PID_AFATECH_AF9035_9035 0x9035
+#define USB_PID_TREKSTOR_DVBT 0x901b
+#define USB_PID_TREKSTOR_TERRES_2_0 0xC803
+#define USB_VID_ALINK_DTU 0xf170
+#define USB_PID_ANSONIC_DVBT_USB 0x6000
+#define USB_PID_ANYSEE 0x861f
+#define USB_PID_AZUREWAVE_AD_TU700 0x3237
+#define USB_PID_AZUREWAVE_6007 0x0ccd
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
+#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
+#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
+#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
+#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
+#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80
+#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397
+#define USB_PID_CONEXANT_D680_DMB 0x86d6
+#define USB_PID_CREATIX_CTX1921 0x1921
+#define USB_PID_DELOCK_USB2_DVBT 0xb803
+#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
+#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
+#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
+#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
+#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
+#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
+#define USB_PID_DIBCOM_STK7700P 0x1e14
+#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
+#define USB_PID_DIBCOM_STK7700D 0x1ef0
+#define USB_PID_DIBCOM_STK7700_U7000 0x7001
+#define USB_PID_DIBCOM_STK7070P 0x1ebc
+#define USB_PID_DIBCOM_STK7070PD 0x1ebe
+#define USB_PID_DIBCOM_STK807XP 0x1f90
+#define USB_PID_DIBCOM_STK807XPVR 0x1f98
+#define USB_PID_DIBCOM_STK8096GP 0x1fa0
+#define USB_PID_DIBCOM_NIM8096MD 0x1fa8
+#define USB_PID_DIBCOM_TFE8096P 0x1f9C
+#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_TFE7090E 0x1bb7
+#define USB_PID_DIBCOM_TFE7790E 0x1e6e
+#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
+#define USB_PID_E3C_EC168_2 0xfffa
+#define USB_PID_E3C_EC168_3 0xfffb
+#define USB_PID_E3C_EC168_4 0x1001
+#define USB_PID_E3C_EC168_5 0x1002
+#define USB_PID_FREECOM_DVBT 0x0160
+#define USB_PID_FREECOM_DVBT_2 0x0161
+#define USB_PID_UNIWILL_STK7700P 0x6003
+#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
+#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_INTEL_CE9500 0x9500
+#define USB_PID_ITETECH_IT9135 0x9135
+#define USB_PID_ITETECH_IT9135_9005 0x9005
+#define USB_PID_ITETECH_IT9135_9006 0x9006
+#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_399U_2 0xe400
+#define USB_PID_KWORLD_395U 0xe396
+#define USB_PID_KWORLD_395U_2 0xe39b
+#define USB_PID_KWORLD_395U_3 0xe395
+#define USB_PID_KWORLD_395U_4 0xe39a
+#define USB_PID_KWORLD_MC810 0xc810
+#define USB_PID_KWORLD_PC160_2T 0xc160
+#define USB_PID_KWORLD_PC160_T 0xc161
+#define USB_PID_KWORLD_UB383_T 0xe383
+#define USB_PID_KWORLD_UB499_2T_T09 0xe409
+#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
+#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
+#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
+#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097
+#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099
+#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9
+#define USB_PID_TWINHAN_VP7041_COLD 0x3201
+#define USB_PID_TWINHAN_VP7041_WARM 0x3202
+#define USB_PID_TWINHAN_VP7020_COLD 0x3203
+#define USB_PID_TWINHAN_VP7020_WARM 0x3204
+#define USB_PID_TWINHAN_VP7045_COLD 0x3205
+#define USB_PID_TWINHAN_VP7045_WARM 0x3206
+#define USB_PID_TWINHAN_VP7021_COLD 0x3207
+#define USB_PID_TWINHAN_VP7021_WARM 0x3208
+#define USB_PID_TINYTWIN 0x3226
+#define USB_PID_TINYTWIN_2 0xe402
+#define USB_PID_TINYTWIN_3 0x9016
+#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
+#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
+#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
+#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
+#define USB_PID_ARTEC_T14_COLD 0x810b
+#define USB_PID_ARTEC_T14_WARM 0x810c
+#define USB_PID_ARTEC_T14BR 0x810f
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
+#define USB_PID_DTT200U_COLD 0x0201
+#define USB_PID_DTT200U_WARM 0x0301
+#define USB_PID_WT220U_ZAP250_COLD 0x0220
+#define USB_PID_WT220U_COLD 0x0222
+#define USB_PID_WT220U_WARM 0x0221
+#define USB_PID_WT220U_FC_COLD 0x0225
+#define USB_PID_WT220U_FC_WARM 0x0226
+#define USB_PID_WT220U_ZL0353_COLD 0x022a
+#define USB_PID_WT220U_ZL0353_WARM 0x022b
+#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
+#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
+#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941
+#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
+#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
+#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070
+#define USB_PID_HAUPPAUGE_MYTV_T 0x7080
+#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
+#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200
+#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200
+#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210
+#define USB_PID_AVERMEDIA_EXPRESS 0xb568
+#define USB_PID_AVERMEDIA_VOLAR 0xa807
+#define USB_PID_AVERMEDIA_VOLAR_2 0xb808
+#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868
+#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039
+#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
+#define USB_PID_AVERMEDIA_A309 0xa309
+#define USB_PID_AVERMEDIA_A310 0xa310
+#define USB_PID_AVERMEDIA_A850 0x850a
+#define USB_PID_AVERMEDIA_A850T 0x850b
+#define USB_PID_AVERMEDIA_A805 0xa805
+#define USB_PID_AVERMEDIA_A815M 0x815a
+#define USB_PID_AVERMEDIA_A835 0xa835
+#define USB_PID_AVERMEDIA_B835 0xb835
+#define USB_PID_AVERMEDIA_1867 0x1867
+#define USB_PID_AVERMEDIA_A867 0xa867
+#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
+#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
+#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
+#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
+#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081
+#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
+#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060
+#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
+#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
+#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
+#define USB_PID_TERRATEC_H7 0x10b4
+#define USB_PID_TERRATEC_H7_2 0x10a3
+#define USB_PID_TERRATEC_T3 0x10a0
+#define USB_PID_TERRATEC_T5 0x10a1
+#define USB_PID_NOXON_DAB_STICK 0x00b3
+#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
+#define USB_PID_PINNACLE_PCTV2000E 0x022c
+#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
+#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229
+#define USB_PID_PINNACLE_PCTV71E 0x022b
+#define USB_PID_PINNACLE_PCTV72E 0x0236
+#define USB_PID_PINNACLE_PCTV73E 0x0237
+#define USB_PID_PINNACLE_PCTV310E 0x3211
+#define USB_PID_PINNACLE_PCTV801E 0x023a
+#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
+#define USB_PID_PINNACLE_PCTV340E 0x023d
+#define USB_PID_PINNACLE_PCTV340E_SE 0x023e
+#define USB_PID_PINNACLE_PCTV73A 0x0243
+#define USB_PID_PINNACLE_PCTV73ESE 0x0245
+#define USB_PID_PINNACLE_PCTV74E 0x0246
+#define USB_PID_PINNACLE_PCTV282E 0x0248
+#define USB_PID_PIXELVIEW_SBTVD 0x5010
+#define USB_PID_PCTV_200E 0x020e
+#define USB_PID_PCTV_400E 0x020f
+#define USB_PID_PCTV_450E 0x0222
+#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_REALTEK_RTL2831U 0x2831
+#define USB_PID_REALTEK_RTL2832U 0x2832
+#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
+#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
+#define USB_PID_NEBULA_DIGITV 0x0201
+#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
+#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
+#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501
+#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00
+#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01
+#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10
+#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11
+#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50
+#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
+#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
+#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
+#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
+#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
+#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
+#define USB_PID_MEDION_MD95700 0x0932
+#define USB_PID_MSI_MEGASKY580 0x5580
+#define USB_PID_MSI_MEGASKY580_55801 0x5581
+#define USB_PID_KYE_DVB_T_COLD 0x701e
+#define USB_PID_KYE_DVB_T_WARM 0x701f
+#define USB_PID_LITEON_DVB_T_COLD 0xf000
+#define USB_PID_LITEON_DVB_T_WARM 0xf001
+#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
+#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
+#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
+#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
+#define USB_PID_WINFAST_DTV2000DS 0x6a04
+#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
+#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
+#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6
+#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
+#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
+#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
+#define USB_PID_GENPIX_8PSK_REV_2 0x0202
+#define USB_PID_GENPIX_SKYWALKER_1 0x0203
+#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204
+#define USB_PID_GENPIX_SKYWALKER_2 0x0206
+#define USB_PID_SIGMATEK_DVB_110 0x6610
+#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
+#define USB_PID_MSI_DIGIVOX_DUO 0x8801
+#define USB_PID_OPERA1_COLD 0x2830
+#define USB_PID_OPERA1_WARM 0x3829
+#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
+#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
+#define USB_PID_GIGABYTE_U7000 0x7001
+#define USB_PID_GIGABYTE_U8000 0x7002
+#define USB_PID_ASUS_U3000 0x171f
+#define USB_PID_ASUS_U3000H 0x1736
+#define USB_PID_ASUS_U3100 0x173f
+#define USB_PID_ASUS_U3100MINI_PLUS 0x1779
+#define USB_PID_YUAN_EC372S 0x1edc
+#define USB_PID_YUAN_STK7700PH 0x1f08
+#define USB_PID_YUAN_PD378S 0x2edc
+#define USB_PID_YUAN_MC770 0x0871
+#define USB_PID_YUAN_STK7700D 0x1efc
+#define USB_PID_YUAN_STK7700D_2 0x1e8c
+#define USB_PID_DW2102 0x2102
+#define USB_PID_XTENSIONS_XD_380 0x0381
+#define USB_PID_TELESTAR_STARSTICK_2 0x8000
+#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
+#define USB_PID_SONY_PLAYTV 0x0003
+#define USB_PID_MYGICA_D689 0xd811
+#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
+#define USB_PID_ELGATO_EYETV_DTT 0x0021
+#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
+#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
+#define USB_PID_ELGATO_EYETV_SAT 0x002a
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
+#define USB_PID_FRIIO_WHITE 0x0001
+#define USB_PID_TVWAY_PLUS 0x0002
+#define USB_PID_SVEON_STV20 0xe39d
+#define USB_PID_SVEON_STV22 0xe401
+#define USB_PID_SVEON_STV22_IT9137 0xe411
+#define USB_PID_AZUREWAVE_AZ6027 0x3275
+#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4
+#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-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 9be65a3b931f..9be65a3b931f 100644
--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h
index 7df2e141187a..7df2e141187a 100644
--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
+++ b/drivers/media/dvb-core/dvb_ca_en50221.h
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
new file mode 100644
index 000000000000..d319717eb535
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -0,0 +1,1318 @@
+/*
+ * dvb_demux.c - DVB kernel demux API
+ *
+ * Copyright (C) 2000-2001 Ralph Metzler <ralph@convergence.de>
+ * & Marcus Metzler <marcus@convergence.de>
+ * for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/crc32.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+
+#include "dvb_demux.h"
+
+#define NOBUFS
+/*
+** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
+*/
+// #define DVB_DEMUX_SECTION_LOSS_LOG
+
+static int dvb_demux_tscheck;
+module_param(dvb_demux_tscheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_tscheck,
+ "enable transport stream continuity and TEI check");
+
+static int dvb_demux_speedcheck;
+module_param(dvb_demux_speedcheck, int, 0644);
+MODULE_PARM_DESC(dvb_demux_speedcheck,
+ "enable transport stream speed check");
+
+static int dvb_demux_feed_err_pkts = 1;
+module_param(dvb_demux_feed_err_pkts, int, 0644);
+MODULE_PARM_DESC(dvb_demux_feed_err_pkts,
+ "when set to 0, drop packets with the TEI bit set (1 by default)");
+
+#define dprintk_tscheck(x...) do { \
+ if (dvb_demux_tscheck && printk_ratelimit()) \
+ printk(x); \
+ } while (0)
+
+/******************************************************************************
+ * static inlined helper functions
+ ******************************************************************************/
+
+static inline u16 section_length(const u8 *buf)
+{
+ return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
+}
+
+static inline u16 ts_pid(const u8 *buf)
+{
+ return ((buf[1] & 0x1f) << 8) + buf[2];
+}
+
+static inline u8 payload(const u8 *tsp)
+{
+ if (!(tsp[3] & 0x10)) // no payload?
+ return 0;
+
+ if (tsp[3] & 0x20) { // adaptation field?
+ if (tsp[4] > 183) // corrupted data?
+ return 0;
+ else
+ return 184 - 1 - tsp[4];
+ }
+
+ return 184;
+}
+
+static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)
+{
+ return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));
+}
+
+static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,
+ size_t len)
+{
+ memcpy(d, s, len);
+}
+
+/******************************************************************************
+ * Software filter functions
+ ******************************************************************************/
+
+static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ int count = payload(buf);
+ int p;
+ //int ccok;
+ //u8 cc;
+
+ if (count == 0)
+ return -1;
+
+ p = 188 - count;
+
+ /*
+ cc = buf[3] & 0x0f;
+ ccok = ((feed->cc + 1) & 0x0f) == cc;
+ feed->cc = cc;
+ if (!ccok)
+ printk("missed packet!\n");
+ */
+
+ if (buf[1] & 0x40) // PUSI ?
+ feed->peslen = 0xfffa;
+
+ feed->peslen += count;
+
+ return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
+}
+
+static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
+ struct dvb_demux_filter *f)
+{
+ u8 neq = 0;
+ int i;
+
+ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+ u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i];
+
+ if (f->maskandmode[i] & xor)
+ return 0;
+
+ neq |= f->maskandnotmode[i] & xor;
+ }
+
+ if (f->doneq && !neq)
+ return 0;
+
+ return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
+ NULL, 0, &f->filter, DMX_OK);
+}
+
+static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct dvb_demux_filter *f = feed->filter;
+ struct dmx_section_feed *sec = &feed->feed.sec;
+ int section_syntax_indicator;
+
+ if (!sec->is_filtering)
+ return 0;
+
+ if (!f)
+ return 0;
+
+ if (sec->check_crc) {
+ section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
+ if (section_syntax_indicator &&
+ demux->check_crc32(feed, sec->secbuf, sec->seclen))
+ return -1;
+ }
+
+ do {
+ if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0)
+ return -1;
+ } while ((f = f->next) && sec->is_filtering);
+
+ sec->seclen = 0;
+
+ return 0;
+}
+
+static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
+{
+ struct dmx_section_feed *sec = &feed->feed.sec;
+
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+ if (sec->secbufp < sec->tsfeedp) {
+ int i, n = sec->tsfeedp - sec->secbufp;
+
+ /*
+ * Section padding is done with 0xff bytes entirely.
+ * Due to speed reasons, we won't check all of them
+ * but just first and last.
+ */
+ if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {
+ printk("dvb_demux.c section ts padding loss: %d/%d\n",
+ n, sec->tsfeedp);
+ printk("dvb_demux.c pad data:");
+ for (i = 0; i < n; i++)
+ printk(" %02x", sec->secbuf[i]);
+ printk("\n");
+ }
+ }
+#endif
+
+ sec->tsfeedp = sec->secbufp = sec->seclen = 0;
+ sec->secbuf = sec->secbuf_base;
+}
+
+/*
+ * Losless Section Demux 1.4.1 by Emard
+ * Valsecchi Patrick:
+ * - middle of section A (no PUSI)
+ * - end of section A and start of section B
+ * (with PUSI pointing to the start of the second section)
+ *
+ * In this case, without feed->pusi_seen you'll receive a garbage section
+ * consisting of the end of section A. Basically because tsfeedp
+ * is incemented and the use=0 condition is not raised
+ * when the second packet arrives.
+ *
+ * Fix:
+ * when demux is started, let feed->pusi_seen = 0 to
+ * prevent initial feeding of garbage from the end of
+ * previous section. When you for the first time see PUSI=1
+ * then set feed->pusi_seen = 1
+ */
+static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
+ const u8 *buf, u8 len)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct dmx_section_feed *sec = &feed->feed.sec;
+ u16 limit, seclen, n;
+
+ if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
+ return 0;
+
+ if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+ printk("dvb_demux.c section buffer full loss: %d/%d\n",
+ sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
+ DMX_MAX_SECFEED_SIZE);
+#endif
+ len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
+ }
+
+ if (len <= 0)
+ return 0;
+
+ demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
+ sec->tsfeedp += len;
+
+ /*
+ * Dump all the sections we can find in the data (Emard)
+ */
+ limit = sec->tsfeedp;
+ if (limit > DMX_MAX_SECFEED_SIZE)
+ return -1; /* internal error should never happen */
+
+ /* to be sure always set secbuf */
+ sec->secbuf = sec->secbuf_base + sec->secbufp;
+
+ for (n = 0; sec->secbufp + 2 < limit; n++) {
+ seclen = section_length(sec->secbuf);
+ if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
+ || seclen + sec->secbufp > limit)
+ return 0;
+ sec->seclen = seclen;
+ sec->crc_val = ~0;
+ /* dump [secbuf .. secbuf+seclen) */
+ if (feed->pusi_seen)
+ dvb_dmx_swfilter_section_feed(feed);
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+ else
+ printk("dvb_demux.c pusi not seen, discarding section data\n");
+#endif
+ sec->secbufp += seclen; /* secbufp and secbuf moving together is */
+ sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
+ }
+
+ return 0;
+}
+
+static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ u8 p, count;
+ int ccok, dc_i = 0;
+ u8 cc;
+
+ count = payload(buf);
+
+ if (count == 0) /* count == 0 if no payload or out of range */
+ return -1;
+
+ p = 188 - count; /* payload start */
+
+ cc = buf[3] & 0x0f;
+ ccok = ((feed->cc + 1) & 0x0f) == cc;
+ feed->cc = cc;
+
+ if (buf[3] & 0x20) {
+ /* adaption field present, check for discontinuity_indicator */
+ if ((buf[4] > 0) && (buf[5] & 0x80))
+ dc_i = 1;
+ }
+
+ if (!ccok || dc_i) {
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+ printk("dvb_demux.c discontinuity detected %d bytes lost\n",
+ count);
+ /*
+ * those bytes under sume circumstances will again be reported
+ * in the following dvb_dmx_swfilter_section_new
+ */
+#endif
+ /*
+ * Discontinuity detected. Reset pusi_seen = 0 to
+ * stop feeding of suspicious data until next PUSI=1 arrives
+ */
+ feed->pusi_seen = 0;
+ dvb_dmx_swfilter_section_new(feed);
+ }
+
+ if (buf[1] & 0x40) {
+ /* PUSI=1 (is set), section boundary is here */
+ if (count > 1 && buf[p] < count) {
+ const u8 *before = &buf[p + 1];
+ u8 before_len = buf[p];
+ const u8 *after = &before[before_len];
+ u8 after_len = count - 1 - before_len;
+
+ dvb_dmx_swfilter_section_copy_dump(feed, before,
+ before_len);
+ /* before start of new section, set pusi_seen = 1 */
+ feed->pusi_seen = 1;
+ dvb_dmx_swfilter_section_new(feed);
+ dvb_dmx_swfilter_section_copy_dump(feed, after,
+ after_len);
+ }
+#ifdef DVB_DEMUX_SECTION_LOSS_LOG
+ else if (count > 0)
+ printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
+#endif
+ } else {
+ /* PUSI=0 (is not set), no section boundary */
+ dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
+ }
+
+ return 0;
+}
+
+static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
+ const u8 *buf)
+{
+ switch (feed->type) {
+ case DMX_TYPE_TS:
+ if (!feed->feed.ts.is_filtering)
+ break;
+ if (feed->ts_type & TS_PACKET) {
+ if (feed->ts_type & TS_PAYLOAD_ONLY)
+ dvb_dmx_swfilter_payload(feed, buf);
+ else
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
+ DMX_OK);
+ }
+ if (feed->ts_type & TS_DECODER)
+ if (feed->demux->write_to_decoder)
+ feed->demux->write_to_decoder(feed, buf, 188);
+ break;
+
+ case DMX_TYPE_SEC:
+ if (!feed->feed.sec.is_filtering)
+ break;
+ if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
+ feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
+ break;
+
+ default:
+ break;
+ }
+}
+
+#define DVR_FEED(f) \
+ (((f)->type == DMX_TYPE_TS) && \
+ ((f)->feed.ts.is_filtering) && \
+ (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
+
+static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
+{
+ struct dvb_demux_feed *feed;
+ u16 pid = ts_pid(buf);
+ int dvr_done = 0;
+
+ if (dvb_demux_speedcheck) {
+ struct timespec cur_time, delta_time;
+ u64 speed_bytes, speed_timedelta;
+
+ demux->speed_pkts_cnt++;
+
+ /* show speed every SPEED_PKTS_INTERVAL packets */
+ if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
+ cur_time = current_kernel_time();
+
+ if (demux->speed_last_time.tv_sec != 0 &&
+ demux->speed_last_time.tv_nsec != 0) {
+ delta_time = timespec_sub(cur_time,
+ demux->speed_last_time);
+ speed_bytes = (u64)demux->speed_pkts_cnt
+ * 188 * 8;
+ /* convert to 1024 basis */
+ speed_bytes = 1000 * div64_u64(speed_bytes,
+ 1024);
+ speed_timedelta =
+ (u64)timespec_to_ns(&delta_time);
+ speed_timedelta = div64_u64(speed_timedelta,
+ 1000000); /* nsec -> usec */
+ printk(KERN_INFO "TS speed %llu Kbits/sec \n",
+ div64_u64(speed_bytes,
+ speed_timedelta));
+ }
+
+ demux->speed_last_time = cur_time;
+ demux->speed_pkts_cnt = 0;
+ }
+ }
+
+ if (buf[1] & 0x80) {
+ dprintk_tscheck("TEI detected. "
+ "PID=0x%x data1=0x%x\n",
+ pid, buf[1]);
+ /* data in this packet cant be trusted - drop it unless
+ * module option dvb_demux_feed_err_pkts is set */
+ if (!dvb_demux_feed_err_pkts)
+ return;
+ } else /* if TEI bit is set, pid may be wrong- skip pkt counter */
+ if (demux->cnt_storage && dvb_demux_tscheck) {
+ /* check pkt counter */
+ if (pid < MAX_PID) {
+ if ((buf[3] & 0xf) != demux->cnt_storage[pid])
+ dprintk_tscheck("TS packet counter mismatch. "
+ "PID=0x%x expected 0x%x "
+ "got 0x%x\n",
+ pid, demux->cnt_storage[pid],
+ buf[3] & 0xf);
+
+ demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf;
+ }
+ /* end check */
+ }
+
+ list_for_each_entry(feed, &demux->feed_list, list_head) {
+ if ((feed->pid != pid) && (feed->pid != 0x2000))
+ continue;
+
+ /* copy each packet only once to the dvr device, even
+ * if a PID is in multiple filters (e.g. video + PCR) */
+ if ((DVR_FEED(feed)) && (dvr_done++))
+ continue;
+
+ if (feed->pid == pid)
+ dvb_dmx_swfilter_packet_type(feed, buf);
+ else if (feed->pid == 0x2000)
+ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
+ }
+}
+
+void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
+ size_t count)
+{
+ spin_lock(&demux->lock);
+
+ while (count--) {
+ if (buf[0] == 0x47)
+ dvb_dmx_swfilter_packet(demux, buf);
+ buf += 188;
+ }
+
+ spin_unlock(&demux->lock);
+}
+
+EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
+
+static inline int find_next_packet(const u8 *buf, int pos, size_t count,
+ const int pktsize)
+{
+ int start = pos, lost;
+
+ while (pos < count) {
+ if (buf[pos] == 0x47 ||
+ (pktsize == 204 && buf[pos] == 0xB8))
+ break;
+ pos++;
+ }
+
+ lost = pos - start;
+ if (lost) {
+ /* This garbage is part of a valid packet? */
+ int backtrack = pos - pktsize;
+ if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
+ (pktsize == 204 && buf[backtrack] == 0xB8)))
+ return backtrack;
+ }
+
+ return pos;
+}
+
+/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
+static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
+ size_t count, const int pktsize)
+{
+ int p = 0, i, j;
+ const u8 *q;
+
+ spin_lock(&demux->lock);
+
+ if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
+ i = demux->tsbufp;
+ j = pktsize - i;
+ if (count < j) {
+ memcpy(&demux->tsbuf[i], buf, count);
+ demux->tsbufp += count;
+ goto bailout;
+ }
+ memcpy(&demux->tsbuf[i], buf, j);
+ if (demux->tsbuf[0] == 0x47) /* double check */
+ dvb_dmx_swfilter_packet(demux, demux->tsbuf);
+ demux->tsbufp = 0;
+ p += j;
+ }
+
+ while (1) {
+ p = find_next_packet(buf, p, count, pktsize);
+ if (p >= count)
+ break;
+ if (count - p < pktsize)
+ break;
+
+ q = &buf[p];
+
+ if (pktsize == 204 && (*q == 0xB8)) {
+ memcpy(demux->tsbuf, q, 188);
+ demux->tsbuf[0] = 0x47;
+ q = demux->tsbuf;
+ }
+ dvb_dmx_swfilter_packet(demux, q);
+ p += pktsize;
+ }
+
+ i = count - p;
+ if (i) {
+ memcpy(demux->tsbuf, &buf[p], i);
+ demux->tsbufp = i;
+ if (pktsize == 204 && demux->tsbuf[0] == 0xB8)
+ demux->tsbuf[0] = 0x47;
+ }
+
+bailout:
+ spin_unlock(&demux->lock);
+}
+
+void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ _dvb_dmx_swfilter(demux, buf, count, 188);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter);
+
+void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ _dvb_dmx_swfilter(demux, buf, count, 204);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+
+void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ spin_lock(&demux->lock);
+
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
+
+ spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
+
+static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
+{
+ int i;
+
+ for (i = 0; i < demux->filternum; i++)
+ if (demux->filter[i].state == DMX_STATE_FREE)
+ break;
+
+ if (i == demux->filternum)
+ return NULL;
+
+ demux->filter[i].state = DMX_STATE_ALLOCATED;
+
+ return &demux->filter[i];
+}
+
+static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
+{
+ int i;
+
+ for (i = 0; i < demux->feednum; i++)
+ if (demux->feed[i].state == DMX_STATE_FREE)
+ break;
+
+ if (i == demux->feednum)
+ return NULL;
+
+ demux->feed[i].state = DMX_STATE_ALLOCATED;
+
+ return &demux->feed[i];
+}
+
+static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux_feed *entry;
+
+ list_for_each_entry(entry, &feed->demux->feed_list, list_head)
+ if (entry == feed)
+ return 1;
+
+ return 0;
+}
+
+static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
+{
+ spin_lock_irq(&feed->demux->lock);
+ if (dvb_demux_feed_find(feed)) {
+ printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
+ __func__, feed->type, feed->state, feed->pid);
+ goto out;
+ }
+
+ list_add(&feed->list_head, &feed->demux->feed_list);
+out:
+ spin_unlock_irq(&feed->demux->lock);
+}
+
+static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
+{
+ spin_lock_irq(&feed->demux->lock);
+ if (!(dvb_demux_feed_find(feed))) {
+ printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
+ __func__, feed->type, feed->state, feed->pid);
+ goto out;
+ }
+
+ list_del(&feed->list_head);
+out:
+ spin_unlock_irq(&feed->demux->lock);
+}
+
+static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
+ enum dmx_ts_pes pes_type,
+ size_t circular_buffer_size, struct timespec timeout)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+
+ if (pid > DMX_MAX_PID)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (ts_type & TS_DECODER) {
+ if (pes_type >= DMX_TS_PES_OTHER) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (demux->pesfilter[pes_type] &&
+ demux->pesfilter[pes_type] != feed) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ demux->pesfilter[pes_type] = feed;
+ demux->pids[pes_type] = pid;
+ }
+
+ dvb_demux_feed_add(feed);
+
+ feed->pid = pid;
+ feed->buffer_size = circular_buffer_size;
+ feed->timeout = timeout;
+ feed->ts_type = ts_type;
+ feed->pes_type = pes_type;
+
+ if (feed->buffer_size) {
+#ifdef NOBUFS
+ feed->buffer = NULL;
+#else
+ feed->buffer = vmalloc(feed->buffer_size);
+ if (!feed->buffer) {
+ mutex_unlock(&demux->mutex);
+ return -ENOMEM;
+ }
+#endif
+ }
+
+ feed->state = DMX_STATE_READY;
+ mutex_unlock(&demux->mutex);
+
+ return 0;
+}
+
+static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret;
+
+ if (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->start_feed) {
+ mutex_unlock(&demux->mutex);
+ return -ENODEV;
+ }
+
+ if ((ret = demux->start_feed(feed)) < 0) {
+ mutex_unlock(&demux->mutex);
+ return ret;
+ }
+
+ spin_lock_irq(&demux->lock);
+ ts_feed->is_filtering = 1;
+ feed->state = DMX_STATE_GO;
+ spin_unlock_irq(&demux->lock);
+ mutex_unlock(&demux->mutex);
+
+ return 0;
+}
+
+static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
+{
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+ struct dvb_demux *demux = feed->demux;
+ int ret;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state < DMX_STATE_GO) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+
+ if (!demux->stop_feed) {
+ mutex_unlock(&demux->mutex);
+ return -ENODEV;
+ }
+
+ ret = demux->stop_feed(feed);
+
+ spin_lock_irq(&demux->lock);
+ ts_feed->is_filtering = 0;
+ feed->state = DMX_STATE_ALLOCATED;
+ spin_unlock_irq(&demux->lock);
+ mutex_unlock(&demux->mutex);
+
+ return ret;
+}
+
+static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
+ struct dmx_ts_feed **ts_feed,
+ dmx_ts_cb callback)
+{
+ struct dvb_demux *demux = (struct dvb_demux *)dmx;
+ struct dvb_demux_feed *feed;
+
+ if (mutex_lock_interruptible(&demux->mutex))
+ return -ERESTARTSYS;
+
+ if (!(feed = dvb_dmx_feed_alloc(demux))) {
+ mutex_unlock(&demux->mutex);
+ return -EBUSY;
+ }
+
+ feed->type = DMX_TYPE_TS;
+ feed->cb.ts = callback;
+ feed->demux = demux;
+ feed->pid = 0xffff;
+ feed->peslen = 0xfffa;
+ feed->buffer = NULL;
+
+ (*ts_feed) = &feed->feed.ts;
+ (*ts_feed)->parent = dmx;
+ (*ts_feed)->priv = NULL;
+ (*ts_feed)->is_filtering = 0;
+ (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
+ (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
+ (*ts_feed)->set = dmx_ts_feed_set;
+
+ if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
+ feed->state = DMX_STATE_FREE;
+ mutex_unlock(&demux->mutex);
+ return -EBUSY;
+ }
+
+ feed->filter->type = DMX_TYPE_TS;
+ feed->filter->feed = feed;
+ feed->filter->state = DMX_STATE_READY;
+
+ mutex_unlock(&demux->mutex);
+
+ return 0;
+}
+
+static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
+ struct dmx_ts_feed *ts_feed)
+{
+ struct dvb_demux *demux = (struct dvb_demux *)dmx;
+ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+
+ mutex_lock(&demux->mutex);
+
+ if (feed->state == DMX_STATE_FREE) {
+ mutex_unlock(&demux->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ vfree(feed->buffer);
+ feed->buffer = NULL;
+#endif
+
+ feed->state = DMX_STATE_FREE;
+ feed->filter->state = DMX_STATE_FREE;
+
+ dvb_demux_feed_del(feed);
+
+ feed->pid = 0xffff;
+
+ if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
+ demux->pesfilter[feed->pes_type] = NULL;
+
+ mutex_unlock(&demux->mutex);
+ return 0;
+}
+
+/******************************************************************************
+ * dmx_section_feed API calls
+ ******************************************************************************/
+
+static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
+ struct dmx_section_filter **filter)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
+ struct dvb_demux_filter *dvbdmxfilter;
+
+ if (mutex_lock_interruptible(&dvbdemux->mutex))
+ return -ERESTARTSYS;
+
+ dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
+ if (!dvbdmxfilter) {
+ mutex_unlock(&dvbdemux->mutex);
+ return -EBUSY;
+ }
+
+ spin_lock_irq(&dvbdemux->lock);
+ *filter = &dvbdmxfilter->filter;
+ (*filter)->parent = feed;
+ (*filter)->priv = NULL;
+ dvbdmxfilter->feed = dvbdmxfeed;
+ dvbdmxfilter->type = DMX_TYPE_SEC;
+ dvbdmxfilter->state = DMX_STATE_READY;
+ dvbdmxfilter->next = dvbdmxfeed->filter;
+ dvbdmxfeed->filter = dvbdmxfilter;
+ spin_unlock_irq(&dvbdemux->lock);
+
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dmx_section_feed_set(struct dmx_section_feed *feed,
+ u16 pid, size_t circular_buffer_size,
+ int check_crc)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ if (pid > 0x1fff)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ dvb_demux_feed_add(dvbdmxfeed);
+
+ dvbdmxfeed->pid = pid;
+ dvbdmxfeed->buffer_size = circular_buffer_size;
+ dvbdmxfeed->feed.sec.check_crc = check_crc;
+
+#ifdef NOBUFS
+ dvbdmxfeed->buffer = NULL;
+#else
+ dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
+ if (!dvbdmxfeed->buffer) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -ENOMEM;
+ }
+#endif
+
+ dvbdmxfeed->state = DMX_STATE_READY;
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
+{
+ int i;
+ struct dvb_demux_filter *f;
+ struct dmx_section_filter *sf;
+ u8 mask, mode, doneq;
+
+ if (!(f = dvbdmxfeed->filter))
+ return;
+ do {
+ sf = &f->filter;
+ doneq = 0;
+ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
+ mode = sf->filter_mode[i];
+ mask = sf->filter_mask[i];
+ f->maskandmode[i] = mask & mode;
+ doneq |= f->maskandnotmode[i] = mask & ~mode;
+ }
+ f->doneq = doneq ? 1 : 0;
+ } while ((f = f->next));
+}
+
+static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (feed->is_filtering) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ if (!dvbdmxfeed->filter) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ dvbdmxfeed->feed.sec.tsfeedp = 0;
+ dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+ dvbdmxfeed->feed.sec.secbufp = 0;
+ dvbdmxfeed->feed.sec.seclen = 0;
+
+ if (!dvbdmx->start_feed) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -ENODEV;
+ }
+
+ prepare_secfilters(dvbdmxfeed);
+
+ if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+ }
+
+ spin_lock_irq(&dvbdmx->lock);
+ feed->is_filtering = 1;
+ dvbdmxfeed->state = DMX_STATE_GO;
+ spin_unlock_irq(&dvbdmx->lock);
+
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ int ret;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (!dvbdmx->stop_feed) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -ENODEV;
+ }
+
+ ret = dvbdmx->stop_feed(dvbdmxfeed);
+
+ spin_lock_irq(&dvbdmx->lock);
+ dvbdmxfeed->state = DMX_STATE_READY;
+ feed->is_filtering = 0;
+ spin_unlock_irq(&dvbdmx->lock);
+
+ mutex_unlock(&dvbdmx->mutex);
+ return ret;
+}
+
+static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
+ struct dmx_section_filter *filter)
+{
+ struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f;
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfilter->feed != dvbdmxfeed) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+
+ if (feed->is_filtering)
+ feed->stop_filtering(feed);
+
+ spin_lock_irq(&dvbdmx->lock);
+ f = dvbdmxfeed->filter;
+
+ if (f == dvbdmxfilter) {
+ dvbdmxfeed->filter = dvbdmxfilter->next;
+ } else {
+ while (f->next != dvbdmxfilter)
+ f = f->next;
+ f->next = f->next->next;
+ }
+
+ dvbdmxfilter->state = DMX_STATE_FREE;
+ spin_unlock_irq(&dvbdmx->lock);
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
+ struct dmx_section_feed **feed,
+ dmx_section_cb callback)
+{
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+ struct dvb_demux_feed *dvbdmxfeed;
+
+ if (mutex_lock_interruptible(&dvbdmx->mutex))
+ return -ERESTARTSYS;
+
+ if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EBUSY;
+ }
+
+ dvbdmxfeed->type = DMX_TYPE_SEC;
+ dvbdmxfeed->cb.sec = callback;
+ dvbdmxfeed->demux = dvbdmx;
+ dvbdmxfeed->pid = 0xffff;
+ dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
+ dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
+ dvbdmxfeed->feed.sec.tsfeedp = 0;
+ dvbdmxfeed->filter = NULL;
+ dvbdmxfeed->buffer = NULL;
+
+ (*feed) = &dvbdmxfeed->feed.sec;
+ (*feed)->is_filtering = 0;
+ (*feed)->parent = demux;
+ (*feed)->priv = NULL;
+
+ (*feed)->set = dmx_section_feed_set;
+ (*feed)->allocate_filter = dmx_section_feed_allocate_filter;
+ (*feed)->start_filtering = dmx_section_feed_start_filtering;
+ (*feed)->stop_filtering = dmx_section_feed_stop_filtering;
+ (*feed)->release_filter = dmx_section_feed_release_filter;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+static int dvbdmx_release_section_feed(struct dmx_demux *demux,
+ struct dmx_section_feed *feed)
+{
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
+ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdmx->mutex);
+
+ if (dvbdmxfeed->state == DMX_STATE_FREE) {
+ mutex_unlock(&dvbdmx->mutex);
+ return -EINVAL;
+ }
+#ifndef NOBUFS
+ vfree(dvbdmxfeed->buffer);
+ dvbdmxfeed->buffer = NULL;
+#endif
+ dvbdmxfeed->state = DMX_STATE_FREE;
+
+ dvb_demux_feed_del(dvbdmxfeed);
+
+ dvbdmxfeed->pid = 0xffff;
+
+ mutex_unlock(&dvbdmx->mutex);
+ return 0;
+}
+
+/******************************************************************************
+ * dvb_demux kernel data API calls
+ ******************************************************************************/
+
+static int dvbdmx_open(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)
+ return -EUSERS;
+
+ dvbdemux->users++;
+ return 0;
+}
+
+static int dvbdmx_close(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if (dvbdemux->users == 0)
+ return -ENODEV;
+
+ dvbdemux->users--;
+ //FIXME: release any unneeded resources if users==0
+ return 0;
+}
+
+static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+ void *p;
+
+ if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
+ return -EINVAL;
+
+ p = memdup_user(buf, count);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ if (mutex_lock_interruptible(&dvbdemux->mutex)) {
+ kfree(p);
+ return -ERESTARTSYS;
+ }
+ dvb_dmx_swfilter(dvbdemux, p, count);
+ kfree(p);
+ mutex_unlock(&dvbdemux->mutex);
+
+ if (signal_pending(current))
+ return -EINTR;
+ return count;
+}
+
+static int dvbdmx_add_frontend(struct dmx_demux *demux,
+ struct dmx_frontend *frontend)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+ struct list_head *head = &dvbdemux->frontend_list;
+
+ list_add(&(frontend->connectivity_list), head);
+
+ return 0;
+}
+
+static int dvbdmx_remove_frontend(struct dmx_demux *demux,
+ struct dmx_frontend *frontend)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+ struct list_head *pos, *n, *head = &dvbdemux->frontend_list;
+
+ list_for_each_safe(pos, n, head) {
+ if (DMX_FE_ENTRY(pos) == frontend) {
+ list_del(pos);
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if (list_empty(&dvbdemux->frontend_list))
+ return NULL;
+
+ return &dvbdemux->frontend_list;
+}
+
+static int dvbdmx_connect_frontend(struct dmx_demux *demux,
+ struct dmx_frontend *frontend)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ if (demux->frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ demux->frontend = frontend;
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ mutex_lock(&dvbdemux->mutex);
+
+ demux->frontend = NULL;
+ mutex_unlock(&dvbdemux->mutex);
+ return 0;
+}
+
+static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
+{
+ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
+
+ memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));
+ return 0;
+}
+
+int dvb_dmx_init(struct dvb_demux *dvbdemux)
+{
+ int i;
+ struct dmx_demux *dmx = &dvbdemux->dmx;
+
+ dvbdemux->cnt_storage = NULL;
+ dvbdemux->users = 0;
+ dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
+
+ if (!dvbdemux->filter)
+ return -ENOMEM;
+
+ dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
+ if (!dvbdemux->feed) {
+ vfree(dvbdemux->filter);
+ dvbdemux->filter = NULL;
+ return -ENOMEM;
+ }
+ for (i = 0; i < dvbdemux->filternum; i++) {
+ dvbdemux->filter[i].state = DMX_STATE_FREE;
+ dvbdemux->filter[i].index = i;
+ }
+ for (i = 0; i < dvbdemux->feednum; i++) {
+ dvbdemux->feed[i].state = DMX_STATE_FREE;
+ dvbdemux->feed[i].index = i;
+ }
+
+ dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
+ if (!dvbdemux->cnt_storage)
+ printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n");
+
+ INIT_LIST_HEAD(&dvbdemux->frontend_list);
+
+ for (i = 0; i < DMX_TS_PES_OTHER; i++) {
+ dvbdemux->pesfilter[i] = NULL;
+ dvbdemux->pids[i] = 0xffff;
+ }
+
+ INIT_LIST_HEAD(&dvbdemux->feed_list);
+
+ dvbdemux->playing = 0;
+ dvbdemux->recording = 0;
+ dvbdemux->tsbufp = 0;
+
+ if (!dvbdemux->check_crc32)
+ dvbdemux->check_crc32 = dvb_dmx_crc32;
+
+ if (!dvbdemux->memcopy)
+ dvbdemux->memcopy = dvb_dmx_memcopy;
+
+ dmx->frontend = NULL;
+ dmx->priv = dvbdemux;
+ dmx->open = dvbdmx_open;
+ dmx->close = dvbdmx_close;
+ dmx->write = dvbdmx_write;
+ dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
+ dmx->release_ts_feed = dvbdmx_release_ts_feed;
+ dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
+ dmx->release_section_feed = dvbdmx_release_section_feed;
+
+ dmx->add_frontend = dvbdmx_add_frontend;
+ dmx->remove_frontend = dvbdmx_remove_frontend;
+ dmx->get_frontends = dvbdmx_get_frontends;
+ dmx->connect_frontend = dvbdmx_connect_frontend;
+ dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
+ dmx->get_pes_pids = dvbdmx_get_pes_pids;
+
+ mutex_init(&dvbdemux->mutex);
+ spin_lock_init(&dvbdemux->lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dvb_dmx_init);
+
+void dvb_dmx_release(struct dvb_demux *dvbdemux)
+{
+ vfree(dvbdemux->cnt_storage);
+ vfree(dvbdemux->filter);
+ vfree(dvbdemux->feed);
+}
+
+EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h
index fa7188a253aa..fa7188a253aa 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb-core/dvb_demux.h
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.c b/drivers/media/dvb-core/dvb_filter.c
index 772003fb1821..772003fb1821 100644
--- a/drivers/media/dvb/dvb-core/dvb_filter.c
+++ b/drivers/media/dvb-core/dvb_filter.c
diff --git a/drivers/media/dvb/dvb-core/dvb_filter.h b/drivers/media/dvb-core/dvb_filter.h
index 375e3be184b1..375e3be184b1 100644
--- a/drivers/media/dvb/dvb-core/dvb_filter.h
+++ b/drivers/media/dvb-core/dvb_filter.h
diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c
new file mode 100644
index 000000000000..8f58f241c10d
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_frontend.c
@@ -0,0 +1,2617 @@
+/*
+ * dvb_frontend.c: DVB frontend tuning interface/thread
+ *
+ *
+ * Copyright (C) 1999-2001 Ralph Metzler
+ * Marcus Metzler
+ * Holger Waechtler
+ * for convergence integrated media GmbH
+ *
+ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* Enables DVBv3 compatibility bits at the headers */
+#define __DVB_CORE__
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/semaphore.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/freezer.h>
+#include <linux/jiffies.h>
+#include <linux/kthread.h>
+#include <asm/processor.h>
+
+#include "dvb_frontend.h"
+#include "dvbdev.h"
+#include <linux/dvb/version.h>
+
+static int dvb_frontend_debug;
+static int dvb_shutdown_timeout;
+static int dvb_force_auto_inversion;
+static int dvb_override_tune_delay;
+static int dvb_powerdown_on_sleep = 1;
+static int dvb_mfe_wait_time = 5;
+
+module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
+MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
+module_param(dvb_shutdown_timeout, int, 0644);
+MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
+module_param(dvb_force_auto_inversion, int, 0644);
+MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
+module_param(dvb_override_tune_delay, int, 0644);
+MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
+module_param(dvb_powerdown_on_sleep, int, 0644);
+MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
+module_param(dvb_mfe_wait_time, int, 0644);
+MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
+
+#define FESTATE_IDLE 1
+#define FESTATE_RETUNE 2
+#define FESTATE_TUNING_FAST 4
+#define FESTATE_TUNING_SLOW 8
+#define FESTATE_TUNED 16
+#define FESTATE_ZIGZAG_FAST 32
+#define FESTATE_ZIGZAG_SLOW 64
+#define FESTATE_DISEQC 128
+#define FESTATE_ERROR 256
+#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
+#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
+#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
+#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
+
+#define FE_ALGO_HW 1
+/*
+ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
+ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
+ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
+ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
+ * FESTATE_TUNED. The frontend has successfully locked on.
+ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
+ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
+ * FESTATE_DISEQC. A DISEQC command has just been issued.
+ * FESTATE_WAITFORLOCK. When we're waiting for a lock.
+ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
+ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
+ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
+ */
+
+#define DVB_FE_NO_EXIT 0
+#define DVB_FE_NORMAL_EXIT 1
+#define DVB_FE_DEVICE_REMOVED 2
+
+static DEFINE_MUTEX(frontend_mutex);
+
+struct dvb_frontend_private {
+
+ /* thread/frontend values */
+ struct dvb_device *dvbdev;
+ struct dvb_frontend_parameters parameters_out;
+ struct dvb_fe_events events;
+ struct semaphore sem;
+ struct list_head list_head;
+ wait_queue_head_t wait_queue;
+ struct task_struct *thread;
+ unsigned long release_jiffies;
+ unsigned int exit;
+ unsigned int wakeup;
+ fe_status_t status;
+ unsigned long tune_mode_flags;
+ unsigned int delay;
+ unsigned int reinitialise;
+ int tone;
+ int voltage;
+
+ /* swzigzag values */
+ unsigned int state;
+ unsigned int bending;
+ int lnb_drift;
+ unsigned int inversion;
+ unsigned int auto_step;
+ unsigned int auto_sub_step;
+ unsigned int started_auto_step;
+ unsigned int min_delay;
+ unsigned int max_drift;
+ unsigned int step_size;
+ int quality;
+ unsigned int check_wrapped;
+ enum dvbfe_search algo_status;
+};
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe);
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p_out);
+static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p);
+
+static bool has_get_frontend(struct dvb_frontend *fe)
+{
+ return fe->ops.get_frontend != NULL;
+}
+
+/*
+ * Due to DVBv3 API calls, a delivery system should be mapped into one of
+ * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC),
+ * otherwise, a DVBv3 call will fail.
+ */
+enum dvbv3_emulation_type {
+ DVBV3_UNKNOWN,
+ DVBV3_QPSK,
+ DVBV3_QAM,
+ DVBV3_OFDM,
+ DVBV3_ATSC,
+};
+
+static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
+{
+ switch (delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ return DVBV3_QAM;
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_ISDBS:
+ case SYS_DSS:
+ return DVBV3_QPSK;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ return DVBV3_OFDM;
+ case SYS_ATSC:
+ case SYS_ATSCMH:
+ case SYS_DVBC_ANNEX_B:
+ return DVBV3_ATSC;
+ case SYS_UNDEFINED:
+ case SYS_ISDBC:
+ case SYS_DVBH:
+ case SYS_DAB:
+ default:
+ /*
+ * Doesn't know how to emulate those types and/or
+ * there's no frontend driver from this type yet
+ * with some emulation code, so, we're not sure yet how
+ * to handle them, or they're not compatible with a DVBv3 call.
+ */
+ return DVBV3_UNKNOWN;
+ }
+}
+
+static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+ struct dvb_frontend_event *e;
+ int wp;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
+ dtv_get_frontend(fe, &fepriv->parameters_out);
+
+ mutex_lock(&events->mtx);
+
+ wp = (events->eventw + 1) % MAX_EVENT;
+ if (wp == events->eventr) {
+ events->overflow = 1;
+ events->eventr = (events->eventr + 1) % MAX_EVENT;
+ }
+
+ e = &events->events[events->eventw];
+ e->status = status;
+ e->parameters = fepriv->parameters_out;
+
+ events->eventw = wp;
+
+ mutex_unlock(&events->mtx);
+
+ wake_up_interruptible (&events->wait_queue);
+}
+
+static int dvb_frontend_get_event(struct dvb_frontend *fe,
+ struct dvb_frontend_event *event, int flags)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (events->overflow) {
+ events->overflow = 0;
+ return -EOVERFLOW;
+ }
+
+ if (events->eventw == events->eventr) {
+ int ret;
+
+ if (flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ up(&fepriv->sem);
+
+ ret = wait_event_interruptible (events->wait_queue,
+ events->eventw != events->eventr);
+
+ if (down_interruptible (&fepriv->sem))
+ return -ERESTARTSYS;
+
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&events->mtx);
+ *event = events->events[events->eventr];
+ events->eventr = (events->eventr + 1) % MAX_EVENT;
+ mutex_unlock(&events->mtx);
+
+ return 0;
+}
+
+static void dvb_frontend_clear_events(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_fe_events *events = &fepriv->events;
+
+ mutex_lock(&events->mtx);
+ events->eventr = events->eventw;
+ mutex_unlock(&events->mtx);
+}
+
+static void dvb_frontend_init(struct dvb_frontend *fe)
+{
+ dev_dbg(fe->dvb->device,
+ "%s: initialising adapter %i frontend %i (%s)...\n",
+ __func__, fe->dvb->num, fe->id, fe->ops.info.name);
+
+ if (fe->ops.init)
+ fe->ops.init(fe);
+ if (fe->ops.tuner_ops.init) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.init(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+}
+
+void dvb_frontend_reinitialise(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ fepriv->reinitialise = 1;
+ dvb_frontend_wakeup(fe);
+}
+EXPORT_SYMBOL(dvb_frontend_reinitialise);
+
+static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)
+{
+ int q2;
+ struct dvb_frontend *fe = fepriv->dvbdev->priv;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (locked)
+ (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
+ else
+ (fepriv->quality) = (fepriv->quality * 220 + 0) / 256;
+
+ q2 = fepriv->quality - 128;
+ q2 *= q2;
+
+ fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128);
+}
+
+/**
+ * Performs automatic twiddling of frontend parameters.
+ *
+ * @param fe The frontend concerned.
+ * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
+ * @returns Number of complete iterations that have been performed.
+ */
+static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped)
+{
+ int autoinversion;
+ int ready = 0;
+ int fe_set_err = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+ int original_inversion = c->inversion;
+ u32 original_frequency = c->frequency;
+
+ /* are we using autoinversion? */
+ autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
+ (c->inversion == INVERSION_AUTO));
+
+ /* setup parameters correctly */
+ while(!ready) {
+ /* calculate the lnb_drift */
+ fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
+
+ /* wrap the auto_step if we've exceeded the maximum drift */
+ if (fepriv->lnb_drift > fepriv->max_drift) {
+ fepriv->auto_step = 0;
+ fepriv->auto_sub_step = 0;
+ fepriv->lnb_drift = 0;
+ }
+
+ /* perform inversion and +/- zigzag */
+ switch(fepriv->auto_sub_step) {
+ case 0:
+ /* try with the current inversion and current drift setting */
+ ready = 1;
+ break;
+
+ case 1:
+ if (!autoinversion) break;
+
+ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+ ready = 1;
+ break;
+
+ case 2:
+ if (fepriv->lnb_drift == 0) break;
+
+ fepriv->lnb_drift = -fepriv->lnb_drift;
+ ready = 1;
+ break;
+
+ case 3:
+ if (fepriv->lnb_drift == 0) break;
+ if (!autoinversion) break;
+
+ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
+ fepriv->lnb_drift = -fepriv->lnb_drift;
+ ready = 1;
+ break;
+
+ default:
+ fepriv->auto_step++;
+ fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
+ break;
+ }
+
+ if (!ready) fepriv->auto_sub_step++;
+ }
+
+ /* if this attempt would hit where we started, indicate a complete
+ * iteration has occurred */
+ if ((fepriv->auto_step == fepriv->started_auto_step) &&
+ (fepriv->auto_sub_step == 0) && check_wrapped) {
+ return 1;
+ }
+
+ dev_dbg(fe->dvb->device, "%s: drift:%i inversion:%i auto_step:%i " \
+ "auto_sub_step:%i started_auto_step:%i\n",
+ __func__, fepriv->lnb_drift, fepriv->inversion,
+ fepriv->auto_step, fepriv->auto_sub_step,
+ fepriv->started_auto_step);
+
+ /* set the frontend itself */
+ c->frequency += fepriv->lnb_drift;
+ if (autoinversion)
+ c->inversion = fepriv->inversion;
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ fe_set_err = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (fe_set_err < 0) {
+ fepriv->state = FESTATE_ERROR;
+ return fe_set_err;
+ }
+
+ c->frequency = original_frequency;
+ c->inversion = original_inversion;
+
+ fepriv->auto_sub_step++;
+ return 0;
+}
+
+static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
+{
+ fe_status_t s = 0;
+ int retval = 0;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
+
+ /* if we've got no parameters, just keep idling */
+ if (fepriv->state & FESTATE_IDLE) {
+ fepriv->delay = 3*HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* in SCAN mode, we just set the frontend when asked and leave it alone */
+ if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
+ if (fepriv->state & FESTATE_RETUNE) {
+ tmp = *c;
+ if (fe->ops.set_frontend)
+ retval = fe->ops.set_frontend(fe);
+ *c = tmp;
+ if (retval < 0)
+ fepriv->state = FESTATE_ERROR;
+ else
+ fepriv->state = FESTATE_TUNED;
+ }
+ fepriv->delay = 3*HZ;
+ fepriv->quality = 0;
+ return;
+ }
+
+ /* get the frontend status */
+ if (fepriv->state & FESTATE_RETUNE) {
+ s = 0;
+ } else {
+ if (fe->ops.read_status)
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
+ }
+
+ /* if we're not tuned, and we have a lock, move to the TUNED state */
+ if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ fepriv->state = FESTATE_TUNED;
+
+ /* if we're tuned, then we have determined the correct inversion */
+ if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
+ (c->inversion == INVERSION_AUTO)) {
+ c->inversion = fepriv->inversion;
+ }
+ return;
+ }
+
+ /* if we are tuned already, check we're still locked */
+ if (fepriv->state & FESTATE_TUNED) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* we're tuned, and the lock is still good... */
+ if (s & FE_HAS_LOCK) {
+ return;
+ } else { /* if we _WERE_ tuned, but now don't have a lock */
+ fepriv->state = FESTATE_ZIGZAG_FAST;
+ fepriv->started_auto_step = fepriv->auto_step;
+ fepriv->check_wrapped = 0;
+ }
+ }
+
+ /* don't actually do anything if we're in the LOSTLOCK state,
+ * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
+ if ((fepriv->state & FESTATE_LOSTLOCK) &&
+ (fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* don't do anything if we're in the DISEQC state, since this
+ * might be someone with a motorized dish controlled by DISEQC.
+ * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */
+ if (fepriv->state & FESTATE_DISEQC) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+ return;
+ }
+
+ /* if we're in the RETUNE state, set everything up for a brand
+ * new scan, keeping the current inversion setting, as the next
+ * tune is _very_ likely to require the same */
+ if (fepriv->state & FESTATE_RETUNE) {
+ fepriv->lnb_drift = 0;
+ fepriv->auto_step = 0;
+ fepriv->auto_sub_step = 0;
+ fepriv->started_auto_step = 0;
+ fepriv->check_wrapped = 0;
+ }
+
+ /* fast zigzag. */
+ if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
+ fepriv->delay = fepriv->min_delay;
+
+ /* perform a tune */
+ retval = dvb_frontend_swzigzag_autotune(fe,
+ fepriv->check_wrapped);
+ if (retval < 0) {
+ return;
+ } else if (retval) {
+ /* OK, if we've run out of trials at the fast speed.
+ * Drop back to slow for the _next_ attempt */
+ fepriv->state = FESTATE_SEARCHING_SLOW;
+ fepriv->started_auto_step = fepriv->auto_step;
+ return;
+ }
+ fepriv->check_wrapped = 1;
+
+ /* if we've just retuned, enter the ZIGZAG_FAST state.
+ * This ensures we cannot return from an
+ * FE_SET_FRONTEND ioctl before the first frontend tune
+ * occurs */
+ if (fepriv->state & FESTATE_RETUNE) {
+ fepriv->state = FESTATE_TUNING_FAST;
+ }
+ }
+
+ /* slow zigzag */
+ if (fepriv->state & FESTATE_SEARCHING_SLOW) {
+ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
+
+ /* Note: don't bother checking for wrapping; we stay in this
+ * state until we get a lock */
+ dvb_frontend_swzigzag_autotune(fe, 0);
+ }
+}
+
+static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fepriv->exit != DVB_FE_NO_EXIT)
+ return 1;
+
+ if (fepriv->dvbdev->writers == 1)
+ if (time_after_eq(jiffies, fepriv->release_jiffies +
+ dvb_shutdown_timeout * HZ))
+ return 1;
+
+ return 0;
+}
+
+static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ if (fepriv->wakeup) {
+ fepriv->wakeup = 0;
+ return 1;
+ }
+ return dvb_frontend_is_exiting(fe);
+}
+
+static void dvb_frontend_wakeup(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ fepriv->wakeup = 1;
+ wake_up_interruptible(&fepriv->wait_queue);
+}
+
+static int dvb_frontend_thread(void *data)
+{
+ struct dvb_frontend *fe = data;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ fe_status_t s;
+ enum dvbfe_algo algo;
+
+ bool re_tune = false;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ fepriv->check_wrapped = 0;
+ fepriv->quality = 0;
+ fepriv->delay = 3*HZ;
+ fepriv->status = 0;
+ fepriv->wakeup = 0;
+ fepriv->reinitialise = 0;
+
+ dvb_frontend_init(fe);
+
+ set_freezable();
+ while (1) {
+ up(&fepriv->sem); /* is locked when we enter the thread... */
+restart:
+ wait_event_interruptible_timeout(fepriv->wait_queue,
+ dvb_frontend_should_wakeup(fe) || kthread_should_stop()
+ || freezing(current),
+ fepriv->delay);
+
+ if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
+ /* got signal or quitting */
+ fepriv->exit = DVB_FE_NORMAL_EXIT;
+ break;
+ }
+
+ if (try_to_freeze())
+ goto restart;
+
+ if (down_interruptible(&fepriv->sem))
+ break;
+
+ if (fepriv->reinitialise) {
+ dvb_frontend_init(fe);
+ if (fe->ops.set_tone && fepriv->tone != -1)
+ fe->ops.set_tone(fe, fepriv->tone);
+ if (fe->ops.set_voltage && fepriv->voltage != -1)
+ fe->ops.set_voltage(fe, fepriv->voltage);
+ fepriv->reinitialise = 0;
+ }
+
+ /* do an iteration of the tuning loop */
+ if (fe->ops.get_frontend_algo) {
+ algo = fe->ops.get_frontend_algo(fe);
+ switch (algo) {
+ case DVBFE_ALGO_HW:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
+
+ if (fepriv->state & FESTATE_RETUNE) {
+ dev_dbg(fe->dvb->device, "%s: Retune requested, FESTATE_RETUNE\n", __func__);
+ re_tune = true;
+ fepriv->state = FESTATE_TUNED;
+ } else {
+ re_tune = false;
+ }
+
+ if (fe->ops.tune)
+ fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s);
+
+ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
+ dev_dbg(fe->dvb->device, "%s: state changed, adding current state\n", __func__);
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
+ break;
+ case DVBFE_ALGO_SW:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
+ dvb_frontend_swzigzag(fe);
+ break;
+ case DVBFE_ALGO_CUSTOM:
+ dev_dbg(fe->dvb->device, "%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
+ if (fepriv->state & FESTATE_RETUNE) {
+ dev_dbg(fe->dvb->device, "%s: Retune requested, FESTAT_RETUNE\n", __func__);
+ fepriv->state = FESTATE_TUNED;
+ }
+ /* Case where we are going to search for a carrier
+ * User asked us to retune again for some reason, possibly
+ * requesting a search with a new set of parameters
+ */
+ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
+ if (fe->ops.search) {
+ fepriv->algo_status = fe->ops.search(fe);
+ /* We did do a search as was requested, the flags are
+ * now unset as well and has the flags wrt to search.
+ */
+ } else {
+ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
+ }
+ }
+ /* Track the carrier if the search was successful */
+ if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) {
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ fepriv->delay = HZ / 2;
+ }
+ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+ fe->ops.read_status(fe, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s); /* update event list */
+ fepriv->status = s;
+ if (!(s & FE_HAS_LOCK)) {
+ fepriv->delay = HZ / 10;
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+ } else {
+ fepriv->delay = 60 * HZ;
+ }
+ }
+ break;
+ default:
+ dev_dbg(fe->dvb->device, "%s: UNDEFINED ALGO !\n", __func__);
+ break;
+ }
+ } else {
+ dvb_frontend_swzigzag(fe);
+ }
+ }
+
+ if (dvb_powerdown_on_sleep) {
+ if (fe->ops.set_voltage)
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
+ if (fe->ops.tuner_ops.sleep) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ fe->ops.tuner_ops.sleep(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ if (fe->ops.sleep)
+ fe->ops.sleep(fe);
+ }
+
+ fepriv->thread = NULL;
+ if (kthread_should_stop())
+ fepriv->exit = DVB_FE_DEVICE_REMOVED;
+ else
+ fepriv->exit = DVB_FE_NO_EXIT;
+ mb();
+
+ dvb_frontend_wakeup(fe);
+ return 0;
+}
+
+static void dvb_frontend_stop(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ fepriv->exit = DVB_FE_NORMAL_EXIT;
+ mb();
+
+ if (!fepriv->thread)
+ return;
+
+ kthread_stop(fepriv->thread);
+
+ sema_init(&fepriv->sem, 1);
+ fepriv->state = FESTATE_IDLE;
+
+ /* paranoia check in case a signal arrived */
+ if (fepriv->thread)
+ dev_warn(fe->dvb->device,
+ "dvb_frontend_stop: warning: thread %p won't exit\n",
+ fepriv->thread);
+}
+
+s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime)
+{
+ return ((curtime.tv_usec < lasttime.tv_usec) ?
+ 1000000 - lasttime.tv_usec + curtime.tv_usec :
+ curtime.tv_usec - lasttime.tv_usec);
+}
+EXPORT_SYMBOL(timeval_usec_diff);
+
+static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec)
+{
+ curtime->tv_usec += add_usec;
+ if (curtime->tv_usec >= 1000000) {
+ curtime->tv_usec -= 1000000;
+ curtime->tv_sec++;
+ }
+}
+
+/*
+ * Sleep until gettimeofday() > waketime + add_usec
+ * This needs to be as precise as possible, but as the delay is
+ * usually between 2ms and 32ms, it is done using a scheduled msleep
+ * followed by usleep (normally a busy-wait loop) for the remainder
+ */
+void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec)
+{
+ struct timeval lasttime;
+ s32 delta, newdelta;
+
+ timeval_usec_add(waketime, add_usec);
+
+ do_gettimeofday(&lasttime);
+ delta = timeval_usec_diff(lasttime, *waketime);
+ if (delta > 2500) {
+ msleep((delta - 1500) / 1000);
+ do_gettimeofday(&lasttime);
+ newdelta = timeval_usec_diff(lasttime, *waketime);
+ delta = (newdelta > delta) ? 0 : newdelta;
+ }
+ if (delta > 0)
+ udelay(delta);
+}
+EXPORT_SYMBOL(dvb_frontend_sleep_until);
+
+static int dvb_frontend_start(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct task_struct *fe_thread;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if (fepriv->thread) {
+ if (fepriv->exit == DVB_FE_NO_EXIT)
+ return 0;
+ else
+ dvb_frontend_stop (fe);
+ }
+
+ if (signal_pending(current))
+ return -EINTR;
+ if (down_interruptible (&fepriv->sem))
+ return -EINTR;
+
+ fepriv->state = FESTATE_IDLE;
+ fepriv->exit = DVB_FE_NO_EXIT;
+ fepriv->thread = NULL;
+ mb();
+
+ fe_thread = kthread_run(dvb_frontend_thread, fe,
+ "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
+ if (IS_ERR(fe_thread)) {
+ ret = PTR_ERR(fe_thread);
+ dev_warn(fe->dvb->device,
+ "dvb_frontend_start: failed to start kthread (%d)\n",
+ ret);
+ up(&fepriv->sem);
+ return ret;
+ }
+ fepriv->thread = fe_thread;
+ return 0;
+}
+
+static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
+ u32 *freq_min, u32 *freq_max)
+{
+ *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
+
+ if (fe->ops.info.frequency_max == 0)
+ *freq_max = fe->ops.tuner_ops.info.frequency_max;
+ else if (fe->ops.tuner_ops.info.frequency_max == 0)
+ *freq_max = fe->ops.info.frequency_max;
+ else
+ *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
+
+ if (*freq_min == 0 || *freq_max == 0)
+ dev_warn(fe->dvb->device, "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
+ fe->dvb->num, fe->id);
+}
+
+static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 freq_min;
+ u32 freq_max;
+
+ /* range check: frequency */
+ dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max);
+ if ((freq_min && c->frequency < freq_min) ||
+ (freq_max && c->frequency > freq_max)) {
+ dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->frequency,
+ freq_min, freq_max);
+ return -EINVAL;
+ }
+
+ /* range check: symbol rate */
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if ((fe->ops.info.symbol_rate_min &&
+ c->symbol_rate < fe->ops.info.symbol_rate_min) ||
+ (fe->ops.info.symbol_rate_max &&
+ c->symbol_rate > fe->ops.info.symbol_rate_max)) {
+ dev_warn(fe->dvb->device, "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
+ fe->dvb->num, fe->id, c->symbol_rate,
+ fe->ops.info.symbol_rate_min,
+ fe->ops.info.symbol_rate_max);
+ return -EINVAL;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int i;
+ u32 delsys;
+
+ delsys = c->delivery_system;
+ memset(c, 0, sizeof(struct dtv_frontend_properties));
+ c->delivery_system = delsys;
+
+ c->state = DTV_CLEAR;
+
+ dev_dbg(fe->dvb->device, "%s: Clearing cache for delivery system %d\n",
+ __func__, c->delivery_system);
+
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+ c->bandwidth_hz = 0; /* AUTO */
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+ c->hierarchy = HIERARCHY_AUTO;
+ c->symbol_rate = 0;
+ c->code_rate_HP = FEC_AUTO;
+ c->code_rate_LP = FEC_AUTO;
+ c->fec_inner = FEC_AUTO;
+ c->rolloff = ROLLOFF_AUTO;
+ c->voltage = SEC_VOLTAGE_OFF;
+ c->sectone = SEC_TONE_OFF;
+ c->pilot = PILOT_AUTO;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 0;
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+
+ c->stream_id = NO_STREAM_ID_FILTER;
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_TURBO:
+ c->modulation = QPSK; /* implied for DVB-S in legacy API */
+ c->rolloff = ROLLOFF_35;/* implied for DVB-S */
+ break;
+ case SYS_ATSC:
+ c->modulation = VSB_8;
+ break;
+ default:
+ c->modulation = QAM_AUTO;
+ break;
+ }
+
+ return 0;
+}
+
+#define _DTV_CMD(n, s, b) \
+[n] = { \
+ .name = #n, \
+ .cmd = n, \
+ .set = s,\
+ .buffer = b \
+}
+
+static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
+ _DTV_CMD(DTV_TUNE, 1, 0),
+ _DTV_CMD(DTV_CLEAR, 1, 0),
+
+ /* Set */
+ _DTV_CMD(DTV_FREQUENCY, 1, 0),
+ _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
+ _DTV_CMD(DTV_MODULATION, 1, 0),
+ _DTV_CMD(DTV_INVERSION, 1, 0),
+ _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
+ _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
+ _DTV_CMD(DTV_INNER_FEC, 1, 0),
+ _DTV_CMD(DTV_VOLTAGE, 1, 0),
+ _DTV_CMD(DTV_TONE, 1, 0),
+ _DTV_CMD(DTV_PILOT, 1, 0),
+ _DTV_CMD(DTV_ROLLOFF, 1, 0),
+ _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
+ _DTV_CMD(DTV_HIERARCHY, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
+ _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
+ _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
+ _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
+ _DTV_CMD(DTV_INTERLEAVING, 1, 0),
+
+ _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
+ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
+ _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
+
+ _DTV_CMD(DTV_STREAM_ID, 1, 0),
+ _DTV_CMD(DTV_DVBT2_PLP_ID_LEGACY, 1, 0),
+ _DTV_CMD(DTV_LNA, 1, 0),
+
+ /* Get */
+ _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
+ _DTV_CMD(DTV_API_VERSION, 0, 0),
+ _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
+ _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
+ _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
+ _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
+ _DTV_CMD(DTV_HIERARCHY, 0, 0),
+ _DTV_CMD(DTV_INTERLEAVING, 0, 0),
+
+ _DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
+
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
+
+ _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
+};
+
+static void dtv_property_dump(struct dvb_frontend *fe, struct dtv_property *tvp)
+{
+ int i;
+
+ if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
+ dev_warn(fe->dvb->device, "%s: tvp.cmd = 0x%08x undefined\n",
+ __func__, tvp->cmd);
+ return;
+ }
+
+ dev_dbg(fe->dvb->device, "%s: tvp.cmd = 0x%08x (%s)\n", __func__,
+ tvp->cmd, dtv_cmds[tvp->cmd].name);
+
+ if (dtv_cmds[tvp->cmd].buffer) {
+ dev_dbg(fe->dvb->device, "%s: tvp.u.buffer.len = 0x%02x\n",
+ __func__, tvp->u.buffer.len);
+
+ for(i = 0; i < tvp->u.buffer.len; i++)
+ dev_dbg(fe->dvb->device,
+ "%s: tvp.u.buffer.data[0x%02x] = 0x%02x\n",
+ __func__, i, tvp->u.buffer.data[i]);
+ } else {
+ dev_dbg(fe->dvb->device, "%s: tvp.u.data = 0x%08x\n", __func__,
+ tvp->u.data);
+ }
+}
+
+/* Synchronise the legacy tuning parameters into the cache, so that demodulator
+ * drivers can use a single set_frontend tuning function, regardless of whether
+ * it's being used for the legacy or new API, reducing code and complexity.
+ */
+static int dtv_property_cache_sync(struct dvb_frontend *fe,
+ struct dtv_frontend_properties *c,
+ const struct dvb_frontend_parameters *p)
+{
+ c->frequency = p->frequency;
+ c->inversion = p->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__);
+ c->symbol_rate = p->u.qpsk.symbol_rate;
+ c->fec_inner = p->u.qpsk.fec_inner;
+ break;
+ case DVBV3_QAM:
+ dev_dbg(fe->dvb->device, "%s: Preparing QAM req\n", __func__);
+ c->symbol_rate = p->u.qam.symbol_rate;
+ c->fec_inner = p->u.qam.fec_inner;
+ c->modulation = p->u.qam.modulation;
+ break;
+ case DVBV3_OFDM:
+ dev_dbg(fe->dvb->device, "%s: Preparing OFDM req\n", __func__);
+
+ switch (p->u.ofdm.bandwidth) {
+ case BANDWIDTH_10_MHZ:
+ c->bandwidth_hz = 10000000;
+ break;
+ case BANDWIDTH_8_MHZ:
+ c->bandwidth_hz = 8000000;
+ break;
+ case BANDWIDTH_7_MHZ:
+ c->bandwidth_hz = 7000000;
+ break;
+ case BANDWIDTH_6_MHZ:
+ c->bandwidth_hz = 6000000;
+ break;
+ case BANDWIDTH_5_MHZ:
+ c->bandwidth_hz = 5000000;
+ break;
+ case BANDWIDTH_1_712_MHZ:
+ c->bandwidth_hz = 1712000;
+ break;
+ case BANDWIDTH_AUTO:
+ c->bandwidth_hz = 0;
+ }
+
+ c->code_rate_HP = p->u.ofdm.code_rate_HP;
+ c->code_rate_LP = p->u.ofdm.code_rate_LP;
+ c->modulation = p->u.ofdm.constellation;
+ c->transmission_mode = p->u.ofdm.transmission_mode;
+ c->guard_interval = p->u.ofdm.guard_interval;
+ c->hierarchy = p->u.ofdm.hierarchy_information;
+ break;
+ case DVBV3_ATSC:
+ dev_dbg(fe->dvb->device, "%s: Preparing ATSC req\n", __func__);
+ c->modulation = p->u.vsb.modulation;
+ if (c->delivery_system == SYS_ATSCMH)
+ break;
+ if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
+ c->delivery_system = SYS_ATSC;
+ else
+ c->delivery_system = SYS_DVBC_ANNEX_B;
+ break;
+ case DVBV3_UNKNOWN:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Ensure the cached values are set correctly in the frontend
+ * legacy tuning structures, for the advanced tuning API.
+ */
+static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p)
+{
+ const struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ p->frequency = c->frequency;
+ p->inversion = c->inversion;
+
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_UNKNOWN:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ return -EINVAL;
+ case DVBV3_QPSK:
+ dev_dbg(fe->dvb->device, "%s: Preparing QPSK req\n", __func__);
+ p->u.qpsk.symbol_rate = c->symbol_rate;
+ p->u.qpsk.fec_inner = c->fec_inner;
+ break;
+ case DVBV3_QAM:
+ dev_dbg(fe->dvb->device, "%s: Preparing QAM req\n", __func__);
+ p->u.qam.symbol_rate = c->symbol_rate;
+ p->u.qam.fec_inner = c->fec_inner;
+ p->u.qam.modulation = c->modulation;
+ break;
+ case DVBV3_OFDM:
+ dev_dbg(fe->dvb->device, "%s: Preparing OFDM req\n", __func__);
+ switch (c->bandwidth_hz) {
+ case 10000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ;
+ break;
+ case 8000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
+ break;
+ case 7000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
+ break;
+ case 6000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
+ break;
+ case 5000000:
+ p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ;
+ break;
+ case 1712000:
+ p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ;
+ break;
+ case 0:
+ default:
+ p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
+ }
+ p->u.ofdm.code_rate_HP = c->code_rate_HP;
+ p->u.ofdm.code_rate_LP = c->code_rate_LP;
+ p->u.ofdm.constellation = c->modulation;
+ p->u.ofdm.transmission_mode = c->transmission_mode;
+ p->u.ofdm.guard_interval = c->guard_interval;
+ p->u.ofdm.hierarchy_information = c->hierarchy;
+ break;
+ case DVBV3_ATSC:
+ dev_dbg(fe->dvb->device, "%s: Preparing VSB req\n", __func__);
+ p->u.vsb.modulation = c->modulation;
+ break;
+ }
+ return 0;
+}
+
+/**
+ * dtv_get_frontend - calls a callback for retrieving DTV parameters
+ * @fe: struct dvb_frontend pointer
+ * @c: struct dtv_frontend_properties pointer (DVBv5 cache)
+ * @p_out struct dvb_frontend_parameters pointer (DVBv3 FE struct)
+ *
+ * This routine calls either the DVBv3 or DVBv5 get_frontend call.
+ * If c is not null, it will update the DVBv5 cache struct pointed by it.
+ * If p_out is not null, it will update the DVBv3 params pointed by it.
+ */
+static int dtv_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *p_out)
+{
+ int r;
+
+ if (fe->ops.get_frontend) {
+ r = fe->ops.get_frontend(fe);
+ if (unlikely(r < 0))
+ return r;
+ if (p_out)
+ dtv_property_legacy_params_sync(fe, p_out);
+ return 0;
+ }
+
+ /* As everything is in cache, get_frontend fops are always supported */
+ return 0;
+}
+
+static int dvb_frontend_ioctl_legacy(struct file *file,
+ unsigned int cmd, void *parg);
+static int dvb_frontend_ioctl_properties(struct file *file,
+ unsigned int cmd, void *parg);
+
+static int dtv_property_process_get(struct dvb_frontend *fe,
+ const struct dtv_frontend_properties *c,
+ struct dtv_property *tvp,
+ struct file *file)
+{
+ int r, ncaps;
+
+ switch(tvp->cmd) {
+ case DTV_ENUM_DELSYS:
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+ tvp->u.buffer.len = ncaps;
+ break;
+ case DTV_FREQUENCY:
+ tvp->u.data = c->frequency;
+ break;
+ case DTV_MODULATION:
+ tvp->u.data = c->modulation;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ tvp->u.data = c->bandwidth_hz;
+ break;
+ case DTV_INVERSION:
+ tvp->u.data = c->inversion;
+ break;
+ case DTV_SYMBOL_RATE:
+ tvp->u.data = c->symbol_rate;
+ break;
+ case DTV_INNER_FEC:
+ tvp->u.data = c->fec_inner;
+ break;
+ case DTV_PILOT:
+ tvp->u.data = c->pilot;
+ break;
+ case DTV_ROLLOFF:
+ tvp->u.data = c->rolloff;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ tvp->u.data = c->delivery_system;
+ break;
+ case DTV_VOLTAGE:
+ tvp->u.data = c->voltage;
+ break;
+ case DTV_TONE:
+ tvp->u.data = c->sectone;
+ break;
+ case DTV_API_VERSION:
+ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
+ break;
+ case DTV_CODE_RATE_HP:
+ tvp->u.data = c->code_rate_HP;
+ break;
+ case DTV_CODE_RATE_LP:
+ tvp->u.data = c->code_rate_LP;
+ break;
+ case DTV_GUARD_INTERVAL:
+ tvp->u.data = c->guard_interval;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ tvp->u.data = c->transmission_mode;
+ break;
+ case DTV_HIERARCHY:
+ tvp->u.data = c->hierarchy;
+ break;
+ case DTV_INTERLEAVING:
+ tvp->u.data = c->interleaving;
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ tvp->u.data = c->isdbt_partial_reception;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ tvp->u.data = c->isdbt_sb_mode;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ tvp->u.data = c->isdbt_sb_subchannel;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ tvp->u.data = c->isdbt_sb_segment_idx;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ tvp->u.data = c->isdbt_sb_segment_count;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ tvp->u.data = c->isdbt_layer_enabled;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ tvp->u.data = c->layer[0].fec;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ tvp->u.data = c->layer[0].modulation;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ tvp->u.data = c->layer[0].segment_count;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[0].interleaving;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ tvp->u.data = c->layer[1].fec;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ tvp->u.data = c->layer[1].modulation;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ tvp->u.data = c->layer[1].segment_count;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[1].interleaving;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ tvp->u.data = c->layer[2].fec;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ tvp->u.data = c->layer[2].modulation;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ tvp->u.data = c->layer[2].segment_count;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ tvp->u.data = c->layer[2].interleaving;
+ break;
+
+ /* Multistream support */
+ case DTV_STREAM_ID:
+ case DTV_DVBT2_PLP_ID_LEGACY:
+ tvp->u.data = c->stream_id;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_FIC_VER:
+ tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
+ break;
+ case DTV_ATSCMH_PARADE_ID:
+ tvp->u.data = fe->dtv_property_cache.atscmh_parade_id;
+ break;
+ case DTV_ATSCMH_NOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_nog;
+ break;
+ case DTV_ATSCMH_TNOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_tnog;
+ break;
+ case DTV_ATSCMH_SGN:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sgn;
+ break;
+ case DTV_ATSCMH_PRC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_prc;
+ break;
+ case DTV_ATSCMH_RS_FRAME_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_PRI:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_SEC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec;
+ break;
+ case DTV_ATSCMH_SCCC_BLOCK_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_A:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_B:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_C:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_D:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Allow the frontend to override outgoing properties */
+ if (fe->ops.get_property) {
+ r = fe->ops.get_property(fe, tvp);
+ if (r < 0)
+ return r;
+ }
+
+ dtv_property_dump(fe, tvp);
+
+ return 0;
+}
+
+static int dtv_set_frontend(struct dvb_frontend *fe);
+
+static bool is_dvbv3_delsys(u32 delsys)
+{
+ bool status;
+
+ status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) ||
+ (delsys == SYS_DVBS) || (delsys == SYS_ATSC);
+
+ return status;
+}
+
+static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
+{
+ int ncaps, i;
+ u32 delsys = SYS_UNDEFINED;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ enum dvbv3_emulation_type type;
+
+ /*
+ * It was reported that some old DVBv5 applications were
+ * filling delivery_system with SYS_UNDEFINED. If this happens,
+ * assume that the application wants to use the first supported
+ * delivery system.
+ */
+ if (c->delivery_system == SYS_UNDEFINED)
+ c->delivery_system = fe->ops.delsys[0];
+
+ if (desired_system == SYS_UNDEFINED) {
+ /*
+ * A DVBv3 call doesn't know what's the desired system.
+ * Also, DVBv3 applications don't know that ops.info->type
+ * could be changed, and they simply dies when it doesn't
+ * match.
+ * So, don't change the current delivery system, as it
+ * may be trying to do the wrong thing, like setting an
+ * ISDB-T frontend as DVB-T. Instead, find the closest
+ * DVBv3 system that matches the delivery system.
+ */
+ if (is_dvbv3_delsys(c->delivery_system)) {
+ dev_dbg(fe->dvb->device,
+ "%s: Using delivery system to %d\n",
+ __func__, c->delivery_system);
+ return 0;
+ }
+ type = dvbv3_type(c->delivery_system);
+ switch (type) {
+ case DVBV3_QPSK:
+ desired_system = SYS_DVBS;
+ break;
+ case DVBV3_QAM:
+ desired_system = SYS_DVBC_ANNEX_A;
+ break;
+ case DVBV3_ATSC:
+ desired_system = SYS_ATSC;
+ break;
+ case DVBV3_OFDM:
+ desired_system = SYS_DVBT;
+ break;
+ default:
+ dev_dbg(fe->dvb->device, "%s: This frontend doesn't support DVBv3 calls\n",
+ __func__);
+ return -EINVAL;
+ }
+ /*
+ * Get a delivery system that is compatible with DVBv3
+ * NOTE: in order for this to work with softwares like Kaffeine that
+ * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
+ * DVB-S, drivers that support both should put the SYS_DVBS entry
+ * before the SYS_DVBS2, otherwise it won't switch back to DVB-S.
+ * The real fix is that userspace applications should not use DVBv3
+ * and not trust on calling FE_SET_FRONTEND to switch the delivery
+ * system.
+ */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if (fe->ops.delsys[ncaps] == desired_system) {
+ delsys = desired_system;
+ break;
+ }
+ ncaps++;
+ }
+ if (delsys == SYS_UNDEFINED) {
+ dev_dbg(fe->dvb->device, "%s: Couldn't find a delivery system that matches %d\n",
+ __func__, desired_system);
+ }
+ } else {
+ /*
+ * This is a DVBv5 call. So, it likely knows the supported
+ * delivery systems.
+ */
+
+ /* Check if the desired delivery system is supported */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if (fe->ops.delsys[ncaps] == desired_system) {
+ c->delivery_system = desired_system;
+ dev_dbg(fe->dvb->device,
+ "%s: Changing delivery system to %d\n",
+ __func__, desired_system);
+ return 0;
+ }
+ ncaps++;
+ }
+ type = dvbv3_type(desired_system);
+
+ /*
+ * The delivery system is not supported. See if it can be
+ * emulated.
+ * The emulation only works if the desired system is one of the
+ * DVBv3 delivery systems
+ */
+ if (!is_dvbv3_delsys(desired_system)) {
+ dev_dbg(fe->dvb->device,
+ "%s: can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Get the last non-DVBv3 delivery system that has the same type
+ * of the desired system
+ */
+ ncaps = 0;
+ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
+ if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) &&
+ !is_dvbv3_delsys(fe->ops.delsys[ncaps]))
+ delsys = fe->ops.delsys[ncaps];
+ ncaps++;
+ }
+ /* There's nothing compatible with the desired delivery system */
+ if (delsys == SYS_UNDEFINED) {
+ dev_dbg(fe->dvb->device,
+ "%s: Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ c->delivery_system = delsys;
+
+ /*
+ * The DVBv3 or DVBv5 call is requesting a different system. So,
+ * emulation is needed.
+ *
+ * Emulate newer delivery systems like ISDBT, DVBT and DTMB
+ * for older DVBv5 applications. The emulation will try to use
+ * the auto mode for most things, and will assume that the desired
+ * delivery system is the last one at the ops.delsys[] array
+ */
+ dev_dbg(fe->dvb->device,
+ "%s: Using delivery system %d emulated as if it were a %d\n",
+ __func__, delsys, desired_system);
+
+ /*
+ * For now, handles ISDB-T calls. More code may be needed here for the
+ * other emulated stuff
+ */
+ if (type == DVBV3_OFDM) {
+ if (c->delivery_system == SYS_ISDBT) {
+ dev_dbg(fe->dvb->device,
+ "%s: Using defaults for SYS_ISDBT\n",
+ __func__);
+
+ if (!c->bandwidth_hz)
+ c->bandwidth_hz = 6000000;
+
+ c->isdbt_partial_reception = 0;
+ c->isdbt_sb_mode = 0;
+ c->isdbt_sb_subchannel = 0;
+ c->isdbt_sb_segment_idx = 0;
+ c->isdbt_sb_segment_count = 0;
+ c->isdbt_layer_enabled = 0;
+ for (i = 0; i < 3; i++) {
+ c->layer[i].fec = FEC_AUTO;
+ c->layer[i].modulation = QAM_AUTO;
+ c->layer[i].interleaving = 0;
+ c->layer[i].segment_count = 0;
+ }
+ }
+ }
+ dev_dbg(fe->dvb->device, "%s: change delivery system on cache to %d\n",
+ __func__, c->delivery_system);
+
+ return 0;
+}
+
+static int dtv_property_process_set(struct dvb_frontend *fe,
+ struct dtv_property *tvp,
+ struct file *file)
+{
+ int r = 0;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+
+ /* Allow the frontend to validate incoming properties */
+ if (fe->ops.set_property) {
+ r = fe->ops.set_property(fe, tvp);
+ if (r < 0)
+ return r;
+ }
+
+ switch(tvp->cmd) {
+ case DTV_CLEAR:
+ /*
+ * Reset a cache of data specific to the frontend here. This does
+ * not effect hardware.
+ */
+ dvb_frontend_clear_cache(fe);
+ break;
+ case DTV_TUNE:
+ /* interpret the cache of data, build either a traditional frontend
+ * tunerequest so we can pass validation in the FE_SET_FRONTEND
+ * ioctl.
+ */
+ c->state = tvp->cmd;
+ dev_dbg(fe->dvb->device, "%s: Finalised property cache\n",
+ __func__);
+
+ r = dtv_set_frontend(fe);
+ break;
+ case DTV_FREQUENCY:
+ c->frequency = tvp->u.data;
+ break;
+ case DTV_MODULATION:
+ c->modulation = tvp->u.data;
+ break;
+ case DTV_BANDWIDTH_HZ:
+ c->bandwidth_hz = tvp->u.data;
+ break;
+ case DTV_INVERSION:
+ c->inversion = tvp->u.data;
+ break;
+ case DTV_SYMBOL_RATE:
+ c->symbol_rate = tvp->u.data;
+ break;
+ case DTV_INNER_FEC:
+ c->fec_inner = tvp->u.data;
+ break;
+ case DTV_PILOT:
+ c->pilot = tvp->u.data;
+ break;
+ case DTV_ROLLOFF:
+ c->rolloff = tvp->u.data;
+ break;
+ case DTV_DELIVERY_SYSTEM:
+ r = set_delivery_system(fe, tvp->u.data);
+ break;
+ case DTV_VOLTAGE:
+ c->voltage = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(file, FE_SET_VOLTAGE,
+ (void *)c->voltage);
+ break;
+ case DTV_TONE:
+ c->sectone = tvp->u.data;
+ r = dvb_frontend_ioctl_legacy(file, FE_SET_TONE,
+ (void *)c->sectone);
+ break;
+ case DTV_CODE_RATE_HP:
+ c->code_rate_HP = tvp->u.data;
+ break;
+ case DTV_CODE_RATE_LP:
+ c->code_rate_LP = tvp->u.data;
+ break;
+ case DTV_GUARD_INTERVAL:
+ c->guard_interval = tvp->u.data;
+ break;
+ case DTV_TRANSMISSION_MODE:
+ c->transmission_mode = tvp->u.data;
+ break;
+ case DTV_HIERARCHY:
+ c->hierarchy = tvp->u.data;
+ break;
+ case DTV_INTERLEAVING:
+ c->interleaving = tvp->u.data;
+ break;
+ case DTV_LNA:
+ if (fe->ops.set_lna)
+ r = fe->ops.set_lna(fe, tvp->u.data);
+ break;
+
+ /* ISDB-T Support here */
+ case DTV_ISDBT_PARTIAL_RECEPTION:
+ c->isdbt_partial_reception = tvp->u.data;
+ break;
+ case DTV_ISDBT_SOUND_BROADCASTING:
+ c->isdbt_sb_mode = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SUBCHANNEL_ID:
+ c->isdbt_sb_subchannel = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_IDX:
+ c->isdbt_sb_segment_idx = tvp->u.data;
+ break;
+ case DTV_ISDBT_SB_SEGMENT_COUNT:
+ c->isdbt_sb_segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYER_ENABLED:
+ c->isdbt_layer_enabled = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_FEC:
+ c->layer[0].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_MODULATION:
+ c->layer[0].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
+ c->layer[0].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
+ c->layer[0].interleaving = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_FEC:
+ c->layer[1].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_MODULATION:
+ c->layer[1].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
+ c->layer[1].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
+ c->layer[1].interleaving = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_FEC:
+ c->layer[2].fec = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_MODULATION:
+ c->layer[2].modulation = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
+ c->layer[2].segment_count = tvp->u.data;
+ break;
+ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
+ c->layer[2].interleaving = tvp->u.data;
+ break;
+
+ /* Multistream support */
+ case DTV_STREAM_ID:
+ case DTV_DVBT2_PLP_ID_LEGACY:
+ c->stream_id = tvp->u.data;
+ break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_PARADE_ID:
+ fe->dtv_property_cache.atscmh_parade_id = tvp->u.data;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+static int dvb_frontend_ioctl(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int err = -ENOTTY;
+
+ dev_dbg(fe->dvb->device, "%s: (%d)\n", __func__, _IOC_NR(cmd));
+ if (fepriv->exit != DVB_FE_NO_EXIT)
+ return -ENODEV;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
+ (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT ||
+ cmd == FE_DISEQC_RECV_SLAVE_REPLY))
+ return -EPERM;
+
+ if (down_interruptible (&fepriv->sem))
+ return -ERESTARTSYS;
+
+ if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
+ err = dvb_frontend_ioctl_properties(file, cmd, parg);
+ else {
+ c->state = DTV_UNDEFINED;
+ err = dvb_frontend_ioctl_legacy(file, cmd, parg);
+ }
+
+ up(&fepriv->sem);
+ return err;
+}
+
+static int dvb_frontend_ioctl_properties(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int err = 0;
+
+ struct dtv_properties *tvps = NULL;
+ struct dtv_property *tvp = NULL;
+ int i;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if(cmd == FE_SET_PROPERTY) {
+ tvps = (struct dtv_properties __user *)parg;
+
+ dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num);
+ dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_set(fe, tvp + i, file);
+ if (err < 0)
+ goto out;
+ (tvp + i)->result = err;
+ }
+
+ if (c->state == DTV_TUNE)
+ dev_dbg(fe->dvb->device, "%s: Property cache is full, tuning\n", __func__);
+
+ } else
+ if(cmd == FE_GET_PROPERTY) {
+ tvps = (struct dtv_properties __user *)parg;
+
+ dev_dbg(fe->dvb->device, "%s: properties.num = %d\n", __func__, tvps->num);
+ dev_dbg(fe->dvb->device, "%s: properties.props = %p\n", __func__, tvps->props);
+
+ /* Put an arbitrary limit on the number of messages that can
+ * be sent at once */
+ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
+ return -EINVAL;
+
+ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
+ if (!tvp) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ /*
+ * Fills the cache out struct with the cache contents, plus
+ * the data retrieved from get_frontend, if the frontend
+ * is not idle. Otherwise, returns the cached content
+ */
+ if (fepriv->state != FESTATE_IDLE) {
+ err = dtv_get_frontend(fe, NULL);
+ if (err < 0)
+ goto out;
+ }
+ for (i = 0; i < tvps->num; i++) {
+ err = dtv_property_process_get(fe, c, tvp + i, file);
+ if (err < 0)
+ goto out;
+ (tvp + i)->result = err;
+ }
+
+ if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ } else
+ err = -ENOTTY;
+
+out:
+ kfree(tvp);
+ return err;
+}
+
+static int dtv_set_frontend(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_frontend_tune_settings fetunesettings;
+ u32 rolloff = 0;
+
+ if (dvb_frontend_check_parameters(fe) < 0)
+ return -EINVAL;
+
+ /*
+ * Initialize output parameters to match the values given by
+ * the user. FE_SET_FRONTEND triggers an initial frontend event
+ * with status = 0, which copies output parameters to userspace.
+ */
+ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
+
+ /*
+ * Be sure that the bandwidth will be filled for all
+ * non-satellite systems, as tuners need to know what
+ * low pass/Nyquist half filter should be applied, in
+ * order to avoid inter-channel noise.
+ *
+ * ISDB-T and DVB-T/T2 already sets bandwidth.
+ * ATSC and DVB-C don't set, so, the core should fill it.
+ *
+ * On DVB-C Annex A and C, the bandwidth is a function of
+ * the roll-off and symbol rate. Annex B defines different
+ * roll-off factors depending on the modulation. Fortunately,
+ * Annex B is only used with 6MHz, so there's no need to
+ * calculate it.
+ *
+ * While not officially supported, a side effect of handling it at
+ * the cache level is that a program could retrieve the bandwidth
+ * via DTV_BANDWIDTH_HZ, which may be useful for test programs.
+ */
+ switch (c->delivery_system) {
+ case SYS_ATSC:
+ case SYS_DVBC_ANNEX_B:
+ c->bandwidth_hz = 6000000;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ rolloff = 115;
+ break;
+ case SYS_DVBC_ANNEX_C:
+ rolloff = 113;
+ break;
+ default:
+ break;
+ }
+ if (rolloff)
+ c->bandwidth_hz = (c->symbol_rate * rolloff) / 100;
+
+ /* force auto frequency inversion if requested */
+ if (dvb_force_auto_inversion)
+ c->inversion = INVERSION_AUTO;
+
+ /*
+ * without hierarchical coding code_rate_LP is irrelevant,
+ * so we tolerate the otherwise invalid FEC_NONE setting
+ */
+ if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE)
+ c->code_rate_LP = FEC_AUTO;
+
+ /* get frontend-specific tuning settings */
+ memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
+ if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) {
+ fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
+ fepriv->max_drift = fetunesettings.max_drift;
+ fepriv->step_size = fetunesettings.step_size;
+ } else {
+ /* default values */
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ case SYS_DVBS2:
+ case SYS_ISDBS:
+ case SYS_TURBO:
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = c->symbol_rate / 16000;
+ fepriv->max_drift = c->symbol_rate / 2000;
+ break;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ case SYS_ISDBT:
+ case SYS_DTMB:
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = fe->ops.info.frequency_stepsize * 2;
+ fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+ break;
+ default:
+ /*
+ * FIXME: This sounds wrong! if freqency_stepsize is
+ * defined by the frontend, why not use it???
+ */
+ fepriv->min_delay = HZ / 20;
+ fepriv->step_size = 0; /* no zigzag */
+ fepriv->max_drift = 0;
+ break;
+ }
+ }
+ if (dvb_override_tune_delay > 0)
+ fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
+
+ fepriv->state = FESTATE_RETUNE;
+
+ /* Request the search algorithm to search */
+ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
+
+ dvb_frontend_clear_events(fe);
+ dvb_frontend_add_event(fe, 0);
+ dvb_frontend_wakeup(fe);
+ fepriv->status = 0;
+
+ return 0;
+}
+
+
+static int dvb_frontend_ioctl_legacy(struct file *file,
+ unsigned int cmd, void *parg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int err = -ENOTTY;
+
+ switch (cmd) {
+ case FE_GET_INFO: {
+ struct dvb_frontend_info* info = parg;
+
+ memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info));
+ dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max);
+
+ /*
+ * Associate the 4 delivery systems supported by DVBv3
+ * API with their DVBv5 counterpart. For the other standards,
+ * use the closest type, assuming that it would hopefully
+ * work with a DVBv3 application.
+ * It should be noticed that, on multi-frontend devices with
+ * different types (terrestrial and cable, for example),
+ * a pure DVBv3 application won't be able to use all delivery
+ * systems. Yet, changing the DVBv5 cache to the other delivery
+ * system should be enough for making it work.
+ */
+ switch (dvbv3_type(c->delivery_system)) {
+ case DVBV3_QPSK:
+ info->type = FE_QPSK;
+ break;
+ case DVBV3_ATSC:
+ info->type = FE_ATSC;
+ break;
+ case DVBV3_QAM:
+ info->type = FE_QAM;
+ break;
+ case DVBV3_OFDM:
+ info->type = FE_OFDM;
+ break;
+ default:
+ dev_err(fe->dvb->device,
+ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
+ __func__, c->delivery_system);
+ fe->ops.info.type = FE_OFDM;
+ }
+ dev_dbg(fe->dvb->device, "%s: current delivery system on cache: %d, V3 type: %d\n",
+ __func__, c->delivery_system, fe->ops.info.type);
+
+ /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
+ * do it, it is done for it. */
+ info->caps |= FE_CAN_INVERSION_AUTO;
+ err = 0;
+ break;
+ }
+
+ case FE_READ_STATUS: {
+ fe_status_t* status = parg;
+
+ /* if retune was requested but hasn't occurred yet, prevent
+ * that user get signal state from previous tuning */
+ if (fepriv->state == FESTATE_RETUNE ||
+ fepriv->state == FESTATE_ERROR) {
+ err=0;
+ *status = 0;
+ break;
+ }
+
+ if (fe->ops.read_status)
+ err = fe->ops.read_status(fe, status);
+ break;
+ }
+
+ case FE_READ_BER:
+ if (fe->ops.read_ber) {
+ if (fepriv->thread)
+ err = fe->ops.read_ber(fe, (__u32 *) parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_SIGNAL_STRENGTH:
+ if (fe->ops.read_signal_strength) {
+ if (fepriv->thread)
+ err = fe->ops.read_signal_strength(fe, (__u16 *) parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_SNR:
+ if (fe->ops.read_snr) {
+ if (fepriv->thread)
+ err = fe->ops.read_snr(fe, (__u16 *) parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ if (fe->ops.read_ucblocks) {
+ if (fepriv->thread)
+ err = fe->ops.read_ucblocks(fe, (__u32 *) parg);
+ else
+ err = -EAGAIN;
+ }
+ break;
+
+ case FE_DISEQC_RESET_OVERLOAD:
+ if (fe->ops.diseqc_reset_overload) {
+ err = fe->ops.diseqc_reset_overload(fe);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_SEND_MASTER_CMD:
+ if (fe->ops.diseqc_send_master_cmd) {
+ err = fe->ops.diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_SEND_BURST:
+ if (fe->ops.diseqc_send_burst) {
+ err = fe->ops.diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_SET_TONE:
+ if (fe->ops.set_tone) {
+ err = fe->ops.set_tone(fe, (fe_sec_tone_mode_t) parg);
+ fepriv->tone = (fe_sec_tone_mode_t) parg;
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_SET_VOLTAGE:
+ if (fe->ops.set_voltage) {
+ err = fe->ops.set_voltage(fe, (fe_sec_voltage_t) parg);
+ fepriv->voltage = (fe_sec_voltage_t) parg;
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISHNETWORK_SEND_LEGACY_CMD:
+ if (fe->ops.dishnetwork_send_legacy_command) {
+ err = fe->ops.dishnetwork_send_legacy_command(fe, (unsigned long) parg);
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ } else if (fe->ops.set_voltage) {
+ /*
+ * NOTE: This is a fallback condition. Some frontends
+ * (stv0299 for instance) take longer than 8msec to
+ * respond to a set_voltage command. Those switches
+ * need custom routines to switch properly. For all
+ * other frontends, the following should work ok.
+ * Dish network legacy switches (as used by Dish500)
+ * are controlled by sending 9-bit command words
+ * spaced 8msec apart.
+ * the actual command word is switch/port dependent
+ * so it is up to the userspace application to send
+ * the right command.
+ * The command must always start with a '0' after
+ * initialization, so parg is 8 bits and does not
+ * include the initialization or start bit
+ */
+ unsigned long swcmd = ((unsigned long) parg) << 1;
+ struct timeval nexttime;
+ struct timeval tv[10];
+ int i;
+ u8 last = 1;
+ if (dvb_frontend_debug)
+ printk("%s switch command: 0x%04lx\n", __func__, swcmd);
+ do_gettimeofday(&nexttime);
+ if (dvb_frontend_debug)
+ memcpy(&tv[0], &nexttime, sizeof(struct timeval));
+ /* before sending a command, initialize by sending
+ * a 32ms 18V to the switch
+ */
+ fe->ops.set_voltage(fe, SEC_VOLTAGE_18);
+ dvb_frontend_sleep_until(&nexttime, 32000);
+
+ for (i = 0; i < 9; i++) {
+ if (dvb_frontend_debug)
+ do_gettimeofday(&tv[i + 1]);
+ if ((swcmd & 0x01) != last) {
+ /* set voltage to (last ? 13V : 18V) */
+ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
+ last = (last) ? 0 : 1;
+ }
+ swcmd = swcmd >> 1;
+ if (i != 8)
+ dvb_frontend_sleep_until(&nexttime, 8000);
+ }
+ if (dvb_frontend_debug) {
+ printk("%s(%d): switch delay (should be 32k followed by all 8k\n",
+ __func__, fe->dvb->num);
+ for (i = 1; i < 10; i++)
+ printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i]));
+ }
+ err = 0;
+ fepriv->state = FESTATE_DISEQC;
+ fepriv->status = 0;
+ }
+ break;
+
+ case FE_DISEQC_RECV_SLAVE_REPLY:
+ if (fe->ops.diseqc_recv_slave_reply)
+ err = fe->ops.diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg);
+ break;
+
+ case FE_ENABLE_HIGH_LNB_VOLTAGE:
+ if (fe->ops.enable_high_lnb_voltage)
+ err = fe->ops.enable_high_lnb_voltage(fe, (long) parg);
+ break;
+
+ case FE_SET_FRONTEND:
+ err = set_delivery_system(fe, SYS_UNDEFINED);
+ if (err)
+ break;
+
+ err = dtv_property_cache_sync(fe, c, parg);
+ if (err)
+ break;
+ err = dtv_set_frontend(fe);
+ break;
+ case FE_GET_EVENT:
+ err = dvb_frontend_get_event (fe, parg, file->f_flags);
+ break;
+
+ case FE_GET_FRONTEND:
+ err = dtv_get_frontend(fe, parg);
+ break;
+
+ case FE_SET_FRONTEND_TUNE_MODE:
+ fepriv->tune_mode_flags = (unsigned long) parg;
+ err = 0;
+ break;
+ };
+
+ return err;
+}
+
+
+static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+
+ dev_dbg_ratelimited(fe->dvb->device, "%s:\n", __func__);
+
+ poll_wait (file, &fepriv->events.wait_queue, wait);
+
+ if (fepriv->events.eventw != fepriv->events.eventr)
+ return (POLLIN | POLLRDNORM | POLLPRI);
+
+ return 0;
+}
+
+static int dvb_frontend_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ struct dvb_adapter *adapter = fe->dvb;
+ int ret;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+ if (fepriv->exit == DVB_FE_DEVICE_REMOVED)
+ return -ENODEV;
+
+ if (adapter->mfe_shared) {
+ mutex_lock (&adapter->mfe_lock);
+
+ if (adapter->mfe_dvbdev == NULL)
+ adapter->mfe_dvbdev = dvbdev;
+
+ else if (adapter->mfe_dvbdev != dvbdev) {
+ struct dvb_device
+ *mfedev = adapter->mfe_dvbdev;
+ struct dvb_frontend
+ *mfe = mfedev->priv;
+ struct dvb_frontend_private
+ *mfepriv = mfe->frontend_priv;
+ int mferetry = (dvb_mfe_wait_time << 1);
+
+ mutex_unlock (&adapter->mfe_lock);
+ while (mferetry-- && (mfedev->users != -1 ||
+ mfepriv->thread != NULL)) {
+ if(msleep_interruptible(500)) {
+ if(signal_pending(current))
+ return -EINTR;
+ }
+ }
+
+ mutex_lock (&adapter->mfe_lock);
+ if(adapter->mfe_dvbdev != dvbdev) {
+ mfedev = adapter->mfe_dvbdev;
+ mfe = mfedev->priv;
+ mfepriv = mfe->frontend_priv;
+ if (mfedev->users != -1 ||
+ mfepriv->thread != NULL) {
+ mutex_unlock (&adapter->mfe_lock);
+ return -EBUSY;
+ }
+ adapter->mfe_dvbdev = dvbdev;
+ }
+ }
+ }
+
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
+ if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
+ goto err0;
+
+ /* If we took control of the bus, we need to force
+ reinitialization. This is because many ts_bus_ctrl()
+ functions strobe the RESET pin on the demod, and if the
+ frontend thread already exists then the dvb_init() routine
+ won't get called (which is what usually does initial
+ register configuration). */
+ fepriv->reinitialise = 1;
+ }
+
+ if ((ret = dvb_generic_open (inode, file)) < 0)
+ goto err1;
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ /* normal tune mode when opened R/W */
+ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT;
+ fepriv->tone = -1;
+ fepriv->voltage = -1;
+
+ ret = dvb_frontend_start (fe);
+ if (ret)
+ goto err2;
+
+ /* empty event queue */
+ fepriv->events.eventr = fepriv->events.eventw = 0;
+ }
+
+ if (adapter->mfe_shared)
+ mutex_unlock (&adapter->mfe_lock);
+ return ret;
+
+err2:
+ dvb_generic_release(inode, file);
+err1:
+ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+err0:
+ if (adapter->mfe_shared)
+ mutex_unlock (&adapter->mfe_lock);
+ return ret;
+}
+
+static int dvb_frontend_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct dvb_frontend *fe = dvbdev->priv;
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int ret;
+
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
+ fepriv->release_jiffies = jiffies;
+ mb();
+ }
+
+ ret = dvb_generic_release (inode, file);
+
+ if (dvbdev->users == -1) {
+ wake_up(&fepriv->wait_queue);
+ if (fepriv->exit != DVB_FE_NO_EXIT) {
+ fops_put(file->f_op);
+ file->f_op = NULL;
+ wake_up(&dvbdev->wait_queue);
+ }
+ if (fe->ops.ts_bus_ctrl)
+ fe->ops.ts_bus_ctrl(fe, 0);
+ }
+
+ return ret;
+}
+
+static const struct file_operations dvb_frontend_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dvb_generic_ioctl,
+ .poll = dvb_frontend_poll,
+ .open = dvb_frontend_open,
+ .release = dvb_frontend_release,
+ .llseek = noop_llseek,
+};
+
+int dvb_frontend_suspend(struct dvb_frontend *fe)
+{
+ int ret = 0;
+
+ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
+ fe->id);
+
+ if (fe->ops.tuner_ops.sleep)
+ ret = fe->ops.tuner_ops.sleep(fe);
+
+ if (fe->ops.sleep)
+ ret = fe->ops.sleep(fe);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_frontend_suspend);
+
+int dvb_frontend_resume(struct dvb_frontend *fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ int ret = 0;
+
+ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num,
+ fe->id);
+
+ if (fe->ops.init)
+ ret = fe->ops.init(fe);
+
+ if (fe->ops.tuner_ops.init)
+ ret = fe->ops.tuner_ops.init(fe);
+
+ fepriv->state = FESTATE_RETUNE;
+ dvb_frontend_wakeup(fe);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_frontend_resume);
+
+int dvb_register_frontend(struct dvb_adapter* dvb,
+ struct dvb_frontend* fe)
+{
+ struct dvb_frontend_private *fepriv;
+ static const struct dvb_device dvbdev_template = {
+ .users = ~0,
+ .writers = 1,
+ .readers = (~0)-1,
+ .fops = &dvb_frontend_fops,
+ .kernel_ioctl = dvb_frontend_ioctl
+ };
+
+ dev_dbg(dvb->device, "%s:\n", __func__);
+
+ if (mutex_lock_interruptible(&frontend_mutex))
+ return -ERESTARTSYS;
+
+ fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
+ if (fe->frontend_priv == NULL) {
+ mutex_unlock(&frontend_mutex);
+ return -ENOMEM;
+ }
+ fepriv = fe->frontend_priv;
+
+ sema_init(&fepriv->sem, 1);
+ init_waitqueue_head (&fepriv->wait_queue);
+ init_waitqueue_head (&fepriv->events.wait_queue);
+ mutex_init(&fepriv->events.mtx);
+ fe->dvb = dvb;
+ fepriv->inversion = INVERSION_OFF;
+
+ dev_info(fe->dvb->device,
+ "DVB: registering adapter %i frontend %i (%s)...\n",
+ fe->dvb->num, fe->id, fe->ops.info.name);
+
+ dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
+ fe, DVB_DEVICE_FRONTEND);
+
+ /*
+ * Initialize the cache to the proper values according with the
+ * first supported delivery system (ops->delsys[0])
+ */
+
+ fe->dtv_property_cache.delivery_system = fe->ops.delsys[0];
+ dvb_frontend_clear_cache(fe);
+
+ mutex_unlock(&frontend_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_frontend);
+
+int dvb_unregister_frontend(struct dvb_frontend* fe)
+{
+ struct dvb_frontend_private *fepriv = fe->frontend_priv;
+ dev_dbg(fe->dvb->device, "%s:\n", __func__);
+
+ mutex_lock(&frontend_mutex);
+ dvb_frontend_stop (fe);
+ mutex_unlock(&frontend_mutex);
+
+ if (fepriv->dvbdev->users < -1)
+ wait_event(fepriv->dvbdev->wait_queue,
+ fepriv->dvbdev->users==-1);
+
+ mutex_lock(&frontend_mutex);
+ dvb_unregister_device (fepriv->dvbdev);
+
+ /* fe is invalid now */
+ kfree(fepriv);
+ mutex_unlock(&frontend_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_frontend);
+
+#ifdef CONFIG_MEDIA_ATTACH
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ void *ptr;
+
+ if (fe->ops.release_sec) {
+ fe->ops.release_sec(fe);
+ symbol_put_addr(fe->ops.release_sec);
+ }
+ if (fe->ops.tuner_ops.release) {
+ fe->ops.tuner_ops.release(fe);
+ symbol_put_addr(fe->ops.tuner_ops.release);
+ }
+ if (fe->ops.analog_ops.release) {
+ fe->ops.analog_ops.release(fe);
+ symbol_put_addr(fe->ops.analog_ops.release);
+ }
+ ptr = (void*)fe->ops.release;
+ if (ptr) {
+ fe->ops.release(fe);
+ symbol_put_addr(ptr);
+ }
+}
+#else
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ if (fe->ops.release_sec)
+ fe->ops.release_sec(fe);
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+ if (fe->ops.analog_ops.release)
+ fe->ops.analog_ops.release(fe);
+ if (fe->ops.release)
+ fe->ops.release(fe);
+}
+#endif
+EXPORT_SYMBOL(dvb_frontend_detach);
diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h
new file mode 100644
index 000000000000..44a445cee74f
--- /dev/null
+++ b/drivers/media/dvb-core/dvb_frontend.h
@@ -0,0 +1,425 @@
+/*
+ * dvb_frontend.h
+ *
+ * Copyright (C) 2001 convergence integrated media GmbH
+ * Copyright (C) 2004 convergence GmbH
+ *
+ * Written by Ralph Metzler
+ * Overhauled by Holger Waechtler
+ * Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DVB_FRONTEND_H_
+#define _DVB_FRONTEND_H_
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/dvb/frontend.h>
+
+#include "dvbdev.h"
+
+/*
+ * Maximum number of Delivery systems per frontend. It
+ * should be smaller or equal to 32
+ */
+#define MAX_DELSYS 8
+
+struct dvb_frontend_tune_settings {
+ int min_delay_ms;
+ int step_size;
+ int max_drift;
+};
+
+struct dvb_frontend;
+
+struct dvb_tuner_info {
+ char name[128];
+
+ u32 frequency_min;
+ u32 frequency_max;
+ u32 frequency_step;
+
+ u32 bandwidth_min;
+ u32 bandwidth_max;
+ u32 bandwidth_step;
+};
+
+struct analog_parameters {
+ unsigned int frequency;
+ unsigned int mode;
+ unsigned int audmode;
+ u64 std;
+};
+
+enum dvbfe_modcod {
+ DVBFE_MODCOD_DUMMY_PLFRAME = 0,
+ DVBFE_MODCOD_QPSK_1_4,
+ DVBFE_MODCOD_QPSK_1_3,
+ DVBFE_MODCOD_QPSK_2_5,
+ DVBFE_MODCOD_QPSK_1_2,
+ DVBFE_MODCOD_QPSK_3_5,
+ DVBFE_MODCOD_QPSK_2_3,
+ DVBFE_MODCOD_QPSK_3_4,
+ DVBFE_MODCOD_QPSK_4_5,
+ DVBFE_MODCOD_QPSK_5_6,
+ DVBFE_MODCOD_QPSK_8_9,
+ DVBFE_MODCOD_QPSK_9_10,
+ DVBFE_MODCOD_8PSK_3_5,
+ DVBFE_MODCOD_8PSK_2_3,
+ DVBFE_MODCOD_8PSK_3_4,
+ DVBFE_MODCOD_8PSK_5_6,
+ DVBFE_MODCOD_8PSK_8_9,
+ DVBFE_MODCOD_8PSK_9_10,
+ DVBFE_MODCOD_16APSK_2_3,
+ DVBFE_MODCOD_16APSK_3_4,
+ DVBFE_MODCOD_16APSK_4_5,
+ DVBFE_MODCOD_16APSK_5_6,
+ DVBFE_MODCOD_16APSK_8_9,
+ DVBFE_MODCOD_16APSK_9_10,
+ DVBFE_MODCOD_32APSK_3_4,
+ DVBFE_MODCOD_32APSK_4_5,
+ DVBFE_MODCOD_32APSK_5_6,
+ DVBFE_MODCOD_32APSK_8_9,
+ DVBFE_MODCOD_32APSK_9_10,
+ DVBFE_MODCOD_RESERVED_1,
+ DVBFE_MODCOD_BPSK_1_3,
+ DVBFE_MODCOD_BPSK_1_4,
+ DVBFE_MODCOD_RESERVED_2
+};
+
+enum tuner_param {
+ DVBFE_TUNER_FREQUENCY = (1 << 0),
+ DVBFE_TUNER_TUNERSTEP = (1 << 1),
+ DVBFE_TUNER_IFFREQ = (1 << 2),
+ DVBFE_TUNER_BANDWIDTH = (1 << 3),
+ DVBFE_TUNER_REFCLOCK = (1 << 4),
+ DVBFE_TUNER_IQSENSE = (1 << 5),
+ DVBFE_TUNER_DUMMY = (1 << 31)
+};
+
+/*
+ * ALGO_HW: (Hardware Algorithm)
+ * ----------------------------------------------------------------
+ * Devices that support this algorithm do everything in hardware
+ * and no software support is needed to handle them.
+ * Requesting these devices to LOCK is the only thing required,
+ * device is supposed to do everything in the hardware.
+ *
+ * ALGO_SW: (Software Algorithm)
+ * ----------------------------------------------------------------
+ * These are dumb devices, that require software to do everything
+ *
+ * ALGO_CUSTOM: (Customizable Agorithm)
+ * ----------------------------------------------------------------
+ * Devices having this algorithm can be customized to have specific
+ * algorithms in the frontend driver, rather than simply doing a
+ * software zig-zag. In this case the zigzag maybe hardware assisted
+ * or it maybe completely done in hardware. In all cases, usage of
+ * this algorithm, in conjunction with the search and track
+ * callbacks, utilizes the driver specific algorithm.
+ *
+ * ALGO_RECOVERY: (Recovery Algorithm)
+ * ----------------------------------------------------------------
+ * These devices have AUTO recovery capabilities from LOCK failure
+ */
+enum dvbfe_algo {
+ DVBFE_ALGO_HW = (1 << 0),
+ DVBFE_ALGO_SW = (1 << 1),
+ DVBFE_ALGO_CUSTOM = (1 << 2),
+ DVBFE_ALGO_RECOVERY = (1 << 31)
+};
+
+struct tuner_state {
+ u32 frequency;
+ u32 tunerstep;
+ u32 ifreq;
+ u32 bandwidth;
+ u32 iqsense;
+ u32 refclock;
+};
+
+/*
+ * search callback possible return status
+ *
+ * DVBFE_ALGO_SEARCH_SUCCESS
+ * The frontend search algorithm completed and returned successfully
+ *
+ * DVBFE_ALGO_SEARCH_ASLEEP
+ * The frontend search algorithm is sleeping
+ *
+ * DVBFE_ALGO_SEARCH_FAILED
+ * The frontend search for a signal failed
+ *
+ * DVBFE_ALGO_SEARCH_INVALID
+ * The frontend search algorith was probably supplied with invalid
+ * parameters and the search is an invalid one
+ *
+ * DVBFE_ALGO_SEARCH_ERROR
+ * The frontend search algorithm failed due to some error
+ *
+ * DVBFE_ALGO_SEARCH_AGAIN
+ * The frontend search algorithm was requested to search again
+ */
+enum dvbfe_search {
+ DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0),
+ DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1),
+ DVBFE_ALGO_SEARCH_FAILED = (1 << 2),
+ DVBFE_ALGO_SEARCH_INVALID = (1 << 3),
+ DVBFE_ALGO_SEARCH_AGAIN = (1 << 4),
+ DVBFE_ALGO_SEARCH_ERROR = (1 << 31),
+};
+
+
+struct dvb_tuner_ops {
+
+ struct dvb_tuner_info info;
+
+ int (*release)(struct dvb_frontend *fe);
+ int (*init)(struct dvb_frontend *fe);
+ int (*sleep)(struct dvb_frontend *fe);
+
+ /** This is for simple PLLs - set all parameters in one go. */
+ int (*set_params)(struct dvb_frontend *fe);
+ int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p);
+
+ /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
+ int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len);
+
+ /** This is to allow setting tuner-specific configs */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+
+ int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
+ int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
+ int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency);
+
+#define TUNER_STATUS_LOCKED 1
+#define TUNER_STATUS_STEREO 2
+ int (*get_status)(struct dvb_frontend *fe, u32 *status);
+ int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
+ int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
+
+ /** These are provided separately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter separately. */
+ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
+ int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
+
+ /*
+ * These are provided separately from set_params in order to facilitate silicon
+ * tuners which require sophisticated tuning loops, controlling each parameter separately.
+ */
+ int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+ int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
+};
+
+struct analog_demod_info {
+ char *name;
+};
+
+struct analog_demod_ops {
+
+ struct analog_demod_info info;
+
+ void (*set_params)(struct dvb_frontend *fe,
+ struct analog_parameters *params);
+ int (*has_signal)(struct dvb_frontend *fe);
+ int (*get_afc)(struct dvb_frontend *fe);
+ void (*tuner_status)(struct dvb_frontend *fe);
+ void (*standby)(struct dvb_frontend *fe);
+ void (*release)(struct dvb_frontend *fe);
+ int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable);
+
+ /** This is to allow setting tuner-specific configuration */
+ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
+};
+
+struct dtv_frontend_properties;
+
+struct dvb_frontend_ops {
+
+ struct dvb_frontend_info info;
+
+ u8 delsys[MAX_DELSYS];
+
+ void (*release)(struct dvb_frontend* fe);
+ void (*release_sec)(struct dvb_frontend* fe);
+
+ int (*init)(struct dvb_frontend* fe);
+ int (*sleep)(struct dvb_frontend* fe);
+
+ int (*write)(struct dvb_frontend* fe, const u8 buf[], int len);
+
+ /* if this is set, it overrides the default swzigzag */
+ int (*tune)(struct dvb_frontend* fe,
+ bool re_tune,
+ unsigned int mode_flags,
+ unsigned int *delay,
+ fe_status_t *status);
+ /* get frontend tuning algorithm from the module */
+ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe);
+
+ /* these two are only used for the swzigzag code */
+ int (*set_frontend)(struct dvb_frontend *fe);
+ int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
+
+ int (*get_frontend)(struct dvb_frontend *fe);
+
+ int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
+ int (*read_ber)(struct dvb_frontend* fe, u32* ber);
+ int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength);
+ int (*read_snr)(struct dvb_frontend* fe, u16* snr);
+ int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks);
+
+ int (*diseqc_reset_overload)(struct dvb_frontend* fe);
+ int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
+ int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply);
+ int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
+ int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
+ int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
+ int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg);
+ int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
+ int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
+ int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
+ int (*set_lna)(struct dvb_frontend *, int);
+
+ /* These callbacks are for devices that implement their own
+ * tuning algorithms, rather than a simple swzigzag
+ */
+ enum dvbfe_search (*search)(struct dvb_frontend *fe);
+
+ struct dvb_tuner_ops tuner_ops;
+ struct analog_demod_ops analog_ops;
+
+ int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+ int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
+};
+
+#ifdef __DVB_CORE__
+#define MAX_EVENT 8
+
+struct dvb_fe_events {
+ struct dvb_frontend_event events[MAX_EVENT];
+ int eventw;
+ int eventr;
+ int overflow;
+ wait_queue_head_t wait_queue;
+ struct mutex mtx;
+};
+#endif
+
+struct dtv_frontend_properties {
+
+ /* Cache State */
+ u32 state;
+
+ u32 frequency;
+ fe_modulation_t modulation;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t sectone;
+ fe_spectral_inversion_t inversion;
+ fe_code_rate_t fec_inner;
+ fe_transmit_mode_t transmission_mode;
+ u32 bandwidth_hz; /* 0 = AUTO */
+ fe_guard_interval_t guard_interval;
+ fe_hierarchy_t hierarchy;
+ u32 symbol_rate;
+ fe_code_rate_t code_rate_HP;
+ fe_code_rate_t code_rate_LP;
+
+ fe_pilot_t pilot;
+ fe_rolloff_t rolloff;
+
+ fe_delivery_system_t delivery_system;
+
+ enum fe_interleaving interleaving;
+
+ /* ISDB-T specifics */
+ u8 isdbt_partial_reception;
+ u8 isdbt_sb_mode;
+ u8 isdbt_sb_subchannel;
+ u32 isdbt_sb_segment_idx;
+ u32 isdbt_sb_segment_count;
+ u8 isdbt_layer_enabled;
+ struct {
+ u8 segment_count;
+ fe_code_rate_t fec;
+ fe_modulation_t modulation;
+ u8 interleaving;
+ } layer[3];
+
+ /* Multistream specifics */
+ u32 stream_id;
+
+ /* ATSC-MH specifics */
+ u8 atscmh_fic_ver;
+ u8 atscmh_parade_id;
+ u8 atscmh_nog;
+ u8 atscmh_tnog;
+ u8 atscmh_sgn;
+ u8 atscmh_prc;
+
+ u8 atscmh_rs_frame_mode;
+ u8 atscmh_rs_frame_ensemble;
+ u8 atscmh_rs_code_mode_pri;
+ u8 atscmh_rs_code_mode_sec;
+ u8 atscmh_sccc_block_mode;
+ u8 atscmh_sccc_code_mode_a;
+ u8 atscmh_sccc_code_mode_b;
+ u8 atscmh_sccc_code_mode_c;
+ u8 atscmh_sccc_code_mode_d;
+};
+
+struct dvb_frontend {
+ struct dvb_frontend_ops ops;
+ struct dvb_adapter *dvb;
+ void *demodulator_priv;
+ void *tuner_priv;
+ void *frontend_priv;
+ void *sec_priv;
+ void *analog_demod_priv;
+ struct dtv_frontend_properties dtv_property_cache;
+#define DVB_FRONTEND_COMPONENT_TUNER 0
+#define DVB_FRONTEND_COMPONENT_DEMOD 1
+ int (*callback)(void *adapter_priv, int component, int cmd, int arg);
+ int id;
+};
+
+extern int dvb_register_frontend(struct dvb_adapter *dvb,
+ struct dvb_frontend *fe);
+
+extern int dvb_unregister_frontend(struct dvb_frontend *fe);
+
+extern void dvb_frontend_detach(struct dvb_frontend *fe);
+
+extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
+extern int dvb_frontend_suspend(struct dvb_frontend *fe);
+extern int dvb_frontend_resume(struct dvb_frontend *fe);
+
+extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
+extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime);
+
+#endif
diff --git a/drivers/media/dvb/dvb-core/dvb_math.c b/drivers/media/dvb-core/dvb_math.c
index beb7c93aa6cb..beb7c93aa6cb 100644
--- a/drivers/media/dvb/dvb-core/dvb_math.c
+++ b/drivers/media/dvb-core/dvb_math.c
diff --git a/drivers/media/dvb/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h
index aecc867e9404..aecc867e9404 100644
--- a/drivers/media/dvb/dvb-core/dvb_math.h
+++ b/drivers/media/dvb-core/dvb_math.h
diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index 8766ce8c354d..8766ce8c354d 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb-core/dvb_net.h
index 1e53acd50cf4..1e53acd50cf4 100644
--- a/drivers/media/dvb/dvb-core/dvb_net.h
+++ b/drivers/media/dvb-core/dvb_net.h
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
index a5712cd7c65f..a5712cd7c65f 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h
index 41f04dae69b6..41f04dae69b6 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/drivers/media/dvb-core/dvb_ringbuffer.h
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
new file mode 100644
index 000000000000..d33101aaf0b5
--- /dev/null
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -0,0 +1,507 @@
+/*
+ * dvbdev.c
+ *
+ * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
+ * & Marcus Metzler <marcus@convergence.de>
+ * for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include "dvbdev.h"
+
+static DEFINE_MUTEX(dvbdev_mutex);
+static int dvbdev_debug;
+
+module_param(dvbdev_debug, int, 0644);
+MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
+
+#define dprintk if (dvbdev_debug) printk
+
+static LIST_HEAD(dvb_adapter_list);
+static DEFINE_MUTEX(dvbdev_register_lock);
+
+static const char * const dnames[] = {
+ "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
+ "net", "osd"
+};
+
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+#define MAX_DVB_MINORS 256
+#define DVB_MAX_IDS MAX_DVB_MINORS
+#else
+#define DVB_MAX_IDS 4
+#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
+#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
+#endif
+
+static struct class *dvb_class;
+
+static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
+static DECLARE_RWSEM(minor_rwsem);
+
+static int dvb_device_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev;
+
+ mutex_lock(&dvbdev_mutex);
+ down_read(&minor_rwsem);
+ dvbdev = dvb_minors[iminor(inode)];
+
+ if (dvbdev && dvbdev->fops) {
+ int err = 0;
+ const struct file_operations *old_fops;
+
+ file->private_data = dvbdev;
+ old_fops = file->f_op;
+ file->f_op = fops_get(dvbdev->fops);
+ if (file->f_op == NULL) {
+ file->f_op = old_fops;
+ goto fail;
+ }
+ if(file->f_op->open)
+ err = file->f_op->open(inode,file);
+ if (err) {
+ fops_put(file->f_op);
+ file->f_op = fops_get(old_fops);
+ }
+ fops_put(old_fops);
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ return err;
+ }
+fail:
+ up_read(&minor_rwsem);
+ mutex_unlock(&dvbdev_mutex);
+ return -ENODEV;
+}
+
+
+static const struct file_operations dvb_device_fops =
+{
+ .owner = THIS_MODULE,
+ .open = dvb_device_open,
+ .llseek = noop_llseek,
+};
+
+static struct cdev dvb_device_cdev;
+
+int dvb_generic_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->users)
+ return -EBUSY;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ if (!dvbdev->readers)
+ return -EBUSY;
+ dvbdev->readers--;
+ } else {
+ if (!dvbdev->writers)
+ return -EBUSY;
+ dvbdev->writers--;
+ }
+
+ dvbdev->users--;
+ return 0;
+}
+EXPORT_SYMBOL(dvb_generic_open);
+
+
+int dvb_generic_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
+ dvbdev->readers++;
+ } else {
+ dvbdev->writers++;
+ }
+
+ dvbdev->users++;
+ return 0;
+}
+EXPORT_SYMBOL(dvb_generic_release);
+
+
+long dvb_generic_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct dvb_device *dvbdev = file->private_data;
+
+ if (!dvbdev)
+ return -ENODEV;
+
+ if (!dvbdev->kernel_ioctl)
+ return -EINVAL;
+
+ return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
+}
+EXPORT_SYMBOL(dvb_generic_ioctl);
+
+
+static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
+{
+ u32 id = 0;
+
+ while (id < DVB_MAX_IDS) {
+ struct dvb_device *dev;
+ list_for_each_entry(dev, &adap->device_list, list_head)
+ if (dev->type == type && dev->id == id)
+ goto skip;
+ return id;
+skip:
+ id++;
+ }
+ return -ENFILE;
+}
+
+
+int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
+ const struct dvb_device *template, void *priv, int type)
+{
+ struct dvb_device *dvbdev;
+ struct file_operations *dvbdevfops;
+ struct device *clsdev;
+ int minor;
+ int id;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ if ((id = dvbdev_get_free_id (adap, type)) < 0){
+ mutex_unlock(&dvbdev_register_lock);
+ *pdvbdev = NULL;
+ printk(KERN_ERR "%s: couldn't find free device id\n", __func__);
+ return -ENFILE;
+ }
+
+ *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
+
+ if (!dvbdev){
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
+
+ if (!dvbdevfops){
+ kfree (dvbdev);
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENOMEM;
+ }
+
+ memcpy(dvbdev, template, sizeof(struct dvb_device));
+ dvbdev->type = type;
+ dvbdev->id = id;
+ dvbdev->adapter = adap;
+ dvbdev->priv = priv;
+ dvbdev->fops = dvbdevfops;
+ init_waitqueue_head (&dvbdev->wait_queue);
+
+ memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
+ dvbdevfops->owner = adap->module;
+
+ list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+ down_write(&minor_rwsem);
+#ifdef CONFIG_DVB_DYNAMIC_MINORS
+ for (minor = 0; minor < MAX_DVB_MINORS; minor++)
+ if (dvb_minors[minor] == NULL)
+ break;
+
+ if (minor == MAX_DVB_MINORS) {
+ kfree(dvbdevfops);
+ kfree(dvbdev);
+ up_write(&minor_rwsem);
+ mutex_unlock(&dvbdev_register_lock);
+ return -EINVAL;
+ }
+#else
+ minor = nums2minor(adap->num, type, id);
+#endif
+
+ dvbdev->minor = minor;
+ dvb_minors[minor] = dvbdev;
+ up_write(&minor_rwsem);
+
+ mutex_unlock(&dvbdev_register_lock);
+
+ clsdev = device_create(dvb_class, adap->device,
+ MKDEV(DVB_MAJOR, minor),
+ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
+ if (IS_ERR(clsdev)) {
+ printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
+ __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
+ return PTR_ERR(clsdev);
+ }
+
+ dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
+ adap->num, dnames[type], id, minor, minor);
+
+ return 0;
+}
+EXPORT_SYMBOL(dvb_register_device);
+
+
+void dvb_unregister_device(struct dvb_device *dvbdev)
+{
+ if (!dvbdev)
+ return;
+
+ down_write(&minor_rwsem);
+ dvb_minors[dvbdev->minor] = NULL;
+ up_write(&minor_rwsem);
+
+ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
+
+ list_del (&dvbdev->list_head);
+ kfree (dvbdev->fops);
+ kfree (dvbdev);
+}
+EXPORT_SYMBOL(dvb_unregister_device);
+
+static int dvbdev_check_free_adapter_num(int num)
+{
+ struct list_head *entry;
+ list_for_each(entry, &dvb_adapter_list) {
+ struct dvb_adapter *adap;
+ adap = list_entry(entry, struct dvb_adapter, list_head);
+ if (adap->num == num)
+ return 0;
+ }
+ return 1;
+}
+
+static int dvbdev_get_free_adapter_num (void)
+{
+ int num = 0;
+
+ while (num < DVB_MAX_ADAPTERS) {
+ if (dvbdev_check_free_adapter_num(num))
+ return num;
+ num++;
+ }
+
+ return -ENFILE;
+}
+
+
+int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+ struct module *module, struct device *device,
+ short *adapter_nums)
+{
+ int i, num;
+
+ mutex_lock(&dvbdev_register_lock);
+
+ for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
+ num = adapter_nums[i];
+ if (num >= 0 && num < DVB_MAX_ADAPTERS) {
+ /* use the one the driver asked for */
+ if (dvbdev_check_free_adapter_num(num))
+ break;
+ } else {
+ num = dvbdev_get_free_adapter_num();
+ break;
+ }
+ num = -1;
+ }
+
+ if (num < 0) {
+ mutex_unlock(&dvbdev_register_lock);
+ return -ENFILE;
+ }
+
+ memset (adap, 0, sizeof(struct dvb_adapter));
+ INIT_LIST_HEAD (&adap->device_list);
+
+ printk(KERN_INFO "DVB: registering new adapter (%s)\n", name);
+
+ adap->num = num;
+ adap->name = name;
+ adap->module = module;
+ adap->device = device;
+ adap->mfe_shared = 0;
+ adap->mfe_dvbdev = NULL;
+ mutex_init (&adap->mfe_lock);
+
+ list_add_tail (&adap->list_head, &dvb_adapter_list);
+
+ mutex_unlock(&dvbdev_register_lock);
+
+ return num;
+}
+EXPORT_SYMBOL(dvb_register_adapter);
+
+
+int dvb_unregister_adapter(struct dvb_adapter *adap)
+{
+ mutex_lock(&dvbdev_register_lock);
+ list_del (&adap->list_head);
+ mutex_unlock(&dvbdev_register_lock);
+ return 0;
+}
+EXPORT_SYMBOL(dvb_unregister_adapter);
+
+/* if the miracle happens and "generic_usercopy()" is included into
+ the kernel, then this can vanish. please don't make the mistake and
+ define this as video_usercopy(). this will introduce a dependecy
+ to the v4l "videodev.o" module, which is unnecessary for some
+ cards (ie. the budget dvb-cards don't need the v4l module...) */
+int dvb_usercopy(struct file *file,
+ unsigned int cmd, unsigned long arg,
+ int (*func)(struct file *file,
+ unsigned int cmd, void *arg))
+{
+ char sbuf[128];
+ void *mbuf = NULL;
+ void *parg = NULL;
+ int err = -EINVAL;
+
+ /* Copy arguments into temp kernel buffer */
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE:
+ /*
+ * For this command, the pointer is actually an integer
+ * argument.
+ */
+ parg = (void *) arg;
+ break;
+ case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+ case _IOC_WRITE:
+ case (_IOC_WRITE | _IOC_READ):
+ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ parg = sbuf;
+ } else {
+ /* too big to allocate from stack */
+ mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
+ if (NULL == mbuf)
+ return -ENOMEM;
+ parg = mbuf;
+ }
+
+ err = -EFAULT;
+ if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+ goto out;
+ break;
+ }
+
+ /* call driver */
+ mutex_lock(&dvbdev_mutex);
+ if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
+ err = -ENOTTY;
+ mutex_unlock(&dvbdev_mutex);
+
+ if (err < 0)
+ goto out;
+
+ /* Copy results into user buffer */
+ switch (_IOC_DIR(cmd))
+ {
+ case _IOC_READ:
+ case (_IOC_WRITE | _IOC_READ):
+ if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+ err = -EFAULT;
+ break;
+ }
+
+out:
+ kfree(mbuf);
+ return err;
+}
+
+static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
+ add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
+ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
+ return 0;
+}
+
+static char *dvb_devnode(struct device *dev, umode_t *mode)
+{
+ struct dvb_device *dvbdev = dev_get_drvdata(dev);
+
+ return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
+ dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
+}
+
+
+static int __init init_dvbdev(void)
+{
+ int retval;
+ dev_t dev = MKDEV(DVB_MAJOR, 0);
+
+ if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
+ printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR);
+ return retval;
+ }
+
+ cdev_init(&dvb_device_cdev, &dvb_device_fops);
+ if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
+ printk(KERN_ERR "dvb-core: unable register character device\n");
+ goto error;
+ }
+
+ dvb_class = class_create(THIS_MODULE, "dvb");
+ if (IS_ERR(dvb_class)) {
+ retval = PTR_ERR(dvb_class);
+ goto error;
+ }
+ dvb_class->dev_uevent = dvb_uevent;
+ dvb_class->devnode = dvb_devnode;
+ return 0;
+
+error:
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(dev, MAX_DVB_MINORS);
+ return retval;
+}
+
+
+static void __exit exit_dvbdev(void)
+{
+ class_destroy(dvb_class);
+ cdev_del(&dvb_device_cdev);
+ unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
+}
+
+subsys_initcall(init_dvbdev);
+module_exit(exit_dvbdev);
+
+MODULE_DESCRIPTION("DVB Core Driver");
+MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h
new file mode 100644
index 000000000000..93a9470d3f0c
--- /dev/null
+++ b/drivers/media/dvb-core/dvbdev.h
@@ -0,0 +1,146 @@
+/*
+ * dvbdev.h
+ *
+ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
+ * for convergence integrated media GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Lesser Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef _DVBDEV_H_
+#define _DVBDEV_H_
+
+#include <linux/types.h>
+#include <linux/poll.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+
+#define DVB_MAJOR 212
+
+#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0
+ #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS
+#else
+ #define DVB_MAX_ADAPTERS 8
+#endif
+
+#define DVB_UNSET (-1)
+
+#define DVB_DEVICE_VIDEO 0
+#define DVB_DEVICE_AUDIO 1
+#define DVB_DEVICE_SEC 2
+#define DVB_DEVICE_FRONTEND 3
+#define DVB_DEVICE_DEMUX 4
+#define DVB_DEVICE_DVR 5
+#define DVB_DEVICE_CA 6
+#define DVB_DEVICE_NET 7
+#define DVB_DEVICE_OSD 8
+
+#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \
+ static short adapter_nr[] = \
+ {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \
+ module_param_array(adapter_nr, short, NULL, 0444); \
+ MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
+
+struct dvb_frontend;
+
+struct dvb_adapter {
+ int num;
+ struct list_head list_head;
+ struct list_head device_list;
+ const char *name;
+ u8 proposed_mac [6];
+ void* priv;
+
+ struct device *device;
+
+ struct module *module;
+
+ int mfe_shared; /* indicates mutually exclusive frontends */
+ struct dvb_device *mfe_dvbdev; /* frontend device in use */
+ struct mutex mfe_lock; /* access lock for thread creation */
+};
+
+
+struct dvb_device {
+ struct list_head list_head;
+ const struct file_operations *fops;
+ struct dvb_adapter *adapter;
+ int type;
+ int minor;
+ u32 id;
+
+ /* in theory, 'users' can vanish now,
+ but I don't want to change too much now... */
+ int readers;
+ int writers;
+ int users;
+
+ wait_queue_head_t wait_queue;
+ /* don't really need those !? -- FIXME: use video_usercopy */
+ int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg);
+
+ void *priv;
+};
+
+
+extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
+ struct module *module, struct device *device,
+ short *adapter_nums);
+extern int dvb_unregister_adapter (struct dvb_adapter *adap);
+
+extern int dvb_register_device (struct dvb_adapter *adap,
+ struct dvb_device **pdvbdev,
+ const struct dvb_device *template,
+ void *priv,
+ int type);
+
+extern void dvb_unregister_device (struct dvb_device *dvbdev);
+
+extern int dvb_generic_open (struct inode *inode, struct file *file);
+extern int dvb_generic_release (struct inode *inode, struct file *file);
+extern long dvb_generic_ioctl (struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+/* we don't mess with video_usercopy() any more,
+we simply define out own dvb_usercopy(), which will hopefully become
+generic_usercopy() someday... */
+
+extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+ int (*func)(struct file *file, unsigned int cmd, void *arg));
+
+/** generic DVB attach function. */
+#ifdef CONFIG_MEDIA_ATTACH
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ void *__r = NULL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (void *) __a(ARGS); \
+ if (__r == NULL) \
+ symbol_put(FUNCTION); \
+ } else { \
+ printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
+ } \
+ __r; \
+})
+
+#else
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+#endif
+
+#endif /* #ifndef _DVBDEV_H_ */
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
new file mode 100644
index 000000000000..5efec73a32d2
--- /dev/null
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -0,0 +1,741 @@
+menu "Customise DVB Frontends"
+ visible if !MEDIA_SUBDRV_AUTOSELECT
+
+comment "Multistandard (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_STB0899
+ tristate "STB0899 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want
+ to support this demodulator based frontends
+
+config DVB_STB6100
+ tristate "STB6100 based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Silicon tuner from ST used in conjunction with the STB0899
+ demodulator. Say Y when you want to support this tuner.
+
+config DVB_STV090x
+ tristate "STV0900/STV0903(A/B) based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators.
+ Say Y when you want to support these frontends.
+
+config DVB_STV6110x
+ tristate "STV6110/(A) based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A Silicon tuner that supports DVB-S and DVB-S2 modes
+
+comment "Multistandard (cable + terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_DRXK
+ tristate "Micronas DRXK based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Micronas DRX-K DVB-C/T demodulator.
+
+ Say Y when you want to support this frontend.
+
+config DVB_TDA18271C2DD
+ tristate "NXP TDA18271C2 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ NXP TDA18271 silicon tuner.
+
+ Say Y when you want to support this tuner.
+
+comment "DVB-S (satellite) frontends"
+ depends on DVB_CORE
+
+config DVB_CX24110
+ tristate "Conexant CX24110 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX24123
+ tristate "Conexant CX24123 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT312
+ tristate "Zarlink VP310/MT312/ZL10313 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10036
+ tristate "Zarlink ZL10036 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10039
+ tristate "Zarlink ZL10039 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_S5H1420
+ tristate "Samsung S5H1420 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV0288
+ tristate "ST STV0288 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STB6000
+ tristate "ST STB6000 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_STV0299
+ tristate "ST STV0299 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV6110
+ tristate "ST STV6110 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_STV0900
+ tristate "ST STV0900 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 demodulator. Say Y when you want to support this frontend.
+
+config DVB_TDA8083
+ tristate "Philips TDA8083 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10086
+ tristate "Philips TDA10086 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA8261
+ tristate "Philips TDA8261 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_VES1X93
+ tristate "VLSI VES1893 or VES1993 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TUNER_ITD1000
+ tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TUNER_CX24113
+ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+
+config DVB_TDA826X
+ tristate "Philips TDA826X silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_TUA6100
+ tristate "Infineon TUA6100 PLL"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S PLL chip.
+
+config DVB_CX24116
+ tristate "Conexant CX24116 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_SI21XX
+ tristate "Silicon Labs SI21XX based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_DS3000
+ tristate "Montage Tehnology DS3000 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
+
+config DVB_MB86A16
+ tristate "Fujitsu MB86A16 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S/DSS Direct Conversion reveiver.
+ Say Y when you want to support this frontend.
+
+config DVB_TDA10071
+ tristate "NXP TDA10071"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+comment "DVB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_SP8870
+ tristate "Spase sp8870 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_SP887X
+ tristate "Spase sp887x based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_CX22700
+ tristate "Conexant CX22700 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_CX22702
+ tristate "Conexant cx22702 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_S5H1432
+ tristate "Samsung s5h1432 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_DRXD
+ tristate "Micronas DRXD driver"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ Note: this driver was based on vendor driver reference code (released
+ under the GPL) as opposed to the existing drx397xd driver, which
+ was written via reverse engineering.
+
+config DVB_L64781
+ tristate "LSI L64781"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA1004X
+ tristate "Philips TDA10045H/TDA10046H based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_NXT6000
+ tristate "NxtWave Communications NXT6000 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_MT352
+ tristate "Zarlink MT352 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_ZL10353
+ tristate "Zarlink ZL10353 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_DIB3000MB
+ tristate "DiBcom 3000M-B"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB3000MC
+ tristate "DiBcom 3000P/M-C"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000M
+ tristate "DiBcom 7000MA/MB/PA/PB/MC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Designed for mobile usage. Say Y when you want
+ to support this frontend.
+
+config DVB_DIB7000P
+ tristate "DiBcom 7000PC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ 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 !MEDIA_SUBDRV_AUTOSELECT
+ 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
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+config DVB_AF9013
+ tristate "Afatech AF9013 demodulator"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_EC100
+ tristate "E3C EC100"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_HD29L2
+ tristate "HDIC HD29L2"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ 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 !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T/C tuner module. Say Y when you want to support this frontend.
+
+config DVB_CXD2820R
+ tristate "Sony CXD2820R"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_RTL2830
+ tristate "Realtek RTL2830 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+config DVB_RTL2832
+ tristate "Realtek RTL2832 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y when you want to support this frontend.
+
+comment "DVB-C (cable) frontends"
+ depends on DVB_CORE
+
+config DVB_VES1820
+ tristate "VLSI VES1820 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10021
+ tristate "Philips TDA10021 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10023
+ tristate "Philips TDA10023 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+config DVB_STV0297
+ tristate "ST STV0297 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-C tuner module. Say Y when you want to support this frontend.
+
+comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends"
+ depends on DVB_CORE
+
+config DVB_NXT200X
+ tristate "NxtWave Communications NXT2002/NXT2004 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" and
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_OR51211
+ tristate "Oren OR51211 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
+
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
+ download it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+config DVB_OR51132
+ tristate "Oren OR51132 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+ This driver needs external firmware. Please use the commands
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_qam" to
+ download firmwares for 8VSB and QAM64/256, respectively. Copy them to
+ /usr/lib/hotplug/firmware or /lib/firmware (depending on
+ configuration of firmware hotplug).
+
+config DVB_BCM3510
+ tristate "Broadcom BCM3510"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
+ support this frontend.
+
+config DVB_LGDT330X
+ tristate "LG Electronics LGDT3302/LGDT3303 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_LGDT3305
+ tristate "LG Electronics LGDT3304 and LGDT3305 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_LG2160
+ tristate "LG Electronics LG216x based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC/MH demodulator module. Say Y when you want
+ to support this frontend.
+
+config DVB_S5H1409
+ tristate "Samsung S5H1409 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+config DVB_AU8522
+ depends on I2C
+ tristate
+
+config DVB_AU8522_DTV
+ tristate "Auvitek AU8522 based DTV demod"
+ depends on DVB_CORE && I2C
+ select DVB_AU8522
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable DTV demodulation support for this frontend.
+
+config DVB_AU8522_V4L
+ tristate "Auvitek AU8522 based ATV demod"
+ depends on VIDEO_V4L2 && I2C
+ select DVB_AU8522
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable ATV demodulation support for this frontend.
+
+config DVB_S5H1411
+ tristate "Samsung S5H1411 based"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
+comment "ISDB-T (terrestrial) frontends"
+ depends on DVB_CORE
+
+config DVB_S921
+ tristate "Sharp S921 frontend"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
+ Say Y when you want to support this frontend.
+
+config DVB_DIB8000
+ tristate "DiBcom 8000MB/MC"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator.
+ Say Y when you want to support this frontend.
+
+config DVB_MB86A20S
+ tristate "Fujitsu mb86a20s"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator.
+ Say Y when you want to support this frontend.
+
+comment "Digital terrestrial only tuners/PLL"
+ depends on DVB_CORE
+
+config DVB_PLL
+ tristate "Generic I2C PLL based tuners"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ This module drives a number of tuners based on PLL chips with a
+ common I2C interface. Say Y when you want to support these tuners.
+
+config DVB_TUNER_DIB0070
+ tristate "DiBcom DiB0070 silicon base-band tuner"
+ depends on I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner DiB0070 from DiBcom.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+config DVB_TUNER_DIB0090
+ tristate "DiBcom DiB0090 silicon base-band tuner"
+ depends on I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner DiB0090 from DiBcom.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+comment "SEC control devices for DVB-S"
+ depends on DVB_CORE
+
+config DVB_LNBP21
+ tristate "LNBP21/LNBH24 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chips.
+
+config DVB_LNBP22
+ tristate "LNBP22 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ LNB power supply and control voltage
+ regulator chip with step-up converter
+ and I2C interface.
+ Say Y when you want to support this chip.
+
+config DVB_ISL6405
+ tristate "ISL6405 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chip.
+
+config DVB_ISL6421
+ tristate "ISL6421 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ An SEC control chip.
+
+config DVB_ISL6423
+ tristate "ISL6423 SEC controller"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A SEC controller chip from Intersil
+
+config DVB_A8293
+ tristate "Allegro A8293"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+
+config DVB_LGS8GL5
+ tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_LGS8GXX
+ tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator"
+ depends on DVB_CORE && I2C
+ select FW_LOADER
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_ATBM8830
+ tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DMB-TH tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA665x
+ tristate "TDA665x tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for tuner modules based on Philips TDA6650/TDA6651 chips.
+ Say Y when you want to support this chip.
+
+ Currently supported tuners:
+ * Panasonic ENV57H12D5 (ET-50DT)
+
+config DVB_IX2505V
+ tristate "Sharp IX2505V silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_IT913X_FE
+ tristate "it913x frontend and it9137 tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T tuner module.
+ Say Y when you want to support this frontend.
+
+config DVB_M88RS2000
+ tristate "M88RS2000 DVB-S demodulator and tuner"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-S tuner module.
+ Say Y when you want to support this frontend.
+
+config DVB_AF9033
+ tristate "Afatech AF9033 DVB-T demodulator"
+ depends on DVB_CORE && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+
+comment "Tools to develop new frontends"
+
+config DVB_DUMMY_FE
+ tristate "Dummy frontend driver"
+ default n
+endmenu
diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile
new file mode 100644
index 000000000000..7eb73bbd2e26
--- /dev/null
+++ b/drivers/media/dvb-frontends/Makefile
@@ -0,0 +1,105 @@
+#
+# Makefile for the kernel DVB frontend device drivers.
+#
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core/
+ccflags-y += -I$(srctree)/drivers/media/tuners/
+
+stb0899-objs := stb0899_drv.o stb0899_algo.o
+stv0900-objs := stv0900_core.o stv0900_sw.o
+drxd-objs := drxd_firm.o drxd_hard.o
+cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
+drxk-objs := drxk_hard.o
+
+obj-$(CONFIG_DVB_PLL) += dvb-pll.o
+obj-$(CONFIG_DVB_STV0299) += stv0299.o
+obj-$(CONFIG_DVB_STB0899) += stb0899.o
+obj-$(CONFIG_DVB_STB6100) += stb6100.o
+obj-$(CONFIG_DVB_SP8870) += sp8870.o
+obj-$(CONFIG_DVB_CX22700) += cx22700.o
+obj-$(CONFIG_DVB_S5H1432) += s5h1432.o
+obj-$(CONFIG_DVB_CX24110) += cx24110.o
+obj-$(CONFIG_DVB_TDA8083) += tda8083.o
+obj-$(CONFIG_DVB_L64781) += l64781.o
+obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
+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
+obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
+obj-$(CONFIG_DVB_SP887X) += sp887x.o
+obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+obj-$(CONFIG_DVB_MT352) += mt352.o
+obj-$(CONFIG_DVB_ZL10036) += zl10036.o
+obj-$(CONFIG_DVB_ZL10039) += zl10039.o
+obj-$(CONFIG_DVB_ZL10353) += zl10353.o
+obj-$(CONFIG_DVB_CX22702) += cx22702.o
+obj-$(CONFIG_DVB_DRXD) += drxd.o
+obj-$(CONFIG_DVB_TDA10021) += tda10021.o
+obj-$(CONFIG_DVB_TDA10023) += tda10023.o
+obj-$(CONFIG_DVB_STV0297) += stv0297.o
+obj-$(CONFIG_DVB_NXT200X) += nxt200x.o
+obj-$(CONFIG_DVB_OR51211) += or51211.o
+obj-$(CONFIG_DVB_OR51132) += or51132.o
+obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
+obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
+obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
+obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
+obj-$(CONFIG_DVB_LG2160) += lg2160.o
+obj-$(CONFIG_DVB_CX24123) += cx24123.o
+obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
+obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
+obj-$(CONFIG_DVB_ISL6405) += isl6405.o
+obj-$(CONFIG_DVB_ISL6421) += isl6421.o
+obj-$(CONFIG_DVB_TDA10086) += tda10086.o
+obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TDA8261) += tda8261.o
+obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
+obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o
+obj-$(CONFIG_DVB_TUA6100) += tua6100.o
+obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
+obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
+obj-$(CONFIG_DVB_AU8522) += au8522_common.o
+obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o
+obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o
+obj-$(CONFIG_DVB_TDA10048) += tda10048.o
+obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
+obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
+obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
+obj-$(CONFIG_DVB_TDA665x) += tda665x.o
+obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
+obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
+obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
+obj-$(CONFIG_DVB_AF9013) += af9013.o
+obj-$(CONFIG_DVB_CX24116) += cx24116.o
+obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+obj-$(CONFIG_DVB_STV0288) += stv0288.o
+obj-$(CONFIG_DVB_STB6000) += stb6000.o
+obj-$(CONFIG_DVB_S921) += s921.o
+obj-$(CONFIG_DVB_STV6110) += stv6110.o
+obj-$(CONFIG_DVB_STV0900) += stv0900.o
+obj-$(CONFIG_DVB_STV090x) += stv090x.o
+obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
+obj-$(CONFIG_DVB_ISL6423) += isl6423.o
+obj-$(CONFIG_DVB_EC100) += ec100.o
+obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
+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
+obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
+obj-$(CONFIG_DVB_DRXK) += drxk.o
+obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
+obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
+obj-$(CONFIG_DVB_A8293) += a8293.o
+obj-$(CONFIG_DVB_TDA10071) += tda10071.o
+obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
+obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
+obj-$(CONFIG_DVB_AF9033) += af9033.o
+
diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
index cff44a389b40..cff44a389b40 100644
--- a/drivers/media/dvb/frontends/a8293.c
+++ b/drivers/media/dvb-frontends/a8293.c
diff --git a/drivers/media/dvb/frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h
index ed29e5504f76..ed29e5504f76 100644
--- a/drivers/media/dvb/frontends/a8293.h
+++ b/drivers/media/dvb-frontends/a8293.h
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
new file mode 100644
index 000000000000..e9f04a36577b
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -0,0 +1,1548 @@
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "af9013_priv.h"
+
+struct af9013_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct af9013_config config;
+
+ /* tuner/demod RF and IF AGC limits used for signal strength calc */
+ u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
+ u16 signal_strength;
+ u32 ber;
+ u32 ucblocks;
+ u16 snr;
+ u32 bandwidth_hz;
+ fe_status_t fe_status;
+ unsigned long set_frontend_jiffies;
+ unsigned long read_status_jiffies;
+ bool first_tune;
+ bool i2c_gate_state;
+ unsigned int statistics_step:3;
+ struct delayed_work statistics_work;
+};
+
+/* write multiple registers */
+static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
+ const u8 *val, int len)
+{
+ int ret;
+ u8 buf[3+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->config.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = mbox;
+ memcpy(&buf[3], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%04x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
+ u8 *val, int len)
+{
+ int ret;
+ u8 buf[3];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->config.i2c_addr,
+ .flags = 0,
+ .len = 3,
+ .buf = buf,
+ }, {
+ .addr = priv->config.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = (reg >> 0) & 0xff;
+ buf[2] = mbox;
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%04x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int af9013_wr_regs(struct af9013_state *priv, u16 reg, const u8 *val,
+ int len)
+{
+ int ret, i;
+ u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(1 << 0);
+
+ if ((priv->config.ts_mode == AF9013_TS_USB) &&
+ ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
+ mbox |= ((len - 1) << 2);
+ ret = af9013_wr_regs_i2c(priv, mbox, reg, val, len);
+ } else {
+ for (i = 0; i < len; i++) {
+ ret = af9013_wr_regs_i2c(priv, mbox, reg+i, val+i, 1);
+ if (ret)
+ goto err;
+ }
+ }
+
+err:
+ return 0;
+}
+
+/* read multiple registers */
+static int af9013_rd_regs(struct af9013_state *priv, u16 reg, u8 *val, int len)
+{
+ int ret, i;
+ u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(0 << 0);
+
+ if ((priv->config.ts_mode == AF9013_TS_USB) &&
+ ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
+ mbox |= ((len - 1) << 2);
+ ret = af9013_rd_regs_i2c(priv, mbox, reg, val, len);
+ } else {
+ for (i = 0; i < len; i++) {
+ ret = af9013_rd_regs_i2c(priv, mbox, reg+i, val+i, 1);
+ if (ret)
+ goto err;
+ }
+ }
+
+err:
+ return 0;
+}
+
+/* write single register */
+static int af9013_wr_reg(struct af9013_state *priv, u16 reg, u8 val)
+{
+ return af9013_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int af9013_rd_reg(struct af9013_state *priv, u16 reg, u8 *val)
+{
+ return af9013_rd_regs(priv, reg, val, 1);
+}
+
+static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
+ u8 len)
+{
+ u8 mbox = (1 << 7)|(1 << 6)|((len - 1) << 2)|(1 << 1)|(1 << 0);
+ return af9013_wr_regs_i2c(state, mbox, reg, val, len);
+}
+
+static int af9013_wr_reg_bits(struct af9013_state *state, u16 reg, int pos,
+ int len, u8 val)
+{
+ int ret;
+ u8 tmp, mask;
+
+ /* no need for read if whole reg is written */
+ if (len != 8) {
+ ret = af9013_rd_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+
+ mask = (0xff >> (8 - len)) << pos;
+ val <<= pos;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return af9013_wr_reg(state, reg, val);
+}
+
+static int af9013_rd_reg_bits(struct af9013_state *state, u16 reg, int pos,
+ int len, u8 *val)
+{
+ int ret;
+ u8 tmp;
+
+ ret = af9013_rd_reg(state, reg, &tmp);
+ if (ret)
+ return ret;
+
+ *val = (tmp >> pos);
+ *val &= (0xff >> (8 - len));
+
+ return 0;
+}
+
+static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
+{
+ int ret;
+ u8 pos;
+ u16 addr;
+
+ dev_dbg(&state->i2c->dev, "%s: gpio=%d gpioval=%02x\n",
+ __func__, gpio, gpioval);
+
+ /*
+ * GPIO0 & GPIO1 0xd735
+ * GPIO2 & GPIO3 0xd736
+ */
+
+ switch (gpio) {
+ case 0:
+ case 1:
+ addr = 0xd735;
+ break;
+ case 2:
+ case 3:
+ addr = 0xd736;
+ break;
+
+ default:
+ dev_err(&state->i2c->dev, "%s: invalid gpio=%d\n",
+ KBUILD_MODNAME, gpio);
+ ret = -EINVAL;
+ goto err;
+ };
+
+ switch (gpio) {
+ case 0:
+ case 2:
+ pos = 0;
+ break;
+ case 1:
+ case 3:
+ default:
+ pos = 4;
+ break;
+ };
+
+ ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static u32 af9013_div(struct af9013_state *state, u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+
+ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n",
+ __func__, a, b, x, r, r);
+
+ return r;
+}
+
+static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
+{
+ int ret, i;
+ u8 tmp;
+
+ dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ /* enable reset */
+ ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 1);
+ if (ret)
+ goto err;
+
+ /* start reset mechanism */
+ ret = af9013_wr_reg(state, 0xaeff, 1);
+ if (ret)
+ goto err;
+
+ /* wait reset performs */
+ for (i = 0; i < 150; i++) {
+ ret = af9013_rd_reg_bits(state, 0xd417, 1, 1, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp)
+ break; /* reset done */
+
+ usleep_range(5000, 25000);
+ }
+
+ if (!tmp)
+ return -ETIMEDOUT;
+
+ if (onoff) {
+ /* clear reset */
+ ret = af9013_wr_reg_bits(state, 0xd417, 1, 1, 0);
+ if (ret)
+ goto err;
+
+ /* disable reset */
+ ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 0);
+
+ /* power on */
+ ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 0);
+ } else {
+ /* power off */
+ ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 1);
+ }
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* reset and start BER counter */
+ ret = af9013_wr_reg_bits(state, 0xd391, 4, 1, 1);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[5];
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* check if error bit count is ready */
+ ret = af9013_rd_reg_bits(state, 0xd391, 4, 1, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (!buf[0]) {
+ dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__);
+ return 0;
+ }
+
+ ret = af9013_rd_regs(state, 0xd387, buf, 5);
+ if (ret)
+ goto err;
+
+ state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ state->ucblocks += (buf[4] << 8) | buf[3];
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_statistics_snr_start(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* start SNR meas */
+ ret = af9013_wr_reg_bits(state, 0xd2e1, 3, 1, 1);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_statistics_snr_result(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 buf[3], tmp;
+ u32 snr_val;
+ const struct af9013_snr *uninitialized_var(snr_lut);
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* check if SNR ready */
+ ret = af9013_rd_reg_bits(state, 0xd2e1, 3, 1, &tmp);
+ if (ret)
+ goto err;
+
+ if (!tmp) {
+ dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__);
+ return 0;
+ }
+
+ /* read value */
+ ret = af9013_rd_regs(state, 0xd2e3, buf, 3);
+ if (ret)
+ goto err;
+
+ snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ /* read current modulation */
+ ret = af9013_rd_reg(state, 0xd3c1, &tmp);
+ if (ret)
+ goto err;
+
+ switch ((tmp >> 6) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_lut);
+ snr_lut = qpsk_snr_lut;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_lut);
+ snr_lut = qam16_snr_lut;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_lut);
+ snr_lut = qam64_snr_lut;
+ break;
+ default:
+ goto err;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ tmp = snr_lut[i].snr;
+
+ if (snr_val < snr_lut[i].val)
+ break;
+ }
+ state->snr = tmp * 10; /* dB/10 */
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret = 0;
+ u8 buf[2], rf_gain, if_gain;
+ int signal_strength;
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ if (!state->signal_strength_en)
+ return 0;
+
+ ret = af9013_rd_regs(state, 0xd07c, buf, 2);
+ if (ret)
+ goto err;
+
+ rf_gain = buf[0];
+ if_gain = buf[1];
+
+ signal_strength = (0xffff / \
+ (9 * (state->rf_50 + state->if_50) - \
+ 11 * (state->rf_80 + state->if_80))) * \
+ (10 * (rf_gain + if_gain) - \
+ 11 * (state->rf_80 + state->if_80));
+ if (signal_strength < 0)
+ signal_strength = 0;
+ else if (signal_strength > 0xffff)
+ signal_strength = 0xffff;
+
+ state->signal_strength = signal_strength;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static void af9013_statistics_work(struct work_struct *work)
+{
+ struct af9013_state *state = container_of(work,
+ struct af9013_state, statistics_work.work);
+ unsigned int next_msec;
+
+ /* update only signal strength when demod is not locked */
+ if (!(state->fe_status & FE_HAS_LOCK)) {
+ state->statistics_step = 0;
+ state->ber = 0;
+ state->snr = 0;
+ }
+
+ switch (state->statistics_step) {
+ default:
+ state->statistics_step = 0;
+ case 0:
+ af9013_statistics_signal_strength(&state->fe);
+ state->statistics_step++;
+ next_msec = 300;
+ break;
+ case 1:
+ af9013_statistics_snr_start(&state->fe);
+ state->statistics_step++;
+ next_msec = 200;
+ break;
+ case 2:
+ af9013_statistics_ber_unc_start(&state->fe);
+ state->statistics_step++;
+ next_msec = 1000;
+ break;
+ case 3:
+ af9013_statistics_snr_result(&state->fe);
+ state->statistics_step++;
+ next_msec = 400;
+ break;
+ case 4:
+ af9013_statistics_ber_unc_result(&state->fe);
+ state->statistics_step++;
+ next_msec = 100;
+ break;
+ }
+
+ schedule_delayed_work(&state->statistics_work,
+ msecs_to_jiffies(next_msec));
+}
+
+static int af9013_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9013_set_frontend(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, sampling_freq;
+ bool auto_mode, spec_inv;
+ u8 buf[6];
+ u32 if_frequency, freq_cw;
+
+ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
+ __func__, c->frequency, c->bandwidth_hz);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* program CFOE coefficients */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
+ if (coeff_lut[i].clock == state->config.clock &&
+ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
+ break;
+ }
+ }
+
+ ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val,
+ sizeof(coeff_lut[i].val));
+ }
+
+ /* program frequency control */
+ if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) {
+ /* get used IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ if_frequency = state->config.if_frequency;
+
+ dev_dbg(&state->i2c->dev, "%s: if_frequency=%d\n",
+ __func__, if_frequency);
+
+ sampling_freq = if_frequency;
+
+ while (sampling_freq > (state->config.clock / 2))
+ sampling_freq -= state->config.clock;
+
+ if (sampling_freq < 0) {
+ sampling_freq *= -1;
+ spec_inv = state->config.spec_inv;
+ } else {
+ spec_inv = !state->config.spec_inv;
+ }
+
+ freq_cw = af9013_div(state, sampling_freq, state->config.clock,
+ 23);
+
+ if (spec_inv)
+ freq_cw = 0x800000 - freq_cw;
+
+ buf[0] = (freq_cw >> 0) & 0xff;
+ buf[1] = (freq_cw >> 8) & 0xff;
+ buf[2] = (freq_cw >> 16) & 0x7f;
+
+ freq_cw = 0x800000 - freq_cw;
+
+ buf[3] = (freq_cw >> 0) & 0xff;
+ buf[4] = (freq_cw >> 8) & 0xff;
+ buf[5] = (freq_cw >> 16) & 0x7f;
+
+ ret = af9013_wr_regs(state, 0xd140, buf, 3);
+ if (ret)
+ goto err;
+
+ ret = af9013_wr_regs(state, 0x9be7, buf, 6);
+ if (ret)
+ goto err;
+ }
+
+ /* clear TPS lock flag */
+ ret = af9013_wr_reg_bits(state, 0xd330, 3, 1, 1);
+ if (ret)
+ goto err;
+
+ /* clear MPEG2 lock flag */
+ ret = af9013_wr_reg_bits(state, 0xd507, 6, 1, 0);
+ if (ret)
+ goto err;
+
+ /* empty channel function */
+ ret = af9013_wr_reg_bits(state, 0x9bfe, 0, 1, 0);
+ if (ret)
+ goto err;
+
+ /* empty DVB-T channel function */
+ ret = af9013_wr_reg_bits(state, 0x9bc2, 0, 1, 0);
+ if (ret)
+ goto err;
+
+ /* transmission parameters */
+ auto_mode = false;
+ memset(buf, 0, 3);
+
+ switch (c->transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ auto_mode = 1;
+ break;
+ case TRANSMISSION_MODE_2K:
+ break;
+ case TRANSMISSION_MODE_8K:
+ buf[0] |= (1 << 0);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid transmission_mode\n",
+ __func__);
+ auto_mode = 1;
+ }
+
+ switch (c->guard_interval) {
+ case GUARD_INTERVAL_AUTO:
+ auto_mode = 1;
+ break;
+ case GUARD_INTERVAL_1_32:
+ break;
+ case GUARD_INTERVAL_1_16:
+ buf[0] |= (1 << 2);
+ break;
+ case GUARD_INTERVAL_1_8:
+ buf[0] |= (2 << 2);
+ break;
+ case GUARD_INTERVAL_1_4:
+ buf[0] |= (3 << 2);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid guard_interval\n",
+ __func__);
+ auto_mode = 1;
+ }
+
+ switch (c->hierarchy) {
+ case HIERARCHY_AUTO:
+ auto_mode = 1;
+ break;
+ case HIERARCHY_NONE:
+ break;
+ case HIERARCHY_1:
+ buf[0] |= (1 << 4);
+ break;
+ case HIERARCHY_2:
+ buf[0] |= (2 << 4);
+ break;
+ case HIERARCHY_4:
+ buf[0] |= (3 << 4);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__);
+ auto_mode = 1;
+ };
+
+ switch (c->modulation) {
+ case QAM_AUTO:
+ auto_mode = 1;
+ break;
+ case QPSK:
+ break;
+ case QAM_16:
+ buf[1] |= (1 << 6);
+ break;
+ case QAM_64:
+ buf[1] |= (2 << 6);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid modulation\n", __func__);
+ auto_mode = 1;
+ }
+
+ /* Use HP. How and which case we can switch to LP? */
+ buf[1] |= (1 << 4);
+
+ switch (c->code_rate_HP) {
+ case FEC_AUTO:
+ auto_mode = 1;
+ break;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 0);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 0);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 0);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 0);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid code_rate_HP\n",
+ __func__);
+ auto_mode = 1;
+ }
+
+ switch (c->code_rate_LP) {
+ case FEC_AUTO:
+ auto_mode = 1;
+ break;
+ case FEC_1_2:
+ break;
+ case FEC_2_3:
+ buf[2] |= (1 << 3);
+ break;
+ case FEC_3_4:
+ buf[2] |= (2 << 3);
+ break;
+ case FEC_5_6:
+ buf[2] |= (3 << 3);
+ break;
+ case FEC_7_8:
+ buf[2] |= (4 << 3);
+ break;
+ case FEC_NONE:
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid code_rate_LP\n",
+ __func__);
+ auto_mode = 1;
+ }
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ break;
+ case 7000000:
+ buf[1] |= (1 << 2);
+ break;
+ case 8000000:
+ buf[1] |= (2 << 2);
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = af9013_wr_regs(state, 0xd3c0, buf, 3);
+ if (ret)
+ goto err;
+
+ if (auto_mode) {
+ /* clear easy mode flag */
+ ret = af9013_wr_reg(state, 0xaefd, 0);
+ if (ret)
+ goto err;
+
+ dev_dbg(&state->i2c->dev, "%s: auto params\n", __func__);
+ } else {
+ /* set easy mode flag */
+ ret = af9013_wr_reg(state, 0xaefd, 1);
+ if (ret)
+ goto err;
+
+ ret = af9013_wr_reg(state, 0xaefe, 0);
+ if (ret)
+ goto err;
+
+ dev_dbg(&state->i2c->dev, "%s: manual params\n", __func__);
+ }
+
+ /* tune */
+ ret = af9013_wr_reg(state, 0xffff, 0);
+ if (ret)
+ goto err;
+
+ state->bandwidth_hz = c->bandwidth_hz;
+ state->set_frontend_jiffies = jiffies;
+ state->first_tune = false;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 buf[3];
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ ret = af9013_rd_regs(state, 0xd3c0, buf, 3);
+ if (ret)
+ goto err;
+
+ switch ((buf[1] >> 6) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[2] >> 3) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 2) & 3) {
+ case 0:
+ c->bandwidth_hz = 6000000;
+ break;
+ case 1:
+ c->bandwidth_hz = 7000000;
+ break;
+ case 2:
+ c->bandwidth_hz = 8000000;
+ break;
+ }
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ /*
+ * Return status from the cache if it is younger than 2000ms with the
+ * exception of last tune is done during 4000ms.
+ */
+ if (time_is_after_jiffies(
+ state->read_status_jiffies + msecs_to_jiffies(2000)) &&
+ time_is_before_jiffies(
+ state->set_frontend_jiffies + msecs_to_jiffies(4000))
+ ) {
+ *status = state->fe_status;
+ return 0;
+ } else {
+ *status = 0;
+ }
+
+ /* MPEG2 lock */
+ ret = af9013_rd_reg_bits(state, 0xd507, 6, 1, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+
+ if (!*status) {
+ /* TPS lock */
+ ret = af9013_rd_reg_bits(state, 0xd330, 3, 1, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ state->fe_status = *status;
+ state->read_status_jiffies = jiffies;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ *snr = state->snr;
+ return 0;
+}
+
+static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ *strength = state->signal_strength;
+ return 0;
+}
+
+static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ *ber = state->ber;
+ return 0;
+}
+
+static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ *ucblocks = state->ucblocks;
+ return 0;
+}
+
+static int af9013_init(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 buf[3], tmp;
+ u32 adc_cw;
+ const struct af9013_reg_bit *init;
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* power on */
+ ret = af9013_power_ctrl(state, 1);
+ if (ret)
+ goto err;
+
+ /* enable ADC */
+ ret = af9013_wr_reg(state, 0xd73a, 0xa4);
+ if (ret)
+ goto err;
+
+ /* write API version to firmware */
+ ret = af9013_wr_regs(state, 0x9bf2, state->config.api_version, 4);
+ if (ret)
+ goto err;
+
+ /* program ADC control */
+ switch (state->config.clock) {
+ case 28800000: /* 28.800 MHz */
+ tmp = 0;
+ break;
+ case 20480000: /* 20.480 MHz */
+ tmp = 1;
+ break;
+ case 28000000: /* 28.000 MHz */
+ tmp = 2;
+ break;
+ case 25000000: /* 25.000 MHz */
+ tmp = 3;
+ break;
+ default:
+ dev_err(&state->i2c->dev, "%s: invalid clock\n",
+ KBUILD_MODNAME);
+ return -EINVAL;
+ }
+
+ adc_cw = af9013_div(state, state->config.clock, 1000000ul, 19);
+ buf[0] = (adc_cw >> 0) & 0xff;
+ buf[1] = (adc_cw >> 8) & 0xff;
+ buf[2] = (adc_cw >> 16) & 0xff;
+
+ ret = af9013_wr_regs(state, 0xd180, buf, 3);
+ if (ret)
+ goto err;
+
+ ret = af9013_wr_reg_bits(state, 0x9bd2, 0, 4, tmp);
+ if (ret)
+ goto err;
+
+ /* set I2C master clock */
+ ret = af9013_wr_reg(state, 0xd416, 0x14);
+ if (ret)
+ goto err;
+
+ /* set 16 embx */
+ ret = af9013_wr_reg_bits(state, 0xd700, 1, 1, 1);
+ if (ret)
+ goto err;
+
+ /* set no trigger */
+ ret = af9013_wr_reg_bits(state, 0xd700, 2, 1, 0);
+ if (ret)
+ goto err;
+
+ /* set read-update bit for constellation */
+ ret = af9013_wr_reg_bits(state, 0xd371, 1, 1, 1);
+ if (ret)
+ goto err;
+
+ /* settings for mp2if */
+ if (state->config.ts_mode == AF9013_TS_USB) {
+ /* AF9015 split PSB to 1.5k + 0.5k */
+ ret = af9013_wr_reg_bits(state, 0xd50b, 2, 1, 1);
+ if (ret)
+ goto err;
+ } else {
+ /* AF9013 change the output bit to data7 */
+ ret = af9013_wr_reg_bits(state, 0xd500, 3, 1, 1);
+ if (ret)
+ goto err;
+
+ /* AF9013 set mpeg to full speed */
+ ret = af9013_wr_reg_bits(state, 0xd502, 4, 1, 1);
+ if (ret)
+ goto err;
+ }
+
+ ret = af9013_wr_reg_bits(state, 0xd520, 4, 1, 1);
+ if (ret)
+ goto err;
+
+ /* load OFSM settings */
+ dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* load tuner specific settings */
+ dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n",
+ __func__);
+ switch (state->config.tuner) {
+ case AF9013_TUNER_MXL5003D:
+ len = ARRAY_SIZE(tuner_init_mxl5003d);
+ init = tuner_init_mxl5003d;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ case AF9013_TUNER_MXL5007T:
+ len = ARRAY_SIZE(tuner_init_mxl5005);
+ init = tuner_init_mxl5005;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ len = ARRAY_SIZE(tuner_init_env77h11d5);
+ init = tuner_init_env77h11d5;
+ break;
+ case AF9013_TUNER_MT2060:
+ len = ARRAY_SIZE(tuner_init_mt2060);
+ init = tuner_init_mt2060;
+ break;
+ case AF9013_TUNER_MC44S803:
+ len = ARRAY_SIZE(tuner_init_mc44s803);
+ init = tuner_init_mc44s803;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ len = ARRAY_SIZE(tuner_init_qt1010);
+ init = tuner_init_qt1010;
+ break;
+ case AF9013_TUNER_MT2060_2:
+ len = ARRAY_SIZE(tuner_init_mt2060_2);
+ init = tuner_init_mt2060_2;
+ break;
+ case AF9013_TUNER_TDA18271:
+ case AF9013_TUNER_TDA18218:
+ len = ARRAY_SIZE(tuner_init_tda18271);
+ init = tuner_init_tda18271;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ len = ARRAY_SIZE(tuner_init_unknown);
+ init = tuner_init_unknown;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
+ init[i].len, init[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* TS mode */
+ ret = af9013_wr_reg_bits(state, 0xd500, 1, 2, state->config.ts_mode);
+ if (ret)
+ goto err;
+
+ /* enable lock led */
+ ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 1);
+ if (ret)
+ goto err;
+
+ /* check if we support signal strength */
+ if (!state->signal_strength_en) {
+ ret = af9013_rd_reg_bits(state, 0x9bee, 0, 1,
+ &state->signal_strength_en);
+ if (ret)
+ goto err;
+ }
+
+ /* read values needed for signal strength calculation */
+ if (state->signal_strength_en && !state->rf_50) {
+ ret = af9013_rd_reg(state, 0x9bbd, &state->rf_50);
+ if (ret)
+ goto err;
+
+ ret = af9013_rd_reg(state, 0x9bd0, &state->rf_80);
+ if (ret)
+ goto err;
+
+ ret = af9013_rd_reg(state, 0x9be2, &state->if_50);
+ if (ret)
+ goto err;
+
+ ret = af9013_rd_reg(state, 0x9be4, &state->if_80);
+ if (ret)
+ goto err;
+ }
+
+ /* SNR */
+ ret = af9013_wr_reg(state, 0xd2e2, 1);
+ if (ret)
+ goto err;
+
+ /* BER / UCB */
+ buf[0] = (10000 >> 0) & 0xff;
+ buf[1] = (10000 >> 8) & 0xff;
+ ret = af9013_wr_regs(state, 0xd385, buf, 2);
+ if (ret)
+ goto err;
+
+ /* enable FEC monitor */
+ ret = af9013_wr_reg_bits(state, 0xd392, 1, 1, 1);
+ if (ret)
+ goto err;
+
+ state->first_tune = true;
+ schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400));
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_sleep(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* stop statistics polling */
+ cancel_delayed_work_sync(&state->statistics_work);
+
+ /* disable lock led */
+ ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 0);
+ if (ret)
+ goto err;
+
+ /* power off */
+ ret = af9013_power_ctrl(state, 0);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct af9013_state *state = fe->demodulator_priv;
+
+ dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable);
+
+ /* gate already open or close */
+ if (state->i2c_gate_state == enable)
+ return 0;
+
+ if (state->config.ts_mode == AF9013_TS_USB)
+ ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable);
+ else
+ ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable);
+ if (ret)
+ goto err;
+
+ state->i2c_gate_state = enable;
+
+ return ret;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static void af9013_release(struct dvb_frontend *fe)
+{
+ struct af9013_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops af9013_ops;
+
+static int af9013_download_firmware(struct af9013_state *state)
+{
+ int i, len, remaining, ret;
+ const struct firmware *fw;
+ u16 checksum = 0;
+ u8 val;
+ u8 fw_params[4];
+ u8 *fw_file = AF9013_FIRMWARE;
+
+ msleep(100);
+ /* check whether firmware is already running */
+ ret = af9013_rd_reg(state, 0x98be, &val);
+ if (ret)
+ goto err;
+ else
+ dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n",
+ __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ dev_info(&state->i2c->dev, "%s: found a '%s' in cold state, will try " \
+ "to load a firmware\n",
+ KBUILD_MODNAME, af9013_ops.info.name);
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
+ if (ret) {
+ dev_info(&state->i2c->dev, "%s: did not find the firmware " \
+ "file. (%s) Please see linux/Documentation/dvb/ for " \
+ "more details on firmware-problems. (%d)\n",
+ KBUILD_MODNAME, fw_file, ret);
+ goto err;
+ }
+
+ dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s'\n",
+ KBUILD_MODNAME, fw_file);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ fw_params[0] = checksum >> 8;
+ fw_params[1] = checksum & 0xff;
+ fw_params[2] = fw->size >> 8;
+ fw_params[3] = fw->size & 0xff;
+
+ /* write fw checksum & size */
+ ret = af9013_write_ofsm_regs(state, 0x50fc,
+ fw_params, sizeof(fw_params));
+ if (ret)
+ goto err_release;
+
+ #define FW_ADDR 0x5100 /* firmware start address */
+ #define LEN_MAX 16 /* max packet size */
+ for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
+ len = remaining;
+ if (len > LEN_MAX)
+ len = LEN_MAX;
+
+ ret = af9013_write_ofsm_regs(state,
+ FW_ADDR + fw->size - remaining,
+ (u8 *) &fw->data[fw->size - remaining], len);
+ if (ret) {
+ dev_err(&state->i2c->dev,
+ "%s: firmware download failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err_release;
+ }
+ }
+
+ /* request boot firmware */
+ ret = af9013_wr_reg(state, 0xe205, 1);
+ if (ret)
+ goto err_release;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9013_rd_reg(state, 0x98be, &val);
+ if (ret)
+ goto err_release;
+
+ dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n",
+ __func__, val);
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ dev_err(&state->i2c->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ } else if (val != 0x0c) {
+ dev_err(&state->i2c->dev, "%s: firmware boot timeout\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ }
+
+err_release:
+ release_firmware(fw);
+err:
+exit:
+ if (!ret)
+ dev_info(&state->i2c->dev, "%s: found a '%s' in warm state\n",
+ KBUILD_MODNAME, af9013_ops.info.name);
+ return ret;
+}
+
+struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9013_state *state = NULL;
+ u8 buf[4], i;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
+ if (state == NULL)
+ goto err;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct af9013_config));
+
+ /* download firmware */
+ if (state->config.ts_mode != AF9013_TS_USB) {
+ ret = af9013_download_firmware(state);
+ if (ret)
+ goto err;
+ }
+
+ /* firmware version */
+ ret = af9013_rd_regs(state, 0x5103, buf, 4);
+ if (ret)
+ goto err;
+
+ dev_info(&state->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
+ KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
+
+ /* set GPIOs */
+ for (i = 0; i < sizeof(state->config.gpio); i++) {
+ ret = af9013_set_gpio(state, i, state->config.gpio[i]);
+ if (ret)
+ goto err;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->fe.ops, &af9013_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->fe.demodulator_priv = state;
+
+ INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
+
+ return &state->fe;
+err:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9013_attach);
+
+static struct dvb_frontend_ops af9013_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Afatech AF9013",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = af9013_release,
+
+ .init = af9013_init,
+ .sleep = af9013_sleep,
+
+ .get_tune_settings = af9013_get_tune_settings,
+ .set_frontend = af9013_set_frontend,
+ .get_frontend = af9013_get_frontend,
+
+ .read_status = af9013_read_status,
+ .read_snr = af9013_read_snr,
+ .read_signal_strength = af9013_read_signal_strength,
+ .read_ber = af9013_read_ber,
+ .read_ucblocks = af9013_read_ucblocks,
+
+ .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(AF9013_FIRMWARE);
diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h
new file mode 100644
index 000000000000..dc837d91327a
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013.h
@@ -0,0 +1,118 @@
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 AF9013_H
+#define AF9013_H
+
+#include <linux/dvb/frontend.h>
+
+/* AF9013/5 GPIOs (mostly guessed)
+ demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
+ demod#1-gpio#1 - xtal setting (?)
+ demod#1-gpio#3 - tuner#1
+ demod#2-gpio#0 - tuner#2
+ demod#2-gpio#1 - xtal setting (?)
+*/
+
+struct af9013_config {
+ /*
+ * I2C address
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock
+ * 20480000, 25000000, 28000000, 28800000
+ */
+ u32 clock;
+
+ /*
+ * tuner
+ */
+#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */
+#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */
+#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */
+#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */
+#define AF9013_TUNER_MT2060 130 /* Microtune */
+#define AF9013_TUNER_MC44S803 133 /* Freescale */
+#define AF9013_TUNER_QT1010 134 /* Quantek */
+#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */
+#define AF9013_TUNER_MT2060_2 147 /* Microtune */
+#define AF9013_TUNER_TDA18271 156 /* NXP */
+#define AF9013_TUNER_QT1010A 162 /* Quantek */
+#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */
+#define AF9013_TUNER_TDA18218 179 /* NXP */
+ u8 tuner;
+
+ /*
+ * IF frequency
+ */
+ u32 if_frequency;
+
+ /*
+ * TS settings
+ */
+#define AF9013_TS_USB 0
+#define AF9013_TS_PARALLEL 1
+#define AF9013_TS_SERIAL 2
+ u8 ts_mode:2;
+
+ /*
+ * input spectrum inversion
+ */
+ bool spec_inv;
+
+ /*
+ * firmware API version
+ */
+ u8 api_version[4];
+
+ /*
+ * GPIOs
+ */
+#define AF9013_GPIO_ON (1 << 0)
+#define AF9013_GPIO_EN (1 << 1)
+#define AF9013_GPIO_O (1 << 2)
+#define AF9013_GPIO_I (1 << 3)
+#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
+#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
+ u8 gpio[4];
+};
+
+#if defined(CONFIG_DVB_AF9013) || \
+ (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9013_attach(
+const struct af9013_config *config, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_AF9013 */
+
+#endif /* AF9013_H */
diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h
new file mode 100644
index 000000000000..8b9392cfc00d
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9013_priv.h
@@ -0,0 +1,909 @@
+/*
+ * Afatech AF9013 demodulator driver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 AF9013_PRIV_H
+#define AF9013_PRIV_H
+
+#include "dvb_frontend.h"
+#include "af9013.h"
+#include <linux/firmware.h>
+
+#define AF9013_FIRMWARE "dvb-fe-af9013.fw"
+
+struct af9013_reg_bit {
+ u16 addr;
+ u8 pos:4;
+ u8 len:4;
+ u8 val;
+};
+
+struct af9013_snr {
+ u32 val;
+ u8 snr;
+};
+
+struct af9013_coeff {
+ u32 clock;
+ u32 bandwidth_hz;
+ u8 val[24];
+};
+
+/* pre-calculated coeff lookup table */
+static const struct af9013_coeff coeff_lut[] = {
+ /* 28.800 MHz */
+ { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14,
+ 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a,
+ 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } },
+ { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71,
+ 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38,
+ 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } },
+ { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf,
+ 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7,
+ 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } },
+ /* 20.480 MHz */
+ { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24,
+ 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92,
+ 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } },
+ { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40,
+ 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00,
+ 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } },
+ { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b,
+ 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d,
+ 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } },
+ /* 28.000 MHz */
+ { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39,
+ 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f,
+ 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } },
+ { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92,
+ 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49,
+ 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } },
+ { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb,
+ 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63,
+ 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } },
+ /* 25.000 MHz */
+ { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9,
+ 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e,
+ 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } },
+ { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e,
+ 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7,
+ 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } },
+ { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63,
+ 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f,
+ 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
+};
+
+/* QPSK SNR lookup table */
+static const struct af9013_snr qpsk_snr_lut[] = {
+ { 0x000000, 0 },
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0xffffff, 15 },
+};
+
+/* QAM16 SNR lookup table */
+static const struct af9013_snr qam16_snr_lut[] = {
+ { 0x000000, 0 },
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0xffffff, 20 },
+};
+
+/* QAM64 SNR lookup table */
+static const struct af9013_snr qam64_snr_lut[] = {
+ { 0x000000, 0 },
+ { 0x03109b, 12 },
+ { 0x0310d4, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0xffffff, 27 },
+};
+
+static const struct af9013_reg_bit ofsm_init[] = {
+ { 0xd73a, 0, 8, 0xa1 },
+ { 0xd73b, 0, 8, 0x1f },
+ { 0xd73c, 4, 4, 0x0a },
+ { 0xd732, 3, 1, 0x00 },
+ { 0xd731, 4, 2, 0x03 },
+ { 0xd73d, 7, 1, 0x01 },
+ { 0xd740, 0, 1, 0x00 },
+ { 0xd740, 1, 1, 0x00 },
+ { 0xd740, 2, 1, 0x00 },
+ { 0xd740, 3, 1, 0x01 },
+ { 0xd3c1, 4, 1, 0x01 },
+ { 0x9124, 0, 8, 0x58 },
+ { 0x9125, 0, 2, 0x02 },
+ { 0xd3a2, 0, 8, 0x00 },
+ { 0xd3a3, 0, 8, 0x04 },
+ { 0xd305, 0, 8, 0x32 },
+ { 0xd306, 0, 8, 0x10 },
+ { 0xd304, 0, 8, 0x04 },
+ { 0x9112, 0, 1, 0x01 },
+ { 0x911d, 0, 1, 0x01 },
+ { 0x911a, 0, 1, 0x01 },
+ { 0x911b, 0, 1, 0x01 },
+ { 0x9bce, 0, 4, 0x02 },
+ { 0x9116, 0, 1, 0x01 },
+ { 0x9122, 0, 8, 0xd0 },
+ { 0xd2e0, 0, 8, 0xd0 },
+ { 0xd2e9, 0, 4, 0x0d },
+ { 0xd38c, 0, 8, 0xfc },
+ { 0xd38d, 0, 8, 0x00 },
+ { 0xd38e, 0, 8, 0x7e },
+ { 0xd38f, 0, 8, 0x00 },
+ { 0xd390, 0, 8, 0x2f },
+ { 0xd145, 4, 1, 0x01 },
+ { 0xd1a9, 4, 1, 0x01 },
+ { 0xd158, 5, 3, 0x01 },
+ { 0xd159, 0, 6, 0x06 },
+ { 0xd167, 0, 8, 0x00 },
+ { 0xd168, 0, 4, 0x07 },
+ { 0xd1c3, 5, 3, 0x00 },
+ { 0xd1c4, 0, 6, 0x00 },
+ { 0xd1c5, 0, 7, 0x10 },
+ { 0xd1c6, 0, 3, 0x02 },
+ { 0xd080, 2, 5, 0x03 },
+ { 0xd081, 4, 4, 0x09 },
+ { 0xd098, 4, 4, 0x0f },
+ { 0xd098, 0, 4, 0x03 },
+ { 0xdbc0, 4, 1, 0x01 },
+ { 0xdbc7, 0, 8, 0x08 },
+ { 0xdbc8, 4, 4, 0x00 },
+ { 0xdbc9, 0, 5, 0x01 },
+ { 0xd280, 0, 8, 0xe0 },
+ { 0xd281, 0, 8, 0xff },
+ { 0xd282, 0, 8, 0xff },
+ { 0xd283, 0, 8, 0xc3 },
+ { 0xd284, 0, 8, 0xff },
+ { 0xd285, 0, 4, 0x01 },
+ { 0xd0f0, 0, 7, 0x1a },
+ { 0xd0f1, 4, 1, 0x01 },
+ { 0xd0f2, 0, 8, 0x0c },
+ { 0xd101, 5, 3, 0x06 },
+ { 0xd103, 0, 4, 0x08 },
+ { 0xd0f8, 0, 7, 0x20 },
+ { 0xd111, 5, 1, 0x00 },
+ { 0xd111, 6, 1, 0x00 },
+ { 0x910b, 0, 8, 0x0a },
+ { 0x9115, 0, 8, 0x02 },
+ { 0x910c, 0, 8, 0x02 },
+ { 0x910d, 0, 8, 0x08 },
+ { 0x910e, 0, 8, 0x0a },
+ { 0x9bf6, 0, 8, 0x06 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf7, 0, 8, 0x05 },
+ { 0x9bf9, 0, 8, 0x0f },
+ { 0x9bfc, 0, 8, 0x13 },
+ { 0x9bd3, 0, 8, 0xff },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+};
+
+/* Panasonic ENV77H11D5 tuner init
+ AF9013_TUNER_ENV77H11D5 = 129 */
+static const struct af9013_reg_bit tuner_init_env77h11d5[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x03 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0xd015, 0, 8, 0x50 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xeb },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf4 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xeb },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0x52 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060 = 130 */
+static const struct af9013_reg_bit tuner_init_mt2060[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bbe, 0, 1, 0x00 },
+ { 0x9bcc, 0, 1, 0x00 },
+ { 0x9bb9, 0, 8, 0x75 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x30 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcc },
+ { 0x9be4, 0, 8, 0xa0 },
+ { 0x9bbd, 0, 8, 0x8e },
+ { 0x9be2, 0, 8, 0x4d },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Microtune MT2060 tuner init
+ AF9013_TUNER_MT2060_2 = 147 */
+static const struct af9013_reg_bit tuner_init_mt2060_2[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0x9bbe, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x32 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x36 },
+ { 0xd00d, 0, 2, 0x03 },
+ { 0xd00a, 0, 8, 0x35 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x90 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x36 },
+ { 0x9bc6, 0, 8, 0x03 },
+ { 0x9bba, 0, 8, 0xc9 },
+ { 0x9bc9, 0, 8, 0x79 },
+ { 0xd011, 0, 8, 0x10 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0x45 },
+ { 0xd014, 0, 2, 0x03 },
+ { 0xd040, 0, 8, 0x98 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0xcf },
+ { 0xd043, 0, 2, 0x03 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x96 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0xd045, 7, 1, 0x00 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5003 tuner init
+ AF9013_TUNER_MXL5003D = 3 */
+static const struct af9013_reg_bit tuner_init_mxl5003d[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bfc, 0, 8, 0x0f },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0xd015, 0, 8, 0x33 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x6c },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x3d },
+ { 0xd00d, 0, 2, 0x00 },
+ { 0xd00a, 0, 8, 0x45 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x07 },
+ { 0x9bc8, 0, 8, 0x52 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x3d },
+ { 0x9bc6, 0, 8, 0x00 },
+ { 0x9bba, 0, 8, 0xa2 },
+ { 0x9bc9, 0, 8, 0xa0 },
+ { 0xd011, 0, 8, 0x56 },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x50 },
+ { 0xd014, 0, 2, 0x00 },
+ { 0xd040, 0, 8, 0x56 },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x50 },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 8, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* MaxLinear MXL5005S & MXL5007T tuner init
+ AF9013_TUNER_MXL5005D = 13
+ AF9013_TUNER_MXL5005R = 30
+ AF9013_TUNER_MXL5007T = 177 */
+static const struct af9013_reg_bit tuner_init_mxl5005[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x07 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x40 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x40 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x73 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0xfa },
+ { 0xd00d, 0, 2, 0x01 },
+ { 0xd00a, 0, 8, 0xff },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x23 },
+ { 0x9bc8, 0, 8, 0x55 },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0xfa },
+ { 0x9bc6, 0, 8, 0x01 },
+ { 0x9bba, 0, 8, 0xff },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0x9bd3, 0, 8, 0x95 },
+ { 0xd011, 0, 8, 0x70 },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xfb },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x70 },
+ { 0xd041, 0, 2, 0x01 },
+ { 0xd042, 0, 8, 0xfb },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x93 },
+ { 0x9be4, 0, 8, 0xfe },
+ { 0x9bbd, 0, 8, 0x63 },
+ { 0x9be2, 0, 8, 0xfe },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Quantek QT1010 tuner init
+ AF9013_TUNER_QT1010 = 134
+ AF9013_TUNER_QT1010A = 162 */
+static const struct af9013_reg_bit tuner_init_qt1010[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x09 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x28 },
+ { 0x9bff, 0, 8, 0x20 },
+ { 0xd008, 0, 8, 0x0f },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x99 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x0f },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0x50 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x00 },
+ { 0x9bc8, 0, 8, 0x00 },
+ { 0x9bc3, 0, 8, 0x0f },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x0f },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xc5 },
+ { 0x9bc9, 0, 8, 0xff },
+ { 0xd011, 0, 8, 0x58 },
+ { 0xd012, 0, 2, 0x02 },
+ { 0xd013, 0, 8, 0x89 },
+ { 0xd014, 0, 2, 0x01 },
+ { 0xd040, 0, 8, 0x58 },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x89 },
+ { 0xd043, 0, 2, 0x01 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xcd },
+ { 0x9be4, 0, 8, 0xbb },
+ { 0x9bbd, 0, 8, 0x93 },
+ { 0x9be2, 0, 8, 0x80 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+/* Freescale MC44S803 tuner init
+ AF9013_TUNER_MC44S803 = 133 */
+static const struct af9013_reg_bit tuner_init_mc44s803[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x06 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x00 },
+ { 0x9be3, 0, 8, 0x00 },
+ { 0x9bf6, 0, 8, 0x01 },
+ { 0x9bf8, 0, 8, 0x02 },
+ { 0x9bf9, 0, 8, 0x02 },
+ { 0x9bfc, 0, 8, 0x1f },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x24 },
+ { 0x9bff, 0, 8, 0x24 },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0x01 },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x7b },
+ { 0xd007, 0, 2, 0x00 },
+ { 0xd00c, 0, 8, 0x7c },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xfe },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bc7, 0, 8, 0x08 },
+ { 0x9bc8, 0, 8, 0x9a },
+ { 0x9bc3, 0, 8, 0x01 },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x7c },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bba, 0, 8, 0xfc },
+ { 0x9bc9, 0, 8, 0xaa },
+ { 0xd011, 0, 8, 0x6b },
+ { 0xd012, 0, 2, 0x00 },
+ { 0xd013, 0, 8, 0x88 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x6b },
+ { 0xd041, 0, 2, 0x00 },
+ { 0xd042, 0, 8, 0x7c },
+ { 0xd043, 0, 2, 0x02 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0x9e },
+ { 0x9be4, 0, 8, 0xff },
+ { 0x9bbd, 0, 8, 0x9e },
+ { 0x9be2, 0, 8, 0x25 },
+ { 0x9bee, 0, 1, 0x01 },
+ { 0xd73b, 3, 1, 0x00 },
+};
+
+/* unknown, probably for tin can tuner, tuner init
+ AF9013_TUNER_UNKNOWN = 140 */
+static const struct af9013_reg_bit tuner_init_unknown[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x02 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+};
+
+/* NXP TDA18271 & TDA18218 tuner init
+ AF9013_TUNER_TDA18271 = 156
+ AF9013_TUNER_TDA18218 = 179 */
+static const struct af9013_reg_bit tuner_init_tda18271[] = {
+ { 0x9bd5, 0, 8, 0x01 },
+ { 0x9bd6, 0, 8, 0x04 },
+ { 0xd1a0, 1, 1, 0x01 },
+ { 0xd000, 0, 1, 0x01 },
+ { 0xd000, 1, 1, 0x00 },
+ { 0xd001, 1, 1, 0x01 },
+ { 0xd001, 0, 1, 0x00 },
+ { 0xd001, 5, 1, 0x00 },
+ { 0xd002, 0, 5, 0x19 },
+ { 0xd003, 0, 5, 0x1a },
+ { 0xd004, 0, 5, 0x19 },
+ { 0xd005, 0, 5, 0x1a },
+ { 0xd00e, 0, 5, 0x10 },
+ { 0xd00f, 0, 3, 0x04 },
+ { 0xd00f, 3, 3, 0x05 },
+ { 0xd010, 0, 3, 0x04 },
+ { 0xd010, 3, 3, 0x05 },
+ { 0xd016, 4, 4, 0x03 },
+ { 0xd01f, 0, 6, 0x0a },
+ { 0xd020, 0, 6, 0x0a },
+ { 0x9bda, 0, 8, 0x01 },
+ { 0x9be3, 0, 8, 0x01 },
+ { 0xd1a0, 1, 1, 0x00 },
+ { 0x9bbe, 0, 1, 0x01 },
+ { 0x9bcc, 0, 1, 0x01 },
+ { 0x9bb9, 0, 8, 0x00 },
+ { 0x9bcd, 0, 8, 0x18 },
+ { 0x9bff, 0, 8, 0x2c },
+ { 0xd015, 0, 8, 0x46 },
+ { 0xd016, 0, 1, 0x00 },
+ { 0xd044, 0, 8, 0x46 },
+ { 0xd045, 0, 1, 0x00 },
+ { 0xd008, 0, 8, 0xdf },
+ { 0xd009, 0, 2, 0x02 },
+ { 0xd006, 0, 8, 0x44 },
+ { 0xd007, 0, 2, 0x01 },
+ { 0xd00c, 0, 8, 0x00 },
+ { 0xd00d, 0, 2, 0x02 },
+ { 0xd00a, 0, 8, 0xf6 },
+ { 0xd00b, 0, 2, 0x01 },
+ { 0x9bba, 0, 8, 0xf9 },
+ { 0x9bc8, 0, 8, 0xaa },
+ { 0x9bc3, 0, 8, 0xdf },
+ { 0x9bc4, 0, 8, 0x02 },
+ { 0x9bc5, 0, 8, 0x00 },
+ { 0x9bc6, 0, 8, 0x02 },
+ { 0x9bc9, 0, 8, 0xf0 },
+ { 0xd011, 0, 8, 0x3c },
+ { 0xd012, 0, 2, 0x01 },
+ { 0xd013, 0, 8, 0xf7 },
+ { 0xd014, 0, 2, 0x02 },
+ { 0xd040, 0, 8, 0x0b },
+ { 0xd041, 0, 2, 0x02 },
+ { 0xd042, 0, 8, 0x4d },
+ { 0xd043, 0, 2, 0x00 },
+ { 0xd045, 1, 1, 0x00 },
+ { 0x9bcf, 0, 1, 0x01 },
+ { 0xd045, 2, 1, 0x01 },
+ { 0xd04f, 0, 8, 0x9a },
+ { 0xd050, 0, 1, 0x01 },
+ { 0xd051, 0, 8, 0x5a },
+ { 0xd052, 0, 1, 0x01 },
+ { 0xd053, 0, 8, 0x50 },
+ { 0xd054, 0, 8, 0x46 },
+ { 0x9bd7, 0, 8, 0x0a },
+ { 0x9bd8, 0, 8, 0x14 },
+ { 0x9bd9, 0, 8, 0x08 },
+ { 0x9bd0, 0, 8, 0xa8 },
+ { 0x9be4, 0, 8, 0x7f },
+ { 0x9bbd, 0, 8, 0xa8 },
+ { 0x9be2, 0, 8, 0x20 },
+ { 0x9bee, 0, 1, 0x01 },
+};
+
+#endif /* AF9013_PRIV_H */
diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
new file mode 100644
index 000000000000..8162d939c4b2
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033.c
@@ -0,0 +1,996 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "af9033_priv.h"
+
+struct af9033_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct af9033_config cfg;
+
+ u32 bandwidth_hz;
+ bool ts_mode_parallel;
+ bool ts_mode_serial;
+
+ u32 ber;
+ u32 ucb;
+ unsigned long last_stat_check;
+};
+
+/* write multiple registers */
+static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[3 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = state->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = (reg >> 16) & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = (reg >> 0) & 0xff;
+ memcpy(&buf[3], val, len);
+
+ ret = i2c_transfer(state->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%06x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read multiple registers */
+static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff,
+ (reg >> 0) & 0xff };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf
+ }, {
+ .addr = state->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%06x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+
+/* write single register */
+static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val)
+{
+ return af9033_wr_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val)
+{
+ return af9033_rd_regs(state, reg, val, 1);
+}
+
+/* write single register with mask */
+static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = af9033_rd_regs(state, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return af9033_wr_regs(state, reg, &val, 1);
+}
+
+/* read single register with mask */
+static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val,
+ u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = af9033_rd_regs(state, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+
+ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n",
+ __func__, a, b, x, r, r);
+
+ return r;
+}
+
+static void af9033_release(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+
+ kfree(state);
+}
+
+static int af9033_init(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ const struct reg_val *init;
+ u8 buf[4];
+ u32 adc_cw, clock_cw;
+ struct reg_val_mask tab[] = {
+ { 0x80fb24, 0x00, 0x08 },
+ { 0x80004c, 0x00, 0xff },
+ { 0x00f641, state->cfg.tuner, 0xff },
+ { 0x80f5ca, 0x01, 0x01 },
+ { 0x80f715, 0x01, 0x01 },
+ { 0x00f41f, 0x04, 0x04 },
+ { 0x00f41a, 0x01, 0x01 },
+ { 0x80f731, 0x00, 0x01 },
+ { 0x00d91e, 0x00, 0x01 },
+ { 0x00d919, 0x00, 0x01 },
+ { 0x80f732, 0x00, 0x01 },
+ { 0x00d91f, 0x00, 0x01 },
+ { 0x00d91a, 0x00, 0x01 },
+ { 0x80f730, 0x00, 0x01 },
+ { 0x80f778, 0x00, 0xff },
+ { 0x80f73c, 0x01, 0x01 },
+ { 0x80f776, 0x00, 0x01 },
+ { 0x00d8fd, 0x01, 0xff },
+ { 0x00d830, 0x01, 0xff },
+ { 0x00d831, 0x00, 0xff },
+ { 0x00d832, 0x00, 0xff },
+ { 0x80f985, state->ts_mode_serial, 0x01 },
+ { 0x80f986, state->ts_mode_parallel, 0x01 },
+ { 0x00d827, 0x00, 0xff },
+ { 0x00d829, 0x00, 0xff },
+ };
+
+ /* program clock control */
+ clock_cw = af9033_div(state, state->cfg.clock, 1000000ul, 19ul);
+ buf[0] = (clock_cw >> 0) & 0xff;
+ buf[1] = (clock_cw >> 8) & 0xff;
+ buf[2] = (clock_cw >> 16) & 0xff;
+ buf[3] = (clock_cw >> 24) & 0xff;
+
+ dev_dbg(&state->i2c->dev, "%s: clock=%d clock_cw=%08x\n",
+ __func__, state->cfg.clock, clock_cw);
+
+ ret = af9033_wr_regs(state, 0x800025, buf, 4);
+ if (ret < 0)
+ goto err;
+
+ /* program ADC control */
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == state->cfg.clock)
+ break;
+ }
+
+ adc_cw = af9033_div(state, clock_adc_lut[i].adc, 1000000ul, 19ul);
+ buf[0] = (adc_cw >> 0) & 0xff;
+ buf[1] = (adc_cw >> 8) & 0xff;
+ buf[2] = (adc_cw >> 16) & 0xff;
+
+ dev_dbg(&state->i2c->dev, "%s: adc=%d adc_cw=%06x\n",
+ __func__, clock_adc_lut[i].adc, adc_cw);
+
+ ret = af9033_wr_regs(state, 0x80f1cd, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ /* program register table */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* settings for TS interface */
+ if (state->cfg.ts_mode == AF9033_TS_MODE_USB) {
+ ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+ } else {
+ ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* load OFSM settings */
+ dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9033_wr_reg(state, init[i].reg, init[i].val);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* load tuner specific settings */
+ dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n",
+ __func__);
+ switch (state->cfg.tuner) {
+ case AF9033_TUNER_TUA9001:
+ len = ARRAY_SIZE(tuner_init_tua9001);
+ init = tuner_init_tua9001;
+ break;
+ case AF9033_TUNER_FC0011:
+ len = ARRAY_SIZE(tuner_init_fc0011);
+ init = tuner_init_fc0011;
+ break;
+ case AF9033_TUNER_MXL5007T:
+ len = ARRAY_SIZE(tuner_init_mxl5007t);
+ init = tuner_init_mxl5007t;
+ break;
+ case AF9033_TUNER_TDA18218:
+ len = ARRAY_SIZE(tuner_init_tda18218);
+ init = tuner_init_tda18218;
+ break;
+ case AF9033_TUNER_FC2580:
+ len = ARRAY_SIZE(tuner_init_fc2580);
+ init = tuner_init_fc2580;
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n",
+ __func__, state->cfg.tuner);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9033_wr_reg(state, init[i].reg, init[i].val);
+ if (ret < 0)
+ goto err;
+ }
+
+ state->bandwidth_hz = 0; /* force to program all parameters */
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_sleep(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i;
+ u8 tmp;
+
+ ret = af9033_wr_reg(state, 0x80004c, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800000, 0);
+ if (ret < 0)
+ goto err;
+
+ for (i = 100, tmp = 1; i && tmp; i--) {
+ ret = af9033_rd_reg(state, 0x80004c, &tmp);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(200, 10000);
+ }
+
+ dev_dbg(&state->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08);
+ if (ret < 0)
+ goto err;
+
+ /* prevent current leak (?) */
+ if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+ /* enable parallel TS */
+ ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9033_set_frontend(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, spec_inv;
+ u8 tmp, buf[3], bandwidth_reg_val;
+ u32 if_frequency, freq_cw, adc_freq;
+
+ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
+ __func__, c->frequency, c->bandwidth_hz);
+
+ /* check bandwidth */
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bandwidth_reg_val = 0x00;
+ break;
+ case 7000000:
+ bandwidth_reg_val = 0x01;
+ break;
+ case 8000000:
+ bandwidth_reg_val = 0x02;
+ break;
+ default:
+ dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n",
+ __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* program CFOE coefficients */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
+ if (coeff_lut[i].clock == state->cfg.clock &&
+ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
+ break;
+ }
+ }
+ ret = af9033_wr_regs(state, 0x800001,
+ coeff_lut[i].val, sizeof(coeff_lut[i].val));
+ }
+
+ /* program frequency control */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ spec_inv = state->cfg.spec_inv ? -1 : 1;
+
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == state->cfg.clock)
+ break;
+ }
+ adc_freq = clock_adc_lut[i].adc;
+
+ /* get used IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ if_frequency = 0;
+
+ while (if_frequency > (adc_freq / 2))
+ if_frequency -= adc_freq;
+
+ if (if_frequency >= 0)
+ spec_inv *= -1;
+ else
+ if_frequency *= -1;
+
+ freq_cw = af9033_div(state, if_frequency, adc_freq, 23ul);
+
+ if (spec_inv == -1)
+ freq_cw *= -1;
+
+ /* get adc multiplies */
+ ret = af9033_rd_reg(state, 0x800045, &tmp);
+ if (ret < 0)
+ goto err;
+
+ if (tmp == 1)
+ freq_cw /= 2;
+
+ buf[0] = (freq_cw >> 0) & 0xff;
+ buf[1] = (freq_cw >> 8) & 0xff;
+ buf[2] = (freq_cw >> 16) & 0x7f;
+ ret = af9033_wr_regs(state, 0x800029, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ state->bandwidth_hz = c->bandwidth_hz;
+ }
+
+ ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800040, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800047, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (c->frequency <= 230000000)
+ tmp = 0x00; /* VHF */
+ else
+ tmp = 0x01; /* UHF */
+
+ ret = af9033_wr_reg(state, 0x80004b, tmp);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800000, 0x00);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_get_frontend(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[8];
+
+ dev_dbg(&state->i2c->dev, "%s:\n", __func__);
+
+ /* read all needed registers */
+ ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf));
+ if (ret < 0)
+ goto err;
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[3] >> 0) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[4] >> 0) & 3) {
+ case 0:
+ c->bandwidth_hz = 6000000;
+ break;
+ case 1:
+ c->bandwidth_hz = 7000000;
+ break;
+ case 2:
+ c->bandwidth_hz = 8000000;
+ break;
+ }
+
+ switch ((buf[6] >> 0) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_HP = FEC_NONE;
+ break;
+ }
+
+ switch ((buf[7] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_LP = FEC_NONE;
+ break;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ *status = 0;
+
+ /* radio channel status, 0=no result, 1=has signal, 2=no signal */
+ ret = af9033_rd_reg(state, 0x800047, &tmp);
+ if (ret < 0)
+ goto err;
+
+ /* has signal */
+ if (tmp == 0x01)
+ *status |= FE_HAS_SIGNAL;
+
+ if (tmp != 0x02) {
+ /* TPS lock */
+ ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+
+ /* full lock */
+ ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 buf[3], tmp;
+ u32 snr_val;
+ const struct val_snr *uninitialized_var(snr_lut);
+
+ /* read value */
+ ret = af9033_rd_regs(state, 0x80002c, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ /* read current modulation */
+ ret = af9033_rd_reg(state, 0x80f903, &tmp);
+ if (ret < 0)
+ goto err;
+
+ switch ((tmp >> 0) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_lut);
+ snr_lut = qpsk_snr_lut;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_lut);
+ snr_lut = qam16_snr_lut;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_lut);
+ snr_lut = qam64_snr_lut;
+ break;
+ default:
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ tmp = snr_lut[i].snr;
+
+ if (snr_val < snr_lut[i].val)
+ break;
+ }
+
+ *snr = tmp * 10; /* dB/10 */
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+ u8 strength2;
+
+ /* read signal strength of 0-100 scale */
+ ret = af9033_rd_reg(state, 0x800048, &strength2);
+ if (ret < 0)
+ goto err;
+
+ /* scale value to 0x0000-0xffff */
+ *strength = strength2 * 0xffff / 100;
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_update_ch_stat(struct af9033_state *state)
+{
+ int ret = 0;
+ u32 err_cnt, bit_cnt;
+ u16 abort_cnt;
+ u8 buf[7];
+
+ /* only update data every half second */
+ if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) {
+ ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf));
+ if (ret < 0)
+ goto err;
+ /* in 8 byte packets? */
+ abort_cnt = (buf[1] << 8) + buf[0];
+ /* in bits */
+ err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2];
+ /* in 8 byte packets? always(?) 0x2710 = 10000 */
+ bit_cnt = (buf[6] << 8) + buf[5];
+
+ if (bit_cnt < abort_cnt) {
+ abort_cnt = 1000;
+ state->ber = 0xffffffff;
+ } else {
+ /* 8 byte packets, that have not been rejected already */
+ bit_cnt -= (u32)abort_cnt;
+ if (bit_cnt == 0) {
+ state->ber = 0xffffffff;
+ } else {
+ err_cnt -= (u32)abort_cnt * 8 * 8;
+ bit_cnt *= 8 * 8;
+ state->ber = err_cnt * (0xffffffff / bit_cnt);
+ }
+ }
+ state->ucb += abort_cnt;
+ state->last_stat_check = jiffies;
+ }
+
+ return 0;
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ ret = af9033_update_ch_stat(state);
+ if (ret < 0)
+ return ret;
+
+ *ber = state->ber;
+
+ return 0;
+}
+
+static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ ret = af9033_update_ch_stat(state);
+ if (ret < 0)
+ return ret;
+
+ *ucblocks = state->ucb;
+
+ return 0;
+}
+
+static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable);
+
+ ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static struct dvb_frontend_ops af9033_ops;
+
+struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9033_state *state;
+ u8 buf[8];
+
+ dev_dbg(&i2c->dev, "%s:\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL);
+ if (state == NULL)
+ goto err;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->cfg, config, sizeof(struct af9033_config));
+
+ if (state->cfg.clock != 12000000) {
+ dev_err(&state->i2c->dev, "%s: af9033: unsupported clock=%d, " \
+ "only 12000000 Hz is supported currently\n",
+ KBUILD_MODNAME, state->cfg.clock);
+ goto err;
+ }
+
+ /* firmware version */
+ ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_rd_regs(state, 0x804191, &buf[4], 4);
+ if (ret < 0)
+ goto err;
+
+ dev_info(&state->i2c->dev, "%s: firmware version: LINK=%d.%d.%d.%d " \
+ "OFDM=%d.%d.%d.%d\n", KBUILD_MODNAME, buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+
+ /* sleep */
+ ret = af9033_wr_reg(state, 0x80004c, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800000, 0);
+ if (ret < 0)
+ goto err;
+
+ /* configure internal TS mode */
+ switch (state->cfg.ts_mode) {
+ case AF9033_TS_MODE_PARALLEL:
+ state->ts_mode_parallel = true;
+ break;
+ case AF9033_TS_MODE_SERIAL:
+ state->ts_mode_serial = true;
+ break;
+ case AF9033_TS_MODE_USB:
+ /* usb mode for AF9035 */
+ default:
+ break;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
+ state->fe.demodulator_priv = state;
+
+ return &state->fe;
+
+err:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9033_attach);
+
+static struct dvb_frontend_ops af9033_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Afatech AF9033 (DVB-T)",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = af9033_release,
+
+ .init = af9033_init,
+ .sleep = af9033_sleep,
+
+ .get_tune_settings = af9033_get_tune_settings,
+ .set_frontend = af9033_set_frontend,
+ .get_frontend = af9033_get_frontend,
+
+ .read_status = af9033_read_status,
+ .read_snr = af9033_read_snr,
+ .read_signal_strength = af9033_read_signal_strength,
+ .read_ber = af9033_read_ber,
+ .read_ucblocks = af9033_read_ucblocks,
+
+ .i2c_gate_ctrl = af9033_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h
new file mode 100644
index 000000000000..bfa4313fde21
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033.h
@@ -0,0 +1,76 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9033_H
+#define AF9033_H
+
+struct af9033_config {
+ /*
+ * I2C address
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock Hz
+ * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000,
+ * 30000000, 36000000, 20480000, 16384000
+ */
+ u32 clock;
+
+ /*
+ * tuner
+ */
+#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */
+#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */
+#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */
+#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */
+#define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */
+ u8 tuner;
+
+ /*
+ * TS settings
+ */
+#define AF9033_TS_MODE_USB 0
+#define AF9033_TS_MODE_PARALLEL 1
+#define AF9033_TS_MODE_SERIAL 2
+ u8 ts_mode:2;
+
+ /*
+ * input spectrum inversion
+ */
+ bool spec_inv;
+};
+
+
+#if defined(CONFIG_DVB_AF9033) || \
+ (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9033_attach(
+ const struct af9033_config *config, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* AF9033_H */
diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h
new file mode 100644
index 000000000000..34dddcd77538
--- /dev/null
+++ b/drivers/media/dvb-frontends/af9033_priv.h
@@ -0,0 +1,507 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9033_PRIV_H
+#define AF9033_PRIV_H
+
+#include "dvb_frontend.h"
+#include "af9033.h"
+
+struct reg_val {
+ u32 reg;
+ u8 val;
+};
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct coeff {
+ u32 clock;
+ u32 bandwidth_hz;
+ u8 val[36];
+};
+
+struct clock_adc {
+ u32 clock;
+ u32 adc;
+};
+
+struct val_snr {
+ u32 val;
+ u8 snr;
+};
+
+/* Xtal clock vs. ADC clock lookup table */
+static const struct clock_adc clock_adc_lut[] = {
+ { 16384000, 20480000 },
+ { 20480000, 20480000 },
+ { 36000000, 20250000 },
+ { 30000000, 20156250 },
+ { 26000000, 20583333 },
+ { 28000000, 20416667 },
+ { 32000000, 20500000 },
+ { 34000000, 20187500 },
+ { 24000000, 20500000 },
+ { 22000000, 20625000 },
+ { 12000000, 20250000 },
+};
+
+/* pre-calculated coeff lookup table */
+static const struct coeff coeff_lut[] = {
+ /* 12.000 MHz */
+ { 12000000, 8000000, {
+ 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5,
+ 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 }
+ },
+ { 12000000, 7000000, {
+ 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b,
+ 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 }
+ },
+ { 12000000, 6000000, {
+ 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60,
+ 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 }
+ },
+};
+
+/* QPSK SNR lookup table */
+static const struct val_snr qpsk_snr_lut[] = {
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0x17a532, 15 },
+ { 0x180f94, 16 },
+ { 0x186ed2, 17 },
+ { 0x18b271, 18 },
+ { 0x18e118, 19 },
+ { 0x18ff4b, 20 },
+ { 0x190af1, 21 },
+ { 0x191451, 22 },
+ { 0xffffff, 23 },
+};
+
+/* QAM16 SNR lookup table */
+static const struct val_snr qam16_snr_lut[] = {
+ { 0x04f0d5, 0 },
+ { 0x05387a, 1 },
+ { 0x0573a4, 2 },
+ { 0x05a99e, 3 },
+ { 0x05cc80, 4 },
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0x0bda6a, 20 },
+ { 0x0c0c75, 21 },
+ { 0x0c3f7d, 22 },
+ { 0x0c5e62, 23 },
+ { 0x0c6c31, 24 },
+ { 0x0c7925, 25 },
+ { 0xffffff, 26 },
+};
+
+/* QAM64 SNR lookup table */
+static const struct val_snr qam64_snr_lut[] = {
+ { 0x0256d0, 0 },
+ { 0x027a65, 1 },
+ { 0x029873, 2 },
+ { 0x02b7fe, 3 },
+ { 0x02cf1e, 4 },
+ { 0x02e234, 5 },
+ { 0x02f409, 6 },
+ { 0x030046, 7 },
+ { 0x030844, 8 },
+ { 0x030a02, 9 },
+ { 0x030cde, 10 },
+ { 0x031031, 11 },
+ { 0x03144c, 12 },
+ { 0x0315dd, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0x059feb, 27 },
+ { 0x05bf38, 28 },
+ { 0xffffff, 29 },
+};
+
+static const struct reg_val ofsm_init[] = {
+ { 0x800051, 0x01 },
+ { 0x800070, 0x0a },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800099, 0x01 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000ab, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000c0, 0x05 },
+ { 0x8000c4, 0x19 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/* Infineon TUA 9001 tuner init
+ AF9033_TUNER_TUA9001 = 0x27 */
+static const struct reg_val tuner_init_tua9001[] = {
+ { 0x800046, 0x27 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x80006d, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800075, 0x03 },
+ { 0x800076, 0x02 },
+ { 0x800077, 0x00 },
+ { 0x800078, 0x01 },
+ { 0x800079, 0x00 },
+ { 0x80007a, 0x7e },
+ { 0x80007b, 0x3e },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x01 },
+ { 0x800095, 0x02 },
+ { 0x800096, 0x01 },
+ { 0x800098, 0x0a },
+ { 0x80009b, 0x05 },
+ { 0x80009c, 0x80 },
+ { 0x8000b3, 0x00 },
+ { 0x8000c1, 0x01 },
+ { 0x8000c2, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f047, 0x00 },
+ { 0x80f054, 0x00 },
+ { 0x80f055, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x00 },
+};
+
+/* Fitipower fc0011 tuner init
+ AF9033_TUNER_FC0011 = 0x28 */
+static const struct reg_val tuner_init_fc0011[] = {
+ { 0x800046, AF9033_TUNER_FC0011 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0xa5 },
+ { 0x80006e, 0x01 },
+ { 0x800071, 0x0A },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x80009b, 0x2D },
+ { 0x80009c, 0x60 },
+ { 0x80009d, 0x23 },
+ { 0x8000a4, 0x50 },
+ { 0x8000ad, 0x50 },
+ { 0x8000b3, 0x01 },
+ { 0x8000b7, 0x88 },
+ { 0x8000b8, 0xa6 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x01 },
+ { 0x8000c7, 0x69 },
+ { 0x80F007, 0x00 },
+ { 0x80F00A, 0x1B },
+ { 0x80F00B, 0x1B },
+ { 0x80F00C, 0x1B },
+ { 0x80F00D, 0x1B },
+ { 0x80F00E, 0xFF },
+ { 0x80F00F, 0x01 },
+ { 0x80F010, 0x00 },
+ { 0x80F011, 0x02 },
+ { 0x80F012, 0xFF },
+ { 0x80F013, 0x01 },
+ { 0x80F014, 0x00 },
+ { 0x80F015, 0x02 },
+ { 0x80F01B, 0xEF },
+ { 0x80F01C, 0x01 },
+ { 0x80F01D, 0x0f },
+ { 0x80F01E, 0x02 },
+ { 0x80F01F, 0x6E },
+ { 0x80F020, 0x00 },
+ { 0x80F025, 0xDE },
+ { 0x80F026, 0x00 },
+ { 0x80F027, 0x0A },
+ { 0x80F028, 0x03 },
+ { 0x80F029, 0x6E },
+ { 0x80F02A, 0x00 },
+ { 0x80F047, 0x00 },
+ { 0x80F054, 0x00 },
+ { 0x80F055, 0x00 },
+ { 0x80F077, 0x01 },
+ { 0x80F1E6, 0x00 },
+};
+
+/* MaxLinear MxL5007T tuner init
+ AF9033_TUNER_MXL5007T = 0xa0 */
+static const struct reg_val tuner_init_mxl5007t[] = {
+ { 0x800046, 0x1b },
+ { 0x800057, 0x01 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x96 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x8000b3, 0x01 },
+ { 0x8000c1, 0x01 },
+ { 0x8000c2, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f00c, 0x19 },
+ { 0x80f00d, 0x1a },
+ { 0x80f012, 0xda },
+ { 0x80f013, 0x00 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f077, 0x02 },
+ { 0x80f1e6, 0x00 },
+};
+
+/* NXP TDA 18218HN tuner init
+ AF9033_TUNER_TDA18218 = 0xa1 */
+static const struct reg_val tuner_init_tda18218[] = {
+ {0x800046, 0xa1},
+ {0x800057, 0x01},
+ {0x800058, 0x01},
+ {0x80005f, 0x00},
+ {0x800060, 0x00},
+ {0x800071, 0x05},
+ {0x800072, 0x02},
+ {0x800074, 0x01},
+ {0x800079, 0x01},
+ {0x800093, 0x00},
+ {0x800094, 0x00},
+ {0x800095, 0x00},
+ {0x800096, 0x00},
+ {0x8000b3, 0x01},
+ {0x8000c3, 0x01},
+ {0x8000c4, 0x00},
+ {0x80f007, 0x00},
+ {0x80f00c, 0x19},
+ {0x80f00d, 0x1a},
+ {0x80f012, 0xda},
+ {0x80f013, 0x00},
+ {0x80f014, 0x00},
+ {0x80f015, 0x02},
+ {0x80f01f, 0x82},
+ {0x80f020, 0x00},
+ {0x80f029, 0x82},
+ {0x80f02a, 0x00},
+ {0x80f077, 0x02},
+ {0x80f1e6, 0x00},
+};
+
+/* FCI FC2580 tuner init */
+static const struct reg_val tuner_init_fc2580[] = {
+ { 0x800046, 0x32 },
+ { 0x800057, 0x01 },
+ { 0x800058, 0x00 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x05 },
+ { 0x8000b3, 0x01 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f00c, 0x19 },
+ { 0x80f00d, 0x1A },
+ { 0x80f00e, 0x00 },
+ { 0x80f00f, 0x02 },
+ { 0x80f010, 0x00 },
+ { 0x80f011, 0x02 },
+ { 0x80f012, 0x00 },
+ { 0x80f013, 0x02 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01f, 0x96 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x96 },
+ { 0x80f02a, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x01 },
+};
+
+#endif /* AF9033_PRIV_H */
+
diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c
new file mode 100644
index 000000000000..4e11dc4b1335
--- /dev/null
+++ b/drivers/media/dvb-frontends/atbm8830.c
@@ -0,0 +1,508 @@
+/*
+ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
+ * ATBM8830, ATBM8831
+ *
+ * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/div64.h>
+#include "dvb_frontend.h"
+
+#include "atbm8830.h"
+#include "atbm8830_priv.h"
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "atbm8830: " args); \
+ } while (0)
+
+static int debug;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data)
+{
+ int ret = 0;
+ u8 dev_addr;
+ u8 buf1[] = { reg >> 8, reg & 0xFF };
+ u8 buf2[] = { data };
+ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+ struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 };
+
+ dev_addr = priv->config->demod_address;
+ msg1.addr = dev_addr;
+ msg2.addr = dev_addr;
+
+ if (debug >= 2)
+ dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data);
+
+ ret = i2c_transfer(priv->i2c, &msg1, 1);
+ if (ret != 1)
+ return -EIO;
+
+ ret = i2c_transfer(priv->i2c, &msg2, 1);
+ return (ret != 1) ? -EIO : 0;
+}
+
+static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data)
+{
+ int ret;
+ u8 dev_addr;
+
+ u8 buf1[] = { reg >> 8, reg & 0xFF };
+ u8 buf2[] = { 0 };
+ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
+ struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 };
+
+ dev_addr = priv->config->demod_address;
+ msg1.addr = dev_addr;
+ msg2.addr = dev_addr;
+
+ ret = i2c_transfer(priv->i2c, &msg1, 1);
+ if (ret != 1) {
+ dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret);
+ return -EIO;
+ }
+
+ ret = i2c_transfer(priv->i2c, &msg2, 1);
+ if (ret != 1)
+ return -EIO;
+
+ *p_data = buf2[0];
+ if (debug >= 2)
+ dprintk("%s: reg=0x%04X, data=0x%02X\n",
+ __func__, reg, buf2[0]);
+
+ return 0;
+}
+
+/* Lock register latch so that multi-register read is atomic */
+static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock)
+{
+ return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0);
+}
+
+static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+ u32 val;
+ u64 t;
+
+ /* 0x100000 * freq / 30.4MHz */
+ t = (u64)0x100000 * freq;
+ do_div(t, 30400);
+ val = t;
+
+ atbm8830_write_reg(priv, REG_OSC_CLK, val);
+ atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8);
+ atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16);
+
+ return 0;
+}
+
+static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
+{
+
+ u32 fs = priv->config->osc_clk_freq;
+ u64 t;
+ u32 val;
+ u8 dat;
+
+ if (freq != 0) {
+ /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */
+ t = (u64) 2 * 31416 * (freq - fs);
+ t <<= 22;
+ do_div(t, fs);
+ do_div(t, 1000);
+ val = t;
+
+ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1);
+ atbm8830_write_reg(priv, REG_IF_FREQ, val);
+ atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8);
+ atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16);
+
+ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+ dat &= 0xFC;
+ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+ } else {
+ /* Zero IF */
+ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0);
+
+ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
+ dat &= 0xFC;
+ dat |= 0x02;
+ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
+
+ if (priv->config->zif_swap_iq)
+ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03);
+ else
+ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01);
+ }
+
+ return 0;
+}
+
+static int is_locked(struct atbm_state *priv, u8 *locked)
+{
+ u8 status;
+
+ atbm8830_read_reg(priv, REG_LOCK_STATUS, &status);
+
+ if (locked != NULL)
+ *locked = (status == 1);
+ return 0;
+}
+
+static int set_agc_config(struct atbm_state *priv,
+ u8 min, u8 max, u8 hold_loop)
+{
+ /* no effect if both min and max are zero */
+ if (!min && !max)
+ return 0;
+
+ atbm8830_write_reg(priv, REG_AGC_MIN, min);
+ atbm8830_write_reg(priv, REG_AGC_MAX, max);
+ atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop);
+
+ return 0;
+}
+
+static int set_static_channel_mode(struct atbm_state *priv)
+{
+ int i;
+
+ for (i = 0; i < 5; i++)
+ atbm8830_write_reg(priv, 0x099B + i, 0x08);
+
+ atbm8830_write_reg(priv, 0x095B, 0x7F);
+ atbm8830_write_reg(priv, 0x09CB, 0x01);
+ atbm8830_write_reg(priv, 0x09CC, 0x7F);
+ atbm8830_write_reg(priv, 0x09CD, 0x7F);
+ atbm8830_write_reg(priv, 0x0E01, 0x20);
+
+ /* For single carrier */
+ atbm8830_write_reg(priv, 0x0B03, 0x0A);
+ atbm8830_write_reg(priv, 0x0935, 0x10);
+ atbm8830_write_reg(priv, 0x0936, 0x08);
+ atbm8830_write_reg(priv, 0x093E, 0x08);
+ atbm8830_write_reg(priv, 0x096E, 0x06);
+
+ /* frame_count_max0 */
+ atbm8830_write_reg(priv, 0x0B09, 0x00);
+ /* frame_count_max1 */
+ atbm8830_write_reg(priv, 0x0B0A, 0x08);
+
+ return 0;
+}
+
+static int set_ts_config(struct atbm_state *priv)
+{
+ const struct atbm8830_config *cfg = priv->config;
+
+ /*Set parallel/serial ts mode*/
+ atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0);
+ atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0);
+ /*Set ts sampling edge*/
+ atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE,
+ cfg->ts_sampling_edge ? 1 : 0);
+ /*Set ts clock freerun*/
+ atbm8830_write_reg(priv, REG_TS_CLK_FREERUN,
+ cfg->ts_clk_gated ? 0 : 1);
+
+ return 0;
+}
+
+static int atbm8830_init(struct dvb_frontend *fe)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ const struct atbm8830_config *cfg = priv->config;
+
+ /*Set oscillator frequency*/
+ set_osc_freq(priv, cfg->osc_clk_freq);
+
+ /*Set IF frequency*/
+ set_if_freq(priv, cfg->if_freq);
+
+ /*Set AGC Config*/
+ set_agc_config(priv, cfg->agc_min, cfg->agc_max,
+ cfg->agc_hold_loop);
+
+ /*Set static channel mode*/
+ set_static_channel_mode(priv);
+
+ set_ts_config(priv);
+ /*Turn off DSP reset*/
+ atbm8830_write_reg(priv, 0x000A, 0);
+
+ /*SW version test*/
+ atbm8830_write_reg(priv, 0x020C, 11);
+
+ /* Run */
+ atbm8830_write_reg(priv, REG_DEMOD_RUN, 1);
+
+ return 0;
+}
+
+
+static void atbm8830_release(struct dvb_frontend *fe)
+{
+ struct atbm_state *state = fe->demodulator_priv;
+ dprintk("%s\n", __func__);
+
+ kfree(state);
+}
+
+static int atbm8830_set_fe(struct dvb_frontend *fe)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ int i;
+ u8 locked = 0;
+ dprintk("%s\n", __func__);
+
+ /* set frequency */
+ 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);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* start auto lock */
+ for (i = 0; i < 10; i++) {
+ mdelay(100);
+ dprintk("Try %d\n", i);
+ is_locked(priv, &locked);
+ if (locked != 0) {
+ dprintk("ATBM8830 locked!\n");
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int atbm8830_get_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ dprintk("%s\n", __func__);
+
+ /* TODO: get real readings from device */
+ /* inversion status */
+ c->inversion = INVERSION_OFF;
+
+ /* bandwidth */
+ c->bandwidth_hz = 8000000;
+
+ c->code_rate_HP = FEC_AUTO;
+ c->code_rate_LP = FEC_AUTO;
+
+ c->modulation = QAM_AUTO;
+
+ /* transmission mode */
+ c->transmission_mode = TRANSMISSION_MODE_AUTO;
+
+ /* guard interval */
+ c->guard_interval = GUARD_INTERVAL_AUTO;
+
+ /* hierarchy */
+ c->hierarchy = HIERARCHY_NONE;
+
+ return 0;
+}
+
+static int atbm8830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 0;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u8 locked = 0;
+ u8 agc_locked = 0;
+
+ dprintk("%s\n", __func__);
+ *fe_status = 0;
+
+ is_locked(priv, &locked);
+ if (locked) {
+ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
+
+ atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked);
+ dprintk("AGC Lock: %d\n", agc_locked);
+
+ return 0;
+}
+
+static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u32 frame_err;
+ u8 t;
+
+ dprintk("%s\n", __func__);
+
+ atbm8830_reglatch_lock(priv, 1);
+
+ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t);
+ frame_err = t & 0x7F;
+ frame_err <<= 8;
+ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t);
+ frame_err |= t;
+
+ atbm8830_reglatch_lock(priv, 0);
+
+ *ber = frame_err * 100 / 32767;
+
+ dprintk("%s: ber=0x%x\n", __func__, *ber);
+ return 0;
+}
+
+static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+ u32 pwm;
+ u8 t;
+
+ dprintk("%s\n", __func__);
+ atbm8830_reglatch_lock(priv, 1);
+
+ atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t);
+ pwm = t & 0x03;
+ pwm <<= 8;
+ atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t);
+ pwm |= t;
+
+ atbm8830_reglatch_lock(priv, 0);
+
+ dprintk("AGC PWM = 0x%02X\n", pwm);
+ pwm = 0x400 - pwm;
+
+ *signal = pwm * 0x10000 / 0x400;
+
+ return 0;
+}
+
+static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ dprintk("%s\n", __func__);
+ *snr = 0;
+ return 0;
+}
+
+static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ dprintk("%s\n", __func__);
+ *ucblocks = 0;
+ return 0;
+}
+
+static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct atbm_state *priv = fe->demodulator_priv;
+
+ return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
+}
+
+static struct dvb_frontend_ops atbm8830_ops = {
+ .delsys = { SYS_DTMB },
+ .info = {
+ .name = "AltoBeam ATBM8830/8831 DMB-TH",
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .caps =
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = atbm8830_release,
+
+ .init = atbm8830_init,
+ .sleep = NULL,
+ .write = NULL,
+ .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl,
+
+ .set_frontend = atbm8830_set_fe,
+ .get_frontend = atbm8830_get_fe,
+ .get_tune_settings = atbm8830_get_tune_settings,
+
+ .read_status = atbm8830_read_status,
+ .read_ber = atbm8830_read_ber,
+ .read_signal_strength = atbm8830_read_signal_strength,
+ .read_snr = atbm8830_read_snr,
+ .read_ucblocks = atbm8830_read_ucblocks,
+};
+
+struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct atbm_state *priv = NULL;
+ u8 data = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (config == NULL || i2c == NULL)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL);
+ if (priv == NULL)
+ goto error_out;
+
+ priv->config = config;
+ priv->i2c = i2c;
+
+ /* check if the demod is there */
+ if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) {
+ dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n",
+ __func__, priv->config->demod_address);
+ goto error_out;
+ }
+ dprintk("atbm8830 chip id: 0x%02X\n", data);
+
+ memcpy(&priv->frontend.ops, &atbm8830_ops,
+ sizeof(struct dvb_frontend_ops));
+ priv->frontend.demodulator_priv = priv;
+
+ atbm8830_init(&priv->frontend);
+
+ atbm8830_i2c_gate_ctrl(&priv->frontend, 1);
+
+ return &priv->frontend;
+
+error_out:
+ dprintk("%s() error_out\n", __func__);
+ kfree(priv);
+ return NULL;
+
+}
+EXPORT_SYMBOL(atbm8830_attach);
+
+MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h
index 024273374bd8..024273374bd8 100644
--- a/drivers/media/dvb/frontends/atbm8830.h
+++ b/drivers/media/dvb-frontends/atbm8830.h
diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb-frontends/atbm8830_priv.h
index d460058d497e..d460058d497e 100644
--- a/drivers/media/dvb/frontends/atbm8830_priv.h
+++ b/drivers/media/dvb-frontends/atbm8830_priv.h
diff --git a/drivers/media/dvb/frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h
index 565dcf31af57..565dcf31af57 100644
--- a/drivers/media/dvb/frontends/au8522.h
+++ b/drivers/media/dvb-frontends/au8522.h
diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c
new file mode 100644
index 000000000000..3559ff230045
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_common.c
@@ -0,0 +1,277 @@
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+ Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
+ Copyright (C) 2005-2008 Auvitek International, Ltd.
+ Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+#include "au8522_priv.h"
+
+static int debug;
+
+#define dprintk(arg...)\
+ do { if (debug)\
+ printk(arg);\
+ } while (0)
+
+/* Despite the name "hybrid_tuner", the framework works just as well for
+ hybrid demodulators as well... */
+static LIST_HEAD(hybrid_tuner_instance_list);
+static DEFINE_MUTEX(au8522_list_mutex);
+
+/* 16 bit registers, 8 bit values */
+int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
+
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 3 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+EXPORT_SYMBOL(au8522_writereg);
+
+u8 au8522_readreg(struct au8522_state *state, u16 reg)
+{
+ int ret;
+ u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
+ 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 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ printk(KERN_ERR "%s: readreg error (ret == %i)\n",
+ __func__, ret);
+ return b1[0];
+}
+EXPORT_SYMBOL(au8522_readreg);
+
+int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ dprintk("%s(%d)\n", __func__, enable);
+
+ if (state->operational_mode == AU8522_ANALOG_MODE) {
+ /* We're being asked to manage the gate even though we're
+ not in digital mode. This can occur if we get switched
+ over to analog mode before the dvb_frontend kernel thread
+ has completely shutdown */
+ return 0;
+ }
+
+ if (enable)
+ return au8522_writereg(state, 0x106, 1);
+ else
+ return au8522_writereg(state, 0x106, 0);
+}
+EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
+
+int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ dprintk("%s(%d)\n", __func__, enable);
+
+ if (enable)
+ return au8522_writereg(state, 0x106, 1);
+ else
+ return au8522_writereg(state, 0x106, 0);
+}
+EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl);
+
+/* Reset the demod hardware and reset all of the configuration registers
+ to a default state. */
+int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
+ u8 client_address)
+{
+ int ret;
+
+ mutex_lock(&au8522_list_mutex);
+ ret = hybrid_tuner_request_state(struct au8522_state, (*state),
+ hybrid_tuner_instance_list,
+ i2c, client_address, "au8522");
+ mutex_unlock(&au8522_list_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(au8522_get_state);
+
+void au8522_release_state(struct au8522_state *state)
+{
+ mutex_lock(&au8522_list_mutex);
+ if (state != NULL)
+ hybrid_tuner_release_state(state);
+ mutex_unlock(&au8522_list_mutex);
+}
+EXPORT_SYMBOL(au8522_release_state);
+
+static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ u8 val;
+
+ /* bail out if we can't control an LED */
+ if (!led_config || !led_config->gpio_output ||
+ !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+ return 0;
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_output & ~0xc000));
+ if (onoff) {
+ /* enable GPIO output */
+ val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+ val |= (led_config->gpio_output_enable & 0xff);
+ } else {
+ /* disable GPIO output */
+ val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+ val |= (led_config->gpio_output_disable & 0xff);
+ }
+ return au8522_writereg(state, 0x8000 |
+ (led_config->gpio_output & ~0xc000), val);
+}
+
+/* led = 0 | off
+ * led = 1 | signal ok
+ * led = 2 | signal strong
+ * led < 0 | only light led if leds are currently off
+ */
+int au8522_led_ctrl(struct au8522_state *state, int led)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ int i, ret = 0;
+
+ /* bail out if we can't control an LED */
+ if (!led_config || !led_config->gpio_leds ||
+ !led_config->num_led_states || !led_config->led_states)
+ return 0;
+
+ if (led < 0) {
+ /* if LED is already lit, then leave it as-is */
+ if (state->led_state)
+ return 0;
+ else
+ led *= -1;
+ }
+
+ /* toggle LED if changing state */
+ if (state->led_state != led) {
+ u8 val;
+
+ dprintk("%s: %d\n", __func__, led);
+
+ au8522_led_gpio_enable(state, 1);
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_leds & ~0xc000));
+
+ /* start with all leds off */
+ for (i = 0; i < led_config->num_led_states; i++)
+ val &= ~led_config->led_states[i];
+
+ /* set selected LED state */
+ if (led < led_config->num_led_states)
+ val |= led_config->led_states[led];
+ else if (led_config->num_led_states)
+ val |=
+ led_config->led_states[led_config->num_led_states - 1];
+
+ ret = au8522_writereg(state, 0x8000 |
+ (led_config->gpio_leds & ~0xc000), val);
+ if (ret < 0)
+ return ret;
+
+ state->led_state = led;
+
+ if (led == 0)
+ au8522_led_gpio_enable(state, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_led_ctrl);
+
+int au8522_init(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ dprintk("%s()\n", __func__);
+
+ state->operational_mode = AU8522_DIGITAL_MODE;
+
+ /* Clear out any state associated with the digital side of the
+ chip, so that when it gets powered back up it won't think
+ that it is already tuned */
+ state->current_frequency = 0;
+
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ au8522_i2c_gate_ctrl(fe, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_init);
+
+int au8522_sleep(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ dprintk("%s()\n", __func__);
+
+ /* Only power down if the digital side is currently using the chip */
+ if (state->operational_mode == AU8522_ANALOG_MODE) {
+ /* We're not in one of the expected power modes, which means
+ that the DVB thread is probably telling us to go to sleep
+ even though the analog frontend has already started using
+ the chip. So ignore the request */
+ return 0;
+ }
+
+ /* turn off led */
+ au8522_led_ctrl(state, 0);
+
+ /* Power down the chip */
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ state->current_frequency = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_sleep);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
new file mode 100644
index 000000000000..5243ba6295cc
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -0,0 +1,839 @@
+/*
+ * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder
+ *
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
+ * Copyright (C) 2005-2008 Auvitek International, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * As published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ */
+
+/* Developer notes:
+ *
+ * VBI support is not yet working
+ * Enough is implemented here for CVBS and S-Video inputs, but the actual
+ * analog demodulator code isn't implemented (not needed for xc5000 since it
+ * has its own demodulator and outputs CVBS)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-device.h>
+#include "au8522.h"
+#include "au8522_priv.h"
+
+MODULE_AUTHOR("Devin Heitmueller");
+MODULE_LICENSE("GPL");
+
+static int au8522_analog_debug;
+
+
+module_param_named(analog_debug, au8522_analog_debug, int, 0644);
+
+MODULE_PARM_DESC(analog_debug,
+ "Analog debugging messages [0=Off (default) 1=On]");
+
+struct au8522_register_config {
+ u16 reg_name;
+ u8 reg_val[8];
+};
+
+
+/* Video Decoder Filter Coefficients
+ The values are as follows from left to right
+ 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"
+*/
+static const struct au8522_register_config filter_coef[] = {
+ {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} },
+ {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} },
+ {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} },
+ {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} },
+ {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} },
+ {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} },
+ {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} },
+ {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} },
+ {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} },
+ {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} },
+ {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} },
+ {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} },
+ {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} },
+ {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} },
+ {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} },
+ {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} },
+ {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} },
+ {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} },
+ {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} },
+ {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} },
+ {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} },
+ {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} },
+
+};
+#define NUM_FILTER_COEF (sizeof(filter_coef)\
+ / sizeof(struct au8522_register_config))
+
+
+/* Registers 0x060b through 0x0652 are the LP Filter coefficients
+ The values are as follows from left to right
+ 0="SIF" 1="ATVRF/ATVRF13"
+ Note: the "ATVRF/ATVRF13" mode has never been tested
+*/
+static const struct au8522_register_config lpfilter_coef[] = {
+ {0x060b, {0x21, 0x0b} },
+ {0x060c, {0xad, 0xad} },
+ {0x060d, {0x70, 0xf0} },
+ {0x060e, {0xea, 0xe9} },
+ {0x060f, {0xdd, 0xdd} },
+ {0x0610, {0x08, 0x64} },
+ {0x0611, {0x60, 0x60} },
+ {0x0612, {0xf8, 0xb2} },
+ {0x0613, {0x01, 0x02} },
+ {0x0614, {0xe4, 0xb4} },
+ {0x0615, {0x19, 0x02} },
+ {0x0616, {0xae, 0x2e} },
+ {0x0617, {0xee, 0xc5} },
+ {0x0618, {0x56, 0x56} },
+ {0x0619, {0x30, 0x58} },
+ {0x061a, {0xf9, 0xf8} },
+ {0x061b, {0x24, 0x64} },
+ {0x061c, {0x07, 0x07} },
+ {0x061d, {0x30, 0x30} },
+ {0x061e, {0xa9, 0xed} },
+ {0x061f, {0x09, 0x0b} },
+ {0x0620, {0x42, 0xc2} },
+ {0x0621, {0x1d, 0x2a} },
+ {0x0622, {0xd6, 0x56} },
+ {0x0623, {0x95, 0x8b} },
+ {0x0624, {0x2b, 0x2b} },
+ {0x0625, {0x30, 0x24} },
+ {0x0626, {0x3e, 0x3e} },
+ {0x0627, {0x62, 0xe2} },
+ {0x0628, {0xe9, 0xf5} },
+ {0x0629, {0x99, 0x19} },
+ {0x062a, {0xd4, 0x11} },
+ {0x062b, {0x03, 0x04} },
+ {0x062c, {0xb5, 0x85} },
+ {0x062d, {0x1e, 0x20} },
+ {0x062e, {0x2a, 0xea} },
+ {0x062f, {0xd7, 0xd2} },
+ {0x0630, {0x15, 0x15} },
+ {0x0631, {0xa3, 0xa9} },
+ {0x0632, {0x1f, 0x1f} },
+ {0x0633, {0xf9, 0xd1} },
+ {0x0634, {0xc0, 0xc3} },
+ {0x0635, {0x4d, 0x8d} },
+ {0x0636, {0x21, 0x31} },
+ {0x0637, {0x83, 0x83} },
+ {0x0638, {0x08, 0x8c} },
+ {0x0639, {0x19, 0x19} },
+ {0x063a, {0x45, 0xa5} },
+ {0x063b, {0xef, 0xec} },
+ {0x063c, {0x8a, 0x8a} },
+ {0x063d, {0xf4, 0xf6} },
+ {0x063e, {0x8f, 0x8f} },
+ {0x063f, {0x44, 0x0c} },
+ {0x0640, {0xef, 0xf0} },
+ {0x0641, {0x66, 0x66} },
+ {0x0642, {0xcc, 0xd2} },
+ {0x0643, {0x41, 0x41} },
+ {0x0644, {0x63, 0x93} },
+ {0x0645, {0x8e, 0x8e} },
+ {0x0646, {0xa2, 0x42} },
+ {0x0647, {0x7b, 0x7b} },
+ {0x0648, {0x04, 0x04} },
+ {0x0649, {0x00, 0x00} },
+ {0x064a, {0x40, 0x40} },
+ {0x064b, {0x8c, 0x98} },
+ {0x064c, {0x00, 0x00} },
+ {0x064d, {0x63, 0xc3} },
+ {0x064e, {0x04, 0x04} },
+ {0x064f, {0x20, 0x20} },
+ {0x0650, {0x00, 0x00} },
+ {0x0651, {0x40, 0x40} },
+ {0x0652, {0x01, 0x01} },
+};
+#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\
+ / sizeof(struct au8522_register_config))
+
+static inline struct au8522_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct au8522_state, sd);
+}
+
+static void setup_vbi(struct au8522_state *state, int aud_input)
+{
+ int i;
+
+ /* These are set to zero regardless of what mode we're in */
+ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H,
+ 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H,
+ 0x00);
+ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H,
+ 0x00);
+
+ /* Setup the VBI registers */
+ for (i = 0x30; i < 0x60; i++)
+ au8522_writereg(state, i, 0x40);
+
+ /* For some reason, every register is 0x40 except register 0x44
+ (confirmed via the HVR-950q USB capture) */
+ au8522_writereg(state, 0x44, 0x60);
+
+ /* Enable VBI (we always do this regardless of whether the user is
+ viewing closed caption info) */
+ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H,
+ AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON);
+
+}
+
+static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
+{
+ int i;
+ int filter_coef_type;
+
+ /* Provide reasonable defaults for picture tuning values */
+ au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07);
+ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed);
+ state->brightness = 0xed - 128;
+ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79);
+ state->contrast = 0x79;
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
+ state->saturation = 0x80;
+ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
+ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
+ state->hue = 0x00;
+
+ /* Other decoder registers */
+ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
+
+ if (input_mode == 0x23) {
+ /* S-Video input mapping */
+ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04);
+ } else {
+ /* All other modes (CVBS/ATVRF etc.) */
+ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00);
+ }
+
+ au8522_writereg(state, AU8522_TVDEC_PGA_REG012H,
+ AU8522_TVDEC_PGA_REG012H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H,
+ AU8522_TVDEC_COMB_MODE_REG015H_CVBS);
+ au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H,
+ AU8522_TVDED_DBG_MODE_REG060H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 |
+ AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN);
+ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,
+ AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC);
+ au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H,
+ AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H,
+ AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H,
+ AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H,
+ AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H,
+ AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H,
+ AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H,
+ AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH,
+ AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH,
+ AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS);
+ if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
+ input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
+ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
+ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO);
+ } else {
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
+ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
+ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS);
+ }
+ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH,
+ AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH,
+ AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H,
+ AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS);
+ au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS);
+ au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H,
+ AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS);
+ au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS);
+ au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H,
+ AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H,
+ AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H,
+ AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH,
+ AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH,
+ AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS);
+ au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H,
+ AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS);
+ au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H,
+ AU8522_TOREGAAGC_REG0E5H_CVBS);
+ au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
+
+ setup_vbi(state, 0);
+
+ if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
+ input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
+ /* Despite what the table says, for the HVR-950q we still need
+ to be in CVBS mode for the S-Video input (reason unknown). */
+ /* filter_coef_type = 3; */
+ filter_coef_type = 5;
+ } else {
+ filter_coef_type = 5;
+ }
+
+ /* Load the Video Decoder Filter Coefficients */
+ for (i = 0; i < NUM_FILTER_COEF; i++) {
+ au8522_writereg(state, filter_coef[i].reg_name,
+ filter_coef[i].reg_val[filter_coef_type]);
+ }
+
+ /* It's not clear what these registers are for, but they are always
+ set to the same value regardless of what mode we're in */
+ au8522_writereg(state, AU8522_REG42EH, 0x87);
+ au8522_writereg(state, AU8522_REG42FH, 0xa2);
+ au8522_writereg(state, AU8522_REG430H, 0xbf);
+ au8522_writereg(state, AU8522_REG431H, 0xcb);
+ au8522_writereg(state, AU8522_REG432H, 0xa1);
+ au8522_writereg(state, AU8522_REG433H, 0x41);
+ au8522_writereg(state, AU8522_REG434H, 0x88);
+ au8522_writereg(state, AU8522_REG435H, 0xc2);
+ au8522_writereg(state, AU8522_REG436H, 0x3c);
+}
+
+static void au8522_setup_cvbs_mode(struct au8522_state *state)
+{
+ /* here we're going to try the pre-programmed route */
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
+
+ /* PGA in automatic mode */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
+
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
+ AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
+
+ setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state)
+{
+ /* here we're going to try the pre-programmed route */
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
+
+ /* It's not clear why we have to have the PGA in automatic mode while
+ enabling clamp control, but it's what Windows does */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);
+
+ /* Disable automatic PGA (since the CVBS is coming from the tuner) */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);
+
+ /* Set input mode to CVBS on channel 4 with SIF audio input enabled */
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
+ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
+
+ setup_decoder_defaults(state,
+ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+static void au8522_setup_svideo_mode(struct au8522_state *state)
+{
+ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
+ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO);
+
+ /* Set input to Y on Channe1, C on Channel 3 */
+ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
+ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
+
+ /* PGA in automatic mode */
+ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
+
+ /* Enable clamping control */
+ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
+
+ setup_decoder_defaults(state,
+ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void disable_audio_input(struct au8522_state *state)
+{
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04);
+ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02);
+
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO);
+}
+
+/* 0=disable, 1=SIF */
+static void set_audio_input(struct au8522_state *state, int aud_input)
+{
+ int i;
+
+ /* Note that this function needs to be used in conjunction with setting
+ the input routing via register 0x81 */
+
+ if (aud_input == AU8522_AUDIO_NONE) {
+ disable_audio_input(state);
+ return;
+ }
+
+ if (aud_input != AU8522_AUDIO_SIF) {
+ /* The caller asked for a mode we don't currently support */
+ printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n",
+ aud_input);
+ return;
+ }
+
+ /* Load the Audio Decoder Filter Coefficients */
+ for (i = 0; i < NUM_LPFILTER_COEF; i++) {
+ au8522_writereg(state, lpfilter_coef[i].reg_name,
+ lpfilter_coef[i].reg_val[0]);
+ }
+
+ /* Setup audio */
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
+ au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);
+ au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);
+ msleep(150);
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00);
+ msleep(1);
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);
+ msleep(50);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);
+ msleep(80);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
+ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
+ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
+ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);
+ msleep(70);
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
+ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
+ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct au8522_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ state->brightness = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH,
+ ctrl->value - 128);
+ break;
+ case V4L2_CID_CONTRAST:
+ state->contrast = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH,
+ ctrl->value);
+ break;
+ case V4L2_CID_SATURATION:
+ state->saturation = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,
+ ctrl->value);
+ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,
+ ctrl->value);
+ break;
+ case V4L2_CID_HUE:
+ state->hue = ctrl->value;
+ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,
+ ctrl->value >> 8);
+ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,
+ ctrl->value & 0xFF);
+ 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:
+ /* Not yet implemented */
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct au8522_state *state = to_state(sd);
+
+ /* Note that we are using values cached in the state structure instead
+ of reading the registers due to issues with i2c reads not working
+ properly/consistently yet on the HVR-950q */
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = state->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = state->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = state->saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ 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:
+ /* Not yet supported */
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int au8522_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct au8522_state *state = to_state(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = au8522_readreg(state, reg->reg & 0xffff);
+ return 0;
+}
+
+static int au8522_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct au8522_state *state = to_state(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ au8522_writereg(state, reg->reg, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int au8522_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct au8522_state *state = to_state(sd);
+
+ if (enable) {
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ 0x01);
+ msleep(1);
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
+ } else {
+ /* This does not completely power down the device
+ (it only reduces it from around 140ma to 80ma) */
+ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
+ 1 << 5);
+ }
+ return 0;
+}
+
+static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1,
+ AU8522_TVDEC_CONTRAST_REG00BH_CVBS);
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0);
+ default:
+ break;
+ }
+
+ qc->type = 0;
+ return -EINVAL;
+}
+
+static int au8522_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct au8522_state *state = to_state(sd);
+
+ state->operational_mode = AU8522_ANALOG_MODE;
+
+ /* Clear out any state associated with the digital side of the
+ chip, so that when it gets powered back up it won't think
+ that it is already tuned */
+ state->current_frequency = 0;
+
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ return 0;
+}
+
+static int au8522_s_video_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct au8522_state *state = to_state(sd);
+
+ au8522_reset(sd, 0);
+
+ if (input == AU8522_COMPOSITE_CH1) {
+ au8522_setup_cvbs_mode(state);
+ } else if (input == AU8522_SVIDEO_CH13) {
+ au8522_setup_svideo_mode(state);
+ } else if (input == AU8522_COMPOSITE_CH4_SIF) {
+ au8522_setup_cvbs_tuner_mode(state);
+ } else {
+ printk(KERN_ERR "au8522 mode not currently supported\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int au8522_s_audio_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct au8522_state *state = to_state(sd);
+ set_audio_input(state, input);
+ return 0;
+}
+
+static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int val = 0;
+ struct au8522_state *state = to_state(sd);
+ u8 lock_status;
+
+ /* Interrogate the decoder to see if we are getting a real signal */
+ lock_status = au8522_readreg(state, 0x00);
+ if (lock_status == 0xa2)
+ vt->signal = 0xffff;
+ else
+ vt->signal = 0x00;
+
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+
+ val = V4L2_TUNER_SUB_MONO;
+ vt->rxsubchans = val;
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ return 0;
+}
+
+static int au8522_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct au8522_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
+
+static int au8522_log_status(struct v4l2_subdev *sd)
+{
+ /* FIXME: Add some status info here */
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops au8522_core_ops = {
+ .log_status = au8522_log_status,
+ .g_chip_ident = au8522_g_chip_ident,
+ .g_ctrl = au8522_g_ctrl,
+ .s_ctrl = au8522_s_ctrl,
+ .queryctrl = au8522_queryctrl,
+ .reset = au8522_reset,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = au8522_g_register,
+ .s_register = au8522_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = {
+ .g_tuner = au8522_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops au8522_audio_ops = {
+ .s_routing = au8522_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops au8522_video_ops = {
+ .s_routing = au8522_s_video_routing,
+ .s_stream = au8522_s_stream,
+};
+
+static const struct v4l2_subdev_ops au8522_ops = {
+ .core = &au8522_core_ops,
+ .tuner = &au8522_tuner_ops,
+ .audio = &au8522_audio_ops,
+ .video = &au8522_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int au8522_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct au8522_state *state;
+ struct v4l2_subdev *sd;
+ int instance;
+ struct au8522_config *demod_config;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ return -EIO;
+ }
+
+ /* allocate memory for the internal state */
+ instance = au8522_get_state(&state, client->adapter, client->addr);
+ switch (instance) {
+ case 0:
+ printk(KERN_ERR "au8522_decoder allocation failed\n");
+ return -EIO;
+ case 1:
+ /* new demod instance */
+ printk(KERN_INFO "au8522_decoder creating new instance...\n");
+ break;
+ default:
+ /* existing demod instance */
+ printk(KERN_INFO "au8522_decoder attach existing instance.\n");
+ break;
+ }
+
+ demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);
+ if (demod_config == NULL) {
+ if (instance == 1)
+ kfree(state);
+ return -ENOMEM;
+ }
+ demod_config->demod_address = 0x8e >> 1;
+
+ state->config = demod_config;
+ state->i2c = client->adapter;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &au8522_ops);
+
+ state->c = client;
+ state->vid_input = AU8522_COMPOSITE_CH1;
+ state->aud_input = AU8522_AUDIO_NONE;
+ state->id = 8522;
+ state->rev = 0;
+
+ /* Jam open the i2c gate to the tuner */
+ au8522_writereg(state, 0x106, 1);
+
+ return 0;
+}
+
+static int au8522_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ v4l2_device_unregister_subdev(sd);
+ au8522_release_state(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id au8522_id[] = {
+ {"au8522", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, au8522_id);
+
+static struct i2c_driver au8522_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "au8522",
+ },
+ .probe = au8522_probe,
+ .remove = au8522_remove,
+ .id_table = au8522_id,
+};
+
+module_i2c_driver(au8522_driver);
diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c
new file mode 100644
index 000000000000..a68974f6d708
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_dig.c
@@ -0,0 +1,828 @@
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "au8522.h"
+#include "au8522_priv.h"
+
+static int debug;
+
+#define dprintk(arg...)\
+ do { if (debug)\
+ printk(arg);\
+ } while (0)
+
+struct mse2snr_tab {
+ u16 val;
+ u16 data;
+};
+
+/* VSB SNR lookup table */
+static struct mse2snr_tab vsb_mse2snr_tab[] = {
+ { 0, 270 },
+ { 2, 250 },
+ { 3, 240 },
+ { 5, 230 },
+ { 7, 220 },
+ { 9, 210 },
+ { 12, 200 },
+ { 13, 195 },
+ { 15, 190 },
+ { 17, 185 },
+ { 19, 180 },
+ { 21, 175 },
+ { 24, 170 },
+ { 27, 165 },
+ { 31, 160 },
+ { 32, 158 },
+ { 33, 156 },
+ { 36, 152 },
+ { 37, 150 },
+ { 39, 148 },
+ { 40, 146 },
+ { 41, 144 },
+ { 43, 142 },
+ { 44, 140 },
+ { 48, 135 },
+ { 50, 130 },
+ { 43, 142 },
+ { 53, 125 },
+ { 56, 120 },
+ { 256, 115 },
+};
+
+/* QAM64 SNR lookup table */
+static struct mse2snr_tab qam64_mse2snr_tab[] = {
+ { 15, 0 },
+ { 16, 290 },
+ { 17, 288 },
+ { 18, 286 },
+ { 19, 284 },
+ { 20, 282 },
+ { 21, 281 },
+ { 22, 279 },
+ { 23, 277 },
+ { 24, 275 },
+ { 25, 273 },
+ { 26, 271 },
+ { 27, 269 },
+ { 28, 268 },
+ { 29, 266 },
+ { 30, 264 },
+ { 31, 262 },
+ { 32, 260 },
+ { 33, 259 },
+ { 34, 258 },
+ { 35, 256 },
+ { 36, 255 },
+ { 37, 254 },
+ { 38, 252 },
+ { 39, 251 },
+ { 40, 250 },
+ { 41, 249 },
+ { 42, 248 },
+ { 43, 246 },
+ { 44, 245 },
+ { 45, 244 },
+ { 46, 242 },
+ { 47, 241 },
+ { 48, 240 },
+ { 50, 239 },
+ { 51, 238 },
+ { 53, 237 },
+ { 54, 236 },
+ { 56, 235 },
+ { 57, 234 },
+ { 59, 233 },
+ { 60, 232 },
+ { 62, 231 },
+ { 63, 230 },
+ { 65, 229 },
+ { 67, 228 },
+ { 68, 227 },
+ { 70, 226 },
+ { 71, 225 },
+ { 73, 224 },
+ { 74, 223 },
+ { 76, 222 },
+ { 78, 221 },
+ { 80, 220 },
+ { 82, 219 },
+ { 85, 218 },
+ { 88, 217 },
+ { 90, 216 },
+ { 92, 215 },
+ { 93, 214 },
+ { 94, 212 },
+ { 95, 211 },
+ { 97, 210 },
+ { 99, 209 },
+ { 101, 208 },
+ { 102, 207 },
+ { 104, 206 },
+ { 107, 205 },
+ { 111, 204 },
+ { 114, 203 },
+ { 118, 202 },
+ { 122, 201 },
+ { 125, 200 },
+ { 128, 199 },
+ { 130, 198 },
+ { 132, 197 },
+ { 256, 190 },
+};
+
+/* QAM256 SNR lookup table */
+static struct mse2snr_tab qam256_mse2snr_tab[] = {
+ { 15, 0 },
+ { 16, 400 },
+ { 17, 398 },
+ { 18, 396 },
+ { 19, 394 },
+ { 20, 392 },
+ { 21, 390 },
+ { 22, 388 },
+ { 23, 386 },
+ { 24, 384 },
+ { 25, 382 },
+ { 26, 380 },
+ { 27, 379 },
+ { 28, 378 },
+ { 29, 377 },
+ { 30, 376 },
+ { 31, 375 },
+ { 32, 374 },
+ { 33, 373 },
+ { 34, 372 },
+ { 35, 371 },
+ { 36, 370 },
+ { 37, 362 },
+ { 38, 354 },
+ { 39, 346 },
+ { 40, 338 },
+ { 41, 330 },
+ { 42, 328 },
+ { 43, 326 },
+ { 44, 324 },
+ { 45, 322 },
+ { 46, 320 },
+ { 47, 319 },
+ { 48, 318 },
+ { 49, 317 },
+ { 50, 316 },
+ { 51, 315 },
+ { 52, 314 },
+ { 53, 313 },
+ { 54, 312 },
+ { 55, 311 },
+ { 56, 310 },
+ { 57, 308 },
+ { 58, 306 },
+ { 59, 304 },
+ { 60, 302 },
+ { 61, 300 },
+ { 62, 298 },
+ { 65, 295 },
+ { 68, 294 },
+ { 70, 293 },
+ { 73, 292 },
+ { 76, 291 },
+ { 78, 290 },
+ { 79, 289 },
+ { 81, 288 },
+ { 82, 287 },
+ { 83, 286 },
+ { 84, 285 },
+ { 85, 284 },
+ { 86, 283 },
+ { 88, 282 },
+ { 89, 281 },
+ { 256, 280 },
+};
+
+static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
+ u16 *snr)
+{
+ int i, ret = -EINVAL;
+ dprintk("%s()\n", __func__);
+
+ for (i = 0; i < sz; i++) {
+ if (mse < tab[i].val) {
+ *snr = tab[i].data;
+ ret = 0;
+ break;
+ }
+ }
+ dprintk("%s() snr=%d\n", __func__, *snr);
+ return ret;
+}
+
+static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ u8 r0b5, r0b6, r0b7;
+ char *ifmhz;
+
+ switch (if_freq) {
+ case AU8522_IF_3_25MHZ:
+ ifmhz = "3.25";
+ r0b5 = 0x00;
+ r0b6 = 0x3d;
+ r0b7 = 0xa0;
+ break;
+ case AU8522_IF_4MHZ:
+ ifmhz = "4.00";
+ r0b5 = 0x00;
+ r0b6 = 0x4b;
+ r0b7 = 0xd9;
+ break;
+ case AU8522_IF_6MHZ:
+ ifmhz = "6.00";
+ r0b5 = 0xfb;
+ r0b6 = 0x8e;
+ r0b7 = 0x39;
+ break;
+ default:
+ dprintk("%s() IF Frequency not supported\n", __func__);
+ return -EINVAL;
+ }
+ dprintk("%s() %s MHz\n", __func__, ifmhz);
+ au8522_writereg(state, 0x80b5, r0b5);
+ au8522_writereg(state, 0x80b6, r0b6);
+ au8522_writereg(state, 0x80b7, r0b7);
+
+ return 0;
+}
+
+/* VSB Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} VSB_mod_tab[] = {
+ { 0x8090, 0x84 },
+ { 0x4092, 0x11 },
+ { 0x2005, 0x00 },
+ { 0x8091, 0x80 },
+ { 0x80a3, 0x0c },
+ { 0x80a4, 0xe8 },
+ { 0x8081, 0xc4 },
+ { 0x80a5, 0x40 },
+ { 0x80a7, 0x40 },
+ { 0x80a6, 0x67 },
+ { 0x8262, 0x20 },
+ { 0x821c, 0x30 },
+ { 0x80d8, 0x1a },
+ { 0x8227, 0xa0 },
+ { 0x8121, 0xff },
+ { 0x80a8, 0xf0 },
+ { 0x80a9, 0x05 },
+ { 0x80aa, 0x77 },
+ { 0x80ab, 0xf0 },
+ { 0x80ac, 0x05 },
+ { 0x80ad, 0x77 },
+ { 0x80ae, 0x41 },
+ { 0x80af, 0x66 },
+ { 0x821b, 0xcc },
+ { 0x821d, 0x80 },
+ { 0x80a4, 0xe8 },
+ { 0x8231, 0x13 },
+};
+
+/* QAM64 Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} QAM64_mod_tab[] = {
+ { 0x00a3, 0x09 },
+ { 0x00a4, 0x00 },
+ { 0x0081, 0xc4 },
+ { 0x00a5, 0x40 },
+ { 0x00aa, 0x77 },
+ { 0x00ad, 0x77 },
+ { 0x00a6, 0x67 },
+ { 0x0262, 0x20 },
+ { 0x021c, 0x30 },
+ { 0x00b8, 0x3e },
+ { 0x00b9, 0xf0 },
+ { 0x00ba, 0x01 },
+ { 0x00bb, 0x18 },
+ { 0x00bc, 0x50 },
+ { 0x00bd, 0x00 },
+ { 0x00be, 0xea },
+ { 0x00bf, 0xef },
+ { 0x00c0, 0xfc },
+ { 0x00c1, 0xbd },
+ { 0x00c2, 0x1f },
+ { 0x00c3, 0xfc },
+ { 0x00c4, 0xdd },
+ { 0x00c5, 0xaf },
+ { 0x00c6, 0x00 },
+ { 0x00c7, 0x38 },
+ { 0x00c8, 0x30 },
+ { 0x00c9, 0x05 },
+ { 0x00ca, 0x4a },
+ { 0x00cb, 0xd0 },
+ { 0x00cc, 0x01 },
+ { 0x00cd, 0xd9 },
+ { 0x00ce, 0x6f },
+ { 0x00cf, 0xf9 },
+ { 0x00d0, 0x70 },
+ { 0x00d1, 0xdf },
+ { 0x00d2, 0xf7 },
+ { 0x00d3, 0xc2 },
+ { 0x00d4, 0xdf },
+ { 0x00d5, 0x02 },
+ { 0x00d6, 0x9a },
+ { 0x00d7, 0xd0 },
+ { 0x0250, 0x0d },
+ { 0x0251, 0xcd },
+ { 0x0252, 0xe0 },
+ { 0x0253, 0x05 },
+ { 0x0254, 0xa7 },
+ { 0x0255, 0xff },
+ { 0x0256, 0xed },
+ { 0x0257, 0x5b },
+ { 0x0258, 0xae },
+ { 0x0259, 0xe6 },
+ { 0x025a, 0x3d },
+ { 0x025b, 0x0f },
+ { 0x025c, 0x0d },
+ { 0x025d, 0xea },
+ { 0x025e, 0xf2 },
+ { 0x025f, 0x51 },
+ { 0x0260, 0xf5 },
+ { 0x0261, 0x06 },
+ { 0x021a, 0x00 },
+ { 0x0546, 0x40 },
+ { 0x0210, 0xc7 },
+ { 0x0211, 0xaa },
+ { 0x0212, 0xab },
+ { 0x0213, 0x02 },
+ { 0x0502, 0x00 },
+ { 0x0121, 0x04 },
+ { 0x0122, 0x04 },
+ { 0x052e, 0x10 },
+ { 0x00a4, 0xca },
+ { 0x00a7, 0x40 },
+ { 0x0526, 0x01 },
+};
+
+/* QAM256 Modulation table */
+static struct {
+ u16 reg;
+ u16 data;
+} QAM256_mod_tab[] = {
+ { 0x80a3, 0x09 },
+ { 0x80a4, 0x00 },
+ { 0x8081, 0xc4 },
+ { 0x80a5, 0x40 },
+ { 0x80aa, 0x77 },
+ { 0x80ad, 0x77 },
+ { 0x80a6, 0x67 },
+ { 0x8262, 0x20 },
+ { 0x821c, 0x30 },
+ { 0x80b8, 0x3e },
+ { 0x80b9, 0xf0 },
+ { 0x80ba, 0x01 },
+ { 0x80bb, 0x18 },
+ { 0x80bc, 0x50 },
+ { 0x80bd, 0x00 },
+ { 0x80be, 0xea },
+ { 0x80bf, 0xef },
+ { 0x80c0, 0xfc },
+ { 0x80c1, 0xbd },
+ { 0x80c2, 0x1f },
+ { 0x80c3, 0xfc },
+ { 0x80c4, 0xdd },
+ { 0x80c5, 0xaf },
+ { 0x80c6, 0x00 },
+ { 0x80c7, 0x38 },
+ { 0x80c8, 0x30 },
+ { 0x80c9, 0x05 },
+ { 0x80ca, 0x4a },
+ { 0x80cb, 0xd0 },
+ { 0x80cc, 0x01 },
+ { 0x80cd, 0xd9 },
+ { 0x80ce, 0x6f },
+ { 0x80cf, 0xf9 },
+ { 0x80d0, 0x70 },
+ { 0x80d1, 0xdf },
+ { 0x80d2, 0xf7 },
+ { 0x80d3, 0xc2 },
+ { 0x80d4, 0xdf },
+ { 0x80d5, 0x02 },
+ { 0x80d6, 0x9a },
+ { 0x80d7, 0xd0 },
+ { 0x8250, 0x0d },
+ { 0x8251, 0xcd },
+ { 0x8252, 0xe0 },
+ { 0x8253, 0x05 },
+ { 0x8254, 0xa7 },
+ { 0x8255, 0xff },
+ { 0x8256, 0xed },
+ { 0x8257, 0x5b },
+ { 0x8258, 0xae },
+ { 0x8259, 0xe6 },
+ { 0x825a, 0x3d },
+ { 0x825b, 0x0f },
+ { 0x825c, 0x0d },
+ { 0x825d, 0xea },
+ { 0x825e, 0xf2 },
+ { 0x825f, 0x51 },
+ { 0x8260, 0xf5 },
+ { 0x8261, 0x06 },
+ { 0x821a, 0x00 },
+ { 0x8546, 0x40 },
+ { 0x8210, 0x26 },
+ { 0x8211, 0xf6 },
+ { 0x8212, 0x84 },
+ { 0x8213, 0x02 },
+ { 0x8502, 0x01 },
+ { 0x8121, 0x04 },
+ { 0x8122, 0x04 },
+ { 0x852e, 0x10 },
+ { 0x80a4, 0xca },
+ { 0x80a7, 0x40 },
+ { 0x8526, 0x01 },
+};
+
+static int au8522_enable_modulation(struct dvb_frontend *fe,
+ fe_modulation_t m)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ int i;
+
+ dprintk("%s(0x%08x)\n", __func__, m);
+
+ switch (m) {
+ case VSB_8:
+ dprintk("%s() VSB_8\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
+ au8522_writereg(state,
+ VSB_mod_tab[i].reg,
+ VSB_mod_tab[i].data);
+ au8522_set_if(fe, state->config->vsb_if);
+ break;
+ case QAM_64:
+ dprintk("%s() QAM 64\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++)
+ au8522_writereg(state,
+ QAM64_mod_tab[i].reg,
+ QAM64_mod_tab[i].data);
+ au8522_set_if(fe, state->config->qam_if);
+ break;
+ case QAM_256:
+ dprintk("%s() QAM 256\n", __func__);
+ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
+ au8522_writereg(state,
+ QAM256_mod_tab[i].reg,
+ QAM256_mod_tab[i].data);
+ au8522_set_if(fe, state->config->qam_if);
+ break;
+ default:
+ dprintk("%s() Invalid modulation\n", __func__);
+ return -EINVAL;
+ }
+
+ state->current_modulation = m;
+
+ return 0;
+}
+
+/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+static int au8522_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct au8522_state *state = fe->demodulator_priv;
+ int ret = -EINVAL;
+
+ dprintk("%s(frequency=%d)\n", __func__, c->frequency);
+
+ if ((state->current_frequency == c->frequency) &&
+ (state->current_modulation == c->modulation))
+ return 0;
+
+ if (fe->ops.tuner_ops.set_params) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Allow the tuner to settle */
+ msleep(100);
+
+ au8522_enable_modulation(fe, c->modulation);
+
+ state->current_frequency = c->frequency;
+
+ return 0;
+}
+
+static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ u8 reg;
+ u32 tuner_status = 0;
+
+ *status = 0;
+
+ if (state->current_modulation == VSB_8) {
+ dprintk("%s() Checking VSB_8\n", __func__);
+ reg = au8522_readreg(state, 0x4088);
+ if ((reg & 0x03) == 0x03)
+ *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
+ } else {
+ dprintk("%s() Checking QAM\n", __func__);
+ reg = au8522_readreg(state, 0x4541);
+ if (reg & 0x80)
+ *status |= FE_HAS_VITERBI;
+ if (reg & 0x20)
+ *status |= FE_HAS_LOCK | FE_HAS_SYNC;
+ }
+
+ switch (state->config->status_mode) {
+ case AU8522_DEMODLOCKING:
+ dprintk("%s() DEMODLOCKING\n", __func__);
+ if (*status & FE_HAS_VITERBI)
+ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+ break;
+ case AU8522_TUNERLOCKING:
+ /* Get the tuner status */
+ dprintk("%s() TUNERLOCKING\n", __func__);
+ if (fe->ops.tuner_ops.get_status) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ fe->ops.tuner_ops.get_status(fe, &tuner_status);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ if (tuner_status)
+ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+ break;
+ }
+ state->fe_status = *status;
+
+ if (*status & FE_HAS_LOCK)
+ /* turn on LED, if it isn't on already */
+ au8522_led_ctrl(state, -1);
+ else
+ /* turn off LED */
+ au8522_led_ctrl(state, 0);
+
+ dprintk("%s() status 0x%08x\n", __func__, *status);
+
+ return 0;
+}
+
+static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ int led;
+ u16 strong;
+
+ /* bail out if we can't control an LED */
+ if (!led_config)
+ return 0;
+
+ if (0 == (state->fe_status & FE_HAS_LOCK))
+ return au8522_led_ctrl(state, 0);
+ else if (state->current_modulation == QAM_256)
+ strong = led_config->qam256_strong;
+ else if (state->current_modulation == QAM_64)
+ strong = led_config->qam64_strong;
+ else /* (state->current_modulation == VSB_8) */
+ strong = led_config->vsb8_strong;
+
+ if (*snr >= strong)
+ led = 2;
+ else
+ led = 1;
+
+ if ((state->led_state) &&
+ (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+ /* snr didn't change enough to bother
+ * changing the color of the led */
+ return 0;
+
+ return au8522_led_ctrl(state, led);
+}
+
+static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ int ret = -EINVAL;
+
+ dprintk("%s()\n", __func__);
+
+ if (state->current_modulation == QAM_256)
+ ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
+ ARRAY_SIZE(qam256_mse2snr_tab),
+ au8522_readreg(state, 0x4522),
+ snr);
+ else if (state->current_modulation == QAM_64)
+ ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
+ ARRAY_SIZE(qam64_mse2snr_tab),
+ au8522_readreg(state, 0x4522),
+ snr);
+ else /* VSB_8 */
+ ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
+ ARRAY_SIZE(vsb_mse2snr_tab),
+ au8522_readreg(state, 0x4311),
+ snr);
+
+ if (state->config->led_cfg)
+ au8522_led_status(state, snr);
+
+ return ret;
+}
+
+static int au8522_read_signal_strength(struct dvb_frontend *fe,
+ u16 *signal_strength)
+{
+ /* borrowed from lgdt330x.c
+ *
+ * Calculate strength from SNR up to 35dB
+ * Even though the SNR can go higher than 35dB,
+ * there is some comfort factor in having a range of
+ * strong signals that can show at 100%
+ */
+ u16 snr;
+ u32 tmp;
+ int ret = au8522_read_snr(fe, &snr);
+
+ *signal_strength = 0;
+
+ if (0 == ret) {
+ /* The following calculation method was chosen
+ * purely for the sake of code re-use from the
+ * other demod drivers that use this method */
+
+ /* Convert from SNR in dB * 10 to 8.24 fixed-point */
+ tmp = (snr * ((1 << 24) / 10));
+
+ /* Convert from 8.24 fixed-point to
+ * scale the range 0 - 35*2^24 into 0 - 65535*/
+ if (tmp >= 8960 * 0x10000)
+ *signal_strength = 0xffff;
+ else
+ *signal_strength = tmp / 8960;
+ }
+
+ return ret;
+}
+
+static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ if (state->current_modulation == VSB_8)
+ *ucblocks = au8522_readreg(state, 0x4087);
+ else
+ *ucblocks = au8522_readreg(state, 0x4543);
+
+ return 0;
+}
+
+static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ return au8522_read_ucblocks(fe, ber);
+}
+
+static int au8522_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct au8522_state *state = fe->demodulator_priv;
+
+ c->frequency = state->current_frequency;
+ c->modulation = state->current_modulation;
+
+ return 0;
+}
+
+static int au8522_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *tune)
+{
+ tune->min_delay_ms = 1000;
+ return 0;
+}
+
+static struct dvb_frontend_ops au8522_ops;
+
+
+static void au8522_release(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ au8522_release_state(state);
+}
+
+struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct au8522_state *state = NULL;
+ int instance;
+
+ /* allocate memory for the internal state */
+ instance = au8522_get_state(&state, i2c, config->demod_address);
+ switch (instance) {
+ case 0:
+ dprintk("%s state allocation failed\n", __func__);
+ break;
+ case 1:
+ /* new demod instance */
+ dprintk("%s using new instance\n", __func__);
+ break;
+ default:
+ /* existing demod instance */
+ dprintk("%s using existing instance\n", __func__);
+ break;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->operational_mode = AU8522_DIGITAL_MODE;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &au8522_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ state->frontend.ops.analog_ops.i2c_gate_ctrl = au8522_analog_i2c_gate_ctrl;
+
+ if (au8522_init(&state->frontend) != 0) {
+ printk(KERN_ERR "%s: Failed to initialize correctly\n",
+ __func__);
+ goto error;
+ }
+
+ /* Note: Leaving the I2C gate open here. */
+ au8522_i2c_gate_ctrl(&state->frontend, 1);
+
+ return &state->frontend;
+
+error:
+ au8522_release_state(state);
+ return NULL;
+}
+EXPORT_SYMBOL(au8522_attach);
+
+static struct dvb_frontend_ops au8522_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Auvitek AU8522 QAM/8VSB Frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+ },
+
+ .init = au8522_init,
+ .sleep = au8522_sleep,
+ .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
+ .set_frontend = au8522_set_frontend,
+ .get_frontend = au8522_get_frontend,
+ .get_tune_settings = au8522_get_tune_settings,
+ .read_status = au8522_read_status,
+ .read_ber = au8522_read_ber,
+ .read_signal_strength = au8522_read_signal_strength,
+ .read_snr = au8522_read_snr,
+ .read_ucblocks = au8522_read_ucblocks,
+ .release = au8522_release,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+
+MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h
new file mode 100644
index 000000000000..0529699a27bd
--- /dev/null
+++ b/drivers/media/dvb-frontends/au8522_priv.h
@@ -0,0 +1,446 @@
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+ Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
+ Copyright (C) 2005-2008 Auvitek International, Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+#include "au8522.h"
+#include "tuner-i2c.h"
+
+#define AU8522_ANALOG_MODE 0
+#define AU8522_DIGITAL_MODE 1
+
+struct au8522_state {
+ struct i2c_client *c;
+ struct i2c_adapter *i2c;
+
+ u8 operational_mode;
+
+ /* Used for sharing of the state between analog and digital mode */
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+
+ /* configuration settings */
+ const struct au8522_config *config;
+
+ struct dvb_frontend frontend;
+
+ u32 current_frequency;
+ fe_modulation_t current_modulation;
+
+ u32 fe_status;
+ unsigned int led_state;
+
+ /* Analog settings */
+ struct v4l2_subdev sd;
+ v4l2_std_id std;
+ int vid_input;
+ int aud_input;
+ u32 id;
+ u32 rev;
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ s16 hue;
+};
+
+/* These are routines shared by both the VSB/QAM demodulator and the analog
+ decoder */
+int au8522_writereg(struct au8522_state *state, u16 reg, u8 data);
+u8 au8522_readreg(struct au8522_state *state, u16 reg);
+int au8522_init(struct dvb_frontend *fe);
+int au8522_sleep(struct dvb_frontend *fe);
+
+int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
+ u8 client_address);
+void au8522_release_state(struct au8522_state *state);
+int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+int au8522_led_ctrl(struct au8522_state *state, int led);
+
+/* REGISTERS */
+#define AU8522_INPUT_CONTROL_REG081H 0x081
+#define AU8522_PGA_CONTROL_REG082H 0x082
+#define AU8522_CLAMPING_CONTROL_REG083H 0x083
+
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5
+#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6
+#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7
+#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8
+#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9
+#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA
+#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB
+#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC
+#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD
+#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE
+#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF
+
+/* Receiver registers */
+#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0
+#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1
+#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2
+#define AU8522_TOREGAGC1_REG0B3H 0x0B3
+#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4
+#define AU8522_FRMREGBBH_REG0B5H 0x0B5
+#define AU8522_FRMREGBBM_REG0B6H 0x0B6
+#define AU8522_FRMREGBBL_REG0B7H 0x0B7
+/* 0xB8 TO 0xD7 are the filter coefficients */
+#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8
+#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9
+#define AU8522_TOREGAGC2_REG0DAH 0x0DA
+#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB
+#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC
+#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD
+#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE
+#define AU8522_TOREGFREQ_REG0DFH 0x0DF
+
+#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB
+#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC
+#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED
+
+#define AU8522_CHIP_MODE_REG0FEH 0x0FE
+
+/* I2C bus control registers */
+#define AU8522_I2C_CONTROL_REG0_REG090H 0x090
+#define AU8522_I2C_CONTROL_REG1_REG091H 0x091
+#define AU8522_I2C_STATUS_REG092H 0x092
+#define AU8522_I2C_WR_DATA0_REG093H 0x093
+#define AU8522_I2C_WR_DATA1_REG094H 0x094
+#define AU8522_I2C_WR_DATA2_REG095H 0x095
+#define AU8522_I2C_WR_DATA3_REG096H 0x096
+#define AU8522_I2C_WR_DATA4_REG097H 0x097
+#define AU8522_I2C_WR_DATA5_REG098H 0x098
+#define AU8522_I2C_WR_DATA6_REG099H 0x099
+#define AU8522_I2C_WR_DATA7_REG09AH 0x09A
+#define AU8522_I2C_RD_DATA0_REG09BH 0x09B
+#define AU8522_I2C_RD_DATA1_REG09CH 0x09C
+#define AU8522_I2C_RD_DATA2_REG09DH 0x09D
+#define AU8522_I2C_RD_DATA3_REG09EH 0x09E
+#define AU8522_I2C_RD_DATA4_REG09FH 0x09F
+#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0
+#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1
+#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2
+
+#define AU8522_ENA_USB_REG101H 0x101
+
+#define AU8522_I2S_CTRL_0_REG110H 0x110
+#define AU8522_I2S_CTRL_1_REG111H 0x111
+#define AU8522_I2S_CTRL_2_REG112H 0x112
+
+#define AU8522_FRMREGFFECONTROL_REG121H 0x121
+#define AU8522_FRMREGDFECONTROL_REG122H 0x122
+
+#define AU8522_CARRFREQOFFSET0_REG201H 0x201
+#define AU8522_CARRFREQOFFSET1_REG202H 0x202
+
+#define AU8522_DECIMATION_GAIN_REG21AH 0x21A
+#define AU8522_FRMREGIFSLP_REG21BH 0x21B
+#define AU8522_FRMREGTHRDL2_REG21CH 0x21C
+#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D
+#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E
+#define AU8522_FRMREGPLLMODE_REG21FH 0x21F
+#define AU8522_FRMREGCSTHRD_REG220H 0x220
+#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221
+#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222
+#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223
+#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224
+#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225
+#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226
+#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227
+#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228
+
+/* Analog TV Decoder */
+#define AU8522_TVDEC_STATUS_REG000H 0x000
+#define AU8522_TVDEC_INT_STATUS_REG001H 0x001
+#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002
+#define AU8522_TVDEC_SHARPNESSREG009H 0x009
+#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A
+#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B
+#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C
+#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D
+#define AU8522_TVDEC_HUE_H_REG00EH 0x00E
+#define AU8522_TVDEC_HUE_L_REG00FH 0x00F
+#define AU8522_TVDEC_INT_MASK_REG010H 0x010
+#define AU8522_VIDEO_MODE_REG011H 0x011
+#define AU8522_TVDEC_PGA_REG012H 0x012
+#define AU8522_TVDEC_COMB_MODE_REG015H 0x015
+#define AU8522_REG016H 0x016
+#define AU8522_TVDED_DBG_MODE_REG060H 0x060
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062
+#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063
+#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064
+#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065
+#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066
+#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067
+#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068
+#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069
+#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A
+#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D
+#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E
+#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F
+#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070
+#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073
+#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077
+#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078
+#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079
+#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A
+#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B
+#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E
+#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F
+
+#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4
+#define AU8522_TOREGAAGC_REG0E5H 0x0E5
+
+#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401
+#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402
+#define AU8522_FILTER_COEF_R410 0x410
+#define AU8522_FILTER_COEF_R411 0x411
+#define AU8522_FILTER_COEF_R412 0x412
+#define AU8522_FILTER_COEF_R413 0x413
+#define AU8522_FILTER_COEF_R414 0x414
+#define AU8522_FILTER_COEF_R415 0x415
+#define AU8522_FILTER_COEF_R416 0x416
+#define AU8522_FILTER_COEF_R417 0x417
+#define AU8522_FILTER_COEF_R418 0x418
+#define AU8522_FILTER_COEF_R419 0x419
+#define AU8522_FILTER_COEF_R41A 0x41A
+#define AU8522_FILTER_COEF_R41B 0x41B
+#define AU8522_FILTER_COEF_R41C 0x41C
+#define AU8522_FILTER_COEF_R41D 0x41D
+#define AU8522_FILTER_COEF_R41E 0x41E
+#define AU8522_FILTER_COEF_R41F 0x41F
+#define AU8522_FILTER_COEF_R420 0x420
+#define AU8522_FILTER_COEF_R421 0x421
+#define AU8522_FILTER_COEF_R422 0x422
+#define AU8522_FILTER_COEF_R423 0x423
+#define AU8522_FILTER_COEF_R424 0x424
+#define AU8522_FILTER_COEF_R425 0x425
+#define AU8522_FILTER_COEF_R426 0x426
+#define AU8522_FILTER_COEF_R427 0x427
+#define AU8522_FILTER_COEF_R428 0x428
+#define AU8522_FILTER_COEF_R429 0x429
+#define AU8522_FILTER_COEF_R42A 0x42A
+#define AU8522_FILTER_COEF_R42B 0x42B
+#define AU8522_FILTER_COEF_R42C 0x42C
+#define AU8522_FILTER_COEF_R42D 0x42D
+
+/* VBI Control Registers */
+#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004
+#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005
+#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006
+#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007
+#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017
+#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018
+#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019
+#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A
+#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B
+#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F
+#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022
+#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023
+
+#define AU8522_REG071H 0x071
+#define AU8522_REG072H 0x072
+#define AU8522_REG074H 0x074
+#define AU8522_REG075H 0x075
+
+/* Digital Demodulator Registers */
+#define AU8522_FRAME_COUNT0_REG084H 0x084
+#define AU8522_RS_STATUS_G0_REG085H 0x085
+#define AU8522_RS_STATUS_B0_REG086H 0x086
+#define AU8522_RS_STATUS_E_REG087H 0x087
+#define AU8522_DEMODULATION_STATUS_REG088H 0x088
+#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6
+#define AU8522_TSPORT_CONTROL_REG10BH 0x10B
+#define AU8522_TSTHES_REG10CH 0x10C
+#define AU8522_FRMREGDFEKEEP_REG301H 0x301
+#define AU8522_DFE_AVERAGE_REG302H 0x302
+#define AU8522_FRMREGEQLERRWIN_REG303H 0x303
+#define AU8522_FRMREGFFEKEEP_REG304H 0x304
+#define AU8522_FRMREGDFECONTROL1_REG305H 0x305
+#define AU8522_FRMREGEQLERRLOW_REG306H 0x306
+
+#define AU8522_REG42EH 0x42E
+#define AU8522_REG42FH 0x42F
+#define AU8522_REG430H 0x430
+#define AU8522_REG431H 0x431
+#define AU8522_REG432H 0x432
+#define AU8522_REG433H 0x433
+#define AU8522_REG434H 0x434
+#define AU8522_REG435H 0x435
+#define AU8522_REG436H 0x436
+
+/* GPIO Registers */
+#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0
+#define AU8522_GPIO_STATUS_REG0E1H 0x0E1
+#define AU8522_GPIO_DATA_REG0E2H 0x0E2
+
+/* Audio Control Registers */
+#define AU8522_AUDIOAGC_REG0EEH 0x0EE
+#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0
+#define AU8522_AUDIO_MODE_REG0F1H 0x0F1
+#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2
+#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3
+#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4
+#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7
+#define AU8522_REG0F9H 0x0F9
+
+#define AU8522_AUDIOAGC2_REG605H 0x605
+#define AU8522_AUDIOFREQ_REG606H 0x606
+
+
+/**************************************************************/
+
+/* Format control 1 */
+
+/* VCR Mode 7-6 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_YES 0x80
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_NO 0x40
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_AUTO 0x00
+/* Field len 5-4 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_625 0x20
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 0x10
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_AUTO 0x00
+/* Line len (us) 3-2 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_64_000 0x0b
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 0x08
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_556 0x04
+/* Subcarrier freq 1-0 */
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO 0x03
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_443 0x02
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN 0x01
+#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_50 0x00
+
+/* Format control 2 */
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_AUTODETECT 0x00
+#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC 0x01
+
+
+#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4
+#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68
+#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28
+/* CH1 AS Y,CH3 AS C */
+#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23
+/* CH2 AS Y,CH4 AS C */
+#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A
+#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02
+
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD
+#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD
+
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01
+#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01
+
+/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */
+#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79
+#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80
+#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80
+#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00
+#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00
+#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F
+#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00
+#define AU8522_REG016H_CVBS 0x00
+#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00
+#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19
+#define AU8522_REG0F9H_AUDIO 0x20
+#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7
+#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A
+#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32
+#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19
+#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23
+#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41
+#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A
+#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34
+#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05
+#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15
+#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E
+#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F
+#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80
+#define AU8522_REG071H_CVBS 0x18
+#define AU8522_REG072H_CVBS 0x30
+#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0
+#define AU8522_REG074H_CVBS 0x80
+#define AU8522_REG075H_CVBS 0xF0
+#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB
+#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04
+#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00
+#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00
+#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE
+#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE
+#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00
+#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40
+
+/* Enables Closed captioning */
+#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21
diff --git a/drivers/media/dvb/frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c
index 033cd7ad3ca2..033cd7ad3ca2 100644
--- a/drivers/media/dvb/frontends/bcm3510.c
+++ b/drivers/media/dvb-frontends/bcm3510.c
diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h
index f4575c0cc446..f4575c0cc446 100644
--- a/drivers/media/dvb/frontends/bcm3510.h
+++ b/drivers/media/dvb-frontends/bcm3510.h
diff --git a/drivers/media/dvb/frontends/bcm3510_priv.h b/drivers/media/dvb-frontends/bcm3510_priv.h
index 3bb1bc2a04f0..3bb1bc2a04f0 100644
--- a/drivers/media/dvb/frontends/bcm3510_priv.h
+++ b/drivers/media/dvb-frontends/bcm3510_priv.h
diff --git a/drivers/media/dvb/frontends/bsbe1-d01a.h b/drivers/media/dvb-frontends/bsbe1-d01a.h
index 7ed3c424178c..7ed3c424178c 100644
--- a/drivers/media/dvb/frontends/bsbe1-d01a.h
+++ b/drivers/media/dvb-frontends/bsbe1-d01a.h
diff --git a/drivers/media/dvb/frontends/bsbe1.h b/drivers/media/dvb-frontends/bsbe1.h
index 53e4d0dbb745..53e4d0dbb745 100644
--- a/drivers/media/dvb/frontends/bsbe1.h
+++ b/drivers/media/dvb-frontends/bsbe1.h
diff --git a/drivers/media/dvb/frontends/bsru6.h b/drivers/media/dvb-frontends/bsru6.h
index c2a578e1314d..c2a578e1314d 100644
--- a/drivers/media/dvb/frontends/bsru6.h
+++ b/drivers/media/dvb-frontends/bsru6.h
diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c
index f2a90f990ce3..f2a90f990ce3 100644
--- a/drivers/media/dvb/frontends/cx22700.c
+++ b/drivers/media/dvb-frontends/cx22700.c
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h
index 4757a930ca05..4757a930ca05 100644
--- a/drivers/media/dvb/frontends/cx22700.h
+++ b/drivers/media/dvb-frontends/cx22700.h
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c
index edc8eafc5c09..edc8eafc5c09 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb-frontends/cx22702.c
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h
index f154e1f428eb..f154e1f428eb 100644
--- a/drivers/media/dvb/frontends/cx22702.h
+++ b/drivers/media/dvb-frontends/cx22702.h
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c
index 3180f5b2a6a6..3180f5b2a6a6 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb-frontends/cx24110.c
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h
index fdcceee91f3a..fdcceee91f3a 100644
--- a/drivers/media/dvb/frontends/cx24110.h
+++ b/drivers/media/dvb-frontends/cx24110.h
diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c
index 3883c3b31aef..3883c3b31aef 100644
--- a/drivers/media/dvb/frontends/cx24113.c
+++ b/drivers/media/dvb-frontends/cx24113.c
diff --git a/drivers/media/dvb/frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h
index 01eb7b9c28f4..01eb7b9c28f4 100644
--- a/drivers/media/dvb/frontends/cx24113.h
+++ b/drivers/media/dvb-frontends/cx24113.h
diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c
index b48879186537..b48879186537 100644
--- a/drivers/media/dvb/frontends/cx24116.c
+++ b/drivers/media/dvb-frontends/cx24116.c
diff --git a/drivers/media/dvb/frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h
index 7d90ab949c03..7d90ab949c03 100644
--- a/drivers/media/dvb/frontends/cx24116.h
+++ b/drivers/media/dvb-frontends/cx24116.h
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c
index 7e28b4ee7d4f..7e28b4ee7d4f 100644
--- a/drivers/media/dvb/frontends/cx24123.c
+++ b/drivers/media/dvb-frontends/cx24123.c
diff --git a/drivers/media/dvb/frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h
index 51ae866e9fed..51ae866e9fed 100644
--- a/drivers/media/dvb/frontends/cx24123.h
+++ b/drivers/media/dvb-frontends/cx24123.h
diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h
new file mode 100644
index 000000000000..6acc21c581c5
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r.h
@@ -0,0 +1,88 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef CXD2820R_H
+#define CXD2820R_H
+
+#include <linux/dvb/frontend.h>
+
+#define CXD2820R_GPIO_D (0 << 0) /* disable */
+#define CXD2820R_GPIO_E (1 << 0) /* enable */
+#define CXD2820R_GPIO_O (0 << 1) /* output */
+#define CXD2820R_GPIO_I (1 << 1) /* input */
+#define CXD2820R_GPIO_L (0 << 2) /* output low */
+#define CXD2820R_GPIO_H (1 << 2) /* output high */
+
+#define CXD2820R_TS_SERIAL 0x08
+#define CXD2820R_TS_SERIAL_MSB 0x28
+#define CXD2820R_TS_PARALLEL 0x30
+#define CXD2820R_TS_PARALLEL_MSB 0x70
+
+struct cxd2820r_config {
+ /* Demodulator I2C address.
+ * Driver determines DVB-C slave I2C address automatically from master
+ * address.
+ * Default: none, must set
+ * Values: 0x6c, 0x6d
+ */
+ u8 i2c_address;
+
+ /* TS output mode.
+ * Default: none, must set.
+ * Values:
+ */
+ u8 ts_mode;
+
+ /* IF AGC polarity.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool if_agc_polarity;
+
+ /* Spectrum inversion.
+ * Default: 0
+ * Values: 0, 1
+ */
+ bool spec_inv;
+};
+
+
+#if defined(CONFIG_DVB_CXD2820R) || \
+ (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE))
+extern struct dvb_frontend *cxd2820r_attach(
+ const struct cxd2820r_config *config,
+ struct i2c_adapter *i2c,
+ int *gpio_chip_base
+);
+#else
+static inline struct dvb_frontend *cxd2820r_attach(
+ const struct cxd2820r_config *config,
+ struct i2c_adapter *i2c,
+ int *gpio_chip_base
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+#endif
+
+#endif /* CXD2820R_H */
diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c
new file mode 100644
index 000000000000..125a44041011
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_c.c
@@ -0,0 +1,343 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 buf[2];
+ u32 if_freq;
+ u16 if_ctl;
+ u64 num;
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x01, 0xff },
+ { 0x00081, 0x05, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+
+ { 0x00082, 0x20, 0x60 },
+ { 0x1016a, 0x48, 0xff },
+ { 0x100a5, 0x00, 0x01 },
+ { 0x10020, 0x06, 0x07 },
+ { 0x10059, 0x50, 0xff },
+ { 0x10087, 0x0c, 0x3c },
+ { 0x1008b, 0x07, 0xff },
+ { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 },
+ { 0x10070, priv->cfg.ts_mode, 0xff },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__,
+ c->frequency, c->symbol_rate);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBC_ANNEX_A) {
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+ }
+
+ priv->delivery_system = SYS_DVBC_ANNEX_A;
+ priv->ber_running = 0; /* tune stops BER counter */
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+ if (ret)
+ goto error;
+ } else
+ if_freq = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq);
+
+ num = if_freq / 1000; /* Hz => kHz */
+ num *= 0x4000;
+ if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
+ buf[0] = (if_ctl >> 8) & 0x3f;
+ buf[1] = (if_ctl >> 0) & 0xff;
+
+ ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[2];
+
+ ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2);
+ if (ret)
+ goto error;
+
+ c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]);
+
+ ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->modulation = QAM_16;
+ break;
+ case 1:
+ c->modulation = QAM_32;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ case 3:
+ c->modulation = QAM_128;
+ break;
+ case 4:
+ c->modulation = QAM_256;
+ break;
+ }
+
+ switch ((buf[0] >> 7) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], start_ber = 0;
+ *ber = 0;
+
+ if (priv->ber_running) {
+ ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
+ *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
+ start_ber = 1;
+ }
+ } else {
+ priv->ber_running = 1;
+ start_ber = 1;
+ }
+
+ if (start_ber) {
+ /* (re)start BER */
+ ret = cxd2820r_wr_reg(priv, 0x10079, 0x01);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 tmp;
+
+ ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ tmp = (buf[0] & 0x03) << 8 | buf[1];
+ tmp = (~tmp & 0x03ff);
+
+ if (tmp == 512)
+ /* ~no signal */
+ tmp = 0;
+ else if (tmp > 350)
+ tmp = 350;
+
+ /* scale value to 0x0000-0xffff */
+ *strength = tmp * 0xffff / (350-0);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ unsigned int A, B;
+ /* report SNR in dB * 10 */
+
+ ret = cxd2820r_rd_reg(priv, 0x10019, &tmp);
+ if (ret)
+ goto error;
+
+ if (((tmp >> 0) & 0x03) % 2) {
+ A = 875;
+ B = 650;
+ } else {
+ A = 950;
+ B = 760;
+ }
+
+ ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp);
+ if (ret)
+ goto error;
+
+ #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */
+ if (tmp)
+ *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5)
+ / 10;
+ else
+ *snr = 0;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ /* no way to read ? */
+ return 0;
+}
+
+int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ *status = 0;
+
+ ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ if (((buf[0] >> 0) & 0x01) == 1) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+
+ if (((buf[1] >> 3) & 0x01) == 1) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: lock=%02x %02x\n", __func__, buf[0],
+ buf[1]);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_init_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_sleep_c(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s\n", __func__);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = 0; /* no zigzag */
+ s->max_drift = 0;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
new file mode 100644
index 000000000000..42648643693e
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -0,0 +1,757 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "cxd2820r_priv.h"
+
+/* write multiple registers */
+static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
+ u8 *val, int len)
+{
+ int ret;
+ u8 buf[len+1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = i2c,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
+ u8 *val, int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = i2c,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = i2c,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write multiple registers */
+int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len)
+{
+ int ret;
+ u8 i2c_addr;
+ u8 reg = (reginfo >> 0) & 0xff;
+ u8 bank = (reginfo >> 8) & 0xff;
+ u8 i2c = (reginfo >> 16) & 0x01;
+
+ /* select I2C */
+ if (i2c)
+ i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
+ else
+ i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
+
+ /* switch bank if needed */
+ if (bank != priv->bank[i2c]) {
+ ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
+ if (ret)
+ return ret;
+ priv->bank[i2c] = bank;
+ }
+ return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len);
+}
+
+/* read multiple registers */
+int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len)
+{
+ int ret;
+ u8 i2c_addr;
+ u8 reg = (reginfo >> 0) & 0xff;
+ u8 bank = (reginfo >> 8) & 0xff;
+ u8 i2c = (reginfo >> 16) & 0x01;
+
+ /* select I2C */
+ if (i2c)
+ i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
+ else
+ i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
+
+ /* switch bank if needed */
+ if (bank != priv->bank[i2c]) {
+ ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
+ if (ret)
+ return ret;
+ priv->bank[i2c] = bank;
+ }
+ return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len);
+}
+
+/* write single register */
+int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val)
+{
+ return cxd2820r_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val)
+{
+ return cxd2820r_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = cxd2820r_rd_reg(priv, reg, &tmp);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return cxd2820r_wr_reg(priv, reg, val);
+}
+
+int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u8 tmp0, tmp1;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ /* update GPIOs only when needed */
+ if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio)))
+ return 0;
+
+ tmp0 = 0x00;
+ tmp1 = 0x00;
+ for (i = 0; i < sizeof(priv->gpio); i++) {
+ /* enable / disable */
+ if (gpio[i] & CXD2820R_GPIO_E)
+ tmp0 |= (2 << 6) >> (2 * i);
+ else
+ tmp0 |= (1 << 6) >> (2 * i);
+
+ /* input / output */
+ if (gpio[i] & CXD2820R_GPIO_I)
+ tmp1 |= (1 << (3 + i));
+ else
+ tmp1 |= (0 << (3 + i));
+
+ /* high / low */
+ if (gpio[i] & CXD2820R_GPIO_H)
+ tmp1 |= (1 << (0 + i));
+ else
+ tmp1 |= (0 << (0 + i));
+
+ dev_dbg(&priv->i2c->dev, "%s: gpio i=%d %02x %02x\n", __func__,
+ i, tmp0, tmp1);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: wr gpio=%02x %02x\n", __func__, tmp0,
+ tmp1);
+
+ /* write bits [7:2] */
+ ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc);
+ if (ret)
+ goto error;
+
+ /* write bits [5:0] */
+ ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f);
+ if (ret)
+ goto error;
+
+ memcpy(priv->gpio, gpio, sizeof(priv->gpio));
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */
+u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor)
+{
+ return div_u64(dividend + (divisor / 2), divisor);
+}
+
+static int cxd2820r_set_frontend(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_init_t(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_t(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_init_t(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_t2(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_init_c(fe);
+ if (ret < 0)
+ goto err;
+ ret = cxd2820r_set_frontend_c(fe);
+ if (ret < 0)
+ goto err;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: error state=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+ ret = -EINVAL;
+ break;
+ }
+err:
+ return ret;
+}
+static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_status_t(fe, status);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_status_t2(fe, status);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_status_c(fe, status);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_get_frontend(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ if (priv->delivery_system == SYS_UNDEFINED)
+ return 0;
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_get_frontend_t(fe);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_get_frontend_t2(fe);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_get_frontend_c(fe);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_ber_t(fe, ber);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_ber_t2(fe, ber);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_ber_c(fe, ber);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_signal_strength_t(fe, strength);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_signal_strength_t2(fe, strength);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_signal_strength_c(fe, strength);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_snr_t(fe, snr);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_snr_t2(fe, snr);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_snr_c(fe, snr);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_read_ucblocks_t(fe, ucblocks);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_read_ucblocks_t2(fe, ucblocks);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_read_ucblocks_c(fe, ucblocks);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_init(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static int cxd2820r_sleep(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_sleep_t(fe);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_sleep_t2(fe);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_sleep_c(fe);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int cxd2820r_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ switch (fe->dtv_property_cache.delivery_system) {
+ case SYS_DVBT:
+ ret = cxd2820r_get_tune_settings_t(fe, s);
+ break;
+ case SYS_DVBT2:
+ ret = cxd2820r_get_tune_settings_t2(fe, s);
+ break;
+ case SYS_DVBC_ANNEX_A:
+ ret = cxd2820r_get_tune_settings_c(fe, s);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ fe_status_t status = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__,
+ fe->dtv_property_cache.delivery_system);
+
+ /* switch between DVB-T and DVB-T2 when tune fails */
+ if (priv->last_tune_failed) {
+ if (priv->delivery_system == SYS_DVBT) {
+ ret = cxd2820r_sleep_t(fe);
+ if (ret)
+ goto error;
+
+ c->delivery_system = SYS_DVBT2;
+ } else if (priv->delivery_system == SYS_DVBT2) {
+ ret = cxd2820r_sleep_t2(fe);
+ if (ret)
+ goto error;
+
+ c->delivery_system = SYS_DVBT;
+ }
+ }
+
+ /* set frontend */
+ ret = cxd2820r_set_frontend(fe);
+ if (ret)
+ goto error;
+
+
+ /* frontend lock wait loop count */
+ switch (priv->delivery_system) {
+ case SYS_DVBT:
+ case SYS_DVBC_ANNEX_A:
+ i = 20;
+ break;
+ case SYS_DVBT2:
+ i = 40;
+ break;
+ case SYS_UNDEFINED:
+ default:
+ i = 0;
+ break;
+ }
+
+ /* wait frontend lock */
+ for (; i > 0; i--) {
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+ msleep(50);
+ ret = cxd2820r_read_status(fe, &status);
+ if (ret)
+ goto error;
+
+ if (status & FE_HAS_LOCK)
+ break;
+ }
+
+ /* check if we have a valid signal */
+ if (status & FE_HAS_LOCK) {
+ priv->last_tune_failed = 0;
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ priv->last_tune_failed = 1;
+ return DVBFE_ALGO_SEARCH_AGAIN;
+ }
+
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static void cxd2820r_release(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int uninitialized_var(ret); /* silence compiler warning */
+
+ dev_dbg(&priv->i2c->dev, "%s\n", __func__);
+
+#ifdef CONFIG_GPIOLIB
+ /* remove GPIOs */
+ if (priv->gpio_chip.label) {
+ ret = gpiochip_remove(&priv->gpio_chip);
+ if (ret)
+ dev_err(&priv->i2c->dev, "%s: gpiochip_remove() " \
+ "failed=%d\n", KBUILD_MODNAME, ret);
+ }
+#endif
+ kfree(priv);
+ return;
+}
+
+static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s: %d\n", __func__, enable);
+
+ /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */
+ return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1);
+}
+
+#ifdef CONFIG_GPIOLIB
+static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
+ int val)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ return cxd2820r_gpio(&priv->fe, gpio);
+}
+
+static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+ u8 gpio[GPIO_COUNT];
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val);
+
+ memcpy(gpio, priv->gpio, sizeof(gpio));
+ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2);
+
+ (void) cxd2820r_gpio(&priv->fe, gpio);
+
+ return;
+}
+
+static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
+{
+ struct cxd2820r_priv *priv =
+ container_of(chip, struct cxd2820r_priv, gpio_chip);
+
+ dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr);
+
+ return (priv->gpio[nr] >> 2) & 0x01;
+}
+#endif
+
+static const struct dvb_frontend_ops cxd2820r_ops = {
+ .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
+ /* default: DVB-T/T2 */
+ .info = {
+ .name = "Sony CXD2820R",
+
+ .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_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 |
+ FE_CAN_QAM_256 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = cxd2820r_release,
+ .init = cxd2820r_init,
+ .sleep = cxd2820r_sleep,
+
+ .get_tune_settings = cxd2820r_get_tune_settings,
+ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
+
+ .get_frontend = cxd2820r_get_frontend,
+
+ .get_frontend_algo = cxd2820r_get_frontend_algo,
+ .search = cxd2820r_search,
+
+ .read_status = cxd2820r_read_status,
+ .read_snr = cxd2820r_read_snr,
+ .read_ber = cxd2820r_read_ber,
+ .read_ucblocks = cxd2820r_read_ucblocks,
+ .read_signal_strength = cxd2820r_read_signal_strength,
+};
+
+struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
+ struct i2c_adapter *i2c, int *gpio_chip_base
+)
+{
+ struct cxd2820r_priv *priv;
+ int ret;
+ u8 tmp, gpio[GPIO_COUNT];
+
+ priv = kzalloc(sizeof(struct cxd2820r_priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev, "%s: kzalloc() failed\n",
+ KBUILD_MODNAME);
+ goto error;
+ }
+
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, cfg, sizeof(struct cxd2820r_config));
+ memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ priv->bank[0] = priv->bank[1] = 0xff;
+ ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp);
+ dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, tmp);
+ if (ret || tmp != 0xe1)
+ goto error;
+
+ if (gpio_chip_base) {
+#ifdef CONFIG_GPIOLIB
+ /* add GPIOs */
+ priv->gpio_chip.label = KBUILD_MODNAME;
+ priv->gpio_chip.dev = &priv->i2c->dev;
+ priv->gpio_chip.owner = THIS_MODULE;
+ priv->gpio_chip.direction_output =
+ cxd2820r_gpio_direction_output;
+ priv->gpio_chip.set = cxd2820r_gpio_set;
+ priv->gpio_chip.get = cxd2820r_gpio_get;
+ priv->gpio_chip.base = -1; /* dynamic allocation */
+ priv->gpio_chip.ngpio = GPIO_COUNT;
+ priv->gpio_chip.can_sleep = 1;
+ ret = gpiochip_add(&priv->gpio_chip);
+ if (ret)
+ goto error;
+
+ dev_dbg(&priv->i2c->dev, "%s: gpio_chip.base=%d\n", __func__,
+ priv->gpio_chip.base);
+
+ *gpio_chip_base = priv->gpio_chip.base;
+#else
+ /*
+ * Use static GPIO configuration if GPIOLIB is undefined.
+ * This is fallback condition.
+ */
+ gpio[0] = (*gpio_chip_base >> 0) & 0x07;
+ gpio[1] = (*gpio_chip_base >> 3) & 0x07;
+ gpio[2] = 0;
+ ret = cxd2820r_gpio(&priv->fe, gpio);
+ if (ret)
+ goto error;
+#endif
+ }
+
+ return &priv->fe;
+error:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(cxd2820r_attach);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Sony CXD2820R demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h
new file mode 100644
index 000000000000..7ff5f60c83e1
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_priv.h
@@ -0,0 +1,148 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef CXD2820R_PRIV_H
+#define CXD2820R_PRIV_H
+
+#include <linux/dvb/version.h>
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "cxd2820r.h"
+#include <linux/gpio.h>
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct cxd2820r_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct cxd2820r_config cfg;
+
+ bool ber_running;
+
+ u8 bank[2];
+#define GPIO_COUNT 3
+ u8 gpio[GPIO_COUNT];
+#ifdef CONFIG_GPIOLIB
+ struct gpio_chip gpio_chip;
+#endif
+
+ fe_delivery_system_t delivery_system;
+ bool last_tune_failed; /* for switch between T and T2 tune */
+};
+
+/* cxd2820r_core.c */
+
+extern int cxd2820r_debug;
+
+int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio);
+
+int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
+ u8 mask);
+
+int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor);
+
+int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
+ int len);
+
+int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val);
+
+int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val);
+
+/* cxd2820r_c.c */
+
+int cxd2820r_get_frontend_c(struct dvb_frontend *fe);
+
+int cxd2820r_set_frontend_c(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status);
+
+int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber);
+
+int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength);
+
+int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr);
+
+int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks);
+
+int cxd2820r_init_c(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_c(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+/* cxd2820r_t.c */
+
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe);
+
+int cxd2820r_set_frontend_t(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status);
+
+int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber);
+
+int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength);
+
+int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr);
+
+int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks);
+
+int cxd2820r_init_t(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_t(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+/* cxd2820r_t2.c */
+
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe);
+
+int cxd2820r_set_frontend_t2(struct dvb_frontend *fe);
+
+int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status);
+
+int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber);
+
+int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength);
+
+int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr);
+
+int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks);
+
+int cxd2820r_init_t2(struct dvb_frontend *fe);
+
+int cxd2820r_sleep_t2(struct dvb_frontend *fe);
+
+int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s);
+
+#endif /* CXD2820R_PRIV_H */
diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c
new file mode 100644
index 000000000000..fa184ca2dd68
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_t.c
@@ -0,0 +1,449 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, bw_i;
+ u32 if_freq, if_ctl;
+ u64 num;
+ u8 buf[3], bw_param;
+ u8 bw_params1[][5] = {
+ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
+ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
+ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
+ };
+ u8 bw_params2[][2] = {
+ { 0x1f, 0xdc }, /* 6 MHz */
+ { 0x12, 0xf8 }, /* 7 MHz */
+ { 0x01, 0xe0 }, /* 8 MHz */
+ };
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x00, 0xff },
+ { 0x00081, 0x03, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+
+ { 0x00070, priv->cfg.ts_mode, 0xff },
+ { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 },
+ { 0x000a5, 0x00, 0x01 },
+ { 0x00082, 0x20, 0x60 },
+ { 0x000c2, 0xc3, 0xff },
+ { 0x0016a, 0x50, 0xff },
+ { 0x00427, 0x41, 0xff },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__,
+ c->frequency, c->bandwidth_hz);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bw_i = 0;
+ bw_param = 2;
+ break;
+ case 7000000:
+ bw_i = 1;
+ bw_param = 1;
+ break;
+ case 8000000:
+ bw_i = 2;
+ bw_param = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBT) {
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+ }
+
+ priv->delivery_system = SYS_DVBT;
+ priv->ber_running = 0; /* tune stops BER counter */
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+ if (ret)
+ goto error;
+ } else
+ if_freq = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq);
+
+ num = if_freq / 1000; /* Hz => kHz */
+ num *= 0x1000000;
+ if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
+ buf[0] = ((if_ctl >> 16) & 0xff);
+ buf[1] = ((if_ctl >> 8) & 0xff);
+ buf[2] = ((if_ctl >> 0) & 0xff);
+
+ ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[bw_i], 5);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[bw_i], 2);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_get_frontend_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[2];
+
+ ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 6) & 0x03) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[1] >> 1) & 0x03) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 0x03) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 3) & 0x07) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 5) & 0x07) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[3], start_ber = 0;
+ *ber = 0;
+
+ if (priv->ber_running) {
+ ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
+ *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
+ start_ber = 1;
+ }
+ } else {
+ priv->ber_running = 1;
+ start_ber = 1;
+ }
+
+ if (start_ber) {
+ /* (re)start BER */
+ ret = cxd2820r_wr_reg(priv, 0x00079, 0x01);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 tmp;
+
+ ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ tmp = (buf[0] & 0x0f) << 8 | buf[1];
+ tmp = ~tmp & 0x0fff;
+
+ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
+ *strength = tmp * 0xffff / 0x0fff;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 tmp;
+ /* report SNR in dB * 10 */
+
+ ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ tmp = (buf[0] & 0x1f) << 8 | buf[1];
+ #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
+ if (tmp)
+ *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
+ / 100);
+ else
+ *snr = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr,
+ tmp);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ /* no way to read ? */
+ return 0;
+}
+
+int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[4];
+ *status = 0;
+
+ ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]);
+ if (ret)
+ goto error;
+
+ if ((buf[0] & 0x07) == 6) {
+ ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]);
+ if (ret)
+ goto error;
+
+ if (((buf[1] >> 3) & 0x01) == 1) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ }
+ } else {
+ ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]);
+ if (ret)
+ goto error;
+
+ if ((buf[2] & 0x0f) >= 4) {
+ ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]);
+ if (ret)
+ goto error;
+
+ if (((buf[3] >> 4) & 0x01) == 1)
+ *status |= FE_HAS_SIGNAL;
+ }
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: lock=%*ph\n", __func__, 4, buf);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_init_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+
+ ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_sleep_t(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s\n", __func__);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c
new file mode 100644
index 000000000000..e82d82a7a2eb
--- /dev/null
+++ b/drivers/media/dvb-frontends/cxd2820r_t2.c
@@ -0,0 +1,423 @@
+/*
+ * Sony CXD2820R demodulator driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#include "cxd2820r_priv.h"
+
+int cxd2820r_set_frontend_t2(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, bw_i;
+ u32 if_freq, if_ctl;
+ u64 num;
+ u8 buf[3], bw_param;
+ u8 bw_params1[][5] = {
+ { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */
+ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
+ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
+ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
+ };
+ struct reg_val_mask tab[] = {
+ { 0x00080, 0x02, 0xff },
+ { 0x00081, 0x20, 0xff },
+ { 0x00085, 0x07, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x02069, 0x01, 0xff },
+
+ { 0x0207f, 0x2a, 0xff },
+ { 0x02082, 0x0a, 0xff },
+ { 0x02083, 0x0a, 0xff },
+ { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 },
+ { 0x02070, priv->cfg.ts_mode, 0xff },
+ { 0x020b5, priv->cfg.spec_inv << 4, 0x10 },
+ { 0x02567, 0x07, 0x0f },
+ { 0x02569, 0x03, 0x03 },
+ { 0x02595, 0x1a, 0xff },
+ { 0x02596, 0x50, 0xff },
+ { 0x02a8c, 0x00, 0xff },
+ { 0x02a8d, 0x34, 0xff },
+ { 0x02a45, 0x06, 0x07 },
+ { 0x03f10, 0x0d, 0xff },
+ { 0x03f11, 0x02, 0xff },
+ { 0x03f12, 0x01, 0xff },
+ { 0x03f23, 0x2c, 0xff },
+ { 0x03f51, 0x13, 0xff },
+ { 0x03f52, 0x01, 0xff },
+ { 0x03f53, 0x00, 0xff },
+ { 0x027e6, 0x14, 0xff },
+ { 0x02786, 0x02, 0x07 },
+ { 0x02787, 0x40, 0xe0 },
+ { 0x027ef, 0x10, 0x18 },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__,
+ c->frequency, c->bandwidth_hz);
+
+ switch (c->bandwidth_hz) {
+ case 5000000:
+ bw_i = 0;
+ bw_param = 3;
+ break;
+ case 6000000:
+ bw_i = 1;
+ bw_param = 2;
+ break;
+ case 7000000:
+ bw_i = 2;
+ bw_param = 1;
+ break;
+ case 8000000:
+ bw_i = 3;
+ bw_param = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ if (priv->delivery_system != SYS_DVBT2) {
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+ }
+
+ priv->delivery_system = SYS_DVBT2;
+
+ /* program IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency) {
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+ if (ret)
+ goto error;
+ } else
+ if_freq = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq);
+
+ num = if_freq / 1000; /* Hz => kHz */
+ num *= 0x1000000;
+ if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
+ buf[0] = ((if_ctl >> 16) & 0xff);
+ buf[1] = ((if_ctl >> 8) & 0xff);
+ buf[2] = ((if_ctl >> 0) & 0xff);
+
+ ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[bw_i], 5);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
+ if (ret)
+ goto error;
+
+ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+
+}
+
+int cxd2820r_get_frontend_t2(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[2];
+
+ ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ case 2:
+ c->transmission_mode = TRANSMISSION_MODE_4K;
+ break;
+ case 3:
+ c->transmission_mode = TRANSMISSION_MODE_1K;
+ break;
+ case 4:
+ c->transmission_mode = TRANSMISSION_MODE_16K;
+ break;
+ case 5:
+ c->transmission_mode = TRANSMISSION_MODE_32K;
+ break;
+ }
+
+ switch ((buf[1] >> 4) & 0x07) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ case 4:
+ c->guard_interval = GUARD_INTERVAL_1_128;
+ break;
+ case 5:
+ c->guard_interval = GUARD_INTERVAL_19_128;
+ break;
+ case 6:
+ c->guard_interval = GUARD_INTERVAL_19_256;
+ break;
+ }
+
+ ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0:
+ c->fec_inner = FEC_1_2;
+ break;
+ case 1:
+ c->fec_inner = FEC_3_5;
+ break;
+ case 2:
+ c->fec_inner = FEC_2_3;
+ break;
+ case 3:
+ c->fec_inner = FEC_3_4;
+ break;
+ case 4:
+ c->fec_inner = FEC_4_5;
+ break;
+ case 5:
+ c->fec_inner = FEC_5_6;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 0x07) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ case 3:
+ c->modulation = QAM_256;
+ break;
+ }
+
+ ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]);
+ if (ret)
+ goto error;
+
+ switch ((buf[0] >> 4) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[1];
+ *status = 0;
+
+ ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]);
+ if (ret)
+ goto error;
+
+ if ((buf[0] & 0x07) == 6) {
+ if (((buf[0] >> 5) & 0x01) == 1) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ }
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: lock=%02x\n", __func__, buf[0]);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[4];
+ unsigned int errbits;
+ *ber = 0;
+ /* FIXME: correct calculation */
+
+ ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ if ((buf[0] >> 4) & 0x01) {
+ errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+
+ if (errbits)
+ *ber = errbits * 64 / 16588800;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 tmp;
+
+ ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ tmp = (buf[0] & 0x0f) << 8 | buf[1];
+ tmp = ~tmp & 0x0fff;
+
+ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
+ *strength = tmp * 0xffff / 0x0fff;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 tmp;
+ /* report SNR in dB * 10 */
+
+ ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ tmp = (buf[0] & 0x0f) << 8 | buf[1];
+ #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
+ if (tmp)
+ *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
+ / 100);
+ else
+ *snr = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr,
+ tmp);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ /* no way to read ? */
+ return 0;
+}
+
+int cxd2820r_sleep_t2(struct dvb_frontend *fe)
+{
+ struct cxd2820r_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ struct reg_val_mask tab[] = {
+ { 0x000ff, 0x1f, 0xff },
+ { 0x00085, 0x00, 0xff },
+ { 0x00088, 0x01, 0xff },
+ { 0x02069, 0x00, 0xff },
+ { 0x00081, 0x00, 0xff },
+ { 0x00080, 0x00, 0xff },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s\n", __func__);
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 1500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c
index 3b024bfe980a..3b024bfe980a 100644
--- a/drivers/media/dvb/frontends/dib0070.c
+++ b/drivers/media/dvb-frontends/dib0070.c
diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h
index 45c31fae3967..45c31fae3967 100644
--- a/drivers/media/dvb/frontends/dib0070.h
+++ b/drivers/media/dvb-frontends/dib0070.h
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c
index d9fe60b4be48..d9fe60b4be48 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb-frontends/dib0090.c
diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h
index 781dc49de45b..781dc49de45b 100644
--- a/drivers/media/dvb/frontends/dib0090.h
+++ b/drivers/media/dvb-frontends/dib0090.h
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h
index 404f63a6f26b..404f63a6f26b 100644
--- a/drivers/media/dvb/frontends/dib3000.h
+++ b/drivers/media/dvb-frontends/dib3000.h
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c
index af91e0c92339..af91e0c92339 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb-frontends/dib3000mb.c
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb-frontends/dib3000mb_priv.h
index 9dc235aa44b7..9dc235aa44b7 100644
--- a/drivers/media/dvb/frontends/dib3000mb_priv.h
+++ b/drivers/media/dvb-frontends/dib3000mb_priv.h
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c
index ffad181a9692..ffad181a9692 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb-frontends/dib3000mc.c
diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h
index d75ffad2d752..d75ffad2d752 100644
--- a/drivers/media/dvb/frontends/dib3000mc.h
+++ b/drivers/media/dvb-frontends/dib3000mc.h
diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c
index 148bf79236fb..148bf79236fb 100644
--- a/drivers/media/dvb/frontends/dib7000m.c
+++ b/drivers/media/dvb-frontends/dib7000m.c
diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h
index 81fcf2241c64..81fcf2241c64 100644
--- a/drivers/media/dvb/frontends/dib7000m.h
+++ b/drivers/media/dvb-frontends/dib7000m.h
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index 3e1eefada0e8..3e1eefada0e8 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h
index b61b03a6e1ed..b61b03a6e1ed 100644
--- a/drivers/media/dvb/frontends/dib7000p.h
+++ b/drivers/media/dvb-frontends/dib7000p.h
diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c
index 1f3bcb5a1de8..1f3bcb5a1de8 100644
--- a/drivers/media/dvb/frontends/dib8000.c
+++ b/drivers/media/dvb-frontends/dib8000.c
diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h
index 39591bb172c1..39591bb172c1 100644
--- a/drivers/media/dvb/frontends/dib8000.h
+++ b/drivers/media/dvb-frontends/dib8000.h
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c
index 6201c59a78dd..6201c59a78dd 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb-frontends/dib9000.c
diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h
index b5781a48034c..b5781a48034c 100644
--- a/drivers/media/dvb/frontends/dib9000.h
+++ b/drivers/media/dvb-frontends/dib9000.h
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c
index 43be7238311e..43be7238311e 100644
--- a/drivers/media/dvb/frontends/dibx000_common.c
+++ b/drivers/media/dvb-frontends/dibx000_common.c
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h
index 5f484881d7b1..5f484881d7b1 100644
--- a/drivers/media/dvb/frontends/dibx000_common.h
+++ b/drivers/media/dvb-frontends/dibx000_common.h
diff --git a/drivers/media/dvb/frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h
index 216c8c3702f8..216c8c3702f8 100644
--- a/drivers/media/dvb/frontends/drxd.h
+++ b/drivers/media/dvb-frontends/drxd.h
diff --git a/drivers/media/dvb/frontends/drxd_firm.c b/drivers/media/dvb-frontends/drxd_firm.c
index 5418b0b1dadc..5418b0b1dadc 100644
--- a/drivers/media/dvb/frontends/drxd_firm.c
+++ b/drivers/media/dvb-frontends/drxd_firm.c
diff --git a/drivers/media/dvb/frontends/drxd_firm.h b/drivers/media/dvb-frontends/drxd_firm.h
index 41597e89941c..41597e89941c 100644
--- a/drivers/media/dvb/frontends/drxd_firm.h
+++ b/drivers/media/dvb-frontends/drxd_firm.h
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c
index f380eb43e9d5..f380eb43e9d5 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb-frontends/drxd_hard.c
diff --git a/drivers/media/dvb/frontends/drxd_map_firm.h b/drivers/media/dvb-frontends/drxd_map_firm.h
index 6bc553abf215..6bc553abf215 100644
--- a/drivers/media/dvb/frontends/drxd_map_firm.h
+++ b/drivers/media/dvb-frontends/drxd_map_firm.h
diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h
new file mode 100644
index 000000000000..94fecfbf14c1
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxk.h
@@ -0,0 +1,68 @@
+#ifndef _DRXK_H_
+#define _DRXK_H_
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/**
+ * struct drxk_config - Configure the initial parameters for DRX-K
+ *
+ * @adr: I2C Address of the DRX-K
+ * @parallel_ts: True means that the device uses parallel TS,
+ * Serial otherwise.
+ * @dynamic_clk: True means that the clock will be dynamically
+ * adjusted. Static clock otherwise.
+ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
+ * @single_master: Device is on the single master mode
+ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
+ * @antenna_gpio: GPIO bit used to control the antenna
+ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
+ * means that 1=DVBC, 0 = DVBT. Zero means the opposite.
+ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
+ * @microcode_name: Name of the firmware file with the microcode
+ * @qam_demod_parameter_count: The number of parameters used for the command
+ * to set the demodulator parameters. All
+ * firmwares are using the 2-parameter commmand.
+ * An exception is the "drxk_a3.mc" firmware,
+ * which uses the 4-parameter command.
+ * A value of 0 (default) or lower indicates that
+ * the correct number of parameters will be
+ * automatically detected.
+ * @load_firmware_sync: Force the firmware load to be synchronous.
+ *
+ * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
+ * UIO-3.
+ */
+struct drxk_config {
+ u8 adr;
+ bool single_master;
+ bool no_i2c_bridge;
+ bool parallel_ts;
+ bool dynamic_clk;
+ bool enable_merr_cfg;
+ bool load_firmware_sync;
+
+ bool antenna_dvbt;
+ u16 antenna_gpio;
+
+ u8 mpeg_out_clk_strength;
+ int chunk_size;
+
+ const char *microcode_name;
+ int qam_demod_parameter_count;
+};
+
+#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
+ && defined(MODULE))
+extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *drxk_attach(const struct drxk_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/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
new file mode 100644
index 000000000000..8b4c6d5f8f36
--- /dev/null
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -0,0 +1,6647 @@
+/*
+ * drxk_hard: DRX-K DVB-C/T demodulator driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/hardirq.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "drxk.h"
+#include "drxk_hard.h"
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode);
+static int PowerDownQAM(struct drxk_state *state);
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int SetQAMStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset);
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode);
+static int DVBTStart(struct drxk_state *state);
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset);
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus);
+static int SwitchAntennaToQAM(struct drxk_state *state);
+static int SwitchAntennaToDVBT(struct drxk_state *state);
+
+static bool IsDVBT(struct drxk_state *state)
+{
+ return state->m_OperationMode == OM_DVBT;
+}
+
+static bool IsQAM(struct drxk_state *state)
+{
+ return state->m_OperationMode == OM_QAM_ITU_A ||
+ state->m_OperationMode == OM_QAM_ITU_B ||
+ state->m_OperationMode == OM_QAM_ITU_C;
+}
+
+bool IsA1WithPatchCode(struct drxk_state *state)
+{
+ return state->m_DRXK_A1_PATCH_CODE;
+}
+
+bool IsA1WithRomCode(struct drxk_state *state)
+{
+ return state->m_DRXK_A1_ROM_CODE;
+}
+
+#define NOA1ROM 0
+
+#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
+#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
+
+#define DEFAULT_MER_83 165
+#define DEFAULT_MER_93 250
+
+#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02)
+#endif
+
+#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
+#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
+#endif
+
+#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
+#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
+
+#ifndef DRXK_KI_RAGC_ATV
+#define DRXK_KI_RAGC_ATV 4
+#endif
+#ifndef DRXK_KI_IAGC_ATV
+#define DRXK_KI_IAGC_ATV 6
+#endif
+#ifndef DRXK_KI_DAGC_ATV
+#define DRXK_KI_DAGC_ATV 7
+#endif
+
+#ifndef DRXK_KI_RAGC_QAM
+#define DRXK_KI_RAGC_QAM 3
+#endif
+#ifndef DRXK_KI_IAGC_QAM
+#define DRXK_KI_IAGC_QAM 4
+#endif
+#ifndef DRXK_KI_DAGC_QAM
+#define DRXK_KI_DAGC_QAM 7
+#endif
+#ifndef DRXK_KI_RAGC_DVBT
+#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2)
+#endif
+#ifndef DRXK_KI_IAGC_DVBT
+#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2)
+#endif
+#ifndef DRXK_KI_DAGC_DVBT
+#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7)
+#endif
+
+#ifndef DRXK_AGC_DAC_OFFSET
+#define DRXK_AGC_DAC_OFFSET (0x800)
+#endif
+
+#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ
+#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ
+#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L)
+#endif
+
+#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ
+#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L)
+#endif
+
+#ifndef DRXK_QAM_SYMBOLRATE_MAX
+#define DRXK_QAM_SYMBOLRATE_MAX (7233000)
+#endif
+
+#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64
+#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0
+#define DRXK_BL_ROM_OFFSET_TAPS_BG 24
+#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32
+#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40
+#define DRXK_BL_ROM_OFFSET_TAPS_FM 48
+#define DRXK_BL_ROM_OFFSET_UCODE 0
+
+#define DRXK_BLC_TIMEOUT 100
+
+#define DRXK_BLCC_NR_ELEMENTS_TAPS 2
+#define DRXK_BLCC_NR_ELEMENTS_UCODE 6
+
+#define DRXK_BLDC_NR_ELEMENTS_TAPS 28
+
+#ifndef DRXK_OFDM_NE_NOTCH_WIDTH
+#define DRXK_OFDM_NE_NOTCH_WIDTH (4)
+#endif
+
+#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960)
+#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480)
+#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008)
+#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992)
+#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520)
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \
+} while (0)
+
+
+static inline u32 MulDiv32(u32 a, u32 b, u32 c)
+{
+ u64 tmp64;
+
+ tmp64 = (u64) a * (u64) b;
+ do_div(tmp64, c);
+
+ return (u32) tmp64;
+}
+
+inline u32 Frac28a(u32 a, u32 c)
+{
+ int i = 0;
+ u32 Q1 = 0;
+ u32 R0 = 0;
+
+ R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */
+ Q1 = a / c; /* integer part, only the 4 least significant bits
+ will be visible in the result */
+
+ /* division using radix 16, 7 nibbles in the result */
+ for (i = 0; i < 7; i++) {
+ Q1 = (Q1 << 4) | (R0 / c);
+ R0 = (R0 % c) << 4;
+ }
+ /* rounding */
+ if ((R0 >> 3) >= c)
+ Q1++;
+
+ return Q1;
+}
+
+static u32 Log10Times100(u32 x)
+{
+ static const u8 scale = 15;
+ static const u8 indexWidth = 5;
+ u8 i = 0;
+ u32 y = 0;
+ u32 d = 0;
+ u32 k = 0;
+ u32 r = 0;
+ /*
+ log2lut[n] = (1<<scale) * 200 * log2(1.0 + ((1.0/(1<<INDEXWIDTH)) * n))
+ 0 <= n < ((1<<INDEXWIDTH)+1)
+ */
+
+ static const u32 log2lut[] = {
+ 0, /* 0.000000 */
+ 290941, /* 290941.300628 */
+ 573196, /* 573196.476418 */
+ 847269, /* 847269.179851 */
+ 1113620, /* 1113620.489452 */
+ 1372674, /* 1372673.576986 */
+ 1624818, /* 1624817.752104 */
+ 1870412, /* 1870411.981536 */
+ 2109788, /* 2109787.962654 */
+ 2343253, /* 2343252.817465 */
+ 2571091, /* 2571091.461923 */
+ 2793569, /* 2793568.696416 */
+ 3010931, /* 3010931.055901 */
+ 3223408, /* 3223408.452106 */
+ 3431216, /* 3431215.635215 */
+ 3634553, /* 3634553.498355 */
+ 3833610, /* 3833610.244726 */
+ 4028562, /* 4028562.434393 */
+ 4219576, /* 4219575.925308 */
+ 4406807, /* 4406806.721144 */
+ 4590402, /* 4590401.736809 */
+ 4770499, /* 4770499.491025 */
+ 4947231, /* 4947230.734179 */
+ 5120719, /* 5120719.018555 */
+ 5291081, /* 5291081.217197 */
+ 5458428, /* 5458427.996830 */
+ 5622864, /* 5622864.249668 */
+ 5784489, /* 5784489.488298 */
+ 5943398, /* 5943398.207380 */
+ 6099680, /* 6099680.215452 */
+ 6253421, /* 6253420.939751 */
+ 6404702, /* 6404701.706649 */
+ 6553600, /* 6553600.000000 */
+ };
+
+
+ if (x == 0)
+ return 0;
+
+ /* Scale x (normalize) */
+ /* computing y in log(x/y) = log(x) - log(y) */
+ if ((x & ((0xffffffff) << (scale + 1))) == 0) {
+ for (k = scale; k > 0; k--) {
+ if (x & (((u32) 1) << scale))
+ break;
+ x <<= 1;
+ }
+ } else {
+ for (k = scale; k < 31; k++) {
+ if ((x & (((u32) (-1)) << (scale + 1))) == 0)
+ break;
+ x >>= 1;
+ }
+ }
+ /*
+ Now x has binary point between bit[scale] and bit[scale-1]
+ and 1.0 <= x < 2.0 */
+
+ /* correction for divison: log(x) = log(x/y)+log(y) */
+ y = k * ((((u32) 1) << scale) * 200);
+
+ /* remove integer part */
+ x &= ((((u32) 1) << scale) - 1);
+ /* get index */
+ i = (u8) (x >> (scale - indexWidth));
+ /* compute delta (x - a) */
+ d = x & ((((u32) 1) << (scale - indexWidth)) - 1);
+ /* compute log, multiplication (d* (..)) must be within range ! */
+ y += log2lut[i] +
+ ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth));
+ /* Conver to log10() */
+ y /= 108853; /* (log2(10) << scale) */
+ r = (y >> 1);
+ /* rounding */
+ if (y & ((u32) 1))
+ r++;
+ return r;
+}
+
+/****************************************************************************/
+/* I2C **********************************************************************/
+/****************************************************************************/
+
+static int drxk_i2c_lock(struct drxk_state *state)
+{
+ i2c_lock_adapter(state->i2c);
+ state->drxk_i2c_exclusive_lock = true;
+
+ return 0;
+}
+
+static void drxk_i2c_unlock(struct drxk_state *state)
+{
+ if (!state->drxk_i2c_exclusive_lock)
+ return;
+
+ i2c_unlock_adapter(state->i2c);
+ state->drxk_i2c_exclusive_lock = false;
+}
+
+static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
+ unsigned len)
+{
+ if (state->drxk_i2c_exclusive_lock)
+ return __i2c_transfer(state->i2c, msgs, len);
+ else
+ return i2c_transfer(state->i2c, msgs, len);
+}
+
+static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1}
+ };
+
+ return drxk_i2c_transfer(state, msgs, 1);
+}
+
+static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len)
+{
+ int status;
+ struct i2c_msg msg = {
+ .addr = adr, .flags = 0, .buf = data, .len = len };
+
+ dprintk(3, ":");
+ if (debug > 2) {
+ int i;
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", data[i]);
+ printk(KERN_CONT "\n");
+ }
+ status = drxk_i2c_transfer(state, &msg, 1);
+ if (status >= 0 && status != 1)
+ status = -EIO;
+
+ if (status < 0)
+ printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr);
+
+ return status;
+}
+
+static int i2c_read(struct drxk_state *state,
+ u8 adr, u8 *msg, int len, u8 *answ, int alen)
+{
+ int status;
+ struct i2c_msg msgs[2] = {
+ {.addr = adr, .flags = 0,
+ .buf = msg, .len = len},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = answ, .len = alen}
+ };
+
+ status = drxk_i2c_transfer(state, msgs, 2);
+ if (status != 2) {
+ if (debug > 2)
+ printk(KERN_CONT ": ERROR!\n");
+ if (status >= 0)
+ status = -EIO;
+
+ printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr);
+ return status;
+ }
+ if (debug > 2) {
+ int i;
+ dprintk(2, ": read from");
+ for (i = 0; i < len; i++)
+ printk(KERN_CONT " %02x", msg[i]);
+ printk(KERN_CONT ", value = ");
+ for (i = 0; i < alen; i++)
+ printk(KERN_CONT " %02x", answ[i]);
+ printk(KERN_CONT "\n");
+ }
+ return 0;
+}
+
+static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[2], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state, adr, mm1, len, mm2, 2);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8);
+
+ return 0;
+}
+
+static int read16(struct drxk_state *state, u32 reg, u16 *data)
+{
+ return read16_flags(state, reg, data, 0);
+}
+
+static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
+{
+ int status;
+ u8 adr = state->demod_address, mm1[4], mm2[4], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm1[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm1[1] = ((reg >> 16) & 0xFF);
+ mm1[2] = ((reg >> 24) & 0xFF) | flags;
+ mm1[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm1[0] = ((reg << 1) & 0xFF);
+ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
+ status = i2c_read(state, adr, mm1, len, mm2, 4);
+ if (status < 0)
+ return status;
+ if (data)
+ *data = mm2[0] | (mm2[1] << 8) |
+ (mm2[2] << 16) | (mm2[3] << 24);
+
+ return 0;
+}
+
+static int read32(struct drxk_state *state, u32 reg, u32 *data)
+{
+ return read32_flags(state, reg, data, 0);
+}
+
+static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[6], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+
+ dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
+ return i2c_write(state, adr, mm, len + 2);
+}
+
+static int write16(struct drxk_state *state, u32 reg, u16 data)
+{
+ return write16_flags(state, reg, data, 0);
+}
+
+static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
+{
+ u8 adr = state->demod_address, mm[8], len;
+
+ if (state->single_master)
+ flags |= 0xC0;
+ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
+ mm[0] = (((reg << 1) & 0xFF) | 0x01);
+ mm[1] = ((reg >> 16) & 0xFF);
+ mm[2] = ((reg >> 24) & 0xFF) | flags;
+ mm[3] = ((reg >> 7) & 0xFF);
+ len = 4;
+ } else {
+ mm[0] = ((reg << 1) & 0xFF);
+ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
+ len = 2;
+ }
+ mm[len] = data & 0xff;
+ mm[len + 1] = (data >> 8) & 0xff;
+ mm[len + 2] = (data >> 16) & 0xff;
+ mm[len + 3] = (data >> 24) & 0xff;
+ dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
+
+ return i2c_write(state, adr, mm, len + 4);
+}
+
+static int write32(struct drxk_state *state, u32 reg, u32 data)
+{
+ return write32_flags(state, reg, data, 0);
+}
+
+static int write_block(struct drxk_state *state, u32 Address,
+ const int BlockSize, const u8 pBlock[])
+{
+ int status = 0, BlkSize = BlockSize;
+ u8 Flags = 0;
+
+ if (state->single_master)
+ Flags |= 0xC0;
+
+ while (BlkSize > 0) {
+ int Chunk = BlkSize > state->m_ChunkSize ?
+ state->m_ChunkSize : BlkSize;
+ u8 *AdrBuf = &state->Chunk[0];
+ u32 AdrLength = 0;
+
+ if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) {
+ AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01);
+ AdrBuf[1] = ((Address >> 16) & 0xFF);
+ AdrBuf[2] = ((Address >> 24) & 0xFF);
+ AdrBuf[3] = ((Address >> 7) & 0xFF);
+ AdrBuf[2] |= Flags;
+ AdrLength = 4;
+ if (Chunk == state->m_ChunkSize)
+ Chunk -= 2;
+ } else {
+ AdrBuf[0] = ((Address << 1) & 0xFF);
+ AdrBuf[1] = (((Address >> 16) & 0x0F) |
+ ((Address >> 18) & 0xF0));
+ AdrLength = 2;
+ }
+ memcpy(&state->Chunk[AdrLength], pBlock, Chunk);
+ dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags);
+ if (debug > 1) {
+ int i;
+ if (pBlock)
+ for (i = 0; i < Chunk; i++)
+ printk(KERN_CONT " %02x", pBlock[i]);
+ printk(KERN_CONT "\n");
+ }
+ status = i2c_write(state, state->demod_address,
+ &state->Chunk[0], Chunk + AdrLength);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
+ __func__, Address);
+ break;
+ }
+ pBlock += Chunk;
+ Address += (Chunk >> 1);
+ BlkSize -= Chunk;
+ }
+ return status;
+}
+
+#ifndef DRXK_MAX_RETRIES_POWERUP
+#define DRXK_MAX_RETRIES_POWERUP 20
+#endif
+
+int PowerUpDevice(struct drxk_state *state)
+{
+ int status;
+ u8 data = 0;
+ u16 retryCount = 0;
+
+ dprintk(1, "\n");
+
+ status = i2c_read1(state, state->demod_address, &data);
+ if (status < 0) {
+ do {
+ data = 0;
+ status = i2c_write(state, state->demod_address,
+ &data, 1);
+ msleep(10);
+ retryCount++;
+ if (status < 0)
+ continue;
+ status = i2c_read1(state, state->demod_address,
+ &data);
+ } while (status < 0 &&
+ (retryCount < DRXK_MAX_RETRIES_POWERUP));
+ if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP)
+ goto error;
+ }
+
+ /* Make sure all clk domains are active */
+ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /* Enable pll lock tests */
+ status = write16(state, SIO_CC_PLL_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ state->m_currentPowerMode = DRX_POWER_UP;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+static int init_state(struct drxk_state *state)
+{
+ /*
+ * FIXME: most (all?) of the values bellow should be moved into
+ * struct drxk_config, as they are probably board-specific
+ */
+ u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulVSBIfAgcOutputLevel = 0;
+ u32 ulVSBIfAgcMinLevel = 0;
+ u32 ulVSBIfAgcMaxLevel = 0x7FFF;
+ u32 ulVSBIfAgcSpeed = 3;
+
+ u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulVSBRfAgcOutputLevel = 0;
+ u32 ulVSBRfAgcMinLevel = 0;
+ u32 ulVSBRfAgcMaxLevel = 0x7FFF;
+ u32 ulVSBRfAgcSpeed = 3;
+ u32 ulVSBRfAgcTop = 9500;
+ u32 ulVSBRfAgcCutOffCurrent = 4000;
+
+ u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO;
+ u32 ulATVIfAgcOutputLevel = 0;
+ u32 ulATVIfAgcMinLevel = 0;
+ u32 ulATVIfAgcMaxLevel = 0;
+ u32 ulATVIfAgcSpeed = 3;
+
+ u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF;
+ u32 ulATVRfAgcOutputLevel = 0;
+ u32 ulATVRfAgcMinLevel = 0;
+ u32 ulATVRfAgcMaxLevel = 0;
+ u32 ulATVRfAgcTop = 9500;
+ u32 ulATVRfAgcCutOffCurrent = 4000;
+ u32 ulATVRfAgcSpeed = 3;
+
+ u32 ulQual83 = DEFAULT_MER_83;
+ u32 ulQual93 = DEFAULT_MER_93;
+
+ u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+
+ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
+ /* io_pad_cfg_mode output mode is drive always */
+ /* io_pad_cfg_drive is set to power 2 (23 mA) */
+ u32 ulGPIOCfg = 0x0113;
+ u32 ulInvertTSClock = 0;
+ u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
+ u32 ulDVBTBitrate = 50000000;
+ u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
+
+ u32 ulInsertRSByte = 0;
+
+ u32 ulRfMirror = 1;
+ u32 ulPowerDown = 0;
+
+ dprintk(1, "\n");
+
+ state->m_hasLNA = false;
+ state->m_hasDVBT = false;
+ state->m_hasDVBC = false;
+ state->m_hasATV = false;
+ state->m_hasOOB = false;
+ state->m_hasAudio = false;
+
+ if (!state->m_ChunkSize)
+ state->m_ChunkSize = 124;
+
+ state->m_oscClockFreq = 0;
+ state->m_smartAntInverted = false;
+ state->m_bPDownOpenBridge = false;
+
+ /* real system clock frequency in kHz */
+ state->m_sysClockFreq = 151875;
+ /* Timing div, 250ns/Psys */
+ /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
+ state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) *
+ HI_I2C_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
+ state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
+ state->m_HICfgWakeUpKey = (state->demod_address << 1);
+ /* port/bridge/power down ctrl */
+ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ state->m_bPowerDown = (ulPowerDown != 0);
+
+ state->m_DRXK_A1_PATCH_CODE = false;
+ state->m_DRXK_A1_ROM_CODE = false;
+ state->m_DRXK_A2_ROM_CODE = false;
+ state->m_DRXK_A3_ROM_CODE = false;
+ state->m_DRXK_A2_PATCH_CODE = false;
+ state->m_DRXK_A3_PATCH_CODE = false;
+
+ /* Init AGC and PGA parameters */
+ /* VSB IF */
+ state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode);
+ state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel);
+ state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel);
+ state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel);
+ state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed);
+ state->m_vsbPgaCfg = 140;
+
+ /* VSB RF */
+ state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode);
+ state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel);
+ state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel);
+ state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel);
+ state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed);
+ state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop);
+ state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent);
+ state->m_vsbPreSawCfg.reference = 0x07;
+ state->m_vsbPreSawCfg.usePreSaw = true;
+
+ state->m_Quality83percent = DEFAULT_MER_83;
+ state->m_Quality93percent = DEFAULT_MER_93;
+ if (ulQual93 <= 500 && ulQual83 < ulQual93) {
+ state->m_Quality83percent = ulQual83;
+ state->m_Quality93percent = ulQual93;
+ }
+
+ /* ATV IF */
+ state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode);
+ state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel);
+ state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel);
+ state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel);
+ state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed);
+
+ /* ATV RF */
+ state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode);
+ state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel);
+ state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel);
+ state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel);
+ state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed);
+ state->m_atvRfAgcCfg.top = (ulATVRfAgcTop);
+ state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent);
+ state->m_atvPreSawCfg.reference = 0x04;
+ state->m_atvPreSawCfg.usePreSaw = true;
+
+
+ /* DVBT RF */
+ state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+ state->m_dvbtRfAgcCfg.outputLevel = 0;
+ state->m_dvbtRfAgcCfg.minOutputLevel = 0;
+ state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF;
+ state->m_dvbtRfAgcCfg.top = 0x2100;
+ state->m_dvbtRfAgcCfg.cutOffCurrent = 4000;
+ state->m_dvbtRfAgcCfg.speed = 1;
+
+
+ /* DVBT IF */
+ state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+ state->m_dvbtIfAgcCfg.outputLevel = 0;
+ state->m_dvbtIfAgcCfg.minOutputLevel = 0;
+ state->m_dvbtIfAgcCfg.maxOutputLevel = 9000;
+ state->m_dvbtIfAgcCfg.top = 13424;
+ state->m_dvbtIfAgcCfg.cutOffCurrent = 0;
+ state->m_dvbtIfAgcCfg.speed = 3;
+ state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30;
+ state->m_dvbtIfAgcCfg.IngainTgtMax = 30000;
+ /* state->m_dvbtPgaCfg = 140; */
+
+ state->m_dvbtPreSawCfg.reference = 4;
+ state->m_dvbtPreSawCfg.usePreSaw = false;
+
+ /* QAM RF */
+ state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
+ state->m_qamRfAgcCfg.outputLevel = 0;
+ state->m_qamRfAgcCfg.minOutputLevel = 6023;
+ state->m_qamRfAgcCfg.maxOutputLevel = 27000;
+ state->m_qamRfAgcCfg.top = 0x2380;
+ state->m_qamRfAgcCfg.cutOffCurrent = 4000;
+ state->m_qamRfAgcCfg.speed = 3;
+
+ /* QAM IF */
+ state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
+ state->m_qamIfAgcCfg.outputLevel = 0;
+ state->m_qamIfAgcCfg.minOutputLevel = 0;
+ state->m_qamIfAgcCfg.maxOutputLevel = 9000;
+ state->m_qamIfAgcCfg.top = 0x0511;
+ state->m_qamIfAgcCfg.cutOffCurrent = 0;
+ state->m_qamIfAgcCfg.speed = 3;
+ state->m_qamIfAgcCfg.IngainTgtMax = 5119;
+ state->m_qamIfAgcCfg.FastClipCtrlDelay = 50;
+
+ state->m_qamPgaCfg = 140;
+ state->m_qamPreSawCfg.reference = 4;
+ state->m_qamPreSawCfg.usePreSaw = false;
+
+ state->m_OperationMode = OM_NONE;
+ state->m_DrxkState = DRXK_UNINITIALIZED;
+
+ /* MPEG output configuration */
+ state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */
+ state->m_insertRSByte = false; /* If TRUE; insert RS byte */
+ state->m_invertDATA = false; /* If TRUE; invert DATA signals */
+ state->m_invertERR = false; /* If TRUE; invert ERR signal */
+ state->m_invertSTR = false; /* If TRUE; invert STR signals */
+ state->m_invertVAL = false; /* If TRUE; invert VAL signals */
+ state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
+
+ /* If TRUE; static MPEG clockrate will be used;
+ otherwise clockrate will adapt to the bitrate of the TS */
+
+ state->m_DVBTBitrate = ulDVBTBitrate;
+ state->m_DVBCBitrate = ulDVBCBitrate;
+
+ state->m_TSDataStrength = (ulTSDataStrength & 0x07);
+
+ /* Maximum bitrate in b/s in case static clockrate is selected */
+ state->m_mpegTsStaticBitrate = 19392658;
+ state->m_disableTEIhandling = false;
+
+ if (ulInsertRSByte)
+ state->m_insertRSByte = true;
+
+ state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
+ if (ulMpegLockTimeOut < 10000)
+ state->m_MpegLockTimeOut = ulMpegLockTimeOut;
+ state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
+ if (ulDemodLockTimeOut < 10000)
+ state->m_DemodLockTimeOut = ulDemodLockTimeOut;
+
+ /* QAM defaults */
+ state->m_Constellation = DRX_CONSTELLATION_AUTO;
+ state->m_qamInterleaveMode = DRXK_QAM_I12_J17;
+ state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */
+ state->m_fecRsPrescale = 1;
+
+ state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM;
+ state->m_agcFastClipCtrlDelay = 0;
+
+ state->m_GPIOCfg = (ulGPIOCfg);
+
+ state->m_bPowerDown = false;
+ state->m_currentPowerMode = DRX_POWER_DOWN;
+
+ state->m_rfmirror = (ulRfMirror == 0);
+ state->m_IfAgcPol = false;
+ return 0;
+}
+
+static int DRXX_Open(struct drxk_state *state)
+{
+ int status = 0;
+ u32 jtag = 0;
+ u16 bid = 0;
+ u16 key = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ /* Check device id */
+ status = read16(state, SIO_TOP_COMM_KEY__A, &key);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, key);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int GetDeviceCapabilities(struct drxk_state *state)
+{
+ u16 sioPdrOhwCfg = 0;
+ u32 sioTopJtagidLo = 0;
+ int status;
+ const char *spin = "";
+
+ dprintk(1, "\n");
+
+ /* driver 0.9.0 */
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+ if (status < 0)
+ goto error;
+
+ switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
+ case 0:
+ /* ignore (bypass ?) */
+ break;
+ case 1:
+ /* 27 MHz */
+ state->m_oscClockFreq = 27000;
+ break;
+ case 2:
+ /* 20.25 MHz */
+ state->m_oscClockFreq = 20250;
+ break;
+ case 3:
+ /* 4 MHz */
+ state->m_oscClockFreq = 20250;
+ break;
+ default:
+ printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n");
+ return -EINVAL;
+ }
+ /*
+ Determine device capabilities
+ Based on pinning v14
+ */
+ status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo);
+ if (status < 0)
+ goto error;
+
+ printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo);
+
+ /* driver 0.9.0 */
+ switch ((sioTopJtagidLo >> 29) & 0xF) {
+ case 0:
+ state->m_deviceSpin = DRXK_SPIN_A1;
+ spin = "A1";
+ break;
+ case 2:
+ state->m_deviceSpin = DRXK_SPIN_A2;
+ spin = "A2";
+ break;
+ case 3:
+ state->m_deviceSpin = DRXK_SPIN_A3;
+ spin = "A3";
+ break;
+ default:
+ state->m_deviceSpin = DRXK_SPIN_UNKNOWN;
+ status = -EINVAL;
+ printk(KERN_ERR "drxk: Spin %d unknown\n",
+ (sioTopJtagidLo >> 29) & 0xF);
+ goto error2;
+ }
+ switch ((sioTopJtagidLo >> 12) & 0xFF) {
+ case 0x13:
+ /* typeId = DRX3913K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = false;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = false;
+ state->m_hasGPIO1 = false;
+ state->m_hasIRQN = false;
+ break;
+ case 0x15:
+ /* typeId = DRX3915K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x16:
+ /* typeId = DRX3916K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x18:
+ /* typeId = DRX3918K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = false;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x21:
+ /* typeId = DRX3921K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x23:
+ /* typeId = DRX3923K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x25:
+ /* typeId = DRX3925K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = true;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ case 0x26:
+ /* typeId = DRX3926K_TYPE_ID */
+ state->m_hasLNA = false;
+ state->m_hasOOB = false;
+ state->m_hasATV = true;
+ state->m_hasAudio = false;
+ state->m_hasDVBT = true;
+ state->m_hasDVBC = true;
+ state->m_hasSAWSW = true;
+ state->m_hasGPIO2 = true;
+ state->m_hasGPIO1 = true;
+ state->m_hasIRQN = false;
+ break;
+ default:
+ printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n",
+ ((sioTopJtagidLo >> 12) & 0xFF));
+ status = -EINVAL;
+ goto error2;
+ }
+
+ printk(KERN_INFO
+ "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
+ ((sioTopJtagidLo >> 12) & 0xFF), spin,
+ state->m_oscClockFreq / 1000,
+ state->m_oscClockFreq % 1000);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+error2:
+ return status;
+}
+
+static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult)
+{
+ int status;
+ bool powerdown_cmd;
+
+ dprintk(1, "\n");
+
+ /* Write command */
+ status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd);
+ if (status < 0)
+ goto error;
+ if (cmd == SIO_HI_RA_RAM_CMD_RESET)
+ msleep(1);
+
+ powerdown_cmd =
+ (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
+ ((state->m_HICfgCtrl) &
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
+ if (powerdown_cmd == false) {
+ /* Wait until command rdy */
+ u32 retryCount = 0;
+ u16 waitCmd;
+
+ do {
+ msleep(1);
+ retryCount += 1;
+ status = read16(state, SIO_HI_RA_RAM_CMD__A,
+ &waitCmd);
+ } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES)
+ && (waitCmd != 0));
+ if (status < 0)
+ goto error;
+ status = read16(state, SIO_HI_RA_RAM_RES__A, pResult);
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int HI_CfgCommand(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
+ if (status < 0)
+ goto error;
+
+ state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+error:
+ mutex_unlock(&state->mutex);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int InitHI(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ state->m_HICfgWakeUpKey = (state->demod_address << 1);
+ state->m_HICfgTimeout = 0x96FF;
+ /* port/bridge/power down ctrl */
+ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
+
+ return HI_CfgCommand(state);
+}
+
+static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
+{
+ int status = -1;
+ u16 sioPdrMclkCfg = 0;
+ u16 sioPdrMdxCfg = 0;
+ u16 err_cfg = 0;
+
+ dprintk(1, ": mpeg %s, %s mode\n",
+ mpegEnable ? "enable" : "disable",
+ state->m_enableParallel ? "parallel" : "serial");
+
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* MPEG TS pad configuration */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
+ if (status < 0)
+ goto error;
+
+ if (mpegEnable == false) {
+ /* Set MPEG TS pads to inputmode */
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ } else {
+ /* Enable MPEG output */
+ sioPdrMdxCfg =
+ ((state->m_TSDataStrength <<
+ SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
+ sioPdrMclkCfg = ((state->m_TSClockkStrength <<
+ SIO_PDR_MCLK_CFG_DRIVE__B) |
+ 0x0003);
+
+ status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+
+ if (state->enable_merr_cfg)
+ err_cfg = sioPdrMdxCfg;
+
+ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
+ if (status < 0)
+ goto error;
+
+ if (state->m_enableParallel == true) {
+ /* paralel -> enable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ } else {
+ sioPdrMdxCfg = ((state->m_TSDataStrength <<
+ SIO_PDR_MD0_CFG_DRIVE__B)
+ | 0x0003);
+ /* serial -> disable MD1 to MD7 */
+ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ }
+ status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg);
+ if (status < 0)
+ goto error;
+ }
+ /* Enable MB output over MPEG pads and ctl input */
+ status = write16(state, SIO_PDR_MON_CFG__A, 0x0000);
+ if (status < 0)
+ goto error;
+ /* Write nomagic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSDisable(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ return MPEGTSConfigurePins(state, false);
+}
+
+static int BLChainCmd(struct drxk_state *state,
+ u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+ u16 blStatus = 0;
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(timeOut);
+ do {
+ msleep(1);
+ status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ if (status < 0)
+ goto error;
+ } while ((blStatus == 0x1) &&
+ ((time_is_after_jiffies(end))));
+
+ if (blStatus == 0x1) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+
+static int DownloadMicrocode(struct drxk_state *state,
+ const u8 pMCImage[], u32 Length)
+{
+ const u8 *pSrc = pMCImage;
+ u32 Address;
+ u16 nBlocks;
+ u16 BlockSize;
+ u32 offset = 0;
+ u32 i;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* down the drain (we don't care about MAGIC_WORD) */
+#if 0
+ /* For future reference */
+ Drain = (pSrc[0] << 8) | pSrc[1];
+#endif
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+ nBlocks = (pSrc[0] << 8) | pSrc[1];
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ for (i = 0; i < nBlocks; i += 1) {
+ Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
+ (pSrc[2] << 8) | pSrc[3];
+ pSrc += sizeof(u32);
+ offset += sizeof(u32);
+
+ BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+#if 0
+ /* For future reference */
+ Flags = (pSrc[0] << 8) | pSrc[1];
+#endif
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+#if 0
+ /* For future reference */
+ BlockCRC = (pSrc[0] << 8) | pSrc[1];
+#endif
+ pSrc += sizeof(u16);
+ offset += sizeof(u16);
+
+ if (offset + BlockSize > Length) {
+ printk(KERN_ERR "drxk: Firmware is corrupted.\n");
+ return -EINVAL;
+ }
+
+ status = write_block(state, Address, BlockSize, pSrc);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d while loading firmware\n", status);
+ break;
+ }
+ pSrc += BlockSize;
+ offset += BlockSize;
+ }
+ return status;
+}
+
+static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable)
+{
+ int status;
+ u16 data = 0;
+ u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
+ u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ if (enable == false) {
+ desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
+ desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
+ }
+
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if (status >= 0 && data == desiredStatus) {
+ /* tokenring already has correct status */
+ return status;
+ }
+ /* Disable/enable dvbt tokenring bridge */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl);
+
+ end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
+ do {
+ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
+ if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end))
+ break;
+ msleep(1);
+ } while (1);
+ if (data != desiredStatus) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ return -EINVAL;
+ }
+ return status;
+}
+
+static int MPEGTSStop(struct drxk_state *state)
+{
+ int status = 0;
+ u16 fecOcSncMode = 0;
+ u16 fecOcIprMode = 0;
+
+ dprintk(1, "\n");
+
+ /* Gracefull shutdown (byte boundaries) */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ if (status < 0)
+ goto error;
+ fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ if (status < 0)
+ goto error;
+
+ /* Suppress MCLK during absence of data */
+ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode);
+ if (status < 0)
+ goto error;
+ fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
+ status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int scu_command(struct drxk_state *state,
+ u16 cmd, u8 parameterLen,
+ u16 *parameter, u8 resultLen, u16 *result)
+{
+#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
+#error DRXK register mapping no longer compatible with this routine!
+#endif
+ u16 curCmd = 0;
+ int status = -EINVAL;
+ unsigned long end;
+ u8 buffer[34];
+ int cnt = 0, ii;
+ const char *p;
+ char errname[30];
+
+ dprintk(1, "\n");
+
+ if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) ||
+ ((resultLen > 0) && (result == NULL))) {
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+ }
+
+ mutex_lock(&state->mutex);
+
+ /* assume that the command register is ready
+ since it is checked afterwards */
+ for (ii = parameterLen - 1; ii >= 0; ii -= 1) {
+ buffer[cnt++] = (parameter[ii] & 0xFF);
+ buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
+ }
+ buffer[cnt++] = (cmd & 0xFF);
+ buffer[cnt++] = ((cmd >> 8) & 0xFF);
+
+ write_block(state, SCU_RAM_PARAM_0__A -
+ (parameterLen - 1), cnt, buffer);
+ /* Wait until SCU has processed command */
+ end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
+ do {
+ msleep(1);
+ status = read16(state, SCU_RAM_COMMAND__A, &curCmd);
+ if (status < 0)
+ goto error;
+ } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
+ if (curCmd != DRX_SCU_READY) {
+ printk(KERN_ERR "drxk: SCU not ready\n");
+ status = -EIO;
+ goto error2;
+ }
+ /* read results */
+ if ((resultLen > 0) && (result != NULL)) {
+ s16 err;
+ int ii;
+
+ for (ii = resultLen - 1; ii >= 0; ii -= 1) {
+ status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Check if an error was reported by SCU */
+ err = (s16)result[0];
+ if (err >= 0)
+ goto error;
+
+ /* check for the known error codes */
+ switch (err) {
+ case SCU_RESULT_UNKCMD:
+ p = "SCU_RESULT_UNKCMD";
+ break;
+ case SCU_RESULT_UNKSTD:
+ p = "SCU_RESULT_UNKSTD";
+ break;
+ case SCU_RESULT_SIZE:
+ p = "SCU_RESULT_SIZE";
+ break;
+ case SCU_RESULT_INVPAR:
+ p = "SCU_RESULT_INVPAR";
+ break;
+ default: /* Other negative values are errors */
+ sprintf(errname, "ERROR: %d\n", err);
+ p = errname;
+ }
+ printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd);
+ print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
+ status = -EINVAL;
+ goto error2;
+ }
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+}
+
+static int SetIqmAf(struct drxk_state *state, bool active)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Configure IQM */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+
+ if (!active) {
+ data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY
+ | IQM_AF_STDBY_STDBY_AMP_STANDBY
+ | IQM_AF_STDBY_STDBY_PD_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY
+ | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY);
+ } else {
+ data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_AMP_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_PD_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY)
+ & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY)
+ );
+ }
+ status = write16(state, IQM_AF_STDBY__A, data);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode)
+{
+ int status = 0;
+ u16 sioCcPwdMode = 0;
+
+ dprintk(1, "\n");
+
+ /* Check arguments */
+ if (mode == NULL)
+ return -EINVAL;
+
+ switch (*mode) {
+ case DRX_POWER_UP:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE;
+ break;
+ case DRXK_POWER_DOWN_OFDM:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM;
+ break;
+ case DRXK_POWER_DOWN_CORE:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
+ break;
+ case DRXK_POWER_DOWN_PLL:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL;
+ break;
+ case DRX_POWER_DOWN:
+ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC;
+ break;
+ default:
+ /* Unknow sleep mode */
+ return -EINVAL;
+ }
+
+ /* If already in requested power mode, do nothing */
+ if (state->m_currentPowerMode == *mode)
+ return 0;
+
+ /* For next steps make sure to start from DRX_POWER_UP mode */
+ if (state->m_currentPowerMode != DRX_POWER_UP) {
+ status = PowerUpDevice(state);
+ if (status < 0)
+ goto error;
+ status = DVBTEnableOFDMTokenRing(state, true);
+ if (status < 0)
+ goto error;
+ }
+
+ if (*mode == DRX_POWER_UP) {
+ /* Restore analog & pin configuartion */
+ } else {
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected
+ to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ /* stop all comm_exec */
+ /* Stop and power down previous standard */
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownDVBT(state, false);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownQAM(state);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ break;
+ }
+ status = DVBTEnableOFDMTokenRing(state, false);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+
+ if (*mode != DRXK_POWER_DOWN_OFDM) {
+ state->m_HICfgCtrl |=
+ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = HI_CfgCommand(state);
+ if (status < 0)
+ goto error;
+ }
+ }
+ state->m_currentPowerMode = *mode;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode)
+{
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ u16 cmdResult = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /* Send OFDM stop command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ /* Send OFDM reset command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ }
+
+ /* Reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* powerdown AFE */
+ status = SetIqmAf(state, false);
+ if (status < 0)
+ goto error;
+
+ /* powerdown to OFDM mode */
+ if (setPowerMode) {
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ goto error;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetOperationMode(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /*
+ Stop and power down previous standard
+ TODO investigate total power down instead of partial
+ power down depending on "previous" standard.
+ */
+
+ /* disable HW lock indicator */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Device is already at the required mode */
+ if (state->m_OperationMode == oMode)
+ return 0;
+
+ switch (state->m_OperationMode) {
+ /* OM_NONE was added for start up */
+ case OM_NONE:
+ break;
+ case OM_DVBT:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownDVBT(state, true);
+ if (status < 0)
+ goto error;
+ state->m_OperationMode = OM_NONE;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = PowerDownQAM(state);
+ if (status < 0)
+ goto error;
+ state->m_OperationMode = OM_NONE;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ /*
+ Power up new standard
+ */
+ switch (oMode) {
+ case OM_DVBT:
+ dprintk(1, ": DVB-T\n");
+ state->m_OperationMode = oMode;
+ status = SetDVBTStandard(state, oMode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ dprintk(1, ": DVB-C Annex %c\n",
+ (state->m_OperationMode == OM_QAM_ITU_A) ? 'A' : 'C');
+ state->m_OperationMode = oMode;
+ status = SetQAMStandard(state, oMode);
+ if (status < 0)
+ goto error;
+ break;
+ case OM_QAM_ITU_B:
+ default:
+ status = -EINVAL;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int Start(struct drxk_state *state, s32 offsetFreq,
+ s32 IntermediateFrequency)
+{
+ int status = -EINVAL;
+
+ u16 IFreqkHz;
+ s32 OffsetkHz = offsetFreq / 1000;
+
+ dprintk(1, "\n");
+ if (state->m_DrxkState != DRXK_STOPPED &&
+ state->m_DrxkState != DRXK_DTV_STARTED)
+ goto error;
+
+ state->m_bMirrorFreqSpect = (state->props.inversion == INVERSION_ON);
+
+ if (IntermediateFrequency < 0) {
+ state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect;
+ IntermediateFrequency = -IntermediateFrequency;
+ }
+
+ switch (state->m_OperationMode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ IFreqkHz = (IntermediateFrequency / 1000);
+ status = SetQAM(state, IFreqkHz, OffsetkHz);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_DTV_STARTED;
+ break;
+ case OM_DVBT:
+ IFreqkHz = (IntermediateFrequency / 1000);
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = SetDVBT(state, IFreqkHz, OffsetkHz);
+ if (status < 0)
+ goto error;
+ status = DVBTStart(state);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_DTV_STARTED;
+ break;
+ default:
+ break;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ShutDown(struct drxk_state *state)
+{
+ dprintk(1, "\n");
+
+ MPEGTSStop(state);
+ return 0;
+}
+
+static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus,
+ u32 Time)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (pLockStatus == NULL)
+ goto error;
+
+ *pLockStatus = NOT_LOCKED;
+
+ /* define the SCU command code */
+ switch (state->m_OperationMode) {
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_B:
+ case OM_QAM_ITU_C:
+ status = GetQAMLockStatus(state, pLockStatus);
+ break;
+ case OM_DVBT:
+ status = GetDVBTLockStatus(state, pLockStatus);
+ break;
+ default:
+ break;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSStart(struct drxk_state *state)
+{
+ int status;
+
+ u16 fecOcSncMode = 0;
+
+ /* Allow OC to sync again */
+ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
+ if (status < 0)
+ goto error;
+ fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
+ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSDtoInit(struct drxk_state *state)
+{
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Rate integration settings */
+ status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4);
+ if (status < 0)
+ goto error;
+
+ /* Additional configuration */
+ status = write16(state, FEC_OC_OCR_INVERT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_LWM__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_HWM__A, 12);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int MPEGTSDtoSetup(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status;
+
+ u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */
+ u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */
+ u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */
+ u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */
+ u16 fecOcTmdMode = 0;
+ u16 fecOcTmdIntUpdRate = 0;
+ u32 maxBitRate = 0;
+ bool staticCLK = false;
+
+ dprintk(1, "\n");
+
+ /* Check insertion of the Reed-Solomon parity bytes */
+ status = read16(state, FEC_OC_MODE__A, &fecOcRegMode);
+ if (status < 0)
+ goto error;
+ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode);
+ if (status < 0)
+ goto error;
+ fecOcRegMode &= (~FEC_OC_MODE_PARITY__M);
+ fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
+ if (state->m_insertRSByte == true) {
+ /* enable parity symbol forward */
+ fecOcRegMode |= FEC_OC_MODE_PARITY__M;
+ /* MVAL disable during parity bytes */
+ fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
+ /* TS burst length to 204 */
+ fecOcDtoBurstLen = 204;
+ }
+
+ /* Check serial or parrallel output */
+ fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
+ if (state->m_enableParallel == false) {
+ /* MPEG data output is serial -> set ipr_mode[0] */
+ fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M;
+ }
+
+ switch (oMode) {
+ case OM_DVBT:
+ maxBitRate = state->m_DVBTBitrate;
+ fecOcTmdMode = 3;
+ fecOcRcnCtlRate = 0xC00000;
+ staticCLK = state->m_DVBTStaticCLK;
+ break;
+ case OM_QAM_ITU_A: /* fallthrough */
+ case OM_QAM_ITU_C:
+ fecOcTmdMode = 0x0004;
+ fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */
+ maxBitRate = state->m_DVBCBitrate;
+ staticCLK = state->m_DVBCStaticCLK;
+ break;
+ default:
+ status = -EINVAL;
+ } /* switch (standard) */
+ if (status < 0)
+ goto error;
+
+ /* Configure DTO's */
+ if (staticCLK) {
+ u32 bitRate = 0;
+
+ /* Rational DTO for MCLK source (static MCLK rate),
+ Dynamic DTO for optimal grouping
+ (avoid intra-packet gaps),
+ DTO offset enable to sync TS burst with MSTRT */
+ fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M |
+ FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
+ fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M |
+ FEC_OC_FCT_MODE_VIRT_ENA__M);
+
+ /* Check user defined bitrate */
+ bitRate = maxBitRate;
+ if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */
+ bitRate = 75900000UL;
+ }
+ /* Rational DTO period:
+ dto_period = (Fsys / bitrate) - 2
+
+ Result should be floored,
+ to make sure >= requested bitrate
+ */
+ fecOcDtoPeriod = (u16) (((state->m_sysClockFreq)
+ * 1000) / bitRate);
+ if (fecOcDtoPeriod <= 2)
+ fecOcDtoPeriod = 0;
+ else
+ fecOcDtoPeriod -= 2;
+ fecOcTmdIntUpdRate = 8;
+ } else {
+ /* (commonAttr->staticCLK == false) => dynamic mode */
+ fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M;
+ fecOcFctMode = FEC_OC_FCT_MODE__PRE;
+ fecOcTmdIntUpdRate = 5;
+ }
+
+ /* Write appropriate registers with requested configuration */
+ status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_MODE__A, fecOcRegMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode);
+ if (status < 0)
+ goto error;
+
+ /* Rate integration settings */
+ status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int MPEGTSConfigurePolarity(struct drxk_state *state)
+{
+ u16 fecOcRegIprInvert = 0;
+
+ /* Data mask for the output data byte */
+ u16 InvertDataMask =
+ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
+ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
+ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
+ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
+
+ dprintk(1, "\n");
+
+ /* Control selective inversion of output bits */
+ fecOcRegIprInvert &= (~(InvertDataMask));
+ if (state->m_invertDATA == true)
+ fecOcRegIprInvert |= InvertDataMask;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M));
+ if (state->m_invertERR == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
+ if (state->m_invertSTR == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
+ if (state->m_invertVAL == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M;
+ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
+ if (state->m_invertCLK == true)
+ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M;
+
+ return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert);
+}
+
+#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
+
+static int SetAgcRf(struct drxk_state *state,
+ struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+ int status = -EINVAL;
+ u16 data = 0;
+ struct SCfgAgc *pIfAgcSettings;
+
+ dprintk(1, "\n");
+
+ if (pAgcCfg == NULL)
+ goto error;
+
+ switch (pAgcCfg->ctrlMode) {
+ case DRXK_AGC_CTRL_AUTO:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU RF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+
+ /* Polarity */
+ if (state->m_RfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+
+ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
+ data |= (~(pAgcCfg->speed <<
+ SCU_RAM_AGC_KI_RED_RAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_RAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (IsDVBT(state))
+ pIfAgcSettings = &state->m_dvbtIfAgcCfg;
+ else if (IsQAM(state))
+ pIfAgcSettings = &state->m_qamIfAgcCfg;
+ else
+ pIfAgcSettings = &state->m_atvIfAgcCfg;
+ if (pIfAgcSettings == NULL) {
+ status = -EINVAL;
+ goto error;
+ }
+
+ /* Set TOP, only if IF-AGC is in AUTO mode */
+ if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO)
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top);
+ if (status < 0)
+ goto error;
+
+ /* Cut-Off current */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent);
+ if (status < 0)
+ goto error;
+
+ /* Max. output level */
+ status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel);
+ if (status < 0)
+ goto error;
+
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+ /* Enable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ if (state->m_RfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* SCU c.o.c. to 0, enabling full control range */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+ /* Disable RF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU RF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+
+ default:
+ status = -EINVAL;
+
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
+
+static int SetAgcIf(struct drxk_state *state,
+ struct SCfgAgc *pAgcCfg, bool isDTV)
+{
+ u16 data = 0;
+ int status = 0;
+ struct SCfgAgc *pRfAgcSettings;
+
+ dprintk(1, "\n");
+
+ switch (pAgcCfg->ctrlMode) {
+ case DRXK_AGC_CTRL_AUTO:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Enable SCU IF AGC loop */
+ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_IfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Set speed (using complementary reduction value) */
+ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
+ data |= (~(pAgcCfg->speed <<
+ SCU_RAM_AGC_KI_RED_IAGC_RED__B)
+ & SCU_RAM_AGC_KI_RED_IAGC_RED__M);
+
+ status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
+ if (status < 0)
+ goto error;
+
+ if (IsQAM(state))
+ pRfAgcSettings = &state->m_qamRfAgcCfg;
+ else
+ pRfAgcSettings = &state->m_atvRfAgcCfg;
+ if (pRfAgcSettings == NULL)
+ return -1;
+ /* Restore TOP */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_USER:
+
+ /* Enable IF AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+
+ /* Polarity */
+ if (state->m_IfAgcPol)
+ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ else
+ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Write value to output pin */
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel);
+ if (status < 0)
+ goto error;
+ break;
+
+ case DRXK_AGC_CTRL_OFF:
+
+ /* Disable If AGC DAC */
+ status = read16(state, IQM_AF_STDBY__A, &data);
+ if (status < 0)
+ goto error;
+ data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
+ status = write16(state, IQM_AF_STDBY__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Disable SCU IF AGC loop */
+ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
+ status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+ break;
+ } /* switch (agcSettingsIf->ctrlMode) */
+
+ /* always set the top to support
+ configurations without if-loop */
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ReadIFAgc(struct drxk_state *state, u32 *pValue)
+{
+ u16 agcDacLvl;
+ int status;
+ u16 Level = 0;
+
+ dprintk(1, "\n");
+
+ status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+ }
+
+ *pValue = 0;
+
+ if (agcDacLvl > DRXK_AGC_DAC_OFFSET)
+ Level = agcDacLvl - DRXK_AGC_DAC_OFFSET;
+ if (Level < 14000)
+ *pValue = (14000 - Level) / 4;
+ else
+ *pValue = 0;
+
+ return status;
+}
+
+static int GetQAMSignalToNoise(struct drxk_state *state,
+ s32 *pSignalToNoise)
+{
+ int status = 0;
+ u16 qamSlErrPower = 0; /* accum. error between
+ raw and sliced symbols */
+ u32 qamSlSigPower = 0; /* used for MER, depends of
+ QAM modulation */
+ u32 qamSlMer = 0; /* QAM MER */
+
+ dprintk(1, "\n");
+
+ /* MER calculation */
+
+ /* get the register value needed for MER */
+ status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower);
+ if (status < 0) {
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return -EINVAL;
+ }
+
+ switch (state->props.modulation) {
+ case QAM_16:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
+ break;
+ case QAM_32:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
+ break;
+ case QAM_64:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
+ break;
+ case QAM_128:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
+ break;
+ default:
+ case QAM_256:
+ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
+ break;
+ }
+
+ if (qamSlErrPower > 0) {
+ qamSlMer = Log10Times100(qamSlSigPower) -
+ Log10Times100((u32) qamSlErrPower);
+ }
+ *pSignalToNoise = qamSlMer;
+
+ return status;
+}
+
+static int GetDVBTSignalToNoise(struct drxk_state *state,
+ s32 *pSignalToNoise)
+{
+ int status;
+ u16 regData = 0;
+ u32 EqRegTdSqrErrI = 0;
+ u32 EqRegTdSqrErrQ = 0;
+ u16 EqRegTdSqrErrExp = 0;
+ u16 EqRegTdTpsPwrOfs = 0;
+ u16 EqRegTdReqSmbCnt = 0;
+ u32 tpsCnt = 0;
+ u32 SqrErrIQ = 0;
+ u32 a = 0;
+ u32 b = 0;
+ u32 c = 0;
+ u32 iMER = 0;
+ u16 transmissionParams = 0;
+
+ dprintk(1, "\n");
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp);
+ if (status < 0)
+ goto error;
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, &regData);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_I operational range */
+ EqRegTdSqrErrI = (u32) regData;
+ if ((EqRegTdSqrErrExp > 11) &&
+ (EqRegTdSqrErrI < 0x00000FFFUL)) {
+ EqRegTdSqrErrI += 0x00010000UL;
+ }
+ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, &regData);
+ if (status < 0)
+ goto error;
+ /* Extend SQR_ERR_Q operational range */
+ EqRegTdSqrErrQ = (u32) regData;
+ if ((EqRegTdSqrErrExp > 11) &&
+ (EqRegTdSqrErrQ < 0x00000FFFUL))
+ EqRegTdSqrErrQ += 0x00010000UL;
+
+ status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams);
+ if (status < 0)
+ goto error;
+
+ /* Check input data for MER */
+
+ /* MER calculation (in 0.1 dB) without math.h */
+ if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0))
+ iMER = 0;
+ else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) {
+ /* No error at all, this must be the HW reset value
+ * Apparently no first measurement yet
+ * Set MER to 0.0 */
+ iMER = 0;
+ } else {
+ SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) <<
+ EqRegTdSqrErrExp;
+ if ((transmissionParams &
+ OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
+ == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
+ tpsCnt = 17;
+ else
+ tpsCnt = 68;
+
+ /* IMER = 100 * log10 (x)
+ where x = (EqRegTdTpsPwrOfs^2 *
+ EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ
+
+ => IMER = a + b -c
+ where a = 100 * log10 (EqRegTdTpsPwrOfs^2)
+ b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt)
+ c = 100 * log10 (SqrErrIQ)
+ */
+
+ /* log(x) x = 9bits * 9bits->18 bits */
+ a = Log10Times100(EqRegTdTpsPwrOfs *
+ EqRegTdTpsPwrOfs);
+ /* log(x) x = 16bits * 7bits->23 bits */
+ b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt);
+ /* log(x) x = (16bits + 16bits) << 15 ->32 bits */
+ c = Log10Times100(SqrErrIQ);
+
+ iMER = a + b;
+ /* No negative MER, clip to zero */
+ if (iMER > c)
+ iMER -= c;
+ else
+ iMER = 0;
+ }
+ *pSignalToNoise = iMER;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise)
+{
+ dprintk(1, "\n");
+
+ *pSignalToNoise = 0;
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ return GetDVBTSignalToNoise(state, pSignalToNoise);
+ case OM_QAM_ITU_A:
+ case OM_QAM_ITU_C:
+ return GetQAMSignalToNoise(state, pSignalToNoise);
+ default:
+ break;
+ }
+ return 0;
+}
+
+#if 0
+static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality)
+{
+ /* SNR Values for quasi errorfree reception rom Nordig 2.2 */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ static s32 QE_SN[] = {
+ 51, /* QPSK 1/2 */
+ 69, /* QPSK 2/3 */
+ 79, /* QPSK 3/4 */
+ 89, /* QPSK 5/6 */
+ 97, /* QPSK 7/8 */
+ 108, /* 16-QAM 1/2 */
+ 131, /* 16-QAM 2/3 */
+ 146, /* 16-QAM 3/4 */
+ 156, /* 16-QAM 5/6 */
+ 160, /* 16-QAM 7/8 */
+ 165, /* 64-QAM 1/2 */
+ 187, /* 64-QAM 2/3 */
+ 202, /* 64-QAM 3/4 */
+ 216, /* 64-QAM 5/6 */
+ 225, /* 64-QAM 7/8 */
+ };
+
+ *pQuality = 0;
+
+ do {
+ s32 SignalToNoise = 0;
+ u16 Constellation = 0;
+ u16 CodeRate = 0;
+ u32 SignalToNoiseRel;
+ u32 BERQuality;
+
+ status = GetDVBTSignalToNoise(state, &SignalToNoise);
+ if (status < 0)
+ break;
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation);
+ if (status < 0)
+ break;
+ Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
+
+ status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate);
+ if (status < 0)
+ break;
+ CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
+
+ if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
+ CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
+ break;
+ SignalToNoiseRel = SignalToNoise -
+ QE_SN[Constellation * 5 + CodeRate];
+ BERQuality = 100;
+
+ if (SignalToNoiseRel < -70)
+ *pQuality = 0;
+ else if (SignalToNoiseRel < 30)
+ *pQuality = ((SignalToNoiseRel + 70) *
+ BERQuality) / 100;
+ else
+ *pQuality = BERQuality;
+ } while (0);
+ return 0;
+};
+
+static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality)
+{
+ int status = 0;
+ *pQuality = 0;
+
+ dprintk(1, "\n");
+
+ do {
+ u32 SignalToNoise = 0;
+ u32 BERQuality = 100;
+ u32 SignalToNoiseRel = 0;
+
+ status = GetQAMSignalToNoise(state, &SignalToNoise);
+ if (status < 0)
+ break;
+
+ switch (state->props.modulation) {
+ case QAM_16:
+ SignalToNoiseRel = SignalToNoise - 200;
+ break;
+ case QAM_32:
+ SignalToNoiseRel = SignalToNoise - 230;
+ break; /* Not in NorDig */
+ case QAM_64:
+ SignalToNoiseRel = SignalToNoise - 260;
+ break;
+ case QAM_128:
+ SignalToNoiseRel = SignalToNoise - 290;
+ break;
+ default:
+ case QAM_256:
+ SignalToNoiseRel = SignalToNoise - 320;
+ break;
+ }
+
+ if (SignalToNoiseRel < -70)
+ *pQuality = 0;
+ else if (SignalToNoiseRel < 30)
+ *pQuality = ((SignalToNoiseRel + 70) *
+ BERQuality) / 100;
+ else
+ *pQuality = BERQuality;
+ } while (0);
+
+ return status;
+}
+
+static int GetQuality(struct drxk_state *state, s32 *pQuality)
+{
+ dprintk(1, "\n");
+
+ switch (state->m_OperationMode) {
+ case OM_DVBT:
+ return GetDVBTQuality(state, pQuality);
+ case OM_QAM_ITU_A:
+ return GetDVBCQuality(state, pQuality);
+ default:
+ break;
+ }
+
+ return 0;
+}
+#endif
+
+/* Free data ram in SIO HI */
+#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
+#define SIO_HI_RA_RAM_USR_END__A 0x420060
+
+#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
+#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
+#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
+#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
+
+#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F)
+#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F)
+#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
+
+static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return 0;
+ if (state->m_DrxkState == DRXK_POWERED_DOWN)
+ goto error;
+
+ if (state->no_i2c_bridge)
+ return 0;
+
+ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
+ if (status < 0)
+ goto error;
+ if (bEnableBridge) {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
+ if (status < 0)
+ goto error;
+ } else {
+ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
+ if (status < 0)
+ goto error;
+ }
+
+ status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetPreSaw(struct drxk_state *state,
+ struct SCfgPreSaw *pPreSawCfg)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ if ((pPreSawCfg == NULL)
+ || (pPreSawCfg->reference > IQM_AF_PDREF__M))
+ goto error;
+
+ status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int BLDirectCmd(struct drxk_state *state, u32 targetAddr,
+ u16 romOffset, u16 nrOfElements, u32 timeOut)
+{
+ u16 blStatus = 0;
+ u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF);
+ u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF);
+ int status;
+ unsigned long end;
+
+ dprintk(1, "\n");
+
+ mutex_lock(&state->mutex);
+ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_HDR__A, blockbank);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_TGT_ADDR__A, offset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_ADDR__A, romOffset);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ end = jiffies + msecs_to_jiffies(timeOut);
+ do {
+ status = read16(state, SIO_BL_STATUS__A, &blStatus);
+ if (status < 0)
+ goto error;
+ } while ((blStatus == 0x1) && time_is_after_jiffies(end));
+ if (blStatus == 0x1) {
+ printk(KERN_ERR "drxk: SIO not ready\n");
+ status = -EINVAL;
+ goto error2;
+ }
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+error2:
+ mutex_unlock(&state->mutex);
+ return status;
+
+}
+
+static int ADCSyncMeasurement(struct drxk_state *state, u16 *count)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ /* Start measurement */
+ status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 1);
+ if (status < 0)
+ goto error;
+
+ *count = 0;
+ status = read16(state, IQM_AF_PHASE0__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE1__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+ status = read16(state, IQM_AF_PHASE2__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == 127)
+ *count = *count + 1;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int ADCSynchronization(struct drxk_state *state)
+{
+ u16 count = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ status = ADCSyncMeasurement(state, &count);
+ if (status < 0)
+ goto error;
+
+ if (count == 1) {
+ /* Try sampling on a diffrent edge */
+ u16 clkNeg = 0;
+
+ status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
+ if (status < 0)
+ goto error;
+ if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
+ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clkNeg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
+ } else {
+ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
+ clkNeg |=
+ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
+ }
+ status = write16(state, IQM_AF_CLKNEG__A, clkNeg);
+ if (status < 0)
+ goto error;
+ status = ADCSyncMeasurement(state, &count);
+ if (status < 0)
+ goto error;
+ }
+
+ if (count < 2)
+ status = -EINVAL;
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetFrequencyShifter(struct drxk_state *state,
+ u16 intermediateFreqkHz,
+ s32 tunerFreqOffset, bool isDTV)
+{
+ bool selectPosImage = false;
+ u32 rfFreqResidual = tunerFreqOffset;
+ u32 fmFrequencyShift = 0;
+ bool tunerMirror = !state->m_bMirrorFreqSpect;
+ u32 adcFreq;
+ bool adcFlip;
+ int status;
+ u32 ifFreqActual;
+ u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3);
+ u32 frequencyShift;
+ bool imageToSelect;
+
+ dprintk(1, "\n");
+
+ /*
+ Program frequency shifter
+ No need to account for mirroring on RF
+ */
+ if (isDTV) {
+ if ((state->m_OperationMode == OM_QAM_ITU_A) ||
+ (state->m_OperationMode == OM_QAM_ITU_C) ||
+ (state->m_OperationMode == OM_DVBT))
+ selectPosImage = true;
+ else
+ selectPosImage = false;
+ }
+ if (tunerMirror)
+ /* tuner doesn't mirror */
+ ifFreqActual = intermediateFreqkHz +
+ rfFreqResidual + fmFrequencyShift;
+ else
+ /* tuner mirrors */
+ ifFreqActual = intermediateFreqkHz -
+ rfFreqResidual - fmFrequencyShift;
+ if (ifFreqActual > samplingFrequency / 2) {
+ /* adc mirrors */
+ adcFreq = samplingFrequency - ifFreqActual;
+ adcFlip = true;
+ } else {
+ /* adc doesn't mirror */
+ adcFreq = ifFreqActual;
+ adcFlip = false;
+ }
+
+ frequencyShift = adcFreq;
+ imageToSelect = state->m_rfmirror ^ tunerMirror ^
+ adcFlip ^ selectPosImage;
+ state->m_IqmFsRateOfs =
+ Frac28a((frequencyShift), samplingFrequency);
+
+ if (imageToSelect)
+ state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1;
+
+ /* Program frequency shifter with tuner offset compensation */
+ /* frequencyShift += tunerFreqOffset; TODO */
+ status = write32(state, IQM_FS_RATE_OFS_LO__A,
+ state->m_IqmFsRateOfs);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int InitAGC(struct drxk_state *state, bool isDTV)
+{
+ u16 ingainTgt = 0;
+ u16 ingainTgtMin = 0;
+ u16 ingainTgtMax = 0;
+ u16 clpCyclen = 0;
+ u16 clpSumMin = 0;
+ u16 clpDirTo = 0;
+ u16 snsSumMin = 0;
+ u16 snsSumMax = 0;
+ u16 clpSumMax = 0;
+ u16 snsDirTo = 0;
+ u16 kiInnergainMin = 0;
+ u16 ifIaccuHiTgt = 0;
+ u16 ifIaccuHiTgtMin = 0;
+ u16 ifIaccuHiTgtMax = 0;
+ u16 data = 0;
+ u16 fastClpCtrlDelay = 0;
+ u16 clpCtrlMode = 0;
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* Common settings */
+ snsSumMax = 1023;
+ ifIaccuHiTgtMin = 2047;
+ clpCyclen = 500;
+ clpSumMax = 1023;
+
+ /* AGCInit() not available for DVBT; init done in microcode */
+ if (!IsQAM(state)) {
+ printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode);
+ return -EINVAL;
+ }
+
+ /* FIXME: Analog TV AGC require different settings */
+
+ /* Standard specific settings */
+ clpSumMin = 8;
+ clpDirTo = (u16) -9;
+ clpCtrlMode = 0;
+ snsSumMin = 8;
+ snsDirTo = (u16) -9;
+ kiInnergainMin = (u16) -1030;
+ ifIaccuHiTgtMax = 0x2380;
+ ifIaccuHiTgt = 0x2380;
+ ingainTgtMin = 0x0511;
+ ingainTgt = 0x0511;
+ ingainTgtMax = 5119;
+ fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay;
+
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500);
+ if (status < 0)
+ goto error;
+
+ /* Initialize inner-loop KI gain factors */
+ status = read16(state, SCU_RAM_AGC_KI__A, &data);
+ if (status < 0)
+ goto error;
+
+ data = 0x0657;
+ data &= ~SCU_RAM_AGC_KI_RF__M;
+ data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B);
+ data &= ~SCU_RAM_AGC_KI_IF__M;
+ data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B);
+
+ status = write16(state, SCU_RAM_AGC_KI__A, data);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (packetErr == NULL)
+ status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
+ else
+ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTScCommand(struct drxk_state *state,
+ u16 cmd, u16 subcmd,
+ u16 param0, u16 param1, u16 param2,
+ u16 param3, u16 param4)
+{
+ u16 curCmd = 0;
+ u16 errCode = 0;
+ u16 retryCnt = 0;
+ u16 scExec = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec);
+ if (scExec != 1) {
+ /* SC is not running */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready to receive command */
+ retryCnt = 0;
+ do {
+ msleep(1);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+ retryCnt++;
+ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Write sub-command */
+ switch (cmd) {
+ /* All commands using sub-cmd */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ /* Write needed parameters and the command */
+ switch (cmd) {
+ /* All commands using 5 parameters */
+ /* All commands using 4 parameters */
+ /* All commands using 3 parameters */
+ /* All commands using 2 parameters */
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
+ /* All commands using 1 parameters */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
+ /* All commands using 0 parameters */
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ /* Write command */
+ status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd);
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Wait until sc is ready processing command */
+ retryCnt = 0;
+ do {
+ msleep(1);
+ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
+ retryCnt++;
+ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
+ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
+ goto error;
+
+ /* Check for illegal cmd */
+ status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode);
+ if (errCode == 0xFFFF) {
+ /* illegal command */
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Retreive results parameters from SC */
+ switch (cmd) {
+ /* All commands yielding 5 results */
+ /* All commands yielding 4 results */
+ /* All commands yielding 3 results */
+ /* All commands yielding 2 results */
+ /* All commands yielding 1 result */
+ case OFDM_SC_RA_RAM_CMD_USER_IO:
+ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
+ status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0));
+ /* All commands yielding 0 results */
+ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
+ case OFDM_SC_RA_RAM_CMD_SET_TIMER:
+ case OFDM_SC_RA_RAM_CMD_PROC_START:
+ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
+ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
+ case OFDM_SC_RA_RAM_CMD_NULL:
+ break;
+ default:
+ /* Unknown command */
+ status = -EINVAL;
+ break;
+ } /* switch (cmd->cmd) */
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int PowerUpDVBT(struct drxk_state *state)
+{
+ enum DRXPowerMode powerMode = DRX_POWER_UP;
+ int status;
+
+ dprintk(1, "\n");
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled)
+{
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled == true)
+ status = write16(state, IQM_CF_BYPASSDET__A, 0);
+ else
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+#define DEFAULT_FR_THRES_8K 4000
+static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled)
+{
+
+ int status;
+
+ dprintk(1, "\n");
+ if (*enabled == true) {
+ /* write mask to 1 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
+ DEFAULT_FR_THRES_8K);
+ } else {
+ /* write mask to 0 */
+ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int DVBTCtrlSetEchoThreshold(struct drxk_state *state,
+ struct DRXKCfgDvbtEchoThres_t *echoThres)
+{
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+ status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data);
+ if (status < 0)
+ goto error;
+
+ switch (echoThres->fftMode) {
+ case DRX_FFTMODE_2K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
+ data |= ((echoThres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
+ break;
+ case DRX_FFTMODE_8K:
+ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
+ data |= ((echoThres->threshold <<
+ OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
+ & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int DVBTCtrlSetSqiSpeed(struct drxk_state *state,
+ enum DRXKCfgDvbtSqiSpeed *speed)
+{
+ int status = -EINVAL;
+
+ dprintk(1, "\n");
+
+ switch (*speed) {
+ case DRXK_DVBT_SQI_SPEED_FAST:
+ case DRXK_DVBT_SQI_SPEED_MEDIUM:
+ case DRXK_DVBT_SQI_SPEED_SLOW:
+ break;
+ default:
+ goto error;
+ }
+ status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A,
+ (u16) *speed);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Activate DVBT specific presets
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* Called in DVBTSetStandard
+*
+*/
+static int DVBTActivatePresets(struct drxk_state *state)
+{
+ int status;
+ bool setincenable = false;
+ bool setfrenable = true;
+
+ struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K };
+ struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K };
+
+ dprintk(1, "\n");
+ status = DVBTCtrlSetIncEnable(state, &setincenable);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetFrEnable(state, &setfrenable);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetEchoThreshold(state, &echoThres2k);
+ if (status < 0)
+ goto error;
+ status = DVBTCtrlSetEchoThreshold(state, &echoThres8k);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Initialize channelswitch-independent settings for DVBT.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*
+* For ROM code channel filter taps are loaded from the bootloader. For microcode
+* the DVB-T taps from the drxk_filters.h are used.
+*/
+static int SetDVBTStandard(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ u16 cmdResult = 0;
+ u16 data = 0;
+ int status;
+
+ dprintk(1, "\n");
+
+ PowerUpDVBT(state);
+ /* added antenna switch */
+ SwitchAntennaToDVBT(state);
+ /* send OFDM reset command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* send OFDM setenv command */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* reset datapath for OFDM, processors first */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+
+ /* IQM setup */
+ /* synchronize on ofdstate->m_festart */
+ status = write16(state, IQM_AF_UPD_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ /* window size for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* window size for for sense pre-SAW detection */
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ /* sense threshold for sense pre-SAW detection */
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+ status = SetIqmAf(state, true);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_AF_AGC_RF__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* Impulse noise cruncher setup */
+ status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE__A, 1600);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SCALE_SH__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* virtual clipping threshold for clipping ADC detection */
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */
+ if (status < 0)
+ goto error;
+
+ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2);
+ if (status < 0)
+ goto error;
+ /* enable power measurement interrupt */
+ status = write16(state, IQM_CF_COMM_INT_MSK__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = ADCSynchronization(state);
+ if (status < 0)
+ goto error;
+ status = SetPreSaw(state, &state->m_dvbtPreSawCfg);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true);
+ if (status < 0)
+ goto error;
+ status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true);
+ if (status < 0)
+ goto error;
+
+ /* Set Noise Estimation notch width and enable DC fix */
+ status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data);
+ if (status < 0)
+ goto error;
+ data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M;
+ status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_DRXK_A3_ROM_CODE) {
+ /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */
+ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay);
+ if (status < 0)
+ goto error;
+ }
+
+ /* OFDM_SC setup */
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* FEC setup */
+ status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */
+ if (status < 0)
+ goto error;
+
+
+#ifdef COMPILE_FOR_NONRT
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400);
+ if (status < 0)
+ goto error;
+#else
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000);
+ if (status < 0)
+ goto error;
+#endif
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001);
+ if (status < 0)
+ goto error;
+
+ /* Setup MPEG bus */
+ status = MPEGTSDtoSetup(state, OM_DVBT);
+ if (status < 0)
+ goto error;
+ /* Set DVBT Presets */
+ status = DVBTActivatePresets(state);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+/**
+* \brief Start dvbt demodulating for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+*/
+static int DVBTStart(struct drxk_state *state)
+{
+ u16 param1;
+ int status;
+ /* DRXKOfdmScCmd_t scCmd; */
+
+ dprintk(1, "\n");
+ /* Start correct processes to get in lock */
+ /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
+ param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
+ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0);
+ if (status < 0)
+ goto error;
+ /* Start FEC OC */
+ status = MPEGTSStart(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Set up dvbt demodulator for channel.
+* \param demod instance of demodulator.
+* \return DRXStatus_t.
+* // original DVBTSetChannel()
+*/
+static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset)
+{
+ u16 cmdResult = 0;
+ u16 transmissionParams = 0;
+ u16 operationMode = 0;
+ u32 iqmRcRateOfs = 0;
+ u32 bandwidth = 0;
+ u16 param1;
+ int status;
+
+ dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset);
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* Stop processors */
+ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* Mandatory fix, always stop CP, required to set spl offset back to
+ hardware default (is set to 0 by ucode during pilot detection */
+ status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /*== Write channel settings to device =====================================*/
+
+ /* mode */
+ switch (state->props.transmission_mode) {
+ case TRANSMISSION_MODE_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
+ /* fall through , try first guess DRX_FFTMODE_8K */
+ case TRANSMISSION_MODE_8K:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
+ break;
+ case TRANSMISSION_MODE_2K:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
+ break;
+ }
+
+ /* guard */
+ switch (state->props.guard_interval) {
+ default:
+ case GUARD_INTERVAL_AUTO:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
+ /* fall through , try first guess DRX_GUARD_1DIV4 */
+ case GUARD_INTERVAL_1_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
+ break;
+ case GUARD_INTERVAL_1_32:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
+ break;
+ case GUARD_INTERVAL_1_16:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
+ break;
+ case GUARD_INTERVAL_1_8:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
+ break;
+ }
+
+ /* hierarchy */
+ switch (state->props.hierarchy) {
+ case HIERARCHY_AUTO:
+ case HIERARCHY_NONE:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
+ /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
+ /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
+ /* break; */
+ case HIERARCHY_1:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
+ break;
+ case HIERARCHY_2:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
+ break;
+ case HIERARCHY_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
+ break;
+ }
+
+
+ /* modulation */
+ switch (state->props.modulation) {
+ case QAM_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
+ /* fall through , try first guess DRX_CONSTELLATION_QAM64 */
+ case QAM_64:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
+ break;
+ case QPSK:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
+ break;
+ case QAM_16:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
+ break;
+ }
+#if 0
+ /* No hierachical channels support in BDA */
+ /* Priority (only for hierarchical channels) */
+ switch (channel->priority) {
+ case DRX_PRIORITY_LOW:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
+ WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_LO);
+ break;
+ case DRX_PRIORITY_HIGH:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ WR16(devAddr, OFDM_EC_SB_PRIOR__A,
+ OFDM_EC_SB_PRIOR_HI));
+ break;
+ case DRX_PRIORITY_UNKNOWN: /* fall through */
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+#else
+ /* Set Priorty high */
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
+ status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
+ if (status < 0)
+ goto error;
+#endif
+
+ /* coderate */
+ switch (state->props.code_rate_HP) {
+ case FEC_AUTO:
+ default:
+ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
+ /* fall through , try first guess DRX_CODERATE_2DIV3 */
+ case FEC_2_3:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
+ break;
+ case FEC_1_2:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
+ break;
+ case FEC_3_4:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
+ break;
+ case FEC_5_6:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
+ break;
+ case FEC_7_8:
+ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
+ break;
+ }
+
+ /* SAW filter selection: normaly not necesarry, but if wanted
+ the application can select a SAW filter via the driver by using UIOs */
+ /* First determine real bandwidth (Hz) */
+ /* Also set delay for impulse noise cruncher */
+ /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed
+ by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC
+ functions */
+ switch (state->props.bandwidth_hz) {
+ case 0:
+ state->props.bandwidth_hz = 8000000;
+ /* fall though */
+ case 8000000:
+ bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 8 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ case 7000000:
+ bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for PAL 7 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ case 6000000:
+ bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
+ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073);
+ if (status < 0)
+ goto error;
+ /* cochannel protection for NTSC 6 MHz */
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
+ if (status < 0)
+ goto error;
+ break;
+ default:
+ status = -EINVAL;
+ goto error;
+ }
+
+ if (iqmRcRateOfs == 0) {
+ /* Now compute IQM_RC_RATE_OFS
+ (((SysFreq/BandWidth)/2)/2) -1) * 2^23)
+ =>
+ ((SysFreq / BandWidth) * (2^21)) - (2^23)
+ */
+ /* (SysFreq / BandWidth) * (2^28) */
+ /* assert (MAX(sysClk)/MIN(bandwidth) < 16)
+ => assert(MAX(sysClk) < 16*MIN(bandwidth))
+ => assert(109714272 > 48000000) = true so Frac 28 can be used */
+ iqmRcRateOfs = Frac28a((u32)
+ ((state->m_sysClockFreq *
+ 1000) / 3), bandwidth);
+ /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
+ if ((iqmRcRateOfs & 0x7fL) >= 0x40)
+ iqmRcRateOfs += 0x80L;
+ iqmRcRateOfs = iqmRcRateOfs >> 7;
+ /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */
+ iqmRcRateOfs = iqmRcRateOfs - (1 << 23);
+ }
+
+ iqmRcRateOfs &=
+ ((((u32) IQM_RC_RATE_OFS_HI__M) <<
+ IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs);
+ if (status < 0)
+ goto error;
+
+ /* Bandwidth setting done */
+
+#if 0
+ status = DVBTSetFrequencyShift(demod, channel, tunerOffset);
+ if (status < 0)
+ goto error;
+#endif
+ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ if (status < 0)
+ goto error;
+
+ /*== Start SC, write channel settings to SC ===============================*/
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Enable SC after setting all other parameters */
+ status = write16(state, OFDM_SC_COMM_STATE__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, OFDM_SC_COMM_EXEC__A, 1);
+ if (status < 0)
+ goto error;
+
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* Write SC parameter registers, set all AUTO flags in operation mode */
+ param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M |
+ OFDM_SC_RA_RAM_OP_AUTO_GUARD__M |
+ OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
+ OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
+ OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
+ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
+ 0, transmissionParams, param1, 0, 0, 0);
+ if (status < 0)
+ goto error;
+
+ if (!state->m_DRXK_A3_ROM_CODE)
+ status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/*============================================================================*/
+
+/**
+* \brief Retreive lock status .
+* \param demod Pointer to demodulator instance.
+* \param lockStat Pointer to lock status structure.
+* \return DRXStatus_t.
+*
+*/
+static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+ int status;
+ const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
+ OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
+ const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
+
+ u16 ScRaRamLock = 0;
+ u16 ScCommExec = 0;
+
+ dprintk(1, "\n");
+
+ *pLockStatus = NOT_LOCKED;
+ /* driver 0.9.0 */
+ /* Check if SC is running */
+ status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec);
+ if (status < 0)
+ goto end;
+ if (ScCommExec == OFDM_SC_COMM_EXEC_STOP)
+ goto end;
+
+ status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock);
+ if (status < 0)
+ goto end;
+
+ if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask)
+ *pLockStatus = MPEG_LOCK;
+ else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask)
+ *pLockStatus = FEC_LOCK;
+ else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask)
+ *pLockStatus = DEMOD_LOCK;
+ else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
+ *pLockStatus = NEVER_LOCK;
+end:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int PowerUpQAM(struct drxk_state *state)
+{
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ int status;
+
+ dprintk(1, "\n");
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+
+/** Power Down QAM */
+static int PowerDownQAM(struct drxk_state *state)
+{
+ u16 data = 0;
+ u16 cmdResult;
+ int status = 0;
+
+ dprintk(1, "\n");
+ status = read16(state, SCU_COMM_EXEC__A, &data);
+ if (status < 0)
+ goto error;
+ if (data == SCU_COMM_EXEC_ACTIVE) {
+ /*
+ STOP demodulator
+ QAM and HW blocks
+ */
+ /* stop all comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+ }
+ /* powerdown AFE */
+ status = SetIqmAf(state, false);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Setup of the QAM Measurement intervals for signal quality
+* \param demod instance of demod.
+* \param modulation current modulation.
+* \return DRXStatus_t.
+*
+* NOTE:
+* Take into account that for certain settings the errorcounters can overflow.
+* The implementation does not check this.
+*
+*/
+static int SetQAMMeasurement(struct drxk_state *state,
+ enum EDrxkConstellation modulation,
+ u32 symbolRate)
+{
+ u32 fecBitsDesired = 0; /* BER accounting period */
+ u32 fecRsPeriodTotal = 0; /* Total period */
+ u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */
+ u16 fecRsPeriod = 0; /* Value for corresponding I2C register */
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ fecRsPrescale = 1;
+ /* fecBitsDesired = symbolRate [kHz] *
+ FrameLenght [ms] *
+ (modulation + 1) *
+ SyncLoss (== 1) *
+ ViterbiLoss (==1)
+ */
+ switch (modulation) {
+ case DRX_CONSTELLATION_QAM16:
+ fecBitsDesired = 4 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM32:
+ fecBitsDesired = 5 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM64:
+ fecBitsDesired = 6 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM128:
+ fecBitsDesired = 7 * symbolRate;
+ break;
+ case DRX_CONSTELLATION_QAM256:
+ fecBitsDesired = 8 * symbolRate;
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */
+ fecBitsDesired *= 500; /* meas. period [ms] */
+
+ /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
+ /* fecRsPeriodTotal = fecBitsDesired / 1632 */
+ fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */
+
+ /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */
+ fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16);
+ if (fecRsPrescale == 0) {
+ /* Divide by zero (though impossible) */
+ status = -EINVAL;
+ if (status < 0)
+ goto error;
+ }
+ fecRsPeriod =
+ ((u16) fecRsPeriodTotal +
+ (fecRsPrescale >> 1)) / fecRsPrescale;
+
+ /* write corresponding registers */
+ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetQAM16(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517);
+ if (status < 0)
+ goto error;
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16);
+ if (status < 0)
+ goto error;
+
+ /* QAM Loop Controller Coeficients */
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127);
+ if (status < 0)
+ goto error;
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM32 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM32(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM64 specific setup
+* \param demod instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM64(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM128 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM128(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief QAM256 specific setup
+* \param demod: instance of demod.
+* \return DRXStatus_t.
+*/
+static int SetQAM256(struct drxk_state *state)
+{
+ int status = 0;
+
+ dprintk(1, "\n");
+ /* QAM Equalizer Setup */
+ /* Equalizer */
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385);
+ if (status < 0)
+ goto error;
+
+ /* Decision Feedback Equalizer */
+ status = write16(state, QAM_DQ_QUAL_FUN0__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN1__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN2__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN3__A, 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN4__A, 6);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_SY_SYNC_HWM__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_AWM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_SYNC_LWM__A, 3);
+ if (status < 0)
+ goto error;
+
+ /* QAM Slicer Settings */
+
+ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM Loop Controller Coeficients */
+
+ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM State Machine (FSM) Thresholds */
+
+ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
+ if (status < 0)
+ goto error;
+
+
+ /* QAM FSM Tracking Parameters */
+
+ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+/*============================================================================*/
+/**
+* \brief Reset QAM block.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMResetQAM(struct drxk_state *state)
+{
+ int status;
+ u16 cmdResult;
+
+ dprintk(1, "\n");
+ /* Stop QAM comstate->m_exec */
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Set QAM symbolrate.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+static int QAMSetSymbolrate(struct drxk_state *state)
+{
+ u32 adcFrequency = 0;
+ u32 symbFreq = 0;
+ u32 iqmRcRate = 0;
+ u16 ratesel = 0;
+ u32 lcSymbRate = 0;
+ int status;
+
+ dprintk(1, "\n");
+ /* Select & calculate correct IQM rate */
+ adcFrequency = (state->m_sysClockFreq * 1000) / 3;
+ ratesel = 0;
+ /* printk(KERN_DEBUG "drxk: SR %d\n", state->props.symbol_rate); */
+ if (state->props.symbol_rate <= 1188750)
+ ratesel = 3;
+ else if (state->props.symbol_rate <= 2377500)
+ ratesel = 2;
+ else if (state->props.symbol_rate <= 4755000)
+ ratesel = 1;
+ status = write16(state, IQM_FD_RATESEL__A, ratesel);
+ if (status < 0)
+ goto error;
+
+ /*
+ IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
+ */
+ symbFreq = state->props.symbol_rate * (1 << ratesel);
+ if (symbFreq == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) +
+ (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) -
+ (1 << 23);
+ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate);
+ if (status < 0)
+ goto error;
+ state->m_iqmRcRate = iqmRcRate;
+ /*
+ LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15))
+ */
+ symbFreq = state->props.symbol_rate;
+ if (adcFrequency == 0) {
+ /* Divide by zero */
+ status = -EINVAL;
+ goto error;
+ }
+ lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) +
+ (Frac28a((symbFreq % adcFrequency), adcFrequency) >>
+ 16);
+ if (lcSymbRate > 511)
+ lcSymbRate = 511;
+ status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate);
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+* \brief Get QAM lock status.
+* \param demod: instance of demod.
+* \param channel: pointer to channel data.
+* \return DRXStatus_t.
+*/
+
+static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
+{
+ int status;
+ u16 Result[2] = { 0, 0 };
+
+ dprintk(1, "\n");
+ *pLockStatus = NOT_LOCKED;
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM |
+ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
+ Result);
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
+ /* 0x0000 NOT LOCKED */
+ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
+ /* 0x4000 DEMOD LOCKED */
+ *pLockStatus = DEMOD_LOCK;
+ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
+ /* 0x8000 DEMOD + FEC LOCKED (system lock) */
+ *pLockStatus = MPEG_LOCK;
+ } else {
+ /* 0xC000 NEVER LOCKED */
+ /* (system will never be able to lock to the signal) */
+ /* TODO: check this, intermediate & standard specific lock states are not
+ taken into account here */
+ *pLockStatus = NEVER_LOCK;
+ }
+ return status;
+}
+
+#define QAM_MIRROR__M 0x03
+#define QAM_MIRROR_NORMAL 0x00
+#define QAM_MIRRORED 0x01
+#define QAM_MIRROR_AUTO_ON 0x02
+#define QAM_LOCKRANGE__M 0x10
+#define QAM_LOCKRANGE_NORMAL 0x10
+
+static int QAMDemodulatorCommand(struct drxk_state *state,
+ int numberOfParameters)
+{
+ int status;
+ u16 cmdResult;
+ u16 setParamParameters[4] = { 0, 0, 0, 0 };
+
+ setParamParameters[0] = state->m_Constellation; /* modulation */
+ setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
+
+ if (numberOfParameters == 2) {
+ u16 setEnvParameters[1] = { 0 };
+
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setEnvParameters[0] = QAM_TOP_ANNEX_C;
+ else
+ setEnvParameters[0] = QAM_TOP_ANNEX_A;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
+ 1, setEnvParameters, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ numberOfParameters, setParamParameters,
+ 1, &cmdResult);
+ } else if (numberOfParameters == 4) {
+ if (state->m_OperationMode == OM_QAM_ITU_C)
+ setParamParameters[2] = QAM_TOP_ANNEX_C;
+ else
+ setParamParameters[2] = QAM_TOP_ANNEX_A;
+
+ setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
+ /* Env parameters */
+ /* check for LOCKRANGE Extented */
+ /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
+
+ status = scu_command(state,
+ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
+ numberOfParameters, setParamParameters,
+ 1, &cmdResult);
+ } else {
+ printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
+ "count %d\n", numberOfParameters);
+ }
+
+error:
+ if (status < 0)
+ printk(KERN_WARNING "drxk: Warning %d on %s\n",
+ status, __func__);
+ return status;
+}
+
+static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
+ s32 tunerFreqOffset)
+{
+ int status;
+ u16 cmdResult;
+ int qamDemodParamCount = state->qam_demod_parameter_count;
+
+ dprintk(1, "\n");
+ /*
+ * STEP 1: reset demodulator
+ * resets FEC DI and FEC RS
+ * resets QAM block
+ * resets SCU variables
+ */
+ status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = QAMResetQAM(state);
+ if (status < 0)
+ goto error;
+
+ /*
+ * STEP 2: configure demodulator
+ * -set params; resets IQM,QAM,FEC HW; initializes some
+ * SCU variables
+ */
+ status = QAMSetSymbolrate(state);
+ if (status < 0)
+ goto error;
+
+ /* Set params */
+ switch (state->props.modulation) {
+ case QAM_256:
+ state->m_Constellation = DRX_CONSTELLATION_QAM256;
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ state->m_Constellation = DRX_CONSTELLATION_QAM64;
+ break;
+ case QAM_16:
+ state->m_Constellation = DRX_CONSTELLATION_QAM16;
+ break;
+ case QAM_32:
+ state->m_Constellation = DRX_CONSTELLATION_QAM32;
+ break;
+ case QAM_128:
+ state->m_Constellation = DRX_CONSTELLATION_QAM128;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Use the 4-parameter if it's requested or we're probing for
+ * the correct command. */
+ if (state->qam_demod_parameter_count == 4
+ || !state->qam_demod_parameter_count) {
+ qamDemodParamCount = 4;
+ status = QAMDemodulatorCommand(state, qamDemodParamCount);
+ }
+
+ /* Use the 2-parameter command if it was requested or if we're
+ * probing for the correct command and the 4-parameter command
+ * failed. */
+ if (state->qam_demod_parameter_count == 2
+ || (!state->qam_demod_parameter_count && status < 0)) {
+ qamDemodParamCount = 2;
+ status = QAMDemodulatorCommand(state, qamDemodParamCount);
+ }
+
+ if (status < 0) {
+ dprintk(1, "Could not set demodulator parameters. Make "
+ "sure qam_demod_parameter_count (%d) is correct for "
+ "your firmware (%s).\n",
+ state->qam_demod_parameter_count,
+ state->microcode_name);
+ goto error;
+ } else if (!state->qam_demod_parameter_count) {
+ dprintk(1, "Auto-probing the correct QAM demodulator command "
+ "parameters was successful - using %d parameters.\n",
+ qamDemodParamCount);
+
+ /*
+ * One of our commands was successful. We don't need to
+ * auto-probe anymore, now that we got the correct command.
+ */
+ state->qam_demod_parameter_count = qamDemodParamCount;
+ }
+
+ /*
+ * STEP 3: enable the system in a mode where the ADC provides valid
+ * signal setup modulation independent registers
+ */
+#if 0
+ status = SetFrequency(channel, tunerFreqOffset));
+ if (status < 0)
+ goto error;
+#endif
+ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
+ if (status < 0)
+ goto error;
+
+ /* Setup BER measurement */
+ status = SetQAMMeasurement(state, state->m_Constellation, state->props.symbol_rate);
+ if (status < 0)
+ goto error;
+
+ /* Reset default values */
+ status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE);
+ if (status < 0)
+ goto error;
+
+ /* Reset default LC values */
+ status = write16(state, QAM_LC_RATE_LIMIT__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORP__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_LPF_FACTORI__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_MODE__A, 7);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, QAM_LC_QUAL_TAB0__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB1__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB2__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB3__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB4__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB5__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB6__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB8__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB9__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB10__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB12__A, 2);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB15__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB16__A, 3);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB20__A, 4);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_LC_QUAL_TAB25__A, 4);
+ if (status < 0)
+ goto error;
+
+ /* Mirroring, QAM-block starting point not inverted */
+ status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* STEP 4: modulation specific setup */
+ switch (state->props.modulation) {
+ case QAM_16:
+ status = SetQAM16(state);
+ break;
+ case QAM_32:
+ status = SetQAM32(state);
+ break;
+ case QAM_AUTO:
+ case QAM_64:
+ status = SetQAM64(state);
+ break;
+ case QAM_128:
+ status = SetQAM128(state);
+ break;
+ case QAM_256:
+ status = SetQAM256(state);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* Re-configure MPEG output, requires knowledge of channel bitrate */
+ /* extAttr->currentChannel.modulation = channel->modulation; */
+ /* extAttr->currentChannel.symbolrate = channel->symbolrate; */
+ status = MPEGTSDtoSetup(state, state->m_OperationMode);
+ if (status < 0)
+ goto error;
+
+ /* Start processes */
+ status = MPEGTSStart(state);
+ if (status < 0)
+ goto error;
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
+ if (status < 0)
+ goto error;
+
+ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
+ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
+ if (status < 0)
+ goto error;
+
+ /* update global DRXK data container */
+/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */
+
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SetQAMStandard(struct drxk_state *state,
+ enum OperationMode oMode)
+{
+ int status;
+#ifdef DRXK_QAM_TAPS
+#define DRXK_QAMA_TAPS_SELECT
+#include "drxk_filters.h"
+#undef DRXK_QAMA_TAPS_SELECT
+#endif
+
+ dprintk(1, "\n");
+
+ /* added antenna switch */
+ SwitchAntennaToQAM(state);
+
+ /* Ensure correct power-up mode */
+ status = PowerUpQAM(state);
+ if (status < 0)
+ goto error;
+ /* Reset QAM block */
+ status = QAMResetQAM(state);
+ if (status < 0)
+ goto error;
+
+ /* Setup IQM */
+
+ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
+ if (status < 0)
+ goto error;
+
+ /* Upload IQM Channel Filter settings by
+ boot loader from ROM table */
+ switch (oMode) {
+ case OM_QAM_ITU_A:
+ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ break;
+ case OM_QAM_ITU_C:
+ status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ if (status < 0)
+ goto error;
+ status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
+ break;
+ default:
+ status = -EINVAL;
+ }
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B));
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_SYMMETRIC__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_RC_STRETCH__A, 21);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_CLP_TH__A, 448);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_SNS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, IQM_FS_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_RC_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_ADJ_SEL__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_UPD_SEL__A, 0);
+ if (status < 0)
+ goto error;
+
+ /* IQM Impulse Noise Processing Unit */
+ status = write16(state, IQM_CF_CLP_VAL__A, 500);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DATATH__A, 1000);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_BYPASSDET__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_DET_LCT__A, 0);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_WND_LEN__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_CF_PKDTH__A, 1);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_INC_BYPASS__A, 1);
+ if (status < 0)
+ goto error;
+
+ /* turn on IQMAF. Must be done before setAgc**() */
+ status = SetIqmAf(state, true);
+ if (status < 0)
+ goto error;
+ status = write16(state, IQM_AF_START_LOCK__A, 0x01);
+ if (status < 0)
+ goto error;
+
+ /* IQM will not be reset from here, sync ADC and update/init AGC */
+ status = ADCSynchronization(state);
+ if (status < 0)
+ goto error;
+
+ /* Set the FSM step period */
+ status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000);
+ if (status < 0)
+ goto error;
+
+ /* Halt SCU to enable safe non-atomic accesses */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
+ if (status < 0)
+ goto error;
+
+ /* No more resets of the IQM, current standard correctly set =>
+ now AGCs can be configured. */
+
+ status = InitAGC(state, true);
+ if (status < 0)
+ goto error;
+ status = SetPreSaw(state, &(state->m_qamPreSawCfg));
+ if (status < 0)
+ goto error;
+
+ /* Configure AGC's */
+ status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true);
+ if (status < 0)
+ goto error;
+ status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true);
+ if (status < 0)
+ goto error;
+
+ /* Activate SCU to enable SCU commands */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int WriteGPIO(struct drxk_state *state)
+{
+ int status;
+ u16 value = 0;
+
+ dprintk(1, "\n");
+ /* stop lock indicator process */
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+
+ /* Write magic word to enable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
+ if (status < 0)
+ goto error;
+
+ if (state->m_hasSAWSW) {
+ if (state->UIO_mask & 0x0001) { /* UIO-1 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0001) == 0)
+ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
+ else
+ value |= 0x8000; /* write one to 15th bit - 1st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->UIO_mask & 0x0002) { /* UIO-2 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0002) == 0)
+ value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */
+ else
+ value |= 0x4000; /* write one to 14th bit - 2st UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ if (state->UIO_mask & 0x0004) { /* UIO-3 */
+ /* write to io pad configuration register - output mode */
+ status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg);
+ if (status < 0)
+ goto error;
+
+ /* use corresponding bit in io data output registar */
+ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
+ if (status < 0)
+ goto error;
+ if ((state->m_GPIO & 0x0004) == 0)
+ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
+ else
+ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
+ /* write back to io data output register */
+ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
+ if (status < 0)
+ goto error;
+ }
+ }
+ /* Write magic word to disable pdr reg write */
+ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SwitchAntennaToQAM(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_GPIO & state->antenna_gpio;
+
+ if (state->antenna_dvbt ^ gpio_state) {
+ /* Antenna is on DVB-T mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_GPIO &= ~state->antenna_gpio;
+ else
+ state->m_GPIO |= state->antenna_gpio;
+ status = WriteGPIO(state);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+static int SwitchAntennaToDVBT(struct drxk_state *state)
+{
+ int status = 0;
+ bool gpio_state;
+
+ dprintk(1, "\n");
+
+ if (!state->antenna_gpio)
+ return 0;
+
+ gpio_state = state->m_GPIO & state->antenna_gpio;
+
+ if (!(state->antenna_dvbt ^ gpio_state)) {
+ /* Antenna is on DVB-C mode. Switch */
+ if (state->antenna_dvbt)
+ state->m_GPIO |= state->antenna_gpio;
+ else
+ state->m_GPIO &= ~state->antenna_gpio;
+ status = WriteGPIO(state);
+ }
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ return status;
+}
+
+
+static int PowerDownDevice(struct drxk_state *state)
+{
+ /* Power down to requested mode */
+ /* Backup some register settings */
+ /* Set pins with possible pull-ups connected to them in input mode */
+ /* Analog power down */
+ /* ADC power down */
+ /* Power down device */
+ int status;
+
+ dprintk(1, "\n");
+ if (state->m_bPDownOpenBridge) {
+ /* Open I2C bridge before power down of DRXK */
+ status = ConfigureI2CBridge(state, true);
+ if (status < 0)
+ goto error;
+ }
+ /* driver 0.9.0 */
+ status = DVBTEnableOFDMTokenRing(state, false);
+ if (status < 0)
+ goto error;
+
+ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
+ status = HI_CfgCommand(state);
+error:
+ if (status < 0)
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+
+ return status;
+}
+
+static int init_drxk(struct drxk_state *state)
+{
+ int status = 0, n = 0;
+ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
+ u16 driverVersion;
+
+ dprintk(1, "\n");
+ if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
+ drxk_i2c_lock(state);
+ status = PowerUpDevice(state);
+ if (status < 0)
+ goto error;
+ status = DRXX_Open(state);
+ if (status < 0)
+ goto error;
+ /* Soft reset of OFDM-, sys- and osc-clockdomain */
+ status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M);
+ if (status < 0)
+ goto error;
+ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
+ if (status < 0)
+ goto error;
+ /* TODO is this needed, if yes how much delay in worst case scenario */
+ msleep(1);
+ state->m_DRXK_A3_PATCH_CODE = true;
+ status = GetDeviceCapabilities(state);
+ if (status < 0)
+ goto error;
+
+ /* Bridge delay, uses oscilator clock */
+ /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
+ /* SDA brdige delay */
+ state->m_HICfgBridgeDelay =
+ (u16) ((state->m_oscClockFreq / 1000) *
+ HI_I2C_BRIDGE_DELAY) / 1000;
+ /* Clipping */
+ if (state->m_HICfgBridgeDelay >
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
+ state->m_HICfgBridgeDelay =
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
+ }
+ /* SCL bridge delay, same as SDA for now */
+ state->m_HICfgBridgeDelay +=
+ state->m_HICfgBridgeDelay <<
+ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
+
+ status = InitHI(state);
+ if (status < 0)
+ goto error;
+ /* disable various processes */
+#if NOA1ROM
+ if (!(state->m_DRXK_A1_ROM_CODE)
+ && !(state->m_DRXK_A2_ROM_CODE))
+#endif
+ {
+ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
+ if (status < 0)
+ goto error;
+ }
+
+ /* disable MPEG port */
+ status = MPEGTSDisable(state);
+ if (status < 0)
+ goto error;
+
+ /* Stop AUD and SCU */
+ status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+
+ /* enable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
+ if (status < 0)
+ goto error;
+
+ /* include boot loader section */
+ status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = BLChainCmd(state, 0, 6, 100);
+ if (status < 0)
+ goto error;
+
+ if (state->fw) {
+ status = DownloadMicrocode(state, state->fw->data,
+ state->fw->size);
+ if (status < 0)
+ goto error;
+ }
+
+ /* disable token-ring bus through OFDM block for possible ucode upload */
+ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
+ if (status < 0)
+ goto error;
+
+ /* Run SCU for a little while to initialize microcode version numbers */
+ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
+ if (status < 0)
+ goto error;
+ status = DRXX_Open(state);
+ if (status < 0)
+ goto error;
+ /* added for test */
+ msleep(30);
+
+ powerMode = DRXK_POWER_DOWN_OFDM;
+ status = CtrlPowerMode(state, &powerMode);
+ if (status < 0)
+ goto error;
+
+ /* Stamp driver version number in SCU data RAM in BCD code
+ Done to enable field application engineers to retreive drxdriver version
+ via I2C from SCU RAM.
+ Not using SCU command interface for SCU register access since no
+ microcode may be present.
+ */
+ driverVersion =
+ (((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
+ (((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
+ ((DRXK_VERSION_MAJOR % 10) << 4) +
+ (DRXK_VERSION_MINOR % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion);
+ if (status < 0)
+ goto error;
+ driverVersion =
+ (((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
+ (((DRXK_VERSION_PATCH / 100) % 10) << 8) +
+ (((DRXK_VERSION_PATCH / 10) % 10) << 4) +
+ (DRXK_VERSION_PATCH % 10);
+ status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion);
+ if (status < 0)
+ goto error;
+
+ printk(KERN_INFO "DRXK driver version %d.%d.%d\n",
+ DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
+ DRXK_VERSION_PATCH);
+
+ /* Dirty fix of default values for ROM/PATCH microcode
+ Dirty because this fix makes it impossible to setup suitable values
+ before calling DRX_Open. This solution requires changes to RF AGC speed
+ to be done via the CTRL function after calling DRX_Open */
+
+ /* m_dvbtRfAgcCfg.speed = 3; */
+
+ /* Reset driver debug flags to 0 */
+ status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
+ if (status < 0)
+ goto error;
+ /* driver 0.9.0 */
+ /* Setup FEC OC:
+ NOTE: No more full FEC resets allowed afterwards!! */
+ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP);
+ if (status < 0)
+ goto error;
+ /* MPEGTS functions are still the same */
+ status = MPEGTSDtoInit(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSStop(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSConfigurePolarity(state);
+ if (status < 0)
+ goto error;
+ status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput);
+ if (status < 0)
+ goto error;
+ /* added: configure GPIO */
+ status = WriteGPIO(state);
+ if (status < 0)
+ goto error;
+
+ state->m_DrxkState = DRXK_STOPPED;
+
+ if (state->m_bPowerDown) {
+ status = PowerDownDevice(state);
+ if (status < 0)
+ goto error;
+ state->m_DrxkState = DRXK_POWERED_DOWN;
+ } else
+ state->m_DrxkState = DRXK_STOPPED;
+
+ /* Initialize the supported delivery systems */
+ n = 0;
+ if (state->m_hasDVBC) {
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
+ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
+ strlcat(state->frontend.ops.info.name, " DVB-C",
+ sizeof(state->frontend.ops.info.name));
+ }
+ if (state->m_hasDVBT) {
+ state->frontend.ops.delsys[n++] = SYS_DVBT;
+ strlcat(state->frontend.ops.info.name, " DVB-T",
+ sizeof(state->frontend.ops.info.name));
+ }
+ drxk_i2c_unlock(state);
+ }
+error:
+ if (status < 0) {
+ state->m_DrxkState = DRXK_NO_DEV;
+ drxk_i2c_unlock(state);
+ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
+ }
+
+ return status;
+}
+
+static void load_firmware_cb(const struct firmware *fw,
+ void *context)
+{
+ struct drxk_state *state = context;
+
+ dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
+ if (!fw) {
+ printk(KERN_ERR
+ "drxk: Could not load firmware file %s.\n",
+ state->microcode_name);
+ printk(KERN_INFO
+ "drxk: Copy %s to your hotplug directory!\n",
+ state->microcode_name);
+ state->microcode_name = NULL;
+
+ /*
+ * As firmware is now load asynchronous, it is not possible
+ * anymore to fail at frontend attach. We might silently
+ * return here, and hope that the driver won't crash.
+ * We might also change all DVB callbacks to return -ENODEV
+ * if the device is not initialized.
+ * As the DRX-K devices have their own internal firmware,
+ * let's just hope that it will match a firmware revision
+ * compatible with this driver and proceed.
+ */
+ }
+ state->fw = fw;
+
+ init_drxk(state);
+}
+
+static void drxk_release(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+ if (state->fw)
+ release_firmware(state->fw);
+
+ kfree(state);
+}
+
+static int drxk_sleep(struct dvb_frontend *fe)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return 0;
+
+ ShutDown(state);
+ return 0;
+}
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, ": %s\n", enable ? "enable" : "disable");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+
+ return ConfigureI2CBridge(state, enable ? true : false);
+}
+
+static int drxk_set_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 delsys = p->delivery_system, old_delsys;
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 IF;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ if (!fe->ops.tuner_ops.get_if_frequency) {
+ printk(KERN_ERR
+ "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
+ return -EINVAL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ old_delsys = state->props.delivery_system;
+ state->props = *p;
+
+ if (old_delsys != delsys) {
+ ShutDown(state);
+ switch (delsys) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if (!state->m_hasDVBC)
+ return -EINVAL;
+ state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? true : false;
+ if (state->m_itut_annex_c)
+ SetOperationMode(state, OM_QAM_ITU_C);
+ else
+ SetOperationMode(state, OM_QAM_ITU_A);
+ break;
+ case SYS_DVBT:
+ if (!state->m_hasDVBT)
+ return -EINVAL;
+ SetOperationMode(state, OM_DVBT);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ fe->ops.tuner_ops.get_if_frequency(fe, &IF);
+ Start(state, 0, IF);
+
+ /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
+
+ return 0;
+}
+
+static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 stat;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ *status = 0;
+ GetLockStatus(state, &stat, 0);
+ if (stat == MPEG_LOCK)
+ *status |= 0x1f;
+ if (stat == FEC_LOCK)
+ *status |= 0x0f;
+ if (stat == DEMOD_LOCK)
+ *status |= 0x07;
+ return 0;
+}
+
+static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ *ber = 0;
+ return 0;
+}
+
+static int drxk_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u32 val = 0;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ ReadIFAgc(state, &val);
+ *strength = val & 0xffff;
+ return 0;
+}
+
+static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ s32 snr2;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ GetSignalToNoise(state, &snr2);
+ *snr = snr2 & 0xffff;
+ return 0;
+}
+
+static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ u16 err;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ DVBTQAMGetAccPktErr(state, &err);
+ *ucblocks = (u32) err;
+ return 0;
+}
+
+static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
+ *sets)
+{
+ struct drxk_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+
+ dprintk(1, "\n");
+
+ if (state->m_DrxkState == DRXK_NO_DEV)
+ return -ENODEV;
+ if (state->m_DrxkState == DRXK_UNINITIALIZED)
+ return -EAGAIN;
+
+ switch (p->delivery_system) {
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ case SYS_DVBT:
+ sets->min_delay_ms = 3000;
+ sets->max_drift = 0;
+ sets->step_size = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct dvb_frontend_ops drxk_ops = {
+ /* .delsys will be filled dynamically */
+ .info = {
+ .name = "DRXK",
+ .frequency_min = 47000000,
+ .frequency_max = 865000000,
+ /* For DVB-C */
+ .symbol_rate_min = 870000,
+ .symbol_rate_max = 11700000,
+ /* For DVB-T */
+ .frequency_stepsize = 166667,
+
+ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
+ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_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_MUTE_TS |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER |
+ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO
+ },
+
+ .release = drxk_release,
+ .sleep = drxk_sleep,
+ .i2c_gate_ctrl = drxk_gate_ctrl,
+
+ .set_frontend = drxk_set_parameters,
+ .get_tune_settings = drxk_get_tune_settings,
+
+ .read_status = drxk_read_status,
+ .read_ber = drxk_read_ber,
+ .read_signal_strength = drxk_read_signal_strength,
+ .read_snr = drxk_read_snr,
+ .read_ucblocks = drxk_read_ucblocks,
+};
+
+struct dvb_frontend *drxk_attach(const struct drxk_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct drxk_state *state = NULL;
+ u8 adr = config->adr;
+ int status;
+
+ dprintk(1, "\n");
+ state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->i2c = i2c;
+ state->demod_address = adr;
+ state->single_master = config->single_master;
+ state->microcode_name = config->microcode_name;
+ state->qam_demod_parameter_count = config->qam_demod_parameter_count;
+ state->no_i2c_bridge = config->no_i2c_bridge;
+ state->antenna_gpio = config->antenna_gpio;
+ state->antenna_dvbt = config->antenna_dvbt;
+ state->m_ChunkSize = config->chunk_size;
+ state->enable_merr_cfg = config->enable_merr_cfg;
+
+ if (config->dynamic_clk) {
+ state->m_DVBTStaticCLK = 0;
+ state->m_DVBCStaticCLK = 0;
+ } else {
+ state->m_DVBTStaticCLK = 1;
+ state->m_DVBCStaticCLK = 1;
+ }
+
+
+ if (config->mpeg_out_clk_strength)
+ state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
+ else
+ state->m_TSClockkStrength = 0x06;
+
+ if (config->parallel_ts)
+ state->m_enableParallel = true;
+ else
+ state->m_enableParallel = false;
+
+ /* NOTE: as more UIO bits will be used, add them to the mask */
+ state->UIO_mask = config->antenna_gpio;
+
+ /* Default gpio to DVB-C */
+ if (!state->antenna_dvbt && state->antenna_gpio)
+ state->m_GPIO |= state->antenna_gpio;
+ else
+ state->m_GPIO &= ~state->antenna_gpio;
+
+ mutex_init(&state->mutex);
+
+ memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops));
+ state->frontend.demodulator_priv = state;
+
+ init_state(state);
+
+ /* Load firmware and initialize DRX-K */
+ if (state->microcode_name) {
+ if (config->load_firmware_sync) {
+ const struct firmware *fw = NULL;
+
+ status = request_firmware(&fw, state->microcode_name,
+ state->i2c->dev.parent);
+ if (status < 0)
+ fw = NULL;
+ load_firmware_cb(fw, state);
+ } else {
+ status = request_firmware_nowait(THIS_MODULE, 1,
+ state->microcode_name,
+ state->i2c->dev.parent,
+ GFP_KERNEL,
+ state, load_firmware_cb);
+ if (status < 0) {
+ printk(KERN_ERR
+ "drxk: failed to request a firmware\n");
+ return NULL;
+ }
+ }
+ } else if (init_drxk(state) < 0)
+ goto error;
+
+ printk(KERN_INFO "drxk: frontend initialized.\n");
+ return &state->frontend;
+
+error:
+ printk(KERN_ERR "drxk: not found\n");
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(drxk_attach);
+
+MODULE_DESCRIPTION("DRX-K driver");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h
index 6bb9fc4a7b96..6bb9fc4a7b96 100644
--- a/drivers/media/dvb/frontends/drxk_hard.h
+++ b/drivers/media/dvb-frontends/drxk_hard.h
diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb-frontends/drxk_map.h
index 23e16c12f234..23e16c12f234 100644
--- a/drivers/media/dvb/frontends/drxk_map.h
+++ b/drivers/media/dvb-frontends/drxk_map.h
diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c
index 4c8ac2657c4a..4c8ac2657c4a 100644
--- a/drivers/media/dvb/frontends/ds3000.c
+++ b/drivers/media/dvb-frontends/ds3000.c
diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h
index 1b736888ea37..1b736888ea37 100644
--- a/drivers/media/dvb/frontends/ds3000.h
+++ b/drivers/media/dvb-frontends/ds3000.h
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
new file mode 100644
index 000000000000..6d8fe8843237
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -0,0 +1,820 @@
+/*
+ * descriptions + helper functions for simple dvb plls.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "dvb-pll.h"
+
+struct dvb_pll_priv {
+ /* pll number */
+ int nr;
+
+ /* i2c details */
+ int pll_i2c_address;
+ struct i2c_adapter *i2c;
+
+ /* the PLL descriptor */
+ struct dvb_pll_desc *pll_desc;
+
+ /* cached frequency/bandwidth */
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#define DVB_PLL_MAX 64
+
+static unsigned int dvb_pll_devcount;
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+static unsigned int id[DVB_PLL_MAX] =
+ { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED };
+module_param_array(id, int, NULL, 0644);
+MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)");
+
+/* ----------------------------------------------------------- */
+
+struct dvb_pll_desc {
+ char *name;
+ u32 min;
+ u32 max;
+ u32 iffreq;
+ void (*set)(struct dvb_frontend *fe, u8 *buf);
+ u8 *initdata;
+ u8 *initdata2;
+ u8 *sleepdata;
+ int count;
+ struct {
+ u32 limit;
+ u32 stepsize;
+ u8 config;
+ u8 cb;
+ } entries[12];
+};
+
+/* ----------------------------------------------------------- */
+/* descriptions */
+
+static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
+ .name = "Thomson dtt7579",
+ .min = 177000000,
+ .max = 858000000,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xb4, 0x03 },
+ .count = 4,
+ .entries = {
+ { 443250000, 166667, 0xb4, 0x02 },
+ { 542000000, 166667, 0xb4, 0x08 },
+ { 771000000, 166667, 0xbc, 0x08 },
+ { 999999999, 166667, 0xf4, 0x08 },
+ },
+};
+
+static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 7000000)
+ buf[3] |= 0x10;
+}
+
+static struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
+ .name = "Thomson dtt759x",
+ .min = 177000000,
+ .max = 896000000,
+ .set = thomson_dtt759x_bw,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0x84, 0x03 },
+ .count = 5,
+ .entries = {
+ { 264000000, 166667, 0xb4, 0x02 },
+ { 470000000, 166667, 0xbc, 0x02 },
+ { 735000000, 166667, 0xbc, 0x08 },
+ { 835000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
+ },
+};
+
+static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] ^= 0x10;
+}
+
+static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = {
+ .name = "Thomson dtt7520x",
+ .min = 185000000,
+ .max = 900000000,
+ .set = thomson_dtt7520x_bw,
+ .iffreq = 36166667,
+ .count = 7,
+ .entries = {
+ { 305000000, 166667, 0xb4, 0x12 },
+ { 405000000, 166667, 0xbc, 0x12 },
+ { 445000000, 166667, 0xbc, 0x12 },
+ { 465000000, 166667, 0xf4, 0x18 },
+ { 735000000, 166667, 0xfc, 0x18 },
+ { 835000000, 166667, 0xbc, 0x18 },
+ { 999999999, 166667, 0xfc, 0x18 },
+ },
+};
+
+static struct dvb_pll_desc dvb_pll_lg_z201 = {
+ .name = "LG z201",
+ .min = 174000000,
+ .max = 862000000,
+ .iffreq= 36166667,
+ .sleepdata = (u8[]){ 2, 0xbc, 0x03 },
+ .count = 5,
+ .entries = {
+ { 157500000, 166667, 0xbc, 0x01 },
+ { 443250000, 166667, 0xbc, 0x02 },
+ { 542000000, 166667, 0xbc, 0x04 },
+ { 830000000, 166667, 0xf4, 0x04 },
+ { 999999999, 166667, 0xfc, 0x04 },
+ },
+};
+
+static struct dvb_pll_desc dvb_pll_unknown_1 = {
+ .name = "unknown 1", /* used by dntv live dvb-t */
+ .min = 174000000,
+ .max = 862000000,
+ .iffreq= 36166667,
+ .count = 9,
+ .entries = {
+ { 150000000, 166667, 0xb4, 0x01 },
+ { 173000000, 166667, 0xbc, 0x01 },
+ { 250000000, 166667, 0xb4, 0x02 },
+ { 400000000, 166667, 0xbc, 0x02 },
+ { 420000000, 166667, 0xf4, 0x02 },
+ { 470000000, 166667, 0xfc, 0x02 },
+ { 600000000, 166667, 0xbc, 0x08 },
+ { 730000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x08 },
+ },
+};
+
+/* Infineon TUA6010XS
+ * used in Thomson Cable Tuner
+ */
+static struct dvb_pll_desc dvb_pll_tua6010xs = {
+ .name = "Infineon TUA6010XS",
+ .min = 44250000,
+ .max = 858000000,
+ .iffreq= 36125000,
+ .count = 3,
+ .entries = {
+ { 115750000, 62500, 0x8e, 0x03 },
+ { 403250000, 62500, 0x8e, 0x06 },
+ { 999999999, 62500, 0x8e, 0x85 },
+ },
+};
+
+/* Panasonic env57h1xd5 (some Philips PLL ?) */
+static struct dvb_pll_desc dvb_pll_env57h1xd5 = {
+ .name = "Panasonic ENV57H1XD5",
+ .min = 44250000,
+ .max = 858000000,
+ .iffreq= 36125000,
+ .count = 4,
+ .entries = {
+ { 153000000, 166667, 0xc2, 0x41 },
+ { 470000000, 166667, 0xc2, 0x42 },
+ { 526000000, 166667, 0xc2, 0x84 },
+ { 999999999, 166667, 0xc2, 0xa4 },
+ },
+};
+
+/* Philips TDA6650/TDA6651
+ * used in Panasonic ENV77H11D5
+ */
+static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] |= 0x08;
+}
+
+static struct dvb_pll_desc dvb_pll_tda665x = {
+ .name = "Philips TDA6650/TDA6651",
+ .min = 44250000,
+ .max = 858000000,
+ .set = tda665x_bw,
+ .iffreq= 36166667,
+ .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab },
+ .count = 12,
+ .entries = {
+ { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ },
+ { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
+ { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ },
+ { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ },
+ { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
+ { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ },
+ { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ },
+ { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
+ { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ },
+ }
+};
+
+/* Infineon TUA6034
+ * used in LG TDTP E102P
+ */
+static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 7000000)
+ buf[3] |= 0x08;
+}
+
+static struct dvb_pll_desc dvb_pll_tua6034 = {
+ .name = "Infineon TUA6034",
+ .min = 44250000,
+ .max = 858000000,
+ .iffreq= 36166667,
+ .count = 3,
+ .set = tua6034_bw,
+ .entries = {
+ { 174500000, 62500, 0xce, 0x01 },
+ { 230000000, 62500, 0xce, 0x02 },
+ { 999999999, 62500, 0xce, 0x04 },
+ },
+};
+
+/* ALPS TDED4
+ * used in Nebula-Cards and USB boxes
+ */
+static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ if (bw == 8000000)
+ buf[3] |= 0x04;
+}
+
+static struct dvb_pll_desc dvb_pll_tded4 = {
+ .name = "ALPS TDED4",
+ .min = 47000000,
+ .max = 863000000,
+ .iffreq= 36166667,
+ .set = tded4_bw,
+ .count = 4,
+ .entries = {
+ { 153000000, 166667, 0x85, 0x01 },
+ { 470000000, 166667, 0x85, 0x02 },
+ { 823000000, 166667, 0x85, 0x08 },
+ { 999999999, 166667, 0x85, 0x88 },
+ }
+};
+
+/* ALPS TDHU2
+ * used in AverTVHD MCE A180
+ */
+static struct dvb_pll_desc dvb_pll_tdhu2 = {
+ .name = "ALPS TDHU2",
+ .min = 54000000,
+ .max = 864000000,
+ .iffreq= 44000000,
+ .count = 4,
+ .entries = {
+ { 162000000, 62500, 0x85, 0x01 },
+ { 426000000, 62500, 0x85, 0x02 },
+ { 782000000, 62500, 0x85, 0x08 },
+ { 999999999, 62500, 0x85, 0x88 },
+ }
+};
+
+/* Samsung TBMV30111IN / TBMV30712IN1
+ * used in Air2PC ATSC - 2nd generation (nxt2002)
+ */
+static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
+ .name = "Samsung TBMV30111IN / TBMV30712IN1",
+ .min = 54000000,
+ .max = 860000000,
+ .iffreq= 44000000,
+ .count = 6,
+ .entries = {
+ { 172000000, 166667, 0xb4, 0x01 },
+ { 214000000, 166667, 0xb4, 0x02 },
+ { 467000000, 166667, 0xbc, 0x02 },
+ { 721000000, 166667, 0xbc, 0x08 },
+ { 841000000, 166667, 0xf4, 0x08 },
+ { 999999999, 166667, 0xfc, 0x02 },
+ }
+};
+
+/*
+ * Philips SD1878 Tuner.
+ */
+static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
+ .name = "Philips SD1878",
+ .min = 950000,
+ .max = 2150000,
+ .iffreq= 249, /* zero-IF, offset 249 is to round up */
+ .count = 4,
+ .entries = {
+ { 1250000, 500, 0xc4, 0x00},
+ { 1450000, 500, 0xc4, 0x40},
+ { 2050000, 500, 0xc4, 0x80},
+ { 2150000, 500, 0xc4, 0xc0},
+ },
+};
+
+static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ u32 b_w = (c->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, 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 },
+ }
+};
+
+static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = {
+ .addr = priv->pll_i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 4
+ };
+ int result;
+
+ 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);
+
+ buf[2] = 0x9e;
+ buf[3] = 0x90;
+
+ return;
+}
+
+/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
+static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
+ .name = "Samsung DTOS403IH102A",
+ .min = 44250000,
+ .max = 858000000,
+ .iffreq = 36125000,
+ .count = 8,
+ .set = samsung_dtos403ih102a_set,
+ .entries = {
+ { 135000000, 62500, 0xbe, 0x01 },
+ { 177000000, 62500, 0xf6, 0x01 },
+ { 370000000, 62500, 0xbe, 0x02 },
+ { 450000000, 62500, 0xf6, 0x02 },
+ { 466000000, 62500, 0xfe, 0x02 },
+ { 538000000, 62500, 0xbe, 0x08 },
+ { 826000000, 62500, 0xf6, 0x08 },
+ { 999999999, 62500, 0xfe, 0x08 },
+ }
+};
+
+/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
+static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
+ .name = "Samsung TDTC9251DH0",
+ .min = 48000000,
+ .max = 863000000,
+ .iffreq = 36166667,
+ .count = 3,
+ .entries = {
+ { 157500000, 166667, 0xcc, 0x09 },
+ { 443000000, 166667, 0xcc, 0x0a },
+ { 863000000, 166667, 0xcc, 0x08 },
+ }
+};
+
+/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
+static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
+ .name = "Samsung TBDU18132",
+ .min = 950000,
+ .max = 2150000, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1550000, 125, 0x84, 0x82 },
+ { 4095937, 125, 0x84, 0x80 },
+ }
+ /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported
+ * by this driver. The two extra bits are 0x60 in the third byte. 15
+ * bits is enough for over 4 GHz, which is enough to cover the range
+ * of this tuner. We could use the additional divisor bits by adding
+ * more entries, e.g.
+ { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, },
+ { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, },
+ { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */
+};
+
+/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
+static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
+ .name = "Samsung TBMU24112",
+ .min = 950000,
+ .max = 2150000, /* guesses */
+ .iffreq = 0,
+ .count = 2,
+ .entries = {
+ { 1500000, 125, 0x84, 0x18 },
+ { 9999999, 125, 0x84, 0x08 },
+ }
+};
+
+/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */
+/* byte 4 : 1 * * AGD R3 R2 R1 R0
+ * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
+ * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95
+ * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
+ * 47 - 153 0 * 0 0 0 0 0 1 0x01
+ * 153 - 430 0 * 0 0 0 0 1 0 0x02
+ * 430 - 822 0 * 0 0 1 0 0 0 0x08
+ * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
+static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
+ .name = "ALPS TDEE4",
+ .min = 47000000,
+ .max = 862000000,
+ .iffreq = 36125000,
+ .count = 4,
+ .entries = {
+ { 153000000, 62500, 0x95, 0x01 },
+ { 430000000, 62500, 0x95, 0x02 },
+ { 822000000, 62500, 0x95, 0x08 },
+ { 999999999, 62500, 0x95, 0x88 },
+ }
+};
+
+/* ----------------------------------------------------------- */
+
+static struct dvb_pll_desc *pll_list[] = {
+ [DVB_PLL_UNDEFINED] = NULL,
+ [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
+ [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
+ [DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x,
+ [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201,
+ [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1,
+ [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs,
+ [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5,
+ [DVB_PLL_TUA6034] = &dvb_pll_tua6034,
+ [DVB_PLL_TDA665X] = &dvb_pll_tda665x,
+ [DVB_PLL_TDED4] = &dvb_pll_tded4,
+ [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4,
+ [DVB_PLL_TDHU2] = &dvb_pll_tdhu2,
+ [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv,
+ [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
+ [DVB_PLL_OPERA1] = &dvb_pll_opera1,
+ [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a,
+ [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0,
+ [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132,
+ [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112,
+};
+
+/* ----------------------------------------------------------- */
+/* code */
+
+static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
+ const u32 frequency)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ struct dvb_pll_desc *desc = priv->pll_desc;
+ u32 div;
+ int i;
+
+ if (frequency && (frequency < desc->min || frequency > desc->max))
+ return -EINVAL;
+
+ for (i = 0; i < desc->count; i++) {
+ if (frequency > desc->entries[i].limit)
+ continue;
+ break;
+ }
+
+ if (debug)
+ printk("pll: %s: freq=%d | i=%d/%d\n", desc->name,
+ frequency, i, desc->count);
+ if (i == desc->count)
+ return -EINVAL;
+
+ div = (frequency + desc->iffreq +
+ desc->entries[i].stepsize/2) / desc->entries[i].stepsize;
+ buf[0] = div >> 8;
+ buf[1] = div & 0xff;
+ buf[2] = desc->entries[i].config;
+ buf[3] = desc->entries[i].cb;
+
+ if (desc->set)
+ desc->set(fe, buf);
+
+ if (debug)
+ printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
+ desc->name, div, buf[0], buf[1], buf[2], buf[3]);
+
+ // calculate the frequency we set it to
+ return (div * desc->entries[i].stepsize) - desc->iffreq;
+}
+
+static int dvb_pll_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int dvb_pll_sleep(struct dvb_frontend *fe)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ if (priv->pll_desc->sleepdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->sleepdata + 1,
+ .len = priv->pll_desc->sleepdata[0] };
+
+ int result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+ return 0;
+ }
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
+}
+
+static int dvb_pll_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ u8 buf[4];
+ struct i2c_msg msg =
+ { .addr = priv->pll_i2c_address, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+ int result;
+ u32 frequency = 0;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ result = dvb_pll_configure(fe, buf, c->frequency);
+ if (result < 0)
+ return result;
+ else
+ frequency = result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
+ return result;
+ }
+
+ priv->frequency = frequency;
+ priv->bandwidth = c->bandwidth_hz;
+
+ return 0;
+}
+
+static int dvb_pll_calc_regs(struct dvb_frontend *fe,
+ u8 *buf, int buf_len)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ int result;
+ u32 frequency = 0;
+
+ if (buf_len < 5)
+ return -EINVAL;
+
+ result = dvb_pll_configure(fe, buf + 1, c->frequency);
+ if (result < 0)
+ return result;
+ else
+ frequency = result;
+
+ buf[0] = priv->pll_i2c_address;
+
+ priv->frequency = frequency;
+ priv->bandwidth = c->bandwidth_hz;
+
+ return 5;
+}
+
+static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int dvb_pll_init(struct dvb_frontend *fe)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
+ if (priv->pll_desc->initdata) {
+ struct i2c_msg msg = { .flags = 0,
+ .addr = priv->pll_i2c_address,
+ .buf = priv->pll_desc->initdata + 1,
+ .len = priv->pll_desc->initdata[0] };
+
+ int result;
+ 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;
+ 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;
+ }
+ /* Shouldn't be called when initdata is NULL, maybe BUG()? */
+ return -EINVAL;
+}
+
+static struct dvb_tuner_ops dvb_pll_tuner_ops = {
+ .release = dvb_pll_release,
+ .sleep = dvb_pll_sleep,
+ .init = dvb_pll_init,
+ .set_params = dvb_pll_set_params,
+ .calc_regs = dvb_pll_calc_regs,
+ .get_frequency = dvb_pll_get_frequency,
+ .get_bandwidth = dvb_pll_get_bandwidth,
+};
+
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id)
+{
+ u8 b1 [] = { 0 };
+ struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD,
+ .buf = b1, .len = 1 };
+ struct dvb_pll_priv *priv = NULL;
+ int ret;
+ struct dvb_pll_desc *desc;
+
+ if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) &&
+ (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list)))
+ pll_desc_id = id[dvb_pll_devcount];
+
+ BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list));
+
+ desc = pll_list[pll_desc_id];
+
+ if (i2c != NULL) {
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = i2c_transfer (i2c, &msg, 1);
+ if (ret != 1)
+ return NULL;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->pll_i2c_address = pll_addr;
+ priv->i2c = i2c;
+ priv->pll_desc = desc;
+ priv->nr = dvb_pll_devcount++;
+
+ memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ strncpy(fe->ops.tuner_ops.info.name, desc->name,
+ sizeof(fe->ops.tuner_ops.info.name));
+ fe->ops.tuner_ops.info.frequency_min = desc->min;
+ fe->ops.tuner_ops.info.frequency_max = desc->max;
+ if (!desc->initdata)
+ fe->ops.tuner_ops.init = NULL;
+ if (!desc->sleepdata)
+ fe->ops.tuner_ops.sleep = NULL;
+
+ fe->tuner_priv = priv;
+
+ if ((debug) || (id[priv->nr] == pll_desc_id)) {
+ printk("dvb-pll[%d]", priv->nr);
+ if (i2c != NULL)
+ printk(" %d-%04x", i2c_adapter_id(i2c), pll_addr);
+ printk(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name,
+ id[priv->nr] == pll_desc_id ?
+ "insmod option" : "autodetected");
+ }
+
+ return fe;
+}
+EXPORT_SYMBOL(dvb_pll_attach);
+
+MODULE_DESCRIPTION("dvb pll library");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h
new file mode 100644
index 000000000000..4de754f76ce9
--- /dev/null
+++ b/drivers/media/dvb-frontends/dvb-pll.h
@@ -0,0 +1,57 @@
+/*
+ * descriptions + helper functions for simple dvb plls.
+ */
+
+#ifndef __DVB_PLL_H__
+#define __DVB_PLL_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+#define DVB_PLL_UNDEFINED 0
+#define DVB_PLL_THOMSON_DTT7579 1
+#define DVB_PLL_THOMSON_DTT759X 2
+#define DVB_PLL_LG_Z201 3
+#define DVB_PLL_UNKNOWN_1 4
+#define DVB_PLL_TUA6010XS 5
+#define DVB_PLL_ENV57H1XD5 6
+#define DVB_PLL_TUA6034 7
+#define DVB_PLL_TDA665X 8
+#define DVB_PLL_TDED4 9
+#define DVB_PLL_TDHU2 10
+#define DVB_PLL_SAMSUNG_TBMV 11
+#define DVB_PLL_PHILIPS_SD1878_TDA8261 12
+#define DVB_PLL_OPERA1 13
+#define DVB_PLL_SAMSUNG_DTOS403IH102A 14
+#define DVB_PLL_SAMSUNG_TDTC9251DH0 15
+#define DVB_PLL_SAMSUNG_TBDU18132 16
+#define DVB_PLL_SAMSUNG_TBMU24112 17
+#define DVB_PLL_TDEE4 18
+#define DVB_PLL_THOMSON_DTT7520X 19
+
+/**
+ * Attach a dvb-pll to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param pll_addr i2c address of the PLL (if used).
+ * @param i2c i2c adapter to use (set to NULL if not used).
+ * @param pll_desc_id dvb_pll_desc to use.
+ * @return Frontend pointer on success, NULL on failure
+ */
+#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE))
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id);
+#else
+static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
+ int pll_addr,
+ struct i2c_adapter *i2c,
+ unsigned int pll_desc_id)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c
index dcfc902c8678..dcfc902c8678 100644
--- a/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c
diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h
index 1fcb987d6386..1fcb987d6386 100644
--- a/drivers/media/dvb/frontends/dvb_dummy_fe.h
+++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h
diff --git a/drivers/media/dvb-frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c
new file mode 100644
index 000000000000..9d424809d06b
--- /dev/null
+++ b/drivers/media/dvb-frontends/ec100.c
@@ -0,0 +1,345 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "dvb_frontend.h"
+#include "ec100.h"
+
+struct ec100_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+ struct ec100_config config;
+
+ u16 ber;
+};
+
+/* write single register */
+static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
+{
+ int ret;
+ u8 buf[2] = {reg, val};
+ struct i2c_msg msg[1] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
+ KBUILD_MODNAME, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read single register */
+static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config.demod_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg
+ }, {
+ .addr = state->config.demod_address,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = val
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n",
+ KBUILD_MODNAME, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+static int ec100_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp, tmp2;
+
+ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n",
+ __func__, c->frequency, c->bandwidth_hz);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ ret = ec100_write_reg(state, 0x04, 0x06);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x67, 0x58);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x05, 0x18);
+ if (ret)
+ goto error;
+
+ /* reg/bw | 6 | 7 | 8
+ -------+------+------+------
+ A 0x1b | 0xa1 | 0xe7 | 0x2c
+ A 0x1c | 0x55 | 0x63 | 0x72
+ -------+------+------+------
+ B 0x1b | 0xb7 | 0x00 | 0x49
+ B 0x1c | 0x55 | 0x64 | 0x72 */
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ tmp = 0xb7;
+ tmp2 = 0x55;
+ break;
+ case 7000000:
+ tmp = 0x00;
+ tmp2 = 0x64;
+ break;
+ case 8000000:
+ default:
+ tmp = 0x49;
+ tmp2 = 0x72;
+ }
+
+ ret = ec100_write_reg(state, 0x1b, tmp);
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x1c, tmp2);
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x08, 0x24);
+ if (ret)
+ goto error;
+
+ ret = ec100_write_reg(state, 0x00, 0x00); /* go */
+ if (ret)
+ goto error;
+ ret = ec100_write_reg(state, 0x00, 0x20); /* go */
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 300;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ ret = ec100_read_reg(state, 0x42, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x80) {
+ /* bit7 set - have lock */
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ ret = ec100_read_reg(state, 0x01, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x10) {
+ /* bit4 set - have signal */
+ *status |= FE_HAS_SIGNAL;
+ if (!(tmp & 0x01)) {
+ /* bit0 clear - have ~valid signal */
+ *status |= FE_HAS_CARRIER | FE_HAS_VITERBI;
+ }
+ }
+ }
+
+ return ret;
+error:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp, tmp2;
+ u16 ber2;
+
+ *ber = 0;
+
+ ret = ec100_read_reg(state, 0x65, &tmp);
+ if (ret)
+ goto error;
+ ret = ec100_read_reg(state, 0x66, &tmp2);
+ if (ret)
+ goto error;
+
+ ber2 = (tmp2 << 8) | tmp;
+
+ /* if counter overflow or clear */
+ if (ber2 < state->ber)
+ *ber = ber2;
+ else
+ *ber = ber2 - state->ber;
+
+ state->ber = ber2;
+
+ return ret;
+error:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ ret = ec100_read_reg(state, 0x24, &tmp);
+ if (ret) {
+ *strength = 0;
+ goto error;
+ }
+
+ *strength = ((tmp << 8) | tmp);
+
+ return ret;
+error:
+ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static void ec100_release(struct dvb_frontend *fe)
+{
+ struct ec100_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops ec100_ops;
+
+struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct ec100_state *state = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->config, config, sizeof(struct ec100_config));
+
+ /* check if the demod is there */
+ ret = ec100_read_reg(state, 0x33, &tmp);
+ if (ret || tmp != 0x0b)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &ec100_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(ec100_attach);
+
+static struct dvb_frontend_ops ec100_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "E3C EC100 DVB-T",
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = ec100_release,
+ .set_frontend = ec100_set_frontend,
+ .get_tune_settings = ec100_get_tune_settings,
+ .read_status = ec100_read_status,
+ .read_ber = ec100_read_ber,
+ .read_signal_strength = ec100_read_signal_strength,
+ .read_snr = ec100_read_snr,
+ .read_ucblocks = ec100_read_ucblocks,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h
new file mode 100644
index 000000000000..b8479719d7f1
--- /dev/null
+++ b/drivers/media/dvb-frontends/ec100.h
@@ -0,0 +1,46 @@
+/*
+ * E3C EC100 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 EC100_H
+#define EC100_H
+
+#include <linux/dvb/frontend.h>
+
+struct ec100_config {
+ /* demodulator's I2C address */
+ u8 demod_address;
+};
+
+
+#if defined(CONFIG_DVB_EC100) || \
+ (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE))
+extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *ec100_attach(
+ const struct ec100_config *config, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* EC100_H */
diff --git a/drivers/media/dvb/frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h
index c983f2f85802..c983f2f85802 100644
--- a/drivers/media/dvb/frontends/eds1547.h
+++ b/drivers/media/dvb-frontends/eds1547.h
diff --git a/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c
new file mode 100644
index 000000000000..d7b9d549156d
--- /dev/null
+++ b/drivers/media/dvb-frontends/hd29l2.c
@@ -0,0 +1,866 @@
+/*
+ * HDIC HD29L2 DMB-TH demodulator driver
+ *
+ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
+ *
+ * Author: Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "hd29l2_priv.h"
+
+/* write multiple registers */
+static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[2 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = 0x00;
+ buf[1] = reg;
+ memcpy(&buf[2], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c wr failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read multiple registers */
+static int hd29l2_rd_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[2] = { 0x00, reg };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev,
+ "%s: i2c rd failed=%d reg=%02x len=%d\n",
+ KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int hd29l2_wr_reg(struct hd29l2_priv *priv, u8 reg, u8 val)
+{
+ return hd29l2_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int hd29l2_rd_reg(struct hd29l2_priv *priv, u8 reg, u8 *val)
+{
+ return hd29l2_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = hd29l2_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return hd29l2_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = hd29l2_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int hd29l2_soft_reset(struct hd29l2_priv *priv)
+{
+ int ret;
+ u8 tmp;
+
+ ret = hd29l2_rd_reg(priv, 0x26, &tmp);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_wr_reg(priv, 0x26, 0x0d);
+ if (ret)
+ goto err;
+
+ usleep_range(10000, 20000);
+
+ ret = hd29l2_wr_reg(priv, 0x26, tmp);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret, i;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 tmp;
+
+ dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable);
+
+ /* set tuner address for demod */
+ if (!priv->tuner_i2c_addr_programmed && enable) {
+ /* no need to set tuner address every time, once is enough */
+ ret = hd29l2_wr_reg(priv, 0x9d, priv->cfg.tuner_i2c_addr << 1);
+ if (ret)
+ goto err;
+
+ priv->tuner_i2c_addr_programmed = true;
+ }
+
+ /* open / close gate */
+ ret = hd29l2_wr_reg(priv, 0x9f, enable);
+ if (ret)
+ goto err;
+
+ /* wait demod ready */
+ for (i = 10; i; i--) {
+ ret = hd29l2_rd_reg(priv, 0x9e, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp == enable)
+ break;
+
+ usleep_range(5000, 10000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ int ret;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 buf[2];
+
+ *status = 0;
+
+ ret = hd29l2_rd_reg(priv, 0x05, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (buf[0] & 0x01) {
+ /* full lock */
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
+ FE_HAS_SYNC | FE_HAS_LOCK;
+ } else {
+ ret = hd29l2_rd_reg(priv, 0x0d, &buf[1]);
+ if (ret)
+ goto err;
+
+ if ((buf[1] & 0xfe) == 0x78)
+ /* partial lock */
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC;
+ }
+
+ priv->fe_status = *status;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ int ret;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 buf[2];
+ u16 tmp;
+
+ if (!(priv->fe_status & FE_HAS_LOCK)) {
+ *snr = 0;
+ ret = 0;
+ goto err;
+ }
+
+ ret = hd29l2_rd_regs(priv, 0x0b, buf, 2);
+ if (ret)
+ goto err;
+
+ tmp = (buf[0] << 8) | buf[1];
+
+ /* report SNR in dB * 10 */
+ #define LOG10_20736_24 72422627 /* log10(20736) << 24 */
+ if (tmp)
+ *snr = (LOG10_20736_24 - intlog10(tmp)) / ((1 << 24) / 100);
+ else
+ *snr = 0;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ int ret;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 buf[2];
+ u16 tmp;
+
+ *strength = 0;
+
+ ret = hd29l2_rd_regs(priv, 0xd5, buf, 2);
+ if (ret)
+ goto err;
+
+ tmp = buf[0] << 8 | buf[1];
+ tmp = ~tmp & 0x0fff;
+
+ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
+ *strength = tmp * 0xffff / 0x0fff;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ int ret;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 buf[2];
+
+ if (!(priv->fe_status & FE_HAS_SYNC)) {
+ *ber = 0;
+ ret = 0;
+ goto err;
+ }
+
+ ret = hd29l2_rd_regs(priv, 0xd9, buf, 2);
+ if (ret) {
+ *ber = 0;
+ goto err;
+ }
+
+ /* LDPC BER */
+ *ber = ((buf[0] & 0x0f) << 8) | buf[1];
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ /* no way to read? */
+ *ucblocks = 0;
+ return 0;
+}
+
+static enum dvbfe_search hd29l2_search(struct dvb_frontend *fe)
+{
+ int ret, i;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 tmp, buf[3];
+ u8 modulation, carrier, guard_interval, interleave, code_rate;
+ u64 num64;
+ u32 if_freq, if_ctl;
+ bool auto_mode;
+
+ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
+ "bandwidth_hz=%d modulation=%d inversion=%d " \
+ "fec_inner=%d guard_interval=%d\n", __func__,
+ c->delivery_system, c->frequency, c->bandwidth_hz,
+ c->modulation, c->inversion, c->fec_inner,
+ c->guard_interval);
+
+ /* as for now we detect always params automatically */
+ auto_mode = true;
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* get and program IF */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
+ else
+ if_freq = 0;
+
+ if (if_freq) {
+ /* normal IF */
+
+ /* calc IF control value */
+ num64 = if_freq;
+ num64 *= 0x800000;
+ num64 = div_u64(num64, HD29L2_XTAL);
+ num64 -= 0x800000;
+ if_ctl = num64;
+
+ tmp = 0xfc; /* tuner type normal */
+ } else {
+ /* zero IF */
+ if_ctl = 0;
+ tmp = 0xfe; /* tuner type Zero-IF */
+ }
+
+ buf[0] = ((if_ctl >> 0) & 0xff);
+ buf[1] = ((if_ctl >> 8) & 0xff);
+ buf[2] = ((if_ctl >> 16) & 0xff);
+
+ /* program IF control */
+ ret = hd29l2_wr_regs(priv, 0x14, buf, 3);
+ if (ret)
+ goto err;
+
+ /* program tuner type */
+ ret = hd29l2_wr_reg(priv, 0xab, tmp);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d if_ctl=%x\n",
+ __func__, if_freq, if_ctl);
+
+ if (auto_mode) {
+ /*
+ * use auto mode
+ */
+
+ /* disable quick mode */
+ ret = hd29l2_wr_reg_mask(priv, 0xac, 0 << 7, 0x80);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_wr_reg_mask(priv, 0x82, 1 << 1, 0x02);
+ if (ret)
+ goto err;
+
+ /* enable auto mode */
+ ret = hd29l2_wr_reg_mask(priv, 0x7d, 1 << 6, 0x40);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_wr_reg_mask(priv, 0x81, 1 << 3, 0x08);
+ if (ret)
+ goto err;
+
+ /* soft reset */
+ ret = hd29l2_soft_reset(priv);
+ if (ret)
+ goto err;
+
+ /* detect modulation */
+ for (i = 30; i; i--) {
+ msleep(100);
+
+ ret = hd29l2_rd_reg(priv, 0x0d, &tmp);
+ if (ret)
+ goto err;
+
+ if ((((tmp & 0xf0) >= 0x10) &&
+ ((tmp & 0x0f) == 0x08)) || (tmp >= 0x2c))
+ break;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0)
+ /* detection failed */
+ return DVBFE_ALGO_SEARCH_FAILED;
+
+ /* read modulation */
+ ret = hd29l2_rd_reg_mask(priv, 0x7d, &modulation, 0x07);
+ if (ret)
+ goto err;
+ } else {
+ /*
+ * use manual mode
+ */
+
+ modulation = HD29L2_QAM64;
+ carrier = HD29L2_CARRIER_MULTI;
+ guard_interval = HD29L2_PN945;
+ interleave = HD29L2_INTERLEAVER_420;
+ code_rate = HD29L2_CODE_RATE_08;
+
+ tmp = (code_rate << 3) | modulation;
+ ret = hd29l2_wr_reg_mask(priv, 0x7d, tmp, 0x5f);
+ if (ret)
+ goto err;
+
+ tmp = (carrier << 2) | guard_interval;
+ ret = hd29l2_wr_reg_mask(priv, 0x81, tmp, 0x0f);
+ if (ret)
+ goto err;
+
+ tmp = interleave;
+ ret = hd29l2_wr_reg_mask(priv, 0x82, tmp, 0x03);
+ if (ret)
+ goto err;
+ }
+
+ /* ensure modulation validy */
+ /* 0=QAM4_NR, 1=QAM4, 2=QAM16, 3=QAM32, 4=QAM64 */
+ if (modulation > (ARRAY_SIZE(reg_mod_vals_tab[0].val) - 1)) {
+ dev_dbg(&priv->i2c->dev, "%s: modulation=%d not valid\n",
+ __func__, modulation);
+ goto err;
+ }
+
+ /* program registers according to modulation */
+ for (i = 0; i < ARRAY_SIZE(reg_mod_vals_tab); i++) {
+ ret = hd29l2_wr_reg(priv, reg_mod_vals_tab[i].reg,
+ reg_mod_vals_tab[i].val[modulation]);
+ if (ret)
+ goto err;
+ }
+
+ /* read guard interval */
+ ret = hd29l2_rd_reg_mask(priv, 0x81, &guard_interval, 0x03);
+ if (ret)
+ goto err;
+
+ /* read carrier mode */
+ ret = hd29l2_rd_reg_mask(priv, 0x81, &carrier, 0x04);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev,
+ "%s: modulation=%d guard_interval=%d carrier=%d\n",
+ __func__, modulation, guard_interval, carrier);
+
+ if ((carrier == HD29L2_CARRIER_MULTI) && (modulation == HD29L2_QAM64) &&
+ (guard_interval == HD29L2_PN945)) {
+ dev_dbg(&priv->i2c->dev, "%s: C=3780 && QAM64 && PN945\n",
+ __func__);
+
+ ret = hd29l2_wr_reg(priv, 0x42, 0x33);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_wr_reg(priv, 0xdd, 0x01);
+ if (ret)
+ goto err;
+ }
+
+ usleep_range(10000, 20000);
+
+ /* soft reset */
+ ret = hd29l2_soft_reset(priv);
+ if (ret)
+ goto err;
+
+ /* wait demod lock */
+ for (i = 30; i; i--) {
+ msleep(100);
+
+ /* read lock bit */
+ ret = hd29l2_rd_reg_mask(priv, 0x05, &tmp, 0x01);
+ if (ret)
+ goto err;
+
+ if (tmp)
+ break;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0)
+ return DVBFE_ALGO_SEARCH_AGAIN;
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static int hd29l2_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static int hd29l2_get_frontend(struct dvb_frontend *fe)
+{
+ int ret;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 buf[3];
+ u32 if_ctl;
+ char *str_constellation, *str_code_rate, *str_constellation_code_rate,
+ *str_guard_interval, *str_carrier, *str_guard_interval_carrier,
+ *str_interleave, *str_interleave_;
+
+ ret = hd29l2_rd_reg(priv, 0x7d, &buf[0]);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_rd_regs(priv, 0x81, &buf[1], 2);
+ if (ret)
+ goto err;
+
+ /* constellation, 0x7d[2:0] */
+ switch ((buf[0] >> 0) & 0x07) {
+ case 0: /* QAM4NR */
+ str_constellation = "QAM4NR";
+ c->modulation = QAM_AUTO; /* FIXME */
+ break;
+ case 1: /* QAM4 */
+ str_constellation = "QAM4";
+ c->modulation = QPSK; /* FIXME */
+ break;
+ case 2:
+ str_constellation = "QAM16";
+ c->modulation = QAM_16;
+ break;
+ case 3:
+ str_constellation = "QAM32";
+ c->modulation = QAM_32;
+ break;
+ case 4:
+ str_constellation = "QAM64";
+ c->modulation = QAM_64;
+ break;
+ default:
+ str_constellation = "?";
+ }
+
+ /* LDPC code rate, 0x7d[4:3] */
+ switch ((buf[0] >> 3) & 0x03) {
+ case 0: /* 0.4 */
+ str_code_rate = "0.4";
+ c->fec_inner = FEC_AUTO; /* FIXME */
+ break;
+ case 1: /* 0.6 */
+ str_code_rate = "0.6";
+ c->fec_inner = FEC_3_5;
+ break;
+ case 2: /* 0.8 */
+ str_code_rate = "0.8";
+ c->fec_inner = FEC_4_5;
+ break;
+ default:
+ str_code_rate = "?";
+ }
+
+ /* constellation & code rate set, 0x7d[6] */
+ switch ((buf[0] >> 6) & 0x01) {
+ case 0:
+ str_constellation_code_rate = "manual";
+ break;
+ case 1:
+ str_constellation_code_rate = "auto";
+ break;
+ default:
+ str_constellation_code_rate = "?";
+ }
+
+ /* frame header, 0x81[1:0] */
+ switch ((buf[1] >> 0) & 0x03) {
+ case 0: /* PN945 */
+ str_guard_interval = "PN945";
+ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
+ break;
+ case 1: /* PN595 */
+ str_guard_interval = "PN595";
+ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
+ break;
+ case 2: /* PN420 */
+ str_guard_interval = "PN420";
+ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
+ break;
+ default:
+ str_guard_interval = "?";
+ }
+
+ /* carrier, 0x81[2] */
+ switch ((buf[1] >> 2) & 0x01) {
+ case 0:
+ str_carrier = "C=1";
+ break;
+ case 1:
+ str_carrier = "C=3780";
+ break;
+ default:
+ str_carrier = "?";
+ }
+
+ /* frame header & carrier set, 0x81[3] */
+ switch ((buf[1] >> 3) & 0x01) {
+ case 0:
+ str_guard_interval_carrier = "manual";
+ break;
+ case 1:
+ str_guard_interval_carrier = "auto";
+ break;
+ default:
+ str_guard_interval_carrier = "?";
+ }
+
+ /* interleave, 0x82[0] */
+ switch ((buf[2] >> 0) & 0x01) {
+ case 0:
+ str_interleave = "M=720";
+ break;
+ case 1:
+ str_interleave = "M=240";
+ break;
+ default:
+ str_interleave = "?";
+ }
+
+ /* interleave set, 0x82[1] */
+ switch ((buf[2] >> 1) & 0x01) {
+ case 0:
+ str_interleave_ = "manual";
+ break;
+ case 1:
+ str_interleave_ = "auto";
+ break;
+ default:
+ str_interleave_ = "?";
+ }
+
+ /*
+ * We can read out current detected NCO and use that value next
+ * time instead of calculating new value from targed IF.
+ * I think it will not effect receiver sensitivity but gaining lock
+ * after tune could be easier...
+ */
+ ret = hd29l2_rd_regs(priv, 0xb1, &buf[0], 3);
+ if (ret)
+ goto err;
+
+ if_ctl = (buf[0] << 16) | ((buf[1] - 7) << 8) | buf[2];
+
+ dev_dbg(&priv->i2c->dev, "%s: %s %s %s | %s %s %s | %s %s | NCO=%06x\n",
+ __func__, str_constellation, str_code_rate,
+ str_constellation_code_rate, str_guard_interval,
+ str_carrier, str_guard_interval_carrier, str_interleave,
+ str_interleave_, if_ctl);
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int hd29l2_init(struct dvb_frontend *fe)
+{
+ int ret, i;
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ u8 tmp;
+ static const struct reg_val tab[] = {
+ { 0x3a, 0x06 },
+ { 0x3b, 0x03 },
+ { 0x3c, 0x04 },
+ { 0xaf, 0x06 },
+ { 0xb0, 0x1b },
+ { 0x80, 0x64 },
+ { 0x10, 0x38 },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ /* reset demod */
+ /* it is recommended to HW reset chip using RST_N pin */
+ if (fe->callback) {
+ ret = fe->callback(fe, DVB_FRONTEND_COMPONENT_DEMOD, 0, 0);
+ if (ret)
+ goto err;
+
+ /* reprogramming needed because HW reset clears registers */
+ priv->tuner_i2c_addr_programmed = false;
+ }
+
+ /* init */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = hd29l2_wr_reg(priv, tab[i].reg, tab[i].val);
+ if (ret)
+ goto err;
+ }
+
+ /* TS params */
+ ret = hd29l2_rd_reg(priv, 0x36, &tmp);
+ if (ret)
+ goto err;
+
+ tmp &= 0x1b;
+ tmp |= priv->cfg.ts_mode;
+ ret = hd29l2_wr_reg(priv, 0x36, tmp);
+ if (ret)
+ goto err;
+
+ ret = hd29l2_rd_reg(priv, 0x31, &tmp);
+ tmp &= 0xef;
+
+ if (!(priv->cfg.ts_mode >> 7))
+ /* set b4 for serial TS */
+ tmp |= 0x10;
+
+ ret = hd29l2_wr_reg(priv, 0x31, tmp);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static void hd29l2_release(struct dvb_frontend *fe)
+{
+ struct hd29l2_priv *priv = fe->demodulator_priv;
+ kfree(priv);
+}
+
+static struct dvb_frontend_ops hd29l2_ops;
+
+struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct hd29l2_priv *priv = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct hd29l2_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the state */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, config, sizeof(struct hd29l2_config));
+
+
+ /* check if the demod is there */
+ ret = hd29l2_rd_reg(priv, 0x00, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &hd29l2_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ return &priv->fe;
+err:
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(hd29l2_attach);
+
+static struct dvb_frontend_ops hd29l2_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "HDIC HD29L2 DMB-TH",
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_BANDWIDTH_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER
+ },
+
+ .release = hd29l2_release,
+
+ .init = hd29l2_init,
+
+ .get_frontend_algo = hd29l2_get_frontend_algo,
+ .search = hd29l2_search,
+ .get_frontend = hd29l2_get_frontend,
+
+ .read_status = hd29l2_read_status,
+ .read_snr = hd29l2_read_snr,
+ .read_signal_strength = hd29l2_read_signal_strength,
+ .read_ber = hd29l2_read_ber,
+ .read_ucblocks = hd29l2_read_ucblocks,
+
+ .i2c_gate_ctrl = hd29l2_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("HDIC HD29L2 DMB-TH demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/hd29l2.h b/drivers/media/dvb-frontends/hd29l2.h
new file mode 100644
index 000000000000..4ad00d79aa77
--- /dev/null
+++ b/drivers/media/dvb-frontends/hd29l2.h
@@ -0,0 +1,66 @@
+/*
+ * HDIC HD29L2 DMB-TH demodulator driver
+ *
+ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
+ *
+ * Author: Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 HD29L2_H
+#define HD29L2_H
+
+#include <linux/dvb/frontend.h>
+
+struct hd29l2_config {
+ /*
+ * demodulator I2C address
+ */
+ u8 i2c_addr;
+
+ /*
+ * tuner I2C address
+ * only needed when tuner is behind demod I2C-gate
+ */
+ u8 tuner_i2c_addr;
+
+ /*
+ * TS settings
+ */
+#define HD29L2_TS_SERIAL 0x00
+#define HD29L2_TS_PARALLEL 0x80
+#define HD29L2_TS_CLK_NORMAL 0x40
+#define HD29L2_TS_CLK_INVERTED 0x00
+#define HD29L2_TS_CLK_GATED 0x20
+#define HD29L2_TS_CLK_FREE 0x00
+ u8 ts_mode;
+};
+
+
+#if defined(CONFIG_DVB_HD29L2) || \
+ (defined(CONFIG_DVB_HD29L2_MODULE) && defined(MODULE))
+extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *hd29l2_attach(
+const struct hd29l2_config *config, struct i2c_adapter *i2c)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* HD29L2_H */
diff --git a/drivers/media/dvb-frontends/hd29l2_priv.h b/drivers/media/dvb-frontends/hd29l2_priv.h
new file mode 100644
index 000000000000..4d571a2282d4
--- /dev/null
+++ b/drivers/media/dvb-frontends/hd29l2_priv.h
@@ -0,0 +1,301 @@
+/*
+ * HDIC HD29L2 DMB-TH demodulator driver
+ *
+ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
+ *
+ * Author: Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 HD29L2_PRIV
+#define HD29L2_PRIV
+
+#include <linux/dvb/version.h>
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "hd29l2.h"
+
+#define HD29L2_XTAL 30400000 /* Hz */
+
+
+#define HD29L2_QAM4NR 0x00
+#define HD29L2_QAM4 0x01
+#define HD29L2_QAM16 0x02
+#define HD29L2_QAM32 0x03
+#define HD29L2_QAM64 0x04
+
+#define HD29L2_CODE_RATE_04 0x00
+#define HD29L2_CODE_RATE_06 0x08
+#define HD29L2_CODE_RATE_08 0x10
+
+#define HD29L2_PN945 0x00
+#define HD29L2_PN595 0x01
+#define HD29L2_PN420 0x02
+
+#define HD29L2_CARRIER_SINGLE 0x00
+#define HD29L2_CARRIER_MULTI 0x01
+
+#define HD29L2_INTERLEAVER_720 0x00
+#define HD29L2_INTERLEAVER_420 0x01
+
+struct reg_val {
+ u8 reg;
+ u8 val;
+};
+
+struct reg_mod_vals {
+ u8 reg;
+ u8 val[5];
+};
+
+struct hd29l2_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct hd29l2_config cfg;
+ u8 tuner_i2c_addr_programmed:1;
+
+ fe_status_t fe_status;
+};
+
+static const struct reg_mod_vals reg_mod_vals_tab[] = {
+ /* REG, QAM4NR, QAM4,QAM16,QAM32,QAM64 */
+ { 0x01, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
+ { 0x02, { 0x07, 0x07, 0x07, 0x07, 0x07 } },
+ { 0x03, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
+ { 0x04, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x05, { 0x61, 0x60, 0x60, 0x61, 0x60 } },
+ { 0x06, { 0xff, 0xff, 0xff, 0xff, 0xff } },
+ { 0x07, { 0xff, 0xff, 0xff, 0xff, 0xff } },
+ { 0x08, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x09, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x0a, { 0x15, 0x15, 0x03, 0x03, 0x03 } },
+ { 0x0d, { 0x78, 0x78, 0x88, 0x78, 0x78 } },
+ { 0x0e, { 0xa0, 0x90, 0xa0, 0xa0, 0xa0 } },
+ { 0x0f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x10, { 0xa0, 0xa0, 0x58, 0x38, 0x38 } },
+ { 0x11, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x12, { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a } },
+ { 0x13, { 0xa2, 0xa2, 0xa2, 0xa2, 0xa2 } },
+ { 0x17, { 0x40, 0x40, 0x40, 0x40, 0x40 } },
+ { 0x18, { 0x21, 0x21, 0x42, 0x52, 0x42 } },
+ { 0x19, { 0x21, 0x21, 0x62, 0x72, 0x62 } },
+ { 0x1a, { 0x32, 0x43, 0xa9, 0xb9, 0xa9 } },
+ { 0x1b, { 0x32, 0x43, 0xb9, 0xd8, 0xb9 } },
+ { 0x1c, { 0x02, 0x02, 0x03, 0x02, 0x03 } },
+ { 0x1d, { 0x0c, 0x0c, 0x01, 0x02, 0x02 } },
+ { 0x1e, { 0x02, 0x02, 0x02, 0x01, 0x02 } },
+ { 0x1f, { 0x02, 0x02, 0x01, 0x02, 0x04 } },
+ { 0x20, { 0x01, 0x02, 0x01, 0x01, 0x01 } },
+ { 0x21, { 0x08, 0x08, 0x0a, 0x0a, 0x0a } },
+ { 0x22, { 0x06, 0x06, 0x04, 0x05, 0x05 } },
+ { 0x23, { 0x06, 0x06, 0x05, 0x03, 0x05 } },
+ { 0x24, { 0x08, 0x08, 0x05, 0x07, 0x07 } },
+ { 0x25, { 0x16, 0x10, 0x10, 0x0a, 0x10 } },
+ { 0x26, { 0x14, 0x14, 0x04, 0x04, 0x04 } },
+ { 0x27, { 0x58, 0x58, 0x58, 0x5c, 0x58 } },
+ { 0x28, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0x29, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0x2a, { 0x08, 0x0a, 0x08, 0x08, 0x08 } },
+ { 0x2b, { 0x08, 0x08, 0x08, 0x08, 0x08 } },
+ { 0x2c, { 0x06, 0x06, 0x06, 0x06, 0x06 } },
+ { 0x2d, { 0x05, 0x06, 0x06, 0x06, 0x06 } },
+ { 0x2e, { 0x21, 0x21, 0x21, 0x21, 0x21 } },
+ { 0x2f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x30, { 0x14, 0x14, 0x14, 0x14, 0x14 } },
+ { 0x33, { 0xb7, 0xb7, 0xb7, 0xb7, 0xb7 } },
+ { 0x34, { 0x81, 0x81, 0x81, 0x81, 0x81 } },
+ { 0x35, { 0x80, 0x80, 0x80, 0x80, 0x80 } },
+ { 0x37, { 0x70, 0x70, 0x70, 0x70, 0x70 } },
+ { 0x38, { 0x04, 0x04, 0x02, 0x02, 0x02 } },
+ { 0x39, { 0x07, 0x07, 0x05, 0x05, 0x05 } },
+ { 0x3a, { 0x06, 0x06, 0x06, 0x06, 0x06 } },
+ { 0x3b, { 0x03, 0x03, 0x03, 0x03, 0x03 } },
+ { 0x3c, { 0x07, 0x06, 0x04, 0x04, 0x04 } },
+ { 0x3d, { 0xf0, 0xf0, 0xf0, 0xf0, 0x80 } },
+ { 0x3e, { 0x60, 0x60, 0x60, 0x60, 0xff } },
+ { 0x3f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x40, { 0x5b, 0x5b, 0x5b, 0x57, 0x50 } },
+ { 0x41, { 0x30, 0x30, 0x30, 0x30, 0x18 } },
+ { 0x42, { 0x20, 0x20, 0x20, 0x00, 0x30 } },
+ { 0x43, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x44, { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f } },
+ { 0x45, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x46, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0x47, { 0x00, 0x00, 0x95, 0x00, 0x95 } },
+ { 0x48, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } },
+ { 0x49, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } },
+ { 0x4a, { 0x40, 0x40, 0x33, 0x11, 0x11 } },
+ { 0x4b, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
+ { 0x4c, { 0x40, 0x40, 0x99, 0x11, 0x11 } },
+ { 0x4d, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
+ { 0x4e, { 0x40, 0x40, 0x66, 0x77, 0x77 } },
+ { 0x4f, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
+ { 0x50, { 0x40, 0x40, 0x88, 0x33, 0x11 } },
+ { 0x51, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
+ { 0x52, { 0x40, 0x40, 0x88, 0x02, 0x02 } },
+ { 0x53, { 0x40, 0x40, 0x00, 0x02, 0x02 } },
+ { 0x54, { 0x00, 0x00, 0x88, 0x33, 0x33 } },
+ { 0x55, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
+ { 0x56, { 0x00, 0x00, 0x00, 0x0b, 0x00 } },
+ { 0x57, { 0x40, 0x40, 0x0a, 0x0b, 0x0a } },
+ { 0x58, { 0xaa, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x59, { 0x7a, 0x40, 0x02, 0x02, 0x02 } },
+ { 0x5a, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
+ { 0x5b, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
+ { 0x5c, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
+ { 0x5d, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
+ { 0x5e, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } },
+ { 0x5f, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } },
+ { 0x60, { 0x40, 0x40, 0x00, 0x30, 0x30 } },
+ { 0x61, { 0x40, 0x40, 0x10, 0x30, 0x30 } },
+ { 0x62, { 0x40, 0x40, 0x00, 0x30, 0x30 } },
+ { 0x63, { 0x40, 0x40, 0x05, 0x30, 0x30 } },
+ { 0x64, { 0x40, 0x40, 0x06, 0x00, 0x30 } },
+ { 0x65, { 0x40, 0x40, 0x06, 0x08, 0x30 } },
+ { 0x66, { 0x40, 0x40, 0x00, 0x00, 0x20 } },
+ { 0x67, { 0x40, 0x40, 0x01, 0x04, 0x20 } },
+ { 0x68, { 0x00, 0x00, 0x30, 0x00, 0x20 } },
+ { 0x69, { 0xa0, 0xa0, 0x00, 0x08, 0x20 } },
+ { 0x6a, { 0x00, 0x00, 0x30, 0x00, 0x25 } },
+ { 0x6b, { 0xa0, 0xa0, 0x00, 0x06, 0x25 } },
+ { 0x6c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x6d, { 0xa0, 0x60, 0x0c, 0x03, 0x0c } },
+ { 0x6e, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x6f, { 0xa0, 0x60, 0x04, 0x01, 0x04 } },
+ { 0x70, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } },
+ { 0x71, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } },
+ { 0x72, { 0x58, 0x58, 0xff, 0xff, 0xff } },
+ { 0x73, { 0x58, 0x58, 0xff, 0xff, 0xff } },
+ { 0x74, { 0x06, 0x06, 0x09, 0x05, 0x05 } },
+ { 0x75, { 0x06, 0x06, 0x0a, 0x10, 0x10 } },
+ { 0x76, { 0x10, 0x10, 0x06, 0x0a, 0x0a } },
+ { 0x77, { 0x12, 0x18, 0x28, 0x10, 0x28 } },
+ { 0x78, { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8 } },
+ { 0x79, { 0x15, 0x15, 0x03, 0x03, 0x03 } },
+ { 0x7a, { 0x02, 0x02, 0x01, 0x04, 0x03 } },
+ { 0x7b, { 0x01, 0x02, 0x03, 0x03, 0x03 } },
+ { 0x7c, { 0x28, 0x28, 0x28, 0x28, 0x28 } },
+ { 0x7f, { 0x25, 0x92, 0x5f, 0x17, 0x2d } },
+ { 0x80, { 0x64, 0x64, 0x64, 0x74, 0x64 } },
+ { 0x83, { 0x06, 0x03, 0x04, 0x04, 0x04 } },
+ { 0x84, { 0xff, 0xff, 0xff, 0xff, 0xff } },
+ { 0x85, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
+ { 0x86, { 0x00, 0x00, 0x11, 0x11, 0x11 } },
+ { 0x87, { 0x03, 0x03, 0x03, 0x03, 0x03 } },
+ { 0x88, { 0x09, 0x09, 0x09, 0x09, 0x09 } },
+ { 0x89, { 0x20, 0x20, 0x30, 0x20, 0x20 } },
+ { 0x8a, { 0x03, 0x03, 0x02, 0x03, 0x02 } },
+ { 0x8b, { 0x00, 0x07, 0x09, 0x00, 0x09 } },
+ { 0x8c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x8d, { 0x4f, 0x4f, 0x4f, 0x3f, 0x4f } },
+ { 0x8e, { 0xf0, 0xf0, 0x60, 0xf0, 0xa0 } },
+ { 0x8f, { 0xe8, 0xe8, 0xe8, 0xe8, 0xe8 } },
+ { 0x90, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
+ { 0x91, { 0x40, 0x40, 0x70, 0x70, 0x10 } },
+ { 0x92, { 0x00, 0x00, 0x00, 0x00, 0x04 } },
+ { 0x93, { 0x60, 0x60, 0x60, 0x60, 0x60 } },
+ { 0x94, { 0x00, 0x00, 0x00, 0x00, 0x03 } },
+ { 0x95, { 0x09, 0x09, 0x47, 0x47, 0x47 } },
+ { 0x96, { 0x80, 0xa0, 0xa0, 0x40, 0xa0 } },
+ { 0x97, { 0x60, 0x60, 0x60, 0x60, 0x60 } },
+ { 0x98, { 0x50, 0x50, 0x50, 0x30, 0x50 } },
+ { 0x99, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
+ { 0x9a, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0x9b, { 0x40, 0x40, 0x40, 0x30, 0x40 } },
+ { 0x9c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa0, { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 } },
+ { 0xa1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa2, { 0x30, 0x30, 0x00, 0x30, 0x00 } },
+ { 0xa3, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa4, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa5, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xa8, { 0x77, 0x77, 0x77, 0x77, 0x77 } },
+ { 0xa9, { 0x02, 0x02, 0x02, 0x02, 0x02 } },
+ { 0xaa, { 0x40, 0x40, 0x40, 0x40, 0x40 } },
+ { 0xac, { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f } },
+ { 0xad, { 0x14, 0x14, 0x14, 0x14, 0x14 } },
+ { 0xae, { 0x78, 0x78, 0x78, 0x78, 0x78 } },
+ { 0xaf, { 0x06, 0x06, 0x06, 0x06, 0x07 } },
+ { 0xb0, { 0x1b, 0x1b, 0x1b, 0x19, 0x1b } },
+ { 0xb1, { 0x18, 0x17, 0x17, 0x18, 0x17 } },
+ { 0xb2, { 0x35, 0x82, 0x82, 0x38, 0x82 } },
+ { 0xb3, { 0xb6, 0xce, 0xc7, 0x5c, 0xb0 } },
+ { 0xb4, { 0x3f, 0x3e, 0x3e, 0x3f, 0x3e } },
+ { 0xb5, { 0x70, 0x58, 0x50, 0x68, 0x50 } },
+ { 0xb6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xb7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xb8, { 0x03, 0x03, 0x01, 0x01, 0x01 } },
+ { 0xb9, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xba, { 0x06, 0x06, 0x0a, 0x05, 0x0a } },
+ { 0xbb, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xbc, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xbd, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xbe, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xbf, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc3, { 0x00, 0x00, 0x88, 0x66, 0x88 } },
+ { 0xc4, { 0x10, 0x10, 0x00, 0x00, 0x00 } },
+ { 0xc5, { 0x00, 0x00, 0x44, 0x60, 0x44 } },
+ { 0xc6, { 0x10, 0x0a, 0x00, 0x00, 0x00 } },
+ { 0xc7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc8, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xc9, { 0x90, 0x04, 0x00, 0x00, 0x00 } },
+ { 0xca, { 0x90, 0x08, 0x01, 0x01, 0x01 } },
+ { 0xcb, { 0xa0, 0x04, 0x00, 0x44, 0x00 } },
+ { 0xcc, { 0xa0, 0x10, 0x03, 0x00, 0x03 } },
+ { 0xcd, { 0x06, 0x06, 0x06, 0x05, 0x06 } },
+ { 0xce, { 0x05, 0x05, 0x01, 0x01, 0x01 } },
+ { 0xcf, { 0x40, 0x20, 0x18, 0x18, 0x18 } },
+ { 0xd0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xd1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xd2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xd3, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xd4, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
+ { 0xd5, { 0x05, 0x05, 0x05, 0x03, 0x05 } },
+ { 0xd6, { 0xac, 0x22, 0xca, 0x8f, 0xca } },
+ { 0xd7, { 0x20, 0x20, 0x20, 0x20, 0x20 } },
+ { 0xd8, { 0x01, 0x01, 0x01, 0x01, 0x01 } },
+ { 0xd9, { 0x00, 0x00, 0x0f, 0x00, 0x0f } },
+ { 0xda, { 0x00, 0xff, 0xff, 0x0e, 0xff } },
+ { 0xdb, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0xdc, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0xdd, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
+ { 0xde, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0xdf, { 0x42, 0x42, 0x44, 0x44, 0x04 } },
+ { 0xe0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xe1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xe2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xe3, { 0x00, 0x00, 0x26, 0x06, 0x26 } },
+ { 0xe4, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xe5, { 0x01, 0x0a, 0x01, 0x01, 0x01 } },
+ { 0xe6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xe7, { 0x08, 0x08, 0x08, 0x08, 0x08 } },
+ { 0xe8, { 0x63, 0x63, 0x63, 0x63, 0x63 } },
+ { 0xe9, { 0x59, 0x59, 0x59, 0x59, 0x59 } },
+ { 0xea, { 0x80, 0x80, 0x20, 0x80, 0x80 } },
+ { 0xeb, { 0x37, 0x37, 0x78, 0x37, 0x77 } },
+ { 0xec, { 0x1f, 0x1f, 0x25, 0x25, 0x25 } },
+ { 0xed, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
+ { 0xee, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { 0xef, { 0x70, 0x70, 0x58, 0x38, 0x58 } },
+ { 0xf0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
+};
+
+#endif /* HD29L2_PRIV */
diff --git a/drivers/media/dvb/frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c
index 33d33f4d8867..33d33f4d8867 100644
--- a/drivers/media/dvb/frontends/isl6405.c
+++ b/drivers/media/dvb-frontends/isl6405.c
diff --git a/drivers/media/dvb/frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h
index 1c793d37576b..1c793d37576b 100644
--- a/drivers/media/dvb/frontends/isl6405.h
+++ b/drivers/media/dvb-frontends/isl6405.h
diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c
index 684c8ec166cb..684c8ec166cb 100644
--- a/drivers/media/dvb/frontends/isl6421.c
+++ b/drivers/media/dvb-frontends/isl6421.c
diff --git a/drivers/media/dvb/frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h
index 47e4518a042d..47e4518a042d 100644
--- a/drivers/media/dvb/frontends/isl6421.h
+++ b/drivers/media/dvb-frontends/isl6421.h
diff --git a/drivers/media/dvb/frontends/isl6423.c b/drivers/media/dvb-frontends/isl6423.c
index dca5bebfeeb5..dca5bebfeeb5 100644
--- a/drivers/media/dvb/frontends/isl6423.c
+++ b/drivers/media/dvb-frontends/isl6423.c
diff --git a/drivers/media/dvb/frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h
index e1a37fba01ca..e1a37fba01ca 100644
--- a/drivers/media/dvb/frontends/isl6423.h
+++ b/drivers/media/dvb-frontends/isl6423.h
diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb-frontends/it913x-fe-priv.h
index eb6fd8aebdb3..eb6fd8aebdb3 100644
--- a/drivers/media/dvb/frontends/it913x-fe-priv.h
+++ b/drivers/media/dvb-frontends/it913x-fe-priv.h
diff --git a/drivers/media/dvb-frontends/it913x-fe.c b/drivers/media/dvb-frontends/it913x-fe.c
new file mode 100644
index 000000000000..6e1c6eb340b7
--- /dev/null
+++ b/drivers/media/dvb-frontends/it913x-fe.c
@@ -0,0 +1,1045 @@
+/*
+ * Driver for it913x-fe Frontend
+ *
+ * with support for on chip it9137 integral tuner
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9137 Copyright (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "dvb_frontend.h"
+#include "it913x-fe.h"
+#include "it913x-fe-priv.h"
+
+static int it913x_debug;
+
+module_param_named(debug, it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & it913x_debug) \
+ printk(KERN_DEBUG "it913x-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define debug_data_snipet(level, name, p) \
+ dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+#define info(format, arg...) \
+ printk(KERN_INFO "it913x-fe: " format "\n" , ## arg)
+
+struct it913x_fe_state {
+ struct dvb_frontend frontend;
+ struct i2c_adapter *i2c_adap;
+ struct ite_config *config;
+ u8 i2c_addr;
+ u32 frequency;
+ fe_modulation_t constellation;
+ fe_transmit_mode_t transmission_mode;
+ u8 priority;
+ u32 crystalFrequency;
+ u32 adcFrequency;
+ u8 tuner_type;
+ struct adctable *table;
+ fe_status_t it913x_status;
+ u16 tun_xtal;
+ u8 tun_fdiv;
+ u8 tun_clk_mode;
+ u32 tun_fn_min;
+ u32 ucblocks;
+};
+
+static int it913x_read_reg(struct it913x_fe_state *state,
+ u32 reg, u8 *data, u8 count)
+{
+ int ret;
+ u8 pro = PRO_DMOD; /* All reads from demodulator */
+ u8 b[4];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = sizeof(b) },
+ { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD,
+ .buf = data, .len = count }
+ };
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+
+ ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+ return ret;
+}
+
+static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg)
+{
+ int ret;
+ u8 b[1];
+ ret = it913x_read_reg(state, reg, &b[0], sizeof(b));
+ return (ret < 0) ? -ENODEV : b[0];
+}
+
+static int it913x_write(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u8 buf[], u8 count)
+{
+ u8 b[256];
+ struct i2c_msg msg[1] = {
+ { .addr = state->i2c_addr + (pro << 1), .flags = 0,
+ .buf = b, .len = count + 4 }
+ };
+ int ret;
+
+ b[0] = (u8) reg >> 24;
+ b[1] = (u8)(reg >> 16) & 0xff;
+ b[2] = (u8)(reg >> 8) & 0xff;
+ b[3] = (u8) reg & 0xff;
+ memcpy(&b[4], buf, count);
+
+ ret = i2c_transfer(state->i2c_adap, msg, 1);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int it913x_write_reg(struct it913x_fe_state *state,
+ u8 pro, u32 reg, u32 data)
+{
+ int ret;
+ u8 b[4];
+ u8 s;
+
+ b[0] = data >> 24;
+ b[1] = (data >> 16) & 0xff;
+ b[2] = (data >> 8) & 0xff;
+ b[3] = data & 0xff;
+ /* expand write as needed */
+ if (data < 0x100)
+ s = 3;
+ else if (data < 0x1000)
+ s = 2;
+ else if (data < 0x100000)
+ s = 1;
+ else
+ s = 0;
+
+ ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s);
+
+ return ret;
+}
+
+static int it913x_fe_script_loader(struct it913x_fe_state *state,
+ struct it913xset *loadscript)
+{
+ int ret, i;
+ if (loadscript == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 1000; ++i) {
+ if (loadscript[i].pro == 0xff)
+ break;
+ ret = it913x_write(state, loadscript[i].pro,
+ loadscript[i].address,
+ loadscript[i].reg, loadscript[i].count);
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int it913x_init_tuner(struct it913x_fe_state *state)
+{
+ int ret, i, reg;
+ u8 val, nv_val;
+ u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
+ u8 b[2];
+
+ reg = it913x_read_reg_u8(state, 0xec86);
+ switch (reg) {
+ case 0:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 2000;
+ state->tun_fdiv = 3;
+ val = 16;
+ break;
+ case -ENODEV:
+ return -ENODEV;
+ case 1:
+ default:
+ state->tun_clk_mode = reg;
+ state->tun_xtal = 640;
+ state->tun_fdiv = 1;
+ val = 6;
+ break;
+ }
+
+ reg = it913x_read_reg_u8(state, 0xed03);
+
+ if (reg < 0)
+ return -ENODEV;
+ else if (reg < ARRAY_SIZE(nv))
+ nv_val = nv[reg];
+ else
+ nv_val = 2;
+
+ for (i = 0; i < 50; i++) {
+ ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b));
+ reg = (b[1] << 8) + b[0];
+ if (reg > 0)
+ break;
+ if (ret < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+ state->tun_fn_min = state->tun_xtal * reg;
+ state->tun_fn_min /= (state->tun_fdiv * nv_val);
+ deb_info("Tuner fn_min %d", state->tun_fn_min);
+
+ if (state->config->chip_ver > 1)
+ msleep(50);
+ else {
+ for (i = 0; i < 50; i++) {
+ reg = it913x_read_reg_u8(state, 0xec82);
+ if (reg > 0)
+ break;
+ if (reg < 0)
+ return -ENODEV;
+ udelay(2000);
+ }
+ }
+
+ return it913x_write_reg(state, PRO_DMOD, 0xed81, val);
+}
+
+static int it9137_set_tuner(struct it913x_fe_state *state,
+ u32 bandwidth, u32 frequency_m)
+{
+ struct it913xset *set_tuner = set_it9137_template;
+ int ret, reg;
+ u32 frequency = frequency_m / 1000;
+ u32 freq, temp_f, tmp;
+ u16 iqik_m_cal;
+ u16 n_div;
+ u8 n;
+ u8 l_band;
+ u8 lna_band;
+ u8 bw;
+
+ if (state->config->firmware_ver == 1)
+ set_tuner = set_it9135_template;
+ else
+ set_tuner = set_it9137_template;
+
+ deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth);
+
+ if (frequency >= 51000 && frequency <= 440000) {
+ l_band = 0;
+ lna_band = 0;
+ } else if (frequency > 440000 && frequency <= 484000) {
+ l_band = 1;
+ lna_band = 1;
+ } else if (frequency > 484000 && frequency <= 533000) {
+ l_band = 1;
+ lna_band = 2;
+ } else if (frequency > 533000 && frequency <= 587000) {
+ l_band = 1;
+ lna_band = 3;
+ } else if (frequency > 587000 && frequency <= 645000) {
+ l_band = 1;
+ lna_band = 4;
+ } else if (frequency > 645000 && frequency <= 710000) {
+ l_band = 1;
+ lna_band = 5;
+ } else if (frequency > 710000 && frequency <= 782000) {
+ l_band = 1;
+ lna_band = 6;
+ } else if (frequency > 782000 && frequency <= 860000) {
+ l_band = 1;
+ lna_band = 7;
+ } else if (frequency > 1450000 && frequency <= 1492000) {
+ l_band = 1;
+ lna_band = 0;
+ } else if (frequency > 1660000 && frequency <= 1685000) {
+ l_band = 1;
+ lna_band = 1;
+ } else
+ return -EINVAL;
+ set_tuner[0].reg[0] = lna_band;
+
+ switch (bandwidth) {
+ case 5000000:
+ bw = 0;
+ break;
+ case 6000000:
+ bw = 2;
+ break;
+ case 7000000:
+ bw = 4;
+ break;
+ default:
+ case 8000000:
+ bw = 6;
+ break;
+ }
+
+ set_tuner[1].reg[0] = bw;
+ set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
+
+ if (frequency > 53000 && frequency <= 74000) {
+ n_div = 48;
+ n = 0;
+ } else if (frequency > 74000 && frequency <= 111000) {
+ n_div = 32;
+ n = 1;
+ } else if (frequency > 111000 && frequency <= 148000) {
+ n_div = 24;
+ n = 2;
+ } else if (frequency > 148000 && frequency <= 222000) {
+ n_div = 16;
+ n = 3;
+ } else if (frequency > 222000 && frequency <= 296000) {
+ n_div = 12;
+ n = 4;
+ } else if (frequency > 296000 && frequency <= 445000) {
+ n_div = 8;
+ n = 5;
+ } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
+ n_div = 6;
+ n = 6;
+ } else if (frequency > state->tun_fn_min && frequency <= 950000) {
+ n_div = 4;
+ n = 7;
+ } else if (frequency > 1450000 && frequency <= 1680000) {
+ n_div = 2;
+ n = 0;
+ } else
+ return -EINVAL;
+
+ reg = it913x_read_reg_u8(state, 0xed81);
+ iqik_m_cal = (u16)reg * n_div;
+
+ if (reg < 0x20) {
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = (iqik_m_cal * 9) >> 5;
+ else
+ iqik_m_cal >>= 1;
+ } else {
+ iqik_m_cal = 0x40 - iqik_m_cal;
+ if (state->tun_clk_mode == 0)
+ iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
+ else
+ iqik_m_cal = ~(iqik_m_cal >> 1);
+ }
+
+ temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
+ freq = temp_f / state->tun_xtal;
+ tmp = freq * state->tun_xtal;
+
+ if ((temp_f - tmp) >= (state->tun_xtal >> 1))
+ freq++;
+
+ freq += (u32) n << 13;
+ /* Frequency OMEGA_IQIK_M_CAL_MID*/
+ temp_f = freq + (u32)iqik_m_cal;
+
+ set_tuner[3].reg[0] = temp_f & 0xff;
+ set_tuner[4].reg[0] = (temp_f >> 8) & 0xff;
+
+ deb_info("High Frequency = %04x", temp_f);
+
+ /* Lower frequency */
+ set_tuner[5].reg[0] = freq & 0xff;
+ set_tuner[6].reg[0] = (freq >> 8) & 0xff;
+
+ deb_info("low Frequency = %04x", freq);
+
+ ret = it913x_fe_script_loader(state, set_tuner);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_select_bw(struct it913x_fe_state *state,
+ u32 bandwidth, u32 adcFrequency)
+{
+ int ret, i;
+ u8 buffer[256];
+ u32 coeff[8];
+ u16 bfsfcw_fftinx_ratio;
+ u16 fftinx_bfsfcw_ratio;
+ u8 count;
+ u8 bw;
+ u8 adcmultiplier;
+
+ deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency);
+
+ switch (bandwidth) {
+ case 5000000:
+ bw = 3;
+ break;
+ case 6000000:
+ bw = 0;
+ break;
+ case 7000000:
+ bw = 1;
+ break;
+ default:
+ case 8000000:
+ bw = 2;
+ break;
+ }
+ ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw);
+
+ if (state->table == NULL)
+ return -EINVAL;
+
+ /* In write order */
+ coeff[0] = state->table[bw].coeff_1_2048;
+ coeff[1] = state->table[bw].coeff_2_2k;
+ coeff[2] = state->table[bw].coeff_1_8191;
+ coeff[3] = state->table[bw].coeff_1_8192;
+ coeff[4] = state->table[bw].coeff_1_8193;
+ coeff[5] = state->table[bw].coeff_2_8k;
+ coeff[6] = state->table[bw].coeff_1_4096;
+ coeff[7] = state->table[bw].coeff_2_4k;
+ bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio;
+ fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio;
+
+ /* ADC multiplier */
+ ret = it913x_read_reg_u8(state, ADC_X_2);
+ if (ret < 0)
+ return -EINVAL;
+
+ adcmultiplier = ret;
+
+ count = 0;
+
+ /* Build Buffer for COEFF Registers */
+ for (i = 0; i < 8; i++) {
+ if (adcmultiplier == 1)
+ coeff[i] /= 2;
+ buffer[count++] = (coeff[i] >> 24) & 0x3;
+ buffer[count++] = (coeff[i] >> 16) & 0xff;
+ buffer[count++] = (coeff[i] >> 8) & 0xff;
+ buffer[count++] = coeff[i] & 0xff;
+ }
+
+ /* bfsfcw_fftinx_ratio register 0x21-0x22 */
+ buffer[count++] = bfsfcw_fftinx_ratio & 0xff;
+ buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff;
+ /* fftinx_bfsfcw_ratio register 0x23-0x24 */
+ buffer[count++] = fftinx_bfsfcw_ratio & 0xff;
+ buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff;
+ /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/
+ ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count);
+
+ for (i = 0; i < 42; i += 8)
+ debug_data_snipet(0x1, "Buffer", &buffer[i]);
+
+ return ret;
+}
+
+
+
+static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret, i;
+ fe_status_t old_status = state->it913x_status;
+ *status = 0;
+
+ if (state->it913x_status == 0) {
+ ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (ret == 0x1) {
+ *status |= FE_HAS_SIGNAL;
+ for (i = 0; i < 40; i++) {
+ ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK);
+ if (ret == 0x1)
+ break;
+ msleep(25);
+ }
+ if (ret == 0x1)
+ *status |= FE_HAS_CARRIER
+ | FE_HAS_VITERBI
+ | FE_HAS_SYNC;
+ state->it913x_status = *status;
+ }
+ }
+
+ if (state->it913x_status & FE_HAS_SYNC) {
+ ret = it913x_read_reg_u8(state, TPSD_LOCK);
+ if (ret == 0x1)
+ *status |= FE_HAS_LOCK
+ | state->it913x_status;
+ else
+ state->it913x_status = 0;
+ if (old_status != state->it913x_status)
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret);
+ }
+
+ return 0;
+}
+
+/* FEC values based on fe_code_rate_t non supported values 0*/
+int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
+int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
+int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
+
+static int it913x_get_signal_strength(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ u8 code_rate;
+ int ret, temp;
+ u8 lna_gain_os;
+
+ ret = it913x_read_reg_u8(state, VAR_P_INBAND);
+ if (ret < 0)
+ return ret;
+
+ /* VHF/UHF gain offset */
+ if (state->frequency < 300000000)
+ lna_gain_os = 7;
+ else
+ lna_gain_os = 14;
+
+ temp = (ret - 100) - lna_gain_os;
+
+ if (state->priority == PRIORITY_HIGH)
+ code_rate = p->code_rate_HP;
+ else
+ code_rate = p->code_rate_LP;
+
+ if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
+ return -EINVAL;
+
+ deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
+
+ /* Apply FEC offset values*/
+ switch (p->modulation) {
+ case QPSK:
+ temp -= it913x_qpsk_pval[code_rate];
+ break;
+ case QAM_16:
+ temp -= it913x_16qam_pval[code_rate];
+ break;
+ case QAM_64:
+ temp -= it913x_64qam_pval[code_rate];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (temp < -15)
+ ret = 0;
+ else if ((-15 <= temp) && (temp < 0))
+ ret = (2 * (temp + 15)) / 3;
+ else if ((0 <= temp) && (temp < 20))
+ ret = 4 * temp + 10;
+ else if ((20 <= temp) && (temp < 35))
+ ret = (2 * (temp - 20)) / 3 + 90;
+ else if (temp >= 35)
+ ret = 100;
+
+ deb_info("Signal Strength :%d", ret);
+
+ return ret;
+}
+
+static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = 0;
+ if (state->config->read_slevel) {
+ if (state->it913x_status & FE_HAS_SIGNAL)
+ ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
+ } else
+ ret = it913x_get_signal_strength(fe);
+
+ if (ret >= 0)
+ *strength = (u16)((u32)ret * 0xffff / 0x64);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret;
+ u8 reg[3];
+ u32 snr_val, snr_min, snr_max;
+ u32 temp;
+
+ ret = it913x_read_reg(state, 0x2c, reg, sizeof(reg));
+
+ snr_val = (u32)(reg[2] << 16) | (reg[1] << 8) | reg[0];
+
+ ret |= it913x_read_reg(state, 0xf78b, reg, 1);
+ if (reg[0])
+ snr_val /= reg[0];
+
+ if (state->transmission_mode == TRANSMISSION_MODE_2K)
+ snr_val *= 4;
+ else if (state->transmission_mode == TRANSMISSION_MODE_4K)
+ snr_val *= 2;
+
+ if (state->constellation == QPSK) {
+ snr_min = 0xb4711;
+ snr_max = 0x191451;
+ } else if (state->constellation == QAM_16) {
+ snr_min = 0x4f0d5;
+ snr_max = 0xc7925;
+ } else if (state->constellation == QAM_64) {
+ snr_min = 0x256d0;
+ snr_max = 0x626be;
+ } else
+ return -EINVAL;
+
+ if (snr_val < snr_min)
+ *snr = 0;
+ else if (snr_val < snr_max) {
+ temp = (snr_val - snr_min) >> 5;
+ temp *= 0xffff;
+ temp /= (snr_max - snr_min) >> 5;
+ *snr = (u16)temp;
+ } else
+ *snr = 0xffff;
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ u8 reg[5];
+ /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */
+ it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
+ state->ucblocks += (u32)(reg[1] << 8) | reg[0];
+ *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2];
+ return 0;
+}
+
+static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret;
+ u8 reg[2];
+ /* Aborted Packets */
+ ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
+ state->ucblocks += (u32)(reg[1] << 8) | reg[0];
+ *ucblocks = state->ucblocks;
+ return ret;
+}
+
+static int it913x_fe_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ u8 reg[8];
+
+ it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
+
+ if (reg[3] < 3)
+ p->modulation = fe_con[reg[3]];
+
+ if (reg[0] < 3)
+ p->transmission_mode = fe_mode[reg[0]];
+
+ if (reg[1] < 4)
+ p->guard_interval = fe_gi[reg[1]];
+
+ if (reg[2] < 4)
+ p->hierarchy = fe_hi[reg[2]];
+
+ state->priority = reg[5];
+
+ p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
+ p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
+
+ /* Update internal state to reflect the autodetected props */
+ state->constellation = p->modulation;
+ state->transmission_mode = p->transmission_mode;
+
+ return 0;
+}
+
+static int it913x_fe_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int i;
+ u8 empty_ch, last_ch;
+
+ state->it913x_status = 0;
+
+ /* Set bw*/
+ it913x_fe_select_bw(state, p->bandwidth_hz,
+ state->adcFrequency);
+
+ /* Training Mode Off */
+ it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
+
+ /* Clear Empty Channel */
+ it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
+
+ /* Clear bits */
+ it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
+ /* LED on */
+ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+ /* Select Band*/
+ if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
+ i = 0;
+ else if ((p->frequency >= 350000000) && (p->frequency <= 900000000))
+ i = 1;
+ else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000))
+ i = 2;
+ else
+ return -EOPNOTSUPP;
+
+ it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
+
+ deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
+ switch (state->tuner_type) {
+ case IT9135_38:
+ case IT9135_51:
+ case IT9135_52:
+ case IT9135_60:
+ case IT9135_61:
+ case IT9135_62:
+ it9137_set_tuner(state,
+ p->bandwidth_hz, p->frequency);
+ break;
+ default:
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ break;
+ }
+ /* LED off */
+ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+ /* Trigger ofsm */
+ it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+ last_ch = 2;
+ for (i = 0; i < 40; ++i) {
+ empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
+ if (last_ch == 1 && empty_ch == 1)
+ break;
+ if (last_ch == 2 && empty_ch == 2)
+ return 0;
+ last_ch = empty_ch;
+ msleep(25);
+ }
+ for (i = 0; i < 40; ++i) {
+ if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1)
+ break;
+ msleep(25);
+ }
+
+ state->frequency = p->frequency;
+ return 0;
+}
+
+static int it913x_fe_suspend(struct it913x_fe_state *state)
+{
+ int ret, i;
+ u8 b;
+
+ ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+
+ for (i = 0; i < 128; i++) {
+ ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1);
+ if (ret < 0)
+ return -ENODEV;
+ if (b == 0)
+ break;
+
+ }
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8);
+ /* Turn LED off */
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+
+ ret |= it913x_fe_script_loader(state, it9137_tuner_off);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+/* Power sequence */
+/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */
+/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */
+
+static int it913x_fe_sleep(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ return it913x_fe_suspend(state);
+}
+
+static u32 compute_div(u32 a, u32 b, u32 x)
+{
+ u32 res = 0;
+ u32 c = 0;
+ u32 i = 0;
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ res += 1;
+ a -= b;
+ }
+ a <<= 1;
+ res <<= 1;
+ }
+
+ res = (c << x) + res;
+
+ return res;
+}
+
+static int it913x_fe_start(struct it913x_fe_state *state)
+{
+ struct it913xset *set_lna;
+ struct it913xset *set_mode;
+ int ret;
+ u8 adf = (state->config->adf & 0xf);
+ u32 adc, xtal;
+ u8 b[4];
+
+ if (state->config->chip_ver == 1)
+ ret = it913x_init_tuner(state);
+
+ info("ADF table value :%02x", adf);
+
+ if (adf < 10) {
+ state->crystalFrequency = fe_clockTable[adf].xtal ;
+ state->table = fe_clockTable[adf].table;
+ state->adcFrequency = state->table->adcFrequency;
+
+ adc = compute_div(state->adcFrequency, 1000000ul, 19ul);
+ xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul);
+
+ } else
+ return -EINVAL;
+
+ /* Set LED indicator on GPIOH3 */
+ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+
+ ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01);
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01);
+
+ b[0] = xtal & 0xff;
+ b[1] = (xtal >> 8) & 0xff;
+ b[2] = (xtal >> 16) & 0xff;
+ b[3] = (xtal >> 24);
+ ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4);
+
+ b[0] = adc & 0xff;
+ b[1] = (adc >> 8) & 0xff;
+ b[2] = (adc >> 16) & 0xff;
+ ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3);
+
+ if (state->config->adc_x2)
+ ret |= it913x_write_reg(state, PRO_DMOD, ADC_X_2, 0x01);
+ b[0] = 0;
+ b[1] = 0;
+ b[2] = 0;
+ ret |= it913x_write(state, PRO_DMOD, 0x0029, b, 3);
+
+ info("Crystal Frequency :%d Adc Frequency :%d ADC X2: %02x",
+ state->crystalFrequency, state->adcFrequency,
+ state->config->adc_x2);
+ deb_info("Xtal value :%04x Adc value :%04x", xtal, adc);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ /* v1 or v2 tuner script */
+ if (state->config->chip_ver > 1)
+ ret = it913x_fe_script_loader(state, it9135_v2);
+ else
+ ret = it913x_fe_script_loader(state, it9135_v1);
+ if (ret < 0)
+ return ret;
+
+ /* LNA Scripts */
+ switch (state->tuner_type) {
+ case IT9135_51:
+ set_lna = it9135_51;
+ break;
+ case IT9135_52:
+ set_lna = it9135_52;
+ break;
+ case IT9135_60:
+ set_lna = it9135_60;
+ break;
+ case IT9135_61:
+ set_lna = it9135_61;
+ break;
+ case IT9135_62:
+ set_lna = it9135_62;
+ break;
+ case IT9135_38:
+ default:
+ set_lna = it9135_38;
+ }
+ info("Tuner LNA type :%02x", state->tuner_type);
+
+ ret = it913x_fe_script_loader(state, set_lna);
+ if (ret < 0)
+ return ret;
+
+ if (state->config->chip_ver == 2) {
+ ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x1);
+ ret |= it913x_write_reg(state, PRO_LINK, PADODPU, 0x0);
+ ret |= it913x_write_reg(state, PRO_LINK, AGC_O_D, 0x0);
+ ret |= it913x_init_tuner(state);
+ }
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Always solo frontend */
+ set_mode = set_solo_fe;
+ ret |= it913x_fe_script_loader(state, set_mode);
+
+ ret |= it913x_fe_suspend(state);
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_fe_init(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ int ret = 0;
+ /* Power Up Tuner - common all versions */
+ ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1);
+
+ ret |= it913x_fe_script_loader(state, init_1);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0);
+
+ ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static void it913x_fe_release(struct dvb_frontend *fe)
+{
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops;
+
+struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr, struct ite_config *config)
+{
+ struct it913x_fe_state *state = NULL;
+ int ret;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+ if (config == NULL)
+ goto error;
+
+ state->i2c_adap = i2c_adap;
+ state->i2c_addr = i2c_addr;
+ state->config = config;
+
+ switch (state->config->tuner_id_0) {
+ case IT9135_51:
+ case IT9135_52:
+ case IT9135_60:
+ case IT9135_61:
+ case IT9135_62:
+ state->tuner_type = state->config->tuner_id_0;
+ break;
+ default:
+ case IT9135_38:
+ state->tuner_type = IT9135_38;
+ }
+
+ ret = it913x_fe_start(state);
+ if (ret < 0)
+ goto error;
+
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+
+ return &state->frontend;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(it913x_fe_attach);
+
+static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "it913x-fe DVB-T",
+ .frequency_min = 51000000,
+ .frequency_max = 1680000000,
+ .frequency_stepsize = 62500,
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+ },
+
+ .release = it913x_fe_release,
+
+ .init = it913x_fe_init,
+ .sleep = it913x_fe_sleep,
+
+ .set_frontend = it913x_fe_set_frontend,
+ .get_frontend = it913x_fe_get_frontend,
+
+ .read_status = it913x_fe_read_status,
+ .read_signal_strength = it913x_fe_read_signal_strength,
+ .read_snr = it913x_fe_read_snr,
+ .read_ber = it913x_fe_read_ber,
+ .read_ucblocks = it913x_fe_read_ucblocks,
+};
+
+MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_VERSION("1.15");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb-frontends/it913x-fe.h
index 07fa4594c12b..07fa4594c12b 100644
--- a/drivers/media/dvb/frontends/it913x-fe.h
+++ b/drivers/media/dvb-frontends/it913x-fe.h
diff --git a/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c
index 316457584fe7..316457584fe7 100644
--- a/drivers/media/dvb/frontends/itd1000.c
+++ b/drivers/media/dvb-frontends/itd1000.c
diff --git a/drivers/media/dvb/frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h
index 5e18df071b88..5e18df071b88 100644
--- a/drivers/media/dvb/frontends/itd1000.h
+++ b/drivers/media/dvb-frontends/itd1000.h
diff --git a/drivers/media/dvb/frontends/itd1000_priv.h b/drivers/media/dvb-frontends/itd1000_priv.h
index 08ca851223c9..08ca851223c9 100644
--- a/drivers/media/dvb/frontends/itd1000_priv.h
+++ b/drivers/media/dvb-frontends/itd1000_priv.h
diff --git a/drivers/media/dvb/frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c
index bc5a82082aaa..bc5a82082aaa 100644
--- a/drivers/media/dvb/frontends/ix2505v.c
+++ b/drivers/media/dvb-frontends/ix2505v.c
diff --git a/drivers/media/dvb/frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h
index 67e89d616d50..67e89d616d50 100644
--- a/drivers/media/dvb/frontends/ix2505v.h
+++ b/drivers/media/dvb-frontends/ix2505v.h
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c
index 36fcf559e361..36fcf559e361 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb-frontends/l64781.c
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h
index 1305a9e7fb0b..1305a9e7fb0b 100644
--- a/drivers/media/dvb/frontends/l64781.h
+++ b/drivers/media/dvb-frontends/l64781.h
diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c
index cc11260e99df..cc11260e99df 100644
--- a/drivers/media/dvb/frontends/lg2160.c
+++ b/drivers/media/dvb-frontends/lg2160.c
diff --git a/drivers/media/dvb/frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h
index 9e2c0f41199a..9e2c0f41199a 100644
--- a/drivers/media/dvb/frontends/lg2160.h
+++ b/drivers/media/dvb-frontends/lg2160.h
diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c
index 1d2c47378cf8..1d2c47378cf8 100644
--- a/drivers/media/dvb/frontends/lgdt3305.c
+++ b/drivers/media/dvb-frontends/lgdt3305.c
diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h
index 02172eca4d47..02172eca4d47 100644
--- a/drivers/media/dvb/frontends/lgdt3305.h
+++ b/drivers/media/dvb-frontends/lgdt3305.h
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index e046622df0e4..e046622df0e4 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
diff --git a/drivers/media/dvb/frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h
index 9012504f0f2d..9012504f0f2d 100644
--- a/drivers/media/dvb/frontends/lgdt330x.h
+++ b/drivers/media/dvb-frontends/lgdt330x.h
diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h
index 38c76695abfe..38c76695abfe 100644
--- a/drivers/media/dvb/frontends/lgdt330x_priv.h
+++ b/drivers/media/dvb-frontends/lgdt330x_priv.h
diff --git a/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c
new file mode 100644
index 000000000000..416cce3fefc7
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgs8gl5.c
@@ -0,0 +1,453 @@
+/*
+ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
+
+ Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ Timothy Lee <timothy.lee@siriushk.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "dvb_frontend.h"
+#include "lgs8gl5.h"
+
+
+#define REG_RESET 0x02
+#define REG_RESET_OFF 0x01
+#define REG_03 0x03
+#define REG_04 0x04
+#define REG_07 0x07
+#define REG_09 0x09
+#define REG_0A 0x0a
+#define REG_0B 0x0b
+#define REG_0C 0x0c
+#define REG_37 0x37
+#define REG_STRENGTH 0x4b
+#define REG_STRENGTH_MASK 0x7f
+#define REG_STRENGTH_CARRIER 0x80
+#define REG_INVERSION 0x7c
+#define REG_INVERSION_ON 0x80
+#define REG_7D 0x7d
+#define REG_7E 0x7e
+#define REG_A2 0xa2
+#define REG_STATUS 0xa4
+#define REG_STATUS_SYNC 0x04
+#define REG_STATUS_LOCK 0x01
+
+
+struct lgs8gl5_state {
+ struct i2c_adapter *i2c;
+ const struct lgs8gl5_config *config;
+ struct dvb_frontend frontend;
+};
+
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "lgs8gl5: " args); \
+ } while (0)
+
+
+/* Writes into demod's register */
+static int
+lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = {reg, data};
+ struct i2c_msg msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+ if (ret != 1)
+ dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
+ __func__, reg, data, ret);
+ return (ret != 1) ? -1 : 0;
+}
+
+
+/* Reads from demod's register */
+static int
+lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
+{
+ int ret;
+ u8 b0[] = {reg};
+ u8 b1[] = {0};
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2)
+ return -EIO;
+
+ return b1[0];
+}
+
+
+static int
+lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ lgs8gl5_read_reg(state, reg);
+ lgs8gl5_write_reg(state, reg, data);
+ return 0;
+}
+
+
+/* Writes into alternate device's register */
+/* TODO: Find out what that device is for! */
+static int
+lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
+{
+ int ret;
+ u8 b0[] = {reg};
+ u8 b1[] = {0};
+ u8 b2[] = {reg, data};
+ struct i2c_msg msg[3] = {
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ },
+ {
+ .addr = state->config->demod_address + 2,
+ .flags = 0,
+ .buf = b2,
+ .len = 2
+ },
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 3);
+ return (ret != 3) ? -1 : 0;
+}
+
+
+static void
+lgs8gl5_soft_reset(struct lgs8gl5_state *state)
+{
+ u8 val;
+
+ dprintk("%s\n", __func__);
+
+ val = lgs8gl5_read_reg(state, REG_RESET);
+ lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
+ lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
+ msleep(5);
+}
+
+
+/* Starts demodulation */
+static void
+lgs8gl5_start_demod(struct lgs8gl5_state *state)
+{
+ u8 val;
+ int n;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+ lgs8gl5_soft_reset(state);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_write_reg(state, REG_09, 0x0e);
+ lgs8gl5_write_reg(state, REG_0A, 0xe5);
+ lgs8gl5_write_reg(state, REG_0B, 0x35);
+ lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+ lgs8gl5_update_reg(state, REG_03, 0x00);
+ lgs8gl5_update_reg(state, REG_7E, 0x01);
+ lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
+ lgs8gl5_update_reg(state, REG_04, 0x02);
+ lgs8gl5_update_reg(state, REG_37, 0x01);
+ lgs8gl5_soft_reset(state);
+
+ /* Wait for carrier */
+ for (n = 0; n < 10; n++) {
+ val = lgs8gl5_read_reg(state, REG_STRENGTH);
+ dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
+ if (val & REG_STRENGTH_CARRIER)
+ break;
+ msleep(4);
+ }
+ if (!(val & REG_STRENGTH_CARRIER))
+ return;
+
+ /* Wait for lock */
+ for (n = 0; n < 20; n++) {
+ val = lgs8gl5_read_reg(state, REG_STATUS);
+ dprintk("Wait for lock[%d] 0x%02X\n", n, val);
+ if (val & REG_STATUS_LOCK)
+ break;
+ msleep(12);
+ }
+ if (!(val & REG_STATUS_LOCK))
+ return;
+
+ lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
+ lgs8gl5_soft_reset(state);
+}
+
+
+static int
+lgs8gl5_init(struct dvb_frontend *fe)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
+ lgs8gl5_soft_reset(state);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_update_reg(state, REG_07, 0x10);
+ lgs8gl5_write_reg(state, REG_09, 0x0e);
+ lgs8gl5_write_reg(state, REG_0A, 0xe5);
+ lgs8gl5_write_reg(state, REG_0B, 0x35);
+ lgs8gl5_write_reg(state, REG_0C, 0x30);
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
+
+ *status = 0;
+
+ if ((level & REG_STRENGTH_MASK) > 0)
+ *status |= FE_HAS_SIGNAL;
+ if (level & REG_STRENGTH_CARRIER)
+ *status |= FE_HAS_CARRIER;
+ if (flags & REG_STATUS_SYNC)
+ *status |= FE_HAS_SYNC;
+ if (flags & REG_STATUS_LOCK)
+ *status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ *signal_strength = (level & REG_STRENGTH_MASK) << 8;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
+ *snr = (level & REG_STRENGTH_MASK) << 8;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (p->bandwidth_hz != 8000000)
+ return -EINVAL;
+
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* lgs8gl5_set_inversion(state, p->inversion); */
+
+ lgs8gl5_start_demod(state);
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
+
+ p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
+
+ p->code_rate_HP = FEC_1_2;
+ p->code_rate_LP = FEC_7_8;
+ p->guard_interval = GUARD_INTERVAL_1_32;
+ p->transmission_mode = TRANSMISSION_MODE_2K;
+ p->modulation = QAM_64;
+ p->hierarchy = HIERARCHY_NONE;
+ p->bandwidth_hz = 8000000;
+
+ return 0;
+}
+
+
+static int
+lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 240;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+
+static void
+lgs8gl5_release(struct dvb_frontend *fe)
+{
+ struct lgs8gl5_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+
+static struct dvb_frontend_ops lgs8gl5_ops;
+
+
+struct dvb_frontend*
+lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
+{
+ struct lgs8gl5_state *state = NULL;
+
+ dprintk("%s\n", __func__);
+
+ /* Allocate memory for the internal state */
+ state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* Setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* Check if the demod is there */
+ if (lgs8gl5_read_reg(state, REG_RESET) < 0)
+ goto error;
+
+ /* Create dvb_frontend */
+ memcpy(&state->frontend.ops, &lgs8gl5_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(lgs8gl5_attach);
+
+
+static struct dvb_frontend_ops lgs8gl5_ops = {
+ .delsys = { SYS_DTMB },
+ .info = {
+ .name = "Legend Silicon LGS-8GL5 DMB-TH",
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .frequency_tolerance = 0,
+ .caps = FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
+ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_BANDWIDTH_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER
+ },
+
+ .release = lgs8gl5_release,
+
+ .init = lgs8gl5_init,
+
+ .set_frontend = lgs8gl5_set_frontend,
+ .get_frontend = lgs8gl5_get_frontend,
+ .get_tune_settings = lgs8gl5_get_tune_settings,
+
+ .read_status = lgs8gl5_read_status,
+ .read_ber = lgs8gl5_read_ber,
+ .read_signal_strength = lgs8gl5_read_signal_strength,
+ .read_snr = lgs8gl5_read_snr,
+ .read_ucblocks = lgs8gl5_read_ucblocks,
+};
+
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
+MODULE_AUTHOR("Timothy Lee");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h
index d14176787a7d..d14176787a7d 100644
--- a/drivers/media/dvb/frontends/lgs8gl5.h
+++ b/drivers/media/dvb-frontends/lgs8gl5.h
diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c
new file mode 100644
index 000000000000..3c92f36ea5c7
--- /dev/null
+++ b/drivers/media/dvb-frontends/lgs8gxx.c
@@ -0,0 +1,1075 @@
+/*
+ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
+ * LGS8913, LGS8GL5, LGS8G75
+ * experimental support LGS8G42, LGS8G52
+ *
+ * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
+ * Copyright (C) 2008 Sirius International (Hong Kong) Limited
+ * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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/div64.h>
+#include <linux/firmware.h>
+
+#include "dvb_frontend.h"
+
+#include "lgs8gxx.h"
+#include "lgs8gxx_priv.h"
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "lgs8gxx: " args); \
+ } while (0)
+
+static int debug;
+static int fake_signal_str = 1;
+
+#define LGS8GXX_FIRMWARE "lgs8g75.fw"
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+module_param(fake_signal_str, int, 0644);
+MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
+"Signal strength calculation is slow.(default:on).");
+
+/* LGS8GXX internal helper functions */
+
+static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
+
+ msg.addr = priv->config->demod_address;
+ if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
+ msg.addr += 0x02;
+
+ if (debug >= 2)
+ dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data);
+
+ ret = i2c_transfer(priv->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+ __func__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+
+static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
+{
+ int ret;
+ u8 dev_addr;
+
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {
+ { .flags = 0, .buf = b0, .len = 1 },
+ { .flags = I2C_M_RD, .buf = b1, .len = 1 },
+ };
+
+ dev_addr = priv->config->demod_address;
+ if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
+ dev_addr += 0x02;
+ msg[1].addr = msg[0].addr = dev_addr;
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret != 2) {
+ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret);
+ return -1;
+ }
+
+ *p_data = b1[0];
+ if (debug >= 2)
+ dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]);
+ return 0;
+}
+
+static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
+{
+ lgs8gxx_write_reg(priv, 0x02, 0x00);
+ msleep(1);
+ lgs8gxx_write_reg(priv, 0x02, 0x01);
+ msleep(100);
+
+ return 0;
+}
+
+static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
+ u8 val, u8 delay, u8 tries)
+{
+ u8 t;
+ int i;
+
+ for (i = 0; i < tries; i++) {
+ lgs8gxx_read_reg(priv, reg, &t);
+
+ if ((t & mask) == val)
+ return 0;
+ msleep(delay);
+ }
+
+ return 1;
+}
+
+static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
+{
+ const struct lgs8gxx_config *config = priv->config;
+ u8 if_conf;
+
+ if_conf = 0x10; /* AGC output on, RF_AGC output off; */
+
+ if_conf |=
+ ((config->ext_adc) ? 0x80 : 0x00) |
+ ((config->if_neg_center) ? 0x04 : 0x00) |
+ ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
+ ((config->adc_signed) ? 0x02 : 0x00) |
+ ((config->if_neg_edge) ? 0x01 : 0x00);
+
+ if (config->ext_adc &&
+ (config->prod == LGS8GXX_PROD_LGS8G52)) {
+ lgs8gxx_write_reg(priv, 0xBA, 0x40);
+ }
+
+ lgs8gxx_write_reg(priv, 0x07, if_conf);
+
+ return 0;
+}
+
+static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
+{
+ u64 val;
+ u32 v32;
+ u32 if_clk;
+
+ if_clk = priv->config->if_clk_freq;
+
+ val = freq;
+ if (freq != 0) {
+ val <<= 32;
+ if (if_clk != 0)
+ do_div(val, if_clk);
+ v32 = val & 0xFFFFFFFF;
+ dprintk("Set IF Freq to %dkHz\n", freq);
+ } else {
+ v32 = 0;
+ dprintk("Set IF Freq to baseband\n");
+ }
+ dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
+ lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
+ lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
+ lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
+ } else {
+ lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
+ lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
+ lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
+ lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
+ }
+
+ return 0;
+}
+
+static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
+{
+ u64 val;
+ u32 v32 = 0;
+ u8 reg_addr, t;
+ int i;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+ reg_addr = 0x23;
+ else
+ reg_addr = 0x48;
+
+ for (i = 0; i < 4; i++) {
+ lgs8gxx_read_reg(priv, reg_addr, &t);
+ v32 <<= 8;
+ v32 |= t;
+ reg_addr--;
+ }
+
+ val = v32;
+ val *= priv->config->if_clk_freq;
+ val >>= 32;
+ dprintk("AFC = %u kHz\n", (u32)val);
+ return 0;
+}
+
+static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
+{
+ u8 t;
+ u8 prod = priv->config->prod;
+
+ if (prod == LGS8GXX_PROD_LGS8913)
+ lgs8gxx_write_reg(priv, 0xC6, 0x01);
+
+ if (prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_read_reg(priv, 0x0C, &t);
+ t &= (~0x04);
+ lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
+ lgs8gxx_write_reg(priv, 0x39, 0x00);
+ lgs8gxx_write_reg(priv, 0x3D, 0x04);
+ } else if (prod == LGS8GXX_PROD_LGS8913 ||
+ prod == LGS8GXX_PROD_LGS8GL5 ||
+ prod == LGS8GXX_PROD_LGS8G42 ||
+ prod == LGS8GXX_PROD_LGS8G52 ||
+ prod == LGS8GXX_PROD_LGS8G54) {
+ lgs8gxx_read_reg(priv, 0x7E, &t);
+ lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
+
+ /* clear FEC self reset */
+ lgs8gxx_read_reg(priv, 0xC5, &t);
+ lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
+ }
+
+ if (prod == LGS8GXX_PROD_LGS8913) {
+ /* FEC auto detect */
+ lgs8gxx_write_reg(priv, 0xC1, 0x03);
+
+ lgs8gxx_read_reg(priv, 0x7C, &t);
+ t = (t & 0x8C) | 0x03;
+ lgs8gxx_write_reg(priv, 0x7C, t);
+
+ /* BER test mode */
+ lgs8gxx_read_reg(priv, 0xC3, &t);
+ t = (t & 0xEF) | 0x10;
+ lgs8gxx_write_reg(priv, 0xC3, t);
+ }
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
+ lgs8gxx_write_reg(priv, 0xD9, 0x40);
+
+ return 0;
+}
+
+static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
+{
+ u8 t;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ u8 t2;
+ lgs8gxx_read_reg(priv, 0x0C, &t);
+ t &= (~0x80);
+ lgs8gxx_write_reg(priv, 0x0C, t);
+
+ lgs8gxx_read_reg(priv, 0x0C, &t);
+ lgs8gxx_read_reg(priv, 0x19, &t2);
+
+ if (((t&0x03) == 0x01) && (t2&0x01)) {
+ lgs8gxx_write_reg(priv, 0x6E, 0x05);
+ lgs8gxx_write_reg(priv, 0x39, 0x02);
+ lgs8gxx_write_reg(priv, 0x39, 0x03);
+ lgs8gxx_write_reg(priv, 0x3D, 0x05);
+ lgs8gxx_write_reg(priv, 0x3E, 0x28);
+ lgs8gxx_write_reg(priv, 0x53, 0x80);
+ } else {
+ lgs8gxx_write_reg(priv, 0x6E, 0x3F);
+ lgs8gxx_write_reg(priv, 0x39, 0x00);
+ lgs8gxx_write_reg(priv, 0x3D, 0x04);
+ }
+
+ lgs8gxx_soft_reset(priv);
+ return 0;
+ }
+
+ /* turn off auto-detect; manual settings */
+ lgs8gxx_write_reg(priv, 0x7E, 0);
+ if (priv->config->prod == LGS8GXX_PROD_LGS8913)
+ lgs8gxx_write_reg(priv, 0xC1, 0);
+
+ lgs8gxx_read_reg(priv, 0xC5, &t);
+ t = (t & 0xE0) | 0x06;
+ lgs8gxx_write_reg(priv, 0xC5, t);
+
+ lgs8gxx_soft_reset(priv);
+
+ return 0;
+}
+
+static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
+{
+ int ret = 0;
+ u8 t;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+ ret = lgs8gxx_read_reg(priv, 0x13, &t);
+ else
+ ret = lgs8gxx_read_reg(priv, 0x4B, &t);
+ if (ret != 0)
+ return ret;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+ *locked = ((t & 0x80) == 0x80) ? 1 : 0;
+ else
+ *locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
+ return 0;
+}
+
+/* Wait for Code Acquisition Lock */
+static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
+{
+ int ret = 0;
+ u8 reg, mask, val;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ reg = 0x13;
+ mask = 0x80;
+ val = 0x80;
+ } else {
+ reg = 0x4B;
+ mask = 0xC0;
+ val = 0xC0;
+ }
+
+ ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
+ *locked = (ret == 0) ? 1 : 0;
+
+ return 0;
+}
+
+static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
+ u8 *finished)
+{
+ int ret = 0;
+ u8 reg, mask, val;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ reg = 0x1f;
+ mask = 0xC0;
+ val = 0x80;
+ } else {
+ reg = 0xA4;
+ mask = 0x03;
+ val = 0x01;
+ }
+
+ ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
+ *finished = (ret == 0) ? 1 : 0;
+
+ return 0;
+}
+
+static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
+ u8 *locked)
+{
+ int err = 0;
+ u8 ad_fini = 0;
+ u8 t1, t2;
+
+ if (gi == GI_945)
+ dprintk("try GI 945\n");
+ else if (gi == GI_595)
+ dprintk("try GI 595\n");
+ else if (gi == GI_420)
+ dprintk("try GI 420\n");
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_read_reg(priv, 0x0C, &t1);
+ lgs8gxx_read_reg(priv, 0x18, &t2);
+ t1 &= ~(GI_MASK);
+ t1 |= gi;
+ t2 &= 0xFE;
+ t2 |= cpn ? 0x01 : 0x00;
+ lgs8gxx_write_reg(priv, 0x0C, t1);
+ lgs8gxx_write_reg(priv, 0x18, t2);
+ } else {
+ lgs8gxx_write_reg(priv, 0x04, gi);
+ }
+ lgs8gxx_soft_reset(priv);
+ err = lgs8gxx_wait_ca_lock(priv, locked);
+ if (err || !(*locked))
+ return err;
+ err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
+ if (err != 0)
+ return err;
+ if (ad_fini) {
+ dprintk("auto detect finished\n");
+ } else
+ *locked = 0;
+
+ return 0;
+}
+
+static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
+ u8 *detected_param, u8 *gi)
+{
+ int i, j;
+ int err = 0;
+ u8 locked = 0, tmp_gi;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gxx_set_mode_auto(priv);
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_write_reg(priv, 0x67, 0xAA);
+ lgs8gxx_write_reg(priv, 0x6E, 0x3F);
+ } else {
+ /* Guard Interval */
+ lgs8gxx_write_reg(priv, 0x03, 00);
+ }
+
+ for (i = 0; i < 2; i++) {
+ for (j = 0; j < 2; j++) {
+ tmp_gi = GI_945;
+ err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
+ if (err)
+ goto out;
+ if (locked)
+ goto locked;
+ }
+ for (j = 0; j < 2; j++) {
+ tmp_gi = GI_420;
+ err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
+ if (err)
+ goto out;
+ if (locked)
+ goto locked;
+ }
+ tmp_gi = GI_595;
+ err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
+ if (err)
+ goto out;
+ if (locked)
+ goto locked;
+ }
+
+locked:
+ if ((err == 0) && (locked == 1)) {
+ u8 t;
+
+ if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_read_reg(priv, 0xA2, &t);
+ *detected_param = t;
+ } else {
+ lgs8gxx_read_reg(priv, 0x1F, &t);
+ *detected_param = t & 0x3F;
+ }
+
+ if (tmp_gi == GI_945)
+ dprintk("GI 945 locked\n");
+ else if (tmp_gi == GI_595)
+ dprintk("GI 595 locked\n");
+ else if (tmp_gi == GI_420)
+ dprintk("GI 420 locked\n");
+ *gi = tmp_gi;
+ }
+ if (!locked)
+ err = -1;
+
+out:
+ return err;
+}
+
+static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
+{
+ s8 err;
+ u8 gi = 0x2;
+ u8 detected_param = 0;
+
+ err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
+
+ if (err != 0) {
+ dprintk("lgs8gxx_auto_detect failed\n");
+ } else
+ dprintk("detected param = 0x%02X\n", detected_param);
+
+ /* Apply detected parameters */
+ if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
+ u8 inter_leave_len = detected_param & TIM_MASK ;
+ /* Fix 8913 time interleaver detection bug */
+ inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
+ detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK;
+ detected_param |= inter_leave_len;
+ }
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ u8 t;
+ lgs8gxx_read_reg(priv, 0x19, &t);
+ t &= 0x81;
+ t |= detected_param << 1;
+ lgs8gxx_write_reg(priv, 0x19, t);
+ } else {
+ lgs8gxx_write_reg(priv, 0x7D, detected_param);
+ if (priv->config->prod == LGS8GXX_PROD_LGS8913)
+ lgs8gxx_write_reg(priv, 0xC0, detected_param);
+ }
+ /* lgs8gxx_soft_reset(priv); */
+
+ /* Enter manual mode */
+ lgs8gxx_set_mode_manual(priv);
+
+ switch (gi) {
+ case GI_945:
+ priv->curr_gi = 945; break;
+ case GI_595:
+ priv->curr_gi = 595; break;
+ case GI_420:
+ priv->curr_gi = 420; break;
+ default:
+ priv->curr_gi = 945; break;
+ }
+}
+
+static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
+ u8 serial, u8 clk_pol, u8 clk_gated)
+{
+ int ret = 0;
+ u8 t, reg_addr;
+
+ reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
+ ret = lgs8gxx_read_reg(priv, reg_addr, &t);
+ if (ret != 0)
+ return ret;
+
+ t &= 0xF8;
+ t |= serial ? TS_SERIAL : TS_PARALLEL;
+ t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
+ t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
+
+ ret = lgs8gxx_write_reg(priv, reg_addr, t);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+/* A/D input peak-to-peak voltage range */
+static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
+ u8 sel)
+{
+ u8 r26 = 0x73, r27 = 0x90;
+
+ if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
+ return 0;
+
+ r26 |= (sel & 0x01) << 7;
+ r27 |= (sel & 0x02) >> 1;
+ lgs8gxx_write_reg(priv, 0x26, r26);
+ lgs8gxx_write_reg(priv, 0x27, r27);
+
+ return 0;
+}
+
+/* LGS8913 demod frontend functions */
+
+static int lgs8913_init(struct lgs8gxx_state *priv)
+{
+ u8 t;
+
+ /* LGS8913 specific */
+ lgs8gxx_write_reg(priv, 0xc1, 0x3);
+
+ lgs8gxx_read_reg(priv, 0x7c, &t);
+ lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
+
+ /* LGS8913 specific */
+ lgs8gxx_read_reg(priv, 0xc3, &t);
+ lgs8gxx_write_reg(priv, 0xc3, t&0x10);
+
+
+ return 0;
+}
+
+static int lgs8g75_init_data(struct lgs8gxx_state *priv)
+{
+ const struct firmware *fw;
+ int rc;
+ int i;
+
+ rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
+ if (rc)
+ return rc;
+
+ lgs8gxx_write_reg(priv, 0xC6, 0x40);
+
+ lgs8gxx_write_reg(priv, 0x3D, 0x04);
+ lgs8gxx_write_reg(priv, 0x39, 0x00);
+
+ lgs8gxx_write_reg(priv, 0x3A, 0x00);
+ lgs8gxx_write_reg(priv, 0x38, 0x00);
+ lgs8gxx_write_reg(priv, 0x3B, 0x00);
+ lgs8gxx_write_reg(priv, 0x38, 0x00);
+
+ for (i = 0; i < fw->size; i++) {
+ lgs8gxx_write_reg(priv, 0x38, 0x00);
+ lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
+ lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
+ lgs8gxx_write_reg(priv, 0x3C, fw->data[i]);
+ }
+
+ lgs8gxx_write_reg(priv, 0x38, 0x00);
+
+ release_firmware(fw);
+ return 0;
+}
+
+static int lgs8gxx_init(struct dvb_frontend *fe)
+{
+ struct lgs8gxx_state *priv =
+ (struct lgs8gxx_state *)fe->demodulator_priv;
+ const struct lgs8gxx_config *config = priv->config;
+ u8 data = 0;
+ s8 err;
+ dprintk("%s\n", __func__);
+
+ lgs8gxx_read_reg(priv, 0, &data);
+ dprintk("reg 0 = 0x%02X\n", data);
+
+ if (config->prod == LGS8GXX_PROD_LGS8G75)
+ lgs8g75_set_adc_vpp(priv, config->adc_vpp);
+
+ /* Setup MPEG output format */
+ err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
+ config->ts_clk_pol,
+ config->ts_clk_gated);
+ if (err != 0)
+ return -EIO;
+
+ if (config->prod == LGS8GXX_PROD_LGS8913)
+ lgs8913_init(priv);
+ lgs8gxx_set_if_freq(priv, priv->config->if_freq);
+ lgs8gxx_set_ad_mode(priv);
+
+ return 0;
+}
+
+static void lgs8gxx_release(struct dvb_frontend *fe)
+{
+ struct lgs8gxx_state *state = fe->demodulator_priv;
+ dprintk("%s\n", __func__);
+
+ kfree(state);
+}
+
+
+static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return lgs8gxx_write_reg(priv, buf[0], buf[1]);
+}
+
+static int lgs8gxx_set_fe(struct dvb_frontend *fe)
+{
+
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ /* set frequency */
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ /* start auto lock */
+ lgs8gxx_auto_lock(priv);
+
+ msleep(10);
+
+ return 0;
+}
+
+static int lgs8gxx_get_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
+ dprintk("%s\n", __func__);
+
+ /* TODO: get real readings from device */
+ /* inversion status */
+ fe_params->inversion = INVERSION_OFF;
+
+ /* bandwidth */
+ fe_params->bandwidth_hz = 8000000;
+
+ fe_params->code_rate_HP = FEC_AUTO;
+ fe_params->code_rate_LP = FEC_AUTO;
+
+ fe_params->modulation = QAM_AUTO;
+
+ /* transmission mode */
+ fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
+
+ /* guard interval */
+ fe_params->guard_interval = GUARD_INTERVAL_AUTO;
+
+ /* hierarchy */
+ fe_params->hierarchy = HIERARCHY_NONE;
+
+ return 0;
+}
+
+static
+int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ /* FIXME: copy from tda1004x.c */
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+ s8 ret;
+ u8 t, locked = 0;
+
+ dprintk("%s\n", __func__);
+ *fe_status = 0;
+
+ lgs8gxx_get_afc_phase(priv);
+ lgs8gxx_is_locked(priv, &locked);
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ if (locked)
+ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ return 0;
+ }
+
+ ret = lgs8gxx_read_reg(priv, 0x4B, &t);
+ if (ret != 0)
+ return -EIO;
+
+ dprintk("Reg 0x4B: 0x%02X\n", t);
+
+ *fe_status = 0;
+ if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
+ if ((t & 0x40) == 0x40)
+ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ if ((t & 0x80) == 0x80)
+ *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ } else {
+ if ((t & 0x80) == 0x80)
+ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+
+ /* success */
+ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
+ return 0;
+}
+
+static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
+{
+ u16 v;
+ u8 agc_lvl[2], cat;
+
+ dprintk("%s()\n", __func__);
+ lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
+ lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
+
+ v = agc_lvl[0];
+ v <<= 8;
+ v |= agc_lvl[1];
+
+ dprintk("agc_lvl: 0x%04X\n", v);
+
+ if (v < 0x100)
+ cat = 0;
+ else if (v < 0x190)
+ cat = 5;
+ else if (v < 0x2A8)
+ cat = 4;
+ else if (v < 0x381)
+ cat = 3;
+ else if (v < 0x400)
+ cat = 2;
+ else if (v == 0x400)
+ cat = 1;
+ else
+ cat = 0;
+
+ *signal = cat * 65535 / 5;
+
+ return 0;
+}
+
+static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
+{
+ u8 t; s8 ret;
+ s16 max_strength = 0;
+ u8 str;
+ u16 i, gi = priv->curr_gi;
+
+ dprintk("%s\n", __func__);
+
+ ret = lgs8gxx_read_reg(priv, 0x4B, &t);
+ if (ret != 0)
+ return -EIO;
+
+ if (fake_signal_str) {
+ if ((t & 0xC0) == 0xC0) {
+ dprintk("Fake signal strength\n");
+ *signal = 0x7FFF;
+ } else
+ *signal = 0;
+ return 0;
+ }
+
+ dprintk("gi = %d\n", gi);
+ for (i = 0; i < gi; i++) {
+
+ if ((i & 0xFF) == 0)
+ lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
+ lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
+
+ lgs8gxx_read_reg(priv, 0x94, &str);
+ if (max_strength < str)
+ max_strength = str;
+ }
+
+ *signal = max_strength;
+ dprintk("%s: signal=0x%02X\n", __func__, *signal);
+
+ lgs8gxx_read_reg(priv, 0x95, &t);
+ dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
+
+ return 0;
+}
+
+static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
+{
+ u8 t;
+ s16 v = 0;
+
+ dprintk("%s\n", __func__);
+
+ lgs8gxx_read_reg(priv, 0xB1, &t);
+ v |= t;
+ v <<= 8;
+ lgs8gxx_read_reg(priv, 0xB0, &t);
+ v |= t;
+
+ *signal = v;
+ dprintk("%s: signal=0x%02X\n", __func__, *signal);
+
+ return 0;
+}
+
+static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8913)
+ return lgs8913_read_signal_strength(priv, signal);
+ else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+ return lgs8g75_read_signal_strength(priv, signal);
+ else
+ return lgs8gxx_read_signal_agc(priv, signal);
+}
+
+static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+ u8 t;
+ *snr = 0;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
+ lgs8gxx_read_reg(priv, 0x34, &t);
+ else
+ lgs8gxx_read_reg(priv, 0x95, &t);
+ dprintk("AVG Noise=0x%02X\n", t);
+ *snr = 256 - t;
+ *snr <<= 8;
+ dprintk("snr=0x%x\n", *snr);
+
+ return 0;
+}
+
+static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
+ return 0;
+}
+
+static void packet_counter_start(struct lgs8gxx_state *priv)
+{
+ u8 orig, t;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_read_reg(priv, 0x30, &orig);
+ orig &= 0xE7;
+ t = orig | 0x10;
+ lgs8gxx_write_reg(priv, 0x30, t);
+ t = orig | 0x18;
+ lgs8gxx_write_reg(priv, 0x30, t);
+ t = orig | 0x10;
+ lgs8gxx_write_reg(priv, 0x30, t);
+ } else {
+ lgs8gxx_write_reg(priv, 0xC6, 0x01);
+ lgs8gxx_write_reg(priv, 0xC6, 0x41);
+ lgs8gxx_write_reg(priv, 0xC6, 0x01);
+ }
+}
+
+static void packet_counter_stop(struct lgs8gxx_state *priv)
+{
+ u8 t;
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ lgs8gxx_read_reg(priv, 0x30, &t);
+ t &= 0xE7;
+ lgs8gxx_write_reg(priv, 0x30, t);
+ } else {
+ lgs8gxx_write_reg(priv, 0xC6, 0x81);
+ }
+}
+
+static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+ u8 reg_err, reg_total, t;
+ u32 total_cnt = 0, err_cnt = 0;
+ int i;
+
+ dprintk("%s\n", __func__);
+
+ packet_counter_start(priv);
+ msleep(200);
+ packet_counter_stop(priv);
+
+ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
+ reg_total = 0x28; reg_err = 0x2C;
+ } else {
+ reg_total = 0xD0; reg_err = 0xD4;
+ }
+
+ for (i = 0; i < 4; i++) {
+ total_cnt <<= 8;
+ lgs8gxx_read_reg(priv, reg_total+3-i, &t);
+ total_cnt |= t;
+ }
+ for (i = 0; i < 4; i++) {
+ err_cnt <<= 8;
+ lgs8gxx_read_reg(priv, reg_err+3-i, &t);
+ err_cnt |= t;
+ }
+ dprintk("error=%d total=%d\n", err_cnt, total_cnt);
+
+ if (total_cnt == 0)
+ *ber = 0;
+ else
+ *ber = err_cnt * 100 / total_cnt;
+
+ dprintk("%s: ber=0x%x\n", __func__, *ber);
+ return 0;
+}
+
+static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct lgs8gxx_state *priv = fe->demodulator_priv;
+
+ if (priv->config->tuner_address == 0)
+ return 0;
+ if (enable) {
+ u8 v = 0x80 | priv->config->tuner_address;
+ return lgs8gxx_write_reg(priv, 0x01, v);
+ }
+ return lgs8gxx_write_reg(priv, 0x01, 0);
+}
+
+static struct dvb_frontend_ops lgs8gxx_ops = {
+ .delsys = { SYS_DTMB },
+ .info = {
+ .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
+ .frequency_min = 474000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 10000,
+ .caps =
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = lgs8gxx_release,
+
+ .init = lgs8gxx_init,
+ .write = lgs8gxx_write,
+ .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
+
+ .set_frontend = lgs8gxx_set_fe,
+ .get_frontend = lgs8gxx_get_fe,
+ .get_tune_settings = lgs8gxx_get_tune_settings,
+
+ .read_status = lgs8gxx_read_status,
+ .read_ber = lgs8gxx_read_ber,
+ .read_signal_strength = lgs8gxx_read_signal_strength,
+ .read_snr = lgs8gxx_read_snr,
+ .read_ucblocks = lgs8gxx_read_ucblocks,
+};
+
+struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct lgs8gxx_state *priv = NULL;
+ u8 data = 0;
+
+ dprintk("%s()\n", __func__);
+
+ if (config == NULL || i2c == NULL)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
+ if (priv == NULL)
+ goto error_out;
+
+ priv->config = config;
+ priv->i2c = i2c;
+
+ /* check if the demod is there */
+ if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
+ dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
+ __func__, priv->config->demod_address);
+ goto error_out;
+ }
+
+ lgs8gxx_read_reg(priv, 1, &data);
+
+ memcpy(&priv->frontend.ops, &lgs8gxx_ops,
+ sizeof(struct dvb_frontend_ops));
+ priv->frontend.demodulator_priv = priv;
+
+ if (config->prod == LGS8GXX_PROD_LGS8G75)
+ lgs8g75_init_data(priv);
+
+ return &priv->frontend;
+
+error_out:
+ dprintk("%s() error_out\n", __func__);
+ kfree(priv);
+ return NULL;
+
+}
+EXPORT_SYMBOL(lgs8gxx_attach);
+
+MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
+MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
diff --git a/drivers/media/dvb/frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h
index 33c3c5e162fa..33c3c5e162fa 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.h
+++ b/drivers/media/dvb-frontends/lgs8gxx.h
diff --git a/drivers/media/dvb/frontends/lgs8gxx_priv.h b/drivers/media/dvb-frontends/lgs8gxx_priv.h
index 8ef376f1414d..8ef376f1414d 100644
--- a/drivers/media/dvb/frontends/lgs8gxx_priv.h
+++ b/drivers/media/dvb-frontends/lgs8gxx_priv.h
diff --git a/drivers/media/dvb/frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h
index c059b165318f..c059b165318f 100644
--- a/drivers/media/dvb/frontends/lnbh24.h
+++ b/drivers/media/dvb-frontends/lnbh24.h
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c
index 13437259eeac..13437259eeac 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb-frontends/lnbp21.c
diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h
index fcdf1c650dde..fcdf1c650dde 100644
--- a/drivers/media/dvb/frontends/lnbp21.h
+++ b/drivers/media/dvb-frontends/lnbp21.h
diff --git a/drivers/media/dvb/frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c
index 84ad0390a4a1..84ad0390a4a1 100644
--- a/drivers/media/dvb/frontends/lnbp22.c
+++ b/drivers/media/dvb-frontends/lnbp22.c
diff --git a/drivers/media/dvb/frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h
index 63e2dec7e68a..63e2dec7e68a 100644
--- a/drivers/media/dvb/frontends/lnbp22.h
+++ b/drivers/media/dvb-frontends/lnbp22.h
diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c
new file mode 100644
index 000000000000..633815ed90ca
--- /dev/null
+++ b/drivers/media/dvb-frontends/m88rs2000.c
@@ -0,0 +1,919 @@
+/*
+ Driver for M88RS2000 demodulator and tuner
+
+ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ Beta Driver
+
+ Include various calculation code from DS3000 driver.
+ Copyright (C) 2009 Konstantin Dimitrov.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+
+#include "dvb_frontend.h"
+#include "m88rs2000.h"
+
+struct m88rs2000_state {
+ struct i2c_adapter *i2c;
+ const struct m88rs2000_config *config;
+ struct dvb_frontend frontend;
+ u8 no_lock_count;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ u8 tuner_level;
+ int errmode;
+};
+
+static int m88rs2000_debug;
+
+module_param_named(debug, m88rs2000_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & m88rs2000_debug) \
+ printk(KERN_DEBUG "m88rs2000-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define info(format, arg...) \
+ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
+
+static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner,
+ u8 reg, u8 data)
+{
+ int ret;
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ return m88rs2000_writereg(state, 1, reg, data);
+}
+
+static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ udelay(10);
+ return m88rs2000_writereg(state, 0, reg, data);
+
+}
+
+static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return m88rs2000_writereg(state, 1, buf[0], buf[1]);
+}
+
+static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ struct i2c_msg msg[] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg)
+{
+ return m88rs2000_readreg(state, 1, reg);
+}
+
+static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg)
+{
+ m88rs2000_demod_write(state, 0x81, 0x85);
+ udelay(10);
+ return m88rs2000_readreg(state, 0, reg);
+}
+
+static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 temp;
+ u8 b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ temp = srate / 1000;
+ temp *= 11831;
+ temp /= 68;
+ temp -= 3;
+
+ b[0] = (u8) (temp >> 16) & 0xff;
+ b[1] = (u8) (temp >> 8) & 0xff;
+ b[2] = (u8) temp & 0xff;
+ ret = m88rs2000_demod_write(state, 0x93, b[2]);
+ ret |= m88rs2000_demod_write(state, 0x94, b[1]);
+ ret |= m88rs2000_demod_write(state, 0x95, b[0]);
+
+ deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
+ return ret;
+}
+
+static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ int i;
+ u8 reg;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ for (i = 0; i < m->msg_len; i++)
+ m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]);
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ reg &= 0x87;
+ reg |= ((m->msg_len - 1) << 3) | 0x07;
+ reg &= 0x7f;
+ m88rs2000_demod_write(state, 0xb1, reg);
+
+ for (i = 0; i < 15; i++) {
+ if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0)
+ break;
+ msleep(20);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ if ((reg & 0x40) > 0x0) {
+ reg &= 0x7f;
+ reg |= 0x40;
+ m88rs2000_demod_write(state, 0xb1, reg);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ reg |= 0x80;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+
+ return 0;
+}
+
+static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ msleep(50);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+ /* TODO complete this section */
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ return 0;
+}
+
+static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+
+ reg1 &= 0x3f;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ reg0 |= 0x4;
+ reg0 &= 0xbc;
+ break;
+ case SEC_TONE_OFF:
+ reg1 |= 0x80;
+ break;
+ default:
+ break;
+ }
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+ return 0;
+}
+
+struct inittab {
+ u8 cmd;
+ u8 reg;
+ u8 val;
+};
+
+struct inittab m88rs2000_setup[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {WRITE_DELAY, 0x19, 0x00},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {DEMOD_WRITE, 0x81, 0xc1},
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {DEMOD_WRITE, 0x86, 0xc6},
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xf0, 0x22},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0xb0, 0x45},
+ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab m88rs2000_shutdown[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xb0, 0x00},
+ {DEMOD_WRITE, 0xf1, 0x89},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {TUNER_WRITE, 0x00, 0x40},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab tuner_reset[] = {
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_reset[] = {
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x20, 0x81},
+ {DEMOD_WRITE, 0x21, 0x80},
+ {DEMOD_WRITE, 0x10, 0x33},
+ {DEMOD_WRITE, 0x11, 0x44},
+ {DEMOD_WRITE, 0x12, 0x07},
+ {DEMOD_WRITE, 0x18, 0x20},
+ {DEMOD_WRITE, 0x28, 0x04},
+ {DEMOD_WRITE, 0x29, 0x8e},
+ {DEMOD_WRITE, 0x3b, 0xff},
+ {DEMOD_WRITE, 0x32, 0x10},
+ {DEMOD_WRITE, 0x33, 0x02},
+ {DEMOD_WRITE, 0x34, 0x30},
+ {DEMOD_WRITE, 0x35, 0xff},
+ {DEMOD_WRITE, 0x38, 0x50},
+ {DEMOD_WRITE, 0x39, 0x68},
+ {DEMOD_WRITE, 0x3c, 0x7f},
+ {DEMOD_WRITE, 0x3d, 0x0f},
+ {DEMOD_WRITE, 0x45, 0x20},
+ {DEMOD_WRITE, 0x46, 0x24},
+ {DEMOD_WRITE, 0x47, 0x7c},
+ {DEMOD_WRITE, 0x48, 0x16},
+ {DEMOD_WRITE, 0x49, 0x04},
+ {DEMOD_WRITE, 0x4a, 0x01},
+ {DEMOD_WRITE, 0x4b, 0x78},
+ {DEMOD_WRITE, 0X4d, 0xd2},
+ {DEMOD_WRITE, 0x4e, 0x6d},
+ {DEMOD_WRITE, 0x50, 0x30},
+ {DEMOD_WRITE, 0x51, 0x30},
+ {DEMOD_WRITE, 0x54, 0x7b},
+ {DEMOD_WRITE, 0x56, 0x09},
+ {DEMOD_WRITE, 0x58, 0x59},
+ {DEMOD_WRITE, 0x59, 0x37},
+ {DEMOD_WRITE, 0x63, 0xfa},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_trigger[] = {
+ {DEMOD_WRITE, 0x97, 0x04},
+ {DEMOD_WRITE, 0x99, 0x77},
+ {DEMOD_WRITE, 0x9b, 0x64},
+ {DEMOD_WRITE, 0x9e, 0x00},
+ {DEMOD_WRITE, 0x9f, 0xf8},
+ {DEMOD_WRITE, 0xa0, 0x20},
+ {DEMOD_WRITE, 0xa1, 0xe0},
+ {DEMOD_WRITE, 0xa3, 0x38},
+ {DEMOD_WRITE, 0x98, 0xff},
+ {DEMOD_WRITE, 0xc0, 0x0f},
+ {DEMOD_WRITE, 0x89, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {WRITE_DELAY, 0x0a, 0x00},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+static int m88rs2000_tab_set(struct m88rs2000_state *state,
+ struct inittab *tab)
+{
+ int ret = 0;
+ u8 i;
+ if (tab == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 255; i++) {
+ switch (tab[i].cmd) {
+ case 0x01:
+ ret = m88rs2000_demod_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x02:
+ ret = m88rs2000_tuner_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x10:
+ if (tab[i].reg > 0)
+ mdelay(tab[i].reg);
+ break;
+ case 0xff:
+ if (tab[i].reg == 0xaa && tab[i].val == 0xff)
+ return 0;
+ case 0x00:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ data = m88rs2000_demod_read(state, 0xb2);
+ data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+ switch (volt) {
+ case SEC_VOLTAGE_18:
+ data &= ~0x03;
+ break;
+ case SEC_VOLTAGE_13:
+ data &= ~0x03;
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ m88rs2000_demod_write(state, 0xb2, data);
+
+ return 0;
+}
+
+static int m88rs2000_startup(struct m88rs2000_state *state)
+{
+ int ret = 0;
+ u8 reg;
+
+ reg = m88rs2000_tuner_read(state, 0x00);
+ if ((reg & 0x40) == 0)
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int m88rs2000_init(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+
+ deb_info("m88rs2000: init chip\n");
+ /* Setup frontend from shutdown/cold */
+ ret = m88rs2000_tab_set(state, m88rs2000_setup);
+
+ return ret;
+}
+
+static int m88rs2000_sleep(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ /* Shutdown the frondend */
+ ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
+ return ret;
+}
+
+static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
+
+ *status = 0;
+
+ if ((reg & 0x7) == 0x7) {
+ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
+ | FE_HAS_SYNC | FE_HAS_LOCK;
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, CALL_IS_READ);
+ }
+ return 0;
+}
+
+/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */
+
+static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ber);
+ *ber = 0;
+ return 0;
+}
+
+static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ deb_info("m88rs2000_read_snr %d\n", *snr);
+ *snr = 0;
+ return 0;
+}
+
+static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ucblocks);
+ *ucblocks = 0;
+ return 0;
+}
+
+static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset)
+{
+ int ret;
+ ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset);
+ ret |= m88rs2000_tuner_write(state, 0x51, 0x1f);
+ ret |= m88rs2000_tuner_write(state, 0x50, offset);
+ ret |= m88rs2000_tuner_write(state, 0x50, 0x00);
+ msleep(20);
+ return ret;
+}
+
+static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int reg;
+ reg = m88rs2000_tuner_read(state, 0x3d);
+ reg &= 0x7f;
+ if (reg < 0x16)
+ reg = 0xa1;
+ else if (reg == 0x16)
+ reg = 0x99;
+ else
+ reg = 0xf9;
+
+ m88rs2000_tuner_write(state, 0x60, reg);
+ reg = m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ return reg;
+}
+
+static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 frequency = c->frequency;
+ s32 offset_khz;
+ s32 tmp;
+ u32 symbol_rate = (c->symbol_rate / 1000);
+ u32 f3db, gdiv28;
+ u16 value, ndiv, lpf_coeff;
+ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
+ u8 lo = 0x01, div4 = 0x0;
+
+ /* Reset Tuner */
+ ret = m88rs2000_tab_set(state, tuner_reset);
+
+ /* Calculate frequency divider */
+ if (frequency < 1060000) {
+ lo |= 0x10;
+ div4 = 0x1;
+ ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ;
+ } else
+ ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ;
+ ndiv = ndiv + ndiv % 2;
+ ndiv = ndiv - 1024;
+
+ ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo);
+
+ /* Set frequency divider */
+ ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf);
+ ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff);
+
+ ret |= m88rs2000_tuner_write(state, 0x03, 0x06);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x10);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Tuner Frequency Range */
+ ret = m88rs2000_tuner_write(state, 0x10, lo);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ /* Tuner RF */
+ ret |= m88rs2000_set_tuner_rf(fe);
+
+ gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+ ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+ if (ret < 0)
+ return -ENODEV;
+
+ value = m88rs2000_tuner_read(state, 0x26);
+
+ f3db = (symbol_rate * 135) / 200 + 2000;
+ f3db += FREQ_OFFSET_LOW_SYM_RATE;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ gdiv28 = gdiv28 * 207 / (value * 2 + 151);
+ mlpf_max = gdiv28 * 135 / 100;
+ mlpf_min = gdiv28 * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ lpf_coeff = 2766;
+
+ nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
+ (FE_CRYSTAL_KHZ / 1000) + 1) / 2;
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+
+ if (lpf_mxdiv < mlpf_min) {
+ nlpf++;
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+ }
+
+ if (lpf_mxdiv > mlpf_max)
+ lpf_mxdiv = mlpf_max;
+
+ ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv);
+ ret |= m88rs2000_tuner_write(state, 0x06, nlpf);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x01);
+
+ msleep(80);
+ /* calculate offset assuming 96000kHz*/
+ offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ
+ / 14 / (div4 + 1) / 2;
+
+ offset_khz -= frequency;
+
+ tmp = offset_khz;
+ tmp *= 65536;
+
+ tmp = (2 * tmp + 96000) / (2 * 96000);
+ if (tmp < 0)
+ tmp += 65536;
+
+ *offset = tmp & 0xffff;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret < 0) ? -EINVAL : 0;
+}
+
+static int m88rs2000_set_fec(struct m88rs2000_state *state,
+ fe_code_rate_t fec)
+{
+ u16 fec_set;
+ switch (fec) {
+ /* This is not confirmed kept for reference */
+/* case FEC_1_2:
+ fec_set = 0x88;
+ break;
+ case FEC_2_3:
+ fec_set = 0x68;
+ break;
+ case FEC_3_4:
+ fec_set = 0x48;
+ break;
+ case FEC_5_6:
+ fec_set = 0x28;
+ break;
+ case FEC_7_8:
+ fec_set = 0x18;
+ break; */
+ case FEC_AUTO:
+ default:
+ fec_set = 0x08;
+ }
+ m88rs2000_demod_write(state, 0x76, fec_set);
+
+ return 0;
+}
+
+
+static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
+{
+ u8 reg;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0x76);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ switch (reg) {
+ case 0x88:
+ return FEC_1_2;
+ case 0x68:
+ return FEC_2_3;
+ case 0x48:
+ return FEC_3_4;
+ case 0x28:
+ return FEC_5_6;
+ case 0x18:
+ return FEC_7_8;
+ case 0x08:
+ default:
+ break;
+ }
+
+ return FEC_AUTO;
+}
+
+static int m88rs2000_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ fe_status_t status;
+ int i, ret;
+ u16 offset = 0;
+ u8 reg;
+
+ state->no_lock_count = 0;
+
+ if (c->delivery_system != SYS_DVBS) {
+ deb_info("%s: unsupported delivery "
+ "system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set Tuner */
+ ret = m88rs2000_set_tuner(fe, &offset);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = m88rs2000_demod_write(state, 0x9a, 0x30);
+ /* Unknown usually 0xc6 sometimes 0xc1 */
+ reg = m88rs2000_demod_read(state, 0x86);
+ ret |= m88rs2000_demod_write(state, 0x86, reg);
+ /* Offset lower nibble always 0 */
+ ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8));
+ ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0);
+
+
+ /* Reset Demod */
+ ret = m88rs2000_tab_set(state, fe_reset);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Unknown */
+ reg = m88rs2000_demod_read(state, 0x70);
+ ret = m88rs2000_demod_write(state, 0x70, reg);
+
+ /* Set FEC */
+ ret |= m88rs2000_set_fec(state, c->fec_inner);
+ ret |= m88rs2000_demod_write(state, 0x85, 0x1);
+ ret |= m88rs2000_demod_write(state, 0x8a, 0xbf);
+ ret |= m88rs2000_demod_write(state, 0x8d, 0x1e);
+ ret |= m88rs2000_demod_write(state, 0x90, 0xf1);
+ ret |= m88rs2000_demod_write(state, 0x91, 0x08);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set Symbol Rate */
+ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set up Demod */
+ ret = m88rs2000_tab_set(state, fe_trigger);
+ if (ret < 0)
+ return -ENODEV;
+
+ for (i = 0; i < 25; i++) {
+ reg = m88rs2000_demod_read(state, 0x8c);
+ if ((reg & 0x7) == 0x7) {
+ status = FE_HAS_LOCK;
+ break;
+ }
+ state->no_lock_count++;
+ if (state->no_lock_count == 15) {
+ reg = m88rs2000_demod_read(state, 0x70);
+ reg ^= 0x4;
+ m88rs2000_demod_write(state, 0x70, reg);
+ state->no_lock_count = 0;
+ }
+ if (state->no_lock_count == 20)
+ m88rs2000_set_tuner_rf(fe);
+ msleep(20);
+ }
+
+ if (status & FE_HAS_LOCK) {
+ state->fec_inner = m88rs2000_get_fec(state);
+ /* Uknown suspect SNR level */
+ reg = m88rs2000_demod_read(state, 0x65);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->symbol_rate = c->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ c->fec_inner = state->fec_inner;
+ c->frequency = state->tuner_frequency;
+ c->symbol_rate = state->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (enable)
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ else
+ m88rs2000_demod_write(state, 0x81, 0x81);
+ udelay(10);
+ return 0;
+}
+
+static void m88rs2000_release(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops m88rs2000_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "M88RS2000 DVB-S",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .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_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = m88rs2000_release,
+ .init = m88rs2000_init,
+ .sleep = m88rs2000_sleep,
+ .write = m88rs2000_write,
+ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
+ .read_status = m88rs2000_read_status,
+ .read_ber = m88rs2000_read_ber,
+ .read_signal_strength = m88rs2000_read_signal_strength,
+ .read_snr = m88rs2000_read_snr,
+ .read_ucblocks = m88rs2000_read_ucblocks,
+ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
+ .diseqc_send_burst = m88rs2000_send_diseqc_burst,
+ .set_tone = m88rs2000_set_tone,
+ .set_voltage = m88rs2000_set_voltage,
+
+ .set_frontend = m88rs2000_set_frontend,
+ .get_frontend = m88rs2000_get_frontend,
+};
+
+struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88rs2000_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+
+ if (m88rs2000_startup(state) < 0)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &m88rs2000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(m88rs2000_attach);
+
+MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.13");
+
diff --git a/drivers/media/dvb/frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h
index 59acdb696873..59acdb696873 100644
--- a/drivers/media/dvb/frontends/m88rs2000.h
+++ b/drivers/media/dvb-frontends/m88rs2000.h
diff --git a/drivers/media/dvb/frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c
index 9ae40abfd71a..9ae40abfd71a 100644
--- a/drivers/media/dvb/frontends/mb86a16.c
+++ b/drivers/media/dvb-frontends/mb86a16.c
diff --git a/drivers/media/dvb/frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h
index 6ea8c376394f..6ea8c376394f 100644
--- a/drivers/media/dvb/frontends/mb86a16.h
+++ b/drivers/media/dvb-frontends/mb86a16.h
diff --git a/drivers/media/dvb/frontends/mb86a16_priv.h b/drivers/media/dvb-frontends/mb86a16_priv.h
index 360a35acfe84..360a35acfe84 100644
--- a/drivers/media/dvb/frontends/mb86a16_priv.h
+++ b/drivers/media/dvb-frontends/mb86a16_priv.h
diff --git a/drivers/media/dvb/frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c
index fade566927c3..fade566927c3 100644
--- a/drivers/media/dvb/frontends/mb86a20s.c
+++ b/drivers/media/dvb-frontends/mb86a20s.c
diff --git a/drivers/media/dvb/frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h
index bf22e77888b9..bf22e77888b9 100644
--- a/drivers/media/dvb/frontends/mb86a20s.h
+++ b/drivers/media/dvb-frontends/mb86a20s.h
diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c
index e20bf13aa860..e20bf13aa860 100644
--- a/drivers/media/dvb/frontends/mt312.c
+++ b/drivers/media/dvb-frontends/mt312.c
diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h
index 29e3bb5496b8..29e3bb5496b8 100644
--- a/drivers/media/dvb/frontends/mt312.h
+++ b/drivers/media/dvb-frontends/mt312.h
diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb-frontends/mt312_priv.h
index a3959f94d639..a3959f94d639 100644
--- a/drivers/media/dvb/frontends/mt312_priv.h
+++ b/drivers/media/dvb-frontends/mt312_priv.h
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c
index 2c3b50e828d7..2c3b50e828d7 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb-frontends/mt352.c
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h
index ca2562d6f289..ca2562d6f289 100644
--- a/drivers/media/dvb/frontends/mt352.h
+++ b/drivers/media/dvb-frontends/mt352.h
diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb-frontends/mt352_priv.h
index 44ad0d4c8f12..44ad0d4c8f12 100644
--- a/drivers/media/dvb/frontends/mt352_priv.h
+++ b/drivers/media/dvb-frontends/mt352_priv.h
diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c
new file mode 100644
index 000000000000..8e288940a61f
--- /dev/null
+++ b/drivers/media/dvb-frontends/nxt200x.c
@@ -0,0 +1,1242 @@
+/*
+ * Support for NXT2002 and NXT2004 - VSB/QAM
+ *
+ * Copyright (C) 2005 Kirk Lapray <kirk.lapray@gmail.com>
+ * Copyright (C) 2006 Michael Krufky <mkrufky@m1k.net>
+ * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net>
+ * and nxt2004 by Jean-Francois Thibert <jeanfrancois@sagetv.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+*/
+
+/*
+ * NOTES ABOUT THIS DRIVER
+ *
+ * This Linux driver supports:
+ * B2C2/BBTI Technisat Air2PC - ATSC (NXT2002)
+ * AverTVHD MCE A180 (NXT2004)
+ * ATI HDTV Wonder (NXT2004)
+ *
+ * This driver needs external firmware. Please use the command
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" or
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
+ * download/extract the appropriate firmware, and then copy it to
+ * /usr/lib/hotplug/firmware/ or /lib/firmware/
+ * (depending on configuration of firmware hotplug).
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
+#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw"
+#define CRC_CCIT_MASK 0x1021
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "dvb_frontend.h"
+#include "nxt200x.h"
+
+struct nxt200x_state {
+
+ struct i2c_adapter* i2c;
+ const struct nxt200x_config* config;
+ struct dvb_frontend frontend;
+
+ /* demodulator private data */
+ nxt_chip_type demod_chip;
+ u8 initialised:1;
+};
+
+static int debug;
+#define dprintk(args...) do { if (debug) pr_debug(args); } while (0)
+
+static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len)
+{
+ int err;
+ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n",
+ __func__, addr, err);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
+{
+ int err;
+ struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n",
+ __func__, addr, err);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
+ const u8 *buf, u8 len)
+{
+ u8 buf2 [len+1];
+ int err;
+ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
+
+ buf2[0] = reg;
+ memcpy(&buf2[1], buf, len);
+
+ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
+ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n",
+ __func__, state->config->demod_address, err);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len)
+{
+ u8 reg2 [] = { reg };
+
+ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 },
+ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
+
+ int err;
+
+ if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
+ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n",
+ __func__, state->config->demod_address, err);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static u16 nxt200x_crc(u16 crc, u8 c)
+{
+ u8 i;
+ u16 input = (u16) c & 0xFF;
+
+ input<<=8;
+ for(i=0; i<8; i++) {
+ if((crc^input) & 0x8000)
+ crc=(crc<<1)^CRC_CCIT_MASK;
+ else
+ crc<<=1;
+ input<<=1;
+ }
+ return crc;
+}
+
+static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len)
+{
+ u8 attr, len2, buf;
+ dprintk("%s\n", __func__);
+
+ /* set mutli register register */
+ nxt200x_writebytes(state, 0x35, &reg, 1);
+
+ /* send the actual data */
+ nxt200x_writebytes(state, 0x36, data, len);
+
+ switch (state->demod_chip) {
+ case NXT2002:
+ len2 = len;
+ buf = 0x02;
+ break;
+ case NXT2004:
+ /* probably not right, but gives correct values */
+ attr = 0x02;
+ if (reg & 0x80) {
+ attr = attr << 1;
+ if (reg & 0x04)
+ attr = attr >> 1;
+ }
+ /* set write bit */
+ len2 = ((attr << 4) | 0x10) | len;
+ buf = 0x80;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* set multi register length */
+ nxt200x_writebytes(state, 0x34, &len2, 1);
+
+ /* toggle the multireg write bit */
+ nxt200x_writebytes(state, 0x21, &buf, 1);
+
+ nxt200x_readbytes(state, 0x21, &buf, 1);
+
+ switch (state->demod_chip) {
+ case NXT2002:
+ if ((buf & 0x02) == 0)
+ return 0;
+ break;
+ case NXT2004:
+ if (buf == 0)
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ pr_warn("Error writing multireg register 0x%02X\n", reg);
+
+ return 0;
+}
+
+static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len)
+{
+ int i;
+ u8 buf, len2, attr;
+ dprintk("%s\n", __func__);
+
+ /* set mutli register register */
+ nxt200x_writebytes(state, 0x35, &reg, 1);
+
+ switch (state->demod_chip) {
+ case NXT2002:
+ /* set multi register length */
+ len2 = len & 0x80;
+ nxt200x_writebytes(state, 0x34, &len2, 1);
+
+ /* read the actual data */
+ nxt200x_readbytes(state, reg, data, len);
+ return 0;
+ break;
+ case NXT2004:
+ /* probably not right, but gives correct values */
+ attr = 0x02;
+ if (reg & 0x80) {
+ attr = attr << 1;
+ if (reg & 0x04)
+ attr = attr >> 1;
+ }
+
+ /* set multi register length */
+ len2 = (attr << 4) | len;
+ nxt200x_writebytes(state, 0x34, &len2, 1);
+
+ /* toggle the multireg bit*/
+ buf = 0x80;
+ nxt200x_writebytes(state, 0x21, &buf, 1);
+
+ /* read the actual data */
+ for(i = 0; i < len; i++) {
+ nxt200x_readbytes(state, 0x36 + i, &data[i], 1);
+ }
+ return 0;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+}
+
+static void nxt200x_microcontroller_stop (struct nxt200x_state* state)
+{
+ u8 buf, stopval, counter = 0;
+ dprintk("%s\n", __func__);
+
+ /* set correct stop value */
+ switch (state->demod_chip) {
+ case NXT2002:
+ stopval = 0x40;
+ break;
+ case NXT2004:
+ stopval = 0x10;
+ break;
+ default:
+ stopval = 0;
+ break;
+ }
+
+ buf = 0x80;
+ nxt200x_writebytes(state, 0x22, &buf, 1);
+
+ while (counter < 20) {
+ nxt200x_readbytes(state, 0x31, &buf, 1);
+ if (buf & stopval)
+ return;
+ msleep(10);
+ counter++;
+ }
+
+ pr_warn("Timeout waiting for nxt200x to stop. This is ok after "
+ "firmware upload.\n");
+ return;
+}
+
+static void nxt200x_microcontroller_start (struct nxt200x_state* state)
+{
+ u8 buf;
+ dprintk("%s\n", __func__);
+
+ buf = 0x00;
+ nxt200x_writebytes(state, 0x22, &buf, 1);
+}
+
+static void nxt2004_microcontroller_init (struct nxt200x_state* state)
+{
+ u8 buf[9];
+ u8 counter = 0;
+ dprintk("%s\n", __func__);
+
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x2b, buf, 1);
+ buf[0] = 0x70;
+ nxt200x_writebytes(state, 0x34, buf, 1);
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x35, buf, 1);
+ buf[0] = 0x01; buf[1] = 0x23; buf[2] = 0x45; buf[3] = 0x67; buf[4] = 0x89;
+ buf[5] = 0xAB; buf[6] = 0xCD; buf[7] = 0xEF; buf[8] = 0xC0;
+ nxt200x_writebytes(state, 0x36, buf, 9);
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x21, buf, 1);
+
+ while (counter < 20) {
+ nxt200x_readbytes(state, 0x21, buf, 1);
+ if (buf[0] == 0)
+ return;
+ msleep(10);
+ counter++;
+ }
+
+ pr_warn("Timeout waiting for nxt2004 to init.\n");
+
+ return;
+}
+
+static int nxt200x_writetuner (struct nxt200x_state* state, u8* data)
+{
+ u8 buf, count = 0;
+
+ dprintk("%s\n", __func__);
+
+ dprintk("Tuner Bytes: %*ph\n", 4, data + 1);
+
+ /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip.
+ * direct write is required for Philips TUV1236D and ALPS TDHU2 */
+ switch (state->demod_chip) {
+ case NXT2004:
+ if (i2c_writebytes(state, data[0], data+1, 4))
+ pr_warn("error writing to tuner\n");
+ /* wait until we have a lock */
+ while (count < 20) {
+ i2c_readbytes(state, data[0], &buf, 1);
+ if (buf & 0x40)
+ return 0;
+ msleep(100);
+ count++;
+ }
+ pr_warn("timeout waiting for tuner lock\n");
+ break;
+ case NXT2002:
+ /* set the i2c transfer speed to the tuner */
+ buf = 0x03;
+ nxt200x_writebytes(state, 0x20, &buf, 1);
+
+ /* setup to transfer 4 bytes via i2c */
+ buf = 0x04;
+ nxt200x_writebytes(state, 0x34, &buf, 1);
+
+ /* write actual tuner bytes */
+ nxt200x_writebytes(state, 0x36, data+1, 4);
+
+ /* set tuner i2c address */
+ buf = data[0] << 1;
+ nxt200x_writebytes(state, 0x35, &buf, 1);
+
+ /* write UC Opmode to begin transfer */
+ buf = 0x80;
+ nxt200x_writebytes(state, 0x21, &buf, 1);
+
+ while (count < 20) {
+ nxt200x_readbytes(state, 0x21, &buf, 1);
+ if ((buf & 0x80)== 0x00)
+ return 0;
+ msleep(100);
+ count++;
+ }
+ pr_warn("timeout error writing to tuner\n");
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return 0;
+}
+
+static void nxt200x_agc_reset(struct nxt200x_state* state)
+{
+ u8 buf;
+ dprintk("%s\n", __func__);
+
+ switch (state->demod_chip) {
+ case NXT2002:
+ buf = 0x08;
+ nxt200x_writebytes(state, 0x08, &buf, 1);
+ buf = 0x00;
+ nxt200x_writebytes(state, 0x08, &buf, 1);
+ break;
+ case NXT2004:
+ nxt200x_readreg_multibyte(state, 0x08, &buf, 1);
+ buf = 0x08;
+ nxt200x_writereg_multibyte(state, 0x08, &buf, 1);
+ buf = 0x00;
+ nxt200x_writereg_multibyte(state, 0x08, &buf, 1);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 buf[3], written = 0, chunkpos = 0;
+ u16 rambase, position, crc = 0;
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes\n", fw->size);
+
+ /* Get the RAM base for this nxt2002 */
+ nxt200x_readbytes(state, 0x10, buf, 1);
+
+ if (buf[0] & 0x10)
+ rambase = 0x1000;
+ else
+ rambase = 0x0000;
+
+ dprintk("rambase on this nxt2002 is %04X\n", rambase);
+
+ /* Hold the micro in reset while loading firmware */
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x2B, buf, 1);
+
+ for (position = 0; position < fw->size; position++) {
+ if (written == 0) {
+ crc = 0;
+ chunkpos = 0x28;
+ buf[0] = ((rambase + position) >> 8);
+ buf[1] = (rambase + position) & 0xFF;
+ buf[2] = 0x81;
+ /* write starting address */
+ nxt200x_writebytes(state, 0x29, buf, 3);
+ }
+ written++;
+ chunkpos++;
+
+ if ((written % 4) == 0)
+ nxt200x_writebytes(state, chunkpos, &fw->data[position-3], 4);
+
+ crc = nxt200x_crc(crc, fw->data[position]);
+
+ if ((written == 255) || (position+1 == fw->size)) {
+ /* write remaining bytes of firmware */
+ nxt200x_writebytes(state, chunkpos+4-(written %4),
+ &fw->data[position-(written %4) + 1],
+ written %4);
+ buf[0] = crc << 8;
+ buf[1] = crc & 0xFF;
+
+ /* write crc */
+ nxt200x_writebytes(state, 0x2C, buf, 2);
+
+ /* do a read to stop things */
+ nxt200x_readbytes(state, 0x2A, buf, 1);
+
+ /* set transfer mode to complete */
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x2B, buf, 1);
+
+ written = 0;
+ }
+ }
+
+ return 0;
+};
+
+static int nxt2004_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
+{
+
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 buf[3];
+ u16 rambase, position, crc=0;
+
+ dprintk("%s\n", __func__);
+ dprintk("Firmware is %zu bytes\n", fw->size);
+
+ /* set rambase */
+ rambase = 0x1000;
+
+ /* hold the micro in reset while loading firmware */
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x2B, buf,1);
+
+ /* calculate firmware CRC */
+ for (position = 0; position < fw->size; position++) {
+ crc = nxt200x_crc(crc, fw->data[position]);
+ }
+
+ buf[0] = rambase >> 8;
+ buf[1] = rambase & 0xFF;
+ buf[2] = 0x81;
+ /* write starting address */
+ nxt200x_writebytes(state,0x29,buf,3);
+
+ for (position = 0; position < fw->size;) {
+ nxt200x_writebytes(state, 0x2C, &fw->data[position],
+ fw->size-position > 255 ? 255 : fw->size-position);
+ position += (fw->size-position > 255 ? 255 : fw->size-position);
+ }
+ buf[0] = crc >> 8;
+ buf[1] = crc & 0xFF;
+
+ dprintk("firmware crc is 0x%02X 0x%02X\n", buf[0], buf[1]);
+
+ /* write crc */
+ nxt200x_writebytes(state, 0x2C, buf,2);
+
+ /* do a read to stop things */
+ nxt200x_readbytes(state, 0x2C, buf, 1);
+
+ /* set transfer mode to complete */
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x2B, buf,1);
+
+ return 0;
+};
+
+static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 buf[5];
+
+ /* stop the micro first */
+ nxt200x_microcontroller_stop(state);
+
+ if (state->demod_chip == NXT2004) {
+ /* make sure demod is set to digital */
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x14, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x17, buf, 1);
+ }
+
+ /* set additional params */
+ switch (p->modulation) {
+ case QAM_64:
+ case QAM_256:
+ /* Set punctured clock for QAM */
+ /* This is just a guess since I am unable to test it */
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 1);
+ break;
+ case VSB_8:
+ /* Set non-punctured clock for VSB */
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, 0);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ if (fe->ops.tuner_ops.calc_regs) {
+ /* get tuning information */
+ fe->ops.tuner_ops.calc_regs(fe, buf, 5);
+
+ /* write frequency information */
+ nxt200x_writetuner(state, buf);
+ }
+
+ /* reset the agc now that tuning has been completed */
+ nxt200x_agc_reset(state);
+
+ /* set target power level */
+ switch (p->modulation) {
+ case QAM_64:
+ case QAM_256:
+ buf[0] = 0x74;
+ break;
+ case VSB_8:
+ buf[0] = 0x70;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ nxt200x_writebytes(state, 0x42, buf, 1);
+
+ /* configure sdm */
+ switch (state->demod_chip) {
+ case NXT2002:
+ buf[0] = 0x87;
+ break;
+ case NXT2004:
+ buf[0] = 0x07;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ nxt200x_writebytes(state, 0x57, buf, 1);
+
+ /* write sdm1 input */
+ buf[0] = 0x10;
+ buf[1] = 0x00;
+ switch (state->demod_chip) {
+ case NXT2002:
+ nxt200x_writereg_multibyte(state, 0x58, buf, 2);
+ break;
+ case NXT2004:
+ nxt200x_writebytes(state, 0x58, buf, 2);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* write sdmx input */
+ switch (p->modulation) {
+ case QAM_64:
+ buf[0] = 0x68;
+ break;
+ case QAM_256:
+ buf[0] = 0x64;
+ break;
+ case VSB_8:
+ buf[0] = 0x60;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ buf[1] = 0x00;
+ switch (state->demod_chip) {
+ case NXT2002:
+ nxt200x_writereg_multibyte(state, 0x5C, buf, 2);
+ break;
+ case NXT2004:
+ nxt200x_writebytes(state, 0x5C, buf, 2);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* write adc power lpf fc */
+ buf[0] = 0x05;
+ nxt200x_writebytes(state, 0x43, buf, 1);
+
+ if (state->demod_chip == NXT2004) {
+ /* write ??? */
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ nxt200x_writebytes(state, 0x46, buf, 2);
+ }
+
+ /* write accumulator2 input */
+ buf[0] = 0x80;
+ buf[1] = 0x00;
+ switch (state->demod_chip) {
+ case NXT2002:
+ nxt200x_writereg_multibyte(state, 0x4B, buf, 2);
+ break;
+ case NXT2004:
+ nxt200x_writebytes(state, 0x4B, buf, 2);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* write kg1 */
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x4D, buf, 1);
+
+ /* write sdm12 lpf fc */
+ buf[0] = 0x44;
+ nxt200x_writebytes(state, 0x55, buf, 1);
+
+ /* write agc control reg */
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x41, buf, 1);
+
+ if (state->demod_chip == NXT2004) {
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x24;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ /* soft reset? */
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x10;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x04;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x81, buf, 1);
+ buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x82, buf, 3);
+ nxt200x_readreg_multibyte(state, 0x88, buf, 1);
+ buf[0] = 0x11;
+ nxt200x_writereg_multibyte(state, 0x88, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x44;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+ }
+
+ /* write agc ucgp0 */
+ switch (p->modulation) {
+ case QAM_64:
+ buf[0] = 0x02;
+ break;
+ case QAM_256:
+ buf[0] = 0x03;
+ break;
+ case VSB_8:
+ buf[0] = 0x00;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ nxt200x_writebytes(state, 0x30, buf, 1);
+
+ /* write agc control reg */
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x41, buf, 1);
+
+ /* write accumulator2 input */
+ buf[0] = 0x80;
+ buf[1] = 0x00;
+ switch (state->demod_chip) {
+ case NXT2002:
+ nxt200x_writereg_multibyte(state, 0x49, buf, 2);
+ nxt200x_writereg_multibyte(state, 0x4B, buf, 2);
+ break;
+ case NXT2004:
+ nxt200x_writebytes(state, 0x49, buf, 2);
+ nxt200x_writebytes(state, 0x4B, buf, 2);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+
+ /* write agc control reg */
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x41, buf, 1);
+
+ nxt200x_microcontroller_start(state);
+
+ if (state->demod_chip == NXT2004) {
+ nxt2004_microcontroller_init(state);
+
+ /* ???? */
+ buf[0] = 0xF0;
+ buf[1] = 0x00;
+ nxt200x_writebytes(state, 0x5C, buf, 2);
+ }
+
+ /* adjacent channel detection should be done here, but I don't
+ have any stations with this need so I cannot test it */
+
+ return 0;
+}
+
+static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 lock;
+ nxt200x_readbytes(state, 0x31, &lock, 1);
+
+ *status = 0;
+ if (lock & 0x20) {
+ *status |= FE_HAS_SIGNAL;
+ *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_VITERBI;
+ *status |= FE_HAS_SYNC;
+ *status |= FE_HAS_LOCK;
+ }
+ return 0;
+}
+
+static int nxt200x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 b[3];
+
+ nxt200x_readreg_multibyte(state, 0xE6, b, 3);
+
+ *ber = ((b[0] << 8) + b[1]) * 8;
+
+ return 0;
+}
+
+static int nxt200x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 b[2];
+ u16 temp = 0;
+
+ /* setup to read cluster variance */
+ b[0] = 0x00;
+ nxt200x_writebytes(state, 0xA1, b, 1);
+
+ /* get multreg val */
+ nxt200x_readreg_multibyte(state, 0xA6, b, 2);
+
+ temp = (b[0] << 8) | b[1];
+ *strength = ((0x7FFF - temp) & 0x0FFF) * 16;
+
+ return 0;
+}
+
+static int nxt200x_read_snr(struct dvb_frontend* fe, u16* snr)
+{
+
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 b[2];
+ u16 temp = 0, temp2;
+ u32 snrdb = 0;
+
+ /* setup to read cluster variance */
+ b[0] = 0x00;
+ nxt200x_writebytes(state, 0xA1, b, 1);
+
+ /* get multreg val from 0xA6 */
+ nxt200x_readreg_multibyte(state, 0xA6, b, 2);
+
+ temp = (b[0] << 8) | b[1];
+ temp2 = 0x7FFF - temp;
+
+ /* snr will be in db */
+ if (temp2 > 0x7F00)
+ snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) );
+ else if (temp2 > 0x7EC0)
+ snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) );
+ else if (temp2 > 0x7C00)
+ snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) );
+ else
+ snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) );
+
+ /* the value reported back from the frontend will be FFFF=32db 0000=0db */
+ *snr = snrdb * (0xFFFF/32000);
+
+ return 0;
+}
+
+static int nxt200x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ u8 b[3];
+
+ nxt200x_readreg_multibyte(state, 0xE6, b, 3);
+ *ucblocks = b[2];
+
+ return 0;
+}
+
+static int nxt200x_sleep(struct dvb_frontend* fe)
+{
+ return 0;
+}
+
+static int nxt2002_init(struct dvb_frontend* fe)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret;
+ u8 buf[2];
+
+ /* request the firmware, this will block until someone uploads it */
+ pr_debug("%s: Waiting for firmware upload (%s)...\n",
+ __func__, NXT2002_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ pr_err("%s: No firmware uploaded (timeout or file not found?)"
+ "\n", __func__);
+ return ret;
+ }
+
+ ret = nxt2002_load_firmware(fe, fw);
+ release_firmware(fw);
+ if (ret) {
+ pr_err("%s: Writing firmware to device failed\n", __func__);
+ return ret;
+ }
+ pr_info("%s: Firmware upload complete\n", __func__);
+
+ /* Put the micro into reset */
+ nxt200x_microcontroller_stop(state);
+
+ /* ensure transfer is complete */
+ buf[0]=0x00;
+ nxt200x_writebytes(state, 0x2B, buf, 1);
+
+ /* Put the micro into reset for real this time */
+ nxt200x_microcontroller_stop(state);
+
+ /* soft reset everything (agc,frontend,eq,fec)*/
+ buf[0] = 0x0F;
+ nxt200x_writebytes(state, 0x08, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x08, buf, 1);
+
+ /* write agc sdm configure */
+ buf[0] = 0xF1;
+ nxt200x_writebytes(state, 0x57, buf, 1);
+
+ /* write mod output format */
+ buf[0] = 0x20;
+ nxt200x_writebytes(state, 0x09, buf, 1);
+
+ /* write fec mpeg mode */
+ buf[0] = 0x7E;
+ buf[1] = 0x00;
+ nxt200x_writebytes(state, 0xE9, buf, 2);
+
+ /* write mux selection */
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0xCC, buf, 1);
+
+ return 0;
+}
+
+static int nxt2004_init(struct dvb_frontend* fe)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ const struct firmware *fw;
+ int ret;
+ u8 buf[3];
+
+ /* ??? */
+ buf[0]=0x00;
+ nxt200x_writebytes(state, 0x1E, buf, 1);
+
+ /* request the firmware, this will block until someone uploads it */
+ pr_debug("%s: Waiting for firmware upload (%s)...\n",
+ __func__, NXT2004_DEFAULT_FIRMWARE);
+ ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE,
+ state->i2c->dev.parent);
+ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__);
+ if (ret) {
+ pr_err("%s: No firmware uploaded (timeout or file not found?)"
+ "\n", __func__);
+ return ret;
+ }
+
+ ret = nxt2004_load_firmware(fe, fw);
+ release_firmware(fw);
+ if (ret) {
+ pr_err("%s: Writing firmware to device failed\n", __func__);
+ return ret;
+ }
+ pr_info("%s: Firmware upload complete\n", __func__);
+
+ /* ensure transfer is complete */
+ buf[0] = 0x01;
+ nxt200x_writebytes(state, 0x19, buf, 1);
+
+ nxt2004_microcontroller_init(state);
+ nxt200x_microcontroller_stop(state);
+ nxt200x_microcontroller_stop(state);
+ nxt2004_microcontroller_init(state);
+ nxt200x_microcontroller_stop(state);
+
+ /* soft reset everything (agc,frontend,eq,fec)*/
+ buf[0] = 0xFF;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+
+ /* write agc sdm configure */
+ buf[0] = 0xD7;
+ nxt200x_writebytes(state, 0x57, buf, 1);
+
+ /* ???*/
+ buf[0] = 0x07;
+ buf[1] = 0xfe;
+ nxt200x_writebytes(state, 0x35, buf, 2);
+ buf[0] = 0x12;
+ nxt200x_writebytes(state, 0x34, buf, 1);
+ buf[0] = 0x80;
+ nxt200x_writebytes(state, 0x21, buf, 1);
+
+ /* ???*/
+ buf[0] = 0x21;
+ nxt200x_writebytes(state, 0x0A, buf, 1);
+
+ /* ???*/
+ buf[0] = 0x01;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ /* write fec mpeg mode */
+ buf[0] = 0x7E;
+ buf[1] = 0x00;
+ nxt200x_writebytes(state, 0xE9, buf, 2);
+
+ /* write mux selection */
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0xCC, buf, 1);
+
+ /* ???*/
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ /* soft reset? */
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x10;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+
+ /* ???*/
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x01;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x70;
+ nxt200x_writereg_multibyte(state, 0x81, buf, 1);
+ buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66;
+ nxt200x_writereg_multibyte(state, 0x82, buf, 3);
+
+ nxt200x_readreg_multibyte(state, 0x88, buf, 1);
+ buf[0] = 0x11;
+ nxt200x_writereg_multibyte(state, 0x88, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x40;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ nxt200x_readbytes(state, 0x10, buf, 1);
+ buf[0] = 0x10;
+ nxt200x_writebytes(state, 0x10, buf, 1);
+ nxt200x_readbytes(state, 0x0A, buf, 1);
+ buf[0] = 0x21;
+ nxt200x_writebytes(state, 0x0A, buf, 1);
+
+ nxt2004_microcontroller_init(state);
+
+ buf[0] = 0x21;
+ nxt200x_writebytes(state, 0x0A, buf, 1);
+ buf[0] = 0x7E;
+ nxt200x_writebytes(state, 0xE9, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0xEA, buf, 1);
+
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ /* soft reset? */
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x10;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+ nxt200x_readreg_multibyte(state, 0x08, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x08, buf, 1);
+
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x04;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x81, buf, 1);
+ buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
+ nxt200x_writereg_multibyte(state, 0x82, buf, 3);
+
+ nxt200x_readreg_multibyte(state, 0x88, buf, 1);
+ buf[0] = 0x11;
+ nxt200x_writereg_multibyte(state, 0x88, buf, 1);
+
+ nxt200x_readreg_multibyte(state, 0x80, buf, 1);
+ buf[0] = 0x44;
+ nxt200x_writereg_multibyte(state, 0x80, buf, 1);
+
+ /* initialize tuner */
+ nxt200x_readbytes(state, 0x10, buf, 1);
+ buf[0] = 0x12;
+ nxt200x_writebytes(state, 0x10, buf, 1);
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x13, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x16, buf, 1);
+ buf[0] = 0x04;
+ nxt200x_writebytes(state, 0x14, buf, 1);
+ buf[0] = 0x00;
+ nxt200x_writebytes(state, 0x14, buf, 1);
+ nxt200x_writebytes(state, 0x17, buf, 1);
+ nxt200x_writebytes(state, 0x14, buf, 1);
+ nxt200x_writebytes(state, 0x17, buf, 1);
+
+ return 0;
+}
+
+static int nxt200x_init(struct dvb_frontend* fe)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ int ret = 0;
+
+ if (!state->initialised) {
+ switch (state->demod_chip) {
+ case NXT2002:
+ ret = nxt2002_init(fe);
+ break;
+ case NXT2004:
+ ret = nxt2004_init(fe);
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ state->initialised = 1;
+ }
+ return ret;
+}
+
+static int nxt200x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 500;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void nxt200x_release(struct dvb_frontend* fe)
+{
+ struct nxt200x_state* state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops nxt200x_ops;
+
+struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct nxt200x_state* state = NULL;
+ u8 buf [] = {0,0,0,0,0};
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct nxt200x_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->initialised = 0;
+
+ /* read card id */
+ nxt200x_readbytes(state, 0x00, buf, 5);
+ dprintk("NXT info: %*ph\n", 5, buf);
+
+ /* set demod chip */
+ switch (buf[0]) {
+ case 0x04:
+ state->demod_chip = NXT2002;
+ pr_info("NXT2002 Detected\n");
+ break;
+ case 0x05:
+ state->demod_chip = NXT2004;
+ pr_info("NXT2004 Detected\n");
+ break;
+ default:
+ goto error;
+ }
+
+ /* make sure demod chip is supported */
+ switch (state->demod_chip) {
+ case NXT2002:
+ if (buf[0] != 0x04) goto error; /* device id */
+ if (buf[1] != 0x02) goto error; /* fab id */
+ if (buf[2] != 0x11) goto error; /* month */
+ if (buf[3] != 0x20) goto error; /* year msb */
+ if (buf[4] != 0x00) goto error; /* year lsb */
+ break;
+ case NXT2004:
+ if (buf[0] != 0x05) goto error; /* device id */
+ break;
+ default:
+ goto error;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &nxt200x_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+ pr_err("Unknown/Unsupported NXT chip: %*ph\n", 5, buf);
+ return NULL;
+}
+
+static struct dvb_frontend_ops nxt200x_ops = {
+ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
+ .info = {
+ .name = "Nextwave NXT200X VSB/QAM frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 860000000,
+ .frequency_stepsize = 166666, /* stepsize is just a guess */
+ .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_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256
+ },
+
+ .release = nxt200x_release,
+
+ .init = nxt200x_init,
+ .sleep = nxt200x_sleep,
+
+ .set_frontend = nxt200x_setup_frontend_parameters,
+ .get_tune_settings = nxt200x_get_tune_settings,
+
+ .read_status = nxt200x_read_status,
+ .read_ber = nxt200x_read_ber,
+ .read_signal_strength = nxt200x_read_signal_strength,
+ .read_snr = nxt200x_read_snr,
+ .read_ucblocks = nxt200x_read_ucblocks,
+};
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
+MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(nxt200x_attach);
+
diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h
index f3c84583770f..f3c84583770f 100644
--- a/drivers/media/dvb/frontends/nxt200x.h
+++ b/drivers/media/dvb-frontends/nxt200x.h
diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c
index 90ae6c72c0e3..90ae6c72c0e3 100644
--- a/drivers/media/dvb/frontends/nxt6000.c
+++ b/drivers/media/dvb-frontends/nxt6000.c
diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h
index 878eb38a075e..878eb38a075e 100644
--- a/drivers/media/dvb/frontends/nxt6000.h
+++ b/drivers/media/dvb-frontends/nxt6000.h
diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb-frontends/nxt6000_priv.h
index 0422e580038a..0422e580038a 100644
--- a/drivers/media/dvb/frontends/nxt6000_priv.h
+++ b/drivers/media/dvb-frontends/nxt6000_priv.h
diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c
index 5ef921823c15..5ef921823c15 100644
--- a/drivers/media/dvb/frontends/or51132.c
+++ b/drivers/media/dvb-frontends/or51132.c
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h
index 1b8e04d973c8..1b8e04d973c8 100644
--- a/drivers/media/dvb/frontends/or51132.h
+++ b/drivers/media/dvb-frontends/or51132.h
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c
index c625b57b4333..c625b57b4333 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb-frontends/or51211.c
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h
index 3ce0508b898e..3ce0508b898e 100644
--- a/drivers/media/dvb/frontends/or51211.h
+++ b/drivers/media/dvb-frontends/or51211.h
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
new file mode 100644
index 000000000000..b0f6ec03d1eb
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -0,0 +1,761 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ */
+
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * have unusual I2C-gate control which closes gate automatically after each
+ * I2C transfer. Using own I2C adapter we can workaround that.
+ */
+
+#include "rtl2830_priv.h"
+
+/* write multiple hardware registers */
+static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, const u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, const u8 *val,
+ int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_wr(priv, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_rd(priv, reg2, val, len);
+}
+
+/* read single register */
+static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
+{
+ return rtl2830_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int rtl2830_init(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ struct rtl2830_reg_val_mask tab[] = {
+ { 0x00d, 0x01, 0x03 },
+ { 0x00d, 0x10, 0x10 },
+ { 0x104, 0x00, 0x1e },
+ { 0x105, 0x80, 0x80 },
+ { 0x110, 0x02, 0x03 },
+ { 0x110, 0x08, 0x0c },
+ { 0x17b, 0x00, 0x40 },
+ { 0x17d, 0x05, 0x0f },
+ { 0x17d, 0x50, 0xf0 },
+ { 0x18c, 0x08, 0x0f },
+ { 0x18d, 0x00, 0xc0 },
+ { 0x188, 0x05, 0x0f },
+ { 0x189, 0x00, 0xfc },
+ { 0x2d5, 0x02, 0x02 },
+ { 0x2f1, 0x02, 0x06 },
+ { 0x2f1, 0x20, 0xf8 },
+ { 0x16d, 0x00, 0x01 },
+ { 0x1a6, 0x00, 0x80 },
+ { 0x106, priv->cfg.vtop, 0x3f },
+ { 0x107, priv->cfg.krf, 0x3f },
+ { 0x112, 0x28, 0xff },
+ { 0x103, priv->cfg.agc_targ_val, 0xff },
+ { 0x00a, 0x02, 0x07 },
+ { 0x140, 0x0c, 0x3c },
+ { 0x140, 0x40, 0xc0 },
+ { 0x15b, 0x05, 0x07 },
+ { 0x15b, 0x28, 0x38 },
+ { 0x15c, 0x05, 0x07 },
+ { 0x15c, 0x28, 0x38 },
+ { 0x115, priv->cfg.spec_inv, 0x01 },
+ { 0x16f, 0x01, 0x07 },
+ { 0x170, 0x18, 0x38 },
+ { 0x172, 0x0f, 0x0f },
+ { 0x173, 0x08, 0x38 },
+ { 0x175, 0x01, 0x07 },
+ { 0x176, 0x00, 0xc0 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto err;
+ }
+
+ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x195,
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+ if (ret)
+ goto err;
+
+ /* TODO: spec init */
+
+ /* soft reset */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
+
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u64 num;
+ u8 buf[3], tmp;
+ u32 if_ctl, if_frequency;
+ static const u8 bw_params1[3][34] = {
+ {
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
+ }, {
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
+ }, {
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
+ },
+ };
+ static const u8 bw_params2[3][6] = {
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30}, /* 6 MHz */
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98}, /* 7 MHz */
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */
+ };
+
+ dev_dbg(&priv->i2c->dev,
+ "%s: frequency=%d bandwidth_hz=%d inversion=%d\n",
+ __func__, c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ break;
+ case 7000000:
+ i = 1;
+ break;
+ case 8000000:
+ i = 2;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
+ if (ret)
+ goto err;
+
+ /* program if frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ ret = -EINVAL;
+
+ if (ret < 0)
+ goto err;
+
+ num = if_frequency % priv->cfg.xtal;
+ num *= 0x400000;
+ num = div_u64(num, priv->cfg.xtal);
+ num = -num;
+ if_ctl = num & 0x3fffff;
+ dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d if_ctl=%08x\n",
+ __func__, if_frequency, if_ctl);
+
+ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
+ if (ret)
+ goto err;
+
+ buf[0] = tmp << 6;
+ buf[0] |= (if_ctl >> 16) & 0x3f;
+ buf[1] = (if_ctl >> 8) & 0xff;
+ buf[2] = (if_ctl >> 0) & 0xff;
+
+ ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* 1/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
+ if (ret)
+ goto err;
+
+ /* 2/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_get_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[3];
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x33c, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_rd_reg(priv, 0x351, &buf[2]);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf);
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[2] >> 2) & 1) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[2] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, hierarchy, constellation;
+ u8 buf[2], tmp;
+ u16 tmp16;
+#define CONSTELLATION_NUM 3
+#define HIERARCHY_NUM 4
+ static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+ { 70705899, 70705899, 70705899, 70705899 },
+ { 82433173, 82433173, 87483115, 94445660 },
+ { 92888734, 92888734, 95487525, 99770748 },
+ };
+
+ if (priv->sleeping)
+ return 0;
+
+ /* reports SNR in resolution of 0.1 dB */
+
+ ret = rtl2830_rd_reg(priv, 0x33c, &tmp);
+ if (ret)
+ goto err;
+
+ constellation = (tmp >> 2) & 0x03; /* [3:2] */
+ if (constellation > CONSTELLATION_NUM - 1)
+ goto err;
+
+ hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
+ if (hierarchy > HIERARCHY_NUM - 1)
+ goto err;
+
+ ret = rtl2830_rd_regs(priv, 0x40c, buf, 2);
+ if (ret)
+ goto err;
+
+ tmp16 = buf[0] << 8 | buf[1];
+
+ if (tmp16)
+ *snr = (snr_constant[constellation][hierarchy] -
+ intlog10(tmp16)) / ((1 << 24) / 100);
+ else
+ *snr = 0;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x34e, buf, 2);
+ if (ret)
+ goto err;
+
+ *ber = buf[0] << 8 | buf[1];
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 if_agc_raw, if_agc;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x359, buf, 2);
+ if (ret)
+ goto err;
+
+ if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff;
+
+ if (if_agc_raw & (1 << 9))
+ if_agc = -(~(if_agc_raw - 1) & 0x1ff);
+ else
+ if_agc = if_agc_raw;
+
+ *strength = (u8) (55 - if_agc / 182);
+ *strength |= *strength << 8;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct dvb_frontend_ops rtl2830_ops;
+
+static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
+ int ret;
+
+ /* open i2c-gate */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ ret = i2c_transfer(priv->i2c, msg, num);
+ if (ret < 0)
+ dev_warn(&priv->i2c->dev, "%s: tuner i2c failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
+ .master_xfer = rtl2830_tuner_i2c_xfer,
+ .functionality = rtl2830_tuner_i2c_func,
+};
+
+struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ return &priv->tuner_i2c_adapter;
+}
+EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
+
+static void rtl2830_release(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+
+ i2c_del_adapter(&priv->tuner_i2c_adapter);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2830_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
+
+ /* check if the demod is there */
+ ret = rtl2830_rd_reg(priv, 0x000, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* create tuner i2c adapter */
+ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
+ sizeof(priv->tuner_i2c_adapter.name));
+ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
+ priv->tuner_i2c_adapter.algo_data = NULL;
+ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
+ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
+ dev_err(&i2c->dev,
+ "%s: tuner i2c bus could not be initialized\n",
+ KBUILD_MODNAME);
+ goto err;
+ }
+
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2830_attach);
+
+static struct dvb_frontend_ops rtl2830_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2830 (DVB-T)",
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2830_release,
+
+ .init = rtl2830_init,
+ .sleep = rtl2830_sleep,
+
+ .get_tune_settings = rtl2830_get_tune_settings,
+
+ .set_frontend = rtl2830_set_frontend,
+ .get_frontend = rtl2830_get_frontend,
+
+ .read_status = rtl2830_read_status,
+ .read_snr = rtl2830_read_snr,
+ .read_ber = rtl2830_read_ber,
+ .read_ucblocks = rtl2830_read_ucblocks,
+ .read_signal_strength = rtl2830_read_signal_strength,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h
new file mode 100644
index 000000000000..f4349a1fc03e
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830.h
@@ -0,0 +1,90 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_H
+#define RTL2830_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2830_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * TS output mode.
+ */
+ u8 ts_mode;
+
+ /*
+ * Spectrum inversion.
+ */
+ bool spec_inv;
+
+ /*
+ */
+ u8 vtop;
+
+ /*
+ */
+ u8 krf;
+
+ /*
+ */
+ u8 agc_targ_val;
+};
+
+#if defined(CONFIG_DVB_RTL2830) || \
+ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+#endif
+
+#endif /* RTL2830_H */
diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h
new file mode 100644
index 000000000000..fab10ecb3c3b
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2830_priv.h
@@ -0,0 +1,45 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_PRIV_H
+#define RTL2830_PRIV_H
+
+#include "dvb_frontend.h"
+#include "dvb_math.h"
+#include "rtl2830.h"
+
+struct rtl2830_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2830_config cfg;
+ struct i2c_adapter tuner_i2c_adapter;
+
+ bool sleeping;
+
+ u8 page; /* active register page */
+};
+
+struct rtl2830_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
+#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
new file mode 100644
index 000000000000..80c8e5f1182f
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -0,0 +1,932 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl2832_priv.h"
+#include "dvb_math.h"
+#include <linux/bitops.h>
+
+int rtl2832_debug;
+module_param_named(debug, rtl2832_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+#define REG_MASK(b) (BIT(b + 1) - 1)
+
+static const struct rtl2832_reg_entry registers[] = {
+ [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2},
+ [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3},
+ [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2},
+ [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0},
+ [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7},
+ [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7},
+ [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6},
+ [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0},
+ [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0},
+ [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0},
+ [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0},
+ [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0},
+ [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0},
+ [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0},
+ [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0},
+ [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0},
+ [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4},
+ [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0},
+ [DVBT_REG_PI] = {0x0, 0xa, 2, 0},
+ [DVBT_PIP_ON] = {0x0, 0x21, 3, 3},
+ [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0},
+ [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0},
+ [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0},
+ [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0},
+ [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0},
+ [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0},
+ [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0},
+ [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0},
+ [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0},
+ [DVBT_KB_P1] = {0x1, 0x64, 3, 1},
+ [DVBT_KB_P2] = {0x1, 0x64, 6, 4},
+ [DVBT_KB_P3] = {0x1, 0x65, 2, 0},
+ [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4},
+ [DVBT_AD_AVI] = {0x0, 0x9, 1, 0},
+ [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2},
+ [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4},
+ [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0},
+ [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3},
+ [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0},
+ [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3},
+ [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0},
+ [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6},
+ [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0},
+ [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0},
+ [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2},
+ [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4},
+ [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3},
+ [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2},
+ [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4},
+ [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0},
+ [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3},
+ [DVBT_GI_IDX] = {0x3, 0x51, 1, 0},
+ [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2},
+ [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0},
+ [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0},
+ [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0},
+ [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0},
+ [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0},
+ [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0},
+ [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0},
+ [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1},
+ [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0},
+ [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5},
+ [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6},
+ [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7},
+ [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0},
+ [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0},
+ [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0},
+ [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0},
+ [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6},
+ [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0},
+ [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6},
+ [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0},
+ [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0},
+ [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0},
+ [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0},
+ [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1},
+ [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1},
+ [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7},
+ [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0},
+ [DVBT_VTOP1] = {0x1, 0x6, 5, 0},
+ [DVBT_VTOP2] = {0x1, 0xc9, 5, 0},
+ [DVBT_VTOP3] = {0x1, 0xca, 5, 0},
+ [DVBT_KRF1] = {0x1, 0xcb, 7, 0},
+ [DVBT_KRF2] = {0x1, 0x7, 7, 0},
+ [DVBT_KRF3] = {0x1, 0xcd, 7, 0},
+ [DVBT_KRF4] = {0x1, 0xce, 7, 0},
+ [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0},
+ [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0},
+ [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0},
+ [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0},
+ [DVBT_THD_DW1] = {0x1, 0xde, 7, 0},
+ [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0},
+ [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3},
+ [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0},
+ [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5},
+ [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6},
+ [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7},
+ [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0},
+ [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1},
+ [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2},
+ [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3},
+ [DVBT_SERIAL] = {0x1, 0x7c, 4, 4},
+ [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5},
+ [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0},
+ [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4},
+ [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7},
+ [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6},
+ [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4},
+ [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3},
+ [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2},
+ [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1},
+ [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0},
+ [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4},
+ [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3},
+ [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2},
+ [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1},
+ [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0},
+ [DVBT_SM_PASS] = {0x1, 0x93, 11, 0},
+ [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0},
+ [DVBT_RSSI_R] = {0x3, 0x1, 6, 0},
+ [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0},
+ [DVBT_REG_MON] = {0x0, 0xd, 1, 0},
+ [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2},
+ [DVBT_REG_GPE] = {0x0, 0xd, 7, 7},
+ [DVBT_REG_GPO] = {0x0, 0x10, 0, 0},
+ [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0},
+};
+
+/* write multiple hardware registers */
+static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+ int len)
+{
+ int ret;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2832_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+}
+
+return rtl2832_wr(priv, reg, val, len);
+}
+
+/* read multiple registers */
+static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
+ int len)
+{
+ int ret;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2832_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2832_rd(priv, reg, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
+{
+ return rtl2832_wr_regs(priv, reg, page, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
+{
+ return rtl2832_rd_regs(priv, reg, page, val, 1);
+}
+
+int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
+{
+ int ret;
+
+ u8 reg_start_addr;
+ u8 msb, lsb;
+ u8 page;
+ u8 reading[4];
+ u32 reading_tmp;
+ int i;
+
+ u8 len;
+ u32 mask;
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ page = registers[reg].page;
+
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ *val = (reading_tmp >> lsb) & mask;
+
+ return ret;
+
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+
+}
+
+int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
+{
+ int ret, i;
+ u8 len;
+ u8 reg_start_addr;
+ u8 msb, lsb;
+ u8 page;
+ u32 mask;
+
+
+ u8 reading[4];
+ u8 writing[4];
+ u32 reading_tmp;
+ u32 writing_tmp;
+
+
+ reg_start_addr = registers[reg].start_address;
+ msb = registers[reg].msb;
+ lsb = registers[reg].lsb;
+ page = registers[reg].page;
+
+ len = (msb >> 3) + 1;
+ mask = REG_MASK(msb - lsb);
+
+
+ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
+ if (ret)
+ goto err;
+
+ reading_tmp = 0;
+ for (i = 0; i < len; i++)
+ reading_tmp |= reading[i] << ((len - 1 - i) * 8);
+
+ writing_tmp = reading_tmp & ~(mask << lsb);
+ writing_tmp |= ((val & mask) << lsb);
+
+
+ for (i = 0; i < len; i++)
+ writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
+
+ ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len);
+ if (ret)
+ goto err;
+
+ return ret;
+
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+
+}
+
+static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int ret;
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable);
+
+ /* gate already open or close */
+ if (priv->i2c_gate_state == enable)
+ return 0;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0));
+ if (ret)
+ goto err;
+
+ priv->i2c_gate_state = enable;
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_init(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int i, ret, len;
+ u8 en_bbin;
+ u64 pset_iffreq;
+ const struct rtl2832_reg_value *init;
+
+ /* initialization values for the demodulator registers */
+ struct rtl2832_reg_value rtl2832_initial_regs[] = {
+ {DVBT_AD_EN_REG, 0x1},
+ {DVBT_AD_EN_REG1, 0x1},
+ {DVBT_RSD_BER_FAIL_VAL, 0x2800},
+ {DVBT_MGD_THD0, 0x10},
+ {DVBT_MGD_THD1, 0x20},
+ {DVBT_MGD_THD2, 0x20},
+ {DVBT_MGD_THD3, 0x40},
+ {DVBT_MGD_THD4, 0x22},
+ {DVBT_MGD_THD5, 0x32},
+ {DVBT_MGD_THD6, 0x37},
+ {DVBT_MGD_THD7, 0x39},
+ {DVBT_EN_BK_TRK, 0x0},
+ {DVBT_EN_CACQ_NOTCH, 0x0},
+ {DVBT_AD_AV_REF, 0x2a},
+ {DVBT_REG_PI, 0x6},
+ {DVBT_PIP_ON, 0x0},
+ {DVBT_CDIV_PH0, 0x8},
+ {DVBT_CDIV_PH1, 0x8},
+ {DVBT_SCALE1_B92, 0x4},
+ {DVBT_SCALE1_B93, 0xb0},
+ {DVBT_SCALE1_BA7, 0x78},
+ {DVBT_SCALE1_BA9, 0x28},
+ {DVBT_SCALE1_BAA, 0x59},
+ {DVBT_SCALE1_BAB, 0x83},
+ {DVBT_SCALE1_BAC, 0xd4},
+ {DVBT_SCALE1_BB0, 0x65},
+ {DVBT_SCALE1_BB1, 0x43},
+ {DVBT_KB_P1, 0x1},
+ {DVBT_KB_P2, 0x4},
+ {DVBT_KB_P3, 0x7},
+ {DVBT_K1_CR_STEP12, 0xa},
+ {DVBT_REG_GPE, 0x1},
+ {DVBT_SERIAL, 0x0},
+ {DVBT_CDIV_PH0, 0x9},
+ {DVBT_CDIV_PH1, 0x9},
+ {DVBT_MPEG_IO_OPT_2_2, 0x0},
+ {DVBT_MPEG_IO_OPT_1_0, 0x0},
+ {DVBT_TRK_KS_P2, 0x4},
+ {DVBT_TRK_KS_I2, 0x7},
+ {DVBT_TR_THD_SET2, 0x6},
+ {DVBT_TRK_KC_I2, 0x5},
+ {DVBT_CR_THD_SET2, 0x1},
+ {DVBT_SPEC_INV, 0x0},
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0);
+
+ /*
+ * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
+ * / CrystalFreqHz)
+ */
+ pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal;
+ pset_iffreq *= 0x400000;
+ pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal);
+ pset_iffreq = pset_iffreq & 0x3fffff;
+
+ for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
+ ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg,
+ rtl2832_initial_regs[i].value);
+ if (ret)
+ goto err;
+ }
+
+ /* load tuner specific settings */
+ dev_dbg(&priv->i2c->dev, "%s: load settings for tuner=%02x\n",
+ __func__, priv->cfg.tuner);
+ switch (priv->cfg.tuner) {
+ case RTL2832_TUNER_FC0012:
+ case RTL2832_TUNER_FC0013:
+ len = ARRAY_SIZE(rtl2832_tuner_init_fc0012);
+ init = rtl2832_tuner_init_fc0012;
+ break;
+ case RTL2832_TUNER_TUA9001:
+ len = ARRAY_SIZE(rtl2832_tuner_init_tua9001);
+ init = rtl2832_tuner_init_tua9001;
+ break;
+ case RTL2832_TUNER_E4000:
+ len = ARRAY_SIZE(rtl2832_tuner_init_e4000);
+ init = rtl2832_tuner_init_e4000;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = rtl2832_wr_demod_reg(priv, init[i].reg, init[i].value);
+ if (ret)
+ goto err;
+ }
+
+ /* if frequency settings */
+ ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2832_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ s->min_delay_ms = 1000;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+ return 0;
+}
+
+static int rtl2832_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, j;
+ u64 bw_mode, num, num2;
+ u32 resamp_ratio, cfreq_off_ratio;
+ static u8 bw_params[3][32] = {
+ /* 6 MHz bandwidth */
+ {
+ 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f,
+ 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2,
+ 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67,
+ 0x19, 0xe0,
+ },
+
+ /* 7 MHz bandwidth */
+ {
+ 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf,
+ 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30,
+ 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22,
+ 0x19, 0x10,
+ },
+
+ /* 8 MHz bandwidth */
+ {
+ 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf,
+ 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7,
+ 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8,
+ 0x19, 0xe0,
+ },
+ };
+
+
+ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d " \
+ "inversion=%d\n", __func__, c->frequency,
+ c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ bw_mode = 48000000;
+ break;
+ case 7000000:
+ i = 1;
+ bw_mode = 56000000;
+ break;
+ case 8000000:
+ i = 2;
+ bw_mode = 64000000;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__);
+ return -EINVAL;
+ }
+
+ for (j = 0; j < sizeof(bw_params[0]); j++) {
+ ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
+ if (ret)
+ goto err;
+ }
+
+ /* calculate and set resample ratio
+ * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
+ * / ConstWithBandwidthMode)
+ */
+ num = priv->cfg.xtal * 7;
+ num *= 0x400000;
+ num = div_u64(num, bw_mode);
+ resamp_ratio = num & 0x3ffffff;
+ ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio);
+ if (ret)
+ goto err;
+
+ /* calculate and set cfreq off ratio
+ * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20)
+ * / (CrystalFreqHz * 7))
+ */
+ num = bw_mode << 20;
+ num2 = priv->cfg.xtal * 7;
+ num = div_u64(num, num2);
+ num = -num;
+ cfreq_off_ratio = num & 0xfffff;
+ ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
+ if (ret)
+ goto err;
+
+
+ /* soft reset */
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_get_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[3];
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2832_rd_regs(priv, 0x3c, 3, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2832_rd_reg(priv, 0x51, 3, &buf[2]);
+ if (ret)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf);
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[2] >> 2) & 1) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[2] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int ret;
+ u32 tmp;
+ *status = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp);
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ /* TODO find out if this is also true for rtl2832? */
+ /*else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }*/
+
+ return ret;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int ret, hierarchy, constellation;
+ u8 buf[2], tmp;
+ u16 tmp16;
+#define CONSTELLATION_NUM 3
+#define HIERARCHY_NUM 4
+ static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+ { 85387325, 85387325, 85387325, 85387325 },
+ { 86676178, 86676178, 87167949, 87795660 },
+ { 87659938, 87659938, 87885178, 88241743 },
+ };
+
+ /* reports SNR in resolution of 0.1 dB */
+
+ ret = rtl2832_rd_reg(priv, 0x3c, 3, &tmp);
+ if (ret)
+ goto err;
+
+ constellation = (tmp >> 2) & 0x03; /* [3:2] */
+ if (constellation > CONSTELLATION_NUM - 1)
+ goto err;
+
+ hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
+ if (hierarchy > HIERARCHY_NUM - 1)
+ goto err;
+
+ ret = rtl2832_rd_regs(priv, 0x0c, 4, buf, 2);
+ if (ret)
+ goto err;
+
+ tmp16 = buf[0] << 8 | buf[1];
+
+ if (tmp16)
+ *snr = (snr_constant[constellation][hierarchy] -
+ intlog10(tmp16)) / ((1 << 24) / 100);
+ else
+ *snr = 0;
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ ret = rtl2832_rd_regs(priv, 0x4e, 3, buf, 2);
+ if (ret)
+ goto err;
+
+ *ber = buf[0] << 8 | buf[1];
+
+ return 0;
+err:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct dvb_frontend_ops rtl2832_ops;
+
+static void rtl2832_release(struct dvb_frontend *fe)
+{
+ struct rtl2832_priv *priv = fe->demodulator_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2832_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ dev_dbg(&i2c->dev, "%s:\n", __func__);
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ priv->tuner = cfg->tuner;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
+
+ /* check if the demod is there */
+ ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* TODO implement sleep mode */
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2832_attach);
+
+static struct dvb_frontend_ops rtl2832_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2832 (DVB-T)",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 166667,
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2832_release,
+
+ .init = rtl2832_init,
+ .sleep = rtl2832_sleep,
+
+ .get_tune_settings = rtl2832_get_tune_settings,
+
+ .set_frontend = rtl2832_set_frontend,
+ .get_frontend = rtl2832_get_frontend,
+
+ .read_status = rtl2832_read_status,
+ .read_snr = rtl2832_read_snr,
+ .read_ber = rtl2832_read_ber,
+
+ .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
+MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h
new file mode 100644
index 000000000000..785a466eb065
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832.h
@@ -0,0 +1,75 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2832_H
+#define RTL2832_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2832_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * IFs for all used modes.
+ * Hz
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+ */
+ u32 if_dvbt;
+
+ /*
+ * tuner
+ * XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver.
+ */
+#define RTL2832_TUNER_TUA9001 0x24
+#define RTL2832_TUNER_FC0012 0x26
+#define RTL2832_TUNER_E4000 0x27
+#define RTL2832_TUNER_FC0013 0x29
+ u8 tuner;
+};
+
+#if defined(CONFIG_DVB_RTL2832) || \
+ (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2832_attach(
+ const struct rtl2832_config *cfg,
+ struct i2c_adapter *i2c
+);
+#else
+static inline struct dvb_frontend *rtl2832_attach(
+ const struct rtl2832_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+
+#endif /* RTL2832_H */
diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h
new file mode 100644
index 000000000000..7d97ce9d2193
--- /dev/null
+++ b/drivers/media/dvb-frontends/rtl2832_priv.h
@@ -0,0 +1,342 @@
+/*
+ * Realtek RTL2832 DVB-T demodulator driver
+ *
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2832_PRIV_H
+#define RTL2832_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2832.h"
+
+struct rtl2832_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2832_config cfg;
+
+ bool i2c_gate_state;
+ bool sleeping;
+
+ u8 tuner;
+ u8 page; /* active register page */
+};
+
+struct rtl2832_reg_entry {
+ u8 page;
+ u8 start_address;
+ u8 msb;
+ u8 lsb;
+};
+
+struct rtl2832_reg_value {
+ int reg;
+ u32 value;
+};
+
+
+/* Demod register bit names */
+enum DVBT_REG_BIT_NAME {
+ DVBT_SOFT_RST,
+ DVBT_IIC_REPEAT,
+ DVBT_TR_WAIT_MIN_8K,
+ DVBT_RSD_BER_FAIL_VAL,
+ DVBT_EN_BK_TRK,
+ DVBT_REG_PI,
+ DVBT_REG_PFREQ_1_0,
+ DVBT_PD_DA8,
+ DVBT_LOCK_TH,
+ DVBT_BER_PASS_SCAL,
+ DVBT_CE_FFSM_BYPASS,
+ DVBT_ALPHAIIR_N,
+ DVBT_ALPHAIIR_DIF,
+ DVBT_EN_TRK_SPAN,
+ DVBT_LOCK_TH_LEN,
+ DVBT_CCI_THRE,
+ DVBT_CCI_MON_SCAL,
+ DVBT_CCI_M0,
+ DVBT_CCI_M1,
+ DVBT_CCI_M2,
+ DVBT_CCI_M3,
+ DVBT_SPEC_INIT_0,
+ DVBT_SPEC_INIT_1,
+ DVBT_SPEC_INIT_2,
+ DVBT_AD_EN_REG,
+ DVBT_AD_EN_REG1,
+ DVBT_EN_BBIN,
+ DVBT_MGD_THD0,
+ DVBT_MGD_THD1,
+ DVBT_MGD_THD2,
+ DVBT_MGD_THD3,
+ DVBT_MGD_THD4,
+ DVBT_MGD_THD5,
+ DVBT_MGD_THD6,
+ DVBT_MGD_THD7,
+ DVBT_EN_CACQ_NOTCH,
+ DVBT_AD_AV_REF,
+ DVBT_PIP_ON,
+ DVBT_SCALE1_B92,
+ DVBT_SCALE1_B93,
+ DVBT_SCALE1_BA7,
+ DVBT_SCALE1_BA9,
+ DVBT_SCALE1_BAA,
+ DVBT_SCALE1_BAB,
+ DVBT_SCALE1_BAC,
+ DVBT_SCALE1_BB0,
+ DVBT_SCALE1_BB1,
+ DVBT_KB_P1,
+ DVBT_KB_P2,
+ DVBT_KB_P3,
+ DVBT_OPT_ADC_IQ,
+ DVBT_AD_AVI,
+ DVBT_AD_AVQ,
+ DVBT_K1_CR_STEP12,
+ DVBT_TRK_KS_P2,
+ DVBT_TRK_KS_I2,
+ DVBT_TR_THD_SET2,
+ DVBT_TRK_KC_P2,
+ DVBT_TRK_KC_I2,
+ DVBT_CR_THD_SET2,
+ DVBT_PSET_IFFREQ,
+ DVBT_SPEC_INV,
+ DVBT_BW_INDEX,
+ DVBT_RSAMP_RATIO,
+ DVBT_CFREQ_OFF_RATIO,
+ DVBT_FSM_STAGE,
+ DVBT_RX_CONSTEL,
+ DVBT_RX_HIER,
+ DVBT_RX_C_RATE_LP,
+ DVBT_RX_C_RATE_HP,
+ DVBT_GI_IDX,
+ DVBT_FFT_MODE_IDX,
+ DVBT_RSD_BER_EST,
+ DVBT_CE_EST_EVM,
+ DVBT_RF_AGC_VAL,
+ DVBT_IF_AGC_VAL,
+ DVBT_DAGC_VAL,
+ DVBT_SFREQ_OFF,
+ DVBT_CFREQ_OFF,
+ DVBT_POLAR_RF_AGC,
+ DVBT_POLAR_IF_AGC,
+ DVBT_AAGC_HOLD,
+ DVBT_EN_RF_AGC,
+ DVBT_EN_IF_AGC,
+ DVBT_IF_AGC_MIN,
+ DVBT_IF_AGC_MAX,
+ DVBT_RF_AGC_MIN,
+ DVBT_RF_AGC_MAX,
+ DVBT_IF_AGC_MAN,
+ DVBT_IF_AGC_MAN_VAL,
+ DVBT_RF_AGC_MAN,
+ DVBT_RF_AGC_MAN_VAL,
+ DVBT_DAGC_TRG_VAL,
+ DVBT_AGC_TARG_VAL,
+ DVBT_LOOP_GAIN_3_0,
+ DVBT_LOOP_GAIN_4,
+ DVBT_VTOP,
+ DVBT_KRF,
+ DVBT_AGC_TARG_VAL_0,
+ DVBT_AGC_TARG_VAL_8_1,
+ DVBT_AAGC_LOOP_GAIN,
+ DVBT_LOOP_GAIN2_3_0,
+ DVBT_LOOP_GAIN2_4,
+ DVBT_LOOP_GAIN3,
+ DVBT_VTOP1,
+ DVBT_VTOP2,
+ DVBT_VTOP3,
+ DVBT_KRF1,
+ DVBT_KRF2,
+ DVBT_KRF3,
+ DVBT_KRF4,
+ DVBT_EN_GI_PGA,
+ DVBT_THD_LOCK_UP,
+ DVBT_THD_LOCK_DW,
+ DVBT_THD_UP1,
+ DVBT_THD_DW1,
+ DVBT_INTER_CNT_LEN,
+ DVBT_GI_PGA_STATE,
+ DVBT_EN_AGC_PGA,
+ DVBT_CKOUTPAR,
+ DVBT_CKOUT_PWR,
+ DVBT_SYNC_DUR,
+ DVBT_ERR_DUR,
+ DVBT_SYNC_LVL,
+ DVBT_ERR_LVL,
+ DVBT_VAL_LVL,
+ DVBT_SERIAL,
+ DVBT_SER_LSB,
+ DVBT_CDIV_PH0,
+ DVBT_CDIV_PH1,
+ DVBT_MPEG_IO_OPT_2_2,
+ DVBT_MPEG_IO_OPT_1_0,
+ DVBT_CKOUTPAR_PIP,
+ DVBT_CKOUT_PWR_PIP,
+ DVBT_SYNC_LVL_PIP,
+ DVBT_ERR_LVL_PIP,
+ DVBT_VAL_LVL_PIP,
+ DVBT_CKOUTPAR_PID,
+ DVBT_CKOUT_PWR_PID,
+ DVBT_SYNC_LVL_PID,
+ DVBT_ERR_LVL_PID,
+ DVBT_VAL_LVL_PID,
+ DVBT_SM_PASS,
+ DVBT_UPDATE_REG_2,
+ DVBT_BTHD_P3,
+ DVBT_BTHD_D3,
+ DVBT_FUNC4_REG0,
+ DVBT_FUNC4_REG1,
+ DVBT_FUNC4_REG2,
+ DVBT_FUNC4_REG3,
+ DVBT_FUNC4_REG4,
+ DVBT_FUNC4_REG5,
+ DVBT_FUNC4_REG6,
+ DVBT_FUNC4_REG7,
+ DVBT_FUNC4_REG8,
+ DVBT_FUNC4_REG9,
+ DVBT_FUNC4_REG10,
+ DVBT_FUNC5_REG0,
+ DVBT_FUNC5_REG1,
+ DVBT_FUNC5_REG2,
+ DVBT_FUNC5_REG3,
+ DVBT_FUNC5_REG4,
+ DVBT_FUNC5_REG5,
+ DVBT_FUNC5_REG6,
+ DVBT_FUNC5_REG7,
+ DVBT_FUNC5_REG8,
+ DVBT_FUNC5_REG9,
+ DVBT_FUNC5_REG10,
+ DVBT_FUNC5_REG11,
+ DVBT_FUNC5_REG12,
+ DVBT_FUNC5_REG13,
+ DVBT_FUNC5_REG14,
+ DVBT_FUNC5_REG15,
+ DVBT_FUNC5_REG16,
+ DVBT_FUNC5_REG17,
+ DVBT_FUNC5_REG18,
+ DVBT_AD7_SETTING,
+ DVBT_RSSI_R,
+ DVBT_ACI_DET_IND,
+ DVBT_REG_MON,
+ DVBT_REG_MONSEL,
+ DVBT_REG_GPE,
+ DVBT_REG_GPO,
+ DVBT_REG_4MSEL,
+ DVBT_TEST_REG_1,
+ DVBT_TEST_REG_2,
+ DVBT_TEST_REG_3,
+ DVBT_TEST_REG_4,
+ DVBT_REG_BIT_NAME_ITEM_TERMINATOR,
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = {
+ {DVBT_DAGC_TRG_VAL, 0x39},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x6},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x16},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x9c},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9f4},
+ {DVBT_OPT_ADC_IQ, 0x1},
+ {DVBT_AD_AVI, 0x0},
+ {DVBT_AD_AVQ, 0x0},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = {
+ {DVBT_DAGC_TRG_VAL, 0x5a},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x16},
+ {DVBT_LOOP_GAIN2_3_0, 0x6},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x16},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9bf},
+ {DVBT_EN_GI_PGA, 0x0},
+ {DVBT_THD_LOCK_UP, 0x0},
+ {DVBT_THD_LOCK_DW, 0x0},
+ {DVBT_THD_UP1, 0x11},
+ {DVBT_THD_DW1, 0xef},
+ {DVBT_INTER_CNT_LEN, 0xc},
+ {DVBT_GI_PGA_STATE, 0x0},
+ {DVBT_EN_AGC_PGA, 0x1},
+ {DVBT_IF_AGC_MAN, 0x0},
+};
+
+static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = {
+ {DVBT_DAGC_TRG_VAL, 0x5a},
+ {DVBT_AGC_TARG_VAL_0, 0x0},
+ {DVBT_AGC_TARG_VAL_8_1, 0x5a},
+ {DVBT_AAGC_LOOP_GAIN, 0x18},
+ {DVBT_LOOP_GAIN2_3_0, 0x8},
+ {DVBT_LOOP_GAIN2_4, 0x1},
+ {DVBT_LOOP_GAIN3, 0x18},
+ {DVBT_VTOP1, 0x35},
+ {DVBT_VTOP2, 0x21},
+ {DVBT_VTOP3, 0x21},
+ {DVBT_KRF1, 0x0},
+ {DVBT_KRF2, 0x40},
+ {DVBT_KRF3, 0x10},
+ {DVBT_KRF4, 0x10},
+ {DVBT_IF_AGC_MIN, 0x80},
+ {DVBT_IF_AGC_MAX, 0x7f},
+ {DVBT_RF_AGC_MIN, 0x80},
+ {DVBT_RF_AGC_MAX, 0x7f},
+ {DVBT_POLAR_RF_AGC, 0x0},
+ {DVBT_POLAR_IF_AGC, 0x0},
+ {DVBT_AD7_SETTING, 0xe9d4},
+ {DVBT_EN_GI_PGA, 0x0},
+ {DVBT_THD_LOCK_UP, 0x0},
+ {DVBT_THD_LOCK_DW, 0x0},
+ {DVBT_THD_UP1, 0x14},
+ {DVBT_THD_DW1, 0xec},
+ {DVBT_INTER_CNT_LEN, 0xc},
+ {DVBT_GI_PGA_STATE, 0x0},
+ {DVBT_EN_AGC_PGA, 0x1},
+ {DVBT_REG_GPE, 0x1},
+ {DVBT_REG_GPO, 0x1},
+ {DVBT_REG_MONSEL, 0x1},
+ {DVBT_REG_MON, 0x1},
+ {DVBT_REG_4MSEL, 0x0},
+};
+
+#endif /* RTL2832_PRIV_H */
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c
index f71b06221e14..f71b06221e14 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb-frontends/s5h1409.c
diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h
index 91f2ebd1a534..91f2ebd1a534 100644
--- a/drivers/media/dvb/frontends/s5h1409.h
+++ b/drivers/media/dvb-frontends/s5h1409.h
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c
index 6cc4b7a9dd60..6cc4b7a9dd60 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb-frontends/s5h1411.c
diff --git a/drivers/media/dvb/frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h
index 45ec0f82989c..45ec0f82989c 100644
--- a/drivers/media/dvb/frontends/s5h1411.h
+++ b/drivers/media/dvb-frontends/s5h1411.h
diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c
index e2fec9ebf947..e2fec9ebf947 100644
--- a/drivers/media/dvb/frontends/s5h1420.c
+++ b/drivers/media/dvb-frontends/s5h1420.c
diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h
index ff308136d865..ff308136d865 100644
--- a/drivers/media/dvb/frontends/s5h1420.h
+++ b/drivers/media/dvb-frontends/s5h1420.h
diff --git a/drivers/media/dvb/frontends/s5h1420_priv.h b/drivers/media/dvb-frontends/s5h1420_priv.h
index d9c58d281816..d9c58d281816 100644
--- a/drivers/media/dvb/frontends/s5h1420_priv.h
+++ b/drivers/media/dvb-frontends/s5h1420_priv.h
diff --git a/drivers/media/dvb/frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c
index 8352ce1c9556..8352ce1c9556 100644
--- a/drivers/media/dvb/frontends/s5h1432.c
+++ b/drivers/media/dvb-frontends/s5h1432.c
diff --git a/drivers/media/dvb/frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h
index b57438c32546..b57438c32546 100644
--- a/drivers/media/dvb/frontends/s5h1432.h
+++ b/drivers/media/dvb-frontends/s5h1432.h
diff --git a/drivers/media/dvb/frontends/s921.c b/drivers/media/dvb-frontends/s921.c
index cd2288c07147..cd2288c07147 100644
--- a/drivers/media/dvb/frontends/s921.c
+++ b/drivers/media/dvb-frontends/s921.c
diff --git a/drivers/media/dvb/frontends/s921.h b/drivers/media/dvb-frontends/s921.h
index f220d8299c81..f220d8299c81 100644
--- a/drivers/media/dvb/frontends/s921.h
+++ b/drivers/media/dvb-frontends/s921.h
diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c
index a68a64800df7..a68a64800df7 100644
--- a/drivers/media/dvb/frontends/si21xx.c
+++ b/drivers/media/dvb-frontends/si21xx.c
diff --git a/drivers/media/dvb/frontends/si21xx.h b/drivers/media/dvb-frontends/si21xx.h
index 141b5b8a5f63..141b5b8a5f63 100644
--- a/drivers/media/dvb/frontends/si21xx.h
+++ b/drivers/media/dvb-frontends/si21xx.h
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c
index e37274c8f14e..e37274c8f14e 100644
--- a/drivers/media/dvb/frontends/sp8870.c
+++ b/drivers/media/dvb-frontends/sp8870.c
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb-frontends/sp8870.h
index a764a793c7d8..a764a793c7d8 100644
--- a/drivers/media/dvb/frontends/sp8870.h
+++ b/drivers/media/dvb-frontends/sp8870.h
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c
index f4096ccb226e..f4096ccb226e 100644
--- a/drivers/media/dvb/frontends/sp887x.c
+++ b/drivers/media/dvb-frontends/sp887x.c
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h
index 04eff6e0eef3..04eff6e0eef3 100644
--- a/drivers/media/dvb/frontends/sp887x.h
+++ b/drivers/media/dvb-frontends/sp887x.h
diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c
index 117a56926dca..117a56926dca 100644
--- a/drivers/media/dvb/frontends/stb0899_algo.c
+++ b/drivers/media/dvb-frontends/stb0899_algo.c
diff --git a/drivers/media/dvb/frontends/stb0899_cfg.h b/drivers/media/dvb-frontends/stb0899_cfg.h
index 0867906d3ff3..0867906d3ff3 100644
--- a/drivers/media/dvb/frontends/stb0899_cfg.h
+++ b/drivers/media/dvb-frontends/stb0899_cfg.h
diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c
new file mode 100644
index 000000000000..79e29de87fb7
--- /dev/null
+++ b/drivers/media/dvb-frontends/stb0899_drv.c
@@ -0,0 +1,1652 @@
+/*
+ STB0899 Multistandard Frontend driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#include "stb0899_drv.h"
+#include "stb0899_priv.h"
+#include "stb0899_reg.h"
+
+static unsigned int verbose = 0;//1;
+module_param(verbose, int, 0644);
+
+/* C/N in dB/10, NIRM/NIRL */
+static const struct stb0899_tab stb0899_cn_tab[] = {
+ { 200, 2600 },
+ { 190, 2700 },
+ { 180, 2860 },
+ { 170, 3020 },
+ { 160, 3210 },
+ { 150, 3440 },
+ { 140, 3710 },
+ { 130, 4010 },
+ { 120, 4360 },
+ { 110, 4740 },
+ { 100, 5190 },
+ { 90, 5670 },
+ { 80, 6200 },
+ { 70, 6770 },
+ { 60, 7360 },
+ { 50, 7970 },
+ { 40, 8250 },
+ { 30, 9000 },
+ { 20, 9450 },
+ { 15, 9600 },
+};
+
+/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.0 dBm directly connected,
+ * -52.0 to -74.8 with extra attenuation.
+ * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm.
+ * Crude linear extrapolation below -84.8dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
+ { -750, -128 },
+ { -748, -94 },
+ { -745, -92 },
+ { -735, -90 },
+ { -720, -87 },
+ { -670, -77 },
+ { -640, -70 },
+ { -610, -62 },
+ { -600, -60 },
+ { -590, -56 },
+ { -560, -41 },
+ { -540, -25 },
+ { -530, -17 },
+ { -520, -11 },
+ { -500, 1 },
+ { -490, 6 },
+ { -480, 10 },
+ { -440, 22 },
+ { -420, 27 },
+ { -400, 31 },
+ { -380, 34 },
+ { -340, 40 },
+ { -320, 43 },
+ { -280, 48 },
+ { -250, 52 },
+ { -230, 55 },
+ { -180, 61 },
+ { -140, 66 },
+ { -90, 73 },
+ { -80, 74 },
+ { 500, 127 }
+};
+
+/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10.
+ * As measured, connected to a modulator.
+ * -8.0 to -50.1 dBm directly connected,
+ * -53.0 to -76.6 with extra attenuation.
+ * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm.
+ * Crude linear extrapolation below -76.6dBm and above -8.0dBm.
+ */
+static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
+ { 700, 0 },
+ { -80, 3217 },
+ { -150, 3893 },
+ { -190, 4217 },
+ { -240, 4621 },
+ { -280, 4945 },
+ { -320, 5273 },
+ { -350, 5545 },
+ { -370, 5741 },
+ { -410, 6147 },
+ { -450, 6671 },
+ { -490, 7413 },
+ { -501, 7665 },
+ { -530, 8767 },
+ { -560, 10219 },
+ { -580, 10939 },
+ { -590, 11518 },
+ { -600, 11723 },
+ { -650, 12659 },
+ { -690, 13219 },
+ { -730, 13645 },
+ { -750, 13909 },
+ { -766, 14153 },
+ { -950, 16383 }
+};
+
+/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
+static struct stb0899_tab stb0899_quant_tab[] = {
+ { 0, 0 },
+ { 0, 100 },
+ { 600, 200 },
+ { 950, 299 },
+ { 1200, 398 },
+ { 1400, 501 },
+ { 1560, 603 },
+ { 1690, 700 },
+ { 1810, 804 },
+ { 1910, 902 },
+ { 2000, 1000 },
+ { 2080, 1096 },
+ { 2160, 1202 },
+ { 2230, 1303 },
+ { 2350, 1496 },
+ { 2410, 1603 },
+ { 2460, 1698 },
+ { 2510, 1799 },
+ { 2600, 1995 },
+ { 2650, 2113 },
+ { 2690, 2213 },
+ { 2720, 2291 },
+ { 2760, 2399 },
+ { 2800, 2512 },
+ { 2860, 2692 },
+ { 2930, 2917 },
+ { 2960, 3020 },
+ { 3010, 3199 },
+ { 3040, 3311 },
+ { 3060, 3388 },
+ { 3120, 3631 },
+ { 3190, 3936 },
+ { 3400, 5012 },
+ { 3610, 6383 },
+ { 3800, 7943 },
+ { 4210, 12735 },
+ { 4500, 17783 },
+ { 4690, 22131 },
+ { 4810, 25410 }
+};
+
+/* DVB-S2 Es/N0 estimate in dB/100 vs read value */
+static struct stb0899_tab stb0899_est_tab[] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 301, 2 },
+ { 1204, 16 },
+ { 1806, 64 },
+ { 2408, 256 },
+ { 2709, 512 },
+ { 3010, 1023 },
+ { 3311, 2046 },
+ { 3612, 4093 },
+ { 3823, 6653 },
+ { 3913, 8185 },
+ { 4010, 10233 },
+ { 4107, 12794 },
+ { 4214, 16368 },
+ { 4266, 18450 },
+ { 4311, 20464 },
+ { 4353, 22542 },
+ { 4391, 24604 },
+ { 4425, 26607 },
+ { 4457, 28642 },
+ { 4487, 30690 },
+ { 4515, 32734 },
+ { 4612, 40926 },
+ { 4692, 49204 },
+ { 4816, 65464 },
+ { 4913, 81846 },
+ { 4993, 98401 },
+ { 5060, 114815 },
+ { 5118, 131220 },
+ { 5200, 158489 },
+ { 5300, 199526 },
+ { 5400, 251189 },
+ { 5500, 316228 },
+ { 5600, 398107 },
+ { 5720, 524807 },
+ { 5721, 526017 },
+};
+
+static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int ret;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+ u8 buf;
+
+ 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 = &buf,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1,
+ "Read error, Reg=[0x%02x], Status=%d",
+ reg, ret);
+
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
+ reg, buf);
+
+ return (unsigned int)buf;
+}
+
+int stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
+{
+ int result;
+
+ result = _stb0899_read_reg(state, reg);
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ return result;
+}
+
+u32 _stb0899_read_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset)
+{
+ int status;
+ u32 data;
+ u8 buf[7] = { 0 };
+ u16 tmpaddr;
+
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 2
+ };
+
+ struct i2c_msg msg_r = {
+ .addr = state->config->demod_address,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = 4
+ };
+
+ tmpaddr = stb0899_reg_offset & 0xff00;
+ if (!(stb0899_reg_offset & 0x8))
+ tmpaddr = stb0899_reg_offset | 0x20;
+
+ buf_1[0] = GETBYTE(tmpaddr, BYTE1);
+ buf_1[1] = GETBYTE(tmpaddr, BYTE0);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+
+ goto err;
+ }
+
+ /* Dummy */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1)
+ goto err;
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1)
+ goto err;
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+
+ /* Actual */
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ goto err;
+ }
+
+ status = i2c_transfer(state->i2c, &msg_r, 1);
+ if (status < 1) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]);
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data);
+
+ return data;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_s2reg(struct stb0899_state *state,
+ u32 stb0899_i2cdev,
+ u32 stb0899_base_addr,
+ u16 stb0899_reg_offset,
+ u32 stb0899_data)
+{
+ int status;
+
+ /* Base Address Setup */
+ u8 buf_0[] = {
+ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
+ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
+ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
+ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
+ };
+ u8 buf_1[] = {
+ 0x00, /* 0xf3 Reg Offset */
+ 0x00, /* 0x44 Reg Offset */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ 0x00, /* data */
+ };
+
+ struct i2c_msg msg_0 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_0,
+ .len = 6
+ };
+
+ struct i2c_msg msg_1 = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf_1,
+ .len = 6
+ };
+
+ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
+ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
+ buf_1[2] = GETBYTE(stb0899_data, BYTE0);
+ buf_1[3] = GETBYTE(stb0899_data, BYTE1);
+ buf_1[4] = GETBYTE(stb0899_data, BYTE2);
+ buf_1[5] = GETBYTE(stb0899_data, BYTE3);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data);
+
+ status = i2c_transfer(state->i2c, &msg_0, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+ goto err;
+ }
+ status = i2c_transfer(state->i2c, &msg_1, 1);
+ if (unlikely(status < 1)) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
+ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
+
+ return status < 0 ? status : -EREMOTEIO;
+ }
+
+ return 0;
+
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count)
+{
+ int status;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+
+ 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 = buf,
+ .len = count
+ }
+ };
+
+ status = i2c_transfer(state->i2c, msg, 2);
+ if (status != 2) {
+ if (status != -ERESTARTSYS)
+ printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n",
+ __func__, reg, count, status);
+ goto err;
+ }
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
+ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ _stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++) {
+ printk(" %02x", buf[i]);
+ }
+ printk("\n");
+ }
+
+ return 0;
+err:
+ return status < 0 ? status : -EREMOTEIO;
+}
+
+int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count)
+{
+ int ret;
+ u8 buf[2 + count];
+ struct i2c_msg i2c_msg = {
+ .addr = state->config->demod_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 2 + count
+ };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ memcpy(&buf[2], data, count);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++)
+ printk(" %02x", data[i]);
+ printk("\n");
+ }
+ ret = i2c_transfer(state->i2c, &i2c_msg, 1);
+
+ /*
+ * Bug ID 9:
+ * access to 0xf2xx/0xf6xx
+ * must be followed by read from 0xf2ff/0xf6ff.
+ */
+ if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
+ stb0899_read_reg(state, (reg | 0x00ff));
+
+ if (ret != 1) {
+ if (ret != -ERESTARTSYS)
+ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
+ reg, data[0], count, ret);
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data)
+{
+ return stb0899_write_regs(state, reg, &data, 1);
+}
+
+/*
+ * stb0899_get_mclk
+ * Get STB0899 master clock frequency
+ * ExtClk: external clock frequency (Hz)
+ */
+static u32 stb0899_get_mclk(struct stb0899_state *state)
+{
+ u32 mclk = 0, div = 0;
+
+ div = stb0899_read_reg(state, STB0899_NCOARSE);
+ mclk = (div + 1) * state->config->xtal_freq / 6;
+ dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk);
+
+ return mclk;
+}
+
+/*
+ * stb0899_set_mclk
+ * Set STB0899 master Clock frequency
+ * Mclk: demodulator master clock
+ * ExtClk: external clock frequency (Hz)
+ */
+static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk)
+{
+ struct stb0899_internal *internal = &state->internal;
+ u8 mdiv = 0;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config);
+ mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1;
+ dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv);
+
+ stb0899_write_reg(state, STB0899_NCOARSE, mdiv);
+ internal->master_clk = stb0899_get_mclk(state);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk);
+}
+
+static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable)
+{
+ struct stb0899_config *config = state->config;
+ const struct stb0899_postproc *postproc = config->postproc;
+
+ /* post process event */
+ if (postproc) {
+ if (enable) {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ } else {
+ if (postproc[ctl].level == STB0899_GPIOPULLUP)
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
+ else
+ stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
+ }
+ }
+ return 0;
+}
+
+static void stb0899_release(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+ kfree(state);
+}
+
+/*
+ * stb0899_get_alpha
+ * return: rolloff
+ */
+static int stb0899_get_alpha(struct stb0899_state *state)
+{
+ u8 mode_coeff;
+
+ mode_coeff = stb0899_read_reg(state, STB0899_DEMOD);
+
+ if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1)
+ return 20;
+ else
+ return 35;
+}
+
+/*
+ * stb0899_init_calc
+ */
+static void stb0899_init_calc(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ int master_clk;
+ u8 agc[2];
+ u32 reg;
+
+ /* Read registers (in burst mode) */
+ stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */
+
+ /* Initial calculations */
+ master_clk = stb0899_get_mclk(state);
+ internal->t_agc1 = 0;
+ internal->t_agc2 = 0;
+ internal->master_clk = master_clk;
+ internal->mclk = master_clk / 65536L;
+ internal->rolloff = stb0899_get_alpha(state);
+
+ /* DVBS2 Initial calculations */
+ /* Set AGC value to the middle */
+ internal->agc_gain = 8154;
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
+ STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain);
+ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
+
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA);
+ internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg);
+
+ internal->center_freq = 0;
+ internal->av_frame_coarse = 10;
+ internal->av_frame_fine = 20;
+ internal->step_size = 2;
+/*
+ if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO))
+ pParams->IQLocked = 0;
+ else
+ pParams->IQLocked = 1;
+*/
+}
+
+static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (1) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (!STB0899_GETFIELD(FIFOFULL, reg))
+ break;
+ if ((jiffies - start) > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out !!");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, i;
+
+ if (cmd->msg_len > 8)
+ return -EINVAL;
+
+ /* enable FIFO precharge */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ for (i = 0; i < cmd->msg_len; i++) {
+ /* wait for FIFO empty */
+ if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ msleep(100);
+ return 0;
+}
+
+static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(RXEND, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, length = 0, i;
+ int result;
+
+ if (stb0899_wait_diseqc_rxidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
+ if (STB0899_GETFIELD(RXEND, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_DISRX_ST1);
+ length = STB0899_GETFIELD(FIFOBYTENBR, reg);
+
+ if (length > sizeof (reply->msg)) {
+ result = -EOVERFLOW;
+ goto exit;
+ }
+ reply->msg_len = length;
+
+ /* extract data */
+ for (i = 0; i < length; i++)
+ reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO);
+ }
+
+ return 0;
+exit:
+
+ return result;
+}
+
+static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout)
+{
+ u8 reg = 0;
+ unsigned long start = jiffies;
+
+ while (!STB0899_GETFIELD(TXIDLE, reg)) {
+ reg = stb0899_read_reg(state, STB0899_DISSTATUS);
+ if (jiffies - start > timeout) {
+ dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
+ return -ETIMEDOUT;
+ }
+ msleep(10);
+ }
+ return 0;
+}
+
+static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ u8 reg, old_state;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ old_state = reg;
+ /* set to burst mode */
+ STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ switch (burst) {
+ case SEC_MINI_A:
+ /* unmodulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0x00);
+ break;
+ case SEC_MINI_B:
+ /* modulated */
+ stb0899_write_reg(state, STB0899_DISFIFO, 0xff);
+ break;
+ }
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ /* restore state */
+ stb0899_write_reg(state, STB0899_DISCNTRL1, old_state);
+
+ return 0;
+}
+
+static int stb0899_diseqc_init(struct stb0899_state *state)
+{
+/*
+ struct dvb_diseqc_slave_reply rx_data;
+*/
+ u8 f22_tx, reg;
+
+ u32 mclk, tx_freq = 22000;/* count = 0, i; */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL2);
+ STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL2, reg);
+
+ /* disable Tx spy */
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
+ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0);
+ stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
+
+ mclk = stb0899_get_mclk(state);
+ f22_tx = mclk / (tx_freq * 32);
+ stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */
+ state->rx_freq = 20000;
+
+ return 0;
+}
+
+static int stb0899_sleep(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+/*
+ u8 reg;
+*/
+ dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
+
+ return 0;
+}
+
+static int stb0899_wakeup(struct dvb_frontend *fe)
+{
+ int rc;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI)))
+ return rc;
+ /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00)))
+ return rc;
+ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00)))
+ return rc;
+
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1);
+
+ return 0;
+}
+
+static int stb0899_init(struct dvb_frontend *fe)
+{
+ int i;
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_config *config = state->config;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... ");
+
+ /* init device */
+ dprintk(state->verbose, FE_DEBUG, 1, "init device");
+ for (i = 0; config->init_dev[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod");
+ /* init S2 demod */
+ for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2DEMOD,
+ config->init_s2_demod[i].base_address,
+ config->init_s2_demod[i].offset,
+ config->init_s2_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod");
+ /* init S1 demod */
+ for (i = 0; config->init_s1_demod[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC");
+ /* init S2 fec */
+ for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++)
+ stb0899_write_s2reg(state, STB0899_S2FEC,
+ config->init_s2_fec[i].base_address,
+ config->init_s2_fec[i].offset,
+ config->init_s2_fec[i].data);
+
+ dprintk(state->verbose, FE_DEBUG, 1, "init TST");
+ /* init test */
+ for (i = 0; config->init_tst[i].address != 0xffff; i++)
+ stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data);
+
+ stb0899_init_calc(state);
+ stb0899_diseqc_init(state);
+
+ return 0;
+}
+
+static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val)
+{
+ int res = 0;
+ int min = 0, med;
+
+ if (val < tab[min].read)
+ res = tab[min].real;
+ else if (val >= tab[max].read)
+ res = tab[max].real;
+ else {
+ while ((max - min) > 1) {
+ med = (max + min) / 2;
+ if (val >= tab[min].read && val < tab[med].read)
+ max = med;
+ else
+ min = med;
+ }
+ res = ((val - tab[min].read) *
+ (tab[max].real - tab[min].real) /
+ (tab[max].read - tab[min].read)) +
+ tab[min].real;
+ }
+
+ return res;
+}
+
+static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ int val;
+ u32 reg;
+ *strength = 0;
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ reg = stb0899_read_reg(state, STB0899_AGCIQIN);
+ val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val);
+ *strength += 750;
+ dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm",
+ val & 0xff, *strength);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN);
+ val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
+
+ *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
+ *strength += 950;
+ dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
+ val & 0x3fff, *strength);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ unsigned int val, quant, quantn = -1, est, estn = -1;
+ u8 buf[2];
+ u32 reg;
+
+ *snr = 0;
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+
+ stb0899_read_regs(state, STB0899_NIRM, buf, 2);
+ val = MAKEWORD16(buf[0], buf[1]);
+
+ *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val);
+ dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n",
+ buf[0], buf[1], val, *snr);
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
+ quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
+ est = STB0899_GETFIELD(ESN0_EST, reg);
+ if (est == 1)
+ val = 301; /* C/N = 30.1 dB */
+ else if (est == 2)
+ val = 270; /* C/N = 27.0 dB */
+ else {
+ /* quantn = 100 * log(quant^2) */
+ quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100);
+ /* estn = 100 * log(est) */
+ estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est);
+ /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */
+ val = (quantn - estn) / 10;
+ }
+ *snr = val;
+ dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm",
+ quant, quantn, est, estn, val);
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+ u8 reg;
+ *status = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS");
+ if (internal->lock) {
+ reg = stb0899_read_reg(state, STB0899_VSTATUS);
+ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+
+ reg = stb0899_read_reg(state, STB0899_PLPARM);
+ if (STB0899_GETFIELD(VITCURPUN, reg)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC");
+ *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2");
+ if (internal->lock) {
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
+ if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) {
+ *status |= FE_HAS_CARRIER;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER");
+
+ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
+ if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) {
+ *status |= FE_HAS_LOCK;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK");
+
+ }
+ if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) {
+ *status |= FE_HAS_VITERBI;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI");
+ }
+ if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) {
+ *status |= FE_HAS_SYNC;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC");
+ /* post process event */
+ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
+ }
+ }
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * stb0899_get_error
+ * viterbi error for DVB-S/DSS
+ * packet error for DVB-S2
+ * Bit Error Rate or Packet Error Rate * 10 ^ 7
+ */
+static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 lsb, msb;
+
+ *ber = 0;
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ if (internal->lock) {
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber = MAKEWORD16(msb, lsb);
+ /* Viterbi Check */
+ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
+ /* Error Rate */
+ *ber *= 9766;
+ /* ber = ber * 10 ^ 7 */
+ *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ *ber /= 8;
+ }
+ }
+ break;
+ case SYS_DVBS2:
+ if (internal->lock) {
+ lsb = stb0899_read_reg(state, STB0899_ECNT1L);
+ msb = stb0899_read_reg(state, STB0899_ECNT1M);
+ *ber = MAKEWORD16(msb, lsb);
+ /* ber = ber * 10 ^ 7 */
+ *ber *= 10000000;
+ *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00);
+ break;
+ case SEC_VOLTAGE_18:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ case SEC_VOLTAGE_OFF:
+ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82);
+ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ u8 div, reg;
+
+ /* wait for diseqc idle */
+ if (stb0899_wait_diseqc_txidle(state, 100) < 0)
+ return -ETIMEDOUT;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ div = (internal->master_clk / 100) / 5632;
+ div = (div + 5) / 10;
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66);
+ reg = stb0899_read_reg(state, STB0899_ACRPRESC);
+ STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03);
+ stb0899_write_reg(state, STB0899_ACRPRESC, reg);
+ stb0899_write_reg(state, STB0899_ACRDIV1, div);
+ break;
+ case SEC_TONE_OFF:
+ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ int i2c_stat;
+ struct stb0899_state *state = fe->demodulator_priv;
+
+ i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT);
+ if (i2c_stat < 0)
+ goto err;
+
+ if (enable) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ...");
+ i2c_stat |= STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ } else {
+ dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ...");
+ i2c_stat &= ~STB0899_I2CTON;
+ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed");
+ return -EREMOTEIO;
+}
+
+
+static inline void CONVERT32(u32 x, char *str)
+{
+ *str++ = (x >> 24) & 0xff;
+ *str++ = (x >> 16) & 0xff;
+ *str++ = (x >> 8) & 0xff;
+ *str++ = (x >> 0) & 0xff;
+ *str = '\0';
+}
+
+int stb0899_get_dev_id(struct stb0899_state *state)
+{
+ u8 chip_id, release;
+ u16 id;
+ u32 demod_ver = 0, fec_ver = 0;
+ char demod_str[5] = { 0 };
+ char fec_str[5] = { 0 };
+
+ id = stb0899_read_reg(state, STB0899_DEV_ID);
+ dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id);
+ chip_id = STB0899_GETFIELD(CHIP_ID, id);
+ release = STB0899_GETFIELD(CHIP_REL, id);
+
+ dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]",
+ chip_id, release);
+
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str);
+
+ demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID);
+ dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
+ CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
+ fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
+ if (! (chip_id > 0)) {
+ dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
+
+ return -ENODEV;
+ }
+ dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver);
+
+ return 0;
+}
+
+static void stb0899_set_delivery(struct stb0899_state *state)
+{
+ u8 reg;
+ u8 stop_clk[2];
+
+ stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1);
+ stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S");
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x40);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x12);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ case SYS_DVBS2:
+ /* FECM/Viterbi OFF */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xb1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x42);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x40);
+ stb0899_write_reg(state, STB0899_TSLPL, 0x02);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 0);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0);
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0);
+ break;
+ case SYS_DSS:
+ /* FECM/Viterbi ON */
+ reg = stb0899_read_reg(state, STB0899_FECM);
+ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1);
+ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
+ stb0899_write_reg(state, STB0899_FECM, reg);
+
+ stb0899_write_reg(state, STB0899_RSULC, 0xa1);
+ stb0899_write_reg(state, STB0899_TSULC, 0x61);
+ stb0899_write_reg(state, STB0899_RSLLC, 0x42);
+
+ reg = stb0899_read_reg(state, STB0899_TSTRES);
+ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
+ stb0899_write_reg(state, STB0899_TSTRES, reg);
+
+ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
+ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
+ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
+
+ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
+
+ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ break;
+ }
+ STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0);
+ stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2);
+}
+
+/*
+ * stb0899_set_iterations
+ * set the LDPC iteration scale function
+ */
+static void stb0899_set_iterations(struct stb0899_state *state)
+{
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+
+ s32 iter_scale;
+ u32 reg;
+
+ iter_scale = 17 * (internal->master_clk / 1000);
+ iter_scale += 410000;
+ iter_scale /= (internal->srate / 1000000);
+ iter_scale /= 1000;
+
+ if (iter_scale > config->ldpc_max_iter)
+ iter_scale = config->ldpc_max_iter;
+
+ reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER);
+ STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
+ stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
+}
+
+static enum dvbfe_search stb0899_search(struct dvb_frontend *fe)
+{
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_params *i_params = &state->params;
+ struct stb0899_internal *internal = &state->internal;
+ struct stb0899_config *config = state->config;
+ struct dtv_frontend_properties *props = &fe->dtv_property_cache;
+
+ u32 SearchRange, gain;
+
+ i_params->freq = props->frequency;
+ i_params->srate = props->symbol_rate;
+ state->delsys = props->delivery_system;
+ dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys);
+
+ SearchRange = 10000000;
+ dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate);
+ /* checking Search Range is meaningless for a fixed 3 Mhz */
+ if (INRANGE(i_params->srate, 1000000, 45000000)) {
+ dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE");
+ stb0899_set_delivery(state);
+
+ if (state->config->tuner_set_rfsiggain) {
+ if (internal->srate > 15000000)
+ gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */
+ else if (internal->srate > 5000000)
+ gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */
+ else
+ gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */
+ state->config->tuner_set_rfsiggain(fe, gain);
+ }
+
+ if (i_params->srate <= 5000000)
+ stb0899_set_mclk(state, config->lo_clk);
+ else
+ stb0899_set_mclk(state, config->hi_clk);
+
+ switch (state->delsys) {
+ case SYS_DVBS:
+ case SYS_DSS:
+ dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system");
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ /*
+ * search = user search range +
+ * 500Khz +
+ * 2 * Tuner_step_size +
+ * 10% of the symbol rate
+ */
+ internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5);
+ internal->derot_percent = 30;
+
+ /* What to do for tuners having no bandwidth setup ? */
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10);
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+ /* Set DVB-S1 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo ..");
+ if (stb0899_dvbs_algo(state) == RANGEOK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status);
+// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ case SYS_DVBS2:
+ internal->freq = i_params->freq;
+ internal->srate = i_params->srate;
+ internal->srch_range = SearchRange;
+
+ /* enable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 1);
+
+ if (state->config->tuner_set_bandwidth)
+ state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange));
+ if (state->config->tuner_get_bandwidth)
+ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
+
+ /* disable tuner I/O */
+ stb0899_i2c_gate_ctrl(&state->frontend, 0);
+
+// pParams->SpectralInv = pSearch->IQ_Inversion;
+
+ /* Set DVB-S2 AGC */
+ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c);
+
+ /* Set IterScale =f(MCLK,SYMB) */
+ stb0899_set_iterations(state);
+
+ /* Run the search algorithm */
+ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo ..");
+ if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) {
+ internal->lock = 1;
+ dprintk(state->verbose, FE_DEBUG, 1,
+ "-------------------------------------> DVB-S2 LOCK !");
+
+// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */
+// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
+// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
+
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ internal->lock = 0;
+
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+ break;
+ default:
+ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
+ return DVBFE_ALGO_SEARCH_INVALID;
+ }
+ }
+
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static int stb0899_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct stb0899_state *state = fe->demodulator_priv;
+ struct stb0899_internal *internal = &state->internal;
+
+ dprintk(state->verbose, FE_DEBUG, 1, "Get params");
+ p->symbol_rate = internal->srate;
+ p->frequency = internal->freq;
+
+ return 0;
+}
+
+static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static struct dvb_frontend_ops stb0899_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
+ .info = {
+ .name = "STB0899 Multistandard",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 5000000,
+ .symbol_rate_max = 45000000,
+
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_2G_MODULATION |
+ FE_CAN_QPSK
+ },
+
+ .release = stb0899_release,
+ .init = stb0899_init,
+ .sleep = stb0899_sleep,
+// .wakeup = stb0899_wakeup,
+
+ .i2c_gate_ctrl = stb0899_i2c_gate_ctrl,
+
+ .get_frontend_algo = stb0899_frontend_algo,
+ .search = stb0899_search,
+ .get_frontend = stb0899_get_frontend,
+
+
+ .read_status = stb0899_read_status,
+ .read_snr = stb0899_read_snr,
+ .read_signal_strength = stb0899_read_signal_strength,
+ .read_ber = stb0899_read_ber,
+
+ .set_voltage = stb0899_set_voltage,
+ .set_tone = stb0899_set_tone,
+
+ .diseqc_send_master_cmd = stb0899_send_diseqc_msg,
+ .diseqc_recv_slave_reply = stb0899_recv_slave_reply,
+ .diseqc_send_burst = stb0899_send_diseqc_burst,
+};
+
+struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c)
+{
+ struct stb0899_state *state = NULL;
+ enum stb0899_inversion inversion;
+
+ state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ inversion = config->inversion;
+ state->verbose = &verbose;
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend.ops = stb0899_ops;
+ state->frontend.demodulator_priv = state;
+ state->internal.inversion = inversion;
+
+ stb0899_wakeup(&state->frontend);
+ if (stb0899_get_dev_id(state) == -ENODEV) {
+ printk("%s: Exiting .. !\n", __func__);
+ goto error;
+ }
+
+ printk("%s: Attaching STB0899 \n", __func__);
+ return &state->frontend;
+
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stb0899_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STB0899 Multi-Std frontend");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h
index 98b200ce0c34..98b200ce0c34 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.h
+++ b/drivers/media/dvb-frontends/stb0899_drv.h
diff --git a/drivers/media/dvb/frontends/stb0899_priv.h b/drivers/media/dvb-frontends/stb0899_priv.h
index 82395b912815..82395b912815 100644
--- a/drivers/media/dvb/frontends/stb0899_priv.h
+++ b/drivers/media/dvb-frontends/stb0899_priv.h
diff --git a/drivers/media/dvb/frontends/stb0899_reg.h b/drivers/media/dvb-frontends/stb0899_reg.h
index ba1ed56304a0..ba1ed56304a0 100644
--- a/drivers/media/dvb/frontends/stb0899_reg.h
+++ b/drivers/media/dvb-frontends/stb0899_reg.h
diff --git a/drivers/media/dvb/frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c
index a0c3c526b132..a0c3c526b132 100644
--- a/drivers/media/dvb/frontends/stb6000.c
+++ b/drivers/media/dvb-frontends/stb6000.c
diff --git a/drivers/media/dvb/frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h
index 7be479c22d5b..7be479c22d5b 100644
--- a/drivers/media/dvb/frontends/stb6000.h
+++ b/drivers/media/dvb-frontends/stb6000.h
diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c
index 2e93e65d2cdb..2e93e65d2cdb 100644
--- a/drivers/media/dvb/frontends/stb6100.c
+++ b/drivers/media/dvb-frontends/stb6100.c
diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h
index 2ab096614b3f..2ab096614b3f 100644
--- a/drivers/media/dvb/frontends/stb6100.h
+++ b/drivers/media/dvb-frontends/stb6100.h
diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h
index 6314d18c797a..6314d18c797a 100644
--- a/drivers/media/dvb/frontends/stb6100_cfg.h
+++ b/drivers/media/dvb-frontends/stb6100_cfg.h
diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb-frontends/stb6100_proc.h
index 112163a48622..112163a48622 100644
--- a/drivers/media/dvb/frontends/stb6100_proc.h
+++ b/drivers/media/dvb-frontends/stb6100_proc.h
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c
index 632b25156e4c..632b25156e4c 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb-frontends/stv0288.c
diff --git a/drivers/media/dvb/frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h
index f2b53db0606d..f2b53db0606d 100644
--- a/drivers/media/dvb/frontends/stv0288.h
+++ b/drivers/media/dvb-frontends/stv0288.h
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c
index d40f226160ef..d40f226160ef 100644
--- a/drivers/media/dvb/frontends/stv0297.c
+++ b/drivers/media/dvb-frontends/stv0297.c
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h
index 3f8f9468f387..3f8f9468f387 100644
--- a/drivers/media/dvb/frontends/stv0297.h
+++ b/drivers/media/dvb-frontends/stv0297.h
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c
index 057b5f8effc0..057b5f8effc0 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb-frontends/stv0299.c
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h
index ba219b767a69..ba219b767a69 100644
--- a/drivers/media/dvb/frontends/stv0299.h
+++ b/drivers/media/dvb-frontends/stv0299.h
diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c
index 2a8aaeb1112d..2a8aaeb1112d 100644
--- a/drivers/media/dvb/frontends/stv0367.c
+++ b/drivers/media/dvb-frontends/stv0367.c
diff --git a/drivers/media/dvb/frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h
index 93cc4a57eea0..93cc4a57eea0 100644
--- a/drivers/media/dvb/frontends/stv0367.h
+++ b/drivers/media/dvb-frontends/stv0367.h
diff --git a/drivers/media/dvb/frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h
index 995db0689ddd..995db0689ddd 100644
--- a/drivers/media/dvb/frontends/stv0367_priv.h
+++ b/drivers/media/dvb-frontends/stv0367_priv.h
diff --git a/drivers/media/dvb/frontends/stv0367_regs.h b/drivers/media/dvb-frontends/stv0367_regs.h
index a96fbdc7e25e..a96fbdc7e25e 100644
--- a/drivers/media/dvb/frontends/stv0367_regs.h
+++ b/drivers/media/dvb-frontends/stv0367_regs.h
diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb-frontends/stv0900.h
index 91c7ee8b2313..91c7ee8b2313 100644
--- a/drivers/media/dvb/frontends/stv0900.h
+++ b/drivers/media/dvb-frontends/stv0900.h
diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
index 7f1badaf0d03..7f1badaf0d03 100644
--- a/drivers/media/dvb/frontends/stv0900_core.c
+++ b/drivers/media/dvb-frontends/stv0900_core.c
diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb-frontends/stv0900_init.h
index b684df9995d8..b684df9995d8 100644
--- a/drivers/media/dvb/frontends/stv0900_init.h
+++ b/drivers/media/dvb-frontends/stv0900_init.h
diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb-frontends/stv0900_priv.h
index e0ea74c8e093..e0ea74c8e093 100644
--- a/drivers/media/dvb/frontends/stv0900_priv.h
+++ b/drivers/media/dvb-frontends/stv0900_priv.h
diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h
index 731afe93a823..731afe93a823 100644
--- a/drivers/media/dvb/frontends/stv0900_reg.h
+++ b/drivers/media/dvb-frontends/stv0900_reg.h
diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c
index 4af20780fb9c..4af20780fb9c 100644
--- a/drivers/media/dvb/frontends/stv0900_sw.c
+++ b/drivers/media/dvb-frontends/stv0900_sw.c
diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
new file mode 100644
index 000000000000..13caec013902
--- /dev/null
+++ b/drivers/media/dvb-frontends/stv090x.c
@@ -0,0 +1,4855 @@
+/*
+ STV0900/0903 Multistandard Broadcast Frontend driver
+ Copyright (C) Manu Abraham <abraham.manu@gmail.com>
+
+ Copyright (C) ST Microelectronics
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+#include "stv6110x.h" /* for demodulator internal modes */
+
+#include "stv090x_reg.h"
+#include "stv090x.h"
+#include "stv090x_priv.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+
+/* internal params node */
+struct stv090x_dev {
+ /* pointer for internal params, one for each pair of demods */
+ struct stv090x_internal *internal;
+ struct stv090x_dev *next_dev;
+};
+
+/* first internal params */
+static struct stv090x_dev *stv090x_first_dev;
+
+/* find chip by i2c adapter and i2c address */
+static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr)
+{
+ struct stv090x_dev *temp_dev = stv090x_first_dev;
+
+ /*
+ Search of the last stv0900 chip or
+ find it by i2c adapter and i2c address */
+ while ((temp_dev != NULL) &&
+ ((temp_dev->internal->i2c_adap != i2c_adap) ||
+ (temp_dev->internal->i2c_addr != i2c_addr))) {
+
+ temp_dev = temp_dev->next_dev;
+ }
+
+ return temp_dev;
+}
+
+/* deallocating chip */
+static void remove_dev(struct stv090x_internal *internal)
+{
+ struct stv090x_dev *prev_dev = stv090x_first_dev;
+ struct stv090x_dev *del_dev = find_dev(internal->i2c_adap,
+ internal->i2c_addr);
+
+ if (del_dev != NULL) {
+ if (del_dev == stv090x_first_dev) {
+ stv090x_first_dev = del_dev->next_dev;
+ } else {
+ while (prev_dev->next_dev != del_dev)
+ prev_dev = prev_dev->next_dev;
+
+ prev_dev->next_dev = del_dev->next_dev;
+ }
+
+ kfree(del_dev);
+ }
+}
+
+/* allocating new chip */
+static struct stv090x_dev *append_internal(struct stv090x_internal *internal)
+{
+ struct stv090x_dev *new_dev;
+ struct stv090x_dev *temp_dev;
+
+ new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL);
+ if (new_dev != NULL) {
+ new_dev->internal = internal;
+ new_dev->next_dev = NULL;
+
+ /* append to list */
+ if (stv090x_first_dev == NULL) {
+ stv090x_first_dev = new_dev;
+ } else {
+ temp_dev = stv090x_first_dev;
+ while (temp_dev->next_dev != NULL)
+ temp_dev = temp_dev->next_dev;
+
+ temp_dev->next_dev = new_dev;
+ }
+ }
+
+ return new_dev;
+}
+
+
+/* DVBS1 and DSS C/N Lookup table */
+static const struct stv090x_tab stv090x_s1cn_tab[] = {
+ { 0, 8917 }, /* 0.0dB */
+ { 5, 8801 }, /* 0.5dB */
+ { 10, 8667 }, /* 1.0dB */
+ { 15, 8522 }, /* 1.5dB */
+ { 20, 8355 }, /* 2.0dB */
+ { 25, 8175 }, /* 2.5dB */
+ { 30, 7979 }, /* 3.0dB */
+ { 35, 7763 }, /* 3.5dB */
+ { 40, 7530 }, /* 4.0dB */
+ { 45, 7282 }, /* 4.5dB */
+ { 50, 7026 }, /* 5.0dB */
+ { 55, 6781 }, /* 5.5dB */
+ { 60, 6514 }, /* 6.0dB */
+ { 65, 6241 }, /* 6.5dB */
+ { 70, 5965 }, /* 7.0dB */
+ { 75, 5690 }, /* 7.5dB */
+ { 80, 5424 }, /* 8.0dB */
+ { 85, 5161 }, /* 8.5dB */
+ { 90, 4902 }, /* 9.0dB */
+ { 95, 4654 }, /* 9.5dB */
+ { 100, 4417 }, /* 10.0dB */
+ { 105, 4186 }, /* 10.5dB */
+ { 110, 3968 }, /* 11.0dB */
+ { 115, 3757 }, /* 11.5dB */
+ { 120, 3558 }, /* 12.0dB */
+ { 125, 3366 }, /* 12.5dB */
+ { 130, 3185 }, /* 13.0dB */
+ { 135, 3012 }, /* 13.5dB */
+ { 140, 2850 }, /* 14.0dB */
+ { 145, 2698 }, /* 14.5dB */
+ { 150, 2550 }, /* 15.0dB */
+ { 160, 2283 }, /* 16.0dB */
+ { 170, 2042 }, /* 17.0dB */
+ { 180, 1827 }, /* 18.0dB */
+ { 190, 1636 }, /* 19.0dB */
+ { 200, 1466 }, /* 20.0dB */
+ { 210, 1315 }, /* 21.0dB */
+ { 220, 1181 }, /* 22.0dB */
+ { 230, 1064 }, /* 23.0dB */
+ { 240, 960 }, /* 24.0dB */
+ { 250, 869 }, /* 25.0dB */
+ { 260, 792 }, /* 26.0dB */
+ { 270, 724 }, /* 27.0dB */
+ { 280, 665 }, /* 28.0dB */
+ { 290, 616 }, /* 29.0dB */
+ { 300, 573 }, /* 30.0dB */
+ { 310, 537 }, /* 31.0dB */
+ { 320, 507 }, /* 32.0dB */
+ { 330, 483 }, /* 33.0dB */
+ { 400, 398 }, /* 40.0dB */
+ { 450, 381 }, /* 45.0dB */
+ { 500, 377 } /* 50.0dB */
+};
+
+/* DVBS2 C/N Lookup table */
+static const struct stv090x_tab stv090x_s2cn_tab[] = {
+ { -30, 13348 }, /* -3.0dB */
+ { -20, 12640 }, /* -2d.0B */
+ { -10, 11883 }, /* -1.0dB */
+ { 0, 11101 }, /* -0.0dB */
+ { 5, 10718 }, /* 0.5dB */
+ { 10, 10339 }, /* 1.0dB */
+ { 15, 9947 }, /* 1.5dB */
+ { 20, 9552 }, /* 2.0dB */
+ { 25, 9183 }, /* 2.5dB */
+ { 30, 8799 }, /* 3.0dB */
+ { 35, 8422 }, /* 3.5dB */
+ { 40, 8062 }, /* 4.0dB */
+ { 45, 7707 }, /* 4.5dB */
+ { 50, 7353 }, /* 5.0dB */
+ { 55, 7025 }, /* 5.5dB */
+ { 60, 6684 }, /* 6.0dB */
+ { 65, 6331 }, /* 6.5dB */
+ { 70, 6036 }, /* 7.0dB */
+ { 75, 5727 }, /* 7.5dB */
+ { 80, 5437 }, /* 8.0dB */
+ { 85, 5164 }, /* 8.5dB */
+ { 90, 4902 }, /* 9.0dB */
+ { 95, 4653 }, /* 9.5dB */
+ { 100, 4408 }, /* 10.0dB */
+ { 105, 4187 }, /* 10.5dB */
+ { 110, 3961 }, /* 11.0dB */
+ { 115, 3751 }, /* 11.5dB */
+ { 120, 3558 }, /* 12.0dB */
+ { 125, 3368 }, /* 12.5dB */
+ { 130, 3191 }, /* 13.0dB */
+ { 135, 3017 }, /* 13.5dB */
+ { 140, 2862 }, /* 14.0dB */
+ { 145, 2710 }, /* 14.5dB */
+ { 150, 2565 }, /* 15.0dB */
+ { 160, 2300 }, /* 16.0dB */
+ { 170, 2058 }, /* 17.0dB */
+ { 180, 1849 }, /* 18.0dB */
+ { 190, 1663 }, /* 19.0dB */
+ { 200, 1495 }, /* 20.0dB */
+ { 210, 1349 }, /* 21.0dB */
+ { 220, 1222 }, /* 22.0dB */
+ { 230, 1110 }, /* 23.0dB */
+ { 240, 1011 }, /* 24.0dB */
+ { 250, 925 }, /* 25.0dB */
+ { 260, 853 }, /* 26.0dB */
+ { 270, 789 }, /* 27.0dB */
+ { 280, 734 }, /* 28.0dB */
+ { 290, 690 }, /* 29.0dB */
+ { 300, 650 }, /* 30.0dB */
+ { 310, 619 }, /* 31.0dB */
+ { 320, 593 }, /* 32.0dB */
+ { 330, 571 }, /* 33.0dB */
+ { 400, 498 }, /* 40.0dB */
+ { 450, 484 }, /* 45.0dB */
+ { 500, 481 } /* 50.0dB */
+};
+
+/* RF level C/N lookup table */
+static const struct stv090x_tab stv090x_rf_tab[] = {
+ { -5, 0xcaa1 }, /* -5dBm */
+ { -10, 0xc229 }, /* -10dBm */
+ { -15, 0xbb08 }, /* -15dBm */
+ { -20, 0xb4bc }, /* -20dBm */
+ { -25, 0xad5a }, /* -25dBm */
+ { -30, 0xa298 }, /* -30dBm */
+ { -35, 0x98a8 }, /* -35dBm */
+ { -40, 0x8389 }, /* -40dBm */
+ { -45, 0x59be }, /* -45dBm */
+ { -50, 0x3a14 }, /* -50dBm */
+ { -55, 0x2d11 }, /* -55dBm */
+ { -60, 0x210d }, /* -60dBm */
+ { -65, 0xa14f }, /* -65dBm */
+ { -70, 0x07aa } /* -70dBm */
+};
+
+
+static struct stv090x_reg stv0900_initval[] = {
+
+ { STV090x_OUTCFG, 0x00 },
+ { STV090x_MODECFG, 0xff },
+ { STV090x_AGCRF1CFG, 0x11 },
+ { STV090x_AGCRF2CFG, 0x13 },
+ { STV090x_TSGENERAL1X, 0x14 },
+ { STV090x_TSTTNR2, 0x21 },
+ { STV090x_TSTTNR4, 0x21 },
+ { STV090x_P2_DISTXCTL, 0x22 },
+ { STV090x_P2_F22TX, 0xc0 },
+ { STV090x_P2_F22RX, 0xc0 },
+ { STV090x_P2_DISRXCTL, 0x00 },
+ { STV090x_P2_DMDCFGMD, 0xF9 },
+ { STV090x_P2_DEMOD, 0x08 },
+ { STV090x_P2_DMDCFG3, 0xc4 },
+ { STV090x_P2_CARFREQ, 0xed },
+ { STV090x_P2_LDT, 0xd0 },
+ { STV090x_P2_LDT2, 0xb8 },
+ { STV090x_P2_TMGCFG, 0xd2 },
+ { STV090x_P2_TMGTHRISE, 0x20 },
+ { STV090x_P1_TMGCFG, 0xd2 },
+
+ { STV090x_P2_TMGTHFALL, 0x00 },
+ { STV090x_P2_FECSPY, 0x88 },
+ { STV090x_P2_FSPYDATA, 0x3a },
+ { STV090x_P2_FBERCPT4, 0x00 },
+ { STV090x_P2_FSPYBER, 0x10 },
+ { STV090x_P2_ERRCTRL1, 0x35 },
+ { STV090x_P2_ERRCTRL2, 0xc1 },
+ { STV090x_P2_CFRICFG, 0xf8 },
+ { STV090x_P2_NOSCFG, 0x1c },
+ { STV090x_P2_DMDTOM, 0x20 },
+ { STV090x_P2_CORRELMANT, 0x70 },
+ { STV090x_P2_CORRELABS, 0x88 },
+ { STV090x_P2_AGC2O, 0x5b },
+ { STV090x_P2_AGC2REF, 0x38 },
+ { STV090x_P2_CARCFG, 0xe4 },
+ { STV090x_P2_ACLC, 0x1A },
+ { STV090x_P2_BCLC, 0x09 },
+ { STV090x_P2_CARHDR, 0x08 },
+ { STV090x_P2_KREFTMG, 0xc1 },
+ { STV090x_P2_SFRUPRATIO, 0xf0 },
+ { STV090x_P2_SFRLOWRATIO, 0x70 },
+ { STV090x_P2_SFRSTEP, 0x58 },
+ { STV090x_P2_TMGCFG2, 0x01 },
+ { STV090x_P2_CAR2CFG, 0x26 },
+ { STV090x_P2_BCLC2S2Q, 0x86 },
+ { STV090x_P2_BCLC2S28, 0x86 },
+ { STV090x_P2_SMAPCOEF7, 0x77 },
+ { STV090x_P2_SMAPCOEF6, 0x85 },
+ { STV090x_P2_SMAPCOEF5, 0x77 },
+ { STV090x_P2_TSCFGL, 0x20 },
+ { STV090x_P2_DMDCFG2, 0x3b },
+ { STV090x_P2_MODCODLST0, 0xff },
+ { STV090x_P2_MODCODLST1, 0xff },
+ { STV090x_P2_MODCODLST2, 0xff },
+ { STV090x_P2_MODCODLST3, 0xff },
+ { STV090x_P2_MODCODLST4, 0xff },
+ { STV090x_P2_MODCODLST5, 0xff },
+ { STV090x_P2_MODCODLST6, 0xff },
+ { STV090x_P2_MODCODLST7, 0xcc },
+ { STV090x_P2_MODCODLST8, 0xcc },
+ { STV090x_P2_MODCODLST9, 0xcc },
+ { STV090x_P2_MODCODLSTA, 0xcc },
+ { STV090x_P2_MODCODLSTB, 0xcc },
+ { STV090x_P2_MODCODLSTC, 0xcc },
+ { STV090x_P2_MODCODLSTD, 0xcc },
+ { STV090x_P2_MODCODLSTE, 0xcc },
+ { STV090x_P2_MODCODLSTF, 0xcf },
+ { STV090x_P1_DISTXCTL, 0x22 },
+ { STV090x_P1_F22TX, 0xc0 },
+ { STV090x_P1_F22RX, 0xc0 },
+ { STV090x_P1_DISRXCTL, 0x00 },
+ { STV090x_P1_DMDCFGMD, 0xf9 },
+ { STV090x_P1_DEMOD, 0x08 },
+ { STV090x_P1_DMDCFG3, 0xc4 },
+ { STV090x_P1_DMDTOM, 0x20 },
+ { STV090x_P1_CARFREQ, 0xed },
+ { STV090x_P1_LDT, 0xd0 },
+ { STV090x_P1_LDT2, 0xb8 },
+ { STV090x_P1_TMGCFG, 0xd2 },
+ { STV090x_P1_TMGTHRISE, 0x20 },
+ { STV090x_P1_TMGTHFALL, 0x00 },
+ { STV090x_P1_SFRUPRATIO, 0xf0 },
+ { STV090x_P1_SFRLOWRATIO, 0x70 },
+ { STV090x_P1_TSCFGL, 0x20 },
+ { STV090x_P1_FECSPY, 0x88 },
+ { STV090x_P1_FSPYDATA, 0x3a },
+ { STV090x_P1_FBERCPT4, 0x00 },
+ { STV090x_P1_FSPYBER, 0x10 },
+ { STV090x_P1_ERRCTRL1, 0x35 },
+ { STV090x_P1_ERRCTRL2, 0xc1 },
+ { STV090x_P1_CFRICFG, 0xf8 },
+ { STV090x_P1_NOSCFG, 0x1c },
+ { STV090x_P1_CORRELMANT, 0x70 },
+ { STV090x_P1_CORRELABS, 0x88 },
+ { STV090x_P1_AGC2O, 0x5b },
+ { STV090x_P1_AGC2REF, 0x38 },
+ { STV090x_P1_CARCFG, 0xe4 },
+ { STV090x_P1_ACLC, 0x1A },
+ { STV090x_P1_BCLC, 0x09 },
+ { STV090x_P1_CARHDR, 0x08 },
+ { STV090x_P1_KREFTMG, 0xc1 },
+ { STV090x_P1_SFRSTEP, 0x58 },
+ { STV090x_P1_TMGCFG2, 0x01 },
+ { STV090x_P1_CAR2CFG, 0x26 },
+ { STV090x_P1_BCLC2S2Q, 0x86 },
+ { STV090x_P1_BCLC2S28, 0x86 },
+ { STV090x_P1_SMAPCOEF7, 0x77 },
+ { STV090x_P1_SMAPCOEF6, 0x85 },
+ { STV090x_P1_SMAPCOEF5, 0x77 },
+ { STV090x_P1_DMDCFG2, 0x3b },
+ { STV090x_P1_MODCODLST0, 0xff },
+ { STV090x_P1_MODCODLST1, 0xff },
+ { STV090x_P1_MODCODLST2, 0xff },
+ { STV090x_P1_MODCODLST3, 0xff },
+ { STV090x_P1_MODCODLST4, 0xff },
+ { STV090x_P1_MODCODLST5, 0xff },
+ { STV090x_P1_MODCODLST6, 0xff },
+ { STV090x_P1_MODCODLST7, 0xcc },
+ { STV090x_P1_MODCODLST8, 0xcc },
+ { STV090x_P1_MODCODLST9, 0xcc },
+ { STV090x_P1_MODCODLSTA, 0xcc },
+ { STV090x_P1_MODCODLSTB, 0xcc },
+ { STV090x_P1_MODCODLSTC, 0xcc },
+ { STV090x_P1_MODCODLSTD, 0xcc },
+ { STV090x_P1_MODCODLSTE, 0xcc },
+ { STV090x_P1_MODCODLSTF, 0xcf },
+ { STV090x_GENCFG, 0x1d },
+ { STV090x_NBITER_NF4, 0x37 },
+ { STV090x_NBITER_NF5, 0x29 },
+ { STV090x_NBITER_NF6, 0x37 },
+ { STV090x_NBITER_NF7, 0x33 },
+ { STV090x_NBITER_NF8, 0x31 },
+ { STV090x_NBITER_NF9, 0x2f },
+ { STV090x_NBITER_NF10, 0x39 },
+ { STV090x_NBITER_NF11, 0x3a },
+ { STV090x_NBITER_NF12, 0x29 },
+ { STV090x_NBITER_NF13, 0x37 },
+ { STV090x_NBITER_NF14, 0x33 },
+ { STV090x_NBITER_NF15, 0x2f },
+ { STV090x_NBITER_NF16, 0x39 },
+ { STV090x_NBITER_NF17, 0x3a },
+ { STV090x_NBITERNOERR, 0x04 },
+ { STV090x_GAINLLR_NF4, 0x0C },
+ { STV090x_GAINLLR_NF5, 0x0F },
+ { STV090x_GAINLLR_NF6, 0x11 },
+ { STV090x_GAINLLR_NF7, 0x14 },
+ { STV090x_GAINLLR_NF8, 0x17 },
+ { STV090x_GAINLLR_NF9, 0x19 },
+ { STV090x_GAINLLR_NF10, 0x20 },
+ { STV090x_GAINLLR_NF11, 0x21 },
+ { STV090x_GAINLLR_NF12, 0x0D },
+ { STV090x_GAINLLR_NF13, 0x0F },
+ { STV090x_GAINLLR_NF14, 0x13 },
+ { STV090x_GAINLLR_NF15, 0x1A },
+ { STV090x_GAINLLR_NF16, 0x1F },
+ { STV090x_GAINLLR_NF17, 0x21 },
+ { STV090x_RCCFGH, 0x20 },
+ { STV090x_P1_FECM, 0x01 }, /* disable DSS modes */
+ { STV090x_P2_FECM, 0x01 }, /* disable DSS modes */
+ { STV090x_P1_PRVIT, 0x2F }, /* disable PR 6/7 */
+ { STV090x_P2_PRVIT, 0x2F }, /* disable PR 6/7 */
+};
+
+static struct stv090x_reg stv0903_initval[] = {
+ { STV090x_OUTCFG, 0x00 },
+ { STV090x_AGCRF1CFG, 0x11 },
+ { STV090x_STOPCLK1, 0x48 },
+ { STV090x_STOPCLK2, 0x14 },
+ { STV090x_TSTTNR1, 0x27 },
+ { STV090x_TSTTNR2, 0x21 },
+ { STV090x_P1_DISTXCTL, 0x22 },
+ { STV090x_P1_F22TX, 0xc0 },
+ { STV090x_P1_F22RX, 0xc0 },
+ { STV090x_P1_DISRXCTL, 0x00 },
+ { STV090x_P1_DMDCFGMD, 0xF9 },
+ { STV090x_P1_DEMOD, 0x08 },
+ { STV090x_P1_DMDCFG3, 0xc4 },
+ { STV090x_P1_CARFREQ, 0xed },
+ { STV090x_P1_TNRCFG2, 0x82 },
+ { STV090x_P1_LDT, 0xd0 },
+ { STV090x_P1_LDT2, 0xb8 },
+ { STV090x_P1_TMGCFG, 0xd2 },
+ { STV090x_P1_TMGTHRISE, 0x20 },
+ { STV090x_P1_TMGTHFALL, 0x00 },
+ { STV090x_P1_SFRUPRATIO, 0xf0 },
+ { STV090x_P1_SFRLOWRATIO, 0x70 },
+ { STV090x_P1_TSCFGL, 0x20 },
+ { STV090x_P1_FECSPY, 0x88 },
+ { STV090x_P1_FSPYDATA, 0x3a },
+ { STV090x_P1_FBERCPT4, 0x00 },
+ { STV090x_P1_FSPYBER, 0x10 },
+ { STV090x_P1_ERRCTRL1, 0x35 },
+ { STV090x_P1_ERRCTRL2, 0xc1 },
+ { STV090x_P1_CFRICFG, 0xf8 },
+ { STV090x_P1_NOSCFG, 0x1c },
+ { STV090x_P1_DMDTOM, 0x20 },
+ { STV090x_P1_CORRELMANT, 0x70 },
+ { STV090x_P1_CORRELABS, 0x88 },
+ { STV090x_P1_AGC2O, 0x5b },
+ { STV090x_P1_AGC2REF, 0x38 },
+ { STV090x_P1_CARCFG, 0xe4 },
+ { STV090x_P1_ACLC, 0x1A },
+ { STV090x_P1_BCLC, 0x09 },
+ { STV090x_P1_CARHDR, 0x08 },
+ { STV090x_P1_KREFTMG, 0xc1 },
+ { STV090x_P1_SFRSTEP, 0x58 },
+ { STV090x_P1_TMGCFG2, 0x01 },
+ { STV090x_P1_CAR2CFG, 0x26 },
+ { STV090x_P1_BCLC2S2Q, 0x86 },
+ { STV090x_P1_BCLC2S28, 0x86 },
+ { STV090x_P1_SMAPCOEF7, 0x77 },
+ { STV090x_P1_SMAPCOEF6, 0x85 },
+ { STV090x_P1_SMAPCOEF5, 0x77 },
+ { STV090x_P1_DMDCFG2, 0x3b },
+ { STV090x_P1_MODCODLST0, 0xff },
+ { STV090x_P1_MODCODLST1, 0xff },
+ { STV090x_P1_MODCODLST2, 0xff },
+ { STV090x_P1_MODCODLST3, 0xff },
+ { STV090x_P1_MODCODLST4, 0xff },
+ { STV090x_P1_MODCODLST5, 0xff },
+ { STV090x_P1_MODCODLST6, 0xff },
+ { STV090x_P1_MODCODLST7, 0xcc },
+ { STV090x_P1_MODCODLST8, 0xcc },
+ { STV090x_P1_MODCODLST9, 0xcc },
+ { STV090x_P1_MODCODLSTA, 0xcc },
+ { STV090x_P1_MODCODLSTB, 0xcc },
+ { STV090x_P1_MODCODLSTC, 0xcc },
+ { STV090x_P1_MODCODLSTD, 0xcc },
+ { STV090x_P1_MODCODLSTE, 0xcc },
+ { STV090x_P1_MODCODLSTF, 0xcf },
+ { STV090x_GENCFG, 0x1c },
+ { STV090x_NBITER_NF4, 0x37 },
+ { STV090x_NBITER_NF5, 0x29 },
+ { STV090x_NBITER_NF6, 0x37 },
+ { STV090x_NBITER_NF7, 0x33 },
+ { STV090x_NBITER_NF8, 0x31 },
+ { STV090x_NBITER_NF9, 0x2f },
+ { STV090x_NBITER_NF10, 0x39 },
+ { STV090x_NBITER_NF11, 0x3a },
+ { STV090x_NBITER_NF12, 0x29 },
+ { STV090x_NBITER_NF13, 0x37 },
+ { STV090x_NBITER_NF14, 0x33 },
+ { STV090x_NBITER_NF15, 0x2f },
+ { STV090x_NBITER_NF16, 0x39 },
+ { STV090x_NBITER_NF17, 0x3a },
+ { STV090x_NBITERNOERR, 0x04 },
+ { STV090x_GAINLLR_NF4, 0x0C },
+ { STV090x_GAINLLR_NF5, 0x0F },
+ { STV090x_GAINLLR_NF6, 0x11 },
+ { STV090x_GAINLLR_NF7, 0x14 },
+ { STV090x_GAINLLR_NF8, 0x17 },
+ { STV090x_GAINLLR_NF9, 0x19 },
+ { STV090x_GAINLLR_NF10, 0x20 },
+ { STV090x_GAINLLR_NF11, 0x21 },
+ { STV090x_GAINLLR_NF12, 0x0D },
+ { STV090x_GAINLLR_NF13, 0x0F },
+ { STV090x_GAINLLR_NF14, 0x13 },
+ { STV090x_GAINLLR_NF15, 0x1A },
+ { STV090x_GAINLLR_NF16, 0x1F },
+ { STV090x_GAINLLR_NF17, 0x21 },
+ { STV090x_RCCFGH, 0x20 },
+ { STV090x_P1_FECM, 0x01 }, /*disable the DSS mode */
+ { STV090x_P1_PRVIT, 0x2f } /*disable puncture rate 6/7*/
+};
+
+static struct stv090x_reg stv0900_cut20_val[] = {
+
+ { STV090x_P2_DMDCFG3, 0xe8 },
+ { STV090x_P2_DMDCFG4, 0x10 },
+ { STV090x_P2_CARFREQ, 0x38 },
+ { STV090x_P2_CARHDR, 0x20 },
+ { STV090x_P2_KREFTMG, 0x5a },
+ { STV090x_P2_SMAPCOEF7, 0x06 },
+ { STV090x_P2_SMAPCOEF6, 0x00 },
+ { STV090x_P2_SMAPCOEF5, 0x04 },
+ { STV090x_P2_NOSCFG, 0x0c },
+ { STV090x_P1_DMDCFG3, 0xe8 },
+ { STV090x_P1_DMDCFG4, 0x10 },
+ { STV090x_P1_CARFREQ, 0x38 },
+ { STV090x_P1_CARHDR, 0x20 },
+ { STV090x_P1_KREFTMG, 0x5a },
+ { STV090x_P1_SMAPCOEF7, 0x06 },
+ { STV090x_P1_SMAPCOEF6, 0x00 },
+ { STV090x_P1_SMAPCOEF5, 0x04 },
+ { STV090x_P1_NOSCFG, 0x0c },
+ { STV090x_GAINLLR_NF4, 0x21 },
+ { STV090x_GAINLLR_NF5, 0x21 },
+ { STV090x_GAINLLR_NF6, 0x20 },
+ { STV090x_GAINLLR_NF7, 0x1F },
+ { STV090x_GAINLLR_NF8, 0x1E },
+ { STV090x_GAINLLR_NF9, 0x1E },
+ { STV090x_GAINLLR_NF10, 0x1D },
+ { STV090x_GAINLLR_NF11, 0x1B },
+ { STV090x_GAINLLR_NF12, 0x20 },
+ { STV090x_GAINLLR_NF13, 0x20 },
+ { STV090x_GAINLLR_NF14, 0x20 },
+ { STV090x_GAINLLR_NF15, 0x20 },
+ { STV090x_GAINLLR_NF16, 0x20 },
+ { STV090x_GAINLLR_NF17, 0x21 },
+};
+
+static struct stv090x_reg stv0903_cut20_val[] = {
+ { STV090x_P1_DMDCFG3, 0xe8 },
+ { STV090x_P1_DMDCFG4, 0x10 },
+ { STV090x_P1_CARFREQ, 0x38 },
+ { STV090x_P1_CARHDR, 0x20 },
+ { STV090x_P1_KREFTMG, 0x5a },
+ { STV090x_P1_SMAPCOEF7, 0x06 },
+ { STV090x_P1_SMAPCOEF6, 0x00 },
+ { STV090x_P1_SMAPCOEF5, 0x04 },
+ { STV090x_P1_NOSCFG, 0x0c },
+ { STV090x_GAINLLR_NF4, 0x21 },
+ { STV090x_GAINLLR_NF5, 0x21 },
+ { STV090x_GAINLLR_NF6, 0x20 },
+ { STV090x_GAINLLR_NF7, 0x1F },
+ { STV090x_GAINLLR_NF8, 0x1E },
+ { STV090x_GAINLLR_NF9, 0x1E },
+ { STV090x_GAINLLR_NF10, 0x1D },
+ { STV090x_GAINLLR_NF11, 0x1B },
+ { STV090x_GAINLLR_NF12, 0x20 },
+ { STV090x_GAINLLR_NF13, 0x20 },
+ { STV090x_GAINLLR_NF14, 0x20 },
+ { STV090x_GAINLLR_NF15, 0x20 },
+ { STV090x_GAINLLR_NF16, 0x20 },
+ { STV090x_GAINLLR_NF17, 0x21 }
+};
+
+/* Cut 2.0 Long Frame Tracking CR loop */
+static struct stv090x_long_frame_crloop stv090x_s2_crl_cut20[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_QPSK_12, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x1e },
+ { STV090x_QPSK_35, 0x2f, 0x3f, 0x2e, 0x2f, 0x3d, 0x0f, 0x0e, 0x2e, 0x3d, 0x0e },
+ { STV090x_QPSK_23, 0x2f, 0x3f, 0x2e, 0x2f, 0x0e, 0x0f, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_QPSK_34, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_QPSK_45, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_QPSK_56, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_QPSK_89, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_QPSK_910, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
+ { STV090x_8PSK_35, 0x3c, 0x3e, 0x1c, 0x2e, 0x0c, 0x1e, 0x2b, 0x2d, 0x1b, 0x1d },
+ { STV090x_8PSK_23, 0x1d, 0x3e, 0x3c, 0x2e, 0x2c, 0x1e, 0x0c, 0x2d, 0x2b, 0x1d },
+ { STV090x_8PSK_34, 0x0e, 0x3e, 0x3d, 0x2e, 0x0d, 0x1e, 0x2c, 0x2d, 0x0c, 0x1d },
+ { STV090x_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d },
+ { STV090x_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d },
+ { STV090x_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }
+};
+
+/* Cut 3.0 Long Frame Tracking CR loop */
+static struct stv090x_long_frame_crloop stv090x_s2_crl_cut30[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_QPSK_12, 0x3c, 0x2c, 0x0c, 0x2c, 0x1b, 0x2c, 0x1b, 0x1c, 0x0b, 0x3b },
+ { STV090x_QPSK_35, 0x0d, 0x0d, 0x0c, 0x0d, 0x1b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
+ { STV090x_QPSK_23, 0x1d, 0x0d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
+ { STV090x_QPSK_34, 0x1d, 0x1d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
+ { STV090x_QPSK_45, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
+ { STV090x_QPSK_56, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
+ { STV090x_QPSK_89, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
+ { STV090x_QPSK_910, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
+ { STV090x_8PSK_35, 0x39, 0x29, 0x39, 0x19, 0x19, 0x19, 0x19, 0x19, 0x09, 0x19 },
+ { STV090x_8PSK_23, 0x2a, 0x39, 0x1a, 0x0a, 0x39, 0x0a, 0x29, 0x39, 0x29, 0x0a },
+ { STV090x_8PSK_34, 0x2b, 0x3a, 0x1b, 0x1b, 0x3a, 0x1b, 0x1a, 0x0b, 0x1a, 0x3a },
+ { STV090x_8PSK_56, 0x0c, 0x1b, 0x3b, 0x3b, 0x1b, 0x3b, 0x3a, 0x3b, 0x3a, 0x1b },
+ { STV090x_8PSK_89, 0x0d, 0x3c, 0x2c, 0x2c, 0x2b, 0x0c, 0x0b, 0x3b, 0x0b, 0x1b },
+ { STV090x_8PSK_910, 0x0d, 0x0d, 0x2c, 0x3c, 0x3b, 0x1c, 0x0b, 0x3b, 0x0b, 0x1b }
+};
+
+/* Cut 2.0 Long Frame Tracking CR Loop */
+static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut20[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_16APSK_23, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x0c },
+ { STV090x_16APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0c, 0x2d, 0x0c, 0x1d, 0x0c },
+ { STV090x_16APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c },
+ { STV090x_16APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c },
+ { STV090x_16APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c },
+ { STV090x_16APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c },
+ { STV090x_32APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
+ { STV090x_32APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
+ { STV090x_32APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
+ { STV090x_32APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
+ { STV090x_32APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }
+};
+
+/* Cut 3.0 Long Frame Tracking CR Loop */
+static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut30[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_16APSK_23, 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x0a, 0x3a, 0x0a, 0x2a, 0x0a },
+ { STV090x_16APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0a, 0x3b, 0x0a, 0x1b, 0x0a },
+ { STV090x_16APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a },
+ { STV090x_16APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a },
+ { STV090x_16APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a },
+ { STV090x_16APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a },
+ { STV090x_32APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
+ { STV090x_32APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
+ { STV090x_32APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
+ { STV090x_32APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
+ { STV090x_32APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }
+};
+
+static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut20[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_QPSK_14, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x2d, 0x1f, 0x3d, 0x3e },
+ { STV090x_QPSK_13, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x3d, 0x0f, 0x3d, 0x2e },
+ { STV090x_QPSK_25, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x2e }
+};
+
+static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut30[] = {
+ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
+ { STV090x_QPSK_14, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x2a, 0x1c, 0x3a, 0x3b },
+ { STV090x_QPSK_13, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x3a, 0x0c, 0x3a, 0x2b },
+ { STV090x_QPSK_25, 0x1c, 0x3c, 0x1b, 0x3c, 0x3a, 0x1c, 0x3a, 0x3b, 0x3a, 0x2b }
+};
+
+/* Cut 2.0 Short Frame Tracking CR Loop */
+static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut20[] = {
+ /* MODCOD 2M 5M 10M 20M 30M */
+ { STV090x_QPSK, 0x2f, 0x2e, 0x0e, 0x0e, 0x3d },
+ { STV090x_8PSK, 0x3e, 0x0e, 0x2d, 0x0d, 0x3c },
+ { STV090x_16APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d },
+ { STV090x_32APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d }
+};
+
+/* Cut 3.0 Short Frame Tracking CR Loop */
+static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut30[] = {
+ /* MODCOD 2M 5M 10M 20M 30M */
+ { STV090x_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A },
+ { STV090x_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 },
+ { STV090x_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A },
+ { STV090x_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }
+};
+
+static inline s32 comp2(s32 __x, s32 __width)
+{
+ if (__width == 32)
+ return __x;
+ else
+ return (__x >= (1 << (__width - 1))) ? (__x - (1 << __width)) : __x;
+}
+
+static int stv090x_read_reg(struct stv090x_state *state, unsigned int reg)
+{
+ const struct stv090x_config *config = state->config;
+ int ret;
+
+ u8 b0[] = { reg >> 8, reg & 0xff };
+ u8 buf;
+
+ struct i2c_msg msg[] = {
+ { .addr = config->address, .flags = 0, .buf = b0, .len = 2 },
+ { .addr = config->address, .flags = I2C_M_RD, .buf = &buf, .len = 1 }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret != 2) {
+ if (ret != -ERESTARTSYS)
+ dprintk(FE_ERROR, 1,
+ "Read error, Reg=[0x%02x], Status=%d",
+ reg, ret);
+
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+ if (unlikely(*state->verbose >= FE_DEBUGREG))
+ dprintk(FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
+ reg, buf);
+
+ return (unsigned int) buf;
+}
+
+static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8 *data, u32 count)
+{
+ const struct stv090x_config *config = state->config;
+ int ret;
+ u8 buf[2 + count];
+ struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count };
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+ memcpy(&buf[2], data, count);
+
+ if (unlikely(*state->verbose >= FE_DEBUGREG)) {
+ int i;
+
+ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
+ for (i = 0; i < count; i++)
+ printk(" %02x", data[i]);
+ printk("\n");
+ }
+
+ ret = i2c_transfer(state->i2c, &i2c_msg, 1);
+ if (ret != 1) {
+ if (ret != -ERESTARTSYS)
+ dprintk(FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
+ reg, data[0], count, ret);
+ return ret < 0 ? ret : -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 data)
+{
+ return stv090x_write_regs(state, reg, &data, 1);
+}
+
+static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable)
+{
+ u32 reg;
+
+ /*
+ * NOTE! A lock is used as a FSM to control the state in which
+ * access is serialized between two tuners on the same demod.
+ * This has nothing to do with a lock to protect a critical section
+ * which may in some other cases be confused with protecting I/O
+ * access to the demodulator gate.
+ * In case of any error, the lock is unlocked and exit within the
+ * relevant operations themselves.
+ */
+ 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) {
+ dprintk(FE_DEBUG, 1, "Enable Gate");
+ STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0)
+ goto err;
+
+ } else {
+ dprintk(FE_DEBUG, 1, "Disable Gate");
+ STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 0);
+ if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0)
+ goto err;
+ }
+
+ 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");
+ if (state->config->tuner_i2c_lock)
+ state->config->tuner_i2c_lock(&state->frontend, 0);
+ else
+ mutex_unlock(&state->internal->tuner_lock);
+ return -1;
+}
+
+static void stv090x_get_lock_tmg(struct stv090x_state *state)
+{
+ switch (state->algo) {
+ case STV090x_BLIND_SEARCH:
+ dprintk(FE_DEBUG, 1, "Blind Search");
+ if (state->srate <= 1500000) { /*10Msps< SR <=15Msps*/
+ state->DemodTimeout = 1500;
+ state->FecTimeout = 400;
+ } else if (state->srate <= 5000000) { /*10Msps< SR <=15Msps*/
+ state->DemodTimeout = 1000;
+ state->FecTimeout = 300;
+ } else { /*SR >20Msps*/
+ state->DemodTimeout = 700;
+ state->FecTimeout = 100;
+ }
+ break;
+
+ case STV090x_COLD_SEARCH:
+ case STV090x_WARM_SEARCH:
+ default:
+ dprintk(FE_DEBUG, 1, "Normal Search");
+ if (state->srate <= 1000000) { /*SR <=1Msps*/
+ state->DemodTimeout = 4500;
+ state->FecTimeout = 1700;
+ } else if (state->srate <= 2000000) { /*1Msps < SR <= 2Msps */
+ state->DemodTimeout = 2500;
+ state->FecTimeout = 1100;
+ } else if (state->srate <= 5000000) { /*2Msps < SR <= 5Msps */
+ state->DemodTimeout = 1000;
+ state->FecTimeout = 550;
+ } else if (state->srate <= 10000000) { /*5Msps < SR <= 10Msps */
+ state->DemodTimeout = 700;
+ state->FecTimeout = 250;
+ } else if (state->srate <= 20000000) { /*10Msps < SR <= 20Msps */
+ state->DemodTimeout = 400;
+ state->FecTimeout = 130;
+ } else { /*SR >20Msps*/
+ state->DemodTimeout = 300;
+ state->FecTimeout = 100;
+ }
+ break;
+ }
+
+ if (state->algo == STV090x_WARM_SEARCH)
+ state->DemodTimeout /= 2;
+}
+
+static int stv090x_set_srate(struct stv090x_state *state, u32 srate)
+{
+ u32 sym;
+
+ if (srate > 60000000) {
+ sym = (srate << 4); /* SR * 2^16 / master_clk */
+ sym /= (state->internal->mclk >> 12);
+ } else if (srate > 6000000) {
+ sym = (srate << 6);
+ sym /= (state->internal->mclk >> 10);
+ } else {
+ sym = (srate << 9);
+ sym /= (state->internal->mclk >> 7);
+ }
+
+ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT0, (sym & 0xff)) < 0) /* LSB */
+ goto err;
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate)
+{
+ u32 sym;
+
+ srate = 105 * (srate / 100);
+ if (srate > 60000000) {
+ sym = (srate << 4); /* SR * 2^16 / master_clk */
+ sym /= (state->internal->mclk >> 12);
+ } else if (srate > 6000000) {
+ sym = (srate << 6);
+ sym /= (state->internal->mclk >> 10);
+ } else {
+ sym = (srate << 9);
+ sym /= (state->internal->mclk >> 7);
+ }
+
+ if (sym < 0x7fff) {
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) /* MSB */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) /* LSB */
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x7f) < 0) /* MSB */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xff) < 0) /* LSB */
+ goto err;
+ }
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate)
+{
+ u32 sym;
+
+ srate = 95 * (srate / 100);
+ if (srate > 60000000) {
+ sym = (srate << 4); /* SR * 2^16 / master_clk */
+ sym /= (state->internal->mclk >> 12);
+ } else if (srate > 6000000) {
+ sym = (srate << 6);
+ sym /= (state->internal->mclk >> 10);
+ } else {
+ sym = (srate << 9);
+ sym /= (state->internal->mclk >> 7);
+ }
+
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */
+ goto err;
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static u32 stv090x_car_width(u32 srate, enum stv090x_rolloff rolloff)
+{
+ u32 ro;
+
+ switch (rolloff) {
+ case STV090x_RO_20:
+ ro = 20;
+ break;
+ case STV090x_RO_25:
+ ro = 25;
+ break;
+ case STV090x_RO_35:
+ default:
+ ro = 35;
+ break;
+ }
+
+ return srate + (srate * ro) / 100;
+}
+
+static int stv090x_set_vit_thacq(struct stv090x_state *state)
+{
+ if (STV090x_WRITE_DEMOD(state, VTH12, 0x96) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH23, 0x64) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH34, 0x36) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH56, 0x23) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH67, 0x1e) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH78, 0x19) < 0)
+ goto err;
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_vit_thtracq(struct stv090x_state *state)
+{
+ if (STV090x_WRITE_DEMOD(state, VTH12, 0xd0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH23, 0x7d) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH34, 0x53) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH56, 0x2f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH67, 0x24) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VTH78, 0x1f) < 0)
+ goto err;
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_viterbi(struct stv090x_state *state)
+{
+ switch (state->search_mode) {
+ case STV090x_SEARCH_AUTO:
+ if (STV090x_WRITE_DEMOD(state, FECM, 0x10) < 0) /* DVB-S and DVB-S2 */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x3f) < 0) /* all puncture rate */
+ goto err;
+ break;
+ case STV090x_SEARCH_DVBS1:
+ if (STV090x_WRITE_DEMOD(state, FECM, 0x00) < 0) /* disable DSS */
+ goto err;
+ switch (state->fec) {
+ case STV090x_PR12:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR23:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR34:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x04) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR56:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x08) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR78:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x20) < 0)
+ goto err;
+ break;
+
+ default:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x2f) < 0) /* all */
+ goto err;
+ break;
+ }
+ break;
+ case STV090x_SEARCH_DSS:
+ if (STV090x_WRITE_DEMOD(state, FECM, 0x80) < 0)
+ goto err;
+ switch (state->fec) {
+ case STV090x_PR12:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR23:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0)
+ goto err;
+ break;
+
+ case STV090x_PR67:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x10) < 0)
+ goto err;
+ break;
+
+ default:
+ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x13) < 0) /* 1/2, 2/3, 6/7 */
+ goto err;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_stop_modcod(struct stv090x_state *state)
+{
+ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xff) < 0)
+ goto err;
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_activate_modcod(struct stv090x_state *state)
+{
+ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xfc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0)
+ goto err;
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_activate_modcod_single(struct stv090x_state *state)
+{
+
+ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xf0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0x0f) < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable)
+{
+ u32 reg;
+
+ switch (state->demod) {
+ case STV090x_DEMODULATOR_0:
+ mutex_lock(&state->internal->demod_lock);
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ mutex_unlock(&state->internal->demod_lock);
+ break;
+
+ case STV090x_DEMODULATOR_1:
+ mutex_lock(&state->internal->demod_lock);
+ reg = stv090x_read_reg(state, STV090x_STOPCLK2);
+ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable);
+ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
+ goto err;
+ mutex_unlock(&state->internal->demod_lock);
+ break;
+
+ default:
+ dprintk(FE_ERROR, 1, "Wrong demodulator!");
+ break;
+ }
+ return 0;
+err:
+ mutex_unlock(&state->internal->demod_lock);
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_dvbs_track_crl(struct stv090x_state *state)
+{
+ if (state->internal->dev_ver >= 0x30) {
+ /* Set ACLC BCLC optimised value vs SR */
+ if (state->srate >= 15000000) {
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1a) < 0)
+ goto err;
+ } else if ((state->srate >= 7000000) && (15000000 > state->srate)) {
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0x0c) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1b) < 0)
+ goto err;
+ } else if (state->srate < 7000000) {
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0x2c) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1c) < 0)
+ goto err;
+ }
+
+ } else {
+ /* Cut 2.0 */
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_delivery_search(struct stv090x_state *state)
+{
+ u32 reg;
+
+ switch (state->search_mode) {
+ case STV090x_SEARCH_DVBS1:
+ case STV090x_SEARCH_DSS:
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ /* Activate Viterbi decoder in legacy search,
+ * do not use FRESVIT1, might impact VITERBI2
+ */
+ if (stv090x_vitclk_ctl(state, 0) < 0)
+ goto err;
+
+ if (stv090x_dvbs_track_crl(state) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x22) < 0) /* disable DVB-S2 */
+ goto err;
+
+ if (stv090x_set_vit_thacq(state) < 0)
+ goto err;
+ if (stv090x_set_viterbi(state) < 0)
+ goto err;
+ break;
+
+ case STV090x_SEARCH_DVBS2:
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ if (stv090x_vitclk_ctl(state, 1) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) /* stop DVB-S CR loop */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0)
+ goto err;
+
+ if (state->internal->dev_ver <= 0x20) {
+ /* enable S2 carrier loop */
+ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
+ goto err;
+ } else {
+ /* > Cut 3: Stop carrier 3 */
+ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0)
+ goto err;
+ }
+
+ if (state->demod_mode != STV090x_SINGLE) {
+ /* Cut 2: enable link during search */
+ if (stv090x_activate_modcod(state) < 0)
+ goto err;
+ } else {
+ /* Single demodulator
+ * Authorize SHORT and LONG frames,
+ * QPSK, 8PSK, 16APSK and 32APSK
+ */
+ if (stv090x_activate_modcod_single(state) < 0)
+ goto err;
+ }
+
+ if (stv090x_set_vit_thtracq(state) < 0)
+ goto err;
+ break;
+
+ case STV090x_SEARCH_AUTO:
+ default:
+ /* enable DVB-S2 and DVB-S2 in Auto MODE */
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ if (stv090x_vitclk_ctl(state, 0) < 0)
+ goto err;
+
+ if (stv090x_dvbs_track_crl(state) < 0)
+ goto err;
+
+ if (state->internal->dev_ver <= 0x20) {
+ /* enable S2 carrier loop */
+ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
+ goto err;
+ } else {
+ /* > Cut 3: Stop carrier 3 */
+ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0)
+ goto err;
+ }
+
+ if (state->demod_mode != STV090x_SINGLE) {
+ /* Cut 2: enable link during search */
+ if (stv090x_activate_modcod(state) < 0)
+ goto err;
+ } else {
+ /* Single demodulator
+ * Authorize SHORT and LONG frames,
+ * QPSK, 8PSK, 16APSK and 32APSK
+ */
+ if (stv090x_activate_modcod_single(state) < 0)
+ goto err;
+ }
+
+ if (stv090x_set_vit_thacq(state) < 0)
+ goto err;
+
+ if (stv090x_set_viterbi(state) < 0)
+ goto err;
+ break;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_start_search(struct stv090x_state *state)
+{
+ u32 reg, freq_abs;
+ s16 freq;
+
+ /* Reset demodulator */
+ reg = STV090x_READ_DEMOD(state, DMDISTATE);
+ STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f);
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
+ goto err;
+
+ if (state->internal->dev_ver <= 0x20) {
+ if (state->srate <= 5000000) {
+ if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0)
+ goto err;
+
+ /*enlarge the timing bandwidth for Low SR*/
+ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0)
+ goto err;
+ } else {
+ /* If the symbol rate is >5 Msps
+ Set The carrier search up and low to auto mode */
+ if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0)
+ goto err;
+ /*reduce the timing bandwidth for high SR*/
+ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0)
+ goto err;
+ }
+ } else {
+ /* >= Cut 3 */
+ if (state->srate <= 5000000) {
+ /* enlarge the timing bandwidth for Low SR */
+ STV090x_WRITE_DEMOD(state, RTCS2, 0x68);
+ } else {
+ /* reduce timing bandwidth for high SR */
+ STV090x_WRITE_DEMOD(state, RTCS2, 0x44);
+ }
+
+ /* Set CFR min and max to manual mode */
+ STV090x_WRITE_DEMOD(state, CARCFG, 0x46);
+
+ if (state->algo == STV090x_WARM_SEARCH) {
+ /* WARM Start
+ * CFR min = -1MHz,
+ * CFR max = +1MHz
+ */
+ freq_abs = 1000 << 16;
+ freq_abs /= (state->internal->mclk / 1000);
+ freq = (s16) freq_abs;
+ } else {
+ /* COLD Start
+ * CFR min =- (SearchRange / 2 + 600KHz)
+ * CFR max = +(SearchRange / 2 + 600KHz)
+ * (600KHz for the tuner step size)
+ */
+ freq_abs = (state->search_range / 2000) + 600;
+ freq_abs = freq_abs << 16;
+ freq_abs /= (state->internal->mclk / 1000);
+ freq = (s16) freq_abs;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0)
+ goto err;
+
+ freq *= -1;
+
+ if (STV090x_WRITE_DEMOD(state, CFRLOW1, MSB(freq)) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRLOW0, LSB(freq)) < 0)
+ goto err;
+
+ }
+
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
+ goto err;
+
+ if ((state->search_mode == STV090x_SEARCH_DVBS1) ||
+ (state->search_mode == STV090x_SEARCH_DSS) ||
+ (state->search_mode == STV090x_SEARCH_AUTO)) {
+
+ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0)
+ goto err;
+ }
+ }
+
+ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xe0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xc0) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ reg = STV090x_READ_DEMOD(state, DMDCFG2);
+ STV090x_SETFIELD_Px(reg, S1S2_SEQUENTIAL_FIELD, 0x0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x20) {
+ /*Frequency offset detector setting*/
+ if (state->srate < 2000000) {
+ if (state->internal->dev_ver <= 0x20) {
+ /* Cut 2 */
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0)
+ goto err;
+ } else {
+ /* Cut 3 */
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0)
+ goto err;
+ }
+ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0)
+ goto err;
+ } else if (state->srate < 10000000) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
+ goto err;
+ }
+ } else {
+ if (state->srate < 10000000) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xef) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xed) < 0)
+ goto err;
+ }
+ }
+
+ switch (state->algo) {
+ case STV090x_WARM_SEARCH:
+ /* The symbol rate and the exact
+ * carrier Frequency are known
+ */
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
+ goto err;
+ break;
+
+ case STV090x_COLD_SEARCH:
+ /* The symbol rate is known */
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
+ goto err;
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_get_agc2_min_level(struct stv090x_state *state)
+{
+ u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg;
+ s32 i, j, steps, dir;
+
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) /* SR = 65 Msps Max */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) /* SR= 400 ksps Min */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) /* stop acq @ coarse carrier state */
+ goto err;
+ if (stv090x_set_srate(state, 1000000) < 0)
+ goto err;
+
+ steps = state->search_range / 1000000;
+ if (steps <= 0)
+ steps = 1;
+
+ dir = 1;
+ freq_step = (1000000 * 256) / (state->internal->mclk / 256);
+ freq_init = 0;
+
+ for (i = 0; i < steps; i++) {
+ if (dir > 0)
+ freq_init = freq_init + (freq_step * i);
+ else
+ freq_init = freq_init - (freq_step * i);
+
+ dir *= -1;
+
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_init >> 8) & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_init & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */
+ goto err;
+ msleep(10);
+
+ agc2 = 0;
+ for (j = 0; j < 10; j++) {
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
+ }
+ agc2 /= 10;
+ if (agc2 < agc2_min)
+ agc2_min = agc2;
+ }
+
+ return agc2_min;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk)
+{
+ u8 r3, r2, r1, r0;
+ s32 srate, int_1, int_2, tmp_1, tmp_2;
+
+ r3 = STV090x_READ_DEMOD(state, SFR3);
+ r2 = STV090x_READ_DEMOD(state, SFR2);
+ r1 = STV090x_READ_DEMOD(state, SFR1);
+ r0 = STV090x_READ_DEMOD(state, SFR0);
+
+ srate = ((r3 << 24) | (r2 << 16) | (r1 << 8) | r0);
+
+ int_1 = clk >> 16;
+ int_2 = srate >> 16;
+
+ tmp_1 = clk % 0x10000;
+ tmp_2 = srate % 0x10000;
+
+ srate = (int_1 * int_2) +
+ ((int_1 * tmp_2) >> 16) +
+ ((int_2 * tmp_1) >> 16);
+
+ return srate;
+}
+
+static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
+{
+ struct dvb_frontend *fe = &state->frontend;
+
+ int tmg_lock = 0, i;
+ s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq;
+ u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg;
+ u32 agc2th;
+
+ if (state->internal->dev_ver >= 0x30)
+ agc2th = 0x2e00;
+ else
+ agc2th = 0x1f00;
+
+ reg = STV090x_READ_DEMOD(state, DMDISTATE);
+ STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0)
+ goto err;
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x30) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0)
+ goto err;
+
+ } else if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0)
+ goto err;
+ }
+
+ if (state->srate <= 2000000)
+ car_step = 1000;
+ else if (state->srate <= 5000000)
+ car_step = 2000;
+ else if (state->srate <= 12000000)
+ car_step = 3000;
+ else
+ car_step = 5000;
+
+ steps = -1 + ((state->search_range / 1000) / car_step);
+ steps /= 2;
+ steps = (2 * steps) + 1;
+ if (steps < 0)
+ steps = 1;
+ else if (steps > 10) {
+ steps = 11;
+ car_step = (state->search_range / 1000) / 10;
+ }
+ cur_step = 0;
+ dir = 1;
+ freq = state->frequency;
+
+ while ((!tmg_lock) && (cur_step < steps)) {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0)
+ goto err;
+ /* trigger acquisition */
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0)
+ goto err;
+ msleep(50);
+ for (i = 0; i < 10; i++) {
+ reg = STV090x_READ_DEMOD(state, DSTATUS);
+ if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
+ tmg_cpt++;
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
+ }
+ agc2 /= 10;
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
+ cur_step++;
+ dir *= -1;
+ if ((tmg_cpt >= 5) && (agc2 < agc2th) &&
+ (srate_coarse < 50000000) && (srate_coarse > 850000))
+ tmg_lock = 1;
+ else if (cur_step < steps) {
+ if (dir > 0)
+ freq += cur_step * car_step;
+ else
+ freq -= cur_step * car_step;
+
+ /* Setup tuner */
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_set_frequency) {
+ if (state->config->tuner_set_frequency(fe, freq) < 0)
+ goto err_gateoff;
+ }
+
+ if (state->config->tuner_set_bandwidth) {
+ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ msleep(50);
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_get_status) {
+ if (state->config->tuner_get_status(fe, &reg) < 0)
+ goto err_gateoff;
+ }
+
+ if (reg)
+ dprintk(FE_DEBUG, 1, "Tuner phase locked");
+ else
+ dprintk(FE_DEBUG, 1, "Tuner unlocked");
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ }
+ }
+ if (!tmg_lock)
+ srate_coarse = 0;
+ else
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
+
+ return srate_coarse;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
+{
+ u32 srate_coarse, freq_coarse, sym, reg;
+
+ srate_coarse = stv090x_get_srate(state, state->internal->mclk);
+ freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8;
+ freq_coarse |= STV090x_READ_DEMOD(state, CFR1);
+ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
+
+ if (sym < state->srate)
+ srate_coarse = 0;
+ else {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0)
+ goto err;
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x30) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0)
+ goto err;
+ } else if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
+ goto err;
+ }
+
+ if (srate_coarse > 3000000) {
+ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
+ sym = (sym / 1000) * 65536;
+ sym /= (state->internal->mclk / 1000);
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
+ goto err;
+ sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */
+ sym = (sym / 1000) * 65536;
+ sym /= (state->internal->mclk / 1000);
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
+ goto err;
+ sym = (srate_coarse / 1000) * 65536;
+ sym /= (state->internal->mclk / 1000);
+ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
+ goto err;
+ } else {
+ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
+ sym = (sym / 100) * 65536;
+ sym /= (state->internal->mclk / 100);
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
+ goto err;
+ sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */
+ sym = (sym / 100) * 65536;
+ sym /= (state->internal->mclk / 100);
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
+ goto err;
+ sym = (srate_coarse / 100) * 65536;
+ sym /= (state->internal->mclk / 100);
+ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
+ goto err;
+ }
+ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_coarse >> 8) & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_coarse & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) /* trigger acquisition */
+ goto err;
+ }
+
+ return srate_coarse;
+
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout)
+{
+ s32 timer = 0, lock = 0;
+ u32 reg;
+ u8 stat;
+
+ while ((timer < timeout) && (!lock)) {
+ reg = STV090x_READ_DEMOD(state, DMDSTATE);
+ stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
+
+ switch (stat) {
+ case 0: /* searching */
+ case 1: /* first PLH detected */
+ default:
+ dprintk(FE_DEBUG, 1, "Demodulator searching ..");
+ lock = 0;
+ break;
+ case 2: /* DVB-S2 mode */
+ case 3: /* DVB-S1/legacy mode */
+ reg = STV090x_READ_DEMOD(state, DSTATUS);
+ lock = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
+ break;
+ }
+
+ if (!lock)
+ msleep(10);
+ else
+ dprintk(FE_DEBUG, 1, "Demodulator acquired LOCK");
+
+ timer += 10;
+ }
+ return lock;
+}
+
+static int stv090x_blind_search(struct stv090x_state *state)
+{
+ u32 agc2, reg, srate_coarse;
+ s32 cpt_fail, agc2_ovflw, i;
+ u8 k_ref, k_max, k_min;
+ int coarse_fail = 0;
+ int lock;
+
+ k_max = 110;
+ k_min = 10;
+
+ agc2 = stv090x_get_agc2_min_level(state);
+
+ if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) {
+ lock = 0;
+ } else {
+
+ if (state->internal->dev_ver <= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0)
+ goto err;
+ } else {
+ /* > Cut 3 */
+ if (STV090x_WRITE_DEMOD(state, CARCFG, 0x06) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) /* set viterbi hysteresis */
+ goto err;
+ }
+
+ k_ref = k_max;
+ do {
+ if (STV090x_WRITE_DEMOD(state, KREFTMG, k_ref) < 0)
+ goto err;
+ if (stv090x_srate_srch_coarse(state) != 0) {
+ srate_coarse = stv090x_srate_srch_fine(state);
+ if (srate_coarse != 0) {
+ stv090x_get_lock_tmg(state);
+ lock = stv090x_get_dmdlock(state,
+ state->DemodTimeout);
+ } else {
+ lock = 0;
+ }
+ } else {
+ cpt_fail = 0;
+ agc2_ovflw = 0;
+ for (i = 0; i < 10; i++) {
+ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
+ STV090x_READ_DEMOD(state, AGC2I0);
+ if (agc2 >= 0xff00)
+ agc2_ovflw++;
+ reg = STV090x_READ_DEMOD(state, DSTATUS2);
+ if ((STV090x_GETFIELD_Px(reg, CFR_OVERFLOW_FIELD) == 0x01) &&
+ (STV090x_GETFIELD_Px(reg, DEMOD_DELOCK_FIELD) == 0x01))
+
+ cpt_fail++;
+ }
+ if ((cpt_fail > 7) || (agc2_ovflw > 7))
+ coarse_fail = 1;
+
+ lock = 0;
+ }
+ k_ref -= 20;
+ } while ((k_ref >= k_min) && (!lock) && (!coarse_fail));
+ }
+
+ return lock;
+
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_chk_tmg(struct stv090x_state *state)
+{
+ u32 reg;
+ s32 tmg_cpt = 0, i;
+ u8 freq, tmg_thh, tmg_thl;
+ int tmg_lock = 0;
+
+ freq = STV090x_READ_DEMOD(state, CARFREQ);
+ tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE);
+ tmg_thl = STV090x_READ_DEMOD(state, TMGTHFALL);
+ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); /* stop carrier offset search */
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, RTC, 0x80) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x40) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x00) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) /* set car ofset to 0 */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x65) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) /* trigger acquisition */
+ goto err;
+ msleep(10);
+
+ for (i = 0; i < 10; i++) {
+ reg = STV090x_READ_DEMOD(state, DSTATUS);
+ if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
+ tmg_cpt++;
+ msleep(1);
+ }
+ if (tmg_cpt >= 3)
+ tmg_lock = 1;
+
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) /* DVB-S1 timing */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) /* DVB-S2 timing */
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, freq) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, tmg_thh) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, tmg_thl) < 0)
+ goto err;
+
+ return tmg_lock;
+
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
+{
+ struct dvb_frontend *fe = &state->frontend;
+
+ u32 reg;
+ s32 car_step, steps, cur_step, dir, freq, timeout_lock;
+ int lock = 0;
+
+ if (state->srate >= 10000000)
+ timeout_lock = timeout_dmd / 3;
+ else
+ timeout_lock = timeout_dmd / 2;
+
+ lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */
+ if (!lock) {
+ if (state->srate >= 10000000) {
+ if (stv090x_chk_tmg(state)) {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
+ goto err;
+ lock = stv090x_get_dmdlock(state, timeout_dmd);
+ } else {
+ lock = 0;
+ }
+ } else {
+ if (state->srate <= 4000000)
+ car_step = 1000;
+ else if (state->srate <= 7000000)
+ car_step = 2000;
+ else if (state->srate <= 10000000)
+ car_step = 3000;
+ else
+ car_step = 5000;
+
+ steps = (state->search_range / 1000) / car_step;
+ steps /= 2;
+ steps = 2 * (steps + 1);
+ if (steps < 0)
+ steps = 2;
+ else if (steps > 12)
+ steps = 12;
+
+ cur_step = 1;
+ dir = 1;
+
+ if (!lock) {
+ freq = state->frequency;
+ state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate;
+ while ((cur_step <= steps) && (!lock)) {
+ if (dir > 0)
+ freq += cur_step * car_step;
+ else
+ freq -= cur_step * car_step;
+
+ /* Setup tuner */
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_set_frequency) {
+ if (state->config->tuner_set_frequency(fe, freq) < 0)
+ goto err_gateoff;
+ }
+
+ if (state->config->tuner_set_bandwidth) {
+ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ msleep(50);
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_get_status) {
+ if (state->config->tuner_get_status(fe, &reg) < 0)
+ goto err_gateoff;
+ }
+
+ if (reg)
+ dprintk(FE_DEBUG, 1, "Tuner phase locked");
+ else
+ dprintk(FE_DEBUG, 1, "Tuner unlocked");
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
+ goto err;
+ lock = stv090x_get_dmdlock(state, (timeout_dmd / 3));
+
+ dir *= -1;
+ cur_step++;
+ }
+ }
+ }
+ }
+
+ return lock;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s32 *timeout_sw, s32 *steps)
+{
+ s32 timeout, inc, steps_max, srate, car_max;
+
+ srate = state->srate;
+ car_max = state->search_range / 1000;
+ car_max += car_max / 10;
+ car_max = 65536 * (car_max / 2);
+ car_max /= (state->internal->mclk / 1000);
+
+ if (car_max > 0x4000)
+ car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */
+
+ inc = srate;
+ inc /= state->internal->mclk / 1000;
+ inc *= 256;
+ inc *= 256;
+ inc /= 1000;
+
+ switch (state->search_mode) {
+ case STV090x_SEARCH_DVBS1:
+ case STV090x_SEARCH_DSS:
+ inc *= 3; /* freq step = 3% of srate */
+ timeout = 20;
+ break;
+
+ case STV090x_SEARCH_DVBS2:
+ inc *= 4;
+ timeout = 25;
+ break;
+
+ case STV090x_SEARCH_AUTO:
+ default:
+ inc *= 3;
+ timeout = 25;
+ break;
+ }
+ inc /= 100;
+ if ((inc > car_max) || (inc < 0))
+ inc = car_max / 2; /* increment <= 1/8 Mclk */
+
+ timeout *= 27500; /* 27.5 Msps reference */
+ if (srate > 0)
+ timeout /= (srate / 1000);
+
+ if ((timeout > 100) || (timeout < 0))
+ timeout = 100;
+
+ steps_max = (car_max / inc) + 1; /* min steps = 3 */
+ if ((steps_max > 100) || (steps_max < 0)) {
+ steps_max = 100; /* max steps <= 100 */
+ inc = car_max / steps_max;
+ }
+ *freq_inc = inc;
+ *timeout_sw = timeout;
+ *steps = steps_max;
+
+ return 0;
+}
+
+static int stv090x_chk_signal(struct stv090x_state *state)
+{
+ s32 offst_car, agc2, car_max;
+ int no_signal;
+
+ offst_car = STV090x_READ_DEMOD(state, CFR2) << 8;
+ offst_car |= STV090x_READ_DEMOD(state, CFR1);
+ offst_car = comp2(offst_car, 16);
+
+ agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8;
+ agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
+ car_max = state->search_range / 1000;
+
+ car_max += (car_max / 10); /* 10% margin */
+ car_max = (65536 * car_max / 2);
+ car_max /= state->internal->mclk / 1000;
+
+ if (car_max > 0x4000)
+ car_max = 0x4000;
+
+ if ((agc2 > 0x2000) || (offst_car > 2 * car_max) || (offst_car < -2 * car_max)) {
+ no_signal = 1;
+ dprintk(FE_DEBUG, 1, "No Signal");
+ } else {
+ no_signal = 0;
+ dprintk(FE_DEBUG, 1, "Found Signal");
+ }
+
+ return no_signal;
+}
+
+static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 timeout, int zigzag, s32 steps_max)
+{
+ int no_signal, lock = 0;
+ s32 cpt_step = 0, offst_freq, car_max;
+ u32 reg;
+
+ car_max = state->search_range / 1000;
+ car_max += (car_max / 10);
+ car_max = (65536 * car_max / 2);
+ car_max /= (state->internal->mclk / 1000);
+ if (car_max > 0x4000)
+ car_max = 0x4000;
+
+ if (zigzag)
+ offst_freq = 0;
+ else
+ offst_freq = -car_max + inc;
+
+ do {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, ((offst_freq / 256) & 0xff)) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, offst_freq & 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x1); /* stop DVB-S2 packet delin */
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+
+ if (zigzag) {
+ if (offst_freq >= 0)
+ offst_freq = -offst_freq - 2 * inc;
+ else
+ offst_freq = -offst_freq;
+ } else {
+ offst_freq += 2 * inc;
+ }
+
+ cpt_step++;
+
+ lock = stv090x_get_dmdlock(state, timeout);
+ no_signal = stv090x_chk_signal(state);
+
+ } while ((!lock) &&
+ (!no_signal) &&
+ ((offst_freq - inc) < car_max) &&
+ ((offst_freq + inc) > -car_max) &&
+ (cpt_step < steps_max));
+
+ reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+
+ return lock;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_sw_algo(struct stv090x_state *state)
+{
+ int no_signal, zigzag, lock = 0;
+ u32 reg;
+
+ s32 dvbs2_fly_wheel;
+ s32 inc, timeout_step, trials, steps_max;
+
+ /* get params */
+ stv090x_get_loop_params(state, &inc, &timeout_step, &steps_max);
+
+ switch (state->search_mode) {
+ case STV090x_SEARCH_DVBS1:
+ case STV090x_SEARCH_DSS:
+ /* accelerate the frequency detector */
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x49) < 0)
+ goto err;
+ zigzag = 0;
+ break;
+
+ case STV090x_SEARCH_DVBS2:
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0)
+ goto err;
+ zigzag = 1;
+ break;
+
+ case STV090x_SEARCH_AUTO:
+ default:
+ /* accelerate the frequency detector */
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0xc9) < 0)
+ goto err;
+ zigzag = 0;
+ break;
+ }
+
+ trials = 0;
+ do {
+ lock = stv090x_search_car_loop(state, inc, timeout_step, zigzag, steps_max);
+ no_signal = stv090x_chk_signal(state);
+ trials++;
+
+ /*run the SW search 2 times maximum*/
+ if (lock || no_signal || (trials == 2)) {
+ /*Check if the demod is not losing lock in DVBS2*/
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
+ goto err;
+ }
+
+ reg = STV090x_READ_DEMOD(state, DMDSTATE);
+ if ((lock) && (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == STV090x_DVBS2)) {
+ /*Check if the demod is not losing lock in DVBS2*/
+ msleep(timeout_step);
+ reg = STV090x_READ_DEMOD(state, DMDFLYW);
+ dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD);
+ if (dvbs2_fly_wheel < 0xd) { /*if correct frames is decrementing */
+ msleep(timeout_step);
+ reg = STV090x_READ_DEMOD(state, DMDFLYW);
+ dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD);
+ }
+ if (dvbs2_fly_wheel < 0xd) {
+ /*FALSE lock, The demod is losing lock */
+ lock = 0;
+ if (trials < 2) {
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0)
+ goto err;
+ }
+ }
+ }
+ }
+ } while ((!lock) && (trials < 2) && (!no_signal));
+
+ return lock;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static enum stv090x_delsys stv090x_get_std(struct stv090x_state *state)
+{
+ u32 reg;
+ enum stv090x_delsys delsys;
+
+ reg = STV090x_READ_DEMOD(state, DMDSTATE);
+ if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 2)
+ delsys = STV090x_DVBS2;
+ else if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 3) {
+ reg = STV090x_READ_DEMOD(state, FECM);
+ if (STV090x_GETFIELD_Px(reg, DSS_DVB_FIELD) == 1)
+ delsys = STV090x_DSS;
+ else
+ delsys = STV090x_DVBS1;
+ } else {
+ delsys = STV090x_ERROR;
+ }
+
+ return delsys;
+}
+
+/* in Hz */
+static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk)
+{
+ s32 derot, int_1, int_2, tmp_1, tmp_2;
+
+ derot = STV090x_READ_DEMOD(state, CFR2) << 16;
+ derot |= STV090x_READ_DEMOD(state, CFR1) << 8;
+ derot |= STV090x_READ_DEMOD(state, CFR0);
+
+ derot = comp2(derot, 24);
+ int_1 = mclk >> 12;
+ int_2 = derot >> 12;
+
+ /* carrier_frequency = MasterClock * Reg / 2^24 */
+ tmp_1 = mclk % 0x1000;
+ tmp_2 = derot % 0x1000;
+
+ derot = (int_1 * int_2) +
+ ((int_1 * tmp_2) >> 12) +
+ ((int_2 * tmp_1) >> 12);
+
+ return derot;
+}
+
+static int stv090x_get_viterbi(struct stv090x_state *state)
+{
+ u32 reg, rate;
+
+ reg = STV090x_READ_DEMOD(state, VITCURPUN);
+ rate = STV090x_GETFIELD_Px(reg, VIT_CURPUN_FIELD);
+
+ switch (rate) {
+ case 13:
+ state->fec = STV090x_PR12;
+ break;
+
+ case 18:
+ state->fec = STV090x_PR23;
+ break;
+
+ case 21:
+ state->fec = STV090x_PR34;
+ break;
+
+ case 24:
+ state->fec = STV090x_PR56;
+ break;
+
+ case 25:
+ state->fec = STV090x_PR67;
+ break;
+
+ case 26:
+ state->fec = STV090x_PR78;
+ break;
+
+ default:
+ state->fec = STV090x_PRERR;
+ break;
+ }
+
+ return 0;
+}
+
+static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state)
+{
+ struct dvb_frontend *fe = &state->frontend;
+
+ u8 tmg;
+ u32 reg;
+ s32 i = 0, offst_freq;
+
+ msleep(5);
+
+ if (state->algo == STV090x_BLIND_SEARCH) {
+ tmg = STV090x_READ_DEMOD(state, TMGREG2);
+ STV090x_WRITE_DEMOD(state, SFRSTEP, 0x5c);
+ while ((i <= 50) && (tmg != 0) && (tmg != 0xff)) {
+ tmg = STV090x_READ_DEMOD(state, TMGREG2);
+ msleep(5);
+ i += 5;
+ }
+ }
+ state->delsys = stv090x_get_std(state);
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_get_frequency) {
+ if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000;
+ state->frequency += offst_freq;
+
+ if (stv090x_get_viterbi(state) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, DMDMODCOD);
+ state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD);
+ state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01;
+ state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1;
+ reg = STV090x_READ_DEMOD(state, TMGOBS);
+ state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD);
+ reg = STV090x_READ_DEMOD(state, FECM);
+ state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD);
+
+ if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) {
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_get_frequency) {
+ if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ if (abs(offst_freq) <= ((state->search_range / 2000) + 500))
+ return STV090x_RANGEOK;
+ else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000))
+ return STV090x_RANGEOK;
+ else
+ return STV090x_OUTOFRANGE; /* Out of Range */
+ } else {
+ if (abs(offst_freq) <= ((state->search_range / 2000) + 500))
+ return STV090x_RANGEOK;
+ else
+ return STV090x_OUTOFRANGE;
+ }
+
+ return STV090x_OUTOFRANGE;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static u32 stv090x_get_tmgoffst(struct stv090x_state *state, u32 srate)
+{
+ s32 offst_tmg;
+
+ offst_tmg = STV090x_READ_DEMOD(state, TMGREG2) << 16;
+ offst_tmg |= STV090x_READ_DEMOD(state, TMGREG1) << 8;
+ offst_tmg |= STV090x_READ_DEMOD(state, TMGREG0);
+
+ offst_tmg = comp2(offst_tmg, 24); /* 2's complement */
+ if (!offst_tmg)
+ offst_tmg = 1;
+
+ offst_tmg = ((s32) srate * 10) / ((s32) 0x1000000 / offst_tmg);
+ offst_tmg /= 320;
+
+ return offst_tmg;
+}
+
+static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_modcod modcod, s32 pilots)
+{
+ u8 aclc = 0x29;
+ s32 i;
+ struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low;
+
+ if (state->internal->dev_ver == 0x20) {
+ car_loop = stv090x_s2_crl_cut20;
+ car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20;
+ car_loop_apsk_low = stv090x_s2_apsk_crl_cut20;
+ } else {
+ /* >= Cut 3 */
+ car_loop = stv090x_s2_crl_cut30;
+ car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut30;
+ car_loop_apsk_low = stv090x_s2_apsk_crl_cut30;
+ }
+
+ if (modcod < STV090x_QPSK_12) {
+ i = 0;
+ while ((i < 3) && (modcod != car_loop_qpsk_low[i].modcod))
+ i++;
+
+ if (i >= 3)
+ i = 2;
+
+ } else {
+ i = 0;
+ while ((i < 14) && (modcod != car_loop[i].modcod))
+ i++;
+
+ if (i >= 14) {
+ i = 0;
+ while ((i < 11) && (modcod != car_loop_apsk_low[i].modcod))
+ i++;
+
+ if (i >= 11)
+ i = 10;
+ }
+ }
+
+ if (modcod <= STV090x_QPSK_25) {
+ if (pilots) {
+ if (state->srate <= 3000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_on_2;
+ else if (state->srate <= 7000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_on_5;
+ else if (state->srate <= 15000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_on_10;
+ else if (state->srate <= 25000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_on_20;
+ else
+ aclc = car_loop_qpsk_low[i].crl_pilots_on_30;
+ } else {
+ if (state->srate <= 3000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_off_2;
+ else if (state->srate <= 7000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_off_5;
+ else if (state->srate <= 15000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_off_10;
+ else if (state->srate <= 25000000)
+ aclc = car_loop_qpsk_low[i].crl_pilots_off_20;
+ else
+ aclc = car_loop_qpsk_low[i].crl_pilots_off_30;
+ }
+
+ } else if (modcod <= STV090x_8PSK_910) {
+ if (pilots) {
+ if (state->srate <= 3000000)
+ aclc = car_loop[i].crl_pilots_on_2;
+ else if (state->srate <= 7000000)
+ aclc = car_loop[i].crl_pilots_on_5;
+ else if (state->srate <= 15000000)
+ aclc = car_loop[i].crl_pilots_on_10;
+ else if (state->srate <= 25000000)
+ aclc = car_loop[i].crl_pilots_on_20;
+ else
+ aclc = car_loop[i].crl_pilots_on_30;
+ } else {
+ if (state->srate <= 3000000)
+ aclc = car_loop[i].crl_pilots_off_2;
+ else if (state->srate <= 7000000)
+ aclc = car_loop[i].crl_pilots_off_5;
+ else if (state->srate <= 15000000)
+ aclc = car_loop[i].crl_pilots_off_10;
+ else if (state->srate <= 25000000)
+ aclc = car_loop[i].crl_pilots_off_20;
+ else
+ aclc = car_loop[i].crl_pilots_off_30;
+ }
+ } else { /* 16APSK and 32APSK */
+ if (state->srate <= 3000000)
+ aclc = car_loop_apsk_low[i].crl_pilots_on_2;
+ else if (state->srate <= 7000000)
+ aclc = car_loop_apsk_low[i].crl_pilots_on_5;
+ else if (state->srate <= 15000000)
+ aclc = car_loop_apsk_low[i].crl_pilots_on_10;
+ else if (state->srate <= 25000000)
+ aclc = car_loop_apsk_low[i].crl_pilots_on_20;
+ else
+ aclc = car_loop_apsk_low[i].crl_pilots_on_30;
+ }
+
+ return aclc;
+}
+
+static u8 stv090x_optimize_carloop_short(struct stv090x_state *state)
+{
+ struct stv090x_short_frame_crloop *short_crl = NULL;
+ s32 index = 0;
+ u8 aclc = 0x0b;
+
+ switch (state->modulation) {
+ case STV090x_QPSK:
+ default:
+ index = 0;
+ break;
+ case STV090x_8PSK:
+ index = 1;
+ break;
+ case STV090x_16APSK:
+ index = 2;
+ break;
+ case STV090x_32APSK:
+ index = 3;
+ break;
+ }
+
+ if (state->internal->dev_ver >= 0x30) {
+ /* Cut 3.0 and up */
+ short_crl = stv090x_s2_short_crl_cut30;
+ } else {
+ /* Cut 2.0 and up: we don't support cuts older than 2.0 */
+ short_crl = stv090x_s2_short_crl_cut20;
+ }
+
+ if (state->srate <= 3000000)
+ aclc = short_crl[index].crl_2;
+ else if (state->srate <= 7000000)
+ aclc = short_crl[index].crl_5;
+ else if (state->srate <= 15000000)
+ aclc = short_crl[index].crl_10;
+ else if (state->srate <= 25000000)
+ aclc = short_crl[index].crl_20;
+ else
+ aclc = short_crl[index].crl_30;
+
+ return aclc;
+}
+
+static int stv090x_optimize_track(struct stv090x_state *state)
+{
+ struct dvb_frontend *fe = &state->frontend;
+
+ enum stv090x_modcod modcod;
+
+ s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0;
+ u32 reg;
+
+ srate = stv090x_get_srate(state, state->internal->mclk);
+ srate += stv090x_get_tmgoffst(state, srate);
+
+ switch (state->delsys) {
+ case STV090x_DVBS1:
+ case STV090x_DSS:
+ if (state->search_mode == STV090x_SEARCH_AUTO) {
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ }
+ reg = STV090x_READ_DEMOD(state, DEMOD);
+ STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff);
+ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x01);
+ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x30) {
+ if (stv090x_get_viterbi(state) < 0)
+ goto err;
+
+ if (state->fec == STV090x_PR12) {
+ if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x98) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x18) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0)
+ goto err;
+ }
+ }
+
+ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0)
+ goto err;
+ break;
+
+ case STV090x_DVBS2:
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 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);
+ pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01;
+ aclc = stv090x_optimize_carloop(state, modcod, pilots);
+ if (modcod <= STV090x_QPSK_910) {
+ STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc);
+ } else if (modcod <= STV090x_8PSK_910) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0)
+ goto err;
+ }
+ if ((state->demod_mode == STV090x_SINGLE) && (modcod > STV090x_8PSK_910)) {
+ if (modcod <= STV090x_16APSK_910) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0)
+ goto err;
+ }
+ }
+ } else {
+ /*Carrier loop setting for short frame*/
+ aclc = stv090x_optimize_carloop_short(state);
+ if (state->modulation == STV090x_QPSK) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc) < 0)
+ goto err;
+ } else if (state->modulation == STV090x_8PSK) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0)
+ goto err;
+ } else if (state->modulation == STV090x_16APSK) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0)
+ goto err;
+ } else if (state->modulation == STV090x_32APSK) {
+ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0)
+ goto err;
+ }
+ }
+
+ STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67); /* PER */
+ break;
+
+ case STV090x_ERROR:
+ default:
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
+ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ break;
+ }
+
+ f_1 = STV090x_READ_DEMOD(state, CFR2);
+ f_0 = STV090x_READ_DEMOD(state, CFR1);
+ reg = STV090x_READ_DEMOD(state, TMGOBS);
+
+ if (state->algo == STV090x_BLIND_SEARCH) {
+ STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00);
+ reg = STV090x_READ_DEMOD(state, DMDCFGMD);
+ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0x00);
+ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
+ goto err;
+
+ if (stv090x_set_srate(state, srate) < 0)
+ goto err;
+ blind_tune = 1;
+
+ if (stv090x_dvbs_track_crl(state) < 0)
+ goto err;
+ }
+
+ if (state->internal->dev_ver >= 0x20) {
+ if ((state->search_mode == STV090x_SEARCH_DVBS1) ||
+ (state->search_mode == STV090x_SEARCH_DSS) ||
+ (state->search_mode == STV090x_SEARCH_AUTO)) {
+
+ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x0a) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x00) < 0)
+ goto err;
+ }
+ }
+
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+
+ /* AUTO tracking MODE */
+ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x80) < 0)
+ goto err;
+ /* AUTO tracking MODE */
+ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0)
+ goto err;
+
+ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) ||
+ (state->srate < 10000000)) {
+ /* update initial carrier freq with the found freq offset */
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
+ goto err;
+ state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000;
+
+ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) {
+
+ if (state->algo != STV090x_WARM_SEARCH) {
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_set_bandwidth) {
+ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ }
+ }
+ if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000))
+ msleep(50); /* blind search: wait 50ms for SR stabilization */
+ else
+ msleep(5);
+
+ stv090x_get_lock_tmg(state);
+
+ if (!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) {
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
+ goto err;
+
+ i = 0;
+
+ while ((!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) && (i <= 2)) {
+
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
+ goto err;
+ i++;
+ }
+ }
+
+ }
+
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
+ goto err;
+ }
+
+ if ((state->delsys == STV090x_DVBS1) || (state->delsys == STV090x_DSS))
+ stv090x_set_vit_thtracq(state);
+
+ return 0;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_get_feclock(struct stv090x_state *state, s32 timeout)
+{
+ s32 timer = 0, lock = 0, stat;
+ u32 reg;
+
+ while ((timer < timeout) && (!lock)) {
+ reg = STV090x_READ_DEMOD(state, DMDSTATE);
+ stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
+
+ switch (stat) {
+ case 0: /* searching */
+ case 1: /* first PLH detected */
+ default:
+ lock = 0;
+ break;
+
+ case 2: /* DVB-S2 mode */
+ reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
+ lock = STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD);
+ break;
+
+ case 3: /* DVB-S1/legacy mode */
+ reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
+ lock = STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD);
+ break;
+ }
+ if (!lock) {
+ msleep(10);
+ timer += 10;
+ }
+ }
+ return lock;
+}
+
+static int stv090x_get_lock(struct stv090x_state *state, s32 timeout_dmd, s32 timeout_fec)
+{
+ u32 reg;
+ s32 timer = 0;
+ int lock;
+
+ lock = stv090x_get_dmdlock(state, timeout_dmd);
+ if (lock)
+ lock = stv090x_get_feclock(state, timeout_fec);
+
+ if (lock) {
+ lock = 0;
+
+ while ((timer < timeout_fec) && (!lock)) {
+ reg = STV090x_READ_DEMOD(state, TSSTATUS);
+ lock = STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD);
+ msleep(1);
+ timer++;
+ }
+ }
+
+ return lock;
+}
+
+static int stv090x_set_s2rolloff(struct stv090x_state *state)
+{
+ u32 reg;
+
+ if (state->internal->dev_ver <= 0x20) {
+ /* rolloff to auto mode if DVBS2 */
+ reg = STV090x_READ_DEMOD(state, DEMOD);
+ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
+ goto err;
+ } else {
+ /* DVB-S2 rolloff to auto mode if DVBS2 */
+ reg = STV090x_READ_DEMOD(state, DEMOD);
+ STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+
+static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
+{
+ struct dvb_frontend *fe = &state->frontend;
+ enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
+ u32 reg;
+ s32 agc1_power, power_iq = 0, i;
+ int lock = 0, low_sr = 0;
+
+ reg = STV090x_READ_DEMOD(state, TSCFGH);
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */
+ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */
+ goto err;
+
+ if (state->internal->dev_ver >= 0x20) {
+ if (state->srate > 5000000) {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0)
+ goto err;
+ }
+ }
+
+ stv090x_get_lock_tmg(state);
+
+ if (state->algo == STV090x_BLIND_SEARCH) {
+ state->tuner_bw = 2 * 36000000; /* wide bw for unknown srate */
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) /* wider srate scan */
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0)
+ goto err;
+ if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */
+ goto err;
+ } else {
+ /* known srate */
+ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0)
+ goto err;
+
+ if (state->srate < 2000000) {
+ /* SR < 2MSPS */
+ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x63) < 0)
+ goto err;
+ } else {
+ /* SR >= 2Msps */
+ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0)
+ goto err;
+ }
+
+ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
+ goto err;
+
+ if (state->internal->dev_ver >= 0x20) {
+ if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0)
+ goto err;
+ if (state->algo == STV090x_COLD_SEARCH)
+ state->tuner_bw = (15 * (stv090x_car_width(state->srate, state->rolloff) + 10000000)) / 10;
+ else if (state->algo == STV090x_WARM_SEARCH)
+ state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + 10000000;
+ }
+
+ /* if cold start or warm (Symbolrate is known)
+ * use a Narrow symbol rate scan range
+ */
+ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) /* narrow srate scan */
+ goto err;
+
+ if (stv090x_set_srate(state, state->srate) < 0)
+ goto err;
+
+ if (stv090x_set_max_srate(state, state->internal->mclk,
+ state->srate) < 0)
+ goto err;
+ if (stv090x_set_min_srate(state, state->internal->mclk,
+ state->srate) < 0)
+ goto err;
+
+ if (state->srate >= 10000000)
+ low_sr = 0;
+ else
+ low_sr = 1;
+ }
+
+ /* Setup tuner */
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (state->config->tuner_set_bbgain) {
+ reg = state->config->tuner_bbgain;
+ if (reg == 0)
+ reg = 10; /* default: 10dB */
+ if (state->config->tuner_set_bbgain(fe, reg) < 0)
+ goto err_gateoff;
+ }
+
+ if (state->config->tuner_set_frequency) {
+ if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
+ goto err_gateoff;
+ }
+
+ if (state->config->tuner_set_bandwidth) {
+ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ msleep(50);
+
+ if (state->config->tuner_get_status) {
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+ if (state->config->tuner_get_status(fe, &reg) < 0)
+ goto err_gateoff;
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ if (reg)
+ dprintk(FE_DEBUG, 1, "Tuner phase locked");
+ else {
+ dprintk(FE_DEBUG, 1, "Tuner unlocked");
+ return STV090x_NOCARRIER;
+ }
+ }
+
+ msleep(10);
+ agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1),
+ STV090x_READ_DEMOD(state, AGCIQIN0));
+
+ if (agc1_power == 0) {
+ /* If AGC1 integrator value is 0
+ * then read POWERI, POWERQ
+ */
+ for (i = 0; i < 5; i++) {
+ power_iq += (STV090x_READ_DEMOD(state, POWERI) +
+ STV090x_READ_DEMOD(state, POWERQ)) >> 1;
+ }
+ power_iq /= 5;
+ }
+
+ if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) {
+ dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq);
+ lock = 0;
+ signal_state = STV090x_NOAGC1;
+ } else {
+ reg = STV090x_READ_DEMOD(state, DEMOD);
+ STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion);
+
+ if (state->internal->dev_ver <= 0x20) {
+ /* rolloff to auto mode if DVBS2 */
+ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1);
+ } else {
+ /* DVB-S2 rolloff to auto mode if DVBS2 */
+ STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 1);
+ }
+ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
+ goto err;
+
+ if (stv090x_delivery_search(state) < 0)
+ goto err;
+
+ if (state->algo != STV090x_BLIND_SEARCH) {
+ if (stv090x_start_search(state) < 0)
+ goto err;
+ }
+ }
+
+ if (signal_state == STV090x_NOAGC1)
+ return signal_state;
+
+ if (state->algo == STV090x_BLIND_SEARCH)
+ lock = stv090x_blind_search(state);
+
+ else if (state->algo == STV090x_COLD_SEARCH)
+ lock = stv090x_get_coldlock(state, state->DemodTimeout);
+
+ else if (state->algo == STV090x_WARM_SEARCH)
+ lock = stv090x_get_dmdlock(state, state->DemodTimeout);
+
+ if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) {
+ if (!low_sr) {
+ if (stv090x_chk_tmg(state))
+ lock = stv090x_sw_algo(state);
+ }
+ }
+
+ if (lock)
+ signal_state = stv090x_get_sig_params(state);
+
+ if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */
+ stv090x_optimize_track(state);
+
+ if (state->internal->dev_ver >= 0x20) {
+ /* >= Cut 2.0 :release TS reset after
+ * demod lock and optimized Tracking
+ */
+ reg = STV090x_READ_DEMOD(state, TSCFGH);
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */
+ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
+ goto err;
+
+ msleep(3);
+
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* merger reset */
+ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
+ goto err;
+
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */
+ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
+ goto err;
+ }
+
+ lock = stv090x_get_lock(state, state->FecTimeout,
+ state->FecTimeout);
+ if (lock) {
+ if (state->delsys == STV090x_DVBS2) {
+ stv090x_set_s2rolloff(state);
+
+ reg = STV090x_READ_DEMOD(state, PDELCTRL2);
+ STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 1);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0)
+ goto err;
+ /* Reset DVBS2 packet delinator error counter */
+ reg = STV090x_READ_DEMOD(state, PDELCTRL2);
+ STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 0);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67) < 0) /* PER */
+ goto err;
+ } else {
+ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0)
+ goto err;
+ }
+ /* Reset the Total packet counter */
+ if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0x00) < 0)
+ goto err;
+ /* Reset the packet Error counter2 */
+ if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
+ goto err;
+ } else {
+ signal_state = STV090x_NODATA;
+ stv090x_chk_signal(state);
+ }
+ }
+ return signal_state;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_mis(struct stv090x_state *state, int mis)
+{
+ u32 reg;
+
+ if (mis < 0 || mis > 255) {
+ dprintk(FE_DEBUG, 1, "Disable MIS filtering");
+ reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+ STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+ } else {
+ dprintk(FE_DEBUG, 1, "Enable MIS filtering - %d", mis);
+ reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+ STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x01);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ISIENTRY, mis) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ISIBITENA, 0xff) < 0)
+ goto err;
+ }
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static enum dvbfe_search stv090x_search(struct dvb_frontend *fe)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *props = &fe->dtv_property_cache;
+
+ if (props->frequency == 0)
+ return DVBFE_ALGO_SEARCH_INVALID;
+
+ state->delsys = props->delivery_system;
+ state->frequency = props->frequency;
+ state->srate = props->symbol_rate;
+ state->search_mode = STV090x_SEARCH_AUTO;
+ state->algo = STV090x_COLD_SEARCH;
+ state->fec = STV090x_PRERR;
+ if (state->srate > 10000000) {
+ dprintk(FE_DEBUG, 1, "Search range: 10 MHz");
+ state->search_range = 10000000;
+ } else {
+ dprintk(FE_DEBUG, 1, "Search range: 5 MHz");
+ state->search_range = 5000000;
+ }
+
+ stv090x_set_mis(state, props->stream_id);
+
+ if (stv090x_algo(state) == STV090x_RANGEOK) {
+ dprintk(FE_DEBUG, 1, "Search success!");
+ return DVBFE_ALGO_SEARCH_SUCCESS;
+ } else {
+ dprintk(FE_DEBUG, 1, "Search failed!");
+ return DVBFE_ALGO_SEARCH_FAILED;
+ }
+
+ return DVBFE_ALGO_SEARCH_ERROR;
+}
+
+static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg, dstatus;
+ u8 search_state;
+
+ *status = 0;
+
+ dstatus = STV090x_READ_DEMOD(state, DSTATUS);
+ if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD))
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+
+ reg = STV090x_READ_DEMOD(state, DMDSTATE);
+ search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
+
+ switch (search_state) {
+ case 0: /* searching */
+ case 1: /* first PLH detected */
+ default:
+ dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)");
+ break;
+
+ case 2: /* DVB-S2 mode */
+ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
+ reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
+ if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
+ *status |= FE_HAS_VITERBI;
+ reg = STV090x_READ_DEMOD(state, TSSTATUS);
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ }
+ break;
+
+ case 3: /* DVB-S1/legacy mode */
+ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S");
+ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
+ reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
+ if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) {
+ *status |= FE_HAS_VITERBI;
+ reg = STV090x_READ_DEMOD(state, TSSTATUS);
+ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int stv090x_read_per(struct dvb_frontend *fe, u32 *per)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+
+ s32 count_4, count_3, count_2, count_1, count_0, count;
+ u32 reg, h, m, l;
+ enum fe_status status;
+
+ stv090x_read_status(fe, &status);
+ if (!(status & FE_HAS_LOCK)) {
+ *per = 1 << 23; /* Max PER */
+ } else {
+ /* Counter 2 */
+ reg = STV090x_READ_DEMOD(state, ERRCNT22);
+ h = STV090x_GETFIELD_Px(reg, ERR_CNT2_FIELD);
+
+ reg = STV090x_READ_DEMOD(state, ERRCNT21);
+ m = STV090x_GETFIELD_Px(reg, ERR_CNT21_FIELD);
+
+ reg = STV090x_READ_DEMOD(state, ERRCNT20);
+ l = STV090x_GETFIELD_Px(reg, ERR_CNT20_FIELD);
+
+ *per = ((h << 16) | (m << 8) | l);
+
+ count_4 = STV090x_READ_DEMOD(state, FBERCPT4);
+ count_3 = STV090x_READ_DEMOD(state, FBERCPT3);
+ count_2 = STV090x_READ_DEMOD(state, FBERCPT2);
+ count_1 = STV090x_READ_DEMOD(state, FBERCPT1);
+ count_0 = STV090x_READ_DEMOD(state, FBERCPT0);
+
+ if ((!count_4) && (!count_3)) {
+ count = (count_2 & 0xff) << 16;
+ count |= (count_1 & 0xff) << 8;
+ count |= count_0 & 0xff;
+ } else {
+ count = 1 << 24;
+ }
+ if (count == 0)
+ *per = 1;
+ }
+ if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
+ goto err;
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
+{
+ int res = 0;
+ int min = 0, med;
+
+ if ((val >= tab[min].read && val < tab[max].read) ||
+ (val >= tab[max].read && val < tab[min].read)) {
+ while ((max - min) > 1) {
+ med = (max + min) / 2;
+ if ((val >= tab[min].read && val < tab[med].read) ||
+ (val >= tab[med].read && val < tab[min].read))
+ max = med;
+ else
+ min = med;
+ }
+ res = ((val - tab[min].read) *
+ (tab[max].real - tab[min].real) /
+ (tab[max].read - tab[min].read)) +
+ tab[min].real;
+ } else {
+ if (tab[min].read < tab[max].read) {
+ if (val < tab[min].read)
+ res = tab[min].real;
+ else if (val >= tab[max].read)
+ res = tab[max].real;
+ } else {
+ if (val >= tab[min].read)
+ res = tab[min].real;
+ else if (val < tab[max].read)
+ res = tab[max].real;
+ }
+ }
+
+ return res;
+}
+
+static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg;
+ s32 agc_0, agc_1, agc;
+ s32 str;
+
+ reg = STV090x_READ_DEMOD(state, AGCIQIN1);
+ agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+ reg = STV090x_READ_DEMOD(state, AGCIQIN0);
+ agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
+ agc = MAKEWORD16(agc_1, agc_0);
+
+ str = stv090x_table_lookup(stv090x_rf_tab,
+ ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
+ if (agc > stv090x_rf_tab[0].read)
+ str = 0;
+ else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read)
+ str = -100;
+ *strength = (str + 100) * 0xFFFF / 100;
+
+ return 0;
+}
+
+static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg_0, reg_1, reg, i;
+ s32 val_0, val_1, val = 0;
+ u8 lock_f;
+ s32 div;
+ u32 last;
+
+ switch (state->delsys) {
+ case STV090x_DVBS2:
+ reg = STV090x_READ_DEMOD(state, DSTATUS);
+ lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
+ if (lock_f) {
+ msleep(5);
+ for (i = 0; i < 16; i++) {
+ reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1);
+ val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
+ reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0);
+ val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD);
+ val += MAKEWORD16(val_1, val_0);
+ msleep(1);
+ }
+ val /= 16;
+ last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
+ div = stv090x_s2cn_tab[0].read -
+ stv090x_s2cn_tab[last].read;
+ *cnr = 0xFFFF - ((val * 0xFFFF) / div);
+ }
+ break;
+
+ case STV090x_DVBS1:
+ case STV090x_DSS:
+ reg = STV090x_READ_DEMOD(state, DSTATUS);
+ lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
+ if (lock_f) {
+ msleep(5);
+ for (i = 0; i < 16; i++) {
+ reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1);
+ val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
+ reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0);
+ val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD);
+ val += MAKEWORD16(val_1, val_0);
+ msleep(1);
+ }
+ val /= 16;
+ last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
+ div = stv090x_s1cn_tab[0].read -
+ stv090x_s1cn_tab[last].read;
+ *cnr = 0xFFFF - ((val * 0xFFFF) / div);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int stv090x_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg;
+
+ reg = STV090x_READ_DEMOD(state, DISTXCTL);
+ switch (tone) {
+ case SEC_TONE_ON:
+ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+ break;
+
+ case SEC_TONE_OFF:
+ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0);
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+
+static enum dvbfe_algo stv090x_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_CUSTOM;
+}
+
+static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg, idle = 0, fifo_full = 1;
+ int i;
+
+ reg = STV090x_READ_DEMOD(state, DISTXCTL);
+
+ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD,
+ (state->config->diseqc_envelope_mode) ? 4 : 2);
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ for (i = 0; i < cmd->msg_len; i++) {
+
+ while (fifo_full) {
+ reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
+ fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD);
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DISTXDATA, cmd->msg[i]) < 0)
+ goto err;
+ }
+ reg = STV090x_READ_DEMOD(state, DISTXCTL);
+ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ i = 0;
+
+ while ((!idle) && (i < 10)) {
+ reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
+ idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD);
+ msleep(10);
+ i++;
+ }
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg, idle = 0, fifo_full = 1;
+ u8 mode, value;
+ int i;
+
+ reg = STV090x_READ_DEMOD(state, DISTXCTL);
+
+ if (burst == SEC_MINI_A) {
+ mode = (state->config->diseqc_envelope_mode) ? 5 : 3;
+ value = 0x00;
+ } else {
+ mode = (state->config->diseqc_envelope_mode) ? 4 : 2;
+ value = 0xFF;
+ }
+
+ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, mode);
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ while (fifo_full) {
+ reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
+ fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD);
+ }
+
+ if (STV090x_WRITE_DEMOD(state, DISTXDATA, value) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, DISTXCTL);
+ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0);
+ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
+ goto err;
+
+ i = 0;
+
+ while ((!idle) && (i < 10)) {
+ reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
+ idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD);
+ msleep(10);
+ i++;
+ }
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg = 0, i = 0, rx_end = 0;
+
+ while ((rx_end != 1) && (i < 10)) {
+ msleep(10);
+ i++;
+ reg = STV090x_READ_DEMOD(state, DISRX_ST0);
+ rx_end = STV090x_GETFIELD_Px(reg, RX_END_FIELD);
+ }
+
+ if (rx_end) {
+ reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD);
+ for (i = 0; i < reply->msg_len; i++)
+ reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA);
+ }
+
+ return 0;
+}
+
+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;
+
+ if (state->config->tuner_sleep) {
+ if (state->config->tuner_sleep(fe) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep",
+ state->device == STV0900 ? "STV0900" : "STV0903",
+ state->demod);
+
+ mutex_lock(&state->internal->demod_lock);
+
+ 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;
+}
+
+static int stv090x_wakeup(struct dvb_frontend *fe)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ u32 reg;
+
+ 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;
+
+ 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;
+}
+
+static void stv090x_release(struct dvb_frontend *fe)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+
+ state->internal->num_used--;
+ if (state->internal->num_used <= 0) {
+
+ dprintk(FE_ERROR, 1, "Actually removing");
+
+ remove_dev(state->internal);
+ kfree(state->internal);
+ }
+
+ kfree(state);
+}
+
+static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc_mode)
+{
+ u32 reg = 0;
+
+ reg = stv090x_read_reg(state, STV090x_GENCFG);
+
+ switch (ldpc_mode) {
+ case STV090x_DUAL:
+ default:
+ if ((state->demod_mode != STV090x_DUAL) || (STV090x_GETFIELD(reg, DDEMOD_FIELD) != 1)) {
+ /* set LDPC to dual mode */
+ if (stv090x_write_reg(state, STV090x_GENCFG, 0x1d) < 0)
+ goto err;
+
+ state->demod_mode = STV090x_DUAL;
+
+ reg = stv090x_read_reg(state, STV090x_TSTRES0);
+ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1);
+ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
+ goto err;
+ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0);
+ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0)
+ goto err;
+
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0)
+ goto err;
+ }
+ break;
+
+ case STV090x_SINGLE:
+ if (stv090x_stop_modcod(state) < 0)
+ goto err;
+ if (stv090x_activate_modcod_single(state) < 0)
+ goto err;
+
+ if (state->demod == STV090x_DEMODULATOR_1) {
+ if (stv090x_write_reg(state, STV090x_GENCFG, 0x06) < 0) /* path 2 */
+ goto err;
+ } else {
+ if (stv090x_write_reg(state, STV090x_GENCFG, 0x04) < 0) /* path 1 */
+ goto err;
+ }
+
+ reg = stv090x_read_reg(state, STV090x_TSTRES0);
+ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1);
+ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
+ goto err;
+ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0);
+ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, PDELCTRL1);
+ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x01);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x00);
+ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
+ goto err;
+ break;
+ }
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+/* return (Hz), clk in Hz*/
+static u32 stv090x_get_mclk(struct stv090x_state *state)
+{
+ const struct stv090x_config *config = state->config;
+ u32 div, reg;
+ u8 ratio;
+
+ div = stv090x_read_reg(state, STV090x_NCOARSE);
+ reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
+ ratio = STV090x_GETFIELD(reg, SELX1RATIO_FIELD) ? 4 : 6;
+
+ return (div + 1) * config->xtal / ratio; /* kHz */
+}
+
+static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk)
+{
+ const struct stv090x_config *config = state->config;
+ u32 reg, div, clk_sel;
+
+ reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
+ clk_sel = ((STV090x_GETFIELD(reg, SELX1RATIO_FIELD) == 1) ? 4 : 6);
+
+ div = ((clk_sel * mclk) / config->xtal) - 1;
+
+ reg = stv090x_read_reg(state, STV090x_NCOARSE);
+ STV090x_SETFIELD(reg, M_DIV_FIELD, div);
+ if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0)
+ goto err;
+
+ state->internal->mclk = stv090x_get_mclk(state);
+
+ /*Set the DiseqC frequency to 22KHz */
+ div = state->internal->mclk / 704000;
+ if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0)
+ goto err;
+ if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0)
+ goto err;
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_set_tspath(struct stv090x_state *state)
+{
+ u32 reg;
+
+ if (state->internal->dev_ver >= 0x20) {
+ switch (state->config->ts1_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ stv090x_write_reg(state, STV090x_TSGENERAL, 0x00);
+ break;
+
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0)
+ goto err;
+ break;
+ }
+ break;
+
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0)
+ goto err;
+ break;
+
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0a) < 0)
+ goto err;
+ break;
+ }
+ break;
+ }
+ } else {
+ switch (state->config->ts1_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10);
+ break;
+
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x16);
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+ goto err;
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 0);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0)
+ goto err;
+ break;
+ }
+ break;
+
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ default:
+ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14);
+ break;
+
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x12);
+ break;
+ }
+ break;
+ }
+ }
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ default:
+ break;
+ }
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ 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)
+ goto err;
+ break;
+
+ default:
+ break;
+ }
+
+ if (state->config->ts1_clk > 0) {
+ u32 speed;
+
+ switch (state->config->ts1_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ default:
+ speed = state->internal->mclk /
+ (state->config->ts1_clk / 4);
+ if (speed < 0x08)
+ speed = 0x08;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ speed = state->internal->mclk /
+ (state->config->ts1_clk / 32);
+ if (speed < 0x20)
+ speed = 0x20;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ }
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0)
+ goto err;
+ }
+
+ if (state->config->ts2_clk > 0) {
+ u32 speed;
+
+ switch (state->config->ts2_mode) {
+ case STV090x_TSMODE_PARALLEL_PUNCTURED:
+ case STV090x_TSMODE_DVBCI:
+ default:
+ speed = state->internal->mclk /
+ (state->config->ts2_clk / 4);
+ if (speed < 0x08)
+ speed = 0x08;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ case STV090x_TSMODE_SERIAL_PUNCTURED:
+ case STV090x_TSMODE_SERIAL_CONTINUOUS:
+ speed = state->internal->mclk /
+ (state->config->ts2_clk / 32);
+ if (speed < 0x20)
+ speed = 0x20;
+ if (speed > 0xFF)
+ speed = 0xFF;
+ break;
+ }
+ reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
+ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
+ if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0)
+ goto err;
+ }
+
+ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01);
+ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00);
+ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
+ goto err;
+
+ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
+ goto err;
+ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00);
+ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
+ goto err;
+
+ return 0;
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_init(struct dvb_frontend *fe)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ const struct stv090x_config *config = state->config;
+ u32 reg;
+
+ if (state->internal->mclk == 0) {
+ /* call tuner init to configure the tuner's clock output
+ divider directly before setting up the master clock of
+ the stv090x. */
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (config->tuner_init) {
+ if (config->tuner_init(fe) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */
+ msleep(5);
+ if (stv090x_write_reg(state, STV090x_SYNTCTRL,
+ 0x20 | config->clk_mode) < 0)
+ goto err;
+ stv090x_get_mclk(state);
+ }
+
+ if (stv090x_wakeup(fe) < 0) {
+ dprintk(FE_ERROR, 1, "Error waking device");
+ goto err;
+ }
+
+ if (stv090x_ldpc_mode(state, state->demod_mode) < 0)
+ goto err;
+
+ reg = STV090x_READ_DEMOD(state, TNRCFG2);
+ STV090x_SETFIELD_Px(reg, TUN_IQSWAP_FIELD, state->inversion);
+ if (STV090x_WRITE_DEMOD(state, TNRCFG2, reg) < 0)
+ goto err;
+ reg = STV090x_READ_DEMOD(state, DEMOD);
+ STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff);
+ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
+ goto err;
+
+ if (stv090x_i2c_gate_ctrl(state, 1) < 0)
+ goto err;
+
+ if (config->tuner_set_mode) {
+ if (config->tuner_set_mode(fe, TUNER_WAKE) < 0)
+ goto err_gateoff;
+ }
+
+ if (config->tuner_init) {
+ if (config->tuner_init(fe) < 0)
+ goto err_gateoff;
+ }
+
+ if (stv090x_i2c_gate_ctrl(state, 0) < 0)
+ goto err;
+
+ if (stv090x_set_tspath(state) < 0)
+ goto err;
+
+ return 0;
+
+err_gateoff:
+ stv090x_i2c_gate_ctrl(state, 0);
+err:
+ dprintk(FE_ERROR, 1, "I/O error");
+ return -1;
+}
+
+static int stv090x_setup(struct dvb_frontend *fe)
+{
+ struct stv090x_state *state = fe->demodulator_priv;
+ const struct stv090x_config *config = state->config;
+ const struct stv090x_reg *stv090x_initval = NULL;
+ const struct stv090x_reg *stv090x_cut20_val = NULL;
+ unsigned long t1_size = 0, t2_size = 0;
+ u32 reg = 0;
+
+ int i;
+
+ if (state->device == STV0900) {
+ dprintk(FE_DEBUG, 1, "Initializing STV0900");
+ stv090x_initval = stv0900_initval;
+ t1_size = ARRAY_SIZE(stv0900_initval);
+ stv090x_cut20_val = stv0900_cut20_val;
+ t2_size = ARRAY_SIZE(stv0900_cut20_val);
+ } else if (state->device == STV0903) {
+ dprintk(FE_DEBUG, 1, "Initializing STV0903");
+ stv090x_initval = stv0903_initval;
+ t1_size = ARRAY_SIZE(stv0903_initval);
+ stv090x_cut20_val = stv0903_cut20_val;
+ t2_size = ARRAY_SIZE(stv0903_cut20_val);
+ }
+
+ /* STV090x init */
+
+ /* Stop Demod */
+ if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0)
+ goto err;
+
+ msleep(5);
+
+ /* Set No Tuner Mode */
+ if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0)
+ goto err;
+
+ /* I2C repeater OFF */
+ STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level);
+ if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0)
+ goto err;
+
+ if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */
+ goto err;
+ msleep(5);
+ if (stv090x_write_reg(state, STV090x_I2CCFG, 0x08) < 0) /* 1/41 oversampling */
+ goto err;
+ if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) /* enable PLL */
+ goto err;
+ msleep(5);
+
+ /* write initval */
+ dprintk(FE_DEBUG, 1, "Setting up initial values");
+ for (i = 0; i < t1_size; i++) {
+ if (stv090x_write_reg(state, stv090x_initval[i].addr, stv090x_initval[i].data) < 0)
+ goto err;
+ }
+
+ state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID);
+ if (state->internal->dev_ver >= 0x20) {
+ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0)
+ goto err;
+
+ /* write cut20_val*/
+ dprintk(FE_DEBUG, 1, "Setting up Cut 2.0 initial values");
+ for (i = 0; i < t2_size; i++) {
+ if (stv090x_write_reg(state, stv090x_cut20_val[i].addr, stv090x_cut20_val[i].data) < 0)
+ goto err;
+ }
+
+ } else if (state->internal->dev_ver < 0x20) {
+ dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!",
+ state->internal->dev_ver);
+
+ goto err;
+ } else if (state->internal->dev_ver > 0x30) {
+ /* we shouldn't bail out from here */
+ dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!",
+ state->internal->dev_ver);
+ }
+
+ /* ADC1 range */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR1);
+ STV090x_SETFIELD(reg, ADC1_INMODE_FIELD,
+ (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
+ goto err;
+
+ /* ADC2 range */
+ reg = stv090x_read_reg(state, STV090x_TSTTNR3);
+ STV090x_SETFIELD(reg, ADC2_INMODE_FIELD,
+ (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1);
+ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0)
+ goto err;
+
+ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0)
+ goto err;
+ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0)
+ goto err;
+
+ 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 = {
+ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
+ .info = {
+ .name = "STV090x Multistandard",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 0,
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = stv090x_release,
+ .init = stv090x_init,
+
+ .sleep = stv090x_sleep,
+ .get_frontend_algo = stv090x_frontend_algo,
+
+ .diseqc_send_master_cmd = stv090x_send_diseqc_msg,
+ .diseqc_send_burst = stv090x_send_diseqc_burst,
+ .diseqc_recv_slave_reply = stv090x_recv_slave_reply,
+ .set_tone = stv090x_set_tone,
+
+ .search = stv090x_search,
+ .read_status = stv090x_read_status,
+ .read_ber = stv090x_read_per,
+ .read_signal_strength = stv090x_read_signal_strength,
+ .read_snr = stv090x_read_cnr,
+};
+
+
+struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
+ struct i2c_adapter *i2c,
+ enum stv090x_demodulator demod)
+{
+ struct stv090x_state *state = NULL;
+ struct stv090x_dev *temp_int;
+
+ state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->verbose = &verbose;
+ state->config = config;
+ state->i2c = i2c;
+ state->frontend.ops = stv090x_ops;
+ state->frontend.demodulator_priv = state;
+ state->demod = demod;
+ state->demod_mode = config->demod_mode; /* Single or Dual mode */
+ state->device = config->device;
+ state->rolloff = STV090x_RO_35; /* default */
+
+ temp_int = find_dev(state->i2c,
+ state->config->address);
+
+ if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) {
+ state->internal = temp_int->internal;
+ state->internal->num_used++;
+ dprintk(FE_INFO, 1, "Found Internal Structure!");
+ } 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);
+
+ if (stv090x_setup(&state->frontend) < 0) {
+ dprintk(FE_ERROR, 1, "Error setting up device");
+ goto err_remove;
+ }
+ }
+
+ if (state->internal->dev_ver >= 0x30)
+ state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM;
+
+ /* 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",
+ demod,
+ state->internal->dev_ver);
+
+ return &state->frontend;
+
+err_remove:
+ remove_dev(state->internal);
+ kfree(state->internal);
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(stv090x_attach);
+MODULE_PARM_DESC(verbose, "Set Verbosity level");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h
index 29cdc2b71314..29cdc2b71314 100644
--- a/drivers/media/dvb/frontends/stv090x.h
+++ b/drivers/media/dvb-frontends/stv090x.h
diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb-frontends/stv090x_priv.h
index 5b780c80d496..5b780c80d496 100644
--- a/drivers/media/dvb/frontends/stv090x_priv.h
+++ b/drivers/media/dvb-frontends/stv090x_priv.h
diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb-frontends/stv090x_reg.h
index 93741ee14297..93741ee14297 100644
--- a/drivers/media/dvb/frontends/stv090x_reg.h
+++ b/drivers/media/dvb-frontends/stv090x_reg.h
diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c
index 20b5fa92c53e..20b5fa92c53e 100644
--- a/drivers/media/dvb/frontends/stv6110.c
+++ b/drivers/media/dvb-frontends/stv6110.c
diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb-frontends/stv6110.h
index fe71bba6a26e..fe71bba6a26e 100644
--- a/drivers/media/dvb/frontends/stv6110.h
+++ b/drivers/media/dvb-frontends/stv6110.h
diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c
index f36cab12bdc7..f36cab12bdc7 100644
--- a/drivers/media/dvb/frontends/stv6110x.c
+++ b/drivers/media/dvb-frontends/stv6110x.c
diff --git a/drivers/media/dvb/frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h
index 47516753929a..47516753929a 100644
--- a/drivers/media/dvb/frontends/stv6110x.h
+++ b/drivers/media/dvb-frontends/stv6110x.h
diff --git a/drivers/media/dvb/frontends/stv6110x_priv.h b/drivers/media/dvb-frontends/stv6110x_priv.h
index 0ec936a660a7..0ec936a660a7 100644
--- a/drivers/media/dvb/frontends/stv6110x_priv.h
+++ b/drivers/media/dvb-frontends/stv6110x_priv.h
diff --git a/drivers/media/dvb/frontends/stv6110x_reg.h b/drivers/media/dvb-frontends/stv6110x_reg.h
index 93e5c70e5fd8..93e5c70e5fd8 100644
--- a/drivers/media/dvb/frontends/stv6110x_reg.h
+++ b/drivers/media/dvb-frontends/stv6110x_reg.h
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c
index 1bff7f457e19..1bff7f457e19 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb-frontends/tda10021.c
diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c
index ca1e0d54b69a..ca1e0d54b69a 100644
--- a/drivers/media/dvb/frontends/tda10023.c
+++ b/drivers/media/dvb-frontends/tda10023.c
diff --git a/drivers/media/dvb/frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h
index 04d19418bf20..04d19418bf20 100644
--- a/drivers/media/dvb/frontends/tda1002x.h
+++ b/drivers/media/dvb-frontends/tda1002x.h
diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c
index 71fb63299de7..71fb63299de7 100644
--- a/drivers/media/dvb/frontends/tda10048.c
+++ b/drivers/media/dvb-frontends/tda10048.c
diff --git a/drivers/media/dvb/frontends/tda10048.h b/drivers/media/dvb-frontends/tda10048.h
index fb2ef5ac9487..fb2ef5ac9487 100644
--- a/drivers/media/dvb/frontends/tda10048.h
+++ b/drivers/media/dvb-frontends/tda10048.h
diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c
new file mode 100644
index 000000000000..a2631be7ffac
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda1004x.c
@@ -0,0 +1,1385 @@
+ /*
+ Driver for Philips tda1004xh OFDM Demodulator
+
+ (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You 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 driver needs external firmware. Please use the commands
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
+ * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
+ * download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ * or /lib/firmware (depending on configuration of firmware hotplug).
+ */
+#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"
+#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda1004x.h"
+
+static int debug;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda1004x: " args); \
+ } while (0)
+
+#define TDA1004X_CHIPID 0x00
+#define TDA1004X_AUTO 0x01
+#define TDA1004X_IN_CONF1 0x02
+#define TDA1004X_IN_CONF2 0x03
+#define TDA1004X_OUT_CONF1 0x04
+#define TDA1004X_OUT_CONF2 0x05
+#define TDA1004X_STATUS_CD 0x06
+#define TDA1004X_CONFC4 0x07
+#define TDA1004X_DSSPARE2 0x0C
+#define TDA10045H_CODE_IN 0x0D
+#define TDA10045H_FWPAGE 0x0E
+#define TDA1004X_SCAN_CPT 0x10
+#define TDA1004X_DSP_CMD 0x11
+#define TDA1004X_DSP_ARG 0x12
+#define TDA1004X_DSP_DATA1 0x13
+#define TDA1004X_DSP_DATA2 0x14
+#define TDA1004X_CONFADC1 0x15
+#define TDA1004X_CONFC1 0x16
+#define TDA10045H_S_AGC 0x1a
+#define TDA10046H_AGC_TUN_LEVEL 0x1a
+#define TDA1004X_SNR 0x1c
+#define TDA1004X_CONF_TS1 0x1e
+#define TDA1004X_CONF_TS2 0x1f
+#define TDA1004X_CBER_RESET 0x20
+#define TDA1004X_CBER_MSB 0x21
+#define TDA1004X_CBER_LSB 0x22
+#define TDA1004X_CVBER_LUT 0x23
+#define TDA1004X_VBER_MSB 0x24
+#define TDA1004X_VBER_MID 0x25
+#define TDA1004X_VBER_LSB 0x26
+#define TDA1004X_UNCOR 0x27
+
+#define TDA10045H_CONFPLL_P 0x2D
+#define TDA10045H_CONFPLL_M_MSB 0x2E
+#define TDA10045H_CONFPLL_M_LSB 0x2F
+#define TDA10045H_CONFPLL_N 0x30
+
+#define TDA10046H_CONFPLL1 0x2D
+#define TDA10046H_CONFPLL2 0x2F
+#define TDA10046H_CONFPLL3 0x30
+#define TDA10046H_TIME_WREF1 0x31
+#define TDA10046H_TIME_WREF2 0x32
+#define TDA10046H_TIME_WREF3 0x33
+#define TDA10046H_TIME_WREF4 0x34
+#define TDA10046H_TIME_WREF5 0x35
+
+#define TDA10045H_UNSURW_MSB 0x31
+#define TDA10045H_UNSURW_LSB 0x32
+#define TDA10045H_WREF_MSB 0x33
+#define TDA10045H_WREF_MID 0x34
+#define TDA10045H_WREF_LSB 0x35
+#define TDA10045H_MUXOUT 0x36
+#define TDA1004X_CONFADC2 0x37
+
+#define TDA10045H_IOFFSET 0x38
+
+#define TDA10046H_CONF_TRISTATE1 0x3B
+#define TDA10046H_CONF_TRISTATE2 0x3C
+#define TDA10046H_CONF_POLARITY 0x3D
+#define TDA10046H_FREQ_OFFSET 0x3E
+#define TDA10046H_GPIO_OUT_SEL 0x41
+#define TDA10046H_GPIO_SELECT 0x42
+#define TDA10046H_AGC_CONF 0x43
+#define TDA10046H_AGC_THR 0x44
+#define TDA10046H_AGC_RENORM 0x45
+#define TDA10046H_AGC_GAINS 0x46
+#define TDA10046H_AGC_TUN_MIN 0x47
+#define TDA10046H_AGC_TUN_MAX 0x48
+#define TDA10046H_AGC_IF_MIN 0x49
+#define TDA10046H_AGC_IF_MAX 0x4A
+
+#define TDA10046H_FREQ_PHY2_MSB 0x4D
+#define TDA10046H_FREQ_PHY2_LSB 0x4E
+
+#define TDA10046H_CVBER_CTRL 0x4F
+#define TDA10046H_AGC_IF_LEVEL 0x52
+#define TDA10046H_CODE_CPT 0x57
+#define TDA10046H_CODE_IN 0x58
+
+
+static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
+{
+ int ret;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
+
+ dprintk("%s: reg=0x%x, data=0x%x\n", __func__, reg, data);
+
+ msg.addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+ __func__, reg, data, ret);
+
+ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__,
+ reg, data, ret);
+ return (ret != 1) ? -1 : 0;
+}
+
+static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },
+ { .flags = I2C_M_RD, .buf = b1, .len = 1 }};
+
+ dprintk("%s: reg=0x%x\n", __func__, reg);
+
+ msg[0].addr = state->config->demod_address;
+ msg[1].addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg,
+ ret);
+ return -EINVAL;
+ }
+
+ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__,
+ reg, b1[0], ret);
+ return b1[0];
+}
+
+static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
+{
+ int val;
+ dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __func__, reg,
+ mask, data);
+
+ // read a byte and check
+ val = tda1004x_read_byte(state, reg);
+ if (val < 0)
+ return val;
+
+ // mask if off
+ val = val & ~mask;
+ val |= data & 0xff;
+
+ // write it out again
+ return tda1004x_write_byteI(state, reg, val);
+}
+
+static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
+{
+ int i;
+ int result;
+
+ dprintk("%s: reg=0x%x, len=0x%x\n", __func__, reg, len);
+
+ result = 0;
+ for (i = 0; i < len; i++) {
+ result = tda1004x_write_byteI(state, reg + i, buf[i]);
+ if (result != 0)
+ break;
+ }
+
+ return result;
+}
+
+static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
+{
+ int result;
+ dprintk("%s\n", __func__);
+
+ result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
+ msleep(20);
+ return result;
+}
+
+static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
+{
+ dprintk("%s\n", __func__);
+
+ return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
+}
+
+static int tda10045h_set_bandwidth(struct tda1004x_state *state,
+ u32 bandwidth)
+{
+ static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
+ static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };
+ static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };
+
+ switch (bandwidth) {
+ case 6000000:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
+ break;
+
+ case 7000000:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
+ break;
+
+ case 8000000:
+ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
+
+ return 0;
+}
+
+static int tda10046h_set_bandwidth(struct tda1004x_state *state,
+ u32 bandwidth)
+{
+ static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 };
+ static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f };
+ static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d };
+
+ static u8 bandwidth_6mhz_48M[] = { 0x70, 0x02, 0x49, 0x24, 0x92 };
+ static u8 bandwidth_7mhz_48M[] = { 0x60, 0x02, 0xaa, 0xaa, 0xab };
+ static u8 bandwidth_8mhz_48M[] = { 0x54, 0x03, 0x0c, 0x30, 0xc3 };
+ int tda10046_clk53m;
+
+ if ((state->config->if_freq == TDA10046_FREQ_045) ||
+ (state->config->if_freq == TDA10046_FREQ_052))
+ tda10046_clk53m = 0;
+ else
+ tda10046_clk53m = 1;
+ switch (bandwidth) {
+ case 6000000:
+ if (tda10046_clk53m)
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_53M,
+ sizeof(bandwidth_6mhz_53M));
+ else
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_48M,
+ sizeof(bandwidth_6mhz_48M));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xab);
+ }
+ break;
+
+ case 7000000:
+ if (tda10046_clk53m)
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_53M,
+ sizeof(bandwidth_7mhz_53M));
+ else
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_48M,
+ sizeof(bandwidth_7mhz_48M));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00);
+ }
+ break;
+
+ case 8000000:
+ if (tda10046_clk53m)
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_53M,
+ sizeof(bandwidth_8mhz_53M));
+ else
+ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_48M,
+ sizeof(bandwidth_8mhz_48M));
+ if (state->config->if_freq == TDA10046_FREQ_045) {
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x55);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tda1004x_do_upload(struct tda1004x_state *state,
+ const unsigned char *mem, unsigned int len,
+ u8 dspCodeCounterReg, u8 dspCodeInReg)
+{
+ u8 buf[65];
+ struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 };
+ int tx_size;
+ int pos = 0;
+
+ /* clear code counter */
+ tda1004x_write_byteI(state, dspCodeCounterReg, 0);
+ fw_msg.addr = state->config->demod_address;
+
+ i2c_lock_adapter(state->i2c);
+ buf[0] = dspCodeInReg;
+ while (pos != len) {
+ // work out how much to send this time
+ tx_size = len - pos;
+ if (tx_size > 0x10)
+ tx_size = 0x10;
+
+ // send the chunk
+ memcpy(buf + 1, mem + pos, tx_size);
+ fw_msg.len = tx_size + 1;
+ if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
+ printk(KERN_ERR "tda1004x: Error during firmware upload\n");
+ i2c_unlock_adapter(state->i2c);
+ return -EIO;
+ }
+ pos += tx_size;
+
+ dprintk("%s: fw_pos=0x%x\n", __func__, pos);
+ }
+ i2c_unlock_adapter(state->i2c);
+
+ /* give the DSP a chance to settle 03/10/05 Hac */
+ msleep(100);
+
+ return 0;
+}
+
+static int tda1004x_check_upload_ok(struct tda1004x_state *state)
+{
+ u8 data1, data2;
+ unsigned long timeout;
+
+ if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+ timeout = jiffies + 2 * HZ;
+ while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n");
+ break;
+ }
+ msleep(1);
+ }
+ } else
+ msleep(100);
+
+ // check upload was OK
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
+ tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
+
+ data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
+ data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
+ if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) {
+ printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2);
+ return -EIO;
+ }
+ printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2);
+ return 0;
+}
+
+static int tda10045_fwupload(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int ret;
+ const struct firmware *fw;
+
+ /* don't re-upload unless necessary */
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);
+ ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+ if (ret) {
+ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
+ return ret;
+ }
+
+ /* reset chip */
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+ msleep(10);
+
+ /* set parameters */
+ tda10045h_set_bandwidth(state, 8000000);
+
+ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
+ release_firmware(fw);
+ if (ret)
+ return ret;
+ printk(KERN_INFO "tda1004x: firmware upload complete\n");
+
+ /* wait for DSP to initialise */
+ /* DSPREADY doesn't seem to work on the TDA10045H */
+ msleep(100);
+
+ return tda1004x_check_upload_ok(state);
+}
+
+static void tda10046_init_plls(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tda10046_clk53m;
+
+ if ((state->config->if_freq == TDA10046_FREQ_045) ||
+ (state->config->if_freq == TDA10046_FREQ_052))
+ tda10046_clk53m = 0;
+ else
+ tda10046_clk53m = 1;
+
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0);
+ if(tda10046_clk53m) {
+ printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n");
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8
+ } else {
+ printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n");
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3
+ }
+ if (state->config->xtal_freq == TDA10046_XTAL_4M ) {
+ dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __func__);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
+ } else {
+ dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __func__);
+ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3
+ }
+ if(tda10046_clk53m)
+ tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x67);
+ else
+ tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x72);
+ /* Note clock frequency is handled implicitly */
+ switch (state->config->if_freq) {
+ case TDA10046_FREQ_045:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00);
+ break;
+ case TDA10046_FREQ_052:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xc7);
+ break;
+ case TDA10046_FREQ_3617:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x59);
+ break;
+ case TDA10046_FREQ_3613:
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7);
+ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x3f);
+ break;
+ }
+ tda10046h_set_bandwidth(state, 8000000); /* default bandwidth 8 MHz */
+ /* let the PLLs settle */
+ msleep(120);
+}
+
+static int tda10046_fwupload(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int ret, confc4;
+ const struct firmware *fw;
+
+ /* reset + wake up chip */
+ if (state->config->xtal_freq == TDA10046_XTAL_4M) {
+ confc4 = 0;
+ } else {
+ dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__);
+ confc4 = 0x80;
+ }
+ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
+
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+ /* set GPIO 1 and 3 */
+ if (state->config->gpio_config != TDA10046_GPTRI) {
+ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33);
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f);
+ }
+ /* let the clocks recover from sleep */
+ msleep(10);
+
+ /* The PLLs need to be reprogrammed after sleep */
+ tda10046_init_plls(fe);
+ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0);
+
+ /* don't re-upload unless necessary */
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
+ /*
+ For i2c normal work, we need to slow down the bus speed.
+ However, the slow down breaks the eeprom firmware load.
+ So, use normal speed for eeprom booting and then restore the
+ i2c speed after that. Tested with MSI TV @nyware A/D board,
+ that comes with firmware version 29 inside their eeprom.
+
+ It should also be noticed that no other I2C transfer should
+ be in course while booting from eeprom, otherwise, tda10046
+ goes into an instable state. So, proper locking are needed
+ at the i2c bus master.
+ */
+ printk(KERN_INFO "tda1004x: trying to boot from eeprom\n");
+ tda1004x_write_byteI(state, TDA1004X_CONFC4, 4);
+ msleep(300);
+ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
+
+ /* Checks if eeprom firmware went without troubles */
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
+ /* eeprom firmware didn't work. Load one manually. */
+
+ if (state->config->request_firmware != NULL) {
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
+ ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
+ if (ret) {
+ /* remain compatible to old bug: try to load with tda10045 image name */
+ ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
+ if (ret) {
+ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
+ return ret;
+ } else {
+ printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n",
+ TDA10046_DEFAULT_FIRMWARE);
+ }
+ }
+ } else {
+ printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n");
+ return -EIO;
+ }
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
+ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
+ release_firmware(fw);
+ return tda1004x_check_upload_ok(state);
+}
+
+static int tda1004x_encode_fec(int fec)
+{
+ // convert known FEC values
+ switch (fec) {
+ case FEC_1_2:
+ return 0;
+ case FEC_2_3:
+ return 1;
+ case FEC_3_4:
+ return 2;
+ case FEC_5_6:
+ return 3;
+ case FEC_7_8:
+ return 4;
+ }
+
+ // unsupported
+ return -EINVAL;
+}
+
+static int tda1004x_decode_fec(int tdafec)
+{
+ // convert known FEC values
+ switch (tdafec) {
+ case 0:
+ return FEC_1_2;
+ case 1:
+ return FEC_2_3;
+ case 2:
+ return FEC_3_4;
+ case 3:
+ return FEC_5_6;
+ case 4:
+ return FEC_7_8;
+ }
+
+ // unsupported
+ return -1;
+}
+
+static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return tda1004x_write_byteI(state, buf[0], buf[1]);
+}
+
+static int tda10045_init(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ if (tda10045_fwupload(fe)) {
+ printk("tda1004x: firmware upload failed\n");
+ return -EIO;
+ }
+
+ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
+
+ // tda setup
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+ tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
+ tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
+ tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
+
+ tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
+
+ return 0;
+}
+
+static int tda10046_init(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ dprintk("%s\n", __func__);
+
+ if (tda10046_fwupload(fe)) {
+ printk("tda1004x: firmware upload failed\n");
+ return -EIO;
+ }
+
+ // tda setup
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
+ tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream
+ tda1004x_write_byteI(state, TDA1004X_CONFC1, 0x88); // enable pulse killer
+
+ switch (state->config->agc_config) {
+ case TDA10046_AGC_DEFAULT:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
+ break;
+ case TDA10046_AGC_IFO_AUTO_NEG:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
+ break;
+ case TDA10046_AGC_IFO_AUTO_POS:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities
+ break;
+ case TDA10046_AGC_TDA827X:
+ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
+ tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
+ tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
+ break;
+ }
+ if (state->config->ts_mode == 0) {
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40);
+ tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
+ } else {
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80);
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10,
+ state->config->invert_oclk << 4);
+ }
+ tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
+ tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on
+ tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // }
+ tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
+ tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // }
+ tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // }
+ tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 0x12); // IF gain 2, TUN gain 1
+ tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
+ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
+ // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes
+
+ return 0;
+}
+
+static int tda1004x_set_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int inversion;
+
+ dprintk("%s\n", __func__);
+
+ if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
+ // setup auto offset
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
+
+ // disable agc_conf[2]
+ tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
+ }
+
+ // set frequency
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ // Hardcoded to use auto as much as possible on the TDA10045 as it
+ // is very unreliable if AUTO mode is _not_ used.
+ if (state->demod_type == TDA1004X_DEMOD_TDA10045) {
+ fe_params->code_rate_HP = FEC_AUTO;
+ fe_params->guard_interval = GUARD_INTERVAL_AUTO;
+ fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
+ }
+
+ // Set standard params.. or put them to auto
+ if ((fe_params->code_rate_HP == FEC_AUTO) ||
+ (fe_params->code_rate_LP == FEC_AUTO) ||
+ (fe_params->modulation == QAM_AUTO) ||
+ (fe_params->hierarchy == HIERARCHY_AUTO)) {
+ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); /* turn off modulation bits */
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits
+ } else {
+ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto
+
+ // set HP FEC
+ tmp = tda1004x_encode_fec(fe_params->code_rate_HP);
+ if (tmp < 0)
+ return tmp;
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp);
+
+ // set LP FEC
+ tmp = tda1004x_encode_fec(fe_params->code_rate_LP);
+ if (tmp < 0)
+ return tmp;
+ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
+
+ /* set modulation */
+ switch (fe_params->modulation) {
+ case QPSK:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
+ break;
+
+ case QAM_16:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
+ break;
+
+ case QAM_64:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set hierarchy
+ switch (fe_params->hierarchy) {
+ case HIERARCHY_NONE:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
+ break;
+
+ case HIERARCHY_1:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
+ break;
+
+ case HIERARCHY_2:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
+ break;
+
+ case HIERARCHY_4:
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ // set bandwidth
+ switch (state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda10045h_set_bandwidth(state, fe_params->bandwidth_hz);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ tda10046h_set_bandwidth(state, fe_params->bandwidth_hz);
+ break;
+ }
+
+ // set inversion
+ inversion = fe_params->inversion;
+ if (state->config->invert)
+ inversion = inversion ? INVERSION_OFF : INVERSION_ON;
+ switch (inversion) {
+ case INVERSION_OFF:
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
+ break;
+
+ case INVERSION_ON:
+ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set guard interval
+ switch (fe_params->guard_interval) {
+ case GUARD_INTERVAL_1_32:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_16:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_8:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
+ break;
+
+ case GUARD_INTERVAL_1_4:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
+ break;
+
+ case GUARD_INTERVAL_AUTO:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // set transmission mode
+ switch (fe_params->transmission_mode) {
+ case TRANSMISSION_MODE_2K:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
+ break;
+
+ case TRANSMISSION_MODE_8K:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
+ break;
+
+ case TRANSMISSION_MODE_AUTO:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
+ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ // start the lock
+ switch (state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
+ msleep(1);
+ tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 1);
+ break;
+ }
+
+ msleep(10);
+
+ return 0;
+}
+
+static int tda1004x_get_fe(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ dprintk("%s\n", __func__);
+
+ // inversion status
+ fe_params->inversion = INVERSION_OFF;
+ if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20)
+ fe_params->inversion = INVERSION_ON;
+ if (state->config->invert)
+ fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
+
+ // bandwidth
+ switch (state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
+ case 0x14:
+ fe_params->bandwidth_hz = 8000000;
+ break;
+ case 0xdb:
+ fe_params->bandwidth_hz = 7000000;
+ break;
+ case 0x4f:
+ fe_params->bandwidth_hz = 6000000;
+ break;
+ }
+ break;
+ case TDA1004X_DEMOD_TDA10046:
+ switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) {
+ case 0x5c:
+ case 0x54:
+ fe_params->bandwidth_hz = 8000000;
+ break;
+ case 0x6a:
+ case 0x60:
+ fe_params->bandwidth_hz = 7000000;
+ break;
+ case 0x7b:
+ case 0x70:
+ fe_params->bandwidth_hz = 6000000;
+ break;
+ }
+ break;
+ }
+
+ // FEC
+ fe_params->code_rate_HP =
+ tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
+ fe_params->code_rate_LP =
+ tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
+
+ /* modulation */
+ switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) {
+ case 0:
+ fe_params->modulation = QPSK;
+ break;
+ case 1:
+ fe_params->modulation = QAM_16;
+ break;
+ case 2:
+ fe_params->modulation = QAM_64;
+ break;
+ }
+
+ // transmission mode
+ fe_params->transmission_mode = TRANSMISSION_MODE_2K;
+ if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10)
+ fe_params->transmission_mode = TRANSMISSION_MODE_8K;
+
+ // guard interval
+ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
+ case 0:
+ fe_params->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ fe_params->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ fe_params->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ fe_params->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ // hierarchy
+ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
+ case 0:
+ fe_params->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ fe_params->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ fe_params->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ fe_params->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ return 0;
+}
+
+static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int status;
+ int cber;
+ int vber;
+
+ dprintk("%s\n", __func__);
+
+ // read status
+ status = tda1004x_read_byte(state, TDA1004X_STATUS_CD);
+ if (status == -1)
+ return -EIO;
+
+ // decode
+ *fe_status = 0;
+ if (status & 4)
+ *fe_status |= FE_HAS_SIGNAL;
+ if (status & 2)
+ *fe_status |= FE_HAS_CARRIER;
+ if (status & 8)
+ *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+
+ // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi
+ // is getting anything valid
+ if (!(*fe_status & FE_HAS_VITERBI)) {
+ // read the CBER
+ cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+ if (cber == -1)
+ return -EIO;
+ status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+ if (status == -1)
+ return -EIO;
+ cber |= (status << 8);
+ // The address 0x20 should be read to cope with a TDA10046 bug
+ tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+ if (cber != 65535)
+ *fe_status |= FE_HAS_VITERBI;
+ }
+
+ // if we DO have some valid VITERBI output, but don't already have SYNC
+ // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid.
+ if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) {
+ // read the VBER
+ vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB);
+ if (vber == -1)
+ return -EIO;
+ status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
+ if (status == -1)
+ return -EIO;
+ vber |= (status << 8);
+ status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
+ if (status == -1)
+ return -EIO;
+ vber |= (status & 0x0f) << 16;
+ // The CVBER_LUT should be read to cope with TDA10046 hardware bug
+ tda1004x_read_byte(state, TDA1004X_CVBER_LUT);
+
+ // if RS has passed some valid TS packets, then we must be
+ // getting some SYNC bytes
+ if (vber < 16632)
+ *fe_status |= FE_HAS_SYNC;
+ }
+
+ // success
+ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
+ return 0;
+}
+
+static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int reg = 0;
+
+ dprintk("%s\n", __func__);
+
+ // determine the register to use
+ switch (state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ reg = TDA10045H_S_AGC;
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ reg = TDA10046H_AGC_IF_LEVEL;
+ break;
+ }
+
+ // read it
+ tmp = tda1004x_read_byte(state, reg);
+ if (tmp < 0)
+ return -EIO;
+
+ *signal = (tmp << 8) | tmp;
+ dprintk("%s: signal=0x%x\n", __func__, *signal);
+ return 0;
+}
+
+static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+
+ dprintk("%s\n", __func__);
+
+ // read it
+ tmp = tda1004x_read_byte(state, TDA1004X_SNR);
+ if (tmp < 0)
+ return -EIO;
+ tmp = 255 - tmp;
+
+ *snr = ((tmp << 8) | tmp);
+ dprintk("%s: snr=0x%x\n", __func__, *snr);
+ return 0;
+}
+
+static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+ int tmp2;
+ int counter;
+
+ dprintk("%s\n", __func__);
+
+ // read the UCBLOCKS and reset
+ counter = 0;
+ tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
+ if (tmp < 0)
+ return -EIO;
+ tmp &= 0x7f;
+ while (counter++ < 5) {
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
+
+ tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
+ if (tmp2 < 0)
+ return -EIO;
+ tmp2 &= 0x7f;
+ if ((tmp2 < tmp) || (tmp2 == 0))
+ break;
+ }
+
+ if (tmp != 0x7f)
+ *ucblocks = tmp;
+ else
+ *ucblocks = 0xffffffff;
+
+ dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
+ return 0;
+}
+
+static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int tmp;
+
+ dprintk("%s\n", __func__);
+
+ // read it in
+ tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
+ if (tmp < 0)
+ return -EIO;
+ *ber = tmp << 1;
+ tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
+ if (tmp < 0)
+ return -EIO;
+ *ber |= (tmp << 9);
+ // The address 0x20 should be read to cope with a TDA10046 bug
+ tda1004x_read_byte(state, TDA1004X_CBER_RESET);
+
+ dprintk("%s: ber=0x%x\n", __func__, *ber);
+ return 0;
+}
+
+static int tda1004x_sleep(struct dvb_frontend* fe)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+ int gpio_conf;
+
+ switch (state->demod_type) {
+ case TDA1004X_DEMOD_TDA10045:
+ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
+ break;
+
+ case TDA1004X_DEMOD_TDA10046:
+ /* set outputs to tristate */
+ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff);
+ /* invert GPIO 1 and 3 if desired*/
+ gpio_conf = state->config->gpio_config;
+ if (gpio_conf >= TDA10046_GP00_I)
+ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f,
+ (gpio_conf & 0x0f) ^ 0x0a);
+
+ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0);
+ tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
+ break;
+ }
+
+ return 0;
+}
+
+static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct tda1004x_state* state = fe->demodulator_priv;
+
+ if (enable) {
+ return tda1004x_enable_tuner_i2c(state);
+ } else {
+ return tda1004x_disable_tuner_i2c(state);
+ }
+}
+
+static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ /* Drift compensation makes no sense for DVB-T */
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+ return 0;
+}
+
+static void tda1004x_release(struct dvb_frontend* fe)
+{
+ struct tda1004x_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10045_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Philips TDA10045H DVB-T",
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = tda1004x_release,
+
+ .init = tda10045_init,
+ .sleep = tda1004x_sleep,
+ .write = tda1004x_write,
+ .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
+
+ .set_frontend = tda1004x_set_fe,
+ .get_frontend = tda1004x_get_fe,
+ .get_tune_settings = tda1004x_get_tune_settings,
+
+ .read_status = tda1004x_read_status,
+ .read_ber = tda1004x_read_ber,
+ .read_signal_strength = tda1004x_read_signal_strength,
+ .read_snr = tda1004x_read_snr,
+ .read_ucblocks = tda1004x_read_ucblocks,
+};
+
+struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda1004x_state *state;
+ int id;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ if (!state) {
+ printk(KERN_ERR "Can't allocate memory for tda10045 state\n");
+ return NULL;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->demod_type = TDA1004X_DEMOD_TDA10045;
+
+ /* check if the demod is there */
+ id = tda1004x_read_byte(state, TDA1004X_CHIPID);
+ if (id < 0) {
+ printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n");
+ kfree(state);
+ return NULL;
+ }
+
+ if (id != 0x25) {
+ printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id);
+ kfree(state);
+ return NULL;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+static struct dvb_frontend_ops tda10046_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Philips TDA10046H DVB-T",
+ .frequency_min = 51000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 166667,
+ .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_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
+ },
+
+ .release = tda1004x_release,
+
+ .init = tda10046_init,
+ .sleep = tda1004x_sleep,
+ .write = tda1004x_write,
+ .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
+
+ .set_frontend = tda1004x_set_fe,
+ .get_frontend = tda1004x_get_fe,
+ .get_tune_settings = tda1004x_get_tune_settings,
+
+ .read_status = tda1004x_read_status,
+ .read_ber = tda1004x_read_ber,
+ .read_signal_strength = tda1004x_read_signal_strength,
+ .read_snr = tda1004x_read_snr,
+ .read_ucblocks = tda1004x_read_ucblocks,
+};
+
+struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda1004x_state *state;
+ int id;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ if (!state) {
+ printk(KERN_ERR "Can't allocate memory for tda10046 state\n");
+ return NULL;
+ }
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->demod_type = TDA1004X_DEMOD_TDA10046;
+
+ /* check if the demod is there */
+ id = tda1004x_read_byte(state, TDA1004X_CHIPID);
+ if (id < 0) {
+ printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n");
+ kfree(state);
+ return NULL;
+ }
+ if (id != 0x46) {
+ printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id);
+ kfree(state);
+ return NULL;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
+MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10045_attach);
+EXPORT_SYMBOL(tda10046_attach);
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h
index 4e27ffb0f14e..4e27ffb0f14e 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb-frontends/tda1004x.h
diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
new file mode 100644
index 000000000000..a83bf6802345
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -0,0 +1,1285 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tda10071_priv.h"
+
+static struct dvb_frontend_ops tda10071_ops;
+
+/* write multiple registers */
+static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len+1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write single register */
+static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val)
+{
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val)
+{
+ return tda10071_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return tda10071_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = tda10071_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+/* execute firmware command */
+static int tda10071_cmd_execute(struct tda10071_priv *priv,
+ struct tda10071_cmd *cmd)
+{
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ /* write cmd and args for firmware */
+ ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len);
+ if (ret)
+ goto error;
+
+ /* start cmd execution */
+ ret = tda10071_wr_reg(priv, 0x1f, 1);
+ if (ret)
+ goto error;
+
+ /* wait cmd execution terminate */
+ for (i = 1000, tmp = 1; i && tmp; i--) {
+ ret = tda10071_rd_reg(priv, 0x1f, &tmp);
+ if (ret)
+ goto error;
+
+ usleep_range(200, 5000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t fe_sec_tone_mode)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tone;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__,
+ fe_sec_tone_mode);
+
+ switch (fe_sec_tone_mode) {
+ case SEC_TONE_ON:
+ tone = 1;
+ break;
+ case SEC_TONE_OFF:
+ tone = 0;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ cmd.args[0] = CMD_LNB_PCB_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = tone;
+ cmd.len = 5;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t fe_sec_voltage)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 voltage;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage);
+
+ switch (fe_sec_voltage) {
+ case SEC_VOLTAGE_13:
+ voltage = 0;
+ break;
+ case SEC_VOLTAGE_18:
+ voltage = 1;
+ break;
+ case SEC_VOLTAGE_OFF:
+ voltage = 0;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ cmd.args[0] = CMD_LNB_SET_DC_LEVEL;
+ cmd.args[1] = 0;
+ cmd.args[2] = voltage;
+ cmd.len = 3;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *diseqc_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__,
+ diseqc_cmd->msg_len);
+
+ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_LNB_SEND_DISEQC;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0;
+ cmd.args[3] = 0;
+ cmd.args[4] = 2;
+ cmd.args[5] = 0;
+ cmd.args[6] = diseqc_cmd->msg_len;
+ memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len);
+ cmd.len = 7 + diseqc_cmd->msg_len;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
+ struct dvb_diseqc_slave_reply *reply)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ /* wait LNB RX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* reply len */
+ ret = tda10071_rd_reg(priv, 0x46, &tmp);
+ if (ret)
+ goto error;
+
+ reply->msg_len = tmp & 0x1f; /* [4:0] */
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg); /* truncate API max */
+
+ /* read reply */
+ cmd.args[0] = CMD_LNB_UPDATE_REPLY;
+ cmd.args[1] = 0;
+ cmd.len = 2;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t fe_sec_mini_cmd)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ u8 tmp, burst;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
+ fe_sec_mini_cmd);
+
+ switch (fe_sec_mini_cmd) {
+ case SEC_MINI_A:
+ burst = 0;
+ break;
+ case SEC_MINI_B:
+ burst = 1;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* wait LNB TX */
+ for (i = 500, tmp = 0; i && !tmp; i--) {
+ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
+ if (ret)
+ goto error;
+
+ usleep_range(10000, 20000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_LNB_SEND_TONEBURST;
+ cmd.args[1] = 0;
+ cmd.args[2] = burst;
+ cmd.len = 3;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ *status = 0;
+
+ if (!priv->warm) {
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_reg(priv, 0x39, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp & 0x01) /* tuner PLL */
+ *status |= FE_HAS_SIGNAL;
+ if (tmp & 0x02) /* demod PLL */
+ *status |= FE_HAS_CARRIER;
+ if (tmp & 0x04) /* viterbi or LDPC*/
+ *status |= FE_HAS_VITERBI;
+ if (tmp & 0x08) /* RS or BCH */
+ *status |= FE_HAS_SYNC | FE_HAS_LOCK;
+
+ priv->fe_status = *status;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *snr = 0;
+ ret = 0;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x3a, buf, 2);
+ if (ret)
+ goto error;
+
+ /* Es/No dBx10 */
+ *snr = buf[0] << 8 | buf[1];
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret;
+ u8 tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *strength = 0;
+ ret = 0;
+ goto error;
+ }
+
+ cmd.args[0] = CMD_GET_AGCACC;
+ cmd.args[1] = 0;
+ cmd.len = 2;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ /* input power estimate dBm */
+ ret = tda10071_rd_reg(priv, 0x50, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp < 181)
+ tmp = 181; /* -75 dBm */
+ else if (tmp > 236)
+ tmp = 236; /* -20 dBm */
+
+ /* scale value to 0x0000-0xffff */
+ *strength = (tmp-181) * 0xffff / (236-181);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len;
+ u8 tmp, reg, buf[8];
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ber = priv->ber = 0;
+ ret = 0;
+ goto error;
+ }
+
+ switch (priv->delivery_system) {
+ case SYS_DVBS:
+ reg = 0x4c;
+ len = 8;
+ i = 1;
+ break;
+ case SYS_DVBS2:
+ reg = 0x4d;
+ len = 4;
+ i = 0;
+ break;
+ default:
+ *ber = priv->ber = 0;
+ return 0;
+ }
+
+ ret = tda10071_rd_reg(priv, reg, &tmp);
+ if (ret)
+ goto error;
+
+ if (priv->meas_count[i] == tmp) {
+ dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__,
+ tmp);
+ *ber = priv->ber;
+ return 0;
+ } else {
+ priv->meas_count[i] = tmp;
+ }
+
+ cmd.args[0] = CMD_BER_UPDATE_COUNTERS;
+ cmd.args[1] = 0;
+ cmd.args[2] = i;
+ cmd.len = 3;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, len);
+ if (ret)
+ goto error;
+
+ if (priv->delivery_system == SYS_DVBS) {
+ *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ priv->ucb += (buf[4] << 8) | buf[5];
+ } else {
+ *ber = (buf[0] << 8) | buf[1];
+ }
+ priv->ber = *ber;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ int ret = 0;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ *ucblocks = 0;
+ goto error;
+ }
+
+ /* UCB is updated when BER is read. Assume BER is read anyway. */
+
+ *ucblocks = priv->ucb;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_set_frontend(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 mode, rolloff, pilot, inversion, div;
+
+ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \
+ "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \
+ "rolloff=%d\n", __func__, c->delivery_system, c->modulation,
+ c->frequency, c->symbol_rate, c->inversion, c->pilot,
+ c->rolloff);
+
+ priv->delivery_system = SYS_UNDEFINED;
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ switch (c->inversion) {
+ case INVERSION_OFF:
+ inversion = 1;
+ break;
+ case INVERSION_ON:
+ inversion = 0;
+ break;
+ case INVERSION_AUTO:
+ /* 2 = auto; try first on then off
+ * 3 = auto; try first off then on */
+ inversion = 3;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->delivery_system) {
+ case SYS_DVBS:
+ rolloff = 0;
+ pilot = 2;
+ break;
+ case SYS_DVBS2:
+ switch (c->rolloff) {
+ case ROLLOFF_20:
+ rolloff = 2;
+ break;
+ case ROLLOFF_25:
+ rolloff = 1;
+ break;
+ case ROLLOFF_35:
+ rolloff = 0;
+ break;
+ case ROLLOFF_AUTO:
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ switch (c->pilot) {
+ case PILOT_OFF:
+ pilot = 0;
+ break;
+ case PILOT_ON:
+ pilot = 1;
+ break;
+ case PILOT_AUTO:
+ pilot = 2;
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ default:
+ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (c->delivery_system == TDA10071_MODCOD[i].delivery_system &&
+ c->modulation == TDA10071_MODCOD[i].modulation &&
+ c->fec_inner == TDA10071_MODCOD[i].fec) {
+ mode = TDA10071_MODCOD[i].val;
+ dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n",
+ __func__, mode);
+ break;
+ }
+ }
+
+ if (mode == 0xff) {
+ dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n",
+ __func__);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (c->symbol_rate <= 5000000)
+ div = 14;
+ else
+ div = 4;
+
+ ret = tda10071_wr_reg(priv, 0x81, div);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe3, div);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_CHANGE_CHANNEL;
+ cmd.args[1] = 0;
+ cmd.args[2] = mode;
+ cmd.args[3] = (c->frequency >> 16) & 0xff;
+ cmd.args[4] = (c->frequency >> 8) & 0xff;
+ cmd.args[5] = (c->frequency >> 0) & 0xff;
+ cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff;
+ cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff;
+ cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
+ cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
+ cmd.args[10] = rolloff;
+ cmd.args[11] = inversion;
+ cmd.args[12] = pilot;
+ cmd.args[13] = 0x00;
+ cmd.args[14] = 0x00;
+ cmd.len = 15;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ priv->delivery_system = c->delivery_system;
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_frontend(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u8 buf[5], tmp;
+
+ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ ret = tda10071_rd_regs(priv, 0x30, buf, 5);
+ if (ret)
+ goto error;
+
+ tmp = buf[0] & 0x3f;
+ for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
+ if (tmp == TDA10071_MODCOD[i].val) {
+ c->modulation = TDA10071_MODCOD[i].modulation;
+ c->fec_inner = TDA10071_MODCOD[i].fec;
+ c->delivery_system = TDA10071_MODCOD[i].delivery_system;
+ }
+ }
+
+ switch ((buf[1] >> 0) & 0x01) {
+ case 0:
+ c->inversion = INVERSION_OFF;
+ break;
+ case 1:
+ c->inversion = INVERSION_ON;
+ break;
+ }
+
+ switch ((buf[1] >> 7) & 0x01) {
+ case 0:
+ c->pilot = PILOT_OFF;
+ break;
+ case 1:
+ c->pilot = PILOT_ON;
+ break;
+ }
+
+ c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0);
+
+ ret = tda10071_rd_regs(priv, 0x52, buf, 3);
+ if (ret)
+ goto error;
+
+ c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_init(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i, len, remaining, fw_size;
+ const struct firmware *fw;
+ u8 *fw_file = TDA10071_FIRMWARE;
+ u8 tmp, buf[4];
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x00, 0x07 },
+ { 0x80, 0x00, 0x02 },
+ { 0xcd, 0x00, 0xc0 },
+ { 0xce, 0x00, 0x1b },
+ { 0x9d, 0x00, 0x01 },
+ { 0x9d, 0x00, 0x02 },
+ { 0x9e, 0x00, 0x01 },
+ { 0x87, 0x00, 0x80 },
+ { 0xce, 0x00, 0x08 },
+ { 0xce, 0x00, 0x10 },
+ };
+ struct tda10071_reg_val_mask tab2[] = {
+ { 0xf1, 0x70, 0xff },
+ { 0x88, priv->cfg.pll_multiplier, 0x3f },
+ { 0x89, 0x00, 0x10 },
+ { 0x89, 0x10, 0x10 },
+ { 0xc0, 0x01, 0x01 },
+ { 0xc0, 0x00, 0x01 },
+ { 0xe0, 0xff, 0xff },
+ { 0xe0, 0x00, 0xff },
+ { 0x96, 0x1e, 0x7e },
+ { 0x8b, 0x08, 0x08 },
+ { 0x8b, 0x00, 0x08 },
+ { 0x8f, 0x1a, 0x7e },
+ { 0x8c, 0x68, 0xff },
+ { 0x8d, 0x08, 0xff },
+ { 0x8e, 0x4c, 0xff },
+ { 0x8f, 0x01, 0x01 },
+ { 0x8b, 0x04, 0x04 },
+ { 0x8b, 0x00, 0x04 },
+ { 0x87, 0x05, 0x07 },
+ { 0x80, 0x00, 0x20 },
+ { 0xc8, 0x01, 0xff },
+ { 0xb4, 0x47, 0xff },
+ { 0xb5, 0x9c, 0xff },
+ { 0xb6, 0x7d, 0xff },
+ { 0xba, 0x00, 0x03 },
+ { 0xb7, 0x47, 0xff },
+ { 0xb8, 0x9c, 0xff },
+ { 0xb9, 0x7d, 0xff },
+ { 0xba, 0x00, 0x0c },
+ { 0xc8, 0x00, 0xff },
+ { 0xcd, 0x00, 0x04 },
+ { 0xcd, 0x00, 0x20 },
+ { 0xe8, 0x02, 0xff },
+ { 0xcf, 0x20, 0xff },
+ { 0x9b, 0xd7, 0xff },
+ { 0x9a, 0x01, 0x03 },
+ { 0xa8, 0x05, 0x0f },
+ { 0xa8, 0x65, 0xf0 },
+ { 0xa6, 0xa0, 0xf0 },
+ { 0x9d, 0x50, 0xfc },
+ { 0x9e, 0x20, 0xe0 },
+ { 0xa3, 0x1c, 0x7c },
+ { 0xd5, 0x03, 0x03 },
+ };
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (!tmp) {
+ /* warm state - wake up device from sleep */
+ priv->warm = 1;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg,
+ tab[i].val, tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ cmd.args[0] = CMD_SET_SLEEP_MODE;
+ cmd.args[1] = 0;
+ cmd.args[2] = 0;
+ cmd.len = 3;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ } else {
+ /* cold state - try to download firmware */
+ priv->warm = 0;
+
+ /* request the firmware, this will block and timeout */
+ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
+ if (ret) {
+ dev_err(&priv->i2c->dev, "%s: did not find the " \
+ "firmware file. (%s) Please see " \
+ "linux/Documentation/dvb/ for more " \
+ "details on firmware-problems. (%d)\n",
+ KBUILD_MODNAME, fw_file, ret);
+ goto error;
+ }
+
+ /* init */
+ for (i = 0; i < ARRAY_SIZE(tab2); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab2[i].reg,
+ tab2[i].val, tab2[i].mask);
+ if (ret)
+ goto error_release_firmware;
+ }
+
+ /* download firmware */
+ ret = tda10071_wr_reg(priv, 0xe0, 0x7f);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x81);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf8, 0x00);
+ if (ret)
+ goto error_release_firmware;
+
+ ret = tda10071_wr_reg(priv, 0xf9, 0x00);
+ if (ret)
+ goto error_release_firmware;
+
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \
+ "will try to load a firmware\n", KBUILD_MODNAME,
+ tda10071_ops.info.name);
+ dev_info(&priv->i2c->dev, "%s: downloading firmware from " \
+ "file '%s'\n", KBUILD_MODNAME, fw_file);
+
+ /* do not download last byte */
+ fw_size = fw->size - 1;
+
+ for (remaining = fw_size; remaining > 0;
+ remaining -= (priv->cfg.i2c_wr_max - 1)) {
+ len = remaining;
+ if (len > (priv->cfg.i2c_wr_max - 1))
+ len = (priv->cfg.i2c_wr_max - 1);
+
+ ret = tda10071_wr_regs(priv, 0xfa,
+ (u8 *) &fw->data[fw_size - remaining], len);
+ if (ret) {
+ dev_err(&priv->i2c->dev, "%s: firmware " \
+ "download failed=%d\n",
+ KBUILD_MODNAME, ret);
+ if (ret)
+ goto error_release_firmware;
+ }
+ }
+ release_firmware(fw);
+
+ ret = tda10071_wr_reg(priv, 0xf7, 0x0c);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg(priv, 0xe0, 0x00);
+ if (ret)
+ goto error;
+
+ /* wait firmware start */
+ msleep(250);
+
+ /* firmware status */
+ ret = tda10071_rd_reg(priv, 0x51, &tmp);
+ if (ret)
+ goto error;
+
+ if (tmp) {
+ dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -EFAULT;
+ goto error;
+ } else {
+ priv->warm = 1;
+ }
+
+ cmd.args[0] = CMD_GET_FW_VERSION;
+ cmd.len = 1;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_rd_regs(priv, cmd.len, buf, 4);
+ if (ret)
+ goto error;
+
+ dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
+ KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
+ dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
+ KBUILD_MODNAME, tda10071_ops.info.name);
+
+ ret = tda10071_rd_regs(priv, 0x81, buf, 2);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_DEMOD_INIT;
+ cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
+ cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
+ cmd.args[3] = buf[0];
+ cmd.args[4] = buf[1];
+ cmd.args[5] = priv->cfg.pll_multiplier;
+ cmd.args[6] = priv->cfg.spec_inv;
+ cmd.args[7] = 0x00;
+ cmd.len = 8;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_TUNER_INIT;
+ cmd.args[1] = 0x00;
+ cmd.args[2] = 0x00;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x00;
+ cmd.args[5] = 0x14;
+ cmd.args[6] = 0x00;
+ cmd.args[7] = 0x03;
+ cmd.args[8] = 0x02;
+ cmd.args[9] = 0x02;
+ cmd.args[10] = 0x00;
+ cmd.args[11] = 0x00;
+ cmd.args[12] = 0x00;
+ cmd.args[13] = 0x00;
+ cmd.args[14] = 0x00;
+ cmd.len = 15;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_MPEG_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = priv->cfg.ts_mode;
+ cmd.args[3] = 0x00;
+ cmd.args[4] = 0x04;
+ cmd.args[5] = 0x00;
+ cmd.len = 6;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_LNB_CONFIG;
+ cmd.args[1] = 0;
+ cmd.args[2] = 150;
+ cmd.args[3] = 3;
+ cmd.args[4] = 22;
+ cmd.args[5] = 1;
+ cmd.args[6] = 1;
+ cmd.args[7] = 30;
+ cmd.args[8] = 30;
+ cmd.args[9] = 30;
+ cmd.args[10] = 30;
+ cmd.len = 11;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ cmd.args[0] = CMD_BER_CONTROL;
+ cmd.args[1] = 0;
+ cmd.args[2] = 14;
+ cmd.args[3] = 14;
+ cmd.len = 4;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error_release_firmware:
+ release_firmware(fw);
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_sleep(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ struct tda10071_cmd cmd;
+ int ret, i;
+ struct tda10071_reg_val_mask tab[] = {
+ { 0xcd, 0x07, 0x07 },
+ { 0x80, 0x02, 0x02 },
+ { 0xcd, 0xc0, 0xc0 },
+ { 0xce, 0x1b, 0x1b },
+ { 0x9d, 0x01, 0x01 },
+ { 0x9d, 0x02, 0x02 },
+ { 0x9e, 0x01, 0x01 },
+ { 0x87, 0x80, 0x80 },
+ { 0xce, 0x08, 0x08 },
+ { 0xce, 0x10, 0x10 },
+ };
+
+ if (!priv->warm) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ cmd.args[0] = CMD_SET_SLEEP_MODE;
+ cmd.args[1] = 0;
+ cmd.args[2] = 1;
+ cmd.len = 3;
+ ret = tda10071_cmd_execute(priv, &cmd);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto error;
+ }
+
+ return ret;
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tda10071_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 8000;
+ s->step_size = 0;
+ s->max_drift = 0;
+
+ return 0;
+}
+
+static void tda10071_release(struct dvb_frontend *fe)
+{
+ struct tda10071_priv *priv = fe->demodulator_priv;
+ kfree(priv);
+}
+
+struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct tda10071_priv *priv = NULL;
+ u8 tmp;
+
+ /* allocate memory for the internal priv */
+ priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL);
+ if (priv == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, config, sizeof(struct tda10071_config));
+
+ /* chip ID */
+ ret = tda10071_rd_reg(priv, 0xff, &tmp);
+ if (ret || tmp != 0x0f)
+ goto error;
+
+ /* chip type */
+ ret = tda10071_rd_reg(priv, 0xdd, &tmp);
+ if (ret || tmp != 0x00)
+ goto error;
+
+ /* chip version */
+ ret = tda10071_rd_reg(priv, 0xfe, &tmp);
+ if (ret || tmp != 0x01)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ return &priv->fe;
+error:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(tda10071_attach);
+
+static struct dvb_frontend_ops tda10071_ops = {
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
+ .info = {
+ .name = "NXP TDA10071",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_4_5 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_6_7 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_8_9 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_RECOVER |
+ FE_CAN_2G_MODULATION
+ },
+
+ .release = tda10071_release,
+
+ .get_tune_settings = tda10071_get_tune_settings,
+
+ .init = tda10071_init,
+ .sleep = tda10071_sleep,
+
+ .set_frontend = tda10071_set_frontend,
+ .get_frontend = tda10071_get_frontend,
+
+ .read_status = tda10071_read_status,
+ .read_snr = tda10071_read_snr,
+ .read_signal_strength = tda10071_read_signal_strength,
+ .read_ber = tda10071_read_ber,
+ .read_ucblocks = tda10071_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd,
+ .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply,
+ .diseqc_send_burst = tda10071_diseqc_send_burst,
+
+ .set_tone = tda10071_set_tone,
+ .set_voltage = tda10071_set_voltage,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(TDA10071_FIRMWARE);
diff --git a/drivers/media/dvb/frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h
index 21163c4b555c..21163c4b555c 100644
--- a/drivers/media/dvb/frontends/tda10071.h
+++ b/drivers/media/dvb-frontends/tda10071.h
diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h
new file mode 100644
index 000000000000..4baf14bfb65a
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda10071_priv.h
@@ -0,0 +1,109 @@
+/*
+ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TDA10071_PRIV
+#define TDA10071_PRIV
+
+#include "dvb_frontend.h"
+#include "tda10071.h"
+#include <linux/firmware.h>
+
+struct tda10071_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct tda10071_config cfg;
+
+ u8 meas_count[2];
+ u32 ber;
+ u32 ucb;
+ fe_status_t fe_status;
+ fe_delivery_system_t delivery_system;
+ bool warm; /* FW running */
+};
+
+static struct tda10071_modcod {
+ fe_delivery_system_t delivery_system;
+ fe_modulation_t modulation;
+ fe_code_rate_t fec;
+ u8 val;
+} TDA10071_MODCOD[] = {
+ /* NBC-QPSK */
+ { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 },
+ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
+ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
+ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
+ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
+ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
+ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
+ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
+ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
+ /* 8PSK */
+ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
+ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
+ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
+ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
+ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
+ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
+ /* QPSK */
+ { SYS_DVBS, QPSK, FEC_AUTO, 0x2d },
+ { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
+ { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
+ { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
+ { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
+ { SYS_DVBS, QPSK, FEC_7_8, 0x32 },
+};
+
+struct tda10071_reg_val_mask {
+ u8 reg;
+ u8 val;
+ u8 mask;
+};
+
+/* firmware filename */
+#define TDA10071_FIRMWARE "dvb-fe-tda10071.fw"
+
+/* firmware commands */
+#define CMD_DEMOD_INIT 0x10
+#define CMD_CHANGE_CHANNEL 0x11
+#define CMD_MPEG_CONFIG 0x13
+#define CMD_TUNER_INIT 0x15
+#define CMD_GET_AGCACC 0x1a
+
+#define CMD_LNB_CONFIG 0x20
+#define CMD_LNB_SEND_DISEQC 0x21
+#define CMD_LNB_SET_DC_LEVEL 0x22
+#define CMD_LNB_PCB_CONFIG 0x23
+#define CMD_LNB_SEND_TONEBURST 0x24
+#define CMD_LNB_UPDATE_REPLY 0x25
+
+#define CMD_GET_FW_VERSION 0x35
+#define CMD_SET_SLEEP_MODE 0x36
+#define CMD_BER_CONTROL 0x3e
+#define CMD_BER_UPDATE_COUNTERS 0x3f
+
+/* firmare command struct */
+#define TDA10071_ARGLEN 30
+struct tda10071_cmd {
+ u8 args[TDA10071_ARGLEN];
+ u8 len;
+};
+
+
+#endif /* TDA10071_PRIV */
diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c
index fcfe2e080cb0..fcfe2e080cb0 100644
--- a/drivers/media/dvb/frontends/tda10086.c
+++ b/drivers/media/dvb-frontends/tda10086.c
diff --git a/drivers/media/dvb/frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h
index 61148c558d8d..61148c558d8d 100644
--- a/drivers/media/dvb/frontends/tda10086.h
+++ b/drivers/media/dvb-frontends/tda10086.h
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c
index ad7c72e8f517..ad7c72e8f517 100644
--- a/drivers/media/dvb/frontends/tda18271c2dd.c
+++ b/drivers/media/dvb-frontends/tda18271c2dd.c
diff --git a/drivers/media/dvb/frontends/tda18271c2dd.h b/drivers/media/dvb-frontends/tda18271c2dd.h
index 1389c74e12ce..1389c74e12ce 100644
--- a/drivers/media/dvb/frontends/tda18271c2dd.h
+++ b/drivers/media/dvb-frontends/tda18271c2dd.h
diff --git a/drivers/media/dvb/frontends/tda18271c2dd_maps.h b/drivers/media/dvb-frontends/tda18271c2dd_maps.h
index b87661b9df14..b87661b9df14 100644
--- a/drivers/media/dvb/frontends/tda18271c2dd_maps.h
+++ b/drivers/media/dvb-frontends/tda18271c2dd_maps.h
diff --git a/drivers/media/dvb/frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c
index 2c1c759a4f42..2c1c759a4f42 100644
--- a/drivers/media/dvb/frontends/tda665x.c
+++ b/drivers/media/dvb-frontends/tda665x.c
diff --git a/drivers/media/dvb/frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h
index ec7927aa75ae..ec7927aa75ae 100644
--- a/drivers/media/dvb/frontends/tda665x.h
+++ b/drivers/media/dvb-frontends/tda665x.h
diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c
index 15912c96926a..15912c96926a 100644
--- a/drivers/media/dvb/frontends/tda8083.c
+++ b/drivers/media/dvb-frontends/tda8083.c
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h
index 5a03c14a10e8..5a03c14a10e8 100644
--- a/drivers/media/dvb/frontends/tda8083.h
+++ b/drivers/media/dvb-frontends/tda8083.h
diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c
new file mode 100644
index 000000000000..19c488814e5c
--- /dev/null
+++ b/drivers/media/dvb-frontends/tda8261.c
@@ -0,0 +1,230 @@
+/*
+ TDA8261 8PSK/QPSK tuner driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda8261.h"
+
+struct tda8261_state {
+ struct dvb_frontend *fe;
+ struct i2c_adapter *i2c;
+ const struct tda8261_config *config;
+
+ /* state cache */
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int tda8261_read(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ pr_err("%s: read error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_write(struct tda8261_state *state, u8 *buf)
+{
+ const struct tda8261_config *config = state->config;
+ int err = 0;
+ struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 };
+
+ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
+ pr_err("%s: write error, err=%d\n", __func__, err);
+
+ return err;
+}
+
+static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ u8 result = 0;
+ int err = 0;
+
+ *status = 0;
+
+ if ((err = tda8261_read(state, &result)) < 0) {
+ pr_err("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if ((result >> 6) & 0x01) {
+ pr_debug("%s: Tuner Phase Locked\n", __func__);
+ *status = 1;
+ }
+
+ return err;
+}
+
+static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
+static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
+
+static int tda8261_get_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ int err = 0;
+
+ switch (param) {
+ case DVBFE_TUNER_FREQUENCY:
+ tstate->frequency = state->frequency;
+ break;
+ case DVBFE_TUNER_BANDWIDTH:
+ tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */
+ break;
+ default:
+ pr_err("%s: Unknown parameter (param=%d)\n", __func__, param);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static int tda8261_set_state(struct dvb_frontend *fe,
+ enum tuner_param param,
+ struct tuner_state *tstate)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+ const struct tda8261_config *config = state->config;
+ u32 frequency, N, status = 0;
+ u8 buf[4];
+ int err = 0;
+
+ if (param & DVBFE_TUNER_FREQUENCY) {
+ /**
+ * N = Max VCO Frequency / Channel Spacing
+ * Max VCO Frequency = VCO frequency + (channel spacing - 1)
+ * (to account for half channel spacing on either side)
+ */
+ frequency = tstate->frequency;
+ if ((frequency < 950000) || (frequency > 2150000)) {
+ pr_warn("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
+ return -EINVAL;
+ }
+ N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
+ pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
+ __func__, config->step_size, div_tab[config->step_size], N, N);
+
+ buf[0] = (N >> 8) & 0xff;
+ buf[1] = N & 0xff;
+ buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
+
+ if (frequency < 1450000)
+ buf[3] = 0x00;
+ else if (frequency < 2000000)
+ buf[3] = 0x40;
+ else if (frequency < 2150000)
+ buf[3] = 0x80;
+
+ /* Set params */
+ if ((err = tda8261_write(state, buf)) < 0) {
+ pr_err("%s: I/O Error\n", __func__);
+ return err;
+ }
+ /* sleep for some time */
+ pr_debug("%s: Waiting to Phase LOCK\n", __func__);
+ msleep(20);
+ /* check status */
+ if ((err = tda8261_get_status(fe, &status)) < 0) {
+ pr_err("%s: I/O Error\n", __func__);
+ return err;
+ }
+ if (status == 1) {
+ pr_debug("%s: Tuner Phase locked: status=%d\n", __func__, status);
+ state->frequency = frequency; /* cache successful state */
+ } else {
+ pr_debug("%s: No Phase lock: status=%d\n", __func__, status);
+ }
+ } else {
+ pr_err("%s: Unknown parameter (param=%d)\n", __func__, param);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tda8261_release(struct dvb_frontend *fe)
+{
+ struct tda8261_state *state = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(state);
+ return 0;
+}
+
+static struct dvb_tuner_ops tda8261_ops = {
+
+ .info = {
+ .name = "TDA8261",
+// .tuner_name = NULL,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 0
+ },
+
+ .set_state = tda8261_set_state,
+ .get_state = tda8261_get_state,
+ .get_status = tda8261_get_status,
+ .release = tda8261_release
+};
+
+struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
+ const struct tda8261_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct tda8261_state *state = NULL;
+
+ if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL)
+ goto exit;
+
+ state->config = config;
+ state->i2c = i2c;
+ state->fe = fe;
+ fe->tuner_priv = state;
+ fe->ops.tuner_ops = tda8261_ops;
+
+ fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
+// fe->ops.tuner_ops.tuner_name = &config->buf;
+
+// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n",
+// __func__, fe->ops.tuner_ops.tuner_name);
+ pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
+
+ return fe;
+
+exit:
+ kfree(state);
+ return NULL;
+}
+
+EXPORT_SYMBOL(tda8261_attach);
+
+MODULE_AUTHOR("Manu Abraham");
+MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h
index 006e45351b94..006e45351b94 100644
--- a/drivers/media/dvb/frontends/tda8261.h
+++ b/drivers/media/dvb-frontends/tda8261.h
diff --git a/drivers/media/dvb/frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h
index 1af1ee49b542..1af1ee49b542 100644
--- a/drivers/media/dvb/frontends/tda8261_cfg.h
+++ b/drivers/media/dvb-frontends/tda8261_cfg.h
diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c
index 04bbcc24de0a..04bbcc24de0a 100644
--- a/drivers/media/dvb/frontends/tda826x.c
+++ b/drivers/media/dvb-frontends/tda826x.c
diff --git a/drivers/media/dvb/frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h
index 89e97926ab23..89e97926ab23 100644
--- a/drivers/media/dvb/frontends/tda826x.h
+++ b/drivers/media/dvb-frontends/tda826x.h
diff --git a/drivers/media/dvb/frontends/tdhd1.h b/drivers/media/dvb-frontends/tdhd1.h
index 17750985db0c..17750985db0c 100644
--- a/drivers/media/dvb/frontends/tdhd1.h
+++ b/drivers/media/dvb-frontends/tdhd1.h
diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c
index 029384d1fddd..029384d1fddd 100644
--- a/drivers/media/dvb/frontends/tua6100.c
+++ b/drivers/media/dvb-frontends/tua6100.c
diff --git a/drivers/media/dvb/frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h
index f83dbd5e42ae..f83dbd5e42ae 100644
--- a/drivers/media/dvb/frontends/tua6100.h
+++ b/drivers/media/dvb-frontends/tua6100.h
diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c
index bb42b563c42d..bb42b563c42d 100644
--- a/drivers/media/dvb/frontends/ves1820.c
+++ b/drivers/media/dvb-frontends/ves1820.c
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h
index e902ed634ec3..e902ed634ec3 100644
--- a/drivers/media/dvb/frontends/ves1820.h
+++ b/drivers/media/dvb-frontends/ves1820.h
diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c
index 9c17eacaec24..9c17eacaec24 100644
--- a/drivers/media/dvb/frontends/ves1x93.c
+++ b/drivers/media/dvb-frontends/ves1x93.c
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h
index 8a5a49e808f6..8a5a49e808f6 100644
--- a/drivers/media/dvb/frontends/ves1x93.h
+++ b/drivers/media/dvb-frontends/ves1x93.h
diff --git a/drivers/media/dvb/frontends/z0194a.h b/drivers/media/dvb-frontends/z0194a.h
index 96d86d6eb473..96d86d6eb473 100644
--- a/drivers/media/dvb/frontends/z0194a.h
+++ b/drivers/media/dvb-frontends/z0194a.h
diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c
index 0903d461b8fa..0903d461b8fa 100644
--- a/drivers/media/dvb/frontends/zl10036.c
+++ b/drivers/media/dvb-frontends/zl10036.c
diff --git a/drivers/media/dvb/frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h
index d84b8f8215e9..d84b8f8215e9 100644
--- a/drivers/media/dvb/frontends/zl10036.h
+++ b/drivers/media/dvb-frontends/zl10036.h
diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c
index eff9c5fde50a..eff9c5fde50a 100644
--- a/drivers/media/dvb/frontends/zl10039.c
+++ b/drivers/media/dvb-frontends/zl10039.c
diff --git a/drivers/media/dvb/frontends/zl10039.h b/drivers/media/dvb-frontends/zl10039.h
index 5eee7ea162a1..5eee7ea162a1 100644
--- a/drivers/media/dvb/frontends/zl10039.h
+++ b/drivers/media/dvb-frontends/zl10039.h
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c
index 82946cd517f5..82946cd517f5 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb-frontends/zl10353.c
diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h
index 6e3ca9eed048..6e3ca9eed048 100644
--- a/drivers/media/dvb/frontends/zl10353.h
+++ b/drivers/media/dvb-frontends/zl10353.h
diff --git a/drivers/media/dvb/frontends/zl10353_priv.h b/drivers/media/dvb-frontends/zl10353_priv.h
index e0dd1d3e09dd..e0dd1d3e09dd 100644
--- a/drivers/media/dvb/frontends/zl10353_priv.h
+++ b/drivers/media/dvb-frontends/zl10353_priv.h
diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig
deleted file mode 100644
index f6e40b3a44cc..000000000000
--- a/drivers/media/dvb/Kconfig
+++ /dev/null
@@ -1,91 +0,0 @@
-#
-# DVB device configuration
-#
-
-config DVB_MAX_ADAPTERS
- int "maximum number of DVB/ATSC adapters"
- depends on DVB_CORE
- default 8
- range 1 255
- help
- Maximum number of DVB/ATSC adapters. Increasing this number
- increases the memory consumption of the DVB subsystem even
- if a much lower number of DVB/ATSC adapters is present.
- Only values in the range 4-32 are tested.
-
- If you are unsure about this, use the default value 8
-
-config DVB_DYNAMIC_MINORS
- bool "Dynamic DVB minor allocation"
- depends on DVB_CORE
- default n
- help
- If you say Y here, the DVB subsystem will use dynamic minor
- allocation for any device that uses the DVB major number.
- This means that you can have more than 4 of a single type
- of device (like demuxes and frontends) per adapter, but udev
- will be required to manage the device nodes.
-
- If you are unsure about this, say N here.
-
-menuconfig DVB_CAPTURE_DRIVERS
- bool "DVB/ATSC adapters"
- depends on DVB_CORE
- default y
- ---help---
- Say Y to select Digital TV adapters
-
-if DVB_CAPTURE_DRIVERS && DVB_CORE
-
-comment "Supported SAA7146 based PCI Adapters"
- depends on DVB_CORE && PCI && I2C
-source "drivers/media/dvb/ttpci/Kconfig"
-
-comment "Supported USB Adapters"
- depends on DVB_CORE && USB && I2C
-source "drivers/media/dvb/dvb-usb/Kconfig"
-source "drivers/media/dvb/ttusb-budget/Kconfig"
-source "drivers/media/dvb/ttusb-dec/Kconfig"
-source "drivers/media/dvb/siano/Kconfig"
-
-comment "Supported FlexCopII (B2C2) Adapters"
- depends on DVB_CORE && (PCI || USB) && I2C
-source "drivers/media/dvb/b2c2/Kconfig"
-
-comment "Supported BT878 Adapters"
- depends on DVB_CORE && PCI && I2C
-source "drivers/media/dvb/bt8xx/Kconfig"
-
-comment "Supported Pluto2 Adapters"
- depends on DVB_CORE && PCI && I2C
-source "drivers/media/dvb/pluto2/Kconfig"
-
-comment "Supported SDMC DM1105 Adapters"
- depends on DVB_CORE && PCI && I2C
-source "drivers/media/dvb/dm1105/Kconfig"
-
-comment "Supported FireWire (IEEE 1394) Adapters"
- depends on DVB_CORE && FIREWIRE
-source "drivers/media/dvb/firewire/Kconfig"
-
-comment "Supported Earthsoft PT1 Adapters"
- depends on DVB_CORE && PCI && I2C
-source "drivers/media/dvb/pt1/Kconfig"
-
-comment "Supported Mantis Adapters"
- depends on DVB_CORE && PCI && I2C
- source "drivers/media/dvb/mantis/Kconfig"
-
-comment "Supported nGene Adapters"
- depends on DVB_CORE && PCI && I2C
- source "drivers/media/dvb/ngene/Kconfig"
-
-comment "Supported ddbridge ('Octopus') Adapters"
- depends on DVB_CORE && PCI && I2C
- source "drivers/media/dvb/ddbridge/Kconfig"
-
-comment "Supported DVB Frontends"
- depends on DVB_CORE
-source "drivers/media/dvb/frontends/Kconfig"
-
-endif # DVB_CAPTURE_DRIVERS
diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile
deleted file mode 100644
index b2cefe637a64..000000000000
--- a/drivers/media/dvb/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Makefile for the kernel multimedia device drivers.
-#
-
-obj-y := dvb-core/ \
- frontends/ \
- ttpci/ \
- ttusb-dec/ \
- ttusb-budget/ \
- b2c2/ \
- bt8xx/ \
- dvb-usb/ \
- pluto2/ \
- siano/ \
- dm1105/ \
- pt1/ \
- mantis/ \
- ngene/ \
- ddbridge/
-
-obj-$(CONFIG_DVB_FIREDTV) += firewire/
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
deleted file mode 100644
index 9e5781400744..000000000000
--- a/drivers/media/dvb/b2c2/Kconfig
+++ /dev/null
@@ -1,45 +0,0 @@
-config DVB_B2C2_FLEXCOP
- tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters"
- depends on DVB_CORE && I2C
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_MT312 if !DVB_FE_CUSTOMISE
- select DVB_NXT200X if !DVB_FE_CUSTOMISE
- select DVB_STV0297 if !DVB_FE_CUSTOMISE
- select DVB_BCM3510 if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_S5H1420 if !DVB_FE_CUSTOMISE
- select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
- select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select DVB_CX24123 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE
- help
- Support for the digital TV receiver chip made by B2C2 Inc. included in
- Technisats PCI cards and USB boxes.
-
- Say Y if you own such a device and want to use it.
-
-config DVB_B2C2_FLEXCOP_PCI
- tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI"
- depends on DVB_B2C2_FLEXCOP && PCI && I2C
- help
- Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2.
-
- Say Y if you own such a device and want to use it.
-
-config DVB_B2C2_FLEXCOP_USB
- tristate "Technisat/B2C2 Air/Sky/Cable2PC USB"
- depends on DVB_B2C2_FLEXCOP && USB && I2C
- help
- Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2,
-
- Say Y if you own such a device and want to use it.
-
-config DVB_B2C2_FLEXCOP_DEBUG
- bool "Enable debug for the B2C2 FlexCop drivers"
- depends on DVB_B2C2_FLEXCOP
- help
- Say Y if you want to enable the module option to control debug messages
- of all B2C2 FlexCop drivers.
diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
deleted file mode 100644
index 3d04a8dba99e..000000000000
--- a/drivers/media/dvb/b2c2/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \
- flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o
-obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
-
-ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),)
-b2c2-flexcop-objs += flexcop-dma.o
-endif
-
-b2c2-flexcop-pci-objs = flexcop-pci.o
-obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
-
-b2c2-flexcop-usb-objs = flexcop-usb.o
-obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners/
diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c
deleted file mode 100644
index 26c666dd3514..000000000000
--- a/drivers/media/dvb/b2c2/flexcop-usb.c
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
- * flexcop-usb.c - covers the USB part
- * see flexcop.c for copyright information
- */
-#define FC_LOG_PREFIX "flexcop_usb"
-#include "flexcop-usb.h"
-#include "flexcop-common.h"
-
-/* Version information */
-#define DRIVER_VERSION "0.1"
-#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
-
-/* debug */
-#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
-#define dprintk(level,args...) \
- do { if ((debug & level)) printk(args); } while (0)
-
-#define debug_dump(b, l, method) do {\
- int i; \
- for (i = 0; i < l; i++) \
- method("%02x ", b[i]); \
- method("\n"); \
-} while (0)
-
-#define DEBSTATUS ""
-#else
-#define dprintk(level, args...)
-#define debug_dump(b, l, method)
-#define DEBSTATUS " (debugging is not enabled)"
-#endif
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,"
- "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
-#undef DEBSTATUS
-
-#define deb_info(args...) dprintk(0x01, args)
-#define deb_ts(args...) dprintk(0x02, args)
-#define deb_ctrl(args...) dprintk(0x04, args)
-#define deb_i2c(args...) dprintk(0x08, args)
-#define deb_v8(args...) dprintk(0x10, args)
-
-/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
- * in the IBI address, to make the V8 code simpler.
- * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
- * in general: 0000 0HHH 000L LL00
- * IBI ADDRESS FORMAT: RHHH BLLL
- *
- * where R is the read(1)/write(0) bit, B is the busy bit
- * and HHH and LLL are the two sets of three bits from the PCI address.
- */
-#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
- (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
-#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
- (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
-
-/*
- * DKT 020228
- * - forget about this VENDOR_BUFFER_SIZE, read and write register
- * deal with DWORD or 4 bytes, that should be should from now on
- * - from now on, we don't support anything older than firm 1.00
- * I eliminated the write register as a 2 trip of writing hi word and lo word
- * and force this to write only 4 bytes at a time.
- * NOTE: this should work with all the firmware from 1.00 and newer
- */
-static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
-{
- struct flexcop_usb *fc_usb = fc->bus_specific;
- u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
- u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
- u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
- (read ? 0x80 : 0);
-
- int len = usb_control_msg(fc_usb->udev,
- read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
- request,
- request_type, /* 0xc0 read or 0x40 write */
- wAddress,
- 0,
- val,
- sizeof(u32),
- B2C2_WAIT_FOR_OPERATION_RDW * HZ);
-
- if (len != sizeof(u32)) {
- err("error while %s dword from %d (%d).", read ? "reading" :
- "writing", wAddress, wRegOffsPCI);
- return -EIO;
- }
- return 0;
-}
-/*
- * DKT 010817 - add support for V8 memory read/write and flash update
- */
-static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
- flexcop_usb_request_t req, u8 page, u16 wAddress,
- u8 *pbBuffer, u32 buflen)
-{
- u8 request_type = USB_TYPE_VENDOR;
- u16 wIndex;
- int nWaitTime, pipe, len;
- wIndex = page << 8;
-
- switch (req) {
- case B2C2_USB_READ_V8_MEM:
- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
- request_type |= USB_DIR_IN;
- pipe = B2C2_USB_CTRL_PIPE_IN;
- break;
- case B2C2_USB_WRITE_V8_MEM:
- wIndex |= pbBuffer[0];
- request_type |= USB_DIR_OUT;
- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
- pipe = B2C2_USB_CTRL_PIPE_OUT;
- break;
- case B2C2_USB_FLASH_BLOCK:
- request_type |= USB_DIR_OUT;
- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
- pipe = B2C2_USB_CTRL_PIPE_OUT;
- break;
- default:
- deb_info("unsupported request for v8_mem_req %x.\n", req);
- return -EINVAL;
- }
- deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
- wAddress, wIndex, buflen);
-
- len = usb_control_msg(fc_usb->udev, pipe,
- req,
- request_type,
- wAddress,
- wIndex,
- pbBuffer,
- buflen,
- nWaitTime * HZ);
-
- debug_dump(pbBuffer, len, deb_v8);
- return len == buflen ? 0 : -EIO;
-}
-
-#define bytes_left_to_read_on_page(paddr,buflen) \
- ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
- ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
-
-static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
- flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
- u32 addr, int extended, u8 *buf, u32 len)
-{
- int i,ret = 0;
- u16 wMax;
- u32 pagechunk = 0;
-
- switch(req) {
- case B2C2_USB_READ_V8_MEM:
- wMax = USB_MEM_READ_MAX;
- break;
- case B2C2_USB_WRITE_V8_MEM:
- wMax = USB_MEM_WRITE_MAX;
- break;
- case B2C2_USB_FLASH_BLOCK:
- wMax = USB_FLASH_MAX;
- break;
- default:
- return -EINVAL;
- break;
- }
- for (i = 0; i < len;) {
- pagechunk =
- wMax < bytes_left_to_read_on_page(addr, len) ?
- wMax :
- bytes_left_to_read_on_page(addr, len);
- deb_info("%x\n",
- (addr & V8_MEMORY_PAGE_MASK) |
- (V8_MEMORY_EXTENDED*extended));
-
- ret = flexcop_usb_v8_memory_req(fc_usb, req,
- page_start + (addr / V8_MEMORY_PAGE_SIZE),
- (addr & V8_MEMORY_PAGE_MASK) |
- (V8_MEMORY_EXTENDED*extended),
- &buf[i], pagechunk);
-
- if (ret < 0)
- return ret;
- addr += pagechunk;
- len -= pagechunk;
- }
- return 0;
-}
-
-static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
-{
- return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
- V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
- fc->dvb_adapter.proposed_mac, 6);
-}
-
-#if 0
-static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
- flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
- u16 buflen, u8 *pvBuffer)
-{
- u16 wValue;
- u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
- int nWaitTime = 2,
- pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;
- wValue = (func << 8) | extra;
-
- len = usb_control_msg(fc_usb->udev,pipe,
- B2C2_USB_UTILITY,
- request_type,
- wValue,
- wIndex,
- pvBuffer,
- buflen,
- nWaitTime * HZ);
- return len == buflen ? 0 : -EIO;
-}
-#endif
-
-/* usb i2c stuff */
-static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
- flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
- u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
-{
- struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
- u16 wValue, wIndex;
- int nWaitTime,pipe,len;
- u8 request_type = USB_TYPE_VENDOR;
-
- switch (func) {
- case USB_FUNC_I2C_WRITE:
- case USB_FUNC_I2C_MULTIWRITE:
- case USB_FUNC_I2C_REPEATWRITE:
- /* DKT 020208 - add this to support special case of DiSEqC */
- case USB_FUNC_I2C_CHECKWRITE:
- pipe = B2C2_USB_CTRL_PIPE_OUT;
- nWaitTime = 2;
- request_type |= USB_DIR_OUT;
- break;
- case USB_FUNC_I2C_READ:
- case USB_FUNC_I2C_REPEATREAD:
- pipe = B2C2_USB_CTRL_PIPE_IN;
- nWaitTime = 2;
- request_type |= USB_DIR_IN;
- break;
- default:
- deb_info("unsupported function for i2c_req %x\n", func);
- return -EINVAL;
- }
- wValue = (func << 8) | (i2c->port << 4);
- wIndex = (chipaddr << 8 ) | addr;
-
- deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
- func, request_type, req,
- wValue & 0xff, wValue >> 8,
- wIndex & 0xff, wIndex >> 8);
-
- len = usb_control_msg(fc_usb->udev,pipe,
- req,
- request_type,
- wValue,
- wIndex,
- buf,
- buflen,
- nWaitTime * HZ);
- return len == buflen ? 0 : -EREMOTEIO;
-}
-
-/* actual bus specific access functions,
- make sure prototype are/will be equal to pci */
-static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
- flexcop_ibi_register reg)
-{
- flexcop_ibi_value val;
- val.raw = 0;
- flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
- return val;
-}
-
-static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
- flexcop_ibi_register reg, flexcop_ibi_value val)
-{
- return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
-}
-
-static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
-{
- if (op == FC_READ)
- return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
- USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
- else
- return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
- USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
-}
-
-static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
- u8 *buffer, int buffer_length)
-{
- u8 *b;
- int l;
-
- deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
- fc_usb->tmp_buffer_length, buffer_length);
-
- if (fc_usb->tmp_buffer_length > 0) {
- memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
- buffer_length);
- fc_usb->tmp_buffer_length += buffer_length;
- b = fc_usb->tmp_buffer;
- l = fc_usb->tmp_buffer_length;
- } else {
- b=buffer;
- l=buffer_length;
- }
-
- while (l >= 190) {
- if (*b == 0xff) {
- switch (*(b+1) & 0x03) {
- case 0x01: /* media packet */
- if (*(b+2) == 0x47)
- flexcop_pass_dmx_packets(
- fc_usb->fc_dev, b+2, 1);
- else
- deb_ts(
- "not ts packet %02x %02x %02x %02x \n",
- *(b+2), *(b+3),
- *(b+4), *(b+5));
- b += 190;
- l -= 190;
- break;
- default:
- deb_ts("wrong packet type\n");
- l = 0;
- break;
- }
- } else {
- deb_ts("wrong header\n");
- l = 0;
- }
- }
-
- if (l>0)
- memcpy(fc_usb->tmp_buffer, b, l);
- fc_usb->tmp_buffer_length = l;
-}
-
-static void flexcop_usb_urb_complete(struct urb *urb)
-{
- struct flexcop_usb *fc_usb = urb->context;
- int i;
-
- if (urb->actual_length > 0)
- deb_ts("urb completed, bufsize: %d actlen; %d\n",
- urb->transfer_buffer_length, urb->actual_length);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- if (urb->iso_frame_desc[i].status < 0) {
- err("iso frame descriptor %d has an error: %d\n", i,
- urb->iso_frame_desc[i].status);
- } else
- if (urb->iso_frame_desc[i].actual_length > 0) {
- deb_ts("passed %d bytes to the demux\n",
- urb->iso_frame_desc[i].actual_length);
-
- flexcop_usb_process_frame(fc_usb,
- urb->transfer_buffer +
- urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].actual_length);
- }
- urb->iso_frame_desc[i].status = 0;
- urb->iso_frame_desc[i].actual_length = 0;
- }
- usb_submit_urb(urb,GFP_ATOMIC);
-}
-
-static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
-{
- /* submit/kill iso packets */
- return 0;
-}
-
-static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
-{
- int i;
- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
- if (fc_usb->iso_urb[i] != NULL) {
- deb_ts("unlinking/killing urb no. %d\n",i);
- usb_kill_urb(fc_usb->iso_urb[i]);
- usb_free_urb(fc_usb->iso_urb[i]);
- }
-
- if (fc_usb->iso_buffer != NULL)
- pci_free_consistent(NULL,
- fc_usb->buffer_size, fc_usb->iso_buffer,
- fc_usb->dma_addr);
-}
-
-static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
-{
- u16 frame_size = le16_to_cpu(
- fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
- int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
- frame_size, i, j, ret;
- int buffer_offset = 0;
-
- deb_ts("creating %d iso-urbs with %d frames "
- "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,
- B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
-
- fc_usb->iso_buffer = pci_alloc_consistent(NULL,
- bufsize, &fc_usb->dma_addr);
- if (fc_usb->iso_buffer == NULL)
- return -ENOMEM;
-
- memset(fc_usb->iso_buffer, 0, bufsize);
- fc_usb->buffer_size = bufsize;
-
- /* creating iso urbs */
- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
- fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
- GFP_ATOMIC);
- if (fc_usb->iso_urb[i] == NULL) {
- ret = -ENOMEM;
- goto urb_error;
- }
- }
-
- /* initialising and submitting iso urbs */
- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
- int frame_offset = 0;
- struct urb *urb = fc_usb->iso_urb[i];
- deb_ts("initializing and submitting urb no. %d "
- "(buf_offset: %d).\n", i, buffer_offset);
-
- urb->dev = fc_usb->udev;
- urb->context = fc_usb;
- urb->complete = flexcop_usb_urb_complete;
- urb->pipe = B2C2_USB_DATA_PIPE;
- urb->transfer_flags = URB_ISO_ASAP;
- urb->interval = 1;
- urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
- urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
- urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
-
- buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
- for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
- deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
- i, j, frame_offset);
- urb->iso_frame_desc[j].offset = frame_offset;
- urb->iso_frame_desc[j].length = frame_size;
- frame_offset += frame_size;
- }
-
- if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
- err("submitting urb %d failed with %d.", i, ret);
- goto urb_error;
- }
- deb_ts("submitted urb no. %d.\n",i);
- }
-
- /* SRAM */
- flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
- FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
- FC_SRAM_DEST_TARGET_WAN_USB);
- flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
- flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
- return 0;
-
-urb_error:
- flexcop_usb_transfer_exit(fc_usb);
- return ret;
-}
-
-static int flexcop_usb_init(struct flexcop_usb *fc_usb)
-{
- /* use the alternate setting with the larges buffer */
- usb_set_interface(fc_usb->udev,0,1);
- switch (fc_usb->udev->speed) {
- case USB_SPEED_LOW:
- err("cannot handle USB speed because it is too slow.");
- return -ENODEV;
- break;
- case USB_SPEED_FULL:
- info("running at FULL speed.");
- break;
- case USB_SPEED_HIGH:
- info("running at HIGH speed.");
- break;
- case USB_SPEED_UNKNOWN: /* fall through */
- default:
- err("cannot handle USB speed because it is unknown.");
- return -ENODEV;
- }
- usb_set_intfdata(fc_usb->uintf, fc_usb);
- return 0;
-}
-
-static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
-{
- usb_set_intfdata(fc_usb->uintf, NULL);
-}
-
-static int flexcop_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct flexcop_usb *fc_usb = NULL;
- struct flexcop_device *fc = NULL;
- int ret;
-
- if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
- err("out of memory\n");
- return -ENOMEM;
- }
-
- /* general flexcop init */
- fc_usb = fc->bus_specific;
- fc_usb->fc_dev = fc;
-
- fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
- fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
- fc->i2c_request = flexcop_usb_i2c_request;
- fc->get_mac_addr = flexcop_usb_get_mac_addr;
-
- fc->stream_control = flexcop_usb_stream_control;
-
- fc->pid_filtering = 1;
- fc->bus_type = FC_USB;
-
- fc->dev = &udev->dev;
- fc->owner = THIS_MODULE;
-
- /* bus specific part */
- fc_usb->udev = udev;
- fc_usb->uintf = intf;
- if ((ret = flexcop_usb_init(fc_usb)) != 0)
- goto err_kfree;
-
- /* init flexcop */
- if ((ret = flexcop_device_initialize(fc)) != 0)
- goto err_usb_exit;
-
- /* xfer init */
- if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
- goto err_fc_exit;
-
- info("%s successfully initialized and connected.", DRIVER_NAME);
- return 0;
-
-err_fc_exit:
- flexcop_device_exit(fc);
-err_usb_exit:
- flexcop_usb_exit(fc_usb);
-err_kfree:
- flexcop_device_kfree(fc);
- return ret;
-}
-
-static void flexcop_usb_disconnect(struct usb_interface *intf)
-{
- struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
- flexcop_usb_transfer_exit(fc_usb);
- flexcop_device_exit(fc_usb->fc_dev);
- flexcop_usb_exit(fc_usb);
- flexcop_device_kfree(fc_usb->fc_dev);
- info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
-}
-
-static struct usb_device_id flexcop_usb_table [] = {
- { USB_DEVICE(0x0af7, 0x0101) },
- { }
-};
-MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver flexcop_usb_driver = {
- .name = "b2c2_flexcop_usb",
- .probe = flexcop_usb_probe,
- .disconnect = flexcop_usb_disconnect,
- .id_table = flexcop_usb_table,
-};
-
-module_usb_driver(flexcop_usb_driver);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_NAME);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c
deleted file mode 100644
index b1e8c99f469b..000000000000
--- a/drivers/media/dvb/b2c2/flexcop.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
- * flexcop.c - main module part
- * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
- * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
- *
- * Acknowledgements:
- * John Jurrius from BBTI, Inc. for extensive support
- * with code examples and data books
- * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
- *
- * Contributions to the skystar2-driver have been done by
- * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
- * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
- * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
- * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
- * filtering)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include "flexcop.h"
-
-#define DRIVER_NAME "B2C2 FlexcopII/II(b)/III digital TV receiver chip"
-#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de"
-
-#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
-#define DEBSTATUS ""
-#else
-#define DEBSTATUS " (debugging is not enabled)"
-#endif
-
-int b2c2_flexcop_debug;
-module_param_named(debug, b2c2_flexcop_debug, int, 0644);
-MODULE_PARM_DESC(debug,
- "set debug level (1=info,2=tuner,4=i2c,8=ts,"
- "16=sram,32=reg (|-able))."
- DEBSTATUS);
-#undef DEBSTATUS
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-/* global zero for ibi values */
-flexcop_ibi_value ibi_zero;
-
-static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct flexcop_device *fc = dvbdmxfeed->demux->priv;
- return flexcop_pid_feed_control(fc, dvbdmxfeed, 1);
-}
-
-static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct flexcop_device *fc = dvbdmxfeed->demux->priv;
- return flexcop_pid_feed_control(fc, dvbdmxfeed, 0);
-}
-
-static int flexcop_dvb_init(struct flexcop_device *fc)
-{
- int ret = dvb_register_adapter(&fc->dvb_adapter,
- "FlexCop Digital TV device", fc->owner,
- fc->dev, adapter_nr);
- if (ret < 0) {
- err("error registering DVB adapter");
- return ret;
- }
- fc->dvb_adapter.priv = fc;
-
- fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING
- | DMX_MEMORY_BASED_FILTERING);
- fc->demux.priv = fc;
- fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
- fc->demux.start_feed = flexcop_dvb_start_feed;
- fc->demux.stop_feed = flexcop_dvb_stop_feed;
- fc->demux.write_to_decoder = NULL;
-
- ret = dvb_dmx_init(&fc->demux);
- if (ret < 0) {
- err("dvb_dmx failed: error %d", ret);
- goto err_dmx;
- }
-
- fc->hw_frontend.source = DMX_FRONTEND_0;
-
- fc->dmxdev.filternum = fc->demux.feednum;
- fc->dmxdev.demux = &fc->demux.dmx;
- fc->dmxdev.capabilities = 0;
- ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter);
- if (ret < 0) {
- err("dvb_dmxdev_init failed: error %d", ret);
- goto err_dmx_dev;
- }
-
- ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend);
- if (ret < 0) {
- err("adding hw_frontend to dmx failed: error %d", ret);
- goto err_dmx_add_hw_frontend;
- }
-
- fc->mem_frontend.source = DMX_MEMORY_FE;
- ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend);
- if (ret < 0) {
- err("adding mem_frontend to dmx failed: error %d", ret);
- goto err_dmx_add_mem_frontend;
- }
-
- ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend);
- if (ret < 0) {
- err("connect frontend failed: error %d", ret);
- goto err_connect_frontend;
- }
-
- ret = dvb_net_init(&fc->dvb_adapter, &fc->dvbnet, &fc->demux.dmx);
- if (ret < 0) {
- err("dvb_net_init failed: error %d", ret);
- goto err_net;
- }
-
- fc->init_state |= FC_STATE_DVB_INIT;
- return 0;
-
-err_net:
- fc->demux.dmx.disconnect_frontend(&fc->demux.dmx);
-err_connect_frontend:
- fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend);
-err_dmx_add_mem_frontend:
- fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend);
-err_dmx_add_hw_frontend:
- dvb_dmxdev_release(&fc->dmxdev);
-err_dmx_dev:
- dvb_dmx_release(&fc->demux);
-err_dmx:
- dvb_unregister_adapter(&fc->dvb_adapter);
- return ret;
-}
-
-static void flexcop_dvb_exit(struct flexcop_device *fc)
-{
- if (fc->init_state & FC_STATE_DVB_INIT) {
- dvb_net_release(&fc->dvbnet);
-
- fc->demux.dmx.close(&fc->demux.dmx);
- fc->demux.dmx.remove_frontend(&fc->demux.dmx,
- &fc->mem_frontend);
- fc->demux.dmx.remove_frontend(&fc->demux.dmx,
- &fc->hw_frontend);
- dvb_dmxdev_release(&fc->dmxdev);
- dvb_dmx_release(&fc->demux);
- dvb_unregister_adapter(&fc->dvb_adapter);
- deb_info("deinitialized dvb stuff\n");
- }
- fc->init_state &= ~FC_STATE_DVB_INIT;
-}
-
-/* these methods are necessary to achieve the long-term-goal of hiding the
- * struct flexcop_device from the bus-parts */
-void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len)
-{
- dvb_dmx_swfilter(&fc->demux, buf, len);
-}
-EXPORT_SYMBOL(flexcop_pass_dmx_data);
-
-void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no)
-{
- dvb_dmx_swfilter_packets(&fc->demux, buf, no);
-}
-EXPORT_SYMBOL(flexcop_pass_dmx_packets);
-
-static void flexcop_reset(struct flexcop_device *fc)
-{
- flexcop_ibi_value v210, v204;
-
- /* reset the flexcop itself */
- fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
-
- v210.raw = 0;
- v210.sw_reset_210.reset_block_000 = 1;
- v210.sw_reset_210.reset_block_100 = 1;
- v210.sw_reset_210.reset_block_200 = 1;
- v210.sw_reset_210.reset_block_300 = 1;
- v210.sw_reset_210.reset_block_400 = 1;
- v210.sw_reset_210.reset_block_500 = 1;
- v210.sw_reset_210.reset_block_600 = 1;
- v210.sw_reset_210.reset_block_700 = 1;
- v210.sw_reset_210.Block_reset_enable = 0xb2;
- v210.sw_reset_210.Special_controls = 0xc259;
- fc->write_ibi_reg(fc,sw_reset_210,v210);
- msleep(1);
-
- /* reset the periphical devices */
-
- v204 = fc->read_ibi_reg(fc,misc_204);
- v204.misc_204.Per_reset_sig = 0;
- fc->write_ibi_reg(fc,misc_204,v204);
- msleep(1);
- v204.misc_204.Per_reset_sig = 1;
- fc->write_ibi_reg(fc,misc_204,v204);
-}
-
-void flexcop_reset_block_300(struct flexcop_device *fc)
-{
- flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208),
- v210 = fc->read_ibi_reg(fc, sw_reset_210);
-
- deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
- fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
-
- v210.sw_reset_210.reset_block_300 = 1;
- v210.sw_reset_210.Block_reset_enable = 0xb2;
-
- fc->write_ibi_reg(fc,sw_reset_210,v210);
- fc->write_ibi_reg(fc,ctrl_208,v208_save);
-}
-
-struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
-{
- void *bus;
- struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device),
- GFP_KERNEL);
- if (!fc) {
- err("no memory");
- return NULL;
- }
-
- bus = kzalloc(bus_specific_len, GFP_KERNEL);
- if (!bus) {
- err("no memory");
- kfree(fc);
- return NULL;
- }
-
- fc->bus_specific = bus;
-
- return fc;
-}
-EXPORT_SYMBOL(flexcop_device_kmalloc);
-
-void flexcop_device_kfree(struct flexcop_device *fc)
-{
- kfree(fc->bus_specific);
- kfree(fc);
-}
-EXPORT_SYMBOL(flexcop_device_kfree);
-
-int flexcop_device_initialize(struct flexcop_device *fc)
-{
- int ret;
- ibi_zero.raw = 0;
-
- flexcop_reset(fc);
- flexcop_determine_revision(fc);
- flexcop_sram_init(fc);
- flexcop_hw_filter_init(fc);
- flexcop_smc_ctrl(fc, 0);
-
- ret = flexcop_dvb_init(fc);
- if (ret)
- goto error;
-
- /* i2c has to be done before doing EEProm stuff -
- * because the EEProm is accessed via i2c */
- ret = flexcop_i2c_init(fc);
- if (ret)
- goto error;
-
- /* do the MAC address reading after initializing the dvb_adapter */
- if (fc->get_mac_addr(fc, 0) == 0) {
- u8 *b = fc->dvb_adapter.proposed_mac;
- info("MAC address = %pM", b);
- flexcop_set_mac_filter(fc,b);
- flexcop_mac_filter_ctrl(fc,1);
- } else
- warn("reading of MAC address failed.\n");
-
- ret = flexcop_frontend_init(fc);
- if (ret)
- goto error;
-
- flexcop_device_name(fc,"initialization of","complete");
- return 0;
-
-error:
- flexcop_device_exit(fc);
- return ret;
-}
-EXPORT_SYMBOL(flexcop_device_initialize);
-
-void flexcop_device_exit(struct flexcop_device *fc)
-{
- flexcop_frontend_exit(fc);
- flexcop_i2c_exit(fc);
- flexcop_dvb_exit(fc);
-}
-EXPORT_SYMBOL(flexcop_device_exit);
-
-static int flexcop_module_init(void)
-{
- info(DRIVER_NAME " loaded successfully");
- return 0;
-}
-
-static void flexcop_module_cleanup(void)
-{
- info(DRIVER_NAME " unloaded successfully");
-}
-
-module_init(flexcop_module_init);
-module_exit(flexcop_module_cleanup);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_NAME);
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
deleted file mode 100644
index 8668e634c7ec..000000000000
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ /dev/null
@@ -1,22 +0,0 @@
-config DVB_BT8XX
- tristate "BT8xx based PCI cards"
- depends on DVB_CORE && PCI && I2C && VIDEO_BT848
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_SP887X if !DVB_FE_CUSTOMISE
- select DVB_NXT6000 if !DVB_FE_CUSTOMISE
- select DVB_CX24110 if !DVB_FE_CUSTOMISE
- select DVB_OR51211 if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- help
- Support for PCI cards based on the Bt8xx PCI bridge. Examples are
- the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
- the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and
- some AVerMedia cards.
-
- Since these cards have no MPEG decoder onboard, they transmit
- only compressed MPEG data over the PCI bus, so you need
- an external software decoder to watch TV on your computer.
-
- Say Y if you own such a device and want to use it.
diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile
deleted file mode 100644
index 0713b3af2050..000000000000
--- a/drivers/media/dvb/bt8xx/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/video/bt8xx
-ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
deleted file mode 100644
index 66f52f116b60..000000000000
--- a/drivers/media/dvb/bt8xx/dst_ca.c
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- CA-driver for TwinHan DST Frontend/Card
-
- Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/string.h>
-#include <linux/dvb/ca.h>
-#include "dvbdev.h"
-#include "dvb_frontend.h"
-#include "dst_ca.h"
-#include "dst_common.h"
-
-#define DST_CA_ERROR 0
-#define DST_CA_NOTICE 1
-#define DST_CA_INFO 2
-#define DST_CA_DEBUG 3
-
-#define dprintk(x, y, z, format, arg...) do { \
- if (z) { \
- if ((x > DST_CA_ERROR) && (x > y)) \
- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
- else if ((x > DST_CA_NOTICE) && (x > y)) \
- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
- else if ((x > DST_CA_INFO) && (x > y)) \
- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
- else if ((x > DST_CA_DEBUG) && (x > y)) \
- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
- } else { \
- if (x > y) \
- printk(format, ## arg); \
- } \
-} while(0)
-
-
-static DEFINE_MUTEX(dst_ca_mutex);
-static unsigned int verbose = 5;
-module_param(verbose, int, 0644);
-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
-
-/* Need some more work */
-static int ca_set_slot_descr(void)
-{
- /* We could make this more graceful ? */
- return -EOPNOTSUPP;
-}
-
-/* Need some more work */
-static int ca_set_pid(void)
-{
- /* We could make this more graceful ? */
- return -EOPNOTSUPP;
-}
-
-static void put_command_and_length(u8 *data, int command, int length)
-{
- data[0] = (command >> 16) & 0xff;
- data[1] = (command >> 8) & 0xff;
- data[2] = command & 0xff;
- data[3] = length;
-}
-
-static void put_checksum(u8 *check_string, int length)
-{
- dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum.");
- dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length);
- check_string[length] = dst_check_sum (check_string, length);
- dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]);
-}
-
-static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
-{
- u8 reply;
-
- mutex_lock(&state->dst_mutex);
- dst_comm_init(state);
- msleep(65);
-
- if (write_dst(state, data, len)) {
- dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover");
- dst_error_recovery(state);
- goto error;
- }
- if ((dst_pio_disable(state)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed.");
- goto error;
- }
- if (read_dst(state, &reply, GET_ACK) < 0) {
- dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
- dst_error_recovery(state);
- goto error;
- }
- if (read) {
- if (! dst_wait_dst_ready(state, LONG_DELAY)) {
- dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready");
- goto error;
- }
- if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
- dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
- dst_error_recovery(state);
- goto error;
- }
- }
- mutex_unlock(&state->dst_mutex);
- return 0;
-
-error:
- mutex_unlock(&state->dst_mutex);
- return -EIO;
-}
-
-
-static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
-{
- u8 dst_ca_comm_err = 0;
-
- while (dst_ca_comm_err < RETRIES) {
- dprintk(verbose, DST_CA_NOTICE, 1, " Put Command");
- if (dst_ci_command(state, data, ca_string, len, read)) { // If error
- dst_error_recovery(state);
- dst_ca_comm_err++; // work required here.
- } else {
- break;
- }
- }
-
- if(dst_ca_comm_err == RETRIES)
- return -1;
-
- return 0;
-}
-
-
-
-static int ca_get_app_info(struct dst_state *state)
-{
- int length, str_length;
- static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
-
- put_checksum(&command[0], command[0]);
- if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
- return -1;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
- dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================");
- dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]",
- state->messages[7], (state->messages[8] << 8) | state->messages[9],
- (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12]));
- dprintk(verbose, DST_CA_INFO, 1, " ==================================================================================================");
-
- // Transform dst message to correct application_info message
- length = state->messages[5];
- str_length = length - 6;
- if (str_length < 0) {
- str_length = 0;
- dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering.");
- }
-
- // First, the command and length fields
- put_command_and_length(&state->messages[0], CA_APP_INFO, length);
-
- // Copy application_type, application_manufacturer and manufacturer_code
- memcpy(&state->messages[4], &state->messages[7], 5);
-
- // Set string length and copy string
- state->messages[9] = str_length;
- memcpy(&state->messages[10], &state->messages[12], str_length);
-
- return 0;
-}
-
-static int ca_get_ca_info(struct dst_state *state)
-{
- int srcPtr, dstPtr, i, num_ids;
- static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff};
- const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7;
-
- put_checksum(&slot_command[0], slot_command[0]);
- if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
- return -1;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
-
- // Print raw data
- dprintk(verbose, DST_CA_INFO, 0, " DST data = [");
- for (i = 0; i < state->messages[0] + 1; i++) {
- dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]);
- }
- dprintk(verbose, DST_CA_INFO, 0, "]\n");
-
- // Set the command and length of the output
- num_ids = state->messages[in_num_ids_pos];
- if (num_ids >= 100) {
- num_ids = 100;
- dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering.");
- }
- put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2);
-
- dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = [");
- srcPtr = in_system_id_pos;
- dstPtr = out_system_id_pos;
- for(i = 0; i < num_ids; i++) {
- dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]);
- // Append to output
- state->messages[dstPtr + 0] = state->messages[srcPtr + 0];
- state->messages[dstPtr + 1] = state->messages[srcPtr + 1];
- srcPtr += 2;
- dstPtr += 2;
- }
- dprintk(verbose, DST_CA_INFO, 0, "]\n");
-
- return 0;
-}
-
-static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg)
-{
- int i;
- u8 slot_cap[256];
- static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
-
- put_checksum(&slot_command[0], slot_command[0]);
- if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
- return -1;
- }
- dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !");
-
- /* Will implement the rest soon */
-
- dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]);
- dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
- for (i = 0; i < slot_cap[0] + 1; i++)
- dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]);
- dprintk(verbose, DST_CA_INFO, 0, "\n");
-
- p_ca_caps->slot_num = 1;
- p_ca_caps->slot_type = 1;
- p_ca_caps->descr_num = slot_cap[7];
- p_ca_caps->descr_type = 1;
-
- if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps)))
- return -EFAULT;
-
- return 0;
-}
-
-/* Need some more work */
-static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
-{
- return -EOPNOTSUPP;
-}
-
-
-static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg)
-{
- int i;
- static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
-
- u8 *slot_info = state->messages;
-
- put_checksum(&slot_command[0], 7);
- if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
- return -1;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
-
- /* Will implement the rest soon */
-
- dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]);
- dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
- for (i = 0; i < 8; i++)
- dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]);
- dprintk(verbose, DST_CA_INFO, 0, "\n");
-
- if (slot_info[4] & 0x80) {
- p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
- p_ca_slot_info->num = 1;
- p_ca_slot_info->type = CA_CI;
- } else if (slot_info[4] & 0x40) {
- p_ca_slot_info->flags = CA_CI_MODULE_READY;
- p_ca_slot_info->num = 1;
- p_ca_slot_info->type = CA_CI;
- } else
- p_ca_slot_info->flags = 0;
-
- if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
-{
- u8 i = 0;
- u32 command = 0;
-
- if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg)))
- return -EFAULT;
-
- if (p_ca_message->msg) {
- dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%02x %02x %02x]", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]);
-
- for (i = 0; i < 3; i++) {
- command = command | p_ca_message->msg[i];
- if (i < 2)
- command = command << 8;
- }
- dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command);
-
- switch (command) {
- case CA_APP_INFO:
- memcpy(p_ca_message->msg, state->messages, 128);
- if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
- return -EFAULT;
- break;
- case CA_INFO:
- memcpy(p_ca_message->msg, state->messages, 128);
- if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
- return -EFAULT;
- break;
- }
- }
-
- return 0;
-}
-
-static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length)
-{
- if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
- hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
- hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
- } else {
- if (length > 247) {
- dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !");
- return -1;
- }
- hw_buffer->msg[0] = (length & 0xff) + 7;
- hw_buffer->msg[1] = 0x40;
- hw_buffer->msg[2] = 0x03;
- hw_buffer->msg[3] = 0x00;
- hw_buffer->msg[4] = 0x03;
- hw_buffer->msg[5] = length & 0xff;
- hw_buffer->msg[6] = 0x00;
-
- /*
- * Need to compute length for EN50221 section 8.3.2, for the time being
- * assuming 8.3.2 is not applicable
- */
- memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length);
- }
-
- return 0;
-}
-
-static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply)
-{
- if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed.");
- dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST.");
- rdc_reset_state(state);
- return -1;
- }
- dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success.");
-
- return 0;
-}
-
-static u32 asn_1_decode(u8 *asn_1_array)
-{
- u8 length_field = 0, word_count = 0, count = 0;
- u32 length = 0;
-
- length_field = asn_1_array[0];
- dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field);
- if (length_field < 0x80) {
- length = length_field & 0x7f;
- dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length);
- } else {
- word_count = length_field & 0x7f;
- for (count = 0; count < word_count; count++) {
- length = length << 8;
- length += asn_1_array[count + 1];
- dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length);
- }
- }
- return length;
-}
-
-static int debug_string(u8 *msg, u32 length, u32 offset)
-{
- u32 i;
-
- dprintk(verbose, DST_CA_DEBUG, 0, " String=[ ");
- for (i = offset; i < length; i++)
- dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]);
- dprintk(verbose, DST_CA_DEBUG, 0, "]\n");
-
- return 0;
-}
-
-
-static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
-{
- u32 length = 0;
- u8 tag_length = 8;
-
- length = asn_1_decode(&p_ca_message->msg[3]);
- dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length);
- debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */
-
- memset(hw_buffer->msg, '\0', length);
- handle_dst_tag(state, p_ca_message, hw_buffer, length);
- put_checksum(hw_buffer->msg, hw_buffer->msg[0]);
-
- debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */
- write_to_8820(state, hw_buffer, (length + tag_length), reply);
-
- return 0;
-}
-
-
-/* Board supports CA PMT reply ? */
-static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
-{
- int ca_pmt_reply_test = 0;
-
- /* Do test board */
- /* Not there yet but soon */
-
- /* CA PMT Reply capable */
- if (ca_pmt_reply_test) {
- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
- return -1;
- }
-
- /* Process CA PMT Reply */
- /* will implement soon */
- dprintk(verbose, DST_CA_ERROR, 1, " Not there yet");
- }
- /* CA PMT Reply not capable */
- if (!ca_pmt_reply_test) {
- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
- return -1;
- }
- dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !");
- /* put a dummy message */
-
- }
- return 0;
-}
-
-static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
-{
- int i = 0;
-
- u32 command = 0;
- struct ca_msg *hw_buffer;
- int result = 0;
-
- if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
- dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
- return -ENOMEM;
- }
- dprintk(verbose, DST_CA_DEBUG, 1, " ");
-
- if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) {
- result = -EFAULT;
- goto free_mem_and_exit;
- }
-
-
- if (p_ca_message->msg) {
- /* EN50221 tag */
- command = 0;
-
- for (i = 0; i < 3; i++) {
- command = command | p_ca_message->msg[i];
- if (i < 2)
- command = command << 8;
- }
- dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command);
-
- switch (command) {
- case CA_PMT:
- dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT");
- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !");
- break;
- case CA_PMT_REPLY:
- dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY");
- /* Have to handle the 2 basic types of cards here */
- if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !");
- break;
- case CA_APP_INFO_ENQUIRY: // only for debugging
- dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information");
-
- if ((ca_get_app_info(state)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !");
- break;
- case CA_INFO_ENQUIRY:
- dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information");
-
- if ((ca_get_ca_info(state)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !");
- break;
- }
- }
-free_mem_and_exit:
- kfree (hw_buffer);
-
- return result;
-}
-
-static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg)
-{
- struct dvb_device *dvbdev;
- struct dst_state *state;
- struct ca_slot_info *p_ca_slot_info;
- struct ca_caps *p_ca_caps;
- struct ca_msg *p_ca_message;
- void __user *arg = (void __user *)ioctl_arg;
- int result = 0;
-
- mutex_lock(&dst_ca_mutex);
- dvbdev = file->private_data;
- state = (struct dst_state *)dvbdev->priv;
- p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
- p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
- p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
- if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) {
- dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
- result = -ENOMEM;
- goto free_mem_and_exit;
- }
-
- /* We have now only the standard ioctl's, the driver is upposed to handle internals. */
- switch (cmd) {
- case CA_SEND_MSG:
- dprintk(verbose, DST_CA_INFO, 1, " Sending message");
- if ((ca_send_message(state, p_ca_message, arg)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- break;
- case CA_GET_MSG:
- dprintk(verbose, DST_CA_INFO, 1, " Getting message");
- if ((ca_get_message(state, p_ca_message, arg)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !");
- break;
- case CA_RESET:
- dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST");
- dst_error_bailout(state);
- msleep(4000);
- break;
- case CA_GET_SLOT_INFO:
- dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info");
- if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !");
- break;
- case CA_GET_CAP:
- dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities");
- if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !");
- break;
- case CA_GET_DESCR_INFO:
- dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description");
- if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !");
- break;
- case CA_SET_DESCR:
- dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler");
- if ((ca_set_slot_descr()) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !");
- break;
- case CA_SET_PID:
- dprintk(verbose, DST_CA_INFO, 1, " Setting PID");
- if ((ca_set_pid()) < 0) {
- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !");
- result = -1;
- goto free_mem_and_exit;
- }
- dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
- default:
- result = -EOPNOTSUPP;
- };
- free_mem_and_exit:
- kfree (p_ca_message);
- kfree (p_ca_slot_info);
- kfree (p_ca_caps);
-
- mutex_unlock(&dst_ca_mutex);
- return result;
-}
-
-static int dst_ca_open(struct inode *inode, struct file *file)
-{
- dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
- try_module_get(THIS_MODULE);
-
- return 0;
-}
-
-static int dst_ca_release(struct inode *inode, struct file *file)
-{
- dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
- module_put(THIS_MODULE);
-
- return 0;
-}
-
-static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
-{
- ssize_t bytes_read = 0;
-
- dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
-
- return bytes_read;
-}
-
-static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
-{
- dprintk(verbose, DST_CA_DEBUG, 1, " Device write.");
-
- return 0;
-}
-
-static const struct file_operations dst_ca_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = dst_ca_ioctl,
- .open = dst_ca_open,
- .release = dst_ca_release,
- .read = dst_ca_read,
- .write = dst_ca_write,
- .llseek = noop_llseek,
-};
-
-static struct dvb_device dvbdev_ca = {
- .priv = NULL,
- .users = 1,
- .readers = 1,
- .writers = 1,
- .fops = &dst_ca_fops
-};
-
-struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
-{
- struct dvb_device *dvbdev;
-
- dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
- if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
- dst->dst_ca = dvbdev;
- return dst->dst_ca;
- }
-
- return NULL;
-}
-
-EXPORT_SYMBOL(dst_ca_attach);
-
-MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
-MODULE_AUTHOR("Manu Abraham");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ddbridge/Kconfig b/drivers/media/dvb/ddbridge/Kconfig
deleted file mode 100644
index d099e1a12c85..000000000000
--- a/drivers/media/dvb/ddbridge/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-config DVB_DDBRIDGE
- tristate "Digital Devices bridge support"
- depends on DVB_CORE && PCI && I2C
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_STV6110x if !DVB_FE_CUSTOMISE
- select DVB_STV090x if !DVB_FE_CUSTOMISE
- select DVB_DRXK if !DVB_FE_CUSTOMISE
- select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
- ---help---
- Support for cards with the Digital Devices PCI express bridge:
- - Octopus PCIe Bridge
- - Octopus mini PCIe Bridge
- - Octopus LE
- - DuoFlex S2 Octopus
- - DuoFlex CT Octopus
- - cineS2(v6)
-
- Say Y if you own such a card and want to use it.
diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile
deleted file mode 100644
index 38019bafb862..000000000000
--- a/drivers/media/dvb/ddbridge/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Makefile for the ddbridge device driver
-#
-
-ddbridge-objs := ddbridge-core.o
-
-obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners/
-
-# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
deleted file mode 100644
index ebf3f05839d2..000000000000
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ /dev/null
@@ -1,1723 +0,0 @@
-/*
- * ddbridge.c: Digital Devices PCIe bridge driver
- *
- * Copyright (C) 2010-2011 Digital Devices GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/io.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/timer.h>
-#include <linux/i2c.h>
-#include <linux/swab.h>
-#include <linux/vmalloc.h>
-#include "ddbridge.h"
-
-#include "ddbridge-regs.h"
-
-#include "tda18271c2dd.h"
-#include "stv6110x.h"
-#include "stv090x.h"
-#include "lnbh24.h"
-#include "drxk.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-/* MSI had problems with lost interrupts, fixed but needs testing */
-#undef CONFIG_PCI_MSI
-
-/******************************************************************************/
-
-static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
-{
- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1 } };
- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
-}
-
-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
-{
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = &reg, .len = 1 },
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1 } };
- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
-}
-
-static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
- u16 reg, u8 *val)
-{
- u8 msg[2] = {reg>>8, reg&0xff};
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = msg, .len = 2},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1} };
- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
-}
-
-static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
-{
- struct ddb *dev = i2c->dev;
- int stat;
- u32 val;
-
- i2c->done = 0;
- ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
- stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
- if (stat <= 0) {
- printk(KERN_ERR "I2C timeout\n");
- { /* MSI debugging*/
- u32 istat = ddbreadl(INTERRUPT_STATUS);
- printk(KERN_ERR "IRS %08x\n", istat);
- ddbwritel(istat, INTERRUPT_ACK);
- }
- return -EIO;
- }
- val = ddbreadl(i2c->regs+I2C_COMMAND);
- if (val & 0x70000)
- return -EIO;
- return 0;
-}
-
-static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
- struct i2c_msg msg[], int num)
-{
- struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
- struct ddb *dev = i2c->dev;
- u8 addr = 0;
-
- if (num)
- addr = msg[0].addr;
-
- if (num == 2 && msg[1].flags & I2C_M_RD &&
- !(msg[0].flags & I2C_M_RD)) {
- memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
- msg[0].buf, msg[0].len);
- ddbwritel(msg[0].len|(msg[1].len << 16),
- i2c->regs+I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 1)) {
- memcpy_fromio(msg[1].buf,
- dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
- msg[1].len);
- return num;
- }
- }
-
- if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
- ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
- ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 2))
- return num;
- }
- if (num == 1 && (msg[0].flags & I2C_M_RD)) {
- ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
- if (!ddb_i2c_cmd(i2c, addr, 3)) {
- ddbcpyfrom(msg[0].buf,
- I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
- return num;
- }
- }
- return -EIO;
-}
-
-
-static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_EMUL;
-}
-
-struct i2c_algorithm ddb_i2c_algo = {
- .master_xfer = ddb_i2c_master_xfer,
- .functionality = ddb_i2c_functionality,
-};
-
-static void ddb_i2c_release(struct ddb *dev)
-{
- int i;
- struct ddb_i2c *i2c;
- struct i2c_adapter *adap;
-
- for (i = 0; i < dev->info->port_num; i++) {
- i2c = &dev->i2c[i];
- adap = &i2c->adap;
- i2c_del_adapter(adap);
- }
-}
-
-static int ddb_i2c_init(struct ddb *dev)
-{
- int i, j, stat = 0;
- struct ddb_i2c *i2c;
- struct i2c_adapter *adap;
-
- for (i = 0; i < dev->info->port_num; i++) {
- i2c = &dev->i2c[i];
- i2c->dev = dev;
- i2c->nr = i;
- i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
- i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
- i2c->regs = 0x80 + i * 0x20;
- ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
- ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
- i2c->regs + I2C_TASKADDRESS);
- init_waitqueue_head(&i2c->wq);
-
- adap = &i2c->adap;
- i2c_set_adapdata(adap, i2c);
-#ifdef I2C_ADAP_CLASS_TV_DIGITAL
- adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
-#else
-#ifdef I2C_CLASS_TV_ANALOG
- adap->class = I2C_CLASS_TV_ANALOG;
-#endif
-#endif
- strcpy(adap->name, "ddbridge");
- adap->algo = &ddb_i2c_algo;
- adap->algo_data = (void *)i2c;
- adap->dev.parent = &dev->pdev->dev;
- stat = i2c_add_adapter(adap);
- if (stat)
- break;
- }
- if (stat)
- for (j = 0; j < i; j++) {
- i2c = &dev->i2c[j];
- adap = &i2c->adap;
- i2c_del_adapter(adap);
- }
- return stat;
-}
-
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-#if 0
-static void set_table(struct ddb *dev, u32 off,
- dma_addr_t *pbuf, u32 num)
-{
- u32 i, base;
- u64 mem;
-
- base = DMA_BASE_ADDRESS_TABLE + off;
- for (i = 0; i < num; i++) {
- mem = pbuf[i];
- ddbwritel(mem & 0xffffffff, base + i * 8);
- ddbwritel(mem >> 32, base + i * 8 + 4);
- }
-}
-#endif
-
-static void ddb_address_table(struct ddb *dev)
-{
- u32 i, j, base;
- u64 mem;
- dma_addr_t *pbuf;
-
- for (i = 0; i < dev->info->port_num * 2; i++) {
- base = DMA_BASE_ADDRESS_TABLE + i * 0x100;
- pbuf = dev->input[i].pbuf;
- for (j = 0; j < dev->input[i].dma_buf_num; j++) {
- mem = pbuf[j];
- ddbwritel(mem & 0xffffffff, base + j * 8);
- ddbwritel(mem >> 32, base + j * 8 + 4);
- }
- }
- for (i = 0; i < dev->info->port_num; i++) {
- base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100;
- pbuf = dev->output[i].pbuf;
- for (j = 0; j < dev->output[i].dma_buf_num; j++) {
- mem = pbuf[j];
- ddbwritel(mem & 0xffffffff, base + j * 8);
- ddbwritel(mem >> 32, base + j * 8 + 4);
- }
- }
-}
-
-static void io_free(struct pci_dev *pdev, u8 **vbuf,
- dma_addr_t *pbuf, u32 size, int num)
-{
- int i;
-
- for (i = 0; i < num; i++) {
- if (vbuf[i]) {
- pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
- vbuf[i] = 0;
- }
- }
-}
-
-static int io_alloc(struct pci_dev *pdev, u8 **vbuf,
- dma_addr_t *pbuf, u32 size, int num)
-{
- int i;
-
- for (i = 0; i < num; i++) {
- vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]);
- if (!vbuf[i])
- return -ENOMEM;
- }
- return 0;
-}
-
-static int ddb_buffers_alloc(struct ddb *dev)
-{
- int i;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- switch (port->class) {
- case DDB_PORT_TUNER:
- if (io_alloc(dev->pdev, port->input[0]->vbuf,
- port->input[0]->pbuf,
- port->input[0]->dma_buf_size,
- port->input[0]->dma_buf_num) < 0)
- return -1;
- if (io_alloc(dev->pdev, port->input[1]->vbuf,
- port->input[1]->pbuf,
- port->input[1]->dma_buf_size,
- port->input[1]->dma_buf_num) < 0)
- return -1;
- break;
- case DDB_PORT_CI:
- if (io_alloc(dev->pdev, port->input[0]->vbuf,
- port->input[0]->pbuf,
- port->input[0]->dma_buf_size,
- port->input[0]->dma_buf_num) < 0)
- return -1;
- if (io_alloc(dev->pdev, port->output->vbuf,
- port->output->pbuf,
- port->output->dma_buf_size,
- port->output->dma_buf_num) < 0)
- return -1;
- break;
- default:
- break;
- }
- }
- ddb_address_table(dev);
- return 0;
-}
-
-static void ddb_buffers_free(struct ddb *dev)
-{
- int i;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- io_free(dev->pdev, port->input[0]->vbuf,
- port->input[0]->pbuf,
- port->input[0]->dma_buf_size,
- port->input[0]->dma_buf_num);
- io_free(dev->pdev, port->input[1]->vbuf,
- port->input[1]->pbuf,
- port->input[1]->dma_buf_size,
- port->input[1]->dma_buf_num);
- io_free(dev->pdev, port->output->vbuf,
- port->output->pbuf,
- port->output->dma_buf_size,
- port->output->dma_buf_num);
- }
-}
-
-static void ddb_input_start(struct ddb_input *input)
-{
- struct ddb *dev = input->port->dev;
-
- spin_lock_irq(&input->lock);
- input->cbuf = 0;
- input->coff = 0;
-
- /* reset */
- ddbwritel(0, TS_INPUT_CONTROL(input->nr));
- ddbwritel(2, TS_INPUT_CONTROL(input->nr));
- ddbwritel(0, TS_INPUT_CONTROL(input->nr));
-
- ddbwritel((1 << 16) |
- (input->dma_buf_num << 11) |
- (input->dma_buf_size >> 7),
- DMA_BUFFER_SIZE(input->nr));
- ddbwritel(0, DMA_BUFFER_ACK(input->nr));
-
- ddbwritel(1, DMA_BASE_WRITE);
- ddbwritel(3, DMA_BUFFER_CONTROL(input->nr));
- ddbwritel(9, TS_INPUT_CONTROL(input->nr));
- input->running = 1;
- spin_unlock_irq(&input->lock);
-}
-
-static void ddb_input_stop(struct ddb_input *input)
-{
- struct ddb *dev = input->port->dev;
-
- spin_lock_irq(&input->lock);
- ddbwritel(0, TS_INPUT_CONTROL(input->nr));
- ddbwritel(0, DMA_BUFFER_CONTROL(input->nr));
- input->running = 0;
- spin_unlock_irq(&input->lock);
-}
-
-static void ddb_output_start(struct ddb_output *output)
-{
- struct ddb *dev = output->port->dev;
-
- spin_lock_irq(&output->lock);
- output->cbuf = 0;
- output->coff = 0;
- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
- ddbwritel(2, TS_OUTPUT_CONTROL(output->nr));
- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
- ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr));
- ddbwritel((1 << 16) |
- (output->dma_buf_num << 11) |
- (output->dma_buf_size >> 7),
- DMA_BUFFER_SIZE(output->nr + 8));
- ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8));
-
- ddbwritel(1, DMA_BASE_READ);
- ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8));
- /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */
- ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr));
- output->running = 1;
- spin_unlock_irq(&output->lock);
-}
-
-static void ddb_output_stop(struct ddb_output *output)
-{
- struct ddb *dev = output->port->dev;
-
- spin_lock_irq(&output->lock);
- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
- ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8));
- output->running = 0;
- spin_unlock_irq(&output->lock);
-}
-
-static u32 ddb_output_free(struct ddb_output *output)
-{
- u32 idx, off, stat = output->stat;
- s32 diff;
-
- idx = (stat >> 11) & 0x1f;
- off = (stat & 0x7ff) << 7;
-
- if (output->cbuf != idx) {
- if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
- (output->dma_buf_size - output->coff <= 188))
- return 0;
- return 188;
- }
- diff = off - output->coff;
- if (diff <= 0 || diff > 188)
- return 188;
- return 0;
-}
-
-static ssize_t ddb_output_write(struct ddb_output *output,
- const u8 *buf, size_t count)
-{
- struct ddb *dev = output->port->dev;
- u32 idx, off, stat = output->stat;
- u32 left = count, len;
-
- idx = (stat >> 11) & 0x1f;
- off = (stat & 0x7ff) << 7;
-
- while (left) {
- len = output->dma_buf_size - output->coff;
- if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
- (off == 0)) {
- if (len <= 188)
- break;
- len -= 188;
- }
- if (output->cbuf == idx) {
- if (off > output->coff) {
-#if 1
- len = off - output->coff;
- len -= (len % 188);
- if (len <= 188)
-
-#endif
- break;
- len -= 188;
- }
- }
- if (len > left)
- len = left;
- if (copy_from_user(output->vbuf[output->cbuf] + output->coff,
- buf, len))
- return -EIO;
- left -= len;
- buf += len;
- output->coff += len;
- if (output->coff == output->dma_buf_size) {
- output->coff = 0;
- output->cbuf = ((output->cbuf + 1) % output->dma_buf_num);
- }
- ddbwritel((output->cbuf << 11) | (output->coff >> 7),
- DMA_BUFFER_ACK(output->nr + 8));
- }
- return count - left;
-}
-
-static u32 ddb_input_avail(struct ddb_input *input)
-{
- struct ddb *dev = input->port->dev;
- u32 idx, off, stat = input->stat;
- u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr));
-
- idx = (stat >> 11) & 0x1f;
- off = (stat & 0x7ff) << 7;
-
- if (ctrl & 4) {
- printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
- ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
- return 0;
- }
- if (input->cbuf != idx)
- return 188;
- return 0;
-}
-
-static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
-{
- struct ddb *dev = input->port->dev;
- u32 left = count;
- u32 idx, free, stat = input->stat;
- int ret;
-
- idx = (stat >> 11) & 0x1f;
-
- while (left) {
- if (input->cbuf == idx)
- return count - left;
- free = input->dma_buf_size - input->coff;
- if (free > left)
- free = left;
- ret = copy_to_user(buf, input->vbuf[input->cbuf] +
- input->coff, free);
- if (ret)
- return -EFAULT;
- input->coff += free;
- if (input->coff == input->dma_buf_size) {
- input->coff = 0;
- input->cbuf = (input->cbuf+1) % input->dma_buf_num;
- }
- left -= free;
- ddbwritel((input->cbuf << 11) | (input->coff >> 7),
- DMA_BUFFER_ACK(input->nr));
- }
- return count;
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-#if 0
-static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe)
-{
- int i;
-
- for (i = 0; i < dev->info->port_num * 2; i++) {
- if (dev->input[i].fe == fe)
- return &dev->input[i];
- }
- return NULL;
-}
-#endif
-
-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct ddb_input *input = fe->sec_priv;
- struct ddb_port *port = input->port;
- int status;
-
- if (enable) {
- mutex_lock(&port->i2c_gate_lock);
- status = input->gate_ctrl(fe, 1);
- } else {
- status = input->gate_ctrl(fe, 0);
- mutex_unlock(&port->i2c_gate_lock);
- }
- return status;
-}
-
-static int demod_attach_drxk(struct ddb_input *input)
-{
- struct i2c_adapter *i2c = &input->port->i2c->adap;
- struct dvb_frontend *fe;
- struct drxk_config config;
-
- memset(&config, 0, sizeof(config));
- config.microcode_name = "drxk_a3.mc";
- config.qam_demod_parameter_count = 4;
- config.adr = 0x29 + (input->nr & 1);
-
- fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
- if (!input->fe) {
- printk(KERN_ERR "No DRXK found!\n");
- return -ENODEV;
- }
- fe->sec_priv = input;
- input->gate_ctrl = fe->ops.i2c_gate_ctrl;
- fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
- return 0;
-}
-
-static int tuner_attach_tda18271(struct ddb_input *input)
-{
- struct i2c_adapter *i2c = &input->port->i2c->adap;
- struct dvb_frontend *fe;
-
- if (input->fe->ops.i2c_gate_ctrl)
- input->fe->ops.i2c_gate_ctrl(input->fe, 1);
- fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
- if (!fe) {
- printk(KERN_ERR "No TDA18271 found!\n");
- return -ENODEV;
- }
- if (input->fe->ops.i2c_gate_ctrl)
- input->fe->ops.i2c_gate_ctrl(input->fe, 0);
- return 0;
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-static struct stv090x_config stv0900 = {
- .device = STV0900,
- .demod_mode = STV090x_DUAL,
- .clk_mode = STV090x_CLK_EXT,
-
- .xtal = 27000000,
- .address = 0x69,
-
- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
-
- .repeater_level = STV090x_RPTLEVEL_16,
-
- .adc1_range = STV090x_ADC_1Vpp,
- .adc2_range = STV090x_ADC_1Vpp,
-
- .diseqc_envelope_mode = true,
-};
-
-static struct stv090x_config stv0900_aa = {
- .device = STV0900,
- .demod_mode = STV090x_DUAL,
- .clk_mode = STV090x_CLK_EXT,
-
- .xtal = 27000000,
- .address = 0x68,
-
- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
-
- .repeater_level = STV090x_RPTLEVEL_16,
-
- .adc1_range = STV090x_ADC_1Vpp,
- .adc2_range = STV090x_ADC_1Vpp,
-
- .diseqc_envelope_mode = true,
-};
-
-static struct stv6110x_config stv6110a = {
- .addr = 0x60,
- .refclk = 27000000,
- .clk_div = 1,
-};
-
-static struct stv6110x_config stv6110b = {
- .addr = 0x63,
- .refclk = 27000000,
- .clk_div = 1,
-};
-
-static int demod_attach_stv0900(struct ddb_input *input, int type)
-{
- struct i2c_adapter *i2c = &input->port->i2c->adap;
- struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
-
- input->fe = dvb_attach(stv090x_attach, feconf, i2c,
- (input->nr & 1) ? STV090x_DEMODULATOR_1
- : STV090x_DEMODULATOR_0);
- if (!input->fe) {
- printk(KERN_ERR "No STV0900 found!\n");
- return -ENODEV;
- }
- if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
- 0, (input->nr & 1) ?
- (0x09 - type) : (0x0b - type))) {
- printk(KERN_ERR "No LNBH24 found!\n");
- return -ENODEV;
- }
- return 0;
-}
-
-static int tuner_attach_stv6110(struct ddb_input *input, int type)
-{
- struct i2c_adapter *i2c = &input->port->i2c->adap;
- struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
- struct stv6110x_config *tunerconf = (input->nr & 1) ?
- &stv6110b : &stv6110a;
- struct stv6110x_devctl *ctl;
-
- ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
- if (!ctl) {
- printk(KERN_ERR "No STV6110X found!\n");
- return -ENODEV;
- }
- printk(KERN_INFO "attach tuner input %d adr %02x\n",
- input->nr, tunerconf->addr);
-
- feconf->tuner_init = ctl->tuner_init;
- feconf->tuner_sleep = ctl->tuner_sleep;
- feconf->tuner_set_mode = ctl->tuner_set_mode;
- feconf->tuner_set_frequency = ctl->tuner_set_frequency;
- feconf->tuner_get_frequency = ctl->tuner_get_frequency;
- feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
- feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
- feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
- feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
- feconf->tuner_set_refclk = ctl->tuner_set_refclk;
- feconf->tuner_get_status = ctl->tuner_get_status;
-
- return 0;
-}
-
-static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
- int (*start_feed)(struct dvb_demux_feed *),
- int (*stop_feed)(struct dvb_demux_feed *),
- void *priv)
-{
- dvbdemux->priv = priv;
-
- dvbdemux->filternum = 256;
- dvbdemux->feednum = 256;
- dvbdemux->start_feed = start_feed;
- dvbdemux->stop_feed = stop_feed;
- dvbdemux->write_to_decoder = NULL;
- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
- DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING);
- return dvb_dmx_init(dvbdemux);
-}
-
-static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
- struct dvb_demux *dvbdemux,
- struct dmx_frontend *hw_frontend,
- struct dmx_frontend *mem_frontend,
- struct dvb_adapter *dvb_adapter)
-{
- int ret;
-
- dmxdev->filternum = 256;
- dmxdev->demux = &dvbdemux->dmx;
- dmxdev->capabilities = 0;
- ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
- if (ret < 0)
- return ret;
-
- hw_frontend->source = DMX_FRONTEND_0;
- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
- mem_frontend->source = DMX_MEMORY_FE;
- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
- return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
-}
-
-static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- struct ddb_input *input = dvbdmx->priv;
-
- if (!input->users)
- ddb_input_start(input);
-
- return ++input->users;
-}
-
-static int stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- struct ddb_input *input = dvbdmx->priv;
-
- if (--input->users)
- return input->users;
-
- ddb_input_stop(input);
- return 0;
-}
-
-
-static void dvb_input_detach(struct ddb_input *input)
-{
- struct dvb_adapter *adap = &input->adap;
- struct dvb_demux *dvbdemux = &input->demux;
-
- switch (input->attached) {
- case 5:
- if (input->fe2)
- dvb_unregister_frontend(input->fe2);
- if (input->fe) {
- dvb_unregister_frontend(input->fe);
- dvb_frontend_detach(input->fe);
- input->fe = NULL;
- }
- case 4:
- dvb_net_release(&input->dvbnet);
-
- case 3:
- dvbdemux->dmx.close(&dvbdemux->dmx);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
- &input->hw_frontend);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
- &input->mem_frontend);
- dvb_dmxdev_release(&input->dmxdev);
-
- case 2:
- dvb_dmx_release(&input->demux);
-
- case 1:
- dvb_unregister_adapter(adap);
- }
- input->attached = 0;
-}
-
-static int dvb_input_attach(struct ddb_input *input)
-{
- int ret;
- struct ddb_port *port = input->port;
- struct dvb_adapter *adap = &input->adap;
- struct dvb_demux *dvbdemux = &input->demux;
-
- ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
- &input->port->dev->pdev->dev,
- adapter_nr);
- if (ret < 0) {
- printk(KERN_ERR "ddbridge: Could not register adapter."
- "Check if you enabled enough adapters in dvb-core!\n");
- return ret;
- }
- input->attached = 1;
-
- ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
- start_feed,
- stop_feed, input);
- if (ret < 0)
- return ret;
- input->attached = 2;
-
- ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux,
- &input->hw_frontend,
- &input->mem_frontend, adap);
- if (ret < 0)
- return ret;
- input->attached = 3;
-
- ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux);
- if (ret < 0)
- return ret;
- input->attached = 4;
-
- input->fe = 0;
- switch (port->type) {
- case DDB_TUNER_DVBS_ST:
- if (demod_attach_stv0900(input, 0) < 0)
- return -ENODEV;
- if (tuner_attach_stv6110(input, 0) < 0)
- return -ENODEV;
- if (input->fe) {
- if (dvb_register_frontend(adap, input->fe) < 0)
- return -ENODEV;
- }
- break;
- case DDB_TUNER_DVBS_ST_AA:
- if (demod_attach_stv0900(input, 1) < 0)
- return -ENODEV;
- if (tuner_attach_stv6110(input, 1) < 0)
- return -ENODEV;
- if (input->fe) {
- if (dvb_register_frontend(adap, input->fe) < 0)
- return -ENODEV;
- }
- break;
- case DDB_TUNER_DVBCT_TR:
- if (demod_attach_drxk(input) < 0)
- return -ENODEV;
- if (tuner_attach_tda18271(input) < 0)
- return -ENODEV;
- if (input->fe) {
- if (dvb_register_frontend(adap, input->fe) < 0)
- return -ENODEV;
- }
- if (input->fe2) {
- if (dvb_register_frontend(adap, input->fe2) < 0)
- return -ENODEV;
- input->fe2->tuner_priv = input->fe->tuner_priv;
- memcpy(&input->fe2->ops.tuner_ops,
- &input->fe->ops.tuner_ops,
- sizeof(struct dvb_tuner_ops));
- }
- break;
- }
- input->attached = 5;
- return 0;
-}
-
-/****************************************************************************/
-/****************************************************************************/
-
-static ssize_t ts_write(struct file *file, const char *buf,
- size_t count, loff_t *ppos)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct ddb_output *output = dvbdev->priv;
- size_t left = count;
- int stat;
-
- while (left) {
- if (ddb_output_free(output) < 188) {
- if (file->f_flags & O_NONBLOCK)
- break;
- if (wait_event_interruptible(
- output->wq, ddb_output_free(output) >= 188) < 0)
- break;
- }
- stat = ddb_output_write(output, buf, left);
- if (stat < 0)
- break;
- buf += stat;
- left -= stat;
- }
- return (left == count) ? -EAGAIN : (count - left);
-}
-
-static ssize_t ts_read(struct file *file, char *buf,
- size_t count, loff_t *ppos)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct ddb_output *output = dvbdev->priv;
- struct ddb_input *input = output->port->input[0];
- int left, read;
-
- count -= count % 188;
- left = count;
- while (left) {
- if (ddb_input_avail(input) < 188) {
- if (file->f_flags & O_NONBLOCK)
- break;
- if (wait_event_interruptible(
- input->wq, ddb_input_avail(input) >= 188) < 0)
- break;
- }
- read = ddb_input_read(input, buf, left);
- if (read < 0)
- return read;
- left -= read;
- buf += read;
- }
- return (left == count) ? -EAGAIN : (count - left);
-}
-
-static unsigned int ts_poll(struct file *file, poll_table *wait)
-{
- /*
- struct dvb_device *dvbdev = file->private_data;
- struct ddb_output *output = dvbdev->priv;
- struct ddb_input *input = output->port->input[0];
- */
- unsigned int mask = 0;
-
-#if 0
- if (data_avail_to_read)
- mask |= POLLIN | POLLRDNORM;
- if (data_avail_to_write)
- mask |= POLLOUT | POLLWRNORM;
-
- poll_wait(file, &read_queue, wait);
- poll_wait(file, &write_queue, wait);
-#endif
- return mask;
-}
-
-static const struct file_operations ci_fops = {
- .owner = THIS_MODULE,
- .read = ts_read,
- .write = ts_write,
- .open = dvb_generic_open,
- .release = dvb_generic_release,
- .poll = ts_poll,
- .mmap = 0,
-};
-
-static struct dvb_device dvbdev_ci = {
- .priv = 0,
- .readers = -1,
- .writers = -1,
- .users = -1,
- .fops = &ci_fops,
-};
-
-/****************************************************************************/
-/****************************************************************************/
-/****************************************************************************/
-
-static void input_tasklet(unsigned long data)
-{
- struct ddb_input *input = (struct ddb_input *) data;
- struct ddb *dev = input->port->dev;
-
- spin_lock(&input->lock);
- if (!input->running) {
- spin_unlock(&input->lock);
- return;
- }
- input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
-
- if (input->port->class == DDB_PORT_TUNER) {
- if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
- printk(KERN_ERR "Overflow input %d\n", input->nr);
- while (input->cbuf != ((input->stat >> 11) & 0x1f)
- || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
- dvb_dmx_swfilter_packets(&input->demux,
- input->vbuf[input->cbuf],
- input->dma_buf_size / 188);
-
- input->cbuf = (input->cbuf + 1) % input->dma_buf_num;
- ddbwritel((input->cbuf << 11),
- DMA_BUFFER_ACK(input->nr));
- input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
- }
- }
- if (input->port->class == DDB_PORT_CI)
- wake_up(&input->wq);
- spin_unlock(&input->lock);
-}
-
-static void output_tasklet(unsigned long data)
-{
- struct ddb_output *output = (struct ddb_output *) data;
- struct ddb *dev = output->port->dev;
-
- spin_lock(&output->lock);
- if (!output->running) {
- spin_unlock(&output->lock);
- return;
- }
- output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8));
- wake_up(&output->wq);
- spin_unlock(&output->lock);
-}
-
-
-struct cxd2099_cfg cxd_cfg = {
- .bitrate = 62000,
- .adr = 0x40,
- .polarity = 1,
- .clock_mode = 1,
-};
-
-static int ddb_ci_attach(struct ddb_port *port)
-{
- int ret;
-
- ret = dvb_register_adapter(&port->output->adap,
- "DDBridge",
- THIS_MODULE,
- &port->dev->pdev->dev,
- adapter_nr);
- if (ret < 0)
- return ret;
- port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
- if (!port->en) {
- dvb_unregister_adapter(&port->output->adap);
- return -ENODEV;
- }
- ddb_input_start(port->input[0]);
- ddb_output_start(port->output);
- dvb_ca_en50221_init(&port->output->adap,
- port->en, 0, 1);
- ret = dvb_register_device(&port->output->adap, &port->output->dev,
- &dvbdev_ci, (void *) port->output,
- DVB_DEVICE_SEC);
- return ret;
-}
-
-static int ddb_port_attach(struct ddb_port *port)
-{
- int ret = 0;
-
- switch (port->class) {
- case DDB_PORT_TUNER:
- ret = dvb_input_attach(port->input[0]);
- if (ret < 0)
- break;
- ret = dvb_input_attach(port->input[1]);
- break;
- case DDB_PORT_CI:
- ret = ddb_ci_attach(port);
- break;
- default:
- break;
- }
- if (ret < 0)
- printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
- return ret;
-}
-
-static int ddb_ports_attach(struct ddb *dev)
-{
- int i, ret = 0;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- ret = ddb_port_attach(port);
- if (ret < 0)
- break;
- }
- return ret;
-}
-
-static void ddb_ports_detach(struct ddb *dev)
-{
- int i;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- switch (port->class) {
- case DDB_PORT_TUNER:
- dvb_input_detach(port->input[0]);
- dvb_input_detach(port->input[1]);
- break;
- case DDB_PORT_CI:
- if (port->output->dev)
- dvb_unregister_device(port->output->dev);
- if (port->en) {
- ddb_input_stop(port->input[0]);
- ddb_output_stop(port->output);
- dvb_ca_en50221_release(port->en);
- kfree(port->en);
- port->en = 0;
- dvb_unregister_adapter(&port->output->adap);
- }
- break;
- }
- }
-}
-
-/****************************************************************************/
-/****************************************************************************/
-
-static int port_has_ci(struct ddb_port *port)
-{
- u8 val;
- return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1;
-}
-
-static int port_has_stv0900(struct ddb_port *port)
-{
- u8 val;
- if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0)
- return 0;
- return 1;
-}
-
-static int port_has_stv0900_aa(struct ddb_port *port)
-{
- u8 val;
- if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0)
- return 0;
- return 1;
-}
-
-static int port_has_drxks(struct ddb_port *port)
-{
- u8 val;
- if (i2c_read(&port->i2c->adap, 0x29, &val) < 0)
- return 0;
- if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0)
- return 0;
- return 1;
-}
-
-static void ddb_port_probe(struct ddb_port *port)
-{
- struct ddb *dev = port->dev;
- char *modname = "NO MODULE";
-
- port->class = DDB_PORT_NONE;
-
- if (port_has_ci(port)) {
- modname = "CI";
- port->class = DDB_PORT_CI;
- ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
- } else if (port_has_stv0900(port)) {
- modname = "DUAL DVB-S2";
- port->class = DDB_PORT_TUNER;
- port->type = DDB_TUNER_DVBS_ST;
- ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
- } else if (port_has_stv0900_aa(port)) {
- modname = "DUAL DVB-S2";
- port->class = DDB_PORT_TUNER;
- port->type = DDB_TUNER_DVBS_ST_AA;
- ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
- } else if (port_has_drxks(port)) {
- modname = "DUAL DVB-C/T";
- port->class = DDB_PORT_TUNER;
- port->type = DDB_TUNER_DVBCT_TR;
- ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
- }
- printk(KERN_INFO "Port %d (TAB %d): %s\n",
- port->nr, port->nr+1, modname);
-}
-
-static void ddb_input_init(struct ddb_port *port, int nr)
-{
- struct ddb *dev = port->dev;
- struct ddb_input *input = &dev->input[nr];
-
- input->nr = nr;
- input->port = port;
- input->dma_buf_num = INPUT_DMA_BUFS;
- input->dma_buf_size = INPUT_DMA_SIZE;
- ddbwritel(0, TS_INPUT_CONTROL(nr));
- ddbwritel(2, TS_INPUT_CONTROL(nr));
- ddbwritel(0, TS_INPUT_CONTROL(nr));
- ddbwritel(0, DMA_BUFFER_ACK(nr));
- tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input);
- spin_lock_init(&input->lock);
- init_waitqueue_head(&input->wq);
-}
-
-static void ddb_output_init(struct ddb_port *port, int nr)
-{
- struct ddb *dev = port->dev;
- struct ddb_output *output = &dev->output[nr];
- output->nr = nr;
- output->port = port;
- output->dma_buf_num = OUTPUT_DMA_BUFS;
- output->dma_buf_size = OUTPUT_DMA_SIZE;
-
- ddbwritel(0, TS_OUTPUT_CONTROL(nr));
- ddbwritel(2, TS_OUTPUT_CONTROL(nr));
- ddbwritel(0, TS_OUTPUT_CONTROL(nr));
- tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output);
- init_waitqueue_head(&output->wq);
-}
-
-static void ddb_ports_init(struct ddb *dev)
-{
- int i;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- port->dev = dev;
- port->nr = i;
- port->i2c = &dev->i2c[i];
- port->input[0] = &dev->input[2 * i];
- port->input[1] = &dev->input[2 * i + 1];
- port->output = &dev->output[i];
-
- mutex_init(&port->i2c_gate_lock);
- ddb_port_probe(port);
- ddb_input_init(port, 2 * i);
- ddb_input_init(port, 2 * i + 1);
- ddb_output_init(port, i);
- }
-}
-
-static void ddb_ports_release(struct ddb *dev)
-{
- int i;
- struct ddb_port *port;
-
- for (i = 0; i < dev->info->port_num; i++) {
- port = &dev->port[i];
- port->dev = dev;
- tasklet_kill(&port->input[0]->tasklet);
- tasklet_kill(&port->input[1]->tasklet);
- tasklet_kill(&port->output->tasklet);
- }
-}
-
-/****************************************************************************/
-/****************************************************************************/
-/****************************************************************************/
-
-static void irq_handle_i2c(struct ddb *dev, int n)
-{
- struct ddb_i2c *i2c = &dev->i2c[n];
-
- i2c->done = 1;
- wake_up(&i2c->wq);
-}
-
-static irqreturn_t irq_handler(int irq, void *dev_id)
-{
- struct ddb *dev = (struct ddb *) dev_id;
- u32 s = ddbreadl(INTERRUPT_STATUS);
-
- if (!s)
- return IRQ_NONE;
-
- do {
- ddbwritel(s, INTERRUPT_ACK);
-
- if (s & 0x00000001)
- irq_handle_i2c(dev, 0);
- if (s & 0x00000002)
- irq_handle_i2c(dev, 1);
- if (s & 0x00000004)
- irq_handle_i2c(dev, 2);
- if (s & 0x00000008)
- irq_handle_i2c(dev, 3);
-
- if (s & 0x00000100)
- tasklet_schedule(&dev->input[0].tasklet);
- if (s & 0x00000200)
- tasklet_schedule(&dev->input[1].tasklet);
- if (s & 0x00000400)
- tasklet_schedule(&dev->input[2].tasklet);
- if (s & 0x00000800)
- tasklet_schedule(&dev->input[3].tasklet);
- if (s & 0x00001000)
- tasklet_schedule(&dev->input[4].tasklet);
- if (s & 0x00002000)
- tasklet_schedule(&dev->input[5].tasklet);
- if (s & 0x00004000)
- tasklet_schedule(&dev->input[6].tasklet);
- if (s & 0x00008000)
- tasklet_schedule(&dev->input[7].tasklet);
-
- if (s & 0x00010000)
- tasklet_schedule(&dev->output[0].tasklet);
- if (s & 0x00020000)
- tasklet_schedule(&dev->output[1].tasklet);
- if (s & 0x00040000)
- tasklet_schedule(&dev->output[2].tasklet);
- if (s & 0x00080000)
- tasklet_schedule(&dev->output[3].tasklet);
-
- /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */
- } while ((s = ddbreadl(INTERRUPT_STATUS)));
-
- return IRQ_HANDLED;
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
-{
- u32 data, shift;
-
- if (wlen > 4)
- ddbwritel(1, SPI_CONTROL);
- while (wlen > 4) {
- /* FIXME: check for big-endian */
- data = swab32(*(u32 *)wbuf);
- wbuf += 4;
- wlen -= 4;
- ddbwritel(data, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
- ;
- }
-
- if (rlen)
- ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
- else
- ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
-
- data = 0;
- shift = ((4 - wlen) * 8);
- while (wlen) {
- data <<= 8;
- data |= *wbuf;
- wlen--;
- wbuf++;
- }
- if (shift)
- data <<= shift;
- ddbwritel(data, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
- ;
-
- if (!rlen) {
- ddbwritel(0, SPI_CONTROL);
- return 0;
- }
- if (rlen > 4)
- ddbwritel(1, SPI_CONTROL);
-
- while (rlen > 4) {
- ddbwritel(0xffffffff, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
- ;
- data = ddbreadl(SPI_DATA);
- *(u32 *) rbuf = swab32(data);
- rbuf += 4;
- rlen -= 4;
- }
- ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
- ddbwritel(0xffffffff, SPI_DATA);
- while (ddbreadl(SPI_CONTROL) & 0x0004)
- ;
-
- data = ddbreadl(SPI_DATA);
- ddbwritel(0, SPI_CONTROL);
-
- if (rlen < 4)
- data <<= ((4 - rlen) * 8);
-
- while (rlen > 0) {
- *rbuf = ((data >> 24) & 0xff);
- data <<= 8;
- rbuf++;
- rlen--;
- }
- return 0;
-}
-
-#define DDB_MAGIC 'd'
-
-struct ddb_flashio {
- __u8 *write_buf;
- __u32 write_len;
- __u8 *read_buf;
- __u32 read_len;
-};
-
-#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
-
-#define DDB_NAME "ddbridge"
-
-static u32 ddb_num;
-static struct ddb *ddbs[32];
-static struct class *ddb_class;
-static int ddb_major;
-
-static int ddb_open(struct inode *inode, struct file *file)
-{
- struct ddb *dev = ddbs[iminor(inode)];
-
- file->private_data = dev;
- return 0;
-}
-
-static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct ddb *dev = file->private_data;
- void *parg = (void *)arg;
- int res;
-
- switch (cmd) {
- case IOCTL_DDB_FLASHIO:
- {
- struct ddb_flashio fio;
- u8 *rbuf, *wbuf;
-
- if (copy_from_user(&fio, parg, sizeof(fio)))
- return -EFAULT;
-
- if (fio.write_len > 1028 || fio.read_len > 1028)
- return -EINVAL;
- if (fio.write_len + fio.read_len > 1028)
- return -EINVAL;
-
- wbuf = &dev->iobuf[0];
- rbuf = wbuf + fio.write_len;
-
- if (copy_from_user(wbuf, fio.write_buf, fio.write_len))
- return -EFAULT;
- res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len);
- if (res)
- return res;
- if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
- return -EFAULT;
- break;
- }
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static const struct file_operations ddb_fops = {
- .unlocked_ioctl = ddb_ioctl,
- .open = ddb_open,
-};
-
-static char *ddb_devnode(struct device *device, umode_t *mode)
-{
- struct ddb *dev = dev_get_drvdata(device);
-
- return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr);
-}
-
-static int ddb_class_create(void)
-{
- ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
- if (ddb_major < 0)
- return ddb_major;
-
- ddb_class = class_create(THIS_MODULE, DDB_NAME);
- if (IS_ERR(ddb_class)) {
- unregister_chrdev(ddb_major, DDB_NAME);
- return -1;
- }
- ddb_class->devnode = ddb_devnode;
- return 0;
-}
-
-static void ddb_class_destroy(void)
-{
- class_destroy(ddb_class);
- unregister_chrdev(ddb_major, DDB_NAME);
-}
-
-static int ddb_device_create(struct ddb *dev)
-{
- dev->nr = ddb_num++;
- dev->ddb_dev = device_create(ddb_class, NULL,
- MKDEV(ddb_major, dev->nr),
- dev, "ddbridge%d", dev->nr);
- ddbs[dev->nr] = dev;
- if (IS_ERR(dev->ddb_dev))
- return -1;
- return 0;
-}
-
-static void ddb_device_destroy(struct ddb *dev)
-{
- ddb_num--;
- if (IS_ERR(dev->ddb_dev))
- return;
- device_destroy(ddb_class, MKDEV(ddb_major, 0));
-}
-
-
-/****************************************************************************/
-/****************************************************************************/
-/****************************************************************************/
-
-static void ddb_unmap(struct ddb *dev)
-{
- if (dev->regs)
- iounmap(dev->regs);
- vfree(dev);
-}
-
-
-static void __devexit ddb_remove(struct pci_dev *pdev)
-{
- struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
-
- ddb_ports_detach(dev);
- ddb_i2c_release(dev);
-
- ddbwritel(0, INTERRUPT_ENABLE);
- free_irq(dev->pdev->irq, dev);
-#ifdef CONFIG_PCI_MSI
- if (dev->msi)
- pci_disable_msi(dev->pdev);
-#endif
- ddb_ports_release(dev);
- ddb_buffers_free(dev);
- ddb_device_destroy(dev);
-
- ddb_unmap(dev);
- pci_set_drvdata(pdev, 0);
- pci_disable_device(pdev);
-}
-
-
-static int __devinit ddb_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct ddb *dev;
- int stat = 0;
- int irq_flag = IRQF_SHARED;
-
- if (pci_enable_device(pdev) < 0)
- return -ENODEV;
-
- dev = vmalloc(sizeof(struct ddb));
- if (dev == NULL)
- return -ENOMEM;
- memset(dev, 0, sizeof(struct ddb));
-
- dev->pdev = pdev;
- pci_set_drvdata(pdev, dev);
- dev->info = (struct ddb_info *) id->driver_data;
- printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
-
- dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
- pci_resource_len(dev->pdev, 0));
- if (!dev->regs) {
- stat = -ENOMEM;
- goto fail;
- }
- printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
-
-#ifdef CONFIG_PCI_MSI
- if (pci_msi_enabled())
- stat = pci_enable_msi(dev->pdev);
- if (stat) {
- printk(KERN_INFO ": MSI not available.\n");
- } else {
- irq_flag = 0;
- dev->msi = 1;
- }
-#endif
- stat = request_irq(dev->pdev->irq, irq_handler,
- irq_flag, "DDBridge", (void *) dev);
- if (stat < 0)
- goto fail1;
- ddbwritel(0, DMA_BASE_WRITE);
- ddbwritel(0, DMA_BASE_READ);
- ddbwritel(0xffffffff, INTERRUPT_ACK);
- ddbwritel(0xfff0f, INTERRUPT_ENABLE);
- ddbwritel(0, MSI1_ENABLE);
-
- if (ddb_i2c_init(dev) < 0)
- goto fail1;
- ddb_ports_init(dev);
- if (ddb_buffers_alloc(dev) < 0) {
- printk(KERN_INFO ": Could not allocate buffer memory\n");
- goto fail2;
- }
- if (ddb_ports_attach(dev) < 0)
- goto fail3;
- ddb_device_create(dev);
- return 0;
-
-fail3:
- ddb_ports_detach(dev);
- printk(KERN_ERR "fail3\n");
- ddb_ports_release(dev);
-fail2:
- printk(KERN_ERR "fail2\n");
- ddb_buffers_free(dev);
-fail1:
- printk(KERN_ERR "fail1\n");
- if (dev->msi)
- pci_disable_msi(dev->pdev);
- free_irq(dev->pdev->irq, dev);
-fail:
- printk(KERN_ERR "fail\n");
- ddb_unmap(dev);
- pci_set_drvdata(pdev, 0);
- pci_disable_device(pdev);
- return -1;
-}
-
-/******************************************************************************/
-/******************************************************************************/
-/******************************************************************************/
-
-static struct ddb_info ddb_none = {
- .type = DDB_NONE,
- .name = "Digital Devices PCIe bridge",
-};
-
-static struct ddb_info ddb_octopus = {
- .type = DDB_OCTOPUS,
- .name = "Digital Devices Octopus DVB adapter",
- .port_num = 4,
-};
-
-static struct ddb_info ddb_octopus_le = {
- .type = DDB_OCTOPUS,
- .name = "Digital Devices Octopus LE DVB adapter",
- .port_num = 2,
-};
-
-static struct ddb_info ddb_v6 = {
- .type = DDB_OCTOPUS,
- .name = "Digital Devices Cine S2 V6 DVB adapter",
- .port_num = 3,
-};
-
-#define DDVID 0xdd01 /* Digital Devices Vendor ID */
-
-#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \
- .vendor = _vend, .device = _dev, \
- .subvendor = _subvend, .subdevice = _subdev, \
- .driver_data = (unsigned long)&_driverdata }
-
-static const struct pci_device_id ddb_id_tbl[] __devinitdata = {
- DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
- DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
- DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
- DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus),
- DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
- /* in case sub-ids got deleted in flash */
- DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
- {0}
-};
-MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
-
-
-static struct pci_driver ddb_pci_driver = {
- .name = "DDBridge",
- .id_table = ddb_id_tbl,
- .probe = ddb_probe,
- .remove = __devexit_p(ddb_remove),
-};
-
-static __init int module_init_ddbridge(void)
-{
- printk(KERN_INFO "Digital Devices PCIE bridge driver, "
- "Copyright (C) 2010-11 Digital Devices GmbH\n");
- if (ddb_class_create())
- return -1;
- return pci_register_driver(&ddb_pci_driver);
-}
-
-static __exit void module_exit_ddbridge(void)
-{
- pci_unregister_driver(&ddb_pci_driver);
- ddb_class_destroy();
-}
-
-module_init(module_init_ddbridge);
-module_exit(module_exit_ddbridge);
-
-MODULE_DESCRIPTION("Digital Devices PCIe Bridge");
-MODULE_AUTHOR("Ralph Metzler");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig
deleted file mode 100644
index f3de0a4d63f2..000000000000
--- a/drivers/media/dvb/dm1105/Kconfig
+++ /dev/null
@@ -1,20 +0,0 @@
-config DVB_DM1105
- tristate "SDMC DM1105 based PCI cards"
- depends on DVB_CORE && PCI && I2C
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_STV0288 if !DVB_FE_CUSTOMISE
- select DVB_STB6000 if !DVB_FE_CUSTOMISE
- select DVB_CX24116 if !DVB_FE_CUSTOMISE
- select DVB_SI21XX if !DVB_FE_CUSTOMISE
- select DVB_DS3000 if !DVB_FE_CUSTOMISE
- depends on RC_CORE
- help
- Support for cards based on the SDMC DM1105 PCI chip like
- DvbWorld 2002
-
- Since these cards have no MPEG decoder onboard, they transmit
- only compressed MPEG data over the PCI bus, so you need
- an external software decoder to watch TV on your computer.
-
- Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile
deleted file mode 100644
index 95a008b71fe5..000000000000
--- a/drivers/media/dvb/dm1105/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_DM1105) += dm1105.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
deleted file mode 100644
index 73970cd97af1..000000000000
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ /dev/null
@@ -1,1277 +0,0 @@
-/*
- * dmxdev.c - DVB demultiplexer device
- *
- * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
- * for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/ioctl.h>
-#include <linux/wait.h>
-#include <asm/uaccess.h>
-#include "dmxdev.h"
-
-static int debug;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-#define dprintk if (debug) printk
-
-static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
- const u8 *src, size_t len)
-{
- ssize_t free;
-
- if (!len)
- return 0;
- if (!buf->data)
- return 0;
-
- free = dvb_ringbuffer_free(buf);
- if (len > free) {
- dprintk("dmxdev: buffer overflow\n");
- return -EOVERFLOW;
- }
-
- return dvb_ringbuffer_write(buf, src, len);
-}
-
-static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src,
- int non_blocking, char __user *buf,
- size_t count, loff_t *ppos)
-{
- size_t todo;
- ssize_t avail;
- ssize_t ret = 0;
-
- if (!src->data)
- return 0;
-
- if (src->error) {
- ret = src->error;
- dvb_ringbuffer_flush(src);
- return ret;
- }
-
- for (todo = count; todo > 0; todo -= ret) {
- if (non_blocking && dvb_ringbuffer_empty(src)) {
- ret = -EWOULDBLOCK;
- break;
- }
-
- ret = wait_event_interruptible(src->queue,
- !dvb_ringbuffer_empty(src) ||
- (src->error != 0));
- if (ret < 0)
- break;
-
- if (src->error) {
- ret = src->error;
- dvb_ringbuffer_flush(src);
- break;
- }
-
- avail = dvb_ringbuffer_avail(src);
- if (avail > todo)
- avail = todo;
-
- ret = dvb_ringbuffer_read_user(src, buf, avail);
- if (ret < 0)
- break;
-
- buf += ret;
- }
-
- return (count - todo) ? (count - todo) : ret;
-}
-
-static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type)
-{
- struct list_head *head, *pos;
-
- head = demux->get_frontends(demux);
- if (!head)
- return NULL;
- list_for_each(pos, head)
- if (DMX_FE_ENTRY(pos)->source == type)
- return DMX_FE_ENTRY(pos);
-
- return NULL;
-}
-
-static int dvb_dvr_open(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
- struct dmx_frontend *front;
-
- dprintk("function : %s\n", __func__);
-
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- if (dmxdev->exit) {
- mutex_unlock(&dmxdev->mutex);
- return -ENODEV;
- }
-
- if ((file->f_flags & O_ACCMODE) == O_RDWR) {
- if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) {
- mutex_unlock(&dmxdev->mutex);
- return -EOPNOTSUPP;
- }
- }
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- void *mem;
- if (!dvbdev->readers) {
- mutex_unlock(&dmxdev->mutex);
- return -EBUSY;
- }
- mem = vmalloc(DVR_BUFFER_SIZE);
- if (!mem) {
- mutex_unlock(&dmxdev->mutex);
- return -ENOMEM;
- }
- dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
- dvbdev->readers--;
- }
-
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
- dmxdev->dvr_orig_fe = dmxdev->demux->frontend;
-
- if (!dmxdev->demux->write) {
- mutex_unlock(&dmxdev->mutex);
- return -EOPNOTSUPP;
- }
-
- front = get_fe(dmxdev->demux, DMX_MEMORY_FE);
-
- if (!front) {
- mutex_unlock(&dmxdev->mutex);
- return -EINVAL;
- }
- dmxdev->demux->disconnect_frontend(dmxdev->demux);
- dmxdev->demux->connect_frontend(dmxdev->demux, front);
- }
- dvbdev->users++;
- mutex_unlock(&dmxdev->mutex);
- return 0;
-}
-
-static int dvb_dvr_release(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
-
- mutex_lock(&dmxdev->mutex);
-
- if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
- dmxdev->demux->disconnect_frontend(dmxdev->demux);
- dmxdev->demux->connect_frontend(dmxdev->demux,
- dmxdev->dvr_orig_fe);
- }
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- dvbdev->readers++;
- if (dmxdev->dvr_buffer.data) {
- void *mem = dmxdev->dvr_buffer.data;
- mb();
- spin_lock_irq(&dmxdev->lock);
- dmxdev->dvr_buffer.data = NULL;
- spin_unlock_irq(&dmxdev->lock);
- vfree(mem);
- }
- }
- /* TODO */
- dvbdev->users--;
- if (dvbdev->users == 1 && dmxdev->exit == 1) {
- fops_put(file->f_op);
- file->f_op = NULL;
- mutex_unlock(&dmxdev->mutex);
- wake_up(&dvbdev->wait_queue);
- } else
- mutex_unlock(&dmxdev->mutex);
-
- return 0;
-}
-
-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
- int ret;
-
- if (!dmxdev->demux->write)
- return -EOPNOTSUPP;
- if ((file->f_flags & O_ACCMODE) != O_WRONLY)
- return -EINVAL;
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- if (dmxdev->exit) {
- mutex_unlock(&dmxdev->mutex);
- return -ENODEV;
- }
- ret = dmxdev->demux->write(dmxdev->demux, buf, count);
- mutex_unlock(&dmxdev->mutex);
- return ret;
-}
-
-static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
-
- if (dmxdev->exit)
- return -ENODEV;
-
- return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
-}
-
-static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev,
- unsigned long size)
-{
- struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer;
- void *newmem;
- void *oldmem;
-
- dprintk("function : %s\n", __func__);
-
- if (buf->size == size)
- return 0;
- if (!size)
- return -EINVAL;
-
- newmem = vmalloc(size);
- if (!newmem)
- return -ENOMEM;
-
- oldmem = buf->data;
-
- spin_lock_irq(&dmxdev->lock);
- buf->data = newmem;
- buf->size = size;
-
- /* reset and not flush in case the buffer shrinks */
- dvb_ringbuffer_reset(buf);
- spin_unlock_irq(&dmxdev->lock);
-
- vfree(oldmem);
-
- return 0;
-}
-
-static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter
- *dmxdevfilter, int state)
-{
- spin_lock_irq(&dmxdevfilter->dev->lock);
- dmxdevfilter->state = state;
- spin_unlock_irq(&dmxdevfilter->dev->lock);
-}
-
-static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter,
- unsigned long size)
-{
- struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
- void *newmem;
- void *oldmem;
-
- if (buf->size == size)
- return 0;
- if (!size)
- return -EINVAL;
- if (dmxdevfilter->state >= DMXDEV_STATE_GO)
- return -EBUSY;
-
- newmem = vmalloc(size);
- if (!newmem)
- return -ENOMEM;
-
- oldmem = buf->data;
-
- spin_lock_irq(&dmxdevfilter->dev->lock);
- buf->data = newmem;
- buf->size = size;
-
- /* reset and not flush in case the buffer shrinks */
- dvb_ringbuffer_reset(buf);
- spin_unlock_irq(&dmxdevfilter->dev->lock);
-
- vfree(oldmem);
-
- return 0;
-}
-
-static void dvb_dmxdev_filter_timeout(unsigned long data)
-{
- struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data;
-
- dmxdevfilter->buffer.error = -ETIMEDOUT;
- spin_lock_irq(&dmxdevfilter->dev->lock);
- dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT;
- spin_unlock_irq(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
-}
-
-static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter)
-{
- struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec;
-
- del_timer(&dmxdevfilter->timer);
- if (para->timeout) {
- dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout;
- dmxdevfilter->timer.data = (unsigned long)dmxdevfilter;
- dmxdevfilter->timer.expires =
- jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000;
- add_timer(&dmxdevfilter->timer);
- }
-}
-
-static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len,
- const u8 *buffer2, size_t buffer2_len,
- struct dmx_section_filter *filter,
- enum dmx_success success)
-{
- struct dmxdev_filter *dmxdevfilter = filter->priv;
- int ret;
-
- if (dmxdevfilter->buffer.error) {
- wake_up(&dmxdevfilter->buffer.queue);
- return 0;
- }
- spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->state != DMXDEV_STATE_GO) {
- spin_unlock(&dmxdevfilter->dev->lock);
- return 0;
- }
- del_timer(&dmxdevfilter->timer);
- dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n",
- buffer1[0], buffer1[1],
- buffer1[2], buffer1[3], buffer1[4], buffer1[5]);
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1,
- buffer1_len);
- if (ret == buffer1_len) {
- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2,
- buffer2_len);
- }
- if (ret < 0) {
- dvb_ringbuffer_flush(&dmxdevfilter->buffer);
- dmxdevfilter->buffer.error = ret;
- }
- if (dmxdevfilter->params.sec.flags & DMX_ONESHOT)
- dmxdevfilter->state = DMXDEV_STATE_DONE;
- spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&dmxdevfilter->buffer.queue);
- return 0;
-}
-
-static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len,
- const u8 *buffer2, size_t buffer2_len,
- struct dmx_ts_feed *feed,
- enum dmx_success success)
-{
- struct dmxdev_filter *dmxdevfilter = feed->priv;
- struct dvb_ringbuffer *buffer;
- int ret;
-
- spin_lock(&dmxdevfilter->dev->lock);
- if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) {
- spin_unlock(&dmxdevfilter->dev->lock);
- return 0;
- }
-
- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP
- || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)
- buffer = &dmxdevfilter->buffer;
- else
- buffer = &dmxdevfilter->dev->dvr_buffer;
- if (buffer->error) {
- spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
- return 0;
- }
- ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len);
- if (ret == buffer1_len)
- ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len);
- if (ret < 0) {
- dvb_ringbuffer_flush(buffer);
- buffer->error = ret;
- }
- spin_unlock(&dmxdevfilter->dev->lock);
- wake_up(&buffer->queue);
- return 0;
-}
-
-/* stop feed but only mark the specified filter as stopped (state set) */
-static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter)
-{
- struct dmxdev_feed *feed;
-
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
-
- switch (dmxdevfilter->type) {
- case DMXDEV_TYPE_SEC:
- del_timer(&dmxdevfilter->timer);
- dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec);
- break;
- case DMXDEV_TYPE_PES:
- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next)
- feed->ts->stop_filtering(feed->ts);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/* start feed associated with the specified filter */
-static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter)
-{
- struct dmxdev_feed *feed;
- int ret;
-
- dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
-
- switch (filter->type) {
- case DMXDEV_TYPE_SEC:
- return filter->feed.sec->start_filtering(filter->feed.sec);
- case DMXDEV_TYPE_PES:
- list_for_each_entry(feed, &filter->feed.ts, next) {
- ret = feed->ts->start_filtering(feed->ts);
- if (ret < 0) {
- dvb_dmxdev_feed_stop(filter);
- return ret;
- }
- }
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* restart section feed if it has filters left associated with it,
- otherwise release the feed */
-static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter)
-{
- int i;
- struct dmxdev *dmxdev = filter->dev;
- u16 pid = filter->params.sec.pid;
-
- for (i = 0; i < dmxdev->filternum; i++)
- if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
- dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
- dmxdev->filter[i].params.sec.pid == pid) {
- dvb_dmxdev_feed_start(&dmxdev->filter[i]);
- return 0;
- }
-
- filter->dev->demux->release_section_feed(dmxdev->demux,
- filter->feed.sec);
-
- return 0;
-}
-
-static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter)
-{
- struct dmxdev_feed *feed;
- struct dmx_demux *demux;
-
- if (dmxdevfilter->state < DMXDEV_STATE_GO)
- return 0;
-
- switch (dmxdevfilter->type) {
- case DMXDEV_TYPE_SEC:
- if (!dmxdevfilter->feed.sec)
- break;
- dvb_dmxdev_feed_stop(dmxdevfilter);
- if (dmxdevfilter->filter.sec)
- dmxdevfilter->feed.sec->
- release_filter(dmxdevfilter->feed.sec,
- dmxdevfilter->filter.sec);
- dvb_dmxdev_feed_restart(dmxdevfilter);
- dmxdevfilter->feed.sec = NULL;
- break;
- case DMXDEV_TYPE_PES:
- dvb_dmxdev_feed_stop(dmxdevfilter);
- demux = dmxdevfilter->dev->demux;
- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) {
- demux->release_ts_feed(demux, feed->ts);
- feed->ts = NULL;
- }
- break;
- default:
- if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED)
- return 0;
- return -EINVAL;
- }
-
- dvb_ringbuffer_flush(&dmxdevfilter->buffer);
- return 0;
-}
-
-static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter)
-{
- struct dmxdev_feed *feed, *tmp;
-
- /* delete all PIDs */
- list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) {
- list_del(&feed->next);
- kfree(feed);
- }
-
- BUG_ON(!list_empty(&dmxdevfilter->feed.ts));
-}
-
-static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter)
-{
- if (dmxdevfilter->state < DMXDEV_STATE_SET)
- return 0;
-
- if (dmxdevfilter->type == DMXDEV_TYPE_PES)
- dvb_dmxdev_delete_pids(dmxdevfilter);
-
- dmxdevfilter->type = DMXDEV_TYPE_NONE;
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
- return 0;
-}
-
-static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev,
- struct dmxdev_filter *filter,
- struct dmxdev_feed *feed)
-{
- struct timespec timeout = { 0 };
- struct dmx_pes_filter_params *para = &filter->params.pes;
- dmx_output_t otype;
- int ret;
- int ts_type;
- dmx_pes_type_t ts_pes;
- struct dmx_ts_feed *tsfeed;
-
- feed->ts = NULL;
- otype = para->output;
-
- ts_pes = para->pes_type;
-
- if (ts_pes < DMX_PES_OTHER)
- ts_type = TS_DECODER;
- else
- ts_type = 0;
-
- if (otype == DMX_OUT_TS_TAP)
- ts_type |= TS_PACKET;
- else if (otype == DMX_OUT_TSDEMUX_TAP)
- ts_type |= TS_PACKET | TS_DEMUX;
- else if (otype == DMX_OUT_TAP)
- ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY;
-
- ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts,
- dvb_dmxdev_ts_callback);
- if (ret < 0)
- return ret;
-
- tsfeed = feed->ts;
- tsfeed->priv = filter;
-
- ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout);
- if (ret < 0) {
- dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
- return ret;
- }
-
- ret = tsfeed->start_filtering(tsfeed);
- if (ret < 0) {
- dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
- return ret;
- }
-
- return 0;
-}
-
-static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
-{
- struct dmxdev *dmxdev = filter->dev;
- struct dmxdev_feed *feed;
- void *mem;
- int ret, i;
-
- if (filter->state < DMXDEV_STATE_SET)
- return -EINVAL;
-
- if (filter->state >= DMXDEV_STATE_GO)
- dvb_dmxdev_filter_stop(filter);
-
- if (!filter->buffer.data) {
- mem = vmalloc(filter->buffer.size);
- if (!mem)
- return -ENOMEM;
- spin_lock_irq(&filter->dev->lock);
- filter->buffer.data = mem;
- spin_unlock_irq(&filter->dev->lock);
- }
-
- dvb_ringbuffer_flush(&filter->buffer);
-
- switch (filter->type) {
- case DMXDEV_TYPE_SEC:
- {
- struct dmx_sct_filter_params *para = &filter->params.sec;
- struct dmx_section_filter **secfilter = &filter->filter.sec;
- struct dmx_section_feed **secfeed = &filter->feed.sec;
-
- *secfilter = NULL;
- *secfeed = NULL;
-
-
- /* find active filter/feed with same PID */
- for (i = 0; i < dmxdev->filternum; i++) {
- if (dmxdev->filter[i].state >= DMXDEV_STATE_GO &&
- dmxdev->filter[i].type == DMXDEV_TYPE_SEC &&
- dmxdev->filter[i].params.sec.pid == para->pid) {
- *secfeed = dmxdev->filter[i].feed.sec;
- break;
- }
- }
-
- /* if no feed found, try to allocate new one */
- if (!*secfeed) {
- ret = dmxdev->demux->allocate_section_feed(dmxdev->demux,
- secfeed,
- dvb_dmxdev_section_callback);
- if (ret < 0) {
- printk("DVB (%s): could not alloc feed\n",
- __func__);
- return ret;
- }
-
- ret = (*secfeed)->set(*secfeed, para->pid, 32768,
- (para->flags & DMX_CHECK_CRC) ? 1 : 0);
- if (ret < 0) {
- printk("DVB (%s): could not set feed\n",
- __func__);
- dvb_dmxdev_feed_restart(filter);
- return ret;
- }
- } else {
- dvb_dmxdev_feed_stop(filter);
- }
-
- ret = (*secfeed)->allocate_filter(*secfeed, secfilter);
- if (ret < 0) {
- dvb_dmxdev_feed_restart(filter);
- filter->feed.sec->start_filtering(*secfeed);
- dprintk("could not get filter\n");
- return ret;
- }
-
- (*secfilter)->priv = filter;
-
- memcpy(&((*secfilter)->filter_value[3]),
- &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
- memcpy(&(*secfilter)->filter_mask[3],
- &para->filter.mask[1], DMX_FILTER_SIZE - 1);
- memcpy(&(*secfilter)->filter_mode[3],
- &para->filter.mode[1], DMX_FILTER_SIZE - 1);
-
- (*secfilter)->filter_value[0] = para->filter.filter[0];
- (*secfilter)->filter_mask[0] = para->filter.mask[0];
- (*secfilter)->filter_mode[0] = para->filter.mode[0];
- (*secfilter)->filter_mask[1] = 0;
- (*secfilter)->filter_mask[2] = 0;
-
- filter->todo = 0;
-
- ret = filter->feed.sec->start_filtering(filter->feed.sec);
- if (ret < 0)
- return ret;
-
- dvb_dmxdev_filter_timer(filter);
- break;
- }
- case DMXDEV_TYPE_PES:
- list_for_each_entry(feed, &filter->feed.ts, next) {
- ret = dvb_dmxdev_start_feed(dmxdev, filter, feed);
- if (ret < 0) {
- dvb_dmxdev_filter_stop(filter);
- return ret;
- }
- }
- break;
- default:
- return -EINVAL;
- }
-
- dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO);
- return 0;
-}
-
-static int dvb_demux_open(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
- int i;
- struct dmxdev_filter *dmxdevfilter;
-
- if (!dmxdev->filter)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- for (i = 0; i < dmxdev->filternum; i++)
- if (dmxdev->filter[i].state == DMXDEV_STATE_FREE)
- break;
-
- if (i == dmxdev->filternum) {
- mutex_unlock(&dmxdev->mutex);
- return -EMFILE;
- }
-
- dmxdevfilter = &dmxdev->filter[i];
- mutex_init(&dmxdevfilter->mutex);
- file->private_data = dmxdevfilter;
-
- dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
- dmxdevfilter->type = DMXDEV_TYPE_NONE;
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
- init_timer(&dmxdevfilter->timer);
-
- dvbdev->users++;
-
- mutex_unlock(&dmxdev->mutex);
- return 0;
-}
-
-static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev,
- struct dmxdev_filter *dmxdevfilter)
-{
- mutex_lock(&dmxdev->mutex);
- mutex_lock(&dmxdevfilter->mutex);
-
- dvb_dmxdev_filter_stop(dmxdevfilter);
- dvb_dmxdev_filter_reset(dmxdevfilter);
-
- if (dmxdevfilter->buffer.data) {
- void *mem = dmxdevfilter->buffer.data;
-
- spin_lock_irq(&dmxdev->lock);
- dmxdevfilter->buffer.data = NULL;
- spin_unlock_irq(&dmxdev->lock);
- vfree(mem);
- }
-
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
- wake_up(&dmxdevfilter->buffer.queue);
- mutex_unlock(&dmxdevfilter->mutex);
- mutex_unlock(&dmxdev->mutex);
- return 0;
-}
-
-static inline void invert_mode(dmx_filter_t *filter)
-{
- int i;
-
- for (i = 0; i < DMX_FILTER_SIZE; i++)
- filter->mode[i] ^= 0xff;
-}
-
-static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev,
- struct dmxdev_filter *filter, u16 pid)
-{
- struct dmxdev_feed *feed;
-
- if ((filter->type != DMXDEV_TYPE_PES) ||
- (filter->state < DMXDEV_STATE_SET))
- return -EINVAL;
-
- /* only TS packet filters may have multiple PIDs */
- if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) &&
- (!list_empty(&filter->feed.ts)))
- return -EINVAL;
-
- feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL);
- if (feed == NULL)
- return -ENOMEM;
-
- feed->pid = pid;
- list_add(&feed->next, &filter->feed.ts);
-
- if (filter->state >= DMXDEV_STATE_GO)
- return dvb_dmxdev_start_feed(dmxdev, filter, feed);
-
- return 0;
-}
-
-static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev,
- struct dmxdev_filter *filter, u16 pid)
-{
- struct dmxdev_feed *feed, *tmp;
-
- if ((filter->type != DMXDEV_TYPE_PES) ||
- (filter->state < DMXDEV_STATE_SET))
- return -EINVAL;
-
- list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) {
- if ((feed->pid == pid) && (feed->ts != NULL)) {
- feed->ts->stop_filtering(feed->ts);
- filter->dev->demux->release_ts_feed(filter->dev->demux,
- feed->ts);
- list_del(&feed->next);
- kfree(feed);
- }
- }
-
- return 0;
-}
-
-static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev,
- struct dmxdev_filter *dmxdevfilter,
- struct dmx_sct_filter_params *params)
-{
- dprintk("function : %s\n", __func__);
-
- dvb_dmxdev_filter_stop(dmxdevfilter);
-
- dmxdevfilter->type = DMXDEV_TYPE_SEC;
- memcpy(&dmxdevfilter->params.sec,
- params, sizeof(struct dmx_sct_filter_params));
- invert_mode(&dmxdevfilter->params.sec.filter);
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
-
- if (params->flags & DMX_IMMEDIATE_START)
- return dvb_dmxdev_filter_start(dmxdevfilter);
-
- return 0;
-}
-
-static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev,
- struct dmxdev_filter *dmxdevfilter,
- struct dmx_pes_filter_params *params)
-{
- int ret;
-
- dvb_dmxdev_filter_stop(dmxdevfilter);
- dvb_dmxdev_filter_reset(dmxdevfilter);
-
- if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0)
- return -EINVAL;
-
- dmxdevfilter->type = DMXDEV_TYPE_PES;
- memcpy(&dmxdevfilter->params, params,
- sizeof(struct dmx_pes_filter_params));
- INIT_LIST_HEAD(&dmxdevfilter->feed.ts);
-
- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET);
-
- ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter,
- dmxdevfilter->params.pes.pid);
- if (ret < 0)
- return ret;
-
- if (params->flags & DMX_IMMEDIATE_START)
- return dvb_dmxdev_filter_start(dmxdevfilter);
-
- return 0;
-}
-
-static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
- struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- int result, hcount;
- int done = 0;
-
- if (dfil->todo <= 0) {
- hcount = 3 + dfil->todo;
- if (hcount > count)
- hcount = count;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
- file->f_flags & O_NONBLOCK,
- buf, hcount, ppos);
- if (result < 0) {
- dfil->todo = 0;
- return result;
- }
- if (copy_from_user(dfil->secheader - dfil->todo, buf, result))
- return -EFAULT;
- buf += result;
- done = result;
- count -= result;
- dfil->todo -= result;
- if (dfil->todo > -3)
- return done;
- dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff;
- if (!count)
- return done;
- }
- if (count > dfil->todo)
- count = dfil->todo;
- result = dvb_dmxdev_buffer_read(&dfil->buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
- if (result < 0)
- return result;
- dfil->todo -= result;
- return (result + done);
-}
-
-static ssize_t
-dvb_demux_read(struct file *file, char __user *buf, size_t count,
- loff_t *ppos)
-{
- struct dmxdev_filter *dmxdevfilter = file->private_data;
- int ret;
-
- if (mutex_lock_interruptible(&dmxdevfilter->mutex))
- return -ERESTARTSYS;
-
- if (dmxdevfilter->type == DMXDEV_TYPE_SEC)
- ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos);
- else
- ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer,
- file->f_flags & O_NONBLOCK,
- buf, count, ppos);
-
- mutex_unlock(&dmxdevfilter->mutex);
- return ret;
-}
-
-static int dvb_demux_do_ioctl(struct file *file,
- unsigned int cmd, void *parg)
-{
- struct dmxdev_filter *dmxdevfilter = file->private_data;
- struct dmxdev *dmxdev = dmxdevfilter->dev;
- unsigned long arg = (unsigned long)parg;
- int ret = 0;
-
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- switch (cmd) {
- case DMX_START:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- if (dmxdevfilter->state < DMXDEV_STATE_SET)
- ret = -EINVAL;
- else
- ret = dvb_dmxdev_filter_start(dmxdevfilter);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_STOP:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- ret = dvb_dmxdev_filter_stop(dmxdevfilter);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_SET_FILTER:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_SET_PES_FILTER:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_SET_BUFFER_SIZE:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- mutex_unlock(&dmxdev->mutex);
- return -ERESTARTSYS;
- }
- ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_GET_PES_PIDS:
- if (!dmxdev->demux->get_pes_pids) {
- ret = -EINVAL;
- break;
- }
- dmxdev->demux->get_pes_pids(dmxdev->demux, parg);
- break;
-
- case DMX_GET_CAPS:
- if (!dmxdev->demux->get_caps) {
- ret = -EINVAL;
- break;
- }
- ret = dmxdev->demux->get_caps(dmxdev->demux, parg);
- break;
-
- case DMX_SET_SOURCE:
- if (!dmxdev->demux->set_source) {
- ret = -EINVAL;
- break;
- }
- ret = dmxdev->demux->set_source(dmxdev->demux, parg);
- break;
-
- case DMX_GET_STC:
- if (!dmxdev->demux->get_stc) {
- ret = -EINVAL;
- break;
- }
- ret = dmxdev->demux->get_stc(dmxdev->demux,
- ((struct dmx_stc *)parg)->num,
- &((struct dmx_stc *)parg)->stc,
- &((struct dmx_stc *)parg)->base);
- break;
-
- case DMX_ADD_PID:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- ret = -ERESTARTSYS;
- break;
- }
- ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- case DMX_REMOVE_PID:
- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
- ret = -ERESTARTSYS;
- break;
- }
- ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg);
- mutex_unlock(&dmxdevfilter->mutex);
- break;
-
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&dmxdev->mutex);
- return ret;
-}
-
-static long dvb_demux_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
-}
-
-static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
-{
- struct dmxdev_filter *dmxdevfilter = file->private_data;
- unsigned int mask = 0;
-
- if (!dmxdevfilter)
- return -EINVAL;
-
- poll_wait(file, &dmxdevfilter->buffer.queue, wait);
-
- if (dmxdevfilter->state != DMXDEV_STATE_GO &&
- dmxdevfilter->state != DMXDEV_STATE_DONE &&
- dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT)
- return 0;
-
- if (dmxdevfilter->buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
-
- if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
-
- return mask;
-}
-
-static int dvb_demux_release(struct inode *inode, struct file *file)
-{
- struct dmxdev_filter *dmxdevfilter = file->private_data;
- struct dmxdev *dmxdev = dmxdevfilter->dev;
-
- int ret;
-
- ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter);
-
- mutex_lock(&dmxdev->mutex);
- dmxdev->dvbdev->users--;
- if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) {
- fops_put(file->f_op);
- file->f_op = NULL;
- mutex_unlock(&dmxdev->mutex);
- wake_up(&dmxdev->dvbdev->wait_queue);
- } else
- mutex_unlock(&dmxdev->mutex);
-
- return ret;
-}
-
-static const struct file_operations dvb_demux_fops = {
- .owner = THIS_MODULE,
- .read = dvb_demux_read,
- .unlocked_ioctl = dvb_demux_ioctl,
- .open = dvb_demux_open,
- .release = dvb_demux_release,
- .poll = dvb_demux_poll,
- .llseek = default_llseek,
-};
-
-static struct dvb_device dvbdev_demux = {
- .priv = NULL,
- .users = 1,
- .writers = 1,
- .fops = &dvb_demux_fops
-};
-
-static int dvb_dvr_do_ioctl(struct file *file,
- unsigned int cmd, void *parg)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
- unsigned long arg = (unsigned long)parg;
- int ret;
-
- if (mutex_lock_interruptible(&dmxdev->mutex))
- return -ERESTARTSYS;
-
- switch (cmd) {
- case DMX_SET_BUFFER_SIZE:
- ret = dvb_dvr_set_buffer_size(dmxdev, arg);
- break;
-
- default:
- ret = -EINVAL;
- break;
- }
- mutex_unlock(&dmxdev->mutex);
- return ret;
-}
-
-static long dvb_dvr_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
-}
-
-static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dmxdev *dmxdev = dvbdev->priv;
- unsigned int mask = 0;
-
- dprintk("function : %s\n", __func__);
-
- poll_wait(file, &dmxdev->dvr_buffer.queue, wait);
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- if (dmxdev->dvr_buffer.error)
- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
-
- if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer))
- mask |= (POLLIN | POLLRDNORM | POLLPRI);
- } else
- mask |= (POLLOUT | POLLWRNORM | POLLPRI);
-
- return mask;
-}
-
-static const struct file_operations dvb_dvr_fops = {
- .owner = THIS_MODULE,
- .read = dvb_dvr_read,
- .write = dvb_dvr_write,
- .unlocked_ioctl = dvb_dvr_ioctl,
- .open = dvb_dvr_open,
- .release = dvb_dvr_release,
- .poll = dvb_dvr_poll,
- .llseek = default_llseek,
-};
-
-static struct dvb_device dvbdev_dvr = {
- .priv = NULL,
- .readers = 1,
- .users = 1,
- .fops = &dvb_dvr_fops
-};
-
-int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
-{
- int i;
-
- if (dmxdev->demux->open(dmxdev->demux) < 0)
- return -EUSERS;
-
- dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter));
- if (!dmxdev->filter)
- return -ENOMEM;
-
- mutex_init(&dmxdev->mutex);
- spin_lock_init(&dmxdev->lock);
- for (i = 0; i < dmxdev->filternum; i++) {
- dmxdev->filter[i].dev = dmxdev;
- dmxdev->filter[i].buffer.data = NULL;
- dvb_dmxdev_filter_state_set(&dmxdev->filter[i],
- DMXDEV_STATE_FREE);
- }
-
- dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev,
- DVB_DEVICE_DEMUX);
- dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr,
- dmxdev, DVB_DEVICE_DVR);
-
- dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192);
-
- return 0;
-}
-
-EXPORT_SYMBOL(dvb_dmxdev_init);
-
-void dvb_dmxdev_release(struct dmxdev *dmxdev)
-{
- dmxdev->exit=1;
- if (dmxdev->dvbdev->users > 1) {
- wait_event(dmxdev->dvbdev->wait_queue,
- dmxdev->dvbdev->users==1);
- }
- if (dmxdev->dvr_dvbdev->users > 1) {
- wait_event(dmxdev->dvr_dvbdev->wait_queue,
- dmxdev->dvr_dvbdev->users==1);
- }
-
- dvb_unregister_device(dmxdev->dvbdev);
- dvb_unregister_device(dmxdev->dvr_dvbdev);
-
- vfree(dmxdev->filter);
- dmxdev->filter = NULL;
- dmxdev->demux->close(dmxdev->demux);
-}
-
-EXPORT_SYMBOL(dvb_dmxdev_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
deleted file mode 100644
index d82469f842e2..000000000000
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ /dev/null
@@ -1,1309 +0,0 @@
-/*
- * dvb_demux.c - DVB kernel demux API
- *
- * Copyright (C) 2000-2001 Ralph Metzler <ralph@convergence.de>
- * & Marcus Metzler <marcus@convergence.de>
- * for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/string.h>
-#include <linux/crc32.h>
-#include <asm/uaccess.h>
-#include <asm/div64.h>
-
-#include "dvb_demux.h"
-
-#define NOBUFS
-/*
-** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog
-*/
-// #define DVB_DEMUX_SECTION_LOSS_LOG
-
-static int dvb_demux_tscheck;
-module_param(dvb_demux_tscheck, int, 0644);
-MODULE_PARM_DESC(dvb_demux_tscheck,
- "enable transport stream continuity and TEI check");
-
-static int dvb_demux_speedcheck;
-module_param(dvb_demux_speedcheck, int, 0644);
-MODULE_PARM_DESC(dvb_demux_speedcheck,
- "enable transport stream speed check");
-
-#define dprintk_tscheck(x...) do { \
- if (dvb_demux_tscheck && printk_ratelimit()) \
- printk(x); \
- } while (0)
-
-/******************************************************************************
- * static inlined helper functions
- ******************************************************************************/
-
-static inline u16 section_length(const u8 *buf)
-{
- return 3 + ((buf[1] & 0x0f) << 8) + buf[2];
-}
-
-static inline u16 ts_pid(const u8 *buf)
-{
- return ((buf[1] & 0x1f) << 8) + buf[2];
-}
-
-static inline u8 payload(const u8 *tsp)
-{
- if (!(tsp[3] & 0x10)) // no payload?
- return 0;
-
- if (tsp[3] & 0x20) { // adaptation field?
- if (tsp[4] > 183) // corrupted data?
- return 0;
- else
- return 184 - 1 - tsp[4];
- }
-
- return 184;
-}
-
-static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len)
-{
- return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len));
-}
-
-static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s,
- size_t len)
-{
- memcpy(d, s, len);
-}
-
-/******************************************************************************
- * Software filter functions
- ******************************************************************************/
-
-static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed,
- const u8 *buf)
-{
- int count = payload(buf);
- int p;
- //int ccok;
- //u8 cc;
-
- if (count == 0)
- return -1;
-
- p = 188 - count;
-
- /*
- cc = buf[3] & 0x0f;
- ccok = ((feed->cc + 1) & 0x0f) == cc;
- feed->cc = cc;
- if (!ccok)
- printk("missed packet!\n");
- */
-
- if (buf[1] & 0x40) // PUSI ?
- feed->peslen = 0xfffa;
-
- feed->peslen += count;
-
- return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK);
-}
-
-static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed,
- struct dvb_demux_filter *f)
-{
- u8 neq = 0;
- int i;
-
- for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
- u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i];
-
- if (f->maskandmode[i] & xor)
- return 0;
-
- neq |= f->maskandnotmode[i] & xor;
- }
-
- if (f->doneq && !neq)
- return 0;
-
- return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen,
- NULL, 0, &f->filter, DMX_OK);
-}
-
-static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct dvb_demux_filter *f = feed->filter;
- struct dmx_section_feed *sec = &feed->feed.sec;
- int section_syntax_indicator;
-
- if (!sec->is_filtering)
- return 0;
-
- if (!f)
- return 0;
-
- if (sec->check_crc) {
- section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0);
- if (section_syntax_indicator &&
- demux->check_crc32(feed, sec->secbuf, sec->seclen))
- return -1;
- }
-
- do {
- if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0)
- return -1;
- } while ((f = f->next) && sec->is_filtering);
-
- sec->seclen = 0;
-
- return 0;
-}
-
-static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed)
-{
- struct dmx_section_feed *sec = &feed->feed.sec;
-
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- if (sec->secbufp < sec->tsfeedp) {
- int i, n = sec->tsfeedp - sec->secbufp;
-
- /*
- * Section padding is done with 0xff bytes entirely.
- * Due to speed reasons, we won't check all of them
- * but just first and last.
- */
- if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) {
- printk("dvb_demux.c section ts padding loss: %d/%d\n",
- n, sec->tsfeedp);
- printk("dvb_demux.c pad data:");
- for (i = 0; i < n; i++)
- printk(" %02x", sec->secbuf[i]);
- printk("\n");
- }
- }
-#endif
-
- sec->tsfeedp = sec->secbufp = sec->seclen = 0;
- sec->secbuf = sec->secbuf_base;
-}
-
-/*
- * Losless Section Demux 1.4.1 by Emard
- * Valsecchi Patrick:
- * - middle of section A (no PUSI)
- * - end of section A and start of section B
- * (with PUSI pointing to the start of the second section)
- *
- * In this case, without feed->pusi_seen you'll receive a garbage section
- * consisting of the end of section A. Basically because tsfeedp
- * is incemented and the use=0 condition is not raised
- * when the second packet arrives.
- *
- * Fix:
- * when demux is started, let feed->pusi_seen = 0 to
- * prevent initial feeding of garbage from the end of
- * previous section. When you for the first time see PUSI=1
- * then set feed->pusi_seen = 1
- */
-static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed,
- const u8 *buf, u8 len)
-{
- struct dvb_demux *demux = feed->demux;
- struct dmx_section_feed *sec = &feed->feed.sec;
- u16 limit, seclen, n;
-
- if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE)
- return 0;
-
- if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) {
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- printk("dvb_demux.c section buffer full loss: %d/%d\n",
- sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE,
- DMX_MAX_SECFEED_SIZE);
-#endif
- len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp;
- }
-
- if (len <= 0)
- return 0;
-
- demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len);
- sec->tsfeedp += len;
-
- /*
- * Dump all the sections we can find in the data (Emard)
- */
- limit = sec->tsfeedp;
- if (limit > DMX_MAX_SECFEED_SIZE)
- return -1; /* internal error should never happen */
-
- /* to be sure always set secbuf */
- sec->secbuf = sec->secbuf_base + sec->secbufp;
-
- for (n = 0; sec->secbufp + 2 < limit; n++) {
- seclen = section_length(sec->secbuf);
- if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE
- || seclen + sec->secbufp > limit)
- return 0;
- sec->seclen = seclen;
- sec->crc_val = ~0;
- /* dump [secbuf .. secbuf+seclen) */
- if (feed->pusi_seen)
- dvb_dmx_swfilter_section_feed(feed);
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- else
- printk("dvb_demux.c pusi not seen, discarding section data\n");
-#endif
- sec->secbufp += seclen; /* secbufp and secbuf moving together is */
- sec->secbuf += seclen; /* redundant but saves pointer arithmetic */
- }
-
- return 0;
-}
-
-static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed,
- const u8 *buf)
-{
- u8 p, count;
- int ccok, dc_i = 0;
- u8 cc;
-
- count = payload(buf);
-
- if (count == 0) /* count == 0 if no payload or out of range */
- return -1;
-
- p = 188 - count; /* payload start */
-
- cc = buf[3] & 0x0f;
- ccok = ((feed->cc + 1) & 0x0f) == cc;
- feed->cc = cc;
-
- if (buf[3] & 0x20) {
- /* adaption field present, check for discontinuity_indicator */
- if ((buf[4] > 0) && (buf[5] & 0x80))
- dc_i = 1;
- }
-
- if (!ccok || dc_i) {
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- printk("dvb_demux.c discontinuity detected %d bytes lost\n",
- count);
- /*
- * those bytes under sume circumstances will again be reported
- * in the following dvb_dmx_swfilter_section_new
- */
-#endif
- /*
- * Discontinuity detected. Reset pusi_seen = 0 to
- * stop feeding of suspicious data until next PUSI=1 arrives
- */
- feed->pusi_seen = 0;
- dvb_dmx_swfilter_section_new(feed);
- }
-
- if (buf[1] & 0x40) {
- /* PUSI=1 (is set), section boundary is here */
- if (count > 1 && buf[p] < count) {
- const u8 *before = &buf[p + 1];
- u8 before_len = buf[p];
- const u8 *after = &before[before_len];
- u8 after_len = count - 1 - before_len;
-
- dvb_dmx_swfilter_section_copy_dump(feed, before,
- before_len);
- /* before start of new section, set pusi_seen = 1 */
- feed->pusi_seen = 1;
- dvb_dmx_swfilter_section_new(feed);
- dvb_dmx_swfilter_section_copy_dump(feed, after,
- after_len);
- }
-#ifdef DVB_DEMUX_SECTION_LOSS_LOG
- else if (count > 0)
- printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count);
-#endif
- } else {
- /* PUSI=0 (is not set), no section boundary */
- dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count);
- }
-
- return 0;
-}
-
-static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed,
- const u8 *buf)
-{
- switch (feed->type) {
- case DMX_TYPE_TS:
- if (!feed->feed.ts.is_filtering)
- break;
- if (feed->ts_type & TS_PACKET) {
- if (feed->ts_type & TS_PAYLOAD_ONLY)
- dvb_dmx_swfilter_payload(feed, buf);
- else
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts,
- DMX_OK);
- }
- if (feed->ts_type & TS_DECODER)
- if (feed->demux->write_to_decoder)
- feed->demux->write_to_decoder(feed, buf, 188);
- break;
-
- case DMX_TYPE_SEC:
- if (!feed->feed.sec.is_filtering)
- break;
- if (dvb_dmx_swfilter_section_packet(feed, buf) < 0)
- feed->feed.sec.seclen = feed->feed.sec.secbufp = 0;
- break;
-
- default:
- break;
- }
-}
-
-#define DVR_FEED(f) \
- (((f)->type == DMX_TYPE_TS) && \
- ((f)->feed.ts.is_filtering) && \
- (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET))
-
-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf)
-{
- struct dvb_demux_feed *feed;
- u16 pid = ts_pid(buf);
- int dvr_done = 0;
-
- if (dvb_demux_speedcheck) {
- struct timespec cur_time, delta_time;
- u64 speed_bytes, speed_timedelta;
-
- demux->speed_pkts_cnt++;
-
- /* show speed every SPEED_PKTS_INTERVAL packets */
- if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) {
- cur_time = current_kernel_time();
-
- if (demux->speed_last_time.tv_sec != 0 &&
- demux->speed_last_time.tv_nsec != 0) {
- delta_time = timespec_sub(cur_time,
- demux->speed_last_time);
- speed_bytes = (u64)demux->speed_pkts_cnt
- * 188 * 8;
- /* convert to 1024 basis */
- speed_bytes = 1000 * div64_u64(speed_bytes,
- 1024);
- speed_timedelta =
- (u64)timespec_to_ns(&delta_time);
- speed_timedelta = div64_u64(speed_timedelta,
- 1000000); /* nsec -> usec */
- printk(KERN_INFO "TS speed %llu Kbits/sec \n",
- div64_u64(speed_bytes,
- speed_timedelta));
- };
-
- demux->speed_last_time = cur_time;
- demux->speed_pkts_cnt = 0;
- };
- };
-
- if (demux->cnt_storage && dvb_demux_tscheck) {
- /* check pkt counter */
- if (pid < MAX_PID) {
- if (buf[1] & 0x80)
- dprintk_tscheck("TEI detected. "
- "PID=0x%x data1=0x%x\n",
- pid, buf[1]);
-
- if ((buf[3] & 0xf) != demux->cnt_storage[pid])
- dprintk_tscheck("TS packet counter mismatch. "
- "PID=0x%x expected 0x%x "
- "got 0x%x\n",
- pid, demux->cnt_storage[pid],
- buf[3] & 0xf);
-
- demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf;
- };
- /* end check */
- };
-
- list_for_each_entry(feed, &demux->feed_list, list_head) {
- if ((feed->pid != pid) && (feed->pid != 0x2000))
- continue;
-
- /* copy each packet only once to the dvr device, even
- * if a PID is in multiple filters (e.g. video + PCR) */
- if ((DVR_FEED(feed)) && (dvr_done++))
- continue;
-
- if (feed->pid == pid)
- dvb_dmx_swfilter_packet_type(feed, buf);
- else if (feed->pid == 0x2000)
- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK);
- }
-}
-
-void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf,
- size_t count)
-{
- spin_lock(&demux->lock);
-
- while (count--) {
- if (buf[0] == 0x47)
- dvb_dmx_swfilter_packet(demux, buf);
- buf += 188;
- }
-
- spin_unlock(&demux->lock);
-}
-
-EXPORT_SYMBOL(dvb_dmx_swfilter_packets);
-
-static inline int find_next_packet(const u8 *buf, int pos, size_t count,
- const int pktsize)
-{
- int start = pos, lost;
-
- while (pos < count) {
- if (buf[pos] == 0x47 ||
- (pktsize == 204 && buf[pos] == 0xB8))
- break;
- pos++;
- }
-
- lost = pos - start;
- if (lost) {
- /* This garbage is part of a valid packet? */
- int backtrack = pos - pktsize;
- if (backtrack >= 0 && (buf[backtrack] == 0x47 ||
- (pktsize == 204 && buf[backtrack] == 0xB8)))
- return backtrack;
- }
-
- return pos;
-}
-
-/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */
-static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf,
- size_t count, const int pktsize)
-{
- int p = 0, i, j;
- const u8 *q;
-
- spin_lock(&demux->lock);
-
- if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */
- i = demux->tsbufp;
- j = pktsize - i;
- if (count < j) {
- memcpy(&demux->tsbuf[i], buf, count);
- demux->tsbufp += count;
- goto bailout;
- }
- memcpy(&demux->tsbuf[i], buf, j);
- if (demux->tsbuf[0] == 0x47) /* double check */
- dvb_dmx_swfilter_packet(demux, demux->tsbuf);
- demux->tsbufp = 0;
- p += j;
- }
-
- while (1) {
- p = find_next_packet(buf, p, count, pktsize);
- if (p >= count)
- break;
- if (count - p < pktsize)
- break;
-
- q = &buf[p];
-
- if (pktsize == 204 && (*q == 0xB8)) {
- memcpy(demux->tsbuf, q, 188);
- demux->tsbuf[0] = 0x47;
- q = demux->tsbuf;
- }
- dvb_dmx_swfilter_packet(demux, q);
- p += pktsize;
- }
-
- i = count - p;
- if (i) {
- memcpy(demux->tsbuf, &buf[p], i);
- demux->tsbufp = i;
- if (pktsize == 204 && demux->tsbuf[0] == 0xB8)
- demux->tsbuf[0] = 0x47;
- }
-
-bailout:
- spin_unlock(&demux->lock);
-}
-
-void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count)
-{
- _dvb_dmx_swfilter(demux, buf, count, 188);
-}
-EXPORT_SYMBOL(dvb_dmx_swfilter);
-
-void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
-{
- _dvb_dmx_swfilter(demux, buf, count, 204);
-}
-EXPORT_SYMBOL(dvb_dmx_swfilter_204);
-
-void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
-{
- spin_lock(&demux->lock);
-
- demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
-
- spin_unlock(&demux->lock);
-}
-EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
-
-static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
-{
- int i;
-
- for (i = 0; i < demux->filternum; i++)
- if (demux->filter[i].state == DMX_STATE_FREE)
- break;
-
- if (i == demux->filternum)
- return NULL;
-
- demux->filter[i].state = DMX_STATE_ALLOCATED;
-
- return &demux->filter[i];
-}
-
-static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux)
-{
- int i;
-
- for (i = 0; i < demux->feednum; i++)
- if (demux->feed[i].state == DMX_STATE_FREE)
- break;
-
- if (i == demux->feednum)
- return NULL;
-
- demux->feed[i].state = DMX_STATE_ALLOCATED;
-
- return &demux->feed[i];
-}
-
-static int dvb_demux_feed_find(struct dvb_demux_feed *feed)
-{
- struct dvb_demux_feed *entry;
-
- list_for_each_entry(entry, &feed->demux->feed_list, list_head)
- if (entry == feed)
- return 1;
-
- return 0;
-}
-
-static void dvb_demux_feed_add(struct dvb_demux_feed *feed)
-{
- spin_lock_irq(&feed->demux->lock);
- if (dvb_demux_feed_find(feed)) {
- printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n",
- __func__, feed->type, feed->state, feed->pid);
- goto out;
- }
-
- list_add(&feed->list_head, &feed->demux->feed_list);
-out:
- spin_unlock_irq(&feed->demux->lock);
-}
-
-static void dvb_demux_feed_del(struct dvb_demux_feed *feed)
-{
- spin_lock_irq(&feed->demux->lock);
- if (!(dvb_demux_feed_find(feed))) {
- printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n",
- __func__, feed->type, feed->state, feed->pid);
- goto out;
- }
-
- list_del(&feed->list_head);
-out:
- spin_unlock_irq(&feed->demux->lock);
-}
-
-static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type,
- enum dmx_ts_pes pes_type,
- size_t circular_buffer_size, struct timespec timeout)
-{
- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
- struct dvb_demux *demux = feed->demux;
-
- if (pid > DMX_MAX_PID)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&demux->mutex))
- return -ERESTARTSYS;
-
- if (ts_type & TS_DECODER) {
- if (pes_type >= DMX_TS_PES_OTHER) {
- mutex_unlock(&demux->mutex);
- return -EINVAL;
- }
-
- if (demux->pesfilter[pes_type] &&
- demux->pesfilter[pes_type] != feed) {
- mutex_unlock(&demux->mutex);
- return -EINVAL;
- }
-
- demux->pesfilter[pes_type] = feed;
- demux->pids[pes_type] = pid;
- }
-
- dvb_demux_feed_add(feed);
-
- feed->pid = pid;
- feed->buffer_size = circular_buffer_size;
- feed->timeout = timeout;
- feed->ts_type = ts_type;
- feed->pes_type = pes_type;
-
- if (feed->buffer_size) {
-#ifdef NOBUFS
- feed->buffer = NULL;
-#else
- feed->buffer = vmalloc(feed->buffer_size);
- if (!feed->buffer) {
- mutex_unlock(&demux->mutex);
- return -ENOMEM;
- }
-#endif
- }
-
- feed->state = DMX_STATE_READY;
- mutex_unlock(&demux->mutex);
-
- return 0;
-}
-
-static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
-{
- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
- struct dvb_demux *demux = feed->demux;
- int ret;
-
- if (mutex_lock_interruptible(&demux->mutex))
- return -ERESTARTSYS;
-
- if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) {
- mutex_unlock(&demux->mutex);
- return -EINVAL;
- }
-
- if (!demux->start_feed) {
- mutex_unlock(&demux->mutex);
- return -ENODEV;
- }
-
- if ((ret = demux->start_feed(feed)) < 0) {
- mutex_unlock(&demux->mutex);
- return ret;
- }
-
- spin_lock_irq(&demux->lock);
- ts_feed->is_filtering = 1;
- feed->state = DMX_STATE_GO;
- spin_unlock_irq(&demux->lock);
- mutex_unlock(&demux->mutex);
-
- return 0;
-}
-
-static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed)
-{
- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
- struct dvb_demux *demux = feed->demux;
- int ret;
-
- mutex_lock(&demux->mutex);
-
- if (feed->state < DMX_STATE_GO) {
- mutex_unlock(&demux->mutex);
- return -EINVAL;
- }
-
- if (!demux->stop_feed) {
- mutex_unlock(&demux->mutex);
- return -ENODEV;
- }
-
- ret = demux->stop_feed(feed);
-
- spin_lock_irq(&demux->lock);
- ts_feed->is_filtering = 0;
- feed->state = DMX_STATE_ALLOCATED;
- spin_unlock_irq(&demux->lock);
- mutex_unlock(&demux->mutex);
-
- return ret;
-}
-
-static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
- struct dmx_ts_feed **ts_feed,
- dmx_ts_cb callback)
-{
- struct dvb_demux *demux = (struct dvb_demux *)dmx;
- struct dvb_demux_feed *feed;
-
- if (mutex_lock_interruptible(&demux->mutex))
- return -ERESTARTSYS;
-
- if (!(feed = dvb_dmx_feed_alloc(demux))) {
- mutex_unlock(&demux->mutex);
- return -EBUSY;
- }
-
- feed->type = DMX_TYPE_TS;
- feed->cb.ts = callback;
- feed->demux = demux;
- feed->pid = 0xffff;
- feed->peslen = 0xfffa;
- feed->buffer = NULL;
-
- (*ts_feed) = &feed->feed.ts;
- (*ts_feed)->parent = dmx;
- (*ts_feed)->priv = NULL;
- (*ts_feed)->is_filtering = 0;
- (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering;
- (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
- (*ts_feed)->set = dmx_ts_feed_set;
-
- if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
- feed->state = DMX_STATE_FREE;
- mutex_unlock(&demux->mutex);
- return -EBUSY;
- }
-
- feed->filter->type = DMX_TYPE_TS;
- feed->filter->feed = feed;
- feed->filter->state = DMX_STATE_READY;
-
- mutex_unlock(&demux->mutex);
-
- return 0;
-}
-
-static int dvbdmx_release_ts_feed(struct dmx_demux *dmx,
- struct dmx_ts_feed *ts_feed)
-{
- struct dvb_demux *demux = (struct dvb_demux *)dmx;
- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
-
- mutex_lock(&demux->mutex);
-
- if (feed->state == DMX_STATE_FREE) {
- mutex_unlock(&demux->mutex);
- return -EINVAL;
- }
-#ifndef NOBUFS
- vfree(feed->buffer);
- feed->buffer = NULL;
-#endif
-
- feed->state = DMX_STATE_FREE;
- feed->filter->state = DMX_STATE_FREE;
-
- dvb_demux_feed_del(feed);
-
- feed->pid = 0xffff;
-
- if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER)
- demux->pesfilter[feed->pes_type] = NULL;
-
- mutex_unlock(&demux->mutex);
- return 0;
-}
-
-/******************************************************************************
- * dmx_section_feed API calls
- ******************************************************************************/
-
-static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed,
- struct dmx_section_filter **filter)
-{
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdemux = dvbdmxfeed->demux;
- struct dvb_demux_filter *dvbdmxfilter;
-
- if (mutex_lock_interruptible(&dvbdemux->mutex))
- return -ERESTARTSYS;
-
- dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux);
- if (!dvbdmxfilter) {
- mutex_unlock(&dvbdemux->mutex);
- return -EBUSY;
- }
-
- spin_lock_irq(&dvbdemux->lock);
- *filter = &dvbdmxfilter->filter;
- (*filter)->parent = feed;
- (*filter)->priv = NULL;
- dvbdmxfilter->feed = dvbdmxfeed;
- dvbdmxfilter->type = DMX_TYPE_SEC;
- dvbdmxfilter->state = DMX_STATE_READY;
- dvbdmxfilter->next = dvbdmxfeed->filter;
- dvbdmxfeed->filter = dvbdmxfilter;
- spin_unlock_irq(&dvbdemux->lock);
-
- mutex_unlock(&dvbdemux->mutex);
- return 0;
-}
-
-static int dmx_section_feed_set(struct dmx_section_feed *feed,
- u16 pid, size_t circular_buffer_size,
- int check_crc)
-{
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
-
- if (pid > 0x1fff)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&dvbdmx->mutex))
- return -ERESTARTSYS;
-
- dvb_demux_feed_add(dvbdmxfeed);
-
- dvbdmxfeed->pid = pid;
- dvbdmxfeed->buffer_size = circular_buffer_size;
- dvbdmxfeed->feed.sec.check_crc = check_crc;
-
-#ifdef NOBUFS
- dvbdmxfeed->buffer = NULL;
-#else
- dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size);
- if (!dvbdmxfeed->buffer) {
- mutex_unlock(&dvbdmx->mutex);
- return -ENOMEM;
- }
-#endif
-
- dvbdmxfeed->state = DMX_STATE_READY;
- mutex_unlock(&dvbdmx->mutex);
- return 0;
-}
-
-static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
-{
- int i;
- struct dvb_demux_filter *f;
- struct dmx_section_filter *sf;
- u8 mask, mode, doneq;
-
- if (!(f = dvbdmxfeed->filter))
- return;
- do {
- sf = &f->filter;
- doneq = 0;
- for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) {
- mode = sf->filter_mode[i];
- mask = sf->filter_mask[i];
- f->maskandmode[i] = mask & mode;
- doneq |= f->maskandnotmode[i] = mask & ~mode;
- }
- f->doneq = doneq ? 1 : 0;
- } while ((f = f->next));
-}
-
-static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
-{
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- int ret;
-
- if (mutex_lock_interruptible(&dvbdmx->mutex))
- return -ERESTARTSYS;
-
- if (feed->is_filtering) {
- mutex_unlock(&dvbdmx->mutex);
- return -EBUSY;
- }
-
- if (!dvbdmxfeed->filter) {
- mutex_unlock(&dvbdmx->mutex);
- return -EINVAL;
- }
-
- dvbdmxfeed->feed.sec.tsfeedp = 0;
- dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
- dvbdmxfeed->feed.sec.secbufp = 0;
- dvbdmxfeed->feed.sec.seclen = 0;
-
- if (!dvbdmx->start_feed) {
- mutex_unlock(&dvbdmx->mutex);
- return -ENODEV;
- }
-
- prepare_secfilters(dvbdmxfeed);
-
- if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
- mutex_unlock(&dvbdmx->mutex);
- return ret;
- }
-
- spin_lock_irq(&dvbdmx->lock);
- feed->is_filtering = 1;
- dvbdmxfeed->state = DMX_STATE_GO;
- spin_unlock_irq(&dvbdmx->lock);
-
- mutex_unlock(&dvbdmx->mutex);
- return 0;
-}
-
-static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed)
-{
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- int ret;
-
- mutex_lock(&dvbdmx->mutex);
-
- if (!dvbdmx->stop_feed) {
- mutex_unlock(&dvbdmx->mutex);
- return -ENODEV;
- }
-
- ret = dvbdmx->stop_feed(dvbdmxfeed);
-
- spin_lock_irq(&dvbdmx->lock);
- dvbdmxfeed->state = DMX_STATE_READY;
- feed->is_filtering = 0;
- spin_unlock_irq(&dvbdmx->lock);
-
- mutex_unlock(&dvbdmx->mutex);
- return ret;
-}
-
-static int dmx_section_feed_release_filter(struct dmx_section_feed *feed,
- struct dmx_section_filter *filter)
-{
- struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f;
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
-
- mutex_lock(&dvbdmx->mutex);
-
- if (dvbdmxfilter->feed != dvbdmxfeed) {
- mutex_unlock(&dvbdmx->mutex);
- return -EINVAL;
- }
-
- if (feed->is_filtering)
- feed->stop_filtering(feed);
-
- spin_lock_irq(&dvbdmx->lock);
- f = dvbdmxfeed->filter;
-
- if (f == dvbdmxfilter) {
- dvbdmxfeed->filter = dvbdmxfilter->next;
- } else {
- while (f->next != dvbdmxfilter)
- f = f->next;
- f->next = f->next->next;
- }
-
- dvbdmxfilter->state = DMX_STATE_FREE;
- spin_unlock_irq(&dvbdmx->lock);
- mutex_unlock(&dvbdmx->mutex);
- return 0;
-}
-
-static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
- struct dmx_section_feed **feed,
- dmx_section_cb callback)
-{
- struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
- struct dvb_demux_feed *dvbdmxfeed;
-
- if (mutex_lock_interruptible(&dvbdmx->mutex))
- return -ERESTARTSYS;
-
- if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
- mutex_unlock(&dvbdmx->mutex);
- return -EBUSY;
- }
-
- dvbdmxfeed->type = DMX_TYPE_SEC;
- dvbdmxfeed->cb.sec = callback;
- dvbdmxfeed->demux = dvbdmx;
- dvbdmxfeed->pid = 0xffff;
- dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base;
- dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0;
- dvbdmxfeed->feed.sec.tsfeedp = 0;
- dvbdmxfeed->filter = NULL;
- dvbdmxfeed->buffer = NULL;
-
- (*feed) = &dvbdmxfeed->feed.sec;
- (*feed)->is_filtering = 0;
- (*feed)->parent = demux;
- (*feed)->priv = NULL;
-
- (*feed)->set = dmx_section_feed_set;
- (*feed)->allocate_filter = dmx_section_feed_allocate_filter;
- (*feed)->start_filtering = dmx_section_feed_start_filtering;
- (*feed)->stop_filtering = dmx_section_feed_stop_filtering;
- (*feed)->release_filter = dmx_section_feed_release_filter;
-
- mutex_unlock(&dvbdmx->mutex);
- return 0;
-}
-
-static int dvbdmx_release_section_feed(struct dmx_demux *demux,
- struct dmx_section_feed *feed)
-{
- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed;
- struct dvb_demux *dvbdmx = (struct dvb_demux *)demux;
-
- mutex_lock(&dvbdmx->mutex);
-
- if (dvbdmxfeed->state == DMX_STATE_FREE) {
- mutex_unlock(&dvbdmx->mutex);
- return -EINVAL;
- }
-#ifndef NOBUFS
- vfree(dvbdmxfeed->buffer);
- dvbdmxfeed->buffer = NULL;
-#endif
- dvbdmxfeed->state = DMX_STATE_FREE;
-
- dvb_demux_feed_del(dvbdmxfeed);
-
- dvbdmxfeed->pid = 0xffff;
-
- mutex_unlock(&dvbdmx->mutex);
- return 0;
-}
-
-/******************************************************************************
- * dvb_demux kernel data API calls
- ******************************************************************************/
-
-static int dvbdmx_open(struct dmx_demux *demux)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- if (dvbdemux->users >= MAX_DVB_DEMUX_USERS)
- return -EUSERS;
-
- dvbdemux->users++;
- return 0;
-}
-
-static int dvbdmx_close(struct dmx_demux *demux)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- if (dvbdemux->users == 0)
- return -ENODEV;
-
- dvbdemux->users--;
- //FIXME: release any unneeded resources if users==0
- return 0;
-}
-
-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- void *p;
-
- if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
- return -EINVAL;
-
- p = memdup_user(buf, count);
- if (IS_ERR(p))
- return PTR_ERR(p);
- if (mutex_lock_interruptible(&dvbdemux->mutex)) {
- kfree(p);
- return -ERESTARTSYS;
- }
- dvb_dmx_swfilter(dvbdemux, p, count);
- kfree(p);
- mutex_unlock(&dvbdemux->mutex);
-
- if (signal_pending(current))
- return -EINTR;
- return count;
-}
-
-static int dvbdmx_add_frontend(struct dmx_demux *demux,
- struct dmx_frontend *frontend)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- struct list_head *head = &dvbdemux->frontend_list;
-
- list_add(&(frontend->connectivity_list), head);
-
- return 0;
-}
-
-static int dvbdmx_remove_frontend(struct dmx_demux *demux,
- struct dmx_frontend *frontend)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
- struct list_head *pos, *n, *head = &dvbdemux->frontend_list;
-
- list_for_each_safe(pos, n, head) {
- if (DMX_FE_ENTRY(pos) == frontend) {
- list_del(pos);
- return 0;
- }
- }
-
- return -ENODEV;
-}
-
-static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- if (list_empty(&dvbdemux->frontend_list))
- return NULL;
-
- return &dvbdemux->frontend_list;
-}
-
-static int dvbdmx_connect_frontend(struct dmx_demux *demux,
- struct dmx_frontend *frontend)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- if (demux->frontend)
- return -EINVAL;
-
- mutex_lock(&dvbdemux->mutex);
-
- demux->frontend = frontend;
- mutex_unlock(&dvbdemux->mutex);
- return 0;
-}
-
-static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- mutex_lock(&dvbdemux->mutex);
-
- demux->frontend = NULL;
- mutex_unlock(&dvbdemux->mutex);
- return 0;
-}
-
-static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
-{
- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
-
- memcpy(pids, dvbdemux->pids, 5 * sizeof(u16));
- return 0;
-}
-
-int dvb_dmx_init(struct dvb_demux *dvbdemux)
-{
- int i;
- struct dmx_demux *dmx = &dvbdemux->dmx;
-
- dvbdemux->cnt_storage = NULL;
- dvbdemux->users = 0;
- dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter));
-
- if (!dvbdemux->filter)
- return -ENOMEM;
-
- dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed));
- if (!dvbdemux->feed) {
- vfree(dvbdemux->filter);
- dvbdemux->filter = NULL;
- return -ENOMEM;
- }
- for (i = 0; i < dvbdemux->filternum; i++) {
- dvbdemux->filter[i].state = DMX_STATE_FREE;
- dvbdemux->filter[i].index = i;
- }
- for (i = 0; i < dvbdemux->feednum; i++) {
- dvbdemux->feed[i].state = DMX_STATE_FREE;
- dvbdemux->feed[i].index = i;
- }
-
- dvbdemux->cnt_storage = vmalloc(MAX_PID + 1);
- if (!dvbdemux->cnt_storage)
- printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n");
-
- INIT_LIST_HEAD(&dvbdemux->frontend_list);
-
- for (i = 0; i < DMX_TS_PES_OTHER; i++) {
- dvbdemux->pesfilter[i] = NULL;
- dvbdemux->pids[i] = 0xffff;
- }
-
- INIT_LIST_HEAD(&dvbdemux->feed_list);
-
- dvbdemux->playing = 0;
- dvbdemux->recording = 0;
- dvbdemux->tsbufp = 0;
-
- if (!dvbdemux->check_crc32)
- dvbdemux->check_crc32 = dvb_dmx_crc32;
-
- if (!dvbdemux->memcopy)
- dvbdemux->memcopy = dvb_dmx_memcopy;
-
- dmx->frontend = NULL;
- dmx->priv = dvbdemux;
- dmx->open = dvbdmx_open;
- dmx->close = dvbdmx_close;
- dmx->write = dvbdmx_write;
- dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed;
- dmx->release_ts_feed = dvbdmx_release_ts_feed;
- dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
- dmx->release_section_feed = dvbdmx_release_section_feed;
-
- dmx->add_frontend = dvbdmx_add_frontend;
- dmx->remove_frontend = dvbdmx_remove_frontend;
- dmx->get_frontends = dvbdmx_get_frontends;
- dmx->connect_frontend = dvbdmx_connect_frontend;
- dmx->disconnect_frontend = dvbdmx_disconnect_frontend;
- dmx->get_pes_pids = dvbdmx_get_pes_pids;
-
- mutex_init(&dvbdemux->mutex);
- spin_lock_init(&dvbdemux->lock);
-
- return 0;
-}
-
-EXPORT_SYMBOL(dvb_dmx_init);
-
-void dvb_dmx_release(struct dvb_demux *dvbdemux)
-{
- vfree(dvbdemux->cnt_storage);
- vfree(dvbdemux->filter);
- vfree(dvbdemux->feed);
-}
-
-EXPORT_SYMBOL(dvb_dmx_release);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
deleted file mode 100644
index aebcdf221dda..000000000000
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ /dev/null
@@ -1,2563 +0,0 @@
-/*
- * dvb_frontend.c: DVB frontend tuning interface/thread
- *
- *
- * Copyright (C) 1999-2001 Ralph Metzler
- * Marcus Metzler
- * Holger Waechtler
- * for convergence integrated media GmbH
- *
- * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-/* Enables DVBv3 compatibility bits at the headers */
-#define __DVB_CORE__
-
-#include <linux/string.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/semaphore.h>
-#include <linux/module.h>
-#include <linux/list.h>
-#include <linux/freezer.h>
-#include <linux/jiffies.h>
-#include <linux/kthread.h>
-#include <asm/processor.h>
-
-#include "dvb_frontend.h"
-#include "dvbdev.h"
-#include <linux/dvb/version.h>
-
-static int dvb_frontend_debug;
-static int dvb_shutdown_timeout;
-static int dvb_force_auto_inversion;
-static int dvb_override_tune_delay;
-static int dvb_powerdown_on_sleep = 1;
-static int dvb_mfe_wait_time = 5;
-
-module_param_named(frontend_debug, dvb_frontend_debug, int, 0644);
-MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off).");
-module_param(dvb_shutdown_timeout, int, 0644);
-MODULE_PARM_DESC(dvb_shutdown_timeout, "wait <shutdown_timeout> seconds after close() before suspending hardware");
-module_param(dvb_force_auto_inversion, int, 0644);
-MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always");
-module_param(dvb_override_tune_delay, int, 0644);
-MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt");
-module_param(dvb_powerdown_on_sleep, int, 0644);
-MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)");
-module_param(dvb_mfe_wait_time, int, 0644);
-MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to <mfe_wait_time> seconds on open() for multi-frontend to become available (default:5 seconds)");
-
-#define dprintk if (dvb_frontend_debug) printk
-
-#define FESTATE_IDLE 1
-#define FESTATE_RETUNE 2
-#define FESTATE_TUNING_FAST 4
-#define FESTATE_TUNING_SLOW 8
-#define FESTATE_TUNED 16
-#define FESTATE_ZIGZAG_FAST 32
-#define FESTATE_ZIGZAG_SLOW 64
-#define FESTATE_DISEQC 128
-#define FESTATE_ERROR 256
-#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC)
-#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST)
-#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW)
-#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW)
-
-#define FE_ALGO_HW 1
-/*
- * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling.
- * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune.
- * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress.
- * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower.
- * FESTATE_TUNED. The frontend has successfully locked on.
- * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it.
- * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower.
- * FESTATE_DISEQC. A DISEQC command has just been issued.
- * FESTATE_WAITFORLOCK. When we're waiting for a lock.
- * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan.
- * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan.
- * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again.
- */
-
-#define DVB_FE_NO_EXIT 0
-#define DVB_FE_NORMAL_EXIT 1
-#define DVB_FE_DEVICE_REMOVED 2
-
-static DEFINE_MUTEX(frontend_mutex);
-
-struct dvb_frontend_private {
-
- /* thread/frontend values */
- struct dvb_device *dvbdev;
- struct dvb_frontend_parameters parameters_out;
- struct dvb_fe_events events;
- struct semaphore sem;
- struct list_head list_head;
- wait_queue_head_t wait_queue;
- struct task_struct *thread;
- unsigned long release_jiffies;
- unsigned int exit;
- unsigned int wakeup;
- fe_status_t status;
- unsigned long tune_mode_flags;
- unsigned int delay;
- unsigned int reinitialise;
- int tone;
- int voltage;
-
- /* swzigzag values */
- unsigned int state;
- unsigned int bending;
- int lnb_drift;
- unsigned int inversion;
- unsigned int auto_step;
- unsigned int auto_sub_step;
- unsigned int started_auto_step;
- unsigned int min_delay;
- unsigned int max_drift;
- unsigned int step_size;
- int quality;
- unsigned int check_wrapped;
- enum dvbfe_search algo_status;
-};
-
-static void dvb_frontend_wakeup(struct dvb_frontend *fe);
-static int dtv_get_frontend(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p_out);
-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p);
-
-static bool has_get_frontend(struct dvb_frontend *fe)
-{
- return fe->ops.get_frontend != NULL;
-}
-
-/*
- * Due to DVBv3 API calls, a delivery system should be mapped into one of
- * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC),
- * otherwise, a DVBv3 call will fail.
- */
-enum dvbv3_emulation_type {
- DVBV3_UNKNOWN,
- DVBV3_QPSK,
- DVBV3_QAM,
- DVBV3_OFDM,
- DVBV3_ATSC,
-};
-
-static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
-{
- switch (delivery_system) {
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- return DVBV3_QAM;
- case SYS_DVBS:
- case SYS_DVBS2:
- case SYS_TURBO:
- case SYS_ISDBS:
- case SYS_DSS:
- return DVBV3_QPSK;
- case SYS_DVBT:
- case SYS_DVBT2:
- case SYS_ISDBT:
- case SYS_DMBTH:
- return DVBV3_OFDM;
- case SYS_ATSC:
- case SYS_ATSCMH:
- case SYS_DVBC_ANNEX_B:
- return DVBV3_ATSC;
- case SYS_UNDEFINED:
- case SYS_ISDBC:
- case SYS_DVBH:
- case SYS_DAB:
- default:
- /*
- * Doesn't know how to emulate those types and/or
- * there's no frontend driver from this type yet
- * with some emulation code, so, we're not sure yet how
- * to handle them, or they're not compatible with a DVBv3 call.
- */
- return DVBV3_UNKNOWN;
- }
-}
-
-static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dvb_fe_events *events = &fepriv->events;
- struct dvb_frontend_event *e;
- int wp;
-
- dprintk ("%s\n", __func__);
-
- if ((status & FE_HAS_LOCK) && has_get_frontend(fe))
- dtv_get_frontend(fe, &fepriv->parameters_out);
-
- mutex_lock(&events->mtx);
-
- wp = (events->eventw + 1) % MAX_EVENT;
- if (wp == events->eventr) {
- events->overflow = 1;
- events->eventr = (events->eventr + 1) % MAX_EVENT;
- }
-
- e = &events->events[events->eventw];
- e->status = status;
- e->parameters = fepriv->parameters_out;
-
- events->eventw = wp;
-
- mutex_unlock(&events->mtx);
-
- wake_up_interruptible (&events->wait_queue);
-}
-
-static int dvb_frontend_get_event(struct dvb_frontend *fe,
- struct dvb_frontend_event *event, int flags)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dvb_fe_events *events = &fepriv->events;
-
- dprintk ("%s\n", __func__);
-
- if (events->overflow) {
- events->overflow = 0;
- return -EOVERFLOW;
- }
-
- if (events->eventw == events->eventr) {
- int ret;
-
- if (flags & O_NONBLOCK)
- return -EWOULDBLOCK;
-
- up(&fepriv->sem);
-
- ret = wait_event_interruptible (events->wait_queue,
- events->eventw != events->eventr);
-
- if (down_interruptible (&fepriv->sem))
- return -ERESTARTSYS;
-
- if (ret < 0)
- return ret;
- }
-
- mutex_lock(&events->mtx);
- *event = events->events[events->eventr];
- events->eventr = (events->eventr + 1) % MAX_EVENT;
- mutex_unlock(&events->mtx);
-
- return 0;
-}
-
-static void dvb_frontend_clear_events(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dvb_fe_events *events = &fepriv->events;
-
- mutex_lock(&events->mtx);
- events->eventr = events->eventw;
- mutex_unlock(&events->mtx);
-}
-
-static void dvb_frontend_init(struct dvb_frontend *fe)
-{
- dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n",
- fe->dvb->num,
- fe->id,
- fe->ops.info.name);
-
- if (fe->ops.init)
- fe->ops.init(fe);
- if (fe->ops.tuner_ops.init) {
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- fe->ops.tuner_ops.init(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-}
-
-void dvb_frontend_reinitialise(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- fepriv->reinitialise = 1;
- dvb_frontend_wakeup(fe);
-}
-EXPORT_SYMBOL(dvb_frontend_reinitialise);
-
-static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked)
-{
- int q2;
-
- dprintk ("%s\n", __func__);
-
- if (locked)
- (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256;
- else
- (fepriv->quality) = (fepriv->quality * 220 + 0) / 256;
-
- q2 = fepriv->quality - 128;
- q2 *= q2;
-
- fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128);
-}
-
-/**
- * Performs automatic twiddling of frontend parameters.
- *
- * @param fe The frontend concerned.
- * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT
- * @returns Number of complete iterations that have been performed.
- */
-static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped)
-{
- int autoinversion;
- int ready = 0;
- int fe_set_err = 0;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
- int original_inversion = c->inversion;
- u32 original_frequency = c->frequency;
-
- /* are we using autoinversion? */
- autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
- (c->inversion == INVERSION_AUTO));
-
- /* setup parameters correctly */
- while(!ready) {
- /* calculate the lnb_drift */
- fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size;
-
- /* wrap the auto_step if we've exceeded the maximum drift */
- if (fepriv->lnb_drift > fepriv->max_drift) {
- fepriv->auto_step = 0;
- fepriv->auto_sub_step = 0;
- fepriv->lnb_drift = 0;
- }
-
- /* perform inversion and +/- zigzag */
- switch(fepriv->auto_sub_step) {
- case 0:
- /* try with the current inversion and current drift setting */
- ready = 1;
- break;
-
- case 1:
- if (!autoinversion) break;
-
- fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
- ready = 1;
- break;
-
- case 2:
- if (fepriv->lnb_drift == 0) break;
-
- fepriv->lnb_drift = -fepriv->lnb_drift;
- ready = 1;
- break;
-
- case 3:
- if (fepriv->lnb_drift == 0) break;
- if (!autoinversion) break;
-
- fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF;
- fepriv->lnb_drift = -fepriv->lnb_drift;
- ready = 1;
- break;
-
- default:
- fepriv->auto_step++;
- fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */
- break;
- }
-
- if (!ready) fepriv->auto_sub_step++;
- }
-
- /* if this attempt would hit where we started, indicate a complete
- * iteration has occurred */
- if ((fepriv->auto_step == fepriv->started_auto_step) &&
- (fepriv->auto_sub_step == 0) && check_wrapped) {
- return 1;
- }
-
- dprintk("%s: drift:%i inversion:%i auto_step:%i "
- "auto_sub_step:%i started_auto_step:%i\n",
- __func__, fepriv->lnb_drift, fepriv->inversion,
- fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step);
-
- /* set the frontend itself */
- c->frequency += fepriv->lnb_drift;
- if (autoinversion)
- c->inversion = fepriv->inversion;
- tmp = *c;
- if (fe->ops.set_frontend)
- fe_set_err = fe->ops.set_frontend(fe);
- *c = tmp;
- if (fe_set_err < 0) {
- fepriv->state = FESTATE_ERROR;
- return fe_set_err;
- }
-
- c->frequency = original_frequency;
- c->inversion = original_inversion;
-
- fepriv->auto_sub_step++;
- return 0;
-}
-
-static void dvb_frontend_swzigzag(struct dvb_frontend *fe)
-{
- fe_status_t s = 0;
- int retval = 0;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp;
-
- /* if we've got no parameters, just keep idling */
- if (fepriv->state & FESTATE_IDLE) {
- fepriv->delay = 3*HZ;
- fepriv->quality = 0;
- return;
- }
-
- /* in SCAN mode, we just set the frontend when asked and leave it alone */
- if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) {
- if (fepriv->state & FESTATE_RETUNE) {
- tmp = *c;
- if (fe->ops.set_frontend)
- retval = fe->ops.set_frontend(fe);
- *c = tmp;
- if (retval < 0)
- fepriv->state = FESTATE_ERROR;
- else
- fepriv->state = FESTATE_TUNED;
- }
- fepriv->delay = 3*HZ;
- fepriv->quality = 0;
- return;
- }
-
- /* get the frontend status */
- if (fepriv->state & FESTATE_RETUNE) {
- s = 0;
- } else {
- if (fe->ops.read_status)
- fe->ops.read_status(fe, &s);
- if (s != fepriv->status) {
- dvb_frontend_add_event(fe, s);
- fepriv->status = s;
- }
- }
-
- /* if we're not tuned, and we have a lock, move to the TUNED state */
- if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) {
- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
- fepriv->state = FESTATE_TUNED;
-
- /* if we're tuned, then we have determined the correct inversion */
- if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) &&
- (c->inversion == INVERSION_AUTO)) {
- c->inversion = fepriv->inversion;
- }
- return;
- }
-
- /* if we are tuned already, check we're still locked */
- if (fepriv->state & FESTATE_TUNED) {
- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
-
- /* we're tuned, and the lock is still good... */
- if (s & FE_HAS_LOCK) {
- return;
- } else { /* if we _WERE_ tuned, but now don't have a lock */
- fepriv->state = FESTATE_ZIGZAG_FAST;
- fepriv->started_auto_step = fepriv->auto_step;
- fepriv->check_wrapped = 0;
- }
- }
-
- /* don't actually do anything if we're in the LOSTLOCK state,
- * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */
- if ((fepriv->state & FESTATE_LOSTLOCK) &&
- (fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) {
- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
- return;
- }
-
- /* don't do anything if we're in the DISEQC state, since this
- * might be someone with a motorized dish controlled by DISEQC.
- * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */
- if (fepriv->state & FESTATE_DISEQC) {
- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
- return;
- }
-
- /* if we're in the RETUNE state, set everything up for a brand
- * new scan, keeping the current inversion setting, as the next
- * tune is _very_ likely to require the same */
- if (fepriv->state & FESTATE_RETUNE) {
- fepriv->lnb_drift = 0;
- fepriv->auto_step = 0;
- fepriv->auto_sub_step = 0;
- fepriv->started_auto_step = 0;
- fepriv->check_wrapped = 0;
- }
-
- /* fast zigzag. */
- if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) {
- fepriv->delay = fepriv->min_delay;
-
- /* perform a tune */
- retval = dvb_frontend_swzigzag_autotune(fe,
- fepriv->check_wrapped);
- if (retval < 0) {
- return;
- } else if (retval) {
- /* OK, if we've run out of trials at the fast speed.
- * Drop back to slow for the _next_ attempt */
- fepriv->state = FESTATE_SEARCHING_SLOW;
- fepriv->started_auto_step = fepriv->auto_step;
- return;
- }
- fepriv->check_wrapped = 1;
-
- /* if we've just retuned, enter the ZIGZAG_FAST state.
- * This ensures we cannot return from an
- * FE_SET_FRONTEND ioctl before the first frontend tune
- * occurs */
- if (fepriv->state & FESTATE_RETUNE) {
- fepriv->state = FESTATE_TUNING_FAST;
- }
- }
-
- /* slow zigzag */
- if (fepriv->state & FESTATE_SEARCHING_SLOW) {
- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK);
-
- /* Note: don't bother checking for wrapping; we stay in this
- * state until we get a lock */
- dvb_frontend_swzigzag_autotune(fe, 0);
- }
-}
-
-static int dvb_frontend_is_exiting(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- if (fepriv->exit != DVB_FE_NO_EXIT)
- return 1;
-
- if (fepriv->dvbdev->writers == 1)
- if (time_after_eq(jiffies, fepriv->release_jiffies +
- dvb_shutdown_timeout * HZ))
- return 1;
-
- return 0;
-}
-
-static int dvb_frontend_should_wakeup(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- if (fepriv->wakeup) {
- fepriv->wakeup = 0;
- return 1;
- }
- return dvb_frontend_is_exiting(fe);
-}
-
-static void dvb_frontend_wakeup(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- fepriv->wakeup = 1;
- wake_up_interruptible(&fepriv->wait_queue);
-}
-
-static int dvb_frontend_thread(void *data)
-{
- struct dvb_frontend *fe = data;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- fe_status_t s;
- enum dvbfe_algo algo;
-
- bool re_tune = false;
-
- dprintk("%s\n", __func__);
-
- fepriv->check_wrapped = 0;
- fepriv->quality = 0;
- fepriv->delay = 3*HZ;
- fepriv->status = 0;
- fepriv->wakeup = 0;
- fepriv->reinitialise = 0;
-
- dvb_frontend_init(fe);
-
- set_freezable();
- while (1) {
- up(&fepriv->sem); /* is locked when we enter the thread... */
-restart:
- wait_event_interruptible_timeout(fepriv->wait_queue,
- dvb_frontend_should_wakeup(fe) || kthread_should_stop()
- || freezing(current),
- fepriv->delay);
-
- if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) {
- /* got signal or quitting */
- fepriv->exit = DVB_FE_NORMAL_EXIT;
- break;
- }
-
- if (try_to_freeze())
- goto restart;
-
- if (down_interruptible(&fepriv->sem))
- break;
-
- if (fepriv->reinitialise) {
- dvb_frontend_init(fe);
- if (fe->ops.set_tone && fepriv->tone != -1)
- fe->ops.set_tone(fe, fepriv->tone);
- if (fe->ops.set_voltage && fepriv->voltage != -1)
- fe->ops.set_voltage(fe, fepriv->voltage);
- fepriv->reinitialise = 0;
- }
-
- /* do an iteration of the tuning loop */
- if (fe->ops.get_frontend_algo) {
- algo = fe->ops.get_frontend_algo(fe);
- switch (algo) {
- case DVBFE_ALGO_HW:
- dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__);
-
- if (fepriv->state & FESTATE_RETUNE) {
- dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
- re_tune = true;
- fepriv->state = FESTATE_TUNED;
- } else {
- re_tune = false;
- }
-
- if (fe->ops.tune)
- fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s);
-
- if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) {
- dprintk("%s: state changed, adding current state\n", __func__);
- dvb_frontend_add_event(fe, s);
- fepriv->status = s;
- }
- break;
- case DVBFE_ALGO_SW:
- dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__);
- dvb_frontend_swzigzag(fe);
- break;
- case DVBFE_ALGO_CUSTOM:
- dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state);
- if (fepriv->state & FESTATE_RETUNE) {
- dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__);
- fepriv->state = FESTATE_TUNED;
- }
- /* Case where we are going to search for a carrier
- * User asked us to retune again for some reason, possibly
- * requesting a search with a new set of parameters
- */
- if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) {
- if (fe->ops.search) {
- fepriv->algo_status = fe->ops.search(fe);
- /* We did do a search as was requested, the flags are
- * now unset as well and has the flags wrt to search.
- */
- } else {
- fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN;
- }
- }
- /* Track the carrier if the search was successful */
- if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) {
- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
- fepriv->delay = HZ / 2;
- }
- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
- fe->ops.read_status(fe, &s);
- if (s != fepriv->status) {
- dvb_frontend_add_event(fe, s); /* update event list */
- fepriv->status = s;
- if (!(s & FE_HAS_LOCK)) {
- fepriv->delay = HZ / 10;
- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
- } else {
- fepriv->delay = 60 * HZ;
- }
- }
- break;
- default:
- dprintk("%s: UNDEFINED ALGO !\n", __func__);
- break;
- }
- } else {
- dvb_frontend_swzigzag(fe);
- }
- }
-
- if (dvb_powerdown_on_sleep) {
- if (fe->ops.set_voltage)
- fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF);
- if (fe->ops.tuner_ops.sleep) {
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- fe->ops.tuner_ops.sleep(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- if (fe->ops.sleep)
- fe->ops.sleep(fe);
- }
-
- fepriv->thread = NULL;
- if (kthread_should_stop())
- fepriv->exit = DVB_FE_DEVICE_REMOVED;
- else
- fepriv->exit = DVB_FE_NO_EXIT;
- mb();
-
- dvb_frontend_wakeup(fe);
- return 0;
-}
-
-static void dvb_frontend_stop(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- dprintk ("%s\n", __func__);
-
- fepriv->exit = DVB_FE_NORMAL_EXIT;
- mb();
-
- if (!fepriv->thread)
- return;
-
- kthread_stop(fepriv->thread);
-
- sema_init(&fepriv->sem, 1);
- fepriv->state = FESTATE_IDLE;
-
- /* paranoia check in case a signal arrived */
- if (fepriv->thread)
- printk("dvb_frontend_stop: warning: thread %p won't exit\n",
- fepriv->thread);
-}
-
-s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime)
-{
- return ((curtime.tv_usec < lasttime.tv_usec) ?
- 1000000 - lasttime.tv_usec + curtime.tv_usec :
- curtime.tv_usec - lasttime.tv_usec);
-}
-EXPORT_SYMBOL(timeval_usec_diff);
-
-static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec)
-{
- curtime->tv_usec += add_usec;
- if (curtime->tv_usec >= 1000000) {
- curtime->tv_usec -= 1000000;
- curtime->tv_sec++;
- }
-}
-
-/*
- * Sleep until gettimeofday() > waketime + add_usec
- * This needs to be as precise as possible, but as the delay is
- * usually between 2ms and 32ms, it is done using a scheduled msleep
- * followed by usleep (normally a busy-wait loop) for the remainder
- */
-void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec)
-{
- struct timeval lasttime;
- s32 delta, newdelta;
-
- timeval_usec_add(waketime, add_usec);
-
- do_gettimeofday(&lasttime);
- delta = timeval_usec_diff(lasttime, *waketime);
- if (delta > 2500) {
- msleep((delta - 1500) / 1000);
- do_gettimeofday(&lasttime);
- newdelta = timeval_usec_diff(lasttime, *waketime);
- delta = (newdelta > delta) ? 0 : newdelta;
- }
- if (delta > 0)
- udelay(delta);
-}
-EXPORT_SYMBOL(dvb_frontend_sleep_until);
-
-static int dvb_frontend_start(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct task_struct *fe_thread;
-
- dprintk ("%s\n", __func__);
-
- if (fepriv->thread) {
- if (fepriv->exit == DVB_FE_NO_EXIT)
- return 0;
- else
- dvb_frontend_stop (fe);
- }
-
- if (signal_pending(current))
- return -EINTR;
- if (down_interruptible (&fepriv->sem))
- return -EINTR;
-
- fepriv->state = FESTATE_IDLE;
- fepriv->exit = DVB_FE_NO_EXIT;
- fepriv->thread = NULL;
- mb();
-
- fe_thread = kthread_run(dvb_frontend_thread, fe,
- "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id);
- if (IS_ERR(fe_thread)) {
- ret = PTR_ERR(fe_thread);
- printk("dvb_frontend_start: failed to start kthread (%d)\n", ret);
- up(&fepriv->sem);
- return ret;
- }
- fepriv->thread = fe_thread;
- return 0;
-}
-
-static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe,
- u32 *freq_min, u32 *freq_max)
-{
- *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min);
-
- if (fe->ops.info.frequency_max == 0)
- *freq_max = fe->ops.tuner_ops.info.frequency_max;
- else if (fe->ops.tuner_ops.info.frequency_max == 0)
- *freq_max = fe->ops.info.frequency_max;
- else
- *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max);
-
- if (*freq_min == 0 || *freq_max == 0)
- printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n",
- fe->dvb->num,fe->id);
-}
-
-static int dvb_frontend_check_parameters(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 freq_min;
- u32 freq_max;
-
- /* range check: frequency */
- dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max);
- if ((freq_min && c->frequency < freq_min) ||
- (freq_max && c->frequency > freq_max)) {
- printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n",
- fe->dvb->num, fe->id, c->frequency, freq_min, freq_max);
- return -EINVAL;
- }
-
- /* range check: symbol rate */
- switch (c->delivery_system) {
- case SYS_DVBS:
- case SYS_DVBS2:
- case SYS_TURBO:
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- if ((fe->ops.info.symbol_rate_min &&
- c->symbol_rate < fe->ops.info.symbol_rate_min) ||
- (fe->ops.info.symbol_rate_max &&
- c->symbol_rate > fe->ops.info.symbol_rate_max)) {
- printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n",
- fe->dvb->num, fe->id, c->symbol_rate,
- fe->ops.info.symbol_rate_min,
- fe->ops.info.symbol_rate_max);
- return -EINVAL;
- }
- default:
- break;
- }
-
- return 0;
-}
-
-static int dvb_frontend_clear_cache(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int i;
- u32 delsys;
-
- delsys = c->delivery_system;
- memset(c, 0, sizeof(struct dtv_frontend_properties));
- c->delivery_system = delsys;
-
- c->state = DTV_CLEAR;
-
- dprintk("%s() Clearing cache for delivery system %d\n", __func__,
- c->delivery_system);
-
- c->transmission_mode = TRANSMISSION_MODE_AUTO;
- c->bandwidth_hz = 0; /* AUTO */
- c->guard_interval = GUARD_INTERVAL_AUTO;
- c->hierarchy = HIERARCHY_AUTO;
- c->symbol_rate = 0;
- c->code_rate_HP = FEC_AUTO;
- c->code_rate_LP = FEC_AUTO;
- c->fec_inner = FEC_AUTO;
- c->rolloff = ROLLOFF_AUTO;
- c->voltage = SEC_VOLTAGE_OFF;
- c->sectone = SEC_TONE_OFF;
- c->pilot = PILOT_AUTO;
-
- c->isdbt_partial_reception = 0;
- c->isdbt_sb_mode = 0;
- c->isdbt_sb_subchannel = 0;
- c->isdbt_sb_segment_idx = 0;
- c->isdbt_sb_segment_count = 0;
- c->isdbt_layer_enabled = 0;
- for (i = 0; i < 3; i++) {
- c->layer[i].fec = FEC_AUTO;
- c->layer[i].modulation = QAM_AUTO;
- c->layer[i].interleaving = 0;
- c->layer[i].segment_count = 0;
- }
-
- c->isdbs_ts_id = 0;
- c->dvbt2_plp_id = 0;
-
- switch (c->delivery_system) {
- case SYS_DVBS:
- case SYS_DVBS2:
- case SYS_TURBO:
- c->modulation = QPSK; /* implied for DVB-S in legacy API */
- c->rolloff = ROLLOFF_35;/* implied for DVB-S */
- break;
- case SYS_ATSC:
- c->modulation = VSB_8;
- break;
- default:
- c->modulation = QAM_AUTO;
- break;
- }
-
- return 0;
-}
-
-#define _DTV_CMD(n, s, b) \
-[n] = { \
- .name = #n, \
- .cmd = n, \
- .set = s,\
- .buffer = b \
-}
-
-static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
- _DTV_CMD(DTV_TUNE, 1, 0),
- _DTV_CMD(DTV_CLEAR, 1, 0),
-
- /* Set */
- _DTV_CMD(DTV_FREQUENCY, 1, 0),
- _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0),
- _DTV_CMD(DTV_MODULATION, 1, 0),
- _DTV_CMD(DTV_INVERSION, 1, 0),
- _DTV_CMD(DTV_DISEQC_MASTER, 1, 1),
- _DTV_CMD(DTV_SYMBOL_RATE, 1, 0),
- _DTV_CMD(DTV_INNER_FEC, 1, 0),
- _DTV_CMD(DTV_VOLTAGE, 1, 0),
- _DTV_CMD(DTV_TONE, 1, 0),
- _DTV_CMD(DTV_PILOT, 1, 0),
- _DTV_CMD(DTV_ROLLOFF, 1, 0),
- _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0),
- _DTV_CMD(DTV_HIERARCHY, 1, 0),
- _DTV_CMD(DTV_CODE_RATE_HP, 1, 0),
- _DTV_CMD(DTV_CODE_RATE_LP, 1, 0),
- _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0),
- _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0),
-
- _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0),
- _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0),
- _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0),
- _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0),
- _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0),
- _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0),
-
- _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0),
- _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0),
-
- /* Get */
- _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1),
- _DTV_CMD(DTV_API_VERSION, 0, 0),
- _DTV_CMD(DTV_CODE_RATE_HP, 0, 0),
- _DTV_CMD(DTV_CODE_RATE_LP, 0, 0),
- _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0),
- _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0),
- _DTV_CMD(DTV_HIERARCHY, 0, 0),
-
- _DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
-
- _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0),
- _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
-
- _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
- _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0),
- _DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
- _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
- _DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
- _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
- _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0),
- _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
- _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
- _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
-};
-
-static void dtv_property_dump(struct dtv_property *tvp)
-{
- int i;
-
- if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) {
- printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n",
- __func__, tvp->cmd);
- return;
- }
-
- dprintk("%s() tvp.cmd = 0x%08x (%s)\n"
- ,__func__
- ,tvp->cmd
- ,dtv_cmds[ tvp->cmd ].name);
-
- if(dtv_cmds[ tvp->cmd ].buffer) {
-
- dprintk("%s() tvp.u.buffer.len = 0x%02x\n"
- ,__func__
- ,tvp->u.buffer.len);
-
- for(i = 0; i < tvp->u.buffer.len; i++)
- dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n"
- ,__func__
- ,i
- ,tvp->u.buffer.data[i]);
-
- } else
- dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data);
-}
-
-/* Synchronise the legacy tuning parameters into the cache, so that demodulator
- * drivers can use a single set_frontend tuning function, regardless of whether
- * it's being used for the legacy or new API, reducing code and complexity.
- */
-static int dtv_property_cache_sync(struct dvb_frontend *fe,
- struct dtv_frontend_properties *c,
- const struct dvb_frontend_parameters *p)
-{
- c->frequency = p->frequency;
- c->inversion = p->inversion;
-
- switch (dvbv3_type(c->delivery_system)) {
- case DVBV3_QPSK:
- dprintk("%s() Preparing QPSK req\n", __func__);
- c->symbol_rate = p->u.qpsk.symbol_rate;
- c->fec_inner = p->u.qpsk.fec_inner;
- break;
- case DVBV3_QAM:
- dprintk("%s() Preparing QAM req\n", __func__);
- c->symbol_rate = p->u.qam.symbol_rate;
- c->fec_inner = p->u.qam.fec_inner;
- c->modulation = p->u.qam.modulation;
- break;
- case DVBV3_OFDM:
- dprintk("%s() Preparing OFDM req\n", __func__);
- switch (p->u.ofdm.bandwidth) {
- case BANDWIDTH_10_MHZ:
- c->bandwidth_hz = 10000000;
- break;
- case BANDWIDTH_8_MHZ:
- c->bandwidth_hz = 8000000;
- break;
- case BANDWIDTH_7_MHZ:
- c->bandwidth_hz = 7000000;
- break;
- case BANDWIDTH_6_MHZ:
- c->bandwidth_hz = 6000000;
- break;
- case BANDWIDTH_5_MHZ:
- c->bandwidth_hz = 5000000;
- break;
- case BANDWIDTH_1_712_MHZ:
- c->bandwidth_hz = 1712000;
- break;
- case BANDWIDTH_AUTO:
- c->bandwidth_hz = 0;
- }
-
- c->code_rate_HP = p->u.ofdm.code_rate_HP;
- c->code_rate_LP = p->u.ofdm.code_rate_LP;
- c->modulation = p->u.ofdm.constellation;
- c->transmission_mode = p->u.ofdm.transmission_mode;
- c->guard_interval = p->u.ofdm.guard_interval;
- c->hierarchy = p->u.ofdm.hierarchy_information;
- break;
- case DVBV3_ATSC:
- dprintk("%s() Preparing ATSC req\n", __func__);
- c->modulation = p->u.vsb.modulation;
- if (c->delivery_system == SYS_ATSCMH)
- break;
- if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
- c->delivery_system = SYS_ATSC;
- else
- c->delivery_system = SYS_DVBC_ANNEX_B;
- break;
- case DVBV3_UNKNOWN:
- printk(KERN_ERR
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* Ensure the cached values are set correctly in the frontend
- * legacy tuning structures, for the advanced tuning API.
- */
-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p)
-{
- const struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-
- p->frequency = c->frequency;
- p->inversion = c->inversion;
-
- switch (dvbv3_type(c->delivery_system)) {
- case DVBV3_UNKNOWN:
- printk(KERN_ERR
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
- return -EINVAL;
- case DVBV3_QPSK:
- dprintk("%s() Preparing QPSK req\n", __func__);
- p->u.qpsk.symbol_rate = c->symbol_rate;
- p->u.qpsk.fec_inner = c->fec_inner;
- break;
- case DVBV3_QAM:
- dprintk("%s() Preparing QAM req\n", __func__);
- p->u.qam.symbol_rate = c->symbol_rate;
- p->u.qam.fec_inner = c->fec_inner;
- p->u.qam.modulation = c->modulation;
- break;
- case DVBV3_OFDM:
- dprintk("%s() Preparing OFDM req\n", __func__);
-
- switch (c->bandwidth_hz) {
- case 10000000:
- p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ;
- break;
- case 8000000:
- p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ;
- break;
- case 7000000:
- p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ;
- break;
- case 6000000:
- p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ;
- break;
- case 5000000:
- p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ;
- break;
- case 1712000:
- p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ;
- break;
- case 0:
- default:
- p->u.ofdm.bandwidth = BANDWIDTH_AUTO;
- }
- p->u.ofdm.code_rate_HP = c->code_rate_HP;
- p->u.ofdm.code_rate_LP = c->code_rate_LP;
- p->u.ofdm.constellation = c->modulation;
- p->u.ofdm.transmission_mode = c->transmission_mode;
- p->u.ofdm.guard_interval = c->guard_interval;
- p->u.ofdm.hierarchy_information = c->hierarchy;
- break;
- case DVBV3_ATSC:
- dprintk("%s() Preparing VSB req\n", __func__);
- p->u.vsb.modulation = c->modulation;
- break;
- }
- return 0;
-}
-
-/**
- * dtv_get_frontend - calls a callback for retrieving DTV parameters
- * @fe: struct dvb_frontend pointer
- * @c: struct dtv_frontend_properties pointer (DVBv5 cache)
- * @p_out struct dvb_frontend_parameters pointer (DVBv3 FE struct)
- *
- * This routine calls either the DVBv3 or DVBv5 get_frontend call.
- * If c is not null, it will update the DVBv5 cache struct pointed by it.
- * If p_out is not null, it will update the DVBv3 params pointed by it.
- */
-static int dtv_get_frontend(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *p_out)
-{
- int r;
-
- if (fe->ops.get_frontend) {
- r = fe->ops.get_frontend(fe);
- if (unlikely(r < 0))
- return r;
- if (p_out)
- dtv_property_legacy_params_sync(fe, p_out);
- return 0;
- }
-
- /* As everything is in cache, get_frontend fops are always supported */
- return 0;
-}
-
-static int dvb_frontend_ioctl_legacy(struct file *file,
- unsigned int cmd, void *parg);
-static int dvb_frontend_ioctl_properties(struct file *file,
- unsigned int cmd, void *parg);
-
-static int dtv_property_process_get(struct dvb_frontend *fe,
- const struct dtv_frontend_properties *c,
- struct dtv_property *tvp,
- struct file *file)
-{
- int r, ncaps;
-
- switch(tvp->cmd) {
- case DTV_ENUM_DELSYS:
- ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
- tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps];
- ncaps++;
- }
- tvp->u.buffer.len = ncaps;
- break;
- case DTV_FREQUENCY:
- tvp->u.data = c->frequency;
- break;
- case DTV_MODULATION:
- tvp->u.data = c->modulation;
- break;
- case DTV_BANDWIDTH_HZ:
- tvp->u.data = c->bandwidth_hz;
- break;
- case DTV_INVERSION:
- tvp->u.data = c->inversion;
- break;
- case DTV_SYMBOL_RATE:
- tvp->u.data = c->symbol_rate;
- break;
- case DTV_INNER_FEC:
- tvp->u.data = c->fec_inner;
- break;
- case DTV_PILOT:
- tvp->u.data = c->pilot;
- break;
- case DTV_ROLLOFF:
- tvp->u.data = c->rolloff;
- break;
- case DTV_DELIVERY_SYSTEM:
- tvp->u.data = c->delivery_system;
- break;
- case DTV_VOLTAGE:
- tvp->u.data = c->voltage;
- break;
- case DTV_TONE:
- tvp->u.data = c->sectone;
- break;
- case DTV_API_VERSION:
- tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR;
- break;
- case DTV_CODE_RATE_HP:
- tvp->u.data = c->code_rate_HP;
- break;
- case DTV_CODE_RATE_LP:
- tvp->u.data = c->code_rate_LP;
- break;
- case DTV_GUARD_INTERVAL:
- tvp->u.data = c->guard_interval;
- break;
- case DTV_TRANSMISSION_MODE:
- tvp->u.data = c->transmission_mode;
- break;
- case DTV_HIERARCHY:
- tvp->u.data = c->hierarchy;
- break;
-
- /* ISDB-T Support here */
- case DTV_ISDBT_PARTIAL_RECEPTION:
- tvp->u.data = c->isdbt_partial_reception;
- break;
- case DTV_ISDBT_SOUND_BROADCASTING:
- tvp->u.data = c->isdbt_sb_mode;
- break;
- case DTV_ISDBT_SB_SUBCHANNEL_ID:
- tvp->u.data = c->isdbt_sb_subchannel;
- break;
- case DTV_ISDBT_SB_SEGMENT_IDX:
- tvp->u.data = c->isdbt_sb_segment_idx;
- break;
- case DTV_ISDBT_SB_SEGMENT_COUNT:
- tvp->u.data = c->isdbt_sb_segment_count;
- break;
- case DTV_ISDBT_LAYER_ENABLED:
- tvp->u.data = c->isdbt_layer_enabled;
- break;
- case DTV_ISDBT_LAYERA_FEC:
- tvp->u.data = c->layer[0].fec;
- break;
- case DTV_ISDBT_LAYERA_MODULATION:
- tvp->u.data = c->layer[0].modulation;
- break;
- case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
- tvp->u.data = c->layer[0].segment_count;
- break;
- case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
- tvp->u.data = c->layer[0].interleaving;
- break;
- case DTV_ISDBT_LAYERB_FEC:
- tvp->u.data = c->layer[1].fec;
- break;
- case DTV_ISDBT_LAYERB_MODULATION:
- tvp->u.data = c->layer[1].modulation;
- break;
- case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
- tvp->u.data = c->layer[1].segment_count;
- break;
- case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
- tvp->u.data = c->layer[1].interleaving;
- break;
- case DTV_ISDBT_LAYERC_FEC:
- tvp->u.data = c->layer[2].fec;
- break;
- case DTV_ISDBT_LAYERC_MODULATION:
- tvp->u.data = c->layer[2].modulation;
- break;
- case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
- tvp->u.data = c->layer[2].segment_count;
- break;
- case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
- tvp->u.data = c->layer[2].interleaving;
- break;
- case DTV_ISDBS_TS_ID:
- tvp->u.data = c->isdbs_ts_id;
- break;
- case DTV_DVBT2_PLP_ID:
- tvp->u.data = c->dvbt2_plp_id;
- break;
-
- /* ATSC-MH */
- case DTV_ATSCMH_FIC_VER:
- tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
- break;
- case DTV_ATSCMH_PARADE_ID:
- tvp->u.data = fe->dtv_property_cache.atscmh_parade_id;
- break;
- case DTV_ATSCMH_NOG:
- tvp->u.data = fe->dtv_property_cache.atscmh_nog;
- break;
- case DTV_ATSCMH_TNOG:
- tvp->u.data = fe->dtv_property_cache.atscmh_tnog;
- break;
- case DTV_ATSCMH_SGN:
- tvp->u.data = fe->dtv_property_cache.atscmh_sgn;
- break;
- case DTV_ATSCMH_PRC:
- tvp->u.data = fe->dtv_property_cache.atscmh_prc;
- break;
- case DTV_ATSCMH_RS_FRAME_MODE:
- tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode;
- break;
- case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
- tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble;
- break;
- case DTV_ATSCMH_RS_CODE_MODE_PRI:
- tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri;
- break;
- case DTV_ATSCMH_RS_CODE_MODE_SEC:
- tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec;
- break;
- case DTV_ATSCMH_SCCC_BLOCK_MODE:
- tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode;
- break;
- case DTV_ATSCMH_SCCC_CODE_MODE_A:
- tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a;
- break;
- case DTV_ATSCMH_SCCC_CODE_MODE_B:
- tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b;
- break;
- case DTV_ATSCMH_SCCC_CODE_MODE_C:
- tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c;
- break;
- case DTV_ATSCMH_SCCC_CODE_MODE_D:
- tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d;
- break;
-
- default:
- return -EINVAL;
- }
-
- /* Allow the frontend to override outgoing properties */
- if (fe->ops.get_property) {
- r = fe->ops.get_property(fe, tvp);
- if (r < 0)
- return r;
- }
-
- dtv_property_dump(tvp);
-
- return 0;
-}
-
-static int dtv_set_frontend(struct dvb_frontend *fe);
-
-static bool is_dvbv3_delsys(u32 delsys)
-{
- bool status;
-
- status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) ||
- (delsys == SYS_DVBS) || (delsys == SYS_ATSC);
-
- return status;
-}
-
-static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system)
-{
- int ncaps, i;
- u32 delsys = SYS_UNDEFINED;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- enum dvbv3_emulation_type type;
-
- /*
- * It was reported that some old DVBv5 applications were
- * filling delivery_system with SYS_UNDEFINED. If this happens,
- * assume that the application wants to use the first supported
- * delivery system.
- */
- if (c->delivery_system == SYS_UNDEFINED)
- c->delivery_system = fe->ops.delsys[0];
-
- if (desired_system == SYS_UNDEFINED) {
- /*
- * A DVBv3 call doesn't know what's the desired system.
- * Also, DVBv3 applications don't know that ops.info->type
- * could be changed, and they simply dies when it doesn't
- * match.
- * So, don't change the current delivery system, as it
- * may be trying to do the wrong thing, like setting an
- * ISDB-T frontend as DVB-T. Instead, find the closest
- * DVBv3 system that matches the delivery system.
- */
- if (is_dvbv3_delsys(c->delivery_system)) {
- dprintk("%s() Using delivery system to %d\n",
- __func__, c->delivery_system);
- return 0;
- }
- type = dvbv3_type(c->delivery_system);
- switch (type) {
- case DVBV3_QPSK:
- desired_system = SYS_DVBS;
- break;
- case DVBV3_QAM:
- desired_system = SYS_DVBC_ANNEX_A;
- break;
- case DVBV3_ATSC:
- desired_system = SYS_ATSC;
- break;
- case DVBV3_OFDM:
- desired_system = SYS_DVBT;
- break;
- default:
- dprintk("%s(): This frontend doesn't support DVBv3 calls\n",
- __func__);
- return -EINVAL;
- }
- /*
- * Get a delivery system that is compatible with DVBv3
- * NOTE: in order for this to work with softwares like Kaffeine that
- * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to
- * DVB-S, drivers that support both should put the SYS_DVBS entry
- * before the SYS_DVBS2, otherwise it won't switch back to DVB-S.
- * The real fix is that userspace applications should not use DVBv3
- * and not trust on calling FE_SET_FRONTEND to switch the delivery
- * system.
- */
- ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
- if (fe->ops.delsys[ncaps] == desired_system) {
- delsys = desired_system;
- break;
- }
- ncaps++;
- }
- if (delsys == SYS_UNDEFINED) {
- dprintk("%s() Couldn't find a delivery system that matches %d\n",
- __func__, desired_system);
- }
- } else {
- /*
- * This is a DVBv5 call. So, it likely knows the supported
- * delivery systems.
- */
-
- /* Check if the desired delivery system is supported */
- ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
- if (fe->ops.delsys[ncaps] == desired_system) {
- c->delivery_system = desired_system;
- dprintk("%s() Changing delivery system to %d\n",
- __func__, desired_system);
- return 0;
- }
- ncaps++;
- }
- type = dvbv3_type(desired_system);
-
- /*
- * The delivery system is not supported. See if it can be
- * emulated.
- * The emulation only works if the desired system is one of the
- * DVBv3 delivery systems
- */
- if (!is_dvbv3_delsys(desired_system)) {
- dprintk("%s() can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n",
- __func__);
- return -EINVAL;
- }
-
- /*
- * Get the last non-DVBv3 delivery system that has the same type
- * of the desired system
- */
- ncaps = 0;
- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) {
- if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) &&
- !is_dvbv3_delsys(fe->ops.delsys[ncaps]))
- delsys = fe->ops.delsys[ncaps];
- ncaps++;
- }
- /* There's nothing compatible with the desired delivery system */
- if (delsys == SYS_UNDEFINED) {
- dprintk("%s() Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n",
- __func__);
- return -EINVAL;
- }
- }
-
- c->delivery_system = delsys;
-
- /*
- * The DVBv3 or DVBv5 call is requesting a different system. So,
- * emulation is needed.
- *
- * Emulate newer delivery systems like ISDBT, DVBT and DMBTH
- * for older DVBv5 applications. The emulation will try to use
- * the auto mode for most things, and will assume that the desired
- * delivery system is the last one at the ops.delsys[] array
- */
- dprintk("%s() Using delivery system %d emulated as if it were a %d\n",
- __func__, delsys, desired_system);
-
- /*
- * For now, handles ISDB-T calls. More code may be needed here for the
- * other emulated stuff
- */
- if (type == DVBV3_OFDM) {
- if (c->delivery_system == SYS_ISDBT) {
- dprintk("%s() Using defaults for SYS_ISDBT\n",
- __func__);
- if (!c->bandwidth_hz)
- c->bandwidth_hz = 6000000;
-
- c->isdbt_partial_reception = 0;
- c->isdbt_sb_mode = 0;
- c->isdbt_sb_subchannel = 0;
- c->isdbt_sb_segment_idx = 0;
- c->isdbt_sb_segment_count = 0;
- c->isdbt_layer_enabled = 0;
- for (i = 0; i < 3; i++) {
- c->layer[i].fec = FEC_AUTO;
- c->layer[i].modulation = QAM_AUTO;
- c->layer[i].interleaving = 0;
- c->layer[i].segment_count = 0;
- }
- }
- }
- dprintk("change delivery system on cache to %d\n", c->delivery_system);
-
- return 0;
-}
-
-static int dtv_property_process_set(struct dvb_frontend *fe,
- struct dtv_property *tvp,
- struct file *file)
-{
- int r = 0;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
-
- /* Allow the frontend to validate incoming properties */
- if (fe->ops.set_property) {
- r = fe->ops.set_property(fe, tvp);
- if (r < 0)
- return r;
- }
-
- switch(tvp->cmd) {
- case DTV_CLEAR:
- /*
- * Reset a cache of data specific to the frontend here. This does
- * not effect hardware.
- */
- dvb_frontend_clear_cache(fe);
- break;
- case DTV_TUNE:
- /* interpret the cache of data, build either a traditional frontend
- * tunerequest so we can pass validation in the FE_SET_FRONTEND
- * ioctl.
- */
- c->state = tvp->cmd;
- dprintk("%s() Finalised property cache\n", __func__);
-
- r = dtv_set_frontend(fe);
- break;
- case DTV_FREQUENCY:
- c->frequency = tvp->u.data;
- break;
- case DTV_MODULATION:
- c->modulation = tvp->u.data;
- break;
- case DTV_BANDWIDTH_HZ:
- c->bandwidth_hz = tvp->u.data;
- break;
- case DTV_INVERSION:
- c->inversion = tvp->u.data;
- break;
- case DTV_SYMBOL_RATE:
- c->symbol_rate = tvp->u.data;
- break;
- case DTV_INNER_FEC:
- c->fec_inner = tvp->u.data;
- break;
- case DTV_PILOT:
- c->pilot = tvp->u.data;
- break;
- case DTV_ROLLOFF:
- c->rolloff = tvp->u.data;
- break;
- case DTV_DELIVERY_SYSTEM:
- r = set_delivery_system(fe, tvp->u.data);
- break;
- case DTV_VOLTAGE:
- c->voltage = tvp->u.data;
- r = dvb_frontend_ioctl_legacy(file, FE_SET_VOLTAGE,
- (void *)c->voltage);
- break;
- case DTV_TONE:
- c->sectone = tvp->u.data;
- r = dvb_frontend_ioctl_legacy(file, FE_SET_TONE,
- (void *)c->sectone);
- break;
- case DTV_CODE_RATE_HP:
- c->code_rate_HP = tvp->u.data;
- break;
- case DTV_CODE_RATE_LP:
- c->code_rate_LP = tvp->u.data;
- break;
- case DTV_GUARD_INTERVAL:
- c->guard_interval = tvp->u.data;
- break;
- case DTV_TRANSMISSION_MODE:
- c->transmission_mode = tvp->u.data;
- break;
- case DTV_HIERARCHY:
- c->hierarchy = tvp->u.data;
- break;
-
- /* ISDB-T Support here */
- case DTV_ISDBT_PARTIAL_RECEPTION:
- c->isdbt_partial_reception = tvp->u.data;
- break;
- case DTV_ISDBT_SOUND_BROADCASTING:
- c->isdbt_sb_mode = tvp->u.data;
- break;
- case DTV_ISDBT_SB_SUBCHANNEL_ID:
- c->isdbt_sb_subchannel = tvp->u.data;
- break;
- case DTV_ISDBT_SB_SEGMENT_IDX:
- c->isdbt_sb_segment_idx = tvp->u.data;
- break;
- case DTV_ISDBT_SB_SEGMENT_COUNT:
- c->isdbt_sb_segment_count = tvp->u.data;
- break;
- case DTV_ISDBT_LAYER_ENABLED:
- c->isdbt_layer_enabled = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERA_FEC:
- c->layer[0].fec = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERA_MODULATION:
- c->layer[0].modulation = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERA_SEGMENT_COUNT:
- c->layer[0].segment_count = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERA_TIME_INTERLEAVING:
- c->layer[0].interleaving = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERB_FEC:
- c->layer[1].fec = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERB_MODULATION:
- c->layer[1].modulation = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERB_SEGMENT_COUNT:
- c->layer[1].segment_count = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERB_TIME_INTERLEAVING:
- c->layer[1].interleaving = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERC_FEC:
- c->layer[2].fec = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERC_MODULATION:
- c->layer[2].modulation = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERC_SEGMENT_COUNT:
- c->layer[2].segment_count = tvp->u.data;
- break;
- case DTV_ISDBT_LAYERC_TIME_INTERLEAVING:
- c->layer[2].interleaving = tvp->u.data;
- break;
- case DTV_ISDBS_TS_ID:
- c->isdbs_ts_id = tvp->u.data;
- break;
- case DTV_DVBT2_PLP_ID:
- c->dvbt2_plp_id = tvp->u.data;
- break;
-
- /* ATSC-MH */
- case DTV_ATSCMH_PARADE_ID:
- fe->dtv_property_cache.atscmh_parade_id = tvp->u.data;
- break;
- case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
- fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data;
- break;
-
- default:
- return -EINVAL;
- }
-
- return r;
-}
-
-static int dvb_frontend_ioctl(struct file *file,
- unsigned int cmd, void *parg)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- int err = -EOPNOTSUPP;
-
- dprintk("%s (%d)\n", __func__, _IOC_NR(cmd));
-
- if (fepriv->exit != DVB_FE_NO_EXIT)
- return -ENODEV;
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY &&
- (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT ||
- cmd == FE_DISEQC_RECV_SLAVE_REPLY))
- return -EPERM;
-
- if (down_interruptible (&fepriv->sem))
- return -ERESTARTSYS;
-
- if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY))
- err = dvb_frontend_ioctl_properties(file, cmd, parg);
- else {
- c->state = DTV_UNDEFINED;
- err = dvb_frontend_ioctl_legacy(file, cmd, parg);
- }
-
- up(&fepriv->sem);
- return err;
-}
-
-static int dvb_frontend_ioctl_properties(struct file *file,
- unsigned int cmd, void *parg)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int err = 0;
-
- struct dtv_properties *tvps = NULL;
- struct dtv_property *tvp = NULL;
- int i;
-
- dprintk("%s\n", __func__);
-
- if(cmd == FE_SET_PROPERTY) {
- tvps = (struct dtv_properties __user *)parg;
-
- dprintk("%s() properties.num = %d\n", __func__, tvps->num);
- dprintk("%s() properties.props = %p\n", __func__, tvps->props);
-
- /* Put an arbitrary limit on the number of messages that can
- * be sent at once */
- if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
- return -EINVAL;
-
- tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
- if (!tvp) {
- err = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
- err = -EFAULT;
- goto out;
- }
-
- for (i = 0; i < tvps->num; i++) {
- err = dtv_property_process_set(fe, tvp + i, file);
- if (err < 0)
- goto out;
- (tvp + i)->result = err;
- }
-
- if (c->state == DTV_TUNE)
- dprintk("%s() Property cache is full, tuning\n", __func__);
-
- } else
- if(cmd == FE_GET_PROPERTY) {
- tvps = (struct dtv_properties __user *)parg;
-
- dprintk("%s() properties.num = %d\n", __func__, tvps->num);
- dprintk("%s() properties.props = %p\n", __func__, tvps->props);
-
- /* Put an arbitrary limit on the number of messages that can
- * be sent at once */
- if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS))
- return -EINVAL;
-
- tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL);
- if (!tvp) {
- err = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) {
- err = -EFAULT;
- goto out;
- }
-
- /*
- * Fills the cache out struct with the cache contents, plus
- * the data retrieved from get_frontend, if the frontend
- * is not idle. Otherwise, returns the cached content
- */
- if (fepriv->state != FESTATE_IDLE) {
- err = dtv_get_frontend(fe, NULL);
- if (err < 0)
- goto out;
- }
- for (i = 0; i < tvps->num; i++) {
- err = dtv_property_process_get(fe, c, tvp + i, file);
- if (err < 0)
- goto out;
- (tvp + i)->result = err;
- }
-
- if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) {
- err = -EFAULT;
- goto out;
- }
-
- } else
- err = -EOPNOTSUPP;
-
-out:
- kfree(tvp);
- return err;
-}
-
-static int dtv_set_frontend(struct dvb_frontend *fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct dvb_frontend_tune_settings fetunesettings;
- u32 rolloff = 0;
-
- if (dvb_frontend_check_parameters(fe) < 0)
- return -EINVAL;
-
- /*
- * Initialize output parameters to match the values given by
- * the user. FE_SET_FRONTEND triggers an initial frontend event
- * with status = 0, which copies output parameters to userspace.
- */
- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out);
-
- /*
- * Be sure that the bandwidth will be filled for all
- * non-satellite systems, as tuners need to know what
- * low pass/Nyquist half filter should be applied, in
- * order to avoid inter-channel noise.
- *
- * ISDB-T and DVB-T/T2 already sets bandwidth.
- * ATSC and DVB-C don't set, so, the core should fill it.
- *
- * On DVB-C Annex A and C, the bandwidth is a function of
- * the roll-off and symbol rate. Annex B defines different
- * roll-off factors depending on the modulation. Fortunately,
- * Annex B is only used with 6MHz, so there's no need to
- * calculate it.
- *
- * While not officially supported, a side effect of handling it at
- * the cache level is that a program could retrieve the bandwidth
- * via DTV_BANDWIDTH_HZ, which may be useful for test programs.
- */
- switch (c->delivery_system) {
- case SYS_ATSC:
- case SYS_DVBC_ANNEX_B:
- c->bandwidth_hz = 6000000;
- break;
- case SYS_DVBC_ANNEX_A:
- rolloff = 115;
- break;
- case SYS_DVBC_ANNEX_C:
- rolloff = 113;
- break;
- default:
- break;
- }
- if (rolloff)
- c->bandwidth_hz = (c->symbol_rate * rolloff) / 100;
-
- /* force auto frequency inversion if requested */
- if (dvb_force_auto_inversion)
- c->inversion = INVERSION_AUTO;
-
- /*
- * without hierarchical coding code_rate_LP is irrelevant,
- * so we tolerate the otherwise invalid FEC_NONE setting
- */
- if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE)
- c->code_rate_LP = FEC_AUTO;
-
- /* get frontend-specific tuning settings */
- memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings));
- if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) {
- fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000;
- fepriv->max_drift = fetunesettings.max_drift;
- fepriv->step_size = fetunesettings.step_size;
- } else {
- /* default values */
- switch (c->delivery_system) {
- case SYS_DVBS:
- case SYS_DVBS2:
- case SYS_ISDBS:
- case SYS_TURBO:
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- fepriv->min_delay = HZ / 20;
- fepriv->step_size = c->symbol_rate / 16000;
- fepriv->max_drift = c->symbol_rate / 2000;
- break;
- case SYS_DVBT:
- case SYS_DVBT2:
- case SYS_ISDBT:
- case SYS_DMBTH:
- fepriv->min_delay = HZ / 20;
- fepriv->step_size = fe->ops.info.frequency_stepsize * 2;
- fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
- break;
- default:
- /*
- * FIXME: This sounds wrong! if freqency_stepsize is
- * defined by the frontend, why not use it???
- */
- fepriv->min_delay = HZ / 20;
- fepriv->step_size = 0; /* no zigzag */
- fepriv->max_drift = 0;
- break;
- }
- }
- if (dvb_override_tune_delay > 0)
- fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000;
-
- fepriv->state = FESTATE_RETUNE;
-
- /* Request the search algorithm to search */
- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN;
-
- dvb_frontend_clear_events(fe);
- dvb_frontend_add_event(fe, 0);
- dvb_frontend_wakeup(fe);
- fepriv->status = 0;
-
- return 0;
-}
-
-
-static int dvb_frontend_ioctl_legacy(struct file *file,
- unsigned int cmd, void *parg)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int cb_err, err = -EOPNOTSUPP;
-
- if (fe->dvb->fe_ioctl_override) {
- cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
- DVB_FE_IOCTL_PRE);
- if (cb_err < 0)
- return cb_err;
- if (cb_err > 0)
- return 0;
- /* fe_ioctl_override returning 0 allows
- * dvb-core to continue handling the ioctl */
- }
-
- switch (cmd) {
- case FE_GET_INFO: {
- struct dvb_frontend_info* info = parg;
-
- memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info));
- dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max);
-
- /*
- * Associate the 4 delivery systems supported by DVBv3
- * API with their DVBv5 counterpart. For the other standards,
- * use the closest type, assuming that it would hopefully
- * work with a DVBv3 application.
- * It should be noticed that, on multi-frontend devices with
- * different types (terrestrial and cable, for example),
- * a pure DVBv3 application won't be able to use all delivery
- * systems. Yet, changing the DVBv5 cache to the other delivery
- * system should be enough for making it work.
- */
- switch (dvbv3_type(c->delivery_system)) {
- case DVBV3_QPSK:
- info->type = FE_QPSK;
- break;
- case DVBV3_ATSC:
- info->type = FE_ATSC;
- break;
- case DVBV3_QAM:
- info->type = FE_QAM;
- break;
- case DVBV3_OFDM:
- info->type = FE_OFDM;
- break;
- default:
- printk(KERN_ERR
- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n",
- __func__, c->delivery_system);
- fe->ops.info.type = FE_OFDM;
- }
- dprintk("current delivery system on cache: %d, V3 type: %d\n",
- c->delivery_system, fe->ops.info.type);
-
- /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't
- * do it, it is done for it. */
- info->caps |= FE_CAN_INVERSION_AUTO;
- err = 0;
- break;
- }
-
- case FE_READ_STATUS: {
- fe_status_t* status = parg;
-
- /* if retune was requested but hasn't occurred yet, prevent
- * that user get signal state from previous tuning */
- if (fepriv->state == FESTATE_RETUNE ||
- fepriv->state == FESTATE_ERROR) {
- err=0;
- *status = 0;
- break;
- }
-
- if (fe->ops.read_status)
- err = fe->ops.read_status(fe, status);
- break;
- }
- case FE_READ_BER:
- if (fe->ops.read_ber)
- err = fe->ops.read_ber(fe, (__u32*) parg);
- break;
-
- case FE_READ_SIGNAL_STRENGTH:
- if (fe->ops.read_signal_strength)
- err = fe->ops.read_signal_strength(fe, (__u16*) parg);
- break;
-
- case FE_READ_SNR:
- if (fe->ops.read_snr)
- err = fe->ops.read_snr(fe, (__u16*) parg);
- break;
-
- case FE_READ_UNCORRECTED_BLOCKS:
- if (fe->ops.read_ucblocks)
- err = fe->ops.read_ucblocks(fe, (__u32*) parg);
- break;
-
-
- case FE_DISEQC_RESET_OVERLOAD:
- if (fe->ops.diseqc_reset_overload) {
- err = fe->ops.diseqc_reset_overload(fe);
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_DISEQC_SEND_MASTER_CMD:
- if (fe->ops.diseqc_send_master_cmd) {
- err = fe->ops.diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg);
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_DISEQC_SEND_BURST:
- if (fe->ops.diseqc_send_burst) {
- err = fe->ops.diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg);
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_SET_TONE:
- if (fe->ops.set_tone) {
- err = fe->ops.set_tone(fe, (fe_sec_tone_mode_t) parg);
- fepriv->tone = (fe_sec_tone_mode_t) parg;
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_SET_VOLTAGE:
- if (fe->ops.set_voltage) {
- err = fe->ops.set_voltage(fe, (fe_sec_voltage_t) parg);
- fepriv->voltage = (fe_sec_voltage_t) parg;
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_DISHNETWORK_SEND_LEGACY_CMD:
- if (fe->ops.dishnetwork_send_legacy_command) {
- err = fe->ops.dishnetwork_send_legacy_command(fe, (unsigned long) parg);
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- } else if (fe->ops.set_voltage) {
- /*
- * NOTE: This is a fallback condition. Some frontends
- * (stv0299 for instance) take longer than 8msec to
- * respond to a set_voltage command. Those switches
- * need custom routines to switch properly. For all
- * other frontends, the following should work ok.
- * Dish network legacy switches (as used by Dish500)
- * are controlled by sending 9-bit command words
- * spaced 8msec apart.
- * the actual command word is switch/port dependent
- * so it is up to the userspace application to send
- * the right command.
- * The command must always start with a '0' after
- * initialization, so parg is 8 bits and does not
- * include the initialization or start bit
- */
- unsigned long swcmd = ((unsigned long) parg) << 1;
- struct timeval nexttime;
- struct timeval tv[10];
- int i;
- u8 last = 1;
- if (dvb_frontend_debug)
- printk("%s switch command: 0x%04lx\n", __func__, swcmd);
- do_gettimeofday(&nexttime);
- if (dvb_frontend_debug)
- memcpy(&tv[0], &nexttime, sizeof(struct timeval));
- /* before sending a command, initialize by sending
- * a 32ms 18V to the switch
- */
- fe->ops.set_voltage(fe, SEC_VOLTAGE_18);
- dvb_frontend_sleep_until(&nexttime, 32000);
-
- for (i = 0; i < 9; i++) {
- if (dvb_frontend_debug)
- do_gettimeofday(&tv[i + 1]);
- if ((swcmd & 0x01) != last) {
- /* set voltage to (last ? 13V : 18V) */
- fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18);
- last = (last) ? 0 : 1;
- }
- swcmd = swcmd >> 1;
- if (i != 8)
- dvb_frontend_sleep_until(&nexttime, 8000);
- }
- if (dvb_frontend_debug) {
- printk("%s(%d): switch delay (should be 32k followed by all 8k\n",
- __func__, fe->dvb->num);
- for (i = 1; i < 10; i++)
- printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i]));
- }
- err = 0;
- fepriv->state = FESTATE_DISEQC;
- fepriv->status = 0;
- }
- break;
-
- case FE_DISEQC_RECV_SLAVE_REPLY:
- if (fe->ops.diseqc_recv_slave_reply)
- err = fe->ops.diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg);
- break;
-
- case FE_ENABLE_HIGH_LNB_VOLTAGE:
- if (fe->ops.enable_high_lnb_voltage)
- err = fe->ops.enable_high_lnb_voltage(fe, (long) parg);
- break;
-
- case FE_SET_FRONTEND:
- err = set_delivery_system(fe, SYS_UNDEFINED);
- if (err)
- break;
-
- err = dtv_property_cache_sync(fe, c, parg);
- if (err)
- break;
- err = dtv_set_frontend(fe);
- break;
- case FE_GET_EVENT:
- err = dvb_frontend_get_event (fe, parg, file->f_flags);
- break;
-
- case FE_GET_FRONTEND:
- err = dtv_get_frontend(fe, parg);
- break;
-
- case FE_SET_FRONTEND_TUNE_MODE:
- fepriv->tune_mode_flags = (unsigned long) parg;
- err = 0;
- break;
- };
-
- if (fe->dvb->fe_ioctl_override) {
- cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg,
- DVB_FE_IOCTL_POST);
- if (cb_err < 0)
- return cb_err;
- }
-
- return err;
-}
-
-
-static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
-
- dprintk ("%s\n", __func__);
-
- poll_wait (file, &fepriv->events.wait_queue, wait);
-
- if (fepriv->events.eventw != fepriv->events.eventr)
- return (POLLIN | POLLRDNORM | POLLPRI);
-
- return 0;
-}
-
-static int dvb_frontend_open(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- struct dvb_adapter *adapter = fe->dvb;
- int ret;
-
- dprintk ("%s\n", __func__);
- if (fepriv->exit == DVB_FE_DEVICE_REMOVED)
- return -ENODEV;
-
- if (adapter->mfe_shared) {
- mutex_lock (&adapter->mfe_lock);
-
- if (adapter->mfe_dvbdev == NULL)
- adapter->mfe_dvbdev = dvbdev;
-
- else if (adapter->mfe_dvbdev != dvbdev) {
- struct dvb_device
- *mfedev = adapter->mfe_dvbdev;
- struct dvb_frontend
- *mfe = mfedev->priv;
- struct dvb_frontend_private
- *mfepriv = mfe->frontend_priv;
- int mferetry = (dvb_mfe_wait_time << 1);
-
- mutex_unlock (&adapter->mfe_lock);
- while (mferetry-- && (mfedev->users != -1 ||
- mfepriv->thread != NULL)) {
- if(msleep_interruptible(500)) {
- if(signal_pending(current))
- return -EINTR;
- }
- }
-
- mutex_lock (&adapter->mfe_lock);
- if(adapter->mfe_dvbdev != dvbdev) {
- mfedev = adapter->mfe_dvbdev;
- mfe = mfedev->priv;
- mfepriv = mfe->frontend_priv;
- if (mfedev->users != -1 ||
- mfepriv->thread != NULL) {
- mutex_unlock (&adapter->mfe_lock);
- return -EBUSY;
- }
- adapter->mfe_dvbdev = dvbdev;
- }
- }
- }
-
- if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) {
- if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0)
- goto err0;
-
- /* If we took control of the bus, we need to force
- reinitialization. This is because many ts_bus_ctrl()
- functions strobe the RESET pin on the demod, and if the
- frontend thread already exists then the dvb_init() routine
- won't get called (which is what usually does initial
- register configuration). */
- fepriv->reinitialise = 1;
- }
-
- if ((ret = dvb_generic_open (inode, file)) < 0)
- goto err1;
-
- if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
- /* normal tune mode when opened R/W */
- fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT;
- fepriv->tone = -1;
- fepriv->voltage = -1;
-
- ret = dvb_frontend_start (fe);
- if (ret)
- goto err2;
-
- /* empty event queue */
- fepriv->events.eventr = fepriv->events.eventw = 0;
- }
-
- if (adapter->mfe_shared)
- mutex_unlock (&adapter->mfe_lock);
- return ret;
-
-err2:
- dvb_generic_release(inode, file);
-err1:
- if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl)
- fe->ops.ts_bus_ctrl(fe, 0);
-err0:
- if (adapter->mfe_shared)
- mutex_unlock (&adapter->mfe_lock);
- return ret;
-}
-
-static int dvb_frontend_release(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
- struct dvb_frontend *fe = dvbdev->priv;
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- int ret;
-
- dprintk ("%s\n", __func__);
-
- if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
- fepriv->release_jiffies = jiffies;
- mb();
- }
-
- ret = dvb_generic_release (inode, file);
-
- if (dvbdev->users == -1) {
- wake_up(&fepriv->wait_queue);
- if (fepriv->exit != DVB_FE_NO_EXIT) {
- fops_put(file->f_op);
- file->f_op = NULL;
- wake_up(&dvbdev->wait_queue);
- }
- if (fe->ops.ts_bus_ctrl)
- fe->ops.ts_bus_ctrl(fe, 0);
- }
-
- return ret;
-}
-
-static const struct file_operations dvb_frontend_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = dvb_generic_ioctl,
- .poll = dvb_frontend_poll,
- .open = dvb_frontend_open,
- .release = dvb_frontend_release,
- .llseek = noop_llseek,
-};
-
-int dvb_register_frontend(struct dvb_adapter* dvb,
- struct dvb_frontend* fe)
-{
- struct dvb_frontend_private *fepriv;
- static const struct dvb_device dvbdev_template = {
- .users = ~0,
- .writers = 1,
- .readers = (~0)-1,
- .fops = &dvb_frontend_fops,
- .kernel_ioctl = dvb_frontend_ioctl
- };
-
- dprintk ("%s\n", __func__);
-
- if (mutex_lock_interruptible(&frontend_mutex))
- return -ERESTARTSYS;
-
- fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL);
- if (fe->frontend_priv == NULL) {
- mutex_unlock(&frontend_mutex);
- return -ENOMEM;
- }
- fepriv = fe->frontend_priv;
-
- sema_init(&fepriv->sem, 1);
- init_waitqueue_head (&fepriv->wait_queue);
- init_waitqueue_head (&fepriv->events.wait_queue);
- mutex_init(&fepriv->events.mtx);
- fe->dvb = dvb;
- fepriv->inversion = INVERSION_OFF;
-
- printk ("DVB: registering adapter %i frontend %i (%s)...\n",
- fe->dvb->num,
- fe->id,
- fe->ops.info.name);
-
- dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template,
- fe, DVB_DEVICE_FRONTEND);
-
- /*
- * Initialize the cache to the proper values according with the
- * first supported delivery system (ops->delsys[0])
- */
-
- fe->dtv_property_cache.delivery_system = fe->ops.delsys[0];
- dvb_frontend_clear_cache(fe);
-
- mutex_unlock(&frontend_mutex);
- return 0;
-}
-EXPORT_SYMBOL(dvb_register_frontend);
-
-int dvb_unregister_frontend(struct dvb_frontend* fe)
-{
- struct dvb_frontend_private *fepriv = fe->frontend_priv;
- dprintk ("%s\n", __func__);
-
- mutex_lock(&frontend_mutex);
- dvb_frontend_stop (fe);
- mutex_unlock(&frontend_mutex);
-
- if (fepriv->dvbdev->users < -1)
- wait_event(fepriv->dvbdev->wait_queue,
- fepriv->dvbdev->users==-1);
-
- mutex_lock(&frontend_mutex);
- dvb_unregister_device (fepriv->dvbdev);
-
- /* fe is invalid now */
- kfree(fepriv);
- mutex_unlock(&frontend_mutex);
- return 0;
-}
-EXPORT_SYMBOL(dvb_unregister_frontend);
-
-#ifdef CONFIG_MEDIA_ATTACH
-void dvb_frontend_detach(struct dvb_frontend* fe)
-{
- void *ptr;
-
- if (fe->ops.release_sec) {
- fe->ops.release_sec(fe);
- symbol_put_addr(fe->ops.release_sec);
- }
- if (fe->ops.tuner_ops.release) {
- fe->ops.tuner_ops.release(fe);
- symbol_put_addr(fe->ops.tuner_ops.release);
- }
- if (fe->ops.analog_ops.release) {
- fe->ops.analog_ops.release(fe);
- symbol_put_addr(fe->ops.analog_ops.release);
- }
- ptr = (void*)fe->ops.release;
- if (ptr) {
- fe->ops.release(fe);
- symbol_put_addr(ptr);
- }
-}
-#else
-void dvb_frontend_detach(struct dvb_frontend* fe)
-{
- if (fe->ops.release_sec)
- fe->ops.release_sec(fe);
- if (fe->ops.tuner_ops.release)
- fe->ops.tuner_ops.release(fe);
- if (fe->ops.analog_ops.release)
- fe->ops.analog_ops.release(fe);
- if (fe->ops.release)
- fe->ops.release(fe);
-}
-#endif
-EXPORT_SYMBOL(dvb_frontend_detach);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
deleted file mode 100644
index 7c64c09103a9..000000000000
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * dvb_frontend.h
- *
- * Copyright (C) 2001 convergence integrated media GmbH
- * Copyright (C) 2004 convergence GmbH
- *
- * Written by Ralph Metzler
- * Overhauled by Holger Waechtler
- * Kernel I2C stuff by Michael Hunold <hunold@convergence.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
-
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef _DVB_FRONTEND_H_
-#define _DVB_FRONTEND_H_
-
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/ioctl.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include <linux/dvb/frontend.h>
-
-#include "dvbdev.h"
-
-/*
- * Maximum number of Delivery systems per frontend. It
- * should be smaller or equal to 32
- */
-#define MAX_DELSYS 8
-
-struct dvb_frontend_tune_settings {
- int min_delay_ms;
- int step_size;
- int max_drift;
-};
-
-struct dvb_frontend;
-
-struct dvb_tuner_info {
- char name[128];
-
- u32 frequency_min;
- u32 frequency_max;
- u32 frequency_step;
-
- u32 bandwidth_min;
- u32 bandwidth_max;
- u32 bandwidth_step;
-};
-
-struct analog_parameters {
- unsigned int frequency;
- unsigned int mode;
- unsigned int audmode;
- u64 std;
-};
-
-enum dvbfe_modcod {
- DVBFE_MODCOD_DUMMY_PLFRAME = 0,
- DVBFE_MODCOD_QPSK_1_4,
- DVBFE_MODCOD_QPSK_1_3,
- DVBFE_MODCOD_QPSK_2_5,
- DVBFE_MODCOD_QPSK_1_2,
- DVBFE_MODCOD_QPSK_3_5,
- DVBFE_MODCOD_QPSK_2_3,
- DVBFE_MODCOD_QPSK_3_4,
- DVBFE_MODCOD_QPSK_4_5,
- DVBFE_MODCOD_QPSK_5_6,
- DVBFE_MODCOD_QPSK_8_9,
- DVBFE_MODCOD_QPSK_9_10,
- DVBFE_MODCOD_8PSK_3_5,
- DVBFE_MODCOD_8PSK_2_3,
- DVBFE_MODCOD_8PSK_3_4,
- DVBFE_MODCOD_8PSK_5_6,
- DVBFE_MODCOD_8PSK_8_9,
- DVBFE_MODCOD_8PSK_9_10,
- DVBFE_MODCOD_16APSK_2_3,
- DVBFE_MODCOD_16APSK_3_4,
- DVBFE_MODCOD_16APSK_4_5,
- DVBFE_MODCOD_16APSK_5_6,
- DVBFE_MODCOD_16APSK_8_9,
- DVBFE_MODCOD_16APSK_9_10,
- DVBFE_MODCOD_32APSK_3_4,
- DVBFE_MODCOD_32APSK_4_5,
- DVBFE_MODCOD_32APSK_5_6,
- DVBFE_MODCOD_32APSK_8_9,
- DVBFE_MODCOD_32APSK_9_10,
- DVBFE_MODCOD_RESERVED_1,
- DVBFE_MODCOD_BPSK_1_3,
- DVBFE_MODCOD_BPSK_1_4,
- DVBFE_MODCOD_RESERVED_2
-};
-
-enum tuner_param {
- DVBFE_TUNER_FREQUENCY = (1 << 0),
- DVBFE_TUNER_TUNERSTEP = (1 << 1),
- DVBFE_TUNER_IFFREQ = (1 << 2),
- DVBFE_TUNER_BANDWIDTH = (1 << 3),
- DVBFE_TUNER_REFCLOCK = (1 << 4),
- DVBFE_TUNER_IQSENSE = (1 << 5),
- DVBFE_TUNER_DUMMY = (1 << 31)
-};
-
-/*
- * ALGO_HW: (Hardware Algorithm)
- * ----------------------------------------------------------------
- * Devices that support this algorithm do everything in hardware
- * and no software support is needed to handle them.
- * Requesting these devices to LOCK is the only thing required,
- * device is supposed to do everything in the hardware.
- *
- * ALGO_SW: (Software Algorithm)
- * ----------------------------------------------------------------
- * These are dumb devices, that require software to do everything
- *
- * ALGO_CUSTOM: (Customizable Agorithm)
- * ----------------------------------------------------------------
- * Devices having this algorithm can be customized to have specific
- * algorithms in the frontend driver, rather than simply doing a
- * software zig-zag. In this case the zigzag maybe hardware assisted
- * or it maybe completely done in hardware. In all cases, usage of
- * this algorithm, in conjunction with the search and track
- * callbacks, utilizes the driver specific algorithm.
- *
- * ALGO_RECOVERY: (Recovery Algorithm)
- * ----------------------------------------------------------------
- * These devices have AUTO recovery capabilities from LOCK failure
- */
-enum dvbfe_algo {
- DVBFE_ALGO_HW = (1 << 0),
- DVBFE_ALGO_SW = (1 << 1),
- DVBFE_ALGO_CUSTOM = (1 << 2),
- DVBFE_ALGO_RECOVERY = (1 << 31)
-};
-
-struct tuner_state {
- u32 frequency;
- u32 tunerstep;
- u32 ifreq;
- u32 bandwidth;
- u32 iqsense;
- u32 refclock;
-};
-
-/*
- * search callback possible return status
- *
- * DVBFE_ALGO_SEARCH_SUCCESS
- * The frontend search algorithm completed and returned successfully
- *
- * DVBFE_ALGO_SEARCH_ASLEEP
- * The frontend search algorithm is sleeping
- *
- * DVBFE_ALGO_SEARCH_FAILED
- * The frontend search for a signal failed
- *
- * DVBFE_ALGO_SEARCH_INVALID
- * The frontend search algorith was probably supplied with invalid
- * parameters and the search is an invalid one
- *
- * DVBFE_ALGO_SEARCH_ERROR
- * The frontend search algorithm failed due to some error
- *
- * DVBFE_ALGO_SEARCH_AGAIN
- * The frontend search algorithm was requested to search again
- */
-enum dvbfe_search {
- DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0),
- DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1),
- DVBFE_ALGO_SEARCH_FAILED = (1 << 2),
- DVBFE_ALGO_SEARCH_INVALID = (1 << 3),
- DVBFE_ALGO_SEARCH_AGAIN = (1 << 4),
- DVBFE_ALGO_SEARCH_ERROR = (1 << 31),
-};
-
-
-struct dvb_tuner_ops {
-
- struct dvb_tuner_info info;
-
- int (*release)(struct dvb_frontend *fe);
- int (*init)(struct dvb_frontend *fe);
- int (*sleep)(struct dvb_frontend *fe);
-
- /** This is for simple PLLs - set all parameters in one go. */
- int (*set_params)(struct dvb_frontend *fe);
- int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p);
-
- /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */
- int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len);
-
- /** This is to allow setting tuner-specific configs */
- int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
-
- int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency);
- int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth);
- int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency);
-
-#define TUNER_STATUS_LOCKED 1
-#define TUNER_STATUS_STEREO 2
- int (*get_status)(struct dvb_frontend *fe, u32 *status);
- int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength);
- int (*get_afc)(struct dvb_frontend *fe, s32 *afc);
-
- /** These are provided separately from set_params in order to facilitate silicon
- * tuners which require sophisticated tuning loops, controlling each parameter separately. */
- int (*set_frequency)(struct dvb_frontend *fe, u32 frequency);
- int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth);
-
- /*
- * These are provided separately from set_params in order to facilitate silicon
- * tuners which require sophisticated tuning loops, controlling each parameter separately.
- */
- int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
- int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state);
-};
-
-struct analog_demod_info {
- char *name;
-};
-
-struct analog_demod_ops {
-
- struct analog_demod_info info;
-
- void (*set_params)(struct dvb_frontend *fe,
- struct analog_parameters *params);
- int (*has_signal)(struct dvb_frontend *fe);
- int (*get_afc)(struct dvb_frontend *fe);
- void (*tuner_status)(struct dvb_frontend *fe);
- void (*standby)(struct dvb_frontend *fe);
- void (*release)(struct dvb_frontend *fe);
- int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable);
-
- /** This is to allow setting tuner-specific configuration */
- int (*set_config)(struct dvb_frontend *fe, void *priv_cfg);
-};
-
-struct dtv_frontend_properties;
-
-struct dvb_frontend_ops {
-
- struct dvb_frontend_info info;
-
- u8 delsys[MAX_DELSYS];
-
- void (*release)(struct dvb_frontend* fe);
- void (*release_sec)(struct dvb_frontend* fe);
-
- int (*init)(struct dvb_frontend* fe);
- int (*sleep)(struct dvb_frontend* fe);
-
- int (*write)(struct dvb_frontend* fe, const u8 buf[], int len);
-
- /* if this is set, it overrides the default swzigzag */
- int (*tune)(struct dvb_frontend* fe,
- bool re_tune,
- unsigned int mode_flags,
- unsigned int *delay,
- fe_status_t *status);
- /* get frontend tuning algorithm from the module */
- enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe);
-
- /* these two are only used for the swzigzag code */
- int (*set_frontend)(struct dvb_frontend *fe);
- int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings);
-
- int (*get_frontend)(struct dvb_frontend *fe);
-
- int (*read_status)(struct dvb_frontend* fe, fe_status_t* status);
- int (*read_ber)(struct dvb_frontend* fe, u32* ber);
- int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength);
- int (*read_snr)(struct dvb_frontend* fe, u16* snr);
- int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks);
-
- int (*diseqc_reset_overload)(struct dvb_frontend* fe);
- int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd);
- int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply);
- int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd);
- int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone);
- int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage);
- int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg);
- int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd);
- int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable);
- int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire);
-
- /* These callbacks are for devices that implement their own
- * tuning algorithms, rather than a simple swzigzag
- */
- enum dvbfe_search (*search)(struct dvb_frontend *fe);
-
- struct dvb_tuner_ops tuner_ops;
- struct analog_demod_ops analog_ops;
-
- int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
- int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp);
-};
-
-#ifdef __DVB_CORE__
-#define MAX_EVENT 8
-
-struct dvb_fe_events {
- struct dvb_frontend_event events[MAX_EVENT];
- int eventw;
- int eventr;
- int overflow;
- wait_queue_head_t wait_queue;
- struct mutex mtx;
-};
-#endif
-
-struct dtv_frontend_properties {
-
- /* Cache State */
- u32 state;
-
- u32 frequency;
- fe_modulation_t modulation;
-
- fe_sec_voltage_t voltage;
- fe_sec_tone_mode_t sectone;
- fe_spectral_inversion_t inversion;
- fe_code_rate_t fec_inner;
- fe_transmit_mode_t transmission_mode;
- u32 bandwidth_hz; /* 0 = AUTO */
- fe_guard_interval_t guard_interval;
- fe_hierarchy_t hierarchy;
- u32 symbol_rate;
- fe_code_rate_t code_rate_HP;
- fe_code_rate_t code_rate_LP;
-
- fe_pilot_t pilot;
- fe_rolloff_t rolloff;
-
- fe_delivery_system_t delivery_system;
-
- /* ISDB-T specifics */
- u8 isdbt_partial_reception;
- u8 isdbt_sb_mode;
- u8 isdbt_sb_subchannel;
- u32 isdbt_sb_segment_idx;
- u32 isdbt_sb_segment_count;
- u8 isdbt_layer_enabled;
- struct {
- u8 segment_count;
- fe_code_rate_t fec;
- fe_modulation_t modulation;
- u8 interleaving;
- } layer[3];
-
- /* ISDB-T specifics */
- u32 isdbs_ts_id;
-
- /* DVB-T2 specifics */
- u32 dvbt2_plp_id;
-
- /* ATSC-MH specifics */
- u8 atscmh_fic_ver;
- u8 atscmh_parade_id;
- u8 atscmh_nog;
- u8 atscmh_tnog;
- u8 atscmh_sgn;
- u8 atscmh_prc;
-
- u8 atscmh_rs_frame_mode;
- u8 atscmh_rs_frame_ensemble;
- u8 atscmh_rs_code_mode_pri;
- u8 atscmh_rs_code_mode_sec;
- u8 atscmh_sccc_block_mode;
- u8 atscmh_sccc_code_mode_a;
- u8 atscmh_sccc_code_mode_b;
- u8 atscmh_sccc_code_mode_c;
- u8 atscmh_sccc_code_mode_d;
-};
-
-struct dvb_frontend {
- struct dvb_frontend_ops ops;
- struct dvb_adapter *dvb;
- void *demodulator_priv;
- void *tuner_priv;
- void *frontend_priv;
- void *sec_priv;
- void *analog_demod_priv;
- struct dtv_frontend_properties dtv_property_cache;
-#define DVB_FRONTEND_COMPONENT_TUNER 0
-#define DVB_FRONTEND_COMPONENT_DEMOD 1
- int (*callback)(void *adapter_priv, int component, int cmd, int arg);
- int id;
-};
-
-extern int dvb_register_frontend(struct dvb_adapter *dvb,
- struct dvb_frontend *fe);
-
-extern int dvb_unregister_frontend(struct dvb_frontend *fe);
-
-extern void dvb_frontend_detach(struct dvb_frontend *fe);
-
-extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
-
-extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
-extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime);
-
-#endif
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
deleted file mode 100644
index 39eab73b01ae..000000000000
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * dvbdev.c
- *
- * Copyright (C) 2000 Ralph Metzler <ralph@convergence.de>
- * & Marcus Metzler <marcus@convergence.de>
- * for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/cdev.h>
-#include <linux/mutex.h>
-#include "dvbdev.h"
-
-static DEFINE_MUTEX(dvbdev_mutex);
-static int dvbdev_debug;
-
-module_param(dvbdev_debug, int, 0644);
-MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off).");
-
-#define dprintk if (dvbdev_debug) printk
-
-static LIST_HEAD(dvb_adapter_list);
-static DEFINE_MUTEX(dvbdev_register_lock);
-
-static const char * const dnames[] = {
- "video", "audio", "sec", "frontend", "demux", "dvr", "ca",
- "net", "osd"
-};
-
-#ifdef CONFIG_DVB_DYNAMIC_MINORS
-#define MAX_DVB_MINORS 256
-#define DVB_MAX_IDS MAX_DVB_MINORS
-#else
-#define DVB_MAX_IDS 4
-#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
-#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
-#endif
-
-static struct class *dvb_class;
-
-static struct dvb_device *dvb_minors[MAX_DVB_MINORS];
-static DECLARE_RWSEM(minor_rwsem);
-
-static int dvb_device_open(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev;
-
- mutex_lock(&dvbdev_mutex);
- down_read(&minor_rwsem);
- dvbdev = dvb_minors[iminor(inode)];
-
- if (dvbdev && dvbdev->fops) {
- int err = 0;
- const struct file_operations *old_fops;
-
- file->private_data = dvbdev;
- old_fops = file->f_op;
- file->f_op = fops_get(dvbdev->fops);
- if (file->f_op == NULL) {
- file->f_op = old_fops;
- goto fail;
- }
- if(file->f_op->open)
- err = file->f_op->open(inode,file);
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
- up_read(&minor_rwsem);
- mutex_unlock(&dvbdev_mutex);
- return err;
- }
-fail:
- up_read(&minor_rwsem);
- mutex_unlock(&dvbdev_mutex);
- return -ENODEV;
-}
-
-
-static const struct file_operations dvb_device_fops =
-{
- .owner = THIS_MODULE,
- .open = dvb_device_open,
- .llseek = noop_llseek,
-};
-
-static struct cdev dvb_device_cdev;
-
-int dvb_generic_open(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
-
- if (!dvbdev)
- return -ENODEV;
-
- if (!dvbdev->users)
- return -EBUSY;
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- if (!dvbdev->readers)
- return -EBUSY;
- dvbdev->readers--;
- } else {
- if (!dvbdev->writers)
- return -EBUSY;
- dvbdev->writers--;
- }
-
- dvbdev->users--;
- return 0;
-}
-EXPORT_SYMBOL(dvb_generic_open);
-
-
-int dvb_generic_release(struct inode *inode, struct file *file)
-{
- struct dvb_device *dvbdev = file->private_data;
-
- if (!dvbdev)
- return -ENODEV;
-
- if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
- dvbdev->readers++;
- } else {
- dvbdev->writers++;
- }
-
- dvbdev->users++;
- return 0;
-}
-EXPORT_SYMBOL(dvb_generic_release);
-
-
-long dvb_generic_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct dvb_device *dvbdev = file->private_data;
-
- if (!dvbdev)
- return -ENODEV;
-
- if (!dvbdev->kernel_ioctl)
- return -EINVAL;
-
- return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl);
-}
-EXPORT_SYMBOL(dvb_generic_ioctl);
-
-
-static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
-{
- u32 id = 0;
-
- while (id < DVB_MAX_IDS) {
- struct dvb_device *dev;
- list_for_each_entry(dev, &adap->device_list, list_head)
- if (dev->type == type && dev->id == id)
- goto skip;
- return id;
-skip:
- id++;
- }
- return -ENFILE;
-}
-
-
-int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
- const struct dvb_device *template, void *priv, int type)
-{
- struct dvb_device *dvbdev;
- struct file_operations *dvbdevfops;
- struct device *clsdev;
- int minor;
- int id;
-
- mutex_lock(&dvbdev_register_lock);
-
- if ((id = dvbdev_get_free_id (adap, type)) < 0){
- mutex_unlock(&dvbdev_register_lock);
- *pdvbdev = NULL;
- printk(KERN_ERR "%s: couldn't find free device id\n", __func__);
- return -ENFILE;
- }
-
- *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL);
-
- if (!dvbdev){
- mutex_unlock(&dvbdev_register_lock);
- return -ENOMEM;
- }
-
- dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
-
- if (!dvbdevfops){
- kfree (dvbdev);
- mutex_unlock(&dvbdev_register_lock);
- return -ENOMEM;
- }
-
- memcpy(dvbdev, template, sizeof(struct dvb_device));
- dvbdev->type = type;
- dvbdev->id = id;
- dvbdev->adapter = adap;
- dvbdev->priv = priv;
- dvbdev->fops = dvbdevfops;
- init_waitqueue_head (&dvbdev->wait_queue);
-
- memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
- dvbdevfops->owner = adap->module;
-
- list_add_tail (&dvbdev->list_head, &adap->device_list);
-
- down_write(&minor_rwsem);
-#ifdef CONFIG_DVB_DYNAMIC_MINORS
- for (minor = 0; minor < MAX_DVB_MINORS; minor++)
- if (dvb_minors[minor] == NULL)
- break;
-
- if (minor == MAX_DVB_MINORS) {
- kfree(dvbdevfops);
- kfree(dvbdev);
- up_write(&minor_rwsem);
- mutex_unlock(&dvbdev_register_lock);
- return -EINVAL;
- }
-#else
- minor = nums2minor(adap->num, type, id);
-#endif
-
- dvbdev->minor = minor;
- dvb_minors[minor] = dvbdev;
- up_write(&minor_rwsem);
-
- mutex_unlock(&dvbdev_register_lock);
-
- clsdev = device_create(dvb_class, adap->device,
- MKDEV(DVB_MAJOR, minor),
- dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
- if (IS_ERR(clsdev)) {
- printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
- __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
- return PTR_ERR(clsdev);
- }
-
- dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n",
- adap->num, dnames[type], id, minor, minor);
-
- return 0;
-}
-EXPORT_SYMBOL(dvb_register_device);
-
-
-void dvb_unregister_device(struct dvb_device *dvbdev)
-{
- if (!dvbdev)
- return;
-
- down_write(&minor_rwsem);
- dvb_minors[dvbdev->minor] = NULL;
- up_write(&minor_rwsem);
-
- device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor));
-
- list_del (&dvbdev->list_head);
- kfree (dvbdev->fops);
- kfree (dvbdev);
-}
-EXPORT_SYMBOL(dvb_unregister_device);
-
-static int dvbdev_check_free_adapter_num(int num)
-{
- struct list_head *entry;
- list_for_each(entry, &dvb_adapter_list) {
- struct dvb_adapter *adap;
- adap = list_entry(entry, struct dvb_adapter, list_head);
- if (adap->num == num)
- return 0;
- }
- return 1;
-}
-
-static int dvbdev_get_free_adapter_num (void)
-{
- int num = 0;
-
- while (num < DVB_MAX_ADAPTERS) {
- if (dvbdev_check_free_adapter_num(num))
- return num;
- num++;
- }
-
- return -ENFILE;
-}
-
-
-int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
- struct module *module, struct device *device,
- short *adapter_nums)
-{
- int i, num;
-
- mutex_lock(&dvbdev_register_lock);
-
- for (i = 0; i < DVB_MAX_ADAPTERS; ++i) {
- num = adapter_nums[i];
- if (num >= 0 && num < DVB_MAX_ADAPTERS) {
- /* use the one the driver asked for */
- if (dvbdev_check_free_adapter_num(num))
- break;
- } else {
- num = dvbdev_get_free_adapter_num();
- break;
- }
- num = -1;
- }
-
- if (num < 0) {
- mutex_unlock(&dvbdev_register_lock);
- return -ENFILE;
- }
-
- memset (adap, 0, sizeof(struct dvb_adapter));
- INIT_LIST_HEAD (&adap->device_list);
-
- printk(KERN_INFO "DVB: registering new adapter (%s)\n", name);
-
- adap->num = num;
- adap->name = name;
- adap->module = module;
- adap->device = device;
- adap->mfe_shared = 0;
- adap->mfe_dvbdev = NULL;
- mutex_init (&adap->mfe_lock);
-
- list_add_tail (&adap->list_head, &dvb_adapter_list);
-
- mutex_unlock(&dvbdev_register_lock);
-
- return num;
-}
-EXPORT_SYMBOL(dvb_register_adapter);
-
-
-int dvb_unregister_adapter(struct dvb_adapter *adap)
-{
- mutex_lock(&dvbdev_register_lock);
- list_del (&adap->list_head);
- mutex_unlock(&dvbdev_register_lock);
- return 0;
-}
-EXPORT_SYMBOL(dvb_unregister_adapter);
-
-/* if the miracle happens and "generic_usercopy()" is included into
- the kernel, then this can vanish. please don't make the mistake and
- define this as video_usercopy(). this will introduce a dependecy
- to the v4l "videodev.o" module, which is unnecessary for some
- cards (ie. the budget dvb-cards don't need the v4l module...) */
-int dvb_usercopy(struct file *file,
- unsigned int cmd, unsigned long arg,
- int (*func)(struct file *file,
- unsigned int cmd, void *arg))
-{
- char sbuf[128];
- void *mbuf = NULL;
- void *parg = NULL;
- int err = -EINVAL;
-
- /* Copy arguments into temp kernel buffer */
- switch (_IOC_DIR(cmd)) {
- case _IOC_NONE:
- /*
- * For this command, the pointer is actually an integer
- * argument.
- */
- parg = (void *) arg;
- break;
- case _IOC_READ: /* some v4l ioctls are marked wrong ... */
- case _IOC_WRITE:
- case (_IOC_WRITE | _IOC_READ):
- if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
- parg = sbuf;
- } else {
- /* too big to allocate from stack */
- mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
- if (NULL == mbuf)
- return -ENOMEM;
- parg = mbuf;
- }
-
- err = -EFAULT;
- if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
- goto out;
- break;
- }
-
- /* call driver */
- mutex_lock(&dvbdev_mutex);
- if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
- err = -EINVAL;
- mutex_unlock(&dvbdev_mutex);
-
- if (err < 0)
- goto out;
-
- /* Copy results into user buffer */
- switch (_IOC_DIR(cmd))
- {
- case _IOC_READ:
- case (_IOC_WRITE | _IOC_READ):
- if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
- err = -EFAULT;
- break;
- }
-
-out:
- kfree(mbuf);
- return err;
-}
-
-static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- struct dvb_device *dvbdev = dev_get_drvdata(dev);
-
- add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num);
- add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]);
- add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id);
- return 0;
-}
-
-static char *dvb_devnode(struct device *dev, umode_t *mode)
-{
- struct dvb_device *dvbdev = dev_get_drvdata(dev);
-
- return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d",
- dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id);
-}
-
-
-static int __init init_dvbdev(void)
-{
- int retval;
- dev_t dev = MKDEV(DVB_MAJOR, 0);
-
- if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) {
- printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR);
- return retval;
- }
-
- cdev_init(&dvb_device_cdev, &dvb_device_fops);
- if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) {
- printk(KERN_ERR "dvb-core: unable register character device\n");
- goto error;
- }
-
- dvb_class = class_create(THIS_MODULE, "dvb");
- if (IS_ERR(dvb_class)) {
- retval = PTR_ERR(dvb_class);
- goto error;
- }
- dvb_class->dev_uevent = dvb_uevent;
- dvb_class->devnode = dvb_devnode;
- return 0;
-
-error:
- cdev_del(&dvb_device_cdev);
- unregister_chrdev_region(dev, MAX_DVB_MINORS);
- return retval;
-}
-
-
-static void __exit exit_dvbdev(void)
-{
- class_destroy(dvb_class);
- cdev_del(&dvb_device_cdev);
- unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS);
-}
-
-subsys_initcall(init_dvbdev);
-module_exit(exit_dvbdev);
-
-MODULE_DESCRIPTION("DVB Core Driver");
-MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
deleted file mode 100644
index fcc6ae98745e..000000000000
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * dvbdev.h
- *
- * Copyright (C) 2000 Ralph Metzler & Marcus Metzler
- * for convergence integrated media GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Lesser Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef _DVBDEV_H_
-#define _DVBDEV_H_
-
-#include <linux/types.h>
-#include <linux/poll.h>
-#include <linux/fs.h>
-#include <linux/list.h>
-
-#define DVB_MAJOR 212
-
-#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0
- #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS
-#else
- #define DVB_MAX_ADAPTERS 8
-#endif
-
-#define DVB_UNSET (-1)
-
-#define DVB_DEVICE_VIDEO 0
-#define DVB_DEVICE_AUDIO 1
-#define DVB_DEVICE_SEC 2
-#define DVB_DEVICE_FRONTEND 3
-#define DVB_DEVICE_DEMUX 4
-#define DVB_DEVICE_DVR 5
-#define DVB_DEVICE_CA 6
-#define DVB_DEVICE_NET 7
-#define DVB_DEVICE_OSD 8
-
-#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \
- static short adapter_nr[] = \
- {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \
- module_param_array(adapter_nr, short, NULL, 0444); \
- MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers")
-
-struct dvb_frontend;
-
-struct dvb_adapter {
- int num;
- struct list_head list_head;
- struct list_head device_list;
- const char *name;
- u8 proposed_mac [6];
- void* priv;
-
- struct device *device;
-
- struct module *module;
-
- int mfe_shared; /* indicates mutually exclusive frontends */
- struct dvb_device *mfe_dvbdev; /* frontend device in use */
- struct mutex mfe_lock; /* access lock for thread creation */
-
- /* Allow the adapter/bridge driver to perform an action before and/or
- * after the core handles an ioctl:
- *
- * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled.
- * DVB_FE_IOCTL_POST indicates that the ioctl has been handled.
- *
- * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg:
- *
- * return 0 to allow dvb-core to handle the ioctl.
- * return a positive int to prevent dvb-core from handling the ioctl,
- * and exit without error.
- * return a negative int to prevent dvb-core from handling the ioctl,
- * and return that value as an error.
- *
- * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg:
- *
- * return 0 to allow the dvb_frontend ioctl handler to exit normally.
- * return a negative int to cause the dvb_frontend ioctl handler to
- * return that value as an error.
- */
-#define DVB_FE_IOCTL_PRE 0
-#define DVB_FE_IOCTL_POST 1
- int (*fe_ioctl_override)(struct dvb_frontend *fe,
- unsigned int cmd, void *parg,
- unsigned int stage);
-};
-
-
-struct dvb_device {
- struct list_head list_head;
- const struct file_operations *fops;
- struct dvb_adapter *adapter;
- int type;
- int minor;
- u32 id;
-
- /* in theory, 'users' can vanish now,
- but I don't want to change too much now... */
- int readers;
- int writers;
- int users;
-
- wait_queue_head_t wait_queue;
- /* don't really need those !? -- FIXME: use video_usercopy */
- int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg);
-
- void *priv;
-};
-
-
-extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name,
- struct module *module, struct device *device,
- short *adapter_nums);
-extern int dvb_unregister_adapter (struct dvb_adapter *adap);
-
-extern int dvb_register_device (struct dvb_adapter *adap,
- struct dvb_device **pdvbdev,
- const struct dvb_device *template,
- void *priv,
- int type);
-
-extern void dvb_unregister_device (struct dvb_device *dvbdev);
-
-extern int dvb_generic_open (struct inode *inode, struct file *file);
-extern int dvb_generic_release (struct inode *inode, struct file *file);
-extern long dvb_generic_ioctl (struct file *file,
- unsigned int cmd, unsigned long arg);
-
-/* we don't mess with video_usercopy() any more,
-we simply define out own dvb_usercopy(), which will hopefully become
-generic_usercopy() someday... */
-
-extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
- int (*func)(struct file *file, unsigned int cmd, void *arg));
-
-/** generic DVB attach function. */
-#ifdef CONFIG_MEDIA_ATTACH
-#define dvb_attach(FUNCTION, ARGS...) ({ \
- void *__r = NULL; \
- typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
- if (__a) { \
- __r = (void *) __a(ARGS); \
- if (__r == NULL) \
- symbol_put(FUNCTION); \
- } else { \
- printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
- } \
- __r; \
-})
-
-#else
-#define dvb_attach(FUNCTION, ARGS...) ({ \
- FUNCTION(ARGS); \
-})
-
-#endif
-
-#endif /* #ifndef _DVBDEV_H_ */
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
deleted file mode 100644
index c2161565023a..000000000000
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ /dev/null
@@ -1,440 +0,0 @@
-config DVB_USB
- tristate "Support for various USB DVB devices"
- depends on DVB_CORE && USB && I2C && RC_CORE
- help
- By enabling this you will be able to choose the various supported
- USB1.1 and USB2.0 DVB devices.
-
- Almost every USB device needs a firmware, please look into
- <file:Documentation/dvb/README.dvb-usb>.
-
- For a complete list of supported USB devices see the LinuxTV DVB Wiki:
- <http://www.linuxtv.org/wiki/index.php/DVB_USB>
-
- Say Y if you own a USB DVB device.
-
-config DVB_USB_DEBUG
- bool "Enable extended debug support for all DVB-USB devices"
- depends on DVB_USB
- help
- Say Y if you want to enable debugging. See modinfo dvb-usb (and the
- appropriate drivers) for debug levels.
-
-config DVB_USB_A800
- tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
- depends on DVB_USB
- select DVB_DIB3000MC
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
-
-config DVB_USB_DIBUSB_MB
- tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_DIB3000MB
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- help
- Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
- DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
-
- For an up-to-date list of devices supported by this driver, have a look
- on the Linux-DVB Wiki at www.linuxtv.org.
-
- Say Y if you own such a device and want to use it. You should build it as
- a module.
-
-config DVB_USB_DIBUSB_MB_FAULTY
- bool "Support faulty USB IDs"
- depends on DVB_USB_DIBUSB_MB
- help
- Support for faulty USB IDs due to an invalid EEPROM on some Artec devices.
-
-config DVB_USB_DIBUSB_MC
- tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
- depends on DVB_USB
- select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- help
- Support for USB2.0 DVB-T receivers based on reference designs made by
- DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
-
- For an up-to-date list of devices supported by this driver, have a look
- on the Linux-DVB Wiki at www.linuxtv.org.
-
- Say Y if you own such a device and want to use it. You should build it as
- a module.
-
-config DVB_USB_DIB0700
- tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
- depends on DVB_USB
- select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select DVB_DIB7000M if !DVB_FE_CUSTOMISE
- select DVB_DIB8000 if !DVB_FE_CUSTOMISE
- select DVB_DIB3000MC if !DVB_FE_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
- select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
- select DVB_TUNER_DIB0090 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
- help
- Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
- USB bridge is also present in devices having the DiB7700 DVB-T-USB
- silicon. This chip can be found in devices offered by Hauppauge,
- Avermedia and other big and small companies.
-
- For an up-to-date list of devices supported by this driver, have a look
- on the LinuxTV Wiki at www.linuxtv.org.
-
- Say Y if you own such a device and want to use it. You should build it as
- a module.
-
-config DVB_USB_UMT_010
- tristate "HanfTek UMT-010 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_DIB3000MC
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
-
-config DVB_USB_CXUSB
- tristate "Conexant USB2.0 hybrid reference design support"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_CX22702 if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
- select DVB_ATBM8830 if !DVB_FE_CUSTOMISE
- select DVB_LGS8GXX if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Conexant USB2.0 hybrid reference design.
- Currently, only DVB and ATSC modes are supported, analog mode
- shall be added in the future. Devices that require this module:
-
- Medion MD95700 hybrid USB2.0 device.
- DViCO FusionHDTV (Bluebird) USB2.0 devices
-
-config DVB_USB_M920X
- tristate "Uli m920x DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
- Currently, only devices with a product id of
- "DTV USB MINI" (in cold state) are supported.
- Firmware required.
-
-config DVB_USB_GL861
- tristate "Genesys Logic GL861 USB2.0 support"
- depends on DVB_USB
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
- receiver with USB ID 0db0:5581.
-
-config DVB_USB_AU6610
- tristate "Alcor Micro AU6610 USB2.0 support"
- depends on DVB_USB
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
-
-config DVB_USB_DIGITV
- tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_NXT6000 if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver.
-
-config DVB_USB_VP7045
- tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support"
- depends on DVB_USB
- help
- Say Y here to support the
-
- TwinhanDTV Alpha (stick) (VP-7045),
- TwinhanDTV MagicBox II (VP-7046),
- DigitalNow TinyUSB 2 DVB-t,
- DigitalRise USB 2.0 Ter (Beetle) and
- TYPHOON DVB-T USB DRIVE
-
- DVB-T USB2.0 receivers.
-
-config DVB_USB_VP702X
- tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support"
- depends on DVB_USB
- help
- Say Y here to support the
-
- TwinhanDTV StarBox,
- DigitalRise USB Starbox and
- TYPHOON DVB-S USB 2.0 BOX
-
- DVB-S USB2.0 receivers.
-
-config DVB_USB_GP8PSK
- tristate "GENPIX 8PSK->USB module support"
- depends on DVB_USB
- help
- Say Y here to support the
- GENPIX 8psk module
-
- DVB-S USB2.0 receivers.
-
-config DVB_USB_NOVA_T_USB2
- tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_DIB3000MC
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
-
-config DVB_USB_TTUSB2
- tristate "Pinnacle 400e DVB-S USB2.0 support"
- depends on DVB_USB
- select DVB_TDA10086 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_TDA826X if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
- firmware protocol used by this module is similar to the one used by the
- old ttusb-driver - that's why the module is called dvb-usb-ttusb2.
-
-config DVB_USB_DTT200U
- tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
- depends on DVB_USB
- help
- Say Y here to support the WideView/Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver.
-
- The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan).
-
- The WT-220U and its clones are pen-sized.
-
-config DVB_USB_OPERA1
- tristate "Opera1 DVB-S USB2.0 receiver"
- depends on DVB_USB
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_PLL if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the Opera DVB-S USB2.0 receiver.
-
-config DVB_USB_AF9005
- tristate "Afatech AF9005 DVB-T USB1.1 support"
- depends on DVB_USB && EXPERIMENTAL
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
- and the TerraTec Cinergy T USB XE (Rev.1)
-
-config DVB_USB_AF9005_REMOTE
- tristate "Afatech AF9005 default remote control support"
- depends on DVB_USB_AF9005
- help
- Say Y here to support the default remote control decoding for the
- Afatech AF9005 based receiver.
-
-config DVB_USB_PCTV452E
- tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600"
- depends on DVB_USB
- select TTPCI_EEPROM
- select DVB_LNBP22 if !DVB_FE_CUSTOMISE
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
- help
- Support for external USB adapter designed by Pinnacle,
- shipped under the brand name 'PCTV HDTV Pro USB'.
- Also supports TT Connect S2-3600/3650 cards.
- Say Y if you own such a device and want to use it.
-
-config DVB_USB_DW2102
- tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_STV0288 if !DVB_FE_CUSTOMISE
- select DVB_STB6000 if !DVB_FE_CUSTOMISE
- select DVB_CX24116 if !DVB_FE_CUSTOMISE
- select DVB_SI21XX if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select DVB_MT312 if !DVB_FE_CUSTOMISE
- select DVB_ZL10039 if !DVB_FE_CUSTOMISE
- select DVB_DS3000 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
- select DVB_STV6110 if !DVB_FE_CUSTOMISE
- select DVB_STV0900 if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0
- receivers.
-
-config DVB_USB_CINERGY_T2
- tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
- depends on DVB_USB
- help
- Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
-
- Say Y if you own such a device and want to use it.
-
-config DVB_USB_ANYSEE
- tristate "Anysee DVB-T/C USB2.0 support"
- depends on DVB_USB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18212 if !MEDIA_TUNER_CUSTOMISE
- select DVB_CX24116 if !DVB_FE_CUSTOMISE
- select DVB_STV0900 if !DVB_FE_CUSTOMISE
- select DVB_STV6110 if !DVB_FE_CUSTOMISE
- select DVB_ISL6423 if !DVB_FE_CUSTOMISE
- select DVB_CXD2820R if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the Anysee E30, Anysee E30 Plus or
- Anysee E30 C Plus DVB USB2.0 receiver.
-
-config DVB_USB_DTV5100
- tristate "AME DTV-5100 USB2.0 DVB-T support"
- depends on DVB_USB
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
-
-config DVB_USB_AF9015
- tristate "Afatech AF9015 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_AF9013
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
-
-config DVB_USB_CE6230
- tristate "Intel CE6230 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_ZL10353
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver
-
-config DVB_USB_FRIIO
- tristate "Friio ISDB-T USB2.0 Receiver support"
- depends on DVB_USB
- help
- Say Y here to support the Japanese DTV receiver Friio.
-
-config DVB_USB_EC168
- tristate "E3C EC168 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_EC100
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
-
-config DVB_USB_AZ6007
- tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
- depends on DVB_USB
- select DVB_DRXK if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE
- help
- Say Y here to support theAfatech AF9005 based DVB-T/DVB-C receivers.
-
-config DVB_USB_AZ6027
- tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 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 AZ6027 device
-
-config DVB_USB_LME2510
- tristate "LME DM04/QQBOX DVB-S USB2.0 support"
- depends on DVB_USB
- select DVB_TDA10086 if !DVB_FE_CUSTOMISE
- select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select DVB_STV0288 if !DVB_FE_CUSTOMISE
- select DVB_IX2505V if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_M88RS2000 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_STV090x if !DVB_FE_CUSTOMISE
- select DVB_STV6110x if !DVB_FE_CUSTOMISE
- help
- Say Y here to support the Technisat USB2 DVB-S/S2 device
-
-config DVB_USB_IT913X
- tristate "it913x driver"
- depends on DVB_USB
- select DVB_IT913X_FE
- help
- Say Y here to support the it913x device
-
-config DVB_USB_MXL111SF
- tristate "MxL111SF DTV USB2.0 support"
- depends on DVB_USB
- select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
- select DVB_LG2160 if !DVB_FE_CUSTOMISE
- select VIDEO_TVEEPROM
- help
- Say Y here to support the MxL111SF USB2.0 DTV receiver.
-
-config DVB_USB_RTL28XXU
- tristate "Realtek RTL28xxU DVB USB support"
- depends on DVB_USB && EXPERIMENTAL
- select DVB_RTL2830
- select DVB_RTL2832
- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_FC0012 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_FC0013 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Realtek RTL28xxU DVB USB receiver.
-
-config DVB_USB_AF9035
- tristate "Afatech AF9035 DVB-T USB2.0 support"
- depends on DVB_USB
- select DVB_AF9033
- select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE
- help
- Say Y here to support the Afatech AF9035 based DVB USB receiver.
-
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
deleted file mode 100644
index b667ac39a4e3..000000000000
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ /dev/null
@@ -1,121 +0,0 @@
-dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o
-obj-$(CONFIG_DVB_USB) += dvb-usb.o
-
-dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o
-obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o
-
-dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o
-obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o
-
-dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o
-obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o
-
-dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o
-obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o
-
-dvb-usb-dibusb-common-objs = dibusb-common.o
-
-dvb-usb-a800-objs = a800.o
-obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o
-
-dvb-usb-dibusb-mb-objs = dibusb-mb.o
-obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o
-
-dvb-usb-dibusb-mc-objs = dibusb-mc.o
-obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o
-
-dvb-usb-nova-t-usb2-objs = nova-t-usb2.o
-obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o
-
-dvb-usb-umt-010-objs = umt-010.o
-obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o
-
-dvb-usb-m920x-objs = m920x.o
-obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o
-
-dvb-usb-gl861-objs = gl861.o
-obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o
-
-dvb-usb-au6610-objs = au6610.o
-obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o
-
-dvb-usb-digitv-objs = digitv.o
-obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o
-
-dvb-usb-cxusb-objs = cxusb.o
-obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o
-
-dvb-usb-ttusb2-objs = ttusb2.o
-obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
-
-dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o
-obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
-
-dvb-usb-opera-objs = opera1.o
-obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
-
-dvb-usb-af9005-objs = af9005.o af9005-fe.o
-obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
-
-dvb-usb-af9005-remote-objs = af9005-remote.o
-obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
-
-dvb-usb-anysee-objs = anysee.o
-obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
-
-dvb-usb-pctv452e-objs = pctv452e.o
-obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o
-
-dvb-usb-dw2102-objs = dw2102.o
-obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
-
-dvb-usb-dtv5100-objs = dtv5100.o
-obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
-
-dvb-usb-af9015-objs = af9015.o
-obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
-
-dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o
-obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
-
-dvb-usb-ce6230-objs = ce6230.o
-obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
-
-dvb-usb-friio-objs = friio.o friio-fe.o
-obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
-
-dvb-usb-ec168-objs = ec168.o
-obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
-
-dvb-usb-az6007-objs = az6007.o
-obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
-
-dvb-usb-az6027-objs = az6027.o
-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
-
-dvb-usb-it913x-objs := it913x.o
-obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
-
-dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o
-obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
-obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
-obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
-
-dvb-usb-rtl28xxu-objs = rtl28xxu.o
-obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
-
-dvb-usb-af9035-objs = af9035.o
-obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o
-
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/
-# due to tuner-xc3028
-ccflags-y += -I$(srctree)/drivers/media/common/tuners
-ccflags-y += -I$(srctree)/drivers/media/dvb/ttpci
-
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
deleted file mode 100644
index 677fed79b01e..000000000000
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ /dev/null
@@ -1,1952 +0,0 @@
-/*
- * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- *
- * Thanks to Afatech who kindly provided information.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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/hash.h>
-#include <linux/slab.h>
-
-#include "af9015.h"
-#include "af9013.h"
-#include "mt2060.h"
-#include "qt1010.h"
-#include "tda18271.h"
-#include "mxl5005s.h"
-#include "mc44s803.h"
-#include "tda18218.h"
-#include "mxl5007t.h"
-
-static int dvb_usb_af9015_debug;
-module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-static int dvb_usb_af9015_remote;
-module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
-MODULE_PARM_DESC(remote, "select remote");
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static DEFINE_MUTEX(af9015_usb_mutex);
-
-static struct af9015_config af9015_config;
-static struct dvb_usb_device_properties af9015_properties[3];
-static int af9015_properties_count = ARRAY_SIZE(af9015_properties);
-
-static struct af9013_config af9015_af9013_config[] = {
- {
- .i2c_addr = AF9015_I2C_DEMOD,
- .ts_mode = AF9013_TS_USB,
- .api_version = { 0, 1, 9, 0 },
- .gpio[0] = AF9013_GPIO_HI,
- .gpio[3] = AF9013_GPIO_TUNER_ON,
-
- }, {
- .ts_mode = AF9013_TS_SERIAL,
- .api_version = { 0, 1, 9, 0 },
- .gpio[0] = AF9013_GPIO_TUNER_ON,
- .gpio[1] = AF9013_GPIO_LO,
- }
-};
-
-static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
-{
-#define BUF_LEN 63
-#define REQ_HDR_LEN 8 /* send header size */
-#define ACK_HDR_LEN 2 /* rece header size */
- int act_len, ret;
- u8 buf[BUF_LEN];
- u8 write = 1;
- u8 msg_len = REQ_HDR_LEN;
- static u8 seq; /* packet sequence number */
-
- if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
- return -EAGAIN;
-
- buf[0] = req->cmd;
- buf[1] = seq++;
- buf[2] = req->i2c_addr;
- buf[3] = req->addr >> 8;
- buf[4] = req->addr & 0xff;
- buf[5] = req->mbox;
- buf[6] = req->addr_len;
- buf[7] = req->data_len;
-
- switch (req->cmd) {
- case GET_CONFIG:
- case READ_MEMORY:
- case RECONNECT_USB:
- write = 0;
- break;
- case READ_I2C:
- write = 0;
- buf[2] |= 0x01; /* set I2C direction */
- case WRITE_I2C:
- buf[0] = READ_WRITE_I2C;
- break;
- case WRITE_MEMORY:
- if (((req->addr & 0xff00) == 0xff00) ||
- ((req->addr & 0xff00) == 0xae00))
- buf[0] = WRITE_VIRTUAL_MEMORY;
- case WRITE_VIRTUAL_MEMORY:
- case COPY_FIRMWARE:
- case DOWNLOAD_FIRMWARE:
- case BOOT:
- break;
- default:
- err("unknown command:%d", req->cmd);
- ret = -1;
- goto error_unlock;
- }
-
- /* buffer overflow check */
- if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) ||
- (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) {
- err("too much data; cmd:%d len:%d", req->cmd, req->data_len);
- ret = -EINVAL;
- goto error_unlock;
- }
-
- /* write requested */
- if (write) {
- memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len);
- msg_len += req->data_len;
- }
-
- deb_xfer(">>> ");
- debug_dump(buf, msg_len, deb_xfer);
-
- /* send req */
- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
- &act_len, AF9015_USB_TIMEOUT);
- if (ret)
- err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
- else
- if (act_len != msg_len)
- ret = -1; /* all data is not send */
- if (ret)
- goto error_unlock;
-
- /* no ack for those packets */
- if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
- goto exit_unlock;
-
- /* write receives seq + status = 2 bytes
- read receives seq + status + data = 2 + N bytes */
- msg_len = ACK_HDR_LEN;
- if (!write)
- msg_len += req->data_len;
-
- ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
- &act_len, AF9015_USB_TIMEOUT);
- if (ret) {
- err("recv bulk message failed:%d", ret);
- ret = -1;
- goto error_unlock;
- }
-
- deb_xfer("<<< ");
- debug_dump(buf, act_len, deb_xfer);
-
- /* check status */
- if (buf[1]) {
- err("command failed:%d", buf[1]);
- ret = -1;
- goto error_unlock;
- }
-
- /* read request, copy returned data to return buf */
- if (!write)
- memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len);
-
-error_unlock:
-exit_unlock:
- mutex_unlock(&af9015_usb_mutex);
-
- return ret;
-}
-
-static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
-{
- return af9015_rw_udev(d->udev, req);
-}
-
-static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
- u8 len)
-{
- struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
- val};
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
-{
- return af9015_write_regs(d, addr, &val, 1);
-}
-
-static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len)
-{
- struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
- val};
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
-{
- return af9015_read_regs(d, addr, val, 1);
-}
-
-static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
- u8 val)
-{
- struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
-
- if (addr == af9015_af9013_config[0].i2c_addr ||
- addr == af9015_af9013_config[1].i2c_addr)
- req.addr_len = 3;
-
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
- u8 *val)
-{
- struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
-
- if (addr == af9015_af9013_config[0].i2c_addr ||
- addr == af9015_af9013_config[1].i2c_addr)
- req.addr_len = 3;
-
- return af9015_ctrl_msg(d, &req);
-}
-
-static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0, i = 0;
- u16 addr;
- u8 uninitialized_var(mbox), addr_len;
- struct req_t req;
-
-/*
-The bus lock is needed because there is two tuners both using same I2C-address.
-Due to that the only way to select correct tuner is use demodulator I2C-gate.
-
-................................................
-. AF9015 includes integrated AF9013 demodulator.
-. ____________ ____________ . ____________
-.| uC | | demod | . | tuner |
-.|------------| |------------| . |------------|
-.| AF9015 | | AF9013/5 | . | MXL5003 |
-.| |--+----I2C-------|-----/ -----|-.-----I2C-------| |
-.| | | | addr 0x38 | . | addr 0xc6 |
-.|____________| | |____________| . |____________|
-.................|..............................
- | ____________ ____________
- | | demod | | tuner |
- | |------------| |------------|
- | | AF9013 | | MXL5003 |
- +----I2C-------|-----/ -----|-------I2C-------| |
- | addr 0x3a | | addr 0xc6 |
- |____________| |____________|
-*/
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- while (i < num) {
- if (msg[i].addr == af9015_af9013_config[0].i2c_addr ||
- msg[i].addr == af9015_af9013_config[1].i2c_addr) {
- addr = msg[i].buf[0] << 8;
- addr += msg[i].buf[1];
- mbox = msg[i].buf[2];
- addr_len = 3;
- } else {
- addr = msg[i].buf[0];
- addr_len = 1;
- /* mbox is don't care in that case */
- }
-
- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].len > 3 || msg[i+1].len > 61) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr == af9015_af9013_config[0].i2c_addr)
- req.cmd = READ_MEMORY;
- else
- req.cmd = READ_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i+1].len;
- req.data = &msg[i+1].buf[0];
- ret = af9015_ctrl_msg(d, &req);
- i += 2;
- } else if (msg[i].flags & I2C_M_RD) {
- if (msg[i].len > 61) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr ==
- af9015_af9013_config[0].i2c_addr) {
- ret = -EINVAL;
- goto error;
- }
- req.cmd = READ_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i].len;
- req.data = &msg[i].buf[0];
- ret = af9015_ctrl_msg(d, &req);
- i += 1;
- } else {
- if (msg[i].len > 21) {
- ret = -EOPNOTSUPP;
- goto error;
- }
- if (msg[i].addr == af9015_af9013_config[0].i2c_addr)
- req.cmd = WRITE_MEMORY;
- else
- req.cmd = WRITE_I2C;
- req.i2c_addr = msg[i].addr;
- req.addr = addr;
- req.mbox = mbox;
- req.addr_len = addr_len;
- req.data_len = msg[i].len-addr_len;
- req.data = &msg[i].buf[addr_len];
- ret = af9015_ctrl_msg(d, &req);
- i += 1;
- }
- if (ret)
- goto error;
-
- }
- ret = i;
-
-error:
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-static u32 af9015_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm af9015_i2c_algo = {
- .master_xfer = af9015_i2c_xfer,
- .functionality = af9015_i2c_func,
-};
-
-static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
-{
- int ret;
- u8 val, mask = 0x01;
-
- ret = af9015_read_reg(d, addr, &val);
- if (ret)
- return ret;
-
- mask <<= bit;
- if (op) {
- /* set bit */
- val |= mask;
- } else {
- /* clear bit */
- mask ^= 0xff;
- val &= mask;
- }
-
- return af9015_write_reg(d, addr, val);
-}
-
-static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
-{
- return af9015_do_reg_bit(d, addr, bit, 1);
-}
-
-static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
-{
- return af9015_do_reg_bit(d, addr, bit, 0);
-}
-
-static int af9015_init_endpoint(struct dvb_usb_device *d)
-{
- int ret;
- u16 frame_size;
- u8 packet_size;
- deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
-
- /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0.
- We use smaller - about 1/4 from the original, 5 and 87. */
-#define TS_PACKET_SIZE 188
-
-#define TS_USB20_PACKET_COUNT 87
-#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
-
-#define TS_USB11_PACKET_COUNT 5
-#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
-
-#define TS_USB20_MAX_PACKET_SIZE 512
-#define TS_USB11_MAX_PACKET_SIZE 64
-
- if (d->udev->speed == USB_SPEED_FULL) {
- frame_size = TS_USB11_FRAME_SIZE/4;
- packet_size = TS_USB11_MAX_PACKET_SIZE/4;
- } else {
- frame_size = TS_USB20_FRAME_SIZE/4;
- packet_size = TS_USB20_MAX_PACKET_SIZE/4;
- }
-
- ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
- if (ret)
- goto error;
- ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
- if (ret)
- goto error;
- ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
- if (ret)
- goto error;
- if (af9015_config.dual_mode) {
- ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
- if (ret)
- goto error;
- }
- ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
- if (ret)
- goto error;
- if (af9015_config.dual_mode) {
- ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
- if (ret)
- goto error;
- }
- /* EP4 xfer length */
- ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
- if (ret)
- goto error;
- /* EP5 xfer length */
- ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
- if (ret)
- goto error;
- ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
- if (ret)
- goto error;
- ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
- if (ret)
- goto error;
- if (af9015_config.dual_mode) {
- ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
- if (ret)
- goto error;
- }
-
- /* enable / disable mp2if2 */
- if (af9015_config.dual_mode)
- ret = af9015_set_reg_bit(d, 0xd50b, 0);
- else
- ret = af9015_clear_reg_bit(d, 0xd50b, 0);
-
-error:
- if (ret)
- err("endpoint init failed:%d", ret);
- return ret;
-}
-
-static int af9015_copy_firmware(struct dvb_usb_device *d)
-{
- int ret;
- u8 fw_params[4];
- u8 val, i;
- struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
- fw_params };
- deb_info("%s:\n", __func__);
-
- fw_params[0] = af9015_config.firmware_size >> 8;
- fw_params[1] = af9015_config.firmware_size & 0xff;
- fw_params[2] = af9015_config.firmware_checksum >> 8;
- fw_params[3] = af9015_config.firmware_checksum & 0xff;
-
- /* wait 2nd demodulator ready */
- msleep(100);
-
- ret = af9015_read_reg_i2c(d,
- af9015_af9013_config[1].i2c_addr, 0x98be, &val);
- if (ret)
- goto error;
- else
- deb_info("%s: firmware status:%02x\n", __func__, val);
-
- if (val == 0x0c) /* fw is running, no need for download */
- goto exit;
-
- /* set I2C master clock to fast (to speed up firmware copy) */
- ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
- if (ret)
- goto error;
-
- msleep(50);
-
- /* copy firmware */
- ret = af9015_ctrl_msg(d, &req);
- if (ret)
- err("firmware copy cmd failed:%d", ret);
- deb_info("%s: firmware copy done\n", __func__);
-
- /* set I2C master clock back to normal */
- ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
- if (ret)
- goto error;
-
- /* request boot firmware */
- ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].i2c_addr,
- 0xe205, 1);
- deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
- if (ret)
- goto error;
-
- for (i = 0; i < 15; i++) {
- msleep(100);
-
- /* check firmware status */
- ret = af9015_read_reg_i2c(d,
- af9015_af9013_config[1].i2c_addr, 0x98be, &val);
- deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
- __func__, ret, val);
- if (ret)
- goto error;
-
- if (val == 0x0c || val == 0x04) /* success or fail */
- break;
- }
-
- if (val == 0x04) {
- err("firmware did not run");
- ret = -1;
- } else if (val != 0x0c) {
- err("firmware boot timeout");
- ret = -1;
- }
-
-error:
-exit:
- return ret;
-}
-
-/* hash (and dump) eeprom */
-static int af9015_eeprom_hash(struct usb_device *udev)
-{
- static const unsigned int eeprom_size = 256;
- unsigned int reg;
- int ret;
- u8 val, *eeprom;
- struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
-
- eeprom = kmalloc(eeprom_size, GFP_KERNEL);
- if (eeprom == NULL)
- return -ENOMEM;
-
- for (reg = 0; reg < eeprom_size; reg++) {
- req.addr = reg;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto free;
- eeprom[reg] = val;
- }
-
- if (dvb_usb_af9015_debug & 0x01)
- print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom,
- eeprom_size);
-
- BUG_ON(eeprom_size % 4);
-
- af9015_config.eeprom_sum = 0;
- for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) {
- af9015_config.eeprom_sum *= GOLDEN_RATIO_PRIME_32;
- af9015_config.eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]);
- }
-
- deb_info("%s: eeprom sum=%.8x\n", __func__, af9015_config.eeprom_sum);
-
- ret = 0;
-free:
- kfree(eeprom);
- return ret;
-}
-
-static int af9015_init(struct dvb_usb_device *d)
-{
- int ret;
- deb_info("%s:\n", __func__);
-
- /* init RC canary */
- ret = af9015_write_reg(d, 0x98e9, 0xff);
- if (ret)
- goto error;
-
- ret = af9015_init_endpoint(d);
- if (ret)
- goto error;
-
-error:
- return ret;
-}
-
-static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- int ret;
- deb_info("%s: onoff:%d\n", __func__, onoff);
-
- if (onoff)
- ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
- else
- ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
-
- return ret;
-}
-
-static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
- int onoff)
-{
- int ret;
- u8 idx;
-
- deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
- __func__, index, pid, onoff);
-
- ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
- if (ret)
- goto error;
-
- ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
- if (ret)
- goto error;
-
- idx = ((index & 0x1f) | (1 << 5));
- ret = af9015_write_reg(adap->dev, 0xd504, idx);
-
-error:
- return ret;
-}
-
-static int af9015_download_firmware(struct usb_device *udev,
- const struct firmware *fw)
-{
- int i, len, remaining, ret;
- struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
- u16 checksum = 0;
-
- deb_info("%s:\n", __func__);
-
- /* calc checksum */
- for (i = 0; i < fw->size; i++)
- checksum += fw->data[i];
-
- af9015_config.firmware_size = fw->size;
- af9015_config.firmware_checksum = checksum;
-
- #define FW_ADDR 0x5100 /* firmware start address */
- #define LEN_MAX 55 /* max packet size */
- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
- len = remaining;
- if (len > LEN_MAX)
- len = LEN_MAX;
-
- req.data_len = len;
- req.data = (u8 *) &fw->data[fw->size - remaining];
- req.addr = FW_ADDR + fw->size - remaining;
-
- ret = af9015_rw_udev(udev, &req);
- if (ret) {
- err("firmware download failed:%d", ret);
- goto error;
- }
- }
-
- /* firmware loaded, request boot */
- req.cmd = BOOT;
- ret = af9015_rw_udev(udev, &req);
- if (ret) {
- err("firmware boot failed:%d", ret);
- goto error;
- }
-
-error:
- return ret;
-}
-
-struct af9015_rc_setup {
- unsigned int id;
- char *rc_codes;
-};
-
-static char *af9015_rc_setup_match(unsigned int id,
- const struct af9015_rc_setup *table)
-{
- for (; table->rc_codes; table++)
- if (table->id == id)
- return table->rc_codes;
- return NULL;
-}
-
-static const struct af9015_rc_setup af9015_rc_setup_modparam[] = {
- { AF9015_REMOTE_A_LINK_DTU_M, RC_MAP_ALINK_DTU_M },
- { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, RC_MAP_MSI_DIGIVOX_II },
- { AF9015_REMOTE_MYGICTV_U718, RC_MAP_TOTAL_MEDIA_IN_HAND },
- { AF9015_REMOTE_DIGITTRADE_DVB_T, RC_MAP_DIGITTRADE },
- { AF9015_REMOTE_AVERMEDIA_KS, RC_MAP_AVERMEDIA_RM_KS },
- { }
-};
-
-static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
- { 0xb8feb708, RC_MAP_MSI_DIGIVOX_II },
- { 0xa3703d00, RC_MAP_ALINK_DTU_M },
- { 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */
- { 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */
- { }
-};
-
-static const struct af9015_rc_setup af9015_rc_setup_usbids[] = {
- { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_RC,
- RC_MAP_TERRATEC_SLIM_2 },
- { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
- RC_MAP_TERRATEC_SLIM },
- { (USB_VID_VISIONPLUS << 16) | USB_PID_AZUREWAVE_AD_TU700,
- RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_VISIONPLUS << 16) | USB_PID_TINYTWIN,
- RC_MAP_AZUREWAVE_AD_TU700 },
- { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGI_VOX_MINI_III,
- RC_MAP_MSI_DIGIVOX_III },
- { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGIVOX_DUO,
- RC_MAP_MSI_DIGIVOX_III },
- { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV_DONGLE_GOLD,
- RC_MAP_LEADTEK_Y04G0051 },
- { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV2000DS,
- RC_MAP_LEADTEK_Y04G0051 },
- { (USB_VID_AVERMEDIA << 16) | USB_PID_AVERMEDIA_VOLAR_X,
- RC_MAP_AVERMEDIA_M135A },
- { (USB_VID_AFATECH << 16) | USB_PID_TREKSTOR_DVBT,
- RC_MAP_TREKSTOR },
- { (USB_VID_KWORLD_2 << 16) | USB_PID_TINYTWIN_2,
- RC_MAP_DIGITALNOW_TINYTWIN },
- { (USB_VID_GTEK << 16) | USB_PID_TINYTWIN_3,
- RC_MAP_DIGITALNOW_TINYTWIN },
- { (USB_VID_KWORLD_2 << 16) | USB_PID_SVEON_STV22,
- RC_MAP_MSI_DIGIVOX_III },
- { }
-};
-
-static void af9015_set_remote_config(struct usb_device *udev,
- struct dvb_usb_device_properties *props)
-{
- u16 vid = le16_to_cpu(udev->descriptor.idVendor);
- u16 pid = le16_to_cpu(udev->descriptor.idProduct);
-
- /* try to load remote based module param */
- props->rc.core.rc_codes = af9015_rc_setup_match(
- dvb_usb_af9015_remote, af9015_rc_setup_modparam);
-
- /* try to load remote based eeprom hash */
- if (!props->rc.core.rc_codes)
- props->rc.core.rc_codes = af9015_rc_setup_match(
- af9015_config.eeprom_sum, af9015_rc_setup_hashes);
-
- /* try to load remote based USB ID */
- if (!props->rc.core.rc_codes)
- props->rc.core.rc_codes = af9015_rc_setup_match(
- (vid << 16) | pid, af9015_rc_setup_usbids);
-
- /* try to load remote based USB iManufacturer string */
- if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) {
- /* Check USB manufacturer and product strings and try
- to determine correct remote in case of chip vendor
- reference IDs are used.
- DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */
- char manufacturer[10];
- memset(manufacturer, 0, sizeof(manufacturer));
- usb_string(udev, udev->descriptor.iManufacturer,
- manufacturer, sizeof(manufacturer));
- if (!strcmp("MSI", manufacturer)) {
- /* iManufacturer 1 MSI
- iProduct 2 MSI K-VOX */
- props->rc.core.rc_codes = af9015_rc_setup_match(
- AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
- af9015_rc_setup_modparam);
- }
- }
-
- /* finally load "empty" just for leaving IR receiver enabled */
- if (!props->rc.core.rc_codes)
- props->rc.core.rc_codes = RC_MAP_EMPTY;
-
- return;
-}
-
-static int af9015_read_config(struct usb_device *udev)
-{
- int ret;
- u8 val, i, offset = 0;
- struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
-
- /* IR remote controller */
- req.addr = AF9015_EEPROM_IR_MODE;
- /* first message will timeout often due to possible hw bug */
- for (i = 0; i < 4; i++) {
- ret = af9015_rw_udev(udev, &req);
- if (!ret)
- break;
- }
- if (ret)
- goto error;
-
- ret = af9015_eeprom_hash(udev);
- if (ret)
- goto error;
-
- deb_info("%s: IR mode=%d\n", __func__, val);
- for (i = 0; i < af9015_properties_count; i++) {
- if (val == AF9015_IR_MODE_DISABLED)
- af9015_properties[i].rc.core.rc_codes = NULL;
- else
- af9015_set_remote_config(udev, &af9015_properties[i]);
- }
-
- /* TS mode - one or two receivers */
- req.addr = AF9015_EEPROM_TS_MODE;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- af9015_config.dual_mode = val;
- deb_info("%s: TS mode=%d\n", __func__, af9015_config.dual_mode);
-
- /* Set adapter0 buffer size according to USB port speed, adapter1 buffer
- size can be static because it is enabled only USB2.0 */
- for (i = 0; i < af9015_properties_count; i++) {
- /* USB1.1 set smaller buffersize and disable 2nd adapter */
- if (udev->speed == USB_SPEED_FULL) {
- af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
- = TS_USB11_FRAME_SIZE;
- /* disable 2nd adapter because we don't have
- PID-filters */
- af9015_config.dual_mode = 0;
- } else {
- af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize
- = TS_USB20_FRAME_SIZE;
- }
- }
-
- if (af9015_config.dual_mode) {
- /* read 2nd demodulator I2C address */
- req.addr = AF9015_EEPROM_DEMOD2_I2C;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- af9015_af9013_config[1].i2c_addr = val;
-
- /* enable 2nd adapter */
- for (i = 0; i < af9015_properties_count; i++)
- af9015_properties[i].num_adapters = 2;
-
- } else {
- /* disable 2nd adapter */
- for (i = 0; i < af9015_properties_count; i++)
- af9015_properties[i].num_adapters = 1;
- }
-
- for (i = 0; i < af9015_properties[0].num_adapters; i++) {
- if (i == 1)
- offset = AF9015_EEPROM_OFFSET;
- /* xtal */
- req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- switch (val) {
- case 0:
- af9015_af9013_config[i].clock = 28800000;
- break;
- case 1:
- af9015_af9013_config[i].clock = 20480000;
- break;
- case 2:
- af9015_af9013_config[i].clock = 28000000;
- break;
- case 3:
- af9015_af9013_config[i].clock = 25000000;
- break;
- };
- deb_info("%s: [%d] xtal=%d set clock=%d\n", __func__, i,
- val, af9015_af9013_config[i].clock);
-
- /* IF frequency */
- req.addr = AF9015_EEPROM_IF1H + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- af9015_af9013_config[i].if_frequency = val << 8;
-
- req.addr = AF9015_EEPROM_IF1L + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- af9015_af9013_config[i].if_frequency += val;
- af9015_af9013_config[i].if_frequency *= 1000;
- deb_info("%s: [%d] IF frequency=%d\n", __func__, i,
- af9015_af9013_config[0].if_frequency);
-
- /* MT2060 IF1 */
- req.addr = AF9015_EEPROM_MT2060_IF1H + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- af9015_config.mt2060_if1[i] = val << 8;
- req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- af9015_config.mt2060_if1[i] += val;
- deb_info("%s: [%d] MT2060 IF1=%d\n", __func__, i,
- af9015_config.mt2060_if1[i]);
-
- /* tuner */
- req.addr = AF9015_EEPROM_TUNER_ID1 + offset;
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- goto error;
- switch (val) {
- case AF9013_TUNER_ENV77H11D5:
- case AF9013_TUNER_MT2060:
- case AF9013_TUNER_QT1010:
- case AF9013_TUNER_UNKNOWN:
- case AF9013_TUNER_MT2060_2:
- case AF9013_TUNER_TDA18271:
- case AF9013_TUNER_QT1010A:
- case AF9013_TUNER_TDA18218:
- af9015_af9013_config[i].spec_inv = 1;
- break;
- case AF9013_TUNER_MXL5003D:
- case AF9013_TUNER_MXL5005D:
- case AF9013_TUNER_MXL5005R:
- case AF9013_TUNER_MXL5007T:
- af9015_af9013_config[i].spec_inv = 0;
- break;
- case AF9013_TUNER_MC44S803:
- af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO;
- af9015_af9013_config[i].spec_inv = 1;
- break;
- default:
- warn("tuner id=%d not supported, please report!", val);
- return -ENODEV;
- };
-
- af9015_af9013_config[i].tuner = val;
- deb_info("%s: [%d] tuner id=%d\n", __func__, i, val);
- }
-
-error:
- if (ret)
- err("eeprom read failed=%d", ret);
-
- /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
- content :-( Override some wrong values here. Ditto for the
- AVerTV Red HD+ (A850T) device. */
- if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_AVERMEDIA &&
- ((le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_AVERMEDIA_A850) ||
- (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_AVERMEDIA_A850T))) {
- deb_info("%s: AverMedia A850: overriding config\n", __func__);
- /* disable dual mode */
- af9015_config.dual_mode = 0;
- /* disable 2nd adapter */
- for (i = 0; i < af9015_properties_count; i++)
- af9015_properties[i].num_adapters = 1;
-
- /* set correct IF */
- af9015_af9013_config[0].if_frequency = 4570000;
- }
-
- return ret;
-}
-
-static int af9015_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- int ret;
- u8 reply;
- struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
-
- ret = af9015_rw_udev(udev, &req);
- if (ret)
- return ret;
-
- deb_info("%s: reply:%02x\n", __func__, reply);
- if (reply == 0x02)
- *cold = 0;
- else
- *cold = 1;
-
- return ret;
-}
-
-static int af9015_rc_query(struct dvb_usb_device *d)
-{
- struct af9015_state *priv = d->priv;
- int ret;
- u8 buf[17];
-
- /* read registers needed to detect remote controller code */
- ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf));
- if (ret)
- goto error;
-
- /* If any of these are non-zero, assume invalid data */
- if (buf[1] || buf[2] || buf[3])
- return ret;
-
- /* Check for repeat of previous code */
- if ((priv->rc_repeat != buf[6] || buf[0]) &&
- !memcmp(&buf[12], priv->rc_last, 4)) {
- deb_rc("%s: key repeated\n", __func__);
- rc_keydown(d->rc_dev, priv->rc_keycode, 0);
- priv->rc_repeat = buf[6];
- return ret;
- }
-
- /* Only process key if canary killed */
- if (buf[16] != 0xff && buf[0] != 0x01) {
- deb_rc("%s: key pressed %02x %02x %02x %02x\n", __func__,
- buf[12], buf[13], buf[14], buf[15]);
-
- /* Reset the canary */
- ret = af9015_write_reg(d, 0x98e9, 0xff);
- if (ret)
- goto error;
-
- /* Remember this key */
- memcpy(priv->rc_last, &buf[12], 4);
- if (buf[14] == (u8) ~buf[15]) {
- if (buf[12] == (u8) ~buf[13]) {
- /* NEC */
- priv->rc_keycode = buf[12] << 8 | buf[14];
- } else {
- /* NEC extended*/
- priv->rc_keycode = buf[12] << 16 |
- buf[13] << 8 | buf[14];
- }
- } else {
- /* 32 bit NEC */
- priv->rc_keycode = buf[12] << 24 | buf[13] << 16 |
- buf[14] << 8 | buf[15];
- }
- rc_keydown(d->rc_dev, priv->rc_keycode, 0);
- } else {
- deb_rc("%s: no key press\n", __func__);
- /* Invalidate last keypress */
- /* Not really needed, but helps with debug */
- priv->rc_last[2] = priv->rc_last[3];
- }
-
- priv->rc_repeat = buf[6];
-
-error:
- if (ret)
- err("%s: failed:%d", __func__, ret);
-
- return ret;
-}
-
-/* override demod callbacks for resource locking */
-static int af9015_af9013_set_frontend(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->set_frontend[adap->id](fe);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-/* override demod callbacks for resource locking */
-static int af9015_af9013_read_status(struct dvb_frontend *fe,
- fe_status_t *status)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->read_status[adap->id](fe, status);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-/* override demod callbacks for resource locking */
-static int af9015_af9013_init(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->init[adap->id](fe);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-/* override demod callbacks for resource locking */
-static int af9015_af9013_sleep(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->sleep[adap->id](fe);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-/* override tuner callbacks for resource locking */
-static int af9015_tuner_init(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->tuner_init[adap->id](fe);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-/* override tuner callbacks for resource locking */
-static int af9015_tuner_sleep(struct dvb_frontend *fe)
-{
- int ret;
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct af9015_state *priv = adap->dev->priv;
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex))
- return -EAGAIN;
-
- ret = priv->tuner_sleep[adap->id](fe);
-
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-
-static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct af9015_state *state = adap->dev->priv;
-
- if (adap->id == 1) {
- /* copy firmware to 2nd demodulator */
- if (af9015_config.dual_mode) {
- ret = af9015_copy_firmware(adap->dev);
- if (ret) {
- err("firmware copy to 2nd frontend " \
- "failed, will disable it");
- af9015_config.dual_mode = 0;
- return -ENODEV;
- }
- } else {
- return -ENODEV;
- }
- }
-
- /* attach demodulator */
- adap->fe_adap[0].fe = dvb_attach(af9013_attach,
- &af9015_af9013_config[adap->id], &adap->dev->i2c_adap);
-
- /*
- * AF9015 firmware does not like if it gets interrupted by I2C adapter
- * request on some critical phases. During normal operation I2C adapter
- * is used only 2nd demodulator and tuner on dual tuner devices.
- * Override demodulator callbacks and use mutex for limit access to
- * those "critical" paths to keep AF9015 happy.
- * Note: we abuse unused usb_mutex here.
- */
- if (adap->fe_adap[0].fe) {
- state->set_frontend[adap->id] =
- adap->fe_adap[0].fe->ops.set_frontend;
- adap->fe_adap[0].fe->ops.set_frontend =
- af9015_af9013_set_frontend;
-
- state->read_status[adap->id] =
- adap->fe_adap[0].fe->ops.read_status;
- adap->fe_adap[0].fe->ops.read_status =
- af9015_af9013_read_status;
-
- state->init[adap->id] = adap->fe_adap[0].fe->ops.init;
- adap->fe_adap[0].fe->ops.init = af9015_af9013_init;
-
- state->sleep[adap->id] = adap->fe_adap[0].fe->ops.sleep;
- adap->fe_adap[0].fe->ops.sleep = af9015_af9013_sleep;
- }
-
- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0;
-}
-
-static struct mt2060_config af9015_mt2060_config = {
- .i2c_address = 0xc0,
- .clock_out = 0,
-};
-
-static struct qt1010_config af9015_qt1010_config = {
- .i2c_address = 0xc4,
-};
-
-static struct tda18271_config af9015_tda18271_config = {
- .gate = TDA18271_GATE_DIGITAL,
- .small_i2c = TDA18271_16_BYTE_CHUNK_INIT,
-};
-
-static struct mxl5005s_config af9015_mxl5003_config = {
- .i2c_address = 0xc6,
- .if_freq = IF_FREQ_4570000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_DEFAULT,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .AgcMasterByte = 0x00,
-};
-
-static struct mxl5005s_config af9015_mxl5005_config = {
- .i2c_address = 0xc6,
- .if_freq = IF_FREQ_4570000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_OFF,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .AgcMasterByte = 0x00,
-};
-
-static struct mc44s803_config af9015_mc44s803_config = {
- .i2c_address = 0xc0,
- .dig_out = 1,
-};
-
-static struct tda18218_config af9015_tda18218_config = {
- .i2c_address = 0xc0,
- .i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */
-};
-
-static struct mxl5007t_config af9015_mxl5007t_config = {
- .xtal_freq_hz = MxL_XTAL_24_MHZ,
- .if_freq_hz = MxL_IF_4_57_MHZ,
-};
-
-static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct af9015_state *state = adap->dev->priv;
- deb_info("%s:\n", __func__);
-
- switch (af9015_af9013_config[adap->id].tuner) {
- case AF9013_TUNER_MT2060:
- case AF9013_TUNER_MT2060_2:
- ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &af9015_mt2060_config,
- af9015_config.mt2060_if1[adap->id])
- == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_QT1010:
- case AF9013_TUNER_QT1010A:
- ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- &af9015_qt1010_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_TDA18271:
- ret = dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0xc0,
- &adap->dev->i2c_adap,
- &af9015_tda18271_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_TDA18218:
- ret = dvb_attach(tda18218_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- &af9015_tda18218_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_MXL5003D:
- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_MXL5005D:
- case AF9013_TUNER_MXL5005R:
- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_ENV77H11D5:
- ret = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0xc0,
- &adap->dev->i2c_adap,
- DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_MC44S803:
- ret = dvb_attach(mc44s803_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- &af9015_mc44s803_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_MXL5007T:
- ret = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap,
- 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
- break;
- case AF9013_TUNER_UNKNOWN:
- default:
- ret = -ENODEV;
- err("Unknown tuner id:%d",
- af9015_af9013_config[adap->id].tuner);
- }
-
- if (adap->fe_adap[0].fe->ops.tuner_ops.init) {
- state->tuner_init[adap->id] =
- adap->fe_adap[0].fe->ops.tuner_ops.init;
- adap->fe_adap[0].fe->ops.tuner_ops.init = af9015_tuner_init;
- }
-
- if (adap->fe_adap[0].fe->ops.tuner_ops.sleep) {
- state->tuner_sleep[adap->id] =
- adap->fe_adap[0].fe->ops.tuner_ops.sleep;
- adap->fe_adap[0].fe->ops.tuner_ops.sleep = af9015_tuner_sleep;
- }
-
- return ret;
-}
-
-enum af9015_usb_table_entry {
- AFATECH_9015,
- AFATECH_9016,
- WINFAST_DTV_GOLD,
- PINNACLE_PCTV_71E,
- KWORLD_PLUSTV_399U,
- TINYTWIN,
- AZUREWAVE_TU700,
- TERRATEC_AF9015,
- KWORLD_PLUSTV_PC160,
- AVERTV_VOLAR_X,
- XTENSIONS_380U,
- MSI_DIGIVOX_DUO,
- AVERTV_VOLAR_X_REV2,
- TELESTAR_STARSTICK_2,
- AVERMEDIA_A309_USB,
- MSI_DIGIVOX_MINI_III,
- KWORLD_E396,
- KWORLD_E39B,
- KWORLD_E395,
- TREKSTOR_DVBT,
- AVERTV_A850,
- AVERTV_A805,
- CONCEPTRONIC_CTVDIGRCU,
- KWORLD_MC810,
- GENIUS_TVGO_DVB_T03,
- KWORLD_399U_2,
- KWORLD_PC160_T,
- SVEON_STV20,
- TINYTWIN_2,
- WINFAST_DTV2000DS,
- KWORLD_UB383_T,
- KWORLD_E39A,
- AVERMEDIA_A815M,
- CINERGY_T_STICK_RC,
- CINERGY_T_DUAL_RC,
- AVERTV_A850T,
- TINYTWIN_3,
- SVEON_STV22,
-};
-
-static struct usb_device_id af9015_usb_table[] = {
- [AFATECH_9015] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)},
- [AFATECH_9016] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)},
- [WINFAST_DTV_GOLD] = {
- USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)},
- [PINNACLE_PCTV_71E] = {
- USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)},
- [KWORLD_PLUSTV_399U] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)},
- [TINYTWIN] = {
- USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)},
- [AZUREWAVE_TU700] = {
- USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)},
- [TERRATEC_AF9015] = {
- USB_DEVICE(USB_VID_TERRATEC,
- USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
- [KWORLD_PLUSTV_PC160] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)},
- [AVERTV_VOLAR_X] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
- [XTENSIONS_380U] = {
- USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
- [MSI_DIGIVOX_DUO] = {
- USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)},
- [AVERTV_VOLAR_X_REV2] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
- [TELESTAR_STARSTICK_2] = {
- USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
- [AVERMEDIA_A309_USB] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
- [MSI_DIGIVOX_MINI_III] = {
- USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
- [KWORLD_E396] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
- [KWORLD_E39B] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)},
- [KWORLD_E395] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)},
- [TREKSTOR_DVBT] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)},
- [AVERTV_A850] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)},
- [AVERTV_A805] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)},
- [CONCEPTRONIC_CTVDIGRCU] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)},
- [KWORLD_MC810] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)},
- [GENIUS_TVGO_DVB_T03] = {
- USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)},
- [KWORLD_399U_2] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)},
- [KWORLD_PC160_T] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)},
- [SVEON_STV20] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)},
- [TINYTWIN_2] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)},
- [WINFAST_DTV2000DS] = {
- USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)},
- [KWORLD_UB383_T] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)},
- [KWORLD_E39A] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)},
- [AVERMEDIA_A815M] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)},
- [CINERGY_T_STICK_RC] = {
- USB_DEVICE(USB_VID_TERRATEC,
- USB_PID_TERRATEC_CINERGY_T_STICK_RC)},
- [CINERGY_T_DUAL_RC] = {
- USB_DEVICE(USB_VID_TERRATEC,
- USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)},
- [AVERTV_A850T] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
- [TINYTWIN_3] = {
- USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)},
- [SVEON_STV22] = {
- USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)},
- { }
-};
-MODULE_DEVICE_TABLE(usb, af9015_usb_table);
-
-#define AF9015_RC_INTERVAL 500
-static struct dvb_usb_device_properties af9015_properties[] = {
- {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = af9015_download_firmware,
- .firmware = "dvb-usb-af9015.fw",
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct af9015_state),
-
- .num_adapters = 2,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
- },
- }
- },
- },
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize = TS_USB20_FRAME_SIZE,
- }
- }
- },
- }
- },
- }
- },
-
- .identify_state = af9015_identify_state,
-
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "af9015",
- .rc_query = af9015_rc_query,
- .rc_interval = AF9015_RC_INTERVAL,
- .allowed_protos = RC_TYPE_NEC,
- },
-
- .i2c_algo = &af9015_i2c_algo,
-
- .num_device_descs = 12, /* check max from dvb-usb.h */
- .devices = {
- {
- .name = "Afatech AF9015 DVB-T USB2.0 stick",
- .cold_ids = {
- &af9015_usb_table[AFATECH_9015],
- &af9015_usb_table[AFATECH_9016],
- },
- }, {
- .name = "Leadtek WinFast DTV Dongle Gold",
- .cold_ids = {
- &af9015_usb_table[WINFAST_DTV_GOLD],
- },
- }, {
- .name = "Pinnacle PCTV 71e",
- .cold_ids = {
- &af9015_usb_table[PINNACLE_PCTV_71E],
- },
- }, {
- .name = "KWorld PlusTV Dual DVB-T Stick " \
- "(DVB-T 399U)",
- .cold_ids = {
- &af9015_usb_table[KWORLD_PLUSTV_399U],
- &af9015_usb_table[KWORLD_399U_2],
- },
- }, {
- .name = "DigitalNow TinyTwin DVB-T Receiver",
- .cold_ids = {
- &af9015_usb_table[TINYTWIN],
- &af9015_usb_table[TINYTWIN_2],
- &af9015_usb_table[TINYTWIN_3],
- },
- }, {
- .name = "TwinHan AzureWave AD-TU700(704J)",
- .cold_ids = {
- &af9015_usb_table[AZUREWAVE_TU700],
- },
- }, {
- .name = "TerraTec Cinergy T USB XE",
- .cold_ids = {
- &af9015_usb_table[TERRATEC_AF9015],
- },
- }, {
- .name = "KWorld PlusTV Dual DVB-T PCI " \
- "(DVB-T PC160-2T)",
- .cold_ids = {
- &af9015_usb_table[KWORLD_PLUSTV_PC160],
- },
- }, {
- .name = "AVerMedia AVerTV DVB-T Volar X",
- .cold_ids = {
- &af9015_usb_table[AVERTV_VOLAR_X],
- },
- }, {
- .name = "TerraTec Cinergy T Stick RC",
- .cold_ids = {
- &af9015_usb_table[CINERGY_T_STICK_RC],
- },
- }, {
- .name = "TerraTec Cinergy T Stick Dual RC",
- .cold_ids = {
- &af9015_usb_table[CINERGY_T_DUAL_RC],
- },
- }, {
- .name = "AverMedia AVerTV Red HD+ (A850T)",
- .cold_ids = {
- &af9015_usb_table[AVERTV_A850T],
- },
- },
- }
- }, {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = af9015_download_firmware,
- .firmware = "dvb-usb-af9015.fw",
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct af9015_state),
-
- .num_adapters = 2,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
- },
- }
- },
- },
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize = TS_USB20_FRAME_SIZE,
- }
- }
- },
- }
- },
- }
- },
-
- .identify_state = af9015_identify_state,
-
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "af9015",
- .rc_query = af9015_rc_query,
- .rc_interval = AF9015_RC_INTERVAL,
- .allowed_protos = RC_TYPE_NEC,
- },
-
- .i2c_algo = &af9015_i2c_algo,
-
- .num_device_descs = 10, /* check max from dvb-usb.h */
- .devices = {
- {
- .name = "Xtensions XD-380",
- .cold_ids = {
- &af9015_usb_table[XTENSIONS_380U],
- },
- }, {
- .name = "MSI DIGIVOX Duo",
- .cold_ids = {
- &af9015_usb_table[MSI_DIGIVOX_DUO],
- },
- }, {
- .name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
- .cold_ids = {
- &af9015_usb_table[AVERTV_VOLAR_X_REV2],
- },
- }, {
- .name = "Telestar Starstick 2",
- .cold_ids = {
- &af9015_usb_table[TELESTAR_STARSTICK_2],
- },
- }, {
- .name = "AVerMedia A309",
- .cold_ids = {
- &af9015_usb_table[AVERMEDIA_A309_USB],
- },
- }, {
- .name = "MSI Digi VOX mini III",
- .cold_ids = {
- &af9015_usb_table[MSI_DIGIVOX_MINI_III],
- },
- }, {
- .name = "KWorld USB DVB-T TV Stick II " \
- "(VS-DVB-T 395U)",
- .cold_ids = {
- &af9015_usb_table[KWORLD_E396],
- &af9015_usb_table[KWORLD_E39B],
- &af9015_usb_table[KWORLD_E395],
- &af9015_usb_table[KWORLD_E39A],
- },
- }, {
- .name = "TrekStor DVB-T USB Stick",
- .cold_ids = {
- &af9015_usb_table[TREKSTOR_DVBT],
- },
- }, {
- .name = "AverMedia AVerTV Volar Black HD " \
- "(A850)",
- .cold_ids = {
- &af9015_usb_table[AVERTV_A850],
- },
- }, {
- .name = "Sveon STV22 Dual USB DVB-T Tuner HDTV",
- .cold_ids = {
- &af9015_usb_table[SVEON_STV22],
- },
- },
- }
- }, {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = af9015_download_firmware,
- .firmware = "dvb-usb-af9015.fw",
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct af9015_state),
-
- .num_adapters = 2,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
- },
- }
- },
- },
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize = TS_USB20_FRAME_SIZE,
- }
- }
- },
- }
- },
- }
- },
-
- .identify_state = af9015_identify_state,
-
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "af9015",
- .rc_query = af9015_rc_query,
- .rc_interval = AF9015_RC_INTERVAL,
- .allowed_protos = RC_TYPE_NEC,
- },
-
- .i2c_algo = &af9015_i2c_algo,
-
- .num_device_descs = 9, /* check max from dvb-usb.h */
- .devices = {
- {
- .name = "AverMedia AVerTV Volar GPS 805 (A805)",
- .cold_ids = {
- &af9015_usb_table[AVERTV_A805],
- },
- }, {
- .name = "Conceptronic USB2.0 DVB-T CTVDIGRCU " \
- "V3.0",
- .cold_ids = {
- &af9015_usb_table[CONCEPTRONIC_CTVDIGRCU],
- },
- }, {
- .name = "KWorld Digial MC-810",
- .cold_ids = {
- &af9015_usb_table[KWORLD_MC810],
- },
- }, {
- .name = "Genius TVGo DVB-T03",
- .cold_ids = {
- &af9015_usb_table[GENIUS_TVGO_DVB_T03],
- },
- }, {
- .name = "KWorld PlusTV DVB-T PCI Pro Card " \
- "(DVB-T PC160-T)",
- .cold_ids = {
- &af9015_usb_table[KWORLD_PC160_T],
- },
- }, {
- .name = "Sveon STV20 Tuner USB DVB-T HDTV",
- .cold_ids = {
- &af9015_usb_table[SVEON_STV20],
- },
- }, {
- .name = "Leadtek WinFast DTV2000DS",
- .cold_ids = {
- &af9015_usb_table[WINFAST_DTV2000DS],
- },
- }, {
- .name = "KWorld USB DVB-T Stick Mobile " \
- "(UB383-T)",
- .cold_ids = {
- &af9015_usb_table[KWORLD_UB383_T],
- },
- }, {
- .name = "AverMedia AVerTV Volar M (A815Mac)",
- .cold_ids = {
- &af9015_usb_table[AVERMEDIA_A815M],
- },
- },
- }
- },
-};
-
-static int af9015_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret = 0;
- struct dvb_usb_device *d = NULL;
- struct usb_device *udev = interface_to_usbdev(intf);
- u8 i;
-
- deb_info("%s: interface:%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- /* interface 0 is used by DVB-T receiver and
- interface 1 is for remote controller (HID) */
- if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
- ret = af9015_read_config(udev);
- if (ret)
- return ret;
-
- for (i = 0; i < af9015_properties_count; i++) {
- ret = dvb_usb_device_init(intf, &af9015_properties[i],
- THIS_MODULE, &d, adapter_nr);
- if (!ret)
- break;
- if (ret != -ENODEV)
- return ret;
- }
- if (ret)
- return ret;
-
- if (d)
- ret = af9015_init(d);
- }
-
- return ret;
-}
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver af9015_usb_driver = {
- .name = "dvb_usb_af9015",
- .probe = af9015_usb_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = af9015_usb_table,
-};
-
-module_usb_driver(af9015_usb_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Afatech AF9015 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
deleted file mode 100644
index 2f68419e899b..000000000000
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- *
- * Thanks to Afatech who kindly provided information.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _DVB_USB_AF9015_H_
-#define _DVB_USB_AF9015_H_
-
-#define DVB_USB_LOG_PREFIX "af9015"
-#include "dvb-usb.h"
-
-#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
-#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args)
-#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
-#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args)
-#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args)
-#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args)
-
-#define AF9015_I2C_EEPROM 0xa0
-#define AF9015_I2C_DEMOD 0x38
-#define AF9015_USB_TIMEOUT 2000
-
-/* EEPROM locations */
-#define AF9015_EEPROM_IR_MODE 0x18
-#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
-#define AF9015_EEPROM_TS_MODE 0x31
-#define AF9015_EEPROM_DEMOD2_I2C 0x32
-
-#define AF9015_EEPROM_SAW_BW1 0x35
-#define AF9015_EEPROM_XTAL_TYPE1 0x36
-#define AF9015_EEPROM_SPEC_INV1 0x37
-#define AF9015_EEPROM_IF1L 0x38
-#define AF9015_EEPROM_IF1H 0x39
-#define AF9015_EEPROM_MT2060_IF1L 0x3a
-#define AF9015_EEPROM_MT2060_IF1H 0x3b
-#define AF9015_EEPROM_TUNER_ID1 0x3c
-
-#define AF9015_EEPROM_SAW_BW2 0x45
-#define AF9015_EEPROM_XTAL_TYPE2 0x46
-#define AF9015_EEPROM_SPEC_INV2 0x47
-#define AF9015_EEPROM_IF2L 0x48
-#define AF9015_EEPROM_IF2H 0x49
-#define AF9015_EEPROM_MT2060_IF2L 0x4a
-#define AF9015_EEPROM_MT2060_IF2H 0x4b
-#define AF9015_EEPROM_TUNER_ID2 0x4c
-
-#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
-
-struct req_t {
- u8 cmd; /* [0] */
- /* seq */ /* [1] */
- u8 i2c_addr; /* [2] */
- u16 addr; /* [3|4] */
- u8 mbox; /* [5] */
- u8 addr_len; /* [6] */
- u8 data_len; /* [7] */
- u8 *data;
-};
-
-enum af9015_cmd {
- GET_CONFIG = 0x10,
- DOWNLOAD_FIRMWARE = 0x11,
- BOOT = 0x13,
- READ_MEMORY = 0x20,
- WRITE_MEMORY = 0x21,
- READ_WRITE_I2C = 0x22,
- COPY_FIRMWARE = 0x23,
- RECONNECT_USB = 0x5a,
- WRITE_VIRTUAL_MEMORY = 0x26,
- GET_IR_CODE = 0x27,
- READ_I2C,
- WRITE_I2C,
-};
-
-enum af9015_ir_mode {
- AF9015_IR_MODE_DISABLED = 0,
- AF9015_IR_MODE_HID,
- AF9015_IR_MODE_RLC,
- AF9015_IR_MODE_RC6,
- AF9015_IR_MODE_POLLING, /* just guess */
-};
-
-struct af9015_state {
- u8 rc_repeat;
- u32 rc_keycode;
- u8 rc_last[4];
-
- /* for demod callback override */
- int (*set_frontend[2]) (struct dvb_frontend *fe);
- int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
- int (*init[2]) (struct dvb_frontend *fe);
- int (*sleep[2]) (struct dvb_frontend *fe);
- int (*tuner_init[2]) (struct dvb_frontend *fe);
- int (*tuner_sleep[2]) (struct dvb_frontend *fe);
-};
-
-struct af9015_config {
- u8 dual_mode:1;
- u16 mt2060_if1[2];
- u16 firmware_size;
- u16 firmware_checksum;
- u32 eeprom_sum;
-};
-
-enum af9015_remote {
- AF9015_REMOTE_NONE = 0,
-/* 1 */ AF9015_REMOTE_A_LINK_DTU_M,
- AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
- AF9015_REMOTE_MYGICTV_U718,
- AF9015_REMOTE_DIGITTRADE_DVB_T,
-/* 5 */ AF9015_REMOTE_AVERMEDIA_KS,
-};
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c
deleted file mode 100644
index e83b39d3993c..000000000000
--- a/drivers/media/dvb/dvb-usb/af9035.c
+++ /dev/null
@@ -1,1242 +0,0 @@
-/*
- * Afatech AF9035 DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "af9035.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-static DEFINE_MUTEX(af9035_usb_mutex);
-static struct dvb_usb_device_properties af9035_properties[2];
-static int af9035_properties_count = ARRAY_SIZE(af9035_properties);
-
-static u16 af9035_checksum(const u8 *buf, size_t len)
-{
- size_t i;
- u16 checksum = 0;
-
- for (i = 1; i < len; i++) {
- if (i % 2)
- checksum += buf[i] << 8;
- else
- checksum += buf[i];
- }
- checksum = ~checksum;
-
- return checksum;
-}
-
-static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req)
-{
-#define BUF_LEN 64
-#define REQ_HDR_LEN 4 /* send header size */
-#define ACK_HDR_LEN 3 /* rece header size */
-#define CHECKSUM_LEN 2
-#define USB_TIMEOUT 2000
-
- int ret, msg_len, act_len;
- u8 buf[BUF_LEN];
- static u8 seq; /* packet sequence number */
- u16 checksum, tmp_checksum;
-
- /* buffer overflow check */
- if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) ||
- req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) {
- pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__,
- req->wlen, req->rlen);
- return -EINVAL;
- }
-
- if (mutex_lock_interruptible(&af9035_usb_mutex) < 0)
- return -EAGAIN;
-
- buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1;
- buf[1] = req->mbox;
- buf[2] = req->cmd;
- buf[3] = seq++;
- if (req->wlen)
- memcpy(&buf[4], req->wbuf, req->wlen);
-
- /* calc and add checksum */
- checksum = af9035_checksum(buf, buf[0] - 1);
- buf[buf[0] - 1] = (checksum >> 8);
- buf[buf[0] - 0] = (checksum & 0xff);
-
- msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ;
-
- /* send req */
- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
- &act_len, USB_TIMEOUT);
- if (ret < 0)
- err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len);
- else
- if (act_len != msg_len)
- ret = -EIO; /* all data is not send */
- if (ret < 0)
- goto err_mutex_unlock;
-
- /* no ack for those packets */
- if (req->cmd == CMD_FW_DL)
- goto exit_mutex_unlock;
-
- /* receive ack and data if read req */
- msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN;
- ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
- &act_len, USB_TIMEOUT);
- if (ret < 0) {
- err("recv bulk message failed=%d", ret);
- ret = -EIO;
- goto err_mutex_unlock;
- }
-
- if (act_len != msg_len) {
- err("recv bulk message truncated (%d != %d)", act_len, msg_len);
- ret = -EIO;
- goto err_mutex_unlock;
- }
-
- /* verify checksum */
- checksum = af9035_checksum(buf, act_len - 2);
- tmp_checksum = (buf[act_len - 2] << 8) | buf[act_len - 1];
- if (tmp_checksum != checksum) {
- err("%s: command=%02x checksum mismatch (%04x != %04x)",
- __func__, req->cmd, tmp_checksum, checksum);
- ret = -EIO;
- goto err_mutex_unlock;
- }
-
- /* check status */
- if (buf[2]) {
- pr_debug("%s: command=%02x failed fw error=%d\n", __func__,
- req->cmd, buf[2]);
- ret = -EIO;
- goto err_mutex_unlock;
- }
-
- /* read request, copy returned data to return buf */
- if (req->rlen)
- memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen);
-
-err_mutex_unlock:
-exit_mutex_unlock:
- mutex_unlock(&af9035_usb_mutex);
-
- return ret;
-}
-
-/* write multiple registers */
-static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
-{
- u8 wbuf[6 + len];
- u8 mbox = (reg >> 16) & 0xff;
- struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
-
- wbuf[0] = len;
- wbuf[1] = 2;
- wbuf[2] = 0;
- wbuf[3] = 0;
- wbuf[4] = (reg >> 8) & 0xff;
- wbuf[5] = (reg >> 0) & 0xff;
- memcpy(&wbuf[6], val, len);
-
- return af9035_ctrl_msg(d->udev, &req);
-}
-
-/* read multiple registers */
-static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
-{
- u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff };
- u8 mbox = (reg >> 16) & 0xff;
- struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val };
-
- return af9035_ctrl_msg(d->udev, &req);
-}
-
-/* write single register */
-static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val)
-{
- return af9035_wr_regs(d, reg, &val, 1);
-}
-
-/* read single register */
-static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val)
-{
- return af9035_rd_regs(d, reg, val, 1);
-}
-
-/* write single register with mask */
-static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val,
- u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = af9035_rd_regs(d, reg, &tmp, 1);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return af9035_wr_regs(d, reg, &val, 1);
-}
-
-static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg msg[], int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct state *state = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- /*
- * I2C sub header is 5 bytes long. Meaning of those bytes are:
- * 0: data len
- * 1: I2C addr << 1
- * 2: reg addr len
- * byte 3 and 4 can be used as reg addr
- * 3: reg addr MSB
- * used when reg addr len is set to 2
- * 4: reg addr LSB
- * used when reg addr len is set to 1 or 2
- *
- * For the simplify we do not use register addr at all.
- * NOTE: As a firmware knows tuner type there is very small possibility
- * there could be some tuner I2C hacks done by firmware and this may
- * lead problems if firmware expects those bytes are used.
- */
- if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
- (msg[1].flags & I2C_M_RD)) {
- if (msg[0].len > 40 || msg[1].len > 40) {
- /* TODO: correct limits > 40 */
- ret = -EOPNOTSUPP;
- } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
- /* integrated demod */
- u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
- msg[0].buf[2];
- ret = af9035_rd_regs(d, reg, &msg[1].buf[0],
- msg[1].len);
- } else {
- /* I2C */
- u8 buf[5 + msg[0].len];
- struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
- buf, msg[1].len, msg[1].buf };
- buf[0] = msg[1].len;
- buf[1] = msg[0].addr << 1;
- buf[2] = 0x00; /* reg addr len */
- buf[3] = 0x00; /* reg addr MSB */
- buf[4] = 0x00; /* reg addr LSB */
- memcpy(&buf[5], msg[0].buf, msg[0].len);
- ret = af9035_ctrl_msg(d->udev, &req);
- }
- } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
- if (msg[0].len > 40) {
- /* TODO: correct limits > 40 */
- ret = -EOPNOTSUPP;
- } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
- /* integrated demod */
- u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
- msg[0].buf[2];
- ret = af9035_wr_regs(d, reg, &msg[0].buf[3],
- msg[0].len - 3);
- } else {
- /* I2C */
- u8 buf[5 + msg[0].len];
- struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf,
- 0, NULL };
- buf[0] = msg[0].len;
- buf[1] = msg[0].addr << 1;
- buf[2] = 0x00; /* reg addr len */
- buf[3] = 0x00; /* reg addr MSB */
- buf[4] = 0x00; /* reg addr LSB */
- memcpy(&buf[5], msg[0].buf, msg[0].len);
- ret = af9035_ctrl_msg(d->udev, &req);
- }
- } else {
- /*
- * We support only two kind of I2C transactions:
- * 1) 1 x read + 1 x write
- * 2) 1 x write
- */
- ret = -EOPNOTSUPP;
- }
-
- mutex_unlock(&d->i2c_mutex);
-
- if (ret < 0)
- return ret;
- else
- return num;
-}
-
-static u32 af9035_i2c_functionality(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm af9035_i2c_algo = {
- .master_xfer = af9035_i2c_master_xfer,
- .functionality = af9035_i2c_functionality,
-};
-
-#define AF9035_POLL 250
-static int af9035_rc_query(struct dvb_usb_device *d)
-{
- unsigned int key;
- unsigned char b[4];
- int ret;
- struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b };
-
- ret = af9035_ctrl_msg(d->udev, &req);
- if (ret < 0)
- goto err;
-
- if ((b[2] + b[3]) == 0xff) {
- if ((b[0] + b[1]) == 0xff) {
- /* NEC */
- key = b[0] << 8 | b[2];
- } else {
- /* ext. NEC */
- key = b[0] << 16 | b[1] << 8 | b[2];
- }
- } else {
- key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
- }
-
- rc_keydown(d->rc_dev, key, 0);
-
-err:
- /* ignore errors */
- return 0;
-}
-
-static int af9035_init(struct dvb_usb_device *d)
-{
- struct state *state = d->priv;
- int ret, i;
- u16 frame_size = 87 * 188 / 4;
- u8 packet_size = 512 / 4;
- struct reg_val_mask tab[] = {
- { 0x80f99d, 0x01, 0x01 },
- { 0x80f9a4, 0x01, 0x01 },
- { 0x00dd11, 0x00, 0x20 },
- { 0x00dd11, 0x00, 0x40 },
- { 0x00dd13, 0x00, 0x20 },
- { 0x00dd13, 0x00, 0x40 },
- { 0x00dd11, 0x20, 0x20 },
- { 0x00dd88, (frame_size >> 0) & 0xff, 0xff},
- { 0x00dd89, (frame_size >> 8) & 0xff, 0xff},
- { 0x00dd0c, packet_size, 0xff},
- { 0x00dd11, state->dual_mode << 6, 0x40 },
- { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff},
- { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff},
- { 0x00dd0d, packet_size, 0xff },
- { 0x80f9a3, 0x00, 0x01 },
- { 0x80f9cd, 0x00, 0x01 },
- { 0x80f99d, 0x00, 0x01 },
- { 0x80f9a4, 0x00, 0x01 },
- };
-
- pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n",
- __func__, d->udev->speed, frame_size, packet_size);
-
- /* init endpoints */
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret < 0)
- goto err;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9035_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- int ret;
- u8 wbuf[1] = { 1 };
- u8 rbuf[4];
- struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf,
- sizeof(rbuf), rbuf };
-
- ret = af9035_ctrl_msg(udev, &req);
- if (ret < 0)
- goto err;
-
- pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__,
- rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
- if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])
- *cold = 0;
- else
- *cold = 1;
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9035_download_firmware(struct usb_device *udev,
- const struct firmware *fw)
-{
- int ret, i, j, len;
- u8 wbuf[1];
- u8 rbuf[4];
- struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
- struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL };
- struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
- u8 hdr_core;
- u16 hdr_addr, hdr_data_len, hdr_checksum;
- #define MAX_DATA 58
- #define HDR_SIZE 7
-
- /*
- * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info!
- *
- * byte 0: MCS 51 core
- * There are two inside the AF9035 (1=Link and 2=OFDM) with separate
- * address spaces
- * byte 1-2: Big endian destination address
- * byte 3-4: Big endian number of data bytes following the header
- * byte 5-6: Big endian header checksum, apparently ignored by the chip
- * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256)
- */
-
- for (i = fw->size; i > HDR_SIZE;) {
- hdr_core = fw->data[fw->size - i + 0];
- hdr_addr = fw->data[fw->size - i + 1] << 8;
- hdr_addr |= fw->data[fw->size - i + 2] << 0;
- hdr_data_len = fw->data[fw->size - i + 3] << 8;
- hdr_data_len |= fw->data[fw->size - i + 4] << 0;
- hdr_checksum = fw->data[fw->size - i + 5] << 8;
- hdr_checksum |= fw->data[fw->size - i + 6] << 0;
-
- pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n",
- __func__, hdr_core, hdr_addr, hdr_data_len,
- hdr_checksum);
-
- if (((hdr_core != 1) && (hdr_core != 2)) ||
- (hdr_data_len > i)) {
- pr_debug("%s: bad firmware\n", __func__);
- break;
- }
-
- /* download begin packet */
- req.cmd = CMD_FW_DL_BEGIN;
- ret = af9035_ctrl_msg(udev, &req);
- if (ret < 0)
- goto err;
-
- /* download firmware packet(s) */
- for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) {
- len = j;
- if (len > MAX_DATA)
- len = MAX_DATA;
- req_fw_dl.wlen = len;
- req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i +
- HDR_SIZE + hdr_data_len - j];
- ret = af9035_ctrl_msg(udev, &req_fw_dl);
- if (ret < 0)
- goto err;
- }
-
- /* download end packet */
- req.cmd = CMD_FW_DL_END;
- ret = af9035_ctrl_msg(udev, &req);
- if (ret < 0)
- goto err;
-
- i -= hdr_data_len + HDR_SIZE;
-
- pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i);
- }
-
- /* firmware loaded, request boot */
- req.cmd = CMD_FW_BOOT;
- ret = af9035_ctrl_msg(udev, &req);
- if (ret < 0)
- goto err;
-
- /* ensure firmware starts */
- wbuf[0] = 1;
- ret = af9035_ctrl_msg(udev, &req_fw_ver);
- if (ret < 0)
- goto err;
-
- if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
- info("firmware did not run");
- ret = -ENODEV;
- goto err;
- }
-
- info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2],
- rbuf[3]);
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9035_download_firmware_it9135(struct usb_device *udev,
- const struct firmware *fw)
-{
- int ret, i, i_prev;
- u8 wbuf[1];
- u8 rbuf[4];
- struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
- struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL };
- struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
- #define HDR_SIZE 7
-
- /*
- * There seems to be following firmware header. Meaning of bytes 0-3
- * is unknown.
- *
- * 0: 3
- * 1: 0, 1
- * 2: 0
- * 3: 1, 2, 3
- * 4: addr MSB
- * 5: addr LSB
- * 6: count of data bytes ?
- */
-
- for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) {
- if (i == fw->size ||
- (fw->data[i + 0] == 0x03 &&
- (fw->data[i + 1] == 0x00 ||
- fw->data[i + 1] == 0x01) &&
- fw->data[i + 2] == 0x00)) {
- req_fw_dl.wlen = i - i_prev;
- req_fw_dl.wbuf = (u8 *) &fw->data[i_prev];
- i_prev = i;
- ret = af9035_ctrl_msg(udev, &req_fw_dl);
- if (ret < 0)
- goto err;
-
- pr_debug("%s: data uploaded=%d\n", __func__, i);
- }
- }
-
- /* firmware loaded, request boot */
- req.cmd = CMD_FW_BOOT;
- ret = af9035_ctrl_msg(udev, &req);
- if (ret < 0)
- goto err;
-
- /* ensure firmware starts */
- wbuf[0] = 1;
- ret = af9035_ctrl_msg(udev, &req_fw_ver);
- if (ret < 0)
- goto err;
-
- if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
- info("firmware did not run");
- ret = -ENODEV;
- goto err;
- }
-
- info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2],
- rbuf[3]);
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-/* abuse that callback as there is no better one for reading eeprom */
-static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
-{
- struct state *state = d->priv;
- int ret, i, eeprom_shift = 0;
- u8 tmp;
- u16 tmp16;
-
- /* check if there is dual tuners */
- ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp);
- if (ret < 0)
- goto err;
-
- state->dual_mode = tmp;
- pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode);
-
- for (i = 0; i < af9035_properties[0].num_adapters; i++) {
- /* tuner */
- ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp);
- if (ret < 0)
- goto err;
-
- state->af9033_config[i].tuner = tmp;
- pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp);
-
- switch (tmp) {
- case AF9033_TUNER_TUA9001:
- case AF9033_TUNER_FC0011:
- case AF9033_TUNER_MXL5007T:
- case AF9033_TUNER_TDA18218:
- state->af9033_config[i].spec_inv = 1;
- break;
- default:
- warn("tuner ID=%02x not supported, please report!",
- tmp);
- };
-
- /* tuner IF frequency */
- ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp);
- if (ret < 0)
- goto err;
-
- tmp16 = tmp;
-
- ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp);
- if (ret < 0)
- goto err;
-
- tmp16 |= tmp << 8;
-
- pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16);
-
- eeprom_shift = 0x10; /* shift for the 2nd tuner params */
- }
-
- /* get demod clock */
- ret = af9035_rd_reg(d, 0x00d800, &tmp);
- if (ret < 0)
- goto err;
-
- tmp = (tmp >> 0) & 0x0f;
-
- for (i = 0; i < af9035_properties[0].num_adapters; i++)
- state->af9033_config[i].clock = clock_lut[tmp];
-
- ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp);
- if (ret < 0)
- goto err;
- pr_debug("%s: ir_mode=%02x\n", __func__, tmp);
-
- /* don't activate rc if in HID mode or if not available */
- if (tmp == 5) {
- ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp);
- if (ret < 0)
- goto err;
- pr_debug("%s: ir_type=%02x\n", __func__, tmp);
-
- switch (tmp) {
- case 0: /* NEC */
- default:
- d->props.rc.core.protocol = RC_TYPE_NEC;
- d->props.rc.core.allowed_protos = RC_TYPE_NEC;
- break;
- case 1: /* RC6 */
- d->props.rc.core.protocol = RC_TYPE_RC6;
- d->props.rc.core.allowed_protos = RC_TYPE_RC6;
- break;
- }
- d->props.rc.core.rc_query = af9035_rc_query;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-/* abuse that callback as there is no better one for reading eeprom */
-static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6])
-{
- struct state *state = d->priv;
- int ret, i;
- u8 tmp;
-
- state->dual_mode = false;
-
- /* get demod clock */
- ret = af9035_rd_reg(d, 0x00d800, &tmp);
- if (ret < 0)
- goto err;
-
- tmp = (tmp >> 0) & 0x0f;
-
- for (i = 0; i < af9035_properties[0].num_adapters; i++)
- state->af9033_config[i].clock = clock_lut_it9135[tmp];
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
- int cmd, int arg)
-{
- int ret;
-
- switch (cmd) {
- case FC0011_FE_CALLBACK_POWER:
- /* Tuner enable */
- ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1);
- if (ret < 0)
- goto err;
-
- /* LED */
- ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1);
- if (ret < 0)
- goto err;
-
- usleep_range(10000, 50000);
- break;
- case FC0011_FE_CALLBACK_RESET:
- ret = af9035_wr_reg(d, 0xd8e9, 1);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg(d, 0xd8e8, 1);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg(d, 0xd8e7, 1);
- if (ret < 0)
- goto err;
-
- usleep_range(10000, 20000);
-
- ret = af9035_wr_reg(d, 0xd8e7, 0);
- if (ret < 0)
- goto err;
-
- usleep_range(10000, 20000);
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
-{
- struct state *state = d->priv;
-
- switch (state->af9033_config[0].tuner) {
- case AF9033_TUNER_FC0011:
- return af9035_fc0011_tuner_callback(d, cmd, arg);
- default:
- break;
- }
-
- return -ENODEV;
-}
-
-static int af9035_frontend_callback(void *adapter_priv, int component,
- int cmd, int arg)
-{
- struct i2c_adapter *adap = adapter_priv;
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
-
- switch (component) {
- case DVB_FRONTEND_COMPONENT_TUNER:
- return af9035_tuner_callback(d, cmd, arg);
- default:
- break;
- }
-
- return -EINVAL;
-}
-
-static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct state *state = adap->dev->priv;
- int ret;
-
- if (!state->af9033_config[adap->id].tuner) {
- /* unsupported tuner */
- ret = -ENODEV;
- goto err;
- }
-
- if (adap->id == 0) {
- state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB;
- state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL;
-
- ret = af9035_wr_reg(adap->dev, 0x00417f,
- state->af9033_config[1].i2c_addr);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg(adap->dev, 0x00d81a,
- state->dual_mode);
- if (ret < 0)
- goto err;
- }
-
- /* attach demodulator */
- adap->fe_adap[0].fe = dvb_attach(af9033_attach,
- &state->af9033_config[adap->id], &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- /* disable I2C-gate */
- adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
- adap->fe_adap[0].fe->callback = af9035_frontend_callback;
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static struct tua9001_config af9035_tua9001_config = {
- .i2c_addr = 0x60,
-};
-
-static const struct fc0011_config af9035_fc0011_config = {
- .i2c_address = 0x60,
-};
-
-static struct mxl5007t_config af9035_mxl5007t_config = {
- .xtal_freq_hz = MxL_XTAL_24_MHZ,
- .if_freq_hz = MxL_IF_4_57_MHZ,
- .invert_if = 0,
- .loop_thru_enable = 0,
- .clk_out_enable = 0,
- .clk_out_amp = MxL_CLKOUT_AMP_0_94V,
-};
-
-static struct tda18218_config af9035_tda18218_config = {
- .i2c_address = 0x60,
- .i2c_wr_max = 21,
-};
-
-static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
-{
- struct state *state = adap->dev->priv;
- int ret;
- struct dvb_frontend *fe;
-
- switch (state->af9033_config[adap->id].tuner) {
- case AF9033_TUNER_TUA9001:
- /* AF9035 gpiot3 = TUA9001 RESETN
- AF9035 gpiot2 = TUA9001 RXEN */
-
- /* configure gpiot2 and gpiot2 as output */
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8ec, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8ed, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8e8, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8e9, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- /* reset tuner */
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x00, 0x01);
- if (ret < 0)
- goto err;
-
- usleep_range(2000, 20000);
-
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- /* activate tuner RX */
- /* TODO: use callback for TUA9001 RXEN */
- ret = af9035_wr_reg_mask(adap->dev, 0x00d8eb, 0x01, 0x01);
- if (ret < 0)
- goto err;
-
- /* attach tuner */
- fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &af9035_tua9001_config);
- break;
- case AF9033_TUNER_FC0011:
- fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &af9035_fc0011_config);
- break;
- case AF9033_TUNER_MXL5007T:
- ret = af9035_wr_reg(adap->dev, 0x00d8e0, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8e1, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8df, 0);
- if (ret < 0)
- goto err;
-
- msleep(30);
-
- ret = af9035_wr_reg(adap->dev, 0x00d8df, 1);
- if (ret < 0)
- goto err;
-
- msleep(300);
-
- ret = af9035_wr_reg(adap->dev, 0x00d8c0, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8c1, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8bf, 0);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8b4, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8b5, 1);
- if (ret < 0)
- goto err;
- ret = af9035_wr_reg(adap->dev, 0x00d8b3, 1);
- if (ret < 0)
- goto err;
-
- /* attach tuner */
- fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config);
- break;
- case AF9033_TUNER_TDA18218:
- /* attach tuner */
- fe = dvb_attach(tda18218_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &af9035_tda18218_config);
- break;
- default:
- fe = NULL;
- }
-
- if (fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-enum af9035_id_entry {
- AF9035_15A4_9035,
- AF9035_15A4_1000,
- AF9035_15A4_1001,
- AF9035_15A4_1002,
- AF9035_15A4_1003,
- AF9035_0CCD_0093,
- AF9035_07CA_A835,
- AF9035_07CA_B835,
- AF9035_07CA_1867,
- AF9035_07CA_A867,
- AF9035_07CA_0825,
-};
-
-static struct usb_device_id af9035_id[] = {
- [AF9035_15A4_9035] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035)},
- [AF9035_15A4_1000] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000)},
- [AF9035_15A4_1001] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001)},
- [AF9035_15A4_1002] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002)},
- [AF9035_15A4_1003] = {
- USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003)},
- [AF9035_0CCD_0093] = {
- USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)},
- [AF9035_07CA_A835] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)},
- [AF9035_07CA_B835] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)},
- [AF9035_07CA_1867] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)},
- [AF9035_07CA_A867] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)},
- [AF9035_07CA_0825] = {
- USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR)},
- {},
-};
-
-MODULE_DEVICE_TABLE(usb, af9035_id);
-
-static struct dvb_usb_device_properties af9035_properties[] = {
- {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = af9035_download_firmware,
- .firmware = "dvb-usb-af9035-02.fw",
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = af9035_frontend_attach,
- .tuner_attach = af9035_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
- .u = {
- .bulk = {
- .buffersize = (87 * 188),
- }
- }
- }
- }
- }
- }
- },
-
- .identify_state = af9035_identify_state,
- .read_mac_address = af9035_read_mac_address,
-
- .i2c_algo = &af9035_i2c_algo,
-
- .rc.core = {
- .protocol = RC_TYPE_UNKNOWN,
- .module_name = "af9035",
- .rc_query = NULL,
- .rc_interval = AF9035_POLL,
- .allowed_protos = RC_TYPE_UNKNOWN,
- .rc_codes = RC_MAP_EMPTY,
- },
- .num_device_descs = 5,
- .devices = {
- {
- .name = "Afatech AF9035 reference design",
- .cold_ids = {
- &af9035_id[AF9035_15A4_9035],
- &af9035_id[AF9035_15A4_1000],
- &af9035_id[AF9035_15A4_1001],
- &af9035_id[AF9035_15A4_1002],
- &af9035_id[AF9035_15A4_1003],
- },
- }, {
- .name = "TerraTec Cinergy T Stick",
- .cold_ids = {
- &af9035_id[AF9035_0CCD_0093],
- },
- }, {
- .name = "AVerMedia AVerTV Volar HD/PRO (A835)",
- .cold_ids = {
- &af9035_id[AF9035_07CA_A835],
- &af9035_id[AF9035_07CA_B835],
- },
- }, {
- .name = "AVerMedia HD Volar (A867)",
- .cold_ids = {
- &af9035_id[AF9035_07CA_1867],
- &af9035_id[AF9035_07CA_A867],
- },
- }, {
- .name = "AVerMedia Twinstar (A825)",
- .cold_ids = {
- &af9035_id[AF9035_07CA_0825],
- },
- },
- }
- },
- {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = af9035_download_firmware_it9135,
- .firmware = "dvb-usb-it9135-01.fw",
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = af9035_frontend_attach,
- .tuner_attach = af9035_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
- .u = {
- .bulk = {
- .buffersize = (87 * 188),
- }
- }
- }
- }
- }
- }
- },
-
- .identify_state = af9035_identify_state,
- .read_mac_address = af9035_read_mac_address_it9135,
-
- .i2c_algo = &af9035_i2c_algo,
-
- .num_device_descs = 0, /* disabled as no support for IT9135 */
- .devices = {
- {
- .name = "ITE Tech. IT9135 reference design",
- },
- }
- },
-};
-
-static int af9035_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret, i;
- struct dvb_usb_device *d = NULL;
- struct usb_device *udev;
- bool found;
-
- pr_debug("%s: interface=%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- /* interface 0 is used by DVB-T receiver and
- interface 1 is for remote controller (HID) */
- if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
- return 0;
-
- /* Dynamic USB ID support. Replaces first device ID with current one. */
- udev = interface_to_usbdev(intf);
-
- for (i = 0, found = false; i < ARRAY_SIZE(af9035_id) - 1; i++) {
- if (af9035_id[i].idVendor ==
- le16_to_cpu(udev->descriptor.idVendor) &&
- af9035_id[i].idProduct ==
- le16_to_cpu(udev->descriptor.idProduct)) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- pr_debug("%s: using dynamic ID %04x:%04x\n", __func__,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
- af9035_properties[0].devices[0].cold_ids[0]->idVendor =
- le16_to_cpu(udev->descriptor.idVendor);
- af9035_properties[0].devices[0].cold_ids[0]->idProduct =
- le16_to_cpu(udev->descriptor.idProduct);
- }
-
-
- for (i = 0; i < af9035_properties_count; i++) {
- ret = dvb_usb_device_init(intf, &af9035_properties[i],
- THIS_MODULE, &d, adapter_nr);
-
- if (ret == -ENODEV)
- continue;
- else
- break;
- }
-
- if (ret < 0)
- goto err;
-
- if (d) {
- ret = af9035_init(d);
- if (ret < 0)
- goto err;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver af9035_usb_driver = {
- .name = "dvb_usb_af9035",
- .probe = af9035_usb_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = af9035_id,
-};
-
-module_usb_driver(af9035_usb_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Afatech AF9035 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h
deleted file mode 100644
index 481a1a43dd2a..000000000000
--- a/drivers/media/dvb/dvb-usb/af9035.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Afatech AF9035 DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef AF9035_H
-#define AF9035_H
-
-/* prefix for dvb-usb log writings */
-#define DVB_USB_LOG_PREFIX "af9035"
-
-#include "dvb-usb.h"
-#include "af9033.h"
-#include "tua9001.h"
-#include "fc0011.h"
-#include "mxl5007t.h"
-#include "tda18218.h"
-
-struct reg_val {
- u32 reg;
- u8 val;
-};
-
-struct reg_val_mask {
- u32 reg;
- u8 val;
- u8 mask;
-};
-
-struct usb_req {
- u8 cmd;
- u8 mbox;
- u8 wlen;
- u8 *wbuf;
- u8 rlen;
- u8 *rbuf;
-};
-
-struct state {
- bool dual_mode;
-
- struct af9033_config af9033_config[2];
-};
-
-u32 clock_lut[] = {
- 20480000, /* FPGA */
- 16384000, /* 16.38 MHz */
- 20480000, /* 20.48 MHz */
- 36000000, /* 36.00 MHz */
- 30000000, /* 30.00 MHz */
- 26000000, /* 26.00 MHz */
- 28000000, /* 28.00 MHz */
- 32000000, /* 32.00 MHz */
- 34000000, /* 34.00 MHz */
- 24000000, /* 24.00 MHz */
- 22000000, /* 22.00 MHz */
- 12000000, /* 12.00 MHz */
-};
-
-u32 clock_lut_it9135[] = {
- 12000000, /* 12.00 MHz */
- 20480000, /* 20.48 MHz */
- 36000000, /* 36.00 MHz */
- 30000000, /* 30.00 MHz */
- 26000000, /* 26.00 MHz */
- 28000000, /* 28.00 MHz */
- 32000000, /* 32.00 MHz */
- 34000000, /* 34.00 MHz */
- 24000000, /* 24.00 MHz */
- 22000000, /* 22.00 MHz */
-};
-
-/* EEPROM locations */
-#define EEPROM_IR_MODE 0x430d
-#define EEPROM_DUAL_MODE 0x4326
-#define EEPROM_IR_TYPE 0x4329
-#define EEPROM_1_IFFREQ_L 0x432d
-#define EEPROM_1_IFFREQ_H 0x432e
-#define EEPROM_1_TUNER_ID 0x4331
-#define EEPROM_2_IFFREQ_L 0x433d
-#define EEPROM_2_IFFREQ_H 0x433e
-#define EEPROM_2_TUNER_ID 0x4341
-
-/* USB commands */
-#define CMD_MEM_RD 0x00
-#define CMD_MEM_WR 0x01
-#define CMD_I2C_RD 0x02
-#define CMD_I2C_WR 0x03
-#define CMD_IR_GET 0x18
-#define CMD_FW_DL 0x21
-#define CMD_FW_QUERYINFO 0x22
-#define CMD_FW_BOOT 0x23
-#define CMD_FW_DL_BEGIN 0x24
-#define CMD_FW_DL_END 0x25
-#define CMD_FW_SCATTER_WR 0x29
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
deleted file mode 100644
index 03c28655af1b..000000000000
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ /dev/null
@@ -1,1406 +0,0 @@
-/*
- * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * TODO:
- * - add smart card reader support for Conditional Access (CA)
- *
- * Card reader in Anysee is nothing more than ISO 7816 card reader.
- * There is no hardware CAM in any Anysee device sold.
- * In my understanding it should be implemented by making own module
- * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
- * module registers serial interface that can be used to communicate
- * with any ISO 7816 smart card.
- *
- * Any help according to implement serial smart card reader support
- * is highly welcome!
- */
-
-#include "anysee.h"
-#include "tda1002x.h"
-#include "mt352.h"
-#include "mt352_priv.h"
-#include "zl10353.h"
-#include "tda18212.h"
-#include "cx24116.h"
-#include "stv0900.h"
-#include "stv6110.h"
-#include "isl6423.h"
-#include "cxd2820r.h"
-
-/* debug */
-static int dvb_usb_anysee_debug;
-module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-static int dvb_usb_anysee_delsys;
-module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644);
-MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)");
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static DEFINE_MUTEX(anysee_usb_mutex);
-
-static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
- u8 *rbuf, u8 rlen)
-{
- struct anysee_state *state = d->priv;
- int act_len, ret, i;
- u8 buf[64];
-
- memcpy(&buf[0], sbuf, slen);
- buf[60] = state->seq++;
-
- if (mutex_lock_interruptible(&anysee_usb_mutex) < 0)
- return -EAGAIN;
-
- deb_xfer(">>> ");
- debug_dump(buf, slen, deb_xfer);
-
- /* We need receive one message more after dvb_usb_generic_rw due
- to weird transaction flow, which is 1 x send + 2 x receive. */
- ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
- if (ret)
- goto error_unlock;
-
- /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32
- * (EPIPE, Broken pipe). Function supports currently msleep() as a
- * parameter but I would not like to use it, since according to
- * Documentation/timers/timers-howto.txt it should not be used such
- * short, under < 20ms, sleeps. Repeating failed message would be
- * better choice as not to add unwanted delays...
- * Fixing that correctly is one of those or both;
- * 1) use repeat if possible
- * 2) add suitable delay
- */
-
- /* get answer, retry few times if error returned */
- for (i = 0; i < 3; i++) {
- /* receive 2nd answer */
- ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
- d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
- &act_len, 2000);
-
- if (ret) {
- deb_info("%s: recv bulk message failed: %d",
- __func__, ret);
- } else {
- deb_xfer("<<< ");
- debug_dump(buf, rlen, deb_xfer);
-
- if (buf[63] != 0x4f)
- deb_info("%s: cmd failed\n", __func__);
-
- break;
- }
- }
-
- if (ret) {
- /* all retries failed, it is fatal */
- err("%s: recv bulk message failed: %d", __func__, ret);
- goto error_unlock;
- }
-
- /* read request, copy returned data to return buf */
- if (rbuf && rlen)
- memcpy(rbuf, buf, rlen);
-
-error_unlock:
- mutex_unlock(&anysee_usb_mutex);
-
- return ret;
-}
-
-static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
-{
- u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01};
- int ret;
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1);
- deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val);
- return ret;
-}
-
-static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val)
-{
- u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val};
- deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val);
- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
-}
-
-/* write single register with mask */
-static int anysee_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val,
- u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = anysee_read_reg(d, reg, &tmp);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return anysee_write_reg(d, reg, val);
-}
-
-/* read single register with mask */
-static int anysee_rd_reg_mask(struct dvb_usb_device *d, u16 reg, u8 *val,
- u8 mask)
-{
- int ret, i;
- u8 tmp;
-
- ret = anysee_read_reg(d, reg, &tmp);
- if (ret)
- return ret;
-
- tmp &= mask;
-
- /* find position of the first bit */
- for (i = 0; i < 8; i++) {
- if ((mask >> i) & 0x01)
- break;
- }
- *val = tmp >> i;
-
- return 0;
-}
-
-static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id)
-{
- u8 buf[] = {CMD_GET_HW_INFO};
- return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3);
-}
-
-static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00};
- deb_info("%s: onoff:%02x\n", __func__, onoff);
- return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0);
-}
-
-static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval)
-{
- u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval};
- deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval);
- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
-}
-
-static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff)
-{
- u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff};
- deb_info("%s: onoff:%02x\n", __func__, onoff);
- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
-}
-
-/* I2C */
-static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0, inc, i = 0;
- u8 buf[52]; /* 4 + 48 (I2C WR USB command header + I2C WR max) */
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- while (i < num) {
- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].len > 2 || msg[i+1].len > 60) {
- ret = -EOPNOTSUPP;
- break;
- }
- buf[0] = CMD_I2C_READ;
- buf[1] = (msg[i].addr << 1) | 0x01;
- buf[2] = msg[i].buf[0];
- buf[3] = msg[i].buf[1];
- buf[4] = msg[i].len-1;
- buf[5] = msg[i+1].len;
- ret = anysee_ctrl_msg(d, buf, 6, msg[i+1].buf,
- msg[i+1].len);
- inc = 2;
- } else {
- if (msg[i].len > 48) {
- ret = -EOPNOTSUPP;
- break;
- }
- buf[0] = CMD_I2C_WRITE;
- buf[1] = (msg[i].addr << 1);
- buf[2] = msg[i].len;
- buf[3] = 0x01;
- memcpy(&buf[4], msg[i].buf, msg[i].len);
- ret = anysee_ctrl_msg(d, buf, 4 + msg[i].len, NULL, 0);
- inc = 1;
- }
- if (ret)
- break;
-
- i += inc;
- }
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret ? ret : i;
-}
-
-static u32 anysee_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm anysee_i2c_algo = {
- .master_xfer = anysee_master_xfer,
- .functionality = anysee_i2c_func,
-};
-
-static int anysee_mt352_demod_init(struct dvb_frontend *fe)
-{
- static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 };
- static u8 reset[] = { RESET, 0x80 };
- static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
- static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 };
- static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 };
- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
-
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
-
- return 0;
-}
-
-/* Callbacks for DVB USB */
-static struct tda10023_config anysee_tda10023_config = {
- .demod_address = (0x1a >> 1),
- .invert = 0,
- .xtal = 16000000,
- .pll_m = 11,
- .pll_p = 3,
- .pll_n = 1,
- .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C,
- .deltaf = 0xfeeb,
-};
-
-static struct mt352_config anysee_mt352_config = {
- .demod_address = (0x1e >> 1),
- .demod_init = anysee_mt352_demod_init,
-};
-
-static struct zl10353_config anysee_zl10353_config = {
- .demod_address = (0x1e >> 1),
- .parallel_ts = 1,
-};
-
-static struct zl10353_config anysee_zl10353_tda18212_config2 = {
- .demod_address = (0x1e >> 1),
- .parallel_ts = 1,
- .disable_i2c_gate_ctrl = 1,
- .no_tuner = 1,
- .if2 = 41500,
-};
-
-static struct zl10353_config anysee_zl10353_tda18212_config = {
- .demod_address = (0x18 >> 1),
- .parallel_ts = 1,
- .disable_i2c_gate_ctrl = 1,
- .no_tuner = 1,
- .if2 = 41500,
-};
-
-static struct tda10023_config anysee_tda10023_tda18212_config = {
- .demod_address = (0x1a >> 1),
- .xtal = 16000000,
- .pll_m = 12,
- .pll_p = 3,
- .pll_n = 1,
- .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_B,
- .deltaf = 0xba02,
-};
-
-static struct tda18212_config anysee_tda18212_config = {
- .i2c_address = (0xc0 >> 1),
- .if_dvbt_6 = 4150,
- .if_dvbt_7 = 4150,
- .if_dvbt_8 = 4150,
- .if_dvbc = 5000,
-};
-
-static struct tda18212_config anysee_tda18212_config2 = {
- .i2c_address = 0x60 /* (0xc0 >> 1) */,
- .if_dvbt_6 = 3550,
- .if_dvbt_7 = 3700,
- .if_dvbt_8 = 4150,
- .if_dvbt2_6 = 3250,
- .if_dvbt2_7 = 4000,
- .if_dvbt2_8 = 4000,
- .if_dvbc = 5000,
-};
-
-static struct cx24116_config anysee_cx24116_config = {
- .demod_address = (0xaa >> 1),
- .mpg_clk_pos_pol = 0x00,
- .i2c_wr_max = 48,
-};
-
-static struct stv0900_config anysee_stv0900_config = {
- .demod_address = (0xd0 >> 1),
- .demod_mode = 0,
- .xtal = 8000000,
- .clkmode = 3,
- .diseqc_mode = 2,
- .tun1_maddress = 0,
- .tun1_adc = 1, /* 1 Vpp */
- .path1_mode = 3,
-};
-
-static struct stv6110_config anysee_stv6110_config = {
- .i2c_address = (0xc0 >> 1),
- .mclk = 16000000,
- .clk_div = 1,
-};
-
-static struct isl6423_config anysee_isl6423_config = {
- .current_max = SEC_CURRENT_800m,
- .curlim = SEC_CURRENT_LIM_OFF,
- .mod_extern = 1,
- .addr = (0x10 >> 1),
-};
-
-static struct cxd2820r_config anysee_cxd2820r_config = {
- .i2c_address = 0x6d, /* (0xda >> 1) */
- .ts_mode = 0x38,
-};
-
-/*
- * New USB device strings: Mfr=1, Product=2, SerialNumber=0
- * Manufacturer: AMT.CO.KR
- *
- * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=????????
- * PCB: ?
- * parts: DNOS404ZH102A(MT352, DTT7579(?))
- *
- * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)"
- * PCB: PCB 507T (rev1.61)
- * parts: DNOS404ZH103A(ZL10353, DTT7579(?))
- * OEA=0a OEB=00 OEC=00 OED=ff OEE=00
- * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00
- *
- * E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee"
- * PCB: 507CD (rev1.1)
- * parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
- * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01
- * IOD[0] ZL10353 1=enabled
- * IOA[7] TS 0=enabled
- * tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not)
- *
- * E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)"
- * PCB: 507DC (rev0.2)
- * parts: TDA10023, DTOS403IH102B TM, CST56I01
- * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
- * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01
- * IOD[0] TDA10023 1=enabled
- *
- * E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)"
- * PCB: 507SI (rev2.1)
- * parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe
- * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01
- * IOD[0] CX24116 1=enabled
- *
- * E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
- * PCB: 507FA (rev0.4)
- * parts: TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
- * IOD[5] TDA10023 1=enabled
- * IOE[0] tuner 1=enabled
- *
- * E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
- * PCB: 507FA (rev1.1)
- * parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024
- * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
- * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
- * DVB-C:
- * IOD[5] TDA10023 1=enabled
- * IOE[0] tuner 1=enabled
- * DVB-T:
- * IOD[0] ZL10353 1=enabled
- * IOE[0] tuner 0=enabled
- * tuner is behind ZL10353 I2C-gate
- *
- * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)"
- * PCB: 508TC (rev0.6)
- * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
- * IOA[7] TS 1=enabled
- * IOE[4] TDA18212 1=enabled
- * DVB-C:
- * IOD[6] ZL10353 0=disabled
- * IOD[5] TDA10023 1=enabled
- * IOE[0] IF 1=enabled
- * DVB-T:
- * IOD[5] TDA10023 0=disabled
- * IOD[6] ZL10353 1=enabled
- * IOE[0] IF 0=enabled
- *
- * E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)"
- * PCB: 508S2 (rev0.7)
- * parts: DNBU10512IST(STV0903, STV6110), ISL6423
- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
- * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
- * IOA[7] TS 1=enabled
- * IOE[5] STV0903 1=enabled
- *
- * E7 T2C VID=1c73 PID=861f HW=20 FW=0.1 AMTCI=0.5 "anysee-E7T2C(LP)"
- * PCB: 508T2C (rev0.3)
- * parts: DNOQ44QCH106A(CXD2820R, TDA18212), TDA8024
- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
- * IOA[7] TS 1=enabled
- * IOE[5] CXD2820R 1=enabled
- *
- * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)"
- * PCB: 508PTC (rev0.5)
- * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
- * IOA[7] TS 1=enabled
- * IOE[4] TDA18212 1=enabled
- * DVB-C:
- * IOD[6] ZL10353 0=disabled
- * IOD[5] TDA10023 1=enabled
- * IOE[0] IF 1=enabled
- * DVB-T:
- * IOD[5] TDA10023 0=disabled
- * IOD[6] ZL10353 1=enabled
- * IOE[0] IF 0=enabled
- *
- * E7 PS2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)"
- * PCB: 508PS2 (rev0.4)
- * parts: DNBU10512IST(STV0903, STV6110), ISL6423
- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
- * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
- * IOA[7] TS 1=enabled
- * IOE[5] STV0903 1=enabled
- */
-
-
-/* external I2C gate used for DNOD44CDH086A(TDA18212) tuner module */
-static int anysee_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
-
- /* enable / disable tuner access on IOE[4] */
- return anysee_wr_reg_mask(adap->dev, REG_IOE, (enable << 4), 0x10);
-}
-
-static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct anysee_state *state = adap->dev->priv;
- int ret;
-
- deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
-
- /* no frontend sleep control */
- if (onoff == 0)
- return 0;
-
- switch (state->hw) {
- case ANYSEE_HW_507FA: /* 15 */
- /* E30 Combo Plus */
- /* E30 C Plus */
-
- if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
- /* disable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
- 0x01);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-C tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
- if (ret)
- goto error;
- } else {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
- 0x01);
- if (ret)
- goto error;
-
- /* enable DVB-T tuner on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
- if (ret)
- goto error;
- }
-
- break;
- case ANYSEE_HW_508TC: /* 18 */
- case ANYSEE_HW_508PTC: /* 21 */
- /* E7 TC */
- /* E7 PTC */
-
- if ((fe->id ^ dvb_usb_anysee_delsys) == 0) {
- /* disable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0),
- 0x01);
- if (ret)
- goto error;
- } else {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* enable IF route on IOE[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0),
- 0x01);
- if (ret)
- goto error;
- }
-
- break;
- default:
- ret = 0;
- }
-
-error:
- return ret;
-}
-
-static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct anysee_state *state = adap->dev->priv;
- u8 hw_info[3];
- u8 tmp;
- struct i2c_msg msg[2] = {
- {
- .addr = anysee_tda18212_config.i2c_address,
- .flags = 0,
- .len = 1,
- .buf = "\x00",
- }, {
- .addr = anysee_tda18212_config.i2c_address,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = &tmp,
- }
- };
-
- /* detect hardware only once */
- if (adap->fe_adap[0].fe == NULL) {
- /* Check which hardware we have.
- * We must do this call two times to get reliable values
- * (hw/fw bug).
- */
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
-
- ret = anysee_get_hw_info(adap->dev, hw_info);
- if (ret)
- goto error;
-
- /* Meaning of these info bytes are guessed. */
- info("firmware version:%d.%d hardware id:%d",
- hw_info[1], hw_info[2], hw_info[0]);
-
- state->hw = hw_info[0];
- }
-
- /* set current frondend ID for devices having two frondends */
- if (adap->fe_adap[0].fe)
- state->fe_id++;
-
- switch (state->hw) {
- case ANYSEE_HW_507T: /* 2 */
- /* E30 */
-
- if (state->fe_id)
- break;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(mt352_attach,
- &anysee_mt352_config, &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe)
- break;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_config, &adap->dev->i2c_adap);
-
- break;
- case ANYSEE_HW_507CD: /* 6 */
- /* E30 Plus */
-
- if (state->fe_id)
- break;
-
- /* enable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
- if (ret)
- goto error;
-
- /* enable transport stream on IOA[7] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (0 << 7), 0x80);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(zl10353_attach,
- &anysee_zl10353_config, &adap->dev->i2c_adap);
-
- break;
- case ANYSEE_HW_507DC: /* 10 */
- /* E30 C Plus */
-
- if (state->fe_id)
- break;
-
- /* enable DVB-C demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
- &anysee_tda10023_config, &adap->dev->i2c_adap, 0x48);
-
- break;
- case ANYSEE_HW_507SI: /* 11 */
- /* E30 S2 Plus */
-
- if (state->fe_id)
- break;
-
- /* enable DVB-S/S2 demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(cx24116_attach,
- &anysee_cx24116_config, &adap->dev->i2c_adap);
-
- break;
- case ANYSEE_HW_507FA: /* 15 */
- /* E30 Combo Plus */
- /* E30 C Plus */
-
- /* enable tuner on IOE[4] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 4), 0x10);
- if (ret)
- goto error;
-
- /* probe TDA18212 */
- tmp = 0;
- ret = i2c_transfer(&adap->dev->i2c_adap, msg, 2);
- if (ret == 2 && tmp == 0xc7)
- deb_info("%s: TDA18212 found\n", __func__);
- else
- tmp = 0;
-
- /* disable tuner on IOE[4] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 4), 0x10);
- if (ret)
- goto error;
-
- if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
- /* disable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0),
- 0x01);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* attach demod */
- if (tmp == 0xc7) {
- /* TDA18212 config */
- adap->fe_adap[state->fe_id].fe = dvb_attach(
- tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
- } else {
- /* PLL config */
- adap->fe_adap[state->fe_id].fe = dvb_attach(
- tda10023_attach,
- &anysee_tda10023_config,
- &adap->dev->i2c_adap, 0x48);
- }
- } else {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[0] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0),
- 0x01);
- if (ret)
- goto error;
-
- /* attach demod */
- if (tmp == 0xc7) {
- /* TDA18212 config */
- adap->fe_adap[state->fe_id].fe = dvb_attach(
- zl10353_attach,
- &anysee_zl10353_tda18212_config2,
- &adap->dev->i2c_adap);
- } else {
- /* PLL config */
- adap->fe_adap[state->fe_id].fe = dvb_attach(
- zl10353_attach,
- &anysee_zl10353_config,
- &adap->dev->i2c_adap);
- }
- }
-
- /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
- if (tmp == 0xc7) {
- if (adap->fe_adap[state->fe_id].fe)
- adap->fe_adap[state->fe_id].fe->ops.i2c_gate_ctrl =
- anysee_i2c_gate_ctrl;
- }
-
- break;
- case ANYSEE_HW_508TC: /* 18 */
- case ANYSEE_HW_508PTC: /* 21 */
- /* E7 TC */
- /* E7 PTC */
-
- if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) {
- /* disable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* enable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[state->fe_id].fe =
- dvb_attach(tda10023_attach,
- &anysee_tda10023_tda18212_config,
- &adap->dev->i2c_adap, 0x48);
- } else {
- /* disable DVB-C demod on IOD[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5),
- 0x20);
- if (ret)
- goto error;
-
- /* enable DVB-T demod on IOD[6] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6),
- 0x40);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[state->fe_id].fe =
- dvb_attach(zl10353_attach,
- &anysee_zl10353_tda18212_config,
- &adap->dev->i2c_adap);
- }
-
- /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
- if (adap->fe_adap[state->fe_id].fe)
- adap->fe_adap[state->fe_id].fe->ops.i2c_gate_ctrl =
- anysee_i2c_gate_ctrl;
-
- state->has_ci = true;
-
- break;
- case ANYSEE_HW_508S2: /* 19 */
- case ANYSEE_HW_508PS2: /* 22 */
- /* E7 S2 */
- /* E7 PS2 */
-
- if (state->fe_id)
- break;
-
- /* enable DVB-S/S2 demod on IOE[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 5), 0x20);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[0].fe = dvb_attach(stv0900_attach,
- &anysee_stv0900_config, &adap->dev->i2c_adap, 0);
-
- state->has_ci = true;
-
- break;
- case ANYSEE_HW_508T2C: /* 20 */
- /* E7 T2C */
-
- if (state->fe_id)
- break;
-
- /* enable DVB-T/T2/C demod on IOE[5] */
- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 5), 0x20);
- if (ret)
- goto error;
-
- /* attach demod */
- adap->fe_adap[state->fe_id].fe = dvb_attach(cxd2820r_attach,
- &anysee_cxd2820r_config, &adap->dev->i2c_adap);
-
- state->has_ci = true;
-
- break;
- }
-
- if (!adap->fe_adap[0].fe) {
- /* we have no frontend :-( */
- ret = -ENODEV;
- err("Unsupported Anysee version. " \
- "Please report the <linux-media@vger.kernel.org>.");
- }
-error:
- return ret;
-}
-
-static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
-{
- struct anysee_state *state = adap->dev->priv;
- struct dvb_frontend *fe;
- int ret;
- deb_info("%s: fe=%d\n", __func__, state->fe_id);
-
- switch (state->hw) {
- case ANYSEE_HW_507T: /* 2 */
- /* E30 */
-
- /* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe,
- (0xc2 >> 1), NULL, DVB_PLL_THOMSON_DTT7579);
-
- break;
- case ANYSEE_HW_507CD: /* 6 */
- /* E30 Plus */
-
- /* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe,
- (0xc2 >> 1), &adap->dev->i2c_adap,
- DVB_PLL_THOMSON_DTT7579);
-
- break;
- case ANYSEE_HW_507DC: /* 10 */
- /* E30 C Plus */
-
- /* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe,
- (0xc0 >> 1), &adap->dev->i2c_adap,
- DVB_PLL_SAMSUNG_DTOS403IH102A);
-
- break;
- case ANYSEE_HW_507SI: /* 11 */
- /* E30 S2 Plus */
-
- /* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &anysee_isl6423_config);
-
- break;
- case ANYSEE_HW_507FA: /* 15 */
- /* E30 Combo Plus */
- /* E30 C Plus */
-
- /* Try first attach TDA18212 silicon tuner on IOE[4], if that
- * fails attach old simple PLL. */
-
- /* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
- &adap->dev->i2c_adap, &anysee_tda18212_config);
- if (fe)
- break;
-
- /* attach tuner */
- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[state->fe_id].fe,
- (0xc0 >> 1), &adap->dev->i2c_adap,
- DVB_PLL_SAMSUNG_DTOS403IH102A);
-
- break;
- case ANYSEE_HW_508TC: /* 18 */
- case ANYSEE_HW_508PTC: /* 21 */
- /* E7 TC */
- /* E7 PTC */
-
- /* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
- &adap->dev->i2c_adap, &anysee_tda18212_config);
-
- break;
- case ANYSEE_HW_508S2: /* 19 */
- case ANYSEE_HW_508PS2: /* 22 */
- /* E7 S2 */
- /* E7 PS2 */
-
- /* attach tuner */
- fe = dvb_attach(stv6110_attach, adap->fe_adap[0].fe,
- &anysee_stv6110_config, &adap->dev->i2c_adap);
-
- if (fe) {
- /* attach LNB controller */
- fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, &anysee_isl6423_config);
- }
-
- break;
-
- case ANYSEE_HW_508T2C: /* 20 */
- /* E7 T2C */
-
- /* attach tuner */
- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe,
- &adap->dev->i2c_adap, &anysee_tda18212_config2);
-
- break;
- default:
- fe = NULL;
- }
-
- if (fe)
- ret = 0;
- else
- ret = -ENODEV;
-
- return ret;
-}
-
-static int anysee_rc_query(struct dvb_usb_device *d)
-{
- u8 buf[] = {CMD_GET_IR_CODE};
- u8 ircode[2];
- int ret;
-
- /* Remote controller is basic NEC using address byte 0x08.
- Anysee device RC query returns only two bytes, status and code,
- address byte is dropped. Also it does not return any value for
- NEC RCs having address byte other than 0x08. Due to that, we
- cannot use that device as standard NEC receiver.
- It could be possible make hack which reads whole code directly
- from device memory... */
-
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), ircode, sizeof(ircode));
- if (ret)
- return ret;
-
- if (ircode[0]) {
- deb_rc("%s: key pressed %02x\n", __func__, ircode[1]);
- rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0);
- }
-
- return 0;
-}
-
-static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot,
- int addr)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
- u8 buf[] = {CMD_CI, 0x02, 0x40 | addr >> 8, addr & 0xff, 0x00, 1};
- u8 val;
-
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1);
- if (ret)
- return ret;
-
- return val;
-}
-
-static int anysee_ci_write_attribute_mem(struct dvb_ca_en50221 *ci, int slot,
- int addr, u8 val)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
- u8 buf[] = {CMD_CI, 0x03, 0x40 | addr >> 8, addr & 0xff, 0x00, 1, val};
-
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int anysee_ci_read_cam_control(struct dvb_ca_en50221 *ci, int slot,
- u8 addr)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
- u8 buf[] = {CMD_CI, 0x04, 0x40, addr, 0x00, 1};
- u8 val;
-
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1);
- if (ret)
- return ret;
-
- return val;
-}
-
-static int anysee_ci_write_cam_control(struct dvb_ca_en50221 *ci, int slot,
- u8 addr, u8 val)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
- u8 buf[] = {CMD_CI, 0x05, 0x40, addr, 0x00, 1, val};
-
- ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int anysee_ci_slot_reset(struct dvb_ca_en50221 *ci, int slot)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
- struct anysee_state *state = d->priv;
-
- state->ci_cam_ready = jiffies + msecs_to_jiffies(1000);
-
- ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80);
- if (ret)
- return ret;
-
- msleep(300);
-
- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int anysee_ci_slot_shutdown(struct dvb_ca_en50221 *ci, int slot)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
-
- ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80);
- if (ret)
- return ret;
-
- msleep(30);
-
- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int anysee_ci_slot_ts_enable(struct dvb_ca_en50221 *ci, int slot)
-{
- struct dvb_usb_device *d = ci->data;
- int ret;
-
- ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 1), 0x02);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int anysee_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot,
- int open)
-{
- struct dvb_usb_device *d = ci->data;
- struct anysee_state *state = d->priv;
- int ret;
- u8 tmp;
-
- ret = anysee_rd_reg_mask(d, REG_IOC, &tmp, 0x40);
- if (ret)
- return ret;
-
- if (tmp == 0) {
- ret = DVB_CA_EN50221_POLL_CAM_PRESENT;
- if (time_after(jiffies, state->ci_cam_ready))
- ret |= DVB_CA_EN50221_POLL_CAM_READY;
- }
-
- return ret;
-}
-
-static int anysee_ci_init(struct dvb_usb_device *d)
-{
- struct anysee_state *state = d->priv;
- int ret;
-
- state->ci.owner = THIS_MODULE;
- state->ci.read_attribute_mem = anysee_ci_read_attribute_mem;
- state->ci.write_attribute_mem = anysee_ci_write_attribute_mem;
- state->ci.read_cam_control = anysee_ci_read_cam_control;
- state->ci.write_cam_control = anysee_ci_write_cam_control;
- state->ci.slot_reset = anysee_ci_slot_reset;
- state->ci.slot_shutdown = anysee_ci_slot_shutdown;
- state->ci.slot_ts_enable = anysee_ci_slot_ts_enable;
- state->ci.poll_slot_status = anysee_ci_poll_slot_status;
- state->ci.data = d;
-
- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
- if (ret)
- return ret;
-
- ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07);
- if (ret)
- return ret;
-
- ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07);
- if (ret)
- return ret;
-
- ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static void anysee_ci_release(struct dvb_usb_device *d)
-{
- struct anysee_state *state = d->priv;
-
- /* detach CI */
- if (state->has_ci)
- dvb_ca_en50221_release(&state->ci);
-
- return;
-}
-
-static int anysee_init(struct dvb_usb_device *d)
-{
- struct anysee_state *state = d->priv;
- int ret;
-
- /* LED light */
- ret = anysee_led_ctrl(d, 0x01, 0x03);
- if (ret)
- return ret;
-
- /* enable IR */
- ret = anysee_ir_ctrl(d, 1);
- if (ret)
- return ret;
-
- /* attach CI */
- if (state->has_ci) {
- ret = anysee_ci_init(d);
- if (ret) {
- state->has_ci = false;
- return ret;
- }
- }
-
- return 0;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties anysee_properties;
-
-static int anysee_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct dvb_usb_device *d;
- struct usb_host_interface *alt;
- int ret;
-
- /* There is one interface with two alternate settings.
- Alternate setting 0 is for bulk transfer.
- Alternate setting 1 is for isochronous transfer.
- We use bulk transfer (alternate setting 0). */
- if (intf->num_altsetting < 1)
- return -ENODEV;
-
- /*
- * Anysee is always warm (its USB-bridge, Cypress FX2, uploads
- * firmware from eeprom). If dvb_usb_device_init() succeeds that
- * means d is a valid pointer.
- */
- ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d,
- adapter_nr);
- if (ret)
- return ret;
-
- alt = usb_altnum_to_altsetting(intf, 0);
- if (alt == NULL) {
- deb_info("%s: no alt found!\n", __func__);
- return -ENODEV;
- }
-
- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
- alt->desc.bAlternateSetting);
- if (ret)
- return ret;
-
- return anysee_init(d);
-}
-
-static void anysee_disconnect(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
-
- anysee_ci_release(d);
- dvb_usb_device_exit(intf);
-
- return;
-}
-
-static struct usb_device_id anysee_table[] = {
- { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) },
- { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, anysee_table);
-
-static struct dvb_usb_device_properties anysee_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
-
- .size_of_priv = sizeof(struct anysee_state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 2,
- .frontend_ctrl = anysee_frontend_ctrl,
- .fe = { {
- .streaming_ctrl = anysee_streaming_ctrl,
- .frontend_attach = anysee_frontend_attach,
- .tuner_attach = anysee_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 8,
- .endpoint = 0x82,
- .u = {
- .bulk = {
- .buffersize = (16*512),
- }
- }
- },
- }, {
- .streaming_ctrl = anysee_streaming_ctrl,
- .frontend_attach = anysee_frontend_attach,
- .tuner_attach = anysee_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 8,
- .endpoint = 0x82,
- .u = {
- .bulk = {
- .buffersize = (16*512),
- }
- }
- },
- } },
- }
- },
-
- .rc.core = {
- .rc_codes = RC_MAP_ANYSEE,
- .protocol = RC_TYPE_OTHER,
- .module_name = "anysee",
- .rc_query = anysee_rc_query,
- .rc_interval = 250, /* windows driver uses 500ms */
- },
-
- .i2c_algo = &anysee_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 1,
-
- .num_device_descs = 1,
- .devices = {
- {
- .name = "Anysee DVB USB2.0",
- .cold_ids = {NULL},
- .warm_ids = {&anysee_table[0],
- &anysee_table[1], NULL},
- },
- }
-};
-
-static struct usb_driver anysee_driver = {
- .name = "dvb_usb_anysee",
- .probe = anysee_probe,
- .disconnect = anysee_disconnect,
- .id_table = anysee_table,
-};
-
-module_usb_driver(anysee_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
deleted file mode 100644
index 8ac879431540..000000000000
--- a/drivers/media/dvb/dvb-usb/anysee.h
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * TODO:
- * - add smart card reader support for Conditional Access (CA)
- *
- * Card reader in Anysee is nothing more than ISO 7816 card reader.
- * There is no hardware CAM in any Anysee device sold.
- * In my understanding it should be implemented by making own module
- * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
- * module registers serial interface that can be used to communicate
- * with any ISO 7816 smart card.
- *
- * Any help according to implement serial smart card reader support
- * is highly welcome!
- */
-
-#ifndef _DVB_USB_ANYSEE_H_
-#define _DVB_USB_ANYSEE_H_
-
-#define DVB_USB_LOG_PREFIX "anysee"
-#include "dvb-usb.h"
-#include "dvb_ca_en50221.h"
-
-#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args)
-#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args)
-#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args)
-#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args)
-#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args)
-#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args)
-
-enum cmd {
- CMD_I2C_READ = 0x33,
- CMD_I2C_WRITE = 0x31,
- CMD_REG_READ = 0xb0,
- CMD_REG_WRITE = 0xb1,
- CMD_STREAMING_CTRL = 0x12,
- CMD_LED_AND_IR_CTRL = 0x16,
- CMD_GET_IR_CODE = 0x41,
- CMD_GET_HW_INFO = 0x19,
- CMD_SMARTCARD = 0x34,
- CMD_CI = 0x37,
-};
-
-struct anysee_state {
- u8 hw; /* PCB ID */
- u8 seq;
- u8 fe_id:1; /* frondend ID */
- u8 has_ci:1;
- struct dvb_ca_en50221 ci;
- unsigned long ci_cam_ready; /* jiffies */
-};
-
-#define ANYSEE_HW_507T 2 /* E30 */
-#define ANYSEE_HW_507CD 6 /* E30 Plus */
-#define ANYSEE_HW_507DC 10 /* E30 C Plus */
-#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
-#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
-#define ANYSEE_HW_508TC 18 /* E7 TC */
-#define ANYSEE_HW_508S2 19 /* E7 S2 */
-#define ANYSEE_HW_508T2C 20 /* E7 T2C */
-#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */
-#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */
-
-#define REG_IOA 0x80 /* Port A (bit addressable) */
-#define REG_IOB 0x90 /* Port B (bit addressable) */
-#define REG_IOC 0xa0 /* Port C (bit addressable) */
-#define REG_IOD 0xb0 /* Port D (bit addressable) */
-#define REG_IOE 0xb1 /* Port E (NOT bit addressable) */
-#define REG_OEA 0xb2 /* Port A Output Enable */
-#define REG_OEB 0xb3 /* Port B Output Enable */
-#define REG_OEC 0xb4 /* Port C Output Enable */
-#define REG_OED 0xb5 /* Port D Output Enable */
-#define REG_OEE 0xb6 /* Port E Output Enable */
-
-#endif
-
-/***************************************************************************
- * USB API description (reverse engineered)
- ***************************************************************************
-
-Transaction flow:
-=================
-BULK[00001] >>> REQUEST PACKET 64 bytes
-BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY)
-BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY)
-
-General reply packet(s) are always used if not own reply defined.
-
-============================================================================
-| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY)
-============================================================================
-| 00 | reply data (if any) from previous transaction
-| | Just same reply packet as returned during previous transaction.
-| | Needed only if reply is missed in previous transaction.
-| | Just skip normally.
-----------------------------------------------------------------------------
-| 01-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY)
-============================================================================
-| 00 | reply data (if any)
-----------------------------------------------------------------------------
-| 01-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | I2C WRITE REQUEST PACKET
-============================================================================
-| 00 | 0x31 I2C write command
-----------------------------------------------------------------------------
-| 01 | i2c address
-----------------------------------------------------------------------------
-| 02 | data length
-| | 0x02 (for typical I2C reg / val pair)
-----------------------------------------------------------------------------
-| 03 | 0x01
-----------------------------------------------------------------------------
-| 04- | data
-----------------------------------------------------------------------------
-| -59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | I2C READ REQUEST PACKET
-============================================================================
-| 00 | 0x33 I2C read command
-----------------------------------------------------------------------------
-| 01 | i2c address + 1
-----------------------------------------------------------------------------
-| 02 | register
-----------------------------------------------------------------------------
-| 03 | 0x00
-----------------------------------------------------------------------------
-| 04 | 0x00
-----------------------------------------------------------------------------
-| 05 | data length
-----------------------------------------------------------------------------
-| 06-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET
-============================================================================
-| 00 | 0xb1 register write command
-----------------------------------------------------------------------------
-| 01-02 | register
-----------------------------------------------------------------------------
-| 03 | 0x01
-----------------------------------------------------------------------------
-| 04 | value
-----------------------------------------------------------------------------
-| 05-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET
-============================================================================
-| 00 | 0xb0 register read command
-----------------------------------------------------------------------------
-| 01-02 | register
-----------------------------------------------------------------------------
-| 03 | 0x01
-----------------------------------------------------------------------------
-| 04-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | LED CONTROL REQUEST PACKET
-============================================================================
-| 00 | 0x16 LED and IR control command
-----------------------------------------------------------------------------
-| 01 | 0x01 (LED)
-----------------------------------------------------------------------------
-| 03 | 0x00 blink
-| | 0x01 lights continuously
-----------------------------------------------------------------------------
-| 04 | blink interval
-| | 0x00 fastest (looks like LED lights continuously)
-| | 0xff slowest
-----------------------------------------------------------------------------
-| 05-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | IR CONTROL REQUEST PACKET
-============================================================================
-| 00 | 0x16 LED and IR control command
-----------------------------------------------------------------------------
-| 01 | 0x02 (IR)
-----------------------------------------------------------------------------
-| 03 | 0x00 IR disabled
-| | 0x01 IR enabled
-----------------------------------------------------------------------------
-| 04-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | STREAMING CONTROL REQUEST PACKET
-============================================================================
-| 00 | 0x12 streaming control command
-----------------------------------------------------------------------------
-| 01 | 0x00 streaming disabled
-| | 0x01 streaming enabled
-----------------------------------------------------------------------------
-| 02 | 0x00
-----------------------------------------------------------------------------
-| 03-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | REMOTE CONTROL REQUEST PACKET
-============================================================================
-| 00 | 0x41 remote control command
-----------------------------------------------------------------------------
-| 01-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | REMOTE CONTROL REPLY PACKET
-============================================================================
-| 00 | 0x00 code not received
-| | 0x01 code received
-----------------------------------------------------------------------------
-| 01 | remote control code
-----------------------------------------------------------------------------
-| 02-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | GET HARDWARE INFO REQUEST PACKET
-============================================================================
-| 00 | 0x19 get hardware info command
-----------------------------------------------------------------------------
-| 01-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | GET HARDWARE INFO REPLY PACKET
-============================================================================
-| 00 | hardware id
-----------------------------------------------------------------------------
-| 01-02 | firmware version
-----------------------------------------------------------------------------
-| 03-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-============================================================================
-| 00-63 | SMART CARD READER PACKET
-============================================================================
-| 00 | 0x34 smart card reader command
-----------------------------------------------------------------------------
-| xx |
-----------------------------------------------------------------------------
-| xx-59 | don't care
-----------------------------------------------------------------------------
-| 60 | packet sequence number
-----------------------------------------------------------------------------
-| 61-63 | don't care
-----------------------------------------------------------------------------
-
-*/
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
deleted file mode 100644
index 16210c060302..000000000000
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
- *
- * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "au6610.h"
-#include "zl10353.h"
-#include "qt1010.h"
-
-/* debug */
-static int dvb_usb_au6610_debug;
-module_param_named(debug, dvb_usb_au6610_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
-{
- int ret;
- u16 index;
- u8 *usb_buf;
-
- /*
- * allocate enough for all known requests,
- * read returns 5 and write 6 bytes
- */
- usb_buf = kmalloc(6, GFP_KERNEL);
- if (!usb_buf)
- return -ENOMEM;
-
- switch (wlen) {
- case 1:
- index = wbuf[0] << 8;
- break;
- case 2:
- index = wbuf[0] << 8;
- index += wbuf[1];
- break;
- default:
- warn("wlen = %x, aborting.", wlen);
- ret = -EINVAL;
- goto error;
- }
-
- ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
- USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
- usb_buf, 6, AU6610_USB_TIMEOUT);
- if (ret < 0)
- goto error;
-
- switch (operation) {
- case AU6610_REQ_I2C_READ:
- case AU6610_REQ_USB_READ:
- /* requested value is always 5th byte in buffer */
- rbuf[0] = usb_buf[4];
- }
-error:
- kfree(usb_buf);
- return ret;
-}
-
-static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
-{
- u8 request;
- u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
-
- if (wo) {
- request = AU6610_REQ_I2C_WRITE;
- } else { /* rw */
- request = AU6610_REQ_I2C_READ;
- }
-
- return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
-}
-
-
-/* I2C */
-static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int i;
-
- if (num > 2)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- for (i = 0; i < num; i++) {
- /* write/read request */
- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
- if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
- msg[i].len, msg[i+1].buf,
- msg[i+1].len) < 0)
- break;
- i++;
- } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
- msg[i].len, NULL, 0) < 0)
- break;
- }
-
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-
-static u32 au6610_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm au6610_i2c_algo = {
- .master_xfer = au6610_i2c_xfer,
- .functionality = au6610_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-static struct zl10353_config au6610_zl10353_config = {
- .demod_address = 0x0f,
- .no_tuner = 1,
- .parallel_ts = 1,
-};
-
-static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
-{
- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
- return -ENODEV;
-
- return 0;
-}
-
-static struct qt1010_config au6610_qt1010_config = {
- .i2c_address = 0x62
-};
-
-static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
-{
- return dvb_attach(qt1010_attach,
- adap->fe_adap[0].fe, &adap->dev->i2c_adap,
- &au6610_qt1010_config) == NULL ? -ENODEV : 0;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties au6610_properties;
-
-static int au6610_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct dvb_usb_device *d;
- struct usb_host_interface *alt;
- int ret;
-
- if (intf->num_altsetting < AU6610_ALTSETTING_COUNT)
- return -ENODEV;
-
- ret = dvb_usb_device_init(intf, &au6610_properties, THIS_MODULE, &d,
- adapter_nr);
- if (ret == 0) {
- alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING);
-
- if (alt == NULL) {
- deb_info("%s: no alt found!\n", __func__);
- return -ENODEV;
- }
- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
- alt->desc.bAlternateSetting);
- }
-
- return ret;
-}
-
-static struct usb_device_id au6610_table [] = {
- { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, au6610_table);
-
-static struct dvb_usb_device_properties au6610_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
-
- .size_of_priv = 0,
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = au6610_zl10353_frontend_attach,
- .tuner_attach = au6610_qt1010_tuner_attach,
-
- .stream = {
- .type = USB_ISOC,
- .count = 5,
- .endpoint = 0x82,
- .u = {
- .isoc = {
- .framesperurb = 40,
- .framesize = 942,
- .interval = 1,
- }
- }
- },
- }},
- }
- },
-
- .i2c_algo = &au6610_i2c_algo,
-
- .num_device_descs = 1,
- .devices = {
- {
- .name = "Sigmatek DVB-110 DVB-T USB2.0",
- .cold_ids = {NULL},
- .warm_ids = {&au6610_table[0], NULL},
- },
- }
-};
-
-static struct usb_driver au6610_driver = {
- .name = "dvb_usb_au6610",
- .probe = au6610_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = au6610_table,
-};
-
-module_usb_driver(au6610_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
-MODULE_VERSION("0.1");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/au6610.h b/drivers/media/dvb/dvb-usb/au6610.h
deleted file mode 100644
index 7849abe2c614..000000000000
--- a/drivers/media/dvb/dvb-usb/au6610.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
- *
- * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _DVB_USB_AU6610_H_
-#define _DVB_USB_AU6610_H_
-
-#define DVB_USB_LOG_PREFIX "au6610"
-#include "dvb-usb.h"
-
-#define deb_info(args...) dprintk(dvb_usb_au6610_debug, 0x01, args)
-
-#define AU6610_REQ_I2C_WRITE 0x14
-#define AU6610_REQ_I2C_READ 0x13
-#define AU6610_REQ_USB_WRITE 0x16
-#define AU6610_REQ_USB_READ 0x15
-
-#define AU6610_USB_TIMEOUT 1000
-
-#define AU6610_ALTSETTING_COUNT 6
-#define AU6610_ALTSETTING 5
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c
deleted file mode 100644
index 86861e6f86d2..000000000000
--- a/drivers/media/dvb/dvb-usb/az6007.c
+++ /dev/null
@@ -1,955 +0,0 @@
-/*
- * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
- *
- * Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
- *
- * This driver was made publicly available by Terratec, at:
- * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
- * The original driver's license is GPL, as declared with MODULE_LICENSE()
- *
- * Copyright (c) 2010-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
- * Driver modified by in order to work with upstream drxk driver, and
- * tons of bugs got fixed.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation under version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include "drxk.h"
-#include "mt2063.h"
-#include "dvb_ca_en50221.h"
-
-#define DVB_USB_LOG_PREFIX "az6007"
-#include "dvb-usb.h"
-
-/* debug */
-int dvb_usb_az6007_debug;
-module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
- DVB_USB_DEBUG_STATUS);
-
-#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
-#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
-#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args)
-#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args)
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
-
-#define FX2_OED 0xb5
-#define AZ6007_READ_DATA 0xb7
-#define AZ6007_I2C_RD 0xb9
-#define AZ6007_POWER 0xbc
-#define AZ6007_I2C_WR 0xbd
-#define FX2_SCON1 0xc0
-#define AZ6007_TS_THROUGH 0xc7
-#define AZ6007_READ_IR 0xb4
-
-struct az6007_device_state {
- struct mutex mutex;
- struct mutex ca_mutex;
- struct dvb_ca_en50221 ca;
- unsigned warm:1;
- int (*gate_ctrl) (struct dvb_frontend *, int);
- unsigned char data[4096];
-};
-
-static struct drxk_config terratec_h7_drxk = {
- .adr = 0x29,
- .parallel_ts = true,
- .dynamic_clk = true,
- .single_master = true,
- .enable_merr_cfg = true,
- .no_i2c_bridge = false,
- .chunk_size = 64,
- .mpeg_out_clk_strength = 0x02,
- .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
-};
-
-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct dvb_usb_adapter *adap = fe->sec_priv;
- struct az6007_device_state *st;
- int status = 0;
-
- deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
-
- if (!adap)
- return -EINVAL;
-
- st = adap->dev->priv;
-
- if (!st)
- return -EINVAL;
-
- if (enable)
- status = st->gate_ctrl(fe, 1);
- else
- status = st->gate_ctrl(fe, 0);
-
- return status;
-}
-
-static struct mt2063_config az6007_mt2063_config = {
- .tuner_address = 0x60,
- .refclock = 36125000,
-};
-
-static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
- u16 index, u8 *b, int blen)
-{
- int ret;
-
- ret = usb_control_msg(udev,
- usb_rcvctrlpipe(udev, 0),
- req,
- USB_TYPE_VENDOR | USB_DIR_IN,
- value, index, b, blen, 5000);
- if (ret < 0) {
- warn("usb read operation failed. (%d)", ret);
- return -EIO;
- }
-
- deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
- index);
- debug_dump(b, blen, deb_xfer);
-
- return ret;
-}
-
-static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
- u16 index, u8 *b, int blen)
-{
- struct az6007_device_state *st = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&st->mutex) < 0)
- return -EAGAIN;
-
- ret = __az6007_read(d->udev, req, value, index, b, blen);
-
- mutex_unlock(&st->mutex);
-
- return ret;
-}
-
-static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
- u16 index, u8 *b, int blen)
-{
- int ret;
-
- deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
- index);
- debug_dump(b, blen, deb_xfer);
-
- if (blen > 64) {
- err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
- blen);
- return -EOPNOTSUPP;
- }
-
- ret = usb_control_msg(udev,
- usb_sndctrlpipe(udev, 0),
- req,
- USB_TYPE_VENDOR | USB_DIR_OUT,
- value, index, b, blen, 5000);
- if (ret != blen) {
- err("usb write operation failed. (%d)", ret);
- return -EIO;
- }
-
- return 0;
-}
-
-static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
- u16 index, u8 *b, int blen)
-{
- struct az6007_device_state *st = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&st->mutex) < 0)
- return -EAGAIN;
-
- ret = __az6007_write(d->udev, req, value, index, b, blen);
-
- mutex_unlock(&st->mutex);
-
- return ret;
-}
-
-static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dvb_usb_device *d = adap->dev;
-
- deb_info("%s: %s", __func__, onoff ? "enable" : "disable");
-
- return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
-}
-
-/* remote control stuff (does not work with my box) */
-static int az6007_rc_query(struct dvb_usb_device *d)
-{
- struct az6007_device_state *st = d->priv;
- unsigned code = 0;
-
- az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
-
- if (st->data[1] == 0x44)
- return 0;
-
- if ((st->data[1] ^ st->data[2]) == 0xff)
- code = st->data[1];
- else
- code = st->data[1] << 8 | st->data[2];
-
- if ((st->data[3] ^ st->data[4]) == 0xff)
- code = code << 8 | st->data[3];
- else
- code = code << 16 | st->data[3] << 8 | st->data[4];
-
- rc_keydown(d->rc_dev, code, st->data[5]);
-
- return 0;
-}
-
-static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot,
- int address)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret;
- u8 req;
- u16 value;
- u16 index;
- int blen;
- u8 *b;
-
- if (slot != 0)
- return -EINVAL;
-
- b = kmalloc(12, GFP_KERNEL);
- if (!b)
- return -ENOMEM;
-
- mutex_lock(&state->ca_mutex);
-
- req = 0xC1;
- value = address;
- index = 0;
- blen = 1;
-
- ret = az6007_read(d, req, value, index, b, blen);
- if (ret < 0) {
- warn("usb in operation failed. (%d)", ret);
- ret = -EINVAL;
- } else {
- ret = b[0];
- }
-
- mutex_unlock(&state->ca_mutex);
- kfree(b);
- return ret;
-}
-
-static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot,
- int address,
- u8 value)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret;
- u8 req;
- u16 value1;
- u16 index;
- int blen;
-
- deb_info("%s %d", __func__, slot);
- if (slot != 0)
- return -EINVAL;
-
- mutex_lock(&state->ca_mutex);
- req = 0xC2;
- value1 = address;
- index = value;
- blen = 0;
-
- ret = az6007_write(d, req, value1, index, NULL, blen);
- if (ret != 0)
- warn("usb out operation failed. (%d)", ret);
-
- mutex_unlock(&state->ca_mutex);
- return ret;
-}
-
-static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
- int slot,
- u8 address)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret;
- u8 req;
- u16 value;
- u16 index;
- int blen;
- u8 *b;
-
- if (slot != 0)
- return -EINVAL;
-
- b = kmalloc(12, GFP_KERNEL);
- if (!b)
- return -ENOMEM;
-
- mutex_lock(&state->ca_mutex);
-
- req = 0xC3;
- value = address;
- index = 0;
- blen = 2;
-
- ret = az6007_read(d, req, value, index, b, blen);
- if (ret < 0) {
- warn("usb in operation failed. (%d)", ret);
- ret = -EINVAL;
- } else {
- if (b[0] == 0)
- warn("Read CI IO error");
-
- ret = b[1];
- deb_info("read cam data = %x from 0x%x", b[1], value);
- }
-
- mutex_unlock(&state->ca_mutex);
- kfree(b);
- return ret;
-}
-
-static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
- int slot,
- u8 address,
- u8 value)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret;
- u8 req;
- u16 value1;
- u16 index;
- int blen;
-
- if (slot != 0)
- return -EINVAL;
-
- mutex_lock(&state->ca_mutex);
- req = 0xC4;
- value1 = address;
- index = value;
- blen = 0;
-
- ret = az6007_write(d, req, value1, index, NULL, blen);
- if (ret != 0) {
- warn("usb out operation failed. (%d)", ret);
- goto failed;
- }
-
-failed:
- mutex_unlock(&state->ca_mutex);
- return ret;
-}
-
-static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
-
- int ret;
- u8 req;
- u16 value;
- u16 index;
- int blen;
- u8 *b;
-
- b = kmalloc(12, GFP_KERNEL);
- if (!b)
- return -ENOMEM;
-
- req = 0xC8;
- value = 0;
- index = 0;
- blen = 1;
-
- ret = az6007_read(d, req, value, index, b, blen);
- if (ret < 0) {
- warn("usb in operation failed. (%d)", ret);
- ret = -EIO;
- } else{
- ret = b[0];
- }
- kfree(b);
- return ret;
-}
-
-static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret, i;
- u8 req;
- u16 value;
- u16 index;
- int blen;
-
- mutex_lock(&state->ca_mutex);
-
- req = 0xC6;
- value = 1;
- index = 0;
- blen = 0;
-
- ret = az6007_write(d, req, value, index, NULL, blen);
- if (ret != 0) {
- warn("usb out operation failed. (%d)", ret);
- goto failed;
- }
-
- msleep(500);
- req = 0xC6;
- value = 0;
- index = 0;
- blen = 0;
-
- ret = az6007_write(d, req, value, index, NULL, blen);
- if (ret != 0) {
- warn("usb out operation failed. (%d)", ret);
- goto failed;
- }
-
- for (i = 0; i < 15; i++) {
- msleep(100);
-
- if (CI_CamReady(ca, slot)) {
- deb_info("CAM Ready");
- break;
- }
- }
- msleep(5000);
-
-failed:
- mutex_unlock(&state->ca_mutex);
- return ret;
-}
-
-static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
-{
- return 0;
-}
-
-static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
-
- int ret;
- u8 req;
- u16 value;
- u16 index;
- int blen;
-
- deb_info("%s", __func__);
- mutex_lock(&state->ca_mutex);
- req = 0xC7;
- value = 1;
- index = 0;
- blen = 0;
-
- ret = az6007_write(d, req, value, index, NULL, blen);
- if (ret != 0) {
- warn("usb out operation failed. (%d)", ret);
- goto failed;
- }
-
-failed:
- mutex_unlock(&state->ca_mutex);
- return ret;
-}
-
-static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
- int ret;
- u8 req;
- u16 value;
- u16 index;
- int blen;
- u8 *b;
-
- b = kmalloc(12, GFP_KERNEL);
- if (!b)
- return -ENOMEM;
- mutex_lock(&state->ca_mutex);
-
- req = 0xC5;
- value = 0;
- index = 0;
- blen = 1;
-
- ret = az6007_read(d, req, value, index, b, blen);
- if (ret < 0) {
- warn("usb in operation failed. (%d)", ret);
- ret = -EIO;
- } else
- ret = 0;
-
- if (!ret && b[0] == 1) {
- ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY;
- }
-
- mutex_unlock(&state->ca_mutex);
- kfree(b);
- return ret;
-}
-
-
-static void az6007_ci_uninit(struct dvb_usb_device *d)
-{
- struct az6007_device_state *state;
-
- deb_info("%s", __func__);
-
- if (NULL == d)
- return;
-
- state = (struct az6007_device_state *)d->priv;
- if (NULL == state)
- return;
-
- if (NULL == state->ca.data)
- return;
-
- dvb_ca_en50221_release(&state->ca);
-
- memset(&state->ca, 0, sizeof(state->ca));
-}
-
-
-static int az6007_ci_init(struct dvb_usb_adapter *a)
-{
- struct dvb_usb_device *d = a->dev;
- struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
- int ret;
-
- deb_info("%s", __func__);
-
- mutex_init(&state->ca_mutex);
-
- state->ca.owner = THIS_MODULE;
- state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
- state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
- state->ca.read_cam_control = az6007_ci_read_cam_control;
- state->ca.write_cam_control = az6007_ci_write_cam_control;
- state->ca.slot_reset = az6007_ci_slot_reset;
- state->ca.slot_shutdown = az6007_ci_slot_shutdown;
- state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
- state->ca.poll_slot_status = az6007_ci_poll_slot_status;
- state->ca.data = d;
-
- ret = dvb_ca_en50221_init(&a->dvb_adap,
- &state->ca,
- 0, /* flags */
- 1);/* n_slots */
- if (ret != 0) {
- err("Cannot initialize CI: Error %d.", ret);
- memset(&state->ca, 0, sizeof(state->ca));
- return ret;
- }
-
- deb_info("CI initialized.");
-
- return 0;
-}
-
-static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
-{
- struct az6007_device_state *st = d->priv;
- int ret;
-
- ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
- memcpy(mac, st->data, 6);
-
- if (ret > 0)
- deb_info("%s: mac is %pM\n", __func__, mac);
-
- return ret;
-}
-
-static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct az6007_device_state *st = adap->dev->priv;
-
- deb_info("attaching demod drxk");
-
- adap->fe_adap[0].fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
- &adap->dev->i2c_adap);
- if (!adap->fe_adap[0].fe)
- return -EINVAL;
-
- adap->fe_adap[0].fe->sec_priv = adap;
- st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl;
- adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
-
- az6007_ci_init(adap);
-
- return 0;
-}
-
-static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
-{
- deb_info("attaching tuner mt2063");
-
- /* Attach mt2063 to DVB-C frontend */
- if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
- adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
- if (!dvb_attach(mt2063_attach, adap->fe_adap[0].fe,
- &az6007_mt2063_config,
- &adap->dev->i2c_adap))
- return -EINVAL;
-
- if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
- adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
-
- return 0;
-}
-
-int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- struct az6007_device_state *st = d->priv;
- int ret;
-
- deb_info("%s()\n", __func__);
-
- if (!st->warm) {
- mutex_init(&st->mutex);
-
- ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
- if (ret < 0)
- return ret;
- msleep(60);
- ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
- if (ret < 0)
- return ret;
- msleep(100);
- ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
- if (ret < 0)
- return ret;
- msleep(20);
- ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
- if (ret < 0)
- return ret;
-
- msleep(400);
- ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
- if (ret < 0)
- return ret;
- msleep(150);
- ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
- if (ret < 0)
- return ret;
- msleep(430);
- ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
- if (ret < 0)
- return ret;
-
- st->warm = true;
-
- return 0;
- }
-
- if (!onoff)
- return 0;
-
- az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
- az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
-
- return 0;
-}
-
-/* I2C */
-static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct az6007_device_state *st = d->priv;
- int i, j, len;
- int ret = 0;
- u16 index;
- u16 value;
- int length;
- u8 req, addr;
-
- if (mutex_lock_interruptible(&st->mutex) < 0)
- return -EAGAIN;
-
- for (i = 0; i < num; i++) {
- addr = msgs[i].addr << 1;
- if (((i + 1) < num)
- && (msgs[i].len == 1)
- && (!msgs[i].flags & I2C_M_RD)
- && (msgs[i + 1].flags & I2C_M_RD)
- && (msgs[i].addr == msgs[i + 1].addr)) {
- /*
- * A write + read xfer for the same address, where
- * the first xfer has just 1 byte length.
- * Need to join both into one operation
- */
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_DEBUG
- "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
- addr, msgs[i].len, msgs[i + 1].len);
- req = AZ6007_I2C_RD;
- index = msgs[i].buf[0];
- value = addr | (1 << 8);
- length = 6 + msgs[i + 1].len;
- len = msgs[i + 1].len;
- ret = __az6007_read(d->udev, req, value, index,
- st->data, length);
- if (ret >= len) {
- for (j = 0; j < len; j++) {
- msgs[i + 1].buf[j] = st->data[j + 5];
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_CONT
- "0x%02x ",
- msgs[i + 1].buf[j]);
- }
- } else
- ret = -EIO;
- i++;
- } else if (!(msgs[i].flags & I2C_M_RD)) {
- /* write bytes */
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_DEBUG
- "az6007 I2C xfer write addr=0x%x len=%d: ",
- addr, msgs[i].len);
- req = AZ6007_I2C_WR;
- index = msgs[i].buf[0];
- value = addr | (1 << 8);
- length = msgs[i].len - 1;
- len = msgs[i].len - 1;
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
- for (j = 0; j < len; j++) {
- st->data[j] = msgs[i].buf[j + 1];
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_CONT "0x%02x ",
- st->data[j]);
- }
- ret = __az6007_write(d->udev, req, value, index,
- st->data, length);
- } else {
- /* read bytes */
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_DEBUG
- "az6007 I2C xfer read addr=0x%x len=%d: ",
- addr, msgs[i].len);
- req = AZ6007_I2C_RD;
- index = msgs[i].buf[0];
- value = addr;
- length = msgs[i].len + 6;
- len = msgs[i].len;
- ret = __az6007_read(d->udev, req, value, index,
- st->data, length);
- for (j = 0; j < len; j++) {
- msgs[i].buf[j] = st->data[j + 5];
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_CONT
- "0x%02x ", st->data[j + 5]);
- }
- }
- if (dvb_usb_az6007_debug & 2)
- printk(KERN_CONT "\n");
- if (ret < 0)
- goto err;
- }
-err:
- mutex_unlock(&st->mutex);
-
- if (ret < 0) {
- info("%s ERROR: %i", __func__, ret);
- return ret;
- }
- return num;
-}
-
-static u32 az6007_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm az6007_i2c_algo = {
- .master_xfer = az6007_i2c_xfer,
- .functionality = az6007_i2c_func,
-};
-
-int az6007_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc, int *cold)
-{
- int ret;
- u8 *mac;
-
- mac = kmalloc(6, GFP_ATOMIC);
- if (!mac)
- return -ENOMEM;
-
- /* Try to read the mac address */
- ret = __az6007_read(udev, AZ6007_READ_DATA, 6, 0, mac, 6);
- if (ret == 6)
- *cold = 0;
- else
- *cold = 1;
-
- kfree(mac);
-
- if (*cold) {
- __az6007_write(udev, 0x09, 1, 0, NULL, 0);
- __az6007_write(udev, 0x00, 0, 0, NULL, 0);
- __az6007_write(udev, 0x00, 0, 0, NULL, 0);
- }
-
- deb_info("Device is on %s state\n", *cold ? "warm" : "cold");
- return 0;
-}
-
-static struct dvb_usb_device_properties az6007_properties;
-
-static void az6007_usb_disconnect(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
- az6007_ci_uninit(d);
- dvb_usb_device_exit(intf);
-}
-
-static int az6007_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- return dvb_usb_device_init(intf, &az6007_properties,
- THIS_MODULE, NULL, adapter_nr);
-}
-
-static struct usb_device_id az6007_usb_table[] = {
- {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
- {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
- {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2)},
- {0},
-};
-
-MODULE_DEVICE_TABLE(usb, az6007_usb_table);
-
-static struct dvb_usb_device_properties az6007_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
- .usb_ctrl = CYPRESS_FX2,
- .firmware = "dvb-usb-terratec-h7-az6007.fw",
- .no_reconnect = 1,
- .size_of_priv = sizeof(struct az6007_device_state),
- .identify_state = az6007_identify_state,
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .streaming_ctrl = az6007_streaming_ctrl,
- .tuner_attach = az6007_tuner_attach,
- .frontend_attach = az6007_frontend_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 10,
- .endpoint = 0x02,
- .u = {
- .bulk = {
- .buffersize = 4096,
- }
- }
- },
- } }
- } },
- .power_ctrl = az6007_power_ctrl,
- .read_mac_address = az6007_read_mac_addr,
-
- .rc.core = {
- .rc_interval = 400,
- .rc_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
- .module_name = "az6007",
- .rc_query = az6007_rc_query,
- .allowed_protos = RC_TYPE_NEC,
- },
- .i2c_algo = &az6007_i2c_algo,
-
- .num_device_descs = 2,
- .devices = {
- { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
- .cold_ids = { &az6007_usb_table[0], NULL },
- .warm_ids = { NULL },
- },
- { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
- .cold_ids = { &az6007_usb_table[1], &az6007_usb_table[2], NULL },
- .warm_ids = { NULL },
- },
- { NULL },
- }
-};
-
-/* usb specific object needed to register this driver with the usb subsystem */
-static struct usb_driver az6007_usb_driver = {
- .name = "dvb_usb_az6007",
- .probe = az6007_usb_probe,
- .disconnect = az6007_usb_disconnect,
- .id_table = az6007_usb_table,
-};
-
-/* module stuff */
-static int __init az6007_usb_module_init(void)
-{
- int result;
- deb_info("az6007 usb module init\n");
-
- result = usb_register(&az6007_usb_driver);
- if (result) {
- err("usb_register failed. (%d)", result);
- return result;
- }
-
- return 0;
-}
-
-static void __exit az6007_usb_module_exit(void)
-{
- /* deregister this driver from the USB subsystem */
- deb_info("az6007 usb module exit\n");
- usb_deregister(&az6007_usb_driver);
-}
-
-module_init(az6007_usb_module_init);
-module_exit(az6007_usb_module_exit);
-
-MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
-MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
-MODULE_VERSION("1.1");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c
deleted file mode 100644
index fa637255729c..000000000000
--- a/drivers/media/dvb/dvb-usb/ce6230.c
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "ce6230.h"
-#include "zl10353.h"
-#include "mxl5005s.h"
-
-/* debug */
-static int dvb_usb_ce6230_debug;
-module_param_named(debug, dvb_usb_ce6230_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static struct zl10353_config ce6230_zl10353_config;
-
-static int ce6230_rw_udev(struct usb_device *udev, struct req_t *req)
-{
- int ret;
- unsigned int pipe;
- u8 request;
- u8 requesttype;
- u16 value;
- u16 index;
- u8 *buf;
-
- request = req->cmd;
- value = req->value;
- index = req->index;
-
- switch (req->cmd) {
- case I2C_READ:
- case DEMOD_READ:
- case REG_READ:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
- break;
- case I2C_WRITE:
- case DEMOD_WRITE:
- case REG_WRITE:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
- break;
- default:
- err("unknown command:%02x", req->cmd);
- ret = -EPERM;
- goto error;
- }
-
- buf = kmalloc(req->data_len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto error;
- }
-
- if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
- /* write */
- memcpy(buf, req->data, req->data_len);
- pipe = usb_sndctrlpipe(udev, 0);
- } else {
- /* read */
- pipe = usb_rcvctrlpipe(udev, 0);
- }
-
- msleep(1); /* avoid I2C errors */
-
- ret = usb_control_msg(udev, pipe, request, requesttype, value, index,
- buf, req->data_len, CE6230_USB_TIMEOUT);
-
- ce6230_debug_dump(request, requesttype, value, index, buf,
- req->data_len, deb_xfer);
-
- if (ret < 0)
- deb_info("%s: usb_control_msg failed:%d\n", __func__, ret);
- else
- ret = 0;
-
- /* read request, copy returned data to return buf */
- if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
- memcpy(req->data, buf, req->data_len);
-
- kfree(buf);
-error:
- return ret;
-}
-
-static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
-{
- return ce6230_rw_udev(d->udev, req);
-}
-
-/* I2C */
-static int ce6230_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int i = 0;
- struct req_t req;
- int ret = 0;
- memset(&req, 0, sizeof(req));
-
- if (num > 2)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- while (i < num) {
- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].addr ==
- ce6230_zl10353_config.demod_address) {
- req.cmd = DEMOD_READ;
- req.value = msg[i].addr >> 1;
- req.index = msg[i].buf[0];
- req.data_len = msg[i+1].len;
- req.data = &msg[i+1].buf[0];
- ret = ce6230_ctrl_msg(d, &req);
- } else {
- err("i2c read not implemented");
- ret = -EPERM;
- }
- i += 2;
- } else {
- if (msg[i].addr ==
- ce6230_zl10353_config.demod_address) {
- req.cmd = DEMOD_WRITE;
- req.value = msg[i].addr >> 1;
- req.index = msg[i].buf[0];
- req.data_len = msg[i].len-1;
- req.data = &msg[i].buf[1];
- ret = ce6230_ctrl_msg(d, &req);
- } else {
- req.cmd = I2C_WRITE;
- req.value = 0x2000 + (msg[i].addr >> 1);
- req.index = 0x0000;
- req.data_len = msg[i].len;
- req.data = &msg[i].buf[0];
- ret = ce6230_ctrl_msg(d, &req);
- }
- i += 1;
- }
- if (ret)
- break;
- }
-
- mutex_unlock(&d->i2c_mutex);
- return ret ? ret : i;
-}
-
-static u32 ce6230_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm ce6230_i2c_algo = {
- .master_xfer = ce6230_i2c_xfer,
- .functionality = ce6230_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-static struct zl10353_config ce6230_zl10353_config = {
- .demod_address = 0x1e,
- .adc_clock = 450000,
- .if2 = 45700,
- .no_tuner = 1,
- .parallel_ts = 1,
- .clock_ctl_1 = 0x34,
- .pll_0 = 0x0e,
-};
-
-static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
-{
- deb_info("%s:\n", __func__);
- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
- return -ENODEV;
- return 0;
-}
-
-static struct mxl5005s_config ce6230_mxl5003s_config = {
- .i2c_address = 0xc6,
- .if_freq = IF_FREQ_4570000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_DEFAULT,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .AgcMasterByte = 0x00,
-};
-
-static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- deb_info("%s:\n", __func__);
- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
- &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
- return ret;
-}
-
-static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- int ret;
- deb_info("%s: onoff:%d\n", __func__, onoff);
-
- /* InterfaceNumber 1 / AlternateSetting 0 idle
- InterfaceNumber 1 / AlternateSetting 1 streaming */
- ret = usb_set_interface(d->udev, 1, onoff);
- if (ret)
- err("usb_set_interface failed with error:%d", ret);
-
- return ret;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties ce6230_properties;
-
-static int ce6230_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret = 0;
- struct dvb_usb_device *d = NULL;
-
- deb_info("%s: interface:%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
- ret = dvb_usb_device_init(intf, &ce6230_properties, THIS_MODULE,
- &d, adapter_nr);
- if (ret)
- err("init failed with error:%d\n", ret);
- }
-
- return ret;
-}
-
-static struct usb_device_id ce6230_table[] = {
- { USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500) },
- { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310) },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, ce6230_table);
-
-static struct dvb_usb_device_properties ce6230_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .no_reconnect = 1,
-
- .size_of_priv = 0,
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = ce6230_zl10353_frontend_attach,
- .tuner_attach = ce6230_mxl5003s_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x82,
- .u = {
- .bulk = {
- .buffersize = (16*512),
- }
- }
- },
- }},
- }
- },
-
- .power_ctrl = ce6230_power_ctrl,
-
- .i2c_algo = &ce6230_i2c_algo,
-
- .num_device_descs = 2,
- .devices = {
- {
- .name = "Intel CE9500 reference design",
- .cold_ids = {NULL},
- .warm_ids = {&ce6230_table[0], NULL},
- },
- {
- .name = "AVerMedia A310 USB 2.0 DVB-T tuner",
- .cold_ids = {NULL},
- .warm_ids = {&ce6230_table[1], NULL},
- },
- }
-};
-
-static struct usb_driver ce6230_driver = {
- .name = "dvb_usb_ce6230",
- .probe = ce6230_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = ce6230_table,
-};
-
-module_usb_driver(ce6230_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver for Intel CE6230 DVB-T USB2.0");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ce6230.h b/drivers/media/dvb/dvb-usb/ce6230.h
deleted file mode 100644
index 97c42482ccb3..000000000000
--- a/drivers/media/dvb/dvb-usb/ce6230.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _DVB_USB_CE6230_H_
-#define _DVB_USB_CE6230_H_
-
-#define DVB_USB_LOG_PREFIX "ce6230"
-#include "dvb-usb.h"
-
-#define deb_info(args...) dprintk(dvb_usb_ce6230_debug, 0x01, args)
-#define deb_rc(args...) dprintk(dvb_usb_ce6230_debug, 0x02, args)
-#define deb_xfer(args...) dprintk(dvb_usb_ce6230_debug, 0x04, args)
-#define deb_reg(args...) dprintk(dvb_usb_ce6230_debug, 0x08, args)
-#define deb_i2c(args...) dprintk(dvb_usb_ce6230_debug, 0x10, args)
-#define deb_fw(args...) dprintk(dvb_usb_ce6230_debug, 0x20, args)
-
-#define ce6230_debug_dump(r, t, v, i, b, l, func) { \
- int loop_; \
- func("%02x %02x %02x %02x %02x %02x %02x %02x", \
- t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
- if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
- func(" >>> "); \
- else \
- func(" <<< "); \
- for (loop_ = 0; loop_ < l; loop_++) \
- func("%02x ", b[loop_]); \
- func("\n");\
-}
-
-#define CE6230_USB_TIMEOUT 1000
-
-struct req_t {
- u8 cmd; /* [1] */
- u16 value; /* [2|3] */
- u16 index; /* [4|5] */
- u16 data_len; /* [6|7] */
- u8 *data;
-};
-
-enum ce6230_cmd {
- CONFIG_READ = 0xd0, /* rd 0 (unclear) */
- UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */
- I2C_READ = 0xd9, /* rd 9 (unclear) */
- I2C_WRITE = 0xca, /* wr a */
- DEMOD_READ = 0xdb, /* rd b */
- DEMOD_WRITE = 0xcc, /* wr c */
- REG_READ = 0xde, /* rd e */
- REG_WRITE = 0xcf, /* wr f */
-};
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
deleted file mode 100644
index 7e9e00fae04e..000000000000
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ /dev/null
@@ -1,845 +0,0 @@
-/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
- *
- * This program is free software; 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.
- *
- * Copyright (C) 2005-6 DiBcom, SA
- */
-#include "dib0700.h"
-
-/* debug */
-int dvb_usb_dib0700_debug;
-module_param_named(debug,dvb_usb_dib0700_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS);
-
-static int nb_packet_buffer_size = 21;
-module_param(nb_packet_buffer_size, int, 0644);
-MODULE_PARM_DESC(nb_packet_buffer_size,
- "Set the dib0700 driver data buffer size. This parameter "
- "corresponds to the number of TS packets. The actual size of "
- "the data buffer corresponds to this parameter "
- "multiplied by 188 (default: 21)");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-
-int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
- u32 *romversion, u32 *ramversion, u32 *fwtype)
-{
- struct dib0700_state *st = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
- REQUEST_GET_VERSION,
- USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
- st->buf, 16, USB_CTRL_GET_TIMEOUT);
- if (hwversion != NULL)
- *hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) |
- (st->buf[2] << 8) | st->buf[3];
- if (romversion != NULL)
- *romversion = (st->buf[4] << 24) | (st->buf[5] << 16) |
- (st->buf[6] << 8) | st->buf[7];
- if (ramversion != NULL)
- *ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) |
- (st->buf[10] << 8) | st->buf[11];
- if (fwtype != NULL)
- *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
- (st->buf[14] << 8) | st->buf[15];
- mutex_unlock(&d->usb_mutex);
- return ret;
-}
-
-/* expecting rx buffer: request data[0] data[1] ... data[2] */
-static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen)
-{
- int status;
-
- deb_data(">>> ");
- debug_dump(tx, txlen, deb_data);
-
- status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0),
- tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen,
- USB_CTRL_GET_TIMEOUT);
-
- if (status != txlen)
- deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen);
-
- return status < 0 ? status : 0;
-}
-
-/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */
-int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
-{
- u16 index, value;
- int status;
-
- if (txlen < 2) {
- err("tx buffer length is smaller than 2. Makes no sense.");
- return -EINVAL;
- }
- if (txlen > 4) {
- err("tx buffer length is larger than 4. Not supported.");
- return -EINVAL;
- }
-
- deb_data(">>> ");
- debug_dump(tx,txlen,deb_data);
-
- value = ((txlen - 2) << 8) | tx[1];
- index = 0;
- if (txlen > 2)
- index |= (tx[2] << 8);
- if (txlen > 3)
- index |= tx[3];
-
- status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
- USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
- USB_CTRL_GET_TIMEOUT);
-
- if (status < 0)
- deb_info("ep 0 read error (status = %d)\n",status);
-
- deb_data("<<< ");
- debug_dump(rx, rxlen, deb_data);
-
- return status; /* length in case of success */
-}
-
-int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
-{
- struct dib0700_state *st = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_SET_GPIO;
- st->buf[1] = gpio;
- st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6);
-
- ret = dib0700_ctrl_wr(d, st->buf, 3);
-
- mutex_unlock(&d->usb_mutex);
- return ret;
-}
-
-static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
-{
- struct dib0700_state *st = d->priv;
- int ret;
-
- if (st->fw_version >= 0x10201) {
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_SET_USB_XFER_LEN;
- st->buf[1] = (nb_ts_packets >> 8) & 0xff;
- st->buf[2] = nb_ts_packets & 0xff;
-
- deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
-
- ret = dib0700_ctrl_wr(d, st->buf, 3);
- mutex_unlock(&d->usb_mutex);
- } else {
- deb_info("this firmware does not allow to change the USB xfer len\n");
- ret = -EIO;
- }
-
- return ret;
-}
-
-/*
- * I2C master xfer function (supported in 1.20 firmware)
- */
-static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
- int num)
-{
- /* The new i2c firmware messages are more reliable and in particular
- properly support i2c read calls not preceded by a write */
-
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct dib0700_state *st = d->priv;
- uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
- uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
- uint8_t en_start = 0;
- uint8_t en_stop = 0;
- int result, i;
-
- /* 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 -EINTR;
-
- for (i = 0; i < num; i++) {
- if (i == 0) {
- /* First message in the transaction */
- en_start = 1;
- } else if (!(msg[i].flags & I2C_M_NOSTART)) {
- /* Device supports repeated-start */
- en_start = 1;
- } else {
- /* Not the first packet and device doesn't support
- repeated start */
- en_start = 0;
- }
- if (i == (num - 1)) {
- /* Last message in the transaction */
- en_stop = 1;
- }
-
- if (msg[i].flags & I2C_M_RD) {
- /* Read request */
- u16 index, value;
- uint8_t i2c_dest;
-
- i2c_dest = (msg[i].addr << 1);
- value = ((en_start << 7) | (en_stop << 6) |
- (msg[i].len & 0x3F)) << 8 | i2c_dest;
- /* I2C ctrl + FE bus; */
- index = ((gen_mode << 6) & 0xC0) |
- ((bus_mode << 4) & 0x30);
-
- result = usb_control_msg(d->udev,
- usb_rcvctrlpipe(d->udev, 0),
- REQUEST_NEW_I2C_READ,
- USB_TYPE_VENDOR | USB_DIR_IN,
- value, index, msg[i].buf,
- msg[i].len,
- USB_CTRL_GET_TIMEOUT);
- if (result < 0) {
- deb_info("i2c read error (status = %d)\n", result);
- break;
- }
-
- deb_data("<<< ");
- debug_dump(msg[i].buf, msg[i].len, deb_data);
-
- } else {
- /* Write request */
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- mutex_unlock(&d->i2c_mutex);
- return -EINTR;
- }
- st->buf[0] = REQUEST_NEW_I2C_WRITE;
- st->buf[1] = msg[i].addr << 1;
- st->buf[2] = (en_start << 7) | (en_stop << 6) |
- (msg[i].len & 0x3F);
- /* I2C ctrl + FE bus; */
- st->buf[3] = ((gen_mode << 6) & 0xC0) |
- ((bus_mode << 4) & 0x30);
- /* The Actual i2c payload */
- memcpy(&st->buf[4], msg[i].buf, msg[i].len);
-
- deb_data(">>> ");
- debug_dump(st->buf, msg[i].len + 4, deb_data);
-
- result = usb_control_msg(d->udev,
- usb_sndctrlpipe(d->udev, 0),
- REQUEST_NEW_I2C_WRITE,
- USB_TYPE_VENDOR | USB_DIR_OUT,
- 0, 0, st->buf, msg[i].len + 4,
- USB_CTRL_GET_TIMEOUT);
- mutex_unlock(&d->usb_mutex);
- if (result < 0) {
- deb_info("i2c write error (status = %d)\n", result);
- break;
- }
- }
- }
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-/*
- * I2C master xfer function (pre-1.20 firmware)
- */
-static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
- struct i2c_msg *msg, int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct dib0700_state *st = d->priv;
- int i,len;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EINTR;
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- mutex_unlock(&d->i2c_mutex);
- return -EINTR;
- }
-
- for (i = 0; i < num; i++) {
- /* fill in the address */
- st->buf[1] = msg[i].addr << 1;
- /* fill the buffer */
- memcpy(&st->buf[2], msg[i].buf, msg[i].len);
-
- /* write/read request */
- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
- st->buf[0] = REQUEST_I2C_READ;
- st->buf[1] |= 1;
-
- /* special thing in the current firmware: when length is zero the read-failed */
- len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
- msg[i+1].buf, msg[i+1].len);
- if (len <= 0) {
- deb_info("I2C read failed on address 0x%02x\n",
- msg[i].addr);
- break;
- }
-
- msg[i+1].len = len;
-
- i++;
- } else {
- st->buf[0] = REQUEST_I2C_WRITE;
- if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0)
- break;
- }
- }
- mutex_unlock(&d->usb_mutex);
- mutex_unlock(&d->i2c_mutex);
-
- return i;
-}
-
-static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct dib0700_state *st = d->priv;
-
- if (st->fw_use_new_i2c_api == 1) {
- /* User running at least fw 1.20 */
- return dib0700_i2c_xfer_new(adap, msg, num);
- } else {
- /* Use legacy calls */
- return dib0700_i2c_xfer_legacy(adap, msg, num);
- }
-}
-
-static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-struct i2c_algorithm dib0700_i2c_algo = {
- .master_xfer = dib0700_i2c_xfer,
- .functionality = dib0700_i2c_func,
-};
-
-int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc, int *cold)
-{
- s16 ret;
- u8 *b;
-
- b = kmalloc(16, GFP_KERNEL);
- if (!b)
- return -ENOMEM;
-
-
- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
-
- deb_info("FW GET_VERSION length: %d\n",ret);
-
- *cold = ret <= 0;
- deb_info("cold: %d\n", *cold);
-
- kfree(b);
- return 0;
-}
-
-static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
- u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
- u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
-{
- struct dib0700_state *st = d->priv;
- int ret;
-
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_SET_CLOCK;
- st->buf[1] = (en_pll << 7) | (pll_src << 6) |
- (pll_range << 5) | (clock_gpio3 << 4);
- st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */
- st->buf[3] = pll_prediv & 0xff; /* LSB */
- st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */
- st->buf[5] = pll_loopdiv & 0xff; /* LSB */
- st->buf[6] = (free_div >> 8) & 0xff; /* MSB */
- st->buf[7] = free_div & 0xff; /* LSB */
- st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */
- st->buf[9] = dsuScaler & 0xff; /* LSB */
-
- ret = dib0700_ctrl_wr(d, st->buf, 10);
- mutex_unlock(&d->usb_mutex);
-
- return ret;
-}
-
-int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
-{
- struct dib0700_state *st = d->priv;
- u16 divider;
- int ret;
-
- if (scl_kHz == 0)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_SET_I2C_PARAM;
- divider = (u16) (30000 / scl_kHz);
- st->buf[1] = 0;
- st->buf[2] = (u8) (divider >> 8);
- st->buf[3] = (u8) (divider & 0xff);
- divider = (u16) (72000 / scl_kHz);
- st->buf[4] = (u8) (divider >> 8);
- st->buf[5] = (u8) (divider & 0xff);
- divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
- st->buf[6] = (u8) (divider >> 8);
- st->buf[7] = (u8) (divider & 0xff);
-
- deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
- (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
- st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
-
- ret = dib0700_ctrl_wr(d, st->buf, 8);
- mutex_unlock(&d->usb_mutex);
-
- return ret;
-}
-
-
-int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
-{
- switch (clk_MHz) {
- case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
- default: return -EINVAL;
- }
- return 0;
-}
-
-static int dib0700_jumpram(struct usb_device *udev, u32 address)
-{
- int ret = 0, actlen;
- u8 *buf;
-
- buf = kmalloc(8, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- buf[0] = REQUEST_JUMPRAM;
- buf[1] = 0;
- buf[2] = 0;
- buf[3] = 0;
- buf[4] = (address >> 24) & 0xff;
- buf[5] = (address >> 16) & 0xff;
- buf[6] = (address >> 8) & 0xff;
- buf[7] = address & 0xff;
-
- if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) {
- deb_fw("jumpram to 0x%x failed\n",address);
- goto out;
- }
- if (actlen != 8) {
- deb_fw("jumpram to 0x%x failed\n",address);
- ret = -EIO;
- goto out;
- }
-out:
- kfree(buf);
- return ret;
-}
-
-int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
-{
- struct hexline hx;
- int pos = 0, ret, act_len, i, adap_num;
- u8 *buf;
- u32 fw_version;
-
- buf = kmalloc(260, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) {
- deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",
- hx.addr, hx.len, hx.chk);
-
- buf[0] = hx.len;
- buf[1] = (hx.addr >> 8) & 0xff;
- buf[2] = hx.addr & 0xff;
- buf[3] = hx.type;
- memcpy(&buf[4],hx.data,hx.len);
- buf[4+hx.len] = hx.chk;
-
- ret = usb_bulk_msg(udev,
- usb_sndbulkpipe(udev, 0x01),
- buf,
- hx.len + 5,
- &act_len,
- 1000);
-
- if (ret < 0) {
- err("firmware download failed at %d with %d",pos,ret);
- goto out;
- }
- }
-
- if (ret == 0) {
- /* start the firmware */
- if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
- info("firmware started successfully.");
- msleep(500);
- }
- } else
- ret = -EIO;
-
- /* the number of ts packet has to be at least 1 */
- if (nb_packet_buffer_size < 1)
- nb_packet_buffer_size = 1;
-
- /* get the fimware version */
- usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- REQUEST_GET_VERSION,
- USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
- buf, 16, USB_CTRL_GET_TIMEOUT);
- fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
-
- /* set the buffer size - DVB-USB is allocating URB buffers
- * only after the firwmare download was successful */
- for (i = 0; i < dib0700_device_count; i++) {
- for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
- adap_num++) {
- if (fw_version >= 0x10201) {
- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
- } else {
- /* for fw version older than 1.20.1,
- * the buffersize has to be n times 512 */
- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
- if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
- }
- }
- }
-out:
- kfree(buf);
- return ret;
-}
-
-int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dib0700_state *st = adap->dev->priv;
- int ret;
-
- if ((onoff != 0) && (st->fw_version >= 0x10201)) {
- /* for firmware later than 1.20.1,
- * the USB xfer length can be set */
- ret = dib0700_set_usb_xfer_len(adap->dev,
- st->nb_packet_buffer_size);
- if (ret < 0) {
- deb_info("can not set the USB xfer len\n");
- return ret;
- }
- }
-
- if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_ENABLE_VIDEO;
- /* this bit gives a kind of command,
- * rather than enabling something or not */
- st->buf[1] = (onoff << 4) | 0x00;
-
- if (st->disable_streaming_master_mode == 1)
- st->buf[2] = 0x00;
- else
- st->buf[2] = 0x01 << 4; /* Master mode */
-
- st->buf[3] = 0x00;
-
- deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
-
- st->channel_state &= ~0x3;
- if ((adap->fe_adap[0].stream.props.endpoint != 2)
- && (adap->fe_adap[0].stream.props.endpoint != 3)) {
- deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].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->fe_adap[0].stream.props.endpoint-2);
- else
- st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint);
- }
-
- st->buf[2] |= st->channel_state;
-
- deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
-
- ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
- mutex_unlock(&adap->dev->usb_mutex);
-
- return ret;
-}
-
-int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
-{
- struct dvb_usb_device *d = rc->priv;
- struct dib0700_state *st = d->priv;
- int new_proto, ret;
-
- if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
- err("could not acquire lock");
- return -EINTR;
- }
-
- st->buf[0] = REQUEST_SET_RC;
- st->buf[1] = 0;
- st->buf[2] = 0;
-
- /* Set the IR mode */
- if (rc_type == RC_TYPE_RC5)
- new_proto = 1;
- else if (rc_type == RC_TYPE_NEC)
- new_proto = 0;
- else if (rc_type == RC_TYPE_RC6) {
- if (st->fw_version < 0x10200) {
- ret = -EINVAL;
- goto out;
- }
-
- new_proto = 2;
- } else {
- ret = -EINVAL;
- goto out;
- }
-
- st->buf[1] = new_proto;
-
- ret = dib0700_ctrl_wr(d, st->buf, 3);
- if (ret < 0) {
- err("ir protocol setup failed");
- goto out;
- }
-
- d->props.rc.core.protocol = rc_type;
-
-out:
- mutex_unlock(&d->usb_mutex);
- return ret;
-}
-
-/* Number of keypresses to ignore before start repeating */
-#define RC_REPEAT_DELAY_V1_20 10
-
-/* This is the structure of the RC response packet starting in firmware 1.20 */
-struct dib0700_rc_response {
- u8 report_id;
- u8 data_state;
- union {
- u16 system16;
- struct {
- u8 not_system;
- u8 system;
- };
- };
- u8 data;
- u8 not_data;
-};
-#define RC_MSG_SIZE_V1_20 6
-
-static void dib0700_rc_urb_completion(struct urb *purb)
-{
- struct dvb_usb_device *d = purb->context;
- struct dib0700_rc_response *poll_reply;
- u32 uninitialized_var(keycode);
- u8 toggle;
-
- deb_info("%s()\n", __func__);
- if (d->rc_dev == NULL) {
- /* This will occur if disable_rc_polling=1 */
- kfree(purb->transfer_buffer);
- usb_free_urb(purb);
- return;
- }
-
- poll_reply = purb->transfer_buffer;
-
- if (purb->status < 0) {
- deb_info("discontinuing polling\n");
- kfree(purb->transfer_buffer);
- usb_free_urb(purb);
- return;
- }
-
- if (purb->actual_length != RC_MSG_SIZE_V1_20) {
- deb_info("malformed rc msg size=%d\n", purb->actual_length);
- goto resubmit;
- }
-
- deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
- poll_reply->report_id, poll_reply->data_state,
- poll_reply->system, poll_reply->not_system,
- poll_reply->data, poll_reply->not_data,
- purb->actual_length);
-
- switch (d->props.rc.core.protocol) {
- case RC_TYPE_NEC:
- toggle = 0;
-
- /* NEC protocol sends repeat code as 0 0 0 FF */
- if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00)
- && (poll_reply->not_data == 0xff)) {
- poll_reply->data_state = 2;
- break;
- }
-
- if ((poll_reply->system ^ poll_reply->not_system) != 0xff) {
- deb_data("NEC extended protocol\n");
- /* NEC extended code - 24 bits */
- keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data;
- } else {
- deb_data("NEC normal protocol\n");
- /* normal NEC code - 16 bits */
- keycode = poll_reply->system << 8 | poll_reply->data;
- }
-
- break;
- default:
- deb_data("RC5 protocol\n");
- /* RC5 Protocol */
- toggle = poll_reply->report_id;
- keycode = poll_reply->system << 8 | poll_reply->data;
-
- break;
- }
-
- if ((poll_reply->data + poll_reply->not_data) != 0xff) {
- /* Key failed integrity check */
- err("key failed integrity check: %04x %02x %02x",
- poll_reply->system,
- poll_reply->data, poll_reply->not_data);
- goto resubmit;
- }
-
- rc_keydown(d->rc_dev, keycode, toggle);
-
-resubmit:
- /* Clean the buffer before we requeue */
- memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
-
- /* Requeue URB */
- usb_submit_urb(purb, GFP_ATOMIC);
-}
-
-int dib0700_rc_setup(struct dvb_usb_device *d)
-{
- struct dib0700_state *st = d->priv;
- struct urb *purb;
- int ret;
-
- /* Poll-based. Don't initialize bulk mode */
- if (st->fw_version < 0x10200)
- return 0;
-
- /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
- purb = usb_alloc_urb(0, GFP_KERNEL);
- if (purb == NULL) {
- err("rc usb alloc urb failed\n");
- return -ENOMEM;
- }
-
- purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
- if (purb->transfer_buffer == NULL) {
- err("rc kzalloc failed\n");
- usb_free_urb(purb);
- return -ENOMEM;
- }
-
- purb->status = -EINPROGRESS;
- usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1),
- purb->transfer_buffer, RC_MSG_SIZE_V1_20,
- dib0700_rc_urb_completion, d);
-
- ret = usb_submit_urb(purb, GFP_ATOMIC);
- if (ret) {
- err("rc submit urb failed\n");
- kfree(purb->transfer_buffer);
- usb_free_urb(purb);
- }
-
- return ret;
-}
-
-static int dib0700_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int i;
- struct dvb_usb_device *dev;
-
- for (i = 0; i < dib0700_device_count; i++)
- if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
- &dev, adapter_nr) == 0) {
- struct dib0700_state *st = dev->priv;
- u32 hwversion, romversion, fw_version, fwtype;
-
- dib0700_get_version(dev, &hwversion, &romversion,
- &fw_version, &fwtype);
-
- deb_info("Firmware version: %x, %d, 0x%x, %d\n",
- hwversion, romversion, fw_version, fwtype);
-
- st->fw_version = fw_version;
- st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
-
- /* Disable polling mode on newer firmwares */
- if (st->fw_version >= 0x10200)
- dev->props.rc.core.bulk_mode = true;
- else
- dev->props.rc.core.bulk_mode = false;
-
- dib0700_rc_setup(dev);
-
- return 0;
- }
-
- return -ENODEV;
-}
-
-static struct usb_driver dib0700_driver = {
- .name = "dvb_usb_dib0700",
- .probe = dib0700_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = dib0700_usb_id_table,
-};
-
-module_usb_driver(dib0700_driver);
-
-MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
-MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
-MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
-MODULE_VERSION("1.0");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
deleted file mode 100644
index ddf282f355b3..000000000000
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ /dev/null
@@ -1,289 +0,0 @@
-/* dvb-usb-dvb.c is part of the DVB USB library.
- *
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
- * see dvb-usb-init.c for copyright information.
- *
- * This file contains functions for initializing and handling the
- * linux-dvb API.
- */
-#include "dvb-usb-common.h"
-
-/* does the complete input transfer handling */
-static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
-{
- struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv;
- int newfeedcount, ret;
-
- if (adap == NULL)
- return -ENODEV;
-
- if ((adap->active_fe < 0) ||
- (adap->active_fe >= adap->num_frontends_initialized)) {
- return -EINVAL;
- }
-
- newfeedcount = adap->feedcount + (onoff ? 1 : -1);
-
- /* stop feed before setting a new pid if there will be no pid anymore */
- if (newfeedcount == 0) {
- deb_ts("stop feeding\n");
- usb_urb_kill(&adap->fe_adap[adap->active_fe].stream);
-
- if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
- ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0);
- if (ret < 0) {
- err("error while stopping stream.");
- return ret;
- }
- }
- }
-
- adap->feedcount = newfeedcount;
-
- /* activate the pid on the device specific pid_filter */
- deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",
- adap->fe_adap[adap->active_fe].pid_filtering ?
- "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid,
- dvbdmxfeed->index, onoff ? "on" : "off");
- if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->fe_adap[adap->active_fe].pid_filtering &&
- adap->props.fe[adap->active_fe].pid_filter != NULL)
- adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
-
- /* start the feed if this was the first feed and there is still a feed
- * for reception.
- */
- if (adap->feedcount == onoff && adap->feedcount > 0) {
- deb_ts("submitting all URBs\n");
- usb_urb_submit(&adap->fe_adap[adap->active_fe].stream);
-
- deb_ts("controlling pid parser\n");
- if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
- adap->props.fe[adap->active_fe].caps &
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
- adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) {
- ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap,
- adap->fe_adap[adap->active_fe].pid_filtering);
- if (ret < 0) {
- err("could not handle pid_parser");
- return ret;
- }
- }
- deb_ts("start feeding\n");
- if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
- ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1);
- if (ret < 0) {
- err("error while enabling fifo.");
- return ret;
- }
- }
-
- }
- return 0;
-}
-
-static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
- return dvb_usb_ctrl_feed(dvbdmxfeed,1);
-}
-
-static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
- return dvb_usb_ctrl_feed(dvbdmxfeed,0);
-}
-
-int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
-{
- int i;
- int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
- adap->dev->owner, &adap->dev->udev->dev,
- adapter_nums);
-
- if (ret < 0) {
- deb_info("dvb_register_adapter failed: error %d", ret);
- goto err;
- }
- adap->dvb_adap.priv = adap;
- adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override;
-
- if (adap->dev->props.read_mac_address) {
- if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
- info("MAC address: %pM",adap->dvb_adap.proposed_mac);
- else
- err("MAC address reading failed.");
- }
-
-
- adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
- adap->demux.priv = adap;
-
- adap->demux.filternum = 0;
- for (i = 0; i < adap->props.num_frontends; i++) {
- if (adap->demux.filternum < adap->fe_adap[i].max_feed_count)
- adap->demux.filternum = adap->fe_adap[i].max_feed_count;
- }
- adap->demux.feednum = adap->demux.filternum;
- adap->demux.start_feed = dvb_usb_start_feed;
- adap->demux.stop_feed = dvb_usb_stop_feed;
- adap->demux.write_to_decoder = NULL;
- if ((ret = dvb_dmx_init(&adap->demux)) < 0) {
- err("dvb_dmx_init failed: error %d",ret);
- goto err_dmx;
- }
-
- adap->dmxdev.filternum = adap->demux.filternum;
- adap->dmxdev.demux = &adap->demux.dmx;
- adap->dmxdev.capabilities = 0;
- if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) {
- err("dvb_dmxdev_init failed: error %d",ret);
- goto err_dmx_dev;
- }
-
- if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net,
- &adap->demux.dmx)) < 0) {
- err("dvb_net_init failed: error %d",ret);
- goto err_net_init;
- }
-
- adap->state |= DVB_USB_ADAP_STATE_DVB;
- return 0;
-
-err_net_init:
- dvb_dmxdev_release(&adap->dmxdev);
-err_dmx_dev:
- dvb_dmx_release(&adap->demux);
-err_dmx:
- dvb_unregister_adapter(&adap->dvb_adap);
-err:
- return ret;
-}
-
-int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap)
-{
- if (adap->state & DVB_USB_ADAP_STATE_DVB) {
- deb_info("unregistering DVB part\n");
- dvb_net_release(&adap->dvb_net);
- adap->demux.dmx.close(&adap->demux.dmx);
- dvb_dmxdev_release(&adap->dmxdev);
- dvb_dmx_release(&adap->demux);
- dvb_unregister_adapter(&adap->dvb_adap);
- adap->state &= ~DVB_USB_ADAP_STATE_DVB;
- }
- return 0;
-}
-
-static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
-
- int ret = (adap->props.frontend_ctrl) ?
- adap->props.frontend_ctrl(fe, onoff) : 0;
-
- if (ret < 0) {
- err("frontend_ctrl request failed");
- return ret;
- }
- if (onoff)
- adap->active_fe = fe->id;
-
- return 0;
-}
-
-static int dvb_usb_fe_wakeup(struct dvb_frontend *fe)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
-
- dvb_usb_device_power_ctrl(adap->dev, 1);
-
- dvb_usb_set_active_fe(fe, 1);
-
- if (adap->fe_adap[fe->id].fe_init)
- adap->fe_adap[fe->id].fe_init(fe);
-
- return 0;
-}
-
-static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
-
- if (adap->fe_adap[fe->id].fe_sleep)
- adap->fe_adap[fe->id].fe_sleep(fe);
-
- dvb_usb_set_active_fe(fe, 0);
-
- return dvb_usb_device_power_ctrl(adap->dev, 0);
-}
-
-int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
-{
- int ret, i;
-
- /* register all given adapter frontends */
- for (i = 0; i < adap->props.num_frontends; i++) {
-
- if (adap->props.fe[i].frontend_attach == NULL) {
- err("strange: '%s' #%d,%d "
- "doesn't want to attach a frontend.",
- adap->dev->desc->name, adap->id, i);
-
- return 0;
- }
-
- ret = adap->props.fe[i].frontend_attach(adap);
- if (ret || adap->fe_adap[i].fe == NULL) {
- /* only print error when there is no FE at all */
- if (i == 0)
- err("no frontend was attached by '%s'",
- adap->dev->desc->name);
-
- return 0;
- }
-
- adap->fe_adap[i].fe->id = i;
-
- /* re-assign sleep and wakeup functions */
- adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init;
- adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup;
- adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
- adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
-
- if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
- err("Frontend %d registration failed.", i);
- dvb_frontend_detach(adap->fe_adap[i].fe);
- adap->fe_adap[i].fe = NULL;
- /* In error case, do not try register more FEs,
- * still leaving already registered FEs alive. */
- if (i == 0)
- return -ENODEV;
- else
- return 0;
- }
-
- /* only attach the tuner if the demod is there */
- if (adap->props.fe[i].tuner_attach != NULL)
- adap->props.fe[i].tuner_attach(adap);
-
- adap->num_frontends_initialized++;
- }
-
- return 0;
-}
-
-int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
-{
- int i = adap->num_frontends_initialized - 1;
-
- /* unregister all given adapter frontends */
- for (; i >= 0; i--) {
- if (adap->fe_adap[i].fe != NULL) {
- dvb_unregister_frontend(adap->fe_adap[i].fe);
- dvb_frontend_detach(adap->fe_adap[i].fe);
- }
- }
- adap->num_frontends_initialized = 0;
-
- return 0;
-}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
deleted file mode 100644
index 26c44818a5ab..000000000000
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ /dev/null
@@ -1,361 +0,0 @@
-/* dvb-usb-ids.h is part of the DVB USB library.
- *
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see
- * dvb-usb-init.c for copyright information.
- *
- * a header file containing define's for the USB device supported by the
- * various drivers.
- */
-#ifndef _DVB_USB_IDS_H_
-#define _DVB_USB_IDS_H_
-
-/* Vendor IDs */
-#define USB_VID_ADSTECH 0x06e1
-#define USB_VID_AFATECH 0x15a4
-#define USB_VID_ALCOR_MICRO 0x058f
-#define USB_VID_ALINK 0x05e3
-#define USB_VID_AMT 0x1c73
-#define USB_VID_ANCHOR 0x0547
-#define USB_VID_ANSONIC 0x10b9
-#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
-#define USB_VID_ASUS 0x0b05
-#define USB_VID_AVERMEDIA 0x07ca
-#define USB_VID_COMPRO 0x185b
-#define USB_VID_COMPRO_UNK 0x145f
-#define USB_VID_CONEXANT 0x0572
-#define USB_VID_CYPRESS 0x04b4
-#define USB_VID_DIBCOM 0x10b8
-#define USB_VID_DPOSH 0x1498
-#define USB_VID_DVICO 0x0fe9
-#define USB_VID_E3C 0x18b4
-#define USB_VID_ELGATO 0x0fd9
-#define USB_VID_EMPIA 0xeb1a
-#define USB_VID_GENPIX 0x09c0
-#define USB_VID_GRANDTEC 0x5032
-#define USB_VID_GTEK 0x1f4d
-#define USB_VID_HANFTEK 0x15f4
-#define USB_VID_HAUPPAUGE 0x2040
-#define USB_VID_HYPER_PALTEK 0x1025
-#define USB_VID_INTEL 0x8086
-#define USB_VID_ITETECH 0x048d
-#define USB_VID_KWORLD 0xeb2a
-#define USB_VID_KWORLD_2 0x1b80
-#define USB_VID_KYE 0x0458
-#define USB_VID_LEADTEK 0x0413
-#define USB_VID_LITEON 0x04ca
-#define USB_VID_MEDION 0x1660
-#define USB_VID_MIGLIA 0x18f3
-#define USB_VID_MSI 0x0db0
-#define USB_VID_MSI_2 0x1462
-#define USB_VID_OPERA1 0x695c
-#define USB_VID_PINNACLE 0x2304
-#define USB_VID_PCTV 0x2013
-#define USB_VID_PIXELVIEW 0x1554
-#define USB_VID_REALTEK 0x0bda
-#define USB_VID_TECHNOTREND 0x0b48
-#define USB_VID_TERRATEC 0x0ccd
-#define USB_VID_TELESTAR 0x10b9
-#define USB_VID_VISIONPLUS 0x13d3
-#define USB_VID_SONY 0x1415
-#define USB_VID_TWINHAN 0x1822
-#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
-#define USB_VID_UNIWILL 0x1584
-#define USB_VID_WIDEVIEW 0x14aa
-#define USB_VID_GIGABYTE 0x1044
-#define USB_VID_YUAN 0x1164
-#define USB_VID_XTENSIONS 0x1ae7
-#define USB_VID_HUMAX_COEX 0x10b9
-#define USB_VID_774 0x7a69
-#define USB_VID_EVOLUTEPC 0x1e59
-#define USB_VID_AZUREWAVE 0x13d3
-#define USB_VID_TECHNISAT 0x14f7
-
-/* Product IDs */
-#define USB_PID_ADSTECH_USB2_COLD 0xa333
-#define USB_PID_ADSTECH_USB2_WARM 0xa334
-#define USB_PID_AFATECH_AF9005 0x9020
-#define USB_PID_AFATECH_AF9015_9015 0x9015
-#define USB_PID_AFATECH_AF9015_9016 0x9016
-#define USB_PID_AFATECH_AF9035_1000 0x1000
-#define USB_PID_AFATECH_AF9035_1001 0x1001
-#define USB_PID_AFATECH_AF9035_1002 0x1002
-#define USB_PID_AFATECH_AF9035_1003 0x1003
-#define USB_PID_AFATECH_AF9035_9035 0x9035
-#define USB_PID_TREKSTOR_DVBT 0x901b
-#define USB_VID_ALINK_DTU 0xf170
-#define USB_PID_ANSONIC_DVBT_USB 0x6000
-#define USB_PID_ANYSEE 0x861f
-#define USB_PID_AZUREWAVE_AD_TU700 0x3237
-#define USB_PID_AZUREWAVE_6007 0x0ccd
-#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
-#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
-#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
-#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
-#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
-#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
-#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
-#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
-#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78
-#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80
-#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397
-#define USB_PID_CONEXANT_D680_DMB 0x86d6
-#define USB_PID_CREATIX_CTX1921 0x1921
-#define USB_PID_DELOCK_USB2_DVBT 0xb803
-#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
-#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
-#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
-#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
-#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
-#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
-#define USB_PID_DIBCOM_STK7700P 0x1e14
-#define USB_PID_DIBCOM_STK7700P_PC 0x1e78
-#define USB_PID_DIBCOM_STK7700D 0x1ef0
-#define USB_PID_DIBCOM_STK7700_U7000 0x7001
-#define USB_PID_DIBCOM_STK7070P 0x1ebc
-#define USB_PID_DIBCOM_STK7070PD 0x1ebe
-#define USB_PID_DIBCOM_STK807XP 0x1f90
-#define USB_PID_DIBCOM_STK807XPVR 0x1f98
-#define USB_PID_DIBCOM_STK8096GP 0x1fa0
-#define USB_PID_DIBCOM_NIM8096MD 0x1fa8
-#define USB_PID_DIBCOM_TFE8096P 0x1f9C
-#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_TFE7090E 0x1bb7
-#define USB_PID_DIBCOM_TFE7790E 0x1e6e
-#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
-#define USB_PID_E3C_EC168_2 0xfffa
-#define USB_PID_E3C_EC168_3 0xfffb
-#define USB_PID_E3C_EC168_4 0x1001
-#define USB_PID_E3C_EC168_5 0x1002
-#define USB_PID_FREECOM_DVBT 0x0160
-#define USB_PID_FREECOM_DVBT_2 0x0161
-#define USB_PID_UNIWILL_STK7700P 0x6003
-#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
-#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
-#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
-#define USB_PID_INTEL_CE9500 0x9500
-#define USB_PID_ITETECH_IT9135 0x9135
-#define USB_PID_ITETECH_IT9135_9005 0x9005
-#define USB_PID_ITETECH_IT9135_9006 0x9006
-#define USB_PID_KWORLD_399U 0xe399
-#define USB_PID_KWORLD_399U_2 0xe400
-#define USB_PID_KWORLD_395U 0xe396
-#define USB_PID_KWORLD_395U_2 0xe39b
-#define USB_PID_KWORLD_395U_3 0xe395
-#define USB_PID_KWORLD_395U_4 0xe39a
-#define USB_PID_KWORLD_MC810 0xc810
-#define USB_PID_KWORLD_PC160_2T 0xc160
-#define USB_PID_KWORLD_PC160_T 0xc161
-#define USB_PID_KWORLD_UB383_T 0xe383
-#define USB_PID_KWORLD_UB499_2T_T09 0xe409
-#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
-#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
-#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
-#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
-#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
-#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097
-#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099
-#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9
-#define USB_PID_TWINHAN_VP7041_COLD 0x3201
-#define USB_PID_TWINHAN_VP7041_WARM 0x3202
-#define USB_PID_TWINHAN_VP7020_COLD 0x3203
-#define USB_PID_TWINHAN_VP7020_WARM 0x3204
-#define USB_PID_TWINHAN_VP7045_COLD 0x3205
-#define USB_PID_TWINHAN_VP7045_WARM 0x3206
-#define USB_PID_TWINHAN_VP7021_COLD 0x3207
-#define USB_PID_TWINHAN_VP7021_WARM 0x3208
-#define USB_PID_TINYTWIN 0x3226
-#define USB_PID_TINYTWIN_2 0xe402
-#define USB_PID_TINYTWIN_3 0x9016
-#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
-#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
-#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
-#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
-#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
-#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
-#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
-#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
-#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
-#define USB_PID_ARTEC_T14_COLD 0x810b
-#define USB_PID_ARTEC_T14_WARM 0x810c
-#define USB_PID_ARTEC_T14BR 0x810f
-#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
-#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
-#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
-#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
-#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
-#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
-#define USB_PID_DTT200U_COLD 0x0201
-#define USB_PID_DTT200U_WARM 0x0301
-#define USB_PID_WT220U_ZAP250_COLD 0x0220
-#define USB_PID_WT220U_COLD 0x0222
-#define USB_PID_WT220U_WARM 0x0221
-#define USB_PID_WT220U_FC_COLD 0x0225
-#define USB_PID_WT220U_FC_WARM 0x0226
-#define USB_PID_WT220U_ZL0353_COLD 0x022a
-#define USB_PID_WT220U_ZL0353_WARM 0x022b
-#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
-#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
-#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941
-#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950
-#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400
-#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050
-#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060
-#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070
-#define USB_PID_HAUPPAUGE_MYTV_T 0x7080
-#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
-#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200
-#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200
-#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210
-#define USB_PID_AVERMEDIA_EXPRESS 0xb568
-#define USB_PID_AVERMEDIA_VOLAR 0xa807
-#define USB_PID_AVERMEDIA_VOLAR_2 0xb808
-#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868
-#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228
-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039
-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039
-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039
-#define USB_PID_AVERMEDIA_VOLAR_X 0xa815
-#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150
-#define USB_PID_AVERMEDIA_A309 0xa309
-#define USB_PID_AVERMEDIA_A310 0xa310
-#define USB_PID_AVERMEDIA_A850 0x850a
-#define USB_PID_AVERMEDIA_A850T 0x850b
-#define USB_PID_AVERMEDIA_A805 0xa805
-#define USB_PID_AVERMEDIA_A815M 0x815a
-#define USB_PID_AVERMEDIA_A835 0xa835
-#define USB_PID_AVERMEDIA_B835 0xb835
-#define USB_PID_AVERMEDIA_1867 0x1867
-#define USB_PID_AVERMEDIA_A867 0xa867
-#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
-#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
-#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
-#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
-#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081
-#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058
-#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060
-#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
-#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
-#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
-#define USB_PID_TERRATEC_H7 0x10b4
-#define USB_PID_TERRATEC_H7_2 0x10a3
-#define USB_PID_TERRATEC_T3 0x10a0
-#define USB_PID_TERRATEC_T5 0x10a1
-#define USB_PID_NOXON_DAB_STICK 0x00b3
-#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
-#define USB_PID_PINNACLE_PCTV2000E 0x022c
-#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228
-#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229
-#define USB_PID_PINNACLE_PCTV71E 0x022b
-#define USB_PID_PINNACLE_PCTV72E 0x0236
-#define USB_PID_PINNACLE_PCTV73E 0x0237
-#define USB_PID_PINNACLE_PCTV310E 0x3211
-#define USB_PID_PINNACLE_PCTV801E 0x023a
-#define USB_PID_PINNACLE_PCTV801E_SE 0x023b
-#define USB_PID_PINNACLE_PCTV340E 0x023d
-#define USB_PID_PINNACLE_PCTV340E_SE 0x023e
-#define USB_PID_PINNACLE_PCTV73A 0x0243
-#define USB_PID_PINNACLE_PCTV73ESE 0x0245
-#define USB_PID_PINNACLE_PCTV74E 0x0246
-#define USB_PID_PINNACLE_PCTV282E 0x0248
-#define USB_PID_PIXELVIEW_SBTVD 0x5010
-#define USB_PID_PCTV_200E 0x020e
-#define USB_PID_PCTV_400E 0x020f
-#define USB_PID_PCTV_450E 0x0222
-#define USB_PID_PCTV_452E 0x021f
-#define USB_PID_REALTEK_RTL2831U 0x2831
-#define USB_PID_REALTEK_RTL2832U 0x2832
-#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
-#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
-#define USB_PID_NEBULA_DIGITV 0x0201
-#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
-#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
-#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501
-#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00
-#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01
-#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10
-#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11
-#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50
-#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51
-#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58
-#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59
-#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78
-#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98
-#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70
-#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54
-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55
-#define USB_PID_MEDION_MD95700 0x0932
-#define USB_PID_MSI_MEGASKY580 0x5580
-#define USB_PID_MSI_MEGASKY580_55801 0x5581
-#define USB_PID_KYE_DVB_T_COLD 0x701e
-#define USB_PID_KYE_DVB_T_WARM 0x701f
-#define USB_PID_LITEON_DVB_T_COLD 0xf000
-#define USB_PID_LITEON_DVB_T_WARM 0xf001
-#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
-#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
-#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
-#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
-#define USB_PID_WINFAST_DTV2000DS 0x6a04
-#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
-#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
-#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00
-#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6
-#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01
-#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029
-#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200
-#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201
-#define USB_PID_GENPIX_8PSK_REV_2 0x0202
-#define USB_PID_GENPIX_SKYWALKER_1 0x0203
-#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204
-#define USB_PID_GENPIX_SKYWALKER_2 0x0206
-#define USB_PID_SIGMATEK_DVB_110 0x6610
-#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513
-#define USB_PID_MSI_DIGIVOX_DUO 0x8801
-#define USB_PID_OPERA1_COLD 0x2830
-#define USB_PID_OPERA1_WARM 0x3829
-#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514
-#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513
-#define USB_PID_GIGABYTE_U7000 0x7001
-#define USB_PID_GIGABYTE_U8000 0x7002
-#define USB_PID_ASUS_U3000 0x171f
-#define USB_PID_ASUS_U3000H 0x1736
-#define USB_PID_ASUS_U3100 0x173f
-#define USB_PID_YUAN_EC372S 0x1edc
-#define USB_PID_YUAN_STK7700PH 0x1f08
-#define USB_PID_YUAN_PD378S 0x2edc
-#define USB_PID_YUAN_MC770 0x0871
-#define USB_PID_YUAN_STK7700D 0x1efc
-#define USB_PID_YUAN_STK7700D_2 0x1e8c
-#define USB_PID_DW2102 0x2102
-#define USB_PID_XTENSIONS_XD_380 0x0381
-#define USB_PID_TELESTAR_STARSTICK_2 0x8000
-#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
-#define USB_PID_SONY_PLAYTV 0x0003
-#define USB_PID_MYGICA_D689 0xd811
-#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
-#define USB_PID_ELGATO_EYETV_DTT 0x0021
-#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
-#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
-#define USB_PID_ELGATO_EYETV_SAT 0x002a
-#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
-#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
-#define USB_PID_FRIIO_WHITE 0x0001
-#define USB_PID_TVWAY_PLUS 0x0002
-#define USB_PID_SVEON_STV20 0xe39d
-#define USB_PID_SVEON_STV22 0xe401
-#define USB_PID_SVEON_STV22_IT9137 0xe411
-#define USB_PID_AZUREWAVE_AZ6027 0x3275
-#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4
-#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.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
deleted file mode 100644
index 99f94409efa1..000000000000
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ /dev/null
@@ -1,485 +0,0 @@
-/* dvb-usb.h is part of the DVB USB library.
- *
- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
- * see dvb-usb-init.c for copyright information.
- *
- * the headerfile, all dvb-usb-drivers have to include.
- *
- * TODO: clean-up the structures for unused fields and update the comments
- */
-#ifndef __DVB_USB_H__
-#define __DVB_USB_H__
-
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/firmware.h>
-#include <linux/mutex.h>
-#include <media/rc-core.h>
-
-#include "dvb_frontend.h"
-#include "dvb_demux.h"
-#include "dvb_net.h"
-#include "dmxdev.h"
-
-#include "dvb-pll.h"
-
-#include "dvb-usb-ids.h"
-
-/* debug */
-#ifdef CONFIG_DVB_USB_DEBUG
-#define dprintk(var,level,args...) \
- do { if ((var & level)) { printk(args); } } while (0)
-
-#define debug_dump(b,l,func) {\
- int loop_; \
- for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \
- func("\n");\
-}
-#define DVB_USB_DEBUG_STATUS
-#else
-#define dprintk(args...)
-#define debug_dump(b,l,func)
-
-#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)"
-
-#endif
-
-/* generic log methods - taken from usb.h */
-#ifndef DVB_USB_LOG_PREFIX
- #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)"
-#endif
-
-#undef err
-#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
-#undef info
-#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
-#undef warn
-#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
-
-/**
- * struct dvb_usb_device_description - name and its according USB IDs
- * @name: real name of the box, regardless which DVB USB device class is in use
- * @cold_ids: array of struct usb_device_id which describe the device in
- * pre-firmware state
- * @warm_ids: array of struct usb_device_id which describe the device in
- * post-firmware state
- *
- * Each DVB USB device class can have one or more actual devices, this struct
- * assigns a name to it.
- */
-struct dvb_usb_device_description {
- const char *name;
-
-#define DVB_USB_ID_MAX_NUM 15
- struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM];
- struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM];
-};
-
-static inline u8 rc5_custom(struct rc_map_table *key)
-{
- return (key->scancode >> 8) & 0xff;
-}
-
-static inline u8 rc5_data(struct rc_map_table *key)
-{
- return key->scancode & 0xff;
-}
-
-static inline u16 rc5_scan(struct rc_map_table *key)
-{
- return key->scancode & 0xffff;
-}
-
-struct dvb_usb_device;
-struct dvb_usb_adapter;
-struct usb_data_stream;
-
-/**
- * Properties of USB streaming - TODO this structure should be somewhere else
- * describes the kind of USB transfer used for data-streaming.
- * (BULK or ISOC)
- */
-struct usb_data_stream_properties {
-#define USB_BULK 1
-#define USB_ISOC 2
- int type;
- int count;
- int endpoint;
-
- union {
- struct {
- int buffersize; /* per URB */
- } bulk;
- struct {
- int framesperurb;
- int framesize;
- int interval;
- } isoc;
- } u;
-};
-
-/**
- * struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter.
- * A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device.
- * @caps: capabilities of the DVB USB device.
- * @pid_filter_count: number of PID filter position in the optional hardware
- * PID-filter.
- * @num_frontends: number of frontends of the DVB USB adapter.
- * @frontend_ctrl: called to power on/off active frontend.
- * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the
- * device (not URB submitting/killing).
- * @pid_filter_ctrl: called to en/disable the PID filter, if any.
- * @pid_filter: called to set/unset a PID for filtering.
- * @frontend_attach: called to attach the possible frontends (fill fe-field
- * of struct dvb_usb_device).
- * @tuner_attach: called to attach the correct tuner and to fill pll_addr,
- * pll_desc and pll_init_buf of struct dvb_usb_device).
- * @stream: configuration of the USB streaming
- */
-struct dvb_usb_adapter_fe_properties {
-#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
-#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
-#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
-#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08
-#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10
- int caps;
- int pid_filter_count;
-
- int (*streaming_ctrl) (struct dvb_usb_adapter *, int);
- int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int);
- int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int);
-
- int (*frontend_attach) (struct dvb_usb_adapter *);
- int (*tuner_attach) (struct dvb_usb_adapter *);
-
- struct usb_data_stream_properties stream;
-
- int size_of_priv;
-};
-
-#define MAX_NO_OF_FE_PER_ADAP 3
-struct dvb_usb_adapter_properties {
- int size_of_priv;
-
- int (*frontend_ctrl) (struct dvb_frontend *, int);
- int (*fe_ioctl_override) (struct dvb_frontend *,
- unsigned int, void *, unsigned int);
-
- int num_frontends;
- struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP];
-};
-
-/**
- * struct dvb_rc_legacy - old properties of remote controller
- * @rc_map_table: a hard-wired array of struct rc_map_table (NULL to disable
- * remote control handling).
- * @rc_map_size: number of items in @rc_map_table.
- * @rc_query: called to query an event event.
- * @rc_interval: time in ms between two queries.
- */
-struct dvb_rc_legacy {
-/* remote control properties */
-#define REMOTE_NO_KEY_PRESSED 0x00
-#define REMOTE_KEY_PRESSED 0x01
-#define REMOTE_KEY_REPEAT 0x02
- struct rc_map_table *rc_map_table;
- int rc_map_size;
- int (*rc_query) (struct dvb_usb_device *, u32 *, int *);
- int rc_interval;
-};
-
-/**
- * struct dvb_rc properties of remote controller, using rc-core
- * @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.
- * @bulk_mode: device supports bulk mode for RC (disable polling mode)
- */
-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);
- int rc_interval;
- bool bulk_mode; /* uses bulk mode */
-};
-
-/**
- * enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one
- * based on rc-core
- * This is initialized/used only inside dvb-usb-remote.c.
- * It shouldn't be set by the drivers.
- */
-enum dvb_usb_mode {
- DVB_RC_LEGACY,
- DVB_RC_CORE,
-};
-
-/**
- * struct dvb_usb_device_properties - properties of a dvb-usb-device
- * @usb_ctrl: which USB device-side controller is in use. Needed for firmware
- * download.
- * @firmware: name of the firmware file.
- * @download_firmware: called to download the firmware when the usb_ctrl is
- * DEVICE_SPECIFIC.
- * @no_reconnect: device doesn't do a reconnect after downloading the firmware,
- * so do the warm initialization right after it
- *
- * @size_of_priv: how many bytes shall be allocated for the private field
- * of struct dvb_usb_device.
- *
- * @power_ctrl: called to enable/disable power of the device.
- * @read_mac_address: called to read the MAC address of the device.
- * @identify_state: called to determine the state (cold or warm), when it
- * is not distinguishable by the USB IDs.
- *
- * @rc: remote controller properties
- *
- * @i2c_algo: i2c_algorithm if the device has I2CoverUSB.
- *
- * @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic
- * endpoint which received control messages with bulk transfers. When this
- * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write-
- * helper functions.
- *
- * @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate
- * endpoint for responses to control messages sent with bulk transfers via
- * the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used
- * instead of the generic_bulk_ctrl_endpoint when reading usb responses in
- * the dvb_usb_generic_rw helper function.
- *
- * @num_device_descs: number of struct dvb_usb_device_description in @devices
- * @devices: array of struct dvb_usb_device_description compatibles with these
- * properties.
- */
-#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
-struct dvb_usb_device_properties {
-
-#define DVB_USB_IS_AN_I2C_ADAPTER 0x01
- int caps;
-
-#define DEVICE_SPECIFIC 0
-#define CYPRESS_AN2135 1
-#define CYPRESS_AN2235 2
-#define CYPRESS_FX2 3
- int usb_ctrl;
- int (*download_firmware) (struct usb_device *, const struct firmware *);
- const char *firmware;
- int no_reconnect;
-
- int size_of_priv;
-
- int num_adapters;
- struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
-
- int (*power_ctrl) (struct dvb_usb_device *, int);
- int (*read_mac_address) (struct dvb_usb_device *, u8 []);
- int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *,
- struct dvb_usb_device_description **, int *);
-
- struct {
- enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */
- struct dvb_rc_legacy legacy;
- struct dvb_rc core;
- } rc;
-
- struct i2c_algorithm *i2c_algo;
-
- int generic_bulk_ctrl_endpoint;
- int generic_bulk_ctrl_endpoint_response;
-
- int num_device_descs;
- struct dvb_usb_device_description devices[12];
-};
-
-/**
- * struct usb_data_stream - generic object of an USB stream
- * @buf_num: number of buffer allocated.
- * @buf_size: size of each buffer in buf_list.
- * @buf_list: array containing all allocate buffers for streaming.
- * @dma_addr: list of dma_addr_t for each buffer in buf_list.
- *
- * @urbs_initialized: number of URBs initialized.
- * @urbs_submitted: number of URBs submitted.
- */
-#define MAX_NO_URBS_FOR_DATA_STREAM 10
-struct usb_data_stream {
- struct usb_device *udev;
- struct usb_data_stream_properties props;
-
-#define USB_STATE_INIT 0x00
-#define USB_STATE_URB_BUF 0x01
- int state;
-
- void (*complete) (struct usb_data_stream *, u8 *, size_t);
-
- struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM];
- int buf_num;
- unsigned long buf_size;
- u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM];
- dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM];
-
- int urbs_initialized;
- int urbs_submitted;
-
- void *user_priv;
-};
-
-/**
- * struct dvb_usb_adapter - a DVB adapter on a USB device
- * @id: index of this adapter (starting with 0).
- *
- * @feedcount: number of reqested feeds (used for streaming-activation)
- * @pid_filtering: is hardware pid_filtering used or not.
- *
- * @pll_addr: I2C address of the tuner for programming
- * @pll_init: array containing the initialization buffer
- * @pll_desc: pointer to the appropriate struct dvb_pll_desc
- * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board
- *
- * @dvb_adap: device's dvb_adapter.
- * @dmxdev: device's dmxdev.
- * @demux: device's software demuxer.
- * @dvb_net: device's dvb_net interfaces.
- * @dvb_frontend: device's frontend.
- * @max_feed_count: how many feeds can be handled simultaneously by this
- * device
- *
- * @fe_init: rerouted frontend-init (wakeup) function.
- * @fe_sleep: rerouted frontend-sleep function.
- *
- * @stream: the usb data stream.
- */
-struct dvb_usb_fe_adapter {
- struct dvb_frontend *fe;
-
- int (*fe_init) (struct dvb_frontend *);
- int (*fe_sleep) (struct dvb_frontend *);
-
- struct usb_data_stream stream;
-
- int pid_filtering;
- int max_feed_count;
-
- void *priv;
-};
-
-struct dvb_usb_adapter {
- struct dvb_usb_device *dev;
- struct dvb_usb_adapter_properties props;
-
-#define DVB_USB_ADAP_STATE_INIT 0x000
-#define DVB_USB_ADAP_STATE_DVB 0x001
- int state;
-
- u8 id;
-
- int feedcount;
-
- /* dvb */
- struct dvb_adapter dvb_adap;
- struct dmxdev dmxdev;
- struct dvb_demux demux;
- struct dvb_net dvb_net;
-
- struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP];
- int active_fe;
- int num_frontends_initialized;
-
- void *priv;
-};
-
-/**
- * struct dvb_usb_device - object of a DVB USB device
- * @props: copy of the struct dvb_usb_properties this device belongs to.
- * @desc: pointer to the device's struct dvb_usb_device_description.
- * @state: initialization and runtime state of the device.
- *
- * @powered: indicated whether the device is power or not.
- * Powered is in/decremented for each call to modify the state.
- * @udev: pointer to the device's struct usb_device.
- *
- * @usb_mutex: semaphore of USB control messages (reading needs two messages)
- * @i2c_mutex: semaphore for i2c-transfers
- *
- * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB
- *
- * @rc_dev: rc device for the remote control (rc-core mode)
- * @input_dev: input device for the remote control (legacy mode)
- * @rc_query_work: struct work_struct frequent rc queries
- * @last_event: last triggered event
- * @last_state: last state (no, pressed, repeat)
- * @owner: owner of the dvb_adapter
- * @priv: private data of the actual driver (allocate by dvb-usb, size defined
- * in size_of_priv of dvb_usb_properties).
- */
-struct dvb_usb_device {
- struct dvb_usb_device_properties props;
- struct dvb_usb_device_description *desc;
-
- struct usb_device *udev;
-
-#define DVB_USB_STATE_INIT 0x000
-#define DVB_USB_STATE_I2C 0x001
-#define DVB_USB_STATE_DVB 0x002
-#define DVB_USB_STATE_REMOTE 0x004
- int state;
-
- int powered;
-
- /* locking */
- struct mutex usb_mutex;
-
- /* i2c */
- struct mutex i2c_mutex;
- struct i2c_adapter i2c_adap;
-
- int num_adapters_initialized;
- struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
-
- /* remote control */
- struct rc_dev *rc_dev;
- struct input_dev *input_dev;
- char rc_phys[64];
- struct delayed_work rc_query_work;
- u32 last_event;
- int last_state;
-
- struct module *owner;
-
- void *priv;
-};
-
-extern int dvb_usb_device_init(struct usb_interface *,
- struct dvb_usb_device_properties *,
- struct module *, struct dvb_usb_device **,
- short *adapter_nums);
-extern void dvb_usb_device_exit(struct usb_interface *);
-
-/* the generic read/write method for device control */
-extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int);
-extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16);
-
-/* commonly used remote control parsing */
-extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *);
-
-/* commonly used firmware download types and function */
-struct hexline {
- u8 len;
- u32 addr;
- u8 type;
- u8 data[255];
- u8 chk;
-};
-extern int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type);
-extern int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, int *pos);
-
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c
deleted file mode 100644
index b4989ba8897d..000000000000
--- a/drivers/media/dvb/dvb-usb/ec168.c
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * E3C EC168 DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "ec168.h"
-#include "ec100.h"
-#include "mxl5005s.h"
-
-/* debug */
-static int dvb_usb_ec168_debug;
-module_param_named(debug, dvb_usb_ec168_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static struct ec100_config ec168_ec100_config;
-
-static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req)
-{
- int ret;
- unsigned int pipe;
- u8 request, requesttype;
- u8 *buf;
-
-
-
- switch (req->cmd) {
- case DOWNLOAD_FIRMWARE:
- case GPIO:
- case WRITE_I2C:
- case STREAMING_CTRL:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
- request = req->cmd;
- break;
- case READ_I2C:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
- request = req->cmd;
- break;
- case GET_CONFIG:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
- request = CONFIG;
- break;
- case SET_CONFIG:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
- request = CONFIG;
- break;
- case WRITE_DEMOD:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
- request = DEMOD_RW;
- break;
- case READ_DEMOD:
- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
- request = DEMOD_RW;
- break;
- default:
- err("unknown command:%02x", req->cmd);
- ret = -EPERM;
- goto error;
- }
-
- buf = kmalloc(req->size, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto error;
- }
-
- if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
- /* write */
- memcpy(buf, req->data, req->size);
- pipe = usb_sndctrlpipe(udev, 0);
- } else {
- /* read */
- pipe = usb_rcvctrlpipe(udev, 0);
- }
-
- msleep(1); /* avoid I2C errors */
-
- ret = usb_control_msg(udev, pipe, request, requesttype, req->value,
- req->index, buf, req->size, EC168_USB_TIMEOUT);
-
- ec168_debug_dump(request, requesttype, req->value, req->index, buf,
- req->size, deb_xfer);
-
- if (ret < 0)
- goto err_dealloc;
- else
- ret = 0;
-
- /* read request, copy returned data to return buf */
- if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
- memcpy(req->data, buf, req->size);
-
- kfree(buf);
- return ret;
-
-err_dealloc:
- kfree(buf);
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
-{
- return ec168_rw_udev(d->udev, req);
-}
-
-/* I2C */
-static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct ec168_req req;
- int i = 0;
- int ret;
-
- if (num > 2)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- while (i < num) {
- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
- if (msg[i].addr == ec168_ec100_config.demod_address) {
- req.cmd = READ_DEMOD;
- req.value = 0;
- req.index = 0xff00 + msg[i].buf[0]; /* reg */
- req.size = msg[i+1].len; /* bytes to read */
- req.data = &msg[i+1].buf[0];
- ret = ec168_ctrl_msg(d, &req);
- i += 2;
- } else {
- err("I2C read not implemented");
- ret = -ENOSYS;
- i += 2;
- }
- } else {
- if (msg[i].addr == ec168_ec100_config.demod_address) {
- req.cmd = WRITE_DEMOD;
- req.value = msg[i].buf[1]; /* val */
- req.index = 0xff00 + msg[i].buf[0]; /* reg */
- req.size = 0;
- req.data = NULL;
- ret = ec168_ctrl_msg(d, &req);
- i += 1;
- } else {
- req.cmd = WRITE_I2C;
- req.value = msg[i].buf[0]; /* val */
- req.index = 0x0100 + msg[i].addr; /* I2C addr */
- req.size = msg[i].len-1;
- req.data = &msg[i].buf[1];
- ret = ec168_ctrl_msg(d, &req);
- i += 1;
- }
- }
- if (ret)
- goto error;
-
- }
- ret = i;
-
-error:
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-
-static u32 ec168_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm ec168_i2c_algo = {
- .master_xfer = ec168_i2c_xfer,
- .functionality = ec168_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-static struct ec100_config ec168_ec100_config = {
- .demod_address = 0xff, /* not real address, demod is integrated */
-};
-
-static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
-{
- deb_info("%s:\n", __func__);
- adap->fe_adap[0].fe = dvb_attach(ec100_attach, &ec168_ec100_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
- return -ENODEV;
-
- return 0;
-}
-
-static struct mxl5005s_config ec168_mxl5003s_config = {
- .i2c_address = 0xc6,
- .if_freq = IF_FREQ_4570000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_OFF,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .AgcMasterByte = 0x00,
-};
-
-static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
-{
- deb_info("%s:\n", __func__);
- return dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
- &ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
-}
-
-static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
- deb_info("%s: onoff:%d\n", __func__, onoff);
- if (onoff)
- req.index = 0x0102;
- return ec168_ctrl_msg(adap->dev, &req);
-}
-
-static int ec168_download_firmware(struct usb_device *udev,
- const struct firmware *fw)
-{
- int i, len, packets, remainder, ret;
- u16 addr = 0x0000; /* firmware start address */
- struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
- deb_info("%s:\n", __func__);
-
- #define FW_PACKET_MAX_DATA 2048
- packets = fw->size / FW_PACKET_MAX_DATA;
- remainder = fw->size % FW_PACKET_MAX_DATA;
- len = FW_PACKET_MAX_DATA;
- for (i = 0; i <= packets; i++) {
- if (i == packets) /* set size of the last packet */
- len = remainder;
-
- req.size = len;
- req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA);
- req.index = addr;
- addr += FW_PACKET_MAX_DATA;
-
- ret = ec168_rw_udev(udev, &req);
- if (ret) {
- err("firmware download failed:%d packet:%d", ret, i);
- goto error;
- }
- }
- req.size = 0;
-
- /* set "warm"? */
- req.cmd = SET_CONFIG;
- req.value = 0;
- req.index = 0x0001;
- ret = ec168_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- /* really needed - no idea what does */
- req.cmd = GPIO;
- req.value = 0;
- req.index = 0x0206;
- ret = ec168_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- /* activate tuner I2C? */
- req.cmd = WRITE_I2C;
- req.value = 0;
- req.index = 0x00c6;
- ret = ec168_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec168_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc, int *cold)
-{
- int ret;
- u8 reply;
- struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
- deb_info("%s:\n", __func__);
-
- ret = ec168_rw_udev(udev, &req);
- if (ret)
- goto error;
-
- deb_info("%s: reply:%02x\n", __func__, reply);
-
- if (reply == 0x01)
- *cold = 0;
- else
- *cold = 1;
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties ec168_properties;
-
-static int ec168_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret;
- deb_info("%s: interface:%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL,
- adapter_nr);
- if (ret)
- goto error;
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-#define E3C_EC168_1689 0
-#define E3C_EC168_FFFA 1
-#define E3C_EC168_FFFB 2
-#define E3C_EC168_1001 3
-#define E3C_EC168_1002 4
-
-static struct usb_device_id ec168_id[] = {
- [E3C_EC168_1689] =
- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)},
- [E3C_EC168_FFFA] =
- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)},
- [E3C_EC168_FFFB] =
- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)},
- [E3C_EC168_1001] =
- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)},
- [E3C_EC168_1002] =
- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)},
- {} /* terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, ec168_id);
-
-static struct dvb_usb_device_properties ec168_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = ec168_download_firmware,
- .firmware = "dvb-usb-ec168.fw",
- .no_reconnect = 1,
-
- .size_of_priv = 0,
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .streaming_ctrl = ec168_streaming_ctrl,
- .frontend_attach = ec168_ec100_frontend_attach,
- .tuner_attach = ec168_mxl5003s_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x82,
- .u = {
- .bulk = {
- .buffersize = (32*512),
- }
- }
- },
- }},
- }
- },
-
- .identify_state = ec168_identify_state,
-
- .i2c_algo = &ec168_i2c_algo,
-
- .num_device_descs = 1,
- .devices = {
- {
- .name = "E3C EC168 DVB-T USB2.0 reference design",
- .cold_ids = {
- &ec168_id[E3C_EC168_1689],
- &ec168_id[E3C_EC168_FFFA],
- &ec168_id[E3C_EC168_FFFB],
- &ec168_id[E3C_EC168_1001],
- &ec168_id[E3C_EC168_1002],
- NULL},
- .warm_ids = {NULL},
- },
- }
-};
-
-static struct usb_driver ec168_driver = {
- .name = "dvb_usb_ec168",
- .probe = ec168_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = ec168_id,
-};
-
-module_usb_driver(ec168_driver);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h
deleted file mode 100644
index e7e0b831314e..000000000000
--- a/drivers/media/dvb/dvb-usb/ec168.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * E3C EC168 DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 EC168_H
-#define EC168_H
-
-#define DVB_USB_LOG_PREFIX "ec168"
-#include "dvb-usb.h"
-
-#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args)
-#define deb_rc(args...) dprintk(dvb_usb_ec168_debug, 0x02, args)
-#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args)
-#define deb_reg(args...) dprintk(dvb_usb_ec168_debug, 0x08, args)
-#define deb_i2c(args...) dprintk(dvb_usb_ec168_debug, 0x10, args)
-#define deb_fw(args...) dprintk(dvb_usb_ec168_debug, 0x20, args)
-
-#define ec168_debug_dump(r, t, v, i, b, l, func) { \
- int loop_; \
- func("%02x %02x %02x %02x %02x %02x %02x %02x", \
- t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
- if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
- func(" >>> "); \
- else \
- func(" <<< "); \
- for (loop_ = 0; loop_ < l; loop_++) \
- func("%02x ", b[loop_]); \
- func("\n");\
-}
-
-#define EC168_USB_TIMEOUT 1000
-
-struct ec168_req {
- u8 cmd; /* [1] */
- u16 value; /* [2|3] */
- u16 index; /* [4|5] */
- u16 size; /* [6|7] */
- u8 *data;
-};
-
-enum ec168_cmd {
- DOWNLOAD_FIRMWARE = 0x00,
- CONFIG = 0x01,
- DEMOD_RW = 0x03,
- GPIO = 0x04,
- STREAMING_CTRL = 0x10,
- READ_I2C = 0x20,
- WRITE_I2C = 0x21,
- HID_DOWNLOAD = 0x30,
- GET_CONFIG,
- SET_CONFIG,
- READ_DEMOD,
- WRITE_DEMOD,
-};
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
deleted file mode 100644
index c1f5582e1cdf..000000000000
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/* DVB USB compliant linux driver for GL861 USB2.0 devices.
- *
- * This program is free software; 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 "gl861.h"
-
-#include "zl10353.h"
-#include "qt1010.h"
-
-/* debug */
-static int dvb_usb_gl861_debug;
-module_param_named(debug, dvb_usb_gl861_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))."
- DVB_USB_DEBUG_STATUS);
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
-{
- u16 index;
- u16 value = addr << (8 + 1);
- int wo = (rbuf == NULL || rlen == 0); /* write-only */
- u8 req, type;
-
- if (wo) {
- req = GL861_REQ_I2C_WRITE;
- type = GL861_WRITE;
- } else { /* rw */
- req = GL861_REQ_I2C_READ;
- type = GL861_READ;
- }
-
- switch (wlen) {
- case 1:
- index = wbuf[0];
- break;
- case 2:
- index = wbuf[0];
- value = value + wbuf[1];
- break;
- default:
- warn("wlen = %x, aborting.", wlen);
- return -EINVAL;
- }
-
- msleep(1); /* avoid I2C errors */
-
- return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
- value, index, rbuf, rlen, 2000);
-}
-
-/* I2C */
-static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int i;
-
- if (num > 2)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- for (i = 0; i < num; i++) {
- /* write/read request */
- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
- if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
- msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
- break;
- i++;
- } else
- if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
- msg[i].len, NULL, 0) < 0)
- break;
- }
-
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-static u32 gl861_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm gl861_i2c_algo = {
- .master_xfer = gl861_i2c_xfer,
- .functionality = gl861_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-static struct zl10353_config gl861_zl10353_config = {
- .demod_address = 0x0f,
- .no_tuner = 1,
- .parallel_ts = 1,
-};
-
-static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
-{
-
- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL)
- return -EIO;
-
- return 0;
-}
-
-static struct qt1010_config gl861_qt1010_config = {
- .i2c_address = 0x62
-};
-
-static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
-{
- return dvb_attach(qt1010_attach,
- adap->fe_adap[0].fe, &adap->dev->i2c_adap,
- &gl861_qt1010_config) == NULL ? -ENODEV : 0;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties gl861_properties;
-
-static int gl861_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct dvb_usb_device *d;
- struct usb_host_interface *alt;
- int ret;
-
- if (intf->num_altsetting < 2)
- return -ENODEV;
-
- ret = dvb_usb_device_init(intf, &gl861_properties, THIS_MODULE, &d,
- adapter_nr);
- if (ret == 0) {
- alt = usb_altnum_to_altsetting(intf, 0);
-
- if (alt == NULL) {
- deb_rc("not alt found!\n");
- return -ENODEV;
- }
-
- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
- alt->desc.bAlternateSetting);
- }
-
- return ret;
-}
-
-static struct usb_device_id gl861_table [] = {
- { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) },
- { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, gl861_table);
-
-static struct dvb_usb_device_properties gl861_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
- .usb_ctrl = DEVICE_SPECIFIC,
-
- .size_of_priv = 0,
-
- .num_adapters = 1,
- .adapter = {{
- .num_frontends = 1,
- .fe = {{
-
- .frontend_attach = gl861_frontend_attach,
- .tuner_attach = gl861_tuner_attach,
-
- .stream = {
- .type = USB_BULK,
- .count = 7,
- .endpoint = 0x81,
- .u = {
- .bulk = {
- .buffersize = 512,
- }
- }
- },
- }},
- } },
- .i2c_algo = &gl861_i2c_algo,
-
- .num_device_descs = 2,
- .devices = {
- {
- .name = "MSI Mega Sky 55801 DVB-T USB2.0",
- .cold_ids = { NULL },
- .warm_ids = { &gl861_table[0], NULL },
- },
- {
- .name = "A-LINK DTU DVB-T USB2.0",
- .cold_ids = { NULL },
- .warm_ids = { &gl861_table[1], NULL },
- },
- }
-};
-
-static struct usb_driver gl861_driver = {
- .name = "dvb_usb_gl861",
- .probe = gl861_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = gl861_table,
-};
-
-module_usb_driver(gl861_driver);
-
-MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
-MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
-MODULE_VERSION("0.1");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/gl861.h b/drivers/media/dvb/dvb-usb/gl861.h
deleted file mode 100644
index c54855e2c233..000000000000
--- a/drivers/media/dvb/dvb-usb/gl861.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef _DVB_USB_GL861_H_
-#define _DVB_USB_GL861_H_
-
-#define DVB_USB_LOG_PREFIX "gl861"
-#include "dvb-usb.h"
-
-#define deb_rc(args...) dprintk(dvb_usb_gl861_debug, 0x01, args)
-
-#define GL861_WRITE 0x40
-#define GL861_READ 0xc0
-
-#define GL861_REQ_I2C_WRITE 0x01
-#define GL861_REQ_I2C_READ 0x02
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
deleted file mode 100644
index 6244fe9d1a3a..000000000000
--- a/drivers/media/dvb/dvb-usb/it913x.c
+++ /dev/null
@@ -1,931 +0,0 @@
-/* DVB USB compliant linux driver for IT9137
- *
- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- * IT9137 (C) ITE Tech Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License Version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- * see Documentation/dvb/README.dvb-usb for more information
- * see Documentation/dvb/it9137.txt for firmware information
- *
- */
-#define DVB_USB_LOG_PREFIX "it913x"
-
-#include <linux/usb.h>
-#include <linux/usb/input.h>
-#include <media/rc-core.h>
-
-#include "dvb-usb.h"
-#include "it913x-fe.h"
-
-/* debug */
-static int dvb_usb_it913x_debug;
-#define l_dprintk(var, level, args...) do { \
- if ((var >= level)) \
- printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \
-} while (0)
-
-#define deb_info(level, args...) l_dprintk(dvb_usb_it913x_debug, level, args)
-#define debug_data_snipet(level, name, p) \
- deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
- *p, *(p+1), *(p+2), *(p+3), *(p+4), \
- *(p+5), *(p+6), *(p+7));
-
-
-module_param_named(debug, dvb_usb_it913x_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."
- DVB_USB_DEBUG_STATUS);
-
-static int pid_filter;
-module_param_named(pid, pid_filter, int, 0644);
-MODULE_PARM_DESC(pid, "set default 0=on 1=off");
-
-static int dvb_usb_it913x_firmware;
-module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644);
-MODULE_PARM_DESC(firmware, "set firmware 0=auto 1=IT9137 2=IT9135V1");
-
-
-int cmd_counter;
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-struct it913x_state {
- u8 id;
- struct ite_config it913x_config;
- u8 pid_filter_onoff;
-};
-
-struct ite_config it913x_config;
-
-#define IT913X_RETRY 10
-#define IT913X_SND_TIMEOUT 100
-#define IT913X_RCV_TIMEOUT 200
-
-static int it913x_bulk_write(struct usb_device *dev,
- u8 *snd, int len, u8 pipe)
-{
- int ret, actual_l, i;
-
- for (i = 0; i < IT913X_RETRY; i++) {
- ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
- snd, len , &actual_l, IT913X_SND_TIMEOUT);
- if (ret != -EBUSY && ret != -ETIMEDOUT)
- break;
- }
-
- if (len != actual_l && ret == 0)
- ret = -EAGAIN;
-
- return ret;
-}
-
-static int it913x_bulk_read(struct usb_device *dev,
- u8 *rev, int len, u8 pipe)
-{
- int ret, actual_l, i;
-
- for (i = 0; i < IT913X_RETRY; i++) {
- ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
- rev, len , &actual_l, IT913X_RCV_TIMEOUT);
- if (ret != -EBUSY && ret != -ETIMEDOUT)
- break;
- }
-
- if (len != actual_l && ret == 0)
- ret = -EAGAIN;
-
- return ret;
-}
-
-static u16 check_sum(u8 *p, u8 len)
-{
- u16 sum = 0;
- u8 i = 1;
- while (i < len)
- sum += (i++ & 1) ? (*p++) << 8 : *p++;
- return ~sum;
-}
-
-static int it913x_usb_talk(struct usb_device *udev, u8 mode, u8 pro,
- u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
-{
- int ret = 0, i, buf_size = 1;
- u8 *buff;
- u8 rlen;
- u16 chk_sum;
-
- buff = kzalloc(256, GFP_KERNEL);
- if (!buff) {
- info("USB Buffer Failed");
- return -ENOMEM;
- }
-
- buff[buf_size++] = pro;
- buff[buf_size++] = cmd;
- buff[buf_size++] = cmd_counter;
-
- switch (mode) {
- case READ_LONG:
- case WRITE_LONG:
- buff[buf_size++] = len;
- buff[buf_size++] = 2;
- buff[buf_size++] = (reg >> 24);
- buff[buf_size++] = (reg >> 16) & 0xff;
- buff[buf_size++] = (reg >> 8) & 0xff;
- buff[buf_size++] = reg & 0xff;
- break;
- case READ_SHORT:
- buff[buf_size++] = addr;
- break;
- case WRITE_SHORT:
- buff[buf_size++] = len;
- buff[buf_size++] = addr;
- buff[buf_size++] = (reg >> 8) & 0xff;
- buff[buf_size++] = reg & 0xff;
- break;
- case READ_DATA:
- case WRITE_DATA:
- break;
- case WRITE_CMD:
- mode = 7;
- break;
- default:
- kfree(buff);
- return -EINVAL;
- }
-
- if (mode & 1) {
- for (i = 0; i < len ; i++)
- buff[buf_size++] = data[i];
- }
- chk_sum = check_sum(&buff[1], buf_size);
-
- buff[buf_size++] = chk_sum >> 8;
- buff[0] = buf_size;
- buff[buf_size++] = (chk_sum & 0xff);
-
- ret = it913x_bulk_write(udev, buff, buf_size , 0x02);
- if (ret < 0)
- goto error;
-
- ret = it913x_bulk_read(udev, buff, (mode & 1) ?
- 5 : len + 5 , 0x01);
- if (ret < 0)
- goto error;
-
- rlen = (mode & 0x1) ? 0x1 : len;
-
- if (mode & 1)
- ret = buff[2];
- else
- memcpy(data, &buff[3], rlen);
-
- cmd_counter++;
-
-error: kfree(buff);
-
- return ret;
-}
-
-static int it913x_io(struct usb_device *udev, u8 mode, u8 pro,
- u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
-{
- int ret, i;
-
- for (i = 0; i < IT913X_RETRY; i++) {
- ret = it913x_usb_talk(udev, mode, pro,
- cmd, reg, addr, data, len);
- if (ret != -EAGAIN)
- break;
- }
-
- return ret;
-}
-
-static int it913x_wr_reg(struct usb_device *udev, u8 pro, u32 reg , u8 data)
-{
- int ret;
- u8 b[1];
- b[0] = data;
- ret = it913x_io(udev, WRITE_LONG, pro,
- CMD_DEMOD_WRITE, reg, 0, b, sizeof(b));
-
- return ret;
-}
-
-static int it913x_read_reg(struct usb_device *udev, u32 reg)
-{
- int ret;
- u8 data[1];
-
- ret = it913x_io(udev, READ_LONG, DEV_0,
- CMD_DEMOD_READ, reg, 0, &data[0], 1);
-
- return (ret < 0) ? ret : data[0];
-}
-
-static u32 it913x_query(struct usb_device *udev, u8 pro)
-{
- int ret, i;
- u8 data[4];
- u8 ver;
-
- for (i = 0; i < 5; i++) {
- ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ,
- 0x1222, 0, &data[0], 3);
- ver = data[0];
- if (ver > 0 && ver < 3)
- break;
- msleep(100);
- }
-
- if (ver < 1 || ver > 2) {
- info("Failed to identify chip version applying 1");
- it913x_config.chip_ver = 0x1;
- it913x_config.chip_type = 0x9135;
- return 0;
- }
-
- it913x_config.chip_ver = ver;
- it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
-
- info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver,
- it913x_config.chip_type);
-
- ret |= it913x_io(udev, READ_SHORT, pro,
- CMD_QUERYINFO, 0, 0x1, &data[0], 4);
-
- it913x_config.firmware = (data[0] << 24) + (data[1] << 16) +
- (data[2] << 8) + data[3];
-
- return (ret < 0) ? 0 : it913x_config.firmware;
-}
-
-static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct it913x_state *st = adap->dev->priv;
- struct usb_device *udev = adap->dev->udev;
- int ret;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- mutex_lock(&adap->dev->i2c_mutex);
-
- deb_info(1, "PID_C (%02x)", onoff);
-
- ret = it913x_wr_reg(udev, pro, PID_EN, st->pid_filter_onoff);
-
- mutex_unlock(&adap->dev->i2c_mutex);
- return ret;
-}
-
-static int it913x_pid_filter(struct dvb_usb_adapter *adap,
- int index, u16 pid, int onoff)
-{
- struct it913x_state *st = adap->dev->priv;
- struct usb_device *udev = adap->dev->udev;
- int ret;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- mutex_lock(&adap->dev->i2c_mutex);
-
- deb_info(1, "PID_F (%02x)", onoff);
-
- ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
-
- ret |= it913x_wr_reg(udev, pro, PID_MSB, (u8)(pid >> 8));
-
- ret |= it913x_wr_reg(udev, pro, PID_INX_EN, (u8)onoff);
-
- ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
-
- if (udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
- ret |= it913x_wr_reg(udev, pro, PID_EN, !onoff);
- st->pid_filter_onoff = !onoff;
- } else
- st->pid_filter_onoff =
- adap->fe_adap[adap->active_fe].pid_filtering;
-
- mutex_unlock(&adap->dev->i2c_mutex);
- return 0;
-}
-
-
-static int it913x_return_status(struct usb_device *udev)
-{
- u32 firm = 0;
-
- firm = it913x_query(udev, DEV_0);
- if (firm > 0)
- info("Firmware Version %d", firm);
-
- return (firm > 0) ? firm : 0;
-}
-
-static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- static u8 data[256];
- int ret;
- u32 reg;
- u8 pro;
-
- mutex_lock(&d->i2c_mutex);
-
- debug_data_snipet(1, "Message out", msg[0].buf);
- deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
-
- pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0;
- pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0;
- memcpy(data, msg[0].buf, msg[0].len);
- reg = (data[0] << 24) + (data[1] << 16) +
- (data[2] << 8) + data[3];
- if (num == 2) {
- ret = it913x_io(d->udev, READ_LONG, pro,
- CMD_DEMOD_READ, reg, 0, data, msg[1].len);
- memcpy(msg[1].buf, data, msg[1].len);
- } else
- ret = it913x_io(d->udev, WRITE_LONG, pro, CMD_DEMOD_WRITE,
- reg, 0, &data[4], msg[0].len - 4);
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-static u32 it913x_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm it913x_i2c_algo = {
- .master_xfer = it913x_i2c_xfer,
- .functionality = it913x_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-#define IT913X_POLL 250
-static int it913x_rc_query(struct dvb_usb_device *d)
-{
- u8 ibuf[4];
- int ret;
- u32 key;
- /* Avoid conflict with frontends*/
- mutex_lock(&d->i2c_mutex);
-
- ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
- 0, 0, &ibuf[0], sizeof(ibuf));
-
- if ((ibuf[2] + ibuf[3]) == 0xff) {
- key = ibuf[2];
- key += ibuf[0] << 16;
- key += ibuf[1] << 8;
- deb_info(1, "NEC Extended Key =%08x", key);
- if (d->rc_dev != NULL)
- rc_keydown(d->rc_dev, key, 0);
- }
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-/* Firmware sets raw */
-const char fw_it9135_v1[] = "dvb-usb-it9135-01.fw";
-const char fw_it9135_v2[] = "dvb-usb-it9135-02.fw";
-const char fw_it9137[] = "dvb-usb-it9137-01.fw";
-
-static int ite_firmware_select(struct usb_device *udev,
- struct dvb_usb_device_properties *props)
-{
- int sw;
- /* auto switch */
- if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_KWORLD_2)
- sw = IT9137_FW;
- else if (it913x_config.chip_ver == 1)
- sw = IT9135_V1_FW;
- else
- sw = IT9135_V2_FW;
-
- /* force switch */
- if (dvb_usb_it913x_firmware != IT9135_AUTO)
- sw = dvb_usb_it913x_firmware;
-
- switch (sw) {
- case IT9135_V1_FW:
- it913x_config.firmware_ver = 1;
- it913x_config.adc_x2 = 1;
- it913x_config.read_slevel = false;
- props->firmware = fw_it9135_v1;
- break;
- case IT9135_V2_FW:
- it913x_config.firmware_ver = 1;
- it913x_config.adc_x2 = 1;
- it913x_config.read_slevel = false;
- props->firmware = fw_it9135_v2;
- switch (it913x_config.tuner_id_0) {
- case IT9135_61:
- case IT9135_62:
- break;
- default:
- info("Unknown tuner ID applying default 0x60");
- case IT9135_60:
- it913x_config.tuner_id_0 = IT9135_60;
- }
- break;
- case IT9137_FW:
- default:
- it913x_config.firmware_ver = 0;
- it913x_config.adc_x2 = 0;
- it913x_config.read_slevel = true;
- props->firmware = fw_it9137;
- }
-
- return 0;
-}
-
-static void it913x_select_remote(struct usb_device *udev,
- struct dvb_usb_device_properties *props)
-{
- switch (le16_to_cpu(udev->descriptor.idProduct)) {
- case USB_PID_ITETECH_IT9135_9005:
- props->rc.core.rc_codes = RC_MAP_IT913X_V2;
- return;
- default:
- props->rc.core.rc_codes = RC_MAP_IT913X_V1;
- }
- return;
-}
-
-#define TS_MPEG_PKT_SIZE 188
-#define EP_LOW 21
-#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
-#define EP_HIGH 348
-#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
-
-static int it913x_select_config(struct usb_device *udev,
- struct dvb_usb_device_properties *props)
-{
- int ret = 0, reg;
- bool proprietary_ir = false;
-
- if (it913x_config.chip_ver == 0x02
- && it913x_config.chip_type == 0x9135)
- reg = it913x_read_reg(udev, 0x461d);
- else
- reg = it913x_read_reg(udev, 0x461b);
-
- if (reg < 0)
- return reg;
-
- if (reg == 0) {
- it913x_config.dual_mode = 0;
- it913x_config.tuner_id_0 = IT9135_38;
- proprietary_ir = true;
- } else {
- /* TS mode */
- reg = it913x_read_reg(udev, 0x49c5);
- if (reg < 0)
- return reg;
- it913x_config.dual_mode = reg;
-
- /* IR mode type */
- reg = it913x_read_reg(udev, 0x49ac);
- if (reg < 0)
- return reg;
- if (reg == 5) {
- info("Remote propriety (raw) mode");
- proprietary_ir = true;
- } else if (reg == 1) {
- info("Remote HID mode NOT SUPPORTED");
- proprietary_ir = false;
- props->rc.core.rc_codes = NULL;
- } else
- props->rc.core.rc_codes = NULL;
-
- /* Tuner_id */
- reg = it913x_read_reg(udev, 0x49d0);
- if (reg < 0)
- return reg;
- it913x_config.tuner_id_0 = reg;
- }
-
- if (proprietary_ir)
- it913x_select_remote(udev, props);
-
- if (udev->speed != USB_SPEED_HIGH) {
- props->adapter[0].fe[0].pid_filter_count = 5;
- info("USB 1 low speed mode - connect to USB 2 port");
- if (pid_filter > 0)
- pid_filter = 0;
- if (it913x_config.dual_mode) {
- it913x_config.dual_mode = 0;
- info("Dual mode not supported in USB 1");
- }
- } else /* For replugging */
- if(props->adapter[0].fe[0].pid_filter_count == 5)
- props->adapter[0].fe[0].pid_filter_count = 31;
-
- /* Select Stream Buffer Size and pid filter option*/
- if (pid_filter) {
- props->adapter[0].fe[0].stream.u.bulk.buffersize =
- TS_BUFFER_SIZE_MAX;
- props->adapter[0].fe[0].caps &=
- ~DVB_USB_ADAP_NEED_PID_FILTERING;
- } else
- props->adapter[0].fe[0].stream.u.bulk.buffersize =
- TS_BUFFER_SIZE_PID;
-
- if (it913x_config.dual_mode) {
- props->adapter[1].fe[0].stream.u.bulk.buffersize =
- props->adapter[0].fe[0].stream.u.bulk.buffersize;
- props->num_adapters = 2;
- if (pid_filter)
- props->adapter[1].fe[0].caps =
- props->adapter[0].fe[0].caps;
- } else
- props->num_adapters = 1;
-
- info("Dual mode=%x Tuner Type=%x", it913x_config.dual_mode,
- it913x_config.tuner_id_0);
-
- ret = ite_firmware_select(udev, props);
-
- return ret;
-}
-
-static int it913x_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- int ret = 0, firm_no;
- u8 reg;
-
- firm_no = it913x_return_status(udev);
-
- /* Read and select config */
- ret = it913x_select_config(udev, props);
- if (ret < 0)
- return ret;
-
- if (firm_no > 0) {
- *cold = 0;
- return 0;
- }
-
- if (it913x_config.dual_mode) {
- it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0);
- ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1);
- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1);
- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
- msleep(50);
- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0);
- msleep(50);
- reg = it913x_read_reg(udev, GPIOH1_O);
- if (reg == 0) {
- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1);
- ret |= it913x_return_status(udev);
- if (ret != 0)
- ret = it913x_wr_reg(udev, DEV_0,
- GPIOH1_O, 0x0);
- }
- }
-
- reg = it913x_read_reg(udev, IO_MUX_POWER_CLK);
-
- if (it913x_config.dual_mode) {
- ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
- if (it913x_config.firmware_ver == 1)
- ret |= it913x_wr_reg(udev, DEV_0, 0xcfff, 0x1);
- else
- ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1);
- } else {
- ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0);
- if (it913x_config.firmware_ver == 1)
- ret |= it913x_wr_reg(udev, DEV_0, 0xcfff, 0x0);
- else
- ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0);
- }
-
- *cold = 1;
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct it913x_state *st = adap->dev->priv;
- int ret = 0;
- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
-
- deb_info(1, "STM (%02x)", onoff);
-
- if (!onoff) {
- mutex_lock(&adap->dev->i2c_mutex);
-
- ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
-
- mutex_unlock(&adap->dev->i2c_mutex);
- st->pid_filter_onoff =
- adap->fe_adap[adap->active_fe].pid_filtering;
-
- }
-
- return ret;
-}
-
-static int it913x_download_firmware(struct usb_device *udev,
- const struct firmware *fw)
-{
- int ret = 0, i = 0, pos = 0;
- u8 packet_size, min_pkt;
- u8 *fw_data;
-
- ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100);
-
- info("FRM Starting Firmware Download");
-
- /* Multi firmware loader */
- /* This uses scatter write firmware headers */
- /* The firmware must start with 03 XX 00 */
- /* and be the extact firmware length */
-
- if (it913x_config.chip_ver == 2)
- min_pkt = 0x11;
- else
- min_pkt = 0x19;
-
- while (i <= fw->size) {
- if (((fw->data[i] == 0x3) && (fw->data[i + 2] == 0x0))
- || (i == fw->size)) {
- packet_size = i - pos;
- if ((packet_size > min_pkt) || (i == fw->size)) {
- fw_data = (u8 *)(fw->data + pos);
- pos += packet_size;
- if (packet_size > 0) {
- ret = it913x_io(udev, WRITE_DATA,
- DEV_0, CMD_SCATTER_WRITE, 0,
- 0, fw_data, packet_size);
- if (ret < 0)
- break;
- }
- udelay(1000);
- }
- }
- i++;
- }
-
- if (ret < 0)
- info("FRM Firmware Download Failed (%d)" , ret);
- else
- info("FRM Firmware Download Completed - Resetting Device");
-
- msleep(30);
-
- ret = it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0);
- if (ret < 0)
- info("FRM Device not responding to reboot");
-
- ret = it913x_return_status(udev);
- if (ret == 0) {
- info("FRM Failed to reboot device");
- return -ENODEV;
- }
-
- msleep(30);
-
- ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400);
-
- msleep(30);
-
- /* Tuner function */
- if (it913x_config.dual_mode)
- ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0);
- else
- ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0x68);
-
- if ((it913x_config.chip_ver == 1) &&
- (it913x_config.chip_type == 0x9135)) {
- ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0);
- ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0);
- if (it913x_config.dual_mode) {
- ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0);
- ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0);
- }
- }
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_name(struct dvb_usb_adapter *adap)
-{
- const char *desc = adap->dev->desc->name;
- char *fe_name[] = {"_1", "_2", "_3", "_4"};
- char *name = adap->fe_adap[0].fe->ops.info.name;
-
- strlcpy(name, desc, 128);
- strlcat(name, fe_name[adap->id], 128);
-
- return 0;
-}
-
-static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct usb_device *udev = adap->dev->udev;
- struct it913x_state *st = adap->dev->priv;
- int ret = 0;
- u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
- u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize / 4;
- u8 pkt_size = 0x80;
-
- if (adap->dev->udev->speed != USB_SPEED_HIGH)
- pkt_size = 0x10;
-
- it913x_config.adf = it913x_read_reg(udev, IO_MUX_POWER_CLK);
-
- if (adap->id == 0)
- memcpy(&st->it913x_config, &it913x_config,
- sizeof(struct ite_config));
-
- adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach,
- &adap->dev->i2c_adap, adap_addr, &st->it913x_config);
-
- if (adap->id == 0 && adap->fe_adap[0].fe) {
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x0f);
- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_NAK, 0x1b);
- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x2f);
- ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_LSB,
- ep_size & 0xff);
- ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
- ret = it913x_wr_reg(udev, DEV_0, EP4_MAX_PKT, pkt_size);
- } else if (adap->id == 1 && adap->fe_adap[0].fe) {
- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x6f);
- ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_LSB,
- ep_size & 0xff);
- ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
- ret = it913x_wr_reg(udev, DEV_0, EP5_MAX_PKT, pkt_size);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_EN, 0x1);
- ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_SERIAL, 0x1);
- ret = it913x_wr_reg(udev, DEV_1, TOP_HOSTB_SER_MODE, 0x1);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, TSIS_ENABLE, 0x1);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x0);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x0);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0);
- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF_STOP_EN, 0x1);
- ret = it913x_wr_reg(udev, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0);
- ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_STOP_EN, 0x0);
- } else
- return -ENODEV;
-
- ret = it913x_name(adap);
-
- return ret;
-}
-
-/* DVB USB Driver */
-static struct dvb_usb_device_properties it913x_properties;
-
-static int it913x_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- cmd_counter = 0;
- if (0 == dvb_usb_device_init(intf, &it913x_properties,
- THIS_MODULE, NULL, adapter_nr)) {
- info("DEV registering device driver");
- return 0;
- }
-
- info("DEV it913x Error");
- return -ENODEV;
-
-}
-
-static struct usb_device_id it913x_table[] = {
- { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) },
- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) },
- { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137) },
- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005) },
- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006) },
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, it913x_table);
-
-static struct dvb_usb_device_properties it913x_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
- .usb_ctrl = DEVICE_SPECIFIC,
- .download_firmware = it913x_download_firmware,
- .firmware = "dvb-usb-it9137-01.fw",
- .no_reconnect = 1,
- .size_of_priv = sizeof(struct it913x_state),
- .num_adapters = 2,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_NEED_PID_FILTERING|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .streaming_ctrl = it913x_streaming_ctrl,
- .pid_filter_count = 31,
- .pid_filter = it913x_pid_filter,
- .pid_filter_ctrl = it913x_pid_filter_ctrl,
- .frontend_attach = it913x_frontend_attach,
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 10,
- .endpoint = 0x04,
- .u = {/* Keep Low if PID filter on */
- .bulk = {
- .buffersize =
- TS_BUFFER_SIZE_PID,
- }
- }
- }
- }},
- },
- {
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_NEED_PID_FILTERING|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .streaming_ctrl = it913x_streaming_ctrl,
- .pid_filter_count = 31,
- .pid_filter = it913x_pid_filter,
- .pid_filter_ctrl = it913x_pid_filter_ctrl,
- .frontend_attach = it913x_frontend_attach,
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 5,
- .endpoint = 0x05,
- .u = {
- .bulk = {
- .buffersize =
- TS_BUFFER_SIZE_PID,
- }
- }
- }
- }},
- }
- },
- .identify_state = it913x_identify_state,
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "it913x",
- .rc_query = it913x_rc_query,
- .rc_interval = IT913X_POLL,
- .allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_IT913X_V1,
- },
- .i2c_algo = &it913x_i2c_algo,
- .num_device_descs = 5,
- .devices = {
- { "Kworld UB499-2T T09(IT9137)",
- { &it913x_table[0], NULL },
- },
- { "ITE 9135 Generic",
- { &it913x_table[1], NULL },
- },
- { "Sveon STV22 Dual DVB-T HDTV(IT9137)",
- { &it913x_table[2], NULL },
- },
- { "ITE 9135(9005) Generic",
- { &it913x_table[3], NULL },
- },
- { "ITE 9135(9006) Generic",
- { &it913x_table[4], NULL },
- },
- }
-};
-
-static struct usb_driver it913x_driver = {
- .name = "it913x",
- .probe = it913x_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = it913x_table,
-};
-
-module_usb_driver(it913x_driver);
-
-MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
-MODULE_DESCRIPTION("it913x USB 2 Driver");
-MODULE_VERSION("1.28");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
deleted file mode 100644
index 25d1031460f8..000000000000
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ /dev/null
@@ -1,1407 +0,0 @@
-/* DVB USB compliant linux driver for
- *
- * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395
- * LME2510C + LG TDQY-P001F
- * LME2510C + BS2F7HZ0194
- * LME2510 + LG TDQY-P001F
- * LME2510 + BS2F7HZ0194
- *
- * MVB7395 (LME2510C+SHARP:BS2F7HZ7395)
- * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V)
- *
- * MV001F (LME2510+LGTDQY-P001F)
- * LG TDQY - P001F =(TDA8263 + TDA10086H)
- *
- * MVB0001F (LME2510C+LGTDQT-P001F)
- *
- * MV0194 (LME2510+SHARP:BS2F7HZ0194)
- * SHARP:BS2F7HZ0194 = (STV0299+IX2410)
- *
- * MVB0194 (LME2510C+SHARP0194)
- *
- * For firmware see Documentation/dvb/lmedm04.txt
- *
- * I2C addresses:
- * 0xd0 - STV0288 - Demodulator
- * 0xc0 - Sharp IX2505V - Tuner
- * --
- * 0x1c - TDA10086 - Demodulator
- * 0xc0 - TDA8263 - Tuner
- * --
- * 0xd0 - STV0299 - Demodulator
- * 0xc0 - IX2410 - Tuner
- *
- *
- * VID = 3344 PID LME2510=1122 LME2510C=1120
- *
- * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com)
- * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- * see Documentation/dvb/README.dvb-usb for more information
- *
- * Known Issues :
- * LME2510: Non Intel USB chipsets fail to maintain High Speed on
- * Boot or Hot Plug.
- *
- * QQbox suffers from noise on LNB voltage.
- *
- * LME2510: SHARP:BS2F7HZ0194(MV0194) cannot cold reset and share system
- * with other tuners. After a cold reset streaming will not start.
- *
- */
-#define DVB_USB_LOG_PREFIX "LME2510(C)"
-#include <linux/usb.h>
-#include <linux/usb/input.h>
-#include <media/rc-core.h>
-
-#include "dvb-usb.h"
-#include "lmedm04.h"
-#include "tda826x.h"
-#include "tda10086.h"
-#include "stv0288.h"
-#include "ix2505v.h"
-#include "stv0299.h"
-#include "dvb-pll.h"
-#include "z0194a.h"
-#include "m88rs2000.h"
-
-
-
-/* debug */
-static int dvb_usb_lme2510_debug;
-#define l_dprintk(var, level, args...) do { \
- if ((var >= level)) \
- printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \
-} while (0)
-
-#define deb_info(level, args...) l_dprintk(dvb_usb_lme2510_debug, level, args)
-#define debug_data_snipet(level, name, p) \
- deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
- *p, *(p+1), *(p+2), *(p+3), *(p+4), \
- *(p+5), *(p+6), *(p+7));
-
-
-module_param_named(debug, dvb_usb_lme2510_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."
- DVB_USB_DEBUG_STATUS);
-
-static int dvb_usb_lme2510_firmware;
-module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644);
-MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG");
-
-static int pid_filter;
-module_param_named(pid, pid_filter, int, 0644);
-MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on");
-
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define TUNER_DEFAULT 0x0
-#define TUNER_LG 0x1
-#define TUNER_S7395 0x2
-#define TUNER_S0194 0x3
-#define TUNER_RS2000 0x4
-
-struct lme2510_state {
- u8 id;
- u8 tuner_config;
- u8 signal_lock;
- u8 signal_level;
- u8 signal_sn;
- u8 time_key;
- u8 last_key;
- u8 key_timeout;
- u8 i2c_talk_onoff;
- u8 i2c_gate;
- u8 i2c_tuner_gate_w;
- u8 i2c_tuner_gate_r;
- u8 i2c_tuner_addr;
- u8 stream_on;
- u8 pid_size;
- u8 pid_off;
- void *buffer;
- struct urb *lme_urb;
- void *usb_buffer;
-
-};
-
-static int lme2510_bulk_write(struct usb_device *dev,
- u8 *snd, int len, u8 pipe)
-{
- int ret, actual_l;
-
- ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
- snd, len , &actual_l, 100);
- return ret;
-}
-
-static int lme2510_bulk_read(struct usb_device *dev,
- u8 *rev, int len, u8 pipe)
-{
- int ret, actual_l;
-
- ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
- rev, len , &actual_l, 200);
- return ret;
-}
-
-static int lme2510_usb_talk(struct dvb_usb_device *d,
- u8 *wbuf, int wlen, u8 *rbuf, int rlen)
-{
- struct lme2510_state *st = d->priv;
- u8 *buff;
- int ret = 0;
-
- if (st->usb_buffer == NULL) {
- st->usb_buffer = kmalloc(64, GFP_KERNEL);
- if (st->usb_buffer == NULL) {
- info("MEM Error no memory");
- return -ENOMEM;
- }
- }
- buff = st->usb_buffer;
-
- ret = mutex_lock_interruptible(&d->usb_mutex);
-
- if (ret < 0)
- return -EAGAIN;
-
- /* the read/write capped at 64 */
- memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
-
- ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
-
- ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
- rlen : 64 , 0x01);
-
- if (rlen > 0)
- memcpy(rbuf, buff, rlen);
-
- mutex_unlock(&d->usb_mutex);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int lme2510_stream_restart(struct dvb_usb_device *d)
-{
- struct lme2510_state *st = d->priv;
- u8 all_pids[] = LME_ALL_PIDS;
- u8 stream_on[] = LME_ST_ON_W;
- int ret;
- u8 rbuff[1];
- if (st->pid_off)
- ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
- rbuff, sizeof(rbuff));
- /*Restart Stream Command*/
- ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
- rbuff, sizeof(rbuff));
- return ret;
-}
-
-static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out)
-{
- struct lme2510_state *st = d->priv;
- static u8 pid_buff[] = LME_ZERO_PID;
- static u8 rbuf[1];
- u8 pid_no = index * 2;
- u8 pid_len = pid_no + 2;
- int ret = 0;
- deb_info(1, "PID Setting Pid %04x", pid_out);
-
- if (st->pid_size == 0)
- ret |= lme2510_stream_restart(d);
-
- pid_buff[2] = pid_no;
- pid_buff[3] = (u8)pid_out & 0xff;
- pid_buff[4] = pid_no + 1;
- pid_buff[5] = (u8)(pid_out >> 8);
-
- if (pid_len > st->pid_size)
- st->pid_size = pid_len;
- pid_buff[7] = 0x80 + st->pid_size;
-
- ret |= lme2510_usb_talk(d, pid_buff ,
- sizeof(pid_buff) , rbuf, sizeof(rbuf));
-
- if (st->stream_on)
- ret |= lme2510_stream_restart(d);
-
- return ret;
-}
-
-static void lme2510_int_response(struct urb *lme_urb)
-{
- struct dvb_usb_adapter *adap = lme_urb->context;
- struct lme2510_state *st = adap->dev->priv;
- static u8 *ibuf, *rbuf;
- int i = 0, offset;
- u32 key;
-
- switch (lme_urb->status) {
- case 0:
- case -ETIMEDOUT:
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- default:
- info("Error %x", lme_urb->status);
- break;
- }
-
- rbuf = (u8 *) lme_urb->transfer_buffer;
-
- offset = ((lme_urb->actual_length/8) > 4)
- ? 4 : (lme_urb->actual_length/8) ;
-
- for (i = 0; i < offset; ++i) {
- ibuf = (u8 *)&rbuf[i*8];
- deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x",
- offset, i, ibuf[0], ibuf[1]);
-
- switch (ibuf[0]) {
- case 0xaa:
- debug_data_snipet(1, "INT Remote data snipet", ibuf);
- if ((ibuf[4] + ibuf[5]) == 0xff) {
- key = ibuf[5];
- key += (ibuf[3] > 0)
- ? (ibuf[3] ^ 0xff) << 8 : 0;
- key += (ibuf[2] ^ 0xff) << 16;
- deb_info(1, "INT Key =%08x", key);
- if (adap->dev->rc_dev != NULL)
- rc_keydown(adap->dev->rc_dev, key, 0);
- }
- break;
- case 0xbb:
- switch (st->tuner_config) {
- case TUNER_LG:
- if (ibuf[2] > 0)
- st->signal_lock = ibuf[2];
- st->signal_level = ibuf[4];
- st->signal_sn = ibuf[3];
- st->time_key = ibuf[7];
- break;
- case TUNER_S7395:
- case TUNER_S0194:
- /* Tweak for earlier firmware*/
- if (ibuf[1] == 0x03) {
- if (ibuf[2] > 1)
- st->signal_lock = ibuf[2];
- st->signal_level = ibuf[3];
- st->signal_sn = ibuf[4];
- } else {
- st->signal_level = ibuf[4];
- st->signal_sn = ibuf[5];
- st->signal_lock =
- (st->signal_lock & 0xf7) +
- ((ibuf[2] & 0x01) << 0x03);
- }
- break;
- case TUNER_RS2000:
- if (ibuf[2] > 0)
- st->signal_lock = 0xff;
- else
- st->signal_lock = 0xf0;
- st->signal_level = ibuf[4];
- st->signal_sn = ibuf[5];
- st->time_key = ibuf[7];
- default:
- break;
- }
- debug_data_snipet(5, "INT Remote data snipet in", ibuf);
- break;
- case 0xcc:
- debug_data_snipet(1, "INT Control data snipet", ibuf);
- break;
- default:
- debug_data_snipet(1, "INT Unknown data snipet", ibuf);
- break;
- }
- }
- usb_submit_urb(lme_urb, GFP_ATOMIC);
-}
-
-static int lme2510_int_read(struct dvb_usb_adapter *adap)
-{
- struct lme2510_state *lme_int = adap->dev->priv;
-
- lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC);
-
- if (lme_int->lme_urb == NULL)
- return -ENOMEM;
-
- lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 128, GFP_ATOMIC,
- &lme_int->lme_urb->transfer_dma);
-
- if (lme_int->buffer == NULL)
- return -ENOMEM;
-
- usb_fill_int_urb(lme_int->lme_urb,
- adap->dev->udev,
- usb_rcvintpipe(adap->dev->udev, 0xa),
- lme_int->buffer,
- 128,
- lme2510_int_response,
- adap,
- 8);
-
- lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC);
- info("INT Interrupt Service Started");
-
- return 0;
-}
-
-static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct lme2510_state *st = adap->dev->priv;
- static u8 clear_pid_reg[] = LME_ALL_PIDS;
- static u8 rbuf[1];
- int ret = 0;
-
- deb_info(1, "PID Clearing Filter");
-
- mutex_lock(&adap->dev->i2c_mutex);
-
- if (!onoff) {
- ret |= lme2510_usb_talk(adap->dev, clear_pid_reg,
- sizeof(clear_pid_reg), rbuf, sizeof(rbuf));
- st->pid_off = true;
- } else
- st->pid_off = false;
-
- st->pid_size = 0;
-
- mutex_unlock(&adap->dev->i2c_mutex);
-
- return 0;
-}
-
-static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
- int onoff)
-{
- int ret = 0;
-
- deb_info(3, "%s PID=%04x Index=%04x onoff=%02x", __func__,
- pid, index, onoff);
-
- if (onoff) {
- mutex_lock(&adap->dev->i2c_mutex);
- ret |= lme2510_enable_pid(adap->dev, index, pid);
- mutex_unlock(&adap->dev->i2c_mutex);
- }
-
-
- return ret;
-}
-
-
-static int lme2510_return_status(struct usb_device *dev)
-{
- int ret = 0;
- u8 *data;
-
- data = kzalloc(10, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200);
- info("Firmware Status: %x (%x)", ret , data[2]);
-
- ret = (ret < 0) ? -ENODEV : data[2];
- kfree(data);
- return ret;
-}
-
-static int lme2510_msg(struct dvb_usb_device *d,
- u8 *wbuf, int wlen, u8 *rbuf, int rlen)
-{
- int ret = 0;
- struct lme2510_state *st = d->priv;
-
- if (st->i2c_talk_onoff == 1) {
-
- ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
-
- switch (st->tuner_config) {
- case TUNER_LG:
- if (wbuf[2] == 0x1c) {
- if (wbuf[3] == 0x0e) {
- st->signal_lock = rbuf[1];
- if ((st->stream_on & 1) &&
- (st->signal_lock & 0x10)) {
- lme2510_stream_restart(d);
- st->i2c_talk_onoff = 0;
- }
- msleep(80);
- }
- }
- break;
- case TUNER_S7395:
- if (wbuf[2] == 0xd0) {
- if (wbuf[3] == 0x24) {
- st->signal_lock = rbuf[1];
- if ((st->stream_on & 1) &&
- (st->signal_lock & 0x8)) {
- lme2510_stream_restart(d);
- st->i2c_talk_onoff = 0;
- }
- }
- }
- break;
- case TUNER_S0194:
- if (wbuf[2] == 0xd0) {
- if (wbuf[3] == 0x1b) {
- st->signal_lock = rbuf[1];
- if ((st->stream_on & 1) &&
- (st->signal_lock & 0x8)) {
- lme2510_stream_restart(d);
- st->i2c_talk_onoff = 0;
- }
- }
- }
- break;
- case TUNER_RS2000:
- default:
- break;
- }
- } else {
- /* TODO rewrite this section */
- switch (st->tuner_config) {
- case TUNER_LG:
- switch (wbuf[3]) {
- case 0x0e:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_lock;
- break;
- case 0x43:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_level;
- break;
- case 0x1c:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_sn;
- break;
- case 0x15:
- case 0x16:
- case 0x17:
- case 0x18:
- rbuf[0] = 0x55;
- rbuf[1] = 0x00;
- break;
- default:
- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
- st->i2c_talk_onoff = 1;
- break;
- }
- break;
- case TUNER_S7395:
- switch (wbuf[3]) {
- case 0x10:
- rbuf[0] = 0x55;
- rbuf[1] = (st->signal_level & 0x80)
- ? 0 : (st->signal_level * 2);
- break;
- case 0x2d:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_sn;
- break;
- case 0x24:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_lock;
- break;
- case 0x2e:
- case 0x26:
- case 0x27:
- rbuf[0] = 0x55;
- rbuf[1] = 0x00;
- break;
- default:
- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
- st->i2c_talk_onoff = 1;
- break;
- }
- break;
- case TUNER_S0194:
- switch (wbuf[3]) {
- case 0x18:
- rbuf[0] = 0x55;
- rbuf[1] = (st->signal_level & 0x80)
- ? 0 : (st->signal_level * 2);
- break;
- case 0x24:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_sn;
- break;
- case 0x1b:
- rbuf[0] = 0x55;
- rbuf[1] = st->signal_lock;
- break;
- case 0x19:
- case 0x25:
- case 0x1e:
- case 0x1d:
- rbuf[0] = 0x55;
- rbuf[1] = 0x00;
- break;
- default:
- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
- st->i2c_talk_onoff = 1;
- break;
- }
- break;
- case TUNER_RS2000:
- switch (wbuf[3]) {
- case 0x8c:
- rbuf[0] = 0x55;
- rbuf[1] = 0xff;
- if (st->last_key == st->time_key) {
- st->key_timeout++;
- if (st->key_timeout > 5)
- rbuf[1] = 0;
- } else
- st->key_timeout = 0;
- st->last_key = st->time_key;
- break;
- default:
- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
- st->i2c_talk_onoff = 1;
- break;
- }
- default:
- break;
- }
-
- deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)",
- wbuf[3], rbuf[1]);
-
- }
-
- return ret;
-}
-
-
-static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct lme2510_state *st = d->priv;
- static u8 obuf[64], ibuf[64];
- int i, read, read_o;
- u16 len;
- u8 gate = st->i2c_gate;
-
- mutex_lock(&d->i2c_mutex);
-
- if (gate == 0)
- gate = 5;
-
- if (num > 2)
- warn("more than 2 i2c messages"
- "at a time is not handled yet. TODO.");
-
- for (i = 0; i < num; i++) {
- read_o = 1 & (msg[i].flags & I2C_M_RD);
- read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
- read |= read_o;
- gate = (msg[i].addr == st->i2c_tuner_addr)
- ? (read) ? st->i2c_tuner_gate_r
- : st->i2c_tuner_gate_w
- : st->i2c_gate;
- obuf[0] = gate | (read << 7);
-
- if (gate == 5)
- obuf[1] = (read) ? 2 : msg[i].len + 1;
- else
- obuf[1] = msg[i].len + read + 1;
-
- obuf[2] = msg[i].addr;
- if (read) {
- if (read_o)
- len = 3;
- else {
- memcpy(&obuf[3], msg[i].buf, msg[i].len);
- obuf[msg[i].len+3] = msg[i+1].len;
- len = msg[i].len+4;
- }
- } else {
- memcpy(&obuf[3], msg[i].buf, msg[i].len);
- len = msg[i].len+3;
- }
-
- if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
- deb_info(1, "i2c transfer failed.");
- mutex_unlock(&d->i2c_mutex);
- return -EAGAIN;
- }
-
- if (read) {
- if (read_o)
- memcpy(msg[i].buf, &ibuf[1], msg[i].len);
- else {
- memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
- i++;
- }
- }
- }
-
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-static u32 lme2510_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm lme2510_i2c_algo = {
- .master_xfer = lme2510_i2c_xfer,
- .functionality = lme2510_i2c_func,
-};
-
-/* Callbacks for DVB USB */
-static int lme2510_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- if (pid_filter != 2)
- props->adapter[0].fe[0].caps &=
- ~DVB_USB_ADAP_NEED_PID_FILTERING;
- *cold = 0;
- return 0;
-}
-
-static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct lme2510_state *st = adap->dev->priv;
- static u8 clear_reg_3[] = LME_ALL_PIDS;
- static u8 rbuf[1];
- int ret = 0, rlen = sizeof(rbuf);
-
- deb_info(1, "STM (%02x)", onoff);
-
- /* Streaming is started by FE_HAS_LOCK */
- if (onoff == 1)
- st->stream_on = 1;
- else {
- deb_info(1, "STM Steam Off");
- /* mutex is here only to avoid collision with I2C */
- mutex_lock(&adap->dev->i2c_mutex);
-
- ret = lme2510_usb_talk(adap->dev, clear_reg_3,
- sizeof(clear_reg_3), rbuf, rlen);
- st->stream_on = 0;
- st->i2c_talk_onoff = 1;
-
- mutex_unlock(&adap->dev->i2c_mutex);
- }
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static u8 check_sum(u8 *p, u8 len)
-{
- u8 sum = 0;
- while (len--)
- sum += *p++;
- return sum;
-}
-
-static int lme2510_download_firmware(struct usb_device *dev,
- const struct firmware *fw)
-{
- int ret = 0;
- u8 *data;
- u16 j, wlen, len_in, start, end;
- u8 packet_size, dlen, i;
- u8 *fw_data;
-
- packet_size = 0x31;
- len_in = 1;
-
- data = kzalloc(512, GFP_KERNEL);
- if (!data) {
- info("FRM Could not start Firmware Download (Buffer allocation failed)");
- return -ENOMEM;
- }
-
- info("FRM Starting Firmware Download");
-
- for (i = 1; i < 3; i++) {
- start = (i == 1) ? 0 : 512;
- end = (i == 1) ? 512 : fw->size;
- for (j = start; j < end; j += (packet_size+1)) {
- fw_data = (u8 *)(fw->data + j);
- if ((end - j) > packet_size) {
- data[0] = i;
- dlen = packet_size;
- } else {
- data[0] = i | 0x80;
- dlen = (u8)(end - j)-1;
- }
- data[1] = dlen;
- memcpy(&data[2], fw_data, dlen+1);
- wlen = (u8) dlen + 4;
- data[wlen-1] = check_sum(fw_data, dlen+1);
- deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3],
- data[dlen+2], data[dlen+3]);
- ret |= lme2510_bulk_write(dev, data, wlen, 1);
- ret |= lme2510_bulk_read(dev, data, len_in , 1);
- ret |= (data[0] == 0x88) ? 0 : -1;
- }
- }
-
- usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- 0x06, 0x80, 0x0200, 0x00, data, 0x0109, 1000);
-
-
- data[0] = 0x8a;
- len_in = 1;
- msleep(2000);
- ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Resetting*/
- ret |= lme2510_bulk_read(dev, data, len_in, 1);
- msleep(400);
-
- if (ret < 0)
- info("FRM Firmware Download Failed (%04x)" , ret);
- else
- info("FRM Firmware Download Completed - Resetting Device");
-
- kfree(data);
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static void lme_coldreset(struct usb_device *dev)
-{
- int ret = 0, len_in;
- u8 data[512] = {0};
-
- data[0] = 0x0a;
- len_in = 1;
- info("FRM Firmware Cold Reset");
- ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Cold Resetting*/
- ret |= lme2510_bulk_read(dev, data, len_in, 1);
-
- return;
-}
-
-static int lme_firmware_switch(struct usb_device *udev, int cold)
-{
- const struct firmware *fw = NULL;
- const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw";
- const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw";
- const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw";
- const char fw_c_rs2000[] = "dvb-usb-lme2510c-rs2000.fw";
- const char fw_lg[] = "dvb-usb-lme2510-lg.fw";
- const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw";
- const char *fw_lme;
- int ret = 0, cold_fw;
-
- cold = (cold > 0) ? (cold & 1) : 0;
-
- cold_fw = !cold;
-
- switch (le16_to_cpu(udev->descriptor.idProduct)) {
- case 0x1122:
- switch (dvb_usb_lme2510_firmware) {
- default:
- dvb_usb_lme2510_firmware = TUNER_S0194;
- case TUNER_S0194:
- fw_lme = fw_s0194;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0) {
- cold = 0;
- break;
- }
- dvb_usb_lme2510_firmware = TUNER_LG;
- case TUNER_LG:
- fw_lme = fw_lg;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
- break;
- info("FRM No Firmware Found - please install");
- dvb_usb_lme2510_firmware = TUNER_DEFAULT;
- cold = 0;
- cold_fw = 0;
- break;
- }
- break;
- case 0x1120:
- switch (dvb_usb_lme2510_firmware) {
- default:
- dvb_usb_lme2510_firmware = TUNER_S7395;
- case TUNER_S7395:
- fw_lme = fw_c_s7395;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0) {
- cold = 0;
- break;
- }
- dvb_usb_lme2510_firmware = TUNER_LG;
- case TUNER_LG:
- fw_lme = fw_c_lg;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
- break;
- dvb_usb_lme2510_firmware = TUNER_S0194;
- case TUNER_S0194:
- fw_lme = fw_c_s0194;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- if (ret == 0)
- break;
- info("FRM No Firmware Found - please install");
- dvb_usb_lme2510_firmware = TUNER_DEFAULT;
- cold = 0;
- cold_fw = 0;
- break;
- }
- break;
- case 0x22f0:
- fw_lme = fw_c_rs2000;
- ret = request_firmware(&fw, fw_lme, &udev->dev);
- dvb_usb_lme2510_firmware = TUNER_RS2000;
- break;
- default:
- fw_lme = fw_c_s7395;
- }
-
-
- if (cold_fw) {
- info("FRM Loading %s file", fw_lme);
- ret = lme2510_download_firmware(udev, fw);
- }
-
- release_firmware(fw);
-
- if (cold) {
- info("FRM Changing to %s firmware", fw_lme);
- lme_coldreset(udev);
- return -ENODEV;
- }
-
- return ret;
-}
-
-static int lme2510_kill_urb(struct usb_data_stream *stream)
-{
- int i;
-
- for (i = 0; i < stream->urbs_submitted; i++) {
- deb_info(3, "killing URB no. %d.", i);
- /* stop the URB */
- usb_kill_urb(stream->urb_list[i]);
- }
- stream->urbs_submitted = 0;
-
- return 0;
-}
-
-static struct tda10086_config tda10086_config = {
- .demod_address = 0x1c,
- .invert = 0,
- .diseqc_tone = 1,
- .xtal_freq = TDA10086_XTAL_16M,
-};
-
-static struct stv0288_config lme_config = {
- .demod_address = 0xd0,
- .min_delay_ms = 15,
- .inittab = s7395_inittab,
-};
-
-static struct ix2505v_config lme_tuner = {
- .tuner_address = 0xc0,
- .min_delay_ms = 100,
- .tuner_gain = 0x0,
- .tuner_chargepump = 0x3,
-};
-
-static struct stv0299_config sharp_z0194_config = {
- .demod_address = 0xd0,
- .inittab = sharp_z0194a_inittab,
- .mclk = 88000000UL,
- .invert = 0,
- .skip_reinit = 0,
- .lock_output = STV0299_LOCKOUTPUT_1,
- .volt13_op0_op1 = STV0299_VOLT13_OP1,
- .min_delay_ms = 100,
- .set_symbol_rate = sharp_z0194a_set_symbol_rate,
-};
-
-static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
- int caller)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct dvb_usb_device *d = adap->dev;
- struct lme2510_state *st = d->priv;
-
- mutex_lock(&d->i2c_mutex);
- if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
- st->i2c_talk_onoff = 0;
- lme2510_stream_restart(d);
- }
- mutex_unlock(&d->i2c_mutex);
-
- return 0;
-}
-
-static struct m88rs2000_config m88rs2000_config = {
- .demod_addr = 0xd0,
- .tuner_addr = 0xc0,
- .set_ts_params = dm04_rs2000_set_ts_param,
-};
-
-static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- static u8 voltage_low[] = LME_VOLTAGE_L;
- static u8 voltage_high[] = LME_VOLTAGE_H;
- static u8 rbuf[1];
- int ret = 0, len = 3, rlen = 1;
-
- mutex_lock(&adap->dev->i2c_mutex);
-
- switch (voltage) {
- case SEC_VOLTAGE_18:
- ret |= lme2510_usb_talk(adap->dev,
- voltage_high, len, rbuf, rlen);
- break;
-
- case SEC_VOLTAGE_OFF:
- case SEC_VOLTAGE_13:
- default:
- ret |= lme2510_usb_talk(adap->dev,
- voltage_low, len, rbuf, rlen);
- break;
- }
-
- mutex_unlock(&adap->dev->i2c_mutex);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct lme2510_state *st = adap->dev->priv;
-
- *strength = (u16)((u32)st->signal_level * 0xffff / 0x7f);
- return 0;
-}
-
-static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct lme2510_state *st = adap->dev->priv;
-
- *snr = (u16)((u32)st->signal_sn * 0xffff / 0xff);
- return 0;
-}
-
-static int lme_name(struct dvb_usb_adapter *adap)
-{
- struct lme2510_state *st = adap->dev->priv;
- const char *desc = adap->dev->desc->name;
- char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
- " SHARP:BS2F7HZ0194", " RS2000"};
- char *name = adap->fe_adap[0].fe->ops.info.name;
-
- strlcpy(name, desc, 128);
- strlcat(name, fe_name[st->tuner_config], 128);
-
- return 0;
-}
-
-static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct lme2510_state *st = adap->dev->priv;
-
- int ret = 0;
-
- st->i2c_talk_onoff = 1;
- switch (le16_to_cpu(adap->dev->udev->descriptor.idProduct)) {
- case 0x1122:
- case 0x1120:
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(tda10086_attach,
- &tda10086_config, &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("TUN Found Frontend TDA10086");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 4;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_LG;
- if (dvb_usb_lme2510_firmware != TUNER_LG) {
- dvb_usb_lme2510_firmware = TUNER_LG;
- ret = lme_firmware_switch(adap->dev->udev, 1);
- }
- break;
- }
-
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(stv0299_attach,
- &sharp_z0194_config, &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0299");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S0194;
- if (dvb_usb_lme2510_firmware != TUNER_S0194) {
- dvb_usb_lme2510_firmware = TUNER_S0194;
- ret = lme_firmware_switch(adap->dev->udev, 1);
- }
- break;
- }
-
- st->i2c_gate = 5;
- adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
- &adap->dev->i2c_adap);
-
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0288");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S7395;
- if (dvb_usb_lme2510_firmware != TUNER_S7395) {
- dvb_usb_lme2510_firmware = TUNER_S7395;
- ret = lme_firmware_switch(adap->dev->udev, 1);
- }
- break;
- }
- case 0x22f0:
- st->i2c_gate = 5;
- adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
- &m88rs2000_config, &adap->dev->i2c_adap);
-
- if (adap->fe_adap[0].fe) {
- info("FE Found M88RS2000");
- st->i2c_tuner_gate_w = 5;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_RS2000;
- adap->fe_adap[0].fe->ops.read_signal_strength =
- dm04_rs2000_read_signal_strength;
- adap->fe_adap[0].fe->ops.read_snr =
- dm04_rs2000_read_snr;
- }
- break;
- }
-
- if (adap->fe_adap[0].fe == NULL) {
- info("DM04/QQBOX Not Powered up or not Supported");
- return -ENODEV;
- }
-
- if (ret) {
- if (adap->fe_adap[0].fe) {
- dvb_frontend_detach(adap->fe_adap[0].fe);
- adap->fe_adap[0].fe = NULL;
- }
- adap->dev->props.rc.core.rc_codes = NULL;
- return -ENODEV;
- }
-
- adap->fe_adap[0].fe->ops.set_voltage = dm04_lme2510_set_voltage;
- ret = lme_name(adap);
- return ret;
-}
-
-static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
-{
- struct lme2510_state *st = adap->dev->priv;
- char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"};
- int ret = 0;
-
- switch (st->tuner_config) {
- case TUNER_LG:
- if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0xc0,
- &adap->dev->i2c_adap, 1))
- ret = st->tuner_config;
- break;
- case TUNER_S7395:
- if (dvb_attach(ix2505v_attach , adap->fe_adap[0].fe, &lme_tuner,
- &adap->dev->i2c_adap))
- ret = st->tuner_config;
- break;
- case TUNER_S0194:
- if (dvb_attach(dvb_pll_attach , adap->fe_adap[0].fe, 0xc0,
- &adap->dev->i2c_adap, DVB_PLL_OPERA1))
- ret = st->tuner_config;
- break;
- case TUNER_RS2000:
- ret = st->tuner_config;
- break;
- default:
- break;
- }
-
- if (ret)
- info("TUN Found %s tuner", tun_msg[ret]);
- else {
- info("TUN No tuner found --- resetting device");
- lme_coldreset(adap->dev->udev);
- return -ENODEV;
- }
-
- /* Start the Interrupt*/
- ret = lme2510_int_read(adap);
- if (ret < 0) {
- info("INT Unable to start Interrupt Service");
- return -ENODEV;
- }
-
- return ret;
-}
-
-static int lme2510_powerup(struct dvb_usb_device *d, int onoff)
-{
- struct lme2510_state *st = d->priv;
- static u8 lnb_on[] = LNB_ON;
- static u8 lnb_off[] = LNB_OFF;
- static u8 rbuf[1];
- int ret = 0, len = 3, rlen = 1;
-
- mutex_lock(&d->i2c_mutex);
-
- if (onoff)
- ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen);
- else
- ret = lme2510_usb_talk(d, lnb_off, len, rbuf, rlen);
-
- st->i2c_talk_onoff = 1;
-
- mutex_unlock(&d->i2c_mutex);
-
- return ret;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties lme2510_properties;
-static struct dvb_usb_device_properties lme2510c_properties;
-
-static int lme2510_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
-
- usb_reset_configuration(udev);
-
- usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1);
-
- if (udev->speed != USB_SPEED_HIGH) {
- usb_reset_device(udev);
- info("DEV Failed to connect in HIGH SPEED mode");
- return -ENODEV;
- }
-
- if (lme2510_return_status(udev) == 0x44) {
- lme_firmware_switch(udev, 0);
- return -ENODEV;
- }
-
- if (0 == dvb_usb_device_init(intf, &lme2510_properties,
- THIS_MODULE, NULL, adapter_nr)) {
- info("DEV registering device driver");
- return 0;
- }
- if (0 == dvb_usb_device_init(intf, &lme2510c_properties,
- THIS_MODULE, NULL, adapter_nr)) {
- info("DEV registering device driver");
- return 0;
- }
-
- info("DEV lme2510 Error");
- return -ENODEV;
-
-}
-
-static struct usb_device_id lme2510_table[] = {
- { USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */
- { USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */
- { USB_DEVICE(0x3344, 0x22f0) }, /* LME2510C RS2000 */
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, lme2510_table);
-
-static struct dvb_usb_device_properties lme2510_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
- .size_of_priv = sizeof(struct lme2510_state),
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_NEED_PID_FILTERING|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 32,
- .pid_filter = lme2510_pid_filter,
- .pid_filter_ctrl = lme2510_pid_filter_ctrl,
- .frontend_attach = dm04_lme2510_frontend_attach,
- .tuner_attach = dm04_lme2510_tuner,
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 10,
- .endpoint = 0x06,
- .u = {
- .bulk = {
- .buffersize = 4096,
-
- }
- }
- }
- }},
- }
- },
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "LME2510 Remote Control",
- .allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_LME2510,
- },
- .power_ctrl = lme2510_powerup,
- .identify_state = lme2510_identify_state,
- .i2c_algo = &lme2510_i2c_algo,
- .generic_bulk_ctrl_endpoint = 0,
- .num_device_descs = 1,
- .devices = {
- { "DM04_LME2510_DVB-S",
- { &lme2510_table[0], NULL },
- },
-
- }
-};
-
-static struct dvb_usb_device_properties lme2510c_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
- .size_of_priv = sizeof(struct lme2510_state),
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER|
- DVB_USB_ADAP_NEED_PID_FILTERING|
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
- .streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 32,
- .pid_filter = lme2510_pid_filter,
- .pid_filter_ctrl = lme2510_pid_filter_ctrl,
- .frontend_attach = dm04_lme2510_frontend_attach,
- .tuner_attach = dm04_lme2510_tuner,
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_BULK,
- .count = 10,
- .endpoint = 0x8,
- .u = {
- .bulk = {
- .buffersize = 4096,
-
- }
- }
- }
- }},
- }
- },
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "LME2510 Remote Control",
- .allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_LME2510,
- },
- .power_ctrl = lme2510_powerup,
- .identify_state = lme2510_identify_state,
- .i2c_algo = &lme2510_i2c_algo,
- .generic_bulk_ctrl_endpoint = 0,
- .num_device_descs = 2,
- .devices = {
- { "DM04_LME2510C_DVB-S",
- { &lme2510_table[1], NULL },
- },
- { "DM04_LME2510C_DVB-S RS2000",
- { &lme2510_table[2], NULL },
- },
- }
-};
-
-static void *lme2510_exit_int(struct dvb_usb_device *d)
-{
- struct lme2510_state *st = d->priv;
- struct dvb_usb_adapter *adap = &d->adapter[0];
- void *buffer = NULL;
-
- if (adap != NULL) {
- lme2510_kill_urb(&adap->fe_adap[0].stream);
- adap->feedcount = 0;
- }
-
- if (st->usb_buffer != NULL) {
- st->i2c_talk_onoff = 1;
- st->signal_lock = 0;
- st->signal_level = 0;
- st->signal_sn = 0;
- buffer = st->usb_buffer;
- }
-
- if (st->lme_urb != NULL) {
- usb_kill_urb(st->lme_urb);
- usb_free_coherent(d->udev, 128, st->buffer,
- st->lme_urb->transfer_dma);
- info("Interrupt Service Stopped");
- }
-
- return buffer;
-}
-
-static void lme2510_exit(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
- void *usb_buffer;
-
- if (d != NULL) {
- usb_buffer = lme2510_exit_int(d);
- dvb_usb_device_exit(intf);
- if (usb_buffer != NULL)
- kfree(usb_buffer);
- }
-}
-
-static struct usb_driver lme2510_driver = {
- .name = "LME2510C_DVB-S",
- .probe = lme2510_probe,
- .disconnect = lme2510_exit,
- .id_table = lme2510_table,
-};
-
-module_usb_driver(lme2510_driver);
-
-MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
-MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.99");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
deleted file mode 100644
index 74da5bb1ce99..000000000000
--- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
- *
- * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "mxl111sf-tuner.h"
-#include "mxl111sf-phy.h"
-#include "mxl111sf-reg.h"
-
-/* debug */
-static int mxl111sf_tuner_debug;
-module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
-
-#define mxl_dbg(fmt, arg...) \
- if (mxl111sf_tuner_debug) \
- mxl_printk(KERN_DEBUG, fmt, ##arg)
-
-/* ------------------------------------------------------------------------ */
-
-struct mxl111sf_tuner_state {
- struct mxl111sf_state *mxl_state;
-
- struct mxl111sf_tuner_config *cfg;
-
- enum mxl_if_freq if_freq;
-
- u32 frequency;
- u32 bandwidth;
-};
-
-static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
- u8 addr, u8 *data)
-{
- return (state->cfg->read_reg) ?
- state->cfg->read_reg(state->mxl_state, addr, data) :
- -EINVAL;
-}
-
-static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
- u8 addr, u8 data)
-{
- return (state->cfg->write_reg) ?
- state->cfg->write_reg(state->mxl_state, addr, data) :
- -EINVAL;
-}
-
-static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
- struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
-{
- return (state->cfg->program_regs) ?
- state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
- -EINVAL;
-}
-
-static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
- int onoff)
-{
- return (state->cfg->top_master_ctrl) ?
- state->cfg->top_master_ctrl(state->mxl_state, onoff) :
- -EINVAL;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
- {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
- DIG_MODEINDEX, _A, _CSF, */
- {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
- {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
- {0, 0, 0}
-};
-
-/* ------------------------------------------------------------------------ */
-
-static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
- u8 bw)
-{
- u8 filt_bw;
-
- /* set channel bandwidth */
- switch (bw) {
- case 0: /* ATSC */
- filt_bw = 25;
- break;
- case 1: /* QAM */
- filt_bw = 69;
- break;
- case 6:
- filt_bw = 21;
- break;
- case 7:
- filt_bw = 42;
- break;
- case 8:
- filt_bw = 63;
- break;
- default:
- err("%s: invalid bandwidth setting!", __func__);
- return NULL;
- }
-
- /* calculate RF channel */
- freq /= 1000000;
-
- freq *= 64;
-#if 0
- /* do round */
- freq += 0.5;
-#endif
- /* set bandwidth */
- mxl_phy_tune_rf[0].data = filt_bw;
-
- /* set RF */
- mxl_phy_tune_rf[1].data = (freq & 0xff);
- mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
-
- /* start tune */
- return mxl_phy_tune_rf;
-}
-
-static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
-{
- int ret;
- u8 ctrl;
-#if 0
- u16 iffcw;
- u32 if_freq;
-#endif
- mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
- state->cfg->invert_spectrum, state->cfg->if_freq);
-
- /* set IF polarity */
- ctrl = state->cfg->invert_spectrum;
-
- ctrl |= state->cfg->if_freq;
-
- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
- if (mxl_fail(ret))
- goto fail;
-
-#if 0
- if_freq /= 1000000;
-
- /* do round */
- if_freq += 0.5;
-
- if (MXL_IF_LO == state->cfg->if_freq) {
- ctrl = 0x08;
- iffcw = (u16)(if_freq / (108 * 4096));
- } else if (MXL_IF_HI == state->cfg->if_freq) {
- ctrl = 0x08;
- iffcw = (u16)(if_freq / (216 * 4096));
- } else {
- ctrl = 0;
- iffcw = 0;
- }
-
- ctrl |= (iffcw >> 8);
-#endif
- ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
- if (mxl_fail(ret))
- goto fail;
-
- ctrl &= 0xf0;
- ctrl |= 0x90;
-
- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
- if (mxl_fail(ret))
- goto fail;
-
-#if 0
- ctrl = iffcw & 0x00ff;
-#endif
- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
- if (mxl_fail(ret))
- goto fail;
-
- state->if_freq = state->cfg->if_freq;
-fail:
- return ret;
-}
-
-static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
- int ret;
- u8 mxl_mode;
-
- mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
-
- /* stop tune */
- ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
- if (mxl_fail(ret))
- goto fail;
-
- /* check device mode */
- ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
- if (mxl_fail(ret))
- goto fail;
-
- /* Fill out registers for channel tune */
- reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
- if (!reg_ctrl_array)
- return -EINVAL;
-
- ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
- if (mxl_fail(ret))
- goto fail;
-
- if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
- /* IF tuner mode only */
- mxl1x1sf_tuner_top_master_ctrl(state, 0);
- mxl1x1sf_tuner_top_master_ctrl(state, 1);
- mxl1x1sf_tuner_set_if_output_freq(state);
- }
-
- ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
- if (mxl_fail(ret))
- goto fail;
-
- if (state->cfg->ant_hunt)
- state->cfg->ant_hunt(fe);
-fail:
- return ret;
-}
-
-static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
- int *rf_synth_lock,
- int *ref_synth_lock)
-{
- int ret;
- u8 data;
-
- *rf_synth_lock = 0;
- *ref_synth_lock = 0;
-
- ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
- if (mxl_fail(ret))
- goto fail;
-
- *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
- *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
-fail:
- return ret;
-}
-
-#if 0
-static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
- int onoff)
-{
- return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
- onoff ? 1 : 0);
-}
-#endif
-
-/* ------------------------------------------------------------------------ */
-
-static int mxl111sf_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u32 delsys = c->delivery_system;
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- int ret;
- u8 bw;
-
- mxl_dbg("()");
-
- switch (delsys) {
- case SYS_ATSC:
- case SYS_ATSCMH:
- bw = 0; /* ATSC */
- break;
- case SYS_DVBC_ANNEX_B:
- bw = 1; /* US CABLE */
- break;
- case SYS_DVBT:
- switch (c->bandwidth_hz) {
- case 6000000:
- bw = 6;
- break;
- case 7000000:
- bw = 7;
- break;
- case 8000000:
- bw = 8;
- break;
- default:
- err("%s: bandwidth not set!", __func__);
- return -EINVAL;
- }
- break;
- default:
- err("%s: modulation type not supported!", __func__);
- return -EINVAL;
- }
- ret = mxl1x1sf_tune_rf(fe, c->frequency, bw);
- if (mxl_fail(ret))
- goto fail;
-
- state->frequency = c->frequency;
- state->bandwidth = c->bandwidth_hz;
-fail:
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-#if 0
-static int mxl111sf_tuner_init(struct dvb_frontend *fe)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- int ret;
-
- /* wake from standby handled by usb driver */
-
- return ret;
-}
-
-static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- int ret;
-
- /* enter standby mode handled by usb driver */
-
- return ret;
-}
-#endif
-
-/* ------------------------------------------------------------------------ */
-
-static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- int rf_locked, ref_locked, ret;
-
- *status = 0;
-
- ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
- if (mxl_fail(ret))
- goto fail;
- mxl_info("%s%s", rf_locked ? "rf locked " : "",
- ref_locked ? "ref locked" : "");
-
- if ((rf_locked) || (ref_locked))
- *status |= TUNER_STATUS_LOCKED;
-fail:
- return ret;
-}
-
-static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- u8 val1, val2;
- int ret;
-
- *strength = 0;
-
- ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
- if (mxl_fail(ret))
- goto fail;
-
- *strength = val1 | ((val2 & 0x07) << 8);
-fail:
- ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
- mxl_fail(ret);
-
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- *frequency = state->frequency;
- return 0;
-}
-
-static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- *bandwidth = state->bandwidth;
- return 0;
-}
-
-static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe,
- u32 *frequency)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
-
- *frequency = 0;
-
- switch (state->if_freq) {
- case MXL_IF_4_0: /* 4.0 MHz */
- *frequency = 4000000;
- break;
- case MXL_IF_4_5: /* 4.5 MHz */
- *frequency = 4500000;
- break;
- case MXL_IF_4_57: /* 4.57 MHz */
- *frequency = 4570000;
- break;
- case MXL_IF_5_0: /* 5.0 MHz */
- *frequency = 5000000;
- break;
- case MXL_IF_5_38: /* 5.38 MHz */
- *frequency = 5380000;
- break;
- case MXL_IF_6_0: /* 6.0 MHz */
- *frequency = 6000000;
- break;
- case MXL_IF_6_28: /* 6.28 MHz */
- *frequency = 6280000;
- break;
- case MXL_IF_7_2: /* 7.2 MHz */
- *frequency = 7200000;
- break;
- case MXL_IF_35_25: /* 35.25 MHz */
- *frequency = 35250000;
- break;
- case MXL_IF_36: /* 36 MHz */
- *frequency = 36000000;
- break;
- case MXL_IF_36_15: /* 36.15 MHz */
- *frequency = 36150000;
- break;
- case MXL_IF_44: /* 44 MHz */
- *frequency = 44000000;
- break;
- }
- return 0;
-}
-
-static int mxl111sf_tuner_release(struct dvb_frontend *fe)
-{
- struct mxl111sf_tuner_state *state = fe->tuner_priv;
- mxl_dbg("()");
- kfree(state);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
- .info = {
- .name = "MaxLinear MxL111SF",
-#if 0
- .frequency_min = ,
- .frequency_max = ,
- .frequency_step = ,
-#endif
- },
-#if 0
- .init = mxl111sf_tuner_init,
- .sleep = mxl111sf_tuner_sleep,
-#endif
- .set_params = mxl111sf_tuner_set_params,
- .get_status = mxl111sf_tuner_get_status,
- .get_rf_strength = mxl111sf_get_rf_strength,
- .get_frequency = mxl111sf_tuner_get_frequency,
- .get_bandwidth = mxl111sf_tuner_get_bandwidth,
- .get_if_frequency = mxl111sf_tuner_get_if_frequency,
- .release = mxl111sf_tuner_release,
-};
-
-struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
- struct mxl111sf_state *mxl_state,
- struct mxl111sf_tuner_config *cfg)
-{
- struct mxl111sf_tuner_state *state = NULL;
-
- mxl_dbg("()");
-
- state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
- if (state == NULL)
- return NULL;
-
- state->mxl_state = mxl_state;
- state->cfg = cfg;
-
- memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- fe->tuner_priv = state;
- return fe;
-}
-EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
-
-MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
-MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.1");
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
deleted file mode 100644
index cd842798f5af..000000000000
--- a/drivers/media/dvb/dvb-usb/mxl111sf.c
+++ /dev/null
@@ -1,1835 +0,0 @@
-/*
- * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, version 2.
- *
- * see Documentation/dvb/README.dvb-usb for more information
- */
-
-#include <linux/vmalloc.h>
-#include <linux/i2c.h>
-
-#include "mxl111sf.h"
-#include "mxl111sf-reg.h"
-#include "mxl111sf-phy.h"
-#include "mxl111sf-i2c.h"
-#include "mxl111sf-gpio.h"
-
-#include "mxl111sf-demod.h"
-#include "mxl111sf-tuner.h"
-
-#include "lgdt3305.h"
-#include "lg2160.h"
-
-int dvb_usb_mxl111sf_debug;
-module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level "
- "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
-
-int dvb_usb_mxl111sf_isoc;
-module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
-MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
-
-int dvb_usb_mxl111sf_spi;
-module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644);
-MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi).");
-
-#define ANT_PATH_AUTO 0
-#define ANT_PATH_EXTERNAL 1
-#define ANT_PATH_INTERNAL 2
-
-int dvb_usb_mxl111sf_rfswitch =
-#if 0
- ANT_PATH_AUTO;
-#else
- ANT_PATH_EXTERNAL;
-#endif
-
-module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644);
-MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args)
-#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args)
-#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args)
-
-int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
-{
- int wo = (rbuf == NULL || rlen == 0); /* write-only */
- int ret;
- u8 sndbuf[1+wlen];
-
- deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
-
- memset(sndbuf, 0, 1+wlen);
-
- sndbuf[0] = cmd;
- memcpy(&sndbuf[1], wbuf, wlen);
-
- ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) :
- dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0);
- mxl_fail(ret);
-
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-#define MXL_CMD_REG_READ 0xaa
-#define MXL_CMD_REG_WRITE 0x55
-
-int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
-{
- u8 buf[2];
- int ret;
-
- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
- if (mxl_fail(ret)) {
- mxl_debug("error reading reg: 0x%02x", addr);
- goto fail;
- }
-
- if (buf[0] == addr)
- *data = buf[1];
- else {
- err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
- addr, buf[0], buf[1]);
- ret = -EINVAL;
- }
-
- deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
-fail:
- return ret;
-}
-
-int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
-{
- u8 buf[] = { addr, data };
- int ret;
-
- deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
-
- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
- if (mxl_fail(ret))
- err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
- u8 addr, u8 mask, u8 data)
-{
- int ret;
- u8 val;
-
- if (mask != 0xff) {
- ret = mxl111sf_read_reg(state, addr, &val);
-#if 1
- /* dont know why this usually errors out on the first try */
- if (mxl_fail(ret))
- err("error writing addr: 0x%02x, mask: 0x%02x, "
- "data: 0x%02x, retrying...", addr, mask, data);
-
- ret = mxl111sf_read_reg(state, addr, &val);
-#endif
- if (mxl_fail(ret))
- goto fail;
- }
- val &= ~mask;
- val |= data;
-
- ret = mxl111sf_write_reg(state, addr, val);
- mxl_fail(ret);
-fail:
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
- struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
-{
- int i, ret = 0;
-
- for (i = 0; ctrl_reg_info[i].addr |
- ctrl_reg_info[i].mask |
- ctrl_reg_info[i].data; i++) {
-
- ret = mxl111sf_write_reg_mask(state,
- ctrl_reg_info[i].addr,
- ctrl_reg_info[i].mask,
- ctrl_reg_info[i].data);
- if (mxl_fail(ret)) {
- err("failed on reg #%d (0x%02x)", i,
- ctrl_reg_info[i].addr);
- break;
- }
- }
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state)
-{
- int ret;
- u8 id, ver;
- char *mxl_chip, *mxl_rev;
-
- if ((state->chip_id) && (state->chip_ver))
- return 0;
-
- ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id);
- if (mxl_fail(ret))
- goto fail;
- state->chip_id = id;
-
- ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver);
- if (mxl_fail(ret))
- goto fail;
- state->chip_ver = ver;
-
- switch (id) {
- case 0x61:
- mxl_chip = "MxL101SF";
- break;
- case 0x63:
- mxl_chip = "MxL111SF";
- break;
- default:
- mxl_chip = "UNKNOWN MxL1X1";
- break;
- }
- switch (ver) {
- case 0x36:
- state->chip_rev = MXL111SF_V6;
- mxl_rev = "v6";
- break;
- case 0x08:
- state->chip_rev = MXL111SF_V8_100;
- mxl_rev = "v8_100";
- break;
- case 0x18:
- state->chip_rev = MXL111SF_V8_200;
- mxl_rev = "v8_200";
- break;
- default:
- state->chip_rev = 0;
- mxl_rev = "UNKNOWN REVISION";
- break;
- }
- info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
-fail:
- return ret;
-}
-
-#define get_chip_info(state) \
-({ \
- int ___ret; \
- ___ret = mxl1x1sf_get_chip_info(state); \
- if (mxl_fail(___ret)) { \
- mxl_debug("failed to get chip info" \
- " on first probe attempt"); \
- ___ret = mxl1x1sf_get_chip_info(state); \
- if (mxl_fail(___ret)) \
- err("failed to get chip info during probe"); \
- else \
- mxl_debug("probe needed a retry " \
- "in order to succeed."); \
- } \
- ___ret; \
-})
-
-/* ------------------------------------------------------------------------ */
-
-static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- /* power control depends on which adapter is being woken:
- * save this for init, instead, via mxl111sf_adap_fe_init */
- return 0;
-}
-
-static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
-
- int err;
-
- /* exit if we didnt initialize the driver yet */
- if (!state->chip_id) {
- mxl_debug("driver not yet initialized, exit.");
- goto fail;
- }
-
- deb_info("%s()\n", __func__);
-
- mutex_lock(&state->fe_lock);
-
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- err = mxl1x1sf_soft_reset(state);
- mxl_fail(err);
- err = mxl111sf_init_tuner_demod(state);
- mxl_fail(err);
- err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
-
- mxl_fail(err);
- mxl111sf_enable_usb_output(state);
- mxl_fail(err);
- mxl1x1sf_top_master_ctrl(state, 1);
- mxl_fail(err);
-
- if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
- (state->chip_rev > MXL111SF_V6)) {
- mxl111sf_config_pin_mux_modes(state,
- PIN_MUX_TS_SPI_IN_MODE_1);
- mxl_fail(err);
- }
- err = mxl111sf_init_port_expander(state);
- if (!mxl_fail(err)) {
- state->gpio_mode = adap_state->gpio_mode;
- err = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
- mxl_fail(err);
-#if 0
- err = fe->ops.init(fe);
-#endif
- msleep(100); /* add short delay after enabling
- * the demod before touching it */
- }
-
- return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0;
-fail:
- return -ENODEV;
-}
-
-static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv;
- int err;
-
- /* exit if we didnt initialize the driver yet */
- if (!state->chip_id) {
- mxl_debug("driver not yet initialized, exit.");
- goto fail;
- }
-
- deb_info("%s()\n", __func__);
-
- err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
-
- mutex_unlock(&state->fe_lock);
-
- return err;
-fail:
- return -ENODEV;
-}
-
-
-static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv;
- int ret = 0;
-
- deb_info("%s(%d)\n", __func__, onoff);
-
- if (onoff) {
- ret = mxl111sf_enable_usb_output(state);
- mxl_fail(ret);
- ret = mxl111sf_config_mpeg_in(state, 1, 1,
- adap_state->ep6_clockphase,
- 0, 0);
- mxl_fail(ret);
-#if 0
- } else {
- ret = mxl111sf_disable_656_port(state);
- mxl_fail(ret);
-#endif
- }
-
- return ret;
-}
-
-static int mxl111sf_ep5_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int ret = 0;
-
- deb_info("%s(%d)\n", __func__, onoff);
-
- if (onoff) {
- ret = mxl111sf_enable_usb_output(state);
- mxl_fail(ret);
-
- ret = mxl111sf_init_i2s_port(state, 200);
- mxl_fail(ret);
- ret = mxl111sf_config_i2s(state, 0, 15);
- mxl_fail(ret);
- } else {
- ret = mxl111sf_disable_i2s_port(state);
- mxl_fail(ret);
- }
- if (state->chip_rev > MXL111SF_V6)
- ret = mxl111sf_config_spi(state, onoff);
- mxl_fail(ret);
-
- return ret;
-}
-
-static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int ret = 0;
-
- deb_info("%s(%d)\n", __func__, onoff);
-
- if (onoff) {
- ret = mxl111sf_enable_usb_output(state);
- mxl_fail(ret);
- }
-
- return ret;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static struct lgdt3305_config hauppauge_lgdt3305_config = {
- .i2c_addr = 0xb2 >> 1,
- .mpeg_mode = LGDT3305_MPEG_SERIAL,
- .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
- .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .qam_if_khz = 6000,
- .vsb_if_khz = 6000,
-};
-
-static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
- int ret;
-
- deb_adv("%s()\n", __func__);
-
- /* save a pointer to the dvb_usb_device in device state */
- state->d = d;
- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
- adap_state->gpio_mode = state->gpio_mode;
- adap_state->device_mode = MXL_TUNER_MODE;
- adap_state->ep6_clockphase = 1;
-
- ret = mxl1x1sf_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_init_tuner_demod(state);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_enable_usb_output(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl1x1sf_top_master_ctrl(state, 1);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_init_port_expander(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
- if (mxl_fail(ret))
- goto fail;
-
- adap->fe_adap[fe_id].fe = dvb_attach(lgdt3305_attach,
- &hauppauge_lgdt3305_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[fe_id].fe) {
- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
- return 0;
- }
- ret = -EIO;
-fail:
- return ret;
-}
-
-static struct lg2160_config hauppauge_lg2160_config = {
- .lg_chip = LG2160,
- .i2c_addr = 0x1c >> 1,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .if_khz = 6000,
-};
-
-static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
- int ret;
-
- deb_adv("%s()\n", __func__);
-
- /* save a pointer to the dvb_usb_device in device state */
- state->d = d;
- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- state->gpio_mode = MXL111SF_GPIO_MOD_MH;
- adap_state->gpio_mode = state->gpio_mode;
- adap_state->device_mode = MXL_TUNER_MODE;
- adap_state->ep6_clockphase = 1;
-
- ret = mxl1x1sf_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_init_tuner_demod(state);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_enable_usb_output(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl1x1sf_top_master_ctrl(state, 1);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_init_port_expander(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = get_chip_info(state);
- if (mxl_fail(ret))
- goto fail;
-
- adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
- &hauppauge_lg2160_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[fe_id].fe) {
- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
- return 0;
- }
- ret = -EIO;
-fail:
- return ret;
-}
-
-static struct lg2160_config hauppauge_lg2161_1019_config = {
- .lg_chip = LG2161_1019,
- .i2c_addr = 0x1c >> 1,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .if_khz = 6000,
- .output_if = 2, /* LG2161_OIF_SPI_MAS */
-};
-
-static struct lg2160_config hauppauge_lg2161_1040_config = {
- .lg_chip = LG2161_1040,
- .i2c_addr = 0x1c >> 1,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .if_khz = 6000,
- .output_if = 4, /* LG2161_OIF_SPI_MAS */
-};
-
-static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
- int ret;
-
- deb_adv("%s()\n", __func__);
-
- /* save a pointer to the dvb_usb_device in device state */
- state->d = d;
- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- state->gpio_mode = MXL111SF_GPIO_MOD_MH;
- adap_state->gpio_mode = state->gpio_mode;
- adap_state->device_mode = MXL_TUNER_MODE;
- adap_state->ep6_clockphase = 1;
-
- ret = mxl1x1sf_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_init_tuner_demod(state);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_enable_usb_output(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl1x1sf_top_master_ctrl(state, 1);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_init_port_expander(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = get_chip_info(state);
- if (mxl_fail(ret))
- goto fail;
-
- adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
- (MXL111SF_V8_200 == state->chip_rev) ?
- &hauppauge_lg2161_1040_config :
- &hauppauge_lg2161_1019_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[fe_id].fe) {
- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
- return 0;
- }
- ret = -EIO;
-fail:
- return ret;
-}
-
-static struct lg2160_config hauppauge_lg2161_1019_ep6_config = {
- .lg_chip = LG2161_1019,
- .i2c_addr = 0x1c >> 1,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .if_khz = 6000,
- .output_if = 1, /* LG2161_OIF_SERIAL_TS */
-};
-
-static struct lg2160_config hauppauge_lg2161_1040_ep6_config = {
- .lg_chip = LG2161_1040,
- .i2c_addr = 0x1c >> 1,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 0,
- .if_khz = 6000,
- .output_if = 7, /* LG2161_OIF_SERIAL_TS */
-};
-
-static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
- int ret;
-
- deb_adv("%s()\n", __func__);
-
- /* save a pointer to the dvb_usb_device in device state */
- state->d = d;
- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- state->gpio_mode = MXL111SF_GPIO_MOD_MH;
- adap_state->gpio_mode = state->gpio_mode;
- adap_state->device_mode = MXL_TUNER_MODE;
- adap_state->ep6_clockphase = 0;
-
- ret = mxl1x1sf_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_init_tuner_demod(state);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_enable_usb_output(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl1x1sf_top_master_ctrl(state, 1);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_init_port_expander(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = get_chip_info(state);
- if (mxl_fail(ret))
- goto fail;
-
- adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
- (MXL111SF_V8_200 == state->chip_rev) ?
- &hauppauge_lg2161_1040_ep6_config :
- &hauppauge_lg2161_1019_ep6_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[fe_id].fe) {
- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
- return 0;
- }
- ret = -EIO;
-fail:
- return ret;
-}
-
-static struct mxl111sf_demod_config mxl_demod_config = {
- .read_reg = mxl111sf_read_reg,
- .write_reg = mxl111sf_write_reg,
- .program_regs = mxl111sf_ctrl_program_regs,
-};
-
-static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
- int ret;
-
- deb_adv("%s()\n", __func__);
-
- /* save a pointer to the dvb_usb_device in device state */
- state->d = d;
- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
- state->alt_mode = adap_state->alt_mode;
-
- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
- err("set interface failed");
-
- state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
- adap_state->gpio_mode = state->gpio_mode;
- adap_state->device_mode = MXL_SOC_MODE;
- adap_state->ep6_clockphase = 1;
-
- ret = mxl1x1sf_soft_reset(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl111sf_init_tuner_demod(state);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
- if (mxl_fail(ret))
- goto fail;
-
- ret = mxl111sf_enable_usb_output(state);
- if (mxl_fail(ret))
- goto fail;
- ret = mxl1x1sf_top_master_ctrl(state, 1);
- if (mxl_fail(ret))
- goto fail;
-
- /* dont care if this fails */
- mxl111sf_init_port_expander(state);
-
- adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state,
- &mxl_demod_config);
- if (adap->fe_adap[fe_id].fe) {
- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
- return 0;
- }
- ret = -EIO;
-fail:
- return ret;
-}
-
-static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
- int antpath)
-{
- return mxl111sf_idac_config(state, 1, 1,
- (antpath == ANT_PATH_INTERNAL) ?
- 0x3f : 0x00, 0);
-}
-
-#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
- err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
- __func__, __LINE__, \
- (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
- pwr0, pwr1, pwr2, pwr3)
-
-#define ANT_HUNT_SLEEP 90
-#define ANT_EXT_TWEAK 0
-
-static int mxl111sf_ant_hunt(struct dvb_frontend *fe)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
-
- int antctrl = dvb_usb_mxl111sf_rfswitch;
-
- u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2;
-
- /* FIXME: must force EXTERNAL for QAM - done elsewhere */
- mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ?
- ANT_PATH_EXTERNAL : antctrl);
-
- if (antctrl == ANT_PATH_AUTO) {
-#if 0
- msleep(ANT_HUNT_SLEEP);
-#endif
- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA);
-
- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
- msleep(ANT_HUNT_SLEEP);
- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0);
-
- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
- msleep(ANT_HUNT_SLEEP);
- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1);
-
- mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL);
- msleep(ANT_HUNT_SLEEP);
- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2);
-
- if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) {
- /* return with EXTERNAL enabled */
- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
- DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA,
- rxPwr0, rxPwr1, rxPwr2);
- } else {
- /* return with INTERNAL enabled */
- DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA,
- rxPwr0, rxPwr1, rxPwr2);
- }
- }
- return 0;
-}
-
-static struct mxl111sf_tuner_config mxl_tuner_config = {
- .if_freq = MXL_IF_6_0, /* applies to external IF output, only */
- .invert_spectrum = 0,
- .read_reg = mxl111sf_read_reg,
- .write_reg = mxl111sf_write_reg,
- .program_regs = mxl111sf_ctrl_program_regs,
- .top_master_ctrl = mxl1x1sf_top_master_ctrl,
- .ant_hunt = mxl111sf_ant_hunt,
-};
-
-static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
-{
- struct dvb_usb_device *d = adap->dev;
- struct mxl111sf_state *state = d->priv;
- int fe_id = adap->num_frontends_initialized;
-
- deb_adv("%s()\n", __func__);
-
- if (NULL != dvb_attach(mxl111sf_tuner_attach,
- adap->fe_adap[fe_id].fe, state,
- &mxl_tuner_config))
- return 0;
-
- return -EIO;
-}
-
-static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe,
- unsigned int cmd, void *parg,
- unsigned int stage)
-{
- int err = 0;
-
- switch (stage) {
- case DVB_FE_IOCTL_PRE:
-
- switch (cmd) {
- case FE_READ_SIGNAL_STRENGTH:
- err = fe->ops.tuner_ops.get_rf_strength(fe, parg);
- /* If no error occurs, prevent dvb-core from handling
- * this IOCTL, otherwise return the error */
- if (0 == err)
- err = 1;
- break;
- }
- break;
-
- case DVB_FE_IOCTL_POST:
- /* no post-ioctl handling required */
- break;
- }
- return err;
-};
-
-static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-struct i2c_algorithm mxl111sf_i2c_algo = {
- .master_xfer = mxl111sf_i2c_xfer,
- .functionality = mxl111sf_i2c_func,
-#ifdef NEED_ALGO_CONTROL
- .algo_control = dummy_algo_control,
-#endif
-};
-
-static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties;
-static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties;
-
-static int mxl111sf_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct dvb_usb_device *d = NULL;
-
- deb_adv("%s()\n", __func__);
-
- if (((dvb_usb_mxl111sf_isoc) &&
- (0 == dvb_usb_device_init(intf,
- &mxl111sf_dvbt_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_atsc_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_atsc_mh_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mh_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- ((dvb_usb_mxl111sf_spi) &&
- (0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_spi_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_mh_spi_isoc_properties,
- THIS_MODULE, &d, adapter_nr))) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_tp_isoc_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_mh_tp_isoc_properties,
- THIS_MODULE, &d, adapter_nr))) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_dvbt_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_atsc_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_atsc_mh_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mh_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- ((dvb_usb_mxl111sf_spi) &&
- (0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_spi_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_mh_spi_bulk_properties,
- THIS_MODULE, &d, adapter_nr))) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_tp_bulk_properties,
- THIS_MODULE, &d, adapter_nr) ||
- 0 == dvb_usb_device_init(intf,
- &mxl111sf_mercury_mh_tp_bulk_properties,
- THIS_MODULE, &d, adapter_nr) || 0) {
-
- struct mxl111sf_state *state = d->priv;
- static u8 eeprom[256];
- struct i2c_client c;
- int ret;
-
- ret = get_chip_info(state);
- if (mxl_fail(ret))
- err("failed to get chip info during probe");
-
- mutex_init(&state->fe_lock);
-
- if (state->chip_rev > MXL111SF_V6)
- mxl111sf_config_pin_mux_modes(state,
- PIN_MUX_TS_SPI_IN_MODE_1);
-
- c.adapter = &d->i2c_adap;
- c.addr = 0xa0 >> 1;
-
- ret = tveeprom_read(&c, eeprom, sizeof(eeprom));
- if (mxl_fail(ret))
- return 0;
- tveeprom_hauppauge_analog(&c, &state->tv,
- (0x84 == eeprom[0xa0]) ?
- eeprom + 0xa0 : eeprom + 0x80);
-#if 0
- switch (state->tv.model) {
- case 117001:
- case 126001:
- case 138001:
- break;
- default:
- printk(KERN_WARNING "%s: warning: "
- "unknown hauppauge model #%d\n",
- __func__, state->tv.model);
- }
-#endif
- return 0;
- }
- err("Your device is not yet supported by this driver. "
- "See kernellabs.com for more info");
- return -EINVAL;
-}
-
-static struct usb_device_id mxl111sf_table[] = {
-/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */
-/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */
-/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */
-/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */
-/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */
-/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */
-/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */
- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */
- {} /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, mxl111sf_table);
-
-
-#define MXL111SF_EP4_BULK_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
- .stream = { \
- .type = USB_BULK, \
- .count = 5, \
- .endpoint = 0x04, \
- .u = { \
- .bulk = { \
- .buffersize = 8192, \
- } \
- } \
- }
-
-/* FIXME: works for v6 but not v8 silicon */
-#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \
- .stream = { \
- .type = USB_ISOC, \
- .count = 5, \
- .endpoint = 0x04, \
- .u = { \
- .isoc = { \
- .framesperurb = 96, \
- /* FIXME: v6 SILICON: */ \
- .framesize = 564, \
- .interval = 1, \
- } \
- } \
- }
-
-#define MXL111SF_EP5_BULK_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \
- .stream = { \
- .type = USB_BULK, \
- .count = 5, \
- .endpoint = 0x05, \
- .u = { \
- .bulk = { \
- .buffersize = 8192, \
- } \
- } \
- }
-
-#define MXL111SF_EP5_ISOC_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \
- .stream = { \
- .type = USB_ISOC, \
- .count = 5, \
- .endpoint = 0x05, \
- .u = { \
- .isoc = { \
- .framesperurb = 96, \
- .framesize = 200, \
- .interval = 1, \
- } \
- } \
- }
-
-#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
- .stream = { \
- .type = USB_BULK, \
- .count = 5, \
- .endpoint = 0x06, \
- .u = { \
- .bulk = { \
- .buffersize = 8192, \
- } \
- } \
- }
-
-/* FIXME */
-#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \
- .size_of_priv = sizeof(struct mxl111sf_adap_state), \
- .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
- .stream = { \
- .type = USB_ISOC, \
- .count = 5, \
- .endpoint = 0x06, \
- .u = { \
- .isoc = { \
- .framesperurb = 24, \
- .framesize = 3072, \
- .interval = 1, \
- } \
- } \
- }
-
-#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \
- .caps = DVB_USB_IS_AN_I2C_ADAPTER, \
- .usb_ctrl = DEVICE_SPECIFIC, \
- /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \
- EP6 BULK transfer (atsc/qam), \
- use usb alt setting 2 for EP4 BULK transfer (dvb-t), \
- EP6 ISOC transfer (atsc/qam), \
- */ \
- .power_ctrl = mxl111sf_power_ctrl, \
- .i2c_algo = &mxl111sf_i2c_algo, \
- .generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \
- .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \
- .size_of_priv = sizeof(struct mxl111sf_state)
-
-static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- } },
- },
- },
- .num_device_descs = 3,
- .devices = {
- { "Hauppauge 126xxx DVBT (bulk)",
- { NULL },
- { &mxl111sf_table[4], &mxl111sf_table[8],
- NULL },
- },
- { "Hauppauge 117xxx DVBT (bulk)",
- { NULL },
- { &mxl111sf_table[15], &mxl111sf_table[18],
- NULL },
- },
- { "Hauppauge 138xxx DVBT (bulk)",
- { NULL },
- { &mxl111sf_table[20], &mxl111sf_table[22],
- &mxl111sf_table[24], &mxl111sf_table[26],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- } },
- },
- },
- .num_device_descs = 3,
- .devices = {
- { "Hauppauge 126xxx DVBT (isoc)",
- { NULL },
- { &mxl111sf_table[4], &mxl111sf_table[8],
- NULL },
- },
- { "Hauppauge 117xxx DVBT (isoc)",
- { NULL },
- { &mxl111sf_table[15], &mxl111sf_table[18],
- NULL },
- },
- { "Hauppauge 138xxx DVBT (isoc)",
- { NULL },
- { &mxl111sf_table[20], &mxl111sf_table[22],
- &mxl111sf_table[24], &mxl111sf_table[26],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge 126xxx ATSC (bulk)",
- { NULL },
- { &mxl111sf_table[1], &mxl111sf_table[5],
- NULL },
- },
- { "Hauppauge 117xxx ATSC (bulk)",
- { NULL },
- { &mxl111sf_table[12],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge 126xxx ATSC (isoc)",
- { NULL },
- { &mxl111sf_table[1], &mxl111sf_table[5],
- NULL },
- },
- { "Hauppauge 117xxx ATSC (isoc)",
- { NULL },
- { &mxl111sf_table[12],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2160_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "HCW 126xxx (bulk)",
- { NULL },
- { &mxl111sf_table[2], &mxl111sf_table[6],
- NULL },
- },
- { "HCW 117xxx (bulk)",
- { NULL },
- { &mxl111sf_table[13],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2160_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "HCW 126xxx (isoc)",
- { NULL },
- { &mxl111sf_table[2], &mxl111sf_table[6],
- NULL },
- },
- { "HCW 117xxx (isoc)",
- { NULL },
- { &mxl111sf_table[13],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2160_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge 126xxx ATSC+ (bulk)",
- { NULL },
- { &mxl111sf_table[0], &mxl111sf_table[3],
- &mxl111sf_table[7], &mxl111sf_table[9],
- &mxl111sf_table[10], NULL },
- },
- { "Hauppauge 117xxx ATSC+ (bulk)",
- { NULL },
- { &mxl111sf_table[11], &mxl111sf_table[14],
- &mxl111sf_table[16], &mxl111sf_table[17],
- &mxl111sf_table[32], &mxl111sf_table[33],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2160_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge 126xxx ATSC+ (isoc)",
- { NULL },
- { &mxl111sf_table[0], &mxl111sf_table[3],
- &mxl111sf_table[7], &mxl111sf_table[9],
- &mxl111sf_table[10], NULL },
- },
- { "Hauppauge 117xxx ATSC+ (isoc)",
- { NULL },
- { &mxl111sf_table[11], &mxl111sf_table[14],
- &mxl111sf_table[16], &mxl111sf_table[17],
- &mxl111sf_table[32], &mxl111sf_table[33],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge Mercury (spi-bulk)",
- { NULL },
- { &mxl111sf_table[19], &mxl111sf_table[21],
- &mxl111sf_table[23], &mxl111sf_table[25],
- NULL },
- },
- { "Hauppauge WinTV-Aero-M (spi-bulk)",
- { NULL },
- { &mxl111sf_table[29], &mxl111sf_table[31],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge Mercury (spi-isoc)",
- { NULL },
- { &mxl111sf_table[19], &mxl111sf_table[21],
- &mxl111sf_table[23], &mxl111sf_table[25],
- NULL },
- },
- { "Hauppauge WinTV-Aero-M (spi-isoc)",
- { NULL },
- { &mxl111sf_table[29], &mxl111sf_table[31],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge Mercury (tp-bulk)",
- { NULL },
- { &mxl111sf_table[19], &mxl111sf_table[21],
- &mxl111sf_table[23], &mxl111sf_table[25],
- &mxl111sf_table[27], NULL },
- },
- { "Hauppauge WinTV-Aero-M",
- { NULL },
- { &mxl111sf_table[29], &mxl111sf_table[31],
- NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 3,
- .fe = {{
- .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- },
- {
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 2,
- .devices = {
- { "Hauppauge Mercury (tp-isoc)",
- { NULL },
- { &mxl111sf_table[19], &mxl111sf_table[21],
- &mxl111sf_table[23], &mxl111sf_table[25],
- &mxl111sf_table[27], NULL },
- },
- { "Hauppauge WinTV-Aero-M (tp-isoc)",
- { NULL },
- { &mxl111sf_table[29], &mxl111sf_table[31],
- NULL },
- },
- }
-};
-
-static
-struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 1,
- .devices = {
- { "Hauppauge 126xxx (tp-bulk)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
- }
-};
-
-static
-struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP6_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 1,
- .devices = {
- { "Hauppauge 126xxx (tp-isoc)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
- }
-};
-
-static
-struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_BULK_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 1,
- .devices = {
- { "Hauppauge 126xxx (spi-bulk)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
- }
-};
-
-static
-struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties = {
- MXL111SF_DEFAULT_DEVICE_PROPERTIES,
-
- .num_adapters = 1,
- .adapter = {
- {
- .fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
- .fe = {{
- .frontend_attach = mxl111sf_attach_demod,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP4_ISOC_STREAMING_CONFIG,
- },
- {
- .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
-
- .frontend_attach = mxl111sf_lg2161_frontend_attach,
- .tuner_attach = mxl111sf_attach_tuner,
-
- MXL111SF_EP5_ISOC_STREAMING_CONFIG,
- }},
- },
- },
- .num_device_descs = 1,
- .devices = {
- { "Hauppauge 126xxx (spi-isoc)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
- }
-};
-
-static struct usb_driver mxl111sf_driver = {
- .name = "dvb_usb_mxl111sf",
- .probe = mxl111sf_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = mxl111sf_table,
-};
-
-module_usb_driver(mxl111sf_driver);
-
-MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
-MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
-MODULE_VERSION("1.0");
-MODULE_LICENSE("GPL");
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h
deleted file mode 100644
index 364d89f826bd..000000000000
--- a/drivers/media/dvb/dvb-usb/mxl111sf.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, version 2.
- *
- * see Documentation/dvb/README.dvb-usb for more information
- */
-
-#ifndef _DVB_USB_MXL111SF_H_
-#define _DVB_USB_MXL111SF_H_
-
-#ifdef DVB_USB_LOG_PREFIX
-#undef DVB_USB_LOG_PREFIX
-#endif
-#define DVB_USB_LOG_PREFIX "mxl111sf"
-#include "dvb-usb.h"
-#include <media/tveeprom.h>
-
-#define MXL_EP1_REG_READ 1
-#define MXL_EP2_REG_WRITE 2
-#define MXL_EP3_INTERRUPT 3
-#define MXL_EP4_MPEG2 4
-#define MXL_EP5_I2S 5
-#define MXL_EP6_656 6
-#define MXL_EP6_MPEG2 6
-
-#ifdef USING_ENUM_mxl111sf_current_mode
-enum mxl111sf_current_mode {
- mxl_mode_dvbt = MXL_EP4_MPEG2,
- mxl_mode_mh = MXL_EP5_I2S,
- mxl_mode_atsc = MXL_EP6_MPEG2,
-};
-#endif
-
-enum mxl111sf_gpio_port_expander {
- mxl111sf_gpio_hw,
- mxl111sf_PCA9534,
-};
-
-struct mxl111sf_state {
- struct dvb_usb_device *d;
-
- enum mxl111sf_gpio_port_expander gpio_port_expander;
- u8 port_expander_addr;
-
- u8 chip_id;
- u8 chip_ver;
-#define MXL111SF_V6 1
-#define MXL111SF_V8_100 2
-#define MXL111SF_V8_200 3
- u8 chip_rev;
-
-#ifdef USING_ENUM_mxl111sf_current_mode
- enum mxl111sf_current_mode current_mode;
-#endif
-
-#define MXL_TUNER_MODE 0
-#define MXL_SOC_MODE 1
-#define MXL_DEV_MODE_MASK 0x01
-#if 1
- int device_mode;
-#endif
- /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
- EP5 BULK transfer (atsc-mh),
- EP6 BULK transfer (atsc/qam),
- use usb alt setting 2 for EP4 BULK transfer (dvb-t),
- EP5 ISOC transfer (atsc-mh),
- EP6 ISOC transfer (atsc/qam),
- */
- int alt_mode;
- int gpio_mode;
- struct tveeprom tv;
-
- struct mutex fe_lock;
-};
-
-struct mxl111sf_adap_state {
- int alt_mode;
- int gpio_mode;
- int device_mode;
- int ep6_clockphase;
- int (*fe_init)(struct dvb_frontend *);
- int (*fe_sleep)(struct dvb_frontend *);
-};
-
-int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
-int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
-
-struct mxl111sf_reg_ctrl_info {
- u8 addr;
- u8 mask;
- u8 data;
-};
-
-int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
- u8 addr, u8 mask, u8 data);
-int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
- struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
-
-/* needed for hardware i2c functions in mxl111sf-i2c.c:
- * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
-int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
-
-#define mxl_printk(kern, fmt, arg...) \
- printk(kern "%s: " fmt "\n", __func__, ##arg)
-
-#define mxl_info(fmt, arg...) \
- mxl_printk(KERN_INFO, fmt, ##arg)
-
-extern int dvb_usb_mxl111sf_debug;
-#define mxl_debug(fmt, arg...) \
- if (dvb_usb_mxl111sf_debug) \
- mxl_printk(KERN_DEBUG, fmt, ##arg)
-
-#define MXL_I2C_DBG 0x04
-#define MXL_ADV_DBG 0x10
-#define mxl_debug_adv(fmt, arg...) \
- if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
- mxl_printk(KERN_DEBUG, fmt, ##arg)
-
-#define mxl_i2c(fmt, arg...) \
- if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
- mxl_printk(KERN_DEBUG, fmt, ##arg)
-
-#define mxl_i2c_adv(fmt, arg...) \
- if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
- (MXL_I2C_DBG | MXL_ADV_DBG)) \
- mxl_printk(KERN_DEBUG, fmt, ##arg)
-
-/* The following allows the mxl_fail() macro defined below to work
- * in externel modules, such as mxl111sf-tuner.ko, even though
- * dvb_usb_mxl111sf_debug is not defined within those modules */
-#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
-#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
-#else
-#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
-#endif
-
-#define mxl_fail(ret) \
-({ \
- int __ret; \
- __ret = (ret < 0); \
- if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
- mxl_printk(KERN_ERR, "error %d on line %d", \
- ret, __LINE__); \
- __ret; \
-})
-
-#endif /* _DVB_USB_MXL111SF_H_ */
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/dvb/dvb-usb/pctv452e.c b/drivers/media/dvb/dvb-usb/pctv452e.c
deleted file mode 100644
index f526eb05cc7a..000000000000
--- a/drivers/media/dvb/dvb-usb/pctv452e.c
+++ /dev/null
@@ -1,1064 +0,0 @@
-/*
- * PCTV 452e DVB driver
- *
- * Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net>
- *
- * TT connect S2-3650-CI Common Interface support, MAC readout
- * Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- */
-
-/* dvb usb framework */
-#define DVB_USB_LOG_PREFIX "pctv452e"
-#include "dvb-usb.h"
-
-/* Demodulator */
-#include "stb0899_drv.h"
-#include "stb0899_reg.h"
-#include "stb0899_cfg.h"
-/* Tuner */
-#include "stb6100.h"
-#include "stb6100_cfg.h"
-/* FE Power */
-#include "lnbp22.h"
-
-#include "dvb_ca_en50221.h"
-#include "ttpci-eeprom.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define ISOC_INTERFACE_ALTERNATIVE 3
-
-#define SYNC_BYTE_OUT 0xaa
-#define SYNC_BYTE_IN 0x55
-
-/* guessed: (copied from ttusb-budget) */
-#define PCTV_CMD_RESET 0x15
-/* command to poll IR receiver */
-#define PCTV_CMD_IR 0x1b
-/* command to send I2C */
-#define PCTV_CMD_I2C 0x31
-
-#define I2C_ADDR_STB0899 (0xd0 >> 1)
-#define I2C_ADDR_STB6100 (0xc0 >> 1)
-#define I2C_ADDR_LNBP22 (0x10 >> 1)
-#define I2C_ADDR_24C16 (0xa0 >> 1)
-#define I2C_ADDR_24C64 (0xa2 >> 1)
-
-
-/* pctv452e sends us this amount of data for each issued usb-command */
-#define PCTV_ANSWER_LEN 64
-/* Wait up to 1000ms for device */
-#define PCTV_TIMEOUT 1000
-
-
-#define PCTV_LED_GPIO STB0899_GPIO01
-#define PCTV_LED_GREEN 0x82
-#define PCTV_LED_ORANGE 0x02
-
-#define ci_dbg(format, arg...) \
-do { \
- if (0) \
- printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
- ": " format "\n" , ## arg); \
-} while (0)
-
-enum {
- TT3650_CMD_CI_TEST = 0x40,
- TT3650_CMD_CI_RD_CTRL,
- TT3650_CMD_CI_WR_CTRL,
- TT3650_CMD_CI_RD_ATTR,
- TT3650_CMD_CI_WR_ATTR,
- TT3650_CMD_CI_RESET,
- TT3650_CMD_CI_SET_VIDEO_PORT
-};
-
-
-static struct stb0899_postproc pctv45e_postproc[] = {
- { PCTV_LED_GPIO, STB0899_GPIOPULLUP },
- { 0, 0 }
-};
-
-/*
- * stores all private variables for communication with the PCTV452e DVB-S2
- */
-struct pctv452e_state {
- struct dvb_ca_en50221 ca;
- struct mutex ca_mutex;
-
- u8 c; /* transaction counter, wraps around... */
- u8 initialized; /* set to 1 if 0x15 has been sent */
- u16 last_rc_key;
-};
-
-static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
- unsigned int write_len, unsigned int read_len)
-{
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 buf[64];
- u8 id;
- unsigned int rlen;
- int ret;
-
- BUG_ON(NULL == data && 0 != (write_len | read_len));
- BUG_ON(write_len > 64 - 4);
- BUG_ON(read_len > 64 - 4);
-
- id = state->c++;
-
- buf[0] = SYNC_BYTE_OUT;
- buf[1] = id;
- buf[2] = cmd;
- buf[3] = write_len;
-
- memcpy(buf + 4, data, write_len);
-
- rlen = (read_len > 0) ? 64 : 0;
- ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
- buf, rlen, /* delay_ms */ 0);
- if (0 != ret)
- goto failed;
-
- ret = -EIO;
- if (SYNC_BYTE_IN != buf[0] || id != buf[1])
- goto failed;
-
- memcpy(data, buf + 4, read_len);
-
- return 0;
-
-failed:
- err("CI error %d; %02X %02X %02X -> %02X %02X %02X.",
- ret, SYNC_BYTE_OUT, id, cmd, buf[0], buf[1], buf[2]);
-
- return ret;
-}
-
-static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca,
- u8 cmd, u8 *data, unsigned int write_len,
- unsigned int read_len)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- int ret;
-
- mutex_lock(&state->ca_mutex);
- ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
- mutex_unlock(&state->ca_mutex);
-
- return ret;
-}
-
-static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot, int address)
-{
- u8 buf[3];
- int ret;
-
- if (0 != slot)
- return -EINVAL;
-
- buf[0] = (address >> 8) & 0x0F;
- buf[1] = address;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
-
- ci_dbg("%s %04x -> %d 0x%02x",
- __func__, address, ret, buf[2]);
-
- if (ret < 0)
- return ret;
-
- return buf[2];
-}
-
-static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
- int slot, int address, u8 value)
-{
- u8 buf[3];
-
- ci_dbg("%s %d 0x%04x 0x%02x",
- __func__, slot, address, value);
-
- if (0 != slot)
- return -EINVAL;
-
- buf[0] = (address >> 8) & 0x0F;
- buf[1] = address;
- buf[2] = value;
-
- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
-}
-
-static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca,
- int slot,
- u8 address)
-{
- u8 buf[2];
- int ret;
-
- if (0 != slot)
- return -EINVAL;
-
- buf[0] = address & 3;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
-
- ci_dbg("%s 0x%02x -> %d 0x%02x",
- __func__, address, ret, buf[1]);
-
- if (ret < 0)
- return ret;
-
- return buf[1];
-}
-
-static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca,
- int slot,
- u8 address,
- u8 value)
-{
- u8 buf[2];
-
- ci_dbg("%s %d 0x%02x 0x%02x",
- __func__, slot, address, value);
-
- if (0 != slot)
- return -EINVAL;
-
- buf[0] = address;
- buf[1] = value;
-
- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
-}
-
-static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca,
- int slot,
- int enable)
-{
- u8 buf[1];
- int ret;
-
- ci_dbg("%s %d %d", __func__, slot, enable);
-
- if (0 != slot)
- return -EINVAL;
-
- enable = !!enable;
- buf[0] = enable;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
- if (ret < 0)
- return ret;
-
- if (enable != buf[0]) {
- err("CI not %sabled.", enable ? "en" : "dis");
- return -EIO;
- }
-
- return 0;
-}
-
-static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
-{
- return tt3650_ci_set_video_port(ca, slot, /* enable */ 0);
-}
-
-static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
-{
- return tt3650_ci_set_video_port(ca, slot, /* enable */ 1);
-}
-
-static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
-{
- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 buf[1];
- int ret;
-
- ci_dbg("%s %d", __func__, slot);
-
- if (0 != slot)
- return -EINVAL;
-
- buf[0] = 0;
-
- mutex_lock(&state->ca_mutex);
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
- if (0 != ret)
- goto failed;
-
- msleep(500);
-
- buf[0] = 1;
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
- if (0 != ret)
- goto failed;
-
- msleep(500);
-
- buf[0] = 0; /* FTA */
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
-
- failed:
- mutex_unlock(&state->ca_mutex);
-
- return ret;
-}
-
-static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca,
- int slot,
- int open)
-{
- u8 buf[1];
- int ret;
-
- if (0 != slot)
- return -EINVAL;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
- if (0 != ret)
- return ret;
-
- if (1 == buf[0])
- return DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY;
-
- return 0;
-
-}
-
-static void tt3650_ci_uninit(struct dvb_usb_device *d)
-{
- struct pctv452e_state *state;
-
- ci_dbg("%s", __func__);
-
- if (NULL == d)
- return;
-
- state = (struct pctv452e_state *)d->priv;
- if (NULL == state)
- return;
-
- if (NULL == state->ca.data)
- return;
-
- /* Error ignored. */
- tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
-
- dvb_ca_en50221_release(&state->ca);
-
- memset(&state->ca, 0, sizeof(state->ca));
-}
-
-static int tt3650_ci_init(struct dvb_usb_adapter *a)
-{
- struct dvb_usb_device *d = a->dev;
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- int ret;
-
- ci_dbg("%s", __func__);
-
- mutex_init(&state->ca_mutex);
-
- state->ca.owner = THIS_MODULE;
- state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
- state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
- state->ca.read_cam_control = tt3650_ci_read_cam_control;
- state->ca.write_cam_control = tt3650_ci_write_cam_control;
- state->ca.slot_reset = tt3650_ci_slot_reset;
- state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
- state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
- state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
- state->ca.data = d;
-
- ret = dvb_ca_en50221_init(&a->dvb_adap,
- &state->ca,
- /* flags */ 0,
- /* n_slots */ 1);
- if (0 != ret) {
- err("Cannot initialize CI: Error %d.", ret);
- memset(&state->ca, 0, sizeof(state->ca));
- return ret;
- }
-
- info("CI initialized.");
-
- return 0;
-}
-
-#define CMD_BUFFER_SIZE 0x28
-static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
- const u8 *snd_buf, u8 snd_len,
- u8 *rcv_buf, u8 rcv_len)
-{
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 buf[64];
- u8 id;
- int ret;
-
- id = state->c++;
-
- ret = -EINVAL;
- if (snd_len > 64 - 7 || rcv_len > 64 - 7)
- goto failed;
-
- buf[0] = SYNC_BYTE_OUT;
- buf[1] = id;
- buf[2] = PCTV_CMD_I2C;
- buf[3] = snd_len + 3;
- buf[4] = addr << 1;
- buf[5] = snd_len;
- buf[6] = rcv_len;
-
- memcpy(buf + 7, snd_buf, snd_len);
-
- ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
- buf, /* rcv_len */ 64,
- /* delay_ms */ 0);
- if (ret < 0)
- goto failed;
-
- /* TT USB protocol error. */
- ret = -EIO;
- if (SYNC_BYTE_IN != buf[0] || id != buf[1])
- goto failed;
-
- /* I2C device didn't respond as expected. */
- ret = -EREMOTEIO;
- if (buf[5] < snd_len || buf[6] < rcv_len)
- goto failed;
-
- memcpy(rcv_buf, buf + 7, rcv_len);
-
- return rcv_len;
-
-failed:
- err("I2C error %d; %02X %02X %02X %02X %02X -> "
- "%02X %02X %02X %02X %02X.",
- ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
- buf[0], buf[1], buf[4], buf[5], buf[6]);
-
- return ret;
-}
-
-static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
- int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adapter);
- int i;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- for (i = 0; i < num; i++) {
- u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
- int ret;
-
- if (msg[i].flags & I2C_M_RD) {
- addr = msg[i].addr;
- snd_buf = NULL;
- snd_len = 0;
- rcv_buf = msg[i].buf;
- rcv_len = msg[i].len;
- } else {
- addr = msg[i].addr;
- snd_buf = msg[i].buf;
- snd_len = msg[i].len;
- rcv_buf = NULL;
- rcv_len = 0;
- }
-
- ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf,
- rcv_len);
- if (ret < rcv_len)
- break;
- }
-
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-static u32 pctv452e_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
-{
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 };
- u8 rx[PCTV_ANSWER_LEN];
- int ret;
-
- info("%s: %d\n", __func__, i);
-
- if (!i)
- return 0;
-
- if (state->initialized)
- return 0;
-
- /* hmm where shoud this should go? */
- ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
- if (ret != 0)
- info("%s: Warning set interface returned: %d\n",
- __func__, ret);
-
- /* this is a one-time initialization, dont know where to put */
- b0[1] = state->c++;
- /* reset board */
- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
- if (ret)
- return ret;
-
- b0[1] = state->c++;
- b0[4] = 1;
- /* reset board (again?) */
- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
- if (ret)
- return ret;
-
- state->initialized = 1;
-
- return 0;
-}
-
-static int pctv452e_rc_query(struct dvb_usb_device *d)
-{
- struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
- u8 b[CMD_BUFFER_SIZE];
- u8 rx[PCTV_ANSWER_LEN];
- int ret, i;
- u8 id = state->c++;
-
- /* prepare command header */
- b[0] = SYNC_BYTE_OUT;
- b[1] = id;
- b[2] = PCTV_CMD_IR;
- b[3] = 0;
-
- /* send ir request */
- ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
- if (ret != 0)
- return ret;
-
- if (debug > 3) {
- info("%s: read: %2d: %02x %02x %02x: ", __func__,
- ret, rx[0], rx[1], rx[2]);
- for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
- info(" %02x", rx[i+3]);
-
- info("\n");
- }
-
- if ((rx[3] == 9) && (rx[12] & 0x01)) {
- /* got a "press" event */
- state->last_rc_key = (rx[7] << 8) | rx[6];
- if (debug > 2)
- info("%s: cmd=0x%02x sys=0x%02x\n",
- __func__, rx[6], rx[7]);
-
- rc_keydown(d->rc_dev, state->last_rc_key, 0);
- } else if (state->last_rc_key) {
- rc_keyup(d->rc_dev);
- state->last_rc_key = 0;
- }
-
- return 0;
-}
-
-static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
-{
- const u8 mem_addr[] = { 0x1f, 0xcc };
- u8 encoded_mac[20];
- int ret;
-
- ret = -EAGAIN;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- goto failed;
-
- ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16,
- mem_addr + 1, /* snd_len */ 1,
- encoded_mac, /* rcv_len */ 20);
- if (-EREMOTEIO == ret)
- /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a
- byte write if /WC is low. */
- ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64,
- mem_addr, 2,
- encoded_mac, 20);
-
- mutex_unlock(&d->i2c_mutex);
-
- if (20 != ret)
- goto failed;
-
- ret = ttpci_eeprom_decode_mac(mac, encoded_mac);
- if (0 != ret)
- goto failed;
-
- return 0;
-
-failed:
- memset(mac, 0, 6);
-
- return ret;
-}
-
-static const struct stb0899_s1_reg pctv452e_init_dev[] = {
- { STB0899_DISCNTRL1, 0x26 },
- { STB0899_DISCNTRL2, 0x80 },
- { STB0899_DISRX_ST0, 0x04 },
- { STB0899_DISRX_ST1, 0x20 },
- { STB0899_DISPARITY, 0x00 },
- { STB0899_DISFIFO, 0x00 },
- { STB0899_DISF22, 0x99 },
- { STB0899_DISF22RX, 0x85 }, /* 0xa8 */
- { STB0899_ACRPRESC, 0x11 },
- { STB0899_ACRDIV1, 0x0a },
- { STB0899_ACRDIV2, 0x05 },
- { STB0899_DACR1 , 0x00 },
- { STB0899_DACR2 , 0x00 },
- { STB0899_OUTCFG, 0x00 },
- { STB0899_MODECFG, 0x00 }, /* Inversion */
- { STB0899_IRQMSK_3, 0xf3 },
- { STB0899_IRQMSK_2, 0xfc },
- { STB0899_IRQMSK_1, 0xff },
- { STB0899_IRQMSK_0, 0xff },
- { STB0899_I2CCFG, 0x88 },
- { STB0899_I2CRPT, 0x58 },
- { STB0899_GPIO00CFG, 0x82 },
- { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */
- { STB0899_GPIO02CFG, 0x82 },
- { STB0899_GPIO03CFG, 0x82 },
- { STB0899_GPIO04CFG, 0x82 },
- { STB0899_GPIO05CFG, 0x82 },
- { STB0899_GPIO06CFG, 0x82 },
- { STB0899_GPIO07CFG, 0x82 },
- { STB0899_GPIO08CFG, 0x82 },
- { STB0899_GPIO09CFG, 0x82 },
- { STB0899_GPIO10CFG, 0x82 },
- { STB0899_GPIO11CFG, 0x82 },
- { STB0899_GPIO12CFG, 0x82 },
- { STB0899_GPIO13CFG, 0x82 },
- { STB0899_GPIO14CFG, 0x82 },
- { STB0899_GPIO15CFG, 0x82 },
- { STB0899_GPIO16CFG, 0x82 },
- { STB0899_GPIO17CFG, 0x82 },
- { STB0899_GPIO18CFG, 0x82 },
- { STB0899_GPIO19CFG, 0x82 },
- { STB0899_GPIO20CFG, 0x82 },
- { STB0899_SDATCFG, 0xb8 },
- { STB0899_SCLTCFG, 0xba },
- { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */
- { STB0899_GPIO22, 0x82 },
- { STB0899_GPIO21, 0x91 },
- { STB0899_DIRCLKCFG, 0x82 },
- { STB0899_CLKOUT27CFG, 0x7e },
- { STB0899_STDBYCFG, 0x82 },
- { STB0899_CS0CFG, 0x82 },
- { STB0899_CS1CFG, 0x82 },
- { STB0899_DISEQCOCFG, 0x20 },
- { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */
- { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */
- { STB0899_FILTCTRL, 0x00 },
- { STB0899_SYSCTRL, 0x00 },
- { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */
- { STB0899_STOPCLK2, 0x00 },
- { STB0899_INTBUFCTRL, 0x0a },
- { STB0899_AGC2I1, 0x00 },
- { STB0899_AGC2I2, 0x00 },
- { STB0899_AGCIQIN, 0x00 },
- { STB0899_TSTRES, 0x40 }, /* rjkm */
- { 0xffff, 0xff },
-};
-
-static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = {
- { STB0899_DEMOD, 0x00 },
- { STB0899_RCOMPC, 0xc9 },
- { STB0899_AGC1CN, 0x01 },
- { STB0899_AGC1REF, 0x10 },
- { STB0899_RTC, 0x23 },
- { STB0899_TMGCFG, 0x4e },
- { STB0899_AGC2REF, 0x34 },
- { STB0899_TLSR, 0x84 },
- { STB0899_CFD, 0xf7 },
- { STB0899_ACLC, 0x87 },
- { STB0899_BCLC, 0x94 },
- { STB0899_EQON, 0x41 },
- { STB0899_LDT, 0xf1 },
- { STB0899_LDT2, 0xe3 },
- { STB0899_EQUALREF, 0xb4 },
- { STB0899_TMGRAMP, 0x10 },
- { STB0899_TMGTHD, 0x30 },
- { STB0899_IDCCOMP, 0xfd },
- { STB0899_QDCCOMP, 0xff },
- { STB0899_POWERI, 0x0c },
- { STB0899_POWERQ, 0x0f },
- { STB0899_RCOMP, 0x6c },
- { STB0899_AGCIQIN, 0x80 },
- { STB0899_AGC2I1, 0x06 },
- { STB0899_AGC2I2, 0x00 },
- { STB0899_TLIR, 0x30 },
- { STB0899_RTF, 0x7f },
- { STB0899_DSTATUS, 0x00 },
- { STB0899_LDI, 0xbc },
- { STB0899_CFRM, 0xea },
- { STB0899_CFRL, 0x31 },
- { STB0899_NIRM, 0x2b },
- { STB0899_NIRL, 0x80 },
- { STB0899_ISYMB, 0x1d },
- { STB0899_QSYMB, 0xa6 },
- { STB0899_SFRH, 0x2f },
- { STB0899_SFRM, 0x68 },
- { STB0899_SFRL, 0x40 },
- { STB0899_SFRUPH, 0x2f },
- { STB0899_SFRUPM, 0x68 },
- { STB0899_SFRUPL, 0x40 },
- { STB0899_EQUAI1, 0x02 },
- { STB0899_EQUAQ1, 0xff },
- { STB0899_EQUAI2, 0x04 },
- { STB0899_EQUAQ2, 0x05 },
- { STB0899_EQUAI3, 0x02 },
- { STB0899_EQUAQ3, 0xfd },
- { STB0899_EQUAI4, 0x03 },
- { STB0899_EQUAQ4, 0x07 },
- { STB0899_EQUAI5, 0x08 },
- { STB0899_EQUAQ5, 0xf5 },
- { STB0899_DSTATUS2, 0x00 },
- { STB0899_VSTATUS, 0x00 },
- { STB0899_VERROR, 0x86 },
- { STB0899_IQSWAP, 0x2a },
- { STB0899_ECNT1M, 0x00 },
- { STB0899_ECNT1L, 0x00 },
- { STB0899_ECNT2M, 0x00 },
- { STB0899_ECNT2L, 0x00 },
- { STB0899_ECNT3M, 0x0a },
- { STB0899_ECNT3L, 0xad },
- { STB0899_FECAUTO1, 0x06 },
- { STB0899_FECM, 0x01 },
- { STB0899_VTH12, 0xb0 },
- { STB0899_VTH23, 0x7a },
- { STB0899_VTH34, 0x58 },
- { STB0899_VTH56, 0x38 },
- { STB0899_VTH67, 0x34 },
- { STB0899_VTH78, 0x24 },
- { STB0899_PRVIT, 0xff },
- { STB0899_VITSYNC, 0x19 },
- { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
- { STB0899_TSULC, 0x42 },
- { STB0899_RSLLC, 0x41 },
- { STB0899_TSLPL, 0x12 },
- { STB0899_TSCFGH, 0x0c },
- { STB0899_TSCFGM, 0x00 },
- { STB0899_TSCFGL, 0x00 },
- { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */
- { STB0899_RSSYNCDEL, 0x00 },
- { STB0899_TSINHDELH, 0x02 },
- { STB0899_TSINHDELM, 0x00 },
- { STB0899_TSINHDELL, 0x00 },
- { STB0899_TSLLSTKM, 0x1b },
- { STB0899_TSLLSTKL, 0xb3 },
- { STB0899_TSULSTKM, 0x00 },
- { STB0899_TSULSTKL, 0x00 },
- { STB0899_PCKLENUL, 0xbc },
- { STB0899_PCKLENLL, 0xcc },
- { STB0899_RSPCKLEN, 0xbd },
- { STB0899_TSSTATUS, 0x90 },
- { STB0899_ERRCTRL1, 0xb6 },
- { STB0899_ERRCTRL2, 0x95 },
- { STB0899_ERRCTRL3, 0x8d },
- { STB0899_DMONMSK1, 0x27 },
- { STB0899_DMONMSK0, 0x03 },
- { STB0899_DEMAPVIT, 0x5c },
- { STB0899_PLPARM, 0x19 },
- { STB0899_PDELCTRL, 0x48 },
- { STB0899_PDELCTRL2, 0x00 },
- { STB0899_BBHCTRL1, 0x00 },
- { STB0899_BBHCTRL2, 0x00 },
- { STB0899_HYSTTHRESH, 0x77 },
- { STB0899_MATCSTM, 0x00 },
- { STB0899_MATCSTL, 0x00 },
- { STB0899_UPLCSTM, 0x00 },
- { STB0899_UPLCSTL, 0x00 },
- { STB0899_DFLCSTM, 0x00 },
- { STB0899_DFLCSTL, 0x00 },
- { STB0899_SYNCCST, 0x00 },
- { STB0899_SYNCDCSTM, 0x00 },
- { STB0899_SYNCDCSTL, 0x00 },
- { STB0899_ISI_ENTRY, 0x00 },
- { STB0899_ISI_BIT_EN, 0x00 },
- { STB0899_MATSTRM, 0xf0 },
- { STB0899_MATSTRL, 0x02 },
- { STB0899_UPLSTRM, 0x45 },
- { STB0899_UPLSTRL, 0x60 },
- { STB0899_DFLSTRM, 0xe3 },
- { STB0899_DFLSTRL, 0x00 },
- { STB0899_SYNCSTR, 0x47 },
- { STB0899_SYNCDSTRM, 0x05 },
- { STB0899_SYNCDSTRL, 0x18 },
- { STB0899_CFGPDELSTATUS1, 0x19 },
- { STB0899_CFGPDELSTATUS2, 0x2b },
- { STB0899_BBFERRORM, 0x00 },
- { STB0899_BBFERRORL, 0x01 },
- { STB0899_UPKTERRORM, 0x00 },
- { STB0899_UPKTERRORL, 0x00 },
- { 0xffff, 0xff },
-};
-
-static struct stb0899_config stb0899_config = {
- .init_dev = pctv452e_init_dev,
- .init_s2_demod = stb0899_s2_init_2,
- .init_s1_demod = pctv452e_init_s1_demod,
- .init_s2_fec = stb0899_s2_init_4,
- .init_tst = stb0899_s1_init_5,
-
- .demod_address = I2C_ADDR_STB0899, /* I2C Address */
- .block_sync_mode = STB0899_SYNC_FORCED, /* ? */
-
- .xtal_freq = 27000000, /* Assume Hz ? */
- .inversion = IQ_SWAP_ON, /* ? */
-
- .lo_clk = 76500000,
- .hi_clk = 99000000,
-
- .ts_output_mode = 0, /* Use parallel mode */
- .clock_polarity = 0,
- .data_clk_parity = 0,
- .fec_mode = 0,
-
- .esno_ave = STB0899_DVBS2_ESNO_AVE,
- .esno_quant = STB0899_DVBS2_ESNO_QUANT,
- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
-
- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
-
- .tuner_get_frequency = stb6100_get_frequency,
- .tuner_set_frequency = stb6100_set_frequency,
- .tuner_set_bandwidth = stb6100_set_bandwidth,
- .tuner_get_bandwidth = stb6100_get_bandwidth,
- .tuner_set_rfsiggain = NULL,
-
- /* helper for switching LED green/orange */
- .postproc = pctv45e_postproc
-};
-
-static struct stb6100_config stb6100_config = {
- .tuner_address = I2C_ADDR_STB6100,
- .refclock = 27000000
-};
-
-
-static struct i2c_algorithm pctv452e_i2c_algo = {
- .master_xfer = pctv452e_i2c_xfer,
- .functionality = pctv452e_i2c_func
-};
-
-static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
-{
- struct usb_device_id *id;
-
- a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config,
- &a->dev->i2c_adap);
- if (!a->fe_adap[0].fe)
- return -ENODEV;
- if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
- &a->dev->i2c_adap)) == 0)
- err("Cannot attach lnbp22\n");
-
- id = a->dev->desc->warm_ids[0];
- if (USB_VID_TECHNOTREND == id->idVendor
- && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct)
- /* Error ignored. */
- tt3650_ci_init(a);
-
- return 0;
-}
-
-static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
-{
- if (!a->fe_adap[0].fe)
- return -ENODEV;
- if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
- &a->dev->i2c_adap) == 0) {
- err("%s failed\n", __func__);
- return -ENODEV;
- }
-
- return 0;
-}
-
-static struct usb_device_id pctv452e_usb_table[] = {
- {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)},
- {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)},
- {USB_DEVICE(USB_VID_TECHNOTREND,
- USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, pctv452e_usb_table);
-
-static struct dvb_usb_device_properties pctv452e_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
- .usb_ctrl = DEVICE_SPECIFIC,
-
- .size_of_priv = sizeof(struct pctv452e_state),
-
- .power_ctrl = pctv452e_power_ctrl,
-
- .rc.core = {
- .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
- .allowed_protos = RC_TYPE_UNKNOWN,
- .rc_query = pctv452e_rc_query,
- .rc_interval = 100,
- },
-
- .num_adapters = 1,
- .adapter = {{
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = pctv452e_frontend_attach,
- .tuner_attach = pctv452e_tuner_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 4,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1
- }
- }
- },
- } },
- } },
-
- .i2c_algo = &pctv452e_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */
-
- .num_device_descs = 1,
- .devices = {
- { .name = "PCTV HDTV USB",
- .cold_ids = { NULL, NULL }, /* this is a warm only device */
- .warm_ids = { &pctv452e_usb_table[0], NULL }
- },
- { 0 },
- }
-};
-
-static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
- .usb_ctrl = DEVICE_SPECIFIC,
-
- .size_of_priv = sizeof(struct pctv452e_state),
-
- .power_ctrl = pctv452e_power_ctrl,
- .read_mac_address = pctv452e_read_mac_address,
-
- .rc.core = {
- .rc_codes = RC_MAP_TT_1500,
- .allowed_protos = RC_TYPE_UNKNOWN,
- .rc_query = pctv452e_rc_query,
- .rc_interval = 100,
- },
-
- .num_adapters = 1,
- .adapter = {{
- .num_frontends = 1,
- .fe = {{
- .frontend_attach = pctv452e_frontend_attach,
- .tuner_attach = pctv452e_tuner_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 7,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1
- }
- }
- },
-
- } },
- } },
-
- .i2c_algo = &pctv452e_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/
-
- .num_device_descs = 2,
- .devices = {
- { .name = "Technotrend TT Connect S2-3600",
- .cold_ids = { NULL, NULL }, /* this is a warm only device */
- .warm_ids = { &pctv452e_usb_table[1], NULL }
- },
- { .name = "Technotrend TT Connect S2-3650-CI",
- .cold_ids = { NULL, NULL },
- .warm_ids = { &pctv452e_usb_table[2], NULL }
- },
- { 0 },
- }
-};
-
-static void pctv452e_usb_disconnect(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
-
- tt3650_ci_uninit(d);
- dvb_usb_device_exit(intf);
-}
-
-static int pctv452e_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- if (0 == dvb_usb_device_init(intf, &pctv452e_properties,
- THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties,
- THIS_MODULE, NULL, adapter_nr))
- return 0;
-
- return -ENODEV;
-}
-
-static struct usb_driver pctv452e_usb_driver = {
- .name = "pctv452e",
- .probe = pctv452e_usb_probe,
- .disconnect = pctv452e_usb_disconnect,
- .id_table = pctv452e_usb_table,
-};
-
-module_usb_driver(pctv452e_usb_driver);
-
-MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>");
-MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>");
-MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>");
-MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
deleted file mode 100644
index 6bd0bd792437..000000000000
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.c
+++ /dev/null
@@ -1,1428 +0,0 @@
-/*
- * Realtek RTL28xxU DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Thomas Mair <thomas.mair86@googlemail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "rtl28xxu.h"
-
-#include "rtl2830.h"
-#include "rtl2832.h"
-
-#include "qt1010.h"
-#include "mt2060.h"
-#include "mxl5005s.h"
-#include "fc0012.h"
-#include "fc0013.h"
-
-/* debug */
-static int dvb_usb_rtl28xxu_debug;
-module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
-{
- int ret;
- unsigned int pipe;
- u8 requesttype;
- u8 *buf;
-
- buf = kmalloc(req->size, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto err;
- }
-
- if (req->index & CMD_WR_FLAG) {
- /* write */
- memcpy(buf, req->data, req->size);
- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
- pipe = usb_sndctrlpipe(d->udev, 0);
- } else {
- /* read */
- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
- pipe = usb_rcvctrlpipe(d->udev, 0);
- }
-
- ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
- req->index, buf, req->size, 1000);
- if (ret > 0)
- ret = 0;
-
- deb_dump(0, requesttype, req->value, req->index, buf, req->size,
- deb_xfer);
-
- /* read request, copy returned data to return buf */
- if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
- memcpy(req->data, buf, req->size);
-
- kfree(buf);
-
- if (ret)
- goto err;
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
-{
- struct rtl28xxu_req req;
-
- if (reg < 0x3000)
- req.index = CMD_USB_WR;
- else if (reg < 0x4000)
- req.index = CMD_SYS_WR;
- else
- req.index = CMD_IR_WR;
-
- req.value = reg;
- req.size = len;
- req.data = val;
-
- return rtl28xxu_ctrl_msg(d, &req);
-}
-
-static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
-{
- struct rtl28xxu_req req;
-
- if (reg < 0x3000)
- req.index = CMD_USB_RD;
- else if (reg < 0x4000)
- req.index = CMD_SYS_RD;
- else
- req.index = CMD_IR_RD;
-
- req.value = reg;
- req.size = len;
- req.data = val;
-
- return rtl28xxu_ctrl_msg(d, &req);
-}
-
-static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
-{
- return rtl28xx_wr_regs(d, reg, &val, 1);
-}
-
-static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
-{
- return rtl2831_rd_regs(d, reg, val, 1);
-}
-
-/* I2C */
-static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
- int num)
-{
- int ret;
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- struct rtl28xxu_priv *priv = d->priv;
- struct rtl28xxu_req req;
-
- /*
- * It is not known which are real I2C bus xfer limits, but testing
- * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
- * TODO: find out RTL2832U lens
- */
-
- /*
- * I2C adapter logic looks rather complicated due to fact it handles
- * three different access methods. Those methods are;
- * 1) integrated demod access
- * 2) old I2C access
- * 3) new I2C access
- *
- * Used method is selected in order 1, 2, 3. Method 3 can handle all
- * requests but there is two reasons why not use it always;
- * 1) It is most expensive, usually two USB messages are needed
- * 2) At least RTL2831U does not support it
- *
- * Method 3 is needed in case of I2C write+read (typical register read)
- * where write is more than one byte.
- */
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
- (msg[1].flags & I2C_M_RD)) {
- if (msg[0].len > 24 || msg[1].len > 24) {
- /* TODO: check msg[0].len max */
- ret = -EOPNOTSUPP;
- goto err_mutex_unlock;
- } else if (msg[0].addr == 0x10) {
- /* method 1 - integrated demod */
- req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
- req.index = CMD_DEMOD_RD | priv->page;
- req.size = msg[1].len;
- req.data = &msg[1].buf[0];
- ret = rtl28xxu_ctrl_msg(d, &req);
- } else if (msg[0].len < 2) {
- /* method 2 - old I2C */
- req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
- req.index = CMD_I2C_RD;
- req.size = msg[1].len;
- req.data = &msg[1].buf[0];
- ret = rtl28xxu_ctrl_msg(d, &req);
- } else {
- /* method 3 - new I2C */
- req.value = (msg[0].addr << 1);
- req.index = CMD_I2C_DA_WR;
- req.size = msg[0].len;
- req.data = msg[0].buf;
- ret = rtl28xxu_ctrl_msg(d, &req);
- if (ret)
- goto err_mutex_unlock;
-
- req.value = (msg[0].addr << 1);
- req.index = CMD_I2C_DA_RD;
- req.size = msg[1].len;
- req.data = msg[1].buf;
- ret = rtl28xxu_ctrl_msg(d, &req);
- }
- } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
- if (msg[0].len > 22) {
- /* TODO: check msg[0].len max */
- ret = -EOPNOTSUPP;
- goto err_mutex_unlock;
- } else if (msg[0].addr == 0x10) {
- /* method 1 - integrated demod */
- if (msg[0].buf[0] == 0x00) {
- /* save demod page for later demod access */
- priv->page = msg[0].buf[1];
- ret = 0;
- } else {
- req.value = (msg[0].buf[0] << 8) |
- (msg[0].addr << 1);
- req.index = CMD_DEMOD_WR | priv->page;
- req.size = msg[0].len-1;
- req.data = &msg[0].buf[1];
- ret = rtl28xxu_ctrl_msg(d, &req);
- }
- } else if (msg[0].len < 23) {
- /* method 2 - old I2C */
- req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
- req.index = CMD_I2C_WR;
- req.size = msg[0].len-1;
- req.data = &msg[0].buf[1];
- ret = rtl28xxu_ctrl_msg(d, &req);
- } else {
- /* method 3 - new I2C */
- req.value = (msg[0].addr << 1);
- req.index = CMD_I2C_DA_WR;
- req.size = msg[0].len;
- req.data = msg[0].buf;
- ret = rtl28xxu_ctrl_msg(d, &req);
- }
- } else {
- ret = -EINVAL;
- }
-
-err_mutex_unlock:
- mutex_unlock(&d->i2c_mutex);
-
- return ret ? ret : num;
-}
-
-static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm rtl28xxu_i2c_algo = {
- .master_xfer = rtl28xxu_i2c_xfer,
- .functionality = rtl28xxu_i2c_func,
-};
-
-static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
- .i2c_addr = 0x10, /* 0x20 */
- .xtal = 28800000,
- .ts_mode = 0,
- .spec_inv = 1,
- .if_dvbt = 36150000,
- .vtop = 0x20,
- .krf = 0x04,
- .agc_targ_val = 0x2d,
-
-};
-
-static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
- .i2c_addr = 0x10, /* 0x20 */
- .xtal = 28800000,
- .ts_mode = 0,
- .spec_inv = 1,
- .if_dvbt = 36125000,
- .vtop = 0x20,
- .krf = 0x04,
- .agc_targ_val = 0x2d,
-};
-
-static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
- .i2c_addr = 0x10, /* 0x20 */
- .xtal = 28800000,
- .ts_mode = 0,
- .spec_inv = 0,
- .if_dvbt = 4570000,
- .vtop = 0x3f,
- .krf = 0x04,
- .agc_targ_val = 0x3e,
-};
-
-static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct rtl28xxu_priv *priv = adap->dev->priv;
- u8 buf[1];
- struct rtl2830_config *rtl2830_config;
- /* open RTL2831U/RTL2830 I2C gate */
- struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" };
- /* for MT2060 tuner probe */
- struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf };
- /* for QT1010 tuner probe */
- struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf };
-
- deb_info("%s:\n", __func__);
-
- /*
- * RTL2831U GPIOs
- * =========================================================
- * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
- * GPIO2 | LED | 0 off | 1 on |
- * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
- */
-
- /* GPIO direction */
- ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
- if (ret)
- goto err;
-
- /* enable as output GPIO0, GPIO2, GPIO4 */
- ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
- if (ret)
- goto err;
-
- /*
- * Probe used tuner. We need to know used tuner before demod attach
- * since there is some demod params needed to set according to tuner.
- */
-
- /* demod needs some time to wake up */
- msleep(20);
-
- /* open demod I2C gate */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
- if (ret)
- goto err;
-
- /* check QT1010 ID(?) register; reg=0f val=2c */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
- if (ret == 0 && buf[0] == 0x2c) {
- priv->tuner = TUNER_RTL2830_QT1010;
- rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
- deb_info("%s: QT1010\n", __func__);
- goto found;
- } else {
- deb_info("%s: QT1010 probe failed=%d - %02x\n",
- __func__, ret, buf[0]);
- }
-
- /* open demod I2C gate */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
- if (ret)
- goto err;
-
- /* check MT2060 ID register; reg=00 val=63 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
- if (ret == 0 && buf[0] == 0x63) {
- priv->tuner = TUNER_RTL2830_MT2060;
- rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
- deb_info("%s: MT2060\n", __func__);
- goto found;
- } else {
- deb_info("%s: MT2060 probe failed=%d - %02x\n",
- __func__, ret, buf[0]);
- }
-
- /* assume MXL5005S */
- ret = 0;
- priv->tuner = TUNER_RTL2830_MXL5005S;
- rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
- deb_info("%s: MXL5005S\n", __func__);
- goto found;
-
-found:
- /* attach demodulator */
- adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
- .i2c_addr = 0x10, /* 0x20 */
- .xtal = 28800000,
- .if_dvbt = 0,
- .tuner = TUNER_RTL2832_FC0012
-};
-
-static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
- .i2c_addr = 0x10, /* 0x20 */
- .xtal = 28800000,
- .if_dvbt = 0,
- .tuner = TUNER_RTL2832_FC0013
-};
-
-static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d,
- int cmd, int arg)
-{
- int ret;
- u8 val;
-
- deb_info("%s cmd=%d arg=%d\n", __func__, cmd, arg);
- switch (cmd) {
- case FC_FE_CALLBACK_VHF_ENABLE:
- /* set output values */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
- if (ret)
- goto err;
-
- if (arg)
- val &= 0xbf; /* set GPIO6 low */
- else
- val |= 0x40; /* set GPIO6 high */
-
-
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
- if (ret)
- goto err;
- break;
- default:
- ret = -EINVAL;
- goto err;
- }
- return 0;
-
-err:
- err("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-
-static int rtl2832u_fc0013_tuner_callback(struct dvb_usb_device *d,
- int cmd, int arg)
-{
- /* TODO implement*/
- return 0;
-}
-
-static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
-{
- struct rtl28xxu_priv *priv = d->priv;
-
- switch (priv->tuner) {
- case TUNER_RTL2832_FC0012:
- return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
-
- case TUNER_RTL2832_FC0013:
- return rtl2832u_fc0013_tuner_callback(d, cmd, arg);
- default:
- break;
- }
-
- return -ENODEV;
-}
-
-static int rtl2832u_frontend_callback(void *adapter_priv, int component,
- int cmd, int arg)
-{
- struct i2c_adapter *adap = adapter_priv;
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
-
- switch (component) {
- case DVB_FRONTEND_COMPONENT_TUNER:
- return rtl2832u_tuner_callback(d, cmd, arg);
- default:
- break;
- }
-
- return -EINVAL;
-}
-
-
-
-
-static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct rtl28xxu_priv *priv = adap->dev->priv;
- struct rtl2832_config *rtl2832_config;
-
- u8 buf[2], val;
- /* open RTL2832U/RTL2832 I2C gate */
- struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
- /* close RTL2832U/RTL2832 I2C gate */
- struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
- /* for FC0012 tuner probe */
- struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf};
- /* for FC0013 tuner probe */
- struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf};
- /* for MT2266 tuner probe */
- struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf};
- /* for FC2580 tuner probe */
- struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
- /* for MT2063 tuner probe */
- struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf};
- /* for MAX3543 tuner probe */
- struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf};
- /* for TUA9001 tuner probe */
- struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf};
- /* for MXL5007T tuner probe */
- struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf};
- /* for E4000 tuner probe */
- struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf};
- /* for TDA18272 tuner probe */
- struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
-
- deb_info("%s:\n", __func__);
-
-
- ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_DIR, &val);
- if (ret)
- goto err;
-
- val &= 0xbf;
-
- ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_DIR, val);
- if (ret)
- goto err;
-
-
- /* enable as output GPIO3 and GPIO6*/
- ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_EN, &val);
- if (ret)
- goto err;
-
- val |= 0x48;
-
- ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_EN, val);
- if (ret)
- goto err;
-
-
-
- /*
- * Probe used tuner. We need to know used tuner before demod attach
- * since there is some demod params needed to set according to tuner.
- */
-
- /* open demod I2C gate */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open);
- if (ret)
- goto err;
-
- priv->tuner = TUNER_NONE;
-
- /* check FC0012 ID register; reg=00 val=a1 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0012);
- if (ret == 0 && buf[0] == 0xa1) {
- priv->tuner = TUNER_RTL2832_FC0012;
- rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
- info("%s: FC0012 tuner found", __func__);
- goto found;
- }
-
- /* check FC0013 ID register; reg=00 val=a3 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc0013);
- if (ret == 0 && buf[0] == 0xa3) {
- priv->tuner = TUNER_RTL2832_FC0013;
- rtl2832_config = &rtl28xxu_rtl2832_fc0013_config;
- info("%s: FC0013 tuner found", __func__);
- goto found;
- }
-
- /* check MT2266 ID register; reg=00 val=85 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2266);
- if (ret == 0 && buf[0] == 0x85) {
- priv->tuner = TUNER_RTL2832_MT2266;
- /* TODO implement tuner */
- info("%s: MT2266 tuner found", __func__);
- goto unsupported;
- }
-
- /* check FC2580 ID register; reg=01 val=56 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
- if (ret == 0 && buf[0] == 0x56) {
- priv->tuner = TUNER_RTL2832_FC2580;
- /* TODO implement tuner */
- info("%s: FC2580 tuner found", __func__);
- goto unsupported;
- }
-
- /* check MT2063 ID register; reg=00 val=9e || 9c */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2063);
- if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
- priv->tuner = TUNER_RTL2832_MT2063;
- /* TODO implement tuner */
- info("%s: MT2063 tuner found", __func__);
- goto unsupported;
- }
-
- /* check MAX3543 ID register; reg=00 val=38 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_max3543);
- if (ret == 0 && buf[0] == 0x38) {
- priv->tuner = TUNER_RTL2832_MAX3543;
- /* TODO implement tuner */
- info("%s: MAX3534 tuner found", __func__);
- goto unsupported;
- }
-
- /* check TUA9001 ID register; reg=7e val=2328 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_tua9001);
- if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
- priv->tuner = TUNER_RTL2832_TUA9001;
- /* TODO implement tuner */
- info("%s: TUA9001 tuner found", __func__);
- goto unsupported;
- }
-
- /* check MXL5007R ID register; reg=d9 val=14 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_mxl5007t);
- if (ret == 0 && buf[0] == 0x14) {
- priv->tuner = TUNER_RTL2832_MXL5007T;
- /* TODO implement tuner */
- info("%s: MXL5007T tuner found", __func__);
- goto unsupported;
- }
-
- /* check E4000 ID register; reg=02 val=40 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_e4000);
- if (ret == 0 && buf[0] == 0x40) {
- priv->tuner = TUNER_RTL2832_E4000;
- /* TODO implement tuner */
- info("%s: E4000 tuner found", __func__);
- goto unsupported;
- }
-
- /* check TDA18272 ID register; reg=00 val=c760 */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_tda18272);
- if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
- priv->tuner = TUNER_RTL2832_TDA18272;
- /* TODO implement tuner */
- info("%s: TDA18272 tuner found", __func__);
- goto unsupported;
- }
-
-unsupported:
- /* close demod I2C gate */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
- if (ret)
- goto err;
-
- /* tuner not found */
- deb_info("No compatible tuner found");
- ret = -ENODEV;
- return ret;
-
-found:
- /* close demod I2C gate */
- ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
- if (ret)
- goto err;
-
- /* attach demodulator */
- adap->fe_adap[0].fe = dvb_attach(rtl2832_attach, rtl2832_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- /* set fe callbacks */
- adap->fe_adap[0].fe->callback = rtl2832u_frontend_callback;
-
- return ret;
-
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static struct qt1010_config rtl28xxu_qt1010_config = {
- .i2c_address = 0x62, /* 0xc4 */
-};
-
-static struct mt2060_config rtl28xxu_mt2060_config = {
- .i2c_address = 0x60, /* 0xc0 */
- .clock_out = 0,
-};
-
-static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
- .i2c_address = 0x63, /* 0xc6 */
- .if_freq = IF_FREQ_4570000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_C_H,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .AgcMasterByte = 0x00,
-};
-
-static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct rtl28xxu_priv *priv = adap->dev->priv;
- struct i2c_adapter *rtl2830_tuner_i2c;
- struct dvb_frontend *fe;
-
- deb_info("%s:\n", __func__);
-
- /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
- rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe);
-
- switch (priv->tuner) {
- case TUNER_RTL2830_QT1010:
- fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
- rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
- break;
- case TUNER_RTL2830_MT2060:
- fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
- rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
- 1220);
- break;
- case TUNER_RTL2830_MXL5005S:
- fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
- rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
- break;
- default:
- fe = NULL;
- err("unknown tuner=%d", priv->tuner);
- }
-
- if (fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- return 0;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
-{
- int ret;
- struct rtl28xxu_priv *priv = adap->dev->priv;
- struct dvb_frontend *fe;
-
- deb_info("%s:\n", __func__);
-
- switch (priv->tuner) {
- case TUNER_RTL2832_FC0012:
- fe = dvb_attach(fc0012_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
-
- /* since fc0012 includs reading the signal strength delegate
- * that to the tuner driver */
- adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0].
- fe->ops.tuner_ops.get_rf_strength;
- return 0;
- break;
- case TUNER_RTL2832_FC0013:
- fe = dvb_attach(fc0013_attach, adap->fe_adap[0].fe,
- &adap->dev->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
-
- /* fc0013 also supports signal strength reading */
- adap->fe_adap[0].fe->ops.read_signal_strength = adap->fe_adap[0]
- .fe->ops.tuner_ops.get_rf_strength;
- return 0;
- default:
- fe = NULL;
- err("unknown tuner=%d", priv->tuner);
- }
-
- if (fe == NULL) {
- ret = -ENODEV;
- goto err;
- }
-
- return 0;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2831u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
-{
- int ret;
- u8 buf[2], gpio;
-
- deb_info("%s: onoff=%d\n", __func__, onoff);
-
- ret = rtl28xx_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
- if (ret)
- goto err;
-
- if (onoff) {
- buf[0] = 0x00;
- buf[1] = 0x00;
- gpio |= 0x04; /* LED on */
- } else {
- buf[0] = 0x10; /* stall EPA */
- buf[1] = 0x02; /* reset EPA */
- gpio &= (~0x04); /* LED off */
- }
-
- ret = rtl28xx_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
- if (ret)
- goto err;
-
- ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
- if (ret)
- goto err;
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2832u_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
-{
- int ret;
- u8 buf[2];
-
- deb_info("%s: onoff=%d\n", __func__, onoff);
-
-
- if (onoff) {
- buf[0] = 0x00;
- buf[1] = 0x00;
- } else {
- buf[0] = 0x10; /* stall EPA */
- buf[1] = 0x02; /* reset EPA */
- }
-
- ret = rtl28xx_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
- if (ret)
- goto err;
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- int ret;
- u8 gpio, sys0;
-
- deb_info("%s: onoff=%d\n", __func__, onoff);
-
- /* demod adc */
- ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0);
- if (ret)
- goto err;
-
- /* tuner power, read GPIOs */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
- if (ret)
- goto err;
-
- deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
-
- if (onoff) {
- gpio |= 0x01; /* GPIO0 = 1 */
- gpio &= (~0x10); /* GPIO4 = 0 */
- sys0 = sys0 & 0x0f;
- sys0 |= 0xe0;
- } else {
- gpio &= (~0x01); /* GPIO0 = 0 */
- gpio |= 0x10; /* GPIO4 = 1 */
- sys0 = sys0 & (~0xc0);
- }
-
- deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
-
- /* demod adc */
- ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0);
- if (ret)
- goto err;
-
- /* tuner power, write GPIOs */
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
- if (ret)
- goto err;
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- int ret;
- u8 val;
-
- deb_info("%s: onoff=%d\n", __func__, onoff);
-
- if (onoff) {
- /* set output values */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
- if (ret)
- goto err;
-
- val |= 0x08;
- val &= 0xef;
-
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
- if (ret)
- goto err;
-
- /* demod_ctl_1 */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
- if (ret)
- goto err;
-
- val &= 0xef;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
- if (ret)
- goto err;
-
- /* demod control */
- /* PLL enable */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- /* bit 7 to 1 */
- val |= 0x80;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
- /* demod HW reset */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
- /* bit 5 to 0 */
- val &= 0xdf;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val |= 0x20;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
- mdelay(5);
-
- /*enable ADC_Q and ADC_I */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val |= 0x48;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
-
- } else {
- /* demod_ctl_1 */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
- if (ret)
- goto err;
-
- val |= 0x0c;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
- if (ret)
- goto err;
-
- /* set output values */
- ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
- if (ret)
- goto err;
-
- val |= 0x10;
-
- ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
- if (ret)
- goto err;
-
- /* demod control */
- ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
- if (ret)
- goto err;
-
- val &= 0x37;
-
- ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
- if (ret)
- goto err;
-
- }
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-
-static int rtl2831u_rc_query(struct dvb_usb_device *d)
-{
- int ret, i;
- struct rtl28xxu_priv *priv = d->priv;
- u8 buf[5];
- u32 rc_code;
- struct rtl28xxu_reg_val rc_nec_tab[] = {
- { 0x3033, 0x80 },
- { 0x3020, 0x43 },
- { 0x3021, 0x16 },
- { 0x3022, 0x16 },
- { 0x3023, 0x5a },
- { 0x3024, 0x2d },
- { 0x3025, 0x16 },
- { 0x3026, 0x01 },
- { 0x3028, 0xb0 },
- { 0x3029, 0x04 },
- { 0x302c, 0x88 },
- { 0x302e, 0x13 },
- { 0x3030, 0xdf },
- { 0x3031, 0x05 },
- };
-
- /* init remote controller */
- if (!priv->rc_active) {
- for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
- ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
- rc_nec_tab[i].val);
- if (ret)
- goto err;
- }
- priv->rc_active = true;
- }
-
- ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
- if (ret)
- goto err;
-
- if (buf[4] & 0x01) {
- if (buf[2] == (u8) ~buf[3]) {
- if (buf[0] == (u8) ~buf[1]) {
- /* NEC standard (16 bit) */
- rc_code = buf[0] << 8 | buf[2];
- } else {
- /* NEC extended (24 bit) */
- rc_code = buf[0] << 16 |
- buf[1] << 8 | buf[2];
- }
- } else {
- /* NEC full (32 bit) */
- rc_code = buf[0] << 24 | buf[1] << 16 |
- buf[2] << 8 | buf[3];
- }
-
- rc_keydown(d->rc_dev, rc_code, 0);
-
- ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
- if (ret)
- goto err;
-
- /* repeated intentionally to avoid extra keypress */
- ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
- if (ret)
- goto err;
- }
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int rtl2832u_rc_query(struct dvb_usb_device *d)
-{
- int ret, i;
- struct rtl28xxu_priv *priv = d->priv;
- u8 buf[128];
- int len;
- struct rtl28xxu_reg_val rc_nec_tab[] = {
- { IR_RX_CTRL, 0x20 },
- { IR_RX_BUF_CTRL, 0x80 },
- { IR_RX_IF, 0xff },
- { IR_RX_IE, 0xff },
- { IR_MAX_DURATION0, 0xd0 },
- { IR_MAX_DURATION1, 0x07 },
- { IR_IDLE_LEN0, 0xc0 },
- { IR_IDLE_LEN1, 0x00 },
- { IR_GLITCH_LEN, 0x03 },
- { IR_RX_CLK, 0x09 },
- { IR_RX_CFG, 0x1c },
- { IR_MAX_H_TOL_LEN, 0x1e },
- { IR_MAX_L_TOL_LEN, 0x1e },
- { IR_RX_CTRL, 0x80 },
- };
-
- /* init remote controller */
- if (!priv->rc_active) {
- for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
- ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
- rc_nec_tab[i].val);
- if (ret)
- goto err;
- }
- priv->rc_active = true;
- }
-
- ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]);
- if (ret)
- goto err;
-
- if (buf[0] != 0x83)
- goto exit;
-
- ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]);
- if (ret)
- goto err;
-
- len = buf[0];
- ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
-
- /* TODO: pass raw IR to Kernel IR decoder */
-
- ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03);
- ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
- ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80);
-
-exit:
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-enum rtl28xxu_usb_table_entry {
- RTL2831U_0BDA_2831,
- RTL2831U_14AA_0160,
- RTL2831U_14AA_0161,
- RTL2832U_0CCD_00A9,
- RTL2832U_1F4D_B803,
- RTL2832U_0CCD_00B3,
-};
-
-static struct usb_device_id rtl28xxu_table[] = {
- /* RTL2831U */
- [RTL2831U_0BDA_2831] = {
- USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)},
- [RTL2831U_14AA_0160] = {
- USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)},
- [RTL2831U_14AA_0161] = {
- USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
-
- /* RTL2832U */
- [RTL2832U_0CCD_00A9] = {
- USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1)},
- [RTL2832U_1F4D_B803] = {
- USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT)},
- [RTL2832U_0CCD_00B3] = {
- USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK)},
- {} /* terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
-
-static struct dvb_usb_device_properties rtl28xxu_properties[] = {
- {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct rtl28xxu_priv),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = rtl2831u_frontend_attach,
- .tuner_attach = rtl2831u_tuner_attach,
- .streaming_ctrl = rtl2831u_streaming_ctrl,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x81,
- .u = {
- .bulk = {
- .buffersize = 8*512,
- }
- }
- }
- }
- }
- }
- },
-
- .power_ctrl = rtl2831u_power_ctrl,
-
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "rtl28xxu",
- .rc_query = rtl2831u_rc_query,
- .rc_interval = 400,
- .allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_EMPTY,
- },
-
- .i2c_algo = &rtl28xxu_i2c_algo,
-
- .num_device_descs = 2,
- .devices = {
- {
- .name = "Realtek RTL2831U reference design",
- .warm_ids = {
- &rtl28xxu_table[RTL2831U_0BDA_2831],
- },
- },
- {
- .name = "Freecom USB2.0 DVB-T",
- .warm_ids = {
- &rtl28xxu_table[RTL2831U_14AA_0160],
- &rtl28xxu_table[RTL2831U_14AA_0161],
- },
- },
- }
- },
- {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = DEVICE_SPECIFIC,
- .no_reconnect = 1,
-
- .size_of_priv = sizeof(struct rtl28xxu_priv),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {
- {
- .frontend_attach = rtl2832u_frontend_attach,
- .tuner_attach = rtl2832u_tuner_attach,
- .streaming_ctrl = rtl2832u_streaming_ctrl,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x81,
- .u = {
- .bulk = {
- .buffersize = 8*512,
- }
- }
- }
- }
- }
- }
- },
-
- .power_ctrl = rtl2832u_power_ctrl,
-
- .rc.core = {
- .protocol = RC_TYPE_NEC,
- .module_name = "rtl28xxu",
- .rc_query = rtl2832u_rc_query,
- .rc_interval = 400,
- .allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_EMPTY,
- },
-
- .i2c_algo = &rtl28xxu_i2c_algo,
-
- .num_device_descs = 3,
- .devices = {
- {
- .name = "Terratec Cinergy T Stick Black",
- .warm_ids = {
- &rtl28xxu_table[RTL2832U_0CCD_00A9],
- },
- },
- {
- .name = "G-Tek Electronics Group Lifeview LV5TDLX DVB-T",
- .warm_ids = {
- &rtl28xxu_table[RTL2832U_1F4D_B803],
- },
- },
- {
- .name = "NOXON DAB/DAB+ USB dongle",
- .warm_ids = {
- &rtl28xxu_table[RTL2832U_0CCD_00B3],
- },
- },
- }
- },
-
-};
-
-static int rtl28xxu_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret, i;
- u8 val;
- int properties_count = ARRAY_SIZE(rtl28xxu_properties);
- struct dvb_usb_device *d;
- struct usb_device *udev;
- bool found;
-
- deb_info("%s: interface=%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
- return 0;
-
- /* Dynamic USB ID support. Replaces first device ID with current one .*/
- udev = interface_to_usbdev(intf);
-
- for (i = 0, found = false; i < ARRAY_SIZE(rtl28xxu_table) - 1; i++) {
- if (rtl28xxu_table[i].idVendor ==
- le16_to_cpu(udev->descriptor.idVendor) &&
- rtl28xxu_table[i].idProduct ==
- le16_to_cpu(udev->descriptor.idProduct)) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- deb_info("%s: using dynamic ID %04x:%04x\n", __func__,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
- rtl28xxu_properties[0].devices[0].warm_ids[0]->idVendor =
- le16_to_cpu(udev->descriptor.idVendor);
- rtl28xxu_properties[0].devices[0].warm_ids[0]->idProduct =
- le16_to_cpu(udev->descriptor.idProduct);
- }
-
- for (i = 0; i < properties_count; i++) {
- ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
- THIS_MODULE, &d, adapter_nr);
- if (ret == 0 || ret != -ENODEV)
- break;
- }
-
- if (ret)
- goto err;
-
-
- /* init USB endpoints */
- ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val);
- if (ret)
- goto err;
-
- /* enable DMA and Full Packet Mode*/
- val |= 0x09;
- ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val);
- if (ret)
- goto err;
-
- /* set EPA maximum packet size to 0x0200 */
- ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
- if (ret)
- goto err;
-
- /* change EPA FIFO length */
- ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
- if (ret)
- goto err;
-
- return ret;
-err:
- deb_info("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static struct usb_driver rtl28xxu_driver = {
- .name = "dvb_usb_rtl28xxu",
- .probe = rtl28xxu_probe,
- .disconnect = dvb_usb_device_exit,
- .id_table = rtl28xxu_table,
-};
-
-/* module stuff */
-static int __init rtl28xxu_module_init(void)
-{
- int ret;
-
- deb_info("%s:\n", __func__);
-
- ret = usb_register(&rtl28xxu_driver);
- if (ret)
- err("usb_register failed=%d", ret);
-
- return ret;
-}
-
-static void __exit rtl28xxu_module_exit(void)
-{
- deb_info("%s:\n", __func__);
-
- /* deregister this driver from the USB subsystem */
- usb_deregister(&rtl28xxu_driver);
-}
-
-module_init(rtl28xxu_module_init);
-module_exit(rtl28xxu_module_exit);
-
-MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_AUTHOR("Thomas Mair <thomas.mair86@googlemail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.h b/drivers/media/dvb/dvb-usb/rtl28xxu.h
deleted file mode 100644
index 90f3bb4f4c0e..000000000000
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.h
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Realtek RTL28xxU DVB USB driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef RTL28XXU_H
-#define RTL28XXU_H
-
-#define DVB_USB_LOG_PREFIX "rtl28xxu"
-#include "dvb-usb.h"
-
-#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args)
-#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args)
-#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args)
-#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args)
-#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args)
-#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args)
-
-#define deb_dump(r, t, v, i, b, l, func) { \
- int loop_; \
- func("%02x %02x %02x %02x %02x %02x %02x %02x", \
- t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
- if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
- func(" >>> "); \
- else \
- func(" <<< "); \
- for (loop_ = 0; loop_ < l; loop_++) \
- func("%02x ", b[loop_]); \
- func("\n");\
-}
-
-/*
- * USB commands
- * (usb_control_msg() index parameter)
- */
-
-#define DEMOD 0x0000
-#define USB 0x0100
-#define SYS 0x0200
-#define I2C 0x0300
-#define I2C_DA 0x0600
-
-#define CMD_WR_FLAG 0x0010
-#define CMD_DEMOD_RD 0x0000
-#define CMD_DEMOD_WR 0x0010
-#define CMD_USB_RD 0x0100
-#define CMD_USB_WR 0x0110
-#define CMD_SYS_RD 0x0200
-#define CMD_IR_RD 0x0201
-#define CMD_IR_WR 0x0211
-#define CMD_SYS_WR 0x0210
-#define CMD_I2C_RD 0x0300
-#define CMD_I2C_WR 0x0310
-#define CMD_I2C_DA_RD 0x0600
-#define CMD_I2C_DA_WR 0x0610
-
-
-struct rtl28xxu_priv {
- u8 chip_id;
- u8 tuner;
- u8 page; /* integrated demod active register page */
- bool rc_active;
-};
-
-enum rtl28xxu_chip_id {
- CHIP_ID_NONE,
- CHIP_ID_RTL2831U,
- CHIP_ID_RTL2832U,
-};
-
-enum rtl28xxu_tuner {
- TUNER_NONE,
-
- TUNER_RTL2830_QT1010,
- TUNER_RTL2830_MT2060,
- TUNER_RTL2830_MXL5005S,
-
- TUNER_RTL2832_MT2266,
- TUNER_RTL2832_FC2580,
- TUNER_RTL2832_MT2063,
- TUNER_RTL2832_MAX3543,
- TUNER_RTL2832_TUA9001,
- TUNER_RTL2832_MXL5007T,
- TUNER_RTL2832_FC0012,
- TUNER_RTL2832_E4000,
- TUNER_RTL2832_TDA18272,
- TUNER_RTL2832_FC0013,
-};
-
-struct rtl28xxu_req {
- u16 value;
- u16 index;
- u16 size;
- u8 *data;
-};
-
-struct rtl28xxu_reg_val {
- u16 reg;
- u8 val;
-};
-
-/*
- * memory map
- *
- * 0x0000 DEMOD : demodulator
- * 0x2000 USB : SIE, USB endpoint, debug, DMA
- * 0x3000 SYS : system
- * 0xfc00 RC : remote controller (not RTL2831U)
- */
-
-/*
- * USB registers
- */
-/* SIE Control Registers */
-#define USB_SYSCTL 0x2000 /* USB system control */
-#define USB_SYSCTL_0 0x2000 /* USB system control */
-#define USB_SYSCTL_1 0x2001 /* USB system control */
-#define USB_SYSCTL_2 0x2002 /* USB system control */
-#define USB_SYSCTL_3 0x2003 /* USB system control */
-#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
-#define USB_IRQEN 0x200C /* SIE interrupt enable */
-#define USB_CTRL 0x2010 /* USB control */
-#define USB_STAT 0x2014 /* USB status */
-#define USB_DEVADDR 0x2018 /* USB device address */
-#define USB_TEST 0x201C /* USB test mode */
-#define USB_FRAME_NUMBER 0x2020 /* frame number */
-#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
-#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
-#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
-/* Endpoint Registers */
-#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
-#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
-#define USB_EP0_CFG 0x2104 /* EP 0 configure */
-#define USB_EP0_CTL 0x2108 /* EP 0 control */
-#define USB_EP0_STAT 0x210C /* EP 0 status */
-#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
-#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
-#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
-#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
-#define USB_EPA_CFG 0x2144 /* EP A configure */
-#define USB_EPA_CFG_0 0x2144 /* EP A configure */
-#define USB_EPA_CFG_1 0x2145 /* EP A configure */
-#define USB_EPA_CFG_2 0x2146 /* EP A configure */
-#define USB_EPA_CFG_3 0x2147 /* EP A configure */
-#define USB_EPA_CTL 0x2148 /* EP A control */
-#define USB_EPA_CTL_0 0x2148 /* EP A control */
-#define USB_EPA_CTL_1 0x2149 /* EP A control */
-#define USB_EPA_CTL_2 0x214A /* EP A control */
-#define USB_EPA_CTL_3 0x214B /* EP A control */
-#define USB_EPA_STAT 0x214C /* EP A status */
-#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
-#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
-#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
-#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
-#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
-#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
-#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
-#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
-#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
-#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
-#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
-#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
-/* Debug Registers */
-#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
-#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
-#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
-#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
-#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
-#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
-#define USB_UTMI_TST 0x2F80 /* UTMI test */
-#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
-#define USB_TSTCTL 0x2F88 /* test control */
-#define USB_TSTCTL2 0x2F8C /* test control 2 */
-#define USB_PID_FORCE 0x2F90 /* force PID */
-#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
-#define USB_RXERR_CNT 0x2F98 /* RX error counter */
-#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
-#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
-#define USB_CNTTEST 0x2FA4 /* counter test */
-#define USB_PHYTST 0x2FC0 /* USB PHY test */
-#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
-#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
-
-/*
- * SYS registers
- */
-/* demod control registers */
-#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
-#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
-/* GPIO registers */
-#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
-#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
-#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
-#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
-#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
-#define SYS_SYSINTE 0x3005 /* system interrupt enable */
-#define SYS_SYSINTS 0x3006 /* system interrupt status */
-#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
-#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
-#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
-#define SYS_DEMOD_CTL1 0x300B
-
-/* IrDA registers */
-#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
-#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
-#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
-#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
-#define SYS_IRRC_CR 0x3030 /* IR control */
-#define SYS_IRRC_RP 0x3034 /* IR read port */
-#define SYS_IRRC_SR 0x3038 /* IR status */
-/* I2C master registers */
-#define SYS_I2CCR 0x3040 /* I2C clock */
-#define SYS_I2CMCR 0x3044 /* I2C master control */
-#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
-#define SYS_I2CMSR 0x304C /* I2C master status */
-#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
-
-/*
- * IR registers
- */
-#define IR_RX_BUF 0xFC00
-#define IR_RX_IE 0xFD00
-#define IR_RX_IF 0xFD01
-#define IR_RX_CTRL 0xFD02
-#define IR_RX_CFG 0xFD03
-#define IR_MAX_DURATION0 0xFD04
-#define IR_MAX_DURATION1 0xFD05
-#define IR_IDLE_LEN0 0xFD06
-#define IR_IDLE_LEN1 0xFD07
-#define IR_GLITCH_LEN 0xFD08
-#define IR_RX_BUF_CTRL 0xFD09
-#define IR_RX_BUF_DATA 0xFD0A
-#define IR_RX_BC 0xFD0B
-#define IR_RX_CLK 0xFD0C
-#define IR_RX_C_COUNT_L 0xFD0D
-#define IR_RX_C_COUNT_H 0xFD0E
-#define IR_SUSPEND_CTRL 0xFD10
-#define IR_ERR_TOL_CTRL 0xFD11
-#define IR_UNIT_LEN 0xFD12
-#define IR_ERR_TOL_LEN 0xFD13
-#define IR_MAX_H_TOL_LEN 0xFD14
-#define IR_MAX_L_TOL_LEN 0xFD15
-#define IR_MASK_CTRL 0xFD16
-#define IR_MASK_DATA 0xFD17
-#define IR_RES_MASK_ADDR 0xFD18
-#define IR_RES_MASK_T_LEN 0xFD19
-
-#endif
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c
deleted file mode 100644
index e53a1061cb8e..000000000000
--- a/drivers/media/dvb/dvb-usb/ttusb2.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
- * (e.g. Pinnacle 400e DVB-S USB2.0).
- *
- * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes.
- *
- * TDA8263 + TDA10086
- *
- * I2C addresses:
- * 0x08 - LNBP21PD - LNB power supply
- * 0x0e - TDA10086 - Demodulator
- * 0x50 - FX2 eeprom
- * 0x60 - TDA8263 - Tuner
- * 0x78 ???
- *
- * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
- * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
- * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, version 2.
- *
- * see Documentation/dvb/README.dvb-usb for more information
- */
-#define DVB_USB_LOG_PREFIX "ttusb2"
-#include "dvb-usb.h"
-
-#include "ttusb2.h"
-
-#include "tda826x.h"
-#include "tda10086.h"
-#include "tda1002x.h"
-#include "tda10048.h"
-#include "tda827x.h"
-#include "lnbp21.h"
-/* CA */
-#include "dvb_ca_en50221.h"
-
-/* debug */
-static int dvb_usb_ttusb2_debug;
-#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args)
-module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
-static int dvb_usb_ttusb2_debug_ci;
-module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644);
-MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS);
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define ci_dbg(format, arg...) \
-do { \
- if (dvb_usb_ttusb2_debug_ci) \
- printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
- ": %s " format "\n" , __func__, ## arg); \
-} while (0)
-
-enum {
- TT3650_CMD_CI_TEST = 0x40,
- TT3650_CMD_CI_RD_CTRL,
- TT3650_CMD_CI_WR_CTRL,
- TT3650_CMD_CI_RD_ATTR,
- TT3650_CMD_CI_WR_ATTR,
- TT3650_CMD_CI_RESET,
- TT3650_CMD_CI_SET_VIDEO_PORT
-};
-
-struct ttusb2_state {
- struct dvb_ca_en50221 ca;
- struct mutex ca_mutex;
- u8 id;
- u16 last_rc_key;
-};
-
-static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
- u8 *wbuf, int wlen, u8 *rbuf, int rlen)
-{
- struct ttusb2_state *st = d->priv;
- u8 *s, *r = NULL;
- int ret = 0;
-
- s = kzalloc(wlen+4, GFP_KERNEL);
- if (!s)
- return -ENOMEM;
-
- r = kzalloc(64, GFP_KERNEL);
- if (!r) {
- kfree(s);
- return -ENOMEM;
- }
-
- s[0] = 0xaa;
- s[1] = ++st->id;
- s[2] = cmd;
- s[3] = wlen;
- memcpy(&s[4],wbuf,wlen);
-
- ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0);
-
- if (ret != 0 ||
- r[0] != 0x55 ||
- r[1] != s[1] ||
- r[2] != cmd ||
- (rlen > 0 && r[3] != rlen)) {
- warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]);
- kfree(s);
- kfree(r);
- return -EIO;
- }
-
- if (rlen > 0)
- memcpy(rbuf, &r[4], rlen);
-
- kfree(s);
- kfree(r);
-
- return 0;
-}
-
-/* ci */
-static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
-{
- int ret;
- u8 rx[60];/* (64 -4) */
- ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len);
- if (!ret)
- memcpy(data, rx, read_len);
- return ret;
-}
-
-static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
-{
- struct dvb_usb_device *d = ca->data;
- struct ttusb2_state *state = d->priv;
- int ret;
-
- mutex_lock(&state->ca_mutex);
- ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
- mutex_unlock(&state->ca_mutex);
-
- return ret;
-}
-
-static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
-{
- u8 buf[3];
- int ret = 0;
-
- if (slot)
- return -EINVAL;
-
- buf[0] = (address >> 8) & 0x0F;
- buf[1] = address;
-
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
-
- ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]);
-
- if (ret < 0)
- return ret;
-
- return buf[2];
-}
-
-static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
-{
- u8 buf[3];
-
- ci_dbg("%d 0x%04x 0x%02x", slot, address, value);
-
- if (slot)
- return -EINVAL;
-
- buf[0] = (address >> 8) & 0x0F;
- buf[1] = address;
- buf[2] = value;
-
- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
-}
-
-static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
-{
- u8 buf[2];
- int ret;
-
- if (slot)
- return -EINVAL;
-
- buf[0] = address & 3;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
-
- ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]);
-
- if (ret < 0)
- return ret;
-
- return buf[1];
-}
-
-static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
-{
- u8 buf[2];
-
- ci_dbg("%d 0x%02x 0x%02x", slot, address, value);
-
- if (slot)
- return -EINVAL;
-
- buf[0] = address;
- buf[1] = value;
-
- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
-}
-
-static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable)
-{
- u8 buf[1];
- int ret;
-
- ci_dbg("%d %d", slot, enable);
-
- if (slot)
- return -EINVAL;
-
- buf[0] = enable;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
- if (ret < 0)
- return ret;
-
- if (enable != buf[0]) {
- err("CI not %sabled.", enable ? "en" : "dis");
- return -EIO;
- }
-
- return 0;
-}
-
-static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
-{
- return tt3650_ci_set_video_port(ca, slot, 0);
-}
-
-static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
-{
- return tt3650_ci_set_video_port(ca, slot, 1);
-}
-
-static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
-{
- struct dvb_usb_device *d = ca->data;
- struct ttusb2_state *state = d->priv;
- u8 buf[1];
- int ret;
-
- ci_dbg("%d", slot);
-
- if (slot)
- return -EINVAL;
-
- buf[0] = 0;
-
- mutex_lock(&state->ca_mutex);
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
- if (ret)
- goto failed;
-
- msleep(500);
-
- buf[0] = 1;
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
- if (ret)
- goto failed;
-
- msleep(500);
-
- buf[0] = 0; /* FTA */
-
- ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
-
- msleep(1100);
-
- failed:
- mutex_unlock(&state->ca_mutex);
-
- return ret;
-}
-
-static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
-{
- u8 buf[1];
- int ret;
-
- if (slot)
- return -EINVAL;
-
- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
- if (ret)
- return ret;
-
- if (1 == buf[0]) {
- return DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY;
- }
- return 0;
-}
-
-static void tt3650_ci_uninit(struct dvb_usb_device *d)
-{
- struct ttusb2_state *state;
-
- ci_dbg("");
-
- if (NULL == d)
- return;
-
- state = d->priv;
- if (NULL == state)
- return;
-
- if (NULL == state->ca.data)
- return;
-
- dvb_ca_en50221_release(&state->ca);
-
- memset(&state->ca, 0, sizeof(state->ca));
-}
-
-static int tt3650_ci_init(struct dvb_usb_adapter *a)
-{
- struct dvb_usb_device *d = a->dev;
- struct ttusb2_state *state = d->priv;
- int ret;
-
- ci_dbg("");
-
- mutex_init(&state->ca_mutex);
-
- state->ca.owner = THIS_MODULE;
- state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
- state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
- state->ca.read_cam_control = tt3650_ci_read_cam_control;
- state->ca.write_cam_control = tt3650_ci_write_cam_control;
- state->ca.slot_reset = tt3650_ci_slot_reset;
- state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
- state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
- state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
- state->ca.data = d;
-
- ret = dvb_ca_en50221_init(&a->dvb_adap,
- &state->ca,
- /* flags */ 0,
- /* n_slots */ 1);
- if (ret) {
- err("Cannot initialize CI: Error %d.", ret);
- memset(&state->ca, 0, sizeof(state->ca));
- return ret;
- }
-
- info("CI initialized.");
-
- return 0;
-}
-
-static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
-{
- struct dvb_usb_device *d = i2c_get_adapdata(adap);
- static u8 obuf[60], ibuf[60];
- int i, write_read, read;
-
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
- if (num > 2)
- warn("more than 2 i2c messages at a time is not handled yet. TODO.");
-
- for (i = 0; i < num; i++) {
- write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
- read = msg[i].flags & I2C_M_RD;
-
- obuf[0] = (msg[i].addr << 1) | (write_read | read);
- if (read)
- obuf[1] = 0;
- else
- obuf[1] = msg[i].len;
-
- /* read request */
- if (write_read)
- obuf[2] = msg[i+1].len;
- else if (read)
- obuf[2] = msg[i].len;
- else
- obuf[2] = 0;
-
- memcpy(&obuf[3], msg[i].buf, msg[i].len);
-
- if (ttusb2_msg(d, CMD_I2C_XFER, obuf, obuf[1]+3, ibuf, obuf[2] + 3) < 0) {
- err("i2c transfer failed.");
- break;
- }
-
- if (write_read) {
- memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len);
- i++;
- } else if (read)
- memcpy(msg[i].buf, &ibuf[3], msg[i].len);
- }
-
- mutex_unlock(&d->i2c_mutex);
- return i;
-}
-
-static u32 ttusb2_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm ttusb2_i2c_algo = {
- .master_xfer = ttusb2_i2c_xfer,
- .functionality = ttusb2_i2c_func,
-};
-
-/* command to poll IR receiver (copied from pctv452e.c) */
-#define CMD_GET_IR_CODE 0x1b
-
-/* IR */
-static int tt3650_rc_query(struct dvb_usb_device *d)
-{
- int ret;
- u8 rx[9]; /* A CMD_GET_IR_CODE reply is 9 bytes long */
- struct ttusb2_state *st = d->priv;
- ret = ttusb2_msg(d, CMD_GET_IR_CODE, NULL, 0, rx, sizeof(rx));
- if (ret != 0)
- return ret;
-
- if (rx[8] & 0x01) {
- /* got a "press" event */
- st->last_rc_key = (rx[3] << 8) | rx[2];
- deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]);
- rc_keydown(d->rc_dev, st->last_rc_key, 0);
- } else if (st->last_rc_key) {
- rc_keyup(d->rc_dev);
- st->last_rc_key = 0;
- }
-
- return 0;
-}
-
-
-/* Callbacks for DVB USB */
-static int ttusb2_identify_state (struct usb_device *udev, struct
- dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
- int *cold)
-{
- *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
- return 0;
-}
-
-static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff)
-{
- u8 b = onoff;
- ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0);
- return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0);
-}
-
-
-static struct tda10086_config tda10086_config = {
- .demod_address = 0x0e,
- .invert = 0,
- .diseqc_tone = 1,
- .xtal_freq = TDA10086_XTAL_16M,
-};
-
-static struct tda10023_config tda10023_config = {
- .demod_address = 0x0c,
- .invert = 0,
- .xtal = 16000000,
- .pll_m = 11,
- .pll_p = 3,
- .pll_n = 1,
- .deltaf = 0xa511,
-};
-
-static struct tda10048_config tda10048_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_PARALLEL_OUTPUT,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_4000,
- .dtv7_if_freq_khz = TDA10048_IF_4500,
- .dtv8_if_freq_khz = TDA10048_IF_5000,
- .clk_freq_khz = TDA10048_CLK_16000,
- .no_firmware = 1,
- .set_pll = true ,
- .pll_m = 5,
- .pll_n = 3,
- .pll_p = 0,
-};
-
-static struct tda827x_config tda827x_config = {
- .config = 0,
-};
-
-static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
-{
- if (usb_set_interface(adap->dev->udev,0,3) < 0)
- err("set interface to alts=3 failed");
-
- if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
- deb_info("TDA10086 attach failed\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct dvb_usb_adapter *adap = fe->dvb->priv;
-
- return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable);
-}
-
-static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap)
-{
- if (usb_set_interface(adap->dev->udev, 0, 3) < 0)
- err("set interface to alts=3 failed");
-
- if (adap->fe_adap[0].fe == NULL) {
- /* FE 0 DVB-C */
- adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
- &tda10023_config, &adap->dev->i2c_adap, 0x48);
-
- if (adap->fe_adap[0].fe == NULL) {
- deb_info("TDA10023 attach failed\n");
- return -ENODEV;
- }
- tt3650_ci_init(adap);
- } else {
- adap->fe_adap[1].fe = dvb_attach(tda10048_attach,
- &tda10048_config, &adap->dev->i2c_adap);
-
- if (adap->fe_adap[1].fe == NULL) {
- deb_info("TDA10048 attach failed\n");
- return -ENODEV;
- }
-
- /* tuner is behind TDA10023 I2C-gate */
- adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl;
-
- }
-
- return 0;
-}
-
-static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
-{
- struct dvb_frontend *fe;
-
- /* MFE: select correct FE to attach tuner since that's called twice */
- if (adap->fe_adap[1].fe == NULL)
- fe = adap->fe_adap[0].fe;
- else
- fe = adap->fe_adap[1].fe;
-
- /* attach tuner */
- if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) {
- printk(KERN_ERR "%s: No tda827x found!\n", __func__);
- return -ENODEV;
- }
- return 0;
-}
-
-static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap)
-{
- if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
- deb_info("TDA8263 attach failed\n");
- return -ENODEV;
- }
-
- if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
- deb_info("LNBP21 attach failed\n");
- return -ENODEV;
- }
- return 0;
-}
-
-/* DVB USB Driver stuff */
-static struct dvb_usb_device_properties ttusb2_properties;
-static struct dvb_usb_device_properties ttusb2_properties_s2400;
-static struct dvb_usb_device_properties ttusb2_properties_ct3650;
-
-static void ttusb2_usb_disconnect(struct usb_interface *intf)
-{
- struct dvb_usb_device *d = usb_get_intfdata(intf);
-
- tt3650_ci_uninit(d);
- dvb_usb_device_exit(intf);
-}
-
-static int ttusb2_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- if (0 == dvb_usb_device_init(intf, &ttusb2_properties,
- THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &ttusb2_properties_s2400,
- THIS_MODULE, NULL, adapter_nr) ||
- 0 == dvb_usb_device_init(intf, &ttusb2_properties_ct3650,
- THIS_MODULE, NULL, adapter_nr))
- return 0;
- return -ENODEV;
-}
-
-static struct usb_device_id ttusb2_table [] = {
- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) },
- { USB_DEVICE(USB_VID_TECHNOTREND,
- USB_PID_TECHNOTREND_CONNECT_S2400) },
- { USB_DEVICE(USB_VID_TECHNOTREND,
- USB_PID_TECHNOTREND_CONNECT_CT3650) },
- {} /* Terminating entry */
-};
-MODULE_DEVICE_TABLE (usb, ttusb2_table);
-
-static struct dvb_usb_device_properties ttusb2_properties = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = CYPRESS_FX2,
- .firmware = "dvb-usb-pctv-400e-01.fw",
-
- .size_of_priv = sizeof(struct ttusb2_state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
-
- .frontend_attach = ttusb2_frontend_tda10086_attach,
- .tuner_attach = ttusb2_tuner_tda826x_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1,
- }
- }
- }
- }},
- }
- },
-
- .power_ctrl = ttusb2_power_ctrl,
- .identify_state = ttusb2_identify_state,
-
- .i2c_algo = &ttusb2_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 0x01,
-
- .num_device_descs = 2,
- .devices = {
- { "Pinnacle 400e DVB-S USB2.0",
- { &ttusb2_table[0], NULL },
- { NULL },
- },
- { "Pinnacle 450e DVB-S USB2.0",
- { &ttusb2_table[1], NULL },
- { NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = CYPRESS_FX2,
- .firmware = "dvb-usb-tt-s2400-01.fw",
-
- .size_of_priv = sizeof(struct ttusb2_state),
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 1,
- .fe = {{
- .streaming_ctrl = NULL,
-
- .frontend_attach = ttusb2_frontend_tda10086_attach,
- .tuner_attach = ttusb2_tuner_tda826x_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1,
- }
- }
- }
- }},
- }
- },
-
- .power_ctrl = ttusb2_power_ctrl,
- .identify_state = ttusb2_identify_state,
-
- .i2c_algo = &ttusb2_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 0x01,
-
- .num_device_descs = 1,
- .devices = {
- { "Technotrend TT-connect S-2400",
- { &ttusb2_table[2], NULL },
- { NULL },
- },
- }
-};
-
-static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
- .caps = DVB_USB_IS_AN_I2C_ADAPTER,
-
- .usb_ctrl = CYPRESS_FX2,
-
- .size_of_priv = sizeof(struct ttusb2_state),
-
- .rc.core = {
- .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */
- .rc_codes = RC_MAP_TT_1500,
- .rc_query = tt3650_rc_query,
- .allowed_protos = RC_TYPE_UNKNOWN,
- },
-
- .num_adapters = 1,
- .adapter = {
- {
- .num_frontends = 2,
- .fe = {{
- .streaming_ctrl = NULL,
-
- .frontend_attach = ttusb2_frontend_tda10023_attach,
- .tuner_attach = ttusb2_tuner_tda827x_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1,
- }
- }
- }
- }, {
- .streaming_ctrl = NULL,
-
- .frontend_attach = ttusb2_frontend_tda10023_attach,
- .tuner_attach = ttusb2_tuner_tda827x_attach,
-
- /* parameter for the MPEG2-data transfer */
- .stream = {
- .type = USB_ISOC,
- .count = 5,
- .endpoint = 0x02,
- .u = {
- .isoc = {
- .framesperurb = 4,
- .framesize = 940,
- .interval = 1,
- }
- }
- }
- }},
- },
- },
-
- .power_ctrl = ttusb2_power_ctrl,
- .identify_state = ttusb2_identify_state,
-
- .i2c_algo = &ttusb2_i2c_algo,
-
- .generic_bulk_ctrl_endpoint = 0x01,
-
- .num_device_descs = 1,
- .devices = {
- { "Technotrend TT-connect CT-3650",
- .warm_ids = { &ttusb2_table[3], NULL },
- },
- }
-};
-
-static struct usb_driver ttusb2_driver = {
- .name = "dvb_usb_ttusb2",
- .probe = ttusb2_probe,
- .disconnect = ttusb2_usb_disconnect,
- .id_table = ttusb2_table,
-};
-
-module_usb_driver(ttusb2_driver);
-
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
-MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
-MODULE_VERSION("1.0");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile
deleted file mode 100644
index 357b3aab186b..000000000000
--- a/drivers/media/dvb/firewire/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-obj-$(CONFIG_DVB_FIREDTV) += firedtv.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
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
deleted file mode 100644
index a08c2152d0ee..000000000000
--- a/drivers/media/dvb/frontends/Kconfig
+++ /dev/null
@@ -1,756 +0,0 @@
-config DVB_FE_CUSTOMISE
- bool "Customise the frontend modules to build"
- depends on DVB_CORE
- depends on EXPERT
- default y if EXPERT
- help
- This allows the user to select/deselect frontend drivers for their
- hardware from the build.
-
- Use this option with care as deselecting frontends which are in fact
- necessary will result in DVB devices which cannot be tuned due to lack
- of driver support.
-
- If unsure say N.
-
-menu "Customise DVB Frontends"
- visible if DVB_FE_CUSTOMISE
-
-comment "Multistandard (satellite) frontends"
- depends on DVB_CORE
-
-config DVB_STB0899
- tristate "STB0899 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want
- to support this demodulator based frontends
-
-config DVB_STB6100
- tristate "STB6100 based tuners"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A Silicon tuner from ST used in conjunction with the STB0899
- demodulator. Say Y when you want to support this tuner.
-
-config DVB_STV090x
- tristate "STV0900/STV0903(A/B) based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators.
- Say Y when you want to support these frontends.
-
-config DVB_STV6110x
- tristate "STV6110/(A) based tuners"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A Silicon tuner that supports DVB-S and DVB-S2 modes
-
-comment "Multistandard (cable + terrestrial) frontends"
- depends on DVB_CORE
-
-config DVB_DRXK
- tristate "Micronas DRXK based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Micronas DRX-K DVB-C/T demodulator.
-
- Say Y when you want to support this frontend.
-
-config DVB_TDA18271C2DD
- tristate "NXP TDA18271C2 silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- NXP TDA18271 silicon tuner.
-
- Say Y when you want to support this tuner.
-
-comment "DVB-S (satellite) frontends"
- depends on DVB_CORE
-
-config DVB_CX24110
- tristate "Conexant CX24110 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_CX24123
- tristate "Conexant CX24123 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_MT312
- tristate "Zarlink VP310/MT312/ZL10313 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_ZL10036
- tristate "Zarlink ZL10036 silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_ZL10039
- tristate "Zarlink ZL10039 silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_S5H1420
- tristate "Samsung S5H1420 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_STV0288
- tristate "ST STV0288 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_STB6000
- tristate "ST STB6000 silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_STV0299
- tristate "ST STV0299 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_STV6110
- tristate "ST STV6110 silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_STV0900
- tristate "ST STV0900 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S/S2 demodulator. Say Y when you want to support this frontend.
-
-config DVB_TDA8083
- tristate "Philips TDA8083 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA10086
- tristate "Philips TDA10086 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA8261
- tristate "Philips TDA8261 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_VES1X93
- tristate "VLSI VES1893 or VES1993 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_TUNER_ITD1000
- tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_TUNER_CX24113
- tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-
-config DVB_TDA826X
- tristate "Philips TDA826X silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S silicon tuner module. Say Y when you want to support this tuner.
-
-config DVB_TUA6100
- tristate "Infineon TUA6100 PLL"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S PLL chip.
-
-config DVB_CX24116
- tristate "Conexant CX24116 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
-
-config DVB_SI21XX
- tristate "Silicon Labs SI21XX based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_DS3000
- tristate "Montage Tehnology DS3000 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S/S2 tuner module. Say Y when you want to support this frontend.
-
-config DVB_MB86A16
- tristate "Fujitsu MB86A16 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S/DSS Direct Conversion reveiver.
- Say Y when you want to support this frontend.
-
-config DVB_TDA10071
- tristate "NXP TDA10071"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-comment "DVB-T (terrestrial) frontends"
- depends on DVB_CORE
-
-config DVB_SP8870
- tristate "Spase sp8870 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
- This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp8870" to
- download/extract it, and then copy it to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
-config DVB_SP887X
- tristate "Spase sp887x based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
- This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware sp887x" to
- download/extract it, and then copy it to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
-config DVB_CX22700
- tristate "Conexant CX22700 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_CX22702
- tristate "Conexant cx22702 demodulator (OFDM)"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_S5H1432
- tristate "Samsung s5h1432 demodulator (OFDM)"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_DRXD
- tristate "Micronas DRXD driver"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
- Note: this driver was based on vendor driver reference code (released
- under the GPL) as opposed to the existing drx397xd driver, which
- was written via reverse engineering.
-
-config DVB_L64781
- tristate "LSI L64781"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA1004X
- tristate "Philips TDA10045H/TDA10046H based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
- This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
- download/extract them, and then copy them to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
-config DVB_NXT6000
- tristate "NxtWave Communications NXT6000 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_MT352
- tristate "Zarlink MT352 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_ZL10353
- tristate "Zarlink ZL10353 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_DIB3000MB
- tristate "DiBcom 3000M-B"
- 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_DIB3000MC
- tristate "DiBcom 3000P/M-C"
- 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_DIB7000M
- tristate "DiBcom 7000MA/MB/PA/PB/MC"
- 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_DIB7000P
- tristate "DiBcom 7000PC"
- 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_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
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module. Say Y when you want to support this frontend.
-
-config DVB_AF9013
- tristate "Afatech AF9013 demodulator"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-config DVB_EC100
- tristate "E3C EC100"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-config DVB_HD29L2
- tristate "HDIC HD29L2"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- 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.
-
-config DVB_CXD2820R
- tristate "Sony CXD2820R"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-config DVB_RTL2830
- tristate "Realtek RTL2830 DVB-T"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-config DVB_RTL2832
- tristate "Realtek RTL2832 DVB-T"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Say Y when you want to support this frontend.
-
-comment "DVB-C (cable) frontends"
- depends on DVB_CORE
-
-config DVB_VES1820
- tristate "VLSI VES1820 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-C tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA10021
- tristate "Philips TDA10021 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-C tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA10023
- tristate "Philips TDA10023 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-C tuner module. Say Y when you want to support this frontend.
-
-config DVB_STV0297
- tristate "ST STV0297 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-C tuner module. Say Y when you want to support this frontend.
-
-comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends"
- depends on DVB_CORE
-
-config DVB_NXT200X
- tristate "NxtWave Communications NXT2002/NXT2004 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
- This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" and
- "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
- download/extract them, and then copy them to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
-config DVB_OR51211
- tristate "Oren OR51211 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
-
- This driver needs external firmware. Please use the command
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51211" to
- download it, and then copy it to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
-config DVB_OR51132
- tristate "Oren OR51132 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
- This driver needs external firmware. Please use the commands
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or
- "<kerneldir>/Documentation/dvb/get_dvb_firmware or51132_qam" to
- download firmwares for 8VSB and QAM64/256, respectively. Copy them to
- /usr/lib/hotplug/firmware or /lib/firmware (depending on
- configuration of firmware hotplug).
-
-config DVB_BCM3510
- tristate "Broadcom BCM3510"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
- support this frontend.
-
-config DVB_LGDT330X
- tristate "LG Electronics LGDT3302/LGDT3303 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
-config DVB_LGDT3305
- tristate "LG Electronics LGDT3304 and LGDT3305 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
-config DVB_LG2160
- tristate "LG Electronics LG216x based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC/MH demodulator module. Say Y when you want
- to support this frontend.
-
-config DVB_S5H1409
- tristate "Samsung S5H1409 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
-config DVB_AU8522
- depends on I2C
- tristate
-
-config DVB_AU8522_DTV
- tristate "Auvitek AU8522 based DTV demod"
- depends on DVB_CORE && I2C
- select DVB_AU8522
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
- you want to enable DTV demodulation support for this frontend.
-
-config DVB_AU8522_V4L
- tristate "Auvitek AU8522 based ATV demod"
- depends on VIDEO_V4L2 && I2C
- select DVB_AU8522
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
- you want to enable ATV demodulation support for this frontend.
-
-config DVB_S5H1411
- tristate "Samsung S5H1411 based"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
-
-comment "ISDB-T (terrestrial) frontends"
- depends on DVB_CORE
-
-config DVB_S921
- tristate "Sharp S921 frontend"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module.
- Say Y when you want to support this frontend.
-
-config DVB_DIB8000
- tristate "DiBcom 8000MB/MC"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator.
- Say Y when you want to support this frontend.
-
-config DVB_MB86A20S
- tristate "Fujitsu mb86a20s"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator.
- Say Y when you want to support this frontend.
-
-comment "Digital terrestrial only tuners/PLL"
- depends on DVB_CORE
-
-config DVB_PLL
- tristate "Generic I2C PLL based tuners"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- This module drives a number of tuners based on PLL chips with a
- common I2C interface. Say Y when you want to support these tuners.
-
-config DVB_TUNER_DIB0070
- tristate "DiBcom DiB0070 silicon base-band tuner"
- depends on I2C
- default m if DVB_FE_CUSTOMISE
- help
- A driver for the silicon baseband tuner DiB0070 from DiBcom.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-config DVB_TUNER_DIB0090
- tristate "DiBcom DiB0090 silicon base-band tuner"
- depends on I2C
- default m if DVB_FE_CUSTOMISE
- help
- A driver for the silicon baseband tuner DiB0090 from DiBcom.
- This device is only used inside a SiP called together with a
- demodulator for now.
-
-comment "SEC control devices for DVB-S"
- depends on DVB_CORE
-
-config DVB_LNBP21
- tristate "LNBP21/LNBH24 SEC controllers"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An SEC control chips.
-
-config DVB_LNBP22
- tristate "LNBP22 SEC controllers"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- LNB power supply and control voltage
- regulator chip with step-up converter
- and I2C interface.
- Say Y when you want to support this chip.
-
-config DVB_ISL6405
- tristate "ISL6405 SEC controller"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An SEC control chip.
-
-config DVB_ISL6421
- tristate "ISL6421 SEC controller"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- An SEC control chip.
-
-config DVB_ISL6423
- tristate "ISL6423 SEC controller"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A SEC controller chip from Intersil
-
-config DVB_A8293
- tristate "Allegro A8293"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
-
-config DVB_LGS8GL5
- tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DMB-TH tuner module. Say Y when you want to support this frontend.
-
-config DVB_LGS8GXX
- tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator"
- depends on DVB_CORE && I2C
- select FW_LOADER
- default m if DVB_FE_CUSTOMISE
- help
- A DMB-TH tuner module. Say Y when you want to support this frontend.
-
-config DVB_ATBM8830
- tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DMB-TH tuner module. Say Y when you want to support this frontend.
-
-config DVB_TDA665x
- tristate "TDA665x tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- Support for tuner modules based on Philips TDA6650/TDA6651 chips.
- Say Y when you want to support this chip.
-
- Currently supported tuners:
- * Panasonic ENV57H12D5 (ET-50DT)
-
-config DVB_IX2505V
- tristate "Sharp IX2505V silicon tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module. Say Y when you want to support this frontend.
-
-config DVB_IT913X_FE
- tristate "it913x frontend and it9137 tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-T tuner module.
- Say Y when you want to support this frontend.
-
-config DVB_M88RS2000
- tristate "M88RS2000 DVB-S demodulator and tuner"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
- help
- A DVB-S tuner module.
- Say Y when you want to support this frontend.
-
-config DVB_AF9033
- tristate "Afatech AF9033 DVB-T demodulator"
- depends on DVB_CORE && I2C
- default m if DVB_FE_CUSTOMISE
-
-comment "Tools to develop new frontends"
-
-config DVB_DUMMY_FE
- tristate "Dummy frontend driver"
- default n
-endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
deleted file mode 100644
index 185bb8b51952..000000000000
--- a/drivers/media/dvb/frontends/Makefile
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# Makefile for the kernel DVB frontend device drivers.
-#
-
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core/
-ccflags-y += -I$(srctree)/drivers/media/common/tuners/
-
-stb0899-objs = stb0899_drv.o stb0899_algo.o
-stv0900-objs = stv0900_core.o stv0900_sw.o
-drxd-objs = drxd_firm.o drxd_hard.o
-cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
-drxk-objs := drxk_hard.o
-
-obj-$(CONFIG_DVB_PLL) += dvb-pll.o
-obj-$(CONFIG_DVB_STV0299) += stv0299.o
-obj-$(CONFIG_DVB_STB0899) += stb0899.o
-obj-$(CONFIG_DVB_STB6100) += stb6100.o
-obj-$(CONFIG_DVB_SP8870) += sp8870.o
-obj-$(CONFIG_DVB_CX22700) += cx22700.o
-obj-$(CONFIG_DVB_S5H1432) += s5h1432.o
-obj-$(CONFIG_DVB_CX24110) += cx24110.o
-obj-$(CONFIG_DVB_TDA8083) += tda8083.o
-obj-$(CONFIG_DVB_L64781) += l64781.o
-obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
-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
-obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
-obj-$(CONFIG_DVB_SP887X) += sp887x.o
-obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
-obj-$(CONFIG_DVB_MT352) += mt352.o
-obj-$(CONFIG_DVB_ZL10036) += zl10036.o
-obj-$(CONFIG_DVB_ZL10039) += zl10039.o
-obj-$(CONFIG_DVB_ZL10353) += zl10353.o
-obj-$(CONFIG_DVB_CX22702) += cx22702.o
-obj-$(CONFIG_DVB_DRXD) += drxd.o
-obj-$(CONFIG_DVB_TDA10021) += tda10021.o
-obj-$(CONFIG_DVB_TDA10023) += tda10023.o
-obj-$(CONFIG_DVB_STV0297) += stv0297.o
-obj-$(CONFIG_DVB_NXT200X) += nxt200x.o
-obj-$(CONFIG_DVB_OR51211) += or51211.o
-obj-$(CONFIG_DVB_OR51132) += or51132.o
-obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
-obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
-obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
-obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
-obj-$(CONFIG_DVB_LG2160) += lg2160.o
-obj-$(CONFIG_DVB_CX24123) += cx24123.o
-obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
-obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
-obj-$(CONFIG_DVB_ISL6405) += isl6405.o
-obj-$(CONFIG_DVB_ISL6421) += isl6421.o
-obj-$(CONFIG_DVB_TDA10086) += tda10086.o
-obj-$(CONFIG_DVB_TDA826X) += tda826x.o
-obj-$(CONFIG_DVB_TDA8261) += tda8261.o
-obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
-obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o
-obj-$(CONFIG_DVB_TUA6100) += tua6100.o
-obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
-obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
-obj-$(CONFIG_DVB_AU8522) += au8522_common.o
-obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o
-obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o
-obj-$(CONFIG_DVB_TDA10048) += tda10048.o
-obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
-obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
-obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o
-obj-$(CONFIG_DVB_TDA665x) += tda665x.o
-obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o
-obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o
-obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o
-obj-$(CONFIG_DVB_AF9013) += af9013.o
-obj-$(CONFIG_DVB_CX24116) += cx24116.o
-obj-$(CONFIG_DVB_SI21XX) += si21xx.o
-obj-$(CONFIG_DVB_STV0288) += stv0288.o
-obj-$(CONFIG_DVB_STB6000) += stb6000.o
-obj-$(CONFIG_DVB_S921) += s921.o
-obj-$(CONFIG_DVB_STV6110) += stv6110.o
-obj-$(CONFIG_DVB_STV0900) += stv0900.o
-obj-$(CONFIG_DVB_STV090x) += stv090x.o
-obj-$(CONFIG_DVB_STV6110x) += stv6110x.o
-obj-$(CONFIG_DVB_ISL6423) += isl6423.o
-obj-$(CONFIG_DVB_EC100) += ec100.o
-obj-$(CONFIG_DVB_HD29L2) += hd29l2.o
-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
-obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o
-obj-$(CONFIG_DVB_DRXK) += drxk.o
-obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
-obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
-obj-$(CONFIG_DVB_A8293) += a8293.o
-obj-$(CONFIG_DVB_TDA10071) += tda10071.o
-obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
-obj-$(CONFIG_DVB_RTL2832) += rtl2832.o
-obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
-obj-$(CONFIG_DVB_AF9033) += af9033.o
-
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
deleted file mode 100644
index 5bc570d77846..000000000000
--- a/drivers/media/dvb/frontends/af9013.c
+++ /dev/null
@@ -1,1524 +0,0 @@
-/*
- * Afatech AF9013 demodulator driver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * Thanks to Afatech who kindly provided information.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "af9013_priv.h"
-
-int af9013_debug;
-module_param_named(debug, af9013_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-struct af9013_state {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct af9013_config config;
-
- /* tuner/demod RF and IF AGC limits used for signal strength calc */
- u8 signal_strength_en, rf_50, rf_80, if_50, if_80;
- u16 signal_strength;
- u32 ber;
- u32 ucblocks;
- u16 snr;
- u32 bandwidth_hz;
- fe_status_t fe_status;
- unsigned long set_frontend_jiffies;
- unsigned long read_status_jiffies;
- bool first_tune;
- bool i2c_gate_state;
- unsigned int statistics_step:3;
- struct delayed_work statistics_work;
-};
-
-/* write multiple registers */
-static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
- const u8 *val, int len)
-{
- int ret;
- u8 buf[3+len];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->config.i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = (reg >> 8) & 0xff;
- buf[1] = (reg >> 0) & 0xff;
- buf[2] = mbox;
- memcpy(&buf[3], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed=%d reg=%04x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg,
- u8 *val, int len)
-{
- int ret;
- u8 buf[3];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->config.i2c_addr,
- .flags = 0,
- .len = 3,
- .buf = buf,
- }, {
- .addr = priv->config.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- }
- };
-
- buf[0] = (reg >> 8) & 0xff;
- buf[1] = (reg >> 0) & 0xff;
- buf[2] = mbox;
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- warn("i2c rd failed=%d reg=%04x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* write multiple registers */
-static int af9013_wr_regs(struct af9013_state *priv, u16 reg, const u8 *val,
- int len)
-{
- int ret, i;
- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(1 << 0);
-
- if ((priv->config.ts_mode == AF9013_TS_USB) &&
- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
- mbox |= ((len - 1) << 2);
- ret = af9013_wr_regs_i2c(priv, mbox, reg, val, len);
- } else {
- for (i = 0; i < len; i++) {
- ret = af9013_wr_regs_i2c(priv, mbox, reg+i, val+i, 1);
- if (ret)
- goto err;
- }
- }
-
-err:
- return 0;
-}
-
-/* read multiple registers */
-static int af9013_rd_regs(struct af9013_state *priv, u16 reg, u8 *val, int len)
-{
- int ret, i;
- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(0 << 0);
-
- if ((priv->config.ts_mode == AF9013_TS_USB) &&
- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) {
- mbox |= ((len - 1) << 2);
- ret = af9013_rd_regs_i2c(priv, mbox, reg, val, len);
- } else {
- for (i = 0; i < len; i++) {
- ret = af9013_rd_regs_i2c(priv, mbox, reg+i, val+i, 1);
- if (ret)
- goto err;
- }
- }
-
-err:
- return 0;
-}
-
-/* write single register */
-static int af9013_wr_reg(struct af9013_state *priv, u16 reg, u8 val)
-{
- return af9013_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int af9013_rd_reg(struct af9013_state *priv, u16 reg, u8 *val)
-{
- return af9013_rd_regs(priv, reg, val, 1);
-}
-
-static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val,
- u8 len)
-{
- u8 mbox = (1 << 7)|(1 << 6)|((len - 1) << 2)|(1 << 1)|(1 << 0);
- return af9013_wr_regs_i2c(state, mbox, reg, val, len);
-}
-
-static int af9013_wr_reg_bits(struct af9013_state *state, u16 reg, int pos,
- int len, u8 val)
-{
- int ret;
- u8 tmp, mask;
-
- /* no need for read if whole reg is written */
- if (len != 8) {
- ret = af9013_rd_reg(state, reg, &tmp);
- if (ret)
- return ret;
-
- mask = (0xff >> (8 - len)) << pos;
- val <<= pos;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return af9013_wr_reg(state, reg, val);
-}
-
-static int af9013_rd_reg_bits(struct af9013_state *state, u16 reg, int pos,
- int len, u8 *val)
-{
- int ret;
- u8 tmp;
-
- ret = af9013_rd_reg(state, reg, &tmp);
- if (ret)
- return ret;
-
- *val = (tmp >> pos);
- *val &= (0xff >> (8 - len));
-
- return 0;
-}
-
-static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval)
-{
- int ret;
- u8 pos;
- u16 addr;
-
- dbg("%s: gpio=%d gpioval=%02x", __func__, gpio, gpioval);
-
- /*
- * GPIO0 & GPIO1 0xd735
- * GPIO2 & GPIO3 0xd736
- */
-
- switch (gpio) {
- case 0:
- case 1:
- addr = 0xd735;
- break;
- case 2:
- case 3:
- addr = 0xd736;
- break;
-
- default:
- err("invalid gpio:%d\n", gpio);
- ret = -EINVAL;
- goto err;
- };
-
- switch (gpio) {
- case 0:
- case 2:
- pos = 0;
- break;
- case 1:
- case 3:
- default:
- pos = 4;
- break;
- };
-
- ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static u32 af913_div(u32 a, u32 b, u32 x)
-{
- u32 r = 0, c = 0, i;
-
- dbg("%s: a=%d b=%d x=%d", __func__, a, b, x);
-
- if (a > b) {
- c = a / b;
- a = a - c * b;
- }
-
- for (i = 0; i < x; i++) {
- if (a >= b) {
- r += 1;
- a -= b;
- }
- a <<= 1;
- r <<= 1;
- }
- r = (c << (u32)x) + r;
-
- dbg("%s: a=%d b=%d x=%d r=%x", __func__, a, b, x, r);
- return r;
-}
-
-static int af9013_power_ctrl(struct af9013_state *state, u8 onoff)
-{
- int ret, i;
- u8 tmp;
-
- dbg("%s: onoff=%d", __func__, onoff);
-
- /* enable reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 1);
- if (ret)
- goto err;
-
- /* start reset mechanism */
- ret = af9013_wr_reg(state, 0xaeff, 1);
- if (ret)
- goto err;
-
- /* wait reset performs */
- for (i = 0; i < 150; i++) {
- ret = af9013_rd_reg_bits(state, 0xd417, 1, 1, &tmp);
- if (ret)
- goto err;
-
- if (tmp)
- break; /* reset done */
-
- usleep_range(5000, 25000);
- }
-
- if (!tmp)
- return -ETIMEDOUT;
-
- if (onoff) {
- /* clear reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 1, 1, 0);
- if (ret)
- goto err;
-
- /* disable reset */
- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 0);
-
- /* power on */
- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 0);
- } else {
- /* power off */
- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 1);
- }
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
-
- dbg("%s", __func__);
-
- /* reset and start BER counter */
- ret = af9013_wr_reg_bits(state, 0xd391, 4, 1, 1);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
- u8 buf[5];
-
- dbg("%s", __func__);
-
- /* check if error bit count is ready */
- ret = af9013_rd_reg_bits(state, 0xd391, 4, 1, &buf[0]);
- if (ret)
- goto err;
-
- if (!buf[0]) {
- dbg("%s: not ready", __func__);
- return 0;
- }
-
- ret = af9013_rd_regs(state, 0xd387, buf, 5);
- if (ret)
- goto err;
-
- state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0];
- state->ucblocks += (buf[4] << 8) | buf[3];
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_statistics_snr_start(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
-
- dbg("%s", __func__);
-
- /* start SNR meas */
- ret = af9013_wr_reg_bits(state, 0xd2e1, 3, 1, 1);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_statistics_snr_result(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret, i, len;
- u8 buf[3], tmp;
- u32 snr_val;
- const struct af9013_snr *uninitialized_var(snr_lut);
-
- dbg("%s", __func__);
-
- /* check if SNR ready */
- ret = af9013_rd_reg_bits(state, 0xd2e1, 3, 1, &tmp);
- if (ret)
- goto err;
-
- if (!tmp) {
- dbg("%s: not ready", __func__);
- return 0;
- }
-
- /* read value */
- ret = af9013_rd_regs(state, 0xd2e3, buf, 3);
- if (ret)
- goto err;
-
- snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-
- /* read current modulation */
- ret = af9013_rd_reg(state, 0xd3c1, &tmp);
- if (ret)
- goto err;
-
- switch ((tmp >> 6) & 3) {
- case 0:
- len = ARRAY_SIZE(qpsk_snr_lut);
- snr_lut = qpsk_snr_lut;
- break;
- case 1:
- len = ARRAY_SIZE(qam16_snr_lut);
- snr_lut = qam16_snr_lut;
- break;
- case 2:
- len = ARRAY_SIZE(qam64_snr_lut);
- snr_lut = qam64_snr_lut;
- break;
- default:
- goto err;
- break;
- }
-
- for (i = 0; i < len; i++) {
- tmp = snr_lut[i].snr;
-
- if (snr_val < snr_lut[i].val)
- break;
- }
- state->snr = tmp * 10; /* dB/10 */
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_statistics_signal_strength(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret = 0;
- u8 buf[2], rf_gain, if_gain;
- int signal_strength;
-
- dbg("%s", __func__);
-
- if (!state->signal_strength_en)
- return 0;
-
- ret = af9013_rd_regs(state, 0xd07c, buf, 2);
- if (ret)
- goto err;
-
- rf_gain = buf[0];
- if_gain = buf[1];
-
- signal_strength = (0xffff / \
- (9 * (state->rf_50 + state->if_50) - \
- 11 * (state->rf_80 + state->if_80))) * \
- (10 * (rf_gain + if_gain) - \
- 11 * (state->rf_80 + state->if_80));
- if (signal_strength < 0)
- signal_strength = 0;
- else if (signal_strength > 0xffff)
- signal_strength = 0xffff;
-
- state->signal_strength = signal_strength;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static void af9013_statistics_work(struct work_struct *work)
-{
- struct af9013_state *state = container_of(work,
- struct af9013_state, statistics_work.work);
- unsigned int next_msec;
-
- /* update only signal strength when demod is not locked */
- if (!(state->fe_status & FE_HAS_LOCK)) {
- state->statistics_step = 0;
- state->ber = 0;
- state->snr = 0;
- }
-
- switch (state->statistics_step) {
- default:
- state->statistics_step = 0;
- case 0:
- af9013_statistics_signal_strength(&state->fe);
- state->statistics_step++;
- next_msec = 300;
- break;
- case 1:
- af9013_statistics_snr_start(&state->fe);
- state->statistics_step++;
- next_msec = 200;
- break;
- case 2:
- af9013_statistics_ber_unc_start(&state->fe);
- state->statistics_step++;
- next_msec = 1000;
- break;
- case 3:
- af9013_statistics_snr_result(&state->fe);
- state->statistics_step++;
- next_msec = 400;
- break;
- case 4:
- af9013_statistics_ber_unc_result(&state->fe);
- state->statistics_step++;
- next_msec = 100;
- break;
- }
-
- schedule_delayed_work(&state->statistics_work,
- msecs_to_jiffies(next_msec));
-}
-
-static int af9013_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- fesettings->min_delay_ms = 800;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
-
- return 0;
-}
-
-static int af9013_set_frontend(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, sampling_freq;
- bool auto_mode, spec_inv;
- u8 buf[6];
- u32 if_frequency, freq_cw;
-
- dbg("%s: frequency=%d bandwidth_hz=%d", __func__,
- c->frequency, c->bandwidth_hz);
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- /* program CFOE coefficients */
- if (c->bandwidth_hz != state->bandwidth_hz) {
- for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
- if (coeff_lut[i].clock == state->config.clock &&
- coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
- break;
- }
- }
-
- ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val,
- sizeof(coeff_lut[i].val));
- }
-
- /* program frequency control */
- if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) {
- /* get used IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency)
- fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
- else
- if_frequency = state->config.if_frequency;
-
- sampling_freq = if_frequency;
-
- while (sampling_freq > (state->config.clock / 2))
- sampling_freq -= state->config.clock;
-
- if (sampling_freq < 0) {
- sampling_freq *= -1;
- spec_inv = state->config.spec_inv;
- } else {
- spec_inv = !state->config.spec_inv;
- }
-
- freq_cw = af913_div(sampling_freq, state->config.clock, 23);
-
- if (spec_inv)
- freq_cw = 0x800000 - freq_cw;
-
- buf[0] = (freq_cw >> 0) & 0xff;
- buf[1] = (freq_cw >> 8) & 0xff;
- buf[2] = (freq_cw >> 16) & 0x7f;
-
- freq_cw = 0x800000 - freq_cw;
-
- buf[3] = (freq_cw >> 0) & 0xff;
- buf[4] = (freq_cw >> 8) & 0xff;
- buf[5] = (freq_cw >> 16) & 0x7f;
-
- ret = af9013_wr_regs(state, 0xd140, buf, 3);
- if (ret)
- goto err;
-
- ret = af9013_wr_regs(state, 0x9be7, buf, 6);
- if (ret)
- goto err;
- }
-
- /* clear TPS lock flag */
- ret = af9013_wr_reg_bits(state, 0xd330, 3, 1, 1);
- if (ret)
- goto err;
-
- /* clear MPEG2 lock flag */
- ret = af9013_wr_reg_bits(state, 0xd507, 6, 1, 0);
- if (ret)
- goto err;
-
- /* empty channel function */
- ret = af9013_wr_reg_bits(state, 0x9bfe, 0, 1, 0);
- if (ret)
- goto err;
-
- /* empty DVB-T channel function */
- ret = af9013_wr_reg_bits(state, 0x9bc2, 0, 1, 0);
- if (ret)
- goto err;
-
- /* transmission parameters */
- auto_mode = false;
- memset(buf, 0, 3);
-
- switch (c->transmission_mode) {
- case TRANSMISSION_MODE_AUTO:
- auto_mode = 1;
- break;
- case TRANSMISSION_MODE_2K:
- break;
- case TRANSMISSION_MODE_8K:
- buf[0] |= (1 << 0);
- break;
- default:
- dbg("%s: invalid transmission_mode", __func__);
- auto_mode = 1;
- }
-
- switch (c->guard_interval) {
- case GUARD_INTERVAL_AUTO:
- auto_mode = 1;
- break;
- case GUARD_INTERVAL_1_32:
- break;
- case GUARD_INTERVAL_1_16:
- buf[0] |= (1 << 2);
- break;
- case GUARD_INTERVAL_1_8:
- buf[0] |= (2 << 2);
- break;
- case GUARD_INTERVAL_1_4:
- buf[0] |= (3 << 2);
- break;
- default:
- dbg("%s: invalid guard_interval", __func__);
- auto_mode = 1;
- }
-
- switch (c->hierarchy) {
- case HIERARCHY_AUTO:
- auto_mode = 1;
- break;
- case HIERARCHY_NONE:
- break;
- case HIERARCHY_1:
- buf[0] |= (1 << 4);
- break;
- case HIERARCHY_2:
- buf[0] |= (2 << 4);
- break;
- case HIERARCHY_4:
- buf[0] |= (3 << 4);
- break;
- default:
- dbg("%s: invalid hierarchy", __func__);
- auto_mode = 1;
- };
-
- switch (c->modulation) {
- case QAM_AUTO:
- auto_mode = 1;
- break;
- case QPSK:
- break;
- case QAM_16:
- buf[1] |= (1 << 6);
- break;
- case QAM_64:
- buf[1] |= (2 << 6);
- break;
- default:
- dbg("%s: invalid modulation", __func__);
- auto_mode = 1;
- }
-
- /* Use HP. How and which case we can switch to LP? */
- buf[1] |= (1 << 4);
-
- switch (c->code_rate_HP) {
- case FEC_AUTO:
- auto_mode = 1;
- break;
- case FEC_1_2:
- break;
- case FEC_2_3:
- buf[2] |= (1 << 0);
- break;
- case FEC_3_4:
- buf[2] |= (2 << 0);
- break;
- case FEC_5_6:
- buf[2] |= (3 << 0);
- break;
- case FEC_7_8:
- buf[2] |= (4 << 0);
- break;
- default:
- dbg("%s: invalid code_rate_HP", __func__);
- auto_mode = 1;
- }
-
- switch (c->code_rate_LP) {
- case FEC_AUTO:
- auto_mode = 1;
- break;
- case FEC_1_2:
- break;
- case FEC_2_3:
- buf[2] |= (1 << 3);
- break;
- case FEC_3_4:
- buf[2] |= (2 << 3);
- break;
- case FEC_5_6:
- buf[2] |= (3 << 3);
- break;
- case FEC_7_8:
- buf[2] |= (4 << 3);
- break;
- case FEC_NONE:
- break;
- default:
- dbg("%s: invalid code_rate_LP", __func__);
- auto_mode = 1;
- }
-
- switch (c->bandwidth_hz) {
- case 6000000:
- break;
- case 7000000:
- buf[1] |= (1 << 2);
- break;
- case 8000000:
- buf[1] |= (2 << 2);
- break;
- default:
- dbg("%s: invalid bandwidth_hz", __func__);
- ret = -EINVAL;
- goto err;
- }
-
- ret = af9013_wr_regs(state, 0xd3c0, buf, 3);
- if (ret)
- goto err;
-
- if (auto_mode) {
- /* clear easy mode flag */
- ret = af9013_wr_reg(state, 0xaefd, 0);
- if (ret)
- goto err;
-
- dbg("%s: auto params", __func__);
- } else {
- /* set easy mode flag */
- ret = af9013_wr_reg(state, 0xaefd, 1);
- if (ret)
- goto err;
-
- ret = af9013_wr_reg(state, 0xaefe, 0);
- if (ret)
- goto err;
-
- dbg("%s: manual params", __func__);
- }
-
- /* tune */
- ret = af9013_wr_reg(state, 0xffff, 0);
- if (ret)
- goto err;
-
- state->bandwidth_hz = c->bandwidth_hz;
- state->set_frontend_jiffies = jiffies;
- state->first_tune = false;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
- u8 buf[3];
-
- dbg("%s", __func__);
-
- ret = af9013_rd_regs(state, 0xd3c0, buf, 3);
- if (ret)
- goto err;
-
- switch ((buf[1] >> 6) & 3) {
- case 0:
- c->modulation = QPSK;
- break;
- case 1:
- c->modulation = QAM_16;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- }
-
- switch ((buf[0] >> 0) & 3) {
- case 0:
- c->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case 1:
- c->transmission_mode = TRANSMISSION_MODE_8K;
- }
-
- switch ((buf[0] >> 2) & 3) {
- case 0:
- c->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- c->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- c->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- c->guard_interval = GUARD_INTERVAL_1_4;
- break;
- }
-
- switch ((buf[0] >> 4) & 7) {
- case 0:
- c->hierarchy = HIERARCHY_NONE;
- break;
- case 1:
- c->hierarchy = HIERARCHY_1;
- break;
- case 2:
- c->hierarchy = HIERARCHY_2;
- break;
- case 3:
- c->hierarchy = HIERARCHY_4;
- break;
- }
-
- switch ((buf[2] >> 0) & 7) {
- case 0:
- c->code_rate_HP = FEC_1_2;
- break;
- case 1:
- c->code_rate_HP = FEC_2_3;
- break;
- case 2:
- c->code_rate_HP = FEC_3_4;
- break;
- case 3:
- c->code_rate_HP = FEC_5_6;
- break;
- case 4:
- c->code_rate_HP = FEC_7_8;
- break;
- }
-
- switch ((buf[2] >> 3) & 7) {
- case 0:
- c->code_rate_LP = FEC_1_2;
- break;
- case 1:
- c->code_rate_LP = FEC_2_3;
- break;
- case 2:
- c->code_rate_LP = FEC_3_4;
- break;
- case 3:
- c->code_rate_LP = FEC_5_6;
- break;
- case 4:
- c->code_rate_LP = FEC_7_8;
- break;
- }
-
- switch ((buf[1] >> 2) & 3) {
- case 0:
- c->bandwidth_hz = 6000000;
- break;
- case 1:
- c->bandwidth_hz = 7000000;
- break;
- case 2:
- c->bandwidth_hz = 8000000;
- break;
- }
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp;
-
- /*
- * Return status from the cache if it is younger than 2000ms with the
- * exception of last tune is done during 4000ms.
- */
- if (time_is_after_jiffies(
- state->read_status_jiffies + msecs_to_jiffies(2000)) &&
- time_is_before_jiffies(
- state->set_frontend_jiffies + msecs_to_jiffies(4000))
- ) {
- *status = state->fe_status;
- return 0;
- } else {
- *status = 0;
- }
-
- /* MPEG2 lock */
- ret = af9013_rd_reg_bits(state, 0xd507, 6, 1, &tmp);
- if (ret)
- goto err;
-
- if (tmp)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
- FE_HAS_SYNC | FE_HAS_LOCK;
-
- if (!*status) {
- /* TPS lock */
- ret = af9013_rd_reg_bits(state, 0xd330, 3, 1, &tmp);
- if (ret)
- goto err;
-
- if (tmp)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI;
- }
-
- state->fe_status = *status;
- state->read_status_jiffies = jiffies;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct af9013_state *state = fe->demodulator_priv;
- *snr = state->snr;
- return 0;
-}
-
-static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct af9013_state *state = fe->demodulator_priv;
- *strength = state->signal_strength;
- return 0;
-}
-
-static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct af9013_state *state = fe->demodulator_priv;
- *ber = state->ber;
- return 0;
-}
-
-static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct af9013_state *state = fe->demodulator_priv;
- *ucblocks = state->ucblocks;
- return 0;
-}
-
-static int af9013_init(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret, i, len;
- u8 buf[3], tmp;
- u32 adc_cw;
- const struct af9013_reg_bit *init;
-
- dbg("%s", __func__);
-
- /* power on */
- ret = af9013_power_ctrl(state, 1);
- if (ret)
- goto err;
-
- /* enable ADC */
- ret = af9013_wr_reg(state, 0xd73a, 0xa4);
- if (ret)
- goto err;
-
- /* write API version to firmware */
- ret = af9013_wr_regs(state, 0x9bf2, state->config.api_version, 4);
- if (ret)
- goto err;
-
- /* program ADC control */
- switch (state->config.clock) {
- case 28800000: /* 28.800 MHz */
- tmp = 0;
- break;
- case 20480000: /* 20.480 MHz */
- tmp = 1;
- break;
- case 28000000: /* 28.000 MHz */
- tmp = 2;
- break;
- case 25000000: /* 25.000 MHz */
- tmp = 3;
- break;
- default:
- err("invalid clock");
- return -EINVAL;
- }
-
- adc_cw = af913_div(state->config.clock, 1000000ul, 19);
- buf[0] = (adc_cw >> 0) & 0xff;
- buf[1] = (adc_cw >> 8) & 0xff;
- buf[2] = (adc_cw >> 16) & 0xff;
-
- ret = af9013_wr_regs(state, 0xd180, buf, 3);
- if (ret)
- goto err;
-
- ret = af9013_wr_reg_bits(state, 0x9bd2, 0, 4, tmp);
- if (ret)
- goto err;
-
- /* set I2C master clock */
- ret = af9013_wr_reg(state, 0xd416, 0x14);
- if (ret)
- goto err;
-
- /* set 16 embx */
- ret = af9013_wr_reg_bits(state, 0xd700, 1, 1, 1);
- if (ret)
- goto err;
-
- /* set no trigger */
- ret = af9013_wr_reg_bits(state, 0xd700, 2, 1, 0);
- if (ret)
- goto err;
-
- /* set read-update bit for constellation */
- ret = af9013_wr_reg_bits(state, 0xd371, 1, 1, 1);
- if (ret)
- goto err;
-
- /* settings for mp2if */
- if (state->config.ts_mode == AF9013_TS_USB) {
- /* AF9015 split PSB to 1.5k + 0.5k */
- ret = af9013_wr_reg_bits(state, 0xd50b, 2, 1, 1);
- if (ret)
- goto err;
- } else {
- /* AF9013 change the output bit to data7 */
- ret = af9013_wr_reg_bits(state, 0xd500, 3, 1, 1);
- if (ret)
- goto err;
-
- /* AF9013 set mpeg to full speed */
- ret = af9013_wr_reg_bits(state, 0xd502, 4, 1, 1);
- if (ret)
- goto err;
- }
-
- ret = af9013_wr_reg_bits(state, 0xd520, 4, 1, 1);
- if (ret)
- goto err;
-
- /* load OFSM settings */
- dbg("%s: load ofsm settings", __func__);
- len = ARRAY_SIZE(ofsm_init);
- init = ofsm_init;
- for (i = 0; i < len; i++) {
- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
- init[i].len, init[i].val);
- if (ret)
- goto err;
- }
-
- /* load tuner specific settings */
- dbg("%s: load tuner specific settings", __func__);
- switch (state->config.tuner) {
- case AF9013_TUNER_MXL5003D:
- len = ARRAY_SIZE(tuner_init_mxl5003d);
- init = tuner_init_mxl5003d;
- break;
- case AF9013_TUNER_MXL5005D:
- case AF9013_TUNER_MXL5005R:
- case AF9013_TUNER_MXL5007T:
- len = ARRAY_SIZE(tuner_init_mxl5005);
- init = tuner_init_mxl5005;
- break;
- case AF9013_TUNER_ENV77H11D5:
- len = ARRAY_SIZE(tuner_init_env77h11d5);
- init = tuner_init_env77h11d5;
- break;
- case AF9013_TUNER_MT2060:
- len = ARRAY_SIZE(tuner_init_mt2060);
- init = tuner_init_mt2060;
- break;
- case AF9013_TUNER_MC44S803:
- len = ARRAY_SIZE(tuner_init_mc44s803);
- init = tuner_init_mc44s803;
- break;
- case AF9013_TUNER_QT1010:
- case AF9013_TUNER_QT1010A:
- len = ARRAY_SIZE(tuner_init_qt1010);
- init = tuner_init_qt1010;
- break;
- case AF9013_TUNER_MT2060_2:
- len = ARRAY_SIZE(tuner_init_mt2060_2);
- init = tuner_init_mt2060_2;
- break;
- case AF9013_TUNER_TDA18271:
- case AF9013_TUNER_TDA18218:
- len = ARRAY_SIZE(tuner_init_tda18271);
- init = tuner_init_tda18271;
- break;
- case AF9013_TUNER_UNKNOWN:
- default:
- len = ARRAY_SIZE(tuner_init_unknown);
- init = tuner_init_unknown;
- break;
- }
-
- for (i = 0; i < len; i++) {
- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos,
- init[i].len, init[i].val);
- if (ret)
- goto err;
- }
-
- /* TS mode */
- ret = af9013_wr_reg_bits(state, 0xd500, 1, 2, state->config.ts_mode);
- if (ret)
- goto err;
-
- /* enable lock led */
- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 1);
- if (ret)
- goto err;
-
- /* check if we support signal strength */
- if (!state->signal_strength_en) {
- ret = af9013_rd_reg_bits(state, 0x9bee, 0, 1,
- &state->signal_strength_en);
- if (ret)
- goto err;
- }
-
- /* read values needed for signal strength calculation */
- if (state->signal_strength_en && !state->rf_50) {
- ret = af9013_rd_reg(state, 0x9bbd, &state->rf_50);
- if (ret)
- goto err;
-
- ret = af9013_rd_reg(state, 0x9bd0, &state->rf_80);
- if (ret)
- goto err;
-
- ret = af9013_rd_reg(state, 0x9be2, &state->if_50);
- if (ret)
- goto err;
-
- ret = af9013_rd_reg(state, 0x9be4, &state->if_80);
- if (ret)
- goto err;
- }
-
- /* SNR */
- ret = af9013_wr_reg(state, 0xd2e2, 1);
- if (ret)
- goto err;
-
- /* BER / UCB */
- buf[0] = (10000 >> 0) & 0xff;
- buf[1] = (10000 >> 8) & 0xff;
- ret = af9013_wr_regs(state, 0xd385, buf, 2);
- if (ret)
- goto err;
-
- /* enable FEC monitor */
- ret = af9013_wr_reg_bits(state, 0xd392, 1, 1, 1);
- if (ret)
- goto err;
-
- state->first_tune = true;
- schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400));
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_sleep(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- int ret;
-
- dbg("%s", __func__);
-
- /* stop statistics polling */
- cancel_delayed_work_sync(&state->statistics_work);
-
- /* disable lock led */
- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 0);
- if (ret)
- goto err;
-
- /* power off */
- ret = af9013_power_ctrl(state, 0);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- int ret;
- struct af9013_state *state = fe->demodulator_priv;
-
- dbg("%s: enable=%d", __func__, enable);
-
- /* gate already open or close */
- if (state->i2c_gate_state == enable)
- return 0;
-
- if (state->config.ts_mode == AF9013_TS_USB)
- ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable);
- else
- ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable);
- if (ret)
- goto err;
-
- state->i2c_gate_state = enable;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static void af9013_release(struct dvb_frontend *fe)
-{
- struct af9013_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops af9013_ops;
-
-static int af9013_download_firmware(struct af9013_state *state)
-{
- int i, len, remaining, ret;
- const struct firmware *fw;
- u16 checksum = 0;
- u8 val;
- u8 fw_params[4];
- u8 *fw_file = AF9013_DEFAULT_FIRMWARE;
-
- msleep(100);
- /* check whether firmware is already running */
- ret = af9013_rd_reg(state, 0x98be, &val);
- if (ret)
- goto err;
- else
- dbg("%s: firmware status=%02x", __func__, val);
-
- if (val == 0x0c) /* fw is running, no need for download */
- goto exit;
-
- info("found a '%s' in cold state, will try to load a firmware",
- af9013_ops.info.name);
-
- /* request the firmware, this will block and timeout */
- ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
- if (ret) {
- err("did not find the firmware file. (%s) "
- "Please see linux/Documentation/dvb/ for more details" \
- " on firmware-problems. (%d)",
- fw_file, ret);
- goto err;
- }
-
- info("downloading firmware from file '%s'", fw_file);
-
- /* calc checksum */
- for (i = 0; i < fw->size; i++)
- checksum += fw->data[i];
-
- fw_params[0] = checksum >> 8;
- fw_params[1] = checksum & 0xff;
- fw_params[2] = fw->size >> 8;
- fw_params[3] = fw->size & 0xff;
-
- /* write fw checksum & size */
- ret = af9013_write_ofsm_regs(state, 0x50fc,
- fw_params, sizeof(fw_params));
- if (ret)
- goto err_release;
-
- #define FW_ADDR 0x5100 /* firmware start address */
- #define LEN_MAX 16 /* max packet size */
- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
- len = remaining;
- if (len > LEN_MAX)
- len = LEN_MAX;
-
- ret = af9013_write_ofsm_regs(state,
- FW_ADDR + fw->size - remaining,
- (u8 *) &fw->data[fw->size - remaining], len);
- if (ret) {
- err("firmware download failed:%d", ret);
- goto err_release;
- }
- }
-
- /* request boot firmware */
- ret = af9013_wr_reg(state, 0xe205, 1);
- if (ret)
- goto err_release;
-
- for (i = 0; i < 15; i++) {
- msleep(100);
-
- /* check firmware status */
- ret = af9013_rd_reg(state, 0x98be, &val);
- if (ret)
- goto err_release;
-
- dbg("%s: firmware status=%02x", __func__, val);
-
- if (val == 0x0c || val == 0x04) /* success or fail */
- break;
- }
-
- if (val == 0x04) {
- err("firmware did not run");
- ret = -ENODEV;
- } else if (val != 0x0c) {
- err("firmware boot timeout");
- ret = -ENODEV;
- }
-
-err_release:
- release_firmware(fw);
-err:
-exit:
- if (!ret)
- info("found a '%s' in warm state.", af9013_ops.info.name);
- return ret;
-}
-
-struct dvb_frontend *af9013_attach(const struct af9013_config *config,
- struct i2c_adapter *i2c)
-{
- int ret;
- struct af9013_state *state = NULL;
- u8 buf[4], i;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL);
- if (state == NULL)
- goto err;
-
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->config, config, sizeof(struct af9013_config));
-
- /* download firmware */
- if (state->config.ts_mode != AF9013_TS_USB) {
- ret = af9013_download_firmware(state);
- if (ret)
- goto err;
- }
-
- /* firmware version */
- ret = af9013_rd_regs(state, 0x5103, buf, 4);
- if (ret)
- goto err;
-
- info("firmware version %d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]);
-
- /* set GPIOs */
- for (i = 0; i < sizeof(state->config.gpio); i++) {
- ret = af9013_set_gpio(state, i, state->config.gpio[i]);
- if (ret)
- goto err;
- }
-
- /* create dvb_frontend */
- memcpy(&state->fe.ops, &af9013_ops,
- sizeof(struct dvb_frontend_ops));
- state->fe.demodulator_priv = state;
-
- INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work);
-
- return &state->fe;
-err:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(af9013_attach);
-
-static struct dvb_frontend_ops af9013_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Afatech AF9013",
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 250000,
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_MUTE_TS
- },
-
- .release = af9013_release,
-
- .init = af9013_init,
- .sleep = af9013_sleep,
-
- .get_tune_settings = af9013_get_tune_settings,
- .set_frontend = af9013_set_frontend,
- .get_frontend = af9013_get_frontend,
-
- .read_status = af9013_read_status,
- .read_snr = af9013_read_snr,
- .read_signal_strength = af9013_read_signal_strength,
- .read_ber = af9013_read_ber,
- .read_ucblocks = af9013_read_ucblocks,
-
- .i2c_gate_ctrl = af9013_i2c_gate_ctrl,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h
deleted file mode 100644
index b973fc5a0384..000000000000
--- a/drivers/media/dvb/frontends/af9013.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Afatech AF9013 demodulator driver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * Thanks to Afatech who kindly provided information.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 AF9013_H
-#define AF9013_H
-
-#include <linux/dvb/frontend.h>
-
-/* AF9013/5 GPIOs (mostly guessed)
- demod#1-gpio#0 - set demod#2 i2c-addr for dual devices
- demod#1-gpio#1 - xtal setting (?)
- demod#1-gpio#3 - tuner#1
- demod#2-gpio#0 - tuner#2
- demod#2-gpio#1 - xtal setting (?)
-*/
-
-struct af9013_config {
- /*
- * I2C address
- */
- u8 i2c_addr;
-
- /*
- * clock
- * 20480000, 25000000, 28000000, 28800000
- */
- u32 clock;
-
- /*
- * tuner
- */
-#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */
-#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */
-#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */
-#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */
-#define AF9013_TUNER_MT2060 130 /* Microtune */
-#define AF9013_TUNER_MC44S803 133 /* Freescale */
-#define AF9013_TUNER_QT1010 134 /* Quantek */
-#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */
-#define AF9013_TUNER_MT2060_2 147 /* Microtune */
-#define AF9013_TUNER_TDA18271 156 /* NXP */
-#define AF9013_TUNER_QT1010A 162 /* Quantek */
-#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */
-#define AF9013_TUNER_TDA18218 179 /* NXP */
- u8 tuner;
-
- /*
- * IF frequency
- */
- u32 if_frequency;
-
- /*
- * TS settings
- */
-#define AF9013_TS_USB 0
-#define AF9013_TS_PARALLEL 1
-#define AF9013_TS_SERIAL 2
- u8 ts_mode:2;
-
- /*
- * input spectrum inversion
- */
- bool spec_inv;
-
- /*
- * firmware API version
- */
- u8 api_version[4];
-
- /*
- * GPIOs
- */
-#define AF9013_GPIO_ON (1 << 0)
-#define AF9013_GPIO_EN (1 << 1)
-#define AF9013_GPIO_O (1 << 2)
-#define AF9013_GPIO_I (1 << 3)
-#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN)
-#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
-#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN)
-#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O)
- u8 gpio[4];
-};
-
-#if defined(CONFIG_DVB_AF9013) || \
- (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE))
-extern struct dvb_frontend *af9013_attach(const struct af9013_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *af9013_attach(
-const struct af9013_config *config, struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif /* CONFIG_DVB_AF9013 */
-
-#endif /* AF9013_H */
diff --git a/drivers/media/dvb/frontends/af9013_priv.h b/drivers/media/dvb/frontends/af9013_priv.h
deleted file mode 100644
index fa848af6e9b4..000000000000
--- a/drivers/media/dvb/frontends/af9013_priv.h
+++ /dev/null
@@ -1,922 +0,0 @@
-/*
- * Afatech AF9013 demodulator driver
- *
- * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * Thanks to Afatech who kindly provided information.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 AF9013_PRIV_H
-#define AF9013_PRIV_H
-
-#include "dvb_frontend.h"
-#include "af9013.h"
-#include <linux/firmware.h>
-
-#define LOG_PREFIX "af9013"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (af9013_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw"
-
-struct af9013_reg_bit {
- u16 addr;
- u8 pos:4;
- u8 len:4;
- u8 val;
-};
-
-struct af9013_snr {
- u32 val;
- u8 snr;
-};
-
-struct af9013_coeff {
- u32 clock;
- u32 bandwidth_hz;
- u8 val[24];
-};
-
-/* pre-calculated coeff lookup table */
-static const struct af9013_coeff coeff_lut[] = {
- /* 28.800 MHz */
- { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14,
- 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a,
- 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } },
- { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71,
- 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38,
- 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } },
- { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf,
- 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7,
- 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } },
- /* 20.480 MHz */
- { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24,
- 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92,
- 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } },
- { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40,
- 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00,
- 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } },
- { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b,
- 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d,
- 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } },
- /* 28.000 MHz */
- { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39,
- 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f,
- 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } },
- { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92,
- 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49,
- 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } },
- { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb,
- 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63,
- 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } },
- /* 25.000 MHz */
- { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9,
- 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e,
- 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } },
- { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e,
- 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7,
- 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } },
- { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63,
- 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f,
- 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } },
-};
-
-/* QPSK SNR lookup table */
-static const struct af9013_snr qpsk_snr_lut[] = {
- { 0x000000, 0 },
- { 0x0b4771, 0 },
- { 0x0c1aed, 1 },
- { 0x0d0d27, 2 },
- { 0x0e4d19, 3 },
- { 0x0e5da8, 4 },
- { 0x107097, 5 },
- { 0x116975, 6 },
- { 0x1252d9, 7 },
- { 0x131fa4, 8 },
- { 0x13d5e1, 9 },
- { 0x148e53, 10 },
- { 0x15358b, 11 },
- { 0x15dd29, 12 },
- { 0x168112, 13 },
- { 0x170b61, 14 },
- { 0xffffff, 15 },
-};
-
-/* QAM16 SNR lookup table */
-static const struct af9013_snr qam16_snr_lut[] = {
- { 0x000000, 0 },
- { 0x05eb62, 5 },
- { 0x05fecf, 6 },
- { 0x060b80, 7 },
- { 0x062501, 8 },
- { 0x064865, 9 },
- { 0x069604, 10 },
- { 0x06f356, 11 },
- { 0x07706a, 12 },
- { 0x0804d3, 13 },
- { 0x089d1a, 14 },
- { 0x093e3d, 15 },
- { 0x09e35d, 16 },
- { 0x0a7c3c, 17 },
- { 0x0afaf8, 18 },
- { 0x0b719d, 19 },
- { 0xffffff, 20 },
-};
-
-/* QAM64 SNR lookup table */
-static const struct af9013_snr qam64_snr_lut[] = {
- { 0x000000, 0 },
- { 0x03109b, 12 },
- { 0x0310d4, 13 },
- { 0x031920, 14 },
- { 0x0322d0, 15 },
- { 0x0339fc, 16 },
- { 0x0364a1, 17 },
- { 0x038bcc, 18 },
- { 0x03c7d3, 19 },
- { 0x0408cc, 20 },
- { 0x043bed, 21 },
- { 0x048061, 22 },
- { 0x04be95, 23 },
- { 0x04fa7d, 24 },
- { 0x052405, 25 },
- { 0x05570d, 26 },
- { 0xffffff, 27 },
-};
-
-static const struct af9013_reg_bit ofsm_init[] = {
- { 0xd73a, 0, 8, 0xa1 },
- { 0xd73b, 0, 8, 0x1f },
- { 0xd73c, 4, 4, 0x0a },
- { 0xd732, 3, 1, 0x00 },
- { 0xd731, 4, 2, 0x03 },
- { 0xd73d, 7, 1, 0x01 },
- { 0xd740, 0, 1, 0x00 },
- { 0xd740, 1, 1, 0x00 },
- { 0xd740, 2, 1, 0x00 },
- { 0xd740, 3, 1, 0x01 },
- { 0xd3c1, 4, 1, 0x01 },
- { 0x9124, 0, 8, 0x58 },
- { 0x9125, 0, 2, 0x02 },
- { 0xd3a2, 0, 8, 0x00 },
- { 0xd3a3, 0, 8, 0x04 },
- { 0xd305, 0, 8, 0x32 },
- { 0xd306, 0, 8, 0x10 },
- { 0xd304, 0, 8, 0x04 },
- { 0x9112, 0, 1, 0x01 },
- { 0x911d, 0, 1, 0x01 },
- { 0x911a, 0, 1, 0x01 },
- { 0x911b, 0, 1, 0x01 },
- { 0x9bce, 0, 4, 0x02 },
- { 0x9116, 0, 1, 0x01 },
- { 0x9122, 0, 8, 0xd0 },
- { 0xd2e0, 0, 8, 0xd0 },
- { 0xd2e9, 0, 4, 0x0d },
- { 0xd38c, 0, 8, 0xfc },
- { 0xd38d, 0, 8, 0x00 },
- { 0xd38e, 0, 8, 0x7e },
- { 0xd38f, 0, 8, 0x00 },
- { 0xd390, 0, 8, 0x2f },
- { 0xd145, 4, 1, 0x01 },
- { 0xd1a9, 4, 1, 0x01 },
- { 0xd158, 5, 3, 0x01 },
- { 0xd159, 0, 6, 0x06 },
- { 0xd167, 0, 8, 0x00 },
- { 0xd168, 0, 4, 0x07 },
- { 0xd1c3, 5, 3, 0x00 },
- { 0xd1c4, 0, 6, 0x00 },
- { 0xd1c5, 0, 7, 0x10 },
- { 0xd1c6, 0, 3, 0x02 },
- { 0xd080, 2, 5, 0x03 },
- { 0xd081, 4, 4, 0x09 },
- { 0xd098, 4, 4, 0x0f },
- { 0xd098, 0, 4, 0x03 },
- { 0xdbc0, 4, 1, 0x01 },
- { 0xdbc7, 0, 8, 0x08 },
- { 0xdbc8, 4, 4, 0x00 },
- { 0xdbc9, 0, 5, 0x01 },
- { 0xd280, 0, 8, 0xe0 },
- { 0xd281, 0, 8, 0xff },
- { 0xd282, 0, 8, 0xff },
- { 0xd283, 0, 8, 0xc3 },
- { 0xd284, 0, 8, 0xff },
- { 0xd285, 0, 4, 0x01 },
- { 0xd0f0, 0, 7, 0x1a },
- { 0xd0f1, 4, 1, 0x01 },
- { 0xd0f2, 0, 8, 0x0c },
- { 0xd101, 5, 3, 0x06 },
- { 0xd103, 0, 4, 0x08 },
- { 0xd0f8, 0, 7, 0x20 },
- { 0xd111, 5, 1, 0x00 },
- { 0xd111, 6, 1, 0x00 },
- { 0x910b, 0, 8, 0x0a },
- { 0x9115, 0, 8, 0x02 },
- { 0x910c, 0, 8, 0x02 },
- { 0x910d, 0, 8, 0x08 },
- { 0x910e, 0, 8, 0x0a },
- { 0x9bf6, 0, 8, 0x06 },
- { 0x9bf8, 0, 8, 0x02 },
- { 0x9bf7, 0, 8, 0x05 },
- { 0x9bf9, 0, 8, 0x0f },
- { 0x9bfc, 0, 8, 0x13 },
- { 0x9bd3, 0, 8, 0xff },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
-};
-
-/* Panasonic ENV77H11D5 tuner init
- AF9013_TUNER_ENV77H11D5 = 129 */
-static const struct af9013_reg_bit tuner_init_env77h11d5[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x03 },
- { 0x9bbe, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0xd015, 0, 8, 0x50 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0xeb },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf4 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0xeb },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0x52 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
-};
-
-/* Microtune MT2060 tuner init
- AF9013_TUNER_MT2060 = 130 */
-static const struct af9013_reg_bit tuner_init_mt2060[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x07 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bbe, 0, 1, 0x00 },
- { 0x9bcc, 0, 1, 0x00 },
- { 0x9bb9, 0, 8, 0x75 },
- { 0x9bcd, 0, 8, 0x24 },
- { 0x9bff, 0, 8, 0x30 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x32 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x36 },
- { 0xd00d, 0, 2, 0x03 },
- { 0xd00a, 0, 8, 0x35 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x90 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x36 },
- { 0x9bc6, 0, 8, 0x03 },
- { 0x9bba, 0, 8, 0xc9 },
- { 0x9bc9, 0, 8, 0x79 },
- { 0xd011, 0, 8, 0x10 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0x45 },
- { 0xd014, 0, 2, 0x03 },
- { 0xd040, 0, 8, 0x98 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0xcf },
- { 0xd043, 0, 2, 0x03 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xcc },
- { 0x9be4, 0, 8, 0xa0 },
- { 0x9bbd, 0, 8, 0x8e },
- { 0x9be2, 0, 8, 0x4d },
- { 0x9bee, 0, 1, 0x01 },
-};
-
-/* Microtune MT2060 tuner init
- AF9013_TUNER_MT2060_2 = 147 */
-static const struct af9013_reg_bit tuner_init_mt2060_2[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x06 },
- { 0x9bbe, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x32 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x36 },
- { 0xd00d, 0, 2, 0x03 },
- { 0xd00a, 0, 8, 0x35 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x90 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x36 },
- { 0x9bc6, 0, 8, 0x03 },
- { 0x9bba, 0, 8, 0xc9 },
- { 0x9bc9, 0, 8, 0x79 },
- { 0xd011, 0, 8, 0x10 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0x45 },
- { 0xd014, 0, 2, 0x03 },
- { 0xd040, 0, 8, 0x98 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0xcf },
- { 0xd043, 0, 2, 0x03 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 8, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x96 },
- { 0xd054, 0, 8, 0x46 },
- { 0xd045, 7, 1, 0x00 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
-};
-
-/* MaxLinear MXL5003 tuner init
- AF9013_TUNER_MXL5003D = 3 */
-static const struct af9013_reg_bit tuner_init_mxl5003d[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x09 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bfc, 0, 8, 0x0f },
- { 0x9bf6, 0, 8, 0x01 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0xd015, 0, 8, 0x33 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x40 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x6c },
- { 0xd007, 0, 2, 0x00 },
- { 0xd00c, 0, 8, 0x3d },
- { 0xd00d, 0, 2, 0x00 },
- { 0xd00a, 0, 8, 0x45 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x07 },
- { 0x9bc8, 0, 8, 0x52 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x3d },
- { 0x9bc6, 0, 8, 0x00 },
- { 0x9bba, 0, 8, 0xa2 },
- { 0x9bc9, 0, 8, 0xa0 },
- { 0xd011, 0, 8, 0x56 },
- { 0xd012, 0, 2, 0x00 },
- { 0xd013, 0, 8, 0x50 },
- { 0xd014, 0, 2, 0x00 },
- { 0xd040, 0, 8, 0x56 },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0x50 },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 8, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
-};
-
-/* MaxLinear MXL5005S & MXL5007T tuner init
- AF9013_TUNER_MXL5005D = 13
- AF9013_TUNER_MXL5005R = 30
- AF9013_TUNER_MXL5007T = 177 */
-static const struct af9013_reg_bit tuner_init_mxl5005[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x07 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x28 },
- { 0x9bff, 0, 8, 0x24 },
- { 0xd015, 0, 8, 0x40 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x40 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x73 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0xfa },
- { 0xd00d, 0, 2, 0x01 },
- { 0xd00a, 0, 8, 0xff },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x23 },
- { 0x9bc8, 0, 8, 0x55 },
- { 0x9bc3, 0, 8, 0x01 },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0xfa },
- { 0x9bc6, 0, 8, 0x01 },
- { 0x9bba, 0, 8, 0xff },
- { 0x9bc9, 0, 8, 0xff },
- { 0x9bd3, 0, 8, 0x95 },
- { 0xd011, 0, 8, 0x70 },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xfb },
- { 0xd014, 0, 2, 0x01 },
- { 0xd040, 0, 8, 0x70 },
- { 0xd041, 0, 2, 0x01 },
- { 0xd042, 0, 8, 0xfb },
- { 0xd043, 0, 2, 0x01 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0x93 },
- { 0x9be4, 0, 8, 0xfe },
- { 0x9bbd, 0, 8, 0x63 },
- { 0x9be2, 0, 8, 0xfe },
- { 0x9bee, 0, 1, 0x01 },
-};
-
-/* Quantek QT1010 tuner init
- AF9013_TUNER_QT1010 = 134
- AF9013_TUNER_QT1010A = 162 */
-static const struct af9013_reg_bit tuner_init_qt1010[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x09 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x28 },
- { 0x9bff, 0, 8, 0x20 },
- { 0xd008, 0, 8, 0x0f },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x99 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x0f },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0x50 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x00 },
- { 0x9bc8, 0, 8, 0x00 },
- { 0x9bc3, 0, 8, 0x0f },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x0f },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bba, 0, 8, 0xc5 },
- { 0x9bc9, 0, 8, 0xff },
- { 0xd011, 0, 8, 0x58 },
- { 0xd012, 0, 2, 0x02 },
- { 0xd013, 0, 8, 0x89 },
- { 0xd014, 0, 2, 0x01 },
- { 0xd040, 0, 8, 0x58 },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x89 },
- { 0xd043, 0, 2, 0x01 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xcd },
- { 0x9be4, 0, 8, 0xbb },
- { 0x9bbd, 0, 8, 0x93 },
- { 0x9be2, 0, 8, 0x80 },
- { 0x9bee, 0, 1, 0x01 },
-};
-
-/* Freescale MC44S803 tuner init
- AF9013_TUNER_MC44S803 = 133 */
-static const struct af9013_reg_bit tuner_init_mc44s803[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x06 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x00 },
- { 0x9be3, 0, 8, 0x00 },
- { 0x9bf6, 0, 8, 0x01 },
- { 0x9bf8, 0, 8, 0x02 },
- { 0x9bf9, 0, 8, 0x02 },
- { 0x9bfc, 0, 8, 0x1f },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x24 },
- { 0x9bff, 0, 8, 0x24 },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0x01 },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x7b },
- { 0xd007, 0, 2, 0x00 },
- { 0xd00c, 0, 8, 0x7c },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xfe },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bc7, 0, 8, 0x08 },
- { 0x9bc8, 0, 8, 0x9a },
- { 0x9bc3, 0, 8, 0x01 },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x7c },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bba, 0, 8, 0xfc },
- { 0x9bc9, 0, 8, 0xaa },
- { 0xd011, 0, 8, 0x6b },
- { 0xd012, 0, 2, 0x00 },
- { 0xd013, 0, 8, 0x88 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x6b },
- { 0xd041, 0, 2, 0x00 },
- { 0xd042, 0, 8, 0x7c },
- { 0xd043, 0, 2, 0x02 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0x9e },
- { 0x9be4, 0, 8, 0xff },
- { 0x9bbd, 0, 8, 0x9e },
- { 0x9be2, 0, 8, 0x25 },
- { 0x9bee, 0, 1, 0x01 },
- { 0xd73b, 3, 1, 0x00 },
-};
-
-/* unknown, probably for tin can tuner, tuner init
- AF9013_TUNER_UNKNOWN = 140 */
-static const struct af9013_reg_bit tuner_init_unknown[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x02 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x18 },
- { 0x9bff, 0, 8, 0x2c },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x00 },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf6 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc8, 0, 8, 0xaa },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x00 },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0xf0 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
-};
-
-/* NXP TDA18271 & TDA18218 tuner init
- AF9013_TUNER_TDA18271 = 156
- AF9013_TUNER_TDA18218 = 179 */
-static const struct af9013_reg_bit tuner_init_tda18271[] = {
- { 0x9bd5, 0, 8, 0x01 },
- { 0x9bd6, 0, 8, 0x04 },
- { 0xd1a0, 1, 1, 0x01 },
- { 0xd000, 0, 1, 0x01 },
- { 0xd000, 1, 1, 0x00 },
- { 0xd001, 1, 1, 0x01 },
- { 0xd001, 0, 1, 0x00 },
- { 0xd001, 5, 1, 0x00 },
- { 0xd002, 0, 5, 0x19 },
- { 0xd003, 0, 5, 0x1a },
- { 0xd004, 0, 5, 0x19 },
- { 0xd005, 0, 5, 0x1a },
- { 0xd00e, 0, 5, 0x10 },
- { 0xd00f, 0, 3, 0x04 },
- { 0xd00f, 3, 3, 0x05 },
- { 0xd010, 0, 3, 0x04 },
- { 0xd010, 3, 3, 0x05 },
- { 0xd016, 4, 4, 0x03 },
- { 0xd01f, 0, 6, 0x0a },
- { 0xd020, 0, 6, 0x0a },
- { 0x9bda, 0, 8, 0x01 },
- { 0x9be3, 0, 8, 0x01 },
- { 0xd1a0, 1, 1, 0x00 },
- { 0x9bbe, 0, 1, 0x01 },
- { 0x9bcc, 0, 1, 0x01 },
- { 0x9bb9, 0, 8, 0x00 },
- { 0x9bcd, 0, 8, 0x18 },
- { 0x9bff, 0, 8, 0x2c },
- { 0xd015, 0, 8, 0x46 },
- { 0xd016, 0, 1, 0x00 },
- { 0xd044, 0, 8, 0x46 },
- { 0xd045, 0, 1, 0x00 },
- { 0xd008, 0, 8, 0xdf },
- { 0xd009, 0, 2, 0x02 },
- { 0xd006, 0, 8, 0x44 },
- { 0xd007, 0, 2, 0x01 },
- { 0xd00c, 0, 8, 0x00 },
- { 0xd00d, 0, 2, 0x02 },
- { 0xd00a, 0, 8, 0xf6 },
- { 0xd00b, 0, 2, 0x01 },
- { 0x9bba, 0, 8, 0xf9 },
- { 0x9bc8, 0, 8, 0xaa },
- { 0x9bc3, 0, 8, 0xdf },
- { 0x9bc4, 0, 8, 0x02 },
- { 0x9bc5, 0, 8, 0x00 },
- { 0x9bc6, 0, 8, 0x02 },
- { 0x9bc9, 0, 8, 0xf0 },
- { 0xd011, 0, 8, 0x3c },
- { 0xd012, 0, 2, 0x01 },
- { 0xd013, 0, 8, 0xf7 },
- { 0xd014, 0, 2, 0x02 },
- { 0xd040, 0, 8, 0x0b },
- { 0xd041, 0, 2, 0x02 },
- { 0xd042, 0, 8, 0x4d },
- { 0xd043, 0, 2, 0x00 },
- { 0xd045, 1, 1, 0x00 },
- { 0x9bcf, 0, 1, 0x01 },
- { 0xd045, 2, 1, 0x01 },
- { 0xd04f, 0, 8, 0x9a },
- { 0xd050, 0, 1, 0x01 },
- { 0xd051, 0, 8, 0x5a },
- { 0xd052, 0, 1, 0x01 },
- { 0xd053, 0, 8, 0x50 },
- { 0xd054, 0, 8, 0x46 },
- { 0x9bd7, 0, 8, 0x0a },
- { 0x9bd8, 0, 8, 0x14 },
- { 0x9bd9, 0, 8, 0x08 },
- { 0x9bd0, 0, 8, 0xa8 },
- { 0x9be4, 0, 8, 0x7f },
- { 0x9bbd, 0, 8, 0xa8 },
- { 0x9be2, 0, 8, 0x20 },
- { 0x9bee, 0, 1, 0x01 },
-};
-
-#endif /* AF9013_PRIV_H */
diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c
deleted file mode 100644
index a38998286260..000000000000
--- a/drivers/media/dvb/frontends/af9033.c
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
- * Afatech AF9033 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "af9033_priv.h"
-
-struct af9033_state {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct af9033_config cfg;
-
- u32 bandwidth_hz;
- bool ts_mode_parallel;
- bool ts_mode_serial;
-
- u32 ber;
- u32 ucb;
- unsigned long last_stat_check;
-};
-
-/* write multiple registers */
-static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
- int len)
-{
- int ret;
- u8 buf[3 + len];
- struct i2c_msg msg[1] = {
- {
- .addr = state->cfg.i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = (reg >> 16) & 0xff;
- buf[1] = (reg >> 8) & 0xff;
- buf[2] = (reg >> 0) & 0xff;
- memcpy(&buf[3], val, len);
-
- ret = i2c_transfer(state->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- printk(KERN_WARNING "%s: i2c wr failed=%d reg=%06x len=%d\n",
- __func__, ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* read multiple registers */
-static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff,
- (reg >> 0) & 0xff };
- struct i2c_msg msg[2] = {
- {
- .addr = state->cfg.i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf
- }, {
- .addr = state->cfg.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val
- }
- };
-
- ret = i2c_transfer(state->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- printk(KERN_WARNING "%s: i2c rd failed=%d reg=%06x len=%d\n",
- __func__, ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-
-/* write single register */
-static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val)
-{
- return af9033_wr_regs(state, reg, &val, 1);
-}
-
-/* read single register */
-static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val)
-{
- return af9033_rd_regs(state, reg, val, 1);
-}
-
-/* write single register with mask */
-static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
- u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = af9033_rd_regs(state, reg, &tmp, 1);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return af9033_wr_regs(state, reg, &val, 1);
-}
-
-/* read single register with mask */
-static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val,
- u8 mask)
-{
- int ret, i;
- u8 tmp;
-
- ret = af9033_rd_regs(state, reg, &tmp, 1);
- if (ret)
- return ret;
-
- tmp &= mask;
-
- /* find position of the first bit */
- for (i = 0; i < 8; i++) {
- if ((mask >> i) & 0x01)
- break;
- }
- *val = tmp >> i;
-
- return 0;
-}
-
-static u32 af9033_div(u32 a, u32 b, u32 x)
-{
- u32 r = 0, c = 0, i;
-
- pr_debug("%s: a=%d b=%d x=%d\n", __func__, a, b, x);
-
- if (a > b) {
- c = a / b;
- a = a - c * b;
- }
-
- for (i = 0; i < x; i++) {
- if (a >= b) {
- r += 1;
- a -= b;
- }
- a <<= 1;
- r <<= 1;
- }
- r = (c << (u32)x) + r;
-
- pr_debug("%s: a=%d b=%d x=%d r=%d r=%x\n", __func__, a, b, x, r, r);
-
- return r;
-}
-
-static void af9033_release(struct dvb_frontend *fe)
-{
- struct af9033_state *state = fe->demodulator_priv;
-
- kfree(state);
-}
-
-static int af9033_init(struct dvb_frontend *fe)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret, i, len;
- const struct reg_val *init;
- u8 buf[4];
- u32 adc_cw, clock_cw;
- struct reg_val_mask tab[] = {
- { 0x80fb24, 0x00, 0x08 },
- { 0x80004c, 0x00, 0xff },
- { 0x00f641, state->cfg.tuner, 0xff },
- { 0x80f5ca, 0x01, 0x01 },
- { 0x80f715, 0x01, 0x01 },
- { 0x00f41f, 0x04, 0x04 },
- { 0x00f41a, 0x01, 0x01 },
- { 0x80f731, 0x00, 0x01 },
- { 0x00d91e, 0x00, 0x01 },
- { 0x00d919, 0x00, 0x01 },
- { 0x80f732, 0x00, 0x01 },
- { 0x00d91f, 0x00, 0x01 },
- { 0x00d91a, 0x00, 0x01 },
- { 0x80f730, 0x00, 0x01 },
- { 0x80f778, 0x00, 0xff },
- { 0x80f73c, 0x01, 0x01 },
- { 0x80f776, 0x00, 0x01 },
- { 0x00d8fd, 0x01, 0xff },
- { 0x00d830, 0x01, 0xff },
- { 0x00d831, 0x00, 0xff },
- { 0x00d832, 0x00, 0xff },
- { 0x80f985, state->ts_mode_serial, 0x01 },
- { 0x80f986, state->ts_mode_parallel, 0x01 },
- { 0x00d827, 0x00, 0xff },
- { 0x00d829, 0x00, 0xff },
- };
-
- /* program clock control */
- clock_cw = af9033_div(state->cfg.clock, 1000000ul, 19ul);
- buf[0] = (clock_cw >> 0) & 0xff;
- buf[1] = (clock_cw >> 8) & 0xff;
- buf[2] = (clock_cw >> 16) & 0xff;
- buf[3] = (clock_cw >> 24) & 0xff;
-
- pr_debug("%s: clock=%d clock_cw=%08x\n", __func__, state->cfg.clock,
- clock_cw);
-
- ret = af9033_wr_regs(state, 0x800025, buf, 4);
- if (ret < 0)
- goto err;
-
- /* program ADC control */
- for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
- if (clock_adc_lut[i].clock == state->cfg.clock)
- break;
- }
-
- adc_cw = af9033_div(clock_adc_lut[i].adc, 1000000ul, 19ul);
- buf[0] = (adc_cw >> 0) & 0xff;
- buf[1] = (adc_cw >> 8) & 0xff;
- buf[2] = (adc_cw >> 16) & 0xff;
-
- pr_debug("%s: adc=%d adc_cw=%06x\n", __func__, clock_adc_lut[i].adc,
- adc_cw);
-
- ret = af9033_wr_regs(state, 0x80f1cd, buf, 3);
- if (ret < 0)
- goto err;
-
- /* program register table */
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret < 0)
- goto err;
- }
-
- /* settings for TS interface */
- if (state->cfg.ts_mode == AF9033_TS_MODE_USB) {
- ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01);
- if (ret < 0)
- goto err;
- } else {
- ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01);
- if (ret < 0)
- goto err;
- }
-
- /* load OFSM settings */
- pr_debug("%s: load ofsm settings\n", __func__);
- len = ARRAY_SIZE(ofsm_init);
- init = ofsm_init;
- for (i = 0; i < len; i++) {
- ret = af9033_wr_reg(state, init[i].reg, init[i].val);
- if (ret < 0)
- goto err;
- }
-
- /* load tuner specific settings */
- pr_debug("%s: load tuner specific settings\n",
- __func__);
- switch (state->cfg.tuner) {
- case AF9033_TUNER_TUA9001:
- len = ARRAY_SIZE(tuner_init_tua9001);
- init = tuner_init_tua9001;
- break;
- case AF9033_TUNER_FC0011:
- len = ARRAY_SIZE(tuner_init_fc0011);
- init = tuner_init_fc0011;
- break;
- case AF9033_TUNER_MXL5007T:
- len = ARRAY_SIZE(tuner_init_mxl5007t);
- init = tuner_init_mxl5007t;
- break;
- case AF9033_TUNER_TDA18218:
- len = ARRAY_SIZE(tuner_init_tda18218);
- init = tuner_init_tda18218;
- break;
- default:
- pr_debug("%s: unsupported tuner ID=%d\n", __func__,
- state->cfg.tuner);
- ret = -ENODEV;
- goto err;
- }
-
- for (i = 0; i < len; i++) {
- ret = af9033_wr_reg(state, init[i].reg, init[i].val);
- if (ret < 0)
- goto err;
- }
-
- state->bandwidth_hz = 0; /* force to program all parameters */
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_sleep(struct dvb_frontend *fe)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret, i;
- u8 tmp;
-
- ret = af9033_wr_reg(state, 0x80004c, 1);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg(state, 0x800000, 0);
- if (ret < 0)
- goto err;
-
- for (i = 100, tmp = 1; i && tmp; i--) {
- ret = af9033_rd_reg(state, 0x80004c, &tmp);
- if (ret < 0)
- goto err;
-
- usleep_range(200, 10000);
- }
-
- pr_debug("%s: loop=%d\n", __func__, i);
-
- if (i == 0) {
- ret = -ETIMEDOUT;
- goto err;
- }
-
- ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08);
- if (ret < 0)
- goto err;
-
- /* prevent current leak (?) */
- if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
- /* enable parallel TS */
- ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01);
- if (ret < 0)
- goto err;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- fesettings->min_delay_ms = 800;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
-
- return 0;
-}
-
-static int af9033_set_frontend(struct dvb_frontend *fe)
-{
- struct af9033_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, spec_inv;
- u8 tmp, buf[3], bandwidth_reg_val;
- u32 if_frequency, freq_cw, adc_freq;
-
- pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency,
- c->bandwidth_hz);
-
- /* check bandwidth */
- switch (c->bandwidth_hz) {
- case 6000000:
- bandwidth_reg_val = 0x00;
- break;
- case 7000000:
- bandwidth_reg_val = 0x01;
- break;
- case 8000000:
- bandwidth_reg_val = 0x02;
- break;
- default:
- pr_debug("%s: invalid bandwidth_hz\n", __func__);
- ret = -EINVAL;
- goto err;
- }
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- /* program CFOE coefficients */
- if (c->bandwidth_hz != state->bandwidth_hz) {
- for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
- if (coeff_lut[i].clock == state->cfg.clock &&
- coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
- break;
- }
- }
- ret = af9033_wr_regs(state, 0x800001,
- coeff_lut[i].val, sizeof(coeff_lut[i].val));
- }
-
- /* program frequency control */
- if (c->bandwidth_hz != state->bandwidth_hz) {
- spec_inv = state->cfg.spec_inv ? -1 : 1;
-
- for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
- if (clock_adc_lut[i].clock == state->cfg.clock)
- break;
- }
- adc_freq = clock_adc_lut[i].adc;
-
- /* get used IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency)
- fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
- else
- if_frequency = 0;
-
- while (if_frequency > (adc_freq / 2))
- if_frequency -= adc_freq;
-
- if (if_frequency >= 0)
- spec_inv *= -1;
- else
- if_frequency *= -1;
-
- freq_cw = af9033_div(if_frequency, adc_freq, 23ul);
-
- if (spec_inv == -1)
- freq_cw *= -1;
-
- /* get adc multiplies */
- ret = af9033_rd_reg(state, 0x800045, &tmp);
- if (ret < 0)
- goto err;
-
- if (tmp == 1)
- freq_cw /= 2;
-
- buf[0] = (freq_cw >> 0) & 0xff;
- buf[1] = (freq_cw >> 8) & 0xff;
- buf[2] = (freq_cw >> 16) & 0x7f;
- ret = af9033_wr_regs(state, 0x800029, buf, 3);
- if (ret < 0)
- goto err;
-
- state->bandwidth_hz = c->bandwidth_hz;
- }
-
- ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg(state, 0x800040, 0x00);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg(state, 0x800047, 0x00);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01);
- if (ret < 0)
- goto err;
-
- if (c->frequency <= 230000000)
- tmp = 0x00; /* VHF */
- else
- tmp = 0x01; /* UHF */
-
- ret = af9033_wr_reg(state, 0x80004b, tmp);
- if (ret < 0)
- goto err;
-
- ret = af9033_wr_reg(state, 0x800000, 0x00);
- if (ret < 0)
- goto err;
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_get_frontend(struct dvb_frontend *fe)
-{
- struct af9033_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- u8 buf[8];
-
- pr_debug("%s\n", __func__);
-
- /* read all needed registers */
- ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf));
- if (ret < 0)
- goto err;
-
- switch ((buf[0] >> 0) & 3) {
- case 0:
- c->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case 1:
- c->transmission_mode = TRANSMISSION_MODE_8K;
- break;
- }
-
- switch ((buf[1] >> 0) & 3) {
- case 0:
- c->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- c->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- c->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- c->guard_interval = GUARD_INTERVAL_1_4;
- break;
- }
-
- switch ((buf[2] >> 0) & 7) {
- case 0:
- c->hierarchy = HIERARCHY_NONE;
- break;
- case 1:
- c->hierarchy = HIERARCHY_1;
- break;
- case 2:
- c->hierarchy = HIERARCHY_2;
- break;
- case 3:
- c->hierarchy = HIERARCHY_4;
- break;
- }
-
- switch ((buf[3] >> 0) & 3) {
- case 0:
- c->modulation = QPSK;
- break;
- case 1:
- c->modulation = QAM_16;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- }
-
- switch ((buf[4] >> 0) & 3) {
- case 0:
- c->bandwidth_hz = 6000000;
- break;
- case 1:
- c->bandwidth_hz = 7000000;
- break;
- case 2:
- c->bandwidth_hz = 8000000;
- break;
- }
-
- switch ((buf[6] >> 0) & 7) {
- case 0:
- c->code_rate_HP = FEC_1_2;
- break;
- case 1:
- c->code_rate_HP = FEC_2_3;
- break;
- case 2:
- c->code_rate_HP = FEC_3_4;
- break;
- case 3:
- c->code_rate_HP = FEC_5_6;
- break;
- case 4:
- c->code_rate_HP = FEC_7_8;
- break;
- case 5:
- c->code_rate_HP = FEC_NONE;
- break;
- }
-
- switch ((buf[7] >> 0) & 7) {
- case 0:
- c->code_rate_LP = FEC_1_2;
- break;
- case 1:
- c->code_rate_LP = FEC_2_3;
- break;
- case 2:
- c->code_rate_LP = FEC_3_4;
- break;
- case 3:
- c->code_rate_LP = FEC_5_6;
- break;
- case 4:
- c->code_rate_LP = FEC_7_8;
- break;
- case 5:
- c->code_rate_LP = FEC_NONE;
- break;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp;
-
- *status = 0;
-
- /* radio channel status, 0=no result, 1=has signal, 2=no signal */
- ret = af9033_rd_reg(state, 0x800047, &tmp);
- if (ret < 0)
- goto err;
-
- /* has signal */
- if (tmp == 0x01)
- *status |= FE_HAS_SIGNAL;
-
- if (tmp != 0x02) {
- /* TPS lock */
- ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01);
- if (ret < 0)
- goto err;
-
- if (tmp)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI;
-
- /* full lock */
- ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01);
- if (ret < 0)
- goto err;
-
- if (tmp)
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC |
- FE_HAS_LOCK;
- }
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret, i, len;
- u8 buf[3], tmp;
- u32 snr_val;
- const struct val_snr *uninitialized_var(snr_lut);
-
- /* read value */
- ret = af9033_rd_regs(state, 0x80002c, buf, 3);
- if (ret < 0)
- goto err;
-
- snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
-
- /* read current modulation */
- ret = af9033_rd_reg(state, 0x80f903, &tmp);
- if (ret < 0)
- goto err;
-
- switch ((tmp >> 0) & 3) {
- case 0:
- len = ARRAY_SIZE(qpsk_snr_lut);
- snr_lut = qpsk_snr_lut;
- break;
- case 1:
- len = ARRAY_SIZE(qam16_snr_lut);
- snr_lut = qam16_snr_lut;
- break;
- case 2:
- len = ARRAY_SIZE(qam64_snr_lut);
- snr_lut = qam64_snr_lut;
- break;
- default:
- goto err;
- }
-
- for (i = 0; i < len; i++) {
- tmp = snr_lut[i].snr;
-
- if (snr_val < snr_lut[i].val)
- break;
- }
-
- *snr = tmp * 10; /* dB/10 */
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret;
- u8 strength2;
-
- /* read signal strength of 0-100 scale */
- ret = af9033_rd_reg(state, 0x800048, &strength2);
- if (ret < 0)
- goto err;
-
- /* scale value to 0x0000-0xffff */
- *strength = strength2 * 0xffff / 100;
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static int af9033_update_ch_stat(struct af9033_state *state)
-{
- int ret = 0;
- u32 err_cnt, bit_cnt;
- u16 abort_cnt;
- u8 buf[7];
-
- /* only update data every half second */
- if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) {
- ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf));
- if (ret < 0)
- goto err;
- /* in 8 byte packets? */
- abort_cnt = (buf[1] << 8) + buf[0];
- /* in bits */
- err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2];
- /* in 8 byte packets? always(?) 0x2710 = 10000 */
- bit_cnt = (buf[6] << 8) + buf[5];
-
- if (bit_cnt < abort_cnt) {
- abort_cnt = 1000;
- state->ber = 0xffffffff;
- } else {
- /* 8 byte packets, that have not been rejected already */
- bit_cnt -= (u32)abort_cnt;
- if (bit_cnt == 0) {
- state->ber = 0xffffffff;
- } else {
- err_cnt -= (u32)abort_cnt * 8 * 8;
- bit_cnt *= 8 * 8;
- state->ber = err_cnt * (0xffffffff / bit_cnt);
- }
- }
- state->ucb += abort_cnt;
- state->last_stat_check = jiffies;
- }
-
- return 0;
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret;
-
- ret = af9033_update_ch_stat(state);
- if (ret < 0)
- return ret;
-
- *ber = state->ber;
-
- return 0;
-}
-
-static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret;
-
- ret = af9033_update_ch_stat(state);
- if (ret < 0)
- return ret;
-
- *ucblocks = state->ucb;
-
- return 0;
-}
-
-static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct af9033_state *state = fe->demodulator_priv;
- int ret;
-
- pr_debug("%s: enable=%d\n", __func__, enable);
-
- ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01);
- if (ret < 0)
- goto err;
-
- return 0;
-
-err:
- pr_debug("%s: failed=%d\n", __func__, ret);
-
- return ret;
-}
-
-static struct dvb_frontend_ops af9033_ops;
-
-struct dvb_frontend *af9033_attach(const struct af9033_config *config,
- struct i2c_adapter *i2c)
-{
- int ret;
- struct af9033_state *state;
- u8 buf[8];
-
- pr_debug("%s:\n", __func__);
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL);
- if (state == NULL)
- goto err;
-
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->cfg, config, sizeof(struct af9033_config));
-
- if (state->cfg.clock != 12000000) {
- printk(KERN_INFO "af9033: unsupported clock=%d, only " \
- "12000000 Hz is supported currently\n",
- state->cfg.clock);
- goto err;
- }
-
- /* firmware version */
- ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4);
- if (ret < 0)
- goto err;
-
- ret = af9033_rd_regs(state, 0x804191, &buf[4], 4);
- if (ret < 0)
- goto err;
-
- printk(KERN_INFO "af9033: firmware version: LINK=%d.%d.%d.%d " \
- "OFDM=%d.%d.%d.%d\n", buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7]);
-
- /* configure internal TS mode */
- switch (state->cfg.ts_mode) {
- case AF9033_TS_MODE_PARALLEL:
- state->ts_mode_parallel = true;
- break;
- case AF9033_TS_MODE_SERIAL:
- state->ts_mode_serial = true;
- break;
- case AF9033_TS_MODE_USB:
- /* usb mode for AF9035 */
- default:
- break;
- }
-
- /* create dvb_frontend */
- memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
- state->fe.demodulator_priv = state;
-
- return &state->fe;
-
-err:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(af9033_attach);
-
-static struct dvb_frontend_ops af9033_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Afatech AF9033 (DVB-T)",
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 250000,
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_MUTE_TS
- },
-
- .release = af9033_release,
-
- .init = af9033_init,
- .sleep = af9033_sleep,
-
- .get_tune_settings = af9033_get_tune_settings,
- .set_frontend = af9033_set_frontend,
- .get_frontend = af9033_get_frontend,
-
- .read_status = af9033_read_status,
- .read_snr = af9033_read_snr,
- .read_signal_strength = af9033_read_signal_strength,
- .read_ber = af9033_read_ber,
- .read_ucblocks = af9033_read_ucblocks,
-
- .i2c_gate_ctrl = af9033_i2c_gate_ctrl,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h
deleted file mode 100644
index 9e302c3f0f7d..000000000000
--- a/drivers/media/dvb/frontends/af9033.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Afatech AF9033 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef AF9033_H
-#define AF9033_H
-
-struct af9033_config {
- /*
- * I2C address
- */
- u8 i2c_addr;
-
- /*
- * clock Hz
- * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000,
- * 30000000, 36000000, 20480000, 16384000
- */
- u32 clock;
-
- /*
- * tuner
- */
-#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */
-#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */
-#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */
-#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */
- u8 tuner;
-
- /*
- * TS settings
- */
-#define AF9033_TS_MODE_USB 0
-#define AF9033_TS_MODE_PARALLEL 1
-#define AF9033_TS_MODE_SERIAL 2
- u8 ts_mode:2;
-
- /*
- * input spectrum inversion
- */
- bool spec_inv;
-};
-
-
-#if defined(CONFIG_DVB_AF9033) || \
- (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE))
-extern struct dvb_frontend *af9033_attach(const struct af9033_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *af9033_attach(
- const struct af9033_config *config, struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* AF9033_H */
diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h
deleted file mode 100644
index 0b783b9ed75e..000000000000
--- a/drivers/media/dvb/frontends/af9033_priv.h
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Afatech AF9033 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef AF9033_PRIV_H
-#define AF9033_PRIV_H
-
-#include "dvb_frontend.h"
-#include "af9033.h"
-
-struct reg_val {
- u32 reg;
- u8 val;
-};
-
-struct reg_val_mask {
- u32 reg;
- u8 val;
- u8 mask;
-};
-
-struct coeff {
- u32 clock;
- u32 bandwidth_hz;
- u8 val[36];
-};
-
-struct clock_adc {
- u32 clock;
- u32 adc;
-};
-
-struct val_snr {
- u32 val;
- u8 snr;
-};
-
-/* Xtal clock vs. ADC clock lookup table */
-static const struct clock_adc clock_adc_lut[] = {
- { 16384000, 20480000 },
- { 20480000, 20480000 },
- { 36000000, 20250000 },
- { 30000000, 20156250 },
- { 26000000, 20583333 },
- { 28000000, 20416667 },
- { 32000000, 20500000 },
- { 34000000, 20187500 },
- { 24000000, 20500000 },
- { 22000000, 20625000 },
- { 12000000, 20250000 },
-};
-
-/* pre-calculated coeff lookup table */
-static const struct coeff coeff_lut[] = {
- /* 12.000 MHz */
- { 12000000, 8000000, {
- 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
- 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5,
- 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
- 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 }
- },
- { 12000000, 7000000, {
- 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
- 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b,
- 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
- 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 }
- },
- { 12000000, 6000000, {
- 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
- 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60,
- 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
- 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 }
- },
-};
-
-/* QPSK SNR lookup table */
-static const struct val_snr qpsk_snr_lut[] = {
- { 0x0b4771, 0 },
- { 0x0c1aed, 1 },
- { 0x0d0d27, 2 },
- { 0x0e4d19, 3 },
- { 0x0e5da8, 4 },
- { 0x107097, 5 },
- { 0x116975, 6 },
- { 0x1252d9, 7 },
- { 0x131fa4, 8 },
- { 0x13d5e1, 9 },
- { 0x148e53, 10 },
- { 0x15358b, 11 },
- { 0x15dd29, 12 },
- { 0x168112, 13 },
- { 0x170b61, 14 },
- { 0x17a532, 15 },
- { 0x180f94, 16 },
- { 0x186ed2, 17 },
- { 0x18b271, 18 },
- { 0x18e118, 19 },
- { 0x18ff4b, 20 },
- { 0x190af1, 21 },
- { 0x191451, 22 },
- { 0xffffff, 23 },
-};
-
-/* QAM16 SNR lookup table */
-static const struct val_snr qam16_snr_lut[] = {
- { 0x04f0d5, 0 },
- { 0x05387a, 1 },
- { 0x0573a4, 2 },
- { 0x05a99e, 3 },
- { 0x05cc80, 4 },
- { 0x05eb62, 5 },
- { 0x05fecf, 6 },
- { 0x060b80, 7 },
- { 0x062501, 8 },
- { 0x064865, 9 },
- { 0x069604, 10 },
- { 0x06f356, 11 },
- { 0x07706a, 12 },
- { 0x0804d3, 13 },
- { 0x089d1a, 14 },
- { 0x093e3d, 15 },
- { 0x09e35d, 16 },
- { 0x0a7c3c, 17 },
- { 0x0afaf8, 18 },
- { 0x0b719d, 19 },
- { 0x0bda6a, 20 },
- { 0x0c0c75, 21 },
- { 0x0c3f7d, 22 },
- { 0x0c5e62, 23 },
- { 0x0c6c31, 24 },
- { 0x0c7925, 25 },
- { 0xffffff, 26 },
-};
-
-/* QAM64 SNR lookup table */
-static const struct val_snr qam64_snr_lut[] = {
- { 0x0256d0, 0 },
- { 0x027a65, 1 },
- { 0x029873, 2 },
- { 0x02b7fe, 3 },
- { 0x02cf1e, 4 },
- { 0x02e234, 5 },
- { 0x02f409, 6 },
- { 0x030046, 7 },
- { 0x030844, 8 },
- { 0x030a02, 9 },
- { 0x030cde, 10 },
- { 0x031031, 11 },
- { 0x03144c, 12 },
- { 0x0315dd, 13 },
- { 0x031920, 14 },
- { 0x0322d0, 15 },
- { 0x0339fc, 16 },
- { 0x0364a1, 17 },
- { 0x038bcc, 18 },
- { 0x03c7d3, 19 },
- { 0x0408cc, 20 },
- { 0x043bed, 21 },
- { 0x048061, 22 },
- { 0x04be95, 23 },
- { 0x04fa7d, 24 },
- { 0x052405, 25 },
- { 0x05570d, 26 },
- { 0x059feb, 27 },
- { 0x05bf38, 28 },
- { 0xffffff, 29 },
-};
-
-static const struct reg_val ofsm_init[] = {
- { 0x800051, 0x01 },
- { 0x800070, 0x0a },
- { 0x80007e, 0x04 },
- { 0x800081, 0x0a },
- { 0x80008a, 0x01 },
- { 0x80008e, 0x01 },
- { 0x800092, 0x06 },
- { 0x800099, 0x01 },
- { 0x80009f, 0xe1 },
- { 0x8000a0, 0xcf },
- { 0x8000a3, 0x01 },
- { 0x8000a5, 0x01 },
- { 0x8000a6, 0x01 },
- { 0x8000a9, 0x00 },
- { 0x8000aa, 0x01 },
- { 0x8000ab, 0x01 },
- { 0x8000b0, 0x01 },
- { 0x8000c0, 0x05 },
- { 0x8000c4, 0x19 },
- { 0x80f000, 0x0f },
- { 0x80f016, 0x10 },
- { 0x80f017, 0x04 },
- { 0x80f018, 0x05 },
- { 0x80f019, 0x04 },
- { 0x80f01a, 0x05 },
- { 0x80f021, 0x03 },
- { 0x80f022, 0x0a },
- { 0x80f023, 0x0a },
- { 0x80f02b, 0x00 },
- { 0x80f02c, 0x01 },
- { 0x80f064, 0x03 },
- { 0x80f065, 0xf9 },
- { 0x80f066, 0x03 },
- { 0x80f067, 0x01 },
- { 0x80f06f, 0xe0 },
- { 0x80f070, 0x03 },
- { 0x80f072, 0x0f },
- { 0x80f073, 0x03 },
- { 0x80f078, 0x00 },
- { 0x80f087, 0x00 },
- { 0x80f09b, 0x3f },
- { 0x80f09c, 0x00 },
- { 0x80f09d, 0x20 },
- { 0x80f09e, 0x00 },
- { 0x80f09f, 0x0c },
- { 0x80f0a0, 0x00 },
- { 0x80f130, 0x04 },
- { 0x80f132, 0x04 },
- { 0x80f144, 0x1a },
- { 0x80f146, 0x00 },
- { 0x80f14a, 0x01 },
- { 0x80f14c, 0x00 },
- { 0x80f14d, 0x00 },
- { 0x80f14f, 0x04 },
- { 0x80f158, 0x7f },
- { 0x80f15a, 0x00 },
- { 0x80f15b, 0x08 },
- { 0x80f15d, 0x03 },
- { 0x80f15e, 0x05 },
- { 0x80f163, 0x05 },
- { 0x80f166, 0x01 },
- { 0x80f167, 0x40 },
- { 0x80f168, 0x0f },
- { 0x80f17a, 0x00 },
- { 0x80f17b, 0x00 },
- { 0x80f183, 0x01 },
- { 0x80f19d, 0x40 },
- { 0x80f1bc, 0x36 },
- { 0x80f1bd, 0x00 },
- { 0x80f1cb, 0xa0 },
- { 0x80f1cc, 0x01 },
- { 0x80f204, 0x10 },
- { 0x80f214, 0x00 },
- { 0x80f40e, 0x0a },
- { 0x80f40f, 0x40 },
- { 0x80f410, 0x08 },
- { 0x80f55f, 0x0a },
- { 0x80f561, 0x15 },
- { 0x80f562, 0x20 },
- { 0x80f5df, 0xfb },
- { 0x80f5e0, 0x00 },
- { 0x80f5e3, 0x09 },
- { 0x80f5e4, 0x01 },
- { 0x80f5e5, 0x01 },
- { 0x80f5f8, 0x01 },
- { 0x80f5fd, 0x01 },
- { 0x80f600, 0x05 },
- { 0x80f601, 0x08 },
- { 0x80f602, 0x0b },
- { 0x80f603, 0x0e },
- { 0x80f604, 0x11 },
- { 0x80f605, 0x14 },
- { 0x80f606, 0x17 },
- { 0x80f607, 0x1f },
- { 0x80f60e, 0x00 },
- { 0x80f60f, 0x04 },
- { 0x80f610, 0x32 },
- { 0x80f611, 0x10 },
- { 0x80f707, 0xfc },
- { 0x80f708, 0x00 },
- { 0x80f709, 0x37 },
- { 0x80f70a, 0x00 },
- { 0x80f78b, 0x01 },
- { 0x80f80f, 0x40 },
- { 0x80f810, 0x54 },
- { 0x80f811, 0x5a },
- { 0x80f905, 0x01 },
- { 0x80fb06, 0x03 },
- { 0x80fd8b, 0x00 },
-};
-
-/* Infineon TUA 9001 tuner init
- AF9033_TUNER_TUA9001 = 0x27 */
-static const struct reg_val tuner_init_tua9001[] = {
- { 0x800046, 0x27 },
- { 0x800057, 0x00 },
- { 0x800058, 0x01 },
- { 0x80005f, 0x00 },
- { 0x800060, 0x00 },
- { 0x80006d, 0x00 },
- { 0x800071, 0x05 },
- { 0x800072, 0x02 },
- { 0x800074, 0x01 },
- { 0x800075, 0x03 },
- { 0x800076, 0x02 },
- { 0x800077, 0x00 },
- { 0x800078, 0x01 },
- { 0x800079, 0x00 },
- { 0x80007a, 0x7e },
- { 0x80007b, 0x3e },
- { 0x800093, 0x00 },
- { 0x800094, 0x01 },
- { 0x800095, 0x02 },
- { 0x800096, 0x01 },
- { 0x800098, 0x0a },
- { 0x80009b, 0x05 },
- { 0x80009c, 0x80 },
- { 0x8000b3, 0x00 },
- { 0x8000c1, 0x01 },
- { 0x8000c2, 0x00 },
- { 0x80f007, 0x00 },
- { 0x80f01f, 0x82 },
- { 0x80f020, 0x00 },
- { 0x80f029, 0x82 },
- { 0x80f02a, 0x00 },
- { 0x80f047, 0x00 },
- { 0x80f054, 0x00 },
- { 0x80f055, 0x00 },
- { 0x80f077, 0x01 },
- { 0x80f1e6, 0x00 },
-};
-
-/* Fitipower fc0011 tuner init
- AF9033_TUNER_FC0011 = 0x28 */
-static const struct reg_val tuner_init_fc0011[] = {
- { 0x800046, AF9033_TUNER_FC0011 },
- { 0x800057, 0x00 },
- { 0x800058, 0x01 },
- { 0x80005f, 0x00 },
- { 0x800060, 0x00 },
- { 0x800068, 0xa5 },
- { 0x80006e, 0x01 },
- { 0x800071, 0x0A },
- { 0x800072, 0x02 },
- { 0x800074, 0x01 },
- { 0x800079, 0x01 },
- { 0x800093, 0x00 },
- { 0x800094, 0x00 },
- { 0x800095, 0x00 },
- { 0x800096, 0x00 },
- { 0x80009b, 0x2D },
- { 0x80009c, 0x60 },
- { 0x80009d, 0x23 },
- { 0x8000a4, 0x50 },
- { 0x8000ad, 0x50 },
- { 0x8000b3, 0x01 },
- { 0x8000b7, 0x88 },
- { 0x8000b8, 0xa6 },
- { 0x8000c3, 0x01 },
- { 0x8000c4, 0x01 },
- { 0x8000c7, 0x69 },
- { 0x80F007, 0x00 },
- { 0x80F00A, 0x1B },
- { 0x80F00B, 0x1B },
- { 0x80F00C, 0x1B },
- { 0x80F00D, 0x1B },
- { 0x80F00E, 0xFF },
- { 0x80F00F, 0x01 },
- { 0x80F010, 0x00 },
- { 0x80F011, 0x02 },
- { 0x80F012, 0xFF },
- { 0x80F013, 0x01 },
- { 0x80F014, 0x00 },
- { 0x80F015, 0x02 },
- { 0x80F01B, 0xEF },
- { 0x80F01C, 0x01 },
- { 0x80F01D, 0x0f },
- { 0x80F01E, 0x02 },
- { 0x80F01F, 0x6E },
- { 0x80F020, 0x00 },
- { 0x80F025, 0xDE },
- { 0x80F026, 0x00 },
- { 0x80F027, 0x0A },
- { 0x80F028, 0x03 },
- { 0x80F029, 0x6E },
- { 0x80F02A, 0x00 },
- { 0x80F047, 0x00 },
- { 0x80F054, 0x00 },
- { 0x80F055, 0x00 },
- { 0x80F077, 0x01 },
- { 0x80F1E6, 0x00 },
-};
-
-/* MaxLinear MxL5007T tuner init
- AF9033_TUNER_MXL5007T = 0xa0 */
-static const struct reg_val tuner_init_mxl5007t[] = {
- { 0x800046, 0x1b },
- { 0x800057, 0x01 },
- { 0x800058, 0x01 },
- { 0x80005f, 0x00 },
- { 0x800060, 0x00 },
- { 0x800068, 0x96 },
- { 0x800071, 0x05 },
- { 0x800072, 0x02 },
- { 0x800074, 0x01 },
- { 0x800079, 0x01 },
- { 0x800093, 0x00 },
- { 0x800094, 0x00 },
- { 0x800095, 0x00 },
- { 0x800096, 0x00 },
- { 0x8000b3, 0x01 },
- { 0x8000c1, 0x01 },
- { 0x8000c2, 0x00 },
- { 0x80f007, 0x00 },
- { 0x80f00c, 0x19 },
- { 0x80f00d, 0x1a },
- { 0x80f012, 0xda },
- { 0x80f013, 0x00 },
- { 0x80f014, 0x00 },
- { 0x80f015, 0x02 },
- { 0x80f01f, 0x82 },
- { 0x80f020, 0x00 },
- { 0x80f029, 0x82 },
- { 0x80f02a, 0x00 },
- { 0x80f077, 0x02 },
- { 0x80f1e6, 0x00 },
-};
-
-/* NXP TDA 18218HN tuner init
- AF9033_TUNER_TDA18218 = 0xa1 */
-static const struct reg_val tuner_init_tda18218[] = {
- {0x800046, 0xa1},
- {0x800057, 0x01},
- {0x800058, 0x01},
- {0x80005f, 0x00},
- {0x800060, 0x00},
- {0x800071, 0x05},
- {0x800072, 0x02},
- {0x800074, 0x01},
- {0x800079, 0x01},
- {0x800093, 0x00},
- {0x800094, 0x00},
- {0x800095, 0x00},
- {0x800096, 0x00},
- {0x8000b3, 0x01},
- {0x8000c3, 0x01},
- {0x8000c4, 0x00},
- {0x80f007, 0x00},
- {0x80f00c, 0x19},
- {0x80f00d, 0x1a},
- {0x80f012, 0xda},
- {0x80f013, 0x00},
- {0x80f014, 0x00},
- {0x80f015, 0x02},
- {0x80f01f, 0x82},
- {0x80f020, 0x00},
- {0x80f029, 0x82},
- {0x80f02a, 0x00},
- {0x80f077, 0x02},
- {0x80f1e6, 0x00},
-};
-
-#endif /* AF9033_PRIV_H */
-
diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c
deleted file mode 100644
index a2261ea2cf82..000000000000
--- a/drivers/media/dvb/frontends/atbm8830.c
+++ /dev/null
@@ -1,508 +0,0 @@
-/*
- * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator
- * ATBM8830, ATBM8831
- *
- * Copyright (C) 2009 David T.L. Wong <davidtlwong@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <asm/div64.h>
-#include "dvb_frontend.h"
-
-#include "atbm8830.h"
-#include "atbm8830_priv.h"
-
-#define dprintk(args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG "atbm8830: " args); \
- } while (0)
-
-static int debug;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data)
-{
- int ret = 0;
- u8 dev_addr;
- u8 buf1[] = { reg >> 8, reg & 0xFF };
- u8 buf2[] = { data };
- struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
- struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 };
-
- dev_addr = priv->config->demod_address;
- msg1.addr = dev_addr;
- msg2.addr = dev_addr;
-
- if (debug >= 2)
- dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data);
-
- ret = i2c_transfer(priv->i2c, &msg1, 1);
- if (ret != 1)
- return -EIO;
-
- ret = i2c_transfer(priv->i2c, &msg2, 1);
- return (ret != 1) ? -EIO : 0;
-}
-
-static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data)
-{
- int ret;
- u8 dev_addr;
-
- u8 buf1[] = { reg >> 8, reg & 0xFF };
- u8 buf2[] = { 0 };
- struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 };
- struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 };
-
- dev_addr = priv->config->demod_address;
- msg1.addr = dev_addr;
- msg2.addr = dev_addr;
-
- ret = i2c_transfer(priv->i2c, &msg1, 1);
- if (ret != 1) {
- dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret);
- return -EIO;
- }
-
- ret = i2c_transfer(priv->i2c, &msg2, 1);
- if (ret != 1)
- return -EIO;
-
- *p_data = buf2[0];
- if (debug >= 2)
- dprintk("%s: reg=0x%04X, data=0x%02X\n",
- __func__, reg, buf2[0]);
-
- return 0;
-}
-
-/* Lock register latch so that multi-register read is atomic */
-static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock)
-{
- return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0);
-}
-
-static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
-{
- u32 val;
- u64 t;
-
- /* 0x100000 * freq / 30.4MHz */
- t = (u64)0x100000 * freq;
- do_div(t, 30400);
- val = t;
-
- atbm8830_write_reg(priv, REG_OSC_CLK, val);
- atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8);
- atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16);
-
- return 0;
-}
-
-static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/)
-{
-
- u32 fs = priv->config->osc_clk_freq;
- u64 t;
- u32 val;
- u8 dat;
-
- if (freq != 0) {
- /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */
- t = (u64) 2 * 31416 * (freq - fs);
- t <<= 22;
- do_div(t, fs);
- do_div(t, 1000);
- val = t;
-
- atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1);
- atbm8830_write_reg(priv, REG_IF_FREQ, val);
- atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8);
- atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16);
-
- atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
- dat &= 0xFC;
- atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
- } else {
- /* Zero IF */
- atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0);
-
- atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat);
- dat &= 0xFC;
- dat |= 0x02;
- atbm8830_write_reg(priv, REG_ADC_CONFIG, dat);
-
- if (priv->config->zif_swap_iq)
- atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03);
- else
- atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01);
- }
-
- return 0;
-}
-
-static int is_locked(struct atbm_state *priv, u8 *locked)
-{
- u8 status;
-
- atbm8830_read_reg(priv, REG_LOCK_STATUS, &status);
-
- if (locked != NULL)
- *locked = (status == 1);
- return 0;
-}
-
-static int set_agc_config(struct atbm_state *priv,
- u8 min, u8 max, u8 hold_loop)
-{
- /* no effect if both min and max are zero */
- if (!min && !max)
- return 0;
-
- atbm8830_write_reg(priv, REG_AGC_MIN, min);
- atbm8830_write_reg(priv, REG_AGC_MAX, max);
- atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop);
-
- return 0;
-}
-
-static int set_static_channel_mode(struct atbm_state *priv)
-{
- int i;
-
- for (i = 0; i < 5; i++)
- atbm8830_write_reg(priv, 0x099B + i, 0x08);
-
- atbm8830_write_reg(priv, 0x095B, 0x7F);
- atbm8830_write_reg(priv, 0x09CB, 0x01);
- atbm8830_write_reg(priv, 0x09CC, 0x7F);
- atbm8830_write_reg(priv, 0x09CD, 0x7F);
- atbm8830_write_reg(priv, 0x0E01, 0x20);
-
- /* For single carrier */
- atbm8830_write_reg(priv, 0x0B03, 0x0A);
- atbm8830_write_reg(priv, 0x0935, 0x10);
- atbm8830_write_reg(priv, 0x0936, 0x08);
- atbm8830_write_reg(priv, 0x093E, 0x08);
- atbm8830_write_reg(priv, 0x096E, 0x06);
-
- /* frame_count_max0 */
- atbm8830_write_reg(priv, 0x0B09, 0x00);
- /* frame_count_max1 */
- atbm8830_write_reg(priv, 0x0B0A, 0x08);
-
- return 0;
-}
-
-static int set_ts_config(struct atbm_state *priv)
-{
- const struct atbm8830_config *cfg = priv->config;
-
- /*Set parallel/serial ts mode*/
- atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0);
- atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0);
- /*Set ts sampling edge*/
- atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE,
- cfg->ts_sampling_edge ? 1 : 0);
- /*Set ts clock freerun*/
- atbm8830_write_reg(priv, REG_TS_CLK_FREERUN,
- cfg->ts_clk_gated ? 0 : 1);
-
- return 0;
-}
-
-static int atbm8830_init(struct dvb_frontend *fe)
-{
- struct atbm_state *priv = fe->demodulator_priv;
- const struct atbm8830_config *cfg = priv->config;
-
- /*Set oscillator frequency*/
- set_osc_freq(priv, cfg->osc_clk_freq);
-
- /*Set IF frequency*/
- set_if_freq(priv, cfg->if_freq);
-
- /*Set AGC Config*/
- set_agc_config(priv, cfg->agc_min, cfg->agc_max,
- cfg->agc_hold_loop);
-
- /*Set static channel mode*/
- set_static_channel_mode(priv);
-
- set_ts_config(priv);
- /*Turn off DSP reset*/
- atbm8830_write_reg(priv, 0x000A, 0);
-
- /*SW version test*/
- atbm8830_write_reg(priv, 0x020C, 11);
-
- /* Run */
- atbm8830_write_reg(priv, REG_DEMOD_RUN, 1);
-
- return 0;
-}
-
-
-static void atbm8830_release(struct dvb_frontend *fe)
-{
- struct atbm_state *state = fe->demodulator_priv;
- dprintk("%s\n", __func__);
-
- kfree(state);
-}
-
-static int atbm8830_set_fe(struct dvb_frontend *fe)
-{
- struct atbm_state *priv = fe->demodulator_priv;
- int i;
- u8 locked = 0;
- dprintk("%s\n", __func__);
-
- /* set frequency */
- 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);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- /* start auto lock */
- for (i = 0; i < 10; i++) {
- mdelay(100);
- dprintk("Try %d\n", i);
- is_locked(priv, &locked);
- if (locked != 0) {
- dprintk("ATBM8830 locked!\n");
- break;
- }
- }
-
- return 0;
-}
-
-static int atbm8830_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- dprintk("%s\n", __func__);
-
- /* TODO: get real readings from device */
- /* inversion status */
- c->inversion = INVERSION_OFF;
-
- /* bandwidth */
- c->bandwidth_hz = 8000000;
-
- c->code_rate_HP = FEC_AUTO;
- c->code_rate_LP = FEC_AUTO;
-
- c->modulation = QAM_AUTO;
-
- /* transmission mode */
- c->transmission_mode = TRANSMISSION_MODE_AUTO;
-
- /* guard interval */
- c->guard_interval = GUARD_INTERVAL_AUTO;
-
- /* hierarchy */
- c->hierarchy = HIERARCHY_NONE;
-
- return 0;
-}
-
-static int atbm8830_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- fesettings->min_delay_ms = 0;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
- return 0;
-}
-
-static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
-{
- struct atbm_state *priv = fe->demodulator_priv;
- u8 locked = 0;
- u8 agc_locked = 0;
-
- dprintk("%s\n", __func__);
- *fe_status = 0;
-
- is_locked(priv, &locked);
- if (locked) {
- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- }
- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
-
- atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked);
- dprintk("AGC Lock: %d\n", agc_locked);
-
- return 0;
-}
-
-static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct atbm_state *priv = fe->demodulator_priv;
- u32 frame_err;
- u8 t;
-
- dprintk("%s\n", __func__);
-
- atbm8830_reglatch_lock(priv, 1);
-
- atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t);
- frame_err = t & 0x7F;
- frame_err <<= 8;
- atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t);
- frame_err |= t;
-
- atbm8830_reglatch_lock(priv, 0);
-
- *ber = frame_err * 100 / 32767;
-
- dprintk("%s: ber=0x%x\n", __func__, *ber);
- return 0;
-}
-
-static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
-{
- struct atbm_state *priv = fe->demodulator_priv;
- u32 pwm;
- u8 t;
-
- dprintk("%s\n", __func__);
- atbm8830_reglatch_lock(priv, 1);
-
- atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t);
- pwm = t & 0x03;
- pwm <<= 8;
- atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t);
- pwm |= t;
-
- atbm8830_reglatch_lock(priv, 0);
-
- dprintk("AGC PWM = 0x%02X\n", pwm);
- pwm = 0x400 - pwm;
-
- *signal = pwm * 0x10000 / 0x400;
-
- return 0;
-}
-
-static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- dprintk("%s\n", __func__);
- *snr = 0;
- return 0;
-}
-
-static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- dprintk("%s\n", __func__);
- *ucblocks = 0;
- return 0;
-}
-
-static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct atbm_state *priv = fe->demodulator_priv;
-
- return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0);
-}
-
-static struct dvb_frontend_ops atbm8830_ops = {
- .delsys = { SYS_DMBTH },
- .info = {
- .name = "AltoBeam ATBM8830/8831 DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
- .caps =
- FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO
- },
-
- .release = atbm8830_release,
-
- .init = atbm8830_init,
- .sleep = NULL,
- .write = NULL,
- .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl,
-
- .set_frontend = atbm8830_set_fe,
- .get_frontend = atbm8830_get_fe,
- .get_tune_settings = atbm8830_get_tune_settings,
-
- .read_status = atbm8830_read_status,
- .read_ber = atbm8830_read_ber,
- .read_signal_strength = atbm8830_read_signal_strength,
- .read_snr = atbm8830_read_snr,
- .read_ucblocks = atbm8830_read_ucblocks,
-};
-
-struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config,
- struct i2c_adapter *i2c)
-{
- struct atbm_state *priv = NULL;
- u8 data = 0;
-
- dprintk("%s()\n", __func__);
-
- if (config == NULL || i2c == NULL)
- return NULL;
-
- priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL);
- if (priv == NULL)
- goto error_out;
-
- priv->config = config;
- priv->i2c = i2c;
-
- /* check if the demod is there */
- if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) {
- dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n",
- __func__, priv->config->demod_address);
- goto error_out;
- }
- dprintk("atbm8830 chip id: 0x%02X\n", data);
-
- memcpy(&priv->frontend.ops, &atbm8830_ops,
- sizeof(struct dvb_frontend_ops));
- priv->frontend.demodulator_priv = priv;
-
- atbm8830_init(&priv->frontend);
-
- atbm8830_i2c_gate_ctrl(&priv->frontend, 1);
-
- return &priv->frontend;
-
-error_out:
- dprintk("%s() error_out\n", __func__);
- kfree(priv);
- return NULL;
-
-}
-EXPORT_SYMBOL(atbm8830_attach);
-
-MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver");
-MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c
deleted file mode 100644
index 5cfe151ee394..000000000000
--- a/drivers/media/dvb/frontends/au8522_common.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- Auvitek AU8522 QAM/8VSB demodulator driver
-
- Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
- Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
- Copyright (C) 2005-2008 Auvitek International, Ltd.
- Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-#include "au8522_priv.h"
-
-MODULE_LICENSE("GPL");
-
-static int debug;
-
-#define dprintk(arg...)\
- do { if (debug)\
- printk(arg);\
- } while (0)
-
-/* Despite the name "hybrid_tuner", the framework works just as well for
- hybrid demodulators as well... */
-static LIST_HEAD(hybrid_tuner_instance_list);
-static DEFINE_MUTEX(au8522_list_mutex);
-
-/* 16 bit registers, 8 bit values */
-int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
-{
- int ret;
- u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
-
- struct i2c_msg msg = { .addr = state->config->demod_address,
- .flags = 0, .buf = buf, .len = 3 };
-
- ret = i2c_transfer(state->i2c, &msg, 1);
-
- if (ret != 1)
- printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
- "ret == %i)\n", __func__, reg, data, ret);
-
- return (ret != 1) ? -1 : 0;
-}
-EXPORT_SYMBOL(au8522_writereg);
-
-u8 au8522_readreg(struct au8522_state *state, u16 reg)
-{
- int ret;
- u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
- 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 } };
-
- ret = i2c_transfer(state->i2c, msg, 2);
-
- if (ret != 2)
- printk(KERN_ERR "%s: readreg error (ret == %i)\n",
- __func__, ret);
- return b1[0];
-}
-EXPORT_SYMBOL(au8522_readreg);
-
-int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct au8522_state *state = fe->demodulator_priv;
-
- dprintk("%s(%d)\n", __func__, enable);
-
- if (state->operational_mode == AU8522_ANALOG_MODE) {
- /* We're being asked to manage the gate even though we're
- not in digital mode. This can occur if we get switched
- over to analog mode before the dvb_frontend kernel thread
- has completely shutdown */
- return 0;
- }
-
- if (enable)
- return au8522_writereg(state, 0x106, 1);
- else
- return au8522_writereg(state, 0x106, 0);
-}
-EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
-
-/* Reset the demod hardware and reset all of the configuration registers
- to a default state. */
-int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
- u8 client_address)
-{
- int ret;
-
- mutex_lock(&au8522_list_mutex);
- ret = hybrid_tuner_request_state(struct au8522_state, (*state),
- hybrid_tuner_instance_list,
- i2c, client_address, "au8522");
- mutex_unlock(&au8522_list_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL(au8522_get_state);
-
-void au8522_release_state(struct au8522_state *state)
-{
- mutex_lock(&au8522_list_mutex);
- if (state != NULL)
- hybrid_tuner_release_state(state);
- mutex_unlock(&au8522_list_mutex);
-}
-EXPORT_SYMBOL(au8522_release_state);
-
-static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
-{
- struct au8522_led_config *led_config = state->config->led_cfg;
- u8 val;
-
- /* bail out if we can't control an LED */
- if (!led_config || !led_config->gpio_output ||
- !led_config->gpio_output_enable || !led_config->gpio_output_disable)
- return 0;
-
- val = au8522_readreg(state, 0x4000 |
- (led_config->gpio_output & ~0xc000));
- if (onoff) {
- /* enable GPIO output */
- val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
- val |= (led_config->gpio_output_enable & 0xff);
- } else {
- /* disable GPIO output */
- val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
- val |= (led_config->gpio_output_disable & 0xff);
- }
- return au8522_writereg(state, 0x8000 |
- (led_config->gpio_output & ~0xc000), val);
-}
-
-/* led = 0 | off
- * led = 1 | signal ok
- * led = 2 | signal strong
- * led < 0 | only light led if leds are currently off
- */
-int au8522_led_ctrl(struct au8522_state *state, int led)
-{
- struct au8522_led_config *led_config = state->config->led_cfg;
- int i, ret = 0;
-
- /* bail out if we can't control an LED */
- if (!led_config || !led_config->gpio_leds ||
- !led_config->num_led_states || !led_config->led_states)
- return 0;
-
- if (led < 0) {
- /* if LED is already lit, then leave it as-is */
- if (state->led_state)
- return 0;
- else
- led *= -1;
- }
-
- /* toggle LED if changing state */
- if (state->led_state != led) {
- u8 val;
-
- dprintk("%s: %d\n", __func__, led);
-
- au8522_led_gpio_enable(state, 1);
-
- val = au8522_readreg(state, 0x4000 |
- (led_config->gpio_leds & ~0xc000));
-
- /* start with all leds off */
- for (i = 0; i < led_config->num_led_states; i++)
- val &= ~led_config->led_states[i];
-
- /* set selected LED state */
- if (led < led_config->num_led_states)
- val |= led_config->led_states[led];
- else if (led_config->num_led_states)
- val |=
- led_config->led_states[led_config->num_led_states - 1];
-
- ret = au8522_writereg(state, 0x8000 |
- (led_config->gpio_leds & ~0xc000), val);
- if (ret < 0)
- return ret;
-
- state->led_state = led;
-
- if (led == 0)
- au8522_led_gpio_enable(state, 0);
- }
-
- return 0;
-}
-EXPORT_SYMBOL(au8522_led_ctrl);
-
-int au8522_init(struct dvb_frontend *fe)
-{
- struct au8522_state *state = fe->demodulator_priv;
- dprintk("%s()\n", __func__);
-
- state->operational_mode = AU8522_DIGITAL_MODE;
-
- /* Clear out any state associated with the digital side of the
- chip, so that when it gets powered back up it won't think
- that it is already tuned */
- state->current_frequency = 0;
-
- au8522_writereg(state, 0xa4, 1 << 5);
-
- au8522_i2c_gate_ctrl(fe, 1);
-
- return 0;
-}
-EXPORT_SYMBOL(au8522_init);
-
-int au8522_sleep(struct dvb_frontend *fe)
-{
- struct au8522_state *state = fe->demodulator_priv;
- dprintk("%s()\n", __func__);
-
- /* Only power down if the digital side is currently using the chip */
- if (state->operational_mode == AU8522_ANALOG_MODE) {
- /* We're not in one of the expected power modes, which means
- that the DVB thread is probably telling us to go to sleep
- even though the analog frontend has already started using
- the chip. So ignore the request */
- return 0;
- }
-
- /* turn off led */
- au8522_led_ctrl(state, 0);
-
- /* Power down the chip */
- au8522_writereg(state, 0xa4, 1 << 5);
-
- state->current_frequency = 0;
-
- return 0;
-}
-EXPORT_SYMBOL(au8522_sleep);
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
deleted file mode 100644
index 55b6390198e3..000000000000
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ /dev/null
@@ -1,842 +0,0 @@
-/*
- * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder
- *
- * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
- * Copyright (C) 2005-2008 Auvitek International, Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * As published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- */
-
-/* Developer notes:
- *
- * VBI support is not yet working
- * Enough is implemented here for CVBS and S-Video inputs, but the actual
- * analog demodulator code isn't implemented (not needed for xc5000 since it
- * has its own demodulator and outputs CVBS)
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-device.h>
-#include "au8522.h"
-#include "au8522_priv.h"
-
-MODULE_AUTHOR("Devin Heitmueller");
-MODULE_LICENSE("GPL");
-
-static int au8522_analog_debug;
-
-
-module_param_named(analog_debug, au8522_analog_debug, int, 0644);
-
-MODULE_PARM_DESC(analog_debug,
- "Analog debugging messages [0=Off (default) 1=On]");
-
-struct au8522_register_config {
- u16 reg_name;
- u8 reg_val[8];
-};
-
-
-/* Video Decoder Filter Coefficients
- The values are as follows from left to right
- 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"
-*/
-static const struct au8522_register_config filter_coef[] = {
- {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} },
- {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} },
- {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} },
- {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} },
- {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} },
- {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} },
- {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} },
- {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} },
- {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} },
- {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} },
- {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} },
- {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} },
- {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} },
- {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} },
- {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} },
- {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} },
- {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} },
- {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} },
- {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} },
- {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} },
- {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} },
- {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} },
-
-};
-#define NUM_FILTER_COEF (sizeof(filter_coef)\
- / sizeof(struct au8522_register_config))
-
-
-/* Registers 0x060b through 0x0652 are the LP Filter coefficients
- The values are as follows from left to right
- 0="SIF" 1="ATVRF/ATVRF13"
- Note: the "ATVRF/ATVRF13" mode has never been tested
-*/
-static const struct au8522_register_config lpfilter_coef[] = {
- {0x060b, {0x21, 0x0b} },
- {0x060c, {0xad, 0xad} },
- {0x060d, {0x70, 0xf0} },
- {0x060e, {0xea, 0xe9} },
- {0x060f, {0xdd, 0xdd} },
- {0x0610, {0x08, 0x64} },
- {0x0611, {0x60, 0x60} },
- {0x0612, {0xf8, 0xb2} },
- {0x0613, {0x01, 0x02} },
- {0x0614, {0xe4, 0xb4} },
- {0x0615, {0x19, 0x02} },
- {0x0616, {0xae, 0x2e} },
- {0x0617, {0xee, 0xc5} },
- {0x0618, {0x56, 0x56} },
- {0x0619, {0x30, 0x58} },
- {0x061a, {0xf9, 0xf8} },
- {0x061b, {0x24, 0x64} },
- {0x061c, {0x07, 0x07} },
- {0x061d, {0x30, 0x30} },
- {0x061e, {0xa9, 0xed} },
- {0x061f, {0x09, 0x0b} },
- {0x0620, {0x42, 0xc2} },
- {0x0621, {0x1d, 0x2a} },
- {0x0622, {0xd6, 0x56} },
- {0x0623, {0x95, 0x8b} },
- {0x0624, {0x2b, 0x2b} },
- {0x0625, {0x30, 0x24} },
- {0x0626, {0x3e, 0x3e} },
- {0x0627, {0x62, 0xe2} },
- {0x0628, {0xe9, 0xf5} },
- {0x0629, {0x99, 0x19} },
- {0x062a, {0xd4, 0x11} },
- {0x062b, {0x03, 0x04} },
- {0x062c, {0xb5, 0x85} },
- {0x062d, {0x1e, 0x20} },
- {0x062e, {0x2a, 0xea} },
- {0x062f, {0xd7, 0xd2} },
- {0x0630, {0x15, 0x15} },
- {0x0631, {0xa3, 0xa9} },
- {0x0632, {0x1f, 0x1f} },
- {0x0633, {0xf9, 0xd1} },
- {0x0634, {0xc0, 0xc3} },
- {0x0635, {0x4d, 0x8d} },
- {0x0636, {0x21, 0x31} },
- {0x0637, {0x83, 0x83} },
- {0x0638, {0x08, 0x8c} },
- {0x0639, {0x19, 0x19} },
- {0x063a, {0x45, 0xa5} },
- {0x063b, {0xef, 0xec} },
- {0x063c, {0x8a, 0x8a} },
- {0x063d, {0xf4, 0xf6} },
- {0x063e, {0x8f, 0x8f} },
- {0x063f, {0x44, 0x0c} },
- {0x0640, {0xef, 0xf0} },
- {0x0641, {0x66, 0x66} },
- {0x0642, {0xcc, 0xd2} },
- {0x0643, {0x41, 0x41} },
- {0x0644, {0x63, 0x93} },
- {0x0645, {0x8e, 0x8e} },
- {0x0646, {0xa2, 0x42} },
- {0x0647, {0x7b, 0x7b} },
- {0x0648, {0x04, 0x04} },
- {0x0649, {0x00, 0x00} },
- {0x064a, {0x40, 0x40} },
- {0x064b, {0x8c, 0x98} },
- {0x064c, {0x00, 0x00} },
- {0x064d, {0x63, 0xc3} },
- {0x064e, {0x04, 0x04} },
- {0x064f, {0x20, 0x20} },
- {0x0650, {0x00, 0x00} },
- {0x0651, {0x40, 0x40} },
- {0x0652, {0x01, 0x01} },
-};
-#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\
- / sizeof(struct au8522_register_config))
-
-static inline struct au8522_state *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct au8522_state, sd);
-}
-
-static void setup_vbi(struct au8522_state *state, int aud_input)
-{
- int i;
-
- /* These are set to zero regardless of what mode we're in */
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H,
- 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H,
- 0x00);
- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H,
- 0x00);
-
- /* Setup the VBI registers */
- for (i = 0x30; i < 0x60; i++)
- au8522_writereg(state, i, 0x40);
-
- /* For some reason, every register is 0x40 except register 0x44
- (confirmed via the HVR-950q USB capture) */
- au8522_writereg(state, 0x44, 0x60);
-
- /* Enable VBI (we always do this regardless of whether the user is
- viewing closed caption info) */
- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H,
- AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON);
-
-}
-
-static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
-{
- int i;
- int filter_coef_type;
-
- /* Provide reasonable defaults for picture tuning values */
- au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07);
- au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed);
- state->brightness = 0xed - 128;
- au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79);
- state->contrast = 0x79;
- au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
- au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
- state->saturation = 0x80;
- au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
- au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
- state->hue = 0x00;
-
- /* Other decoder registers */
- au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
-
- if (input_mode == 0x23) {
- /* S-Video input mapping */
- au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04);
- } else {
- /* All other modes (CVBS/ATVRF etc.) */
- au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00);
- }
-
- au8522_writereg(state, AU8522_TVDEC_PGA_REG012H,
- AU8522_TVDEC_PGA_REG012H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H,
- AU8522_TVDEC_COMB_MODE_REG015H_CVBS);
- au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H,
- AU8522_TVDED_DBG_MODE_REG060H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,
- AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13);
- au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,
- AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13);
- au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H,
- AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H,
- AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H,
- AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H,
- AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H,
- AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H,
- AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H,
- AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH,
- AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH,
- AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS);
- if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
- input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
- AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO);
- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
- AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO);
- } else {
- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
- AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
- AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS);
- }
- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH,
- AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH,
- AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H,
- AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS);
- au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS);
- au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H,
- AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS);
- au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS);
- au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H,
- AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H,
- AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H,
- AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS);
- au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH,
- AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH,
- AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS);
- au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H,
- AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS);
- au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H,
- AU8522_TOREGAAGC_REG0E5H_CVBS);
- au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
-
- setup_vbi(state, 0);
-
- if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
- input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
- /* Despite what the table says, for the HVR-950q we still need
- to be in CVBS mode for the S-Video input (reason unknown). */
- /* filter_coef_type = 3; */
- filter_coef_type = 5;
- } else {
- filter_coef_type = 5;
- }
-
- /* Load the Video Decoder Filter Coefficients */
- for (i = 0; i < NUM_FILTER_COEF; i++) {
- au8522_writereg(state, filter_coef[i].reg_name,
- filter_coef[i].reg_val[filter_coef_type]);
- }
-
- /* It's not clear what these registers are for, but they are always
- set to the same value regardless of what mode we're in */
- au8522_writereg(state, AU8522_REG42EH, 0x87);
- au8522_writereg(state, AU8522_REG42FH, 0xa2);
- au8522_writereg(state, AU8522_REG430H, 0xbf);
- au8522_writereg(state, AU8522_REG431H, 0xcb);
- au8522_writereg(state, AU8522_REG432H, 0xa1);
- au8522_writereg(state, AU8522_REG433H, 0x41);
- au8522_writereg(state, AU8522_REG434H, 0x88);
- au8522_writereg(state, AU8522_REG435H, 0xc2);
- au8522_writereg(state, AU8522_REG436H, 0x3c);
-}
-
-static void au8522_setup_cvbs_mode(struct au8522_state *state)
-{
- /* here we're going to try the pre-programmed route */
- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
-
- /* PGA in automatic mode */
- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
-
- /* Enable clamping control */
- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
-
- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
- AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
-
- setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
-
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
-}
-
-static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state)
-{
- /* here we're going to try the pre-programmed route */
- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
-
- /* It's not clear why we have to have the PGA in automatic mode while
- enabling clamp control, but it's what Windows does */
- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
-
- /* Enable clamping control */
- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);
-
- /* Disable automatic PGA (since the CVBS is coming from the tuner) */
- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);
-
- /* Set input mode to CVBS on channel 4 with SIF audio input enabled */
- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
- AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
-
- setup_decoder_defaults(state,
- AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
-
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
-}
-
-static void au8522_setup_svideo_mode(struct au8522_state *state)
-{
- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO);
-
- /* Set input to Y on Channe1, C on Channel 3 */
- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
- AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
-
- /* PGA in automatic mode */
- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
-
- /* Enable clamping control */
- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
-
- setup_decoder_defaults(state,
- AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
-
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void disable_audio_input(struct au8522_state *state)
-{
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
-
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04);
- au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02);
-
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO);
-}
-
-/* 0=disable, 1=SIF */
-static void set_audio_input(struct au8522_state *state, int aud_input)
-{
- int i;
-
- /* Note that this function needs to be used in conjunction with setting
- the input routing via register 0x81 */
-
- if (aud_input == AU8522_AUDIO_NONE) {
- disable_audio_input(state);
- return;
- }
-
- if (aud_input != AU8522_AUDIO_SIF) {
- /* The caller asked for a mode we don't currently support */
- printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n",
- aud_input);
- return;
- }
-
- /* Load the Audio Decoder Filter Coefficients */
- for (i = 0; i < NUM_LPFILTER_COEF; i++) {
- au8522_writereg(state, lpfilter_coef[i].reg_name,
- lpfilter_coef[i].reg_val[0]);
- }
-
- /* Setup audio */
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
- au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);
- au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);
- msleep(150);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00);
- msleep(1);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);
- msleep(50);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);
- msleep(80);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
- au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
- au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);
- msleep(70);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
- au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
- au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct au8522_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- state->brightness = ctrl->value;
- au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH,
- ctrl->value - 128);
- break;
- case V4L2_CID_CONTRAST:
- state->contrast = ctrl->value;
- au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH,
- ctrl->value);
- break;
- case V4L2_CID_SATURATION:
- state->saturation = ctrl->value;
- au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH,
- ctrl->value);
- au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH,
- ctrl->value);
- break;
- case V4L2_CID_HUE:
- state->hue = ctrl->value;
- au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH,
- ctrl->value >> 8);
- au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH,
- ctrl->value & 0xFF);
- 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:
- /* Not yet implemented */
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
-{
- struct au8522_state *state = to_state(sd);
-
- /* Note that we are using values cached in the state structure instead
- of reading the registers due to issues with i2c reads not working
- properly/consistently yet on the HVR-950q */
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = state->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = state->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = state->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = state->hue;
- 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:
- /* Not yet supported */
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int au8522_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct au8522_state *state = to_state(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = au8522_readreg(state, reg->reg & 0xffff);
- return 0;
-}
-
-static int au8522_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct au8522_state *state = to_state(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- au8522_writereg(state, reg->reg, reg->val & 0xff);
- return 0;
-}
-#endif
-
-static int au8522_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct au8522_state *state = to_state(sd);
-
- if (enable) {
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- 0x01);
- msleep(1);
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
- } else {
- /* This does not completely power down the device
- (it only reduces it from around 140ma to 80ma) */
- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
- 1 << 5);
- }
- return 0;
-}
-
-static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1,
- AU8522_TVDEC_CONTRAST_REG00BH_CVBS);
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0);
- default:
- break;
- }
-
- qc->type = 0;
- return -EINVAL;
-}
-
-static int au8522_reset(struct v4l2_subdev *sd, u32 val)
-{
- struct au8522_state *state = to_state(sd);
-
- state->operational_mode = AU8522_ANALOG_MODE;
-
- /* Clear out any state associated with the digital side of the
- chip, so that when it gets powered back up it won't think
- that it is already tuned */
- state->current_frequency = 0;
-
- au8522_writereg(state, 0xa4, 1 << 5);
-
- return 0;
-}
-
-static int au8522_s_video_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct au8522_state *state = to_state(sd);
-
- au8522_reset(sd, 0);
-
- /* Jam open the i2c gate to the tuner. We do this here to handle the
- case where the user went into digital mode (causing the gate to be
- closed), and then came back to analog mode */
- au8522_writereg(state, 0x106, 1);
-
- if (input == AU8522_COMPOSITE_CH1) {
- au8522_setup_cvbs_mode(state);
- } else if (input == AU8522_SVIDEO_CH13) {
- au8522_setup_svideo_mode(state);
- } else if (input == AU8522_COMPOSITE_CH4_SIF) {
- au8522_setup_cvbs_tuner_mode(state);
- } else {
- printk(KERN_ERR "au8522 mode not currently supported\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int au8522_s_audio_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct au8522_state *state = to_state(sd);
- set_audio_input(state, input);
- return 0;
-}
-
-static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- int val = 0;
- struct au8522_state *state = to_state(sd);
- u8 lock_status;
-
- /* Interrogate the decoder to see if we are getting a real signal */
- lock_status = au8522_readreg(state, 0x00);
- if (lock_status == 0xa2)
- vt->signal = 0xffff;
- else
- vt->signal = 0x00;
-
- vt->capability |=
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
-
- val = V4L2_TUNER_SUB_MONO;
- vt->rxsubchans = val;
- vt->audmode = V4L2_TUNER_MODE_STEREO;
- return 0;
-}
-
-static int au8522_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct au8522_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
-}
-
-static int au8522_log_status(struct v4l2_subdev *sd)
-{
- /* FIXME: Add some status info here */
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops au8522_core_ops = {
- .log_status = au8522_log_status,
- .g_chip_ident = au8522_g_chip_ident,
- .g_ctrl = au8522_g_ctrl,
- .s_ctrl = au8522_s_ctrl,
- .queryctrl = au8522_queryctrl,
- .reset = au8522_reset,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = au8522_g_register,
- .s_register = au8522_s_register,
-#endif
-};
-
-static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = {
- .g_tuner = au8522_g_tuner,
-};
-
-static const struct v4l2_subdev_audio_ops au8522_audio_ops = {
- .s_routing = au8522_s_audio_routing,
-};
-
-static const struct v4l2_subdev_video_ops au8522_video_ops = {
- .s_routing = au8522_s_video_routing,
- .s_stream = au8522_s_stream,
-};
-
-static const struct v4l2_subdev_ops au8522_ops = {
- .core = &au8522_core_ops,
- .tuner = &au8522_tuner_ops,
- .audio = &au8522_audio_ops,
- .video = &au8522_video_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static int au8522_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct au8522_state *state;
- struct v4l2_subdev *sd;
- int instance;
- struct au8522_config *demod_config;
-
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_BYTE_DATA)) {
- return -EIO;
- }
-
- /* allocate memory for the internal state */
- instance = au8522_get_state(&state, client->adapter, client->addr);
- switch (instance) {
- case 0:
- printk(KERN_ERR "au8522_decoder allocation failed\n");
- return -EIO;
- case 1:
- /* new demod instance */
- printk(KERN_INFO "au8522_decoder creating new instance...\n");
- break;
- default:
- /* existing demod instance */
- printk(KERN_INFO "au8522_decoder attach existing instance.\n");
- break;
- }
-
- demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);
- if (demod_config == NULL) {
- if (instance == 1)
- kfree(state);
- return -ENOMEM;
- }
- demod_config->demod_address = 0x8e >> 1;
-
- state->config = demod_config;
- state->i2c = client->adapter;
-
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &au8522_ops);
-
- state->c = client;
- state->vid_input = AU8522_COMPOSITE_CH1;
- state->aud_input = AU8522_AUDIO_NONE;
- state->id = 8522;
- state->rev = 0;
-
- /* Jam open the i2c gate to the tuner */
- au8522_writereg(state, 0x106, 1);
-
- return 0;
-}
-
-static int au8522_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- v4l2_device_unregister_subdev(sd);
- au8522_release_state(to_state(sd));
- return 0;
-}
-
-static const struct i2c_device_id au8522_id[] = {
- {"au8522", 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, au8522_id);
-
-static struct i2c_driver au8522_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "au8522",
- },
- .probe = au8522_probe,
- .remove = au8522_remove,
- .id_table = au8522_id,
-};
-
-module_i2c_driver(au8522_driver);
diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
deleted file mode 100644
index 5fc70d6cd04f..000000000000
--- a/drivers/media/dvb/frontends/au8522_dig.c
+++ /dev/null
@@ -1,826 +0,0 @@
-/*
- Auvitek AU8522 QAM/8VSB demodulator driver
-
- Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include "dvb_frontend.h"
-#include "au8522.h"
-#include "au8522_priv.h"
-
-static int debug;
-
-#define dprintk(arg...)\
- do { if (debug)\
- printk(arg);\
- } while (0)
-
-struct mse2snr_tab {
- u16 val;
- u16 data;
-};
-
-/* VSB SNR lookup table */
-static struct mse2snr_tab vsb_mse2snr_tab[] = {
- { 0, 270 },
- { 2, 250 },
- { 3, 240 },
- { 5, 230 },
- { 7, 220 },
- { 9, 210 },
- { 12, 200 },
- { 13, 195 },
- { 15, 190 },
- { 17, 185 },
- { 19, 180 },
- { 21, 175 },
- { 24, 170 },
- { 27, 165 },
- { 31, 160 },
- { 32, 158 },
- { 33, 156 },
- { 36, 152 },
- { 37, 150 },
- { 39, 148 },
- { 40, 146 },
- { 41, 144 },
- { 43, 142 },
- { 44, 140 },
- { 48, 135 },
- { 50, 130 },
- { 43, 142 },
- { 53, 125 },
- { 56, 120 },
- { 256, 115 },
-};
-
-/* QAM64 SNR lookup table */
-static struct mse2snr_tab qam64_mse2snr_tab[] = {
- { 15, 0 },
- { 16, 290 },
- { 17, 288 },
- { 18, 286 },
- { 19, 284 },
- { 20, 282 },
- { 21, 281 },
- { 22, 279 },
- { 23, 277 },
- { 24, 275 },
- { 25, 273 },
- { 26, 271 },
- { 27, 269 },
- { 28, 268 },
- { 29, 266 },
- { 30, 264 },
- { 31, 262 },
- { 32, 260 },
- { 33, 259 },
- { 34, 258 },
- { 35, 256 },
- { 36, 255 },
- { 37, 254 },
- { 38, 252 },
- { 39, 251 },
- { 40, 250 },
- { 41, 249 },
- { 42, 248 },
- { 43, 246 },
- { 44, 245 },
- { 45, 244 },
- { 46, 242 },
- { 47, 241 },
- { 48, 240 },
- { 50, 239 },
- { 51, 238 },
- { 53, 237 },
- { 54, 236 },
- { 56, 235 },
- { 57, 234 },
- { 59, 233 },
- { 60, 232 },
- { 62, 231 },
- { 63, 230 },
- { 65, 229 },
- { 67, 228 },
- { 68, 227 },
- { 70, 226 },
- { 71, 225 },
- { 73, 224 },
- { 74, 223 },
- { 76, 222 },
- { 78, 221 },
- { 80, 220 },
- { 82, 219 },
- { 85, 218 },
- { 88, 217 },
- { 90, 216 },
- { 92, 215 },
- { 93, 214 },
- { 94, 212 },
- { 95, 211 },
- { 97, 210 },
- { 99, 209 },
- { 101, 208 },
- { 102, 207 },
- { 104, 206 },
- { 107, 205 },
- { 111, 204 },
- { 114, 203 },
- { 118, 202 },
- { 122, 201 },
- { 125, 200 },
- { 128, 199 },
- { 130, 198 },
- { 132, 197 },
- { 256, 190 },
-};
-
-/* QAM256 SNR lookup table */
-static struct mse2snr_tab qam256_mse2snr_tab[] = {
- { 16, 0 },
- { 17, 400 },
- { 18, 398 },
- { 19, 396 },
- { 20, 394 },
- { 21, 392 },
- { 22, 390 },
- { 23, 388 },
- { 24, 386 },
- { 25, 384 },
- { 26, 382 },
- { 27, 380 },
- { 28, 379 },
- { 29, 378 },
- { 30, 377 },
- { 31, 376 },
- { 32, 375 },
- { 33, 374 },
- { 34, 373 },
- { 35, 372 },
- { 36, 371 },
- { 37, 370 },
- { 38, 362 },
- { 39, 354 },
- { 40, 346 },
- { 41, 338 },
- { 42, 330 },
- { 43, 328 },
- { 44, 326 },
- { 45, 324 },
- { 46, 322 },
- { 47, 320 },
- { 48, 319 },
- { 49, 318 },
- { 50, 317 },
- { 51, 316 },
- { 52, 315 },
- { 53, 314 },
- { 54, 313 },
- { 55, 312 },
- { 56, 311 },
- { 57, 310 },
- { 58, 308 },
- { 59, 306 },
- { 60, 304 },
- { 61, 302 },
- { 62, 300 },
- { 63, 298 },
- { 65, 295 },
- { 68, 294 },
- { 70, 293 },
- { 73, 292 },
- { 76, 291 },
- { 78, 290 },
- { 79, 289 },
- { 81, 288 },
- { 82, 287 },
- { 83, 286 },
- { 84, 285 },
- { 85, 284 },
- { 86, 283 },
- { 88, 282 },
- { 89, 281 },
- { 256, 280 },
-};
-
-static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
- u16 *snr)
-{
- int i, ret = -EINVAL;
- dprintk("%s()\n", __func__);
-
- for (i = 0; i < sz; i++) {
- if (mse < tab[i].val) {
- *snr = tab[i].data;
- ret = 0;
- break;
- }
- }
- dprintk("%s() snr=%d\n", __func__, *snr);
- return ret;
-}
-
-static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
-{
- struct au8522_state *state = fe->demodulator_priv;
- u8 r0b5, r0b6, r0b7;
- char *ifmhz;
-
- switch (if_freq) {
- case AU8522_IF_3_25MHZ:
- ifmhz = "3.25";
- r0b5 = 0x00;
- r0b6 = 0x3d;
- r0b7 = 0xa0;
- break;
- case AU8522_IF_4MHZ:
- ifmhz = "4.00";
- r0b5 = 0x00;
- r0b6 = 0x4b;
- r0b7 = 0xd9;
- break;
- case AU8522_IF_6MHZ:
- ifmhz = "6.00";
- r0b5 = 0xfb;
- r0b6 = 0x8e;
- r0b7 = 0x39;
- break;
- default:
- dprintk("%s() IF Frequency not supported\n", __func__);
- return -EINVAL;
- }
- dprintk("%s() %s MHz\n", __func__, ifmhz);
- au8522_writereg(state, 0x80b5, r0b5);
- au8522_writereg(state, 0x80b6, r0b6);
- au8522_writereg(state, 0x80b7, r0b7);
-
- return 0;
-}
-
-/* VSB Modulation table */
-static struct {
- u16 reg;
- u16 data;
-} VSB_mod_tab[] = {
- { 0x8090, 0x84 },
- { 0x4092, 0x11 },
- { 0x2005, 0x00 },
- { 0x8091, 0x80 },
- { 0x80a3, 0x0c },
- { 0x80a4, 0xe8 },
- { 0x8081, 0xc4 },
- { 0x80a5, 0x40 },
- { 0x80a7, 0x40 },
- { 0x80a6, 0x67 },
- { 0x8262, 0x20 },
- { 0x821c, 0x30 },
- { 0x80d8, 0x1a },
- { 0x8227, 0xa0 },
- { 0x8121, 0xff },
- { 0x80a8, 0xf0 },
- { 0x80a9, 0x05 },
- { 0x80aa, 0x77 },
- { 0x80ab, 0xf0 },
- { 0x80ac, 0x05 },
- { 0x80ad, 0x77 },
- { 0x80ae, 0x41 },
- { 0x80af, 0x66 },
- { 0x821b, 0xcc },
- { 0x821d, 0x80 },
- { 0x80a4, 0xe8 },
- { 0x8231, 0x13 },
-};
-
-/* QAM64 Modulation table */
-static struct {
- u16 reg;
- u16 data;
-} QAM64_mod_tab[] = {
- { 0x00a3, 0x09 },
- { 0x00a4, 0x00 },
- { 0x0081, 0xc4 },
- { 0x00a5, 0x40 },
- { 0x00aa, 0x77 },
- { 0x00ad, 0x77 },
- { 0x00a6, 0x67 },
- { 0x0262, 0x20 },
- { 0x021c, 0x30 },
- { 0x00b8, 0x3e },
- { 0x00b9, 0xf0 },
- { 0x00ba, 0x01 },
- { 0x00bb, 0x18 },
- { 0x00bc, 0x50 },
- { 0x00bd, 0x00 },
- { 0x00be, 0xea },
- { 0x00bf, 0xef },
- { 0x00c0, 0xfc },
- { 0x00c1, 0xbd },
- { 0x00c2, 0x1f },
- { 0x00c3, 0xfc },
- { 0x00c4, 0xdd },
- { 0x00c5, 0xaf },
- { 0x00c6, 0x00 },
- { 0x00c7, 0x38 },
- { 0x00c8, 0x30 },
- { 0x00c9, 0x05 },
- { 0x00ca, 0x4a },
- { 0x00cb, 0xd0 },
- { 0x00cc, 0x01 },
- { 0x00cd, 0xd9 },
- { 0x00ce, 0x6f },
- { 0x00cf, 0xf9 },
- { 0x00d0, 0x70 },
- { 0x00d1, 0xdf },
- { 0x00d2, 0xf7 },
- { 0x00d3, 0xc2 },
- { 0x00d4, 0xdf },
- { 0x00d5, 0x02 },
- { 0x00d6, 0x9a },
- { 0x00d7, 0xd0 },
- { 0x0250, 0x0d },
- { 0x0251, 0xcd },
- { 0x0252, 0xe0 },
- { 0x0253, 0x05 },
- { 0x0254, 0xa7 },
- { 0x0255, 0xff },
- { 0x0256, 0xed },
- { 0x0257, 0x5b },
- { 0x0258, 0xae },
- { 0x0259, 0xe6 },
- { 0x025a, 0x3d },
- { 0x025b, 0x0f },
- { 0x025c, 0x0d },
- { 0x025d, 0xea },
- { 0x025e, 0xf2 },
- { 0x025f, 0x51 },
- { 0x0260, 0xf5 },
- { 0x0261, 0x06 },
- { 0x021a, 0x00 },
- { 0x0546, 0x40 },
- { 0x0210, 0xc7 },
- { 0x0211, 0xaa },
- { 0x0212, 0xab },
- { 0x0213, 0x02 },
- { 0x0502, 0x00 },
- { 0x0121, 0x04 },
- { 0x0122, 0x04 },
- { 0x052e, 0x10 },
- { 0x00a4, 0xca },
- { 0x00a7, 0x40 },
- { 0x0526, 0x01 },
-};
-
-/* QAM256 Modulation table */
-static struct {
- u16 reg;
- u16 data;
-} QAM256_mod_tab[] = {
- { 0x80a3, 0x09 },
- { 0x80a4, 0x00 },
- { 0x8081, 0xc4 },
- { 0x80a5, 0x40 },
- { 0x80aa, 0x77 },
- { 0x80ad, 0x77 },
- { 0x80a6, 0x67 },
- { 0x8262, 0x20 },
- { 0x821c, 0x30 },
- { 0x80b8, 0x3e },
- { 0x80b9, 0xf0 },
- { 0x80ba, 0x01 },
- { 0x80bb, 0x18 },
- { 0x80bc, 0x50 },
- { 0x80bd, 0x00 },
- { 0x80be, 0xea },
- { 0x80bf, 0xef },
- { 0x80c0, 0xfc },
- { 0x80c1, 0xbd },
- { 0x80c2, 0x1f },
- { 0x80c3, 0xfc },
- { 0x80c4, 0xdd },
- { 0x80c5, 0xaf },
- { 0x80c6, 0x00 },
- { 0x80c7, 0x38 },
- { 0x80c8, 0x30 },
- { 0x80c9, 0x05 },
- { 0x80ca, 0x4a },
- { 0x80cb, 0xd0 },
- { 0x80cc, 0x01 },
- { 0x80cd, 0xd9 },
- { 0x80ce, 0x6f },
- { 0x80cf, 0xf9 },
- { 0x80d0, 0x70 },
- { 0x80d1, 0xdf },
- { 0x80d2, 0xf7 },
- { 0x80d3, 0xc2 },
- { 0x80d4, 0xdf },
- { 0x80d5, 0x02 },
- { 0x80d6, 0x9a },
- { 0x80d7, 0xd0 },
- { 0x8250, 0x0d },
- { 0x8251, 0xcd },
- { 0x8252, 0xe0 },
- { 0x8253, 0x05 },
- { 0x8254, 0xa7 },
- { 0x8255, 0xff },
- { 0x8256, 0xed },
- { 0x8257, 0x5b },
- { 0x8258, 0xae },
- { 0x8259, 0xe6 },
- { 0x825a, 0x3d },
- { 0x825b, 0x0f },
- { 0x825c, 0x0d },
- { 0x825d, 0xea },
- { 0x825e, 0xf2 },
- { 0x825f, 0x51 },
- { 0x8260, 0xf5 },
- { 0x8261, 0x06 },
- { 0x821a, 0x00 },
- { 0x8546, 0x40 },
- { 0x8210, 0x26 },
- { 0x8211, 0xf6 },
- { 0x8212, 0x84 },
- { 0x8213, 0x02 },
- { 0x8502, 0x01 },
- { 0x8121, 0x04 },
- { 0x8122, 0x04 },
- { 0x852e, 0x10 },
- { 0x80a4, 0xca },
- { 0x80a7, 0x40 },
- { 0x8526, 0x01 },
-};
-
-static int au8522_enable_modulation(struct dvb_frontend *fe,
- fe_modulation_t m)
-{
- struct au8522_state *state = fe->demodulator_priv;
- int i;
-
- dprintk("%s(0x%08x)\n", __func__, m);
-
- switch (m) {
- case VSB_8:
- dprintk("%s() VSB_8\n", __func__);
- for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
- au8522_writereg(state,
- VSB_mod_tab[i].reg,
- VSB_mod_tab[i].data);
- au8522_set_if(fe, state->config->vsb_if);
- break;
- case QAM_64:
- dprintk("%s() QAM 64\n", __func__);
- for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++)
- au8522_writereg(state,
- QAM64_mod_tab[i].reg,
- QAM64_mod_tab[i].data);
- au8522_set_if(fe, state->config->qam_if);
- break;
- case QAM_256:
- dprintk("%s() QAM 256\n", __func__);
- for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++)
- au8522_writereg(state,
- QAM256_mod_tab[i].reg,
- QAM256_mod_tab[i].data);
- au8522_set_if(fe, state->config->qam_if);
- break;
- default:
- dprintk("%s() Invalid modulation\n", __func__);
- return -EINVAL;
- }
-
- state->current_modulation = m;
-
- return 0;
-}
-
-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
-static int au8522_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct au8522_state *state = fe->demodulator_priv;
- int ret = -EINVAL;
-
- dprintk("%s(frequency=%d)\n", __func__, c->frequency);
-
- if ((state->current_frequency == c->frequency) &&
- (state->current_modulation == c->modulation))
- return 0;
-
- if (fe->ops.tuner_ops.set_params) {
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- ret = fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- if (ret < 0)
- return ret;
-
- /* Allow the tuner to settle */
- msleep(100);
-
- au8522_enable_modulation(fe, c->modulation);
-
- state->current_frequency = c->frequency;
-
- return 0;
-}
-
-static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct au8522_state *state = fe->demodulator_priv;
- u8 reg;
- u32 tuner_status = 0;
-
- *status = 0;
-
- if (state->current_modulation == VSB_8) {
- dprintk("%s() Checking VSB_8\n", __func__);
- reg = au8522_readreg(state, 0x4088);
- if ((reg & 0x03) == 0x03)
- *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
- } else {
- dprintk("%s() Checking QAM\n", __func__);
- reg = au8522_readreg(state, 0x4541);
- if (reg & 0x80)
- *status |= FE_HAS_VITERBI;
- if (reg & 0x20)
- *status |= FE_HAS_LOCK | FE_HAS_SYNC;
- }
-
- switch (state->config->status_mode) {
- case AU8522_DEMODLOCKING:
- dprintk("%s() DEMODLOCKING\n", __func__);
- if (*status & FE_HAS_VITERBI)
- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
- break;
- case AU8522_TUNERLOCKING:
- /* Get the tuner status */
- dprintk("%s() TUNERLOCKING\n", __func__);
- if (fe->ops.tuner_ops.get_status) {
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- fe->ops.tuner_ops.get_status(fe, &tuner_status);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- if (tuner_status)
- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
- break;
- }
- state->fe_status = *status;
-
- if (*status & FE_HAS_LOCK)
- /* turn on LED, if it isn't on already */
- au8522_led_ctrl(state, -1);
- else
- /* turn off LED */
- au8522_led_ctrl(state, 0);
-
- dprintk("%s() status 0x%08x\n", __func__, *status);
-
- return 0;
-}
-
-static int au8522_led_status(struct au8522_state *state, const u16 *snr)
-{
- struct au8522_led_config *led_config = state->config->led_cfg;
- int led;
- u16 strong;
-
- /* bail out if we can't control an LED */
- if (!led_config)
- return 0;
-
- if (0 == (state->fe_status & FE_HAS_LOCK))
- return au8522_led_ctrl(state, 0);
- else if (state->current_modulation == QAM_256)
- strong = led_config->qam256_strong;
- else if (state->current_modulation == QAM_64)
- strong = led_config->qam64_strong;
- else /* (state->current_modulation == VSB_8) */
- strong = led_config->vsb8_strong;
-
- if (*snr >= strong)
- led = 2;
- else
- led = 1;
-
- if ((state->led_state) &&
- (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
- /* snr didn't change enough to bother
- * changing the color of the led */
- return 0;
-
- return au8522_led_ctrl(state, led);
-}
-
-static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct au8522_state *state = fe->demodulator_priv;
- int ret = -EINVAL;
-
- dprintk("%s()\n", __func__);
-
- if (state->current_modulation == QAM_256)
- ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
- ARRAY_SIZE(qam256_mse2snr_tab),
- au8522_readreg(state, 0x4522),
- snr);
- else if (state->current_modulation == QAM_64)
- ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
- ARRAY_SIZE(qam64_mse2snr_tab),
- au8522_readreg(state, 0x4522),
- snr);
- else /* VSB_8 */
- ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
- ARRAY_SIZE(vsb_mse2snr_tab),
- au8522_readreg(state, 0x4311),
- snr);
-
- if (state->config->led_cfg)
- au8522_led_status(state, snr);
-
- return ret;
-}
-
-static int au8522_read_signal_strength(struct dvb_frontend *fe,
- u16 *signal_strength)
-{
- /* borrowed from lgdt330x.c
- *
- * Calculate strength from SNR up to 35dB
- * Even though the SNR can go higher than 35dB,
- * there is some comfort factor in having a range of
- * strong signals that can show at 100%
- */
- u16 snr;
- u32 tmp;
- int ret = au8522_read_snr(fe, &snr);
-
- *signal_strength = 0;
-
- if (0 == ret) {
- /* The following calculation method was chosen
- * purely for the sake of code re-use from the
- * other demod drivers that use this method */
-
- /* Convert from SNR in dB * 10 to 8.24 fixed-point */
- tmp = (snr * ((1 << 24) / 10));
-
- /* Convert from 8.24 fixed-point to
- * scale the range 0 - 35*2^24 into 0 - 65535*/
- if (tmp >= 8960 * 0x10000)
- *signal_strength = 0xffff;
- else
- *signal_strength = tmp / 8960;
- }
-
- return ret;
-}
-
-static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct au8522_state *state = fe->demodulator_priv;
-
- if (state->current_modulation == VSB_8)
- *ucblocks = au8522_readreg(state, 0x4087);
- else
- *ucblocks = au8522_readreg(state, 0x4543);
-
- return 0;
-}
-
-static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- return au8522_read_ucblocks(fe, ber);
-}
-
-static int au8522_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct au8522_state *state = fe->demodulator_priv;
-
- c->frequency = state->current_frequency;
- c->modulation = state->current_modulation;
-
- return 0;
-}
-
-static int au8522_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *tune)
-{
- tune->min_delay_ms = 1000;
- return 0;
-}
-
-static struct dvb_frontend_ops au8522_ops;
-
-
-static void au8522_release(struct dvb_frontend *fe)
-{
- struct au8522_state *state = fe->demodulator_priv;
- au8522_release_state(state);
-}
-
-struct dvb_frontend *au8522_attach(const struct au8522_config *config,
- struct i2c_adapter *i2c)
-{
- struct au8522_state *state = NULL;
- int instance;
-
- /* allocate memory for the internal state */
- instance = au8522_get_state(&state, i2c, config->demod_address);
- switch (instance) {
- case 0:
- dprintk("%s state allocation failed\n", __func__);
- break;
- case 1:
- /* new demod instance */
- dprintk("%s using new instance\n", __func__);
- break;
- default:
- /* existing demod instance */
- dprintk("%s using existing instance\n", __func__);
- break;
- }
-
- /* setup the state */
- state->config = config;
- state->i2c = i2c;
- state->operational_mode = AU8522_DIGITAL_MODE;
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &au8522_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
-
- if (au8522_init(&state->frontend) != 0) {
- printk(KERN_ERR "%s: Failed to initialize correctly\n",
- __func__);
- goto error;
- }
-
- /* Note: Leaving the I2C gate open here. */
- au8522_i2c_gate_ctrl(&state->frontend, 1);
-
- return &state->frontend;
-
-error:
- au8522_release_state(state);
- return NULL;
-}
-EXPORT_SYMBOL(au8522_attach);
-
-static struct dvb_frontend_ops au8522_ops = {
- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
- .info = {
- .name = "Auvitek AU8522 QAM/8VSB Frontend",
- .frequency_min = 54000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 62500,
- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
- },
-
- .init = au8522_init,
- .sleep = au8522_sleep,
- .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
- .set_frontend = au8522_set_frontend,
- .get_frontend = au8522_get_frontend,
- .get_tune_settings = au8522_get_tune_settings,
- .read_status = au8522_read_status,
- .read_ber = au8522_read_ber,
- .read_signal_strength = au8522_read_signal_strength,
- .read_snr = au8522_read_snr,
- .read_ucblocks = au8522_read_ucblocks,
- .release = au8522_release,
-};
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Enable verbose debug messages");
-
-MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
-MODULE_AUTHOR("Steven Toth");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h
deleted file mode 100644
index 6e4a438732b5..000000000000
--- a/drivers/media/dvb/frontends/au8522_priv.h
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- Auvitek AU8522 QAM/8VSB demodulator driver
-
- Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
- Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
- Copyright (C) 2005-2008 Auvitek International, Ltd.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You 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/init.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-#include "au8522.h"
-#include "tuner-i2c.h"
-
-#define AU8522_ANALOG_MODE 0
-#define AU8522_DIGITAL_MODE 1
-
-struct au8522_state {
- struct i2c_client *c;
- struct i2c_adapter *i2c;
-
- u8 operational_mode;
-
- /* Used for sharing of the state between analog and digital mode */
- struct tuner_i2c_props i2c_props;
- struct list_head hybrid_tuner_instance_list;
-
- /* configuration settings */
- const struct au8522_config *config;
-
- struct dvb_frontend frontend;
-
- u32 current_frequency;
- fe_modulation_t current_modulation;
-
- u32 fe_status;
- unsigned int led_state;
-
- /* Analog settings */
- struct v4l2_subdev sd;
- v4l2_std_id std;
- int vid_input;
- int aud_input;
- u32 id;
- u32 rev;
- u8 brightness;
- u8 contrast;
- u8 saturation;
- s16 hue;
-};
-
-/* These are routines shared by both the VSB/QAM demodulator and the analog
- decoder */
-int au8522_writereg(struct au8522_state *state, u16 reg, u8 data);
-u8 au8522_readreg(struct au8522_state *state, u16 reg);
-int au8522_init(struct dvb_frontend *fe);
-int au8522_sleep(struct dvb_frontend *fe);
-
-int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
- u8 client_address);
-void au8522_release_state(struct au8522_state *state);
-int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
-int au8522_led_ctrl(struct au8522_state *state, int led);
-
-/* REGISTERS */
-#define AU8522_INPUT_CONTROL_REG081H 0x081
-#define AU8522_PGA_CONTROL_REG082H 0x082
-#define AU8522_CLAMPING_CONTROL_REG083H 0x083
-
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5
-#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6
-#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7
-#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8
-#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9
-#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA
-#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB
-#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC
-#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD
-#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE
-#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF
-
-/* Receiver registers */
-#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0
-#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1
-#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2
-#define AU8522_TOREGAGC1_REG0B3H 0x0B3
-#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4
-#define AU8522_FRMREGBBH_REG0B5H 0x0B5
-#define AU8522_FRMREGBBM_REG0B6H 0x0B6
-#define AU8522_FRMREGBBL_REG0B7H 0x0B7
-/* 0xB8 TO 0xD7 are the filter coefficients */
-#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8
-#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9
-#define AU8522_TOREGAGC2_REG0DAH 0x0DA
-#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB
-#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC
-#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD
-#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE
-#define AU8522_TOREGFREQ_REG0DFH 0x0DF
-
-#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB
-#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC
-#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED
-
-#define AU8522_CHIP_MODE_REG0FEH 0x0FE
-
-/* I2C bus control registers */
-#define AU8522_I2C_CONTROL_REG0_REG090H 0x090
-#define AU8522_I2C_CONTROL_REG1_REG091H 0x091
-#define AU8522_I2C_STATUS_REG092H 0x092
-#define AU8522_I2C_WR_DATA0_REG093H 0x093
-#define AU8522_I2C_WR_DATA1_REG094H 0x094
-#define AU8522_I2C_WR_DATA2_REG095H 0x095
-#define AU8522_I2C_WR_DATA3_REG096H 0x096
-#define AU8522_I2C_WR_DATA4_REG097H 0x097
-#define AU8522_I2C_WR_DATA5_REG098H 0x098
-#define AU8522_I2C_WR_DATA6_REG099H 0x099
-#define AU8522_I2C_WR_DATA7_REG09AH 0x09A
-#define AU8522_I2C_RD_DATA0_REG09BH 0x09B
-#define AU8522_I2C_RD_DATA1_REG09CH 0x09C
-#define AU8522_I2C_RD_DATA2_REG09DH 0x09D
-#define AU8522_I2C_RD_DATA3_REG09EH 0x09E
-#define AU8522_I2C_RD_DATA4_REG09FH 0x09F
-#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0
-#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1
-#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2
-
-#define AU8522_ENA_USB_REG101H 0x101
-
-#define AU8522_I2S_CTRL_0_REG110H 0x110
-#define AU8522_I2S_CTRL_1_REG111H 0x111
-#define AU8522_I2S_CTRL_2_REG112H 0x112
-
-#define AU8522_FRMREGFFECONTROL_REG121H 0x121
-#define AU8522_FRMREGDFECONTROL_REG122H 0x122
-
-#define AU8522_CARRFREQOFFSET0_REG201H 0x201
-#define AU8522_CARRFREQOFFSET1_REG202H 0x202
-
-#define AU8522_DECIMATION_GAIN_REG21AH 0x21A
-#define AU8522_FRMREGIFSLP_REG21BH 0x21B
-#define AU8522_FRMREGTHRDL2_REG21CH 0x21C
-#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D
-#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E
-#define AU8522_FRMREGPLLMODE_REG21FH 0x21F
-#define AU8522_FRMREGCSTHRD_REG220H 0x220
-#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221
-#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222
-#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223
-#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224
-#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225
-#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226
-#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227
-#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228
-
-/* Analog TV Decoder */
-#define AU8522_TVDEC_STATUS_REG000H 0x000
-#define AU8522_TVDEC_INT_STATUS_REG001H 0x001
-#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002
-#define AU8522_TVDEC_SHARPNESSREG009H 0x009
-#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A
-#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B
-#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C
-#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D
-#define AU8522_TVDEC_HUE_H_REG00EH 0x00E
-#define AU8522_TVDEC_HUE_L_REG00FH 0x00F
-#define AU8522_TVDEC_INT_MASK_REG010H 0x010
-#define AU8522_VIDEO_MODE_REG011H 0x011
-#define AU8522_TVDEC_PGA_REG012H 0x012
-#define AU8522_TVDEC_COMB_MODE_REG015H 0x015
-#define AU8522_REG016H 0x016
-#define AU8522_TVDED_DBG_MODE_REG060H 0x060
-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061
-#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062
-#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063
-#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064
-#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065
-#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066
-#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067
-#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068
-#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069
-#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A
-#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B
-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C
-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D
-#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E
-#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F
-#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070
-#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073
-#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077
-#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078
-#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079
-#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A
-#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B
-#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E
-#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F
-
-#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4
-#define AU8522_TOREGAAGC_REG0E5H 0x0E5
-
-#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401
-#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402
-#define AU8522_FILTER_COEF_R410 0x410
-#define AU8522_FILTER_COEF_R411 0x411
-#define AU8522_FILTER_COEF_R412 0x412
-#define AU8522_FILTER_COEF_R413 0x413
-#define AU8522_FILTER_COEF_R414 0x414
-#define AU8522_FILTER_COEF_R415 0x415
-#define AU8522_FILTER_COEF_R416 0x416
-#define AU8522_FILTER_COEF_R417 0x417
-#define AU8522_FILTER_COEF_R418 0x418
-#define AU8522_FILTER_COEF_R419 0x419
-#define AU8522_FILTER_COEF_R41A 0x41A
-#define AU8522_FILTER_COEF_R41B 0x41B
-#define AU8522_FILTER_COEF_R41C 0x41C
-#define AU8522_FILTER_COEF_R41D 0x41D
-#define AU8522_FILTER_COEF_R41E 0x41E
-#define AU8522_FILTER_COEF_R41F 0x41F
-#define AU8522_FILTER_COEF_R420 0x420
-#define AU8522_FILTER_COEF_R421 0x421
-#define AU8522_FILTER_COEF_R422 0x422
-#define AU8522_FILTER_COEF_R423 0x423
-#define AU8522_FILTER_COEF_R424 0x424
-#define AU8522_FILTER_COEF_R425 0x425
-#define AU8522_FILTER_COEF_R426 0x426
-#define AU8522_FILTER_COEF_R427 0x427
-#define AU8522_FILTER_COEF_R428 0x428
-#define AU8522_FILTER_COEF_R429 0x429
-#define AU8522_FILTER_COEF_R42A 0x42A
-#define AU8522_FILTER_COEF_R42B 0x42B
-#define AU8522_FILTER_COEF_R42C 0x42C
-#define AU8522_FILTER_COEF_R42D 0x42D
-
-/* VBI Control Registers */
-#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004
-#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005
-#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006
-#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007
-#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017
-#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018
-#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019
-#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A
-#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B
-#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C
-#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E
-#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F
-#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020
-#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021
-#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022
-#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023
-
-#define AU8522_REG071H 0x071
-#define AU8522_REG072H 0x072
-#define AU8522_REG074H 0x074
-#define AU8522_REG075H 0x075
-
-/* Digital Demodulator Registers */
-#define AU8522_FRAME_COUNT0_REG084H 0x084
-#define AU8522_RS_STATUS_G0_REG085H 0x085
-#define AU8522_RS_STATUS_B0_REG086H 0x086
-#define AU8522_RS_STATUS_E_REG087H 0x087
-#define AU8522_DEMODULATION_STATUS_REG088H 0x088
-#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6
-#define AU8522_TSPORT_CONTROL_REG10BH 0x10B
-#define AU8522_TSTHES_REG10CH 0x10C
-#define AU8522_FRMREGDFEKEEP_REG301H 0x301
-#define AU8522_DFE_AVERAGE_REG302H 0x302
-#define AU8522_FRMREGEQLERRWIN_REG303H 0x303
-#define AU8522_FRMREGFFEKEEP_REG304H 0x304
-#define AU8522_FRMREGDFECONTROL1_REG305H 0x305
-#define AU8522_FRMREGEQLERRLOW_REG306H 0x306
-
-#define AU8522_REG42EH 0x42E
-#define AU8522_REG42FH 0x42F
-#define AU8522_REG430H 0x430
-#define AU8522_REG431H 0x431
-#define AU8522_REG432H 0x432
-#define AU8522_REG433H 0x433
-#define AU8522_REG434H 0x434
-#define AU8522_REG435H 0x435
-#define AU8522_REG436H 0x436
-
-/* GPIO Registers */
-#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0
-#define AU8522_GPIO_STATUS_REG0E1H 0x0E1
-#define AU8522_GPIO_DATA_REG0E2H 0x0E2
-
-/* Audio Control Registers */
-#define AU8522_AUDIOAGC_REG0EEH 0x0EE
-#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0
-#define AU8522_AUDIO_MODE_REG0F1H 0x0F1
-#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2
-#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3
-#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4
-#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7
-#define AU8522_REG0F9H 0x0F9
-
-#define AU8522_AUDIOAGC2_REG605H 0x605
-#define AU8522_AUDIOFREQ_REG606H 0x606
-
-
-/**************************************************************/
-
-#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4
-#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4
-#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4
-#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4
-#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4
-#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20
-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2
-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0
-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69
-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68
-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28
-/* CH1 AS Y,CH3 AS C */
-#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23
-/* CH2 AS Y,CH4 AS C */
-#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A
-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02
-
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD
-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD
-
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01
-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01
-
-/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */
-#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79
-#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80
-#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80
-#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00
-#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00
-#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F
-#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00
-#define AU8522_REG016H_CVBS 0x00
-#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00
-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS 0x0B
-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13 0x03
-#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13 0x00
-#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19
-#define AU8522_REG0F9H_AUDIO 0x20
-#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7
-#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A
-#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32
-#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19
-#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23
-#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41
-#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A
-#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32
-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34
-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a
-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05
-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15
-#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E
-#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F
-#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80
-#define AU8522_REG071H_CVBS 0x18
-#define AU8522_REG072H_CVBS 0x30
-#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0
-#define AU8522_REG074H_CVBS 0x80
-#define AU8522_REG075H_CVBS 0xF0
-#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB
-#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04
-#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00
-#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00
-#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE
-#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE
-#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00
-#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40
-
-/* Enables Closed captioning */
-#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21
diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h
deleted file mode 100644
index 5aa306ebb7ef..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#ifndef CXD2820R_H
-#define CXD2820R_H
-
-#include <linux/dvb/frontend.h>
-
-#define CXD2820R_GPIO_D (0 << 0) /* disable */
-#define CXD2820R_GPIO_E (1 << 0) /* enable */
-#define CXD2820R_GPIO_O (0 << 1) /* output */
-#define CXD2820R_GPIO_I (1 << 1) /* input */
-#define CXD2820R_GPIO_L (0 << 2) /* output low */
-#define CXD2820R_GPIO_H (1 << 2) /* output high */
-
-#define CXD2820R_TS_SERIAL 0x08
-#define CXD2820R_TS_SERIAL_MSB 0x28
-#define CXD2820R_TS_PARALLEL 0x30
-#define CXD2820R_TS_PARALLEL_MSB 0x70
-
-struct cxd2820r_config {
- /* Demodulator I2C address.
- * Driver determines DVB-C slave I2C address automatically from master
- * address.
- * Default: none, must set
- * Values: 0x6c, 0x6d
- */
- u8 i2c_address;
-
- /* TS output mode.
- * Default: none, must set.
- * Values:
- */
- u8 ts_mode;
-
- /* IF AGC polarity.
- * Default: 0
- * Values: 0, 1
- */
- bool if_agc_polarity;
-
- /* Spectrum inversion.
- * Default: 0
- * Values: 0, 1
- */
- bool spec_inv;
-
- /* GPIOs for all used modes.
- * Default: none, disabled
- * Values: <see above>
- */
- u8 gpio_dvbt[3];
- u8 gpio_dvbt2[3];
- u8 gpio_dvbc[3];
-};
-
-
-#if defined(CONFIG_DVB_CXD2820R) || \
- (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE))
-extern struct dvb_frontend *cxd2820r_attach(
- const struct cxd2820r_config *config,
- struct i2c_adapter *i2c
-);
-#else
-static inline struct dvb_frontend *cxd2820r_attach(
- const struct cxd2820r_config *config,
- struct i2c_adapter *i2c
-)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-
-#endif
-
-#endif /* CXD2820R_H */
diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c
deleted file mode 100644
index ed3b0ba624de..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r_c.c
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#include "cxd2820r_priv.h"
-
-int cxd2820r_set_frontend_c(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u8 buf[2];
- u32 if_freq;
- u16 if_ctl;
- u64 num;
- struct reg_val_mask tab[] = {
- { 0x00080, 0x01, 0xff },
- { 0x00081, 0x05, 0xff },
- { 0x00085, 0x07, 0xff },
- { 0x00088, 0x01, 0xff },
-
- { 0x00082, 0x20, 0x60 },
- { 0x1016a, 0x48, 0xff },
- { 0x100a5, 0x00, 0x01 },
- { 0x10020, 0x06, 0x07 },
- { 0x10059, 0x50, 0xff },
- { 0x10087, 0x0c, 0x3c },
- { 0x1008b, 0x07, 0xff },
- { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 },
- { 0x10070, priv->cfg.ts_mode, 0xff },
- };
-
- dbg("%s: RF=%d SR=%d", __func__, c->frequency, c->symbol_rate);
-
- /* update GPIOs */
- ret = cxd2820r_gpio(fe);
- if (ret)
- goto error;
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- if (priv->delivery_system != SYS_DVBC_ANNEX_A) {
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- tab[i].val, tab[i].mask);
- if (ret)
- goto error;
- }
- }
-
- priv->delivery_system = SYS_DVBC_ANNEX_A;
- priv->ber_running = 0; /* tune stops BER counter */
-
- /* program IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency) {
- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
- if (ret)
- goto error;
- } else
- if_freq = 0;
-
- dbg("%s: if_freq=%d", __func__, if_freq);
-
- num = if_freq / 1000; /* Hz => kHz */
- num *= 0x4000;
- if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- buf[0] = (if_ctl >> 8) & 0x3f;
- buf[1] = (if_ctl >> 0) & 0xff;
-
- ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- if (ret)
- goto error;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_get_frontend_c(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- u8 buf[2];
-
- ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2);
- if (ret)
- goto error;
-
- c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]);
-
- ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]);
- if (ret)
- goto error;
-
- switch ((buf[0] >> 0) & 0x07) {
- case 0:
- c->modulation = QAM_16;
- break;
- case 1:
- c->modulation = QAM_32;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- case 3:
- c->modulation = QAM_128;
- break;
- case 4:
- c->modulation = QAM_256;
- break;
- }
-
- switch ((buf[0] >> 7) & 0x01) {
- case 0:
- c->inversion = INVERSION_OFF;
- break;
- case 1:
- c->inversion = INVERSION_ON;
- break;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[3], start_ber = 0;
- *ber = 0;
-
- if (priv->ber_running) {
- ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf));
- if (ret)
- goto error;
-
- if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
- *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
- start_ber = 1;
- }
- } else {
- priv->ber_running = 1;
- start_ber = 1;
- }
-
- if (start_ber) {
- /* (re)start BER */
- ret = cxd2820r_wr_reg(priv, 0x10079, 0x01);
- if (ret)
- goto error;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 tmp;
-
- ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf));
- if (ret)
- goto error;
-
- tmp = (buf[0] & 0x03) << 8 | buf[1];
- tmp = (~tmp & 0x03ff);
-
- if (tmp == 512)
- /* ~no signal */
- tmp = 0;
- else if (tmp > 350)
- tmp = 350;
-
- /* scale value to 0x0000-0xffff */
- *strength = tmp * 0xffff / (350-0);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 tmp;
- unsigned int A, B;
- /* report SNR in dB * 10 */
-
- ret = cxd2820r_rd_reg(priv, 0x10019, &tmp);
- if (ret)
- goto error;
-
- if (((tmp >> 0) & 0x03) % 2) {
- A = 875;
- B = 650;
- } else {
- A = 950;
- B = 760;
- }
-
- ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp);
- if (ret)
- goto error;
-
- #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */
- if (tmp)
- *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5)
- / 10;
- else
- *snr = 0;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- /* no way to read ? */
- return 0;
-}
-
-int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- *status = 0;
-
- ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf));
- if (ret)
- goto error;
-
- if (((buf[0] >> 0) & 0x01) == 1) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC;
-
- if (((buf[1] >> 3) & 0x01) == 1) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- }
- }
-
- dbg("%s: lock=%02x %02x", __func__, buf[0], buf[1]);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_init_c(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
-
- ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
- if (ret)
- goto error;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_sleep_c(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret, i;
- struct reg_val_mask tab[] = {
- { 0x000ff, 0x1f, 0xff },
- { 0x00085, 0x00, 0xff },
- { 0x00088, 0x01, 0xff },
- { 0x00081, 0x00, 0xff },
- { 0x00080, 0x00, 0xff },
- };
-
- dbg("%s", __func__);
-
- priv->delivery_system = SYS_UNDEFINED;
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret)
- goto error;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 500;
- s->step_size = 0; /* no zigzag */
- s->max_drift = 0;
-
- return 0;
-}
diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
deleted file mode 100644
index 3bba37d74f57..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r_core.c
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#include "cxd2820r_priv.h"
-
-int cxd2820r_debug;
-module_param_named(debug, cxd2820r_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-/* write multiple registers */
-static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
- u8 *val, int len)
-{
- int ret;
- u8 buf[len+1];
- struct i2c_msg msg[1] = {
- {
- .addr = i2c,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg,
- u8 *val, int len)
-{
- int ret;
- u8 buf[len];
- struct i2c_msg msg[2] = {
- {
- .addr = i2c,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = i2c,
- .flags = I2C_M_RD,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, buf, len);
- ret = 0;
- } else {
- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write multiple registers */
-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- int len)
-{
- int ret;
- u8 i2c_addr;
- u8 reg = (reginfo >> 0) & 0xff;
- u8 bank = (reginfo >> 8) & 0xff;
- u8 i2c = (reginfo >> 16) & 0x01;
-
- /* select I2C */
- if (i2c)
- i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
- else
- i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
-
- /* switch bank if needed */
- if (bank != priv->bank[i2c]) {
- ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
- if (ret)
- return ret;
- priv->bank[i2c] = bank;
- }
- return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len);
-}
-
-/* read multiple registers */
-int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- int len)
-{
- int ret;
- u8 i2c_addr;
- u8 reg = (reginfo >> 0) & 0xff;
- u8 bank = (reginfo >> 8) & 0xff;
- u8 i2c = (reginfo >> 16) & 0x01;
-
- /* select I2C */
- if (i2c)
- i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */
- else
- i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */
-
- /* switch bank if needed */
- if (bank != priv->bank[i2c]) {
- ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1);
- if (ret)
- return ret;
- priv->bank[i2c] = bank;
- }
- return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len);
-}
-
-/* write single register */
-int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val)
-{
- return cxd2820r_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val)
-{
- return cxd2820r_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
- u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = cxd2820r_rd_reg(priv, reg, &tmp);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return cxd2820r_wr_reg(priv, reg, val);
-}
-
-int cxd2820r_gpio(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret, i;
- u8 *gpio, tmp0, tmp1;
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
-
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- gpio = priv->cfg.gpio_dvbt;
- break;
- case SYS_DVBT2:
- gpio = priv->cfg.gpio_dvbt2;
- break;
- case SYS_DVBC_ANNEX_AC:
- gpio = priv->cfg.gpio_dvbc;
- break;
- default:
- ret = -EINVAL;
- goto error;
- }
-
- /* update GPIOs only when needed */
- if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio)))
- return 0;
-
- tmp0 = 0x00;
- tmp1 = 0x00;
- for (i = 0; i < sizeof(priv->gpio); i++) {
- /* enable / disable */
- if (gpio[i] & CXD2820R_GPIO_E)
- tmp0 |= (2 << 6) >> (2 * i);
- else
- tmp0 |= (1 << 6) >> (2 * i);
-
- /* input / output */
- if (gpio[i] & CXD2820R_GPIO_I)
- tmp1 |= (1 << (3 + i));
- else
- tmp1 |= (0 << (3 + i));
-
- /* high / low */
- if (gpio[i] & CXD2820R_GPIO_H)
- tmp1 |= (1 << (0 + i));
- else
- tmp1 |= (0 << (0 + i));
-
- dbg("%s: GPIO i=%d %02x %02x", __func__, i, tmp0, tmp1);
- }
-
- dbg("%s: wr gpio=%02x %02x", __func__, tmp0, tmp1);
-
- /* write bits [7:2] */
- ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc);
- if (ret)
- goto error;
-
- /* write bits [5:0] */
- ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f);
- if (ret)
- goto error;
-
- memcpy(priv->gpio, gpio, sizeof(priv->gpio));
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */
-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor)
-{
- return div_u64(dividend + (divisor / 2), divisor);
-}
-
-static int cxd2820r_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (c->delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_init_t(fe);
- if (ret < 0)
- goto err;
- ret = cxd2820r_set_frontend_t(fe);
- if (ret < 0)
- goto err;
- break;
- case SYS_DVBT2:
- ret = cxd2820r_init_t(fe);
- if (ret < 0)
- goto err;
- ret = cxd2820r_set_frontend_t2(fe);
- if (ret < 0)
- goto err;
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_init_c(fe);
- if (ret < 0)
- goto err;
- ret = cxd2820r_set_frontend_c(fe);
- if (ret < 0)
- goto err;
- break;
- default:
- dbg("%s: error state=%d", __func__, fe->dtv_property_cache.delivery_system);
- ret = -EINVAL;
- break;
- }
-err:
- return ret;
-}
-static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_read_status_t(fe, status);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_read_status_t2(fe, status);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_read_status_c(fe, status);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_get_frontend(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
-
- if (priv->delivery_system == SYS_UNDEFINED)
- return 0;
-
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_get_frontend_t(fe);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_get_frontend_t2(fe);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_get_frontend_c(fe);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_read_ber_t(fe, ber);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_read_ber_t2(fe, ber);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_read_ber_c(fe, ber);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_read_signal_strength_t(fe, strength);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_read_signal_strength_t2(fe, strength);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_read_signal_strength_c(fe, strength);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_read_snr_t(fe, snr);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_read_snr_t2(fe, snr);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_read_snr_c(fe, snr);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_read_ucblocks_t(fe, ucblocks);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_read_ucblocks_t2(fe, ucblocks);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_read_ucblocks_c(fe, ucblocks);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_init(struct dvb_frontend *fe)
-{
- return 0;
-}
-
-static int cxd2820r_sleep(struct dvb_frontend *fe)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_sleep_t(fe);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_sleep_t2(fe);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_sleep_c(fe);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cxd2820r_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- int ret;
-
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
- switch (fe->dtv_property_cache.delivery_system) {
- case SYS_DVBT:
- ret = cxd2820r_get_tune_settings_t(fe, s);
- break;
- case SYS_DVBT2:
- ret = cxd2820r_get_tune_settings_t2(fe, s);
- break;
- case SYS_DVBC_ANNEX_A:
- ret = cxd2820r_get_tune_settings_c(fe, s);
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- fe_status_t status = 0;
- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system);
-
- /* switch between DVB-T and DVB-T2 when tune fails */
- if (priv->last_tune_failed) {
- if (priv->delivery_system == SYS_DVBT) {
- ret = cxd2820r_sleep_t(fe);
- if (ret)
- goto error;
-
- c->delivery_system = SYS_DVBT2;
- } else if (priv->delivery_system == SYS_DVBT2) {
- ret = cxd2820r_sleep_t2(fe);
- if (ret)
- goto error;
-
- c->delivery_system = SYS_DVBT;
- }
- }
-
- /* set frontend */
- ret = cxd2820r_set_frontend(fe);
- if (ret)
- goto error;
-
-
- /* frontend lock wait loop count */
- switch (priv->delivery_system) {
- case SYS_DVBT:
- case SYS_DVBC_ANNEX_A:
- i = 20;
- break;
- case SYS_DVBT2:
- i = 40;
- break;
- case SYS_UNDEFINED:
- default:
- i = 0;
- break;
- }
-
- /* wait frontend lock */
- for (; i > 0; i--) {
- dbg("%s: LOOP=%d", __func__, i);
- msleep(50);
- ret = cxd2820r_read_status(fe, &status);
- if (ret)
- goto error;
-
- if (status & FE_HAS_LOCK)
- break;
- }
-
- /* check if we have a valid signal */
- if (status & FE_HAS_LOCK) {
- priv->last_tune_failed = 0;
- return DVBFE_ALGO_SEARCH_SUCCESS;
- } else {
- priv->last_tune_failed = 1;
- return DVBFE_ALGO_SEARCH_AGAIN;
- }
-
-error:
- dbg("%s: failed:%d", __func__, ret);
- return DVBFE_ALGO_SEARCH_ERROR;
-}
-
-static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_CUSTOM;
-}
-
-static void cxd2820r_release(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- dbg("%s", __func__);
-
- kfree(priv);
- return;
-}
-
-static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- dbg("%s: %d", __func__, enable);
-
- /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */
- return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1);
-}
-
-static const struct dvb_frontend_ops cxd2820r_ops = {
- .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A },
- /* default: DVB-T/T2 */
- .info = {
- .name = "Sony CXD2820R",
-
- .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_32 |
- FE_CAN_QAM_64 |
- FE_CAN_QAM_128 |
- FE_CAN_QAM_256 |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_MUTE_TS |
- FE_CAN_2G_MODULATION
- },
-
- .release = cxd2820r_release,
- .init = cxd2820r_init,
- .sleep = cxd2820r_sleep,
-
- .get_tune_settings = cxd2820r_get_tune_settings,
- .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl,
-
- .get_frontend = cxd2820r_get_frontend,
-
- .get_frontend_algo = cxd2820r_get_frontend_algo,
- .search = cxd2820r_search,
-
- .read_status = cxd2820r_read_status,
- .read_snr = cxd2820r_read_snr,
- .read_ber = cxd2820r_read_ber,
- .read_ucblocks = cxd2820r_read_ucblocks,
- .read_signal_strength = cxd2820r_read_signal_strength,
-};
-
-struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg,
- struct i2c_adapter *i2c)
-{
- struct cxd2820r_priv *priv = NULL;
- int ret;
- u8 tmp;
-
- priv = kzalloc(sizeof (struct cxd2820r_priv), GFP_KERNEL);
- if (!priv)
- goto error;
-
- priv->i2c = i2c;
- memcpy(&priv->cfg, cfg, sizeof (struct cxd2820r_config));
-
- priv->bank[0] = priv->bank[1] = 0xff;
- ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp);
- dbg("%s: chip id=%02x", __func__, tmp);
- if (ret || tmp != 0xe1)
- goto error;
-
- memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof (struct dvb_frontend_ops));
- priv->fe.demodulator_priv = priv;
- return &priv->fe;
-error:
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(cxd2820r_attach);
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Sony CXD2820R demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h
deleted file mode 100644
index 9a9822cad9cd..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r_priv.h
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#ifndef CXD2820R_PRIV_H
-#define CXD2820R_PRIV_H
-
-#include <linux/dvb/version.h>
-#include "dvb_frontend.h"
-#include "dvb_math.h"
-#include "cxd2820r.h"
-
-#define LOG_PREFIX "cxd2820r"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (cxd2820r_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct reg_val_mask {
- u32 reg;
- u8 val;
- u8 mask;
-};
-
-struct cxd2820r_priv {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct cxd2820r_config cfg;
-
- bool ber_running;
-
- u8 bank[2];
- u8 gpio[3];
-
- fe_delivery_system_t delivery_system;
- bool last_tune_failed; /* for switch between T and T2 tune */
-};
-
-/* cxd2820r_core.c */
-
-extern int cxd2820r_debug;
-
-int cxd2820r_gpio(struct dvb_frontend *fe);
-
-int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val,
- u8 mask);
-
-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- int len);
-
-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor);
-
-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- int len);
-
-int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val,
- int len);
-
-int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val);
-
-int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val);
-
-/* cxd2820r_c.c */
-
-int cxd2820r_get_frontend_c(struct dvb_frontend *fe);
-
-int cxd2820r_set_frontend_c(struct dvb_frontend *fe);
-
-int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status);
-
-int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber);
-
-int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength);
-
-int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr);
-
-int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks);
-
-int cxd2820r_init_c(struct dvb_frontend *fe);
-
-int cxd2820r_sleep_c(struct dvb_frontend *fe);
-
-int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s);
-
-/* cxd2820r_t.c */
-
-int cxd2820r_get_frontend_t(struct dvb_frontend *fe);
-
-int cxd2820r_set_frontend_t(struct dvb_frontend *fe);
-
-int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status);
-
-int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber);
-
-int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength);
-
-int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr);
-
-int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks);
-
-int cxd2820r_init_t(struct dvb_frontend *fe);
-
-int cxd2820r_sleep_t(struct dvb_frontend *fe);
-
-int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s);
-
-/* cxd2820r_t2.c */
-
-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe);
-
-int cxd2820r_set_frontend_t2(struct dvb_frontend *fe);
-
-int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status);
-
-int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber);
-
-int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength);
-
-int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr);
-
-int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks);
-
-int cxd2820r_init_t2(struct dvb_frontend *fe);
-
-int cxd2820r_sleep_t2(struct dvb_frontend *fe);
-
-int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s);
-
-#endif /* CXD2820R_PRIV_H */
diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c
deleted file mode 100644
index 1a026239cdcc..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r_t.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#include "cxd2820r_priv.h"
-
-int cxd2820r_set_frontend_t(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, bw_i;
- u32 if_freq, if_ctl;
- u64 num;
- u8 buf[3], bw_param;
- u8 bw_params1[][5] = {
- { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
- { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
- { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
- };
- u8 bw_params2[][2] = {
- { 0x1f, 0xdc }, /* 6 MHz */
- { 0x12, 0xf8 }, /* 7 MHz */
- { 0x01, 0xe0 }, /* 8 MHz */
- };
- struct reg_val_mask tab[] = {
- { 0x00080, 0x00, 0xff },
- { 0x00081, 0x03, 0xff },
- { 0x00085, 0x07, 0xff },
- { 0x00088, 0x01, 0xff },
-
- { 0x00070, priv->cfg.ts_mode, 0xff },
- { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 },
- { 0x000a5, 0x00, 0x01 },
- { 0x00082, 0x20, 0x60 },
- { 0x000c2, 0xc3, 0xff },
- { 0x0016a, 0x50, 0xff },
- { 0x00427, 0x41, 0xff },
- };
-
- dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz);
-
- switch (c->bandwidth_hz) {
- case 6000000:
- bw_i = 0;
- bw_param = 2;
- break;
- case 7000000:
- bw_i = 1;
- bw_param = 1;
- break;
- case 8000000:
- bw_i = 2;
- bw_param = 0;
- break;
- default:
- return -EINVAL;
- }
-
- /* update GPIOs */
- ret = cxd2820r_gpio(fe);
- if (ret)
- goto error;
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- if (priv->delivery_system != SYS_DVBT) {
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- tab[i].val, tab[i].mask);
- if (ret)
- goto error;
- }
- }
-
- priv->delivery_system = SYS_DVBT;
- priv->ber_running = 0; /* tune stops BER counter */
-
- /* program IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency) {
- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
- if (ret)
- goto error;
- } else
- if_freq = 0;
-
- dbg("%s: if_freq=%d", __func__, if_freq);
-
- num = if_freq / 1000; /* Hz => kHz */
- num *= 0x1000000;
- if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- buf[0] = ((if_ctl >> 16) & 0xff);
- buf[1] = ((if_ctl >> 8) & 0xff);
- buf[2] = ((if_ctl >> 0) & 0xff);
-
- ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[bw_i], 5);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[bw_i], 2);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- if (ret)
- goto error;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_get_frontend_t(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- u8 buf[2];
-
- ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf));
- if (ret)
- goto error;
-
- switch ((buf[0] >> 6) & 0x03) {
- case 0:
- c->modulation = QPSK;
- break;
- case 1:
- c->modulation = QAM_16;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- }
-
- switch ((buf[1] >> 1) & 0x03) {
- case 0:
- c->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case 1:
- c->transmission_mode = TRANSMISSION_MODE_8K;
- break;
- }
-
- switch ((buf[1] >> 3) & 0x03) {
- case 0:
- c->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- c->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- c->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- c->guard_interval = GUARD_INTERVAL_1_4;
- break;
- }
-
- switch ((buf[0] >> 3) & 0x07) {
- case 0:
- c->hierarchy = HIERARCHY_NONE;
- break;
- case 1:
- c->hierarchy = HIERARCHY_1;
- break;
- case 2:
- c->hierarchy = HIERARCHY_2;
- break;
- case 3:
- c->hierarchy = HIERARCHY_4;
- break;
- }
-
- switch ((buf[0] >> 0) & 0x07) {
- case 0:
- c->code_rate_HP = FEC_1_2;
- break;
- case 1:
- c->code_rate_HP = FEC_2_3;
- break;
- case 2:
- c->code_rate_HP = FEC_3_4;
- break;
- case 3:
- c->code_rate_HP = FEC_5_6;
- break;
- case 4:
- c->code_rate_HP = FEC_7_8;
- break;
- }
-
- switch ((buf[1] >> 5) & 0x07) {
- case 0:
- c->code_rate_LP = FEC_1_2;
- break;
- case 1:
- c->code_rate_LP = FEC_2_3;
- break;
- case 2:
- c->code_rate_LP = FEC_3_4;
- break;
- case 3:
- c->code_rate_LP = FEC_5_6;
- break;
- case 4:
- c->code_rate_LP = FEC_7_8;
- break;
- }
-
- ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]);
- if (ret)
- goto error;
-
- switch ((buf[0] >> 0) & 0x01) {
- case 0:
- c->inversion = INVERSION_OFF;
- break;
- case 1:
- c->inversion = INVERSION_ON;
- break;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[3], start_ber = 0;
- *ber = 0;
-
- if (priv->ber_running) {
- ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf));
- if (ret)
- goto error;
-
- if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) {
- *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0];
- start_ber = 1;
- }
- } else {
- priv->ber_running = 1;
- start_ber = 1;
- }
-
- if (start_ber) {
- /* (re)start BER */
- ret = cxd2820r_wr_reg(priv, 0x00079, 0x01);
- if (ret)
- goto error;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 tmp;
-
- ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf));
- if (ret)
- goto error;
-
- tmp = (buf[0] & 0x0f) << 8 | buf[1];
- tmp = ~tmp & 0x0fff;
-
- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
- *strength = tmp * 0xffff / 0x0fff;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 tmp;
- /* report SNR in dB * 10 */
-
- ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf));
- if (ret)
- goto error;
-
- tmp = (buf[0] & 0x1f) << 8 | buf[1];
- #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
- if (tmp)
- *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
- / 100);
- else
- *snr = 0;
-
- dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- /* no way to read ? */
- return 0;
-}
-
-int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[4];
- *status = 0;
-
- ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]);
- if (ret)
- goto error;
-
- if ((buf[0] & 0x07) == 6) {
- ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]);
- if (ret)
- goto error;
-
- if (((buf[1] >> 3) & 0x01) == 1) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- } else {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC;
- }
- } else {
- ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]);
- if (ret)
- goto error;
-
- if ((buf[2] & 0x0f) >= 4) {
- ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]);
- if (ret)
- goto error;
-
- if (((buf[3] >> 4) & 0x01) == 1)
- *status |= FE_HAS_SIGNAL;
- }
- }
-
- dbg("%s: lock=%02x %02x %02x %02x", __func__,
- buf[0], buf[1], buf[2], buf[3]);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_init_t(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
-
- ret = cxd2820r_wr_reg(priv, 0x00085, 0x07);
- if (ret)
- goto error;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_sleep_t(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret, i;
- struct reg_val_mask tab[] = {
- { 0x000ff, 0x1f, 0xff },
- { 0x00085, 0x00, 0xff },
- { 0x00088, 0x01, 0xff },
- { 0x00081, 0x00, 0xff },
- { 0x00080, 0x00, 0xff },
- };
-
- dbg("%s", __func__);
-
- priv->delivery_system = SYS_UNDEFINED;
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret)
- goto error;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 500;
- s->step_size = fe->ops.info.frequency_stepsize * 2;
- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
-
- return 0;
-}
diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c
deleted file mode 100644
index 3a5759e0d235..000000000000
--- a/drivers/media/dvb/frontends/cxd2820r_t2.c
+++ /dev/null
@@ -1,426 +0,0 @@
-/*
- * Sony CXD2820R demodulator driver
- *
- * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-
-#include "cxd2820r_priv.h"
-
-int cxd2820r_set_frontend_t2(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, bw_i;
- u32 if_freq, if_ctl;
- u64 num;
- u8 buf[3], bw_param;
- u8 bw_params1[][5] = {
- { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */
- { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */
- { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */
- { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */
- };
- struct reg_val_mask tab[] = {
- { 0x00080, 0x02, 0xff },
- { 0x00081, 0x20, 0xff },
- { 0x00085, 0x07, 0xff },
- { 0x00088, 0x01, 0xff },
- { 0x02069, 0x01, 0xff },
-
- { 0x0207f, 0x2a, 0xff },
- { 0x02082, 0x0a, 0xff },
- { 0x02083, 0x0a, 0xff },
- { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 },
- { 0x02070, priv->cfg.ts_mode, 0xff },
- { 0x020b5, priv->cfg.spec_inv << 4, 0x10 },
- { 0x02567, 0x07, 0x0f },
- { 0x02569, 0x03, 0x03 },
- { 0x02595, 0x1a, 0xff },
- { 0x02596, 0x50, 0xff },
- { 0x02a8c, 0x00, 0xff },
- { 0x02a8d, 0x34, 0xff },
- { 0x02a45, 0x06, 0x07 },
- { 0x03f10, 0x0d, 0xff },
- { 0x03f11, 0x02, 0xff },
- { 0x03f12, 0x01, 0xff },
- { 0x03f23, 0x2c, 0xff },
- { 0x03f51, 0x13, 0xff },
- { 0x03f52, 0x01, 0xff },
- { 0x03f53, 0x00, 0xff },
- { 0x027e6, 0x14, 0xff },
- { 0x02786, 0x02, 0x07 },
- { 0x02787, 0x40, 0xe0 },
- { 0x027ef, 0x10, 0x18 },
- };
-
- dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz);
-
- switch (c->bandwidth_hz) {
- case 5000000:
- bw_i = 0;
- bw_param = 3;
- break;
- case 6000000:
- bw_i = 1;
- bw_param = 2;
- break;
- case 7000000:
- bw_i = 2;
- bw_param = 1;
- break;
- case 8000000:
- bw_i = 3;
- bw_param = 0;
- break;
- default:
- return -EINVAL;
- }
-
- /* update GPIOs */
- ret = cxd2820r_gpio(fe);
- if (ret)
- goto error;
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- if (priv->delivery_system != SYS_DVBT2) {
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg,
- tab[i].val, tab[i].mask);
- if (ret)
- goto error;
- }
- }
-
- priv->delivery_system = SYS_DVBT2;
-
- /* program IF frequency */
- if (fe->ops.tuner_ops.get_if_frequency) {
- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
- if (ret)
- goto error;
- } else
- if_freq = 0;
-
- dbg("%s: if_freq=%d", __func__, if_freq);
-
- num = if_freq / 1000; /* Hz => kHz */
- num *= 0x1000000;
- if_ctl = cxd2820r_div_u64_round_closest(num, 41000);
- buf[0] = ((if_ctl >> 16) & 0xff);
- buf[1] = ((if_ctl >> 8) & 0xff);
- buf[2] = ((if_ctl >> 0) & 0xff);
-
- ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[bw_i], 5);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08);
- if (ret)
- goto error;
-
- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01);
- if (ret)
- goto error;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-
-}
-
-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- u8 buf[2];
-
- ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2);
- if (ret)
- goto error;
-
- switch ((buf[0] >> 0) & 0x07) {
- case 0:
- c->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case 1:
- c->transmission_mode = TRANSMISSION_MODE_8K;
- break;
- case 2:
- c->transmission_mode = TRANSMISSION_MODE_4K;
- break;
- case 3:
- c->transmission_mode = TRANSMISSION_MODE_1K;
- break;
- case 4:
- c->transmission_mode = TRANSMISSION_MODE_16K;
- break;
- case 5:
- c->transmission_mode = TRANSMISSION_MODE_32K;
- break;
- }
-
- switch ((buf[1] >> 4) & 0x07) {
- case 0:
- c->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- c->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- c->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- c->guard_interval = GUARD_INTERVAL_1_4;
- break;
- case 4:
- c->guard_interval = GUARD_INTERVAL_1_128;
- break;
- case 5:
- c->guard_interval = GUARD_INTERVAL_19_128;
- break;
- case 6:
- c->guard_interval = GUARD_INTERVAL_19_256;
- break;
- }
-
- ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2);
- if (ret)
- goto error;
-
- switch ((buf[0] >> 0) & 0x07) {
- case 0:
- c->fec_inner = FEC_1_2;
- break;
- case 1:
- c->fec_inner = FEC_3_5;
- break;
- case 2:
- c->fec_inner = FEC_2_3;
- break;
- case 3:
- c->fec_inner = FEC_3_4;
- break;
- case 4:
- c->fec_inner = FEC_4_5;
- break;
- case 5:
- c->fec_inner = FEC_5_6;
- break;
- }
-
- switch ((buf[1] >> 0) & 0x07) {
- case 0:
- c->modulation = QPSK;
- break;
- case 1:
- c->modulation = QAM_16;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- case 3:
- c->modulation = QAM_256;
- break;
- }
-
- ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]);
- if (ret)
- goto error;
-
- switch ((buf[0] >> 4) & 0x01) {
- case 0:
- c->inversion = INVERSION_OFF;
- break;
- case 1:
- c->inversion = INVERSION_ON;
- break;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[1];
- *status = 0;
-
- ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]);
- if (ret)
- goto error;
-
- if ((buf[0] & 0x07) == 6) {
- if (((buf[0] >> 5) & 0x01) == 1) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- } else {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC;
- }
- }
-
- dbg("%s: lock=%02x", __func__, buf[0]);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[4];
- unsigned int errbits;
- *ber = 0;
- /* FIXME: correct calculation */
-
- ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf));
- if (ret)
- goto error;
-
- if ((buf[0] >> 4) & 0x01) {
- errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 |
- buf[2] << 8 | buf[3];
-
- if (errbits)
- *ber = errbits * 64 / 16588800;
- }
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 tmp;
-
- ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf));
- if (ret)
- goto error;
-
- tmp = (buf[0] & 0x0f) << 8 | buf[1];
- tmp = ~tmp & 0x0fff;
-
- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
- *strength = tmp * 0xffff / 0x0fff;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 tmp;
- /* report SNR in dB * 10 */
-
- ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf));
- if (ret)
- goto error;
-
- tmp = (buf[0] & 0x0f) << 8 | buf[1];
- #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */
- if (tmp)
- *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24)
- / 100);
- else
- *snr = 0;
-
- dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp);
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- /* no way to read ? */
- return 0;
-}
-
-int cxd2820r_sleep_t2(struct dvb_frontend *fe)
-{
- struct cxd2820r_priv *priv = fe->demodulator_priv;
- int ret, i;
- struct reg_val_mask tab[] = {
- { 0x000ff, 0x1f, 0xff },
- { 0x00085, 0x00, 0xff },
- { 0x00088, 0x01, 0xff },
- { 0x02069, 0x00, 0xff },
- { 0x00081, 0x00, 0xff },
- { 0x00080, 0x00, 0xff },
- };
-
- dbg("%s", __func__);
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret)
- goto error;
- }
-
- priv->delivery_system = SYS_UNDEFINED;
-
- return ret;
-error:
- dbg("%s: failed:%d", __func__, ret);
- return ret;
-}
-
-int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 1500;
- s->step_size = fe->ops.info.frequency_stepsize * 2;
- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
-
- return 0;
-}
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
deleted file mode 100644
index d615d7d055a2..000000000000
--- a/drivers/media/dvb/frontends/drxk.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef _DRXK_H_
-#define _DRXK_H_
-
-#include <linux/types.h>
-#include <linux/i2c.h>
-
-/**
- * struct drxk_config - Configure the initial parameters for DRX-K
- *
- * @adr: I2C Address of the DRX-K
- * @parallel_ts: True means that the device uses parallel TS,
- * Serial otherwise.
- * @dynamic_clk: True means that the clock will be dynamically
- * adjusted. Static clock otherwise.
- * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
- * @single_master: Device is on the single master mode
- * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
- * @antenna_gpio: GPIO bit used to control the antenna
- * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
- * means that 1=DVBC, 0 = DVBT. Zero means the opposite.
- * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
- * @microcode_name: Name of the firmware file with the microcode
- * @qam_demod_parameter_count: The number of parameters used for the command
- * to set the demodulator parameters. All
- * firmwares are using the 2-parameter commmand.
- * An exception is the "drxk_a3.mc" firmware,
- * which uses the 4-parameter command.
- * A value of 0 (default) or lower indicates that
- * the correct number of parameters will be
- * automatically detected.
- *
- * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
- * UIO-3.
- */
-struct drxk_config {
- u8 adr;
- bool single_master;
- bool no_i2c_bridge;
- bool parallel_ts;
- bool dynamic_clk;
- bool enable_merr_cfg;
-
- bool antenna_dvbt;
- u16 antenna_gpio;
-
- u8 mpeg_out_clk_strength;
- int chunk_size;
-
- const char *microcode_name;
- int qam_demod_parameter_count;
-};
-
-#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \
- && defined(MODULE))
-extern struct dvb_frontend *drxk_attach(const struct drxk_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *drxk_attach(const struct drxk_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/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
deleted file mode 100644
index 1ab8154542da..000000000000
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ /dev/null
@@ -1,6637 +0,0 @@
-/*
- * drxk_hard: DRX-K DVB-C/T demodulator driver
- *
- * Copyright (C) 2010-2011 Digital Devices GmbH
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/firmware.h>
-#include <linux/i2c.h>
-#include <linux/hardirq.h>
-#include <asm/div64.h>
-
-#include "dvb_frontend.h"
-#include "drxk.h"
-#include "drxk_hard.h"
-
-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode);
-static int PowerDownQAM(struct drxk_state *state);
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int SetQAMStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset);
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode);
-static int DVBTStart(struct drxk_state *state);
-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset);
-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus);
-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus);
-static int SwitchAntennaToQAM(struct drxk_state *state);
-static int SwitchAntennaToDVBT(struct drxk_state *state);
-
-static bool IsDVBT(struct drxk_state *state)
-{
- return state->m_OperationMode == OM_DVBT;
-}
-
-static bool IsQAM(struct drxk_state *state)
-{
- return state->m_OperationMode == OM_QAM_ITU_A ||
- state->m_OperationMode == OM_QAM_ITU_B ||
- state->m_OperationMode == OM_QAM_ITU_C;
-}
-
-bool IsA1WithPatchCode(struct drxk_state *state)
-{
- return state->m_DRXK_A1_PATCH_CODE;
-}
-
-bool IsA1WithRomCode(struct drxk_state *state)
-{
- return state->m_DRXK_A1_ROM_CODE;
-}
-
-#define NOA1ROM 0
-
-#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0)
-#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0)
-
-#define DEFAULT_MER_83 165
-#define DEFAULT_MER_93 250
-
-#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH
-#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02)
-#endif
-
-#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH
-#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
-#endif
-
-#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
-#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
-
-#ifndef DRXK_KI_RAGC_ATV
-#define DRXK_KI_RAGC_ATV 4
-#endif
-#ifndef DRXK_KI_IAGC_ATV
-#define DRXK_KI_IAGC_ATV 6
-#endif
-#ifndef DRXK_KI_DAGC_ATV
-#define DRXK_KI_DAGC_ATV 7
-#endif
-
-#ifndef DRXK_KI_RAGC_QAM
-#define DRXK_KI_RAGC_QAM 3
-#endif
-#ifndef DRXK_KI_IAGC_QAM
-#define DRXK_KI_IAGC_QAM 4
-#endif
-#ifndef DRXK_KI_DAGC_QAM
-#define DRXK_KI_DAGC_QAM 7
-#endif
-#ifndef DRXK_KI_RAGC_DVBT
-#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2)
-#endif
-#ifndef DRXK_KI_IAGC_DVBT
-#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2)
-#endif
-#ifndef DRXK_KI_DAGC_DVBT
-#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7)
-#endif
-
-#ifndef DRXK_AGC_DAC_OFFSET
-#define DRXK_AGC_DAC_OFFSET (0x800)
-#endif
-
-#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ
-#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L)
-#endif
-
-#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ
-#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L)
-#endif
-
-#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ
-#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L)
-#endif
-
-#ifndef DRXK_QAM_SYMBOLRATE_MAX
-#define DRXK_QAM_SYMBOLRATE_MAX (7233000)
-#endif
-
-#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56
-#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64
-#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0
-#define DRXK_BL_ROM_OFFSET_TAPS_BG 24
-#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32
-#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40
-#define DRXK_BL_ROM_OFFSET_TAPS_FM 48
-#define DRXK_BL_ROM_OFFSET_UCODE 0
-
-#define DRXK_BLC_TIMEOUT 100
-
-#define DRXK_BLCC_NR_ELEMENTS_TAPS 2
-#define DRXK_BLCC_NR_ELEMENTS_UCODE 6
-
-#define DRXK_BLDC_NR_ELEMENTS_TAPS 28
-
-#ifndef DRXK_OFDM_NE_NOTCH_WIDTH
-#define DRXK_OFDM_NE_NOTCH_WIDTH (4)
-#endif
-
-#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960)
-#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480)
-#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008)
-#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992)
-#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520)
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable debug messages");
-
-#define dprintk(level, fmt, arg...) do { \
-if (debug >= level) \
- printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \
-} while (0)
-
-
-static inline u32 MulDiv32(u32 a, u32 b, u32 c)
-{
- u64 tmp64;
-
- tmp64 = (u64) a * (u64) b;
- do_div(tmp64, c);
-
- return (u32) tmp64;
-}
-
-inline u32 Frac28a(u32 a, u32 c)
-{
- int i = 0;
- u32 Q1 = 0;
- u32 R0 = 0;
-
- R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */
- Q1 = a / c; /* integer part, only the 4 least significant bits
- will be visible in the result */
-
- /* division using radix 16, 7 nibbles in the result */
- for (i = 0; i < 7; i++) {
- Q1 = (Q1 << 4) | (R0 / c);
- R0 = (R0 % c) << 4;
- }
- /* rounding */
- if ((R0 >> 3) >= c)
- Q1++;
-
- return Q1;
-}
-
-static u32 Log10Times100(u32 x)
-{
- static const u8 scale = 15;
- static const u8 indexWidth = 5;
- u8 i = 0;
- u32 y = 0;
- u32 d = 0;
- u32 k = 0;
- u32 r = 0;
- /*
- log2lut[n] = (1<<scale) * 200 * log2(1.0 + ((1.0/(1<<INDEXWIDTH)) * n))
- 0 <= n < ((1<<INDEXWIDTH)+1)
- */
-
- static const u32 log2lut[] = {
- 0, /* 0.000000 */
- 290941, /* 290941.300628 */
- 573196, /* 573196.476418 */
- 847269, /* 847269.179851 */
- 1113620, /* 1113620.489452 */
- 1372674, /* 1372673.576986 */
- 1624818, /* 1624817.752104 */
- 1870412, /* 1870411.981536 */
- 2109788, /* 2109787.962654 */
- 2343253, /* 2343252.817465 */
- 2571091, /* 2571091.461923 */
- 2793569, /* 2793568.696416 */
- 3010931, /* 3010931.055901 */
- 3223408, /* 3223408.452106 */
- 3431216, /* 3431215.635215 */
- 3634553, /* 3634553.498355 */
- 3833610, /* 3833610.244726 */
- 4028562, /* 4028562.434393 */
- 4219576, /* 4219575.925308 */
- 4406807, /* 4406806.721144 */
- 4590402, /* 4590401.736809 */
- 4770499, /* 4770499.491025 */
- 4947231, /* 4947230.734179 */
- 5120719, /* 5120719.018555 */
- 5291081, /* 5291081.217197 */
- 5458428, /* 5458427.996830 */
- 5622864, /* 5622864.249668 */
- 5784489, /* 5784489.488298 */
- 5943398, /* 5943398.207380 */
- 6099680, /* 6099680.215452 */
- 6253421, /* 6253420.939751 */
- 6404702, /* 6404701.706649 */
- 6553600, /* 6553600.000000 */
- };
-
-
- if (x == 0)
- return 0;
-
- /* Scale x (normalize) */
- /* computing y in log(x/y) = log(x) - log(y) */
- if ((x & ((0xffffffff) << (scale + 1))) == 0) {
- for (k = scale; k > 0; k--) {
- if (x & (((u32) 1) << scale))
- break;
- x <<= 1;
- }
- } else {
- for (k = scale; k < 31; k++) {
- if ((x & (((u32) (-1)) << (scale + 1))) == 0)
- break;
- x >>= 1;
- }
- }
- /*
- Now x has binary point between bit[scale] and bit[scale-1]
- and 1.0 <= x < 2.0 */
-
- /* correction for divison: log(x) = log(x/y)+log(y) */
- y = k * ((((u32) 1) << scale) * 200);
-
- /* remove integer part */
- x &= ((((u32) 1) << scale) - 1);
- /* get index */
- i = (u8) (x >> (scale - indexWidth));
- /* compute delta (x - a) */
- d = x & ((((u32) 1) << (scale - indexWidth)) - 1);
- /* compute log, multiplication (d* (..)) must be within range ! */
- y += log2lut[i] +
- ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth));
- /* Conver to log10() */
- y /= 108853; /* (log2(10) << scale) */
- r = (y >> 1);
- /* rounding */
- if (y & ((u32) 1))
- r++;
- return r;
-}
-
-/****************************************************************************/
-/* I2C **********************************************************************/
-/****************************************************************************/
-
-static int drxk_i2c_lock(struct drxk_state *state)
-{
- i2c_lock_adapter(state->i2c);
- state->drxk_i2c_exclusive_lock = true;
-
- return 0;
-}
-
-static void drxk_i2c_unlock(struct drxk_state *state)
-{
- if (!state->drxk_i2c_exclusive_lock)
- return;
-
- i2c_unlock_adapter(state->i2c);
- state->drxk_i2c_exclusive_lock = false;
-}
-
-static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs,
- unsigned len)
-{
- if (state->drxk_i2c_exclusive_lock)
- return __i2c_transfer(state->i2c, msgs, len);
- else
- return i2c_transfer(state->i2c, msgs, len);
-}
-
-static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val)
-{
- struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1}
- };
-
- return drxk_i2c_transfer(state, msgs, 1);
-}
-
-static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len)
-{
- int status;
- struct i2c_msg msg = {
- .addr = adr, .flags = 0, .buf = data, .len = len };
-
- dprintk(3, ":");
- if (debug > 2) {
- int i;
- for (i = 0; i < len; i++)
- printk(KERN_CONT " %02x", data[i]);
- printk(KERN_CONT "\n");
- }
- status = drxk_i2c_transfer(state, &msg, 1);
- if (status >= 0 && status != 1)
- status = -EIO;
-
- if (status < 0)
- printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr);
-
- return status;
-}
-
-static int i2c_read(struct drxk_state *state,
- u8 adr, u8 *msg, int len, u8 *answ, int alen)
-{
- int status;
- struct i2c_msg msgs[2] = {
- {.addr = adr, .flags = 0,
- .buf = msg, .len = len},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = answ, .len = alen}
- };
-
- status = drxk_i2c_transfer(state, msgs, 2);
- if (status != 2) {
- if (debug > 2)
- printk(KERN_CONT ": ERROR!\n");
- if (status >= 0)
- status = -EIO;
-
- printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr);
- return status;
- }
- if (debug > 2) {
- int i;
- dprintk(2, ": read from");
- for (i = 0; i < len; i++)
- printk(KERN_CONT " %02x", msg[i]);
- printk(KERN_CONT ", value = ");
- for (i = 0; i < alen; i++)
- printk(KERN_CONT " %02x", answ[i]);
- printk(KERN_CONT "\n");
- }
- return 0;
-}
-
-static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags)
-{
- int status;
- u8 adr = state->demod_address, mm1[4], mm2[2], len;
-
- if (state->single_master)
- flags |= 0xC0;
-
- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
- mm1[0] = (((reg << 1) & 0xFF) | 0x01);
- mm1[1] = ((reg >> 16) & 0xFF);
- mm1[2] = ((reg >> 24) & 0xFF) | flags;
- mm1[3] = ((reg >> 7) & 0xFF);
- len = 4;
- } else {
- mm1[0] = ((reg << 1) & 0xFF);
- mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
- len = 2;
- }
- dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
- status = i2c_read(state, adr, mm1, len, mm2, 2);
- if (status < 0)
- return status;
- if (data)
- *data = mm2[0] | (mm2[1] << 8);
-
- return 0;
-}
-
-static int read16(struct drxk_state *state, u32 reg, u16 *data)
-{
- return read16_flags(state, reg, data, 0);
-}
-
-static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags)
-{
- int status;
- u8 adr = state->demod_address, mm1[4], mm2[4], len;
-
- if (state->single_master)
- flags |= 0xC0;
-
- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
- mm1[0] = (((reg << 1) & 0xFF) | 0x01);
- mm1[1] = ((reg >> 16) & 0xFF);
- mm1[2] = ((reg >> 24) & 0xFF) | flags;
- mm1[3] = ((reg >> 7) & 0xFF);
- len = 4;
- } else {
- mm1[0] = ((reg << 1) & 0xFF);
- mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
- len = 2;
- }
- dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags);
- status = i2c_read(state, adr, mm1, len, mm2, 4);
- if (status < 0)
- return status;
- if (data)
- *data = mm2[0] | (mm2[1] << 8) |
- (mm2[2] << 16) | (mm2[3] << 24);
-
- return 0;
-}
-
-static int read32(struct drxk_state *state, u32 reg, u32 *data)
-{
- return read32_flags(state, reg, data, 0);
-}
-
-static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags)
-{
- u8 adr = state->demod_address, mm[6], len;
-
- if (state->single_master)
- flags |= 0xC0;
- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
- mm[0] = (((reg << 1) & 0xFF) | 0x01);
- mm[1] = ((reg >> 16) & 0xFF);
- mm[2] = ((reg >> 24) & 0xFF) | flags;
- mm[3] = ((reg >> 7) & 0xFF);
- len = 4;
- } else {
- mm[0] = ((reg << 1) & 0xFF);
- mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
- len = 2;
- }
- mm[len] = data & 0xff;
- mm[len + 1] = (data >> 8) & 0xff;
-
- dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags);
- return i2c_write(state, adr, mm, len + 2);
-}
-
-static int write16(struct drxk_state *state, u32 reg, u16 data)
-{
- return write16_flags(state, reg, data, 0);
-}
-
-static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags)
-{
- u8 adr = state->demod_address, mm[8], len;
-
- if (state->single_master)
- flags |= 0xC0;
- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) {
- mm[0] = (((reg << 1) & 0xFF) | 0x01);
- mm[1] = ((reg >> 16) & 0xFF);
- mm[2] = ((reg >> 24) & 0xFF) | flags;
- mm[3] = ((reg >> 7) & 0xFF);
- len = 4;
- } else {
- mm[0] = ((reg << 1) & 0xFF);
- mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0));
- len = 2;
- }
- mm[len] = data & 0xff;
- mm[len + 1] = (data >> 8) & 0xff;
- mm[len + 2] = (data >> 16) & 0xff;
- mm[len + 3] = (data >> 24) & 0xff;
- dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags);
-
- return i2c_write(state, adr, mm, len + 4);
-}
-
-static int write32(struct drxk_state *state, u32 reg, u32 data)
-{
- return write32_flags(state, reg, data, 0);
-}
-
-static int write_block(struct drxk_state *state, u32 Address,
- const int BlockSize, const u8 pBlock[])
-{
- int status = 0, BlkSize = BlockSize;
- u8 Flags = 0;
-
- if (state->single_master)
- Flags |= 0xC0;
-
- while (BlkSize > 0) {
- int Chunk = BlkSize > state->m_ChunkSize ?
- state->m_ChunkSize : BlkSize;
- u8 *AdrBuf = &state->Chunk[0];
- u32 AdrLength = 0;
-
- if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) {
- AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01);
- AdrBuf[1] = ((Address >> 16) & 0xFF);
- AdrBuf[2] = ((Address >> 24) & 0xFF);
- AdrBuf[3] = ((Address >> 7) & 0xFF);
- AdrBuf[2] |= Flags;
- AdrLength = 4;
- if (Chunk == state->m_ChunkSize)
- Chunk -= 2;
- } else {
- AdrBuf[0] = ((Address << 1) & 0xFF);
- AdrBuf[1] = (((Address >> 16) & 0x0F) |
- ((Address >> 18) & 0xF0));
- AdrLength = 2;
- }
- memcpy(&state->Chunk[AdrLength], pBlock, Chunk);
- dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags);
- if (debug > 1) {
- int i;
- if (pBlock)
- for (i = 0; i < Chunk; i++)
- printk(KERN_CONT " %02x", pBlock[i]);
- printk(KERN_CONT "\n");
- }
- status = i2c_write(state, state->demod_address,
- &state->Chunk[0], Chunk + AdrLength);
- if (status < 0) {
- printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n",
- __func__, Address);
- break;
- }
- pBlock += Chunk;
- Address += (Chunk >> 1);
- BlkSize -= Chunk;
- }
- return status;
-}
-
-#ifndef DRXK_MAX_RETRIES_POWERUP
-#define DRXK_MAX_RETRIES_POWERUP 20
-#endif
-
-int PowerUpDevice(struct drxk_state *state)
-{
- int status;
- u8 data = 0;
- u16 retryCount = 0;
-
- dprintk(1, "\n");
-
- status = i2c_read1(state, state->demod_address, &data);
- if (status < 0) {
- do {
- data = 0;
- status = i2c_write(state, state->demod_address,
- &data, 1);
- msleep(10);
- retryCount++;
- if (status < 0)
- continue;
- status = i2c_read1(state, state->demod_address,
- &data);
- } while (status < 0 &&
- (retryCount < DRXK_MAX_RETRIES_POWERUP));
- if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP)
- goto error;
- }
-
- /* Make sure all clk domains are active */
- status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE);
- if (status < 0)
- goto error;
- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
- if (status < 0)
- goto error;
- /* Enable pll lock tests */
- status = write16(state, SIO_CC_PLL_LOCK__A, 1);
- if (status < 0)
- goto error;
-
- state->m_currentPowerMode = DRX_POWER_UP;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-
-static int init_state(struct drxk_state *state)
-{
- /*
- * FIXME: most (all?) of the values bellow should be moved into
- * struct drxk_config, as they are probably board-specific
- */
- u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulVSBIfAgcOutputLevel = 0;
- u32 ulVSBIfAgcMinLevel = 0;
- u32 ulVSBIfAgcMaxLevel = 0x7FFF;
- u32 ulVSBIfAgcSpeed = 3;
-
- u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulVSBRfAgcOutputLevel = 0;
- u32 ulVSBRfAgcMinLevel = 0;
- u32 ulVSBRfAgcMaxLevel = 0x7FFF;
- u32 ulVSBRfAgcSpeed = 3;
- u32 ulVSBRfAgcTop = 9500;
- u32 ulVSBRfAgcCutOffCurrent = 4000;
-
- u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO;
- u32 ulATVIfAgcOutputLevel = 0;
- u32 ulATVIfAgcMinLevel = 0;
- u32 ulATVIfAgcMaxLevel = 0;
- u32 ulATVIfAgcSpeed = 3;
-
- u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF;
- u32 ulATVRfAgcOutputLevel = 0;
- u32 ulATVRfAgcMinLevel = 0;
- u32 ulATVRfAgcMaxLevel = 0;
- u32 ulATVRfAgcTop = 9500;
- u32 ulATVRfAgcCutOffCurrent = 4000;
- u32 ulATVRfAgcSpeed = 3;
-
- u32 ulQual83 = DEFAULT_MER_83;
- u32 ulQual93 = DEFAULT_MER_93;
-
- u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
- u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
-
- /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */
- /* io_pad_cfg_mode output mode is drive always */
- /* io_pad_cfg_drive is set to power 2 (23 mA) */
- u32 ulGPIOCfg = 0x0113;
- u32 ulInvertTSClock = 0;
- u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
- u32 ulDVBTBitrate = 50000000;
- u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
-
- u32 ulInsertRSByte = 0;
-
- u32 ulRfMirror = 1;
- u32 ulPowerDown = 0;
-
- dprintk(1, "\n");
-
- state->m_hasLNA = false;
- state->m_hasDVBT = false;
- state->m_hasDVBC = false;
- state->m_hasATV = false;
- state->m_hasOOB = false;
- state->m_hasAudio = false;
-
- if (!state->m_ChunkSize)
- state->m_ChunkSize = 124;
-
- state->m_oscClockFreq = 0;
- state->m_smartAntInverted = false;
- state->m_bPDownOpenBridge = false;
-
- /* real system clock frequency in kHz */
- state->m_sysClockFreq = 151875;
- /* Timing div, 250ns/Psys */
- /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */
- state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) *
- HI_I2C_DELAY) / 1000;
- /* Clipping */
- if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M)
- state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M;
- state->m_HICfgWakeUpKey = (state->demod_address << 1);
- /* port/bridge/power down ctrl */
- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
-
- state->m_bPowerDown = (ulPowerDown != 0);
-
- state->m_DRXK_A1_PATCH_CODE = false;
- state->m_DRXK_A1_ROM_CODE = false;
- state->m_DRXK_A2_ROM_CODE = false;
- state->m_DRXK_A3_ROM_CODE = false;
- state->m_DRXK_A2_PATCH_CODE = false;
- state->m_DRXK_A3_PATCH_CODE = false;
-
- /* Init AGC and PGA parameters */
- /* VSB IF */
- state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode);
- state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel);
- state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel);
- state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel);
- state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed);
- state->m_vsbPgaCfg = 140;
-
- /* VSB RF */
- state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode);
- state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel);
- state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel);
- state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel);
- state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed);
- state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop);
- state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent);
- state->m_vsbPreSawCfg.reference = 0x07;
- state->m_vsbPreSawCfg.usePreSaw = true;
-
- state->m_Quality83percent = DEFAULT_MER_83;
- state->m_Quality93percent = DEFAULT_MER_93;
- if (ulQual93 <= 500 && ulQual83 < ulQual93) {
- state->m_Quality83percent = ulQual83;
- state->m_Quality93percent = ulQual93;
- }
-
- /* ATV IF */
- state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode);
- state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel);
- state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel);
- state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel);
- state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed);
-
- /* ATV RF */
- state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode);
- state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel);
- state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel);
- state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel);
- state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed);
- state->m_atvRfAgcCfg.top = (ulATVRfAgcTop);
- state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent);
- state->m_atvPreSawCfg.reference = 0x04;
- state->m_atvPreSawCfg.usePreSaw = true;
-
-
- /* DVBT RF */
- state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
- state->m_dvbtRfAgcCfg.outputLevel = 0;
- state->m_dvbtRfAgcCfg.minOutputLevel = 0;
- state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF;
- state->m_dvbtRfAgcCfg.top = 0x2100;
- state->m_dvbtRfAgcCfg.cutOffCurrent = 4000;
- state->m_dvbtRfAgcCfg.speed = 1;
-
-
- /* DVBT IF */
- state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
- state->m_dvbtIfAgcCfg.outputLevel = 0;
- state->m_dvbtIfAgcCfg.minOutputLevel = 0;
- state->m_dvbtIfAgcCfg.maxOutputLevel = 9000;
- state->m_dvbtIfAgcCfg.top = 13424;
- state->m_dvbtIfAgcCfg.cutOffCurrent = 0;
- state->m_dvbtIfAgcCfg.speed = 3;
- state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30;
- state->m_dvbtIfAgcCfg.IngainTgtMax = 30000;
- /* state->m_dvbtPgaCfg = 140; */
-
- state->m_dvbtPreSawCfg.reference = 4;
- state->m_dvbtPreSawCfg.usePreSaw = false;
-
- /* QAM RF */
- state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF;
- state->m_qamRfAgcCfg.outputLevel = 0;
- state->m_qamRfAgcCfg.minOutputLevel = 6023;
- state->m_qamRfAgcCfg.maxOutputLevel = 27000;
- state->m_qamRfAgcCfg.top = 0x2380;
- state->m_qamRfAgcCfg.cutOffCurrent = 4000;
- state->m_qamRfAgcCfg.speed = 3;
-
- /* QAM IF */
- state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO;
- state->m_qamIfAgcCfg.outputLevel = 0;
- state->m_qamIfAgcCfg.minOutputLevel = 0;
- state->m_qamIfAgcCfg.maxOutputLevel = 9000;
- state->m_qamIfAgcCfg.top = 0x0511;
- state->m_qamIfAgcCfg.cutOffCurrent = 0;
- state->m_qamIfAgcCfg.speed = 3;
- state->m_qamIfAgcCfg.IngainTgtMax = 5119;
- state->m_qamIfAgcCfg.FastClipCtrlDelay = 50;
-
- state->m_qamPgaCfg = 140;
- state->m_qamPreSawCfg.reference = 4;
- state->m_qamPreSawCfg.usePreSaw = false;
-
- state->m_OperationMode = OM_NONE;
- state->m_DrxkState = DRXK_UNINITIALIZED;
-
- /* MPEG output configuration */
- state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */
- state->m_insertRSByte = false; /* If TRUE; insert RS byte */
- state->m_invertDATA = false; /* If TRUE; invert DATA signals */
- state->m_invertERR = false; /* If TRUE; invert ERR signal */
- state->m_invertSTR = false; /* If TRUE; invert STR signals */
- state->m_invertVAL = false; /* If TRUE; invert VAL signals */
- state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
-
- /* If TRUE; static MPEG clockrate will be used;
- otherwise clockrate will adapt to the bitrate of the TS */
-
- state->m_DVBTBitrate = ulDVBTBitrate;
- state->m_DVBCBitrate = ulDVBCBitrate;
-
- state->m_TSDataStrength = (ulTSDataStrength & 0x07);
-
- /* Maximum bitrate in b/s in case static clockrate is selected */
- state->m_mpegTsStaticBitrate = 19392658;
- state->m_disableTEIhandling = false;
-
- if (ulInsertRSByte)
- state->m_insertRSByte = true;
-
- state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
- if (ulMpegLockTimeOut < 10000)
- state->m_MpegLockTimeOut = ulMpegLockTimeOut;
- state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
- if (ulDemodLockTimeOut < 10000)
- state->m_DemodLockTimeOut = ulDemodLockTimeOut;
-
- /* QAM defaults */
- state->m_Constellation = DRX_CONSTELLATION_AUTO;
- state->m_qamInterleaveMode = DRXK_QAM_I12_J17;
- state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */
- state->m_fecRsPrescale = 1;
-
- state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM;
- state->m_agcFastClipCtrlDelay = 0;
-
- state->m_GPIOCfg = (ulGPIOCfg);
-
- state->m_bPowerDown = false;
- state->m_currentPowerMode = DRX_POWER_DOWN;
-
- state->m_rfmirror = (ulRfMirror == 0);
- state->m_IfAgcPol = false;
- return 0;
-}
-
-static int DRXX_Open(struct drxk_state *state)
-{
- int status = 0;
- u32 jtag = 0;
- u16 bid = 0;
- u16 key = 0;
-
- dprintk(1, "\n");
- /* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
- /* Check device id */
- status = read16(state, SIO_TOP_COMM_KEY__A, &key);
- if (status < 0)
- goto error;
- status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
- if (status < 0)
- goto error;
- status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag);
- if (status < 0)
- goto error;
- status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid);
- if (status < 0)
- goto error;
- status = write16(state, SIO_TOP_COMM_KEY__A, key);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int GetDeviceCapabilities(struct drxk_state *state)
-{
- u16 sioPdrOhwCfg = 0;
- u32 sioTopJtagidLo = 0;
- int status;
- const char *spin = "";
-
- dprintk(1, "\n");
-
- /* driver 0.9.0 */
- /* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
- if (status < 0)
- goto error;
- status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
- if (status < 0)
- goto error;
-
- switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) {
- case 0:
- /* ignore (bypass ?) */
- break;
- case 1:
- /* 27 MHz */
- state->m_oscClockFreq = 27000;
- break;
- case 2:
- /* 20.25 MHz */
- state->m_oscClockFreq = 20250;
- break;
- case 3:
- /* 4 MHz */
- state->m_oscClockFreq = 20250;
- break;
- default:
- printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n");
- return -EINVAL;
- }
- /*
- Determine device capabilities
- Based on pinning v14
- */
- status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo);
- if (status < 0)
- goto error;
-
- printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo);
-
- /* driver 0.9.0 */
- switch ((sioTopJtagidLo >> 29) & 0xF) {
- case 0:
- state->m_deviceSpin = DRXK_SPIN_A1;
- spin = "A1";
- break;
- case 2:
- state->m_deviceSpin = DRXK_SPIN_A2;
- spin = "A2";
- break;
- case 3:
- state->m_deviceSpin = DRXK_SPIN_A3;
- spin = "A3";
- break;
- default:
- state->m_deviceSpin = DRXK_SPIN_UNKNOWN;
- status = -EINVAL;
- printk(KERN_ERR "drxk: Spin %d unknown\n",
- (sioTopJtagidLo >> 29) & 0xF);
- goto error2;
- }
- switch ((sioTopJtagidLo >> 12) & 0xFF) {
- case 0x13:
- /* typeId = DRX3913K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = false;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = false;
- state->m_hasGPIO1 = false;
- state->m_hasIRQN = false;
- break;
- case 0x15:
- /* typeId = DRX3915K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x16:
- /* typeId = DRX3916K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x18:
- /* typeId = DRX3918K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = false;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x21:
- /* typeId = DRX3921K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x23:
- /* typeId = DRX3923K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x25:
- /* typeId = DRX3925K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = true;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- case 0x26:
- /* typeId = DRX3926K_TYPE_ID */
- state->m_hasLNA = false;
- state->m_hasOOB = false;
- state->m_hasATV = true;
- state->m_hasAudio = false;
- state->m_hasDVBT = true;
- state->m_hasDVBC = true;
- state->m_hasSAWSW = true;
- state->m_hasGPIO2 = true;
- state->m_hasGPIO1 = true;
- state->m_hasIRQN = false;
- break;
- default:
- printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n",
- ((sioTopJtagidLo >> 12) & 0xFF));
- status = -EINVAL;
- goto error2;
- }
-
- printk(KERN_INFO
- "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n",
- ((sioTopJtagidLo >> 12) & 0xFF), spin,
- state->m_oscClockFreq / 1000,
- state->m_oscClockFreq % 1000);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
-error2:
- return status;
-}
-
-static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult)
-{
- int status;
- bool powerdown_cmd;
-
- dprintk(1, "\n");
-
- /* Write command */
- status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd);
- if (status < 0)
- goto error;
- if (cmd == SIO_HI_RA_RAM_CMD_RESET)
- msleep(1);
-
- powerdown_cmd =
- (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) &&
- ((state->m_HICfgCtrl) &
- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) ==
- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ);
- if (powerdown_cmd == false) {
- /* Wait until command rdy */
- u32 retryCount = 0;
- u16 waitCmd;
-
- do {
- msleep(1);
- retryCount += 1;
- status = read16(state, SIO_HI_RA_RAM_CMD__A,
- &waitCmd);
- } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES)
- && (waitCmd != 0));
- if (status < 0)
- goto error;
- status = read16(state, SIO_HI_RA_RAM_RES__A, pResult);
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int HI_CfgCommand(struct drxk_state *state)
-{
- int status;
-
- dprintk(1, "\n");
-
- mutex_lock(&state->mutex);
-
- status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout);
- if (status < 0)
- goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl);
- if (status < 0)
- goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey);
- if (status < 0)
- goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay);
- if (status < 0)
- goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv);
- if (status < 0)
- goto error;
- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
- if (status < 0)
- goto error;
- status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0);
- if (status < 0)
- goto error;
-
- state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
-error:
- mutex_unlock(&state->mutex);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int InitHI(struct drxk_state *state)
-{
- dprintk(1, "\n");
-
- state->m_HICfgWakeUpKey = (state->demod_address << 1);
- state->m_HICfgTimeout = 0x96FF;
- /* port/bridge/power down ctrl */
- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE;
-
- return HI_CfgCommand(state);
-}
-
-static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
-{
- int status = -1;
- u16 sioPdrMclkCfg = 0;
- u16 sioPdrMdxCfg = 0;
- u16 err_cfg = 0;
-
- dprintk(1, ": mpeg %s, %s mode\n",
- mpegEnable ? "enable" : "disable",
- state->m_enableParallel ? "parallel" : "serial");
-
- /* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
-
- /* MPEG TS pad configuration */
- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA);
- if (status < 0)
- goto error;
-
- if (mpegEnable == false) {
- /* Set MPEG TS pads to inputmode */
- status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- } else {
- /* Enable MPEG output */
- sioPdrMdxCfg =
- ((state->m_TSDataStrength <<
- SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003);
- sioPdrMclkCfg = ((state->m_TSClockkStrength <<
- SIO_PDR_MCLK_CFG_DRIVE__B) |
- 0x0003);
-
- status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
-
- if (state->enable_merr_cfg)
- err_cfg = sioPdrMdxCfg;
-
- status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
- if (status < 0)
- goto error;
-
- if (state->m_enableParallel == true) {
- /* paralel -> enable MD1 to MD7 */
- status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- } else {
- sioPdrMdxCfg = ((state->m_TSDataStrength <<
- SIO_PDR_MD0_CFG_DRIVE__B)
- | 0x0003);
- /* serial -> disable MD1 to MD7 */
- status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- }
- status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg);
- if (status < 0)
- goto error;
- status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg);
- if (status < 0)
- goto error;
- }
- /* Enable MB output over MPEG pads and ctl input */
- status = write16(state, SIO_PDR_MON_CFG__A, 0x0000);
- if (status < 0)
- goto error;
- /* Write nomagic word to enable pdr reg write */
- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int MPEGTSDisable(struct drxk_state *state)
-{
- dprintk(1, "\n");
-
- return MPEGTSConfigurePins(state, false);
-}
-
-static int BLChainCmd(struct drxk_state *state,
- u16 romOffset, u16 nrOfElements, u32 timeOut)
-{
- u16 blStatus = 0;
- int status;
- unsigned long end;
-
- dprintk(1, "\n");
- mutex_lock(&state->mutex);
- status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
- if (status < 0)
- goto error;
-
- end = jiffies + msecs_to_jiffies(timeOut);
- do {
- msleep(1);
- status = read16(state, SIO_BL_STATUS__A, &blStatus);
- if (status < 0)
- goto error;
- } while ((blStatus == 0x1) &&
- ((time_is_after_jiffies(end))));
-
- if (blStatus == 0x1) {
- printk(KERN_ERR "drxk: SIO not ready\n");
- status = -EINVAL;
- goto error2;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-error2:
- mutex_unlock(&state->mutex);
- return status;
-}
-
-
-static int DownloadMicrocode(struct drxk_state *state,
- const u8 pMCImage[], u32 Length)
-{
- const u8 *pSrc = pMCImage;
- u32 Address;
- u16 nBlocks;
- u16 BlockSize;
- u32 offset = 0;
- u32 i;
- int status = 0;
-
- dprintk(1, "\n");
-
- /* down the drain (we don't care about MAGIC_WORD) */
-#if 0
- /* For future reference */
- Drain = (pSrc[0] << 8) | pSrc[1];
-#endif
- pSrc += sizeof(u16);
- offset += sizeof(u16);
- nBlocks = (pSrc[0] << 8) | pSrc[1];
- pSrc += sizeof(u16);
- offset += sizeof(u16);
-
- for (i = 0; i < nBlocks; i += 1) {
- Address = (pSrc[0] << 24) | (pSrc[1] << 16) |
- (pSrc[2] << 8) | pSrc[3];
- pSrc += sizeof(u32);
- offset += sizeof(u32);
-
- BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16);
- pSrc += sizeof(u16);
- offset += sizeof(u16);
-
-#if 0
- /* For future reference */
- Flags = (pSrc[0] << 8) | pSrc[1];
-#endif
- pSrc += sizeof(u16);
- offset += sizeof(u16);
-
-#if 0
- /* For future reference */
- BlockCRC = (pSrc[0] << 8) | pSrc[1];
-#endif
- pSrc += sizeof(u16);
- offset += sizeof(u16);
-
- if (offset + BlockSize > Length) {
- printk(KERN_ERR "drxk: Firmware is corrupted.\n");
- return -EINVAL;
- }
-
- status = write_block(state, Address, BlockSize, pSrc);
- if (status < 0) {
- printk(KERN_ERR "drxk: Error %d while loading firmware\n", status);
- break;
- }
- pSrc += BlockSize;
- offset += BlockSize;
- }
- return status;
-}
-
-static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable)
-{
- int status;
- u16 data = 0;
- u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON;
- u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED;
- unsigned long end;
-
- dprintk(1, "\n");
-
- if (enable == false) {
- desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF;
- desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN;
- }
-
- status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
- if (status >= 0 && data == desiredStatus) {
- /* tokenring already has correct status */
- return status;
- }
- /* Disable/enable dvbt tokenring bridge */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl);
-
- end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT);
- do {
- status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data);
- if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end))
- break;
- msleep(1);
- } while (1);
- if (data != desiredStatus) {
- printk(KERN_ERR "drxk: SIO not ready\n");
- return -EINVAL;
- }
- return status;
-}
-
-static int MPEGTSStop(struct drxk_state *state)
-{
- int status = 0;
- u16 fecOcSncMode = 0;
- u16 fecOcIprMode = 0;
-
- dprintk(1, "\n");
-
- /* Gracefull shutdown (byte boundaries) */
- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
- if (status < 0)
- goto error;
- fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M;
- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
- if (status < 0)
- goto error;
-
- /* Suppress MCLK during absence of data */
- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode);
- if (status < 0)
- goto error;
- fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M;
- status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int scu_command(struct drxk_state *state,
- u16 cmd, u8 parameterLen,
- u16 *parameter, u8 resultLen, u16 *result)
-{
-#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15
-#error DRXK register mapping no longer compatible with this routine!
-#endif
- u16 curCmd = 0;
- int status = -EINVAL;
- unsigned long end;
- u8 buffer[34];
- int cnt = 0, ii;
- const char *p;
- char errname[30];
-
- dprintk(1, "\n");
-
- if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) ||
- ((resultLen > 0) && (result == NULL))) {
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
- }
-
- mutex_lock(&state->mutex);
-
- /* assume that the command register is ready
- since it is checked afterwards */
- for (ii = parameterLen - 1; ii >= 0; ii -= 1) {
- buffer[cnt++] = (parameter[ii] & 0xFF);
- buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF);
- }
- buffer[cnt++] = (cmd & 0xFF);
- buffer[cnt++] = ((cmd >> 8) & 0xFF);
-
- write_block(state, SCU_RAM_PARAM_0__A -
- (parameterLen - 1), cnt, buffer);
- /* Wait until SCU has processed command */
- end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME);
- do {
- msleep(1);
- status = read16(state, SCU_RAM_COMMAND__A, &curCmd);
- if (status < 0)
- goto error;
- } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end)));
- if (curCmd != DRX_SCU_READY) {
- printk(KERN_ERR "drxk: SCU not ready\n");
- status = -EIO;
- goto error2;
- }
- /* read results */
- if ((resultLen > 0) && (result != NULL)) {
- s16 err;
- int ii;
-
- for (ii = resultLen - 1; ii >= 0; ii -= 1) {
- status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]);
- if (status < 0)
- goto error;
- }
-
- /* Check if an error was reported by SCU */
- err = (s16)result[0];
- if (err >= 0)
- goto error;
-
- /* check for the known error codes */
- switch (err) {
- case SCU_RESULT_UNKCMD:
- p = "SCU_RESULT_UNKCMD";
- break;
- case SCU_RESULT_UNKSTD:
- p = "SCU_RESULT_UNKSTD";
- break;
- case SCU_RESULT_SIZE:
- p = "SCU_RESULT_SIZE";
- break;
- case SCU_RESULT_INVPAR:
- p = "SCU_RESULT_INVPAR";
- break;
- default: /* Other negative values are errors */
- sprintf(errname, "ERROR: %d\n", err);
- p = errname;
- }
- printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd);
- print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt);
- status = -EINVAL;
- goto error2;
- }
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-error2:
- mutex_unlock(&state->mutex);
- return status;
-}
-
-static int SetIqmAf(struct drxk_state *state, bool active)
-{
- u16 data = 0;
- int status;
-
- dprintk(1, "\n");
-
- /* Configure IQM */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
-
- if (!active) {
- data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY
- | IQM_AF_STDBY_STDBY_AMP_STANDBY
- | IQM_AF_STDBY_STDBY_PD_STANDBY
- | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY
- | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY);
- } else {
- data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY)
- & (~IQM_AF_STDBY_STDBY_AMP_STANDBY)
- & (~IQM_AF_STDBY_STDBY_PD_STANDBY)
- & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY)
- & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY)
- );
- }
- status = write16(state, IQM_AF_STDBY__A, data);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode)
-{
- int status = 0;
- u16 sioCcPwdMode = 0;
-
- dprintk(1, "\n");
-
- /* Check arguments */
- if (mode == NULL)
- return -EINVAL;
-
- switch (*mode) {
- case DRX_POWER_UP:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE;
- break;
- case DRXK_POWER_DOWN_OFDM:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM;
- break;
- case DRXK_POWER_DOWN_CORE:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK;
- break;
- case DRXK_POWER_DOWN_PLL:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL;
- break;
- case DRX_POWER_DOWN:
- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC;
- break;
- default:
- /* Unknow sleep mode */
- return -EINVAL;
- }
-
- /* If already in requested power mode, do nothing */
- if (state->m_currentPowerMode == *mode)
- return 0;
-
- /* For next steps make sure to start from DRX_POWER_UP mode */
- if (state->m_currentPowerMode != DRX_POWER_UP) {
- status = PowerUpDevice(state);
- if (status < 0)
- goto error;
- status = DVBTEnableOFDMTokenRing(state, true);
- if (status < 0)
- goto error;
- }
-
- if (*mode == DRX_POWER_UP) {
- /* Restore analog & pin configuartion */
- } else {
- /* Power down to requested mode */
- /* Backup some register settings */
- /* Set pins with possible pull-ups connected
- to them in input mode */
- /* Analog power down */
- /* ADC power down */
- /* Power down device */
- /* stop all comm_exec */
- /* Stop and power down previous standard */
- switch (state->m_OperationMode) {
- case OM_DVBT:
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = PowerDownDVBT(state, false);
- if (status < 0)
- goto error;
- break;
- case OM_QAM_ITU_A:
- case OM_QAM_ITU_C:
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = PowerDownQAM(state);
- if (status < 0)
- goto error;
- break;
- default:
- break;
- }
- status = DVBTEnableOFDMTokenRing(state, false);
- if (status < 0)
- goto error;
- status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode);
- if (status < 0)
- goto error;
- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
- if (status < 0)
- goto error;
-
- if (*mode != DRXK_POWER_DOWN_OFDM) {
- state->m_HICfgCtrl |=
- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
- status = HI_CfgCommand(state);
- if (status < 0)
- goto error;
- }
- }
- state->m_currentPowerMode = *mode;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode)
-{
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
- u16 cmdResult = 0;
- u16 data = 0;
- int status;
-
- dprintk(1, "\n");
-
- status = read16(state, SCU_COMM_EXEC__A, &data);
- if (status < 0)
- goto error;
- if (data == SCU_COMM_EXEC_ACTIVE) {
- /* Send OFDM stop command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
- /* Send OFDM reset command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
- }
-
- /* Reset datapath for OFDM, processors first */
- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
- if (status < 0)
- goto error;
-
- /* powerdown AFE */
- status = SetIqmAf(state, false);
- if (status < 0)
- goto error;
-
- /* powerdown to OFDM mode */
- if (setPowerMode) {
- status = CtrlPowerMode(state, &powerMode);
- if (status < 0)
- goto error;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SetOperationMode(struct drxk_state *state,
- enum OperationMode oMode)
-{
- int status = 0;
-
- dprintk(1, "\n");
- /*
- Stop and power down previous standard
- TODO investigate total power down instead of partial
- power down depending on "previous" standard.
- */
-
- /* disable HW lock indicator */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
-
- /* Device is already at the required mode */
- if (state->m_OperationMode == oMode)
- return 0;
-
- switch (state->m_OperationMode) {
- /* OM_NONE was added for start up */
- case OM_NONE:
- break;
- case OM_DVBT:
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = PowerDownDVBT(state, true);
- if (status < 0)
- goto error;
- state->m_OperationMode = OM_NONE;
- break;
- case OM_QAM_ITU_A: /* fallthrough */
- case OM_QAM_ITU_C:
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = PowerDownQAM(state);
- if (status < 0)
- goto error;
- state->m_OperationMode = OM_NONE;
- break;
- case OM_QAM_ITU_B:
- default:
- status = -EINVAL;
- goto error;
- }
-
- /*
- Power up new standard
- */
- switch (oMode) {
- case OM_DVBT:
- dprintk(1, ": DVB-T\n");
- state->m_OperationMode = oMode;
- status = SetDVBTStandard(state, oMode);
- if (status < 0)
- goto error;
- break;
- case OM_QAM_ITU_A: /* fallthrough */
- case OM_QAM_ITU_C:
- dprintk(1, ": DVB-C Annex %c\n",
- (state->m_OperationMode == OM_QAM_ITU_A) ? 'A' : 'C');
- state->m_OperationMode = oMode;
- status = SetQAMStandard(state, oMode);
- if (status < 0)
- goto error;
- break;
- case OM_QAM_ITU_B:
- default:
- status = -EINVAL;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int Start(struct drxk_state *state, s32 offsetFreq,
- s32 IntermediateFrequency)
-{
- int status = -EINVAL;
-
- u16 IFreqkHz;
- s32 OffsetkHz = offsetFreq / 1000;
-
- dprintk(1, "\n");
- if (state->m_DrxkState != DRXK_STOPPED &&
- state->m_DrxkState != DRXK_DTV_STARTED)
- goto error;
-
- state->m_bMirrorFreqSpect = (state->props.inversion == INVERSION_ON);
-
- if (IntermediateFrequency < 0) {
- state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect;
- IntermediateFrequency = -IntermediateFrequency;
- }
-
- switch (state->m_OperationMode) {
- case OM_QAM_ITU_A:
- case OM_QAM_ITU_C:
- IFreqkHz = (IntermediateFrequency / 1000);
- status = SetQAM(state, IFreqkHz, OffsetkHz);
- if (status < 0)
- goto error;
- state->m_DrxkState = DRXK_DTV_STARTED;
- break;
- case OM_DVBT:
- IFreqkHz = (IntermediateFrequency / 1000);
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = SetDVBT(state, IFreqkHz, OffsetkHz);
- if (status < 0)
- goto error;
- status = DVBTStart(state);
- if (status < 0)
- goto error;
- state->m_DrxkState = DRXK_DTV_STARTED;
- break;
- default:
- break;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int ShutDown(struct drxk_state *state)
-{
- dprintk(1, "\n");
-
- MPEGTSStop(state);
- return 0;
-}
-
-static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus,
- u32 Time)
-{
- int status = -EINVAL;
-
- dprintk(1, "\n");
-
- if (pLockStatus == NULL)
- goto error;
-
- *pLockStatus = NOT_LOCKED;
-
- /* define the SCU command code */
- switch (state->m_OperationMode) {
- case OM_QAM_ITU_A:
- case OM_QAM_ITU_B:
- case OM_QAM_ITU_C:
- status = GetQAMLockStatus(state, pLockStatus);
- break;
- case OM_DVBT:
- status = GetDVBTLockStatus(state, pLockStatus);
- break;
- default:
- break;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int MPEGTSStart(struct drxk_state *state)
-{
- int status;
-
- u16 fecOcSncMode = 0;
-
- /* Allow OC to sync again */
- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode);
- if (status < 0)
- goto error;
- fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M;
- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_SNC_UNLOCK__A, 1);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int MPEGTSDtoInit(struct drxk_state *state)
-{
- int status;
-
- dprintk(1, "\n");
-
- /* Rate integration settings */
- status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4);
- if (status < 0)
- goto error;
-
- /* Additional configuration */
- status = write16(state, FEC_OC_OCR_INVERT__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_SNC_LWM__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_SNC_HWM__A, 12);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int MPEGTSDtoSetup(struct drxk_state *state,
- enum OperationMode oMode)
-{
- int status;
-
- u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */
- u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */
- u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */
- u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */
- u16 fecOcTmdMode = 0;
- u16 fecOcTmdIntUpdRate = 0;
- u32 maxBitRate = 0;
- bool staticCLK = false;
-
- dprintk(1, "\n");
-
- /* Check insertion of the Reed-Solomon parity bytes */
- status = read16(state, FEC_OC_MODE__A, &fecOcRegMode);
- if (status < 0)
- goto error;
- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode);
- if (status < 0)
- goto error;
- fecOcRegMode &= (~FEC_OC_MODE_PARITY__M);
- fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M);
- if (state->m_insertRSByte == true) {
- /* enable parity symbol forward */
- fecOcRegMode |= FEC_OC_MODE_PARITY__M;
- /* MVAL disable during parity bytes */
- fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M;
- /* TS burst length to 204 */
- fecOcDtoBurstLen = 204;
- }
-
- /* Check serial or parrallel output */
- fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M));
- if (state->m_enableParallel == false) {
- /* MPEG data output is serial -> set ipr_mode[0] */
- fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M;
- }
-
- switch (oMode) {
- case OM_DVBT:
- maxBitRate = state->m_DVBTBitrate;
- fecOcTmdMode = 3;
- fecOcRcnCtlRate = 0xC00000;
- staticCLK = state->m_DVBTStaticCLK;
- break;
- case OM_QAM_ITU_A: /* fallthrough */
- case OM_QAM_ITU_C:
- fecOcTmdMode = 0x0004;
- fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */
- maxBitRate = state->m_DVBCBitrate;
- staticCLK = state->m_DVBCStaticCLK;
- break;
- default:
- status = -EINVAL;
- } /* switch (standard) */
- if (status < 0)
- goto error;
-
- /* Configure DTO's */
- if (staticCLK) {
- u32 bitRate = 0;
-
- /* Rational DTO for MCLK source (static MCLK rate),
- Dynamic DTO for optimal grouping
- (avoid intra-packet gaps),
- DTO offset enable to sync TS burst with MSTRT */
- fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M |
- FEC_OC_DTO_MODE_OFFSET_ENABLE__M);
- fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M |
- FEC_OC_FCT_MODE_VIRT_ENA__M);
-
- /* Check user defined bitrate */
- bitRate = maxBitRate;
- if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */
- bitRate = 75900000UL;
- }
- /* Rational DTO period:
- dto_period = (Fsys / bitrate) - 2
-
- Result should be floored,
- to make sure >= requested bitrate
- */
- fecOcDtoPeriod = (u16) (((state->m_sysClockFreq)
- * 1000) / bitRate);
- if (fecOcDtoPeriod <= 2)
- fecOcDtoPeriod = 0;
- else
- fecOcDtoPeriod -= 2;
- fecOcTmdIntUpdRate = 8;
- } else {
- /* (commonAttr->staticCLK == false) => dynamic mode */
- fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M;
- fecOcFctMode = FEC_OC_FCT_MODE__PRE;
- fecOcTmdIntUpdRate = 5;
- }
-
- /* Write appropriate registers with requested configuration */
- status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_MODE__A, fecOcRegMode);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode);
- if (status < 0)
- goto error;
-
- /* Rate integration settings */
- status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int MPEGTSConfigurePolarity(struct drxk_state *state)
-{
- u16 fecOcRegIprInvert = 0;
-
- /* Data mask for the output data byte */
- u16 InvertDataMask =
- FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M |
- FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M |
- FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M |
- FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M;
-
- dprintk(1, "\n");
-
- /* Control selective inversion of output bits */
- fecOcRegIprInvert &= (~(InvertDataMask));
- if (state->m_invertDATA == true)
- fecOcRegIprInvert |= InvertDataMask;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M));
- if (state->m_invertERR == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M));
- if (state->m_invertSTR == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M));
- if (state->m_invertVAL == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M;
- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M));
- if (state->m_invertCLK == true)
- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M;
-
- return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert);
-}
-
-#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000
-
-static int SetAgcRf(struct drxk_state *state,
- struct SCfgAgc *pAgcCfg, bool isDTV)
-{
- int status = -EINVAL;
- u16 data = 0;
- struct SCfgAgc *pIfAgcSettings;
-
- dprintk(1, "\n");
-
- if (pAgcCfg == NULL)
- goto error;
-
- switch (pAgcCfg->ctrlMode) {
- case DRXK_AGC_CTRL_AUTO:
- /* Enable RF AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
-
- /* Enable SCU RF AGC loop */
- data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
-
- /* Polarity */
- if (state->m_RfAgcPol)
- data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
- else
- data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
-
- /* Set speed (using complementary reduction value) */
- status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
- if (status < 0)
- goto error;
-
- data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M;
- data |= (~(pAgcCfg->speed <<
- SCU_RAM_AGC_KI_RED_RAGC_RED__B)
- & SCU_RAM_AGC_KI_RED_RAGC_RED__M);
-
- status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
- if (status < 0)
- goto error;
-
- if (IsDVBT(state))
- pIfAgcSettings = &state->m_dvbtIfAgcCfg;
- else if (IsQAM(state))
- pIfAgcSettings = &state->m_qamIfAgcCfg;
- else
- pIfAgcSettings = &state->m_atvIfAgcCfg;
- if (pIfAgcSettings == NULL) {
- status = -EINVAL;
- goto error;
- }
-
- /* Set TOP, only if IF-AGC is in AUTO mode */
- if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO)
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top);
- if (status < 0)
- goto error;
-
- /* Cut-Off current */
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent);
- if (status < 0)
- goto error;
-
- /* Max. output level */
- status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel);
- if (status < 0)
- goto error;
-
- break;
-
- case DRXK_AGC_CTRL_USER:
- /* Enable RF AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
-
- /* Disable SCU RF AGC loop */
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
- data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
- if (state->m_RfAgcPol)
- data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
- else
- data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
-
- /* SCU c.o.c. to 0, enabling full control range */
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0);
- if (status < 0)
- goto error;
-
- /* Write value to output pin */
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel);
- if (status < 0)
- goto error;
- break;
-
- case DRXK_AGC_CTRL_OFF:
- /* Disable RF AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
-
- /* Disable SCU RF AGC loop */
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
- data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
- break;
-
- default:
- status = -EINVAL;
-
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000
-
-static int SetAgcIf(struct drxk_state *state,
- struct SCfgAgc *pAgcCfg, bool isDTV)
-{
- u16 data = 0;
- int status = 0;
- struct SCfgAgc *pRfAgcSettings;
-
- dprintk(1, "\n");
-
- switch (pAgcCfg->ctrlMode) {
- case DRXK_AGC_CTRL_AUTO:
-
- /* Enable IF AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
-
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
-
- /* Enable SCU IF AGC loop */
- data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
-
- /* Polarity */
- if (state->m_IfAgcPol)
- data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
- else
- data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
-
- /* Set speed (using complementary reduction value) */
- status = read16(state, SCU_RAM_AGC_KI_RED__A, &data);
- if (status < 0)
- goto error;
- data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M;
- data |= (~(pAgcCfg->speed <<
- SCU_RAM_AGC_KI_RED_IAGC_RED__B)
- & SCU_RAM_AGC_KI_RED_IAGC_RED__M);
-
- status = write16(state, SCU_RAM_AGC_KI_RED__A, data);
- if (status < 0)
- goto error;
-
- if (IsQAM(state))
- pRfAgcSettings = &state->m_qamRfAgcCfg;
- else
- pRfAgcSettings = &state->m_atvRfAgcCfg;
- if (pRfAgcSettings == NULL)
- return -1;
- /* Restore TOP */
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top);
- if (status < 0)
- goto error;
- break;
-
- case DRXK_AGC_CTRL_USER:
-
- /* Enable IF AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
-
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
-
- /* Disable SCU IF AGC loop */
- data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
-
- /* Polarity */
- if (state->m_IfAgcPol)
- data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
- else
- data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
-
- /* Write value to output pin */
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel);
- if (status < 0)
- goto error;
- break;
-
- case DRXK_AGC_CTRL_OFF:
-
- /* Disable If AGC DAC */
- status = read16(state, IQM_AF_STDBY__A, &data);
- if (status < 0)
- goto error;
- data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY;
- status = write16(state, IQM_AF_STDBY__A, data);
- if (status < 0)
- goto error;
-
- /* Disable SCU IF AGC loop */
- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data);
- if (status < 0)
- goto error;
- data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M;
- status = write16(state, SCU_RAM_AGC_CONFIG__A, data);
- if (status < 0)
- goto error;
- break;
- } /* switch (agcSettingsIf->ctrlMode) */
-
- /* always set the top to support
- configurations without if-loop */
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int ReadIFAgc(struct drxk_state *state, u32 *pValue)
-{
- u16 agcDacLvl;
- int status;
- u16 Level = 0;
-
- dprintk(1, "\n");
-
- status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl);
- if (status < 0) {
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
- }
-
- *pValue = 0;
-
- if (agcDacLvl > DRXK_AGC_DAC_OFFSET)
- Level = agcDacLvl - DRXK_AGC_DAC_OFFSET;
- if (Level < 14000)
- *pValue = (14000 - Level) / 4;
- else
- *pValue = 0;
-
- return status;
-}
-
-static int GetQAMSignalToNoise(struct drxk_state *state,
- s32 *pSignalToNoise)
-{
- int status = 0;
- u16 qamSlErrPower = 0; /* accum. error between
- raw and sliced symbols */
- u32 qamSlSigPower = 0; /* used for MER, depends of
- QAM modulation */
- u32 qamSlMer = 0; /* QAM MER */
-
- dprintk(1, "\n");
-
- /* MER calculation */
-
- /* get the register value needed for MER */
- status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower);
- if (status < 0) {
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return -EINVAL;
- }
-
- switch (state->props.modulation) {
- case QAM_16:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2;
- break;
- case QAM_32:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2;
- break;
- case QAM_64:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2;
- break;
- case QAM_128:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2;
- break;
- default:
- case QAM_256:
- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2;
- break;
- }
-
- if (qamSlErrPower > 0) {
- qamSlMer = Log10Times100(qamSlSigPower) -
- Log10Times100((u32) qamSlErrPower);
- }
- *pSignalToNoise = qamSlMer;
-
- return status;
-}
-
-static int GetDVBTSignalToNoise(struct drxk_state *state,
- s32 *pSignalToNoise)
-{
- int status;
- u16 regData = 0;
- u32 EqRegTdSqrErrI = 0;
- u32 EqRegTdSqrErrQ = 0;
- u16 EqRegTdSqrErrExp = 0;
- u16 EqRegTdTpsPwrOfs = 0;
- u16 EqRegTdReqSmbCnt = 0;
- u32 tpsCnt = 0;
- u32 SqrErrIQ = 0;
- u32 a = 0;
- u32 b = 0;
- u32 c = 0;
- u32 iMER = 0;
- u16 transmissionParams = 0;
-
- dprintk(1, "\n");
-
- status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs);
- if (status < 0)
- goto error;
- status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt);
- if (status < 0)
- goto error;
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp);
- if (status < 0)
- goto error;
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, &regData);
- if (status < 0)
- goto error;
- /* Extend SQR_ERR_I operational range */
- EqRegTdSqrErrI = (u32) regData;
- if ((EqRegTdSqrErrExp > 11) &&
- (EqRegTdSqrErrI < 0x00000FFFUL)) {
- EqRegTdSqrErrI += 0x00010000UL;
- }
- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, &regData);
- if (status < 0)
- goto error;
- /* Extend SQR_ERR_Q operational range */
- EqRegTdSqrErrQ = (u32) regData;
- if ((EqRegTdSqrErrExp > 11) &&
- (EqRegTdSqrErrQ < 0x00000FFFUL))
- EqRegTdSqrErrQ += 0x00010000UL;
-
- status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams);
- if (status < 0)
- goto error;
-
- /* Check input data for MER */
-
- /* MER calculation (in 0.1 dB) without math.h */
- if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0))
- iMER = 0;
- else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) {
- /* No error at all, this must be the HW reset value
- * Apparently no first measurement yet
- * Set MER to 0.0 */
- iMER = 0;
- } else {
- SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) <<
- EqRegTdSqrErrExp;
- if ((transmissionParams &
- OFDM_SC_RA_RAM_OP_PARAM_MODE__M)
- == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K)
- tpsCnt = 17;
- else
- tpsCnt = 68;
-
- /* IMER = 100 * log10 (x)
- where x = (EqRegTdTpsPwrOfs^2 *
- EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ
-
- => IMER = a + b -c
- where a = 100 * log10 (EqRegTdTpsPwrOfs^2)
- b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt)
- c = 100 * log10 (SqrErrIQ)
- */
-
- /* log(x) x = 9bits * 9bits->18 bits */
- a = Log10Times100(EqRegTdTpsPwrOfs *
- EqRegTdTpsPwrOfs);
- /* log(x) x = 16bits * 7bits->23 bits */
- b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt);
- /* log(x) x = (16bits + 16bits) << 15 ->32 bits */
- c = Log10Times100(SqrErrIQ);
-
- iMER = a + b;
- /* No negative MER, clip to zero */
- if (iMER > c)
- iMER -= c;
- else
- iMER = 0;
- }
- *pSignalToNoise = iMER;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise)
-{
- dprintk(1, "\n");
-
- *pSignalToNoise = 0;
- switch (state->m_OperationMode) {
- case OM_DVBT:
- return GetDVBTSignalToNoise(state, pSignalToNoise);
- case OM_QAM_ITU_A:
- case OM_QAM_ITU_C:
- return GetQAMSignalToNoise(state, pSignalToNoise);
- default:
- break;
- }
- return 0;
-}
-
-#if 0
-static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality)
-{
- /* SNR Values for quasi errorfree reception rom Nordig 2.2 */
- int status = 0;
-
- dprintk(1, "\n");
-
- static s32 QE_SN[] = {
- 51, /* QPSK 1/2 */
- 69, /* QPSK 2/3 */
- 79, /* QPSK 3/4 */
- 89, /* QPSK 5/6 */
- 97, /* QPSK 7/8 */
- 108, /* 16-QAM 1/2 */
- 131, /* 16-QAM 2/3 */
- 146, /* 16-QAM 3/4 */
- 156, /* 16-QAM 5/6 */
- 160, /* 16-QAM 7/8 */
- 165, /* 64-QAM 1/2 */
- 187, /* 64-QAM 2/3 */
- 202, /* 64-QAM 3/4 */
- 216, /* 64-QAM 5/6 */
- 225, /* 64-QAM 7/8 */
- };
-
- *pQuality = 0;
-
- do {
- s32 SignalToNoise = 0;
- u16 Constellation = 0;
- u16 CodeRate = 0;
- u32 SignalToNoiseRel;
- u32 BERQuality;
-
- status = GetDVBTSignalToNoise(state, &SignalToNoise);
- if (status < 0)
- break;
- status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation);
- if (status < 0)
- break;
- Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M;
-
- status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate);
- if (status < 0)
- break;
- CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M;
-
- if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM ||
- CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8)
- break;
- SignalToNoiseRel = SignalToNoise -
- QE_SN[Constellation * 5 + CodeRate];
- BERQuality = 100;
-
- if (SignalToNoiseRel < -70)
- *pQuality = 0;
- else if (SignalToNoiseRel < 30)
- *pQuality = ((SignalToNoiseRel + 70) *
- BERQuality) / 100;
- else
- *pQuality = BERQuality;
- } while (0);
- return 0;
-};
-
-static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality)
-{
- int status = 0;
- *pQuality = 0;
-
- dprintk(1, "\n");
-
- do {
- u32 SignalToNoise = 0;
- u32 BERQuality = 100;
- u32 SignalToNoiseRel = 0;
-
- status = GetQAMSignalToNoise(state, &SignalToNoise);
- if (status < 0)
- break;
-
- switch (state->props.modulation) {
- case QAM_16:
- SignalToNoiseRel = SignalToNoise - 200;
- break;
- case QAM_32:
- SignalToNoiseRel = SignalToNoise - 230;
- break; /* Not in NorDig */
- case QAM_64:
- SignalToNoiseRel = SignalToNoise - 260;
- break;
- case QAM_128:
- SignalToNoiseRel = SignalToNoise - 290;
- break;
- default:
- case QAM_256:
- SignalToNoiseRel = SignalToNoise - 320;
- break;
- }
-
- if (SignalToNoiseRel < -70)
- *pQuality = 0;
- else if (SignalToNoiseRel < 30)
- *pQuality = ((SignalToNoiseRel + 70) *
- BERQuality) / 100;
- else
- *pQuality = BERQuality;
- } while (0);
-
- return status;
-}
-
-static int GetQuality(struct drxk_state *state, s32 *pQuality)
-{
- dprintk(1, "\n");
-
- switch (state->m_OperationMode) {
- case OM_DVBT:
- return GetDVBTQuality(state, pQuality);
- case OM_QAM_ITU_A:
- return GetDVBCQuality(state, pQuality);
- default:
- break;
- }
-
- return 0;
-}
-#endif
-
-/* Free data ram in SIO HI */
-#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040
-#define SIO_HI_RA_RAM_USR_END__A 0x420060
-
-#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A)
-#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7)
-#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ
-#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE
-
-#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F)
-#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F)
-#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF)
-
-static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge)
-{
- int status = -EINVAL;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return 0;
- if (state->m_DrxkState == DRXK_POWERED_DOWN)
- goto error;
-
- if (state->no_i2c_bridge)
- return 0;
-
- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY);
- if (status < 0)
- goto error;
- if (bEnableBridge) {
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED);
- if (status < 0)
- goto error;
- } else {
- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN);
- if (status < 0)
- goto error;
- }
-
- status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SetPreSaw(struct drxk_state *state,
- struct SCfgPreSaw *pPreSawCfg)
-{
- int status = -EINVAL;
-
- dprintk(1, "\n");
-
- if ((pPreSawCfg == NULL)
- || (pPreSawCfg->reference > IQM_AF_PDREF__M))
- goto error;
-
- status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int BLDirectCmd(struct drxk_state *state, u32 targetAddr,
- u16 romOffset, u16 nrOfElements, u32 timeOut)
-{
- u16 blStatus = 0;
- u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF);
- u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF);
- int status;
- unsigned long end;
-
- dprintk(1, "\n");
-
- mutex_lock(&state->mutex);
- status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_TGT_HDR__A, blockbank);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_TGT_ADDR__A, offset);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_SRC_ADDR__A, romOffset);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements);
- if (status < 0)
- goto error;
- status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON);
- if (status < 0)
- goto error;
-
- end = jiffies + msecs_to_jiffies(timeOut);
- do {
- status = read16(state, SIO_BL_STATUS__A, &blStatus);
- if (status < 0)
- goto error;
- } while ((blStatus == 0x1) && time_is_after_jiffies(end));
- if (blStatus == 0x1) {
- printk(KERN_ERR "drxk: SIO not ready\n");
- status = -EINVAL;
- goto error2;
- }
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-error2:
- mutex_unlock(&state->mutex);
- return status;
-
-}
-
-static int ADCSyncMeasurement(struct drxk_state *state, u16 *count)
-{
- u16 data = 0;
- int status;
-
- dprintk(1, "\n");
-
- /* Start measurement */
- status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_START_LOCK__A, 1);
- if (status < 0)
- goto error;
-
- *count = 0;
- status = read16(state, IQM_AF_PHASE0__A, &data);
- if (status < 0)
- goto error;
- if (data == 127)
- *count = *count + 1;
- status = read16(state, IQM_AF_PHASE1__A, &data);
- if (status < 0)
- goto error;
- if (data == 127)
- *count = *count + 1;
- status = read16(state, IQM_AF_PHASE2__A, &data);
- if (status < 0)
- goto error;
- if (data == 127)
- *count = *count + 1;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int ADCSynchronization(struct drxk_state *state)
-{
- u16 count = 0;
- int status;
-
- dprintk(1, "\n");
-
- status = ADCSyncMeasurement(state, &count);
- if (status < 0)
- goto error;
-
- if (count == 1) {
- /* Try sampling on a diffrent edge */
- u16 clkNeg = 0;
-
- status = read16(state, IQM_AF_CLKNEG__A, &clkNeg);
- if (status < 0)
- goto error;
- if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) ==
- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) {
- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
- clkNeg |=
- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG;
- } else {
- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M));
- clkNeg |=
- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS;
- }
- status = write16(state, IQM_AF_CLKNEG__A, clkNeg);
- if (status < 0)
- goto error;
- status = ADCSyncMeasurement(state, &count);
- if (status < 0)
- goto error;
- }
-
- if (count < 2)
- status = -EINVAL;
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SetFrequencyShifter(struct drxk_state *state,
- u16 intermediateFreqkHz,
- s32 tunerFreqOffset, bool isDTV)
-{
- bool selectPosImage = false;
- u32 rfFreqResidual = tunerFreqOffset;
- u32 fmFrequencyShift = 0;
- bool tunerMirror = !state->m_bMirrorFreqSpect;
- u32 adcFreq;
- bool adcFlip;
- int status;
- u32 ifFreqActual;
- u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3);
- u32 frequencyShift;
- bool imageToSelect;
-
- dprintk(1, "\n");
-
- /*
- Program frequency shifter
- No need to account for mirroring on RF
- */
- if (isDTV) {
- if ((state->m_OperationMode == OM_QAM_ITU_A) ||
- (state->m_OperationMode == OM_QAM_ITU_C) ||
- (state->m_OperationMode == OM_DVBT))
- selectPosImage = true;
- else
- selectPosImage = false;
- }
- if (tunerMirror)
- /* tuner doesn't mirror */
- ifFreqActual = intermediateFreqkHz +
- rfFreqResidual + fmFrequencyShift;
- else
- /* tuner mirrors */
- ifFreqActual = intermediateFreqkHz -
- rfFreqResidual - fmFrequencyShift;
- if (ifFreqActual > samplingFrequency / 2) {
- /* adc mirrors */
- adcFreq = samplingFrequency - ifFreqActual;
- adcFlip = true;
- } else {
- /* adc doesn't mirror */
- adcFreq = ifFreqActual;
- adcFlip = false;
- }
-
- frequencyShift = adcFreq;
- imageToSelect = state->m_rfmirror ^ tunerMirror ^
- adcFlip ^ selectPosImage;
- state->m_IqmFsRateOfs =
- Frac28a((frequencyShift), samplingFrequency);
-
- if (imageToSelect)
- state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1;
-
- /* Program frequency shifter with tuner offset compensation */
- /* frequencyShift += tunerFreqOffset; TODO */
- status = write32(state, IQM_FS_RATE_OFS_LO__A,
- state->m_IqmFsRateOfs);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int InitAGC(struct drxk_state *state, bool isDTV)
-{
- u16 ingainTgt = 0;
- u16 ingainTgtMin = 0;
- u16 ingainTgtMax = 0;
- u16 clpCyclen = 0;
- u16 clpSumMin = 0;
- u16 clpDirTo = 0;
- u16 snsSumMin = 0;
- u16 snsSumMax = 0;
- u16 clpSumMax = 0;
- u16 snsDirTo = 0;
- u16 kiInnergainMin = 0;
- u16 ifIaccuHiTgt = 0;
- u16 ifIaccuHiTgtMin = 0;
- u16 ifIaccuHiTgtMax = 0;
- u16 data = 0;
- u16 fastClpCtrlDelay = 0;
- u16 clpCtrlMode = 0;
- int status = 0;
-
- dprintk(1, "\n");
-
- /* Common settings */
- snsSumMax = 1023;
- ifIaccuHiTgtMin = 2047;
- clpCyclen = 500;
- clpSumMax = 1023;
-
- /* AGCInit() not available for DVBT; init done in microcode */
- if (!IsQAM(state)) {
- printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode);
- return -EINVAL;
- }
-
- /* FIXME: Analog TV AGC require different settings */
-
- /* Standard specific settings */
- clpSumMin = 8;
- clpDirTo = (u16) -9;
- clpCtrlMode = 0;
- snsSumMin = 8;
- snsDirTo = (u16) -9;
- kiInnergainMin = (u16) -1030;
- ifIaccuHiTgtMax = 0x2380;
- ifIaccuHiTgt = 0x2380;
- ingainTgtMin = 0x0511;
- ingainTgt = 0x0511;
- ingainTgtMax = 5119;
- fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay;
-
- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500);
- if (status < 0)
- goto error;
-
- /* Initialize inner-loop KI gain factors */
- status = read16(state, SCU_RAM_AGC_KI__A, &data);
- if (status < 0)
- goto error;
-
- data = 0x0657;
- data &= ~SCU_RAM_AGC_KI_RF__M;
- data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B);
- data &= ~SCU_RAM_AGC_KI_IF__M;
- data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B);
-
- status = write16(state, SCU_RAM_AGC_KI__A, data);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr)
-{
- int status;
-
- dprintk(1, "\n");
- if (packetErr == NULL)
- status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0);
- else
- status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int DVBTScCommand(struct drxk_state *state,
- u16 cmd, u16 subcmd,
- u16 param0, u16 param1, u16 param2,
- u16 param3, u16 param4)
-{
- u16 curCmd = 0;
- u16 errCode = 0;
- u16 retryCnt = 0;
- u16 scExec = 0;
- int status;
-
- dprintk(1, "\n");
- status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec);
- if (scExec != 1) {
- /* SC is not running */
- status = -EINVAL;
- }
- if (status < 0)
- goto error;
-
- /* Wait until sc is ready to receive command */
- retryCnt = 0;
- do {
- msleep(1);
- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
- retryCnt++;
- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
- goto error;
-
- /* Write sub-command */
- switch (cmd) {
- /* All commands using sub-cmd */
- case OFDM_SC_RA_RAM_CMD_PROC_START:
- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
- status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd);
- if (status < 0)
- goto error;
- break;
- default:
- /* Do nothing */
- break;
- }
-
- /* Write needed parameters and the command */
- switch (cmd) {
- /* All commands using 5 parameters */
- /* All commands using 4 parameters */
- /* All commands using 3 parameters */
- /* All commands using 2 parameters */
- case OFDM_SC_RA_RAM_CMD_PROC_START:
- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
- status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1);
- /* All commands using 1 parameters */
- case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
- case OFDM_SC_RA_RAM_CMD_USER_IO:
- status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0);
- /* All commands using 0 parameters */
- case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
- case OFDM_SC_RA_RAM_CMD_NULL:
- /* Write command */
- status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd);
- break;
- default:
- /* Unknown command */
- status = -EINVAL;
- }
- if (status < 0)
- goto error;
-
- /* Wait until sc is ready processing command */
- retryCnt = 0;
- do {
- msleep(1);
- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd);
- retryCnt++;
- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES));
- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0))
- goto error;
-
- /* Check for illegal cmd */
- status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode);
- if (errCode == 0xFFFF) {
- /* illegal command */
- status = -EINVAL;
- }
- if (status < 0)
- goto error;
-
- /* Retreive results parameters from SC */
- switch (cmd) {
- /* All commands yielding 5 results */
- /* All commands yielding 4 results */
- /* All commands yielding 3 results */
- /* All commands yielding 2 results */
- /* All commands yielding 1 result */
- case OFDM_SC_RA_RAM_CMD_USER_IO:
- case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM:
- status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0));
- /* All commands yielding 0 results */
- case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING:
- case OFDM_SC_RA_RAM_CMD_SET_TIMER:
- case OFDM_SC_RA_RAM_CMD_PROC_START:
- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM:
- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM:
- case OFDM_SC_RA_RAM_CMD_NULL:
- break;
- default:
- /* Unknown command */
- status = -EINVAL;
- break;
- } /* switch (cmd->cmd) */
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int PowerUpDVBT(struct drxk_state *state)
-{
- enum DRXPowerMode powerMode = DRX_POWER_UP;
- int status;
-
- dprintk(1, "\n");
- status = CtrlPowerMode(state, &powerMode);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled)
-{
- int status;
-
- dprintk(1, "\n");
- if (*enabled == true)
- status = write16(state, IQM_CF_BYPASSDET__A, 0);
- else
- status = write16(state, IQM_CF_BYPASSDET__A, 1);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-#define DEFAULT_FR_THRES_8K 4000
-static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled)
-{
-
- int status;
-
- dprintk(1, "\n");
- if (*enabled == true) {
- /* write mask to 1 */
- status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A,
- DEFAULT_FR_THRES_8K);
- } else {
- /* write mask to 0 */
- status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0);
- }
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int DVBTCtrlSetEchoThreshold(struct drxk_state *state,
- struct DRXKCfgDvbtEchoThres_t *echoThres)
-{
- u16 data = 0;
- int status;
-
- dprintk(1, "\n");
- status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data);
- if (status < 0)
- goto error;
-
- switch (echoThres->fftMode) {
- case DRX_FFTMODE_2K:
- data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M;
- data |= ((echoThres->threshold <<
- OFDM_SC_RA_RAM_ECHO_THRES_2K__B)
- & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M));
- break;
- case DRX_FFTMODE_8K:
- data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M;
- data |= ((echoThres->threshold <<
- OFDM_SC_RA_RAM_ECHO_THRES_8K__B)
- & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M));
- break;
- default:
- return -EINVAL;
- }
-
- status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int DVBTCtrlSetSqiSpeed(struct drxk_state *state,
- enum DRXKCfgDvbtSqiSpeed *speed)
-{
- int status = -EINVAL;
-
- dprintk(1, "\n");
-
- switch (*speed) {
- case DRXK_DVBT_SQI_SPEED_FAST:
- case DRXK_DVBT_SQI_SPEED_MEDIUM:
- case DRXK_DVBT_SQI_SPEED_SLOW:
- break;
- default:
- goto error;
- }
- status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A,
- (u16) *speed);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief Activate DVBT specific presets
-* \param demod instance of demodulator.
-* \return DRXStatus_t.
-*
-* Called in DVBTSetStandard
-*
-*/
-static int DVBTActivatePresets(struct drxk_state *state)
-{
- int status;
- bool setincenable = false;
- bool setfrenable = true;
-
- struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K };
- struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K };
-
- dprintk(1, "\n");
- status = DVBTCtrlSetIncEnable(state, &setincenable);
- if (status < 0)
- goto error;
- status = DVBTCtrlSetFrEnable(state, &setfrenable);
- if (status < 0)
- goto error;
- status = DVBTCtrlSetEchoThreshold(state, &echoThres2k);
- if (status < 0)
- goto error;
- status = DVBTCtrlSetEchoThreshold(state, &echoThres8k);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief Initialize channelswitch-independent settings for DVBT.
-* \param demod instance of demodulator.
-* \return DRXStatus_t.
-*
-* For ROM code channel filter taps are loaded from the bootloader. For microcode
-* the DVB-T taps from the drxk_filters.h are used.
-*/
-static int SetDVBTStandard(struct drxk_state *state,
- enum OperationMode oMode)
-{
- u16 cmdResult = 0;
- u16 data = 0;
- int status;
-
- dprintk(1, "\n");
-
- PowerUpDVBT(state);
- /* added antenna switch */
- SwitchAntennaToDVBT(state);
- /* send OFDM reset command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- /* send OFDM setenv command */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- /* reset datapath for OFDM, processors first */
- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
- if (status < 0)
- goto error;
-
- /* IQM setup */
- /* synchronize on ofdstate->m_festart */
- status = write16(state, IQM_AF_UPD_SEL__A, 1);
- if (status < 0)
- goto error;
- /* window size for clipping ADC detection */
- status = write16(state, IQM_AF_CLP_LEN__A, 0);
- if (status < 0)
- goto error;
- /* window size for for sense pre-SAW detection */
- status = write16(state, IQM_AF_SNS_LEN__A, 0);
- if (status < 0)
- goto error;
- /* sense threshold for sense pre-SAW detection */
- status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
- if (status < 0)
- goto error;
- status = SetIqmAf(state, true);
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_AF_AGC_RF__A, 0);
- if (status < 0)
- goto error;
-
- /* Impulse noise cruncher setup */
- status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_RC_STRETCH__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_SCALE__A, 1600);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_SCALE_SH__A, 0);
- if (status < 0)
- goto error;
-
- /* virtual clipping threshold for clipping ADC detection */
- status = write16(state, IQM_AF_CLP_TH__A, 448);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */
- if (status < 0)
- goto error;
-
- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2);
- if (status < 0)
- goto error;
- /* enable power measurement interrupt */
- status = write16(state, IQM_CF_COMM_INT_MSK__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
- if (status < 0)
- goto error;
-
- /* IQM will not be reset from here, sync ADC and update/init AGC */
- status = ADCSynchronization(state);
- if (status < 0)
- goto error;
- status = SetPreSaw(state, &state->m_dvbtPreSawCfg);
- if (status < 0)
- goto error;
-
- /* Halt SCU to enable safe non-atomic accesses */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
- if (status < 0)
- goto error;
-
- status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true);
- if (status < 0)
- goto error;
- status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true);
- if (status < 0)
- goto error;
-
- /* Set Noise Estimation notch width and enable DC fix */
- status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data);
- if (status < 0)
- goto error;
- data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M;
- status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data);
- if (status < 0)
- goto error;
-
- /* Activate SCU to enable SCU commands */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
-
- if (!state->m_DRXK_A3_ROM_CODE) {
- /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */
- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay);
- if (status < 0)
- goto error;
- }
-
- /* OFDM_SC setup */
-#ifdef COMPILE_FOR_NONRT
- status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2);
- if (status < 0)
- goto error;
-#endif
-
- /* FEC setup */
- status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */
- if (status < 0)
- goto error;
-
-
-#ifdef COMPILE_FOR_NONRT
- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400);
- if (status < 0)
- goto error;
-#else
- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000);
- if (status < 0)
- goto error;
-#endif
- status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001);
- if (status < 0)
- goto error;
-
- /* Setup MPEG bus */
- status = MPEGTSDtoSetup(state, OM_DVBT);
- if (status < 0)
- goto error;
- /* Set DVBT Presets */
- status = DVBTActivatePresets(state);
- if (status < 0)
- goto error;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-/**
-* \brief Start dvbt demodulating for channel.
-* \param demod instance of demodulator.
-* \return DRXStatus_t.
-*/
-static int DVBTStart(struct drxk_state *state)
-{
- u16 param1;
- int status;
- /* DRXKOfdmScCmd_t scCmd; */
-
- dprintk(1, "\n");
- /* Start correct processes to get in lock */
- /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */
- param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN;
- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0);
- if (status < 0)
- goto error;
- /* Start FEC OC */
- status = MPEGTSStart(state);
- if (status < 0)
- goto error;
- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-
-/*============================================================================*/
-
-/**
-* \brief Set up dvbt demodulator for channel.
-* \param demod instance of demodulator.
-* \return DRXStatus_t.
-* // original DVBTSetChannel()
-*/
-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset)
-{
- u16 cmdResult = 0;
- u16 transmissionParams = 0;
- u16 operationMode = 0;
- u32 iqmRcRateOfs = 0;
- u32 bandwidth = 0;
- u16 param1;
- int status;
-
- dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset);
-
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- /* Halt SCU to enable safe non-atomic accesses */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
- if (status < 0)
- goto error;
-
- /* Stop processors */
- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
-
- /* Mandatory fix, always stop CP, required to set spl offset back to
- hardware default (is set to 0 by ucode during pilot detection */
- status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
-
- /*== Write channel settings to device =====================================*/
-
- /* mode */
- switch (state->props.transmission_mode) {
- case TRANSMISSION_MODE_AUTO:
- default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M;
- /* fall through , try first guess DRX_FFTMODE_8K */
- case TRANSMISSION_MODE_8K:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K;
- break;
- case TRANSMISSION_MODE_2K:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K;
- break;
- }
-
- /* guard */
- switch (state->props.guard_interval) {
- default:
- case GUARD_INTERVAL_AUTO:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M;
- /* fall through , try first guess DRX_GUARD_1DIV4 */
- case GUARD_INTERVAL_1_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4;
- break;
- case GUARD_INTERVAL_1_32:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32;
- break;
- case GUARD_INTERVAL_1_16:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16;
- break;
- case GUARD_INTERVAL_1_8:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8;
- break;
- }
-
- /* hierarchy */
- switch (state->props.hierarchy) {
- case HIERARCHY_AUTO:
- case HIERARCHY_NONE:
- default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M;
- /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */
- /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */
- /* break; */
- case HIERARCHY_1:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1;
- break;
- case HIERARCHY_2:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2;
- break;
- case HIERARCHY_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4;
- break;
- }
-
-
- /* modulation */
- switch (state->props.modulation) {
- case QAM_AUTO:
- default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M;
- /* fall through , try first guess DRX_CONSTELLATION_QAM64 */
- case QAM_64:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64;
- break;
- case QPSK:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK;
- break;
- case QAM_16:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16;
- break;
- }
-#if 0
- /* No hierachical channels support in BDA */
- /* Priority (only for hierarchical channels) */
- switch (channel->priority) {
- case DRX_PRIORITY_LOW:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO;
- WR16(devAddr, OFDM_EC_SB_PRIOR__A,
- OFDM_EC_SB_PRIOR_LO);
- break;
- case DRX_PRIORITY_HIGH:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
- WR16(devAddr, OFDM_EC_SB_PRIOR__A,
- OFDM_EC_SB_PRIOR_HI));
- break;
- case DRX_PRIORITY_UNKNOWN: /* fall through */
- default:
- status = -EINVAL;
- goto error;
- }
-#else
- /* Set Priorty high */
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI;
- status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI);
- if (status < 0)
- goto error;
-#endif
-
- /* coderate */
- switch (state->props.code_rate_HP) {
- case FEC_AUTO:
- default:
- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M;
- /* fall through , try first guess DRX_CODERATE_2DIV3 */
- case FEC_2_3:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3;
- break;
- case FEC_1_2:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2;
- break;
- case FEC_3_4:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4;
- break;
- case FEC_5_6:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6;
- break;
- case FEC_7_8:
- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8;
- break;
- }
-
- /* SAW filter selection: normaly not necesarry, but if wanted
- the application can select a SAW filter via the driver by using UIOs */
- /* First determine real bandwidth (Hz) */
- /* Also set delay for impulse noise cruncher */
- /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed
- by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC
- functions */
- switch (state->props.bandwidth_hz) {
- case 0:
- state->props.bandwidth_hz = 8000000;
- /* fall though */
- case 8000000:
- bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052);
- if (status < 0)
- goto error;
- /* cochannel protection for PAL 8 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
- if (status < 0)
- goto error;
- break;
- case 7000000:
- bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491);
- if (status < 0)
- goto error;
- /* cochannel protection for PAL 7 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
- if (status < 0)
- goto error;
- break;
- case 6000000:
- bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ;
- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073);
- if (status < 0)
- goto error;
- /* cochannel protection for NTSC 6 MHz */
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1);
- if (status < 0)
- goto error;
- break;
- default:
- status = -EINVAL;
- goto error;
- }
-
- if (iqmRcRateOfs == 0) {
- /* Now compute IQM_RC_RATE_OFS
- (((SysFreq/BandWidth)/2)/2) -1) * 2^23)
- =>
- ((SysFreq / BandWidth) * (2^21)) - (2^23)
- */
- /* (SysFreq / BandWidth) * (2^28) */
- /* assert (MAX(sysClk)/MIN(bandwidth) < 16)
- => assert(MAX(sysClk) < 16*MIN(bandwidth))
- => assert(109714272 > 48000000) = true so Frac 28 can be used */
- iqmRcRateOfs = Frac28a((u32)
- ((state->m_sysClockFreq *
- 1000) / 3), bandwidth);
- /* (SysFreq / BandWidth) * (2^21), rounding before truncating */
- if ((iqmRcRateOfs & 0x7fL) >= 0x40)
- iqmRcRateOfs += 0x80L;
- iqmRcRateOfs = iqmRcRateOfs >> 7;
- /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */
- iqmRcRateOfs = iqmRcRateOfs - (1 << 23);
- }
-
- iqmRcRateOfs &=
- ((((u32) IQM_RC_RATE_OFS_HI__M) <<
- IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M);
- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs);
- if (status < 0)
- goto error;
-
- /* Bandwidth setting done */
-
-#if 0
- status = DVBTSetFrequencyShift(demod, channel, tunerOffset);
- if (status < 0)
- goto error;
-#endif
- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
- if (status < 0)
- goto error;
-
- /*== Start SC, write channel settings to SC ===============================*/
-
- /* Activate SCU to enable SCU commands */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
-
- /* Enable SC after setting all other parameters */
- status = write16(state, OFDM_SC_COMM_STATE__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, OFDM_SC_COMM_EXEC__A, 1);
- if (status < 0)
- goto error;
-
-
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- /* Write SC parameter registers, set all AUTO flags in operation mode */
- param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M |
- OFDM_SC_RA_RAM_OP_AUTO_GUARD__M |
- OFDM_SC_RA_RAM_OP_AUTO_CONST__M |
- OFDM_SC_RA_RAM_OP_AUTO_HIER__M |
- OFDM_SC_RA_RAM_OP_AUTO_RATE__M);
- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM,
- 0, transmissionParams, param1, 0, 0, 0);
- if (status < 0)
- goto error;
-
- if (!state->m_DRXK_A3_ROM_CODE)
- status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-
-/*============================================================================*/
-
-/**
-* \brief Retreive lock status .
-* \param demod Pointer to demodulator instance.
-* \param lockStat Pointer to lock status structure.
-* \return DRXStatus_t.
-*
-*/
-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus)
-{
- int status;
- const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M |
- OFDM_SC_RA_RAM_LOCK_FEC__M);
- const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M);
- const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M;
-
- u16 ScRaRamLock = 0;
- u16 ScCommExec = 0;
-
- dprintk(1, "\n");
-
- *pLockStatus = NOT_LOCKED;
- /* driver 0.9.0 */
- /* Check if SC is running */
- status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec);
- if (status < 0)
- goto end;
- if (ScCommExec == OFDM_SC_COMM_EXEC_STOP)
- goto end;
-
- status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock);
- if (status < 0)
- goto end;
-
- if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask)
- *pLockStatus = MPEG_LOCK;
- else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask)
- *pLockStatus = FEC_LOCK;
- else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask)
- *pLockStatus = DEMOD_LOCK;
- else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M)
- *pLockStatus = NEVER_LOCK;
-end:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int PowerUpQAM(struct drxk_state *state)
-{
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
- int status;
-
- dprintk(1, "\n");
- status = CtrlPowerMode(state, &powerMode);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-
-/** Power Down QAM */
-static int PowerDownQAM(struct drxk_state *state)
-{
- u16 data = 0;
- u16 cmdResult;
- int status = 0;
-
- dprintk(1, "\n");
- status = read16(state, SCU_COMM_EXEC__A, &data);
- if (status < 0)
- goto error;
- if (data == SCU_COMM_EXEC_ACTIVE) {
- /*
- STOP demodulator
- QAM and HW blocks
- */
- /* stop all comstate->m_exec */
- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
- }
- /* powerdown AFE */
- status = SetIqmAf(state, false);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief Setup of the QAM Measurement intervals for signal quality
-* \param demod instance of demod.
-* \param modulation current modulation.
-* \return DRXStatus_t.
-*
-* NOTE:
-* Take into account that for certain settings the errorcounters can overflow.
-* The implementation does not check this.
-*
-*/
-static int SetQAMMeasurement(struct drxk_state *state,
- enum EDrxkConstellation modulation,
- u32 symbolRate)
-{
- u32 fecBitsDesired = 0; /* BER accounting period */
- u32 fecRsPeriodTotal = 0; /* Total period */
- u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */
- u16 fecRsPeriod = 0; /* Value for corresponding I2C register */
- int status = 0;
-
- dprintk(1, "\n");
-
- fecRsPrescale = 1;
- /* fecBitsDesired = symbolRate [kHz] *
- FrameLenght [ms] *
- (modulation + 1) *
- SyncLoss (== 1) *
- ViterbiLoss (==1)
- */
- switch (modulation) {
- case DRX_CONSTELLATION_QAM16:
- fecBitsDesired = 4 * symbolRate;
- break;
- case DRX_CONSTELLATION_QAM32:
- fecBitsDesired = 5 * symbolRate;
- break;
- case DRX_CONSTELLATION_QAM64:
- fecBitsDesired = 6 * symbolRate;
- break;
- case DRX_CONSTELLATION_QAM128:
- fecBitsDesired = 7 * symbolRate;
- break;
- case DRX_CONSTELLATION_QAM256:
- fecBitsDesired = 8 * symbolRate;
- break;
- default:
- status = -EINVAL;
- }
- if (status < 0)
- goto error;
-
- fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */
- fecBitsDesired *= 500; /* meas. period [ms] */
-
- /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */
- /* fecRsPeriodTotal = fecBitsDesired / 1632 */
- fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */
-
- /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */
- fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16);
- if (fecRsPrescale == 0) {
- /* Divide by zero (though impossible) */
- status = -EINVAL;
- if (status < 0)
- goto error;
- }
- fecRsPeriod =
- ((u16) fecRsPeriodTotal +
- (fecRsPrescale >> 1)) / fecRsPrescale;
-
- /* write corresponding registers */
- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod);
- if (status < 0)
- goto error;
- status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale);
- if (status < 0)
- goto error;
- status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SetQAM16(struct drxk_state *state)
-{
- int status = 0;
-
- dprintk(1, "\n");
- /* QAM Equalizer Setup */
- /* Equalizer */
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517);
- if (status < 0)
- goto error;
- /* Decision Feedback Equalizer */
- status = write16(state, QAM_DQ_QUAL_FUN0__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN1__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN2__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN3__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN4__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_SY_SYNC_HWM__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_AWM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_LWM__A, 3);
- if (status < 0)
- goto error;
-
- /* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16);
- if (status < 0)
- goto error;
-
- /* QAM Loop Controller Coeficients */
- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
- if (status < 0)
- goto error;
-
-
- /* QAM State Machine (FSM) Thresholds */
-
- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24);
- if (status < 0)
- goto error;
-
-
- /* QAM FSM Tracking Parameters */
-
- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127);
- if (status < 0)
- goto error;
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief QAM32 specific setup
-* \param demod instance of demod.
-* \return DRXStatus_t.
-*/
-static int SetQAM32(struct drxk_state *state)
-{
- int status = 0;
-
- dprintk(1, "\n");
-
- /* QAM Equalizer Setup */
- /* Equalizer */
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707);
- if (status < 0)
- goto error;
-
- /* Decision Feedback Equalizer */
- status = write16(state, QAM_DQ_QUAL_FUN0__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN1__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN2__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN3__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_SY_SYNC_HWM__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_AWM__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_LWM__A, 3);
- if (status < 0)
- goto error;
-
- /* QAM Slicer Settings */
-
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32);
- if (status < 0)
- goto error;
-
-
- /* QAM Loop Controller Coeficients */
-
- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
- if (status < 0)
- goto error;
-
-
- /* QAM State Machine (FSM) Thresholds */
-
- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10);
- if (status < 0)
- goto error;
-
-
- /* QAM FSM Tracking Parameters */
-
- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief QAM64 specific setup
-* \param demod instance of demod.
-* \return DRXStatus_t.
-*/
-static int SetQAM64(struct drxk_state *state)
-{
- int status = 0;
-
- dprintk(1, "\n");
- /* QAM Equalizer Setup */
- /* Equalizer */
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609);
- if (status < 0)
- goto error;
-
- /* Decision Feedback Equalizer */
- status = write16(state, QAM_DQ_QUAL_FUN0__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN1__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN2__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN3__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN4__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_SY_SYNC_HWM__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_AWM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_LWM__A, 3);
- if (status < 0)
- goto error;
-
- /* QAM Slicer Settings */
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64);
- if (status < 0)
- goto error;
-
-
- /* QAM Loop Controller Coeficients */
-
- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
- if (status < 0)
- goto error;
-
-
- /* QAM State Machine (FSM) Thresholds */
-
- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15);
- if (status < 0)
- goto error;
-
-
- /* QAM FSM Tracking Parameters */
-
- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief QAM128 specific setup
-* \param demod: instance of demod.
-* \return DRXStatus_t.
-*/
-static int SetQAM128(struct drxk_state *state)
-{
- int status = 0;
-
- dprintk(1, "\n");
- /* QAM Equalizer Setup */
- /* Equalizer */
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238);
- if (status < 0)
- goto error;
-
- /* Decision Feedback Equalizer */
- status = write16(state, QAM_DQ_QUAL_FUN0__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN1__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN2__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN3__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN4__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_SY_SYNC_HWM__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_AWM__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_LWM__A, 3);
- if (status < 0)
- goto error;
-
-
- /* QAM Slicer Settings */
-
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128);
- if (status < 0)
- goto error;
-
-
- /* QAM Loop Controller Coeficients */
-
- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0);
- if (status < 0)
- goto error;
-
-
- /* QAM State Machine (FSM) Thresholds */
-
- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
- if (status < 0)
- goto error;
-
- /* QAM FSM Tracking Parameters */
-
- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief QAM256 specific setup
-* \param demod: instance of demod.
-* \return DRXStatus_t.
-*/
-static int SetQAM256(struct drxk_state *state)
-{
- int status = 0;
-
- dprintk(1, "\n");
- /* QAM Equalizer Setup */
- /* Equalizer */
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385);
- if (status < 0)
- goto error;
-
- /* Decision Feedback Equalizer */
- status = write16(state, QAM_DQ_QUAL_FUN0__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN1__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN2__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN3__A, 8);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN4__A, 6);
- if (status < 0)
- goto error;
- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_SY_SYNC_HWM__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_AWM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_SYNC_LWM__A, 3);
- if (status < 0)
- goto error;
-
- /* QAM Slicer Settings */
-
- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256);
- if (status < 0)
- goto error;
-
-
- /* QAM Loop Controller Coeficients */
-
- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10);
- if (status < 0)
- goto error;
-
-
- /* QAM State Machine (FSM) Thresholds */
-
- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110);
- if (status < 0)
- goto error;
-
- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12);
- if (status < 0)
- goto error;
-
-
- /* QAM FSM Tracking Parameters */
-
- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0);
- if (status < 0)
- goto error;
- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-
-/*============================================================================*/
-/**
-* \brief Reset QAM block.
-* \param demod: instance of demod.
-* \param channel: pointer to channel data.
-* \return DRXStatus_t.
-*/
-static int QAMResetQAM(struct drxk_state *state)
-{
- int status;
- u16 cmdResult;
-
- dprintk(1, "\n");
- /* Stop QAM comstate->m_exec */
- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
-
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief Set QAM symbolrate.
-* \param demod: instance of demod.
-* \param channel: pointer to channel data.
-* \return DRXStatus_t.
-*/
-static int QAMSetSymbolrate(struct drxk_state *state)
-{
- u32 adcFrequency = 0;
- u32 symbFreq = 0;
- u32 iqmRcRate = 0;
- u16 ratesel = 0;
- u32 lcSymbRate = 0;
- int status;
-
- dprintk(1, "\n");
- /* Select & calculate correct IQM rate */
- adcFrequency = (state->m_sysClockFreq * 1000) / 3;
- ratesel = 0;
- /* printk(KERN_DEBUG "drxk: SR %d\n", state->props.symbol_rate); */
- if (state->props.symbol_rate <= 1188750)
- ratesel = 3;
- else if (state->props.symbol_rate <= 2377500)
- ratesel = 2;
- else if (state->props.symbol_rate <= 4755000)
- ratesel = 1;
- status = write16(state, IQM_FD_RATESEL__A, ratesel);
- if (status < 0)
- goto error;
-
- /*
- IqmRcRate = ((Fadc / (symbolrate * (4<<ratesel))) - 1) * (1<<23)
- */
- symbFreq = state->props.symbol_rate * (1 << ratesel);
- if (symbFreq == 0) {
- /* Divide by zero */
- status = -EINVAL;
- goto error;
- }
- iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) +
- (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) -
- (1 << 23);
- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate);
- if (status < 0)
- goto error;
- state->m_iqmRcRate = iqmRcRate;
- /*
- LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15))
- */
- symbFreq = state->props.symbol_rate;
- if (adcFrequency == 0) {
- /* Divide by zero */
- status = -EINVAL;
- goto error;
- }
- lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) +
- (Frac28a((symbFreq % adcFrequency), adcFrequency) >>
- 16);
- if (lcSymbRate > 511)
- lcSymbRate = 511;
- status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate);
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-/*============================================================================*/
-
-/**
-* \brief Get QAM lock status.
-* \param demod: instance of demod.
-* \param channel: pointer to channel data.
-* \return DRXStatus_t.
-*/
-
-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus)
-{
- int status;
- u16 Result[2] = { 0, 0 };
-
- dprintk(1, "\n");
- *pLockStatus = NOT_LOCKED;
- status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM |
- SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2,
- Result);
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) {
- /* 0x0000 NOT LOCKED */
- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) {
- /* 0x4000 DEMOD LOCKED */
- *pLockStatus = DEMOD_LOCK;
- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) {
- /* 0x8000 DEMOD + FEC LOCKED (system lock) */
- *pLockStatus = MPEG_LOCK;
- } else {
- /* 0xC000 NEVER LOCKED */
- /* (system will never be able to lock to the signal) */
- /* TODO: check this, intermediate & standard specific lock states are not
- taken into account here */
- *pLockStatus = NEVER_LOCK;
- }
- return status;
-}
-
-#define QAM_MIRROR__M 0x03
-#define QAM_MIRROR_NORMAL 0x00
-#define QAM_MIRRORED 0x01
-#define QAM_MIRROR_AUTO_ON 0x02
-#define QAM_LOCKRANGE__M 0x10
-#define QAM_LOCKRANGE_NORMAL 0x10
-
-static int QAMDemodulatorCommand(struct drxk_state *state,
- int numberOfParameters)
-{
- int status;
- u16 cmdResult;
- u16 setParamParameters[4] = { 0, 0, 0, 0 };
-
- setParamParameters[0] = state->m_Constellation; /* modulation */
- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */
-
- if (numberOfParameters == 2) {
- u16 setEnvParameters[1] = { 0 };
-
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setEnvParameters[0] = QAM_TOP_ANNEX_C;
- else
- setEnvParameters[0] = QAM_TOP_ANNEX_A;
-
- status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV,
- 1, setEnvParameters, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
- numberOfParameters, setParamParameters,
- 1, &cmdResult);
- } else if (numberOfParameters == 4) {
- if (state->m_OperationMode == OM_QAM_ITU_C)
- setParamParameters[2] = QAM_TOP_ANNEX_C;
- else
- setParamParameters[2] = QAM_TOP_ANNEX_A;
-
- setParamParameters[3] |= (QAM_MIRROR_AUTO_ON);
- /* Env parameters */
- /* check for LOCKRANGE Extented */
- /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */
-
- status = scu_command(state,
- SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM,
- numberOfParameters, setParamParameters,
- 1, &cmdResult);
- } else {
- printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter "
- "count %d\n", numberOfParameters);
- }
-
-error:
- if (status < 0)
- printk(KERN_WARNING "drxk: Warning %d on %s\n",
- status, __func__);
- return status;
-}
-
-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz,
- s32 tunerFreqOffset)
-{
- int status;
- u16 cmdResult;
- int qamDemodParamCount = state->qam_demod_parameter_count;
-
- dprintk(1, "\n");
- /*
- * STEP 1: reset demodulator
- * resets FEC DI and FEC RS
- * resets QAM block
- * resets SCU variables
- */
- status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = QAMResetQAM(state);
- if (status < 0)
- goto error;
-
- /*
- * STEP 2: configure demodulator
- * -set params; resets IQM,QAM,FEC HW; initializes some
- * SCU variables
- */
- status = QAMSetSymbolrate(state);
- if (status < 0)
- goto error;
-
- /* Set params */
- switch (state->props.modulation) {
- case QAM_256:
- state->m_Constellation = DRX_CONSTELLATION_QAM256;
- break;
- case QAM_AUTO:
- case QAM_64:
- state->m_Constellation = DRX_CONSTELLATION_QAM64;
- break;
- case QAM_16:
- state->m_Constellation = DRX_CONSTELLATION_QAM16;
- break;
- case QAM_32:
- state->m_Constellation = DRX_CONSTELLATION_QAM32;
- break;
- case QAM_128:
- state->m_Constellation = DRX_CONSTELLATION_QAM128;
- break;
- default:
- status = -EINVAL;
- break;
- }
- if (status < 0)
- goto error;
-
- /* Use the 4-parameter if it's requested or we're probing for
- * the correct command. */
- if (state->qam_demod_parameter_count == 4
- || !state->qam_demod_parameter_count) {
- qamDemodParamCount = 4;
- status = QAMDemodulatorCommand(state, qamDemodParamCount);
- }
-
- /* Use the 2-parameter command if it was requested or if we're
- * probing for the correct command and the 4-parameter command
- * failed. */
- if (state->qam_demod_parameter_count == 2
- || (!state->qam_demod_parameter_count && status < 0)) {
- qamDemodParamCount = 2;
- status = QAMDemodulatorCommand(state, qamDemodParamCount);
- }
-
- if (status < 0) {
- dprintk(1, "Could not set demodulator parameters. Make "
- "sure qam_demod_parameter_count (%d) is correct for "
- "your firmware (%s).\n",
- state->qam_demod_parameter_count,
- state->microcode_name);
- goto error;
- } else if (!state->qam_demod_parameter_count) {
- dprintk(1, "Auto-probing the correct QAM demodulator command "
- "parameters was successful - using %d parameters.\n",
- qamDemodParamCount);
-
- /*
- * One of our commands was successful. We don't need to
- * auto-probe anymore, now that we got the correct command.
- */
- state->qam_demod_parameter_count = qamDemodParamCount;
- }
-
- /*
- * STEP 3: enable the system in a mode where the ADC provides valid
- * signal setup modulation independent registers
- */
-#if 0
- status = SetFrequency(channel, tunerFreqOffset));
- if (status < 0)
- goto error;
-#endif
- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true);
- if (status < 0)
- goto error;
-
- /* Setup BER measurement */
- status = SetQAMMeasurement(state, state->m_Constellation, state->props.symbol_rate);
- if (status < 0)
- goto error;
-
- /* Reset default values */
- status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE);
- if (status < 0)
- goto error;
- status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE);
- if (status < 0)
- goto error;
-
- /* Reset default LC values */
- status = write16(state, QAM_LC_RATE_LIMIT__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_LPF_FACTORP__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_LPF_FACTORI__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_MODE__A, 7);
- if (status < 0)
- goto error;
-
- status = write16(state, QAM_LC_QUAL_TAB0__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB1__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB2__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB3__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB4__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB5__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB6__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB8__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB9__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB10__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB12__A, 2);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB15__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB16__A, 3);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB20__A, 4);
- if (status < 0)
- goto error;
- status = write16(state, QAM_LC_QUAL_TAB25__A, 4);
- if (status < 0)
- goto error;
-
- /* Mirroring, QAM-block starting point not inverted */
- status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS);
- if (status < 0)
- goto error;
-
- /* Halt SCU to enable safe non-atomic accesses */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
- if (status < 0)
- goto error;
-
- /* STEP 4: modulation specific setup */
- switch (state->props.modulation) {
- case QAM_16:
- status = SetQAM16(state);
- break;
- case QAM_32:
- status = SetQAM32(state);
- break;
- case QAM_AUTO:
- case QAM_64:
- status = SetQAM64(state);
- break;
- case QAM_128:
- status = SetQAM128(state);
- break;
- case QAM_256:
- status = SetQAM256(state);
- break;
- default:
- status = -EINVAL;
- break;
- }
- if (status < 0)
- goto error;
-
- /* Activate SCU to enable SCU commands */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
-
- /* Re-configure MPEG output, requires knowledge of channel bitrate */
- /* extAttr->currentChannel.modulation = channel->modulation; */
- /* extAttr->currentChannel.symbolrate = channel->symbolrate; */
- status = MPEGTSDtoSetup(state, state->m_OperationMode);
- if (status < 0)
- goto error;
-
- /* Start processes */
- status = MPEGTSStart(state);
- if (status < 0)
- goto error;
- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE);
- if (status < 0)
- goto error;
-
- /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */
- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult);
- if (status < 0)
- goto error;
-
- /* update global DRXK data container */
-/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */
-
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SetQAMStandard(struct drxk_state *state,
- enum OperationMode oMode)
-{
- int status;
-#ifdef DRXK_QAM_TAPS
-#define DRXK_QAMA_TAPS_SELECT
-#include "drxk_filters.h"
-#undef DRXK_QAMA_TAPS_SELECT
-#endif
-
- dprintk(1, "\n");
-
- /* added antenna switch */
- SwitchAntennaToQAM(state);
-
- /* Ensure correct power-up mode */
- status = PowerUpQAM(state);
- if (status < 0)
- goto error;
- /* Reset QAM block */
- status = QAMResetQAM(state);
- if (status < 0)
- goto error;
-
- /* Setup IQM */
-
- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC);
- if (status < 0)
- goto error;
-
- /* Upload IQM Channel Filter settings by
- boot loader from ROM table */
- switch (oMode) {
- case OM_QAM_ITU_A:
- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
- break;
- case OM_QAM_ITU_C:
- status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
- if (status < 0)
- goto error;
- status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT);
- break;
- default:
- status = -EINVAL;
- }
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B));
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_SYMMETRIC__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B)));
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_RC_STRETCH__A, 21);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_CLP_LEN__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_CLP_TH__A, 448);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_SNS_LEN__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0);
- if (status < 0)
- goto error;
-
- status = write16(state, IQM_FS_ADJ_SEL__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_RC_ADJ_SEL__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_ADJ_SEL__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_UPD_SEL__A, 0);
- if (status < 0)
- goto error;
-
- /* IQM Impulse Noise Processing Unit */
- status = write16(state, IQM_CF_CLP_VAL__A, 500);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_DATATH__A, 1000);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_BYPASSDET__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_DET_LCT__A, 0);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_WND_LEN__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_CF_PKDTH__A, 1);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_INC_BYPASS__A, 1);
- if (status < 0)
- goto error;
-
- /* turn on IQMAF. Must be done before setAgc**() */
- status = SetIqmAf(state, true);
- if (status < 0)
- goto error;
- status = write16(state, IQM_AF_START_LOCK__A, 0x01);
- if (status < 0)
- goto error;
-
- /* IQM will not be reset from here, sync ADC and update/init AGC */
- status = ADCSynchronization(state);
- if (status < 0)
- goto error;
-
- /* Set the FSM step period */
- status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000);
- if (status < 0)
- goto error;
-
- /* Halt SCU to enable safe non-atomic accesses */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD);
- if (status < 0)
- goto error;
-
- /* No more resets of the IQM, current standard correctly set =>
- now AGCs can be configured. */
-
- status = InitAGC(state, true);
- if (status < 0)
- goto error;
- status = SetPreSaw(state, &(state->m_qamPreSawCfg));
- if (status < 0)
- goto error;
-
- /* Configure AGC's */
- status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true);
- if (status < 0)
- goto error;
- status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true);
- if (status < 0)
- goto error;
-
- /* Activate SCU to enable SCU commands */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int WriteGPIO(struct drxk_state *state)
-{
- int status;
- u16 value = 0;
-
- dprintk(1, "\n");
- /* stop lock indicator process */
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
-
- /* Write magic word to enable pdr reg write */
- status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY);
- if (status < 0)
- goto error;
-
- if (state->m_hasSAWSW) {
- if (state->UIO_mask & 0x0001) { /* UIO-1 */
- /* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
- if (status < 0)
- goto error;
-
- /* use corresponding bit in io data output registar */
- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
- if (status < 0)
- goto error;
- if ((state->m_GPIO & 0x0001) == 0)
- value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */
- else
- value |= 0x8000; /* write one to 15th bit - 1st UIO */
- /* write back to io data output register */
- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
- if (status < 0)
- goto error;
- }
- if (state->UIO_mask & 0x0002) { /* UIO-2 */
- /* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg);
- if (status < 0)
- goto error;
-
- /* use corresponding bit in io data output registar */
- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
- if (status < 0)
- goto error;
- if ((state->m_GPIO & 0x0002) == 0)
- value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */
- else
- value |= 0x4000; /* write one to 14th bit - 2st UIO */
- /* write back to io data output register */
- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
- if (status < 0)
- goto error;
- }
- if (state->UIO_mask & 0x0004) { /* UIO-3 */
- /* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg);
- if (status < 0)
- goto error;
-
- /* use corresponding bit in io data output registar */
- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value);
- if (status < 0)
- goto error;
- if ((state->m_GPIO & 0x0004) == 0)
- value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */
- else
- value |= 0x0004; /* write one to 2nd bit - 3rd UIO */
- /* write back to io data output register */
- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value);
- if (status < 0)
- goto error;
- }
- }
- /* Write magic word to disable pdr reg write */
- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SwitchAntennaToQAM(struct drxk_state *state)
-{
- int status = 0;
- bool gpio_state;
-
- dprintk(1, "\n");
-
- if (!state->antenna_gpio)
- return 0;
-
- gpio_state = state->m_GPIO & state->antenna_gpio;
-
- if (state->antenna_dvbt ^ gpio_state) {
- /* Antenna is on DVB-T mode. Switch */
- if (state->antenna_dvbt)
- state->m_GPIO &= ~state->antenna_gpio;
- else
- state->m_GPIO |= state->antenna_gpio;
- status = WriteGPIO(state);
- }
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-static int SwitchAntennaToDVBT(struct drxk_state *state)
-{
- int status = 0;
- bool gpio_state;
-
- dprintk(1, "\n");
-
- if (!state->antenna_gpio)
- return 0;
-
- gpio_state = state->m_GPIO & state->antenna_gpio;
-
- if (!(state->antenna_dvbt ^ gpio_state)) {
- /* Antenna is on DVB-C mode. Switch */
- if (state->antenna_dvbt)
- state->m_GPIO |= state->antenna_gpio;
- else
- state->m_GPIO &= ~state->antenna_gpio;
- status = WriteGPIO(state);
- }
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- return status;
-}
-
-
-static int PowerDownDevice(struct drxk_state *state)
-{
- /* Power down to requested mode */
- /* Backup some register settings */
- /* Set pins with possible pull-ups connected to them in input mode */
- /* Analog power down */
- /* ADC power down */
- /* Power down device */
- int status;
-
- dprintk(1, "\n");
- if (state->m_bPDownOpenBridge) {
- /* Open I2C bridge before power down of DRXK */
- status = ConfigureI2CBridge(state, true);
- if (status < 0)
- goto error;
- }
- /* driver 0.9.0 */
- status = DVBTEnableOFDMTokenRing(state, false);
- if (status < 0)
- goto error;
-
- status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK);
- if (status < 0)
- goto error;
- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
- if (status < 0)
- goto error;
- state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ;
- status = HI_CfgCommand(state);
-error:
- if (status < 0)
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
-
- return status;
-}
-
-static int init_drxk(struct drxk_state *state)
-{
- int status = 0, n = 0;
- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM;
- u16 driverVersion;
-
- dprintk(1, "\n");
- if ((state->m_DrxkState == DRXK_UNINITIALIZED)) {
- drxk_i2c_lock(state);
- status = PowerUpDevice(state);
- if (status < 0)
- goto error;
- status = DRXX_Open(state);
- if (status < 0)
- goto error;
- /* Soft reset of OFDM-, sys- and osc-clockdomain */
- status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M);
- if (status < 0)
- goto error;
- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY);
- if (status < 0)
- goto error;
- /* TODO is this needed, if yes how much delay in worst case scenario */
- msleep(1);
- state->m_DRXK_A3_PATCH_CODE = true;
- status = GetDeviceCapabilities(state);
- if (status < 0)
- goto error;
-
- /* Bridge delay, uses oscilator clock */
- /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */
- /* SDA brdige delay */
- state->m_HICfgBridgeDelay =
- (u16) ((state->m_oscClockFreq / 1000) *
- HI_I2C_BRIDGE_DELAY) / 1000;
- /* Clipping */
- if (state->m_HICfgBridgeDelay >
- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) {
- state->m_HICfgBridgeDelay =
- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M;
- }
- /* SCL bridge delay, same as SDA for now */
- state->m_HICfgBridgeDelay +=
- state->m_HICfgBridgeDelay <<
- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B;
-
- status = InitHI(state);
- if (status < 0)
- goto error;
- /* disable various processes */
-#if NOA1ROM
- if (!(state->m_DRXK_A1_ROM_CODE)
- && !(state->m_DRXK_A2_ROM_CODE))
-#endif
- {
- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE);
- if (status < 0)
- goto error;
- }
-
- /* disable MPEG port */
- status = MPEGTSDisable(state);
- if (status < 0)
- goto error;
-
- /* Stop AUD and SCU */
- status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
-
- /* enable token-ring bus through OFDM block for possible ucode upload */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON);
- if (status < 0)
- goto error;
-
- /* include boot loader section */
- status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
- status = BLChainCmd(state, 0, 6, 100);
- if (status < 0)
- goto error;
-
- if (state->fw) {
- status = DownloadMicrocode(state, state->fw->data,
- state->fw->size);
- if (status < 0)
- goto error;
- }
-
- /* disable token-ring bus through OFDM block for possible ucode upload */
- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF);
- if (status < 0)
- goto error;
-
- /* Run SCU for a little while to initialize microcode version numbers */
- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE);
- if (status < 0)
- goto error;
- status = DRXX_Open(state);
- if (status < 0)
- goto error;
- /* added for test */
- msleep(30);
-
- powerMode = DRXK_POWER_DOWN_OFDM;
- status = CtrlPowerMode(state, &powerMode);
- if (status < 0)
- goto error;
-
- /* Stamp driver version number in SCU data RAM in BCD code
- Done to enable field application engineers to retreive drxdriver version
- via I2C from SCU RAM.
- Not using SCU command interface for SCU register access since no
- microcode may be present.
- */
- driverVersion =
- (((DRXK_VERSION_MAJOR / 100) % 10) << 12) +
- (((DRXK_VERSION_MAJOR / 10) % 10) << 8) +
- ((DRXK_VERSION_MAJOR % 10) << 4) +
- (DRXK_VERSION_MINOR % 10);
- status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion);
- if (status < 0)
- goto error;
- driverVersion =
- (((DRXK_VERSION_PATCH / 1000) % 10) << 12) +
- (((DRXK_VERSION_PATCH / 100) % 10) << 8) +
- (((DRXK_VERSION_PATCH / 10) % 10) << 4) +
- (DRXK_VERSION_PATCH % 10);
- status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion);
- if (status < 0)
- goto error;
-
- printk(KERN_INFO "DRXK driver version %d.%d.%d\n",
- DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR,
- DRXK_VERSION_PATCH);
-
- /* Dirty fix of default values for ROM/PATCH microcode
- Dirty because this fix makes it impossible to setup suitable values
- before calling DRX_Open. This solution requires changes to RF AGC speed
- to be done via the CTRL function after calling DRX_Open */
-
- /* m_dvbtRfAgcCfg.speed = 3; */
-
- /* Reset driver debug flags to 0 */
- status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0);
- if (status < 0)
- goto error;
- /* driver 0.9.0 */
- /* Setup FEC OC:
- NOTE: No more full FEC resets allowed afterwards!! */
- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP);
- if (status < 0)
- goto error;
- /* MPEGTS functions are still the same */
- status = MPEGTSDtoInit(state);
- if (status < 0)
- goto error;
- status = MPEGTSStop(state);
- if (status < 0)
- goto error;
- status = MPEGTSConfigurePolarity(state);
- if (status < 0)
- goto error;
- status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput);
- if (status < 0)
- goto error;
- /* added: configure GPIO */
- status = WriteGPIO(state);
- if (status < 0)
- goto error;
-
- state->m_DrxkState = DRXK_STOPPED;
-
- if (state->m_bPowerDown) {
- status = PowerDownDevice(state);
- if (status < 0)
- goto error;
- state->m_DrxkState = DRXK_POWERED_DOWN;
- } else
- state->m_DrxkState = DRXK_STOPPED;
-
- /* Initialize the supported delivery systems */
- n = 0;
- if (state->m_hasDVBC) {
- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A;
- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C;
- strlcat(state->frontend.ops.info.name, " DVB-C",
- sizeof(state->frontend.ops.info.name));
- }
- if (state->m_hasDVBT) {
- state->frontend.ops.delsys[n++] = SYS_DVBT;
- strlcat(state->frontend.ops.info.name, " DVB-T",
- sizeof(state->frontend.ops.info.name));
- }
- drxk_i2c_unlock(state);
- }
-error:
- if (status < 0) {
- state->m_DrxkState = DRXK_NO_DEV;
- drxk_i2c_unlock(state);
- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__);
- }
-
- return status;
-}
-
-static void load_firmware_cb(const struct firmware *fw,
- void *context)
-{
- struct drxk_state *state = context;
-
- dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded");
- if (!fw) {
- printk(KERN_ERR
- "drxk: Could not load firmware file %s.\n",
- state->microcode_name);
- printk(KERN_INFO
- "drxk: Copy %s to your hotplug directory!\n",
- state->microcode_name);
- state->microcode_name = NULL;
-
- /*
- * As firmware is now load asynchronous, it is not possible
- * anymore to fail at frontend attach. We might silently
- * return here, and hope that the driver won't crash.
- * We might also change all DVB callbacks to return -ENODEV
- * if the device is not initialized.
- * As the DRX-K devices have their own internal firmware,
- * let's just hope that it will match a firmware revision
- * compatible with this driver and proceed.
- */
- }
- state->fw = fw;
-
- init_drxk(state);
-}
-
-static void drxk_release(struct dvb_frontend *fe)
-{
- struct drxk_state *state = fe->demodulator_priv;
-
- dprintk(1, "\n");
- if (state->fw)
- release_firmware(state->fw);
-
- kfree(state);
-}
-
-static int drxk_sleep(struct dvb_frontend *fe)
-{
- struct drxk_state *state = fe->demodulator_priv;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return 0;
-
- ShutDown(state);
- return 0;
-}
-
-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct drxk_state *state = fe->demodulator_priv;
-
- dprintk(1, ": %s\n", enable ? "enable" : "disable");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
-
- return ConfigureI2CBridge(state, enable ? true : false);
-}
-
-static int drxk_set_parameters(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- u32 delsys = p->delivery_system, old_delsys;
- struct drxk_state *state = fe->demodulator_priv;
- u32 IF;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
-
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- if (!fe->ops.tuner_ops.get_if_frequency) {
- printk(KERN_ERR
- "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n");
- return -EINVAL;
- }
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- old_delsys = state->props.delivery_system;
- state->props = *p;
-
- if (old_delsys != delsys) {
- ShutDown(state);
- switch (delsys) {
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- if (!state->m_hasDVBC)
- return -EINVAL;
- state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? true : false;
- if (state->m_itut_annex_c)
- SetOperationMode(state, OM_QAM_ITU_C);
- else
- SetOperationMode(state, OM_QAM_ITU_A);
- break;
- case SYS_DVBT:
- if (!state->m_hasDVBT)
- return -EINVAL;
- SetOperationMode(state, OM_DVBT);
- break;
- default:
- return -EINVAL;
- }
- }
-
- fe->ops.tuner_ops.get_if_frequency(fe, &IF);
- Start(state, 0, IF);
-
- /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */
-
- return 0;
-}
-
-static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct drxk_state *state = fe->demodulator_priv;
- u32 stat;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- *status = 0;
- GetLockStatus(state, &stat, 0);
- if (stat == MPEG_LOCK)
- *status |= 0x1f;
- if (stat == FEC_LOCK)
- *status |= 0x0f;
- if (stat == DEMOD_LOCK)
- *status |= 0x07;
- return 0;
-}
-
-static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct drxk_state *state = fe->demodulator_priv;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- *ber = 0;
- return 0;
-}
-
-static int drxk_read_signal_strength(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct drxk_state *state = fe->demodulator_priv;
- u32 val = 0;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- ReadIFAgc(state, &val);
- *strength = val & 0xffff;
- return 0;
-}
-
-static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct drxk_state *state = fe->demodulator_priv;
- s32 snr2;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- GetSignalToNoise(state, &snr2);
- *snr = snr2 & 0xffff;
- return 0;
-}
-
-static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct drxk_state *state = fe->demodulator_priv;
- u16 err;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- DVBTQAMGetAccPktErr(state, &err);
- *ucblocks = (u32) err;
- return 0;
-}
-
-static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings
- *sets)
-{
- struct drxk_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
-
- dprintk(1, "\n");
-
- if (state->m_DrxkState == DRXK_NO_DEV)
- return -ENODEV;
- if (state->m_DrxkState == DRXK_UNINITIALIZED)
- return -EAGAIN;
-
- switch (p->delivery_system) {
- case SYS_DVBC_ANNEX_A:
- case SYS_DVBC_ANNEX_C:
- case SYS_DVBT:
- sets->min_delay_ms = 3000;
- sets->max_drift = 0;
- sets->step_size = 0;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static struct dvb_frontend_ops drxk_ops = {
- /* .delsys will be filled dynamically */
- .info = {
- .name = "DRXK",
- .frequency_min = 47000000,
- .frequency_max = 865000000,
- /* For DVB-C */
- .symbol_rate_min = 870000,
- .symbol_rate_max = 11700000,
- /* For DVB-T */
- .frequency_stepsize = 166667,
-
- .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
- FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_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_MUTE_TS |
- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO
- },
-
- .release = drxk_release,
- .sleep = drxk_sleep,
- .i2c_gate_ctrl = drxk_gate_ctrl,
-
- .set_frontend = drxk_set_parameters,
- .get_tune_settings = drxk_get_tune_settings,
-
- .read_status = drxk_read_status,
- .read_ber = drxk_read_ber,
- .read_signal_strength = drxk_read_signal_strength,
- .read_snr = drxk_read_snr,
- .read_ucblocks = drxk_read_ucblocks,
-};
-
-struct dvb_frontend *drxk_attach(const struct drxk_config *config,
- struct i2c_adapter *i2c)
-{
- struct drxk_state *state = NULL;
- u8 adr = config->adr;
- int status;
-
- dprintk(1, "\n");
- state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL);
- if (!state)
- return NULL;
-
- state->i2c = i2c;
- state->demod_address = adr;
- state->single_master = config->single_master;
- state->microcode_name = config->microcode_name;
- state->qam_demod_parameter_count = config->qam_demod_parameter_count;
- state->no_i2c_bridge = config->no_i2c_bridge;
- state->antenna_gpio = config->antenna_gpio;
- state->antenna_dvbt = config->antenna_dvbt;
- state->m_ChunkSize = config->chunk_size;
- state->enable_merr_cfg = config->enable_merr_cfg;
-
- if (config->dynamic_clk) {
- state->m_DVBTStaticCLK = 0;
- state->m_DVBCStaticCLK = 0;
- } else {
- state->m_DVBTStaticCLK = 1;
- state->m_DVBCStaticCLK = 1;
- }
-
-
- if (config->mpeg_out_clk_strength)
- state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
- else
- state->m_TSClockkStrength = 0x06;
-
- if (config->parallel_ts)
- state->m_enableParallel = true;
- else
- state->m_enableParallel = false;
-
- /* NOTE: as more UIO bits will be used, add them to the mask */
- state->UIO_mask = config->antenna_gpio;
-
- /* Default gpio to DVB-C */
- if (!state->antenna_dvbt && state->antenna_gpio)
- state->m_GPIO |= state->antenna_gpio;
- else
- state->m_GPIO &= ~state->antenna_gpio;
-
- mutex_init(&state->mutex);
-
- memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops));
- state->frontend.demodulator_priv = state;
-
- init_state(state);
-
- /* Load firmware and initialize DRX-K */
- if (state->microcode_name) {
- status = request_firmware_nowait(THIS_MODULE, 1,
- state->microcode_name,
- state->i2c->dev.parent,
- GFP_KERNEL,
- state, load_firmware_cb);
- if (status < 0) {
- printk(KERN_ERR
- "drxk: failed to request a firmware\n");
- return NULL;
- }
- } else if (init_drxk(state) < 0)
- goto error;
-
- printk(KERN_INFO "drxk: frontend initialized.\n");
- return &state->frontend;
-
-error:
- printk(KERN_ERR "drxk: not found\n");
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(drxk_attach);
-
-MODULE_DESCRIPTION("DRX-K driver");
-MODULE_AUTHOR("Ralph Metzler");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
deleted file mode 100644
index 1ab34838221c..000000000000
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- * descriptions + helper functions for simple dvb plls.
- *
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/dvb/frontend.h>
-#include <asm/types.h>
-
-#include "dvb-pll.h"
-
-struct dvb_pll_priv {
- /* pll number */
- int nr;
-
- /* i2c details */
- int pll_i2c_address;
- struct i2c_adapter *i2c;
-
- /* the PLL descriptor */
- struct dvb_pll_desc *pll_desc;
-
- /* cached frequency/bandwidth */
- u32 frequency;
- u32 bandwidth;
-};
-
-#define DVB_PLL_MAX 64
-
-static unsigned int dvb_pll_devcount;
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable verbose debug messages");
-
-static unsigned int id[DVB_PLL_MAX] =
- { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED };
-module_param_array(id, int, NULL, 0644);
-MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)");
-
-/* ----------------------------------------------------------- */
-
-struct dvb_pll_desc {
- char *name;
- u32 min;
- u32 max;
- u32 iffreq;
- void (*set)(struct dvb_frontend *fe, u8 *buf);
- u8 *initdata;
- u8 *initdata2;
- u8 *sleepdata;
- int count;
- struct {
- u32 limit;
- u32 stepsize;
- u8 config;
- u8 cb;
- } entries[12];
-};
-
-/* ----------------------------------------------------------- */
-/* descriptions */
-
-static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = {
- .name = "Thomson dtt7579",
- .min = 177000000,
- .max = 858000000,
- .iffreq= 36166667,
- .sleepdata = (u8[]){ 2, 0xb4, 0x03 },
- .count = 4,
- .entries = {
- { 443250000, 166667, 0xb4, 0x02 },
- { 542000000, 166667, 0xb4, 0x08 },
- { 771000000, 166667, 0xbc, 0x08 },
- { 999999999, 166667, 0xf4, 0x08 },
- },
-};
-
-static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf)
-{
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- if (bw == 7000000)
- buf[3] |= 0x10;
-}
-
-static struct dvb_pll_desc dvb_pll_thomson_dtt759x = {
- .name = "Thomson dtt759x",
- .min = 177000000,
- .max = 896000000,
- .set = thomson_dtt759x_bw,
- .iffreq= 36166667,
- .sleepdata = (u8[]){ 2, 0x84, 0x03 },
- .count = 5,
- .entries = {
- { 264000000, 166667, 0xb4, 0x02 },
- { 470000000, 166667, 0xbc, 0x02 },
- { 735000000, 166667, 0xbc, 0x08 },
- { 835000000, 166667, 0xf4, 0x08 },
- { 999999999, 166667, 0xfc, 0x08 },
- },
-};
-
-static struct dvb_pll_desc dvb_pll_lg_z201 = {
- .name = "LG z201",
- .min = 174000000,
- .max = 862000000,
- .iffreq= 36166667,
- .sleepdata = (u8[]){ 2, 0xbc, 0x03 },
- .count = 5,
- .entries = {
- { 157500000, 166667, 0xbc, 0x01 },
- { 443250000, 166667, 0xbc, 0x02 },
- { 542000000, 166667, 0xbc, 0x04 },
- { 830000000, 166667, 0xf4, 0x04 },
- { 999999999, 166667, 0xfc, 0x04 },
- },
-};
-
-static struct dvb_pll_desc dvb_pll_unknown_1 = {
- .name = "unknown 1", /* used by dntv live dvb-t */
- .min = 174000000,
- .max = 862000000,
- .iffreq= 36166667,
- .count = 9,
- .entries = {
- { 150000000, 166667, 0xb4, 0x01 },
- { 173000000, 166667, 0xbc, 0x01 },
- { 250000000, 166667, 0xb4, 0x02 },
- { 400000000, 166667, 0xbc, 0x02 },
- { 420000000, 166667, 0xf4, 0x02 },
- { 470000000, 166667, 0xfc, 0x02 },
- { 600000000, 166667, 0xbc, 0x08 },
- { 730000000, 166667, 0xf4, 0x08 },
- { 999999999, 166667, 0xfc, 0x08 },
- },
-};
-
-/* Infineon TUA6010XS
- * used in Thomson Cable Tuner
- */
-static struct dvb_pll_desc dvb_pll_tua6010xs = {
- .name = "Infineon TUA6010XS",
- .min = 44250000,
- .max = 858000000,
- .iffreq= 36125000,
- .count = 3,
- .entries = {
- { 115750000, 62500, 0x8e, 0x03 },
- { 403250000, 62500, 0x8e, 0x06 },
- { 999999999, 62500, 0x8e, 0x85 },
- },
-};
-
-/* Panasonic env57h1xd5 (some Philips PLL ?) */
-static struct dvb_pll_desc dvb_pll_env57h1xd5 = {
- .name = "Panasonic ENV57H1XD5",
- .min = 44250000,
- .max = 858000000,
- .iffreq= 36125000,
- .count = 4,
- .entries = {
- { 153000000, 166667, 0xc2, 0x41 },
- { 470000000, 166667, 0xc2, 0x42 },
- { 526000000, 166667, 0xc2, 0x84 },
- { 999999999, 166667, 0xc2, 0xa4 },
- },
-};
-
-/* Philips TDA6650/TDA6651
- * used in Panasonic ENV77H11D5
- */
-static void tda665x_bw(struct dvb_frontend *fe, u8 *buf)
-{
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- if (bw == 8000000)
- buf[3] |= 0x08;
-}
-
-static struct dvb_pll_desc dvb_pll_tda665x = {
- .name = "Philips TDA6650/TDA6651",
- .min = 44250000,
- .max = 858000000,
- .set = tda665x_bw,
- .iffreq= 36166667,
- .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab },
- .count = 12,
- .entries = {
- { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ },
- { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
- { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ },
- { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
- { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ },
- { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ },
- { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ },
- { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
- { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ },
- { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ },
- { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ },
- { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ },
- }
-};
-
-/* Infineon TUA6034
- * used in LG TDTP E102P
- */
-static void tua6034_bw(struct dvb_frontend *fe, u8 *buf)
-{
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- if (bw == 7000000)
- buf[3] |= 0x08;
-}
-
-static struct dvb_pll_desc dvb_pll_tua6034 = {
- .name = "Infineon TUA6034",
- .min = 44250000,
- .max = 858000000,
- .iffreq= 36166667,
- .count = 3,
- .set = tua6034_bw,
- .entries = {
- { 174500000, 62500, 0xce, 0x01 },
- { 230000000, 62500, 0xce, 0x02 },
- { 999999999, 62500, 0xce, 0x04 },
- },
-};
-
-/* ALPS TDED4
- * used in Nebula-Cards and USB boxes
- */
-static void tded4_bw(struct dvb_frontend *fe, u8 *buf)
-{
- u32 bw = fe->dtv_property_cache.bandwidth_hz;
- if (bw == 8000000)
- buf[3] |= 0x04;
-}
-
-static struct dvb_pll_desc dvb_pll_tded4 = {
- .name = "ALPS TDED4",
- .min = 47000000,
- .max = 863000000,
- .iffreq= 36166667,
- .set = tded4_bw,
- .count = 4,
- .entries = {
- { 153000000, 166667, 0x85, 0x01 },
- { 470000000, 166667, 0x85, 0x02 },
- { 823000000, 166667, 0x85, 0x08 },
- { 999999999, 166667, 0x85, 0x88 },
- }
-};
-
-/* ALPS TDHU2
- * used in AverTVHD MCE A180
- */
-static struct dvb_pll_desc dvb_pll_tdhu2 = {
- .name = "ALPS TDHU2",
- .min = 54000000,
- .max = 864000000,
- .iffreq= 44000000,
- .count = 4,
- .entries = {
- { 162000000, 62500, 0x85, 0x01 },
- { 426000000, 62500, 0x85, 0x02 },
- { 782000000, 62500, 0x85, 0x08 },
- { 999999999, 62500, 0x85, 0x88 },
- }
-};
-
-/* Samsung TBMV30111IN / TBMV30712IN1
- * used in Air2PC ATSC - 2nd generation (nxt2002)
- */
-static struct dvb_pll_desc dvb_pll_samsung_tbmv = {
- .name = "Samsung TBMV30111IN / TBMV30712IN1",
- .min = 54000000,
- .max = 860000000,
- .iffreq= 44000000,
- .count = 6,
- .entries = {
- { 172000000, 166667, 0xb4, 0x01 },
- { 214000000, 166667, 0xb4, 0x02 },
- { 467000000, 166667, 0xbc, 0x02 },
- { 721000000, 166667, 0xbc, 0x08 },
- { 841000000, 166667, 0xf4, 0x08 },
- { 999999999, 166667, 0xfc, 0x02 },
- }
-};
-
-/*
- * Philips SD1878 Tuner.
- */
-static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = {
- .name = "Philips SD1878",
- .min = 950000,
- .max = 2150000,
- .iffreq= 249, /* zero-IF, offset 249 is to round up */
- .count = 4,
- .entries = {
- { 1250000, 500, 0xc4, 0x00},
- { 1450000, 500, 0xc4, 0x40},
- { 2050000, 500, 0xc4, 0x80},
- { 2150000, 500, 0xc4, 0xc0},
- },
-};
-
-static void opera1_bw(struct dvb_frontend *fe, u8 *buf)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct dvb_pll_priv *priv = fe->tuner_priv;
- u32 b_w = (c->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, 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 },
- }
-};
-
-static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
- struct i2c_msg msg = {
- .addr = priv->pll_i2c_address,
- .flags = 0,
- .buf = buf,
- .len = 4
- };
- int result;
-
- 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);
-
- buf[2] = 0x9e;
- buf[3] = 0x90;
-
- return;
-}
-
-/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
-static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
- .name = "Samsung DTOS403IH102A",
- .min = 44250000,
- .max = 858000000,
- .iffreq = 36125000,
- .count = 8,
- .set = samsung_dtos403ih102a_set,
- .entries = {
- { 135000000, 62500, 0xbe, 0x01 },
- { 177000000, 62500, 0xf6, 0x01 },
- { 370000000, 62500, 0xbe, 0x02 },
- { 450000000, 62500, 0xf6, 0x02 },
- { 466000000, 62500, 0xfe, 0x02 },
- { 538000000, 62500, 0xbe, 0x08 },
- { 826000000, 62500, 0xf6, 0x08 },
- { 999999999, 62500, 0xfe, 0x08 },
- }
-};
-
-/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */
-static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = {
- .name = "Samsung TDTC9251DH0",
- .min = 48000000,
- .max = 863000000,
- .iffreq = 36166667,
- .count = 3,
- .entries = {
- { 157500000, 166667, 0xcc, 0x09 },
- { 443000000, 166667, 0xcc, 0x0a },
- { 863000000, 166667, 0xcc, 0x08 },
- }
-};
-
-/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */
-static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = {
- .name = "Samsung TBDU18132",
- .min = 950000,
- .max = 2150000, /* guesses */
- .iffreq = 0,
- .count = 2,
- .entries = {
- { 1550000, 125, 0x84, 0x82 },
- { 4095937, 125, 0x84, 0x80 },
- }
- /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported
- * by this driver. The two extra bits are 0x60 in the third byte. 15
- * bits is enough for over 4 GHz, which is enough to cover the range
- * of this tuner. We could use the additional divisor bits by adding
- * more entries, e.g.
- { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, },
- { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, },
- { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */
-};
-
-/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */
-static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = {
- .name = "Samsung TBMU24112",
- .min = 950000,
- .max = 2150000, /* guesses */
- .iffreq = 0,
- .count = 2,
- .entries = {
- { 1500000, 125, 0x84, 0x18 },
- { 9999999, 125, 0x84, 0x08 },
- }
-};
-
-/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */
-/* byte 4 : 1 * * AGD R3 R2 R1 R0
- * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1
- * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95
- * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5
- * 47 - 153 0 * 0 0 0 0 0 1 0x01
- * 153 - 430 0 * 0 0 0 0 1 0 0x02
- * 430 - 822 0 * 0 0 1 0 0 0 0x08
- * 822 - 862 1 * 0 0 1 0 0 0 0x88 */
-static struct dvb_pll_desc dvb_pll_alps_tdee4 = {
- .name = "ALPS TDEE4",
- .min = 47000000,
- .max = 862000000,
- .iffreq = 36125000,
- .count = 4,
- .entries = {
- { 153000000, 62500, 0x95, 0x01 },
- { 430000000, 62500, 0x95, 0x02 },
- { 822000000, 62500, 0x95, 0x08 },
- { 999999999, 62500, 0x95, 0x88 },
- }
-};
-
-/* ----------------------------------------------------------- */
-
-static struct dvb_pll_desc *pll_list[] = {
- [DVB_PLL_UNDEFINED] = NULL,
- [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579,
- [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x,
- [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201,
- [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1,
- [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs,
- [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5,
- [DVB_PLL_TUA6034] = &dvb_pll_tua6034,
- [DVB_PLL_TDA665X] = &dvb_pll_tda665x,
- [DVB_PLL_TDED4] = &dvb_pll_tded4,
- [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4,
- [DVB_PLL_TDHU2] = &dvb_pll_tdhu2,
- [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv,
- [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
- [DVB_PLL_OPERA1] = &dvb_pll_opera1,
- [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a,
- [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0,
- [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132,
- [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112,
-};
-
-/* ----------------------------------------------------------- */
-/* code */
-
-static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf,
- const u32 frequency)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
- struct dvb_pll_desc *desc = priv->pll_desc;
- u32 div;
- int i;
-
- if (frequency && (frequency < desc->min || frequency > desc->max))
- return -EINVAL;
-
- for (i = 0; i < desc->count; i++) {
- if (frequency > desc->entries[i].limit)
- continue;
- break;
- }
-
- if (debug)
- printk("pll: %s: freq=%d | i=%d/%d\n", desc->name,
- frequency, i, desc->count);
- if (i == desc->count)
- return -EINVAL;
-
- div = (frequency + desc->iffreq +
- desc->entries[i].stepsize/2) / desc->entries[i].stepsize;
- buf[0] = div >> 8;
- buf[1] = div & 0xff;
- buf[2] = desc->entries[i].config;
- buf[3] = desc->entries[i].cb;
-
- if (desc->set)
- desc->set(fe, buf);
-
- if (debug)
- printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n",
- desc->name, div, buf[0], buf[1], buf[2], buf[3]);
-
- // calculate the frequency we set it to
- return (div * desc->entries[i].stepsize) - desc->iffreq;
-}
-
-static int dvb_pll_release(struct dvb_frontend *fe)
-{
- kfree(fe->tuner_priv);
- fe->tuner_priv = NULL;
- return 0;
-}
-
-static int dvb_pll_sleep(struct dvb_frontend *fe)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
-
- if (priv->i2c == NULL)
- return -EINVAL;
-
- if (priv->pll_desc->sleepdata) {
- struct i2c_msg msg = { .flags = 0,
- .addr = priv->pll_i2c_address,
- .buf = priv->pll_desc->sleepdata + 1,
- .len = priv->pll_desc->sleepdata[0] };
-
- int result;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
- return result;
- }
- return 0;
- }
- /* Shouldn't be called when initdata is NULL, maybe BUG()? */
- return -EINVAL;
-}
-
-static int dvb_pll_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct dvb_pll_priv *priv = fe->tuner_priv;
- u8 buf[4];
- struct i2c_msg msg =
- { .addr = priv->pll_i2c_address, .flags = 0,
- .buf = buf, .len = sizeof(buf) };
- int result;
- u32 frequency = 0;
-
- if (priv->i2c == NULL)
- return -EINVAL;
-
- result = dvb_pll_configure(fe, buf, c->frequency);
- if (result < 0)
- return result;
- else
- frequency = result;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) {
- return result;
- }
-
- priv->frequency = frequency;
- priv->bandwidth = c->bandwidth_hz;
-
- return 0;
-}
-
-static int dvb_pll_calc_regs(struct dvb_frontend *fe,
- u8 *buf, int buf_len)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct dvb_pll_priv *priv = fe->tuner_priv;
- int result;
- u32 frequency = 0;
-
- if (buf_len < 5)
- return -EINVAL;
-
- result = dvb_pll_configure(fe, buf + 1, c->frequency);
- if (result < 0)
- return result;
- else
- frequency = result;
-
- buf[0] = priv->pll_i2c_address;
-
- priv->frequency = frequency;
- priv->bandwidth = c->bandwidth_hz;
-
- return 5;
-}
-
-static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
- *frequency = priv->frequency;
- return 0;
-}
-
-static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
- *bandwidth = priv->bandwidth;
- return 0;
-}
-
-static int dvb_pll_init(struct dvb_frontend *fe)
-{
- struct dvb_pll_priv *priv = fe->tuner_priv;
-
- if (priv->i2c == NULL)
- return -EINVAL;
-
- if (priv->pll_desc->initdata) {
- struct i2c_msg msg = { .flags = 0,
- .addr = priv->pll_i2c_address,
- .buf = priv->pll_desc->initdata + 1,
- .len = priv->pll_desc->initdata[0] };
-
- int result;
- 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;
- 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;
- }
- /* Shouldn't be called when initdata is NULL, maybe BUG()? */
- return -EINVAL;
-}
-
-static struct dvb_tuner_ops dvb_pll_tuner_ops = {
- .release = dvb_pll_release,
- .sleep = dvb_pll_sleep,
- .init = dvb_pll_init,
- .set_params = dvb_pll_set_params,
- .calc_regs = dvb_pll_calc_regs,
- .get_frequency = dvb_pll_get_frequency,
- .get_bandwidth = dvb_pll_get_bandwidth,
-};
-
-struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr,
- struct i2c_adapter *i2c,
- unsigned int pll_desc_id)
-{
- u8 b1 [] = { 0 };
- struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD,
- .buf = b1, .len = 1 };
- struct dvb_pll_priv *priv = NULL;
- int ret;
- struct dvb_pll_desc *desc;
-
- if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) &&
- (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list)))
- pll_desc_id = id[dvb_pll_devcount];
-
- BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list));
-
- desc = pll_list[pll_desc_id];
-
- if (i2c != NULL) {
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- ret = i2c_transfer (i2c, &msg, 1);
- if (ret != 1)
- return NULL;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL);
- if (priv == NULL)
- return NULL;
-
- priv->pll_i2c_address = pll_addr;
- priv->i2c = i2c;
- priv->pll_desc = desc;
- priv->nr = dvb_pll_devcount++;
-
- memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops,
- sizeof(struct dvb_tuner_ops));
-
- strncpy(fe->ops.tuner_ops.info.name, desc->name,
- sizeof(fe->ops.tuner_ops.info.name));
- fe->ops.tuner_ops.info.frequency_min = desc->min;
- fe->ops.tuner_ops.info.frequency_max = desc->max;
- if (!desc->initdata)
- fe->ops.tuner_ops.init = NULL;
- if (!desc->sleepdata)
- fe->ops.tuner_ops.sleep = NULL;
-
- fe->tuner_priv = priv;
-
- if ((debug) || (id[priv->nr] == pll_desc_id)) {
- printk("dvb-pll[%d]", priv->nr);
- if (i2c != NULL)
- printk(" %d-%04x", i2c_adapter_id(i2c), pll_addr);
- printk(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name,
- id[priv->nr] == pll_desc_id ?
- "insmod option" : "autodetected");
- }
-
- return fe;
-}
-EXPORT_SYMBOL(dvb_pll_attach);
-
-MODULE_DESCRIPTION("dvb pll library");
-MODULE_AUTHOR("Gerd Knorr");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
deleted file mode 100644
index 086964344c38..000000000000
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * descriptions + helper functions for simple dvb plls.
- */
-
-#ifndef __DVB_PLL_H__
-#define __DVB_PLL_H__
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-#define DVB_PLL_UNDEFINED 0
-#define DVB_PLL_THOMSON_DTT7579 1
-#define DVB_PLL_THOMSON_DTT759X 2
-#define DVB_PLL_LG_Z201 3
-#define DVB_PLL_UNKNOWN_1 4
-#define DVB_PLL_TUA6010XS 5
-#define DVB_PLL_ENV57H1XD5 6
-#define DVB_PLL_TUA6034 7
-#define DVB_PLL_TDA665X 8
-#define DVB_PLL_TDED4 9
-#define DVB_PLL_TDHU2 10
-#define DVB_PLL_SAMSUNG_TBMV 11
-#define DVB_PLL_PHILIPS_SD1878_TDA8261 12
-#define DVB_PLL_OPERA1 13
-#define DVB_PLL_SAMSUNG_DTOS403IH102A 14
-#define DVB_PLL_SAMSUNG_TDTC9251DH0 15
-#define DVB_PLL_SAMSUNG_TBDU18132 16
-#define DVB_PLL_SAMSUNG_TBMU24112 17
-#define DVB_PLL_TDEE4 18
-
-/**
- * Attach a dvb-pll to the supplied frontend structure.
- *
- * @param fe Frontend to attach to.
- * @param pll_addr i2c address of the PLL (if used).
- * @param i2c i2c adapter to use (set to NULL if not used).
- * @param pll_desc_id dvb_pll_desc to use.
- * @return Frontend pointer on success, NULL on failure
- */
-#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE))
-extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
- int pll_addr,
- struct i2c_adapter *i2c,
- unsigned int pll_desc_id);
-#else
-static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe,
- int pll_addr,
- struct i2c_adapter *i2c,
- unsigned int pll_desc_id)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif
diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c
deleted file mode 100644
index c56fddbf53b7..000000000000
--- a/drivers/media/dvb/frontends/ec100.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * E3C EC100 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "dvb_frontend.h"
-#include "ec100_priv.h"
-#include "ec100.h"
-
-int ec100_debug;
-module_param_named(debug, ec100_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-struct ec100_state {
- struct i2c_adapter *i2c;
- struct dvb_frontend frontend;
- struct ec100_config config;
-
- u16 ber;
-};
-
-/* write single register */
-static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val)
-{
- u8 buf[2] = {reg, val};
- struct i2c_msg msg = {
- .addr = state->config.demod_address,
- .flags = 0,
- .len = 2,
- .buf = buf};
-
- if (i2c_transfer(state->i2c, &msg, 1) != 1) {
- warn("I2C write failed reg:%02x", reg);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-/* read single register */
-static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val)
-{
- struct i2c_msg msg[2] = {
- {
- .addr = state->config.demod_address,
- .flags = 0,
- .len = 1,
- .buf = &reg
- }, {
- .addr = state->config.demod_address,
- .flags = I2C_M_RD,
- .len = 1,
- .buf = val
- }
- };
-
- if (i2c_transfer(state->i2c, msg, 2) != 2) {
- warn("I2C read failed reg:%02x", reg);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int ec100_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct ec100_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp, tmp2;
-
- deb_info("%s: freq:%d bw:%d\n", __func__, c->frequency,
- c->bandwidth_hz);
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- ret = ec100_write_reg(state, 0x04, 0x06);
- if (ret)
- goto error;
- ret = ec100_write_reg(state, 0x67, 0x58);
- if (ret)
- goto error;
- ret = ec100_write_reg(state, 0x05, 0x18);
- if (ret)
- goto error;
-
- /* reg/bw | 6 | 7 | 8
- -------+------+------+------
- A 0x1b | 0xa1 | 0xe7 | 0x2c
- A 0x1c | 0x55 | 0x63 | 0x72
- -------+------+------+------
- B 0x1b | 0xb7 | 0x00 | 0x49
- B 0x1c | 0x55 | 0x64 | 0x72 */
-
- switch (c->bandwidth_hz) {
- case 6000000:
- tmp = 0xb7;
- tmp2 = 0x55;
- break;
- case 7000000:
- tmp = 0x00;
- tmp2 = 0x64;
- break;
- case 8000000:
- default:
- tmp = 0x49;
- tmp2 = 0x72;
- }
-
- ret = ec100_write_reg(state, 0x1b, tmp);
- if (ret)
- goto error;
- ret = ec100_write_reg(state, 0x1c, tmp2);
- if (ret)
- goto error;
-
- ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */
- if (ret)
- goto error;
- ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */
- if (ret)
- goto error;
-
- ret = ec100_write_reg(state, 0x08, 0x24);
- if (ret)
- goto error;
-
- ret = ec100_write_reg(state, 0x00, 0x00); /* go */
- if (ret)
- goto error;
- ret = ec100_write_reg(state, 0x00, 0x20); /* go */
- if (ret)
- goto error;
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec100_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- fesettings->min_delay_ms = 300;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
-
- return 0;
-}
-
-static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct ec100_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp;
- *status = 0;
-
- ret = ec100_read_reg(state, 0x42, &tmp);
- if (ret)
- goto error;
-
- if (tmp & 0x80) {
- /* bit7 set - have lock */
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
- FE_HAS_SYNC | FE_HAS_LOCK;
- } else {
- ret = ec100_read_reg(state, 0x01, &tmp);
- if (ret)
- goto error;
-
- if (tmp & 0x10) {
- /* bit4 set - have signal */
- *status |= FE_HAS_SIGNAL;
- if (!(tmp & 0x01)) {
- /* bit0 clear - have ~valid signal */
- *status |= FE_HAS_CARRIER | FE_HAS_VITERBI;
- }
- }
- }
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct ec100_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp, tmp2;
- u16 ber2;
-
- *ber = 0;
-
- ret = ec100_read_reg(state, 0x65, &tmp);
- if (ret)
- goto error;
- ret = ec100_read_reg(state, 0x66, &tmp2);
- if (ret)
- goto error;
-
- ber2 = (tmp2 << 8) | tmp;
-
- /* if counter overflow or clear */
- if (ber2 < state->ber)
- *ber = ber2;
- else
- *ber = ber2 - state->ber;
-
- state->ber = ber2;
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct ec100_state *state = fe->demodulator_priv;
- int ret;
- u8 tmp;
-
- ret = ec100_read_reg(state, 0x24, &tmp);
- if (ret) {
- *strength = 0;
- goto error;
- }
-
- *strength = ((tmp << 8) | tmp);
-
- return ret;
-error:
- deb_info("%s: failed:%d\n", __func__, ret);
- return ret;
-}
-
-static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- *snr = 0;
- return 0;
-}
-
-static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- return 0;
-}
-
-static void ec100_release(struct dvb_frontend *fe)
-{
- struct ec100_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops ec100_ops;
-
-struct dvb_frontend *ec100_attach(const struct ec100_config *config,
- struct i2c_adapter *i2c)
-{
- int ret;
- struct ec100_state *state = NULL;
- u8 tmp;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->config, config, sizeof(struct ec100_config));
-
- /* check if the demod is there */
- ret = ec100_read_reg(state, 0x33, &tmp);
- if (ret || tmp != 0x0b)
- goto error;
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &ec100_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
-
- return &state->frontend;
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(ec100_attach);
-
-static struct dvb_frontend_ops ec100_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "E3C EC100 DVB-T",
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_MUTE_TS
- },
-
- .release = ec100_release,
- .set_frontend = ec100_set_frontend,
- .get_tune_settings = ec100_get_tune_settings,
- .read_status = ec100_read_status,
- .read_ber = ec100_read_ber,
- .read_signal_strength = ec100_read_signal_strength,
- .read_snr = ec100_read_snr,
- .read_ucblocks = ec100_read_ucblocks,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h
deleted file mode 100644
index ee8e52417958..000000000000
--- a/drivers/media/dvb/frontends/ec100.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * E3C EC100 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 EC100_H
-#define EC100_H
-
-#include <linux/dvb/frontend.h>
-
-struct ec100_config {
- /* demodulator's I2C address */
- u8 demod_address;
-};
-
-
-#if defined(CONFIG_DVB_EC100) || \
- (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE))
-extern struct dvb_frontend *ec100_attach(const struct ec100_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *ec100_attach(
- const struct ec100_config *config, struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* EC100_H */
diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h
deleted file mode 100644
index 5c990144bc47..000000000000
--- a/drivers/media/dvb/frontends/ec100_priv.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * E3C EC100 demodulator driver
- *
- * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 EC100_PRIV
-#define EC100_PRIV
-
-#define LOG_PREFIX "ec100"
-
-#define dprintk(var, level, args...) \
- do { if ((var & level)) printk(args); } while (0)
-
-#define deb_info(args...) dprintk(ec100_debug, 0x01, args)
-
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-#endif /* EC100_PRIV */
diff --git a/drivers/media/dvb/frontends/hd29l2.c b/drivers/media/dvb/frontends/hd29l2.c
deleted file mode 100644
index a00318190837..000000000000
--- a/drivers/media/dvb/frontends/hd29l2.c
+++ /dev/null
@@ -1,861 +0,0 @@
-/*
- * HDIC HD29L2 DMB-TH demodulator driver
- *
- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
- *
- * Author: Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 "hd29l2_priv.h"
-
-int hd29l2_debug;
-module_param_named(debug, hd29l2_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-/* write multiple registers */
-static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[2 + len];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = 0x00;
- buf[1] = reg;
- memcpy(&buf[2], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* read multiple registers */
-static int hd29l2_rd_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[2] = { 0x00, reg };
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = 2,
- .buf = buf,
- }, {
- .addr = priv->cfg.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
-
- return ret;
-}
-
-/* write single register */
-static int hd29l2_wr_reg(struct hd29l2_priv *priv, u8 reg, u8 val)
-{
- return hd29l2_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int hd29l2_rd_reg(struct hd29l2_priv *priv, u8 reg, u8 *val)
-{
- return hd29l2_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = hd29l2_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return hd29l2_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register with mask */
-int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask)
-{
- int ret, i;
- u8 tmp;
-
- ret = hd29l2_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- tmp &= mask;
-
- /* find position of the first bit */
- for (i = 0; i < 8; i++) {
- if ((mask >> i) & 0x01)
- break;
- }
- *val = tmp >> i;
-
- return 0;
-}
-
-static int hd29l2_soft_reset(struct hd29l2_priv *priv)
-{
- int ret;
- u8 tmp;
-
- ret = hd29l2_rd_reg(priv, 0x26, &tmp);
- if (ret)
- goto err;
-
- ret = hd29l2_wr_reg(priv, 0x26, 0x0d);
- if (ret)
- goto err;
-
- usleep_range(10000, 20000);
-
- ret = hd29l2_wr_reg(priv, 0x26, tmp);
- if (ret)
- goto err;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- int ret, i;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 tmp;
-
- dbg("%s: enable=%d", __func__, enable);
-
- /* set tuner address for demod */
- if (!priv->tuner_i2c_addr_programmed && enable) {
- /* no need to set tuner address every time, once is enough */
- ret = hd29l2_wr_reg(priv, 0x9d, priv->cfg.tuner_i2c_addr << 1);
- if (ret)
- goto err;
-
- priv->tuner_i2c_addr_programmed = true;
- }
-
- /* open / close gate */
- ret = hd29l2_wr_reg(priv, 0x9f, enable);
- if (ret)
- goto err;
-
- /* wait demod ready */
- for (i = 10; i; i--) {
- ret = hd29l2_rd_reg(priv, 0x9e, &tmp);
- if (ret)
- goto err;
-
- if (tmp == enable)
- break;
-
- usleep_range(5000, 10000);
- }
-
- dbg("%s: loop=%d", __func__, i);
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- int ret;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 buf[2];
-
- *status = 0;
-
- ret = hd29l2_rd_reg(priv, 0x05, &buf[0]);
- if (ret)
- goto err;
-
- if (buf[0] & 0x01) {
- /* full lock */
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI |
- FE_HAS_SYNC | FE_HAS_LOCK;
- } else {
- ret = hd29l2_rd_reg(priv, 0x0d, &buf[1]);
- if (ret)
- goto err;
-
- if ((buf[1] & 0xfe) == 0x78)
- /* partial lock */
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC;
- }
-
- priv->fe_status = *status;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- int ret;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 buf[2];
- u16 tmp;
-
- if (!(priv->fe_status & FE_HAS_LOCK)) {
- *snr = 0;
- ret = 0;
- goto err;
- }
-
- ret = hd29l2_rd_regs(priv, 0x0b, buf, 2);
- if (ret)
- goto err;
-
- tmp = (buf[0] << 8) | buf[1];
-
- /* report SNR in dB * 10 */
- #define LOG10_20736_24 72422627 /* log10(20736) << 24 */
- if (tmp)
- *snr = (LOG10_20736_24 - intlog10(tmp)) / ((1 << 24) / 100);
- else
- *snr = 0;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- int ret;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 buf[2];
- u16 tmp;
-
- *strength = 0;
-
- ret = hd29l2_rd_regs(priv, 0xd5, buf, 2);
- if (ret)
- goto err;
-
- tmp = buf[0] << 8 | buf[1];
- tmp = ~tmp & 0x0fff;
-
- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */
- *strength = tmp * 0xffff / 0x0fff;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- int ret;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 buf[2];
-
- if (!(priv->fe_status & FE_HAS_SYNC)) {
- *ber = 0;
- ret = 0;
- goto err;
- }
-
- ret = hd29l2_rd_regs(priv, 0xd9, buf, 2);
- if (ret) {
- *ber = 0;
- goto err;
- }
-
- /* LDPC BER */
- *ber = ((buf[0] & 0x0f) << 8) | buf[1];
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- /* no way to read? */
- *ucblocks = 0;
- return 0;
-}
-
-static enum dvbfe_search hd29l2_search(struct dvb_frontend *fe)
-{
- int ret, i;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u8 tmp, buf[3];
- u8 modulation, carrier, guard_interval, interleave, code_rate;
- u64 num64;
- u32 if_freq, if_ctl;
- bool auto_mode;
-
- dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d " \
- "modulation=%d inversion=%d fec_inner=%d guard_interval=%d",
- __func__,
- c->delivery_system, c->frequency, c->bandwidth_hz,
- c->modulation, c->inversion, c->fec_inner, c->guard_interval);
-
- /* as for now we detect always params automatically */
- auto_mode = true;
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- /* get and program IF */
- if (fe->ops.tuner_ops.get_if_frequency)
- fe->ops.tuner_ops.get_if_frequency(fe, &if_freq);
- else
- if_freq = 0;
-
- if (if_freq) {
- /* normal IF */
-
- /* calc IF control value */
- num64 = if_freq;
- num64 *= 0x800000;
- num64 = div_u64(num64, HD29L2_XTAL);
- num64 -= 0x800000;
- if_ctl = num64;
-
- tmp = 0xfc; /* tuner type normal */
- } else {
- /* zero IF */
- if_ctl = 0;
- tmp = 0xfe; /* tuner type Zero-IF */
- }
-
- buf[0] = ((if_ctl >> 0) & 0xff);
- buf[1] = ((if_ctl >> 8) & 0xff);
- buf[2] = ((if_ctl >> 16) & 0xff);
-
- /* program IF control */
- ret = hd29l2_wr_regs(priv, 0x14, buf, 3);
- if (ret)
- goto err;
-
- /* program tuner type */
- ret = hd29l2_wr_reg(priv, 0xab, tmp);
- if (ret)
- goto err;
-
- dbg("%s: if_freq=%d if_ctl=%x", __func__, if_freq, if_ctl);
-
- if (auto_mode) {
- /*
- * use auto mode
- */
-
- /* disable quick mode */
- ret = hd29l2_wr_reg_mask(priv, 0xac, 0 << 7, 0x80);
- if (ret)
- goto err;
-
- ret = hd29l2_wr_reg_mask(priv, 0x82, 1 << 1, 0x02);
- if (ret)
- goto err;
-
- /* enable auto mode */
- ret = hd29l2_wr_reg_mask(priv, 0x7d, 1 << 6, 0x40);
- if (ret)
- goto err;
-
- ret = hd29l2_wr_reg_mask(priv, 0x81, 1 << 3, 0x08);
- if (ret)
- goto err;
-
- /* soft reset */
- ret = hd29l2_soft_reset(priv);
- if (ret)
- goto err;
-
- /* detect modulation */
- for (i = 30; i; i--) {
- msleep(100);
-
- ret = hd29l2_rd_reg(priv, 0x0d, &tmp);
- if (ret)
- goto err;
-
- if ((((tmp & 0xf0) >= 0x10) &&
- ((tmp & 0x0f) == 0x08)) || (tmp >= 0x2c))
- break;
- }
-
- dbg("%s: loop=%d", __func__, i);
-
- if (i == 0)
- /* detection failed */
- return DVBFE_ALGO_SEARCH_FAILED;
-
- /* read modulation */
- ret = hd29l2_rd_reg_mask(priv, 0x7d, &modulation, 0x07);
- if (ret)
- goto err;
- } else {
- /*
- * use manual mode
- */
-
- modulation = HD29L2_QAM64;
- carrier = HD29L2_CARRIER_MULTI;
- guard_interval = HD29L2_PN945;
- interleave = HD29L2_INTERLEAVER_420;
- code_rate = HD29L2_CODE_RATE_08;
-
- tmp = (code_rate << 3) | modulation;
- ret = hd29l2_wr_reg_mask(priv, 0x7d, tmp, 0x5f);
- if (ret)
- goto err;
-
- tmp = (carrier << 2) | guard_interval;
- ret = hd29l2_wr_reg_mask(priv, 0x81, tmp, 0x0f);
- if (ret)
- goto err;
-
- tmp = interleave;
- ret = hd29l2_wr_reg_mask(priv, 0x82, tmp, 0x03);
- if (ret)
- goto err;
- }
-
- /* ensure modulation validy */
- /* 0=QAM4_NR, 1=QAM4, 2=QAM16, 3=QAM32, 4=QAM64 */
- if (modulation > (ARRAY_SIZE(reg_mod_vals_tab[0].val) - 1)) {
- dbg("%s: modulation=%d not valid", __func__, modulation);
- goto err;
- }
-
- /* program registers according to modulation */
- for (i = 0; i < ARRAY_SIZE(reg_mod_vals_tab); i++) {
- ret = hd29l2_wr_reg(priv, reg_mod_vals_tab[i].reg,
- reg_mod_vals_tab[i].val[modulation]);
- if (ret)
- goto err;
- }
-
- /* read guard interval */
- ret = hd29l2_rd_reg_mask(priv, 0x81, &guard_interval, 0x03);
- if (ret)
- goto err;
-
- /* read carrier mode */
- ret = hd29l2_rd_reg_mask(priv, 0x81, &carrier, 0x04);
- if (ret)
- goto err;
-
- dbg("%s: modulation=%d guard_interval=%d carrier=%d",
- __func__, modulation, guard_interval, carrier);
-
- if ((carrier == HD29L2_CARRIER_MULTI) && (modulation == HD29L2_QAM64) &&
- (guard_interval == HD29L2_PN945)) {
- dbg("%s: C=3780 && QAM64 && PN945", __func__);
-
- ret = hd29l2_wr_reg(priv, 0x42, 0x33);
- if (ret)
- goto err;
-
- ret = hd29l2_wr_reg(priv, 0xdd, 0x01);
- if (ret)
- goto err;
- }
-
- usleep_range(10000, 20000);
-
- /* soft reset */
- ret = hd29l2_soft_reset(priv);
- if (ret)
- goto err;
-
- /* wait demod lock */
- for (i = 30; i; i--) {
- msleep(100);
-
- /* read lock bit */
- ret = hd29l2_rd_reg_mask(priv, 0x05, &tmp, 0x01);
- if (ret)
- goto err;
-
- if (tmp)
- break;
- }
-
- dbg("%s: loop=%d", __func__, i);
-
- if (i == 0)
- return DVBFE_ALGO_SEARCH_AGAIN;
-
- return DVBFE_ALGO_SEARCH_SUCCESS;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return DVBFE_ALGO_SEARCH_ERROR;
-}
-
-static int hd29l2_get_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_CUSTOM;
-}
-
-static int hd29l2_get_frontend(struct dvb_frontend *fe)
-{
- int ret;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u8 buf[3];
- u32 if_ctl;
- char *str_constellation, *str_code_rate, *str_constellation_code_rate,
- *str_guard_interval, *str_carrier, *str_guard_interval_carrier,
- *str_interleave, *str_interleave_;
-
- ret = hd29l2_rd_reg(priv, 0x7d, &buf[0]);
- if (ret)
- goto err;
-
- ret = hd29l2_rd_regs(priv, 0x81, &buf[1], 2);
- if (ret)
- goto err;
-
- /* constellation, 0x7d[2:0] */
- switch ((buf[0] >> 0) & 0x07) {
- case 0: /* QAM4NR */
- str_constellation = "QAM4NR";
- c->modulation = QAM_AUTO; /* FIXME */
- break;
- case 1: /* QAM4 */
- str_constellation = "QAM4";
- c->modulation = QPSK; /* FIXME */
- break;
- case 2:
- str_constellation = "QAM16";
- c->modulation = QAM_16;
- break;
- case 3:
- str_constellation = "QAM32";
- c->modulation = QAM_32;
- break;
- case 4:
- str_constellation = "QAM64";
- c->modulation = QAM_64;
- break;
- default:
- str_constellation = "?";
- }
-
- /* LDPC code rate, 0x7d[4:3] */
- switch ((buf[0] >> 3) & 0x03) {
- case 0: /* 0.4 */
- str_code_rate = "0.4";
- c->fec_inner = FEC_AUTO; /* FIXME */
- break;
- case 1: /* 0.6 */
- str_code_rate = "0.6";
- c->fec_inner = FEC_3_5;
- break;
- case 2: /* 0.8 */
- str_code_rate = "0.8";
- c->fec_inner = FEC_4_5;
- break;
- default:
- str_code_rate = "?";
- }
-
- /* constellation & code rate set, 0x7d[6] */
- switch ((buf[0] >> 6) & 0x01) {
- case 0:
- str_constellation_code_rate = "manual";
- break;
- case 1:
- str_constellation_code_rate = "auto";
- break;
- default:
- str_constellation_code_rate = "?";
- }
-
- /* frame header, 0x81[1:0] */
- switch ((buf[1] >> 0) & 0x03) {
- case 0: /* PN945 */
- str_guard_interval = "PN945";
- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
- break;
- case 1: /* PN595 */
- str_guard_interval = "PN595";
- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
- break;
- case 2: /* PN420 */
- str_guard_interval = "PN420";
- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */
- break;
- default:
- str_guard_interval = "?";
- }
-
- /* carrier, 0x81[2] */
- switch ((buf[1] >> 2) & 0x01) {
- case 0:
- str_carrier = "C=1";
- break;
- case 1:
- str_carrier = "C=3780";
- break;
- default:
- str_carrier = "?";
- }
-
- /* frame header & carrier set, 0x81[3] */
- switch ((buf[1] >> 3) & 0x01) {
- case 0:
- str_guard_interval_carrier = "manual";
- break;
- case 1:
- str_guard_interval_carrier = "auto";
- break;
- default:
- str_guard_interval_carrier = "?";
- }
-
- /* interleave, 0x82[0] */
- switch ((buf[2] >> 0) & 0x01) {
- case 0:
- str_interleave = "M=720";
- break;
- case 1:
- str_interleave = "M=240";
- break;
- default:
- str_interleave = "?";
- }
-
- /* interleave set, 0x82[1] */
- switch ((buf[2] >> 1) & 0x01) {
- case 0:
- str_interleave_ = "manual";
- break;
- case 1:
- str_interleave_ = "auto";
- break;
- default:
- str_interleave_ = "?";
- }
-
- /*
- * We can read out current detected NCO and use that value next
- * time instead of calculating new value from targed IF.
- * I think it will not effect receiver sensitivity but gaining lock
- * after tune could be easier...
- */
- ret = hd29l2_rd_regs(priv, 0xb1, &buf[0], 3);
- if (ret)
- goto err;
-
- if_ctl = (buf[0] << 16) | ((buf[1] - 7) << 8) | buf[2];
-
- dbg("%s: %s %s %s | %s %s %s | %s %s | NCO=%06x", __func__,
- str_constellation, str_code_rate, str_constellation_code_rate,
- str_guard_interval, str_carrier, str_guard_interval_carrier,
- str_interleave, str_interleave_, if_ctl);
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int hd29l2_init(struct dvb_frontend *fe)
-{
- int ret, i;
- struct hd29l2_priv *priv = fe->demodulator_priv;
- u8 tmp;
- static const struct reg_val tab[] = {
- { 0x3a, 0x06 },
- { 0x3b, 0x03 },
- { 0x3c, 0x04 },
- { 0xaf, 0x06 },
- { 0xb0, 0x1b },
- { 0x80, 0x64 },
- { 0x10, 0x38 },
- };
-
- dbg("%s:", __func__);
-
- /* reset demod */
- /* it is recommended to HW reset chip using RST_N pin */
- if (fe->callback) {
- ret = fe->callback(fe, DVB_FRONTEND_COMPONENT_DEMOD, 0, 0);
- if (ret)
- goto err;
-
- /* reprogramming needed because HW reset clears registers */
- priv->tuner_i2c_addr_programmed = false;
- }
-
- /* init */
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = hd29l2_wr_reg(priv, tab[i].reg, tab[i].val);
- if (ret)
- goto err;
- }
-
- /* TS params */
- ret = hd29l2_rd_reg(priv, 0x36, &tmp);
- if (ret)
- goto err;
-
- tmp &= 0x1b;
- tmp |= priv->cfg.ts_mode;
- ret = hd29l2_wr_reg(priv, 0x36, tmp);
- if (ret)
- goto err;
-
- ret = hd29l2_rd_reg(priv, 0x31, &tmp);
- tmp &= 0xef;
-
- if (!(priv->cfg.ts_mode >> 7))
- /* set b4 for serial TS */
- tmp |= 0x10;
-
- ret = hd29l2_wr_reg(priv, 0x31, tmp);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static void hd29l2_release(struct dvb_frontend *fe)
-{
- struct hd29l2_priv *priv = fe->demodulator_priv;
- kfree(priv);
-}
-
-static struct dvb_frontend_ops hd29l2_ops;
-
-struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config,
- struct i2c_adapter *i2c)
-{
- int ret;
- struct hd29l2_priv *priv = NULL;
- u8 tmp;
-
- /* allocate memory for the internal state */
- priv = kzalloc(sizeof(struct hd29l2_priv), GFP_KERNEL);
- if (priv == NULL)
- goto err;
-
- /* setup the state */
- priv->i2c = i2c;
- memcpy(&priv->cfg, config, sizeof(struct hd29l2_config));
-
-
- /* check if the demod is there */
- ret = hd29l2_rd_reg(priv, 0x00, &tmp);
- if (ret)
- goto err;
-
- /* create dvb_frontend */
- memcpy(&priv->fe.ops, &hd29l2_ops, sizeof(struct dvb_frontend_ops));
- priv->fe.demodulator_priv = priv;
-
- return &priv->fe;
-err:
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(hd29l2_attach);
-
-static struct dvb_frontend_ops hd29l2_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "HDIC HD29L2 DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
- .caps = FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_QAM_16 |
- FE_CAN_QAM_32 |
- FE_CAN_QAM_64 |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_BANDWIDTH_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER
- },
-
- .release = hd29l2_release,
-
- .init = hd29l2_init,
-
- .get_frontend_algo = hd29l2_get_frontend_algo,
- .search = hd29l2_search,
- .get_frontend = hd29l2_get_frontend,
-
- .read_status = hd29l2_read_status,
- .read_snr = hd29l2_read_snr,
- .read_signal_strength = hd29l2_read_signal_strength,
- .read_ber = hd29l2_read_ber,
- .read_ucblocks = hd29l2_read_ucblocks,
-
- .i2c_gate_ctrl = hd29l2_i2c_gate_ctrl,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("HDIC HD29L2 DMB-TH demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/hd29l2.h b/drivers/media/dvb/frontends/hd29l2.h
deleted file mode 100644
index a7a64431364d..000000000000
--- a/drivers/media/dvb/frontends/hd29l2.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * HDIC HD29L2 DMB-TH demodulator driver
- *
- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
- *
- * Author: Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 HD29L2_H
-#define HD29L2_H
-
-#include <linux/dvb/frontend.h>
-
-struct hd29l2_config {
- /*
- * demodulator I2C address
- */
- u8 i2c_addr;
-
- /*
- * tuner I2C address
- * only needed when tuner is behind demod I2C-gate
- */
- u8 tuner_i2c_addr;
-
- /*
- * TS settings
- */
-#define HD29L2_TS_SERIAL 0x00
-#define HD29L2_TS_PARALLEL 0x80
-#define HD29L2_TS_CLK_NORMAL 0x40
-#define HD29L2_TS_CLK_INVERTED 0x00
-#define HD29L2_TS_CLK_GATED 0x20
-#define HD29L2_TS_CLK_FREE 0x00
- u8 ts_mode;
-};
-
-
-#if defined(CONFIG_DVB_HD29L2) || \
- (defined(CONFIG_DVB_HD29L2_MODULE) && defined(MODULE))
-extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config,
- struct i2c_adapter *i2c);
-#else
-static inline struct dvb_frontend *hd29l2_attach(
-const struct hd29l2_config *config, struct i2c_adapter *i2c)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-#endif /* HD29L2_H */
diff --git a/drivers/media/dvb/frontends/hd29l2_priv.h b/drivers/media/dvb/frontends/hd29l2_priv.h
deleted file mode 100644
index ba16dc3ec2bd..000000000000
--- a/drivers/media/dvb/frontends/hd29l2_priv.h
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * HDIC HD29L2 DMB-TH demodulator driver
- *
- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D
- *
- * Author: Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 HD29L2_PRIV
-#define HD29L2_PRIV
-
-#include <linux/dvb/version.h>
-#include "dvb_frontend.h"
-#include "dvb_math.h"
-#include "hd29l2.h"
-
-#define LOG_PREFIX "hd29l2"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (hd29l2_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-#define HD29L2_XTAL 30400000 /* Hz */
-
-
-#define HD29L2_QAM4NR 0x00
-#define HD29L2_QAM4 0x01
-#define HD29L2_QAM16 0x02
-#define HD29L2_QAM32 0x03
-#define HD29L2_QAM64 0x04
-
-#define HD29L2_CODE_RATE_04 0x00
-#define HD29L2_CODE_RATE_06 0x08
-#define HD29L2_CODE_RATE_08 0x10
-
-#define HD29L2_PN945 0x00
-#define HD29L2_PN595 0x01
-#define HD29L2_PN420 0x02
-
-#define HD29L2_CARRIER_SINGLE 0x00
-#define HD29L2_CARRIER_MULTI 0x01
-
-#define HD29L2_INTERLEAVER_720 0x00
-#define HD29L2_INTERLEAVER_420 0x01
-
-struct reg_val {
- u8 reg;
- u8 val;
-};
-
-struct reg_mod_vals {
- u8 reg;
- u8 val[5];
-};
-
-struct hd29l2_priv {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct hd29l2_config cfg;
- u8 tuner_i2c_addr_programmed:1;
-
- fe_status_t fe_status;
-};
-
-static const struct reg_mod_vals reg_mod_vals_tab[] = {
- /* REG, QAM4NR, QAM4,QAM16,QAM32,QAM64 */
- { 0x01, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
- { 0x02, { 0x07, 0x07, 0x07, 0x07, 0x07 } },
- { 0x03, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
- { 0x04, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x05, { 0x61, 0x60, 0x60, 0x61, 0x60 } },
- { 0x06, { 0xff, 0xff, 0xff, 0xff, 0xff } },
- { 0x07, { 0xff, 0xff, 0xff, 0xff, 0xff } },
- { 0x08, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x09, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x0a, { 0x15, 0x15, 0x03, 0x03, 0x03 } },
- { 0x0d, { 0x78, 0x78, 0x88, 0x78, 0x78 } },
- { 0x0e, { 0xa0, 0x90, 0xa0, 0xa0, 0xa0 } },
- { 0x0f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x10, { 0xa0, 0xa0, 0x58, 0x38, 0x38 } },
- { 0x11, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x12, { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a } },
- { 0x13, { 0xa2, 0xa2, 0xa2, 0xa2, 0xa2 } },
- { 0x17, { 0x40, 0x40, 0x40, 0x40, 0x40 } },
- { 0x18, { 0x21, 0x21, 0x42, 0x52, 0x42 } },
- { 0x19, { 0x21, 0x21, 0x62, 0x72, 0x62 } },
- { 0x1a, { 0x32, 0x43, 0xa9, 0xb9, 0xa9 } },
- { 0x1b, { 0x32, 0x43, 0xb9, 0xd8, 0xb9 } },
- { 0x1c, { 0x02, 0x02, 0x03, 0x02, 0x03 } },
- { 0x1d, { 0x0c, 0x0c, 0x01, 0x02, 0x02 } },
- { 0x1e, { 0x02, 0x02, 0x02, 0x01, 0x02 } },
- { 0x1f, { 0x02, 0x02, 0x01, 0x02, 0x04 } },
- { 0x20, { 0x01, 0x02, 0x01, 0x01, 0x01 } },
- { 0x21, { 0x08, 0x08, 0x0a, 0x0a, 0x0a } },
- { 0x22, { 0x06, 0x06, 0x04, 0x05, 0x05 } },
- { 0x23, { 0x06, 0x06, 0x05, 0x03, 0x05 } },
- { 0x24, { 0x08, 0x08, 0x05, 0x07, 0x07 } },
- { 0x25, { 0x16, 0x10, 0x10, 0x0a, 0x10 } },
- { 0x26, { 0x14, 0x14, 0x04, 0x04, 0x04 } },
- { 0x27, { 0x58, 0x58, 0x58, 0x5c, 0x58 } },
- { 0x28, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0x29, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0x2a, { 0x08, 0x0a, 0x08, 0x08, 0x08 } },
- { 0x2b, { 0x08, 0x08, 0x08, 0x08, 0x08 } },
- { 0x2c, { 0x06, 0x06, 0x06, 0x06, 0x06 } },
- { 0x2d, { 0x05, 0x06, 0x06, 0x06, 0x06 } },
- { 0x2e, { 0x21, 0x21, 0x21, 0x21, 0x21 } },
- { 0x2f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x30, { 0x14, 0x14, 0x14, 0x14, 0x14 } },
- { 0x33, { 0xb7, 0xb7, 0xb7, 0xb7, 0xb7 } },
- { 0x34, { 0x81, 0x81, 0x81, 0x81, 0x81 } },
- { 0x35, { 0x80, 0x80, 0x80, 0x80, 0x80 } },
- { 0x37, { 0x70, 0x70, 0x70, 0x70, 0x70 } },
- { 0x38, { 0x04, 0x04, 0x02, 0x02, 0x02 } },
- { 0x39, { 0x07, 0x07, 0x05, 0x05, 0x05 } },
- { 0x3a, { 0x06, 0x06, 0x06, 0x06, 0x06 } },
- { 0x3b, { 0x03, 0x03, 0x03, 0x03, 0x03 } },
- { 0x3c, { 0x07, 0x06, 0x04, 0x04, 0x04 } },
- { 0x3d, { 0xf0, 0xf0, 0xf0, 0xf0, 0x80 } },
- { 0x3e, { 0x60, 0x60, 0x60, 0x60, 0xff } },
- { 0x3f, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x40, { 0x5b, 0x5b, 0x5b, 0x57, 0x50 } },
- { 0x41, { 0x30, 0x30, 0x30, 0x30, 0x18 } },
- { 0x42, { 0x20, 0x20, 0x20, 0x00, 0x30 } },
- { 0x43, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x44, { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f } },
- { 0x45, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x46, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0x47, { 0x00, 0x00, 0x95, 0x00, 0x95 } },
- { 0x48, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } },
- { 0x49, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } },
- { 0x4a, { 0x40, 0x40, 0x33, 0x11, 0x11 } },
- { 0x4b, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
- { 0x4c, { 0x40, 0x40, 0x99, 0x11, 0x11 } },
- { 0x4d, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
- { 0x4e, { 0x40, 0x40, 0x66, 0x77, 0x77 } },
- { 0x4f, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
- { 0x50, { 0x40, 0x40, 0x88, 0x33, 0x11 } },
- { 0x51, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
- { 0x52, { 0x40, 0x40, 0x88, 0x02, 0x02 } },
- { 0x53, { 0x40, 0x40, 0x00, 0x02, 0x02 } },
- { 0x54, { 0x00, 0x00, 0x88, 0x33, 0x33 } },
- { 0x55, { 0x40, 0x40, 0x00, 0x00, 0x00 } },
- { 0x56, { 0x00, 0x00, 0x00, 0x0b, 0x00 } },
- { 0x57, { 0x40, 0x40, 0x0a, 0x0b, 0x0a } },
- { 0x58, { 0xaa, 0x00, 0x00, 0x00, 0x00 } },
- { 0x59, { 0x7a, 0x40, 0x02, 0x02, 0x02 } },
- { 0x5a, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
- { 0x5b, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
- { 0x5c, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
- { 0x5d, { 0x18, 0x18, 0x01, 0x01, 0x01 } },
- { 0x5e, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } },
- { 0x5f, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } },
- { 0x60, { 0x40, 0x40, 0x00, 0x30, 0x30 } },
- { 0x61, { 0x40, 0x40, 0x10, 0x30, 0x30 } },
- { 0x62, { 0x40, 0x40, 0x00, 0x30, 0x30 } },
- { 0x63, { 0x40, 0x40, 0x05, 0x30, 0x30 } },
- { 0x64, { 0x40, 0x40, 0x06, 0x00, 0x30 } },
- { 0x65, { 0x40, 0x40, 0x06, 0x08, 0x30 } },
- { 0x66, { 0x40, 0x40, 0x00, 0x00, 0x20 } },
- { 0x67, { 0x40, 0x40, 0x01, 0x04, 0x20 } },
- { 0x68, { 0x00, 0x00, 0x30, 0x00, 0x20 } },
- { 0x69, { 0xa0, 0xa0, 0x00, 0x08, 0x20 } },
- { 0x6a, { 0x00, 0x00, 0x30, 0x00, 0x25 } },
- { 0x6b, { 0xa0, 0xa0, 0x00, 0x06, 0x25 } },
- { 0x6c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x6d, { 0xa0, 0x60, 0x0c, 0x03, 0x0c } },
- { 0x6e, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x6f, { 0xa0, 0x60, 0x04, 0x01, 0x04 } },
- { 0x70, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } },
- { 0x71, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } },
- { 0x72, { 0x58, 0x58, 0xff, 0xff, 0xff } },
- { 0x73, { 0x58, 0x58, 0xff, 0xff, 0xff } },
- { 0x74, { 0x06, 0x06, 0x09, 0x05, 0x05 } },
- { 0x75, { 0x06, 0x06, 0x0a, 0x10, 0x10 } },
- { 0x76, { 0x10, 0x10, 0x06, 0x0a, 0x0a } },
- { 0x77, { 0x12, 0x18, 0x28, 0x10, 0x28 } },
- { 0x78, { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8 } },
- { 0x79, { 0x15, 0x15, 0x03, 0x03, 0x03 } },
- { 0x7a, { 0x02, 0x02, 0x01, 0x04, 0x03 } },
- { 0x7b, { 0x01, 0x02, 0x03, 0x03, 0x03 } },
- { 0x7c, { 0x28, 0x28, 0x28, 0x28, 0x28 } },
- { 0x7f, { 0x25, 0x92, 0x5f, 0x17, 0x2d } },
- { 0x80, { 0x64, 0x64, 0x64, 0x74, 0x64 } },
- { 0x83, { 0x06, 0x03, 0x04, 0x04, 0x04 } },
- { 0x84, { 0xff, 0xff, 0xff, 0xff, 0xff } },
- { 0x85, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
- { 0x86, { 0x00, 0x00, 0x11, 0x11, 0x11 } },
- { 0x87, { 0x03, 0x03, 0x03, 0x03, 0x03 } },
- { 0x88, { 0x09, 0x09, 0x09, 0x09, 0x09 } },
- { 0x89, { 0x20, 0x20, 0x30, 0x20, 0x20 } },
- { 0x8a, { 0x03, 0x03, 0x02, 0x03, 0x02 } },
- { 0x8b, { 0x00, 0x07, 0x09, 0x00, 0x09 } },
- { 0x8c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x8d, { 0x4f, 0x4f, 0x4f, 0x3f, 0x4f } },
- { 0x8e, { 0xf0, 0xf0, 0x60, 0xf0, 0xa0 } },
- { 0x8f, { 0xe8, 0xe8, 0xe8, 0xe8, 0xe8 } },
- { 0x90, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
- { 0x91, { 0x40, 0x40, 0x70, 0x70, 0x10 } },
- { 0x92, { 0x00, 0x00, 0x00, 0x00, 0x04 } },
- { 0x93, { 0x60, 0x60, 0x60, 0x60, 0x60 } },
- { 0x94, { 0x00, 0x00, 0x00, 0x00, 0x03 } },
- { 0x95, { 0x09, 0x09, 0x47, 0x47, 0x47 } },
- { 0x96, { 0x80, 0xa0, 0xa0, 0x40, 0xa0 } },
- { 0x97, { 0x60, 0x60, 0x60, 0x60, 0x60 } },
- { 0x98, { 0x50, 0x50, 0x50, 0x30, 0x50 } },
- { 0x99, { 0x10, 0x10, 0x10, 0x10, 0x10 } },
- { 0x9a, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0x9b, { 0x40, 0x40, 0x40, 0x30, 0x40 } },
- { 0x9c, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa0, { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 } },
- { 0xa1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa2, { 0x30, 0x30, 0x00, 0x30, 0x00 } },
- { 0xa3, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa4, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa5, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xa8, { 0x77, 0x77, 0x77, 0x77, 0x77 } },
- { 0xa9, { 0x02, 0x02, 0x02, 0x02, 0x02 } },
- { 0xaa, { 0x40, 0x40, 0x40, 0x40, 0x40 } },
- { 0xac, { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f } },
- { 0xad, { 0x14, 0x14, 0x14, 0x14, 0x14 } },
- { 0xae, { 0x78, 0x78, 0x78, 0x78, 0x78 } },
- { 0xaf, { 0x06, 0x06, 0x06, 0x06, 0x07 } },
- { 0xb0, { 0x1b, 0x1b, 0x1b, 0x19, 0x1b } },
- { 0xb1, { 0x18, 0x17, 0x17, 0x18, 0x17 } },
- { 0xb2, { 0x35, 0x82, 0x82, 0x38, 0x82 } },
- { 0xb3, { 0xb6, 0xce, 0xc7, 0x5c, 0xb0 } },
- { 0xb4, { 0x3f, 0x3e, 0x3e, 0x3f, 0x3e } },
- { 0xb5, { 0x70, 0x58, 0x50, 0x68, 0x50 } },
- { 0xb6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xb7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xb8, { 0x03, 0x03, 0x01, 0x01, 0x01 } },
- { 0xb9, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xba, { 0x06, 0x06, 0x0a, 0x05, 0x0a } },
- { 0xbb, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xbc, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xbd, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xbe, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xbf, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc3, { 0x00, 0x00, 0x88, 0x66, 0x88 } },
- { 0xc4, { 0x10, 0x10, 0x00, 0x00, 0x00 } },
- { 0xc5, { 0x00, 0x00, 0x44, 0x60, 0x44 } },
- { 0xc6, { 0x10, 0x0a, 0x00, 0x00, 0x00 } },
- { 0xc7, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc8, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xc9, { 0x90, 0x04, 0x00, 0x00, 0x00 } },
- { 0xca, { 0x90, 0x08, 0x01, 0x01, 0x01 } },
- { 0xcb, { 0xa0, 0x04, 0x00, 0x44, 0x00 } },
- { 0xcc, { 0xa0, 0x10, 0x03, 0x00, 0x03 } },
- { 0xcd, { 0x06, 0x06, 0x06, 0x05, 0x06 } },
- { 0xce, { 0x05, 0x05, 0x01, 0x01, 0x01 } },
- { 0xcf, { 0x40, 0x20, 0x18, 0x18, 0x18 } },
- { 0xd0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xd1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xd2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xd3, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xd4, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
- { 0xd5, { 0x05, 0x05, 0x05, 0x03, 0x05 } },
- { 0xd6, { 0xac, 0x22, 0xca, 0x8f, 0xca } },
- { 0xd7, { 0x20, 0x20, 0x20, 0x20, 0x20 } },
- { 0xd8, { 0x01, 0x01, 0x01, 0x01, 0x01 } },
- { 0xd9, { 0x00, 0x00, 0x0f, 0x00, 0x0f } },
- { 0xda, { 0x00, 0xff, 0xff, 0x0e, 0xff } },
- { 0xdb, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0xdc, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0xdd, { 0x05, 0x05, 0x05, 0x05, 0x05 } },
- { 0xde, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0xdf, { 0x42, 0x42, 0x44, 0x44, 0x04 } },
- { 0xe0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xe1, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xe2, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xe3, { 0x00, 0x00, 0x26, 0x06, 0x26 } },
- { 0xe4, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xe5, { 0x01, 0x0a, 0x01, 0x01, 0x01 } },
- { 0xe6, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xe7, { 0x08, 0x08, 0x08, 0x08, 0x08 } },
- { 0xe8, { 0x63, 0x63, 0x63, 0x63, 0x63 } },
- { 0xe9, { 0x59, 0x59, 0x59, 0x59, 0x59 } },
- { 0xea, { 0x80, 0x80, 0x20, 0x80, 0x80 } },
- { 0xeb, { 0x37, 0x37, 0x78, 0x37, 0x77 } },
- { 0xec, { 0x1f, 0x1f, 0x25, 0x25, 0x25 } },
- { 0xed, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } },
- { 0xee, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
- { 0xef, { 0x70, 0x70, 0x58, 0x38, 0x58 } },
- { 0xf0, { 0x00, 0x00, 0x00, 0x00, 0x00 } },
-};
-
-#endif /* HD29L2_PRIV */
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
deleted file mode 100644
index 708cbf197913..000000000000
--- a/drivers/media/dvb/frontends/it913x-fe.c
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- * Driver for it913x-fe Frontend
- *
- * with support for on chip it9137 integral tuner
- *
- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
- * IT9137 Copyright (C) ITE Tech Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include "dvb_frontend.h"
-#include "it913x-fe.h"
-#include "it913x-fe-priv.h"
-
-static int it913x_debug;
-
-module_param_named(debug, it913x_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
-
-#define dprintk(level, args...) do { \
- if (level & it913x_debug) \
- printk(KERN_DEBUG "it913x-fe: " args); \
-} while (0)
-
-#define deb_info(args...) dprintk(0x01, args)
-#define debug_data_snipet(level, name, p) \
- dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
- *p, *(p+1), *(p+2), *(p+3), *(p+4), \
- *(p+5), *(p+6), *(p+7));
-#define info(format, arg...) \
- printk(KERN_INFO "it913x-fe: " format "\n" , ## arg)
-
-struct it913x_fe_state {
- struct dvb_frontend frontend;
- struct i2c_adapter *i2c_adap;
- struct ite_config *config;
- u8 i2c_addr;
- u32 frequency;
- fe_modulation_t constellation;
- fe_transmit_mode_t transmission_mode;
- u8 priority;
- u32 crystalFrequency;
- u32 adcFrequency;
- u8 tuner_type;
- struct adctable *table;
- fe_status_t it913x_status;
- u16 tun_xtal;
- u8 tun_fdiv;
- u8 tun_clk_mode;
- u32 tun_fn_min;
- u32 ucblocks;
-};
-
-static int it913x_read_reg(struct it913x_fe_state *state,
- u32 reg, u8 *data, u8 count)
-{
- int ret;
- u8 pro = PRO_DMOD; /* All reads from demodulator */
- u8 b[4];
- struct i2c_msg msg[2] = {
- { .addr = state->i2c_addr + (pro << 1), .flags = 0,
- .buf = b, .len = sizeof(b) },
- { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD,
- .buf = data, .len = count }
- };
- b[0] = (u8) reg >> 24;
- b[1] = (u8)(reg >> 16) & 0xff;
- b[2] = (u8)(reg >> 8) & 0xff;
- b[3] = (u8) reg & 0xff;
-
- ret = i2c_transfer(state->i2c_adap, msg, 2);
-
- return ret;
-}
-
-static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg)
-{
- int ret;
- u8 b[1];
- ret = it913x_read_reg(state, reg, &b[0], sizeof(b));
- return (ret < 0) ? -ENODEV : b[0];
-}
-
-static int it913x_write(struct it913x_fe_state *state,
- u8 pro, u32 reg, u8 buf[], u8 count)
-{
- u8 b[256];
- struct i2c_msg msg[1] = {
- { .addr = state->i2c_addr + (pro << 1), .flags = 0,
- .buf = b, .len = count + 4 }
- };
- int ret;
-
- b[0] = (u8) reg >> 24;
- b[1] = (u8)(reg >> 16) & 0xff;
- b[2] = (u8)(reg >> 8) & 0xff;
- b[3] = (u8) reg & 0xff;
- memcpy(&b[4], buf, count);
-
- ret = i2c_transfer(state->i2c_adap, msg, 1);
-
- if (ret < 0)
- return -EIO;
-
- return 0;
-}
-
-static int it913x_write_reg(struct it913x_fe_state *state,
- u8 pro, u32 reg, u32 data)
-{
- int ret;
- u8 b[4];
- u8 s;
-
- b[0] = data >> 24;
- b[1] = (data >> 16) & 0xff;
- b[2] = (data >> 8) & 0xff;
- b[3] = data & 0xff;
- /* expand write as needed */
- if (data < 0x100)
- s = 3;
- else if (data < 0x1000)
- s = 2;
- else if (data < 0x100000)
- s = 1;
- else
- s = 0;
-
- ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s);
-
- return ret;
-}
-
-static int it913x_fe_script_loader(struct it913x_fe_state *state,
- struct it913xset *loadscript)
-{
- int ret, i;
- if (loadscript == NULL)
- return -EINVAL;
-
- for (i = 0; i < 1000; ++i) {
- if (loadscript[i].pro == 0xff)
- break;
- ret = it913x_write(state, loadscript[i].pro,
- loadscript[i].address,
- loadscript[i].reg, loadscript[i].count);
- if (ret < 0)
- return -ENODEV;
- }
- return 0;
-}
-
-static int it913x_init_tuner(struct it913x_fe_state *state)
-{
- int ret, i, reg;
- u8 val, nv_val;
- u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2};
- u8 b[2];
-
- reg = it913x_read_reg_u8(state, 0xec86);
- switch (reg) {
- case 0:
- state->tun_clk_mode = reg;
- state->tun_xtal = 2000;
- state->tun_fdiv = 3;
- val = 16;
- break;
- case -ENODEV:
- return -ENODEV;
- case 1:
- default:
- state->tun_clk_mode = reg;
- state->tun_xtal = 640;
- state->tun_fdiv = 1;
- val = 6;
- break;
- }
-
- reg = it913x_read_reg_u8(state, 0xed03);
-
- if (reg < 0)
- return -ENODEV;
- else if (reg < sizeof(nv))
- nv_val = nv[reg];
- else
- nv_val = 2;
-
- for (i = 0; i < 50; i++) {
- ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b));
- reg = (b[1] << 8) + b[0];
- if (reg > 0)
- break;
- if (ret < 0)
- return -ENODEV;
- udelay(2000);
- }
- state->tun_fn_min = state->tun_xtal * reg;
- state->tun_fn_min /= (state->tun_fdiv * nv_val);
- deb_info("Tuner fn_min %d", state->tun_fn_min);
-
- if (state->config->chip_ver > 1)
- msleep(50);
- else {
- for (i = 0; i < 50; i++) {
- reg = it913x_read_reg_u8(state, 0xec82);
- if (reg > 0)
- break;
- if (reg < 0)
- return -ENODEV;
- udelay(2000);
- }
- }
-
- return it913x_write_reg(state, PRO_DMOD, 0xed81, val);
-}
-
-static int it9137_set_tuner(struct it913x_fe_state *state,
- u32 bandwidth, u32 frequency_m)
-{
- struct it913xset *set_tuner = set_it9137_template;
- int ret, reg;
- u32 frequency = frequency_m / 1000;
- u32 freq, temp_f, tmp;
- u16 iqik_m_cal;
- u16 n_div;
- u8 n;
- u8 l_band;
- u8 lna_band;
- u8 bw;
-
- if (state->config->firmware_ver == 1)
- set_tuner = set_it9135_template;
- else
- set_tuner = set_it9137_template;
-
- deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth);
-
- if (frequency >= 51000 && frequency <= 440000) {
- l_band = 0;
- lna_band = 0;
- } else if (frequency > 440000 && frequency <= 484000) {
- l_band = 1;
- lna_band = 1;
- } else if (frequency > 484000 && frequency <= 533000) {
- l_band = 1;
- lna_band = 2;
- } else if (frequency > 533000 && frequency <= 587000) {
- l_band = 1;
- lna_band = 3;
- } else if (frequency > 587000 && frequency <= 645000) {
- l_band = 1;
- lna_band = 4;
- } else if (frequency > 645000 && frequency <= 710000) {
- l_band = 1;
- lna_band = 5;
- } else if (frequency > 710000 && frequency <= 782000) {
- l_band = 1;
- lna_band = 6;
- } else if (frequency > 782000 && frequency <= 860000) {
- l_band = 1;
- lna_band = 7;
- } else if (frequency > 1450000 && frequency <= 1492000) {
- l_band = 1;
- lna_band = 0;
- } else if (frequency > 1660000 && frequency <= 1685000) {
- l_band = 1;
- lna_band = 1;
- } else
- return -EINVAL;
- set_tuner[0].reg[0] = lna_band;
-
- switch (bandwidth) {
- case 5000000:
- bw = 0;
- break;
- case 6000000:
- bw = 2;
- break;
- case 7000000:
- bw = 4;
- break;
- default:
- case 8000000:
- bw = 6;
- break;
- }
-
- set_tuner[1].reg[0] = bw;
- set_tuner[2].reg[0] = 0xa0 | (l_band << 3);
-
- if (frequency > 53000 && frequency <= 74000) {
- n_div = 48;
- n = 0;
- } else if (frequency > 74000 && frequency <= 111000) {
- n_div = 32;
- n = 1;
- } else if (frequency > 111000 && frequency <= 148000) {
- n_div = 24;
- n = 2;
- } else if (frequency > 148000 && frequency <= 222000) {
- n_div = 16;
- n = 3;
- } else if (frequency > 222000 && frequency <= 296000) {
- n_div = 12;
- n = 4;
- } else if (frequency > 296000 && frequency <= 445000) {
- n_div = 8;
- n = 5;
- } else if (frequency > 445000 && frequency <= state->tun_fn_min) {
- n_div = 6;
- n = 6;
- } else if (frequency > state->tun_fn_min && frequency <= 950000) {
- n_div = 4;
- n = 7;
- } else if (frequency > 1450000 && frequency <= 1680000) {
- n_div = 2;
- n = 0;
- } else
- return -EINVAL;
-
- reg = it913x_read_reg_u8(state, 0xed81);
- iqik_m_cal = (u16)reg * n_div;
-
- if (reg < 0x20) {
- if (state->tun_clk_mode == 0)
- iqik_m_cal = (iqik_m_cal * 9) >> 5;
- else
- iqik_m_cal >>= 1;
- } else {
- iqik_m_cal = 0x40 - iqik_m_cal;
- if (state->tun_clk_mode == 0)
- iqik_m_cal = ~((iqik_m_cal * 9) >> 5);
- else
- iqik_m_cal = ~(iqik_m_cal >> 1);
- }
-
- temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv;
- freq = temp_f / state->tun_xtal;
- tmp = freq * state->tun_xtal;
-
- if ((temp_f - tmp) >= (state->tun_xtal >> 1))
- freq++;
-
- freq += (u32) n << 13;
- /* Frequency OMEGA_IQIK_M_CAL_MID*/
- temp_f = freq + (u32)iqik_m_cal;
-
- set_tuner[3].reg[0] = temp_f & 0xff;
- set_tuner[4].reg[0] = (temp_f >> 8) & 0xff;
-
- deb_info("High Frequency = %04x", temp_f);
-
- /* Lower frequency */
- set_tuner[5].reg[0] = freq & 0xff;
- set_tuner[6].reg[0] = (freq >> 8) & 0xff;
-
- deb_info("low Frequency = %04x", freq);
-
- ret = it913x_fe_script_loader(state, set_tuner);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_select_bw(struct it913x_fe_state *state,
- u32 bandwidth, u32 adcFrequency)
-{
- int ret, i;
- u8 buffer[256];
- u32 coeff[8];
- u16 bfsfcw_fftinx_ratio;
- u16 fftinx_bfsfcw_ratio;
- u8 count;
- u8 bw;
- u8 adcmultiplier;
-
- deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency);
-
- switch (bandwidth) {
- case 5000000:
- bw = 3;
- break;
- case 6000000:
- bw = 0;
- break;
- case 7000000:
- bw = 1;
- break;
- default:
- case 8000000:
- bw = 2;
- break;
- }
- ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw);
-
- if (state->table == NULL)
- return -EINVAL;
-
- /* In write order */
- coeff[0] = state->table[bw].coeff_1_2048;
- coeff[1] = state->table[bw].coeff_2_2k;
- coeff[2] = state->table[bw].coeff_1_8191;
- coeff[3] = state->table[bw].coeff_1_8192;
- coeff[4] = state->table[bw].coeff_1_8193;
- coeff[5] = state->table[bw].coeff_2_8k;
- coeff[6] = state->table[bw].coeff_1_4096;
- coeff[7] = state->table[bw].coeff_2_4k;
- bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio;
- fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio;
-
- /* ADC multiplier */
- ret = it913x_read_reg_u8(state, ADC_X_2);
- if (ret < 0)
- return -EINVAL;
-
- adcmultiplier = ret;
-
- count = 0;
-
- /* Build Buffer for COEFF Registers */
- for (i = 0; i < 8; i++) {
- if (adcmultiplier == 1)
- coeff[i] /= 2;
- buffer[count++] = (coeff[i] >> 24) & 0x3;
- buffer[count++] = (coeff[i] >> 16) & 0xff;
- buffer[count++] = (coeff[i] >> 8) & 0xff;
- buffer[count++] = coeff[i] & 0xff;
- }
-
- /* bfsfcw_fftinx_ratio register 0x21-0x22 */
- buffer[count++] = bfsfcw_fftinx_ratio & 0xff;
- buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff;
- /* fftinx_bfsfcw_ratio register 0x23-0x24 */
- buffer[count++] = fftinx_bfsfcw_ratio & 0xff;
- buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff;
- /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/
- ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count);
-
- for (i = 0; i < 42; i += 8)
- debug_data_snipet(0x1, "Buffer", &buffer[i]);
-
- return ret;
-}
-
-
-
-static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret, i;
- fe_status_t old_status = state->it913x_status;
- *status = 0;
-
- if (state->it913x_status == 0) {
- ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
- if (ret == 0x1) {
- *status |= FE_HAS_SIGNAL;
- for (i = 0; i < 40; i++) {
- ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK);
- if (ret == 0x1)
- break;
- msleep(25);
- }
- if (ret == 0x1)
- *status |= FE_HAS_CARRIER
- | FE_HAS_VITERBI
- | FE_HAS_SYNC;
- state->it913x_status = *status;
- }
- }
-
- if (state->it913x_status & FE_HAS_SYNC) {
- ret = it913x_read_reg_u8(state, TPSD_LOCK);
- if (ret == 0x1)
- *status |= FE_HAS_LOCK
- | state->it913x_status;
- else
- state->it913x_status = 0;
- if (old_status != state->it913x_status)
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret);
- }
-
- return 0;
-}
-
-/* FEC values based on fe_code_rate_t non supported values 0*/
-int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
-int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
-int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
-
-static int it913x_get_signal_strength(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 code_rate;
- int ret, temp;
- u8 lna_gain_os;
-
- ret = it913x_read_reg_u8(state, VAR_P_INBAND);
- if (ret < 0)
- return ret;
-
- /* VHF/UHF gain offset */
- if (state->frequency < 300000000)
- lna_gain_os = 7;
- else
- lna_gain_os = 14;
-
- temp = (ret - 100) - lna_gain_os;
-
- if (state->priority == PRIORITY_HIGH)
- code_rate = p->code_rate_HP;
- else
- code_rate = p->code_rate_LP;
-
- if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
- return -EINVAL;
-
- deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
-
- /* Apply FEC offset values*/
- switch (p->modulation) {
- case QPSK:
- temp -= it913x_qpsk_pval[code_rate];
- break;
- case QAM_16:
- temp -= it913x_16qam_pval[code_rate];
- break;
- case QAM_64:
- temp -= it913x_64qam_pval[code_rate];
- break;
- default:
- return -EINVAL;
- }
-
- if (temp < -15)
- ret = 0;
- else if ((-15 <= temp) && (temp < 0))
- ret = (2 * (temp + 15)) / 3;
- else if ((0 <= temp) && (temp < 20))
- ret = 4 * temp + 10;
- else if ((20 <= temp) && (temp < 35))
- ret = (2 * (temp - 20)) / 3 + 90;
- else if (temp >= 35)
- ret = 100;
-
- deb_info("Signal Strength :%d", ret);
-
- return ret;
-}
-
-static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
- u16 *strength)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = 0;
- if (state->config->read_slevel) {
- if (state->it913x_status & FE_HAS_SIGNAL)
- ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
- } else
- ret = it913x_get_signal_strength(fe);
-
- if (ret >= 0)
- *strength = (u16)((u32)ret * 0xffff / 0x64);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
- u8 reg[3];
- u32 snr_val, snr_min, snr_max;
- u32 temp;
-
- ret = it913x_read_reg(state, 0x2c, reg, sizeof(reg));
-
- snr_val = (u32)(reg[2] << 16) | (reg[1] << 8) | reg[0];
-
- ret |= it913x_read_reg(state, 0xf78b, reg, 1);
- if (reg[0])
- snr_val /= reg[0];
-
- if (state->transmission_mode == TRANSMISSION_MODE_2K)
- snr_val *= 4;
- else if (state->transmission_mode == TRANSMISSION_MODE_4K)
- snr_val *= 2;
-
- if (state->constellation == QPSK) {
- snr_min = 0xb4711;
- snr_max = 0x191451;
- } else if (state->constellation == QAM_16) {
- snr_min = 0x4f0d5;
- snr_max = 0xc7925;
- } else if (state->constellation == QAM_64) {
- snr_min = 0x256d0;
- snr_max = 0x626be;
- } else
- return -EINVAL;
-
- if (snr_val < snr_min)
- *snr = 0;
- else if (snr_val < snr_max) {
- temp = (snr_val - snr_min) >> 5;
- temp *= 0xffff;
- temp /= (snr_max - snr_min) >> 5;
- *snr = (u16)temp;
- } else
- *snr = 0xffff;
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 reg[5];
- /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */
- it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
- state->ucblocks += (u32)(reg[1] << 8) | reg[0];
- *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2];
- return 0;
-}
-
-static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
- u8 reg[2];
- /* Aborted Packets */
- ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
- state->ucblocks += (u32)(reg[1] << 8) | reg[0];
- *ucblocks = state->ucblocks;
- return ret;
-}
-
-static int it913x_fe_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- u8 reg[8];
-
- it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
-
- if (reg[3] < 3)
- p->modulation = fe_con[reg[3]];
-
- if (reg[0] < 3)
- p->transmission_mode = fe_mode[reg[0]];
-
- if (reg[1] < 4)
- p->guard_interval = fe_gi[reg[1]];
-
- if (reg[2] < 4)
- p->hierarchy = fe_hi[reg[2]];
-
- state->priority = reg[5];
-
- p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
- p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
-
- /* Update internal state to reflect the autodetected props */
- state->constellation = p->modulation;
- state->transmission_mode = p->transmission_mode;
-
- return 0;
-}
-
-static int it913x_fe_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct it913x_fe_state *state = fe->demodulator_priv;
- int i;
- u8 empty_ch, last_ch;
-
- state->it913x_status = 0;
-
- /* Set bw*/
- it913x_fe_select_bw(state, p->bandwidth_hz,
- state->adcFrequency);
-
- /* Training Mode Off */
- it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
-
- /* Clear Empty Channel */
- it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
-
- /* Clear bits */
- it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
- /* LED on */
- it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
- /* Select Band*/
- if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
- i = 0;
- else if ((p->frequency >= 350000000) && (p->frequency <= 900000000))
- i = 1;
- else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000))
- i = 2;
- else
- return -EOPNOTSUPP;
-
- it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
-
- deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
- switch (state->tuner_type) {
- case IT9135_38:
- case IT9135_51:
- case IT9135_52:
- case IT9135_60:
- case IT9135_61:
- case IT9135_62:
- it9137_set_tuner(state,
- p->bandwidth_hz, p->frequency);
- break;
- default:
- if (fe->ops.tuner_ops.set_params) {
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- break;
- }
- /* LED off */
- it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
- /* Trigger ofsm */
- it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
- last_ch = 2;
- for (i = 0; i < 40; ++i) {
- empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
- if (last_ch == 1 && empty_ch == 1)
- break;
- if (last_ch == 2 && empty_ch == 2)
- return 0;
- last_ch = empty_ch;
- msleep(25);
- }
- for (i = 0; i < 40; ++i) {
- if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1)
- break;
- msleep(25);
- }
-
- state->frequency = p->frequency;
- return 0;
-}
-
-static int it913x_fe_suspend(struct it913x_fe_state *state)
-{
- int ret, i;
- u8 b;
-
- ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1);
-
- ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
-
- for (i = 0; i < 128; i++) {
- ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1);
- if (ret < 0)
- return -ENODEV;
- if (b == 0)
- break;
-
- }
-
- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8);
- /* Turn LED off */
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
-
- ret |= it913x_fe_script_loader(state, it9137_tuner_off);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-/* Power sequence */
-/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */
-/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */
-
-static int it913x_fe_sleep(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- return it913x_fe_suspend(state);
-}
-
-static u32 compute_div(u32 a, u32 b, u32 x)
-{
- u32 res = 0;
- u32 c = 0;
- u32 i = 0;
-
- if (a > b) {
- c = a / b;
- a = a - c * b;
- }
-
- for (i = 0; i < x; i++) {
- if (a >= b) {
- res += 1;
- a -= b;
- }
- a <<= 1;
- res <<= 1;
- }
-
- res = (c << x) + res;
-
- return res;
-}
-
-static int it913x_fe_start(struct it913x_fe_state *state)
-{
- struct it913xset *set_lna;
- struct it913xset *set_mode;
- int ret;
- u8 adf = (state->config->adf & 0xf);
- u32 adc, xtal;
- u8 b[4];
-
- if (state->config->chip_ver == 1)
- ret = it913x_init_tuner(state);
-
- info("ADF table value :%02x", adf);
-
- if (adf < 10) {
- state->crystalFrequency = fe_clockTable[adf].xtal ;
- state->table = fe_clockTable[adf].table;
- state->adcFrequency = state->table->adcFrequency;
-
- adc = compute_div(state->adcFrequency, 1000000ul, 19ul);
- xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul);
-
- } else
- return -EINVAL;
-
- /* Set LED indicator on GPIOH3 */
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
-
- ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type);
- ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01);
- ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01);
-
- b[0] = xtal & 0xff;
- b[1] = (xtal >> 8) & 0xff;
- b[2] = (xtal >> 16) & 0xff;
- b[3] = (xtal >> 24);
- ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4);
-
- b[0] = adc & 0xff;
- b[1] = (adc >> 8) & 0xff;
- b[2] = (adc >> 16) & 0xff;
- ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3);
-
- if (state->config->adc_x2)
- ret |= it913x_write_reg(state, PRO_DMOD, ADC_X_2, 0x01);
- b[0] = 0;
- b[1] = 0;
- b[2] = 0;
- ret |= it913x_write(state, PRO_DMOD, 0x0029, b, 3);
-
- info("Crystal Frequency :%d Adc Frequency :%d ADC X2: %02x",
- state->crystalFrequency, state->adcFrequency,
- state->config->adc_x2);
- deb_info("Xtal value :%04x Adc value :%04x", xtal, adc);
-
- if (ret < 0)
- return -ENODEV;
-
- /* v1 or v2 tuner script */
- if (state->config->chip_ver > 1)
- ret = it913x_fe_script_loader(state, it9135_v2);
- else
- ret = it913x_fe_script_loader(state, it9135_v1);
- if (ret < 0)
- return ret;
-
- /* LNA Scripts */
- switch (state->tuner_type) {
- case IT9135_51:
- set_lna = it9135_51;
- break;
- case IT9135_52:
- set_lna = it9135_52;
- break;
- case IT9135_60:
- set_lna = it9135_60;
- break;
- case IT9135_61:
- set_lna = it9135_61;
- break;
- case IT9135_62:
- set_lna = it9135_62;
- break;
- case IT9135_38:
- default:
- set_lna = it9135_38;
- }
- info("Tuner LNA type :%02x", state->tuner_type);
-
- ret = it913x_fe_script_loader(state, set_lna);
- if (ret < 0)
- return ret;
-
- if (state->config->chip_ver == 2) {
- ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x1);
- ret |= it913x_write_reg(state, PRO_LINK, PADODPU, 0x0);
- ret |= it913x_write_reg(state, PRO_LINK, AGC_O_D, 0x0);
- ret |= it913x_init_tuner(state);
- }
- if (ret < 0)
- return -ENODEV;
-
- /* Always solo frontend */
- set_mode = set_solo_fe;
- ret |= it913x_fe_script_loader(state, set_mode);
-
- ret |= it913x_fe_suspend(state);
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static int it913x_fe_init(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = 0;
- /* Power Up Tuner - common all versions */
- ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1);
-
- ret |= it913x_fe_script_loader(state, init_1);
-
- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0);
-
- ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0);
-
- return (ret < 0) ? -ENODEV : 0;
-}
-
-static void it913x_fe_release(struct dvb_frontend *fe)
-{
- struct it913x_fe_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops it913x_fe_ofdm_ops;
-
-struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap,
- u8 i2c_addr, struct ite_config *config)
-{
- struct it913x_fe_state *state = NULL;
- int ret;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL);
- if (state == NULL)
- return NULL;
- if (config == NULL)
- goto error;
-
- state->i2c_adap = i2c_adap;
- state->i2c_addr = i2c_addr;
- state->config = config;
-
- switch (state->config->tuner_id_0) {
- case IT9135_51:
- case IT9135_52:
- case IT9135_60:
- case IT9135_61:
- case IT9135_62:
- state->tuner_type = state->config->tuner_id_0;
- break;
- default:
- case IT9135_38:
- state->tuner_type = IT9135_38;
- }
-
- ret = it913x_fe_start(state);
- if (ret < 0)
- goto error;
-
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
-
- return &state->frontend;
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(it913x_fe_attach);
-
-static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "it913x-fe DVB-T",
- .frequency_min = 51000000,
- .frequency_max = 1680000000,
- .frequency_stepsize = 62500,
- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 |
- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO,
- },
-
- .release = it913x_fe_release,
-
- .init = it913x_fe_init,
- .sleep = it913x_fe_sleep,
-
- .set_frontend = it913x_fe_set_frontend,
- .get_frontend = it913x_fe_get_frontend,
-
- .read_status = it913x_fe_read_status,
- .read_signal_strength = it913x_fe_read_signal_strength,
- .read_snr = it913x_fe_read_snr,
- .read_ber = it913x_fe_read_ber,
- .read_ucblocks = it913x_fe_read_ucblocks,
-};
-
-MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
-MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
-MODULE_VERSION("1.15");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c
deleted file mode 100644
index 2cec8041a106..000000000000
--- a/drivers/media/dvb/frontends/lgs8gl5.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver
-
- Copyright (C) 2008 Sirius International (Hong Kong) Limited
- Timothy Lee <timothy.lee@siriushk.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include "dvb_frontend.h"
-#include "lgs8gl5.h"
-
-
-#define REG_RESET 0x02
-#define REG_RESET_OFF 0x01
-#define REG_03 0x03
-#define REG_04 0x04
-#define REG_07 0x07
-#define REG_09 0x09
-#define REG_0A 0x0a
-#define REG_0B 0x0b
-#define REG_0C 0x0c
-#define REG_37 0x37
-#define REG_STRENGTH 0x4b
-#define REG_STRENGTH_MASK 0x7f
-#define REG_STRENGTH_CARRIER 0x80
-#define REG_INVERSION 0x7c
-#define REG_INVERSION_ON 0x80
-#define REG_7D 0x7d
-#define REG_7E 0x7e
-#define REG_A2 0xa2
-#define REG_STATUS 0xa4
-#define REG_STATUS_SYNC 0x04
-#define REG_STATUS_LOCK 0x01
-
-
-struct lgs8gl5_state {
- struct i2c_adapter *i2c;
- const struct lgs8gl5_config *config;
- struct dvb_frontend frontend;
-};
-
-
-static int debug;
-#define dprintk(args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG "lgs8gl5: " args); \
- } while (0)
-
-
-/* Writes into demod's register */
-static int
-lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
-{
- int ret;
- u8 buf[] = {reg, data};
- struct i2c_msg msg = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf,
- .len = 2
- };
-
- ret = i2c_transfer(state->i2c, &msg, 1);
- if (ret != 1)
- dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n",
- __func__, reg, data, ret);
- return (ret != 1) ? -1 : 0;
-}
-
-
-/* Reads from demod's register */
-static int
-lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg)
-{
- int ret;
- u8 b0[] = {reg};
- u8 b1[] = {0};
- struct i2c_msg msg[2] = {
- {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = b0,
- .len = 1
- },
- {
- .addr = state->config->demod_address,
- .flags = I2C_M_RD,
- .buf = b1,
- .len = 1
- }
- };
-
- ret = i2c_transfer(state->i2c, msg, 2);
- if (ret != 2)
- return -EIO;
-
- return b1[0];
-}
-
-
-static int
-lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
-{
- lgs8gl5_read_reg(state, reg);
- lgs8gl5_write_reg(state, reg, data);
- return 0;
-}
-
-
-/* Writes into alternate device's register */
-/* TODO: Find out what that device is for! */
-static int
-lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data)
-{
- int ret;
- u8 b0[] = {reg};
- u8 b1[] = {0};
- u8 b2[] = {reg, data};
- struct i2c_msg msg[3] = {
- {
- .addr = state->config->demod_address + 2,
- .flags = 0,
- .buf = b0,
- .len = 1
- },
- {
- .addr = state->config->demod_address + 2,
- .flags = I2C_M_RD,
- .buf = b1,
- .len = 1
- },
- {
- .addr = state->config->demod_address + 2,
- .flags = 0,
- .buf = b2,
- .len = 2
- },
- };
-
- ret = i2c_transfer(state->i2c, msg, 3);
- return (ret != 3) ? -1 : 0;
-}
-
-
-static void
-lgs8gl5_soft_reset(struct lgs8gl5_state *state)
-{
- u8 val;
-
- dprintk("%s\n", __func__);
-
- val = lgs8gl5_read_reg(state, REG_RESET);
- lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF);
- lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF);
- msleep(5);
-}
-
-
-/* Starts demodulation */
-static void
-lgs8gl5_start_demod(struct lgs8gl5_state *state)
-{
- u8 val;
- int n;
-
- dprintk("%s\n", __func__);
-
- lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
- lgs8gl5_soft_reset(state);
- lgs8gl5_update_reg(state, REG_07, 0x10);
- lgs8gl5_update_reg(state, REG_07, 0x10);
- lgs8gl5_write_reg(state, REG_09, 0x0e);
- lgs8gl5_write_reg(state, REG_0A, 0xe5);
- lgs8gl5_write_reg(state, REG_0B, 0x35);
- lgs8gl5_write_reg(state, REG_0C, 0x30);
-
- lgs8gl5_update_reg(state, REG_03, 0x00);
- lgs8gl5_update_reg(state, REG_7E, 0x01);
- lgs8gl5_update_alt_reg(state, 0xc5, 0x00);
- lgs8gl5_update_reg(state, REG_04, 0x02);
- lgs8gl5_update_reg(state, REG_37, 0x01);
- lgs8gl5_soft_reset(state);
-
- /* Wait for carrier */
- for (n = 0; n < 10; n++) {
- val = lgs8gl5_read_reg(state, REG_STRENGTH);
- dprintk("Wait for carrier[%d] 0x%02X\n", n, val);
- if (val & REG_STRENGTH_CARRIER)
- break;
- msleep(4);
- }
- if (!(val & REG_STRENGTH_CARRIER))
- return;
-
- /* Wait for lock */
- for (n = 0; n < 20; n++) {
- val = lgs8gl5_read_reg(state, REG_STATUS);
- dprintk("Wait for lock[%d] 0x%02X\n", n, val);
- if (val & REG_STATUS_LOCK)
- break;
- msleep(12);
- }
- if (!(val & REG_STATUS_LOCK))
- return;
-
- lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2));
- lgs8gl5_soft_reset(state);
-}
-
-
-static int
-lgs8gl5_init(struct dvb_frontend *fe)
-{
- struct lgs8gl5_state *state = fe->demodulator_priv;
-
- dprintk("%s\n", __func__);
-
- lgs8gl5_update_alt_reg(state, 0xc2, 0x28);
- lgs8gl5_soft_reset(state);
- lgs8gl5_update_reg(state, REG_07, 0x10);
- lgs8gl5_update_reg(state, REG_07, 0x10);
- lgs8gl5_write_reg(state, REG_09, 0x0e);
- lgs8gl5_write_reg(state, REG_0A, 0xe5);
- lgs8gl5_write_reg(state, REG_0B, 0x35);
- lgs8gl5_write_reg(state, REG_0C, 0x30);
-
- return 0;
-}
-
-
-static int
-lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct lgs8gl5_state *state = fe->demodulator_priv;
- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
- u8 flags = lgs8gl5_read_reg(state, REG_STATUS);
-
- *status = 0;
-
- if ((level & REG_STRENGTH_MASK) > 0)
- *status |= FE_HAS_SIGNAL;
- if (level & REG_STRENGTH_CARRIER)
- *status |= FE_HAS_CARRIER;
- if (flags & REG_STATUS_SYNC)
- *status |= FE_HAS_SYNC;
- if (flags & REG_STATUS_LOCK)
- *status |= FE_HAS_LOCK;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- *ber = 0;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength)
-{
- struct lgs8gl5_state *state = fe->demodulator_priv;
- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
- *signal_strength = (level & REG_STRENGTH_MASK) << 8;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct lgs8gl5_state *state = fe->demodulator_priv;
- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH);
- *snr = (level & REG_STRENGTH_MASK) << 8;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct lgs8gl5_state *state = fe->demodulator_priv;
-
- dprintk("%s\n", __func__);
-
- if (p->bandwidth_hz != 8000000)
- return -EINVAL;
-
- if (fe->ops.tuner_ops.set_params) {
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- /* lgs8gl5_set_inversion(state, p->inversion); */
-
- lgs8gl5_start_demod(state);
-
- return 0;
-}
-
-
-static int
-lgs8gl5_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct lgs8gl5_state *state = fe->demodulator_priv;
- u8 inv = lgs8gl5_read_reg(state, REG_INVERSION);
-
- p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF;
-
- p->code_rate_HP = FEC_1_2;
- p->code_rate_LP = FEC_7_8;
- p->guard_interval = GUARD_INTERVAL_1_32;
- p->transmission_mode = TRANSMISSION_MODE_2K;
- p->modulation = QAM_64;
- p->hierarchy = HIERARCHY_NONE;
- p->bandwidth_hz = 8000000;
-
- return 0;
-}
-
-
-static int
-lgs8gl5_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- fesettings->min_delay_ms = 240;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
- return 0;
-}
-
-
-static void
-lgs8gl5_release(struct dvb_frontend *fe)
-{
- struct lgs8gl5_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-
-static struct dvb_frontend_ops lgs8gl5_ops;
-
-
-struct dvb_frontend*
-lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
-{
- struct lgs8gl5_state *state = NULL;
-
- dprintk("%s\n", __func__);
-
- /* Allocate memory for the internal state */
- state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- /* Setup the state */
- state->config = config;
- state->i2c = i2c;
-
- /* Check if the demod is there */
- if (lgs8gl5_read_reg(state, REG_RESET) < 0)
- goto error;
-
- /* Create dvb_frontend */
- memcpy(&state->frontend.ops, &lgs8gl5_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
- return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(lgs8gl5_attach);
-
-
-static struct dvb_frontend_ops lgs8gl5_ops = {
- .delsys = { SYS_DMBTH },
- .info = {
- .name = "Legend Silicon LGS-8GL5 DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
- .frequency_tolerance = 0,
- .caps = FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 |
- FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_BANDWIDTH_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER
- },
-
- .release = lgs8gl5_release,
-
- .init = lgs8gl5_init,
-
- .set_frontend = lgs8gl5_set_frontend,
- .get_frontend = lgs8gl5_get_frontend,
- .get_tune_settings = lgs8gl5_get_tune_settings,
-
- .read_status = lgs8gl5_read_status,
- .read_ber = lgs8gl5_read_ber,
- .read_signal_strength = lgs8gl5_read_signal_strength,
- .read_snr = lgs8gl5_read_snr,
- .read_ucblocks = lgs8gl5_read_ucblocks,
-};
-
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver");
-MODULE_AUTHOR("Timothy Lee");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c
deleted file mode 100644
index c2ea2749ebed..000000000000
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ /dev/null
@@ -1,1075 +0,0 @@
-/*
- * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator
- * LGS8913, LGS8GL5, LGS8G75
- * experimental support LGS8G42, LGS8G52
- *
- * Copyright (C) 2007-2009 David T.L. Wong <davidtlwong@gmail.com>
- * Copyright (C) 2008 Sirius International (Hong Kong) Limited
- * Timothy Lee <timothy.lee@siriushk.com> (for initial work on LGS8GL5)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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/div64.h>
-#include <linux/firmware.h>
-
-#include "dvb_frontend.h"
-
-#include "lgs8gxx.h"
-#include "lgs8gxx_priv.h"
-
-#define dprintk(args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG "lgs8gxx: " args); \
- } while (0)
-
-static int debug;
-static int fake_signal_str = 1;
-
-#define LGS8GXX_FIRMWARE "lgs8g75.fw"
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-module_param(fake_signal_str, int, 0644);
-MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913."
-"Signal strength calculation is slow.(default:on).");
-
-/* LGS8GXX internal helper functions */
-
-static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data)
-{
- int ret;
- u8 buf[] = { reg, data };
- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
-
- msg.addr = priv->config->demod_address;
- if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
- msg.addr += 0x02;
-
- if (debug >= 2)
- dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data);
-
- ret = i2c_transfer(priv->i2c, &msg, 1);
-
- if (ret != 1)
- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
- __func__, reg, data, ret);
-
- return (ret != 1) ? -1 : 0;
-}
-
-static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data)
-{
- int ret;
- u8 dev_addr;
-
- u8 b0[] = { reg };
- u8 b1[] = { 0 };
- struct i2c_msg msg[] = {
- { .flags = 0, .buf = b0, .len = 1 },
- { .flags = I2C_M_RD, .buf = b1, .len = 1 },
- };
-
- dev_addr = priv->config->demod_address;
- if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0)
- dev_addr += 0x02;
- msg[1].addr = msg[0].addr = dev_addr;
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret != 2) {
- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret);
- return -1;
- }
-
- *p_data = b1[0];
- if (debug >= 2)
- dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]);
- return 0;
-}
-
-static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv)
-{
- lgs8gxx_write_reg(priv, 0x02, 0x00);
- msleep(1);
- lgs8gxx_write_reg(priv, 0x02, 0x01);
- msleep(100);
-
- return 0;
-}
-
-static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask,
- u8 val, u8 delay, u8 tries)
-{
- u8 t;
- int i;
-
- for (i = 0; i < tries; i++) {
- lgs8gxx_read_reg(priv, reg, &t);
-
- if ((t & mask) == val)
- return 0;
- msleep(delay);
- }
-
- return 1;
-}
-
-static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv)
-{
- const struct lgs8gxx_config *config = priv->config;
- u8 if_conf;
-
- if_conf = 0x10; /* AGC output on, RF_AGC output off; */
-
- if_conf |=
- ((config->ext_adc) ? 0x80 : 0x00) |
- ((config->if_neg_center) ? 0x04 : 0x00) |
- ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */
- ((config->adc_signed) ? 0x02 : 0x00) |
- ((config->if_neg_edge) ? 0x01 : 0x00);
-
- if (config->ext_adc &&
- (config->prod == LGS8GXX_PROD_LGS8G52)) {
- lgs8gxx_write_reg(priv, 0xBA, 0x40);
- }
-
- lgs8gxx_write_reg(priv, 0x07, if_conf);
-
- return 0;
-}
-
-static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/)
-{
- u64 val;
- u32 v32;
- u32 if_clk;
-
- if_clk = priv->config->if_clk_freq;
-
- val = freq;
- if (freq != 0) {
- val <<= 32;
- if (if_clk != 0)
- do_div(val, if_clk);
- v32 = val & 0xFFFFFFFF;
- dprintk("Set IF Freq to %dkHz\n", freq);
- } else {
- v32 = 0;
- dprintk("Set IF Freq to baseband\n");
- }
- dprintk("AFC_INIT_FREQ = 0x%08X\n", v32);
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32));
- lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8));
- lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16));
- lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24));
- } else {
- lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32));
- lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8));
- lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16));
- lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24));
- }
-
- return 0;
-}
-
-static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv)
-{
- u64 val;
- u32 v32 = 0;
- u8 reg_addr, t;
- int i;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
- reg_addr = 0x23;
- else
- reg_addr = 0x48;
-
- for (i = 0; i < 4; i++) {
- lgs8gxx_read_reg(priv, reg_addr, &t);
- v32 <<= 8;
- v32 |= t;
- reg_addr--;
- }
-
- val = v32;
- val *= priv->config->if_clk_freq;
- val >>= 32;
- dprintk("AFC = %u kHz\n", (u32)val);
- return 0;
-}
-
-static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
-{
- u8 t;
- u8 prod = priv->config->prod;
-
- if (prod == LGS8GXX_PROD_LGS8913)
- lgs8gxx_write_reg(priv, 0xC6, 0x01);
-
- if (prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_read_reg(priv, 0x0C, &t);
- t &= (~0x04);
- lgs8gxx_write_reg(priv, 0x0C, t | 0x80);
- lgs8gxx_write_reg(priv, 0x39, 0x00);
- lgs8gxx_write_reg(priv, 0x3D, 0x04);
- } else if (prod == LGS8GXX_PROD_LGS8913 ||
- prod == LGS8GXX_PROD_LGS8GL5 ||
- prod == LGS8GXX_PROD_LGS8G42 ||
- prod == LGS8GXX_PROD_LGS8G52 ||
- prod == LGS8GXX_PROD_LGS8G54) {
- lgs8gxx_read_reg(priv, 0x7E, &t);
- lgs8gxx_write_reg(priv, 0x7E, t | 0x01);
-
- /* clear FEC self reset */
- lgs8gxx_read_reg(priv, 0xC5, &t);
- lgs8gxx_write_reg(priv, 0xC5, t & 0xE0);
- }
-
- if (prod == LGS8GXX_PROD_LGS8913) {
- /* FEC auto detect */
- lgs8gxx_write_reg(priv, 0xC1, 0x03);
-
- lgs8gxx_read_reg(priv, 0x7C, &t);
- t = (t & 0x8C) | 0x03;
- lgs8gxx_write_reg(priv, 0x7C, t);
-
- /* BER test mode */
- lgs8gxx_read_reg(priv, 0xC3, &t);
- t = (t & 0xEF) | 0x10;
- lgs8gxx_write_reg(priv, 0xC3, t);
- }
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G52)
- lgs8gxx_write_reg(priv, 0xD9, 0x40);
-
- return 0;
-}
-
-static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
-{
- u8 t;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- u8 t2;
- lgs8gxx_read_reg(priv, 0x0C, &t);
- t &= (~0x80);
- lgs8gxx_write_reg(priv, 0x0C, t);
-
- lgs8gxx_read_reg(priv, 0x0C, &t);
- lgs8gxx_read_reg(priv, 0x19, &t2);
-
- if (((t&0x03) == 0x01) && (t2&0x01)) {
- lgs8gxx_write_reg(priv, 0x6E, 0x05);
- lgs8gxx_write_reg(priv, 0x39, 0x02);
- lgs8gxx_write_reg(priv, 0x39, 0x03);
- lgs8gxx_write_reg(priv, 0x3D, 0x05);
- lgs8gxx_write_reg(priv, 0x3E, 0x28);
- lgs8gxx_write_reg(priv, 0x53, 0x80);
- } else {
- lgs8gxx_write_reg(priv, 0x6E, 0x3F);
- lgs8gxx_write_reg(priv, 0x39, 0x00);
- lgs8gxx_write_reg(priv, 0x3D, 0x04);
- }
-
- lgs8gxx_soft_reset(priv);
- return 0;
- }
-
- /* turn off auto-detect; manual settings */
- lgs8gxx_write_reg(priv, 0x7E, 0);
- if (priv->config->prod == LGS8GXX_PROD_LGS8913)
- lgs8gxx_write_reg(priv, 0xC1, 0);
-
- lgs8gxx_read_reg(priv, 0xC5, &t);
- t = (t & 0xE0) | 0x06;
- lgs8gxx_write_reg(priv, 0xC5, t);
-
- lgs8gxx_soft_reset(priv);
-
- return 0;
-}
-
-static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked)
-{
- int ret = 0;
- u8 t;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
- ret = lgs8gxx_read_reg(priv, 0x13, &t);
- else
- ret = lgs8gxx_read_reg(priv, 0x4B, &t);
- if (ret != 0)
- return ret;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
- *locked = ((t & 0x80) == 0x80) ? 1 : 0;
- else
- *locked = ((t & 0xC0) == 0xC0) ? 1 : 0;
- return 0;
-}
-
-/* Wait for Code Acquisition Lock */
-static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked)
-{
- int ret = 0;
- u8 reg, mask, val;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- reg = 0x13;
- mask = 0x80;
- val = 0x80;
- } else {
- reg = 0x4B;
- mask = 0xC0;
- val = 0xC0;
- }
-
- ret = wait_reg_mask(priv, reg, mask, val, 50, 40);
- *locked = (ret == 0) ? 1 : 0;
-
- return 0;
-}
-
-static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv,
- u8 *finished)
-{
- int ret = 0;
- u8 reg, mask, val;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- reg = 0x1f;
- mask = 0xC0;
- val = 0x80;
- } else {
- reg = 0xA4;
- mask = 0x03;
- val = 0x01;
- }
-
- ret = wait_reg_mask(priv, reg, mask, val, 10, 20);
- *finished = (ret == 0) ? 1 : 0;
-
- return 0;
-}
-
-static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn,
- u8 *locked)
-{
- int err = 0;
- u8 ad_fini = 0;
- u8 t1, t2;
-
- if (gi == GI_945)
- dprintk("try GI 945\n");
- else if (gi == GI_595)
- dprintk("try GI 595\n");
- else if (gi == GI_420)
- dprintk("try GI 420\n");
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_read_reg(priv, 0x0C, &t1);
- lgs8gxx_read_reg(priv, 0x18, &t2);
- t1 &= ~(GI_MASK);
- t1 |= gi;
- t2 &= 0xFE;
- t2 |= cpn ? 0x01 : 0x00;
- lgs8gxx_write_reg(priv, 0x0C, t1);
- lgs8gxx_write_reg(priv, 0x18, t2);
- } else {
- lgs8gxx_write_reg(priv, 0x04, gi);
- }
- lgs8gxx_soft_reset(priv);
- err = lgs8gxx_wait_ca_lock(priv, locked);
- if (err || !(*locked))
- return err;
- err = lgs8gxx_is_autodetect_finished(priv, &ad_fini);
- if (err != 0)
- return err;
- if (ad_fini) {
- dprintk("auto detect finished\n");
- } else
- *locked = 0;
-
- return 0;
-}
-
-static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv,
- u8 *detected_param, u8 *gi)
-{
- int i, j;
- int err = 0;
- u8 locked = 0, tmp_gi;
-
- dprintk("%s\n", __func__);
-
- lgs8gxx_set_mode_auto(priv);
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_write_reg(priv, 0x67, 0xAA);
- lgs8gxx_write_reg(priv, 0x6E, 0x3F);
- } else {
- /* Guard Interval */
- lgs8gxx_write_reg(priv, 0x03, 00);
- }
-
- for (i = 0; i < 2; i++) {
- for (j = 0; j < 2; j++) {
- tmp_gi = GI_945;
- err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked);
- if (err)
- goto out;
- if (locked)
- goto locked;
- }
- for (j = 0; j < 2; j++) {
- tmp_gi = GI_420;
- err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked);
- if (err)
- goto out;
- if (locked)
- goto locked;
- }
- tmp_gi = GI_595;
- err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked);
- if (err)
- goto out;
- if (locked)
- goto locked;
- }
-
-locked:
- if ((err == 0) && (locked == 1)) {
- u8 t;
-
- if (priv->config->prod != LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_read_reg(priv, 0xA2, &t);
- *detected_param = t;
- } else {
- lgs8gxx_read_reg(priv, 0x1F, &t);
- *detected_param = t & 0x3F;
- }
-
- if (tmp_gi == GI_945)
- dprintk("GI 945 locked\n");
- else if (tmp_gi == GI_595)
- dprintk("GI 595 locked\n");
- else if (tmp_gi == GI_420)
- dprintk("GI 420 locked\n");
- *gi = tmp_gi;
- }
- if (!locked)
- err = -1;
-
-out:
- return err;
-}
-
-static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv)
-{
- s8 err;
- u8 gi = 0x2;
- u8 detected_param = 0;
-
- err = lgs8gxx_auto_detect(priv, &detected_param, &gi);
-
- if (err != 0) {
- dprintk("lgs8gxx_auto_detect failed\n");
- } else
- dprintk("detected param = 0x%02X\n", detected_param);
-
- /* Apply detected parameters */
- if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
- u8 inter_leave_len = detected_param & TIM_MASK ;
- /* Fix 8913 time interleaver detection bug */
- inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40;
- detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK;
- detected_param |= inter_leave_len;
- }
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- u8 t;
- lgs8gxx_read_reg(priv, 0x19, &t);
- t &= 0x81;
- t |= detected_param << 1;
- lgs8gxx_write_reg(priv, 0x19, t);
- } else {
- lgs8gxx_write_reg(priv, 0x7D, detected_param);
- if (priv->config->prod == LGS8GXX_PROD_LGS8913)
- lgs8gxx_write_reg(priv, 0xC0, detected_param);
- }
- /* lgs8gxx_soft_reset(priv); */
-
- /* Enter manual mode */
- lgs8gxx_set_mode_manual(priv);
-
- switch (gi) {
- case GI_945:
- priv->curr_gi = 945; break;
- case GI_595:
- priv->curr_gi = 595; break;
- case GI_420:
- priv->curr_gi = 420; break;
- default:
- priv->curr_gi = 945; break;
- }
-}
-
-static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv,
- u8 serial, u8 clk_pol, u8 clk_gated)
-{
- int ret = 0;
- u8 t, reg_addr;
-
- reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2;
- ret = lgs8gxx_read_reg(priv, reg_addr, &t);
- if (ret != 0)
- return ret;
-
- t &= 0xF8;
- t |= serial ? TS_SERIAL : TS_PARALLEL;
- t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL;
- t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN;
-
- ret = lgs8gxx_write_reg(priv, reg_addr, t);
- if (ret != 0)
- return ret;
-
- return 0;
-}
-
-/* A/D input peak-to-peak voltage range */
-static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv,
- u8 sel)
-{
- u8 r26 = 0x73, r27 = 0x90;
-
- if (priv->config->prod != LGS8GXX_PROD_LGS8G75)
- return 0;
-
- r26 |= (sel & 0x01) << 7;
- r27 |= (sel & 0x02) >> 1;
- lgs8gxx_write_reg(priv, 0x26, r26);
- lgs8gxx_write_reg(priv, 0x27, r27);
-
- return 0;
-}
-
-/* LGS8913 demod frontend functions */
-
-static int lgs8913_init(struct lgs8gxx_state *priv)
-{
- u8 t;
-
- /* LGS8913 specific */
- lgs8gxx_write_reg(priv, 0xc1, 0x3);
-
- lgs8gxx_read_reg(priv, 0x7c, &t);
- lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3);
-
- /* LGS8913 specific */
- lgs8gxx_read_reg(priv, 0xc3, &t);
- lgs8gxx_write_reg(priv, 0xc3, t&0x10);
-
-
- return 0;
-}
-
-static int lgs8g75_init_data(struct lgs8gxx_state *priv)
-{
- const struct firmware *fw;
- int rc;
- int i;
-
- rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev);
- if (rc)
- return rc;
-
- lgs8gxx_write_reg(priv, 0xC6, 0x40);
-
- lgs8gxx_write_reg(priv, 0x3D, 0x04);
- lgs8gxx_write_reg(priv, 0x39, 0x00);
-
- lgs8gxx_write_reg(priv, 0x3A, 0x00);
- lgs8gxx_write_reg(priv, 0x38, 0x00);
- lgs8gxx_write_reg(priv, 0x3B, 0x00);
- lgs8gxx_write_reg(priv, 0x38, 0x00);
-
- for (i = 0; i < fw->size; i++) {
- lgs8gxx_write_reg(priv, 0x38, 0x00);
- lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff));
- lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8));
- lgs8gxx_write_reg(priv, 0x3C, fw->data[i]);
- }
-
- lgs8gxx_write_reg(priv, 0x38, 0x00);
-
- release_firmware(fw);
- return 0;
-}
-
-static int lgs8gxx_init(struct dvb_frontend *fe)
-{
- struct lgs8gxx_state *priv =
- (struct lgs8gxx_state *)fe->demodulator_priv;
- const struct lgs8gxx_config *config = priv->config;
- u8 data = 0;
- s8 err;
- dprintk("%s\n", __func__);
-
- lgs8gxx_read_reg(priv, 0, &data);
- dprintk("reg 0 = 0x%02X\n", data);
-
- if (config->prod == LGS8GXX_PROD_LGS8G75)
- lgs8g75_set_adc_vpp(priv, config->adc_vpp);
-
- /* Setup MPEG output format */
- err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts,
- config->ts_clk_pol,
- config->ts_clk_gated);
- if (err != 0)
- return -EIO;
-
- if (config->prod == LGS8GXX_PROD_LGS8913)
- lgs8913_init(priv);
- lgs8gxx_set_if_freq(priv, priv->config->if_freq);
- lgs8gxx_set_ad_mode(priv);
-
- return 0;
-}
-
-static void lgs8gxx_release(struct dvb_frontend *fe)
-{
- struct lgs8gxx_state *state = fe->demodulator_priv;
- dprintk("%s\n", __func__);
-
- kfree(state);
-}
-
-
-static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
-
- if (len != 2)
- return -EINVAL;
-
- return lgs8gxx_write_reg(priv, buf[0], buf[1]);
-}
-
-static int lgs8gxx_set_fe(struct dvb_frontend *fe)
-{
-
- struct lgs8gxx_state *priv = fe->demodulator_priv;
-
- dprintk("%s\n", __func__);
-
- /* set frequency */
- if (fe->ops.tuner_ops.set_params) {
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- /* start auto lock */
- lgs8gxx_auto_lock(priv);
-
- msleep(10);
-
- return 0;
-}
-
-static int lgs8gxx_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
- dprintk("%s\n", __func__);
-
- /* TODO: get real readings from device */
- /* inversion status */
- fe_params->inversion = INVERSION_OFF;
-
- /* bandwidth */
- fe_params->bandwidth_hz = 8000000;
-
- fe_params->code_rate_HP = FEC_AUTO;
- fe_params->code_rate_LP = FEC_AUTO;
-
- fe_params->modulation = QAM_AUTO;
-
- /* transmission mode */
- fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
-
- /* guard interval */
- fe_params->guard_interval = GUARD_INTERVAL_AUTO;
-
- /* hierarchy */
- fe_params->hierarchy = HIERARCHY_NONE;
-
- return 0;
-}
-
-static
-int lgs8gxx_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *fesettings)
-{
- /* FIXME: copy from tda1004x.c */
- fesettings->min_delay_ms = 800;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
- return 0;
-}
-
-static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
- s8 ret;
- u8 t, locked = 0;
-
- dprintk("%s\n", __func__);
- *fe_status = 0;
-
- lgs8gxx_get_afc_phase(priv);
- lgs8gxx_is_locked(priv, &locked);
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- if (locked)
- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- return 0;
- }
-
- ret = lgs8gxx_read_reg(priv, 0x4B, &t);
- if (ret != 0)
- return -EIO;
-
- dprintk("Reg 0x4B: 0x%02X\n", t);
-
- *fe_status = 0;
- if (priv->config->prod == LGS8GXX_PROD_LGS8913) {
- if ((t & 0x40) == 0x40)
- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
- if ((t & 0x80) == 0x80)
- *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC |
- FE_HAS_LOCK;
- } else {
- if ((t & 0x80) == 0x80)
- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- }
-
- /* success */
- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
- return 0;
-}
-
-static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal)
-{
- u16 v;
- u8 agc_lvl[2], cat;
-
- dprintk("%s()\n", __func__);
- lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]);
- lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]);
-
- v = agc_lvl[0];
- v <<= 8;
- v |= agc_lvl[1];
-
- dprintk("agc_lvl: 0x%04X\n", v);
-
- if (v < 0x100)
- cat = 0;
- else if (v < 0x190)
- cat = 5;
- else if (v < 0x2A8)
- cat = 4;
- else if (v < 0x381)
- cat = 3;
- else if (v < 0x400)
- cat = 2;
- else if (v == 0x400)
- cat = 1;
- else
- cat = 0;
-
- *signal = cat * 65535 / 5;
-
- return 0;
-}
-
-static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
-{
- u8 t; s8 ret;
- s16 max_strength = 0;
- u8 str;
- u16 i, gi = priv->curr_gi;
-
- dprintk("%s\n", __func__);
-
- ret = lgs8gxx_read_reg(priv, 0x4B, &t);
- if (ret != 0)
- return -EIO;
-
- if (fake_signal_str) {
- if ((t & 0xC0) == 0xC0) {
- dprintk("Fake signal strength\n");
- *signal = 0x7FFF;
- } else
- *signal = 0;
- return 0;
- }
-
- dprintk("gi = %d\n", gi);
- for (i = 0; i < gi; i++) {
-
- if ((i & 0xFF) == 0)
- lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8));
- lgs8gxx_write_reg(priv, 0x83, i & 0xFF);
-
- lgs8gxx_read_reg(priv, 0x94, &str);
- if (max_strength < str)
- max_strength = str;
- }
-
- *signal = max_strength;
- dprintk("%s: signal=0x%02X\n", __func__, *signal);
-
- lgs8gxx_read_reg(priv, 0x95, &t);
- dprintk("%s: AVG Noise=0x%02X\n", __func__, t);
-
- return 0;
-}
-
-static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal)
-{
- u8 t;
- s16 v = 0;
-
- dprintk("%s\n", __func__);
-
- lgs8gxx_read_reg(priv, 0xB1, &t);
- v |= t;
- v <<= 8;
- lgs8gxx_read_reg(priv, 0xB0, &t);
- v |= t;
-
- *signal = v;
- dprintk("%s: signal=0x%02X\n", __func__, *signal);
-
- return 0;
-}
-
-static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8913)
- return lgs8913_read_signal_strength(priv, signal);
- else if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
- return lgs8g75_read_signal_strength(priv, signal);
- else
- return lgs8gxx_read_signal_agc(priv, signal);
-}
-
-static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
- u8 t;
- *snr = 0;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75)
- lgs8gxx_read_reg(priv, 0x34, &t);
- else
- lgs8gxx_read_reg(priv, 0x95, &t);
- dprintk("AVG Noise=0x%02X\n", t);
- *snr = 256 - t;
- *snr <<= 8;
- dprintk("snr=0x%x\n", *snr);
-
- return 0;
-}
-
-static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
- return 0;
-}
-
-static void packet_counter_start(struct lgs8gxx_state *priv)
-{
- u8 orig, t;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_read_reg(priv, 0x30, &orig);
- orig &= 0xE7;
- t = orig | 0x10;
- lgs8gxx_write_reg(priv, 0x30, t);
- t = orig | 0x18;
- lgs8gxx_write_reg(priv, 0x30, t);
- t = orig | 0x10;
- lgs8gxx_write_reg(priv, 0x30, t);
- } else {
- lgs8gxx_write_reg(priv, 0xC6, 0x01);
- lgs8gxx_write_reg(priv, 0xC6, 0x41);
- lgs8gxx_write_reg(priv, 0xC6, 0x01);
- }
-}
-
-static void packet_counter_stop(struct lgs8gxx_state *priv)
-{
- u8 t;
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- lgs8gxx_read_reg(priv, 0x30, &t);
- t &= 0xE7;
- lgs8gxx_write_reg(priv, 0x30, t);
- } else {
- lgs8gxx_write_reg(priv, 0xC6, 0x81);
- }
-}
-
-static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
- u8 reg_err, reg_total, t;
- u32 total_cnt = 0, err_cnt = 0;
- int i;
-
- dprintk("%s\n", __func__);
-
- packet_counter_start(priv);
- msleep(200);
- packet_counter_stop(priv);
-
- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
- reg_total = 0x28; reg_err = 0x2C;
- } else {
- reg_total = 0xD0; reg_err = 0xD4;
- }
-
- for (i = 0; i < 4; i++) {
- total_cnt <<= 8;
- lgs8gxx_read_reg(priv, reg_total+3-i, &t);
- total_cnt |= t;
- }
- for (i = 0; i < 4; i++) {
- err_cnt <<= 8;
- lgs8gxx_read_reg(priv, reg_err+3-i, &t);
- err_cnt |= t;
- }
- dprintk("error=%d total=%d\n", err_cnt, total_cnt);
-
- if (total_cnt == 0)
- *ber = 0;
- else
- *ber = err_cnt * 100 / total_cnt;
-
- dprintk("%s: ber=0x%x\n", __func__, *ber);
- return 0;
-}
-
-static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct lgs8gxx_state *priv = fe->demodulator_priv;
-
- if (priv->config->tuner_address == 0)
- return 0;
- if (enable) {
- u8 v = 0x80 | priv->config->tuner_address;
- return lgs8gxx_write_reg(priv, 0x01, v);
- }
- return lgs8gxx_write_reg(priv, 0x01, 0);
-}
-
-static struct dvb_frontend_ops lgs8gxx_ops = {
- .delsys = { SYS_DMBTH },
- .info = {
- .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH",
- .frequency_min = 474000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 10000,
- .caps =
- FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO
- },
-
- .release = lgs8gxx_release,
-
- .init = lgs8gxx_init,
- .write = lgs8gxx_write,
- .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl,
-
- .set_frontend = lgs8gxx_set_fe,
- .get_frontend = lgs8gxx_get_fe,
- .get_tune_settings = lgs8gxx_get_tune_settings,
-
- .read_status = lgs8gxx_read_status,
- .read_ber = lgs8gxx_read_ber,
- .read_signal_strength = lgs8gxx_read_signal_strength,
- .read_snr = lgs8gxx_read_snr,
- .read_ucblocks = lgs8gxx_read_ucblocks,
-};
-
-struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config,
- struct i2c_adapter *i2c)
-{
- struct lgs8gxx_state *priv = NULL;
- u8 data = 0;
-
- dprintk("%s()\n", __func__);
-
- if (config == NULL || i2c == NULL)
- return NULL;
-
- priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL);
- if (priv == NULL)
- goto error_out;
-
- priv->config = config;
- priv->i2c = i2c;
-
- /* check if the demod is there */
- if (lgs8gxx_read_reg(priv, 0, &data) != 0) {
- dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n",
- __func__, priv->config->demod_address);
- goto error_out;
- }
-
- lgs8gxx_read_reg(priv, 1, &data);
-
- memcpy(&priv->frontend.ops, &lgs8gxx_ops,
- sizeof(struct dvb_frontend_ops));
- priv->frontend.demodulator_priv = priv;
-
- if (config->prod == LGS8GXX_PROD_LGS8G75)
- lgs8g75_init_data(priv);
-
- return &priv->frontend;
-
-error_out:
- dprintk("%s() error_out\n", __func__);
- kfree(priv);
- return NULL;
-
-}
-EXPORT_SYMBOL(lgs8gxx_attach);
-
-MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver");
-MODULE_AUTHOR("David T. L. Wong <davidtlwong@gmail.com>");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(LGS8GXX_FIRMWARE);
diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c
deleted file mode 100644
index 312588e84dae..000000000000
--- a/drivers/media/dvb/frontends/m88rs2000.c
+++ /dev/null
@@ -1,919 +0,0 @@
-/*
- Driver for M88RS2000 demodulator and tuner
-
- Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
- Beta Driver
-
- Include various calculation code from DS3000 driver.
- Copyright (C) 2009 Konstantin Dimitrov.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/jiffies.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-
-#include "dvb_frontend.h"
-#include "m88rs2000.h"
-
-struct m88rs2000_state {
- struct i2c_adapter *i2c;
- const struct m88rs2000_config *config;
- struct dvb_frontend frontend;
- u8 no_lock_count;
- u32 tuner_frequency;
- u32 symbol_rate;
- fe_code_rate_t fec_inner;
- u8 tuner_level;
- int errmode;
-};
-
-static int m88rs2000_debug;
-
-module_param_named(debug, m88rs2000_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
-
-#define dprintk(level, args...) do { \
- if (level & m88rs2000_debug) \
- printk(KERN_DEBUG "m88rs2000-fe: " args); \
-} while (0)
-
-#define deb_info(args...) dprintk(0x01, args)
-#define info(format, arg...) \
- printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
-
-static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner,
- u8 reg, u8 data)
-{
- int ret;
- u8 addr = (tuner == 0) ? state->config->tuner_addr :
- state->config->demod_addr;
- u8 buf[] = { reg, data };
- struct i2c_msg msg = {
- .addr = addr,
- .flags = 0,
- .buf = buf,
- .len = 2
- };
-
- ret = i2c_transfer(state->i2c, &msg, 1);
-
- if (ret != 1)
- deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
- "ret == %i)\n", __func__, reg, data, ret);
-
- return (ret != 1) ? -EREMOTEIO : 0;
-}
-
-static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data)
-{
- return m88rs2000_writereg(state, 1, reg, data);
-}
-
-static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data)
-{
- m88rs2000_demod_write(state, 0x81, 0x84);
- udelay(10);
- return m88rs2000_writereg(state, 0, reg, data);
-
-}
-
-static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
-
- if (len != 2)
- return -EINVAL;
-
- return m88rs2000_writereg(state, 1, buf[0], buf[1]);
-}
-
-static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg)
-{
- int ret;
- u8 b0[] = { reg };
- u8 b1[] = { 0 };
- u8 addr = (tuner == 0) ? state->config->tuner_addr :
- state->config->demod_addr;
- struct i2c_msg msg[] = {
- {
- .addr = addr,
- .flags = 0,
- .buf = b0,
- .len = 1
- }, {
- .addr = addr,
- .flags = I2C_M_RD,
- .buf = b1,
- .len = 1
- }
- };
-
- ret = i2c_transfer(state->i2c, msg, 2);
-
- if (ret != 2)
- deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
- __func__, reg, ret);
-
- return b1[0];
-}
-
-static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg)
-{
- return m88rs2000_readreg(state, 1, reg);
-}
-
-static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg)
-{
- m88rs2000_demod_write(state, 0x81, 0x85);
- udelay(10);
- return m88rs2000_readreg(state, 0, reg);
-}
-
-static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- int ret;
- u32 temp;
- u8 b[3];
-
- if ((srate < 1000000) || (srate > 45000000))
- return -EINVAL;
-
- temp = srate / 1000;
- temp *= 11831;
- temp /= 68;
- temp -= 3;
-
- b[0] = (u8) (temp >> 16) & 0xff;
- b[1] = (u8) (temp >> 8) & 0xff;
- b[2] = (u8) temp & 0xff;
- ret = m88rs2000_demod_write(state, 0x93, b[2]);
- ret |= m88rs2000_demod_write(state, 0x94, b[1]);
- ret |= m88rs2000_demod_write(state, 0x95, b[0]);
-
- deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
- return ret;
-}
-
-static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
- struct dvb_diseqc_master_cmd *m)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
-
- int i;
- u8 reg;
- deb_info("%s\n", __func__);
- m88rs2000_demod_write(state, 0x9a, 0x30);
- reg = m88rs2000_demod_read(state, 0xb2);
- reg &= 0x3f;
- m88rs2000_demod_write(state, 0xb2, reg);
- for (i = 0; i < m->msg_len; i++)
- m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]);
-
- reg = m88rs2000_demod_read(state, 0xb1);
- reg &= 0x87;
- reg |= ((m->msg_len - 1) << 3) | 0x07;
- reg &= 0x7f;
- m88rs2000_demod_write(state, 0xb1, reg);
-
- for (i = 0; i < 15; i++) {
- if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0)
- break;
- msleep(20);
- }
-
- reg = m88rs2000_demod_read(state, 0xb1);
- if ((reg & 0x40) > 0x0) {
- reg &= 0x7f;
- reg |= 0x40;
- m88rs2000_demod_write(state, 0xb1, reg);
- }
-
- reg = m88rs2000_demod_read(state, 0xb2);
- reg &= 0x3f;
- reg |= 0x80;
- m88rs2000_demod_write(state, 0xb2, reg);
- m88rs2000_demod_write(state, 0x9a, 0xb0);
-
-
- return 0;
-}
-
-static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
- fe_sec_mini_cmd_t burst)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- u8 reg0, reg1;
- deb_info("%s\n", __func__);
- m88rs2000_demod_write(state, 0x9a, 0x30);
- msleep(50);
- reg0 = m88rs2000_demod_read(state, 0xb1);
- reg1 = m88rs2000_demod_read(state, 0xb2);
- /* TODO complete this section */
- m88rs2000_demod_write(state, 0xb2, reg1);
- m88rs2000_demod_write(state, 0xb1, reg0);
- m88rs2000_demod_write(state, 0x9a, 0xb0);
-
- return 0;
-}
-
-static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- u8 reg0, reg1;
- m88rs2000_demod_write(state, 0x9a, 0x30);
- reg0 = m88rs2000_demod_read(state, 0xb1);
- reg1 = m88rs2000_demod_read(state, 0xb2);
-
- reg1 &= 0x3f;
-
- switch (tone) {
- case SEC_TONE_ON:
- reg0 |= 0x4;
- reg0 &= 0xbc;
- break;
- case SEC_TONE_OFF:
- reg1 |= 0x80;
- break;
- default:
- break;
- }
- m88rs2000_demod_write(state, 0xb2, reg1);
- m88rs2000_demod_write(state, 0xb1, reg0);
- m88rs2000_demod_write(state, 0x9a, 0xb0);
- return 0;
-}
-
-struct inittab {
- u8 cmd;
- u8 reg;
- u8 val;
-};
-
-struct inittab m88rs2000_setup[] = {
- {DEMOD_WRITE, 0x9a, 0x30},
- {DEMOD_WRITE, 0x00, 0x01},
- {WRITE_DELAY, 0x19, 0x00},
- {DEMOD_WRITE, 0x00, 0x00},
- {DEMOD_WRITE, 0x9a, 0xb0},
- {DEMOD_WRITE, 0x81, 0xc1},
- {TUNER_WRITE, 0x42, 0x73},
- {TUNER_WRITE, 0x05, 0x07},
- {TUNER_WRITE, 0x20, 0x27},
- {TUNER_WRITE, 0x07, 0x02},
- {TUNER_WRITE, 0x11, 0xff},
- {TUNER_WRITE, 0x60, 0xf9},
- {TUNER_WRITE, 0x08, 0x01},
- {TUNER_WRITE, 0x00, 0x41},
- {DEMOD_WRITE, 0x81, 0x81},
- {DEMOD_WRITE, 0x86, 0xc6},
- {DEMOD_WRITE, 0x9a, 0x30},
- {DEMOD_WRITE, 0xf0, 0x22},
- {DEMOD_WRITE, 0xf1, 0xbf},
- {DEMOD_WRITE, 0xb0, 0x45},
- {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
- {DEMOD_WRITE, 0x9a, 0xb0},
- {0xff, 0xaa, 0xff}
-};
-
-struct inittab m88rs2000_shutdown[] = {
- {DEMOD_WRITE, 0x9a, 0x30},
- {DEMOD_WRITE, 0xb0, 0x00},
- {DEMOD_WRITE, 0xf1, 0x89},
- {DEMOD_WRITE, 0x00, 0x01},
- {DEMOD_WRITE, 0x9a, 0xb0},
- {TUNER_WRITE, 0x00, 0x40},
- {DEMOD_WRITE, 0x81, 0x81},
- {0xff, 0xaa, 0xff}
-};
-
-struct inittab tuner_reset[] = {
- {TUNER_WRITE, 0x42, 0x73},
- {TUNER_WRITE, 0x05, 0x07},
- {TUNER_WRITE, 0x20, 0x27},
- {TUNER_WRITE, 0x07, 0x02},
- {TUNER_WRITE, 0x11, 0xff},
- {TUNER_WRITE, 0x60, 0xf9},
- {TUNER_WRITE, 0x08, 0x01},
- {TUNER_WRITE, 0x00, 0x41},
- {0xff, 0xaa, 0xff}
-};
-
-struct inittab fe_reset[] = {
- {DEMOD_WRITE, 0x00, 0x01},
- {DEMOD_WRITE, 0xf1, 0xbf},
- {DEMOD_WRITE, 0x00, 0x01},
- {DEMOD_WRITE, 0x20, 0x81},
- {DEMOD_WRITE, 0x21, 0x80},
- {DEMOD_WRITE, 0x10, 0x33},
- {DEMOD_WRITE, 0x11, 0x44},
- {DEMOD_WRITE, 0x12, 0x07},
- {DEMOD_WRITE, 0x18, 0x20},
- {DEMOD_WRITE, 0x28, 0x04},
- {DEMOD_WRITE, 0x29, 0x8e},
- {DEMOD_WRITE, 0x3b, 0xff},
- {DEMOD_WRITE, 0x32, 0x10},
- {DEMOD_WRITE, 0x33, 0x02},
- {DEMOD_WRITE, 0x34, 0x30},
- {DEMOD_WRITE, 0x35, 0xff},
- {DEMOD_WRITE, 0x38, 0x50},
- {DEMOD_WRITE, 0x39, 0x68},
- {DEMOD_WRITE, 0x3c, 0x7f},
- {DEMOD_WRITE, 0x3d, 0x0f},
- {DEMOD_WRITE, 0x45, 0x20},
- {DEMOD_WRITE, 0x46, 0x24},
- {DEMOD_WRITE, 0x47, 0x7c},
- {DEMOD_WRITE, 0x48, 0x16},
- {DEMOD_WRITE, 0x49, 0x04},
- {DEMOD_WRITE, 0x4a, 0x01},
- {DEMOD_WRITE, 0x4b, 0x78},
- {DEMOD_WRITE, 0X4d, 0xd2},
- {DEMOD_WRITE, 0x4e, 0x6d},
- {DEMOD_WRITE, 0x50, 0x30},
- {DEMOD_WRITE, 0x51, 0x30},
- {DEMOD_WRITE, 0x54, 0x7b},
- {DEMOD_WRITE, 0x56, 0x09},
- {DEMOD_WRITE, 0x58, 0x59},
- {DEMOD_WRITE, 0x59, 0x37},
- {DEMOD_WRITE, 0x63, 0xfa},
- {0xff, 0xaa, 0xff}
-};
-
-struct inittab fe_trigger[] = {
- {DEMOD_WRITE, 0x97, 0x04},
- {DEMOD_WRITE, 0x99, 0x77},
- {DEMOD_WRITE, 0x9b, 0x64},
- {DEMOD_WRITE, 0x9e, 0x00},
- {DEMOD_WRITE, 0x9f, 0xf8},
- {DEMOD_WRITE, 0xa0, 0x20},
- {DEMOD_WRITE, 0xa1, 0xe0},
- {DEMOD_WRITE, 0xa3, 0x38},
- {DEMOD_WRITE, 0x98, 0xff},
- {DEMOD_WRITE, 0xc0, 0x0f},
- {DEMOD_WRITE, 0x89, 0x01},
- {DEMOD_WRITE, 0x00, 0x00},
- {WRITE_DELAY, 0x0a, 0x00},
- {DEMOD_WRITE, 0x00, 0x01},
- {DEMOD_WRITE, 0x00, 0x00},
- {DEMOD_WRITE, 0x9a, 0xb0},
- {0xff, 0xaa, 0xff}
-};
-
-static int m88rs2000_tab_set(struct m88rs2000_state *state,
- struct inittab *tab)
-{
- int ret = 0;
- u8 i;
- if (tab == NULL)
- return -EINVAL;
-
- for (i = 0; i < 255; i++) {
- switch (tab[i].cmd) {
- case 0x01:
- ret = m88rs2000_demod_write(state, tab[i].reg,
- tab[i].val);
- break;
- case 0x02:
- ret = m88rs2000_tuner_write(state, tab[i].reg,
- tab[i].val);
- break;
- case 0x10:
- if (tab[i].reg > 0)
- mdelay(tab[i].reg);
- break;
- case 0xff:
- if (tab[i].reg == 0xaa && tab[i].val == 0xff)
- return 0;
- case 0x00:
- break;
- default:
- return -EINVAL;
- }
- if (ret < 0)
- return -ENODEV;
- }
- return 0;
-}
-
-static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- u8 data;
-
- data = m88rs2000_demod_read(state, 0xb2);
- data |= 0x03; /* bit0 V/H, bit1 off/on */
-
- switch (volt) {
- case SEC_VOLTAGE_18:
- data &= ~0x03;
- break;
- case SEC_VOLTAGE_13:
- data &= ~0x03;
- data |= 0x01;
- break;
- case SEC_VOLTAGE_OFF:
- break;
- }
-
- m88rs2000_demod_write(state, 0xb2, data);
-
- return 0;
-}
-
-static int m88rs2000_startup(struct m88rs2000_state *state)
-{
- int ret = 0;
- u8 reg;
-
- reg = m88rs2000_tuner_read(state, 0x00);
- if ((reg & 0x40) == 0)
- ret = -ENODEV;
-
- return ret;
-}
-
-static int m88rs2000_init(struct dvb_frontend *fe)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- int ret;
-
- deb_info("m88rs2000: init chip\n");
- /* Setup frontend from shutdown/cold */
- ret = m88rs2000_tab_set(state, m88rs2000_setup);
-
- return ret;
-}
-
-static int m88rs2000_sleep(struct dvb_frontend *fe)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- int ret;
- /* Shutdown the frondend */
- ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
- return ret;
-}
-
-static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- u8 reg = m88rs2000_demod_read(state, 0x8c);
-
- *status = 0;
-
- if ((reg & 0x7) == 0x7) {
- *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
- | FE_HAS_LOCK;
- if (state->config->set_ts_params)
- state->config->set_ts_params(fe, CALL_IS_READ);
- }
- return 0;
-}
-
-/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */
-
-static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- deb_info("m88rs2000_read_ber %d\n", *ber);
- *ber = 0;
- return 0;
-}
-
-static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
- u16 *strength)
-{
- *strength = 0;
- return 0;
-}
-
-static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- deb_info("m88rs2000_read_snr %d\n", *snr);
- *snr = 0;
- return 0;
-}
-
-static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- deb_info("m88rs2000_read_ber %d\n", *ucblocks);
- *ucblocks = 0;
- return 0;
-}
-
-static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset)
-{
- int ret;
- ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset);
- ret |= m88rs2000_tuner_write(state, 0x51, 0x1f);
- ret |= m88rs2000_tuner_write(state, 0x50, offset);
- ret |= m88rs2000_tuner_write(state, 0x50, 0x00);
- msleep(20);
- return ret;
-}
-
-static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- int reg;
- reg = m88rs2000_tuner_read(state, 0x3d);
- reg &= 0x7f;
- if (reg < 0x16)
- reg = 0xa1;
- else if (reg == 0x16)
- reg = 0x99;
- else
- reg = 0xf9;
-
- m88rs2000_tuner_write(state, 0x60, reg);
- reg = m88rs2000_tuner_gate_ctrl(state, 0x08);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- return reg;
-}
-
-static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct m88rs2000_state *state = fe->demodulator_priv;
- int ret;
- u32 frequency = c->frequency;
- s32 offset_khz;
- s32 tmp;
- u32 symbol_rate = (c->symbol_rate / 1000);
- u32 f3db, gdiv28;
- u16 value, ndiv, lpf_coeff;
- u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
- u8 lo = 0x01, div4 = 0x0;
-
- /* Reset Tuner */
- ret = m88rs2000_tab_set(state, tuner_reset);
-
- /* Calculate frequency divider */
- if (frequency < 1060000) {
- lo |= 0x10;
- div4 = 0x1;
- ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ;
- } else
- ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ;
- ndiv = ndiv + ndiv % 2;
- ndiv = ndiv - 1024;
-
- ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo);
-
- /* Set frequency divider */
- ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf);
- ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff);
-
- ret |= m88rs2000_tuner_write(state, 0x03, 0x06);
- ret |= m88rs2000_tuner_gate_ctrl(state, 0x10);
- if (ret < 0)
- return -ENODEV;
-
- /* Tuner Frequency Range */
- ret = m88rs2000_tuner_write(state, 0x10, lo);
-
- ret |= m88rs2000_tuner_gate_ctrl(state, 0x08);
-
- /* Tuner RF */
- ret |= m88rs2000_set_tuner_rf(fe);
-
- gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
- ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff);
- ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
- if (ret < 0)
- return -ENODEV;
-
- value = m88rs2000_tuner_read(state, 0x26);
-
- f3db = (symbol_rate * 135) / 200 + 2000;
- f3db += FREQ_OFFSET_LOW_SYM_RATE;
- if (f3db < 7000)
- f3db = 7000;
- if (f3db > 40000)
- f3db = 40000;
-
- gdiv28 = gdiv28 * 207 / (value * 2 + 151);
- mlpf_max = gdiv28 * 135 / 100;
- mlpf_min = gdiv28 * 78 / 100;
- if (mlpf_max > 63)
- mlpf_max = 63;
-
- lpf_coeff = 2766;
-
- nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
- (FE_CRYSTAL_KHZ / 1000) + 1) / 2;
- if (nlpf > 23)
- nlpf = 23;
- if (nlpf < 1)
- nlpf = 1;
-
- lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
- * lpf_coeff * 2 / f3db + 1) / 2;
-
- if (lpf_mxdiv < mlpf_min) {
- nlpf++;
- lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
- * lpf_coeff * 2 / f3db + 1) / 2;
- }
-
- if (lpf_mxdiv > mlpf_max)
- lpf_mxdiv = mlpf_max;
-
- ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv);
- ret |= m88rs2000_tuner_write(state, 0x06, nlpf);
-
- ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
-
- ret |= m88rs2000_tuner_gate_ctrl(state, 0x01);
-
- msleep(80);
- /* calculate offset assuming 96000kHz*/
- offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ
- / 14 / (div4 + 1) / 2;
-
- offset_khz -= frequency;
-
- tmp = offset_khz;
- tmp *= 65536;
-
- tmp = (2 * tmp + 96000) / (2 * 96000);
- if (tmp < 0)
- tmp += 65536;
-
- *offset = tmp & 0xffff;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
-
- return (ret < 0) ? -EINVAL : 0;
-}
-
-static int m88rs2000_set_fec(struct m88rs2000_state *state,
- fe_code_rate_t fec)
-{
- u16 fec_set;
- switch (fec) {
- /* This is not confirmed kept for reference */
-/* case FEC_1_2:
- fec_set = 0x88;
- break;
- case FEC_2_3:
- fec_set = 0x68;
- break;
- case FEC_3_4:
- fec_set = 0x48;
- break;
- case FEC_5_6:
- fec_set = 0x28;
- break;
- case FEC_7_8:
- fec_set = 0x18;
- break; */
- case FEC_AUTO:
- default:
- fec_set = 0x08;
- }
- m88rs2000_demod_write(state, 0x76, fec_set);
-
- return 0;
-}
-
-
-static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
-{
- u8 reg;
- m88rs2000_demod_write(state, 0x9a, 0x30);
- reg = m88rs2000_demod_read(state, 0x76);
- m88rs2000_demod_write(state, 0x9a, 0xb0);
-
- switch (reg) {
- case 0x88:
- return FEC_1_2;
- case 0x68:
- return FEC_2_3;
- case 0x48:
- return FEC_3_4;
- case 0x28:
- return FEC_5_6;
- case 0x18:
- return FEC_7_8;
- case 0x08:
- default:
- break;
- }
-
- return FEC_AUTO;
-}
-
-static int m88rs2000_set_frontend(struct dvb_frontend *fe)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- fe_status_t status;
- int i, ret;
- u16 offset = 0;
- u8 reg;
-
- state->no_lock_count = 0;
-
- if (c->delivery_system != SYS_DVBS) {
- deb_info("%s: unsupported delivery "
- "system selected (%d)\n",
- __func__, c->delivery_system);
- return -EOPNOTSUPP;
- }
-
- /* Set Tuner */
- ret = m88rs2000_set_tuner(fe, &offset);
- if (ret < 0)
- return -ENODEV;
-
- ret = m88rs2000_demod_write(state, 0x9a, 0x30);
- /* Unknown usually 0xc6 sometimes 0xc1 */
- reg = m88rs2000_demod_read(state, 0x86);
- ret |= m88rs2000_demod_write(state, 0x86, reg);
- /* Offset lower nibble always 0 */
- ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8));
- ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0);
-
-
- /* Reset Demod */
- ret = m88rs2000_tab_set(state, fe_reset);
- if (ret < 0)
- return -ENODEV;
-
- /* Unknown */
- reg = m88rs2000_demod_read(state, 0x70);
- ret = m88rs2000_demod_write(state, 0x70, reg);
-
- /* Set FEC */
- ret |= m88rs2000_set_fec(state, c->fec_inner);
- ret |= m88rs2000_demod_write(state, 0x85, 0x1);
- ret |= m88rs2000_demod_write(state, 0x8a, 0xbf);
- ret |= m88rs2000_demod_write(state, 0x8d, 0x1e);
- ret |= m88rs2000_demod_write(state, 0x90, 0xf1);
- ret |= m88rs2000_demod_write(state, 0x91, 0x08);
-
- if (ret < 0)
- return -ENODEV;
-
- /* Set Symbol Rate */
- ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
- if (ret < 0)
- return -ENODEV;
-
- /* Set up Demod */
- ret = m88rs2000_tab_set(state, fe_trigger);
- if (ret < 0)
- return -ENODEV;
-
- for (i = 0; i < 25; i++) {
- reg = m88rs2000_demod_read(state, 0x8c);
- if ((reg & 0x7) == 0x7) {
- status = FE_HAS_LOCK;
- break;
- }
- state->no_lock_count++;
- if (state->no_lock_count == 15) {
- reg = m88rs2000_demod_read(state, 0x70);
- reg ^= 0x4;
- m88rs2000_demod_write(state, 0x70, reg);
- state->no_lock_count = 0;
- }
- if (state->no_lock_count == 20)
- m88rs2000_set_tuner_rf(fe);
- msleep(20);
- }
-
- if (status & FE_HAS_LOCK) {
- state->fec_inner = m88rs2000_get_fec(state);
- /* Uknown suspect SNR level */
- reg = m88rs2000_demod_read(state, 0x65);
- }
-
- state->tuner_frequency = c->frequency;
- state->symbol_rate = c->symbol_rate;
- return 0;
-}
-
-static int m88rs2000_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct m88rs2000_state *state = fe->demodulator_priv;
- c->fec_inner = state->fec_inner;
- c->frequency = state->tuner_frequency;
- c->symbol_rate = state->symbol_rate;
- return 0;
-}
-
-static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
-
- if (enable)
- m88rs2000_demod_write(state, 0x81, 0x84);
- else
- m88rs2000_demod_write(state, 0x81, 0x81);
- udelay(10);
- return 0;
-}
-
-static void m88rs2000_release(struct dvb_frontend *fe)
-{
- struct m88rs2000_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops m88rs2000_ops = {
- .delsys = { SYS_DVBS },
- .info = {
- .name = "M88RS2000 DVB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1000, /* kHz for QPSK frontends */
- .frequency_tolerance = 5000,
- .symbol_rate_min = 1000000,
- .symbol_rate_max = 45000000,
- .symbol_rate_tolerance = 500, /* ppm */
- .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_QPSK |
- FE_CAN_FEC_AUTO
- },
-
- .release = m88rs2000_release,
- .init = m88rs2000_init,
- .sleep = m88rs2000_sleep,
- .write = m88rs2000_write,
- .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
- .read_status = m88rs2000_read_status,
- .read_ber = m88rs2000_read_ber,
- .read_signal_strength = m88rs2000_read_signal_strength,
- .read_snr = m88rs2000_read_snr,
- .read_ucblocks = m88rs2000_read_ucblocks,
- .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
- .diseqc_send_burst = m88rs2000_send_diseqc_burst,
- .set_tone = m88rs2000_set_tone,
- .set_voltage = m88rs2000_set_voltage,
-
- .set_frontend = m88rs2000_set_frontend,
- .get_frontend = m88rs2000_get_frontend,
-};
-
-struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
- struct i2c_adapter *i2c)
-{
- struct m88rs2000_state *state = NULL;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- /* setup the state */
- state->config = config;
- state->i2c = i2c;
- state->tuner_frequency = 0;
- state->symbol_rate = 0;
- state->fec_inner = 0;
-
- if (m88rs2000_startup(state) < 0)
- goto error;
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &m88rs2000_ops,
- sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
- return &state->frontend;
-
-error:
- kfree(state);
-
- return NULL;
-}
-EXPORT_SYMBOL(m88rs2000_attach);
-
-MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
-MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("1.13");
-
diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c
deleted file mode 100644
index 49ca78d883b1..000000000000
--- a/drivers/media/dvb/frontends/nxt200x.c
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*
- * Support for NXT2002 and NXT2004 - VSB/QAM
- *
- * Copyright (C) 2005 Kirk Lapray <kirk.lapray@gmail.com>
- * Copyright (C) 2006 Michael Krufky <mkrufky@m1k.net>
- * based on nxt2002 by Taylor Jacob <rtjacob@earthlink.net>
- * and nxt2004 by Jean-Francois Thibert <jeanfrancois@sagetv.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
-*/
-
-/*
- * NOTES ABOUT THIS DRIVER
- *
- * This Linux driver supports:
- * B2C2/BBTI Technisat Air2PC - ATSC (NXT2002)
- * AverTVHD MCE A180 (NXT2004)
- * ATI HDTV Wonder (NXT2004)
- *
- * This driver needs external firmware. Please use the command
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2002" or
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware nxt2004" to
- * download/extract the appropriate firmware, and then copy it to
- * /usr/lib/hotplug/firmware/ or /lib/firmware/
- * (depending on configuration of firmware hotplug).
- */
-#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw"
-#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw"
-#define CRC_CCIT_MASK 0x1021
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include "dvb_frontend.h"
-#include "nxt200x.h"
-
-struct nxt200x_state {
-
- struct i2c_adapter* i2c;
- const struct nxt200x_config* config;
- struct dvb_frontend frontend;
-
- /* demodulator private data */
- nxt_chip_type demod_chip;
- u8 initialised:1;
-};
-
-static int debug;
-#define dprintk(args...) \
- do { \
- if (debug) printk(KERN_DEBUG "nxt200x: " args); \
- } while (0)
-
-static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len)
-{
- int err;
- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len };
-
- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n",
- __func__, addr, err);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len)
-{
- int err;
- struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len };
-
- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n",
- __func__, addr, err);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
- const u8 *buf, u8 len)
-{
- u8 buf2 [len+1];
- int err;
- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 };
-
- buf2[0] = reg;
- memcpy(&buf2[1], buf, len);
-
- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n",
- __func__, state->config->demod_address, err);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len)
-{
- u8 reg2 [] = { reg };
-
- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 },
- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } };
-
- int err;
-
- if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) {
- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n",
- __func__, state->config->demod_address, err);
- return -EREMOTEIO;
- }
- return 0;
-}
-
-static u16 nxt200x_crc(u16 crc, u8 c)
-{
- u8 i;
- u16 input = (u16) c & 0xFF;
-
- input<<=8;
- for(i=0; i<8; i++) {
- if((crc^input) & 0x8000)
- crc=(crc<<1)^CRC_CCIT_MASK;
- else
- crc<<=1;
- input<<=1;
- }
- return crc;
-}
-
-static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len)
-{
- u8 attr, len2, buf;
- dprintk("%s\n", __func__);
-
- /* set mutli register register */
- nxt200x_writebytes(state, 0x35, &reg, 1);
-
- /* send the actual data */
- nxt200x_writebytes(state, 0x36, data, len);
-
- switch (state->demod_chip) {
- case NXT2002:
- len2 = len;
- buf = 0x02;
- break;
- case NXT2004:
- /* probably not right, but gives correct values */
- attr = 0x02;
- if (reg & 0x80) {
- attr = attr << 1;
- if (reg & 0x04)
- attr = attr >> 1;
- }
- /* set write bit */
- len2 = ((attr << 4) | 0x10) | len;
- buf = 0x80;
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* set multi register length */
- nxt200x_writebytes(state, 0x34, &len2, 1);
-
- /* toggle the multireg write bit */
- nxt200x_writebytes(state, 0x21, &buf, 1);
-
- nxt200x_readbytes(state, 0x21, &buf, 1);
-
- switch (state->demod_chip) {
- case NXT2002:
- if ((buf & 0x02) == 0)
- return 0;
- break;
- case NXT2004:
- if (buf == 0)
- return 0;
- break;
- default:
- return -EINVAL;
- break;
- }
-
- printk(KERN_WARNING "nxt200x: Error writing multireg register 0x%02X\n",reg);
-
- return 0;
-}
-
-static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len)
-{
- int i;
- u8 buf, len2, attr;
- dprintk("%s\n", __func__);
-
- /* set mutli register register */
- nxt200x_writebytes(state, 0x35, &reg, 1);
-
- switch (state->demod_chip) {
- case NXT2002:
- /* set multi register length */
- len2 = len & 0x80;
- nxt200x_writebytes(state, 0x34, &len2, 1);
-
- /* read the actual data */
- nxt200x_readbytes(state, reg, data, len);
- return 0;
- break;
- case NXT2004:
- /* probably not right, but gives correct values */
- attr = 0x02;
- if (reg & 0x80) {
- attr = attr << 1;
- if (reg & 0x04)
- attr = attr >> 1;
- }
-
- /* set multi register length */
- len2 = (attr << 4) | len;
- nxt200x_writebytes(state, 0x34, &len2, 1);
-
- /* toggle the multireg bit*/
- buf = 0x80;
- nxt200x_writebytes(state, 0x21, &buf, 1);
-
- /* read the actual data */
- for(i = 0; i < len; i++) {
- nxt200x_readbytes(state, 0x36 + i, &data[i], 1);
- }
- return 0;
- break;
- default:
- return -EINVAL;
- break;
- }
-}
-
-static void nxt200x_microcontroller_stop (struct nxt200x_state* state)
-{
- u8 buf, stopval, counter = 0;
- dprintk("%s\n", __func__);
-
- /* set correct stop value */
- switch (state->demod_chip) {
- case NXT2002:
- stopval = 0x40;
- break;
- case NXT2004:
- stopval = 0x10;
- break;
- default:
- stopval = 0;
- break;
- }
-
- buf = 0x80;
- nxt200x_writebytes(state, 0x22, &buf, 1);
-
- while (counter < 20) {
- nxt200x_readbytes(state, 0x31, &buf, 1);
- if (buf & stopval)
- return;
- msleep(10);
- counter++;
- }
-
- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt200x to stop. This is ok after firmware upload.\n");
- return;
-}
-
-static void nxt200x_microcontroller_start (struct nxt200x_state* state)
-{
- u8 buf;
- dprintk("%s\n", __func__);
-
- buf = 0x00;
- nxt200x_writebytes(state, 0x22, &buf, 1);
-}
-
-static void nxt2004_microcontroller_init (struct nxt200x_state* state)
-{
- u8 buf[9];
- u8 counter = 0;
- dprintk("%s\n", __func__);
-
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x2b, buf, 1);
- buf[0] = 0x70;
- nxt200x_writebytes(state, 0x34, buf, 1);
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x35, buf, 1);
- buf[0] = 0x01; buf[1] = 0x23; buf[2] = 0x45; buf[3] = 0x67; buf[4] = 0x89;
- buf[5] = 0xAB; buf[6] = 0xCD; buf[7] = 0xEF; buf[8] = 0xC0;
- nxt200x_writebytes(state, 0x36, buf, 9);
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x21, buf, 1);
-
- while (counter < 20) {
- nxt200x_readbytes(state, 0x21, buf, 1);
- if (buf[0] == 0)
- return;
- msleep(10);
- counter++;
- }
-
- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt2004 to init.\n");
-
- return;
-}
-
-static int nxt200x_writetuner (struct nxt200x_state* state, u8* data)
-{
- u8 buf, count = 0;
-
- dprintk("%s\n", __func__);
-
- dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[1], data[2], data[3], data[4]);
-
- /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip.
- * direct write is required for Philips TUV1236D and ALPS TDHU2 */
- switch (state->demod_chip) {
- case NXT2004:
- if (i2c_writebytes(state, data[0], data+1, 4))
- printk(KERN_WARNING "nxt200x: error writing to tuner\n");
- /* wait until we have a lock */
- while (count < 20) {
- i2c_readbytes(state, data[0], &buf, 1);
- if (buf & 0x40)
- return 0;
- msleep(100);
- count++;
- }
- printk("nxt2004: timeout waiting for tuner lock\n");
- break;
- case NXT2002:
- /* set the i2c transfer speed to the tuner */
- buf = 0x03;
- nxt200x_writebytes(state, 0x20, &buf, 1);
-
- /* setup to transfer 4 bytes via i2c */
- buf = 0x04;
- nxt200x_writebytes(state, 0x34, &buf, 1);
-
- /* write actual tuner bytes */
- nxt200x_writebytes(state, 0x36, data+1, 4);
-
- /* set tuner i2c address */
- buf = data[0] << 1;
- nxt200x_writebytes(state, 0x35, &buf, 1);
-
- /* write UC Opmode to begin transfer */
- buf = 0x80;
- nxt200x_writebytes(state, 0x21, &buf, 1);
-
- while (count < 20) {
- nxt200x_readbytes(state, 0x21, &buf, 1);
- if ((buf & 0x80)== 0x00)
- return 0;
- msleep(100);
- count++;
- }
- printk("nxt2002: timeout error writing tuner\n");
- break;
- default:
- return -EINVAL;
- break;
- }
- return 0;
-}
-
-static void nxt200x_agc_reset(struct nxt200x_state* state)
-{
- u8 buf;
- dprintk("%s\n", __func__);
-
- switch (state->demod_chip) {
- case NXT2002:
- buf = 0x08;
- nxt200x_writebytes(state, 0x08, &buf, 1);
- buf = 0x00;
- nxt200x_writebytes(state, 0x08, &buf, 1);
- break;
- case NXT2004:
- nxt200x_readreg_multibyte(state, 0x08, &buf, 1);
- buf = 0x08;
- nxt200x_writereg_multibyte(state, 0x08, &buf, 1);
- buf = 0x00;
- nxt200x_writereg_multibyte(state, 0x08, &buf, 1);
- break;
- default:
- break;
- }
- return;
-}
-
-static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
-{
-
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 buf[3], written = 0, chunkpos = 0;
- u16 rambase, position, crc = 0;
-
- dprintk("%s\n", __func__);
- dprintk("Firmware is %zu bytes\n", fw->size);
-
- /* Get the RAM base for this nxt2002 */
- nxt200x_readbytes(state, 0x10, buf, 1);
-
- if (buf[0] & 0x10)
- rambase = 0x1000;
- else
- rambase = 0x0000;
-
- dprintk("rambase on this nxt2002 is %04X\n", rambase);
-
- /* Hold the micro in reset while loading firmware */
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x2B, buf, 1);
-
- for (position = 0; position < fw->size; position++) {
- if (written == 0) {
- crc = 0;
- chunkpos = 0x28;
- buf[0] = ((rambase + position) >> 8);
- buf[1] = (rambase + position) & 0xFF;
- buf[2] = 0x81;
- /* write starting address */
- nxt200x_writebytes(state, 0x29, buf, 3);
- }
- written++;
- chunkpos++;
-
- if ((written % 4) == 0)
- nxt200x_writebytes(state, chunkpos, &fw->data[position-3], 4);
-
- crc = nxt200x_crc(crc, fw->data[position]);
-
- if ((written == 255) || (position+1 == fw->size)) {
- /* write remaining bytes of firmware */
- nxt200x_writebytes(state, chunkpos+4-(written %4),
- &fw->data[position-(written %4) + 1],
- written %4);
- buf[0] = crc << 8;
- buf[1] = crc & 0xFF;
-
- /* write crc */
- nxt200x_writebytes(state, 0x2C, buf, 2);
-
- /* do a read to stop things */
- nxt200x_readbytes(state, 0x2A, buf, 1);
-
- /* set transfer mode to complete */
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x2B, buf, 1);
-
- written = 0;
- }
- }
-
- return 0;
-};
-
-static int nxt2004_load_firmware (struct dvb_frontend* fe, const struct firmware *fw)
-{
-
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 buf[3];
- u16 rambase, position, crc=0;
-
- dprintk("%s\n", __func__);
- dprintk("Firmware is %zu bytes\n", fw->size);
-
- /* set rambase */
- rambase = 0x1000;
-
- /* hold the micro in reset while loading firmware */
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x2B, buf,1);
-
- /* calculate firmware CRC */
- for (position = 0; position < fw->size; position++) {
- crc = nxt200x_crc(crc, fw->data[position]);
- }
-
- buf[0] = rambase >> 8;
- buf[1] = rambase & 0xFF;
- buf[2] = 0x81;
- /* write starting address */
- nxt200x_writebytes(state,0x29,buf,3);
-
- for (position = 0; position < fw->size;) {
- nxt200x_writebytes(state, 0x2C, &fw->data[position],
- fw->size-position > 255 ? 255 : fw->size-position);
- position += (fw->size-position > 255 ? 255 : fw->size-position);
- }
- buf[0] = crc >> 8;
- buf[1] = crc & 0xFF;
-
- dprintk("firmware crc is 0x%02X 0x%02X\n", buf[0], buf[1]);
-
- /* write crc */
- nxt200x_writebytes(state, 0x2C, buf,2);
-
- /* do a read to stop things */
- nxt200x_readbytes(state, 0x2C, buf, 1);
-
- /* set transfer mode to complete */
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x2B, buf,1);
-
- return 0;
-};
-
-static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 buf[5];
-
- /* stop the micro first */
- nxt200x_microcontroller_stop(state);
-
- if (state->demod_chip == NXT2004) {
- /* make sure demod is set to digital */
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x14, buf, 1);
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x17, buf, 1);
- }
-
- /* set additional params */
- switch (p->modulation) {
- case QAM_64:
- case QAM_256:
- /* Set punctured clock for QAM */
- /* This is just a guess since I am unable to test it */
- if (state->config->set_ts_params)
- state->config->set_ts_params(fe, 1);
- break;
- case VSB_8:
- /* Set non-punctured clock for VSB */
- if (state->config->set_ts_params)
- state->config->set_ts_params(fe, 0);
- break;
- default:
- return -EINVAL;
- break;
- }
-
- if (fe->ops.tuner_ops.calc_regs) {
- /* get tuning information */
- fe->ops.tuner_ops.calc_regs(fe, buf, 5);
-
- /* write frequency information */
- nxt200x_writetuner(state, buf);
- }
-
- /* reset the agc now that tuning has been completed */
- nxt200x_agc_reset(state);
-
- /* set target power level */
- switch (p->modulation) {
- case QAM_64:
- case QAM_256:
- buf[0] = 0x74;
- break;
- case VSB_8:
- buf[0] = 0x70;
- break;
- default:
- return -EINVAL;
- break;
- }
- nxt200x_writebytes(state, 0x42, buf, 1);
-
- /* configure sdm */
- switch (state->demod_chip) {
- case NXT2002:
- buf[0] = 0x87;
- break;
- case NXT2004:
- buf[0] = 0x07;
- break;
- default:
- return -EINVAL;
- break;
- }
- nxt200x_writebytes(state, 0x57, buf, 1);
-
- /* write sdm1 input */
- buf[0] = 0x10;
- buf[1] = 0x00;
- switch (state->demod_chip) {
- case NXT2002:
- nxt200x_writereg_multibyte(state, 0x58, buf, 2);
- break;
- case NXT2004:
- nxt200x_writebytes(state, 0x58, buf, 2);
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* write sdmx input */
- switch (p->modulation) {
- case QAM_64:
- buf[0] = 0x68;
- break;
- case QAM_256:
- buf[0] = 0x64;
- break;
- case VSB_8:
- buf[0] = 0x60;
- break;
- default:
- return -EINVAL;
- break;
- }
- buf[1] = 0x00;
- switch (state->demod_chip) {
- case NXT2002:
- nxt200x_writereg_multibyte(state, 0x5C, buf, 2);
- break;
- case NXT2004:
- nxt200x_writebytes(state, 0x5C, buf, 2);
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* write adc power lpf fc */
- buf[0] = 0x05;
- nxt200x_writebytes(state, 0x43, buf, 1);
-
- if (state->demod_chip == NXT2004) {
- /* write ??? */
- buf[0] = 0x00;
- buf[1] = 0x00;
- nxt200x_writebytes(state, 0x46, buf, 2);
- }
-
- /* write accumulator2 input */
- buf[0] = 0x80;
- buf[1] = 0x00;
- switch (state->demod_chip) {
- case NXT2002:
- nxt200x_writereg_multibyte(state, 0x4B, buf, 2);
- break;
- case NXT2004:
- nxt200x_writebytes(state, 0x4B, buf, 2);
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* write kg1 */
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x4D, buf, 1);
-
- /* write sdm12 lpf fc */
- buf[0] = 0x44;
- nxt200x_writebytes(state, 0x55, buf, 1);
-
- /* write agc control reg */
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x41, buf, 1);
-
- if (state->demod_chip == NXT2004) {
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x24;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- /* soft reset? */
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x10;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
-
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x04;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x81, buf, 1);
- buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
- nxt200x_writereg_multibyte(state, 0x82, buf, 3);
- nxt200x_readreg_multibyte(state, 0x88, buf, 1);
- buf[0] = 0x11;
- nxt200x_writereg_multibyte(state, 0x88, buf, 1);
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x44;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
- }
-
- /* write agc ucgp0 */
- switch (p->modulation) {
- case QAM_64:
- buf[0] = 0x02;
- break;
- case QAM_256:
- buf[0] = 0x03;
- break;
- case VSB_8:
- buf[0] = 0x00;
- break;
- default:
- return -EINVAL;
- break;
- }
- nxt200x_writebytes(state, 0x30, buf, 1);
-
- /* write agc control reg */
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x41, buf, 1);
-
- /* write accumulator2 input */
- buf[0] = 0x80;
- buf[1] = 0x00;
- switch (state->demod_chip) {
- case NXT2002:
- nxt200x_writereg_multibyte(state, 0x49, buf, 2);
- nxt200x_writereg_multibyte(state, 0x4B, buf, 2);
- break;
- case NXT2004:
- nxt200x_writebytes(state, 0x49, buf, 2);
- nxt200x_writebytes(state, 0x4B, buf, 2);
- break;
- default:
- return -EINVAL;
- break;
- }
-
- /* write agc control reg */
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x41, buf, 1);
-
- nxt200x_microcontroller_start(state);
-
- if (state->demod_chip == NXT2004) {
- nxt2004_microcontroller_init(state);
-
- /* ???? */
- buf[0] = 0xF0;
- buf[1] = 0x00;
- nxt200x_writebytes(state, 0x5C, buf, 2);
- }
-
- /* adjacent channel detection should be done here, but I don't
- have any stations with this need so I cannot test it */
-
- return 0;
-}
-
-static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 lock;
- nxt200x_readbytes(state, 0x31, &lock, 1);
-
- *status = 0;
- if (lock & 0x20) {
- *status |= FE_HAS_SIGNAL;
- *status |= FE_HAS_CARRIER;
- *status |= FE_HAS_VITERBI;
- *status |= FE_HAS_SYNC;
- *status |= FE_HAS_LOCK;
- }
- return 0;
-}
-
-static int nxt200x_read_ber(struct dvb_frontend* fe, u32* ber)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 b[3];
-
- nxt200x_readreg_multibyte(state, 0xE6, b, 3);
-
- *ber = ((b[0] << 8) + b[1]) * 8;
-
- return 0;
-}
-
-static int nxt200x_read_signal_strength(struct dvb_frontend* fe, u16* strength)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 b[2];
- u16 temp = 0;
-
- /* setup to read cluster variance */
- b[0] = 0x00;
- nxt200x_writebytes(state, 0xA1, b, 1);
-
- /* get multreg val */
- nxt200x_readreg_multibyte(state, 0xA6, b, 2);
-
- temp = (b[0] << 8) | b[1];
- *strength = ((0x7FFF - temp) & 0x0FFF) * 16;
-
- return 0;
-}
-
-static int nxt200x_read_snr(struct dvb_frontend* fe, u16* snr)
-{
-
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 b[2];
- u16 temp = 0, temp2;
- u32 snrdb = 0;
-
- /* setup to read cluster variance */
- b[0] = 0x00;
- nxt200x_writebytes(state, 0xA1, b, 1);
-
- /* get multreg val from 0xA6 */
- nxt200x_readreg_multibyte(state, 0xA6, b, 2);
-
- temp = (b[0] << 8) | b[1];
- temp2 = 0x7FFF - temp;
-
- /* snr will be in db */
- if (temp2 > 0x7F00)
- snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) );
- else if (temp2 > 0x7EC0)
- snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) );
- else if (temp2 > 0x7C00)
- snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) );
- else
- snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) );
-
- /* the value reported back from the frontend will be FFFF=32db 0000=0db */
- *snr = snrdb * (0xFFFF/32000);
-
- return 0;
-}
-
-static int nxt200x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- u8 b[3];
-
- nxt200x_readreg_multibyte(state, 0xE6, b, 3);
- *ucblocks = b[2];
-
- return 0;
-}
-
-static int nxt200x_sleep(struct dvb_frontend* fe)
-{
- return 0;
-}
-
-static int nxt2002_init(struct dvb_frontend* fe)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- const struct firmware *fw;
- int ret;
- u8 buf[2];
-
- /* request the firmware, this will block until someone uploads it */
- printk("nxt2002: Waiting for firmware upload (%s)...\n", NXT2002_DEFAULT_FIRMWARE);
- ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE,
- state->i2c->dev.parent);
- printk("nxt2002: Waiting for firmware upload(2)...\n");
- if (ret) {
- printk("nxt2002: No firmware uploaded (timeout or file not found?)\n");
- return ret;
- }
-
- ret = nxt2002_load_firmware(fe, fw);
- release_firmware(fw);
- if (ret) {
- printk("nxt2002: Writing firmware to device failed\n");
- return ret;
- }
- printk("nxt2002: Firmware upload complete\n");
-
- /* Put the micro into reset */
- nxt200x_microcontroller_stop(state);
-
- /* ensure transfer is complete */
- buf[0]=0x00;
- nxt200x_writebytes(state, 0x2B, buf, 1);
-
- /* Put the micro into reset for real this time */
- nxt200x_microcontroller_stop(state);
-
- /* soft reset everything (agc,frontend,eq,fec)*/
- buf[0] = 0x0F;
- nxt200x_writebytes(state, 0x08, buf, 1);
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x08, buf, 1);
-
- /* write agc sdm configure */
- buf[0] = 0xF1;
- nxt200x_writebytes(state, 0x57, buf, 1);
-
- /* write mod output format */
- buf[0] = 0x20;
- nxt200x_writebytes(state, 0x09, buf, 1);
-
- /* write fec mpeg mode */
- buf[0] = 0x7E;
- buf[1] = 0x00;
- nxt200x_writebytes(state, 0xE9, buf, 2);
-
- /* write mux selection */
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0xCC, buf, 1);
-
- return 0;
-}
-
-static int nxt2004_init(struct dvb_frontend* fe)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- const struct firmware *fw;
- int ret;
- u8 buf[3];
-
- /* ??? */
- buf[0]=0x00;
- nxt200x_writebytes(state, 0x1E, buf, 1);
-
- /* request the firmware, this will block until someone uploads it */
- printk("nxt2004: Waiting for firmware upload (%s)...\n", NXT2004_DEFAULT_FIRMWARE);
- ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE,
- state->i2c->dev.parent);
- printk("nxt2004: Waiting for firmware upload(2)...\n");
- if (ret) {
- printk("nxt2004: No firmware uploaded (timeout or file not found?)\n");
- return ret;
- }
-
- ret = nxt2004_load_firmware(fe, fw);
- release_firmware(fw);
- if (ret) {
- printk("nxt2004: Writing firmware to device failed\n");
- return ret;
- }
- printk("nxt2004: Firmware upload complete\n");
-
- /* ensure transfer is complete */
- buf[0] = 0x01;
- nxt200x_writebytes(state, 0x19, buf, 1);
-
- nxt2004_microcontroller_init(state);
- nxt200x_microcontroller_stop(state);
- nxt200x_microcontroller_stop(state);
- nxt2004_microcontroller_init(state);
- nxt200x_microcontroller_stop(state);
-
- /* soft reset everything (agc,frontend,eq,fec)*/
- buf[0] = 0xFF;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
-
- /* write agc sdm configure */
- buf[0] = 0xD7;
- nxt200x_writebytes(state, 0x57, buf, 1);
-
- /* ???*/
- buf[0] = 0x07;
- buf[1] = 0xfe;
- nxt200x_writebytes(state, 0x35, buf, 2);
- buf[0] = 0x12;
- nxt200x_writebytes(state, 0x34, buf, 1);
- buf[0] = 0x80;
- nxt200x_writebytes(state, 0x21, buf, 1);
-
- /* ???*/
- buf[0] = 0x21;
- nxt200x_writebytes(state, 0x0A, buf, 1);
-
- /* ???*/
- buf[0] = 0x01;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- /* write fec mpeg mode */
- buf[0] = 0x7E;
- buf[1] = 0x00;
- nxt200x_writebytes(state, 0xE9, buf, 2);
-
- /* write mux selection */
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0xCC, buf, 1);
-
- /* ???*/
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- /* soft reset? */
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x10;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
-
- /* ???*/
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x01;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x70;
- nxt200x_writereg_multibyte(state, 0x81, buf, 1);
- buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66;
- nxt200x_writereg_multibyte(state, 0x82, buf, 3);
-
- nxt200x_readreg_multibyte(state, 0x88, buf, 1);
- buf[0] = 0x11;
- nxt200x_writereg_multibyte(state, 0x88, buf, 1);
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x40;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- nxt200x_readbytes(state, 0x10, buf, 1);
- buf[0] = 0x10;
- nxt200x_writebytes(state, 0x10, buf, 1);
- nxt200x_readbytes(state, 0x0A, buf, 1);
- buf[0] = 0x21;
- nxt200x_writebytes(state, 0x0A, buf, 1);
-
- nxt2004_microcontroller_init(state);
-
- buf[0] = 0x21;
- nxt200x_writebytes(state, 0x0A, buf, 1);
- buf[0] = 0x7E;
- nxt200x_writebytes(state, 0xE9, buf, 1);
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0xEA, buf, 1);
-
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- /* soft reset? */
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x10;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
- nxt200x_readreg_multibyte(state, 0x08, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x08, buf, 1);
-
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x04;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x00;
- nxt200x_writereg_multibyte(state, 0x81, buf, 1);
- buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00;
- nxt200x_writereg_multibyte(state, 0x82, buf, 3);
-
- nxt200x_readreg_multibyte(state, 0x88, buf, 1);
- buf[0] = 0x11;
- nxt200x_writereg_multibyte(state, 0x88, buf, 1);
-
- nxt200x_readreg_multibyte(state, 0x80, buf, 1);
- buf[0] = 0x44;
- nxt200x_writereg_multibyte(state, 0x80, buf, 1);
-
- /* initialize tuner */
- nxt200x_readbytes(state, 0x10, buf, 1);
- buf[0] = 0x12;
- nxt200x_writebytes(state, 0x10, buf, 1);
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x13, buf, 1);
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x16, buf, 1);
- buf[0] = 0x04;
- nxt200x_writebytes(state, 0x14, buf, 1);
- buf[0] = 0x00;
- nxt200x_writebytes(state, 0x14, buf, 1);
- nxt200x_writebytes(state, 0x17, buf, 1);
- nxt200x_writebytes(state, 0x14, buf, 1);
- nxt200x_writebytes(state, 0x17, buf, 1);
-
- return 0;
-}
-
-static int nxt200x_init(struct dvb_frontend* fe)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- int ret = 0;
-
- if (!state->initialised) {
- switch (state->demod_chip) {
- case NXT2002:
- ret = nxt2002_init(fe);
- break;
- case NXT2004:
- ret = nxt2004_init(fe);
- break;
- default:
- return -EINVAL;
- break;
- }
- state->initialised = 1;
- }
- return ret;
-}
-
-static int nxt200x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
-{
- fesettings->min_delay_ms = 500;
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
- return 0;
-}
-
-static void nxt200x_release(struct dvb_frontend* fe)
-{
- struct nxt200x_state* state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops nxt200x_ops;
-
-struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
- struct i2c_adapter* i2c)
-{
- struct nxt200x_state* state = NULL;
- u8 buf [] = {0,0,0,0,0};
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct nxt200x_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- /* setup the state */
- state->config = config;
- state->i2c = i2c;
- state->initialised = 0;
-
- /* read card id */
- nxt200x_readbytes(state, 0x00, buf, 5);
- dprintk("NXT info: %02X %02X %02X %02X %02X\n",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
-
- /* set demod chip */
- switch (buf[0]) {
- case 0x04:
- state->demod_chip = NXT2002;
- printk("nxt200x: NXT2002 Detected\n");
- break;
- case 0x05:
- state->demod_chip = NXT2004;
- printk("nxt200x: NXT2004 Detected\n");
- break;
- default:
- goto error;
- }
-
- /* make sure demod chip is supported */
- switch (state->demod_chip) {
- case NXT2002:
- if (buf[0] != 0x04) goto error; /* device id */
- if (buf[1] != 0x02) goto error; /* fab id */
- if (buf[2] != 0x11) goto error; /* month */
- if (buf[3] != 0x20) goto error; /* year msb */
- if (buf[4] != 0x00) goto error; /* year lsb */
- break;
- case NXT2004:
- if (buf[0] != 0x05) goto error; /* device id */
- break;
- default:
- goto error;
- }
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &nxt200x_ops, sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
- return &state->frontend;
-
-error:
- kfree(state);
- printk("Unknown/Unsupported NXT chip: %02X %02X %02X %02X %02X\n",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
- return NULL;
-}
-
-static struct dvb_frontend_ops nxt200x_ops = {
- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B },
- .info = {
- .name = "Nextwave NXT200X VSB/QAM frontend",
- .frequency_min = 54000000,
- .frequency_max = 860000000,
- .frequency_stepsize = 166666, /* stepsize is just a guess */
- .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_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256
- },
-
- .release = nxt200x_release,
-
- .init = nxt200x_init,
- .sleep = nxt200x_sleep,
-
- .set_frontend = nxt200x_setup_frontend_parameters,
- .get_tune_settings = nxt200x_get_tune_settings,
-
- .read_status = nxt200x_read_status,
- .read_ber = nxt200x_read_ber,
- .read_signal_strength = nxt200x_read_signal_strength,
- .read_snr = nxt200x_read_snr,
- .read_ucblocks = nxt200x_read_ucblocks,
-};
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver");
-MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(nxt200x_attach);
-
diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c
deleted file mode 100644
index 93612ebac519..000000000000
--- a/drivers/media/dvb/frontends/rtl2830.c
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * Realtek RTL2830 DVB-T demodulator driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- */
-
-
-/*
- * Driver implements own I2C-adapter for tuner I2C access. That's since chip
- * have unusual I2C-gate control which closes gate automatically after each
- * I2C transfer. Using own I2C adapter we can workaround that.
- */
-
-#include "rtl2830_priv.h"
-
-int rtl2830_debug;
-module_param_named(debug, rtl2830_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-/* write multiple hardware registers */
-static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[1+len];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = 1+len,
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple hardware registers */
-static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* write multiple registers */
-static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
-{
- int ret;
- u8 reg2 = (reg >> 0) & 0xff;
- u8 page = (reg >> 8) & 0xff;
-
- /* switch bank if needed */
- if (page != priv->page) {
- ret = rtl2830_wr(priv, 0x00, &page, 1);
- if (ret)
- return ret;
-
- priv->page = page;
- }
-
- return rtl2830_wr(priv, reg2, val, len);
-}
-
-/* read multiple registers */
-static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
-{
- int ret;
- u8 reg2 = (reg >> 0) & 0xff;
- u8 page = (reg >> 8) & 0xff;
-
- /* switch bank if needed */
- if (page != priv->page) {
- ret = rtl2830_wr(priv, 0x00, &page, 1);
- if (ret)
- return ret;
-
- priv->page = page;
- }
-
- return rtl2830_rd(priv, reg2, val, len);
-}
-
-#if 0 /* currently not used */
-/* write single register */
-static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
-{
- return rtl2830_wr_regs(priv, reg, &val, 1);
-}
-#endif
-
-/* read single register */
-static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
-{
- return rtl2830_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return rtl2830_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register with mask */
-int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
-{
- int ret, i;
- u8 tmp;
-
- ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- tmp &= mask;
-
- /* find position of the first bit */
- for (i = 0; i < 8; i++) {
- if ((mask >> i) & 0x01)
- break;
- }
- *val = tmp >> i;
-
- return 0;
-}
-
-static int rtl2830_init(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- int ret, i;
- u64 num;
- u8 buf[3], tmp;
- u32 if_ctl;
- struct rtl2830_reg_val_mask tab[] = {
- { 0x00d, 0x01, 0x03 },
- { 0x00d, 0x10, 0x10 },
- { 0x104, 0x00, 0x1e },
- { 0x105, 0x80, 0x80 },
- { 0x110, 0x02, 0x03 },
- { 0x110, 0x08, 0x0c },
- { 0x17b, 0x00, 0x40 },
- { 0x17d, 0x05, 0x0f },
- { 0x17d, 0x50, 0xf0 },
- { 0x18c, 0x08, 0x0f },
- { 0x18d, 0x00, 0xc0 },
- { 0x188, 0x05, 0x0f },
- { 0x189, 0x00, 0xfc },
- { 0x2d5, 0x02, 0x02 },
- { 0x2f1, 0x02, 0x06 },
- { 0x2f1, 0x20, 0xf8 },
- { 0x16d, 0x00, 0x01 },
- { 0x1a6, 0x00, 0x80 },
- { 0x106, priv->cfg.vtop, 0x3f },
- { 0x107, priv->cfg.krf, 0x3f },
- { 0x112, 0x28, 0xff },
- { 0x103, priv->cfg.agc_targ_val, 0xff },
- { 0x00a, 0x02, 0x07 },
- { 0x140, 0x0c, 0x3c },
- { 0x140, 0x40, 0xc0 },
- { 0x15b, 0x05, 0x07 },
- { 0x15b, 0x28, 0x38 },
- { 0x15c, 0x05, 0x07 },
- { 0x15c, 0x28, 0x38 },
- { 0x115, priv->cfg.spec_inv, 0x01 },
- { 0x16f, 0x01, 0x07 },
- { 0x170, 0x18, 0x38 },
- { 0x172, 0x0f, 0x0f },
- { 0x173, 0x08, 0x38 },
- { 0x175, 0x01, 0x07 },
- { 0x176, 0x00, 0xc0 },
- };
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret)
- goto err;
- }
-
- ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
- if (ret)
- goto err;
-
- ret = rtl2830_wr_regs(priv, 0x195,
- "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
- if (ret)
- goto err;
-
- num = priv->cfg.if_dvbt % priv->cfg.xtal;
- num *= 0x400000;
- num = div_u64(num, priv->cfg.xtal);
- num = -num;
- if_ctl = num & 0x3fffff;
- dbg("%s: if_ctl=%08x", __func__, if_ctl);
-
- ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
- if (ret)
- goto err;
-
- buf[0] = tmp << 6;
- buf[0] = (if_ctl >> 16) & 0x3f;
- buf[1] = (if_ctl >> 8) & 0xff;
- buf[2] = (if_ctl >> 0) & 0xff;
-
- ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
- if (ret)
- goto err;
-
- /* TODO: spec init */
-
- /* soft reset */
- ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
- if (ret)
- goto err;
-
- ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
- if (ret)
- goto err;
-
- priv->sleeping = false;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_sleep(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- priv->sleeping = true;
- return 0;
-}
-
-int rtl2830_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 500;
- s->step_size = fe->ops.info.frequency_stepsize * 2;
- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
-
- return 0;
-}
-
-static int rtl2830_set_frontend(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- static u8 bw_params1[3][34] = {
- {
- 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
- 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
- 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
- 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
- }, {
- 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
- 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
- 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
- 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
- }, {
- 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
- 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
- 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
- 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
- },
- };
- static u8 bw_params2[3][6] = {
- {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
- {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
- {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
- };
-
-
- dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
- c->frequency, c->bandwidth_hz, c->inversion);
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
- switch (c->bandwidth_hz) {
- case 6000000:
- i = 0;
- break;
- case 7000000:
- i = 1;
- break;
- case 8000000:
- i = 2;
- break;
- default:
- dbg("invalid bandwidth");
- return -EINVAL;
- }
-
- ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
- if (ret)
- goto err;
-
- /* 1/2 split I2C write */
- ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
- if (ret)
- goto err;
-
- /* 2/2 split I2C write */
- ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
- if (ret)
- goto err;
-
- ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
- if (ret)
- goto err;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_get_frontend(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret;
- u8 buf[3];
-
- if (priv->sleeping)
- return 0;
-
- ret = rtl2830_rd_regs(priv, 0x33c, buf, 2);
- if (ret)
- goto err;
-
- ret = rtl2830_rd_reg(priv, 0x351, &buf[2]);
- if (ret)
- goto err;
-
- dbg("%s: TPS=%02x %02x %02x", __func__, buf[0], buf[1], buf[2]);
-
- switch ((buf[0] >> 2) & 3) {
- case 0:
- c->modulation = QPSK;
- break;
- case 1:
- c->modulation = QAM_16;
- break;
- case 2:
- c->modulation = QAM_64;
- break;
- }
-
- switch ((buf[2] >> 2) & 1) {
- case 0:
- c->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case 1:
- c->transmission_mode = TRANSMISSION_MODE_8K;
- }
-
- switch ((buf[2] >> 0) & 3) {
- case 0:
- c->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- c->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- c->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- c->guard_interval = GUARD_INTERVAL_1_4;
- break;
- }
-
- switch ((buf[0] >> 4) & 7) {
- case 0:
- c->hierarchy = HIERARCHY_NONE;
- break;
- case 1:
- c->hierarchy = HIERARCHY_1;
- break;
- case 2:
- c->hierarchy = HIERARCHY_2;
- break;
- case 3:
- c->hierarchy = HIERARCHY_4;
- break;
- }
-
- switch ((buf[1] >> 3) & 7) {
- case 0:
- c->code_rate_HP = FEC_1_2;
- break;
- case 1:
- c->code_rate_HP = FEC_2_3;
- break;
- case 2:
- c->code_rate_HP = FEC_3_4;
- break;
- case 3:
- c->code_rate_HP = FEC_5_6;
- break;
- case 4:
- c->code_rate_HP = FEC_7_8;
- break;
- }
-
- switch ((buf[1] >> 0) & 7) {
- case 0:
- c->code_rate_LP = FEC_1_2;
- break;
- case 1:
- c->code_rate_LP = FEC_2_3;
- break;
- case 2:
- c->code_rate_LP = FEC_3_4;
- break;
- case 3:
- c->code_rate_LP = FEC_5_6;
- break;
- case 4:
- c->code_rate_LP = FEC_7_8;
- break;
- }
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- int ret;
- u8 tmp;
- *status = 0;
-
- if (priv->sleeping)
- return 0;
-
- ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
- if (ret)
- goto err;
-
- if (tmp == 11) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- } else if (tmp == 10) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI;
- }
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- int ret, hierarchy, constellation;
- u8 buf[2], tmp;
- u16 tmp16;
-#define CONSTELLATION_NUM 3
-#define HIERARCHY_NUM 4
- static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
- { 70705899, 70705899, 70705899, 70705899 },
- { 82433173, 82433173, 87483115, 94445660 },
- { 92888734, 92888734, 95487525, 99770748 },
- };
-
- if (priv->sleeping)
- return 0;
-
- /* reports SNR in resolution of 0.1 dB */
-
- ret = rtl2830_rd_reg(priv, 0x33c, &tmp);
- if (ret)
- goto err;
-
- constellation = (tmp >> 2) & 0x03; /* [3:2] */
- if (constellation > CONSTELLATION_NUM - 1)
- goto err;
-
- hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
- if (hierarchy > HIERARCHY_NUM - 1)
- goto err;
-
- ret = rtl2830_rd_regs(priv, 0x40c, buf, 2);
- if (ret)
- goto err;
-
- tmp16 = buf[0] << 8 | buf[1];
-
- if (tmp16)
- *snr = (snr_constant[constellation][hierarchy] -
- intlog10(tmp16)) / ((1 << 24) / 100);
- else
- *snr = 0;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
-
- if (priv->sleeping)
- return 0;
-
- ret = rtl2830_rd_regs(priv, 0x34e, buf, 2);
- if (ret)
- goto err;
-
- *ber = buf[0] << 8 | buf[1];
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- return 0;
-}
-
-static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
- u16 if_agc_raw, if_agc;
-
- if (priv->sleeping)
- return 0;
-
- ret = rtl2830_rd_regs(priv, 0x359, buf, 2);
- if (ret)
- goto err;
-
- if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff;
-
- if (if_agc_raw & (1 << 9))
- if_agc = -(~(if_agc_raw - 1) & 0x1ff);
- else
- if_agc = if_agc_raw;
-
- *strength = (u8) (55 - if_agc / 182);
- *strength |= *strength << 8;
-
- return 0;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static struct dvb_frontend_ops rtl2830_ops;
-
-static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
-{
- return I2C_FUNC_I2C;
-}
-
-static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg msg[], int num)
-{
- struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
- int ret;
-
- /* open i2c-gate */
- ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
- if (ret)
- goto err;
-
- ret = i2c_transfer(priv->i2c, msg, num);
- if (ret < 0)
- warn("tuner i2c failed=%d", ret);
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
- .master_xfer = rtl2830_tuner_i2c_xfer,
- .functionality = rtl2830_tuner_i2c_func,
-};
-
-struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
- return &priv->tuner_i2c_adapter;
-}
-EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
-
-static void rtl2830_release(struct dvb_frontend *fe)
-{
- struct rtl2830_priv *priv = fe->demodulator_priv;
-
- i2c_del_adapter(&priv->tuner_i2c_adapter);
- kfree(priv);
-}
-
-struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
- struct i2c_adapter *i2c)
-{
- struct rtl2830_priv *priv = NULL;
- int ret = 0;
- u8 tmp;
-
- /* allocate memory for the internal state */
- priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
- if (priv == NULL)
- goto err;
-
- /* setup the priv */
- priv->i2c = i2c;
- memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
-
- /* check if the demod is there */
- ret = rtl2830_rd_reg(priv, 0x000, &tmp);
- if (ret)
- goto err;
-
- /* create dvb_frontend */
- memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
- priv->fe.demodulator_priv = priv;
-
- /* create tuner i2c adapter */
- strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
- sizeof(priv->tuner_i2c_adapter.name));
- priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
- priv->tuner_i2c_adapter.algo_data = NULL;
- i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
- if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
- err("tuner I2C bus could not be initialized");
- goto err;
- }
-
- priv->sleeping = true;
-
- return &priv->fe;
-err:
- dbg("%s: failed=%d", __func__, ret);
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(rtl2830_attach);
-
-static struct dvb_frontend_ops rtl2830_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Realtek RTL2830 (DVB-T)",
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_MUTE_TS
- },
-
- .release = rtl2830_release,
-
- .init = rtl2830_init,
- .sleep = rtl2830_sleep,
-
- .get_tune_settings = rtl2830_get_tune_settings,
-
- .set_frontend = rtl2830_set_frontend,
- .get_frontend = rtl2830_get_frontend,
-
- .read_status = rtl2830_read_status,
- .read_snr = rtl2830_read_snr,
- .read_ber = rtl2830_read_ber,
- .read_ucblocks = rtl2830_read_ucblocks,
- .read_signal_strength = rtl2830_read_signal_strength,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/rtl2830.h b/drivers/media/dvb/frontends/rtl2830.h
deleted file mode 100644
index 1c6ee91749c2..000000000000
--- a/drivers/media/dvb/frontends/rtl2830.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Realtek RTL2830 DVB-T demodulator driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef RTL2830_H
-#define RTL2830_H
-
-#include <linux/dvb/frontend.h>
-
-struct rtl2830_config {
- /*
- * Demodulator I2C address.
- */
- u8 i2c_addr;
-
- /*
- * Xtal frequency.
- * Hz
- * 4000000, 16000000, 25000000, 28800000
- */
- u32 xtal;
-
- /*
- * TS output mode.
- */
- u8 ts_mode;
-
- /*
- * Spectrum inversion.
- */
- bool spec_inv;
-
- /*
- * IFs for all used modes.
- * Hz
- * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
- */
- u32 if_dvbt;
-
- /*
- */
- u8 vtop;
-
- /*
- */
- u8 krf;
-
- /*
- */
- u8 agc_targ_val;
-};
-
-#if defined(CONFIG_DVB_RTL2830) || \
- (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
-extern struct dvb_frontend *rtl2830_attach(
- const struct rtl2830_config *config,
- struct i2c_adapter *i2c
-);
-
-extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-);
-#else
-static inline struct dvb_frontend *rtl2830_attach(
- const struct rtl2830_config *config,
- struct i2c_adapter *i2c
-)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-
-static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-)
-{
- return NULL;
-}
-#endif
-
-#endif /* RTL2830_H */
diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h
deleted file mode 100644
index 9b20557ccf6c..000000000000
--- a/drivers/media/dvb/frontends/rtl2830_priv.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Realtek RTL2830 DVB-T demodulator driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef RTL2830_PRIV_H
-#define RTL2830_PRIV_H
-
-#include "dvb_frontend.h"
-#include "dvb_math.h"
-#include "rtl2830.h"
-
-#define LOG_PREFIX "rtl2830"
-
-#undef dbg
-#define dbg(f, arg...) \
- if (rtl2830_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct rtl2830_priv {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct rtl2830_config cfg;
- struct i2c_adapter tuner_i2c_adapter;
-
- bool sleeping;
-
- u8 page; /* active register page */
-};
-
-struct rtl2830_reg_val_mask {
- u16 reg;
- u8 val;
- u8 mask;
-};
-
-#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb/frontends/rtl2832.c b/drivers/media/dvb/frontends/rtl2832.c
deleted file mode 100644
index 28269ccaeab7..000000000000
--- a/drivers/media/dvb/frontends/rtl2832.c
+++ /dev/null
@@ -1,789 +0,0 @@
-/*
- * Realtek RTL2832 DVB-T demodulator driver
- *
- * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "rtl2832_priv.h"
-#include <linux/bitops.h>
-
-int rtl2832_debug;
-module_param_named(debug, rtl2832_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-#define REG_MASK(b) (BIT(b + 1) - 1)
-
-static const struct rtl2832_reg_entry registers[] = {
- [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2},
- [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3},
- [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2},
- [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0},
- [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7},
- [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7},
- [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6},
- [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0},
- [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0},
- [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0},
- [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0},
- [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0},
- [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0},
- [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0},
- [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0},
- [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0},
- [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4},
- [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0},
- [DVBT_REG_PI] = {0x0, 0xa, 2, 0},
- [DVBT_PIP_ON] = {0x0, 0x21, 3, 3},
- [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0},
- [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0},
- [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0},
- [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0},
- [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0},
- [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0},
- [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0},
- [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0},
- [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0},
- [DVBT_KB_P1] = {0x1, 0x64, 3, 1},
- [DVBT_KB_P2] = {0x1, 0x64, 6, 4},
- [DVBT_KB_P3] = {0x1, 0x65, 2, 0},
- [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4},
- [DVBT_AD_AVI] = {0x0, 0x9, 1, 0},
- [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2},
- [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4},
- [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0},
- [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3},
- [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0},
- [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3},
- [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0},
- [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6},
- [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0},
- [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0},
- [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2},
- [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4},
- [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3},
- [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2},
- [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4},
- [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0},
- [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3},
- [DVBT_GI_IDX] = {0x3, 0x51, 1, 0},
- [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2},
- [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0},
- [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0},
- [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0},
- [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0},
- [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0},
- [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0},
- [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0},
- [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1},
- [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0},
- [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5},
- [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6},
- [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7},
- [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0},
- [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0},
- [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0},
- [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0},
- [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6},
- [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0},
- [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6},
- [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0},
- [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0},
- [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0},
- [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0},
- [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1},
- [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1},
- [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7},
- [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0},
- [DVBT_VTOP1] = {0x1, 0x6, 5, 0},
- [DVBT_VTOP2] = {0x1, 0xc9, 5, 0},
- [DVBT_VTOP3] = {0x1, 0xca, 5, 0},
- [DVBT_KRF1] = {0x1, 0xcb, 7, 0},
- [DVBT_KRF2] = {0x1, 0x7, 7, 0},
- [DVBT_KRF3] = {0x1, 0xcd, 7, 0},
- [DVBT_KRF4] = {0x1, 0xce, 7, 0},
- [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0},
- [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0},
- [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0},
- [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0},
- [DVBT_THD_DW1] = {0x1, 0xde, 7, 0},
- [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0},
- [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3},
- [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0},
- [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5},
- [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6},
- [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7},
- [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0},
- [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1},
- [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2},
- [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3},
- [DVBT_SERIAL] = {0x1, 0x7c, 4, 4},
- [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5},
- [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0},
- [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4},
- [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7},
- [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6},
- [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4},
- [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3},
- [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2},
- [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1},
- [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0},
- [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4},
- [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3},
- [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2},
- [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1},
- [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0},
- [DVBT_SM_PASS] = {0x1, 0x93, 11, 0},
- [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0},
- [DVBT_RSSI_R] = {0x3, 0x1, 6, 0},
- [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0},
- [DVBT_REG_MON] = {0x0, 0xd, 1, 0},
- [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2},
- [DVBT_REG_GPE] = {0x0, 0xd, 7, 7},
- [DVBT_REG_GPO] = {0x0, 0x10, 0, 0},
- [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0},
-};
-
-/* write multiple hardware registers */
-static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- u8 buf[1+len];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = 1+len,
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple hardware registers */
-static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len)
-{
- int ret;
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg.i2c_addr,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg.i2c_addr,
- .flags = I2C_M_RD,
- .len = len,
- .buf = val,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- ret = 0;
- } else {
- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
- ret = -EREMOTEIO;
-}
-return ret;
-}
-
-/* write multiple registers */
-static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
- int len)
-{
- int ret;
-
-
- /* switch bank if needed */
- if (page != priv->page) {
- ret = rtl2832_wr(priv, 0x00, &page, 1);
- if (ret)
- return ret;
-
- priv->page = page;
-}
-
-return rtl2832_wr(priv, reg, val, len);
-}
-
-/* read multiple registers */
-static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val,
- int len)
-{
- int ret;
-
- /* switch bank if needed */
- if (page != priv->page) {
- ret = rtl2832_wr(priv, 0x00, &page, 1);
- if (ret)
- return ret;
-
- priv->page = page;
- }
-
- return rtl2832_rd(priv, reg, val, len);
-}
-
-#if 0 /* currently not used */
-/* write single register */
-static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val)
-{
- return rtl2832_wr_regs(priv, reg, page, &val, 1);
-}
-#endif
-
-/* read single register */
-static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val)
-{
- return rtl2832_rd_regs(priv, reg, page, val, 1);
-}
-
-int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val)
-{
- int ret;
-
- u8 reg_start_addr;
- u8 msb, lsb;
- u8 page;
- u8 reading[4];
- u32 reading_tmp;
- int i;
-
- u8 len;
- u32 mask;
-
- reg_start_addr = registers[reg].start_address;
- msb = registers[reg].msb;
- lsb = registers[reg].lsb;
- page = registers[reg].page;
-
- len = (msb >> 3) + 1;
- mask = REG_MASK(msb - lsb);
-
- ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
- if (ret)
- goto err;
-
- reading_tmp = 0;
- for (i = 0; i < len; i++)
- reading_tmp |= reading[i] << ((len - 1 - i) * 8);
-
- *val = (reading_tmp >> lsb) & mask;
-
- return ret;
-
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-
-}
-
-int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val)
-{
- int ret, i;
- u8 len;
- u8 reg_start_addr;
- u8 msb, lsb;
- u8 page;
- u32 mask;
-
-
- u8 reading[4];
- u8 writing[4];
- u32 reading_tmp;
- u32 writing_tmp;
-
-
- reg_start_addr = registers[reg].start_address;
- msb = registers[reg].msb;
- lsb = registers[reg].lsb;
- page = registers[reg].page;
-
- len = (msb >> 3) + 1;
- mask = REG_MASK(msb - lsb);
-
-
- ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len);
- if (ret)
- goto err;
-
- reading_tmp = 0;
- for (i = 0; i < len; i++)
- reading_tmp |= reading[i] << ((len - 1 - i) * 8);
-
- writing_tmp = reading_tmp & ~(mask << lsb);
- writing_tmp |= ((val & mask) << lsb);
-
-
- for (i = 0; i < len; i++)
- writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
-
- ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len);
- if (ret)
- goto err;
-
- return ret;
-
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-
-}
-
-
-static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- int ret;
- struct rtl2832_priv *priv = fe->demodulator_priv;
-
- dbg("%s: enable=%d", __func__, enable);
-
- /* gate already open or close */
- if (priv->i2c_gate_state == enable)
- return 0;
-
- ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0));
- if (ret)
- goto err;
-
- priv->i2c_gate_state = enable;
-
- return ret;
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-
-
-static int rtl2832_init(struct dvb_frontend *fe)
-{
- struct rtl2832_priv *priv = fe->demodulator_priv;
- int i, ret;
-
- u8 en_bbin;
- u64 pset_iffreq;
-
- /* initialization values for the demodulator registers */
- struct rtl2832_reg_value rtl2832_initial_regs[] = {
- {DVBT_AD_EN_REG, 0x1},
- {DVBT_AD_EN_REG1, 0x1},
- {DVBT_RSD_BER_FAIL_VAL, 0x2800},
- {DVBT_MGD_THD0, 0x10},
- {DVBT_MGD_THD1, 0x20},
- {DVBT_MGD_THD2, 0x20},
- {DVBT_MGD_THD3, 0x40},
- {DVBT_MGD_THD4, 0x22},
- {DVBT_MGD_THD5, 0x32},
- {DVBT_MGD_THD6, 0x37},
- {DVBT_MGD_THD7, 0x39},
- {DVBT_EN_BK_TRK, 0x0},
- {DVBT_EN_CACQ_NOTCH, 0x0},
- {DVBT_AD_AV_REF, 0x2a},
- {DVBT_REG_PI, 0x6},
- {DVBT_PIP_ON, 0x0},
- {DVBT_CDIV_PH0, 0x8},
- {DVBT_CDIV_PH1, 0x8},
- {DVBT_SCALE1_B92, 0x4},
- {DVBT_SCALE1_B93, 0xb0},
- {DVBT_SCALE1_BA7, 0x78},
- {DVBT_SCALE1_BA9, 0x28},
- {DVBT_SCALE1_BAA, 0x59},
- {DVBT_SCALE1_BAB, 0x83},
- {DVBT_SCALE1_BAC, 0xd4},
- {DVBT_SCALE1_BB0, 0x65},
- {DVBT_SCALE1_BB1, 0x43},
- {DVBT_KB_P1, 0x1},
- {DVBT_KB_P2, 0x4},
- {DVBT_KB_P3, 0x7},
- {DVBT_K1_CR_STEP12, 0xa},
- {DVBT_REG_GPE, 0x1},
- {DVBT_SERIAL, 0x0},
- {DVBT_CDIV_PH0, 0x9},
- {DVBT_CDIV_PH1, 0x9},
- {DVBT_MPEG_IO_OPT_2_2, 0x0},
- {DVBT_MPEG_IO_OPT_1_0, 0x0},
- {DVBT_TRK_KS_P2, 0x4},
- {DVBT_TRK_KS_I2, 0x7},
- {DVBT_TR_THD_SET2, 0x6},
- {DVBT_TRK_KC_I2, 0x5},
- {DVBT_CR_THD_SET2, 0x1},
- {DVBT_SPEC_INV, 0x0},
- {DVBT_DAGC_TRG_VAL, 0x5a},
- {DVBT_AGC_TARG_VAL_0, 0x0},
- {DVBT_AGC_TARG_VAL_8_1, 0x5a},
- {DVBT_AAGC_LOOP_GAIN, 0x16},
- {DVBT_LOOP_GAIN2_3_0, 0x6},
- {DVBT_LOOP_GAIN2_4, 0x1},
- {DVBT_LOOP_GAIN3, 0x16},
- {DVBT_VTOP1, 0x35},
- {DVBT_VTOP2, 0x21},
- {DVBT_VTOP3, 0x21},
- {DVBT_KRF1, 0x0},
- {DVBT_KRF2, 0x40},
- {DVBT_KRF3, 0x10},
- {DVBT_KRF4, 0x10},
- {DVBT_IF_AGC_MIN, 0x80},
- {DVBT_IF_AGC_MAX, 0x7f},
- {DVBT_RF_AGC_MIN, 0x80},
- {DVBT_RF_AGC_MAX, 0x7f},
- {DVBT_POLAR_RF_AGC, 0x0},
- {DVBT_POLAR_IF_AGC, 0x0},
- {DVBT_AD7_SETTING, 0xe9bf},
- {DVBT_EN_GI_PGA, 0x0},
- {DVBT_THD_LOCK_UP, 0x0},
- {DVBT_THD_LOCK_DW, 0x0},
- {DVBT_THD_UP1, 0x11},
- {DVBT_THD_DW1, 0xef},
- {DVBT_INTER_CNT_LEN, 0xc},
- {DVBT_GI_PGA_STATE, 0x0},
- {DVBT_EN_AGC_PGA, 0x1},
- {DVBT_IF_AGC_MAN, 0x0},
- };
-
-
- dbg("%s", __func__);
-
- en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0);
-
- /*
- * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22)
- * / CrystalFreqHz)
- */
- pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal;
- pset_iffreq *= 0x400000;
- pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal);
- pset_iffreq = pset_iffreq & 0x3fffff;
-
-
-
- for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) {
- ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg,
- rtl2832_initial_regs[i].value);
- if (ret)
- goto err;
- }
-
- /* if frequency settings */
- ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin);
- if (ret)
- goto err;
-
- ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq);
- if (ret)
- goto err;
-
- priv->sleeping = false;
-
- return ret;
-
-err:
- dbg("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2832_sleep(struct dvb_frontend *fe)
-{
- struct rtl2832_priv *priv = fe->demodulator_priv;
-
- dbg("%s", __func__);
- priv->sleeping = true;
- return 0;
-}
-
-int rtl2832_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- dbg("%s", __func__);
- s->min_delay_ms = 1000;
- s->step_size = fe->ops.info.frequency_stepsize * 2;
- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
- return 0;
-}
-
-static int rtl2832_set_frontend(struct dvb_frontend *fe)
-{
- struct rtl2832_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i, j;
- u64 bw_mode, num, num2;
- u32 resamp_ratio, cfreq_off_ratio;
-
-
- static u8 bw_params[3][32] = {
- /* 6 MHz bandwidth */
- {
- 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f,
- 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2,
- 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67,
- 0x19, 0xe0,
- },
-
- /* 7 MHz bandwidth */
- {
- 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf,
- 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30,
- 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22,
- 0x19, 0x10,
- },
-
- /* 8 MHz bandwidth */
- {
- 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf,
- 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7,
- 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8,
- 0x19, 0xe0,
- },
- };
-
-
- dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
- c->frequency, c->bandwidth_hz, c->inversion);
-
-
- /* program tuner */
- if (fe->ops.tuner_ops.set_params)
- fe->ops.tuner_ops.set_params(fe);
-
-
- switch (c->bandwidth_hz) {
- case 6000000:
- i = 0;
- bw_mode = 48000000;
- break;
- case 7000000:
- i = 1;
- bw_mode = 56000000;
- break;
- case 8000000:
- i = 2;
- bw_mode = 64000000;
- break;
- default:
- dbg("invalid bandwidth");
- return -EINVAL;
- }
-
- for (j = 0; j < sizeof(bw_params[0]); j++) {
- ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1);
- if (ret)
- goto err;
- }
-
- /* calculate and set resample ratio
- * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22)
- * / ConstWithBandwidthMode)
- */
- num = priv->cfg.xtal * 7;
- num *= 0x400000;
- num = div_u64(num, bw_mode);
- resamp_ratio = num & 0x3ffffff;
- ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio);
- if (ret)
- goto err;
-
- /* calculate and set cfreq off ratio
- * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20)
- * / (CrystalFreqHz * 7))
- */
- num = bw_mode << 20;
- num2 = priv->cfg.xtal * 7;
- num = div_u64(num, num2);
- num = -num;
- cfreq_off_ratio = num & 0xfffff;
- ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio);
- if (ret)
- goto err;
-
-
- /* soft reset */
- ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1);
- if (ret)
- goto err;
-
- ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0);
- if (ret)
- goto err;
-
- return ret;
-err:
- info("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct rtl2832_priv *priv = fe->demodulator_priv;
- int ret;
- u32 tmp;
- *status = 0;
-
-
- dbg("%s", __func__);
- if (priv->sleeping)
- return 0;
-
- ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp);
- if (ret)
- goto err;
-
- if (tmp == 11) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
- }
- /* TODO find out if this is also true for rtl2832? */
- /*else if (tmp == 10) {
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
- FE_HAS_VITERBI;
- }*/
-
- return ret;
-err:
- info("%s: failed=%d", __func__, ret);
- return ret;
-}
-
-static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- *snr = 0;
- return 0;
-}
-
-static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- *ber = 0;
- return 0;
-}
-
-static int rtl2832_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- *ucblocks = 0;
- return 0;
-}
-
-
-static int rtl2832_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- *strength = 0;
- return 0;
-}
-
-static struct dvb_frontend_ops rtl2832_ops;
-
-static void rtl2832_release(struct dvb_frontend *fe)
-{
- struct rtl2832_priv *priv = fe->demodulator_priv;
-
- dbg("%s", __func__);
- kfree(priv);
-}
-
-struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg,
- struct i2c_adapter *i2c)
-{
- struct rtl2832_priv *priv = NULL;
- int ret = 0;
- u8 tmp;
-
- dbg("%s", __func__);
-
- /* allocate memory for the internal state */
- priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL);
- if (priv == NULL)
- goto err;
-
- /* setup the priv */
- priv->i2c = i2c;
- priv->tuner = cfg->tuner;
- memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config));
-
- /* check if the demod is there */
- ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp);
- if (ret)
- goto err;
-
- /* create dvb_frontend */
- memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
- priv->fe.demodulator_priv = priv;
-
- /* TODO implement sleep mode */
- priv->sleeping = true;
-
- return &priv->fe;
-err:
- dbg("%s: failed=%d", __func__, ret);
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(rtl2832_attach);
-
-static struct dvb_frontend_ops rtl2832_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Realtek RTL2832 (DVB-T)",
- .frequency_min = 174000000,
- .frequency_max = 862000000,
- .frequency_stepsize = 166667,
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_HIERARCHY_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_MUTE_TS
- },
-
- .release = rtl2832_release,
-
- .init = rtl2832_init,
- .sleep = rtl2832_sleep,
-
- .get_tune_settings = rtl2832_get_tune_settings,
-
- .set_frontend = rtl2832_set_frontend,
-
- .read_status = rtl2832_read_status,
- .read_snr = rtl2832_read_snr,
- .read_ber = rtl2832_read_ber,
- .read_ucblocks = rtl2832_read_ucblocks,
- .read_signal_strength = rtl2832_read_signal_strength,
- .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl,
-};
-
-MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>");
-MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/frontends/rtl2832.h b/drivers/media/dvb/frontends/rtl2832.h
deleted file mode 100644
index d94dc9a3fa62..000000000000
--- a/drivers/media/dvb/frontends/rtl2832.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Realtek RTL2832 DVB-T demodulator driver
- *
- * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef RTL2832_H
-#define RTL2832_H
-
-#include <linux/dvb/frontend.h>
-
-struct rtl2832_config {
- /*
- * Demodulator I2C address.
- */
- u8 i2c_addr;
-
- /*
- * Xtal frequency.
- * Hz
- * 4000000, 16000000, 25000000, 28800000
- */
- u32 xtal;
-
- /*
- * IFs for all used modes.
- * Hz
- * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
- */
- u32 if_dvbt;
-
- /*
- */
- u8 tuner;
-};
-
-
-#if defined(CONFIG_DVB_RTL2832) || \
- (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE))
-extern struct dvb_frontend *rtl2832_attach(
- const struct rtl2832_config *cfg,
- struct i2c_adapter *i2c
-);
-
-extern struct i2c_adapter *rtl2832_get_tuner_i2c_adapter(
- struct dvb_frontend *fe
-);
-#else
-static inline struct dvb_frontend *rtl2832_attach(
- const struct rtl2832_config *config,
- struct i2c_adapter *i2c
-)
-{
- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
- return NULL;
-}
-#endif
-
-
-#endif /* RTL2832_H */
diff --git a/drivers/media/dvb/frontends/rtl2832_priv.h b/drivers/media/dvb/frontends/rtl2832_priv.h
deleted file mode 100644
index 0ce9502da8ba..000000000000
--- a/drivers/media/dvb/frontends/rtl2832_priv.h
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Realtek RTL2832 DVB-T demodulator driver
- *
- * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef RTL2832_PRIV_H
-#define RTL2832_PRIV_H
-
-#include "dvb_frontend.h"
-#include "rtl2832.h"
-
-#define LOG_PREFIX "rtl2832"
-
-#undef dbg
-#define dbg(f, arg...) \
-do { \
- if (rtl2832_debug) \
- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg); \
-} while (0)
-#undef err
-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
-#undef info
-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
-#undef warn
-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
-
-struct rtl2832_priv {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct rtl2832_config cfg;
-
- bool i2c_gate_state;
- bool sleeping;
-
- u8 tuner;
- u8 page; /* active register page */
-};
-
-struct rtl2832_reg_entry {
- u8 page;
- u8 start_address;
- u8 msb;
- u8 lsb;
-};
-
-struct rtl2832_reg_value {
- int reg;
- u32 value;
-};
-
-
-/* Demod register bit names */
-enum DVBT_REG_BIT_NAME {
- DVBT_SOFT_RST,
- DVBT_IIC_REPEAT,
- DVBT_TR_WAIT_MIN_8K,
- DVBT_RSD_BER_FAIL_VAL,
- DVBT_EN_BK_TRK,
- DVBT_REG_PI,
- DVBT_REG_PFREQ_1_0,
- DVBT_PD_DA8,
- DVBT_LOCK_TH,
- DVBT_BER_PASS_SCAL,
- DVBT_CE_FFSM_BYPASS,
- DVBT_ALPHAIIR_N,
- DVBT_ALPHAIIR_DIF,
- DVBT_EN_TRK_SPAN,
- DVBT_LOCK_TH_LEN,
- DVBT_CCI_THRE,
- DVBT_CCI_MON_SCAL,
- DVBT_CCI_M0,
- DVBT_CCI_M1,
- DVBT_CCI_M2,
- DVBT_CCI_M3,
- DVBT_SPEC_INIT_0,
- DVBT_SPEC_INIT_1,
- DVBT_SPEC_INIT_2,
- DVBT_AD_EN_REG,
- DVBT_AD_EN_REG1,
- DVBT_EN_BBIN,
- DVBT_MGD_THD0,
- DVBT_MGD_THD1,
- DVBT_MGD_THD2,
- DVBT_MGD_THD3,
- DVBT_MGD_THD4,
- DVBT_MGD_THD5,
- DVBT_MGD_THD6,
- DVBT_MGD_THD7,
- DVBT_EN_CACQ_NOTCH,
- DVBT_AD_AV_REF,
- DVBT_PIP_ON,
- DVBT_SCALE1_B92,
- DVBT_SCALE1_B93,
- DVBT_SCALE1_BA7,
- DVBT_SCALE1_BA9,
- DVBT_SCALE1_BAA,
- DVBT_SCALE1_BAB,
- DVBT_SCALE1_BAC,
- DVBT_SCALE1_BB0,
- DVBT_SCALE1_BB1,
- DVBT_KB_P1,
- DVBT_KB_P2,
- DVBT_KB_P3,
- DVBT_OPT_ADC_IQ,
- DVBT_AD_AVI,
- DVBT_AD_AVQ,
- DVBT_K1_CR_STEP12,
- DVBT_TRK_KS_P2,
- DVBT_TRK_KS_I2,
- DVBT_TR_THD_SET2,
- DVBT_TRK_KC_P2,
- DVBT_TRK_KC_I2,
- DVBT_CR_THD_SET2,
- DVBT_PSET_IFFREQ,
- DVBT_SPEC_INV,
- DVBT_BW_INDEX,
- DVBT_RSAMP_RATIO,
- DVBT_CFREQ_OFF_RATIO,
- DVBT_FSM_STAGE,
- DVBT_RX_CONSTEL,
- DVBT_RX_HIER,
- DVBT_RX_C_RATE_LP,
- DVBT_RX_C_RATE_HP,
- DVBT_GI_IDX,
- DVBT_FFT_MODE_IDX,
- DVBT_RSD_BER_EST,
- DVBT_CE_EST_EVM,
- DVBT_RF_AGC_VAL,
- DVBT_IF_AGC_VAL,
- DVBT_DAGC_VAL,
- DVBT_SFREQ_OFF,
- DVBT_CFREQ_OFF,
- DVBT_POLAR_RF_AGC,
- DVBT_POLAR_IF_AGC,
- DVBT_AAGC_HOLD,
- DVBT_EN_RF_AGC,
- DVBT_EN_IF_AGC,
- DVBT_IF_AGC_MIN,
- DVBT_IF_AGC_MAX,
- DVBT_RF_AGC_MIN,
- DVBT_RF_AGC_MAX,
- DVBT_IF_AGC_MAN,
- DVBT_IF_AGC_MAN_VAL,
- DVBT_RF_AGC_MAN,
- DVBT_RF_AGC_MAN_VAL,
- DVBT_DAGC_TRG_VAL,
- DVBT_AGC_TARG_VAL,
- DVBT_LOOP_GAIN_3_0,
- DVBT_LOOP_GAIN_4,
- DVBT_VTOP,
- DVBT_KRF,
- DVBT_AGC_TARG_VAL_0,
- DVBT_AGC_TARG_VAL_8_1,
- DVBT_AAGC_LOOP_GAIN,
- DVBT_LOOP_GAIN2_3_0,
- DVBT_LOOP_GAIN2_4,
- DVBT_LOOP_GAIN3,
- DVBT_VTOP1,
- DVBT_VTOP2,
- DVBT_VTOP3,
- DVBT_KRF1,
- DVBT_KRF2,
- DVBT_KRF3,
- DVBT_KRF4,
- DVBT_EN_GI_PGA,
- DVBT_THD_LOCK_UP,
- DVBT_THD_LOCK_DW,
- DVBT_THD_UP1,
- DVBT_THD_DW1,
- DVBT_INTER_CNT_LEN,
- DVBT_GI_PGA_STATE,
- DVBT_EN_AGC_PGA,
- DVBT_CKOUTPAR,
- DVBT_CKOUT_PWR,
- DVBT_SYNC_DUR,
- DVBT_ERR_DUR,
- DVBT_SYNC_LVL,
- DVBT_ERR_LVL,
- DVBT_VAL_LVL,
- DVBT_SERIAL,
- DVBT_SER_LSB,
- DVBT_CDIV_PH0,
- DVBT_CDIV_PH1,
- DVBT_MPEG_IO_OPT_2_2,
- DVBT_MPEG_IO_OPT_1_0,
- DVBT_CKOUTPAR_PIP,
- DVBT_CKOUT_PWR_PIP,
- DVBT_SYNC_LVL_PIP,
- DVBT_ERR_LVL_PIP,
- DVBT_VAL_LVL_PIP,
- DVBT_CKOUTPAR_PID,
- DVBT_CKOUT_PWR_PID,
- DVBT_SYNC_LVL_PID,
- DVBT_ERR_LVL_PID,
- DVBT_VAL_LVL_PID,
- DVBT_SM_PASS,
- DVBT_UPDATE_REG_2,
- DVBT_BTHD_P3,
- DVBT_BTHD_D3,
- DVBT_FUNC4_REG0,
- DVBT_FUNC4_REG1,
- DVBT_FUNC4_REG2,
- DVBT_FUNC4_REG3,
- DVBT_FUNC4_REG4,
- DVBT_FUNC4_REG5,
- DVBT_FUNC4_REG6,
- DVBT_FUNC4_REG7,
- DVBT_FUNC4_REG8,
- DVBT_FUNC4_REG9,
- DVBT_FUNC4_REG10,
- DVBT_FUNC5_REG0,
- DVBT_FUNC5_REG1,
- DVBT_FUNC5_REG2,
- DVBT_FUNC5_REG3,
- DVBT_FUNC5_REG4,
- DVBT_FUNC5_REG5,
- DVBT_FUNC5_REG6,
- DVBT_FUNC5_REG7,
- DVBT_FUNC5_REG8,
- DVBT_FUNC5_REG9,
- DVBT_FUNC5_REG10,
- DVBT_FUNC5_REG11,
- DVBT_FUNC5_REG12,
- DVBT_FUNC5_REG13,
- DVBT_FUNC5_REG14,
- DVBT_FUNC5_REG15,
- DVBT_FUNC5_REG16,
- DVBT_FUNC5_REG17,
- DVBT_FUNC5_REG18,
- DVBT_AD7_SETTING,
- DVBT_RSSI_R,
- DVBT_ACI_DET_IND,
- DVBT_REG_MON,
- DVBT_REG_MONSEL,
- DVBT_REG_GPE,
- DVBT_REG_GPO,
- DVBT_REG_4MSEL,
- DVBT_TEST_REG_1,
- DVBT_TEST_REG_2,
- DVBT_TEST_REG_3,
- DVBT_TEST_REG_4,
- DVBT_REG_BIT_NAME_ITEM_TERMINATOR,
-};
-
-#endif /* RTL2832_PRIV_H */
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
deleted file mode 100644
index 5d7f8a9b451b..000000000000
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ /dev/null
@@ -1,1651 +0,0 @@
-/*
- STB0899 Multistandard Frontend driver
- Copyright (C) Manu Abraham (abraham.manu@gmail.com)
-
- Copyright (C) ST Microelectronics
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-#include "stb0899_drv.h"
-#include "stb0899_priv.h"
-#include "stb0899_reg.h"
-
-static unsigned int verbose = 0;//1;
-module_param(verbose, int, 0644);
-
-/* C/N in dB/10, NIRM/NIRL */
-static const struct stb0899_tab stb0899_cn_tab[] = {
- { 200, 2600 },
- { 190, 2700 },
- { 180, 2860 },
- { 170, 3020 },
- { 160, 3210 },
- { 150, 3440 },
- { 140, 3710 },
- { 130, 4010 },
- { 120, 4360 },
- { 110, 4740 },
- { 100, 5190 },
- { 90, 5670 },
- { 80, 6200 },
- { 70, 6770 },
- { 60, 7360 },
- { 50, 7970 },
- { 40, 8250 },
- { 30, 9000 },
- { 20, 9450 },
- { 15, 9600 },
-};
-
-/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10.
- * As measured, connected to a modulator.
- * -8.0 to -50.0 dBm directly connected,
- * -52.0 to -74.8 with extra attenuation.
- * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm.
- * Crude linear extrapolation below -84.8dBm and above -8.0dBm.
- */
-static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
- { -750, -128 },
- { -748, -94 },
- { -745, -92 },
- { -735, -90 },
- { -720, -87 },
- { -670, -77 },
- { -640, -70 },
- { -610, -62 },
- { -600, -60 },
- { -590, -56 },
- { -560, -41 },
- { -540, -25 },
- { -530, -17 },
- { -520, -11 },
- { -500, 1 },
- { -490, 6 },
- { -480, 10 },
- { -440, 22 },
- { -420, 27 },
- { -400, 31 },
- { -380, 34 },
- { -340, 40 },
- { -320, 43 },
- { -280, 48 },
- { -250, 52 },
- { -230, 55 },
- { -180, 61 },
- { -140, 66 },
- { -90, 73 },
- { -80, 74 },
- { 500, 127 }
-};
-
-/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10.
- * As measured, connected to a modulator.
- * -8.0 to -50.1 dBm directly connected,
- * -53.0 to -76.6 with extra attenuation.
- * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm.
- * Crude linear extrapolation below -76.6dBm and above -8.0dBm.
- */
-static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
- { 700, 0 },
- { -80, 3217 },
- { -150, 3893 },
- { -190, 4217 },
- { -240, 4621 },
- { -280, 4945 },
- { -320, 5273 },
- { -350, 5545 },
- { -370, 5741 },
- { -410, 6147 },
- { -450, 6671 },
- { -490, 7413 },
- { -501, 7665 },
- { -530, 8767 },
- { -560, 10219 },
- { -580, 10939 },
- { -590, 11518 },
- { -600, 11723 },
- { -650, 12659 },
- { -690, 13219 },
- { -730, 13645 },
- { -750, 13909 },
- { -766, 14153 },
- { -950, 16383 }
-};
-
-/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
-static struct stb0899_tab stb0899_quant_tab[] = {
- { 0, 0 },
- { 0, 100 },
- { 600, 200 },
- { 950, 299 },
- { 1200, 398 },
- { 1400, 501 },
- { 1560, 603 },
- { 1690, 700 },
- { 1810, 804 },
- { 1910, 902 },
- { 2000, 1000 },
- { 2080, 1096 },
- { 2160, 1202 },
- { 2230, 1303 },
- { 2350, 1496 },
- { 2410, 1603 },
- { 2460, 1698 },
- { 2510, 1799 },
- { 2600, 1995 },
- { 2650, 2113 },
- { 2690, 2213 },
- { 2720, 2291 },
- { 2760, 2399 },
- { 2800, 2512 },
- { 2860, 2692 },
- { 2930, 2917 },
- { 2960, 3020 },
- { 3010, 3199 },
- { 3040, 3311 },
- { 3060, 3388 },
- { 3120, 3631 },
- { 3190, 3936 },
- { 3400, 5012 },
- { 3610, 6383 },
- { 3800, 7943 },
- { 4210, 12735 },
- { 4500, 17783 },
- { 4690, 22131 },
- { 4810, 25410 }
-};
-
-/* DVB-S2 Es/N0 estimate in dB/100 vs read value */
-static struct stb0899_tab stb0899_est_tab[] = {
- { 0, 0 },
- { 0, 1 },
- { 301, 2 },
- { 1204, 16 },
- { 1806, 64 },
- { 2408, 256 },
- { 2709, 512 },
- { 3010, 1023 },
- { 3311, 2046 },
- { 3612, 4093 },
- { 3823, 6653 },
- { 3913, 8185 },
- { 4010, 10233 },
- { 4107, 12794 },
- { 4214, 16368 },
- { 4266, 18450 },
- { 4311, 20464 },
- { 4353, 22542 },
- { 4391, 24604 },
- { 4425, 26607 },
- { 4457, 28642 },
- { 4487, 30690 },
- { 4515, 32734 },
- { 4612, 40926 },
- { 4692, 49204 },
- { 4816, 65464 },
- { 4913, 81846 },
- { 4993, 98401 },
- { 5060, 114815 },
- { 5118, 131220 },
- { 5200, 158489 },
- { 5300, 199526 },
- { 5400, 251189 },
- { 5500, 316228 },
- { 5600, 398107 },
- { 5720, 524807 },
- { 5721, 526017 },
-};
-
-static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
-{
- int ret;
-
- u8 b0[] = { reg >> 8, reg & 0xff };
- u8 buf;
-
- 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 = &buf,
- .len = 1
- }
- };
-
- ret = i2c_transfer(state->i2c, msg, 2);
- if (ret != 2) {
- if (ret != -ERESTARTSYS)
- dprintk(state->verbose, FE_ERROR, 1,
- "Read error, Reg=[0x%02x], Status=%d",
- reg, ret);
-
- return ret < 0 ? ret : -EREMOTEIO;
- }
- if (unlikely(*state->verbose >= FE_DEBUGREG))
- dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
- reg, buf);
-
- return (unsigned int)buf;
-}
-
-int stb0899_read_reg(struct stb0899_state *state, unsigned int reg)
-{
- int result;
-
- result = _stb0899_read_reg(state, reg);
- /*
- * Bug ID 9:
- * access to 0xf2xx/0xf6xx
- * must be followed by read from 0xf2ff/0xf6ff.
- */
- if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
- (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
- _stb0899_read_reg(state, (reg | 0x00ff));
-
- return result;
-}
-
-u32 _stb0899_read_s2reg(struct stb0899_state *state,
- u32 stb0899_i2cdev,
- u32 stb0899_base_addr,
- u16 stb0899_reg_offset)
-{
- int status;
- u32 data;
- u8 buf[7] = { 0 };
- u16 tmpaddr;
-
- u8 buf_0[] = {
- GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
- GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
- GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
- };
- u8 buf_1[] = {
- 0x00, /* 0xf3 Reg Offset */
- 0x00, /* 0x44 Reg Offset */
- };
-
- struct i2c_msg msg_0 = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf_0,
- .len = 6
- };
-
- struct i2c_msg msg_1 = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf_1,
- .len = 2
- };
-
- struct i2c_msg msg_r = {
- .addr = state->config->demod_address,
- .flags = I2C_M_RD,
- .buf = buf,
- .len = 4
- };
-
- tmpaddr = stb0899_reg_offset & 0xff00;
- if (!(stb0899_reg_offset & 0x8))
- tmpaddr = stb0899_reg_offset | 0x20;
-
- buf_1[0] = GETBYTE(tmpaddr, BYTE1);
- buf_1[1] = GETBYTE(tmpaddr, BYTE0);
-
- status = i2c_transfer(state->i2c, &msg_0, 1);
- if (status < 1) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
-
- goto err;
- }
-
- /* Dummy */
- status = i2c_transfer(state->i2c, &msg_1, 1);
- if (status < 1)
- goto err;
-
- status = i2c_transfer(state->i2c, &msg_r, 1);
- if (status < 1)
- goto err;
-
- buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
- buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
-
- /* Actual */
- status = i2c_transfer(state->i2c, &msg_1, 1);
- if (status < 1) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
- goto err;
- }
-
- status = i2c_transfer(state->i2c, &msg_r, 1);
- if (status < 1) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status);
- return status < 0 ? status : -EREMOTEIO;
- }
-
- data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]);
- if (unlikely(*state->verbose >= FE_DEBUGREG))
- printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data);
-
- return data;
-
-err:
- return status < 0 ? status : -EREMOTEIO;
-}
-
-int stb0899_write_s2reg(struct stb0899_state *state,
- u32 stb0899_i2cdev,
- u32 stb0899_base_addr,
- u16 stb0899_reg_offset,
- u32 stb0899_data)
-{
- int status;
-
- /* Base Address Setup */
- u8 buf_0[] = {
- GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */
- GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */
- GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */
- GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */
- };
- u8 buf_1[] = {
- 0x00, /* 0xf3 Reg Offset */
- 0x00, /* 0x44 Reg Offset */
- 0x00, /* data */
- 0x00, /* data */
- 0x00, /* data */
- 0x00, /* data */
- };
-
- struct i2c_msg msg_0 = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf_0,
- .len = 6
- };
-
- struct i2c_msg msg_1 = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf_1,
- .len = 6
- };
-
- buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1);
- buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0);
- buf_1[2] = GETBYTE(stb0899_data, BYTE0);
- buf_1[3] = GETBYTE(stb0899_data, BYTE1);
- buf_1[4] = GETBYTE(stb0899_data, BYTE2);
- buf_1[5] = GETBYTE(stb0899_data, BYTE3);
-
- if (unlikely(*state->verbose >= FE_DEBUGREG))
- printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data);
-
- status = i2c_transfer(state->i2c, &msg_0, 1);
- if (unlikely(status < 1)) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
- goto err;
- }
- status = i2c_transfer(state->i2c, &msg_1, 1);
- if (unlikely(status < 1)) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n",
- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status);
-
- return status < 0 ? status : -EREMOTEIO;
- }
-
- return 0;
-
-err:
- return status < 0 ? status : -EREMOTEIO;
-}
-
-int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count)
-{
- int status;
-
- u8 b0[] = { reg >> 8, reg & 0xff };
-
- 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 = buf,
- .len = count
- }
- };
-
- status = i2c_transfer(state->i2c, msg, 2);
- if (status != 2) {
- if (status != -ERESTARTSYS)
- printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n",
- __func__, reg, count, status);
- goto err;
- }
- /*
- * Bug ID 9:
- * access to 0xf2xx/0xf6xx
- * must be followed by read from 0xf2ff/0xf6ff.
- */
- if ((reg != 0xf2ff) && (reg != 0xf6ff) &&
- (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
- _stb0899_read_reg(state, (reg | 0x00ff));
-
- if (unlikely(*state->verbose >= FE_DEBUGREG)) {
- int i;
-
- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
- for (i = 0; i < count; i++) {
- printk(" %02x", buf[i]);
- }
- printk("\n");
- }
-
- return 0;
-err:
- return status < 0 ? status : -EREMOTEIO;
-}
-
-int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count)
-{
- int ret;
- u8 buf[2 + count];
- struct i2c_msg i2c_msg = {
- .addr = state->config->demod_address,
- .flags = 0,
- .buf = buf,
- .len = 2 + count
- };
-
- buf[0] = reg >> 8;
- buf[1] = reg & 0xff;
- memcpy(&buf[2], data, count);
-
- if (unlikely(*state->verbose >= FE_DEBUGREG)) {
- int i;
-
- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
- for (i = 0; i < count; i++)
- printk(" %02x", data[i]);
- printk("\n");
- }
- ret = i2c_transfer(state->i2c, &i2c_msg, 1);
-
- /*
- * Bug ID 9:
- * access to 0xf2xx/0xf6xx
- * must be followed by read from 0xf2ff/0xf6ff.
- */
- if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600)))
- stb0899_read_reg(state, (reg | 0x00ff));
-
- if (ret != 1) {
- if (ret != -ERESTARTSYS)
- dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
- reg, data[0], count, ret);
- return ret < 0 ? ret : -EREMOTEIO;
- }
-
- return 0;
-}
-
-int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data)
-{
- return stb0899_write_regs(state, reg, &data, 1);
-}
-
-/*
- * stb0899_get_mclk
- * Get STB0899 master clock frequency
- * ExtClk: external clock frequency (Hz)
- */
-static u32 stb0899_get_mclk(struct stb0899_state *state)
-{
- u32 mclk = 0, div = 0;
-
- div = stb0899_read_reg(state, STB0899_NCOARSE);
- mclk = (div + 1) * state->config->xtal_freq / 6;
- dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk);
-
- return mclk;
-}
-
-/*
- * stb0899_set_mclk
- * Set STB0899 master Clock frequency
- * Mclk: demodulator master clock
- * ExtClk: external clock frequency (Hz)
- */
-static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk)
-{
- struct stb0899_internal *internal = &state->internal;
- u8 mdiv = 0;
-
- dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config);
- mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1;
- dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv);
-
- stb0899_write_reg(state, STB0899_NCOARSE, mdiv);
- internal->master_clk = stb0899_get_mclk(state);
-
- dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk);
-}
-
-static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable)
-{
- struct stb0899_config *config = state->config;
- const struct stb0899_postproc *postproc = config->postproc;
-
- /* post process event */
- if (postproc) {
- if (enable) {
- if (postproc[ctl].level == STB0899_GPIOPULLUP)
- stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
- else
- stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
- } else {
- if (postproc[ctl].level == STB0899_GPIOPULLUP)
- stb0899_write_reg(state, postproc[ctl].gpio, 0x82);
- else
- stb0899_write_reg(state, postproc[ctl].gpio, 0x02);
- }
- }
- return 0;
-}
-
-static void stb0899_release(struct dvb_frontend *fe)
-{
- struct stb0899_state *state = fe->demodulator_priv;
-
- dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend");
- /* post process event */
- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
- kfree(state);
-}
-
-/*
- * stb0899_get_alpha
- * return: rolloff
- */
-static int stb0899_get_alpha(struct stb0899_state *state)
-{
- u8 mode_coeff;
-
- mode_coeff = stb0899_read_reg(state, STB0899_DEMOD);
-
- if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1)
- return 20;
- else
- return 35;
-}
-
-/*
- * stb0899_init_calc
- */
-static void stb0899_init_calc(struct stb0899_state *state)
-{
- struct stb0899_internal *internal = &state->internal;
- int master_clk;
- u8 agc[2];
- u32 reg;
-
- /* Read registers (in burst mode) */
- stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */
-
- /* Initial calculations */
- master_clk = stb0899_get_mclk(state);
- internal->t_agc1 = 0;
- internal->t_agc2 = 0;
- internal->master_clk = master_clk;
- internal->mclk = master_clk / 65536L;
- internal->rolloff = stb0899_get_alpha(state);
-
- /* DVBS2 Initial calculations */
- /* Set AGC value to the middle */
- internal->agc_gain = 8154;
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL);
- STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain);
- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg);
-
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA);
- internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg);
-
- internal->center_freq = 0;
- internal->av_frame_coarse = 10;
- internal->av_frame_fine = 20;
- internal->step_size = 2;
-/*
- if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO))
- pParams->IQLocked = 0;
- else
- pParams->IQLocked = 1;
-*/
-}
-
-static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout)
-{
- u8 reg = 0;
- unsigned long start = jiffies;
-
- while (1) {
- reg = stb0899_read_reg(state, STB0899_DISSTATUS);
- if (!STB0899_GETFIELD(FIFOFULL, reg))
- break;
- if ((jiffies - start) > timeout) {
- dprintk(state->verbose, FE_ERROR, 1, "timed out !!");
- return -ETIMEDOUT;
- }
- }
-
- return 0;
-}
-
-static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- u8 reg, i;
-
- if (cmd->msg_len > 8)
- return -EINVAL;
-
- /* enable FIFO precharge */
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
- for (i = 0; i < cmd->msg_len; i++) {
- /* wait for FIFO empty */
- if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0)
- return -ETIMEDOUT;
-
- stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]);
- }
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
- msleep(100);
- return 0;
-}
-
-static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout)
-{
- u8 reg = 0;
- unsigned long start = jiffies;
-
- while (!STB0899_GETFIELD(RXEND, reg)) {
- reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
- if (jiffies - start > timeout) {
- dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
- return -ETIMEDOUT;
- }
- msleep(10);
- }
-
- return 0;
-}
-
-static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- u8 reg, length = 0, i;
- int result;
-
- if (stb0899_wait_diseqc_rxidle(state, 100) < 0)
- return -ETIMEDOUT;
-
- reg = stb0899_read_reg(state, STB0899_DISRX_ST0);
- if (STB0899_GETFIELD(RXEND, reg)) {
-
- reg = stb0899_read_reg(state, STB0899_DISRX_ST1);
- length = STB0899_GETFIELD(FIFOBYTENBR, reg);
-
- if (length > sizeof (reply->msg)) {
- result = -EOVERFLOW;
- goto exit;
- }
- reply->msg_len = length;
-
- /* extract data */
- for (i = 0; i < length; i++)
- reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO);
- }
-
- return 0;
-exit:
-
- return result;
-}
-
-static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout)
-{
- u8 reg = 0;
- unsigned long start = jiffies;
-
- while (!STB0899_GETFIELD(TXIDLE, reg)) {
- reg = stb0899_read_reg(state, STB0899_DISSTATUS);
- if (jiffies - start > timeout) {
- dprintk(state->verbose, FE_ERROR, 1, "timed out!!");
- return -ETIMEDOUT;
- }
- msleep(10);
- }
- return 0;
-}
-
-static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- u8 reg, old_state;
-
- /* wait for diseqc idle */
- if (stb0899_wait_diseqc_txidle(state, 100) < 0)
- return -ETIMEDOUT;
-
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- old_state = reg;
- /* set to burst mode */
- STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03);
- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
- switch (burst) {
- case SEC_MINI_A:
- /* unmodulated */
- stb0899_write_reg(state, STB0899_DISFIFO, 0x00);
- break;
- case SEC_MINI_B:
- /* modulated */
- stb0899_write_reg(state, STB0899_DISFIFO, 0xff);
- break;
- }
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
- /* wait for diseqc idle */
- if (stb0899_wait_diseqc_txidle(state, 100) < 0)
- return -ETIMEDOUT;
-
- /* restore state */
- stb0899_write_reg(state, STB0899_DISCNTRL1, old_state);
-
- return 0;
-}
-
-static int stb0899_diseqc_init(struct stb0899_state *state)
-{
-/*
- struct dvb_diseqc_slave_reply rx_data;
-*/
- u8 f22_tx, reg;
-
- u32 mclk, tx_freq = 22000;/* count = 0, i; */
- reg = stb0899_read_reg(state, STB0899_DISCNTRL2);
- STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0);
- stb0899_write_reg(state, STB0899_DISCNTRL2, reg);
-
- /* disable Tx spy */
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
-
- reg = stb0899_read_reg(state, STB0899_DISCNTRL1);
- STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0);
- stb0899_write_reg(state, STB0899_DISCNTRL1, reg);
-
- mclk = stb0899_get_mclk(state);
- f22_tx = mclk / (tx_freq * 32);
- stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */
- state->rx_freq = 20000;
-
- return 0;
-}
-
-static int stb0899_sleep(struct dvb_frontend *fe)
-{
- struct stb0899_state *state = fe->demodulator_priv;
-/*
- u8 reg;
-*/
- dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))");
- /* post process event */
- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0);
-
- return 0;
-}
-
-static int stb0899_wakeup(struct dvb_frontend *fe)
-{
- int rc;
- struct stb0899_state *state = fe->demodulator_priv;
-
- if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI)))
- return rc;
- /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */
- if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00)))
- return rc;
- if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00)))
- return rc;
-
- /* post process event */
- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1);
-
- return 0;
-}
-
-static int stb0899_init(struct dvb_frontend *fe)
-{
- int i;
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_config *config = state->config;
-
- dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... ");
-
- /* init device */
- dprintk(state->verbose, FE_DEBUG, 1, "init device");
- for (i = 0; config->init_dev[i].address != 0xffff; i++)
- stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data);
-
- dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod");
- /* init S2 demod */
- for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++)
- stb0899_write_s2reg(state, STB0899_S2DEMOD,
- config->init_s2_demod[i].base_address,
- config->init_s2_demod[i].offset,
- config->init_s2_demod[i].data);
-
- dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod");
- /* init S1 demod */
- for (i = 0; config->init_s1_demod[i].address != 0xffff; i++)
- stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data);
-
- dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC");
- /* init S2 fec */
- for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++)
- stb0899_write_s2reg(state, STB0899_S2FEC,
- config->init_s2_fec[i].base_address,
- config->init_s2_fec[i].offset,
- config->init_s2_fec[i].data);
-
- dprintk(state->verbose, FE_DEBUG, 1, "init TST");
- /* init test */
- for (i = 0; config->init_tst[i].address != 0xffff; i++)
- stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data);
-
- stb0899_init_calc(state);
- stb0899_diseqc_init(state);
-
- return 0;
-}
-
-static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val)
-{
- int res = 0;
- int min = 0, med;
-
- if (val < tab[min].read)
- res = tab[min].real;
- else if (val >= tab[max].read)
- res = tab[max].real;
- else {
- while ((max - min) > 1) {
- med = (max + min) / 2;
- if (val >= tab[min].read && val < tab[med].read)
- max = med;
- else
- min = med;
- }
- res = ((val - tab[min].read) *
- (tab[max].real - tab[min].real) /
- (tab[max].read - tab[min].read)) +
- tab[min].real;
- }
-
- return res;
-}
-
-static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
-
- int val;
- u32 reg;
- *strength = 0;
- switch (state->delsys) {
- case SYS_DVBS:
- case SYS_DSS:
- if (internal->lock) {
- reg = stb0899_read_reg(state, STB0899_VSTATUS);
- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
-
- reg = stb0899_read_reg(state, STB0899_AGCIQIN);
- val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg);
-
- *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val);
- *strength += 750;
- dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm",
- val & 0xff, *strength);
- }
- }
- break;
- case SYS_DVBS2:
- if (internal->lock) {
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN);
- val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
-
- *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
- *strength += 950;
- dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
- val & 0x3fff, *strength);
- }
- break;
- default:
- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
-
- unsigned int val, quant, quantn = -1, est, estn = -1;
- u8 buf[2];
- u32 reg;
-
- *snr = 0;
- reg = stb0899_read_reg(state, STB0899_VSTATUS);
- switch (state->delsys) {
- case SYS_DVBS:
- case SYS_DSS:
- if (internal->lock) {
- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
-
- stb0899_read_regs(state, STB0899_NIRM, buf, 2);
- val = MAKEWORD16(buf[0], buf[1]);
-
- *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val);
- dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n",
- buf[0], buf[1], val, *snr);
- }
- }
- break;
- case SYS_DVBS2:
- if (internal->lock) {
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1);
- quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg);
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2);
- est = STB0899_GETFIELD(ESN0_EST, reg);
- if (est == 1)
- val = 301; /* C/N = 30.1 dB */
- else if (est == 2)
- val = 270; /* C/N = 27.0 dB */
- else {
- /* quantn = 100 * log(quant^2) */
- quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100);
- /* estn = 100 * log(est) */
- estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est);
- /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */
- val = (quantn - estn) / 10;
- }
- *snr = val;
- dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm",
- quant, quantn, est, estn, val);
- }
- break;
- default:
- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
- u8 reg;
- *status = 0;
-
- switch (state->delsys) {
- case SYS_DVBS:
- case SYS_DSS:
- dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS");
- if (internal->lock) {
- reg = stb0899_read_reg(state, STB0899_VSTATUS);
- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
- dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
-
- reg = stb0899_read_reg(state, STB0899_PLPARM);
- if (STB0899_GETFIELD(VITCURPUN, reg)) {
- dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC");
- *status |= FE_HAS_VITERBI | FE_HAS_SYNC;
- /* post process event */
- stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
- }
- }
- }
- break;
- case SYS_DVBS2:
- dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2");
- if (internal->lock) {
- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2);
- if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) {
- *status |= FE_HAS_CARRIER;
- dprintk(state->verbose, FE_DEBUG, 1,
- "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER");
-
- reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1);
- if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) {
- *status |= FE_HAS_LOCK;
- dprintk(state->verbose, FE_DEBUG, 1,
- "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK");
-
- }
- if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) {
- *status |= FE_HAS_VITERBI;
- dprintk(state->verbose, FE_DEBUG, 1,
- "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI");
- }
- if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) {
- *status |= FE_HAS_SYNC;
- dprintk(state->verbose, FE_DEBUG, 1,
- "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC");
- /* post process event */
- stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1);
- }
- }
- }
- break;
- default:
- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * stb0899_get_error
- * viterbi error for DVB-S/DSS
- * packet error for DVB-S2
- * Bit Error Rate or Packet Error Rate * 10 ^ 7
- */
-static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
-
- u8 lsb, msb;
-
- *ber = 0;
-
- switch (state->delsys) {
- case SYS_DVBS:
- case SYS_DSS:
- if (internal->lock) {
- lsb = stb0899_read_reg(state, STB0899_ECNT1L);
- msb = stb0899_read_reg(state, STB0899_ECNT1M);
- *ber = MAKEWORD16(msb, lsb);
- /* Viterbi Check */
- if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) {
- /* Error Rate */
- *ber *= 9766;
- /* ber = ber * 10 ^ 7 */
- *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
- *ber /= 8;
- }
- }
- break;
- case SYS_DVBS2:
- if (internal->lock) {
- lsb = stb0899_read_reg(state, STB0899_ECNT1L);
- msb = stb0899_read_reg(state, STB0899_ECNT1M);
- *ber = MAKEWORD16(msb, lsb);
- /* ber = ber * 10 ^ 7 */
- *ber *= 10000000;
- *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl))));
- }
- break;
- default:
- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
-{
- struct stb0899_state *state = fe->demodulator_priv;
-
- switch (voltage) {
- case SEC_VOLTAGE_13:
- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00);
- break;
- case SEC_VOLTAGE_18:
- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02);
- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02);
- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
- break;
- case SEC_VOLTAGE_OFF:
- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82);
- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82);
- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
-
- u8 div, reg;
-
- /* wait for diseqc idle */
- if (stb0899_wait_diseqc_txidle(state, 100) < 0)
- return -ETIMEDOUT;
-
- switch (tone) {
- case SEC_TONE_ON:
- div = (internal->master_clk / 100) / 5632;
- div = (div + 5) / 10;
- stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66);
- reg = stb0899_read_reg(state, STB0899_ACRPRESC);
- STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03);
- stb0899_write_reg(state, STB0899_ACRPRESC, reg);
- stb0899_write_reg(state, STB0899_ACRDIV1, div);
- break;
- case SEC_TONE_OFF:
- stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- int i2c_stat;
- struct stb0899_state *state = fe->demodulator_priv;
-
- i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT);
- if (i2c_stat < 0)
- goto err;
-
- if (enable) {
- dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ...");
- i2c_stat |= STB0899_I2CTON;
- if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
- goto err;
- } else {
- dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ...");
- i2c_stat &= ~STB0899_I2CTON;
- if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0)
- goto err;
- }
- return 0;
-err:
- dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed");
- return -EREMOTEIO;
-}
-
-
-static inline void CONVERT32(u32 x, char *str)
-{
- *str++ = (x >> 24) & 0xff;
- *str++ = (x >> 16) & 0xff;
- *str++ = (x >> 8) & 0xff;
- *str++ = (x >> 0) & 0xff;
- *str = '\0';
-}
-
-int stb0899_get_dev_id(struct stb0899_state *state)
-{
- u8 chip_id, release;
- u16 id;
- u32 demod_ver = 0, fec_ver = 0;
- char demod_str[5] = { 0 };
- char fec_str[5] = { 0 };
-
- id = stb0899_read_reg(state, STB0899_DEV_ID);
- dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id);
- chip_id = STB0899_GETFIELD(CHIP_ID, id);
- release = STB0899_GETFIELD(CHIP_REL, id);
-
- dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]",
- chip_id, release);
-
- CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str);
-
- demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID);
- dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver);
- CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str);
- fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG);
- if (! (chip_id > 0)) {
- dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899");
-
- return -ENODEV;
- }
- dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver);
-
- return 0;
-}
-
-static void stb0899_set_delivery(struct stb0899_state *state)
-{
- u8 reg;
- u8 stop_clk[2];
-
- stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1);
- stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2);
-
- switch (state->delsys) {
- case SYS_DVBS:
- dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S");
- /* FECM/Viterbi ON */
- reg = stb0899_read_reg(state, STB0899_FECM);
- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
- stb0899_write_reg(state, STB0899_FECM, reg);
-
- stb0899_write_reg(state, STB0899_RSULC, 0xb1);
- stb0899_write_reg(state, STB0899_TSULC, 0x40);
- stb0899_write_reg(state, STB0899_RSLLC, 0x42);
- stb0899_write_reg(state, STB0899_TSLPL, 0x12);
-
- reg = stb0899_read_reg(state, STB0899_TSTRES);
- STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
- stb0899_write_reg(state, STB0899_TSTRES, reg);
-
- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
-
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
-
- STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
-
- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
- break;
- case SYS_DVBS2:
- /* FECM/Viterbi OFF */
- reg = stb0899_read_reg(state, STB0899_FECM);
- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0);
- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0);
- stb0899_write_reg(state, STB0899_FECM, reg);
-
- stb0899_write_reg(state, STB0899_RSULC, 0xb1);
- stb0899_write_reg(state, STB0899_TSULC, 0x42);
- stb0899_write_reg(state, STB0899_RSLLC, 0x40);
- stb0899_write_reg(state, STB0899_TSLPL, 0x02);
-
- reg = stb0899_read_reg(state, STB0899_TSTRES);
- STB0899_SETFIELD_VAL(FRESLDPC, reg, 0);
- stb0899_write_reg(state, STB0899_TSTRES, reg);
-
- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0);
- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0);
-
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0);
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0);
-
- STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0);
- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
-
- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0);
- break;
- case SYS_DSS:
- /* FECM/Viterbi ON */
- reg = stb0899_read_reg(state, STB0899_FECM);
- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1);
- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1);
- stb0899_write_reg(state, STB0899_FECM, reg);
-
- stb0899_write_reg(state, STB0899_RSULC, 0xa1);
- stb0899_write_reg(state, STB0899_TSULC, 0x61);
- stb0899_write_reg(state, STB0899_RSLLC, 0x42);
-
- reg = stb0899_read_reg(state, STB0899_TSTRES);
- STB0899_SETFIELD_VAL(FRESLDPC, reg, 1);
- stb0899_write_reg(state, STB0899_TSTRES, reg);
-
- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1);
- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1);
-
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1);
- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1);
-
- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0);
-
- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1);
- break;
- default:
- dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
- break;
- }
- STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0);
- stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2);
-}
-
-/*
- * stb0899_set_iterations
- * set the LDPC iteration scale function
- */
-static void stb0899_set_iterations(struct stb0899_state *state)
-{
- struct stb0899_internal *internal = &state->internal;
- struct stb0899_config *config = state->config;
-
- s32 iter_scale;
- u32 reg;
-
- iter_scale = 17 * (internal->master_clk / 1000);
- iter_scale += 410000;
- iter_scale /= (internal->srate / 1000000);
- iter_scale /= 1000;
-
- if (iter_scale > config->ldpc_max_iter)
- iter_scale = config->ldpc_max_iter;
-
- reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER);
- STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale);
- stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg);
-}
-
-static enum dvbfe_search stb0899_search(struct dvb_frontend *fe)
-{
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_params *i_params = &state->params;
- struct stb0899_internal *internal = &state->internal;
- struct stb0899_config *config = state->config;
- struct dtv_frontend_properties *props = &fe->dtv_property_cache;
-
- u32 SearchRange, gain;
-
- i_params->freq = props->frequency;
- i_params->srate = props->symbol_rate;
- state->delsys = props->delivery_system;
- dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys);
-
- SearchRange = 10000000;
- dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate);
- /* checking Search Range is meaningless for a fixed 3 Mhz */
- if (INRANGE(i_params->srate, 1000000, 45000000)) {
- dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE");
- stb0899_set_delivery(state);
-
- if (state->config->tuner_set_rfsiggain) {
- if (internal->srate > 15000000)
- gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */
- else if (internal->srate > 5000000)
- gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */
- else
- gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */
- state->config->tuner_set_rfsiggain(fe, gain);
- }
-
- if (i_params->srate <= 5000000)
- stb0899_set_mclk(state, config->lo_clk);
- else
- stb0899_set_mclk(state, config->hi_clk);
-
- switch (state->delsys) {
- case SYS_DVBS:
- case SYS_DSS:
- dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system");
- internal->freq = i_params->freq;
- internal->srate = i_params->srate;
- /*
- * search = user search range +
- * 500Khz +
- * 2 * Tuner_step_size +
- * 10% of the symbol rate
- */
- internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5);
- internal->derot_percent = 30;
-
- /* What to do for tuners having no bandwidth setup ? */
- /* enable tuner I/O */
- stb0899_i2c_gate_ctrl(&state->frontend, 1);
-
- if (state->config->tuner_set_bandwidth)
- state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10);
- if (state->config->tuner_get_bandwidth)
- state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
-
- /* disable tuner I/O */
- stb0899_i2c_gate_ctrl(&state->frontend, 0);
-
- /* Set DVB-S1 AGC */
- stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11);
-
- /* Run the search algorithm */
- dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo ..");
- if (stb0899_dvbs_algo(state) == RANGEOK) {
- internal->lock = 1;
- dprintk(state->verbose, FE_DEBUG, 1,
- "-------------------------------------> DVB-S LOCK !");
-
-// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */
-// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
-// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
-// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status);
-// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl);
-
- return DVBFE_ALGO_SEARCH_SUCCESS;
- } else {
- internal->lock = 0;
-
- return DVBFE_ALGO_SEARCH_FAILED;
- }
- break;
- case SYS_DVBS2:
- internal->freq = i_params->freq;
- internal->srate = i_params->srate;
- internal->srch_range = SearchRange;
-
- /* enable tuner I/O */
- stb0899_i2c_gate_ctrl(&state->frontend, 1);
-
- if (state->config->tuner_set_bandwidth)
- state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange));
- if (state->config->tuner_get_bandwidth)
- state->config->tuner_get_bandwidth(fe, &internal->tuner_bw);
-
- /* disable tuner I/O */
- stb0899_i2c_gate_ctrl(&state->frontend, 0);
-
-// pParams->SpectralInv = pSearch->IQ_Inversion;
-
- /* Set DVB-S2 AGC */
- stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c);
-
- /* Set IterScale =f(MCLK,SYMB) */
- stb0899_set_iterations(state);
-
- /* Run the search algorithm */
- dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo ..");
- if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) {
- internal->lock = 1;
- dprintk(state->verbose, FE_DEBUG, 1,
- "-------------------------------------> DVB-S2 LOCK !");
-
-// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */
-// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS);
-// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1);
-
- return DVBFE_ALGO_SEARCH_SUCCESS;
- } else {
- internal->lock = 0;
-
- return DVBFE_ALGO_SEARCH_FAILED;
- }
- break;
- default:
- dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system");
- return DVBFE_ALGO_SEARCH_INVALID;
- }
- }
-
- return DVBFE_ALGO_SEARCH_ERROR;
-}
-
-static int stb0899_get_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct stb0899_state *state = fe->demodulator_priv;
- struct stb0899_internal *internal = &state->internal;
-
- dprintk(state->verbose, FE_DEBUG, 1, "Get params");
- p->symbol_rate = internal->srate;
-
- return 0;
-}
-
-static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_CUSTOM;
-}
-
-static struct dvb_frontend_ops stb0899_ops = {
- .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
- .info = {
- .name = "STB0899 Multistandard",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 0,
- .frequency_tolerance = 0,
- .symbol_rate_min = 5000000,
- .symbol_rate_max = 45000000,
-
- .caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_AUTO |
- FE_CAN_2G_MODULATION |
- FE_CAN_QPSK
- },
-
- .release = stb0899_release,
- .init = stb0899_init,
- .sleep = stb0899_sleep,
-// .wakeup = stb0899_wakeup,
-
- .i2c_gate_ctrl = stb0899_i2c_gate_ctrl,
-
- .get_frontend_algo = stb0899_frontend_algo,
- .search = stb0899_search,
- .get_frontend = stb0899_get_frontend,
-
-
- .read_status = stb0899_read_status,
- .read_snr = stb0899_read_snr,
- .read_signal_strength = stb0899_read_signal_strength,
- .read_ber = stb0899_read_ber,
-
- .set_voltage = stb0899_set_voltage,
- .set_tone = stb0899_set_tone,
-
- .diseqc_send_master_cmd = stb0899_send_diseqc_msg,
- .diseqc_recv_slave_reply = stb0899_recv_slave_reply,
- .diseqc_send_burst = stb0899_send_diseqc_burst,
-};
-
-struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c)
-{
- struct stb0899_state *state = NULL;
- enum stb0899_inversion inversion;
-
- state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- inversion = config->inversion;
- state->verbose = &verbose;
- state->config = config;
- state->i2c = i2c;
- state->frontend.ops = stb0899_ops;
- state->frontend.demodulator_priv = state;
- state->internal.inversion = inversion;
-
- stb0899_wakeup(&state->frontend);
- if (stb0899_get_dev_id(state) == -ENODEV) {
- printk("%s: Exiting .. !\n", __func__);
- goto error;
- }
-
- printk("%s: Attaching STB0899 \n", __func__);
- return &state->frontend;
-
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(stb0899_attach);
-MODULE_PARM_DESC(verbose, "Set Verbosity level");
-MODULE_AUTHOR("Manu Abraham");
-MODULE_DESCRIPTION("STB0899 Multi-Std frontend");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
deleted file mode 100644
index ea86a5603e57..000000000000
--- a/drivers/media/dvb/frontends/stv090x.c
+++ /dev/null
@@ -1,4823 +0,0 @@
-/*
- STV0900/0903 Multistandard Broadcast Frontend driver
- Copyright (C) Manu Abraham <abraham.manu@gmail.com>
-
- Copyright (C) ST Microelectronics
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-#include <linux/dvb/frontend.h>
-#include "dvb_frontend.h"
-
-#include "stv6110x.h" /* for demodulator internal modes */
-
-#include "stv090x_reg.h"
-#include "stv090x.h"
-#include "stv090x_priv.h"
-
-static unsigned int verbose;
-module_param(verbose, int, 0644);
-
-/* internal params node */
-struct stv090x_dev {
- /* pointer for internal params, one for each pair of demods */
- struct stv090x_internal *internal;
- struct stv090x_dev *next_dev;
-};
-
-/* first internal params */
-static struct stv090x_dev *stv090x_first_dev;
-
-/* find chip by i2c adapter and i2c address */
-static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap,
- u8 i2c_addr)
-{
- struct stv090x_dev *temp_dev = stv090x_first_dev;
-
- /*
- Search of the last stv0900 chip or
- find it by i2c adapter and i2c address */
- while ((temp_dev != NULL) &&
- ((temp_dev->internal->i2c_adap != i2c_adap) ||
- (temp_dev->internal->i2c_addr != i2c_addr))) {
-
- temp_dev = temp_dev->next_dev;
- }
-
- return temp_dev;
-}
-
-/* deallocating chip */
-static void remove_dev(struct stv090x_internal *internal)
-{
- struct stv090x_dev *prev_dev = stv090x_first_dev;
- struct stv090x_dev *del_dev = find_dev(internal->i2c_adap,
- internal->i2c_addr);
-
- if (del_dev != NULL) {
- if (del_dev == stv090x_first_dev) {
- stv090x_first_dev = del_dev->next_dev;
- } else {
- while (prev_dev->next_dev != del_dev)
- prev_dev = prev_dev->next_dev;
-
- prev_dev->next_dev = del_dev->next_dev;
- }
-
- kfree(del_dev);
- }
-}
-
-/* allocating new chip */
-static struct stv090x_dev *append_internal(struct stv090x_internal *internal)
-{
- struct stv090x_dev *new_dev;
- struct stv090x_dev *temp_dev;
-
- new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL);
- if (new_dev != NULL) {
- new_dev->internal = internal;
- new_dev->next_dev = NULL;
-
- /* append to list */
- if (stv090x_first_dev == NULL) {
- stv090x_first_dev = new_dev;
- } else {
- temp_dev = stv090x_first_dev;
- while (temp_dev->next_dev != NULL)
- temp_dev = temp_dev->next_dev;
-
- temp_dev->next_dev = new_dev;
- }
- }
-
- return new_dev;
-}
-
-
-/* DVBS1 and DSS C/N Lookup table */
-static const struct stv090x_tab stv090x_s1cn_tab[] = {
- { 0, 8917 }, /* 0.0dB */
- { 5, 8801 }, /* 0.5dB */
- { 10, 8667 }, /* 1.0dB */
- { 15, 8522 }, /* 1.5dB */
- { 20, 8355 }, /* 2.0dB */
- { 25, 8175 }, /* 2.5dB */
- { 30, 7979 }, /* 3.0dB */
- { 35, 7763 }, /* 3.5dB */
- { 40, 7530 }, /* 4.0dB */
- { 45, 7282 }, /* 4.5dB */
- { 50, 7026 }, /* 5.0dB */
- { 55, 6781 }, /* 5.5dB */
- { 60, 6514 }, /* 6.0dB */
- { 65, 6241 }, /* 6.5dB */
- { 70, 5965 }, /* 7.0dB */
- { 75, 5690 }, /* 7.5dB */
- { 80, 5424 }, /* 8.0dB */
- { 85, 5161 }, /* 8.5dB */
- { 90, 4902 }, /* 9.0dB */
- { 95, 4654 }, /* 9.5dB */
- { 100, 4417 }, /* 10.0dB */
- { 105, 4186 }, /* 10.5dB */
- { 110, 3968 }, /* 11.0dB */
- { 115, 3757 }, /* 11.5dB */
- { 120, 3558 }, /* 12.0dB */
- { 125, 3366 }, /* 12.5dB */
- { 130, 3185 }, /* 13.0dB */
- { 135, 3012 }, /* 13.5dB */
- { 140, 2850 }, /* 14.0dB */
- { 145, 2698 }, /* 14.5dB */
- { 150, 2550 }, /* 15.0dB */
- { 160, 2283 }, /* 16.0dB */
- { 170, 2042 }, /* 17.0dB */
- { 180, 1827 }, /* 18.0dB */
- { 190, 1636 }, /* 19.0dB */
- { 200, 1466 }, /* 20.0dB */
- { 210, 1315 }, /* 21.0dB */
- { 220, 1181 }, /* 22.0dB */
- { 230, 1064 }, /* 23.0dB */
- { 240, 960 }, /* 24.0dB */
- { 250, 869 }, /* 25.0dB */
- { 260, 792 }, /* 26.0dB */
- { 270, 724 }, /* 27.0dB */
- { 280, 665 }, /* 28.0dB */
- { 290, 616 }, /* 29.0dB */
- { 300, 573 }, /* 30.0dB */
- { 310, 537 }, /* 31.0dB */
- { 320, 507 }, /* 32.0dB */
- { 330, 483 }, /* 33.0dB */
- { 400, 398 }, /* 40.0dB */
- { 450, 381 }, /* 45.0dB */
- { 500, 377 } /* 50.0dB */
-};
-
-/* DVBS2 C/N Lookup table */
-static const struct stv090x_tab stv090x_s2cn_tab[] = {
- { -30, 13348 }, /* -3.0dB */
- { -20, 12640 }, /* -2d.0B */
- { -10, 11883 }, /* -1.0dB */
- { 0, 11101 }, /* -0.0dB */
- { 5, 10718 }, /* 0.5dB */
- { 10, 10339 }, /* 1.0dB */
- { 15, 9947 }, /* 1.5dB */
- { 20, 9552 }, /* 2.0dB */
- { 25, 9183 }, /* 2.5dB */
- { 30, 8799 }, /* 3.0dB */
- { 35, 8422 }, /* 3.5dB */
- { 40, 8062 }, /* 4.0dB */
- { 45, 7707 }, /* 4.5dB */
- { 50, 7353 }, /* 5.0dB */
- { 55, 7025 }, /* 5.5dB */
- { 60, 6684 }, /* 6.0dB */
- { 65, 6331 }, /* 6.5dB */
- { 70, 6036 }, /* 7.0dB */
- { 75, 5727 }, /* 7.5dB */
- { 80, 5437 }, /* 8.0dB */
- { 85, 5164 }, /* 8.5dB */
- { 90, 4902 }, /* 9.0dB */
- { 95, 4653 }, /* 9.5dB */
- { 100, 4408 }, /* 10.0dB */
- { 105, 4187 }, /* 10.5dB */
- { 110, 3961 }, /* 11.0dB */
- { 115, 3751 }, /* 11.5dB */
- { 120, 3558 }, /* 12.0dB */
- { 125, 3368 }, /* 12.5dB */
- { 130, 3191 }, /* 13.0dB */
- { 135, 3017 }, /* 13.5dB */
- { 140, 2862 }, /* 14.0dB */
- { 145, 2710 }, /* 14.5dB */
- { 150, 2565 }, /* 15.0dB */
- { 160, 2300 }, /* 16.0dB */
- { 170, 2058 }, /* 17.0dB */
- { 180, 1849 }, /* 18.0dB */
- { 190, 1663 }, /* 19.0dB */
- { 200, 1495 }, /* 20.0dB */
- { 210, 1349 }, /* 21.0dB */
- { 220, 1222 }, /* 22.0dB */
- { 230, 1110 }, /* 23.0dB */
- { 240, 1011 }, /* 24.0dB */
- { 250, 925 }, /* 25.0dB */
- { 260, 853 }, /* 26.0dB */
- { 270, 789 }, /* 27.0dB */
- { 280, 734 }, /* 28.0dB */
- { 290, 690 }, /* 29.0dB */
- { 300, 650 }, /* 30.0dB */
- { 310, 619 }, /* 31.0dB */
- { 320, 593 }, /* 32.0dB */
- { 330, 571 }, /* 33.0dB */
- { 400, 498 }, /* 40.0dB */
- { 450, 484 }, /* 45.0dB */
- { 500, 481 } /* 50.0dB */
-};
-
-/* RF level C/N lookup table */
-static const struct stv090x_tab stv090x_rf_tab[] = {
- { -5, 0xcaa1 }, /* -5dBm */
- { -10, 0xc229 }, /* -10dBm */
- { -15, 0xbb08 }, /* -15dBm */
- { -20, 0xb4bc }, /* -20dBm */
- { -25, 0xad5a }, /* -25dBm */
- { -30, 0xa298 }, /* -30dBm */
- { -35, 0x98a8 }, /* -35dBm */
- { -40, 0x8389 }, /* -40dBm */
- { -45, 0x59be }, /* -45dBm */
- { -50, 0x3a14 }, /* -50dBm */
- { -55, 0x2d11 }, /* -55dBm */
- { -60, 0x210d }, /* -60dBm */
- { -65, 0xa14f }, /* -65dBm */
- { -70, 0x07aa } /* -70dBm */
-};
-
-
-static struct stv090x_reg stv0900_initval[] = {
-
- { STV090x_OUTCFG, 0x00 },
- { STV090x_MODECFG, 0xff },
- { STV090x_AGCRF1CFG, 0x11 },
- { STV090x_AGCRF2CFG, 0x13 },
- { STV090x_TSGENERAL1X, 0x14 },
- { STV090x_TSTTNR2, 0x21 },
- { STV090x_TSTTNR4, 0x21 },
- { STV090x_P2_DISTXCTL, 0x22 },
- { STV090x_P2_F22TX, 0xc0 },
- { STV090x_P2_F22RX, 0xc0 },
- { STV090x_P2_DISRXCTL, 0x00 },
- { STV090x_P2_DMDCFGMD, 0xF9 },
- { STV090x_P2_DEMOD, 0x08 },
- { STV090x_P2_DMDCFG3, 0xc4 },
- { STV090x_P2_CARFREQ, 0xed },
- { STV090x_P2_LDT, 0xd0 },
- { STV090x_P2_LDT2, 0xb8 },
- { STV090x_P2_TMGCFG, 0xd2 },
- { STV090x_P2_TMGTHRISE, 0x20 },
- { STV090x_P1_TMGCFG, 0xd2 },
-
- { STV090x_P2_TMGTHFALL, 0x00 },
- { STV090x_P2_FECSPY, 0x88 },
- { STV090x_P2_FSPYDATA, 0x3a },
- { STV090x_P2_FBERCPT4, 0x00 },
- { STV090x_P2_FSPYBER, 0x10 },
- { STV090x_P2_ERRCTRL1, 0x35 },
- { STV090x_P2_ERRCTRL2, 0xc1 },
- { STV090x_P2_CFRICFG, 0xf8 },
- { STV090x_P2_NOSCFG, 0x1c },
- { STV090x_P2_DMDTOM, 0x20 },
- { STV090x_P2_CORRELMANT, 0x70 },
- { STV090x_P2_CORRELABS, 0x88 },
- { STV090x_P2_AGC2O, 0x5b },
- { STV090x_P2_AGC2REF, 0x38 },
- { STV090x_P2_CARCFG, 0xe4 },
- { STV090x_P2_ACLC, 0x1A },
- { STV090x_P2_BCLC, 0x09 },
- { STV090x_P2_CARHDR, 0x08 },
- { STV090x_P2_KREFTMG, 0xc1 },
- { STV090x_P2_SFRUPRATIO, 0xf0 },
- { STV090x_P2_SFRLOWRATIO, 0x70 },
- { STV090x_P2_SFRSTEP, 0x58 },
- { STV090x_P2_TMGCFG2, 0x01 },
- { STV090x_P2_CAR2CFG, 0x26 },
- { STV090x_P2_BCLC2S2Q, 0x86 },
- { STV090x_P2_BCLC2S28, 0x86 },
- { STV090x_P2_SMAPCOEF7, 0x77 },
- { STV090x_P2_SMAPCOEF6, 0x85 },
- { STV090x_P2_SMAPCOEF5, 0x77 },
- { STV090x_P2_TSCFGL, 0x20 },
- { STV090x_P2_DMDCFG2, 0x3b },
- { STV090x_P2_MODCODLST0, 0xff },
- { STV090x_P2_MODCODLST1, 0xff },
- { STV090x_P2_MODCODLST2, 0xff },
- { STV090x_P2_MODCODLST3, 0xff },
- { STV090x_P2_MODCODLST4, 0xff },
- { STV090x_P2_MODCODLST5, 0xff },
- { STV090x_P2_MODCODLST6, 0xff },
- { STV090x_P2_MODCODLST7, 0xcc },
- { STV090x_P2_MODCODLST8, 0xcc },
- { STV090x_P2_MODCODLST9, 0xcc },
- { STV090x_P2_MODCODLSTA, 0xcc },
- { STV090x_P2_MODCODLSTB, 0xcc },
- { STV090x_P2_MODCODLSTC, 0xcc },
- { STV090x_P2_MODCODLSTD, 0xcc },
- { STV090x_P2_MODCODLSTE, 0xcc },
- { STV090x_P2_MODCODLSTF, 0xcf },
- { STV090x_P1_DISTXCTL, 0x22 },
- { STV090x_P1_F22TX, 0xc0 },
- { STV090x_P1_F22RX, 0xc0 },
- { STV090x_P1_DISRXCTL, 0x00 },
- { STV090x_P1_DMDCFGMD, 0xf9 },
- { STV090x_P1_DEMOD, 0x08 },
- { STV090x_P1_DMDCFG3, 0xc4 },
- { STV090x_P1_DMDTOM, 0x20 },
- { STV090x_P1_CARFREQ, 0xed },
- { STV090x_P1_LDT, 0xd0 },
- { STV090x_P1_LDT2, 0xb8 },
- { STV090x_P1_TMGCFG, 0xd2 },
- { STV090x_P1_TMGTHRISE, 0x20 },
- { STV090x_P1_TMGTHFALL, 0x00 },
- { STV090x_P1_SFRUPRATIO, 0xf0 },
- { STV090x_P1_SFRLOWRATIO, 0x70 },
- { STV090x_P1_TSCFGL, 0x20 },
- { STV090x_P1_FECSPY, 0x88 },
- { STV090x_P1_FSPYDATA, 0x3a },
- { STV090x_P1_FBERCPT4, 0x00 },
- { STV090x_P1_FSPYBER, 0x10 },
- { STV090x_P1_ERRCTRL1, 0x35 },
- { STV090x_P1_ERRCTRL2, 0xc1 },
- { STV090x_P1_CFRICFG, 0xf8 },
- { STV090x_P1_NOSCFG, 0x1c },
- { STV090x_P1_CORRELMANT, 0x70 },
- { STV090x_P1_CORRELABS, 0x88 },
- { STV090x_P1_AGC2O, 0x5b },
- { STV090x_P1_AGC2REF, 0x38 },
- { STV090x_P1_CARCFG, 0xe4 },
- { STV090x_P1_ACLC, 0x1A },
- { STV090x_P1_BCLC, 0x09 },
- { STV090x_P1_CARHDR, 0x08 },
- { STV090x_P1_KREFTMG, 0xc1 },
- { STV090x_P1_SFRSTEP, 0x58 },
- { STV090x_P1_TMGCFG2, 0x01 },
- { STV090x_P1_CAR2CFG, 0x26 },
- { STV090x_P1_BCLC2S2Q, 0x86 },
- { STV090x_P1_BCLC2S28, 0x86 },
- { STV090x_P1_SMAPCOEF7, 0x77 },
- { STV090x_P1_SMAPCOEF6, 0x85 },
- { STV090x_P1_SMAPCOEF5, 0x77 },
- { STV090x_P1_DMDCFG2, 0x3b },
- { STV090x_P1_MODCODLST0, 0xff },
- { STV090x_P1_MODCODLST1, 0xff },
- { STV090x_P1_MODCODLST2, 0xff },
- { STV090x_P1_MODCODLST3, 0xff },
- { STV090x_P1_MODCODLST4, 0xff },
- { STV090x_P1_MODCODLST5, 0xff },
- { STV090x_P1_MODCODLST6, 0xff },
- { STV090x_P1_MODCODLST7, 0xcc },
- { STV090x_P1_MODCODLST8, 0xcc },
- { STV090x_P1_MODCODLST9, 0xcc },
- { STV090x_P1_MODCODLSTA, 0xcc },
- { STV090x_P1_MODCODLSTB, 0xcc },
- { STV090x_P1_MODCODLSTC, 0xcc },
- { STV090x_P1_MODCODLSTD, 0xcc },
- { STV090x_P1_MODCODLSTE, 0xcc },
- { STV090x_P1_MODCODLSTF, 0xcf },
- { STV090x_GENCFG, 0x1d },
- { STV090x_NBITER_NF4, 0x37 },
- { STV090x_NBITER_NF5, 0x29 },
- { STV090x_NBITER_NF6, 0x37 },
- { STV090x_NBITER_NF7, 0x33 },
- { STV090x_NBITER_NF8, 0x31 },
- { STV090x_NBITER_NF9, 0x2f },
- { STV090x_NBITER_NF10, 0x39 },
- { STV090x_NBITER_NF11, 0x3a },
- { STV090x_NBITER_NF12, 0x29 },
- { STV090x_NBITER_NF13, 0x37 },
- { STV090x_NBITER_NF14, 0x33 },
- { STV090x_NBITER_NF15, 0x2f },
- { STV090x_NBITER_NF16, 0x39 },
- { STV090x_NBITER_NF17, 0x3a },
- { STV090x_NBITERNOERR, 0x04 },
- { STV090x_GAINLLR_NF4, 0x0C },
- { STV090x_GAINLLR_NF5, 0x0F },
- { STV090x_GAINLLR_NF6, 0x11 },
- { STV090x_GAINLLR_NF7, 0x14 },
- { STV090x_GAINLLR_NF8, 0x17 },
- { STV090x_GAINLLR_NF9, 0x19 },
- { STV090x_GAINLLR_NF10, 0x20 },
- { STV090x_GAINLLR_NF11, 0x21 },
- { STV090x_GAINLLR_NF12, 0x0D },
- { STV090x_GAINLLR_NF13, 0x0F },
- { STV090x_GAINLLR_NF14, 0x13 },
- { STV090x_GAINLLR_NF15, 0x1A },
- { STV090x_GAINLLR_NF16, 0x1F },
- { STV090x_GAINLLR_NF17, 0x21 },
- { STV090x_RCCFGH, 0x20 },
- { STV090x_P1_FECM, 0x01 }, /* disable DSS modes */
- { STV090x_P2_FECM, 0x01 }, /* disable DSS modes */
- { STV090x_P1_PRVIT, 0x2F }, /* disable PR 6/7 */
- { STV090x_P2_PRVIT, 0x2F }, /* disable PR 6/7 */
-};
-
-static struct stv090x_reg stv0903_initval[] = {
- { STV090x_OUTCFG, 0x00 },
- { STV090x_AGCRF1CFG, 0x11 },
- { STV090x_STOPCLK1, 0x48 },
- { STV090x_STOPCLK2, 0x14 },
- { STV090x_TSTTNR1, 0x27 },
- { STV090x_TSTTNR2, 0x21 },
- { STV090x_P1_DISTXCTL, 0x22 },
- { STV090x_P1_F22TX, 0xc0 },
- { STV090x_P1_F22RX, 0xc0 },
- { STV090x_P1_DISRXCTL, 0x00 },
- { STV090x_P1_DMDCFGMD, 0xF9 },
- { STV090x_P1_DEMOD, 0x08 },
- { STV090x_P1_DMDCFG3, 0xc4 },
- { STV090x_P1_CARFREQ, 0xed },
- { STV090x_P1_TNRCFG2, 0x82 },
- { STV090x_P1_LDT, 0xd0 },
- { STV090x_P1_LDT2, 0xb8 },
- { STV090x_P1_TMGCFG, 0xd2 },
- { STV090x_P1_TMGTHRISE, 0x20 },
- { STV090x_P1_TMGTHFALL, 0x00 },
- { STV090x_P1_SFRUPRATIO, 0xf0 },
- { STV090x_P1_SFRLOWRATIO, 0x70 },
- { STV090x_P1_TSCFGL, 0x20 },
- { STV090x_P1_FECSPY, 0x88 },
- { STV090x_P1_FSPYDATA, 0x3a },
- { STV090x_P1_FBERCPT4, 0x00 },
- { STV090x_P1_FSPYBER, 0x10 },
- { STV090x_P1_ERRCTRL1, 0x35 },
- { STV090x_P1_ERRCTRL2, 0xc1 },
- { STV090x_P1_CFRICFG, 0xf8 },
- { STV090x_P1_NOSCFG, 0x1c },
- { STV090x_P1_DMDTOM, 0x20 },
- { STV090x_P1_CORRELMANT, 0x70 },
- { STV090x_P1_CORRELABS, 0x88 },
- { STV090x_P1_AGC2O, 0x5b },
- { STV090x_P1_AGC2REF, 0x38 },
- { STV090x_P1_CARCFG, 0xe4 },
- { STV090x_P1_ACLC, 0x1A },
- { STV090x_P1_BCLC, 0x09 },
- { STV090x_P1_CARHDR, 0x08 },
- { STV090x_P1_KREFTMG, 0xc1 },
- { STV090x_P1_SFRSTEP, 0x58 },
- { STV090x_P1_TMGCFG2, 0x01 },
- { STV090x_P1_CAR2CFG, 0x26 },
- { STV090x_P1_BCLC2S2Q, 0x86 },
- { STV090x_P1_BCLC2S28, 0x86 },
- { STV090x_P1_SMAPCOEF7, 0x77 },
- { STV090x_P1_SMAPCOEF6, 0x85 },
- { STV090x_P1_SMAPCOEF5, 0x77 },
- { STV090x_P1_DMDCFG2, 0x3b },
- { STV090x_P1_MODCODLST0, 0xff },
- { STV090x_P1_MODCODLST1, 0xff },
- { STV090x_P1_MODCODLST2, 0xff },
- { STV090x_P1_MODCODLST3, 0xff },
- { STV090x_P1_MODCODLST4, 0xff },
- { STV090x_P1_MODCODLST5, 0xff },
- { STV090x_P1_MODCODLST6, 0xff },
- { STV090x_P1_MODCODLST7, 0xcc },
- { STV090x_P1_MODCODLST8, 0xcc },
- { STV090x_P1_MODCODLST9, 0xcc },
- { STV090x_P1_MODCODLSTA, 0xcc },
- { STV090x_P1_MODCODLSTB, 0xcc },
- { STV090x_P1_MODCODLSTC, 0xcc },
- { STV090x_P1_MODCODLSTD, 0xcc },
- { STV090x_P1_MODCODLSTE, 0xcc },
- { STV090x_P1_MODCODLSTF, 0xcf },
- { STV090x_GENCFG, 0x1c },
- { STV090x_NBITER_NF4, 0x37 },
- { STV090x_NBITER_NF5, 0x29 },
- { STV090x_NBITER_NF6, 0x37 },
- { STV090x_NBITER_NF7, 0x33 },
- { STV090x_NBITER_NF8, 0x31 },
- { STV090x_NBITER_NF9, 0x2f },
- { STV090x_NBITER_NF10, 0x39 },
- { STV090x_NBITER_NF11, 0x3a },
- { STV090x_NBITER_NF12, 0x29 },
- { STV090x_NBITER_NF13, 0x37 },
- { STV090x_NBITER_NF14, 0x33 },
- { STV090x_NBITER_NF15, 0x2f },
- { STV090x_NBITER_NF16, 0x39 },
- { STV090x_NBITER_NF17, 0x3a },
- { STV090x_NBITERNOERR, 0x04 },
- { STV090x_GAINLLR_NF4, 0x0C },
- { STV090x_GAINLLR_NF5, 0x0F },
- { STV090x_GAINLLR_NF6, 0x11 },
- { STV090x_GAINLLR_NF7, 0x14 },
- { STV090x_GAINLLR_NF8, 0x17 },
- { STV090x_GAINLLR_NF9, 0x19 },
- { STV090x_GAINLLR_NF10, 0x20 },
- { STV090x_GAINLLR_NF11, 0x21 },
- { STV090x_GAINLLR_NF12, 0x0D },
- { STV090x_GAINLLR_NF13, 0x0F },
- { STV090x_GAINLLR_NF14, 0x13 },
- { STV090x_GAINLLR_NF15, 0x1A },
- { STV090x_GAINLLR_NF16, 0x1F },
- { STV090x_GAINLLR_NF17, 0x21 },
- { STV090x_RCCFGH, 0x20 },
- { STV090x_P1_FECM, 0x01 }, /*disable the DSS mode */
- { STV090x_P1_PRVIT, 0x2f } /*disable puncture rate 6/7*/
-};
-
-static struct stv090x_reg stv0900_cut20_val[] = {
-
- { STV090x_P2_DMDCFG3, 0xe8 },
- { STV090x_P2_DMDCFG4, 0x10 },
- { STV090x_P2_CARFREQ, 0x38 },
- { STV090x_P2_CARHDR, 0x20 },
- { STV090x_P2_KREFTMG, 0x5a },
- { STV090x_P2_SMAPCOEF7, 0x06 },
- { STV090x_P2_SMAPCOEF6, 0x00 },
- { STV090x_P2_SMAPCOEF5, 0x04 },
- { STV090x_P2_NOSCFG, 0x0c },
- { STV090x_P1_DMDCFG3, 0xe8 },
- { STV090x_P1_DMDCFG4, 0x10 },
- { STV090x_P1_CARFREQ, 0x38 },
- { STV090x_P1_CARHDR, 0x20 },
- { STV090x_P1_KREFTMG, 0x5a },
- { STV090x_P1_SMAPCOEF7, 0x06 },
- { STV090x_P1_SMAPCOEF6, 0x00 },
- { STV090x_P1_SMAPCOEF5, 0x04 },
- { STV090x_P1_NOSCFG, 0x0c },
- { STV090x_GAINLLR_NF4, 0x21 },
- { STV090x_GAINLLR_NF5, 0x21 },
- { STV090x_GAINLLR_NF6, 0x20 },
- { STV090x_GAINLLR_NF7, 0x1F },
- { STV090x_GAINLLR_NF8, 0x1E },
- { STV090x_GAINLLR_NF9, 0x1E },
- { STV090x_GAINLLR_NF10, 0x1D },
- { STV090x_GAINLLR_NF11, 0x1B },
- { STV090x_GAINLLR_NF12, 0x20 },
- { STV090x_GAINLLR_NF13, 0x20 },
- { STV090x_GAINLLR_NF14, 0x20 },
- { STV090x_GAINLLR_NF15, 0x20 },
- { STV090x_GAINLLR_NF16, 0x20 },
- { STV090x_GAINLLR_NF17, 0x21 },
-};
-
-static struct stv090x_reg stv0903_cut20_val[] = {
- { STV090x_P1_DMDCFG3, 0xe8 },
- { STV090x_P1_DMDCFG4, 0x10 },
- { STV090x_P1_CARFREQ, 0x38 },
- { STV090x_P1_CARHDR, 0x20 },
- { STV090x_P1_KREFTMG, 0x5a },
- { STV090x_P1_SMAPCOEF7, 0x06 },
- { STV090x_P1_SMAPCOEF6, 0x00 },
- { STV090x_P1_SMAPCOEF5, 0x04 },
- { STV090x_P1_NOSCFG, 0x0c },
- { STV090x_GAINLLR_NF4, 0x21 },
- { STV090x_GAINLLR_NF5, 0x21 },
- { STV090x_GAINLLR_NF6, 0x20 },
- { STV090x_GAINLLR_NF7, 0x1F },
- { STV090x_GAINLLR_NF8, 0x1E },
- { STV090x_GAINLLR_NF9, 0x1E },
- { STV090x_GAINLLR_NF10, 0x1D },
- { STV090x_GAINLLR_NF11, 0x1B },
- { STV090x_GAINLLR_NF12, 0x20 },
- { STV090x_GAINLLR_NF13, 0x20 },
- { STV090x_GAINLLR_NF14, 0x20 },
- { STV090x_GAINLLR_NF15, 0x20 },
- { STV090x_GAINLLR_NF16, 0x20 },
- { STV090x_GAINLLR_NF17, 0x21 }
-};
-
-/* Cut 2.0 Long Frame Tracking CR loop */
-static struct stv090x_long_frame_crloop stv090x_s2_crl_cut20[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_QPSK_12, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x1e },
- { STV090x_QPSK_35, 0x2f, 0x3f, 0x2e, 0x2f, 0x3d, 0x0f, 0x0e, 0x2e, 0x3d, 0x0e },
- { STV090x_QPSK_23, 0x2f, 0x3f, 0x2e, 0x2f, 0x0e, 0x0f, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_QPSK_34, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_QPSK_45, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_QPSK_56, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_QPSK_89, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_QPSK_910, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d },
- { STV090x_8PSK_35, 0x3c, 0x3e, 0x1c, 0x2e, 0x0c, 0x1e, 0x2b, 0x2d, 0x1b, 0x1d },
- { STV090x_8PSK_23, 0x1d, 0x3e, 0x3c, 0x2e, 0x2c, 0x1e, 0x0c, 0x2d, 0x2b, 0x1d },
- { STV090x_8PSK_34, 0x0e, 0x3e, 0x3d, 0x2e, 0x0d, 0x1e, 0x2c, 0x2d, 0x0c, 0x1d },
- { STV090x_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d },
- { STV090x_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d },
- { STV090x_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }
-};
-
-/* Cut 3.0 Long Frame Tracking CR loop */
-static struct stv090x_long_frame_crloop stv090x_s2_crl_cut30[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_QPSK_12, 0x3c, 0x2c, 0x0c, 0x2c, 0x1b, 0x2c, 0x1b, 0x1c, 0x0b, 0x3b },
- { STV090x_QPSK_35, 0x0d, 0x0d, 0x0c, 0x0d, 0x1b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
- { STV090x_QPSK_23, 0x1d, 0x0d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
- { STV090x_QPSK_34, 0x1d, 0x1d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b },
- { STV090x_QPSK_45, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
- { STV090x_QPSK_56, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
- { STV090x_QPSK_89, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
- { STV090x_QPSK_910, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b },
- { STV090x_8PSK_35, 0x39, 0x29, 0x39, 0x19, 0x19, 0x19, 0x19, 0x19, 0x09, 0x19 },
- { STV090x_8PSK_23, 0x2a, 0x39, 0x1a, 0x0a, 0x39, 0x0a, 0x29, 0x39, 0x29, 0x0a },
- { STV090x_8PSK_34, 0x2b, 0x3a, 0x1b, 0x1b, 0x3a, 0x1b, 0x1a, 0x0b, 0x1a, 0x3a },
- { STV090x_8PSK_56, 0x0c, 0x1b, 0x3b, 0x3b, 0x1b, 0x3b, 0x3a, 0x3b, 0x3a, 0x1b },
- { STV090x_8PSK_89, 0x0d, 0x3c, 0x2c, 0x2c, 0x2b, 0x0c, 0x0b, 0x3b, 0x0b, 0x1b },
- { STV090x_8PSK_910, 0x0d, 0x0d, 0x2c, 0x3c, 0x3b, 0x1c, 0x0b, 0x3b, 0x0b, 0x1b }
-};
-
-/* Cut 2.0 Long Frame Tracking CR Loop */
-static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut20[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_16APSK_23, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x0c },
- { STV090x_16APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0c, 0x2d, 0x0c, 0x1d, 0x0c },
- { STV090x_16APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c },
- { STV090x_16APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c },
- { STV090x_16APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c },
- { STV090x_16APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c },
- { STV090x_32APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
- { STV090x_32APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
- { STV090x_32APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
- { STV090x_32APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
- { STV090x_32APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }
-};
-
-/* Cut 3.0 Long Frame Tracking CR Loop */
-static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut30[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_16APSK_23, 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x0a, 0x3a, 0x0a, 0x2a, 0x0a },
- { STV090x_16APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0a, 0x3b, 0x0a, 0x1b, 0x0a },
- { STV090x_16APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a },
- { STV090x_16APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a },
- { STV090x_16APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a },
- { STV090x_16APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a },
- { STV090x_32APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
- { STV090x_32APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
- { STV090x_32APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
- { STV090x_32APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a },
- { STV090x_32APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }
-};
-
-static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut20[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_QPSK_14, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x2d, 0x1f, 0x3d, 0x3e },
- { STV090x_QPSK_13, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x3d, 0x0f, 0x3d, 0x2e },
- { STV090x_QPSK_25, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x2e }
-};
-
-static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut30[] = {
- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
- { STV090x_QPSK_14, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x2a, 0x1c, 0x3a, 0x3b },
- { STV090x_QPSK_13, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x3a, 0x0c, 0x3a, 0x2b },
- { STV090x_QPSK_25, 0x1c, 0x3c, 0x1b, 0x3c, 0x3a, 0x1c, 0x3a, 0x3b, 0x3a, 0x2b }
-};
-
-/* Cut 2.0 Short Frame Tracking CR Loop */
-static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut20[] = {
- /* MODCOD 2M 5M 10M 20M 30M */
- { STV090x_QPSK, 0x2f, 0x2e, 0x0e, 0x0e, 0x3d },
- { STV090x_8PSK, 0x3e, 0x0e, 0x2d, 0x0d, 0x3c },
- { STV090x_16APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d },
- { STV090x_32APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d }
-};
-
-/* Cut 3.0 Short Frame Tracking CR Loop */
-static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut30[] = {
- /* MODCOD 2M 5M 10M 20M 30M */
- { STV090x_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A },
- { STV090x_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 },
- { STV090x_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A },
- { STV090x_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }
-};
-
-static inline s32 comp2(s32 __x, s32 __width)
-{
- if (__width == 32)
- return __x;
- else
- return (__x >= (1 << (__width - 1))) ? (__x - (1 << __width)) : __x;
-}
-
-static int stv090x_read_reg(struct stv090x_state *state, unsigned int reg)
-{
- const struct stv090x_config *config = state->config;
- int ret;
-
- u8 b0[] = { reg >> 8, reg & 0xff };
- u8 buf;
-
- struct i2c_msg msg[] = {
- { .addr = config->address, .flags = 0, .buf = b0, .len = 2 },
- { .addr = config->address, .flags = I2C_M_RD, .buf = &buf, .len = 1 }
- };
-
- ret = i2c_transfer(state->i2c, msg, 2);
- if (ret != 2) {
- if (ret != -ERESTARTSYS)
- dprintk(FE_ERROR, 1,
- "Read error, Reg=[0x%02x], Status=%d",
- reg, ret);
-
- return ret < 0 ? ret : -EREMOTEIO;
- }
- if (unlikely(*state->verbose >= FE_DEBUGREG))
- dprintk(FE_ERROR, 1, "Reg=[0x%02x], data=%02x",
- reg, buf);
-
- return (unsigned int) buf;
-}
-
-static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8 *data, u32 count)
-{
- const struct stv090x_config *config = state->config;
- int ret;
- u8 buf[2 + count];
- struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count };
-
- buf[0] = reg >> 8;
- buf[1] = reg & 0xff;
- memcpy(&buf[2], data, count);
-
- if (unlikely(*state->verbose >= FE_DEBUGREG)) {
- int i;
-
- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg);
- for (i = 0; i < count; i++)
- printk(" %02x", data[i]);
- printk("\n");
- }
-
- ret = i2c_transfer(state->i2c, &i2c_msg, 1);
- if (ret != 1) {
- if (ret != -ERESTARTSYS)
- dprintk(FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d",
- reg, data[0], count, ret);
- return ret < 0 ? ret : -EREMOTEIO;
- }
-
- return 0;
-}
-
-static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 data)
-{
- return stv090x_write_regs(state, reg, &data, 1);
-}
-
-static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable)
-{
- u32 reg;
-
- /*
- * NOTE! A lock is used as a FSM to control the state in which
- * access is serialized between two tuners on the same demod.
- * This has nothing to do with a lock to protect a critical section
- * which may in some other cases be confused with protecting I/O
- * access to the demodulator gate.
- * In case of any error, the lock is unlocked and exit within the
- * relevant operations themselves.
- */
- 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) {
- dprintk(FE_DEBUG, 1, "Enable Gate");
- STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0)
- goto err;
-
- } else {
- dprintk(FE_DEBUG, 1, "Disable Gate");
- STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 0);
- if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0)
- goto err;
- }
-
- 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");
- if (state->config->tuner_i2c_lock)
- state->config->tuner_i2c_lock(&state->frontend, 0);
- else
- mutex_unlock(&state->internal->tuner_lock);
- return -1;
-}
-
-static void stv090x_get_lock_tmg(struct stv090x_state *state)
-{
- switch (state->algo) {
- case STV090x_BLIND_SEARCH:
- dprintk(FE_DEBUG, 1, "Blind Search");
- if (state->srate <= 1500000) { /*10Msps< SR <=15Msps*/
- state->DemodTimeout = 1500;
- state->FecTimeout = 400;
- } else if (state->srate <= 5000000) { /*10Msps< SR <=15Msps*/
- state->DemodTimeout = 1000;
- state->FecTimeout = 300;
- } else { /*SR >20Msps*/
- state->DemodTimeout = 700;
- state->FecTimeout = 100;
- }
- break;
-
- case STV090x_COLD_SEARCH:
- case STV090x_WARM_SEARCH:
- default:
- dprintk(FE_DEBUG, 1, "Normal Search");
- if (state->srate <= 1000000) { /*SR <=1Msps*/
- state->DemodTimeout = 4500;
- state->FecTimeout = 1700;
- } else if (state->srate <= 2000000) { /*1Msps < SR <= 2Msps */
- state->DemodTimeout = 2500;
- state->FecTimeout = 1100;
- } else if (state->srate <= 5000000) { /*2Msps < SR <= 5Msps */
- state->DemodTimeout = 1000;
- state->FecTimeout = 550;
- } else if (state->srate <= 10000000) { /*5Msps < SR <= 10Msps */
- state->DemodTimeout = 700;
- state->FecTimeout = 250;
- } else if (state->srate <= 20000000) { /*10Msps < SR <= 20Msps */
- state->DemodTimeout = 400;
- state->FecTimeout = 130;
- } else { /*SR >20Msps*/
- state->DemodTimeout = 300;
- state->FecTimeout = 100;
- }
- break;
- }
-
- if (state->algo == STV090x_WARM_SEARCH)
- state->DemodTimeout /= 2;
-}
-
-static int stv090x_set_srate(struct stv090x_state *state, u32 srate)
-{
- u32 sym;
-
- if (srate > 60000000) {
- sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->internal->mclk >> 12);
- } else if (srate > 6000000) {
- sym = (srate << 6);
- sym /= (state->internal->mclk >> 10);
- } else {
- sym = (srate << 9);
- sym /= (state->internal->mclk >> 7);
- }
-
- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRINIT0, (sym & 0xff)) < 0) /* LSB */
- goto err;
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate)
-{
- u32 sym;
-
- srate = 105 * (srate / 100);
- if (srate > 60000000) {
- sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->internal->mclk >> 12);
- } else if (srate > 6000000) {
- sym = (srate << 6);
- sym /= (state->internal->mclk >> 10);
- } else {
- sym = (srate << 9);
- sym /= (state->internal->mclk >> 7);
- }
-
- if (sym < 0x7fff) {
- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) /* MSB */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) /* LSB */
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x7f) < 0) /* MSB */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xff) < 0) /* LSB */
- goto err;
- }
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate)
-{
- u32 sym;
-
- srate = 95 * (srate / 100);
- if (srate > 60000000) {
- sym = (srate << 4); /* SR * 2^16 / master_clk */
- sym /= (state->internal->mclk >> 12);
- } else if (srate > 6000000) {
- sym = (srate << 6);
- sym /= (state->internal->mclk >> 10);
- } else {
- sym = (srate << 9);
- sym /= (state->internal->mclk >> 7);
- }
-
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */
- goto err;
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static u32 stv090x_car_width(u32 srate, enum stv090x_rolloff rolloff)
-{
- u32 ro;
-
- switch (rolloff) {
- case STV090x_RO_20:
- ro = 20;
- break;
- case STV090x_RO_25:
- ro = 25;
- break;
- case STV090x_RO_35:
- default:
- ro = 35;
- break;
- }
-
- return srate + (srate * ro) / 100;
-}
-
-static int stv090x_set_vit_thacq(struct stv090x_state *state)
-{
- if (STV090x_WRITE_DEMOD(state, VTH12, 0x96) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH23, 0x64) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH34, 0x36) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH56, 0x23) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH67, 0x1e) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH78, 0x19) < 0)
- goto err;
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_set_vit_thtracq(struct stv090x_state *state)
-{
- if (STV090x_WRITE_DEMOD(state, VTH12, 0xd0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH23, 0x7d) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH34, 0x53) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH56, 0x2f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH67, 0x24) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VTH78, 0x1f) < 0)
- goto err;
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_set_viterbi(struct stv090x_state *state)
-{
- switch (state->search_mode) {
- case STV090x_SEARCH_AUTO:
- if (STV090x_WRITE_DEMOD(state, FECM, 0x10) < 0) /* DVB-S and DVB-S2 */
- goto err;
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x3f) < 0) /* all puncture rate */
- goto err;
- break;
- case STV090x_SEARCH_DVBS1:
- if (STV090x_WRITE_DEMOD(state, FECM, 0x00) < 0) /* disable DSS */
- goto err;
- switch (state->fec) {
- case STV090x_PR12:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0)
- goto err;
- break;
-
- case STV090x_PR23:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0)
- goto err;
- break;
-
- case STV090x_PR34:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x04) < 0)
- goto err;
- break;
-
- case STV090x_PR56:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x08) < 0)
- goto err;
- break;
-
- case STV090x_PR78:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x20) < 0)
- goto err;
- break;
-
- default:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x2f) < 0) /* all */
- goto err;
- break;
- }
- break;
- case STV090x_SEARCH_DSS:
- if (STV090x_WRITE_DEMOD(state, FECM, 0x80) < 0)
- goto err;
- switch (state->fec) {
- case STV090x_PR12:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0)
- goto err;
- break;
-
- case STV090x_PR23:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0)
- goto err;
- break;
-
- case STV090x_PR67:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x10) < 0)
- goto err;
- break;
-
- default:
- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x13) < 0) /* 1/2, 2/3, 6/7 */
- goto err;
- break;
- }
- break;
- default:
- break;
- }
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_stop_modcod(struct stv090x_state *state)
-{
- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xff) < 0)
- goto err;
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_activate_modcod(struct stv090x_state *state)
-{
- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xfc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0)
- goto err;
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_activate_modcod_single(struct stv090x_state *state)
-{
-
- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xf0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0x0f) < 0)
- goto err;
-
- return 0;
-
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable)
-{
- u32 reg;
-
- switch (state->demod) {
- case STV090x_DEMODULATOR_0:
- mutex_lock(&state->internal->demod_lock);
- reg = stv090x_read_reg(state, STV090x_STOPCLK2);
- STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable);
- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
- goto err;
- mutex_unlock(&state->internal->demod_lock);
- break;
-
- case STV090x_DEMODULATOR_1:
- mutex_lock(&state->internal->demod_lock);
- reg = stv090x_read_reg(state, STV090x_STOPCLK2);
- STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable);
- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0)
- goto err;
- mutex_unlock(&state->internal->demod_lock);
- break;
-
- default:
- dprintk(FE_ERROR, 1, "Wrong demodulator!");
- break;
- }
- return 0;
-err:
- mutex_unlock(&state->internal->demod_lock);
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_dvbs_track_crl(struct stv090x_state *state)
-{
- if (state->internal->dev_ver >= 0x30) {
- /* Set ACLC BCLC optimised value vs SR */
- if (state->srate >= 15000000) {
- if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1a) < 0)
- goto err;
- } else if ((state->srate >= 7000000) && (15000000 > state->srate)) {
- if (STV090x_WRITE_DEMOD(state, ACLC, 0x0c) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1b) < 0)
- goto err;
- } else if (state->srate < 7000000) {
- if (STV090x_WRITE_DEMOD(state, ACLC, 0x2c) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1c) < 0)
- goto err;
- }
-
- } else {
- /* Cut 2.0 */
- if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0)
- goto err;
- }
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_delivery_search(struct stv090x_state *state)
-{
- u32 reg;
-
- switch (state->search_mode) {
- case STV090x_SEARCH_DVBS1:
- case STV090x_SEARCH_DSS:
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- /* Activate Viterbi decoder in legacy search,
- * do not use FRESVIT1, might impact VITERBI2
- */
- if (stv090x_vitclk_ctl(state, 0) < 0)
- goto err;
-
- if (stv090x_dvbs_track_crl(state) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x22) < 0) /* disable DVB-S2 */
- goto err;
-
- if (stv090x_set_vit_thacq(state) < 0)
- goto err;
- if (stv090x_set_viterbi(state) < 0)
- goto err;
- break;
-
- case STV090x_SEARCH_DVBS2:
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- if (stv090x_vitclk_ctl(state, 1) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) /* stop DVB-S CR loop */
- goto err;
- if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0)
- goto err;
-
- if (state->internal->dev_ver <= 0x20) {
- /* enable S2 carrier loop */
- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
- goto err;
- } else {
- /* > Cut 3: Stop carrier 3 */
- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0)
- goto err;
- }
-
- if (state->demod_mode != STV090x_SINGLE) {
- /* Cut 2: enable link during search */
- if (stv090x_activate_modcod(state) < 0)
- goto err;
- } else {
- /* Single demodulator
- * Authorize SHORT and LONG frames,
- * QPSK, 8PSK, 16APSK and 32APSK
- */
- if (stv090x_activate_modcod_single(state) < 0)
- goto err;
- }
-
- if (stv090x_set_vit_thtracq(state) < 0)
- goto err;
- break;
-
- case STV090x_SEARCH_AUTO:
- default:
- /* enable DVB-S2 and DVB-S2 in Auto MODE */
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- if (stv090x_vitclk_ctl(state, 0) < 0)
- goto err;
-
- if (stv090x_dvbs_track_crl(state) < 0)
- goto err;
-
- if (state->internal->dev_ver <= 0x20) {
- /* enable S2 carrier loop */
- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0)
- goto err;
- } else {
- /* > Cut 3: Stop carrier 3 */
- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0)
- goto err;
- }
-
- if (state->demod_mode != STV090x_SINGLE) {
- /* Cut 2: enable link during search */
- if (stv090x_activate_modcod(state) < 0)
- goto err;
- } else {
- /* Single demodulator
- * Authorize SHORT and LONG frames,
- * QPSK, 8PSK, 16APSK and 32APSK
- */
- if (stv090x_activate_modcod_single(state) < 0)
- goto err;
- }
-
- if (stv090x_set_vit_thacq(state) < 0)
- goto err;
-
- if (stv090x_set_viterbi(state) < 0)
- goto err;
- break;
- }
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_start_search(struct stv090x_state *state)
-{
- u32 reg, freq_abs;
- s16 freq;
-
- /* Reset demodulator */
- reg = STV090x_READ_DEMOD(state, DMDISTATE);
- STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f);
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
- goto err;
-
- if (state->internal->dev_ver <= 0x20) {
- if (state->srate <= 5000000) {
- if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0)
- goto err;
-
- /*enlarge the timing bandwidth for Low SR*/
- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0)
- goto err;
- } else {
- /* If the symbol rate is >5 Msps
- Set The carrier search up and low to auto mode */
- if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0)
- goto err;
- /*reduce the timing bandwidth for high SR*/
- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0)
- goto err;
- }
- } else {
- /* >= Cut 3 */
- if (state->srate <= 5000000) {
- /* enlarge the timing bandwidth for Low SR */
- STV090x_WRITE_DEMOD(state, RTCS2, 0x68);
- } else {
- /* reduce timing bandwidth for high SR */
- STV090x_WRITE_DEMOD(state, RTCS2, 0x44);
- }
-
- /* Set CFR min and max to manual mode */
- STV090x_WRITE_DEMOD(state, CARCFG, 0x46);
-
- if (state->algo == STV090x_WARM_SEARCH) {
- /* WARM Start
- * CFR min = -1MHz,
- * CFR max = +1MHz
- */
- freq_abs = 1000 << 16;
- freq_abs /= (state->internal->mclk / 1000);
- freq = (s16) freq_abs;
- } else {
- /* COLD Start
- * CFR min =- (SearchRange / 2 + 600KHz)
- * CFR max = +(SearchRange / 2 + 600KHz)
- * (600KHz for the tuner step size)
- */
- freq_abs = (state->search_range / 2000) + 600;
- freq_abs = freq_abs << 16;
- freq_abs /= (state->internal->mclk / 1000);
- freq = (s16) freq_abs;
- }
-
- if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0)
- goto err;
-
- freq *= -1;
-
- if (STV090x_WRITE_DEMOD(state, CFRLOW1, MSB(freq)) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRLOW0, LSB(freq)) < 0)
- goto err;
-
- }
-
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
- goto err;
-
- if ((state->search_mode == STV090x_SEARCH_DVBS1) ||
- (state->search_mode == STV090x_SEARCH_DSS) ||
- (state->search_mode == STV090x_SEARCH_AUTO)) {
-
- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0)
- goto err;
- }
- }
-
- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xe0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xc0) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- reg = STV090x_READ_DEMOD(state, DMDCFG2);
- STV090x_SETFIELD_Px(reg, S1S2_SEQUENTIAL_FIELD, 0x0);
- if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x20) {
- /*Frequency offset detector setting*/
- if (state->srate < 2000000) {
- if (state->internal->dev_ver <= 0x20) {
- /* Cut 2 */
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0)
- goto err;
- } else {
- /* Cut 3 */
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0)
- goto err;
- }
- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0)
- goto err;
- } else if (state->srate < 10000000) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0)
- goto err;
- }
- } else {
- if (state->srate < 10000000) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xef) < 0)
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xed) < 0)
- goto err;
- }
- }
-
- switch (state->algo) {
- case STV090x_WARM_SEARCH:
- /* The symbol rate and the exact
- * carrier Frequency are known
- */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
- goto err;
- break;
-
- case STV090x_COLD_SEARCH:
- /* The symbol rate is known */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
- goto err;
- break;
-
- default:
- break;
- }
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_get_agc2_min_level(struct stv090x_state *state)
-{
- u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg;
- s32 i, j, steps, dir;
-
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
- goto err;
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) /* SR = 65 Msps Max */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) /* SR= 400 ksps Min */
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) /* stop acq @ coarse carrier state */
- goto err;
- if (stv090x_set_srate(state, 1000000) < 0)
- goto err;
-
- steps = state->search_range / 1000000;
- if (steps <= 0)
- steps = 1;
-
- dir = 1;
- freq_step = (1000000 * 256) / (state->internal->mclk / 256);
- freq_init = 0;
-
- for (i = 0; i < steps; i++) {
- if (dir > 0)
- freq_init = freq_init + (freq_step * i);
- else
- freq_init = freq_init - (freq_step * i);
-
- dir *= -1;
-
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_init >> 8) & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_init & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */
- goto err;
- msleep(10);
-
- agc2 = 0;
- for (j = 0; j < 10; j++) {
- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
- STV090x_READ_DEMOD(state, AGC2I0);
- }
- agc2 /= 10;
- if (agc2 < agc2_min)
- agc2_min = agc2;
- }
-
- return agc2_min;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk)
-{
- u8 r3, r2, r1, r0;
- s32 srate, int_1, int_2, tmp_1, tmp_2;
-
- r3 = STV090x_READ_DEMOD(state, SFR3);
- r2 = STV090x_READ_DEMOD(state, SFR2);
- r1 = STV090x_READ_DEMOD(state, SFR1);
- r0 = STV090x_READ_DEMOD(state, SFR0);
-
- srate = ((r3 << 24) | (r2 << 16) | (r1 << 8) | r0);
-
- int_1 = clk >> 16;
- int_2 = srate >> 16;
-
- tmp_1 = clk % 0x10000;
- tmp_2 = srate % 0x10000;
-
- srate = (int_1 * int_2) +
- ((int_1 * tmp_2) >> 16) +
- ((int_2 * tmp_1) >> 16);
-
- return srate;
-}
-
-static u32 stv090x_srate_srch_coarse(struct stv090x_state *state)
-{
- struct dvb_frontend *fe = &state->frontend;
-
- int tmg_lock = 0, i;
- s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq;
- u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg;
- u32 agc2th;
-
- if (state->internal->dev_ver >= 0x30)
- agc2th = 0x2e00;
- else
- agc2th = 0x1f00;
-
- reg = STV090x_READ_DEMOD(state, DMDISTATE);
- STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0)
- goto err;
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x30) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0)
- goto err;
-
- } else if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0)
- goto err;
- }
-
- if (state->srate <= 2000000)
- car_step = 1000;
- else if (state->srate <= 5000000)
- car_step = 2000;
- else if (state->srate <= 12000000)
- car_step = 3000;
- else
- car_step = 5000;
-
- steps = -1 + ((state->search_range / 1000) / car_step);
- steps /= 2;
- steps = (2 * steps) + 1;
- if (steps < 0)
- steps = 1;
- else if (steps > 10) {
- steps = 11;
- car_step = (state->search_range / 1000) / 10;
- }
- cur_step = 0;
- dir = 1;
- freq = state->frequency;
-
- while ((!tmg_lock) && (cur_step < steps)) {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0)
- goto err;
- /* trigger acquisition */
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0)
- goto err;
- msleep(50);
- for (i = 0; i < 10; i++) {
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
- tmg_cpt++;
- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
- STV090x_READ_DEMOD(state, AGC2I0);
- }
- agc2 /= 10;
- srate_coarse = stv090x_get_srate(state, state->internal->mclk);
- cur_step++;
- dir *= -1;
- if ((tmg_cpt >= 5) && (agc2 < agc2th) &&
- (srate_coarse < 50000000) && (srate_coarse > 850000))
- tmg_lock = 1;
- else if (cur_step < steps) {
- if (dir > 0)
- freq += cur_step * car_step;
- else
- freq -= cur_step * car_step;
-
- /* Setup tuner */
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, freq) < 0)
- goto err_gateoff;
- }
-
- if (state->config->tuner_set_bandwidth) {
- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- msleep(50);
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_get_status) {
- if (state->config->tuner_get_status(fe, &reg) < 0)
- goto err_gateoff;
- }
-
- if (reg)
- dprintk(FE_DEBUG, 1, "Tuner phase locked");
- else
- dprintk(FE_DEBUG, 1, "Tuner unlocked");
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- }
- }
- if (!tmg_lock)
- srate_coarse = 0;
- else
- srate_coarse = stv090x_get_srate(state, state->internal->mclk);
-
- return srate_coarse;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static u32 stv090x_srate_srch_fine(struct stv090x_state *state)
-{
- u32 srate_coarse, freq_coarse, sym, reg;
-
- srate_coarse = stv090x_get_srate(state, state->internal->mclk);
- freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8;
- freq_coarse |= STV090x_READ_DEMOD(state, CFR1);
- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
-
- if (sym < state->srate)
- srate_coarse = 0;
- else {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0)
- goto err;
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x30) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0)
- goto err;
- } else if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
- goto err;
- }
-
- if (srate_coarse > 3000000) {
- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
- sym = (sym / 1000) * 65536;
- sym /= (state->internal->mclk / 1000);
- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
- goto err;
- sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */
- sym = (sym / 1000) * 65536;
- sym /= (state->internal->mclk / 1000);
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
- goto err;
- sym = (srate_coarse / 1000) * 65536;
- sym /= (state->internal->mclk / 1000);
- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
- goto err;
- } else {
- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */
- sym = (sym / 100) * 65536;
- sym /= (state->internal->mclk / 100);
- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0)
- goto err;
- sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */
- sym = (sym / 100) * 65536;
- sym /= (state->internal->mclk / 100);
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0)
- goto err;
- sym = (srate_coarse / 100) * 65536;
- sym /= (state->internal->mclk / 100);
- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0)
- goto err;
- }
- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_coarse >> 8) & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_coarse & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) /* trigger acquisition */
- goto err;
- }
-
- return srate_coarse;
-
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout)
-{
- s32 timer = 0, lock = 0;
- u32 reg;
- u8 stat;
-
- while ((timer < timeout) && (!lock)) {
- reg = STV090x_READ_DEMOD(state, DMDSTATE);
- stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
-
- switch (stat) {
- case 0: /* searching */
- case 1: /* first PLH detected */
- default:
- dprintk(FE_DEBUG, 1, "Demodulator searching ..");
- lock = 0;
- break;
- case 2: /* DVB-S2 mode */
- case 3: /* DVB-S1/legacy mode */
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- lock = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
- break;
- }
-
- if (!lock)
- msleep(10);
- else
- dprintk(FE_DEBUG, 1, "Demodulator acquired LOCK");
-
- timer += 10;
- }
- return lock;
-}
-
-static int stv090x_blind_search(struct stv090x_state *state)
-{
- u32 agc2, reg, srate_coarse;
- s32 cpt_fail, agc2_ovflw, i;
- u8 k_ref, k_max, k_min;
- int coarse_fail = 0;
- int lock;
-
- k_max = 110;
- k_min = 10;
-
- agc2 = stv090x_get_agc2_min_level(state);
-
- if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) {
- lock = 0;
- } else {
-
- if (state->internal->dev_ver <= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0)
- goto err;
- } else {
- /* > Cut 3 */
- if (STV090x_WRITE_DEMOD(state, CARCFG, 0x06) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) /* set viterbi hysteresis */
- goto err;
- }
-
- k_ref = k_max;
- do {
- if (STV090x_WRITE_DEMOD(state, KREFTMG, k_ref) < 0)
- goto err;
- if (stv090x_srate_srch_coarse(state) != 0) {
- srate_coarse = stv090x_srate_srch_fine(state);
- if (srate_coarse != 0) {
- stv090x_get_lock_tmg(state);
- lock = stv090x_get_dmdlock(state,
- state->DemodTimeout);
- } else {
- lock = 0;
- }
- } else {
- cpt_fail = 0;
- agc2_ovflw = 0;
- for (i = 0; i < 10; i++) {
- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) |
- STV090x_READ_DEMOD(state, AGC2I0);
- if (agc2 >= 0xff00)
- agc2_ovflw++;
- reg = STV090x_READ_DEMOD(state, DSTATUS2);
- if ((STV090x_GETFIELD_Px(reg, CFR_OVERFLOW_FIELD) == 0x01) &&
- (STV090x_GETFIELD_Px(reg, DEMOD_DELOCK_FIELD) == 0x01))
-
- cpt_fail++;
- }
- if ((cpt_fail > 7) || (agc2_ovflw > 7))
- coarse_fail = 1;
-
- lock = 0;
- }
- k_ref -= 20;
- } while ((k_ref >= k_min) && (!lock) && (!coarse_fail));
- }
-
- return lock;
-
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_chk_tmg(struct stv090x_state *state)
-{
- u32 reg;
- s32 tmg_cpt = 0, i;
- u8 freq, tmg_thh, tmg_thl;
- int tmg_lock = 0;
-
- freq = STV090x_READ_DEMOD(state, CARFREQ);
- tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE);
- tmg_thl = STV090x_READ_DEMOD(state, TMGTHFALL);
- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); /* stop carrier offset search */
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, RTC, 0x80) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x40) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x00) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) /* set car ofset to 0 */
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x65) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) /* trigger acquisition */
- goto err;
- msleep(10);
-
- for (i = 0; i < 10; i++) {
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2)
- tmg_cpt++;
- msleep(1);
- }
- if (tmg_cpt >= 3)
- tmg_lock = 1;
-
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) /* DVB-S1 timing */
- goto err;
- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) /* DVB-S2 timing */
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, CARFREQ, freq) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, tmg_thh) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, tmg_thl) < 0)
- goto err;
-
- return tmg_lock;
-
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd)
-{
- struct dvb_frontend *fe = &state->frontend;
-
- u32 reg;
- s32 car_step, steps, cur_step, dir, freq, timeout_lock;
- int lock = 0;
-
- if (state->srate >= 10000000)
- timeout_lock = timeout_dmd / 3;
- else
- timeout_lock = timeout_dmd / 2;
-
- lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */
- if (!lock) {
- if (state->srate >= 10000000) {
- if (stv090x_chk_tmg(state)) {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
- goto err;
- lock = stv090x_get_dmdlock(state, timeout_dmd);
- } else {
- lock = 0;
- }
- } else {
- if (state->srate <= 4000000)
- car_step = 1000;
- else if (state->srate <= 7000000)
- car_step = 2000;
- else if (state->srate <= 10000000)
- car_step = 3000;
- else
- car_step = 5000;
-
- steps = (state->search_range / 1000) / car_step;
- steps /= 2;
- steps = 2 * (steps + 1);
- if (steps < 0)
- steps = 2;
- else if (steps > 12)
- steps = 12;
-
- cur_step = 1;
- dir = 1;
-
- if (!lock) {
- freq = state->frequency;
- state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate;
- while ((cur_step <= steps) && (!lock)) {
- if (dir > 0)
- freq += cur_step * car_step;
- else
- freq -= cur_step * car_step;
-
- /* Setup tuner */
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, freq) < 0)
- goto err_gateoff;
- }
-
- if (state->config->tuner_set_bandwidth) {
- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- msleep(50);
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_get_status) {
- if (state->config->tuner_get_status(fe, &reg) < 0)
- goto err_gateoff;
- }
-
- if (reg)
- dprintk(FE_DEBUG, 1, "Tuner phase locked");
- else
- dprintk(FE_DEBUG, 1, "Tuner unlocked");
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c);
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0)
- goto err;
- lock = stv090x_get_dmdlock(state, (timeout_dmd / 3));
-
- dir *= -1;
- cur_step++;
- }
- }
- }
- }
-
- return lock;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s32 *timeout_sw, s32 *steps)
-{
- s32 timeout, inc, steps_max, srate, car_max;
-
- srate = state->srate;
- car_max = state->search_range / 1000;
- car_max += car_max / 10;
- car_max = 65536 * (car_max / 2);
- car_max /= (state->internal->mclk / 1000);
-
- if (car_max > 0x4000)
- car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */
-
- inc = srate;
- inc /= state->internal->mclk / 1000;
- inc *= 256;
- inc *= 256;
- inc /= 1000;
-
- switch (state->search_mode) {
- case STV090x_SEARCH_DVBS1:
- case STV090x_SEARCH_DSS:
- inc *= 3; /* freq step = 3% of srate */
- timeout = 20;
- break;
-
- case STV090x_SEARCH_DVBS2:
- inc *= 4;
- timeout = 25;
- break;
-
- case STV090x_SEARCH_AUTO:
- default:
- inc *= 3;
- timeout = 25;
- break;
- }
- inc /= 100;
- if ((inc > car_max) || (inc < 0))
- inc = car_max / 2; /* increment <= 1/8 Mclk */
-
- timeout *= 27500; /* 27.5 Msps reference */
- if (srate > 0)
- timeout /= (srate / 1000);
-
- if ((timeout > 100) || (timeout < 0))
- timeout = 100;
-
- steps_max = (car_max / inc) + 1; /* min steps = 3 */
- if ((steps_max > 100) || (steps_max < 0)) {
- steps_max = 100; /* max steps <= 100 */
- inc = car_max / steps_max;
- }
- *freq_inc = inc;
- *timeout_sw = timeout;
- *steps = steps_max;
-
- return 0;
-}
-
-static int stv090x_chk_signal(struct stv090x_state *state)
-{
- s32 offst_car, agc2, car_max;
- int no_signal;
-
- offst_car = STV090x_READ_DEMOD(state, CFR2) << 8;
- offst_car |= STV090x_READ_DEMOD(state, CFR1);
- offst_car = comp2(offst_car, 16);
-
- agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8;
- agc2 |= STV090x_READ_DEMOD(state, AGC2I0);
- car_max = state->search_range / 1000;
-
- car_max += (car_max / 10); /* 10% margin */
- car_max = (65536 * car_max / 2);
- car_max /= state->internal->mclk / 1000;
-
- if (car_max > 0x4000)
- car_max = 0x4000;
-
- if ((agc2 > 0x2000) || (offst_car > 2 * car_max) || (offst_car < -2 * car_max)) {
- no_signal = 1;
- dprintk(FE_DEBUG, 1, "No Signal");
- } else {
- no_signal = 0;
- dprintk(FE_DEBUG, 1, "Found Signal");
- }
-
- return no_signal;
-}
-
-static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 timeout, int zigzag, s32 steps_max)
-{
- int no_signal, lock = 0;
- s32 cpt_step = 0, offst_freq, car_max;
- u32 reg;
-
- car_max = state->search_range / 1000;
- car_max += (car_max / 10);
- car_max = (65536 * car_max / 2);
- car_max /= (state->internal->mclk / 1000);
- if (car_max > 0x4000)
- car_max = 0x4000;
-
- if (zigzag)
- offst_freq = 0;
- else
- offst_freq = -car_max + inc;
-
- do {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, ((offst_freq / 256) & 0xff)) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, offst_freq & 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, PDELCTRL1);
- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x1); /* stop DVB-S2 packet delin */
- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
- goto err;
-
- if (zigzag) {
- if (offst_freq >= 0)
- offst_freq = -offst_freq - 2 * inc;
- else
- offst_freq = -offst_freq;
- } else {
- offst_freq += 2 * inc;
- }
-
- cpt_step++;
-
- lock = stv090x_get_dmdlock(state, timeout);
- no_signal = stv090x_chk_signal(state);
-
- } while ((!lock) &&
- (!no_signal) &&
- ((offst_freq - inc) < car_max) &&
- ((offst_freq + inc) > -car_max) &&
- (cpt_step < steps_max));
-
- reg = STV090x_READ_DEMOD(state, PDELCTRL1);
- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
- goto err;
-
- return lock;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_sw_algo(struct stv090x_state *state)
-{
- int no_signal, zigzag, lock = 0;
- u32 reg;
-
- s32 dvbs2_fly_wheel;
- s32 inc, timeout_step, trials, steps_max;
-
- /* get params */
- stv090x_get_loop_params(state, &inc, &timeout_step, &steps_max);
-
- switch (state->search_mode) {
- case STV090x_SEARCH_DVBS1:
- case STV090x_SEARCH_DSS:
- /* accelerate the frequency detector */
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x49) < 0)
- goto err;
- zigzag = 0;
- break;
-
- case STV090x_SEARCH_DVBS2:
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0)
- goto err;
- zigzag = 1;
- break;
-
- case STV090x_SEARCH_AUTO:
- default:
- /* accelerate the frequency detector */
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0xc9) < 0)
- goto err;
- zigzag = 0;
- break;
- }
-
- trials = 0;
- do {
- lock = stv090x_search_car_loop(state, inc, timeout_step, zigzag, steps_max);
- no_signal = stv090x_chk_signal(state);
- trials++;
-
- /*run the SW search 2 times maximum*/
- if (lock || no_signal || (trials == 2)) {
- /*Check if the demod is not losing lock in DVBS2*/
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
- goto err;
- }
-
- reg = STV090x_READ_DEMOD(state, DMDSTATE);
- if ((lock) && (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == STV090x_DVBS2)) {
- /*Check if the demod is not losing lock in DVBS2*/
- msleep(timeout_step);
- reg = STV090x_READ_DEMOD(state, DMDFLYW);
- dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD);
- if (dvbs2_fly_wheel < 0xd) { /*if correct frames is decrementing */
- msleep(timeout_step);
- reg = STV090x_READ_DEMOD(state, DMDFLYW);
- dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD);
- }
- if (dvbs2_fly_wheel < 0xd) {
- /*FALSE lock, The demod is losing lock */
- lock = 0;
- if (trials < 2) {
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0)
- goto err;
- }
- }
- }
- }
- } while ((!lock) && (trials < 2) && (!no_signal));
-
- return lock;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static enum stv090x_delsys stv090x_get_std(struct stv090x_state *state)
-{
- u32 reg;
- enum stv090x_delsys delsys;
-
- reg = STV090x_READ_DEMOD(state, DMDSTATE);
- if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 2)
- delsys = STV090x_DVBS2;
- else if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 3) {
- reg = STV090x_READ_DEMOD(state, FECM);
- if (STV090x_GETFIELD_Px(reg, DSS_DVB_FIELD) == 1)
- delsys = STV090x_DSS;
- else
- delsys = STV090x_DVBS1;
- } else {
- delsys = STV090x_ERROR;
- }
-
- return delsys;
-}
-
-/* in Hz */
-static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk)
-{
- s32 derot, int_1, int_2, tmp_1, tmp_2;
-
- derot = STV090x_READ_DEMOD(state, CFR2) << 16;
- derot |= STV090x_READ_DEMOD(state, CFR1) << 8;
- derot |= STV090x_READ_DEMOD(state, CFR0);
-
- derot = comp2(derot, 24);
- int_1 = mclk >> 12;
- int_2 = derot >> 12;
-
- /* carrier_frequency = MasterClock * Reg / 2^24 */
- tmp_1 = mclk % 0x1000;
- tmp_2 = derot % 0x1000;
-
- derot = (int_1 * int_2) +
- ((int_1 * tmp_2) >> 12) +
- ((int_2 * tmp_1) >> 12);
-
- return derot;
-}
-
-static int stv090x_get_viterbi(struct stv090x_state *state)
-{
- u32 reg, rate;
-
- reg = STV090x_READ_DEMOD(state, VITCURPUN);
- rate = STV090x_GETFIELD_Px(reg, VIT_CURPUN_FIELD);
-
- switch (rate) {
- case 13:
- state->fec = STV090x_PR12;
- break;
-
- case 18:
- state->fec = STV090x_PR23;
- break;
-
- case 21:
- state->fec = STV090x_PR34;
- break;
-
- case 24:
- state->fec = STV090x_PR56;
- break;
-
- case 25:
- state->fec = STV090x_PR67;
- break;
-
- case 26:
- state->fec = STV090x_PR78;
- break;
-
- default:
- state->fec = STV090x_PRERR;
- break;
- }
-
- return 0;
-}
-
-static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state)
-{
- struct dvb_frontend *fe = &state->frontend;
-
- u8 tmg;
- u32 reg;
- s32 i = 0, offst_freq;
-
- msleep(5);
-
- if (state->algo == STV090x_BLIND_SEARCH) {
- tmg = STV090x_READ_DEMOD(state, TMGREG2);
- STV090x_WRITE_DEMOD(state, SFRSTEP, 0x5c);
- while ((i <= 50) && (tmg != 0) && (tmg != 0xff)) {
- tmg = STV090x_READ_DEMOD(state, TMGREG2);
- msleep(5);
- i += 5;
- }
- }
- state->delsys = stv090x_get_std(state);
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_get_frequency) {
- if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000;
- state->frequency += offst_freq;
-
- if (stv090x_get_viterbi(state) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, DMDMODCOD);
- state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD);
- state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01;
- state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1;
- reg = STV090x_READ_DEMOD(state, TMGOBS);
- state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD);
- reg = STV090x_READ_DEMOD(state, FECM);
- state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD);
-
- if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) {
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_get_frequency) {
- if (state->config->tuner_get_frequency(fe, &state->frequency) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- if (abs(offst_freq) <= ((state->search_range / 2000) + 500))
- return STV090x_RANGEOK;
- else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000))
- return STV090x_RANGEOK;
- else
- return STV090x_OUTOFRANGE; /* Out of Range */
- } else {
- if (abs(offst_freq) <= ((state->search_range / 2000) + 500))
- return STV090x_RANGEOK;
- else
- return STV090x_OUTOFRANGE;
- }
-
- return STV090x_OUTOFRANGE;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static u32 stv090x_get_tmgoffst(struct stv090x_state *state, u32 srate)
-{
- s32 offst_tmg;
-
- offst_tmg = STV090x_READ_DEMOD(state, TMGREG2) << 16;
- offst_tmg |= STV090x_READ_DEMOD(state, TMGREG1) << 8;
- offst_tmg |= STV090x_READ_DEMOD(state, TMGREG0);
-
- offst_tmg = comp2(offst_tmg, 24); /* 2's complement */
- if (!offst_tmg)
- offst_tmg = 1;
-
- offst_tmg = ((s32) srate * 10) / ((s32) 0x1000000 / offst_tmg);
- offst_tmg /= 320;
-
- return offst_tmg;
-}
-
-static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_modcod modcod, s32 pilots)
-{
- u8 aclc = 0x29;
- s32 i;
- struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low;
-
- if (state->internal->dev_ver == 0x20) {
- car_loop = stv090x_s2_crl_cut20;
- car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20;
- car_loop_apsk_low = stv090x_s2_apsk_crl_cut20;
- } else {
- /* >= Cut 3 */
- car_loop = stv090x_s2_crl_cut30;
- car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut30;
- car_loop_apsk_low = stv090x_s2_apsk_crl_cut30;
- }
-
- if (modcod < STV090x_QPSK_12) {
- i = 0;
- while ((i < 3) && (modcod != car_loop_qpsk_low[i].modcod))
- i++;
-
- if (i >= 3)
- i = 2;
-
- } else {
- i = 0;
- while ((i < 14) && (modcod != car_loop[i].modcod))
- i++;
-
- if (i >= 14) {
- i = 0;
- while ((i < 11) && (modcod != car_loop_apsk_low[i].modcod))
- i++;
-
- if (i >= 11)
- i = 10;
- }
- }
-
- if (modcod <= STV090x_QPSK_25) {
- if (pilots) {
- if (state->srate <= 3000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_on_2;
- else if (state->srate <= 7000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_on_5;
- else if (state->srate <= 15000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_on_10;
- else if (state->srate <= 25000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_on_20;
- else
- aclc = car_loop_qpsk_low[i].crl_pilots_on_30;
- } else {
- if (state->srate <= 3000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_off_2;
- else if (state->srate <= 7000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_off_5;
- else if (state->srate <= 15000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_off_10;
- else if (state->srate <= 25000000)
- aclc = car_loop_qpsk_low[i].crl_pilots_off_20;
- else
- aclc = car_loop_qpsk_low[i].crl_pilots_off_30;
- }
-
- } else if (modcod <= STV090x_8PSK_910) {
- if (pilots) {
- if (state->srate <= 3000000)
- aclc = car_loop[i].crl_pilots_on_2;
- else if (state->srate <= 7000000)
- aclc = car_loop[i].crl_pilots_on_5;
- else if (state->srate <= 15000000)
- aclc = car_loop[i].crl_pilots_on_10;
- else if (state->srate <= 25000000)
- aclc = car_loop[i].crl_pilots_on_20;
- else
- aclc = car_loop[i].crl_pilots_on_30;
- } else {
- if (state->srate <= 3000000)
- aclc = car_loop[i].crl_pilots_off_2;
- else if (state->srate <= 7000000)
- aclc = car_loop[i].crl_pilots_off_5;
- else if (state->srate <= 15000000)
- aclc = car_loop[i].crl_pilots_off_10;
- else if (state->srate <= 25000000)
- aclc = car_loop[i].crl_pilots_off_20;
- else
- aclc = car_loop[i].crl_pilots_off_30;
- }
- } else { /* 16APSK and 32APSK */
- if (state->srate <= 3000000)
- aclc = car_loop_apsk_low[i].crl_pilots_on_2;
- else if (state->srate <= 7000000)
- aclc = car_loop_apsk_low[i].crl_pilots_on_5;
- else if (state->srate <= 15000000)
- aclc = car_loop_apsk_low[i].crl_pilots_on_10;
- else if (state->srate <= 25000000)
- aclc = car_loop_apsk_low[i].crl_pilots_on_20;
- else
- aclc = car_loop_apsk_low[i].crl_pilots_on_30;
- }
-
- return aclc;
-}
-
-static u8 stv090x_optimize_carloop_short(struct stv090x_state *state)
-{
- struct stv090x_short_frame_crloop *short_crl = NULL;
- s32 index = 0;
- u8 aclc = 0x0b;
-
- switch (state->modulation) {
- case STV090x_QPSK:
- default:
- index = 0;
- break;
- case STV090x_8PSK:
- index = 1;
- break;
- case STV090x_16APSK:
- index = 2;
- break;
- case STV090x_32APSK:
- index = 3;
- break;
- }
-
- if (state->internal->dev_ver >= 0x30) {
- /* Cut 3.0 and up */
- short_crl = stv090x_s2_short_crl_cut30;
- } else {
- /* Cut 2.0 and up: we don't support cuts older than 2.0 */
- short_crl = stv090x_s2_short_crl_cut20;
- }
-
- if (state->srate <= 3000000)
- aclc = short_crl[index].crl_2;
- else if (state->srate <= 7000000)
- aclc = short_crl[index].crl_5;
- else if (state->srate <= 15000000)
- aclc = short_crl[index].crl_10;
- else if (state->srate <= 25000000)
- aclc = short_crl[index].crl_20;
- else
- aclc = short_crl[index].crl_30;
-
- return aclc;
-}
-
-static int stv090x_optimize_track(struct stv090x_state *state)
-{
- struct dvb_frontend *fe = &state->frontend;
-
- enum stv090x_modcod modcod;
-
- s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0;
- u32 reg;
-
- srate = stv090x_get_srate(state, state->internal->mclk);
- srate += stv090x_get_tmgoffst(state, srate);
-
- switch (state->delsys) {
- case STV090x_DVBS1:
- case STV090x_DSS:
- if (state->search_mode == STV090x_SEARCH_AUTO) {
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- }
- reg = STV090x_READ_DEMOD(state, DEMOD);
- STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff);
- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x01);
- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x30) {
- if (stv090x_get_viterbi(state) < 0)
- goto err;
-
- if (state->fec == STV090x_PR12) {
- if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x98) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0)
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x18) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0)
- goto err;
- }
- }
-
- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0)
- goto err;
- break;
-
- case STV090x_DVBS2:
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 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);
- pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01;
- aclc = stv090x_optimize_carloop(state, modcod, pilots);
- if (modcod <= STV090x_QPSK_910) {
- STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc);
- } else if (modcod <= STV090x_8PSK_910) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0)
- goto err;
- }
- if ((state->demod_mode == STV090x_SINGLE) && (modcod > STV090x_8PSK_910)) {
- if (modcod <= STV090x_16APSK_910) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0)
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0)
- goto err;
- }
- }
- } else {
- /*Carrier loop setting for short frame*/
- aclc = stv090x_optimize_carloop_short(state);
- if (state->modulation == STV090x_QPSK) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc) < 0)
- goto err;
- } else if (state->modulation == STV090x_8PSK) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0)
- goto err;
- } else if (state->modulation == STV090x_16APSK) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0)
- goto err;
- } else if (state->modulation == STV090x_32APSK) {
- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0)
- goto err;
- }
- }
-
- STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67); /* PER */
- break;
-
- case STV090x_ERROR:
- default:
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1);
- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- break;
- }
-
- f_1 = STV090x_READ_DEMOD(state, CFR2);
- f_0 = STV090x_READ_DEMOD(state, CFR1);
- reg = STV090x_READ_DEMOD(state, TMGOBS);
-
- if (state->algo == STV090x_BLIND_SEARCH) {
- STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00);
- reg = STV090x_READ_DEMOD(state, DMDCFGMD);
- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0x00);
- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00);
- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0)
- goto err;
-
- if (stv090x_set_srate(state, srate) < 0)
- goto err;
- blind_tune = 1;
-
- if (stv090x_dvbs_track_crl(state) < 0)
- goto err;
- }
-
- if (state->internal->dev_ver >= 0x20) {
- if ((state->search_mode == STV090x_SEARCH_DVBS1) ||
- (state->search_mode == STV090x_SEARCH_DSS) ||
- (state->search_mode == STV090x_SEARCH_AUTO)) {
-
- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x0a) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x00) < 0)
- goto err;
- }
- }
-
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
- goto err;
-
- /* AUTO tracking MODE */
- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x80) < 0)
- goto err;
- /* AUTO tracking MODE */
- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0)
- goto err;
-
- if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) ||
- (state->srate < 10000000)) {
- /* update initial carrier freq with the found freq offset */
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
- goto err;
- state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000;
-
- if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) {
-
- if (state->algo != STV090x_WARM_SEARCH) {
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_set_bandwidth) {
- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- }
- }
- if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000))
- msleep(50); /* blind search: wait 50ms for SR stabilization */
- else
- msleep(5);
-
- stv090x_get_lock_tmg(state);
-
- if (!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) {
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
- goto err;
-
- i = 0;
-
- while ((!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) && (i <= 2)) {
-
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0)
- goto err;
- i++;
- }
- }
-
- }
-
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0)
- goto err;
- }
-
- if ((state->delsys == STV090x_DVBS1) || (state->delsys == STV090x_DSS))
- stv090x_set_vit_thtracq(state);
-
- return 0;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_get_feclock(struct stv090x_state *state, s32 timeout)
-{
- s32 timer = 0, lock = 0, stat;
- u32 reg;
-
- while ((timer < timeout) && (!lock)) {
- reg = STV090x_READ_DEMOD(state, DMDSTATE);
- stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
-
- switch (stat) {
- case 0: /* searching */
- case 1: /* first PLH detected */
- default:
- lock = 0;
- break;
-
- case 2: /* DVB-S2 mode */
- reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
- lock = STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD);
- break;
-
- case 3: /* DVB-S1/legacy mode */
- reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
- lock = STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD);
- break;
- }
- if (!lock) {
- msleep(10);
- timer += 10;
- }
- }
- return lock;
-}
-
-static int stv090x_get_lock(struct stv090x_state *state, s32 timeout_dmd, s32 timeout_fec)
-{
- u32 reg;
- s32 timer = 0;
- int lock;
-
- lock = stv090x_get_dmdlock(state, timeout_dmd);
- if (lock)
- lock = stv090x_get_feclock(state, timeout_fec);
-
- if (lock) {
- lock = 0;
-
- while ((timer < timeout_fec) && (!lock)) {
- reg = STV090x_READ_DEMOD(state, TSSTATUS);
- lock = STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD);
- msleep(1);
- timer++;
- }
- }
-
- return lock;
-}
-
-static int stv090x_set_s2rolloff(struct stv090x_state *state)
-{
- u32 reg;
-
- if (state->internal->dev_ver <= 0x20) {
- /* rolloff to auto mode if DVBS2 */
- reg = STV090x_READ_DEMOD(state, DEMOD);
- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00);
- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
- goto err;
- } else {
- /* DVB-S2 rolloff to auto mode if DVBS2 */
- reg = STV090x_READ_DEMOD(state, DEMOD);
- STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 0x00);
- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
- goto err;
- }
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-
-static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state)
-{
- struct dvb_frontend *fe = &state->frontend;
- enum stv090x_signal_state signal_state = STV090x_NOCARRIER;
- u32 reg;
- s32 agc1_power, power_iq = 0, i;
- int lock = 0, low_sr = 0;
-
- reg = STV090x_READ_DEMOD(state, TSCFGH);
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */
- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */
- goto err;
-
- if (state->internal->dev_ver >= 0x20) {
- if (state->srate > 5000000) {
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0)
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0)
- goto err;
- }
- }
-
- stv090x_get_lock_tmg(state);
-
- if (state->algo == STV090x_BLIND_SEARCH) {
- state->tuner_bw = 2 * 36000000; /* wide bw for unknown srate */
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) /* wider srate scan */
- goto err;
- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0)
- goto err;
- if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */
- goto err;
- } else {
- /* known srate */
- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0)
- goto err;
-
- if (state->srate < 2000000) {
- /* SR < 2MSPS */
- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x63) < 0)
- goto err;
- } else {
- /* SR >= 2Msps */
- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0)
- goto err;
- }
-
- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0)
- goto err;
-
- if (state->internal->dev_ver >= 0x20) {
- if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0)
- goto err;
- if (state->algo == STV090x_COLD_SEARCH)
- state->tuner_bw = (15 * (stv090x_car_width(state->srate, state->rolloff) + 10000000)) / 10;
- else if (state->algo == STV090x_WARM_SEARCH)
- state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + 10000000;
- }
-
- /* if cold start or warm (Symbolrate is known)
- * use a Narrow symbol rate scan range
- */
- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) /* narrow srate scan */
- goto err;
-
- if (stv090x_set_srate(state, state->srate) < 0)
- goto err;
-
- if (stv090x_set_max_srate(state, state->internal->mclk,
- state->srate) < 0)
- goto err;
- if (stv090x_set_min_srate(state, state->internal->mclk,
- state->srate) < 0)
- goto err;
-
- if (state->srate >= 10000000)
- low_sr = 0;
- else
- low_sr = 1;
- }
-
- /* Setup tuner */
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (state->config->tuner_set_bbgain) {
- reg = state->config->tuner_bbgain;
- if (reg == 0)
- reg = 10; /* default: 10dB */
- if (state->config->tuner_set_bbgain(fe, reg) < 0)
- goto err_gateoff;
- }
-
- if (state->config->tuner_set_frequency) {
- if (state->config->tuner_set_frequency(fe, state->frequency) < 0)
- goto err_gateoff;
- }
-
- if (state->config->tuner_set_bandwidth) {
- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- msleep(50);
-
- if (state->config->tuner_get_status) {
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
- if (state->config->tuner_get_status(fe, &reg) < 0)
- goto err_gateoff;
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- if (reg)
- dprintk(FE_DEBUG, 1, "Tuner phase locked");
- else {
- dprintk(FE_DEBUG, 1, "Tuner unlocked");
- return STV090x_NOCARRIER;
- }
- }
-
- msleep(10);
- agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1),
- STV090x_READ_DEMOD(state, AGCIQIN0));
-
- if (agc1_power == 0) {
- /* If AGC1 integrator value is 0
- * then read POWERI, POWERQ
- */
- for (i = 0; i < 5; i++) {
- power_iq += (STV090x_READ_DEMOD(state, POWERI) +
- STV090x_READ_DEMOD(state, POWERQ)) >> 1;
- }
- power_iq /= 5;
- }
-
- if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) {
- dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq);
- lock = 0;
- signal_state = STV090x_NOAGC1;
- } else {
- reg = STV090x_READ_DEMOD(state, DEMOD);
- STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion);
-
- if (state->internal->dev_ver <= 0x20) {
- /* rolloff to auto mode if DVBS2 */
- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1);
- } else {
- /* DVB-S2 rolloff to auto mode if DVBS2 */
- STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 1);
- }
- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
- goto err;
-
- if (stv090x_delivery_search(state) < 0)
- goto err;
-
- if (state->algo != STV090x_BLIND_SEARCH) {
- if (stv090x_start_search(state) < 0)
- goto err;
- }
- }
-
- if (signal_state == STV090x_NOAGC1)
- return signal_state;
-
- if (state->algo == STV090x_BLIND_SEARCH)
- lock = stv090x_blind_search(state);
-
- else if (state->algo == STV090x_COLD_SEARCH)
- lock = stv090x_get_coldlock(state, state->DemodTimeout);
-
- else if (state->algo == STV090x_WARM_SEARCH)
- lock = stv090x_get_dmdlock(state, state->DemodTimeout);
-
- if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) {
- if (!low_sr) {
- if (stv090x_chk_tmg(state))
- lock = stv090x_sw_algo(state);
- }
- }
-
- if (lock)
- signal_state = stv090x_get_sig_params(state);
-
- if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */
- stv090x_optimize_track(state);
-
- if (state->internal->dev_ver >= 0x20) {
- /* >= Cut 2.0 :release TS reset after
- * demod lock and optimized Tracking
- */
- reg = STV090x_READ_DEMOD(state, TSCFGH);
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */
- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
- goto err;
-
- msleep(3);
-
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* merger reset */
- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
- goto err;
-
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */
- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0)
- goto err;
- }
-
- lock = stv090x_get_lock(state, state->FecTimeout,
- state->FecTimeout);
- if (lock) {
- if (state->delsys == STV090x_DVBS2) {
- stv090x_set_s2rolloff(state);
-
- reg = STV090x_READ_DEMOD(state, PDELCTRL2);
- STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 1);
- if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0)
- goto err;
- /* Reset DVBS2 packet delinator error counter */
- reg = STV090x_READ_DEMOD(state, PDELCTRL2);
- STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 0);
- if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67) < 0) /* PER */
- goto err;
- } else {
- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0)
- goto err;
- }
- /* Reset the Total packet counter */
- if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0x00) < 0)
- goto err;
- /* Reset the packet Error counter2 */
- if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
- goto err;
- } else {
- signal_state = STV090x_NODATA;
- stv090x_chk_signal(state);
- }
- }
- return signal_state;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static enum dvbfe_search stv090x_search(struct dvb_frontend *fe)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- struct dtv_frontend_properties *props = &fe->dtv_property_cache;
-
- if (props->frequency == 0)
- return DVBFE_ALGO_SEARCH_INVALID;
-
- state->delsys = props->delivery_system;
- state->frequency = props->frequency;
- state->srate = props->symbol_rate;
- state->search_mode = STV090x_SEARCH_AUTO;
- state->algo = STV090x_COLD_SEARCH;
- state->fec = STV090x_PRERR;
- if (state->srate > 10000000) {
- dprintk(FE_DEBUG, 1, "Search range: 10 MHz");
- state->search_range = 10000000;
- } else {
- dprintk(FE_DEBUG, 1, "Search range: 5 MHz");
- state->search_range = 5000000;
- }
-
- if (stv090x_algo(state) == STV090x_RANGEOK) {
- dprintk(FE_DEBUG, 1, "Search success!");
- return DVBFE_ALGO_SEARCH_SUCCESS;
- } else {
- dprintk(FE_DEBUG, 1, "Search failed!");
- return DVBFE_ALGO_SEARCH_FAILED;
- }
-
- return DVBFE_ALGO_SEARCH_ERROR;
-}
-
-static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg, dstatus;
- u8 search_state;
-
- *status = 0;
-
- dstatus = STV090x_READ_DEMOD(state, DSTATUS);
- if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD))
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
-
- reg = STV090x_READ_DEMOD(state, DMDSTATE);
- search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD);
-
- switch (search_state) {
- case 0: /* searching */
- case 1: /* first PLH detected */
- default:
- dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)");
- break;
-
- case 2: /* DVB-S2 mode */
- dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2");
- if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
- reg = STV090x_READ_DEMOD(state, PDELSTATUS1);
- if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) {
- *status |= FE_HAS_VITERBI;
- reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
- *status |= FE_HAS_SYNC | FE_HAS_LOCK;
- }
- }
- break;
-
- case 3: /* DVB-S1/legacy mode */
- dprintk(FE_DEBUG, 1, "Delivery system: DVB-S");
- if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) {
- reg = STV090x_READ_DEMOD(state, VSTATUSVIT);
- if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) {
- *status |= FE_HAS_VITERBI;
- reg = STV090x_READ_DEMOD(state, TSSTATUS);
- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD))
- *status |= FE_HAS_SYNC | FE_HAS_LOCK;
- }
- }
- break;
- }
-
- return 0;
-}
-
-static int stv090x_read_per(struct dvb_frontend *fe, u32 *per)
-{
- struct stv090x_state *state = fe->demodulator_priv;
-
- s32 count_4, count_3, count_2, count_1, count_0, count;
- u32 reg, h, m, l;
- enum fe_status status;
-
- stv090x_read_status(fe, &status);
- if (!(status & FE_HAS_LOCK)) {
- *per = 1 << 23; /* Max PER */
- } else {
- /* Counter 2 */
- reg = STV090x_READ_DEMOD(state, ERRCNT22);
- h = STV090x_GETFIELD_Px(reg, ERR_CNT2_FIELD);
-
- reg = STV090x_READ_DEMOD(state, ERRCNT21);
- m = STV090x_GETFIELD_Px(reg, ERR_CNT21_FIELD);
-
- reg = STV090x_READ_DEMOD(state, ERRCNT20);
- l = STV090x_GETFIELD_Px(reg, ERR_CNT20_FIELD);
-
- *per = ((h << 16) | (m << 8) | l);
-
- count_4 = STV090x_READ_DEMOD(state, FBERCPT4);
- count_3 = STV090x_READ_DEMOD(state, FBERCPT3);
- count_2 = STV090x_READ_DEMOD(state, FBERCPT2);
- count_1 = STV090x_READ_DEMOD(state, FBERCPT1);
- count_0 = STV090x_READ_DEMOD(state, FBERCPT0);
-
- if ((!count_4) && (!count_3)) {
- count = (count_2 & 0xff) << 16;
- count |= (count_1 & 0xff) << 8;
- count |= count_0 & 0xff;
- } else {
- count = 1 << 24;
- }
- if (count == 0)
- *per = 1;
- }
- if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0)
- goto err;
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val)
-{
- int res = 0;
- int min = 0, med;
-
- if ((val >= tab[min].read && val < tab[max].read) ||
- (val >= tab[max].read && val < tab[min].read)) {
- while ((max - min) > 1) {
- med = (max + min) / 2;
- if ((val >= tab[min].read && val < tab[med].read) ||
- (val >= tab[med].read && val < tab[min].read))
- max = med;
- else
- min = med;
- }
- res = ((val - tab[min].read) *
- (tab[max].real - tab[min].real) /
- (tab[max].read - tab[min].read)) +
- tab[min].real;
- } else {
- if (tab[min].read < tab[max].read) {
- if (val < tab[min].read)
- res = tab[min].real;
- else if (val >= tab[max].read)
- res = tab[max].real;
- } else {
- if (val >= tab[min].read)
- res = tab[min].real;
- else if (val < tab[max].read)
- res = tab[max].real;
- }
- }
-
- return res;
-}
-
-static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg;
- s32 agc_0, agc_1, agc;
- s32 str;
-
- reg = STV090x_READ_DEMOD(state, AGCIQIN1);
- agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
- reg = STV090x_READ_DEMOD(state, AGCIQIN0);
- agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD);
- agc = MAKEWORD16(agc_1, agc_0);
-
- str = stv090x_table_lookup(stv090x_rf_tab,
- ARRAY_SIZE(stv090x_rf_tab) - 1, agc);
- if (agc > stv090x_rf_tab[0].read)
- str = 0;
- else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read)
- str = -100;
- *strength = (str + 100) * 0xFFFF / 100;
-
- return 0;
-}
-
-static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg_0, reg_1, reg, i;
- s32 val_0, val_1, val = 0;
- u8 lock_f;
- s32 div;
- u32 last;
-
- switch (state->delsys) {
- case STV090x_DVBS2:
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
- if (lock_f) {
- msleep(5);
- for (i = 0; i < 16; i++) {
- reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1);
- val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD);
- reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0);
- val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD);
- val += MAKEWORD16(val_1, val_0);
- msleep(1);
- }
- val /= 16;
- last = ARRAY_SIZE(stv090x_s2cn_tab) - 1;
- div = stv090x_s2cn_tab[0].read -
- stv090x_s2cn_tab[last].read;
- *cnr = 0xFFFF - ((val * 0xFFFF) / div);
- }
- break;
-
- case STV090x_DVBS1:
- case STV090x_DSS:
- reg = STV090x_READ_DEMOD(state, DSTATUS);
- lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD);
- if (lock_f) {
- msleep(5);
- for (i = 0; i < 16; i++) {
- reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1);
- val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD);
- reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0);
- val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD);
- val += MAKEWORD16(val_1, val_0);
- msleep(1);
- }
- val /= 16;
- last = ARRAY_SIZE(stv090x_s1cn_tab) - 1;
- div = stv090x_s1cn_tab[0].read -
- stv090x_s1cn_tab[last].read;
- *cnr = 0xFFFF - ((val * 0xFFFF) / div);
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int stv090x_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg;
-
- reg = STV090x_READ_DEMOD(state, DISTXCTL);
- switch (tone) {
- case SEC_TONE_ON:
- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
- break;
-
- case SEC_TONE_OFF:
- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0);
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-
-static enum dvbfe_algo stv090x_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_CUSTOM;
-}
-
-static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg, idle = 0, fifo_full = 1;
- int i;
-
- reg = STV090x_READ_DEMOD(state, DISTXCTL);
-
- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD,
- (state->config->diseqc_envelope_mode) ? 4 : 2);
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- for (i = 0; i < cmd->msg_len; i++) {
-
- while (fifo_full) {
- reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
- fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD);
- }
-
- if (STV090x_WRITE_DEMOD(state, DISTXDATA, cmd->msg[i]) < 0)
- goto err;
- }
- reg = STV090x_READ_DEMOD(state, DISTXCTL);
- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- i = 0;
-
- while ((!idle) && (i < 10)) {
- reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
- idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD);
- msleep(10);
- i++;
- }
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg, idle = 0, fifo_full = 1;
- u8 mode, value;
- int i;
-
- reg = STV090x_READ_DEMOD(state, DISTXCTL);
-
- if (burst == SEC_MINI_A) {
- mode = (state->config->diseqc_envelope_mode) ? 5 : 3;
- value = 0x00;
- } else {
- mode = (state->config->diseqc_envelope_mode) ? 4 : 2;
- value = 0xFF;
- }
-
- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, mode);
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- while (fifo_full) {
- reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
- fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD);
- }
-
- if (STV090x_WRITE_DEMOD(state, DISTXDATA, value) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, DISTXCTL);
- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0);
- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0)
- goto err;
-
- i = 0;
-
- while ((!idle) && (i < 10)) {
- reg = STV090x_READ_DEMOD(state, DISTXSTATUS);
- idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD);
- msleep(10);
- i++;
- }
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg = 0, i = 0, rx_end = 0;
-
- while ((rx_end != 1) && (i < 10)) {
- msleep(10);
- i++;
- reg = STV090x_READ_DEMOD(state, DISRX_ST0);
- rx_end = STV090x_GETFIELD_Px(reg, RX_END_FIELD);
- }
-
- if (rx_end) {
- reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD);
- for (i = 0; i < reply->msg_len; i++)
- reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA);
- }
-
- return 0;
-}
-
-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;
-
- if (state->config->tuner_sleep) {
- if (state->config->tuner_sleep(fe) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep",
- state->device == STV0900 ? "STV0900" : "STV0903",
- state->demod);
-
- mutex_lock(&state->internal->demod_lock);
-
- 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;
-}
-
-static int stv090x_wakeup(struct dvb_frontend *fe)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- u32 reg;
-
- 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;
-
- 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;
-}
-
-static void stv090x_release(struct dvb_frontend *fe)
-{
- struct stv090x_state *state = fe->demodulator_priv;
-
- state->internal->num_used--;
- if (state->internal->num_used <= 0) {
-
- dprintk(FE_ERROR, 1, "Actually removing");
-
- remove_dev(state->internal);
- kfree(state->internal);
- }
-
- kfree(state);
-}
-
-static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc_mode)
-{
- u32 reg = 0;
-
- reg = stv090x_read_reg(state, STV090x_GENCFG);
-
- switch (ldpc_mode) {
- case STV090x_DUAL:
- default:
- if ((state->demod_mode != STV090x_DUAL) || (STV090x_GETFIELD(reg, DDEMOD_FIELD) != 1)) {
- /* set LDPC to dual mode */
- if (stv090x_write_reg(state, STV090x_GENCFG, 0x1d) < 0)
- goto err;
-
- state->demod_mode = STV090x_DUAL;
-
- reg = stv090x_read_reg(state, STV090x_TSTRES0);
- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1);
- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
- goto err;
- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0);
- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0)
- goto err;
-
- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0)
- goto err;
- }
- break;
-
- case STV090x_SINGLE:
- if (stv090x_stop_modcod(state) < 0)
- goto err;
- if (stv090x_activate_modcod_single(state) < 0)
- goto err;
-
- if (state->demod == STV090x_DEMODULATOR_1) {
- if (stv090x_write_reg(state, STV090x_GENCFG, 0x06) < 0) /* path 2 */
- goto err;
- } else {
- if (stv090x_write_reg(state, STV090x_GENCFG, 0x04) < 0) /* path 1 */
- goto err;
- }
-
- reg = stv090x_read_reg(state, STV090x_TSTRES0);
- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1);
- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
- goto err;
- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0);
- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, PDELCTRL1);
- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x01);
- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x00);
- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0)
- goto err;
- break;
- }
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-/* return (Hz), clk in Hz*/
-static u32 stv090x_get_mclk(struct stv090x_state *state)
-{
- const struct stv090x_config *config = state->config;
- u32 div, reg;
- u8 ratio;
-
- div = stv090x_read_reg(state, STV090x_NCOARSE);
- reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
- ratio = STV090x_GETFIELD(reg, SELX1RATIO_FIELD) ? 4 : 6;
-
- return (div + 1) * config->xtal / ratio; /* kHz */
-}
-
-static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk)
-{
- const struct stv090x_config *config = state->config;
- u32 reg, div, clk_sel;
-
- reg = stv090x_read_reg(state, STV090x_SYNTCTRL);
- clk_sel = ((STV090x_GETFIELD(reg, SELX1RATIO_FIELD) == 1) ? 4 : 6);
-
- div = ((clk_sel * mclk) / config->xtal) - 1;
-
- reg = stv090x_read_reg(state, STV090x_NCOARSE);
- STV090x_SETFIELD(reg, M_DIV_FIELD, div);
- if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0)
- goto err;
-
- state->internal->mclk = stv090x_get_mclk(state);
-
- /*Set the DiseqC frequency to 22KHz */
- div = state->internal->mclk / 704000;
- if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0)
- goto err;
- if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0)
- goto err;
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_set_tspath(struct stv090x_state *state)
-{
- u32 reg;
-
- if (state->internal->dev_ver >= 0x20) {
- switch (state->config->ts1_mode) {
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- switch (state->config->ts2_mode) {
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- stv090x_write_reg(state, STV090x_TSGENERAL, 0x00);
- break;
-
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */
- goto err;
- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
- goto err;
- reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
- if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0)
- goto err;
- break;
- }
- break;
-
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- switch (state->config->ts2_mode) {
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0)
- goto err;
- break;
-
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0a) < 0)
- goto err;
- break;
- }
- break;
- }
- } else {
- switch (state->config->ts1_mode) {
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- switch (state->config->ts2_mode) {
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10);
- break;
-
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x16);
- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
- goto err;
- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 0);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0)
- goto err;
- break;
- }
- break;
-
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- switch (state->config->ts2_mode) {
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- default:
- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14);
- break;
-
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x12);
- break;
- }
- break;
- }
- }
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- default:
- break;
- }
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- 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)
- goto err;
- break;
-
- default:
- break;
- }
-
- if (state->config->ts1_clk > 0) {
- u32 speed;
-
- switch (state->config->ts1_mode) {
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- default:
- speed = state->internal->mclk /
- (state->config->ts1_clk / 4);
- if (speed < 0x08)
- speed = 0x08;
- if (speed > 0xFF)
- speed = 0xFF;
- break;
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- speed = state->internal->mclk /
- (state->config->ts1_clk / 32);
- if (speed < 0x20)
- speed = 0x20;
- if (speed > 0xFF)
- speed = 0xFF;
- break;
- }
- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0)
- goto err;
- }
-
- if (state->config->ts2_clk > 0) {
- u32 speed;
-
- switch (state->config->ts2_mode) {
- case STV090x_TSMODE_PARALLEL_PUNCTURED:
- case STV090x_TSMODE_DVBCI:
- default:
- speed = state->internal->mclk /
- (state->config->ts2_clk / 4);
- if (speed < 0x08)
- speed = 0x08;
- if (speed > 0xFF)
- speed = 0xFF;
- break;
- case STV090x_TSMODE_SERIAL_PUNCTURED:
- case STV090x_TSMODE_SERIAL_CONTINUOUS:
- speed = state->internal->mclk /
- (state->config->ts2_clk / 32);
- if (speed < 0x20)
- speed = 0x20;
- if (speed > 0xFF)
- speed = 0xFF;
- break;
- }
- reg = stv090x_read_reg(state, STV090x_P2_TSCFGM);
- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3);
- if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0)
- goto err;
- }
-
- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH);
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01);
- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00);
- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0)
- goto err;
-
- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH);
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
- goto err;
- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00);
- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0)
- goto err;
-
- return 0;
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_init(struct dvb_frontend *fe)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- const struct stv090x_config *config = state->config;
- u32 reg;
-
- if (state->internal->mclk == 0) {
- /* call tuner init to configure the tuner's clock output
- divider directly before setting up the master clock of
- the stv090x. */
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (config->tuner_init) {
- if (config->tuner_init(fe) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */
- msleep(5);
- if (stv090x_write_reg(state, STV090x_SYNTCTRL,
- 0x20 | config->clk_mode) < 0)
- goto err;
- stv090x_get_mclk(state);
- }
-
- if (stv090x_wakeup(fe) < 0) {
- dprintk(FE_ERROR, 1, "Error waking device");
- goto err;
- }
-
- if (stv090x_ldpc_mode(state, state->demod_mode) < 0)
- goto err;
-
- reg = STV090x_READ_DEMOD(state, TNRCFG2);
- STV090x_SETFIELD_Px(reg, TUN_IQSWAP_FIELD, state->inversion);
- if (STV090x_WRITE_DEMOD(state, TNRCFG2, reg) < 0)
- goto err;
- reg = STV090x_READ_DEMOD(state, DEMOD);
- STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff);
- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0)
- goto err;
-
- if (stv090x_i2c_gate_ctrl(state, 1) < 0)
- goto err;
-
- if (config->tuner_set_mode) {
- if (config->tuner_set_mode(fe, TUNER_WAKE) < 0)
- goto err_gateoff;
- }
-
- if (config->tuner_init) {
- if (config->tuner_init(fe) < 0)
- goto err_gateoff;
- }
-
- if (stv090x_i2c_gate_ctrl(state, 0) < 0)
- goto err;
-
- if (stv090x_set_tspath(state) < 0)
- goto err;
-
- return 0;
-
-err_gateoff:
- stv090x_i2c_gate_ctrl(state, 0);
-err:
- dprintk(FE_ERROR, 1, "I/O error");
- return -1;
-}
-
-static int stv090x_setup(struct dvb_frontend *fe)
-{
- struct stv090x_state *state = fe->demodulator_priv;
- const struct stv090x_config *config = state->config;
- const struct stv090x_reg *stv090x_initval = NULL;
- const struct stv090x_reg *stv090x_cut20_val = NULL;
- unsigned long t1_size = 0, t2_size = 0;
- u32 reg = 0;
-
- int i;
-
- if (state->device == STV0900) {
- dprintk(FE_DEBUG, 1, "Initializing STV0900");
- stv090x_initval = stv0900_initval;
- t1_size = ARRAY_SIZE(stv0900_initval);
- stv090x_cut20_val = stv0900_cut20_val;
- t2_size = ARRAY_SIZE(stv0900_cut20_val);
- } else if (state->device == STV0903) {
- dprintk(FE_DEBUG, 1, "Initializing STV0903");
- stv090x_initval = stv0903_initval;
- t1_size = ARRAY_SIZE(stv0903_initval);
- stv090x_cut20_val = stv0903_cut20_val;
- t2_size = ARRAY_SIZE(stv0903_cut20_val);
- }
-
- /* STV090x init */
-
- /* Stop Demod */
- if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0)
- goto err;
-
- msleep(5);
-
- /* Set No Tuner Mode */
- if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0)
- goto err;
-
- /* I2C repeater OFF */
- STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level);
- if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0)
- goto err;
-
- if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */
- goto err;
- msleep(5);
- if (stv090x_write_reg(state, STV090x_I2CCFG, 0x08) < 0) /* 1/41 oversampling */
- goto err;
- if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) /* enable PLL */
- goto err;
- msleep(5);
-
- /* write initval */
- dprintk(FE_DEBUG, 1, "Setting up initial values");
- for (i = 0; i < t1_size; i++) {
- if (stv090x_write_reg(state, stv090x_initval[i].addr, stv090x_initval[i].data) < 0)
- goto err;
- }
-
- state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID);
- if (state->internal->dev_ver >= 0x20) {
- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0)
- goto err;
-
- /* write cut20_val*/
- dprintk(FE_DEBUG, 1, "Setting up Cut 2.0 initial values");
- for (i = 0; i < t2_size; i++) {
- if (stv090x_write_reg(state, stv090x_cut20_val[i].addr, stv090x_cut20_val[i].data) < 0)
- goto err;
- }
-
- } else if (state->internal->dev_ver < 0x20) {
- dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!",
- state->internal->dev_ver);
-
- goto err;
- } else if (state->internal->dev_ver > 0x30) {
- /* we shouldn't bail out from here */
- dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!",
- state->internal->dev_ver);
- }
-
- /* ADC1 range */
- reg = stv090x_read_reg(state, STV090x_TSTTNR1);
- STV090x_SETFIELD(reg, ADC1_INMODE_FIELD,
- (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1);
- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0)
- goto err;
-
- /* ADC2 range */
- reg = stv090x_read_reg(state, STV090x_TSTTNR3);
- STV090x_SETFIELD(reg, ADC2_INMODE_FIELD,
- (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1);
- if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0)
- goto err;
-
- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0)
- goto err;
- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0)
- goto err;
-
- 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 = {
- .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS },
- .info = {
- .name = "STV090x Multistandard",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 0,
- .frequency_tolerance = 0,
- .symbol_rate_min = 1000000,
- .symbol_rate_max = 45000000,
- .caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_2G_MODULATION
- },
-
- .release = stv090x_release,
- .init = stv090x_init,
-
- .sleep = stv090x_sleep,
- .get_frontend_algo = stv090x_frontend_algo,
-
- .diseqc_send_master_cmd = stv090x_send_diseqc_msg,
- .diseqc_send_burst = stv090x_send_diseqc_burst,
- .diseqc_recv_slave_reply = stv090x_recv_slave_reply,
- .set_tone = stv090x_set_tone,
-
- .search = stv090x_search,
- .read_status = stv090x_read_status,
- .read_ber = stv090x_read_per,
- .read_signal_strength = stv090x_read_signal_strength,
- .read_snr = stv090x_read_cnr,
-};
-
-
-struct dvb_frontend *stv090x_attach(const struct stv090x_config *config,
- struct i2c_adapter *i2c,
- enum stv090x_demodulator demod)
-{
- struct stv090x_state *state = NULL;
- struct stv090x_dev *temp_int;
-
- state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
-
- state->verbose = &verbose;
- state->config = config;
- state->i2c = i2c;
- state->frontend.ops = stv090x_ops;
- state->frontend.demodulator_priv = state;
- state->demod = demod;
- state->demod_mode = config->demod_mode; /* Single or Dual mode */
- state->device = config->device;
- state->rolloff = STV090x_RO_35; /* default */
-
- temp_int = find_dev(state->i2c,
- state->config->address);
-
- if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) {
- state->internal = temp_int->internal;
- state->internal->num_used++;
- dprintk(FE_INFO, 1, "Found Internal Structure!");
- } 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);
-
- if (stv090x_setup(&state->frontend) < 0) {
- dprintk(FE_ERROR, 1, "Error setting up device");
- goto err_remove;
- }
- }
-
- /* 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",
- demod,
- state->internal->dev_ver);
-
- return &state->frontend;
-
-err_remove:
- remove_dev(state->internal);
- kfree(state->internal);
-error:
- kfree(state);
- return NULL;
-}
-EXPORT_SYMBOL(stv090x_attach);
-MODULE_PARM_DESC(verbose, "Set Verbosity level");
-MODULE_AUTHOR("Manu Abraham");
-MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
deleted file mode 100644
index 35d72b46aa1e..000000000000
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ /dev/null
@@ -1,1381 +0,0 @@
- /*
- Driver for Philips tda1004xh OFDM Demodulator
-
- (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
- GNU General Public License for more details.
-
- You 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 driver needs external firmware. Please use the commands
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10045",
- * "<kerneldir>/Documentation/dvb/get_dvb_firmware tda10046" to
- * download/extract them, and then copy them to /usr/lib/hotplug/firmware
- * or /lib/firmware (depending on configuration of firmware hotplug).
- */
-#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw"
-#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw"
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/jiffies.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-
-#include "dvb_frontend.h"
-#include "tda1004x.h"
-
-static int debug;
-#define dprintk(args...) \
- do { \
- if (debug) printk(KERN_DEBUG "tda1004x: " args); \
- } while (0)
-
-#define TDA1004X_CHIPID 0x00
-#define TDA1004X_AUTO 0x01
-#define TDA1004X_IN_CONF1 0x02
-#define TDA1004X_IN_CONF2 0x03
-#define TDA1004X_OUT_CONF1 0x04
-#define TDA1004X_OUT_CONF2 0x05
-#define TDA1004X_STATUS_CD 0x06
-#define TDA1004X_CONFC4 0x07
-#define TDA1004X_DSSPARE2 0x0C
-#define TDA10045H_CODE_IN 0x0D
-#define TDA10045H_FWPAGE 0x0E
-#define TDA1004X_SCAN_CPT 0x10
-#define TDA1004X_DSP_CMD 0x11
-#define TDA1004X_DSP_ARG 0x12
-#define TDA1004X_DSP_DATA1 0x13
-#define TDA1004X_DSP_DATA2 0x14
-#define TDA1004X_CONFADC1 0x15
-#define TDA1004X_CONFC1 0x16
-#define TDA10045H_S_AGC 0x1a
-#define TDA10046H_AGC_TUN_LEVEL 0x1a
-#define TDA1004X_SNR 0x1c
-#define TDA1004X_CONF_TS1 0x1e
-#define TDA1004X_CONF_TS2 0x1f
-#define TDA1004X_CBER_RESET 0x20
-#define TDA1004X_CBER_MSB 0x21
-#define TDA1004X_CBER_LSB 0x22
-#define TDA1004X_CVBER_LUT 0x23
-#define TDA1004X_VBER_MSB 0x24
-#define TDA1004X_VBER_MID 0x25
-#define TDA1004X_VBER_LSB 0x26
-#define TDA1004X_UNCOR 0x27
-
-#define TDA10045H_CONFPLL_P 0x2D
-#define TDA10045H_CONFPLL_M_MSB 0x2E
-#define TDA10045H_CONFPLL_M_LSB 0x2F
-#define TDA10045H_CONFPLL_N 0x30
-
-#define TDA10046H_CONFPLL1 0x2D
-#define TDA10046H_CONFPLL2 0x2F
-#define TDA10046H_CONFPLL3 0x30
-#define TDA10046H_TIME_WREF1 0x31
-#define TDA10046H_TIME_WREF2 0x32
-#define TDA10046H_TIME_WREF3 0x33
-#define TDA10046H_TIME_WREF4 0x34
-#define TDA10046H_TIME_WREF5 0x35
-
-#define TDA10045H_UNSURW_MSB 0x31
-#define TDA10045H_UNSURW_LSB 0x32
-#define TDA10045H_WREF_MSB 0x33
-#define TDA10045H_WREF_MID 0x34
-#define TDA10045H_WREF_LSB 0x35
-#define TDA10045H_MUXOUT 0x36
-#define TDA1004X_CONFADC2 0x37
-
-#define TDA10045H_IOFFSET 0x38
-
-#define TDA10046H_CONF_TRISTATE1 0x3B
-#define TDA10046H_CONF_TRISTATE2 0x3C
-#define TDA10046H_CONF_POLARITY 0x3D
-#define TDA10046H_FREQ_OFFSET 0x3E
-#define TDA10046H_GPIO_OUT_SEL 0x41
-#define TDA10046H_GPIO_SELECT 0x42
-#define TDA10046H_AGC_CONF 0x43
-#define TDA10046H_AGC_THR 0x44
-#define TDA10046H_AGC_RENORM 0x45
-#define TDA10046H_AGC_GAINS 0x46
-#define TDA10046H_AGC_TUN_MIN 0x47
-#define TDA10046H_AGC_TUN_MAX 0x48
-#define TDA10046H_AGC_IF_MIN 0x49
-#define TDA10046H_AGC_IF_MAX 0x4A
-
-#define TDA10046H_FREQ_PHY2_MSB 0x4D
-#define TDA10046H_FREQ_PHY2_LSB 0x4E
-
-#define TDA10046H_CVBER_CTRL 0x4F
-#define TDA10046H_AGC_IF_LEVEL 0x52
-#define TDA10046H_CODE_CPT 0x57
-#define TDA10046H_CODE_IN 0x58
-
-
-static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data)
-{
- int ret;
- u8 buf[] = { reg, data };
- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 };
-
- dprintk("%s: reg=0x%x, data=0x%x\n", __func__, reg, data);
-
- msg.addr = state->config->demod_address;
- ret = i2c_transfer(state->i2c, &msg, 1);
-
- if (ret != 1)
- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
- __func__, reg, data, ret);
-
- dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__,
- reg, data, ret);
- return (ret != 1) ? -1 : 0;
-}
-
-static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
-{
- int ret;
- u8 b0[] = { reg };
- u8 b1[] = { 0 };
- struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },
- { .flags = I2C_M_RD, .buf = b1, .len = 1 }};
-
- dprintk("%s: reg=0x%x\n", __func__, reg);
-
- msg[0].addr = state->config->demod_address;
- msg[1].addr = state->config->demod_address;
- ret = i2c_transfer(state->i2c, msg, 2);
-
- if (ret != 2) {
- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg,
- ret);
- return -EINVAL;
- }
-
- dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__,
- reg, b1[0], ret);
- return b1[0];
-}
-
-static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data)
-{
- int val;
- dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __func__, reg,
- mask, data);
-
- // read a byte and check
- val = tda1004x_read_byte(state, reg);
- if (val < 0)
- return val;
-
- // mask if off
- val = val & ~mask;
- val |= data & 0xff;
-
- // write it out again
- return tda1004x_write_byteI(state, reg, val);
-}
-
-static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len)
-{
- int i;
- int result;
-
- dprintk("%s: reg=0x%x, len=0x%x\n", __func__, reg, len);
-
- result = 0;
- for (i = 0; i < len; i++) {
- result = tda1004x_write_byteI(state, reg + i, buf[i]);
- if (result != 0)
- break;
- }
-
- return result;
-}
-
-static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state)
-{
- int result;
- dprintk("%s\n", __func__);
-
- result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2);
- msleep(20);
- return result;
-}
-
-static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state)
-{
- dprintk("%s\n", __func__);
-
- return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0);
-}
-
-static int tda10045h_set_bandwidth(struct tda1004x_state *state,
- u32 bandwidth)
-{
- static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f };
- static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb };
- static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 };
-
- switch (bandwidth) {
- case 6000000:
- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz));
- break;
-
- case 7000000:
- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz));
- break;
-
- case 8000000:
- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz));
- break;
-
- default:
- return -EINVAL;
- }
-
- tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0);
-
- return 0;
-}
-
-static int tda10046h_set_bandwidth(struct tda1004x_state *state,
- u32 bandwidth)
-{
- static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 };
- static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f };
- static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d };
-
- static u8 bandwidth_6mhz_48M[] = { 0x70, 0x02, 0x49, 0x24, 0x92 };
- static u8 bandwidth_7mhz_48M[] = { 0x60, 0x02, 0xaa, 0xaa, 0xab };
- static u8 bandwidth_8mhz_48M[] = { 0x54, 0x03, 0x0c, 0x30, 0xc3 };
- int tda10046_clk53m;
-
- if ((state->config->if_freq == TDA10046_FREQ_045) ||
- (state->config->if_freq == TDA10046_FREQ_052))
- tda10046_clk53m = 0;
- else
- tda10046_clk53m = 1;
- switch (bandwidth) {
- case 6000000:
- if (tda10046_clk53m)
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_53M,
- sizeof(bandwidth_6mhz_53M));
- else
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_48M,
- sizeof(bandwidth_6mhz_48M));
- if (state->config->if_freq == TDA10046_FREQ_045) {
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xab);
- }
- break;
-
- case 7000000:
- if (tda10046_clk53m)
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_53M,
- sizeof(bandwidth_7mhz_53M));
- else
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_48M,
- sizeof(bandwidth_7mhz_48M));
- if (state->config->if_freq == TDA10046_FREQ_045) {
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00);
- }
- break;
-
- case 8000000:
- if (tda10046_clk53m)
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_53M,
- sizeof(bandwidth_8mhz_53M));
- else
- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_48M,
- sizeof(bandwidth_8mhz_48M));
- if (state->config->if_freq == TDA10046_FREQ_045) {
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x55);
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tda1004x_do_upload(struct tda1004x_state *state,
- const unsigned char *mem, unsigned int len,
- u8 dspCodeCounterReg, u8 dspCodeInReg)
-{
- u8 buf[65];
- struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 };
- int tx_size;
- int pos = 0;
-
- /* clear code counter */
- tda1004x_write_byteI(state, dspCodeCounterReg, 0);
- fw_msg.addr = state->config->demod_address;
-
- buf[0] = dspCodeInReg;
- while (pos != len) {
- // work out how much to send this time
- tx_size = len - pos;
- if (tx_size > 0x10)
- tx_size = 0x10;
-
- // send the chunk
- memcpy(buf + 1, mem + pos, tx_size);
- fw_msg.len = tx_size + 1;
- if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) {
- printk(KERN_ERR "tda1004x: Error during firmware upload\n");
- return -EIO;
- }
- pos += tx_size;
-
- dprintk("%s: fw_pos=0x%x\n", __func__, pos);
- }
- // give the DSP a chance to settle 03/10/05 Hac
- msleep(100);
-
- return 0;
-}
-
-static int tda1004x_check_upload_ok(struct tda1004x_state *state)
-{
- u8 data1, data2;
- unsigned long timeout;
-
- if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
- timeout = jiffies + 2 * HZ;
- while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) {
- if (time_after(jiffies, timeout)) {
- printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n");
- break;
- }
- msleep(1);
- }
- } else
- msleep(100);
-
- // check upload was OK
- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP
- tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67);
-
- data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1);
- data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2);
- if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) {
- printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2);
- return -EIO;
- }
- printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2);
- return 0;
-}
-
-static int tda10045_fwupload(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int ret;
- const struct firmware *fw;
-
- /* don't re-upload unless necessary */
- if (tda1004x_check_upload_ok(state) == 0)
- return 0;
-
- /* request the firmware, this will block until someone uploads it */
- printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE);
- ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
- if (ret) {
- printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
- return ret;
- }
-
- /* reset chip */
- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0);
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
- msleep(10);
-
- /* set parameters */
- tda10045h_set_bandwidth(state, 8000000);
-
- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN);
- release_firmware(fw);
- if (ret)
- return ret;
- printk(KERN_INFO "tda1004x: firmware upload complete\n");
-
- /* wait for DSP to initialise */
- /* DSPREADY doesn't seem to work on the TDA10045H */
- msleep(100);
-
- return tda1004x_check_upload_ok(state);
-}
-
-static void tda10046_init_plls(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int tda10046_clk53m;
-
- if ((state->config->if_freq == TDA10046_FREQ_045) ||
- (state->config->if_freq == TDA10046_FREQ_052))
- tda10046_clk53m = 0;
- else
- tda10046_clk53m = 1;
-
- tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0);
- if(tda10046_clk53m) {
- printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n");
- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8
- } else {
- printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n");
- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3
- }
- if (state->config->xtal_freq == TDA10046_XTAL_4M ) {
- dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __func__);
- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0
- } else {
- dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __func__);
- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3
- }
- if(tda10046_clk53m)
- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x67);
- else
- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x72);
- /* Note clock frequency is handled implicitly */
- switch (state->config->if_freq) {
- case TDA10046_FREQ_045:
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00);
- break;
- case TDA10046_FREQ_052:
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xc7);
- break;
- case TDA10046_FREQ_3617:
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x59);
- break;
- case TDA10046_FREQ_3613:
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7);
- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x3f);
- break;
- }
- tda10046h_set_bandwidth(state, 8000000); /* default bandwidth 8 MHz */
- /* let the PLLs settle */
- msleep(120);
-}
-
-static int tda10046_fwupload(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int ret, confc4;
- const struct firmware *fw;
-
- /* reset + wake up chip */
- if (state->config->xtal_freq == TDA10046_XTAL_4M) {
- confc4 = 0;
- } else {
- dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__);
- confc4 = 0x80;
- }
- tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
-
- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
- /* set GPIO 1 and 3 */
- if (state->config->gpio_config != TDA10046_GPTRI) {
- tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33);
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f);
- }
- /* let the clocks recover from sleep */
- msleep(10);
-
- /* The PLLs need to be reprogrammed after sleep */
- tda10046_init_plls(fe);
- tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0);
-
- /* don't re-upload unless necessary */
- if (tda1004x_check_upload_ok(state) == 0)
- return 0;
-
- /*
- For i2c normal work, we need to slow down the bus speed.
- However, the slow down breaks the eeprom firmware load.
- So, use normal speed for eeprom booting and then restore the
- i2c speed after that. Tested with MSI TV @nyware A/D board,
- that comes with firmware version 29 inside their eeprom.
-
- It should also be noticed that no other I2C transfer should
- be in course while booting from eeprom, otherwise, tda10046
- goes into an instable state. So, proper locking are needed
- at the i2c bus master.
- */
- printk(KERN_INFO "tda1004x: trying to boot from eeprom\n");
- tda1004x_write_byteI(state, TDA1004X_CONFC4, 4);
- msleep(300);
- tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
-
- /* Checks if eeprom firmware went without troubles */
- if (tda1004x_check_upload_ok(state) == 0)
- return 0;
-
- /* eeprom firmware didn't work. Load one manually. */
-
- if (state->config->request_firmware != NULL) {
- /* request the firmware, this will block until someone uploads it */
- printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
- ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE);
- if (ret) {
- /* remain compatible to old bug: try to load with tda10045 image name */
- ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE);
- if (ret) {
- printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n");
- return ret;
- } else {
- printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n",
- TDA10046_DEFAULT_FIRMWARE);
- }
- }
- } else {
- printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n");
- return -EIO;
- }
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST
- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN);
- release_firmware(fw);
- return tda1004x_check_upload_ok(state);
-}
-
-static int tda1004x_encode_fec(int fec)
-{
- // convert known FEC values
- switch (fec) {
- case FEC_1_2:
- return 0;
- case FEC_2_3:
- return 1;
- case FEC_3_4:
- return 2;
- case FEC_5_6:
- return 3;
- case FEC_7_8:
- return 4;
- }
-
- // unsupported
- return -EINVAL;
-}
-
-static int tda1004x_decode_fec(int tdafec)
-{
- // convert known FEC values
- switch (tdafec) {
- case 0:
- return FEC_1_2;
- case 1:
- return FEC_2_3;
- case 2:
- return FEC_3_4;
- case 3:
- return FEC_5_6;
- case 4:
- return FEC_7_8;
- }
-
- // unsupported
- return -1;
-}
-
-static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
-
- if (len != 2)
- return -EINVAL;
-
- return tda1004x_write_byteI(state, buf[0], buf[1]);
-}
-
-static int tda10045_init(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
-
- dprintk("%s\n", __func__);
-
- if (tda10045_fwupload(fe)) {
- printk("tda1004x: firmware upload failed\n");
- return -EIO;
- }
-
- tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC
-
- // tda setup
- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
- tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer
- tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset
- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset
- tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface
- tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface
- tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity
- tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e);
-
- tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk);
-
- return 0;
-}
-
-static int tda10046_init(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- dprintk("%s\n", __func__);
-
- if (tda10046_fwupload(fe)) {
- printk("tda1004x: firmware upload failed\n");
- return -EIO;
- }
-
- // tda setup
- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer
- tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream
- tda1004x_write_byteI(state, TDA1004X_CONFC1, 0x88); // enable pulse killer
-
- switch (state->config->agc_config) {
- case TDA10046_AGC_DEFAULT:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
- break;
- case TDA10046_AGC_IFO_AUTO_NEG:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
- break;
- case TDA10046_AGC_IFO_AUTO_POS:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities
- break;
- case TDA10046_AGC_TDA827X:
- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup
- tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold
- tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities
- break;
- }
- if (state->config->ts_mode == 0) {
- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40);
- tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7);
- } else {
- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80);
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10,
- state->config->invert_oclk << 4);
- }
- tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38);
- tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on
- tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // }
- tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values
- tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // }
- tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // }
- tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 0x12); // IF gain 2, TUN gain 1
- tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits
- tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config
- tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config
- // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes
-
- return 0;
-}
-
-static int tda1004x_set_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
- struct tda1004x_state* state = fe->demodulator_priv;
- int tmp;
- int inversion;
-
- dprintk("%s\n", __func__);
-
- if (state->demod_type == TDA1004X_DEMOD_TDA10046) {
- // setup auto offset
- tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0);
-
- // disable agc_conf[2]
- tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0);
- }
-
- // set frequency
- if (fe->ops.tuner_ops.set_params) {
- fe->ops.tuner_ops.set_params(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
-
- // Hardcoded to use auto as much as possible on the TDA10045 as it
- // is very unreliable if AUTO mode is _not_ used.
- if (state->demod_type == TDA1004X_DEMOD_TDA10045) {
- fe_params->code_rate_HP = FEC_AUTO;
- fe_params->guard_interval = GUARD_INTERVAL_AUTO;
- fe_params->transmission_mode = TRANSMISSION_MODE_AUTO;
- }
-
- // Set standard params.. or put them to auto
- if ((fe_params->code_rate_HP == FEC_AUTO) ||
- (fe_params->code_rate_LP == FEC_AUTO) ||
- (fe_params->modulation == QAM_AUTO) ||
- (fe_params->hierarchy == HIERARCHY_AUTO)) {
- tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); /* turn off modulation bits */
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits
- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits
- } else {
- tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto
-
- // set HP FEC
- tmp = tda1004x_encode_fec(fe_params->code_rate_HP);
- if (tmp < 0)
- return tmp;
- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp);
-
- // set LP FEC
- tmp = tda1004x_encode_fec(fe_params->code_rate_LP);
- if (tmp < 0)
- return tmp;
- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3);
-
- /* set modulation */
- switch (fe_params->modulation) {
- case QPSK:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0);
- break;
-
- case QAM_16:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1);
- break;
-
- case QAM_64:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2);
- break;
-
- default:
- return -EINVAL;
- }
-
- // set hierarchy
- switch (fe_params->hierarchy) {
- case HIERARCHY_NONE:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5);
- break;
-
- case HIERARCHY_1:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5);
- break;
-
- case HIERARCHY_2:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5);
- break;
-
- case HIERARCHY_4:
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5);
- break;
-
- default:
- return -EINVAL;
- }
- }
-
- // set bandwidth
- switch (state->demod_type) {
- case TDA1004X_DEMOD_TDA10045:
- tda10045h_set_bandwidth(state, fe_params->bandwidth_hz);
- break;
-
- case TDA1004X_DEMOD_TDA10046:
- tda10046h_set_bandwidth(state, fe_params->bandwidth_hz);
- break;
- }
-
- // set inversion
- inversion = fe_params->inversion;
- if (state->config->invert)
- inversion = inversion ? INVERSION_OFF : INVERSION_ON;
- switch (inversion) {
- case INVERSION_OFF:
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0);
- break;
-
- case INVERSION_ON:
- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20);
- break;
-
- default:
- return -EINVAL;
- }
-
- // set guard interval
- switch (fe_params->guard_interval) {
- case GUARD_INTERVAL_1_32:
- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
- break;
-
- case GUARD_INTERVAL_1_16:
- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2);
- break;
-
- case GUARD_INTERVAL_1_8:
- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2);
- break;
-
- case GUARD_INTERVAL_1_4:
- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2);
- break;
-
- case GUARD_INTERVAL_AUTO:
- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2);
- break;
-
- default:
- return -EINVAL;
- }
-
- // set transmission mode
- switch (fe_params->transmission_mode) {
- case TRANSMISSION_MODE_2K:
- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4);
- break;
-
- case TRANSMISSION_MODE_8K:
- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4);
- break;
-
- case TRANSMISSION_MODE_AUTO:
- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4);
- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0);
- break;
-
- default:
- return -EINVAL;
- }
-
- // start the lock
- switch (state->demod_type) {
- case TDA1004X_DEMOD_TDA10045:
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8);
- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0);
- break;
-
- case TDA1004X_DEMOD_TDA10046:
- tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40);
- msleep(1);
- tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 1);
- break;
- }
-
- msleep(10);
-
- return 0;
-}
-
-static int tda1004x_get_fe(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache;
- struct tda1004x_state* state = fe->demodulator_priv;
-
- dprintk("%s\n", __func__);
-
- // inversion status
- fe_params->inversion = INVERSION_OFF;
- if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20)
- fe_params->inversion = INVERSION_ON;
- if (state->config->invert)
- fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON;
-
- // bandwidth
- switch (state->demod_type) {
- case TDA1004X_DEMOD_TDA10045:
- switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) {
- case 0x14:
- fe_params->bandwidth_hz = 8000000;
- break;
- case 0xdb:
- fe_params->bandwidth_hz = 7000000;
- break;
- case 0x4f:
- fe_params->bandwidth_hz = 6000000;
- break;
- }
- break;
- case TDA1004X_DEMOD_TDA10046:
- switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) {
- case 0x5c:
- case 0x54:
- fe_params->bandwidth_hz = 8000000;
- break;
- case 0x6a:
- case 0x60:
- fe_params->bandwidth_hz = 7000000;
- break;
- case 0x7b:
- case 0x70:
- fe_params->bandwidth_hz = 6000000;
- break;
- }
- break;
- }
-
- // FEC
- fe_params->code_rate_HP =
- tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7);
- fe_params->code_rate_LP =
- tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7);
-
- /* modulation */
- switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) {
- case 0:
- fe_params->modulation = QPSK;
- break;
- case 1:
- fe_params->modulation = QAM_16;
- break;
- case 2:
- fe_params->modulation = QAM_64;
- break;
- }
-
- // transmission mode
- fe_params->transmission_mode = TRANSMISSION_MODE_2K;
- if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10)
- fe_params->transmission_mode = TRANSMISSION_MODE_8K;
-
- // guard interval
- switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) {
- case 0:
- fe_params->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case 1:
- fe_params->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case 2:
- fe_params->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case 3:
- fe_params->guard_interval = GUARD_INTERVAL_1_4;
- break;
- }
-
- // hierarchy
- switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) {
- case 0:
- fe_params->hierarchy = HIERARCHY_NONE;
- break;
- case 1:
- fe_params->hierarchy = HIERARCHY_1;
- break;
- case 2:
- fe_params->hierarchy = HIERARCHY_2;
- break;
- case 3:
- fe_params->hierarchy = HIERARCHY_4;
- break;
- }
-
- return 0;
-}
-
-static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int status;
- int cber;
- int vber;
-
- dprintk("%s\n", __func__);
-
- // read status
- status = tda1004x_read_byte(state, TDA1004X_STATUS_CD);
- if (status == -1)
- return -EIO;
-
- // decode
- *fe_status = 0;
- if (status & 4)
- *fe_status |= FE_HAS_SIGNAL;
- if (status & 2)
- *fe_status |= FE_HAS_CARRIER;
- if (status & 8)
- *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
-
- // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi
- // is getting anything valid
- if (!(*fe_status & FE_HAS_VITERBI)) {
- // read the CBER
- cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
- if (cber == -1)
- return -EIO;
- status = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
- if (status == -1)
- return -EIO;
- cber |= (status << 8);
- // The address 0x20 should be read to cope with a TDA10046 bug
- tda1004x_read_byte(state, TDA1004X_CBER_RESET);
-
- if (cber != 65535)
- *fe_status |= FE_HAS_VITERBI;
- }
-
- // if we DO have some valid VITERBI output, but don't already have SYNC
- // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid.
- if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) {
- // read the VBER
- vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB);
- if (vber == -1)
- return -EIO;
- status = tda1004x_read_byte(state, TDA1004X_VBER_MID);
- if (status == -1)
- return -EIO;
- vber |= (status << 8);
- status = tda1004x_read_byte(state, TDA1004X_VBER_MSB);
- if (status == -1)
- return -EIO;
- vber |= (status & 0x0f) << 16;
- // The CVBER_LUT should be read to cope with TDA10046 hardware bug
- tda1004x_read_byte(state, TDA1004X_CVBER_LUT);
-
- // if RS has passed some valid TS packets, then we must be
- // getting some SYNC bytes
- if (vber < 16632)
- *fe_status |= FE_HAS_SYNC;
- }
-
- // success
- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status);
- return 0;
-}
-
-static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int tmp;
- int reg = 0;
-
- dprintk("%s\n", __func__);
-
- // determine the register to use
- switch (state->demod_type) {
- case TDA1004X_DEMOD_TDA10045:
- reg = TDA10045H_S_AGC;
- break;
-
- case TDA1004X_DEMOD_TDA10046:
- reg = TDA10046H_AGC_IF_LEVEL;
- break;
- }
-
- // read it
- tmp = tda1004x_read_byte(state, reg);
- if (tmp < 0)
- return -EIO;
-
- *signal = (tmp << 8) | tmp;
- dprintk("%s: signal=0x%x\n", __func__, *signal);
- return 0;
-}
-
-static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int tmp;
-
- dprintk("%s\n", __func__);
-
- // read it
- tmp = tda1004x_read_byte(state, TDA1004X_SNR);
- if (tmp < 0)
- return -EIO;
- tmp = 255 - tmp;
-
- *snr = ((tmp << 8) | tmp);
- dprintk("%s: snr=0x%x\n", __func__, *snr);
- return 0;
-}
-
-static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int tmp;
- int tmp2;
- int counter;
-
- dprintk("%s\n", __func__);
-
- // read the UCBLOCKS and reset
- counter = 0;
- tmp = tda1004x_read_byte(state, TDA1004X_UNCOR);
- if (tmp < 0)
- return -EIO;
- tmp &= 0x7f;
- while (counter++ < 5) {
- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0);
-
- tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR);
- if (tmp2 < 0)
- return -EIO;
- tmp2 &= 0x7f;
- if ((tmp2 < tmp) || (tmp2 == 0))
- break;
- }
-
- if (tmp != 0x7f)
- *ucblocks = tmp;
- else
- *ucblocks = 0xffffffff;
-
- dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks);
- return 0;
-}
-
-static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int tmp;
-
- dprintk("%s\n", __func__);
-
- // read it in
- tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB);
- if (tmp < 0)
- return -EIO;
- *ber = tmp << 1;
- tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB);
- if (tmp < 0)
- return -EIO;
- *ber |= (tmp << 9);
- // The address 0x20 should be read to cope with a TDA10046 bug
- tda1004x_read_byte(state, TDA1004X_CBER_RESET);
-
- dprintk("%s: ber=0x%x\n", __func__, *ber);
- return 0;
-}
-
-static int tda1004x_sleep(struct dvb_frontend* fe)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
- int gpio_conf;
-
- switch (state->demod_type) {
- case TDA1004X_DEMOD_TDA10045:
- tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10);
- break;
-
- case TDA1004X_DEMOD_TDA10046:
- /* set outputs to tristate */
- tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff);
- /* invert GPIO 1 and 3 if desired*/
- gpio_conf = state->config->gpio_config;
- if (gpio_conf >= TDA10046_GP00_I)
- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f,
- (gpio_conf & 0x0f) ^ 0x0a);
-
- tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0);
- tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1);
- break;
- }
-
- return 0;
-}
-
-static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
-{
- struct tda1004x_state* state = fe->demodulator_priv;
-
- if (enable) {
- return tda1004x_enable_tuner_i2c(state);
- } else {
- return tda1004x_disable_tuner_i2c(state);
- }
-}
-
-static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
-{
- fesettings->min_delay_ms = 800;
- /* Drift compensation makes no sense for DVB-T */
- fesettings->step_size = 0;
- fesettings->max_drift = 0;
- return 0;
-}
-
-static void tda1004x_release(struct dvb_frontend* fe)
-{
- struct tda1004x_state *state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops tda10045_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Philips TDA10045H DVB-T",
- .frequency_min = 51000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 166667,
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
- },
-
- .release = tda1004x_release,
-
- .init = tda10045_init,
- .sleep = tda1004x_sleep,
- .write = tda1004x_write,
- .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
-
- .set_frontend = tda1004x_set_fe,
- .get_frontend = tda1004x_get_fe,
- .get_tune_settings = tda1004x_get_tune_settings,
-
- .read_status = tda1004x_read_status,
- .read_ber = tda1004x_read_ber,
- .read_signal_strength = tda1004x_read_signal_strength,
- .read_snr = tda1004x_read_snr,
- .read_ucblocks = tda1004x_read_ucblocks,
-};
-
-struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
- struct i2c_adapter* i2c)
-{
- struct tda1004x_state *state;
- int id;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
- if (!state) {
- printk(KERN_ERR "Can't allocate memory for tda10045 state\n");
- return NULL;
- }
-
- /* setup the state */
- state->config = config;
- state->i2c = i2c;
- state->demod_type = TDA1004X_DEMOD_TDA10045;
-
- /* check if the demod is there */
- id = tda1004x_read_byte(state, TDA1004X_CHIPID);
- if (id < 0) {
- printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n");
- kfree(state);
- return NULL;
- }
-
- if (id != 0x25) {
- printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id);
- kfree(state);
- return NULL;
- }
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &tda10045_ops, sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
- return &state->frontend;
-}
-
-static struct dvb_frontend_ops tda10046_ops = {
- .delsys = { SYS_DVBT },
- .info = {
- .name = "Philips TDA10046H DVB-T",
- .frequency_min = 51000000,
- .frequency_max = 858000000,
- .frequency_stepsize = 166667,
- .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_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO
- },
-
- .release = tda1004x_release,
-
- .init = tda10046_init,
- .sleep = tda1004x_sleep,
- .write = tda1004x_write,
- .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
-
- .set_frontend = tda1004x_set_fe,
- .get_frontend = tda1004x_get_fe,
- .get_tune_settings = tda1004x_get_tune_settings,
-
- .read_status = tda1004x_read_status,
- .read_ber = tda1004x_read_ber,
- .read_signal_strength = tda1004x_read_signal_strength,
- .read_snr = tda1004x_read_snr,
- .read_ucblocks = tda1004x_read_ucblocks,
-};
-
-struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
- struct i2c_adapter* i2c)
-{
- struct tda1004x_state *state;
- int id;
-
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
- if (!state) {
- printk(KERN_ERR "Can't allocate memory for tda10046 state\n");
- return NULL;
- }
-
- /* setup the state */
- state->config = config;
- state->i2c = i2c;
- state->demod_type = TDA1004X_DEMOD_TDA10046;
-
- /* check if the demod is there */
- id = tda1004x_read_byte(state, TDA1004X_CHIPID);
- if (id < 0) {
- printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n");
- kfree(state);
- return NULL;
- }
- if (id != 0x46) {
- printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id);
- kfree(state);
- return NULL;
- }
-
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &tda10046_ops, sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
- return &state->frontend;
-}
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
-
-MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator");
-MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(tda10045_attach);
-EXPORT_SYMBOL(tda10046_attach);
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
deleted file mode 100644
index 703c3d05f9f4..000000000000
--- a/drivers/media/dvb/frontends/tda10071.c
+++ /dev/null
@@ -1,1284 +0,0 @@
-/*
- * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "tda10071_priv.h"
-
-static struct dvb_frontend_ops tda10071_ops;
-
-/* write multiple registers */
-static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len+1];
- struct i2c_msg msg[1] = {
- {
- .addr = priv->cfg.i2c_address,
- .flags = 0,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- buf[0] = reg;
- memcpy(&buf[1], val, len);
-
- ret = i2c_transfer(priv->i2c, msg, 1);
- if (ret == 1) {
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* read multiple registers */
-static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val,
- int len)
-{
- int ret;
- u8 buf[len];
- struct i2c_msg msg[2] = {
- {
- .addr = priv->cfg.i2c_address,
- .flags = 0,
- .len = 1,
- .buf = &reg,
- }, {
- .addr = priv->cfg.i2c_address,
- .flags = I2C_M_RD,
- .len = sizeof(buf),
- .buf = buf,
- }
- };
-
- ret = i2c_transfer(priv->i2c, msg, 2);
- if (ret == 2) {
- memcpy(val, buf, len);
- ret = 0;
- } else {
- dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
- "len=%d\n", KBUILD_MODNAME, ret, reg, len);
- ret = -EREMOTEIO;
- }
- return ret;
-}
-
-/* write single register */
-static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val)
-{
- return tda10071_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register */
-static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val)
-{
- return tda10071_rd_regs(priv, reg, val, 1);
-}
-
-/* write single register with mask */
-int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask)
-{
- int ret;
- u8 tmp;
-
- /* no need for read if whole reg is written */
- if (mask != 0xff) {
- ret = tda10071_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- val &= mask;
- tmp &= ~mask;
- val |= tmp;
- }
-
- return tda10071_wr_regs(priv, reg, &val, 1);
-}
-
-/* read single register with mask */
-int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask)
-{
- int ret, i;
- u8 tmp;
-
- ret = tda10071_rd_regs(priv, reg, &tmp, 1);
- if (ret)
- return ret;
-
- tmp &= mask;
-
- /* find position of the first bit */
- for (i = 0; i < 8; i++) {
- if ((mask >> i) & 0x01)
- break;
- }
- *val = tmp >> i;
-
- return 0;
-}
-
-/* execute firmware command */
-static int tda10071_cmd_execute(struct tda10071_priv *priv,
- struct tda10071_cmd *cmd)
-{
- int ret, i;
- u8 tmp;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- /* write cmd and args for firmware */
- ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len);
- if (ret)
- goto error;
-
- /* start cmd execution */
- ret = tda10071_wr_reg(priv, 0x1f, 1);
- if (ret)
- goto error;
-
- /* wait cmd execution terminate */
- for (i = 1000, tmp = 1; i && tmp; i--) {
- ret = tda10071_rd_reg(priv, 0x1f, &tmp);
- if (ret)
- goto error;
-
- usleep_range(200, 5000);
- }
-
- dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
-
- if (i == 0) {
- ret = -ETIMEDOUT;
- goto error;
- }
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_set_tone(struct dvb_frontend *fe,
- fe_sec_tone_mode_t fe_sec_tone_mode)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret;
- u8 tone;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__,
- fe_sec_tone_mode);
-
- switch (fe_sec_tone_mode) {
- case SEC_TONE_ON:
- tone = 1;
- break;
- case SEC_TONE_OFF:
- tone = 0;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
-
- cmd.args[0] = CMD_LNB_PCB_CONFIG;
- cmd.args[1] = 0;
- cmd.args[2] = 0x00;
- cmd.args[3] = 0x00;
- cmd.args[4] = tone;
- cmd.len = 5;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t fe_sec_voltage)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret;
- u8 voltage;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage);
-
- switch (fe_sec_voltage) {
- case SEC_VOLTAGE_13:
- voltage = 0;
- break;
- case SEC_VOLTAGE_18:
- voltage = 1;
- break;
- case SEC_VOLTAGE_OFF:
- voltage = 0;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n",
- __func__);
- ret = -EINVAL;
- goto error;
- };
-
- cmd.args[0] = CMD_LNB_SET_DC_LEVEL;
- cmd.args[1] = 0;
- cmd.args[2] = voltage;
- cmd.len = 3;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe,
- struct dvb_diseqc_master_cmd *diseqc_cmd)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i;
- u8 tmp;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__,
- diseqc_cmd->msg_len);
-
- if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) {
- ret = -EINVAL;
- goto error;
- }
-
- /* wait LNB TX */
- for (i = 500, tmp = 0; i && !tmp; i--) {
- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
- if (ret)
- goto error;
-
- usleep_range(10000, 20000);
- }
-
- dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
-
- if (i == 0) {
- ret = -ETIMEDOUT;
- goto error;
- }
-
- ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_LNB_SEND_DISEQC;
- cmd.args[1] = 0;
- cmd.args[2] = 0;
- cmd.args[3] = 0;
- cmd.args[4] = 2;
- cmd.args[5] = 0;
- cmd.args[6] = diseqc_cmd->msg_len;
- memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len);
- cmd.len = 7 + diseqc_cmd->msg_len;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe,
- struct dvb_diseqc_slave_reply *reply)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i;
- u8 tmp;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
-
- /* wait LNB RX */
- for (i = 500, tmp = 0; i && !tmp; i--) {
- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02);
- if (ret)
- goto error;
-
- usleep_range(10000, 20000);
- }
-
- dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
-
- if (i == 0) {
- ret = -ETIMEDOUT;
- goto error;
- }
-
- /* reply len */
- ret = tda10071_rd_reg(priv, 0x46, &tmp);
- if (ret)
- goto error;
-
- reply->msg_len = tmp & 0x1f; /* [4:0] */;
- if (reply->msg_len > sizeof(reply->msg))
- reply->msg_len = sizeof(reply->msg); /* truncate API max */
-
- /* read reply */
- cmd.args[0] = CMD_LNB_UPDATE_REPLY;
- cmd.args[1] = 0;
- cmd.len = 2;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len);
- if (ret)
- goto error;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_diseqc_send_burst(struct dvb_frontend *fe,
- fe_sec_mini_cmd_t fe_sec_mini_cmd)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i;
- u8 tmp, burst;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__,
- fe_sec_mini_cmd);
-
- switch (fe_sec_mini_cmd) {
- case SEC_MINI_A:
- burst = 0;
- break;
- case SEC_MINI_B:
- burst = 1;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
-
- /* wait LNB TX */
- for (i = 500, tmp = 0; i && !tmp; i--) {
- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01);
- if (ret)
- goto error;
-
- usleep_range(10000, 20000);
- }
-
- dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i);
-
- if (i == 0) {
- ret = -ETIMEDOUT;
- goto error;
- }
-
- ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_LNB_SEND_TONEBURST;
- cmd.args[1] = 0;
- cmd.args[2] = burst;
- cmd.len = 3;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- int ret;
- u8 tmp;
-
- *status = 0;
-
- if (!priv->warm) {
- ret = 0;
- goto error;
- }
-
- ret = tda10071_rd_reg(priv, 0x39, &tmp);
- if (ret)
- goto error;
-
- if (tmp & 0x01) /* tuner PLL */
- *status |= FE_HAS_SIGNAL;
- if (tmp & 0x02) /* demod PLL */
- *status |= FE_HAS_CARRIER;
- if (tmp & 0x04) /* viterbi or LDPC*/
- *status |= FE_HAS_VITERBI;
- if (tmp & 0x08) /* RS or BCH */
- *status |= FE_HAS_SYNC | FE_HAS_LOCK;
-
- priv->fe_status = *status;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- int ret;
- u8 buf[2];
-
- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
- *snr = 0;
- ret = 0;
- goto error;
- }
-
- ret = tda10071_rd_regs(priv, 0x3a, buf, 2);
- if (ret)
- goto error;
-
- /* Es/No dBx10 */
- *snr = buf[0] << 8 | buf[1];
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret;
- u8 tmp;
-
- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
- *strength = 0;
- ret = 0;
- goto error;
- }
-
- cmd.args[0] = CMD_GET_AGCACC;
- cmd.args[1] = 0;
- cmd.len = 2;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- /* input power estimate dBm */
- ret = tda10071_rd_reg(priv, 0x50, &tmp);
- if (ret)
- goto error;
-
- if (tmp < 181)
- tmp = 181; /* -75 dBm */
- else if (tmp > 236)
- tmp = 236; /* -20 dBm */
-
- /* scale value to 0x0000-0xffff */
- *strength = (tmp-181) * 0xffff / (236-181);
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i, len;
- u8 tmp, reg, buf[8];
-
- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
- *ber = priv->ber = 0;
- ret = 0;
- goto error;
- }
-
- switch (priv->delivery_system) {
- case SYS_DVBS:
- reg = 0x4c;
- len = 8;
- i = 1;
- break;
- case SYS_DVBS2:
- reg = 0x4d;
- len = 4;
- i = 0;
- break;
- default:
- *ber = priv->ber = 0;
- return 0;
- }
-
- ret = tda10071_rd_reg(priv, reg, &tmp);
- if (ret)
- goto error;
-
- if (priv->meas_count[i] == tmp) {
- dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__,
- tmp);
- *ber = priv->ber;
- return 0;
- } else {
- priv->meas_count[i] = tmp;
- }
-
- cmd.args[0] = CMD_BER_UPDATE_COUNTERS;
- cmd.args[1] = 0;
- cmd.args[2] = i;
- cmd.len = 3;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- ret = tda10071_rd_regs(priv, cmd.len, buf, len);
- if (ret)
- goto error;
-
- if (priv->delivery_system == SYS_DVBS) {
- *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
- priv->ucb += (buf[4] << 8) | buf[5];
- } else {
- *ber = (buf[0] << 8) | buf[1];
- }
- priv->ber = *ber;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- int ret = 0;
-
- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
- *ucblocks = 0;
- goto error;
- }
-
- /* UCB is updated when BER is read. Assume BER is read anyway. */
-
- *ucblocks = priv->ucb;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_set_frontend(struct dvb_frontend *fe)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u8 mode, rolloff, pilot, inversion, div;
-
- dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \
- "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \
- "rolloff=%d\n", __func__, c->delivery_system, c->modulation,
- c->frequency, c->symbol_rate, c->inversion, c->pilot,
- c->rolloff);
-
- priv->delivery_system = SYS_UNDEFINED;
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- switch (c->inversion) {
- case INVERSION_OFF:
- inversion = 1;
- break;
- case INVERSION_ON:
- inversion = 0;
- break;
- case INVERSION_AUTO:
- /* 2 = auto; try first on then off
- * 3 = auto; try first off then on */
- inversion = 3;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__);
- ret = -EINVAL;
- goto error;
- }
-
- switch (c->delivery_system) {
- case SYS_DVBS:
- rolloff = 0;
- pilot = 2;
- break;
- case SYS_DVBS2:
- switch (c->rolloff) {
- case ROLLOFF_20:
- rolloff = 2;
- break;
- case ROLLOFF_25:
- rolloff = 1;
- break;
- case ROLLOFF_35:
- rolloff = 0;
- break;
- case ROLLOFF_AUTO:
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
-
- switch (c->pilot) {
- case PILOT_OFF:
- pilot = 0;
- break;
- case PILOT_ON:
- pilot = 1;
- break;
- case PILOT_AUTO:
- pilot = 2;
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
- break;
- default:
- dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
-
- for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
- if (c->delivery_system == TDA10071_MODCOD[i].delivery_system &&
- c->modulation == TDA10071_MODCOD[i].modulation &&
- c->fec_inner == TDA10071_MODCOD[i].fec) {
- mode = TDA10071_MODCOD[i].val;
- dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n",
- __func__, mode);
- break;
- }
- }
-
- if (mode == 0xff) {
- dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n",
- __func__);
- ret = -EINVAL;
- goto error;
- }
-
- if (c->symbol_rate <= 5000000)
- div = 14;
- else
- div = 4;
-
- ret = tda10071_wr_reg(priv, 0x81, div);
- if (ret)
- goto error;
-
- ret = tda10071_wr_reg(priv, 0xe3, div);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_CHANGE_CHANNEL;
- cmd.args[1] = 0;
- cmd.args[2] = mode;
- cmd.args[3] = (c->frequency >> 16) & 0xff;
- cmd.args[4] = (c->frequency >> 8) & 0xff;
- cmd.args[5] = (c->frequency >> 0) & 0xff;
- cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff;
- cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff;
- cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff;
- cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff;
- cmd.args[10] = rolloff;
- cmd.args[11] = inversion;
- cmd.args[12] = pilot;
- cmd.args[13] = 0x00;
- cmd.args[14] = 0x00;
- cmd.len = 15;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- priv->delivery_system = c->delivery_system;
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_get_frontend(struct dvb_frontend *fe)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- int ret, i;
- u8 buf[5], tmp;
-
- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) {
- ret = -EFAULT;
- goto error;
- }
-
- ret = tda10071_rd_regs(priv, 0x30, buf, 5);
- if (ret)
- goto error;
-
- tmp = buf[0] & 0x3f;
- for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) {
- if (tmp == TDA10071_MODCOD[i].val) {
- c->modulation = TDA10071_MODCOD[i].modulation;
- c->fec_inner = TDA10071_MODCOD[i].fec;
- c->delivery_system = TDA10071_MODCOD[i].delivery_system;
- }
- }
-
- switch ((buf[1] >> 0) & 0x01) {
- case 0:
- c->inversion = INVERSION_OFF;
- break;
- case 1:
- c->inversion = INVERSION_ON;
- break;
- }
-
- switch ((buf[1] >> 7) & 0x01) {
- case 0:
- c->pilot = PILOT_OFF;
- break;
- case 1:
- c->pilot = PILOT_ON;
- break;
- }
-
- c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0);
-
- ret = tda10071_rd_regs(priv, 0x52, buf, 3);
- if (ret)
- goto error;
-
- c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0);
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_init(struct dvb_frontend *fe)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i, len, remaining, fw_size;
- const struct firmware *fw;
- u8 *fw_file = TDA10071_DEFAULT_FIRMWARE;
- u8 tmp, buf[4];
- struct tda10071_reg_val_mask tab[] = {
- { 0xcd, 0x00, 0x07 },
- { 0x80, 0x00, 0x02 },
- { 0xcd, 0x00, 0xc0 },
- { 0xce, 0x00, 0x1b },
- { 0x9d, 0x00, 0x01 },
- { 0x9d, 0x00, 0x02 },
- { 0x9e, 0x00, 0x01 },
- { 0x87, 0x00, 0x80 },
- { 0xce, 0x00, 0x08 },
- { 0xce, 0x00, 0x10 },
- };
- struct tda10071_reg_val_mask tab2[] = {
- { 0xf1, 0x70, 0xff },
- { 0x88, priv->cfg.pll_multiplier, 0x3f },
- { 0x89, 0x00, 0x10 },
- { 0x89, 0x10, 0x10 },
- { 0xc0, 0x01, 0x01 },
- { 0xc0, 0x00, 0x01 },
- { 0xe0, 0xff, 0xff },
- { 0xe0, 0x00, 0xff },
- { 0x96, 0x1e, 0x7e },
- { 0x8b, 0x08, 0x08 },
- { 0x8b, 0x00, 0x08 },
- { 0x8f, 0x1a, 0x7e },
- { 0x8c, 0x68, 0xff },
- { 0x8d, 0x08, 0xff },
- { 0x8e, 0x4c, 0xff },
- { 0x8f, 0x01, 0x01 },
- { 0x8b, 0x04, 0x04 },
- { 0x8b, 0x00, 0x04 },
- { 0x87, 0x05, 0x07 },
- { 0x80, 0x00, 0x20 },
- { 0xc8, 0x01, 0xff },
- { 0xb4, 0x47, 0xff },
- { 0xb5, 0x9c, 0xff },
- { 0xb6, 0x7d, 0xff },
- { 0xba, 0x00, 0x03 },
- { 0xb7, 0x47, 0xff },
- { 0xb8, 0x9c, 0xff },
- { 0xb9, 0x7d, 0xff },
- { 0xba, 0x00, 0x0c },
- { 0xc8, 0x00, 0xff },
- { 0xcd, 0x00, 0x04 },
- { 0xcd, 0x00, 0x20 },
- { 0xe8, 0x02, 0xff },
- { 0xcf, 0x20, 0xff },
- { 0x9b, 0xd7, 0xff },
- { 0x9a, 0x01, 0x03 },
- { 0xa8, 0x05, 0x0f },
- { 0xa8, 0x65, 0xf0 },
- { 0xa6, 0xa0, 0xf0 },
- { 0x9d, 0x50, 0xfc },
- { 0x9e, 0x20, 0xe0 },
- { 0xa3, 0x1c, 0x7c },
- { 0xd5, 0x03, 0x03 },
- };
-
- /* firmware status */
- ret = tda10071_rd_reg(priv, 0x51, &tmp);
- if (ret)
- goto error;
-
- if (!tmp) {
- /* warm state - wake up device from sleep */
- priv->warm = 1;
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = tda10071_wr_reg_mask(priv, tab[i].reg,
- tab[i].val, tab[i].mask);
- if (ret)
- goto error;
- }
-
- cmd.args[0] = CMD_SET_SLEEP_MODE;
- cmd.args[1] = 0;
- cmd.args[2] = 0;
- cmd.len = 3;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
- } else {
- /* cold state - try to download firmware */
- priv->warm = 0;
-
- /* request the firmware, this will block and timeout */
- ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent);
- if (ret) {
- dev_err(&priv->i2c->dev, "%s: did not find the " \
- "firmware file. (%s) Please see " \
- "linux/Documentation/dvb/ for more " \
- "details on firmware-problems. (%d)\n",
- KBUILD_MODNAME, fw_file, ret);
- goto error;
- }
-
- /* init */
- for (i = 0; i < ARRAY_SIZE(tab2); i++) {
- ret = tda10071_wr_reg_mask(priv, tab2[i].reg,
- tab2[i].val, tab2[i].mask);
- if (ret)
- goto error_release_firmware;
- }
-
- /* download firmware */
- ret = tda10071_wr_reg(priv, 0xe0, 0x7f);
- if (ret)
- goto error_release_firmware;
-
- ret = tda10071_wr_reg(priv, 0xf7, 0x81);
- if (ret)
- goto error_release_firmware;
-
- ret = tda10071_wr_reg(priv, 0xf8, 0x00);
- if (ret)
- goto error_release_firmware;
-
- ret = tda10071_wr_reg(priv, 0xf9, 0x00);
- if (ret)
- goto error_release_firmware;
-
- dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \
- "will try to load a firmware\n", KBUILD_MODNAME,
- tda10071_ops.info.name);
- dev_info(&priv->i2c->dev, "%s: downloading firmware from " \
- "file '%s'\n", KBUILD_MODNAME, fw_file);
-
- /* do not download last byte */
- fw_size = fw->size - 1;
-
- for (remaining = fw_size; remaining > 0;
- remaining -= (priv->cfg.i2c_wr_max - 1)) {
- len = remaining;
- if (len > (priv->cfg.i2c_wr_max - 1))
- len = (priv->cfg.i2c_wr_max - 1);
-
- ret = tda10071_wr_regs(priv, 0xfa,
- (u8 *) &fw->data[fw_size - remaining], len);
- if (ret) {
- dev_err(&priv->i2c->dev, "%s: firmware " \
- "download failed=%d\n",
- KBUILD_MODNAME, ret);
- if (ret)
- goto error_release_firmware;
- }
- }
- release_firmware(fw);
-
- ret = tda10071_wr_reg(priv, 0xf7, 0x0c);
- if (ret)
- goto error;
-
- ret = tda10071_wr_reg(priv, 0xe0, 0x00);
- if (ret)
- goto error;
-
- /* wait firmware start */
- msleep(250);
-
- /* firmware status */
- ret = tda10071_rd_reg(priv, 0x51, &tmp);
- if (ret)
- goto error;
-
- if (tmp) {
- dev_info(&priv->i2c->dev, "%s: firmware did not run\n",
- KBUILD_MODNAME);
- ret = -EFAULT;
- goto error;
- } else {
- priv->warm = 1;
- }
-
- cmd.args[0] = CMD_GET_FW_VERSION;
- cmd.len = 1;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- ret = tda10071_rd_regs(priv, cmd.len, buf, 4);
- if (ret)
- goto error;
-
- dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n",
- KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]);
- dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n",
- KBUILD_MODNAME, tda10071_ops.info.name);
-
- ret = tda10071_rd_regs(priv, 0x81, buf, 2);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_DEMOD_INIT;
- cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff;
- cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff;
- cmd.args[3] = buf[0];
- cmd.args[4] = buf[1];
- cmd.args[5] = priv->cfg.pll_multiplier;
- cmd.args[6] = priv->cfg.spec_inv;
- cmd.args[7] = 0x00;
- cmd.len = 8;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_TUNER_INIT;
- cmd.args[1] = 0x00;
- cmd.args[2] = 0x00;
- cmd.args[3] = 0x00;
- cmd.args[4] = 0x00;
- cmd.args[5] = 0x14;
- cmd.args[6] = 0x00;
- cmd.args[7] = 0x03;
- cmd.args[8] = 0x02;
- cmd.args[9] = 0x02;
- cmd.args[10] = 0x00;
- cmd.args[11] = 0x00;
- cmd.args[12] = 0x00;
- cmd.args[13] = 0x00;
- cmd.args[14] = 0x00;
- cmd.len = 15;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_MPEG_CONFIG;
- cmd.args[1] = 0;
- cmd.args[2] = priv->cfg.ts_mode;
- cmd.args[3] = 0x00;
- cmd.args[4] = 0x04;
- cmd.args[5] = 0x00;
- cmd.len = 6;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_LNB_CONFIG;
- cmd.args[1] = 0;
- cmd.args[2] = 150;
- cmd.args[3] = 3;
- cmd.args[4] = 22;
- cmd.args[5] = 1;
- cmd.args[6] = 1;
- cmd.args[7] = 30;
- cmd.args[8] = 30;
- cmd.args[9] = 30;
- cmd.args[10] = 30;
- cmd.len = 11;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- cmd.args[0] = CMD_BER_CONTROL;
- cmd.args[1] = 0;
- cmd.args[2] = 14;
- cmd.args[3] = 14;
- cmd.len = 4;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
- }
-
- return ret;
-error_release_firmware:
- release_firmware(fw);
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_sleep(struct dvb_frontend *fe)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- struct tda10071_cmd cmd;
- int ret, i;
- struct tda10071_reg_val_mask tab[] = {
- { 0xcd, 0x07, 0x07 },
- { 0x80, 0x02, 0x02 },
- { 0xcd, 0xc0, 0xc0 },
- { 0xce, 0x1b, 0x1b },
- { 0x9d, 0x01, 0x01 },
- { 0x9d, 0x02, 0x02 },
- { 0x9e, 0x01, 0x01 },
- { 0x87, 0x80, 0x80 },
- { 0xce, 0x08, 0x08 },
- { 0xce, 0x10, 0x10 },
- };
-
- if (!priv->warm) {
- ret = -EFAULT;
- goto error;
- }
-
- cmd.args[0] = CMD_SET_SLEEP_MODE;
- cmd.args[1] = 0;
- cmd.args[2] = 1;
- cmd.len = 3;
- ret = tda10071_cmd_execute(priv, &cmd);
- if (ret)
- goto error;
-
- for (i = 0; i < ARRAY_SIZE(tab); i++) {
- ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val,
- tab[i].mask);
- if (ret)
- goto error;
- }
-
- return ret;
-error:
- dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
- return ret;
-}
-
-static int tda10071_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *s)
-{
- s->min_delay_ms = 8000;
- s->step_size = 0;
- s->max_drift = 0;
-
- return 0;
-}
-
-static void tda10071_release(struct dvb_frontend *fe)
-{
- struct tda10071_priv *priv = fe->demodulator_priv;
- kfree(priv);
-}
-
-struct dvb_frontend *tda10071_attach(const struct tda10071_config *config,
- struct i2c_adapter *i2c)
-{
- int ret;
- struct tda10071_priv *priv = NULL;
- u8 tmp;
-
- /* allocate memory for the internal priv */
- priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL);
- if (priv == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- /* setup the priv */
- priv->i2c = i2c;
- memcpy(&priv->cfg, config, sizeof(struct tda10071_config));
-
- /* chip ID */
- ret = tda10071_rd_reg(priv, 0xff, &tmp);
- if (ret || tmp != 0x0f)
- goto error;
-
- /* chip type */
- ret = tda10071_rd_reg(priv, 0xdd, &tmp);
- if (ret || tmp != 0x00)
- goto error;
-
- /* chip version */
- ret = tda10071_rd_reg(priv, 0xfe, &tmp);
- if (ret || tmp != 0x01)
- goto error;
-
- /* create dvb_frontend */
- memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops));
- priv->fe.demodulator_priv = priv;
-
- return &priv->fe;
-error:
- dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
- kfree(priv);
- return NULL;
-}
-EXPORT_SYMBOL(tda10071_attach);
-
-static struct dvb_frontend_ops tda10071_ops = {
- .delsys = { SYS_DVBS, SYS_DVBS2 },
- .info = {
- .name = "NXP TDA10071",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_tolerance = 5000,
- .symbol_rate_min = 1000000,
- .symbol_rate_max = 45000000,
- .caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 |
- FE_CAN_FEC_2_3 |
- FE_CAN_FEC_3_4 |
- FE_CAN_FEC_4_5 |
- FE_CAN_FEC_5_6 |
- FE_CAN_FEC_6_7 |
- FE_CAN_FEC_7_8 |
- FE_CAN_FEC_8_9 |
- FE_CAN_FEC_AUTO |
- FE_CAN_QPSK |
- FE_CAN_RECOVER |
- FE_CAN_2G_MODULATION
- },
-
- .release = tda10071_release,
-
- .get_tune_settings = tda10071_get_tune_settings,
-
- .init = tda10071_init,
- .sleep = tda10071_sleep,
-
- .set_frontend = tda10071_set_frontend,
- .get_frontend = tda10071_get_frontend,
-
- .read_status = tda10071_read_status,
- .read_snr = tda10071_read_snr,
- .read_signal_strength = tda10071_read_signal_strength,
- .read_ber = tda10071_read_ber,
- .read_ucblocks = tda10071_read_ucblocks,
-
- .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd,
- .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply,
- .diseqc_send_burst = tda10071_diseqc_send_burst,
-
- .set_tone = tda10071_set_tone,
- .set_voltage = tda10071_set_voltage,
-};
-
-MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h
deleted file mode 100644
index 0fa85cfa70c2..000000000000
--- a/drivers/media/dvb/frontends/tda10071_priv.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver
- *
- * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#ifndef TDA10071_PRIV
-#define TDA10071_PRIV
-
-#include "dvb_frontend.h"
-#include "tda10071.h"
-#include <linux/firmware.h>
-
-struct tda10071_priv {
- struct i2c_adapter *i2c;
- struct dvb_frontend fe;
- struct tda10071_config cfg;
-
- u8 meas_count[2];
- u32 ber;
- u32 ucb;
- fe_status_t fe_status;
- fe_delivery_system_t delivery_system;
- bool warm; /* FW running */
-};
-
-static struct tda10071_modcod {
- fe_delivery_system_t delivery_system;
- fe_modulation_t modulation;
- fe_code_rate_t fec;
- u8 val;
-} TDA10071_MODCOD[] = {
- /* NBC-QPSK */
- { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 },
- { SYS_DVBS2, QPSK, FEC_1_2, 0x04 },
- { SYS_DVBS2, QPSK, FEC_3_5, 0x05 },
- { SYS_DVBS2, QPSK, FEC_2_3, 0x06 },
- { SYS_DVBS2, QPSK, FEC_3_4, 0x07 },
- { SYS_DVBS2, QPSK, FEC_4_5, 0x08 },
- { SYS_DVBS2, QPSK, FEC_5_6, 0x09 },
- { SYS_DVBS2, QPSK, FEC_8_9, 0x0a },
- { SYS_DVBS2, QPSK, FEC_9_10, 0x0b },
- /* 8PSK */
- { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c },
- { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d },
- { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e },
- { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f },
- { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 },
- { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 },
- /* QPSK */
- { SYS_DVBS, QPSK, FEC_AUTO, 0x2d },
- { SYS_DVBS, QPSK, FEC_1_2, 0x2e },
- { SYS_DVBS, QPSK, FEC_2_3, 0x2f },
- { SYS_DVBS, QPSK, FEC_3_4, 0x30 },
- { SYS_DVBS, QPSK, FEC_5_6, 0x31 },
- { SYS_DVBS, QPSK, FEC_7_8, 0x32 },
-};
-
-struct tda10071_reg_val_mask {
- u8 reg;
- u8 val;
- u8 mask;
-};
-
-/* firmware filename */
-#define TDA10071_DEFAULT_FIRMWARE "dvb-fe-tda10071.fw"
-
-/* firmware commands */
-#define CMD_DEMOD_INIT 0x10
-#define CMD_CHANGE_CHANNEL 0x11
-#define CMD_MPEG_CONFIG 0x13
-#define CMD_TUNER_INIT 0x15
-#define CMD_GET_AGCACC 0x1a
-
-#define CMD_LNB_CONFIG 0x20
-#define CMD_LNB_SEND_DISEQC 0x21
-#define CMD_LNB_SET_DC_LEVEL 0x22
-#define CMD_LNB_PCB_CONFIG 0x23
-#define CMD_LNB_SEND_TONEBURST 0x24
-#define CMD_LNB_UPDATE_REPLY 0x25
-
-#define CMD_GET_FW_VERSION 0x35
-#define CMD_SET_SLEEP_MODE 0x36
-#define CMD_BER_CONTROL 0x3e
-#define CMD_BER_UPDATE_COUNTERS 0x3f
-
-/* firmare command struct */
-#define TDA10071_ARGLEN 30
-struct tda10071_cmd {
- u8 args[TDA10071_ARGLEN];
- u8 len;
-};
-
-
-#endif /* TDA10071_PRIV */
diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c
deleted file mode 100644
index 53c7d8f1df28..000000000000
--- a/drivers/media/dvb/frontends/tda8261.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- TDA8261 8PSK/QPSK tuner driver
- Copyright (C) Manu Abraham (abraham.manu@gmail.com)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-
-#include "dvb_frontend.h"
-#include "tda8261.h"
-
-struct tda8261_state {
- struct dvb_frontend *fe;
- struct i2c_adapter *i2c;
- const struct tda8261_config *config;
-
- /* state cache */
- u32 frequency;
- u32 bandwidth;
-};
-
-static int tda8261_read(struct tda8261_state *state, u8 *buf)
-{
- const struct tda8261_config *config = state->config;
- int err = 0;
- struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 };
-
- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
- printk("%s: read error, err=%d\n", __func__, err);
-
- return err;
-}
-
-static int tda8261_write(struct tda8261_state *state, u8 *buf)
-{
- const struct tda8261_config *config = state->config;
- int err = 0;
- struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 };
-
- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1)
- printk("%s: write error, err=%d\n", __func__, err);
-
- return err;
-}
-
-static int tda8261_get_status(struct dvb_frontend *fe, u32 *status)
-{
- struct tda8261_state *state = fe->tuner_priv;
- u8 result = 0;
- int err = 0;
-
- *status = 0;
-
- if ((err = tda8261_read(state, &result)) < 0) {
- printk("%s: I/O Error\n", __func__);
- return err;
- }
- if ((result >> 6) & 0x01) {
- printk("%s: Tuner Phase Locked\n", __func__);
- *status = 1;
- }
-
- return err;
-}
-
-static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */
-static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 };
-
-static int tda8261_get_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
-{
- struct tda8261_state *state = fe->tuner_priv;
- int err = 0;
-
- switch (param) {
- case DVBFE_TUNER_FREQUENCY:
- tstate->frequency = state->frequency;
- break;
- case DVBFE_TUNER_BANDWIDTH:
- tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */
- break;
- default:
- printk("%s: Unknown parameter (param=%d)\n", __func__, param);
- err = -EINVAL;
- break;
- }
-
- return err;
-}
-
-static int tda8261_set_state(struct dvb_frontend *fe,
- enum tuner_param param,
- struct tuner_state *tstate)
-{
- struct tda8261_state *state = fe->tuner_priv;
- const struct tda8261_config *config = state->config;
- u32 frequency, N, status = 0;
- u8 buf[4];
- int err = 0;
-
- if (param & DVBFE_TUNER_FREQUENCY) {
- /**
- * N = Max VCO Frequency / Channel Spacing
- * Max VCO Frequency = VCO frequency + (channel spacing - 1)
- * (to account for half channel spacing on either side)
- */
- frequency = tstate->frequency;
- if ((frequency < 950000) || (frequency > 2150000)) {
- printk("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency);
- return -EINVAL;
- }
- N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size];
- printk("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n",
- __func__, config->step_size, div_tab[config->step_size], N, N);
-
- buf[0] = (N >> 8) & 0xff;
- buf[1] = N & 0xff;
- buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1);
-
- if (frequency < 1450000)
- buf[3] = 0x00;
- else if (frequency < 2000000)
- buf[3] = 0x40;
- else if (frequency < 2150000)
- buf[3] = 0x80;
-
- /* Set params */
- if ((err = tda8261_write(state, buf)) < 0) {
- printk("%s: I/O Error\n", __func__);
- return err;
- }
- /* sleep for some time */
- printk("%s: Waiting to Phase LOCK\n", __func__);
- msleep(20);
- /* check status */
- if ((err = tda8261_get_status(fe, &status)) < 0) {
- printk("%s: I/O Error\n", __func__);
- return err;
- }
- if (status == 1) {
- printk("%s: Tuner Phase locked: status=%d\n", __func__, status);
- state->frequency = frequency; /* cache successful state */
- } else {
- printk("%s: No Phase lock: status=%d\n", __func__, status);
- }
- } else {
- printk("%s: Unknown parameter (param=%d)\n", __func__, param);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int tda8261_release(struct dvb_frontend *fe)
-{
- struct tda8261_state *state = fe->tuner_priv;
-
- fe->tuner_priv = NULL;
- kfree(state);
- return 0;
-}
-
-static struct dvb_tuner_ops tda8261_ops = {
-
- .info = {
- .name = "TDA8261",
-// .tuner_name = NULL,
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_step = 0
- },
-
- .set_state = tda8261_set_state,
- .get_state = tda8261_get_state,
- .get_status = tda8261_get_status,
- .release = tda8261_release
-};
-
-struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe,
- const struct tda8261_config *config,
- struct i2c_adapter *i2c)
-{
- struct tda8261_state *state = NULL;
-
- if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL)
- goto exit;
-
- state->config = config;
- state->i2c = i2c;
- state->fe = fe;
- fe->tuner_priv = state;
- fe->ops.tuner_ops = tda8261_ops;
-
- fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size];
-// fe->ops.tuner_ops.tuner_name = &config->buf;
-
-// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n",
-// __func__, fe->ops.tuner_ops.tuner_name);
- printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__);
-
- return fe;
-
-exit:
- kfree(state);
- return NULL;
-}
-
-EXPORT_SYMBOL(tda8261_attach);
-
-MODULE_AUTHOR("Manu Abraham");
-MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig
deleted file mode 100644
index a13a50503134..000000000000
--- a/drivers/media/dvb/mantis/Kconfig
+++ /dev/null
@@ -1,38 +0,0 @@
-config MANTIS_CORE
- tristate "Mantis/Hopper PCI bridge based devices"
- depends on PCI && I2C && INPUT && RC_CORE
-
- help
- Support for PCI cards based on the Mantis and Hopper PCi bridge.
-
- Say Y if you own such a device and want to use it.
-
-config DVB_MANTIS
- tristate "MANTIS based cards"
- depends on MANTIS_CORE && DVB_CORE && PCI && I2C
- select DVB_MB86A16 if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
- select DVB_TDA665x if !DVB_FE_CUSTOMISE
- select DVB_TDA10021 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select DVB_PLL
- help
- Support for PCI cards based on the Mantis PCI bridge.
- Say Y when you have a Mantis based DVB card and want to use it.
-
- If unsure say N.
-
-config DVB_HOPPER
- tristate "HOPPER based cards"
- depends on MANTIS_CORE && DVB_CORE && PCI && I2C
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_PLL
- help
- Support for PCI cards based on the Hopper PCI bridge.
- Say Y when you have a Hopper based DVB card and want to use it.
-
- If unsure say N
diff --git a/drivers/media/dvb/mantis/Makefile b/drivers/media/dvb/mantis/Makefile
deleted file mode 100644
index ec8116dcb368..000000000000
--- a/drivers/media/dvb/mantis/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-mantis_core-objs := mantis_ioc.o \
- mantis_uart.o \
- mantis_dma.o \
- mantis_pci.o \
- mantis_i2c.o \
- mantis_dvb.o \
- mantis_evm.o \
- mantis_hif.o \
- mantis_ca.o \
- mantis_pcmcia.o \
- mantis_input.o
-
-mantis-objs := mantis_cards.o \
- mantis_vp1033.o \
- mantis_vp1034.o \
- mantis_vp1041.o \
- mantis_vp2033.o \
- mantis_vp2040.o \
- mantis_vp3030.o
-
-hopper-objs := hopper_cards.o \
- hopper_vp3028.o
-
-obj-$(CONFIG_MANTIS_CORE) += mantis_core.o
-obj-$(CONFIG_DVB_MANTIS) += mantis.o
-obj-$(CONFIG_DVB_HOPPER) += hopper.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c
deleted file mode 100644
index 095cf3a994e2..000000000000
--- a/drivers/media/dvb/mantis/mantis_cards.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- Mantis PCI bridge driver
-
- Copyright (C) Manu Abraham (abraham.manu@gmail.com)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-#include <asm/irq.h>
-#include <linux/interrupt.h>
-
-#include "dmxdev.h"
-#include "dvbdev.h"
-#include "dvb_demux.h"
-#include "dvb_frontend.h"
-#include "dvb_net.h"
-
-#include "mantis_common.h"
-
-#include "mantis_vp1033.h"
-#include "mantis_vp1034.h"
-#include "mantis_vp1041.h"
-#include "mantis_vp2033.h"
-#include "mantis_vp2040.h"
-#include "mantis_vp3030.h"
-
-#include "mantis_dma.h"
-#include "mantis_ca.h"
-#include "mantis_dvb.h"
-#include "mantis_uart.h"
-#include "mantis_ioc.h"
-#include "mantis_pci.h"
-#include "mantis_i2c.h"
-#include "mantis_reg.h"
-
-static unsigned int verbose;
-module_param(verbose, int, 0644);
-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)");
-
-static int devs;
-
-#define DRIVER_NAME "Mantis"
-
-static char *label[10] = {
- "DMA",
- "IRQ-0",
- "IRQ-1",
- "OCERR",
- "PABRT",
- "RIPRR",
- "PPERR",
- "FTRGT",
- "RISCI",
- "RACK"
-};
-
-static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
-{
- u32 stat = 0, mask = 0;
- u32 rst_stat = 0, rst_mask = 0;
-
- struct mantis_pci *mantis;
- struct mantis_ca *ca;
-
- mantis = (struct mantis_pci *) dev_id;
- if (unlikely(mantis == NULL)) {
- dprintk(MANTIS_ERROR, 1, "Mantis == NULL");
- return IRQ_NONE;
- }
- ca = mantis->mantis_ca;
-
- stat = mmread(MANTIS_INT_STAT);
- mask = mmread(MANTIS_INT_MASK);
- if (!(stat & mask))
- return IRQ_NONE;
-
- rst_mask = MANTIS_GPIF_WRACK |
- MANTIS_GPIF_OTHERR |
- MANTIS_SBUF_WSTO |
- MANTIS_GPIF_EXTIRQ;
-
- rst_stat = mmread(MANTIS_GPIF_STATUS);
- rst_stat &= rst_mask;
- mmwrite(rst_stat, MANTIS_GPIF_STATUS);
-
- mantis->mantis_int_stat = stat;
- mantis->mantis_int_mask = mask;
- dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask);
- if (stat & MANTIS_INT_RISCEN) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]);
- }
- if (stat & MANTIS_INT_IRQ0) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]);
- mantis->gpif_status = rst_stat;
- wake_up(&ca->hif_write_wq);
- schedule_work(&ca->hif_evm_work);
- }
- if (stat & MANTIS_INT_IRQ1) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]);
- schedule_work(&mantis->uart_work);
- }
- if (stat & MANTIS_INT_OCERR) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]);
- }
- if (stat & MANTIS_INT_PABORT) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]);
- }
- if (stat & MANTIS_INT_RIPERR) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]);
- }
- if (stat & MANTIS_INT_PPERR) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]);
- }
- if (stat & MANTIS_INT_FTRGT) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]);
- }
- if (stat & MANTIS_INT_RISCI) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
- mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
- tasklet_schedule(&mantis->tasklet);
- }
- if (stat & MANTIS_INT_I2CDONE) {
- dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
- wake_up(&mantis->i2c_wq);
- }
- mmwrite(stat, MANTIS_INT_STAT);
- stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE |
- MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 |
- MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 |
- MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 |
- MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 |
- MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 |
- MANTIS_INT_IRQ0 | MANTIS_INT_OCERR |
- MANTIS_INT_PABORT | MANTIS_INT_RIPERR |
- MANTIS_INT_PPERR | MANTIS_INT_FTRGT |
- MANTIS_INT_RISCI);
-
- if (stat)
- dprintk(MANTIS_DEBUG, 0, "<Unknown> Stat=<%02x> Mask=<%02x>", stat, mask);
-
- dprintk(MANTIS_DEBUG, 0, "\n");
- return IRQ_HANDLED;
-}
-
-static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
-{
- struct mantis_pci *mantis;
- struct mantis_hwconfig *config;
- int err = 0;
-
- mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL);
- if (mantis == NULL) {
- printk(KERN_ERR "%s ERROR: Out of memory\n", __func__);
- err = -ENOMEM;
- goto fail0;
- }
-
- mantis->num = devs;
- mantis->verbose = verbose;
- mantis->pdev = pdev;
- config = (struct mantis_hwconfig *) pci_id->driver_data;
- config->irq_handler = &mantis_irq_handler;
- mantis->hwconfig = config;
-
- err = mantis_pci_init(mantis);
- if (err) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err);
- goto fail1;
- }
-
- err = mantis_stream_control(mantis, STREAM_TO_HIF);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err);
- goto fail1;
- }
-
- err = mantis_i2c_init(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err);
- goto fail2;
- }
-
- err = mantis_get_mac(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err);
- goto fail2;
- }
-
- err = mantis_dma_init(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err);
- goto fail3;
- }
-
- err = mantis_dvb_init(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err);
- goto fail4;
- }
- err = mantis_uart_init(mantis);
- if (err < 0) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err);
- goto fail6;
- }
-
- devs++;
-
- return err;
-
-
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err);
- mantis_uart_exit(mantis);
-
-fail6:
-fail4:
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err);
- mantis_dma_exit(mantis);
-
-fail3:
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err);
- mantis_i2c_exit(mantis);
-
-fail2:
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err);
- mantis_pci_exit(mantis);
-
-fail1:
- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err);
- kfree(mantis);
-
-fail0:
- return err;
-}
-
-static void __devexit mantis_pci_remove(struct pci_dev *pdev)
-{
- struct mantis_pci *mantis = pci_get_drvdata(pdev);
-
- if (mantis) {
-
- mantis_uart_exit(mantis);
- mantis_dvb_exit(mantis);
- mantis_dma_exit(mantis);
- mantis_i2c_exit(mantis);
- mantis_pci_exit(mantis);
- kfree(mantis);
- }
- return;
-}
-
-static struct pci_device_id mantis_pci_table[] = {
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config),
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config),
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config),
- MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config),
- MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config),
- MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config),
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config),
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config),
- MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config),
- MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2033_config),
- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config),
- { }
-};
-
-MODULE_DEVICE_TABLE(pci, mantis_pci_table);
-
-static struct pci_driver mantis_pci_driver = {
- .name = DRIVER_NAME,
- .id_table = mantis_pci_table,
- .probe = mantis_pci_probe,
- .remove = mantis_pci_remove,
-};
-
-static int __devinit mantis_init(void)
-{
- return pci_register_driver(&mantis_pci_driver);
-}
-
-static void __devexit mantis_exit(void)
-{
- return pci_unregister_driver(&mantis_pci_driver);
-}
-
-module_init(mantis_init);
-module_exit(mantis_exit);
-
-MODULE_DESCRIPTION("MANTIS driver");
-MODULE_AUTHOR("Manu Abraham");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/mantis/mantis_core.c b/drivers/media/dvb/mantis/mantis_core.c
deleted file mode 100644
index 22524a8e6f61..000000000000
--- a/drivers/media/dvb/mantis/mantis_core.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- Mantis PCI bridge driver
-
- Copyright (C) Manu Abraham (abraham.manu@gmail.com)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include "mantis_common.h"
-#include "mantis_core.h"
-#include "mantis_vp1033.h"
-#include "mantis_vp1034.h"
-#include "mantis_vp1041.h"
-#include "mantis_vp2033.h"
-#include "mantis_vp2040.h"
-#include "mantis_vp3030.h"
-
-static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
-{
- int err;
- struct i2c_msg msg[] = {
- {
- .addr = 0x50,
- .flags = 0,
- .buf = data,
- .len = 1
- }, {
- .addr = 0x50,
- .flags = I2C_M_RD,
- .buf = data,
- .len = length
- },
- };
-
- err = i2c_transfer(&mantis->adapter, msg, 2);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1,
- "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >",
- err, data[0], data[1]);
-
- return err;
- }
-
- return 0;
-}
-
-static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
-{
- int err;
-
- struct i2c_msg msg = {
- .addr = 0x50,
- .flags = 0,
- .buf = data,
- .len = length
- };
-
- err = i2c_transfer(&mantis->adapter, &msg, 1);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1,
- "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >",
- err, length, data[0], data[1]);
-
- return err;
- }
-
- return 0;
-}
-
-static int get_mac_address(struct mantis_pci *mantis)
-{
- int err;
-
- mantis->mac_address[0] = 0x08;
- err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
-
- return err;
- }
- dprintk(verbose, MANTIS_ERROR, 0,
- " MAC Address=[%pM]\n", mantis->mac_address);
-
- return 0;
-}
-
-#define MANTIS_MODEL_UNKNOWN "UNKNOWN"
-#define MANTIS_DEV_UNKNOWN "UNKNOWN"
-
-struct mantis_hwconfig unknown_device = {
- .model_name = MANTIS_MODEL_UNKNOWN,
- .dev_type = MANTIS_DEV_UNKNOWN,
-};
-
-static void mantis_load_config(struct mantis_pci *mantis)
-{
- switch (mantis->subsystem_device) {
- case MANTIS_VP_1033_DVB_S: /* VP-1033 */
- mantis->hwconfig = &vp1033_mantis_config;
- break;
- case MANTIS_VP_1034_DVB_S: /* VP-1034 */
- mantis->hwconfig = &vp1034_mantis_config;
- break;
- case MANTIS_VP_1041_DVB_S2: /* VP-1041 */
- case TECHNISAT_SKYSTAR_HD2:
- mantis->hwconfig = &vp1041_mantis_config;
- break;
- case MANTIS_VP_2033_DVB_C: /* VP-2033 */
- mantis->hwconfig = &vp2033_mantis_config;
- break;
- case MANTIS_VP_2040_DVB_C: /* VP-2040 */
- case TERRATEC_CINERGY_C_PCI: /* VP-2040 clone */
- case TECHNISAT_CABLESTAR_HD2:
- mantis->hwconfig = &vp2040_mantis_config;
- break;
- case MANTIS_VP_3030_DVB_T: /* VP-3030 */
- mantis->hwconfig = &vp3030_mantis_config;
- break;
- default:
- mantis->hwconfig = &unknown_device;
- break;
- }
-}
-
-int mantis_core_init(struct mantis_pci *mantis)
-{
- int err = 0;
-
- mantis_load_config(mantis);
- dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n",
- mantis->hwconfig->model_name, mantis->hwconfig->dev_type,
- mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn));
- dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ",
- mantis->revision,
- mantis->subsystem_vendor, mantis->subsystem_device);
- dprintk(verbose, MANTIS_ERROR, 0,
- "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n",
- mantis->pdev->irq, mantis->latency,
- mantis->mantis_addr, mantis->mantis_mmio);
-
- err = mantis_i2c_init(mantis);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed");
- return err;
- }
- err = get_mac_address(mantis);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed");
- return err;
- }
- err = mantis_dma_init(mantis);
- if (err < 0) {
- dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed");
- return err;
- }
- err = mantis_dvb_init(mantis);
- if (err < 0) {
- dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed");
- return err;
- }
- err = mantis_uart_init(mantis);
- if (err < 0) {
- dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed");
- return err;
- }
-
- return 0;
-}
-
-int mantis_core_exit(struct mantis_pci *mantis)
-{
- mantis_dma_stop(mantis);
- dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping");
-
- mantis_uart_exit(mantis);
- dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed");
-
- if (mantis_dma_exit(mantis) < 0)
- dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed");
- if (mantis_dvb_exit(mantis) < 0)
- dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed");
- if (mantis_i2c_exit(mantis) < 0)
- dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed");
-
- return 0;
-}
-
-/* Turn the given bit on or off. */
-void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value)
-{
- u32 cur;
-
- cur = mmread(MANTIS_GPIF_ADDR);
- if (value)
- mantis->gpio_status = cur | (1 << bitpos);
- else
- mantis->gpio_status = cur & (~(1 << bitpos));
-
- mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR);
- mmwrite(0x00, MANTIS_GPIF_DOUT);
- udelay(100);
-}
-
-/* direction = 0 , no CI passthrough ; 1 , CI passthrough */
-void mantis_set_direction(struct mantis_pci *mantis, int direction)
-{
- u32 reg;
-
- reg = mmread(0x28);
- dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup");
- if (direction == 0x01) {
- /* to CI */
- reg |= 0x04;
- mmwrite(reg, 0x28);
- reg &= 0xff - 0x04;
- mmwrite(reg, 0x28);
- } else {
- reg &= 0xff - 0x04;
- mmwrite(reg, 0x28);
- reg |= 0x04;
- mmwrite(reg, 0x28);
- }
-}
diff --git a/drivers/media/dvb/mantis/mantis_dvb.c b/drivers/media/dvb/mantis/mantis_dvb.c
deleted file mode 100644
index e5180e45d310..000000000000
--- a/drivers/media/dvb/mantis/mantis_dvb.c
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- Mantis PCI bridge driver
- Copyright (C) Manu Abraham (abraham.manu@gmail.com)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-
-#include "dmxdev.h"
-#include "dvbdev.h"
-#include "dvb_demux.h"
-#include "dvb_frontend.h"
-#include "dvb_net.h"
-
-#include "mantis_common.h"
-#include "mantis_dma.h"
-#include "mantis_ca.h"
-#include "mantis_ioc.h"
-#include "mantis_dvb.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power)
-{
- struct mantis_hwconfig *config = mantis->hwconfig;
-
- switch (power) {
- case POWER_ON:
- dprintk(MANTIS_DEBUG, 1, "Power ON");
- mantis_gpio_set_bits(mantis, config->power, POWER_ON);
- msleep(100);
- mantis_gpio_set_bits(mantis, config->power, POWER_ON);
- msleep(100);
- break;
-
- case POWER_OFF:
- dprintk(MANTIS_DEBUG, 1, "Power OFF");
- mantis_gpio_set_bits(mantis, config->power, POWER_OFF);
- msleep(100);
- break;
-
- default:
- dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power);
- return -1;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(mantis_frontend_power);
-
-void mantis_frontend_soft_reset(struct mantis_pci *mantis)
-{
- struct mantis_hwconfig *config = mantis->hwconfig;
-
- dprintk(MANTIS_DEBUG, 1, "Frontend RESET");
- mantis_gpio_set_bits(mantis, config->reset, 0);
- msleep(100);
- mantis_gpio_set_bits(mantis, config->reset, 0);
- msleep(100);
- mantis_gpio_set_bits(mantis, config->reset, 1);
- msleep(100);
- mantis_gpio_set_bits(mantis, config->reset, 1);
- msleep(100);
-
- return;
-}
-EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset);
-
-static int mantis_frontend_shutdown(struct mantis_pci *mantis)
-{
- int err;
-
- mantis_frontend_soft_reset(mantis);
- err = mantis_frontend_power(mantis, POWER_OFF);
- if (err != 0) {
- dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err);
- return 1;
- }
-
- return 0;
-}
-
-static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- struct mantis_pci *mantis = dvbdmx->priv;
-
- dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed");
- if (!dvbdmx->dmx.frontend) {
- dprintk(MANTIS_DEBUG, 1, "no frontend ?");
- return -EINVAL;
- }
-
- mantis->feeds++;
- dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds);
-
- if (mantis->feeds == 1) {
- dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma");
- mantis_dma_start(mantis);
- tasklet_enable(&mantis->tasklet);
- }
-
- return mantis->feeds;
-}
-
-static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
- struct mantis_pci *mantis = dvbdmx->priv;
-
- dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed");
- if (!dvbdmx->dmx.frontend) {
- dprintk(MANTIS_DEBUG, 1, "no frontend ?");
- return -EINVAL;
- }
-
- mantis->feeds--;
- if (mantis->feeds == 0) {
- dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma");
- tasklet_disable(&mantis->tasklet);
- mantis_dma_stop(mantis);
- }
-
- return 0;
-}
-
-int __devinit mantis_dvb_init(struct mantis_pci *mantis)
-{
- struct mantis_hwconfig *config = mantis->hwconfig;
- int result = -1;
-
- dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter");
-
- result = dvb_register_adapter(&mantis->dvb_adapter,
- "Mantis DVB adapter",
- THIS_MODULE,
- &mantis->pdev->dev,
- adapter_nr);
-
- if (result < 0) {
-
- dprintk(MANTIS_ERROR, 1, "Error registering adapter");
- return -ENODEV;
- }
-
- mantis->dvb_adapter.priv = mantis;
- mantis->demux.dmx.capabilities = DMX_TS_FILTERING |
- DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
-
- mantis->demux.priv = mantis;
- mantis->demux.filternum = 256;
- mantis->demux.feednum = 256;
- mantis->demux.start_feed = mantis_dvb_start_feed;
- mantis->demux.stop_feed = mantis_dvb_stop_feed;
- mantis->demux.write_to_decoder = NULL;
-
- dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init");
- result = dvb_dmx_init(&mantis->demux);
- if (result < 0) {
- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
-
- goto err0;
- }
-
- mantis->dmxdev.filternum = 256;
- mantis->dmxdev.demux = &mantis->demux.dmx;
- mantis->dmxdev.capabilities = 0;
- dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init");
-
- result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter);
- if (result < 0) {
-
- dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
- goto err1;
- }
-
- mantis->fe_hw.source = DMX_FRONTEND_0;
- result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw);
- if (result < 0) {
-
- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
- goto err2;
- }
-
- mantis->fe_mem.source = DMX_MEMORY_FE;
- result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem);
- if (result < 0) {
- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
- goto err3;
- }
-
- result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw);
- if (result < 0) {
- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
- goto err4;
- }
-
- dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx);
- tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis);
- tasklet_disable(&mantis->tasklet);
- if (mantis->hwconfig) {
- result = config->frontend_init(mantis, mantis->fe);
- if (result < 0) {
- dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!");
- goto err5;
- } else {
- if (mantis->fe == NULL) {
- dprintk(MANTIS_ERROR, 1, "FE <NULL>");
- goto err5;
- }
-
- if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) {
- dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed");
-
- if (mantis->fe->ops.release)
- mantis->fe->ops.release(mantis->fe);
-
- mantis->fe = NULL;
- goto err5;
- }
- }
- }
-
- return 0;
-
- /* Error conditions .. */
-err5:
- tasklet_kill(&mantis->tasklet);
- dvb_net_release(&mantis->dvbnet);
- dvb_unregister_frontend(mantis->fe);
- dvb_frontend_detach(mantis->fe);
-err4:
- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
-
-err3:
- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
-
-err2:
- dvb_dmxdev_release(&mantis->dmxdev);
-
-err1:
- dvb_dmx_release(&mantis->demux);
-
-err0:
- dvb_unregister_adapter(&mantis->dvb_adapter);
-
- return result;
-}
-EXPORT_SYMBOL_GPL(mantis_dvb_init);
-
-int __devexit mantis_dvb_exit(struct mantis_pci *mantis)
-{
- int err;
-
- if (mantis->fe) {
- /* mantis_ca_exit(mantis); */
- err = mantis_frontend_shutdown(mantis);
- if (err != 0)
- dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err);
- dvb_unregister_frontend(mantis->fe);
- dvb_frontend_detach(mantis->fe);
- }
-
- tasklet_kill(&mantis->tasklet);
- dvb_net_release(&mantis->dvbnet);
-
- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
-
- dvb_dmxdev_release(&mantis->dmxdev);
- dvb_dmx_release(&mantis->demux);
-
- dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter");
- dvb_unregister_adapter(&mantis->dvb_adapter);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(mantis_dvb_exit);
diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig
deleted file mode 100644
index 64c84702ba5c..000000000000
--- a/drivers/media/dvb/ngene/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-config DVB_NGENE
- tristate "Micronas nGene support"
- depends on DVB_CORE && PCI && I2C
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_STV6110x if !DVB_FE_CUSTOMISE
- select DVB_STV090x if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_DRXK if !DVB_FE_CUSTOMISE
- select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
- ---help---
- Support for Micronas PCI express cards with nGene bridge.
-
diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile
deleted file mode 100644
index 13ebeffb705f..000000000000
--- a/drivers/media/dvb/ngene/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-#
-# Makefile for the nGene device driver
-#
-
-ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o
-
-obj-$(CONFIG_DVB_NGENE) += ngene.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners/
-
-# For the staging CI driver cxd2099
-ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
deleted file mode 100644
index 72ee8de02260..000000000000
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*
- * ngene-cards.c: nGene PCIe bridge driver - card specific info
- *
- * Copyright (C) 2005-2007 Micronas
- *
- * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
- * Modifications for new nGene firmware,
- * support for EEPROM-copying,
- * support for new dual DVB-S2 card prototype
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-
-#include "ngene.h"
-
-/* demods/tuners */
-#include "stv6110x.h"
-#include "stv090x.h"
-#include "lnbh24.h"
-#include "lgdt330x.h"
-#include "mt2131.h"
-#include "tda18271c2dd.h"
-#include "drxk.h"
-
-
-/****************************************************************************/
-/* Demod/tuner attachment ***************************************************/
-/****************************************************************************/
-
-static int tuner_attach_stv6110(struct ngene_channel *chan)
-{
- struct i2c_adapter *i2c;
- struct stv090x_config *feconf = (struct stv090x_config *)
- chan->dev->card_info->fe_config[chan->number];
- struct stv6110x_config *tunerconf = (struct stv6110x_config *)
- chan->dev->card_info->tuner_config[chan->number];
- struct stv6110x_devctl *ctl;
-
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
- ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
- if (ctl == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
- return -ENODEV;
- }
-
- feconf->tuner_init = ctl->tuner_init;
- feconf->tuner_sleep = ctl->tuner_sleep;
- feconf->tuner_set_mode = ctl->tuner_set_mode;
- feconf->tuner_set_frequency = ctl->tuner_set_frequency;
- feconf->tuner_get_frequency = ctl->tuner_get_frequency;
- feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
- feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
- feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
- feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
- feconf->tuner_set_refclk = ctl->tuner_set_refclk;
- feconf->tuner_get_status = ctl->tuner_get_status;
-
- return 0;
-}
-
-
-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct ngene_channel *chan = fe->sec_priv;
- int status;
-
- if (enable) {
- down(&chan->dev->pll_mutex);
- status = chan->gate_ctrl(fe, 1);
- } else {
- status = chan->gate_ctrl(fe, 0);
- up(&chan->dev->pll_mutex);
- }
- return status;
-}
-
-static int tuner_attach_tda18271(struct ngene_channel *chan)
-{
- struct i2c_adapter *i2c;
- struct dvb_frontend *fe;
-
- i2c = &chan->dev->channel[0].i2c_adapter;
- if (chan->fe->ops.i2c_gate_ctrl)
- chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
- fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
- if (chan->fe->ops.i2c_gate_ctrl)
- chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
- if (!fe) {
- printk(KERN_ERR "No TDA18271 found!\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int tuner_attach_probe(struct ngene_channel *chan)
-{
- if (chan->demod_type == 0)
- return tuner_attach_stv6110(chan);
- if (chan->demod_type == 1)
- return tuner_attach_tda18271(chan);
- return -EINVAL;
-}
-
-static int demod_attach_stv0900(struct ngene_channel *chan)
-{
- struct i2c_adapter *i2c;
- struct stv090x_config *feconf = (struct stv090x_config *)
- chan->dev->card_info->fe_config[chan->number];
-
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
- /* Note: Both adapters share the same i2c bus, but the demod */
- /* driver requires that each demod has its own i2c adapter */
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
- chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
- (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
- : STV090x_DEMODULATOR_1);
- if (chan->fe == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
- return -ENODEV;
- }
-
- /* store channel info */
- if (feconf->tuner_i2c_lock)
- chan->fe->analog_demod_priv = chan;
-
- if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
- 0, chan->dev->card_info->lnb[chan->number])) {
- printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
- dvb_frontend_detach(chan->fe);
- chan->fe = NULL;
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
-{
- struct ngene_channel *chan = fe->analog_demod_priv;
-
- if (lock)
- down(&chan->dev->pll_mutex);
- else
- up(&chan->dev->pll_mutex);
-}
-
-static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
-{
- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1 } };
- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
-}
-
-static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
- u16 reg, u8 *val)
-{
- u8 msg[2] = {reg>>8, reg&0xff};
- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
- .buf = msg, .len = 2},
- {.addr = adr, .flags = I2C_M_RD,
- .buf = val, .len = 1} };
- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
-}
-
-static int port_has_stv0900(struct i2c_adapter *i2c, int port)
-{
- u8 val;
- if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
- return 0;
- return 1;
-}
-
-static int port_has_drxk(struct i2c_adapter *i2c, int port)
-{
- u8 val;
-
- if (i2c_read(i2c, 0x29+port, &val) < 0)
- return 0;
- return 1;
-}
-
-static int demod_attach_drxk(struct ngene_channel *chan,
- struct i2c_adapter *i2c)
-{
- struct drxk_config config;
-
- memset(&config, 0, sizeof(config));
- config.microcode_name = "drxk_a3.mc";
- config.qam_demod_parameter_count = 4;
- config.adr = 0x29 + (chan->number ^ 2);
-
- chan->fe = dvb_attach(drxk_attach, &config, i2c);
- if (!chan->fe) {
- printk(KERN_ERR "No DRXK found!\n");
- return -ENODEV;
- }
- chan->fe->sec_priv = chan;
- chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
- chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
- return 0;
-}
-
-static int cineS2_probe(struct ngene_channel *chan)
-{
- struct i2c_adapter *i2c;
- struct stv090x_config *fe_conf;
- u8 buf[3];
- struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
- int rc;
-
- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
- if (chan->number < 2)
- i2c = &chan->dev->channel[0].i2c_adapter;
- else
- i2c = &chan->dev->channel[1].i2c_adapter;
-
- if (port_has_stv0900(i2c, chan->number)) {
- chan->demod_type = 0;
- fe_conf = chan->dev->card_info->fe_config[chan->number];
- /* demod found, attach it */
- rc = demod_attach_stv0900(chan);
- if (rc < 0 || chan->number < 2)
- return rc;
-
- /* demod #2: reprogram outputs DPN1 & DPN2 */
- i2c_msg.addr = fe_conf->address;
- i2c_msg.len = 3;
- buf[0] = 0xf1;
- switch (chan->number) {
- case 2:
- buf[1] = 0x5c;
- buf[2] = 0xc2;
- break;
- case 3:
- buf[1] = 0x61;
- buf[2] = 0xcc;
- break;
- default:
- return -ENODEV;
- }
- rc = i2c_transfer(i2c, &i2c_msg, 1);
- if (rc != 1) {
- printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
- return -EIO;
- }
- } else if (port_has_drxk(i2c, chan->number^2)) {
- chan->demod_type = 1;
- demod_attach_drxk(chan, i2c);
- } else {
- printk(KERN_ERR "No demod found on chan %d\n", chan->number);
- return -ENODEV;
- }
- return 0;
-}
-
-
-static struct lgdt330x_config aver_m780 = {
- .demod_address = 0xb2 >> 1,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x00, /* PARALLEL */
- .clock_polarity_flip = 1,
-};
-
-static struct mt2131_config m780_tunerconfig = {
- 0xc0 >> 1
-};
-
-/* A single func to attach the demo and tuner, rather than
- * use two sep funcs like the current design mandates.
- */
-static int demod_attach_lg330x(struct ngene_channel *chan)
-{
- chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
- if (chan->fe == NULL) {
- printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n");
- return -ENODEV;
- }
-
- dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
- &m780_tunerconfig, 0);
-
- return (chan->fe) ? 0 : -ENODEV;
-}
-
-/****************************************************************************/
-/* Switch control (I2C gates, etc.) *****************************************/
-/****************************************************************************/
-
-
-static struct stv090x_config fe_cineS2 = {
- .device = STV0900,
- .demod_mode = STV090x_DUAL,
- .clk_mode = STV090x_CLK_EXT,
-
- .xtal = 27000000,
- .address = 0x68,
-
- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
-
- .repeater_level = STV090x_RPTLEVEL_16,
-
- .adc1_range = STV090x_ADC_1Vpp,
- .adc2_range = STV090x_ADC_1Vpp,
-
- .diseqc_envelope_mode = true,
-
- .tuner_i2c_lock = cineS2_tuner_i2c_lock,
-};
-
-static struct stv090x_config fe_cineS2_2 = {
- .device = STV0900,
- .demod_mode = STV090x_DUAL,
- .clk_mode = STV090x_CLK_EXT,
-
- .xtal = 27000000,
- .address = 0x69,
-
- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
-
- .repeater_level = STV090x_RPTLEVEL_16,
-
- .adc1_range = STV090x_ADC_1Vpp,
- .adc2_range = STV090x_ADC_1Vpp,
-
- .diseqc_envelope_mode = true,
-
- .tuner_i2c_lock = cineS2_tuner_i2c_lock,
-};
-
-static struct stv6110x_config tuner_cineS2_0 = {
- .addr = 0x60,
- .refclk = 27000000,
- .clk_div = 1,
-};
-
-static struct stv6110x_config tuner_cineS2_1 = {
- .addr = 0x63,
- .refclk = 27000000,
- .clk_div = 1,
-};
-
-static struct ngene_info ngene_info_cineS2 = {
- .type = NGENE_SIDEWINDER,
- .name = "Linux4Media cineS2 DVB-S2 Twin Tuner",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
- .fe_config = {&fe_cineS2, &fe_cineS2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0b, 0x08},
- .tsf = {3, 3},
- .fw_version = 18,
- .msi_supported = true,
-};
-
-static struct ngene_info ngene_info_satixS2 = {
- .type = NGENE_SIDEWINDER,
- .name = "Mystique SaTiX-S2 Dual",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
- .fe_config = {&fe_cineS2, &fe_cineS2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0b, 0x08},
- .tsf = {3, 3},
- .fw_version = 18,
- .msi_supported = true,
-};
-
-static struct ngene_info ngene_info_satixS2v2 = {
- .type = NGENE_SIDEWINDER,
- .name = "Mystique SaTiX-S2 Dual (v2)",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
- NGENE_IO_TSOUT},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08, 0x0b, 0x09},
- .tsf = {3, 3},
- .fw_version = 18,
- .msi_supported = true,
-};
-
-static struct ngene_info ngene_info_cineS2v5 = {
- .type = NGENE_SIDEWINDER,
- .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
- NGENE_IO_TSOUT},
- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08, 0x0b, 0x09},
- .tsf = {3, 3},
- .fw_version = 18,
- .msi_supported = true,
-};
-
-
-static struct ngene_info ngene_info_duoFlex = {
- .type = NGENE_SIDEWINDER,
- .name = "Digital Devices DuoFlex PCIe or miniPCIe",
- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
- NGENE_IO_TSOUT},
- .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
- .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
- .lnb = {0x0a, 0x08, 0x0b, 0x09},
- .tsf = {3, 3},
- .fw_version = 18,
- .msi_supported = true,
-};
-
-static struct ngene_info ngene_info_m780 = {
- .type = NGENE_APP,
- .name = "Aver M780 ATSC/QAM-B",
-
- /* Channel 0 is analog, which is currently unsupported */
- .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN },
- .demod_attach = { NULL, demod_attach_lg330x },
-
- /* Ensure these are NULL else the frame will call them (as funcs) */
- .tuner_attach = { 0, 0, 0, 0 },
- .fe_config = { NULL, &aver_m780 },
- .avf = { 0 },
-
- /* A custom electrical interface config for the demod to bridge */
- .tsf = { 4, 4 },
- .fw_version = 15,
-};
-
-/****************************************************************************/
-
-
-
-/****************************************************************************/
-/* PCI Subsystem ID *********************************************************/
-/****************************************************************************/
-
-#define NGENE_ID(_subvend, _subdev, _driverdata) { \
- .vendor = NGENE_VID, .device = NGENE_PID, \
- .subvendor = _subvend, .subdevice = _subdev, \
- .driver_data = (unsigned long) &_driverdata }
-
-/****************************************************************************/
-
-static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
- NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
- NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
- NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
- NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
- NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
- NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
- NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
- NGENE_ID(0x1461, 0x062e, ngene_info_m780),
- {0}
-};
-MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
-
-/****************************************************************************/
-/* Init/Exit ****************************************************************/
-/****************************************************************************/
-
-static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
- enum pci_channel_state state)
-{
- printk(KERN_ERR DEVICE_NAME ": PCI error\n");
- if (state == pci_channel_io_perm_failure)
- return PCI_ERS_RESULT_DISCONNECT;
- if (state == pci_channel_io_frozen)
- return PCI_ERS_RESULT_NEED_RESET;
- return PCI_ERS_RESULT_CAN_RECOVER;
-}
-
-static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
-{
- printk(KERN_INFO DEVICE_NAME ": link reset\n");
- return 0;
-}
-
-static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
-{
- printk(KERN_INFO DEVICE_NAME ": slot reset\n");
- return 0;
-}
-
-static void ngene_resume(struct pci_dev *dev)
-{
- printk(KERN_INFO DEVICE_NAME ": resume\n");
-}
-
-static struct pci_error_handlers ngene_errors = {
- .error_detected = ngene_error_detected,
- .link_reset = ngene_link_reset,
- .slot_reset = ngene_slot_reset,
- .resume = ngene_resume,
-};
-
-static struct pci_driver ngene_pci_driver = {
- .name = "ngene",
- .id_table = ngene_id_tbl,
- .probe = ngene_probe,
- .remove = __devexit_p(ngene_remove),
- .err_handler = &ngene_errors,
- .shutdown = ngene_shutdown,
-};
-
-static __init int module_init_ngene(void)
-{
- printk(KERN_INFO
- "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
- return pci_register_driver(&ngene_pci_driver);
-}
-
-static __exit void module_exit_ngene(void)
-{
- pci_unregister_driver(&ngene_pci_driver);
-}
-
-module_init(module_init_ngene);
-module_exit(module_exit_ngene);
-
-MODULE_DESCRIPTION("nGene");
-MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
deleted file mode 100644
index 39857384af10..000000000000
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ /dev/null
@@ -1,1713 +0,0 @@
-/*
- * ngene.c: nGene PCIe bridge driver
- *
- * Copyright (C) 2005-2007 Micronas
- *
- * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
- * Modifications for new nGene firmware,
- * support for EEPROM-copying,
- * support for new dual DVB-S2 card prototype
- *
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 only, as published by the Free Software Foundation.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/poll.h>
-#include <linux/io.h>
-#include <asm/div64.h>
-#include <linux/pci.h>
-#include <linux/timer.h>
-#include <linux/byteorder/generic.h>
-#include <linux/firmware.h>
-#include <linux/vmalloc.h>
-
-#include "ngene.h"
-
-static int one_adapter;
-module_param(one_adapter, int, 0444);
-MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
-
-static int shutdown_workaround;
-module_param(shutdown_workaround, int, 0644);
-MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets.");
-
-static int debug;
-module_param(debug, int, 0444);
-MODULE_PARM_DESC(debug, "Print debugging information.");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk if (debug) printk
-
-#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
-#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr)))
-#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
-#define ngreadl(adr) readl(dev->iomem + (adr))
-#define ngreadb(adr) readb(dev->iomem + (adr))
-#define ngcpyto(adr, src, count) memcpy_toio((char *) \
- (dev->iomem + (adr)), (src), (count))
-#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
- (dev->iomem + (adr)), (count))
-
-/****************************************************************************/
-/* nGene interrupt handler **************************************************/
-/****************************************************************************/
-
-static void event_tasklet(unsigned long data)
-{
- struct ngene *dev = (struct ngene *)data;
-
- while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
- struct EVENT_BUFFER Event =
- dev->EventQueue[dev->EventQueueReadIndex];
- dev->EventQueueReadIndex =
- (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
-
- if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
- dev->TxEventNotify(dev, Event.TimeStamp);
- if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
- dev->RxEventNotify(dev, Event.TimeStamp,
- Event.RXCharacter);
- }
-}
-
-static void demux_tasklet(unsigned long data)
-{
- struct ngene_channel *chan = (struct ngene_channel *)data;
- struct SBufferHeader *Cur = chan->nextBuffer;
-
- spin_lock_irq(&chan->state_lock);
-
- while (Cur->ngeneBuffer.SR.Flags & 0x80) {
- if (chan->mode & NGENE_IO_TSOUT) {
- u32 Flags = chan->DataFormatFlags;
- if (Cur->ngeneBuffer.SR.Flags & 0x20)
- Flags |= BEF_OVERFLOW;
- if (chan->pBufferExchange) {
- if (!chan->pBufferExchange(chan,
- Cur->Buffer1,
- chan->Capture1Length,
- Cur->ngeneBuffer.SR.
- Clock, Flags)) {
- /*
- We didn't get data
- Clear in service flag to make sure we
- get called on next interrupt again.
- leave fill/empty (0x80) flag alone
- to avoid hardware running out of
- buffers during startup, we hold only
- in run state ( the source may be late
- delivering data )
- */
-
- if (chan->HWState == HWSTATE_RUN) {
- Cur->ngeneBuffer.SR.Flags &=
- ~0x40;
- break;
- /* Stop processing stream */
- }
- } else {
- /* We got a valid buffer,
- so switch to run state */
- chan->HWState = HWSTATE_RUN;
- }
- } else {
- printk(KERN_ERR DEVICE_NAME ": OOPS\n");
- if (chan->HWState == HWSTATE_RUN) {
- Cur->ngeneBuffer.SR.Flags &= ~0x40;
- break; /* Stop processing stream */
- }
- }
- if (chan->AudioDTOUpdated) {
- printk(KERN_INFO DEVICE_NAME
- ": Update AudioDTO = %d\n",
- chan->AudioDTOValue);
- Cur->ngeneBuffer.SR.DTOUpdate =
- chan->AudioDTOValue;
- chan->AudioDTOUpdated = 0;
- }
- } else {
- if (chan->HWState == HWSTATE_RUN) {
- u32 Flags = chan->DataFormatFlags;
- IBufferExchange *exch1 = chan->pBufferExchange;
- IBufferExchange *exch2 = chan->pBufferExchange2;
- if (Cur->ngeneBuffer.SR.Flags & 0x01)
- Flags |= BEF_EVEN_FIELD;
- if (Cur->ngeneBuffer.SR.Flags & 0x20)
- Flags |= BEF_OVERFLOW;
- spin_unlock_irq(&chan->state_lock);
- if (exch1)
- exch1(chan, Cur->Buffer1,
- chan->Capture1Length,
- Cur->ngeneBuffer.SR.Clock,
- Flags);
- if (exch2)
- exch2(chan, Cur->Buffer2,
- chan->Capture2Length,
- Cur->ngeneBuffer.SR.Clock,
- Flags);
- spin_lock_irq(&chan->state_lock);
- } else if (chan->HWState != HWSTATE_STOP)
- chan->HWState = HWSTATE_RUN;
- }
- Cur->ngeneBuffer.SR.Flags = 0x00;
- Cur = Cur->Next;
- }
- chan->nextBuffer = Cur;
-
- spin_unlock_irq(&chan->state_lock);
-}
-
-static irqreturn_t irq_handler(int irq, void *dev_id)
-{
- struct ngene *dev = (struct ngene *)dev_id;
- u32 icounts = 0;
- irqreturn_t rc = IRQ_NONE;
- u32 i = MAX_STREAM;
- u8 *tmpCmdDoneByte;
-
- if (dev->BootFirmware) {
- icounts = ngreadl(NGENE_INT_COUNTS);
- if (icounts != dev->icounts) {
- ngwritel(0, FORCE_NMI);
- dev->cmd_done = 1;
- wake_up(&dev->cmd_wq);
- dev->icounts = icounts;
- rc = IRQ_HANDLED;
- }
- return rc;
- }
-
- ngwritel(0, FORCE_NMI);
-
- spin_lock(&dev->cmd_lock);
- tmpCmdDoneByte = dev->CmdDoneByte;
- if (tmpCmdDoneByte &&
- (*tmpCmdDoneByte ||
- (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
- dev->CmdDoneByte = NULL;
- dev->cmd_done = 1;
- wake_up(&dev->cmd_wq);
- rc = IRQ_HANDLED;
- }
- spin_unlock(&dev->cmd_lock);
-
- if (dev->EventBuffer->EventStatus & 0x80) {
- u8 nextWriteIndex =
- (dev->EventQueueWriteIndex + 1) &
- (EVENT_QUEUE_SIZE - 1);
- if (nextWriteIndex != dev->EventQueueReadIndex) {
- dev->EventQueue[dev->EventQueueWriteIndex] =
- *(dev->EventBuffer);
- dev->EventQueueWriteIndex = nextWriteIndex;
- } else {
- printk(KERN_ERR DEVICE_NAME ": event overflow\n");
- dev->EventQueueOverflowCount += 1;
- dev->EventQueueOverflowFlag = 1;
- }
- dev->EventBuffer->EventStatus &= ~0x80;
- tasklet_schedule(&dev->event_tasklet);
- rc = IRQ_HANDLED;
- }
-
- while (i > 0) {
- i--;
- spin_lock(&dev->channel[i].state_lock);
- /* if (dev->channel[i].State>=KSSTATE_RUN) { */
- if (dev->channel[i].nextBuffer) {
- if ((dev->channel[i].nextBuffer->
- ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
- dev->channel[i].nextBuffer->
- ngeneBuffer.SR.Flags |= 0x40;
- tasklet_schedule(
- &dev->channel[i].demux_tasklet);
- rc = IRQ_HANDLED;
- }
- }
- spin_unlock(&dev->channel[i].state_lock);
- }
-
- /* Request might have been processed by a previous call. */
- return IRQ_HANDLED;
-}
-
-/****************************************************************************/
-/* nGene command interface **************************************************/
-/****************************************************************************/
-
-static void dump_command_io(struct ngene *dev)
-{
- u8 buf[8], *b;
-
- ngcpyfrom(buf, HOST_TO_NGENE, 8);
- printk(KERN_ERR "host_to_ngene (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
- HOST_TO_NGENE, buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7]);
-
- ngcpyfrom(buf, NGENE_TO_HOST, 8);
- printk(KERN_ERR "ngene_to_host (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n",
- NGENE_TO_HOST, buf[0], buf[1], buf[2], buf[3],
- buf[4], buf[5], buf[6], buf[7]);
-
- b = dev->hosttongene;
- printk(KERN_ERR "dev->hosttongene (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
- b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
-
- b = dev->ngenetohost;
- printk(KERN_ERR "dev->ngenetohost (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n",
- b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
-}
-
-static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
-{
- int ret;
- u8 *tmpCmdDoneByte;
-
- dev->cmd_done = 0;
-
- if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
- dev->BootFirmware = 1;
- dev->icounts = ngreadl(NGENE_INT_COUNTS);
- ngwritel(0, NGENE_COMMAND);
- ngwritel(0, NGENE_COMMAND_HI);
- ngwritel(0, NGENE_STATUS);
- ngwritel(0, NGENE_STATUS_HI);
- ngwritel(0, NGENE_EVENT);
- ngwritel(0, NGENE_EVENT_HI);
- } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
- u64 fwio = dev->PAFWInterfaceBuffer;
-
- ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
- ngwritel(fwio >> 32, NGENE_COMMAND_HI);
- ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
- ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
- ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
- ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
- }
-
- memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
-
- if (dev->BootFirmware)
- ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
-
- spin_lock_irq(&dev->cmd_lock);
- tmpCmdDoneByte = dev->ngenetohost + com->out_len;
- if (!com->out_len)
- tmpCmdDoneByte++;
- *tmpCmdDoneByte = 0;
- dev->ngenetohost[0] = 0;
- dev->ngenetohost[1] = 0;
- dev->CmdDoneByte = tmpCmdDoneByte;
- spin_unlock_irq(&dev->cmd_lock);
-
- /* Notify 8051. */
- ngwritel(1, FORCE_INT);
-
- ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
- if (!ret) {
- /*ngwritel(0, FORCE_NMI);*/
-
- printk(KERN_ERR DEVICE_NAME
- ": Command timeout cmd=%02x prev=%02x\n",
- com->cmd.hdr.Opcode, dev->prev_cmd);
- dump_command_io(dev);
- return -1;
- }
- if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
- dev->BootFirmware = 0;
-
- dev->prev_cmd = com->cmd.hdr.Opcode;
-
- if (!com->out_len)
- return 0;
-
- memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
-
- return 0;
-}
-
-int ngene_command(struct ngene *dev, struct ngene_command *com)
-{
- int result;
-
- down(&dev->cmd_mutex);
- result = ngene_command_mutex(dev, com);
- up(&dev->cmd_mutex);
- return result;
-}
-
-
-static int ngene_command_load_firmware(struct ngene *dev,
- u8 *ngene_fw, u32 size)
-{
-#define FIRSTCHUNK (1024)
- u32 cleft;
- struct ngene_command com;
-
- com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
- com.cmd.hdr.Length = 0;
- com.in_len = 0;
- com.out_len = 0;
-
- ngene_command(dev, &com);
-
- cleft = (size + 3) & ~3;
- if (cleft > FIRSTCHUNK) {
- ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
- cleft - FIRSTCHUNK);
- cleft = FIRSTCHUNK;
- }
- ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
-
- memset(&com, 0, sizeof(struct ngene_command));
- com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
- com.cmd.hdr.Length = 4;
- com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
- com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
- com.in_len = 4;
- com.out_len = 0;
-
- return ngene_command(dev, &com);
-}
-
-
-static int ngene_command_config_buf(struct ngene *dev, u8 config)
-{
- struct ngene_command com;
-
- com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
- com.cmd.hdr.Length = 1;
- com.cmd.ConfigureBuffers.config = config;
- com.in_len = 1;
- com.out_len = 0;
-
- if (ngene_command(dev, &com) < 0)
- return -EIO;
- return 0;
-}
-
-static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
-{
- struct ngene_command com;
-
- com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
- com.cmd.hdr.Length = 6;
- memcpy(&com.cmd.ConfigureBuffers.config, config, 6);
- com.in_len = 6;
- com.out_len = 0;
-
- if (ngene_command(dev, &com) < 0)
- return -EIO;
-
- return 0;
-}
-
-int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
-{
- struct ngene_command com;
-
- com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
- com.cmd.hdr.Length = 1;
- com.cmd.SetGpioPin.select = select | (level << 7);
- com.in_len = 1;
- com.out_len = 0;
-
- return ngene_command(dev, &com);
-}
-
-
-/*
- 02000640 is sample on rising edge.
- 02000740 is sample on falling edge.
- 02000040 is ignore "valid" signal
-
- 0: FD_CTL1 Bit 7,6 must be 0,1
- 7 disable(fw controlled)
- 6 0-AUX,1-TS
- 5 0-par,1-ser
- 4 0-lsb/1-msb
- 3,2 reserved
- 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
- 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
- 2: FD_STA is read-only. 0-sync
- 3: FD_INSYNC is number of 47s to trigger "in sync".
- 4: FD_OUTSYNC is number of 47s to trigger "out of sync".
- 5: FD_MAXBYTE1 is low-order of bytes per packet.
- 6: FD_MAXBYTE2 is high-order of bytes per packet.
- 7: Top byte is unused.
-*/
-
-/****************************************************************************/
-
-static u8 TSFeatureDecoderSetup[8 * 5] = {
- 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
- 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */
- 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */
- 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
- 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
-};
-
-/* Set NGENE I2S Config to 16 bit packed */
-static u8 I2SConfiguration[] = {
- 0x00, 0x10, 0x00, 0x00,
- 0x80, 0x10, 0x00, 0x00,
-};
-
-static u8 SPDIFConfiguration[10] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-/* Set NGENE I2S Config to transport stream compatible mode */
-
-static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 };
-
-static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 };
-
-static u8 ITUDecoderSetup[4][16] = {
- {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */
- 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
- {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
- {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */
- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
- {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */
- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
-};
-
-/*
- * 50 48 60 gleich
- * 27p50 9f 00 22 80 42 69 18 ...
- * 27p60 93 00 22 80 82 69 1c ...
- */
-
-/* Maxbyte to 1144 (for raw data) */
-static u8 ITUFeatureDecoderSetup[8] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
-};
-
-void FillTSBuffer(void *Buffer, int Length, u32 Flags)
-{
- u32 *ptr = Buffer;
-
- memset(Buffer, TS_FILLER, Length);
- while (Length > 0) {
- if (Flags & DF_SWAP32)
- *ptr = 0x471FFF10;
- else
- *ptr = 0x10FF1F47;
- ptr += (188 / 4);
- Length -= 188;
- }
-}
-
-
-static void flush_buffers(struct ngene_channel *chan)
-{
- u8 val;
-
- do {
- msleep(1);
- spin_lock_irq(&chan->state_lock);
- val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
- spin_unlock_irq(&chan->state_lock);
- } while (val);
-}
-
-static void clear_buffers(struct ngene_channel *chan)
-{
- struct SBufferHeader *Cur = chan->nextBuffer;
-
- do {
- memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
- if (chan->mode & NGENE_IO_TSOUT)
- FillTSBuffer(Cur->Buffer1,
- chan->Capture1Length,
- chan->DataFormatFlags);
- Cur = Cur->Next;
- } while (Cur != chan->nextBuffer);
-
- if (chan->mode & NGENE_IO_TSOUT) {
- chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
- chan->AudioDTOValue;
- chan->AudioDTOUpdated = 0;
-
- Cur = chan->TSIdleBuffer.Head;
-
- do {
- memset(&Cur->ngeneBuffer.SR, 0,
- sizeof(Cur->ngeneBuffer.SR));
- FillTSBuffer(Cur->Buffer1,
- chan->Capture1Length,
- chan->DataFormatFlags);
- Cur = Cur->Next;
- } while (Cur != chan->TSIdleBuffer.Head);
- }
-}
-
-static int ngene_command_stream_control(struct ngene *dev, u8 stream,
- u8 control, u8 mode, u8 flags)
-{
- struct ngene_channel *chan = &dev->channel[stream];
- struct ngene_command com;
- u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
- u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
- u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
- u16 BsSDO = 0x9B00;
-
- down(&dev->stream_mutex);
- memset(&com, 0, sizeof(com));
- com.cmd.hdr.Opcode = CMD_CONTROL;
- com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
- com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
- if (chan->mode & NGENE_IO_TSOUT)
- com.cmd.StreamControl.Stream |= 0x07;
- com.cmd.StreamControl.Control = control |
- (flags & SFLAG_ORDER_LUMA_CHROMA);
- com.cmd.StreamControl.Mode = mode;
- com.in_len = sizeof(struct FW_STREAM_CONTROL);
- com.out_len = 0;
-
- dprintk(KERN_INFO DEVICE_NAME
- ": Stream=%02x, Control=%02x, Mode=%02x\n",
- com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
- com.cmd.StreamControl.Mode);
-
- chan->Mode = mode;
-
- if (!(control & 0x80)) {
- spin_lock_irq(&chan->state_lock);
- if (chan->State == KSSTATE_RUN) {
- chan->State = KSSTATE_ACQUIRE;
- chan->HWState = HWSTATE_STOP;
- spin_unlock_irq(&chan->state_lock);
- if (ngene_command(dev, &com) < 0) {
- up(&dev->stream_mutex);
- return -1;
- }
- /* clear_buffers(chan); */
- flush_buffers(chan);
- up(&dev->stream_mutex);
- return 0;
- }
- spin_unlock_irq(&chan->state_lock);
- up(&dev->stream_mutex);
- return 0;
- }
-
- if (mode & SMODE_AUDIO_CAPTURE) {
- com.cmd.StreamControl.CaptureBlockCount =
- chan->Capture1Length / AUDIO_BLOCK_SIZE;
- com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
- } else if (mode & SMODE_TRANSPORT_STREAM) {
- com.cmd.StreamControl.CaptureBlockCount =
- chan->Capture1Length / TS_BLOCK_SIZE;
- com.cmd.StreamControl.MaxLinesPerField =
- chan->Capture1Length / TS_BLOCK_SIZE;
- com.cmd.StreamControl.Buffer_Address =
- chan->TSRingBuffer.PAHead;
- if (chan->mode & NGENE_IO_TSOUT) {
- com.cmd.StreamControl.BytesPerVBILine =
- chan->Capture1Length / TS_BLOCK_SIZE;
- com.cmd.StreamControl.Stream |= 0x07;
- }
- } else {
- com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
- com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
- com.cmd.StreamControl.MinLinesPerField = 100;
- com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
-
- if (mode & SMODE_VBI_CAPTURE) {
- com.cmd.StreamControl.MaxVBILinesPerField =
- chan->nVBILines;
- com.cmd.StreamControl.MinVBILinesPerField = 0;
- com.cmd.StreamControl.BytesPerVBILine =
- chan->nBytesPerVBILine;
- }
- if (flags & SFLAG_COLORBAR)
- com.cmd.StreamControl.Stream |= 0x04;
- }
-
- spin_lock_irq(&chan->state_lock);
- if (mode & SMODE_AUDIO_CAPTURE) {
- chan->nextBuffer = chan->RingBuffer.Head;
- if (mode & SMODE_AUDIO_SPDIF) {
- com.cmd.StreamControl.SetupDataLen =
- sizeof(SPDIFConfiguration);
- com.cmd.StreamControl.SetupDataAddr = BsSPI;
- memcpy(com.cmd.StreamControl.SetupData,
- SPDIFConfiguration, sizeof(SPDIFConfiguration));
- } else {
- com.cmd.StreamControl.SetupDataLen = 4;
- com.cmd.StreamControl.SetupDataAddr = BsSDI;
- memcpy(com.cmd.StreamControl.SetupData,
- I2SConfiguration +
- 4 * dev->card_info->i2s[stream], 4);
- }
- } else if (mode & SMODE_TRANSPORT_STREAM) {
- chan->nextBuffer = chan->TSRingBuffer.Head;
- if (stream >= STREAM_AUDIOIN1) {
- if (chan->mode & NGENE_IO_TSOUT) {
- com.cmd.StreamControl.SetupDataLen =
- sizeof(TS_I2SOutConfiguration);
- com.cmd.StreamControl.SetupDataAddr = BsSDO;
- memcpy(com.cmd.StreamControl.SetupData,
- TS_I2SOutConfiguration,
- sizeof(TS_I2SOutConfiguration));
- } else {
- com.cmd.StreamControl.SetupDataLen =
- sizeof(TS_I2SConfiguration);
- com.cmd.StreamControl.SetupDataAddr = BsSDI;
- memcpy(com.cmd.StreamControl.SetupData,
- TS_I2SConfiguration,
- sizeof(TS_I2SConfiguration));
- }
- } else {
- com.cmd.StreamControl.SetupDataLen = 8;
- com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
- memcpy(com.cmd.StreamControl.SetupData,
- TSFeatureDecoderSetup +
- 8 * dev->card_info->tsf[stream], 8);
- }
- } else {
- chan->nextBuffer = chan->RingBuffer.Head;
- com.cmd.StreamControl.SetupDataLen =
- 16 + sizeof(ITUFeatureDecoderSetup);
- com.cmd.StreamControl.SetupDataAddr = BsUVI;
- memcpy(com.cmd.StreamControl.SetupData,
- ITUDecoderSetup[chan->itumode], 16);
- memcpy(com.cmd.StreamControl.SetupData + 16,
- ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
- }
- clear_buffers(chan);
- chan->State = KSSTATE_RUN;
- if (mode & SMODE_TRANSPORT_STREAM)
- chan->HWState = HWSTATE_RUN;
- else
- chan->HWState = HWSTATE_STARTUP;
- spin_unlock_irq(&chan->state_lock);
-
- if (ngene_command(dev, &com) < 0) {
- up(&dev->stream_mutex);
- return -1;
- }
- up(&dev->stream_mutex);
- return 0;
-}
-
-void set_transfer(struct ngene_channel *chan, int state)
-{
- u8 control = 0, mode = 0, flags = 0;
- struct ngene *dev = chan->dev;
- int ret;
-
- /*
- printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
- msleep(100);
- */
-
- if (state) {
- if (chan->running) {
- printk(KERN_INFO DEVICE_NAME ": already running\n");
- return;
- }
- } else {
- if (!chan->running) {
- printk(KERN_INFO DEVICE_NAME ": already stopped\n");
- return;
- }
- }
-
- if (dev->card_info->switch_ctrl)
- dev->card_info->switch_ctrl(chan, 1, state ^ 1);
-
- if (state) {
- spin_lock_irq(&chan->state_lock);
-
- /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
- ngreadl(0x9310)); */
- dvb_ringbuffer_flush(&dev->tsout_rbuf);
- control = 0x80;
- if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
- chan->Capture1Length = 512 * 188;
- mode = SMODE_TRANSPORT_STREAM;
- }
- if (chan->mode & NGENE_IO_TSOUT) {
- chan->pBufferExchange = tsout_exchange;
- /* 0x66666666 = 50MHz *2^33 /250MHz */
- chan->AudioDTOValue = 0x80000000;
- chan->AudioDTOUpdated = 1;
- }
- if (chan->mode & NGENE_IO_TSIN)
- chan->pBufferExchange = tsin_exchange;
- spin_unlock_irq(&chan->state_lock);
- } else
- ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
- ngreadl(0x9310)); */
-
- ret = ngene_command_stream_control(dev, chan->number,
- control, mode, flags);
- if (!ret)
- chan->running = state;
- else
- printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
- state);
- if (!state) {
- spin_lock_irq(&chan->state_lock);
- chan->pBufferExchange = NULL;
- dvb_ringbuffer_flush(&dev->tsout_rbuf);
- spin_unlock_irq(&chan->state_lock);
- }
-}
-
-
-/****************************************************************************/
-/* nGene hardware init and release functions ********************************/
-/****************************************************************************/
-
-static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
-{
- struct SBufferHeader *Cur = rb->Head;
- u32 j;
-
- if (!Cur)
- return;
-
- for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
- if (Cur->Buffer1)
- pci_free_consistent(dev->pci_dev,
- rb->Buffer1Length,
- Cur->Buffer1,
- Cur->scList1->Address);
-
- if (Cur->Buffer2)
- pci_free_consistent(dev->pci_dev,
- rb->Buffer2Length,
- Cur->Buffer2,
- Cur->scList2->Address);
- }
-
- if (rb->SCListMem)
- pci_free_consistent(dev->pci_dev, rb->SCListMemSize,
- rb->SCListMem, rb->PASCListMem);
-
- pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead);
-}
-
-static void free_idlebuffer(struct ngene *dev,
- struct SRingBufferDescriptor *rb,
- struct SRingBufferDescriptor *tb)
-{
- int j;
- struct SBufferHeader *Cur = tb->Head;
-
- if (!rb->Head)
- return;
- free_ringbuffer(dev, rb);
- for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
- Cur->Buffer2 = NULL;
- Cur->scList2 = NULL;
- Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
- Cur->ngeneBuffer.Number_of_entries_2 = 0;
- }
-}
-
-static void free_common_buffers(struct ngene *dev)
-{
- u32 i;
- struct ngene_channel *chan;
-
- for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
- chan = &dev->channel[i];
- free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
- free_ringbuffer(dev, &chan->RingBuffer);
- free_ringbuffer(dev, &chan->TSRingBuffer);
- }
-
- if (dev->OverflowBuffer)
- pci_free_consistent(dev->pci_dev,
- OVERFLOW_BUFFER_SIZE,
- dev->OverflowBuffer, dev->PAOverflowBuffer);
-
- if (dev->FWInterfaceBuffer)
- pci_free_consistent(dev->pci_dev,
- 4096,
- dev->FWInterfaceBuffer,
- dev->PAFWInterfaceBuffer);
-}
-
-/****************************************************************************/
-/* Ring buffer handling *****************************************************/
-/****************************************************************************/
-
-static int create_ring_buffer(struct pci_dev *pci_dev,
- struct SRingBufferDescriptor *descr, u32 NumBuffers)
-{
- dma_addr_t tmp;
- struct SBufferHeader *Head;
- u32 i;
- u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
- u64 PARingBufferHead;
- u64 PARingBufferCur;
- u64 PARingBufferNext;
- struct SBufferHeader *Cur, *Next;
-
- descr->Head = NULL;
- descr->MemSize = 0;
- descr->PAHead = 0;
- descr->NumBuffers = 0;
-
- if (MemSize < 4096)
- MemSize = 4096;
-
- Head = pci_alloc_consistent(pci_dev, MemSize, &tmp);
- PARingBufferHead = tmp;
-
- if (!Head)
- return -ENOMEM;
-
- memset(Head, 0, MemSize);
-
- PARingBufferCur = PARingBufferHead;
- Cur = Head;
-
- for (i = 0; i < NumBuffers - 1; i++) {
- Next = (struct SBufferHeader *)
- (((u8 *) Cur) + SIZEOF_SBufferHeader);
- PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
- Cur->Next = Next;
- Cur->ngeneBuffer.Next = PARingBufferNext;
- Cur = Next;
- PARingBufferCur = PARingBufferNext;
- }
- /* Last Buffer points back to first one */
- Cur->Next = Head;
- Cur->ngeneBuffer.Next = PARingBufferHead;
-
- descr->Head = Head;
- descr->MemSize = MemSize;
- descr->PAHead = PARingBufferHead;
- descr->NumBuffers = NumBuffers;
-
- return 0;
-}
-
-static int AllocateRingBuffers(struct pci_dev *pci_dev,
- dma_addr_t of,
- struct SRingBufferDescriptor *pRingBuffer,
- u32 Buffer1Length, u32 Buffer2Length)
-{
- dma_addr_t tmp;
- u32 i, j;
- int status = 0;
- u32 SCListMemSize = pRingBuffer->NumBuffers
- * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
- NUM_SCATTER_GATHER_ENTRIES)
- * sizeof(struct HW_SCATTER_GATHER_ELEMENT);
-
- u64 PASCListMem;
- struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
- u64 PASCListEntry;
- struct SBufferHeader *Cur;
- void *SCListMem;
-
- if (SCListMemSize < 4096)
- SCListMemSize = 4096;
-
- SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp);
-
- PASCListMem = tmp;
- if (SCListMem == NULL)
- return -ENOMEM;
-
- memset(SCListMem, 0, SCListMemSize);
-
- pRingBuffer->SCListMem = SCListMem;
- pRingBuffer->PASCListMem = PASCListMem;
- pRingBuffer->SCListMemSize = SCListMemSize;
- pRingBuffer->Buffer1Length = Buffer1Length;
- pRingBuffer->Buffer2Length = Buffer2Length;
-
- SCListEntry = SCListMem;
- PASCListEntry = PASCListMem;
- Cur = pRingBuffer->Head;
-
- for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
- u64 PABuffer;
-
- void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length,
- &tmp);
- PABuffer = tmp;
-
- if (Buffer == NULL)
- return -ENOMEM;
-
- Cur->Buffer1 = Buffer;
-
- SCListEntry->Address = PABuffer;
- SCListEntry->Length = Buffer1Length;
-
- Cur->scList1 = SCListEntry;
- Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
- Cur->ngeneBuffer.Number_of_entries_1 =
- NUM_SCATTER_GATHER_ENTRIES;
-
- SCListEntry += 1;
- PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
-
-#if NUM_SCATTER_GATHER_ENTRIES > 1
- for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
- SCListEntry->Address = of;
- SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
- SCListEntry += 1;
- PASCListEntry +=
- sizeof(struct HW_SCATTER_GATHER_ELEMENT);
- }
-#endif
-
- if (!Buffer2Length)
- continue;
-
- Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp);
- PABuffer = tmp;
-
- if (Buffer == NULL)
- return -ENOMEM;
-
- Cur->Buffer2 = Buffer;
-
- SCListEntry->Address = PABuffer;
- SCListEntry->Length = Buffer2Length;
-
- Cur->scList2 = SCListEntry;
- Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
- Cur->ngeneBuffer.Number_of_entries_2 =
- NUM_SCATTER_GATHER_ENTRIES;
-
- SCListEntry += 1;
- PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
-
-#if NUM_SCATTER_GATHER_ENTRIES > 1
- for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
- SCListEntry->Address = of;
- SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
- SCListEntry += 1;
- PASCListEntry +=
- sizeof(struct HW_SCATTER_GATHER_ELEMENT);
- }
-#endif
-
- }
-
- return status;
-}
-
-static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
- struct SRingBufferDescriptor *pRingBuffer)
-{
- int status = 0;
-
- /* Copy pointer to scatter gather list in TSRingbuffer
- structure for buffer 2
- Load number of buffer
- */
- u32 n = pRingBuffer->NumBuffers;
-
- /* Point to first buffer entry */
- struct SBufferHeader *Cur = pRingBuffer->Head;
- int i;
- /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
- for (i = 0; i < n; i++) {
- Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
- Cur->scList2 = pIdleBuffer->Head->scList1;
- Cur->ngeneBuffer.Address_of_first_entry_2 =
- pIdleBuffer->Head->ngeneBuffer.
- Address_of_first_entry_1;
- Cur->ngeneBuffer.Number_of_entries_2 =
- pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
- Cur = Cur->Next;
- }
- return status;
-}
-
-static u32 RingBufferSizes[MAX_STREAM] = {
- RING_SIZE_VIDEO,
- RING_SIZE_VIDEO,
- RING_SIZE_AUDIO,
- RING_SIZE_AUDIO,
- RING_SIZE_AUDIO,
-};
-
-static u32 Buffer1Sizes[MAX_STREAM] = {
- MAX_VIDEO_BUFFER_SIZE,
- MAX_VIDEO_BUFFER_SIZE,
- MAX_AUDIO_BUFFER_SIZE,
- MAX_AUDIO_BUFFER_SIZE,
- MAX_AUDIO_BUFFER_SIZE
-};
-
-static u32 Buffer2Sizes[MAX_STREAM] = {
- MAX_VBI_BUFFER_SIZE,
- MAX_VBI_BUFFER_SIZE,
- 0,
- 0,
- 0
-};
-
-
-static int AllocCommonBuffers(struct ngene *dev)
-{
- int status = 0, i;
-
- dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096,
- &dev->PAFWInterfaceBuffer);
- if (!dev->FWInterfaceBuffer)
- return -ENOMEM;
- dev->hosttongene = dev->FWInterfaceBuffer;
- dev->ngenetohost = dev->FWInterfaceBuffer + 256;
- dev->EventBuffer = dev->FWInterfaceBuffer + 512;
-
- dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev,
- OVERFLOW_BUFFER_SIZE,
- &dev->PAOverflowBuffer);
- if (!dev->OverflowBuffer)
- return -ENOMEM;
- memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE);
-
- for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
- int type = dev->card_info->io_type[i];
-
- dev->channel[i].State = KSSTATE_STOP;
-
- if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
- status = create_ring_buffer(dev->pci_dev,
- &dev->channel[i].RingBuffer,
- RingBufferSizes[i]);
- if (status < 0)
- break;
-
- if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
- status = AllocateRingBuffers(dev->pci_dev,
- dev->
- PAOverflowBuffer,
- &dev->channel[i].
- RingBuffer,
- Buffer1Sizes[i],
- Buffer2Sizes[i]);
- if (status < 0)
- break;
- } else if (type & NGENE_IO_HDTV) {
- status = AllocateRingBuffers(dev->pci_dev,
- dev->
- PAOverflowBuffer,
- &dev->channel[i].
- RingBuffer,
- MAX_HDTV_BUFFER_SIZE,
- 0);
- if (status < 0)
- break;
- }
- }
-
- if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
-
- status = create_ring_buffer(dev->pci_dev,
- &dev->channel[i].
- TSRingBuffer, RING_SIZE_TS);
- if (status < 0)
- break;
-
- status = AllocateRingBuffers(dev->pci_dev,
- dev->PAOverflowBuffer,
- &dev->channel[i].
- TSRingBuffer,
- MAX_TS_BUFFER_SIZE, 0);
- if (status)
- break;
- }
-
- if (type & NGENE_IO_TSOUT) {
- status = create_ring_buffer(dev->pci_dev,
- &dev->channel[i].
- TSIdleBuffer, 1);
- if (status < 0)
- break;
- status = AllocateRingBuffers(dev->pci_dev,
- dev->PAOverflowBuffer,
- &dev->channel[i].
- TSIdleBuffer,
- MAX_TS_BUFFER_SIZE, 0);
- if (status)
- break;
- FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
- &dev->channel[i].TSRingBuffer);
- }
- }
- return status;
-}
-
-static void ngene_release_buffers(struct ngene *dev)
-{
- if (dev->iomem)
- iounmap(dev->iomem);
- free_common_buffers(dev);
- vfree(dev->tsout_buf);
- vfree(dev->tsin_buf);
- vfree(dev->ain_buf);
- vfree(dev->vin_buf);
- vfree(dev);
-}
-
-static int ngene_get_buffers(struct ngene *dev)
-{
- if (AllocCommonBuffers(dev))
- return -ENOMEM;
- if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
- dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
- if (!dev->tsout_buf)
- return -ENOMEM;
- dvb_ringbuffer_init(&dev->tsout_rbuf,
- dev->tsout_buf, TSOUT_BUF_SIZE);
- }
- if (dev->card_info->io_type[2]&NGENE_IO_TSIN) {
- dev->tsin_buf = vmalloc(TSIN_BUF_SIZE);
- if (!dev->tsin_buf)
- return -ENOMEM;
- dvb_ringbuffer_init(&dev->tsin_rbuf,
- dev->tsin_buf, TSIN_BUF_SIZE);
- }
- if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
- dev->ain_buf = vmalloc(AIN_BUF_SIZE);
- if (!dev->ain_buf)
- return -ENOMEM;
- dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
- }
- if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
- dev->vin_buf = vmalloc(VIN_BUF_SIZE);
- if (!dev->vin_buf)
- return -ENOMEM;
- dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
- }
- dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
- pci_resource_len(dev->pci_dev, 0));
- if (!dev->iomem)
- return -ENOMEM;
-
- return 0;
-}
-
-static void ngene_init(struct ngene *dev)
-{
- int i;
-
- tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
-
- memset_io(dev->iomem + 0xc000, 0x00, 0x220);
- memset_io(dev->iomem + 0xc400, 0x00, 0x100);
-
- for (i = 0; i < MAX_STREAM; i++) {
- dev->channel[i].dev = dev;
- dev->channel[i].number = i;
- }
-
- dev->fw_interface_version = 0;
-
- ngwritel(0, NGENE_INT_ENABLE);
-
- dev->icounts = ngreadl(NGENE_INT_COUNTS);
-
- dev->device_version = ngreadl(DEV_VER) & 0x0f;
- printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
- dev->device_version);
-}
-
-static int ngene_load_firm(struct ngene *dev)
-{
- u32 size;
- const struct firmware *fw = NULL;
- u8 *ngene_fw;
- char *fw_name;
- int err, version;
-
- version = dev->card_info->fw_version;
-
- switch (version) {
- default:
- case 15:
- version = 15;
- size = 23466;
- fw_name = "ngene_15.fw";
- dev->cmd_timeout_workaround = true;
- break;
- case 16:
- size = 23498;
- fw_name = "ngene_16.fw";
- dev->cmd_timeout_workaround = true;
- break;
- case 17:
- size = 24446;
- fw_name = "ngene_17.fw";
- dev->cmd_timeout_workaround = true;
- break;
- case 18:
- size = 0;
- fw_name = "ngene_18.fw";
- break;
- }
-
- if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
- printk(KERN_ERR DEVICE_NAME
- ": Could not load firmware file %s.\n", fw_name);
- printk(KERN_INFO DEVICE_NAME
- ": Copy %s to your hotplug directory!\n", fw_name);
- return -1;
- }
- if (size == 0)
- size = fw->size;
- if (size != fw->size) {
- printk(KERN_ERR DEVICE_NAME
- ": Firmware %s has invalid size!", fw_name);
- err = -1;
- } else {
- printk(KERN_INFO DEVICE_NAME
- ": Loading firmware file %s.\n", fw_name);
- ngene_fw = (u8 *) fw->data;
- err = ngene_command_load_firmware(dev, ngene_fw, size);
- }
-
- release_firmware(fw);
-
- return err;
-}
-
-static void ngene_stop(struct ngene *dev)
-{
- down(&dev->cmd_mutex);
- i2c_del_adapter(&(dev->channel[0].i2c_adapter));
- i2c_del_adapter(&(dev->channel[1].i2c_adapter));
- ngwritel(0, NGENE_INT_ENABLE);
- ngwritel(0, NGENE_COMMAND);
- ngwritel(0, NGENE_COMMAND_HI);
- ngwritel(0, NGENE_STATUS);
- ngwritel(0, NGENE_STATUS_HI);
- ngwritel(0, NGENE_EVENT);
- ngwritel(0, NGENE_EVENT_HI);
- free_irq(dev->pci_dev->irq, dev);
-#ifdef CONFIG_PCI_MSI
- if (dev->msi_enabled)
- pci_disable_msi(dev->pci_dev);
-#endif
-}
-
-static int ngene_buffer_config(struct ngene *dev)
-{
- int stat;
-
- if (dev->card_info->fw_version >= 17) {
- u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
- u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 };
- u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 };
- u8 *bconf = tsin12_config;
-
- if (dev->card_info->io_type[2]&NGENE_IO_TSIN &&
- dev->card_info->io_type[3]&NGENE_IO_TSIN) {
- bconf = tsin1234_config;
- if (dev->card_info->io_type[4]&NGENE_IO_TSOUT &&
- dev->ci.en)
- bconf = tsio1235_config;
- }
- stat = ngene_command_config_free_buf(dev, bconf);
- } else {
- int bconf = BUFFER_CONFIG_4422;
-
- if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
- bconf = BUFFER_CONFIG_3333;
- stat = ngene_command_config_buf(dev, bconf);
- }
- return stat;
-}
-
-
-static int ngene_start(struct ngene *dev)
-{
- int stat;
- int i;
-
- pci_set_master(dev->pci_dev);
- ngene_init(dev);
-
- stat = request_irq(dev->pci_dev->irq, irq_handler,
- IRQF_SHARED, "nGene",
- (void *)dev);
- if (stat < 0)
- return stat;
-
- init_waitqueue_head(&dev->cmd_wq);
- init_waitqueue_head(&dev->tx_wq);
- init_waitqueue_head(&dev->rx_wq);
- sema_init(&dev->cmd_mutex, 1);
- sema_init(&dev->stream_mutex, 1);
- sema_init(&dev->pll_mutex, 1);
- sema_init(&dev->i2c_switch_mutex, 1);
- spin_lock_init(&dev->cmd_lock);
- for (i = 0; i < MAX_STREAM; i++)
- spin_lock_init(&dev->channel[i].state_lock);
- ngwritel(1, TIMESTAMPS);
-
- ngwritel(1, NGENE_INT_ENABLE);
-
- stat = ngene_load_firm(dev);
- if (stat < 0)
- goto fail;
-
-#ifdef CONFIG_PCI_MSI
- /* enable MSI if kernel and card support it */
- if (pci_msi_enabled() && dev->card_info->msi_supported) {
- unsigned long flags;
-
- ngwritel(0, NGENE_INT_ENABLE);
- free_irq(dev->pci_dev->irq, dev);
- stat = pci_enable_msi(dev->pci_dev);
- if (stat) {
- printk(KERN_INFO DEVICE_NAME
- ": MSI not available\n");
- flags = IRQF_SHARED;
- } else {
- flags = 0;
- dev->msi_enabled = true;
- }
- stat = request_irq(dev->pci_dev->irq, irq_handler,
- flags, "nGene", dev);
- if (stat < 0)
- goto fail2;
- ngwritel(1, NGENE_INT_ENABLE);
- }
-#endif
-
- stat = ngene_i2c_init(dev, 0);
- if (stat < 0)
- goto fail;
-
- stat = ngene_i2c_init(dev, 1);
- if (stat < 0)
- goto fail;
-
- return 0;
-
-fail:
- ngwritel(0, NGENE_INT_ENABLE);
- free_irq(dev->pci_dev->irq, dev);
-#ifdef CONFIG_PCI_MSI
-fail2:
- if (dev->msi_enabled)
- pci_disable_msi(dev->pci_dev);
-#endif
- return stat;
-}
-
-/****************************************************************************/
-/****************************************************************************/
-/****************************************************************************/
-
-static void release_channel(struct ngene_channel *chan)
-{
- struct dvb_demux *dvbdemux = &chan->demux;
- struct ngene *dev = chan->dev;
-
- if (chan->running)
- set_transfer(chan, 0);
-
- tasklet_kill(&chan->demux_tasklet);
-
- if (chan->ci_dev) {
- dvb_unregister_device(chan->ci_dev);
- chan->ci_dev = NULL;
- }
-
- if (chan->fe2)
- dvb_unregister_frontend(chan->fe2);
-
- if (chan->fe) {
- dvb_unregister_frontend(chan->fe);
- dvb_frontend_detach(chan->fe);
- chan->fe = NULL;
- }
-
- if (chan->has_demux) {
- dvb_net_release(&chan->dvbnet);
- dvbdemux->dmx.close(&dvbdemux->dmx);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
- &chan->hw_frontend);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
- &chan->mem_frontend);
- dvb_dmxdev_release(&chan->dmxdev);
- dvb_dmx_release(&chan->demux);
- chan->has_demux = false;
- }
-
- if (chan->has_adapter) {
- dvb_unregister_adapter(&dev->adapter[chan->number]);
- chan->has_adapter = false;
- }
-}
-
-static int init_channel(struct ngene_channel *chan)
-{
- int ret = 0, nr = chan->number;
- struct dvb_adapter *adapter = NULL;
- struct dvb_demux *dvbdemux = &chan->demux;
- struct ngene *dev = chan->dev;
- struct ngene_info *ni = dev->card_info;
- int io = ni->io_type[nr];
-
- tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan);
- chan->users = 0;
- chan->type = io;
- chan->mode = chan->type; /* for now only one mode */
-
- if (io & NGENE_IO_TSIN) {
- chan->fe = NULL;
- if (ni->demod_attach[nr]) {
- ret = ni->demod_attach[nr](chan);
- if (ret < 0)
- goto err;
- }
- if (chan->fe && ni->tuner_attach[nr]) {
- ret = ni->tuner_attach[nr](chan);
- if (ret < 0)
- goto err;
- }
- }
-
- if (!dev->ci.en && (io & NGENE_IO_TSOUT))
- return 0;
-
- if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
- if (nr >= STREAM_AUDIOIN1)
- chan->DataFormatFlags = DF_SWAP32;
-
- if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
- adapter = &dev->adapter[nr];
- ret = dvb_register_adapter(adapter, "nGene",
- THIS_MODULE,
- &chan->dev->pci_dev->dev,
- adapter_nr);
- if (ret < 0)
- goto err;
- if (dev->first_adapter == NULL)
- dev->first_adapter = adapter;
- chan->has_adapter = true;
- } else
- adapter = dev->first_adapter;
- }
-
- if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
- dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
- set_transfer(chan, 1);
- chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
- set_transfer(&chan->dev->channel[2], 1);
- dvb_register_device(adapter, &chan->ci_dev,
- &ngene_dvbdev_ci, (void *) chan,
- DVB_DEVICE_SEC);
- if (!chan->ci_dev)
- goto err;
- }
-
- if (chan->fe) {
- if (dvb_register_frontend(adapter, chan->fe) < 0)
- goto err;
- chan->has_demux = true;
- }
- if (chan->fe2) {
- if (dvb_register_frontend(adapter, chan->fe2) < 0)
- goto err;
- chan->fe2->tuner_priv = chan->fe->tuner_priv;
- memcpy(&chan->fe2->ops.tuner_ops,
- &chan->fe->ops.tuner_ops,
- sizeof(struct dvb_tuner_ops));
- }
-
- if (chan->has_demux) {
- ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
- ngene_start_feed,
- ngene_stop_feed, chan);
- ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
- &chan->hw_frontend,
- &chan->mem_frontend, adapter);
- ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx);
- }
-
- return ret;
-
-err:
- if (chan->fe) {
- dvb_frontend_detach(chan->fe);
- chan->fe = NULL;
- }
- release_channel(chan);
- return 0;
-}
-
-static int init_channels(struct ngene *dev)
-{
- int i, j;
-
- for (i = 0; i < MAX_STREAM; i++) {
- dev->channel[i].number = i;
- if (init_channel(&dev->channel[i]) < 0) {
- for (j = i - 1; j >= 0; j--)
- release_channel(&dev->channel[j]);
- return -1;
- }
- }
- return 0;
-}
-
-static struct cxd2099_cfg cxd_cfg = {
- .bitrate = 62000,
- .adr = 0x40,
- .polarity = 0,
- .clock_mode = 0,
-};
-
-static void cxd_attach(struct ngene *dev)
-{
- struct ngene_ci *ci = &dev->ci;
-
- ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
- ci->dev = dev;
- return;
-}
-
-static void cxd_detach(struct ngene *dev)
-{
- struct ngene_ci *ci = &dev->ci;
-
- dvb_ca_en50221_release(ci->en);
- kfree(ci->en);
- ci->en = 0;
-}
-
-/***********************************/
-/* workaround for shutdown failure */
-/***********************************/
-
-static void ngene_unlink(struct ngene *dev)
-{
- struct ngene_command com;
-
- com.cmd.hdr.Opcode = CMD_MEM_WRITE;
- com.cmd.hdr.Length = 3;
- com.cmd.MemoryWrite.address = 0x910c;
- com.cmd.MemoryWrite.data = 0xff;
- com.in_len = 3;
- com.out_len = 1;
-
- down(&dev->cmd_mutex);
- ngwritel(0, NGENE_INT_ENABLE);
- ngene_command_mutex(dev, &com);
- up(&dev->cmd_mutex);
-}
-
-void ngene_shutdown(struct pci_dev *pdev)
-{
- struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
-
- if (!dev || !shutdown_workaround)
- return;
-
- printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n");
- ngene_unlink(dev);
- pci_disable_device(pdev);
-}
-
-/****************************************************************************/
-/* device probe/remove calls ************************************************/
-/****************************************************************************/
-
-void __devexit ngene_remove(struct pci_dev *pdev)
-{
- struct ngene *dev = pci_get_drvdata(pdev);
- int i;
-
- tasklet_kill(&dev->event_tasklet);
- for (i = MAX_STREAM - 1; i >= 0; i--)
- release_channel(&dev->channel[i]);
- if (dev->ci.en)
- cxd_detach(dev);
- ngene_stop(dev);
- ngene_release_buffers(dev);
- pci_set_drvdata(pdev, NULL);
- pci_disable_device(pdev);
-}
-
-int __devinit ngene_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *id)
-{
- struct ngene *dev;
- int stat = 0;
-
- if (pci_enable_device(pci_dev) < 0)
- return -ENODEV;
-
- dev = vzalloc(sizeof(struct ngene));
- if (dev == NULL) {
- stat = -ENOMEM;
- goto fail0;
- }
-
- dev->pci_dev = pci_dev;
- dev->card_info = (struct ngene_info *)id->driver_data;
- printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
-
- pci_set_drvdata(pci_dev, dev);
-
- /* Alloc buffers and start nGene */
- stat = ngene_get_buffers(dev);
- if (stat < 0)
- goto fail1;
- stat = ngene_start(dev);
- if (stat < 0)
- goto fail1;
-
- cxd_attach(dev);
-
- stat = ngene_buffer_config(dev);
- if (stat < 0)
- goto fail1;
-
-
- dev->i2c_current_bus = -1;
-
- /* Register DVB adapters and devices for both channels */
- if (init_channels(dev) < 0)
- goto fail2;
-
- return 0;
-
-fail2:
- ngene_stop(dev);
-fail1:
- ngene_release_buffers(dev);
-fail0:
- pci_disable_device(pci_dev);
- pci_set_drvdata(pci_dev, NULL);
- return stat;
-}
diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile
deleted file mode 100644
index 700822350ec5..000000000000
--- a/drivers/media/dvb/pluto2/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_PLUTO2) += pluto2.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile
deleted file mode 100644
index d80d8e8e7c57..000000000000
--- a/drivers/media/dvb/pt1/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
-
-obj-$(CONFIG_DVB_PT1) += earth-pt1.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c
deleted file mode 100644
index d980dfb21e5e..000000000000
--- a/drivers/media/dvb/pt1/va1j5jf8007s.c
+++ /dev/null
@@ -1,735 +0,0 @@
-/*
- * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
- *
- * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
- *
- * based on pt1dvr - http://pt1dvr.sourceforge.jp/
- * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-#include "va1j5jf8007s.h"
-
-enum va1j5jf8007s_tune_state {
- VA1J5JF8007S_IDLE,
- VA1J5JF8007S_SET_FREQUENCY_1,
- VA1J5JF8007S_SET_FREQUENCY_2,
- VA1J5JF8007S_SET_FREQUENCY_3,
- VA1J5JF8007S_CHECK_FREQUENCY,
- VA1J5JF8007S_SET_MODULATION,
- VA1J5JF8007S_CHECK_MODULATION,
- VA1J5JF8007S_SET_TS_ID,
- VA1J5JF8007S_CHECK_TS_ID,
- VA1J5JF8007S_TRACK,
-};
-
-struct va1j5jf8007s_state {
- const struct va1j5jf8007s_config *config;
- struct i2c_adapter *adap;
- struct dvb_frontend fe;
- enum va1j5jf8007s_tune_state tune_state;
-};
-
-static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
-{
- struct va1j5jf8007s_state *state;
- u8 addr;
- int i;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
- s32 word, x1, x2, x3, x4, x5, y;
-
- state = fe->demodulator_priv;
- addr = state->config->demod_address;
-
- word = 0;
- for (i = 0; i < 2; i++) {
- write_buf[0] = 0xbc + i;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- word <<= 8;
- word |= read_buf[0];
- }
-
- word -= 3000;
- if (word < 0)
- word = 0;
-
- x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
- x2 = (s64)x1 * x1 >> 31;
- x3 = (s64)x2 * x1 >> 31;
- x4 = (s64)x2 * x2 >> 31;
- x5 = (s64)x4 * x1 >> 31;
-
- y = (58857ll << 23) / 1000;
- y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
- y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
- y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
- y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
- y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
-
- *snr = y < 0 ? 0 : y >> 15;
- return 0;
-}
-
-static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
-{
- return DVBFE_ALGO_HW;
-}
-
-static int
-va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status)
-{
- struct va1j5jf8007s_state *state;
-
- state = fe->demodulator_priv;
-
- switch (state->tune_state) {
- case VA1J5JF8007S_IDLE:
- case VA1J5JF8007S_SET_FREQUENCY_1:
- case VA1J5JF8007S_SET_FREQUENCY_2:
- case VA1J5JF8007S_SET_FREQUENCY_3:
- case VA1J5JF8007S_CHECK_FREQUENCY:
- *status = 0;
- return 0;
-
-
- case VA1J5JF8007S_SET_MODULATION:
- case VA1J5JF8007S_CHECK_MODULATION:
- *status |= FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_SET_TS_ID:
- case VA1J5JF8007S_CHECK_TS_ID:
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
-
- case VA1J5JF8007S_TRACK:
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
- }
-
- BUG();
-}
-
-struct va1j5jf8007s_cb_map {
- u32 frequency;
- u8 cb;
-};
-
-static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
- { 986000, 0xb2 },
- { 1072000, 0xd2 },
- { 1154000, 0xe2 },
- { 1291000, 0x20 },
- { 1447000, 0x40 },
- { 1615000, 0x60 },
- { 1791000, 0x80 },
- { 1972000, 0xa0 },
-};
-
-static u8 va1j5jf8007s_lookup_cb(u32 frequency)
-{
- int i;
- const struct va1j5jf8007s_cb_map *map;
-
- for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
- map = &va1j5jf8007s_cb_maps[i];
- if (frequency < map->frequency)
- return map->cb;
- }
- return 0xc0;
-}
-
-static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
-{
- u32 frequency;
- u16 word;
- u8 buf[6];
- struct i2c_msg msg;
-
- frequency = state->fe.dtv_property_cache.frequency;
-
- word = (frequency + 500) / 1000;
- if (frequency < 1072000)
- word = (word << 1 & ~0x1f) | (word & 0x0f);
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0x40 | word >> 8;
- buf[3] = word;
- buf[4] = 0xe0;
- buf[5] = va1j5jf8007s_lookup_cb(frequency);
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
-{
- u8 buf[3];
- struct i2c_msg msg;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xe4;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
-{
- u32 frequency;
- u8 buf[4];
- struct i2c_msg msg;
-
- frequency = state->fe.dtv_property_cache.frequency;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xf4;
- buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[2], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xfe;
- write_buf[1] = 0xc1;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = read_buf[0] & 0x40;
- return 0;
-}
-
-static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x03;
- buf[1] = 0x01;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xc3;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = !(read_buf[0] & 0x10);
- return 0;
-}
-
-static int
-va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
-{
- u32 ts_id;
- u8 buf[3];
- struct i2c_msg msg;
-
- ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
- if (!ts_id)
- return 0;
-
- buf[0] = 0x8f;
- buf[1] = ts_id >> 8;
- buf[2] = ts_id;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int
-va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
-{
- u8 addr;
- u8 write_buf[1], read_buf[2];
- struct i2c_msg msgs[2];
- u32 ts_id;
-
- ts_id = state->fe.dtv_property_cache.isdbs_ts_id;
- if (!ts_id) {
- *lock = 1;
- return 0;
- }
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0xe6;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
- return 0;
-}
-
-static int
-va1j5jf8007s_tune(struct dvb_frontend *fe,
- bool re_tune,
- unsigned int mode_flags, unsigned int *delay,
- fe_status_t *status)
-{
- struct va1j5jf8007s_state *state;
- int ret;
- int lock = 0;
-
- state = fe->demodulator_priv;
-
- if (re_tune)
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
-
- switch (state->tune_state) {
- case VA1J5JF8007S_IDLE:
- *delay = 3 * HZ;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_1:
- ret = va1j5jf8007s_set_frequency_1(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
- *delay = 0;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_2:
- ret = va1j5jf8007s_set_frequency_2(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
- *delay = (HZ + 99) / 100;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_SET_FREQUENCY_3:
- ret = va1j5jf8007s_set_frequency_3(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
- *delay = 0;
- *status = 0;
- return 0;
-
- case VA1J5JF8007S_CHECK_FREQUENCY:
- ret = va1j5jf8007s_check_frequency(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 999) / 1000;
- *status = 0;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_SET_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_SET_MODULATION:
- ret = va1j5jf8007s_set_modulation(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
- *delay = 0;
- *status = FE_HAS_SIGNAL;
- return 0;
-
- case VA1J5JF8007S_CHECK_MODULATION:
- ret = va1j5jf8007s_check_modulation(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 49) / 50;
- *status = FE_HAS_SIGNAL;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_SET_TS_ID;
- *delay = 0;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
-
- case VA1J5JF8007S_SET_TS_ID:
- ret = va1j5jf8007s_set_ts_id(state);
- if (ret < 0)
- return ret;
-
- state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
- return 0;
-
- case VA1J5JF8007S_CHECK_TS_ID:
- ret = va1j5jf8007s_check_ts_id(state, &lock);
- if (ret < 0)
- return ret;
-
- if (!lock) {
- *delay = (HZ + 99) / 100;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
- return 0;
- }
-
- state->tune_state = VA1J5JF8007S_TRACK;
- /* fall through */
-
- case VA1J5JF8007S_TRACK:
- *delay = 3 * HZ;
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
- return 0;
- }
-
- BUG();
-}
-
-static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
-{
- u8 buf[4];
- struct i2c_msg msg;
-
- buf[0] = 0xfe;
- buf[1] = 0xc0;
- buf[2] = 0xf0;
- buf[3] = 0x04;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
-{
- u8 buf[2];
- struct i2c_msg msg;
-
- buf[0] = 0x17;
- buf[1] = sleep ? 0x01 : 0x00;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
-
- return 0;
-}
-
-static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- ret = va1j5jf8007s_init_frequency(state);
- if (ret < 0)
- return ret;
-
- return va1j5jf8007s_set_sleep(state, 1);
-}
-
-static int va1j5jf8007s_init(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
-
- state = fe->demodulator_priv;
- state->tune_state = VA1J5JF8007S_IDLE;
-
- return va1j5jf8007s_set_sleep(state, 0);
-}
-
-static void va1j5jf8007s_release(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- state = fe->demodulator_priv;
- kfree(state);
-}
-
-static struct dvb_frontend_ops va1j5jf8007s_ops = {
- .delsys = { SYS_ISDBS },
- .info = {
- .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .frequency_stepsize = 1000,
- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
- },
-
- .read_snr = va1j5jf8007s_read_snr,
- .get_frontend_algo = va1j5jf8007s_get_frontend_algo,
- .read_status = va1j5jf8007s_read_status,
- .tune = va1j5jf8007s_tune,
- .sleep = va1j5jf8007s_sleep,
- .init = va1j5jf8007s_init,
- .release = va1j5jf8007s_release,
-};
-
-static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
-{
- u8 addr;
- u8 write_buf[1], read_buf[1];
- struct i2c_msg msgs[2];
-
- addr = state->config->demod_address;
-
- write_buf[0] = 0x07;
-
- msgs[0].addr = addr;
- msgs[0].flags = 0;
- msgs[0].len = sizeof(write_buf);
- msgs[0].buf = write_buf;
-
- msgs[1].addr = addr;
- msgs[1].flags = I2C_M_RD;
- msgs[1].len = sizeof(read_buf);
- msgs[1].buf = read_buf;
-
- if (i2c_transfer(state->adap, msgs, 2) != 2)
- return -EREMOTEIO;
-
- if (read_buf[0] != 0x41)
- return -EIO;
-
- return 0;
-}
-
-static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = {
- {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
- {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
- {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
- {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
-};
-
-static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = {
- {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a},
- {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89},
- {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04},
- {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
-};
-
-static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
-{
- const u8 (*bufs)[2];
- int size;
- u8 addr;
- u8 buf[2];
- struct i2c_msg msg;
- int i;
-
- switch (state->config->frequency) {
- case VA1J5JF8007S_20MHZ:
- bufs = va1j5jf8007s_20mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs);
- break;
- case VA1J5JF8007S_25MHZ:
- bufs = va1j5jf8007s_25mhz_prepare_bufs;
- size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs);
- break;
- default:
- return -EINVAL;
- }
-
- addr = state->config->demod_address;
-
- msg.addr = addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = buf;
- for (i = 0; i < size; i++) {
- memcpy(buf, bufs[i], sizeof(buf));
- if (i2c_transfer(state->adap, &msg, 1) != 1)
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-/* must be called after va1j5jf8007t_attach */
-int va1j5jf8007s_prepare(struct dvb_frontend *fe)
-{
- struct va1j5jf8007s_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- ret = va1j5jf8007s_prepare_1(state);
- if (ret < 0)
- return ret;
-
- ret = va1j5jf8007s_prepare_2(state);
- if (ret < 0)
- return ret;
-
- return va1j5jf8007s_init_frequency(state);
-}
-
-struct dvb_frontend *
-va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
- struct i2c_adapter *adap)
-{
- struct va1j5jf8007s_state *state;
- struct dvb_frontend *fe;
- u8 buf[2];
- struct i2c_msg msg;
-
- state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
- if (!state)
- return NULL;
-
- state->config = config;
- state->adap = adap;
-
- fe = &state->fe;
- memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
- fe->demodulator_priv = state;
-
- buf[0] = 0x01;
- buf[1] = 0x80;
-
- msg.addr = state->config->demod_address;
- msg.flags = 0;
- msg.len = sizeof(buf);
- msg.buf = buf;
-
- if (i2c_transfer(state->adap, &msg, 1) != 1) {
- kfree(state);
- return NULL;
- }
-
- return fe;
-}
diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig
deleted file mode 100644
index bc6456eb2c4f..000000000000
--- a/drivers/media/dvb/siano/Kconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Siano Mobile Silicon Digital TV device configuration
-#
-
-config SMS_SIANO_MDTV
- tristate "Siano SMS1xxx based MDTV receiver"
- depends on DVB_CORE && RC_CORE && HAS_DMA
- ---help---
- Choose Y or M here if you have MDTV receiver with a Siano chipset.
-
- To compile this driver as a module, choose M here
- (The module will be called smsmdtv).
-
- Further documentation on this driver can be found on the WWW
- at http://www.siano-ms.com/
-
-if SMS_SIANO_MDTV
-menu "Siano module components"
-
-# Hardware interfaces support
-
-config SMS_USB_DRV
- tristate "USB interface support"
- depends on DVB_CORE && USB
- ---help---
- Choose if you would like to have Siano's support for USB interface
-
-config SMS_SDIO_DRV
- tristate "SDIO interface support"
- depends on DVB_CORE && MMC
- ---help---
- Choose if you would like to have Siano's support for SDIO interface
-endmenu
-endif # SMS_SIANO_MDTV
diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
deleted file mode 100644
index f233b57c86fb..000000000000
--- a/drivers/media/dvb/siano/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-
-smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
-
-obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
-obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
-obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core
-
-ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
-
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
deleted file mode 100644
index 664e460f247b..000000000000
--- a/drivers/media/dvb/siano/smsusb.c
+++ /dev/null
@@ -1,568 +0,0 @@
-/****************************************************************
-
-Siano Mobile Silicon, Inc.
-MDTV receiver kernel modules.
-Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-****************************************************************/
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/usb.h>
-#include <linux/firmware.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include "smscoreapi.h"
-#include "sms-cards.h"
-#include "smsendian.h"
-
-static int sms_dbg;
-module_param_named(debug, sms_dbg, int, 0644);
-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
-
-#define USB1_BUFFER_SIZE 0x1000
-#define USB2_BUFFER_SIZE 0x4000
-
-#define MAX_BUFFERS 50
-#define MAX_URBS 10
-
-struct smsusb_device_t;
-
-struct smsusb_urb_t {
- struct smscore_buffer_t *cb;
- struct smsusb_device_t *dev;
-
- struct urb urb;
-};
-
-struct smsusb_device_t {
- struct usb_device *udev;
- struct smscore_device_t *coredev;
-
- struct smsusb_urb_t surbs[MAX_URBS];
-
- int response_alignment;
- int buffer_size;
-};
-
-static int smsusb_submit_urb(struct smsusb_device_t *dev,
- struct smsusb_urb_t *surb);
-
-static void smsusb_onresponse(struct urb *urb)
-{
- struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
- struct smsusb_device_t *dev = surb->dev;
-
- if (urb->status == -ESHUTDOWN) {
- sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",
- urb->status, urb->actual_length);
- return;
- }
-
- if ((urb->actual_length > 0) && (urb->status == 0)) {
- struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p;
-
- smsendian_handle_message_header(phdr);
- if (urb->actual_length >= phdr->msgLength) {
- surb->cb->size = phdr->msgLength;
-
- if (dev->response_alignment &&
- (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {
-
- surb->cb->offset =
- dev->response_alignment +
- ((phdr->msgFlags >> 8) & 3);
-
- /* sanity check */
- if (((int) phdr->msgLength +
- surb->cb->offset) > urb->actual_length) {
- sms_err("invalid response "
- "msglen %d offset %d "
- "size %d",
- phdr->msgLength,
- surb->cb->offset,
- urb->actual_length);
- goto exit_and_resubmit;
- }
-
- /* move buffer pointer and
- * copy header to its new location */
- memcpy((char *) phdr + surb->cb->offset,
- phdr, sizeof(struct SmsMsgHdr_ST));
- } else
- surb->cb->offset = 0;
-
- smscore_onresponse(dev->coredev, surb->cb);
- surb->cb = NULL;
- } else {
- sms_err("invalid response "
- "msglen %d actual %d",
- phdr->msgLength, urb->actual_length);
- }
- } else
- sms_err("error, urb status %d, %d bytes",
- urb->status, urb->actual_length);
-
-
-exit_and_resubmit:
- smsusb_submit_urb(dev, surb);
-}
-
-static int smsusb_submit_urb(struct smsusb_device_t *dev,
- struct smsusb_urb_t *surb)
-{
- if (!surb->cb) {
- surb->cb = smscore_getbuffer(dev->coredev);
- if (!surb->cb) {
- sms_err("smscore_getbuffer(...) returned NULL");
- return -ENOMEM;
- }
- }
-
- usb_fill_bulk_urb(
- &surb->urb,
- dev->udev,
- usb_rcvbulkpipe(dev->udev, 0x81),
- surb->cb->p,
- dev->buffer_size,
- smsusb_onresponse,
- surb
- );
- surb->urb.transfer_dma = surb->cb->phys;
- surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- return usb_submit_urb(&surb->urb, GFP_ATOMIC);
-}
-
-static void smsusb_stop_streaming(struct smsusb_device_t *dev)
-{
- int i;
-
- for (i = 0; i < MAX_URBS; i++) {
- usb_kill_urb(&dev->surbs[i].urb);
-
- if (dev->surbs[i].cb) {
- smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
- dev->surbs[i].cb = NULL;
- }
- }
-}
-
-static int smsusb_start_streaming(struct smsusb_device_t *dev)
-{
- int i, rc;
-
- for (i = 0; i < MAX_URBS; i++) {
- rc = smsusb_submit_urb(dev, &dev->surbs[i]);
- if (rc < 0) {
- sms_err("smsusb_submit_urb(...) failed");
- smsusb_stop_streaming(dev);
- break;
- }
- }
-
- return rc;
-}
-
-static int smsusb_sendrequest(void *context, void *buffer, size_t size)
-{
- struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
- int dummy;
-
- smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer);
- return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
- buffer, size, &dummy, 1000);
-}
-
-static char *smsusb1_fw_lkup[] = {
- "dvbt_stellar_usb.inp",
- "dvbh_stellar_usb.inp",
- "tdmb_stellar_usb.inp",
- "none",
- "dvbt_bda_stellar_usb.inp",
-};
-
-static inline char *sms_get_fw_name(int mode, int board_id)
-{
- char **fw = sms_get_board(board_id)->fw;
- return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];
-}
-
-static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
-{
- const struct firmware *fw;
- u8 *fw_buffer;
- int rc, dummy;
- char *fw_filename;
-
- if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
- sms_err("invalid firmware id specified %d", id);
- return -EINVAL;
- }
-
- fw_filename = sms_get_fw_name(id, board_id);
-
- rc = request_firmware(&fw, fw_filename, &udev->dev);
- if (rc < 0) {
- sms_warn("failed to open \"%s\" mode %d, "
- "trying again with default firmware", fw_filename, id);
-
- fw_filename = smsusb1_fw_lkup[id];
- rc = request_firmware(&fw, fw_filename, &udev->dev);
- if (rc < 0) {
- sms_warn("failed to open \"%s\" mode %d",
- fw_filename, id);
-
- return rc;
- }
- }
-
- fw_buffer = kmalloc(fw->size, GFP_KERNEL);
- if (fw_buffer) {
- memcpy(fw_buffer, fw->data, fw->size);
-
- rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
- fw_buffer, fw->size, &dummy, 1000);
-
- sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
-
- kfree(fw_buffer);
- } else {
- sms_err("failed to allocate firmware buffer");
- rc = -ENOMEM;
- }
- sms_info("read FW %s, size=%zd", fw_filename, fw->size);
-
- release_firmware(fw);
-
- return rc;
-}
-
-static void smsusb1_detectmode(void *context, int *mode)
-{
- char *product_string =
- ((struct smsusb_device_t *) context)->udev->product;
-
- *mode = DEVICE_MODE_NONE;
-
- if (!product_string) {
- product_string = "none";
- sms_err("product string not found");
- } else if (strstr(product_string, "DVBH"))
- *mode = 1;
- else if (strstr(product_string, "BDA"))
- *mode = 4;
- else if (strstr(product_string, "DVBT"))
- *mode = 0;
- else if (strstr(product_string, "TDMB"))
- *mode = 2;
-
- sms_info("%d \"%s\"", *mode, product_string);
-}
-
-static int smsusb1_setmode(void *context, int mode)
-{
- struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
- sizeof(struct SmsMsgHdr_ST), 0 };
-
- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
- sms_err("invalid firmware id specified %d", mode);
- return -EINVAL;
- }
-
- return smsusb_sendrequest(context, &Msg, sizeof(Msg));
-}
-
-static void smsusb_term_device(struct usb_interface *intf)
-{
- struct smsusb_device_t *dev = usb_get_intfdata(intf);
-
- if (dev) {
- smsusb_stop_streaming(dev);
-
- /* unregister from smscore */
- if (dev->coredev)
- smscore_unregister_device(dev->coredev);
-
- sms_info("device %p destroyed", dev);
- kfree(dev);
- }
-
- usb_set_intfdata(intf, NULL);
-}
-
-static int smsusb_init_device(struct usb_interface *intf, int board_id)
-{
- struct smsdevice_params_t params;
- struct smsusb_device_t *dev;
- int i, rc;
-
- /* create device object */
- dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
- if (!dev) {
- sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
- return -ENOMEM;
- }
-
- memset(&params, 0, sizeof(params));
- usb_set_intfdata(intf, dev);
- dev->udev = interface_to_usbdev(intf);
-
- params.device_type = sms_get_board(board_id)->type;
-
- switch (params.device_type) {
- case SMS_STELLAR:
- dev->buffer_size = USB1_BUFFER_SIZE;
-
- params.setmode_handler = smsusb1_setmode;
- params.detectmode_handler = smsusb1_detectmode;
- break;
- default:
- sms_err("Unspecified sms device type!");
- /* fall-thru */
- case SMS_NOVA_A0:
- case SMS_NOVA_B0:
- case SMS_VEGA:
- dev->buffer_size = USB2_BUFFER_SIZE;
- dev->response_alignment =
- le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) -
- sizeof(struct SmsMsgHdr_ST);
-
- params.flags |= SMS_DEVICE_FAMILY2;
- break;
- }
-
- params.device = &dev->udev->dev;
- params.buffer_size = dev->buffer_size;
- params.num_buffers = MAX_BUFFERS;
- params.sendrequest_handler = smsusb_sendrequest;
- params.context = dev;
- usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));
-
- /* register in smscore */
- rc = smscore_register_device(&params, &dev->coredev);
- if (rc < 0) {
- sms_err("smscore_register_device(...) failed, rc %d", rc);
- smsusb_term_device(intf);
- return rc;
- }
-
- smscore_set_board_id(dev->coredev, board_id);
-
- /* initialize urbs */
- for (i = 0; i < MAX_URBS; i++) {
- dev->surbs[i].dev = dev;
- usb_init_urb(&dev->surbs[i].urb);
- }
-
- sms_info("smsusb_start_streaming(...).");
- rc = smsusb_start_streaming(dev);
- if (rc < 0) {
- sms_err("smsusb_start_streaming(...) failed");
- smsusb_term_device(intf);
- return rc;
- }
-
- rc = smscore_start_device(dev->coredev);
- if (rc < 0) {
- sms_err("smscore_start_device(...) failed");
- smsusb_term_device(intf);
- return rc;
- }
-
- sms_info("device %p created", dev);
-
- return rc;
-}
-
-static int __devinit smsusb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- char devpath[32];
- int i, rc;
-
- rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
- rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
-
- if (intf->num_altsetting > 0) {
- rc = usb_set_interface(
- udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
- if (rc < 0) {
- sms_err("usb_set_interface failed, rc %d", rc);
- return rc;
- }
- }
-
- sms_info("smsusb_probe %d",
- intf->cur_altsetting->desc.bInterfaceNumber);
- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
- sms_info("endpoint %d %02x %02x %d", i,
- intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
- intf->cur_altsetting->endpoint[i].desc.bmAttributes,
- intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
-
- if ((udev->actconfig->desc.bNumInterfaces == 2) &&
- (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
- sms_err("rom interface 0 is not used");
- return -ENODEV;
- }
-
- if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
- snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
- udev->bus->busnum, udev->devpath);
- sms_info("stellar device was found.");
- return smsusb1_load_firmware(
- udev, smscore_registry_getmode(devpath),
- id->driver_info);
- }
-
- rc = smsusb_init_device(intf, id->driver_info);
- sms_info("rc %d", rc);
- sms_board_load_modules(id->driver_info);
- return rc;
-}
-
-static void smsusb_disconnect(struct usb_interface *intf)
-{
- smsusb_term_device(intf);
-}
-
-static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
-{
- struct smsusb_device_t *dev = usb_get_intfdata(intf);
- printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);
- smsusb_stop_streaming(dev);
- return 0;
-}
-
-static int smsusb_resume(struct usb_interface *intf)
-{
- int rc, i;
- struct smsusb_device_t *dev = usb_get_intfdata(intf);
- struct usb_device *udev = interface_to_usbdev(intf);
-
- printk(KERN_INFO "%s: Entering.\n", __func__);
- usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
- usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
-
- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
- printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
- intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
- intf->cur_altsetting->endpoint[i].desc.bmAttributes,
- intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
-
- if (intf->num_altsetting > 0) {
- rc = usb_set_interface(udev,
- intf->cur_altsetting->desc.
- bInterfaceNumber, 0);
- if (rc < 0) {
- printk(KERN_INFO "%s usb_set_interface failed, "
- "rc %d\n", __func__, rc);
- return rc;
- }
- }
-
- smsusb_start_streaming(dev);
- return 0;
-}
-
-static const struct usb_device_id smsusb_id_table[] __devinitconst = {
- { USB_DEVICE(0x187f, 0x0010),
- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
- { USB_DEVICE(0x187f, 0x0100),
- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
- { USB_DEVICE(0x187f, 0x0200),
- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
- { USB_DEVICE(0x187f, 0x0201),
- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
- { USB_DEVICE(0x187f, 0x0300),
- .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
- { USB_DEVICE(0x2040, 0x1700),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
- { USB_DEVICE(0x2040, 0x1800),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
- { USB_DEVICE(0x2040, 0x1801),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
- { USB_DEVICE(0x2040, 0x2000),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
- { USB_DEVICE(0x2040, 0x2009),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
- { USB_DEVICE(0x2040, 0x200a),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
- { USB_DEVICE(0x2040, 0x2010),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
- { USB_DEVICE(0x2040, 0x2011),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
- { USB_DEVICE(0x2040, 0x2019),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
- { USB_DEVICE(0x2040, 0x5500),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0x5510),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0x5520),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0x5530),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0x5580),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0x5590),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x187f, 0x0202),
- .driver_info = SMS1XXX_BOARD_SIANO_NICE },
- { USB_DEVICE(0x187f, 0x0301),
- .driver_info = SMS1XXX_BOARD_SIANO_VENICE },
- { USB_DEVICE(0x2040, 0xb900),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xb910),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xb980),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xb990),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xc000),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xc010),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xc080),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xc090),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xc0a0),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { USB_DEVICE(0x2040, 0xf5a0),
- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
- { } /* Terminating entry */
- };
-
-MODULE_DEVICE_TABLE(usb, smsusb_id_table);
-
-static struct usb_driver smsusb_driver = {
- .name = "smsusb",
- .probe = smsusb_probe,
- .disconnect = smsusb_disconnect,
- .id_table = smsusb_id_table,
-
- .suspend = smsusb_suspend,
- .resume = smsusb_resume,
-};
-
-module_usb_driver(smsusb_driver);
-
-MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle");
-MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
deleted file mode 100644
index 9d83ced69dd6..000000000000
--- a/drivers/media/dvb/ttpci/Kconfig
+++ /dev/null
@@ -1,159 +0,0 @@
-config TTPCI_EEPROM
- tristate
- depends on I2C
- default n
-
-config DVB_AV7110
- tristate "AV7110 cards"
- depends on DVB_CORE && PCI && I2C
- select TTPCI_EEPROM
- select VIDEO_SAA7146_VV
- depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
- select DVB_VES1820 if !DVB_FE_CUSTOMISE
- select DVB_VES1X93 if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_TDA8083 if !DVB_FE_CUSTOMISE
- select DVB_SP8870 if !DVB_FE_CUSTOMISE
- select DVB_STV0297 if !DVB_FE_CUSTOMISE
- select DVB_L64781 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- help
- Support for SAA7146 and AV7110 based DVB cards as produced
- by Fujitsu-Siemens, Technotrend, Hauppauge and others.
-
- This driver only supports the fullfeatured cards with
- onboard MPEG2 decoder.
-
- This driver needs an external firmware. Please use the script
- "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
- download/extract it, and then copy it to /usr/lib/hotplug/firmware
- or /lib/firmware (depending on configuration of firmware hotplug).
-
- Alternatively, you can download the file and use the kernel's
- EXTRA_FIRMWARE configuration option to build it into your
- kernel image by adding the filename to the EXTRA_FIRMWARE
- configuration option string.
-
- Say Y if you own such a card and want to use it.
-
-config DVB_AV7110_OSD
- bool "AV7110 OSD support"
- depends on DVB_AV7110
- default y if DVB_AV7110=y || DVB_AV7110=m
- help
- The AV7110 firmware provides some code to generate an OnScreenDisplay
- on the video output. This is kind of nonstandard and not guaranteed to
- be maintained.
-
- Anyway, some popular DVB software like VDR uses this OSD to render
- its menus, so say Y if you want to use this software.
-
- All other people say N.
-
-config DVB_BUDGET_CORE
- tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
- depends on DVB_CORE && PCI && I2C
- select VIDEO_SAA7146
- select TTPCI_EEPROM
- help
- Support for simple SAA7146 based DVB cards
- (so called Budget- or Nova-PCI cards) without onboard
- MPEG2 decoder.
-
-config DVB_BUDGET
- tristate "Budget cards"
- depends on DVB_BUDGET_CORE && I2C
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_VES1X93 if !DVB_FE_CUSTOMISE
- select DVB_VES1820 if !DVB_FE_CUSTOMISE
- select DVB_L64781 if !DVB_FE_CUSTOMISE
- select DVB_TDA8083 if !DVB_FE_CUSTOMISE
- select DVB_S5H1420 if !DVB_FE_CUSTOMISE
- select DVB_TDA10086 if !DVB_FE_CUSTOMISE
- select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select DVB_ISL6423 if !DVB_FE_CUSTOMISE
- select DVB_STV090x if !DVB_FE_CUSTOMISE
- select DVB_STV6110x if !DVB_FE_CUSTOMISE
- help
- Support for simple SAA7146 based DVB cards (so called Budget-
- or Nova-PCI cards) without onboard MPEG2 decoder, and without
- analog inputs or an onboard Common Interface connector.
-
- Say Y if you own such a card and want to use it.
-
- To compile this driver as a module, choose M here: the
- module will be called budget.
-
-config DVB_BUDGET_CI
- tristate "Budget cards with onboard CI connector"
- depends on DVB_BUDGET_CORE && I2C
- select DVB_STV0297 if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_STV0288 if !DVB_FE_CUSTOMISE
- select DVB_STB6000 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
- depends on RC_CORE
- help
- Support for simple SAA7146 based DVB cards
- (so called Budget- or Nova-PCI cards) without onboard
- MPEG2 decoder, but with onboard Common Interface connector.
-
- Note: The Common Interface is not yet supported by this driver
- due to lack of information from the vendor.
-
- Say Y if you own such a card and want to use it.
-
- To compile this driver as a module, choose M here: the
- module will be called budget-ci.
-
-config DVB_BUDGET_AV
- tristate "Budget cards with analog video inputs"
- depends on DVB_BUDGET_CORE && I2C
- select VIDEO_SAA7146_VV
- depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select DVB_TDA10021 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select DVB_STB0899 if !DVB_FE_CUSTOMISE
- select DVB_TDA8261 if !DVB_FE_CUSTOMISE
- select DVB_TUA6100 if !DVB_FE_CUSTOMISE
- help
- Support for simple SAA7146 based DVB cards
- (so called Budget- or Nova-PCI cards) without onboard
- MPEG2 decoder, but with one or more analog video inputs.
-
- Say Y if you own such a card and want to use it.
-
- To compile this driver as a module, choose M here: the
- module will be called budget-av.
-
-config DVB_BUDGET_PATCH
- tristate "AV7110 cards with Budget Patch"
- depends on DVB_BUDGET_CORE && I2C
- depends on DVB_AV7110
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_VES1X93 if !DVB_FE_CUSTOMISE
- select DVB_TDA8083 if !DVB_FE_CUSTOMISE
- help
- Support for Budget Patch (full TS) modification on
- SAA7146+AV7110 based cards (DVB-S cards). This
- driver doesn't use onboard MPEG2 decoder. The
- card is driven in Budget-only mode. Card is
- required to have loaded firmware to tune properly.
- Firmware can be loaded by insertion and removal of
- standard AV7110 driver prior to loading this
- driver.
-
- Say Y if you own such a card and want to use it.
-
- To compile this driver as a module, choose M here: the
- module will be called budget-patch.
diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile
deleted file mode 100644
index f6e869372e30..000000000000
--- a/drivers/media/dvb/ttpci/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Makefile for the kernel SAA7146 FULL TS DVB device driver
-# and the AV7110 DVB device driver
-#
-
-dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
-
-ifdef CONFIG_INPUT_EVDEV
-dvb-ttpci-objs += av7110_ir.o
-endif
-
-obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
-obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
-obj-$(CONFIG_DVB_BUDGET) += budget.o
-obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
-obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
-obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
-obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
deleted file mode 100644
index 1b2d15140a1d..000000000000
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ /dev/null
@@ -1,966 +0,0 @@
-/*
- * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module
- *
- * Copyright (C) 1999-2002 Ralph Metzler
- * & Marcus Metzler for convergence integrated media GmbH
- *
- * originally based on code by:
- * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- * the project's page is at http://www.linuxtv.org/
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/timer.h>
-#include <linux/poll.h>
-
-#include "av7110.h"
-#include "av7110_hw.h"
-#include "av7110_av.h"
-
-int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
-{
- u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
- struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg };
-
- switch (av7110->adac_type) {
- case DVB_ADAC_MSP34x0:
- msgs.addr = 0x40;
- break;
- case DVB_ADAC_MSP34x5:
- msgs.addr = 0x42;
- break;
- default:
- return 0;
- }
-
- if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
- dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
- av7110->dvb_adapter.num, reg, val);
- return -EIO;
- }
- return 0;
-}
-
-static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
-{
- u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
- u8 msg2[2];
- struct i2c_msg msgs[2] = {
- { .flags = 0 , .len = 3, .buf = msg1 },
- { .flags = I2C_M_RD, .len = 2, .buf = msg2 }
- };
-
- switch (av7110->adac_type) {
- case DVB_ADAC_MSP34x0:
- msgs[0].addr = 0x40;
- msgs[1].addr = 0x40;
- break;
- case DVB_ADAC_MSP34x5:
- msgs[0].addr = 0x42;
- msgs[1].addr = 0x42;
- break;
- default:
- return 0;
- }
-
- if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
- dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
- av7110->dvb_adapter.num, reg);
- return -EIO;
- }
- *val = (msg2[0] << 8) | msg2[1];
- return 0;
-}
-
-static struct v4l2_input inputs[4] = {
- {
- .index = 0,
- .name = "DVB",
- .type = V4L2_INPUT_TYPE_CAMERA,
- .audioset = 1,
- .tuner = 0, /* ignored */
- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
- .status = 0,
- .capabilities = V4L2_IN_CAP_STD,
- }, {
- .index = 1,
- .name = "Television",
- .type = V4L2_INPUT_TYPE_TUNER,
- .audioset = 1,
- .tuner = 0,
- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
- .status = 0,
- .capabilities = V4L2_IN_CAP_STD,
- }, {
- .index = 2,
- .name = "Video",
- .type = V4L2_INPUT_TYPE_CAMERA,
- .audioset = 0,
- .tuner = 0,
- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
- .status = 0,
- .capabilities = V4L2_IN_CAP_STD,
- }, {
- .index = 3,
- .name = "Y/C",
- .type = V4L2_INPUT_TYPE_CAMERA,
- .audioset = 0,
- .tuner = 0,
- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
- .status = 0,
- .capabilities = V4L2_IN_CAP_STD,
- }
-};
-
-static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
-{
- struct av7110 *av7110 = dev->ext_priv;
- u8 buf[] = { 0x00, reg, data };
- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 };
-
- dprintk(4, "dev: %p\n", dev);
-
- if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
- return -1;
- return 0;
-}
-
-static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4])
-{
- struct av7110 *av7110 = dev->ext_priv;
- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
-
- dprintk(4, "dev: %p\n", dev);
-
- if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
- return -1;
- return 0;
-}
-
-static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq)
-{
- u32 div;
- u8 config;
- u8 buf[4];
-
- dprintk(4, "freq: 0x%08x\n", freq);
-
- /* magic number: 614. tuning with the frequency given by v4l2
- is always off by 614*62.5 = 38375 kHz...*/
- div = freq + 614;
-
- buf[0] = (div >> 8) & 0x7f;
- buf[1] = div & 0xff;
- buf[2] = 0x8e;
-
- if (freq < (u32) (16 * 168.25))
- config = 0xa0;
- else if (freq < (u32) (16 * 447.25))
- config = 0x90;
- else
- config = 0x30;
- config &= ~0x02;
-
- buf[3] = config;
-
- return tuner_write(dev, 0x61, buf);
-}
-
-static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq)
-{
- struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
- u32 div;
- u8 data[4];
-
- div = (freq + 38900000 + 31250) / 62500;
-
- data[0] = (div >> 8) & 0x7f;
- data[1] = div & 0xff;
- data[2] = 0xce;
-
- if (freq < 45000000)
- return -EINVAL;
- else if (freq < 137000000)
- data[3] = 0x01;
- else if (freq < 403000000)
- data[3] = 0x02;
- else if (freq < 860000000)
- data[3] = 0x04;
- else
- return -EINVAL;
-
- if (av7110->fe->ops.i2c_gate_ctrl)
- av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1);
- return tuner_write(dev, 0x63, data);
-}
-
-
-
-static struct saa7146_standard analog_standard[];
-static struct saa7146_standard dvb_standard[];
-static struct saa7146_standard standard[];
-
-static struct v4l2_audio msp3400_v4l2_audio = {
- .index = 0,
- .name = "Television",
- .capability = V4L2_AUDCAP_STEREO
-};
-
-static int av7110_dvb_c_switch(struct saa7146_fh *fh)
-{
- struct saa7146_dev *dev = fh->dev;
- struct saa7146_vv *vv = dev->vv_data;
- struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
- u16 adswitch;
- int source, sync, err;
-
- dprintk(4, "%p\n", av7110);
-
- if ((vv->video_status & STATUS_OVERLAY) != 0) {
- vv->ov_suspend = vv->video_fh;
- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
- if (err != 0) {
- dprintk(2, "suspending video failed\n");
- vv->ov_suspend = NULL;
- }
- }
-
- if (0 != av7110->current_input) {
- dprintk(1, "switching to analog TV:\n");
- adswitch = 1;
- source = SAA7146_HPS_SOURCE_PORT_B;
- sync = SAA7146_HPS_SYNC_PORT_B;
- memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
-
- switch (av7110->current_input) {
- case 1:
- dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
- msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
-
- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
- if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
- dprintk(1, "setting band in demodulator failed\n");
- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
- }
- if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1)
- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
- break;
- case 2:
- dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
- if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
- break;
- case 3:
- dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
- if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
- break;
- default:
- dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
- }
- } else {
- adswitch = 0;
- source = SAA7146_HPS_SOURCE_PORT_A;
- sync = SAA7146_HPS_SYNC_PORT_A;
- memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
- dprintk(1, "switching DVB mode\n");
- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
- msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
-
- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
- if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed\n");
- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
- }
- }
-
- /* hmm, this does not do anything!? */
- if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch))
- dprintk(1, "ADSwitch error\n");
-
- saa7146_set_hps_source_and_sync(dev, source, sync);
-
- if (vv->ov_suspend != NULL) {
- saa7146_start_preview(vv->ov_suspend);
- vv->ov_suspend = NULL;
- }
-
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- u16 stereo_det;
- s8 stereo;
-
- dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
-
- if (!av7110->analog_tuner_flags || t->index != 0)
- return -EINVAL;
-
- memset(t, 0, sizeof(*t));
- strcpy((char *)t->name, "Television");
-
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
- t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
- /* FIXME: add the real signal strength here */
- t->signal = 0xffff;
- t->afc = 0;
-
- /* FIXME: standard / stereo detection is still broken */
- msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
- dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
- msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
- dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
- stereo = (s8)(stereo_det >> 8);
- if (stereo > 0x10) {
- /* stereo */
- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
- t->audmode = V4L2_TUNER_MODE_STEREO;
- } else if (stereo < -0x10) {
- /* bilingual */
- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- t->audmode = V4L2_TUNER_MODE_LANG1;
- } else /* mono */
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
- u16 fm_matrix, src;
- dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
-
- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
- return -EINVAL;
-
- switch (t->audmode) {
- case V4L2_TUNER_MODE_STEREO:
- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
- fm_matrix = 0x3001; /* stereo */
- src = 0x0020;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
- fm_matrix = 0x3000; /* bilingual */
- src = 0x0020;
- break;
- case V4L2_TUNER_MODE_LANG1:
- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
- fm_matrix = 0x3000; /* mono */
- src = 0x0000;
- break;
- case V4L2_TUNER_MODE_LANG2:
- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
- fm_matrix = 0x3000; /* mono */
- src = 0x0010;
- break;
- default: /* case V4L2_TUNER_MODE_MONO: */
- dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
- fm_matrix = 0x3000; /* mono */
- src = 0x0030;
- break;
- }
- msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
- msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
- msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
- msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
-
- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
- return -EINVAL;
-
- memset(f, 0, sizeof(*f));
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = av7110->current_freq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
-
- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
- return -EINVAL;
-
- if (V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
-
- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
-
- /* tune in desired frequency */
- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
- ves1820_set_tv_freq(dev, f->frequency);
- else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
- stv0297_set_tv_freq(dev, f->frequency);
- av7110->current_freq = f->frequency;
-
- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
-
- if (av7110->analog_tuner_flags) {
- if (i->index >= 4)
- return -EINVAL;
- } else {
- if (i->index != 0)
- return -EINVAL;
- }
-
- memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- *input = av7110->current_input;
- dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
-
- if (!av7110->analog_tuner_flags)
- return input ? -EINVAL : 0;
-
- if (input >= 4)
- return -EINVAL;
-
- av7110->current_input = input;
- return av7110_dvb_c_switch(fh);
-}
-
-static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
- if (a->index != 0)
- return -EINVAL;
- *a = msp3400_v4l2_audio;
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
- if (a->index != 0)
- return -EINVAL;
- if (av7110->current_input >= 2)
- return -EINVAL;
- *a = msp3400_v4l2_audio;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
- if (av7110->current_input >= 2)
- return -EINVAL;
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
- struct v4l2_sliced_vbi_cap *cap)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
- if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
- return -EINVAL;
- if (FW_VERSION(av7110->arm_app) >= 0x2623) {
- cap->service_set = V4L2_SLICED_WSS_625;
- cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
- }
- return 0;
-}
-
-static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_G_FMT:\n");
- if (FW_VERSION(av7110->arm_app) < 0x2623)
- return -EINVAL;
- memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
- if (av7110->wssMode) {
- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
- f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
- }
- return 0;
-}
-
-static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
-
- dprintk(2, "VIDIOC_S_FMT\n");
- if (FW_VERSION(av7110->arm_app) < 0x2623)
- return -EINVAL;
- if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
- f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
- memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
- /* WSS controlled by firmware */
- av7110->wssMode = 0;
- av7110->wssData = 0;
- return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
- SetWSSConfig, 1, 0);
- } else {
- memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
- f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
- /* WSS controlled by userspace */
- av7110->wssMode = 1;
- av7110->wssData = 0;
- }
- return 0;
-}
-
-static int av7110_vbi_reset(struct file *file)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
-
- dprintk(2, "%s\n", __func__);
- av7110->wssMode = 0;
- av7110->wssData = 0;
- if (FW_VERSION(av7110->arm_app) < 0x2623)
- return 0;
- else
- return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
-}
-
-static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
-{
- struct saa7146_fh *fh = file->private_data;
- struct saa7146_dev *dev = fh->dev;
- struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
- struct v4l2_sliced_vbi_data d;
- int rc;
-
- dprintk(2, "%s\n", __func__);
- if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d)
- return -EINVAL;
- if (copy_from_user(&d, data, count))
- return -EFAULT;
- if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
- return -EINVAL;
- if (d.id)
- av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
- else
- av7110->wssData = 0x8000;
- rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData);
- return (rc < 0) ? rc : count;
-}
-
-/****************************************************************************
- * INITIALIZATION
- ****************************************************************************/
-
-static u8 saa7113_init_regs[] = {
- 0x02, 0xd0,
- 0x03, 0x23,
- 0x04, 0x00,
- 0x05, 0x00,
- 0x06, 0xe9,
- 0x07, 0x0d,
- 0x08, 0x98,
- 0x09, 0x02,
- 0x0a, 0x80,
- 0x0b, 0x40,
- 0x0c, 0x40,
- 0x0d, 0x00,
- 0x0e, 0x01,
- 0x0f, 0x7c,
- 0x10, 0x48,
- 0x11, 0x0c,
- 0x12, 0x8b,
- 0x13, 0x1a,
- 0x14, 0x00,
- 0x15, 0x00,
- 0x16, 0x00,
- 0x17, 0x00,
- 0x18, 0x00,
- 0x19, 0x00,
- 0x1a, 0x00,
- 0x1b, 0x00,
- 0x1c, 0x00,
- 0x1d, 0x00,
- 0x1e, 0x00,
-
- 0x41, 0x77,
- 0x42, 0x77,
- 0x43, 0x77,
- 0x44, 0x77,
- 0x45, 0x77,
- 0x46, 0x77,
- 0x47, 0x77,
- 0x48, 0x77,
- 0x49, 0x77,
- 0x4a, 0x77,
- 0x4b, 0x77,
- 0x4c, 0x77,
- 0x4d, 0x77,
- 0x4e, 0x77,
- 0x4f, 0x77,
- 0x50, 0x77,
- 0x51, 0x77,
- 0x52, 0x77,
- 0x53, 0x77,
- 0x54, 0x77,
- 0x55, 0x77,
- 0x56, 0x77,
- 0x57, 0xff,
-
- 0xff
-};
-
-
-static struct saa7146_ext_vv av7110_vv_data_st;
-static struct saa7146_ext_vv av7110_vv_data_c;
-
-int av7110_init_analog_module(struct av7110 *av7110)
-{
- u16 version1, version2;
-
- if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
- i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
- pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
- av7110->dvb_adapter.num);
- av7110->adac_type = DVB_ADAC_MSP34x0;
- } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
- i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
- pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
- av7110->dvb_adapter.num);
- av7110->adac_type = DVB_ADAC_MSP34x5;
- } else
- return -ENODEV;
-
- msleep(100); // the probing above resets the msp...
- msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
- msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
- dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n",
- av7110->dvb_adapter.num, version1, version2);
- msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
- msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
- msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
-
- if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
- pr_info("saa7113 not accessible\n");
- } else {
- u8 *i = saa7113_init_regs;
-
- if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
- /* Fujitsu/Siemens DVB-Cable */
- av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
- } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) {
- /* Hauppauge/TT DVB-C premium */
- av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
- } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) {
- /* Hauppauge/TT DVB-C premium */
- av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297;
- }
-
- /* setup for DVB by default */
- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
- if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
- dprintk(1, "setting band in demodulator failed\n");
- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
- saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
- saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
- }
-
- /* init the saa7113 */
- while (*i != 0xff) {
- if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) {
- dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num);
- break;
- }
- i += 2;
- }
- /* setup msp for analog sound: B/G Dual-FM */
- msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2
- msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG
- msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz
- msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI
- msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz
- msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI
- msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2
- }
-
- memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
- /* set dd1 stream a & b */
- saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
- saa7146_write(av7110->dev, DD1_INIT, 0x03000700);
- saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
-
- return 0;
-}
-
-int av7110_init_v4l(struct av7110 *av7110)
-{
- struct saa7146_dev* dev = av7110->dev;
- struct saa7146_ext_vv *vv_data;
- int ret;
-
- /* special case DVB-C: these cards have an analog tuner
- plus need some special handling, so we have separate
- saa7146_ext_vv data for these... */
- if (av7110->analog_tuner_flags)
- vv_data = &av7110_vv_data_c;
- else
- vv_data = &av7110_vv_data_st;
- ret = saa7146_vv_init(dev, vv_data);
-
- if (ret) {
- ERR("cannot init capture device. skipping\n");
- return -ENODEV;
- }
- vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input;
- vv_data->vid_ops.vidioc_g_input = vidioc_g_input;
- vv_data->vid_ops.vidioc_s_input = vidioc_s_input;
- vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner;
- vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner;
- vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency;
- vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency;
- vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio;
- vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio;
- vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio;
- vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL;
-
- vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner;
- vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner;
- vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency;
- vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency;
- vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL;
- vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
- vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
- vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
-
- if (FW_VERSION(av7110->arm_app) < 0x2623)
- vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT;
-
- if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
- ERR("cannot register capture device. skipping\n");
- saa7146_vv_release(dev);
- return -ENODEV;
- }
- if (FW_VERSION(av7110->arm_app) >= 0x2623) {
- if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
- ERR("cannot register vbi v4l2 device. skipping\n");
- }
- return 0;
-}
-
-int av7110_exit_v4l(struct av7110 *av7110)
-{
- struct saa7146_dev* dev = av7110->dev;
-
- saa7146_unregister_device(&av7110->v4l_dev, av7110->dev);
- saa7146_unregister_device(&av7110->vbi_dev, av7110->dev);
-
- saa7146_vv_release(dev);
-
- return 0;
-}
-
-
-
-/* FIXME: these values are experimental values that look better than the
- values from the latest "official" driver -- at least for me... (MiHu) */
-static struct saa7146_standard standard[] = {
- {
- .name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x15, .v_field = 288,
- .h_offset = 0x48, .h_pixels = 708,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244,
- .h_offset = 0x40, .h_pixels = 708,
- .v_max_out = 480, .h_max_out = 640,
- }
-};
-
-static struct saa7146_standard analog_standard[] = {
- {
- .name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x1b, .v_field = 288,
- .h_offset = 0x08, .h_pixels = 708,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244,
- .h_offset = 0x40, .h_pixels = 708,
- .v_max_out = 480, .h_max_out = 640,
- }
-};
-
-static struct saa7146_standard dvb_standard[] = {
- {
- .name = "PAL", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x14, .v_field = 288,
- .h_offset = 0x48, .h_pixels = 708,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x10, .v_field = 244,
- .h_offset = 0x40, .h_pixels = 708,
- .v_max_out = 480, .h_max_out = 640,
- }
-};
-
-static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
-{
- struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
-
- if (std->id & V4L2_STD_PAL) {
- av7110->vidmode = AV7110_VIDEO_MODE_PAL;
- av7110_set_vidmode(av7110, av7110->vidmode);
- }
- else if (std->id & V4L2_STD_NTSC) {
- av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
- av7110_set_vidmode(av7110, av7110->vidmode);
- }
- else
- return -1;
-
- return 0;
-}
-
-
-static struct saa7146_ext_vv av7110_vv_data_st = {
- .inputs = 1,
- .audios = 1,
- .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
- .flags = 0,
-
- .stds = &standard[0],
- .num_stds = ARRAY_SIZE(standard),
- .std_callback = &std_callback,
-
- .vbi_fops.open = av7110_vbi_reset,
- .vbi_fops.release = av7110_vbi_reset,
- .vbi_fops.write = av7110_vbi_write,
-};
-
-static struct saa7146_ext_vv av7110_vv_data_c = {
- .inputs = 1,
- .audios = 1,
- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
- .flags = SAA7146_USE_PORT_B_FOR_VBI,
-
- .stds = &standard[0],
- .num_stds = ARRAY_SIZE(standard),
- .std_callback = &std_callback,
-
- .vbi_fops.open = av7110_vbi_reset,
- .vbi_fops.release = av7110_vbi_reset,
- .vbi_fops.write = av7110_vbi_write,
-};
-
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
deleted file mode 100644
index b21bcce66708..000000000000
--- a/drivers/media/dvb/ttpci/budget.c
+++ /dev/null
@@ -1,811 +0,0 @@
-/*
- * budget.c: driver for the SAA7146 based Budget DVB cards
- *
- * Compiled from various sources by Michael Hunold <michael@mihu.de>
- *
- * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
- *
- * Copyright (C) 1999-2002 Ralph Metzler
- * & Marcus Metzler for convergence integrated media GmbH
- *
- * 26feb2004 Support for FS Activy Card (Grundig tuner) by
- * Michael Dreher <michael@5dot1.de>,
- * Oliver Endriss <o.endriss@gmx.de> and
- * Andreas 'randy' Weinberger
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
- *
- * the project's page is at http://www.linuxtv.org/
- */
-
-#include "budget.h"
-#include "stv0299.h"
-#include "ves1x93.h"
-#include "ves1820.h"
-#include "l64781.h"
-#include "tda8083.h"
-#include "s5h1420.h"
-#include "tda10086.h"
-#include "tda826x.h"
-#include "lnbp21.h"
-#include "bsru6.h"
-#include "bsbe1.h"
-#include "tdhd1.h"
-#include "stv6110x.h"
-#include "stv090x.h"
-#include "isl6423.h"
-
-static int diseqc_method;
-module_param(diseqc_method, int, 0444);
-MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static void Set22K (struct budget *budget, int state)
-{
- struct saa7146_dev *dev=budget->dev;
- dprintk(2, "budget: %p\n", budget);
- saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
-}
-
-/* Diseqc functions only for TT Budget card */
-/* taken from the Skyvision DVB driver by
- Ralph Metzler <rjkm@metzlerbros.de> */
-
-static void DiseqcSendBit (struct budget *budget, int data)
-{
- struct saa7146_dev *dev=budget->dev;
- dprintk(2, "budget: %p\n", budget);
-
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
- udelay(data ? 500 : 1000);
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
- udelay(data ? 1000 : 500);
-}
-
-static void DiseqcSendByte (struct budget *budget, int data)
-{
- int i, par=1, d;
-
- dprintk(2, "budget: %p\n", budget);
-
- for (i=7; i>=0; i--) {
- d = (data>>i)&1;
- par ^= d;
- DiseqcSendBit(budget, d);
- }
-
- DiseqcSendBit(budget, par);
-}
-
-static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
-{
- struct saa7146_dev *dev=budget->dev;
- int i;
-
- dprintk(2, "budget: %p\n", budget);
-
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
- mdelay(16);
-
- for (i=0; i<len; i++)
- DiseqcSendByte(budget, msg[i]);
-
- mdelay(16);
-
- if (burst!=-1) {
- if (burst)
- DiseqcSendByte(budget, 0xff);
- else {
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
- mdelay(12);
- udelay(500);
- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
- }
- msleep(20);
- }
-
- return 0;
-}
-
-/*
- * Routines for the Fujitsu Siemens Activy budget card
- * 22 kHz tone and DiSEqC are handled by the frontend.
- * Voltage must be set here.
- * GPIO 1: LNBP EN, GPIO 2: LNBP VSEL
- */
-static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
-{
- struct saa7146_dev *dev=budget->dev;
-
- dprintk(2, "budget: %p\n", budget);
-
- switch (voltage) {
- case SEC_VOLTAGE_13:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
- break;
- case SEC_VOLTAGE_18:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
- break;
- case SEC_VOLTAGE_OFF:
- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
-{
- struct budget* budget = (struct budget*) fe->dvb->priv;
-
- return SetVoltage_Activy (budget, voltage);
-}
-
-static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
-{
- struct budget* budget = (struct budget*) fe->dvb->priv;
-
- switch (tone) {
- case SEC_TONE_ON:
- Set22K (budget, 1);
- break;
-
- case SEC_TONE_OFF:
- Set22K (budget, 0);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
-{
- struct budget* budget = (struct budget*) fe->dvb->priv;
-
- SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
-
- return 0;
-}
-
-static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
-{
- struct budget* budget = (struct budget*) fe->dvb->priv;
-
- SendDiSEqCMsg (budget, 0, NULL, minicmd);
-
- return 0;
-}
-
-static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct budget* budget = (struct budget*) fe->dvb->priv;
- u8 pwr = 0;
- u8 buf[4];
- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
- u32 div = (c->frequency + 479500) / 125;
-
- if (c->frequency > 2000000)
- pwr = 3;
- else if (c->frequency > 1800000)
- pwr = 2;
- else if (c->frequency > 1600000)
- pwr = 1;
- else if (c->frequency > 1200000)
- pwr = 0;
- else if (c->frequency >= 1100000)
- pwr = 1;
- else pwr = 2;
-
- buf[0] = (div >> 8) & 0x7f;
- buf[1] = div & 0xff;
- buf[2] = ((div & 0x18000) >> 10) | 0x95;
- buf[3] = (pwr << 6) | 0x30;
-
- // NOTE: since we're using a prescaler of 2, we set the
- // divisor frequency to 62.5kHz and divide by 125 above
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
- return 0;
-}
-
-static struct ves1x93_config alps_bsrv2_config =
-{
- .demod_address = 0x08,
- .xin = 90100000UL,
- .invert_pwm = 0,
-};
-
-static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct budget* budget = (struct budget*) fe->dvb->priv;
- u32 div;
- u8 data[4];
- struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
-
- div = (c->frequency + 35937500 + 31250) / 62500;
-
- data[0] = (div >> 8) & 0x7f;
- data[1] = div & 0xff;
- data[2] = 0x85 | ((div >> 10) & 0x60);
- data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81);
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
- return 0;
-}
-
-static struct ves1820_config alps_tdbe2_config = {
- .demod_address = 0x09,
- .xin = 57840000UL,
- .invert = 1,
- .selagc = VES1820_SELAGC_SIGNAMPERR,
-};
-
-static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct budget *budget = fe->dvb->priv;
- u8 *tuner_addr = fe->tuner_priv;
- u32 div;
- u8 cfg, cpump, band_select;
- u8 data[4];
- struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) };
-
- if (tuner_addr)
- msg.addr = *tuner_addr;
- else
- msg.addr = 0x61;
-
- div = (36125000 + c->frequency) / 166666;
-
- cfg = 0x88;
-
- if (c->frequency < 175000000)
- cpump = 2;
- else if (c->frequency < 390000000)
- cpump = 1;
- else if (c->frequency < 470000000)
- cpump = 2;
- else if (c->frequency < 750000000)
- cpump = 1;
- else
- cpump = 3;
-
- if (c->frequency < 175000000)
- band_select = 0x0e;
- else if (c->frequency < 470000000)
- band_select = 0x05;
- else
- band_select = 0x03;
-
- data[0] = (div >> 8) & 0x7f;
- data[1] = div & 0xff;
- data[2] = ((div >> 10) & 0x60) | cfg;
- data[3] = (cpump << 6) | band_select;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
- return 0;
-}
-
-static struct l64781_config grundig_29504_401_config = {
- .demod_address = 0x55,
-};
-
-static struct l64781_config grundig_29504_401_config_activy = {
- .demod_address = 0x54,
-};
-
-static u8 tuner_address_grundig_29504_401_activy = 0x60;
-
-static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct budget* budget = (struct budget*) fe->dvb->priv;
- u32 div;
- u8 data[4];
- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
-
- div = c->frequency / 125;
- data[0] = (div >> 8) & 0x7f;
- data[1] = div & 0xff;
- data[2] = 0x8e;
- data[3] = 0x00;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
- return 0;
-}
-
-static struct tda8083_config grundig_29504_451_config = {
- .demod_address = 0x68,
-};
-
-static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct budget* budget = (struct budget*) fe->dvb->priv;
- u32 div;
- u8 data[4];
- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
-
- div = c->frequency / 1000;
- data[0] = (div >> 8) & 0x7f;
- data[1] = div & 0xff;
- data[2] = 0xc2;
-
- if (div < 1450)
- data[3] = 0x00;
- else if (div < 1850)
- data[3] = 0x40;
- else if (div < 2000)
- data[3] = 0x80;
- else
- data[3] = 0xc0;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
-
- return 0;
-}
-
-static struct s5h1420_config s5h1420_config = {
- .demod_address = 0x53,
- .invert = 1,
- .cdclk_polarity = 1,
-};
-
-static struct tda10086_config tda10086_config = {
- .demod_address = 0x0e,
- .invert = 0,
- .diseqc_tone = 1,
- .xtal_freq = TDA10086_XTAL_16M,
-};
-
-static struct stv0299_config alps_bsru6_config_activy = {
- .demod_address = 0x68,
- .inittab = alps_bsru6_inittab,
- .mclk = 88000000UL,
- .invert = 1,
- .op0_off = 1,
- .min_delay_ms = 100,
- .set_symbol_rate = alps_bsru6_set_symbol_rate,
-};
-
-static struct stv0299_config alps_bsbe1_config_activy = {
- .demod_address = 0x68,
- .inittab = alps_bsbe1_inittab,
- .mclk = 88000000UL,
- .invert = 1,
- .op0_off = 1,
- .min_delay_ms = 100,
- .set_symbol_rate = alps_bsbe1_set_symbol_rate,
-};
-
-static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
-{
- struct budget *budget = (struct budget *)fe->dvb->priv;
-
- return request_firmware(fw, name, &budget->dev->pci->dev);
-}
-
-
-static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
-{
- u8 val;
- struct i2c_msg msg[] = {
- { .addr = adr, .flags = 0, .buf = &reg, .len = 1 },
- { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
- };
-
- return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
-}
-
-static u8 read_pwm(struct budget* budget)
-{
- u8 b = 0xff;
- u8 pwm;
- struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
- { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
-
- if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
- pwm = 0x48;
-
- return pwm;
-}
-
-static struct stv090x_config tt1600_stv090x_config = {
- .device = STV0903,
- .demod_mode = STV090x_SINGLE,
- .clk_mode = STV090x_CLK_EXT,
-
- .xtal = 13500000,
- .address = 0x68,
-
- .ts1_mode = STV090x_TSMODE_DVBCI,
- .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
-
- .repeater_level = STV090x_RPTLEVEL_16,
-
- .tuner_init = NULL,
- .tuner_sleep = NULL,
- .tuner_set_mode = NULL,
- .tuner_set_frequency = NULL,
- .tuner_get_frequency = NULL,
- .tuner_set_bandwidth = NULL,
- .tuner_get_bandwidth = NULL,
- .tuner_set_bbgain = NULL,
- .tuner_get_bbgain = NULL,
- .tuner_set_refclk = NULL,
- .tuner_get_status = NULL,
-};
-
-static struct stv6110x_config tt1600_stv6110x_config = {
- .addr = 0x60,
- .refclk = 27000000,
- .clk_div = 2,
-};
-
-static struct isl6423_config tt1600_isl6423_config = {
- .current_max = SEC_CURRENT_515m,
- .curlim = SEC_CURRENT_LIM_ON,
- .mod_extern = 1,
- .addr = 0x08,
-};
-
-static void frontend_init(struct budget *budget)
-{
- (void)alps_bsbe1_config; /* avoid warning */
-
- switch(budget->dev->pci->subsystem_device) {
- case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
- case 0x1013:
- // try the ALPS BSRV2 first of all
- budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
- budget->dvb_frontend->ops.set_tone = budget_set_tone;
- break;
- }
-
- // try the ALPS BSRU6 now
- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
- budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
- if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) {
- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
- budget->dvb_frontend->ops.set_tone = budget_set_tone;
- }
- break;
- }
- break;
-
- case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
-
- budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
- break;
- }
- break;
-
- case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
-
- budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
- budget->dvb_frontend->tuner_priv = NULL;
- break;
- }
- break;
-
- case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */
- {
- int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67);
-
- if (subtype < 0)
- break;
- /* fixme: find a better way to identify the card */
- if (subtype < 0x36) {
- /* assume ALPS BSRU6 */
- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
- budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
- break;
- }
- } else {
- /* assume ALPS BSBE1 */
- /* reset tuner */
- saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO);
- msleep(50);
- saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI);
- msleep(250);
- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
- budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
- break;
- }
- }
- break;
- }
-
- case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
- budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
- }
- break;
-
- case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
- budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
- budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
- }
- break;
-
- case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
- budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy;
- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
- }
- break;
-
- case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
- budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
- printk("%s: No LNBP21 found!\n", __func__);
- goto error_out;
- }
- break;
- }
-
- case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
- // gpio2 is connected to CLB - reset it + leave it high
- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
- msleep(1);
- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
- msleep(1);
-
- budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
- if (budget->dvb_frontend) {
- if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
- printk("%s: No tda826x found!\n", __func__);
- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
- printk("%s: No LNBP21 found!\n", __func__);
- goto error_out;
- }
- break;
- }
-
- case 0x101c: { /* TT S2-1600 */
- struct stv6110x_devctl *ctl;
- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
- msleep(50);
- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
- msleep(250);
-
- budget->dvb_frontend = dvb_attach(stv090x_attach,
- &tt1600_stv090x_config,
- &budget->i2c_adap,
- STV090x_DEMODULATOR_0);
-
- if (budget->dvb_frontend) {
-
- ctl = dvb_attach(stv6110x_attach,
- budget->dvb_frontend,
- &tt1600_stv6110x_config,
- &budget->i2c_adap);
-
- if (ctl) {
- tt1600_stv090x_config.tuner_init = ctl->tuner_init;
- tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
- tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
- tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
- tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
- tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
- tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
- tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
- tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
- tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
- tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
-
- /* call the init function once to initialize
- tuner's clock output divider and demod's
- master clock */
- if (budget->dvb_frontend->ops.init)
- budget->dvb_frontend->ops.init(budget->dvb_frontend);
-
- if (dvb_attach(isl6423_attach,
- budget->dvb_frontend,
- &budget->i2c_adap,
- &tt1600_isl6423_config) == NULL) {
- printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
- goto error_out;
- }
- } else {
- printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
- goto error_out;
- }
- }
- }
- break;
- }
-
- if (budget->dvb_frontend == NULL) {
- printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
- budget->dev->pci->vendor,
- budget->dev->pci->device,
- budget->dev->pci->subsystem_vendor,
- budget->dev->pci->subsystem_device);
- } else {
- if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend))
- goto error_out;
- }
- return;
-
-error_out:
- printk("budget: Frontend registration failed!\n");
- dvb_frontend_detach(budget->dvb_frontend);
- budget->dvb_frontend = NULL;
- return;
-}
-
-static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
-{
- struct budget *budget = NULL;
- int err;
-
- budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
- if( NULL == budget ) {
- return -ENOMEM;
- }
-
- dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
-
- dev->ext_priv = budget;
-
- err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
- if (err) {
- printk("==> failed\n");
- kfree (budget);
- return err;
- }
-
- budget->dvb_adapter.priv = budget;
- frontend_init(budget);
-
- ttpci_budget_init_hooks(budget);
-
- return 0;
-}
-
-static int budget_detach (struct saa7146_dev* dev)
-{
- struct budget *budget = (struct budget*) dev->ext_priv;
- int err;
-
- if (budget->dvb_frontend) {
- dvb_unregister_frontend(budget->dvb_frontend);
- dvb_frontend_detach(budget->dvb_frontend);
- }
-
- err = ttpci_budget_deinit (budget);
-
- kfree (budget);
- dev->ext_priv = NULL;
-
- return err;
-}
-
-static struct saa7146_extension budget_extension;
-
-MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
-MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
-MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
-MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
-MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
-MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT);
-MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
-MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
-MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
-MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
-
-static struct pci_device_id pci_tbl[] = {
- MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
- MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
- MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
- MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
- MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
- MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
- MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
- MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
- MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
- MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
- MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
- {
- .vendor = 0,
- }
-};
-
-MODULE_DEVICE_TABLE(pci, pci_tbl);
-
-static struct saa7146_extension budget_extension = {
- .name = "budget dvb",
- .flags = SAA7146_USE_I2C_IRQ,
-
- .module = THIS_MODULE,
- .pci_tbl = pci_tbl,
- .attach = budget_attach,
- .detach = budget_detach,
-
- .irq_mask = MASK_10,
- .irq_func = ttpci_budget_irq10_handler,
-};
-
-static int __init budget_init(void)
-{
- return saa7146_register_extension(&budget_extension);
-}
-
-static void __exit budget_exit(void)
-{
- saa7146_unregister_extension(&budget_extension);
-}
-
-module_init(budget_init);
-module_exit(budget_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
-MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
- "budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig
deleted file mode 100644
index 2663ae39b886..000000000000
--- a/drivers/media/dvb/ttusb-budget/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-config DVB_TTUSB_BUDGET
- tristate "Technotrend/Hauppauge Nova-USB devices"
- depends on DVB_CORE && USB && I2C && PCI
- select DVB_CX22700 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select DVB_VES1820 if !DVB_FE_CUSTOMISE
- select DVB_TDA8083 if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_STV0297 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- help
- Support for external USB adapters designed by Technotrend and
- produced by Hauppauge, shipped under the brand name 'Nova-USB'.
-
- These devices don't have a MPEG decoder built in, so you need
- an external software decoder to watch TV.
-
- Say Y if you own such a device and want to use it.
diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile
deleted file mode 100644
index 8d6c4acb7f1d..000000000000
--- a/drivers/media/dvb/ttusb-budget/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends
diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile
deleted file mode 100644
index ed28b5384d20..000000000000
--- a/drivers/media/dvb/ttusb-dec/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core/
diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/firewire/Kconfig
index f3e9448c3955..f3e9448c3955 100644
--- a/drivers/media/dvb/firewire/Kconfig
+++ b/drivers/media/firewire/Kconfig
diff --git a/drivers/media/firewire/Makefile b/drivers/media/firewire/Makefile
new file mode 100644
index 000000000000..239481344d7c
--- /dev/null
+++ b/drivers/media/firewire/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_DVB_FIREDTV) += firedtv.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-core
diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
index d1a1a1324ef8..d1a1a1324ef8 100644
--- a/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/drivers/media/firewire/firedtv-avc.c
diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index e5ebdbfe8c19..e5ebdbfe8c19 100644
--- a/drivers/media/dvb/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/firewire/firedtv-dvb.c
index eb7496eab130..eb7496eab130 100644
--- a/drivers/media/dvb/firewire/firedtv-dvb.c
+++ b/drivers/media/firewire/firedtv-dvb.c
diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/firewire/firedtv-fe.c
index 6fe9793b98b3..6fe9793b98b3 100644
--- a/drivers/media/dvb/firewire/firedtv-fe.c
+++ b/drivers/media/firewire/firedtv-fe.c
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c
index e24ec539a5fd..e24ec539a5fd 100644
--- a/drivers/media/dvb/firewire/firedtv-fw.c
+++ b/drivers/media/firewire/firedtv-fw.c
diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/firewire/firedtv-rc.c
index f82d4a93feb3..f82d4a93feb3 100644
--- a/drivers/media/dvb/firewire/firedtv-rc.c
+++ b/drivers/media/firewire/firedtv-rc.c
diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/firewire/firedtv.h
index 4fdcd8cb7530..4fdcd8cb7530 100644
--- a/drivers/media/dvb/firewire/firedtv.h
+++ b/drivers/media/firewire/firedtv.h
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
new file mode 100644
index 000000000000..24d78e28e493
--- /dev/null
+++ b/drivers/media/i2c/Kconfig
@@ -0,0 +1,591 @@
+#
+# Generic video config states
+#
+
+config VIDEO_BTCX
+ depends on PCI
+ tristate
+
+config VIDEO_TVEEPROM
+ tristate
+ depends on I2C
+
+#
+# Multimedia Video device configuration
+#
+
+if VIDEO_V4L2
+
+config VIDEO_IR_I2C
+ tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT
+ depends on I2C && RC_CORE
+ default y
+ ---help---
+ Most boards have an IR chip directly connected via GPIO. However,
+ some video boards have the IR connected via I2C bus.
+
+ If your board doesn't have an I2C IR chip, you may disable this
+ option.
+
+ In doubt, say Y.
+
+#
+# Encoder / Decoder module configuration
+#
+
+menu "Encoders, decoders, sensors and other helper chips"
+ visible if !MEDIA_SUBDRV_AUTOSELECT
+
+comment "Audio decoders, processors and mixers"
+
+config VIDEO_TVAUDIO
+ tristate "Simple audio decoder chips"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for several audio decoder chips found on some bt8xx boards:
+ Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
+ tea6320, tea6420, tda8425, ta8874z.
+ Microchip: pic16c54 based design on ProVideo PV951 board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvaudio.
+
+config VIDEO_TDA7432
+ tristate "Philips TDA7432 audio processor"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for tda7432 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda7432.
+
+config VIDEO_TDA9840
+ tristate "Philips TDA9840 audio processor"
+ depends on I2C
+ ---help---
+ Support for tda9840 audio decoder chip found on some Zoran boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda9840.
+
+config VIDEO_TEA6415C
+ tristate "Philips TEA6415C audio processor"
+ depends on I2C
+ ---help---
+ Support for tea6415c audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6415c.
+
+config VIDEO_TEA6420
+ tristate "Philips TEA6420 audio processor"
+ depends on I2C
+ ---help---
+ Support for tea6420 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6420.
+
+config VIDEO_MSP3400
+ tristate "Micronas MSP34xx audio decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Micronas MSP34xx series of audio decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called msp3400.
+
+config VIDEO_CS5345
+ tristate "Cirrus Logic CS5345 audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Cirrus Logic CS5345 24-bit, 192 kHz
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs5345.
+
+config VIDEO_CS53L32A
+ tristate "Cirrus Logic CS53L32A audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Cirrus Logic CS53L32A low voltage
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs53l32a.
+
+config VIDEO_TLV320AIC23B
+ tristate "Texas Instruments TLV320AIC23B audio codec"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TLV320AIC23B audio codec.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tlv320aic23b.
+
+config VIDEO_WM8775
+ tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Wolfson Microelectronics WM8775 high
+ performance stereo A/D Converter with a 4 channel input mixer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8775.
+
+config VIDEO_WM8739
+ tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Wolfson Microelectronics WM8739
+ stereo A/D Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8739.
+
+config VIDEO_VP27SMPX
+ tristate "Panasonic VP27s internal MPX"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the internal MPX of the Panasonic VP27s tuner.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vp27smpx.
+
+comment "RDS decoders"
+
+config VIDEO_SAA6588
+ tristate "SAA6588 Radio Chip RDS decoder support"
+ depends on VIDEO_V4L2 && I2C
+
+ help
+ Support for this Radio Data System (RDS) decoder. This allows
+ seeing radio station identification transmitted using this
+ standard.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa6588.
+
+comment "Video decoders"
+
+config VIDEO_ADV7180
+ tristate "Analog Devices ADV7180 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7180 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7180.
+
+config VIDEO_ADV7183
+ tristate "Analog Devices ADV7183 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ V4l2 subdevice driver for the Analog Devices
+ ADV7183 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7183.
+
+config VIDEO_ADV7604
+ tristate "Analog Devices ADV7604 decoder"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ Support for the Analog Devices ADV7604 video decoder.
+
+ This is a Analog Devices Component/Graphics Digitizer
+ with 4:1 Multiplexed HDMI Receiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7604.
+
+config VIDEO_BT819
+ tristate "BT819A VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT819A video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt819.
+
+config VIDEO_BT856
+ tristate "BT856 VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT856 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt856.
+
+config VIDEO_BT866
+ tristate "BT866 VideoStream decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT866 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt866.
+
+config VIDEO_KS0127
+ tristate "KS0127 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for KS0127 video decoder.
+
+ This chip is used on AverMedia AVS6EYES Zoran-based MJPEG
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ks0127.
+
+config VIDEO_SAA7110
+ tristate "Philips SAA7110 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7110 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7110.
+
+config VIDEO_SAA711X
+ tristate "Philips SAA7111/3/4/5 video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7111/3/4/5 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7115.
+
+config VIDEO_SAA7191
+ tristate "Philips SAA7191 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7191 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7191.
+
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
+config VIDEO_TVP5150
+ tristate "Texas Instruments TVP5150 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP5150 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp5150.
+
+config VIDEO_TVP7002
+ tristate "Texas Instruments TVP7002 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP7002 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp7002.
+
+config VIDEO_VPX3220
+ tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for VPX322x video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpx3220.
+
+comment "Video and audio decoders"
+
+config VIDEO_SAA717X
+ tristate "Philips SAA7171/3/4 audio/video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7171/3/4 audio/video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa717x.
+
+source "drivers/media/i2c/cx25840/Kconfig"
+
+comment "MPEG video encoders"
+
+config VIDEO_CX2341X
+ tristate "Conexant CX2341x MPEG encoders"
+ depends on VIDEO_V4L2
+ ---help---
+ Support for the Conexant CX23416 MPEG encoders
+ and CX23415 MPEG encoder/decoders.
+
+ This module currently supports the encoding functions only.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx2341x.
+
+comment "Video encoders"
+
+config VIDEO_SAA7127
+ tristate "Philips SAA7127/9 digital video encoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7127/9 digital video encoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7127.
+
+config VIDEO_SAA7185
+ tristate "Philips SAA7185 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7185 video encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7185.
+
+config VIDEO_ADV7170
+ tristate "Analog Devices ADV7170 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7170 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7170.
+
+config VIDEO_ADV7175
+ tristate "Analog Devices ADV7175 video encoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7175 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7175.
+
+config VIDEO_ADV7343
+ tristate "ADV7343 video encoder"
+ depends on I2C
+ help
+ Support for Analog Devices I2C bus based ADV7343 encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7343.
+
+config VIDEO_ADV7393
+ tristate "ADV7393 video encoder"
+ depends on I2C
+ help
+ Support for Analog Devices I2C bus based ADV7393 encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7393.
+
+config VIDEO_AD9389B
+ tristate "Analog Devices AD9389B encoder"
+ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ Support for the Analog Devices AD9389B video encoder.
+
+ This is a Analog Devices HDMI transmitter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad9389b.
+
+config VIDEO_AK881X
+ tristate "AK8813/AK8814 video encoders"
+ depends on I2C
+ help
+ Video output driver for AKM AK8813 and AK8814 TV encoders
+
+comment "Camera sensor devices"
+
+config VIDEO_APTINA_PLL
+ tristate
+
+config VIDEO_SMIAPP_PLL
+ tristate
+
+config VIDEO_OV7670
+ tristate "OmniVision OV7670 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV7670 VGA camera. It currently only works with the M88ALP01
+ controller.
+
+config VIDEO_VS6624
+ tristate "ST VS6624 sensor support"
+ depends on VIDEO_V4L2 && I2C
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the ST VS6624
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vs6624.
+
+config VIDEO_MT9M032
+ tristate "MT9M032 camera sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ select VIDEO_APTINA_PLL
+ ---help---
+ This driver supports MT9M032 camera sensors from Aptina, monochrome
+ models only.
+
+config VIDEO_MT9P031
+ tristate "Aptina MT9P031 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ select VIDEO_APTINA_PLL
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt9p031 5 Mpixel camera.
+
+config VIDEO_MT9T001
+ tristate "Aptina MT9T001 support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Aptina
+ (Micron) mt0t001 3 Mpixel camera.
+
+config VIDEO_MT9V011
+ tristate "Micron mt9v011 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Micron
+ mt0v011 1.3 Mpixel camera. It currently only works with the
+ em28xx driver.
+
+config VIDEO_MT9V032
+ tristate "Micron MT9V032 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Micron
+ MT9V032 752x480 CMOS sensor.
+
+config VIDEO_TCM825X
+ tristate "TCM825x camera sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a driver for the Toshiba TCM825x VGA camera sensor.
+ It is used for example in Nokia N800.
+
+config VIDEO_SR030PC30
+ tristate "Siliconfile SR030PC30 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This driver supports SR030PC30 VGA camera from Siliconfile
+
+config VIDEO_NOON010PC30
+ tristate "Siliconfile NOON010PC30 sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This driver supports NOON010PC30 CIF camera from Siliconfile
+
+source "drivers/media/i2c/m5mols/Kconfig"
+
+config VIDEO_S5K6AA
+ tristate "Samsung S5K6AAFX sensor support"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
+ camera sensor with an embedded SoC image signal processor.
+
+config VIDEO_S5K4ECGX
+ tristate "Samsung S5K4ECGX sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ ---help---
+ This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M
+ camera sensor with an embedded SoC image signal processor.
+
+source "drivers/media/i2c/smiapp/Kconfig"
+
+comment "Flash devices"
+
+config VIDEO_ADP1653
+ tristate "ADP1653 flash support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a driver for the ADP1653 flash controller. It is used for
+ example in Nokia N900.
+
+config VIDEO_AS3645A
+ tristate "AS3645A flash driver support"
+ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a driver for the AS3645A and LM3555 flash controllers. It has
+ build in control for flash, torch and indicator LEDs.
+
+comment "Video improvement chips"
+
+config VIDEO_UPD64031A
+ tristate "NEC Electronics uPD64031A Ghost Reduction"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the NEC Electronics uPD64031A Ghost Reduction
+ video chip. It is most often found in NTSC TV cards made for
+ Japan and is used to reduce the 'ghosting' effect that can
+ be present in analog TV broadcasts.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64031a.
+
+config VIDEO_UPD64083
+ tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the NEC Electronics uPD64083 3-Dimensional Y/C
+ separation video chip. It is used to improve the quality of
+ the colors of a composite signal.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64083.
+
+comment "Miscelaneous helper chips"
+
+config VIDEO_THS7303
+ tristate "THS7303 Video Amplifier"
+ depends on I2C
+ help
+ Support for TI THS7303 video amplifier
+
+ To compile this driver as a module, choose M here: the
+ module will be called ths7303.
+
+config VIDEO_M52790
+ tristate "Mitsubishi M52790 A/V switch"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Mitsubishi M52790 A/V switch.
+
+ To compile this driver as a module, choose M here: the
+ module will be called m52790.
+endmenu
+
+menu "Sensors used on soc_camera driver"
+
+if SOC_CAMERA
+ source "drivers/media/i2c/soc_camera/Kconfig"
+endif
+
+endmenu
+
+endif
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
new file mode 100644
index 000000000000..b1d62dfd49b8
--- /dev/null
+++ b/drivers/media/i2c/Makefile
@@ -0,0 +1,67 @@
+msp3400-objs := msp3400-driver.o msp3400-kthreads.o
+obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
+
+obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/
+obj-$(CONFIG_VIDEO_CX25840) += cx25840/
+obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
+obj-y += soc_camera/
+
+obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
+obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
+obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
+obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
+obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
+obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
+obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
+obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
+obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
+obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
+obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
+obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
+obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
+obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
+obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
+obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
+obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
+obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
+obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o
+obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o
+obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
+obj-$(CONFIG_VIDEO_BT819) += bt819.o
+obj-$(CONFIG_VIDEO_BT856) += bt856.o
+obj-$(CONFIG_VIDEO_BT866) += bt866.o
+obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
+obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
+obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
+obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
+obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
+obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
+obj-$(CONFIG_VIDEO_M52790) += m52790.o
+obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
+obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
+obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
+obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
+obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
+obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
+obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
+obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
+obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
+obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
+obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
+obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
+obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
+obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
+obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o
+obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
+obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
+obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
+obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o
+obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
+obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
+obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c
new file mode 100644
index 000000000000..c2886b6a727d
--- /dev/null
+++ b/drivers/media/i2c/ad9389b.c
@@ -0,0 +1,1328 @@
+/*
+ * Analog Devices AD9389B/AD9889B video encoder driver
+ *
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * References (c = chapter, p = page):
+ * REF_01 - Analog Devices, Programming Guide, AD9889B/AD9389B,
+ * HDMI Transitter, Rev. A, October 2010
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/ad9389b.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+MODULE_DESCRIPTION("Analog Devices AD9389B/AD9889B video encoder driver");
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_AUTHOR("Martin Bugge <marbugge@cisco.com>");
+MODULE_LICENSE("GPL");
+
+#define MASK_AD9389B_EDID_RDY_INT 0x04
+#define MASK_AD9389B_MSEN_INT 0x40
+#define MASK_AD9389B_HPD_INT 0x80
+
+#define MASK_AD9389B_HPD_DETECT 0x40
+#define MASK_AD9389B_MSEN_DETECT 0x20
+#define MASK_AD9389B_EDID_RDY 0x10
+
+#define EDID_MAX_RETRIES (8)
+#define EDID_DELAY 250
+#define EDID_MAX_SEGM 8
+
+/*
+**********************************************************************
+*
+* Arrays with configuration parameters for the AD9389B
+*
+**********************************************************************
+*/
+
+struct i2c_reg_value {
+ u8 reg;
+ u8 value;
+};
+
+struct ad9389b_state_edid {
+ /* total number of blocks */
+ u32 blocks;
+ /* Number of segments read */
+ u32 segments;
+ u8 data[EDID_MAX_SEGM * 256];
+ /* Number of EDID read retries left */
+ unsigned read_retries;
+};
+
+struct ad9389b_state {
+ struct ad9389b_platform_data pdata;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler hdl;
+ int chip_revision;
+ /* Is the ad9389b powered on? */
+ bool power_on;
+ /* Did we receive hotplug and rx-sense signals? */
+ bool have_monitor;
+ /* timings from s_dv_timings */
+ struct v4l2_dv_timings dv_timings;
+ /* controls */
+ struct v4l2_ctrl *hdmi_mode_ctrl;
+ struct v4l2_ctrl *hotplug_ctrl;
+ struct v4l2_ctrl *rx_sense_ctrl;
+ struct v4l2_ctrl *have_edid0_ctrl;
+ struct v4l2_ctrl *rgb_quantization_range_ctrl;
+ struct i2c_client *edid_i2c_client;
+ struct ad9389b_state_edid edid;
+ /* Running counter of the number of detected EDIDs (for debugging) */
+ unsigned edid_detect_counter;
+ struct workqueue_struct *work_queue;
+ struct delayed_work edid_handler; /* work entry */
+};
+
+static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd);
+static bool ad9389b_check_edid_status(struct v4l2_subdev *sd);
+static void ad9389b_setup(struct v4l2_subdev *sd);
+static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+
+static inline struct ad9389b_state *get_ad9389b_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ad9389b_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct ad9389b_state, hdl)->sd;
+}
+
+/* ------------------------ I2C ----------------------------------------------- */
+
+static int ad9389b_rd(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret == 0)
+ return 0;
+ }
+ v4l2_err(sd, "I2C Write Problem\n");
+ return ret;
+}
+
+/* To set specific bits in the register, a clear-mask is given (to be AND-ed),
+ and then the value-mask (to be OR-ed). */
+static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg,
+ u8 clr_mask, u8 val_mask)
+{
+ ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask);
+}
+
+static void ad9389b_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ int i;
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ for (i = 0; i < len; i++)
+ buf[i] = i2c_smbus_read_byte_data(state->edid_i2c_client, i);
+}
+
+static inline bool ad9389b_have_hotplug(struct v4l2_subdev *sd)
+{
+ return ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT;
+}
+
+static inline bool ad9389b_have_rx_sense(struct v4l2_subdev *sd)
+{
+ return ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT;
+}
+
+static void ad9389b_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode)
+{
+ ad9389b_wr_and_or(sd, 0x17, 0xe7, (mode & 0x3)<<3);
+ ad9389b_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5);
+}
+
+static void ad9389b_csc_coeff(struct v4l2_subdev *sd,
+ u16 A1, u16 A2, u16 A3, u16 A4,
+ u16 B1, u16 B2, u16 B3, u16 B4,
+ u16 C1, u16 C2, u16 C3, u16 C4)
+{
+ /* A */
+ ad9389b_wr_and_or(sd, 0x18, 0xe0, A1>>8);
+ ad9389b_wr(sd, 0x19, A1);
+ ad9389b_wr_and_or(sd, 0x1A, 0xe0, A2>>8);
+ ad9389b_wr(sd, 0x1B, A2);
+ ad9389b_wr_and_or(sd, 0x1c, 0xe0, A3>>8);
+ ad9389b_wr(sd, 0x1d, A3);
+ ad9389b_wr_and_or(sd, 0x1e, 0xe0, A4>>8);
+ ad9389b_wr(sd, 0x1f, A4);
+
+ /* B */
+ ad9389b_wr_and_or(sd, 0x20, 0xe0, B1>>8);
+ ad9389b_wr(sd, 0x21, B1);
+ ad9389b_wr_and_or(sd, 0x22, 0xe0, B2>>8);
+ ad9389b_wr(sd, 0x23, B2);
+ ad9389b_wr_and_or(sd, 0x24, 0xe0, B3>>8);
+ ad9389b_wr(sd, 0x25, B3);
+ ad9389b_wr_and_or(sd, 0x26, 0xe0, B4>>8);
+ ad9389b_wr(sd, 0x27, B4);
+
+ /* C */
+ ad9389b_wr_and_or(sd, 0x28, 0xe0, C1>>8);
+ ad9389b_wr(sd, 0x29, C1);
+ ad9389b_wr_and_or(sd, 0x2A, 0xe0, C2>>8);
+ ad9389b_wr(sd, 0x2B, C2);
+ ad9389b_wr_and_or(sd, 0x2C, 0xe0, C3>>8);
+ ad9389b_wr(sd, 0x2D, C3);
+ ad9389b_wr_and_or(sd, 0x2E, 0xe0, C4>>8);
+ ad9389b_wr(sd, 0x2F, C4);
+}
+
+static void ad9389b_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable)
+{
+ if (enable) {
+ u8 csc_mode = 0;
+
+ ad9389b_csc_conversion_mode(sd, csc_mode);
+ ad9389b_csc_coeff(sd,
+ 4096-564, 0, 0, 256,
+ 0, 4096-564, 0, 256,
+ 0, 0, 4096-564, 256);
+ /* enable CSC */
+ ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x1);
+ /* AVI infoframe: Limited range RGB (16-235) */
+ ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x02);
+ } else {
+ /* disable CSC */
+ ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x0);
+ /* AVI infoframe: Full range RGB (0-255) */
+ ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x04);
+ }
+}
+
+static void ad9389b_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ /* CEA format, not IT */
+ ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x00);
+ } else {
+ /* IT format */
+ ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x40);
+ }
+}
+
+static int ad9389b_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ switch (ctrl->val) {
+ case V4L2_DV_RGB_RANGE_AUTO:
+ /* automatic */
+ if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ /* cea format, RGB limited range (16-235) */
+ ad9389b_csc_rgb_full2limit(sd, true);
+ } else {
+ /* not cea format, RGB full range (0-255) */
+ ad9389b_csc_rgb_full2limit(sd, false);
+ }
+ break;
+ case V4L2_DV_RGB_RANGE_LIMITED:
+ /* RGB limited range (16-235) */
+ ad9389b_csc_rgb_full2limit(sd, true);
+ break;
+ case V4L2_DV_RGB_RANGE_FULL:
+ /* RGB full range (0-255) */
+ ad9389b_csc_rgb_full2limit(sd, false);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void ad9389b_set_manual_pll_gear(struct v4l2_subdev *sd, u32 pixelclock)
+{
+ u8 gear;
+
+ /* Workaround for TMDS PLL problem
+ * The TMDS PLL in AD9389b change gear when the chip is heated above a
+ * certain temperature. The output is disabled when the PLL change gear
+ * so the monitor has to lock on the signal again. A workaround for
+ * this is to use the manual PLL gears. This is a solution from Analog
+ * Devices that is not documented in the datasheets.
+ * 0x98 [7] = enable manual gearing. 0x98 [6:4] = gear
+ *
+ * The pixel frequency ranges are based on readout of the gear the
+ * automatic gearing selects for different pixel clocks
+ * (read from 0x9e [3:1]).
+ */
+
+ if (pixelclock > 140000000)
+ gear = 0xc0; /* 4th gear */
+ else if (pixelclock > 117000000)
+ gear = 0xb0; /* 3rd gear */
+ else if (pixelclock > 87000000)
+ gear = 0xa0; /* 2nd gear */
+ else if (pixelclock > 60000000)
+ gear = 0x90; /* 1st gear */
+ else
+ gear = 0x80; /* 0th gear */
+
+ ad9389b_wr_and_or(sd, 0x98, 0x0f, gear);
+}
+
+/* ------------------------------ CTRL OPS ------------------------------ */
+
+static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ v4l2_dbg(1, debug, sd,
+ "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val);
+
+ if (state->hdmi_mode_ctrl == ctrl) {
+ /* Set HDMI or DVI-D */
+ ad9389b_wr_and_or(sd, 0xaf, 0xfd,
+ ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00);
+ return 0;
+ }
+ if (state->rgb_quantization_range_ctrl == ctrl)
+ return ad9389b_set_rgb_quantization_mode(sd, ctrl);
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops ad9389b_ctrl_ops = {
+ .s_ctrl = ad9389b_s_ctrl,
+};
+
+/* ---------------------------- CORE OPS ------------------------------------------- */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = ad9389b_rd(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int ad9389b_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ad9389b_wr(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int ad9389b_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_AD9389B, 0);
+}
+
+static int ad9389b_log_status(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ struct ad9389b_state_edid *edid = &state->edid;
+
+ static const char * const states[] = {
+ "in reset",
+ "reading EDID",
+ "idle",
+ "initializing HDCP",
+ "HDCP enabled",
+ "initializing HDCP repeater",
+ "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"
+ };
+ static const char * const errors[] = {
+ "no error",
+ "bad receiver BKSV",
+ "Ri mismatch",
+ "Pj mismatch",
+ "i2c error",
+ "timed out",
+ "max repeater cascade exceeded",
+ "hash check failed",
+ "too many devices",
+ "9", "A", "B", "C", "D", "E", "F"
+ };
+
+ u8 manual_gear;
+
+ v4l2_info(sd, "chip revision %d\n", state->chip_revision);
+ v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off");
+ v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n",
+ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ?
+ "detected" : "no",
+ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ?
+ "detected" : "no",
+ edid->segments ? "found" : "no", edid->blocks);
+ if (state->have_monitor) {
+ v4l2_info(sd, "%s output %s\n",
+ (ad9389b_rd(sd, 0xaf) & 0x02) ?
+ "HDMI" : "DVI-D",
+ (ad9389b_rd(sd, 0xa1) & 0x3c) ?
+ "disabled" : "enabled");
+ }
+ v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ?
+ "encrypted" : "no encryption");
+ v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n",
+ states[ad9389b_rd(sd, 0xc8) & 0xf],
+ errors[ad9389b_rd(sd, 0xc8) >> 4],
+ state->edid_detect_counter,
+ ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96));
+ manual_gear = ad9389b_rd(sd, 0x98) & 0x80;
+ v4l2_info(sd, "ad9389b: RGB quantization: %s range\n",
+ ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full");
+ v4l2_info(sd, "ad9389b: %s gear %d\n",
+ manual_gear ? "manual" : "automatic",
+ manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) :
+ ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1));
+ if (state->have_monitor) {
+ if (ad9389b_rd(sd, 0xaf) & 0x02) {
+ /* HDMI only */
+ u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80;
+ u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x02) << 8 |
+ ad9389b_rd(sd, 0x03);
+ u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2;
+ u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f;
+ u32 CTS;
+
+ if (manual_cts)
+ CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x08) << 8 |
+ ad9389b_rd(sd, 0x09);
+ else
+ CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x05) << 8 |
+ ad9389b_rd(sd, 0x06);
+ N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 |
+ ad9389b_rd(sd, 0x02) << 8 |
+ ad9389b_rd(sd, 0x03);
+
+ v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n",
+ manual_cts ? "manual" : "automatic", N, CTS);
+
+ v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n",
+ vic_detect, vic_sent);
+ }
+ }
+ if (state->dv_timings.type == V4L2_DV_BT_656_1120) {
+ struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt;
+ u32 frame_width = bt->width + bt->hfrontporch +
+ bt->hsync + bt->hbackporch;
+ u32 frame_height = bt->height + bt->vfrontporch +
+ bt->vsync + bt->vbackporch;
+ u32 frame_size = frame_width * frame_height;
+
+ v4l2_info(sd, "timings: %ux%u%s%u (%ux%u). Pix freq. = %u Hz. Polarities = 0x%x\n",
+ bt->width, bt->height, bt->interlaced ? "i" : "p",
+ frame_size > 0 ? (unsigned)bt->pixelclock / frame_size : 0,
+ frame_width, frame_height,
+ (unsigned)bt->pixelclock, bt->polarities);
+ } else {
+ v4l2_info(sd, "no timings set\n");
+ }
+ return 0;
+}
+
+/* Power up/down ad9389b */
+static int ad9389b_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ struct ad9389b_platform_data *pdata = &state->pdata;
+ const int retries = 20;
+ int i;
+
+ v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off");
+
+ state->power_on = on;
+
+ if (!on) {
+ /* Power down */
+ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40);
+ return true;
+ }
+
+ /* Power up */
+ /* The ad9389b does not always come up immediately.
+ Retry multiple times. */
+ for (i = 0; i < retries; i++) {
+ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x0);
+ if ((ad9389b_rd(sd, 0x41) & 0x40) == 0)
+ break;
+ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40);
+ msleep(10);
+ }
+ if (i == retries) {
+ v4l2_dbg(1, debug, sd, "failed to powerup the ad9389b\n");
+ ad9389b_s_power(sd, 0);
+ return false;
+ }
+ if (i > 1)
+ v4l2_dbg(1, debug, sd,
+ "needed %d retries to powerup the ad9389b\n", i);
+
+ /* Select chip: AD9389B */
+ ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10);
+
+ /* Reserved registers that must be set according to REF_01 p. 11*/
+ ad9389b_wr_and_or(sd, 0x98, 0xf0, 0x07);
+ ad9389b_wr(sd, 0x9c, 0x38);
+ ad9389b_wr_and_or(sd, 0x9d, 0xfc, 0x01);
+
+ /* Differential output drive strength */
+ if (pdata->diff_data_drive_strength > 0)
+ ad9389b_wr(sd, 0xa2, pdata->diff_data_drive_strength);
+ else
+ ad9389b_wr(sd, 0xa2, 0x87);
+
+ if (pdata->diff_clk_drive_strength > 0)
+ ad9389b_wr(sd, 0xa3, pdata->diff_clk_drive_strength);
+ else
+ ad9389b_wr(sd, 0xa3, 0x87);
+
+ ad9389b_wr(sd, 0x0a, 0x01);
+ ad9389b_wr(sd, 0xbb, 0xff);
+
+ /* Set number of attempts to read the EDID */
+ ad9389b_wr(sd, 0xc9, 0xf);
+ return true;
+}
+
+/* Enable interrupts */
+static void ad9389b_set_isr(struct v4l2_subdev *sd, bool enable)
+{
+ u8 irqs = MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT;
+ u8 irqs_rd;
+ int retries = 100;
+
+ /* The datasheet says that the EDID ready interrupt should be
+ disabled if there is no hotplug. */
+ if (!enable)
+ irqs = 0;
+ else if (ad9389b_have_hotplug(sd))
+ irqs |= MASK_AD9389B_EDID_RDY_INT;
+
+ /*
+ * This i2c write can fail (approx. 1 in 1000 writes). But it
+ * is essential that this register is correct, so retry it
+ * multiple times.
+ *
+ * Note that the i2c write does not report an error, but the readback
+ * clearly shows the wrong value.
+ */
+ do {
+ ad9389b_wr(sd, 0x94, irqs);
+ irqs_rd = ad9389b_rd(sd, 0x94);
+ } while (retries-- && irqs_rd != irqs);
+
+ if (irqs_rd != irqs)
+ v4l2_err(sd, "Could not set interrupts: hw failure?\n");
+}
+
+/* Interrupt handler */
+static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ u8 irq_status;
+
+ /* disable interrupts to prevent a race condition */
+ ad9389b_set_isr(sd, false);
+ irq_status = ad9389b_rd(sd, 0x96);
+ /* clear detected interrupts */
+ ad9389b_wr(sd, 0x96, irq_status);
+
+ if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT))
+ ad9389b_check_monitor_present_status(sd);
+ if (irq_status & MASK_AD9389B_EDID_RDY_INT)
+ ad9389b_check_edid_status(sd);
+
+ /* enable interrupts */
+ ad9389b_set_isr(sd, true);
+ *handled = true;
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops ad9389b_core_ops = {
+ .log_status = ad9389b_log_status,
+ .g_chip_ident = ad9389b_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ad9389b_g_register,
+ .s_register = ad9389b_s_register,
+#endif
+ .s_power = ad9389b_s_power,
+ .interrupt_service_routine = ad9389b_isr,
+};
+
+/* ------------------------------ PAD OPS ------------------------------ */
+
+static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ if (edid->pad != 0)
+ return -EINVAL;
+ if (edid->blocks == 0 || edid->blocks > 256)
+ return -EINVAL;
+ if (!edid->edid)
+ return -EINVAL;
+ if (!state->edid.segments) {
+ v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n");
+ return -ENODATA;
+ }
+ if (edid->start_block >= state->edid.segments * 2)
+ return -E2BIG;
+ if (edid->blocks + edid->start_block >= state->edid.segments * 2)
+ edid->blocks = state->edid.segments * 2 - edid->start_block;
+ memcpy(edid->edid, &state->edid.data[edid->start_block * 128],
+ 128 * edid->blocks);
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = {
+ .get_edid = ad9389b_get_edid,
+};
+
+/* ------------------------------ VIDEO OPS ------------------------------ */
+
+/* Enable/disable ad9389b output */
+static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));
+
+ ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c));
+ if (enable) {
+ ad9389b_check_monitor_present_status(sd);
+ } else {
+ ad9389b_s_power(sd, 0);
+ state->have_monitor = false;
+ }
+ return 0;
+}
+
+static const struct v4l2_dv_timings ad9389b_timings[] = {
+ V4L2_DV_BT_CEA_720X480P59_94,
+ V4L2_DV_BT_CEA_720X576P50,
+ V4L2_DV_BT_CEA_1280X720P24,
+ V4L2_DV_BT_CEA_1280X720P25,
+ V4L2_DV_BT_CEA_1280X720P30,
+ V4L2_DV_BT_CEA_1280X720P50,
+ V4L2_DV_BT_CEA_1280X720P60,
+ V4L2_DV_BT_CEA_1920X1080P24,
+ V4L2_DV_BT_CEA_1920X1080P25,
+ V4L2_DV_BT_CEA_1920X1080P30,
+ V4L2_DV_BT_CEA_1920X1080P50,
+ V4L2_DV_BT_CEA_1920X1080P60,
+
+ V4L2_DV_BT_DMT_640X350P85,
+ V4L2_DV_BT_DMT_640X400P85,
+ V4L2_DV_BT_DMT_720X400P85,
+ V4L2_DV_BT_DMT_640X480P60,
+ V4L2_DV_BT_DMT_640X480P72,
+ V4L2_DV_BT_DMT_640X480P75,
+ V4L2_DV_BT_DMT_640X480P85,
+ V4L2_DV_BT_DMT_800X600P56,
+ V4L2_DV_BT_DMT_800X600P60,
+ V4L2_DV_BT_DMT_800X600P72,
+ V4L2_DV_BT_DMT_800X600P75,
+ V4L2_DV_BT_DMT_800X600P85,
+ V4L2_DV_BT_DMT_848X480P60,
+ V4L2_DV_BT_DMT_1024X768P60,
+ V4L2_DV_BT_DMT_1024X768P70,
+ V4L2_DV_BT_DMT_1024X768P75,
+ V4L2_DV_BT_DMT_1024X768P85,
+ V4L2_DV_BT_DMT_1152X864P75,
+ V4L2_DV_BT_DMT_1280X768P60_RB,
+ V4L2_DV_BT_DMT_1280X768P60,
+ V4L2_DV_BT_DMT_1280X768P75,
+ V4L2_DV_BT_DMT_1280X768P85,
+ V4L2_DV_BT_DMT_1280X800P60_RB,
+ V4L2_DV_BT_DMT_1280X800P60,
+ V4L2_DV_BT_DMT_1280X800P75,
+ V4L2_DV_BT_DMT_1280X800P85,
+ V4L2_DV_BT_DMT_1280X960P60,
+ V4L2_DV_BT_DMT_1280X960P85,
+ V4L2_DV_BT_DMT_1280X1024P60,
+ V4L2_DV_BT_DMT_1280X1024P75,
+ V4L2_DV_BT_DMT_1280X1024P85,
+ V4L2_DV_BT_DMT_1360X768P60,
+ V4L2_DV_BT_DMT_1400X1050P60_RB,
+ V4L2_DV_BT_DMT_1400X1050P60,
+ V4L2_DV_BT_DMT_1400X1050P75,
+ V4L2_DV_BT_DMT_1400X1050P85,
+ V4L2_DV_BT_DMT_1440X900P60_RB,
+ V4L2_DV_BT_DMT_1440X900P60,
+ V4L2_DV_BT_DMT_1600X1200P60,
+ V4L2_DV_BT_DMT_1680X1050P60_RB,
+ V4L2_DV_BT_DMT_1680X1050P60,
+ V4L2_DV_BT_DMT_1792X1344P60,
+ V4L2_DV_BT_DMT_1856X1392P60,
+ V4L2_DV_BT_DMT_1920X1200P60_RB,
+ V4L2_DV_BT_DMT_1366X768P60,
+ V4L2_DV_BT_DMT_1920X1080P60,
+ {},
+};
+
+static int ad9389b_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ int i;
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ /* quick sanity check */
+ if (timings->type != V4L2_DV_BT_656_1120)
+ return -EINVAL;
+
+ if (timings->bt.interlaced)
+ return -EINVAL;
+ if (timings->bt.pixelclock < 27000000 ||
+ timings->bt.pixelclock > 170000000)
+ return -EINVAL;
+
+ /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
+ if the format is listed in ad9389b_timings[] */
+ for (i = 0; ad9389b_timings[i].bt.width; i++) {
+ if (v4l_match_dv_timings(timings, &ad9389b_timings[i], 0)) {
+ *timings = ad9389b_timings[i];
+ break;
+ }
+ }
+
+ timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS;
+
+ /* save timings */
+ state->dv_timings = *timings;
+
+ /* update quantization range based on new dv_timings */
+ ad9389b_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl);
+
+ /* update PLL gear based on new dv_timings */
+ if (state->pdata.tmds_pll_gear == AD9389B_TMDS_PLL_GEAR_SEMI_AUTOMATIC)
+ ad9389b_set_manual_pll_gear(sd, (u32)timings->bt.pixelclock);
+
+ /* update AVI infoframe */
+ ad9389b_set_IT_content_AVI_InfoFrame(sd);
+
+ return 0;
+}
+
+static int ad9389b_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ if (!timings)
+ return -EINVAL;
+
+ *timings = state->dv_timings;
+
+ return 0;
+}
+
+static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ if (timings->index >= ARRAY_SIZE(ad9389b_timings))
+ return -EINVAL;
+
+ memset(timings->reserved, 0, sizeof(timings->reserved));
+ timings->timings = ad9389b_timings[timings->index];
+ return 0;
+}
+
+static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ cap->type = V4L2_DV_BT_656_1120;
+ cap->bt.max_width = 1920;
+ cap->bt.max_height = 1200;
+ cap->bt.min_pixelclock = 27000000;
+ cap->bt.max_pixelclock = 170000000;
+ cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
+ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM;
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops ad9389b_video_ops = {
+ .s_stream = ad9389b_s_stream,
+ .s_dv_timings = ad9389b_s_dv_timings,
+ .g_dv_timings = ad9389b_g_dv_timings,
+ .enum_dv_timings = ad9389b_enum_dv_timings,
+ .dv_timings_cap = ad9389b_dv_timings_cap,
+};
+
+static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable)
+{
+ v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis"));
+
+ if (enable)
+ ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x80);
+ else
+ ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x40);
+
+ return 0;
+}
+
+static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ u32 N;
+
+ switch (freq) {
+ case 32000: N = 4096; break;
+ case 44100: N = 6272; break;
+ case 48000: N = 6144; break;
+ case 88200: N = 12544; break;
+ case 96000: N = 12288; break;
+ case 176400: N = 25088; break;
+ case 192000: N = 24576; break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set N (used with CTS to regenerate the audio clock) */
+ ad9389b_wr(sd, 0x01, (N >> 16) & 0xf);
+ ad9389b_wr(sd, 0x02, (N >> 8) & 0xff);
+ ad9389b_wr(sd, 0x03, N & 0xff);
+
+ return 0;
+}
+
+static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ u32 i2s_sf;
+
+ switch (freq) {
+ case 32000: i2s_sf = 0x30; break;
+ case 44100: i2s_sf = 0x00; break;
+ case 48000: i2s_sf = 0x20; break;
+ case 88200: i2s_sf = 0x80; break;
+ case 96000: i2s_sf = 0xa0; break;
+ case 176400: i2s_sf = 0xc0; break;
+ case 192000: i2s_sf = 0xe0; break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Set sampling frequency for I2S audio to 48 kHz */
+ ad9389b_wr_and_or(sd, 0x15, 0xf, i2s_sf);
+
+ return 0;
+}
+
+static int ad9389b_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config)
+{
+ /* TODO based on input/output/config */
+ /* TODO See datasheet "Programmers guide" p. 39-40 */
+
+ /* Only 2 channels in use for application */
+ ad9389b_wr_and_or(sd, 0x50, 0x1f, 0x20);
+ /* Speaker mapping */
+ ad9389b_wr(sd, 0x51, 0x00);
+
+ /* TODO Where should this be placed? */
+ /* 16 bit audio word length */
+ ad9389b_wr_and_or(sd, 0x14, 0xf0, 0x02);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_audio_ops ad9389b_audio_ops = {
+ .s_stream = ad9389b_s_audio_stream,
+ .s_clock_freq = ad9389b_s_clock_freq,
+ .s_i2s_clock_freq = ad9389b_s_i2s_clock_freq,
+ .s_routing = ad9389b_s_routing,
+};
+
+/* --------------------- SUBDEV OPS --------------------------------------- */
+
+static const struct v4l2_subdev_ops ad9389b_ops = {
+ .core = &ad9389b_core_ops,
+ .video = &ad9389b_video_ops,
+ .audio = &ad9389b_audio_ops,
+ .pad = &ad9389b_pad_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd,
+ int segment, u8 *buf)
+{
+ int i, j;
+
+ if (debug < lvl)
+ return;
+
+ v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment);
+ for (i = 0; i < 256; i += 16) {
+ u8 b[128];
+ u8 *bp = b;
+
+ if (i == 128)
+ v4l2_dbg(lvl, debug, sd, "\n");
+ for (j = i; j < i + 16; j++) {
+ sprintf(bp, "0x%02x, ", buf[j]);
+ bp += 6;
+ }
+ bp[0] = '\0';
+ v4l2_dbg(lvl, debug, sd, "%s\n", b);
+ }
+}
+
+static void ad9389b_edid_handler(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct ad9389b_state *state = container_of(dwork,
+ struct ad9389b_state, edid_handler);
+ struct v4l2_subdev *sd = &state->sd;
+ struct ad9389b_edid_detect ed;
+
+ v4l2_dbg(1, debug, sd, "%s:\n", __func__);
+
+ if (ad9389b_check_edid_status(sd)) {
+ /* Return if we received the EDID. */
+ return;
+ }
+
+ if (ad9389b_have_hotplug(sd)) {
+ /* We must retry reading the EDID several times, it is possible
+ * that initially the EDID couldn't be read due to i2c errors
+ * (DVI connectors are particularly prone to this problem). */
+ if (state->edid.read_retries) {
+ state->edid.read_retries--;
+ /* EDID read failed, trigger a retry */
+ ad9389b_wr(sd, 0xc9, 0xf);
+ queue_delayed_work(state->work_queue,
+ &state->edid_handler, EDID_DELAY);
+ return;
+ }
+ }
+
+ /* We failed to read the EDID, so send an event for this. */
+ ed.present = false;
+ ed.segment = ad9389b_rd(sd, 0xc4);
+ v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed);
+ v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__);
+}
+
+static void ad9389b_audio_setup(struct v4l2_subdev *sd)
+{
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+
+ ad9389b_s_i2s_clock_freq(sd, 48000);
+ ad9389b_s_clock_freq(sd, 48000);
+ ad9389b_s_routing(sd, 0, 0, 0);
+}
+
+/* Initial setup of AD9389b */
+
+/* Configure hdmi transmitter. */
+static void ad9389b_setup(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+
+ /* Input format: RGB 4:4:4 */
+ ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0);
+ /* Output format: RGB 4:4:4 */
+ ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0);
+ /* CSC fixed point: +/-2, 1st order interpolation 4:2:2 -> 4:4:4 up
+ conversion, Aspect ratio: 16:9 */
+ ad9389b_wr_and_or(sd, 0x17, 0xe1, 0x0e);
+ /* Disable pixel repetition and CSC */
+ ad9389b_wr_and_or(sd, 0x3b, 0x9e, 0x0);
+ /* Output format: RGB 4:4:4, Active Format Information is valid. */
+ ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08);
+ /* Underscanned */
+ ad9389b_wr_and_or(sd, 0x46, 0x3f, 0x80);
+ /* Setup video format */
+ ad9389b_wr(sd, 0x3c, 0x0);
+ /* Active format aspect ratio: same as picure. */
+ ad9389b_wr(sd, 0x47, 0x80);
+ /* No encryption */
+ ad9389b_wr_and_or(sd, 0xaf, 0xef, 0x0);
+ /* Positive clk edge capture for input video clock */
+ ad9389b_wr_and_or(sd, 0xba, 0x1f, 0x60);
+
+ ad9389b_audio_setup(sd);
+
+ v4l2_ctrl_handler_setup(&state->hdl);
+
+ ad9389b_set_IT_content_AVI_InfoFrame(sd);
+}
+
+static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd)
+{
+ struct ad9389b_monitor_detect mdt;
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ mdt.present = state->have_monitor;
+ v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt);
+}
+
+static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ /* read hotplug and rx-sense state */
+ u8 status = ad9389b_rd(sd, 0x42);
+
+ v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n",
+ __func__,
+ status,
+ status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "",
+ status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : "");
+
+ if ((status & MASK_AD9389B_HPD_DETECT) &&
+ ((status & MASK_AD9389B_MSEN_DETECT) || state->edid.segments)) {
+ v4l2_dbg(1, debug, sd,
+ "%s: hotplug and (rx-sense or edid)\n", __func__);
+ if (!state->have_monitor) {
+ v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__);
+ state->have_monitor = true;
+ ad9389b_set_isr(sd, true);
+ if (!ad9389b_s_power(sd, true)) {
+ v4l2_dbg(1, debug, sd,
+ "%s: monitor detected, powerup failed\n", __func__);
+ return;
+ }
+ ad9389b_setup(sd);
+ ad9389b_notify_monitor_detect(sd);
+ state->edid.read_retries = EDID_MAX_RETRIES;
+ queue_delayed_work(state->work_queue,
+ &state->edid_handler, EDID_DELAY);
+ }
+ } else if (status & MASK_AD9389B_HPD_DETECT) {
+ v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__);
+ state->edid.read_retries = EDID_MAX_RETRIES;
+ queue_delayed_work(state->work_queue,
+ &state->edid_handler, EDID_DELAY);
+ } else if (!(status & MASK_AD9389B_HPD_DETECT)) {
+ v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__);
+ if (state->have_monitor) {
+ v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__);
+ state->have_monitor = false;
+ ad9389b_notify_monitor_detect(sd);
+ }
+ ad9389b_s_power(sd, false);
+ memset(&state->edid, 0, sizeof(struct ad9389b_state_edid));
+ }
+
+ /* update read only ctrls */
+ v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0);
+ v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0);
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
+}
+
+static bool edid_block_verify_crc(u8 *edid_block)
+{
+ int i;
+ u8 sum = 0;
+
+ for (i = 0; i < 127; i++)
+ sum += *(edid_block + i);
+ return ((255 - sum + 1) == edid_block[127]);
+}
+
+static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ u32 blocks = state->edid.blocks;
+ u8 *data = state->edid.data;
+
+ if (edid_block_verify_crc(&data[segment * 256])) {
+ if ((segment + 1) * 2 <= blocks)
+ return edid_block_verify_crc(&data[segment * 256 + 128]);
+ return true;
+ }
+ return false;
+}
+
+static bool ad9389b_check_edid_status(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ struct ad9389b_edid_detect ed;
+ int segment;
+ u8 edidRdy = ad9389b_rd(sd, 0xc5);
+
+ v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n",
+ __func__, EDID_MAX_RETRIES - state->edid.read_retries);
+
+ if (!(edidRdy & MASK_AD9389B_EDID_RDY))
+ return false;
+
+ segment = ad9389b_rd(sd, 0xc4);
+ if (segment >= EDID_MAX_SEGM) {
+ v4l2_err(sd, "edid segment number too big\n");
+ return false;
+ }
+ v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment);
+ ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]);
+ ad9389b_dbg_dump_edid(2, debug, sd, segment,
+ &state->edid.data[segment * 256]);
+ if (segment == 0) {
+ state->edid.blocks = state->edid.data[0x7e] + 1;
+ v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n",
+ __func__, state->edid.blocks);
+ }
+ if (!edid_segment_verify_crc(sd, segment)) {
+ /* edid crc error, force reread of edid segment */
+ ad9389b_s_power(sd, false);
+ ad9389b_s_power(sd, true);
+ return false;
+ }
+ /* one more segment read ok */
+ state->edid.segments = segment + 1;
+ if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) {
+ /* Request next EDID segment */
+ v4l2_dbg(1, debug, sd, "%s: request segment %d\n",
+ __func__, state->edid.segments);
+ ad9389b_wr(sd, 0xc9, 0xf);
+ ad9389b_wr(sd, 0xc4, state->edid.segments);
+ state->edid.read_retries = EDID_MAX_RETRIES;
+ queue_delayed_work(state->work_queue,
+ &state->edid_handler, EDID_DELAY);
+ return false;
+ }
+
+ /* report when we have all segments but report only for segment 0 */
+ ed.present = true;
+ ed.segment = 0;
+ v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed);
+ state->edid_detect_counter++;
+ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0);
+ return ed.present;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void ad9389b_init_setup(struct v4l2_subdev *sd)
+{
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+ struct ad9389b_state_edid *edid = &state->edid;
+
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+
+ /* clear all interrupts */
+ ad9389b_wr(sd, 0x96, 0xff);
+
+ memset(edid, 0, sizeof(struct ad9389b_state_edid));
+ state->have_monitor = false;
+ ad9389b_set_isr(sd, false);
+}
+
+static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ const struct v4l2_dv_timings dv1080p60 = V4L2_DV_BT_CEA_1920X1080P60;
+ struct ad9389b_state *state;
+ struct ad9389b_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_subdev *sd;
+ int err = -EIO;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n",
+ client->addr << 1);
+
+ state = kzalloc(sizeof(struct ad9389b_state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ /* Platform data */
+ if (pdata == NULL) {
+ v4l_err(client, "No platform data!\n");
+ err = -ENODEV;
+ goto err_free;
+ }
+ memcpy(&state->pdata, pdata, sizeof(state->pdata));
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &ad9389b_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+
+ /* private controls */
+
+ state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops,
+ V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI,
+ 0, V4L2_DV_TX_MODE_DVI_D);
+ state->hdmi_mode_ctrl->is_private = true;
+ state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0);
+ state->hotplug_ctrl->is_private = true;
+ state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0);
+ state->rx_sense_ctrl->is_private = true;
+ state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0);
+ state->have_edid0_ctrl->is_private = true;
+ state->rgb_quantization_range_ctrl =
+ v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops,
+ V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
+ 0, V4L2_DV_RGB_RANGE_AUTO);
+ state->rgb_quantization_range_ctrl->is_private = true;
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ err = hdl->error;
+
+ goto err_hdl;
+ }
+
+ state->pad.flags = MEDIA_PAD_FL_SINK;
+ err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ if (err)
+ goto err_hdl;
+
+ state->chip_revision = ad9389b_rd(sd, 0x0);
+ if (state->chip_revision != 2) {
+ v4l2_err(sd, "chip_revision %d != 2\n", state->chip_revision);
+ err = -EIO;
+ goto err_entity;
+ }
+ v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n",
+ ad9389b_rd(sd, 0x41), state->chip_revision);
+
+ state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1));
+ if (state->edid_i2c_client == NULL) {
+ v4l2_err(sd, "failed to register edid i2c client\n");
+ goto err_entity;
+ }
+
+ state->work_queue = create_singlethread_workqueue(sd->name);
+ if (state->work_queue == NULL) {
+ v4l2_err(sd, "could not create workqueue\n");
+ goto err_unreg;
+ }
+
+ INIT_DELAYED_WORK(&state->edid_handler, ad9389b_edid_handler);
+ state->dv_timings = dv1080p60;
+
+ ad9389b_init_setup(sd);
+ ad9389b_set_isr(sd, true);
+
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+ return 0;
+
+err_unreg:
+ i2c_unregister_device(state->edid_i2c_client);
+err_entity:
+ media_entity_cleanup(&sd->entity);
+err_hdl:
+ v4l2_ctrl_handler_free(&state->hdl);
+err_free:
+ kfree(state);
+ return err;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int ad9389b_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ad9389b_state *state = get_ad9389b_state(sd);
+
+ state->chip_revision = -1;
+
+ v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+
+ ad9389b_s_stream(sd, false);
+ ad9389b_s_audio_stream(sd, false);
+ ad9389b_init_setup(sd);
+ cancel_delayed_work(&state->edid_handler);
+ i2c_unregister_device(state->edid_i2c_client);
+ destroy_workqueue(state->work_queue);
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(get_ad9389b_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_device_id ad9389b_id[] = {
+ { "ad9389b", V4L2_IDENT_AD9389B },
+ { "ad9889b", V4L2_IDENT_AD9389B },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad9389b_id);
+
+static struct i2c_driver ad9389b_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ad9389b",
+ },
+ .probe = ad9389b_probe,
+ .remove = ad9389b_remove,
+ .id_table = ad9389b_id,
+};
+
+module_i2c_driver(ad9389b_driver);
diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
new file mode 100644
index 000000000000..18a38b38fcb8
--- /dev/null
+++ b/drivers/media/i2c/adp1653.c
@@ -0,0 +1,487 @@
+/*
+ * drivers/media/i2c/adp1653.c
+ *
+ * Copyright (C) 2008--2011 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Contributors:
+ * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ * Tuukka Toivonen <tuukkat76@gmail.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - fault interrupt handling
+ * - hardware strobe
+ * - power doesn't need to be ON if all lights are off
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <media/adp1653.h>
+#include <media/v4l2-device.h>
+
+#define TIMEOUT_MAX 820000
+#define TIMEOUT_STEP 54600
+#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
+ * TIMEOUT_STEP)
+#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
+ / TIMEOUT_STEP)
+#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
+
+/* Write values into ADP1653 registers. */
+static int adp1653_update_hw(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel;
+ u8 config = 0;
+ int rval;
+
+ out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+
+ switch (flash->led_mode->val) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ /* Flash mode, light on with strobe, duration from timer */
+ config = ADP1653_REG_CONFIG_TMR_CFG;
+ config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
+ << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
+ break;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ /* Torch mode, light immediately on, duration indefinite */
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->torch_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ break;
+ }
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval < 0)
+ return rval;
+
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
+ if (rval < 0)
+ return rval;
+
+ return 0;
+}
+
+static int adp1653_get_fault(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int fault;
+ int rval;
+
+ fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
+ if (IS_ERR_VALUE(fault))
+ return fault;
+
+ flash->fault |= fault;
+
+ if (!flash->fault)
+ return 0;
+
+ /* Clear faults. */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
+
+ rval = adp1653_update_hw(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ return flash->fault;
+}
+
+static int adp1653_strobe(struct adp1653_flash *flash, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
+ flash->indicator_intensity->val)
+ << ADP1653_REG_OUT_SEL_ILED_SHIFT;
+ int rval;
+
+ if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ if (!enable)
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
+ out_sel);
+
+ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
+ flash->flash_intensity->val)
+ << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
+ if (rval)
+ return rval;
+
+ /* Software strobe using i2c */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
+ ADP1653_REG_SW_STROBE_SW_STROBE);
+ if (rval)
+ return rval;
+ return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+
+ ctrl->cur.val = 0;
+
+ if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+
+ flash->fault = 0;
+
+ return 0;
+}
+
+static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct adp1653_flash *flash =
+ container_of(ctrl->handler, struct adp1653_flash, ctrls);
+ int rval;
+
+ rval = adp1653_get_fault(flash);
+ if (IS_ERR_VALUE(rval))
+ return rval;
+ if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
+ ADP1653_REG_FAULT_FLT_OT |
+ ADP1653_REG_FAULT_FLT_OV)) &&
+ (ctrl->id == V4L2_CID_FLASH_STROBE ||
+ ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
+ ctrl->id == V4L2_CID_FLASH_LED_MODE))
+ return -EBUSY;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_STROBE:
+ return adp1653_strobe(flash, 1);
+ case V4L2_CID_FLASH_STROBE_STOP:
+ return adp1653_strobe(flash, 0);
+ }
+
+ return adp1653_update_hw(flash);
+}
+
+static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
+ .g_volatile_ctrl = adp1653_get_ctrl,
+ .s_ctrl = adp1653_set_ctrl,
+};
+
+static int adp1653_init_controls(struct adp1653_flash *flash)
+{
+ struct v4l2_ctrl *fault;
+
+ v4l2_ctrl_handler_init(&flash->ctrls, 9);
+
+ flash->led_mode =
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_LED_MODE,
+ V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+ flash->flash_timeout =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
+ flash->platform_data->max_flash_timeout,
+ TIMEOUT_STEP,
+ flash->platform_data->max_flash_timeout);
+ flash->flash_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY,
+ ADP1653_FLASH_INTENSITY_MIN,
+ flash->platform_data->max_flash_intensity,
+ 1, flash->platform_data->max_flash_intensity);
+ flash->torch_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ ADP1653_TORCH_INTENSITY_MIN,
+ flash->platform_data->max_torch_intensity,
+ ADP1653_FLASH_INTENSITY_STEP,
+ flash->platform_data->max_torch_intensity);
+ flash->indicator_intensity =
+ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ ADP1653_INDICATOR_INTENSITY_MIN,
+ flash->platform_data->max_indicator_intensity,
+ ADP1653_INDICATOR_INTENSITY_STEP,
+ ADP1653_INDICATOR_INTENSITY_MIN);
+ fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE
+ | V4L2_FLASH_FAULT_OVER_TEMPERATURE
+ | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+
+ if (flash->ctrls.error)
+ return flash->ctrls.error;
+
+ fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ flash->subdev.ctrl_handler = &flash->ctrls;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int
+adp1653_init_device(struct adp1653_flash *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ /* Clear FAULT register by writing zero to OUT_SEL */
+ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
+ if (rval < 0) {
+ dev_err(&client->dev, "failed writing fault register\n");
+ return -EIO;
+ }
+
+ mutex_lock(flash->ctrls.lock);
+ /* Reset faults before reading new ones. */
+ flash->fault = 0;
+ rval = adp1653_get_fault(flash);
+ mutex_unlock(flash->ctrls.lock);
+ if (rval > 0) {
+ dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
+ return -EIO;
+ }
+
+ mutex_lock(flash->ctrls.lock);
+ rval = adp1653_update_hw(flash);
+ mutex_unlock(flash->ctrls.lock);
+ if (rval) {
+ dev_err(&client->dev,
+ "adp1653_update_hw failed at %s\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+__adp1653_set_power(struct adp1653_flash *flash, int on)
+{
+ int ret;
+
+ ret = flash->platform_data->power(&flash->subdev, on);
+ if (ret < 0)
+ return ret;
+
+ if (!on)
+ return 0;
+
+ ret = adp1653_init_device(flash);
+ if (ret < 0)
+ flash->platform_data->power(&flash->subdev, 0);
+
+ return ret;
+}
+
+static int
+adp1653_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+ int ret = 0;
+
+ mutex_lock(&flash->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (flash->power_count == !on) {
+ ret = __adp1653_set_power(flash, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ /* Update the power count. */
+ flash->power_count += on ? 1 : -1;
+ WARN_ON(flash->power_count < 0);
+
+done:
+ mutex_unlock(&flash->power_lock);
+ return ret;
+}
+
+static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 1);
+}
+
+static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return adp1653_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops adp1653_core_ops = {
+ .s_power = adp1653_set_power,
+};
+
+static const struct v4l2_subdev_ops adp1653_ops = {
+ .core = &adp1653_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
+ .open = adp1653_open,
+ .close = adp1653_close,
+};
+
+/* --------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int adp1653_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 0);
+}
+
+static int adp1653_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ if (!flash->power_count)
+ return 0;
+
+ return __adp1653_set_power(flash, 1);
+}
+
+#else
+
+#define adp1653_suspend NULL
+#define adp1653_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int adp1653_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct adp1653_flash *flash;
+ int ret;
+
+ /* we couldn't work without platform data */
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->platform_data = client->dev.platform_data;
+
+ mutex_init(&flash->power_lock);
+
+ v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
+ flash->subdev.internal_ops = &adp1653_internal_ops;
+ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = adp1653_init_controls(flash);
+ if (ret)
+ goto free_and_quit;
+
+ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ if (ret < 0)
+ goto free_and_quit;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ return 0;
+
+free_and_quit:
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
+ return ret;
+}
+
+static int __exit adp1653_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct adp1653_flash *flash = to_adp1653_flash(subdev);
+
+ v4l2_device_unregister_subdev(&flash->subdev);
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ media_entity_cleanup(&flash->subdev.entity);
+ kfree(flash);
+ return 0;
+}
+
+static const struct i2c_device_id adp1653_id_table[] = {
+ { ADP1653_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
+
+static struct dev_pm_ops adp1653_pm_ops = {
+ .suspend = adp1653_suspend,
+ .resume = adp1653_resume,
+};
+
+static struct i2c_driver adp1653_i2c_driver = {
+ .driver = {
+ .name = ADP1653_NAME,
+ .pm = &adp1653_pm_ops,
+ },
+ .probe = adp1653_probe,
+ .remove = __exit_p(adp1653_remove),
+ .id_table = adp1653_id_table,
+};
+
+module_i2c_driver(adp1653_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/adv7170.c b/drivers/media/i2c/adv7170.c
index 6bc01fb98ff8..6bc01fb98ff8 100644
--- a/drivers/media/video/adv7170.c
+++ b/drivers/media/i2c/adv7170.c
diff --git a/drivers/media/video/adv7175.c b/drivers/media/i2c/adv7175.c
index c7640fab5730..c7640fab5730 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
diff --git a/drivers/media/video/adv7180.c b/drivers/media/i2c/adv7180.c
index 45ecf8db1eae..45ecf8db1eae 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
diff --git a/drivers/media/video/adv7183.c b/drivers/media/i2c/adv7183.c
index e1d4c89d7140..e1d4c89d7140 100644
--- a/drivers/media/video/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h
index 4a5b7d211d2f..4a5b7d211d2f 100644
--- a/drivers/media/video/adv7183_regs.h
+++ b/drivers/media/i2c/adv7183_regs.h
diff --git a/drivers/media/video/adv7343.c b/drivers/media/i2c/adv7343.c
index 2b5aa676a84e..2b5aa676a84e 100644
--- a/drivers/media/video/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
diff --git a/drivers/media/video/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h
index 446606764346..446606764346 100644
--- a/drivers/media/video/adv7343_regs.h
+++ b/drivers/media/i2c/adv7343_regs.h
diff --git a/drivers/media/video/adv7393.c b/drivers/media/i2c/adv7393.c
index 3dc6098c7267..3dc6098c7267 100644
--- a/drivers/media/video/adv7393.c
+++ b/drivers/media/i2c/adv7393.c
diff --git a/drivers/media/video/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h
index 78968330f0be..78968330f0be 100644
--- a/drivers/media/video/adv7393_regs.h
+++ b/drivers/media/i2c/adv7393_regs.h
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
new file mode 100644
index 000000000000..109bc9b12e74
--- /dev/null
+++ b/drivers/media/i2c/adv7604.c
@@ -0,0 +1,1959 @@
+/*
+ * adv7604 - Analog Devices ADV7604 video decoder driver
+ *
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * References (c = chapter, p = page):
+ * REF_01 - Analog devices, ADV7604, Register Settings Recommendations,
+ * Revision 2.5, June 2010
+ * REF_02 - Analog devices, Register map documentation, Documentation of
+ * the register maps, Software manual, Rev. F, June 2010
+ * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/adv7604.h>
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+
+MODULE_DESCRIPTION("Analog Devices ADV7604 video decoder driver");
+MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>");
+MODULE_AUTHOR("Mats Randgaard <mats.randgaard@cisco.com>");
+MODULE_LICENSE("GPL");
+
+/* ADV7604 system clock frequency */
+#define ADV7604_fsc (28636360)
+
+#define DIGITAL_INPUT ((state->prim_mode == ADV7604_PRIM_MODE_HDMI_COMP) || \
+ (state->prim_mode == ADV7604_PRIM_MODE_HDMI_GR))
+
+/*
+ **********************************************************************
+ *
+ * Arrays with configuration parameters for the ADV7604
+ *
+ **********************************************************************
+ */
+struct adv7604_state {
+ struct adv7604_platform_data pdata;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler hdl;
+ enum adv7604_prim_mode prim_mode;
+ struct v4l2_dv_timings timings;
+ u8 edid[256];
+ unsigned edid_blocks;
+ struct v4l2_fract aspect_ratio;
+ u32 rgb_quantization_range;
+ struct workqueue_struct *work_queues;
+ struct delayed_work delayed_work_enable_hotplug;
+ bool connector_hdmi;
+
+ /* i2c clients */
+ struct i2c_client *i2c_avlink;
+ struct i2c_client *i2c_cec;
+ struct i2c_client *i2c_infoframe;
+ struct i2c_client *i2c_esdp;
+ struct i2c_client *i2c_dpp;
+ struct i2c_client *i2c_afe;
+ struct i2c_client *i2c_repeater;
+ struct i2c_client *i2c_edid;
+ struct i2c_client *i2c_hdmi;
+ struct i2c_client *i2c_test;
+ struct i2c_client *i2c_cp;
+ struct i2c_client *i2c_vdp;
+
+ /* controls */
+ struct v4l2_ctrl *detect_tx_5v_ctrl;
+ struct v4l2_ctrl *analog_sampling_phase_ctrl;
+ struct v4l2_ctrl *free_run_color_manual_ctrl;
+ struct v4l2_ctrl *free_run_color_ctrl;
+ struct v4l2_ctrl *rgb_quantization_range_ctrl;
+};
+
+/* Supported CEA and DMT timings */
+static const struct v4l2_dv_timings adv7604_timings[] = {
+ V4L2_DV_BT_CEA_720X480P59_94,
+ V4L2_DV_BT_CEA_720X576P50,
+ V4L2_DV_BT_CEA_1280X720P24,
+ V4L2_DV_BT_CEA_1280X720P25,
+ V4L2_DV_BT_CEA_1280X720P30,
+ V4L2_DV_BT_CEA_1280X720P50,
+ V4L2_DV_BT_CEA_1280X720P60,
+ V4L2_DV_BT_CEA_1920X1080P24,
+ V4L2_DV_BT_CEA_1920X1080P25,
+ V4L2_DV_BT_CEA_1920X1080P30,
+ V4L2_DV_BT_CEA_1920X1080P50,
+ V4L2_DV_BT_CEA_1920X1080P60,
+
+ V4L2_DV_BT_DMT_640X350P85,
+ V4L2_DV_BT_DMT_640X400P85,
+ V4L2_DV_BT_DMT_720X400P85,
+ V4L2_DV_BT_DMT_640X480P60,
+ V4L2_DV_BT_DMT_640X480P72,
+ V4L2_DV_BT_DMT_640X480P75,
+ V4L2_DV_BT_DMT_640X480P85,
+ V4L2_DV_BT_DMT_800X600P56,
+ V4L2_DV_BT_DMT_800X600P60,
+ V4L2_DV_BT_DMT_800X600P72,
+ V4L2_DV_BT_DMT_800X600P75,
+ V4L2_DV_BT_DMT_800X600P85,
+ V4L2_DV_BT_DMT_848X480P60,
+ V4L2_DV_BT_DMT_1024X768P60,
+ V4L2_DV_BT_DMT_1024X768P70,
+ V4L2_DV_BT_DMT_1024X768P75,
+ V4L2_DV_BT_DMT_1024X768P85,
+ V4L2_DV_BT_DMT_1152X864P75,
+ V4L2_DV_BT_DMT_1280X768P60_RB,
+ V4L2_DV_BT_DMT_1280X768P60,
+ V4L2_DV_BT_DMT_1280X768P75,
+ V4L2_DV_BT_DMT_1280X768P85,
+ V4L2_DV_BT_DMT_1280X800P60_RB,
+ V4L2_DV_BT_DMT_1280X800P60,
+ V4L2_DV_BT_DMT_1280X800P75,
+ V4L2_DV_BT_DMT_1280X800P85,
+ V4L2_DV_BT_DMT_1280X960P60,
+ V4L2_DV_BT_DMT_1280X960P85,
+ V4L2_DV_BT_DMT_1280X1024P60,
+ V4L2_DV_BT_DMT_1280X1024P75,
+ V4L2_DV_BT_DMT_1280X1024P85,
+ V4L2_DV_BT_DMT_1360X768P60,
+ V4L2_DV_BT_DMT_1400X1050P60_RB,
+ V4L2_DV_BT_DMT_1400X1050P60,
+ V4L2_DV_BT_DMT_1400X1050P75,
+ V4L2_DV_BT_DMT_1400X1050P85,
+ V4L2_DV_BT_DMT_1440X900P60_RB,
+ V4L2_DV_BT_DMT_1440X900P60,
+ V4L2_DV_BT_DMT_1600X1200P60,
+ V4L2_DV_BT_DMT_1680X1050P60_RB,
+ V4L2_DV_BT_DMT_1680X1050P60,
+ V4L2_DV_BT_DMT_1792X1344P60,
+ V4L2_DV_BT_DMT_1856X1392P60,
+ V4L2_DV_BT_DMT_1920X1200P60_RB,
+ V4L2_DV_BT_DMT_1366X768P60,
+ V4L2_DV_BT_DMT_1920X1080P60,
+ { },
+};
+
+/* ----------------------------------------------------------------------- */
+
+static inline struct adv7604_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7604_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7604_state, hdl)->sd;
+}
+
+static inline unsigned hblanking(const struct v4l2_bt_timings *t)
+{
+ return t->hfrontporch + t->hsync + t->hbackporch;
+}
+
+static inline unsigned htotal(const struct v4l2_bt_timings *t)
+{
+ return t->width + t->hfrontporch + t->hsync + t->hbackporch;
+}
+
+static inline unsigned vblanking(const struct v4l2_bt_timings *t)
+{
+ return t->vfrontporch + t->vsync + t->vbackporch;
+}
+
+static inline unsigned vtotal(const struct v4l2_bt_timings *t)
+{
+ return t->height + t->vfrontporch + t->vsync + t->vbackporch;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static s32 adv_smbus_read_byte_data_check(struct i2c_client *client,
+ u8 command, bool check)
+{
+ union i2c_smbus_data data;
+
+ if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data))
+ return data.byte;
+ if (check)
+ v4l_err(client, "error reading %02x, %02x\n",
+ client->addr, command);
+ return -EIO;
+}
+
+static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command)
+{
+ return adv_smbus_read_byte_data_check(client, command, true);
+}
+
+static s32 adv_smbus_write_byte_data(struct i2c_client *client,
+ u8 command, u8 value)
+{
+ union i2c_smbus_data data;
+ int err;
+ int i;
+
+ data.byte = value;
+ for (i = 0; i < 3; i++) {
+ err = i2c_smbus_xfer(client->adapter, client->addr,
+ client->flags,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (!err)
+ break;
+ }
+ if (err < 0)
+ v4l_err(client, "error writing %02x, %02x, %02x\n",
+ client->addr, command, value);
+ return err;
+}
+
+static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client,
+ u8 command, unsigned length, const u8 *values)
+{
+ union i2c_smbus_data data;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+ data.block[0] = length;
+ memcpy(data.block + 1, values, length);
+ return i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int io_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return adv_smbus_read_byte_data(client, reg);
+}
+
+static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return adv_smbus_write_byte_data(client, reg, val);
+}
+
+static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return io_write(sd, reg, (io_read(sd, reg) & mask) | val);
+}
+
+static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_avlink, reg);
+}
+
+static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_avlink, reg, val);
+}
+
+static inline int cec_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_cec, reg);
+}
+
+static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_cec, reg, val);
+}
+
+static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val);
+}
+
+static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_infoframe, reg);
+}
+
+static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val);
+}
+
+static inline int esdp_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_esdp, reg);
+}
+
+static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_esdp, reg, val);
+}
+
+static inline int dpp_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_dpp, reg);
+}
+
+static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_dpp, reg, val);
+}
+
+static inline int afe_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_afe, reg);
+}
+
+static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_afe, reg, val);
+}
+
+static inline int rep_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_repeater, reg);
+}
+
+static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_repeater, reg, val);
+}
+
+static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val);
+}
+
+static inline int edid_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_edid, reg);
+}
+
+static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_edid, reg, val);
+}
+
+static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val)
+{
+ struct adv7604_state *state = to_state(sd);
+ struct i2c_client *client = state->i2c_edid;
+ u8 msgbuf0[1] = { 0 };
+ u8 msgbuf1[256];
+ struct i2c_msg msg[2] = { { client->addr, 0, 1, msgbuf0 },
+ { client->addr, 0 | I2C_M_RD, len, msgbuf1 }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) < 0)
+ return -EIO;
+ memcpy(val, msgbuf1, len);
+ return 0;
+}
+
+static void adv7604_delayed_work_enable_hotplug(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct adv7604_state *state = container_of(dwork, struct adv7604_state,
+ delayed_work_enable_hotplug);
+ struct v4l2_subdev *sd = &state->sd;
+
+ v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__);
+
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1);
+}
+
+static inline int edid_write_block(struct v4l2_subdev *sd,
+ unsigned len, const u8 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct adv7604_state *state = to_state(sd);
+ int err = 0;
+ int i;
+
+ v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len);
+
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0);
+
+ /* Disables I2C access to internal EDID ram from DDC port */
+ rep_write_and_or(sd, 0x77, 0xf0, 0x0);
+
+ for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX)
+ err = adv_smbus_write_i2c_block_data(state->i2c_edid, i,
+ I2C_SMBUS_BLOCK_MAX, val + i);
+ if (err)
+ return err;
+
+ /* adv7604 calculates the checksums and enables I2C access to internal
+ EDID ram from DDC port. */
+ rep_write_and_or(sd, 0x77, 0xf0, 0x1);
+
+ for (i = 0; i < 1000; i++) {
+ if (rep_read(sd, 0x7d) & 1)
+ break;
+ mdelay(1);
+ }
+ if (i == 1000) {
+ v4l_err(client, "error enabling edid\n");
+ return -EIO;
+ }
+
+ /* enable hotplug after 100 ms */
+ queue_delayed_work(state->work_queues,
+ &state->delayed_work_enable_hotplug, HZ / 10);
+ return 0;
+}
+
+static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_hdmi, reg);
+}
+
+static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val);
+}
+
+static inline int test_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_test, reg);
+}
+
+static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_test, reg, val);
+}
+
+static inline int cp_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_cp, reg);
+}
+
+static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_cp, reg, val);
+}
+
+static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val)
+{
+ return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val);
+}
+
+static inline int vdp_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_read_byte_data(state->i2c_vdp, reg);
+}
+
+static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ return adv_smbus_write_byte_data(state->i2c_vdp, reg, val);
+}
+
+/* ----------------------------------------------------------------------- */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static void adv7604_inv_register(struct v4l2_subdev *sd)
+{
+ v4l2_info(sd, "0x000-0x0ff: IO Map\n");
+ v4l2_info(sd, "0x100-0x1ff: AVLink Map\n");
+ v4l2_info(sd, "0x200-0x2ff: CEC Map\n");
+ v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n");
+ v4l2_info(sd, "0x400-0x4ff: ESDP Map\n");
+ v4l2_info(sd, "0x500-0x5ff: DPP Map\n");
+ v4l2_info(sd, "0x600-0x6ff: AFE Map\n");
+ v4l2_info(sd, "0x700-0x7ff: Repeater Map\n");
+ v4l2_info(sd, "0x800-0x8ff: EDID Map\n");
+ v4l2_info(sd, "0x900-0x9ff: HDMI Map\n");
+ v4l2_info(sd, "0xa00-0xaff: Test Map\n");
+ v4l2_info(sd, "0xb00-0xbff: CP Map\n");
+ v4l2_info(sd, "0xc00-0xcff: VDP Map\n");
+}
+
+static int adv7604_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->size = 1;
+ switch (reg->reg >> 8) {
+ case 0:
+ reg->val = io_read(sd, reg->reg & 0xff);
+ break;
+ case 1:
+ reg->val = avlink_read(sd, reg->reg & 0xff);
+ break;
+ case 2:
+ reg->val = cec_read(sd, reg->reg & 0xff);
+ break;
+ case 3:
+ reg->val = infoframe_read(sd, reg->reg & 0xff);
+ break;
+ case 4:
+ reg->val = esdp_read(sd, reg->reg & 0xff);
+ break;
+ case 5:
+ reg->val = dpp_read(sd, reg->reg & 0xff);
+ break;
+ case 6:
+ reg->val = afe_read(sd, reg->reg & 0xff);
+ break;
+ case 7:
+ reg->val = rep_read(sd, reg->reg & 0xff);
+ break;
+ case 8:
+ reg->val = edid_read(sd, reg->reg & 0xff);
+ break;
+ case 9:
+ reg->val = hdmi_read(sd, reg->reg & 0xff);
+ break;
+ case 0xa:
+ reg->val = test_read(sd, reg->reg & 0xff);
+ break;
+ case 0xb:
+ reg->val = cp_read(sd, reg->reg & 0xff);
+ break;
+ case 0xc:
+ reg->val = vdp_read(sd, reg->reg & 0xff);
+ break;
+ default:
+ v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
+ adv7604_inv_register(sd);
+ break;
+ }
+ return 0;
+}
+
+static int adv7604_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ switch (reg->reg >> 8) {
+ case 0:
+ io_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 1:
+ avlink_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 2:
+ cec_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 3:
+ infoframe_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 4:
+ esdp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 5:
+ dpp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 6:
+ afe_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 7:
+ rep_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 8:
+ edid_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 9:
+ hdmi_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 0xa:
+ test_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 0xb:
+ cp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ case 0xc:
+ vdp_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ break;
+ default:
+ v4l2_info(sd, "Register %03llx not supported\n", reg->reg);
+ adv7604_inv_register(sd);
+ break;
+ }
+ return 0;
+}
+#endif
+
+static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ /* port A only */
+ return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
+ ((io_read(sd, 0x6f) & 0x10) >> 4));
+}
+
+static void configure_free_run(struct v4l2_subdev *sd, const struct v4l2_bt_timings *timings)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u32 width = htotal(timings);
+ u32 height = vtotal(timings);
+ u16 ch1_fr_ll = (((u32)timings->pixelclock / 100) > 0) ?
+ ((width * (ADV7604_fsc / 100)) / ((u32)timings->pixelclock / 100)) : 0;
+
+ v4l2_dbg(2, debug, sd, "%s\n", __func__);
+
+ cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); /* CH1_FR_LL */
+ cp_write(sd, 0x90, ch1_fr_ll & 0xff); /* CH1_FR_LL */
+ cp_write(sd, 0xab, (height >> 4) & 0xff); /* CP_LCOUNT_MAX */
+ cp_write(sd, 0xac, (height & 0x0f) << 4); /* CP_LCOUNT_MAX */
+ /* TODO support interlaced */
+ cp_write(sd, 0x91, 0x10); /* INTERLACED */
+
+ /* Should only be set in auto-graphics mode [REF_02 p. 91-92] */
+ if ((io_read(sd, 0x00) == 0x07) && (io_read(sd, 0x01) == 0x02)) {
+ u16 cp_start_sav, cp_start_eav, cp_start_vbi, cp_end_vbi;
+ const u8 pll[2] = {
+ (0xc0 | ((width >> 8) & 0x1f)),
+ (width & 0xff)
+ };
+
+ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */
+ /* IO-map reg. 0x16 and 0x17 should be written in sequence */
+ if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) {
+ v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n");
+ return;
+ }
+
+ /* active video - horizontal timing */
+ cp_start_sav = timings->hsync + timings->hbackporch - 4;
+ cp_start_eav = width - timings->hfrontporch;
+ cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff);
+ cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | ((cp_start_eav >> 8) & 0x0f));
+ cp_write(sd, 0xa4, cp_start_eav & 0xff);
+
+ /* active video - vertical timing */
+ cp_start_vbi = height - timings->vfrontporch;
+ cp_end_vbi = timings->vsync + timings->vbackporch;
+ cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff);
+ cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | ((cp_end_vbi >> 8) & 0xf));
+ cp_write(sd, 0xa7, cp_end_vbi & 0xff);
+ } else {
+ /* reset to default values */
+ io_write(sd, 0x16, 0x43);
+ io_write(sd, 0x17, 0x5a);
+ cp_write(sd, 0xa2, 0x00);
+ cp_write(sd, 0xa3, 0x00);
+ cp_write(sd, 0xa4, 0x00);
+ cp_write(sd, 0xa5, 0x00);
+ cp_write(sd, 0xa6, 0x00);
+ cp_write(sd, 0xa7, 0x00);
+ }
+}
+
+
+static void set_rgb_quantization_range(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ switch (state->rgb_quantization_range) {
+ case V4L2_DV_RGB_RANGE_AUTO:
+ /* automatic */
+ if ((hdmi_read(sd, 0x05) & 0x80) ||
+ (state->prim_mode == ADV7604_PRIM_MODE_COMP) ||
+ (state->prim_mode == ADV7604_PRIM_MODE_RGB)) {
+ /* receiving HDMI or analog signal */
+ io_write_and_or(sd, 0x02, 0x0f, 0xf0);
+ } else {
+ /* receiving DVI-D signal */
+
+ /* ADV7604 selects RGB limited range regardless of
+ input format (CE/IT) in automatic mode */
+ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+
+ } else {
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ }
+ }
+ break;
+ case V4L2_DV_RGB_RANGE_LIMITED:
+ /* RGB limited range (16-235) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x00);
+ break;
+ case V4L2_DV_RGB_RANGE_FULL:
+ /* RGB full range (0-255) */
+ io_write_and_or(sd, 0x02, 0x0f, 0x10);
+ break;
+ }
+}
+
+
+static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct adv7604_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cp_write(sd, 0x3c, ctrl->val);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ cp_write(sd, 0x3a, ctrl->val);
+ return 0;
+ case V4L2_CID_SATURATION:
+ cp_write(sd, 0x3b, ctrl->val);
+ return 0;
+ case V4L2_CID_HUE:
+ cp_write(sd, 0x3d, ctrl->val);
+ return 0;
+ case V4L2_CID_DV_RX_RGB_RANGE:
+ state->rgb_quantization_range = ctrl->val;
+ set_rgb_quantization_range(sd);
+ return 0;
+ case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE:
+ /* Set the analog sampling phase. This is needed to find the
+ best sampling phase for analog video: an application or
+ driver has to try a number of phases and analyze the picture
+ quality before settling on the best performing phase. */
+ afe_write(sd, 0xc8, ctrl->val);
+ return 0;
+ case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL:
+ /* Use the default blue color for free running mode,
+ or supply your own. */
+ cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2));
+ return 0;
+ case V4L2_CID_ADV_RX_FREE_RUN_COLOR:
+ cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16);
+ cp_write(sd, 0xc1, (ctrl->val & 0x00ff00) >> 8);
+ cp_write(sd, 0xc2, (u8)(ctrl->val & 0x0000ff));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int adv7604_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_ADV7604, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline bool no_power(struct v4l2_subdev *sd)
+{
+ /* Entire chip or CP powered off */
+ return io_read(sd, 0x0c) & 0x24;
+}
+
+static inline bool no_signal_tmds(struct v4l2_subdev *sd)
+{
+ /* TODO port B, C and D */
+ return !(io_read(sd, 0x6a) & 0x10);
+}
+
+static inline bool no_lock_tmds(struct v4l2_subdev *sd)
+{
+ return (io_read(sd, 0x6a) & 0xe0) != 0xe0;
+}
+
+static inline bool no_lock_sspd(struct v4l2_subdev *sd)
+{
+ /* TODO channel 2 */
+ return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0);
+}
+
+static inline bool no_lock_stdi(struct v4l2_subdev *sd)
+{
+ /* TODO channel 2 */
+ return !(cp_read(sd, 0xb1) & 0x80);
+}
+
+static inline bool no_signal(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+ bool ret;
+
+ ret = no_power(sd);
+
+ ret |= no_lock_stdi(sd);
+ ret |= no_lock_sspd(sd);
+
+ if (DIGITAL_INPUT) {
+ ret |= no_lock_tmds(sd);
+ ret |= no_signal_tmds(sd);
+ }
+
+ return ret;
+}
+
+static inline bool no_lock_cp(struct v4l2_subdev *sd)
+{
+ /* CP has detected a non standard number of lines on the incoming
+ video compared to what it is configured to receive by s_dv_timings */
+ return io_read(sd, 0x12) & 0x01;
+}
+
+static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ *status = 0;
+ *status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0;
+ *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0;
+ if (no_lock_cp(sd))
+ *status |= DIGITAL_INPUT ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK;
+
+ v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void adv7604_print_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings, const char *txt, bool detailed)
+{
+ struct v4l2_bt_timings *bt = &timings->bt;
+ u32 htot, vtot;
+
+ if (timings->type != V4L2_DV_BT_656_1120)
+ return;
+
+ htot = htotal(bt);
+ vtot = vtotal(bt);
+
+ v4l2_info(sd, "%s %dx%d%s%d (%dx%d)",
+ txt, bt->width, bt->height, bt->interlaced ? "i" : "p",
+ (htot * vtot) > 0 ? ((u32)bt->pixelclock /
+ (htot * vtot)) : 0,
+ htot, vtot);
+
+ if (detailed) {
+ v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n",
+ bt->hfrontporch,
+ (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-",
+ bt->hsync, bt->hbackporch);
+ v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n",
+ bt->vfrontporch,
+ (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-",
+ bt->vsync, bt->vbackporch);
+ v4l2_info(sd, " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n",
+ bt->pixelclock, bt->flags, bt->standards);
+ }
+}
+
+struct stdi_readback {
+ u16 bl, lcf, lcvs;
+ u8 hs_pol, vs_pol;
+ bool interlaced;
+};
+
+static int stdi2dv_timings(struct v4l2_subdev *sd,
+ struct stdi_readback *stdi,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv7604_state *state = to_state(sd);
+ u32 hfreq = (ADV7604_fsc * 8) / stdi->bl;
+ u32 pix_clk;
+ int i;
+
+ for (i = 0; adv7604_timings[i].bt.height; i++) {
+ if (vtotal(&adv7604_timings[i].bt) != stdi->lcf + 1)
+ continue;
+ if (adv7604_timings[i].bt.vsync != stdi->lcvs)
+ continue;
+
+ pix_clk = hfreq * htotal(&adv7604_timings[i].bt);
+
+ if ((pix_clk < adv7604_timings[i].bt.pixelclock + 1000000) &&
+ (pix_clk > adv7604_timings[i].bt.pixelclock - 1000000)) {
+ *timings = adv7604_timings[i];
+ return 0;
+ }
+ }
+
+ if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs,
+ (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
+ (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
+ timings))
+ return 0;
+ if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs,
+ (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) |
+ (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0),
+ state->aspect_ratio, timings))
+ return 0;
+
+ v4l2_dbg(2, debug, sd, "%s: No format candidate found for lcf=%d, bl = %d\n",
+ __func__, stdi->lcf, stdi->bl);
+ return -1;
+}
+
+static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi)
+{
+ if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
+ v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__);
+ return -1;
+ }
+
+ /* read STDI */
+ stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2);
+ stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4);
+ stdi->lcvs = cp_read(sd, 0xb3) >> 3;
+ stdi->interlaced = io_read(sd, 0x12) & 0x10;
+
+ /* read SSPD */
+ if ((cp_read(sd, 0xb5) & 0x03) == 0x01) {
+ stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ?
+ ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x');
+ stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ?
+ ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x');
+ } else {
+ stdi->hs_pol = 'x';
+ stdi->vs_pol = 'x';
+ }
+
+ if (no_lock_stdi(sd) || no_lock_sspd(sd)) {
+ v4l2_dbg(2, debug, sd,
+ "%s: signal lost during readout of STDI/SSPD\n", __func__);
+ return -1;
+ }
+
+ if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) {
+ v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__);
+ memset(stdi, 0, sizeof(struct stdi_readback));
+ return -1;
+ }
+
+ v4l2_dbg(2, debug, sd,
+ "%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n",
+ __func__, stdi->lcf, stdi->bl, stdi->lcvs,
+ stdi->hs_pol, stdi->vs_pol,
+ stdi->interlaced ? "interlaced" : "progressive");
+
+ return 0;
+}
+
+static int adv7604_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1)
+ return -EINVAL;
+ memset(timings->reserved, 0, sizeof(timings->reserved));
+ timings->timings = adv7604_timings[timings->index];
+ return 0;
+}
+
+static int adv7604_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ cap->type = V4L2_DV_BT_656_1120;
+ cap->bt.max_width = 1920;
+ cap->bt.max_height = 1200;
+ cap->bt.min_pixelclock = 27000000;
+ if (DIGITAL_INPUT)
+ cap->bt.max_pixelclock = 225000000;
+ else
+ cap->bt.max_pixelclock = 170000000;
+ cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
+ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM;
+ return 0;
+}
+
+/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings
+ if the format is listed in adv7604_timings[] */
+static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv7604_state *state = to_state(sd);
+ int i;
+
+ for (i = 0; adv7604_timings[i].bt.width; i++) {
+ if (v4l_match_dv_timings(timings, &adv7604_timings[i],
+ DIGITAL_INPUT ? 250000 : 1000000)) {
+ *timings = adv7604_timings[i];
+ break;
+ }
+ }
+}
+
+static int adv7604_query_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv7604_state *state = to_state(sd);
+ struct v4l2_bt_timings *bt = &timings->bt;
+ struct stdi_readback stdi;
+
+ if (!timings)
+ return -EINVAL;
+
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+
+ if (no_signal(sd)) {
+ v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__);
+ return -ENOLINK;
+ }
+
+ /* read STDI */
+ if (read_stdi(sd, &stdi)) {
+ v4l2_dbg(1, debug, sd, "%s: STDI/SSPD not locked\n", __func__);
+ return -ENOLINK;
+ }
+ bt->interlaced = stdi.interlaced ?
+ V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+
+ if (DIGITAL_INPUT) {
+ timings->type = V4L2_DV_BT_656_1120;
+
+ bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08);
+ bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a);
+ bt->pixelclock = (hdmi_read(sd, 0x06) * 1000000) +
+ ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000;
+ bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 +
+ hdmi_read(sd, 0x21);
+ bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 +
+ hdmi_read(sd, 0x23);
+ bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 +
+ hdmi_read(sd, 0x25);
+ bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2b)) / 2;
+ bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2f)) / 2;
+ bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 +
+ hdmi_read(sd, 0x33)) / 2;
+ bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) |
+ ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0);
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 +
+ hdmi_read(sd, 0x0c);
+ bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 +
+ hdmi_read(sd, 0x2d)) / 2;
+ bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 +
+ hdmi_read(sd, 0x31)) / 2;
+ bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
+ hdmi_read(sd, 0x35)) / 2;
+ }
+ adv7604_fill_optional_dv_timings_fields(sd, timings);
+ } else {
+ /* find format
+ * Since LCVS values are inaccurate (REF_03, page 275-276),
+ * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails.
+ */
+ if (!stdi2dv_timings(sd, &stdi, timings))
+ goto found;
+ stdi.lcvs += 1;
+ v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs);
+ if (!stdi2dv_timings(sd, &stdi, timings))
+ goto found;
+ stdi.lcvs -= 2;
+ v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs);
+ if (stdi2dv_timings(sd, &stdi, timings)) {
+ v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__);
+ return -ERANGE;
+ }
+ }
+found:
+
+ if (no_signal(sd)) {
+ v4l2_dbg(1, debug, sd, "%s: signal lost during readout\n", __func__);
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ return -ENOLINK;
+ }
+
+ if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) ||
+ (DIGITAL_INPUT && bt->pixelclock > 225000000)) {
+ v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
+ __func__, (u32)bt->pixelclock);
+ return -ERANGE;
+ }
+
+ if (debug > 1)
+ adv7604_print_timings(sd, timings,
+ "adv7604_query_dv_timings:", true);
+
+ return 0;
+}
+
+static int adv7604_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv7604_state *state = to_state(sd);
+ struct v4l2_bt_timings *bt;
+
+ if (!timings)
+ return -EINVAL;
+
+ bt = &timings->bt;
+
+ if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) ||
+ (DIGITAL_INPUT && bt->pixelclock > 225000000)) {
+ v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n",
+ __func__, (u32)bt->pixelclock);
+ return -ERANGE;
+ }
+ adv7604_fill_optional_dv_timings_fields(sd, timings);
+
+ state->timings = *timings;
+
+ /* freerun */
+ configure_free_run(sd, bt);
+
+ set_rgb_quantization_range(sd);
+
+
+ if (debug > 1)
+ adv7604_print_timings(sd, timings,
+ "adv7604_s_dv_timings:", true);
+ return 0;
+}
+
+static int adv7604_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ *timings = state->timings;
+ return 0;
+}
+
+static void enable_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mode)
+{
+ switch (prim_mode) {
+ case ADV7604_PRIM_MODE_COMP:
+ case ADV7604_PRIM_MODE_RGB:
+ /* enable */
+ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */
+ break;
+ case ADV7604_PRIM_MODE_HDMI_COMP:
+ case ADV7604_PRIM_MODE_HDMI_GR:
+ /* enable */
+ hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */
+ hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */
+ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */
+ break;
+ default:
+ v4l2_err(sd, "%s: reserved primary mode 0x%0x\n",
+ __func__, prim_mode);
+ break;
+ }
+}
+
+static void disable_input(struct v4l2_subdev *sd)
+{
+ /* disable */
+ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */
+ hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */
+ hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */
+}
+
+static void select_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mode)
+{
+ switch (prim_mode) {
+ case ADV7604_PRIM_MODE_COMP:
+ case ADV7604_PRIM_MODE_RGB:
+ /* set mode and select free run resolution */
+ io_write(sd, 0x00, 0x07); /* video std */
+ io_write(sd, 0x01, 0x02); /* prim mode */
+ /* enable embedded syncs for auto graphics mode */
+ cp_write_and_or(sd, 0x81, 0xef, 0x10);
+
+ /* reset ADI recommended settings for HDMI: */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
+ hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */
+ hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */
+ hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x8d, 0x18); /* equaliser */
+ hdmi_write(sd, 0x8e, 0x34); /* equaliser */
+ hdmi_write(sd, 0x93, 0x88); /* equaliser */
+ hdmi_write(sd, 0x94, 0x2e); /* equaliser */
+ hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */
+
+ afe_write(sd, 0x00, 0x08); /* power up ADC */
+ afe_write(sd, 0x01, 0x06); /* power up Analog Front End */
+ afe_write(sd, 0xc8, 0x00); /* phase control */
+
+ /* set ADI recommended settings for digitizer */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
+ afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */
+ afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */
+ cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */
+ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
+ cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */
+ break;
+
+ case ADV7604_PRIM_MODE_HDMI_COMP:
+ case ADV7604_PRIM_MODE_HDMI_GR:
+ /* set mode and select free run resolution */
+ /* video std */
+ io_write(sd, 0x00,
+ (prim_mode == ADV7604_PRIM_MODE_HDMI_GR) ? 0x02 : 0x1e);
+ io_write(sd, 0x01, prim_mode); /* prim mode */
+ /* disable embedded syncs for auto graphics mode */
+ cp_write_and_or(sd, 0x81, 0xef, 0x00);
+
+ /* set ADI recommended settings for HDMI: */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */
+ hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */
+ hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */
+ hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */
+ hdmi_write(sd, 0x8d, 0x18); /* equaliser */
+ hdmi_write(sd, 0x8e, 0x34); /* equaliser */
+ hdmi_write(sd, 0x93, 0x8b); /* equaliser */
+ hdmi_write(sd, 0x94, 0x2d); /* equaliser */
+ hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */
+
+ afe_write(sd, 0x00, 0xff); /* power down ADC */
+ afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */
+ afe_write(sd, 0xc8, 0x40); /* phase control */
+
+ /* reset ADI recommended settings for digitizer */
+ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */
+ afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */
+ afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */
+ cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */
+ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */
+ cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */
+
+ break;
+ default:
+ v4l2_err(sd, "%s: reserved primary mode 0x%0x\n", __func__, prim_mode);
+ break;
+ }
+}
+
+static int adv7604_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input);
+
+ switch (input) {
+ case 0:
+ /* TODO select HDMI_COMP or HDMI_GR */
+ state->prim_mode = ADV7604_PRIM_MODE_HDMI_COMP;
+ break;
+ case 1:
+ state->prim_mode = ADV7604_PRIM_MODE_RGB;
+ break;
+ case 2:
+ state->prim_mode = ADV7604_PRIM_MODE_COMP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ disable_input(sd);
+
+ select_input(sd, state->prim_mode);
+
+ enable_input(sd, state->prim_mode);
+
+ return 0;
+}
+
+static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+ /* Good enough for now */
+ *code = V4L2_MBUS_FMT_FIXED;
+ return 0;
+}
+
+static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ fmt->width = state->timings.bt.width;
+ fmt->height = state->timings.bt.height;
+ fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->field = V4L2_FIELD_NONE;
+ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) {
+ fmt->colorspace = (state->timings.bt.height <= 576) ?
+ V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+ }
+ return 0;
+}
+
+static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct adv7604_state *state = to_state(sd);
+ u8 fmt_change, fmt_change_digital, tx_5v;
+
+ /* format change */
+ fmt_change = io_read(sd, 0x43) & 0x98;
+ if (fmt_change)
+ io_write(sd, 0x44, fmt_change);
+ fmt_change_digital = DIGITAL_INPUT ? (io_read(sd, 0x6b) & 0xc0) : 0;
+ if (fmt_change_digital)
+ io_write(sd, 0x6c, fmt_change_digital);
+ if (fmt_change || fmt_change_digital) {
+ v4l2_dbg(1, debug, sd,
+ "%s: ADV7604_FMT_CHANGE, fmt_change = 0x%x, fmt_change_digital = 0x%x\n",
+ __func__, fmt_change, fmt_change_digital);
+ v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL);
+ if (handled)
+ *handled = true;
+ }
+ /* tx 5v detect */
+ tx_5v = io_read(sd, 0x70) & 0x10;
+ if (tx_5v) {
+ v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v);
+ io_write(sd, 0x71, tx_5v);
+ adv7604_s_detect_tx_5v_ctrl(sd);
+ if (handled)
+ *handled = true;
+ }
+ return 0;
+}
+
+static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ struct adv7604_state *state = to_state(sd);
+
+ if (edid->pad != 0)
+ return -EINVAL;
+ if (edid->blocks == 0)
+ return -EINVAL;
+ if (edid->start_block >= state->edid_blocks)
+ return -EINVAL;
+ if (edid->start_block + edid->blocks > state->edid_blocks)
+ edid->blocks = state->edid_blocks - edid->start_block;
+ if (!edid->edid)
+ return -EINVAL;
+ memcpy(edid->edid + edid->start_block * 128,
+ state->edid + edid->start_block * 128,
+ edid->blocks * 128);
+ return 0;
+}
+
+static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ struct adv7604_state *state = to_state(sd);
+ int err;
+
+ if (edid->pad != 0)
+ return -EINVAL;
+ if (edid->start_block != 0)
+ return -EINVAL;
+ if (edid->blocks == 0) {
+ /* Pull down the hotplug pin */
+ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0);
+ /* Disables I2C access to internal EDID ram from DDC port */
+ rep_write_and_or(sd, 0x77, 0xf0, 0x0);
+ state->edid_blocks = 0;
+ /* Fall back to a 16:9 aspect ratio */
+ state->aspect_ratio.numerator = 16;
+ state->aspect_ratio.denominator = 9;
+ return 0;
+ }
+ if (edid->blocks > 2)
+ return -E2BIG;
+ if (!edid->edid)
+ return -EINVAL;
+ memcpy(state->edid, edid->edid, 128 * edid->blocks);
+ state->edid_blocks = edid->blocks;
+ state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15],
+ edid->edid[0x16]);
+ err = edid_write_block(sd, 128 * edid->blocks, state->edid);
+ if (err < 0)
+ v4l2_err(sd, "error %d writing edid\n", err);
+ return err;
+}
+
+/*********** avi info frame CEA-861-E **************/
+
+static void print_avi_infoframe(struct v4l2_subdev *sd)
+{
+ int i;
+ u8 buf[14];
+ u8 avi_len;
+ u8 avi_ver;
+
+ if (!(hdmi_read(sd, 0x05) & 0x80)) {
+ v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n");
+ return;
+ }
+ if (!(io_read(sd, 0x60) & 0x01)) {
+ v4l2_info(sd, "AVI infoframe not received\n");
+ return;
+ }
+
+ if (io_read(sd, 0x83) & 0x01) {
+ v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n");
+ io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */
+ if (io_read(sd, 0x83) & 0x01) {
+ v4l2_info(sd, "AVI infoframe checksum error still present\n");
+ io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */
+ }
+ }
+
+ avi_len = infoframe_read(sd, 0xe2);
+ avi_ver = infoframe_read(sd, 0xe1);
+ v4l2_info(sd, "AVI infoframe version %d (%d byte)\n",
+ avi_ver, avi_len);
+
+ if (avi_ver != 0x02)
+ return;
+
+ for (i = 0; i < 14; i++)
+ buf[i] = infoframe_read(sd, i);
+
+ v4l2_info(sd,
+ "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]);
+}
+
+static int adv7604_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+ struct v4l2_dv_timings timings;
+ struct stdi_readback stdi;
+ u8 reg_io_0x02 = io_read(sd, 0x02);
+
+ char *csc_coeff_sel_rb[16] = {
+ "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB",
+ "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709",
+ "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709",
+ "reserved", "reserved", "reserved", "reserved", "manual"
+ };
+ char *input_color_space_txt[16] = {
+ "RGB limited range (16-235)", "RGB full range (0-255)",
+ "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)",
+ "XvYCC Bt.601", "XvYCC Bt.709",
+ "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)",
+ "invalid", "invalid", "invalid", "invalid", "invalid",
+ "invalid", "invalid", "automatic"
+ };
+ char *rgb_quantization_range_txt[] = {
+ "Automatic",
+ "RGB limited range (16-235)",
+ "RGB full range (0-255)",
+ };
+
+ v4l2_info(sd, "-----Chip status-----\n");
+ v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on");
+ v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ?
+ "HDMI" : (DIGITAL_INPUT ? "DVI-D" : "DVI-A"));
+ v4l2_info(sd, "EDID: %s\n", ((rep_read(sd, 0x7d) & 0x01) &&
+ (rep_read(sd, 0x77) & 0x01)) ? "enabled" : "disabled ");
+ v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ?
+ "enabled" : "disabled");
+
+ v4l2_info(sd, "-----Signal status-----\n");
+ v4l2_info(sd, "Cable detected (+5V power): %s\n",
+ (io_read(sd, 0x6f) & 0x10) ? "true" : "false");
+ v4l2_info(sd, "TMDS signal detected: %s\n",
+ no_signal_tmds(sd) ? "false" : "true");
+ v4l2_info(sd, "TMDS signal locked: %s\n",
+ no_lock_tmds(sd) ? "false" : "true");
+ v4l2_info(sd, "SSPD locked: %s\n", no_lock_sspd(sd) ? "false" : "true");
+ v4l2_info(sd, "STDI locked: %s\n", no_lock_stdi(sd) ? "false" : "true");
+ v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true");
+ v4l2_info(sd, "CP free run: %s\n",
+ (!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off"));
+ v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x\n",
+ io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f);
+
+ v4l2_info(sd, "-----Video Timings-----\n");
+ if (read_stdi(sd, &stdi))
+ v4l2_info(sd, "STDI: not locked\n");
+ else
+ v4l2_info(sd, "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %s, %chsync, %cvsync\n",
+ stdi.lcf, stdi.bl, stdi.lcvs,
+ stdi.interlaced ? "interlaced" : "progressive",
+ stdi.hs_pol, stdi.vs_pol);
+ if (adv7604_query_dv_timings(sd, &timings))
+ v4l2_info(sd, "No video detected\n");
+ else
+ adv7604_print_timings(sd, &timings, "Detected format:", true);
+ adv7604_print_timings(sd, &state->timings, "Configured format:", true);
+
+ v4l2_info(sd, "-----Color space-----\n");
+ v4l2_info(sd, "RGB quantization range ctrl: %s\n",
+ rgb_quantization_range_txt[state->rgb_quantization_range]);
+ v4l2_info(sd, "Input color space: %s\n",
+ input_color_space_txt[reg_io_0x02 >> 4]);
+ v4l2_info(sd, "Output color space: %s %s, saturator %s\n",
+ (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr",
+ (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)",
+ ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ?
+ "enabled" : "disabled");
+ v4l2_info(sd, "Color space conversion: %s\n",
+ csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]);
+
+ /* Digital video */
+ if (DIGITAL_INPUT) {
+ v4l2_info(sd, "-----HDMI status-----\n");
+ v4l2_info(sd, "HDCP encrypted content: %s\n",
+ hdmi_read(sd, 0x05) & 0x40 ? "true" : "false");
+
+ print_avi_infoframe(sd);
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops adv7604_ctrl_ops = {
+ .s_ctrl = adv7604_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7604_core_ops = {
+ .log_status = adv7604_log_status,
+ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
+ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
+ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
+ .g_ctrl = v4l2_subdev_g_ctrl,
+ .s_ctrl = v4l2_subdev_s_ctrl,
+ .queryctrl = v4l2_subdev_queryctrl,
+ .querymenu = v4l2_subdev_querymenu,
+ .g_chip_ident = adv7604_g_chip_ident,
+ .interrupt_service_routine = adv7604_isr,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = adv7604_g_register,
+ .s_register = adv7604_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops adv7604_video_ops = {
+ .s_routing = adv7604_s_routing,
+ .g_input_status = adv7604_g_input_status,
+ .s_dv_timings = adv7604_s_dv_timings,
+ .g_dv_timings = adv7604_g_dv_timings,
+ .query_dv_timings = adv7604_query_dv_timings,
+ .enum_dv_timings = adv7604_enum_dv_timings,
+ .dv_timings_cap = adv7604_dv_timings_cap,
+ .enum_mbus_fmt = adv7604_enum_mbus_fmt,
+ .g_mbus_fmt = adv7604_g_mbus_fmt,
+ .try_mbus_fmt = adv7604_g_mbus_fmt,
+ .s_mbus_fmt = adv7604_g_mbus_fmt,
+};
+
+static const struct v4l2_subdev_pad_ops adv7604_pad_ops = {
+ .get_edid = adv7604_get_edid,
+ .set_edid = adv7604_set_edid,
+};
+
+static const struct v4l2_subdev_ops adv7604_ops = {
+ .core = &adv7604_core_ops,
+ .video = &adv7604_video_ops,
+ .pad = &adv7604_pad_ops,
+};
+
+/* -------------------------- custom ctrls ---------------------------------- */
+
+static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = {
+ .ops = &adv7604_ctrl_ops,
+ .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE,
+ .name = "Analog Sampling Phase",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 0x1f,
+ .step = 1,
+ .def = 0,
+};
+
+static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color_manual = {
+ .ops = &adv7604_ctrl_ops,
+ .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL,
+ .name = "Free Running Color, Manual",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = false,
+ .max = true,
+ .step = 1,
+ .def = false,
+};
+
+static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = {
+ .ops = &adv7604_ctrl_ops,
+ .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR,
+ .name = "Free Running Color",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0x0,
+ .max = 0xffffff,
+ .step = 0x1,
+ .def = 0x0,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int adv7604_core_init(struct v4l2_subdev *sd)
+{
+ struct adv7604_state *state = to_state(sd);
+ struct adv7604_platform_data *pdata = &state->pdata;
+
+ hdmi_write(sd, 0x48,
+ (pdata->disable_pwrdnb ? 0x80 : 0) |
+ (pdata->disable_cable_det_rst ? 0x40 : 0));
+
+ disable_input(sd);
+
+ /* power */
+ io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */
+ io_write(sd, 0x0b, 0x44); /* Power down ESDP block */
+ cp_write(sd, 0xcf, 0x01); /* Power down macrovision */
+
+ /* video format */
+ io_write_and_or(sd, 0x02, 0xf0,
+ pdata->alt_gamma << 3 |
+ pdata->op_656_range << 2 |
+ pdata->rgb_out << 1 |
+ pdata->alt_data_sat << 0);
+ io_write(sd, 0x03, pdata->op_format_sel);
+ io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5);
+ io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 |
+ pdata->insert_av_codes << 2 |
+ pdata->replicate_av_codes << 1 |
+ pdata->invert_cbcr << 0);
+
+ /* TODO from platform data */
+ cp_write(sd, 0x69, 0x30); /* Enable CP CSC */
+ io_write(sd, 0x06, 0xa6); /* positive VS and HS */
+ io_write(sd, 0x14, 0x7f); /* Drive strength adjusted to max */
+ cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */
+ cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */
+ cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold -
+ ADI recommended setting [REF_01 c. 2.3.3] */
+ cp_write(sd, 0x45, 0x23); /* STDI ch. 2 - LCVS change threshold -
+ ADI recommended setting [REF_01 c. 2.3.3] */
+ cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution
+ for digital formats */
+
+ /* TODO from platform data */
+ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */
+
+ afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */
+ io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4);
+
+ state->prim_mode = pdata->prim_mode;
+ select_input(sd, pdata->prim_mode);
+
+ enable_input(sd, pdata->prim_mode);
+
+ /* interrupts */
+ io_write(sd, 0x40, 0xc2); /* Configure INT1 */
+ io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */
+ io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */
+ io_write(sd, 0x6e, 0xc0); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */
+ io_write(sd, 0x73, 0x10); /* Enable CABLE_DET_A_ST (+5v) interrupt */
+
+ return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static void adv7604_unregister_clients(struct adv7604_state *state)
+{
+ if (state->i2c_avlink)
+ i2c_unregister_device(state->i2c_avlink);
+ if (state->i2c_cec)
+ i2c_unregister_device(state->i2c_cec);
+ if (state->i2c_infoframe)
+ i2c_unregister_device(state->i2c_infoframe);
+ if (state->i2c_esdp)
+ i2c_unregister_device(state->i2c_esdp);
+ if (state->i2c_dpp)
+ i2c_unregister_device(state->i2c_dpp);
+ if (state->i2c_afe)
+ i2c_unregister_device(state->i2c_afe);
+ if (state->i2c_repeater)
+ i2c_unregister_device(state->i2c_repeater);
+ if (state->i2c_edid)
+ i2c_unregister_device(state->i2c_edid);
+ if (state->i2c_hdmi)
+ i2c_unregister_device(state->i2c_hdmi);
+ if (state->i2c_test)
+ i2c_unregister_device(state->i2c_test);
+ if (state->i2c_cp)
+ i2c_unregister_device(state->i2c_cp);
+ if (state->i2c_vdp)
+ i2c_unregister_device(state->i2c_vdp);
+}
+
+static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd,
+ u8 addr, u8 io_reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (addr)
+ io_write(sd, io_reg, addr << 1);
+ return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1);
+}
+
+static int adv7604_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7604_state *state;
+ struct adv7604_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_ctrl_handler *hdl;
+ struct v4l2_subdev *sd;
+ int err;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+ v4l_dbg(1, debug, client, "detecting adv7604 client on address 0x%x\n",
+ client->addr << 1);
+
+ state = kzalloc(sizeof(struct adv7604_state), GFP_KERNEL);
+ if (!state) {
+ v4l_err(client, "Could not allocate adv7604_state memory!\n");
+ return -ENOMEM;
+ }
+
+ /* platform data */
+ if (!pdata) {
+ v4l_err(client, "No platform data!\n");
+ err = -ENODEV;
+ goto err_state;
+ }
+ memcpy(&state->pdata, pdata, sizeof(state->pdata));
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7604_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ state->connector_hdmi = pdata->connector_hdmi;
+
+ /* i2c access to adv7604? */
+ if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) {
+ v4l2_info(sd, "not an adv7604 on address 0x%x\n",
+ client->addr << 1);
+ err = -ENODEV;
+ goto err_state;
+ }
+
+ /* control handlers */
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 9);
+
+ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops,
+ V4L2_CID_HUE, 0, 128, 1, 0);
+
+ /* private controls */
+ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
+ state->detect_tx_5v_ctrl->is_private = true;
+ state->rgb_quantization_range_ctrl =
+ v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops,
+ V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL,
+ 0, V4L2_DV_RGB_RANGE_AUTO);
+ state->rgb_quantization_range_ctrl->is_private = true;
+
+ /* custom controls */
+ state->analog_sampling_phase_ctrl =
+ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL);
+ state->analog_sampling_phase_ctrl->is_private = true;
+ state->free_run_color_manual_ctrl =
+ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL);
+ state->free_run_color_manual_ctrl->is_private = true;
+ state->free_run_color_ctrl =
+ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL);
+ state->free_run_color_ctrl->is_private = true;
+
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ err = hdl->error;
+ goto err_hdl;
+ }
+ if (adv7604_s_detect_tx_5v_ctrl(sd)) {
+ err = -ENODEV;
+ goto err_hdl;
+ }
+
+ state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3);
+ state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4);
+ state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5);
+ state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6);
+ state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7);
+ state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8);
+ state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9);
+ state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa);
+ state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb);
+ state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc);
+ state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd);
+ state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe);
+ if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe ||
+ !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe ||
+ !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi ||
+ !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) {
+ err = -ENOMEM;
+ v4l2_err(sd, "failed to create all i2c clients\n");
+ goto err_i2c;
+ }
+
+ /* work queues */
+ state->work_queues = create_singlethread_workqueue(client->name);
+ if (!state->work_queues) {
+ v4l2_err(sd, "Could not create work queue\n");
+ err = -ENOMEM;
+ goto err_i2c;
+ }
+
+ INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug,
+ adv7604_delayed_work_enable_hotplug);
+
+ state->pad.flags = MEDIA_PAD_FL_SOURCE;
+ err = media_entity_init(&sd->entity, 1, &state->pad, 0);
+ if (err)
+ goto err_work_queues;
+
+ err = adv7604_core_init(sd);
+ if (err)
+ goto err_entity;
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+ return 0;
+
+err_entity:
+ media_entity_cleanup(&sd->entity);
+err_work_queues:
+ cancel_delayed_work(&state->delayed_work_enable_hotplug);
+ destroy_workqueue(state->work_queues);
+err_i2c:
+ adv7604_unregister_clients(state);
+err_hdl:
+ v4l2_ctrl_handler_free(hdl);
+err_state:
+ kfree(state);
+ return err;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int adv7604_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7604_state *state = to_state(sd);
+
+ cancel_delayed_work(&state->delayed_work_enable_hotplug);
+ destroy_workqueue(state->work_queues);
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ adv7604_unregister_clients(to_state(sd));
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_device_id adv7604_id[] = {
+ { "adv7604", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adv7604_id);
+
+static struct i2c_driver adv7604_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7604",
+ },
+ .probe = adv7604_probe,
+ .remove = adv7604_remove,
+ .id_table = adv7604_id,
+};
+
+module_i2c_driver(adv7604_driver);
diff --git a/drivers/media/video/ak881x.c b/drivers/media/i2c/ak881x.c
index ba674656b10d..ba674656b10d 100644
--- a/drivers/media/video/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/i2c/aptina-pll.c
index 8153a449846b..8153a449846b 100644
--- a/drivers/media/video/aptina-pll.c
+++ b/drivers/media/i2c/aptina-pll.c
diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/i2c/aptina-pll.h
index b370e341e75d..b370e341e75d 100644
--- a/drivers/media/video/aptina-pll.h
+++ b/drivers/media/i2c/aptina-pll.h
diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c
new file mode 100644
index 000000000000..3bfdbf9d9bf1
--- /dev/null
+++ b/drivers/media/i2c/as3645a.c
@@ -0,0 +1,888 @@
+/*
+ * drivers/media/i2c/as3645a.c - AS3645A and LM3555 flash controllers driver
+ *
+ * Copyright (C) 2008-2011 Nokia Corporation
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * TODO:
+ * - Check hardware FSTROBE control when sensor driver add support for this
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/as3645a.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50)
+#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100)
+
+/* Register definitions */
+
+/* Read-only Design info register: Reset state: xxxx 0001 */
+#define AS_DESIGN_INFO_REG 0x00
+#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4))
+#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f)
+
+/* Read-only Version control register: Reset state: 0000 0000
+ * for first engineering samples
+ */
+#define AS_VERSION_CONTROL_REG 0x01
+#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4))
+#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f)
+
+/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */
+#define AS_INDICATOR_AND_TIMER_REG 0x02
+#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0
+#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4
+#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6
+
+/* Read / Write (Current set register): Reset state: 0110 1001 */
+#define AS_CURRENT_SET_REG 0x03
+#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0
+#define AS_CURRENT_LED_DET_ON (1 << 3)
+#define AS_CURRENT_FLASH_CURRENT_SHIFT 4
+
+/* Read / Write (Control register): Reset state: 1011 0100 */
+#define AS_CONTROL_REG 0x04
+#define AS_CONTROL_MODE_SETTING_SHIFT 0
+#define AS_CONTROL_STROBE_ON (1 << 2)
+#define AS_CONTROL_OUT_ON (1 << 3)
+#define AS_CONTROL_EXT_TORCH_ON (1 << 4)
+#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5)
+#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5)
+#define AS_CONTROL_COIL_PEAK_SHIFT 6
+
+/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */
+#define AS_FAULT_INFO_REG 0x05
+#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1)
+#define AS_FAULT_INFO_INDICATOR_LED (1 << 2)
+#define AS_FAULT_INFO_LED_AMOUNT (1 << 3)
+#define AS_FAULT_INFO_TIMEOUT (1 << 4)
+#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5)
+#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6)
+#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7)
+
+/* Boost register */
+#define AS_BOOST_REG 0x0d
+#define AS_BOOST_CURRENT_DISABLE (0 << 0)
+#define AS_BOOST_CURRENT_ENABLE (1 << 0)
+
+/* Password register is used to unlock boost register writing */
+#define AS_PASSWORD_REG 0x0f
+#define AS_PASSWORD_UNLOCK_VALUE 0x55
+
+enum as_mode {
+ AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT,
+ AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT,
+};
+
+/*
+ * struct as3645a
+ *
+ * @subdev: V4L2 subdev
+ * @pdata: Flash platform data
+ * @power_lock: Protects power_count
+ * @power_count: Power reference count
+ * @led_mode: V4L2 flash LED mode
+ * @timeout: Flash timeout in microseconds
+ * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum
+ * values are 400mA for two LEDs and 500mA for one LED.
+ * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA)
+ * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA)
+ * @strobe_source: Flash strobe source (software or external)
+ */
+struct as3645a {
+ struct v4l2_subdev subdev;
+ const struct as3645a_platform_data *pdata;
+
+ struct mutex power_lock;
+ int power_count;
+
+ /* Controls */
+ struct v4l2_ctrl_handler ctrls;
+
+ enum v4l2_flash_led_mode led_mode;
+ unsigned int timeout;
+ u8 flash_current;
+ u8 assist_current;
+ u8 indicator_current;
+ enum v4l2_flash_strobe_source strobe_source;
+};
+
+#define to_as3645a(sd) container_of(sd, struct as3645a, subdev)
+
+/* Return negative errno else zero on success */
+static int as3645a_write(struct as3645a *flash, u8 addr, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ rval = i2c_smbus_write_byte_data(client, addr, val);
+
+ dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
+ rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+/* Return negative errno else a data byte received from the device. */
+static int as3645a_read(struct as3645a *flash, u8 addr)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ rval = i2c_smbus_read_byte_data(client, addr);
+
+ dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval,
+ rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration and trigger
+ */
+
+/*
+ * as3645a_set_config - Set flash configuration registers
+ * @flash: The flash
+ *
+ * Configure the hardware with flash, assist and indicator currents, as well as
+ * flash timeout.
+ *
+ * Return 0 on success, or a negative error code if an I2C communication error
+ * occurred.
+ */
+static int as3645a_set_config(struct as3645a *flash)
+{
+ int ret;
+ u8 val;
+
+ val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT)
+ | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT)
+ | AS_CURRENT_LED_DET_ON;
+
+ ret = as3645a_write(flash, AS_CURRENT_SET_REG, val);
+ if (ret < 0)
+ return ret;
+
+ val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000)
+ << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT;
+
+ val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT)
+ | ((flash->indicator_current ? flash->indicator_current - 1 : 0)
+ << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT);
+
+ return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val);
+}
+
+/*
+ * as3645a_set_control - Set flash control register
+ * @flash: The flash
+ * @mode: Desired output mode
+ * @on: Desired output state
+ *
+ * Configure the hardware with output mode and state.
+ *
+ * Return 0 on success, or a negative error code if an I2C communication error
+ * occurred.
+ */
+static int
+as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on)
+{
+ u8 reg;
+
+ /* Configure output parameters and operation mode. */
+ reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT)
+ | (on ? AS_CONTROL_OUT_ON : 0)
+ | mode;
+
+ if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH &&
+ flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) {
+ reg |= AS_CONTROL_STROBE_TYPE_LEVEL
+ | AS_CONTROL_STROBE_ON;
+ }
+
+ return as3645a_write(flash, AS_CONTROL_REG, reg);
+}
+
+/*
+ * as3645a_set_output - Configure output and operation mode
+ * @flash: Flash controller
+ * @strobe: Strobe the flash (only valid in flash mode)
+ *
+ * Turn the LEDs output on/off and set the operation mode based on the current
+ * parameters.
+ *
+ * The AS3645A can't control the indicator LED independently of the flash/torch
+ * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the
+ * chip to indicator mode. Otherwise set it to assist light (torch) or flash
+ * mode.
+ *
+ * In indicator and assist modes, turn the output on/off based on the indicator
+ * and torch currents. In software strobe flash mode, turn the output on/off
+ * based on the strobe parameter.
+ */
+static int as3645a_set_output(struct as3645a *flash, bool strobe)
+{
+ enum as_mode mode;
+ bool on;
+
+ switch (flash->led_mode) {
+ case V4L2_FLASH_LED_MODE_NONE:
+ on = flash->indicator_current != 0;
+ mode = AS_MODE_INDICATOR;
+ break;
+ case V4L2_FLASH_LED_MODE_TORCH:
+ on = true;
+ mode = AS_MODE_ASSIST;
+ break;
+ case V4L2_FLASH_LED_MODE_FLASH:
+ on = strobe;
+ mode = AS_MODE_FLASH;
+ break;
+ default:
+ BUG();
+ }
+
+ /* Configure output parameters and operation mode. */
+ return as3645a_set_control(flash, mode, on);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 controls
+ */
+
+static int as3645a_is_active(struct as3645a *flash)
+{
+ int ret;
+
+ ret = as3645a_read(flash, AS_CONTROL_REG);
+ return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON);
+}
+
+static int as3645a_read_fault(struct as3645a *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int rval;
+
+ /* NOTE: reading register clear fault status */
+ rval = as3645a_read(flash, AS_FAULT_INFO_REG);
+ if (rval < 0)
+ return rval;
+
+ if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
+ dev_dbg(&client->dev, "Inductor Peak limit fault\n");
+
+ if (rval & AS_FAULT_INFO_INDICATOR_LED)
+ dev_dbg(&client->dev, "Indicator LED fault: "
+ "Short circuit or open loop\n");
+
+ dev_dbg(&client->dev, "%u connected LEDs\n",
+ rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1);
+
+ if (rval & AS_FAULT_INFO_TIMEOUT)
+ dev_dbg(&client->dev, "Timeout fault\n");
+
+ if (rval & AS_FAULT_INFO_OVER_TEMPERATURE)
+ dev_dbg(&client->dev, "Over temperature fault\n");
+
+ if (rval & AS_FAULT_INFO_SHORT_CIRCUIT)
+ dev_dbg(&client->dev, "Short circuit fault\n");
+
+ if (rval & AS_FAULT_INFO_OVER_VOLTAGE)
+ dev_dbg(&client->dev, "Over voltage fault: "
+ "Indicates missing capacitor or open connection\n");
+
+ return rval;
+}
+
+static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct as3645a *flash =
+ container_of(ctrl->handler, struct as3645a, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_FAULT:
+ value = as3645a_read_fault(flash);
+ if (value < 0)
+ return value;
+
+ ctrl->cur.val = 0;
+ if (value & AS_FAULT_INFO_SHORT_CIRCUIT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
+ if (value & AS_FAULT_INFO_OVER_TEMPERATURE)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
+ if (value & AS_FAULT_INFO_TIMEOUT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
+ if (value & AS_FAULT_INFO_OVER_VOLTAGE)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
+ if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT;
+ if (value & AS_FAULT_INFO_INDICATOR_LED)
+ ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR;
+ break;
+
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
+ ctrl->cur.val = 0;
+ break;
+ }
+
+ value = as3645a_is_active(flash);
+ if (value < 0)
+ return value;
+
+ ctrl->cur.val = value;
+ break;
+ }
+
+ dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val);
+
+ return 0;
+}
+
+static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct as3645a *flash =
+ container_of(ctrl->handler, struct as3645a, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int ret;
+
+ dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val);
+
+ /* If a control that doesn't apply to the current mode is modified,
+ * we store the value and return immediately. The setting will be
+ * applied when the LED mode is changed. Otherwise we apply the setting
+ * immediately.
+ */
+
+ switch (ctrl->id) {
+ case V4L2_CID_FLASH_LED_MODE:
+ if (flash->indicator_current)
+ return -EBUSY;
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ flash->led_mode = ctrl->val;
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ flash->strobe_source = ctrl->val;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_STROBE:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ return as3645a_set_output(flash, true);
+
+ case V4L2_CID_FLASH_STROBE_STOP:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ return -EBUSY;
+
+ return as3645a_set_output(flash, false);
+
+ case V4L2_CID_FLASH_TIMEOUT:
+ flash->timeout = ctrl->val;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_INTENSITY:
+ flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN)
+ / AS3645A_FLASH_INTENSITY_STEP;
+
+ /* Applies to flash mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_TORCH_INTENSITY:
+ flash->assist_current =
+ (ctrl->val - AS3645A_TORCH_INTENSITY_MIN)
+ / AS3645A_TORCH_INTENSITY_STEP;
+
+ /* Applies to torch mode only. */
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH)
+ break;
+
+ return as3645a_set_config(flash);
+
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY:
+ if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE)
+ return -EBUSY;
+
+ flash->indicator_current =
+ (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN)
+ / AS3645A_INDICATOR_INTENSITY_STEP;
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ if ((ctrl->val == 0) == (ctrl->cur.val == 0))
+ break;
+
+ return as3645a_set_output(flash, false);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops as3645a_ctrl_ops = {
+ .g_volatile_ctrl = as3645a_get_ctrl,
+ .s_ctrl = as3645a_set_ctrl,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+/* Put device into know state. */
+static int as3645a_setup(struct as3645a *flash)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
+ int ret;
+
+ /* clear errors */
+ ret = as3645a_read(flash, AS_FAULT_INFO_REG);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Fault info: %02x\n", ret);
+
+ ret = as3645a_set_config(flash);
+ if (ret < 0)
+ return ret;
+
+ ret = as3645a_set_output(flash, false);
+ if (ret < 0)
+ return ret;
+
+ /* read status */
+ ret = as3645a_read_fault(flash);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n",
+ as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG));
+ dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n",
+ as3645a_read(flash, AS_CURRENT_SET_REG));
+ dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n",
+ as3645a_read(flash, AS_CONTROL_REG));
+
+ return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0;
+}
+
+static int __as3645a_set_power(struct as3645a *flash, int on)
+{
+ int ret;
+
+ if (!on)
+ as3645a_set_control(flash, AS_MODE_EXT_TORCH, false);
+
+ if (flash->pdata->set_power) {
+ ret = flash->pdata->set_power(&flash->subdev, on);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (!on)
+ return 0;
+
+ ret = as3645a_setup(flash);
+ if (ret < 0) {
+ if (flash->pdata->set_power)
+ flash->pdata->set_power(&flash->subdev, 0);
+ }
+
+ return ret;
+}
+
+static int as3645a_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct as3645a *flash = to_as3645a(sd);
+ int ret = 0;
+
+ mutex_lock(&flash->power_lock);
+
+ if (flash->power_count == !on) {
+ ret = __as3645a_set_power(flash, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ flash->power_count += on ? 1 : -1;
+ WARN_ON(flash->power_count < 0);
+
+done:
+ mutex_unlock(&flash->power_lock);
+ return ret;
+}
+
+static int as3645a_registered(struct v4l2_subdev *sd)
+{
+ struct as3645a *flash = to_as3645a(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int rval, man, model, rfu, version;
+ const char *vendor;
+
+ /* Power up the flash driver and read manufacturer ID, model ID, RFU
+ * and version.
+ */
+ rval = as3645a_set_power(&flash->subdev, 1);
+ if (rval < 0)
+ return rval;
+
+ rval = as3645a_read(flash, AS_DESIGN_INFO_REG);
+ if (rval < 0)
+ goto power_off;
+
+ man = AS_DESIGN_INFO_FACTORY(rval);
+ model = AS_DESIGN_INFO_MODEL(rval);
+
+ rval = as3645a_read(flash, AS_VERSION_CONTROL_REG);
+ if (rval < 0)
+ goto power_off;
+
+ rfu = AS_VERSION_CONTROL_RFU(rval);
+ version = AS_VERSION_CONTROL_VERSION(rval);
+
+ /* Verify the chip model and version. */
+ if (model != 0x01 || rfu != 0x00) {
+ dev_err(&client->dev, "AS3645A not detected "
+ "(model %d rfu %d)\n", model, rfu);
+ rval = -ENODEV;
+ goto power_off;
+ }
+
+ switch (man) {
+ case 1:
+ vendor = "AMS, Austria Micro Systems";
+ break;
+ case 2:
+ vendor = "ADI, Analog Devices Inc.";
+ break;
+ case 3:
+ vendor = "NSC, National Semiconductor";
+ break;
+ case 4:
+ vendor = "NXP";
+ break;
+ case 5:
+ vendor = "TI, Texas Instrument";
+ break;
+ default:
+ vendor = "Unknown";
+ }
+
+ dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor,
+ man, version);
+
+ rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE);
+ if (rval < 0)
+ goto power_off;
+
+ rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
+ if (rval < 0)
+ goto power_off;
+
+ /* Setup default values. This makes sure that the chip is in a known
+ * state, in case the power rail can't be controlled.
+ */
+ rval = as3645a_setup(flash);
+
+power_off:
+ as3645a_set_power(&flash->subdev, 0);
+
+ return rval;
+}
+
+static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return as3645a_set_power(sd, 1);
+}
+
+static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return as3645a_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_core_ops as3645a_core_ops = {
+ .s_power = as3645a_set_power,
+};
+
+static const struct v4l2_subdev_ops as3645a_ops = {
+ .core = &as3645a_core_ops,
+};
+
+static const struct v4l2_subdev_internal_ops as3645a_internal_ops = {
+ .registered = as3645a_registered,
+ .open = as3645a_open,
+ .close = as3645a_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * I2C driver
+ */
+#ifdef CONFIG_PM
+
+static int as3645a_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+ int rval;
+
+ if (flash->power_count == 0)
+ return 0;
+
+ rval = __as3645a_set_power(flash, 0);
+
+ dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
+
+ return rval;
+}
+
+static int as3645a_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+ int rval;
+
+ if (flash->power_count == 0)
+ return 0;
+
+ rval = __as3645a_set_power(flash, 1);
+
+ dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
+
+ return rval;
+}
+
+#else
+
+#define as3645a_suspend NULL
+#define as3645a_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*
+ * as3645a_init_controls - Create controls
+ * @flash: The flash
+ *
+ * The number of LEDs reported in platform data is used to compute default
+ * limits. Parameters passed through platform data can override those limits.
+ */
+static int __devinit as3645a_init_controls(struct as3645a *flash)
+{
+ const struct as3645a_platform_data *pdata = flash->pdata;
+ struct v4l2_ctrl *ctrl;
+ int maximum;
+
+ v4l2_ctrl_handler_init(&flash->ctrls, 10);
+
+ /* V4L2_CID_FLASH_LED_MODE */
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_LED_MODE, 2, ~7,
+ V4L2_FLASH_LED_MODE_NONE);
+
+ /* V4L2_CID_FLASH_STROBE_SOURCE */
+ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_SOURCE,
+ pdata->ext_strobe ? 1 : 0,
+ pdata->ext_strobe ? ~3 : ~1,
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
+
+ flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
+
+ /* V4L2_CID_FLASH_STROBE */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
+
+ /* V4L2_CID_FLASH_STROBE_STOP */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
+
+ /* V4L2_CID_FLASH_STROBE_STATUS */
+ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1);
+ if (ctrl != NULL)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ /* V4L2_CID_FLASH_TIMEOUT */
+ maximum = pdata->timeout_max;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN,
+ maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum);
+
+ flash->timeout = maximum;
+
+ /* V4L2_CID_FLASH_INTENSITY */
+ maximum = pdata->flash_max_current;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN,
+ maximum, AS3645A_FLASH_INTENSITY_STEP, maximum);
+
+ flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN)
+ / AS3645A_FLASH_INTENSITY_STEP;
+
+ /* V4L2_CID_FLASH_TORCH_INTENSITY */
+ maximum = pdata->torch_max_current;
+
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_TORCH_INTENSITY,
+ AS3645A_TORCH_INTENSITY_MIN, maximum,
+ AS3645A_TORCH_INTENSITY_STEP,
+ AS3645A_TORCH_INTENSITY_MIN);
+
+ flash->assist_current = 0;
+
+ /* V4L2_CID_FLASH_INDICATOR_INTENSITY */
+ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_INDICATOR_INTENSITY,
+ AS3645A_INDICATOR_INTENSITY_MIN,
+ AS3645A_INDICATOR_INTENSITY_MAX,
+ AS3645A_INDICATOR_INTENSITY_STEP,
+ AS3645A_INDICATOR_INTENSITY_MIN);
+
+ flash->indicator_current = 0;
+
+ /* V4L2_CID_FLASH_FAULT */
+ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
+ V4L2_CID_FLASH_FAULT, 0,
+ V4L2_FLASH_FAULT_OVER_VOLTAGE |
+ V4L2_FLASH_FAULT_TIMEOUT |
+ V4L2_FLASH_FAULT_OVER_TEMPERATURE |
+ V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
+ if (ctrl != NULL)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ flash->subdev.ctrl_handler = &flash->ctrls;
+
+ return flash->ctrls.error;
+}
+
+static int __devinit as3645a_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct as3645a *flash;
+ int ret;
+
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ flash = kzalloc(sizeof(*flash), GFP_KERNEL);
+ if (flash == NULL)
+ return -ENOMEM;
+
+ flash->pdata = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops);
+ flash->subdev.internal_ops = &as3645a_internal_ops;
+ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = as3645a_init_controls(flash);
+ if (ret < 0)
+ goto done;
+
+ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
+ if (ret < 0)
+ goto done;
+
+ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
+
+ mutex_init(&flash->power_lock);
+
+ flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
+
+done:
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ kfree(flash);
+ }
+
+ return ret;
+}
+
+static int __devexit as3645a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct as3645a *flash = to_as3645a(subdev);
+
+ v4l2_device_unregister_subdev(subdev);
+ v4l2_ctrl_handler_free(&flash->ctrls);
+ media_entity_cleanup(&flash->subdev.entity);
+ mutex_destroy(&flash->power_lock);
+ kfree(flash);
+
+ return 0;
+}
+
+static const struct i2c_device_id as3645a_id_table[] = {
+ { AS3645A_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, as3645a_id_table);
+
+static const struct dev_pm_ops as3645a_pm_ops = {
+ .suspend = as3645a_suspend,
+ .resume = as3645a_resume,
+};
+
+static struct i2c_driver as3645a_i2c_driver = {
+ .driver = {
+ .name = AS3645A_NAME,
+ .pm = &as3645a_pm_ops,
+ },
+ .probe = as3645a_probe,
+ .remove = __devexit_p(as3645a_remove),
+ .id_table = as3645a_id_table,
+};
+
+module_i2c_driver(as3645a_i2c_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/bt819.c b/drivers/media/i2c/bt819.c
index 377bf05b1efd..377bf05b1efd 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/i2c/bt819.c
diff --git a/drivers/media/video/bt856.c b/drivers/media/i2c/bt856.c
index 7e5bd365c239..7e5bd365c239 100644
--- a/drivers/media/video/bt856.c
+++ b/drivers/media/i2c/bt856.c
diff --git a/drivers/media/video/bt866.c b/drivers/media/i2c/bt866.c
index 905320b67a1c..905320b67a1c 100644
--- a/drivers/media/video/bt866.c
+++ b/drivers/media/i2c/bt866.c
diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/i2c/btcx-risc.c
index ac1b2687a20d..ac1b2687a20d 100644
--- a/drivers/media/video/btcx-risc.c
+++ b/drivers/media/i2c/btcx-risc.c
diff --git a/drivers/media/video/btcx-risc.h b/drivers/media/i2c/btcx-risc.h
index f8bc6e8e7b51..f8bc6e8e7b51 100644
--- a/drivers/media/video/btcx-risc.h
+++ b/drivers/media/i2c/btcx-risc.h
diff --git a/drivers/media/video/cs5345.c b/drivers/media/i2c/cs5345.c
index c8581e26fa9c..c8581e26fa9c 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/i2c/cs5345.c
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/i2c/cs53l32a.c
index b293912206eb..b293912206eb 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/i2c/cs53l32a.c
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/i2c/cx2341x.c
index 103ef6bad2e2..103ef6bad2e2 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/i2c/cx2341x.c
diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/i2c/cx25840/Kconfig
index 451133ad41ff..451133ad41ff 100644
--- a/drivers/media/video/cx25840/Kconfig
+++ b/drivers/media/i2c/cx25840/Kconfig
diff --git a/drivers/media/i2c/cx25840/Makefile b/drivers/media/i2c/cx25840/Makefile
new file mode 100644
index 000000000000..898eb13340ae
--- /dev/null
+++ b/drivers/media/i2c/cx25840/Makefile
@@ -0,0 +1,6 @@
+cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
+ cx25840-vbi.o cx25840-ir.o
+
+obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
+
+ccflags-y += -Idrivers/media/i2c
diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/i2c/cx25840/cx25840-audio.c
index 34b96c7cfd62..34b96c7cfd62 100644
--- a/drivers/media/video/cx25840/cx25840-audio.c
+++ b/drivers/media/i2c/cx25840/cx25840-audio.c
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index d8eac3e30a7e..d8eac3e30a7e 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h
index bd4ada28b490..bd4ada28b490 100644
--- a/drivers/media/video/cx25840/cx25840-core.h
+++ b/drivers/media/i2c/cx25840/cx25840-core.h
diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c
new file mode 100644
index 000000000000..b3169f94ece8
--- /dev/null
+++ b/drivers/media/i2c/cx25840/cx25840-firmware.c
@@ -0,0 +1,175 @@
+/* cx25840 firmware functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+/*
+ * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
+ * size of the firmware chunks sent down the I2C bus to the chip.
+ * Previously this had been set to 1024 but unfortunately some I2C
+ * implementations can't transfer data in such big gulps.
+ * Specifically, the pvrusb2 driver has a hard limit of around 60
+ * bytes, due to the encapsulation there of I2C traffic into USB
+ * messages. So we have to significantly reduce this parameter.
+ */
+#define FWSEND 48
+
+#define FWDEV(x) &((x)->dev)
+
+static char *firmware = "";
+
+module_param(firmware, charp, 0444);
+
+MODULE_PARM_DESC(firmware, "Firmware image to load");
+
+static void start_fw_load(struct i2c_client *client)
+{
+ /* DL_ADDR_LB=0 DL_ADDR_HB=0 */
+ cx25840_write(client, 0x800, 0x00);
+ cx25840_write(client, 0x801, 0x00);
+ // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
+ cx25840_write(client, 0x803, 0x0b);
+ /* AUTO_INC_DIS=1 */
+ cx25840_write(client, 0x000, 0x20);
+}
+
+static void end_fw_load(struct i2c_client *client)
+{
+ /* AUTO_INC_DIS=0 */
+ cx25840_write(client, 0x000, 0x00);
+ /* DL_ENABLE=0 */
+ cx25840_write(client, 0x803, 0x03);
+}
+
+#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw"
+#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw"
+#define CX25840_FIRMWARE "v4l-cx25840.fw"
+
+static const char *get_fw_name(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+
+ if (firmware[0])
+ return firmware;
+ if (is_cx2388x(state))
+ return CX2388x_FIRMWARE;
+ if (is_cx231xx(state))
+ return CX231xx_FIRMWARE;
+ return CX25840_FIRMWARE;
+}
+
+static int check_fw_load(struct i2c_client *client, int size)
+{
+ /* DL_ADDR_HB DL_ADDR_LB */
+ int s = cx25840_read(client, 0x801) << 8;
+ s |= cx25840_read(client, 0x800);
+
+ if (size != s) {
+ v4l_err(client, "firmware %s load failed\n",
+ get_fw_name(client));
+ return -EINVAL;
+ }
+
+ v4l_info(client, "loaded %s firmware (%d bytes)\n",
+ get_fw_name(client), size);
+ return 0;
+}
+
+static int fw_write(struct i2c_client *client, const u8 *data, int size)
+{
+ if (i2c_master_send(client, data, size) < size) {
+ v4l_err(client, "firmware load i2c failure\n");
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+int cx25840_loadfw(struct i2c_client *client)
+{
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+ const struct firmware *fw = NULL;
+ u8 buffer[FWSEND];
+ const u8 *ptr;
+ const char *fwname = get_fw_name(client);
+ int size, retval;
+ int MAX_BUF_SIZE = FWSEND;
+ u32 gpio_oe = 0, gpio_da = 0;
+
+ if (is_cx2388x(state)) {
+ /* Preserve the GPIO OE and output bits */
+ gpio_oe = cx25840_read(client, 0x160);
+ gpio_da = cx25840_read(client, 0x164);
+ }
+
+ if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
+ v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
+ MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
+ }
+
+ if (request_firmware(&fw, fwname, FWDEV(client)) != 0) {
+ v4l_err(client, "unable to open firmware %s\n", fwname);
+ return -EINVAL;
+ }
+
+ start_fw_load(client);
+
+ buffer[0] = 0x08;
+ buffer[1] = 0x02;
+
+ size = fw->size;
+ ptr = fw->data;
+ while (size > 0) {
+ int len = min(MAX_BUF_SIZE - 2, size);
+
+ memcpy(buffer + 2, ptr, len);
+
+ retval = fw_write(client, buffer, len + 2);
+
+ if (retval < 0) {
+ release_firmware(fw);
+ return retval;
+ }
+
+ size -= len;
+ ptr += len;
+ }
+
+ end_fw_load(client);
+
+ size = fw->size;
+ release_firmware(fw);
+
+ if (is_cx2388x(state)) {
+ /* Restore GPIO configuration after f/w load */
+ cx25840_write(client, 0x160, gpio_oe);
+ cx25840_write(client, 0x164, gpio_da);
+ }
+
+ return check_fw_load(client, size);
+}
+
+MODULE_FIRMWARE(CX2388x_FIRMWARE);
+MODULE_FIRMWARE(CX231xx_FIRMWARE);
+MODULE_FIRMWARE(CX25840_FIRMWARE);
+
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c
index 38ce76ed1924..38ce76ed1924 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/i2c/cx25840/cx25840-ir.c
diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c
new file mode 100644
index 000000000000..c39e91dc1137
--- /dev/null
+++ b/drivers/media/i2c/cx25840/cx25840-vbi.c
@@ -0,0 +1,257 @@
+/* cx25840 VBI functions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <media/v4l2-common.h>
+#include <media/cx25840.h>
+
+#include "cx25840-core.h"
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int decode_vps(u8 * dst, u8 * p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ static const u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
+
+ memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
+ svbi->service_set = 0;
+ /* we're done if raw VBI is active */
+ if ((cx25840_read(client, 0x404) & 0x10) == 0)
+ return 0;
+
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx25840_read(client, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx25840_read(client, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ }
+ return 0;
+}
+
+int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int vbi_offset = is_pal ? 1 : 0;
+
+ /* Setup standard */
+ cx25840_std_setup(client);
+
+ /* VBI Offset */
+ cx25840_write(client, 0x47f, vbi_offset);
+ cx25840_write(client, 0x404, 0x2e);
+ return 0;
+}
+
+int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct cx25840_state *state = to_state(sd);
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int vbi_offset = is_pal ? 1 : 0;
+ int i, x;
+ u8 lcr[24];
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup standard */
+ cx25840_std_setup(client);
+
+ /* Sliced VBI */
+ cx25840_write(client, 0x404, 0x32); /* Ancillary data */
+ cx25840_write(client, 0x406, 0x13);
+ cx25840_write(client, 0x47f, vbi_offset);
+
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx25840_write(client, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx25840_write(client, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx25840_write(client, i, 0);
+ }
+
+ cx25840_write(client, 0x43c, 0x16);
+ cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
+ return 0;
+}
+
+int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct cx25840_state *state = to_state(sd);
+ u8 *p = vbi->p;
+ int id1, id2, l, err = 0;
+
+ if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+ (p[3] != 0x55 && p[3] != 0x91)) {
+ vbi->line = vbi->type = 0;
+ return 0;
+ }
+
+ p += 4;
+ id1 = p[-1];
+ id2 = p[0] & 0xf;
+ l = p[2] & 0x3f;
+ l += state->vbi_line_offset;
+ p += 4;
+
+ switch (id2) {
+ case 1:
+ id2 = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ id2 = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ id2 = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ id2 = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
+ err = 1;
+ break;
+ default:
+ id2 = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : id2;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (id1 == 0x55);
+ vbi->p = p;
+ return 0;
+}
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 04f192a0398a..04f192a0398a 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c
new file mode 100644
index 000000000000..04a6efa37cc3
--- /dev/null
+++ b/drivers/media/i2c/ks0127.c
@@ -0,0 +1,733 @@
+/*
+ * Video Capture Driver (Video for Linux 1/2)
+ * for the Matrox Marvel G200,G400 and Rainbow Runner-G series
+ *
+ * This module is an interface to the KS0127 video decoder chip.
+ *
+ * Copyright (C) 1999 Ryan Drake <stiletto@mediaone.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *****************************************************************************
+ *
+ * Modified and extended by
+ * Mike Bernson <mike@mlb.org>
+ * Gerard v.d. Horst
+ * Leon van Stuivenberg <l.vanstuivenberg@chello.nl>
+ * Gernot Ziegler <gz@lysator.liu.se>
+ *
+ * Version History:
+ * V1.0 Ryan Drake Initial version by Ryan Drake
+ * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include "ks0127.h"
+
+MODULE_DESCRIPTION("KS0127 video decoder driver");
+MODULE_AUTHOR("Ryan Drake");
+MODULE_LICENSE("GPL");
+
+/* Addresses */
+#define I2C_KS0127_ADDON 0xD8
+#define I2C_KS0127_ONBOARD 0xDA
+
+
+/* ks0127 control registers */
+#define KS_STAT 0x00
+#define KS_CMDA 0x01
+#define KS_CMDB 0x02
+#define KS_CMDC 0x03
+#define KS_CMDD 0x04
+#define KS_HAVB 0x05
+#define KS_HAVE 0x06
+#define KS_HS1B 0x07
+#define KS_HS1E 0x08
+#define KS_HS2B 0x09
+#define KS_HS2E 0x0a
+#define KS_AGC 0x0b
+#define KS_HXTRA 0x0c
+#define KS_CDEM 0x0d
+#define KS_PORTAB 0x0e
+#define KS_LUMA 0x0f
+#define KS_CON 0x10
+#define KS_BRT 0x11
+#define KS_CHROMA 0x12
+#define KS_CHROMB 0x13
+#define KS_DEMOD 0x14
+#define KS_SAT 0x15
+#define KS_HUE 0x16
+#define KS_VERTIA 0x17
+#define KS_VERTIB 0x18
+#define KS_VERTIC 0x19
+#define KS_HSCLL 0x1a
+#define KS_HSCLH 0x1b
+#define KS_VSCLL 0x1c
+#define KS_VSCLH 0x1d
+#define KS_OFMTA 0x1e
+#define KS_OFMTB 0x1f
+#define KS_VBICTL 0x20
+#define KS_CCDAT2 0x21
+#define KS_CCDAT1 0x22
+#define KS_VBIL30 0x23
+#define KS_VBIL74 0x24
+#define KS_VBIL118 0x25
+#define KS_VBIL1512 0x26
+#define KS_TTFRAM 0x27
+#define KS_TESTA 0x28
+#define KS_UVOFFH 0x29
+#define KS_UVOFFL 0x2a
+#define KS_UGAIN 0x2b
+#define KS_VGAIN 0x2c
+#define KS_VAVB 0x2d
+#define KS_VAVE 0x2e
+#define KS_CTRACK 0x2f
+#define KS_POLCTL 0x30
+#define KS_REFCOD 0x31
+#define KS_INVALY 0x32
+#define KS_INVALU 0x33
+#define KS_INVALV 0x34
+#define KS_UNUSEY 0x35
+#define KS_UNUSEU 0x36
+#define KS_UNUSEV 0x37
+#define KS_USRSAV 0x38
+#define KS_USREAV 0x39
+#define KS_SHS1A 0x3a
+#define KS_SHS1B 0x3b
+#define KS_SHS1C 0x3c
+#define KS_CMDE 0x3d
+#define KS_VSDEL 0x3e
+#define KS_CMDF 0x3f
+#define KS_GAMMA0 0x40
+#define KS_GAMMA1 0x41
+#define KS_GAMMA2 0x42
+#define KS_GAMMA3 0x43
+#define KS_GAMMA4 0x44
+#define KS_GAMMA5 0x45
+#define KS_GAMMA6 0x46
+#define KS_GAMMA7 0x47
+#define KS_GAMMA8 0x48
+#define KS_GAMMA9 0x49
+#define KS_GAMMA10 0x4a
+#define KS_GAMMA11 0x4b
+#define KS_GAMMA12 0x4c
+#define KS_GAMMA13 0x4d
+#define KS_GAMMA14 0x4e
+#define KS_GAMMA15 0x4f
+#define KS_GAMMA16 0x50
+#define KS_GAMMA17 0x51
+#define KS_GAMMA18 0x52
+#define KS_GAMMA19 0x53
+#define KS_GAMMA20 0x54
+#define KS_GAMMA21 0x55
+#define KS_GAMMA22 0x56
+#define KS_GAMMA23 0x57
+#define KS_GAMMA24 0x58
+#define KS_GAMMA25 0x59
+#define KS_GAMMA26 0x5a
+#define KS_GAMMA27 0x5b
+#define KS_GAMMA28 0x5c
+#define KS_GAMMA29 0x5d
+#define KS_GAMMA30 0x5e
+#define KS_GAMMA31 0x5f
+#define KS_GAMMAD0 0x60
+#define KS_GAMMAD1 0x61
+#define KS_GAMMAD2 0x62
+#define KS_GAMMAD3 0x63
+#define KS_GAMMAD4 0x64
+#define KS_GAMMAD5 0x65
+#define KS_GAMMAD6 0x66
+#define KS_GAMMAD7 0x67
+#define KS_GAMMAD8 0x68
+#define KS_GAMMAD9 0x69
+#define KS_GAMMAD10 0x6a
+#define KS_GAMMAD11 0x6b
+#define KS_GAMMAD12 0x6c
+#define KS_GAMMAD13 0x6d
+#define KS_GAMMAD14 0x6e
+#define KS_GAMMAD15 0x6f
+#define KS_GAMMAD16 0x70
+#define KS_GAMMAD17 0x71
+#define KS_GAMMAD18 0x72
+#define KS_GAMMAD19 0x73
+#define KS_GAMMAD20 0x74
+#define KS_GAMMAD21 0x75
+#define KS_GAMMAD22 0x76
+#define KS_GAMMAD23 0x77
+#define KS_GAMMAD24 0x78
+#define KS_GAMMAD25 0x79
+#define KS_GAMMAD26 0x7a
+#define KS_GAMMAD27 0x7b
+#define KS_GAMMAD28 0x7c
+#define KS_GAMMAD29 0x7d
+#define KS_GAMMAD30 0x7e
+#define KS_GAMMAD31 0x7f
+
+
+/****************************************************************************
+* mga_dev : represents one ks0127 chip.
+****************************************************************************/
+
+struct adjust {
+ int contrast;
+ int bright;
+ int hue;
+ int ugain;
+ int vgain;
+};
+
+struct ks0127 {
+ struct v4l2_subdev sd;
+ v4l2_std_id norm;
+ int ident;
+ u8 regs[256];
+};
+
+static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ks0127, sd);
+}
+
+
+static int debug; /* insmod parameter */
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug output");
+
+static u8 reg_defaults[64];
+
+static void init_reg_defaults(void)
+{
+ static int initialized;
+ u8 *table = reg_defaults;
+
+ if (initialized)
+ return;
+ initialized = 1;
+
+ table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */
+ table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */
+ table[KS_CMDC] = 0x00; /* Test options */
+ /* clock & input select, write 1 to PORTA */
+ table[KS_CMDD] = 0x01;
+ table[KS_HAVB] = 0x00; /* HAV Start Control */
+ table[KS_HAVE] = 0x00; /* HAV End Control */
+ table[KS_HS1B] = 0x10; /* HS1 Start Control */
+ table[KS_HS1E] = 0x00; /* HS1 End Control */
+ table[KS_HS2B] = 0x00; /* HS2 Start Control */
+ table[KS_HS2E] = 0x00; /* HS2 End Control */
+ table[KS_AGC] = 0x53; /* Manual setting for AGC */
+ table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */
+ table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */
+ table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */
+ table[KS_LUMA] = 0x01; /* Luma control */
+ table[KS_CON] = 0x00; /* Contrast Control */
+ table[KS_BRT] = 0x00; /* Brightness Control */
+ table[KS_CHROMA] = 0x2a; /* Chroma control A */
+ table[KS_CHROMB] = 0x90; /* Chroma control B */
+ table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */
+ table[KS_SAT] = 0x00; /* Color Saturation Control*/
+ table[KS_HUE] = 0x00; /* Hue Control */
+ table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */
+ /* Vertical Processing Control B, luma 1 line delayed */
+ table[KS_VERTIB] = 0x12;
+ table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */
+ table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */
+ table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */
+ table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */
+ table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */
+ /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */
+ table[KS_OFMTA] = 0x30;
+ table[KS_OFMTB] = 0x00; /* Output Control B */
+ /* VBI Decoder Control; 4bit fmt: avoid Y overflow */
+ table[KS_VBICTL] = 0x5d;
+ table[KS_CCDAT2] = 0x00; /* Read Only register */
+ table[KS_CCDAT1] = 0x00; /* Read Only register */
+ table[KS_VBIL30] = 0xa8; /* VBI data decoding options */
+ table[KS_VBIL74] = 0xaa; /* VBI data decoding options */
+ table[KS_VBIL118] = 0x2a; /* VBI data decoding options */
+ table[KS_VBIL1512] = 0x00; /* VBI data decoding options */
+ table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */
+ table[KS_TESTA] = 0x00; /* test register, shouldn't be written */
+ table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */
+ table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */
+ table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */
+ table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */
+ table[KS_VAVB] = 0x07; /* VAV Begin */
+ table[KS_VAVE] = 0x00; /* VAV End */
+ table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */
+ table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */
+ table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */
+ table[KS_INVALY] = 0x10; /* Invalid Y Code */
+ table[KS_INVALU] = 0x80; /* Invalid U Code */
+ table[KS_INVALV] = 0x80; /* Invalid V Code */
+ table[KS_UNUSEY] = 0x10; /* Unused Y Code */
+ table[KS_UNUSEU] = 0x80; /* Unused U Code */
+ table[KS_UNUSEV] = 0x80; /* Unused V Code */
+ table[KS_USRSAV] = 0x00; /* reserved */
+ table[KS_USREAV] = 0x00; /* reserved */
+ table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */
+ /* User Defined SHS1 B, ALT656=1 on 0127B */
+ table[KS_SHS1B] = 0x80;
+ table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */
+ table[KS_CMDE] = 0x00; /* Command Register E */
+ table[KS_VSDEL] = 0x00; /* VS Delay Control */
+ /* Command Register F, update -immediately- */
+ /* (there might come no vsync)*/
+ table[KS_CMDF] = 0x02;
+}
+
+
+/* We need to manually read because of a bug in the KS0127 chip.
+ *
+ * An explanation from kayork@mail.utexas.edu:
+ *
+ * During I2C reads, the KS0127 only samples for a stop condition
+ * during the place where the acknowledge bit should be. Any standard
+ * I2C implementation (correctly) throws in another clock transition
+ * at the 9th bit, and the KS0127 will not recognize the stop condition
+ * and will continue to clock out data.
+ *
+ * So we have to do the read ourself. Big deal.
+ * workaround in i2c-algo-bit
+ */
+
+
+static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ char val = 0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(reg),
+ .buf = &reg
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD | I2C_M_NO_RD_ACK,
+ .len = sizeof(val),
+ .buf = &val
+ }
+ };
+ int ret;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ v4l2_dbg(1, debug, sd, "read error\n");
+
+ return val;
+}
+
+
+static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ks0127 *ks = to_ks0127(sd);
+ char msg[] = { reg, val };
+
+ if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg))
+ v4l2_dbg(1, debug, sd, "write error\n");
+
+ ks->regs[reg] = val;
+}
+
+
+/* generic bit-twiddling */
+static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ u8 val = ks->regs[reg];
+ val = (val & and_v) | or_v;
+ ks0127_write(sd, reg, val);
+}
+
+
+
+/****************************************************************************
+* ks0127 private api
+****************************************************************************/
+static void ks0127_init(struct v4l2_subdev *sd)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+ u8 *table = reg_defaults;
+ int i;
+
+ ks->ident = V4L2_IDENT_KS0127;
+
+ v4l2_dbg(1, debug, sd, "reset\n");
+ msleep(1);
+
+ /* initialize all registers to known values */
+ /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */
+
+ for (i = 1; i < 33; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 35; i < 40; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 41; i < 56; i++)
+ ks0127_write(sd, i, table[i]);
+
+ for (i = 58; i < 64; i++)
+ ks0127_write(sd, i, table[i]);
+
+
+ if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) {
+ ks->ident = V4L2_IDENT_KS0122S;
+ v4l2_dbg(1, debug, sd, "ks0122s found\n");
+ return;
+ }
+
+ switch (ks0127_read(sd, KS_CMDE) & 0x0f) {
+ case 0:
+ v4l2_dbg(1, debug, sd, "ks0127 found\n");
+ break;
+
+ case 9:
+ ks->ident = V4L2_IDENT_KS0127B;
+ v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n");
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd, "unknown revision\n");
+ break;
+ }
+}
+
+static int ks0127_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ switch (input) {
+ case KS_INPUT_COMPOSITE_1:
+ case KS_INPUT_COMPOSITE_2:
+ case KS_INPUT_COMPOSITE_3:
+ case KS_INPUT_COMPOSITE_4:
+ case KS_INPUT_COMPOSITE_5:
+ case KS_INPUT_COMPOSITE_6:
+ v4l2_dbg(1, debug, sd,
+ "s_routing %d: Composite\n", input);
+ /* autodetect 50/60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
+ /* VSE=0 */
+ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
+ /* set input line */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, input);
+ /* non-freerunning mode */
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
+ /* analog input */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
+ /* enable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
+ /* chroma trap, HYBWR=1 */
+ ks0127_and_or(sd, KS_LUMA, 0x00,
+ (reg_defaults[KS_LUMA])|0x0c);
+ /* scaler fullbw, luma comb off */
+ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
+ /* manual chroma comb .25 .5 .25 */
+ ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90);
+
+ /* chroma path delay */
+ ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90);
+
+ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
+ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
+ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+ break;
+
+ case KS_INPUT_SVIDEO_1:
+ case KS_INPUT_SVIDEO_2:
+ case KS_INPUT_SVIDEO_3:
+ v4l2_dbg(1, debug, sd,
+ "s_routing %d: S-Video\n", input);
+ /* autodetect 50/60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
+ /* VSE=0 */
+ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
+ /* set input line */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, input);
+ /* non-freerunning mode */
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
+ /* analog input */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
+ /* enable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
+ ks0127_and_or(sd, KS_LUMA, 0x00,
+ reg_defaults[KS_LUMA]);
+ /* disable luma comb */
+ ks0127_and_or(sd, KS_VERTIA, 0x08,
+ (reg_defaults[KS_VERTIA]&0xf0)|0x01);
+ ks0127_and_or(sd, KS_VERTIC, 0x0f,
+ reg_defaults[KS_VERTIC]&0xf0);
+
+ ks0127_and_or(sd, KS_CHROMB, 0x0f,
+ reg_defaults[KS_CHROMB]&0xf0);
+
+ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
+ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
+ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+ break;
+
+ case KS_INPUT_YUV656:
+ v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n");
+ if (ks->norm & V4L2_STD_525_60)
+ /* force 60 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03);
+ else
+ /* force 50 Hz */
+ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02);
+
+ ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */
+ /* set input line and VALIGN */
+ ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40));
+ /* freerunning mode, */
+ /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/
+ ks0127_and_or(sd, KS_CMDC, 0x70, 0x87);
+ /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */
+ ks0127_and_or(sd, KS_CMDD, 0x03, 0x08);
+ /* disable chroma demodulation */
+ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30);
+ /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */
+ ks0127_and_or(sd, KS_LUMA, 0x00, 0x71);
+ ks0127_and_or(sd, KS_VERTIC, 0x0f,
+ reg_defaults[KS_VERTIC]&0xf0);
+
+ /* scaler fullbw, luma comb off */
+ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
+
+ ks0127_and_or(sd, KS_CHROMB, 0x0f,
+ reg_defaults[KS_CHROMB]&0xf0);
+
+ ks0127_and_or(sd, KS_CON, 0x00, 0x00);
+ ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */
+ /* spec: 229 (e5) */
+ ks0127_and_or(sd, KS_SAT, 0x00, 0xe8);
+ ks0127_and_or(sd, KS_HUE, 0x00, 0);
+
+ ks0127_and_or(sd, KS_UGAIN, 0x00, 238);
+ ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00);
+
+ /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */
+ ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f);
+ ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00);
+ break;
+
+ default:
+ v4l2_dbg(1, debug, sd,
+ "s_routing: Unknown input %d\n", input);
+ break;
+ }
+
+ /* hack: CDMLPF sometimes spontaneously switches on; */
+ /* force back off */
+ ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]);
+ return 0;
+}
+
+static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ks0127 *ks = to_ks0127(sd);
+
+ /* Set to automatic SECAM/Fsc mode */
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
+
+ ks->norm = std;
+ if (std & V4L2_STD_NTSC) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: NTSC_M\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
+ } else if (std & V4L2_STD_PAL_N) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: NTSC_N (fixme)\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
+ } else if (std & V4L2_STD_PAL) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: PAL_N\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
+ } else if (std & V4L2_STD_PAL_M) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: PAL_M (fixme)\n");
+ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
+ } else if (std & V4L2_STD_SECAM) {
+ v4l2_dbg(1, debug, sd,
+ "s_std: SECAM\n");
+
+ /* set to secam autodetection */
+ ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20);
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
+ schedule_timeout_interruptible(HZ/10+1);
+
+ /* did it autodetect? */
+ if (!(ks0127_read(sd, KS_DEMOD) & 0x40))
+ /* force to secam mode */
+ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f);
+ } else {
+ v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n",
+ (unsigned long long)std);
+ }
+ return 0;
+}
+
+static int ks0127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable);
+ if (enable) {
+ /* All output pins on */
+ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30);
+ /* Obey the OEN pin */
+ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00);
+ } else {
+ /* Video output pins off */
+ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00);
+ /* Ignore the OEN pin */
+ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80);
+ }
+ return 0;
+}
+
+static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+{
+ int stat = V4L2_IN_ST_NO_SIGNAL;
+ u8 status;
+ v4l2_std_id std = V4L2_STD_ALL;
+
+ status = ks0127_read(sd, KS_STAT);
+ if (!(status & 0x20)) /* NOVID not set */
+ stat = 0;
+ if (!(status & 0x01)) /* CLOCK set */
+ stat |= V4L2_IN_ST_NO_COLOR;
+ if ((status & 0x08)) /* PALDET set */
+ std = V4L2_STD_PAL;
+ else
+ std = V4L2_STD_NTSC;
+ if (pstd)
+ *pstd = std;
+ if (pstatus)
+ *pstatus = stat;
+ return 0;
+}
+
+static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ v4l2_dbg(1, debug, sd, "querystd\n");
+ return ks0127_status(sd, NULL, std);
+}
+
+static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ v4l2_dbg(1, debug, sd, "g_input_status\n");
+ return ks0127_status(sd, status, NULL);
+}
+
+static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ks0127 *ks = to_ks0127(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops ks0127_core_ops = {
+ .g_chip_ident = ks0127_g_chip_ident,
+ .s_std = ks0127_s_std,
+};
+
+static const struct v4l2_subdev_video_ops ks0127_video_ops = {
+ .s_routing = ks0127_s_routing,
+ .s_stream = ks0127_s_stream,
+ .querystd = ks0127_querystd,
+ .g_input_status = ks0127_g_input_status,
+};
+
+static const struct v4l2_subdev_ops ks0127_ops = {
+ .core = &ks0127_core_ops,
+ .video = &ks0127_video_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct ks0127 *ks;
+ struct v4l2_subdev *sd;
+
+ v4l_info(client, "%s chip found @ 0x%x (%s)\n",
+ client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
+ client->addr << 1, client->adapter->name);
+
+ ks = kzalloc(sizeof(*ks), GFP_KERNEL);
+ if (ks == NULL)
+ return -ENOMEM;
+ sd = &ks->sd;
+ v4l2_i2c_subdev_init(sd, client, &ks0127_ops);
+
+ /* power up */
+ init_reg_defaults();
+ ks0127_write(sd, KS_CMDA, 0x2c);
+ mdelay(10);
+
+ /* reset the device */
+ ks0127_init(sd);
+ return 0;
+}
+
+static int ks0127_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */
+ ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */
+ kfree(to_ks0127(sd));
+ return 0;
+}
+
+static const struct i2c_device_id ks0127_id[] = {
+ { "ks0127", 0 },
+ { "ks0127b", 0 },
+ { "ks0122s", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ks0127_id);
+
+static struct i2c_driver ks0127_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ks0127",
+ },
+ .probe = ks0127_probe,
+ .remove = ks0127_remove,
+ .id_table = ks0127_id,
+};
+
+module_i2c_driver(ks0127_driver);
diff --git a/drivers/media/video/ks0127.h b/drivers/media/i2c/ks0127.h
index cb8abd5403b3..cb8abd5403b3 100644
--- a/drivers/media/video/ks0127.h
+++ b/drivers/media/i2c/ks0127.h
diff --git a/drivers/media/video/m52790.c b/drivers/media/i2c/m52790.c
index 0991576f4c82..0991576f4c82 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/i2c/m52790.c
diff --git a/drivers/media/video/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig
index dc8c2505907e..dc8c2505907e 100644
--- a/drivers/media/video/m5mols/Kconfig
+++ b/drivers/media/i2c/m5mols/Kconfig
diff --git a/drivers/media/video/m5mols/Makefile b/drivers/media/i2c/m5mols/Makefile
index 0a44e028edc7..0a44e028edc7 100644
--- a/drivers/media/video/m5mols/Makefile
+++ b/drivers/media/i2c/m5mols/Makefile
diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h
new file mode 100644
index 000000000000..86c815be348c
--- /dev/null
+++ b/drivers/media/i2c/m5mols/m5mols.h
@@ -0,0 +1,338 @@
+/*
+ * Header for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.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 version.
+ */
+
+#ifndef M5MOLS_H
+#define M5MOLS_H
+
+#include <media/v4l2-subdev.h>
+#include "m5mols_reg.h"
+
+extern int m5mols_debug;
+
+enum m5mols_restype {
+ M5MOLS_RESTYPE_MONITOR,
+ M5MOLS_RESTYPE_CAPTURE,
+ M5MOLS_RESTYPE_MAX,
+};
+
+/**
+ * struct m5mols_resolution - structure for the resolution
+ * @type: resolution type according to the pixel code
+ * @width: width of the resolution
+ * @height: height of the resolution
+ * @reg: resolution preset register value
+ */
+struct m5mols_resolution {
+ u8 reg;
+ enum m5mols_restype type;
+ u16 width;
+ u16 height;
+};
+
+/**
+ * struct m5mols_exif - structure for the EXIF information of M-5MOLS
+ * @exposure_time: exposure time register value
+ * @shutter_speed: speed of the shutter register value
+ * @aperture: aperture register value
+ * @exposure_bias: it calls also EV bias
+ * @iso_speed: ISO register value
+ * @flash: status register value of the flash
+ * @sdr: status register value of the Subject Distance Range
+ * @qval: not written exact meaning in document
+ */
+struct m5mols_exif {
+ u32 exposure_time;
+ u32 shutter_speed;
+ u32 aperture;
+ u32 brightness;
+ u32 exposure_bias;
+ u16 iso_speed;
+ u16 flash;
+ u16 sdr;
+ u16 qval;
+};
+
+/**
+ * struct m5mols_capture - Structure for the capture capability
+ * @exif: EXIF information
+ * @main: size in bytes of the main image
+ * @thumb: size in bytes of the thumb image, if it was accompanied
+ * @total: total size in bytes of the produced image
+ */
+struct m5mols_capture {
+ struct m5mols_exif exif;
+ u32 main;
+ u32 thumb;
+ u32 total;
+};
+
+/**
+ * struct m5mols_scenemode - structure for the scenemode capability
+ * @metering: metering light register value
+ * @ev_bias: EV bias register value
+ * @wb_mode: mode which means the WhiteBalance is Auto or Manual
+ * @wb_preset: whitebalance preset register value in the Manual mode
+ * @chroma_en: register value whether the Chroma capability is enabled or not
+ * @chroma_lvl: chroma's level register value
+ * @edge_en: register value Whether the Edge capability is enabled or not
+ * @edge_lvl: edge's level register value
+ * @af_range: Auto Focus's range
+ * @fd_mode: Face Detection mode
+ * @mcc: Multi-axis Color Conversion which means emotion color
+ * @light: status of the Light
+ * @flash: status of the Flash
+ * @tone: Tone color which means Contrast
+ * @iso: ISO register value
+ * @capt_mode: Mode of the Image Stabilization while the camera capturing
+ * @wdr: Wide Dynamic Range register value
+ *
+ * The each value according to each scenemode is recommended in the documents.
+ */
+struct m5mols_scenemode {
+ u8 metering;
+ u8 ev_bias;
+ u8 wb_mode;
+ u8 wb_preset;
+ u8 chroma_en;
+ u8 chroma_lvl;
+ u8 edge_en;
+ u8 edge_lvl;
+ u8 af_range;
+ u8 fd_mode;
+ u8 mcc;
+ u8 light;
+ u8 flash;
+ u8 tone;
+ u8 iso;
+ u8 capt_mode;
+ u8 wdr;
+};
+
+/**
+ * struct m5mols_version - firmware version information
+ * @customer: customer information
+ * @project: version of project information according to customer
+ * @fw: firmware revision
+ * @hw: hardware revision
+ * @param: version of the parameter
+ * @awb: Auto WhiteBalance algorithm version
+ * @str: information about manufacturer and packaging vendor
+ * @af: Auto Focus version
+ *
+ * The register offset starts the customer version at 0x0, and it ends
+ * the awb version at 0x09. The customer, project information occupies 1 bytes
+ * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
+ * unique string associated with firmware's version. It includes information
+ * about manufacturer and the vendor of the sensor's packaging. The least
+ * significant 2 bytes of the string indicate packaging manufacturer.
+ */
+#define VERSION_STRING_SIZE 22
+struct m5mols_version {
+ u8 customer;
+ u8 project;
+ u16 fw;
+ u16 hw;
+ u16 param;
+ u16 awb;
+ u8 str[VERSION_STRING_SIZE];
+ u8 af;
+};
+
+/**
+ * struct m5mols_info - M-5MOLS driver data structure
+ * @pdata: platform data
+ * @sd: v4l-subdev instance
+ * @pad: media pad
+ * @irq_waitq: waitqueue for the capture
+ * @irq_done: set to 1 in the interrupt handler
+ * @handle: control handler
+ * @auto_exposure: auto/manual exposure control
+ * @exposure_bias: exposure compensation control
+ * @exposure: manual exposure control
+ * @metering: exposure metering control
+ * @auto_iso: auto/manual ISO sensitivity control
+ * @iso: manual ISO sensitivity control
+ * @auto_wb: auto white balance control
+ * @lock_3a: 3A lock control
+ * @colorfx: color effect control
+ * @saturation: saturation control
+ * @zoom: zoom control
+ * @wdr: wide dynamic range control
+ * @stabilization: image stabilization control
+ * @jpeg_quality: JPEG compression quality control
+ * @set_power: optional power callback to the board code
+ * @lock: mutex protecting the structure fields below
+ * @ffmt: current fmt according to resolution type
+ * @res_type: current resolution type
+ * @ver: information of the version
+ * @cap: the capture mode attributes
+ * @isp_ready: 1 when the ISP controller has completed booting
+ * @power: current sensor's power status
+ * @ctrl_sync: 1 when the control handler state is restored in H/W
+ * @resolution: register value for current resolution
+ * @mode: register value for current operation mode
+ */
+struct m5mols_info {
+ const struct m5mols_platform_data *pdata;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ wait_queue_head_t irq_waitq;
+ atomic_t irq_done;
+
+ struct v4l2_ctrl_handler handle;
+ struct {
+ /* exposure/exposure bias/auto exposure cluster */
+ struct v4l2_ctrl *auto_exposure;
+ struct v4l2_ctrl *exposure_bias;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *metering;
+ };
+ struct {
+ /* iso/auto iso cluster */
+ struct v4l2_ctrl *auto_iso;
+ struct v4l2_ctrl *iso;
+ };
+ struct v4l2_ctrl *auto_wb;
+
+ struct v4l2_ctrl *lock_3a;
+ struct v4l2_ctrl *colorfx;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *zoom;
+ struct v4l2_ctrl *wdr;
+ struct v4l2_ctrl *stabilization;
+ struct v4l2_ctrl *jpeg_quality;
+
+ int (*set_power)(struct device *dev, int on);
+
+ struct mutex lock;
+
+ struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
+ int res_type;
+
+ struct m5mols_version ver;
+ struct m5mols_capture cap;
+
+ unsigned int isp_ready:1;
+ unsigned int power:1;
+ unsigned int ctrl_sync:1;
+
+ u8 resolution;
+ u8 mode;
+};
+
+#define is_available_af(__info) (__info->ver.af)
+#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
+#define is_manufacturer(__info, __manufacturer) \
+ (__info->ver.str[0] == __manufacturer[0] && \
+ __info->ver.str[1] == __manufacturer[1])
+/*
+ * I2C operation of the M-5MOLS
+ *
+ * The I2C read operation of the M-5MOLS requires 2 messages. The first
+ * message sends the information about the command, command category, and total
+ * message size. The second message is used to retrieve the data specifed in
+ * the first message
+ *
+ * 1st message 2nd message
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+-------+ +------+------+------+------+
+ * - size1: message data size(5 in this case)
+ * - size2: desired buffer size of the 2nd message
+ * - d[0..3]: according to size2
+ *
+ * The I2C write operation needs just one message. The message includes
+ * category, command, total size, and desired data.
+ *
+ * 1st message
+ * +-------+---+----------+-----+------+------+------+------+
+ * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
+ * +-------+---+----------+-----+------+------+------+------+
+ * - d[0..3]: according to size1
+ */
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val);
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val);
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
+int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
+
+int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
+ int timeout);
+
+/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */
+#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16)
+/* ISP state transition timeout, in ms */
+#define M5MOLS_MODE_CHANGE_TIMEOUT 200
+#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250
+
+/*
+ * Mode operation of the M-5MOLS
+ *
+ * Changing the mode of the M-5MOLS is needed right executing order.
+ * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
+ * by user. There are various categories associated with each mode.
+ *
+ * +============================================================+
+ * | mode | category |
+ * +============================================================+
+ * | FLASH | FLASH(only after Stand-by or Power-on) |
+ * | SYSTEM | SYSTEM(only after sensor arm-booting) |
+ * | PARAMETER | PARAMETER |
+ * | MONITOR | MONITOR(preview), Auto Focus, Face Detection |
+ * | CAPTURE | Single CAPTURE, Preview(recording) |
+ * +============================================================+
+ *
+ * The available executing order between each modes are as follows:
+ * PARAMETER <---> MONITOR <---> CAPTURE
+ */
+int m5mols_set_mode(struct m5mols_info *info, u8 mode);
+
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
+int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout);
+int m5mols_restore_controls(struct m5mols_info *info);
+int m5mols_start_capture(struct m5mols_info *info);
+int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
+int m5mols_lock_3a(struct m5mols_info *info, bool lock);
+int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+int m5mols_init_controls(struct v4l2_subdev *sd);
+
+/* The firmware function */
+int m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool));
+
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ struct m5mols_info *info = container_of(ctrl->handler,
+ struct m5mols_info, handle);
+ return &info->sd;
+}
+
+static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl,
+ unsigned int mode)
+{
+ ctrl->priv = (void *)(uintptr_t)mode;
+}
+
+static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl)
+{
+ return (unsigned int)(uintptr_t)ctrl->priv;
+}
+
+#endif /* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c
index cb243bd278ce..cb243bd278ce 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/i2c/m5mols/m5mols_capture.c
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c
index f34429e452ab..f34429e452ab 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/i2c/m5mols/m5mols_controls.c
diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c
new file mode 100644
index 000000000000..2f490ef26c38
--- /dev/null
+++ b/drivers/media/i2c/m5mols/m5mols_core.c
@@ -0,0 +1,1010 @@
+/*
+ * Driver for M-5MOLS 8M Pixel camera sensor with ISP
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Author: HeungJun Kim <riverful.kim@samsung.com>
+ *
+ * Copyright (C) 2009 Samsung Electronics Co., Ltd.
+ * Author: Dongsoo Nathaniel Kim <dongsoo45.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 version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/m5mols.h>
+
+#include "m5mols.h"
+#include "m5mols_reg.h"
+
+int m5mols_debug;
+module_param(m5mols_debug, int, 0644);
+
+#define MODULE_NAME "M5MOLS"
+#define M5MOLS_I2C_CHECK_RETRY 500
+
+/* The regulator consumer names for external voltage regulators */
+static struct regulator_bulk_data supplies[] = {
+ {
+ .supply = "core", /* ARM core power, 1.2V */
+ }, {
+ .supply = "dig_18", /* digital power 1, 1.8V */
+ }, {
+ .supply = "d_sensor", /* sensor power 1, 1.8V */
+ }, {
+ .supply = "dig_28", /* digital power 2, 2.8V */
+ }, {
+ .supply = "a_sensor", /* analog power */
+ }, {
+ .supply = "dig_12", /* digital power 3, 1.2V */
+ },
+};
+
+static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
+ [M5MOLS_RESTYPE_MONITOR] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ [M5MOLS_RESTYPE_CAPTURE] = {
+ .width = 1920,
+ .height = 1080,
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt)
+
+static const struct m5mols_resolution m5mols_reg_res[] = {
+ { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */
+ { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */
+ { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */
+ { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
+ { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */
+ { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */
+ { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */
+ { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */
+ { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */
+ { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
+ { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */
+ { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */
+ { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
+ { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */
+ { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */
+ { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */
+ { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */
+ { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */
+ { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */
+
+ { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */
+ { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */
+ { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
+ { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */
+ { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */
+ { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */
+ { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */
+ { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */
+ { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */
+ { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */
+ { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */
+ { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
+ { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */
+ { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
+ { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */
+ { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */
+ { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
+ { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */
+};
+
+/**
+ * m5mols_swap_byte - an byte array to integer conversion function
+ * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
+ *
+ * Convert I2C data byte array with performing any required byte
+ * reordering to assure proper values for each data type, regardless
+ * of the architecture endianness.
+ */
+static u32 m5mols_swap_byte(u8 *data, u8 length)
+{
+ if (length == 1)
+ return *data;
+ else if (length == 2)
+ return be16_to_cpu(*((u16 *)data));
+ else
+ return be32_to_cpu(*((u32 *)data));
+}
+
+/**
+ * m5mols_read - I2C read function
+ * @reg: combination of size, category and command for the I2C packet
+ * @size: desired size of I2C packet
+ * @val: read value
+ *
+ * Returns 0 on success, or else negative errno.
+ */
+static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ struct i2c_msg msg[2];
+ u8 wbuf[5];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 5;
+ msg[0].buf = wbuf;
+ wbuf[0] = 5;
+ wbuf[1] = M5MOLS_BYTE_READ;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+ wbuf[4] = size;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = size + 1;
+ msg[1].buf = rbuf;
+
+ /* minimum stabilization time */
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+
+ if (ret == 2) {
+ *val = m5mols_swap_byte(&rbuf[1], size);
+ return 0;
+ }
+
+ if (info->isp_ready)
+ v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
+ size, category, cmd, ret);
+
+ return ret < 0 ? ret : -EIO;
+}
+
+int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
+{
+ u32 val_32;
+ int ret;
+
+ if (I2C_SIZE(reg) != 1) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+ if (ret)
+ return ret;
+
+ *val = (u8)val_32;
+ return ret;
+}
+
+int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
+{
+ u32 val_32;
+ int ret;
+
+ if (I2C_SIZE(reg) != 2) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
+ if (ret)
+ return ret;
+
+ *val = (u16)val_32;
+ return ret;
+}
+
+int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
+{
+ if (I2C_SIZE(reg) != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ return m5mols_read(sd, I2C_SIZE(reg), reg, val);
+}
+
+/**
+ * m5mols_write - I2C command write function
+ * @reg: combination of size, category and command for the I2C packet
+ * @val: value to write
+ *
+ * Returns 0 on success, or else negative errno.
+ */
+int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
+ u8 category = I2C_CATEGORY(reg);
+ u8 cmd = I2C_COMMAND(reg);
+ u8 size = I2C_SIZE(reg);
+ u32 *buf = (u32 *)&wbuf[4];
+ struct i2c_msg msg[1];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ if (size != 1 && size != 2 && size != 4) {
+ v4l2_err(sd, "Wrong data size\n");
+ return -EINVAL;
+ }
+
+ msg->addr = client->addr;
+ msg->flags = 0;
+ msg->len = (u16)size + 4;
+ msg->buf = wbuf;
+ wbuf[0] = size + 4;
+ wbuf[1] = M5MOLS_BYTE_WRITE;
+ wbuf[2] = category;
+ wbuf[3] = cmd;
+
+ *buf = m5mols_swap_byte((u8 *)&val, size);
+
+ usleep_range(200, 200);
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret == 1)
+ return 0;
+
+ if (info->isp_ready)
+ v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n",
+ category, cmd, ret);
+
+ return ret < 0 ? ret : -EIO;
+}
+
+/**
+ * m5mols_busy_wait - Busy waiting with I2C register polling
+ * @reg: the I2C_REG() address of an 8-bit status register to check
+ * @value: expected status register value
+ * @mask: bit mask for the read status register value
+ * @timeout: timeout in miliseconds, or -1 for default timeout
+ *
+ * The @reg register value is ORed with @mask before comparing with @value.
+ *
+ * Return: 0 if the requested condition became true within less than
+ * @timeout ms, or else negative errno.
+ */
+int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
+ int timeout)
+{
+ int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout;
+ unsigned long end = jiffies + msecs_to_jiffies(ms);
+ u8 status;
+
+ do {
+ int ret = m5mols_read_u8(sd, reg, &status);
+
+ if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL))
+ return ret;
+ if (!ret && (status & mask & 0xff) == (value & 0xff))
+ return 0;
+ usleep_range(100, 250);
+ } while (ms > 0 && time_is_after_jiffies(end));
+
+ return -EBUSY;
+}
+
+/**
+ * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
+ *
+ * Before writing desired interrupt value the INT_FACTOR register should
+ * be read to clear pending interrupts.
+ */
+int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u8 mask = is_available_af(info) ? REG_INT_AF : 0;
+ u8 dummy;
+ int ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
+ if (!ret)
+ ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
+ return ret;
+}
+
+int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ int ret = wait_event_interruptible_timeout(info->irq_waitq,
+ atomic_add_unless(&info->irq_done, -1, 0),
+ msecs_to_jiffies(timeout));
+ if (ret <= 0)
+ return ret ? ret : -ETIMEDOUT;
+
+ return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask,
+ M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1);
+}
+
+/**
+ * m5mols_reg_mode - Write the mode and check busy status
+ *
+ * It always accompanies a little delay changing the M-5MOLS mode, so it is
+ * needed checking current busy status to guarantee right mode.
+ */
+static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
+{
+ int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
+ if (ret < 0)
+ return ret;
+ return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff,
+ M5MOLS_MODE_CHANGE_TIMEOUT);
+}
+
+/**
+ * m5mols_set_mode - set the M-5MOLS controller mode
+ * @mode: the required operation mode
+ *
+ * The commands of M-5MOLS are grouped into specific modes. Each functionality
+ * can be guaranteed only when the sensor is operating in mode which a command
+ * belongs to.
+ */
+int m5mols_set_mode(struct m5mols_info *info, u8 mode)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = -EINVAL;
+ u8 reg;
+
+ if (mode < REG_PARAMETER || mode > REG_CAPTURE)
+ return ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
+ if (ret || reg == mode)
+ return ret;
+
+ switch (reg) {
+ case REG_PARAMETER:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_MONITOR:
+ if (mode == REG_PARAMETER) {
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+ }
+
+ ret = m5mols_reg_mode(sd, REG_CAPTURE);
+ break;
+
+ case REG_CAPTURE:
+ ret = m5mols_reg_mode(sd, REG_MONITOR);
+ if (mode == REG_MONITOR)
+ break;
+ if (!ret)
+ ret = m5mols_reg_mode(sd, REG_PARAMETER);
+ break;
+
+ default:
+ v4l2_warn(sd, "Wrong mode: %d\n", mode);
+ }
+
+ if (!ret)
+ info->mode = mode;
+
+ return ret;
+}
+
+/**
+ * m5mols_get_version - retrieve full revisions information of M-5MOLS
+ *
+ * The version information includes revisions of hardware and firmware,
+ * AutoFocus alghorithm version and the version string.
+ */
+static int m5mols_get_version(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct m5mols_version *ver = &info->ver;
+ u8 *str = ver->str;
+ int i;
+ int ret;
+
+ ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
+ if (!ret)
+ ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
+ if (!ret)
+ ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
+ if (!ret)
+ ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < VERSION_STRING_SIZE; i++) {
+ ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
+ if (ret)
+ return ret;
+ }
+
+ ver->fw = be16_to_cpu(ver->fw);
+ ver->hw = be16_to_cpu(ver->hw);
+ ver->param = be16_to_cpu(ver->param);
+ ver->awb = be16_to_cpu(ver->awb);
+
+ v4l2_info(sd, "Manufacturer\t[%s]\n",
+ is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
+ "Samsung Electro-Machanics" :
+ is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
+ "Samsung Fiber-Optics" :
+ is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
+ "Samsung Techwin" : "None");
+ v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
+ info->ver.customer, info->ver.project);
+
+ if (!is_available_af(info))
+ v4l2_info(sd, "No support Auto Focus on this firmware\n");
+
+ return ret;
+}
+
+/**
+ * __find_restype - Lookup M-5MOLS resolution type according to pixel code
+ * @code: pixel code
+ */
+static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
+{
+ enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
+
+ do {
+ if (code == m5mols_default_ffmt[type].code)
+ return type;
+ } while (type++ != SIZE_DEFAULT_FFMT);
+
+ return 0;
+}
+
+/**
+ * __find_resolution - Lookup preset and type of M-5MOLS's resolution
+ * @mf: pixel format to find/negotiate the resolution preset for
+ * @type: M-5MOLS resolution type
+ * @resolution: M-5MOLS resolution preset register value
+ *
+ * Find nearest resolution matching resolution preset and adjust mf
+ * to supported values.
+ */
+static int __find_resolution(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf,
+ enum m5mols_restype *type,
+ u32 *resolution)
+{
+ const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
+ const struct m5mols_resolution *match = NULL;
+ enum m5mols_restype stype = __find_restype(mf->code);
+ int i = ARRAY_SIZE(m5mols_reg_res);
+ unsigned int min_err = ~0;
+
+ while (i--) {
+ int err;
+ if (stype == fsize->type) {
+ 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;
+ *resolution = match->reg;
+ *type = stype;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
+ struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which,
+ enum m5mols_restype type)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
+
+ return &info->ffmt[type];
+}
+
+static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format;
+ int ret = 0;
+
+ mutex_lock(&info->lock);
+
+ format = __find_format(info, fh, fmt->which, info->res_type);
+ if (!format)
+ fmt->format = *format;
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ struct v4l2_mbus_framefmt *format = &fmt->format;
+ struct v4l2_mbus_framefmt *sfmt;
+ enum m5mols_restype type;
+ u32 resolution = 0;
+ int ret;
+
+ ret = __find_resolution(sd, format, &type, &resolution);
+ if (ret < 0)
+ return ret;
+
+ sfmt = __find_format(info, fh, fmt->which, type);
+ if (!sfmt)
+ return 0;
+
+ mutex_lock(&info->lock);
+
+ format->code = m5mols_default_ffmt[type].code;
+ format->colorspace = V4L2_COLORSPACE_JPEG;
+ format->field = V4L2_FIELD_NONE;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ *sfmt = *format;
+ info->resolution = resolution;
+ info->res_type = type;
+ }
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (!code || code->index >= SIZE_DEFAULT_FFMT)
+ return -EINVAL;
+
+ code->code = m5mols_default_ffmt[code->index].code;
+
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
+ .enum_mbus_code = m5mols_enum_mbus_code,
+ .get_fmt = m5mols_get_fmt,
+ .set_fmt = m5mols_set_fmt,
+};
+
+/**
+ * m5mols_restore_controls - Apply current control values to the registers
+ *
+ * m5mols_do_scenemode() handles all parameters for which there is yet no
+ * individual control. It should be replaced at some point by setting each
+ * control individually, in required register set up order.
+ */
+int m5mols_restore_controls(struct m5mols_info *info)
+{
+ int ret;
+
+ if (info->ctrl_sync)
+ return 0;
+
+ ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
+ if (ret)
+ return ret;
+
+ ret = v4l2_ctrl_handler_setup(&info->handle);
+ info->ctrl_sync = !ret;
+
+ return ret;
+}
+
+/**
+ * m5mols_start_monitor - Start the monitor mode
+ *
+ * Before applying the controls setup the resolution and frame rate
+ * in PARAMETER mode, and then switch over to MONITOR mode.
+ */
+static int m5mols_start_monitor(struct m5mols_info *info)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret;
+
+ ret = m5mols_set_mode(info, REG_PARAMETER);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
+ if (!ret)
+ ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
+ if (!ret)
+ ret = m5mols_set_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_restore_controls(info);
+
+ return ret;
+}
+
+static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u32 code;
+ int ret;
+
+ mutex_lock(&info->lock);
+ code = info->ffmt[info->res_type].code;
+
+ if (enable) {
+ if (is_code(code, M5MOLS_RESTYPE_MONITOR))
+ ret = m5mols_start_monitor(info);
+ if (is_code(code, M5MOLS_RESTYPE_CAPTURE))
+ ret = m5mols_start_capture(info);
+ else
+ ret = -EINVAL;
+ } else {
+ ret = m5mols_set_mode(info, REG_PARAMETER);
+ }
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops m5mols_video_ops = {
+ .s_stream = m5mols_s_stream,
+};
+
+static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct m5mols_platform_data *pdata = info->pdata;
+ int ret;
+
+ if (info->power == enable)
+ return 0;
+
+ if (enable) {
+ if (info->set_power) {
+ ret = info->set_power(&client->dev, 1);
+ if (ret)
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ info->set_power(&client->dev, 0);
+ return ret;
+ }
+
+ gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
+ info->power = 1;
+
+ return ret;
+ }
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ return ret;
+
+ if (info->set_power)
+ info->set_power(&client->dev, 0);
+
+ gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
+
+ info->isp_ready = 0;
+ info->power = 0;
+
+ return ret;
+}
+
+/* m5mols_update_fw - optional firmware update routine */
+int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
+ int (*set_power)(struct m5mols_info *, bool))
+{
+ return 0;
+}
+
+/**
+ * m5mols_fw_start - M-5MOLS internal ARM controller initialization
+ *
+ * Execute the M-5MOLS internal ARM controller initialization sequence.
+ * This function should be called after the supply voltage has been
+ * applied and before any requests to the device are made.
+ */
+static int m5mols_fw_start(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ atomic_set(&info->irq_done, 0);
+ /* Wait until I2C slave is initialized in Flash Writer mode */
+ ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE,
+ M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1);
+ if (!ret)
+ ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
+ if (!ret)
+ ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000);
+ if (ret < 0)
+ return ret;
+
+ info->isp_ready = 1;
+
+ ret = m5mols_get_version(sd);
+ if (!ret)
+ ret = m5mols_update_fw(sd, m5mols_sensor_power);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
+
+ ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
+ if (!ret)
+ ret = m5mols_enable_interrupt(sd,
+ REG_INT_AF | REG_INT_CAPTURE);
+
+ return ret;
+}
+
+/* Execute the lens soft-landing algorithm */
+static int m5mols_auto_focus_stop(struct m5mols_info *info)
+{
+ int ret;
+
+ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+ if (!ret)
+ ret = m5mols_write(&info->sd, AF_MODE, REG_AF_POWEROFF);
+ if (!ret)
+ ret = m5mols_busy_wait(&info->sd, SYSTEM_STATUS, REG_AF_IDLE,
+ 0xff, -1);
+ return ret;
+}
+
+/**
+ * m5mols_s_power - Main sensor power control function
+ *
+ * To prevent breaking the lens when the sensor is powered off the Soft-Landing
+ * algorithm is called where available. The Soft-Landing algorithm availability
+ * dependends on the firmware provider.
+ */
+static int m5mols_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ if (on) {
+ ret = m5mols_sensor_power(info, true);
+ if (!ret)
+ ret = m5mols_fw_start(sd);
+ } else {
+ if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
+ ret = m5mols_set_mode(info, REG_MONITOR);
+ if (!ret)
+ ret = m5mols_auto_focus_stop(info);
+ if (ret < 0)
+ v4l2_warn(sd, "Soft landing lens failed\n");
+ }
+ ret = m5mols_sensor_power(info, false);
+
+ info->ctrl_sync = 0;
+ }
+
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int m5mols_log_status(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_ctrl_handler_log_status(&info->handle, sd->name);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops m5mols_core_ops = {
+ .s_power = m5mols_s_power,
+ .log_status = m5mols_log_status,
+};
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ *format = m5mols_default_ffmt[0];
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = {
+ .open = m5mols_open,
+};
+
+static const struct v4l2_subdev_ops m5mols_ops = {
+ .core = &m5mols_core_ops,
+ .pad = &m5mols_pad_ops,
+ .video = &m5mols_video_ops,
+};
+
+static irqreturn_t m5mols_irq_handler(int irq, void *data)
+{
+ struct m5mols_info *info = to_m5mols(data);
+
+ atomic_set(&info->irq_done, 1);
+ wake_up_interruptible(&info->irq_waitq);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit m5mols_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct m5mols_platform_data *pdata = client->dev.platform_data;
+ struct m5mols_info *info;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!gpio_is_valid(pdata->gpio_reset)) {
+ dev_err(&client->dev, "No valid RESET GPIO specified\n");
+ return -EINVAL;
+ }
+
+ if (!client->irq) {
+ dev_err(&client->dev, "Interrupt not assigned\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pdata = pdata;
+ info->set_power = pdata->set_power;
+
+ ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
+ if (ret) {
+ dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
+ goto out_free;
+ }
+ gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
+
+ ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
+ goto out_gpio;
+ }
+
+ sd = &info->sd;
+ v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ sd->internal_ops = &m5mols_subdev_internal_ops;
+ info->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
+ if (ret < 0)
+ goto out_reg;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+ init_waitqueue_head(&info->irq_waitq);
+ mutex_init(&info->lock);
+
+ ret = request_irq(client->irq, m5mols_irq_handler,
+ IRQF_TRIGGER_RISING, MODULE_NAME, sd);
+ if (ret) {
+ dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
+ goto out_me;
+ }
+ info->res_type = M5MOLS_RESTYPE_MONITOR;
+ info->ffmt[0] = m5mols_default_ffmt[0];
+ info->ffmt[1] = m5mols_default_ffmt[1];
+
+ ret = m5mols_sensor_power(info, true);
+ if (ret)
+ goto out_irq;
+
+ ret = m5mols_fw_start(sd);
+ if (!ret)
+ ret = m5mols_init_controls(sd);
+
+ ret = m5mols_sensor_power(info, false);
+ if (!ret)
+ return 0;
+out_irq:
+ free_irq(client->irq, sd);
+out_me:
+ media_entity_cleanup(&sd->entity);
+out_reg:
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+out_gpio:
+ gpio_free(pdata->gpio_reset);
+out_free:
+ kfree(info);
+ return ret;
+}
+
+static int __devexit m5mols_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct m5mols_info *info = to_m5mols(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ free_irq(client->irq, sd);
+
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+ gpio_free(info->pdata->gpio_reset);
+ media_entity_cleanup(&sd->entity);
+ kfree(info);
+ return 0;
+}
+
+static const struct i2c_device_id m5mols_id[] = {
+ { MODULE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, m5mols_id);
+
+static struct i2c_driver m5mols_i2c_driver = {
+ .driver = {
+ .name = MODULE_NAME,
+ },
+ .probe = m5mols_probe,
+ .remove = __devexit_p(m5mols_remove),
+ .id_table = m5mols_id,
+};
+
+module_i2c_driver(m5mols_i2c_driver);
+
+MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
+MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
+MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/i2c/m5mols/m5mols_reg.h
index 14d4be72aeff..14d4be72aeff 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/i2c/m5mols/m5mols_reg.h
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
new file mode 100644
index 000000000000..766305f69a28
--- /dev/null
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -0,0 +1,927 @@
+/*
+ * Programming the mspx4xx sound processor family
+ *
+ * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * what works and what doesn't:
+ *
+ * AM-Mono
+ * Support for Hauppauge cards added (decoding handled by tuner) added by
+ * Frederic Crozat <fcrozat@mail.dotcom.fr>
+ *
+ * FM-Mono
+ * should work. The stereo modes are backward compatible to FM-mono,
+ * therefore FM-Mono should be allways available.
+ *
+ * FM-Stereo (B/G, used in germany)
+ * should work, with autodetect
+ *
+ * FM-Stereo (satellite)
+ * should work, no autodetect (i.e. default is mono, but you can
+ * switch to stereo -- untested)
+ *
+ * NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
+ * should work, with autodetect. Support for NICAM was added by
+ * Pekka Pietikainen <pp@netppl.fi>
+ *
+ * TODO:
+ * - better SAT support
+ *
+ * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
+ * using soundcore instead of OSS
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/msp3400.h>
+#include <media/tvaudio.h>
+#include "msp3400-driver.h"
+
+/* ---------------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");
+MODULE_AUTHOR("Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+/* module parameters */
+static int opmode = OPMODE_AUTO;
+int msp_debug; /* msp_debug output */
+bool msp_once; /* no continuous stereo monitoring */
+bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
+ the autoscan seems work well only with FM... */
+int msp_standard = 1; /* Override auto detect of audio msp_standard,
+ if needed. */
+bool msp_dolby;
+
+int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
+ (msp34xxg only) 0x00a0-0x03c0 */
+
+/* read-only */
+module_param(opmode, int, 0444);
+
+/* read-write */
+module_param_named(once, msp_once, bool, 0644);
+module_param_named(debug, msp_debug, int, 0644);
+module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644);
+module_param_named(standard, msp_standard, int, 0644);
+module_param_named(amsound, msp_amsound, bool, 0644);
+module_param_named(dolby, msp_dolby, bool, 0644);
+
+MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
+MODULE_PARM_DESC(once, "No continuous stereo monitoring");
+MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");
+MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");
+MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");
+MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");
+MODULE_PARM_DESC(dolby, "Activates Dolby processing");
+
+/* ---------------------------------------------------------------------- */
+
+/* control subaddress */
+#define I2C_MSP_CONTROL 0x00
+/* demodulator unit subaddress */
+#define I2C_MSP_DEM 0x10
+/* DSP unit subaddress */
+#define I2C_MSP_DSP 0x12
+
+
+/* ----------------------------------------------------------------------- */
+/* functions for talking to the MSP3400C Sound processor */
+
+int msp_reset(struct i2c_client *client)
+{
+ /* reset and read revision code */
+ static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };
+ static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 };
+ static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e };
+ u8 read[2];
+ struct i2c_msg reset[2] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_IGNORE_NAK,
+ .len = 3,
+ .buf = reset_off
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_IGNORE_NAK,
+ .len = 3,
+ .buf = reset_on
+ },
+ };
+ struct i2c_msg test[2] = {
+ {
+ .addr = client->addr,
+ .len = 3,
+ .buf = write
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 2,
+ .buf = read
+ },
+ };
+
+ v4l_dbg(3, msp_debug, client, "msp_reset\n");
+ if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||
+ i2c_transfer(client->adapter, &reset[1], 1) != 1 ||
+ i2c_transfer(client->adapter, test, 2) != 2) {
+ v4l_err(client, "chip reset failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int msp_read(struct i2c_client *client, int dev, int addr)
+{
+ int err, retval;
+ u8 write[3];
+ u8 read[2];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .len = 3,
+ .buf = write
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 2,
+ .buf = read
+ }
+ };
+
+ write[0] = dev + 1;
+ write[1] = addr >> 8;
+ write[2] = addr & 0xff;
+
+ for (err = 0; err < 3; err++) {
+ if (i2c_transfer(client->adapter, msgs, 2) == 2)
+ break;
+ v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err,
+ dev, addr);
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ }
+ if (err == 3) {
+ v4l_warn(client, "resetting chip, sound will go off.\n");
+ msp_reset(client);
+ return -1;
+ }
+ retval = read[0] << 8 | read[1];
+ v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n",
+ dev, addr, retval);
+ return retval;
+}
+
+int msp_read_dem(struct i2c_client *client, int addr)
+{
+ return msp_read(client, I2C_MSP_DEM, addr);
+}
+
+int msp_read_dsp(struct i2c_client *client, int addr)
+{
+ return msp_read(client, I2C_MSP_DSP, addr);
+}
+
+static int msp_write(struct i2c_client *client, int dev, int addr, int val)
+{
+ int err;
+ u8 buffer[5];
+
+ buffer[0] = dev;
+ buffer[1] = addr >> 8;
+ buffer[2] = addr & 0xff;
+ buffer[3] = val >> 8;
+ buffer[4] = val & 0xff;
+
+ v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n",
+ dev, addr, val);
+ for (err = 0; err < 3; err++) {
+ if (i2c_master_send(client, buffer, 5) == 5)
+ break;
+ v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err,
+ dev, addr);
+ schedule_timeout_interruptible(msecs_to_jiffies(10));
+ }
+ if (err == 3) {
+ v4l_warn(client, "resetting chip, sound will go off.\n");
+ msp_reset(client);
+ return -1;
+ }
+ return 0;
+}
+
+int msp_write_dem(struct i2c_client *client, int addr, int val)
+{
+ return msp_write(client, I2C_MSP_DEM, addr, val);
+}
+
+int msp_write_dsp(struct i2c_client *client, int addr, int val)
+{
+ return msp_write(client, I2C_MSP_DSP, addr, val);
+}
+
+/* ----------------------------------------------------------------------- *
+ * bits 9 8 5 - SCART DSP input Select:
+ * 0 0 0 - SCART 1 to DSP input (reset position)
+ * 0 1 0 - MONO to DSP input
+ * 1 0 0 - SCART 2 to DSP input
+ * 1 1 1 - Mute DSP input
+ *
+ * bits 11 10 6 - SCART 1 Output Select:
+ * 0 0 0 - undefined (reset position)
+ * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS)
+ * 1 0 0 - MONO input to SCART 1 Output
+ * 1 1 0 - SCART 1 DA to SCART 1 Output
+ * 0 0 1 - SCART 2 DA to SCART 1 Output
+ * 0 1 1 - SCART 1 Input to SCART 1 Output
+ * 1 1 1 - Mute SCART 1 Output
+ *
+ * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART):
+ * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position)
+ * 0 1 0 - SCART 1 Input to SCART 2 Output
+ * 1 0 0 - MONO input to SCART 2 Output
+ * 0 0 1 - SCART 2 DA to SCART 2 Output
+ * 0 1 1 - SCART 2 Input to SCART 2 Output
+ * 1 1 0 - Mute SCART 2 Output
+ *
+ * Bits 4 to 0 should be zero.
+ * ----------------------------------------------------------------------- */
+
+static int scarts[3][9] = {
+ /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */
+ /* SCART DSP Input select */
+ { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 },
+ /* SCART1 Output select */
+ { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 },
+ /* SCART2 Output select */
+ { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 },
+};
+
+static char *scart_names[] = {
+ "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute"
+};
+
+void msp_set_scart(struct i2c_client *client, int in, int out)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ state->in_scart = in;
+
+ if (in >= 0 && in <= 7 && out >= 0 && out <= 2) {
+ if (-1 == scarts[out][in + 1])
+ return;
+
+ state->acb &= ~scarts[out][0];
+ state->acb |= scarts[out][in + 1];
+ } else
+ state->acb = 0xf60; /* Mute Input and SCART 1 Output */
+
+ v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",
+ scart_names[in], out, state->acb);
+ msp_write_dsp(client, 0x13, state->acb);
+
+ /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
+ if (state->has_i2s_conf)
+ msp_write_dem(client, 0x40, state->i2s_mode);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void msp_wake_thread(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ if (NULL == state->kthread)
+ return;
+ state->watch_stereo = 0;
+ state->restart = 1;
+ wake_up_interruptible(&state->wq);
+}
+
+int msp_sleep(struct msp_state *state, int timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&state->wq, &wait);
+ if (!kthread_should_stop()) {
+ if (timeout < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ } else {
+ schedule_timeout_interruptible
+ (msecs_to_jiffies(timeout));
+ }
+ }
+
+ remove_wait_queue(&state->wq, &wait);
+ try_to_freeze();
+ return state->restart;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct msp_state *state = ctrl_to_state(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
+ int val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_VOLUME: {
+ /* audio volume cluster */
+ int reallymuted = state->muted->val | state->scan_in_progress;
+
+ if (!reallymuted)
+ val = (val * 0x7f / 65535) << 8;
+
+ v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
+ state->muted->val ? "on" : "off",
+ state->scan_in_progress ? "yes" : "no",
+ state->volume->val);
+
+ msp_write_dsp(client, 0x0000, val);
+ msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_scart2_out_volume)
+ msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0006, val);
+ break;
+ }
+
+ case V4L2_CID_AUDIO_BASS:
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0002, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0031, val);
+ break;
+
+ case V4L2_CID_AUDIO_TREBLE:
+ val = ((val - 32768) * 0x60 / 65535) << 8;
+ msp_write_dsp(client, 0x0003, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0032, val);
+ break;
+
+ case V4L2_CID_AUDIO_LOUDNESS:
+ val = val ? ((5 * 4) << 8) : 0;
+ msp_write_dsp(client, 0x0004, val);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0033, val);
+ break;
+
+ case V4L2_CID_AUDIO_BALANCE:
+ val = (u8)((val / 256) - 128);
+ msp_write_dsp(client, 0x0001, val << 8);
+ if (state->has_headphones)
+ msp_write_dsp(client, 0x0030, val << 8);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void msp_update_volume(struct msp_state *state)
+{
+ /* Force an update of the volume/mute cluster */
+ v4l2_ctrl_lock(state->volume);
+ state->volume->val = state->volume->cur.val;
+ state->muted->val = state->muted->cur.val;
+ msp_s_ctrl(state->volume);
+ v4l2_ctrl_unlock(state->volume);
+}
+
+/* --- v4l2 ioctls --- */
+static int msp_s_radio(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio)
+ return 0;
+ state->radio = 1;
+ v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
+ state->watch_stereo = 0;
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ /* set msp3400 to FM radio mode */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ msp_update_volume(state);
+ break;
+ case OPMODE_AUTODETECT:
+ case OPMODE_AUTOSELECT:
+ /* the thread will do for us */
+ msp_wake_thread(client);
+ break;
+ }
+ return 0;
+}
+
+static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ *id &= state->detected_std;
+
+ v4l_dbg(2, msp_debug, client,
+ "detected standard: %s(0x%08Lx)\n",
+ msp_standard_std_name(state->std), state->detected_std);
+
+ return 0;
+}
+
+static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int update = state->radio || state->v4l2_std != id;
+
+ state->v4l2_std = id;
+ state->radio = 0;
+ if (update)
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int tuner = (input >> 3) & 1;
+ int sc_in = input & 0x7;
+ int sc1_out = output & 0xf;
+ int sc2_out = (output >> 4) & 0xf;
+ u16 val, reg;
+ int i;
+ int extern_input = 1;
+
+ if (state->route_in == input && state->route_out == output)
+ return 0;
+ state->route_in = input;
+ state->route_out = output;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
+ }
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ msp_set_scart(client, sc_in, 0);
+ msp_set_scart(client, sc1_out, 1);
+ msp_set_scart(client, sc2_out, 2);
+ msp_set_audmode(client);
+ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+ val = msp_read_dem(client, reg);
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
+ return 0;
+}
+
+static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (vt->type != V4L2_TUNER_ANALOG_TV)
+ return 0;
+ if (!state->radio) {
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ vt->rxsubchans = state->rxsubchans;
+ }
+ vt->audmode = state->audmode;
+ vt->capability |= V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ return 0;
+}
+
+static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (state->radio) /* TODO: add mono/stereo support for radio */
+ return 0;
+ if (state->audmode == vt->audmode)
+ return 0;
+ state->audmode = vt->audmode;
+ /* only set audmode */
+ msp_set_audmode(client);
+ return 0;
+}
+
+static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
+
+ switch (freq) {
+ case 1024000:
+ state->i2s_mode = 0;
+ break;
+ case 2048000:
+ state->i2s_mode = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident,
+ (state->rev1 << 16) | state->rev2);
+}
+
+static int msp_log_status(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const char *p;
+ char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
+
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
+ client->name, state->rev1, state->rev2);
+ snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
+ v4l2_ctrl_handler_log_status(&state->hdl, prefix);
+ switch (state->mode) {
+ case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
+ case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
+ case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break;
+ case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;
+ case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;
+ case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;
+ case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break;
+ case MSP_MODE_BTSC: p = "BTSC"; break;
+ case MSP_MODE_EXTERN: p = "External input"; break;
+ default: p = "unknown"; break;
+ }
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
+ v4l_info(client, "Mode: %s (%s%s)\n", p,
+ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
+ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
+ } else {
+ if (state->opmode == OPMODE_AUTODETECT)
+ v4l_info(client, "Mode: %s\n", p);
+ v4l_info(client, "Standard: %s (%s%s)\n",
+ msp_standard_std_name(state->std),
+ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
+ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
+ }
+ v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
+ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
+ state->route_in, state->route_out);
+ v4l_info(client, "ACB: 0x%04x\n", state->acb);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int msp_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ v4l_dbg(1, msp_debug, client, "suspend\n");
+ msp_reset(client);
+ return 0;
+}
+
+static int msp_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ v4l_dbg(1, msp_debug, client, "resume\n");
+ msp_wake_thread(client);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops msp_ctrl_ops = {
+ .s_ctrl = msp_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops msp_core_ops = {
+ .log_status = msp_log_status,
+ .g_chip_ident = msp_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,
+ .s_std = msp_s_std,
+};
+
+static const struct v4l2_subdev_video_ops msp_video_ops = {
+ .querystd = msp_querystd,
+};
+
+static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
+ .s_frequency = msp_s_frequency,
+ .g_tuner = msp_g_tuner,
+ .s_tuner = msp_s_tuner,
+ .s_radio = msp_s_radio,
+};
+
+static const struct v4l2_subdev_audio_ops msp_audio_ops = {
+ .s_routing = msp_s_routing,
+ .s_i2s_clock_freq = msp_s_i2s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops msp_ops = {
+ .core = &msp_core_ops,
+ .video = &msp_video_ops,
+ .tuner = &msp_tuner_ops,
+ .audio = &msp_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct msp_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int (*thread_func)(void *data) = NULL;
+ int msp_hard;
+ int msp_family;
+ int msp_revision;
+ int msp_product, msp_prod_hi, msp_prod_lo;
+ int msp_rom;
+
+ if (!id)
+ strlcpy(client->name, "msp3400", sizeof(client->name));
+
+ if (msp_reset(client) == -1) {
+ v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
+ return -ENODEV;
+ }
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &msp_ops);
+
+ state->v4l2_std = V4L2_STD_NTSC;
+ state->detected_std = V4L2_STD_ALL;
+ state->audmode = V4L2_TUNER_MODE_STEREO;
+ state->input = -1;
+ state->i2s_mode = 0;
+ init_waitqueue_head(&state->wq);
+ /* These are the reset input/output positions */
+ state->route_in = MSP_INPUT_DEFAULT;
+ state->route_out = MSP_OUTPUT_DEFAULT;
+
+ state->rev1 = msp_read_dsp(client, 0x1e);
+ if (state->rev1 != -1)
+ state->rev2 = msp_read_dsp(client, 0x1f);
+ v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n",
+ state->rev1, state->rev2);
+ if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
+ v4l_dbg(1, msp_debug, client,
+ "not an msp3400 (cannot read chip version)\n");
+ kfree(state);
+ return -ENODEV;
+ }
+
+ msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
+ msp_product = (state->rev2 >> 8) & 0xff;
+ msp_prod_hi = msp_product / 10;
+ msp_prod_lo = msp_product % 10;
+ msp_revision = (state->rev1 & 0x0f) + '@';
+ msp_hard = ((state->rev1 >> 8) & 0xff) + '@';
+ msp_rom = state->rev2 & 0x1f;
+ /* Rev B=2, C=3, D=4, G=7 */
+ state->ident = msp_family * 10000 + 4000 + msp_product * 10 +
+ msp_revision - '@';
+
+ /* Has NICAM support: all mspx41x and mspx45x products have NICAM */
+ state->has_nicam =
+ msp_prod_hi == 1 || msp_prod_hi == 5;
+ /* Has radio support: was added with revision G */
+ state->has_radio =
+ msp_revision >= 'G';
+ /* Has headphones output: not for stripped down products */
+ state->has_headphones =
+ msp_prod_lo < 5;
+ /* Has scart2 input: not in stripped down products of the '3' family */
+ state->has_scart2 =
+ msp_family >= 4 || msp_prod_lo < 7;
+ /* Has scart3 input: not in stripped down products of the '3' family */
+ state->has_scart3 =
+ msp_family >= 4 || msp_prod_lo < 5;
+ /* Has scart4 input: not in pre D revisions, not in stripped D revs */
+ state->has_scart4 =
+ msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
+ /* Has scart2 output: not in stripped down products of
+ * the '3' family */
+ state->has_scart2_out =
+ msp_family >= 4 || msp_prod_lo < 5;
+ /* Has scart2 a volume control? Not in pre-D revisions. */
+ state->has_scart2_out_volume =
+ msp_revision > 'C' && state->has_scart2_out;
+ /* Has a configurable i2s out? */
+ state->has_i2s_conf =
+ msp_revision >= 'G' && msp_prod_lo < 7;
+ /* Has subwoofer output: not in pre-D revs and not in stripped down
+ * products */
+ state->has_subwoofer =
+ msp_revision >= 'D' && msp_prod_lo < 5;
+ /* Has soundprocessing (bass/treble/balance/loudness/equalizer):
+ * not in stripped down products */
+ state->has_sound_processing =
+ msp_prod_lo < 7;
+ /* Has Virtual Dolby Surround: only in msp34x1 */
+ state->has_virtual_dolby_surround =
+ msp_revision == 'G' && msp_prod_lo == 1;
+ /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
+ state->has_dolby_pro_logic =
+ msp_revision == 'G' && msp_prod_lo == 2;
+ /* The msp343xG supports BTSC only and cannot do Automatic Standard
+ * Detection. */
+ state->force_btsc =
+ msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
+
+ state->opmode = opmode;
+ if (state->opmode == OPMODE_AUTO) {
+ /* MSP revision G and up have both autodetect and autoselect */
+ if (msp_revision >= 'G')
+ state->opmode = OPMODE_AUTOSELECT;
+ /* MSP revision D and up have autodetect */
+ else if (msp_revision >= 'D')
+ state->opmode = OPMODE_AUTODETECT;
+ else
+ state->opmode = OPMODE_MANUAL;
+ }
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ if (state->has_sound_processing) {
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
+ }
+ state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
+ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
+ state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &state->volume);
+ v4l2_ctrl_handler_setup(hdl);
+
+ /* hello world :-) */
+ v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
+ msp_family, msp_product,
+ msp_revision, msp_hard, msp_rom,
+ client->addr << 1, client->adapter->name);
+ v4l_info(client, "%s ", client->name);
+ if (state->has_nicam && state->has_radio)
+ printk(KERN_CONT "supports nicam and radio, ");
+ else if (state->has_nicam)
+ printk(KERN_CONT "supports nicam, ");
+ else if (state->has_radio)
+ printk(KERN_CONT "supports radio, ");
+ printk(KERN_CONT "mode is ");
+
+ /* version-specific initialization */
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ printk(KERN_CONT "manual");
+ thread_func = msp3400c_thread;
+ break;
+ case OPMODE_AUTODETECT:
+ printk(KERN_CONT "autodetect");
+ thread_func = msp3410d_thread;
+ break;
+ case OPMODE_AUTOSELECT:
+ printk(KERN_CONT "autodetect and autoselect");
+ thread_func = msp34xxg_thread;
+ break;
+ }
+ printk(KERN_CONT "\n");
+
+ /* startup control thread if needed */
+ if (thread_func) {
+ state->kthread = kthread_run(thread_func, client, "msp34xx");
+
+ if (IS_ERR(state->kthread))
+ v4l_warn(client, "kernel_thread() failed\n");
+ msp_wake_thread(client);
+ }
+ return 0;
+}
+
+static int msp_remove(struct i2c_client *client)
+{
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+
+ v4l2_device_unregister_subdev(&state->sd);
+ /* shutdown control thread */
+ if (state->kthread) {
+ state->restart = 1;
+ kthread_stop(state->kthread);
+ }
+ msp_reset(client);
+
+ v4l2_ctrl_handler_free(&state->hdl);
+ kfree(state);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct dev_pm_ops msp3400_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume)
+};
+
+static const struct i2c_device_id msp_id[] = {
+ { "msp3400", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, msp_id);
+
+static struct i2c_driver msp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "msp3400",
+ .pm = &msp3400_pm_ops,
+ },
+ .probe = msp_probe,
+ .remove = msp_remove,
+ .id_table = msp_id,
+};
+
+module_i2c_driver(msp_driver);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h
index fbe5e0715f93..fbe5e0715f93 100644
--- a/drivers/media/video/msp3400-driver.h
+++ b/drivers/media/i2c/msp3400-driver.h
diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c
index f8b51714f2f9..f8b51714f2f9 100644
--- a/drivers/media/video/msp3400-kthreads.c
+++ b/drivers/media/i2c/msp3400-kthreads.c
diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c
new file mode 100644
index 000000000000..f80c1d7ec884
--- /dev/null
+++ b/drivers/media/i2c/mt9m032.c
@@ -0,0 +1,878 @@
+/*
+ * Driver for MT9M032 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2010-2011 Lund Engineering
+ * Contact: Gil Lund <gwlund@lundeng.com>
+ * Author: Martin Hostettler <martin@neutronstar.dyndns.org>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/media-entity.h>
+#include <media/mt9m032.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "aptina-pll.h"
+
+/*
+ * width and height include active boundary and black parts
+ *
+ * column 0- 15 active boundary
+ * column 16-1455 image
+ * column 1456-1471 active boundary
+ * column 1472-1599 black
+ *
+ * row 0- 51 black
+ * row 53- 59 active boundary
+ * row 60-1139 image
+ * row 1140-1147 active boundary
+ * row 1148-1151 black
+ */
+
+#define MT9M032_PIXEL_ARRAY_WIDTH 1600
+#define MT9M032_PIXEL_ARRAY_HEIGHT 1152
+
+#define MT9M032_CHIP_VERSION 0x00
+#define MT9M032_CHIP_VERSION_VALUE 0x1402
+#define MT9M032_ROW_START 0x01
+#define MT9M032_ROW_START_MIN 0
+#define MT9M032_ROW_START_MAX 1152
+#define MT9M032_ROW_START_DEF 60
+#define MT9M032_COLUMN_START 0x02
+#define MT9M032_COLUMN_START_MIN 0
+#define MT9M032_COLUMN_START_MAX 1600
+#define MT9M032_COLUMN_START_DEF 16
+#define MT9M032_ROW_SIZE 0x03
+#define MT9M032_ROW_SIZE_MIN 32
+#define MT9M032_ROW_SIZE_MAX 1152
+#define MT9M032_ROW_SIZE_DEF 1080
+#define MT9M032_COLUMN_SIZE 0x04
+#define MT9M032_COLUMN_SIZE_MIN 32
+#define MT9M032_COLUMN_SIZE_MAX 1600
+#define MT9M032_COLUMN_SIZE_DEF 1440
+#define MT9M032_HBLANK 0x05
+#define MT9M032_VBLANK 0x06
+#define MT9M032_VBLANK_MAX 0x7ff
+#define MT9M032_SHUTTER_WIDTH_HIGH 0x08
+#define MT9M032_SHUTTER_WIDTH_LOW 0x09
+#define MT9M032_SHUTTER_WIDTH_MIN 1
+#define MT9M032_SHUTTER_WIDTH_MAX 1048575
+#define MT9M032_SHUTTER_WIDTH_DEF 1943
+#define MT9M032_PIX_CLK_CTRL 0x0a
+#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000
+#define MT9M032_RESTART 0x0b
+#define MT9M032_RESET 0x0d
+#define MT9M032_PLL_CONFIG1 0x11
+#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f
+#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8
+#define MT9M032_READ_MODE1 0x1e
+#define MT9M032_READ_MODE2 0x20
+#define MT9M032_READ_MODE2_VFLIP_SHIFT 15
+#define MT9M032_READ_MODE2_HFLIP_SHIFT 14
+#define MT9M032_READ_MODE2_ROW_BLC 0x40
+#define MT9M032_GAIN_GREEN1 0x2b
+#define MT9M032_GAIN_BLUE 0x2c
+#define MT9M032_GAIN_RED 0x2d
+#define MT9M032_GAIN_GREEN2 0x2e
+
+/* write only */
+#define MT9M032_GAIN_ALL 0x35
+#define MT9M032_GAIN_DIGITAL_MASK 0x7f
+#define MT9M032_GAIN_DIGITAL_SHIFT 8
+#define MT9M032_GAIN_AMUL_SHIFT 6
+#define MT9M032_GAIN_ANALOG_MASK 0x3f
+#define MT9M032_FORMATTER1 0x9e
+#define MT9M032_FORMATTER2 0x9f
+#define MT9M032_FORMATTER2_DOUT_EN 0x1000
+#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000
+
+/*
+ * The available MT9M032 datasheet is missing documentation for register 0x10
+ * MT9P031 seems to be close enough, so use constants from that datasheet for
+ * now.
+ * But keep the name MT9P031 to remind us, that this isn't really confirmed
+ * for this sensor.
+ */
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG2 0x11
+#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f
+
+struct mt9m032 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct mt9m032_platform_data *pdata;
+
+ unsigned int pix_clock;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+
+ struct mutex lock; /* Protects streaming, format, interval and crop */
+
+ bool streaming;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+ struct v4l2_fract frame_interval;
+};
+
+#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev)
+#define to_dev(sensor) \
+ (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev)
+
+static int mt9m032_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width)
+{
+ unsigned int effective_width;
+ u32 ns;
+
+ effective_width = width + 716; /* empirical value */
+ ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock);
+ dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns);
+ return ns;
+}
+
+static int mt9m032_update_timing(struct mt9m032 *sensor,
+ struct v4l2_fract *interval)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct v4l2_rect *crop = &sensor->crop;
+ unsigned int min_vblank;
+ unsigned int vblank;
+ u32 row_time;
+
+ if (!interval)
+ interval = &sensor->frame_interval;
+
+ row_time = mt9m032_row_time(sensor, crop->width);
+
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+
+ if (vblank > MT9M032_VBLANK_MAX) {
+ /* hardware limits to 11 bit values */
+ interval->denominator = 1000;
+ interval->numerator =
+ div_u64((crop->height + MT9M032_VBLANK_MAX) *
+ (u64)row_time * interval->denominator,
+ 1000000000ULL);
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+ }
+ /* enforce minimal 1.6ms blanking time. */
+ min_vblank = 1600000 / row_time;
+ vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX);
+
+ return mt9m032_write(client, MT9M032_VBLANK, vblank);
+}
+
+static int mt9m032_update_geom_timing(struct mt9m032 *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ ret = mt9m032_write(client, MT9M032_COLUMN_SIZE,
+ sensor->crop.width - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_SIZE,
+ sensor->crop.height - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_COLUMN_START,
+ sensor->crop.left);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_START,
+ sensor->crop.top);
+ if (!ret)
+ ret = mt9m032_update_timing(sensor, NULL);
+ return ret;
+}
+
+static int update_formatter2(struct mt9m032 *sensor, bool streaming)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ u16 reg_val = MT9M032_FORMATTER2_DOUT_EN
+ | 0x0070; /* parts reserved! */
+ /* possibly for changing to 14-bit mode */
+
+ if (streaming)
+ reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */
+
+ return mt9m032_write(client, MT9M032_FORMATTER2, reg_val);
+}
+
+static int mt9m032_setup_pll(struct mt9m032 *sensor)
+{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 8000000,
+ .ext_clock_max = 16500000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 24000000,
+ .out_clock_min = 322000000,
+ .out_clock_max = 693000000,
+ .pix_clock_max = 99000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct mt9m032_platform_data *pdata = sensor->pdata;
+ struct aptina_pll pll;
+ int ret;
+
+ pll.ext_clock = pdata->ext_clock;
+ pll.pix_clock = pdata->pix_clock;
+
+ ret = aptina_pll_calculate(&client->dev, &limits, &pll);
+ if (ret < 0)
+ return ret;
+
+ sensor->pix_clock = pdata->pix_clock;
+
+ ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
+ (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
+ | (pll.p1 - 1));
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ if (!ret) /* more reserved, Continuous, Master Mode */
+ ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
+ if (!ret) /* Set 14-bit mode, select 7 divider */
+ ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_Y8_1X8;
+ return 0;
+}
+
+static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
+ return -EINVAL;
+
+ fse->min_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->max_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->min_height = MT9M032_ROW_SIZE_DEF;
+ fse->max_height = MT9M032_ROW_SIZE_DEF;
+
+ return 0;
+}
+
+/**
+ * __mt9m032_get_pad_crop() - get crop rect
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try crop rect from
+ * @which: select try or active crop rect
+ *
+ * Returns a pointer the current active or fh relative try crop rect
+ */
+static struct v4l2_rect *
+__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->crop;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * __mt9m032_get_pad_format() - get format
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try format from
+ * @which: select try or active format
+ *
+ * Returns a pointer the current active or fh relative try format
+ */
+static struct v4l2_mbus_framefmt *
+__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->format;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Scaling is not supported, the format is thus fixed. */
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+ ret = 0;
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
+ MT9M032_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
+ MT9M032_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
+ MT9M032_COLUMN_SIZE_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
+ MT9M032_ROW_SIZE_MAX);
+
+ rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = __mt9m032_get_pad_format(sensor, fh, crop->which);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ret = mt9m032_update_geom_timing(sensor);
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ memset(fi, 0, sizeof(*fi));
+ fi->interval = sensor->frame_interval;
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Avoid divisions by 0. */
+ if (fi->interval.denominator == 0)
+ fi->interval.denominator = 1;
+
+ ret = mt9m032_update_timing(sensor, &fi->interval);
+ if (!ret)
+ sensor->frame_interval = fi->interval;
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+ ret = update_formatter2(sensor, streaming);
+ if (!ret)
+ sensor->streaming = streaming;
+ mutex_unlock(&sensor->lock);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m032_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int val;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ val = mt9m032_read(client, reg->reg);
+ if (val < 0)
+ return -EIO;
+
+ reg->size = 2;
+ reg->val = val;
+
+ return 0;
+}
+
+static int mt9m032_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ return mt9m032_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT)
+ | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT)
+ | MT9M032_READ_MODE2_ROW_BLC
+ | 0x0007;
+
+ return mt9m032_write(client, MT9M032_READ_MODE2, reg_val);
+}
+
+static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int digital_gain_val; /* in 1/8th (0..127) */
+ int analog_mul; /* 0 or 1 */
+ int analog_gain_val; /* in 1/16th. (0..63) */
+ u16 reg_val;
+
+ digital_gain_val = 51; /* from setup example */
+
+ if (val < 63) {
+ analog_mul = 0;
+ analog_gain_val = val;
+ } else {
+ analog_mul = 1;
+ analog_gain_val = val / 2;
+ }
+
+ /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */
+ /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
+
+ reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK)
+ << MT9M032_GAIN_DIGITAL_SHIFT)
+ | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT)
+ | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
+
+ return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val);
+}
+
+static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) {
+ /* round because of multiplier used for values >= 63 */
+ ctrl->val &= ~1;
+ }
+
+ return 0;
+}
+
+static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m032 *sensor =
+ container_of(ctrl->handler, struct mt9m032, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ return mt9m032_set_gain(sensor, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ /* case V4L2_CID_VFLIP: -- In the same cluster */
+ return update_read_mode2(sensor, sensor->vflip->val,
+ sensor->hflip->val);
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
+ .s_ctrl = mt9m032_set_ctrl,
+ .try_ctrl = mt9m032_try_ctrl,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops mt9m032_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m032_g_register,
+ .s_register = mt9m032_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops mt9m032_video_ops = {
+ .s_stream = mt9m032_s_stream,
+ .g_frame_interval = mt9m032_get_frame_interval,
+ .s_frame_interval = mt9m032_set_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = {
+ .enum_mbus_code = mt9m032_enum_mbus_code,
+ .enum_frame_size = mt9m032_enum_frame_size,
+ .get_fmt = mt9m032_get_pad_format,
+ .set_fmt = mt9m032_set_pad_format,
+ .set_crop = mt9m032_set_pad_crop,
+ .get_crop = mt9m032_get_pad_crop,
+};
+
+static const struct v4l2_subdev_ops mt9m032_ops = {
+ .core = &mt9m032_core_ops,
+ .video = &mt9m032_video_ops,
+ .pad = &mt9m032_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9m032_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct mt9m032_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *adapter = client->adapter;
+ struct mt9m032 *sensor;
+ int chip_version;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ if (!client->dev.platform_data)
+ return -ENODEV;
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+
+ sensor->pdata = pdata;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
+ if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9M032 not detected, wrong version "
+ "0x%04x\n", chip_version);
+ ret = -ENODEV;
+ goto error_sensor;
+ }
+
+ dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
+ client->addr);
+
+ sensor->frame_interval.numerator = 1;
+ sensor->frame_interval.denominator = 30;
+
+ sensor->crop.left = MT9M032_COLUMN_START_DEF;
+ sensor->crop.top = MT9M032_ROW_START_DEF;
+ sensor->crop.width = MT9M032_COLUMN_SIZE_DEF;
+ sensor->crop.height = MT9M032_ROW_SIZE_DEF;
+
+ sensor->format.width = sensor->crop.width;
+ sensor->format.height = sensor->crop.height;
+ sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
+ sensor->format.field = V4L2_FIELD_NONE;
+ sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 5);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
+ MT9M032_SHUTTER_WIDTH_MAX, 1,
+ MT9M032_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, pdata->pix_clock,
+ pdata->pix_clock, 1, pdata->pix_clock);
+
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "control initialization error %d\n", ret);
+ goto error_ctrl;
+ }
+
+ v4l2_ctrl_cluster(2, &sensor->hflip);
+
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ goto error_ctrl;
+
+ ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, MT9M032_RESET, 0); /* reset off */
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_setup_pll(sensor);
+ if (ret < 0)
+ goto error_entity;
+ usleep_range(10000, 11000);
+
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto error_entity;
+
+ /* SIZE */
+ ret = mt9m032_update_geom_timing(sensor);
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ if (sensor->pdata->invert_pixclock) {
+ ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
+ MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
+ if (ret < 0)
+ goto error_entity;
+ }
+
+ ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = update_formatter2(sensor, false);
+ if (ret < 0)
+ goto error_entity;
+
+ return ret;
+
+error_entity:
+ media_entity_cleanup(&sensor->subdev.entity);
+error_ctrl:
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+error_sensor:
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return ret;
+}
+
+static int mt9m032_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ v4l2_device_unregister_subdev(subdev);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ media_entity_cleanup(&subdev->entity);
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return 0;
+}
+
+static const struct i2c_device_id mt9m032_id_table[] = {
+ { MT9M032_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9m032_id_table);
+
+static struct i2c_driver mt9m032_i2c_driver = {
+ .driver = {
+ .name = MT9M032_NAME,
+ },
+ .probe = mt9m032_probe,
+ .remove = mt9m032_remove,
+ .id_table = mt9m032_id_table,
+};
+
+module_i2c_driver(mt9m032_i2c_driver);
+
+MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>");
+MODULE_DESCRIPTION("MT9M032 camera sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
new file mode 100644
index 000000000000..2c0f4077c491
--- /dev/null
+++ b/drivers/media/i2c/mt9p031.c
@@ -0,0 +1,1071 @@
+/*
+ * Driver for MT9P031 CMOS Image Sensor from Aptina
+ *
+ * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
+ * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on the MT9V032 driver and Bastian Hecht's code.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9p031.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "aptina-pll.h"
+
+#define MT9P031_PIXEL_ARRAY_WIDTH 2752
+#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
+
+#define MT9P031_CHIP_VERSION 0x00
+#define MT9P031_CHIP_VERSION_VALUE 0x1801
+#define MT9P031_ROW_START 0x01
+#define MT9P031_ROW_START_MIN 0
+#define MT9P031_ROW_START_MAX 2004
+#define MT9P031_ROW_START_DEF 54
+#define MT9P031_COLUMN_START 0x02
+#define MT9P031_COLUMN_START_MIN 0
+#define MT9P031_COLUMN_START_MAX 2750
+#define MT9P031_COLUMN_START_DEF 16
+#define MT9P031_WINDOW_HEIGHT 0x03
+#define MT9P031_WINDOW_HEIGHT_MIN 2
+#define MT9P031_WINDOW_HEIGHT_MAX 2006
+#define MT9P031_WINDOW_HEIGHT_DEF 1944
+#define MT9P031_WINDOW_WIDTH 0x04
+#define MT9P031_WINDOW_WIDTH_MIN 2
+#define MT9P031_WINDOW_WIDTH_MAX 2752
+#define MT9P031_WINDOW_WIDTH_DEF 2592
+#define MT9P031_HORIZONTAL_BLANK 0x05
+#define MT9P031_HORIZONTAL_BLANK_MIN 0
+#define MT9P031_HORIZONTAL_BLANK_MAX 4095
+#define MT9P031_VERTICAL_BLANK 0x06
+#define MT9P031_VERTICAL_BLANK_MIN 1
+#define MT9P031_VERTICAL_BLANK_MAX 4096
+#define MT9P031_VERTICAL_BLANK_DEF 26
+#define MT9P031_OUTPUT_CONTROL 0x07
+#define MT9P031_OUTPUT_CONTROL_CEN 2
+#define MT9P031_OUTPUT_CONTROL_SYN 1
+#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
+#define MT9P031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9P031_SHUTTER_WIDTH_LOWER 0x09
+#define MT9P031_SHUTTER_WIDTH_MIN 1
+#define MT9P031_SHUTTER_WIDTH_MAX 1048575
+#define MT9P031_SHUTTER_WIDTH_DEF 1943
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG_1 0x11
+#define MT9P031_PLL_CONFIG_2 0x12
+#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9P031_FRAME_RESTART 0x0b
+#define MT9P031_SHUTTER_DELAY 0x0c
+#define MT9P031_RST 0x0d
+#define MT9P031_RST_ENABLE 1
+#define MT9P031_RST_DISABLE 0
+#define MT9P031_READ_MODE_1 0x1e
+#define MT9P031_READ_MODE_2 0x20
+#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15)
+#define MT9P031_READ_MODE_2_COL_MIR (1 << 14)
+#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6)
+#define MT9P031_ROW_ADDRESS_MODE 0x22
+#define MT9P031_COLUMN_ADDRESS_MODE 0x23
+#define MT9P031_GLOBAL_GAIN 0x35
+#define MT9P031_GLOBAL_GAIN_MIN 8
+#define MT9P031_GLOBAL_GAIN_MAX 1024
+#define MT9P031_GLOBAL_GAIN_DEF 8
+#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
+#define MT9P031_ROW_BLACK_TARGET 0x49
+#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
+#define MT9P031_GREEN1_OFFSET 0x60
+#define MT9P031_GREEN2_OFFSET 0x61
+#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62
+#define MT9P031_BLC_MANUAL_BLC (1 << 0)
+#define MT9P031_RED_OFFSET 0x63
+#define MT9P031_BLUE_OFFSET 0x64
+#define MT9P031_TEST_PATTERN 0xa0
+#define MT9P031_TEST_PATTERN_SHIFT 3
+#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
+#define MT9P031_TEST_PATTERN_DISABLE (0 << 0)
+#define MT9P031_TEST_PATTERN_GREEN 0xa1
+#define MT9P031_TEST_PATTERN_RED 0xa2
+#define MT9P031_TEST_PATTERN_BLUE 0xa3
+
+enum mt9p031_model {
+ MT9P031_MODEL_COLOR,
+ MT9P031_MODEL_MONOCHROME,
+};
+
+struct mt9p031 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_rect crop; /* Sensor window */
+ struct v4l2_mbus_framefmt format;
+ struct mt9p031_platform_data *pdata;
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+
+ enum mt9p031_model model;
+ struct aptina_pll pll;
+ int reset;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *blc_auto;
+ struct v4l2_ctrl *blc_offset;
+
+ /* Registers cache */
+ u16 output_control;
+ u16 mode2;
+};
+
+static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9p031, subdev);
+}
+
+static int mt9p031_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
+ u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->output_control & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->output_control = value;
+ return 0;
+}
+
+static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 value = (mt9p031->mode2 & ~clear) | set;
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
+ if (ret < 0)
+ return ret;
+
+ mt9p031->mode2 = value;
+ return 0;
+}
+
+static int mt9p031_reset(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
+ 0);
+}
+
+static int mt9p031_pll_setup(struct mt9p031 *mt9p031)
+{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 6000000,
+ .ext_clock_max = 27000000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 13500000,
+ .out_clock_min = 180000000,
+ .out_clock_max = 360000000,
+ .pix_clock_max = 96000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct mt9p031_platform_data *pdata = mt9p031->pdata;
+
+ mt9p031->pll.ext_clock = pdata->ext_freq;
+ mt9p031->pll.pix_clock = pdata->target_freq;
+
+ return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
+}
+
+static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
+ (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 2000);
+ ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ return ret;
+}
+
+static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+
+ return mt9p031_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWROFF);
+}
+
+static int mt9p031_power_on(struct mt9p031 *mt9p031)
+{
+ /* Ensure RESET_BAR is low */
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 0);
+ usleep_range(1000, 2000);
+ }
+
+ /* Emable clock */
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev,
+ mt9p031->pdata->ext_freq);
+
+ /* Now RESET_BAR must be high */
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 1);
+ usleep_range(1000, 2000);
+ }
+
+ return 0;
+}
+
+static void mt9p031_power_off(struct mt9p031 *mt9p031)
+{
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 0);
+ usleep_range(1000, 2000);
+ }
+
+ if (mt9p031->pdata->set_xclk)
+ mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
+}
+
+static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ int ret;
+
+ if (!on) {
+ mt9p031_power_off(mt9p031);
+ return 0;
+ }
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_reset(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to reset the camera\n");
+ return ret;
+ }
+
+ return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int mt9p031_set_params(struct mt9p031 *mt9p031)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ struct v4l2_mbus_framefmt *format = &mt9p031->format;
+ const struct v4l2_rect *crop = &mt9p031->crop;
+ unsigned int hblank;
+ unsigned int vblank;
+ unsigned int xskip;
+ unsigned int yskip;
+ unsigned int xbin;
+ unsigned int ybin;
+ int ret;
+
+ /* Windows position and size.
+ *
+ * TODO: Make sure the start coordinates and window size match the
+ * skipping, binning and mirroring (see description of registers 2 and 4
+ * in table 13, and Binning section on page 41).
+ */
+ ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Row and column binning and skipping. Use the maximum binning value
+ * compatible with the skipping settings.
+ */
+ xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
+ yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
+ xbin = 1 << (ffs(xskip) - 1);
+ ybin = 1 << (ffs(yskip) - 1);
+
+ ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ /* Blanking - use minimum value for horizontal blanking and default
+ * value for vertical blanking.
+ */
+ hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3));
+ vblank = MT9P031_VERTICAL_BLANK_DEF;
+
+ ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret;
+
+ if (!enable) {
+ /* Stop sensor readout */
+ ret = mt9p031_set_output_control(mt9p031,
+ MT9P031_OUTPUT_CONTROL_CEN, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_disable(mt9p031);
+ }
+
+ ret = mt9p031_set_params(mt9p031);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ ret = mt9p031_set_output_control(mt9p031, 0,
+ MT9P031_OUTPUT_CONTROL_CEN);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_pll_enable(mt9p031);
+}
+
+static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (code->pad || code->index)
+ return -EINVAL;
+
+ code->code = mt9p031->format.code;
+ return 0;
+}
+
+static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ if (fse->index >= 8 || fse->code != mt9p031->format.code)
+ return -EINVAL;
+
+ fse->min_width = MT9P031_WINDOW_WIDTH_DEF
+ / min_t(unsigned int, 7, fse->index + 1);
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9p031->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9p031_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
+ fmt->which);
+ return 0;
+}
+
+static int mt9p031_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9p031_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9p031_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
+ MT9P031_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
+ MT9P031_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9P031_WINDOW_WIDTH_MIN,
+ MT9P031_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9P031_WINDOW_HEIGHT_MIN,
+ MT9P031_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004)
+#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005)
+
+static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9p031 *mt9p031 =
+ container_of(ctrl->handler, struct mt9p031, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
+ u16 data;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
+ ctrl->val & 0xffff);
+
+ case V4L2_CID_GAIN:
+ /* Gain is controlled by 2 analog stages and a digital stage.
+ * Valid values for the 3 stages are
+ *
+ * Stage Min Max Step
+ * ------------------------------------------
+ * First analog stage x1 x2 1
+ * Second analog stage x1 x4 0.125
+ * Digital stage x1 x16 0.125
+ *
+ * To minimize noise, the gain stages should be used in the
+ * second analog stage, first analog stage, digital stage order.
+ * Gain from a previous stage should be pushed to its maximum
+ * value before the next stage is used.
+ */
+ if (ctrl->val <= 32) {
+ data = ctrl->val;
+ } else if (ctrl->val <= 64) {
+ ctrl->val &= ~1;
+ data = (1 << 6) | (ctrl->val >> 1);
+ } else {
+ ctrl->val &= ~7;
+ data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
+ }
+
+ return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
+
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_COL_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_COL_MIR, 0);
+
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return mt9p031_set_mode2(mt9p031,
+ 0, MT9P031_READ_MODE_2_ROW_MIR);
+ else
+ return mt9p031_set_mode2(mt9p031,
+ MT9P031_READ_MODE_2_ROW_MIR, 0);
+
+ case V4L2_CID_TEST_PATTERN:
+ if (!ctrl->val) {
+ /* Restore the black level compensation settings. */
+ if (mt9p031->blc_auto->cur.val != 0) {
+ ret = mt9p031_s_ctrl(mt9p031->blc_auto);
+ if (ret < 0)
+ return ret;
+ }
+ if (mt9p031->blc_offset->cur.val != 0) {
+ ret = mt9p031_s_ctrl(mt9p031->blc_offset);
+ if (ret < 0)
+ return ret;
+ }
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ MT9P031_TEST_PATTERN_DISABLE);
+ }
+
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
+ if (ret < 0)
+ return ret;
+
+ /* Disable digital black level compensation when using a test
+ * pattern.
+ */
+ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
+ 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_TEST_PATTERN,
+ ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
+ | MT9P031_TEST_PATTERN_ENABLE);
+
+ case V4L2_CID_BLC_AUTO:
+ ret = mt9p031_set_mode2(mt9p031,
+ ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
+ ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
+ ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
+
+ case V4L2_CID_BLC_TARGET_LEVEL:
+ return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
+ ctrl->val);
+
+ case V4L2_CID_BLC_ANALOG_OFFSET:
+ data = ctrl->val & ((1 << 9) - 1);
+
+ ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
+
+ case V4L2_CID_BLC_DIGITAL_OFFSET:
+ return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
+ ctrl->val & ((1 << 12) - 1));
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
+ .s_ctrl = mt9p031_s_ctrl,
+};
+
+static const char * const mt9p031_test_pattern_menu[] = {
+ "Disabled",
+ "Color Field",
+ "Horizontal Gradient",
+ "Vertical Gradient",
+ "Diagonal Gradient",
+ "Classic Test Pattern",
+ "Walking 1s",
+ "Monochrome Horizontal Bars",
+ "Monochrome Vertical Bars",
+ "Vertical Color Bars",
+};
+
+static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
+ {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Test Pattern",
+ .min = 0,
+ .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1,
+ .step = 0,
+ .def = 0,
+ .flags = 0,
+ .menu_skip_mask = 0,
+ .qmenu = mt9p031_test_pattern_menu,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "BLC, Auto",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_TARGET_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Target Level",
+ .min = 0,
+ .max = 4095,
+ .step = 1,
+ .def = 168,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_ANALOG_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Analog Offset",
+ .min = -255,
+ .max = 255,
+ .step = 1,
+ .def = 32,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_DIGITAL_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Digital Offset",
+ .min = -2048,
+ .max = 2047,
+ .step = 1,
+ .def = 40,
+ .flags = 0,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9p031->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9p031->power_count == !on) {
+ ret = __mt9p031_set_power(mt9p031, !!on);
+ if (ret < 0)
+ goto out;
+ }
+
+ /* Update the power count. */
+ mt9p031->power_count += on ? 1 : -1;
+ WARN_ON(mt9p031->power_count < 0);
+
+out:
+ mutex_unlock(&mt9p031->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9p031_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ s32 data;
+ int ret;
+
+ ret = mt9p031_power_on(mt9p031);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9P031 power up failed\n");
+ return ret;
+ }
+
+ /* Read out the chip version register */
+ data = mt9p031_read(client, MT9P031_CHIP_VERSION);
+ if (data != MT9P031_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9P031 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9p031_power_off(mt9p031);
+
+ dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
+ client->addr);
+
+ return ret;
+}
+
+static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9P031_COLUMN_START_DEF;
+ crop->top = MT9P031_ROW_START_DEF;
+ crop->width = MT9P031_WINDOW_WIDTH_DEF;
+ crop->height = MT9P031_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
+ format->code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ format->width = MT9P031_WINDOW_WIDTH_DEF;
+ format->height = MT9P031_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return mt9p031_set_power(subdev, 1);
+}
+
+static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9p031_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
+ .s_power = mt9p031_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
+ .s_stream = mt9p031_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
+ .enum_mbus_code = mt9p031_enum_mbus_code,
+ .enum_frame_size = mt9p031_enum_frame_size,
+ .get_fmt = mt9p031_get_format,
+ .set_fmt = mt9p031_set_format,
+ .get_crop = mt9p031_get_crop,
+ .set_crop = mt9p031_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9p031_subdev_ops = {
+ .core = &mt9p031_subdev_core_ops,
+ .video = &mt9p031_subdev_video_ops,
+ .pad = &mt9p031_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
+ .registered = mt9p031_registered,
+ .open = mt9p031_open,
+ .close = mt9p031_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9p031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9p031_platform_data *pdata = client->dev.platform_data;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mt9p031 *mt9p031;
+ unsigned int i;
+ int ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL);
+ if (mt9p031 == NULL)
+ return -ENOMEM;
+
+ mt9p031->pdata = pdata;
+ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
+ mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
+ mt9p031->model = did->driver_data;
+ mt9p031->reset = -1;
+
+ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5);
+
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
+ MT9P031_SHUTTER_WIDTH_MAX, 1,
+ MT9P031_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
+ MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, pdata->target_freq,
+ pdata->target_freq, 1, pdata->target_freq);
+
+ for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
+
+ mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
+
+ if (mt9p031->ctrls.error) {
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9p031->ctrls.error);
+ ret = mt9p031->ctrls.error;
+ goto done;
+ }
+
+ mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
+ mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
+ V4L2_CID_BLC_DIGITAL_OFFSET);
+
+ mutex_init(&mt9p031->power_lock);
+ v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
+ mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
+
+ mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
+ if (ret < 0)
+ goto done;
+
+ mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
+ mt9p031->crop.top = MT9P031_ROW_START_DEF;
+
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
+ mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
+ else
+ mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
+
+ mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
+ mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
+ mt9p031->format.field = V4L2_FIELD_NONE;
+ mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ if (pdata->reset != -1) {
+ ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW,
+ "mt9p031_rst");
+ if (ret < 0)
+ goto done;
+
+ mt9p031->reset = pdata->reset;
+ }
+
+ ret = mt9p031_pll_setup(mt9p031);
+
+done:
+ if (ret < 0) {
+ if (mt9p031->reset != -1)
+ gpio_free(mt9p031->reset);
+
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ media_entity_cleanup(&mt9p031->subdev.entity);
+ kfree(mt9p031);
+ }
+
+ return ret;
+}
+
+static int mt9p031_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9p031 *mt9p031 = to_mt9p031(subdev);
+
+ v4l2_ctrl_handler_free(&mt9p031->ctrls);
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ if (mt9p031->reset != -1)
+ gpio_free(mt9p031->reset);
+ kfree(mt9p031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9p031_id[] = {
+ { "mt9p031", MT9P031_MODEL_COLOR },
+ { "mt9p031m", MT9P031_MODEL_MONOCHROME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9p031_id);
+
+static struct i2c_driver mt9p031_i2c_driver = {
+ .driver = {
+ .name = "mt9p031",
+ },
+ .probe = mt9p031_probe,
+ .remove = mt9p031_remove,
+ .id_table = mt9p031_id,
+};
+
+module_i2c_driver(mt9p031_i2c_driver);
+
+MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/i2c/mt9t001.c
index 6d343adf891d..6d343adf891d 100644
--- a/drivers/media/video/mt9t001.c
+++ b/drivers/media/i2c/mt9t001.c
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/i2c/mt9v011.c
index 6bf01ad62765..6bf01ad62765 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
new file mode 100644
index 000000000000..e2177405dad2
--- /dev/null
+++ b/drivers/media/i2c/mt9v032.c
@@ -0,0 +1,853 @@
+/*
+ * Driver for MT9V032 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * Based on the MT9M001 driver,
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/mt9v032.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#define MT9V032_PIXEL_ARRAY_HEIGHT 492
+#define MT9V032_PIXEL_ARRAY_WIDTH 782
+
+#define MT9V032_SYSCLK_FREQ_DEF 26600000
+
+#define MT9V032_CHIP_VERSION 0x00
+#define MT9V032_CHIP_ID_REV1 0x1311
+#define MT9V032_CHIP_ID_REV3 0x1313
+#define MT9V032_COLUMN_START 0x01
+#define MT9V032_COLUMN_START_MIN 1
+#define MT9V032_COLUMN_START_DEF 1
+#define MT9V032_COLUMN_START_MAX 752
+#define MT9V032_ROW_START 0x02
+#define MT9V032_ROW_START_MIN 4
+#define MT9V032_ROW_START_DEF 5
+#define MT9V032_ROW_START_MAX 482
+#define MT9V032_WINDOW_HEIGHT 0x03
+#define MT9V032_WINDOW_HEIGHT_MIN 1
+#define MT9V032_WINDOW_HEIGHT_DEF 480
+#define MT9V032_WINDOW_HEIGHT_MAX 480
+#define MT9V032_WINDOW_WIDTH 0x04
+#define MT9V032_WINDOW_WIDTH_MIN 1
+#define MT9V032_WINDOW_WIDTH_DEF 752
+#define MT9V032_WINDOW_WIDTH_MAX 752
+#define MT9V032_HORIZONTAL_BLANKING 0x05
+#define MT9V032_HORIZONTAL_BLANKING_MIN 43
+#define MT9V032_HORIZONTAL_BLANKING_DEF 94
+#define MT9V032_HORIZONTAL_BLANKING_MAX 1023
+#define MT9V032_VERTICAL_BLANKING 0x06
+#define MT9V032_VERTICAL_BLANKING_MIN 4
+#define MT9V032_VERTICAL_BLANKING_DEF 45
+#define MT9V032_VERTICAL_BLANKING_MAX 3000
+#define MT9V032_CHIP_CONTROL 0x07
+#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3)
+#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7)
+#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8)
+#define MT9V032_SHUTTER_WIDTH1 0x08
+#define MT9V032_SHUTTER_WIDTH2 0x09
+#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a
+#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b
+#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1
+#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480
+#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767
+#define MT9V032_RESET 0x0c
+#define MT9V032_READ_MODE 0x0d
+#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0)
+#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0
+#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2)
+#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2
+#define MT9V032_READ_MODE_ROW_FLIP (1 << 4)
+#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5)
+#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6)
+#define MT9V032_READ_MODE_DARK_ROWS (1 << 7)
+#define MT9V032_PIXEL_OPERATION_MODE 0x0f
+#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2)
+#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6)
+#define MT9V032_ANALOG_GAIN 0x35
+#define MT9V032_ANALOG_GAIN_MIN 16
+#define MT9V032_ANALOG_GAIN_DEF 16
+#define MT9V032_ANALOG_GAIN_MAX 64
+#define MT9V032_MAX_ANALOG_GAIN 0x36
+#define MT9V032_MAX_ANALOG_GAIN_MAX 127
+#define MT9V032_FRAME_DARK_AVERAGE 0x42
+#define MT9V032_DARK_AVG_THRESH 0x46
+#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0)
+#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0
+#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8)
+#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8
+#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70
+#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5)
+#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7)
+#define MT9V032_PIXEL_CLOCK 0x74
+#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0)
+#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1)
+#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2)
+#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3)
+#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4)
+#define MT9V032_TEST_PATTERN 0x7f
+#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0)
+#define MT9V032_TEST_PATTERN_DATA_SHIFT 0
+#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10)
+#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11)
+#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11)
+#define MT9V032_TEST_PATTERN_ENABLE (1 << 13)
+#define MT9V032_TEST_PATTERN_FLIP (1 << 14)
+#define MT9V032_AEC_AGC_ENABLE 0xaf
+#define MT9V032_AEC_ENABLE (1 << 0)
+#define MT9V032_AGC_ENABLE (1 << 1)
+#define MT9V032_THERMAL_INFO 0xc1
+
+struct mt9v032 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct {
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ };
+
+ struct mutex power_lock;
+ int power_count;
+
+ struct mt9v032_platform_data *pdata;
+
+ u32 sysclk;
+ u16 chip_control;
+ u16 aec_agc;
+ u16 hblank;
+};
+
+static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9v032, subdev);
+}
+
+static int mt9v032_read(struct i2c_client *client, const u8 reg)
+{
+ s32 data = i2c_smbus_read_word_swapped(client, reg);
+ dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__,
+ data, reg);
+ return data;
+}
+
+static int mt9v032_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__,
+ data, reg);
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u16 value = (mt9v032->chip_control & ~clear) | set;
+ int ret;
+
+ ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value);
+ if (ret < 0)
+ return ret;
+
+ mt9v032->chip_control = value;
+ return 0;
+}
+
+static int
+mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u16 value = mt9v032->aec_agc;
+ int ret;
+
+ if (enable)
+ value |= which;
+ else
+ value &= ~which;
+
+ ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value);
+ if (ret < 0)
+ return ret;
+
+ mt9v032->aec_agc = value;
+ return 0;
+}
+
+static int
+mt9v032_update_hblank(struct mt9v032 *mt9v032)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ struct v4l2_rect *crop = &mt9v032->crop;
+
+ return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING,
+ max_t(s32, mt9v032->hblank, 660 - crop->width));
+}
+
+#define EXT_CLK 25000000
+
+static int mt9v032_power_on(struct mt9v032 *mt9v032)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ int ret;
+
+ if (mt9v032->pdata->set_clock) {
+ mt9v032->pdata->set_clock(&mt9v032->subdev, mt9v032->sysclk);
+ udelay(1);
+ }
+
+ /* Reset the chip and stop data read out */
+ ret = mt9v032_write(client, MT9V032_RESET, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_RESET, 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0);
+}
+
+static void mt9v032_power_off(struct mt9v032 *mt9v032)
+{
+ if (mt9v032->pdata->set_clock)
+ mt9v032->pdata->set_clock(&mt9v032->subdev, 0);
+}
+
+static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ int ret;
+
+ if (!on) {
+ mt9v032_power_off(mt9v032);
+ return 0;
+ }
+
+ ret = mt9v032_power_on(mt9v032);
+ if (ret < 0)
+ return ret;
+
+ /* Configure the pixel clock polarity */
+ if (mt9v032->pdata && mt9v032->pdata->clk_pol) {
+ ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK,
+ MT9V032_PIXEL_CLOCK_INV_PXL_CLK);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Disable the noise correction algorithm and restore the controls. */
+ ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0);
+ if (ret < 0)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(&mt9v032->ctrls);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9v032->format;
+ default:
+ return NULL;
+ }
+}
+
+static struct v4l2_rect *
+__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &mt9v032->crop;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE
+ | MT9V032_CHIP_CONTROL_DOUT_ENABLE
+ | MT9V032_CHIP_CONTROL_SEQUENTIAL;
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *format = &mt9v032->format;
+ struct v4l2_rect *crop = &mt9v032->crop;
+ unsigned int hratio;
+ unsigned int vratio;
+ int ret;
+
+ if (!enable)
+ return mt9v032_set_chip_control(mt9v032, mode, 0);
+
+ /* Configure the window size and row/column bin */
+ hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
+ vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
+
+ ret = mt9v032_write(client, MT9V032_READ_MODE,
+ (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT |
+ (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_ROW_START, crop->top);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9v032_update_hblank(mt9v032);
+ if (ret < 0)
+ return ret;
+
+ /* Switch to master "normal" mode */
+ return mt9v032_set_chip_control(mt9v032, 0, mode);
+}
+
+static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ return 0;
+}
+
+static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
+ return -EINVAL;
+
+ fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index;
+ fse->max_width = fse->min_width;
+ fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int mt9v032_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad,
+ format->which);
+ return 0;
+}
+
+static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032,
+ unsigned int hratio)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ int ret;
+
+ ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate,
+ mt9v032->sysclk / hratio);
+ if (ret < 0)
+ dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret);
+}
+
+static int mt9v032_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *format)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ unsigned int width;
+ unsigned int height;
+ unsigned int hratio;
+ unsigned int vratio;
+
+ __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad,
+ format->which);
+
+ /* Clamp the width and height to avoid dividing by zero. */
+ width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
+ max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN),
+ __crop->width);
+ height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
+ max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN),
+ __crop->height);
+
+ hratio = DIV_ROUND_CLOSEST(__crop->width, width);
+ vratio = DIV_ROUND_CLOSEST(__crop->height, height);
+
+ __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,
+ format->which);
+ __format->width = __crop->width / hratio;
+ __format->height = __crop->height / vratio;
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ mt9v032_configure_pixel_rate(mt9v032, hratio);
+
+ format->format = *__format;
+
+ return 0;
+}
+
+static int mt9v032_get_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad,
+ crop->which);
+ return 0;
+}
+
+static int mt9v032_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ struct v4l2_mbus_framefmt *__format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+
+ /* Clamp the crop rectangle boundaries and align them to a non multiple
+ * of 2 pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
+ MT9V032_COLUMN_START_MIN,
+ MT9V032_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
+ MT9V032_ROW_START_MIN,
+ MT9V032_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2),
+ MT9V032_WINDOW_WIDTH_MIN,
+ MT9V032_WINDOW_WIDTH_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2),
+ MT9V032_WINDOW_HEIGHT_MIN,
+ MT9V032_WINDOW_HEIGHT_MAX);
+
+ rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad,
+ crop->which);
+ __format->width = rect.width;
+ __format->height = rect.height;
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ mt9v032_configure_pixel_rate(mt9v032, 1);
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+
+static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v032 *mt9v032 =
+ container_of(ctrl->handler, struct mt9v032, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
+ u32 freq;
+ u16 data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE,
+ ctrl->val);
+
+ case V4L2_CID_GAIN:
+ return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val);
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE,
+ !ctrl->val);
+
+ case V4L2_CID_EXPOSURE:
+ return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH,
+ ctrl->val);
+
+ case V4L2_CID_HBLANK:
+ mt9v032->hblank = ctrl->val;
+ return mt9v032_update_hblank(mt9v032);
+
+ case V4L2_CID_VBLANK:
+ return mt9v032_write(client, MT9V032_VERTICAL_BLANKING,
+ ctrl->val);
+
+ case V4L2_CID_PIXEL_RATE:
+ case V4L2_CID_LINK_FREQ:
+ if (mt9v032->link_freq == NULL)
+ break;
+
+ freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val];
+ mt9v032->pixel_rate->val64 = freq;
+ mt9v032->sysclk = freq;
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ switch (ctrl->val) {
+ case 0:
+ data = 0;
+ break;
+ case 1:
+ data = MT9V032_TEST_PATTERN_GRAY_VERTICAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ case 2:
+ data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ case 3:
+ data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL
+ | MT9V032_TEST_PATTERN_ENABLE;
+ break;
+ default:
+ data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT)
+ | MT9V032_TEST_PATTERN_USE_DATA
+ | MT9V032_TEST_PATTERN_ENABLE
+ | MT9V032_TEST_PATTERN_FLIP;
+ break;
+ }
+
+ return mt9v032_write(client, MT9V032_TEST_PATTERN, data);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9v032_ctrl_ops = {
+ .s_ctrl = mt9v032_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config mt9v032_ctrls[] = {
+ {
+ .ops = &mt9v032_ctrl_ops,
+ .id = V4L2_CID_TEST_PATTERN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Test pattern",
+ .min = 0,
+ .max = 1023,
+ .step = 1,
+ .def = 0,
+ .flags = 0,
+ }
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int mt9v032_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9v032->power_lock);
+
+ /* If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9v032->power_count == !on) {
+ ret = __mt9v032_set_power(mt9v032, !!on);
+ if (ret < 0)
+ goto done;
+ }
+
+ /* Update the power count. */
+ mt9v032->power_count += on ? 1 : -1;
+ WARN_ON(mt9v032->power_count < 0);
+
+done:
+ mutex_unlock(&mt9v032->power_lock);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev internal operations
+ */
+
+static int mt9v032_registered(struct v4l2_subdev *subdev)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+ s32 data;
+ int ret;
+
+ dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n",
+ client->addr);
+
+ ret = mt9v032_power_on(mt9v032);
+ if (ret < 0) {
+ dev_err(&client->dev, "MT9V032 power up failed\n");
+ return ret;
+ }
+
+ /* Read and check the sensor version */
+ data = mt9v032_read(client, MT9V032_CHIP_VERSION);
+ if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) {
+ dev_err(&client->dev, "MT9V032 not detected, wrong version "
+ "0x%04x\n", data);
+ return -ENODEV;
+ }
+
+ mt9v032_power_off(mt9v032);
+
+ dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n",
+ client->addr);
+
+ mt9v032_configure_pixel_rate(mt9v032, 1);
+
+ return ret;
+}
+
+static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ crop->left = MT9V032_COLUMN_START_DEF;
+ crop->top = MT9V032_ROW_START_DEF;
+ crop->width = MT9V032_WINDOW_WIDTH_DEF;
+ crop->height = MT9V032_WINDOW_HEIGHT_DEF;
+
+ format = v4l2_subdev_get_try_format(fh, 0);
+ format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format->width = MT9V032_WINDOW_WIDTH_DEF;
+ format->height = MT9V032_WINDOW_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return mt9v032_set_power(subdev, 1);
+}
+
+static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
+{
+ return mt9v032_set_power(subdev, 0);
+}
+
+static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = {
+ .s_power = mt9v032_set_power,
+};
+
+static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = {
+ .s_stream = mt9v032_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = {
+ .enum_mbus_code = mt9v032_enum_mbus_code,
+ .enum_frame_size = mt9v032_enum_frame_size,
+ .get_fmt = mt9v032_get_format,
+ .set_fmt = mt9v032_set_format,
+ .get_crop = mt9v032_get_crop,
+ .set_crop = mt9v032_set_crop,
+};
+
+static struct v4l2_subdev_ops mt9v032_subdev_ops = {
+ .core = &mt9v032_subdev_core_ops,
+ .video = &mt9v032_subdev_video_ops,
+ .pad = &mt9v032_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = {
+ .registered = mt9v032_registered,
+ .open = mt9v032_open,
+ .close = mt9v032_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9v032_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9v032_platform_data *pdata = client->dev.platform_data;
+ struct mt9v032 *mt9v032;
+ unsigned int i;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL);
+ if (!mt9v032)
+ return -ENOMEM;
+
+ mutex_init(&mt9v032->power_lock);
+ mt9v032->pdata = pdata;
+
+ v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 8);
+
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN,
+ MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF);
+ v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
+ MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1,
+ MT9V032_TOTAL_SHUTTER_WIDTH_DEF);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_HBLANK, MT9V032_HORIZONTAL_BLANKING_MIN,
+ MT9V032_HORIZONTAL_BLANKING_MAX, 1,
+ MT9V032_HORIZONTAL_BLANKING_DEF);
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_VBLANK, MT9V032_VERTICAL_BLANKING_MIN,
+ MT9V032_VERTICAL_BLANKING_MAX, 1,
+ MT9V032_VERTICAL_BLANKING_DEF);
+
+ mt9v032->pixel_rate =
+ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
+
+ if (pdata && pdata->link_freqs) {
+ unsigned int def = 0;
+
+ for (i = 0; pdata->link_freqs[i]; ++i) {
+ if (pdata->link_freqs[i] == pdata->link_def_freq)
+ def = i;
+ }
+
+ mt9v032->link_freq =
+ v4l2_ctrl_new_int_menu(&mt9v032->ctrls,
+ &mt9v032_ctrl_ops,
+ V4L2_CID_LINK_FREQ, i - 1, def,
+ pdata->link_freqs);
+ v4l2_ctrl_cluster(2, &mt9v032->link_freq);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i)
+ v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL);
+
+ mt9v032->subdev.ctrl_handler = &mt9v032->ctrls;
+
+ if (mt9v032->ctrls.error)
+ printk(KERN_INFO "%s: control initialization error %d\n",
+ __func__, mt9v032->ctrls.error);
+
+ mt9v032->crop.left = MT9V032_COLUMN_START_DEF;
+ mt9v032->crop.top = MT9V032_ROW_START_DEF;
+ mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF;
+ mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF;
+
+ mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;
+ mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;
+ mt9v032->format.field = V4L2_FIELD_NONE;
+ mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE;
+ mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF;
+ mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF;
+
+ v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops);
+ mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops;
+ mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0);
+ if (ret < 0)
+ kfree(mt9v032);
+
+ return ret;
+}
+
+static int mt9v032_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9v032 *mt9v032 = to_mt9v032(subdev);
+
+ v4l2_device_unregister_subdev(subdev);
+ media_entity_cleanup(&subdev->entity);
+ kfree(mt9v032);
+ return 0;
+}
+
+static const struct i2c_device_id mt9v032_id[] = {
+ { "mt9v032", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v032_id);
+
+static struct i2c_driver mt9v032_driver = {
+ .driver = {
+ .name = "mt9v032",
+ },
+ .probe = mt9v032_probe,
+ .remove = mt9v032_remove,
+ .id_table = mt9v032_id,
+};
+
+module_i2c_driver(mt9v032_driver);
+
+MODULE_DESCRIPTION("Aptina MT9V032 Camera driver");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/i2c/noon010pc30.c
index 440c12962bae..440c12962bae 100644
--- a/drivers/media/video/noon010pc30.c
+++ b/drivers/media/i2c/noon010pc30.c
diff --git a/drivers/media/video/ov7670.c b/drivers/media/i2c/ov7670.c
index e7c82b297514..e7c82b297514 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c
new file mode 100644
index 000000000000..49c1b3abb425
--- /dev/null
+++ b/drivers/media/i2c/s5k4ecgx.c
@@ -0,0 +1,1036 @@
+/*
+ * Driver for Samsung S5K4ECGX 1/4" 5Mp CMOS Image Sensor SoC
+ * with an Embedded Image Signal Processor.
+ *
+ * Copyright (C) 2012, Linaro, Sangwook Lee <sangwook.lee@linaro.org>
+ * Copyright (C) 2012, Insignal Co,. Ltd, Homin Lee <suapapa@insignal.co.kr>
+ *
+ * Based on s5k6aa and noon010pc30 driver
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/clk.h>
+#include <linux/crc32.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include <media/media-entity.h>
+#include <media/s5k4ecgx.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);
+
+#define S5K4ECGX_DRIVER_NAME "s5k4ecgx"
+#define S5K4ECGX_FIRMWARE "s5k4ecgx.bin"
+
+/* Firmware revision information */
+#define REG_FW_REVISION 0x700001a6
+#define REG_FW_VERSION 0x700001a4
+#define S5K4ECGX_REVISION_1_1 0x11
+#define S5K4ECGX_FW_VERSION 0x4ec0
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x7000022c
+#define REG_USER_CONTRAST 0x7000022e
+#define REG_USER_SATURATION 0x70000230
+
+#define REG_G_ENABLE_PREV 0x7000023e
+#define REG_G_ENABLE_PREV_CHG 0x70000240
+#define REG_G_NEW_CFG_SYNC 0x7000024a
+#define REG_G_PREV_IN_WIDTH 0x70000250
+#define REG_G_PREV_IN_HEIGHT 0x70000252
+#define REG_G_PREV_IN_XOFFS 0x70000254
+#define REG_G_PREV_IN_YOFFS 0x70000256
+#define REG_G_CAP_IN_WIDTH 0x70000258
+#define REG_G_CAP_IN_HEIGHT 0x7000025a
+#define REG_G_CAP_IN_XOFFS 0x7000025c
+#define REG_G_CAP_IN_YOFFS 0x7000025e
+#define REG_G_INPUTS_CHANGE_REQ 0x70000262
+#define REG_G_ACTIVE_PREV_CFG 0x70000266
+#define REG_G_PREV_CFG_CHG 0x70000268
+#define REG_G_PREV_OPEN_AFTER_CH 0x7000026a
+
+/* Preview context register sets. n = 0...4. */
+#define PREG(n, x) ((n) * 0x30 + (x))
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x700002a6)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x700002a8)
+#define REG_P_FMT(n) PREG(n, 0x700002aa)
+#define REG_P_PVI_MASK(n) PREG(n, 0x700002b4)
+#define REG_P_FR_TIME_TYPE(n) PREG(n, 0x700002be)
+#define FR_TIME_DYNAMIC 0
+#define FR_TIME_FIXED 1
+#define FR_TIME_FIXED_ACCURATE 2
+#define REG_P_FR_TIME_Q_TYPE(n) PREG(n, 0x700002c0)
+#define FR_TIME_Q_DYNAMIC 0
+#define FR_TIME_Q_BEST_FRRATE 1
+#define FR_TIME_Q_BEST_QUALITY 2
+
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x700002c2)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x700002c4)
+#define US_TO_FR_TIME(__t) ((__t) / 100)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x700002d0)
+#define REG_P_CAP_MIRROR(n) PREG(n, 0x700002d2)
+
+#define REG_G_PREVZOOM_IN_WIDTH 0x70000494
+#define REG_G_PREVZOOM_IN_HEIGHT 0x70000496
+#define REG_G_PREVZOOM_IN_XOFFS 0x70000498
+#define REG_G_PREVZOOM_IN_YOFFS 0x7000049a
+#define REG_G_CAPZOOM_IN_WIDTH 0x7000049c
+#define REG_G_CAPZOOM_IN_HEIGHT 0x7000049e
+#define REG_G_CAPZOOM_IN_XOFFS 0x700004a0
+#define REG_G_CAPZOOM_IN_YOFFS 0x700004a2
+
+/* n = 0...4 */
+#define REG_USER_SHARPNESS(n) (0x70000a28 + (n) * 0xb6)
+
+/* Reduce sharpness range for user space API */
+#define SHARPNESS_DIV 8208
+#define TOK_TERM 0xffffffff
+
+/*
+ * FIXME: This is copied from s5k6aa, because of no information
+ * in the S5K4ECGX datasheet.
+ * H/W register Interface (0xd0000000 - 0xd0000fff)
+ */
+#define AHB_MSB_ADDR_PTR 0xfcfc
+#define GEN_REG_OFFSH 0xd000
+#define REG_CMDWR_ADDRH 0x0028
+#define REG_CMDWR_ADDRL 0x002a
+#define REG_CMDRD_ADDRH 0x002c
+#define REG_CMDRD_ADDRL 0x002e
+#define REG_CMDBUF0_ADDR 0x0f12
+
+struct s5k4ecgx_frmsize {
+ struct v4l2_frmsize_discrete size;
+ /* Fixed sensor matrix crop rectangle */
+ struct v4l2_rect input_window;
+};
+
+struct regval_list {
+ u32 addr;
+ u16 val;
+};
+
+/*
+ * TODO: currently only preview is supported and snapshot (capture)
+ * is not implemented yet
+ */
+static const struct s5k4ecgx_frmsize s5k4ecgx_prev_sizes[] = {
+ {
+ .size = { 176, 144 },
+ .input_window = { 0x00, 0x00, 0x928, 0x780 },
+ }, {
+ .size = { 352, 288 },
+ .input_window = { 0x00, 0x00, 0x928, 0x780 },
+ }, {
+ .size = { 640, 480 },
+ .input_window = { 0x00, 0x00, 0xa00, 0x780 },
+ }, {
+ .size = { 720, 480 },
+ .input_window = { 0x00, 0x00, 0xa00, 0x6a8 },
+ }
+};
+
+#define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes)
+
+struct s5k4ecgx_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_TC_PCFG_Format register value */
+ u16 reg_p_format;
+};
+
+/* By default value, output from sensor will be YUV422 0-255 */
+static const struct s5k4ecgx_pixfmt s5k4ecgx_formats[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+};
+
+static const char * const s5k4ecgx_supply_names[] = {
+ /*
+ * Usually 2.8V is used for analog power (vdda)
+ * and digital IO (vddio, vdddcore)
+ */
+ "vdda",
+ "vddio",
+ "vddcore",
+ "vddreg", /* The internal s5k4ecgx regulator's supply (1.8V) */
+};
+
+#define S5K4ECGX_NUM_SUPPLIES ARRAY_SIZE(s5k4ecgx_supply_names)
+
+enum s5k4ecgx_gpio_id {
+ STBY,
+ RST,
+ GPIO_NUM,
+};
+
+struct s5k4ecgx {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler handler;
+
+ struct s5k4ecgx_platform_data *pdata;
+ const struct s5k4ecgx_pixfmt *curr_pixfmt;
+ const struct s5k4ecgx_frmsize *curr_frmsize;
+ struct mutex lock;
+ u8 streaming;
+ u8 set_params;
+
+ struct regulator_bulk_data supplies[S5K4ECGX_NUM_SUPPLIES];
+ struct s5k4ecgx_gpio gpio[GPIO_NUM];
+};
+
+static inline struct s5k4ecgx *to_s5k4ecgx(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k4ecgx, sd);
+}
+
+static int s5k4ecgx_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ u8 wbuf[2] = { addr >> 8, addr & 0xff };
+ struct i2c_msg msg[2];
+ u8 rbuf[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = wbuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rbuf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ *val = be16_to_cpu(*((u16 *)rbuf));
+
+ v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int s5k4ecgx_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+ u8 buf[4] = { addr >> 8, addr & 0xff, val >> 8, val & 0xff };
+
+ int ret = i2c_master_send(client, buf, 4);
+ v4l2_dbg(4, debug, client, "i2c_write: 0x%04x : 0x%04x\n", addr, val);
+
+ return ret == 4 ? 0 : ret;
+}
+
+static int s5k4ecgx_write(struct i2c_client *client, u32 addr, u16 val)
+{
+ u16 high = addr >> 16, low = addr & 0xffff;
+ int ret;
+
+ v4l2_dbg(3, debug, client, "write: 0x%08x : 0x%04x\n", addr, val);
+
+ ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRH, high);
+ if (!ret)
+ ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRL, low);
+ if (!ret)
+ ret = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val);
+
+ return ret;
+}
+
+static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val)
+{
+ u16 high = addr >> 16, low = addr & 0xffff;
+ int ret;
+
+ ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRH, high);
+ if (!ret)
+ ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low);
+ if (!ret)
+ ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val);
+ if (!ret)
+ dev_err(&client->dev, "Failed to execute read command\n");
+
+ return ret;
+}
+
+static int s5k4ecgx_read_fw_ver(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 hw_rev, fw_ver = 0;
+ int ret;
+
+ ret = s5k4ecgx_read(client, REG_FW_VERSION, &fw_ver);
+ if (ret < 0 || fw_ver != S5K4ECGX_FW_VERSION) {
+ v4l2_err(sd, "FW version check failed!\n");
+ return -ENODEV;
+ }
+
+ ret = s5k4ecgx_read(client, REG_FW_REVISION, &hw_rev);
+ if (ret < 0)
+ return ret;
+
+ v4l2_info(sd, "chip found FW ver: 0x%x, HW rev: 0x%x\n",
+ fw_ver, hw_rev);
+ return 0;
+}
+
+static int s5k4ecgx_set_ahb_address(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ /* Set APB peripherals start address */
+ ret = s5k4ecgx_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+ if (ret < 0)
+ return ret;
+ /*
+ * FIXME: This is copied from s5k6aa, because of no information
+ * in s5k4ecgx's datasheet.
+ * sw_reset is activated to put device into idle status
+ */
+ ret = s5k4ecgx_i2c_write(client, 0x0010, 0x0001);
+ if (ret < 0)
+ return ret;
+
+ ret = s5k4ecgx_i2c_write(client, 0x1030, 0x0000);
+ if (ret < 0)
+ return ret;
+ /* Halt ARM CPU */
+ return s5k4ecgx_i2c_write(client, 0x0014, 0x0001);
+}
+
+#define FW_CRC_SIZE 4
+/* Register address, value are 4, 2 bytes */
+#define FW_RECORD_SIZE 6
+/*
+ * The firmware has following format:
+ * < total number of records (4 bytes + 2 bytes padding) N >,
+ * < record 0 >, ..., < record N - 1 >, < CRC32-CCITT (4-bytes) >,
+ * where "record" is a 4-byte register address followed by 2-byte
+ * register value (little endian).
+ * The firmware generator can be found in following git repository:
+ * git://git.linaro.org/people/sangwook/fimc-v4l2-app.git
+ */
+static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const struct firmware *fw;
+ const u8 *ptr;
+ int err, i, regs_num;
+ u32 addr, crc, crc_file, addr_inc = 0;
+ u16 val;
+
+ err = request_firmware(&fw, S5K4ECGX_FIRMWARE, sd->v4l2_dev->dev);
+ if (err) {
+ v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE);
+ return err;
+ }
+ regs_num = le32_to_cpu(get_unaligned_le32(fw->data));
+
+ v4l2_dbg(3, debug, sd, "FW: %s size %d register sets %d\n",
+ S5K4ECGX_FIRMWARE, fw->size, regs_num);
+
+ regs_num++; /* Add header */
+ if (fw->size != regs_num * FW_RECORD_SIZE + FW_CRC_SIZE) {
+ err = -EINVAL;
+ goto fw_out;
+ }
+ crc_file = le32_to_cpu(get_unaligned_le32(fw->data +
+ regs_num * FW_RECORD_SIZE));
+ crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE);
+ if (crc != crc_file) {
+ v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file);
+ err = -EINVAL;
+ goto fw_out;
+ }
+ ptr = fw->data + FW_RECORD_SIZE;
+ for (i = 1; i < regs_num; i++) {
+ addr = le32_to_cpu(get_unaligned_le32(ptr));
+ ptr += sizeof(u32);
+ val = le16_to_cpu(get_unaligned_le16(ptr));
+ ptr += sizeof(u16);
+ if (addr - addr_inc != 2)
+ err = s5k4ecgx_write(client, addr, val);
+ else
+ err = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val);
+ if (err)
+ break;
+ addr_inc = addr;
+ }
+fw_out:
+ release_firmware(fw);
+ return err;
+}
+
+/* Set preview and capture input window */
+static int s5k4ecgx_set_input_window(struct i2c_client *c,
+ const struct v4l2_rect *r)
+{
+ int ret;
+
+ ret = s5k4ecgx_write(c, REG_G_PREV_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREV_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREV_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREV_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAP_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAP_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAP_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAP_IN_YOFFS, r->top);
+
+ return ret;
+}
+
+/* Set preview and capture zoom input window */
+static int s5k4ecgx_set_zoom_window(struct i2c_client *c,
+ const struct v4l2_rect *r)
+{
+ int ret;
+
+ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_YOFFS, r->top);
+
+ return ret;
+}
+
+static int s5k4ecgx_set_output_framefmt(struct s5k4ecgx *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+ int ret;
+
+ ret = s5k4ecgx_write(client, REG_P_OUT_WIDTH(0),
+ priv->curr_frmsize->size.width);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_OUT_HEIGHT(0),
+ priv->curr_frmsize->size.height);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_FMT(0),
+ priv->curr_pixfmt->reg_p_format);
+ return ret;
+}
+
+static int s5k4ecgx_init_sensor(struct v4l2_subdev *sd)
+{
+ int ret;
+
+ ret = s5k4ecgx_set_ahb_address(sd);
+
+ /* The delay is from manufacturer's settings */
+ msleep(100);
+
+ if (!ret)
+ ret = s5k4ecgx_load_firmware(sd);
+ if (ret)
+ v4l2_err(sd, "Failed to write initial settings\n");
+
+ return ret;
+}
+
+static int s5k4ecgx_gpio_set_value(struct s5k4ecgx *priv, int id, u32 val)
+{
+ if (!gpio_is_valid(priv->gpio[id].gpio))
+ return 0;
+ gpio_set_value(priv->gpio[id].gpio, val);
+
+ return 1;
+}
+
+static int __s5k4ecgx_power_on(struct s5k4ecgx *priv)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K4ECGX_NUM_SUPPLIES, priv->supplies);
+ if (ret)
+ return ret;
+ usleep_range(30, 50);
+
+ /* The polarity of STBY is controlled by TSP */
+ if (s5k4ecgx_gpio_set_value(priv, STBY, priv->gpio[STBY].level))
+ usleep_range(30, 50);
+
+ if (s5k4ecgx_gpio_set_value(priv, RST, priv->gpio[RST].level))
+ usleep_range(30, 50);
+
+ return 0;
+}
+
+static int __s5k4ecgx_power_off(struct s5k4ecgx *priv)
+{
+ if (s5k4ecgx_gpio_set_value(priv, RST, !priv->gpio[RST].level))
+ usleep_range(30, 50);
+
+ if (s5k4ecgx_gpio_set_value(priv, STBY, !priv->gpio[STBY].level))
+ usleep_range(30, 50);
+
+ priv->streaming = 0;
+
+ return regulator_bulk_disable(S5K4ECGX_NUM_SUPPLIES, priv->supplies);
+}
+
+/* Find nearest matching image pixel size. */
+static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt *mf,
+ const struct s5k4ecgx_frmsize **size)
+{
+ unsigned int min_err = ~0;
+ int i = ARRAY_SIZE(s5k4ecgx_prev_sizes);
+ const struct s5k4ecgx_frmsize *fsize = &s5k4ecgx_prev_sizes[0],
+ *match = NULL;
+
+ while (i--) {
+ int err = abs(fsize->size.width - mf->width)
+ + abs(fsize->size.height - mf->height);
+ if (err < min_err) {
+ min_err = err;
+ match = fsize;
+ }
+ fsize++;
+ }
+ if (match) {
+ mf->width = match->size.width;
+ mf->height = match->size.height;
+ if (size)
+ *size = match;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k4ecgx_formats))
+ return -EINVAL;
+ code->code = s5k4ecgx_formats[code->index].code;
+
+ return 0;
+}
+
+static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ }
+ return 0;
+ }
+
+ mf = &fmt->format;
+
+ mutex_lock(&priv->lock);
+ mf->width = priv->curr_frmsize->size.width;
+ mf->height = priv->curr_frmsize->size.height;
+ mf->code = priv->curr_pixfmt->code;
+ mf->colorspace = priv->curr_pixfmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mutex_unlock(&priv->lock);
+
+ return 0;
+}
+
+static const struct s5k4ecgx_pixfmt *s5k4ecgx_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i = ARRAY_SIZE(s5k4ecgx_formats);
+
+ while (--i)
+ if (mf->code == s5k4ecgx_formats[i].code)
+ break;
+ mf->code = s5k4ecgx_formats[i].code;
+
+ return &s5k4ecgx_formats[i];
+}
+
+static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+ const struct s5k4ecgx_frmsize *fsize = NULL;
+ const struct s5k4ecgx_pixfmt *pf;
+ struct v4l2_mbus_framefmt *mf;
+ int ret = 0;
+
+ pf = s5k4ecgx_try_fmt(sd, &fmt->format);
+ s5k4ecgx_try_frame_size(&fmt->format, &fsize);
+ fmt->format.colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ if (fh) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ *mf = fmt->format;
+ }
+ return 0;
+ }
+
+ mutex_lock(&priv->lock);
+ if (!priv->streaming) {
+ priv->curr_frmsize = fsize;
+ priv->curr_pixfmt = pf;
+ priv->set_params = 1;
+ } else {
+ ret = -EBUSY;
+ }
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops s5k4ecgx_pad_ops = {
+ .enum_mbus_code = s5k4ecgx_enum_mbus_code,
+ .get_fmt = s5k4ecgx_get_fmt,
+ .set_fmt = s5k4ecgx_set_fmt,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+static int s5k4ecgx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = &container_of(ctrl->handler, struct s5k4ecgx,
+ handler)->sd;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+ unsigned int i;
+ int err = 0;
+
+ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+ mutex_lock(&priv->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ err = s5k4ecgx_write(client, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ err = s5k4ecgx_write(client, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ /* TODO: Revisit, is this setting for all presets ? */
+ for (i = 0; i < 4 && !err; i++)
+ err = s5k4ecgx_write(client, REG_USER_SHARPNESS(i),
+ ctrl->val * SHARPNESS_DIV);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ err = s5k4ecgx_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+ }
+ mutex_unlock(&priv->lock);
+ if (err < 0)
+ v4l2_err(sd, "Failed to write s_ctrl err %d\n", err);
+
+ return err;
+}
+
+static const struct v4l2_ctrl_ops s5k4ecgx_ctrl_ops = {
+ .s_ctrl = s5k4ecgx_s_ctrl,
+};
+
+/*
+ * Reading s5k4ecgx version information
+ */
+static int s5k4ecgx_registered(struct v4l2_subdev *sd)
+{
+ int ret;
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+
+ mutex_lock(&priv->lock);
+ ret = __s5k4ecgx_power_on(priv);
+ if (!ret) {
+ ret = s5k4ecgx_read_fw_ver(sd);
+ __s5k4ecgx_power_off(priv);
+ }
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0);
+
+ mf->width = s5k4ecgx_prev_sizes[0].size.width;
+ mf->height = s5k4ecgx_prev_sizes[0].size.height;
+ mf->code = s5k4ecgx_formats[0].code;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5k4ecgx_subdev_internal_ops = {
+ .registered = s5k4ecgx_registered,
+ .open = s5k4ecgx_open,
+};
+
+static int s5k4ecgx_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "Switching %s\n", on ? "on" : "off");
+
+ if (on) {
+ ret = __s5k4ecgx_power_on(priv);
+ if (ret < 0)
+ return ret;
+ /* Time to stabilize sensor */
+ msleep(100);
+ ret = s5k4ecgx_init_sensor(sd);
+ if (ret < 0)
+ __s5k4ecgx_power_off(priv);
+ else
+ priv->set_params = 1;
+ } else {
+ ret = __s5k4ecgx_power_off(priv);
+ }
+
+ return ret;
+}
+
+static int s5k4ecgx_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops = {
+ .s_power = s5k4ecgx_s_power,
+ .log_status = s5k4ecgx_log_status,
+};
+
+static int __s5k4ecgx_s_params(struct s5k4ecgx *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+ const struct v4l2_rect *crop_rect = &priv->curr_frmsize->input_window;
+ int ret;
+
+ ret = s5k4ecgx_set_input_window(client, crop_rect);
+ if (!ret)
+ ret = s5k4ecgx_set_zoom_window(client, crop_rect);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_G_INPUTS_CHANGE_REQ, 1);
+ if (!ret)
+ ret = s5k4ecgx_write(client, 0x70000a1e, 0x28);
+ if (!ret)
+ ret = s5k4ecgx_write(client, 0x70000ad4, 0x3c);
+ if (!ret)
+ ret = s5k4ecgx_set_output_framefmt(priv);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_PVI_MASK(0), 0x52);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_FR_TIME_TYPE(0),
+ FR_TIME_DYNAMIC);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_FR_TIME_Q_TYPE(0),
+ FR_TIME_Q_BEST_FRRATE);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_MIN_FR_TIME(0),
+ US_TO_FR_TIME(33300));
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_MAX_FR_TIME(0),
+ US_TO_FR_TIME(66600));
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_PREV_MIRROR(0), 0);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_P_CAP_MIRROR(0), 0);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_G_ACTIVE_PREV_CFG, 0);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_G_PREV_OPEN_AFTER_CH, 1);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_G_NEW_CFG_SYNC, 1);
+ if (!ret)
+ ret = s5k4ecgx_write(client, REG_G_PREV_CFG_CHG, 1);
+
+ return ret;
+}
+
+static int __s5k4ecgx_s_stream(struct s5k4ecgx *priv, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd);
+ int ret;
+
+ if (on && priv->set_params) {
+ ret = __s5k4ecgx_s_params(priv);
+ if (ret < 0)
+ return ret;
+ priv->set_params = 0;
+ }
+ /*
+ * This enables/disables preview stream only. Capture requests
+ * are not supported yet.
+ */
+ ret = s5k4ecgx_write(client, REG_G_ENABLE_PREV, on);
+ if (ret < 0)
+ return ret;
+ return s5k4ecgx_write(client, REG_G_ENABLE_PREV_CHG, 1);
+}
+
+static int s5k4ecgx_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "Turn streaming %s\n", on ? "on" : "off");
+
+ mutex_lock(&priv->lock);
+
+ if (priv->streaming == !on) {
+ ret = __s5k4ecgx_s_stream(priv, on);
+ if (!ret)
+ priv->streaming = on & 1;
+ }
+
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops = {
+ .s_stream = s5k4ecgx_s_stream,
+};
+
+static const struct v4l2_subdev_ops s5k4ecgx_ops = {
+ .core = &s5k4ecgx_core_ops,
+ .pad = &s5k4ecgx_pad_ops,
+ .video = &s5k4ecgx_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k4ecgx_config_gpio(int nr, int val, const char *name)
+{
+ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ int ret;
+
+ if (!gpio_is_valid(nr))
+ return 0;
+ ret = gpio_request_one(nr, flags, name);
+ if (!ret)
+ gpio_export(nr, 0);
+
+ return ret;
+}
+
+static void s5k4ecgx_free_gpios(struct s5k4ecgx *priv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->gpio); i++) {
+ if (!gpio_is_valid(priv->gpio[i].gpio))
+ continue;
+ gpio_free(priv->gpio[i].gpio);
+ priv->gpio[i].gpio = -EINVAL;
+ }
+}
+
+static int s5k4ecgx_config_gpios(struct s5k4ecgx *priv,
+ const struct s5k4ecgx_platform_data *pdata)
+{
+ const struct s5k4ecgx_gpio *gpio = &pdata->gpio_stby;
+ int ret;
+
+ priv->gpio[STBY].gpio = -EINVAL;
+ priv->gpio[RST].gpio = -EINVAL;
+
+ ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_STBY");
+
+ if (ret) {
+ s5k4ecgx_free_gpios(priv);
+ return ret;
+ }
+ priv->gpio[STBY] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ gpio = &pdata->gpio_reset;
+
+ ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_RST");
+ if (ret) {
+ s5k4ecgx_free_gpios(priv);
+ return ret;
+ }
+ priv->gpio[RST] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ return 0;
+}
+
+static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx *priv)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k4ecgx_ctrl_ops;
+ struct v4l2_ctrl_handler *hdl = &priv->handler;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(hdl, 4);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -208, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+
+ /* Sharpness default is 24612, and then (24612/SHARPNESS_DIV) = 2 */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -32704/SHARPNESS_DIV,
+ 24612/SHARPNESS_DIV, 1, 2);
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+ priv->sd.ctrl_handler = hdl;
+
+ return 0;
+};
+
+static int s5k4ecgx_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_subdev *sd;
+ struct s5k4ecgx *priv;
+ int ret, i;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "platform data is missing!\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&client->dev, sizeof(struct s5k4ecgx), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+ priv->streaming = 0;
+
+ sd = &priv->sd;
+ /* Registering subdev */
+ v4l2_i2c_subdev_init(sd, client, &s5k4ecgx_ops);
+ strlcpy(sd->name, S5K4ECGX_DRIVER_NAME, sizeof(sd->name));
+
+ sd->internal_ops = &s5k4ecgx_subdev_internal_ops;
+ /* Support v4l2 sub-device user space API */
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ priv->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &priv->pad, 0);
+ if (ret)
+ return ret;
+
+ ret = s5k4ecgx_config_gpios(priv, pdata);
+ if (ret) {
+ dev_err(&client->dev, "Failed to set gpios\n");
+ goto out_err1;
+ }
+ for (i = 0; i < S5K4ECGX_NUM_SUPPLIES; i++)
+ priv->supplies[i].supply = s5k4ecgx_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev, S5K4ECGX_NUM_SUPPLIES,
+ priv->supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators\n");
+ goto out_err2;
+ }
+ ret = s5k4ecgx_init_v4l2_ctrls(priv);
+ if (ret)
+ goto out_err2;
+
+ priv->curr_pixfmt = &s5k4ecgx_formats[0];
+ priv->curr_frmsize = &s5k4ecgx_prev_sizes[0];
+
+ return 0;
+
+out_err2:
+ s5k4ecgx_free_gpios(priv);
+out_err1:
+ media_entity_cleanup(&priv->sd.entity);
+
+ return ret;
+}
+
+static int s5k4ecgx_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k4ecgx *priv = to_s5k4ecgx(sd);
+
+ mutex_destroy(&priv->lock);
+ s5k4ecgx_free_gpios(priv);
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&priv->handler);
+ media_entity_cleanup(&sd->entity);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k4ecgx_id[] = {
+ { S5K4ECGX_DRIVER_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, s5k4ecgx_id);
+
+static struct i2c_driver v4l2_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = S5K4ECGX_DRIVER_NAME,
+ },
+ .probe = s5k4ecgx_probe,
+ .remove = s5k4ecgx_remove,
+ .id_table = s5k4ecgx_id,
+};
+
+module_i2c_driver(v4l2_i2c_driver);
+
+MODULE_DESCRIPTION("Samsung S5K4ECGX 5MP SOC camera");
+MODULE_AUTHOR("Sangwook Lee <sangwook.lee@linaro.org>");
+MODULE_AUTHOR("Seok-Young Jang <quartz.jang@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(S5K4ECGX_FIRMWARE);
diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c
new file mode 100644
index 000000000000..57cd4fa0193d
--- /dev/null
+++ b/drivers/media/i2c/s5k6aa.c
@@ -0,0 +1,1664 @@
+/*
+ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
+ * with embedded SoC ISP.
+ *
+ * Copyright (C) 2011, Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * Based on a driver authored by Dongsoo Nathaniel Kim.
+ * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.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 version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5k6aa.h>
+
+static int debug;
+module_param(debug, int, 0644);
+
+#define DRIVER_NAME "S5K6AA"
+
+/* The token to indicate array termination */
+#define S5K6AA_TERM 0xffff
+#define S5K6AA_OUT_WIDTH_DEF 640
+#define S5K6AA_OUT_HEIGHT_DEF 480
+#define S5K6AA_WIN_WIDTH_MAX 1280
+#define S5K6AA_WIN_HEIGHT_MAX 1024
+#define S5K6AA_WIN_WIDTH_MIN 8
+#define S5K6AA_WIN_HEIGHT_MIN 8
+
+/*
+ * H/W register Interface (0xD0000000 - 0xD0000FFF)
+ */
+#define AHB_MSB_ADDR_PTR 0xfcfc
+#define GEN_REG_OFFSH 0xd000
+#define REG_CMDWR_ADDRH 0x0028
+#define REG_CMDWR_ADDRL 0x002a
+#define REG_CMDRD_ADDRH 0x002c
+#define REG_CMDRD_ADDRL 0x002e
+#define REG_CMDBUF0_ADDR 0x0f12
+#define REG_CMDBUF1_ADDR 0x0f10
+
+/*
+ * Host S/W Register interface (0x70000000 - 0x70002000)
+ * The value of the two most significant address bytes is 0x7000,
+ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
+ */
+#define HOST_SWIF_OFFSH 0x7000
+
+/* Initialization parameters */
+/* Master clock frequency in KHz */
+#define REG_I_INCLK_FREQ_L 0x01b8
+#define REG_I_INCLK_FREQ_H 0x01ba
+#define MIN_MCLK_FREQ_KHZ 6000U
+#define MAX_MCLK_FREQ_KHZ 27000U
+#define REG_I_USE_NPVI_CLOCKS 0x01c6
+#define REG_I_USE_NMIPI_CLOCKS 0x01c8
+
+/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
+#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
+#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
+#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
+#define SYS_PLL_OUT_FREQ (48000000 / 4000)
+#define PCLK_FREQ_MIN (24000000 / 4000)
+#define PCLK_FREQ_MAX (48000000 / 4000)
+#define REG_I_INIT_PARAMS_UPDATED 0x01e0
+#define REG_I_ERROR_INFO 0x01e2
+
+/* General purpose parameters */
+#define REG_USER_BRIGHTNESS 0x01e4
+#define REG_USER_CONTRAST 0x01e6
+#define REG_USER_SATURATION 0x01e8
+#define REG_USER_SHARPBLUR 0x01ea
+
+#define REG_G_SPEC_EFFECTS 0x01ee
+#define REG_G_ENABLE_PREV 0x01f0
+#define REG_G_ENABLE_PREV_CHG 0x01f2
+#define REG_G_NEW_CFG_SYNC 0x01f8
+#define REG_G_PREVZOOM_IN_WIDTH 0x020a
+#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
+#define REG_G_PREVZOOM_IN_XOFFS 0x020e
+#define REG_G_PREVZOOM_IN_YOFFS 0x0210
+#define REG_G_INPUTS_CHANGE_REQ 0x021a
+#define REG_G_ACTIVE_PREV_CFG 0x021c
+#define REG_G_PREV_CFG_CHG 0x021e
+#define REG_G_PREV_OPEN_AFTER_CH 0x0220
+#define REG_G_PREV_CFG_ERROR 0x0222
+
+/* Preview control section. n = 0...4. */
+#define PREG(n, x) ((n) * 0x26 + x)
+#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
+#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
+#define REG_P_FMT(n) PREG(n, 0x0246)
+#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
+#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
+#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
+#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
+#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
+#define FR_RATE_DYNAMIC 0
+#define FR_RATE_FIXED 1
+#define FR_RATE_FIXED_ACCURATE 2
+#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
+#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
+#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
+/* Frame period in 0.1 ms units */
+#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
+#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
+/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
+#define US_TO_FR_TIME(__t) ((__t) / 100)
+#define S5K6AA_MIN_FR_TIME 33300 /* us */
+#define S5K6AA_MAX_FR_TIME 650000 /* us */
+#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */
+/* The below 5 registers are for "device correction" values */
+#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
+#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
+
+/* Extended image property controls */
+/* Exposure time in 10 us units */
+#define REG_SF_USR_EXPOSURE_L 0x03c6
+#define REG_SF_USR_EXPOSURE_H 0x03c8
+#define REG_SF_USR_EXPOSURE_CHG 0x03ca
+#define REG_SF_USR_TOT_GAIN 0x03cc
+#define REG_SF_USR_TOT_GAIN_CHG 0x03ce
+#define REG_SF_RGAIN 0x03d0
+#define REG_SF_RGAIN_CHG 0x03d2
+#define REG_SF_GGAIN 0x03d4
+#define REG_SF_GGAIN_CHG 0x03d6
+#define REG_SF_BGAIN 0x03d8
+#define REG_SF_BGAIN_CHG 0x03da
+#define REG_SF_FLICKER_QUANT 0x03dc
+#define REG_SF_FLICKER_QUANT_CHG 0x03de
+
+/* Output interface (parallel/MIPI) setup */
+#define REG_OIF_EN_MIPI_LANES 0x03fa
+#define REG_OIF_EN_PACKETS 0x03fc
+#define REG_OIF_CFG_CHG 0x03fe
+
+/* Auto-algorithms enable mask */
+#define REG_DBG_AUTOALG_EN 0x0400
+#define AALG_ALL_EN_MASK (1 << 0)
+#define AALG_AE_EN_MASK (1 << 1)
+#define AALG_DIVLEI_EN_MASK (1 << 2)
+#define AALG_WB_EN_MASK (1 << 3)
+#define AALG_FLICKER_EN_MASK (1 << 5)
+#define AALG_FIT_EN_MASK (1 << 6)
+#define AALG_WRHW_EN_MASK (1 << 7)
+
+/* Firmware revision information */
+#define REG_FW_APIVER 0x012e
+#define S5K6AAFX_FW_APIVER 0x0001
+#define REG_FW_REVISION 0x0130
+
+/* For now we use only one user configuration register set */
+#define S5K6AA_MAX_PRESETS 1
+
+static const char * const s5k6aa_supply_names[] = {
+ "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */
+ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
+ "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V)
+ or 2.8V (2.6V to 3.0) */
+ "vddio", /* I/O supply 1.8V (1.65V to 1.95V)
+ or 2.8V (2.5V to 3.1V) */
+};
+#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
+
+enum s5k6aa_gpio_id {
+ STBY,
+ RST,
+ GPIO_NUM,
+};
+
+struct s5k6aa_regval {
+ u16 addr;
+ u16 val;
+};
+
+struct s5k6aa_pixfmt {
+ enum v4l2_mbus_pixelcode code;
+ u32 colorspace;
+ /* REG_P_FMT(x) register value */
+ u16 reg_p_fmt;
+};
+
+struct s5k6aa_preset {
+ /* output pixel format and resolution */
+ struct v4l2_mbus_framefmt mbus_fmt;
+ u8 clk_id;
+ u8 index;
+};
+
+struct s5k6aa_ctrls {
+ struct v4l2_ctrl_handler handler;
+ /* Auto / manual white balance cluster */
+ struct v4l2_ctrl *awb;
+ struct v4l2_ctrl *gain_red;
+ struct v4l2_ctrl *gain_blue;
+ struct v4l2_ctrl *gain_green;
+ /* Mirror cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ /* Auto exposure / manual exposure and gain cluster */
+ struct v4l2_ctrl *auto_exp;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+};
+
+struct s5k6aa_interval {
+ u16 reg_fr_time;
+ struct v4l2_fract interval;
+ /* Maximum rectangle for the interval */
+ struct v4l2_frmsize_discrete size;
+};
+
+struct s5k6aa {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ enum v4l2_mbus_type bus_type;
+ u8 mipi_lanes;
+
+ int (*s_power)(int enable);
+ struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
+ struct s5k6aa_gpio gpio[GPIO_NUM];
+
+ /* external master clock frequency */
+ unsigned long mclk_frequency;
+ /* ISP internal master clock frequency */
+ u16 clk_fop;
+ /* output pixel clock frequency range */
+ u16 pclk_fmin;
+ u16 pclk_fmax;
+
+ unsigned int inv_hflip:1;
+ unsigned int inv_vflip:1;
+
+ /* protects the struct members below */
+ struct mutex lock;
+
+ /* sensor matrix scan window */
+ struct v4l2_rect ccd_rect;
+
+ struct s5k6aa_ctrls ctrls;
+ struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
+ struct s5k6aa_preset *preset;
+ const struct s5k6aa_interval *fiv;
+
+ unsigned int streaming:1;
+ unsigned int apply_cfg:1;
+ unsigned int apply_crop:1;
+ unsigned int power;
+};
+
+static struct s5k6aa_regval s5k6aa_analog_config[] = {
+ /* Analog settings */
+ { 0x112a, 0x0000 }, { 0x1132, 0x0000 },
+ { 0x113e, 0x0000 }, { 0x115c, 0x0000 },
+ { 0x1164, 0x0000 }, { 0x1174, 0x0000 },
+ { 0x1178, 0x0000 }, { 0x077a, 0x0000 },
+ { 0x077c, 0x0000 }, { 0x077e, 0x0000 },
+ { 0x0780, 0x0000 }, { 0x0782, 0x0000 },
+ { 0x0784, 0x0000 }, { 0x0786, 0x0000 },
+ { 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
+ { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
+ { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
+ { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
+ { 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
+ { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
+};
+
+/* TODO: Add RGB888 and Bayer format */
+static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
+ /* range 16-240 */
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
+ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
+};
+
+static const struct s5k6aa_interval s5k6aa_intervals[] = {
+ { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
+ { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */
+ { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */
+ { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */
+ { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */
+};
+
+#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
+
+static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
+}
+
+static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct s5k6aa, sd);
+}
+
+/* Set initial values for all preview presets */
+static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
+{
+ struct s5k6aa_preset *preset = &s5k6aa->presets[0];
+ int i;
+
+ for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
+ preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF;
+ preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF;
+ preset->mbus_fmt.code = s5k6aa_formats[0].code;
+ preset->index = i;
+ preset->clk_id = 0;
+ preset++;
+ }
+
+ s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
+ s5k6aa->preset = &s5k6aa->presets[0];
+}
+
+static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ u8 wbuf[2] = {addr >> 8, addr & 0xFF};
+ struct i2c_msg msg[2];
+ u8 rbuf[2];
+ int ret;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = wbuf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rbuf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ *val = be16_to_cpu(*((u16 *)rbuf));
+
+ v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
+
+ return ret == 2 ? 0 : ret;
+}
+
+static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
+{
+ u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
+
+ int ret = i2c_master_send(client, buf, 4);
+ v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
+
+ return ret == 4 ? 0 : ret;
+}
+
+/* The command register write, assumes Command_Wr_addH = 0x7000. */
+static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
+{
+ int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
+}
+
+/* The command register read, assumes Command_Rd_addH = 0x7000. */
+static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
+{
+ int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
+}
+
+static int s5k6aa_write_array(struct v4l2_subdev *sd,
+ const struct s5k6aa_regval *msg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr_incr = 0;
+ int ret = 0;
+
+ while (msg->addr != S5K6AA_TERM) {
+ if (addr_incr != 2)
+ ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
+ msg->addr);
+ if (ret)
+ break;
+ ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
+ if (ret)
+ break;
+ /* Assume that msg->addr is always less than 0xfffc */
+ addr_incr = (msg + 1)->addr - msg->addr;
+ msg++;
+ }
+
+ return ret;
+}
+
+/* Configure the AHB high address bytes for GTG registers access */
+static int s5k6aa_set_ahb_address(struct i2c_client *client)
+{
+ int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
+ if (ret)
+ return ret;
+ ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
+ if (ret)
+ return ret;
+ return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
+}
+
+/**
+ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
+ *
+ * Configure the internal ISP PLL for the required output frequency.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
+ u16 status;
+ int ret;
+
+ if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
+ "Invalid clock frequency: %ld\n", fmclk))
+ return -EINVAL;
+
+ s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
+ s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
+ s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
+
+ /* External input clock frequency in kHz */
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
+ /* Internal PLL frequency */
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
+ if (!ret)
+ ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
+
+ return ret ? ret : (status ? -EINVAL : 0);
+}
+
+/* Set horizontal and vertical image flipping */
+static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int index = s5k6aa->preset->index;
+
+ unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
+ unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
+
+ return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
+}
+
+/* Configure auto/manual white balance and R/G/B gains */
+static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ u16 reg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
+
+ if (!ret && !awb) {
+ ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
+ }
+ if (!ret) {
+ reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
+ ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
+ }
+
+ return ret;
+}
+
+/* Program FW with exposure time, 'exposure' in us units */
+static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
+{
+ unsigned int time = exposure / 10;
+
+ int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
+}
+
+static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
+{
+ int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
+}
+
+/* Set auto/manual exposure and total gain */
+static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ unsigned int exp_time = s5k6aa->ctrls.exposure->val;
+ u16 auto_alg;
+
+ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
+ exp_time, value, auto_alg);
+
+ if (value == V4L2_EXPOSURE_AUTO) {
+ auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
+ } else {
+ ret = s5k6aa_set_user_exposure(c, exp_time);
+ if (ret)
+ return ret;
+ ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
+ if (ret)
+ return ret;
+ auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
+ }
+
+ return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 auto_alg;
+ int ret;
+
+ ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
+ if (ret)
+ return ret;
+
+ if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
+ auto_alg |= AALG_FLICKER_EN_MASK;
+ } else {
+ auto_alg &= ~AALG_FLICKER_EN_MASK;
+ /* The V4L2_CID_LINE_FREQUENCY control values match
+ * the register values */
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
+ if (ret)
+ return ret;
+ }
+
+ return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
+}
+
+static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ static const struct v4l2_control colorfx[] = {
+ { V4L2_COLORFX_NONE, 0 },
+ { V4L2_COLORFX_BW, 1 },
+ { V4L2_COLORFX_NEGATIVE, 2 },
+ { V4L2_COLORFX_SEPIA, 3 },
+ { V4L2_COLORFX_SKY_BLUE, 4 },
+ { V4L2_COLORFX_SKETCH, 5 },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
+ if (colorfx[i].id == val)
+ return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
+ colorfx[i].value);
+ }
+ return -EINVAL;
+}
+
+static int s5k6aa_preview_config_status(struct i2c_client *client)
+{
+ u16 error = 0;
+ int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
+
+ v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
+ return ret ? ret : (error ? -EINVAL : 0);
+}
+
+static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
+ if (mf->colorspace == s5k6aa_formats[i].colorspace &&
+ mf->code == s5k6aa_formats[i].code)
+ return i;
+ return 0;
+}
+
+static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
+ int ret;
+
+ ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
+ preset->mbus_fmt.width);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
+ preset->mbus_fmt.height);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FMT(preset->index),
+ s5k6aa_formats[fmt_index].reg_p_fmt);
+ return ret;
+}
+
+static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
+ struct v4l2_rect *r = &s5k6aa->ccd_rect;
+ int ret;
+
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
+ if (!ret)
+ ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
+ if (!ret)
+ s5k6aa->apply_crop = 0;
+
+ return ret;
+}
+
+/**
+ * s5k6aa_configure_video_bus - configure the video output interface
+ * @bus_type: video bus type: parallel or MIPI-CSI
+ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
+ *
+ * Note: Only parallel bus operation has been tested.
+ */
+static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
+ enum v4l2_mbus_type bus_type, int nlanes)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 cfg = 0;
+ int ret;
+
+ /*
+ * TODO: The sensor is supposed to support BT.601 and BT.656
+ * but there is nothing indicating how to switch between both
+ * in the datasheet. For now default BT.601 interface is assumed.
+ */
+ if (bus_type == V4L2_MBUS_CSI2)
+ cfg = nlanes;
+ else if (bus_type != V4L2_MBUS_PARALLEL)
+ return -EINVAL;
+
+ ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
+ if (ret)
+ return ret;
+ return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
+}
+
+/* This function should be called when switching to new user configuration set*/
+static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
+ int cid)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+ u16 reg = 1;
+ int ret;
+
+ ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
+ if (timeout == 0)
+ return ret;
+
+ while (ret >= 0 && time_is_after_jiffies(end)) {
+ ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
+ if (!reg)
+ return 0;
+ usleep_range(1000, 5000);
+ }
+ return ret ? ret : -ETIMEDOUT;
+}
+
+/**
+ * s5k6aa_set_prev_config - write user preview register set
+ *
+ * Configure output resolution and color fromat, pixel clock
+ * frequency range, device frame rate type and frame period range.
+ */
+static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
+ struct s5k6aa_preset *preset)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int idx = preset->index;
+ u16 frame_rate_q;
+ int ret;
+
+ if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
+ frame_rate_q = FR_RATE_Q_BEST_FRRATE;
+ else
+ frame_rate_q = FR_RATE_Q_BEST_QUALITY;
+
+ ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
+ s5k6aa->pclk_fmax);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
+ s5k6aa->pclk_fmin);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
+ preset->clk_id);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
+ FR_RATE_DYNAMIC);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
+ frame_rate_q);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time + 33);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
+ s5k6aa->fiv->reg_fr_time - 33);
+ if (!ret)
+ ret = s5k6aa_new_config_sync(client, 250, idx);
+ if (!ret)
+ ret = s5k6aa_preview_config_status(client);
+ if (!ret)
+ s5k6aa->apply_cfg = 0;
+
+ v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
+ s5k6aa->fiv->reg_fr_time, ret);
+ return ret;
+}
+
+/**
+ * s5k6aa_initialize_isp - basic ISP MCU initialization
+ *
+ * Configure AHB addresses for registers read/write; configure PLLs for
+ * required output pixel clock. The ISP power supply needs to be already
+ * enabled, with an optional H/W reset.
+ * Locking: called with s5k6aa.lock mutex held.
+ */
+static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ s5k6aa->apply_crop = 1;
+ s5k6aa->apply_cfg = 1;
+ msleep(100);
+
+ ret = s5k6aa_set_ahb_address(client);
+ if (ret)
+ return ret;
+ ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
+ s5k6aa->mipi_lanes);
+ if (ret)
+ return ret;
+ ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
+ if (ret)
+ return ret;
+ msleep(20);
+
+ return s5k6aa_configure_pixel_clocks(s5k6aa);
+}
+
+static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
+{
+ if (!gpio_is_valid(priv->gpio[id].gpio))
+ return 0;
+ gpio_set_value(priv->gpio[id].gpio, !!val);
+ return 1;
+}
+
+static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
+}
+
+static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
+{
+ return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
+}
+
+static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ if (ret)
+ return ret;
+ if (s5k6aa_gpio_deassert(s5k6aa, STBY))
+ usleep_range(150, 200);
+
+ if (s5k6aa->s_power)
+ ret = s5k6aa->s_power(1);
+ usleep_range(4000, 4000);
+
+ if (s5k6aa_gpio_deassert(s5k6aa, RST))
+ msleep(20);
+
+ return ret;
+}
+
+static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
+{
+ int ret;
+
+ if (s5k6aa_gpio_assert(s5k6aa, RST))
+ usleep_range(100, 150);
+
+ if (s5k6aa->s_power) {
+ ret = s5k6aa->s_power(0);
+ if (ret)
+ return ret;
+ }
+ if (s5k6aa_gpio_assert(s5k6aa, STBY))
+ usleep_range(50, 100);
+ s5k6aa->streaming = 0;
+
+ return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+}
+
+/*
+ * V4L2 subdev core and video operations
+ */
+static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (!on == s5k6aa->power) {
+ if (on) {
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret)
+ ret = s5k6aa_initialize_isp(sd);
+ } else {
+ ret = __s5k6aa_power_off(s5k6aa);
+ }
+
+ if (!ret)
+ s5k6aa->power += on ? 1 : -1;
+ }
+
+ mutex_unlock(&s5k6aa->lock);
+
+ if (!on || ret || s5k6aa->power != 1)
+ return ret;
+
+ return v4l2_ctrl_handler_setup(sd->ctrl_handler);
+}
+
+static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ int ret = 0;
+
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
+ if (!ret)
+ ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
+ if (!ret)
+ s5k6aa->streaming = enable;
+
+ return ret;
+}
+
+static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+
+ if (s5k6aa->streaming == !on) {
+ if (!ret && s5k6aa->apply_cfg)
+ ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
+ if (s5k6aa->apply_crop)
+ ret = s5k6aa_set_input_params(s5k6aa);
+ if (!ret)
+ ret = __s5k6aa_stream(s5k6aa, !!on);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ mutex_lock(&s5k6aa->lock);
+ fi->interval = s5k6aa->fiv->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
+ const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
+ unsigned int err, min_err = UINT_MAX;
+ unsigned int i, fr_time;
+
+ if (fi->interval.denominator == 0)
+ return -EINVAL;
+
+ fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
+ const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
+
+ if (mbus_fmt->width > iv->size.width ||
+ mbus_fmt->height > iv->size.height)
+ continue;
+
+ err = abs(iv->reg_fr_time - fr_time);
+ if (err < min_err) {
+ fiv = iv;
+ min_err = err;
+ }
+ }
+ s5k6aa->fiv = fiv;
+
+ v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
+ fiv->reg_fr_time * 100);
+ return 0;
+}
+
+static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
+ fi->interval.numerator, fi->interval.denominator);
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
+ s5k6aa->apply_cfg = 1;
+
+ mutex_unlock(&s5k6aa->lock);
+ return ret;
+}
+
+/*
+ * V4L2 subdev pad level and video operations
+ */
+static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ const struct s5k6aa_interval *fi;
+ int ret = 0;
+
+ if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
+ return -EINVAL;
+
+ v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &fie->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ mutex_lock(&s5k6aa->lock);
+ fi = &s5k6aa_intervals[fie->index];
+ if (fie->width > fi->size.width || fie->height > fi->size.height)
+ ret = -EINVAL;
+ else
+ fie->interval = fi->interval;
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5k6aa_formats))
+ return -EINVAL;
+
+ code->code = s5k6aa_formats[code->index].code;
+ return 0;
+}
+
+static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int i = ARRAY_SIZE(s5k6aa_formats);
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ while (--i)
+ if (fse->code == s5k6aa_formats[i].code)
+ break;
+
+ fse->code = s5k6aa_formats[i].code;
+ fse->min_width = S5K6AA_WIN_WIDTH_MIN;
+ fse->max_width = S5K6AA_WIN_WIDTH_MAX;
+ fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
+ fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
+
+ return 0;
+}
+
+static struct v4l2_rect *
+__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return &s5k6aa->ccd_rect;
+
+ WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY);
+ return v4l2_subdev_get_try_crop(fh, 0);
+}
+
+static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int index;
+
+ v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &mf->height, S5K6AA_WIN_HEIGHT_MIN,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
+ mf->colorspace != V4L2_COLORSPACE_REC709)
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
+
+ mf->colorspace = s5k6aa_formats[index].colorspace;
+ mf->code = s5k6aa_formats[index].code;
+ mf->field = V4L2_FIELD_NONE;
+}
+
+static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ memset(fmt->reserved, 0, sizeof(fmt->reserved));
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ mutex_lock(&s5k6aa->lock);
+ fmt->format = s5k6aa->preset->mbus_fmt;
+ mutex_unlock(&s5k6aa->lock);
+
+ return 0;
+}
+
+static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct s5k6aa_preset *preset = s5k6aa->preset;
+ struct v4l2_mbus_framefmt *mf;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ mutex_lock(&s5k6aa->lock);
+ s5k6aa_try_format(s5k6aa, &fmt->format);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ crop = v4l2_subdev_get_try_crop(fh, 0);
+ } else {
+ if (s5k6aa->streaming) {
+ ret = -EBUSY;
+ } else {
+ mf = &preset->mbus_fmt;
+ crop = &s5k6aa->ccd_rect;
+ s5k6aa->apply_cfg = 1;
+ }
+ }
+
+ if (ret == 0) {
+ struct v4l2_subdev_frame_interval fiv = {
+ .interval = {0, 1}
+ };
+
+ *mf = fmt->format;
+ /*
+ * Make sure the crop window is valid, i.e. its size is
+ * greater than the output window, as the ISP supports
+ * only down-scaling.
+ */
+ crop->width = clamp_t(unsigned int, crop->width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX);
+ crop->height = clamp_t(unsigned int, crop->height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX);
+ crop->left = clamp_t(unsigned int, crop->left, 0,
+ S5K6AA_WIN_WIDTH_MAX - crop->width);
+ crop->top = clamp_t(unsigned int, crop->top, 0,
+ S5K6AA_WIN_HEIGHT_MAX - crop->height);
+
+ /* Reset to minimum possible frame interval */
+ ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_rect *rect;
+
+ memset(crop->reserved, 0, sizeof(crop->reserved));
+
+ mutex_lock(&s5k6aa->lock);
+ rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+ crop->rect = *rect;
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
+ rect->left, rect->top, rect->width, rect->height);
+
+ return 0;
+}
+
+static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int max_x, max_y;
+ struct v4l2_rect *crop_r;
+
+ mutex_lock(&s5k6aa->lock);
+ crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ mf = &s5k6aa->preset->mbus_fmt;
+ s5k6aa->apply_crop = 1;
+ } else {
+ mf = v4l2_subdev_get_try_format(fh, 0);
+ }
+ v4l_bound_align_image(&crop->rect.width, mf->width,
+ S5K6AA_WIN_WIDTH_MAX, 1,
+ &crop->rect.height, mf->height,
+ S5K6AA_WIN_HEIGHT_MAX, 1, 0);
+
+ max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
+ max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
+
+ crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
+ crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y);
+
+ *crop_r = crop->rect;
+
+ mutex_unlock(&s5k6aa->lock);
+
+ v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
+ crop_r->left, crop_r->top, crop_r->width, crop_r->height);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
+ .enum_mbus_code = s5k6aa_enum_mbus_code,
+ .enum_frame_size = s5k6aa_enum_frame_size,
+ .enum_frame_interval = s5k6aa_enum_frame_interval,
+ .get_fmt = s5k6aa_get_fmt,
+ .set_fmt = s5k6aa_set_fmt,
+ .get_crop = s5k6aa_get_crop,
+ .set_crop = s5k6aa_set_crop,
+};
+
+static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
+ .g_frame_interval = s5k6aa_g_frame_interval,
+ .s_frame_interval = s5k6aa_s_frame_interval,
+ .s_stream = s5k6aa_s_stream,
+};
+
+/*
+ * V4L2 subdev controls
+ */
+
+static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int idx, err = 0;
+
+ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
+
+ mutex_lock(&s5k6aa->lock);
+ /*
+ * If the device is not powered up by the host driver do
+ * not apply any controls to H/W at this time. Instead
+ * the controls will be restored right after power-up.
+ */
+ if (s5k6aa->power == 0)
+ goto unlock;
+ idx = s5k6aa->preset->index;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ err = s5k6aa_set_awb(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_BRIGHTNESS:
+ err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
+ break;
+
+ case V4L2_CID_COLORFX:
+ err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
+ break;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_HFLIP:
+ err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
+ break;
+
+ case V4L2_CID_SHARPNESS:
+ err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
+ break;
+
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
+ if (err)
+ break;
+ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
+ break;
+ }
+unlock:
+ mutex_unlock(&s5k6aa->lock);
+ return err;
+}
+
+static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
+ .s_ctrl = s5k6aa_s_ctrl,
+};
+
+static int s5k6aa_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
+ return 0;
+}
+
+#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001)
+#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002)
+#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003)
+
+static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
+ {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_RED_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Red",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_GREEN_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Green",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ }, {
+ .ops = &s5k6aa_ctrl_ops,
+ .id = V4L2_CID_BLUE_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain, Blue",
+ .min = 0,
+ .max = 256,
+ .def = 127,
+ .step = 1,
+ },
+};
+
+static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
+{
+ const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
+ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
+ struct v4l2_ctrl_handler *hdl = &ctrls->handler;
+
+ int ret = v4l2_ctrl_handler_init(hdl, 16);
+ if (ret)
+ return ret;
+ /* Auto white balance cluster */
+ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
+ 0, 1, 1, 1);
+ ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
+ ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
+ ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
+ v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
+
+ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &ctrls->hflip);
+
+ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ /* Exposure time: x 1 us */
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ 0, 6000000U, 1, 100000U);
+ /* Total gain: 256 <=> 1x */
+ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
+ 0, 256, 1, 256);
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+
+ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
+ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 256, 1, 0);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ s5k6aa->sd.ctrl_handler = hdl;
+ return 0;
+}
+
+/*
+ * V4L2 subdev internal operations
+ */
+static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
+
+ format->colorspace = s5k6aa_formats[0].colorspace;
+ format->code = s5k6aa_formats[0].code;
+ format->width = S5K6AA_OUT_WIDTH_DEF;
+ format->height = S5K6AA_OUT_HEIGHT_DEF;
+ format->field = V4L2_FIELD_NONE;
+
+ crop->width = S5K6AA_WIN_WIDTH_MAX;
+ crop->height = S5K6AA_WIN_HEIGHT_MAX;
+ crop->left = 0;
+ crop->top = 0;
+
+ return 0;
+}
+
+static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
+ u16 api_ver = 0, fw_rev = 0;
+
+ int ret = s5k6aa_set_ahb_address(client);
+
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
+ if (!ret)
+ ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
+ if (ret) {
+ v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
+ return ret;
+ }
+
+ v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
+ api_ver, fw_rev);
+
+ return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
+}
+
+static int s5k6aa_registered(struct v4l2_subdev *sd)
+{
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+ int ret;
+
+ mutex_lock(&s5k6aa->lock);
+ ret = __s5k6aa_power_on(s5k6aa);
+ if (!ret) {
+ msleep(100);
+ ret = s5k6aa_check_fw_revision(s5k6aa);
+ __s5k6aa_power_off(s5k6aa);
+ }
+ mutex_unlock(&s5k6aa->lock);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
+ .registered = s5k6aa_registered,
+ .open = s5k6aa_open,
+};
+
+static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
+ .s_power = s5k6aa_set_power,
+ .log_status = s5k6aa_log_status,
+};
+
+static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
+ .core = &s5k6aa_core_ops,
+ .pad = &s5k6aa_pad_ops,
+ .video = &s5k6aa_video_ops,
+};
+
+/*
+ * GPIO setup
+ */
+static int s5k6aa_configure_gpio(int nr, int val, const char *name)
+{
+ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
+ int ret;
+
+ if (!gpio_is_valid(nr))
+ return 0;
+ ret = gpio_request_one(nr, flags, name);
+ if (!ret)
+ gpio_export(nr, 0);
+ return ret;
+}
+
+static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
+ if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
+ continue;
+ gpio_free(s5k6aa->gpio[i].gpio);
+ s5k6aa->gpio[i].gpio = -EINVAL;
+ }
+}
+
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
+ const struct s5k6aa_platform_data *pdata)
+{
+ const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+ int ret;
+
+ s5k6aa->gpio[STBY].gpio = -EINVAL;
+ s5k6aa->gpio[RST].gpio = -EINVAL;
+
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[STBY] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ gpio = &pdata->gpio_reset;
+ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
+ if (ret) {
+ s5k6aa_free_gpios(s5k6aa);
+ return ret;
+ }
+ s5k6aa->gpio[RST] = *gpio;
+ if (gpio_is_valid(gpio->gpio))
+ gpio_set_value(gpio->gpio, 0);
+
+ return 0;
+}
+
+static int s5k6aa_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+ struct v4l2_subdev *sd;
+ struct s5k6aa *s5k6aa;
+ int i, ret;
+
+ if (pdata == NULL) {
+ dev_err(&client->dev, "Platform data not specified\n");
+ return -EINVAL;
+ }
+
+ if (pdata->mclk_frequency == 0) {
+ dev_err(&client->dev, "MCLK frequency not specified\n");
+ return -EINVAL;
+ }
+
+ s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL);
+ if (!s5k6aa)
+ return -ENOMEM;
+
+ mutex_init(&s5k6aa->lock);
+
+ s5k6aa->mclk_frequency = pdata->mclk_frequency;
+ s5k6aa->bus_type = pdata->bus_type;
+ s5k6aa->mipi_lanes = pdata->nlanes;
+ s5k6aa->s_power = pdata->set_power;
+ s5k6aa->inv_hflip = pdata->horiz_flip;
+ s5k6aa->inv_vflip = pdata->vert_flip;
+
+ sd = &s5k6aa->sd;
+ v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
+
+ sd->internal_ops = &s5k6aa_subdev_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+ ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
+ if (ret)
+ return ret;
+
+ ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+ if (ret)
+ goto out_err2;
+
+ for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
+ s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
+
+ ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
+ s5k6aa->supplies);
+ if (ret) {
+ dev_err(&client->dev, "Failed to get regulators\n");
+ goto out_err3;
+ }
+
+ ret = s5k6aa_initialize_ctrls(s5k6aa);
+ if (ret)
+ goto out_err4;
+
+ s5k6aa_presets_data_init(s5k6aa);
+
+ s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
+ s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX;
+ s5k6aa->ccd_rect.left = 0;
+ s5k6aa->ccd_rect.top = 0;
+
+ return 0;
+
+out_err4:
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+out_err3:
+ s5k6aa_free_gpios(s5k6aa);
+out_err2:
+ media_entity_cleanup(&s5k6aa->sd.entity);
+ return ret;
+}
+
+static int s5k6aa_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct s5k6aa *s5k6aa = to_s5k6aa(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ media_entity_cleanup(&sd->entity);
+ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
+ s5k6aa_free_gpios(s5k6aa);
+
+ return 0;
+}
+
+static const struct i2c_device_id s5k6aa_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
+
+
+static struct i2c_driver s5k6aa_i2c_driver = {
+ .driver = {
+ .name = DRIVER_NAME
+ },
+ .probe = s5k6aa_probe,
+ .remove = s5k6aa_remove,
+ .id_table = s5k6aa_id,
+};
+
+module_i2c_driver(s5k6aa_i2c_driver);
+
+MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/saa6588.c b/drivers/media/i2c/saa6588.c
index 0caac50d7cf4..0caac50d7cf4 100644
--- a/drivers/media/video/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
diff --git a/drivers/media/video/saa7110.c b/drivers/media/i2c/saa7110.c
index 51cd4c8f0520..51cd4c8f0520 100644
--- a/drivers/media/video/saa7110.c
+++ b/drivers/media/i2c/saa7110.c
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
new file mode 100644
index 000000000000..6b6788cd08f6
--- /dev/null
+++ b/drivers/media/i2c/saa7115.c
@@ -0,0 +1,1728 @@
+/* saa711x - Philips SAA711x video decoder driver
+ * This driver can work with saa7111, saa7111a, saa7113, saa7114,
+ * saa7115 and saa7118.
+ *
+ * Based on saa7114 driver by Maxim Yevtyushkin, which is based on
+ * the saa7111 driver by Dave Perks.
+ *
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Slight changes for video timing and attachment output by
+ * Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003)
+ * by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com>
+ * (2/17/2003)
+ *
+ * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * SAA7111, SAA7113 and SAA7118 support
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "saa711x_regs.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/saa7115.h>
+#include <asm/div64.h>
+
+#define VRES_60HZ (480+16)
+
+MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver");
+MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
+ "Hans Verkuil, Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+struct saa711x_state {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ struct {
+ /* chroma gain control cluster */
+ struct v4l2_ctrl *agc;
+ struct v4l2_ctrl *gain;
+ };
+
+ v4l2_std_id std;
+ int input;
+ int output;
+ int enable;
+ int radio;
+ int width;
+ int height;
+ u32 ident;
+ u32 audclk_freq;
+ u32 crystal_freq;
+ u8 ucgc;
+ u8 cgcdiv;
+ u8 apll;
+};
+
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa711x_state, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+/* Sanity routine to check if a register is present */
+static int saa711x_has_reg(const int id, const u8 reg)
+{
+ if (id == V4L2_IDENT_SAA7111)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
+ if (id == V4L2_IDENT_SAA7111A)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ reg != 0x14 && reg != 0x18 && reg != 0x19 &&
+ reg != 0x1d && reg != 0x1e;
+
+ /* common for saa7113/4/5/8 */
+ if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
+ reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) ||
+ reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) ||
+ reg == 0x82 || (reg >= 0x89 && reg <= 0x8e)))
+ return 0;
+
+ switch (id) {
+ case V4L2_IDENT_SAA7113:
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
+ reg != 0x5d && reg < 0x63;
+ case V4L2_IDENT_SAA7114:
+ return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
+ reg != 0x81 && reg < 0xf0;
+ case V4L2_IDENT_SAA7115:
+ return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
+ case V4L2_IDENT_SAA7118:
+ return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
+ (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
+ }
+ return 1;
+}
+
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
+{
+ struct saa711x_state *state = to_state(sd);
+ unsigned char reg, data;
+
+ while (*regs != 0x00) {
+ reg = *(regs++);
+ data = *(regs++);
+
+ /* According with datasheets, reserved regs should be
+ filled with 0 - seems better not to touch on they */
+ if (saa711x_has_reg(state->ident, reg)) {
+ if (saa711x_write(sd, reg, data) < 0)
+ return -1;
+ } else {
+ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
+ }
+ }
+ return 0;
+}
+
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* SAA7111 initialization table */
+static const unsigned char saa7111_init[] = {
+ R_01_INC_DELAY, 0x00, /* reserved */
+
+ /*front end */
+ R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */
+ R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0,
+ * GAFIX=0, GAI1=256, GAI2=256 */
+ R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */
+ R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */
+
+ /* decoder */
+ R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz)
+ * pixels after end of last line */
+ R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to
+ * work with NTSC, too */
+ R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0,
+ * VTRC=1, HPLL=0, VNOI=0 */
+ R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0,
+ * VBLB=0, UPTCV=0, APER=1 */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
+ * FCTC=0, CHBW=1 */
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */
+ R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+ R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
+ * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+ R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */
+ R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
+/* SAA7113 init codes */
+static const unsigned char saa7113_init[] = {
+ R_01_INC_DELAY, 0x08,
+ R_02_INPUT_CNTL_1, 0xc2,
+ R_03_INPUT_CNTL_2, 0x30,
+ R_04_INPUT_CNTL_3, 0x00,
+ R_05_INPUT_CNTL_4, 0x00,
+ R_06_H_SYNC_START, 0x89,
+ R_07_H_SYNC_STOP, 0x0d,
+ R_08_SYNC_CNTL, 0x88,
+ R_09_LUMA_CNTL, 0x01,
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47,
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01,
+ R_0F_CHROMA_GAIN_CNTL, 0x2a,
+ R_10_CHROMA_CNTL_2, 0x08,
+ R_11_MODE_DELAY_CNTL, 0x0c,
+ R_12_RT_SIGNAL_CNTL, 0x07,
+ R_13_RT_X_PORT_OUT_CNTL, 0x00,
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
+/* If a value differs from the Hauppauge driver values, then the comment starts with
+ 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
+ Hauppauge driver sets. */
+
+/* SAA7114 and SAA7115 initialization table */
+static const unsigned char saa7115_init_auto_input[] = {
+ /* Front-End Part */
+ R_01_INC_DELAY, 0x48, /* white peak control disabled */
+ R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */
+ R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */
+ R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */
+ /* Decoder Part */
+ R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */
+ R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */
+ R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
+ R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
+ R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */
+ R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */
+ R_11_MODE_DELAY_CNTL, 0x00,
+ R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */
+ R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */
+ R_19_RAW_DATA_OFF_CNTL, 0x80,
+ R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */
+ R_1B_MISC_TVVCRDET, 0x42, /* recommended value */
+ R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */
+ R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */
+
+
+ R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */
+
+ /* Power Device Control */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */
+ 0x00, 0x00
+};
+
+/* Used to reset saa7113, saa7114 and saa7115 */
+static const unsigned char saa7115_cfg_reset_scaler[] = {
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates ============= */
+
+static const unsigned char saa7115_cfg_60hz_video[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+
+ R_15_VGATE_START_FID_CHG, 0x03,
+ R_16_VGATE_STOP, 0x11,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x9c,
+
+ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
+ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
+
+ R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */
+
+ /* Task A */
+ R_90_A_TASK_HANDLING_CNTL, 0x80,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
+
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x01,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_98_A_VERT_INPUT_WINDOW_START, 0x05,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,
+
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c,
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ /* Task B */
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
+
+ /* 0x0002 is minimum */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ /* vwindow start 0x12 = 18 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x12,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vwindow length 0xf8 = 248 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9,
+
+ /* hwindow 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */
+ R_F5_PULSGEN_LINE_LENGTH, 0xad,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_50hz_video[] = {
+ R_80_GLOBAL_CNTL_1, 0x00,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+
+ R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */
+ R_16_VGATE_STOP, 0x16,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x99,
+
+ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
+ R_0E_CHROMA_CNTL_1, 0x07,
+
+ R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */
+
+ /* Task A */
+ R_90_A_TASK_HANDLING_CNTL, 0x81,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
+
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_98_A_VERT_INPUT_WINDOW_START, 0x03,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x12 = 18 */
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ /* hsize 0x05a0 = 1440 */
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */
+
+ /* Task B */
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
+
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ /* hoffset low (input), 0x0002 is minimum. See comment above. */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ /* voffset 0x16 = 22 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x16,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x0120 = 288 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01,
+
+ /* hsize 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */
+ R_F5_PULSGEN_LINE_LENGTH, 0xb0,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
+
+ 0x00, 0x00
+};
+
+/* ============== SAA7715 VIDEO templates (end) ======= */
+
+static const unsigned char saa7115_cfg_vbi_on[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
+
+ 0x00, 0x00
+};
+
+static const unsigned char saa7115_cfg_vbi_off[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
+
+ 0x00, 0x00
+};
+
+
+static const unsigned char saa7115_init_misc[] = {
+ R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01,
+ R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01,
+ R_84_I_PORT_SIGNAL_DEF, 0x20,
+ R_85_I_PORT_SIGNAL_POLAR, 0x21,
+ R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5,
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,
+
+ /* Task A */
+ R_A0_A_HORIZ_PRESCALING, 0x01,
+ R_A1_A_ACCUMULATION_LENGTH, 0x00,
+ R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_A5_A_LUMA_CONTRAST_CNTL, 0x40,
+ R_A6_A_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
+ R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02,
+
+ R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be horiz lum scaling / 2 */
+ R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00,
+ R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01,
+
+ /* must be offset luma / 2 */
+ R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00,
+
+ R_B0_A_VERT_LUMA_SCALING_INC, 0x00,
+ R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_B2_A_VERT_CHROMA_SCALING_INC, 0x00,
+ R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_B4_A_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00,
+
+ /* Task B */
+ R_D0_B_HORIZ_PRESCALING, 0x01,
+ R_D1_B_ACCUMULATION_LENGTH, 0x00,
+ R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_D5_B_LUMA_CONTRAST_CNTL, 0x40,
+ R_D6_B_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* hor lum scaling 0x0400 = 1 */
+ R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be hor lum scaling / 2 */
+ R_DC_B_HORIZ_CHROMA_SCALING, 0x00,
+ R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02,
+
+ /* must be offset luma / 2 */
+ R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00,
+
+ R_E0_B_VERT_LUMA_SCALING_INC, 0x00,
+ R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_E2_B_VERT_CHROMA_SCALING_INC, 0x00,
+ R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_E4_B_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00,
+
+ R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
+ R_F3_PLL_INCREMENT, 0x46,
+ R_F4_PLL2_STATUS, 0x00,
+ R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */
+ R_F8_PULSE_B_POS, 0x00,
+ R_F9_PULSE_B_POS_MSB, 0x4b,
+ R_FA_PULSE_C_POS, 0x00,
+ R_FB_PULSE_C_POS_MSB, 0x4b,
+
+ /* PLL2 lock detection settings: 71 lines 50% phase error */
+ R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88,
+
+ /* Turn off VBI */
+ R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */
+ R_41_LCR_BASE, 0xff,
+ R_41_LCR_BASE+1, 0xff,
+ R_41_LCR_BASE+2, 0xff,
+ R_41_LCR_BASE+3, 0xff,
+ R_41_LCR_BASE+4, 0xff,
+ R_41_LCR_BASE+5, 0xff,
+ R_41_LCR_BASE+6, 0xff,
+ R_41_LCR_BASE+7, 0xff,
+ R_41_LCR_BASE+8, 0xff,
+ R_41_LCR_BASE+9, 0xff,
+ R_41_LCR_BASE+10, 0xff,
+ R_41_LCR_BASE+11, 0xff,
+ R_41_LCR_BASE+12, 0xff,
+ R_41_LCR_BASE+13, 0xff,
+ R_41_LCR_BASE+14, 0xff,
+ R_41_LCR_BASE+15, 0xff,
+ R_41_LCR_BASE+16, 0xff,
+ R_41_LCR_BASE+17, 0xff,
+ R_41_LCR_BASE+18, 0xff,
+ R_41_LCR_BASE+19, 0xff,
+ R_41_LCR_BASE+20, 0xff,
+ R_41_LCR_BASE+21, 0xff,
+ R_41_LCR_BASE+22, 0xff,
+ R_58_PROGRAM_FRAMING_CODE, 0x40,
+ R_59_H_OFF_FOR_SLICER, 0x47,
+ R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83,
+ R_5D_DID, 0xbd,
+ R_5E_SDID, 0x35,
+
+ R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */
+
+ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,
+ 0x00, 0x00
+};
+
+static int saa711x_odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int saa711x_decode_vps(u8 *dst, u8 *p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+ int i;
+ u8 c, err = 0;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+ return err & 0xf0;
+}
+
+static int saa711x_decode_wss(u8 *p)
+{
+ static const int wss_bits[8] = {
+ 0, 0, 0, 1, 0, 1, 1, 1
+ };
+ unsigned char parity;
+ int wss = 0;
+ int i;
+
+ for (i = 0; i < 16; i++) {
+ int b1 = wss_bits[p[i] & 7];
+ int b2 = wss_bits[(p[i] >> 3) & 7];
+
+ if (b1 == b2)
+ return -1;
+ wss |= b2 << i;
+ }
+ parity = wss & 15;
+ parity ^= parity >> 2;
+ parity ^= parity >> 1;
+
+ if (!(parity & 1))
+ return -1;
+
+ return wss;
+}
+
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct saa711x_state *state = to_state(sd);
+ u32 acpf;
+ u32 acni;
+ u32 hz;
+ u64 f;
+ u8 acc = 0; /* reg 0x3a, audio clock control */
+
+ /* Checks for chips that don't have audio clock (saa7111, saa7113) */
+ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
+
+ /* sanity check */
+ if (freq < 32000 || freq > 48000)
+ return -EINVAL;
+
+ /* hz is the refresh rate times 100 */
+ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
+ /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
+ acpf = (25600 * freq) / hz;
+ /* acni = (256 * freq * 2^23) / crystal_frequency =
+ (freq * 2^(8+23)) / crystal_frequency =
+ (freq << 31) / crystal_frequency */
+ f = freq;
+ f = f << 31;
+ do_div(f, state->crystal_freq);
+ acni = f;
+ if (state->ucgc) {
+ acpf = acpf * state->cgcdiv / 16;
+ acni = acni * state->cgcdiv / 16;
+ acc = 0x80;
+ if (state->cgcdiv == 3)
+ acc |= 0x40;
+ }
+ if (state->apll)
+ acc |= 0x08;
+
+ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ (acpf >> 8) & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ (acpf >> 16) & 0x03);
+
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+ state->audclk_freq = freq;
+ return 0;
+}
+
+static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct saa711x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CHROMA_AGC:
+ /* chroma gain cluster */
+ if (state->agc->val)
+ state->gain->val =
+ saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
+ break;
+ }
+ return 0;
+}
+
+static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct saa711x_state *state = to_state(sd);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_CONTRAST:
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_SATURATION:
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_HUE:
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
+ break;
+
+ case V4L2_CID_CHROMA_AGC:
+ /* chroma gain cluster */
+ if (state->agc->val)
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
+ else
+ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
+{
+ struct saa711x_state *state = to_state(sd);
+ int HPSC, HFSC;
+ int VSCY;
+ int res;
+ int is_50hz = state->std & V4L2_STD_625_50;
+ int Vsrc = is_50hz ? 576 : 480;
+
+ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
+
+ /* FIXME need better bounds checking here */
+ if ((width < 1) || (width > 1440))
+ return -EINVAL;
+ if ((height < 1) || (height > Vsrc))
+ return -EINVAL;
+
+ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
+ /* Decoder only supports 720 columns and 480 or 576 lines */
+ if (width != 720)
+ return -EINVAL;
+ if (height != Vsrc)
+ return -EINVAL;
+ }
+
+ state->width = width;
+ state->height = height;
+
+ if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH))
+ return 0;
+
+ /* probably have a valid size, let's set it */
+ /* Set output width/height */
+ /* width */
+
+ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ (u8) (width & 0xff));
+ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((width >> 8) & 0xff));
+
+ /* Vertical Scaling uses height/2 */
+ res = height / 2;
+
+ /* On 60Hz, it is using a higher Vertical Output Size */
+ if (!is_50hz)
+ res += (VRES_60HZ - 480) >> 1;
+
+ /* height */
+ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ (u8) (res & 0xff));
+ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((res >> 8) & 0xff));
+
+ /* Scaling settings */
+ /* Hprescaler is floor(inres/outres) */
+ HPSC = (int)(720 / width);
+ /* 0 is not allowed (div. by zero) */
+ HPSC = HPSC ? HPSC : 1;
+ HFSC = (int)((1024 * 720) / (HPSC * width));
+ /* FIXME hardcodes to "Task B"
+ * write H prescaler integer */
+ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
+ (u8) (HPSC & 0x3f));
+
+ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ /* write H fine-scaling (luminance) */
+ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ (u8) (HFSC & 0xff));
+ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ (u8) ((HFSC >> 8) & 0xff));
+ /* write H fine-scaling (chrominance)
+ * must be lum/2, so i'll just bitshift :) */
+ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
+ (u8) ((HFSC >> 1) & 0xff));
+ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ (u8) ((HFSC >> 9) & 0xff));
+
+ VSCY = (int)((1024 * Vsrc) / height);
+ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+
+ /* Correct Contrast and Luminance */
+ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
+ (u8) (64 * 1024 / VSCY));
+ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
+ (u8) (64 * 1024 / VSCY));
+
+ /* write V fine-scaling (luminance) */
+ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+ /* write V fine-scaling (chrominance) */
+ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+
+ /* Activates task "B" */
+ saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
+
+ return 0;
+}
+
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ /* Prevent unnecessary standard changes. During a standard
+ change the I-Port is temporarily disabled. Any devices
+ reading from that port can get confused.
+ Note that s_std is also used to switch from
+ radio to TV mode, so if a s_std is broadcast to
+ all I2C devices then you do not want to have an unwanted
+ side-effect here. */
+ if (std == state->std)
+ return;
+
+ state->std = std;
+
+ // This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
+ if (std & V4L2_STD_525_60) {
+ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ saa711x_set_size(sd, 720, 480);
+ } else {
+ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ saa711x_set_size(sd, 720, 576);
+ }
+
+ /* Register 0E - Bits D6-D4 on NO-AUTO mode
+ (SAA7111 and SAA7113 doesn't have auto mode)
+ 50 Hz / 625 lines 60 Hz / 525 lines
+ 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz)
+ 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz)
+ 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz)
+ 011 NTSC N (3.58MHz) PAL M (3.58MHz)
+ 100 reserved NTSC-Japan (3.58MHz)
+ */
+ if (state->ident <= V4L2_IDENT_SAA7113) {
+ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
+
+ if (std == V4L2_STD_PAL_M) {
+ reg |= 0x30;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ reg |= 0x20;
+ } else if (std == V4L2_STD_PAL_60) {
+ reg |= 0x10;
+ } else if (std == V4L2_STD_NTSC_M_JP) {
+ reg |= 0x40;
+ } else if (std & V4L2_STD_SECAM) {
+ reg |= 0x50;
+ }
+ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
+ } else {
+ /* restart task B if needed */
+ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
+
+ if (taskb && state->ident == V4L2_IDENT_SAA7114) {
+ saa711x_writeregs(sd, saa7115_cfg_vbi_on);
+ }
+
+ /* switch audio mode too! */
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ }
+}
+
+/* setup the sliced VBI lcr registers according to the sliced VBI format */
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ struct saa711x_state *state = to_state(sd);
+ int is_50hz = (state->std & V4L2_STD_625_50);
+ u8 lcr[24];
+ int i, x;
+
+#if 1
+ /* saa7113/7114/7118 VBI support are experimental */
+ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
+ return;
+
+#else
+ /* SAA7113 and SAA7118 also should support VBI - Need testing */
+ if (state->ident != V4L2_IDENT_SAA7115)
+ return;
+#endif
+
+ for (i = 0; i <= 23; i++)
+ lcr[i] = 0xff;
+
+ if (fmt == NULL) {
+ /* raw VBI */
+ if (is_50hz)
+ for (i = 6; i <= 23; i++)
+ lcr[i] = 0xdd;
+ else
+ for (i = 10; i <= 21; i++)
+ lcr[i] = 0xdd;
+ } else {
+ /* sliced VBI */
+ /* first clear lines that cannot be captured */
+ if (is_50hz) {
+ for (i = 0; i <= 5; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+ else {
+ for (i = 0; i <= 9; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ for (i = 22; i <= 23; i++)
+ fmt->service_lines[0][i] =
+ fmt->service_lines[1][i] = 0;
+ }
+
+ /* Now set the lcr values according to the specified service */
+ for (i = 6; i <= 23; i++) {
+ lcr[i] = 0;
+ for (x = 0; x <= 1; x++) {
+ switch (fmt->service_lines[1-x][i]) {
+ case 0:
+ lcr[i] |= 0xf << (4 * x);
+ break;
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 5 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 7 << (4 * x);
+ break;
+ }
+ }
+ }
+ }
+
+ /* write the lcr registers */
+ for (i = 2; i <= 23; i++) {
+ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
+ }
+
+ /* enable/disable raw VBI capturing */
+ saa711x_writeregs(sd, fmt == NULL ?
+ saa7115_cfg_vbi_on :
+ saa7115_cfg_vbi_off);
+}
+
+static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced)
+{
+ static u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_CAPTION_525, /* 4 */
+ V4L2_SLICED_WSS_625, 0, /* 5 */
+ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */
+ 0, 0, 0, 0
+ };
+ int i;
+
+ memset(sliced->service_lines, 0, sizeof(sliced->service_lines));
+ sliced->service_set = 0;
+ /* done if using raw VBI */
+ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
+ return 0;
+ for (i = 2; i <= 23; i++) {
+ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
+
+ sliced->service_lines[0][i] = lcr2vbi[v >> 4];
+ sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
+ sliced->service_set |=
+ sliced->service_lines[0][i] | sliced->service_lines[1][i];
+ }
+ return 0;
+}
+
+static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ saa711x_set_lcr(sd, NULL);
+ return 0;
+}
+
+static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ saa711x_set_lcr(sd, fmt);
+ return 0;
+}
+
+static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
+{
+ if (fmt->code != V4L2_MBUS_FMT_FIXED)
+ return -EINVAL;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return saa711x_set_size(sd, fmt->width, fmt->height);
+}
+
+/* Decode the sliced VBI data stream as created by the saa7115.
+ The format is described in the saa7115 datasheet in Tables 25 and 26
+ and in Figure 33.
+ The current implementation uses SAV/EAV codes and not the ancillary data
+ headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
+ code. */
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct saa711x_state *state = to_state(sd);
+ static const char vbi_no_data_pattern[] = {
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
+ };
+ u8 *p = vbi->p;
+ u32 wss;
+ int id1, id2; /* the ID1 and ID2 bytes from the internal header */
+
+ vbi->type = 0; /* mark result as a failure */
+ id1 = p[2];
+ id2 = p[3];
+ /* Note: the field bit is inverted for 60 Hz video */
+ if (state->std & V4L2_STD_525_60)
+ id1 ^= 0x40;
+
+ /* Skip internal header, p now points to the start of the payload */
+ p += 4;
+ vbi->p = p;
+
+ /* calculate field and line number of the VBI packet (1-23) */
+ vbi->is_second_field = ((id1 & 0x40) != 0);
+ vbi->line = (id1 & 0x3f) << 3;
+ vbi->line |= (id2 & 0x70) >> 4;
+
+ /* Obtain data type */
+ id2 &= 0xf;
+
+ /* If the VBI slicer does not detect any signal it will fill up
+ the payload buffer with 0xa0 bytes. */
+ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
+ return 0;
+
+ /* decode payloads */
+ switch (id2) {
+ case 1:
+ vbi->type = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
+ return 0;
+ vbi->type = V4L2_SLICED_CAPTION_525;
+ break;
+ case 5:
+ wss = saa711x_decode_wss(p);
+ if (wss == -1)
+ return 0;
+ p[0] = wss & 0xff;
+ p[1] = wss >> 8;
+ vbi->type = V4L2_SLICED_WSS_625;
+ break;
+ case 7:
+ if (saa711x_decode_vps(p, p) != 0)
+ return 0;
+ vbi->type = V4L2_SLICED_VPS;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* ============ SAA7115 AUDIO settings (end) ============= */
+
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa711x_state *state = to_state(sd);
+ int status;
+
+ if (state->radio)
+ return 0;
+ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+
+ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ return 0;
+}
+
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ state->radio = 0;
+ saa711x_set_v4lstd(sd, std);
+ return 0;
+}
+
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ state->radio = 1;
+ return 0;
+}
+
+static int saa711x_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa711x_state *state = to_state(sd);
+ u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
+ input, output);
+
+ /* saa7111/3 does not have these inputs */
+ if (state->ident <= V4L2_IDENT_SAA7113 &&
+ (input == SAA7115_COMPOSITE4 ||
+ input == SAA7115_COMPOSITE5)) {
+ return -EINVAL;
+ }
+ if (input > SAA7115_SVIDEO3)
+ return -EINVAL;
+ if (state->input == input && state->output == output)
+ return 0;
+ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+ (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+ (output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+ state->input = input;
+
+ /* saa7111 has slightly different input numbering */
+ if (state->ident <= V4L2_IDENT_SAA7111A) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(sd, R_10_CHROMA_CNTL_2,
+ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((output & 0xc0) ^ 0x40));
+ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((output & 2) ? 0x0a : 0));
+ }
+
+ /* select mode */
+ saa711x_write(sd, R_02_INPUT_CNTL_1,
+ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+ input);
+
+ /* bypass chrominance trap for S-Video modes */
+ saa711x_write(sd, R_09_LUMA_CNTL,
+ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+
+ state->output = output;
+ if (state->ident == V4L2_IDENT_SAA7114 ||
+ state->ident == V4L2_IDENT_SAA7115) {
+ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
+ }
+ return 0;
+}
+
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ if (state->ident > V4L2_IDENT_SAA7111A)
+ return -EINVAL;
+ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+ (val ? 0x80 : 0));
+ return 0;
+}
+
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
+
+ if (state->enable == enable)
+ return 0;
+ state->enable = enable;
+ if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
+ return 0;
+ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
+ return 0;
+}
+
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
+{
+ struct saa711x_state *state = to_state(sd);
+
+ if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
+ return -EINVAL;
+ state->crystal_freq = freq;
+ state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+ state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+ state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ return 0;
+}
+
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+ v4l2_dbg(1, debug, sd, "decoder RESET\n");
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+ return 0;
+}
+
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+ /* Note: the internal field ID is inverted for NTSC,
+ so data->field 0 maps to the saa7115 even field,
+ whereas for PAL it maps to the saa7115 odd field. */
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa711x_read(sd, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x6c);
+ data->data[1] = saa711x_read(sd, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa711x_read(sd, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x69);
+ data->data[1] = saa711x_read(sd, 0x6a);
+ return 0;
+ }
+ /* XDS */
+ if (saa711x_read(sd, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x67);
+ data->data[1] = saa711x_read(sd, 0x68);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1f, reg1e;
+
+ /*
+ * The V4L2 core already initializes std with all supported
+ * Standards. All driver needs to do is to mask it, to remove
+ * standards that don't apply from the mask
+ */
+
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
+
+ /* horizontal/vertical not locked */
+ if (reg1f & 0x40)
+ goto ret;
+
+ if (reg1f & 0x20)
+ *std &= V4L2_STD_525_60;
+ else
+ *std &= V4L2_STD_625_50;
+
+ if (state->ident != V4L2_IDENT_SAA7115)
+ goto ret;
+
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+
+ switch (reg1e & 0x03) {
+ case 1:
+ *std &= V4L2_STD_NTSC;
+ break;
+ case 2:
+ /*
+ * V4L2_STD_PAL just cover the european PAL standards.
+ * This is wrong, as the device could also be using an
+ * other PAL standard.
+ */
+ *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_PAL_M | V4L2_STD_PAL_60;
+ break;
+ case 3:
+ *std &= V4L2_STD_SECAM;
+ break;
+ default:
+ /* Can't detect anything */
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
+
+ret:
+ v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
+
+ return 0;
+}
+
+static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e = 0x80;
+ int reg1f;
+
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ if (state->ident == V4L2_IDENT_SAA7115)
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
+ *status = 0;
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa711x_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa711x_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
+
+ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+ if (state->ident != V4L2_IDENT_SAA7115) {
+ /* status for the saa7114 */
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ return 0;
+ }
+
+ /* status for the saa7115 */
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
+
+ if (state->input >= 6)
+ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
+ else
+ v4l2_info(sd, "Input: Composite %d\n", state->input);
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+
+ switch (reg1e & 0x03) {
+ case 1:
+ v4l2_info(sd, "Detected format: NTSC\n");
+ break;
+ case 2:
+ v4l2_info(sd, "Detected format: PAL\n");
+ break;
+ case 3:
+ v4l2_info(sd, "Detected format: SECAM\n");
+ break;
+ default:
+ v4l2_info(sd, "Detected format: BW/No color\n");
+ break;
+ }
+ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
+ v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
+ .s_ctrl = saa711x_s_ctrl,
+ .g_volatile_ctrl = saa711x_g_volatile_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops saa711x_core_ops = {
+ .log_status = saa711x_log_status,
+ .g_chip_ident = saa711x_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,
+ .s_std = saa711x_s_std,
+ .reset = saa711x_reset,
+ .s_gpio = saa711x_s_gpio,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa711x_g_register,
+ .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+ .s_radio = saa711x_s_radio,
+ .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+ .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_routing = saa711x_s_routing,
+ .s_crystal_freq = saa711x_s_crystal_freq,
+ .s_mbus_fmt = saa711x_s_mbus_fmt,
+ .s_stream = saa711x_s_stream,
+ .querystd = saa711x_querystd,
+ .g_input_status = saa711x_g_input_status,
+};
+
+static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = {
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .g_sliced_fmt = saa711x_g_sliced_fmt,
+ .s_sliced_fmt = saa711x_s_sliced_fmt,
+ .s_raw_fmt = saa711x_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops saa711x_ops = {
+ .core = &saa711x_core_ops,
+ .tuner = &saa711x_tuner_ops,
+ .audio = &saa711x_audio_ops,
+ .video = &saa711x_video_ops,
+ .vbi = &saa711x_vbi_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa711x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa711x_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int i;
+ char name[17];
+ char chip_id;
+ int autodetect = !id || id->driver_data == 1;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ for (i = 0; i < 0x0f; i++) {
+ i2c_smbus_write_byte_data(client, 0, i);
+ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
+ if (name[i] > '9')
+ name[i] += 'a' - '9' - 1;
+ }
+ name[i] = '\0';
+
+ chip_id = name[5];
+
+ /* Check whether this chip is part of the saa711x series */
+ if (memcmp(name + 1, "f711", 4)) {
+ v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
+ client->addr << 1, name);
+ return -ENODEV;
+ }
+
+ /* Safety check */
+ if (!autodetect && id->name[6] != chip_id) {
+ v4l_warn(client, "found saa711%c while %s was expected\n",
+ chip_id, id->name);
+ }
+ snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
+ v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
+ client->addr << 1, client->adapter->name);
+
+ state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
+
+ hdl = &state->hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ /* add in ascending ID order */
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
+ state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
+ V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(state);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
+
+ state->input = -1;
+ state->output = SAA7115_IPORT_ON;
+ state->enable = 1;
+ state->radio = 0;
+ switch (chip_id) {
+ case '1':
+ state->ident = V4L2_IDENT_SAA7111;
+ if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
+ v4l_info(client, "saa7111a variant found\n");
+ state->ident = V4L2_IDENT_SAA7111A;
+ }
+ break;
+ case '3':
+ state->ident = V4L2_IDENT_SAA7113;
+ break;
+ case '4':
+ state->ident = V4L2_IDENT_SAA7114;
+ break;
+ case '5':
+ state->ident = V4L2_IDENT_SAA7115;
+ break;
+ case '8':
+ state->ident = V4L2_IDENT_SAA7118;
+ break;
+ default:
+ state->ident = V4L2_IDENT_SAA7111;
+ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
+ break;
+ }
+
+ state->audclk_freq = 48000;
+
+ v4l2_dbg(1, debug, sd, "writing init values\n");
+
+ /* init to 60hz/48khz */
+ state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
+ switch (state->ident) {
+ case V4L2_IDENT_SAA7111:
+ case V4L2_IDENT_SAA7111A:
+ saa711x_writeregs(sd, saa7111_init);
+ break;
+ case V4L2_IDENT_SAA7113:
+ saa711x_writeregs(sd, saa7113_init);
+ break;
+ default:
+ state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
+ saa711x_writeregs(sd, saa7115_init_auto_input);
+ }
+ if (state->ident > V4L2_IDENT_SAA7111A)
+ saa711x_writeregs(sd, saa7115_init_misc);
+ saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
+ v4l2_ctrl_handler_setup(hdl);
+
+ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa711x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ kfree(to_state(sd));
+ return 0;
+}
+
+static const struct i2c_device_id saa711x_id[] = {
+ { "saa7115_auto", 1 }, /* autodetect */
+ { "saa7111", 0 },
+ { "saa7113", 0 },
+ { "saa7114", 0 },
+ { "saa7115", 0 },
+ { "saa7118", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa711x_id);
+
+static struct i2c_driver saa711x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7115",
+ },
+ .probe = saa711x_probe,
+ .remove = saa711x_remove,
+ .id_table = saa711x_id,
+};
+
+module_i2c_driver(saa711x_driver);
diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h
index 4e5f2eb0a2c1..4e5f2eb0a2c1 100644
--- a/drivers/media/video/saa711x_regs.h
+++ b/drivers/media/i2c/saa711x_regs.h
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
new file mode 100644
index 000000000000..b745f68fbc92
--- /dev/null
+++ b/drivers/media/i2c/saa7127.c
@@ -0,0 +1,852 @@
+/*
+ * saa7127 - Philips SAA7127/SAA7129 video encoder driver
+ *
+ * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl>
+ *
+ * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter
+ *
+ * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
+ * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
+ *
+ * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo
+ *
+ * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
+ *
+ * This driver is designed for the Hauppauge 250/350 Linux driver
+ * from the ivtv Project
+ *
+ * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com>
+ *
+ * Dual output support:
+ * Copyright (C) 2004 Eric Varsanyi
+ *
+ * NTSC Tuning and 7.5 IRE Setup
+ * Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ *
+ * VBI additions & cleanup:
+ * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: the saa7126 is identical to the saa7127, and the saa7128 is
+ * identical to the saa7129, except that the saa7126 and saa7128 have
+ * macrovision anti-taping support. This driver will almost certainly
+ * work fine for those chips, except of course for the missing anti-taping
+ * support.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/saa7127.h>
+
+static int debug;
+static int test_image;
+
+MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver");
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_LICENSE("GPL");
+module_param(debug, int, 0644);
+module_param(test_image, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
+MODULE_PARM_DESC(test_image, "test_image (0-1)");
+
+
+/*
+ * SAA7127 registers
+ */
+
+#define SAA7127_REG_STATUS 0x00
+#define SAA7127_REG_WIDESCREEN_CONFIG 0x26
+#define SAA7127_REG_WIDESCREEN_ENABLE 0x27
+#define SAA7127_REG_BURST_START 0x28
+#define SAA7127_REG_BURST_END 0x29
+#define SAA7127_REG_COPYGEN_0 0x2a
+#define SAA7127_REG_COPYGEN_1 0x2b
+#define SAA7127_REG_COPYGEN_2 0x2c
+#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d
+#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38
+#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39
+#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a
+#define SAA7129_REG_FADE_KEY_COL2 0x4f
+#define SAA7127_REG_CHROMA_PHASE 0x5a
+#define SAA7127_REG_GAINU 0x5b
+#define SAA7127_REG_GAINV 0x5c
+#define SAA7127_REG_BLACK_LEVEL 0x5d
+#define SAA7127_REG_BLANKING_LEVEL 0x5e
+#define SAA7127_REG_VBI_BLANKING 0x5f
+#define SAA7127_REG_DAC_CONTROL 0x61
+#define SAA7127_REG_BURST_AMP 0x62
+#define SAA7127_REG_SUBC3 0x63
+#define SAA7127_REG_SUBC2 0x64
+#define SAA7127_REG_SUBC1 0x65
+#define SAA7127_REG_SUBC0 0x66
+#define SAA7127_REG_LINE_21_ODD_0 0x67
+#define SAA7127_REG_LINE_21_ODD_1 0x68
+#define SAA7127_REG_LINE_21_EVEN_0 0x69
+#define SAA7127_REG_LINE_21_EVEN_1 0x6a
+#define SAA7127_REG_RCV_PORT_CONTROL 0x6b
+#define SAA7127_REG_VTRIG 0x6c
+#define SAA7127_REG_HTRIG_HI 0x6d
+#define SAA7127_REG_MULTI 0x6e
+#define SAA7127_REG_CLOSED_CAPTION 0x6f
+#define SAA7127_REG_RCV2_OUTPUT_START 0x70
+#define SAA7127_REG_RCV2_OUTPUT_END 0x71
+#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72
+#define SAA7127_REG_TTX_REQUEST_H_START 0x73
+#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74
+#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75
+#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76
+#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78
+#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79
+#define SAA7127_REG_FIRST_ACTIVE 0x7a
+#define SAA7127_REG_LAST_ACTIVE 0x7b
+#define SAA7127_REG_MSB_VERTICAL 0x7c
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e
+#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f
+
+/*
+ **********************************************************************
+ *
+ * Arrays with configuration parameters for the SAA7127
+ *
+ **********************************************************************
+ */
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+static const struct i2c_reg_value saa7129_init_config_extra[] = {
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 },
+ { SAA7127_REG_VTRIG, 0xfa },
+ { 0, 0 }
+};
+
+static const struct i2c_reg_value saa7127_init_config_common[] = {
+ { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d },
+ { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 },
+ { SAA7127_REG_COPYGEN_0, 0x77 },
+ { SAA7127_REG_COPYGEN_1, 0x41 },
+ { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */
+ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf },
+ { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 },
+ { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 },
+ { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */
+ { SAA7127_REG_LINE_21_ODD_0, 0x77 },
+ { SAA7127_REG_LINE_21_ODD_1, 0x41 },
+ { SAA7127_REG_LINE_21_EVEN_0, 0x88 },
+ { SAA7127_REG_LINE_21_EVEN_1, 0x41 },
+ { SAA7127_REG_RCV_PORT_CONTROL, 0x12 },
+ { SAA7127_REG_VTRIG, 0xf9 },
+ { SAA7127_REG_HTRIG_HI, 0x00 },
+ { SAA7127_REG_RCV2_OUTPUT_START, 0x41 },
+ { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 },
+ { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 },
+ { SAA7127_REG_TTX_REQUEST_H_START, 0x3e },
+ { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 },
+ { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 },
+ { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 },
+ { SAA7127_REG_FIRST_ACTIVE, 0x1a },
+ { SAA7127_REG_LAST_ACTIVE, 0x01 },
+ { SAA7127_REG_MSB_VERTICAL, 0xc0 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 },
+ { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 },
+ { 0, 0 }
+};
+
+#define SAA7127_60HZ_DAC_CONTROL 0x15
+static const struct i2c_reg_value saa7127_init_config_60hz[] = {
+ { SAA7127_REG_BURST_START, 0x19 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0xa3 },
+ { SAA7127_REG_GAINU, 0x98 },
+ { SAA7127_REG_GAINV, 0xd3 },
+ { SAA7127_REG_BLACK_LEVEL, 0x39 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x2e },
+ { SAA7127_REG_VBI_BLANKING, 0x2e },
+ { SAA7127_REG_DAC_CONTROL, 0x15 },
+ { SAA7127_REG_BURST_AMP, 0x4d },
+ { SAA7127_REG_SUBC3, 0x1f },
+ { SAA7127_REG_SUBC2, 0x7c },
+ { SAA7127_REG_SUBC1, 0xf0 },
+ { SAA7127_REG_SUBC0, 0x21 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x11 },
+ { 0, 0 }
+};
+
+#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02
+static struct i2c_reg_value saa7127_init_config_50hz_pal[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x7d },
+ { SAA7127_REG_GAINV, 0xaf },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x02 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xcb },
+ { SAA7127_REG_SUBC2, 0x8a },
+ { SAA7127_REG_SUBC1, 0x09 },
+ { SAA7127_REG_SUBC0, 0x2a },
+ { SAA7127_REG_MULTI, 0xa0 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
+#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08
+static struct i2c_reg_value saa7127_init_config_50hz_secam[] = {
+ { SAA7127_REG_BURST_START, 0x21 },
+ /* BURST_END is also used as a chip ID in saa7127_probe */
+ { SAA7127_REG_BURST_END, 0x1d },
+ { SAA7127_REG_CHROMA_PHASE, 0x3f },
+ { SAA7127_REG_GAINU, 0x6a },
+ { SAA7127_REG_GAINV, 0x81 },
+ { SAA7127_REG_BLACK_LEVEL, 0x33 },
+ { SAA7127_REG_BLANKING_LEVEL, 0x35 },
+ { SAA7127_REG_VBI_BLANKING, 0x35 },
+ { SAA7127_REG_DAC_CONTROL, 0x08 },
+ { SAA7127_REG_BURST_AMP, 0x2f },
+ { SAA7127_REG_SUBC3, 0xb2 },
+ { SAA7127_REG_SUBC2, 0x3b },
+ { SAA7127_REG_SUBC1, 0xa3 },
+ { SAA7127_REG_SUBC0, 0x28 },
+ { SAA7127_REG_MULTI, 0x90 },
+ { SAA7127_REG_CLOSED_CAPTION, 0x00 },
+ { 0, 0 }
+};
+
+/*
+ **********************************************************************
+ *
+ * Encoder Struct, holds the configuration state of the encoder
+ *
+ **********************************************************************
+ */
+
+struct saa7127_state {
+ struct v4l2_subdev sd;
+ v4l2_std_id std;
+ u32 ident;
+ enum saa7127_input_type input_type;
+ enum saa7127_output_type output_type;
+ int video_enable;
+ int wss_enable;
+ u16 wss_mode;
+ int cc_enable;
+ u16 cc_data;
+ int xds_enable;
+ u16 xds_data;
+ int vps_enable;
+ u8 vps_data[5];
+ u8 reg_2d;
+ u8 reg_3a;
+ u8 reg_3a_cb; /* colorbar bit */
+ u8 reg_61;
+};
+
+static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7127_state, sd);
+}
+
+static const char * const output_strs[] =
+{
+ "S-Video + Composite",
+ "Composite",
+ "S-Video",
+ "RGB",
+ "YUV C",
+ "YUV V"
+};
+
+static const char * const wss_strs[] = {
+ "invalid",
+ "letterbox 14:9 center",
+ "letterbox 14:9 top",
+ "invalid",
+ "letterbox 16:9 top",
+ "invalid",
+ "invalid",
+ "16:9 full format anamorphic",
+ "4:3 full format",
+ "invalid",
+ "invalid",
+ "letterbox 16:9 center",
+ "invalid",
+ "letterbox >16:9 center",
+ "14:9 full format center",
+ "invalid",
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_read(struct v4l2_subdev *sd, u8 reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (i2c_smbus_write_byte_data(client, reg, val) == 0)
+ return 0;
+ }
+ v4l2_err(sd, "I2C Write Problem\n");
+ return -1;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0) {
+ saa7127_write(sd, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 16))
+ return -EINVAL;
+ if (state->vps_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x54, enable << 7);
+ state->vps_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ state->vps_data[0] = data->data[2];
+ state->vps_data[1] = data->data[8];
+ state->vps_data[2] = data->data[9];
+ state->vps_data[3] = data->data[10];
+ state->vps_data[4] = data->data[11];
+ v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data);
+ saa7127_write(sd, 0x55, state->vps_data[0]);
+ saa7127_write(sd, 0x56, state->vps_data[1]);
+ saa7127_write(sd, 0x57, state->vps_data[2]);
+ saa7127_write(sd, 0x58, state->vps_data[3]);
+ saa7127_write(sd, 0x59, state->vps_data[4]);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ u16 cc = data->data[1] << 8 | data->data[0];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 21))
+ return -EINVAL;
+ if (state->cc_enable != enable) {
+ v4l2_dbg(1, debug, sd,
+ "Turn CC %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
+ (state->xds_enable << 7) | (enable << 6) | 0x11);
+ state->cc_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ state->cc_data = cc;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ u16 xds = data->data[1] << 8 | data->data[0];
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 1 || data->line != 21))
+ return -EINVAL;
+ if (state->xds_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
+ (enable << 7) | (state->cc_enable << 6) | 0x11);
+ state->xds_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ state->xds_data = xds;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ struct saa7127_state *state = to_state(sd);
+ int enable = (data->line != 0);
+
+ if (enable && (data->field != 0 || data->line != 23))
+ return -EINVAL;
+ if (state->wss_enable != enable) {
+ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x27, enable << 7);
+ state->wss_enable = enable;
+ }
+ if (!enable)
+ return 0;
+
+ saa7127_write(sd, 0x26, data->data[0]);
+ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
+ v4l2_dbg(1, debug, sd,
+ "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
+ state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (enable) {
+ v4l2_dbg(1, debug, sd, "Enable Video Output\n");
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x61, state->reg_61);
+ } else {
+ v4l2_dbg(1, debug, sd, "Disable Video Output\n");
+ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
+ }
+ state->video_enable = enable;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7127_state *state = to_state(sd);
+ const struct i2c_reg_value *inittab;
+
+ if (std & V4L2_STD_525_60) {
+ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
+ inittab = saa7127_init_config_60hz;
+ state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
+
+ } else if (state->ident == V4L2_IDENT_SAA7129 &&
+ (std & V4L2_STD_SECAM) &&
+ !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) {
+
+ /* If and only if SECAM, with a SAA712[89] */
+ v4l2_dbg(1, debug, sd,
+ "Selecting 50 Hz SECAM video Standard\n");
+ inittab = saa7127_init_config_50hz_secam;
+ state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL;
+
+ } else {
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n");
+ inittab = saa7127_init_config_50hz_pal;
+ state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL;
+ }
+
+ /* Write Table */
+ saa7127_write_inittab(sd, inittab);
+ state->std = std;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ switch (output) {
+ case SAA7127_OUTPUT_TYPE_RGB:
+ state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_COMPOSITE:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x20; /* CVBS only */
+ else
+ state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_SVIDEO:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x18; /* Y + C */
+ else
+ state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_V:
+ state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_YUV_C:
+ state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */
+ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
+ break;
+
+ case SAA7127_OUTPUT_TYPE_BOTH:
+ if (state->ident == V4L2_IDENT_SAA7129)
+ state->reg_2d = 0x38;
+ else
+ state->reg_2d = 0xbf;
+ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ v4l2_dbg(1, debug, sd,
+ "Selecting %s output type\n", output_strs[output]);
+
+ /* Configure Encoder */
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->output_type = output;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ switch (input) {
+ case SAA7127_INPUT_TYPE_NORMAL: /* avia */
+ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
+ state->reg_3a_cb = 0;
+ break;
+
+ case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
+ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
+ state->reg_3a_cb = 0x80;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
+ state->input_type = input;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (state->std == std)
+ return 0;
+ return saa7127_set_std(sd, std);
+}
+
+static int saa7127_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct saa7127_state *state = to_state(sd);
+ int rc = 0;
+
+ if (state->input_type != input)
+ rc = saa7127_set_input_type(sd, input);
+ if (rc == 0 && state->output_type != output)
+ rc = saa7127_set_output_type(sd, output);
+ return rc;
+}
+
+static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ if (state->video_enable == enable)
+ return 0;
+ return saa7127_set_video_enable(sd, enable);
+}
+
+static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ memset(fmt->service_lines, 0, sizeof(fmt->service_lines));
+ if (state->vps_enable)
+ fmt->service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ }
+ fmt->service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ return 0;
+}
+
+static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(sd, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(sd, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(sd, data);
+ return saa7127_set_xds(sd, data);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa7127_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+{
+ struct saa7127_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa7127_log_status(struct v4l2_subdev *sd)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
+ v4l2_info(sd, "Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7127_core_ops = {
+ .log_status = saa7127_log_status,
+ .g_chip_ident = saa7127_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa7127_g_register,
+ .s_register = saa7127_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops saa7127_video_ops = {
+ .s_std_output = saa7127_s_std_output,
+ .s_routing = saa7127_s_routing,
+ .s_stream = saa7127_s_stream,
+};
+
+static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = {
+ .s_vbi_data = saa7127_s_vbi_data,
+ .g_sliced_fmt = saa7127_g_sliced_fmt,
+};
+
+static const struct v4l2_subdev_ops saa7127_ops = {
+ .core = &saa7127_core_ops,
+ .video = &saa7127_video_ops,
+ .vbi = &saa7127_vbi_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct saa7127_state *state;
+ struct v4l2_subdev *sd;
+ struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
+ client->addr << 1);
+
+ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
+
+ /* First test register 0: Bits 5-7 are a version ID (should be 0),
+ and bit 2 should also be 0.
+ This is rather general, so the second test is more specific and
+ looks at the 'ending point of burst in clock cycles' which is
+ 0x1d after a reset and not expected to ever change. */
+ if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
+ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
+ v4l2_dbg(1, debug, sd, "saa7127 not found\n");
+ kfree(state);
+ return -ENODEV;
+ }
+
+ if (id->driver_data) { /* Chip type is already known */
+ state->ident = id->driver_data;
+ } else { /* Needs detection */
+ int read_result;
+
+ /* Detect if it's an saa7129 */
+ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
+ read_result);
+ state->ident = V4L2_IDENT_SAA7129;
+ strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
+ } else {
+ state->ident = V4L2_IDENT_SAA7127;
+ strlcpy(client->name, "saa7127", I2C_NAME_SIZE);
+ }
+ }
+
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
+ client->addr << 1, client->adapter->name);
+
+ v4l2_dbg(1, debug, sd, "Configuring encoder\n");
+ saa7127_write_inittab(sd, saa7127_init_config_common);
+ saa7127_set_std(sd, V4L2_STD_NTSC);
+ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(sd, &vbi);
+ saa7127_set_wss(sd, &vbi);
+ saa7127_set_cc(sd, &vbi);
+ saa7127_set_xds(sd, &vbi);
+ if (test_image == 1)
+ /* The Encoder has an internal Colorbar generator */
+ /* This can be used for debugging */
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ else
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
+ saa7127_set_video_enable(sd, 1);
+
+ if (state->ident == V4L2_IDENT_SAA7129)
+ saa7127_write_inittab(sd, saa7129_init_config_extra);
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int saa7127_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ /* Turn off TV output */
+ saa7127_set_video_enable(sd, 0);
+ kfree(to_state(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_device_id saa7127_id[] = {
+ { "saa7127_auto", 0 }, /* auto-detection */
+ { "saa7126", V4L2_IDENT_SAA7127 },
+ { "saa7127", V4L2_IDENT_SAA7127 },
+ { "saa7128", V4L2_IDENT_SAA7129 },
+ { "saa7129", V4L2_IDENT_SAA7129 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, saa7127_id);
+
+static struct i2c_driver saa7127_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "saa7127",
+ },
+ .probe = saa7127_probe,
+ .remove = saa7127_remove,
+ .id_table = saa7127_id,
+};
+
+module_i2c_driver(saa7127_driver);
diff --git a/drivers/media/video/saa717x.c b/drivers/media/i2c/saa717x.c
index 1e84466515aa..1e84466515aa 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
diff --git a/drivers/media/video/saa7185.c b/drivers/media/i2c/saa7185.c
index 2c6b65c76e2b..2c6b65c76e2b 100644
--- a/drivers/media/video/saa7185.c
+++ b/drivers/media/i2c/saa7185.c
diff --git a/drivers/media/video/saa7191.c b/drivers/media/i2c/saa7191.c
index d7d1670e0ca3..d7d1670e0ca3 100644
--- a/drivers/media/video/saa7191.c
+++ b/drivers/media/i2c/saa7191.c
diff --git a/drivers/media/video/saa7191.h b/drivers/media/i2c/saa7191.h
index 803c74d6066f..803c74d6066f 100644
--- a/drivers/media/video/saa7191.h
+++ b/drivers/media/i2c/saa7191.h
diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c
new file mode 100644
index 000000000000..a577614bd84f
--- /dev/null
+++ b/drivers/media/i2c/smiapp-pll.c
@@ -0,0 +1,418 @@
+/*
+ * drivers/media/i2c/smiapp-pll.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/gcd.h>
+#include <linux/lcm.h>
+#include <linux/module.h>
+
+#include "smiapp-pll.h"
+
+/* Return an even number or one. */
+static inline uint32_t clk_div_even(uint32_t a)
+{
+ return max_t(uint32_t, 1, a & ~1);
+}
+
+/* Return an even number or one. */
+static inline uint32_t clk_div_even_up(uint32_t a)
+{
+ if (a == 1)
+ return 1;
+ return (a + 1) & ~1;
+}
+
+static inline uint32_t is_one_or_even(uint32_t a)
+{
+ if (a == 1)
+ return 1;
+ if (a & 1)
+ return 0;
+
+ return 1;
+}
+
+static int bounds_check(struct device *dev, uint32_t val,
+ uint32_t min, uint32_t max, char *str)
+{
+ if (val >= min && val <= max)
+ return 0;
+
+ dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
+
+ return -EINVAL;
+}
+
+static void print_pll(struct device *dev, struct smiapp_pll *pll)
+{
+ dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div);
+ dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier);
+ if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div);
+ dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div);
+ }
+ dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div);
+ dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div);
+
+ dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz);
+ dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz);
+ dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz);
+ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n",
+ pll->op_sys_clk_freq_hz);
+ dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n",
+ pll->op_pix_clk_freq_hz);
+ }
+ dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz);
+ dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
+}
+
+int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll)
+{
+ uint32_t sys_div;
+ uint32_t best_pix_div = INT_MAX >> 1;
+ uint32_t vt_op_binning_div;
+ uint32_t lane_op_clock_ratio;
+ uint32_t mul, div;
+ uint32_t more_mul_min, more_mul_max;
+ uint32_t more_mul_factor;
+ uint32_t min_vt_div, max_vt_div, vt_div;
+ uint32_t min_sys_div, max_sys_div;
+ unsigned int i;
+ int rval;
+
+ if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
+ lane_op_clock_ratio = pll->lanes;
+ else
+ lane_op_clock_ratio = 1;
+ dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
+
+ dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
+ pll->binning_vertical);
+
+ /* CSI transfers 2 bits per clock per lane; thus times 2 */
+ pll->pll_op_clk_freq_hz = pll->link_freq * 2
+ * (pll->lanes / lane_op_clock_ratio);
+
+ /* Figure out limits for pre-pll divider based on extclk */
+ dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+ limits->max_pre_pll_clk_div =
+ min_t(uint16_t, limits->max_pre_pll_clk_div,
+ clk_div_even(pll->ext_clk_freq_hz /
+ limits->min_pll_ip_freq_hz));
+ limits->min_pre_pll_clk_div =
+ max_t(uint16_t, limits->min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(pll->ext_clk_freq_hz,
+ limits->max_pll_ip_freq_hz)));
+ dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+
+ i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
+ mul = div_u64(pll->pll_op_clk_freq_hz, i);
+ div = pll->ext_clk_freq_hz / i;
+ dev_dbg(dev, "mul %d / div %d\n", mul, div);
+
+ limits->min_pre_pll_clk_div =
+ max_t(uint16_t, limits->min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
+ limits->max_pll_op_freq_hz)));
+ dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+
+ if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) {
+ dev_err(dev, "unable to compute pre_pll divisor\n");
+ return -EINVAL;
+ }
+
+ pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;
+
+ /*
+ * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
+ * too high.
+ */
+ dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div);
+
+ /* Don't go above max pll multiplier. */
+ more_mul_max = limits->max_pll_multiplier / mul;
+ dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n",
+ more_mul_max);
+ /* Don't go above max pll op frequency. */
+ more_mul_max =
+ min_t(int,
+ more_mul_max,
+ limits->max_pll_op_freq_hz
+ / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
+ dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n",
+ more_mul_max);
+ /* Don't go above the division capability of op sys clock divider. */
+ more_mul_max = min(more_mul_max,
+ limits->max_op_sys_clk_div * pll->pre_pll_clk_div
+ / div);
+ dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n",
+ more_mul_max);
+ /* Ensure we won't go above min_pll_multiplier. */
+ more_mul_max = min(more_mul_max,
+ DIV_ROUND_UP(limits->max_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n",
+ more_mul_max);
+
+ /* Ensure we won't go below min_pll_op_freq_hz. */
+ more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
+ pll->ext_clk_freq_hz / pll->pre_pll_clk_div
+ * mul);
+ dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n",
+ more_mul_min);
+ /* Ensure we won't go below min_pll_multiplier. */
+ more_mul_min = max(more_mul_min,
+ DIV_ROUND_UP(limits->min_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n",
+ more_mul_min);
+
+ if (more_mul_min > more_mul_max) {
+ dev_warn(dev,
+ "unable to compute more_mul_min and more_mul_max");
+ return -EINVAL;
+ }
+
+ more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
+ dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor);
+ more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div);
+ dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
+ more_mul_factor);
+ i = roundup(more_mul_min, more_mul_factor);
+ if (!is_one_or_even(i))
+ i <<= 1;
+
+ dev_dbg(dev, "final more_mul: %d\n", i);
+ if (i > more_mul_max) {
+ dev_warn(dev, "final more_mul is bad, max %d", more_mul_max);
+ return -EINVAL;
+ }
+
+ pll->pll_multiplier = mul * i;
+ pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div;
+ dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div);
+
+ pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
+ / pll->pre_pll_clk_div;
+
+ pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
+ * pll->pll_multiplier;
+
+ /* Derive pll_op_clk_freq_hz. */
+ pll->op_sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / pll->op_sys_clk_div;
+
+ pll->op_pix_clk_div = pll->bits_per_pixel;
+ dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div);
+
+ pll->op_pix_clk_freq_hz =
+ pll->op_sys_clk_freq_hz / pll->op_pix_clk_div;
+
+ /*
+ * Some sensors perform analogue binning and some do this
+ * digitally. The ones doing this digitally can be roughly be
+ * found out using this formula. The ones doing this digitally
+ * should run at higher clock rate, so smaller divisor is used
+ * on video timing side.
+ */
+ if (limits->min_line_length_pck_bin > limits->min_line_length_pck
+ / pll->binning_horizontal)
+ vt_op_binning_div = pll->binning_horizontal;
+ else
+ vt_op_binning_div = 1;
+ dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div);
+
+ /*
+ * Profile 2 supports vt_pix_clk_div E [4, 10]
+ *
+ * Horizontal binning can be used as a base for difference in
+ * divisors. One must make sure that horizontal blanking is
+ * enough to accommodate the CSI-2 sync codes.
+ *
+ * Take scaling factor into account as well.
+ *
+ * Find absolute limits for the factor of vt divider.
+ */
+ dev_dbg(dev, "scale_m: %d\n", pll->scale_m);
+ min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div
+ * pll->scale_n,
+ lane_op_clock_ratio * vt_op_binning_div
+ * pll->scale_m);
+
+ /* Find smallest and biggest allowed vt divisor. */
+ dev_dbg(dev, "min_vt_div: %d\n", min_vt_div);
+ min_vt_div = max(min_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->max_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n",
+ min_vt_div);
+ min_vt_div = max_t(uint32_t, min_vt_div,
+ limits->min_vt_pix_clk_div
+ * limits->min_vt_sys_clk_div);
+ dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div);
+
+ max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div;
+ dev_dbg(dev, "max_vt_div: %d\n", max_vt_div);
+ max_vt_div = min(max_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n",
+ max_vt_div);
+
+ /*
+ * Find limitsits for sys_clk_div. Not all values are possible
+ * with all values of pix_clk_div.
+ */
+ min_sys_div = limits->min_vt_sys_clk_div;
+ dev_dbg(dev, "min_sys_div: %d\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ DIV_ROUND_UP(min_vt_div,
+ limits->max_vt_pix_clk_div));
+ dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ pll->pll_op_clk_freq_hz
+ / limits->max_vt_sys_clk_freq_hz);
+ dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div);
+ min_sys_div = clk_div_even_up(min_sys_div);
+ dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div);
+
+ max_sys_div = limits->max_vt_sys_clk_div;
+ dev_dbg(dev, "max_sys_div: %d\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(max_vt_div,
+ limits->min_vt_pix_clk_div));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div);
+
+ /*
+ * Find pix_div such that a legal pix_div * sys_div results
+ * into a value which is not smaller than div, the desired
+ * divisor.
+ */
+ for (vt_div = min_vt_div; vt_div <= max_vt_div;
+ vt_div += 2 - (vt_div & 1)) {
+ for (sys_div = min_sys_div;
+ sys_div <= max_sys_div;
+ sys_div += 2 - (sys_div & 1)) {
+ int pix_div = DIV_ROUND_UP(vt_div, sys_div);
+
+ if (pix_div < limits->min_vt_pix_clk_div
+ || pix_div > limits->max_vt_pix_clk_div) {
+ dev_dbg(dev,
+ "pix_div %d too small or too big (%d--%d)\n",
+ pix_div,
+ limits->min_vt_pix_clk_div,
+ limits->max_vt_pix_clk_div);
+ continue;
+ }
+
+ /* Check if this one is better. */
+ if (pix_div * sys_div
+ <= roundup(min_vt_div, best_pix_div))
+ best_pix_div = pix_div;
+ }
+ if (best_pix_div < INT_MAX >> 1)
+ break;
+ }
+
+ pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
+ pll->vt_pix_clk_div = best_pix_div;
+
+ pll->vt_sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div;
+ pll->vt_pix_clk_freq_hz =
+ pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div;
+
+ pll->pixel_rate_csi =
+ pll->op_pix_clk_freq_hz * lane_op_clock_ratio;
+
+ print_pll(dev, pll);
+
+ rval = bounds_check(dev, pll->pre_pll_clk_div,
+ limits->min_pre_pll_clk_div,
+ limits->max_pre_pll_clk_div, "pre_pll_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_ip_clk_freq_hz,
+ limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz,
+ "pll_ip_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_multiplier,
+ limits->min_pll_multiplier, limits->max_pll_multiplier,
+ "pll_multiplier");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_op_clk_freq_hz,
+ limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz,
+ "pll_op_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_sys_clk_div,
+ limits->min_op_sys_clk_div, limits->max_op_sys_clk_div,
+ "op_sys_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_pix_clk_div,
+ limits->min_op_pix_clk_div, limits->max_op_pix_clk_div,
+ "op_pix_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_sys_clk_freq_hz,
+ limits->min_op_sys_clk_freq_hz,
+ limits->max_op_sys_clk_freq_hz,
+ "op_sys_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_pix_clk_freq_hz,
+ limits->min_op_pix_clk_freq_hz,
+ limits->max_op_pix_clk_freq_hz,
+ "op_pix_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->vt_sys_clk_freq_hz,
+ limits->min_vt_sys_clk_freq_hz,
+ limits->max_vt_sys_clk_freq_hz,
+ "vt_sys_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->vt_pix_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz,
+ limits->max_vt_pix_clk_freq_hz,
+ "vt_pix_clk_freq_hz");
+
+ return rval;
+}
+EXPORT_SYMBOL_GPL(smiapp_pll_calculate);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h
new file mode 100644
index 000000000000..cb2d2db5d02d
--- /dev/null
+++ b/drivers/media/i2c/smiapp-pll.h
@@ -0,0 +1,103 @@
+/*
+ * drivers/media/i2c/smiapp-pll.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef SMIAPP_PLL_H
+#define SMIAPP_PLL_H
+
+#include <linux/device.h>
+
+struct smiapp_pll {
+ uint8_t lanes;
+ uint8_t binning_horizontal;
+ uint8_t binning_vertical;
+ uint8_t scale_m;
+ uint8_t scale_n;
+ uint8_t bits_per_pixel;
+ uint16_t flags;
+ uint32_t link_freq;
+
+ uint16_t pre_pll_clk_div;
+ uint16_t pll_multiplier;
+ uint16_t op_sys_clk_div;
+ uint16_t op_pix_clk_div;
+ uint16_t vt_sys_clk_div;
+ uint16_t vt_pix_clk_div;
+
+ uint32_t ext_clk_freq_hz;
+ uint32_t pll_ip_clk_freq_hz;
+ uint32_t pll_op_clk_freq_hz;
+ uint32_t op_sys_clk_freq_hz;
+ uint32_t op_pix_clk_freq_hz;
+ uint32_t vt_sys_clk_freq_hz;
+ uint32_t vt_pix_clk_freq_hz;
+
+ uint32_t pixel_rate_csi;
+};
+
+struct smiapp_pll_limits {
+ /* Strict PLL limits */
+ uint32_t min_ext_clk_freq_hz;
+ uint32_t max_ext_clk_freq_hz;
+ uint16_t min_pre_pll_clk_div;
+ uint16_t max_pre_pll_clk_div;
+ uint32_t min_pll_ip_freq_hz;
+ uint32_t max_pll_ip_freq_hz;
+ uint16_t min_pll_multiplier;
+ uint16_t max_pll_multiplier;
+ uint32_t min_pll_op_freq_hz;
+ uint32_t max_pll_op_freq_hz;
+
+ uint16_t min_vt_sys_clk_div;
+ uint16_t max_vt_sys_clk_div;
+ uint32_t min_vt_sys_clk_freq_hz;
+ uint32_t max_vt_sys_clk_freq_hz;
+ uint16_t min_vt_pix_clk_div;
+ uint16_t max_vt_pix_clk_div;
+ uint32_t min_vt_pix_clk_freq_hz;
+ uint32_t max_vt_pix_clk_freq_hz;
+
+ uint16_t min_op_sys_clk_div;
+ uint16_t max_op_sys_clk_div;
+ uint32_t min_op_sys_clk_freq_hz;
+ uint32_t max_op_sys_clk_freq_hz;
+ uint16_t min_op_pix_clk_div;
+ uint16_t max_op_pix_clk_div;
+ uint32_t min_op_pix_clk_freq_hz;
+ uint32_t max_op_pix_clk_freq_hz;
+
+ /* Other relevant limits */
+ uint32_t min_line_length_pck_bin;
+ uint32_t min_line_length_pck;
+};
+
+/* op pix clock is for all lanes in total normally */
+#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
+#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
+
+struct device;
+
+int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll);
+
+#endif /* SMIAPP_PLL_H */
diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig
index 3149cda1d0db..3149cda1d0db 100644
--- a/drivers/media/video/smiapp/Kconfig
+++ b/drivers/media/i2c/smiapp/Kconfig
diff --git a/drivers/media/i2c/smiapp/Makefile b/drivers/media/i2c/smiapp/Makefile
new file mode 100644
index 000000000000..f45a003cbe7e
--- /dev/null
+++ b/drivers/media/i2c/smiapp/Makefile
@@ -0,0 +1,5 @@
+smiapp-objs += smiapp-core.o smiapp-regs.o \
+ smiapp-quirk.o smiapp-limits.o
+obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o
+
+ccflags-y += -Idrivers/media/i2c
diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c
new file mode 100644
index 000000000000..e08e588ad24b
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-core.c
@@ -0,0 +1,2898 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-core.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2010--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Based on smiapp driver by Vimarsh Zutshi
+ * Based on jt8ev1.c by Vimarsh Zutshi
+ * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-device.h>
+
+#include "smiapp.h"
+
+#define SMIAPP_ALIGN_DIM(dim, flags) \
+ ((flags) & V4L2_SEL_FLAG_GE \
+ ? ALIGN((dim), 2) \
+ : (dim) & ~1)
+
+/*
+ * smiapp_module_idents - supported camera modules
+ */
+static const struct smiapp_module_ident smiapp_module_idents[] = {
+ SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"),
+ SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"),
+ SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"),
+ SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"),
+ SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"),
+ SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk),
+ SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"),
+ SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"),
+ SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk),
+ SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk),
+ SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk),
+};
+
+/*
+ *
+ * Dynamic Capability Identification
+ *
+ */
+
+static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc;
+ unsigned int i;
+ int rval;
+ int line_count = 0;
+ int embedded_start = -1, embedded_end = -1;
+ int image_start = 0;
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE,
+ &fmt_model_type);
+ if (rval)
+ return rval;
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE,
+ &fmt_model_subtype);
+ if (rval)
+ return rval;
+
+ ncol_desc = (fmt_model_subtype
+ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK)
+ >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT;
+ nrow_desc = fmt_model_subtype
+ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK;
+
+ dev_dbg(&client->dev, "format_model_type %s\n",
+ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE
+ ? "2 byte" :
+ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE
+ ? "4 byte" : "is simply bad");
+
+ for (i = 0; i < ncol_desc + nrow_desc; i++) {
+ u32 desc;
+ u32 pixelcode;
+ u32 pixels;
+ char *which;
+ char *what;
+
+ if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i),
+ &desc);
+ if (rval)
+ return rval;
+
+ pixelcode =
+ (desc
+ & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK)
+ >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT;
+ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK;
+ } else if (fmt_model_type
+ == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i),
+ &desc);
+ if (rval)
+ return rval;
+
+ pixelcode =
+ (desc
+ & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK)
+ >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT;
+ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK;
+ } else {
+ dev_dbg(&client->dev,
+ "invalid frame format model type %d\n",
+ fmt_model_type);
+ return -EINVAL;
+ }
+
+ if (i < ncol_desc)
+ which = "columns";
+ else
+ which = "rows";
+
+ switch (pixelcode) {
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED:
+ what = "embedded";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY:
+ what = "dummy";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK:
+ what = "black";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK:
+ what = "dark";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE:
+ what = "visible";
+ break;
+ default:
+ what = "invalid";
+ dev_dbg(&client->dev, "pixelcode %d\n", pixelcode);
+ break;
+ }
+
+ dev_dbg(&client->dev, "%s pixels: %d %s\n",
+ what, pixels, which);
+
+ if (i < ncol_desc)
+ continue;
+
+ /* Handle row descriptors */
+ if (pixelcode
+ == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) {
+ embedded_start = line_count;
+ } else {
+ if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE
+ || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2)
+ image_start = line_count;
+ if (embedded_start != -1 && embedded_end == -1)
+ embedded_end = line_count;
+ }
+ line_count += pixels;
+ }
+
+ if (embedded_start == -1 || embedded_end == -1) {
+ embedded_start = 0;
+ embedded_end = 0;
+ }
+
+ dev_dbg(&client->dev, "embedded data from lines %d to %d\n",
+ embedded_start, embedded_end);
+ dev_dbg(&client->dev, "image data starts at line %d\n", image_start);
+
+ return 0;
+}
+
+static int smiapp_pll_configure(struct smiapp_sensor *sensor)
+{
+ struct smiapp_pll *pll = &sensor->pll;
+ int rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier);
+ if (rval < 0)
+ return rval;
+
+ /* Lane op clock ratio does not apply here. */
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS,
+ DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256));
+ if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div);
+ if (rval < 0)
+ return rval;
+
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div);
+}
+
+static int smiapp_pll_update(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct smiapp_pll_limits lim = {
+ .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV],
+ .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV],
+ .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ],
+ .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ],
+ .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER],
+ .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER],
+ .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ],
+ .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ],
+
+ .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV],
+ .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV],
+ .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV],
+ .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV],
+ .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ],
+ .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ],
+ .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ],
+ .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ],
+
+ .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV],
+ .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV],
+ .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV],
+ .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV],
+ .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ],
+ .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ],
+ .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ],
+ .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ],
+
+ .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
+ .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
+ };
+ struct smiapp_pll *pll = &sensor->pll;
+ int rval;
+
+ memset(&sensor->pll, 0, sizeof(sensor->pll));
+
+ pll->lanes = sensor->platform_data->lanes;
+ pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+
+ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
+ /*
+ * Fill in operational clock divisors limits from the
+ * video timing ones. On profile 0 sensors the
+ * requirements regarding them are essentially the
+ * same as on VT ones.
+ */
+ lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div;
+ lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div;
+ lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div;
+ lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div;
+ lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz;
+ lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz;
+ lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz;
+ lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz;
+ /* Profile 0 sensors have no separate OP clock branch. */
+ pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+ }
+
+ if (smiapp_needs_quirk(sensor,
+ SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
+ pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+
+ pll->binning_horizontal = sensor->binning_horizontal;
+ pll->binning_vertical = sensor->binning_vertical;
+ pll->link_freq =
+ sensor->link_freq->qmenu_int[sensor->link_freq->val];
+ pll->scale_m = sensor->scale_m;
+ pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ pll->bits_per_pixel = sensor->csi_format->compressed;
+
+ rval = smiapp_pll_calculate(&client->dev, &lim, pll);
+ if (rval < 0)
+ return rval;
+
+ sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz;
+ sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi;
+
+ return 0;
+}
+
+
+/*
+ *
+ * V4L2 Controls handling
+ *
+ */
+
+static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor)
+{
+ struct v4l2_ctrl *ctrl = sensor->exposure;
+ int max;
+
+ max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + sensor->vblank->val
+ - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN];
+
+ ctrl->maximum = max;
+ if (ctrl->default_value > max)
+ ctrl->default_value = max;
+ if (ctrl->val > max)
+ ctrl->val = max;
+ if (ctrl->cur.val > max)
+ ctrl->cur.val = max;
+}
+
+/*
+ * Order matters.
+ *
+ * 1. Bits-per-pixel, descending.
+ * 2. Bits-per-pixel compressed, descending.
+ * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel
+ * orders must be defined.
+ */
+static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = {
+ { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+};
+
+const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
+
+#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \
+ - (unsigned long)smiapp_csi_data_formats) \
+ / sizeof(*smiapp_csi_data_formats))
+
+static u32 smiapp_pixel_order(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int flip = 0;
+
+ if (sensor->hflip) {
+ if (sensor->hflip->val)
+ flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
+
+ if (sensor->vflip->val)
+ flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
+ }
+
+ flip ^= sensor->hvflip_inv_mask;
+
+ dev_dbg(&client->dev, "flip %d\n", flip);
+ return sensor->default_pixel_order ^ flip;
+}
+
+static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int csi_format_idx =
+ to_csi_format_idx(sensor->csi_format) & ~3;
+ unsigned int internal_csi_format_idx =
+ to_csi_format_idx(sensor->internal_csi_format) & ~3;
+ unsigned int pixel_order = smiapp_pixel_order(sensor);
+
+ sensor->mbus_frame_fmts =
+ sensor->default_mbus_frame_fmts << pixel_order;
+ sensor->csi_format =
+ &smiapp_csi_data_formats[csi_format_idx + pixel_order];
+ sensor->internal_csi_format =
+ &smiapp_csi_data_formats[internal_csi_format_idx
+ + pixel_order];
+
+ BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order
+ >= ARRAY_SIZE(smiapp_csi_data_formats));
+ BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0);
+
+ dev_dbg(&client->dev, "new pixel order %s\n",
+ pixel_order_str[pixel_order]);
+}
+
+static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct smiapp_sensor *sensor =
+ container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler)
+ ->sensor;
+ u32 orient = 0;
+ int exposure;
+ int rval;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ return smiapp_write(
+ sensor,
+ SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val);
+
+ case V4L2_CID_EXPOSURE:
+ return smiapp_write(
+ sensor,
+ SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ if (sensor->streaming)
+ return -EBUSY;
+
+ if (sensor->hflip->val)
+ orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
+
+ if (sensor->vflip->val)
+ orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
+
+ orient ^= sensor->hvflip_inv_mask;
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_IMAGE_ORIENTATION,
+ orient);
+ if (rval < 0)
+ return rval;
+
+ smiapp_update_mbus_formats(sensor);
+
+ return 0;
+
+ case V4L2_CID_VBLANK:
+ exposure = sensor->exposure->val;
+
+ __smiapp_update_exposure_limits(sensor);
+
+ if (exposure > sensor->exposure->maximum) {
+ sensor->exposure->val =
+ sensor->exposure->maximum;
+ rval = smiapp_set_ctrl(
+ sensor->exposure);
+ if (rval < 0)
+ return rval;
+ }
+
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + ctrl->val);
+
+ case V4L2_CID_HBLANK:
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
+ + ctrl->val);
+
+ case V4L2_CID_LINK_FREQ:
+ if (sensor->streaming)
+ return -EBUSY;
+
+ return smiapp_pll_update(sensor);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops smiapp_ctrl_ops = {
+ .s_ctrl = smiapp_set_ctrl,
+};
+
+static int smiapp_init_controls(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int max;
+ int rval;
+
+ rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7);
+ if (rval)
+ return rval;
+ sensor->pixel_array->ctrl_handler.lock = &sensor->mutex;
+
+ sensor->analog_gain = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN],
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX],
+ max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U),
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]);
+
+ /* Exposure limits will be updated soon, use just something here. */
+ sensor->exposure = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0, 1, 0);
+
+ sensor->hflip = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ sensor->vblank = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_VBLANK, 0, 1, 1, 0);
+
+ if (sensor->vblank)
+ sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ sensor->hblank = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_HBLANK, 0, 1, 1, 0);
+
+ if (sensor->hblank)
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ sensor->pixel_rate_parray = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
+
+ if (sensor->pixel_array->ctrl_handler.error) {
+ dev_err(&client->dev,
+ "pixel array controls initialization failed (%d)\n",
+ sensor->pixel_array->ctrl_handler.error);
+ rval = sensor->pixel_array->ctrl_handler.error;
+ goto error;
+ }
+
+ sensor->pixel_array->sd.ctrl_handler =
+ &sensor->pixel_array->ctrl_handler;
+
+ v4l2_ctrl_cluster(2, &sensor->hflip);
+
+ rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0);
+ if (rval)
+ goto error;
+ sensor->src->ctrl_handler.lock = &sensor->mutex;
+
+ for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++);
+
+ sensor->link_freq = v4l2_ctrl_new_int_menu(
+ &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_LINK_FREQ, max, 0,
+ sensor->platform_data->op_sys_clock);
+
+ sensor->pixel_rate_csi = v4l2_ctrl_new_std(
+ &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
+
+ if (sensor->src->ctrl_handler.error) {
+ dev_err(&client->dev,
+ "src controls initialization failed (%d)\n",
+ sensor->src->ctrl_handler.error);
+ rval = sensor->src->ctrl_handler.error;
+ goto error;
+ }
+
+ sensor->src->sd.ctrl_handler =
+ &sensor->src->ctrl_handler;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler);
+ v4l2_ctrl_handler_free(&sensor->src->ctrl_handler);
+
+ return rval;
+}
+
+static void smiapp_free_controls(struct smiapp_sensor *sensor)
+{
+ unsigned int i;
+
+ for (i = 0; i < sensor->ssds_used; i++)
+ v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler);
+}
+
+static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,
+ unsigned int n)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int i;
+ u32 val;
+ int rval;
+
+ for (i = 0; i < n; i++) {
+ rval = smiapp_read(
+ sensor, smiapp_reg_limits[limit[i]].addr, &val);
+ if (rval)
+ return rval;
+ sensor->limits[limit[i]] = val;
+ dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limit[i]].addr,
+ smiapp_reg_limits[limit[i]].what, val, val);
+ }
+
+ return 0;
+}
+
+static int smiapp_get_all_limits(struct smiapp_sensor *sensor)
+{
+ unsigned int i;
+ int rval;
+
+ for (i = 0; i < SMIAPP_LIMIT_LAST; i++) {
+ rval = smiapp_get_limits(sensor, &i, 1);
+ if (rval < 0)
+ return rval;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0)
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16);
+
+ return 0;
+}
+
+static int smiapp_get_limits_binning(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ static u32 const limits[] = {
+ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN,
+ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN,
+ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN,
+ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN,
+ };
+ static u32 const limits_replace[] = {
+ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES,
+ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES,
+ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK,
+ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN,
+ };
+ unsigned int i;
+ int rval;
+
+ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] ==
+ SMIAPP_BINNING_CAPABILITY_NO) {
+ for (i = 0; i < ARRAY_SIZE(limits); i++)
+ sensor->limits[limits[i]] =
+ sensor->limits[limits_replace[i]];
+
+ return 0;
+ }
+
+ rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits));
+ if (rval < 0)
+ return rval;
+
+ /*
+ * Sanity check whether the binning limits are valid. If not,
+ * use the non-binning ones.
+ */
+ if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]
+ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]
+ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(limits); i++) {
+ dev_dbg(&client->dev,
+ "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limits[i]].addr,
+ smiapp_reg_limits[limits[i]].what,
+ sensor->limits[limits_replace[i]],
+ sensor->limits[limits_replace[i]]);
+ sensor->limits[limits[i]] =
+ sensor->limits[limits_replace[i]];
+ }
+
+ return 0;
+}
+
+static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int type, n;
+ unsigned int i, pixel_order;
+ int rval;
+
+ rval = smiapp_read(
+ sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type);
+ if (rval)
+ return rval;
+
+ dev_dbg(&client->dev, "data_format_model_type %d\n", type);
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER,
+ &pixel_order);
+ if (rval)
+ return rval;
+
+ if (pixel_order >= ARRAY_SIZE(pixel_order_str)) {
+ dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order);
+ return -EINVAL;
+ }
+
+ dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order,
+ pixel_order_str[pixel_order]);
+
+ switch (type) {
+ case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL:
+ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N;
+ break;
+ case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED:
+ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor->default_pixel_order = pixel_order;
+ sensor->mbus_frame_fmts = 0;
+
+ for (i = 0; i < n; i++) {
+ unsigned int fmt, j;
+
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt);
+ if (rval)
+ return rval;
+
+ dev_dbg(&client->dev, "bpp %d, compressed %d\n",
+ fmt >> 8, (u8)fmt);
+
+ for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {
+ const struct smiapp_csi_data_format *f =
+ &smiapp_csi_data_formats[j];
+
+ if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG)
+ continue;
+
+ if (f->width != fmt >> 8 || f->compressed != (u8)fmt)
+ continue;
+
+ dev_dbg(&client->dev, "jolly good! %d\n", j);
+
+ sensor->default_mbus_frame_fmts |= 1 << j;
+ if (!sensor->csi_format
+ || f->width > sensor->csi_format->width
+ || (f->width == sensor->csi_format->width
+ && f->compressed
+ > sensor->csi_format->compressed)) {
+ sensor->csi_format = f;
+ sensor->internal_csi_format = f;
+ }
+ }
+ }
+
+ if (!sensor->csi_format) {
+ dev_err(&client->dev, "no supported mbus code found\n");
+ return -EINVAL;
+ }
+
+ smiapp_update_mbus_formats(sensor);
+
+ return 0;
+}
+
+static void smiapp_update_blanking(struct smiapp_sensor *sensor)
+{
+ struct v4l2_ctrl *vblank = sensor->vblank;
+ struct v4l2_ctrl *hblank = sensor->hblank;
+
+ vblank->minimum =
+ max_t(int,
+ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES],
+ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height);
+ vblank->maximum =
+ sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height;
+
+ vblank->val = clamp_t(int, vblank->val,
+ vblank->minimum, vblank->maximum);
+ vblank->default_value = vblank->minimum;
+ vblank->val = vblank->val;
+ vblank->cur.val = vblank->val;
+
+ hblank->minimum =
+ max_t(int,
+ sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width,
+ sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]);
+ hblank->maximum =
+ sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width;
+
+ hblank->val = clamp_t(int, hblank->val,
+ hblank->minimum, hblank->maximum);
+ hblank->default_value = hblank->minimum;
+ hblank->val = hblank->val;
+ hblank->cur.val = hblank->val;
+
+ __smiapp_update_exposure_limits(sensor);
+}
+
+static int smiapp_update_mode(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int binning_mode;
+ int rval;
+
+ dev_dbg(&client->dev, "frame size: %dx%d\n",
+ sensor->src->crop[SMIAPP_PAD_SRC].width,
+ sensor->src->crop[SMIAPP_PAD_SRC].height);
+ dev_dbg(&client->dev, "csi format width: %d\n",
+ sensor->csi_format->width);
+
+ /* Binning has to be set up here; it affects limits */
+ if (sensor->binning_horizontal == 1 &&
+ sensor->binning_vertical == 1) {
+ binning_mode = 0;
+ } else {
+ u8 binning_type =
+ (sensor->binning_horizontal << 4)
+ | sensor->binning_vertical;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type);
+ if (rval < 0)
+ return rval;
+
+ binning_mode = 1;
+ }
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode);
+ if (rval < 0)
+ return rval;
+
+ /* Get updated limits due to binning */
+ rval = smiapp_get_limits_binning(sensor);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_pll_update(sensor);
+ if (rval < 0)
+ return rval;
+
+ /* Output from pixel array, including blanking */
+ smiapp_update_blanking(sensor);
+
+ dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val);
+ dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
+
+ dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
+ sensor->pll.vt_pix_clk_freq_hz /
+ ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
+ + sensor->hblank->val) *
+ (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + sensor->vblank->val) / 100));
+
+ return 0;
+}
+
+/*
+ *
+ * SMIA++ NVM handling
+ *
+ */
+static int smiapp_read_nvm(struct smiapp_sensor *sensor,
+ unsigned char *nvm)
+{
+ u32 i, s, p, np, v;
+ int rval = 0, rval2;
+
+ np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE;
+ for (p = 0; p < np; p++) {
+ rval = smiapp_write(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p);
+ if (rval)
+ goto out;
+
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL,
+ SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN |
+ SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN);
+ if (rval)
+ goto out;
+
+ for (i = 0; i < 1000; i++) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s);
+
+ if (rval)
+ goto out;
+
+ if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY)
+ break;
+
+ if (--i == 0) {
+ rval = -ETIMEDOUT;
+ goto out;
+ }
+
+ }
+
+ for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i,
+ &v);
+ if (rval)
+ goto out;
+
+ *nvm++ = v;
+ }
+ }
+
+out:
+ rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0);
+ if (rval < 0)
+ return rval;
+ else
+ return rval2;
+}
+
+/*
+ *
+ * SMIA++ CCI address control
+ *
+ */
+static int smiapp_change_cci_addr(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+ u32 val;
+
+ client->addr = sensor->platform_data->i2c_addr_dfl;
+
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_CCI_ADDRESS_CONTROL,
+ sensor->platform_data->i2c_addr_alt << 1);
+ if (rval)
+ return rval;
+
+ client->addr = sensor->platform_data->i2c_addr_alt;
+
+ /* verify addr change went ok */
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val);
+ if (rval)
+ return rval;
+
+ if (val != sensor->platform_data->i2c_addr_alt << 1)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ *
+ * SMIA++ Mode Control
+ *
+ */
+static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor)
+{
+ struct smiapp_flash_strobe_parms *strobe_setup;
+ unsigned int ext_freq = sensor->platform_data->ext_clk;
+ u32 tmp;
+ u32 strobe_adjustment;
+ u32 strobe_width_high_rs;
+ int rval;
+
+ strobe_setup = sensor->platform_data->strobe_setup;
+
+ /*
+ * How to calculate registers related to strobe length. Please
+ * do not change, or if you do at least know what you're
+ * doing. :-)
+ *
+ * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> 2010-10-25
+ *
+ * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl
+ * / EXTCLK freq [Hz]) * flash_strobe_adjustment
+ *
+ * tFlash_strobe_width_ctrl E N, [1 - 0xffff]
+ * flash_strobe_adjustment E N, [1 - 0xff]
+ *
+ * The formula above is written as below to keep it on one
+ * line:
+ *
+ * l / 10^6 = w / e * a
+ *
+ * Let's mark w * a by x:
+ *
+ * x = w * a
+ *
+ * Thus, we get:
+ *
+ * x = l * e / 10^6
+ *
+ * The strobe width must be at least as long as requested,
+ * thus rounding upwards is needed.
+ *
+ * x = (l * e + 10^6 - 1) / 10^6
+ * -----------------------------
+ *
+ * Maximum possible accuracy is wanted at all times. Thus keep
+ * a as small as possible.
+ *
+ * Calculate a, assuming maximum w, with rounding upwards:
+ *
+ * a = (x + (2^16 - 1) - 1) / (2^16 - 1)
+ * -------------------------------------
+ *
+ * Thus, we also get w, with that a, with rounding upwards:
+ *
+ * w = (x + a - 1) / a
+ * -------------------
+ *
+ * To get limits:
+ *
+ * x E [1, (2^16 - 1) * (2^8 - 1)]
+ *
+ * Substituting maximum x to the original formula (with rounding),
+ * the maximum l is thus
+ *
+ * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1
+ *
+ * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e
+ * --------------------------------------------------
+ *
+ * flash_strobe_length must be clamped between 1 and
+ * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq.
+ *
+ * Then,
+ *
+ * flash_strobe_adjustment = ((flash_strobe_length *
+ * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1)
+ *
+ * tFlash_strobe_width_ctrl = ((flash_strobe_length *
+ * EXTCLK freq + 10^6 - 1) / 10^6 +
+ * flash_strobe_adjustment - 1) / flash_strobe_adjustment
+ */
+ tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) -
+ 1000000 + 1, ext_freq);
+ strobe_setup->strobe_width_high_us =
+ clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp);
+
+ tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq +
+ 1000000 - 1), 1000000ULL);
+ strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1);
+ strobe_width_high_rs = (tmp + strobe_adjustment - 1) /
+ strobe_adjustment;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS,
+ strobe_setup->mode);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT,
+ strobe_adjustment);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL,
+ strobe_width_high_rs);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL,
+ strobe_setup->strobe_delay);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT,
+ strobe_setup->stobe_start_point);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS,
+ strobe_setup->trigger);
+
+out:
+ sensor->platform_data->strobe_setup->trigger = 0;
+
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int smiapp_power_on(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int sleep;
+ int rval;
+
+ rval = regulator_enable(sensor->vana);
+ if (rval) {
+ dev_err(&client->dev, "failed to enable vana regulator\n");
+ return rval;
+ }
+ usleep_range(1000, 1000);
+
+ if (sensor->platform_data->set_xclk)
+ rval = sensor->platform_data->set_xclk(
+ &sensor->src->sd, sensor->platform_data->ext_clk);
+ else
+ rval = clk_enable(sensor->ext_clk);
+ if (rval < 0) {
+ dev_dbg(&client->dev, "failed to set xclk\n");
+ goto out_xclk_fail;
+ }
+ usleep_range(1000, 1000);
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 1);
+
+ sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk);
+ usleep_range(sleep, sleep);
+
+ /*
+ * Failures to respond to the address change command have been noticed.
+ * Those failures seem to be caused by the sensor requiring a longer
+ * boot time than advertised. An additional 10ms delay seems to work
+ * around the issue, but the SMIA++ I2C write retry hack makes the delay
+ * unnecessary. The failures need to be investigated to find a proper
+ * fix, and a delay will likely need to be added here if the I2C write
+ * retry hack is reverted before the root cause of the boot time issue
+ * is found.
+ */
+
+ if (sensor->platform_data->i2c_addr_alt) {
+ rval = smiapp_change_cci_addr(sensor);
+ if (rval) {
+ dev_err(&client->dev, "cci address change error\n");
+ goto out_cci_addr_fail;
+ }
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET,
+ SMIAPP_SOFTWARE_RESET);
+ if (rval < 0) {
+ dev_err(&client->dev, "software reset failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ if (sensor->platform_data->i2c_addr_alt) {
+ rval = smiapp_change_cci_addr(sensor);
+ if (rval) {
+ dev_err(&client->dev, "cci address change error\n");
+ goto out_cci_addr_fail;
+ }
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE,
+ SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR);
+ if (rval) {
+ dev_err(&client->dev, "compression mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ,
+ sensor->platform_data->ext_clk / (1000000 / (1 << 8)));
+ if (rval) {
+ dev_err(&client->dev, "extclk frequency set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE,
+ sensor->platform_data->lanes - 1);
+ if (rval) {
+ dev_err(&client->dev, "csi lane mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL,
+ SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE);
+ if (rval) {
+ dev_err(&client->dev, "fast standby set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE,
+ sensor->platform_data->csi_signalling_mode);
+ if (rval) {
+ dev_err(&client->dev, "csi signalling mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ /* DPHY control done by sensor based on requested link rate */
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL,
+ SMIAPP_DPHY_CTRL_UI);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_call_quirk(sensor, post_poweron);
+ if (rval) {
+ dev_err(&client->dev, "post_poweron quirks failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ /* Are we still initialising...? If yes, return here. */
+ if (!sensor->pixel_array)
+ return 0;
+
+ rval = v4l2_ctrl_handler_setup(
+ &sensor->pixel_array->ctrl_handler);
+ if (rval)
+ goto out_cci_addr_fail;
+
+ rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
+ if (rval)
+ goto out_cci_addr_fail;
+
+ mutex_lock(&sensor->mutex);
+ rval = smiapp_update_mode(sensor);
+ mutex_unlock(&sensor->mutex);
+ if (rval < 0)
+ goto out_cci_addr_fail;
+
+ return 0;
+
+out_cci_addr_fail:
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+
+out_xclk_fail:
+ regulator_disable(sensor->vana);
+ return rval;
+}
+
+static void smiapp_power_off(struct smiapp_sensor *sensor)
+{
+ /*
+ * Currently power/clock to lens are enable/disabled separately
+ * but they are essentially the same signals. So if the sensor is
+ * powered off while the lens is powered on the sensor does not
+ * really see a power off and next time the cci address change
+ * will fail. So do a soft reset explicitly here.
+ */
+ if (sensor->platform_data->i2c_addr_alt)
+ smiapp_write(sensor,
+ SMIAPP_REG_U8_SOFTWARE_RESET,
+ SMIAPP_SOFTWARE_RESET);
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+ usleep_range(5000, 5000);
+ regulator_disable(sensor->vana);
+ sensor->streaming = 0;
+}
+
+static int smiapp_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int ret = 0;
+
+ mutex_lock(&sensor->power_mutex);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0
+ * to 0, update the power state.
+ */
+ if (!sensor->power_count == !on)
+ goto out;
+
+ if (on) {
+ /* Power on and perform initialisation. */
+ ret = smiapp_power_on(sensor);
+ if (ret < 0)
+ goto out;
+ } else {
+ smiapp_power_off(sensor);
+ }
+
+ /* Update the power count. */
+ sensor->power_count += on ? 1 : -1;
+ WARN_ON(sensor->power_count < 0);
+
+out:
+ mutex_unlock(&sensor->power_mutex);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video stream management
+ */
+
+static int smiapp_start_streaming(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT,
+ (sensor->csi_format->width << 8) |
+ sensor->csi_format->compressed);
+ if (rval)
+ goto out;
+
+ rval = smiapp_pll_configure(sensor);
+ if (rval)
+ goto out;
+
+ /* Analog crop start coordinates */
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top);
+ if (rval < 0)
+ goto out;
+
+ /* Analog crop end coordinates */
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_X_ADDR_END,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left
+ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_Y_ADDR_END,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top
+ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1);
+ if (rval < 0)
+ goto out;
+
+ /*
+ * Output from pixel array, including blanking, is set using
+ * controls below. No need to set here.
+ */
+
+ /* Digital crop */
+ if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].left);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].top);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].width);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].height);
+ if (rval < 0)
+ goto out;
+ }
+
+ /* Scaling */
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE) {
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE,
+ sensor->scaling_mode);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M,
+ sensor->scale_m);
+ if (rval < 0)
+ goto out;
+ }
+
+ /* Output size from sensor */
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE,
+ sensor->src->crop[SMIAPP_PAD_SRC].width);
+ if (rval < 0)
+ goto out;
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE,
+ sensor->src->crop[SMIAPP_PAD_SRC].height);
+ if (rval < 0)
+ goto out;
+
+ if ((sensor->flash_capability &
+ (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
+ SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) &&
+ sensor->platform_data->strobe_setup != NULL &&
+ sensor->platform_data->strobe_setup->trigger != 0) {
+ rval = smiapp_setup_flash_strobe(sensor);
+ if (rval)
+ goto out;
+ }
+
+ rval = smiapp_call_quirk(sensor, pre_streamon);
+ if (rval) {
+ dev_err(&client->dev, "pre_streamon quirks failed\n");
+ goto out;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
+ SMIAPP_MODE_SELECT_STREAMING);
+
+out:
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static int smiapp_stop_streaming(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
+ SMIAPP_MODE_SELECT_SOFTWARE_STANDBY);
+ if (rval)
+ goto out;
+
+ rval = smiapp_call_quirk(sensor, post_streamoff);
+ if (rval)
+ dev_err(&client->dev, "post_streamoff quirks failed\n");
+
+out:
+ mutex_unlock(&sensor->mutex);
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ if (sensor->streaming == enable)
+ return 0;
+
+ if (enable) {
+ sensor->streaming = 1;
+ rval = smiapp_start_streaming(sensor);
+ if (rval < 0)
+ sensor->streaming = 0;
+ } else {
+ rval = smiapp_stop_streaming(sensor);
+ sensor->streaming = 0;
+ }
+
+ return rval;
+}
+
+static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+ int idx = -1;
+ int rval = -EINVAL;
+
+ mutex_lock(&sensor->mutex);
+
+ dev_err(&client->dev, "subdev %s, pad %d, index %d\n",
+ subdev->name, code->pad, code->index);
+
+ if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) {
+ if (code->index)
+ goto out;
+
+ code->code = sensor->internal_csi_format->code;
+ rval = 0;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & (1 << i))
+ idx++;
+
+ if (idx == code->index) {
+ code->code = smiapp_csi_data_formats[i].code;
+ dev_err(&client->dev, "found index %d, i %d, code %x\n",
+ code->index, i, code->code);
+ rval = 0;
+ break;
+ }
+ }
+
+out:
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev,
+ unsigned int pad)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+
+ if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC)
+ return sensor->csi_format->code;
+ else
+ return sensor->internal_csi_format->code;
+}
+
+static int __smiapp_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
+ } else {
+ struct v4l2_rect *r;
+
+ if (fmt->pad == ssd->source_pad)
+ r = &ssd->crop[ssd->source_pad];
+ else
+ r = &ssd->sink_fmt;
+
+ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
+ fmt->format.width = r->width;
+ fmt->format.height = r->height;
+ }
+
+ return 0;
+}
+
+static int smiapp_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = __smiapp_get_format(subdev, fh, fmt);
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static void smiapp_get_crop_compose(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_rect **crops,
+ struct v4l2_rect **comps, int which)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ unsigned int i;
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (crops)
+ for (i = 0; i < subdev->entity.num_pads; i++)
+ crops[i] = &ssd->crop[i];
+ if (comps)
+ *comps = &ssd->compose;
+ } else {
+ if (crops) {
+ for (i = 0; i < subdev->entity.num_pads; i++) {
+ crops[i] = v4l2_subdev_get_try_crop(fh, i);
+ BUG_ON(!crops[i]);
+ }
+ }
+ if (comps) {
+ *comps = v4l2_subdev_get_try_compose(fh,
+ SMIAPP_PAD_SINK);
+ BUG_ON(!*comps);
+ }
+ }
+}
+
+/* Changes require propagation only on sink pad. */
+static void smiapp_propagate(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh, int which,
+ int target)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
+
+ switch (target) {
+ case V4L2_SEL_TGT_CROP:
+ comp->width = crops[SMIAPP_PAD_SINK]->width;
+ comp->height = crops[SMIAPP_PAD_SINK]->height;
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (ssd == sensor->scaler) {
+ sensor->scale_m =
+ sensor->limits[
+ SMIAPP_LIMIT_SCALER_N_MIN];
+ sensor->scaling_mode =
+ SMIAPP_SCALING_MODE_NONE;
+ } else if (ssd == sensor->binner) {
+ sensor->binning_horizontal = 1;
+ sensor->binning_vertical = 1;
+ }
+ }
+ /* Fall through */
+ case V4L2_SEL_TGT_COMPOSE:
+ *crops[SMIAPP_PAD_SRC] = *comp;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static const struct smiapp_csi_data_format
+*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code)
+{
+ const struct smiapp_csi_data_format *csi_format = sensor->csi_format;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & (1 << i)
+ && smiapp_csi_data_formats[i].code == code)
+ return &smiapp_csi_data_formats[i];
+ }
+
+ return csi_format;
+}
+
+static int smiapp_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *crops[SMIAPP_PADS];
+
+ mutex_lock(&sensor->mutex);
+
+ /*
+ * Media bus code is changeable on src subdev's source pad. On
+ * other source pads we just get format here.
+ */
+ if (fmt->pad == ssd->source_pad) {
+ u32 code = fmt->format.code;
+ int rval = __smiapp_get_format(subdev, fh, fmt);
+
+ if (!rval && subdev == &sensor->src->sd) {
+ const struct smiapp_csi_data_format *csi_format =
+ smiapp_validate_csi_data_format(sensor, code);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ sensor->csi_format = csi_format;
+ fmt->format.code = csi_format->code;
+ }
+
+ mutex_unlock(&sensor->mutex);
+ return rval;
+ }
+
+ /* Sink pad. Width and height are changeable here. */
+ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
+ fmt->format.width &= ~1;
+ fmt->format.height &= ~1;
+
+ fmt->format.width =
+ clamp(fmt->format.width,
+ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
+ sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]);
+ fmt->format.height =
+ clamp(fmt->format.height,
+ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
+ sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]);
+
+ smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which);
+
+ crops[ssd->sink_pad]->left = 0;
+ crops[ssd->sink_pad]->top = 0;
+ crops[ssd->sink_pad]->width = fmt->format.width;
+ crops[ssd->sink_pad]->height = fmt->format.height;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ssd->sink_fmt = *crops[ssd->sink_pad];
+ smiapp_propagate(subdev, fh, fmt->which,
+ V4L2_SEL_TGT_CROP);
+
+ mutex_unlock(&sensor->mutex);
+
+ return 0;
+}
+
+/*
+ * Calculate goodness of scaled image size compared to expected image
+ * size and flags provided.
+ */
+#define SCALING_GOODNESS 100000
+#define SCALING_GOODNESS_EXTREME 100000000
+static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
+ int h, int ask_h, u32 flags)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ int val = 0;
+
+ w &= ~1;
+ ask_w &= ~1;
+ h &= ~1;
+ ask_h &= ~1;
+
+ if (flags & V4L2_SEL_FLAG_GE) {
+ if (w < ask_w)
+ val -= SCALING_GOODNESS;
+ if (h < ask_h)
+ val -= SCALING_GOODNESS;
+ }
+
+ if (flags & V4L2_SEL_FLAG_LE) {
+ if (w > ask_w)
+ val -= SCALING_GOODNESS;
+ if (h > ask_h)
+ val -= SCALING_GOODNESS;
+ }
+
+ val -= abs(w - ask_w);
+ val -= abs(h - ask_h);
+
+ if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE])
+ val -= SCALING_GOODNESS_EXTREME;
+
+ dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n",
+ w, ask_h, h, ask_h, val);
+
+ return val;
+}
+
+static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel,
+ struct v4l2_rect **crops,
+ struct v4l2_rect *comp)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+ unsigned int binh = 1, binv = 1;
+ unsigned int best = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width, sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags);
+
+ for (i = 0; i < sensor->nbinning_subtypes; i++) {
+ int this = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width
+ / sensor->binning_subtypes[i].horizontal,
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height
+ / sensor->binning_subtypes[i].vertical,
+ sel->r.height, sel->flags);
+
+ if (this > best) {
+ binh = sensor->binning_subtypes[i].horizontal;
+ binv = sensor->binning_subtypes[i].vertical;
+ best = this;
+ }
+ }
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sensor->binning_vertical = binv;
+ sensor->binning_horizontal = binh;
+ }
+
+ sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1;
+ sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1;
+}
+
+/*
+ * Calculate best scaling ratio and mode for given output resolution.
+ *
+ * Try all of these: horizontal ratio, vertical ratio and smallest
+ * size possible (horizontally).
+ *
+ * Also try whether horizontal scaler or full scaler gives a better
+ * result.
+ */
+static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel,
+ struct v4l2_rect **crops,
+ struct v4l2_rect *comp)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ u32 min, max, a, b, max_m;
+ u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ int mode = SMIAPP_SCALING_MODE_HORIZONTAL;
+ u32 try[4];
+ u32 ntry = 0;
+ unsigned int i;
+ int best = INT_MIN;
+
+ sel->r.width = min_t(unsigned int, sel->r.width,
+ crops[SMIAPP_PAD_SINK]->width);
+ sel->r.height = min_t(unsigned int, sel->r.height,
+ crops[SMIAPP_PAD_SINK]->height);
+
+ a = crops[SMIAPP_PAD_SINK]->width
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width;
+ b = crops[SMIAPP_PAD_SINK]->height
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height;
+ max_m = crops[SMIAPP_PAD_SINK]->width
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]
+ / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE];
+
+ a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+ b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+ max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+
+ dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m);
+
+ min = min(max_m, min(a, b));
+ max = min(max_m, max(a, b));
+
+ try[ntry] = min;
+ ntry++;
+ if (min != max) {
+ try[ntry] = max;
+ ntry++;
+ }
+ if (max != max_m) {
+ try[ntry] = min + 1;
+ ntry++;
+ if (min != max) {
+ try[ntry] = max + 1;
+ ntry++;
+ }
+ }
+
+ for (i = 0; i < ntry; i++) {
+ int this = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height,
+ sel->r.height,
+ sel->flags);
+
+ dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i);
+
+ if (this > best) {
+ scale_m = try[i];
+ mode = SMIAPP_SCALING_MODE_HORIZONTAL;
+ best = this;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
+ continue;
+
+ this = scaling_goodness(
+ subdev, crops[SMIAPP_PAD_SINK]->width
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.height,
+ sel->flags);
+
+ if (this > best) {
+ scale_m = try[i];
+ mode = SMIAPP_SCALING_MODE_BOTH;
+ best = this;
+ }
+ }
+
+ sel->r.width =
+ (crops[SMIAPP_PAD_SINK]->width
+ / scale_m
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1;
+ if (mode == SMIAPP_SCALING_MODE_BOTH)
+ sel->r.height =
+ (crops[SMIAPP_PAD_SINK]->height
+ / scale_m
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN])
+ & ~1;
+ else
+ sel->r.height = crops[SMIAPP_PAD_SINK]->height;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sensor->scale_m = scale_m;
+ sensor->scaling_mode = mode;
+ }
+}
+/* We're only called on source pads. This function sets scaling. */
+static int smiapp_set_compose(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+
+ sel->r.top = 0;
+ sel->r.left = 0;
+
+ if (ssd == sensor->binner)
+ smiapp_set_compose_binner(subdev, fh, sel, crops, comp);
+ else
+ smiapp_set_compose_scaler(subdev, fh, sel, crops, comp);
+
+ *comp = sel->r;
+ smiapp_propagate(subdev, fh, sel->which,
+ V4L2_SEL_TGT_COMPOSE);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return smiapp_update_mode(sensor);
+
+ return 0;
+}
+
+static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+
+ /* We only implement crop in three places. */
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (ssd == sensor->pixel_array
+ && sel->pad == SMIAPP_PA_PAD_SRC)
+ return 0;
+ if (ssd == sensor->src
+ && sel->pad == SMIAPP_PAD_SRC)
+ return 0;
+ if (ssd == sensor->scaler
+ && sel->pad == SMIAPP_PAD_SINK
+ && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
+ return 0;
+ return -EINVAL;
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ if (sel->pad == ssd->source_pad)
+ return -EINVAL;
+ if (ssd == sensor->binner)
+ return 0;
+ if (ssd == sensor->scaler
+ && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE)
+ return 0;
+ /* Fall through */
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smiapp_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *src_size, *crops[SMIAPP_PADS];
+ struct v4l2_rect _r;
+
+ smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (sel->pad == ssd->sink_pad)
+ src_size = &ssd->sink_fmt;
+ else
+ src_size = &ssd->compose;
+ } else {
+ if (sel->pad == ssd->sink_pad) {
+ _r.left = 0;
+ _r.top = 0;
+ _r.width = v4l2_subdev_get_try_format(fh, sel->pad)
+ ->width;
+ _r.height = v4l2_subdev_get_try_format(fh, sel->pad)
+ ->height;
+ src_size = &_r;
+ } else {
+ src_size =
+ v4l2_subdev_get_try_compose(
+ fh, ssd->sink_pad);
+ }
+ }
+
+ if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ }
+
+ sel->r.width = min(sel->r.width, src_size->width);
+ sel->r.height = min(sel->r.height, src_size->height);
+
+ sel->r.left = min(sel->r.left, src_size->width - sel->r.width);
+ sel->r.top = min(sel->r.top, src_size->height - sel->r.height);
+
+ *crops[sel->pad] = sel->r;
+
+ if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
+ smiapp_propagate(subdev, fh, sel->which,
+ V4L2_SEL_TGT_CROP);
+
+ return 0;
+}
+
+static int __smiapp_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+ struct v4l2_rect sink_fmt;
+ int ret;
+
+ ret = __smiapp_sel_supported(subdev, sel);
+ if (ret)
+ return ret;
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sink_fmt = ssd->sink_fmt;
+ } else {
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(fh, ssd->sink_pad);
+
+ sink_fmt.left = 0;
+ sink_fmt.top = 0;
+ sink_fmt.width = fmt->width;
+ sink_fmt.height = fmt->height;
+ }
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (ssd == sensor->pixel_array) {
+ sel->r.width =
+ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ sel->r.height =
+ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ } else if (sel->pad == ssd->sink_pad) {
+ sel->r = sink_fmt;
+ } else {
+ sel->r = *comp;
+ }
+ break;
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ sel->r = *crops[sel->pad];
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = *comp;
+ break;
+ }
+
+ return 0;
+}
+
+static int smiapp_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = __smiapp_get_selection(subdev, fh, sel);
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+static int smiapp_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int ret;
+
+ ret = __smiapp_sel_supported(subdev, sel);
+ if (ret)
+ return ret;
+
+ mutex_lock(&sensor->mutex);
+
+ sel->r.left = max(0, sel->r.left & ~1);
+ sel->r.top = max(0, sel->r.top & ~1);
+ sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags));
+ sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags));
+
+ sel->r.width = max_t(unsigned int,
+ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
+ sel->r.width);
+ sel->r.height = max_t(unsigned int,
+ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
+ sel->r.height);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ ret = smiapp_set_crop(subdev, fh, sel);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ ret = smiapp_set_compose(subdev, fh, sel);
+ break;
+ default:
+ BUG();
+ }
+
+ mutex_unlock(&sensor->mutex);
+ return ret;
+}
+
+static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+
+ *frames = sensor->frame_skip;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * sysfs attributes
+ */
+
+static ssize_t
+smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int nbytes;
+
+ if (!sensor->dev_init_done)
+ return -EBUSY;
+
+ if (!sensor->nvm_size) {
+ /* NVM not read yet - read it now */
+ sensor->nvm_size = sensor->platform_data->nvm_size;
+ if (smiapp_set_power(subdev, 1) < 0)
+ return -ENODEV;
+ if (smiapp_read_nvm(sensor, sensor->nvm)) {
+ dev_err(&client->dev, "nvm read failed\n");
+ return -ENODEV;
+ }
+ smiapp_set_power(subdev, 0);
+ }
+ /*
+ * NVM is still way below a PAGE_SIZE, so we can safely
+ * assume this for now.
+ */
+ nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE);
+ memcpy(buf, sensor->nvm, nbytes);
+
+ return nbytes;
+}
+static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL);
+
+static ssize_t
+smiapp_sysfs_ident_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_module_info *minfo = &sensor->minfo;
+
+ return snprintf(buf, PAGE_SIZE, "%2.2x%4.4x%2.2x\n",
+ minfo->manufacturer_id, minfo->model_id,
+ minfo->revision_number_major) + 1;
+}
+
+static DEVICE_ATTR(ident, S_IRUGO, smiapp_sysfs_ident_read, NULL);
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int smiapp_identify_module(struct v4l2_subdev *subdev)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_module_info *minfo = &sensor->minfo;
+ unsigned int i;
+ int rval = 0;
+
+ minfo->name = SMIAPP_NAME;
+
+ /* Module info */
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID,
+ &minfo->manufacturer_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID,
+ &minfo->model_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_REVISION_NUMBER_MAJOR,
+ &minfo->revision_number_major);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_REVISION_NUMBER_MINOR,
+ &minfo->revision_number_minor);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_MODULE_DATE_YEAR,
+ &minfo->module_year);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_MODULE_DATE_MONTH,
+ &minfo->module_month);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY,
+ &minfo->module_day);
+
+ /* Sensor info */
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID,
+ &minfo->sensor_manufacturer_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U16_SENSOR_MODEL_ID,
+ &minfo->sensor_model_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_REVISION_NUMBER,
+ &minfo->sensor_revision_number);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION,
+ &minfo->sensor_firmware_version);
+
+ /* SMIA */
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION,
+ &minfo->smia_version);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION,
+ &minfo->smiapp_version);
+
+ if (rval) {
+ dev_err(&client->dev, "sensor detection failed\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n",
+ minfo->manufacturer_id, minfo->model_id);
+
+ dev_dbg(&client->dev,
+ "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n",
+ minfo->revision_number_major, minfo->revision_number_minor,
+ minfo->module_year, minfo->module_month, minfo->module_day);
+
+ dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n",
+ minfo->sensor_manufacturer_id, minfo->sensor_model_id);
+
+ dev_dbg(&client->dev,
+ "sensor revision 0x%2.2x firmware version 0x%2.2x\n",
+ minfo->sensor_revision_number, minfo->sensor_firmware_version);
+
+ dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n",
+ minfo->smia_version, minfo->smiapp_version);
+
+ /*
+ * Some modules have bad data in the lvalues below. Hope the
+ * rvalues have better stuff. The lvalues are module
+ * parameters whereas the rvalues are sensor parameters.
+ */
+ if (!minfo->manufacturer_id && !minfo->model_id) {
+ minfo->manufacturer_id = minfo->sensor_manufacturer_id;
+ minfo->model_id = minfo->sensor_model_id;
+ minfo->revision_number_major = minfo->sensor_revision_number;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) {
+ if (smiapp_module_idents[i].manufacturer_id
+ != minfo->manufacturer_id)
+ continue;
+ if (smiapp_module_idents[i].model_id != minfo->model_id)
+ continue;
+ if (smiapp_module_idents[i].flags
+ & SMIAPP_MODULE_IDENT_FLAG_REV_LE) {
+ if (smiapp_module_idents[i].revision_number_major
+ < minfo->revision_number_major)
+ continue;
+ } else {
+ if (smiapp_module_idents[i].revision_number_major
+ != minfo->revision_number_major)
+ continue;
+ }
+
+ minfo->name = smiapp_module_idents[i].name;
+ minfo->quirk = smiapp_module_idents[i].quirk;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(smiapp_module_idents))
+ dev_warn(&client->dev,
+ "no quirks for this module; let's hope it's fully compliant\n");
+
+ dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n",
+ minfo->name, minfo->manufacturer_id, minfo->model_id,
+ minfo->revision_number_major);
+
+ strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name));
+
+ return 0;
+}
+
+static const struct v4l2_subdev_ops smiapp_ops;
+static const struct v4l2_subdev_internal_ops smiapp_internal_ops;
+static const struct media_entity_operations smiapp_entity_ops;
+
+static int smiapp_registered(struct v4l2_subdev *subdev)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_subdev *last = NULL;
+ u32 tmp;
+ unsigned int i;
+ int rval;
+
+ sensor->vana = devm_regulator_get(&client->dev, "VANA");
+ if (IS_ERR(sensor->vana)) {
+ dev_err(&client->dev, "could not get regulator for vana\n");
+ return -ENODEV;
+ }
+
+ if (!sensor->platform_data->set_xclk) {
+ sensor->ext_clk = devm_clk_get(&client->dev,
+ sensor->platform_data->ext_clk_name);
+ if (IS_ERR(sensor->ext_clk)) {
+ dev_err(&client->dev, "could not get clock %s\n",
+ sensor->platform_data->ext_clk_name);
+ return -ENODEV;
+ }
+
+ rval = clk_set_rate(sensor->ext_clk,
+ sensor->platform_data->ext_clk);
+ if (rval < 0) {
+ dev_err(&client->dev,
+ "unable to set clock %s freq to %u\n",
+ sensor->platform_data->ext_clk_name,
+ sensor->platform_data->ext_clk);
+ return -ENODEV;
+ }
+ }
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
+ if (gpio_request_one(sensor->platform_data->xshutdown, 0,
+ "SMIA++ xshutdown") != 0) {
+ dev_err(&client->dev,
+ "unable to acquire reset gpio %d\n",
+ sensor->platform_data->xshutdown);
+ return -ENODEV;
+ }
+ }
+
+ rval = smiapp_power_on(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_smiapp_power_on;
+ }
+
+ rval = smiapp_identify_module(subdev);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ rval = smiapp_get_all_limits(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ /*
+ * Handle Sensor Module orientation on the board.
+ *
+ * The application of H-FLIP and V-FLIP on the sensor is modified by
+ * the sensor orientation on the board.
+ *
+ * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set
+ * both H-FLIP and V-FLIP for normal operation which also implies
+ * that a set/unset operation for user space HFLIP and VFLIP v4l2
+ * controls will need to be internally inverted.
+ *
+ * Rotation also changes the bayer pattern.
+ */
+ if (sensor->platform_data->module_board_orient ==
+ SMIAPP_MODULE_BOARD_ORIENT_180)
+ sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
+ SMIAPP_IMAGE_ORIENTATION_VFLIP;
+
+ rval = smiapp_get_mbus_formats(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
+ u32 val;
+
+ rval = smiapp_read(sensor,
+ SMIAPP_REG_U8_BINNING_SUBTYPES, &val);
+ if (rval < 0) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+ sensor->nbinning_subtypes = min_t(u8, val,
+ SMIAPP_BINNING_SUBTYPES);
+
+ for (i = 0; i < sensor->nbinning_subtypes; i++) {
+ rval = smiapp_read(
+ sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val);
+ if (rval < 0) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+ sensor->binning_subtypes[i] =
+ *(struct smiapp_binning_subtype *)&val;
+
+ dev_dbg(&client->dev, "binning %xx%x\n",
+ sensor->binning_subtypes[i].horizontal,
+ sensor->binning_subtypes[i].vertical);
+ }
+ }
+ sensor->binning_horizontal = 1;
+ sensor->binning_vertical = 1;
+
+ if (device_create_file(&client->dev, &dev_attr_ident) != 0) {
+ dev_err(&client->dev, "sysfs ident entry creation failed\n");
+ rval = -ENOENT;
+ goto out_power_off;
+ }
+ /* SMIA++ NVM initialization - it will be read from the sensor
+ * when it is first requested by userspace.
+ */
+ if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) {
+ sensor->nvm = devm_kzalloc(&client->dev,
+ sensor->platform_data->nvm_size, GFP_KERNEL);
+ if (sensor->nvm == NULL) {
+ dev_err(&client->dev, "nvm buf allocation failed\n");
+ rval = -ENOMEM;
+ goto out_ident_release;
+ }
+
+ if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
+ dev_err(&client->dev, "sysfs nvm entry failed\n");
+ rval = -EBUSY;
+ goto out_ident_release;
+ }
+ }
+
+ rval = smiapp_call_quirk(sensor, limits);
+ if (rval) {
+ dev_err(&client->dev, "limits quirks failed\n");
+ goto out_nvm_release;
+ }
+
+ /* We consider this as profile 0 sensor if any of these are zero. */
+ if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) {
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0;
+ } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE) {
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1;
+ else
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2;
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ }
+ sensor->binner = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+
+ sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+
+ for (i = 0; i < SMIAPP_SUBDEVS; i++) {
+ struct {
+ struct smiapp_subdev *ssd;
+ char *name;
+ } const __this[] = {
+ { sensor->scaler, "scaler", },
+ { sensor->binner, "binner", },
+ { sensor->pixel_array, "pixel array", },
+ }, *_this = &__this[i];
+ struct smiapp_subdev *this = _this->ssd;
+
+ if (!this)
+ continue;
+
+ if (this != sensor->src)
+ v4l2_subdev_init(&this->sd, &smiapp_ops);
+
+ this->sensor = sensor;
+
+ if (this == sensor->pixel_array) {
+ this->npads = 1;
+ } else {
+ this->npads = 2;
+ this->source_pad = 1;
+ }
+
+ snprintf(this->sd.name,
+ sizeof(this->sd.name), "%s %s",
+ sensor->minfo.name, _this->name);
+
+ this->sink_fmt.width =
+ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ this->sink_fmt.height =
+ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ this->compose.width = this->sink_fmt.width;
+ this->compose.height = this->sink_fmt.height;
+ this->crop[this->source_pad] = this->compose;
+ this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE;
+ if (this != sensor->pixel_array) {
+ this->crop[this->sink_pad] = this->compose;
+ this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK;
+ }
+
+ this->sd.entity.ops = &smiapp_entity_ops;
+
+ if (last == NULL) {
+ last = this;
+ continue;
+ }
+
+ this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ this->sd.internal_ops = &smiapp_internal_ops;
+ this->sd.owner = NULL;
+ v4l2_set_subdevdata(&this->sd, client);
+
+ rval = media_entity_init(&this->sd.entity,
+ this->npads, this->pads, 0);
+ if (rval) {
+ dev_err(&client->dev,
+ "media_entity_init failed\n");
+ goto out_nvm_release;
+ }
+
+ rval = media_entity_create_link(&this->sd.entity,
+ this->source_pad,
+ &last->sd.entity,
+ last->sink_pad,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (rval) {
+ dev_err(&client->dev,
+ "media_entity_create_link failed\n");
+ goto out_nvm_release;
+ }
+
+ rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
+ &this->sd);
+ if (rval) {
+ dev_err(&client->dev,
+ "v4l2_device_register_subdev failed\n");
+ goto out_nvm_release;
+ }
+
+ last = this;
+ }
+
+ dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
+
+ sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+ /* final steps */
+ smiapp_read_frame_fmt(sensor);
+ rval = smiapp_init_controls(sensor);
+ if (rval < 0)
+ goto out_nvm_release;
+
+ rval = smiapp_update_mode(sensor);
+ if (rval) {
+ dev_err(&client->dev, "update mode failed\n");
+ goto out_nvm_release;
+ }
+
+ sensor->streaming = false;
+ sensor->dev_init_done = true;
+
+ /* check flash capability */
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp);
+ sensor->flash_capability = tmp;
+ if (rval)
+ goto out_nvm_release;
+
+ smiapp_power_off(sensor);
+
+ return 0;
+
+out_nvm_release:
+ device_remove_file(&client->dev, &dev_attr_nvm);
+
+out_ident_release:
+ device_remove_file(&client->dev, &dev_attr_ident);
+
+out_power_off:
+ smiapp_power_off(sensor);
+
+out_smiapp_power_on:
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_free(sensor->platform_data->xshutdown);
+
+ return rval;
+}
+
+static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(sd);
+ struct smiapp_sensor *sensor = ssd->sensor;
+ u32 mbus_code =
+ smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code;
+ unsigned int i;
+
+ mutex_lock(&sensor->mutex);
+
+ for (i = 0; i < ssd->npads; i++) {
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(fh, i);
+ struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i);
+ struct v4l2_rect *try_comp;
+
+ try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ try_fmt->code = mbus_code;
+
+ try_crop->top = 0;
+ try_crop->left = 0;
+ try_crop->width = try_fmt->width;
+ try_crop->height = try_fmt->height;
+
+ if (ssd != sensor->pixel_array)
+ continue;
+
+ try_comp = v4l2_subdev_get_try_compose(fh, i);
+ *try_comp = *try_crop;
+ }
+
+ mutex_unlock(&sensor->mutex);
+
+ return smiapp_set_power(sd, 1);
+}
+
+static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return smiapp_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_video_ops smiapp_video_ops = {
+ .s_stream = smiapp_set_stream,
+};
+
+static const struct v4l2_subdev_core_ops smiapp_core_ops = {
+ .s_power = smiapp_set_power,
+};
+
+static const struct v4l2_subdev_pad_ops smiapp_pad_ops = {
+ .enum_mbus_code = smiapp_enum_mbus_code,
+ .get_fmt = smiapp_get_format,
+ .set_fmt = smiapp_set_format,
+ .get_selection = smiapp_get_selection,
+ .set_selection = smiapp_set_selection,
+};
+
+static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = {
+ .g_skip_frames = smiapp_get_skip_frames,
+};
+
+static const struct v4l2_subdev_ops smiapp_ops = {
+ .core = &smiapp_core_ops,
+ .video = &smiapp_video_ops,
+ .pad = &smiapp_pad_ops,
+ .sensor = &smiapp_sensor_ops,
+};
+
+static const struct media_entity_operations smiapp_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = {
+ .registered = smiapp_registered,
+ .open = smiapp_open,
+ .close = smiapp_close,
+};
+
+static const struct v4l2_subdev_internal_ops smiapp_internal_ops = {
+ .open = smiapp_open,
+ .close = smiapp_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * I2C Driver
+ */
+
+#ifdef CONFIG_PM
+
+static int smiapp_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ bool streaming;
+
+ BUG_ON(mutex_is_locked(&sensor->mutex));
+
+ if (sensor->power_count == 0)
+ return 0;
+
+ if (sensor->streaming)
+ smiapp_stop_streaming(sensor);
+
+ streaming = sensor->streaming;
+
+ smiapp_power_off(sensor);
+
+ /* save state for resume */
+ sensor->streaming = streaming;
+
+ return 0;
+}
+
+static int smiapp_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ if (sensor->power_count == 0)
+ return 0;
+
+ rval = smiapp_power_on(sensor);
+ if (rval)
+ return rval;
+
+ if (sensor->streaming)
+ rval = smiapp_start_streaming(sensor);
+
+ return rval;
+}
+
+#else
+
+#define smiapp_suspend NULL
+#define smiapp_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int smiapp_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct smiapp_sensor *sensor;
+
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
+ return -ENOMEM;
+
+ sensor->platform_data = client->dev.platform_data;
+ mutex_init(&sensor->mutex);
+ mutex_init(&sensor->power_mutex);
+ sensor->src = &sensor->ssds[sensor->ssds_used];
+
+ v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops);
+ sensor->src->sd.internal_ops = &smiapp_internal_src_ops;
+ sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->src->sensor = sensor;
+
+ sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+ return media_entity_init(&sensor->src->sd.entity, 2,
+ sensor->src->pads, 0);
+}
+
+static int __exit smiapp_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+
+ if (sensor->power_count) {
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+ sensor->power_count = 0;
+ }
+
+ device_remove_file(&client->dev, &dev_attr_ident);
+ if (sensor->nvm)
+ device_remove_file(&client->dev, &dev_attr_nvm);
+
+ for (i = 0; i < sensor->ssds_used; i++) {
+ media_entity_cleanup(&sensor->ssds[i].sd.entity);
+ v4l2_device_unregister_subdev(&sensor->ssds[i].sd);
+ }
+ smiapp_free_controls(sensor);
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_free(sensor->platform_data->xshutdown);
+
+ return 0;
+}
+
+static const struct i2c_device_id smiapp_id_table[] = {
+ { SMIAPP_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, smiapp_id_table);
+
+static const struct dev_pm_ops smiapp_pm_ops = {
+ .suspend = smiapp_suspend,
+ .resume = smiapp_resume,
+};
+
+static struct i2c_driver smiapp_i2c_driver = {
+ .driver = {
+ .name = SMIAPP_NAME,
+ .pm = &smiapp_pm_ops,
+ },
+ .probe = smiapp_probe,
+ .remove = __exit_p(smiapp_remove),
+ .id_table = smiapp_id_table,
+};
+
+module_i2c_driver(smiapp_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c
new file mode 100644
index 000000000000..fb2f81ad8c3b
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-limits.c
@@ -0,0 +1,132 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-limits.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "smiapp.h"
+
+struct smiapp_reg_limits smiapp_reg_limits[] = {
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" },
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" },
+ { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" },
+ { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" },
+ { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */
+ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" },
+ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" },
+ { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */
+ { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" },
+ { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" },
+ { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */
+ { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" },
+ { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" },
+ { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" },
+ { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" },
+ { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */
+ { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" },
+ { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" },
+ { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" },
+ { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" },
+ { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */
+ { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" },
+ { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" },
+ { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */
+ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" },
+ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" },
+ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" },
+ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" },
+ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */
+ { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" },
+ { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" },
+ { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" },
+ { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" },
+ { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */
+ { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" },
+ { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" },
+ { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */
+ { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" },
+ { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" },
+ { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" },
+ { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" },
+ { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */
+ { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" },
+ { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" },
+ { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" },
+ { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" },
+ { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */
+ { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" },
+ { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" },
+ { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" },
+ { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" },
+ { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */
+ { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" },
+ { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" },
+ { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" },
+ { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" },
+ { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */
+ { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" },
+ { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" },
+ { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" },
+ { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" },
+ { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */
+ { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */
+ { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" },
+ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" },
+ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" },
+ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" },
+ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */
+ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" },
+ { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" },
+ { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */
+ { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" },
+ { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" },
+ { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" },
+ { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" },
+ { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */
+ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" },
+ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" },
+ { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" },
+ { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" },
+ { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */
+ { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" },
+ { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" },
+ { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" },
+ { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" },
+ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */
+ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" },
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" },
+ { 0, NULL },
+};
diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h
new file mode 100644
index 000000000000..9ae765e23ea5
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-limits.h
@@ -0,0 +1,128 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-limits.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2
+#define SMIAPP_LIMIT_THS_ZERO_MIN 3
+#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4
+#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5
+#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6
+#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9
+#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10
+#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11
+#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12
+#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13
+#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14
+#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15
+#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16
+#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17
+#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18
+#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19
+#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20
+#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21
+#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22
+#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23
+#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24
+#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25
+#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26
+#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27
+#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28
+#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29
+#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30
+#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31
+#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33
+#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34
+#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35
+#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37
+#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38
+#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39
+#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40
+#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41
+#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42
+#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43
+#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44
+#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45
+#define SMIAPP_LIMIT_X_ADDR_MIN 46
+#define SMIAPP_LIMIT_Y_ADDR_MIN 47
+#define SMIAPP_LIMIT_X_ADDR_MAX 48
+#define SMIAPP_LIMIT_Y_ADDR_MAX 49
+#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50
+#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51
+#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52
+#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53
+#define SMIAPP_LIMIT_MIN_EVEN_INC 54
+#define SMIAPP_LIMIT_MAX_EVEN_INC 55
+#define SMIAPP_LIMIT_MIN_ODD_INC 56
+#define SMIAPP_LIMIT_MAX_ODD_INC 57
+#define SMIAPP_LIMIT_SCALING_CAPABILITY 58
+#define SMIAPP_LIMIT_SCALER_M_MIN 59
+#define SMIAPP_LIMIT_SCALER_M_MAX 60
+#define SMIAPP_LIMIT_SCALER_N_MIN 61
+#define SMIAPP_LIMIT_SCALER_N_MAX 62
+#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63
+#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64
+#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65
+#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66
+#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67
+#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68
+#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69
+#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70
+#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75
+#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76
+#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77
+#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79
+#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80
+#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83
+#define SMIAPP_LIMIT_BINNING_CAPABILITY 84
+#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85
+#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86
+#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87
+#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88
+#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89
+#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90
+#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91
+#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92
+#define SMIAPP_LIMIT_EDOF_CAPABILITY 93
+#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94
+#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95
+#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96
+#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97
+#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98
+#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99
+#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100
+#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102
+#define SMIAPP_LIMIT_LAST 103
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c
new file mode 100644
index 000000000000..725cf62836c6
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.c
@@ -0,0 +1,286 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-quirk.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+
+#include "smiapp.h"
+
+static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)
+{
+ return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val);
+}
+
+static int smiapp_write_8s(struct smiapp_sensor *sensor,
+ struct smiapp_reg_8 *regs, int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ for (; len > 0; len--, regs++) {
+ rval = smiapp_write_8(sensor, regs->reg, regs->val);
+ if (rval < 0) {
+ dev_err(&client->dev,
+ "error %d writing reg 0x%4.4x, val 0x%2.2x",
+ rval, regs->reg, regs->val);
+ return rval;
+ }
+ }
+
+ return 0;
+}
+
+void smiapp_replace_limit(struct smiapp_sensor *sensor,
+ u32 limit, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+
+ dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limit].addr,
+ smiapp_reg_limits[limit].what, val, val);
+ sensor->limits[limit] = val;
+}
+
+bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
+ u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ const struct smia_reg *sreg;
+
+ if (!sensor->minfo.quirk)
+ return false;
+
+ sreg = sensor->minfo.quirk->regs;
+
+ if (!sreg)
+ return false;
+
+ while (sreg->type) {
+ u16 type = reg >> 16;
+ u16 reg16 = reg;
+
+ if (sreg->type != type || sreg->reg != reg16) {
+ sreg++;
+ continue;
+ }
+
+ switch ((u8)type) {
+ case SMIA_REG_8BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n",
+ reg, sreg->val);
+ break;
+ case SMIA_REG_16BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n",
+ reg, sreg->val);
+ break;
+ case SMIA_REG_32BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n",
+ reg, sreg->val);
+ break;
+ }
+
+ *val = sreg->val;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int jt8ew9_limits(struct smiapp_sensor *sensor)
+{
+ if (sensor->minfo.revision_number_major < 0x03)
+ sensor->frame_skip = 1;
+
+ /* Below 24 gain doesn't have effect at all, */
+ /* but ~59 is needed for full dynamic range */
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59);
+ smiapp_replace_limit(
+ sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000);
+
+ return 0;
+}
+
+static int jt8ew9_post_poweron(struct smiapp_sensor *sensor)
+{
+ struct smiapp_reg_8 regs[] = {
+ { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */
+ { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
+ { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
+ { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */
+ { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ /* Taken from v03. No idea what the rest are. */
+ { 0x32e0, 0x05 },
+ { 0x32e1, 0x05 },
+ { 0x32e2, 0x04 },
+ { 0x32e5, 0x04 },
+ { 0x32e6, 0x04 },
+
+ };
+
+ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+}
+
+const struct smiapp_quirk smiapp_jt8ew9_quirk = {
+ .limits = jt8ew9_limits,
+ .post_poweron = jt8ew9_post_poweron,
+};
+
+static int imx125es_post_poweron(struct smiapp_sensor *sensor)
+{
+ /* Taken from v02. No idea what the other two are. */
+ struct smiapp_reg_8 regs[] = {
+ /*
+ * 0x3302: clk during frame blanking:
+ * 0x00 - HS mode, 0x01 - LP11
+ */
+ { 0x3302, 0x01 },
+ { 0x302d, 0x00 },
+ { 0x3b08, 0x8c },
+ };
+
+ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+}
+
+const struct smiapp_quirk smiapp_imx125es_quirk = {
+ .post_poweron = imx125es_post_poweron,
+};
+
+static int jt8ev1_limits(struct smiapp_sensor *sensor)
+{
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271);
+ smiapp_replace_limit(sensor,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184);
+
+ return 0;
+}
+
+static int jt8ev1_post_poweron(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ struct smiapp_reg_8 regs[] = {
+ { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */
+ { 0x30a3, 0xd0 }, /* FLASH STROBE enable */
+ { 0x3237, 0x00 }, /* For control of pulse timing for ADC */
+ { 0x3238, 0x43 },
+ { 0x3301, 0x06 }, /* For analog bias for sensor */
+ { 0x3302, 0x06 },
+ { 0x3304, 0x00 },
+ { 0x3305, 0x88 },
+ { 0x332a, 0x14 },
+ { 0x332c, 0x6b },
+ { 0x3336, 0x01 },
+ { 0x333f, 0x1f },
+ { 0x3355, 0x00 },
+ { 0x3356, 0x20 },
+ { 0x33bf, 0x20 }, /* Adjust the FBC speed */
+ { 0x33c9, 0x20 },
+ { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */
+ { 0x33cf, 0xec }, /* For Black sun */
+ { 0x3328, 0x80 }, /* Ugh. No idea what's this. */
+ };
+
+ struct smiapp_reg_8 regs_96[] = {
+ { 0x30ae, 0x00 }, /* For control of ADC clock */
+ { 0x30af, 0xd0 },
+ { 0x30b0, 0x01 },
+ };
+
+ rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+ if (rval < 0)
+ return rval;
+
+ switch (sensor->platform_data->ext_clk) {
+ case 9600000:
+ return smiapp_write_8s(sensor, regs_96,
+ ARRAY_SIZE(regs_96));
+ default:
+ dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n",
+ sensor->platform_data->ext_clk);
+ return 0;
+ }
+}
+
+static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor)
+{
+ return smiapp_write_8(sensor, 0x3328, 0x00);
+}
+
+static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)
+{
+ int rval;
+
+ /* Workaround: allows fast standby to work properly */
+ rval = smiapp_write_8(sensor, 0x3205, 0x04);
+ if (rval < 0)
+ return rval;
+
+ /* Wait for 1 ms + one line => 2 ms is likely enough */
+ usleep_range(2000, 2000);
+
+ /* Restore it */
+ rval = smiapp_write_8(sensor, 0x3205, 0x00);
+ if (rval < 0)
+ return rval;
+
+ return smiapp_write_8(sensor, 0x3328, 0x80);
+}
+
+const struct smiapp_quirk smiapp_jt8ev1_quirk = {
+ .limits = jt8ev1_limits,
+ .post_poweron = jt8ev1_post_poweron,
+ .pre_streamon = jt8ev1_pre_streamon,
+ .post_streamoff = jt8ev1_post_streamoff,
+ .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE,
+};
+
+static int tcm8500md_limits(struct smiapp_sensor *sensor)
+{
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000);
+
+ return 0;
+}
+
+const struct smiapp_quirk smiapp_tcm8500md_quirk = {
+ .limits = tcm8500md_limits,
+};
diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h
new file mode 100644
index 000000000000..86fd3e8bfb0f
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-quirk.h
@@ -0,0 +1,83 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-quirk.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __SMIAPP_QUIRK__
+#define __SMIAPP_QUIRK__
+
+struct smiapp_sensor;
+
+/**
+ * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard
+ *
+ * @limits: Replace sensor->limits with values which can't be read from
+ * sensor registers. Called the first time the sensor is powered up.
+ * @post_poweron: Called always after the sensor has been fully powered on.
+ * @pre_streamon: Called just before streaming is enabled.
+ * @post_streamon: Called right after stopping streaming.
+ */
+struct smiapp_quirk {
+ int (*limits)(struct smiapp_sensor *sensor);
+ int (*post_poweron)(struct smiapp_sensor *sensor);
+ int (*pre_streamon)(struct smiapp_sensor *sensor);
+ int (*post_streamoff)(struct smiapp_sensor *sensor);
+ const struct smia_reg *regs;
+ unsigned long flags;
+};
+
+/* op pix clock is for all lanes in total normally */
+#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
+#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1)
+
+struct smiapp_reg_8 {
+ u16 reg;
+ u8 val;
+};
+
+void smiapp_replace_limit(struct smiapp_sensor *sensor,
+ u32 limit, u32 val);
+bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
+ u32 reg, u32 *val);
+
+#define SMIAPP_MK_QUIRK_REG(_reg, _val) \
+ { \
+ .type = (_reg >> 16), \
+ .reg = (u16)_reg, \
+ .val = _val, \
+ }
+
+#define smiapp_call_quirk(_sensor, _quirk, ...) \
+ (_sensor->minfo.quirk && \
+ _sensor->minfo.quirk->_quirk ? \
+ _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0)
+
+#define smiapp_needs_quirk(_sensor, _quirk) \
+ (_sensor->minfo.quirk ? \
+ _sensor->minfo.quirk->flags & _quirk : 0)
+
+extern const struct smiapp_quirk smiapp_jt8ev1_quirk;
+extern const struct smiapp_quirk smiapp_imx125es_quirk;
+extern const struct smiapp_quirk smiapp_jt8ew9_quirk;
+extern const struct smiapp_quirk smiapp_tcm8500md_quirk;
+
+#endif /* __SMIAPP_QUIRK__ */
diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
new file mode 100644
index 000000000000..defa7c5adebf
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h
@@ -0,0 +1,503 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-reg-defs.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r))
+#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r))
+#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r))
+
+#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r))
+
+#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000)
+#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002)
+#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003)
+#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004)
+#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005)
+#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006)
+#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008)
+#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c)
+#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010)
+#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011)
+#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012)
+#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013)
+#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014)
+#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015)
+#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016)
+#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018)
+#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019)
+#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a)
+#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c)
+#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040)
+#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041)
+#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */
+#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092)
+#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0)
+#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1)
+#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1))
+#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100)
+#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101)
+#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103)
+#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104)
+#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105)
+#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106)
+#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107)
+#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108)
+#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109)
+#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110)
+#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111)
+#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112)
+#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114)
+#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115)
+#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116)
+#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117)
+#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118)
+#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119)
+#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a)
+#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b)
+#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c)
+#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d)
+#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e)
+#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120)
+#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130)
+#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132)
+#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134)
+#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136)
+#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138)
+#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139)
+#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214)
+#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300)
+#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302)
+#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304)
+#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306)
+#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308)
+#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a)
+#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340)
+#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342)
+#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344)
+#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346)
+#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348)
+#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a)
+#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c)
+#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e)
+#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380)
+#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382)
+#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384)
+#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386)
+#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400)
+#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402)
+#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404)
+#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406)
+#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408)
+#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a)
+#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c)
+#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e)
+#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500)
+#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600)
+#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602)
+#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604)
+#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606)
+#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608)
+#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a)
+#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c)
+#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e)
+#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610)
+#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700)
+#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800)
+#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801)
+#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802)
+#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803)
+#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804)
+#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805)
+#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806)
+#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807)
+#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808)
+#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820)
+#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900)
+#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901)
+#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87)
+#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00)
+#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03)
+#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04)
+#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05)
+#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06)
+#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07)
+#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08)
+#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09)
+#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a)
+#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d)
+#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e)
+#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f)
+#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10)
+#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11)
+#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12)
+#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13)
+#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14)
+#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15)
+#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16)
+#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80)
+#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83)
+#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85)
+#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86)
+#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88)
+#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a)
+#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94)
+#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0)
+#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca)
+#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00)
+#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01)
+#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02)
+#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03)
+#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04)
+#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06)
+#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08)
+#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a)
+#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e)
+#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10)
+#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12)
+#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14)
+#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18)
+#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a)
+#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b)
+#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c)
+#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d)
+#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e)
+#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20)
+#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22)
+#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24)
+#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28)
+#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a)
+#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e)
+#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80)
+#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82)
+#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84)
+#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86)
+#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88)
+#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a)
+#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c)
+#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e)
+#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00)
+#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01)
+#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02)
+#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03)
+#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86)
+#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88)
+#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89)
+#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00)
+#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01)
+#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02)
+#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10)
+#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff)
+#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088)
+#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100)
+#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104)
+#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108)
+#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a)
+#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c)
+#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110)
+#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114)
+#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116)
+#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118)
+#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c)
+#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120)
+#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122)
+#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124)
+#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128)
+#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c)
+#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130)
+#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134)
+#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136)
+#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140)
+#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142)
+#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144)
+#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146)
+#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148)
+#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a)
+#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c)
+#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160)
+#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162)
+#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164)
+#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168)
+#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c)
+#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e)
+#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170)
+#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174)
+#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180)
+#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182)
+#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184)
+#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186)
+#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188)
+#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a)
+#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c)
+#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e)
+#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0)
+#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2)
+#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4)
+#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6)
+#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200)
+#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204)
+#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206)
+#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208)
+#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a)
+#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c)
+#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e)
+#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410)
+#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500)
+#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502)
+#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600)
+#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601)
+#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602)
+#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603)
+#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614)
+#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618)
+#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700)
+#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702)
+#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704)
+#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706)
+#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c)
+#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710)
+#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711)
+#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712)
+#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800)
+#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901)
+#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903)
+#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904)
+#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906)
+#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980)
+#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981)
+#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982)
+#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983)
+#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984)
+#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985)
+#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986)
+#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987)
+#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988)
+#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0)
+#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1)
+#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2)
+#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4)
+#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00)
+#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02)
+#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02)
+#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04)
+#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40)
+#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01)
+#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02)
diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h
new file mode 100644
index 000000000000..54568ca2fe6d
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-reg.h
@@ -0,0 +1,122 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-reg.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __SMIAPP_REG_H_
+#define __SMIAPP_REG_H_
+
+#include "smiapp-reg-defs.h"
+
+/* Bits for above register */
+#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0)
+#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1)
+
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3)
+
+#define SMIAPP_SOFTWARE_RESET (1 << 0)
+
+#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0)
+#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1)
+
+#define SMIAPP_DPHY_CTRL_AUTOMATIC 0
+/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */
+#define SMIAPP_DPHY_CTRL_UI 1
+#define SMIAPP_DPHY_CTRL_REGISTER 2
+
+#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1
+#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2
+
+#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0
+#define SMIAPP_MODE_SELECT_STREAMING 1
+
+#define SMIAPP_SCALING_MODE_NONE 0
+#define SMIAPP_SCALING_MODE_HORIZONTAL 1
+#define SMIAPP_SCALING_MODE_BOTH 2
+
+#define SMIAPP_SCALING_CAPABILITY_NONE 0
+#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1
+#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */
+
+/* digital crop right before scaler */
+#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0
+#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1
+
+#define SMIAPP_BINNING_CAPABILITY_NO 0
+#define SMIAPP_BINNING_CAPABILITY_YES 1
+
+/* Maximum number of binning subtypes */
+#define SMIAPP_BINNING_SUBTYPES 253
+
+#define SMIAPP_PIXEL_ORDER_GRBG 0
+#define SMIAPP_PIXEL_ORDER_RGGB 1
+#define SMIAPP_PIXEL_ORDER_BGGR 2
+#define SMIAPP_PIXEL_ORDER_GBRG 3
+
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16
+
+#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01
+#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4
+
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff
+
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff
+
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5
+
+#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0
+#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1
+
+/* Scaling N factor */
+#define SMIAPP_SCALE_N 16
+
+/* Image statistics registers */
+/* Registers 0x2000 to 0x2fff are reserved for future
+ * use for statistics features.
+ */
+
+/* Manufacturer Specific Registers: 0x3000 to 0x3fff
+ * The manufacturer specifies these as a black box.
+ */
+
+#endif /* __SMIAPP_REG_H_ */
diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c
new file mode 100644
index 000000000000..70e0d8db0130
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp-regs.c
@@ -0,0 +1,273 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp-regs.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#include "smiapp.h"
+#include "smiapp-regs.h"
+
+static uint32_t float_to_u32_mul_1000000(struct i2c_client *client,
+ uint32_t phloat)
+{
+ int32_t exp;
+ uint64_t man;
+
+ if (phloat >= 0x80000000) {
+ dev_err(&client->dev, "this is a negative number\n");
+ return 0;
+ }
+
+ if (phloat == 0x7f800000)
+ return ~0; /* Inf. */
+
+ if ((phloat & 0x7f800000) == 0x7f800000) {
+ dev_err(&client->dev, "NaN or other special number\n");
+ return 0;
+ }
+
+ /* Valid cases begin here */
+ if (phloat == 0)
+ return 0; /* Valid zero */
+
+ if (phloat > 0x4f800000)
+ return ~0; /* larger than 4294967295 */
+
+ /*
+ * Unbias exponent (note how phloat is now guaranteed to
+ * have 0 in the high bit)
+ */
+ exp = ((int32_t)phloat >> 23) - 127;
+
+ /* Extract mantissa, add missing '1' bit and it's in MHz */
+ man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL;
+
+ if (exp < 0)
+ man >>= -exp;
+ else
+ man <<= exp;
+
+ man >>= 23; /* Remove mantissa bias */
+
+ return man & 0xffffffff;
+}
+
+
+/*
+ * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,
+ u16 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct i2c_msg msg;
+ unsigned char data[4];
+ u16 offset = reg;
+ int r;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8) (offset >> 8);
+ data[1] = (u8) offset;
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r != 1) {
+ if (r >= 0)
+ r = -EBUSY;
+ goto err;
+ }
+
+ msg.len = len;
+ msg.flags = I2C_M_RD;
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r != 1) {
+ if (r >= 0)
+ r = -EBUSY;
+ goto err;
+ }
+
+ *val = 0;
+ /* high byte comes first */
+ switch (len) {
+ case SMIA_REG_32BIT:
+ *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +
+ data[3];
+ break;
+ case SMIA_REG_16BIT:
+ *val = (data[0] << 8) + data[1];
+ break;
+ case SMIA_REG_8BIT:
+ *val = data[0];
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+
+err:
+ dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r);
+
+ return r;
+}
+
+/* Read a register using 8-bit access only. */
+static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg,
+ u16 len, u32 *val)
+{
+ unsigned int i;
+ int rval;
+
+ *val = 0;
+
+ for (i = 0; i < len; i++) {
+ u32 val8;
+
+ rval = ____smiapp_read(sensor, reg + i, 1, &val8);
+ if (rval < 0)
+ return rval;
+ *val |= val8 << ((len - i - 1) << 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,
+ bool only8)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int len = (u8)(reg >> 16);
+ int rval;
+
+ if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT
+ && len != SMIA_REG_32BIT)
+ return -EINVAL;
+
+ if (smiapp_quirk_reg(sensor, reg, val))
+ goto found_quirk;
+
+ if (len == SMIA_REG_8BIT && !only8)
+ rval = ____smiapp_read(sensor, (u16)reg, len, val);
+ else
+ rval = ____smiapp_read_8only(sensor, (u16)reg, len, val);
+ if (rval < 0)
+ return rval;
+
+found_quirk:
+ if (reg & SMIA_REG_FLAG_FLOAT)
+ *val = float_to_u32_mul_1000000(client, *val);
+
+ return 0;
+}
+
+int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+ return __smiapp_read(
+ sensor, reg, val,
+ smiapp_needs_quirk(sensor,
+ SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));
+}
+
+int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+ return __smiapp_read(sensor, reg, val, true);
+}
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct i2c_msg msg;
+ unsigned char data[6];
+ unsigned int retries;
+ unsigned int flags = reg >> 24;
+ unsigned int len = (u8)(reg >> 16);
+ u16 offset = reg;
+ int r;
+
+ if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT &&
+ len != SMIA_REG_32BIT) || flags)
+ return -EINVAL;
+
+ msg.addr = client->addr;
+ msg.flags = 0; /* Write */
+ msg.len = 2 + len;
+ msg.buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8) (reg >> 8);
+ data[1] = (u8) (reg & 0xff);
+
+ switch (len) {
+ case SMIA_REG_8BIT:
+ data[2] = val;
+ break;
+ case SMIA_REG_16BIT:
+ data[2] = val >> 8;
+ data[3] = val;
+ break;
+ case SMIA_REG_32BIT:
+ data[2] = val >> 24;
+ data[3] = val >> 16;
+ data[4] = val >> 8;
+ data[5] = val;
+ break;
+ default:
+ BUG();
+ }
+
+ for (retries = 0; retries < 5; retries++) {
+ /*
+ * Due to unknown reason sensor stops responding. This
+ * loop is a temporaty solution until the root cause
+ * is found.
+ */
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r == 1) {
+ if (retries)
+ dev_err(&client->dev,
+ "sensor i2c stall encountered. "
+ "retries: %d\n", retries);
+ return 0;
+ }
+
+ usleep_range(2000, 2000);
+ }
+
+ dev_err(&client->dev,
+ "wrote 0x%x to offset 0x%x error %d\n", val, offset, r);
+
+ return r;
+}
diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h
index 7f9013b47971..7f9013b47971 100644
--- a/drivers/media/video/smiapp/smiapp-regs.h
+++ b/drivers/media/i2c/smiapp/smiapp-regs.h
diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h
new file mode 100644
index 000000000000..4182a695ab53
--- /dev/null
+++ b/drivers/media/i2c/smiapp/smiapp.h
@@ -0,0 +1,252 @@
+/*
+ * drivers/media/i2c/smiapp/smiapp.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2010--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __SMIAPP_PRIV_H_
+#define __SMIAPP_PRIV_H_
+
+#include <linux/mutex.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/smiapp.h>
+
+#include "smiapp-pll.h"
+#include "smiapp-reg.h"
+#include "smiapp-regs.h"
+#include "smiapp-quirk.h"
+
+/*
+ * Standard SMIA++ constants
+ */
+#define SMIA_VERSION_1 10
+#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */
+#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */
+#define SMIAPP_VERSION_1 10
+
+#define SMIAPP_PROFILE_0 0
+#define SMIAPP_PROFILE_1 1
+#define SMIAPP_PROFILE_2 2
+
+#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */
+
+#define SMIAPP_RESET_DELAY_CLOCKS 2400
+#define SMIAPP_RESET_DELAY(clk) \
+ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \
+ + (clk) / 1000 - 1) / ((clk) / 1000))
+
+#include "smiapp-limits.h"
+
+struct smiapp_quirk;
+
+#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0)
+
+struct smiapp_module_ident {
+ u8 manufacturer_id;
+ u16 model_id;
+ u8 revision_number_major;
+
+ u8 flags;
+
+ char *name;
+ const struct smiapp_quirk *quirk;
+};
+
+struct smiapp_module_info {
+ u32 manufacturer_id;
+ u32 model_id;
+ u32 revision_number_major;
+ u32 revision_number_minor;
+
+ u32 module_year;
+ u32 module_month;
+ u32 module_day;
+
+ u32 sensor_manufacturer_id;
+ u32 sensor_model_id;
+ u32 sensor_revision_number;
+ u32 sensor_firmware_version;
+
+ u32 smia_version;
+ u32 smiapp_version;
+
+ u32 smiapp_profile;
+
+ char *name;
+ const struct smiapp_quirk *quirk;
+};
+
+#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = fl, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
+ .name = _name, }
+
+#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = 0, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT(manufacturer, model, rev, _name) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = 0, \
+ .name = _name, }
+
+struct smiapp_reg_limits {
+ u32 addr;
+ char *what;
+};
+
+extern struct smiapp_reg_limits smiapp_reg_limits[];
+
+struct smiapp_csi_data_format {
+ u32 code;
+ u8 width;
+ u8 compressed;
+ u8 pixel_order;
+};
+
+#define SMIAPP_SUBDEVS 3
+
+#define SMIAPP_PA_PAD_SRC 0
+#define SMIAPP_PAD_SINK 0
+#define SMIAPP_PAD_SRC 1
+#define SMIAPP_PADS 2
+
+struct smiapp_binning_subtype {
+ u8 horizontal:4;
+ u8 vertical:4;
+} __packed;
+
+struct smiapp_subdev {
+ struct v4l2_subdev sd;
+ struct media_pad pads[2];
+ struct v4l2_rect sink_fmt;
+ struct v4l2_rect crop[2];
+ struct v4l2_rect compose; /* compose on sink */
+ unsigned short sink_pad;
+ unsigned short source_pad;
+ int npads;
+ struct smiapp_sensor *sensor;
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct smiapp_sensor - Main device structure
+ */
+struct smiapp_sensor {
+ /*
+ * "mutex" is used to serialise access to all fields here
+ * except v4l2_ctrls at the end of the struct. "mutex" is also
+ * used to serialise access to file handle specific
+ * information. The exception to this rule is the power_mutex
+ * below.
+ */
+ struct mutex mutex;
+ /*
+ * power_mutex is used to serialise power management related
+ * activities. Acquiring "mutex" at that time isn't necessary
+ * since there are no other users anyway.
+ */
+ struct mutex power_mutex;
+ struct smiapp_subdev ssds[SMIAPP_SUBDEVS];
+ u32 ssds_used;
+ struct smiapp_subdev *src;
+ struct smiapp_subdev *binner;
+ struct smiapp_subdev *scaler;
+ struct smiapp_subdev *pixel_array;
+ struct smiapp_platform_data *platform_data;
+ struct regulator *vana;
+ struct clk *ext_clk;
+ u32 limits[SMIAPP_LIMIT_LAST];
+ u8 nbinning_subtypes;
+ struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES];
+ u32 mbus_frame_fmts;
+ const struct smiapp_csi_data_format *csi_format;
+ const struct smiapp_csi_data_format *internal_csi_format;
+ u32 default_mbus_frame_fmts;
+ int default_pixel_order;
+
+ u8 binning_horizontal;
+ u8 binning_vertical;
+
+ u8 scale_m;
+ u8 scaling_mode;
+
+ u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */
+ u8 flash_capability;
+ u8 frame_skip;
+
+ int power_count;
+
+ bool streaming;
+ bool dev_init_done;
+
+ u8 *nvm; /* nvm memory buffer */
+ unsigned int nvm_size; /* bytes */
+
+ struct smiapp_module_info minfo;
+
+ struct smiapp_pll pll;
+
+ /* Pixel array controls */
+ struct v4l2_ctrl *analog_gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *pixel_rate_parray;
+ /* src controls */
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate_csi;
+};
+
+#define to_smiapp_subdev(_sd) \
+ container_of(_sd, struct smiapp_subdev, sd)
+
+#define to_smiapp_sensor(_sd) \
+ (to_smiapp_subdev(_sd)->sensor)
+
+#endif /* __SMIAPP_PRIV_H_ */
diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig
new file mode 100644
index 000000000000..6dff2b7ad520
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/Kconfig
@@ -0,0 +1,89 @@
+comment "soc_camera sensor drivers"
+
+config SOC_CAMERA_IMX074
+ tristate "imx074 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports IMX074 cameras from Sony
+
+config SOC_CAMERA_MT9M001
+ tristate "mt9m001 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9M001_PCA9536_SWITCH
+ help
+ This driver supports MT9M001 cameras from Micron, monochrome
+ and colour models.
+
+config SOC_CAMERA_MT9M111
+ tristate "mt9m111, mt9m112 and mt9m131 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9M111, MT9M112 and MT9M131 cameras from
+ Micron/Aptina
+
+config SOC_CAMERA_MT9T031
+ tristate "mt9t031 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T031 cameras from Micron.
+
+config SOC_CAMERA_MT9T112
+ tristate "mt9t112 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This driver supports MT9T112 cameras from Aptina.
+
+config SOC_CAMERA_MT9V022
+ tristate "mt9v022 and mt9v024 support"
+ depends on SOC_CAMERA && I2C
+ select GPIO_PCA953X if MT9V022_PCA9536_SWITCH
+ help
+ This driver supports MT9V022 cameras from Micron
+
+config SOC_CAMERA_OV2640
+ tristate "ov2640 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov2640 camera driver
+
+config SOC_CAMERA_OV5642
+ tristate "ov5642 camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a V4L2 camera driver for the OmniVision OV5642 sensor
+
+config SOC_CAMERA_OV6650
+ tristate "ov6650 sensor support"
+ depends on SOC_CAMERA && I2C
+ ---help---
+ This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
+
+config SOC_CAMERA_OV772X
+ tristate "ov772x camera support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a ov772x camera driver
+
+config SOC_CAMERA_OV9640
+ tristate "ov9640 camera support"
+ depends on SOC_CAMERA && I2C
+ 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 SOC_CAMERA_RJ54N1
+ tristate "rj54n1cb0c support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a rj54n1cb0c video driver
+
+config SOC_CAMERA_TW9910
+ tristate "tw9910 support"
+ depends on SOC_CAMERA && I2C
+ help
+ This is a tw9910 video driver
diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile
new file mode 100644
index 000000000000..d0421feaa796
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/Makefile
@@ -0,0 +1,14 @@
+obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
+obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
+obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
+obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
+obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
+obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
+obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
+obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.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
diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c
new file mode 100644
index 000000000000..f8534eec9de9
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/imx074.c
@@ -0,0 +1,495 @@
+/*
+ * Driver for IMX074 CMOS Image Sensor from Sony
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Partially inspired by the IMX074 driver from the Android / MSM tree
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+
+/* IMX074 registers */
+
+#define MODE_SELECT 0x0100
+#define IMAGE_ORIENTATION 0x0101
+#define GROUPED_PARAMETER_HOLD 0x0104
+
+/* Integration Time */
+#define COARSE_INTEGRATION_TIME_HI 0x0202
+#define COARSE_INTEGRATION_TIME_LO 0x0203
+/* Gain */
+#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
+#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
+
+/* PLL registers */
+#define PRE_PLL_CLK_DIV 0x0305
+#define PLL_MULTIPLIER 0x0307
+#define PLSTATIM 0x302b
+#define VNDMY_ABLMGSHLMT 0x300a
+#define Y_OPBADDR_START_DI 0x3014
+/* mode setting */
+#define FRAME_LENGTH_LINES_HI 0x0340
+#define FRAME_LENGTH_LINES_LO 0x0341
+#define LINE_LENGTH_PCK_HI 0x0342
+#define LINE_LENGTH_PCK_LO 0x0343
+#define YADDR_START 0x0347
+#define YADDR_END 0x034b
+#define X_OUTPUT_SIZE_MSB 0x034c
+#define X_OUTPUT_SIZE_LSB 0x034d
+#define Y_OUTPUT_SIZE_MSB 0x034e
+#define Y_OUTPUT_SIZE_LSB 0x034f
+#define X_EVEN_INC 0x0381
+#define X_ODD_INC 0x0383
+#define Y_EVEN_INC 0x0385
+#define Y_ODD_INC 0x0387
+
+#define HMODEADD 0x3001
+#define VMODEADD 0x3016
+#define VAPPLINE_START 0x3069
+#define VAPPLINE_END 0x306b
+#define SHUTTER 0x3086
+#define HADDAVE 0x30e8
+#define LANESEL 0x3301
+
+/* IMX074 supported geometry */
+#define IMX074_WIDTH 1052
+#define IMX074_HEIGHT 780
+
+/* IMX074 has only one fixed colorspace per pixelcode */
+struct imx074_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct imx074 {
+ struct v4l2_subdev subdev;
+ const struct imx074_datafmt *fmt;
+};
+
+static const struct imx074_datafmt imx074_colour_fmts[] = {
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static struct imx074 *to_imx074(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct imx074, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
+ if (imx074_colour_fmts[i].code == code)
+ return imx074_colour_fmts + i;
+
+ return NULL;
+}
+
+static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ unsigned char tx[3];
+ int ret;
+
+ msg.addr = client->addr;
+ msg.buf = tx;
+ msg.len = 3;
+ msg.flags = 0;
+
+ tx[0] = addr >> 8;
+ tx[1] = addr & 0xff;
+ tx[2] = data;
+
+ ret = i2c_transfer(adap, &msg, 1);
+
+ mdelay(2);
+
+ return ret == 1 ? 0 : -EIO;
+}
+
+static int reg_read(struct i2c_client *client, const u16 addr)
+{
+ u8 buf[2] = {addr >> 8, addr & 0xff};
+ int ret;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = buf,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 2,
+ .buf = buf,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0) {
+ dev_warn(&client->dev, "Reading register %x from %x failed\n",
+ addr, client->addr);
+ return ret;
+ }
+
+ return buf[0] & 0xff; /* no sign-extension */
+}
+
+static int imx074_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
+
+ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+ if (!fmt) {
+ mf->code = imx074_colour_fmts[0].code;
+ mf->colorspace = imx074_colour_fmts[0].colorspace;
+ }
+
+ mf->width = IMX074_WIDTH;
+ mf->height = IMX074_HEIGHT;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int imx074_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct imx074 *priv = to_imx074(client);
+
+ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!imx074_find_datafmt(mf->code))
+ return -EINVAL;
+
+ imx074_try_fmt(sd, mf);
+
+ priv->fmt = imx074_find_datafmt(mf->code);
+
+ return 0;
+}
+
+static int imx074_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct imx074 *priv = to_imx074(client);
+
+ const struct imx074_datafmt *fmt = priv->fmt;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = IMX074_WIDTH;
+ mf->height = IMX074_HEIGHT;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rect->top = 0;
+ rect->left = 0;
+ rect->width = IMX074_WIDTH;
+ rect->height = IMX074_HEIGHT;
+
+ return 0;
+}
+
+static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = IMX074_WIDTH;
+ a->bounds.height = IMX074_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
+ return -EINVAL;
+
+ *code = imx074_colour_fmts[index].code;
+ return 0;
+}
+
+static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* MODE_SELECT: stream or standby */
+ return reg_write(client, MODE_SELECT, !!enable);
+}
+
+static int imx074_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_IMX074;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int imx074_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int imx074_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE |
+ V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
+ .s_stream = imx074_s_stream,
+ .s_mbus_fmt = imx074_s_fmt,
+ .g_mbus_fmt = imx074_g_fmt,
+ .try_mbus_fmt = imx074_try_fmt,
+ .enum_mbus_fmt = imx074_enum_fmt,
+ .g_crop = imx074_g_crop,
+ .cropcap = imx074_cropcap,
+ .g_mbus_config = imx074_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
+ .g_chip_ident = imx074_g_chip_ident,
+ .s_power = imx074_s_power,
+};
+
+static struct v4l2_subdev_ops imx074_subdev_ops = {
+ .core = &imx074_subdev_core_ops,
+ .video = &imx074_subdev_video_ops,
+};
+
+static int imx074_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ int ret;
+ u16 id;
+
+ ret = imx074_s_power(subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Read sensor Model ID */
+ ret = reg_read(client, 0);
+ if (ret < 0)
+ goto done;
+
+ id = ret << 8;
+
+ ret = reg_read(client, 1);
+ if (ret < 0)
+ goto done;
+
+ id |= ret;
+
+ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+ if (id != 0x74) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ /* PLL Setting EXTCLK=24MHz, 22.5times */
+ reg_write(client, PLL_MULTIPLIER, 0x2D);
+ reg_write(client, PRE_PLL_CLK_DIV, 0x02);
+ reg_write(client, PLSTATIM, 0x4B);
+
+ /* 2-lane mode */
+ reg_write(client, 0x3024, 0x00);
+
+ reg_write(client, IMAGE_ORIENTATION, 0x00);
+
+ /* select RAW mode:
+ * 0x08+0x08 = top 8 bits
+ * 0x0a+0x08 = compressed 8-bits
+ * 0x0a+0x0a = 10 bits
+ */
+ reg_write(client, 0x0112, 0x08);
+ reg_write(client, 0x0113, 0x08);
+
+ /* Base setting for High frame mode */
+ reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
+ reg_write(client, Y_OPBADDR_START_DI, 0x08);
+ reg_write(client, 0x3015, 0x37);
+ reg_write(client, 0x301C, 0x01);
+ reg_write(client, 0x302C, 0x05);
+ reg_write(client, 0x3031, 0x26);
+ reg_write(client, 0x3041, 0x60);
+ reg_write(client, 0x3051, 0x24);
+ reg_write(client, 0x3053, 0x34);
+ reg_write(client, 0x3057, 0xC0);
+ reg_write(client, 0x305C, 0x09);
+ reg_write(client, 0x305D, 0x07);
+ reg_write(client, 0x3060, 0x30);
+ reg_write(client, 0x3065, 0x00);
+ reg_write(client, 0x30AA, 0x08);
+ reg_write(client, 0x30AB, 0x1C);
+ reg_write(client, 0x30B0, 0x32);
+ reg_write(client, 0x30B2, 0x83);
+ reg_write(client, 0x30D3, 0x04);
+ reg_write(client, 0x3106, 0x78);
+ reg_write(client, 0x310C, 0x82);
+ reg_write(client, 0x3304, 0x05);
+ reg_write(client, 0x3305, 0x04);
+ reg_write(client, 0x3306, 0x11);
+ reg_write(client, 0x3307, 0x02);
+ reg_write(client, 0x3308, 0x0C);
+ reg_write(client, 0x3309, 0x06);
+ reg_write(client, 0x330A, 0x08);
+ reg_write(client, 0x330B, 0x04);
+ reg_write(client, 0x330C, 0x08);
+ reg_write(client, 0x330D, 0x06);
+ reg_write(client, 0x330E, 0x01);
+ reg_write(client, 0x3381, 0x00);
+
+ /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
+ /* 1608 = 1560 + 48 (black lines) */
+ reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
+ reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
+ reg_write(client, YADDR_START, 0x00);
+ reg_write(client, YADDR_END, 0x2F);
+ /* 0x838 == 2104 */
+ reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
+ reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
+ /* 0x618 == 1560 */
+ reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
+ reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
+ reg_write(client, X_EVEN_INC, 0x01);
+ reg_write(client, X_ODD_INC, 0x03);
+ reg_write(client, Y_EVEN_INC, 0x01);
+ reg_write(client, Y_ODD_INC, 0x03);
+ reg_write(client, HMODEADD, 0x00);
+ reg_write(client, VMODEADD, 0x16);
+ reg_write(client, VAPPLINE_START, 0x24);
+ reg_write(client, VAPPLINE_END, 0x53);
+ reg_write(client, SHUTTER, 0x00);
+ reg_write(client, HADDAVE, 0x80);
+
+ reg_write(client, LANESEL, 0x00);
+
+ reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */
+
+ ret = 0;
+
+done:
+ imx074_s_power(subdev, 0);
+ return ret;
+}
+
+static int imx074_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct imx074 *priv;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "IMX074: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(struct imx074), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
+
+ priv->fmt = &imx074_colour_fmts[0];
+
+ ret = imx074_video_probe(client);
+ if (ret < 0) {
+ kfree(priv);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int imx074_remove(struct i2c_client *client)
+{
+ struct imx074 *priv = to_imx074(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id imx074_id[] = {
+ { "imx074", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, imx074_id);
+
+static struct i2c_driver imx074_i2c_driver = {
+ .driver = {
+ .name = "imx074",
+ },
+ .probe = imx074_probe,
+ .remove = imx074_remove,
+ .id_table = imx074_id,
+};
+
+module_i2c_driver(imx074_i2c_driver);
+
+MODULE_DESCRIPTION("Sony IMX074 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c
new file mode 100644
index 000000000000..19f8a07764f9
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/mt9m001.c
@@ -0,0 +1,757 @@
+/*
+ * Driver for MT9M001 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * mt9m001 i2c address 0x5d
+ * The platform has to define struct i2c_board_info objects and link to them
+ * from struct soc_camera_link
+ */
+
+/* mt9m001 selected register addresses */
+#define MT9M001_CHIP_VERSION 0x00
+#define MT9M001_ROW_START 0x01
+#define MT9M001_COLUMN_START 0x02
+#define MT9M001_WINDOW_HEIGHT 0x03
+#define MT9M001_WINDOW_WIDTH 0x04
+#define MT9M001_HORIZONTAL_BLANKING 0x05
+#define MT9M001_VERTICAL_BLANKING 0x06
+#define MT9M001_OUTPUT_CONTROL 0x07
+#define MT9M001_SHUTTER_WIDTH 0x09
+#define MT9M001_FRAME_RESTART 0x0b
+#define MT9M001_SHUTTER_DELAY 0x0c
+#define MT9M001_RESET 0x0d
+#define MT9M001_READ_OPTIONS1 0x1e
+#define MT9M001_READ_OPTIONS2 0x20
+#define MT9M001_GLOBAL_GAIN 0x35
+#define MT9M001_CHIP_ENABLE 0xF1
+
+#define MT9M001_MAX_WIDTH 1280
+#define MT9M001_MAX_HEIGHT 1024
+#define MT9M001_MIN_WIDTH 48
+#define MT9M001_MIN_HEIGHT 32
+#define MT9M001_COLUMN_SKIP 20
+#define MT9M001_ROW_SKIP 12
+
+/* MT9M001 has only one fixed colorspace per pixelcode */
+struct mt9m001_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9m001_datafmt *mt9m001_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
+ /* Order important - see above */
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+};
+
+struct mt9m001 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ const struct mt9m001_datafmt *fmt;
+ const struct mt9m001_datafmt *fmts;
+ int num_fmts;
+ int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
+ unsigned int total_h;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int mt9m001_init(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ /*
+ * We don't know, whether platform provides reset, issue a soft reset
+ * too. This returns all registers to their default values.
+ */
+ ret = reg_write(client, MT9M001_RESET, 1);
+ if (!ret)
+ ret = reg_write(client, MT9M001_RESET, 0);
+
+ /* Disable chip, synchronous option update */
+ if (!ret)
+ ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
+
+ return ret;
+}
+
+static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* Switch to master "normal" mode or stop sensor readout */
+ if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+ const u16 hblank = 9, vblank = 25;
+
+ if (mt9m001->fmts == mt9m001_colour_fmts)
+ /*
+ * Bayer format - even number of rows for simplicity,
+ * but let the user play with the top row.
+ */
+ rect.height = ALIGN(rect.height, 2);
+
+ /* Datasheet requirement: see register description */
+ rect.width = ALIGN(rect.width, 2);
+ rect.left = ALIGN(rect.left, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
+
+ mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
+
+ /* Blanking and start values - default... */
+ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
+ if (!ret)
+ ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
+
+ /*
+ * The caller provides a supported format, as verified per
+ * call to .try_mbus_fmt()
+ */
+ if (!ret)
+ ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
+ if (!ret)
+ ret = reg_write(client, MT9M001_ROW_START, rect.top);
+ if (!ret)
+ ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
+ if (!ret)
+ ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
+ rect.height + mt9m001->y_skip_top - 1);
+ if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
+
+ if (!ret)
+ mt9m001->rect = rect;
+
+ return ret;
+}
+
+static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ a->c = mt9m001->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9M001_COLUMN_SKIP;
+ a->bounds.top = MT9M001_ROW_SKIP;
+ a->bounds.width = MT9M001_MAX_WIDTH;
+ a->bounds.height = MT9M001_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m001_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mf->code = mt9m001->fmt->code;
+ mf->colorspace = mt9m001->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9m001_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9m001->rect.left,
+ .top = mt9m001->rect.top,
+ .width = mf->width,
+ .height = mf->height,
+ },
+ };
+ int ret;
+
+ /* No support for scaling so far, just crop. TODO: use skipping */
+ ret = mt9m001_s_crop(sd, &a);
+ if (!ret) {
+ mf->width = mt9m001->rect.width;
+ mf->height = mt9m001->rect.height;
+ mt9m001->fmt = mt9m001_find_datafmt(mf->code,
+ mt9m001->fmts, mt9m001->num_fmts);
+ mf->colorspace = mt9m001->fmt->colorspace;
+ }
+
+ return ret;
+}
+
+static int mt9m001_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ const struct mt9m001_datafmt *fmt;
+
+ v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
+ MT9M001_MAX_WIDTH, 1,
+ &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
+ MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
+
+ if (mt9m001->fmts == mt9m001_colour_fmts)
+ mf->height = ALIGN(mf->height - 1, 2);
+
+ fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
+ mt9m001->num_fmts);
+ if (!fmt) {
+ fmt = mt9m001->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9m001->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m001_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 2;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9m001_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9m001_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ s32 min, max;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_AUTO:
+ min = mt9m001->exposure->minimum;
+ max = mt9m001->exposure->maximum;
+ mt9m001->exposure->val =
+ (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
+ break;
+ }
+ return 0;
+}
+
+static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m001 *mt9m001 = container_of(ctrl->handler,
+ struct mt9m001, hdl);
+ struct v4l2_subdev *sd = &mt9m001->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *exp = mt9m001->exposure;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
+ else
+ data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+
+ case V4L2_CID_GAIN:
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->val <= ctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
+ data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..15 variable step, register values 9..67 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
+ 111 + range / 2) / range + 9;
+
+ if (gain <= 32)
+ data = gain;
+ else if (gain <= 64)
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ data = ((gain - 64) * 7 + 28) / 56 + 96;
+
+ dev_dbg(&client->dev, "Setting gain from %d to %d\n",
+ reg_read(client, MT9M001_GLOBAL_GAIN), data);
+ data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+ return 0;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
+
+ dev_dbg(&client->dev,
+ "Setting shutter width from %d to %lu\n",
+ reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
+ return -EIO;
+ } else {
+ const u16 vblank = 25;
+
+ mt9m001->total_h = mt9m001->rect.height +
+ mt9m001->y_skip_top + vblank;
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
+ return -EIO;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m001_video_probe(struct soc_camera_link *icl,
+ struct i2c_client *client)
+{
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ s32 data;
+ unsigned long flags;
+ int ret;
+
+ ret = mt9m001_s_power(&mt9m001->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Enable the chip */
+ data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
+ dev_dbg(&client->dev, "write: %d\n", data);
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9M001_CHIP_VERSION);
+
+ /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
+ switch (data) {
+ case 0x8411:
+ case 0x8421:
+ mt9m001->model = V4L2_IDENT_MT9M001C12ST;
+ mt9m001->fmts = mt9m001_colour_fmts;
+ break;
+ case 0x8431:
+ mt9m001->model = V4L2_IDENT_MT9M001C12STM;
+ mt9m001->fmts = mt9m001_monochrome_fmts;
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9M001 chip detected, register read %x\n", data);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ mt9m001->num_fmts = 0;
+
+ /*
+ * This is a 10bit sensor, so by default we only allow 10bit.
+ * The platform may support different bus widths due to
+ * different routing of the data lines.
+ */
+ if (icl->query_bus_param)
+ flags = icl->query_bus_param(icl);
+ else
+ flags = SOCAM_DATAWIDTH_10;
+
+ if (flags & SOCAM_DATAWIDTH_10)
+ mt9m001->num_fmts++;
+ else
+ mt9m001->fmts++;
+
+ if (flags & SOCAM_DATAWIDTH_8)
+ mt9m001->num_fmts++;
+
+ mt9m001->fmt = &mt9m001->fmts[0];
+
+ dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
+ data == 0x8431 ? "C12STM" : "C12ST");
+
+ ret = mt9m001_init(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+ goto done;
+ }
+
+ /* mt9m001_init() has reset the chip, returning registers to defaults */
+ ret = v4l2_ctrl_handler_setup(&mt9m001->hdl);
+
+done:
+ mt9m001_s_power(&mt9m001->subdev, 0);
+ return ret;
+}
+
+static void mt9m001_video_remove(struct soc_camera_link *icl)
+{
+ if (icl->free_bus)
+ icl->free_bus(icl);
+}
+
+static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ *lines = mt9m001->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
+ .g_volatile_ctrl = mt9m001_g_volatile_ctrl,
+ .s_ctrl = mt9m001_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
+ .g_chip_ident = mt9m001_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m001_g_register,
+ .s_register = mt9m001_s_register,
+#endif
+ .s_power = mt9m001_s_power,
+};
+
+static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ if (index >= mt9m001->num_fmts)
+ return -EINVAL;
+
+ *code = mt9m001->fmts[index].code;
+ return 0;
+}
+
+static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* MT9M001 has all capture_format parameters fixed */
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ const struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
+
+ if (icl->set_bus_param)
+ return icl->set_bus_param(icl, 1 << (bps - 1));
+
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return bps == 10 ? 0 : -EINVAL;
+}
+
+static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
+ .s_stream = mt9m001_s_stream,
+ .s_mbus_fmt = mt9m001_s_fmt,
+ .g_mbus_fmt = mt9m001_g_fmt,
+ .try_mbus_fmt = mt9m001_try_fmt,
+ .s_crop = mt9m001_s_crop,
+ .g_crop = mt9m001_g_crop,
+ .cropcap = mt9m001_cropcap,
+ .enum_mbus_fmt = mt9m001_enum_fmt,
+ .g_mbus_config = mt9m001_g_mbus_config,
+ .s_mbus_config = mt9m001_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9m001_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9m001_subdev_ops = {
+ .core = &mt9m001_subdev_core_ops,
+ .video = &mt9m001_subdev_video_ops,
+ .sensor = &mt9m001_subdev_sensor_ops,
+};
+
+static int mt9m001_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9m001 *mt9m001;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9M001 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL);
+ if (!mt9m001)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+ mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
+ &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
+ if (mt9m001->hdl.error) {
+ int err = mt9m001->hdl.error;
+
+ kfree(mt9m001);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ /* Second stage probe - when a capture adapter is there */
+ mt9m001->y_skip_top = 0;
+ mt9m001->rect.left = MT9M001_COLUMN_SKIP;
+ mt9m001->rect.top = MT9M001_ROW_SKIP;
+ mt9m001->rect.width = MT9M001_MAX_WIDTH;
+ mt9m001->rect.height = MT9M001_MAX_HEIGHT;
+
+ ret = mt9m001_video_probe(icl, client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ kfree(mt9m001);
+ }
+
+ return ret;
+}
+
+static int mt9m001_remove(struct i2c_client *client)
+{
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&mt9m001->subdev);
+ v4l2_ctrl_handler_free(&mt9m001->hdl);
+ mt9m001_video_remove(icl);
+ kfree(mt9m001);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m001_id[] = {
+ { "mt9m001", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9m001_id);
+
+static struct i2c_driver mt9m001_i2c_driver = {
+ .driver = {
+ .name = "mt9m001",
+ },
+ .probe = mt9m001_probe,
+ .remove = mt9m001_remove,
+ .id_table = mt9m001_id,
+};
+
+module_i2c_driver(mt9m001_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c
new file mode 100644
index 000000000000..62fd94af599b
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/mt9m111.c
@@ -0,0 +1,1046 @@
+/*
+ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
+ *
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ *
+ * 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/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+
+/*
+ * MT9M111, MT9M112 and MT9M131:
+ * i2c address is 0x48 or 0x5d (depending on SADDR pin)
+ * The platform has to define i2c_board_info and call i2c_register_board_info()
+ */
+
+/*
+ * Sensor core register addresses (0x000..0x0ff)
+ */
+#define MT9M111_CHIP_VERSION 0x000
+#define MT9M111_ROW_START 0x001
+#define MT9M111_COLUMN_START 0x002
+#define MT9M111_WINDOW_HEIGHT 0x003
+#define MT9M111_WINDOW_WIDTH 0x004
+#define MT9M111_HORIZONTAL_BLANKING_B 0x005
+#define MT9M111_VERTICAL_BLANKING_B 0x006
+#define MT9M111_HORIZONTAL_BLANKING_A 0x007
+#define MT9M111_VERTICAL_BLANKING_A 0x008
+#define MT9M111_SHUTTER_WIDTH 0x009
+#define MT9M111_ROW_SPEED 0x00a
+#define MT9M111_EXTRA_DELAY 0x00b
+#define MT9M111_SHUTTER_DELAY 0x00c
+#define MT9M111_RESET 0x00d
+#define MT9M111_READ_MODE_B 0x020
+#define MT9M111_READ_MODE_A 0x021
+#define MT9M111_FLASH_CONTROL 0x023
+#define MT9M111_GREEN1_GAIN 0x02b
+#define MT9M111_BLUE_GAIN 0x02c
+#define MT9M111_RED_GAIN 0x02d
+#define MT9M111_GREEN2_GAIN 0x02e
+#define MT9M111_GLOBAL_GAIN 0x02f
+#define MT9M111_CONTEXT_CONTROL 0x0c8
+#define MT9M111_PAGE_MAP 0x0f0
+#define MT9M111_BYTE_WISE_ADDR 0x0f1
+
+#define MT9M111_RESET_SYNC_CHANGES (1 << 15)
+#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9)
+#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8)
+#define MT9M111_RESET_RESET_SOC (1 << 5)
+#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4)
+#define MT9M111_RESET_CHIP_ENABLE (1 << 3)
+#define MT9M111_RESET_ANALOG_STANDBY (1 << 2)
+#define MT9M111_RESET_RESTART_FRAME (1 << 1)
+#define MT9M111_RESET_RESET_MODE (1 << 0)
+
+#define MT9M111_RM_FULL_POWER_RD (0 << 10)
+#define MT9M111_RM_LOW_POWER_RD (1 << 10)
+#define MT9M111_RM_COL_SKIP_4X (1 << 5)
+#define MT9M111_RM_ROW_SKIP_4X (1 << 4)
+#define MT9M111_RM_COL_SKIP_2X (1 << 3)
+#define MT9M111_RM_ROW_SKIP_2X (1 << 2)
+#define MT9M111_RMB_MIRROR_COLS (1 << 1)
+#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
+#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
+#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12)
+#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10)
+#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9)
+#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8)
+#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7)
+#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3)
+#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
+#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
+#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
+
+/*
+ * Colorpipe register addresses (0x100..0x1ff)
+ */
+#define MT9M111_OPER_MODE_CTRL 0x106
+#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
+#define MT9M111_REDUCER_XZOOM_B 0x1a0
+#define MT9M111_REDUCER_XSIZE_B 0x1a1
+#define MT9M111_REDUCER_YZOOM_B 0x1a3
+#define MT9M111_REDUCER_YSIZE_B 0x1a4
+#define MT9M111_REDUCER_XZOOM_A 0x1a6
+#define MT9M111_REDUCER_XSIZE_A 0x1a7
+#define MT9M111_REDUCER_YZOOM_A 0x1a9
+#define MT9M111_REDUCER_YSIZE_A 0x1aa
+
+#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a
+#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
+
+#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
+#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
+#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9)
+#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8)
+#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
+#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
+#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
+#define MT9M111_OUTFMT_RGB (1 << 8)
+#define MT9M111_OUTFMT_RGB565 (0 << 6)
+#define MT9M111_OUTFMT_RGB555 (1 << 6)
+#define MT9M111_OUTFMT_RGB444x (2 << 6)
+#define MT9M111_OUTFMT_RGBx444 (3 << 6)
+#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4)
+#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4)
+#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
+#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
+#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
+#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0)
+
+/*
+ * Camera control register addresses (0x200..0x2ff not implemented)
+ */
+
+#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
+#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
+#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
+#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
+#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
+ (val), (mask))
+
+#define MT9M111_MIN_DARK_ROWS 8
+#define MT9M111_MIN_DARK_COLS 26
+#define MT9M111_MAX_HEIGHT 1024
+#define MT9M111_MAX_WIDTH 1280
+
+struct mt9m111_context {
+ u16 read_mode;
+ u16 blanking_h;
+ u16 blanking_v;
+ u16 reducer_xzoom;
+ u16 reducer_yzoom;
+ u16 reducer_xsize;
+ u16 reducer_ysize;
+ u16 output_fmt_ctrl2;
+ u16 control;
+};
+
+static struct mt9m111_context context_a = {
+ .read_mode = MT9M111_READ_MODE_A,
+ .blanking_h = MT9M111_HORIZONTAL_BLANKING_A,
+ .blanking_v = MT9M111_VERTICAL_BLANKING_A,
+ .reducer_xzoom = MT9M111_REDUCER_XZOOM_A,
+ .reducer_yzoom = MT9M111_REDUCER_YZOOM_A,
+ .reducer_xsize = MT9M111_REDUCER_XSIZE_A,
+ .reducer_ysize = MT9M111_REDUCER_YSIZE_A,
+ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A,
+ .control = MT9M111_CTXT_CTRL_RESTART,
+};
+
+static struct mt9m111_context context_b = {
+ .read_mode = MT9M111_READ_MODE_B,
+ .blanking_h = MT9M111_HORIZONTAL_BLANKING_B,
+ .blanking_v = MT9M111_VERTICAL_BLANKING_B,
+ .reducer_xzoom = MT9M111_REDUCER_XZOOM_B,
+ .reducer_yzoom = MT9M111_REDUCER_YZOOM_B,
+ .reducer_xsize = MT9M111_REDUCER_XSIZE_B,
+ .reducer_ysize = MT9M111_REDUCER_YSIZE_B,
+ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B,
+ .control = MT9M111_CTXT_CTRL_RESTART |
+ MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B |
+ MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B |
+ MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B |
+ MT9M111_CTXT_CTRL_HBLANK_SEL_B,
+};
+
+/* MT9M111 has only one fixed colorspace per pixelcode */
+struct mt9m111_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+};
+
+struct mt9m111 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *gain;
+ int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
+ * from v4l2-chip-ident.h */
+ struct mt9m111_context *ctx;
+ struct v4l2_rect rect; /* cropping rectangle */
+ int width; /* output */
+ int height; /* sizes */
+ struct mutex power_lock; /* lock to protect power_count */
+ int power_count;
+ const struct mt9m111_datafmt *fmt;
+ int lastpage; /* PageMap cache value */
+};
+
+/* Find a data format by a pixel code */
+static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111,
+ enum v4l2_mbus_pixelcode code)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++)
+ if (mt9m111_colour_fmts[i].code == code)
+ return mt9m111_colour_fmts + i;
+
+ return mt9m111->fmt;
+}
+
+static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m111, subdev);
+}
+
+static int reg_page_map_set(struct i2c_client *client, const u16 reg)
+{
+ int ret;
+ u16 page;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ page = (reg >> 8);
+ if (page == mt9m111->lastpage)
+ return 0;
+ if (page > 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page);
+ if (!ret)
+ mt9m111->lastpage = page;
+ return ret;
+}
+
+static int mt9m111_reg_read(struct i2c_client *client, const u16 reg)
+{
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = i2c_smbus_read_word_swapped(client, reg & 0xff);
+
+ dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret);
+ return ret;
+}
+
+static int mt9m111_reg_write(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_page_map_set(client, reg);
+ if (!ret)
+ ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data);
+ dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
+ return ret;
+}
+
+static int mt9m111_reg_set(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, ret | data);
+ return ret;
+}
+
+static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, ret & ~data);
+ return ret;
+}
+
+static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
+ const u16 data, const u16 mask)
+{
+ int ret;
+
+ ret = mt9m111_reg_read(client, reg);
+ if (ret >= 0)
+ ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
+ return ret;
+}
+
+static int mt9m111_set_context(struct mt9m111 *mt9m111,
+ struct mt9m111_context *ctx)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ return reg_write(CONTEXT_CONTROL, ctx->control);
+}
+
+static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111,
+ struct mt9m111_context *ctx, struct v4l2_rect *rect,
+ unsigned int width, unsigned int height)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_xsize, width);
+ if (!ret)
+ ret = mt9m111_reg_write(client, ctx->reducer_ysize, height);
+ return ret;
+}
+
+static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect,
+ int width, int height, enum v4l2_mbus_pixelcode code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ ret = reg_write(COLUMN_START, rect->left);
+ if (!ret)
+ ret = reg_write(ROW_START, rect->top);
+
+ if (!ret)
+ ret = reg_write(WINDOW_WIDTH, rect->width);
+ if (!ret)
+ ret = reg_write(WINDOW_HEIGHT, rect->height);
+
+ if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* IFP in use, down-scaling possible */
+ if (!ret)
+ ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
+ rect, width, height);
+ if (!ret)
+ ret = mt9m111_setup_rect_ctx(mt9m111, &context_a,
+ rect, width, height);
+ }
+
+ dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n",
+ __func__, code, rect->width, rect->height, rect->left, rect->top,
+ width, height, ret);
+
+ return ret;
+}
+
+static int mt9m111_enable(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE);
+}
+
+static int mt9m111_reset(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+ if (!ret)
+ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC);
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
+ | MT9M111_RESET_RESET_SOC);
+
+ return ret;
+}
+
+static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ int width, height;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* Bayer format - even size lengths */
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ /* FIXME: the datasheet doesn't specify minimum sizes */
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
+
+ width = min(mt9m111->width, rect.width);
+ height = min(mt9m111->height, rect.height);
+
+ ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code);
+ if (!ret) {
+ mt9m111->rect = rect;
+ mt9m111->width = width;
+ mt9m111->height = height;
+ }
+
+ return ret;
+}
+
+static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ a->c = mt9m111->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = MT9M111_MIN_DARK_COLS;
+ a->bounds.top = MT9M111_MIN_DARK_ROWS;
+ a->bounds.width = MT9M111_MAX_WIDTH;
+ a->bounds.height = MT9M111_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m111_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ mf->width = mt9m111->width;
+ mf->height = mt9m111->height;
+ mf->code = mt9m111->fmt->code;
+ mf->colorspace = mt9m111->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
+ MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ int ret;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
+ MT9M111_OUTFMT_RGB;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_BE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_BGR565_2X8_LE:
+ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
+ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ data_outfmt2 = 0;
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
+ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
+ break;
+ default:
+ dev_err(&client->dev, "Pixel format not handled: %x\n", code);
+ return -EINVAL;
+ }
+
+ ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2,
+ data_outfmt2, mask_outfmt2);
+ if (!ret)
+ ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2,
+ data_outfmt2, mask_outfmt2);
+
+ return ret;
+}
+
+static int mt9m111_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ const struct mt9m111_datafmt *fmt;
+ struct v4l2_rect *rect = &mt9m111->rect;
+ bool bayer;
+
+ fmt = mt9m111_find_datafmt(mt9m111, mf->code);
+
+ bayer = fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
+
+ /*
+ * With Bayer format enforce even side lengths, but let the user play
+ * with the starting pixel
+ */
+ if (bayer) {
+ rect->width = ALIGN(rect->width, 2);
+ rect->height = ALIGN(rect->height, 2);
+ }
+
+ if (fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
+ /* IFP bypass mode, no scaling */
+ mf->width = rect->width;
+ mf->height = rect->height;
+ } else {
+ /* No upscaling */
+ if (mf->width > rect->width)
+ mf->width = rect->width;
+ if (mf->height > rect->height)
+ mf->height = rect->height;
+ }
+
+ dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__,
+ mf->width, mf->height, fmt->code);
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9m111_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct mt9m111_datafmt *fmt;
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ struct v4l2_rect *rect = &mt9m111->rect;
+ int ret;
+
+ mt9m111_try_fmt(sd, mf);
+ fmt = mt9m111_find_datafmt(mt9m111, mf->code);
+ /* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */
+
+ ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code);
+ if (!ret)
+ ret = mt9m111_set_pixfmt(mt9m111, mf->code);
+ if (!ret) {
+ mt9m111->width = mf->width;
+ mt9m111->height = mf->height;
+ mt9m111->fmt = fmt;
+ }
+
+ return ret;
+}
+
+static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9m111->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m111_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int val;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ val = mt9m111_reg_read(client, reg->reg);
+ reg->size = 2;
+ reg->val = (u64)val;
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9m111_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (mt9m111_reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ if (flip)
+ ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask);
+ else
+ ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask);
+
+ return ret;
+}
+
+static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int data;
+
+ data = reg_read(GLOBAL_GAIN);
+ if (data >= 0)
+ return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
+ (1 << ((data >> 9) & 1));
+ return data;
+}
+
+static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ u16 val;
+
+ if (gain > 63 * 2 * 2)
+ return -EINVAL;
+
+ if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
+ val = (1 << 10) | (1 << 9) | (gain / 4);
+ else if ((gain >= 64) && (gain < 64 * 2))
+ val = (1 << 9) | (gain / 2);
+ else
+ val = gain;
+
+ return reg_write(GLOBAL_GAIN, val);
+}
+
+static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+
+ if (on)
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
+}
+
+static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+
+ if (on)
+ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
+}
+
+static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m111 *mt9m111 = container_of(ctrl->handler,
+ struct mt9m111, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ return mt9m111_set_flip(mt9m111, ctrl->val,
+ MT9M111_RMB_MIRROR_ROWS);
+ case V4L2_CID_HFLIP:
+ return mt9m111_set_flip(mt9m111, ctrl->val,
+ MT9M111_RMB_MIRROR_COLS);
+ case V4L2_CID_GAIN:
+ return mt9m111_set_global_gain(mt9m111, ctrl->val);
+ case V4L2_CID_EXPOSURE_AUTO:
+ return mt9m111_set_autoexposure(mt9m111, ctrl->val);
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
+ }
+
+ return -EINVAL;
+}
+
+static int mt9m111_suspend(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+ if (!ret)
+ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC |
+ MT9M111_RESET_OUTPUT_DISABLE |
+ MT9M111_RESET_ANALOG_STANDBY);
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
+
+ return ret;
+}
+
+static void mt9m111_restore_state(struct mt9m111 *mt9m111)
+{
+ mt9m111_set_context(mt9m111, mt9m111->ctx);
+ mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
+ mt9m111_setup_geometry(mt9m111, &mt9m111->rect,
+ mt9m111->width, mt9m111->height, mt9m111->fmt->code);
+ v4l2_ctrl_handler_setup(&mt9m111->hdl);
+}
+
+static int mt9m111_resume(struct mt9m111 *mt9m111)
+{
+ int ret = mt9m111_enable(mt9m111);
+ if (!ret)
+ ret = mt9m111_reset(mt9m111);
+ if (!ret)
+ mt9m111_restore_state(mt9m111);
+
+ return ret;
+}
+
+static int mt9m111_init(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ int ret;
+
+ /* Default HIGHPOWER context */
+ mt9m111->ctx = &context_b;
+ ret = mt9m111_enable(mt9m111);
+ if (!ret)
+ ret = mt9m111_reset(mt9m111);
+ if (!ret)
+ ret = mt9m111_set_context(mt9m111, mt9m111->ctx);
+ if (ret)
+ dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
+ return ret;
+}
+
+static int mt9m111_power_on(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ ret = soc_camera_power_on(&client->dev, icl);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9m111_resume(mt9m111);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret);
+ soc_camera_power_off(&client->dev, icl);
+ }
+
+ return ret;
+}
+
+static void mt9m111_power_off(struct mt9m111 *mt9m111)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ mt9m111_suspend(mt9m111);
+ soc_camera_power_off(&client->dev, icl);
+}
+
+static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
+ int ret = 0;
+
+ mutex_lock(&mt9m111->power_lock);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0 to 0,
+ * update the power state.
+ */
+ if (mt9m111->power_count == !on) {
+ if (on)
+ ret = mt9m111_power_on(mt9m111);
+ else
+ mt9m111_power_off(mt9m111);
+ }
+
+ if (!ret) {
+ /* Update the power count. */
+ mt9m111->power_count += on ? 1 : -1;
+ WARN_ON(mt9m111->power_count < 0);
+ }
+
+ mutex_unlock(&mt9m111->power_lock);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
+ .s_ctrl = mt9m111_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
+ .g_chip_ident = mt9m111_g_chip_ident,
+ .s_power = mt9m111_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m111_g_register,
+ .s_register = mt9m111_s_register,
+#endif
+};
+
+static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(mt9m111_colour_fmts))
+ return -EINVAL;
+
+ *code = mt9m111_colour_fmts[index].code;
+ return 0;
+}
+
+static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
+ .s_mbus_fmt = mt9m111_s_fmt,
+ .g_mbus_fmt = mt9m111_g_fmt,
+ .try_mbus_fmt = mt9m111_try_fmt,
+ .s_crop = mt9m111_s_crop,
+ .g_crop = mt9m111_g_crop,
+ .cropcap = mt9m111_cropcap,
+ .enum_mbus_fmt = mt9m111_enum_fmt,
+ .g_mbus_config = mt9m111_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops mt9m111_subdev_ops = {
+ .core = &mt9m111_subdev_core_ops,
+ .video = &mt9m111_subdev_video_ops,
+};
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9m111_video_probe(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+ s32 data;
+ int ret;
+
+ ret = mt9m111_s_power(&mt9m111->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ data = reg_read(CHIP_VERSION);
+
+ switch (data) {
+ case 0x143a: /* MT9M111 or MT9M131 */
+ mt9m111->model = V4L2_IDENT_MT9M111;
+ dev_info(&client->dev,
+ "Detected a MT9M111/MT9M131 chip ID %x\n", data);
+ break;
+ case 0x148c: /* MT9M112 */
+ mt9m111->model = V4L2_IDENT_MT9M112;
+ dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
+ data);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = mt9m111_init(mt9m111);
+ if (ret)
+ goto done;
+
+ ret = v4l2_ctrl_handler_setup(&mt9m111->hdl);
+
+done:
+ mt9m111_s_power(&mt9m111->subdev, 0);
+ return ret;
+}
+
+static int mt9m111_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9m111 *mt9m111;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "mt9m111: driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL);
+ if (!mt9m111)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
+ v4l2_ctrl_new_std_menu(&mt9m111->hdl,
+ &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
+ if (mt9m111->hdl.error) {
+ int err = mt9m111->hdl.error;
+
+ kfree(mt9m111);
+ return err;
+ }
+
+ /* Second stage probe - when a capture adapter is there */
+ mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
+ mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
+ mt9m111->rect.width = MT9M111_MAX_WIDTH;
+ mt9m111->rect.height = MT9M111_MAX_HEIGHT;
+ mt9m111->fmt = &mt9m111_colour_fmts[0];
+ mt9m111->lastpage = -1;
+ mutex_init(&mt9m111->power_lock);
+
+ ret = mt9m111_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
+ kfree(mt9m111);
+ }
+
+ return ret;
+}
+
+static int mt9m111_remove(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ v4l2_device_unregister_subdev(&mt9m111->subdev);
+ v4l2_ctrl_handler_free(&mt9m111->hdl);
+ kfree(mt9m111);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9m111_id[] = {
+ { "mt9m111", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9m111_id);
+
+static struct i2c_driver mt9m111_i2c_driver = {
+ .driver = {
+ .name = "mt9m111",
+ },
+ .probe = mt9m111_probe,
+ .remove = mt9m111_remove,
+ .id_table = mt9m111_id,
+};
+
+module_i2c_driver(mt9m111_i2c_driver);
+
+MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
+MODULE_AUTHOR("Robert Jarzmik");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c
new file mode 100644
index 000000000000..40800b10a080
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/mt9t031.c
@@ -0,0 +1,857 @@
+/*
+ * Driver for MT9T031 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
+ *
+ * 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/device.h>
+#include <linux/i2c.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * ATTENTION: this driver still cannot be used outside of the soc-camera
+ * framework because of its PM implementation, using the video_device node.
+ * If hardware becomes available for testing, alternative PM approaches shall
+ * be considered and tested.
+ */
+
+/*
+ * mt9t031 i2c address 0x5d
+ * The platform has to define i2c_board_info and link to it from
+ * struct soc_camera_link
+ */
+
+/* mt9t031 selected register addresses */
+#define MT9T031_CHIP_VERSION 0x00
+#define MT9T031_ROW_START 0x01
+#define MT9T031_COLUMN_START 0x02
+#define MT9T031_WINDOW_HEIGHT 0x03
+#define MT9T031_WINDOW_WIDTH 0x04
+#define MT9T031_HORIZONTAL_BLANKING 0x05
+#define MT9T031_VERTICAL_BLANKING 0x06
+#define MT9T031_OUTPUT_CONTROL 0x07
+#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
+#define MT9T031_SHUTTER_WIDTH 0x09
+#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
+#define MT9T031_FRAME_RESTART 0x0b
+#define MT9T031_SHUTTER_DELAY 0x0c
+#define MT9T031_RESET 0x0d
+#define MT9T031_READ_MODE_1 0x1e
+#define MT9T031_READ_MODE_2 0x20
+#define MT9T031_READ_MODE_3 0x21
+#define MT9T031_ROW_ADDRESS_MODE 0x22
+#define MT9T031_COLUMN_ADDRESS_MODE 0x23
+#define MT9T031_GLOBAL_GAIN 0x35
+#define MT9T031_CHIP_ENABLE 0xF8
+
+#define MT9T031_MAX_HEIGHT 1536
+#define MT9T031_MAX_WIDTH 2048
+#define MT9T031_MIN_HEIGHT 2
+#define MT9T031_MIN_WIDTH 18
+#define MT9T031_HORIZONTAL_BLANK 142
+#define MT9T031_VERTICAL_BLANK 25
+#define MT9T031_COLUMN_SKIP 32
+#define MT9T031_ROW_SKIP 20
+
+struct mt9t031 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
+ u16 xskip;
+ u16 yskip;
+ unsigned int total_h;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int set_shutter(struct i2c_client *client, const u32 data)
+{
+ int ret;
+
+ ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
+
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);
+
+ return ret;
+}
+
+static int get_shutter(struct i2c_client *client, u32 *data)
+{
+ int ret;
+
+ ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
+ *data = ret << 16;
+
+ if (ret >= 0)
+ ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
+ *data |= ret & 0xffff;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_idle(struct i2c_client *client)
+{
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+ ret = reg_write(client, MT9T031_RESET, 1);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_RESET, 0);
+ if (ret >= 0)
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
+
+ return ret >= 0 ? 0 : -EIO;
+}
+
+static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable)
+ /* Switch to master "normal" mode */
+ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2);
+ else
+ /* Stop sensor readout */
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
+
+ if (ret < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/* target must be _even_ */
+static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
+{
+ unsigned int skip;
+
+ if (*source < target + target / 2) {
+ *source = target;
+ return 1;
+ }
+
+ skip = min(max, *source + target / 2) / target;
+ if (skip > 8)
+ skip = 8;
+ *source = target * skip;
+
+ return skip;
+}
+
+/* rect is the sensor rectangle, the caller guarantees parameter validity */
+static int mt9t031_set_params(struct i2c_client *client,
+ struct v4l2_rect *rect, u16 xskip, u16 yskip)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ int ret;
+ u16 xbin, ybin;
+ const u16 hblank = MT9T031_HORIZONTAL_BLANK,
+ vblank = MT9T031_VERTICAL_BLANK;
+
+ xbin = min(xskip, (u16)3);
+ ybin = min(yskip, (u16)3);
+
+ /*
+ * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper.
+ * There is always a valid suitably aligned value. The worst case is
+ * xbin = 3, width = 2048. Then we will start at 36, the last read out
+ * pixel will be 2083, which is < 2085 - first black pixel.
+ *
+ * MT9T031 datasheet imposes window left border alignment, depending on
+ * the selected xskip. Failing to conform to this requirement produces
+ * dark horizontal stripes in the image. However, even obeying to this
+ * requirement doesn't eliminate the stripes in all configurations. They
+ * appear "locally reproducibly," but can differ between tests under
+ * different lighting conditions.
+ */
+ switch (xbin) {
+ case 1:
+ rect->left &= ~1;
+ break;
+ case 2:
+ rect->left &= ~3;
+ break;
+ case 3:
+ rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ?
+ (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6);
+ }
+
+ rect->top &= ~1;
+
+ dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
+ xskip, yskip, rect->width, rect->height, rect->left, rect->top);
+
+ /* Disable register update, reconfigure atomically */
+ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Blanking and start values - default... */
+ ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank);
+
+ if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
+ /* Binning, skipping */
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (xskip - 1));
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ }
+ dev_dbg(&client->dev, "new physical left %u, top %u\n",
+ rect->left, rect->top);
+
+ /*
+ * The caller provides a supported format, as guaranteed by
+ * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
+ */
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_ROW_START, rect->top);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
+ if (ret >= 0)
+ ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
+ rect->height + mt9t031->y_skip_top - 1);
+ if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
+ mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
+
+ ret = set_shutter(client, mt9t031->total_h);
+ }
+
+ /* Re-enable register update, commit all changes */
+ if (ret >= 0)
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
+
+ if (ret >= 0) {
+ mt9t031->rect = *rect;
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
+ return ret < 0 ? ret : 0;
+}
+
+static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
+
+ return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
+}
+
+static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ a->c = mt9t031->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9T031_COLUMN_SKIP;
+ a->bounds.top = MT9T031_ROW_SKIP;
+ a->bounds.width = MT9T031_MAX_WIDTH;
+ a->bounds.height = MT9T031_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9t031_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ mf->width = mt9t031->rect.width / mt9t031->xskip;
+ mf->height = mt9t031->rect.height / mt9t031->yskip;
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t031_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ u16 xskip, yskip;
+ struct v4l2_rect rect = mt9t031->rect;
+
+ /*
+ * try_fmt has put width and height within limits.
+ * S_FMT: use binning and skipping for scaling
+ */
+ xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
+ yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ /* mt9t031_set_params() doesn't change width and height */
+ return mt9t031_set_params(client, &rect, xskip, yskip);
+}
+
+/*
+ * If a user window larger than sensor window is requested, we'll increase the
+ * sensor window.
+ */
+static int mt9t031_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ v4l_bound_align_image(
+ &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
+ &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
+
+ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9t031->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t031_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9t031_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
+ s32 min, max;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE_AUTO:
+ min = mt9t031->exposure->minimum;
+ max = mt9t031->exposure->maximum;
+ mt9t031->exposure->val =
+ (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
+ / shutter_max + min;
+ break;
+ }
+ return 0;
+}
+
+static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9t031 *mt9t031 = container_of(ctrl->handler,
+ struct mt9t031, hdl);
+ struct v4l2_subdev *sd = &mt9t031->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *exp = mt9t031->exposure;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
+ else
+ data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
+ else
+ data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_GAIN:
+ /* See Datasheet Table 7, Gain settings. */
+ if (ctrl->val <= ctrl->default_value) {
+ /* Pack it into 0..1 step 0.125, register values 0..8 */
+ unsigned long range = ctrl->default_value - ctrl->minimum;
+ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
+
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
+ data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ } else {
+ /* Pack it into 1.125..128 variable step, register values 9..0x7860 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = ctrl->maximum - ctrl->default_value - 1;
+ /* calculated gain: map 65..127 to 9..1024 step 0.125 */
+ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
+ 1015 + range / 2) / range + 9;
+
+ if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
+ data = gain;
+ else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+ /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
+ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
+
+ dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n",
+ reg_read(client, MT9T031_GLOBAL_GAIN), data);
+ data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+ }
+ return 0;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
+ unsigned int range = exp->maximum - exp->minimum;
+ unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
+ range / 2) / range + 1;
+ u32 old;
+
+ get_shutter(client, &old);
+ dev_dbg(&client->dev, "Set shutter from %u to %u\n",
+ old, shutter);
+ if (set_shutter(client, shutter) < 0)
+ return -EIO;
+ } else {
+ const u16 vblank = MT9T031_VERTICAL_BLANK;
+ mt9t031->total_h = mt9t031->rect.height +
+ mt9t031->y_skip_top + vblank;
+
+ if (set_shutter(client, mt9t031->total_h) < 0)
+ return -EIO;
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Power Management:
+ * This function does nothing for now but must be present for pm to work
+ */
+static int mt9t031_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+/*
+ * Power Management:
+ * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged
+ * they are however changed at reset if the platform hook is present
+ * thus we rewrite them with the values stored by the driver
+ */
+static int mt9t031_runtime_resume(struct device *dev)
+{
+ struct video_device *vdev = to_video_device(dev);
+ struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ int ret;
+ u16 xbin, ybin;
+
+ xbin = min(mt9t031->xskip, (u16)3);
+ ybin = min(mt9t031->yskip, (u16)3);
+
+ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
+ ((xbin - 1) << 4) | (mt9t031->xskip - 1));
+ if (ret < 0)
+ return ret;
+
+ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (mt9t031->yskip - 1));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct dev_pm_ops mt9t031_dev_pm_ops = {
+ .runtime_suspend = mt9t031_runtime_suspend,
+ .runtime_resume = mt9t031_runtime_resume,
+};
+
+static struct device_type mt9t031_dev_type = {
+ .name = "MT9T031",
+ .pm = &mt9t031_dev_pm_ops,
+};
+
+static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct video_device *vdev = soc_camera_i2c_to_vdev(client);
+ int ret;
+
+ if (on) {
+ ret = soc_camera_power_on(&client->dev, icl);
+ if (ret < 0)
+ return ret;
+ vdev->dev.type = &mt9t031_dev_type;
+ } else {
+ vdev->dev.type = NULL;
+ soc_camera_power_off(&client->dev, icl);
+ }
+
+ return 0;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9t031_video_probe(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ s32 data;
+ int ret;
+
+ ret = mt9t031_s_power(&mt9t031->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mt9t031_idle(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+ goto done;
+ }
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9T031_CHIP_VERSION);
+
+ switch (data) {
+ case 0x1621:
+ mt9t031->model = V4L2_IDENT_MT9T031;
+ break;
+ default:
+ dev_err(&client->dev,
+ "No MT9T031 chip detected, register read %x\n", data);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
+
+ ret = v4l2_ctrl_handler_setup(&mt9t031->hdl);
+
+done:
+ mt9t031_s_power(&mt9t031->subdev, 0);
+
+ return ret;
+}
+
+static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ *lines = mt9t031->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
+ .g_volatile_ctrl = mt9t031_g_volatile_ctrl,
+ .s_ctrl = mt9t031_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
+ .g_chip_ident = mt9t031_g_chip_ident,
+ .s_power = mt9t031_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t031_g_register,
+ .s_register = mt9t031_s_register,
+#endif
+};
+
+static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+ else
+ return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+}
+
+static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
+ .s_stream = mt9t031_s_stream,
+ .s_mbus_fmt = mt9t031_s_fmt,
+ .g_mbus_fmt = mt9t031_g_fmt,
+ .try_mbus_fmt = mt9t031_try_fmt,
+ .s_crop = mt9t031_s_crop,
+ .g_crop = mt9t031_g_crop,
+ .cropcap = mt9t031_cropcap,
+ .enum_mbus_fmt = mt9t031_enum_fmt,
+ .g_mbus_config = mt9t031_g_mbus_config,
+ .s_mbus_config = mt9t031_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9t031_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9t031_subdev_ops = {
+ .core = &mt9t031_subdev_core_ops,
+ .video = &mt9t031_subdev_video_ops,
+ .sensor = &mt9t031_subdev_sensor_ops,
+};
+
+static int mt9t031_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t031 *mt9t031;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9T031 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
+ if (!mt9t031)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
+ &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
+ if (mt9t031->hdl.error) {
+ int err = mt9t031->hdl.error;
+
+ kfree(mt9t031);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ mt9t031->y_skip_top = 0;
+ mt9t031->rect.left = MT9T031_COLUMN_SKIP;
+ mt9t031->rect.top = MT9T031_ROW_SKIP;
+ mt9t031->rect.width = MT9T031_MAX_WIDTH;
+ mt9t031->rect.height = MT9T031_MAX_HEIGHT;
+
+ mt9t031->xskip = 1;
+ mt9t031->yskip = 1;
+
+ ret = mt9t031_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
+ kfree(mt9t031);
+ }
+
+ return ret;
+}
+
+static int mt9t031_remove(struct i2c_client *client)
+{
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+
+ v4l2_device_unregister_subdev(&mt9t031->subdev);
+ v4l2_ctrl_handler_free(&mt9t031->hdl);
+ kfree(mt9t031);
+
+ return 0;
+}
+
+static const struct i2c_device_id mt9t031_id[] = {
+ { "mt9t031", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t031_id);
+
+static struct i2c_driver mt9t031_i2c_driver = {
+ .driver = {
+ .name = "mt9t031",
+ },
+ .probe = mt9t031_probe,
+ .remove = mt9t031_remove,
+ .id_table = mt9t031_id,
+};
+
+module_i2c_driver(mt9t031_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c
new file mode 100644
index 000000000000..de7cd836b0a2
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/mt9t112.c
@@ -0,0 +1,1142 @@
+/*
+ * mt9t112 Camera Driver
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver, mt9m111 driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/mt9t112.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+
+/* you can check PLL/clock info */
+/* #define EXT_CLOCK 24000000 */
+
+/************************************************************************
+ macro
+************************************************************************/
+/*
+ * frame size
+ */
+#define MAX_WIDTH 2048
+#define MAX_HEIGHT 1536
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+
+/*
+ * macro of read/write
+ */
+#define ECHECKER(ret, x) \
+ do { \
+ (ret) = (x); \
+ if ((ret) < 0) \
+ return (ret); \
+ } while (0)
+
+#define mt9t112_reg_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_reg_write(client, a, b))
+#define mt9t112_mcu_write(ret, client, a, b) \
+ ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
+
+#define mt9t112_reg_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
+#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
+ ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
+
+#define mt9t112_reg_read(ret, client, a) \
+ ECHECKER(ret, __mt9t112_reg_read(client, a))
+
+/*
+ * Logical address
+ */
+#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
+#define VAR(id, offset) _VAR(id, offset, 0x0000)
+#define VAR8(id, offset) _VAR(id, offset, 0x8000)
+
+/************************************************************************
+ struct
+************************************************************************/
+struct mt9t112_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u16 fmt;
+ u16 order;
+};
+
+struct mt9t112_priv {
+ struct v4l2_subdev subdev;
+ struct mt9t112_camera_info *info;
+ struct i2c_client *client;
+ struct v4l2_rect frame;
+ const struct mt9t112_format *format;
+ int model;
+ u32 flags;
+/* for flags */
+#define INIT_DONE (1 << 0)
+#define PCLK_RISING (1 << 1)
+};
+
+/************************************************************************
+ supported format
+************************************************************************/
+
+static const struct mt9t112_format mt9t112_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ }, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 1,
+ }, {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 3,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 8,
+ .order = 2,
+ }, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .fmt = 4,
+ .order = 2,
+ },
+};
+
+/************************************************************************
+ general function
+************************************************************************/
+static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client),
+ struct mt9t112_priv,
+ subdev);
+}
+
+static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
+{
+ struct i2c_msg msg[2];
+ u8 buf[2];
+ int ret;
+
+ command = swab16(command);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 2;
+ msg[0].buf = (u8 *)&command;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = buf;
+
+ /*
+ * if return value of this function is < 0,
+ * it mean error.
+ * else, under 16bit is valid data.
+ */
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+
+ memcpy(&ret, buf, 2);
+ return swab16(ret);
+}
+
+static int __mt9t112_reg_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ command = swab16(command);
+ data = swab16(data);
+
+ memcpy(buf + 0, &command, 2);
+ memcpy(buf + 2, &data, 2);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 4;
+ msg.buf = buf;
+
+ /*
+ * i2c_transfer return message length,
+ * but this function should return 0 if correct case
+ */
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret >= 0)
+ ret = 0;
+
+ return ret;
+}
+
+static int __mt9t112_reg_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_reg_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_reg_write(client, command, val);
+}
+
+/* mcu access */
+static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_read(client, 0x0990);
+}
+
+static int __mt9t112_mcu_write(const struct i2c_client *client,
+ u16 command, u16 data)
+{
+ int ret;
+
+ ret = __mt9t112_reg_write(client, 0x098E, command);
+ if (ret < 0)
+ return ret;
+
+ return __mt9t112_reg_write(client, 0x0990, data);
+}
+
+static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
+ u16 command,
+ u16 mask,
+ u16 set)
+{
+ int val = __mt9t112_mcu_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return __mt9t112_mcu_write(client, command, val);
+}
+
+static int mt9t112_reset(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
+ msleep(1);
+ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
+
+ return ret;
+}
+
+#ifndef EXT_CLOCK
+#define CLOCK_INFO(a, b)
+#else
+#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
+static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
+{
+ int m, n, p1, p2, p3, p4, p5, p6, p7;
+ u32 vco, clk;
+ char *enable;
+
+ ext /= 1000; /* kbyte order */
+
+ mt9t112_reg_read(n, client, 0x0012);
+ p1 = n & 0x000f;
+ n = n >> 4;
+ p2 = n & 0x000f;
+ n = n >> 4;
+ p3 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002a);
+ p4 = n & 0x000f;
+ n = n >> 4;
+ p5 = n & 0x000f;
+ n = n >> 4;
+ p6 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x002c);
+ p7 = n & 0x000f;
+
+ mt9t112_reg_read(n, client, 0x0010);
+ m = n & 0x00ff;
+ n = (n >> 8) & 0x003f;
+
+ enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
+ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
+
+ vco = 2 * m * ext / (n+1);
+ enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
+ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
+
+ clk = vco / (p1+1) / (p2+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p3+1);
+ enable = (768000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p6+1);
+ enable = (96000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p5+1);
+ enable = (54000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p4+1);
+ enable = (70000 < clk) ? "X" : "";
+ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
+
+ clk = vco / (p7+1);
+ dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
+
+ clk = ext / (n+1);
+ enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
+ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
+
+ return 0;
+}
+#endif
+
+static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
+{
+ soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
+ soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
+}
+
+static int mt9t112_set_a_frame_size(const struct i2c_client *client,
+ u16 width,
+ u16 height)
+{
+ int ret;
+ u16 wstart = (MAX_WIDTH - width) / 2;
+ u16 hstart = (MAX_HEIGHT - height) / 2;
+
+ /* (Context A) Image Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(26, 0), width);
+ mt9t112_mcu_write(ret, client, VAR(26, 2), height);
+
+ /* (Context A) Output Width/Height */
+ mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
+ mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
+
+ /* (Context A) Start Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
+
+ /* (Context A) End Row/Column */
+ mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
+ mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ return ret;
+}
+
+static int mt9t112_set_pll_dividers(const struct i2c_client *client,
+ u8 m, u8 n,
+ u8 p1, u8 p2, u8 p3,
+ u8 p4, u8 p5, u8 p6,
+ u8 p7)
+{
+ int ret;
+ u16 val;
+
+ /* N/M */
+ val = (n << 8) |
+ (m << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
+
+ /* P1/P2/P3 */
+ val = ((p3 & 0x0F) << 8) |
+ ((p2 & 0x0F) << 4) |
+ ((p1 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
+
+ /* P4/P5/P6 */
+ val = (0x7 << 12) |
+ ((p6 & 0x0F) << 8) |
+ ((p5 & 0x0F) << 4) |
+ ((p4 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
+
+ /* P7 */
+ val = (0x1 << 12) |
+ ((p7 & 0x0F) << 0);
+ mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
+
+ return ret;
+}
+
+static int mt9t112_init_pll(const struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int data, i, ret;
+
+ mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
+
+ /* PLL control: BYPASS PLL = 8517 */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2145);
+
+ /* Replace these registers when new timing parameters are generated */
+ mt9t112_set_pll_dividers(client,
+ priv->info->divider.m,
+ priv->info->divider.n,
+ priv->info->divider.p1,
+ priv->info->divider.p2,
+ priv->info->divider.p3,
+ priv->info->divider.p4,
+ priv->info->divider.p5,
+ priv->info->divider.p6,
+ priv->info->divider.p7);
+
+ /*
+ * TEST_BYPASS on
+ * PLL_ENABLE on
+ * SEL_LOCK_DET on
+ * TEST_BYPASS off
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x2525);
+ mt9t112_reg_write(ret, client, 0x0014, 0x2527);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3427);
+ mt9t112_reg_write(ret, client, 0x0014, 0x3027);
+
+ mdelay(10);
+
+ /*
+ * PLL_BYPASS off
+ * Reference clock count
+ * I2C Master Clock Divider
+ */
+ mt9t112_reg_write(ret, client, 0x0014, 0x3046);
+ mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */
+ mt9t112_reg_write(ret, client, 0x0022, 0x0190);
+ mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
+
+ /* External sensor clock is PLL bypass */
+ mt9t112_reg_write(ret, client, 0x002E, 0x0500);
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
+ mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
+
+ /* MCU disabled */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
+
+ /* out of standby */
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
+
+ mdelay(50);
+
+ /*
+ * Standby Workaround
+ * Disable Secondary I2C Pads
+ */
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+ mt9t112_reg_write(ret, client, 0x0614, 0x0001);
+ mdelay(1);
+
+ /* poll to verify out of standby. Must Poll this bit */
+ for (i = 0; i < 100; i++) {
+ mt9t112_reg_read(data, client, 0x0018);
+ if (!(0x4000 & data))
+ break;
+
+ mdelay(10);
+ }
+
+ return ret;
+}
+
+static int mt9t112_init_setting(const struct i2c_client *client)
+{
+
+ int ret;
+
+ /* Adaptive Output Clock (A) */
+ mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
+
+ /* Read Mode (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
+
+ /* Fine Correction (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
+
+ /* Fine IT Min (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
+
+ /* Fine IT Max Margin (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
+
+ /* Base Frame Lines (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
+
+ /* Min Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
+
+ /* Line Length (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
+
+ /* Adaptive Output Clock (B) */
+ mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
+
+ /* Row Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
+
+ /* Column Start (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
+
+ /* Row End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
+
+ /* Column End (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
+
+ /* Fine Correction (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
+
+ /* Fine IT Min (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
+
+ /* Fine IT Max Margin (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
+
+ /* Base Frame Lines (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
+
+ /* Min Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
+
+ /* Line Length (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
+
+ /*
+ * Flicker Dectection registers
+ * This section should be replaced whenever new Timing file is generated
+ * All the following registers need to be replaced
+ * Following registers are generated from Register Wizard but user can
+ * modify them. For detail see auto flicker detection tuning
+ */
+
+ /* FD_FDPERIOD_SELECT */
+ mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
+
+ /* PRI_B_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
+
+ /* PRI_A_CONFIG_FD_ALGO_RUN */
+ mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
+
+ /*
+ * AFD range detection tuning registers
+ */
+
+ /* search_f1_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
+
+ /* search_f2_50 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
+
+ /* search_f1_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
+
+ /* search_f2_60 */
+ mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
+
+ /* period_50Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
+
+ /* secret register by aptina */
+ /* period_50Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
+
+ /* period_60Hz (A) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
+
+ /* secret register by aptina */
+ /* period_60Hz (A MSB) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
+
+ /* period_50Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
+
+ /* secret register by aptina */
+ /* period_50Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
+
+ /* period_60Hz (B) */
+ mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
+
+ /* secret register by aptina */
+ /* period_60Hz (B) MSB */
+ mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
+
+ /* FD Mode */
+ mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
+
+ /* Stat_min */
+ mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
+
+ /* Stat_max */
+ mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
+
+ /* Min_amplitude */
+ mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
+
+ /* RX FIFO Watermark (A) */
+ mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
+
+ /* RX FIFO Watermark (B) */
+ mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
+
+ /* MCLK: 16MHz
+ * PCLK: 73MHz
+ * CorePixCLK: 36.5 MHz
+ */
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
+
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
+ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_setting(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
+ mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_reg_write(ret, client, 0x0614, 0x0000);
+
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+ mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
+ mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
+ mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
+ mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
+ mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
+ mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
+
+ return ret;
+}
+
+static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
+{
+ int ret;
+
+ mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
+
+ return ret;
+}
+
+static int mt9t112_init_camera(const struct i2c_client *client)
+{
+ int ret;
+
+ ECHECKER(ret, mt9t112_reset(client));
+
+ ECHECKER(ret, mt9t112_init_pll(client));
+
+ ECHECKER(ret, mt9t112_init_setting(client));
+
+ ECHECKER(ret, mt9t112_auto_focus_setting(client));
+
+ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
+
+ /* Analog setting B */
+ mt9t112_reg_write(ret, client, 0x3084, 0x2409);
+ mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
+ mt9t112_reg_write(ret, client, 0x3094, 0x4949);
+ mt9t112_reg_write(ret, client, 0x3096, 0x4950);
+
+ /*
+ * Disable adaptive clock
+ * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
+ */
+ mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
+ mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
+
+ /* Configure STatus in Status_before_length Format and enable header */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
+
+ /* Enable JPEG in context B */
+ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
+ mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
+
+ /* Disable Dac_TXLO */
+ mt9t112_reg_write(ret, client, 0x316C, 0x350F);
+
+ /* Set max slew rates */
+ mt9t112_reg_write(ret, client, 0x1E, 0x777);
+
+ return ret;
+}
+
+/************************************************************************
+ v4l2_subdev_core_ops
+************************************************************************/
+static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9t112_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 2;
+ mt9t112_reg_read(ret, client, reg->reg);
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int mt9t112_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ mt9t112_reg_write(ret, client, reg->reg, reg->val);
+
+ return ret;
+}
+#endif
+
+static int mt9t112_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
+ .g_chip_ident = mt9t112_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t112_g_register,
+ .s_register = mt9t112_s_register,
+#endif
+ .s_power = mt9t112_s_power,
+};
+
+
+/************************************************************************
+ v4l2_subdev_video_ops
+************************************************************************/
+static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ int ret = 0;
+
+ if (!enable) {
+ /* FIXME
+ *
+ * If user selected large output size,
+ * and used it long time,
+ * mt9t112 camera will be very warm.
+ *
+ * But current driver can not stop mt9t112 camera.
+ * So, set small size here to solve this problem.
+ */
+ mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
+ return ret;
+ }
+
+ if (!(priv->flags & INIT_DONE)) {
+ u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
+
+ ECHECKER(ret, mt9t112_init_camera(client));
+
+ /* Invert PCLK (Data sampled on falling edge of pixclk) */
+ mt9t112_reg_write(ret, client, 0x3C20, param);
+
+ mdelay(5);
+
+ priv->flags |= INIT_DONE;
+ }
+
+ mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
+ mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
+ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
+
+ mt9t112_set_a_frame_size(client,
+ priv->frame.width,
+ priv->frame.height);
+
+ ECHECKER(ret, mt9t112_auto_focus_trigger(client));
+
+ dev_dbg(&client->dev, "format : %d\n", priv->format->code);
+ dev_dbg(&client->dev, "size : %d x %d\n",
+ priv->frame.width,
+ priv->frame.height);
+
+ CLOCK_INFO(client, EXT_CLOCK);
+
+ return ret;
+}
+
+static int mt9t112_set_params(struct mt9t112_priv *priv,
+ const struct v4l2_rect *rect,
+ enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ /*
+ * get color format
+ */
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ priv->frame = *rect;
+
+ /*
+ * frame size check
+ */
+ mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
+ &priv->frame.left, &priv->frame.top);
+
+ priv->format = mt9t112_cfmts + i;
+
+ return 0;
+}
+
+static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = MAX_WIDTH;
+ a->bounds.height = MAX_HEIGHT;
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VGA_WIDTH;
+ a->defrect.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ a->c = priv->frame;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const struct v4l2_rect *rect = &a->c;
+
+ return mt9t112_set_params(priv, rect, priv->format->code);
+}
+
+static int mt9t112_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ mf->width = priv->frame.width;
+ mf->height = priv->frame.height;
+ mf->colorspace = priv->format->colorspace;
+ mf->code = priv->format->code;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ struct v4l2_rect rect = {
+ .width = mf->width,
+ .height = mf->height,
+ .left = priv->frame.left,
+ .top = priv->frame.top,
+ };
+ int ret;
+
+ ret = mt9t112_set_params(priv, &rect, mf->code);
+
+ if (!ret)
+ mf->colorspace = priv->format->colorspace;
+
+ return ret;
+}
+
+static int mt9t112_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ unsigned int top, left;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
+ if (mt9t112_cfmts[i].code == mf->code)
+ break;
+
+ if (i == ARRAY_SIZE(mt9t112_cfmts)) {
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ mf->colorspace = mt9t112_cfmts[i].colorspace;
+ }
+
+ mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(mt9t112_cfmts))
+ return -EINVAL;
+
+ *code = mt9t112_cfmts[index].code;
+
+ return 0;
+}
+
+static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ priv->flags |= PCLK_RISING;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
+ .s_stream = mt9t112_s_stream,
+ .g_mbus_fmt = mt9t112_g_fmt,
+ .s_mbus_fmt = mt9t112_s_fmt,
+ .try_mbus_fmt = mt9t112_try_fmt,
+ .cropcap = mt9t112_cropcap,
+ .g_crop = mt9t112_g_crop,
+ .s_crop = mt9t112_s_crop,
+ .enum_mbus_fmt = mt9t112_enum_fmt,
+ .g_mbus_config = mt9t112_g_mbus_config,
+ .s_mbus_config = mt9t112_s_mbus_config,
+};
+
+/************************************************************************
+ i2c driver
+************************************************************************/
+static struct v4l2_subdev_ops mt9t112_subdev_ops = {
+ .core = &mt9t112_subdev_core_ops,
+ .video = &mt9t112_subdev_video_ops,
+};
+
+static int mt9t112_camera_probe(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+ const char *devname;
+ int chipid;
+ int ret;
+
+ ret = mt9t112_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show chip ID
+ */
+ mt9t112_reg_read(chipid, client, 0x0000);
+
+ switch (chipid) {
+ case 0x2680:
+ devname = "mt9t111";
+ priv->model = V4L2_IDENT_MT9T111;
+ break;
+ case 0x2682:
+ devname = "mt9t112";
+ priv->model = V4L2_IDENT_MT9T112;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %04x\n", chipid);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
+
+done:
+ mt9t112_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static int mt9t112_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9t112_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct v4l2_rect rect = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .left = (MAX_WIDTH - VGA_WIDTH) / 2,
+ .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
+ };
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "mt9t112: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = icl->priv;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
+
+ ret = mt9t112_camera_probe(client);
+ if (ret) {
+ kfree(priv);
+ return ret;
+ }
+
+ /* Cannot fail: using the default supported pixel code */
+ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
+
+ return ret;
+}
+
+static int mt9t112_remove(struct i2c_client *client)
+{
+ struct mt9t112_priv *priv = to_mt9t112(client);
+
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id mt9t112_id[] = {
+ { "mt9t112", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9t112_id);
+
+static struct i2c_driver mt9t112_i2c_driver = {
+ .driver = {
+ .name = "mt9t112",
+ },
+ .probe = mt9t112_probe,
+ .remove = mt9t112_remove,
+ .id_table = mt9t112_id,
+};
+
+module_i2c_driver(mt9t112_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c
new file mode 100644
index 000000000000..13057b966ee9
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/mt9v022.c
@@ -0,0 +1,919 @@
+/*
+ * Driver for MT9V022 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/videodev2.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/*
+ * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
+ * The platform has to define struct i2c_board_info objects and link to them
+ * from struct soc_camera_link
+ */
+
+static char *sensor_type;
+module_param(sensor_type, charp, S_IRUGO);
+MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
+
+/* mt9v022 selected register addresses */
+#define MT9V022_CHIP_VERSION 0x00
+#define MT9V022_COLUMN_START 0x01
+#define MT9V022_ROW_START 0x02
+#define MT9V022_WINDOW_HEIGHT 0x03
+#define MT9V022_WINDOW_WIDTH 0x04
+#define MT9V022_HORIZONTAL_BLANKING 0x05
+#define MT9V022_VERTICAL_BLANKING 0x06
+#define MT9V022_CHIP_CONTROL 0x07
+#define MT9V022_SHUTTER_WIDTH1 0x08
+#define MT9V022_SHUTTER_WIDTH2 0x09
+#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a
+#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b
+#define MT9V022_RESET 0x0c
+#define MT9V022_READ_MODE 0x0d
+#define MT9V022_MONITOR_MODE 0x0e
+#define MT9V022_PIXEL_OPERATION_MODE 0x0f
+#define MT9V022_LED_OUT_CONTROL 0x1b
+#define MT9V022_ADC_MODE_CONTROL 0x1c
+#define MT9V022_ANALOG_GAIN 0x35
+#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
+#define MT9V022_PIXCLK_FV_LV 0x74
+#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
+#define MT9V022_AEC_AGC_ENABLE 0xAF
+#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD
+
+/* mt9v024 partial list register addresses changes with respect to mt9v022 */
+#define MT9V024_PIXCLK_FV_LV 0x72
+#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD
+
+/* Progressive scan, master, defaults */
+#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
+
+#define MT9V022_MAX_WIDTH 752
+#define MT9V022_MAX_HEIGHT 480
+#define MT9V022_MIN_WIDTH 48
+#define MT9V022_MIN_HEIGHT 32
+#define MT9V022_COLUMN_SKIP 1
+#define MT9V022_ROW_SKIP 4
+
+#define is_mt9v024(id) (id == 0x1324)
+
+/* MT9V022 has only one fixed colorspace per pixelcode */
+struct mt9v022_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct mt9v022_datafmt *mt9v022_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct mt9v022_datafmt mt9v022_colour_fmts[] = {
+ /*
+ * Order important: first natively supported,
+ * second supported with a GPIO extender
+ */
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
+};
+
+static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
+ /* Order important - see above */
+ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
+};
+
+/* only registers with different addresses on different mt9v02x sensors */
+struct mt9v02x_register {
+ u8 max_total_shutter_width;
+ u8 pixclk_fv_lv;
+};
+
+static const struct mt9v02x_register mt9v022_register = {
+ .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
+ .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV,
+};
+
+static const struct mt9v02x_register mt9v024_register = {
+ .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH,
+ .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV,
+};
+
+struct mt9v022 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/auto-exposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/auto-gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_rect rect; /* Sensor window */
+ const struct mt9v022_datafmt *fmt;
+ const struct mt9v022_datafmt *fmts;
+ const struct mt9v02x_register *reg;
+ int num_fmts;
+ int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
+ u16 chip_control;
+ unsigned short y_skip_top; /* Lines to skip at the top */
+};
+
+static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9v022, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int reg_write(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static int reg_set(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret | data);
+}
+
+static int reg_clear(struct i2c_client *client, const u8 reg,
+ const u16 data)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, ret & ~data);
+}
+
+static int mt9v022_init(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ int ret;
+
+ /*
+ * Almost the default mode: master, parallel, simultaneous, and an
+ * undocumented bit 0x200, which is present in table 7, but not in 8,
+ * plus snapshot mode to disable scan for now
+ */
+ mt9v022->chip_control |= 0x10;
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (!ret)
+ ret = reg_write(client, MT9V022_READ_MODE, 0x300);
+
+ /* All defaults */
+ if (!ret)
+ /* AEC, AGC on */
+ ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3);
+ if (!ret)
+ ret = reg_write(client, MT9V022_ANALOG_GAIN, 16);
+ if (!ret)
+ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480);
+ if (!ret)
+ ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480);
+ if (!ret)
+ /* default - auto */
+ ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
+ if (!ret)
+ ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
+ if (!ret)
+ return v4l2_ctrl_handler_setup(&mt9v022->hdl);
+
+ return ret;
+}
+
+static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (enable)
+ /* Switch to master "normal" mode */
+ mt9v022->chip_control &= ~0x10;
+ else
+ /* Switch to snapshot mode */
+ mt9v022->chip_control |= 0x10;
+
+ if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+
+ /* Bayer format - even size lengths */
+ if (mt9v022->fmts == mt9v022_colour_fmts) {
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT);
+
+ /* Like in example app. Contradicts the datasheet though */
+ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE);
+ if (ret >= 0) {
+ if (ret & 1) /* Autoexposure */
+ ret = reg_write(client, mt9v022->reg->max_total_shutter_width,
+ rect.height + mt9v022->y_skip_top + 43);
+ else
+ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
+ rect.height + mt9v022->y_skip_top + 43);
+ }
+ /* Setup frame format: defaults apart from width and height */
+ if (!ret)
+ ret = reg_write(client, MT9V022_COLUMN_START, rect.left);
+ if (!ret)
+ ret = reg_write(client, MT9V022_ROW_START, rect.top);
+ if (!ret)
+ /*
+ * Default 94, Phytec driver says:
+ * "width + horizontal blank >= 660"
+ */
+ ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
+ rect.width > 660 - 43 ? 43 :
+ 660 - rect.width);
+ if (!ret)
+ ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45);
+ if (!ret)
+ ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
+ if (!ret)
+ ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
+ rect.height + mt9v022->y_skip_top);
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height);
+
+ mt9v022->rect = rect;
+
+ return 0;
+}
+
+static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ a->c = mt9v022->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9V022_COLUMN_SKIP;
+ a->bounds.top = MT9V022_ROW_SKIP;
+ a->bounds.width = MT9V022_MAX_WIDTH;
+ a->bounds.height = MT9V022_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9v022_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mf->code = mt9v022->fmt->code;
+ mf->colorspace = mt9v022->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int mt9v022_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9v022->rect.left,
+ .top = mt9v022->rect.top,
+ .width = mf->width,
+ .height = mf->height,
+ },
+ };
+ int ret;
+
+ /*
+ * The caller provides a supported format, as verified per call to
+ * .try_mbus_fmt(), datawidth is from our supported format list
+ */
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_Y10_1X10:
+ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
+ return -EINVAL;
+ break;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* No support for scaling on this camera, just crop. */
+ ret = mt9v022_s_crop(sd, &a);
+ if (!ret) {
+ mf->width = mt9v022->rect.width;
+ mf->height = mt9v022->rect.height;
+ mt9v022->fmt = mt9v022_find_datafmt(mf->code,
+ mt9v022->fmts, mt9v022->num_fmts);
+ mf->colorspace = mt9v022->fmt->colorspace;
+ }
+
+ return ret;
+}
+
+static int mt9v022_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ const struct mt9v022_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
+
+ v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
+ MT9V022_MAX_WIDTH, align,
+ &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top,
+ MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0);
+
+ fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts,
+ mt9v022->num_fmts);
+ if (!fmt) {
+ fmt = mt9v022->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = mt9v022->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9v022_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 2;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xffff)
+ return -EIO;
+
+ return 0;
+}
+
+static int mt9v022_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int mt9v022_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range;
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ data = reg_read(client, MT9V022_ANALOG_GAIN);
+ if (data < 0)
+ return -EIO;
+
+ range = gain->maximum - gain->minimum;
+ gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
+ if (data < 0)
+ return -EIO;
+
+ range = exp->maximum - exp->minimum;
+ exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9v022 *mt9v022 = container_of(ctrl->handler,
+ struct mt9v022, hdl);
+ struct v4l2_subdev *sd = &mt9v022->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9V022_READ_MODE, 0x10);
+ else
+ data = reg_clear(client, MT9V022_READ_MODE, 0x10);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, MT9V022_READ_MODE, 0x20);
+ else
+ data = reg_clear(client, MT9V022_READ_MODE, 0x20);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val) {
+ if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+ } else {
+ struct v4l2_ctrl *gain = mt9v022->gain;
+ /* mt9v022 has minimum == default */
+ unsigned long range = gain->maximum - gain->minimum;
+ /* Valid values 16 to 64, 32 to 64 must be even. */
+ unsigned long gain_val = ((gain->val - gain->minimum) *
+ 48 + range / 2) / range + 16;
+
+ if (gain_val >= 32)
+ gain_val &= ~1;
+
+ /*
+ * The user wants to set gain manually, hope, she
+ * knows, what she's doing... Switch AGC off.
+ */
+ if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
+ return -EIO;
+
+ dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
+ reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
+ if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
+ return -EIO;
+ }
+ return 0;
+ case V4L2_CID_EXPOSURE_AUTO:
+ if (ctrl->val == V4L2_EXPOSURE_AUTO) {
+ data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ } else {
+ struct v4l2_ctrl *exp = mt9v022->exposure;
+ unsigned long range = exp->maximum - exp->minimum;
+ unsigned long shutter = ((exp->val - exp->minimum) *
+ 479 + range / 2) / range + 1;
+
+ /*
+ * The user wants to set shutter width manually, hope,
+ * she knows, what she's doing... Switch AEC off.
+ */
+ data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
+ if (data < 0)
+ return -EIO;
+ dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
+ reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
+ shutter);
+ if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
+ shutter) < 0)
+ return -EIO;
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int mt9v022_video_probe(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ s32 data;
+ int ret;
+ unsigned long flags;
+
+ ret = mt9v022_s_power(&mt9v022->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Read out the chip version register */
+ data = reg_read(client, MT9V022_CHIP_VERSION);
+
+ /* must be 0x1311, 0x1313 or 0x1324 */
+ if (data != 0x1311 && data != 0x1313 && data != 0x1324) {
+ ret = -ENODEV;
+ dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n",
+ data);
+ goto ei2c;
+ }
+
+ mt9v022->reg = is_mt9v024(data) ? &mt9v024_register :
+ &mt9v022_register;
+
+ /* Soft reset */
+ ret = reg_write(client, MT9V022_RESET, 1);
+ if (ret < 0)
+ goto ei2c;
+ /* 15 clock cycles */
+ udelay(200);
+ if (reg_read(client, MT9V022_RESET)) {
+ dev_err(&client->dev, "Resetting MT9V022 failed!\n");
+ if (ret > 0)
+ ret = -EIO;
+ goto ei2c;
+ }
+
+ /* Set monochrome or colour sensor type */
+ if (sensor_type && (!strcmp("colour", sensor_type) ||
+ !strcmp("color", sensor_type))) {
+ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
+ mt9v022->fmts = mt9v022_colour_fmts;
+ } else {
+ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
+ mt9v022->fmts = mt9v022_monochrome_fmts;
+ }
+
+ if (ret < 0)
+ goto ei2c;
+
+ mt9v022->num_fmts = 0;
+
+ /*
+ * This is a 10bit sensor, so by default we only allow 10bit.
+ * The platform may support different bus widths due to
+ * different routing of the data lines.
+ */
+ if (icl->query_bus_param)
+ flags = icl->query_bus_param(icl);
+ else
+ flags = SOCAM_DATAWIDTH_10;
+
+ if (flags & SOCAM_DATAWIDTH_10)
+ mt9v022->num_fmts++;
+ else
+ mt9v022->fmts++;
+
+ if (flags & SOCAM_DATAWIDTH_8)
+ mt9v022->num_fmts++;
+
+ mt9v022->fmt = &mt9v022->fmts[0];
+
+ dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
+ data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
+ "monochrome" : "colour");
+
+ ret = mt9v022_init(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
+
+ei2c:
+ mt9v022_s_power(&mt9v022->subdev, 0);
+ return ret;
+}
+
+static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ *lines = mt9v022->y_skip_top;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
+ .g_volatile_ctrl = mt9v022_g_volatile_ctrl,
+ .s_ctrl = mt9v022_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
+ .g_chip_ident = mt9v022_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9v022_g_register,
+ .s_register = mt9v022_s_register,
+#endif
+ .s_power = mt9v022_s_power,
+};
+
+static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ if (index >= mt9v022->num_fmts)
+ return -EINVAL;
+
+ *code = mt9v022->fmts[index].code;
+ return 0;
+}
+
+static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
+ int ret;
+ u16 pixclk = 0;
+
+ if (icl->set_bus_param) {
+ ret = icl->set_bus_param(icl, 1 << (bps - 1));
+ if (ret)
+ return ret;
+ } else if (bps != 10) {
+ /*
+ * Without board specific bus width settings we only support the
+ * sensors native bus width
+ */
+ return -EINVAL;
+ }
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ pixclk |= 0x10;
+
+ if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
+ pixclk |= 0x1;
+
+ if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
+ pixclk |= 0x2;
+
+ ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk);
+ if (ret < 0)
+ return ret;
+
+ if (!(flags & V4L2_MBUS_MASTER))
+ mt9v022->chip_control &= ~0x8;
+
+ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ pixclk, mt9v022->chip_control);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
+ .s_stream = mt9v022_s_stream,
+ .s_mbus_fmt = mt9v022_s_fmt,
+ .g_mbus_fmt = mt9v022_g_fmt,
+ .try_mbus_fmt = mt9v022_try_fmt,
+ .s_crop = mt9v022_s_crop,
+ .g_crop = mt9v022_g_crop,
+ .cropcap = mt9v022_cropcap,
+ .enum_mbus_fmt = mt9v022_enum_fmt,
+ .g_mbus_config = mt9v022_g_mbus_config,
+ .s_mbus_config = mt9v022_s_mbus_config,
+};
+
+static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
+ .g_skip_top_lines = mt9v022_g_skip_top_lines,
+};
+
+static struct v4l2_subdev_ops mt9v022_subdev_ops = {
+ .core = &mt9v022_subdev_core_ops,
+ .video = &mt9v022_subdev_video_ops,
+ .sensor = &mt9v022_subdev_sensor_ops,
+};
+
+static int mt9v022_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct mt9v022 *mt9v022;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "MT9V022 driver needs platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL);
+ if (!mt9v022)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+ v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ /*
+ * Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width
+ */
+ mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
+ &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
+ V4L2_EXPOSURE_AUTO);
+ mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 255, 1, 255);
+
+ mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
+ if (mt9v022->hdl.error) {
+ int err = mt9v022->hdl.error;
+
+ kfree(mt9v022);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+ v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
+
+ mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
+
+ /*
+ * MT9V022 _really_ corrupts the first read out line.
+ * TODO: verify on i.MX31
+ */
+ mt9v022->y_skip_top = 1;
+ mt9v022->rect.left = MT9V022_COLUMN_SKIP;
+ mt9v022->rect.top = MT9V022_ROW_SKIP;
+ mt9v022->rect.width = MT9V022_MAX_WIDTH;
+ mt9v022->rect.height = MT9V022_MAX_HEIGHT;
+
+ ret = mt9v022_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
+ kfree(mt9v022);
+ }
+
+ return ret;
+}
+
+static int mt9v022_remove(struct i2c_client *client)
+{
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&mt9v022->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&mt9v022->hdl);
+ kfree(mt9v022);
+
+ return 0;
+}
+static const struct i2c_device_id mt9v022_id[] = {
+ { "mt9v022", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v022_id);
+
+static struct i2c_driver mt9v022_i2c_driver = {
+ .driver = {
+ .name = "mt9v022",
+ },
+ .probe = mt9v022_probe,
+ .remove = mt9v022_remove,
+ .id_table = mt9v022_id,
+};
+
+module_i2c_driver(mt9v022_i2c_driver);
+
+MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c
new file mode 100644
index 000000000000..78ac5744cb5d
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov2640.c
@@ -0,0 +1,1122 @@
+/*
+ * ov2640 Camera Driver
+ *
+ * Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
+ *
+ * Based on ov772x, ov9640 drivers and previous non merged implementations.
+ *
+ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2006, OmniVision
+ *
+ * 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 <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+
+#define VAL_SET(x, mask, rshift, lshift) \
+ ((((x) >> rshift) & mask) << lshift)
+/*
+ * DSP registers
+ * register offset for BANK_SEL == BANK_SEL_DSP
+ */
+#define R_BYPASS 0x05 /* Bypass DSP */
+#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
+#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
+#define QS 0x44 /* Quantization Scale Factor */
+#define CTRLI 0x50
+#define CTRLI_LP_DP 0x80
+#define CTRLI_ROUND 0x40
+#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
+#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
+#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
+#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
+#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define XOFFL 0x53 /* OFFSET_X[7:0] */
+#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define YOFFL 0x54 /* OFFSET_Y[7:0] */
+#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
+#define VHYX 0x55 /* Offset and size completion */
+#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
+#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
+#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
+#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
+#define DPRP 0x56
+#define TEST 0x57 /* Horizontal size completion */
+#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
+#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
+#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
+#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
+#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
+#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
+#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
+#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
+#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
+#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
+#define CTRL2 0x86 /* DSP Module enable 2 */
+#define CTRL2_DCW_EN 0x20
+#define CTRL2_SDE_EN 0x10
+#define CTRL2_UV_ADJ_EN 0x08
+#define CTRL2_UV_AVG_EN 0x04
+#define CTRL2_CMX_EN 0x01
+#define CTRL3 0x87 /* DSP Module enable 3 */
+#define CTRL3_BPC_EN 0x80
+#define CTRL3_WPC_EN 0x40
+#define SIZEL 0x8C /* Image Size Completion */
+#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
+#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
+#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
+#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
+#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
+#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
+#define CTRL0 0xC2 /* DSP Module enable 0 */
+#define CTRL0_AEC_EN 0x80
+#define CTRL0_AEC_SEL 0x40
+#define CTRL0_STAT_SEL 0x20
+#define CTRL0_VFIRST 0x10
+#define CTRL0_YUV422 0x08
+#define CTRL0_YUV_EN 0x04
+#define CTRL0_RGB_EN 0x02
+#define CTRL0_RAW_EN 0x01
+#define CTRL1 0xC3 /* DSP Module enable 1 */
+#define CTRL1_CIP 0x80
+#define CTRL1_DMY 0x40
+#define CTRL1_RAW_GMA 0x20
+#define CTRL1_DG 0x10
+#define CTRL1_AWB 0x08
+#define CTRL1_AWB_GAIN 0x04
+#define CTRL1_LENC 0x02
+#define CTRL1_PRE 0x01
+#define R_DVP_SP 0xD3 /* DVP output speed control */
+#define R_DVP_SP_AUTO_MODE 0x80
+#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
+ * = sysclk (48)/(2*[6:0]) (RAW);*/
+#define IMAGE_MODE 0xDA /* Image Output Format Select */
+#define IMAGE_MODE_Y8_DVP_EN 0x40
+#define IMAGE_MODE_JPEG_EN 0x10
+#define IMAGE_MODE_YUV422 0x00
+#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
+#define IMAGE_MODE_RGB565 0x08
+#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output
+ * mode (0 for HREF is same as sensor) */
+#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP
+ * 1: Low byte first UYVY (C2[4] =0)
+ * VYUY (C2[4] =1)
+ * 0: High byte first YUYV (C2[4]=0)
+ * YVYU (C2[4] = 1) */
+#define RESET 0xE0 /* Reset */
+#define RESET_MICROC 0x40
+#define RESET_SCCB 0x20
+#define RESET_JPEG 0x10
+#define RESET_DVP 0x04
+#define RESET_IPU 0x02
+#define RESET_CIF 0x01
+#define REGED 0xED /* Register ED */
+#define REGED_CLK_OUT_DIS 0x10
+#define MS_SP 0xF0 /* SCCB Master Speed */
+#define SS_ID 0xF7 /* SCCB Slave ID */
+#define SS_CTRL 0xF8 /* SCCB Slave Control */
+#define SS_CTRL_ADD_AUTO_INC 0x20
+#define SS_CTRL_EN 0x08
+#define SS_CTRL_DELAY_CLK 0x04
+#define SS_CTRL_ACC_EN 0x02
+#define SS_CTRL_SEN_PASS_THR 0x01
+#define MC_BIST 0xF9 /* Microcontroller misc register */
+#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
+#define MC_BIST_BOOT_ROM_SEL 0x40
+#define MC_BIST_12KB_SEL 0x20
+#define MC_BIST_12KB_MASK 0x30
+#define MC_BIST_512KB_SEL 0x08
+#define MC_BIST_512KB_MASK 0x0C
+#define MC_BIST_BUSY_BIT_R 0x02
+#define MC_BIST_MC_RES_ONE_SH_W 0x02
+#define MC_BIST_LAUNCH 0x01
+#define BANK_SEL 0xFF /* Register Bank Select */
+#define BANK_SEL_DSP 0x00
+#define BANK_SEL_SENS 0x01
+
+/*
+ * Sensor registers
+ * register offset for BANK_SEL == BANK_SEL_SENS
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define COM1 0x03 /* Common control 1 */
+#define COM1_1_DUMMY_FR 0x40
+#define COM1_3_DUMMY_FR 0x80
+#define COM1_7_DUMMY_FR 0xC0
+#define COM1_VWIN_LSB_UXGA 0x0F
+#define COM1_VWIN_LSB_SVGA 0x0A
+#define COM1_VWIN_LSB_CIF 0x06
+#define REG04 0x04 /* Register 04 */
+#define REG04_DEF 0x20 /* Always set */
+#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
+#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
+#define REG04_VREF_EN 0x10
+#define REG04_HREF_EN 0x08
+#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
+#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
+#define COM2 0x09 /* Common control 2 */
+#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
+#define COM3_BAND_AUTO 0x02 /* Auto Banding */
+#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the
+ * snapshot sequence*/
+#define AEC 0x10 /* AEC[9:2] Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define CLKRC_EN 0x80
+#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
+#define COM7 0x12 /* Common control 7 */
+#define COM7_SRST 0x80 /* Initiates system reset. All registers are
+ * set to factory default values after which
+ * the chip resumes normal operation */
+#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
+#define COM7_RES_SVGA 0x40 /* SVGA */
+#define COM7_RES_CIF 0x20 /* CIF */
+#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
+#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
+#define COM8 0x13 /* Common control 8 */
+#define COM8_DEF 0xC0 /* Banding filter ON/OFF */
+#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
+#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
+#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
+#define COM9 0x14 /* Common control 9
+ * Automatic gain ceiling - maximum AGC value [7:5]*/
+#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
+#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
+#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
+#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
+#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
+#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
+#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
+#define COM10 0x15 /* Common control 10 */
+#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
+#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of
+ * PCLK (user can latch data at the next
+ * falling edge of PCLK).
+ * 0 otherwise. */
+#define COM10_HREF_INV 0x08 /* Invert HREF polarity:
+ * HREF negative for valid data*/
+#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */
+#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
+#define HEND 0x18 /* Horizontal Window end MSB 8 bit */
+#define VSTART 0x19 /* Vertical Window start MSB 8 bit */
+#define VEND 0x1A /* Vertical Window end MSB 8 bit */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VV 0x26 /* AGC/AEC Fast mode operating region */
+#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
+#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
+#define REG2A 0x2A /* Dummy pixel insert MSB */
+#define FRARL 0x2B /* Dummy pixel insert LSB */
+#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
+#define YAVG 0x2F /* Y/G Channel Average value */
+#define REG32 0x32 /* Common Control 32 */
+#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
+#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
+#define ARCOM2 0x34 /* Zoom: Horizontal start point */
+#define REG45 0x45 /* Register 45 */
+#define FLL 0x46 /* Frame Length Adjustment LSBs */
+#define FLH 0x47 /* Frame Length Adjustment MSBs */
+#define COM19 0x48 /* Zoom: Vertical start point */
+#define ZOOMS 0x49 /* Zoom: Vertical start point */
+#define COM22 0x4B /* Flash light control */
+#define COM25 0x4E /* For Banding operations */
+#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
+#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
+#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
+#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
+#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
+#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
+#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
+#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
+
+/*
+ * ID
+ */
+#define MANUFACTURER_ID 0x7FA2
+#define PID_OV2640 0x2642
+#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
+
+/*
+ * Struct
+ */
+struct regval_list {
+ u8 reg_num;
+ u8 value;
+};
+
+/* Supported resolutions */
+enum ov2640_width {
+ W_QCIF = 176,
+ W_QVGA = 320,
+ W_CIF = 352,
+ W_VGA = 640,
+ W_SVGA = 800,
+ W_XGA = 1024,
+ W_SXGA = 1280,
+ W_UXGA = 1600,
+};
+
+enum ov2640_height {
+ H_QCIF = 144,
+ H_QVGA = 240,
+ H_CIF = 288,
+ H_VGA = 480,
+ H_SVGA = 600,
+ H_XGA = 768,
+ H_SXGA = 1024,
+ H_UXGA = 1200,
+};
+
+struct ov2640_win_size {
+ char *name;
+ enum ov2640_width width;
+ enum ov2640_height height;
+ const struct regval_list *regs;
+};
+
+
+struct ov2640_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ enum v4l2_mbus_pixelcode cfmt_code;
+ const struct ov2640_win_size *win;
+ int model;
+};
+
+/*
+ * Registers settings
+ */
+
+#define ENDMARKER { 0xff, 0xff }
+
+static const struct regval_list ov2640_init_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { 0x2c, 0xff },
+ { 0x2e, 0xdf },
+ { BANK_SEL, BANK_SEL_SENS },
+ { 0x3c, 0x32 },
+ { CLKRC, CLKRC_DIV_SET(1) },
+ { COM2, COM2_OCAP_Nx_SET(3) },
+ { REG04, REG04_DEF | REG04_HREF_EN },
+ { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN },
+ { COM9, COM9_AGC_GAIN_8x | 0x08},
+ { 0x2c, 0x0c },
+ { 0x33, 0x78 },
+ { 0x3a, 0x33 },
+ { 0x3b, 0xfb },
+ { 0x3e, 0x00 },
+ { 0x43, 0x11 },
+ { 0x16, 0x10 },
+ { 0x39, 0x02 },
+ { 0x35, 0x88 },
+ { 0x22, 0x0a },
+ { 0x37, 0x40 },
+ { 0x23, 0x00 },
+ { ARCOM2, 0xa0 },
+ { 0x06, 0x02 },
+ { 0x06, 0x88 },
+ { 0x07, 0xc0 },
+ { 0x0d, 0xb7 },
+ { 0x0e, 0x01 },
+ { 0x4c, 0x00 },
+ { 0x4a, 0x81 },
+ { 0x21, 0x99 },
+ { AEW, 0x40 },
+ { AEB, 0x38 },
+ { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) },
+ { 0x5c, 0x00 },
+ { 0x63, 0x00 },
+ { FLL, 0x22 },
+ { COM3, 0x38 | COM3_BAND_AUTO },
+ { REG5D, 0x55 },
+ { REG5E, 0x7d },
+ { REG5F, 0x7d },
+ { REG60, 0x55 },
+ { HISTO_LOW, 0x70 },
+ { HISTO_HIGH, 0x80 },
+ { 0x7c, 0x05 },
+ { 0x20, 0x80 },
+ { 0x28, 0x30 },
+ { 0x6c, 0x00 },
+ { 0x6d, 0x80 },
+ { 0x6e, 0x00 },
+ { 0x70, 0x02 },
+ { 0x71, 0x94 },
+ { 0x73, 0xc1 },
+ { 0x3d, 0x34 },
+ { COM7, COM7_RES_UXGA | COM7_ZOOM_EN },
+ { 0x5a, 0x57 },
+ { BD50, 0xbb },
+ { BD60, 0x9c },
+ { BANK_SEL, BANK_SEL_DSP },
+ { 0xe5, 0x7f },
+ { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
+ { 0x41, 0x24 },
+ { RESET, RESET_JPEG | RESET_DVP },
+ { 0x76, 0xff },
+ { 0x33, 0xa0 },
+ { 0x42, 0x20 },
+ { 0x43, 0x18 },
+ { 0x4c, 0x00 },
+ { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
+ { 0x88, 0x3f },
+ { 0xd7, 0x03 },
+ { 0xd9, 0x10 },
+ { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
+ { 0xc8, 0x08 },
+ { 0xc9, 0x80 },
+ { BPADDR, 0x00 },
+ { BPDATA, 0x00 },
+ { BPADDR, 0x03 },
+ { BPDATA, 0x48 },
+ { BPDATA, 0x48 },
+ { BPADDR, 0x08 },
+ { BPDATA, 0x20 },
+ { BPDATA, 0x10 },
+ { BPDATA, 0x0e },
+ { 0x90, 0x00 },
+ { 0x91, 0x0e },
+ { 0x91, 0x1a },
+ { 0x91, 0x31 },
+ { 0x91, 0x5a },
+ { 0x91, 0x69 },
+ { 0x91, 0x75 },
+ { 0x91, 0x7e },
+ { 0x91, 0x88 },
+ { 0x91, 0x8f },
+ { 0x91, 0x96 },
+ { 0x91, 0xa3 },
+ { 0x91, 0xaf },
+ { 0x91, 0xc4 },
+ { 0x91, 0xd7 },
+ { 0x91, 0xe8 },
+ { 0x91, 0x20 },
+ { 0x92, 0x00 },
+ { 0x93, 0x06 },
+ { 0x93, 0xe3 },
+ { 0x93, 0x03 },
+ { 0x93, 0x03 },
+ { 0x93, 0x00 },
+ { 0x93, 0x02 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x93, 0x00 },
+ { 0x96, 0x00 },
+ { 0x97, 0x08 },
+ { 0x97, 0x19 },
+ { 0x97, 0x02 },
+ { 0x97, 0x0c },
+ { 0x97, 0x24 },
+ { 0x97, 0x30 },
+ { 0x97, 0x28 },
+ { 0x97, 0x26 },
+ { 0x97, 0x02 },
+ { 0x97, 0x98 },
+ { 0x97, 0x80 },
+ { 0x97, 0x00 },
+ { 0x97, 0x00 },
+ { 0xa4, 0x00 },
+ { 0xa8, 0x00 },
+ { 0xc5, 0x11 },
+ { 0xc6, 0x51 },
+ { 0xbf, 0x80 },
+ { 0xc7, 0x10 },
+ { 0xb6, 0x66 },
+ { 0xb8, 0xA5 },
+ { 0xb7, 0x64 },
+ { 0xb9, 0x7C },
+ { 0xb3, 0xaf },
+ { 0xb4, 0x97 },
+ { 0xb5, 0xFF },
+ { 0xb0, 0xC5 },
+ { 0xb1, 0x94 },
+ { 0xb2, 0x0f },
+ { 0xc4, 0x5c },
+ { 0xa6, 0x00 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x1b },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x19 },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0xa7, 0x20 },
+ { 0xa7, 0xd8 },
+ { 0xa7, 0x19 },
+ { 0xa7, 0x31 },
+ { 0xa7, 0x00 },
+ { 0xa7, 0x18 },
+ { 0x7f, 0x00 },
+ { 0xe5, 0x1f },
+ { 0xe1, 0x77 },
+ { 0xdd, 0x7f },
+ { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN },
+ ENDMARKER,
+};
+
+/*
+ * Register settings for window size
+ * The preamble, setup the internal DSP to input an UXGA (1600x1200) image.
+ * Then the different zooming configurations will setup the output image size.
+ */
+static const struct regval_list ov2640_size_change_preamble_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { RESET, RESET_DVP },
+ { HSIZE8, HSIZE8_SET(W_UXGA) },
+ { VSIZE8, VSIZE8_SET(H_UXGA) },
+ { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
+ CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
+ { HSIZE, HSIZE_SET(W_UXGA) },
+ { VSIZE, VSIZE_SET(H_UXGA) },
+ { XOFFL, XOFFL_SET(0) },
+ { YOFFL, YOFFL_SET(0) },
+ { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
+ VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
+ { TEST, TEST_HSIZE_SET(W_UXGA) },
+ ENDMARKER,
+};
+
+#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
+ { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \
+ CTRLI_H_DIV_SET(h_div)}, \
+ { ZMOW, ZMOW_OUTW_SET(x) }, \
+ { ZMOH, ZMOH_OUTH_SET(y) }, \
+ { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \
+ { R_DVP_SP, pclk_div }, \
+ { RESET, 0x00}
+
+static const struct regval_list ov2640_qcif_regs[] = {
+ PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_qvga_regs[] = {
+ PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_cif_regs[] = {
+ PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_vga_regs[] = {
+ PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_svga_regs[] = {
+ PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_xga_regs[] = {
+ PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
+ { CTRLI, 0x00},
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_sxga_regs[] = {
+ PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
+ { CTRLI, 0x00},
+ { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_uxga_regs[] = {
+ PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
+ { CTRLI, 0x00},
+ { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
+ ENDMARKER,
+};
+
+#define OV2640_SIZE(n, w, h, r) \
+ {.name = n, .width = w , .height = h, .regs = r }
+
+static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
+ OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
+ OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
+ OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
+ OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
+ OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
+ OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
+ OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
+ OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
+};
+
+/*
+ * Register settings for pixel formats
+ */
+static const struct regval_list ov2640_format_change_preamble_regs[] = {
+ { BANK_SEL, BANK_SEL_DSP },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_yuv422_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
+ { 0xD7, 0x01 },
+ { 0x33, 0xa0 },
+ { 0xe1, 0x67 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static const struct regval_list ov2640_rgb565_regs[] = {
+ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
+ { 0xd7, 0x03 },
+ { RESET, 0x00 },
+ { R_BYPASS, R_BYPASS_USE_DSP },
+ ENDMARKER,
+};
+
+static enum v4l2_mbus_pixelcode ov2640_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_RGB565_2X8_LE,
+};
+
+/*
+ * General functions
+ */
+static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov2640_priv,
+ subdev);
+}
+
+static int ov2640_write_array(struct i2c_client *client,
+ const struct regval_list *vals)
+{
+ int ret;
+
+ while ((vals->reg_num != 0xff) || (vals->value != 0xff)) {
+ ret = i2c_smbus_write_byte_data(client,
+ vals->reg_num, vals->value);
+ dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x",
+ vals->reg_num, vals->value);
+
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ return 0;
+}
+
+static int ov2640_mask_set(struct i2c_client *client,
+ u8 reg, u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, reg);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ dev_vdbg(&client->dev, "masks: 0x%02x, 0x%02x", reg, val);
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int ov2640_reset(struct i2c_client *client)
+{
+ int ret;
+ const struct regval_list reset_seq[] = {
+ {BANK_SEL, BANK_SEL_SENS},
+ {COM7, COM7_SRST},
+ ENDMARKER,
+ };
+
+ ret = ov2640_write_array(client, reset_seq);
+ if (ret)
+ goto err;
+
+ msleep(5);
+err:
+ dev_dbg(&client->dev, "%s: (ret %d)", __func__, ret);
+ return ret;
+}
+
+/*
+ * soc_camera_ops functions
+ */
+static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd =
+ &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
+ return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
+ }
+
+ return -EINVAL;
+}
+
+static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov2640_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 1;
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+
+ return 0;
+}
+
+static int ov2640_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov2640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+/* Select the nearest higher resolution for capture */
+static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height)
+{
+ int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1;
+
+ for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) {
+ if (ov2640_supported_win_sizes[i].width >= *width &&
+ ov2640_supported_win_sizes[i].height >= *height) {
+ *width = ov2640_supported_win_sizes[i].width;
+ *height = ov2640_supported_win_sizes[i].height;
+ return &ov2640_supported_win_sizes[i];
+ }
+ }
+
+ *width = ov2640_supported_win_sizes[default_size].width;
+ *height = ov2640_supported_win_sizes[default_size].height;
+ return &ov2640_supported_win_sizes[default_size];
+}
+
+static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
+ enum v4l2_mbus_pixelcode code)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+ const struct regval_list *selected_cfmt_regs;
+ int ret;
+
+ /* select win */
+ priv->win = ov2640_select_win(width, height);
+
+ /* select format */
+ priv->cfmt_code = 0;
+ switch (code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ dev_dbg(&client->dev, "%s: Selected cfmt RGB565", __func__);
+ selected_cfmt_regs = ov2640_rgb565_regs;
+ break;
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ dev_dbg(&client->dev, "%s: Selected cfmt YUV422", __func__);
+ selected_cfmt_regs = ov2640_yuv422_regs;
+ }
+
+ /* reset hardware */
+ ov2640_reset(client);
+
+ /* initialize the sensor with default data */
+ dev_dbg(&client->dev, "%s: Init default", __func__);
+ ret = ov2640_write_array(client, ov2640_init_regs);
+ if (ret < 0)
+ goto err;
+
+ /* select preamble */
+ dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name);
+ ret = ov2640_write_array(client, ov2640_size_change_preamble_regs);
+ if (ret < 0)
+ goto err;
+
+ /* set size win */
+ ret = ov2640_write_array(client, priv->win->regs);
+ if (ret < 0)
+ goto err;
+
+ /* cfmt preamble */
+ dev_dbg(&client->dev, "%s: Set cfmt", __func__);
+ ret = ov2640_write_array(client, ov2640_format_change_preamble_regs);
+ if (ret < 0)
+ goto err;
+
+ /* set cfmt */
+ ret = ov2640_write_array(client, selected_cfmt_regs);
+ if (ret < 0)
+ goto err;
+
+ priv->cfmt_code = code;
+ *width = priv->win->width;
+ *height = priv->win->height;
+
+ return 0;
+
+err:
+ dev_err(&client->dev, "%s: Error %d", __func__, ret);
+ ov2640_reset(client);
+ priv->win = NULL;
+
+ return ret;
+}
+
+static int ov2640_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ if (!priv->win) {
+ u32 width = W_SVGA, height = H_SVGA;
+ priv->win = ov2640_select_win(&width, &height);
+ priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8;
+ }
+
+ mf->width = priv->win->width;
+ mf->height = priv->win->height;
+ mf->code = priv->cfmt_code;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov2640_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code);
+
+ return ret;
+}
+
+static int ov2640_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct ov2640_win_size *win;
+
+ /*
+ * select suitable win
+ */
+ win = ov2640_select_win(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ return 0;
+}
+
+static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov2640_codes))
+ return -EINVAL;
+
+ *code = ov2640_codes[index];
+ return 0;
+}
+
+static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = W_UXGA;
+ a->c.height = H_UXGA;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = W_UXGA;
+ a->bounds.height = H_UXGA;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov2640_video_probe(struct i2c_client *client)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+ u8 pid, ver, midh, midl;
+ const char *devname;
+ int ret;
+
+ ret = ov2640_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
+ pid = i2c_smbus_read_byte_data(client, PID);
+ ver = i2c_smbus_read_byte_data(client, VER);
+ midh = i2c_smbus_read_byte_data(client, MIDH);
+ midl = i2c_smbus_read_byte_data(client, MIDL);
+
+ switch (VERSION(pid, ver)) {
+ case PID_OV2640:
+ devname = "ov2640";
+ priv->model = V4L2_IDENT_OV2640;
+ break;
+ default:
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname, pid, ver, midh, midl);
+
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov2640_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
+ .s_ctrl = ov2640_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
+ .g_chip_ident = ov2640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov2640_g_register,
+ .s_register = ov2640_s_register,
+#endif
+ .s_power = ov2640_s_power,
+};
+
+static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
+ .s_stream = ov2640_s_stream,
+ .g_mbus_fmt = ov2640_g_fmt,
+ .s_mbus_fmt = ov2640_s_fmt,
+ .try_mbus_fmt = ov2640_try_fmt,
+ .cropcap = ov2640_cropcap,
+ .g_crop = ov2640_g_crop,
+ .enum_mbus_fmt = ov2640_enum_fmt,
+ .g_mbus_config = ov2640_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov2640_subdev_ops = {
+ .core = &ov2640_subdev_core_ops,
+ .video = &ov2640_subdev_video_ops,
+};
+
+/*
+ * i2c_driver functions
+ */
+static int ov2640_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov2640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl) {
+ dev_err(&adapter->dev,
+ "OV2640: Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "OV2640: I2C-Adapter doesn't support SMBUS\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&adapter->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov2640_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ } else {
+ dev_info(&adapter->dev, "OV2640 Probed\n");
+ }
+
+ return ret;
+}
+
+static int ov2640_remove(struct i2c_client *client)
+{
+ struct ov2640_priv *priv = to_ov2640(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov2640_id[] = {
+ { "ov2640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov2640_id);
+
+static struct i2c_driver ov2640_i2c_driver = {
+ .driver = {
+ .name = "ov2640",
+ },
+ .probe = ov2640_probe,
+ .remove = ov2640_remove,
+ .id_table = ov2640_id,
+};
+
+module_i2c_driver(ov2640_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor");
+MODULE_AUTHOR("Alberto Panizzo");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c
new file mode 100644
index 000000000000..8577e0cfb7fe
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov5642.c
@@ -0,0 +1,1088 @@
+/*
+ * Driver for OV5642 CMOS Image Sensor from Omnivision
+ *
+ * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com>
+ *
+ * Based on Sony IMX074 Camera Driver
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * Based on Omnivision OV7670 Camera Driver
+ * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/* OV5642 registers */
+#define REG_CHIP_ID_HIGH 0x300a
+#define REG_CHIP_ID_LOW 0x300b
+
+#define REG_WINDOW_START_X_HIGH 0x3800
+#define REG_WINDOW_START_X_LOW 0x3801
+#define REG_WINDOW_START_Y_HIGH 0x3802
+#define REG_WINDOW_START_Y_LOW 0x3803
+#define REG_WINDOW_WIDTH_HIGH 0x3804
+#define REG_WINDOW_WIDTH_LOW 0x3805
+#define REG_WINDOW_HEIGHT_HIGH 0x3806
+#define REG_WINDOW_HEIGHT_LOW 0x3807
+#define REG_OUT_WIDTH_HIGH 0x3808
+#define REG_OUT_WIDTH_LOW 0x3809
+#define REG_OUT_HEIGHT_HIGH 0x380a
+#define REG_OUT_HEIGHT_LOW 0x380b
+#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c
+#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
+#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
+#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
+#define REG_OUTPUT_FORMAT 0x4300
+#define REG_ISP_CTRL_01 0x5001
+#define REG_AVG_WINDOW_END_X_HIGH 0x5682
+#define REG_AVG_WINDOW_END_X_LOW 0x5683
+#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
+#define REG_AVG_WINDOW_END_Y_LOW 0x5687
+
+/* active pixel array size */
+#define OV5642_SENSOR_SIZE_X 2592
+#define OV5642_SENSOR_SIZE_Y 1944
+
+/*
+ * About OV5642 resolution, cropping and binning:
+ * This sensor supports it all, at least in the feature description.
+ * Unfortunately, no combination of appropriate registers settings could make
+ * the chip work the intended way. As it works with predefined register lists,
+ * some undocumented registers are presumably changed there to achieve their
+ * goals.
+ * This driver currently only works for resolutions up to 720 lines with a
+ * 1:1 scale. Hopefully these restrictions will be removed in the future.
+ */
+#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
+#define OV5642_MAX_HEIGHT 720
+
+/* default sizes */
+#define OV5642_DEFAULT_WIDTH 1280
+#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
+
+/* minimum extra blanking */
+#define BLANKING_EXTRA_WIDTH 500
+#define BLANKING_EXTRA_HEIGHT 20
+
+/*
+ * the sensor's autoexposure is buggy when setting total_height low.
+ * It tries to expose longer than 1 frame period without taking care of it
+ * and this leads to weird output. So we set 1000 lines as minimum.
+ */
+#define BLANKING_MIN_HEIGHT 1000
+
+struct regval_list {
+ u16 reg_num;
+ u8 value;
+};
+
+static struct regval_list ov5642_default_regs_init[] = {
+ { 0x3103, 0x93 },
+ { 0x3008, 0x82 },
+ { 0x3017, 0x7f },
+ { 0x3018, 0xfc },
+ { 0x3810, 0xc2 },
+ { 0x3615, 0xf0 },
+ { 0x3000, 0x0 },
+ { 0x3001, 0x0 },
+ { 0x3002, 0x0 },
+ { 0x3003, 0x0 },
+ { 0x3004, 0xff },
+ { 0x3030, 0x2b },
+ { 0x3011, 0x8 },
+ { 0x3010, 0x10 },
+ { 0x3604, 0x60 },
+ { 0x3622, 0x60 },
+ { 0x3621, 0x9 },
+ { 0x3709, 0x0 },
+ { 0x4000, 0x21 },
+ { 0x401d, 0x22 },
+ { 0x3600, 0x54 },
+ { 0x3605, 0x4 },
+ { 0x3606, 0x3f },
+ { 0x3c01, 0x80 },
+ { 0x300d, 0x22 },
+ { 0x3623, 0x22 },
+ { 0x5000, 0x4f },
+ { 0x5020, 0x4 },
+ { 0x5181, 0x79 },
+ { 0x5182, 0x0 },
+ { 0x5185, 0x22 },
+ { 0x5197, 0x1 },
+ { 0x5500, 0xa },
+ { 0x5504, 0x0 },
+ { 0x5505, 0x7f },
+ { 0x5080, 0x8 },
+ { 0x300e, 0x18 },
+ { 0x4610, 0x0 },
+ { 0x471d, 0x5 },
+ { 0x4708, 0x6 },
+ { 0x370c, 0xa0 },
+ { 0x5687, 0x94 },
+ { 0x501f, 0x0 },
+ { 0x5000, 0x4f },
+ { 0x5001, 0xcf },
+ { 0x4300, 0x30 },
+ { 0x4300, 0x30 },
+ { 0x460b, 0x35 },
+ { 0x471d, 0x0 },
+ { 0x3002, 0xc },
+ { 0x3002, 0x0 },
+ { 0x4713, 0x3 },
+ { 0x471c, 0x50 },
+ { 0x4721, 0x2 },
+ { 0x4402, 0x90 },
+ { 0x460c, 0x22 },
+ { 0x3815, 0x44 },
+ { 0x3503, 0x7 },
+ { 0x3501, 0x73 },
+ { 0x3502, 0x80 },
+ { 0x350b, 0x0 },
+ { 0x3818, 0xc8 },
+ { 0x3824, 0x11 },
+ { 0x3a00, 0x78 },
+ { 0x3a1a, 0x4 },
+ { 0x3a13, 0x30 },
+ { 0x3a18, 0x0 },
+ { 0x3a19, 0x7c },
+ { 0x3a08, 0x12 },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0xf },
+ { 0x3a0b, 0xa0 },
+ { 0x350c, 0x7 },
+ { 0x350d, 0xd0 },
+ { 0x3a0d, 0x8 },
+ { 0x3a0e, 0x6 },
+ { 0x3500, 0x0 },
+ { 0x3501, 0x0 },
+ { 0x3502, 0x0 },
+ { 0x350a, 0x0 },
+ { 0x350b, 0x0 },
+ { 0x3503, 0x0 },
+ { 0x3a0f, 0x3c },
+ { 0x3a10, 0x32 },
+ { 0x3a1b, 0x3c },
+ { 0x3a1e, 0x32 },
+ { 0x3a11, 0x80 },
+ { 0x3a1f, 0x20 },
+ { 0x3030, 0x2b },
+ { 0x3a02, 0x0 },
+ { 0x3a03, 0x7d },
+ { 0x3a04, 0x0 },
+ { 0x3a14, 0x0 },
+ { 0x3a15, 0x7d },
+ { 0x3a16, 0x0 },
+ { 0x3a00, 0x78 },
+ { 0x3a08, 0x9 },
+ { 0x3a09, 0x60 },
+ { 0x3a0a, 0x7 },
+ { 0x3a0b, 0xd0 },
+ { 0x3a0d, 0x10 },
+ { 0x3a0e, 0xd },
+ { 0x4407, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x589b, 0x0 },
+ { 0x589a, 0xc0 },
+ { 0x401e, 0x20 },
+ { 0x4001, 0x42 },
+ { 0x401c, 0x6 },
+ { 0x3825, 0xac },
+ { 0x3827, 0xc },
+ { 0x528a, 0x1 },
+ { 0x528b, 0x4 },
+ { 0x528c, 0x8 },
+ { 0x528d, 0x10 },
+ { 0x528e, 0x20 },
+ { 0x528f, 0x28 },
+ { 0x5290, 0x30 },
+ { 0x5292, 0x0 },
+ { 0x5293, 0x1 },
+ { 0x5294, 0x0 },
+ { 0x5295, 0x4 },
+ { 0x5296, 0x0 },
+ { 0x5297, 0x8 },
+ { 0x5298, 0x0 },
+ { 0x5299, 0x10 },
+ { 0x529a, 0x0 },
+ { 0x529b, 0x20 },
+ { 0x529c, 0x0 },
+ { 0x529d, 0x28 },
+ { 0x529e, 0x0 },
+ { 0x529f, 0x30 },
+ { 0x5282, 0x0 },
+ { 0x5300, 0x0 },
+ { 0x5301, 0x20 },
+ { 0x5302, 0x0 },
+ { 0x5303, 0x7c },
+ { 0x530c, 0x0 },
+ { 0x530d, 0xc },
+ { 0x530e, 0x20 },
+ { 0x530f, 0x80 },
+ { 0x5310, 0x20 },
+ { 0x5311, 0x80 },
+ { 0x5308, 0x20 },
+ { 0x5309, 0x40 },
+ { 0x5304, 0x0 },
+ { 0x5305, 0x30 },
+ { 0x5306, 0x0 },
+ { 0x5307, 0x80 },
+ { 0x5314, 0x8 },
+ { 0x5315, 0x20 },
+ { 0x5319, 0x30 },
+ { 0x5316, 0x10 },
+ { 0x5317, 0x0 },
+ { 0x5318, 0x2 },
+ { 0x5380, 0x1 },
+ { 0x5381, 0x0 },
+ { 0x5382, 0x0 },
+ { 0x5383, 0x4e },
+ { 0x5384, 0x0 },
+ { 0x5385, 0xf },
+ { 0x5386, 0x0 },
+ { 0x5387, 0x0 },
+ { 0x5388, 0x1 },
+ { 0x5389, 0x15 },
+ { 0x538a, 0x0 },
+ { 0x538b, 0x31 },
+ { 0x538c, 0x0 },
+ { 0x538d, 0x0 },
+ { 0x538e, 0x0 },
+ { 0x538f, 0xf },
+ { 0x5390, 0x0 },
+ { 0x5391, 0xab },
+ { 0x5392, 0x0 },
+ { 0x5393, 0xa2 },
+ { 0x5394, 0x8 },
+ { 0x5480, 0x14 },
+ { 0x5481, 0x21 },
+ { 0x5482, 0x36 },
+ { 0x5483, 0x57 },
+ { 0x5484, 0x65 },
+ { 0x5485, 0x71 },
+ { 0x5486, 0x7d },
+ { 0x5487, 0x87 },
+ { 0x5488, 0x91 },
+ { 0x5489, 0x9a },
+ { 0x548a, 0xaa },
+ { 0x548b, 0xb8 },
+ { 0x548c, 0xcd },
+ { 0x548d, 0xdd },
+ { 0x548e, 0xea },
+ { 0x548f, 0x1d },
+ { 0x5490, 0x5 },
+ { 0x5491, 0x0 },
+ { 0x5492, 0x4 },
+ { 0x5493, 0x20 },
+ { 0x5494, 0x3 },
+ { 0x5495, 0x60 },
+ { 0x5496, 0x2 },
+ { 0x5497, 0xb8 },
+ { 0x5498, 0x2 },
+ { 0x5499, 0x86 },
+ { 0x549a, 0x2 },
+ { 0x549b, 0x5b },
+ { 0x549c, 0x2 },
+ { 0x549d, 0x3b },
+ { 0x549e, 0x2 },
+ { 0x549f, 0x1c },
+ { 0x54a0, 0x2 },
+ { 0x54a1, 0x4 },
+ { 0x54a2, 0x1 },
+ { 0x54a3, 0xed },
+ { 0x54a4, 0x1 },
+ { 0x54a5, 0xc5 },
+ { 0x54a6, 0x1 },
+ { 0x54a7, 0xa5 },
+ { 0x54a8, 0x1 },
+ { 0x54a9, 0x6c },
+ { 0x54aa, 0x1 },
+ { 0x54ab, 0x41 },
+ { 0x54ac, 0x1 },
+ { 0x54ad, 0x20 },
+ { 0x54ae, 0x0 },
+ { 0x54af, 0x16 },
+ { 0x54b0, 0x1 },
+ { 0x54b1, 0x20 },
+ { 0x54b2, 0x0 },
+ { 0x54b3, 0x10 },
+ { 0x54b4, 0x0 },
+ { 0x54b5, 0xf0 },
+ { 0x54b6, 0x0 },
+ { 0x54b7, 0xdf },
+ { 0x5402, 0x3f },
+ { 0x5403, 0x0 },
+ { 0x3406, 0x0 },
+ { 0x5180, 0xff },
+ { 0x5181, 0x52 },
+ { 0x5182, 0x11 },
+ { 0x5183, 0x14 },
+ { 0x5184, 0x25 },
+ { 0x5185, 0x24 },
+ { 0x5186, 0x6 },
+ { 0x5187, 0x8 },
+ { 0x5188, 0x8 },
+ { 0x5189, 0x7c },
+ { 0x518a, 0x60 },
+ { 0x518b, 0xb2 },
+ { 0x518c, 0xb2 },
+ { 0x518d, 0x44 },
+ { 0x518e, 0x3d },
+ { 0x518f, 0x58 },
+ { 0x5190, 0x46 },
+ { 0x5191, 0xf8 },
+ { 0x5192, 0x4 },
+ { 0x5193, 0x70 },
+ { 0x5194, 0xf0 },
+ { 0x5195, 0xf0 },
+ { 0x5196, 0x3 },
+ { 0x5197, 0x1 },
+ { 0x5198, 0x4 },
+ { 0x5199, 0x12 },
+ { 0x519a, 0x4 },
+ { 0x519b, 0x0 },
+ { 0x519c, 0x6 },
+ { 0x519d, 0x82 },
+ { 0x519e, 0x0 },
+ { 0x5025, 0x80 },
+ { 0x3a0f, 0x38 },
+ { 0x3a10, 0x30 },
+ { 0x3a1b, 0x3a },
+ { 0x3a1e, 0x2e },
+ { 0x3a11, 0x60 },
+ { 0x3a1f, 0x10 },
+ { 0x5688, 0xa6 },
+ { 0x5689, 0x6a },
+ { 0x568a, 0xea },
+ { 0x568b, 0xae },
+ { 0x568c, 0xa6 },
+ { 0x568d, 0x6a },
+ { 0x568e, 0x62 },
+ { 0x568f, 0x26 },
+ { 0x5583, 0x40 },
+ { 0x5584, 0x40 },
+ { 0x5580, 0x2 },
+ { 0x5000, 0xcf },
+ { 0x5800, 0x27 },
+ { 0x5801, 0x19 },
+ { 0x5802, 0x12 },
+ { 0x5803, 0xf },
+ { 0x5804, 0x10 },
+ { 0x5805, 0x15 },
+ { 0x5806, 0x1e },
+ { 0x5807, 0x2f },
+ { 0x5808, 0x15 },
+ { 0x5809, 0xd },
+ { 0x580a, 0xa },
+ { 0x580b, 0x9 },
+ { 0x580c, 0xa },
+ { 0x580d, 0xc },
+ { 0x580e, 0x12 },
+ { 0x580f, 0x19 },
+ { 0x5810, 0xb },
+ { 0x5811, 0x7 },
+ { 0x5812, 0x4 },
+ { 0x5813, 0x3 },
+ { 0x5814, 0x3 },
+ { 0x5815, 0x6 },
+ { 0x5816, 0xa },
+ { 0x5817, 0xf },
+ { 0x5818, 0xa },
+ { 0x5819, 0x5 },
+ { 0x581a, 0x1 },
+ { 0x581b, 0x0 },
+ { 0x581c, 0x0 },
+ { 0x581d, 0x3 },
+ { 0x581e, 0x8 },
+ { 0x581f, 0xc },
+ { 0x5820, 0xa },
+ { 0x5821, 0x5 },
+ { 0x5822, 0x1 },
+ { 0x5823, 0x0 },
+ { 0x5824, 0x0 },
+ { 0x5825, 0x3 },
+ { 0x5826, 0x8 },
+ { 0x5827, 0xc },
+ { 0x5828, 0xe },
+ { 0x5829, 0x8 },
+ { 0x582a, 0x6 },
+ { 0x582b, 0x4 },
+ { 0x582c, 0x5 },
+ { 0x582d, 0x7 },
+ { 0x582e, 0xb },
+ { 0x582f, 0x12 },
+ { 0x5830, 0x18 },
+ { 0x5831, 0x10 },
+ { 0x5832, 0xc },
+ { 0x5833, 0xa },
+ { 0x5834, 0xb },
+ { 0x5835, 0xe },
+ { 0x5836, 0x15 },
+ { 0x5837, 0x19 },
+ { 0x5838, 0x32 },
+ { 0x5839, 0x1f },
+ { 0x583a, 0x18 },
+ { 0x583b, 0x16 },
+ { 0x583c, 0x17 },
+ { 0x583d, 0x1e },
+ { 0x583e, 0x26 },
+ { 0x583f, 0x53 },
+ { 0x5840, 0x10 },
+ { 0x5841, 0xf },
+ { 0x5842, 0xd },
+ { 0x5843, 0xc },
+ { 0x5844, 0xe },
+ { 0x5845, 0x9 },
+ { 0x5846, 0x11 },
+ { 0x5847, 0x10 },
+ { 0x5848, 0x10 },
+ { 0x5849, 0x10 },
+ { 0x584a, 0x10 },
+ { 0x584b, 0xe },
+ { 0x584c, 0x10 },
+ { 0x584d, 0x10 },
+ { 0x584e, 0x11 },
+ { 0x584f, 0x10 },
+ { 0x5850, 0xf },
+ { 0x5851, 0xc },
+ { 0x5852, 0xf },
+ { 0x5853, 0x10 },
+ { 0x5854, 0x10 },
+ { 0x5855, 0xf },
+ { 0x5856, 0xe },
+ { 0x5857, 0xb },
+ { 0x5858, 0x10 },
+ { 0x5859, 0xd },
+ { 0x585a, 0xd },
+ { 0x585b, 0xc },
+ { 0x585c, 0xc },
+ { 0x585d, 0xc },
+ { 0x585e, 0xb },
+ { 0x585f, 0xc },
+ { 0x5860, 0xc },
+ { 0x5861, 0xc },
+ { 0x5862, 0xd },
+ { 0x5863, 0x8 },
+ { 0x5864, 0x11 },
+ { 0x5865, 0x18 },
+ { 0x5866, 0x18 },
+ { 0x5867, 0x19 },
+ { 0x5868, 0x17 },
+ { 0x5869, 0x19 },
+ { 0x586a, 0x16 },
+ { 0x586b, 0x13 },
+ { 0x586c, 0x13 },
+ { 0x586d, 0x12 },
+ { 0x586e, 0x13 },
+ { 0x586f, 0x16 },
+ { 0x5870, 0x14 },
+ { 0x5871, 0x12 },
+ { 0x5872, 0x10 },
+ { 0x5873, 0x11 },
+ { 0x5874, 0x11 },
+ { 0x5875, 0x16 },
+ { 0x5876, 0x14 },
+ { 0x5877, 0x11 },
+ { 0x5878, 0x10 },
+ { 0x5879, 0xf },
+ { 0x587a, 0x10 },
+ { 0x587b, 0x14 },
+ { 0x587c, 0x13 },
+ { 0x587d, 0x12 },
+ { 0x587e, 0x11 },
+ { 0x587f, 0x11 },
+ { 0x5880, 0x12 },
+ { 0x5881, 0x15 },
+ { 0x5882, 0x14 },
+ { 0x5883, 0x15 },
+ { 0x5884, 0x15 },
+ { 0x5885, 0x15 },
+ { 0x5886, 0x13 },
+ { 0x5887, 0x17 },
+ { 0x3710, 0x10 },
+ { 0x3632, 0x51 },
+ { 0x3702, 0x10 },
+ { 0x3703, 0xb2 },
+ { 0x3704, 0x18 },
+ { 0x370b, 0x40 },
+ { 0x370d, 0x3 },
+ { 0x3631, 0x1 },
+ { 0x3632, 0x52 },
+ { 0x3606, 0x24 },
+ { 0x3620, 0x96 },
+ { 0x5785, 0x7 },
+ { 0x3a13, 0x30 },
+ { 0x3600, 0x52 },
+ { 0x3604, 0x48 },
+ { 0x3606, 0x1b },
+ { 0x370d, 0xb },
+ { 0x370f, 0xc0 },
+ { 0x3709, 0x1 },
+ { 0x3823, 0x0 },
+ { 0x5007, 0x0 },
+ { 0x5009, 0x0 },
+ { 0x5011, 0x0 },
+ { 0x5013, 0x0 },
+ { 0x519e, 0x0 },
+ { 0x5086, 0x0 },
+ { 0x5087, 0x0 },
+ { 0x5088, 0x0 },
+ { 0x5089, 0x0 },
+ { 0x302b, 0x0 },
+ { 0x3503, 0x7 },
+ { 0x3011, 0x8 },
+ { 0x350c, 0x2 },
+ { 0x350d, 0xe4 },
+ { 0x3621, 0xc9 },
+ { 0x370a, 0x81 },
+ { 0xffff, 0xff },
+};
+
+static struct regval_list ov5642_default_regs_finalise[] = {
+ { 0x3810, 0xc2 },
+ { 0x3818, 0xc9 },
+ { 0x381c, 0x10 },
+ { 0x381d, 0xa0 },
+ { 0x381e, 0x5 },
+ { 0x381f, 0xb0 },
+ { 0x3820, 0x0 },
+ { 0x3821, 0x0 },
+ { 0x3824, 0x11 },
+ { 0x3a08, 0x1b },
+ { 0x3a09, 0xc0 },
+ { 0x3a0a, 0x17 },
+ { 0x3a0b, 0x20 },
+ { 0x3a0d, 0x2 },
+ { 0x3a0e, 0x1 },
+ { 0x401c, 0x4 },
+ { 0x5682, 0x5 },
+ { 0x5683, 0x0 },
+ { 0x5686, 0x2 },
+ { 0x5687, 0xcc },
+ { 0x5001, 0x4f },
+ { 0x589b, 0x6 },
+ { 0x589a, 0xc5 },
+ { 0x3503, 0x0 },
+ { 0x460c, 0x20 },
+ { 0x460b, 0x37 },
+ { 0x471c, 0xd0 },
+ { 0x471d, 0x5 },
+ { 0x3815, 0x1 },
+ { 0x3818, 0xc1 },
+ { 0x501f, 0x0 },
+ { 0x5002, 0xe0 },
+ { 0x4300, 0x32 }, /* UYVY */
+ { 0x3002, 0x1c },
+ { 0x4800, 0x14 },
+ { 0x4801, 0xf },
+ { 0x3007, 0x3b },
+ { 0x300e, 0x4 },
+ { 0x4803, 0x50 },
+ { 0x3815, 0x1 },
+ { 0x4713, 0x2 },
+ { 0x4842, 0x1 },
+ { 0x300f, 0xe },
+ { 0x3003, 0x3 },
+ { 0x3003, 0x1 },
+ { 0xffff, 0xff },
+};
+
+struct ov5642_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+struct ov5642 {
+ struct v4l2_subdev subdev;
+ const struct ov5642_datafmt *fmt;
+ struct v4l2_rect crop_rect;
+
+ /* blanking information */
+ int total_width;
+ int total_height;
+};
+
+static const struct ov5642_datafmt ov5642_colour_fmts[] = {
+ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
+};
+
+static struct ov5642 *to_ov5642(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
+}
+
+/* Find a data format by a pixel code in an array */
+static const struct ov5642_datafmt
+ *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++)
+ if (ov5642_colour_fmts[i].code == code)
+ return ov5642_colour_fmts + i;
+
+ return NULL;
+}
+
+static int reg_read(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int ret;
+ /* We have 16-bit i2c addresses - care for endianess */
+ unsigned char data[2] = { reg >> 8, reg & 0xff };
+
+ ret = i2c_master_send(client, data, 2);
+ if (ret < 2) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ ret = i2c_master_recv(client, val, 1);
+ if (ret < 1) {
+ dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+ return 0;
+}
+
+static int reg_write(struct i2c_client *client, u16 reg, u8 val)
+{
+ int ret;
+ unsigned char data[3] = { reg >> 8, reg & 0xff, val };
+
+ ret = i2c_master_send(client, data, 3);
+ if (ret < 3) {
+ dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
+ __func__, reg);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * convenience function to write 16 bit register values that are split up
+ * into two consecutive high and low parts
+ */
+static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
+{
+ int ret;
+
+ ret = reg_write(client, reg, val16 >> 8);
+ if (ret)
+ return ret;
+ return reg_write(client, reg + 1, val16 & 0x00ff);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov5642_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 = 1;
+
+ ret = reg_read(client, reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov5642_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 reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov5642_write_array(struct i2c_client *client,
+ struct regval_list *vals)
+{
+ while (vals->reg_num != 0xffff || vals->value != 0xff) {
+ int ret = reg_write(client, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+ }
+ dev_dbg(&client->dev, "Register list loaded\n");
+ return 0;
+}
+
+static int ov5642_set_resolution(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ int width = priv->crop_rect.width;
+ int height = priv->crop_rect.height;
+ int total_width = priv->total_width;
+ int total_height = priv->total_height;
+ int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
+ int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
+ int ret;
+
+ /*
+ * This should set the starting point for cropping.
+ * Doesn't work so far.
+ */
+ ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
+ if (!ret) {
+ priv->crop_rect.left = start_x;
+ priv->crop_rect.top = start_y;
+ }
+
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
+ if (ret)
+ return ret;
+ priv->crop_rect.width = width;
+ priv->crop_rect.height = height;
+
+ /* Set the output window size. Only 1:1 scale is supported so far. */
+ ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
+
+ /* Total width = output size + blanking */
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
+ if (!ret)
+ ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
+
+ /* Sets the window for AWB calculations */
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
+ if (!ret)
+ ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
+
+ return ret;
+}
+
+static int ov5642_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
+
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+
+ if (!fmt) {
+ mf->code = ov5642_colour_fmts[0].code;
+ mf->colorspace = ov5642_colour_fmts[0].colorspace;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ /* MIPI CSI could have changed the format, double-check */
+ if (!ov5642_find_datafmt(mf->code))
+ return -EINVAL;
+
+ ov5642_try_fmt(sd, mf);
+ priv->fmt = ov5642_find_datafmt(mf->code);
+
+ return 0;
+}
+
+static int ov5642_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+
+ const struct ov5642_datafmt *fmt = priv->fmt;
+
+ mf->code = fmt->code;
+ mf->colorspace = fmt->colorspace;
+ mf->width = priv->crop_rect.width;
+ mf->height = priv->crop_rect.height;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov5642_colour_fmts))
+ return -EINVAL;
+
+ *code = ov5642_colour_fmts[index].code;
+ return 0;
+}
+
+static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_OV5642;
+ id->revision = 0;
+
+ return 0;
+}
+
+static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+
+ v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1,
+ &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0);
+
+ priv->crop_rect.width = rect.width;
+ priv->crop_rect.height = rect.height;
+ priv->total_width = rect.width + BLANKING_EXTRA_WIDTH;
+ priv->total_height = max_t(int, rect.height +
+ BLANKING_EXTRA_HEIGHT,
+ BLANKING_MIN_HEIGHT);
+ priv->crop_rect.width = rect.width;
+ priv->crop_rect.height = rect.height;
+
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov5642 *priv = to_ov5642(client);
+ struct v4l2_rect *rect = &a->c;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ *rect = priv->crop_rect;
+
+ return 0;
+}
+
+static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = OV5642_MAX_WIDTH;
+ a->bounds.height = OV5642_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ cfg->type = V4L2_MBUS_CSI2;
+ cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
+ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+ return 0;
+}
+
+static int ov5642_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!on)
+ return soc_camera_power_off(&client->dev, icl);
+
+ ret = soc_camera_power_on(&client->dev, icl);
+ if (ret < 0)
+ return ret;
+
+ ret = ov5642_write_array(client, ov5642_default_regs_init);
+ if (!ret)
+ ret = ov5642_set_resolution(sd);
+ if (!ret)
+ ret = ov5642_write_array(client, ov5642_default_regs_finalise);
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
+ .s_mbus_fmt = ov5642_s_fmt,
+ .g_mbus_fmt = ov5642_g_fmt,
+ .try_mbus_fmt = ov5642_try_fmt,
+ .enum_mbus_fmt = ov5642_enum_fmt,
+ .s_crop = ov5642_s_crop,
+ .g_crop = ov5642_g_crop,
+ .cropcap = ov5642_cropcap,
+ .g_mbus_config = ov5642_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
+ .s_power = ov5642_s_power,
+ .g_chip_ident = ov5642_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov5642_get_register,
+ .s_register = ov5642_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov5642_subdev_ops = {
+ .core = &ov5642_subdev_core_ops,
+ .video = &ov5642_subdev_video_ops,
+};
+
+static int ov5642_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ int ret;
+ u8 id_high, id_low;
+ u16 id;
+
+ ret = ov5642_s_power(subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Read sensor Model ID */
+ ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high);
+ if (ret < 0)
+ goto done;
+
+ id = id_high << 8;
+
+ ret = reg_read(client, REG_CHIP_ID_LOW, &id_low);
+ if (ret < 0)
+ goto done;
+
+ id |= id_low;
+
+ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
+
+ if (id != 0x5642) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ ov5642_s_power(subdev, 0);
+ return ret;
+}
+
+static int ov5642_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov5642 *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "OV5642: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
+
+ priv->fmt = &ov5642_colour_fmts[0];
+
+ priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
+ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
+ priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
+ priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
+ priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
+ priv->total_height = BLANKING_MIN_HEIGHT;
+
+ ret = ov5642_video_probe(client);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ kfree(priv);
+ return ret;
+}
+
+static int ov5642_remove(struct i2c_client *client)
+{
+ struct ov5642 *priv = to_ov5642(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ kfree(priv);
+
+ return 0;
+}
+
+static const struct i2c_device_id ov5642_id[] = {
+ { "ov5642", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov5642_id);
+
+static struct i2c_driver ov5642_i2c_driver = {
+ .driver = {
+ .name = "ov5642",
+ },
+ .probe = ov5642_probe,
+ .remove = ov5642_remove,
+ .id_table = ov5642_id,
+};
+
+module_i2c_driver(ov5642_i2c_driver);
+
+MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
+MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c
new file mode 100644
index 000000000000..e87feb0881e3
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov6650.c
@@ -0,0 +1,1069 @@
+/*
+ * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
+ *
+ * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on OmniVision OV96xx Camera Driver
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/video/omap/sensor_ov6650.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/module.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+/* Register definitions */
+#define REG_GAIN 0x00 /* range 00 - 3F */
+#define REG_BLUE 0x01
+#define REG_RED 0x02
+#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */
+#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */
+
+#define REG_BRT 0x06
+
+#define REG_PIDH 0x0a
+#define REG_PIDL 0x0b
+
+#define REG_AECH 0x10
+#define REG_CLKRC 0x11 /* Data Format and Internal Clock */
+ /* [7:6] Input system clock (MHz)*/
+ /* 00=8, 01=12, 10=16, 11=24 */
+ /* [5:0]: Internal Clock Pre-Scaler */
+#define REG_COMA 0x12 /* [7] Reset */
+#define REG_COMB 0x13
+#define REG_COMC 0x14
+#define REG_COMD 0x15
+#define REG_COML 0x16
+#define REG_HSTRT 0x17
+#define REG_HSTOP 0x18
+#define REG_VSTRT 0x19
+#define REG_VSTOP 0x1a
+#define REG_PSHFT 0x1b
+#define REG_MIDH 0x1c
+#define REG_MIDL 0x1d
+#define REG_HSYNS 0x1e
+#define REG_HSYNE 0x1f
+#define REG_COME 0x20
+#define REG_YOFF 0x21
+#define REG_UOFF 0x22
+#define REG_VOFF 0x23
+#define REG_AEW 0x24
+#define REG_AEB 0x25
+#define REG_COMF 0x26
+#define REG_COMG 0x27
+#define REG_COMH 0x28
+#define REG_COMI 0x29
+
+#define REG_FRARL 0x2b
+#define REG_COMJ 0x2c
+#define REG_COMK 0x2d
+#define REG_AVGY 0x2e
+#define REG_REF0 0x2f
+#define REG_REF1 0x30
+#define REG_REF2 0x31
+#define REG_FRAJH 0x32
+#define REG_FRAJL 0x33
+#define REG_FACT 0x34
+#define REG_L1AEC 0x35
+#define REG_AVGU 0x36
+#define REG_AVGV 0x37
+
+#define REG_SPCB 0x60
+#define REG_SPCC 0x61
+#define REG_GAM1 0x62
+#define REG_GAM2 0x63
+#define REG_GAM3 0x64
+#define REG_SPCD 0x65
+
+#define REG_SPCE 0x68
+#define REG_ADCL 0x69
+
+#define REG_RMCO 0x6c
+#define REG_GMCO 0x6d
+#define REG_BMCO 0x6e
+
+
+/* Register bits, values, etc. */
+#define OV6650_PIDH 0x66 /* high byte of product ID number */
+#define OV6650_PIDL 0x50 /* low byte of product ID number */
+#define OV6650_MIDH 0x7F /* high byte of mfg ID */
+#define OV6650_MIDL 0xA2 /* low byte of mfg ID */
+
+#define DEF_GAIN 0x00
+#define DEF_BLUE 0x80
+#define DEF_RED 0x80
+
+#define SAT_SHIFT 4
+#define SAT_MASK (0xf << SAT_SHIFT)
+#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK)
+
+#define HUE_EN BIT(5)
+#define HUE_MASK 0x1f
+#define DEF_HUE 0x10
+#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK))
+
+#define DEF_AECH 0x4D
+
+#define CLKRC_6MHz 0x00
+#define CLKRC_12MHz 0x40
+#define CLKRC_16MHz 0x80
+#define CLKRC_24MHz 0xc0
+#define CLKRC_DIV_MASK 0x3f
+#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1)
+
+#define COMA_RESET BIT(7)
+#define COMA_QCIF BIT(5)
+#define COMA_RAW_RGB BIT(4)
+#define COMA_RGB BIT(3)
+#define COMA_BW BIT(2)
+#define COMA_WORD_SWAP BIT(1)
+#define COMA_BYTE_SWAP BIT(0)
+#define DEF_COMA 0x00
+
+#define COMB_FLIP_V BIT(7)
+#define COMB_FLIP_H BIT(5)
+#define COMB_BAND_FILTER BIT(4)
+#define COMB_AWB BIT(2)
+#define COMB_AGC BIT(1)
+#define COMB_AEC BIT(0)
+#define DEF_COMB 0x5f
+
+#define COML_ONE_CHANNEL BIT(7)
+
+#define DEF_HSTRT 0x24
+#define DEF_HSTOP 0xd4
+#define DEF_VSTRT 0x04
+#define DEF_VSTOP 0x94
+
+#define COMF_HREF_LOW BIT(4)
+
+#define COMJ_PCLK_RISING BIT(4)
+#define COMJ_VSYNC_HIGH BIT(0)
+
+/* supported resolutions */
+#define W_QCIF (DEF_HSTOP - DEF_HSTRT)
+#define W_CIF (W_QCIF << 1)
+#define H_QCIF (DEF_VSTOP - DEF_VSTRT)
+#define H_CIF (H_QCIF << 1)
+
+#define FRAME_RATE_MAX 30
+
+
+struct ov6650_reg {
+ u8 reg;
+ u8 val;
+};
+
+struct ov6650 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* exposure/autoexposure cluster */
+ struct v4l2_ctrl *autoexposure;
+ struct v4l2_ctrl *exposure;
+ };
+ struct {
+ /* gain/autogain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct {
+ /* blue/red/autowhitebalance cluster */
+ struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
+ bool half_scale; /* scale down output by 2 */
+ struct v4l2_rect rect; /* sensor cropping window */
+ unsigned long pclk_limit; /* from host */
+ unsigned long pclk_max; /* from resolution and format */
+ struct v4l2_fract tpf; /* as requested with s_parm */
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+
+static enum v4l2_mbus_pixelcode ov6650_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_YVYU8_2X8,
+ V4L2_MBUS_FMT_VYUY8_2X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_Y8_1X8,
+};
+
+/* read a register */
+static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+ u8 data = reg;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ *val = data;
+ return 0;
+
+err:
+ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+ return ret;
+}
+
+/* write a register */
+static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+ unsigned char data[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ udelay(100);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+ return ret;
+ }
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
+{
+ u8 val;
+ int ret;
+
+ ret = ov6650_reg_read(client, reg, &val);
+ if (ret) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register 0x%02x failed!\n",
+ reg);
+ return ret;
+ }
+
+ val &= ~mask;
+ val |= set;
+
+ ret = ov6650_reg_write(client, reg, val);
+ if (ret)
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register 0x%02x failed!\n",
+ reg);
+
+ return ret;
+}
+
+static struct ov6650 *to_ov6650(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
+}
+
+/* Start/Stop streaming from the device */
+static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+/* Get status of additional camera capabilities */
+static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ uint8_t reg, reg2;
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov6650_reg_read(client, REG_GAIN, &reg);
+ if (!ret)
+ priv->gain->val = reg;
+ return ret;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov6650_reg_read(client, REG_BLUE, &reg);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_RED, &reg2);
+ if (!ret) {
+ priv->blue->val = reg;
+ priv->red->val = reg2;
+ }
+ return ret;
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = ov6650_reg_read(client, REG_AECH, &reg);
+ if (!ret)
+ priv->exposure->val = reg;
+ return ret;
+ }
+ return -EINVAL;
+}
+
+/* Set status of additional camera capabilities */
+static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ ret = ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_AGC : 0, COMB_AGC);
+ if (!ret && !ctrl->val)
+ ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
+ return ret;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ ret = ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_AWB : 0, COMB_AWB);
+ if (!ret && !ctrl->val) {
+ ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_RED,
+ priv->red->val);
+ }
+ return ret;
+ case V4L2_CID_SATURATION:
+ return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
+ SAT_MASK);
+ case V4L2_CID_HUE:
+ return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
+ HUE_MASK);
+ case V4L2_CID_BRIGHTNESS:
+ return ov6650_reg_write(client, REG_BRT, ctrl->val);
+ case V4L2_CID_EXPOSURE_AUTO:
+ ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
+ V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
+ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
+ ret = ov6650_reg_write(client, REG_AECH,
+ priv->exposure->val);
+ return ret;
+ case V4L2_CID_GAMMA:
+ return ov6650_reg_write(client, REG_GAM1, ctrl->val);
+ case V4L2_CID_VFLIP:
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
+ case V4L2_CID_HFLIP:
+ return ov6650_reg_rmw(client, REG_COMB,
+ ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
+ }
+
+ return -EINVAL;
+}
+
+/* Get chip identification */
+static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ id->ident = V4L2_IDENT_OV6650;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov6650_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 & ~0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov6650_reg_read(client, reg->reg, &val);
+ if (!ret)
+ reg->val = (__u64)val;
+
+ return ret;
+}
+
+static int ov6650_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov6650_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov6650_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = priv->rect;
+
+ return 0;
+}
+
+static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_rect rect = a->c;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ rect.left = ALIGN(rect.left, 2);
+ rect.width = ALIGN(rect.width, 2);
+ rect.top = ALIGN(rect.top, 2);
+ rect.height = ALIGN(rect.height, 2);
+ soc_camera_limit_side(&rect.left, &rect.width,
+ DEF_HSTRT << 1, 2, W_CIF);
+ soc_camera_limit_side(&rect.top, &rect.height,
+ DEF_VSTRT << 1, 2, H_CIF);
+
+ ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1);
+ if (!ret) {
+ priv->rect.left = rect.left;
+ ret = ov6650_reg_write(client, REG_HSTOP,
+ (rect.left + rect.width) >> 1);
+ }
+ if (!ret) {
+ priv->rect.width = rect.width;
+ ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1);
+ }
+ if (!ret) {
+ priv->rect.top = rect.top;
+ ret = ov6650_reg_write(client, REG_VSTOP,
+ (rect.top + rect.height) >> 1);
+ }
+ if (!ret)
+ priv->rect.height = rect.height;
+
+ return ret;
+}
+
+static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = DEF_HSTRT << 1;
+ a->bounds.top = DEF_VSTRT << 1;
+ a->bounds.width = W_CIF;
+ a->bounds.height = H_CIF;
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov6650_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ mf->width = priv->rect.width >> priv->half_scale;
+ mf->height = priv->rect.height >> priv->half_scale;
+ mf->code = priv->code;
+ mf->colorspace = priv->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
+{
+ return width > rect->width >> 1 || height > rect->height >> 1;
+}
+
+static u8 to_clkrc(struct v4l2_fract *timeperframe,
+ unsigned long pclk_limit, unsigned long pclk_max)
+{
+ unsigned long pclk;
+
+ if (timeperframe->numerator && timeperframe->denominator)
+ pclk = pclk_max * timeperframe->denominator /
+ (FRAME_RATE_MAX * timeperframe->numerator);
+ else
+ pclk = pclk_max;
+
+ if (pclk_limit && pclk_limit < pclk)
+ pclk = pclk_limit;
+
+ return (pclk_max - 1) / pclk;
+}
+
+/* set the format we will capture in */
+static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
+ struct soc_camera_sense *sense = icd->sense;
+ struct ov6650 *priv = to_ov6650(client);
+ bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
+ struct v4l2_crop a = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .c = {
+ .left = priv->rect.left + (priv->rect.width >> 1) -
+ (mf->width >> (1 - half_scale)),
+ .top = priv->rect.top + (priv->rect.height >> 1) -
+ (mf->height >> (1 - half_scale)),
+ .width = mf->width << half_scale,
+ .height = mf->height << half_scale,
+ },
+ };
+ enum v4l2_mbus_pixelcode code = mf->code;
+ unsigned long mclk, pclk;
+ u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
+ int ret;
+
+ /* select color matrix configuration for given color encoding */
+ switch (code) {
+ case V4L2_MBUS_FMT_Y8_1X8:
+ dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
+ coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
+ coma_set |= COMA_BW;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
+ coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
+ coma_set |= COMA_WORD_SWAP;
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
+ COMA_BYTE_SWAP;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
+ if (half_scale) {
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+ coma_set |= COMA_BYTE_SWAP;
+ } else {
+ coma_mask |= COMA_RGB | COMA_BW;
+ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ }
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
+ if (half_scale) {
+ coma_mask |= COMA_RGB | COMA_BW;
+ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ } else {
+ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
+ coma_set |= COMA_BYTE_SWAP;
+ }
+ break;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
+ coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
+ coma_set |= COMA_RAW_RGB | COMA_RGB;
+ break;
+ default:
+ dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
+ return -EINVAL;
+ }
+ priv->code = code;
+
+ if (code == V4L2_MBUS_FMT_Y8_1X8 ||
+ code == V4L2_MBUS_FMT_SBGGR8_1X8) {
+ coml_mask = COML_ONE_CHANNEL;
+ coml_set = 0;
+ priv->pclk_max = 4000000;
+ } else {
+ coml_mask = 0;
+ coml_set = COML_ONE_CHANNEL;
+ priv->pclk_max = 8000000;
+ }
+
+ if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
+ priv->colorspace = V4L2_COLORSPACE_SRGB;
+ else if (code != 0)
+ priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (half_scale) {
+ dev_dbg(&client->dev, "max resolution: QCIF\n");
+ coma_set |= COMA_QCIF;
+ priv->pclk_max /= 2;
+ } else {
+ dev_dbg(&client->dev, "max resolution: CIF\n");
+ coma_mask |= COMA_QCIF;
+ }
+ priv->half_scale = half_scale;
+
+ if (sense) {
+ if (sense->master_clock == 8000000) {
+ dev_dbg(&client->dev, "8MHz input clock\n");
+ clkrc = CLKRC_6MHz;
+ } else if (sense->master_clock == 12000000) {
+ dev_dbg(&client->dev, "12MHz input clock\n");
+ clkrc = CLKRC_12MHz;
+ } else if (sense->master_clock == 16000000) {
+ dev_dbg(&client->dev, "16MHz input clock\n");
+ clkrc = CLKRC_16MHz;
+ } else if (sense->master_clock == 24000000) {
+ dev_dbg(&client->dev, "24MHz input clock\n");
+ clkrc = CLKRC_24MHz;
+ } else {
+ dev_err(&client->dev,
+ "unsupported input clock, check platform data\n");
+ return -EINVAL;
+ }
+ mclk = sense->master_clock;
+ priv->pclk_limit = sense->pixel_clock_max;
+ } else {
+ clkrc = CLKRC_24MHz;
+ mclk = 24000000;
+ priv->pclk_limit = 0;
+ dev_dbg(&client->dev, "using default 24MHz input clock\n");
+ }
+
+ clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+ pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc);
+ dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
+ mclk / pclk, 10 * mclk % pclk / pclk);
+
+ ret = ov6650_s_crop(sd, &a);
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
+ if (!ret)
+ ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
+
+ if (!ret) {
+ mf->colorspace = priv->colorspace;
+ mf->width = priv->rect.width >> half_scale;
+ mf->height = priv->rect.height >> half_scale;
+ }
+
+ return ret;
+}
+
+static int ov6650_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+
+ if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
+ v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
+ &mf->height, 2, H_CIF, 1, 0);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_Y10_1X10:
+ mf->code = V4L2_MBUS_FMT_Y8_1X8;
+ case V4L2_MBUS_FMT_Y8_1X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
+ case V4L2_MBUS_FMT_SBGGR8_1X8:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ }
+
+ return 0;
+}
+
+static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov6650_codes))
+ return -EINVAL;
+
+ *code = ov6650_codes[index];
+ return 0;
+}
+
+static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
+ priv->pclk_limit, priv->pclk_max));
+ cp->timeperframe.denominator = FRAME_RATE_MAX;
+
+ dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
+ cp->timeperframe.numerator, cp->timeperframe.denominator);
+
+ return 0;
+}
+
+static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov6650 *priv = to_ov6650(client);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int div, ret;
+ u8 clkrc;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+ div = 1; /* Reset to full rate */
+ else
+ div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
+
+ if (div == 0)
+ div = 1;
+ else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK))
+ div = GET_CLKRC_DIV(CLKRC_DIV_MASK);
+
+ /*
+ * Keep result to be used as tpf limit
+ * for subseqent clock divider calculations
+ */
+ priv->tpf.numerator = div;
+ priv->tpf.denominator = FRAME_RATE_MAX;
+
+ clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
+
+ ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK);
+ if (!ret) {
+ tpf->numerator = GET_CLKRC_DIV(clkrc);
+ tpf->denominator = FRAME_RATE_MAX;
+ }
+
+ return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov6650_reset(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "reset\n");
+
+ ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
+ if (ret)
+ dev_err(&client->dev,
+ "An error occurred while entering soft reset!\n");
+
+ return ret;
+}
+
+/* program default register values */
+static int ov6650_prog_dflt(struct i2c_client *client)
+{
+ int ret;
+
+ dev_dbg(&client->dev, "initializing\n");
+
+ ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */
+ if (!ret)
+ ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
+
+ return ret;
+}
+
+static int ov6650_video_probe(struct i2c_client *client)
+{
+ struct ov6650 *priv = to_ov6650(client);
+ u8 pidh, pidl, midh, midl;
+ int ret;
+
+ ret = ov6650_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ ret = ov6650_reg_read(client, REG_PIDH, &pidh);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_PIDL, &pidl);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_MIDH, &midh);
+ if (!ret)
+ ret = ov6650_reg_read(client, REG_MIDL, &midl);
+
+ if (ret)
+ goto done;
+
+ if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
+ dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
+ pidh, pidl);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev,
+ "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
+ pidh, pidl, midh, midl);
+
+ ret = ov6650_reset(client);
+ if (!ret)
+ ret = ov6650_prog_dflt(client);
+ if (!ret)
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov6650_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
+ .g_volatile_ctrl = ov6550_g_volatile_ctrl,
+ .s_ctrl = ov6550_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov6650_core_ops = {
+ .g_chip_ident = ov6650_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov6650_get_register,
+ .s_register = ov6650_set_register,
+#endif
+ .s_power = ov6650_s_power,
+};
+
+/* Request bus settings on camera side */
+static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+/* Alter bus settings on camera side */
+static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+ int ret;
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
+ if (ret)
+ return ret;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
+ else
+ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
+
+ return ret;
+}
+
+static struct v4l2_subdev_video_ops ov6650_video_ops = {
+ .s_stream = ov6650_s_stream,
+ .g_mbus_fmt = ov6650_g_fmt,
+ .s_mbus_fmt = ov6650_s_fmt,
+ .try_mbus_fmt = ov6650_try_fmt,
+ .enum_mbus_fmt = ov6650_enum_fmt,
+ .cropcap = ov6650_cropcap,
+ .g_crop = ov6650_g_crop,
+ .s_crop = ov6650_s_crop,
+ .g_parm = ov6650_g_parm,
+ .s_parm = ov6650_s_parm,
+ .g_mbus_config = ov6650_g_mbus_config,
+ .s_mbus_config = ov6650_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov6650_subdev_ops = {
+ .core = &ov6650_core_ops,
+ .video = &ov6650_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov6650_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov6650 *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
+ priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
+ priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
+ priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
+ &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
+ priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
+ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
+
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+ v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
+ v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
+ v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
+ V4L2_EXPOSURE_MANUAL, true);
+
+ priv->rect.left = DEF_HSTRT << 1;
+ priv->rect.top = DEF_VSTRT << 1;
+ priv->rect.width = W_CIF;
+ priv->rect.height = H_CIF;
+ priv->half_scale = false;
+ priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ priv->colorspace = V4L2_COLORSPACE_JPEG;
+
+ ret = ov6650_video_probe(client);
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov6650_remove(struct i2c_client *client)
+{
+ struct ov6650 *priv = to_ov6650(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov6650_id[] = {
+ { "ov6650", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov6650_id);
+
+static struct i2c_driver ov6650_i2c_driver = {
+ .driver = {
+ .name = "ov6650",
+ },
+ .probe = ov6650_probe,
+ .remove = ov6650_remove,
+ .id_table = ov6650_id,
+};
+
+module_i2c_driver(ov6650_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c
new file mode 100644
index 000000000000..e4a10751894d
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov772x.c
@@ -0,0 +1,1137 @@
+/*
+ * ov772x Camera Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/ov772x.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * register offset
+ */
+#define GAIN 0x00 /* AGC - Gain control gain setting */
+#define BLUE 0x01 /* AWB - Blue channel gain setting */
+#define RED 0x02 /* AWB - Red channel gain setting */
+#define GREEN 0x03 /* AWB - Green channel gain setting */
+#define COM1 0x04 /* Common control 1 */
+#define BAVG 0x05 /* U/B Average Level */
+#define GAVG 0x06 /* Y/Gb Average Level */
+#define RAVG 0x07 /* V/R Average Level */
+#define AECH 0x08 /* Exposure Value - AEC MSBs */
+#define COM2 0x09 /* Common control 2 */
+#define PID 0x0A /* Product ID Number MSB */
+#define VER 0x0B /* Product ID Number LSB */
+#define COM3 0x0C /* Common control 3 */
+#define COM4 0x0D /* Common control 4 */
+#define COM5 0x0E /* Common control 5 */
+#define COM6 0x0F /* Common control 6 */
+#define AEC 0x10 /* Exposure Value */
+#define CLKRC 0x11 /* Internal clock */
+#define COM7 0x12 /* Common control 7 */
+#define COM8 0x13 /* Common control 8 */
+#define COM9 0x14 /* Common control 9 */
+#define COM10 0x15 /* Common control 10 */
+#define REG16 0x16 /* Register 16 */
+#define HSTART 0x17 /* Horizontal sensor size */
+#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
+#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
+#define VSIZE 0x1A /* Vertical sensor size */
+#define PSHFT 0x1B /* Data format - pixel delay select */
+#define MIDH 0x1C /* Manufacturer ID byte - high */
+#define MIDL 0x1D /* Manufacturer ID byte - low */
+#define LAEC 0x1F /* Fine AEC value */
+#define COM11 0x20 /* Common control 11 */
+#define BDBASE 0x22 /* Banding filter Minimum AEC value */
+#define DBSTEP 0x23 /* Banding filter Maximum Setp */
+#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
+#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
+#define VPT 0x26 /* AGC/AEC Fast mode operating region */
+#define REG28 0x28 /* Register 28 */
+#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
+#define EXHCH 0x2A /* Dummy pixel insert MSB */
+#define EXHCL 0x2B /* Dummy pixel insert LSB */
+#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
+#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
+#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
+#define YAVE 0x2F /* Y/G Channel Average value */
+#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
+#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
+#define HREF 0x32 /* Image start and size control */
+#define DM_LNL 0x33 /* Dummy line low 8 bits */
+#define DM_LNH 0x34 /* Dummy line high 8 bits */
+#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
+#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
+#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
+#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
+#define OFF_B 0x39 /* Analog process B channel offset value */
+#define OFF_R 0x3A /* Analog process R channel offset value */
+#define OFF_GB 0x3B /* Analog process Gb channel offset value */
+#define OFF_GR 0x3C /* Analog process Gr channel offset value */
+#define COM12 0x3D /* Common control 12 */
+#define COM13 0x3E /* Common control 13 */
+#define COM14 0x3F /* Common control 14 */
+#define COM15 0x40 /* Common control 15*/
+#define COM16 0x41 /* Common control 16 */
+#define TGT_B 0x42 /* BLC blue channel target value */
+#define TGT_R 0x43 /* BLC red channel target value */
+#define TGT_GB 0x44 /* BLC Gb channel target value */
+#define TGT_GR 0x45 /* BLC Gr channel target value */
+/* for ov7720 */
+#define LCC0 0x46 /* Lens correction control 0 */
+#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
+#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
+#define LCC3 0x49 /* Lens correction option 3 */
+#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
+#define LCC5 0x4B /* Lens correction option 5 */
+#define LCC6 0x4C /* Lens correction option 6 */
+/* for ov7725 */
+#define LC_CTR 0x46 /* Lens correction control */
+#define LC_XC 0x47 /* X coordinate of lens correction center relative */
+#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
+#define LC_COEF 0x49 /* Lens correction coefficient */
+#define LC_RADI 0x4A /* Lens correction radius */
+#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
+#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
+
+#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define AREF0 0x4E /* Sensor reference control */
+#define AREF1 0x4F /* Sensor reference current control */
+#define AREF2 0x50 /* Analog reference control */
+#define AREF3 0x51 /* ADC reference control */
+#define AREF4 0x52 /* ADC reference control */
+#define AREF5 0x53 /* ADC reference control */
+#define AREF6 0x54 /* Analog reference control */
+#define AREF7 0x55 /* Analog reference control */
+#define UFIX 0x60 /* U channel fixed value output */
+#define VFIX 0x61 /* V channel fixed value output */
+#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
+#define AWB_CTRL0 0x63 /* AWB control byte 0 */
+#define DSP_CTRL1 0x64 /* DSP control byte 1 */
+#define DSP_CTRL2 0x65 /* DSP control byte 2 */
+#define DSP_CTRL3 0x66 /* DSP control byte 3 */
+#define DSP_CTRL4 0x67 /* DSP control byte 4 */
+#define AWB_BIAS 0x68 /* AWB BLC level clip */
+#define AWB_CTRL1 0x69 /* AWB control 1 */
+#define AWB_CTRL2 0x6A /* AWB control 2 */
+#define AWB_CTRL3 0x6B /* AWB control 3 */
+#define AWB_CTRL4 0x6C /* AWB control 4 */
+#define AWB_CTRL5 0x6D /* AWB control 5 */
+#define AWB_CTRL6 0x6E /* AWB control 6 */
+#define AWB_CTRL7 0x6F /* AWB control 7 */
+#define AWB_CTRL8 0x70 /* AWB control 8 */
+#define AWB_CTRL9 0x71 /* AWB control 9 */
+#define AWB_CTRL10 0x72 /* AWB control 10 */
+#define AWB_CTRL11 0x73 /* AWB control 11 */
+#define AWB_CTRL12 0x74 /* AWB control 12 */
+#define AWB_CTRL13 0x75 /* AWB control 13 */
+#define AWB_CTRL14 0x76 /* AWB control 14 */
+#define AWB_CTRL15 0x77 /* AWB control 15 */
+#define AWB_CTRL16 0x78 /* AWB control 16 */
+#define AWB_CTRL17 0x79 /* AWB control 17 */
+#define AWB_CTRL18 0x7A /* AWB control 18 */
+#define AWB_CTRL19 0x7B /* AWB control 19 */
+#define AWB_CTRL20 0x7C /* AWB control 20 */
+#define AWB_CTRL21 0x7D /* AWB control 21 */
+#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
+#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
+#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
+#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
+#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
+#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
+#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
+#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
+#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
+#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
+#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
+#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
+#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
+#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
+#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
+#define SLOP 0x8D /* Gamma curve highest segment slope */
+#define DNSTH 0x8E /* De-noise threshold */
+#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */
+#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
+#define DNSOFF 0x91 /* Auto De-noise threshold control */
+#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */
+#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */
+#define MTX1 0x94 /* Matrix coefficient 1 */
+#define MTX2 0x95 /* Matrix coefficient 2 */
+#define MTX3 0x96 /* Matrix coefficient 3 */
+#define MTX4 0x97 /* Matrix coefficient 4 */
+#define MTX5 0x98 /* Matrix coefficient 5 */
+#define MTX6 0x99 /* Matrix coefficient 6 */
+#define MTX_CTRL 0x9A /* Matrix control */
+#define BRIGHT 0x9B /* Brightness control */
+#define CNTRST 0x9C /* Contrast contrast */
+#define CNTRST_CTRL 0x9D /* Contrast contrast center */
+#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
+#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
+#define SCAL0 0xA0 /* Scaling control 0 */
+#define SCAL1 0xA1 /* Scaling control 1 */
+#define SCAL2 0xA2 /* Scaling control 2 */
+#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
+#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
+#define SDE 0xA6 /* Special digital effect control */
+#define USAT 0xA7 /* U component saturation control */
+#define VSAT 0xA8 /* V component saturation control */
+/* for ov7720 */
+#define HUE0 0xA9 /* Hue control 0 */
+#define HUE1 0xAA /* Hue control 1 */
+/* for ov7725 */
+#define HUECOS 0xA9 /* Cosine value */
+#define HUESIN 0xAA /* Sine value */
+
+#define SIGN 0xAB /* Sign bit for Hue and contrast */
+#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
+
+/*
+ * register detail
+ */
+
+/* COM2 */
+#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
+ /* Output drive capability */
+#define OCAP_1x 0x00 /* 1x */
+#define OCAP_2x 0x01 /* 2x */
+#define OCAP_3x 0x02 /* 3x */
+#define OCAP_4x 0x03 /* 4x */
+
+/* COM3 */
+#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
+#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
+
+#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
+#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
+#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+#define SWAP_ML 0x08 /* Swap output MSB/LSB */
+ /* Tri-state option for output clock */
+#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+ /* Tri-state option for output data */
+#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
+ /* 1: No tri-state at this period */
+#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
+
+/* COM4 */
+ /* PLL frequency control */
+#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
+#define PLL_4x 0x40 /* 01: PLL 4x */
+#define PLL_6x 0x80 /* 10: PLL 6x */
+#define PLL_8x 0xc0 /* 11: PLL 8x */
+ /* AEC evaluate window */
+#define AEC_FULL 0x00 /* 00: Full window */
+#define AEC_1p2 0x10 /* 01: 1/2 window */
+#define AEC_1p4 0x20 /* 10: 1/4 window */
+#define AEC_2p3 0x30 /* 11: Low 2/3 window */
+
+/* COM5 */
+#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
+#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
+ /* Auto frame rate max rate control */
+#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
+#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
+#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
+#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
+ /* Auto frame rate active point control */
+#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
+#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
+#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
+#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
+ /* AEC max step control */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+ /* 1 : No limit to AEC increase step */
+
+/* COM7 */
+ /* SCCB Register Reset */
+#define SCCB_RESET 0x80 /* 0 : No change */
+ /* 1 : Resets all registers to default */
+ /* Resolution selection */
+#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
+#define SLCT_VGA 0x00 /* 0 : VGA */
+#define SLCT_QVGA 0x40 /* 1 : QVGA */
+#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+#define SENSOR_RAW 0x10 /* Sensor RAW */
+ /* RGB output format control */
+#define FMT_MASK 0x0c /* Mask of color format */
+#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+#define FMT_RGB565 0x04 /* 01 : RGB 565 */
+#define FMT_RGB555 0x08 /* 10 : RGB 555 */
+#define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
+#define OFMT_MASK 0x03 /* Mask of output format */
+#define OFMT_YUV 0x00 /* 00 : YUV */
+#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+#define OFMT_RGB 0x02 /* 10 : RGB */
+#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
+
+/* COM8 */
+#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
+ /* AEC Setp size limit */
+#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
+ /* 1 : Unlimited step size */
+#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
+#define AEC_BND 0x10 /* Enable AEC below banding value */
+#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
+#define AGC_ON 0x04 /* AGC Enable */
+#define AWB_ON 0x02 /* AWB Enable */
+#define AEC_ON 0x01 /* AEC Enable */
+
+/* COM9 */
+#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
+ /* Automatic gain ceiling - maximum AGC value */
+#define GAIN_2x 0x00 /* 000 : 2x */
+#define GAIN_4x 0x10 /* 001 : 4x */
+#define GAIN_8x 0x20 /* 010 : 8x */
+#define GAIN_16x 0x30 /* 011 : 16x */
+#define GAIN_32x 0x40 /* 100 : 32x */
+#define GAIN_64x 0x50 /* 101 : 64x */
+#define GAIN_128x 0x60 /* 110 : 128x */
+#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
+#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
+
+/* COM11 */
+#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
+#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
+
+/* HREF */
+#define HREF_VSTART_SHIFT 6 /* VSTART LSB */
+#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */
+#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */
+#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */
+
+/* EXHCH */
+#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */
+#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */
+
+/* DSP_CTRL1 */
+#define FIFO_ON 0x80 /* FIFO enable/disable selection */
+#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
+#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
+#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
+#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
+#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
+#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
+#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
+
+/* DSP_CTRL3 */
+#define UV_MASK 0x80 /* UV output sequence option */
+#define UV_ON 0x80 /* ON */
+#define UV_OFF 0x00 /* OFF */
+#define CBAR_MASK 0x20 /* DSP Color bar mask */
+#define CBAR_ON 0x20 /* ON */
+#define CBAR_OFF 0x00 /* OFF */
+
+/* DSP_CTRL4 */
+#define DSP_OFMT_YUV 0x00
+#define DSP_OFMT_RGB 0x00
+#define DSP_OFMT_RAW8 0x02
+#define DSP_OFMT_RAW10 0x03
+
+/* DSPAUTO (DSP Auto Function ON/OFF Control) */
+#define AWB_ACTRL 0x80 /* AWB auto threshold control */
+#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */
+#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */
+#define UV_ACTRL 0x10 /* UV adjust auto slope control */
+#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */
+#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define OV772X_MAX_WIDTH VGA_WIDTH
+#define OV772X_MAX_HEIGHT VGA_HEIGHT
+
+/*
+ * ID
+ */
+#define OV7720 0x7720
+#define OV7725 0x7721
+#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
+
+/*
+ * struct
+ */
+
+struct ov772x_color_format {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+ u8 dsp3;
+ u8 dsp4;
+ u8 com3;
+ u8 com7;
+};
+
+struct ov772x_win_size {
+ char *name;
+ unsigned char com7_bit;
+ struct v4l2_rect rect;
+};
+
+struct ov772x_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct ov772x_camera_info *info;
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+ int model;
+ unsigned short flag_vflip:1;
+ unsigned short flag_hflip:1;
+ /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+ unsigned short band_filter;
+};
+
+/*
+ * supported color format list
+ */
+static const struct ov772x_color_format ov772x_cfmts[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = UV_ON,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_YUV,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = OFMT_YUV,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = SWAP_RGB,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_YUV,
+ .com3 = 0x0,
+ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output,
+ * regardless of the COM7 value. We can thus only support 10-bit
+ * Bayer until someone figures it out.
+ */
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .dsp3 = 0x0,
+ .dsp4 = DSP_OFMT_RAW10,
+ .com3 = 0x0,
+ .com7 = SENSOR_RAW | OFMT_BRAW,
+ },
+};
+
+
+/*
+ * window size list
+ */
+
+static const struct ov772x_win_size ov772x_win_sizes[] = {
+ {
+ .name = "VGA",
+ .com7_bit = SLCT_VGA,
+ .rect = {
+ .left = 140,
+ .top = 14,
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ },
+ }, {
+ .name = "QVGA",
+ .com7_bit = SLCT_QVGA,
+ .rect = {
+ .left = 252,
+ .top = 6,
+ .width = QVGA_WIDTH,
+ .height = QVGA_HEIGHT,
+ },
+ },
+};
+
+/*
+ * general function
+ */
+
+static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ov772x_priv, subdev);
+}
+
+static inline int ov772x_read(struct i2c_client *client, u8 addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask,
+ u8 set)
+{
+ s32 val = ov772x_read(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return ov772x_write(client, command, val);
+}
+
+static int ov772x_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov772x_write(client, COM7, SCCB_RESET);
+ if (ret < 0)
+ return ret;
+
+ msleep(1);
+
+ return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+}
+
+/*
+ * soc_camera_ops function
+ */
+
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ if (!enable) {
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+ return 0;
+ }
+
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
+
+ dev_dbg(&client->dev, "format %d, win %s\n",
+ priv->cfmt->code, priv->win->name);
+
+ return 0;
+}
+
+static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov772x_priv *priv = container_of(ctrl->handler,
+ struct ov772x_priv, hdl);
+ struct v4l2_subdev *sd = &priv->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
+ u8 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ val = ctrl->val ? VFLIP_IMG : 0x00;
+ priv->flag_vflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val ^= VFLIP_IMG;
+ return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
+ case V4L2_CID_HFLIP:
+ val = ctrl->val ? HFLIP_IMG : 0x00;
+ priv->flag_hflip = ctrl->val;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val ^= HFLIP_IMG;
+ return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
+ case V4L2_CID_BAND_STOP_FILTER:
+ if (!ctrl->val) {
+ /* Switch the filter off, it is on now */
+ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+ if (!ret)
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, 0);
+ } else {
+ /* Switch the filter on, set AEC low limit */
+ val = 256 - ctrl->val;
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, BNDF_ON_OFF);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, val);
+ }
+ if (!ret)
+ priv->band_filter = ctrl->val;
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ id->ident = priv->model;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov772x_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ reg->size = 1;
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = ov772x_read(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int ov772x_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return ov772x_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov772x_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
+{
+ const struct ov772x_win_size *win = &ov772x_win_sizes[0];
+ u32 best_diff = UINT_MAX;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) {
+ u32 diff = abs(width - ov772x_win_sizes[i].rect.width)
+ + abs(height - ov772x_win_sizes[i].rect.height);
+ if (diff < best_diff) {
+ best_diff = diff;
+ win = &ov772x_win_sizes[i];
+ }
+ }
+
+ return win;
+}
+
+static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf,
+ const struct ov772x_color_format **cfmt,
+ const struct ov772x_win_size **win)
+{
+ unsigned int i;
+
+ /* Select a format. */
+ *cfmt = &ov772x_cfmts[0];
+
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (mf->code == ov772x_cfmts[i].code) {
+ *cfmt = &ov772x_cfmts[i];
+ break;
+ }
+ }
+
+ /* Select a window size. */
+ *win = ov772x_select_win(mf->width, mf->height);
+}
+
+static int ov772x_set_params(struct ov772x_priv *priv,
+ const struct ov772x_color_format *cfmt,
+ const struct ov772x_win_size *win)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ int ret;
+ u8 val;
+
+ /*
+ * reset hardware
+ */
+ ov772x_reset(client);
+
+ /*
+ * Edge Ctrl
+ */
+ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
+
+ /*
+ * Manual Edge Control Mode
+ *
+ * Edge auto strength bit is set by default.
+ * Remove it when manual mode.
+ */
+
+ ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
+ priv->info->edgectrl.threshold);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
+ priv->info->edgectrl.strength);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
+ /*
+ * Auto Edge Control Mode
+ *
+ * set upper and lower limit
+ */
+ ret = ov772x_mask_set(client,
+ EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
+ priv->info->edgectrl.upper);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ ret = ov772x_mask_set(client,
+ EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
+ priv->info->edgectrl.lower);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /* Format and window size */
+ ret = ov772x_write(client, HSTART, win->rect.left >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HSIZE, win->rect.width >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VSTART, win->rect.top >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VSIZE, win->rect.height >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, HREF,
+ ((win->rect.top & 1) << HREF_VSTART_SHIFT) |
+ ((win->rect.left & 3) << HREF_HSTART_SHIFT) |
+ ((win->rect.height & 1) << HREF_VSIZE_SHIFT) |
+ ((win->rect.width & 3) << HREF_HSIZE_SHIFT));
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ ret = ov772x_write(client, EXHCH,
+ ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) |
+ ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT));
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * set DSP_CTRL3
+ */
+ val = cfmt->dsp3;
+ if (val) {
+ ret = ov772x_mask_set(client,
+ DSP_CTRL3, UV_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /* DSP_CTRL4: AEC reference point and DSP output format. */
+ if (cfmt->dsp4) {
+ ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ /*
+ * set COM3
+ */
+ val = cfmt->com3;
+ if (priv->info->flags & OV772X_FLAG_VFLIP)
+ val |= VFLIP_IMG;
+ if (priv->info->flags & OV772X_FLAG_HFLIP)
+ val |= HFLIP_IMG;
+ if (priv->flag_vflip)
+ val ^= VFLIP_IMG;
+ if (priv->flag_hflip)
+ val ^= HFLIP_IMG;
+
+ ret = ov772x_mask_set(client,
+ COM3, SWAP_MASK | IMG_MASK, val);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /* COM7: Sensor resolution and output format control. */
+ ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+
+ /*
+ * set COM8
+ */
+ if (priv->band_filter) {
+ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, 256 - priv->band_filter);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ return ret;
+
+ov772x_set_fmt_error:
+
+ ov772x_reset(client);
+
+ return ret;
+}
+
+static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = VGA_WIDTH;
+ a->c.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = OV772X_MAX_WIDTH;
+ a->bounds.height = OV772X_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov772x_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+
+ mf->width = priv->win->rect.width;
+ mf->height = priv->win->rect.height;
+ mf->code = priv->cfmt->code;
+ mf->colorspace = priv->cfmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
+{
+ struct ov772x_priv *priv = to_ov772x(sd);
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+ int ret;
+
+ ov772x_select_params(mf, &cfmt, &win);
+
+ ret = ov772x_set_params(priv, cfmt, win);
+ if (ret < 0)
+ return ret;
+
+ priv->win = win;
+ priv->cfmt = cfmt;
+
+ mf->code = cfmt->code;
+ mf->width = win->rect.width;
+ mf->height = win->rect.height;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = cfmt->colorspace;
+
+ return 0;
+}
+
+static int ov772x_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ const struct ov772x_color_format *cfmt;
+ const struct ov772x_win_size *win;
+
+ ov772x_select_params(mf, &cfmt, &win);
+
+ mf->code = cfmt->code;
+ mf->width = win->rect.width;
+ mf->height = win->rect.height;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = cfmt->colorspace;
+
+ return 0;
+}
+
+static int ov772x_video_probe(struct ov772x_priv *priv)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+ u8 pid, ver;
+ const char *devname;
+ int ret;
+
+ ret = ov772x_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ pid = ov772x_read(client, PID);
+ ver = ov772x_read(client, VER);
+
+ switch (VERSION(pid, ver)) {
+ case OV7720:
+ devname = "ov7720";
+ priv->model = V4L2_IDENT_OV7720;
+ break;
+ case OV7725:
+ devname = "ov7725";
+ priv->model = V4L2_IDENT_OV7725;
+ break;
+ default:
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev,
+ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname,
+ pid,
+ ver,
+ ov772x_read(client, MIDH),
+ ov772x_read(client, MIDL));
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov772x_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
+ .s_ctrl = ov772x_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+ .g_chip_ident = ov772x_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov772x_g_register,
+ .s_register = ov772x_s_register,
+#endif
+ .s_power = ov772x_s_power,
+};
+
+static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov772x_cfmts))
+ return -EINVAL;
+
+ *code = ov772x_cfmts[index].code;
+ return 0;
+}
+
+static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+ .s_stream = ov772x_s_stream,
+ .g_mbus_fmt = ov772x_g_fmt,
+ .s_mbus_fmt = ov772x_s_fmt,
+ .try_mbus_fmt = ov772x_try_fmt,
+ .cropcap = ov772x_cropcap,
+ .g_crop = ov772x_g_crop,
+ .enum_mbus_fmt = ov772x_enum_fmt,
+ .g_mbus_config = ov772x_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov772x_subdev_ops = {
+ .core = &ov772x_subdev_core_ops,
+ .video = &ov772x_subdev_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int ov772x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov772x_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "OV772X: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&adapter->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = icl->priv;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
+ v4l2_ctrl_handler_init(&priv->hdl, 3);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ ret = priv->hdl.error;
+ goto done;
+ }
+
+ ret = ov772x_video_probe(priv);
+ if (ret < 0)
+ goto done;
+
+ priv->cfmt = &ov772x_cfmts[0];
+ priv->win = &ov772x_win_sizes[0];
+
+done:
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+ return ret;
+}
+
+static int ov772x_remove(struct i2c_client *client)
+{
+ struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov772x_id[] = {
+ { "ov772x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov772x_id);
+
+static struct i2c_driver ov772x_i2c_driver = {
+ .driver = {
+ .name = "ov772x",
+ },
+ .probe = ov772x_probe,
+ .remove = ov772x_remove,
+ .id_table = ov772x_id,
+};
+
+module_i2c_driver(ov772x_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for ov772x");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c
new file mode 100644
index 000000000000..b323684eaf77
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov9640.c
@@ -0,0 +1,763 @@
+/*
+ * OmniVision OV96xx Camera Driver
+ *
+ * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * Based on ov772x camera driver:
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov7670 and soc_camera_platform driver,
+ *
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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 <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+
+#include "ov9640.h"
+
+#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev)
+
+/* default register setup */
+static const struct ov9640_reg ov9640_regs_dflt[] = {
+ { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
+ { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
+ OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
+ { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
+ { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
+ { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY },
+ { OV9640_COM16, OV9640_COM16_RB_AVG },
+
+ /* Gamma curve P */
+ { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
+ { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
+ { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
+ { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
+
+ /* Gamma curve T */
+ { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
+ { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
+ { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
+ { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
+};
+
+/* Configurations
+ * NOTE: for YUV, alter the following registers:
+ * COM12 |= OV9640_COM12_YUV_AVG
+ *
+ * for RGB, alter the following registers:
+ * COM7 |= OV9640_COM7_RGB
+ * COM13 |= OV9640_COM13_RGB_AVG
+ * COM15 |= proper RGB color encoding mode
+ */
+static const struct ov9640_reg ov9640_regs_qqcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qqvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qcif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QCIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_qvga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
+ { OV9640_COM7, OV9640_COM7_QVGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_cif[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_CIF },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_vga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, OV9640_COM7_VGA },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_sxga[] = {
+ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
+ { OV9640_COM3, OV9640_COM3_VP },
+ { OV9640_COM7, 0 },
+ { OV9640_COM12, OV9640_COM12_RSVD },
+ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
+ { OV9640_COM15, OV9640_COM15_OR_10F0 },
+};
+
+static const struct ov9640_reg ov9640_regs_yuv[] = {
+ { OV9640_MTX1, 0x58 },
+ { OV9640_MTX2, 0x48 },
+ { OV9640_MTX3, 0x10 },
+ { OV9640_MTX4, 0x28 },
+ { OV9640_MTX5, 0x48 },
+ { OV9640_MTX6, 0x70 },
+ { OV9640_MTX7, 0x40 },
+ { OV9640_MTX8, 0x40 },
+ { OV9640_MTX9, 0x40 },
+ { OV9640_MTXS, 0x0f },
+};
+
+static const struct ov9640_reg ov9640_regs_rgb[] = {
+ { OV9640_MTX1, 0x71 },
+ { OV9640_MTX2, 0x3e },
+ { OV9640_MTX3, 0x0c },
+ { OV9640_MTX4, 0x33 },
+ { OV9640_MTX5, 0x72 },
+ { OV9640_MTX6, 0x00 },
+ { OV9640_MTX7, 0x2b },
+ { OV9640_MTX8, 0x66 },
+ { OV9640_MTX9, 0xd2 },
+ { OV9640_MTXS, 0x65 },
+};
+
+static enum v4l2_mbus_pixelcode ov9640_codes[] = {
+ V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ V4L2_MBUS_FMT_RGB565_2X8_LE,
+};
+
+/* read a register */
+static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+ int ret;
+ u8 data = reg;
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ msg.flags = I2C_M_RD;
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ goto err;
+
+ *val = data;
+ return 0;
+
+err:
+ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
+ return ret;
+}
+
+/* write a register */
+static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
+{
+ int ret;
+ u8 _val;
+ unsigned char data[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = data,
+ };
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
+ return ret;
+ }
+
+ /* we have to read the register back ... no idea why, maybe HW bug */
+ ret = ov9640_reg_read(client, reg, &_val);
+ if (ret)
+ dev_err(&client->dev,
+ "Failed reading back register 0x%02x!\n", reg);
+
+ return 0;
+}
+
+
+/* Read a register, alter its bits, write it back */
+static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
+{
+ u8 val;
+ int ret;
+
+ ret = ov9640_reg_read(client, reg, &val);
+ if (ret) {
+ dev_err(&client->dev,
+ "[Read]-Modify-Write of register %02x failed!\n", reg);
+ return val;
+ }
+
+ val |= set;
+ val &= ~unset;
+
+ ret = ov9640_reg_write(client, reg, val);
+ if (ret)
+ dev_err(&client->dev,
+ "Read-Modify-[Write] of register %02x failed!\n", reg);
+
+ return ret;
+}
+
+/* Soft reset the camera. This has nothing to do with the RESET pin! */
+static int ov9640_reset(struct i2c_client *client)
+{
+ int ret;
+
+ ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
+ if (ret)
+ dev_err(&client->dev,
+ "An error occurred while entering soft reset!\n");
+
+ return ret;
+}
+
+/* Start/Stop streaming from the device */
+static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
+ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_V, 0);
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ return ov9640_reg_rmw(client, OV9640_MVFP,
+ OV9640_MVFP_H, 0);
+ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
+ }
+ return -EINVAL;
+}
+
+/* Get chip identification */
+static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+
+ id->ident = priv->model;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ov9640_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 & ~0xff)
+ return -EINVAL;
+
+ reg->size = 1;
+
+ ret = ov9640_reg_read(client, reg->reg, &val);
+ if (ret)
+ return ret;
+
+ reg->val = (__u64)val;
+
+ return 0;
+}
+
+static int ov9640_set_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg & ~0xff || reg->val & ~0xff)
+ return -EINVAL;
+
+ return ov9640_reg_write(client, reg->reg, reg->val);
+}
+#endif
+
+static int ov9640_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+/* select nearest higher resolution for capture */
+static void ov9640_res_roundup(u32 *width, u32 *height)
+{
+ int i;
+ enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
+ int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
+ int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
+
+ for (i = 0; i < ARRAY_SIZE(res_x); i++) {
+ if (res_x[i] >= *width && res_y[i] >= *height) {
+ *width = res_x[i];
+ *height = res_y[i];
+ return;
+ }
+ }
+
+ *width = res_x[SXGA];
+ *height = res_y[SXGA];
+}
+
+/* Prepare necessary register changes depending on color encoding */
+static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
+ struct ov9640_reg_alt *alt)
+{
+ switch (code) {
+ default:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ alt->com12 = OV9640_COM12_YUV_AVG;
+ alt->com13 = OV9640_COM13_Y_DELAY_EN |
+ OV9640_COM13_YUV_DLY(0x01);
+ break;
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_555;
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ alt->com7 = OV9640_COM7_RGB;
+ alt->com13 = OV9640_COM13_RGB_AVG;
+ alt->com15 = OV9640_COM15_RGB_565;
+ break;
+ };
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9640_write_regs(struct i2c_client *client, u32 width,
+ enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
+{
+ const struct ov9640_reg *ov9640_regs, *matrix_regs;
+ int ov9640_regs_len, matrix_regs_len;
+ int i, ret;
+ u8 val;
+
+ /* select register configuration for given resolution */
+ switch (width) {
+ case W_QQCIF:
+ ov9640_regs = ov9640_regs_qqcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
+ break;
+ case W_QQVGA:
+ ov9640_regs = ov9640_regs_qqvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
+ break;
+ case W_QCIF:
+ ov9640_regs = ov9640_regs_qcif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
+ break;
+ case W_QVGA:
+ ov9640_regs = ov9640_regs_qvga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
+ break;
+ case W_CIF:
+ ov9640_regs = ov9640_regs_cif;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
+ break;
+ case W_VGA:
+ ov9640_regs = ov9640_regs_vga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
+ break;
+ case W_SXGA:
+ ov9640_regs = ov9640_regs_sxga;
+ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
+ break;
+ default:
+ dev_err(&client->dev, "Failed to select resolution!\n");
+ return -EINVAL;
+ }
+
+ /* select color matrix configuration for given color encoding */
+ if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ matrix_regs = ov9640_regs_yuv;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
+ } else {
+ matrix_regs = ov9640_regs_rgb;
+ matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
+ }
+
+ /* write register settings into the module */
+ for (i = 0; i < ov9640_regs_len; i++) {
+ val = ov9640_regs[i].val;
+
+ switch (ov9640_regs[i].reg) {
+ case OV9640_COM7:
+ val |= alts->com7;
+ break;
+ case OV9640_COM12:
+ val |= alts->com12;
+ break;
+ case OV9640_COM13:
+ val |= alts->com13;
+ break;
+ case OV9640_COM15:
+ val |= alts->com15;
+ break;
+ }
+
+ ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
+ if (ret)
+ return ret;
+ }
+
+ /* write color matrix configuration into the module */
+ for (i = 0; i < matrix_regs_len; i++) {
+ ret = ov9640_reg_write(client, matrix_regs[i].reg,
+ matrix_regs[i].val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* program default register values */
+static int ov9640_prog_dflt(struct i2c_client *client)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
+ ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
+ ov9640_regs_dflt[i].val);
+ if (ret)
+ return ret;
+ }
+
+ /* wait for the changes to actually happen, 140ms are not enough yet */
+ mdelay(150);
+
+ return 0;
+}
+
+/* set the format we will capture in */
+static int ov9640_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct ov9640_reg_alt alts = {0};
+ enum v4l2_colorspace cspace;
+ enum v4l2_mbus_pixelcode code = mf->code;
+ int ret;
+
+ ov9640_res_roundup(&mf->width, &mf->height);
+ ov9640_alter_regs(mf->code, &alts);
+
+ ov9640_reset(client);
+
+ ret = ov9640_prog_dflt(client);
+ if (ret)
+ return ret;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ cspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ cspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ ret = ov9640_write_regs(client, mf->width, code, &alts);
+ if (!ret) {
+ mf->code = code;
+ mf->colorspace = cspace;
+ }
+
+ return ret;
+}
+
+static int ov9640_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ ov9640_res_roundup(&mf->width, &mf->height);
+
+ mf->field = V4L2_FIELD_NONE;
+
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ mf->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ default:
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ }
+
+ return 0;
+}
+
+static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(ov9640_codes))
+ return -EINVAL;
+
+ *code = ov9640_codes[index];
+ return 0;
+}
+
+static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = W_SXGA;
+ a->c.height = H_SXGA;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = W_SXGA;
+ a->bounds.height = H_SXGA;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int ov9640_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+ u8 pid, ver, midh, midl;
+ const char *devname;
+ int ret;
+
+ ret = ov9640_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+
+ ret = ov9640_reg_read(client, OV9640_PID, &pid);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_VER, &ver);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
+ if (!ret)
+ ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
+ if (ret)
+ goto done;
+
+ switch (VERSION(pid, ver)) {
+ case OV9640_V2:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 2;
+ break;
+ case OV9640_V3:
+ devname = "ov9640";
+ priv->model = V4L2_IDENT_OV9640;
+ priv->revision = 3;
+ break;
+ default:
+ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
+ devname, pid, ver, midh, midl);
+
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov9640_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
+ .s_ctrl = ov9640_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops ov9640_core_ops = {
+ .g_chip_ident = ov9640_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9640_get_register,
+ .s_register = ov9640_set_register,
+#endif
+ .s_power = ov9640_s_power,
+};
+
+/* Request bus settings on camera side */
+static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops ov9640_video_ops = {
+ .s_stream = ov9640_s_stream,
+ .s_mbus_fmt = ov9640_s_fmt,
+ .try_mbus_fmt = ov9640_try_fmt,
+ .enum_mbus_fmt = ov9640_enum_fmt,
+ .cropcap = ov9640_cropcap,
+ .g_crop = ov9640_g_crop,
+ .g_mbus_config = ov9640_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops ov9640_subdev_ops = {
+ .core = &ov9640_core_ops,
+ .video = &ov9640_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9640_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9640_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl) {
+ dev_err(&client->dev, "Missing platform_data for driver\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&client->dev,
+ "Failed to allocate memory for private data!\n");
+ return -ENOMEM;
+ }
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
+
+ v4l2_ctrl_handler_init(&priv->hdl, 2);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov9640_video_probe(client);
+
+ if (ret) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9640_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9640_priv *priv = to_ov9640_sensor(sd);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id ov9640_id[] = {
+ { "ov9640", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ov9640_id);
+
+static struct i2c_driver ov9640_i2c_driver = {
+ .driver = {
+ .name = "ov9640",
+ },
+ .probe = ov9640_probe,
+ .remove = ov9640_remove,
+ .id_table = ov9640_id,
+};
+
+module_i2c_driver(ov9640_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h
index 6b33a972c83c..6b33a972c83c 100644
--- a/drivers/media/video/ov9640.h
+++ b/drivers/media/i2c/soc_camera/ov9640.h
diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c
new file mode 100644
index 000000000000..7a55889e397b
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/ov9740.c
@@ -0,0 +1,1020 @@
+/*
+ * 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 <linux/v4l2-mediabus.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.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_CTRL15 0x3615
+#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
+
+#define OV9740_MAX_WIDTH 1280
+#define OV9740_MAX_HEIGHT 720
+
+/* Misc. structures */
+struct ov9740_reg {
+ u16 reg;
+ u8 val;
+};
+
+struct ov9740_priv {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+
+ int ident;
+ u16 model;
+ u8 revision;
+ u8 manid;
+ u8 smiaver;
+
+ bool flag_vflip;
+ bool flag_hflip;
+
+ /* For suspend/resume. */
+ struct v4l2_mbus_framefmt current_mf;
+ bool current_enable;
+};
+
+static const struct ov9740_reg ov9740_defaults[] = {
+ /* Software Reset */
+ { OV9740_SOFTWARE_RESET, 0x01 },
+
+ /* 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 },
+ { OV9740_ANALOG_CTRL15, 0xf0 },
+
+ /* 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 }, /* 0x64 for discontinuous clk */
+ { 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 },
+
+ /* YUYV order */
+ { OV9740_ISP_CTRL19, 0x02 },
+};
+
+static enum v4l2_mbus_pixelcode ov9740_codes[] = {
+ V4L2_MBUS_FMT_YUYV8_2X8,
+};
+
+/* 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 0x%04x 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 0x%04x 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);
+ }
+
+ priv->current_enable = enable;
+
+ return ret;
+}
+
+/* select nearest higher resolution for capture */
+static void ov9740_res_roundup(u32 *width, u32 *height)
+{
+ /* Width must be a multiple of 4 pixels. */
+ *width = ALIGN(*width, 4);
+
+ /* Max resolution is 1280x720 (720p). */
+ if (*width > OV9740_MAX_WIDTH)
+ *width = OV9740_MAX_WIDTH;
+
+ if (*height > OV9740_MAX_HEIGHT)
+ *height = OV9740_MAX_HEIGHT;
+}
+
+/* Setup registers according to resolution and color encoding */
+static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
+{
+ u32 x_start;
+ u32 y_start;
+ u32 x_end;
+ u32 y_end;
+ bool scaling = 0;
+ u32 scale_input_x;
+ u32 scale_input_y;
+ int ret;
+
+ if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
+ scaling = 1;
+
+ /*
+ * Try to use as much of the sensor area as possible when supporting
+ * smaller resolutions. Depending on the aspect ratio of the
+ * chosen resolution, we can either use the full width of the sensor,
+ * or the full height of the sensor (or both if the aspect ratio is
+ * the same as 1280x720.
+ */
+ if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) {
+ scale_input_x = (OV9740_MAX_HEIGHT * width) / height;
+ scale_input_y = OV9740_MAX_HEIGHT;
+ } else {
+ scale_input_x = OV9740_MAX_WIDTH;
+ scale_input_y = (OV9740_MAX_WIDTH * height) / width;
+ }
+
+ /* These describe the area of the sensor to use. */
+ x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2;
+ y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2;
+ x_end = x_start + scale_input_x - 1;
+ y_end = y_start + scale_input_y - 1;
+
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI,
+ (scale_input_x - width) >> 8);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO,
+ (scale_input_x - width) & 0xff);
+ if (ret)
+ goto done;
+
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff);
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef |
+ (scaling << 4));
+ if (ret)
+ goto done;
+ ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff);
+
+done:
+ 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);
+ struct ov9740_priv *priv = to_ov9740(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, mf->height);
+ if (ret < 0)
+ return ret;
+
+ mf->code = code;
+ mf->colorspace = cspace;
+
+ memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt));
+
+ 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_MAX_WIDTH;
+ a->bounds.height = OV9740_MAX_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_MAX_WIDTH;
+ a->c.height = OV9740_MAX_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+/* Set status of additional camera capabilities */
+static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov9740_priv *priv =
+ container_of(ctrl->handler, struct ov9740_priv, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ priv->flag_vflip = ctrl->val;
+ break;
+ case V4L2_CID_HFLIP:
+ priv->flag_hflip = ctrl->val;
+ 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;
+}
+
+static int ov9740_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ int ret;
+
+ if (on) {
+ ret = soc_camera_power_on(&client->dev, icl);
+ if (ret < 0)
+ return ret;
+
+ if (priv->current_enable) {
+ ov9740_s_fmt(sd, &priv->current_mf);
+ ov9740_s_stream(sd, 1);
+ }
+ } else {
+ if (priv->current_enable) {
+ ov9740_s_stream(sd, 0);
+ priv->current_enable = true;
+ }
+
+ soc_camera_power_off(&client->dev, icl);
+ }
+
+ 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
+
+static int ov9740_video_probe(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov9740_priv *priv = to_ov9740(sd);
+ u8 modelhi, modello;
+ int ret;
+
+ ret = ov9740_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show product ID and manufacturer ID
+ */
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi);
+ if (ret < 0)
+ goto done;
+
+ ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello);
+ if (ret < 0)
+ goto done;
+
+ priv->model = (modelhi << 8) | modello;
+
+ ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision);
+ if (ret < 0)
+ goto done;
+
+ ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid);
+ if (ret < 0)
+ goto done;
+
+ ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver);
+ if (ret < 0)
+ goto done;
+
+ if (priv->model != 0x9740) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ 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);
+
+ ret = v4l2_ctrl_handler_setup(&priv->hdl);
+
+done:
+ ov9740_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+/* Request bus settings on camera side */
+static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+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,
+ .g_mbus_config = ov9740_g_mbus_config,
+};
+
+static struct v4l2_subdev_core_ops ov9740_core_ops = {
+ .g_chip_ident = ov9740_g_chip_ident,
+ .s_power = ov9740_s_power,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = ov9740_get_register,
+ .s_register = ov9740_set_register,
+#endif
+};
+
+static struct v4l2_subdev_ops ov9740_subdev_ops = {
+ .core = &ov9740_core_ops,
+ .video = &ov9740_video_ops,
+};
+
+static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
+ .s_ctrl = ov9740_s_ctrl,
+};
+
+/*
+ * i2c_driver function
+ */
+static int ov9740_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct ov9740_priv *priv;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ 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);
+ v4l2_ctrl_handler_init(&priv->hdl, 13);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ priv->subdev.ctrl_handler = &priv->hdl;
+ if (priv->hdl.error) {
+ int err = priv->hdl.error;
+
+ kfree(priv);
+ return err;
+ }
+
+ ret = ov9740_video_probe(client);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&priv->hdl);
+ kfree(priv);
+ }
+
+ return ret;
+}
+
+static int ov9740_remove(struct i2c_client *client)
+{
+ struct ov9740_priv *priv = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(&priv->subdev);
+ v4l2_ctrl_handler_free(&priv->hdl);
+ 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,
+};
+
+module_i2c_driver(ov9740_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
+MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
new file mode 100644
index 000000000000..02f0400051d9
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c
@@ -0,0 +1,1431 @@
+/*
+ * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp
+ *
+ * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/rj54n1cb0c.h>
+#include <media/soc_camera.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#define RJ54N1_DEV_CODE 0x0400
+#define RJ54N1_DEV_CODE2 0x0401
+#define RJ54N1_OUT_SEL 0x0403
+#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404
+#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405
+#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406
+#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407
+#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408
+#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409
+#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a
+#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b
+#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c
+#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d
+#define RJ54N1_RESIZE_N 0x040e
+#define RJ54N1_RESIZE_N_STEP 0x040f
+#define RJ54N1_RESIZE_STEP 0x0410
+#define RJ54N1_RESIZE_HOLD_H 0x0411
+#define RJ54N1_RESIZE_HOLD_L 0x0412
+#define RJ54N1_H_OBEN_OFS 0x0413
+#define RJ54N1_V_OBEN_OFS 0x0414
+#define RJ54N1_RESIZE_CONTROL 0x0415
+#define RJ54N1_STILL_CONTROL 0x0417
+#define RJ54N1_INC_USE_SEL_H 0x0425
+#define RJ54N1_INC_USE_SEL_L 0x0426
+#define RJ54N1_MIRROR_STILL_MODE 0x0427
+#define RJ54N1_INIT_START 0x0428
+#define RJ54N1_SCALE_1_2_LEV 0x0429
+#define RJ54N1_SCALE_4_LEV 0x042a
+#define RJ54N1_Y_GAIN 0x04d8
+#define RJ54N1_APT_GAIN_UP 0x04fa
+#define RJ54N1_RA_SEL_UL 0x0530
+#define RJ54N1_BYTE_SWAP 0x0531
+#define RJ54N1_OUT_SIGPO 0x053b
+#define RJ54N1_WB_SEL_WEIGHT_I 0x054e
+#define RJ54N1_BIT8_WB 0x0569
+#define RJ54N1_HCAPS_WB 0x056a
+#define RJ54N1_VCAPS_WB 0x056b
+#define RJ54N1_HCAPE_WB 0x056c
+#define RJ54N1_VCAPE_WB 0x056d
+#define RJ54N1_EXPOSURE_CONTROL 0x058c
+#define RJ54N1_FRAME_LENGTH_S_H 0x0595
+#define RJ54N1_FRAME_LENGTH_S_L 0x0596
+#define RJ54N1_FRAME_LENGTH_P_H 0x0597
+#define RJ54N1_FRAME_LENGTH_P_L 0x0598
+#define RJ54N1_PEAK_H 0x05b7
+#define RJ54N1_PEAK_50 0x05b8
+#define RJ54N1_PEAK_60 0x05b9
+#define RJ54N1_PEAK_DIFF 0x05ba
+#define RJ54N1_IOC 0x05ef
+#define RJ54N1_TG_BYPASS 0x0700
+#define RJ54N1_PLL_L 0x0701
+#define RJ54N1_PLL_N 0x0702
+#define RJ54N1_PLL_EN 0x0704
+#define RJ54N1_RATIO_TG 0x0706
+#define RJ54N1_RATIO_T 0x0707
+#define RJ54N1_RATIO_R 0x0708
+#define RJ54N1_RAMP_TGCLK_EN 0x0709
+#define RJ54N1_OCLK_DSP 0x0710
+#define RJ54N1_RATIO_OP 0x0711
+#define RJ54N1_RATIO_O 0x0712
+#define RJ54N1_OCLK_SEL_EN 0x0713
+#define RJ54N1_CLK_RST 0x0717
+#define RJ54N1_RESET_STANDBY 0x0718
+#define RJ54N1_FWFLG 0x07fe
+
+#define E_EXCLK (1 << 7)
+#define SOFT_STDBY (1 << 4)
+#define SEN_RSTX (1 << 2)
+#define TG_RSTX (1 << 1)
+#define DSP_RSTX (1 << 0)
+
+#define RESIZE_HOLD_SEL (1 << 2)
+#define RESIZE_GO (1 << 1)
+
+/*
+ * When cropping, the camera automatically centers the cropped region, there
+ * doesn't seem to be a way to specify an explicit location of the rectangle.
+ */
+#define RJ54N1_COLUMN_SKIP 0
+#define RJ54N1_ROW_SKIP 0
+#define RJ54N1_MAX_WIDTH 1600
+#define RJ54N1_MAX_HEIGHT 1200
+
+#define PLL_L 2
+#define PLL_N 0x31
+
+/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
+
+/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
+struct rj54n1_datafmt {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_colorspace colorspace;
+};
+
+/* Find a data format by a pixel code in an array */
+static const struct rj54n1_datafmt *rj54n1_find_datafmt(
+ enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt,
+ int n)
+{
+ int i;
+ for (i = 0; i < n; i++)
+ if (fmt[i].code == code)
+ return fmt + i;
+
+ return NULL;
+}
+
+static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
+ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
+ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
+ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
+};
+
+struct rj54n1_clock_div {
+ u8 ratio_tg; /* can be 0 or an odd number */
+ u8 ratio_t;
+ u8 ratio_r;
+ u8 ratio_op;
+ u8 ratio_o;
+};
+
+struct rj54n1 {
+ struct v4l2_subdev subdev;
+ struct v4l2_ctrl_handler hdl;
+ struct rj54n1_clock_div clk_div;
+ const struct rj54n1_datafmt *fmt;
+ struct v4l2_rect rect; /* Sensor window */
+ unsigned int tgclk_mhz;
+ bool auto_wb;
+ unsigned short width; /* Output window */
+ unsigned short height;
+ unsigned short resize; /* Sensor * 1024 / resize = Output */
+ unsigned short scale;
+ u8 bank;
+};
+
+struct rj54n1_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+static const struct rj54n1_reg_val bank_4[] = {
+ {0x417, 0},
+ {0x42c, 0},
+ {0x42d, 0xf0},
+ {0x42e, 0},
+ {0x42f, 0x50},
+ {0x430, 0xf5},
+ {0x431, 0x16},
+ {0x432, 0x20},
+ {0x433, 0},
+ {0x434, 0xc8},
+ {0x43c, 8},
+ {0x43e, 0x90},
+ {0x445, 0x83},
+ {0x4ba, 0x58},
+ {0x4bb, 4},
+ {0x4bc, 0x20},
+ {0x4db, 4},
+ {0x4fe, 2},
+};
+
+static const struct rj54n1_reg_val bank_5[] = {
+ {0x514, 0},
+ {0x516, 0},
+ {0x518, 0},
+ {0x51a, 0},
+ {0x51d, 0xff},
+ {0x56f, 0x28},
+ {0x575, 0x40},
+ {0x5bc, 0x48},
+ {0x5c1, 6},
+ {0x5e5, 0x11},
+ {0x5e6, 0x43},
+ {0x5e7, 0x33},
+ {0x5e8, 0x21},
+ {0x5e9, 0x30},
+ {0x5ea, 0x0},
+ {0x5eb, 0xa5},
+ {0x5ec, 0xff},
+ {0x5fe, 2},
+};
+
+static const struct rj54n1_reg_val bank_7[] = {
+ {0x70a, 0},
+ {0x714, 0xff},
+ {0x715, 0xff},
+ {0x716, 0x1f},
+ {0x7FE, 2},
+};
+
+static const struct rj54n1_reg_val bank_8[] = {
+ {0x800, 0x00},
+ {0x801, 0x01},
+ {0x802, 0x61},
+ {0x805, 0x00},
+ {0x806, 0x00},
+ {0x807, 0x00},
+ {0x808, 0x00},
+ {0x809, 0x01},
+ {0x80A, 0x61},
+ {0x80B, 0x00},
+ {0x80C, 0x01},
+ {0x80D, 0x00},
+ {0x80E, 0x00},
+ {0x80F, 0x00},
+ {0x810, 0x00},
+ {0x811, 0x01},
+ {0x812, 0x61},
+ {0x813, 0x00},
+ {0x814, 0x11},
+ {0x815, 0x00},
+ {0x816, 0x41},
+ {0x817, 0x00},
+ {0x818, 0x51},
+ {0x819, 0x01},
+ {0x81A, 0x1F},
+ {0x81B, 0x00},
+ {0x81C, 0x01},
+ {0x81D, 0x00},
+ {0x81E, 0x11},
+ {0x81F, 0x00},
+ {0x820, 0x41},
+ {0x821, 0x00},
+ {0x822, 0x51},
+ {0x823, 0x00},
+ {0x824, 0x00},
+ {0x825, 0x00},
+ {0x826, 0x47},
+ {0x827, 0x01},
+ {0x828, 0x4F},
+ {0x829, 0x00},
+ {0x82A, 0x00},
+ {0x82B, 0x00},
+ {0x82C, 0x30},
+ {0x82D, 0x00},
+ {0x82E, 0x40},
+ {0x82F, 0x00},
+ {0x830, 0xB3},
+ {0x831, 0x00},
+ {0x832, 0xE3},
+ {0x833, 0x00},
+ {0x834, 0x00},
+ {0x835, 0x00},
+ {0x836, 0x00},
+ {0x837, 0x00},
+ {0x838, 0x00},
+ {0x839, 0x01},
+ {0x83A, 0x61},
+ {0x83B, 0x00},
+ {0x83C, 0x01},
+ {0x83D, 0x00},
+ {0x83E, 0x00},
+ {0x83F, 0x00},
+ {0x840, 0x00},
+ {0x841, 0x01},
+ {0x842, 0x61},
+ {0x843, 0x00},
+ {0x844, 0x1D},
+ {0x845, 0x00},
+ {0x846, 0x00},
+ {0x847, 0x00},
+ {0x848, 0x00},
+ {0x849, 0x01},
+ {0x84A, 0x1F},
+ {0x84B, 0x00},
+ {0x84C, 0x05},
+ {0x84D, 0x00},
+ {0x84E, 0x19},
+ {0x84F, 0x01},
+ {0x850, 0x21},
+ {0x851, 0x01},
+ {0x852, 0x5D},
+ {0x853, 0x00},
+ {0x854, 0x00},
+ {0x855, 0x00},
+ {0x856, 0x19},
+ {0x857, 0x01},
+ {0x858, 0x21},
+ {0x859, 0x00},
+ {0x85A, 0x00},
+ {0x85B, 0x00},
+ {0x85C, 0x00},
+ {0x85D, 0x00},
+ {0x85E, 0x00},
+ {0x85F, 0x00},
+ {0x860, 0xB3},
+ {0x861, 0x00},
+ {0x862, 0xE3},
+ {0x863, 0x00},
+ {0x864, 0x00},
+ {0x865, 0x00},
+ {0x866, 0x00},
+ {0x867, 0x00},
+ {0x868, 0x00},
+ {0x869, 0xE2},
+ {0x86A, 0x00},
+ {0x86B, 0x01},
+ {0x86C, 0x06},
+ {0x86D, 0x00},
+ {0x86E, 0x00},
+ {0x86F, 0x00},
+ {0x870, 0x60},
+ {0x871, 0x8C},
+ {0x872, 0x10},
+ {0x873, 0x00},
+ {0x874, 0xE0},
+ {0x875, 0x00},
+ {0x876, 0x27},
+ {0x877, 0x01},
+ {0x878, 0x00},
+ {0x879, 0x00},
+ {0x87A, 0x00},
+ {0x87B, 0x03},
+ {0x87C, 0x00},
+ {0x87D, 0x00},
+ {0x87E, 0x00},
+ {0x87F, 0x00},
+ {0x880, 0x00},
+ {0x881, 0x00},
+ {0x882, 0x00},
+ {0x883, 0x00},
+ {0x884, 0x00},
+ {0x885, 0x00},
+ {0x886, 0xF8},
+ {0x887, 0x00},
+ {0x888, 0x03},
+ {0x889, 0x00},
+ {0x88A, 0x64},
+ {0x88B, 0x00},
+ {0x88C, 0x03},
+ {0x88D, 0x00},
+ {0x88E, 0xB1},
+ {0x88F, 0x00},
+ {0x890, 0x03},
+ {0x891, 0x01},
+ {0x892, 0x1D},
+ {0x893, 0x00},
+ {0x894, 0x03},
+ {0x895, 0x01},
+ {0x896, 0x4B},
+ {0x897, 0x00},
+ {0x898, 0xE5},
+ {0x899, 0x00},
+ {0x89A, 0x01},
+ {0x89B, 0x00},
+ {0x89C, 0x01},
+ {0x89D, 0x04},
+ {0x89E, 0xC8},
+ {0x89F, 0x00},
+ {0x8A0, 0x01},
+ {0x8A1, 0x01},
+ {0x8A2, 0x61},
+ {0x8A3, 0x00},
+ {0x8A4, 0x01},
+ {0x8A5, 0x00},
+ {0x8A6, 0x00},
+ {0x8A7, 0x00},
+ {0x8A8, 0x00},
+ {0x8A9, 0x00},
+ {0x8AA, 0x7F},
+ {0x8AB, 0x03},
+ {0x8AC, 0x00},
+ {0x8AD, 0x00},
+ {0x8AE, 0x00},
+ {0x8AF, 0x00},
+ {0x8B0, 0x00},
+ {0x8B1, 0x00},
+ {0x8B6, 0x00},
+ {0x8B7, 0x01},
+ {0x8B8, 0x00},
+ {0x8B9, 0x00},
+ {0x8BA, 0x02},
+ {0x8BB, 0x00},
+ {0x8BC, 0xFF},
+ {0x8BD, 0x00},
+ {0x8FE, 2},
+};
+
+static const struct rj54n1_reg_val bank_10[] = {
+ {0x10bf, 0x69}
+};
+
+/* Clock dividers - these are default register values, divider = register + 1 */
+static const struct rj54n1_clock_div clk_div = {
+ .ratio_tg = 3 /* default: 5 */,
+ .ratio_t = 4 /* default: 1 */,
+ .ratio_r = 4 /* default: 0 */,
+ .ratio_op = 1 /* default: 5 */,
+ .ratio_o = 9 /* default: 0 */,
+};
+
+static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
+}
+
+static int reg_read(struct i2c_client *client, const u16 reg)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ return i2c_smbus_read_byte_data(client, reg & 0xff);
+}
+
+static int reg_write(struct i2c_client *client, const u16 reg,
+ const u8 data)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* set bank */
+ if (rj54n1->bank != reg >> 8) {
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
+ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
+ if (ret < 0)
+ return ret;
+ rj54n1->bank = reg >> 8;
+ }
+ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
+ return i2c_smbus_write_byte_data(client, reg & 0xff, data);
+}
+
+static int reg_set(struct i2c_client *client, const u16 reg,
+ const u8 data, const u8 mask)
+{
+ int ret;
+
+ ret = reg_read(client, reg);
+ if (ret < 0)
+ return ret;
+ return reg_write(client, reg, (ret & ~mask) | (data & mask));
+}
+
+static int reg_write_multiple(struct i2c_client *client,
+ const struct rj54n1_reg_val *rv, const int n)
+{
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ ret = reg_write(client, rv->reg, rv->val);
+ if (ret < 0)
+ return ret;
+ rv++;
+ }
+
+ return 0;
+}
+
+static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(rj54n1_colour_fmts))
+ return -EINVAL;
+
+ *code = rj54n1_colour_fmts[index].code;
+ return 0;
+}
+
+static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* Switch between preview and still shot modes */
+ return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
+}
+
+static int rj54n1_set_rect(struct i2c_client *client,
+ u16 reg_x, u16 reg_y, u16 reg_xy,
+ u32 width, u32 height)
+{
+ int ret;
+
+ ret = reg_write(client, reg_xy,
+ ((width >> 4) & 0x70) |
+ ((height >> 8) & 7));
+
+ if (!ret)
+ ret = reg_write(client, reg_x, width & 0xff);
+ if (!ret)
+ ret = reg_write(client, reg_y, height & 0xff);
+
+ return ret;
+}
+
+/*
+ * Some commands, specifically certain initialisation sequences, require
+ * a commit operation.
+ */
+static int rj54n1_commit(struct i2c_client *client)
+{
+ int ret = reg_write(client, RJ54N1_INIT_START, 1);
+ msleep(10);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INIT_START, 0);
+ return ret;
+}
+
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
+ s32 *out_w, s32 *out_h);
+
+static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct v4l2_rect *rect = &a->c;
+ int dummy = 0, output_w, output_h,
+ input_w = rect->width, input_h = rect->height;
+ int ret;
+
+ /* arbitrary minimum width and height, edges unimportant */
+ soc_camera_limit_side(&dummy, &input_w,
+ RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH);
+
+ soc_camera_limit_side(&dummy, &input_h,
+ RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT);
+
+ output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+ output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize;
+
+ dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n",
+ input_w, input_h, rj54n1->resize, output_w, output_h);
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+
+ return 0;
+}
+
+static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+ a->c = rj54n1->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = RJ54N1_COLUMN_SKIP;
+ a->bounds.top = RJ54N1_ROW_SKIP;
+ a->bounds.width = RJ54N1_MAX_WIDTH;
+ a->bounds.height = RJ54N1_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int rj54n1_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+
+ mf->code = rj54n1->fmt->code;
+ mf->colorspace = rj54n1->fmt->colorspace;
+ mf->field = V4L2_FIELD_NONE;
+ mf->width = rj54n1->width;
+ mf->height = rj54n1->height;
+
+ return 0;
+}
+
+/*
+ * The actual geometry configuration routine. It scales the input window into
+ * the output one, updates the window sizes and returns an error or the resize
+ * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
+ */
+static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
+ s32 *out_w, s32 *out_h)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
+ output_w = *out_w, output_h = *out_h;
+ u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom;
+ unsigned int peak, peak_50, peak_60;
+ int ret;
+
+ /*
+ * We have a problem with crops, where the window is larger than 512x384
+ * and output window is larger than a half of the input one. In this
+ * case we have to either reduce the input window to equal or below
+ * 512x384 or the output window to equal or below 1/2 of the input.
+ */
+ if (output_w > max(512U, input_w / 2)) {
+ if (2 * output_w > RJ54N1_MAX_WIDTH) {
+ input_w = RJ54N1_MAX_WIDTH;
+ output_w = RJ54N1_MAX_WIDTH / 2;
+ } else {
+ input_w = output_w * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
+ input_w, output_w);
+ }
+
+ if (output_h > max(384U, input_h / 2)) {
+ if (2 * output_h > RJ54N1_MAX_HEIGHT) {
+ input_h = RJ54N1_MAX_HEIGHT;
+ output_h = RJ54N1_MAX_HEIGHT / 2;
+ } else {
+ input_h = output_h * 2;
+ }
+
+ dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
+ input_h, output_h);
+ }
+
+ /* Idea: use the read mode for snapshots, handle separate geometries */
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
+ RJ54N1_Y_OUTPUT_SIZE_S_L,
+ RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
+ if (!ret)
+ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
+ RJ54N1_Y_OUTPUT_SIZE_P_L,
+ RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
+
+ if (ret < 0)
+ return ret;
+
+ if (output_w > input_w && output_h > input_h) {
+ input_w = output_w;
+ input_h = output_h;
+
+ resize = 1024;
+ } else {
+ unsigned int resize_x, resize_y;
+ resize_x = (input_w * 1024 + output_w / 2) / output_w;
+ resize_y = (input_h * 1024 + output_h / 2) / output_h;
+
+ /* We want max(resize_x, resize_y), check if it still fits */
+ if (resize_x > resize_y &&
+ (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT)
+ resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) /
+ output_h;
+ else if (resize_y > resize_x &&
+ (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH)
+ resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) /
+ output_w;
+ else
+ resize = max(resize_x, resize_y);
+
+ /* Prohibited value ranges */
+ switch (resize) {
+ case 2040 ... 2047:
+ resize = 2039;
+ break;
+ case 4080 ... 4095:
+ resize = 4079;
+ break;
+ case 8160 ... 8191:
+ resize = 8159;
+ break;
+ case 16320 ... 16384:
+ resize = 16319;
+ }
+ }
+
+ /* Set scaling */
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
+
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Configure a skipping bitmask. The sensor will select a skipping value
+ * among set bits automatically. This is very unclear in the datasheet
+ * too. I was told, in this register one enables all skipping values,
+ * that are required for a specific resize, and the camera selects
+ * automatically, which ones to use. But it is unclear how to identify,
+ * which cropping values are needed. Secondly, why don't we just set all
+ * bits and let the camera choose? Would it increase processing time and
+ * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to
+ * improve the image quality or stability for larger frames (see comment
+ * above), but I didn't check the framerate.
+ */
+ skip = min(resize / 1024, 15U);
+
+ inc_sel = 1 << skip;
+
+ if (inc_sel <= 2)
+ inc_sel = 0xc;
+ else if (resize & 1023 && skip < 15)
+ inc_sel |= 1 << (skip + 1);
+
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
+
+ if (!rj54n1->auto_wb) {
+ /* Auto white balance window */
+ wb_left = output_w / 16;
+ wb_right = (3 * output_w / 4 - 3) / 4;
+ wb_top = output_h / 16;
+ wb_bottom = (3 * output_h / 4 - 3) / 4;
+ wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) |
+ ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
+ }
+
+ /* Antiflicker */
+ peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz /
+ 10000;
+ peak_50 = peak / 6;
+ peak_60 = peak / 5;
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_H,
+ ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_50, peak_50);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_60, peak_60);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
+
+ /* Start resizing */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | RESIZE_GO | 1);
+
+ if (ret < 0)
+ return ret;
+
+ /* Constant taken from manufacturer's example */
+ msleep(230);
+
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
+ if (ret < 0)
+ return ret;
+
+ *in_w = (output_w * resize + 512) / 1024;
+ *in_h = (output_h * resize + 512) / 1024;
+ *out_w = output_w;
+ *out_h = output_h;
+
+ dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n",
+ *in_w, *in_h, resize, output_w, output_h, skip);
+
+ return resize;
+}
+
+static int rj54n1_set_clock(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret;
+
+ /* Enable external clock */
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
+ /* Leave stand-by. Note: use this when implementing suspend / resume */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_L, PLL_L);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
+
+ /* TGCLK dividers */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_TG,
+ rj54n1->clk_div.ratio_tg);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_T,
+ rj54n1->clk_div.ratio_t);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_R,
+ rj54n1->clk_div.ratio_r);
+
+ /* Enable TGCLK & RAMP */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
+
+ /* Disable clock output */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
+
+ /* Set divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_OP,
+ rj54n1->clk_div.ratio_op);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RATIO_O,
+ rj54n1->clk_div.ratio_o);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ /* Use PLL for Timing Generator, write 2 to reserved bits */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
+
+ /* Take sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | SEN_RSTX);
+ /* Enable PLL */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_PLL_EN, 1);
+
+ /* Wait for PLL to stabilise */
+ msleep(10);
+
+ /* Enable clock to frequency divider */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_CLK_RST, 1);
+
+ if (!ret)
+ ret = reg_read(client, RJ54N1_CLK_RST);
+ if (ret != 1) {
+ dev_err(&client->dev,
+ "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
+ return -EIO;
+ }
+
+ /* Start the PLL */
+ ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
+
+ /* Enable OCLK */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
+
+ return ret;
+}
+
+static int rj54n1_reg_init(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int ret = rj54n1_set_clock(client);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
+ if (!ret)
+ ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
+
+ /* Set binning divisors */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
+ if (!ret)
+ ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
+
+ /* Switch to fixed resize mode */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
+ RESIZE_HOLD_SEL | 1);
+
+ /* Set gain */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
+
+ /*
+ * Mirror the image back: default is upside down and left-to-right...
+ * Set manual preview / still shot switching
+ */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
+
+ if (!ret)
+ ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
+
+ /* Auto exposure area */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80);
+ /* Check current auto WB config */
+ if (!ret)
+ ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I);
+ if (ret >= 0) {
+ rj54n1->auto_wb = ret & 0x80;
+ ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
+ }
+ if (!ret)
+ ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
+
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | SEN_RSTX);
+
+ /* Commit init */
+ if (!ret)
+ ret = rj54n1_commit(client);
+
+ /* Take DSP, TG, sensor out of reset */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RESET_STANDBY,
+ E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
+
+ /* Start register update? Same register as 0x?FE in many bank_* sets */
+ if (!ret)
+ ret = reg_write(client, RJ54N1_FWFLG, 2);
+
+ /* Constant taken from manufacturer's example */
+ msleep(700);
+
+ return ret;
+}
+
+static int rj54n1_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct rj54n1_datafmt *fmt;
+ int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ||
+ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE;
+
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+ if (!fmt) {
+ fmt = rj54n1->fmt;
+ mf->code = fmt->code;
+ }
+
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
+
+ v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align,
+ &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0);
+
+ return 0;
+}
+
+static int rj54n1_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ const struct rj54n1_datafmt *fmt;
+ int output_w, output_h, max_w, max_h,
+ input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
+ int ret;
+
+ /*
+ * The host driver can call us without .try_fmt(), so, we have to take
+ * care ourseleves
+ */
+ rj54n1_try_fmt(sd, mf);
+
+ /*
+ * Verify if the sensor has just been powered on. TODO: replace this
+ * with proper PM, when a suitable API is available.
+ */
+ ret = reg_read(client, RJ54N1_RESET_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & E_EXCLK)) {
+ ret = rj54n1_reg_init(client);
+ if (ret < 0)
+ return ret;
+ }
+
+ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
+ __func__, mf->code, mf->width, mf->height);
+
+ /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
+ switch (mf->code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 4);
+ if (!ret)
+ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
+ if (!ret)
+ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ ret = reg_write(client, RJ54N1_OUT_SEL, 5);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ /* Special case: a raw mode with 10 bits of data per clock tick */
+ if (!ret)
+ ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
+ (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2);
+
+ if (ret < 0)
+ return ret;
+
+ /* Supported scales 1:1 >= scale > 1:16 */
+ max_w = mf->width * (16 * 1024 - 1) / 1024;
+ if (input_w > max_w)
+ input_w = max_w;
+ max_h = mf->height * (16 * 1024 - 1) / 1024;
+ if (input_h > max_h)
+ input_h = max_h;
+
+ output_w = mf->width;
+ output_h = mf->height;
+
+ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
+ if (ret < 0)
+ return ret;
+
+ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
+ ARRAY_SIZE(rj54n1_colour_fmts));
+
+ rj54n1->fmt = fmt;
+ rj54n1->resize = ret;
+ rj54n1->rect.width = input_w;
+ rj54n1->rect.height = input_h;
+ rj54n1->width = output_w;
+ rj54n1->height = output_h;
+
+ mf->width = output_w;
+ mf->height = output_h;
+ mf->field = V4L2_FIELD_NONE;
+ mf->colorspace = fmt->colorspace;
+
+ return 0;
+}
+
+static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ if (id->match.addr != client->addr)
+ return -ENODEV;
+
+ id->ident = V4L2_IDENT_RJ54N1CB0C;
+ id->revision = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int rj54n1_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers > 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ reg->size = 1;
+ reg->val = reg_read(client, reg->reg);
+
+ if (reg->val > 0xff)
+ return -EIO;
+
+ return 0;
+}
+
+static int rj54n1_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
+ reg->reg < 0x400 || reg->reg > 0x1fff)
+ /* Registers >= 0x0800 are only available from Sharp support */
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ if (reg_write(client, reg->reg, reg->val) < 0)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
+static int rj54n1_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
+ struct v4l2_subdev *sd = &rj54n1->subdev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int data;
+
+ switch (ctrl->id) {
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
+ else
+ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
+ if (data < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_GAIN:
+ if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
+ return -EIO;
+ return 0;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ /* Auto WB area - whole image */
+ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
+ 0x80) < 0)
+ return -EIO;
+ rj54n1->auto_wb = ctrl->val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
+ .s_ctrl = rj54n1_s_ctrl,
+};
+
+static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
+ .g_chip_ident = rj54n1_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = rj54n1_g_register,
+ .s_register = rj54n1_s_register,
+#endif
+ .s_power = rj54n1_s_power,
+};
+
+static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags =
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
+ if (soc_camera_apply_board_flags(icl, cfg) &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
+ else
+ return reg_write(client, RJ54N1_OUT_SIGPO, 0);
+}
+
+static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
+ .s_stream = rj54n1_s_stream,
+ .s_mbus_fmt = rj54n1_s_fmt,
+ .g_mbus_fmt = rj54n1_g_fmt,
+ .try_mbus_fmt = rj54n1_try_fmt,
+ .enum_mbus_fmt = rj54n1_enum_fmt,
+ .g_crop = rj54n1_g_crop,
+ .s_crop = rj54n1_s_crop,
+ .cropcap = rj54n1_cropcap,
+ .g_mbus_config = rj54n1_g_mbus_config,
+ .s_mbus_config = rj54n1_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops rj54n1_subdev_ops = {
+ .core = &rj54n1_subdev_core_ops,
+ .video = &rj54n1_subdev_video_ops,
+};
+
+/*
+ * Interface active, can use i2c. If it fails, it can indeed mean, that
+ * this wasn't our capture interface, so, we wait for the right one
+ */
+static int rj54n1_video_probe(struct i2c_client *client,
+ struct rj54n1_pdata *priv)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ int data1, data2;
+ int ret;
+
+ ret = rj54n1_s_power(&rj54n1->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Read out the chip version register */
+ data1 = reg_read(client, RJ54N1_DEV_CODE);
+ data2 = reg_read(client, RJ54N1_DEV_CODE2);
+
+ if (data1 != 0x51 || data2 != 0x10) {
+ ret = -ENODEV;
+ dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
+ data1, data2);
+ goto done;
+ }
+
+ /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
+ ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7);
+ if (ret < 0)
+ goto done;
+
+ dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
+ data1, data2);
+
+ ret = v4l2_ctrl_handler_setup(&rj54n1->hdl);
+
+done:
+ rj54n1_s_power(&rj54n1->subdev, 0);
+ return ret;
+}
+
+static int rj54n1_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct rj54n1 *rj54n1;
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct rj54n1_pdata *rj54n1_priv;
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ rj54n1_priv = icl->priv;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_warn(&adapter->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
+ return -EIO;
+ }
+
+ rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL);
+ if (!rj54n1)
+ return -ENOMEM;
+
+ v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
+ v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 66);
+ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+ rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
+ if (rj54n1->hdl.error) {
+ int err = rj54n1->hdl.error;
+
+ kfree(rj54n1);
+ return err;
+ }
+
+ rj54n1->clk_div = clk_div;
+ rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
+ rj54n1->rect.top = RJ54N1_ROW_SKIP;
+ rj54n1->rect.width = RJ54N1_MAX_WIDTH;
+ rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
+ rj54n1->width = RJ54N1_MAX_WIDTH;
+ rj54n1->height = RJ54N1_MAX_HEIGHT;
+ rj54n1->fmt = &rj54n1_colour_fmts[0];
+ rj54n1->resize = 1024;
+ rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
+ (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
+
+ ret = rj54n1_video_probe(client, rj54n1_priv);
+ if (ret < 0) {
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
+ kfree(rj54n1);
+ }
+
+ return ret;
+}
+
+static int rj54n1_remove(struct i2c_client *client)
+{
+ struct rj54n1 *rj54n1 = to_rj54n1(client);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ v4l2_device_unregister_subdev(&rj54n1->subdev);
+ if (icl->free_bus)
+ icl->free_bus(icl);
+ v4l2_ctrl_handler_free(&rj54n1->hdl);
+ kfree(rj54n1);
+
+ return 0;
+}
+
+static const struct i2c_device_id rj54n1_id[] = {
+ { "rj54n1cb0c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rj54n1_id);
+
+static struct i2c_driver rj54n1_i2c_driver = {
+ .driver = {
+ .name = "rj54n1cb0c",
+ },
+ .probe = rj54n1_probe,
+ .remove = rj54n1_remove,
+ .id_table = rj54n1_id,
+};
+
+module_i2c_driver(rj54n1_i2c_driver);
+
+MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c
new file mode 100644
index 000000000000..140716e71a15
--- /dev/null
+++ b/drivers/media/i2c/soc_camera/tw9910.c
@@ -0,0 +1,973 @@
+/*
+ * tw9910 Video Driver
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Based on ov772x driver,
+ *
+ * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
+ * Copyright (C) 2008 Magnus Damm
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/tw9910.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-subdev.h>
+
+#define GET_ID(val) ((val & 0xF8) >> 3)
+#define GET_REV(val) (val & 0x07)
+
+/*
+ * register offset
+ */
+#define ID 0x00 /* Product ID Code Register */
+#define STATUS1 0x01 /* Chip Status Register I */
+#define INFORM 0x02 /* Input Format */
+#define OPFORM 0x03 /* Output Format Control Register */
+#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
+#define OUTCTR1 0x05 /* Output Control I */
+#define ACNTL1 0x06 /* Analog Control Register 1 */
+#define CROP_HI 0x07 /* Cropping Register, High */
+#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
+#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
+#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
+#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
+#define CNTRL1 0x0C /* Control Register I */
+#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
+#define SCALE_HI 0x0E /* Scaling Register, High */
+#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
+#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
+#define CONTRAST 0x11 /* CONTRAST Control Register */
+#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
+#define SAT_U 0x13 /* Chroma (U) Gain Register */
+#define SAT_V 0x14 /* Chroma (V) Gain Register */
+#define HUE 0x15 /* Hue Control Register */
+#define CORING1 0x17
+#define CORING2 0x18 /* Coring and IF compensation */
+#define VBICNTL 0x19 /* VBI Control Register */
+#define ACNTL2 0x1A /* Analog Control 2 */
+#define OUTCTR2 0x1B /* Output Control 2 */
+#define SDT 0x1C /* Standard Selection */
+#define SDTR 0x1D /* Standard Recognition */
+#define TEST 0x1F /* Test Control Register */
+#define CLMPG 0x20 /* Clamping Gain */
+#define IAGC 0x21 /* Individual AGC Gain */
+#define AGCGAIN 0x22 /* AGC Gain */
+#define PEAKWT 0x23 /* White Peak Threshold */
+#define CLMPL 0x24 /* Clamp level */
+#define SYNCT 0x25 /* Sync Amplitude */
+#define MISSCNT 0x26 /* Sync Miss Count Register */
+#define PCLAMP 0x27 /* Clamp Position Register */
+#define VCNTL1 0x28 /* Vertical Control I */
+#define VCNTL2 0x29 /* Vertical Control II */
+#define CKILL 0x2A /* Color Killer Level Control */
+#define COMB 0x2B /* Comb Filter Control */
+#define LDLY 0x2C /* Luma Delay and H Filter Control */
+#define MISC1 0x2D /* Miscellaneous Control I */
+#define LOOP 0x2E /* LOOP Control Register */
+#define MISC2 0x2F /* Miscellaneous Control II */
+#define MVSN 0x30 /* Macrovision Detection */
+#define STATUS2 0x31 /* Chip STATUS II */
+#define HFREF 0x32 /* H monitor */
+#define CLMD 0x33 /* CLAMP MODE */
+#define IDCNTL 0x34 /* ID Detection Control */
+#define CLCNTL1 0x35 /* Clamp Control I */
+#define ANAPLLCTL 0x4C
+#define VBIMIN 0x4D
+#define HSLOWCTL 0x4E
+#define WSS3 0x4F
+#define FILLDATA 0x50
+#define SDID 0x51
+#define DID 0x52
+#define WSS1 0x53
+#define WSS2 0x54
+#define VVBI 0x55
+#define LCTL6 0x56
+#define LCTL7 0x57
+#define LCTL8 0x58
+#define LCTL9 0x59
+#define LCTL10 0x5A
+#define LCTL11 0x5B
+#define LCTL12 0x5C
+#define LCTL13 0x5D
+#define LCTL14 0x5E
+#define LCTL15 0x5F
+#define LCTL16 0x60
+#define LCTL17 0x61
+#define LCTL18 0x62
+#define LCTL19 0x63
+#define LCTL20 0x64
+#define LCTL21 0x65
+#define LCTL22 0x66
+#define LCTL23 0x67
+#define LCTL24 0x68
+#define LCTL25 0x69
+#define LCTL26 0x6A
+#define HSBEGIN 0x6B
+#define HSEND 0x6C
+#define OVSDLY 0x6D
+#define OVSEND 0x6E
+#define VBIDELAY 0x6F
+
+/*
+ * register detail
+ */
+
+/* INFORM */
+#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
+#define FC27_FF 0x00 /* 0 : Square pixel mode. */
+ /* Must use 24.54MHz for 60Hz field rate */
+ /* source or 29.5MHz for 50Hz field rate */
+#define IFSEL_S 0x10 /* 01 : S-video decoding */
+#define IFSEL_C 0x00 /* 00 : Composite video decoding */
+ /* Y input video selection */
+#define YSEL_M0 0x00 /* 00 : Mux0 selected */
+#define YSEL_M1 0x04 /* 01 : Mux1 selected */
+#define YSEL_M2 0x08 /* 10 : Mux2 selected */
+#define YSEL_M3 0x10 /* 11 : Mux3 selected */
+
+/* OPFORM */
+#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
+ /* 1 : ITU-R-656 compatible data sequence format */
+#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
+ /* 1 : 16-bit YCrCb 4:2:2 output format.*/
+#define LLCMODE 0x20 /* 1 : LLC output mode. */
+ /* 0 : free-run output mode */
+#define AINC 0x10 /* Serial interface auto-indexing control */
+ /* 0 : auto-increment */
+ /* 1 : non-auto */
+#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
+ /* 0 : Vertical out ctrl by HACTIVE and DVALID */
+#define OEN_TRI_SEL_MASK 0x07
+#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
+#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
+#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
+
+/* OUTCTR1 */
+#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
+#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
+ /* VS pin output control */
+#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
+#define VSSL_VACT 0x10 /* 1 : VACT */
+#define VSSL_FIELD 0x20 /* 2 : FIELD */
+#define VSSL_VVALID 0x30 /* 3 : VVALID */
+#define VSSL_ZERO 0x70 /* 7 : 0 */
+#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
+#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
+ /* HS pin output control */
+#define HSSL_HACT 0x00 /* 0 : HACT */
+#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
+#define HSSL_DVALID 0x02 /* 2 : DVALID */
+#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
+#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
+#define HSSL_ZERO 0x07 /* 7 : 0 */
+
+/* ACNTL1 */
+#define SRESET 0x80 /* resets the device to its default state
+ * but all register content remain unchanged.
+ * This bit is self-resetting.
+ */
+#define ACNTL1_PDN_MASK 0x0e
+#define CLK_PDN 0x08 /* system clock power down */
+#define Y_PDN 0x04 /* Luma ADC power down */
+#define C_PDN 0x02 /* Chroma ADC power down */
+
+/* ACNTL2 */
+#define ACNTL2_PDN_MASK 0x40
+#define PLL_PDN 0x40 /* PLL power down */
+
+/* VBICNTL */
+
+/* RTSEL : control the real time signal output from the MPOUT pin */
+#define RTSEL_MASK 0x07
+#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
+#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
+#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
+#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
+#define RTSEL_MONO 0x04 /* 0100 = MONO */
+#define RTSEL_DET50 0x05 /* 0101 = DET50 */
+#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
+#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
+
+/* HSYNC start and end are constant for now */
+#define HSYNC_START 0x0260
+#define HSYNC_END 0x0300
+
+/*
+ * structure
+ */
+
+struct regval_list {
+ unsigned char reg_num;
+ unsigned char value;
+};
+
+struct tw9910_scale_ctrl {
+ char *name;
+ unsigned short width;
+ unsigned short height;
+ u16 hscale;
+ u16 vscale;
+};
+
+struct tw9910_priv {
+ struct v4l2_subdev subdev;
+ struct tw9910_video_info *info;
+ const struct tw9910_scale_ctrl *scale;
+ v4l2_std_id norm;
+ u32 revision;
+};
+
+static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
+ {
+ .name = "NTSC SQ",
+ .width = 640,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC CCIR601",
+ .width = 720,
+ .height = 480,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "NTSC SQ (CIF)",
+ .width = 320,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC CCIR601 (CIF)",
+ .width = 360,
+ .height = 240,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "NTSC SQ (QCIF)",
+ .width = 160,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "NTSC CCIR601 (QCIF)",
+ .width = 180,
+ .height = 120,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
+ {
+ .name = "PAL SQ",
+ .width = 768,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL CCIR601",
+ .width = 720,
+ .height = 576,
+ .hscale = 0x0100,
+ .vscale = 0x0100,
+ },
+ {
+ .name = "PAL SQ (CIF)",
+ .width = 384,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL CCIR601 (CIF)",
+ .width = 360,
+ .height = 288,
+ .hscale = 0x0200,
+ .vscale = 0x0200,
+ },
+ {
+ .name = "PAL SQ (QCIF)",
+ .width = 192,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+ {
+ .name = "PAL CCIR601 (QCIF)",
+ .width = 180,
+ .height = 144,
+ .hscale = 0x0400,
+ .vscale = 0x0400,
+ },
+};
+
+/*
+ * general function
+ */
+static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct tw9910_priv,
+ subdev);
+}
+
+static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+{
+ s32 val = i2c_smbus_read_byte_data(client, command);
+ if (val < 0)
+ return val;
+
+ val &= ~mask;
+ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+}
+
+static int tw9910_set_scale(struct i2c_client *client,
+ const struct tw9910_scale_ctrl *scale)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SCALE_HI,
+ (scale->vscale & 0x0F00) >> 4 |
+ (scale->hscale & 0x0F00) >> 8);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
+ scale->hscale & 0x00FF);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
+ scale->vscale & 0x00FF);
+
+ return ret;
+}
+
+static int tw9910_set_hsync(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSBEGIN,
+ (HSYNC_START & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* bit 10 - 3 */
+ ret = i2c_smbus_write_byte_data(client, HSEND,
+ (HSYNC_END & 0x07F8) >> 3);
+ if (ret < 0)
+ return ret;
+
+ /* So far only revisions 0 and 1 have been seen */
+ /* bit 2 - 0 */
+ if (1 == priv->revision)
+ ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
+ (HSYNC_START & 0x0007) << 4 |
+ (HSYNC_END & 0x0007));
+
+ return ret;
+}
+
+static void tw9910_reset(struct i2c_client *client)
+{
+ tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
+ msleep(1);
+}
+
+static int tw9910_power(struct i2c_client *client, int enable)
+{
+ int ret;
+ u8 acntl1;
+ u8 acntl2;
+
+ if (enable) {
+ acntl1 = 0;
+ acntl2 = 0;
+ } else {
+ acntl1 = CLK_PDN | Y_PDN | C_PDN;
+ acntl2 = PLL_PDN;
+ }
+
+ ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
+}
+
+static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
+ u32 width, u32 height)
+{
+ const struct tw9910_scale_ctrl *scale;
+ const struct tw9910_scale_ctrl *ret = NULL;
+ __u32 diff = 0xffffffff, tmp;
+ int size, i;
+
+ if (norm & V4L2_STD_NTSC) {
+ scale = tw9910_ntsc_scales;
+ size = ARRAY_SIZE(tw9910_ntsc_scales);
+ } else if (norm & V4L2_STD_PAL) {
+ scale = tw9910_pal_scales;
+ size = ARRAY_SIZE(tw9910_pal_scales);
+ } else {
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ tmp = abs(width - scale[i].width) +
+ abs(height - scale[i].height);
+ if (tmp < diff) {
+ diff = tmp;
+ ret = scale + i;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * subdevice operations
+ */
+static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ u8 val;
+ int ret;
+
+ if (!enable) {
+ switch (priv->revision) {
+ case 0:
+ val = OEN_TRI_SEL_ALL_OFF_r0;
+ break;
+ case 1:
+ val = OEN_TRI_SEL_ALL_OFF_r1;
+ break;
+ default:
+ dev_err(&client->dev, "un-supported revision\n");
+ return -EINVAL;
+ }
+ } else {
+ val = OEN_TRI_SEL_ALL_ON;
+
+ if (!priv->scale) {
+ dev_err(&client->dev, "norm select error\n");
+ return -EPERM;
+ }
+
+ dev_dbg(&client->dev, "%s %dx%d\n",
+ priv->scale->name,
+ priv->scale->width,
+ priv->scale->height);
+ }
+
+ ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return tw9910_power(client, enable);
+}
+
+static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ *norm = priv->norm;
+
+ return 0;
+}
+
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
+ return -EINVAL;
+
+ priv->norm = norm;
+
+ return 0;
+}
+
+static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ id->ident = V4L2_IDENT_TW9910;
+ id->revision = priv->revision;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tw9910_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (reg->reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * ret = int
+ * reg->val = __u64
+ */
+ reg->val = (__u64)ret;
+
+ return 0;
+}
+
+static int tw9910_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (reg->reg > 0xff ||
+ reg->val > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
+}
+#endif
+
+static int tw9910_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ return soc_camera_set_power(&client->dev, icl, on);
+}
+
+static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ int ret = -EINVAL;
+ u8 val;
+
+ /*
+ * select suitable norm
+ */
+ priv->scale = tw9910_select_norm(priv->norm, *width, *height);
+ if (!priv->scale)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * reset hardware
+ */
+ tw9910_reset(client);
+
+ /*
+ * set bus width
+ */
+ val = 0x00;
+ if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
+ val = LEN;
+
+ ret = tw9910_mask_set(client, OPFORM, LEN, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * select MPOUT behavior
+ */
+ switch (priv->info->mpout) {
+ case TW9910_MPO_VLOSS:
+ val = RTSEL_VLOSS; break;
+ case TW9910_MPO_HLOCK:
+ val = RTSEL_HLOCK; break;
+ case TW9910_MPO_SLOCK:
+ val = RTSEL_SLOCK; break;
+ case TW9910_MPO_VLOCK:
+ val = RTSEL_VLOCK; break;
+ case TW9910_MPO_MONO:
+ val = RTSEL_MONO; break;
+ case TW9910_MPO_DET50:
+ val = RTSEL_DET50; break;
+ case TW9910_MPO_FIELD:
+ val = RTSEL_FIELD; break;
+ case TW9910_MPO_RTCO:
+ val = RTSEL_RTCO; break;
+ default:
+ val = 0;
+ }
+
+ ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set scale
+ */
+ ret = tw9910_set_scale(client, priv->scale);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ /*
+ * set hsync
+ */
+ ret = tw9910_set_hsync(client);
+ if (ret < 0)
+ goto tw9910_set_fmt_error;
+
+ *width = priv->scale->width;
+ *height = priv->scale->height;
+
+ return ret;
+
+tw9910_set_fmt_error:
+
+ tw9910_reset(client);
+ priv->scale = NULL;
+
+ return ret;
+}
+
+static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ a->c.left = 0;
+ a->c.top = 0;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->c.width = 640;
+ a->c.height = 480;
+ } else {
+ a->c.width = 768;
+ a->c.height = 576;
+ }
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ if (priv->norm & V4L2_STD_NTSC) {
+ a->bounds.width = 640;
+ a->bounds.height = 480;
+ } else {
+ a->bounds.width = 768;
+ a->bounds.height = 576;
+ }
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int tw9910_g_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (!priv->scale) {
+ priv->scale = tw9910_select_norm(priv->norm, 640, 480);
+ if (!priv->scale)
+ return -EINVAL;
+ }
+
+ mf->width = priv->scale->width;
+ mf->height = priv->scale->height;
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+
+ return 0;
+}
+
+static int tw9910_s_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ u32 width = mf->width, height = mf->height;
+ int ret;
+
+ WARN_ON(mf->field != V4L2_FIELD_ANY &&
+ mf->field != V4L2_FIELD_INTERLACED_BT);
+
+ /*
+ * check color format
+ */
+ if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
+ return -EINVAL;
+
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ ret = tw9910_set_frame(sd, &width, &height);
+ if (!ret) {
+ mf->width = width;
+ mf->height = height;
+ }
+ return ret;
+}
+
+static int tw9910_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tw9910_priv *priv = to_tw9910(client);
+ const struct tw9910_scale_ctrl *scale;
+
+ if (V4L2_FIELD_ANY == mf->field) {
+ mf->field = V4L2_FIELD_INTERLACED_BT;
+ } else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
+ dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
+ return -EINVAL;
+ }
+
+ mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ /*
+ * select suitable norm
+ */
+ scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
+ if (!scale)
+ return -EINVAL;
+
+ mf->width = scale->width;
+ mf->height = scale->height;
+
+ return 0;
+}
+
+static int tw9910_video_probe(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+ s32 id;
+ int ret;
+
+ /*
+ * tw9910 only use 8 or 16 bit bus width
+ */
+ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
+ SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
+ dev_err(&client->dev, "bus width error\n");
+ return -ENODEV;
+ }
+
+ ret = tw9910_s_power(&priv->subdev, 1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * check and show Product ID
+ * So far only revisions 0 and 1 have been seen
+ */
+ id = i2c_smbus_read_byte_data(client, ID);
+ priv->revision = GET_REV(id);
+ id = GET_ID(id);
+
+ if (0x0B != id ||
+ 0x01 < priv->revision) {
+ dev_err(&client->dev,
+ "Product ID error %x:%x\n",
+ id, priv->revision);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ dev_info(&client->dev,
+ "tw9910 Product ID %0x:%0x\n", id, priv->revision);
+
+ priv->norm = V4L2_STD_NTSC;
+
+done:
+ tw9910_s_power(&priv->subdev, 0);
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
+ .g_chip_ident = tw9910_g_chip_ident,
+ .s_std = tw9910_s_std,
+ .g_std = tw9910_g_std,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tw9910_g_register,
+ .s_register = tw9910_s_register,
+#endif
+ .s_power = tw9910_s_power,
+};
+
+static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+
+ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_DATA_ACTIVE_HIGH;
+ cfg->type = V4L2_MBUS_PARALLEL;
+ cfg->flags = soc_camera_apply_board_flags(icl, cfg);
+
+ return 0;
+}
+
+static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
+ const struct v4l2_mbus_config *cfg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ u8 val = VSSL_VVALID | HSSL_DVALID;
+ unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
+
+ /*
+ * set OUTCTR1
+ *
+ * We use VVALID and DVALID signals to control VSYNC and HSYNC
+ * outputs, in this mode their polarity is inverted.
+ */
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ val |= HSP_HI;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ val |= VSP_HI;
+
+ return i2c_smbus_write_byte_data(client, OUTCTR1, val);
+}
+
+static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+ .s_stream = tw9910_s_stream,
+ .g_mbus_fmt = tw9910_g_fmt,
+ .s_mbus_fmt = tw9910_s_fmt,
+ .try_mbus_fmt = tw9910_try_fmt,
+ .cropcap = tw9910_cropcap,
+ .g_crop = tw9910_g_crop,
+ .enum_mbus_fmt = tw9910_enum_fmt,
+ .g_mbus_config = tw9910_g_mbus_config,
+ .s_mbus_config = tw9910_s_mbus_config,
+};
+
+static struct v4l2_subdev_ops tw9910_subdev_ops = {
+ .core = &tw9910_subdev_core_ops,
+ .video = &tw9910_subdev_video_ops,
+};
+
+/*
+ * i2c_driver function
+ */
+
+static int tw9910_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+
+{
+ struct tw9910_priv *priv;
+ struct tw9910_video_info *info;
+ struct i2c_adapter *adapter =
+ to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
+ int ret;
+
+ if (!icl || !icl->priv) {
+ dev_err(&client->dev, "TW9910: missing platform data!\n");
+ return -EINVAL;
+ }
+
+ info = icl->priv;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "I2C-Adapter doesn't support "
+ "I2C_FUNC_SMBUS_BYTE_DATA\n");
+ return -EIO;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->info = info;
+
+ v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
+
+ ret = tw9910_video_probe(client);
+ if (ret)
+ kfree(priv);
+
+ return ret;
+}
+
+static int tw9910_remove(struct i2c_client *client)
+{
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ kfree(priv);
+ return 0;
+}
+
+static const struct i2c_device_id tw9910_id[] = {
+ { "tw9910", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tw9910_id);
+
+static struct i2c_driver tw9910_i2c_driver = {
+ .driver = {
+ .name = "tw9910",
+ },
+ .probe = tw9910_probe,
+ .remove = tw9910_remove,
+ .id_table = tw9910_id,
+};
+
+module_i2c_driver(tw9910_i2c_driver);
+
+MODULE_DESCRIPTION("SoC Camera driver for tw9910");
+MODULE_AUTHOR("Kuninori Morimoto");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/i2c/sr030pc30.c
index e9d95bda2ab1..e9d95bda2ab1 100644
--- a/drivers/media/video/sr030pc30.c
+++ b/drivers/media/i2c/sr030pc30.c
diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c
new file mode 100644
index 000000000000..9252529fc5dd
--- /dev/null
+++ b/drivers/media/i2c/tcm825x.c
@@ -0,0 +1,937 @@
+/*
+ * drivers/media/i2c/tcm825x.c
+ *
+ * TCM825X camera sensor driver.
+ *
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from David Cohen <david.cohen@indt.org.br>
+ *
+ * This driver was based on ov9640 sensor driver from MontaVista
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <media/v4l2-int-device.h>
+
+#include "tcm825x.h"
+
+/*
+ * The sensor has two fps modes: the lower one just gives half the fps
+ * at the same xclk than the high one.
+ */
+#define MAX_FPS 30
+#define MIN_FPS 8
+#define MAX_HALF_FPS (MAX_FPS / 2)
+#define HIGH_FPS_MODE_LOWER_LIMIT 14
+#define DEFAULT_FPS MAX_HALF_FPS
+
+struct tcm825x_sensor {
+ const struct tcm825x_platform_data *platform_data;
+ struct v4l2_int_device *v4l2_int_device;
+ struct i2c_client *i2c_client;
+ struct v4l2_pix_format pix;
+ struct v4l2_fract timeperframe;
+};
+
+/* list of image formats supported by TCM825X sensor */
+static const struct v4l2_fmtdesc tcm825x_formats[] = {
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ }, {
+ /* Note: V4L2 defines RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
+ *
+ * We interpret RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
+ */
+ .description = "RGB565, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+};
+
+#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats)
+
+/*
+ * TCM825X register configuration for all combinations of pixel format and
+ * image size
+ */
+static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ };
+static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ };
+static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ };
+static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ };
+static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ };
+static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ };
+
+static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT };
+static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT };
+
+/* Our own specific controls */
+#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE
+#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1
+#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2
+#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3
+#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4
+#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME
+
+/* Video controls */
+static struct vcontrol {
+ struct v4l2_queryctrl qc;
+ u16 reg;
+ u16 start_bit;
+} video_control[] = {
+ {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ },
+ .reg = TCM825X_AG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ },
+ .reg = TCM825X_MRG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ },
+ .reg = TCM825X_MBG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_AWBSW,
+ .start_bit = 7,
+ },
+ {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure Time",
+ .minimum = 0,
+ .maximum = 0x1fff,
+ .step = 1,
+ },
+ .reg = TCM825X_ESRSPD_U,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror Image",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_H_INV,
+ .start_bit = 6,
+ },
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_V_INV,
+ .start_bit = 7,
+ },
+ /* Private controls */
+ {
+ {
+ .id = V4L2_CID_ALC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Luminance Control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 0,
+ },
+ .reg = TCM825X_ALCSW,
+ .start_bit = 7,
+ },
+ {
+ {
+ .id = V4L2_CID_H_EDGE_EN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Horizontal Edge Enhancement",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ },
+ .reg = TCM825X_HDTG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_V_EDGE_EN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Edge Enhancement",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ },
+ .reg = TCM825X_VDTG,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_LENS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Lens Shading Compensation",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ },
+ .reg = TCM825X_LENS,
+ .start_bit = 0,
+ },
+ {
+ {
+ .id = V4L2_CID_MAX_EXPOSURE_TIME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Maximum Exposure Time",
+ .minimum = 0,
+ .maximum = 0x3,
+ .step = 1,
+ },
+ .reg = TCM825X_ESRLIM,
+ .start_bit = 5,
+ },
+};
+
+
+static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] =
+{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga };
+
+static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] =
+{ &yuv422, &rgb565 };
+
+/*
+ * Read a value from a register in an TCM825X sensor device. The value is
+ * returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_read_reg(struct i2c_client *client, int reg)
+{
+ int err;
+ struct i2c_msg msg[2];
+ u8 reg_buf, data_buf = 0;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &reg_buf;
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 1;
+ msg[1].buf = &data_buf;
+
+ reg_buf = reg;
+
+ err = i2c_transfer(client->adapter, msg, 2);
+ if (err < 0)
+ return err;
+ return data_buf;
+}
+
+/*
+ * Write a value to a register in an TCM825X sensor device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ struct i2c_msg msg[1];
+ unsigned char data[2];
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ msg->addr = client->addr;
+ msg->flags = 0;
+ msg->len = 2;
+ msg->buf = data;
+ data[0] = reg;
+ data[1] = val;
+ err = i2c_transfer(client->adapter, msg, 1);
+ if (err >= 0)
+ return 0;
+ return err;
+}
+
+static int __tcm825x_write_reg_mask(struct i2c_client *client,
+ u8 reg, u8 val, u8 mask)
+{
+ int rc;
+
+ /* need to do read - modify - write */
+ rc = tcm825x_read_reg(client, reg);
+ if (rc < 0)
+ return rc;
+
+ rc &= (~mask); /* Clear the masked bits */
+ val &= mask; /* Enforce mask on value */
+ val |= rc;
+
+ /* write the new value to the register */
+ rc = tcm825x_write_reg(client, reg, val);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+#define tcm825x_write_reg_mask(client, regmask, val) \
+ __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \
+ TCM825X_MASK((regmask)))
+
+
+/*
+ * Initialize a list of TCM825X registers.
+ * The list of registers is terminated by the pair of values
+ * { TCM825X_REG_TERM, TCM825X_VAL_TERM }.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tcm825x_write_default_regs(struct i2c_client *client,
+ const struct tcm825x_reg *reglist)
+{
+ int err;
+ const struct tcm825x_reg *next = reglist;
+
+ while (!((next->reg == TCM825X_REG_TERM)
+ && (next->val == TCM825X_VAL_TERM))) {
+ err = tcm825x_write_reg(client, next->reg, next->val);
+ if (err) {
+ dev_err(&client->dev, "register writing failed\n");
+ return err;
+ }
+ next++;
+ }
+
+ return 0;
+}
+
+static struct vcontrol *find_vctrl(int id)
+{
+ int i;
+
+ if (id < V4L2_CID_BASE)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(video_control); i++)
+ if (video_control[i].qc.id == id)
+ return &video_control[i];
+
+ return NULL;
+}
+
+/*
+ * Find the best match for a requested image capture size. The best match
+ * is chosen as the nearest match that has the same number or fewer pixels
+ * as the requested size, or the smallest image size if the requested size
+ * has fewer pixels than the smallest image.
+ */
+static enum image_size tcm825x_find_size(struct v4l2_int_device *s,
+ unsigned int width,
+ unsigned int height)
+{
+ enum image_size isize;
+ unsigned long pixels = width * height;
+ struct tcm825x_sensor *sensor = s->priv;
+
+ for (isize = subQCIF; isize < VGA; isize++) {
+ if (tcm825x_sizes[isize + 1].height
+ * tcm825x_sizes[isize + 1].width > pixels) {
+ dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize);
+
+ return isize;
+ }
+ }
+
+ dev_dbg(&sensor->i2c_client->dev, "format default VGA\n");
+
+ return VGA;
+}
+
+/*
+ * Configure the TCM825X for current image size, pixel format, and
+ * frame period. fper is the frame period (in seconds) expressed as a
+ * fraction. Returns zero if successful, or non-zero otherwise. The
+ * actual frame period is returned in fper.
+ */
+static int tcm825x_configure(struct v4l2_int_device *s)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_pix_format *pix = &sensor->pix;
+ enum image_size isize = tcm825x_find_size(s, pix->width, pix->height);
+ struct v4l2_fract *fper = &sensor->timeperframe;
+ enum pixel_format pfmt;
+ int err;
+ u32 tgt_fps;
+ u8 val;
+
+ /* common register initialization */
+ err = tcm825x_write_default_regs(
+ sensor->i2c_client, sensor->platform_data->default_regs());
+ if (err)
+ return err;
+
+ /* configure image size */
+ val = tcm825x_siz_reg[isize]->val;
+ dev_dbg(&sensor->i2c_client->dev,
+ "configuring image size %d\n", isize);
+ err = tcm825x_write_reg_mask(sensor->i2c_client,
+ tcm825x_siz_reg[isize]->reg, val);
+ if (err)
+ return err;
+
+ /* configure pixel format */
+ switch (pix->pixelformat) {
+ default:
+ case V4L2_PIX_FMT_RGB565:
+ pfmt = RGB565;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ pfmt = YUV422;
+ break;
+ }
+
+ dev_dbg(&sensor->i2c_client->dev,
+ "configuring pixel format %d\n", pfmt);
+ val = tcm825x_fmt_reg[pfmt]->val;
+
+ err = tcm825x_write_reg_mask(sensor->i2c_client,
+ tcm825x_fmt_reg[pfmt]->reg, val);
+ if (err)
+ return err;
+
+ /*
+ * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be
+ * set. Frame rate will be halved from the normal.
+ */
+ tgt_fps = fper->denominator / fper->numerator;
+ if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) {
+ val = tcm825x_read_reg(sensor->i2c_client, 0x02);
+ val |= 0x80;
+ tcm825x_write_reg(sensor->i2c_client, 0x02, val);
+ }
+
+ return 0;
+}
+
+static int ioctl_queryctrl(struct v4l2_int_device *s,
+ struct v4l2_queryctrl *qc)
+{
+ struct vcontrol *control;
+
+ control = find_vctrl(qc->id);
+
+ if (control == NULL)
+ return -EINVAL;
+
+ *qc = control->qc;
+
+ return 0;
+}
+
+static int ioctl_g_ctrl(struct v4l2_int_device *s,
+ struct v4l2_control *vc)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct i2c_client *client = sensor->i2c_client;
+ int val, r;
+ struct vcontrol *lvc;
+
+ /* exposure time is special, spread across 2 registers */
+ if (vc->id == V4L2_CID_EXPOSURE) {
+ int val_lower, val_upper;
+
+ val_upper = tcm825x_read_reg(client,
+ TCM825X_ADDR(TCM825X_ESRSPD_U));
+ if (val_upper < 0)
+ return val_upper;
+ val_lower = tcm825x_read_reg(client,
+ TCM825X_ADDR(TCM825X_ESRSPD_L));
+ if (val_lower < 0)
+ return val_lower;
+
+ vc->value = ((val_upper & 0x1f) << 8) | (val_lower);
+ return 0;
+ }
+
+ lvc = find_vctrl(vc->id);
+ if (lvc == NULL)
+ return -EINVAL;
+
+ r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg));
+ if (r < 0)
+ return r;
+ val = r & TCM825X_MASK(lvc->reg);
+ val >>= lvc->start_bit;
+
+ if (val < 0)
+ return val;
+
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
+ vc->value = val;
+ return 0;
+}
+
+static int ioctl_s_ctrl(struct v4l2_int_device *s,
+ struct v4l2_control *vc)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct i2c_client *client = sensor->i2c_client;
+ struct vcontrol *lvc;
+ int val = vc->value;
+
+ /* exposure time is special, spread across 2 registers */
+ if (vc->id == V4L2_CID_EXPOSURE) {
+ int val_lower, val_upper;
+ val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L);
+ val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U);
+
+ if (tcm825x_write_reg_mask(client,
+ TCM825X_ESRSPD_U, val_upper))
+ return -EIO;
+
+ if (tcm825x_write_reg_mask(client,
+ TCM825X_ESRSPD_L, val_lower))
+ return -EIO;
+
+ return 0;
+ }
+
+ lvc = find_vctrl(vc->id);
+ if (lvc == NULL)
+ return -EINVAL;
+
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
+ val = val << lvc->start_bit;
+ if (tcm825x_write_reg_mask(client, lvc->reg, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (index >= TCM825X_NUM_CAPTURE_FORMATS)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ fmt->flags = tcm825x_formats[index].flags;
+ strlcpy(fmt->description, tcm825x_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = tcm825x_formats[index].pixelformat;
+
+ return 0;
+}
+
+static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ enum image_size isize;
+ int ifmt;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ isize = tcm825x_find_size(s, pix->width, pix->height);
+ dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n",
+ isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS);
+
+ pix->width = tcm825x_sizes[isize].width;
+ pix->height = tcm825x_sizes[isize].height;
+
+ for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++)
+ if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat)
+ break;
+
+ if (ifmt == TCM825X_NUM_CAPTURE_FORMATS)
+ ifmt = 0; /* Default = YUV 4:2:2 */
+
+ pix->pixelformat = tcm825x_formats[ifmt].pixelformat;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+ dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n",
+ pix->pixelformat);
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ break;
+ }
+
+ return 0;
+}
+
+static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int rval;
+
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ rval = tcm825x_configure(s);
+
+ sensor->pix = *pix;
+
+ return rval;
+}
+
+static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
+ struct v4l2_format *f)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ f->fmt.pix = sensor->pix;
+
+ return 0;
+}
+
+static int ioctl_g_parm(struct v4l2_int_device *s,
+ struct v4l2_streamparm *a)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_captureparm *cparm = &a->parm.capture;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe = sensor->timeperframe;
+
+ return 0;
+}
+
+static int ioctl_s_parm(struct v4l2_int_device *s,
+ struct v4l2_streamparm *a)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
+ u32 tgt_fps; /* target frames per secound */
+ int rval;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if ((timeperframe->numerator == 0)
+ || (timeperframe->denominator == 0)) {
+ timeperframe->denominator = DEFAULT_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+ if (tgt_fps > MAX_FPS) {
+ timeperframe->denominator = MAX_FPS;
+ timeperframe->numerator = 1;
+ } else if (tgt_fps < MIN_FPS) {
+ timeperframe->denominator = MIN_FPS;
+ timeperframe->numerator = 1;
+ }
+
+ sensor->timeperframe = *timeperframe;
+
+ rval = tcm825x_configure(s);
+
+ return rval;
+}
+
+static int ioctl_s_power(struct v4l2_int_device *s, int on)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ return sensor->platform_data->power_set(on);
+}
+
+/*
+ * Given the image capture format in pix, the nominal frame period in
+ * timeperframe, calculate the required xclk frequency.
+ *
+ * TCM825X input frequency characteristics are:
+ * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz
+ */
+
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ struct v4l2_fract *timeperframe = &sensor->timeperframe;
+ u32 tgt_xclk; /* target xclk */
+ u32 tgt_fps; /* target frames per secound */
+ int rval;
+
+ rval = sensor->platform_data->ifparm(p);
+ if (rval)
+ return rval;
+
+ tgt_fps = timeperframe->denominator / timeperframe->numerator;
+
+ tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ?
+ (2457 * tgt_fps) / MAX_HALF_FPS :
+ (2457 * tgt_fps) / MAX_FPS;
+ tgt_xclk *= 10000;
+
+ tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX);
+ tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN);
+
+ p->u.bt656.clock_curr = tgt_xclk;
+
+ return 0;
+}
+
+static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+
+ return sensor->platform_data->needs_reset(s, buf, &sensor->pix);
+}
+
+static int ioctl_reset(struct v4l2_int_device *s)
+{
+ return -EBUSY;
+}
+
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ return tcm825x_configure(s);
+}
+
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tcm825x_sensor *sensor = s->priv;
+ int r;
+
+ r = tcm825x_read_reg(sensor->i2c_client, 0x01);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dev_err(&sensor->i2c_client->dev, "device not detected\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = {
+ { vidioc_int_dev_init_num,
+ (v4l2_int_ioctl_func *)ioctl_dev_init },
+ { vidioc_int_dev_exit_num,
+ (v4l2_int_ioctl_func *)ioctl_dev_exit },
+ { vidioc_int_s_power_num,
+ (v4l2_int_ioctl_func *)ioctl_s_power },
+ { vidioc_int_g_ifparm_num,
+ (v4l2_int_ioctl_func *)ioctl_g_ifparm },
+ { vidioc_int_g_needs_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_g_needs_reset },
+ { vidioc_int_reset_num,
+ (v4l2_int_ioctl_func *)ioctl_reset },
+ { vidioc_int_init_num,
+ (v4l2_int_ioctl_func *)ioctl_init },
+ { vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
+ { vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
+ { vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
+ { vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
+ { vidioc_int_g_parm_num,
+ (v4l2_int_ioctl_func *)ioctl_g_parm },
+ { vidioc_int_s_parm_num,
+ (v4l2_int_ioctl_func *)ioctl_s_parm },
+ { vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_queryctrl },
+ { vidioc_int_g_ctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_g_ctrl },
+ { vidioc_int_s_ctrl_num,
+ (v4l2_int_ioctl_func *)ioctl_s_ctrl },
+};
+
+static struct v4l2_int_slave tcm825x_slave = {
+ .ioctls = tcm825x_ioctl_desc,
+ .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc),
+};
+
+static struct tcm825x_sensor tcm825x;
+
+static struct v4l2_int_device tcm825x_int_device = {
+ .module = THIS_MODULE,
+ .name = TCM825X_NAME,
+ .priv = &tcm825x,
+ .type = v4l2_int_type_slave,
+ .u = {
+ .slave = &tcm825x_slave,
+ },
+};
+
+static int tcm825x_probe(struct i2c_client *client,
+ const struct i2c_device_id *did)
+{
+ struct tcm825x_sensor *sensor = &tcm825x;
+
+ if (i2c_get_clientdata(client))
+ return -EBUSY;
+
+ sensor->platform_data = client->dev.platform_data;
+
+ if (sensor->platform_data == NULL
+ || !sensor->platform_data->is_okay())
+ return -ENODEV;
+
+ sensor->v4l2_int_device = &tcm825x_int_device;
+
+ sensor->i2c_client = client;
+ i2c_set_clientdata(client, sensor);
+
+ /* Make the default capture format QVGA RGB565 */
+ sensor->pix.width = tcm825x_sizes[QVGA].width;
+ sensor->pix.height = tcm825x_sizes[QVGA].height;
+ sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
+
+ return v4l2_int_device_register(sensor->v4l2_int_device);
+}
+
+static int tcm825x_remove(struct i2c_client *client)
+{
+ struct tcm825x_sensor *sensor = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(sensor->v4l2_int_device);
+
+ return 0;
+}
+
+static const struct i2c_device_id tcm825x_id[] = {
+ { "tcm825x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tcm825x_id);
+
+static struct i2c_driver tcm825x_i2c_driver = {
+ .driver = {
+ .name = TCM825X_NAME,
+ },
+ .probe = tcm825x_probe,
+ .remove = tcm825x_remove,
+ .id_table = tcm825x_id,
+};
+
+static struct tcm825x_sensor tcm825x = {
+ .timeperframe = {
+ .numerator = 1,
+ .denominator = DEFAULT_FPS,
+ },
+};
+
+static int __init tcm825x_init(void)
+{
+ int rval;
+
+ rval = i2c_add_driver(&tcm825x_i2c_driver);
+ if (rval)
+ printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n",
+ __func__);
+
+ return rval;
+}
+
+static void __exit tcm825x_exit(void)
+{
+ i2c_del_driver(&tcm825x_i2c_driver);
+}
+
+/*
+ * FIXME: Menelaus isn't ready (?) at module_init stage, so use
+ * late_initcall for now.
+ */
+late_initcall(tcm825x_init);
+module_exit(tcm825x_exit);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("TCM825x camera sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h
new file mode 100644
index 000000000000..8ebab953963f
--- /dev/null
+++ b/drivers/media/i2c/tcm825x.h
@@ -0,0 +1,200 @@
+/*
+ * drivers/media/i2c/tcm825x.h
+ *
+ * Register definitions for the TCM825X CameraChip.
+ *
+ * Author: David Cohen (david.cohen@indt.org.br)
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * This file was based on ov9640.h from MontaVista
+ */
+
+#ifndef TCM825X_H
+#define TCM825X_H
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-int-device.h>
+
+#define TCM825X_NAME "tcm825x"
+
+#define TCM825X_MASK(x) x & 0x00ff
+#define TCM825X_ADDR(x) (x & 0xff00) >> 8
+
+/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */
+#define TCM825X_I2C_ADDR 0x3d
+
+/*
+ * define register offsets for the TCM825X sensor chip
+ * OFFSET(8 bits) + MASK(8 bits)
+ * MASK bit 4 and 3 are used when the register uses more than one address
+ */
+#define TCM825X_FPS 0x0280
+#define TCM825X_ACF 0x0240
+#define TCM825X_DOUTBUF 0x020C
+#define TCM825X_DCLKP 0x0202
+#define TCM825X_ACFDET 0x0201
+#define TCM825X_DOUTSW 0x0380
+#define TCM825X_DATAHZ 0x0340
+#define TCM825X_PICSIZ 0x033c
+#define TCM825X_PICFMT 0x0302
+#define TCM825X_V_INV 0x0480
+#define TCM825X_H_INV 0x0440
+#define TCM825X_ESRLSW 0x0430
+#define TCM825X_V_LENGTH 0x040F
+#define TCM825X_ALCSW 0x0580
+#define TCM825X_ESRLIM 0x0560
+#define TCM825X_ESRSPD_U 0x051F
+#define TCM825X_ESRSPD_L 0x06FF
+#define TCM825X_AG 0x07FF
+#define TCM825X_ESRSPD2 0x06FF
+#define TCM825X_ALCMODE 0x0830
+#define TCM825X_ALCH 0x080F
+#define TCM825X_ALCL 0x09FF
+#define TCM825X_AWBSW 0x0A80
+#define TCM825X_MRG 0x0BFF
+#define TCM825X_MBG 0x0CFF
+#define TCM825X_GAMSW 0x0D80
+#define TCM825X_HDTG 0x0EFF
+#define TCM825X_VDTG 0x0FFF
+#define TCM825X_HDTCORE 0x10F0
+#define TCM825X_VDTCORE 0x100F
+#define TCM825X_CONT 0x11FF
+#define TCM825X_BRIGHT 0x12FF
+#define TCM825X_VHUE 0x137F
+#define TCM825X_UHUE 0x147F
+#define TCM825X_VGAIN 0x153F
+#define TCM825X_UGAIN 0x163F
+#define TCM825X_UVCORE 0x170F
+#define TCM825X_SATU 0x187F
+#define TCM825X_MHMODE 0x1980
+#define TCM825X_MHLPFSEL 0x1940
+#define TCM825X_YMODE 0x1930
+#define TCM825X_MIXHG 0x1907
+#define TCM825X_LENS 0x1A3F
+#define TCM825X_AGLIM 0x1BE0
+#define TCM825X_LENSRPOL 0x1B10
+#define TCM825X_LENSRGAIN 0x1B0F
+#define TCM825X_ES100S 0x1CFF
+#define TCM825X_ES120S 0x1DFF
+#define TCM825X_DMASK 0x1EC0
+#define TCM825X_CODESW 0x1E20
+#define TCM825X_CODESEL 0x1E10
+#define TCM825X_TESPIC 0x1E04
+#define TCM825X_PICSEL 0x1E03
+#define TCM825X_HNUM 0x20FF
+#define TCM825X_VOUTPH 0x287F
+#define TCM825X_ESROUT 0x327F
+#define TCM825X_ESROUT2 0x33FF
+#define TCM825X_AGOUT 0x34FF
+#define TCM825X_DGOUT 0x353F
+#define TCM825X_AGSLOW1 0x39C0
+#define TCM825X_FLLSMODE 0x3930
+#define TCM825X_FLLSLIM 0x390F
+#define TCM825X_DETSEL 0x3AF0
+#define TCM825X_ACDETNC 0x3A0F
+#define TCM825X_AGSLOW2 0x3BC0
+#define TCM825X_DG 0x3B3F
+#define TCM825X_REJHLEV 0x3CFF
+#define TCM825X_ALCLOCK 0x3D80
+#define TCM825X_FPSLNKSW 0x3D40
+#define TCM825X_ALCSPD 0x3D30
+#define TCM825X_REJH 0x3D03
+#define TCM825X_SHESRSW 0x3E80
+#define TCM825X_ESLIMSEL 0x3E40
+#define TCM825X_SHESRSPD 0x3E30
+#define TCM825X_ELSTEP 0x3E0C
+#define TCM825X_ELSTART 0x3E03
+#define TCM825X_AGMIN 0x3FFF
+#define TCM825X_PREGRG 0x423F
+#define TCM825X_PREGBG 0x433F
+#define TCM825X_PRERG 0x443F
+#define TCM825X_PREBG 0x453F
+#define TCM825X_MSKBR 0x477F
+#define TCM825X_MSKGR 0x487F
+#define TCM825X_MSKRB 0x497F
+#define TCM825X_MSKGB 0x4A7F
+#define TCM825X_MSKRG 0x4B7F
+#define TCM825X_MSKBG 0x4C7F
+#define TCM825X_HDTCSW 0x4D80
+#define TCM825X_VDTCSW 0x4D40
+#define TCM825X_DTCYL 0x4D3F
+#define TCM825X_HDTPSW 0x4E80
+#define TCM825X_VDTPSW 0x4E40
+#define TCM825X_DTCGAIN 0x4E3F
+#define TCM825X_DTLLIMSW 0x4F10
+#define TCM825X_DTLYLIM 0x4F0F
+#define TCM825X_YLCUTLMSK 0x5080
+#define TCM825X_YLCUTL 0x503F
+#define TCM825X_YLCUTHMSK 0x5180
+#define TCM825X_YLCUTH 0x513F
+#define TCM825X_UVSKNC 0x527F
+#define TCM825X_UVLJ 0x537F
+#define TCM825X_WBGMIN 0x54FF
+#define TCM825X_WBGMAX 0x55FF
+#define TCM825X_WBSPDUP 0x5603
+#define TCM825X_ALLAREA 0x5820
+#define TCM825X_WBLOCK 0x5810
+#define TCM825X_WB2SP 0x580F
+#define TCM825X_KIZUSW 0x5920
+#define TCM825X_PBRSW 0x5910
+#define TCM825X_ABCSW 0x5903
+#define TCM825X_PBDLV 0x5AFF
+#define TCM825X_PBC1LV 0x5BFF
+
+#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1)
+
+#define TCM825X_BYTES_PER_PIXEL 2
+
+#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */
+#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */
+
+/* define a structure for tcm825x register initialization values */
+struct tcm825x_reg {
+ u8 val;
+ u16 reg;
+};
+
+enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA };
+enum pixel_format { YUV422 = 0, RGB565 };
+#define NUM_IMAGE_SIZES 6
+#define NUM_PIXEL_FORMATS 2
+
+#define TCM825X_XCLK_MIN 11900000
+#define TCM825X_XCLK_MAX 25000000
+
+struct capture_size {
+ unsigned long width;
+ unsigned long height;
+};
+
+struct tcm825x_platform_data {
+ /* Is the sensor usable? Doesn't yet mean it's there, but you
+ * can try! */
+ int (*is_okay)(void);
+ /* Set power state, zero is off, non-zero is on. */
+ int (*power_set)(int power);
+ /* Default registers written after power-on or reset. */
+ const struct tcm825x_reg *(*default_regs)(void);
+ int (*needs_reset)(struct v4l2_int_device *s, void *buf,
+ struct v4l2_pix_format *fmt);
+ int (*ifparm)(struct v4l2_ifparm *p);
+ int (*is_upside_down)(void);
+};
+
+/* Array of image sizes supported by TCM825X. These must be ordered from
+ * smallest image size to largest.
+ */
+static const struct capture_size tcm825x_sizes[] = {
+ { 128, 96 }, /* subQCIF */
+ { 160, 120 }, /* QQVGA */
+ { 176, 144 }, /* QCIF */
+ { 320, 240 }, /* QVGA */
+ { 352, 288 }, /* CIF */
+ { 640, 480 }, /* VGA */
+};
+
+#endif /* ifndef TCM825X_H */
diff --git a/drivers/media/video/tda7432.c b/drivers/media/i2c/tda7432.c
index f7707e65761e..f7707e65761e 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/i2c/tda7432.c
diff --git a/drivers/media/video/tda9840.c b/drivers/media/i2c/tda9840.c
index 3d7ddd93282d..3d7ddd93282d 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/i2c/tda9840.c
diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c
new file mode 100644
index 000000000000..3d5b06a5c308
--- /dev/null
+++ b/drivers/media/i2c/tea6415c.c
@@ -0,0 +1,187 @@
+ /*
+ tea6415c - i2c-driver for the tea6415c by SGS Thomson
+
+ Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ The tea6415c is a bus controlled video-matrix-switch
+ with 8 inputs and 6 outputs.
+ It is cascadable, i.e. it can be found at the addresses
+ 0x86 and 0x06 on the i2c-bus.
+
+ For detailed informations download the specifications directly
+ from SGS Thomson at http://www.st.com
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License vs published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include "tea6415c.h"
+
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_DESCRIPTION("tea6415c driver");
+MODULE_LICENSE("GPL");
+
+static int debug;
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+
+/* makes a connection between the input-pin 'i' and the output-pin 'o' */
+static int tea6415c_s_routing(struct v4l2_subdev *sd,
+ u32 i, u32 o, u32 config)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte = 0;
+ int ret;
+
+ v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
+
+ /* check if the pins are valid */
+ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
+ && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
+ return -EINVAL;
+
+ /* to understand this, have a look at the tea6415c-specs (p.5) */
+ switch (o) {
+ case 18:
+ byte = 0x00;
+ break;
+ case 14:
+ byte = 0x20;
+ break;
+ case 16:
+ byte = 0x10;
+ break;
+ case 17:
+ byte = 0x08;
+ break;
+ case 15:
+ byte = 0x18;
+ break;
+ case 13:
+ byte = 0x28;
+ break;
+ }
+
+ switch (i) {
+ case 5:
+ byte |= 0x00;
+ break;
+ case 8:
+ byte |= 0x04;
+ break;
+ case 3:
+ byte |= 0x02;
+ break;
+ case 20:
+ byte |= 0x06;
+ break;
+ case 6:
+ byte |= 0x01;
+ break;
+ case 10:
+ byte |= 0x05;
+ break;
+ case 1:
+ byte |= 0x03;
+ break;
+ case 11:
+ byte |= 0x07;
+ break;
+ }
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+ v4l2_dbg(1, debug, sd,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+ return ret;
+}
+
+static int tea6415c_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_TEA6415C, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+ .g_chip_ident = tea6415c_g_chip_ident,
+};
+
+static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
+ .s_routing = tea6415c_s_routing,
+};
+
+static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
+ .video = &tea6415c_video_ops,
+};
+
+static int tea6415c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct v4l2_subdev *sd;
+
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EIO;
+
+ 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)
+ return -ENOMEM;
+ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
+ return 0;
+}
+
+static int tea6415c_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
+static const struct i2c_device_id tea6415c_id[] = {
+ { "tea6415c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tea6415c_id);
+
+static struct i2c_driver tea6415c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tea6415c",
+ },
+ .probe = tea6415c_probe,
+ .remove = tea6415c_remove,
+ .id_table = tea6415c_id,
+};
+
+module_i2c_driver(tea6415c_driver);
diff --git a/drivers/media/video/tea6415c.h b/drivers/media/i2c/tea6415c.h
index 3a47d697536e..3a47d697536e 100644
--- a/drivers/media/video/tea6415c.h
+++ b/drivers/media/i2c/tea6415c.h
diff --git a/drivers/media/video/tea6420.c b/drivers/media/i2c/tea6420.c
index 38757217a074..38757217a074 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/i2c/tea6420.c
diff --git a/drivers/media/video/tea6420.h b/drivers/media/i2c/tea6420.h
index 4aa3edb3e193..4aa3edb3e193 100644
--- a/drivers/media/video/tea6420.h
+++ b/drivers/media/i2c/tea6420.h
diff --git a/drivers/media/video/ths7303.c b/drivers/media/i2c/ths7303.c
index e5c0eedebc58..e5c0eedebc58 100644
--- a/drivers/media/video/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c
index 809a75a558ee..809a75a558ee 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/i2c/tlv320aic23b.c
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
new file mode 100644
index 000000000000..3b24d3fc1866
--- /dev/null
+++ b/drivers/media/i2c/tvaudio.c
@@ -0,0 +1,2131 @@
+/*
+ * Driver for simple i2c audio chips.
+ *
+ * Copyright (c) 2000 Gerd Knorr
+ * based on code by:
+ * Eric Sandeen (eric_sandeen@bigfoot.com)
+ * Steve VanDeBogart (vandebo@uclink.berkeley.edu)
+ * Greg Alexander (galexand@acm.org)
+ *
+ * For the TDA9875 part:
+ * Copyright (c) 2000 Guillaume Delvit based on Gerd Knorr source
+ * and Eric Sandeen
+ *
+ * Copyright(c) 2005-2008 Mauro Carvalho Chehab
+ * - Some cleanups, code fixes, etc
+ * - Convert it to V4L2 API
+ *
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * OPTIONS:
+ * debug - set to 1 if you'd like to see debug messages
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/tvaudio.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+
+#include <media/i2c-addr.h>
+
+/* ---------------------------------------------------------------------- */
+/* insmod args */
+
+static int debug; /* insmod parameter */
+module_param(debug, int, 0644);
+
+MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips");
+MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr");
+MODULE_LICENSE("GPL");
+
+#define UNSET (-1U)
+
+/* ---------------------------------------------------------------------- */
+/* our structs */
+
+#define MAXREGS 256
+
+struct CHIPSTATE;
+typedef int (*getvalue)(int);
+typedef int (*checkit)(struct CHIPSTATE*);
+typedef int (*initialize)(struct CHIPSTATE*);
+typedef int (*getrxsubchans)(struct CHIPSTATE *);
+typedef void (*setaudmode)(struct CHIPSTATE*, int mode);
+
+/* i2c command */
+typedef struct AUDIOCMD {
+ int count; /* # of bytes to send */
+ unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */
+} audiocmd;
+
+/* chip description */
+struct CHIPDESC {
+ char *name; /* chip name */
+ int addr_lo, addr_hi; /* i2c address range */
+ int registers; /* # of registers */
+
+ int *insmodopt;
+ checkit checkit;
+ initialize initialize;
+ int flags;
+#define CHIP_HAS_VOLUME 1
+#define CHIP_HAS_BASSTREBLE 2
+#define CHIP_HAS_INPUTSEL 4
+#define CHIP_NEED_CHECKMODE 8
+
+ /* various i2c command sequences */
+ audiocmd init;
+
+ /* which register has which value */
+ int leftreg,rightreg,treblereg,bassreg;
+
+ /* initialize with (defaults to 65535/65535/32768/32768 */
+ int leftinit,rightinit,trebleinit,bassinit;
+
+ /* functions to convert the values (v4l -> chip) */
+ getvalue volfunc,treblefunc,bassfunc;
+
+ /* get/set mode */
+ getrxsubchans getrxsubchans;
+ setaudmode setaudmode;
+
+ /* input switch register + values for v4l inputs */
+ int inputreg;
+ int inputmap[4];
+ int inputmute;
+ int inputmask;
+};
+
+/* current state of the chip */
+struct CHIPSTATE {
+ struct v4l2_subdev sd;
+
+ /* chip-specific description - should point to
+ an entry at CHIPDESC table */
+ struct CHIPDESC *desc;
+
+ /* shadow register set */
+ audiocmd shadow;
+
+ /* current settings */
+ __u16 left, right, treble, bass, muted;
+ int prevmode;
+ int radio;
+ int input;
+
+ /* thread */
+ struct task_struct *thread;
+ struct timer_list wt;
+ int audmode;
+};
+
+static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct CHIPSTATE, sd);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* i2c I/O functions */
+
+static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[2];
+
+ if (subaddr < 0) {
+ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
+ chip->shadow.bytes[1] = val;
+ buffer[0] = val;
+ if (1 != i2c_master_send(c, buffer, 1)) {
+ v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
+ return -1;
+ }
+ } else {
+ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register: %d\n",
+ subaddr);
+ return -EINVAL;
+ }
+
+ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
+ subaddr, val);
+ chip->shadow.bytes[subaddr+1] = val;
+ buffer[0] = subaddr;
+ buffer[1] = val;
+ if (2 != i2c_master_send(c, buffer, 2)) {
+ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
+ subaddr, val);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int chip_write_masked(struct CHIPSTATE *chip,
+ int subaddr, int val, int mask)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ if (mask != 0) {
+ if (subaddr < 0) {
+ val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
+ } else {
+ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register: %d\n",
+ subaddr);
+ return -EINVAL;
+ }
+
+ val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask);
+ }
+ }
+ return chip_write(chip, subaddr, val);
+}
+
+static int chip_read(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer;
+
+ if (1 != i2c_master_recv(c, &buffer, 1)) {
+ v4l2_warn(sd, "I/O error (read)\n");
+ return -1;
+ }
+ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
+ return buffer;
+}
+
+static int chip_read2(struct CHIPSTATE *chip, int subaddr)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char write[1];
+ unsigned char read[1];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = c->addr,
+ .len = 1,
+ .buf = write
+ },
+ {
+ .addr = c->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = read
+ }
+ };
+
+ write[0] = subaddr;
+
+ if (2 != i2c_transfer(c->adapter, msgs, 2)) {
+ v4l2_warn(sd, "I/O error (read2)\n");
+ return -1;
+ }
+ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
+ subaddr, read[0]);
+ return read[0];
+}
+
+static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ int i;
+
+ if (0 == cmd->count)
+ return 0;
+
+ if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
+ v4l2_info(sd,
+ "Tried to access a non-existent register range: %d to %d\n",
+ cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
+ return -EINVAL;
+ }
+
+ /* FIXME: it seems that the shadow bytes are wrong bellow !*/
+
+ /* update our shadow register set; print bytes if (debug > 0) */
+ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
+ name, cmd->bytes[0]);
+ for (i = 1; i < cmd->count; i++) {
+ if (debug)
+ printk(KERN_CONT " 0x%x", cmd->bytes[i]);
+ chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
+ }
+ if (debug)
+ printk(KERN_CONT "\n");
+
+ /* send data to the chip */
+ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
+ v4l2_warn(sd, "I/O error (%s)\n", name);
+ return -1;
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* kernel thread for doing i2c stuff asyncronly
+ * right now it is used only to check the audio mode (mono/stereo/whatever)
+ * some time after switching to another TV channel, then turn on stereo
+ * if available, ...
+ */
+
+static void chip_thread_wake(unsigned long data)
+{
+ struct CHIPSTATE *chip = (struct CHIPSTATE*)data;
+ wake_up_process(chip->thread);
+}
+
+static int chip_thread(void *data)
+{
+ struct CHIPSTATE *chip = data;
+ struct CHIPDESC *desc = chip->desc;
+ struct v4l2_subdev *sd = &chip->sd;
+ int mode, selected;
+
+ v4l2_dbg(1, debug, sd, "thread started\n");
+ set_freezable();
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop())
+ schedule();
+ set_current_state(TASK_RUNNING);
+ try_to_freeze();
+ if (kthread_should_stop())
+ break;
+ v4l2_dbg(1, debug, sd, "thread wakeup\n");
+
+ /* don't do anything for radio */
+ if (chip->radio)
+ continue;
+
+ /* have a look what's going on */
+ mode = desc->getrxsubchans(chip);
+ if (mode == chip->prevmode)
+ continue;
+
+ /* chip detected a new audio mode - set it */
+ v4l2_dbg(1, debug, sd, "thread checkmode\n");
+
+ chip->prevmode = mode;
+
+ selected = V4L2_TUNER_MODE_MONO;
+ switch (chip->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ if (mode & V4L2_TUNER_SUB_LANG1)
+ selected = V4L2_TUNER_MODE_LANG1;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ if (mode & V4L2_TUNER_SUB_LANG1)
+ selected = V4L2_TUNER_MODE_LANG1;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ if (mode & V4L2_TUNER_SUB_LANG2)
+ selected = V4L2_TUNER_MODE_LANG2;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ if (mode & V4L2_TUNER_SUB_LANG2)
+ selected = V4L2_TUNER_MODE_LANG1_LANG2;
+ else if (mode & V4L2_TUNER_SUB_STEREO)
+ selected = V4L2_TUNER_MODE_STEREO;
+ }
+ desc->setaudmode(chip, selected);
+
+ /* schedule next check */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ }
+
+ v4l2_dbg(1, debug, sd, "thread exiting\n");
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9840 */
+
+#define TDA9840_SW 0x00
+#define TDA9840_LVADJ 0x02
+#define TDA9840_STADJ 0x03
+#define TDA9840_TEST 0x04
+
+#define TDA9840_MONO 0x10
+#define TDA9840_STEREO 0x2a
+#define TDA9840_DUALA 0x12
+#define TDA9840_DUALB 0x1e
+#define TDA9840_DUALAB 0x1a
+#define TDA9840_DUALBA 0x16
+#define TDA9840_EXTERNAL 0x7a
+
+#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */
+#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */
+#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */
+
+#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */
+#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */
+
+static int tda9840_getrxsubchans(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int val, mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_SUB_MONO;
+ if (val & TDA9840_DS_DUAL)
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ if (val & TDA9840_ST_STEREO)
+ mode = V4L2_TUNER_SUB_STEREO;
+
+ v4l2_dbg(1, debug, sd,
+ "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n",
+ val, mode);
+ return mode;
+}
+
+static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ t |= TDA9840_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ t |= TDA9840_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ t |= TDA9840_DUALA;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ t |= TDA9840_DUALB;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ t |= TDA9840_DUALAB;
+ break;
+ default:
+ update = 0;
+ }
+
+ if (update)
+ chip_write(chip, TDA9840_SW, t);
+}
+
+static int tda9840_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+ rc = chip_read(chip);
+ /* lower 5 bits should be 0 */
+ return ((rc & 0x1f) == 0) ? 1 : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda985x */
+
+/* subaddresses for TDA9855 */
+#define TDA9855_VR 0x00 /* Volume, right */
+#define TDA9855_VL 0x01 /* Volume, left */
+#define TDA9855_BA 0x02 /* Bass */
+#define TDA9855_TR 0x03 /* Treble */
+#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */
+
+/* subaddresses for TDA9850 */
+#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */
+
+/* subaddesses for both chips */
+#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */
+#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */
+#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */
+#define TDA985x_A1 0x08 /* Alignment 1 for both chips */
+#define TDA985x_A2 0x09 /* Alignment 2 for both chips */
+#define TDA985x_A3 0x0a /* Alignment 3 for both chips */
+
+/* Masks for bits in TDA9855 subaddresses */
+/* 0x00 - VR in TDA9855 */
+/* 0x01 - VL in TDA9855 */
+/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f)
+ * in 1dB steps - mute is 0x27 */
+
+
+/* 0x02 - BA in TDA9855 */
+/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
+ * in .5dB steps - 0 is 0x0E */
+
+
+/* 0x03 - TR in TDA9855 */
+/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb)
+ * in 3dB steps - 0 is 0x7 */
+
+/* Masks for bits in both chips' subaddresses */
+/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */
+/* Unique to TDA9855: */
+/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
+ * in 3dB steps - mute is 0x0 */
+
+/* Unique to TDA9850: */
+/* lower 4 bits control stereo noise threshold, over which stereo turns off
+ * set to values of 0x00 through 0x0f for Ster1 through Ster16 */
+
+
+/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/
+/* Unique to TDA9855: */
+#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */
+#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */
+#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
+#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
+ /* Bits 0 to 3 select various combinations
+ * of line in and line out, only the
+ * interesting ones are defined */
+#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
+#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
+
+/* Unique to TDA9850: */
+/* lower 4 bits contol SAP noise threshold, over which SAP turns off
+ * set to values of 0x00 through 0x0f for SAP1 through SAP16 */
+
+
+/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
+/* Common to TDA9855 and TDA9850: */
+#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
+#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */
+#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
+#define TDA985x_MONO 0 /* Forces Mono output */
+#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
+
+/* Unique to TDA9855: */
+#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */
+#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/
+#define TDA9855_LINEAR 0 /* Linear Stereo */
+#define TDA9855_PSEUDO 1 /* Pseudo Stereo */
+#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */
+#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */
+#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/
+
+/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF)
+ * in .5dB steps - 0dB is 0x7 */
+
+/* 0x08, 0x09 - A1 and A2 (read/write) */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 5 bites are wideband and spectral expander alignment
+ * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */
+#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */
+#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */
+#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/
+
+/* 0x0a - A3 */
+/* Common to both TDA9855 and TDA9850: */
+/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1),
+ * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */
+#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */
+
+static int tda9855_volume(int val) { return val/0x2e8+0x27; }
+static int tda9855_bass(int val) { return val/0xccc+0x06; }
+static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
+
+static int tda985x_getrxsubchans(struct CHIPSTATE *chip)
+{
+ int mode, val;
+
+ /* Add mono mode regardless of SAP and stereo */
+ /* Allows forced mono */
+ mode = V4L2_TUNER_SUB_MONO;
+ val = chip_read(chip);
+ if (val & TDA985x_STP)
+ mode = V4L2_TUNER_SUB_STEREO;
+ if (val & TDA985x_SAPP)
+ mode |= V4L2_TUNER_SUB_SAP;
+ return mode;
+}
+
+static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ int update = 1;
+ int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ c6 |= TDA985x_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ c6 |= TDA985x_STEREO;
+ break;
+ case V4L2_TUNER_MODE_SAP:
+ c6 |= TDA985x_SAP;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ c6 |= TDA985x_MONOSAP;
+ break;
+ default:
+ update = 0;
+ }
+ if (update)
+ chip_write(chip,TDA985x_C6,c6);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda9873h */
+
+/* Subaddresses for TDA9873H */
+
+#define TDA9873_SW 0x00 /* Switching */
+#define TDA9873_AD 0x01 /* Adjust */
+#define TDA9873_PT 0x02 /* Port */
+
+/* Subaddress 0x00: Switching Data
+ * B7..B0:
+ *
+ * B1, B0: Input source selection
+ * 0, 0 internal
+ * 1, 0 external stereo
+ * 0, 1 external mono
+ */
+#define TDA9873_INP_MASK 3
+#define TDA9873_INTERNAL 0
+#define TDA9873_EXT_STEREO 2
+#define TDA9873_EXT_MONO 1
+
+/* B3, B2: output signal select
+ * B4 : transmission mode
+ * 0, 0, 1 Mono
+ * 1, 0, 0 Stereo
+ * 1, 1, 1 Stereo (reversed channel)
+ * 0, 0, 0 Dual AB
+ * 0, 0, 1 Dual AA
+ * 0, 1, 0 Dual BB
+ * 0, 1, 1 Dual BA
+ */
+
+#define TDA9873_TR_MASK (7 << 2)
+#define TDA9873_TR_MONO 4
+#define TDA9873_TR_STEREO 1 << 4
+#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2))
+#define TDA9873_TR_DUALA 1 << 2
+#define TDA9873_TR_DUALB 1 << 3
+#define TDA9873_TR_DUALAB 0
+
+/* output level controls
+ * B5: output level switch (0 = reduced gain, 1 = normal gain)
+ * B6: mute (1 = muted)
+ * B7: auto-mute (1 = auto-mute enabled)
+ */
+
+#define TDA9873_GAIN_NORMAL 1 << 5
+#define TDA9873_MUTE 1 << 6
+#define TDA9873_AUTOMUTE 1 << 7
+
+/* Subaddress 0x01: Adjust/standard */
+
+/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB)
+ * Recommended value is +0 dB
+ */
+
+#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */
+
+/* Bits C6..C4 control FM stantard
+ * C6, C5, C4
+ * 0, 0, 0 B/G (PAL FM)
+ * 0, 0, 1 M
+ * 0, 1, 0 D/K(1)
+ * 0, 1, 1 D/K(2)
+ * 1, 0, 0 D/K(3)
+ * 1, 0, 1 I
+ */
+#define TDA9873_BG 0
+#define TDA9873_M 1
+#define TDA9873_DK1 2
+#define TDA9873_DK2 3
+#define TDA9873_DK3 4
+#define TDA9873_I 5
+
+/* C7 controls identification response time (1=fast/0=normal)
+ */
+#define TDA9873_IDR_NORM 0
+#define TDA9873_IDR_FAST 1 << 7
+
+
+/* Subaddress 0x02: Port data */
+
+/* E1, E0 free programmable ports P1/P2
+ 0, 0 both ports low
+ 0, 1 P1 high
+ 1, 0 P2 high
+ 1, 1 both ports high
+*/
+
+#define TDA9873_PORTS 3
+
+/* E2: test port */
+#define TDA9873_TST_PORT 1 << 2
+
+/* E5..E3 control mono output channel (together with transmission mode bit B4)
+ *
+ * E5 E4 E3 B4 OUTM
+ * 0 0 0 0 mono
+ * 0 0 1 0 DUAL B
+ * 0 1 0 1 mono (from stereo decoder)
+ */
+#define TDA9873_MOUT_MONO 0
+#define TDA9873_MOUT_FMONO 0
+#define TDA9873_MOUT_DUALA 0
+#define TDA9873_MOUT_DUALB 1 << 3
+#define TDA9873_MOUT_ST 1 << 4
+#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3))
+#define TDA9873_MOUT_EXTL 1 << 5
+#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3))
+#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4))
+#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3))
+
+/* Status bits: (chip read) */
+#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */
+#define TDA9873_STEREO 2 /* Stereo sound is identified */
+#define TDA9873_DUAL 4 /* Dual sound is identified */
+
+static int tda9873_getrxsubchans(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int val,mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_SUB_MONO;
+ if (val & TDA9873_STEREO)
+ mode = V4L2_TUNER_SUB_STEREO;
+ if (val & TDA9873_DUAL)
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ v4l2_dbg(1, debug, sd,
+ "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n",
+ val, mode);
+ return mode;
+}
+
+static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
+ /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
+
+ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): external input\n");
+ return;
+ }
+
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n",
+ TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
+ v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n",
+ sw_data);
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ sw_data |= TDA9873_TR_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ sw_data |= TDA9873_TR_STEREO;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ sw_data |= TDA9873_TR_DUALA;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ sw_data |= TDA9873_TR_DUALB;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ sw_data |= TDA9873_TR_DUALAB;
+ break;
+ default:
+ return;
+ }
+
+ chip_write(chip, TDA9873_SW, sw_data);
+ v4l2_dbg(1, debug, sd,
+ "tda9873_setaudmode(): req. mode %d; chip_write: %d\n",
+ mode, sw_data);
+}
+
+static int tda9873_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+
+ if (-1 == (rc = chip_read2(chip,254)))
+ return 0;
+ return (rc & ~0x1f) == 0x80;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip description - defines+functions for tda9874h and tda9874a */
+/* Dariusz Kowalewski <darekk@automex.pl> */
+
+/* Subaddresses for TDA9874H and TDA9874A (slave rx) */
+#define TDA9874A_AGCGR 0x00 /* AGC gain */
+#define TDA9874A_GCONR 0x01 /* general config */
+#define TDA9874A_MSR 0x02 /* monitor select */
+#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */
+#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */
+#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */
+#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */
+#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */
+#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */
+#define TDA9874A_DCR 0x09 /* demodulator config */
+#define TDA9874A_FMER 0x0a /* FM de-emphasis */
+#define TDA9874A_FMMR 0x0b /* FM dematrix */
+#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */
+#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */
+#define TDA9874A_NCONR 0x0e /* NICAM config */
+#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */
+#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */
+#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */
+#define TDA9874A_AMCONR 0x12 /* audio mute control */
+#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */
+#define TDA9874A_AOSR 0x14 /* analog output select */
+#define TDA9874A_DAICONR 0x15 /* digital audio interface config */
+#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */
+#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */
+#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */
+#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */
+
+/* Subaddresses for TDA9874H and TDA9874A (slave tx) */
+#define TDA9874A_DSR 0x00 /* device status */
+#define TDA9874A_NSR 0x01 /* NICAM status */
+#define TDA9874A_NECR 0x02 /* NICAM error count */
+#define TDA9874A_DR1 0x03 /* add. data LSB */
+#define TDA9874A_DR2 0x04 /* add. data MSB */
+#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */
+#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */
+#define TDA9874A_SIFLR 0x07 /* SIF level */
+#define TDA9874A_TR2 252 /* test reg. 2 */
+#define TDA9874A_TR1 253 /* test reg. 1 */
+#define TDA9874A_DIC 254 /* device id. code */
+#define TDA9874A_SIC 255 /* software id. code */
+
+
+static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */
+static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */
+static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */
+static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */
+static int tda9874a_dic = -1; /* device id. code */
+
+/* insmod options for tda9874a */
+static unsigned int tda9874a_SIF = UNSET;
+static unsigned int tda9874a_AMSEL = UNSET;
+static unsigned int tda9874a_STD = UNSET;
+module_param(tda9874a_SIF, int, 0444);
+module_param(tda9874a_AMSEL, int, 0444);
+module_param(tda9874a_STD, int, 0444);
+
+/*
+ * initialization table for tda9874 decoder:
+ * - carrier 1 freq. registers (3 bytes)
+ * - carrier 2 freq. registers (3 bytes)
+ * - demudulator config register
+ * - FM de-emphasis register (slow identification mode)
+ * Note: frequency registers must be written in single i2c transfer.
+ */
+static struct tda9874a_MODES {
+ char *name;
+ audiocmd cmd;
+} tda9874a_modelist[9] = {
+ { "A2, B/G", /* default */
+ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} },
+ { "A2, M (Korea)",
+ { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} },
+ { "A2, D/K (1)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} },
+ { "A2, D/K (2)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} },
+ { "A2, D/K (3)",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} },
+ { "NICAM, I",
+ { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} },
+ { "NICAM, B/G",
+ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} },
+ { "NICAM, D/K",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} },
+ { "NICAM, L",
+ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} }
+};
+
+static int tda9874a_setup(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
+ chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
+ if(tda9874a_dic == 0x11) {
+ chip_write(chip, TDA9874A_FMMR, 0x80);
+ } else { /* dic == 0x07 */
+ chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd);
+ chip_write(chip, TDA9874A_FMMR, 0x00);
+ }
+ chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */
+ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
+ chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */
+ /* Note: If signal quality is poor you may want to change NICAM */
+ /* error limit registers (NLELR and NUELR) to some greater values. */
+ /* Then the sound would remain stereo, but won't be so clear. */
+ chip_write(chip, TDA9874A_NLELR, 0x14); /* default */
+ chip_write(chip, TDA9874A_NUELR, 0x50); /* default */
+
+ if(tda9874a_dic == 0x11) {
+ chip_write(chip, TDA9874A_AMCONR, 0xf9);
+ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
+ chip_write(chip, TDA9874A_AOSR, 0x80);
+ chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80);
+ chip_write(chip, TDA9874A_ESP, tda9874a_ESP);
+ } else { /* dic == 0x07 */
+ chip_write(chip, TDA9874A_AMCONR, 0xfb);
+ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
+ chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
+ }
+ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
+ tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
+ return 1;
+}
+
+static int tda9874a_getrxsubchans(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dsr,nsr,mode;
+ int necr; /* just for debugging */
+
+ mode = V4L2_TUNER_SUB_MONO;
+
+ if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
+ return mode;
+ if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR)))
+ return mode;
+ if(-1 == (necr = chip_read2(chip,TDA9874A_NECR)))
+ return mode;
+
+ /* need to store dsr/nsr somewhere */
+ chip->shadow.bytes[MAXREGS-2] = dsr;
+ chip->shadow.bytes[MAXREGS-1] = nsr;
+
+ if(tda9874a_mode) {
+ /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked.
+ * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates
+ * that sound has (temporarily) switched from NICAM to
+ * mono FM (or AM) on 1st sound carrier due to high NICAM bit
+ * error count. So in fact there is no stereo in this case :-(
+ * But changing the mode to V4L2_TUNER_MODE_MONO would switch
+ * external 4052 multiplexer in audio_hook().
+ */
+ if(nsr & 0x02) /* NSR.S/MB=1 */
+ mode = V4L2_TUNER_SUB_STEREO;
+ if(nsr & 0x01) /* NSR.D/SB=1 */
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ } else {
+ if(dsr & 0x02) /* DSR.IDSTE=1 */
+ mode = V4L2_TUNER_SUB_STEREO;
+ if(dsr & 0x04) /* DSR.IDDUA=1 */
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ }
+
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
+ dsr, nsr, necr, mode);
+ return mode;
+}
+
+static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+
+ /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
+ /* If auto-muting is disabled, we can hear a signal of degrading quality. */
+ if (tda9874a_mode) {
+ if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
+ tda9874a_NCONR &= 0xfe; /* enable */
+ else
+ tda9874a_NCONR |= 0x01; /* disable */
+ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
+ }
+
+ /* Note: TDA9874A supports automatic FM dematrixing (FMMR register)
+ * and has auto-select function for audio output (AOSR register).
+ * Old TDA9874H doesn't support these features.
+ * TDA9874A also has additional mono output pin (OUTM), which
+ * on same (all?) tv-cards is not used, anyway (as well as MONOIN).
+ */
+ if(tda9874a_dic == 0x11) {
+ int aosr = 0x80;
+ int mdacosr = (tda9874a_mode) ? 0x82:0x80;
+
+ switch(mode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ aosr = 0x80; /* auto-select, dual A/A */
+ mdacosr = (tda9874a_mode) ? 0x82:0x80;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ aosr = 0xa0; /* auto-select, dual B/B */
+ mdacosr = (tda9874a_mode) ? 0x83:0x81;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ aosr = 0x00; /* always route L to L and R to R */
+ mdacosr = (tda9874a_mode) ? 0x82:0x80;
+ break;
+ default:
+ return;
+ }
+ chip_write(chip, TDA9874A_AOSR, aosr);
+ chip_write(chip, TDA9874A_MDACOSR, mdacosr);
+
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
+ mode, aosr, mdacosr);
+
+ } else { /* dic == 0x07 */
+ int fmmr,aosr;
+
+ switch(mode) {
+ case V4L2_TUNER_MODE_MONO:
+ fmmr = 0x00; /* mono */
+ aosr = 0x10; /* A/A */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ if(tda9874a_mode) {
+ fmmr = 0x00;
+ aosr = 0x00; /* handled by NICAM auto-mute */
+ } else {
+ fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */
+ aosr = 0x00;
+ }
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ fmmr = 0x02; /* dual */
+ aosr = 0x10; /* dual A/A */
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ fmmr = 0x02; /* dual */
+ aosr = 0x20; /* dual B/B */
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ fmmr = 0x02; /* dual */
+ aosr = 0x00; /* dual A/B */
+ break;
+ default:
+ return;
+ }
+ chip_write(chip, TDA9874A_FMMR, fmmr);
+ chip_write(chip, TDA9874A_AOSR, aosr);
+
+ v4l2_dbg(1, debug, sd,
+ "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
+ mode, fmmr, aosr);
+ }
+}
+
+static int tda9874a_checkit(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dic,sic; /* device id. and software id. codes */
+
+ if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
+ return 0;
+ if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
+
+ if((dic == 0x11)||(dic == 0x07)) {
+ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
+ tda9874a_dic = dic; /* remember device id. */
+ return 1;
+ }
+ return 0; /* not found */
+}
+
+static int tda9874a_initialize(struct CHIPSTATE *chip)
+{
+ if (tda9874a_SIF > 2)
+ tda9874a_SIF = 1;
+ if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist))
+ tda9874a_STD = 0;
+ if(tda9874a_AMSEL > 1)
+ tda9874a_AMSEL = 0;
+
+ if(tda9874a_SIF == 1)
+ tda9874a_GCONR = 0xc0; /* sound IF input 1 */
+ else
+ tda9874a_GCONR = 0xc1; /* sound IF input 2 */
+
+ tda9874a_ESP = tda9874a_STD;
+ tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1;
+
+ if(tda9874a_AMSEL == 0)
+ tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */
+ else
+ tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */
+
+ tda9874a_setup(chip);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip description - defines+functions for tda9875 */
+/* The TDA9875 is made by Philips Semiconductor
+ * http://www.semiconductors.philips.com
+ * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator
+ *
+ */
+
+/* subaddresses for TDA9875 */
+#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/
+#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */
+#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/
+#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/
+
+#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/
+#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/
+#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/
+#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/
+
+#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/
+#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/
+#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/
+#define TDA9875_MVL 0x1a /* Main volume gauche */
+#define TDA9875_MVR 0x1b /* Main volume droite */
+#define TDA9875_MBA 0x1d /* Main Basse */
+#define TDA9875_MTR 0x1e /* Main treble */
+#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/
+#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */
+#define TDA9875_AVR 0x21 /* Auxiliary volume droite */
+#define TDA9875_ABA 0x22 /* Auxiliary Basse */
+#define TDA9875_ATR 0x23 /* Auxiliary treble */
+
+#define TDA9875_MSR 0x02 /* Monitor select register */
+#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */
+#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */
+#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */
+#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */
+#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */
+#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */
+#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/
+#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/
+#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/
+
+/* values */
+#define TDA9875_MUTE_ON 0xff /* general mute */
+#define TDA9875_MUTE_OFF 0xcc /* general no mute */
+
+static int tda9875_initialize(struct CHIPSTATE *chip)
+{
+ chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
+ chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
+ chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
+ chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
+ chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
+ chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
+ chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
+ chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
+ chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/
+ chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
+ chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
+ chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
+ chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
+
+ chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
+ chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
+ chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
+ chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
+ chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
+ chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
+ chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
+ chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
+ chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
+ chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
+ chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
+ chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
+ chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
+ chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
+ chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
+ chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
+
+ chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */
+ return 0;
+}
+
+static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); }
+static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); }
+static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); }
+
+/* ----------------------------------------------------------------------- */
+
+
+/* *********************** *
+ * i2c interface functions *
+ * *********************** */
+
+static int tda9875_checkit(struct CHIPSTATE *chip)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int dic, rev;
+
+ dic = chip_read2(chip, 254);
+ rev = chip_read2(chip, 255);
+
+ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
+ v4l2_info(sd, "found tda9875%s rev. %d.\n",
+ dic == 0 ? "" : "A", rev);
+ return 1;
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tea6420 */
+
+#define TEA6300_VL 0x00 /* volume left */
+#define TEA6300_VR 0x01 /* volume right */
+#define TEA6300_BA 0x02 /* bass */
+#define TEA6300_TR 0x03 /* treble */
+#define TEA6300_FA 0x04 /* fader control */
+#define TEA6300_S 0x05 /* switch register */
+ /* values for those registers: */
+#define TEA6300_S_SA 0x01 /* stereo A input */
+#define TEA6300_S_SB 0x02 /* stereo B */
+#define TEA6300_S_SC 0x04 /* stereo C */
+#define TEA6300_S_GMU 0x80 /* general mute */
+
+#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */
+#define TEA6320_FFR 0x01 /* fader front right (0-5) */
+#define TEA6320_FFL 0x02 /* fader front left (0-5) */
+#define TEA6320_FRR 0x03 /* fader rear right (0-5) */
+#define TEA6320_FRL 0x04 /* fader rear left (0-5) */
+#define TEA6320_BA 0x05 /* bass (0-4) */
+#define TEA6320_TR 0x06 /* treble (0-4) */
+#define TEA6320_S 0x07 /* switch register */
+ /* values for those registers: */
+#define TEA6320_S_SA 0x07 /* stereo A input */
+#define TEA6320_S_SB 0x06 /* stereo B */
+#define TEA6320_S_SC 0x05 /* stereo C */
+#define TEA6320_S_SD 0x04 /* stereo D */
+#define TEA6320_S_GMU 0x80 /* general mute */
+
+#define TEA6420_S_SA 0x00 /* stereo A input */
+#define TEA6420_S_SB 0x01 /* stereo B */
+#define TEA6420_S_SC 0x02 /* stereo C */
+#define TEA6420_S_SD 0x03 /* stereo D */
+#define TEA6420_S_SE 0x04 /* stereo E */
+#define TEA6420_S_GMU 0x05 /* general mute */
+
+static int tea6300_shift10(int val) { return val >> 10; }
+static int tea6300_shift12(int val) { return val >> 12; }
+
+/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */
+/* 0x0c mirror those immediately higher) */
+static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; }
+static int tea6320_shift11(int val) { return val >> 11; }
+static int tea6320_initialize(struct CHIPSTATE * chip)
+{
+ chip_write(chip, TEA6320_FFR, 0x3f);
+ chip_write(chip, TEA6320_FFL, 0x3f);
+ chip_write(chip, TEA6320_FRR, 0x3f);
+ chip_write(chip, TEA6320_FRL, 0x3f);
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for tda8425 */
+
+#define TDA8425_VL 0x00 /* volume left */
+#define TDA8425_VR 0x01 /* volume right */
+#define TDA8425_BA 0x02 /* bass */
+#define TDA8425_TR 0x03 /* treble */
+#define TDA8425_S1 0x08 /* switch functions */
+ /* values for those registers: */
+#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */
+#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */
+#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */
+#define TDA8425_S1_MU 0x20 /* mute bit */
+#define TDA8425_S1_STEREO 0x18 /* stereo bits */
+#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */
+#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */
+#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */
+#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */
+#define TDA8425_S1_ML 0x06 /* language selector */
+#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */
+#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */
+#define TDA8425_S1_ML_STEREO 0x06 /* stereo */
+#define TDA8425_S1_IS 0x01 /* channel selector */
+
+
+static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
+static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
+
+static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
+
+ switch (mode) {
+ case V4L2_TUNER_MODE_LANG1:
+ s1 |= TDA8425_S1_ML_SOUND_A;
+ s1 |= TDA8425_S1_STEREO_PSEUDO;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ s1 |= TDA8425_S1_ML_SOUND_B;
+ s1 |= TDA8425_S1_STEREO_PSEUDO;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ s1 |= TDA8425_S1_ML_STEREO;
+ s1 |= TDA8425_S1_STEREO_LINEAR;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ s1 |= TDA8425_S1_ML_STEREO;
+ s1 |= TDA8425_S1_STEREO_MONO;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ s1 |= TDA8425_S1_ML_STEREO;
+ s1 |= TDA8425_S1_STEREO_SPATIAL;
+ break;
+ default:
+ return;
+ }
+ chip_write(chip,TDA8425_S1,s1);
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for pic16c54 (PV951) */
+
+/* the registers of 16C54, I2C sub address. */
+#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */
+#define PIC16C54_REG_MISC 0x02
+
+/* bit definition of the RESET register, I2C data. */
+#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */
+ /* code of remote controller */
+#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */
+#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */
+#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */
+#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */
+#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */
+#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */
+#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - defines+functions for TA8874Z */
+
+/* write 1st byte */
+#define TA8874Z_LED_STE 0x80
+#define TA8874Z_LED_BIL 0x40
+#define TA8874Z_LED_EXT 0x20
+#define TA8874Z_MONO_SET 0x10
+#define TA8874Z_MUTE 0x08
+#define TA8874Z_F_MONO 0x04
+#define TA8874Z_MODE_SUB 0x02
+#define TA8874Z_MODE_MAIN 0x01
+
+/* write 2nd byte */
+/*#define TA8874Z_TI 0x80 */ /* test mode */
+#define TA8874Z_SEPARATION 0x3f
+#define TA8874Z_SEPARATION_DEFAULT 0x10
+
+/* read */
+#define TA8874Z_B1 0x80
+#define TA8874Z_B0 0x40
+#define TA8874Z_CHAG_FLAG 0x20
+
+/*
+ * B1 B0
+ * mono L H
+ * stereo L L
+ * BIL H L
+ */
+static int ta8874z_getrxsubchans(struct CHIPSTATE *chip)
+{
+ int val, mode;
+
+ val = chip_read(chip);
+ mode = V4L2_TUNER_SUB_MONO;
+ if (val & TA8874Z_B1){
+ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ }else if (!(val & TA8874Z_B0)){
+ mode = V4L2_TUNER_SUB_STEREO;
+ }
+ /* v4l2_dbg(1, debug, &chip->sd,
+ "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n",
+ val, mode); */
+ return mode;
+}
+
+static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
+static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
+
+static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode)
+{
+ struct v4l2_subdev *sd = &chip->sd;
+ int update = 1;
+ audiocmd *t = NULL;
+
+ v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode);
+
+ switch(mode){
+ case V4L2_TUNER_MODE_MONO:
+ t = &ta8874z_mono;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ t = &ta8874z_stereo;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ t = &ta8874z_main;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ t = &ta8874z_sub;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ t = &ta8874z_both;
+ break;
+ default:
+ update = 0;
+ }
+
+ if(update)
+ chip_cmd(chip, "TA8874Z", t);
+}
+
+static int ta8874z_checkit(struct CHIPSTATE *chip)
+{
+ int rc;
+ rc = chip_read(chip);
+ return ((rc & 0x1f) == 0x1f) ? 1 : 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* audio chip descriptions - struct CHIPDESC */
+
+/* insmod options to enable/disable individual audio chips */
+static int tda8425 = 1;
+static int tda9840 = 1;
+static int tda9850 = 1;
+static int tda9855 = 1;
+static int tda9873 = 1;
+static int tda9874a = 1;
+static int tda9875 = 1;
+static int tea6300; /* default 0 - address clash with msp34xx */
+static int tea6320; /* default 0 - address clash with msp34xx */
+static int tea6420 = 1;
+static int pic16c54 = 1;
+static int ta8874z; /* default 0 - address clash with tda9840 */
+
+module_param(tda8425, int, 0444);
+module_param(tda9840, int, 0444);
+module_param(tda9850, int, 0444);
+module_param(tda9855, int, 0444);
+module_param(tda9873, int, 0444);
+module_param(tda9874a, int, 0444);
+module_param(tda9875, int, 0444);
+module_param(tea6300, int, 0444);
+module_param(tea6320, int, 0444);
+module_param(tea6420, int, 0444);
+module_param(pic16c54, int, 0444);
+module_param(ta8874z, int, 0444);
+
+static struct CHIPDESC chiplist[] = {
+ {
+ .name = "tda9840",
+ .insmodopt = &tda9840,
+ .addr_lo = I2C_ADDR_TDA9840 >> 1,
+ .addr_hi = I2C_ADDR_TDA9840 >> 1,
+ .registers = 5,
+ .flags = CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .checkit = tda9840_checkit,
+ .getrxsubchans = tda9840_getrxsubchans,
+ .setaudmode = tda9840_setaudmode,
+
+ .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN
+ /* ,TDA9840_SW, TDA9840_MONO */} }
+ },
+ {
+ .name = "tda9873h",
+ .insmodopt = &tda9873,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 3,
+ .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .checkit = tda9873_checkit,
+ .getrxsubchans = tda9873_getrxsubchans,
+ .setaudmode = tda9873_setaudmode,
+
+ .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } },
+ .inputreg = TDA9873_SW,
+ .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE,
+ .inputmap = {0xa0, 0xa2, 0xa0, 0xa0},
+ .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE,
+
+ },
+ {
+ .name = "tda9874h/a",
+ .insmodopt = &tda9874a,
+ .addr_lo = I2C_ADDR_TDA9874 >> 1,
+ .addr_hi = I2C_ADDR_TDA9874 >> 1,
+ .flags = CHIP_NEED_CHECKMODE,
+
+ /* callbacks */
+ .initialize = tda9874a_initialize,
+ .checkit = tda9874a_checkit,
+ .getrxsubchans = tda9874a_getrxsubchans,
+ .setaudmode = tda9874a_setaudmode,
+ },
+ {
+ .name = "tda9875",
+ .insmodopt = &tda9875,
+ .addr_lo = I2C_ADDR_TDA9875 >> 1,
+ .addr_hi = I2C_ADDR_TDA9875 >> 1,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
+
+ /* callbacks */
+ .initialize = tda9875_initialize,
+ .checkit = tda9875_checkit,
+ .volfunc = tda9875_volume,
+ .bassfunc = tda9875_bass,
+ .treblefunc = tda9875_treble,
+ .leftreg = TDA9875_MVL,
+ .rightreg = TDA9875_MVR,
+ .bassreg = TDA9875_MBA,
+ .treblereg = TDA9875_MTR,
+ .leftinit = 58880,
+ .rightinit = 58880,
+ },
+ {
+ .name = "tda9850",
+ .insmodopt = &tda9850,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 11,
+
+ .getrxsubchans = tda985x_getrxsubchans,
+ .setaudmode = tda985x_setaudmode,
+
+ .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
+ },
+ {
+ .name = "tda9855",
+ .insmodopt = &tda9855,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+ .addr_hi = I2C_ADDR_TDA985x_H >> 1,
+ .registers = 11,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
+
+ .leftreg = TDA9855_VL,
+ .rightreg = TDA9855_VR,
+ .bassreg = TDA9855_BA,
+ .treblereg = TDA9855_TR,
+
+ /* callbacks */
+ .volfunc = tda9855_volume,
+ .bassfunc = tda9855_bass,
+ .treblefunc = tda9855_treble,
+ .getrxsubchans = tda985x_getrxsubchans,
+ .setaudmode = tda985x_setaudmode,
+
+ .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
+ TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
+ TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM,
+ 0x07, 0x10, 0x10, 0x03 }}
+ },
+ {
+ .name = "tea6300",
+ .insmodopt = &tea6300,
+ .addr_lo = I2C_ADDR_TEA6300 >> 1,
+ .addr_hi = I2C_ADDR_TEA6300 >> 1,
+ .registers = 6,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TEA6300_VR,
+ .rightreg = TEA6300_VL,
+ .bassreg = TEA6300_BA,
+ .treblereg = TEA6300_TR,
+
+ /* callbacks */
+ .volfunc = tea6300_shift10,
+ .bassfunc = tea6300_shift12,
+ .treblefunc = tea6300_shift12,
+
+ .inputreg = TEA6300_S,
+ .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tea6320",
+ .insmodopt = &tea6320,
+ .addr_lo = I2C_ADDR_TEA6300 >> 1,
+ .addr_hi = I2C_ADDR_TEA6300 >> 1,
+ .registers = 8,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TEA6320_V,
+ .rightreg = TEA6320_V,
+ .bassreg = TEA6320_BA,
+ .treblereg = TEA6320_TR,
+
+ /* callbacks */
+ .initialize = tea6320_initialize,
+ .volfunc = tea6320_volume,
+ .bassfunc = tea6320_shift11,
+ .treblefunc = tea6320_shift11,
+
+ .inputreg = TEA6320_S,
+ .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tea6420",
+ .insmodopt = &tea6420,
+ .addr_lo = I2C_ADDR_TEA6420 >> 1,
+ .addr_hi = I2C_ADDR_TEA6420 >> 1,
+ .registers = 1,
+ .flags = CHIP_HAS_INPUTSEL,
+
+ .inputreg = -1,
+ .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC },
+ .inputmute = TEA6300_S_GMU,
+ },
+ {
+ .name = "tda8425",
+ .insmodopt = &tda8425,
+ .addr_lo = I2C_ADDR_TDA8425 >> 1,
+ .addr_hi = I2C_ADDR_TDA8425 >> 1,
+ .registers = 9,
+ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
+
+ .leftreg = TDA8425_VL,
+ .rightreg = TDA8425_VR,
+ .bassreg = TDA8425_BA,
+ .treblereg = TDA8425_TR,
+
+ /* callbacks */
+ .volfunc = tda8425_shift10,
+ .bassfunc = tda8425_shift12,
+ .treblefunc = tda8425_shift12,
+ .setaudmode = tda8425_setaudmode,
+
+ .inputreg = TDA8425_S1,
+ .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
+ .inputmute = TDA8425_S1_OFF,
+
+ },
+ {
+ .name = "pic16c54 (PV951)",
+ .insmodopt = &pic16c54,
+ .addr_lo = I2C_ADDR_PIC16C54 >> 1,
+ .addr_hi = I2C_ADDR_PIC16C54>> 1,
+ .registers = 2,
+ .flags = CHIP_HAS_INPUTSEL,
+
+ .inputreg = PIC16C54_REG_MISC,
+ .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER,
+ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
+ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
+ PIC16C54_MISC_SND_MUTE},
+ .inputmute = PIC16C54_MISC_SND_MUTE,
+ },
+ {
+ .name = "ta8874z",
+ .checkit = ta8874z_checkit,
+ .insmodopt = &ta8874z,
+ .addr_lo = I2C_ADDR_TDA9840 >> 1,
+ .addr_hi = I2C_ADDR_TDA9840 >> 1,
+ .registers = 2,
+
+ /* callbacks */
+ .getrxsubchans = ta8874z_getrxsubchans,
+ .setaudmode = ta8874z_setaudmode,
+
+ .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
+ },
+ { .name = NULL } /* EOF */
+};
+
+
+/* ---------------------------------------------------------------------- */
+
+static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ break;
+ ctrl->value=chip->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+ ctrl->value = max(chip->left,chip->right);
+ return 0;
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume;
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+ volume = max(chip->left,chip->right);
+ if (volume)
+ ctrl->value=(32768*min(chip->left,chip->right))/volume;
+ else
+ ctrl->value=32768;
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ ctrl->value = chip->bass;
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ ctrl->value = chip->treble;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ break;
+
+ if (ctrl->value < 0 || ctrl->value >= 2)
+ return -ERANGE;
+ chip->muted = ctrl->value;
+ if (chip->muted)
+ chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask);
+ else
+ chip_write_masked(chip,desc->inputreg,
+ desc->inputmap[chip->input],desc->inputmask);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ {
+ int volume,balance;
+
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+
+ volume = max(chip->left,chip->right);
+ if (volume)
+ balance=(32768*min(chip->left,chip->right))/volume;
+ else
+ balance=32768;
+
+ volume=ctrl->value;
+ chip->left = (min(65536 - balance,32768) * volume) / 32768;
+ chip->right = (min(balance,volume *(__u16)32768)) / 32768;
+
+ chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
+ chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
+
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BALANCE:
+ {
+ int volume, balance;
+
+ if (!(desc->flags & CHIP_HAS_VOLUME))
+ break;
+
+ volume = max(chip->left, chip->right);
+ balance = ctrl->value;
+ chip->left = (min(65536 - balance, 32768) * volume) / 32768;
+ chip->right = (min(balance, volume * (__u16)32768)) / 32768;
+
+ chip_write(chip, desc->leftreg, desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg, desc->volfunc(chip->right));
+
+ return 0;
+ }
+ case V4L2_CID_AUDIO_BASS:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ chip->bass = ctrl->value;
+ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
+
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+ break;
+ chip->treble = ctrl->value;
+ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
+
+ return 0;
+ }
+ return -EINVAL;
+}
+
+
+/* ---------------------------------------------------------------------- */
+/* video4linux interface */
+
+static int tvaudio_s_radio(struct v4l2_subdev *sd)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 1;
+ /* del_timer(&chip->wt); */
+ return 0;
+}
+
+static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (desc->flags & CHIP_HAS_INPUTSEL)
+ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ if (desc->flags & CHIP_HAS_VOLUME)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ if (desc->flags & CHIP_HAS_VOLUME)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ if (desc->flags & CHIP_HAS_BASSTREBLE)
+ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int tvaudio_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ if (!(desc->flags & CHIP_HAS_INPUTSEL))
+ return 0;
+ if (input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = input;
+ if (chip->muted)
+ return 0;
+ chip_write_masked(chip, desc->inputreg,
+ desc->inputmap[chip->input], desc->inputmask);
+ return 0;
+}
+
+static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ if (!desc->setaudmode)
+ return 0;
+ if (chip->radio)
+ return 0;
+
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1:
+ case V4L2_TUNER_MODE_LANG2:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ break;
+ default:
+ return -EINVAL;
+ }
+ chip->audmode = vt->audmode;
+
+ if (chip->thread)
+ wake_up_process(chip->thread);
+ else
+ desc->setaudmode(chip, vt->audmode);
+
+ return 0;
+}
+
+static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ if (!desc->getrxsubchans)
+ return 0;
+ if (chip->radio)
+ return 0;
+
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = desc->getrxsubchans(chip);
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+ return 0;
+}
+
+static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+
+ chip->radio = 0;
+ return 0;
+}
+
+static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+ /* For chips that provide getrxsubchans and setaudmode, and doesn't
+ automatically follows the stereo carrier, a kthread is
+ created to set the audio standard. In this case, when then
+ the video channel is changed, tvaudio starts on MONO mode.
+ After waiting for 2 seconds, the kernel thread is called,
+ to follow whatever audio standard is pointed by the
+ audio carrier.
+ */
+ if (chip->thread) {
+ desc->setaudmode(chip, V4L2_TUNER_MODE_MONO);
+ chip->prevmode = -1; /* reset previous mode */
+ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
+ }
+ return 0;
+}
+
+static int tvaudio_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_TVAUDIO, 0);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
+ .g_chip_ident = tvaudio_g_chip_ident,
+ .queryctrl = tvaudio_queryctrl,
+ .g_ctrl = tvaudio_g_ctrl,
+ .s_ctrl = tvaudio_s_ctrl,
+ .s_std = tvaudio_s_std,
+};
+
+static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
+ .s_radio = tvaudio_s_radio,
+ .s_frequency = tvaudio_s_frequency,
+ .s_tuner = tvaudio_s_tuner,
+ .g_tuner = tvaudio_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
+ .s_routing = tvaudio_s_routing,
+};
+
+static const struct v4l2_subdev_ops tvaudio_ops = {
+ .core = &tvaudio_core_ops,
+ .tuner = &tvaudio_tuner_ops,
+ .audio = &tvaudio_audio_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+
+
+/* i2c registration */
+
+static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct CHIPSTATE *chip;
+ struct CHIPDESC *desc;
+ struct v4l2_subdev *sd;
+
+ if (debug) {
+ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
+ printk(KERN_INFO "tvaudio: known chips: ");
+ for (desc = chiplist; desc->name != NULL; desc++)
+ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
+ printk("\n");
+ }
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ sd = &chip->sd;
+ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
+
+ /* find description for the chip */
+ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
+ for (desc = chiplist; desc->name != NULL; desc++) {
+ if (0 == *(desc->insmodopt))
+ continue;
+ if (client->addr < desc->addr_lo ||
+ client->addr > desc->addr_hi)
+ continue;
+ if (desc->checkit && !desc->checkit(chip))
+ continue;
+ break;
+ }
+ if (desc->name == NULL) {
+ v4l2_dbg(1, debug, sd, "no matching chip description found\n");
+ kfree(chip);
+ return -EIO;
+ }
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
+ if (desc->flags) {
+ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
+ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
+ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
+ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
+ }
+
+ /* fill required data structures */
+ if (!id)
+ strlcpy(client->name, desc->name, I2C_NAME_SIZE);
+ chip->desc = desc;
+ chip->shadow.count = desc->registers+1;
+ chip->prevmode = -1;
+ chip->audmode = V4L2_TUNER_MODE_LANG1;
+
+ /* initialization */
+ if (desc->initialize != NULL)
+ desc->initialize(chip);
+ else
+ chip_cmd(chip, "init", &desc->init);
+
+ if (desc->flags & CHIP_HAS_VOLUME) {
+ if (!desc->volfunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without volume controls
+ */
+ v4l2_info(sd, "volume callback undefined!\n");
+ desc->flags &= ~CHIP_HAS_VOLUME;
+ } else {
+ chip->left = desc->leftinit ? desc->leftinit : 65535;
+ chip->right = desc->rightinit ? desc->rightinit : 65535;
+ chip_write(chip, desc->leftreg,
+ desc->volfunc(chip->left));
+ chip_write(chip, desc->rightreg,
+ desc->volfunc(chip->right));
+ }
+ }
+ if (desc->flags & CHIP_HAS_BASSTREBLE) {
+ if (!desc->bassfunc || !desc->treblefunc) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without bass/treble controls
+ */
+ v4l2_info(sd, "bass/treble callbacks undefined!\n");
+ desc->flags &= ~CHIP_HAS_BASSTREBLE;
+ } else {
+ chip->treble = desc->trebleinit ?
+ desc->trebleinit : 32768;
+ chip->bass = desc->bassinit ?
+ desc->bassinit : 32768;
+ chip_write(chip, desc->bassreg,
+ desc->bassfunc(chip->bass));
+ chip_write(chip, desc->treblereg,
+ desc->treblefunc(chip->treble));
+ }
+ }
+
+ chip->thread = NULL;
+ init_timer(&chip->wt);
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getrxsubchans || !desc->setaudmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+ without kthread
+ */
+ v4l2_info(sd, "set/get mode callbacks undefined!\n");
+ return 0;
+ }
+ /* start async thread */
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+ if (IS_ERR(chip->thread)) {
+ v4l2_warn(sd, "failed to create kthread\n");
+ chip->thread = NULL;
+ }
+ }
+ return 0;
+}
+
+static int tvaudio_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct CHIPSTATE *chip = to_state(sd);
+
+ del_timer_sync(&chip->wt);
+ if (chip->thread) {
+ /* shutdown async thread */
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
+ }
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(chip);
+ 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 tvaudio_id[] = {
+ { "tvaudio", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvaudio_id);
+
+static struct i2c_driver tvaudio_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tvaudio",
+ },
+ .probe = tvaudio_probe,
+ .remove = tvaudio_remove,
+ .id_table = tvaudio_id,
+};
+
+module_i2c_driver(tvaudio_driver);
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/i2c/tveeprom.c
index 3b6cf034976a..3b6cf034976a 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/i2c/tveeprom.c
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
new file mode 100644
index 000000000000..1f3943bb87d5
--- /dev/null
+++ b/drivers/media/i2c/tvp514x.c
@@ -0,0 +1,1166 @@
+/*
+ * drivers/media/i2c/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/v4l2-device.h>
+#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"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static bool debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
+
+/* enum tvp514x_std - enum for supported standards */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+static struct tvp514x_reg tvp514x_reg_list_default[0x40];
+
+static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable);
+/**
+ * struct tvp514x_decoder - TVP5146/47 decoder object
+ * @sd: Subdevice Slave handle
+ * @tvp514x_regs: copy of hw's regs with preset values.
+ * @pdata: Board specific
+ * @ver: Chip version
+ * @streaming: TVP5146/47 decoder streaming - enabled or disabled.
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @input: Input routing at chip level
+ * @output: Output routing at chip level
+ */
+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;
+
+ int ver;
+ int streaming;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ const struct tvp514x_std_info *std_list;
+ /* Input and Output Routing parameters */
+ u32 input;
+ u32 output;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list_default[] = {
+ /* Composite selected */
+ {TOK_WRITE, REG_INPUT_SEL, 0x05},
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ /* Auto mode */
+ {TOK_WRITE, REG_VIDEO_STD, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ /* Reserved */
+ {TOK_SKIP, 0x0F, 0x00},
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ /* Reserved */
+ {TOK_SKIP, 0x13, 0x00},
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ /* Reserved */
+ {TOK_SKIP, 0x15, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55},
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04},
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01},
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x26, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x27, 0x00},
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ /* Reserved */
+ {TOK_SKIP, 0x29, 0x00},
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x2B, 0x00},
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x2F, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x30, 0x00},
+ /* Reserved */
+ {TOK_SKIP, 0x31, 0x00},
+ /* HS, VS active high */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00},
+ /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00},
+ /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11},
+ /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE},
+ /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ /* Clear status */
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01},
+ {TOK_TERM, 0, 0},
+};
+
+/**
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static const struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+
+
+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.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ *
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err < 0) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * dump_reg() - dump the register content of TVP5146/47.
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ */
+static void dump_reg(struct v4l2_subdev *sd, u8 reg)
+{
+ u32 val;
+
+ val = tvp514x_read_reg(sd, reg);
+ v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val);
+}
+
+/**
+ * tvp514x_write_reg() - Write a value to a register in TVP5146/47
+ * @sd: ptr to v4l2_subdev struct
+ * @reg: TVP5146/47 register address
+ * @val: value to be written to the register
+ *
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val)
+{
+ int err, retry = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l2_warn(sd, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @reglist: list of TVP5146/47 registers and values
+ *
+ * Initializes a list of TVP5146/47 registers:-
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct v4l2_subdev *sd,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(sd, next->reg, (u8) next->val);
+ if (err) {
+ v4l2_err(sd, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/**
+ * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47
+ * @sd: ptr to v4l2_subdev struct
+ *
+ * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no
+ * standard detected.
+ */
+static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(sd, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT)
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS);
+ else
+ /* use the standard register itself */
+ std_status = std;
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/* TVP5146/47 register dump function */
+static void tvp514x_reg_dump(struct v4l2_subdev *sd)
+{
+ dump_reg(sd, REG_INPUT_SEL);
+ dump_reg(sd, REG_AFE_GAIN_CTRL);
+ dump_reg(sd, REG_VIDEO_STD);
+ dump_reg(sd, REG_OPERATION_MODE);
+ dump_reg(sd, REG_COLOR_KILLER);
+ dump_reg(sd, REG_LUMA_CONTROL1);
+ dump_reg(sd, REG_LUMA_CONTROL2);
+ dump_reg(sd, REG_LUMA_CONTROL3);
+ dump_reg(sd, REG_BRIGHTNESS);
+ dump_reg(sd, REG_CONTRAST);
+ dump_reg(sd, REG_SATURATION);
+ dump_reg(sd, REG_HUE);
+ dump_reg(sd, REG_CHROMA_CONTROL1);
+ dump_reg(sd, REG_CHROMA_CONTROL2);
+ dump_reg(sd, REG_COMP_PR_SATURATION);
+ dump_reg(sd, REG_COMP_Y_CONTRAST);
+ dump_reg(sd, REG_COMP_PB_SATURATION);
+ dump_reg(sd, REG_COMP_Y_BRIGHTNESS);
+ dump_reg(sd, REG_AVID_START_PIXEL_LSB);
+ dump_reg(sd, REG_AVID_START_PIXEL_MSB);
+ dump_reg(sd, REG_AVID_STOP_PIXEL_LSB);
+ dump_reg(sd, REG_AVID_STOP_PIXEL_MSB);
+ dump_reg(sd, REG_HSYNC_START_PIXEL_LSB);
+ dump_reg(sd, REG_HSYNC_START_PIXEL_MSB);
+ dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB);
+ dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB);
+ dump_reg(sd, REG_VSYNC_START_LINE_LSB);
+ dump_reg(sd, REG_VSYNC_START_LINE_MSB);
+ dump_reg(sd, REG_VSYNC_STOP_LINE_LSB);
+ dump_reg(sd, REG_VSYNC_STOP_LINE_MSB);
+ dump_reg(sd, REG_VBLK_START_LINE_LSB);
+ dump_reg(sd, REG_VBLK_START_LINE_MSB);
+ dump_reg(sd, REG_VBLK_STOP_LINE_LSB);
+ dump_reg(sd, REG_VBLK_STOP_LINE_MSB);
+ dump_reg(sd, REG_SYNC_CONTROL);
+ dump_reg(sd, REG_OUTPUT_FORMATTER1);
+ dump_reg(sd, REG_OUTPUT_FORMATTER2);
+ dump_reg(sd, REG_OUTPUT_FORMATTER3);
+ dump_reg(sd, REG_OUTPUT_FORMATTER4);
+ dump_reg(sd, REG_OUTPUT_FORMATTER5);
+ dump_reg(sd, REG_OUTPUT_FORMATTER6);
+ dump_reg(sd, REG_CLEAR_LOST_LOCK);
+}
+
+/**
+ * tvp514x_configure() - Configure the TVP5146/47 registers
+ * @sd: ptr to v4l2_subdev struct
+ * @decoder: ptr to tvp514x_decoder structure
+ *
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct v4l2_subdev *sd,
+ struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(sd, decoder->tvp514x_regs);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(sd);
+
+ return 0;
+}
+
+/**
+ * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision.
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @decoder: pointer to tvp514x_decoder structure
+ *
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct v4l2_subdev *sd,
+ struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION);
+
+ v4l2_dbg(1, debug, sd,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+
+ v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n",
+ client->name, decoder->ver,
+ client->addr << 1, client->adapter->name);
+ return 0;
+}
+
+/**
+ * tvp514x_querystd() - V4L2 decoder interface handler for querystd
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected then *std_id is set to 0 and the function returns 0.
+ */
+static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ *std_id = V4L2_STD_UNKNOWN;
+
+ /* query the current standard */
+ current_std = tvp514x_query_current_std(sd);
+ if (current_std == STD_INVALID)
+ return 0;
+
+ input_sel = decoder->input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return 0; /* No input detected */
+
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l2_dbg(1, debug, sd, "Current STD: %s\n",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * tvp514x_s_std() - V4L2 decoder interface handler for s_std
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err, i;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(sd, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ decoder->tvp514x_regs[REG_VIDEO_STD].val =
+ decoder->std_list[i].video_std;
+
+ v4l2_dbg(1, debug, sd, "Standard set to: %s\n",
+ decoder->std_list[i].standard.name);
+ return 0;
+}
+
+/**
+ * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @input: input selector for routing the signal
+ * @output: output selector for routing the signal
+ * @config: config value. Not used
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int tvp514x_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((input >= INPUT_INVALID) ||
+ (output >= OUTPUT_INVALID))
+ /* Index out of bound */
+ return -EINVAL;
+
+ /*
+ * For the sequence streamon -> streamoff and again s_input
+ * it fails to lock the signal, since streamoff puts TVP514x
+ * into power off state which leads to failure in sub-sequent s_input.
+ *
+ * So power up the TVP514x device here, since it is important to lock
+ * the signal at this stage.
+ */
+ if (!decoder->streaming)
+ tvp514x_s_stream(sd, 1);
+
+ input_sel = input;
+ output_sel = output;
+
+ err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(sd,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel;
+ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /* Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ sync_lock_status = tvp514x_read_reg(sd,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ /* Input detected */
+ break;
+ }
+
+ if (try_count < 0)
+ return -EINVAL;
+
+ decoder->input = input;
+ decoder->output = output;
+
+ v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel);
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl
+ * @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_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ int err = -EINVAL, value;
+
+ value = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ err = tvp514x_write_reg(sd, REG_CONTRAST, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ err = tvp514x_write_reg(sd, REG_SATURATION, 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;
+ err = tvp514x_write_reg(sd, REG_HUE, value);
+ if (!err)
+ decoder->tvp514x_regs[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c);
+ if (!err)
+ decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n",
+ ctrl->id, ctrl->val);
+ return err;
+}
+
+/**
+ * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @index: index of pixelcode to retrieve
+ * @code: receives the pixelcode
+ *
+ * Enumerates supported mediabus formats
+ */
+static int
+tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_YUYV10_2X10;
+ return 0;
+}
+
+/**
+ * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @f: pointer to the mediabus format structure
+ *
+ * Negotiates the image capture size and mediabus format.
+ */
+static int
+tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ /* Calculate height and width based on current standard */
+ current_std = decoder->current_std;
+
+ f->code = V4L2_MBUS_FMT_YUYV10_2X10;
+ f->width = decoder->std_list[current_std].width;
+ f->height = decoder->std_list[current_std].height;
+ f->field = V4L2_FIELD_INTERLACED;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n",
+ f->width, f->height);
+ return 0;
+}
+
+/**
+ * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = decoder->current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ /* only capture is supported */
+ return -EINVAL;
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = decoder->current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream
+ * @sd: pointer to standard V4L2 sub-device structure
+ * @enable: streaming enable or disable
+ *
+ * Sets streaming to enable or disable, if possible.
+ */
+static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ int err = 0;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+
+ if (decoder->streaming == enable)
+ return 0;
+
+ switch (enable) {
+ case 0:
+ {
+ /* Power Down Sequence */
+ err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01);
+ if (err) {
+ v4l2_err(sd, "Unable to turn off decoder\n");
+ return err;
+ }
+ decoder->streaming = enable;
+ break;
+ }
+ case 1:
+ {
+ struct tvp514x_reg *int_seq = (struct tvp514x_reg *)
+ client->driver->id_table->driver_data;
+
+ /* Power Up Sequence */
+ err = tvp514x_write_regs(sd, int_seq);
+ if (err) {
+ v4l2_err(sd, "Unable to turn on decoder\n");
+ return err;
+ }
+ /* Detect if not already detected */
+ err = tvp514x_detect(sd, decoder);
+ if (err) {
+ v4l2_err(sd, "Unable to detect decoder\n");
+ return err;
+ }
+ err = tvp514x_configure(sd, decoder);
+ if (err) {
+ v4l2_err(sd, "Unable to configure decoder\n");
+ return err;
+ }
+ decoder->streaming = enable;
+ break;
+ }
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+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,
+};
+
+static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
+ .s_routing = tvp514x_s_routing,
+ .querystd = tvp514x_querystd,
+ .enum_mbus_fmt = tvp514x_enum_mbus_fmt,
+ .g_mbus_fmt = tvp514x_mbus_fmt,
+ .try_mbus_fmt = tvp514x_mbus_fmt,
+ .s_mbus_fmt = tvp514x_mbus_fmt,
+ .g_parm = tvp514x_g_parm,
+ .s_parm = tvp514x_s_parm,
+ .s_stream = tvp514x_s_stream,
+};
+
+static const struct v4l2_subdev_ops tvp514x_ops = {
+ .core = &tvp514x_core_ops,
+ .video = &tvp514x_video_ops,
+};
+
+static struct tvp514x_decoder tvp514x_dev = {
+ .streaming = 0,
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+
+};
+
+/**
+ * tvp514x_probe() - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ * @id: i2c driver id table
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ if (!client->dev.platform_data) {
+ v4l2_err(client, "No platform data!!\n");
+ return -ENODEV;
+ }
+
+ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+
+ /* Initialize the tvp514x_decoder with default configuration */
+ *decoder = tvp514x_dev;
+ /* Copy default register configuration */
+ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default,
+ sizeof(tvp514x_reg_list_default));
+
+ /* Copy board specific information here */
+ decoder->pdata = client->dev.platform_data;
+
+ /**
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+ decoder->tvp514x_regs[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /* Set default standard to auto */
+ decoder->tvp514x_regs[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ /* Register with V4L2 layer as slave device */
+ 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;
+
+}
+
+/**
+ * tvp514x_remove() - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int tvp514x_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct tvp514x_decoder *decoder = to_decoder(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(&decoder->hdl);
+ kfree(decoder);
+ return 0;
+}
+/* TVP5146 Init/Power on Sequence */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/* TVP5147 Init/Power on Sequence */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/* TVP5146M2/TVP5147M1 Init/Power on Sequence */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_TERM, 0, 0},
+};
+
+/**
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)tvp5146_init_reg_seq},
+ {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq},
+ {"tvp5147", (unsigned long)tvp5147_init_reg_seq},
+ {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TVP514X_MODULE_NAME,
+ },
+ .probe = tvp514x_probe,
+ .remove = tvp514x_remove,
+ .id_table = tvp514x_id,
+};
+
+module_i2c_driver(tvp514x_driver);
diff --git a/drivers/media/i2c/tvp514x_regs.h b/drivers/media/i2c/tvp514x_regs.h
new file mode 100644
index 000000000000..d23aa2fbb9b1
--- /dev/null
+++ b/drivers/media/i2c/tvp514x_regs.h
@@ -0,0 +1,287 @@
+/*
+ * drivers/media/i2c/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
new file mode 100644
index 000000000000..31104a960652
--- /dev/null
+++ b/drivers/media/i2c/tvp5150.c
@@ -0,0 +1,1274 @@
+/*
+ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver
+ *
+ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <media/v4l2-device.h>
+#include <media/tvp5150.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+
+#include "tvp5150_reg.h"
+
+#define TVP5150_H_MAX 720
+#define TVP5150_V_MAX_525_60 480
+#define TVP5150_V_MAX_OTHERS 576
+#define TVP5150_MAX_CROP_LEFT 511
+#define TVP5150_MAX_CROP_TOP 127
+#define TVP5150_CROP_SHIFT 2
+
+MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
+MODULE_AUTHOR("Mauro Carvalho Chehab");
+MODULE_LICENSE("GPL");
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+struct tvp5150 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_rect rect;
+
+ v4l2_std_id norm; /* Current set standard */
+ u32 input;
+ u32 output;
+ int enable;
+};
+
+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);
+ unsigned char buffer[1];
+ int rc;
+
+ buffer[0] = addr;
+
+ rc = i2c_master_send(c, buffer, 1);
+ if (rc < 0) {
+ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
+
+ msleep(10);
+
+ rc = i2c_master_recv(c, buffer, 1);
+ if (rc < 0) {
+ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
+ return rc;
+ }
+
+ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
+
+ return (buffer[0]);
+}
+
+static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
+ unsigned char value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[2];
+ int rc;
+
+ buffer[0] = addr;
+ buffer[1] = value;
+ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
+ if (2 != (rc = i2c_master_send(c, buffer, 2)))
+ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
+}
+
+static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
+ const u8 end, int max_line)
+{
+ int i = 0;
+
+ while (init != (u8)(end + 1)) {
+ if ((i % max_line) == 0) {
+ if (i > 0)
+ printk("\n");
+ printk("tvp5150: %s reg 0x%02x = ", s, init);
+ }
+ printk("%02x ", tvp5150_read(sd, init));
+
+ init++;
+ i++;
+ }
+ printk("\n");
+}
+
+static int tvp5150_log_status(struct v4l2_subdev *sd)
+{
+ printk("tvp5150: Video input source selection #1 = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
+ printk("tvp5150: Analog channel controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
+ printk("tvp5150: Operation mode controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_OP_MODE_CTL));
+ printk("tvp5150: Miscellaneous controls = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MISC_CTL));
+ printk("tvp5150: Autoswitch mask= 0x%02x\n",
+ tvp5150_read(sd, TVP5150_AUTOSW_MSK));
+ printk("tvp5150: Color killer threshold control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
+ printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
+ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
+ printk("tvp5150: Brightness control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_BRIGHT_CTL));
+ printk("tvp5150: Color saturation control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_SATURATION_CTL));
+ printk("tvp5150: Hue control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_HUE_CTL));
+ printk("tvp5150: Contrast control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_CONTRAST_CTL));
+ printk("tvp5150: Outputs and data rates select = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
+ printk("tvp5150: Configuration shared pins = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
+ printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
+ printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
+ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
+ printk("tvp5150: Genlock/RTC = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_GENLOCK));
+ printk("tvp5150: Horizontal sync start = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
+ printk("tvp5150: Vertical blanking start = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
+ printk("tvp5150: Vertical blanking stop = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
+ printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
+ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
+ printk("tvp5150: Interrupt reset register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
+ printk("tvp5150: Interrupt enable register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
+ printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
+ printk("tvp5150: Video standard = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VIDEO_STD));
+ printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
+ tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
+ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
+ printk("tvp5150: Macrovision on counter = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
+ printk("tvp5150: Macrovision off counter = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
+ printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
+ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
+ printk("tvp5150: Device ID = %02x%02x\n",
+ tvp5150_read(sd, TVP5150_MSB_DEV_ID),
+ tvp5150_read(sd, TVP5150_LSB_DEV_ID));
+ printk("tvp5150: ROM version = (hex) %02x.%02x\n",
+ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
+ printk("tvp5150: Vertical line count = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
+ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
+ printk("tvp5150: Interrupt status register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
+ printk("tvp5150: Interrupt active register B = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
+ printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
+ tvp5150_read(sd, TVP5150_STATUS_REG_1),
+ tvp5150_read(sd, TVP5150_STATUS_REG_2),
+ tvp5150_read(sd, TVP5150_STATUS_REG_3),
+ tvp5150_read(sd, TVP5150_STATUS_REG_4),
+ tvp5150_read(sd, TVP5150_STATUS_REG_5));
+
+ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
+ TVP5150_TELETEXT_FIL1_END, 8);
+ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
+ TVP5150_TELETEXT_FIL2_END, 8);
+
+ printk("tvp5150: Teletext filter enable = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
+ printk("tvp5150: Interrupt status register A = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
+ printk("tvp5150: Interrupt enable register A = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
+ printk("tvp5150: Interrupt configuration = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_INT_CONF));
+ printk("tvp5150: VDP status register = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
+ printk("tvp5150: FIFO word count = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
+ printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
+ printk("tvp5150: FIFO reset = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_RESET));
+ printk("tvp5150: Line number interrupt = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
+ printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
+ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
+ printk("tvp5150: FIFO output control = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
+ printk("tvp5150: Full field enable = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
+ printk("tvp5150: Full field mode register = 0x%02x\n",
+ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
+
+ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
+ TVP5150_CC_DATA_END, 8);
+
+ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
+ TVP5150_WSS_DATA_END, 8);
+
+ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
+ TVP5150_VPS_DATA_END, 8);
+
+ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
+ TVP5150_VITC_DATA_END, 10);
+
+ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
+ TVP5150_LINE_MODE_END, 8);
+ return 0;
+}
+
+/****************************************************************************
+ Basic functions
+ ****************************************************************************/
+
+static inline void tvp5150_selmux(struct v4l2_subdev *sd)
+{
+ int opmode = 0;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int input = 0;
+ int val;
+
+ if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
+ input = 8;
+
+ switch (decoder->input) {
+ case TVP5150_COMPOSITE1:
+ input |= 2;
+ /* fall through */
+ case TVP5150_COMPOSITE0:
+ break;
+ case TVP5150_SVIDEO:
+ default:
+ input |= 1;
+ break;
+ }
+
+ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
+ "=> tvp5150 input=%i, opmode=%i\n",
+ decoder->input, decoder->output,
+ input, opmode);
+
+ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
+ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
+
+ /* Svideo should enable YCrCb output and disable GPCL output
+ * For Composite and TV, it should be the reverse
+ */
+ val = tvp5150_read(sd, TVP5150_MISC_CTL);
+ if (val < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n", __func__, val);
+ return;
+ }
+
+ if (decoder->input == TVP5150_SVIDEO)
+ val = (val & ~0x40) | 0x10;
+ else
+ val = (val & ~0x10) | 0x40;
+ tvp5150_write(sd, TVP5150_MISC_CTL, val);
+};
+
+struct i2c_reg_value {
+ unsigned char reg;
+ unsigned char value;
+};
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150_init_default[] = {
+ { /* 0x00 */
+ TVP5150_VD_IN_SRC_SEL_1,0x00
+ },
+ { /* 0x01 */
+ TVP5150_ANAL_CHL_CTL,0x15
+ },
+ { /* 0x02 */
+ TVP5150_OP_MODE_CTL,0x00
+ },
+ { /* 0x03 */
+ TVP5150_MISC_CTL,0x01
+ },
+ { /* 0x06 */
+ TVP5150_COLOR_KIL_THSH_CTL,0x10
+ },
+ { /* 0x07 */
+ TVP5150_LUMA_PROC_CTL_1,0x60
+ },
+ { /* 0x08 */
+ TVP5150_LUMA_PROC_CTL_2,0x00
+ },
+ { /* 0x09 */
+ TVP5150_BRIGHT_CTL,0x80
+ },
+ { /* 0x0a */
+ TVP5150_SATURATION_CTL,0x80
+ },
+ { /* 0x0b */
+ TVP5150_HUE_CTL,0x00
+ },
+ { /* 0x0c */
+ TVP5150_CONTRAST_CTL,0x80
+ },
+ { /* 0x0d */
+ TVP5150_DATA_RATE_SEL,0x47
+ },
+ { /* 0x0e */
+ TVP5150_LUMA_PROC_CTL_3,0x00
+ },
+ { /* 0x0f */
+ TVP5150_CONF_SHARED_PIN,0x08
+ },
+ { /* 0x11 */
+ TVP5150_ACT_VD_CROP_ST_MSB,0x00
+ },
+ { /* 0x12 */
+ TVP5150_ACT_VD_CROP_ST_LSB,0x00
+ },
+ { /* 0x13 */
+ TVP5150_ACT_VD_CROP_STP_MSB,0x00
+ },
+ { /* 0x14 */
+ TVP5150_ACT_VD_CROP_STP_LSB,0x00
+ },
+ { /* 0x15 */
+ TVP5150_GENLOCK,0x01
+ },
+ { /* 0x16 */
+ TVP5150_HORIZ_SYNC_START,0x80
+ },
+ { /* 0x18 */
+ TVP5150_VERT_BLANKING_START,0x00
+ },
+ { /* 0x19 */
+ TVP5150_VERT_BLANKING_STOP,0x00
+ },
+ { /* 0x1a */
+ TVP5150_CHROMA_PROC_CTL_1,0x0c
+ },
+ { /* 0x1b */
+ TVP5150_CHROMA_PROC_CTL_2,0x14
+ },
+ { /* 0x1c */
+ TVP5150_INT_RESET_REG_B,0x00
+ },
+ { /* 0x1d */
+ TVP5150_INT_ENABLE_REG_B,0x00
+ },
+ { /* 0x1e */
+ TVP5150_INTT_CONFIG_REG_B,0x00
+ },
+ { /* 0x28 */
+ TVP5150_VIDEO_STD,0x00
+ },
+ { /* 0x2e */
+ TVP5150_MACROVISION_ON_CTR,0x0f
+ },
+ { /* 0x2f */
+ TVP5150_MACROVISION_OFF_CTR,0x01
+ },
+ { /* 0xbb */
+ TVP5150_TELETEXT_FIL_ENA,0x00
+ },
+ { /* 0xc0 */
+ TVP5150_INT_STATUS_REG_A,0x00
+ },
+ { /* 0xc1 */
+ TVP5150_INT_ENABLE_REG_A,0x00
+ },
+ { /* 0xc2 */
+ TVP5150_INT_CONF,0x04
+ },
+ { /* 0xc8 */
+ TVP5150_FIFO_INT_THRESHOLD,0x80
+ },
+ { /* 0xc9 */
+ TVP5150_FIFO_RESET,0x00
+ },
+ { /* 0xca */
+ TVP5150_LINE_NUMBER_INT,0x00
+ },
+ { /* 0xcb */
+ TVP5150_PIX_ALIGN_REG_LOW,0x4e
+ },
+ { /* 0xcc */
+ TVP5150_PIX_ALIGN_REG_HIGH,0x00
+ },
+ { /* 0xcd */
+ TVP5150_FIFO_OUT_CTRL,0x01
+ },
+ { /* 0xcf */
+ TVP5150_FULL_FIELD_ENA,0x00
+ },
+ { /* 0xd0 */
+ TVP5150_LINE_MODE_INI,0x00
+ },
+ { /* 0xfc */
+ TVP5150_FULL_FIELD_MODE_REG,0x7f
+ },
+ { /* end of data */
+ 0xff,0xff
+ }
+};
+
+/* Default values as sugested at TVP5150AM1 datasheet */
+static const struct i2c_reg_value tvp5150_init_enable[] = {
+ {
+ TVP5150_CONF_SHARED_PIN, 2
+ },{ /* Automatic offset and AGC enabled */
+ TVP5150_ANAL_CHL_CTL, 0x15
+ },{ /* Activate YCrCb output 0x9 or 0xd ? */
+ TVP5150_MISC_CTL, 0x6f
+ },{ /* Activates video std autodetection for all standards */
+ TVP5150_AUTOSW_MSK, 0x0
+ },{ /* Default format: 0x47. For 4:2:2: 0x40 */
+ TVP5150_DATA_RATE_SEL, 0x47
+ },{
+ TVP5150_CHROMA_PROC_CTL_1, 0x0c
+ },{
+ TVP5150_CHROMA_PROC_CTL_2, 0x54
+ },{ /* Non documented, but initialized on WinTV USB2 */
+ 0x27, 0x20
+ },{
+ 0xff,0xff
+ }
+};
+
+struct tvp5150_vbi_type {
+ unsigned int vbi_type;
+ unsigned int ini_line;
+ unsigned int end_line;
+ unsigned int by_field :1;
+};
+
+struct i2c_vbi_ram_value {
+ u16 reg;
+ struct tvp5150_vbi_type type;
+ unsigned char values[16];
+};
+
+/* This struct have the values for each supported VBI Standard
+ * by
+ tvp5150_vbi_types should follow the same order as vbi_ram_default
+ * value 0 means rom position 0x10, value 1 means rom position 0x30
+ * and so on. There are 16 possible locations from 0 to 15.
+ */
+
+static struct i2c_vbi_ram_value vbi_ram_default[] =
+{
+ /* FIXME: Current api doesn't handle all VBI types, those not
+ yet supported are placed under #if 0 */
+#if 0
+ {0x010, /* Teletext, SECAM, WST System A */
+ {V4L2_SLICED_TELETEXT_SECAM,6,23,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
+ 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+#endif
+ {0x030, /* Teletext, PAL, WST System B */
+ {V4L2_SLICED_TELETEXT_B,6,22,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
+ 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+#if 0
+ {0x050, /* Teletext, PAL, WST System C */
+ {V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
+ 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x070, /* Teletext, NTSC, WST System B */
+ {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x090, /* Tetetext, NTSC NABTS System C */
+ {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
+ },
+ {0x0b0, /* Teletext, NTSC-J, NABTS System D */
+ {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
+ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
+ },
+ {0x0d0, /* Closed Caption, PAL/SECAM */
+ {V4L2_SLICED_CAPTION_625,22,22,1},
+ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
+ 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
+ },
+#endif
+ {0x0f0, /* Closed Caption, NTSC */
+ {V4L2_SLICED_CAPTION_525,21,21,1},
+ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
+ 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
+ },
+ {0x110, /* Wide Screen Signal, PAL/SECAM */
+ {V4L2_SLICED_WSS_625,23,23,1},
+ { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
+ 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
+ },
+#if 0
+ {0x130, /* Wide Screen Signal, NTSC C */
+ {V4L2_SLICED_WSS_525,20,20,1},
+ { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
+ 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
+ },
+ {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
+ {V4l2_SLICED_VITC_625,6,22,0},
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
+ 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
+ },
+ {0x170, /* Vertical Interval Timecode (VITC), NTSC */
+ {V4l2_SLICED_VITC_525,10,20,0},
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
+ 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
+ },
+#endif
+ {0x190, /* Video Program System (VPS), PAL */
+ {V4L2_SLICED_VPS,16,16,0},
+ { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
+ 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
+ },
+ /* 0x1d0 User programmable */
+
+ /* End of struct */
+ { (u16)-1 }
+};
+
+static int tvp5150_write_inittab(struct v4l2_subdev *sd,
+ const struct i2c_reg_value *regs)
+{
+ while (regs->reg != 0xff) {
+ tvp5150_write(sd, regs->reg, regs->value);
+ regs++;
+ }
+ return 0;
+}
+
+static int tvp5150_vdp_init(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs)
+{
+ unsigned int i;
+
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Before programming, Line mode should be at 0xff */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
+
+ /* Load Ram Table */
+ while (regs->reg != (u16)-1) {
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
+ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
+
+ for (i = 0; i < 16; i++)
+ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
+
+ regs++;
+ }
+ return 0;
+}
+
+/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
+static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
+ struct v4l2_sliced_vbi_cap *cap)
+{
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
+ int line;
+
+ v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n");
+ memset(cap, 0, sizeof *cap);
+
+ while (regs->reg != (u16)-1 ) {
+ for (line=regs->type.ini_line;line<=regs->type.end_line;line++) {
+ cap->service_lines[0][line] |= regs->type.vbi_type;
+ }
+ cap->service_set |= regs->type.vbi_type;
+
+ regs++;
+ }
+ return 0;
+}
+
+/* Set vbi processing
+ * type - one of tvp5150_vbi_types
+ * line - line to gather data
+ * fields: bit 0 field1, bit 1, field2
+ * flags (default=0xf0) is a bitmask, were set means:
+ * bit 7: enable filtering null bytes on CC
+ * bit 6: send data also to FIFO
+ * bit 5: don't allow data with errors on FIFO
+ * bit 4: enable ECC when possible
+ * pix_align = pix alignment:
+ * LSB = field1
+ * MSB = field2
+ */
+static int tvp5150_set_vbi(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs,
+ unsigned int type,u8 flags, int line,
+ const int fields)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
+ u8 reg;
+ int pos=0;
+
+ if (std == V4L2_STD_ALL) {
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
+ return 0;
+ } else if (std & V4L2_STD_625_50) {
+ /* Don't follow NTSC Line number convension */
+ line += 3;
+ }
+
+ if (line<6||line>27)
+ return 0;
+
+ while (regs->reg != (u16)-1 ) {
+ if ((type & regs->type.vbi_type) &&
+ (line>=regs->type.ini_line) &&
+ (line<=regs->type.end_line)) {
+ type=regs->type.vbi_type;
+ break;
+ }
+
+ regs++;
+ pos++;
+ }
+ if (regs->reg == (u16)-1)
+ return 0;
+
+ type=pos | (flags & 0xf0);
+ reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
+
+ if (fields&1) {
+ tvp5150_write(sd, reg, type);
+ }
+
+ if (fields&2) {
+ tvp5150_write(sd, reg+1, type);
+ }
+
+ return type;
+}
+
+static int tvp5150_get_vbi(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs, int line)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std = decoder->norm;
+ u8 reg;
+ int pos, type = 0;
+ int i, ret = 0;
+
+ if (std == V4L2_STD_ALL) {
+ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
+ return 0;
+ } else if (std & V4L2_STD_625_50) {
+ /* Don't follow NTSC Line number convension */
+ line += 3;
+ }
+
+ if (line < 6 || line > 27)
+ return 0;
+
+ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
+
+ for (i = 0; i <= 1; i++) {
+ ret = tvp5150_read(sd, reg + i);
+ if (ret < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n",
+ __func__, ret);
+ return 0;
+ }
+ pos = ret & 0x0f;
+ if (pos < 0x0f)
+ type |= regs[pos].type.vbi_type;
+ }
+
+ return type;
+}
+
+static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ int fmt = 0;
+
+ decoder->norm = std;
+
+ /* First tests should be against specific std */
+
+ if (std == V4L2_STD_ALL) {
+ fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */
+ } else if (std & V4L2_STD_NTSC_443) {
+ fmt = VIDEO_STD_NTSC_4_43_BIT;
+ } else if (std & V4L2_STD_PAL_M) {
+ fmt = VIDEO_STD_PAL_M_BIT;
+ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
+ fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
+ } else {
+ /* Then, test against generic ones */
+ if (std & V4L2_STD_NTSC)
+ fmt = VIDEO_STD_NTSC_MJ_BIT;
+ else if (std & V4L2_STD_PAL)
+ fmt = VIDEO_STD_PAL_BDGHIN_BIT;
+ else if (std & V4L2_STD_SECAM)
+ fmt = VIDEO_STD_SECAM_BIT;
+ }
+
+ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
+ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
+ return 0;
+}
+
+static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (decoder->norm == std)
+ return 0;
+
+ /* Change cropping height limits */
+ if (std & V4L2_STD_525_60)
+ decoder->rect.height = TVP5150_V_MAX_525_60;
+ else
+ decoder->rect.height = TVP5150_V_MAX_OTHERS;
+
+
+ return tvp5150_set_std(sd, std);
+}
+
+static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ /* Initializes TVP5150 to its default values */
+ tvp5150_write_inittab(sd, tvp5150_init_default);
+
+ /* Initializes VDP registers */
+ tvp5150_vdp_init(sd, vbi_ram_default);
+
+ /* Selects decoder input */
+ tvp5150_selmux(sd);
+
+ /* Initializes TVP5150 to stream enabled values */
+ tvp5150_write_inittab(sd, tvp5150_init_enable);
+
+ /* Initialize image preferences */
+ v4l2_ctrl_handler_setup(&decoder->hdl);
+
+ tvp5150_set_std(sd, decoder->norm);
+ return 0;
+};
+
+static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_CONTRAST:
+ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
+ return 0;
+ case V4L2_CID_HUE:
+ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
+{
+ int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
+
+ switch (val & 0x0F) {
+ case 0x01:
+ return V4L2_STD_NTSC;
+ case 0x03:
+ return V4L2_STD_PAL;
+ case 0x05:
+ return V4L2_STD_PAL_M;
+ case 0x07:
+ return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
+ case 0x09:
+ return V4L2_STD_NTSC_443;
+ case 0xb:
+ return V4L2_STD_SECAM;
+ default:
+ return V4L2_STD_UNKNOWN;
+ }
+}
+
+static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *f)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ if (f == NULL)
+ return -EINVAL;
+
+ tvp5150_reset(sd, 0);
+
+ f->width = decoder->rect.width;
+ f->height = decoder->rect.height;
+
+ f->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ f->field = V4L2_FIELD_SEQ_TB;
+ f->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
+ f->height);
+ return 0;
+}
+
+static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std;
+ int hmax;
+
+ v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
+ __func__, rect.left, rect.top, rect.width, rect.height);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* tvp5150 has some special limits */
+ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
+ rect.width = clamp(rect.width,
+ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
+ TVP5150_H_MAX - rect.left);
+ rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ hmax = TVP5150_V_MAX_525_60;
+ else
+ hmax = TVP5150_V_MAX_OTHERS;
+
+ rect.height = clamp(rect.height,
+ hmax - TVP5150_MAX_CROP_TOP - rect.top,
+ hmax - rect.top);
+
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
+ rect.top + rect.height - hmax);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
+ rect.left >> TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
+ rect.left | (1 << TVP5150_CROP_SHIFT));
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
+ (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
+ TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
+ rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
+
+ decoder->rect = rect;
+
+ return 0;
+}
+
+static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+
+ a->c = decoder->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+ v4l2_std_id std;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = TVP5150_H_MAX;
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ a->bounds.height = TVP5150_V_MAX_525_60;
+ else
+ a->bounds.height = TVP5150_V_MAX_OTHERS;
+
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+/****************************************************************************
+ I2C Command
+ ****************************************************************************/
+
+static int tvp5150_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct tvp5150 *decoder = to_tvp5150(sd);
+
+ decoder->input = input;
+ decoder->output = output;
+ tvp5150_selmux(sd);
+ return 0;
+}
+
+static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ /* this is for capturing 36 raw vbi lines
+ if there's a way to cut off the beginning 2 vbi lines
+ with the tvp5150 then the vbi line count could be lowered
+ to 17 lines/field again, although I couldn't find a register
+ which could do that cropping */
+ if (fmt->sample_format == V4L2_PIX_FMT_GREY)
+ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
+ if (fmt->count[0] == 18 && fmt->count[1] == 18) {
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
+ }
+ return 0;
+}
+
+static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ int i;
+
+ if (svbi->service_set != 0) {
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[1][i] = 0;
+ svbi->service_lines[0][i] =
+ tvp5150_set_vbi(sd, vbi_ram_default,
+ svbi->service_lines[0][i], 0xf0, i, 3);
+ }
+ /* Enables FIFO */
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
+ } else {
+ /* Disables FIFO*/
+ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
+
+ /* Disable Full Field */
+ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
+
+ /* Disable Line modes */
+ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
+ tvp5150_write(sd, i, 0xff);
+ }
+ return 0;
+}
+
+static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ int i, mask = 0;
+
+ memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
+
+ for (i = 0; i <= 23; i++) {
+ svbi->service_lines[0][i] =
+ tvp5150_get_vbi(sd, vbi_ram_default, i);
+ mask |= svbi->service_lines[0][i];
+ }
+ svbi->service_set = mask;
+ return 0;
+}
+
+static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
+ tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
+ rev);
+}
+
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ int res;
+
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ res = tvp5150_read(sd, reg->reg & 0xff);
+ if (res < 0) {
+ v4l2_err(sd, "%s: failed with error = %d\n", __func__, res);
+ return res;
+ }
+
+ reg->val = res;
+ reg->size = 1;
+ return 0;
+}
+
+static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ int status = tvp5150_read(sd, 0x88);
+
+ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+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_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,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = tvp5150_g_register,
+ .s_register = tvp5150_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
+ .g_tuner = tvp5150_g_tuner,
+};
+
+static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
+ .s_routing = tvp5150_s_routing,
+ .enum_mbus_fmt = tvp5150_enum_mbus_fmt,
+ .s_mbus_fmt = tvp5150_mbus_fmt,
+ .try_mbus_fmt = tvp5150_mbus_fmt,
+ .g_mbus_fmt = tvp5150_mbus_fmt,
+ .s_crop = tvp5150_s_crop,
+ .g_crop = tvp5150_g_crop,
+ .cropcap = tvp5150_cropcap,
+};
+
+static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
+ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
+ .g_sliced_fmt = tvp5150_g_sliced_fmt,
+ .s_sliced_fmt = tvp5150_s_sliced_fmt,
+ .s_raw_fmt = tvp5150_s_raw_fmt,
+};
+
+static const struct v4l2_subdev_ops tvp5150_ops = {
+ .core = &tvp5150_core_ops,
+ .tuner = &tvp5150_tuner_ops,
+ .video = &tvp5150_video_ops,
+ .vbi = &tvp5150_vbi_ops,
+};
+
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+
+static int tvp5150_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ struct tvp5150 *core;
+ struct v4l2_subdev *sd;
+ int tvp5150_id[4];
+ int i, res;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
+ if (!core) {
+ return -ENOMEM;
+ }
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
+
+ /*
+ * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
+ * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
+ */
+ for (i = 0; i < 4; i++) {
+ res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
+ if (res < 0)
+ goto free_core;
+ tvp5150_id[i] = res;
+ }
+
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
+
+ if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */
+ v4l2_info(sd, "tvp%02x%02xam1 detected.\n",
+ tvp5150_id[0], tvp5150_id[1]);
+
+ /* ITU-T BT.656.4 timing */
+ tvp5150_write(sd, TVP5150_REV_SELECT, 0);
+ } else {
+ /* Is TVP5150A */
+ if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
+ v4l2_info(sd, "tvp%02x%02xa detected.\n",
+ tvp5150_id[2], tvp5150_id[3]);
+ } else {
+ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
+ tvp5150_id[2], tvp5150_id[3]);
+ v4l2_info(sd, "*** Rom ver is %d.%d\n",
+ tvp5150_id[2], tvp5150_id[3]);
+ }
+ }
+
+ core->norm = V4L2_STD_ALL; /* Default is autodetect */
+ core->input = TVP5150_COMPOSITE1;
+ core->enable = 1;
+
+ 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) {
+ res = core->hdl.error;
+ v4l2_ctrl_handler_free(&core->hdl);
+ goto free_core;
+ }
+ v4l2_ctrl_handler_setup(&core->hdl);
+
+ /* Default is no cropping */
+ core->rect.top = 0;
+ if (tvp5150_read_std(sd) & V4L2_STD_525_60)
+ core->rect.height = TVP5150_V_MAX_525_60;
+ else
+ core->rect.height = TVP5150_V_MAX_OTHERS;
+ core->rect.left = 0;
+ core->rect.width = TVP5150_H_MAX;
+
+ if (debug > 1)
+ tvp5150_log_status(sd);
+ return 0;
+
+free_core:
+ kfree(core);
+ return res;
+}
+
+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;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id tvp5150_id[] = {
+ { "tvp5150", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+
+static struct i2c_driver tvp5150_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tvp5150",
+ },
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+ .id_table = tvp5150_id,
+};
+
+module_i2c_driver(tvp5150_driver);
diff --git a/drivers/media/video/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h
index 25a994944918..25a994944918 100644
--- a/drivers/media/video/tvp5150_reg.h
+++ b/drivers/media/i2c/tvp5150_reg.h
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/i2c/tvp7002.c
index fb6a5b57eb83..fb6a5b57eb83 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/i2c/tvp7002_reg.h
index 0e34ca9bccf3..0e34ca9bccf3 100644
--- a/drivers/media/video/tvp7002_reg.h
+++ b/drivers/media/i2c/tvp7002_reg.h
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/i2c/upd64031a.c
index 1e7446542091..1e7446542091 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/i2c/upd64031a.c
diff --git a/drivers/media/video/upd64083.c b/drivers/media/i2c/upd64083.c
index 75d6acc62018..75d6acc62018 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/i2c/upd64083.c
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/i2c/vp27smpx.c
index 7cfbc9d94a48..7cfbc9d94a48 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/i2c/vp27smpx.c
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/i2c/vpx3220.c
index 2f67b4c5c823..2f67b4c5c823 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/i2c/vpx3220.c
diff --git a/drivers/media/video/vs6624.c b/drivers/media/i2c/vs6624.c
index 42ae9dc9c574..42ae9dc9c574 100644
--- a/drivers/media/video/vs6624.c
+++ b/drivers/media/i2c/vs6624.c
diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/i2c/vs6624_regs.h
index 6ba2ee25827e..6ba2ee25827e 100644
--- a/drivers/media/video/vs6624_regs.h
+++ b/drivers/media/i2c/vs6624_regs.h
diff --git a/drivers/media/video/wm8739.c b/drivers/media/i2c/wm8739.c
index 3bb99e93febe..3bb99e93febe 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/i2c/wm8739.c
diff --git a/drivers/media/video/wm8775.c b/drivers/media/i2c/wm8775.c
index bee77ea9f49e..bee77ea9f49e 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/i2c/wm8775.c
diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
index 6f9eb94e85b3..d01fcb7e87c2 100644
--- a/drivers/media/media-device.c
+++ b/drivers/media/media-device.c
@@ -59,7 +59,9 @@ static int media_device_get_info(struct media_device *dev,
info.hw_revision = dev->hw_revision;
info.driver_version = dev->driver_version;
- return copy_to_user(__info, &info, sizeof(*__info));
+ if (copy_to_user(__info, &info, sizeof(*__info)))
+ return -EFAULT;
+ return 0;
}
static struct media_entity *find_entity(struct media_device *mdev, u32 id)
diff --git a/drivers/media/media-devnode.c b/drivers/media/media-devnode.c
index f6b52d549430..023b2a1cbb9b 100644
--- a/drivers/media/media-devnode.c
+++ b/drivers/media/media-devnode.c
@@ -30,6 +30,8 @@
* counting.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -215,7 +217,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev)
minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0);
if (minor == MEDIA_NUM_DEVICES) {
mutex_unlock(&media_devnode_lock);
- printk(KERN_ERR "could not get a free minor\n");
+ pr_err("could not get a free minor\n");
return -ENFILE;
}
@@ -230,7 +232,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev)
ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
if (ret < 0) {
- printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+ pr_err("%s: cdev_add failed\n", __func__);
goto error;
}
@@ -243,7 +245,7 @@ int __must_check media_devnode_register(struct media_devnode *mdev)
dev_set_name(&mdev->dev, "media%d", mdev->minor);
ret = device_register(&mdev->dev);
if (ret < 0) {
- printk(KERN_ERR "%s: device_register failed\n", __func__);
+ pr_err("%s: device_register failed\n", __func__);
goto error;
}
@@ -287,18 +289,18 @@ static int __init media_devnode_init(void)
{
int ret;
- printk(KERN_INFO "Linux media interface: v0.10\n");
+ pr_info("Linux media interface: v0.10\n");
ret = alloc_chrdev_region(&media_dev_t, 0, MEDIA_NUM_DEVICES,
MEDIA_NAME);
if (ret < 0) {
- printk(KERN_WARNING "media: unable to allocate major\n");
+ pr_warn("unable to allocate major\n");
return ret;
}
ret = bus_register(&media_bus_type);
if (ret < 0) {
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
- printk(KERN_WARNING "media: bus_register failed\n");
+ pr_warn("bus_register failed\n");
return -EIO;
}
diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig
new file mode 100644
index 000000000000..8c30ada27c79
--- /dev/null
+++ b/drivers/media/mmc/Kconfig
@@ -0,0 +1,2 @@
+comment "Supported MMC/SDIO adapters"
+source "drivers/media/mmc/siano/Kconfig"
diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile
new file mode 100644
index 000000000000..31e297a202fb
--- /dev/null
+++ b/drivers/media/mmc/Makefile
@@ -0,0 +1 @@
+obj-y += siano/
diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig
new file mode 100644
index 000000000000..fa62475be3bf
--- /dev/null
+++ b/drivers/media/mmc/siano/Kconfig
@@ -0,0 +1,10 @@
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config SMS_SDIO_DRV
+ tristate "Siano SMS1xxx based MDTV via SDIO interface"
+ depends on DVB_CORE && RC_CORE && HAS_DMA
+ depends on MMC
+ ---help---
+ Choose if you would like to have Siano's support for SDIO interface
diff --git a/drivers/media/mmc/siano/Makefile b/drivers/media/mmc/siano/Makefile
new file mode 100644
index 000000000000..0e01f973db6b
--- /dev/null
+++ b/drivers/media/mmc/siano/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/common/siano
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
+
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c
index d6f3f100699a..d6f3f100699a 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/mmc/siano/smssdio.c
diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig
new file mode 100644
index 000000000000..ece13dcff07d
--- /dev/null
+++ b/drivers/media/parport/Kconfig
@@ -0,0 +1,52 @@
+menuconfig MEDIA_PARPORT_SUPPORT
+ bool "ISA and parallel port devices"
+ depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT
+ help
+ Enables drivers for ISA and parallel port bus. If you
+ need media drivers using those legacy buses, say Y.
+
+if MEDIA_PARPORT_SUPPORT
+config VIDEO_BWQCAM
+ tristate "Quickcam BW Video For Linux"
+ depends on PARPORT && VIDEO_V4L2
+ help
+ Say Y have if you the black and white version of the QuickCam
+ camera. See the next option for the color version.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bw-qcam.
+
+config VIDEO_CQCAM
+ tristate "QuickCam Colour Video For Linux"
+ depends on PARPORT && VIDEO_V4L2
+ help
+ This is the video4linux driver for the colour version of the
+ Connectix QuickCam. If you have one of these cameras, say Y here,
+ otherwise say N. This driver does not work with the original
+ monochrome QuickCam, QuickCam VC or QuickClip. It is also available
+ as a module (c-qcam).
+ Read <file:Documentation/video4linux/CQcam.txt> for more information.
+
+config VIDEO_PMS
+ tristate "Mediavision Pro Movie Studio Video For Linux"
+ depends on ISA && VIDEO_V4L2
+ help
+ Say Y if you have the ISA Mediavision Pro Movie Studio
+ capture card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pms.
+
+config VIDEO_W9966
+ tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
+ depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
+ help
+ Video4linux driver for Winbond's w9966 based Webcams.
+ Currently tested with the LifeView FlyCam Supra.
+ If you have one of these cameras, say Y here
+ otherwise say N.
+ This driver is also available as a module (w9966).
+
+ Check out <file:Documentation/video4linux/w9966.txt> for more
+ information.
+endif
diff --git a/drivers/media/parport/Makefile b/drivers/media/parport/Makefile
new file mode 100644
index 000000000000..4eea06d7af5b
--- /dev/null
+++ b/drivers/media/parport/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
+obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
+obj-$(CONFIG_VIDEO_W9966) += w9966.o
+obj-$(CONFIG_VIDEO_PMS) += pms.o
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/parport/bw-qcam.c
index 5b75a64b199b..5b75a64b199b 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/parport/bw-qcam.c
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/parport/c-qcam.c
index ec51e1f12e82..ec51e1f12e82 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/parport/c-qcam.c
diff --git a/drivers/media/video/pms.c b/drivers/media/parport/pms.c
index 77f9c92186f4..77f9c92186f4 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/parport/pms.c
diff --git a/drivers/media/video/w9966.c b/drivers/media/parport/w9966.c
index db2a6003a1c3..db2a6003a1c3 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/parport/w9966.c
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
new file mode 100644
index 000000000000..d4e2ed3f27e5
--- /dev/null
+++ b/drivers/media/pci/Kconfig
@@ -0,0 +1,47 @@
+menuconfig MEDIA_PCI_SUPPORT
+ bool "Media PCI Adapters"
+ depends on PCI && MEDIA_SUPPORT
+ help
+ Enable media drivers for PCI/PCIe bus.
+ If you have such devices, say Y.
+
+if MEDIA_PCI_SUPPORT
+
+if MEDIA_CAMERA_SUPPORT
+ comment "Media capture support"
+source "drivers/media/pci/meye/Kconfig"
+source "drivers/media/pci/sta2x11/Kconfig"
+endif
+
+if MEDIA_ANALOG_TV_SUPPORT
+ comment "Media capture/analog TV support"
+source "drivers/media/pci/ivtv/Kconfig"
+source "drivers/media/pci/zoran/Kconfig"
+source "drivers/media/pci/saa7146/Kconfig"
+endif
+
+if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT
+ comment "Media capture/analog/hybrid TV support"
+source "drivers/media/pci/cx18/Kconfig"
+source "drivers/media/pci/cx23885/Kconfig"
+source "drivers/media/pci/cx25821/Kconfig"
+source "drivers/media/pci/cx88/Kconfig"
+source "drivers/media/pci/bt8xx/Kconfig"
+source "drivers/media/pci/saa7134/Kconfig"
+source "drivers/media/pci/saa7164/Kconfig"
+
+endif
+
+if MEDIA_DIGITAL_TV_SUPPORT
+ comment "Media digital TV PCI Adapters"
+source "drivers/media/pci/ttpci/Kconfig"
+source "drivers/media/pci/b2c2/Kconfig"
+source "drivers/media/pci/pluto2/Kconfig"
+source "drivers/media/pci/dm1105/Kconfig"
+source "drivers/media/pci/pt1/Kconfig"
+source "drivers/media/pci/mantis/Kconfig"
+source "drivers/media/pci/ngene/Kconfig"
+source "drivers/media/pci/ddbridge/Kconfig"
+endif
+
+endif #MEDIA_PCI_SUPPORT
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
new file mode 100644
index 000000000000..35cc57862c01
--- /dev/null
+++ b/drivers/media/pci/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for the kernel multimedia device drivers.
+#
+
+obj-y += ttpci/ \
+ b2c2/ \
+ pluto2/ \
+ dm1105/ \
+ pt1/ \
+ mantis/ \
+ ngene/ \
+ ddbridge/ \
+ b2c2/ \
+ saa7146/
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv/
+obj-$(CONFIG_VIDEO_ZORAN) += zoran/
+obj-$(CONFIG_VIDEO_CX18) += cx18/
+obj-$(CONFIG_VIDEO_CX23885) += cx23885/
+obj-$(CONFIG_VIDEO_CX25821) += cx25821/
+obj-$(CONFIG_VIDEO_CX88) += cx88/
+obj-$(CONFIG_VIDEO_BT848) += bt8xx/
+obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
+obj-$(CONFIG_VIDEO_MEYE) += meye/
+obj-$(CONFIG_STA2X11_VIP) += sta2x11/
diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig
new file mode 100644
index 000000000000..58761a21caa0
--- /dev/null
+++ b/drivers/media/pci/b2c2/Kconfig
@@ -0,0 +1,15 @@
+config DVB_B2C2_FLEXCOP_PCI
+ tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI"
+ depends on DVB_CORE && I2C
+ help
+ Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2.
+
+ Say Y if you own such a device and want to use it.
+
+config DVB_B2C2_FLEXCOP_PCI_DEBUG
+ bool "Enable debug for the B2C2 FlexCop drivers"
+ depends on DVB_B2C2_FLEXCOP_PCI
+ select DVB_B2C2_FLEXCOP_DEBUG
+ help
+ Say Y if you want to enable the module option to control debug messages
+ of all B2C2 FlexCop drivers.
diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile
new file mode 100644
index 000000000000..b894320a5f21
--- /dev/null
+++ b/drivers/media/pci/b2c2/Makefile
@@ -0,0 +1,9 @@
+ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),)
+b2c2-flexcop-pci-objs += flexcop-dma.o
+endif
+
+b2c2-flexcop-pci-objs += flexcop-pci.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/common/b2c2/
diff --git a/drivers/media/dvb/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c
index 2881e0d956ad..2881e0d956ad 100644
--- a/drivers/media/dvb/b2c2/flexcop-dma.c
+++ b/drivers/media/pci/b2c2/flexcop-dma.c
diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c
index 44f8fb5f17ff..44f8fb5f17ff 100644
--- a/drivers/media/dvb/b2c2/flexcop-pci.c
+++ b/drivers/media/pci/b2c2/flexcop-pci.c
diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig
new file mode 100644
index 000000000000..61d09e010814
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Kconfig
@@ -0,0 +1,43 @@
+config VIDEO_BT848
+ tristate "BT848 Video For Linux"
+ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEOBUF_DMA_SG
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ Support for BT848 based frame grabber/overlay boards. This includes
+ the Miro, Hauppauge and STB boards. Please read the material in
+ <file:Documentation/video4linux/bttv/> for more information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bttv.
+
+config DVB_BT8XX
+ tristate "DVB/ATSC Support for bt878 based TV cards"
+ depends on DVB_CORE && PCI && I2C && VIDEO_BT848
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+ the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
+ the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and
+ some AVerMedia cards.
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the PCI bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y if you own such a device and want to use it.
diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile
new file mode 100644
index 000000000000..5f06597c6a6e
--- /dev/null
+++ b/drivers/media/pci/bt8xx/Makefile
@@ -0,0 +1,11 @@
+bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
+ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
+ bttv-input.o bttv-audio-hook.o
+
+obj-$(CONFIG_VIDEO_BT848) += bttv.o
+obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/video/bt8xx/bt848.h b/drivers/media/pci/bt8xx/bt848.h
index c37e6acffded..c37e6acffded 100644
--- a/drivers/media/video/bt8xx/bt848.h
+++ b/drivers/media/pci/bt8xx/bt848.h
diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c
index b34fa95185e4..b34fa95185e4 100644
--- a/drivers/media/dvb/bt8xx/bt878.c
+++ b/drivers/media/pci/bt8xx/bt878.c
diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h
index d19b59299d78..d19b59299d78 100644
--- a/drivers/media/dvb/bt8xx/bt878.h
+++ b/drivers/media/pci/bt8xx/bt878.h
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c
index 2364d16586b3..2364d16586b3 100644
--- a/drivers/media/video/bt8xx/bttv-audio-hook.c
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.c
diff --git a/drivers/media/video/bt8xx/bttv-audio-hook.h b/drivers/media/pci/bt8xx/bttv-audio-hook.h
index 159d07adeff8..159d07adeff8 100644
--- a/drivers/media/video/bt8xx/bttv-audio-hook.h
+++ b/drivers/media/pci/bt8xx/bttv-audio-hook.h
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c
index 38952faaffda..38952faaffda 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/pci/bt8xx/bttv-cards.c
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
new file mode 100644
index 000000000000..16f5ca23698c
--- /dev/null
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -0,0 +1,4630 @@
+/*
+
+ bttv - Bt848 frame grabber driver
+
+ Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de>
+ & Marcus Metzler <mocm@thp.uni-koeln.de>
+ (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+
+ some v4l2 code lines are taken from Justin's bttv2 driver which is
+ (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+
+ V4L1 removal from:
+ (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
+
+ Fixes to be fully V4L2 compliant by
+ (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ Cropping and overscan support
+ Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
+ Sponsored by OPQ Systems AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include "bttvp.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tvaudio.h>
+#include <media/msp3400.h>
+
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <media/saa6588.h>
+
+#define BTTV_VERSION "0.9.19"
+
+unsigned int bttv_num; /* number of Bt848s in use */
+struct bttv *bttvs[BTTV_MAX];
+
+unsigned int bttv_debug;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio;
+
+/* config variables */
+#ifdef __BIG_ENDIAN
+static unsigned int bigendian=1;
+#else
+static unsigned int bigendian;
+#endif
+static unsigned int radio[BTTV_MAX];
+static unsigned int irq_debug;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int reset_crop = 1;
+
+static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
+static int debug_latency;
+static int disable_ir;
+
+static unsigned int fdsr;
+
+/* options */
+static unsigned int combfilter;
+static unsigned int lumafilter;
+static unsigned int automute = 1;
+static unsigned int chroma_agc;
+static unsigned int adc_crush = 1;
+static unsigned int whitecrush_upper = 0xCF;
+static unsigned int whitecrush_lower = 0x7F;
+static unsigned int vcr_hack;
+static unsigned int irq_iswitch;
+static unsigned int uv_ratio = 50;
+static unsigned int full_luma_range;
+static unsigned int coring;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int v4l2 = 1;
+
+/* insmod args */
+module_param(bttv_verbose, int, 0644);
+module_param(bttv_gpio, int, 0644);
+module_param(bttv_debug, int, 0644);
+module_param(irq_debug, int, 0644);
+module_param(debug_latency, int, 0644);
+module_param(disable_ir, int, 0444);
+
+module_param(fdsr, int, 0444);
+module_param(gbuffers, int, 0444);
+module_param(gbufsize, int, 0444);
+module_param(reset_crop, int, 0444);
+
+module_param(v4l2, int, 0644);
+module_param(bigendian, int, 0644);
+module_param(irq_iswitch, int, 0644);
+module_param(combfilter, int, 0444);
+module_param(lumafilter, int, 0444);
+module_param(automute, int, 0444);
+module_param(chroma_agc, int, 0444);
+module_param(adc_crush, int, 0444);
+module_param(whitecrush_upper, int, 0444);
+module_param(whitecrush_lower, int, 0444);
+module_param(vcr_hack, int, 0444);
+module_param(uv_ratio, int, 0444);
+module_param(full_luma_range, int, 0444);
+module_param(coring, int, 0444);
+
+module_param_array(radio, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
+MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
+MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
+MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
+MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
+MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
+MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
+ "is 1 (yes) for compatibility with older applications");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
+MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
+MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
+MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
+MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
+MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
+MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
+MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(BTTV_VERSION);
+
+/* ----------------------------------------------------------------------- */
+/* sysfs */
+
+static ssize_t show_card(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vfd = container_of(cd, struct video_device, dev);
+ struct bttv *btv = video_get_drvdata(vfd);
+ return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+}
+static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+
+/* ----------------------------------------------------------------------- */
+/* dvb auto-load setup */
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ request_module("dvb-bt8xx");
+}
+
+static void request_modules(struct bttv *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct bttv *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+
+/* ----------------------------------------------------------------------- */
+/* static data */
+
+/* special timing tables from conexant... */
+static u8 SRAM_Table[][60] =
+{
+ /* PAL digital input over GPIO[7:0] */
+ {
+ 45, // 45 bytes following
+ 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
+ 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
+ 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
+ 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
+ 0x37,0x00,0xAF,0x21,0x00
+ },
+ /* NTSC digital input over GPIO[7:0] */
+ {
+ 51, // 51 bytes following
+ 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
+ 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
+ 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
+ 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
+ 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
+ 0x00,
+ },
+ // TGB_NTSC392 // quartzsight
+ // This table has been modified to be used for Fusion Rev D
+ {
+ 0x2A, // size of table = 42
+ 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
+ 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
+ 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
+ 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
+ 0x20, 0x00
+ }
+};
+
+/* minhdelayx1 first video pixel we can capture on a line and
+ hdelayx1 start of active video, both relative to rising edge of
+ /HRESET pulse (0H) in 1 / fCLKx1.
+ swidth width of active video and
+ totalwidth total line width, both in 1 / fCLKx1.
+ sqwidth total line width in square pixels.
+ vdelay start of active video in 2 * field lines relative to
+ trailing edge of /VRESET pulse (VDELAY register).
+ sheight height of active video in 2 * field lines.
+ videostart0 ITU-R frame line number of the line corresponding
+ to vdelay in the first field. */
+#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \
+ vdelay, sheight, videostart0) \
+ .cropcap.bounds.left = minhdelayx1, \
+ /* * 2 because vertically we count field lines times two, */ \
+ /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \
+ .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
+ /* 4 is a safety margin at the end of the line. */ \
+ .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \
+ .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \
+ .cropcap.defrect.left = hdelayx1, \
+ .cropcap.defrect.top = (videostart0) * 2, \
+ .cropcap.defrect.width = swidth, \
+ .cropcap.defrect.height = sheight, \
+ .cropcap.pixelaspect.numerator = totalwidth, \
+ .cropcap.pixelaspect.denominator = sqwidth,
+
+const struct bttv_tvnorm bttv_tvnorms[] = {
+ /* PAL-BDGHI */
+ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
+ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
+ {
+ .v4l2_id = V4L2_STD_PAL,
+ .name = "PAL",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x20,
+ .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */
+ .sram = 0,
+ /* ITU-R frame line number of the first VBI line
+ we can capture, of the first and second field.
+ The last line is determined by cropcap.bounds. */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* Should be (768 * 1135 + 944 / 2) / 944.
+ cropcap.defrect is used for image width
+ checks, so we keep the old value 924. */
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ /* bt878 (and bt848?) can capture another
+ line below active video. */
+ .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
+ .name = "NTSC",
+ .Fsc = 28636363,
+ .swidth = 768,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+ .scaledtwidth = 910,
+ .hdelayx1 = 128,
+ .hactivex1 = 910,
+ .vdelay = 0x1a,
+ .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */
+ .sram = 1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 128,
+ /* Should be (640 * 910 + 780 / 2) / 780? */
+ /* swidth */ 768,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_SECAM,
+ .name = "SECAM",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0xb0,
+ .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 255,
+ .sram = 0, /* like PAL, correct? */
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_Nc,
+ .name = "PAL-Nc",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 576,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 130,
+ .hactivex1 = 734,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 130,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_M,
+ .name = "PAL-M",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x1a,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_PAL_N,
+ .name = "PAL-N",
+ .Fsc = 35468950,
+ .swidth = 768,
+ .sheight = 576,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+ .scaledtwidth = 944,
+ .hdelayx1 = 186,
+ .hactivex1 = 922,
+ .vdelay = 0x20,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 7, 320 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ (768 * 1135 + 944 / 2) / 944,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x20,
+ /* sheight */ 576,
+ /* videostart0 */ 23)
+ },{
+ .v4l2_id = V4L2_STD_NTSC_M_JP,
+ .name = "NTSC-JP",
+ .Fsc = 28636363,
+ .swidth = 640,
+ .sheight = 480,
+ .totalwidth = 910,
+ .adelay = 0x68,
+ .bdelay = 0x5d,
+ .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+ .scaledtwidth = 780,
+ .hdelayx1 = 135,
+ .hactivex1 = 754,
+ .vdelay = 0x16,
+ .vbipack = 144,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 135,
+ /* swidth */ (640 * 910 + 780 / 2) / 780,
+ /* totalwidth */ 910,
+ /* sqwidth */ 780,
+ /* vdelay */ 0x16,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ },{
+ /* that one hopefully works with the strange timing
+ * which video recorders produce when playing a NTSC
+ * tape on a PAL TV ... */
+ .v4l2_id = V4L2_STD_PAL_60,
+ .name = "PAL-60",
+ .Fsc = 35468950,
+ .swidth = 924,
+ .sheight = 480,
+ .totalwidth = 1135,
+ .adelay = 0x7f,
+ .bdelay = 0x72,
+ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ .scaledtwidth = 1135,
+ .hdelayx1 = 186,
+ .hactivex1 = 924,
+ .vdelay = 0x1a,
+ .vbipack = 255,
+ .vtotal = 524,
+ .sram = -1,
+ .vbistart = { 10, 273 },
+ CROPCAP(/* minhdelayx1 */ 68,
+ /* hdelayx1 */ 186,
+ /* swidth */ 924,
+ /* totalwidth */ 1135,
+ /* sqwidth */ 944,
+ /* vdelay */ 0x1a,
+ /* sheight */ 480,
+ /* videostart0 */ 23)
+ }
+};
+static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
+
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+ packed pixel formats must come first */
+static const struct bttv_format formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .btformat = BT848_COLOR_FMT_Y8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "8 bpp, dithered color",
+ .fourcc = V4L2_PIX_FMT_HI240,
+ .btformat = BT848_COLOR_FMT_RGB8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .btformat = BT848_COLOR_FMT_RGB15,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .btformat = BT848_COLOR_FMT_RGB16,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .btformat = BT848_COLOR_FMT_RGB24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .btformat = BT848_COLOR_FMT_RGB32,
+ .btswap = 0x0f, /* byte+word swap */
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .btformat = BT848_COLOR_FMT_YUY2,
+ .btswap = 0x03, /* byteswap */
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .btformat = BT848_COLOR_FMT_YCrCb422,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:1:1, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 0,
+ },{
+ .name = "4:1:0, planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "4:1:0, planar, Y-Cr-Cb",
+ .fourcc = V4L2_PIX_FMT_YVU410,
+ .btformat = BT848_COLOR_FMT_YCrCb411,
+ .depth = 9,
+ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+ .hshift = 2,
+ .vshift = 2,
+ },{
+ .name = "raw scanlines",
+ .fourcc = -1,
+ .btformat = BT848_COLOR_FMT_RAW,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_RAW,
+ }
+};
+static const unsigned int FORMATS = ARRAY_SIZE(formats);
+
+/* ----------------------------------------------------------------------- */
+
+#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5)
+#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8)
+#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9)
+#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11)
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 27648,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 128,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 256,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .name = "Balance",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BASS,
+ .name = "Bass",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .name = "Treble",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_CHROMA_AGC,
+ .name = "chroma agc",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_COMBFILTER,
+ .name = "combfilter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_LUMAFILTER,
+ .name = "luma decimation filter",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_AGC_CRUSH,
+ .name = "agc crush",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_VCR_HACK,
+ .name = "vcr hack",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
+ .name = "whitecrush upper",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0xCF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
+ .name = "whitecrush lower",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 0x7F,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_UV_RATIO,
+ .name = "uv ratio",
+ .minimum = 0,
+ .maximum = 100,
+ .step = 1,
+ .default_value = 50,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
+ .name = "full luma range",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_CORING,
+ .name = "coring",
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+
+
+
+};
+
+static const struct v4l2_queryctrl *ctrl_by_id(int id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
+ if (bttv_ctls[i].id == id)
+ return bttv_ctls+i;
+
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+/*
+ RESOURCE_ allocated by freed by
+
+ VIDEO_READ bttv_read 1) bttv_read 2)
+
+ VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ VIDIOCMCAPTURE 1)
+
+ OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off
+ VIDIOC_OVERLAY on VIDIOC_OVERLAY off
+ 3) bttv_release
+
+ VBI VIDIOC_STREAMON VIDIOC_STREAMOFF
+ VIDIOC_QBUF 1) bttv_release
+ bttv_read, bttv_poll 1) 4)
+
+ 1) The resource must be allocated when we enter buffer prepare functions
+ and remain allocated while buffers are in the DMA queue.
+ 2) This is a single frame read.
+ 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
+ RESOURCE_OVERLAY is allocated.
+ 4) This is a continuous read, implies VIDIOC_STREAMON.
+
+ Note this driver permits video input and standard changes regardless if
+ resources are allocated.
+*/
+
+#define VBI_RESOURCES (RESOURCE_VBI)
+#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
+ RESOURCE_VIDEO_STREAM | \
+ RESOURCE_OVERLAY)
+
+static
+int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
+{
+ int xbits; /* mutual exclusive resources */
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ xbits = bit;
+ if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
+ xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
+
+ /* is it free? */
+ if (btv->resources & xbits) {
+ /* no, someone else uses it */
+ goto fail;
+ }
+
+ if ((bit & VIDEO_RESOURCES)
+ && 0 == (btv->resources & VIDEO_RESOURCES)) {
+ /* Do crop - use current, don't - use default parameters. */
+ __s32 top = btv->crop[!!fh->do_crop].rect.top;
+
+ if (btv->vbi_end > top)
+ goto fail;
+
+ /* We cannot capture the same line as video and VBI data.
+ Claim scan lines crop[].rect.top to bottom. */
+ btv->crop_start = top;
+ } else if (bit & VBI_RESOURCES) {
+ __s32 end = fh->vbi_fmt.end;
+
+ if (end > btv->crop_start)
+ goto fail;
+
+ /* Claim scan lines above fh->vbi_fmt.end. */
+ btv->vbi_end = end;
+ }
+
+ /* it's free, grab it */
+ fh->resources |= bit;
+ btv->resources |= bit;
+ return 1;
+
+ fail:
+ return 0;
+}
+
+static
+int check_btres(struct bttv_fh *fh, int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int locked_btres(struct bttv *btv, int bit)
+{
+ return (btv->resources & bit);
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_vbi_lines(struct bttv *btv)
+{
+ btv->vbi_end = 0;
+}
+
+/* Call with btv->lock down. */
+static void
+disclaim_video_lines(struct bttv *btv)
+{
+ const struct bttv_tvnorm *tvnorm;
+ u8 crop;
+
+ tvnorm = &bttv_tvnorms[btv->tvnorm];
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+
+ /* VBI capturing ends at VDELAY, start of video capturing, no
+ matter how many lines the VBI RISC program expects. When video
+ capturing is off, it shall no longer "preempt" VBI capturing,
+ so we set VDELAY to maximum. */
+ crop = btread(BT848_E_CROP) | 0xc0;
+ btwrite(crop, BT848_E_CROP);
+ btwrite(0xfe, BT848_E_VDELAY_LO);
+ btwrite(crop, BT848_O_CROP);
+ btwrite(0xfe, BT848_O_VDELAY_LO);
+}
+
+static
+void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
+{
+ if ((fh->resources & bits) != bits) {
+ /* trying to free resources not allocated by us ... */
+ pr_err("BUG! (btres)\n");
+ }
+ fh->resources &= ~bits;
+ btv->resources &= ~bits;
+
+ bits = btv->resources;
+
+ if (0 == (bits & VIDEO_RESOURCES))
+ disclaim_video_lines(btv);
+
+ if (0 == (bits & VBI_RESOURCES))
+ disclaim_vbi_lines(btv);
+}
+
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
+
+/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
+ PLL_X = Reference pre-divider (0=1, 1=2)
+ PLL_C = Post divider (0=6, 1=4)
+ PLL_I = Integer input
+ PLL_F = Fractional input
+
+ F_input = 28.636363 MHz:
+ PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
+*/
+
+static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
+{
+ unsigned char fl, fh, fi;
+
+ /* prevent overflows */
+ fin/=4;
+ fout/=4;
+
+ fout*=12;
+ fi=fout/fin;
+
+ fout=(fout%fin)*256;
+ fh=fout/fin;
+
+ fout=(fout%fin)*256;
+ fl=fout/fin;
+
+ btwrite(fl, BT848_PLL_F_LO);
+ btwrite(fh, BT848_PLL_F_HI);
+ btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
+}
+
+static void set_pll(struct bttv *btv)
+{
+ int i;
+
+ if (!btv->pll.pll_crystal)
+ return;
+
+ if (btv->pll.pll_ofreq == btv->pll.pll_current) {
+ dprintk("%d: PLL: no change required\n", btv->c.nr);
+ return;
+ }
+
+ if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
+ /* no PLL needed */
+ if (btv->pll.pll_current == 0)
+ return;
+ if (bttv_verbose)
+ pr_info("%d: PLL can sleep, using XTAL (%d)\n",
+ btv->c.nr, btv->pll.pll_ifreq);
+ btwrite(0x00,BT848_TGCTRL);
+ btwrite(0x00,BT848_PLL_XCI);
+ btv->pll.pll_current = 0;
+ return;
+ }
+
+ if (bttv_verbose)
+ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
+ btv->c.nr,
+ btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+ set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+
+ for (i=0; i<10; i++) {
+ /* Let other people run while the PLL stabilizes */
+ msleep(10);
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+ btwrite(0,BT848_DSTATUS);
+ } else {
+ btwrite(0x08,BT848_TGCTRL);
+ btv->pll.pll_current = btv->pll.pll_ofreq;
+ if (bttv_verbose)
+ pr_info("PLL set ok\n");
+ return;
+ }
+ }
+ btv->pll.pll_current = -1;
+ if (bttv_verbose)
+ pr_info("Setting PLL failed\n");
+ return;
+}
+
+/* used to switch between the bt848's analog/digital video capture modes */
+static void bt848A_set_timing(struct bttv *btv)
+{
+ int i, len;
+ int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+ int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
+
+ if (btv->input == btv->dig) {
+ dprintk("%d: load digital timing table (table_idx=%d)\n",
+ btv->c.nr,table_idx);
+
+ /* timing change...reset timing generator address */
+ btwrite(0x00, BT848_TGCTRL);
+ btwrite(0x02, BT848_TGCTRL);
+ btwrite(0x00, BT848_TGCTRL);
+
+ len=SRAM_Table[table_idx][0];
+ for(i = 1; i <= len; i++)
+ btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
+ btv->pll.pll_ofreq = 27000000;
+
+ set_pll(btv);
+ btwrite(0x11, BT848_TGCTRL);
+ btwrite(0x41, BT848_DVSIF);
+ } else {
+ btv->pll.pll_ofreq = fsc;
+ set_pll(btv);
+ btwrite(0x0, BT848_DVSIF);
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
+{
+ int value;
+
+ // printk("set bright: %d\n", bright); // DEBUG
+ btv->bright = bright;
+
+ /* We want -128 to 127 we get 0-65535 */
+ value = (bright >> 8) - 128;
+ btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+ int value;
+
+ btv->hue = hue;
+
+ /* -128 to 127 */
+ value = (hue >> 8) - 128;
+ btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+ int value,hibit;
+
+ btv->contrast = cont;
+
+ /* 0-511 */
+ value = (cont >> 7);
+ hibit = (value >> 6) & 4;
+ btwrite(value & 0xff, BT848_CONTRAST_LO);
+ btaor(hibit, ~4, BT848_E_CONTROL);
+ btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+ int val_u,val_v,hibits;
+
+ btv->saturation = color;
+
+ /* 0-511 for the color */
+ val_u = ((color * btv->opt_uv_ratio) / 50) >> 7;
+ val_v = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
+ hibits = (val_u >> 7) & 2;
+ hibits |= (val_v >> 8) & 1;
+ btwrite(val_u & 0xff, BT848_SAT_U_LO);
+ btwrite(val_v & 0xff, BT848_SAT_V_LO);
+ btaor(hibits, ~3, BT848_E_CONTROL);
+ btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+ int mux,mask2;
+
+ if (input >= bttv_tvcards[btv->c.type].video_inputs)
+ return -EINVAL;
+
+ /* needed by RemoteVideo MX */
+ mask2 = bttv_tvcards[btv->c.type].gpiomask2;
+ if (mask2)
+ gpio_inout(mask2,mask2);
+
+ if (input == btv->svhs) {
+ btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
+ } else {
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+ mux = bttv_muxsel(btv, input);
+ btaor(mux<<5, ~(3<<5), BT848_IFORM);
+ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
+
+ /* card specific hook */
+ if(bttv_tvcards[btv->c.type].muxsel_hook)
+ bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
+ return 0;
+}
+
+static char *audio_modes[] = {
+ "audio: tuner", "audio: radio", "audio: extern",
+ "audio: intern", "audio: mute"
+};
+
+static int
+audio_mux(struct bttv *btv, int input, int mute)
+{
+ int gpio_val, signal;
+ struct v4l2_control ctrl;
+
+ gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+ bttv_tvcards[btv->c.type].gpiomask);
+ signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
+
+ btv->mute = mute;
+ btv->audio = input;
+
+ /* automute */
+ mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
+
+ if (mute)
+ gpio_val = bttv_tvcards[btv->c.type].gpiomute;
+ else
+ gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
+ break;
+
+ default:
+ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
+ }
+
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+ if (in_interrupt())
+ return 0;
+
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = btv->mute;
+ bttv_call_all(btv, core, s_ctrl, &ctrl);
+ if (btv->sd_msp34xx) {
+ u32 in;
+
+ /* Note: the inputs tuner/radio/extern/intern are translated
+ to msp routings. This assumes common behavior for all msp3400
+ based TV cards. When this assumption fails, then the
+ specific MSP routing must be added to the card table.
+ For now this is sufficient. */
+ switch (input) {
+ case TVAUDIO_INPUT_RADIO:
+ /* Some boards need the msp do to the radio demod */
+ if (btv->radio_uses_msp_demodulator) {
+ in = MSP_INPUT_DEFAULT;
+ break;
+ }
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_EXTERN:
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_INTERN:
+ /* Yes, this is the same input as for RADIO. I doubt
+ if this is ever used. The only board with an INTERN
+ input is the BTTV_BOARD_AVERMEDIA98. I wonder how
+ that was tested. My guess is that the whole INTERN
+ input does not work. */
+ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case TVAUDIO_INPUT_TUNER:
+ default:
+ /* This is the only card that uses TUNER2, and afaik,
+ is the only difference between the VOODOOTV_FM
+ and VOODOOTV_200 */
+ if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
+ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
+ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
+ else
+ in = MSP_INPUT_DEFAULT;
+ break;
+ }
+ v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
+ in, MSP_OUTPUT_DEFAULT, 0);
+ }
+ if (btv->sd_tvaudio) {
+ v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
+ input, 0, 0);
+ }
+ return 0;
+}
+
+static inline int
+audio_mute(struct bttv *btv, int mute)
+{
+ return audio_mux(btv, btv->audio, mute);
+}
+
+static inline int
+audio_input(struct bttv *btv, int input)
+{
+ return audio_mux(btv, input, btv->mute);
+}
+
+static void
+bttv_crop_calc_limits(struct bttv_crop *c)
+{
+ /* Scale factor min. 1:1, max. 16:1. Min. image size
+ 48 x 32. Scaled width must be a multiple of 4. */
+
+ if (1) {
+ /* For bug compatibility with VIDIOCGCAP and image
+ size checks in earlier driver versions. */
+ c->min_scaled_width = 48;
+ c->min_scaled_height = 32;
+ } else {
+ c->min_scaled_width =
+ (max(48, c->rect.width >> 4) + 3) & ~3;
+ c->min_scaled_height =
+ max(32, c->rect.height >> 4);
+ }
+
+ c->max_scaled_width = c->rect.width & ~3;
+ c->max_scaled_height = c->rect.height;
+}
+
+static void
+bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+{
+ c->rect = bttv_tvnorms[norm].cropcap.defrect;
+ bttv_crop_calc_limits(c);
+}
+
+/* Call with btv->lock down. */
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+ const struct bttv_tvnorm *tvnorm;
+ v4l2_std_id id;
+
+ BUG_ON(norm >= BTTV_TVNORMS);
+ BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+
+ tvnorm = &bttv_tvnorms[norm];
+
+ if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
+ sizeof (tvnorm->cropcap))) {
+ bttv_crop_reset(&btv->crop[0], norm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+
+ if (0 == (btv->resources & VIDEO_RESOURCES)) {
+ btv->crop_start = tvnorm->cropcap.bounds.top
+ + tvnorm->cropcap.bounds.height;
+ }
+ }
+
+ btv->tvnorm = norm;
+
+ btwrite(tvnorm->adelay, BT848_ADELAY);
+ btwrite(tvnorm->bdelay, BT848_BDELAY);
+ btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+ BT848_IFORM);
+ btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+ btwrite(1, BT848_VBI_PACK_DEL);
+ bt848A_set_timing(btv);
+
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+ bttv_tda9880_setnorm(btv, gpio_read());
+ break;
+ }
+ id = tvnorm->v4l2_id;
+ bttv_call_all(btv, core, s_std, id);
+
+ return 0;
+}
+
+/* Call with btv->lock down. */
+static void
+set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+{
+ unsigned long flags;
+
+ btv->input = input;
+ if (irq_iswitch) {
+ spin_lock_irqsave(&btv->s_lock,flags);
+ if (btv->curr.frame_irq) {
+ /* active capture -> delayed input switch */
+ btv->new_input = input;
+ } else {
+ video_mux(btv,input);
+ }
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ } else {
+ video_mux(btv,input);
+ }
+ audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
+ TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+ set_tvnorm(btv, norm);
+}
+
+static void init_irqreg(struct bttv *btv)
+{
+ /* clear status */
+ btwrite(0xfffffUL, BT848_INT_STAT);
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* i2c only */
+ btwrite(BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ } else {
+ /* full video */
+ btwrite((btv->triton1) |
+ (btv->gpioirq ? BT848_INT_GPINT : 0) |
+ BT848_INT_SCERR |
+ (fdsr ? BT848_INT_FDSR : 0) |
+ BT848_INT_RISCI | BT848_INT_OCERR |
+ BT848_INT_FMTCHG|BT848_INT_HLOCK|
+ BT848_INT_I2CDONE,
+ BT848_INT_MASK);
+ }
+}
+
+static void init_bt848(struct bttv *btv)
+{
+ int val;
+
+ if (bttv_tvcards[btv->c.type].no_video) {
+ /* very basic init only */
+ init_irqreg(btv);
+ return;
+ }
+
+ btwrite(0x00, BT848_CAP_CTL);
+ btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+ btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+ /* set planar and packed mode trigger points and */
+ /* set rising edge of inverted GPINTR pin as irq trigger */
+ btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+ BT848_GPIO_DMA_CTL_PLTP1_16|
+ BT848_GPIO_DMA_CTL_PLTP23_16|
+ BT848_GPIO_DMA_CTL_GPINTC|
+ BT848_GPIO_DMA_CTL_GPINTI,
+ BT848_GPIO_DMA_CTL);
+
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+
+ btwrite(0x20, BT848_E_VSCALE_HI);
+ btwrite(0x20, BT848_O_VSCALE_HI);
+ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+
+ btwrite(whitecrush_upper, BT848_WC_UP);
+ btwrite(whitecrush_lower, BT848_WC_DOWN);
+
+ if (btv->opt_lumafilter) {
+ btwrite(0, BT848_E_CONTROL);
+ btwrite(0, BT848_O_CONTROL);
+ } else {
+ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+
+ bt848_bright(btv, btv->bright);
+ bt848_hue(btv, btv->hue);
+ bt848_contrast(btv, btv->contrast);
+ bt848_sat(btv, btv->saturation);
+
+ /* interrupt */
+ init_irqreg(btv);
+}
+
+static void bttv_reinit_bt848(struct bttv *btv)
+{
+ unsigned long flags;
+
+ if (bttv_verbose)
+ pr_info("%d: reset, reinitialize\n", btv->c.nr);
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->errors=0;
+ bttv_set_dma(btv,0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ init_bt848(btv);
+ btv->pll.pll_current = -1;
+ set_input(btv, btv->input, btv->tvnorm);
+}
+
+static int bttv_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = btv->bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = btv->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = btv->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = btv->saturation;
+ break;
+
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, g_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ c->value = btv->opt_chroma_agc;
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ c->value = btv->opt_combfilter;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ c->value = btv->opt_lumafilter;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = btv->opt_automute;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ c->value = btv->opt_adc_crush;
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ c->value = btv->opt_vcr_hack;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ c->value = btv->opt_whitecrush_upper;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ c->value = btv->opt_whitecrush_lower;
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ c->value = btv->opt_uv_ratio;
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ c->value = btv->opt_full_luma_range;
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ c->value = btv->opt_coring;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int bttv_s_ctrl(struct file *file, void *f,
+ struct v4l2_control *c)
+{
+ int err;
+ int val;
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ bt848_bright(btv, c->value);
+ break;
+ case V4L2_CID_HUE:
+ bt848_hue(btv, c->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ bt848_contrast(btv, c->value);
+ break;
+ case V4L2_CID_SATURATION:
+ bt848_sat(btv, c->value);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ audio_mute(btv, c->value);
+ /* fall through */
+ case V4L2_CID_AUDIO_VOLUME:
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ bttv_call_all(btv, core, s_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+ btv->opt_chroma_agc = c->value;
+ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+ btwrite(val, BT848_E_SCLOOP);
+ btwrite(val, BT848_O_SCLOOP);
+ break;
+ case V4L2_CID_PRIVATE_COMBFILTER:
+ btv->opt_combfilter = c->value;
+ break;
+ case V4L2_CID_PRIVATE_LUMAFILTER:
+ btv->opt_lumafilter = c->value;
+ if (btv->opt_lumafilter) {
+ btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ } else {
+ btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+ btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+ }
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ btv->opt_automute = c->value;
+ break;
+ case V4L2_CID_PRIVATE_AGC_CRUSH:
+ btv->opt_adc_crush = c->value;
+ btwrite(BT848_ADC_RESERVED |
+ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+ BT848_ADC);
+ break;
+ case V4L2_CID_PRIVATE_VCR_HACK:
+ btv->opt_vcr_hack = c->value;
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
+ btv->opt_whitecrush_upper = c->value;
+ btwrite(c->value, BT848_WC_UP);
+ break;
+ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
+ btv->opt_whitecrush_lower = c->value;
+ btwrite(c->value, BT848_WC_DOWN);
+ break;
+ case V4L2_CID_PRIVATE_UV_RATIO:
+ btv->opt_uv_ratio = c->value;
+ bt848_sat(btv, btv->saturation);
+ break;
+ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
+ btv->opt_full_luma_range = c->value;
+ btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
+ break;
+ case V4L2_CID_PRIVATE_CORING:
+ btv->opt_coring = c->value;
+ btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
+{
+ unsigned int outbits, data;
+ outbits = btread(BT848_GPIO_OUT_EN);
+ data = btread(BT848_GPIO_DATA);
+ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+ btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
+}
+
+static void bttv_field_count(struct bttv *btv)
+{
+ int need_count = 0;
+
+ if (btv->users)
+ need_count++;
+
+ if (need_count) {
+ /* start field counter */
+ btor(BT848_INT_VSYNC,BT848_INT_MASK);
+ } else {
+ /* stop field counter */
+ btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+ btv->field_count = 0;
+ }
+}
+
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (-1 == formats[i].fourcc)
+ continue;
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ }
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers */
+
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+ struct bttv_buffer *new)
+{
+ struct bttv_buffer *old;
+ unsigned long flags;
+ int retval = 0;
+
+ dprintk("switch_overlay: enter [new=%p]\n", new);
+ if (new)
+ new->vb.state = VIDEOBUF_DONE;
+ spin_lock_irqsave(&btv->s_lock,flags);
+ old = btv->screen;
+ btv->screen = new;
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ if (NULL != old) {
+ dprintk("switch_overlay: old=%p state is %d\n",
+ old, old->vb.state);
+ bttv_dma_free(&fh->cap,btv, old);
+ kfree(old);
+ }
+ if (NULL == new)
+ free_btres_lock(btv,fh,RESOURCE_OVERLAY);
+ dprintk("switch_overlay: done\n");
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface */
+
+static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
+ struct bttv_buffer *buf,
+ const struct bttv_format *fmt,
+ unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ struct bttv_fh *fh = q->priv_data;
+ int redo_dma_risc = 0;
+ struct bttv_crop c;
+ int norm;
+ int rc;
+
+ /* check settings */
+ if (NULL == fmt)
+ return -EINVAL;
+ if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+ width = RAW_BPL;
+ height = RAW_LINES*2;
+ if (width*height > buf->vb.bsize)
+ return -EINVAL;
+ buf->vb.size = buf->vb.bsize;
+
+ /* Make sure tvnorm and vbi_end remain consistent
+ until we're done. */
+
+ norm = btv->tvnorm;
+
+ /* In this mode capturing always starts at defrect.top
+ (default VDELAY), ignoring cropping parameters. */
+ if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
+ return -EINVAL;
+ }
+
+ c.rect = bttv_tvnorms[norm].cropcap.defrect;
+ } else {
+ norm = btv->tvnorm;
+ c = btv->crop[!!fh->do_crop];
+
+ if (width < c.min_scaled_width ||
+ width > c.max_scaled_width ||
+ height < c.min_scaled_height)
+ return -EINVAL;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ /* btv->crop counts frame lines. Max. scale
+ factor is 16:1 for frames, 8:1 for fields. */
+ if (height * 2 > c.max_scaled_height)
+ return -EINVAL;
+ break;
+
+ default:
+ if (height > c.max_scaled_height)
+ return -EINVAL;
+ break;
+ }
+
+ buf->vb.size = (width * height * fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+ }
+
+ /* alloc + fill struct bttv_buffer (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->vb.field != field ||
+ buf->tvnorm != norm || buf->fmt != fmt ||
+ buf->crop.top != c.rect.top ||
+ buf->crop.left != c.rect.left ||
+ buf->crop.width != c.rect.width ||
+ buf->crop.height != c.rect.height) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = norm;
+ buf->fmt = fmt;
+ buf->crop = c.rect;
+ redo_dma_risc = 1;
+ }
+
+ /* alloc risc memory */
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ redo_dma_risc = 1;
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
+ goto fail;
+ }
+
+ if (redo_dma_risc)
+ if (0 != (rc = bttv_buffer_risc(btv,buf)))
+ goto fail;
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ bttv_dma_free(q,btv,buf);
+ return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct bttv_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ if (*size * *count > gbuffers * gbufsize)
+ *count = (gbuffers * gbufsize) / *size;
+ return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
+ fh->width, fh->height, field);
+}
+
+static void
+buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+ struct bttv *btv = fh->btv;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue,&btv->capture);
+ if (!btv->curr.frame_irq) {
+ btv->loop_irq |= 1;
+ bttv_set_dma(btv, 0x03);
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
+ struct bttv_fh *fh = q->priv_data;
+
+ bttv_dma_free(q,fh->btv,buf);
+}
+
+static struct videobuf_queue_ops bttv_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ unsigned int i;
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (err)
+ goto err;
+
+ for (i = 0; i < BTTV_TVNORMS; i++)
+ if (*id & bttv_tvnorms[i].v4l2_id)
+ break;
+ if (i == BTTV_TVNORMS) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_tvnorm(btv, i);
+
+err:
+
+ return err;
+}
+
+static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
+ *id = V4L2_STD_625_50;
+ else
+ *id = V4L2_STD_525_60;
+ return 0;
+}
+
+static int bttv_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int rc = 0;
+
+ if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->audioset = 1;
+
+ if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+ } else if (i->index == btv->svhs) {
+ sprintf(i->name, "S-Video");
+ } else {
+ sprintf(i->name, "Composite%d", i->index);
+ }
+
+ if (i->index == btv->input) {
+ __u32 dstatus = btread(BT848_DSTATUS);
+ if (0 == (dstatus & BT848_DSTATUS_PRES))
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if (0 == (dstatus & BT848_DSTATUS_HLOC))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+
+ i->std = BTTV_NORMS;
+
+err:
+
+ return rc;
+}
+
+static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ *i = btv->input;
+
+ return 0;
+}
+
+static int bttv_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ int err;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (i > bttv_tvcards[btv->c.type].video_inputs) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ set_input(btv, i, btv->tvnorm);
+
+err:
+ return 0;
+}
+
+static int bttv_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(0 != t->index))
+ return -EINVAL;
+
+ if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
+
+err:
+
+ return 0;
+}
+
+static int bttv_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = btv->freq;
+
+ return 0;
+}
+
+static int bttv_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int err;
+
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ err = v4l2_prio_check(&btv->prio, fh->prio);
+ if (unlikely(err))
+ goto err;
+
+ if (unlikely(f->type != (btv->radio_user
+ ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
+ err = -EINVAL;
+ goto err;
+ }
+ btv->freq = f->frequency;
+ bttv_call_all(btv, tuner, s_frequency, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+err:
+
+ return 0;
+}
+
+static int bttv_log_status(struct file *file, void *f)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ bttv_call_all(btv, core, log_status);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bttv_g_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ reg->val = btread(reg->reg);
+ reg->size = 1;
+
+ return 0;
+}
+
+static int bttv_s_register(struct file *file, void *f,
+ struct v4l2_dbg_register *reg)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ /* bt848 has a 12-bit register space */
+ reg->reg &= 0xfff;
+ btwrite(reg->val, reg->reg);
+
+ return 0;
+}
+#endif
+
+/* Given cropping boundaries b and the scaled width and height of a
+ single field or frame, which must not exceed hardware limits, this
+ function adjusts the cropping parameters c. */
+static void
+bttv_crop_adjust (struct bttv_crop * c,
+ const struct v4l2_rect * b,
+ __s32 width,
+ __s32 height,
+ enum v4l2_field field)
+{
+ __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
+ __s32 max_left;
+ __s32 max_top;
+
+ if (width < c->min_scaled_width) {
+ /* Max. hor. scale factor 16:1. */
+ c->rect.width = width * 16;
+ } else if (width > c->max_scaled_width) {
+ /* Min. hor. scale factor 1:1. */
+ c->rect.width = width;
+
+ max_left = b->left + b->width - width;
+ max_left = min(max_left, (__s32) MAX_HDELAY);
+ if (c->rect.left > max_left)
+ c->rect.left = max_left;
+ }
+
+ if (height < c->min_scaled_height) {
+ /* Max. vert. scale factor 16:1, single fields 8:1. */
+ c->rect.height = height * 16;
+ } else if (frame_height > c->max_scaled_height) {
+ /* Min. vert. scale factor 1:1.
+ Top and height count field lines times two. */
+ c->rect.height = (frame_height + 1) & ~1;
+
+ max_top = b->top + b->height - c->rect.height;
+ if (c->rect.top > max_top)
+ c->rect.top = max_top;
+ }
+
+ bttv_crop_calc_limits(c);
+}
+
+/* Returns an error if scaling to a frame or single field with the given
+ width and height is not possible with the current cropping parameters
+ and width aligned according to width_mask. If adjust_size is TRUE the
+ function may adjust the width and/or height instead, rounding width
+ to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
+ also adjust the current cropping parameters to get closer to the
+ desired image size. */
+static int
+limit_scaled_size_lock (struct bttv_fh * fh,
+ __s32 * width,
+ __s32 * height,
+ enum v4l2_field field,
+ unsigned int width_mask,
+ unsigned int width_bias,
+ int adjust_size,
+ int adjust_crop)
+{
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ struct bttv_crop *c;
+ __s32 min_width;
+ __s32 min_height;
+ __s32 max_width;
+ __s32 max_height;
+ int rc;
+
+ BUG_ON((int) width_mask >= 0 ||
+ width_bias >= (unsigned int) -width_mask);
+
+ /* Make sure tvnorm, vbi_end and the current cropping parameters
+ remain consistent until we're done. */
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ /* Do crop - use current, don't - use default parameters. */
+ c = &btv->crop[!!fh->do_crop];
+
+ if (fh->do_crop
+ && adjust_size
+ && adjust_crop
+ && !locked_btres(btv, VIDEO_RESOURCES)) {
+ min_width = 48;
+ min_height = 32;
+
+ /* We cannot scale up. When the scaled image is larger
+ than crop.rect we adjust the crop.rect as required
+ by the V4L2 spec, hence cropcap.bounds are our limit. */
+ max_width = min(b->width, (__s32) MAX_HACTIVE);
+ max_height = b->height;
+
+ /* We cannot capture the same line as video and VBI data.
+ Note btv->vbi_end is really a minimum, see
+ bttv_vbi_try_fmt(). */
+ if (btv->vbi_end > b->top) {
+ max_height -= btv->vbi_end - b->top;
+ rc = -EBUSY;
+ if (min_height > max_height)
+ goto fail;
+ }
+ } else {
+ rc = -EBUSY;
+ if (btv->vbi_end > c->rect.top)
+ goto fail;
+
+ min_width = c->min_scaled_width;
+ min_height = c->min_scaled_height;
+ max_width = c->max_scaled_width;
+ max_height = c->max_scaled_height;
+
+ adjust_crop = 0;
+ }
+
+ min_width = (min_width - width_mask - 1) & width_mask;
+ max_width = max_width & width_mask;
+
+ /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
+ min_height = min_height;
+ /* Min. scale factor is 1:1. */
+ max_height >>= !V4L2_FIELD_HAS_BOTH(field);
+
+ if (adjust_size) {
+ *width = clamp(*width, min_width, max_width);
+ *height = clamp(*height, min_height, max_height);
+
+ /* Round after clamping to avoid overflow. */
+ *width = (*width + width_bias) & width_mask;
+
+ if (adjust_crop) {
+ bttv_crop_adjust(c, b, *width, *height, field);
+
+ if (btv->vbi_end > c->rect.top) {
+ /* Move the crop window out of the way. */
+ c->rect.top = btv->vbi_end;
+ }
+ }
+ } else {
+ rc = -EINVAL;
+ if (*width < min_width ||
+ *height < min_height ||
+ *width > max_width ||
+ *height > max_height ||
+ 0 != (*width & ~width_mask))
+ goto fail;
+ }
+
+ rc = 0; /* success */
+
+ fail:
+
+ return rc;
+}
+
+/* Returns an error if the given overlay window dimensions are not
+ possible with the current cropping parameters. If adjust_size is
+ TRUE the function may adjust the window width and/or height
+ instead, however it always rounds the horizontal position and
+ width as btcx_align() does. If adjust_crop is TRUE the function
+ may also adjust the current cropping parameters to get closer
+ to the desired window size. */
+static int
+verify_window_lock (struct bttv_fh * fh,
+ struct v4l2_window * win,
+ int adjust_size,
+ int adjust_crop)
+{
+ enum v4l2_field field;
+ unsigned int width_mask;
+ int rc;
+
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (win->w.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* 4-byte alignment. */
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ width_mask = ~0;
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ width_mask = ~3;
+ break;
+ case 16:
+ width_mask = ~1;
+ break;
+ case 32:
+ break;
+ default:
+ BUG();
+ }
+
+ win->w.width -= win->w.left & ~width_mask;
+ win->w.left = (win->w.left - width_mask - 1) & width_mask;
+
+ rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
+ field, width_mask,
+ /* width_bias: round down */ 0,
+ adjust_size, adjust_crop);
+ if (0 != rc)
+ return rc;
+
+ win->field = field;
+ return 0;
+}
+
+static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
+ struct v4l2_window *win, int fixup)
+{
+ struct v4l2_clip *clips = NULL;
+ int n,size,retval = 0;
+
+ if (NULL == fh->ovfmt)
+ return -EINVAL;
+ if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+ retval = verify_window_lock(fh, win,
+ /* adjust_size */ fixup,
+ /* adjust_crop */ fixup);
+ if (0 != retval)
+ return retval;
+
+ /* copy clips -- luckily v4l1 + v4l2 are binary
+ compatible here ...*/
+ n = win->clipcount;
+ size = sizeof(*clips)*(n+4);
+ clips = kmalloc(size,GFP_KERNEL);
+ if (NULL == clips)
+ return -ENOMEM;
+ if (n > 0) {
+ if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
+ kfree(clips);
+ return -EFAULT;
+ }
+ }
+
+ /* clip against screen */
+ if (NULL != btv->fbuf.base)
+ n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
+ &win->w, clips, n);
+ btcx_sort_clips(clips,n);
+
+ /* 4-byte alignments */
+ switch (fh->ovfmt->depth) {
+ case 8:
+ case 24:
+ btcx_align(&win->w, clips, n, 3);
+ break;
+ case 16:
+ btcx_align(&win->w, clips, n, 1);
+ break;
+ case 32:
+ /* no alignment fixups needed */
+ break;
+ default:
+ BUG();
+ }
+
+ kfree(fh->ov.clips);
+ fh->ov.clips = clips;
+ fh->ov.nclips = n;
+
+ fh->ov.w = win->w;
+ fh->ov.field = win->field;
+ fh->ov.setup_ok = 1;
+
+ btv->init.ov.w.width = win->w.width;
+ btv->init.ov.w.height = win->w.height;
+ btv->init.ov.field = win->field;
+
+ /* update overlay if needed */
+ retval = 0;
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv,fh,new);
+ }
+ return retval;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int bttv_resource(struct bttv_fh *fh)
+{
+ int res = 0;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ res = RESOURCE_VIDEO_STREAM;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ res = RESOURCE_VBI;
+ break;
+ default:
+ BUG();
+ }
+ return res;
+}
+
+static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
+{
+ struct videobuf_queue *q = bttv_queue(fh);
+ int res = bttv_resource(fh);
+
+ if (check_btres(fh,res))
+ return -EBUSY;
+ if (videobuf_queue_is_busy(q))
+ return -EBUSY;
+ fh->type = type;
+ return 0;
+}
+
+static void
+pix_format_set_size (struct v4l2_pix_format * f,
+ const struct bttv_format * fmt,
+ unsigned int width,
+ unsigned int height)
+{
+ f->width = width;
+ f->height = height;
+
+ if (fmt->flags & FORMAT_FLAGS_PLANAR) {
+ f->bytesperline = width; /* Y plane */
+ f->sizeimage = (width * height * fmt->depth) >> 3;
+ } else {
+ f->bytesperline = (width * fmt->depth) >> 3;
+ f->sizeimage = height * f->bytesperline;
+ }
+}
+
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ pix_format_set_size(&f->fmt.pix, fh->fmt,
+ fh->width, fh->height);
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+
+ return 0;
+}
+
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ f->fmt.win.w = fh->ov.w;
+ f->fmt.win.field = fh->ov.field;
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ enum v4l2_field field;
+ __s32 width, height;
+ int rc;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+
+ if (V4L2_FIELD_ANY == field) {
+ __s32 height2;
+
+ height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
+ field = (f->fmt.pix.height > height2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ if (V4L2_FIELD_SEQ_BT == field)
+ field = V4L2_FIELD_SEQ_TB;
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_ALTERNATE:
+ case V4L2_FIELD_INTERLACED:
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (fmt->flags & FORMAT_FLAGS_PLANAR)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+
+ rc = limit_scaled_size_lock(fh, &width, &height, field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+ if (0 != rc)
+ return rc;
+
+ /* update data for the application */
+ f->fmt.pix.field = field;
+ pix_format_set_size(&f->fmt.pix, fmt, width, height);
+
+ return 0;
+}
+
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+
+ return verify_window_lock(fh, &f->fmt.win,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 0);
+}
+
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int retval;
+ const struct bttv_format *fmt;
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ __s32 width, height;
+ enum v4l2_field field;
+
+ retval = bttv_switch_type(fh, f->type);
+ if (0 != retval)
+ return retval;
+
+ retval = bttv_try_fmt_vid_cap(file, priv, f);
+ if (0 != retval)
+ return retval;
+
+ width = f->fmt.pix.width;
+ height = f->fmt.pix.height;
+ field = f->fmt.pix.field;
+
+ retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
+ /* width_mask: 4 pixels */ ~3,
+ /* width_bias: nearest */ 2,
+ /* adjust_size */ 1,
+ /* adjust_crop */ 1);
+ if (0 != retval)
+ return retval;
+
+ f->fmt.pix.field = field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ /* update our state informations */
+ fh->fmt = fmt;
+ fh->cap.field = f->fmt.pix.field;
+ fh->cap.last = V4L2_FIELD_NONE;
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ btv->init.fmt = fmt;
+ btv->init.width = f->fmt.pix.width;
+ btv->init.height = f->fmt.pix.height;
+
+ return 0;
+}
+
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ return setup_window_lock(fh, btv, &f->fmt.win, 1);
+}
+
+static int bttv_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 == v4l2)
+ return -EINVAL;
+
+ strlcpy(cap->driver, "bttv", sizeof(cap->driver));
+ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ /*
+ * No need to lock here: those vars are initialized during board
+ * probe and remains untouched during the rest of the driver lifecycle
+ */
+ if (btv->has_saa6588)
+ cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+ if (btv->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
+{
+ int index = -1, i;
+
+ for (i = 0; i < FORMATS; i++) {
+ if (formats[i].fourcc != -1)
+ index++;
+ if ((unsigned int)index == f->index)
+ break;
+ }
+ if (FORMATS == i)
+ return -EINVAL;
+
+ f->pixelformat = formats[i].fourcc;
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
+
+ return i;
+}
+
+static int bttv_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int rc;
+
+ if (no_overlay > 0) {
+ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ rc = bttv_enum_fmt_cap_ovr(f);
+
+ if (rc < 0)
+ return rc;
+
+ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int bttv_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *fb = btv->fbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ if (fh->ovfmt)
+ fb->fmt.pixelformat = fh->ovfmt->fourcc;
+ return 0;
+}
+
+static int bttv_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ struct bttv_buffer *new;
+ int retval = 0;
+
+ if (on) {
+ /* verify args */
+ if (unlikely(!btv->fbuf.base)) {
+ return -EINVAL;
+ }
+ if (unlikely(!fh->ov.setup_ok)) {
+ dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
+ retval = -EINVAL;
+ }
+ if (retval)
+ return retval;
+ }
+
+ if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+
+ if (on) {
+ fh->ov.tvnorm = btv->tvnorm;
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ } else {
+ new = NULL;
+ }
+
+ /* switch over */
+ retval = bttv_switch_overlay(btv, fh, new);
+ return retval;
+}
+
+static int bttv_s_fbuf(struct file *file, void *f,
+ const struct v4l2_framebuffer *fb)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct bttv_format *fmt;
+ int retval;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+ return -EINVAL;
+
+ retval = -EINVAL;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ __s32 width = fb->fmt.width;
+ __s32 height = fb->fmt.height;
+
+ retval = limit_scaled_size_lock(fh, &width, &height,
+ V4L2_FIELD_INTERLACED,
+ /* width_mask */ ~3,
+ /* width_bias */ 2,
+ /* adjust_size */ 0,
+ /* adjust_crop */ 0);
+ if (0 != retval)
+ return retval;
+ }
+
+ /* ok, accept it */
+ btv->fbuf.base = fb->base;
+ btv->fbuf.fmt.width = fb->fmt.width;
+ btv->fbuf.fmt.height = fb->fmt.height;
+ if (0 != fb->fmt.bytesperline)
+ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
+ else
+ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
+
+ retval = 0;
+ fh->ovfmt = fmt;
+ btv->init.ovfmt = fmt;
+ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+ fh->ov.w.left = 0;
+ fh->ov.w.top = 0;
+ fh->ov.w.width = fb->fmt.width;
+ fh->ov.w.height = fb->fmt.height;
+ btv->init.ov.w.width = fb->fmt.width;
+ btv->init.ov.w.height = fb->fmt.height;
+ kfree(fh->ov.clips);
+ fh->ov.clips = NULL;
+ fh->ov.nclips = 0;
+
+ if (check_btres(fh, RESOURCE_OVERLAY)) {
+ struct bttv_buffer *new;
+
+ new = videobuf_sg_alloc(sizeof(*new));
+ new->crop = btv->crop[!!fh->do_crop].rect;
+ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+ retval = bttv_switch_overlay(btv, fh, new);
+ }
+ }
+ return retval;
+}
+
+static int bttv_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_reqbufs(bttv_queue(fh), p);
+}
+
+static int bttv_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_querybuf(bttv_queue(fh), b);
+}
+
+static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+
+ return videobuf_qbuf(bttv_queue(fh), b);
+}
+
+static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct bttv_fh *fh = priv;
+ return videobuf_dqbuf(bttv_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int bttv_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int res = bttv_resource(fh);
+
+ if (!check_alloc_btres_lock(btv, fh, res))
+ return -EBUSY;
+ return videobuf_streamon(bttv_queue(fh));
+}
+
+
+static int bttv_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ int retval;
+ int res = bttv_resource(fh);
+
+
+ retval = videobuf_streamoff(bttv_queue(fh));
+ if (retval < 0)
+ return retval;
+ free_btres_lock(btv, fh, res);
+ return 0;
+}
+
+static int bttv_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+
+ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
+ *c = no_ctl;
+ else {
+ ctrl = ctrl_by_id(c->id);
+
+ *c = (NULL != ctrl) ? *ctrl : no_ctl;
+ }
+
+ return 0;
+}
+
+static int bttv_g_parm(struct file *file, void *f,
+ struct v4l2_streamparm *parm)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
+ &parm->parm.capture.timeperframe);
+
+ return 0;
+}
+
+static int bttv_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ bttv_call_all(btv, tuner, g_tuner, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+ t->signal = 0xffff;
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ *p = v4l2_prio_max(&btv->prio);
+
+ return 0;
+}
+
+static int bttv_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ int rc;
+
+ rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
+
+ return rc;
+}
+
+static int bttv_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ *cap = bttv_tvnorms[btv->tvnorm].cropcap;
+
+ return 0;
+}
+
+static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* No fh->do_crop = 1; because btv->crop[1] may be
+ inconsistent with fh->width or fh->height and apps
+ do not expect a change here. */
+
+ crop->c = btv->crop[!!fh->do_crop].rect;
+
+ return 0;
+}
+
+static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+{
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+ const struct v4l2_rect *b;
+ int retval;
+ struct bttv_crop c;
+ __s32 b_left;
+ __s32 b_top;
+ __s32 b_right;
+ __s32 b_bottom;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* Make sure tvnorm, vbi_end and the current cropping
+ parameters remain consistent until we're done. Note
+ read() may change vbi_end in check_alloc_btres_lock(). */
+ retval = v4l2_prio_check(&btv->prio, fh->prio);
+ if (0 != retval) {
+ return retval;
+ }
+
+ retval = -EBUSY;
+
+ if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
+ return retval;
+ }
+
+ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
+
+ b_left = b->left;
+ b_right = b_left + b->width;
+ b_bottom = b->top + b->height;
+
+ b_top = max(b->top, btv->vbi_end);
+ if (b_top + 32 >= b_bottom) {
+ return retval;
+ }
+
+ /* Min. scaled size 48 x 32. */
+ c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48);
+ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
+
+ c.rect.width = clamp_t(s32, crop->c.width,
+ 48, b_right - c.rect.left);
+
+ c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32);
+ /* Top and height must be a multiple of two. */
+ c.rect.top = (c.rect.top + 1) & ~1;
+
+ c.rect.height = clamp_t(s32, crop->c.height,
+ 32, b_bottom - c.rect.top);
+ c.rect.height = (c.rect.height + 1) & ~1;
+
+ bttv_crop_calc_limits(&c);
+
+ btv->crop[1] = c;
+
+ fh->do_crop = 1;
+
+ if (fh->width < c.min_scaled_width) {
+ fh->width = c.min_scaled_width;
+ btv->init.width = c.min_scaled_width;
+ } else if (fh->width > c.max_scaled_width) {
+ fh->width = c.max_scaled_width;
+ btv->init.width = c.max_scaled_width;
+ }
+
+ if (fh->height < c.min_scaled_height) {
+ fh->height = c.min_scaled_height;
+ btv->init.height = c.min_scaled_height;
+ } else if (fh->height > c.max_scaled_height) {
+ fh->height = c.max_scaled_height;
+ btv->init.height = c.max_scaled_height;
+ }
+
+ return 0;
+}
+
+static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t bttv_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ int retval = 0;
+
+ if (fh->btv->errors)
+ bttv_reinit_bt848(fh->btv);
+ dprintk("%d: read count=%d type=%s\n",
+ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
+ /* VIDEO_READ in use by another fh,
+ or VIDEO_STREAM by any fh. */
+ return -EBUSY;
+ }
+ retval = videobuf_read_one(&fh->cap, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return -EBUSY;
+ retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ }
+ return retval;
+}
+
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv_buffer *buf;
+ enum v4l2_field field;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+ }
+
+ if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
+ /* streaming capture */
+ if (list_empty(&fh->cap.stream))
+ goto err;
+ buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ if (NULL == fh->cap.read_buf) {
+ /* need to capture a new frame */
+ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
+ goto err;
+ fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
+ if (NULL == fh->cap.read_buf)
+ goto err;
+ fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
+ field = videobuf_next_field(&fh->cap);
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
+ kfree (fh->cap.read_buf);
+ fh->cap.read_buf = NULL;
+ goto err;
+ }
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ buf = (struct bttv_buffer*)fh->cap.read_buf;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+err:
+ return rc;
+}
+
+static int bttv_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+ enum v4l2_buf_type type = 0;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ if (vdev->vfl_type == VFL_TYPE_GRABBER) {
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ } else if (vdev->vfl_type == VFL_TYPE_VBI) {
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ } else {
+ WARN_ON(1);
+ return -ENODEV;
+ }
+
+ dprintk("%d: open called (type=%s)\n",
+ btv->c.nr, v4l2_type_names[type]);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+
+ *fh = btv->init;
+
+ fh->type = type;
+ fh->ov.setup_ok = 0;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
+ &btv->c.pci->dev, &btv->s_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct bttv_buffer),
+ fh, &btv->lock);
+ set_tvnorm(btv,btv->tvnorm);
+ set_input(btv, btv->input, btv->tvnorm);
+
+ btv->users++;
+
+ /* The V4L2 spec requires one global set of cropping parameters
+ which only change on request. These are stored in btv->crop[1].
+ However for compatibility with V4L apps and cropping unaware
+ V4L2 apps we now reset the cropping parameters as seen through
+ this fh, which is to say VIDIOC_G_CROP and scaling limit checks
+ will use btv->crop[0], the default cropping parameters for the
+ current video standard, and VIDIOC_S_FMT will not implicitely
+ change the cropping parameters until VIDIOC_S_CROP has been
+ called. */
+ fh->do_crop = !reset_crop; /* module parameter */
+
+ /* Likewise there should be one global set of VBI capture
+ parameters, but for compatibility with V4L apps and earlier
+ driver versions each fh has its own parameters. */
+ bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
+
+ bttv_field_count(btv);
+ return 0;
+}
+
+static int bttv_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+
+ /* turn off overlay */
+ if (check_btres(fh, RESOURCE_OVERLAY))
+ bttv_switch_overlay(btv,fh,NULL);
+
+ /* stop video capture */
+ if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
+ videobuf_streamoff(&fh->cap);
+ free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+ if (check_btres(fh, RESOURCE_VIDEO_READ)) {
+ free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
+ }
+
+ /* stop vbi capture */
+ if (check_btres(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbi);
+ free_btres_lock(btv,fh,RESOURCE_VBI);
+ }
+
+ /* free stuff */
+
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->users--;
+ bttv_field_count(btv);
+
+ if (!btv->users)
+ audio_mute(btv, 1);
+
+ return 0;
+}
+
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bttv_fh *fh = file->private_data;
+
+ dprintk("%d: mmap type=%s 0x%lx+%ld\n",
+ fh->btv->c.nr, v4l2_type_names[fh->type],
+ vma->vm_start, vma->vm_end - vma->vm_start);
+ return videobuf_mmap_mapper(bttv_queue(fh),vma);
+}
+
+static const struct v4l2_file_operations bttv_fops =
+{
+ .owner = THIS_MODULE,
+ .open = bttv_open,
+ .release = bttv_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = bttv_read,
+ .mmap = bttv_mmap,
+ .poll = bttv_poll,
+};
+
+static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
+ .vidioc_querycap = bttv_querycap,
+ .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
+ .vidioc_g_audio = bttv_g_audio,
+ .vidioc_s_audio = bttv_s_audio,
+ .vidioc_cropcap = bttv_cropcap,
+ .vidioc_reqbufs = bttv_reqbufs,
+ .vidioc_querybuf = bttv_querybuf,
+ .vidioc_qbuf = bttv_qbuf,
+ .vidioc_dqbuf = bttv_dqbuf,
+ .vidioc_s_std = bttv_s_std,
+ .vidioc_enum_input = bttv_enum_input,
+ .vidioc_g_input = bttv_g_input,
+ .vidioc_s_input = bttv_s_input,
+ .vidioc_queryctrl = bttv_queryctrl,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_streamon = bttv_streamon,
+ .vidioc_streamoff = bttv_streamoff,
+ .vidioc_g_tuner = bttv_g_tuner,
+ .vidioc_s_tuner = bttv_s_tuner,
+ .vidioc_g_crop = bttv_g_crop,
+ .vidioc_s_crop = bttv_s_crop,
+ .vidioc_g_fbuf = bttv_g_fbuf,
+ .vidioc_s_fbuf = bttv_s_fbuf,
+ .vidioc_overlay = bttv_overlay,
+ .vidioc_g_priority = bttv_g_priority,
+ .vidioc_s_priority = bttv_s_priority,
+ .vidioc_g_parm = bttv_g_parm,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+ .vidioc_log_status = bttv_log_status,
+ .vidioc_querystd = bttv_querystd,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bttv_g_register,
+ .vidioc_s_register = bttv_s_register,
+#endif
+};
+
+static struct video_device bttv_video_template = {
+ .fops = &bttv_fops,
+ .ioctl_ops = &bttv_ioctl_ops,
+ .tvnorms = BTTV_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+/* ----------------------------------------------------------------------- */
+/* radio interface */
+
+static int radio_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+
+ dprintk("open dev=%s\n", video_device_node_name(vdev));
+
+ dprintk("%d: open called (radio)\n", btv->c.nr);
+
+ /* allocate per filehandle data */
+ fh = kmalloc(sizeof(*fh), GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+ file->private_data = fh;
+ *fh = btv->init;
+
+ v4l2_prio_open(&btv->prio, &fh->prio);
+
+ btv->radio_user++;
+
+ bttv_call_all(btv, tuner, s_radio);
+ audio_input(btv,TVAUDIO_INPUT_RADIO);
+
+ return 0;
+}
+
+static int radio_release(struct file *file)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+
+ v4l2_prio_close(&btv->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ btv->radio_user--;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+ return 0;
+}
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ strcpy(cap->driver, "bttv");
+ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ bttv_call_all(btv, tuner, g_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ bttv_call_all(btv, tuner, s_tuner, t);
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ if (unlikely(i))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctl;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.block_count = count/3;
+ cmd.buffer = data;
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+ return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+ struct bttv_fh *fh = file->private_data;
+ struct bttv *btv = fh->btv;
+ struct saa6588_command cmd;
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+ bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+ return cmd.result;
+}
+
+static const struct v4l2_file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = radio_open,
+ .read = radio_read,
+ .release = radio_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = bttv_g_ctrl,
+ .vidioc_s_ctrl = bttv_s_ctrl,
+ .vidioc_g_frequency = bttv_g_frequency,
+ .vidioc_s_frequency = bttv_s_frequency,
+};
+
+static struct video_device radio_template = {
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* ----------------------------------------------------------------------- */
+/* some debug code */
+
+static int bttv_risc_decode(u32 risc)
+{
+ static char *instr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = "write",
+ [ BT848_RISC_SKIP >> 28 ] = "skip",
+ [ BT848_RISC_WRITEC >> 28 ] = "writec",
+ [ BT848_RISC_JUMP >> 28 ] = "jump",
+ [ BT848_RISC_SYNC >> 28 ] = "sync",
+ [ BT848_RISC_WRITE123 >> 28 ] = "write123",
+ [ BT848_RISC_SKIP123 >> 28 ] = "skip123",
+ [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
+ };
+ static int incr[16] = {
+ [ BT848_RISC_WRITE >> 28 ] = 2,
+ [ BT848_RISC_JUMP >> 28 ] = 2,
+ [ BT848_RISC_SYNC >> 28 ] = 2,
+ [ BT848_RISC_WRITE123 >> 28 ] = 5,
+ [ BT848_RISC_SKIP123 >> 28 ] = 2,
+ [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
+ };
+ static char *bits[] = {
+ "be0", "be1", "be2", "be3/resync",
+ "set0", "set1", "set2", "set3",
+ "clr0", "clr1", "clr2", "clr3",
+ "irq", "res", "eol", "sol",
+ };
+ int i;
+
+ pr_cont("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ pr_cont(" %s", bits[i]);
+ pr_cont(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+static void bttv_risc_disasm(struct bttv *btv,
+ struct btcx_riscmem *risc)
+{
+ unsigned int i,j,n;
+
+ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
+ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ for (i = 0; i < (risc->size >> 2); i += n) {
+ pr_info("%s: 0x%lx: ",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
+ n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+ for (j = 1; j < n; j++)
+ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+ btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
+ if (0 == risc->cpu[i])
+ break;
+ }
+}
+
+static void bttv_print_riscaddr(struct bttv *btv)
+{
+ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
+ pr_info(" vbi : o=%08llx e=%08llx\n",
+ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
+ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
+ pr_info(" cap : o=%08llx e=%08llx\n",
+ btv->curr.top
+ ? (unsigned long long)btv->curr.top->top.dma : 0,
+ btv->curr.bottom
+ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
+ pr_info(" scr : o=%08llx e=%08llx\n",
+ btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
+ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
+ bttv_risc_disasm(btv, &btv->main);
+}
+
+/* ----------------------------------------------------------------------- */
+/* irq handler */
+
+static char *irq_name[] = {
+ "FMTCHG", // format change detected (525 vs. 625)
+ "VSYNC", // vertical sync (new field)
+ "HSYNC", // horizontal sync
+ "OFLOW", // chroma/luma AGC overflow
+ "HLOCK", // horizontal lock changed
+ "VPRES", // video presence changed
+ "6", "7",
+ "I2CDONE", // hw irc operation finished
+ "GPINT", // gpio port triggered irq
+ "10",
+ "RISCI", // risc instruction triggered irq
+ "FBUS", // pixel data fifo dropped data (high pci bus latencies)
+ "FTRGT", // pixel data fifo overrun
+ "FDSR", // fifo data stream resyncronisation
+ "PPERR", // parity error (data transfer)
+ "RIPERR", // parity error (read risc instructions)
+ "PABORT", // pci abort
+ "OCERR", // risc instruction error
+ "SCERR", // syncronisation error
+};
+
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+ unsigned int i;
+
+ pr_cont("bits:");
+ for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
+ if (print & (1 << i))
+ pr_cont(" %s", irq_name[i]);
+ if (mark & (1 << i))
+ pr_cont("*");
+ }
+}
+
+static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
+{
+ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
+ btv->c.nr,
+ (unsigned long)btv->main.dma,
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
+ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
+ (unsigned long)rc);
+
+ if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
+ pr_notice("%d: Oh, there (temporarily?) is no input signal. "
+ "Ok, then this is harmless, don't worry ;)\n",
+ btv->c.nr);
+ return;
+ }
+ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
+ btv->c.nr);
+ pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
+ btv->c.nr);
+ dump_stack();
+}
+
+static int
+bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
+{
+ struct bttv_buffer *item;
+
+ memset(set,0,sizeof(*set));
+
+ /* capture request ? */
+ if (!list_empty(&btv->capture)) {
+ set->frame_irq = 1;
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ if (V4L2_FIELD_HAS_TOP(item->vb.field))
+ set->top = item;
+ if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
+ set->bottom = item;
+
+ /* capture request for other field ? */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
+ (item->vb.queue.next != &btv->capture)) {
+ item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
+ /* Mike Isely <isely@pobox.com> - Only check
+ * and set up the bottom field in the logic
+ * below. Don't ever do the top field. This
+ * of course means that if we set up the
+ * bottom field in the above code that we'll
+ * actually skip a field. But that's OK.
+ * Having processed only a single buffer this
+ * time, then the next time around the first
+ * available buffer should be for a top field.
+ * That will then cause us here to set up a
+ * top then a bottom field in the normal way.
+ * The alternative to this understanding is
+ * that we set up the second available buffer
+ * as a top field, but that's out of order
+ * since this driver always processes the top
+ * field first - the effect will be the two
+ * buffers being returned in the wrong order,
+ * with the second buffer also being delayed
+ * by one field time (owing to the fifo nature
+ * of videobuf). Worse still, we'll be stuck
+ * doing fields out of order now every time
+ * until something else causes a field to be
+ * dropped. By effectively forcing a field to
+ * drop this way then we always get back into
+ * sync within a single frame time. (Out of
+ * order fields can screw up deinterlacing
+ * algorithms.) */
+ if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
+ if (NULL == set->bottom &&
+ V4L2_FIELD_BOTTOM == item->vb.field) {
+ set->bottom = item;
+ }
+ if (NULL != set->top && NULL != set->bottom)
+ set->top_irq = 2;
+ }
+ }
+ }
+
+ /* screen overlay ? */
+ if (NULL != btv->screen) {
+ if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
+ if (NULL == set->top && NULL == set->bottom) {
+ set->top = btv->screen;
+ set->bottom = btv->screen;
+ }
+ } else {
+ if (V4L2_FIELD_TOP == btv->screen->vb.field &&
+ NULL == set->top) {
+ set->top = btv->screen;
+ }
+ if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
+ NULL == set->bottom) {
+ set->bottom = btv->screen;
+ }
+ }
+ }
+
+ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
+ btv->c.nr, set->top, set->bottom,
+ btv->screen, set->frame_irq, set->top_irq);
+ return 0;
+}
+
+static void
+bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
+ struct bttv_buffer_set *curr, unsigned int state)
+{
+ struct timeval ts;
+
+ do_gettimeofday(&ts);
+
+ if (wakeup->top == wakeup->bottom) {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: both=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ } else {
+ if (NULL != wakeup->top && curr->top != wakeup->top) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: top=%p\n",
+ btv->c.nr, wakeup->top);
+ wakeup->top->vb.ts = ts;
+ wakeup->top->vb.field_count = btv->field_count;
+ wakeup->top->vb.state = state;
+ wake_up(&wakeup->top->vb.done);
+ }
+ if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
+ if (irq_debug > 1)
+ pr_debug("%d: wakeup: bottom=%p\n",
+ btv->c.nr, wakeup->bottom);
+ wakeup->bottom->vb.ts = ts;
+ wakeup->bottom->vb.field_count = btv->field_count;
+ wakeup->bottom->vb.state = state;
+ wake_up(&wakeup->bottom->vb.done);
+ }
+ }
+}
+
+static void
+bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
+ unsigned int state)
+{
+ struct timeval ts;
+
+ if (NULL == wakeup)
+ return;
+
+ do_gettimeofday(&ts);
+ wakeup->vb.ts = ts;
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = state;
+ wake_up(&wakeup->vb.done);
+}
+
+static void bttv_irq_timeout(unsigned long data)
+{
+ struct bttv *btv = (struct bttv *)data;
+ struct bttv_buffer_set old,new;
+ struct bttv_buffer *ovbi;
+ struct bttv_buffer *item;
+ unsigned long flags;
+
+ if (bttv_verbose) {
+ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
+ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(btread(BT848_INT_STAT),0);
+ pr_cont("\n");
+ }
+
+ spin_lock_irqsave(&btv->s_lock,flags);
+
+ /* deactivate stuff */
+ memset(&new,0,sizeof(new));
+ old = btv->curr;
+ ovbi = btv->cvbi;
+ btv->curr = new;
+ btv->cvbi = NULL;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+
+ /* wake up */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
+ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
+
+ /* cancel all outstanding capture / vbi requests */
+ while (!list_empty(&btv->capture)) {
+ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+ while (!list_empty(&btv->vcapture)) {
+ item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ list_del(&item->vb.queue);
+ item->vb.state = VIDEOBUF_ERROR;
+ wake_up(&item->vb.done);
+ }
+
+ btv->errors++;
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+}
+
+static void
+bttv_irq_wakeup_top(struct bttv *btv)
+{
+ struct bttv_buffer *wakeup = btv->curr.top;
+
+ if (NULL == wakeup)
+ return;
+
+ spin_lock(&btv->s_lock);
+ btv->curr.top_irq = 0;
+ btv->curr.top = NULL;
+ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+
+ do_gettimeofday(&wakeup->vb.ts);
+ wakeup->vb.field_count = btv->field_count;
+ wakeup->vb.state = VIDEOBUF_DONE;
+ wake_up(&wakeup->vb.done);
+ spin_unlock(&btv->s_lock);
+}
+
+static inline int is_active(struct btcx_riscmem *risc, u32 rc)
+{
+ if (rc < risc->dma)
+ return 0;
+ if (rc > risc->dma + risc->size)
+ return 0;
+ return 1;
+}
+
+static void
+bttv_irq_switch_video(struct bttv *btv)
+{
+ struct bttv_buffer_set new;
+ struct bttv_buffer_set old;
+ dma_addr_t rc;
+
+ spin_lock(&btv->s_lock);
+
+ /* new buffer set */
+ bttv_irq_next_video(btv, &new);
+ rc = btread(BT848_RISC_COUNT);
+ if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) ||
+ (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch over */
+ old = btv->curr;
+ btv->curr = new;
+ btv->loop_irq &= ~1;
+ bttv_buffer_activate_video(btv, &new);
+ bttv_set_dma(btv, 0);
+
+ /* switch input */
+ if (UNSET != btv->new_input) {
+ video_mux(btv,btv->new_input);
+ btv->new_input = UNSET;
+ }
+
+ /* wake up finished buffers */
+ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static void
+bttv_irq_switch_vbi(struct bttv *btv)
+{
+ struct bttv_buffer *new = NULL;
+ struct bttv_buffer *old;
+ u32 rc;
+
+ spin_lock(&btv->s_lock);
+
+ if (!list_empty(&btv->vcapture))
+ new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+ old = btv->cvbi;
+
+ rc = btread(BT848_RISC_COUNT);
+ if (NULL != old && (is_active(&old->top, rc) ||
+ is_active(&old->bottom, rc))) {
+ btv->framedrop++;
+ if (debug_latency)
+ bttv_irq_debug_low_latency(btv, rc);
+ spin_unlock(&btv->s_lock);
+ return;
+ }
+
+ /* switch */
+ btv->cvbi = new;
+ btv->loop_irq &= ~4;
+ bttv_buffer_activate_vbi(btv, new);
+ bttv_set_dma(btv, 0);
+
+ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
+ spin_unlock(&btv->s_lock);
+}
+
+static irqreturn_t bttv_irq(int irq, void *dev_id)
+{
+ u32 stat,astat;
+ u32 dstat;
+ int count;
+ struct bttv *btv;
+ int handled = 0;
+
+ btv=(struct bttv *)dev_id;
+
+ count=0;
+ while (1) {
+ /* get/clear interrupt status bits */
+ stat=btread(BT848_INT_STAT);
+ astat=stat&btread(BT848_INT_MASK);
+ if (!astat)
+ break;
+ handled = 1;
+ btwrite(stat,BT848_INT_STAT);
+
+ /* get device status bits */
+ dstat=btread(BT848_DSTATUS);
+
+ if (irq_debug) {
+ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
+ btv->c.nr, count, btv->field_count,
+ stat>>28, btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ if (stat & BT848_INT_HLOCK)
+ pr_cont(" HLOC => %s",
+ dstat & BT848_DSTATUS_HLOC
+ ? "yes" : "no");
+ if (stat & BT848_INT_VPRES)
+ pr_cont(" PRES => %s",
+ dstat & BT848_DSTATUS_PRES
+ ? "yes" : "no");
+ if (stat & BT848_INT_FMTCHG)
+ pr_cont(" NUML => %s",
+ dstat & BT848_DSTATUS_NUML
+ ? "625" : "525");
+ pr_cont("\n");
+ }
+
+ if (astat&BT848_INT_VSYNC)
+ btv->field_count++;
+
+ if ((astat & BT848_INT_GPINT) && btv->remote) {
+ bttv_input_irq(btv);
+ }
+
+ if (astat & BT848_INT_I2CDONE) {
+ btv->i2c_done = stat;
+ wake_up(&btv->i2c_queue);
+ }
+
+ if ((astat & BT848_INT_RISCI) && (stat & (4<<28)))
+ bttv_irq_switch_vbi(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (2<<28)))
+ bttv_irq_wakeup_top(btv);
+
+ if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
+ bttv_irq_switch_video(btv);
+
+ if ((astat & BT848_INT_HLOCK) && btv->opt_automute)
+ audio_mute(btv, btv->mute); /* trigger automute */
+
+ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+ pr_info("%d: %s%s @ %08x,",
+ btv->c.nr,
+ (astat & BT848_INT_SCERR) ? "SCERR" : "",
+ (astat & BT848_INT_OCERR) ? "OCERR" : "",
+ btread(BT848_RISC_COUNT));
+ bttv_print_irqbits(stat,astat);
+ pr_cont("\n");
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+ if (fdsr && astat & BT848_INT_FDSR) {
+ pr_info("%d: FDSR @ %08x\n",
+ btv->c.nr, btread(BT848_RISC_COUNT));
+ if (bttv_debug)
+ bttv_print_riscaddr(btv);
+ }
+
+ count++;
+ if (count > 4) {
+
+ if (count > 8 || !(astat & BT848_INT_GPINT)) {
+ btwrite(0, BT848_INT_MASK);
+
+ pr_err("%d: IRQ lockup, cleared int mask [",
+ btv->c.nr);
+ } else {
+ pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
+ btv->c.nr);
+
+ btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
+ BT848_INT_MASK);
+ };
+
+ bttv_print_irqbits(stat,astat);
+
+ pr_cont("]\n");
+ }
+ }
+ btv->irq_total++;
+ if (handled)
+ btv->irq_me++;
+ return IRQ_RETVAL(handled);
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* initialitation */
+
+static struct video_device *vdev_init(struct bttv *btv,
+ const struct video_device *template,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &btv->c.v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = bttv_debug;
+ video_set_drvdata(vfd, btv);
+ snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+ btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+ type_name, bttv_tvcards[btv->c.type].name);
+ return vfd;
+}
+
+static void bttv_unregister_video(struct bttv *btv)
+{
+ if (btv->video_dev) {
+ if (video_is_registered(btv->video_dev))
+ video_unregister_device(btv->video_dev);
+ else
+ video_device_release(btv->video_dev);
+ btv->video_dev = NULL;
+ }
+ if (btv->vbi_dev) {
+ if (video_is_registered(btv->vbi_dev))
+ video_unregister_device(btv->vbi_dev);
+ else
+ video_device_release(btv->vbi_dev);
+ btv->vbi_dev = NULL;
+ }
+ if (btv->radio_dev) {
+ if (video_is_registered(btv->radio_dev))
+ video_unregister_device(btv->radio_dev);
+ else
+ video_device_release(btv->radio_dev);
+ btv->radio_dev = NULL;
+ }
+}
+
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
+{
+ if (no_overlay > 0)
+ pr_notice("Overlay support disabled\n");
+
+ /* video */
+ btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
+
+ if (NULL == btv->video_dev)
+ goto err;
+ if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
+ video_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->video_dev));
+ if (device_create_file(&btv->video_dev->dev,
+ &dev_attr_card)<0) {
+ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
+ goto err;
+ }
+
+ /* vbi */
+ btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
+
+ if (NULL == btv->vbi_dev)
+ goto err;
+ if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->vbi_dev));
+
+ if (!btv->has_radio)
+ return 0;
+ /* radio */
+ btv->radio_dev = vdev_init(btv, &radio_template, "radio");
+ if (NULL == btv->radio_dev)
+ goto err;
+ if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[btv->c.nr]) < 0)
+ goto err;
+ pr_info("%d: registered device %s\n",
+ btv->c.nr, video_device_node_name(btv->radio_dev));
+
+ /* all done */
+ return 0;
+
+ err:
+ bttv_unregister_video(btv);
+ return -1;
+}
+
+
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
+{
+#if defined(__powerpc__)
+ unsigned int cmd;
+
+ pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+ cmd = (cmd | PCI_COMMAND_MEMORY );
+ pci_write_config_dword(dev, PCI_COMMAND, cmd);
+#endif
+}
+
+static int __devinit bttv_probe(struct pci_dev *dev,
+ const struct pci_device_id *pci_id)
+{
+ int result;
+ unsigned char lat;
+ struct bttv *btv;
+
+ if (bttv_num == BTTV_MAX)
+ return -ENOMEM;
+ pr_info("Bt8xx card found (%d)\n", bttv_num);
+ bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
+ if (btv == NULL) {
+ pr_err("out of memory\n");
+ return -ENOMEM;
+ }
+ btv->c.nr = bttv_num;
+ snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
+ "bttv%d", btv->c.nr);
+
+ /* initialize structs / fill in defaults */
+ mutex_init(&btv->lock);
+ spin_lock_init(&btv->s_lock);
+ spin_lock_init(&btv->gpio_lock);
+ init_waitqueue_head(&btv->i2c_queue);
+ INIT_LIST_HEAD(&btv->c.subs);
+ INIT_LIST_HEAD(&btv->capture);
+ INIT_LIST_HEAD(&btv->vcapture);
+ v4l2_prio_init(&btv->prio);
+
+ init_timer(&btv->timeout);
+ btv->timeout.function = bttv_irq_timeout;
+ btv->timeout.data = (unsigned long)btv;
+
+ btv->i2c_rc = -1;
+ btv->tuner_type = UNSET;
+ btv->new_input = UNSET;
+ btv->has_radio=radio[btv->c.nr];
+
+ /* pci stuff (init, get irq/mmio, ... */
+ btv->c.pci = dev;
+ btv->id = dev->device;
+ if (pci_enable_device(dev)) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+ pr_warn("%d: No suitable DMA available\n", btv->c.nr);
+ return -EIO;
+ }
+ if (!request_mem_region(pci_resource_start(dev,0),
+ pci_resource_len(dev,0),
+ btv->c.v4l2_dev.name)) {
+ pr_warn("%d: can't request iomem (0x%llx)\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev, 0));
+ return -EBUSY;
+ }
+ pci_set_master(dev);
+ pci_set_command(dev);
+
+ result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
+ if (result < 0) {
+ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
+ goto fail0;
+ }
+
+ btv->revision = dev->revision;
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
+ bttv_num, btv->id, btv->revision, pci_name(dev),
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev, 0));
+ schedule();
+
+ btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
+ if (NULL == btv->bt848_mmio) {
+ pr_err("%d: ioremap() failed\n", btv->c.nr);
+ result = -EIO;
+ goto fail1;
+ }
+
+ /* identify card */
+ bttv_idcard(btv);
+
+ /* disable irqs, register irq handler */
+ btwrite(0, BT848_INT_MASK);
+ result = request_irq(btv->c.pci->irq, bttv_irq,
+ IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+ if (result < 0) {
+ pr_err("%d: can't get IRQ %d\n",
+ bttv_num, btv->c.pci->irq);
+ goto fail1;
+ }
+
+ if (0 != bttv_handle_chipset(btv)) {
+ result = -EIO;
+ goto fail2;
+ }
+
+ /* init options from insmod args */
+ btv->opt_combfilter = combfilter;
+ btv->opt_lumafilter = lumafilter;
+ btv->opt_automute = automute;
+ btv->opt_chroma_agc = chroma_agc;
+ btv->opt_adc_crush = adc_crush;
+ btv->opt_vcr_hack = vcr_hack;
+ btv->opt_whitecrush_upper = whitecrush_upper;
+ btv->opt_whitecrush_lower = whitecrush_lower;
+ btv->opt_uv_ratio = uv_ratio;
+ btv->opt_full_luma_range = full_luma_range;
+ btv->opt_coring = coring;
+
+ /* fill struct bttv with some useful defaults */
+ btv->init.btv = btv;
+ btv->init.ov.w.width = 320;
+ btv->init.ov.w.height = 240;
+ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ btv->init.width = 320;
+ btv->init.height = 240;
+ btv->input = 0;
+
+ /* initialize hardware */
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"pre-init");
+
+ bttv_risc_init_main(btv);
+ init_bt848(btv);
+
+ /* gpio */
+ btwrite(0x00, BT848_GPIO_REG_INP);
+ btwrite(0x00, BT848_GPIO_OUT_EN);
+ if (bttv_verbose)
+ bttv_gpio_tracking(btv,"init");
+
+ /* needs to be done before i2c is registered */
+ bttv_init_card1(btv);
+
+ /* register i2c + gpio */
+ init_bttv_i2c(btv);
+
+ /* some card-specific stuff (needs working i2c) */
+ bttv_init_card2(btv);
+ bttv_init_tuner(btv);
+ init_irqreg(btv);
+
+ /* register video4linux + input */
+ if (!bttv_tvcards[btv->c.type].no_video) {
+ bttv_register_video(btv);
+ bt848_bright(btv,32768);
+ bt848_contrast(btv, 27648);
+ bt848_hue(btv,32768);
+ bt848_sat(btv,32768);
+ audio_mute(btv, 1);
+ set_input(btv, 0, btv->tvnorm);
+ bttv_crop_reset(&btv->crop[0], btv->tvnorm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+ disclaim_vbi_lines(btv);
+ disclaim_video_lines(btv);
+ }
+
+ /* add subdevices and autoload dvb-bt8xx if needed */
+ if (bttv_tvcards[btv->c.type].has_dvb) {
+ bttv_sub_add_device(&btv->c, "dvb");
+ request_modules(btv);
+ }
+
+ if (!disable_ir) {
+ init_bttv_i2c_ir(btv);
+ bttv_input_init(btv);
+ }
+
+ /* everything is fine */
+ bttv_num++;
+ return 0;
+
+fail2:
+ free_irq(btv->c.pci->irq,btv);
+
+fail1:
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+
+fail0:
+ if (btv->bt848_mmio)
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+ return result;
+}
+
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+
+ if (bttv_verbose)
+ pr_info("%d: unloading\n", btv->c.nr);
+
+ if (bttv_tvcards[btv->c.type].has_dvb)
+ flush_request_modules(btv);
+
+ /* shutdown everything (DMA+IRQs) */
+ btand(~15, BT848_GPIO_DMA_CTL);
+ btwrite(0, BT848_INT_MASK);
+ btwrite(~0x0, BT848_INT_STAT);
+ btwrite(0x0, BT848_GPIO_OUT_EN);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"cleanup");
+
+ /* tell gpio modules we are leaving ... */
+ btv->shutdown=1;
+ bttv_input_fini(btv);
+ bttv_sub_del_devices(&btv->c);
+
+ /* unregister i2c_bus + input */
+ fini_bttv_i2c(btv);
+
+ /* unregister video4linux */
+ bttv_unregister_video(btv);
+
+ /* free allocated memory */
+ btcx_riscmem_free(btv->c.pci,&btv->main);
+
+ /* free ressources */
+ free_irq(btv->c.pci->irq,btv);
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+
+ v4l2_device_unregister(&btv->c.v4l2_dev);
+ bttvs[btv->c.nr] = NULL;
+ kfree(btv);
+
+ return;
+}
+
+#ifdef CONFIG_PM
+static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ struct bttv_buffer_set idle;
+ unsigned long flags;
+
+ dprintk("%d: suspend %d\n", btv->c.nr, state.event);
+
+ /* stop dma + irqs */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ memset(&idle, 0, sizeof(idle));
+ btv->state.video = btv->curr;
+ btv->state.vbi = btv->cvbi;
+ btv->state.loop_irq = btv->loop_irq;
+ btv->curr = idle;
+ btv->loop_irq = 0;
+ bttv_buffer_activate_video(btv, &idle);
+ bttv_buffer_activate_vbi(btv, NULL);
+ bttv_set_dma(btv, 0);
+ btwrite(0, BT848_INT_MASK);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+
+ /* save bt878 state */
+ btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
+ btv->state.gpio_data = gpio_read();
+
+ /* save pci state */
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ btv->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int bttv_resume(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct bttv *btv = to_bttv(v4l2_dev);
+ unsigned long flags;
+ int err;
+
+ dprintk("%d: resume\n", btv->c.nr);
+
+ /* restore pci state */
+ if (btv->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ return err;
+ }
+ btv->state.disabled = 0;
+ }
+ err=pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ pci_disable_device(pci_dev);
+ pr_warn("%d: Can't enable device\n", btv->c.nr);
+ btv->state.disabled = 1;
+ return err;
+ }
+
+ pci_restore_state(pci_dev);
+
+ /* restore bt878 state */
+ bttv_reinit_bt848(btv);
+ gpio_inout(0xffffff, btv->state.gpio_enable);
+ gpio_write(btv->state.gpio_data);
+
+ /* restart dma */
+ spin_lock_irqsave(&btv->s_lock,flags);
+ btv->curr = btv->state.video;
+ btv->cvbi = btv->state.vbi;
+ btv->loop_irq = btv->state.loop_irq;
+ bttv_buffer_activate_video(btv, &btv->curr);
+ bttv_buffer_activate_vbi(btv, btv->cvbi);
+ bttv_set_dma(btv, 0);
+ spin_unlock_irqrestore(&btv->s_lock,flags);
+ return 0;
+}
+#endif
+
+static struct pci_device_id bttv_pci_tbl[] = {
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
+ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
+
+static struct pci_driver bttv_pci_driver = {
+ .name = "bttv",
+ .id_table = bttv_pci_tbl,
+ .probe = bttv_probe,
+ .remove = __devexit_p(bttv_remove),
+#ifdef CONFIG_PM
+ .suspend = bttv_suspend,
+ .resume = bttv_resume,
+#endif
+};
+
+static int __init bttv_init_module(void)
+{
+ int ret;
+
+ bttv_num = 0;
+
+ pr_info("driver version %s loaded\n", BTTV_VERSION);
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize > BTTV_MAX_FBUF)
+ gbufsize = BTTV_MAX_FBUF;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+ if (bttv_verbose)
+ pr_info("using %d buffers with %dk (%d pages) each for capture\n",
+ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
+
+ bttv_check_chipset();
+
+ ret = bus_register(&bttv_sub_bus_type);
+ if (ret < 0) {
+ pr_warn("bus_register error: %d\n", ret);
+ return ret;
+ }
+ ret = pci_register_driver(&bttv_pci_driver);
+ if (ret < 0)
+ bus_unregister(&bttv_sub_bus_type);
+
+ return ret;
+}
+
+static void __exit bttv_cleanup_module(void)
+{
+ pci_unregister_driver(&bttv_pci_driver);
+ bus_unregister(&bttv_sub_bus_type);
+}
+
+module_init(bttv_init_module);
+module_exit(bttv_cleanup_module);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c
index 922e8233fd0b..922e8233fd0b 100644
--- a/drivers/media/video/bt8xx/bttv-gpio.c
+++ b/drivers/media/pci/bt8xx/bttv-gpio.c
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c
index 580c8e682392..580c8e682392 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/pci/bt8xx/bttv-i2c.c
diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c
index a6a540dc9e4b..a6a540dc9e4b 100644
--- a/drivers/media/video/bt8xx/bttv-if.c
+++ b/drivers/media/pci/bt8xx/bttv-if.c
diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c
index ef4c7cd41982..ef4c7cd41982 100644
--- a/drivers/media/video/bt8xx/bttv-input.c
+++ b/drivers/media/pci/bt8xx/bttv-input.c
diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c
index 82cc47d2e3fa..82cc47d2e3fa 100644
--- a/drivers/media/video/bt8xx/bttv-risc.c
+++ b/drivers/media/pci/bt8xx/bttv-risc.c
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c
index b433267d9aa9..b433267d9aa9 100644
--- a/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/drivers/media/pci/bt8xx/bttv-vbi.c
diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h
index 79a11240a590..79a11240a590 100644
--- a/drivers/media/video/bt8xx/bttv.h
+++ b/drivers/media/pci/bt8xx/bttv.h
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h
index 70fd4f23f605..70fd4f23f605 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/pci/bt8xx/bttvp.h
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c
index 430b3eb11815..430b3eb11815 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/pci/bt8xx/dst.c
diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c
new file mode 100644
index 000000000000..ee3884fbc9ce
--- /dev/null
+++ b/drivers/media/pci/bt8xx/dst_ca.c
@@ -0,0 +1,726 @@
+/*
+ CA-driver for TwinHan DST Frontend/Card
+
+ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/dvb/ca.h>
+#include "dvbdev.h"
+#include "dvb_frontend.h"
+#include "dst_ca.h"
+#include "dst_common.h"
+
+#define DST_CA_ERROR 0
+#define DST_CA_NOTICE 1
+#define DST_CA_INFO 2
+#define DST_CA_DEBUG 3
+
+#define dprintk(x, y, z, format, arg...) do { \
+ if (z) { \
+ if ((x > DST_CA_ERROR) && (x > y)) \
+ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_NOTICE) && (x > y)) \
+ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_INFO) && (x > y)) \
+ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \
+ else if ((x > DST_CA_DEBUG) && (x > y)) \
+ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \
+ } else { \
+ if (x > y) \
+ printk(format, ## arg); \
+ } \
+} while(0)
+
+
+static DEFINE_MUTEX(dst_ca_mutex);
+static unsigned int verbose = 5;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)");
+
+/* Need some more work */
+static int ca_set_slot_descr(void)
+{
+ /* We could make this more graceful ? */
+ return -EOPNOTSUPP;
+}
+
+/* Need some more work */
+static int ca_set_pid(void)
+{
+ /* We could make this more graceful ? */
+ return -EOPNOTSUPP;
+}
+
+static void put_command_and_length(u8 *data, int command, int length)
+{
+ data[0] = (command >> 16) & 0xff;
+ data[1] = (command >> 8) & 0xff;
+ data[2] = command & 0xff;
+ data[3] = length;
+}
+
+static void put_checksum(u8 *check_string, int length)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum.");
+ dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length);
+ check_string[length] = dst_check_sum (check_string, length);
+ dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]);
+}
+
+static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read)
+{
+ u8 reply;
+
+ mutex_lock(&state->dst_mutex);
+ dst_comm_init(state);
+ msleep(65);
+
+ if (write_dst(state, data, len)) {
+ dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ if ((dst_pio_disable(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed.");
+ goto error;
+ }
+ if (read_dst(state, &reply, GET_ACK) < 0) {
+ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ if (read) {
+ if (! dst_wait_dst_ready(state, LONG_DELAY)) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready");
+ goto error;
+ }
+ if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */
+ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover");
+ dst_error_recovery(state);
+ goto error;
+ }
+ }
+ mutex_unlock(&state->dst_mutex);
+ return 0;
+
+error:
+ mutex_unlock(&state->dst_mutex);
+ return -EIO;
+}
+
+
+static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read)
+{
+ u8 dst_ca_comm_err = 0;
+
+ while (dst_ca_comm_err < RETRIES) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " Put Command");
+ if (dst_ci_command(state, data, ca_string, len, read)) { // If error
+ dst_error_recovery(state);
+ dst_ca_comm_err++; // work required here.
+ } else {
+ break;
+ }
+ }
+
+ if(dst_ca_comm_err == RETRIES)
+ return -1;
+
+ return 0;
+}
+
+
+
+static int ca_get_app_info(struct dst_state *state)
+{
+ int length, str_length;
+ static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff};
+
+ put_checksum(&command[0], command[0]);
+ if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+ dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================");
+ dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]",
+ state->messages[7], (state->messages[8] << 8) | state->messages[9],
+ (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12]));
+ dprintk(verbose, DST_CA_INFO, 1, " ==================================================================================================");
+
+ // Transform dst message to correct application_info message
+ length = state->messages[5];
+ str_length = length - 6;
+ if (str_length < 0) {
+ str_length = 0;
+ dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering.");
+ }
+
+ // First, the command and length fields
+ put_command_and_length(&state->messages[0], CA_APP_INFO, length);
+
+ // Copy application_type, application_manufacturer and manufacturer_code
+ memcpy(&state->messages[4], &state->messages[7], 5);
+
+ // Set string length and copy string
+ state->messages[9] = str_length;
+ memcpy(&state->messages[10], &state->messages[12], str_length);
+
+ return 0;
+}
+
+static int ca_get_ca_info(struct dst_state *state)
+{
+ int srcPtr, dstPtr, i, num_ids;
+ static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff};
+ const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7;
+
+ put_checksum(&slot_command[0], slot_command[0]);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+ // Print raw data
+ dprintk(verbose, DST_CA_INFO, 0, " DST data = [");
+ for (i = 0; i < state->messages[0] + 1; i++) {
+ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]);
+ }
+ dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+ // Set the command and length of the output
+ num_ids = state->messages[in_num_ids_pos];
+ if (num_ids >= 100) {
+ num_ids = 100;
+ dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering.");
+ }
+ put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2);
+
+ dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = [");
+ srcPtr = in_system_id_pos;
+ dstPtr = out_system_id_pos;
+ for(i = 0; i < num_ids; i++) {
+ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]);
+ // Append to output
+ state->messages[dstPtr + 0] = state->messages[srcPtr + 0];
+ state->messages[dstPtr + 1] = state->messages[srcPtr + 1];
+ srcPtr += 2;
+ dstPtr += 2;
+ }
+ dprintk(verbose, DST_CA_INFO, 0, "]\n");
+
+ return 0;
+}
+
+static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg)
+{
+ int i;
+ u8 slot_cap[256];
+ static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff};
+
+ put_checksum(&slot_command[0], slot_command[0]);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !");
+
+ /* Will implement the rest soon */
+
+ dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]);
+ dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+ for (i = 0; i < slot_cap[0] + 1; i++)
+ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]);
+ dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+ p_ca_caps->slot_num = 1;
+ p_ca_caps->slot_type = 1;
+ p_ca_caps->descr_num = slot_cap[7];
+ p_ca_caps->descr_type = 1;
+
+ if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/* Need some more work */
+static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ return -EOPNOTSUPP;
+}
+
+
+static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg)
+{
+ int i;
+ static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
+
+ u8 *slot_info = state->messages;
+
+ put_checksum(&slot_command[0], 7);
+ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !");
+
+ /* Will implement the rest soon */
+
+ dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]);
+ dprintk(verbose, DST_CA_INFO, 0, "===================================\n");
+ for (i = 0; i < 8; i++)
+ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]);
+ dprintk(verbose, DST_CA_INFO, 0, "\n");
+
+ if (slot_info[4] & 0x80) {
+ p_ca_slot_info->flags = CA_CI_MODULE_PRESENT;
+ p_ca_slot_info->num = 1;
+ p_ca_slot_info->type = CA_CI;
+ } else if (slot_info[4] & 0x40) {
+ p_ca_slot_info->flags = CA_CI_MODULE_READY;
+ p_ca_slot_info->num = 1;
+ p_ca_slot_info->type = CA_CI;
+ } else
+ p_ca_slot_info->flags = 0;
+
+ if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ u8 i = 0;
+ u32 command = 0;
+
+ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg)))
+ return -EFAULT;
+
+ if (p_ca_message->msg) {
+ dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]",
+ 3, p_ca_message->msg);
+
+ for (i = 0; i < 3; i++) {
+ command = command | p_ca_message->msg[i];
+ if (i < 2)
+ command = command << 8;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command);
+
+ switch (command) {
+ case CA_APP_INFO:
+ memcpy(p_ca_message->msg, state->messages, 128);
+ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+ return -EFAULT;
+ break;
+ case CA_INFO:
+ memcpy(p_ca_message->msg, state->messages, 128);
+ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) )
+ return -EFAULT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length)
+{
+ if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) {
+ hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */
+ hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */
+ } else {
+ if (length > 247) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !");
+ return -1;
+ }
+ hw_buffer->msg[0] = (length & 0xff) + 7;
+ hw_buffer->msg[1] = 0x40;
+ hw_buffer->msg[2] = 0x03;
+ hw_buffer->msg[3] = 0x00;
+ hw_buffer->msg[4] = 0x03;
+ hw_buffer->msg[5] = length & 0xff;
+ hw_buffer->msg[6] = 0x00;
+
+ /*
+ * Need to compute length for EN50221 section 8.3.2, for the time being
+ * assuming 8.3.2 is not applicable
+ */
+ memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length);
+ }
+
+ return 0;
+}
+
+static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply)
+{
+ if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed.");
+ dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST.");
+ rdc_reset_state(state);
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success.");
+
+ return 0;
+}
+
+static u32 asn_1_decode(u8 *asn_1_array)
+{
+ u8 length_field = 0, word_count = 0, count = 0;
+ u32 length = 0;
+
+ length_field = asn_1_array[0];
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field);
+ if (length_field < 0x80) {
+ length = length_field & 0x7f;
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length);
+ } else {
+ word_count = length_field & 0x7f;
+ for (count = 0; count < word_count; count++) {
+ length = length << 8;
+ length += asn_1_array[count + 1];
+ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length);
+ }
+ }
+ return length;
+}
+
+static int debug_string(u8 *msg, u32 length, u32 offset)
+{
+ u32 i;
+
+ dprintk(verbose, DST_CA_DEBUG, 0, " String=[ ");
+ for (i = offset; i < length; i++)
+ dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]);
+ dprintk(verbose, DST_CA_DEBUG, 0, "]\n");
+
+ return 0;
+}
+
+
+static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query)
+{
+ u32 length = 0;
+ u8 tag_length = 8;
+
+ length = asn_1_decode(&p_ca_message->msg[3]);
+ dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length);
+ debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */
+
+ memset(hw_buffer->msg, '\0', length);
+ handle_dst_tag(state, p_ca_message, hw_buffer, length);
+ put_checksum(hw_buffer->msg, hw_buffer->msg[0]);
+
+ debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */
+ write_to_8820(state, hw_buffer, (length + tag_length), reply);
+
+ return 0;
+}
+
+
+/* Board supports CA PMT reply ? */
+static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer)
+{
+ int ca_pmt_reply_test = 0;
+
+ /* Do test board */
+ /* Not there yet but soon */
+
+ /* CA PMT Reply capable */
+ if (ca_pmt_reply_test) {
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+ return -1;
+ }
+
+ /* Process CA PMT Reply */
+ /* will implement soon */
+ dprintk(verbose, DST_CA_ERROR, 1, " Not there yet");
+ }
+ /* CA PMT Reply not capable */
+ if (!ca_pmt_reply_test) {
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !");
+ return -1;
+ }
+ dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !");
+ /* put a dummy message */
+
+ }
+ return 0;
+}
+
+static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
+{
+ int i = 0;
+
+ u32 command = 0;
+ struct ca_msg *hw_buffer;
+ int result = 0;
+
+ if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+ return -ENOMEM;
+ }
+ dprintk(verbose, DST_CA_DEBUG, 1, " ");
+
+ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) {
+ result = -EFAULT;
+ goto free_mem_and_exit;
+ }
+
+
+ if (p_ca_message->msg) {
+ /* EN50221 tag */
+ command = 0;
+
+ for (i = 0; i < 3; i++) {
+ command = command | p_ca_message->msg[i];
+ if (i < 2)
+ command = command << 8;
+ }
+ dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command);
+
+ switch (command) {
+ case CA_PMT:
+ dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT");
+ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !");
+ break;
+ case CA_PMT_REPLY:
+ dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY");
+ /* Have to handle the 2 basic types of cards here */
+ if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !");
+ break;
+ case CA_APP_INFO_ENQUIRY: // only for debugging
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information");
+
+ if ((ca_get_app_info(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !");
+ break;
+ case CA_INFO_ENQUIRY:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information");
+
+ if ((ca_get_ca_info(state)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !");
+ break;
+ }
+ }
+free_mem_and_exit:
+ kfree (hw_buffer);
+
+ return result;
+}
+
+static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg)
+{
+ struct dvb_device *dvbdev;
+ struct dst_state *state;
+ struct ca_slot_info *p_ca_slot_info;
+ struct ca_caps *p_ca_caps;
+ struct ca_msg *p_ca_message;
+ void __user *arg = (void __user *)ioctl_arg;
+ int result = 0;
+
+ mutex_lock(&dst_ca_mutex);
+ dvbdev = file->private_data;
+ state = (struct dst_state *)dvbdev->priv;
+ p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
+ p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
+ p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
+ if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) {
+ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure");
+ result = -ENOMEM;
+ goto free_mem_and_exit;
+ }
+
+ /* We have now only the standard ioctl's, the driver is upposed to handle internals. */
+ switch (cmd) {
+ case CA_SEND_MSG:
+ dprintk(verbose, DST_CA_INFO, 1, " Sending message");
+ if ((ca_send_message(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ break;
+ case CA_GET_MSG:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting message");
+ if ((ca_get_message(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !");
+ break;
+ case CA_RESET:
+ dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST");
+ dst_error_bailout(state);
+ msleep(4000);
+ break;
+ case CA_GET_SLOT_INFO:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info");
+ if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !");
+ break;
+ case CA_GET_CAP:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities");
+ if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !");
+ break;
+ case CA_GET_DESCR_INFO:
+ dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description");
+ if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !");
+ break;
+ case CA_SET_DESCR:
+ dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler");
+ if ((ca_set_slot_descr()) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !");
+ break;
+ case CA_SET_PID:
+ dprintk(verbose, DST_CA_INFO, 1, " Setting PID");
+ if ((ca_set_pid()) < 0) {
+ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !");
+ result = -1;
+ goto free_mem_and_exit;
+ }
+ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !");
+ default:
+ result = -EOPNOTSUPP;
+ };
+ free_mem_and_exit:
+ kfree (p_ca_message);
+ kfree (p_ca_slot_info);
+ kfree (p_ca_caps);
+
+ mutex_unlock(&dst_ca_mutex);
+ return result;
+}
+
+static int dst_ca_open(struct inode *inode, struct file *file)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file);
+ try_module_get(THIS_MODULE);
+
+ return 0;
+}
+
+static int dst_ca_release(struct inode *inode, struct file *file)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device closed.");
+ module_put(THIS_MODULE);
+
+ return 0;
+}
+
+static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
+{
+ ssize_t bytes_read = 0;
+
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device read.");
+
+ return bytes_read;
+}
+
+static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
+{
+ dprintk(verbose, DST_CA_DEBUG, 1, " Device write.");
+
+ return 0;
+}
+
+static const struct file_operations dst_ca_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = dst_ca_ioctl,
+ .open = dst_ca_open,
+ .release = dst_ca_release,
+ .read = dst_ca_read,
+ .write = dst_ca_write,
+ .llseek = noop_llseek,
+};
+
+static struct dvb_device dvbdev_ca = {
+ .priv = NULL,
+ .users = 1,
+ .readers = 1,
+ .writers = 1,
+ .fops = &dst_ca_fops
+};
+
+struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
+{
+ struct dvb_device *dvbdev;
+
+ dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
+ if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
+ dst->dst_ca = dvbdev;
+ return dst->dst_ca;
+ }
+
+ return NULL;
+}
+
+EXPORT_SYMBOL(dst_ca_attach);
+
+MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/bt8xx/dst_ca.h b/drivers/media/pci/bt8xx/dst_ca.h
index 59cd0ddd6d8e..59cd0ddd6d8e 100644
--- a/drivers/media/dvb/bt8xx/dst_ca.h
+++ b/drivers/media/pci/bt8xx/dst_ca.h
diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h
index d70d98f1a571..d70d98f1a571 100644
--- a/drivers/media/dvb/bt8xx/dst_common.h
+++ b/drivers/media/pci/bt8xx/dst_common.h
diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/pci/bt8xx/dst_priv.h
index 3974a4c6ebe7..3974a4c6ebe7 100644
--- a/drivers/media/dvb/bt8xx/dst_priv.h
+++ b/drivers/media/pci/bt8xx/dst_priv.h
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c
index 81fab9adc1ca..81fab9adc1ca 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/pci/bt8xx/dvb-bt8xx.h
index 4499ed2ac0ed..4499ed2ac0ed 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.h
diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig
new file mode 100644
index 000000000000..c675b83c43a9
--- /dev/null
+++ b/drivers/media/pci/cx18/Kconfig
@@ -0,0 +1,35 @@
+config VIDEO_CX18
+ tristate "Conexant cx23418 MPEG encoder support"
+ depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C
+ select I2C_ALGOBIT
+ select VIDEOBUF_VMALLOC
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CS5345
+ select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for Conexant cx23418 based
+ PCI combo video recorder devices.
+
+ This is used in devices such as the Hauppauge HVR-1600
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18.
+
+config VIDEO_CX18_ALSA
+ tristate "Conexant 23418 DMA audio support"
+ depends on VIDEO_CX18 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 23418 based TV cards using ALSA.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx18-alsa.
diff --git a/drivers/media/pci/cx18/Makefile b/drivers/media/pci/cx18/Makefile
new file mode 100644
index 000000000000..d3ff1545c2c5
--- /dev/null
+++ b/drivers/media/pci/cx18/Makefile
@@ -0,0 +1,13 @@
+cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
+ cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
+ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
+ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
+ cx18-dvb.o cx18-io.o
+cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
+
+obj-$(CONFIG_VIDEO_CX18) += cx18.o
+obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c
index 6d2a98246b6d..6d2a98246b6d 100644
--- a/drivers/media/video/cx18/cx18-alsa-main.c
+++ b/drivers/media/pci/cx18/cx18-alsa-main.c
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c
index 341bddc00b77..341bddc00b77 100644
--- a/drivers/media/video/cx18/cx18-alsa-mixer.c
+++ b/drivers/media/pci/cx18/cx18-alsa-mixer.c
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.h b/drivers/media/pci/cx18/cx18-alsa-mixer.h
index ec9238793f6f..ec9238793f6f 100644
--- a/drivers/media/video/cx18/cx18-alsa-mixer.h
+++ b/drivers/media/pci/cx18/cx18-alsa-mixer.h
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c
index 7a5b84a86bb3..7a5b84a86bb3 100644
--- a/drivers/media/video/cx18/cx18-alsa-pcm.c
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/pci/cx18/cx18-alsa-pcm.h
index d26e51f94577..d26e51f94577 100644
--- a/drivers/media/video/cx18/cx18-alsa-pcm.h
+++ b/drivers/media/pci/cx18/cx18-alsa-pcm.h
diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/pci/cx18/cx18-alsa.h
index 447da374c9e8..447da374c9e8 100644
--- a/drivers/media/video/cx18/cx18-alsa.h
+++ b/drivers/media/pci/cx18/cx18-alsa.h
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/pci/cx18/cx18-audio.c
index 35268923911c..35268923911c 100644
--- a/drivers/media/video/cx18/cx18-audio.c
+++ b/drivers/media/pci/cx18/cx18-audio.c
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/pci/cx18/cx18-audio.h
index 2731d29b0ab9..2731d29b0ab9 100644
--- a/drivers/media/video/cx18/cx18-audio.h
+++ b/drivers/media/pci/cx18/cx18-audio.h
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/pci/cx18/cx18-av-audio.c
index 4a24ffb17a7d..4a24ffb17a7d 100644
--- a/drivers/media/video/cx18/cx18-av-audio.c
+++ b/drivers/media/pci/cx18/cx18-av-audio.c
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
index f164b7f610a5..f164b7f610a5 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/pci/cx18/cx18-av-core.c
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/pci/cx18/cx18-av-core.h
index e9c69d9c9e4a..e9c69d9c9e4a 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/pci/cx18/cx18-av-core.h
diff --git a/drivers/media/pci/cx18/cx18-av-firmware.c b/drivers/media/pci/cx18/cx18-av-firmware.c
new file mode 100644
index 000000000000..a34fd082b76e
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-firmware.c
@@ -0,0 +1,225 @@
+/*
+ * cx18 ADEC firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include <linux/firmware.h>
+
+#define CX18_AUDIO_ENABLE 0xc72014
+#define CX18_AI1_MUX_MASK 0x30
+#define CX18_AI1_MUX_I2S1 0x00
+#define CX18_AI1_MUX_I2S2 0x10
+#define CX18_AI1_MUX_843_I2S 0x20
+#define CX18_AI1_MUX_INVALID 0x30
+
+#define FWFILE "v4l-cx23418-dig.fw"
+
+static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw)
+{
+ struct v4l2_subdev *sd = &cx->av_state.sd;
+ int ret = 0;
+ const u8 *data;
+ u32 size;
+ int addr;
+ u32 expected, dl_control;
+
+ /* Ensure we put the 8051 in reset and enable firmware upload mode */
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ do {
+ dl_control &= 0x00ffffff;
+ dl_control |= 0x0f000000;
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control);
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ } while ((dl_control & 0xff000000) != 0x0f000000);
+
+ /* Read and auto increment until at address 0x0000 */
+ while (dl_control & 0x3fff)
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+
+ data = fw->data;
+ size = fw->size;
+ for (addr = 0; addr < size; addr++) {
+ dl_control &= 0xffff3fff; /* ignore top 2 bits of address */
+ expected = 0x0f000000 | ((u32)data[addr] << 16) | addr;
+ if (expected != dl_control) {
+ CX18_ERR_DEV(sd, "verification of %s firmware load "
+ "failed: expected %#010x got %#010x\n",
+ FWFILE, expected, dl_control);
+ ret = -EIO;
+ break;
+ }
+ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
+ }
+ if (ret == 0)
+ CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n",
+ FWFILE, size);
+ return ret;
+}
+
+int cx18_av_loadfw(struct cx18 *cx)
+{
+ struct v4l2_subdev *sd = &cx->av_state.sd;
+ const struct firmware *fw = NULL;
+ u32 size;
+ u32 u, v;
+ const u8 *ptr;
+ int i;
+ int retries1 = 0;
+
+ if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
+ CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
+ return -EINVAL;
+ }
+
+ /* The firmware load often has byte errors, so allow for several
+ retries, both at byte level and at the firmware load level. */
+ while (retries1 < 5) {
+ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
+
+ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
+ cx18_av_write4_expect(cx, 0x8100, 0x00010000,
+ 0x00008430, 0xffffffff); /* cx25843 */
+
+ /* Put the 8051 in reset and enable firmware upload */
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
+
+ ptr = fw->data;
+ size = fw->size;
+
+ for (i = 0; i < size; i++) {
+ u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
+ u32 value = 0;
+ int retries2;
+ int unrec_err = 0;
+
+ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
+ retries2++) {
+ cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
+ dl_control);
+ udelay(10);
+ value = cx18_av_read4(cx, CXADEC_DL_CTL);
+ if (value == dl_control)
+ break;
+ /* Check if we can correct the byte by changing
+ the address. We can only write the lower
+ address byte of the address. */
+ if ((value & 0x3F00) != (dl_control & 0x3F00)) {
+ unrec_err = 1;
+ break;
+ }
+ }
+ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
+ break;
+ }
+ if (i == size)
+ break;
+ retries1++;
+ }
+ if (retries1 >= 5) {
+ CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
+ release_firmware(fw);
+ return -EIO;
+ }
+
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x03000000 | fw->size, 0x03000000, 0x13000000);
+
+ CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
+
+ if (cx18_av_verifyfw(cx, fw) == 0)
+ cx18_av_write4_expect(cx, CXADEC_DL_CTL,
+ 0x13000000 | fw->size, 0x13000000, 0x13000000);
+
+ /* Output to the 416 */
+ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
+
+ /* Audio input control 1 set to Sony mode */
+ /* Audio output input 2 is 0 for slave operation input */
+ /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
+
+ /* Audio output control 1 is set to Sony mode */
+ /* Audio output control 2 is set to 1 for master mode */
+ /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
+ /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
+ after WS transition for first bit of audio word. */
+ /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
+ are generated) */
+ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+ /* set alt I2s master clock to /0x16 and enable alt divider i2s
+ passthrough */
+ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
+
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
+ /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
+
+ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
+ /* Register 0x09CC is defined by the Merlin firmware, and doesn't
+ have a name in the spec. */
+ cx18_av_write4(cx, 0x09CC, 1);
+
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ /* If bit 11 is 1, clear bit 10 */
+ if (v & 0x800)
+ cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
+ 0, 0x400);
+
+ /* Toggle the AI1 MUX */
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ u = v & CX18_AI1_MUX_MASK;
+ v &= ~CX18_AI1_MUX_MASK;
+ if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) {
+ /* Switch to I2S1 */
+ v |= CX18_AI1_MUX_I2S1;
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+ /* Switch back to the A/V decoder core I2S output */
+ v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S;
+ } else {
+ /* Switch to the A/V decoder core I2S output */
+ v |= CX18_AI1_MUX_843_I2S;
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+ /* Switch back to I2S1 or I2S2 */
+ v = (v & ~CX18_AI1_MUX_MASK) | u;
+ }
+ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
+ v, CX18_AI1_MUX_MASK);
+
+ /* Enable WW auto audio standard detection */
+ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+ v |= 0xFF; /* Auto by default */
+ v |= 0x400; /* Stereo by default */
+ v |= 0x14000000;
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
+
+ release_firmware(fw);
+ return 0;
+}
+
+MODULE_FIRMWARE(FWFILE);
diff --git a/drivers/media/pci/cx18/cx18-av-vbi.c b/drivers/media/pci/cx18/cx18-av-vbi.c
new file mode 100644
index 000000000000..246982841fec
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-av-vbi.c
@@ -0,0 +1,313 @@
+/*
+ * cx18 ADEC VBI functions
+ *
+ * Derived from cx25840-vbi.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#include "cx18-driver.h"
+
+/*
+ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
+ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
+ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
+ * (should!) look like:
+ * 4 byte EAV code: 0xff 0x00 0x00 0xRP
+ * unknown number of possible idle bytes
+ * 3 byte Anc data preamble: 0x00 0xff 0xff
+ * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
+ * 1 byte secondary data id: nessssss (parity bits, SDID bits)
+ * 1 byte data word count: necccccc (parity bits, NN Dword count)
+ * 2 byte Internal DID: VBI-line-# 0x80
+ * NN data bytes
+ * 1 byte checksum
+ * Fill bytes needed to fil out to 4*NN bytes of payload
+ *
+ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
+ * in the vertical blanking interval are:
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code, just
+ * before the first active region line and for active lines, they are:
+ * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
+ * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
+ *
+ * The user application DID bytes we care about are:
+ * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
+ * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
+ *
+ */
+static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
+
+struct vbi_anc_data {
+ /* u8 eav[4]; */
+ /* u8 idle[]; Variable number of idle bytes */
+ u8 preamble[3];
+ u8 did;
+ u8 sdid;
+ u8 data_count;
+ u8 idid[2];
+ u8 payload[1]; /* data_count of payload */
+ /* u8 checksum; */
+ /* u8 fill[]; Variable number of fill bytes */
+};
+
+static int odd_parity(u8 c)
+{
+ c ^= (c >> 4);
+ c ^= (c >> 2);
+ c ^= (c >> 1);
+
+ return c & 1;
+}
+
+static int decode_vps(u8 *dst, u8 *p)
+{
+ static const u8 biphase_tbl[] = {
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
+ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
+ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
+ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
+ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
+ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
+ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
+ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
+ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
+ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
+ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
+ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
+ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
+ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
+ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
+ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
+ };
+
+ u8 c, err = 0;
+ int i;
+
+ for (i = 0; i < 2 * 13; i += 2) {
+ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
+ c = (biphase_tbl[p[i + 1]] & 0xf) |
+ ((biphase_tbl[p[i]] & 0xf) << 4);
+ dst[i / 2] = c;
+ }
+
+ return err & 0xf0;
+}
+
+int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ static const u16 lcr2vbi[] = {
+ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+ V4L2_SLICED_CAPTION_525, /* 6 */
+ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+ 0, 0, 0, 0
+ };
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i;
+
+ memset(svbi->service_lines, 0, sizeof(svbi->service_lines));
+ svbi->service_set = 0;
+
+ /* we're done if raw VBI is active */
+ if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+ return 0;
+
+ if (is_pal) {
+ for (i = 7; i <= 23; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 7);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ } else {
+ for (i = 10; i <= 21; i++) {
+ u8 v = cx18_av_read(cx, 0x424 + i - 10);
+
+ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+ svbi->service_set |= svbi->service_lines[0][i] |
+ svbi->service_lines[1][i];
+ }
+ }
+ return 0;
+}
+
+int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+ /* VBI Offset */
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+ cx18_av_write(cx, 0x404, 0x2e);
+ return 0;
+}
+
+int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ int is_pal = !(state->std & V4L2_STD_525_60);
+ int i, x;
+ u8 lcr[24];
+
+ for (x = 0; x <= 23; x++)
+ lcr[x] = 0x00;
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+ /* Sliced VBI */
+ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
+ cx18_av_write(cx, 0x406, 0x13);
+ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+
+ /* Force impossible lines to 0 */
+ if (is_pal) {
+ for (i = 0; i <= 6; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ } else {
+ for (i = 0; i <= 9; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+
+ for (i = 22; i <= 23; i++)
+ svbi->service_lines[0][i] =
+ svbi->service_lines[1][i] = 0;
+ }
+
+ /* Build register values for requested service lines */
+ for (i = 7; i <= 23; i++) {
+ for (x = 0; x <= 1; x++) {
+ switch (svbi->service_lines[1-x][i]) {
+ case V4L2_SLICED_TELETEXT_B:
+ lcr[i] |= 1 << (4 * x);
+ break;
+ case V4L2_SLICED_WSS_625:
+ lcr[i] |= 4 << (4 * x);
+ break;
+ case V4L2_SLICED_CAPTION_525:
+ lcr[i] |= 6 << (4 * x);
+ break;
+ case V4L2_SLICED_VPS:
+ lcr[i] |= 9 << (4 * x);
+ break;
+ }
+ }
+ }
+
+ if (is_pal) {
+ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+ cx18_av_write(cx, i, lcr[6 + x]);
+ } else {
+ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+ cx18_av_write(cx, i, lcr[9 + x]);
+ for (i = 0x431; i <= 0x434; i++)
+ cx18_av_write(cx, i, 0);
+ }
+
+ cx18_av_write(cx, 0x43c, 0x16);
+ /* Should match vblank set in cx18_av_std_setup() */
+ cx18_av_write(cx, 0x474, is_pal ? 38 : 26);
+ return 0;
+}
+
+int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
+ struct v4l2_decode_vbi_line *vbi)
+{
+ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+ struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
+ u8 *p;
+ int did, sdid, l, err = 0;
+
+ /*
+ * Check for the ancillary data header for sliced VBI
+ */
+ if (anc->preamble[0] ||
+ anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
+ (anc->did != sliced_vbi_did[0] &&
+ anc->did != sliced_vbi_did[1])) {
+ vbi->line = vbi->type = 0;
+ return 0;
+ }
+
+ did = anc->did;
+ sdid = anc->sdid & 0xf;
+ l = anc->idid[0] & 0x3f;
+ l += state->slicer_line_offset;
+ p = anc->payload;
+
+ /* Decode the SDID set by the slicer */
+ switch (sdid) {
+ case 1:
+ sdid = V4L2_SLICED_TELETEXT_B;
+ break;
+ case 4:
+ sdid = V4L2_SLICED_WSS_625;
+ break;
+ case 6:
+ sdid = V4L2_SLICED_CAPTION_525;
+ err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ break;
+ case 9:
+ sdid = V4L2_SLICED_VPS;
+ if (decode_vps(p, p) != 0)
+ err = 1;
+ break;
+ default:
+ sdid = 0;
+ err = 1;
+ break;
+ }
+
+ vbi->type = err ? 0 : sdid;
+ vbi->line = err ? 0 : l;
+ vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
+ vbi->p = p;
+ return 0;
+}
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c
index c07c849b1aaf..c07c849b1aaf 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/pci/cx18/cx18-cards.c
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/pci/cx18/cx18-cards.h
index add7391ecaba..add7391ecaba 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/pci/cx18/cx18-cards.h
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
index 282a3d29fdaa..282a3d29fdaa 100644
--- a/drivers/media/video/cx18/cx18-controls.c
+++ b/drivers/media/pci/cx18/cx18-controls.c
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/pci/cx18/cx18-controls.h
index cb5dfc7b2054..cb5dfc7b2054 100644
--- a/drivers/media/video/cx18/cx18-controls.h
+++ b/drivers/media/pci/cx18/cx18-controls.h
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
new file mode 100644
index 000000000000..c67733d32c8a
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -0,0 +1,1360 @@
+/*
+ * cx18 driver initialization and card probing
+ *
+ * Derived from ivtv-driver.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-cards.h"
+#include "cx18-i2c.h"
+#include "cx18-irq.h"
+#include "cx18-gpio.h"
+#include "cx18-firmware.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-av-core.h"
+#include "cx18-scb.h"
+#include "cx18-mailbox.h"
+#include "cx18-ioctl.h"
+#include "cx18-controls.h"
+#include "tuner-xc2028.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a Compro H900 with. Normally this would give a
+ video1 device together with a radio0 device for the Compro. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int cx18_first_minor;
+
+/* Callback for registering extensions */
+int (*cx18_ext_init)(struct cx18 *);
+EXPORT_SYMBOL(cx18_ext_init);
+
+/* add your revision and whatnot here */
+static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
+static atomic_t cx18_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[CX18_MAX_CARDS];
+static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static unsigned cardtype_c = 1;
+static unsigned tuner_c = 1;
+static unsigned radio_c = 1;
+static char pal[] = "--";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
+static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
+static int cx18_pci_latency = 1;
+
+static int mmio_ndelay;
+static int retry_mmio = 1;
+
+int cx18_debug;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug, cx18_debug, int, 0644);
+module_param(mmio_ndelay, int, 0644);
+module_param(retry_mmio, int, 0644);
+module_param(cx18_pci_latency, int, 0644);
+module_param(cx18_first_minor, int, 0644);
+
+module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
+ "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
+ "\t\t\t 3 = Compro VideoMate H900\n"
+ "\t\t\t 4 = Yuan MPC718\n"
+ "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+ "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+ "\t\t\t 7 = Leadtek WinFast PVR2100\n"
+ "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
+ "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
+ "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n"
+ "\t\t\t 4/0x0004: mailbox\n"
+ "\t\t\t 8/0x0008: dma\n"
+ "\t\t\t 16/0x0010: ioctl\n"
+ "\t\t\t 32/0x0020: file\n"
+ "\t\t\t 64/0x0040: i2c\n"
+ "\t\t\t128/0x0080: irq\n"
+ "\t\t\t256/0x0100: high volume\n");
+MODULE_PARM_DESC(cx18_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(retry_mmio,
+ "(Deprecated) MMIO writes are now always checked and retried\n"
+ "\t\t\tEffectively: 1 [Yes]");
+MODULE_PARM_DESC(mmio_ndelay,
+ "(Deprecated) MMIO accesses are now never purposely delayed\n"
+ "\t\t\tEffectively: 0 ns");
+MODULE_PARM_DESC(enc_ts_buffers,
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "(Deprecated) Encoder IDX buffer memory (MB)\n"
+ "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
+ "\t\t\tDefault: 1 [Enabled]");
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
+ "\t\t\t(multiples of size required for 64 index entries)\n"
+ "\t\t\tDefault: 2");
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
+ "\t\t\t(multiples of size required for 32 screen lines)\n"
+ "\t\t\tDefault: 102");
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers");
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
+
+MODULE_PARM_DESC(cx18_first_minor,
+ "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("CX23418 driver");
+MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(CX18_VERSION);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
+
+ /* Make sure cx18-alsa module is loaded */
+ request_module("cx18-alsa");
+
+ /* Initialize cx18-alsa for this instance of the cx18 device */
+ if (cx18_ext_init != NULL)
+ cx18_ext_init(dev);
+}
+
+static void request_modules(struct cx18 *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct cx18 *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+/* Generic utility functions */
+int cx18_msleep_timeout(unsigned int msecs, int intr)
+{
+ long int timeout = msecs_to_jiffies(msecs);
+ int sig;
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ sig = intr ? signal_pending(current) : 0;
+ } while (!sig && timeout);
+ return sig;
+}
+
+/* Release ioremapped memory */
+static void cx18_iounmap(struct cx18 *cx)
+{
+ if (cx == NULL)
+ return;
+
+ /* Release io memory */
+ if (cx->enc_mem != NULL) {
+ CX18_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(cx->enc_mem);
+ cx->enc_mem = NULL;
+ }
+}
+
+static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
+{
+ int i;
+
+ CX18_INFO("eeprom dump:\n");
+ for (i = 0; i < len; i++) {
+ if (0 == (i % 16))
+ CX18_INFO("eeprom %02x:", i);
+ printk(KERN_CONT " %02x", eedata[i]);
+ if (15 == (i % 16))
+ printk(KERN_CONT "\n");
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+{
+ struct i2c_client c;
+ u8 eedata[256];
+
+ memset(&c, 0, sizeof(c));
+ strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
+ c.adapter = &cx->i2c_adap[0];
+ c.addr = 0xA0 >> 1;
+
+ memset(tv, 0, sizeof(*tv));
+ if (tveeprom_read(&c, eedata, sizeof(eedata)))
+ return;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ case CX18_CARD_HVR_1600_S5H1411:
+ tveeprom_hauppauge_analog(&c, tv, eedata);
+ break;
+ case CX18_CARD_YUAN_MPC718:
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ tv->model = 0x718;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
+ eedata[2], eedata[1], eedata[4], eedata[3]);
+ break;
+ default:
+ tv->model = 0xffffffff;
+ cx18_eeprom_dump(cx, eedata, sizeof(eedata));
+ break;
+ }
+}
+
+static void cx18_process_eeprom(struct cx18 *cx)
+{
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ /* Note: the Samsung memory models cannot be reliably determined
+ from the model number. Use the cardtype module option if you
+ have one of these preproduction models. */
+ switch (tv.model) {
+ case 74301: /* Retail models */
+ case 74321:
+ case 74351: /* OEM models */
+ case 74361:
+ /* Digital side is s5h1411/tda18271 */
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
+ break;
+ case 74021: /* Retail models */
+ case 74031:
+ case 74041:
+ case 74141:
+ case 74541: /* OEM models */
+ case 74551:
+ case 74591:
+ case 74651:
+ case 74691:
+ case 74751:
+ case 74891:
+ /* Digital side is s5h1409/mxl5005s */
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ case 0x718:
+ return;
+ case 0xffffffff:
+ CX18_INFO("Unknown EEPROM encoding\n");
+ return;
+ case 0:
+ CX18_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
+ "(cardtype=1)\n", tv.model);
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ break;
+ }
+
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+
+ CX18_INFO("Autodetected %s\n", cx->card_name);
+
+ if (tv.tuner_type == TUNER_ABSENT)
+ CX18_ERR("tveeprom cannot autodetect tuner!\n");
+
+ if (cx->options.tuner == -1)
+ cx->options.tuner = tv.tuner_type;
+ if (cx->options.radio == -1)
+ cx->options.radio = (tv.has_radio != 0);
+
+ if (cx->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \
+ V4L2_STD_MN | \
+ V4L2_STD_PAL_I | \
+ V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
+ V4L2_STD_DK)
+ if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
+ == TVEEPROM_TUNER_FORMAT_ALL) {
+ CX18_DEBUG_INFO("Worldwide tuner detected\n");
+ cx->std = V4L2_STD_ALL;
+ } else if (tv.tuner_formats & V4L2_STD_PAL) {
+ CX18_DEBUG_INFO("PAL tuner detected\n");
+ cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ CX18_DEBUG_INFO("NTSC tuner detected\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ CX18_DEBUG_INFO("SECAM tuner detected\n");
+ cx->std |= V4L2_STD_SECAM_L;
+ } else {
+ CX18_INFO("No tuner detected, default to NTSC-M\n");
+ cx->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id cx18_parse_std(struct cx18 *cx)
+{
+ switch (pal[0]) {
+ case '6':
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ return V4L2_STD_PAL_BG;
+ case 'h':
+ case 'H':
+ return V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ CX18_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ CX18_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ CX18_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void cx18_process_options(struct cx18 *cx)
+{
+ int i, j;
+
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+ /* Ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */
+ cx->options.megabytes[i] <= 0 || /* User said 0 MB total */
+ cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+ /*
+ * YUV is a special case where the stream_buf_size needs to be
+ * an integral multiple of 33.75 kB (storage for 32 screens
+ * lines to maintain alignment in case of lost buffers).
+ *
+ * IDX is a special case where the stream_buf_size should be
+ * an integral multiple of 1.5 kB (storage for 64 index entries
+ * to maintain alignment in case of lost buffers).
+ *
+ */
+ if (i == CX18_ENC_STREAM_TYPE_YUV) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_YUV_BUFSIZE;
+ } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
+ cx->stream_buf_size[i] *= 1024;
+ cx->stream_buf_size[i] -=
+ (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
+
+ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
+ cx->stream_buf_size[i] =
+ CX18_UNIT_ENC_IDX_BUFSIZE;
+ }
+ /*
+ * YUV and IDX are special cases where the stream_buf_size is
+ * now in bytes.
+ * VBI is a special case where the stream_buf_size is fixed
+ * and already in bytes
+ */
+ if (i == CX18_ENC_STREAM_TYPE_VBI ||
+ i == CX18_ENC_STREAM_TYPE_YUV ||
+ i == CX18_ENC_STREAM_TYPE_IDX) {
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024 * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i]/(1024 * 1024);
+ }
+ } else {
+ /* All other streams have stream_buf_size in kB here */
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] =
+ cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+ } else {
+ /* N.B. This might round down to 0 */
+ cx->options.megabytes[i] =
+ cx->stream_buffers[i]
+ * cx->stream_buf_size[i] / 1024;
+ }
+ /* convert from kB to bytes */
+ cx->stream_buf_size[i] *= 1024;
+ }
+ CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
+ "%d bytes\n", i, cx->options.megabytes[i],
+ cx->stream_buffers[i], cx->stream_buf_size[i]);
+ }
+
+ cx->options.cardtype = cardtype[cx->instance];
+ cx->options.tuner = tuner[cx->instance];
+ cx->options.radio = radio[cx->instance];
+
+ cx->std = cx18_parse_std(cx);
+ if (cx->options.cardtype == -1) {
+ CX18_INFO("Ignore card\n");
+ return;
+ }
+ cx->card = cx18_get_card(cx->options.cardtype - 1);
+ if (cx->card)
+ CX18_INFO("User specified %s card\n", cx->card->name);
+ else if (cx->options.cardtype != 0)
+ CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+ if (cx->card == NULL) {
+ if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_INFO("Autodetected Hauppauge card\n");
+ }
+ }
+ if (cx->card == NULL) {
+ for (i = 0; (cx->card = cx18_get_card(i)); i++) {
+ if (cx->card->pci_list == NULL)
+ continue;
+ for (j = 0; cx->card->pci_list[j].device; j++) {
+ if (cx->pci_dev->device !=
+ cx->card->pci_list[j].device)
+ continue;
+ if (cx->pci_dev->subsystem_vendor !=
+ cx->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (cx->pci_dev->subsystem_device !=
+ cx->card->pci_list[j].subsystem_device)
+ continue;
+ CX18_INFO("Autodetected %s card\n", cx->card->name);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (cx->card == NULL) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+ cx->pci_dev->vendor, cx->pci_dev->device);
+ CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+ cx->pci_dev->subsystem_vendor,
+ cx->pci_dev->subsystem_device);
+ CX18_ERR("Defaulting to %s card\n", cx->card->name);
+ CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
+ }
+ cx->v4l2_cap = cx->card->v4l2_capabilities;
+ cx->card_name = cx->card->name;
+ cx->card_i2c = cx->card->i2c;
+}
+
+static int __devinit cx18_create_in_workq(struct cx18 *cx)
+{
+ snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
+ cx->v4l2_dev.name);
+ cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
+ if (cx->in_work_queue == NULL) {
+ CX18_ERR("Unable to create incoming mailbox handler thread\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
+ cx->in_work_order[i].cx = cx;
+ cx->in_work_order[i].str = cx->epu_debug_str;
+ INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
+ }
+}
+
+/* Precondition: the cx18 structure has been memset to 0. Only
+ the dev and instance fields have been filled in.
+ No assumptions on the card type may be made here (see cx18_init_struct2
+ for that).
+ */
+static int __devinit cx18_init_struct1(struct cx18 *cx)
+{
+ int ret;
+
+ cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+
+ mutex_init(&cx->serialize_lock);
+ mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
+
+ ret = cx18_create_in_workq(cx);
+ if (ret)
+ return ret;
+
+ cx18_init_in_work_orders(cx);
+
+ /* start counting open_id at 1 */
+ cx->open_id = 1;
+
+ /* Initial settings */
+ cx->cxhdl.port = CX2341X_PORT_MEMORY;
+ cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+ cx->cxhdl.ops = &cx18_cxhdl_ops;
+ cx->cxhdl.func = cx18_api_func;
+ cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ ret = cx2341x_handler_init(&cx->cxhdl, 50);
+ if (ret)
+ return ret;
+ cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
+
+ cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
+ cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
+ cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
+ (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
+ (cx->cxhdl.video_median_filter_type->cur.val << 2);
+
+ init_waitqueue_head(&cx->cap_w);
+ init_waitqueue_head(&cx->mb_apu_waitq);
+ init_waitqueue_head(&cx->mb_cpu_waitq);
+ init_waitqueue_head(&cx->dma_waitq);
+
+ /* VBI */
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+
+ /* IVTV style VBI insertion into MPEG streams */
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
+ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
+ list_add(&cx->vbi.sliced_mpeg_buf.list,
+ &cx->vbi.sliced_mpeg_mdl.buf_list);
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit cx18_init_struct2(struct cx18 *cx)
+{
+ int i;
+
+ for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
+ if (cx->card->video_inputs[i].video_type == 0)
+ break;
+ cx->nof_inputs = i;
+ for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
+ if (cx->card->audio_inputs[i].audio_type == 0)
+ break;
+ cx->nof_audio_inputs = i;
+
+ /* Find tuner input */
+ for (i = 0; i < cx->nof_inputs; i++) {
+ if (cx->card->video_inputs[i].video_type ==
+ CX18_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == cx->nof_inputs)
+ i = 0;
+ cx->active_input = i;
+ cx->audio_input = cx->card->video_inputs[i].audio_index;
+}
+
+static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ CX18_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(pci_dev)) {
+ CX18_ERR("Can't enable device %d!\n", cx->instance);
+ return -EIO;
+ }
+ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
+ CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+ return -EIO;
+ }
+ if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+ CX18_ERR("Cannot request encoder memory region, card %d\n",
+ cx->instance);
+ return -EIO;
+ }
+
+ /* Enable bus mastering and memory mapped IO for the CX23418 */
+ pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+
+ cx->card_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && cx18_pci_latency) {
+ CX18_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+
+ CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%llx\n",
+ cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
+ cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
+
+ return 0;
+}
+
+static void cx18_init_subdevs(struct cx18 *cx)
+{
+ u32 hw = cx->card->hw_all;
+ u32 device;
+ int i;
+
+ for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+
+ if (!(device & hw))
+ continue;
+
+ switch (device) {
+ case CX18_HW_DVB:
+ case CX18_HW_TVEEPROM:
+ /* These subordinate devices do not use probing */
+ cx->hw_flags |= device;
+ break;
+ case CX18_HW_418_AV:
+ /* The A/V decoder gets probed earlier to set PLLs */
+ /* Just note that the card uses it (i.e. has analog) */
+ cx->hw_flags |= device;
+ break;
+ case CX18_HW_GPIO_RESET_CTRL:
+ /*
+ * The Reset Controller gets probed and added to
+ * hw_flags earlier for i2c adapter/bus initialization
+ */
+ break;
+ case CX18_HW_GPIO_MUX:
+ if (cx18_gpio_register(cx, device) == 0)
+ cx->hw_flags |= device;
+ break;
+ default:
+ if (cx18_i2c_register(cx, i) == 0)
+ cx->hw_flags |= device;
+ break;
+ }
+ }
+
+ if (cx->hw_flags & CX18_HW_418_AV)
+ cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
+
+ if (cx->card->hw_muxer != 0)
+ cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+}
+
+static int __devinit cx18_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int i;
+ u32 devtype;
+ struct cx18 *cx;
+
+ /* FIXME - module parameter arrays constrain max instances */
+ i = atomic_inc_return(&cx18_instance) - 1;
+ if (i >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
+ "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+ return -ENOMEM;
+ }
+
+ cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+ if (cx == NULL) {
+ printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
+ i);
+ return -ENOMEM;
+ }
+ cx->pci_dev = pci_dev;
+ cx->instance = i;
+
+ retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
+ if (retval) {
+ printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
+ "\n", cx->instance);
+ kfree(cx);
+ return retval;
+ }
+ snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
+ cx->instance);
+ CX18_INFO("Initializing card %d\n", cx->instance);
+
+ cx18_process_options(cx);
+ if (cx->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+
+ retval = cx18_init_struct1(cx);
+ if (retval)
+ goto err;
+
+ CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
+
+ /* PCI Device Setup */
+ retval = cx18_setup_pci(cx, pci_dev, pci_id);
+ if (retval != 0)
+ goto free_workqueues;
+
+ /* map io memory */
+ CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+ (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+ cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
+ CX18_MEM_SIZE);
+ if (!cx->enc_mem) {
+ CX18_ERR("ioremap failed. Can't get a window into CX23418 "
+ "memory and register space\n");
+ CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
+ "vmalloc address space for the window\n");
+ CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ CX18_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
+ devtype = cx18_read_reg(cx, 0xC72028);
+ switch (devtype & 0xff000000) {
+ case 0xff000000:
+ CX18_INFO("cx23418 revision %08x (A)\n", devtype);
+ break;
+ case 0x01000000:
+ CX18_INFO("cx23418 revision %08x (B)\n", devtype);
+ break;
+ default:
+ CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
+ break;
+ }
+
+ cx18_init_power(cx, 1);
+ cx18_init_memory(cx);
+
+ cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
+ cx18_init_scb(cx);
+
+ cx18_gpio_init(cx);
+
+ /* Initialize integrated A/V decoder early to set PLLs, just in case */
+ retval = cx18_av_probe(cx);
+ if (retval) {
+ CX18_ERR("Could not register A/V decoder subdevice\n");
+ goto free_map;
+ }
+
+ /* Initialize GPIO Reset Controller to do chip resets during i2c init */
+ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
+ if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
+ CX18_WARN("Could not register GPIO reset controller"
+ "subdevice; proceeding anyway.\n");
+ else
+ cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
+ }
+
+ /* active i2c */
+ CX18_DEBUG_INFO("activating i2c...\n");
+ retval = init_cx18_i2c(cx);
+ if (retval) {
+ CX18_ERR("Could not initialize i2c\n");
+ goto free_map;
+ }
+
+ if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ const struct cx18_card *orig_card = cx->card;
+ cx18_process_eeprom(cx);
+
+ if (cx->card != orig_card) {
+ /* Changed the cardtype; re-reset the I2C chips */
+ cx18_gpio_init(cx);
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
+ core, reset, (u32) CX18_GPIO_RESET_I2C);
+ }
+ }
+ if (cx->card->comment)
+ CX18_INFO("%s", cx->card->comment);
+ if (cx->card->v4l2_capabilities == 0) {
+ retval = -ENODEV;
+ goto free_i2c;
+ }
+ cx18_init_memory(cx);
+ cx18_init_scb(cx);
+
+ /* Register IRQ */
+ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED,
+ cx->v4l2_dev.name, (void *)cx);
+ if (retval) {
+ CX18_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+ }
+
+ if (cx->std == 0)
+ cx->std = V4L2_STD_NTSC_M;
+
+ if (cx->options.tuner == -1) {
+ for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
+ if ((cx->std & cx->card->tuners[i].std) == 0)
+ continue;
+ cx->options.tuner = cx->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
+ cx->std = cx->card->tuners[0].std;
+ if (cx->std & V4L2_STD_PAL)
+ cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ else if (cx->std & V4L2_STD_NTSC)
+ cx->std = V4L2_STD_NTSC_M;
+ else if (cx->std & V4L2_STD_SECAM)
+ cx->std = V4L2_STD_SECAM_L;
+ cx->options.tuner = cx->card->tuners[0].tuner;
+ }
+ if (cx->options.radio == -1)
+ cx->options.radio = (cx->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ cx18_init_struct2(cx);
+
+ cx18_init_subdevs(cx);
+
+ if (cx->std & V4L2_STD_525_60)
+ cx->is_60hz = 1;
+ else
+ cx->is_50hz = 1;
+
+ cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
+
+ if (cx->options.radio > 0)
+ cx->v4l2_cap |= V4L2_CAP_RADIO;
+
+ if (cx->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = cx->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (cx->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ cx18_reset_tuner_gpio : NULL;
+ cx18_call_all(cx, tuner, s_type_addr, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = cx->options.tuner,
+ .priv = &ctrl,
+ };
+ cx18_call_all(cx, tuner, s_config, &cfg);
+ }
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ cx->tuner_std = cx->std;
+ if (cx->std == V4L2_STD_ALL)
+ cx->std = V4L2_STD_NTSC_M;
+
+ retval = cx18_streams_setup(cx);
+ if (retval) {
+ CX18_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
+ }
+ retval = cx18_streams_register(cx);
+ if (retval) {
+ CX18_ERR("Error %d registering devices\n", retval);
+ goto free_streams;
+ }
+
+ CX18_INFO("Initialized card: %s\n", cx->card_name);
+
+ /* Load cx18 submodules (cx18-alsa) */
+ request_modules(cx);
+ return 0;
+
+free_streams:
+ cx18_streams_cleanup(cx, 1);
+free_irq:
+ free_irq(cx->pci_dev->irq, (void *)cx);
+free_i2c:
+ exit_cx18_i2c(cx);
+free_map:
+ cx18_iounmap(cx);
+free_mem:
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+free_workqueues:
+ destroy_workqueue(cx->in_work_queue);
+err:
+ if (retval == 0)
+ retval = -ENODEV;
+ CX18_ERR("Error %d on initialization\n", retval);
+
+ v4l2_device_unregister(&cx->v4l2_dev);
+ kfree(cx);
+ return retval;
+}
+
+int cx18_init_on_first_open(struct cx18 *cx)
+{
+ int video_input;
+ int fw_retry_count = 3;
+ struct v4l2_frequency vf;
+ struct cx18_open_id fh;
+ v4l2_std_id std;
+
+ fh.cx = cx;
+
+ if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
+ return -ENXIO;
+
+ if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
+ return 0;
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+ set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+ /*
+ * Init the firmware twice to work around a silicon bug
+ * with the digital TS.
+ *
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ fw_retry_count = 3;
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (cx18_firmware_init(cx) == 0)
+ break;
+ if (fw_retry_count > 1)
+ CX18_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(CX18_F_I_FAILED, &cx->i_flags);
+ return -ENXIO;
+ }
+
+ /*
+ * The second firmware load requires us to normalize the APU state,
+ * or the audio for the first analog capture will be badly incorrect.
+ *
+ * I can't seem to call APU_RESETAI and have it succeed without the
+ * APU capturing audio, so we start and stop it here to do the reset
+ */
+
+ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
+ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
+ cx18_vapi(cx, CX18_APU_RESETAI, 0);
+ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ /* Init the A/V decoder, if it hasn't been already */
+ v4l2_subdev_call(cx->sd_av, core, load_fw);
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+ /* Set initial frequency. For PAL/SECAM broadcasts no
+ 'default' channel exists AFAIK. */
+ if (cx->std == V4L2_STD_NTSC_M_JP)
+ vf.frequency = 1460; /* ch. 1 91250*16/1000 */
+ else if (cx->std & V4L2_STD_NTSC_M)
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+
+ video_input = cx->active_input;
+ cx->active_input++; /* Force update of input */
+ cx18_s_input(NULL, &fh, video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ cx->std++; /* Force full standard initialization */
+ std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
+ cx18_s_std(NULL, &fh, &std);
+ cx18_s_frequency(NULL, &fh, &vf);
+ return 0;
+}
+
+static void cx18_cancel_in_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
+ cancel_work_sync(&cx->in_work_order[i].work);
+}
+
+static void cx18_cancel_out_work_orders(struct cx18 *cx)
+{
+ int i;
+ for (i = 0; i < CX18_MAX_STREAMS; i++)
+ if (&cx->streams[i].video_dev != NULL)
+ cancel_work_sync(&cx->streams[i].out_work_order);
+}
+
+static void cx18_remove(struct pci_dev *pci_dev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
+ struct cx18 *cx = to_cx18(v4l2_dev);
+ int i;
+
+ CX18_DEBUG_INFO("Removing Card\n");
+
+ flush_request_modules(cx);
+
+ /* Stop all captures */
+ CX18_DEBUG_INFO("Stopping all streams\n");
+ if (atomic_read(&cx->tot_capturing) > 0)
+ cx18_stop_all_captures(cx);
+
+ /* Stop interrupts that cause incoming work to be queued */
+ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+
+ /* Incoming work can cause outgoing work, so clean up incoming first */
+ cx18_cancel_in_work_orders(cx);
+ cx18_cancel_out_work_orders(cx);
+
+ /* Stop ack interrupts that may have been needed for work to finish */
+ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ cx18_halt_firmware(cx);
+
+ destroy_workqueue(cx->in_work_queue);
+
+ cx18_streams_cleanup(cx, 1);
+
+ exit_cx18_i2c(cx);
+
+ free_irq(cx->pci_dev->irq, (void *)cx);
+
+ cx18_iounmap(cx);
+
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+ pci_disable_device(cx->pci_dev);
+
+ if (cx->vbi.sliced_mpeg_data[0] != NULL)
+ for (i = 0; i < CX18_VBI_FRAMES; i++)
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+
+ v4l2_ctrl_handler_free(&cx->av_state.hdl);
+
+ CX18_INFO("Removed %s\n", cx->card_name);
+
+ v4l2_device_unregister(v4l2_dev);
+ kfree(cx);
+}
+
+
+/* define a pci_driver for card detection */
+static struct pci_driver cx18_pci_driver = {
+ .name = "cx18",
+ .id_table = cx18_pci_tbl,
+ .probe = cx18_probe,
+ .remove = cx18_remove,
+};
+
+static int __init module_start(void)
+{
+ printk(KERN_INFO "cx18: Start initialization, version %s\n",
+ CX18_VERSION);
+
+ /* Validate parameters */
+ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
+ CX18_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (cx18_debug < 0 || cx18_debug > 511) {
+ cx18_debug = 0;
+ printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
+ }
+
+ if (pci_register_driver(&cx18_pci_driver)) {
+ printk(KERN_ERR "cx18: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "cx18: End initialization\n");
+ return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+ pci_unregister_driver(&cx18_pci_driver);
+}
+
+module_init(module_start);
+module_exit(module_cleanup);
+MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index 2767c64df0c8..2767c64df0c8 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c
new file mode 100644
index 000000000000..3eac59c51231
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-dvb.c
@@ -0,0 +1,609 @@
+/*
+ * cx18 functions for DVB support
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "cx18-version.h"
+#include "cx18-dvb.h"
+#include "cx18-io.h"
+#include "cx18-queue.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-gpio.h"
+#include "s5h1409.h"
+#include "mxl5005s.h"
+#include "s5h1411.h"
+#include "tda18271.h"
+#include "zl10353.h"
+
+#include <linux/firmware.h>
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "tuner-xc2028.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define FWFILE "dvb-cx18-mpc718-mt352.fw"
+
+#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
+#define CX18_CLOCK_ENABLE2 0xc71024
+#define CX18_DMUX_CLK_MASK 0x0080
+
+/*
+ * CX18_CARD_HVR_1600_ESMT
+ * CX18_CARD_HVR_1600_SAMSUNG
+ */
+
+static struct mxl5005s_config hauppauge_hvr1600_tuner = {
+ .i2c_address = 0xC6 >> 1,
+ .if_freq = IF_FREQ_5380000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .qam_gain = 0x02,
+ .AgcMasterByte = 0x00,
+};
+
+static struct s5h1409_config hauppauge_hvr1600_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE
+};
+
+/*
+ * CX18_CARD_HVR_1600_S5H1411
+ */
+static struct s5h1411_config hcw_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+/*
+ * CX18_CARD_LEADTEK_DVR3100H
+ */
+/* Information/confirmation of proper config values provided by Terry Wu */
+static struct zl10353_config leadtek_dvr3100h_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+/*
+ * CX18_CARD_YUAN_MPC718
+ */
+/*
+ * Due to
+ *
+ * 1. an absence of information on how to prgram the MT352
+ * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
+ *
+ * We have to use an init sequence that *you* must extract from the Windows
+ * driver (yuanrap.sys) and which we load as a firmware.
+ *
+ * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual
+ * with chip programming details, then I can remove this annoyance.
+ */
+static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
+ const struct firmware **fw)
+{
+ struct cx18 *cx = stream->cx;
+ const char *fn = FWFILE;
+ int ret;
+
+ ret = request_firmware(fw, fn, &cx->pci_dev->dev);
+ if (ret)
+ CX18_ERR("Unable to open firmware file %s\n", fn);
+ else {
+ size_t sz = (*fw)->size;
+ if (sz < 2 || sz > 64 || (sz % 2) != 0) {
+ CX18_ERR("Firmware %s has a bad size: %lu bytes\n",
+ fn, (unsigned long) sz);
+ ret = -EILSEQ;
+ release_firmware(*fw);
+ *fw = NULL;
+ }
+ }
+
+ if (ret) {
+ CX18_ERR("The MPC718 board variant with the MT352 DVB-T"
+ "demodualtor will not work without it\n");
+ CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware "
+ "mpc718' if you need the firmware\n");
+ }
+ return ret;
+}
+
+static int yuan_mpc718_mt352_init(struct dvb_frontend *fe)
+{
+ struct cx18_dvb *dvb = container_of(fe->dvb,
+ struct cx18_dvb, dvb_adapter);
+ struct cx18_stream *stream = dvb->stream;
+ const struct firmware *fw = NULL;
+ int ret;
+ int i;
+ u8 buf[3];
+
+ ret = yuan_mpc718_mt352_reqfw(stream, &fw);
+ if (ret)
+ return ret;
+
+ /* Loop through all the register-value pairs in the firmware file */
+ for (i = 0; i < fw->size; i += 2) {
+ buf[0] = fw->data[i];
+ /* Intercept a few registers we want to set ourselves */
+ switch (buf[0]) {
+ case TRL_NOMINAL_RATE_0:
+ /* Set our custom OFDM bandwidth in the case below */
+ break;
+ case TRL_NOMINAL_RATE_1:
+ /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */
+ /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */
+ /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */
+ buf[1] = 0x72;
+ buf[2] = 0x49;
+ mt352_write(fe, buf, 3);
+ break;
+ case INPUT_FREQ_0:
+ /* Set our custom IF in the case below */
+ break;
+ case INPUT_FREQ_1:
+ /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */
+ buf[1] = 0x31;
+ buf[2] = 0xc0;
+ mt352_write(fe, buf, 3);
+ break;
+ default:
+ /* Pass through the register-value pair from the fw */
+ buf[1] = fw->data[i+1];
+ mt352_write(fe, buf, 2);
+ break;
+ }
+ }
+
+ buf[0] = (u8) TUNER_GO;
+ buf[1] = 0x01; /* Go */
+ mt352_write(fe, buf, 2);
+ release_firmware(fw);
+ return 0;
+}
+
+static struct mt352_config yuan_mpc718_mt352_demod = {
+ .demod_address = 0x1e >> 1,
+ .adc_clock = 20480, /* 20.480 MHz */
+ .if2 = 4560, /* 4.560 MHz */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .demod_init = yuan_mpc718_mt352_init,
+};
+
+static struct zl10353_config yuan_mpc718_zl10353_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+static struct zl10353_config gotview_dvd3_zl10353_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
+static int dvb_register(struct cx18_stream *stream);
+
+/* Kernel DVB framework calls this when the feed needs to start.
+ * The CX18 framework should enable the transport DMA handling
+ * and queue processing.
+ */
+static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
+ struct cx18 *cx;
+ int ret;
+ u32 v;
+
+ if (!stream)
+ return -EINVAL;
+
+ cx = stream->cx;
+ CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
+ mutex_lock(&cx->serialize_lock);
+ ret = cx18_init_on_first_open(cx);
+ mutex_unlock(&cx->serialize_lock);
+ if (ret) {
+ CX18_ERR("Failed to initialize firmware starting DVB feed\n");
+ return ret;
+ }
+ ret = -EINVAL;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ case CX18_CARD_HVR_1600_S5H1411:
+ v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ v |= 0x00400000; /* Serial Mode */
+ v |= 0x00002000; /* Data Length - Byte */
+ v |= 0x00010000; /* Error - Polarity */
+ v |= 0x00020000; /* Error - Passthru */
+ v |= 0x000c0000; /* Error - Ignore */
+ cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
+ break;
+
+ case CX18_CARD_LEADTEK_DVR3100H:
+ case CX18_CARD_YUAN_MPC718:
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ default:
+ /* Assumption - Parallel transport - Signalling
+ * undefined or default.
+ */
+ break;
+ }
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&stream->dvb->feedlock);
+ if (stream->dvb->feeding++ == 0) {
+ CX18_DEBUG_INFO("Starting Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
+ set_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ ret = cx18_start_v4l2_encode_stream(stream);
+ if (ret < 0) {
+ CX18_DEBUG_INFO("Failed to start Transport DMA\n");
+ stream->dvb->feeding--;
+ if (stream->dvb->feeding == 0)
+ clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
+ }
+ mutex_unlock(&cx->serialize_lock);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb->feedlock);
+
+ return ret;
+}
+
+/* Kernel DVB framework calls this when the feed needs to stop. */
+static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
+ struct cx18 *cx;
+ int ret = -EINVAL;
+
+ if (stream) {
+ cx = stream->cx;
+ CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
+ feed->pid, feed->index);
+
+ mutex_lock(&stream->dvb->feedlock);
+ if (--stream->dvb->feeding == 0) {
+ CX18_DEBUG_INFO("Stopping Transport DMA\n");
+ mutex_lock(&cx->serialize_lock);
+ ret = cx18_stop_v4l2_encode_stream(stream, 0);
+ mutex_unlock(&cx->serialize_lock);
+ } else
+ ret = 0;
+ mutex_unlock(&stream->dvb->feedlock);
+ }
+
+ return ret;
+}
+
+int cx18_dvb_register(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+ int ret;
+
+ if (!dvb)
+ return -EINVAL;
+
+ dvb->enabled = 0;
+ dvb->stream = stream;
+
+ ret = dvb_register_adapter(&dvb->dvb_adapter,
+ CX18_DRIVER_NAME,
+ THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_out;
+
+ dvb_adapter = &dvb->dvb_adapter;
+
+ dvbdemux = &dvb->demux;
+
+ dvbdemux->priv = (void *)stream;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = cx18_dvb_start_feed;
+ dvbdemux->stop_feed = cx18_dvb_stop_feed;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
+ ret = dvb_dmx_init(dvbdemux);
+ if (ret < 0)
+ goto err_dvb_unregister_adapter;
+
+ dmx = &dvbdemux->dmx;
+
+ dvb->hw_frontend.source = DMX_FRONTEND_0;
+ dvb->mem_frontend.source = DMX_MEMORY_FE;
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = dmx;
+
+ ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
+ if (ret < 0)
+ goto err_dvb_dmx_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_dvb_dmxdev_release;
+
+ ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
+ if (ret < 0)
+ goto err_remove_hw_frontend;
+
+ ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
+ if (ret < 0)
+ goto err_remove_mem_frontend;
+
+ ret = dvb_register(stream);
+ if (ret < 0)
+ goto err_disconnect_frontend;
+
+ dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
+
+ CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
+ stream->dvb->dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024,
+ (stream->buf_size * 100 / 1024) % 100);
+
+ mutex_init(&dvb->feedlock);
+ dvb->enabled = 1;
+ return ret;
+
+err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+err_remove_mem_frontend:
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+err_remove_hw_frontend:
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+err_dvb_dmxdev_release:
+ dvb_dmxdev_release(&dvb->dmxdev);
+err_dvb_dmx_release:
+ dvb_dmx_release(dvbdemux);
+err_dvb_unregister_adapter:
+ dvb_unregister_adapter(dvb_adapter);
+err_out:
+ return ret;
+}
+
+void cx18_dvb_unregister(struct cx18_stream *stream)
+{
+ struct cx18 *cx = stream->cx;
+ struct cx18_dvb *dvb = stream->dvb;
+ struct dvb_adapter *dvb_adapter;
+ struct dvb_demux *dvbdemux;
+ struct dmx_demux *dmx;
+
+ CX18_INFO("unregister DVB\n");
+
+ if (dvb == NULL || !dvb->enabled)
+ return;
+
+ dvb_adapter = &dvb->dvb_adapter;
+ dvbdemux = &dvb->demux;
+ dmx = &dvbdemux->dmx;
+
+ dmx->close(dmx);
+ dvb_net_release(&dvb->dvbnet);
+ dmx->remove_frontend(dmx, &dvb->mem_frontend);
+ dmx->remove_frontend(dmx, &dvb->hw_frontend);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(dvbdemux);
+ dvb_unregister_frontend(dvb->fe);
+ dvb_frontend_detach(dvb->fe);
+ dvb_unregister_adapter(dvb_adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. cx18_dvb_start_feed() will also need changes.
+ */
+static int dvb_register(struct cx18_stream *stream)
+{
+ struct cx18_dvb *dvb = stream->dvb;
+ struct cx18 *cx = stream->cx;
+ int ret = 0;
+
+ switch (cx->card->type) {
+ case CX18_CARD_HVR_1600_ESMT:
+ case CX18_CARD_HVR_1600_SAMSUNG:
+ dvb->fe = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1600_config,
+ &cx->i2c_adap[0]);
+ if (dvb->fe != NULL) {
+ dvb_attach(mxl5005s_attach, dvb->fe,
+ &cx->i2c_adap[0],
+ &hauppauge_hvr1600_tuner);
+ ret = 0;
+ }
+ break;
+ case CX18_CARD_HVR_1600_S5H1411:
+ dvb->fe = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &cx->i2c_adap[0]);
+ if (dvb->fe != NULL)
+ dvb_attach(tda18271_attach, dvb->fe,
+ 0x60, &cx->i2c_adap[0],
+ &hauppauge_tda18271_config);
+ break;
+ case CX18_CARD_LEADTEK_DVR3100H:
+ dvb->fe = dvb_attach(zl10353_attach,
+ &leadtek_dvr3100h_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ case CX18_CARD_YUAN_MPC718:
+ /*
+ * TODO
+ * Apparently, these cards also could instead have a
+ * DiBcom demod supported by one of the db7000 drivers
+ */
+ dvb->fe = dvb_attach(mt352_attach,
+ &yuan_mpc718_mt352_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe == NULL)
+ dvb->fe = dvb_attach(zl10353_attach,
+ &yuan_mpc718_zl10353_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ case CX18_CARD_GOTVIEW_PCI_DVD3:
+ dvb->fe = dvb_attach(zl10353_attach,
+ &gotview_dvd3_zl10353_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
+ default:
+ /* No Digital Tv Support */
+ break;
+ }
+
+ if (dvb->fe == NULL) {
+ CX18_ERR("frontend initialization failed\n");
+ return -1;
+ }
+
+ dvb->fe->callback = cx18_reset_tuner_gpio;
+
+ ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
+ if (ret < 0) {
+ if (dvb->fe->ops.release)
+ dvb->fe->ops.release(dvb->fe);
+ return ret;
+ }
+
+ /*
+ * The firmware seems to enable the TS DMUX clock
+ * under various circumstances. However, since we know we
+ * might use it, let's just turn it on ourselves here.
+ */
+ cx18_write_reg_expect(cx,
+ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK,
+ CX18_CLOCK_ENABLE2,
+ CX18_DMUX_CLK_MASK,
+ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK);
+
+ return ret;
+}
+
+MODULE_FIRMWARE(FWFILE);
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/pci/cx18/cx18-dvb.h
index bf8d8f6f5455..bf8d8f6f5455 100644
--- a/drivers/media/video/cx18/cx18-dvb.h
+++ b/drivers/media/pci/cx18/cx18-dvb.h
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
index 4bfd865a4106..4bfd865a4106 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/pci/cx18/cx18-fileops.c
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/pci/cx18/cx18-fileops.h
index b9e5110ad043..b9e5110ad043 100644
--- a/drivers/media/video/cx18/cx18-fileops.h
+++ b/drivers/media/pci/cx18/cx18-fileops.h
diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c
new file mode 100644
index 000000000000..a1c1cec05f98
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-firmware.c
@@ -0,0 +1,459 @@
+/*
+ * cx18 firmware functions
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-scb.h"
+#include "cx18-irq.h"
+#include "cx18-firmware.h"
+#include "cx18-cards.h"
+#include <linux/firmware.h>
+
+#define CX18_PROC_SOFT_RESET 0xc70010
+#define CX18_DDR_SOFT_RESET 0xc70014
+#define CX18_CLOCK_SELECT1 0xc71000
+#define CX18_CLOCK_SELECT2 0xc71004
+#define CX18_HALF_CLOCK_SELECT1 0xc71008
+#define CX18_HALF_CLOCK_SELECT2 0xc7100C
+#define CX18_CLOCK_POLARITY1 0xc71010
+#define CX18_CLOCK_POLARITY2 0xc71014
+#define CX18_ADD_DELAY_ENABLE1 0xc71018
+#define CX18_ADD_DELAY_ENABLE2 0xc7101C
+#define CX18_CLOCK_ENABLE1 0xc71020
+#define CX18_CLOCK_ENABLE2 0xc71024
+
+#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
+
+#define CX18_FAST_CLOCK_PLL_INT 0xc78000
+#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004
+#define CX18_FAST_CLOCK_PLL_POST 0xc78008
+#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C
+#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
+
+#define CX18_SLOW_CLOCK_PLL_INT 0xc78014
+#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018
+#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C
+#define CX18_MPEG_CLOCK_PLL_INT 0xc78040
+#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044
+#define CX18_MPEG_CLOCK_PLL_POST 0xc78048
+#define CX18_PLL_POWER_DOWN 0xc78088
+#define CX18_SW1_INT_STATUS 0xc73104
+#define CX18_SW1_INT_ENABLE_PCI 0xc7311C
+#define CX18_SW2_INT_SET 0xc73140
+#define CX18_SW2_INT_STATUS 0xc73144
+#define CX18_ADEC_CONTROL 0xc78120
+
+#define CX18_DDR_REQUEST_ENABLE 0xc80000
+#define CX18_DDR_CHIP_CONFIG 0xc80004
+#define CX18_DDR_REFRESH 0xc80008
+#define CX18_DDR_TIMING1 0xc8000C
+#define CX18_DDR_TIMING2 0xc80010
+#define CX18_DDR_POWER_REG 0xc8001C
+
+#define CX18_DDR_TUNE_LANE 0xc80048
+#define CX18_DDR_INITIAL_EMRS 0xc80054
+#define CX18_DDR_MB_PER_ROW_7 0xc8009C
+#define CX18_DDR_BASE_63_ADDR 0xc804FC
+
+#define CX18_WMB_CLIENT02 0xc90108
+#define CX18_WMB_CLIENT05 0xc90114
+#define CX18_WMB_CLIENT06 0xc90118
+#define CX18_WMB_CLIENT07 0xc9011C
+#define CX18_WMB_CLIENT08 0xc90120
+#define CX18_WMB_CLIENT09 0xc90124
+#define CX18_WMB_CLIENT10 0xc90128
+#define CX18_WMB_CLIENT11 0xc9012C
+#define CX18_WMB_CLIENT12 0xc90130
+#define CX18_WMB_CLIENT13 0xc90134
+#define CX18_WMB_CLIENT14 0xc90138
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
+#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
+
+struct cx18_apu_rom_seghdr {
+ u32 sync1;
+ u32 sync2;
+ u32 addr;
+ u32 size;
+};
+
+static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
+{
+ const struct firmware *fw = NULL;
+ int i, j;
+ unsigned size;
+ u32 __iomem *dst = (u32 __iomem *)mem;
+ const u32 *src;
+
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("Unable to open firmware %s\n", fn);
+ CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+ }
+
+ src = (const u32 *)fw->data;
+
+ for (i = 0; i < fw->size; i += 4096) {
+ cx18_setup_page(cx, i);
+ for (j = i; j < fw->size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ cx18_raw_writel(cx, *src, dst);
+ if (cx18_raw_readl(cx, dst) != *src) {
+ CX18_ERR("Mismatch at offset %x\n", i);
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return -EIO;
+ }
+ dst++;
+ src++;
+ }
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ size = fw->size;
+ release_firmware(fw);
+ cx18_setup_page(cx, SCB_OFFSET);
+ return size;
+}
+
+static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 *entry_addr)
+{
+ const struct firmware *fw = NULL;
+ int i, j;
+ unsigned size;
+ const u32 *src;
+ struct cx18_apu_rom_seghdr seghdr;
+ const u8 *vers;
+ u32 offset = 0;
+ u32 apu_version = 0;
+ int sz;
+
+ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("unable to open firmware %s\n", fn);
+ CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
+ return -ENOMEM;
+ }
+
+ *entry_addr = 0;
+ src = (const u32 *)fw->data;
+ vers = fw->data + sizeof(seghdr);
+ sz = fw->size;
+
+ apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
+ while (offset + sizeof(seghdr) < fw->size) {
+ const u32 *shptr = src + offset / 4;
+
+ seghdr.sync1 = le32_to_cpu(shptr[0]);
+ seghdr.sync2 = le32_to_cpu(shptr[1]);
+ seghdr.addr = le32_to_cpu(shptr[2]);
+ seghdr.size = le32_to_cpu(shptr[3]);
+
+ offset += sizeof(seghdr);
+ if (seghdr.sync1 != APU_ROM_SYNC1 ||
+ seghdr.sync2 != APU_ROM_SYNC2) {
+ offset += seghdr.size;
+ continue;
+ }
+ CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
+ seghdr.addr + seghdr.size - 1);
+ if (*entry_addr == 0)
+ *entry_addr = seghdr.addr;
+ if (offset + seghdr.size > sz)
+ break;
+ for (i = 0; i < seghdr.size; i += 4096) {
+ cx18_setup_page(cx, seghdr.addr + i);
+ for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
+ /* no need for endianness conversion on the ppc */
+ cx18_raw_writel(cx, src[(offset + j) / 4],
+ dst + seghdr.addr + j);
+ if (cx18_raw_readl(cx, dst + seghdr.addr + j)
+ != src[(offset + j) / 4]) {
+ CX18_ERR("Mismatch at offset %x\n",
+ offset + j);
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return -EIO;
+ }
+ }
+ }
+ offset += seghdr.size;
+ }
+ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
+ CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
+ fn, apu_version, fw->size);
+ size = fw->size;
+ release_firmware(fw);
+ cx18_setup_page(cx, 0);
+ return size;
+}
+
+void cx18_halt_firmware(struct cx18 *cx)
+{
+ CX18_DEBUG_INFO("Preparing for firmware halt.\n");
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
+ 0x00000002, 0x00020002);
+}
+
+void cx18_init_power(struct cx18 *cx, int lowpwr)
+{
+ /* power-down Spare and AOM PLLs */
+ /* power-up fast, slow and mpeg PLLs */
+ cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
+
+ /* ADEC out of sleep */
+ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
+ 0x00000000, 0x00020002);
+
+ /*
+ * The PLL parameters are based on the external crystal frequency that
+ * would ideally be:
+ *
+ * NTSC Color subcarrier freq * 8 =
+ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
+ *
+ * The accidents of history and rationale that explain from where this
+ * combination of magic numbers originate can be found in:
+ *
+ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
+ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
+ *
+ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
+ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
+ *
+ * As Mike Bradley has rightly pointed out, it's not the exact crystal
+ * frequency that matters, only that all parts of the driver and
+ * firmware are using the same value (close to the ideal value).
+ *
+ * Since I have a strong suspicion that, if the firmware ever assumes a
+ * crystal value at all, it will assume 28.636360 MHz, the crystal
+ * freq used in calculations in this driver will be:
+ *
+ * xtal_freq = 28.636360 MHz
+ *
+ * an error of less than 0.13 ppm which is way, way better than any off
+ * the shelf crystal will have for accuracy anyway.
+ *
+ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
+ *
+ * Many thanks to Jeff Campbell and Mike Bradley for their extensive
+ * investigation, experimentation, testing, and suggested solutions of
+ * of audio/video sync problems with SVideo and CVBS captures.
+ */
+
+ /* the fast clock is at 200/245 MHz */
+ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
+ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
+ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
+ CX18_FAST_CLOCK_PLL_FRAC);
+
+ cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
+ cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
+ cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
+
+ /* set slow clock to 125/120 MHz */
+ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
+ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
+ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
+ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
+ CX18_SLOW_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
+
+ /* mpeg clock pll 54MHz */
+ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
+ cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+
+ /* Defaults */
+ /* APU = SC or SC/2 = 125/62.5 */
+ /* EPU = SC = 125 */
+ /* DDR = FC = 180 */
+ /* ENC = SC = 125 */
+ /* AI1 = SC = 125 */
+ /* VIM2 = disabled */
+ /* PCI = FC/2 = 90 */
+ /* AI2 = disabled */
+ /* DEMUX = disabled */
+ /* AO = SC/2 = 62.5 */
+ /* SER = 54MHz */
+ /* VFC = disabled */
+ /* USB = disabled */
+
+ if (lowpwr) {
+ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
+ 0x00000020, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
+ 0x00000004, 0xFFFFFFFF);
+ } else {
+ /* This doesn't explicitly set every clock select */
+ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
+ 0x00000004, 0x00060006);
+ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
+ 0x00000006, 0x00060006);
+ }
+
+ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
+ 0x00000002, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
+ 0x00000104, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
+ 0x00009026, 0xFFFFFFFF);
+ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
+ 0x00003105, 0xFFFFFFFF);
+}
+
+void cx18_init_memory(struct cx18 *cx)
+{
+ cx18_msleep_timeout(10, 0);
+ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00010001);
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
+
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
+ cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
+ cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
+
+ cx18_msleep_timeout(10, 0);
+
+ /* Initialize DQS pad time */
+ cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
+ cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
+
+ cx18_msleep_timeout(10, 0);
+
+ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
+ 0x00000000, 0x00020002);
+ cx18_msleep_timeout(10, 0);
+
+ /* use power-down mode when idle */
+ cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
+
+ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
+ 0x00000001, 0x00010001);
+
+ cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
+ cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
+
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */
+ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */
+}
+
+#define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw"
+#define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw"
+
+int cx18_firmware_init(struct cx18 *cx)
+{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
+ /* Allow chip to control CLKRUN */
+ cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
+
+ /* Stop the firmware */
+ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
+ 0x0000000F, 0x000F000F);
+
+ cx18_msleep_timeout(1, 0);
+
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
+ cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
+ cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
+ }
+
+ /*
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
+ */
+ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
+ /* initialize GPIO */
+ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
+ return 0;
+}
+
+MODULE_FIRMWARE(CX18_CPU_FIRMWARE);
+MODULE_FIRMWARE(CX18_APU_FIRMWARE);
diff --git a/drivers/media/video/cx18/cx18-firmware.h b/drivers/media/pci/cx18/cx18-firmware.h
index 38d4c05e8499..38d4c05e8499 100644
--- a/drivers/media/video/cx18/cx18-firmware.h
+++ b/drivers/media/pci/cx18/cx18-firmware.h
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c
index 5374aeb0cd22..5374aeb0cd22 100644
--- a/drivers/media/video/cx18/cx18-gpio.c
+++ b/drivers/media/pci/cx18/cx18-gpio.c
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/pci/cx18/cx18-gpio.h
index 4aea2ef88e8d..4aea2ef88e8d 100644
--- a/drivers/media/video/cx18/cx18-gpio.h
+++ b/drivers/media/pci/cx18/cx18-gpio.h
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c
index 51609d5c88ce..51609d5c88ce 100644
--- a/drivers/media/video/cx18/cx18-i2c.c
+++ b/drivers/media/pci/cx18/cx18-i2c.c
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/pci/cx18/cx18-i2c.h
index 1180fdc8d983..1180fdc8d983 100644
--- a/drivers/media/video/cx18/cx18-i2c.h
+++ b/drivers/media/pci/cx18/cx18-i2c.h
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/pci/cx18/cx18-io.c
index 49b9dbd06248..49b9dbd06248 100644
--- a/drivers/media/video/cx18/cx18-io.c
+++ b/drivers/media/pci/cx18/cx18-io.c
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/pci/cx18/cx18-io.h
index 18974d886cf7..18974d886cf7 100644
--- a/drivers/media/video/cx18/cx18-io.h
+++ b/drivers/media/pci/cx18/cx18-io.h
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
new file mode 100644
index 000000000000..cd8d2c2b1624
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -0,0 +1,1190 @@
+/*
+ * cx18 ioctl system call
+ *
+ * Derived from ivtv-ioctl.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-version.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-fileops.h"
+#include "cx18-vbi.h"
+#include "cx18-audio.h"
+#include "cx18-video.h"
+#include "cx18-streams.h"
+#include "cx18-ioctl.h"
+#include "cx18-gpio.h"
+#include "cx18-controls.h"
+#include "cx18-cards.h"
+#include "cx18-av-core.h"
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+
+u16 cx18_service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return CX18_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return CX18_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return CX18_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return CX18_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+/* Check if VBI services are allowed on the (field, line) for the video std */
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 &&
+ ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+/*
+ * For a (field, line, std) and inbound potential set of services for that line,
+ * return the first valid service of those passed in the incoming set for that
+ * line in priority order:
+ * CC, VPS, or WSS over TELETEXT for well known lines
+ * TELETEXT, before VPS, before CC, before WSS, for other lines
+ */
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal))
+ return 0;
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ } else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+/*
+ * Expand the service_set of *fmt into valid service_lines for the std,
+ * and clear the passed in fmt->service_set
+ */
+void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+}
+
+/*
+ * Sanitize the service_lines in *fmt per the video std, and return 1
+ * if any service_line is left as valid after santization
+ */
+static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set != 0;
+}
+
+/* Compute the service_set from the assumed valid service_lines of *fmt */
+u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++)
+ set |= fmt->service_lines[f][l];
+ }
+ return set;
+}
+
+static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ pixfmt->width = cx->cxhdl.width;
+ pixfmt->height = cx->cxhdl.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = s->pixelformat;
+ pixfmt->sizeimage = s->vb_bytes_per_frame;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+ vbifmt->sampling_rate = 27000000;
+ vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
+ vbifmt->samples_per_line = vbi_active_samples - 4;
+ vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbifmt->start[0] = cx->vbi.start[0];
+ vbifmt->start[1] = cx->vbi.start[1];
+ vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
+ vbifmt->flags = 0;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ return 0;
+}
+
+static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ /* sane, V4L2 spec compliant, defaults */
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+ vbifmt->service_set = 0;
+
+ /*
+ * Fetch the configured service_lines and total service_set from the
+ * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in
+ * fmt->fmt.sliced under valid calling conditions
+ */
+ if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
+ return -EINVAL;
+
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
+}
+
+static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+ int min_h = 2;
+
+ w = min(w, 720);
+ w = max(w, 2);
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ /* YUV height must be a multiple of 32 */
+ h &= ~0x1f;
+ min_h = 32;
+ }
+ h = min(h, cx->is_50hz ? 576 : 480);
+ h = max(h, min_h);
+
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return 0;
+}
+
+static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+
+ /* If given a service set, expand it validly & clear passed in set */
+ if (vbifmt->service_set)
+ cx18_expand_service_set(vbifmt, cx->is_50hz);
+ /* Sanitize the service_lines, and compute the new set if any valid */
+ if (check_service_set(vbifmt, cx->is_50hz))
+ vbifmt->service_set = cx18_get_service_set(vbifmt);
+ return 0;
+}
+
+static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct cx18_stream *s = &cx->streams[id->type];
+ int ret;
+ int w, h;
+
+ ret = cx18_try_fmt_vid_cap(file, fh, fmt);
+ if (ret)
+ return ret;
+ w = fmt->fmt.pix.width;
+ h = fmt->fmt.pix.height;
+
+ if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
+ s->pixelformat == fmt->fmt.pix.pixelformat)
+ return 0;
+
+ if (atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ s->pixelformat = fmt->fmt.pix.pixelformat;
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = h * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = h * 720 * 2;
+
+ mbus_fmt.width = cx->cxhdl.width = w;
+ mbus_fmt.height = cx->cxhdl.height = h;
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
+ return cx18_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int ret;
+
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ /*
+ * Set the digitizer registers for raw active VBI.
+ * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
+ * calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
+ if (ret)
+ return ret;
+
+ /* Store our new v4l2 (non-)sliced VBI state */
+ cx->vbi.sliced_in->service_set = 0;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+
+ return cx18_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ int ret;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ /*
+ * Changing the Encoder's Raw VBI parameters won't have any effect
+ * if any analog capture is ongoing
+ */
+ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+ /*
+ * Set the service_lines requested in the digitizer/slicer registers.
+ * Note, cx18_av_vbi() wipes some "impossible" service lines in the
+ * passed in fmt->fmt.sliced under valid calling conditions
+ */
+ ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
+ if (ret)
+ return ret;
+ /* Store our current v4l2 sliced VBI settings */
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
+ return 0;
+}
+
+static int cx18_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ int err = 0;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (chip->match.addr) {
+ case 0:
+ chip->ident = V4L2_IDENT_CX23418;
+ chip->revision = cx18_read_reg(cx, 0xC72028);
+ break;
+ case 1:
+ /*
+ * The A/V decoder is always present, but in the rare
+ * case that the card doesn't have analog, we don't
+ * use it. We find it w/o using the cx->sd_av pointer
+ */
+ cx18_call_hw(cx, CX18_HW_418_AV,
+ core, g_chip_ident, chip);
+ break;
+ default:
+ /*
+ * Could return ident = V4L2_IDENT_UNKNOWN if we had
+ * other host chips at higher addresses, but we don't
+ */
+ err = -EINVAL; /* per V4L2 spec */
+ break;
+ }
+ break;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /*
+ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
+ * to look if a chip is at the address with no driver. That's a
+ * dangerous thing to do with EEPROMs anyway.
+ */
+ cx18_call_all(cx, core, g_chip_ident, chip);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+{
+ struct v4l2_dbg_register *regs = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+
+ regs->size = 4;
+ if (cmd == VIDIOC_DBG_S_REGISTER)
+ cx18_write_enc(cx, regs->val, regs->reg);
+ else
+ regs->val = cx18_read_enc(cx, regs->reg);
+ return 0;
+}
+
+static int cx18_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, g_register, reg);
+ return 0;
+}
+
+static int cx18_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
+ /* FIXME - errors shouldn't be ignored */
+ cx18_call_all(cx, core, s_register, reg);
+ return 0;
+}
+#endif
+
+static int cx18_querycap(struct file *file, void *fh,
+ struct v4l2_capability *vcap)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+ "PCI:%s", pci_name(cx->pci_dev));
+ vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV)
+ vcap->capabilities |= V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ vin->index = cx->audio_input;
+ return cx18_get_audio_input(cx, vin->index, vin);
+}
+
+static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vout->index >= cx->nof_audio_inputs)
+ return -EINVAL;
+ cx->audio_input = vout->index;
+ cx18_audio_set_io(cx);
+ return 0;
+}
+
+static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ /* set it to defaults from our table */
+ return cx18_get_input(cx, vin->index, vin);
+}
+
+static int cx18_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = cx->is_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
+ return -EINVAL;
+}
+
+static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
+ return -EINVAL;
+}
+
+static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ static const struct v4l2_fmtdesc formats[] = {
+ { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
+ },
+ { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
+ },
+ { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 }
+ },
+ };
+
+ if (fmt->index > ARRAY_SIZE(formats) - 1)
+ return -EINVAL;
+ *fmt = formats[fmt->index];
+ return 0;
+}
+
+static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ *i = cx->active_input;
+ return 0;
+}
+
+int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (inp >= cx->nof_inputs)
+ return -EINVAL;
+
+ if (inp == cx->active_input) {
+ CX18_DEBUG_INFO("Input unchanged\n");
+ return 0;
+ }
+
+ CX18_DEBUG_INFO("Changing input from %d to %d\n",
+ cx->active_input, inp);
+
+ cx->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the input type. */
+ cx->audio_input = cx->card->video_inputs[inp].audio_index;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ cx18_mute(cx);
+ cx18_video_set_io(cx);
+ cx18_audio_set_io(cx);
+ cx18_unmute(cx);
+ return 0;
+}
+
+static int cx18_g_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *vf)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, g_frequency, vf);
+ return 0;
+}
+
+int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ cx18_mute(cx);
+ CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+ cx18_call_all(cx, tuner, s_frequency, vf);
+ cx18_unmute(cx);
+ return 0;
+}
+
+static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ *std = cx->std;
+ return 0;
+}
+
+int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if ((*std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (*std == cx->std)
+ return 0;
+
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
+ atomic_read(&cx->ana_capturing) > 0) {
+ /* Switching standard would turn off the radio or mess
+ with already running streams, prevent that by
+ returning EBUSY. */
+ return -EBUSY;
+ }
+
+ cx->std = *std;
+ cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ cx->is_50hz = !cx->is_60hz;
+ cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
+ cx->cxhdl.width = 720;
+ cx->cxhdl.height = cx->is_50hz ? 576 : 480;
+ cx->vbi.count = cx->is_50hz ? 18 : 12;
+ cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+ cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+ CX18_DEBUG_INFO("Switching standard to %llx.\n",
+ (unsigned long long) cx->std);
+
+ /* Tuner */
+ cx18_call_all(cx, core, s_std, cx->std);
+ return 0;
+}
+
+static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, s_tuner, vt);
+ return 0;
+}
+
+static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ cx18_call_all(cx, tuner, g_tuner, vt);
+
+ if (vt->type == V4L2_TUNER_RADIO)
+ strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+ else
+ strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
+ return 0;
+}
+
+static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_sliced_vbi_cap *cap)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+
+ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
+
+ cap->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, cx->is_50hz)) {
+ /*
+ * We can find all v4l2 supported vbi services
+ * for the standard, on a valid line for the std
+ */
+ cap->service_lines[f][l] = set;
+ cap->service_set |= set;
+ } else
+ cap->service_lines[f][l] = 0;
+ }
+ }
+ for (f = 0; f < 3; f++)
+ cap->reserved[f] = 0;
+ return 0;
+}
+
+static int _cx18_process_idx_data(struct cx18_buffer *buf,
+ struct v4l2_enc_idx *idx)
+{
+ int consumed, remaining;
+ struct v4l2_enc_idx_entry *e_idx;
+ struct cx18_enc_idx_entry *e_buf;
+
+ /* Frame type lookup: 1=I, 2=P, 4=B */
+ const int mapping[8] = {
+ -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
+ -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
+ };
+
+ /*
+ * Assumption here is that a buf holds an integral number of
+ * struct cx18_enc_idx_entry objects and is properly aligned.
+ * This is enforced by the module options on IDX buffer sizes.
+ */
+ remaining = buf->bytesused - buf->readpos;
+ consumed = 0;
+ e_idx = &idx->entry[idx->entries];
+ e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
+
+ while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
+ idx->entries < V4L2_ENC_IDX_ENTRIES) {
+
+ e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
+ | le32_to_cpu(e_buf->offset_low);
+
+ e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
+ | le32_to_cpu(e_buf->pts_low);
+
+ e_idx->length = le32_to_cpu(e_buf->length);
+
+ e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
+
+ e_idx->reserved[0] = 0;
+ e_idx->reserved[1] = 0;
+
+ idx->entries++;
+ e_idx = &idx->entry[idx->entries];
+ e_buf++;
+
+ remaining -= sizeof(struct cx18_enc_idx_entry);
+ consumed += sizeof(struct cx18_enc_idx_entry);
+ }
+
+ /* Swallow any partial entries at the end, if there are any */
+ if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
+ consumed += remaining;
+
+ buf->readpos += consumed;
+ return consumed;
+}
+
+static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
+ struct v4l2_enc_idx *idx)
+{
+ if (s->type != CX18_ENC_STREAM_TYPE_IDX)
+ return -EINVAL;
+
+ if (mdl->curr_buf == NULL)
+ mdl->curr_buf = list_first_entry(&mdl->buf_list,
+ struct cx18_buffer, list);
+
+ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ /*
+ * For some reason we've exhausted the buffers, but the MDL
+ * object still said some data was unread.
+ * Fix that and bail out.
+ */
+ mdl->readpos = mdl->bytesused;
+ return 0;
+ }
+
+ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
+
+ /* Skip any empty buffers in the MDL */
+ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
+ continue;
+
+ mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
+
+ /* exit when MDL drained or request satisfied */
+ if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
+ mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
+ mdl->readpos >= mdl->bytesused)
+ break;
+ }
+ return 0;
+}
+
+static int cx18_g_enc_index(struct file *file, void *fh,
+ struct v4l2_enc_idx *idx)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ s32 tmp;
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
+ return -EINVAL;
+
+ /* Compute the best case number of entries we can buffer */
+ tmp = s->buffers -
+ s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
+ if (tmp <= 0)
+ tmp = 1;
+ tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
+
+ /* Fill out the header of the return structure */
+ idx->entries = 0;
+ idx->entries_cap = tmp;
+ memset(idx->reserved, 0, sizeof(idx->reserved));
+
+ /* Pull IDX MDLs and buffers from q_full and populate the entries */
+ do {
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl == NULL) /* No more IDX data right now */
+ break;
+
+ /* Extract the Index entry data from the MDL and buffers */
+ cx18_process_idx_data(s, mdl, idx);
+ if (mdl->readpos < mdl->bytesused) {
+ /* We finished with data remaining, push the MDL back */
+ cx18_push(s, mdl, &s->q_full);
+ break;
+ }
+
+ /* We drained this MDL, schedule it to go to the firmware */
+ cx18_enqueue(s, mdl, &s->q_free);
+
+ } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
+
+ /* Tell the work handler to send free IDX MDLs to the firmware */
+ cx18_stream_load_fw_queue(s);
+ return 0;
+}
+
+static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
+{
+ struct videobuf_queue *q = NULL;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ switch (s->vb_type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &s->vbuf_q;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ break;
+ default:
+ break;
+ }
+ return q;
+}
+
+static int cx18_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ /* Start the hardware only if we're the video device */
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+ return -EINVAL;
+
+ /* Establish a buffer timeout */
+ mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
+
+ return videobuf_streamon(cx18_vb_queue(id));
+}
+
+static int cx18_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ /* Start the hardware only if we're the video device */
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (id->type != CX18_ENC_STREAM_TYPE_YUV)
+ return -EINVAL;
+
+ return videobuf_streamoff(cx18_vb_queue(id));
+}
+
+static int cx18_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_reqbufs(cx18_vb_queue(id), rb);
+}
+
+static int cx18_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_querybuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_qbuf(cx18_vb_queue(id), b);
+}
+
+static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx18_open_id *id = file->private_data;
+ struct cx18 *cx = id->cx;
+ struct cx18_stream *s = &cx->streams[id->type];
+
+ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
+}
+
+static int cx18_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct cx18_open_id *id = fh2id(fh);
+ struct cx18 *cx = id->cx;
+ u32 h;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return cx18_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ cx18_stop_capture(id,
+ enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ break;
+
+ case V4L2_ENC_CMD_PAUSE:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ if (!atomic_read(&cx->ana_capturing))
+ return -EPERM;
+ if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_PAUSE\n");
+ return -EBADFD;
+ }
+ cx18_mute(cx);
+ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ if (!atomic_read(&cx->ana_capturing))
+ return -EPERM;
+ if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
+ return 0;
+ h = cx18_find_handle(cx);
+ if (h == CX18_INVALID_TASK_HANDLE) {
+ CX18_ERR("Can't find valid task handle for "
+ "V4L2_ENC_CMD_RESUME\n");
+ return -EBADFD;
+ }
+ cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
+ cx18_unmute(cx);
+ break;
+
+ default:
+ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_try_encoder_cmd(struct file *file, void *fh,
+ struct v4l2_encoder_cmd *enc)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ break;
+
+ case V4L2_ENC_CMD_STOP:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ break;
+
+ case V4L2_ENC_CMD_PAUSE:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ break;
+
+ default:
+ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cx18_log_status(struct file *file, void *fh)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
+ if (cx->hw_flags & CX18_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+ }
+ cx18_call_all(cx, core, log_status);
+ cx18_get_input(cx, cx->active_input, &vidin);
+ cx18_get_audio_input(cx, cx->audio_input, &audin);
+ CX18_INFO("Video Input: %s\n", vidin.name);
+ CX18_INFO("Audio Input: %s\n", audin.name);
+ mutex_lock(&cx->gpio_lock);
+ CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n",
+ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
+ CX18_INFO("Tuner: %s\n",
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
+ v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
+ CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->video_dev == NULL || s->buffers == 0)
+ continue;
+ CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+ s->name, s->s_flags,
+ atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
+ / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+ CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+ (long long)cx->mpg_data_received,
+ (long long)cx->vbi_data_inserted);
+ return 0;
+}
+
+static long cx18_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct cx18 *cx = fh2id(fh)->cx;
+
+ switch (cmd) {
+ case VIDIOC_INT_RESET: {
+ u32 val = *(u32 *)arg;
+
+ if ((val == 0) || (val & 0x01))
+ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
+ (u32) CX18_GPIO_RESET_Z8F0811);
+ break;
+ }
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
+ .vidioc_querycap = cx18_querycap,
+ .vidioc_s_audio = cx18_s_audio,
+ .vidioc_g_audio = cx18_g_audio,
+ .vidioc_enumaudio = cx18_enumaudio,
+ .vidioc_enum_input = cx18_enum_input,
+ .vidioc_cropcap = cx18_cropcap,
+ .vidioc_s_crop = cx18_s_crop,
+ .vidioc_g_crop = cx18_g_crop,
+ .vidioc_g_input = cx18_g_input,
+ .vidioc_s_input = cx18_s_input,
+ .vidioc_g_frequency = cx18_g_frequency,
+ .vidioc_s_frequency = cx18_s_frequency,
+ .vidioc_s_tuner = cx18_s_tuner,
+ .vidioc_g_tuner = cx18_g_tuner,
+ .vidioc_g_enc_index = cx18_g_enc_index,
+ .vidioc_g_std = cx18_g_std,
+ .vidioc_s_std = cx18_s_std,
+ .vidioc_log_status = cx18_log_status,
+ .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap,
+ .vidioc_encoder_cmd = cx18_encoder_cmd,
+ .vidioc_try_encoder_cmd = cx18_try_encoder_cmd,
+ .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap,
+ .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap,
+ .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap,
+ .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap,
+ .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap,
+ .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap,
+ .vidioc_g_chip_ident = cx18_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx18_g_register,
+ .vidioc_s_register = cx18_s_register,
+#endif
+ .vidioc_default = cx18_default,
+ .vidioc_streamon = cx18_streamon,
+ .vidioc_streamoff = cx18_streamoff,
+ .vidioc_reqbufs = cx18_reqbufs,
+ .vidioc_querybuf = cx18_querybuf,
+ .vidioc_qbuf = cx18_qbuf,
+ .vidioc_dqbuf = cx18_dqbuf,
+};
+
+void cx18_set_funcs(struct video_device *vdev)
+{
+ vdev->ioctl_ops = &cx18_ioctl_ops;
+}
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/pci/cx18/cx18-ioctl.h
index 2f9dd591ee0f..2f9dd591ee0f 100644
--- a/drivers/media/video/cx18/cx18-ioctl.h
+++ b/drivers/media/pci/cx18/cx18-ioctl.h
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/pci/cx18/cx18-irq.c
index 80edfe93a3d8..80edfe93a3d8 100644
--- a/drivers/media/video/cx18/cx18-irq.c
+++ b/drivers/media/pci/cx18/cx18-irq.c
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/pci/cx18/cx18-irq.h
index 30e7eaf8cb55..30e7eaf8cb55 100644
--- a/drivers/media/video/cx18/cx18-irq.h
+++ b/drivers/media/pci/cx18/cx18-irq.h
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c
index eabf00c6351b..eabf00c6351b 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/pci/cx18/cx18-mailbox.c
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/pci/cx18/cx18-mailbox.h
index b63fdfaac49e..b63fdfaac49e 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/pci/cx18/cx18-mailbox.h
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c
index 8884537bd62f..8884537bd62f 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/pci/cx18/cx18-queue.c
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/pci/cx18/cx18-queue.h
index 4201ddc16091..4201ddc16091 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/pci/cx18/cx18-queue.h
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/pci/cx18/cx18-scb.c
index 85cc59637e54..85cc59637e54 100644
--- a/drivers/media/video/cx18/cx18-scb.c
+++ b/drivers/media/pci/cx18/cx18-scb.c
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/pci/cx18/cx18-scb.h
index 08877652e321..08877652e321 100644
--- a/drivers/media/video/cx18/cx18-scb.h
+++ b/drivers/media/pci/cx18/cx18-scb.h
diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c
new file mode 100644
index 000000000000..72af9b5c2d7d
--- /dev/null
+++ b/drivers/media/pci/cx18/cx18-streams.c
@@ -0,0 +1,1059 @@
+/*
+ * cx18 init/start/stop/exit stream functions
+ *
+ * Derived from ivtv-streams.c
+ *
+ * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
+ * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include "cx18-driver.h"
+#include "cx18-io.h"
+#include "cx18-fileops.h"
+#include "cx18-mailbox.h"
+#include "cx18-i2c.h"
+#include "cx18-queue.h"
+#include "cx18-ioctl.h"
+#include "cx18-streams.h"
+#include "cx18-cards.h"
+#include "cx18-scb.h"
+#include "cx18-dvb.h"
+
+#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+
+static struct v4l2_file_operations cx18_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = cx18_v4l2_read,
+ .open = cx18_v4l2_open,
+ .unlocked_ioctl = video_ioctl2,
+ .release = cx18_v4l2_close,
+ .poll = cx18_v4l2_enc_poll,
+ .mmap = cx18_v4l2_mmap,
+};
+
+/* offset from 0 to register ts v4l2 minors on */
+#define CX18_V4L2_ENC_TS_OFFSET 16
+/* offset from 0 to register pcm v4l2 minors on */
+#define CX18_V4L2_ENC_PCM_OFFSET 24
+/* offset from 0 to register yuv v4l2 minors on */
+#define CX18_V4L2_ENC_YUV_OFFSET 32
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int num_offset;
+ int dma;
+} cx18_stream_info[] = {
+ { /* CX18_ENC_STREAM_TYPE_MPG */
+ "encoder MPEG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_TS */
+ "TS",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_PCM */
+ "encoder PCM audio",
+ VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_IDX */
+ "encoder IDX",
+ VFL_TYPE_GRABBER, -1,
+ PCI_DMA_FROMDEVICE,
+ },
+ { /* CX18_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE,
+ },
+};
+
+
+void cx18_dma_free(struct videobuf_queue *q,
+ struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
+{
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int cx18_prepare_buffer(struct videobuf_queue *q,
+ struct cx18_stream *s,
+ struct cx18_videobuf_buffer *buf,
+ u32 pixelformat,
+ unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ struct cx18 *cx = s->cx;
+ int rc = 0;
+
+ /* check settings */
+ buf->bytes_used = 0;
+
+ if ((width < 48) || (height < 32))
+ return -EINVAL;
+
+ buf->vb.size = (width * height * 2);
+ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+ return -EINVAL;
+
+ /* alloc + fill struct (if changed) */
+ if (buf->vb.width != width || buf->vb.height != height ||
+ buf->vb.field != field || s->pixelformat != pixelformat ||
+ buf->tvnorm != cx->std) {
+
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = cx->std;
+ s->pixelformat = pixelformat;
+
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
+ cx18_dma_free(q, s, buf);
+ }
+
+ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
+ return -EINVAL;
+
+ if (buf->vb.field == 0)
+ buf->vb.field = V4L2_FIELD_INTERLACED;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = width;
+ buf->vb.height = height;
+ buf->vb.field = field;
+ buf->tvnorm = cx->std;
+ s->pixelformat = pixelformat;
+
+ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
+ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->vb_bytes_per_frame = height * 720 * 3 / 2;
+ else
+ s->vb_bytes_per_frame = height * 720 * 2;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ cx18_dma_free(q, s, buf);
+ return rc;
+
+}
+
+/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576)
+ 1440 is a single line of 4:2:2 YUV at 720 luma samples wide
+*/
+#define VB_MIN_BUFFERS 32
+#define VB_MIN_BUFSIZE 4147200
+
+static int buffer_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx18_stream *s = q->priv_data;
+ struct cx18 *cx = s->cx;
+
+ *size = 2 * cx->cxhdl.width * cx->cxhdl.height;
+ if (*count == 0)
+ *count = VB_MIN_BUFFERS;
+
+ while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
+ (*count)--;
+
+ q->field = V4L2_FIELD_INTERLACED;
+ q->last = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+ struct cx18 *cx = s->cx;
+
+ return cx18_prepare_buffer(q, s, buf, s->pixelformat,
+ cx->cxhdl.width, cx->cxhdl.height, field);
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+
+ cx18_dma_free(q, s, buf);
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx18_videobuf_buffer *buf =
+ container_of(vb, struct cx18_videobuf_buffer, vb);
+ struct cx18_stream *s = q->priv_data;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+
+ list_add_tail(&buf->vb.queue, &s->vb_capture);
+}
+
+static struct videobuf_queue_ops cx18_videobuf_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static void cx18_stream_init(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ struct video_device *video_dev = s->video_dev;
+
+ /* we need to keep video_dev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->video_dev = video_dev;
+
+ /* initialize cx18_stream fields */
+ s->dvb = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+ s->handle = CX18_INVALID_TASK_HANDLE;
+
+ s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
+ s->buf_size = cx->stream_buf_size[type];
+ INIT_LIST_HEAD(&s->buf_pool);
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
+
+ init_waitqueue_head(&s->waitq);
+ s->id = -1;
+ spin_lock_init(&s->q_free.lock);
+ cx18_queue_init(&s->q_free);
+ spin_lock_init(&s->q_busy.lock);
+ cx18_queue_init(&s->q_busy);
+ spin_lock_init(&s->q_full.lock);
+ cx18_queue_init(&s->q_full);
+ spin_lock_init(&s->q_idle.lock);
+ cx18_queue_init(&s->q_idle);
+
+ INIT_WORK(&s->out_work_order, cx18_out_work_handler);
+
+ INIT_LIST_HEAD(&s->vb_capture);
+ s->vb_timeout.function = cx18_vb_timeout;
+ s->vb_timeout.data = (unsigned long)s;
+ init_timer(&s->vb_timeout);
+ spin_lock_init(&s->vb_lock);
+ if (type == CX18_ENC_STREAM_TYPE_YUV) {
+ spin_lock_init(&s->vbuf_q_lock);
+
+ s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops,
+ &cx->pci_dev->dev, &s->vbuf_q_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx18_videobuf_buffer),
+ s, &cx->serialize_lock);
+
+ /* Assume the previous pixel default */
+ s->pixelformat = V4L2_PIX_FMT_HM12;
+ s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
+ }
+}
+
+static int cx18_prep_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ u32 cap = cx->v4l2_cap;
+ int num_offset = cx18_stream_info[type].num_offset;
+ int num = cx->instance + cx18_first_minor + num_offset;
+
+ /*
+ * These five fields are always initialized.
+ * For analog capture related streams, if video_dev == NULL then the
+ * stream is not in use.
+ * For the TS stream, if dvb == NULL then the stream is not in use.
+ * In those cases no other fields but these four can be used.
+ */
+ s->video_dev = NULL;
+ s->dvb = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
+ return 0;
+
+ /* Check whether VBI is supported */
+ if (type == CX18_ENC_STREAM_TYPE_VBI &&
+ !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
+ return 0;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
+ cx->stream_buffers[type] == 0) {
+ CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
+ return 0;
+ }
+
+ cx18_stream_init(cx, type);
+
+ /* Allocate the cx18_dvb struct only for the TS on cards with DTV */
+ if (type == CX18_ENC_STREAM_TYPE_TS) {
+ if (cx->card->hw_all & CX18_HW_DVB) {
+ s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
+ if (s->dvb == NULL) {
+ CX18_ERR("Couldn't allocate cx18_dvb structure"
+ " for %s\n", s->name);
+ return -ENOMEM;
+ }
+ } else {
+ /* Don't need buffers for the TS, if there is no DVB */
+ s->buffers = 0;
+ }
+ }
+
+ if (num_offset == -1)
+ return 0;
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->video_dev = video_device_alloc();
+ if (s->video_dev == NULL) {
+ CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
+ s->name);
+ return -ENOMEM;
+ }
+
+ snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
+ cx->v4l2_dev.name, s->name);
+
+ s->video_dev->num = num;
+ s->video_dev->v4l2_dev = &cx->v4l2_dev;
+ s->video_dev->fops = &cx18_v4l2_enc_fops;
+ s->video_dev->release = video_device_release;
+ s->video_dev->tvnorms = V4L2_STD_ALL;
+ s->video_dev->lock = &cx->serialize_lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
+ cx18_set_funcs(s->video_dev);
+ return 0;
+}
+
+/* Initialize v4l2 variables and register v4l2 devices */
+int cx18_streams_setup(struct cx18 *cx)
+{
+ int type, ret;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ /* Prepare device */
+ ret = cx18_prep_dev(cx, type);
+ if (ret < 0)
+ break;
+
+ /* Allocate Stream */
+ ret = cx18_stream_alloc(&cx->streams[type]);
+ if (ret < 0)
+ break;
+ }
+ if (type == CX18_MAX_STREAMS)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx, 0);
+ return ret;
+}
+
+static int cx18_reg_dev(struct cx18 *cx, int type)
+{
+ struct cx18_stream *s = &cx->streams[type];
+ int vfl_type = cx18_stream_info[type].vfl_type;
+ const char *name;
+ int num, ret;
+
+ if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
+ ret = cx18_dvb_register(s);
+ if (ret < 0) {
+ CX18_ERR("DVB failed to register\n");
+ return ret;
+ }
+ }
+
+ if (s->video_dev == NULL)
+ return 0;
+
+ num = s->video_dev->num;
+ /* card number + user defined offset + device offset */
+ if (type != CX18_ENC_STREAM_TYPE_MPG) {
+ struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->video_dev)
+ num = s_mpg->video_dev->num
+ + cx18_stream_info[type].num_offset;
+ }
+ video_set_drvdata(s->video_dev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+ ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
+ if (ret < 0) {
+ CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+ s->name, num);
+ video_device_release(s->video_dev);
+ s->video_dev = NULL;
+ return ret;
+ }
+
+ name = video_device_node_name(s->video_dev);
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
+ name, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type] / 1024,
+ (cx->stream_buf_size[type] * 100 / 1024) % 100);
+ break;
+
+ case VFL_TYPE_RADIO:
+ CX18_INFO("Registered device %s for %s\n", name, s->name);
+ break;
+
+ case VFL_TYPE_VBI:
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device %s for %s "
+ "(%d x %d bytes)\n",
+ name, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
+ else
+ CX18_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ }
+
+ return 0;
+}
+
+/* Register v4l2 devices */
+int cx18_streams_register(struct cx18 *cx)
+{
+ int type;
+ int err;
+ int ret = 0;
+
+ /* Register V4L2 devices */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+ err = cx18_reg_dev(cx, type);
+ if (err && ret == 0)
+ ret = err;
+ }
+
+ if (ret == 0)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ cx18_streams_cleanup(cx, 1);
+ return ret;
+}
+
+/* Unregister v4l2 devices */
+void cx18_streams_cleanup(struct cx18 *cx, int unregister)
+{
+ struct video_device *vdev;
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < CX18_MAX_STREAMS; type++) {
+
+ /* The TS has a cx18_dvb structure, not a video_device */
+ if (type == CX18_ENC_STREAM_TYPE_TS) {
+ if (cx->streams[type].dvb != NULL) {
+ if (unregister)
+ cx18_dvb_unregister(&cx->streams[type]);
+ kfree(cx->streams[type].dvb);
+ cx->streams[type].dvb = NULL;
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
+ }
+
+ /* No struct video_device, but can have buffers allocated */
+ if (type == CX18_ENC_STREAM_TYPE_IDX) {
+ /* If the module params didn't inhibit IDX ... */
+ if (cx->stream_buffers[type] != 0) {
+ cx->stream_buffers[type] = 0;
+ /*
+ * Before calling cx18_stream_free(),
+ * check if the IDX stream was actually set up.
+ * Needed, since the cx18_probe() error path
+ * exits through here as well as normal clean up
+ */
+ if (cx->streams[type].buffers != 0)
+ cx18_stream_free(&cx->streams[type]);
+ }
+ continue;
+ }
+
+ /* If struct video_device exists, can have buffers allocated */
+ vdev = cx->streams[type].video_dev;
+
+ cx->streams[type].video_dev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ if (type == CX18_ENC_STREAM_TYPE_YUV)
+ videobuf_mmap_free(&cx->streams[type].vbuf_q);
+
+ cx18_stream_free(&cx->streams[type]);
+
+ /* Unregister or release device */
+ if (unregister)
+ video_unregister_device(vdev);
+ else
+ video_device_release(vdev);
+ }
+}
+
+static void cx18_vbi_setup(struct cx18_stream *s)
+{
+ struct cx18 *cx = s->cx;
+ int raw = cx18_raw_vbi(cx);
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+
+ if (cx->is_60hz) {
+ cx->vbi.count = 12;
+ cx->vbi.start[0] = 10;
+ cx->vbi.start[1] = 273;
+ } else { /* PAL/SECAM */
+ cx->vbi.count = 18;
+ cx->vbi.start[0] = 6;
+ cx->vbi.start[1] = 318;
+ }
+
+ /* setup VBI registers */
+ if (raw)
+ v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
+ else
+ v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
+
+ /*
+ * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
+ * VBI when the first analog capture channel starts, as once it starts
+ * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
+ * (i.e. for the VBI capture channels). We also send it for each
+ * analog capture channel anyway just to make sure we get the proper
+ * behavior
+ */
+ if (raw) {
+ lines = cx->vbi.count * 2;
+ } else {
+ /*
+ * For 525/60 systems, according to the VIP 2 & BT.656 std:
+ * The EAV RP code's Field bit toggles on line 4, a few lines
+ * after the Vertcal Blank bit has already toggled.
+ * Tell the encoder to capture 21-4+1=18 lines per field,
+ * since we want lines 10 through 21.
+ *
+ * For 625/50 systems, according to the VIP 2 & BT.656 std:
+ * The EAV RP code's Field bit toggles on line 1, a few lines
+ * after the Vertcal Blank bit has already toggled.
+ * (We've actually set the digitizer so that the Field bit
+ * toggles on line 2.) Tell the encoder to capture 23-2+1=22
+ * lines per field, since we want lines 6 through 23.
+ */
+ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
+ }
+
+ data[0] = s->handle;
+ /* Lines per field */
+ data[1] = (lines / 2) | ((lines / 2) << 16);
+ /* bytes per line */
+ data[2] = (raw ? vbi_active_samples
+ : (cx->is_60hz ? vbi_hblank_samples_60Hz
+ : vbi_hblank_samples_50Hz));
+ /* Every X number of frames a VBI interrupt arrives
+ (frames as in 25 or 30 fps) */
+ data[3] = 1;
+ /*
+ * Set the SAV/EAV RP codes to look for as start/stop points
+ * when in VIP-1.1 mode
+ */
+ if (raw) {
+ /*
+ * Start codes for beginning of "active" line in vertical blank
+ * 0x20 ( VerticalBlank )
+ * 0x60 ( EvenField VerticalBlank )
+ */
+ data[4] = 0x20602060;
+ /*
+ * End codes for end of "active" raw lines and regular lines
+ * 0x30 ( VerticalBlank HorizontalBlank)
+ * 0x70 ( EvenField VerticalBlank HorizontalBlank)
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ */
+ data[5] = 0x307090d0;
+ } else {
+ /*
+ * End codes for active video, we want data in the hblank region
+ * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
+ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
+ *
+ * Since the V bit is only allowed to toggle in the EAV RP code,
+ * just before the first active region line, these two
+ * are problematic:
+ * 0x90 (Task HorizontalBlank)
+ * 0xd0 (Task EvenField HorizontalBlank)
+ *
+ * We have set the digitzer such that we don't have to worry
+ * about these problem codes.
+ */
+ data[4] = 0xB0F0B0F0;
+ /*
+ * Start codes for beginning of active line in vertical blank
+ * 0xa0 (Task VerticalBlank )
+ * 0xe0 (Task EvenField VerticalBlank )
+ */
+ data[5] = 0xA0E0A0E0;
+ }
+
+ CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+
+ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+}
+
+void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
+{
+ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ struct cx18_mdl *mdl;
+
+ if (!cx18_stream_enabled(s))
+ return;
+
+ /* Return if the firmware is not running low on MDLs */
+ if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
+ CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
+ return;
+
+ /* Return if there are no MDLs to rotate back to the firmware */
+ if (atomic_read(&s->q_full.depth) < 2)
+ return;
+
+ /*
+ * Take the oldest IDX MDL still holding data, and discard its index
+ * entries by scheduling the MDL to go back to the firmware
+ */
+ mdl = cx18_dequeue(s, &s->q_full);
+ if (mdl != NULL)
+ cx18_enqueue(s, mdl, &s->q_free);
+}
+
+static
+struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
+ struct cx18_mdl *mdl)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, mdl, &s->q_free);
+
+ q = cx18_enqueue(s, mdl, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max MDLs it can handle */
+
+ cx18_mdl_sync_for_device(s, mdl);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
+ s->bufs_per_mdl, mdl->id, s->mdl_size);
+ return q;
+}
+
+static
+void _cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_mdl *mdl;
+
+ if (atomic_read(&s->q_free.depth) == 0 ||
+ atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ mdl = cx18_dequeue(s, &s->q_free);
+ if (mdl == NULL)
+ break;
+ q = _cx18_stream_put_mdl_fw(s, mdl);
+ } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
+void cx18_out_work_handler(struct work_struct *work)
+{
+ struct cx18_stream *s =
+ container_of(work, struct cx18_stream, out_work_order);
+
+ _cx18_stream_load_fw_queue(s);
+}
+
+static void cx18_stream_configure_mdls(struct cx18_stream *s)
+{
+ cx18_unload_queues(s);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_YUV:
+ /*
+ * Height should be a multiple of 32 lines.
+ * Set the MDL size to the exact size needed for one frame.
+ * Use enough buffers per MDL to cover the MDL size
+ */
+ if (s->pixelformat == V4L2_PIX_FMT_HM12)
+ s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
+ else
+ s->mdl_size = 720 * s->cx->cxhdl.height * 2;
+ s->bufs_per_mdl = s->mdl_size / s->buf_size;
+ if (s->mdl_size % s->buf_size)
+ s->bufs_per_mdl++;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+ s->bufs_per_mdl = 1;
+ if (cx18_raw_vbi(s->cx)) {
+ s->mdl_size = (s->cx->is_60hz ? 12 : 18)
+ * 2 * vbi_active_samples;
+ } else {
+ /*
+ * See comment in cx18_vbi_setup() below about the
+ * extra lines we capture in sliced VBI mode due to
+ * the lines on which EAV RP codes toggle.
+ */
+ s->mdl_size = s->cx->is_60hz
+ ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
+ : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
+ }
+ break;
+ default:
+ s->bufs_per_mdl = 1;
+ s->mdl_size = s->buf_size * s->bufs_per_mdl;
+ break;
+ }
+
+ cx18_load_queues(s);
+}
+
+int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+{
+ u32 data[MAX_MB_ARGUMENTS];
+ struct cx18 *cx = s->cx;
+ int captype = 0;
+ struct cx18_stream *s_idx;
+
+ if (!cx18_stream_enabled(s))
+ return -EINVAL;
+
+ CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case CX18_ENC_STREAM_TYPE_MPG:
+ captype = CAPTURE_CHANNEL_TYPE_MPEG;
+ cx->mpg_data_received = cx->vbi_data_inserted = 0;
+ cx->dualwatch_jiffies = jiffies;
+ cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
+ cx->search_pack_header = 0;
+ break;
+
+ case CX18_ENC_STREAM_TYPE_IDX:
+ captype = CAPTURE_CHANNEL_TYPE_INDEX;
+ break;
+ case CX18_ENC_STREAM_TYPE_TS:
+ captype = CAPTURE_CHANNEL_TYPE_TS;
+ break;
+ case CX18_ENC_STREAM_TYPE_YUV:
+ captype = CAPTURE_CHANNEL_TYPE_YUV;
+ break;
+ case CX18_ENC_STREAM_TYPE_PCM:
+ captype = CAPTURE_CHANNEL_TYPE_PCM;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
+#ifdef CX18_ENCODER_PARSES_SLICED
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
+#else
+ /*
+ * Currently we set things up so that Sliced VBI from the
+ * digitizer is handled as Raw VBI by the encoder
+ */
+ captype = CAPTURE_CHANNEL_TYPE_VBI;
+#endif
+ cx->vbi.frame = 0;
+ cx->vbi.inserted_frame = 0;
+ memset(cx->vbi.sliced_mpeg_size,
+ 0, sizeof(cx->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+ cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
+ s->handle = data[0];
+ cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+ /*
+ * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
+ * set up all the parameters, as it is not obvious which parameters the
+ * firmware shares across capture channel types and which it does not.
+ *
+ * Some of the cx18_vapi() calls below apply to only certain capture
+ * channel types. We're hoping there's no harm in calling most of them
+ * anyway, as long as the values are all consistent. Setting some
+ * shared parameters will have no effect once an analog capture channel
+ * has started streaming.
+ */
+ if (captype != CAPTURE_CHANNEL_TYPE_TS) {
+ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+
+ /*
+ * Audio related reset according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ */
+ if (atomic_read(&cx->ana_capturing) == 0)
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
+ s->handle, 12);
+
+ /*
+ * Number of lines for Field 1 & Field 2 according to
+ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
+ * Field 1 is 312 for 625 line systems in BT.656
+ * Field 2 is 313 for 625 line systems in BT.656
+ */
+ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+ s->handle, 312, 313);
+
+ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+ cx18_vbi_setup(s);
+
+ /*
+ * Select to receive I, P, and B frame index entries, if the
+ * index stream is enabled. Otherwise disable index entry
+ * generation.
+ */
+ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
+ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
+ s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
+
+ /* Call out to the common CX2341x API setup for user controls */
+ cx->cxhdl.priv = s;
+ cx2341x_handler_setup(&cx->cxhdl);
+
+ /*
+ * When starting a capture and we're set for radio,
+ * ensure the video is muted, despite the user control.
+ */
+ if (!cx->cxhdl.video_mute &&
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
+ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
+ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
+
+ /* Enable the Video Format Converter for UYVY 4:2:2 support,
+ * rather than the default HM12 Macroblovk 4:2:0 support.
+ */
+ if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
+ if (s->pixelformat == V4L2_PIX_FMT_UYVY)
+ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+ s->handle, 1);
+ else
+ /* If in doubt, default to HM12 */
+ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
+ s->handle, 0);
+ }
+ }
+
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ cx2341x_handler_set_busy(&cx->cxhdl, 1);
+ clear_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
+ }
+
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
+ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
+ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
+
+ /* Init all the cpu_mdls for this stream */
+ cx18_stream_configure_mdls(s);
+ _cx18_stream_load_fw_queue(s);
+
+ /* begin_capture */
+ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
+ CX18_DEBUG_WARN("Error starting capture!\n");
+ /* Ensure we're really not capturing before releasing MDLs */
+ set_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
+ return -EINVAL;
+ }
+
+ /* you're live! sit back and await interrupts :) */
+ if (captype != CAPTURE_CHANNEL_TYPE_TS)
+ atomic_inc(&cx->ana_capturing);
+ atomic_inc(&cx->tot_capturing);
+ return 0;
+}
+EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
+
+void cx18_stop_all_captures(struct cx18 *cx)
+{
+ int i;
+
+ for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (!cx18_stream_enabled(s))
+ continue;
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ cx18_stop_v4l2_encode_stream(s, 0);
+ }
+}
+
+int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+{
+ struct cx18 *cx = s->cx;
+
+ if (!cx18_stream_enabled(s))
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ CX18_DEBUG_INFO("Stop Capture\n");
+
+ if (atomic_read(&cx->tot_capturing) == 0)
+ return 0;
+
+ set_bit(CX18_F_S_STOPPING, &s->s_flags);
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG)
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
+ else
+ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
+ CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
+ }
+
+ if (s->type != CX18_ENC_STREAM_TYPE_TS)
+ atomic_dec(&cx->ana_capturing);
+ atomic_dec(&cx->tot_capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+
+ /* Tell the CX23418 it can't use our buffers anymore */
+ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
+
+ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ clear_bit(CX18_F_S_STOPPING, &s->s_flags);
+
+ if (atomic_read(&cx->tot_capturing) > 0)
+ return 0;
+
+ cx2341x_handler_set_busy(&cx->cxhdl, 0);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ wake_up(&s->waitq);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
+
+u32 cx18_find_handle(struct cx18 *cx)
+{
+ int i;
+
+ /* find first available handle to be used for global settings */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+ if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+ return s->handle;
+ }
+ return CX18_INVALID_TASK_HANDLE;
+}
+
+struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+{
+ int i;
+ struct cx18_stream *s;
+
+ if (handle == CX18_INVALID_TASK_HANDLE)
+ return NULL;
+
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+ if (cx18_stream_enabled(s))
+ return s;
+ }
+ return NULL;
+}
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/pci/cx18/cx18-streams.h
index 713b0e61536d..713b0e61536d 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/pci/cx18/cx18-streams.h
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c
index 6d3121ff45a2..6d3121ff45a2 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/pci/cx18/cx18-vbi.c
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/pci/cx18/cx18-vbi.h
index b365cf4b4668..b365cf4b4668 100644
--- a/drivers/media/video/cx18/cx18-vbi.h
+++ b/drivers/media/pci/cx18/cx18-vbi.h
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/pci/cx18/cx18-version.h
index fed48b6bb67b..fed48b6bb67b 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/pci/cx18/cx18-version.h
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/pci/cx18/cx18-video.c
index 6dc84aac8f44..6dc84aac8f44 100644
--- a/drivers/media/video/cx18/cx18-video.c
+++ b/drivers/media/pci/cx18/cx18-video.c
diff --git a/drivers/media/video/cx18/cx18-video.h b/drivers/media/pci/cx18/cx18-video.h
index 529006a06e5c..529006a06e5c 100644
--- a/drivers/media/video/cx18/cx18-video.h
+++ b/drivers/media/pci/cx18/cx18-video.h
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h
index 767a8d23e3f2..767a8d23e3f2 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/pci/cx18/cx23418.h
diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig
new file mode 100644
index 000000000000..eafa1144b17d
--- /dev/null
+++ b/drivers/media/pci/cx23885/Kconfig
@@ -0,0 +1,50 @@
+config VIDEO_CX23885
+ tristate "Conexant cx23885 (2388x successor) support"
+ depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
+ select SND_PCM
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_DVB
+ select VIDEOBUF_DMA_SG
+ select VIDEO_CX25840
+ select VIDEO_CX2341X
+ select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for Conexant 23885 based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx23885
+
+config MEDIA_ALTERA_CI
+ tristate "Altera FPGA based CI module"
+ depends on VIDEO_CX23885 && DVB_CORE
+ select ALTERA_STAPL
+ ---help---
+ An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
+
+ To compile this driver as a module, choose M here: the
+ module will be called altera-ci
diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile
new file mode 100644
index 000000000000..a2cbdcf15a8c
--- /dev/null
+++ b/drivers/media/pci/cx23885/Makefile
@@ -0,0 +1,15 @@
+cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
+ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
+ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
+ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
+ cx23885-f300.o cx23885-alsa.o
+
+obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
+obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c
new file mode 100644
index 000000000000..aee7f0dacff1
--- /dev/null
+++ b/drivers/media/pci/cx23885/altera-ci.c
@@ -0,0 +1,839 @@
+/*
+ * altera-ci.c
+ *
+ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
+ *
+ * Copyright (C) 2010,2011 NetUP Inc.
+ * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * currently cx23885 GPIO's used.
+ * GPIO-0 ~INT in
+ * GPIO-1 TMS out
+ * GPIO-2 ~reset chips out
+ * GPIO-3 to GPIO-10 data/addr for CA in/out
+ * GPIO-11 ~CS out
+ * GPIO-12 AD_RG out
+ * GPIO-13 ~WR out
+ * GPIO-14 ~RD out
+ * GPIO-15 ~RDY in
+ * GPIO-16 TCK out
+ * GPIO-17 TDO in
+ * GPIO-18 TDI out
+ */
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ * bits 31-16
+ * +-----------+
+ * | Reserved |
+ * +-----------+
+ * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS |
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
+ * +-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include "altera-ci.h"
+#include "dvb_ca_en50221.h"
+
+/* FPGA regs */
+#define NETUP_CI_INT_CTRL 0x00
+#define NETUP_CI_BUSCTRL2 0x01
+#define NETUP_CI_ADDR0 0x04
+#define NETUP_CI_ADDR1 0x05
+#define NETUP_CI_DATA 0x06
+#define NETUP_CI_BUSCTRL 0x07
+#define NETUP_CI_PID_ADDR0 0x08
+#define NETUP_CI_PID_ADDR1 0x09
+#define NETUP_CI_PID_DATA 0x0a
+#define NETUP_CI_TSA_DIV 0x0c
+#define NETUP_CI_TSB_DIV 0x0d
+#define NETUP_CI_REVISION 0x0f
+
+/* const for ci op */
+#define NETUP_CI_FLG_CTL 1
+#define NETUP_CI_FLG_RD 1
+#define NETUP_CI_FLG_AD 1
+
+static unsigned int ci_dbg;
+module_param(ci_dbg, int, 0644);
+MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
+
+static unsigned int pid_dbg;
+module_param(pid_dbg, int, 0644);
+MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
+
+MODULE_DESCRIPTION("altera FPGA CI module");
+MODULE_AUTHOR("Igor M. Liplianin <liplianin@netup.ru>");
+MODULE_LICENSE("GPL");
+
+#define ci_dbg_print(args...) \
+ do { \
+ if (ci_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+#define pid_dbg_print(args...) \
+ do { \
+ if (pid_dbg) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+struct altera_ci_state;
+struct netup_hw_pid_filter;
+
+struct fpga_internal {
+ void *dev;
+ struct mutex fpga_mutex;/* two CI's on the same fpga */
+ struct netup_hw_pid_filter *pid_filt[2];
+ struct altera_ci_state *state[2];
+ struct work_struct work;
+ int (*fpga_rw) (void *dev, int flag, int data, int rw);
+ int cis_used;
+ int filts_used;
+ int strt_wrk;
+};
+
+/* stores all private variables for communication with CI */
+struct altera_ci_state {
+ struct fpga_internal *internal;
+ struct dvb_ca_en50221 ca;
+ int status;
+ int nr;
+};
+
+/* stores all private variables for hardware pid filtering */
+struct netup_hw_pid_filter {
+ struct fpga_internal *internal;
+ struct dvb_demux *demux;
+ /* save old functions */
+ int (*start_feed)(struct dvb_demux_feed *feed);
+ int (*stop_feed)(struct dvb_demux_feed *feed);
+
+ int status;
+ int nr;
+};
+
+/* internal params node */
+struct fpga_inode {
+ /* pointer for internal params, one for each pair of CI's */
+ struct fpga_internal *internal;
+ struct fpga_inode *next_inode;
+};
+
+/* first internal params */
+static struct fpga_inode *fpga_first_inode;
+
+/* find chip by dev */
+static struct fpga_inode *find_inode(void *dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+
+ if (temp_chip == NULL)
+ return temp_chip;
+
+ /*
+ Search for the last fpga CI chip or
+ find it by dev */
+ while ((temp_chip != NULL) &&
+ (temp_chip->internal->dev != dev))
+ temp_chip = temp_chip->next_inode;
+
+ return temp_chip;
+}
+/* check demux */
+static struct fpga_internal *check_filter(struct fpga_internal *temp_int,
+ void *demux_dev, int filt_nr)
+{
+ if (temp_int == NULL)
+ return NULL;
+
+ if ((temp_int->pid_filt[filt_nr]) == NULL)
+ return NULL;
+
+ if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
+ return temp_int;
+
+ return NULL;
+}
+
+/* find chip by demux */
+static struct fpga_inode *find_dinode(void *demux_dev)
+{
+ struct fpga_inode *temp_chip = fpga_first_inode;
+ struct fpga_internal *temp_int;
+
+ /*
+ * Search of the last fpga CI chip or
+ * find it by demux
+ */
+ while (temp_chip != NULL) {
+ if (temp_chip->internal != NULL) {
+ temp_int = temp_chip->internal;
+ if (check_filter(temp_int, demux_dev, 0))
+ break;
+ if (check_filter(temp_int, demux_dev, 1))
+ break;
+ }
+
+ temp_chip = temp_chip->next_inode;
+ }
+
+ return temp_chip;
+}
+
+/* deallocating chip */
+static void remove_inode(struct fpga_internal *internal)
+{
+ struct fpga_inode *prev_node = fpga_first_inode;
+ struct fpga_inode *del_node = find_inode(internal->dev);
+
+ if (del_node != NULL) {
+ if (del_node == fpga_first_inode) {
+ fpga_first_inode = del_node->next_inode;
+ } else {
+ while (prev_node->next_inode != del_node)
+ prev_node = prev_node->next_inode;
+
+ if (del_node->next_inode == NULL)
+ prev_node->next_inode = NULL;
+ else
+ prev_node->next_inode =
+ prev_node->next_inode->next_inode;
+ }
+
+ kfree(del_node);
+ }
+}
+
+/* allocating new chip */
+static struct fpga_inode *append_internal(struct fpga_internal *internal)
+{
+ struct fpga_inode *new_node = fpga_first_inode;
+
+ if (new_node == NULL) {
+ new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ fpga_first_inode = new_node;
+ } else {
+ while (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+
+ new_node->next_inode =
+ kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
+ if (new_node->next_inode != NULL)
+ new_node = new_node->next_inode;
+ else
+ new_node = NULL;
+ }
+
+ if (new_node != NULL) {
+ new_node->internal = internal;
+ new_node->next_inode = NULL;
+ }
+
+ return new_node;
+}
+
+static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
+ u8 val, u8 read)
+{
+ inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
+ return inter->fpga_rw(inter->dev, 0, val, read);
+}
+
+/* flag - mem/io, read - read/write */
+int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
+ u8 flag, u8 read, int addr, u8 val)
+{
+
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+
+ u8 store;
+ int mem = 0;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
+ netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ store &= 0x0f;
+ store |= ((state->nr << 7) | (flag << 6));
+
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
+ mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
+ (read) ? "read" : "write", addr,
+ (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
+ (read) ? mem : val);
+
+ return mem;
+}
+
+int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
+{
+ return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
+}
+
+int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
+ NETUP_CI_FLG_RD, addr, 0);
+}
+
+int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
+ u8 addr, u8 data)
+{
+ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
+}
+
+int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ /* reasonable timeout for CI reset is 10 seconds */
+ unsigned long t_out = jiffies + msecs_to_jiffies(9999);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ (ret & 0xcf) | (1 << (5 - state->nr)), 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ for (;;) {
+ mdelay(50);
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ 0, NETUP_CI_FLG_RD);
+ mutex_unlock(&inter->fpga_mutex);
+
+ if ((ret & (1 << (5 - state->nr))) == 0)
+ break;
+ if (time_after(jiffies, t_out))
+ break;
+ }
+
+
+ ci_dbg_print("%s: %d msecs\n", __func__,
+ jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
+
+ return 0;
+}
+
+int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
+{
+ /* not implemented */
+ return 0;
+}
+
+int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
+{
+ struct altera_ci_state *state = en50221->data;
+ struct fpga_internal *inter = state->internal;
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
+ (ret & 0x0f) | (1 << (3 - state->nr)), 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+
+/* work handler */
+static void netup_read_ci_status(struct work_struct *work)
+{
+ struct fpga_internal *inter =
+ container_of(work, struct fpga_internal, work);
+ int ret;
+
+ ci_dbg_print("%s\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+ /* ack' irq */
+ ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
+ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ if (inter->state[1] != NULL) {
+ inter->state[1]->status =
+ ((ret & 1) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
+ __func__, inter->state[1]->status);
+ };
+
+ if (inter->state[0] != NULL) {
+ inter->state[0]->status =
+ ((ret & 2) == 0 ?
+ DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY : 0);
+ ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
+ __func__, inter->state[0]->status);
+ };
+}
+
+/* CI irq handler */
+int altera_ci_irq(void *dev)
+{
+ struct fpga_inode *temp_int = NULL;
+ struct fpga_internal *inter = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (dev != NULL) {
+ temp_int = find_inode(dev);
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ schedule_work(&inter->work);
+ }
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(altera_ci_irq);
+
+int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
+ int open)
+{
+ struct altera_ci_state *state = en50221->data;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ return state->status;
+}
+
+void altera_hw_filt_release(void *main_dev, int filt_nr)
+{
+ struct fpga_inode *temp_int = find_inode(main_dev);
+ struct netup_hw_pid_filter *pid_filt = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
+ /* stored old feed controls */
+ pid_filt->demux->start_feed = pid_filt->start_feed;
+ pid_filt->demux->stop_feed = pid_filt->stop_feed;
+
+ if (((--(temp_int->internal->filts_used)) <= 0) &&
+ ((temp_int->internal->cis_used) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(pid_filt->internal);
+ }
+
+ kfree(pid_filt);
+
+ }
+
+}
+EXPORT_SYMBOL(altera_hw_filt_release);
+
+void altera_ci_release(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct altera_ci_state *state = NULL;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int != NULL) {
+ state = temp_int->internal->state[ci_nr - 1];
+ altera_hw_filt_release(dev, ci_nr);
+
+
+ if (((temp_int->internal->filts_used) <= 0) &&
+ ((--(temp_int->internal->cis_used)) <= 0)) {
+
+ ci_dbg_print("%s: Actually removing\n", __func__);
+
+ remove_inode(temp_int->internal);
+ kfree(state->internal);
+ }
+
+ if (state != NULL) {
+ if (state->ca.data != NULL)
+ dvb_ca_en50221_release(&state->ca);
+
+ kfree(state);
+ }
+ }
+
+}
+EXPORT_SYMBOL(altera_ci_release);
+
+static void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
+ u16 pid, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+
+ /* pid 0-0x1f always enabled, don't touch them */
+ if ((pid == 0x2000) || (pid < 0x20))
+ return;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
+
+ if (onoff)/* 0 - on, 1 - off */
+ store |= (1 << (pid & 7));
+ else
+ store &= ~(1 << (pid & 7));
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
+ pid_filt->nr, pid, pid, onoff ? "off" : "on");
+}
+
+static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
+ int filt_nr, int onoff)
+{
+ struct fpga_internal *inter = pid_filt->internal;
+ u8 store = 0;
+ int i;
+
+ pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr,
+ onoff ? "off" : "on");
+
+ if (onoff)/* 0 - on, 1 - off */
+ store = 0xff;/* ignore pid */
+ else
+ store = 0;/* enable pid */
+
+ mutex_lock(&inter->fpga_mutex);
+
+ for (i = 0; i < 1024; i++) {
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
+
+ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
+ ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
+ /* pid 0-0x1f always enabled */
+ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
+ (i > 3 ? store : 0), 0);
+ }
+
+ mutex_unlock(&inter->fpga_mutex);
+}
+
+int altera_pid_feed_control(void *demux_dev, int filt_nr,
+ struct dvb_demux_feed *feed, int onoff)
+{
+ struct fpga_inode *temp_int = find_dinode(demux_dev);
+ struct fpga_internal *inter = temp_int->internal;
+ struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
+
+ altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
+ /* call old feed proc's */
+ if (onoff)
+ pid_filt->start_feed(feed);
+ else
+ pid_filt->stop_feed(feed);
+
+ if (feed->pid == 0x2000)
+ altera_toggle_fullts_streaming(pid_filt, filt_nr,
+ onoff ? 0 : 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_pid_feed_control);
+
+int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 1);
+
+ return 0;
+}
+
+int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
+{
+ altera_pid_feed_control(feed->demux, num, feed, 0);
+
+ return 0;
+}
+
+int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 1);
+}
+
+int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 1);
+}
+
+int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_start_feed(feed, 2);
+}
+
+int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
+{
+ return altera_ci_stop_feed(feed, 2);
+}
+
+int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
+{
+ struct netup_hw_pid_filter *pid_filt = NULL;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+
+ pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!pid_filt) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->filts_used)++;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->filts_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
+ pid_filt, hw_filt_nr - 1);
+ inter->pid_filt[hw_filt_nr - 1] = pid_filt;
+ pid_filt->demux = config->demux;
+ pid_filt->internal = inter;
+ pid_filt->nr = hw_filt_nr - 1;
+ /* store old feed controls */
+ pid_filt->start_feed = config->demux->start_feed;
+ pid_filt->stop_feed = config->demux->stop_feed;
+ /* replace with new feed controls */
+ if (hw_filt_nr == 1) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_1;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
+ } else if (hw_filt_nr == 2) {
+ pid_filt->demux->start_feed = altera_ci_start_feed_2;
+ pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
+ }
+
+ altera_toggle_fullts_streaming(pid_filt, 0, 1);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
+ __func__, ret);
+
+ kfree(pid_filt);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_hw_filt_init);
+
+int altera_ci_init(struct altera_ci_config *config, int ci_nr)
+{
+ struct altera_ci_state *state;
+ struct fpga_inode *temp_int = find_inode(config->dev);
+ struct fpga_internal *inter = NULL;
+ int ret = 0;
+ u8 store = 0;
+
+ state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (!state) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (temp_int != NULL) {
+ inter = temp_int->internal;
+ (inter->cis_used)++;
+ inter->fpga_rw = config->fpga_rw;
+ ci_dbg_print("%s: Find Internal Structure!\n", __func__);
+ } else {
+ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
+ if (!inter) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ temp_int = append_internal(inter);
+ inter->cis_used = 1;
+ inter->dev = config->dev;
+ inter->fpga_rw = config->fpga_rw;
+ mutex_init(&inter->fpga_mutex);
+ inter->strt_wrk = 1;
+ ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
+ }
+
+ ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
+ state, ci_nr - 1);
+ state->internal = inter;
+ state->nr = ci_nr - 1;
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
+ state->ca.read_cam_control = altera_ci_read_cam_ctl;
+ state->ca.write_cam_control = altera_ci_write_cam_ctl;
+ state->ca.slot_reset = altera_ci_slot_reset;
+ state->ca.slot_shutdown = altera_ci_slot_shutdown;
+ state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
+ state->ca.poll_slot_status = altera_poll_ci_slot_status;
+ state->ca.data = state;
+
+ ret = dvb_ca_en50221_init(config->adapter,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret)
+ goto err;
+
+ inter->state[ci_nr - 1] = state;
+
+ altera_hw_filt_init(config, ci_nr);
+
+ if (inter->strt_wrk) {
+ INIT_WORK(&inter->work, netup_read_ci_status);
+ inter->strt_wrk = 0;
+ }
+
+ ci_dbg_print("%s: CI initialized!\n", __func__);
+
+ mutex_lock(&inter->fpga_mutex);
+
+ /* Enable div */
+ netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
+ netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
+
+ /* enable TS out */
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store |= (3 << 4);
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
+ /* enable irq */
+ netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
+
+ schedule_work(&inter->work);
+
+ return 0;
+err:
+ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
+
+ kfree(state);
+
+ return ret;
+}
+EXPORT_SYMBOL(altera_ci_init);
+
+int altera_ci_tuner_reset(void *dev, int ci_nr)
+{
+ struct fpga_inode *temp_int = find_inode(dev);
+ struct fpga_internal *inter = NULL;
+ u8 store;
+
+ ci_dbg_print("%s\n", __func__);
+
+ if (temp_int == NULL)
+ return -1;
+
+ if (temp_int->internal == NULL)
+ return -1;
+
+ inter = temp_int->internal;
+
+ mutex_lock(&inter->fpga_mutex);
+
+ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
+ store &= ~(4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+ msleep(100);
+ store |= (4 << (2 - ci_nr));
+ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
+
+ mutex_unlock(&inter->fpga_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(altera_ci_tuner_reset);
diff --git a/drivers/media/video/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h
index 70e4fd69ad9e..70e4fd69ad9e 100644
--- a/drivers/media/video/cx23885/altera-ci.h
+++ b/drivers/media/pci/cx23885/altera-ci.h
diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c
index c9f15d6dec40..c9f15d6dec40 100644
--- a/drivers/media/video/cx23885/cimax2.c
+++ b/drivers/media/pci/cx23885/cimax2.c
diff --git a/drivers/media/video/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h
index 518744a4c8a5..518744a4c8a5 100644
--- a/drivers/media/video/cx23885/cimax2.h
+++ b/drivers/media/pci/cx23885/cimax2.h
diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c
new file mode 100644
index 000000000000..5d5052d0253f
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-417.c
@@ -0,0 +1,1790 @@
+/*
+ *
+ * Support for a cx23417 mpeg encoder via cx23885 host port.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * - CX23885/7/8 support
+ *
+ * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+
+#include "cx23885.h"
+#include "cx23885-ioctl.h"
+
+#define CX23885_FIRM_IMAGE_SIZE 376836
+#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 32;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+ "number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (v4l_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, \
+ (dev) ? dev->name : "cx23885[?]", ## arg); \
+ } while (0)
+
+static struct cx23885_tvnorm cx23885_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }, {
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ }, {
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ }, {
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ }, {
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ }, {
+ .name = "PAL-N",
+ .id = V4L2_STD_PAL_N,
+ }, {
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ }, {
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+ }, {
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ }, {
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ }
+};
+
+/* ------------------------------------------------------------------ */
+enum cx23885_capture_type {
+ CX23885_MPEG_CAPTURE,
+ CX23885_RAW_CAPTURE,
+ CX23885_RAW_PASSTHRU_CAPTURE
+};
+enum cx23885_capture_bits {
+ CX23885_RAW_BITS_NONE = 0x00,
+ CX23885_RAW_BITS_YUV_CAPTURE = 0x01,
+ CX23885_RAW_BITS_PCM_CAPTURE = 0x02,
+ CX23885_RAW_BITS_VBI_CAPTURE = 0x04,
+ CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum cx23885_capture_end {
+ CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */
+ CX23885_END_NOW, /* stop immediately, no irq */
+};
+enum cx23885_framerate {
+ CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum cx23885_stream_port {
+ CX23885_OUTPUT_PORT_MEMORY,
+ CX23885_OUTPUT_PORT_STREAMING,
+ CX23885_OUTPUT_PORT_SERIAL
+};
+enum cx23885_data_xfer_status {
+ CX23885_MORE_BUFFERS_FOLLOW,
+ CX23885_LAST_BUFFER,
+};
+enum cx23885_picture_mask {
+ CX23885_PICTURE_MASK_NONE,
+ CX23885_PICTURE_MASK_I_FRAMES,
+ CX23885_PICTURE_MASK_I_P_FRAMES = 0x3,
+ CX23885_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx23885_vbi_mode_bits {
+ CX23885_VBI_BITS_SLICED,
+ CX23885_VBI_BITS_RAW,
+};
+enum cx23885_vbi_insertion_bits {
+ CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx23885_dma_unit {
+ CX23885_DMA_BYTES,
+ CX23885_DMA_FRAMES,
+};
+enum cx23885_dma_transfer_status_bits {
+ CX23885_DMA_TRANSFER_BITS_DONE = 0x01,
+ CX23885_DMA_TRANSFER_BITS_ERROR = 0x04,
+ CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx23885_pause {
+ CX23885_PAUSE_ENCODING,
+ CX23885_RESUME_ENCODING,
+};
+enum cx23885_copyright {
+ CX23885_COPYRIGHT_OFF,
+ CX23885_COPYRIGHT_ON,
+};
+enum cx23885_notification_type {
+ CX23885_NOTIFICATION_REFRESH,
+};
+enum cx23885_notification_status {
+ CX23885_NOTIFICATION_OFF,
+ CX23885_NOTIFICATION_ON,
+};
+enum cx23885_notification_mailbox {
+ CX23885_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx23885_field1_lines {
+ CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx23885_field2_lines {
+ CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx23885_custom_data_type {
+ CX23885_CUSTOM_EXTENSION_USR_DATA,
+ CX23885_CUSTOM_PRIVATE_PACKET,
+};
+enum cx23885_mute {
+ CX23885_UNMUTE,
+ CX23885_MUTE,
+};
+enum cx23885_mute_video_mask {
+ CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx23885_mute_video_shift {
+ CX23885_MUTE_VIDEO_V_SHIFT = 8,
+ CX23885_MUTE_VIDEO_U_SHIFT = 16,
+ CX23885_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
+ bits 31-16
++-----------+
+| Reserved |
++-----------+
+ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
++-------+-------+-------+-------+-------+-------+-------+-------+
+| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
++-------+-------+-------+-------+-------+-------+-------+-------+
+|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
++-------+-------+-------+-------+-------+-------+-------+-------+
+***/
+#define MC417_MIWR 0x8000
+#define MC417_MIRD 0x4000
+#define MC417_MICS 0x2000
+#define MC417_MIRDY 0x1000
+#define MC417_MIADDR 0x0F00
+#define MC417_MIDATA 0x00FF
+
+/* MIADDR* nibble definitions */
+#define MCI_MEMORY_DATA_BYTE0 0x000
+#define MCI_MEMORY_DATA_BYTE1 0x100
+#define MCI_MEMORY_DATA_BYTE2 0x200
+#define MCI_MEMORY_DATA_BYTE3 0x300
+#define MCI_MEMORY_ADDRESS_BYTE2 0x400
+#define MCI_MEMORY_ADDRESS_BYTE1 0x500
+#define MCI_MEMORY_ADDRESS_BYTE0 0x600
+#define MCI_REGISTER_DATA_BYTE0 0x800
+#define MCI_REGISTER_DATA_BYTE1 0x900
+#define MCI_REGISTER_DATA_BYTE2 0xA00
+#define MCI_REGISTER_DATA_BYTE3 0xB00
+#define MCI_REGISTER_ADDRESS_BYTE0 0xC00
+#define MCI_REGISTER_ADDRESS_BYTE1 0xD00
+#define MCI_REGISTER_MODE 0xE00
+
+/* Read and write modes */
+#define MCI_MODE_REGISTER_READ 0
+#define MCI_MODE_REGISTER_WRITE 1
+#define MCI_MODE_MEMORY_READ 0
+#define MCI_MODE_MEMORY_WRITE 0x40
+
+/*** Bit definitions for MC417_CTL register ****
+ bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
++--------+-------------+--------+--------------+------------+
+|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
++--------+-------------+--------+--------------+------------+
+***/
+#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN 0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW 0x1
+#define MC417_SPD_CTL_MEDIUM 0x0
+#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3 0x3
+#define MC417_GPIO_SEL_GPIO2 0x2
+#define MC417_GPIO_SEL_GPIO1 0x1
+#define MC417_GPIO_SEL_GPIO0 0x0
+
+void cx23885_mc417_init(struct cx23885_dev *dev)
+{
+ u32 regval;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Configure MC417_CTL register to defaults. */
+ regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) |
+ MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) |
+ MC417_UART_GPIO_EN;
+ cx_write(MC417_CTL, regval);
+
+ /* Configure MC417_OEN to defaults. */
+ regval = MC417_MIRDY;
+ cx_write(MC417_OEN, regval);
+
+ /* Configure MC417_RWD to defaults. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS;
+ cx_write(MC417_RWD, regval);
+}
+
+static int mc417_wait_ready(struct cx23885_dev *dev)
+{
+ u32 mi_ready;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+
+ for (;;) {
+ mi_ready = cx_read(MC417_RWD) & MC417_MIRDY;
+ if (mi_ready != 0)
+ return 0;
+ if (time_after(jiffies, timeout))
+ return -1;
+ udelay(1);
+ }
+}
+
+int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
+{
+ u32 regval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 |
+ (value & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+
+ /* Transition CS/WR to effect write transaction across bus. */
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 |
+ ((value >> 8) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 |
+ ((value >> 16) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 |
+ ((value >> 24) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Indicate that this is a write. */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+ MCI_MODE_REGISTER_WRITE;
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ return mc417_wait_ready(dev);
+}
+
+int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
+{
+ int retval;
+ u32 regval;
+ u32 tempval;
+ u32 dataval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
+ ((address & 0x00FF));
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Indicate that this is a register read. */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
+ MCI_MODE_REGISTER_READ;
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ retval = mc417_wait_ready(dev);
+
+ /* switch the DAT0-7 GPIO[10:3] to input mode */
+ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+ /* Read data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+
+ /* Transition RD to effect read transaction across bus.
+ * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)?
+ * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its
+ * input only...)
+ */
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+
+ /* Collect byte */
+ tempval = cx_read(MC417_RWD);
+ dataval = tempval & 0x000000FF;
+
+ /* Bring CS and RD high. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 8);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 16);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 24);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ *value = dataval;
+
+ return retval;
+}
+
+int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value)
+{
+ u32 regval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 |
+ (value & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+
+ /* Transition CS/WR to effect write transaction across bus. */
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 |
+ ((value >> 8) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 |
+ ((value >> 16) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 |
+ ((value >> 24) & 0x000000FF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+ MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ return mc417_wait_ready(dev);
+}
+
+int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value)
+{
+ int retval;
+ u32 regval;
+ u32 tempval;
+ u32 dataval;
+
+ /* Enable MC417 GPIO outputs except for MC417_MIRDY,
+ * which is an input.
+ */
+ cx_write(MC417_OEN, MC417_MIRDY);
+
+ /* Write address byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
+ MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
+ ((address >> 8) & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Write address byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
+ (address & 0xFF);
+ cx_write(MC417_RWD, regval);
+ regval |= MC417_MICS | MC417_MIWR;
+ cx_write(MC417_RWD, regval);
+
+ /* Wait for the trans to complete (MC417_MIRDY asserted). */
+ retval = mc417_wait_ready(dev);
+
+ /* switch the DAT0-7 GPIO[10:3] to input mode */
+ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
+
+ /* Read data byte 3 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+
+ /* Transition RD to effect read transaction across bus. */
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
+ cx_write(MC417_RWD, regval);
+
+ /* Collect byte */
+ tempval = cx_read(MC417_RWD);
+ dataval = ((tempval & 0x000000FF) << 24);
+
+ /* Bring CS and RD high. */
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 2 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 16);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 1 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= ((tempval & 0x000000FF) << 8);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ /* Read data byte 0 */
+ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
+ cx_write(MC417_RWD, regval);
+ tempval = cx_read(MC417_RWD);
+ dataval |= (tempval & 0x000000FF);
+ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
+ cx_write(MC417_RWD, regval);
+
+ *value = dataval;
+
+ return retval;
+}
+
+void mc417_gpio_set(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Set the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val |= (mask & 0x000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask)
+{
+ u32 val;
+
+ /* Clear the gpio value */
+ mc417_register_read(dev, 0x900C, &val);
+ val &= ~(mask & 0x0000ffff);
+ mc417_register_write(dev, 0x900C, val);
+}
+
+void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
+{
+ u32 val;
+
+ /* Enable GPIO direction bits */
+ mc417_register_read(dev, 0x9020, &val);
+ if (asoutput)
+ val |= (mask & 0x0000ffff);
+ else
+ val &= ~(mask & 0x0000ffff);
+
+ mc417_register_write(dev, 0x9020, val);
+}
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+ switch (cmd) {
+ case CX2341X_ENC_PING_FW:
+ return "PING_FW";
+ case CX2341X_ENC_START_CAPTURE:
+ return "START_CAPTURE";
+ case CX2341X_ENC_STOP_CAPTURE:
+ return "STOP_CAPTURE";
+ case CX2341X_ENC_SET_AUDIO_ID:
+ return "SET_AUDIO_ID";
+ case CX2341X_ENC_SET_VIDEO_ID:
+ return "SET_VIDEO_ID";
+ case CX2341X_ENC_SET_PCR_ID:
+ return "SET_PCR_ID";
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return "SET_FRAME_RATE";
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return "SET_FRAME_SIZE";
+ case CX2341X_ENC_SET_BIT_RATE:
+ return "SET_BIT_RATE";
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return "SET_GOP_PROPERTIES";
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return "SET_ASPECT_RATIO";
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ return "SET_DNR_FILTER_MODE";
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return "SET_CORING_LEVELS";
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return "SET_SPATIAL_FILTER_TYPE";
+ case CX2341X_ENC_SET_VBI_LINE:
+ return "SET_VBI_LINE";
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return "SET_STREAM_TYPE";
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return "SET_OUTPUT_PORT";
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return "SET_AUDIO_PROPERTIES";
+ case CX2341X_ENC_HALT_FW:
+ return "HALT_FW";
+ case CX2341X_ENC_GET_VERSION:
+ return "GET_VERSION";
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return "SET_GOP_CLOSURE";
+ case CX2341X_ENC_GET_SEQ_END:
+ return "GET_SEQ_END";
+ case CX2341X_ENC_SET_PGM_INDEX_INFO:
+ return "SET_PGM_INDEX_INFO";
+ case CX2341X_ENC_SET_VBI_CONFIG:
+ return "SET_VBI_CONFIG";
+ case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+ return "SET_DMA_BLOCK_SIZE";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+ return "GET_PREV_DMA_INFO_MB_10";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+ return "GET_PREV_DMA_INFO_MB_9";
+ case CX2341X_ENC_SCHED_DMA_TO_HOST:
+ return "SCHED_DMA_TO_HOST";
+ case CX2341X_ENC_INITIALIZE_INPUT:
+ return "INITIALIZE_INPUT";
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return "SET_FRAME_DROP_RATE";
+ case CX2341X_ENC_PAUSE_ENCODER:
+ return "PAUSE_ENCODER";
+ case CX2341X_ENC_REFRESH_INPUT:
+ return "REFRESH_INPUT";
+ case CX2341X_ENC_SET_COPYRIGHT:
+ return "SET_COPYRIGHT";
+ case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+ return "SET_EVENT_NOTIFICATION";
+ case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+ return "SET_NUM_VSYNC_LINES";
+ case CX2341X_ENC_SET_PLACEHOLDER:
+ return "SET_PLACEHOLDER";
+ case CX2341X_ENC_MUTE_VIDEO:
+ return "MUTE_VIDEO";
+ case CX2341X_ENC_MUTE_AUDIO:
+ return "MUTE_AUDIO";
+ case CX2341X_ENC_MISC:
+ return "MISC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int cx23885_mbox_func(void *priv,
+ u32 command,
+ int in,
+ int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx23885_dev *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval = 0;
+ int i;
+
+ dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+ cmd_to_str(command));
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+ if (value != 0x12345678) {
+ printk(KERN_ERR
+ "Firmware and/or mailbox pointer not initialized "
+ "or corrupted, signature = 0x%x, cmd = %s\n", value,
+ cmd_to_str(command));
+ return -1;
+ }
+
+ /* This read looks at 32 bits, but flag is only 8 bits.
+ * Seems we also bail if CMD or TIMEOUT bytes are set???
+ */
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (flag) {
+ printk(KERN_ERR "ERROR: Mailbox appears to be in use "
+ "(%x), cmd = %s\n", flag, cmd_to_str(command));
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ /* command code */
+ mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+ mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+ IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+ dprintk(3, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+ dprintk(3, "API Output %d = %d\n", i, data[i]);
+ }
+
+ mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+ dprintk(3, "API result = %d\n", retval);
+
+ flag = 0;
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx23885_api_cmd(struct cx23885_dev *dev,
+ u32 command,
+ u32 inputcnt,
+ u32 outputcnt,
+ ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+ va_start(vargs, outputcnt);
+ for (i = 0; i < inputcnt; i++)
+ data[i] = va_arg(vargs, int);
+
+ err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+
+ return err;
+}
+
+static int cx23885_find_mailbox(struct cx23885_dev *dev)
+{
+ u32 signature[4] = {
+ 0x12345678, 0x34567812, 0x56781234, 0x78123456
+ };
+ int signaturecnt = 0;
+ u32 value;
+ int i;
+
+ dprintk(2, "%s()\n", __func__);
+
+ for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) {
+ mc417_memory_read(dev, i, &value);
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+ return i+1;
+ }
+ }
+ printk(KERN_ERR "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static int cx23885_load_firmware(struct cx23885_dev *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 gpio_output = 0;
+ u32 gpio_value;
+ u32 checksum = 0;
+ u32 *dataptr;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Save GPIO settings before reset of APU */
+ retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+ retval |= mc417_memory_read(dev, 0x900C, &gpio_value);
+
+ retval = mc417_register_write(dev,
+ IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_APU, 0);
+
+ if (retval != 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return -1;
+ }
+
+ retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME,
+ &dev->pci->dev);
+
+ if (retval != 0) {
+ printk(KERN_ERR
+ "ERROR: Hotplug firmware request failed (%s).\n",
+ CX23885_FIRM_IMAGE_NAME);
+ printk(KERN_ERR "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != CX23885_FIRM_IMAGE_SIZE) {
+ printk(KERN_ERR "ERROR: Firmware size mismatch "
+ "(have %zd, expected %d)\n",
+ firmware->size, CX23885_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ printk(KERN_ERR
+ "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ /* transfer to the chip */
+ dprintk(2, "Loading firmware ...\n");
+ dataptr = (u32 *)firmware->data;
+ for (i = 0; i < (firmware->size >> 2); i++) {
+ value = *dataptr;
+ checksum += ~value;
+ if (mc417_memory_write(dev, i, value) != 0) {
+ printk(KERN_ERR "ERROR: Loading firmware failed!\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ dataptr++;
+ }
+
+ /* read back to verify with the checksum */
+ dprintk(1, "Verifying firmware ...\n");
+ for (i--; i >= 0; i--) {
+ if (mc417_memory_read(dev, i, &value) != 0) {
+ printk(KERN_ERR "ERROR: Reading firmware failed!\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ checksum -= ~value;
+ }
+ if (checksum) {
+ printk(KERN_ERR
+ "ERROR: Firmware load failed (checksum mismatch).\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ release_firmware(firmware);
+ dprintk(1, "Firmware upload successful.\n");
+
+ retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+ IVTV_CMD_HW_BLOCKS_RST);
+
+ /* F/W power up disturbs the GPIOs, restore state */
+ retval |= mc417_register_write(dev, 0x9020, gpio_output);
+ retval |= mc417_register_write(dev, 0x900C, gpio_value);
+
+ retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+ retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ /* Hardcoded GPIO's here */
+ retval |= mc417_register_write(dev, 0x9020, 0x4000);
+ retval |= mc417_register_write(dev, 0x900C, 0x4000);
+
+ mc417_register_read(dev, 0x9020, &gpio_output);
+ mc417_register_read(dev, 0x900C, &gpio_value);
+
+ if (retval < 0)
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return 0;
+}
+
+void cx23885_417_check_encoder(struct cx23885_dev *dev)
+{
+ u32 status, seq;
+
+ status = seq = 0;
+ cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+ dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx23885_codec_settings(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* Dynamically change the height based on video standard */
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ dev->ts1.height = 480;
+ else
+ dev->ts1.height = 576;
+
+ /* assign frame size */
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->ts1.height, dev->ts1.width);
+
+ dev->mpeg_params.width = dev->ts1.width;
+ dev->mpeg_params.height = dev->ts1.height;
+ dev->mpeg_params.is_50hz =
+ (dev->encodernorm.id & V4L2_STD_625_50) != 0;
+
+ cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params);
+
+ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
+{
+ int version;
+ int retval;
+ u32 i, data[7];
+
+ dprintk(1, "%s()\n", __func__);
+
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(2, "%s() PING OK\n", __func__);
+ retval = cx23885_load_firmware(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() f/w load failed\n", __func__);
+ return retval;
+ }
+ retval = cx23885_find_mailbox(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() mailbox < 0, error\n",
+ __func__);
+ return -1;
+ }
+ dev->cx23417_mailbox = retval;
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "ERROR: cx23417 firmware ping failed!\n");
+ return -1;
+ }
+ retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+ &version);
+ if (retval < 0) {
+ printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+ "version failed!\n");
+ return -1;
+ }
+ dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+ msleep(200);
+ }
+
+ cx23885_codec_settings(dev);
+ msleep(60);
+
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115);
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0);
+
+ /* Setup to capture VBI */
+ data[0] = 0x0001BD00;
+ data[1] = 1; /* frames per interrupt */
+ data[2] = 4; /* total bufs */
+ data[3] = 0x91559155; /* start codes */
+ data[4] = 0x206080C0; /* stop codes */
+ data[5] = 6; /* lines */
+ data[6] = 64; /* BPL */
+
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+ data[2], data[3], data[4], data[5], data[6]);
+
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ valid = ((i >= 19) && (i <= 21));
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+ valid, 0 , 0, 0);
+ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+ i | 0x80000000, valid, 0, 0, 0);
+ }
+
+ cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE);
+ msleep(60);
+
+ /* initialize the video input */
+ cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ msleep(60);
+
+ /* Enable VIP style pixel invalidation so we work with scaled mode */
+ mc417_memory_write(dev, 2120, 0x00000080);
+
+ /* start capturing to the host interface */
+ if (startencoder) {
+ cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE);
+ msleep(10);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ fh->dev->ts1.ts_packet_size = mpeglinesize;
+ fh->dev->ts1.ts_packet_count = mpeglines;
+
+ *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+ *count = mpegbufs;
+
+ return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ return cx23885_buf_prepare(q, &fh->dev->ts1,
+ (struct cx23885_buffer *)vb,
+ field);
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb);
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static struct videobuf_queue_ops cx23885_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int cx23885_queryctrl(struct cx23885_dev *dev,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ /* MPEG V4L2 controls */
+ if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
+}
+
+static int cx23885_querymenu(struct cx23885_dev *dev,
+ struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ cx23885_queryctrl(dev, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ call_all(dev, core, g_std, id);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
+ if (*id & cx23885_tvnorms[i].id)
+ break;
+ if (i == ARRAY_SIZE(cx23885_tvnorms))
+ return -EINVAL;
+ dev->encodernorm = cx23885_tvnorms[i];
+
+ /* Have the drier core notify the subdevices */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *id);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ return cx23885_get_input(file, priv, i);
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ strcpy(t->name, "Television");
+ call_all(dev, tuner, g_tuner, t);
+
+ dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return cx23885_set_frequency(file, priv, f);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_tsport *tsport = &dev->ts1;
+
+ strlcpy(cap->driver, dev->name, sizeof(cap->driver));
+ strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ 0;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = dev->ts1.width;
+ f->fmt.pix.height = dev->ts1.height;
+ f->fmt.pix.field = fh->mpegq.field;
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_reqbufs(&fh->mpegq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_querybuf(&fh->mpegq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_qbuf(&fh->mpegq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx23885_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
+
+ if (err == 0) {
+ err = cx2341x_update(dev, cx23885_mbox_func,
+ &dev->mpeg_params, &p);
+ dev->mpeg_params = p;
+ }
+ return err;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ printk(KERN_INFO
+ "%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ cx2341x_log_status(&dev->mpeg_params, name);
+ printk(KERN_INFO
+ "%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *a)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ return cx23885_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ return cx23885_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+ struct cx23885_dev *dev = video_drvdata(file);
+ struct cx23885_fh *fh;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+ videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops,
+ &dev->pci->dev, &dev->ts1.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* FIXME: Review this crap */
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+ /* stop mpeg capture */
+ cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ CX23885_END_NOW, CX23885_MPEG_CAPTURE,
+ CX23885_RAW_BITS_NONE);
+
+ msleep(500);
+ cx23885_417_check_encoder(dev);
+
+ cx23885_cancel_buffers(&fh->dev->ts1);
+ }
+ }
+
+ if (fh->mpegq.streaming)
+ videobuf_streamoff(&fh->mpegq);
+ if (fh->mpegq.reading)
+ videobuf_read_stop(&fh->mpegq);
+
+ videobuf_mmap_free(&fh->mpegq);
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+ /* Start mpeg encoder on first read. */
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+ if (cx23885_initialize_codec(dev, 1) < 0)
+ return -EINVAL;
+ }
+ }
+
+ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s\n", __func__);
+
+ return videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_querystd = vidioc_g_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
+#endif
+};
+
+static struct video_device cx23885_mpeg_template = {
+ .name = "cx23885",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+void cx23885_417_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ if (dev->v4l_device) {
+ if (video_is_registered(dev->v4l_device))
+ video_unregister_device(dev->v4l_device);
+ else
+ video_device_release(dev->v4l_device);
+ dev->v4l_device = NULL;
+ }
+}
+
+static struct video_device *cx23885_video_dev_alloc(
+ struct cx23885_tsport *tsport,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ struct cx23885_dev *dev = tsport->dev;
+
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+ cx23885_boards[tsport->dev->board].name, type);
+ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ return vfd;
+}
+
+int cx23885_417_register(struct cx23885_dev *dev)
+{
+ /* FIXME: Port1 hardcoded here */
+ int err = -ENODEV;
+ struct cx23885_tsport *tsport = &dev->ts1;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER)
+ return err;
+
+ /* Set default TV standard */
+ dev->encodernorm = cx23885_tvnorms[0];
+
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ tsport->height = 480;
+ else
+ tsport->height = 576;
+
+ tsport->width = 720;
+ cx2341x_fill_defaults(&dev->mpeg_params);
+
+ dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+ /* Allocate and initialize V4L video device */
+ dev->v4l_device = cx23885_video_dev_alloc(tsport,
+ dev->pci, &cx23885_mpeg_template, "mpeg");
+ video_set_drvdata(dev->v4l_device, dev);
+ err = video_register_device(dev->v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register mpeg device\n", dev->name);
+ return err;
+ }
+
+ printk(KERN_INFO "%s: registered device %s [mpeg]\n",
+ dev->name, video_device_node_name(dev->v4l_device));
+
+ /* ST: Configure the encoder paramaters, but don't begin
+ * encoding, this resolves an issue where the first time the
+ * encoder is started video can be choppy.
+ */
+ cx23885_initialize_codec(dev, 0);
+
+ return 0;
+}
+
+MODULE_FIRMWARE(CX23885_FIRM_IMAGE_NAME);
diff --git a/drivers/media/video/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c
index 795169237e70..795169237e70 100644
--- a/drivers/media/video/cx23885/cx23885-alsa.c
+++ b/drivers/media/pci/cx23885/cx23885-alsa.c
diff --git a/drivers/media/video/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c
index 134ebddd860f..134ebddd860f 100644
--- a/drivers/media/video/cx23885/cx23885-av.c
+++ b/drivers/media/pci/cx23885/cx23885-av.c
diff --git a/drivers/media/video/cx23885/cx23885-av.h b/drivers/media/pci/cx23885/cx23885-av.h
index d2915c3e53a2..d2915c3e53a2 100644
--- a/drivers/media/video/cx23885/cx23885-av.h
+++ b/drivers/media/pci/cx23885/cx23885-av.h
diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c
new file mode 100644
index 000000000000..39a4a4b9ed7e
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-cards.c
@@ -0,0 +1,1694 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <media/cx25840.h>
+#include <linux/firmware.h>
+#include <misc/altera.h>
+
+#include "cx23885.h"
+#include "tuner-xc2028.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "altera-ci.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "cx23888-ir.h"
+
+static unsigned int netup_card_rev = 4;
+module_param(netup_card_rev, int, 0644);
+MODULE_PARM_DESC(netup_card_rev,
+ "NetUP Dual DVB-T/C CI card revision");
+static unsigned int enable_885_ir;
+module_param(enable_885_ir, int, 0644);
+MODULE_PARM_DESC(enable_885_ir,
+ "Enable integrated IR controller for supported\n"
+ "\t\t CX2388[57] boards that are wired for it:\n"
+ "\t\t\tHVR-1250 (reported safe)\n"
+ "\t\t\tTerraTec Cinergy T PCIe Dual (not well tested, appears to be safe)\n"
+ "\t\t\tTeVii S470 (reported unsafe)\n"
+ "\t\t This can cause an interrupt storm with some cards.\n"
+ "\t\t Default: 0 [Disabled]");
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+struct cx23885_board cx23885_boards[] = {
+ [CX23885_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = 1,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = 2,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = 3,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1800lp] = {
+ .name = "Hauppauge WinTV-HVR1800lp",
+ .portc = CX23885_MPEG_DVB,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00,
+ }, {
+ .type = CX23885_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xff02,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1800] = {
+ .name = "Hauppauge WinTV-HVR1800",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_ENCODER,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .tuner_bus = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .amux = CX25840_AUDIO8,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1250] = {
+ .name = "Hauppauge WinTV-HVR1250",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+#ifdef MT2131_NO_ANALOG_SUPPORT_YET
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .tuner_bus = 1,
+#endif
+ .force_bff = 1,
+ .input = {{
+#ifdef MT2131_NO_ANALOG_SUPPORT_YET
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .amux = CX25840_AUDIO8,
+ .gpio0 = 0xff00,
+ }, {
+#endif
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0xff02,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0xff02,
+ } },
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP] = {
+ .name = "DViCO FusionHDTV5 Express",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = {
+ .name = "Hauppauge WinTV-HVR1500Q",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1500] = {
+ .name = "Hauppauge WinTV-HVR1500",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61, /* 0xc2 >> 1 */
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1200] = {
+ .name = "Hauppauge WinTV-HVR1200",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1700] = {
+ .name = "Hauppauge WinTV-HVR1700",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1400] = {
+ .name = "Hauppauge WinTV-HVR1400",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV7 Dual Express",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV DVB-T Dual Express",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = {
+ .name = "Leadtek Winfast PxDVR3200 H",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
+ .name = "Leadtek Winfast PxDVR3200 H XC4000",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TBS_6920] = {
+ .name = "TurboSight TBS 6920",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TEVII_S470] = {
+ .name = "TeVii S470",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_DVBWORLD_2005] = {
+ .name = "DVBWorld DVB-S2 2005",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
+ .ci_type = 1,
+ .name = "NetUP Dual DVB-S2 CI",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1270] = {
+ .name = "Hauppauge WinTV-HVR1270",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1275] = {
+ .name = "Hauppauge WinTV-HVR1275",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1255] = {
+ .name = "Hauppauge WinTV-HVR1255",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1 |
+ CX25840_DIF_ON,
+ .amux = CX25840_AUDIO8,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1255_22111] = {
+ .name = "Hauppauge WinTV-HVR1255",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1 |
+ CX25840_DIF_ON,
+ .amux = CX25840_AUDIO8,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1210] = {
+ .name = "Hauppauge WinTV-HVR1210",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_MYGICA_X8506] = {
+ .name = "Mygica X8506 DMB-TH",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
+ .name = "Magic-Pro ProHDTV Extreme 2",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
+ .name = "Hauppauge WinTV-HVR1850",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_ENCODER,
+ .portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
+ .force_bff = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1 |
+ CX25840_DIF_ON,
+ .amux = CX25840_AUDIO8,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .amux = CX25840_AUDIO7,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .amux = CX25840_AUDIO7,
+ } },
+ },
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
+ .name = "Compro VideoMate E800",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_HAUPPAUGE_HVR1290] = {
+ .name = "Hauppauge WinTV-HVR1290",
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_MYGICA_X8558PRO] = {
+ .name = "Mygica X8558 PRO DMB-TH",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = {
+ .name = "LEADTEK WinFast PxTV1200",
+ .porta = CX23885_ANALOG_VIDEO,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2 |
+ CX25840_NONE0_CH3,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ }, {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_VIN7_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN8_CH3 |
+ CX25840_COMPONENT_ON,
+ } },
+ },
+ [CX23885_BOARD_GOTVIEW_X5_3D_HYBRID] = {
+ .name = "GoTView X5 3D Hybrid",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x64,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .input = {{
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_VIN2_CH1 |
+ CX25840_VIN5_CH2,
+ .gpio0 = 0x02,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX23885_VMUX_COMPOSITE1,
+ }, {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ } },
+ },
+ [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = {
+ .ci_type = 2,
+ .name = "NetUP Dual DVB-T/C-CI RF",
+ .porta = CX23885_ANALOG_VIDEO,
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ .num_fds_portb = 2,
+ .num_fds_portc = 2,
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x64,
+ .input = { {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE1,
+ } },
+ },
+ [CX23885_BOARD_MPX885] = {
+ .name = "MPX-885",
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {{
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE1,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE2,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO6,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE3,
+ .vmux = CX25840_COMPOSITE3,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ }, {
+ .type = CX23885_VMUX_COMPOSITE4,
+ .vmux = CX25840_COMPOSITE4,
+ .amux = CX25840_AUDIO7,
+ .gpio0 = 0,
+ } },
+ },
+ [CX23885_BOARD_MYGICA_X8507] = {
+ .name = "Mygica X8507",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .tuner_bus = 1,
+ .porta = CX23885_ANALOG_VIDEO,
+ .input = {
+ {
+ .type = CX23885_VMUX_TELEVISION,
+ .vmux = CX25840_COMPOSITE2,
+ .amux = CX25840_AUDIO8,
+ },
+ {
+ .type = CX23885_VMUX_COMPOSITE1,
+ .vmux = CX25840_COMPOSITE8,
+ },
+ {
+ .type = CX23885_VMUX_SVIDEO,
+ .vmux = CX25840_SVIDEO_LUMA3 |
+ CX25840_SVIDEO_CHROMA4,
+ },
+ {
+ .type = CX23885_VMUX_COMPONENT,
+ .vmux = CX25840_COMPONENT_ON |
+ CX25840_VIN1_CH1 |
+ CX25840_VIN6_CH2 |
+ CX25840_VIN7_CH3,
+ },
+ },
+ },
+ [CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL] = {
+ .name = "TerraTec Cinergy T PCIe Dual",
+ .portb = CX23885_MPEG_DVB,
+ .portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TEVII_S471] = {
+ .name = "TeVii S471",
+ .portb = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_PROF_8000] = {
+ .name = "Prof Revolution DVB-S2 8000",
+ .portb = CX23885_MPEG_DVB,
+ }
+};
+const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs */
+
+struct cx23885_subid cx23885_subids[] = {
+ {
+ .subvendor = 0x0070,
+ .subdevice = 0x3400,
+ .card = CX23885_BOARD_UNKNOWN,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7600,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800lp,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7800,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7801,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7809,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7911,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1250,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd500,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7790,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7797,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7710,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x7717,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x71d1,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x71d3,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8101,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1700,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8010,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1400,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd618,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb78,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6681,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f39,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
+ }, {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
+ }, {
+ .subvendor = 0x6920,
+ .subdevice = 0x8888,
+ .card = CX23885_BOARD_TBS_6920,
+ }, {
+ .subvendor = 0xd470,
+ .subdevice = 0x9022,
+ .card = CX23885_BOARD_TEVII_S470,
+ }, {
+ .subvendor = 0x0001,
+ .subdevice = 0x2005,
+ .card = CX23885_BOARD_DVBWORLD_2005,
+ }, {
+ .subvendor = 0x1b55,
+ .subdevice = 0x2a2c,
+ .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2211,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1270,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2215,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x221d,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2251,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2259,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255_22111,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2291,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2295,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x2299,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x229d,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f0,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f1,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f2,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f3,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f4,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x22f5,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8651,
+ .card = CX23885_BOARD_MYGICA_X8506,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8657,
+ .card = CX23885_BOARD_MAGICPRO_PROHDTVE2,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8541,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1850,
+ }, {
+ .subvendor = 0x1858,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x8551,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1290,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8578,
+ .card = CX23885_BOARD_MYGICA_X8558PRO,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f22,
+ .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200,
+ }, {
+ .subvendor = 0x5654,
+ .subdevice = 0x2390,
+ .card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID,
+ }, {
+ .subvendor = 0x1b55,
+ .subdevice = 0xe2e4,
+ .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8502,
+ .card = CX23885_BOARD_MYGICA_X8507,
+ }, {
+ .subvendor = 0x153b,
+ .subdevice = 0x117e,
+ .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
+ }, {
+ .subvendor = 0xd471,
+ .subdevice = 0x9022,
+ .card = CX23885_BOARD_TEVII_S471,
+ }, {
+ .subvendor = 0x8000,
+ .subdevice = 0x3034,
+ .card = CX23885_BOARD_PROF_8000,
+ },
+};
+const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
+
+void cx23885_card_list(struct cx23885_dev *dev)
+{
+ int i;
+
+ if (0 == dev->pci->subsystem_vendor &&
+ 0 == dev->pci->subsystem_device) {
+ printk(KERN_INFO
+ "%s: Board has no valid PCIe Subsystem ID and can't\n"
+ "%s: be autodetected. Pass card=<n> insmod option\n"
+ "%s: to workaround that. Redirect complaints to the\n"
+ "%s: vendor of the TV card. Best regards,\n"
+ "%s: -- tux\n",
+ dev->name, dev->name, dev->name, dev->name, dev->name);
+ } else {
+ printk(KERN_INFO
+ "%s: Your board isn't known (yet) to the driver.\n"
+ "%s: Try to pick one of the existing card configs via\n"
+ "%s: card=<n> insmod option. Updating to the latest\n"
+ "%s: version might help as well.\n",
+ dev->name, dev->name, dev->name, dev->name);
+ }
+ printk(KERN_INFO "%s: Here is a list of valid choices for the card=<n> insmod option:\n",
+ dev->name);
+ for (i = 0; i < cx23885_bcount; i++)
+ printk(KERN_INFO "%s: card=%d -> %s\n",
+ dev->name, i, cx23885_boards[i].name);
+}
+
+static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
+ eeprom_data);
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 22001:
+ /* WinTV-HVR1270 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Blast */
+ case 22009:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Blast */
+ case 22011:
+ /* WinTV-HVR1270 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22019:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Recv */
+ case 22021:
+ /* WinTV-HVR1275 (PCIe, Retail, half height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22029:
+ /* WinTV-HVR1210 (PCIe, Retail, half height)
+ * DVB-T and basic analog, IR Recv */
+ case 22101:
+ /* WinTV-HVR1270 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Blast */
+ case 22109:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Blast */
+ case 22111:
+ /* WinTV-HVR1270 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22119:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Recv */
+ case 22121:
+ /* WinTV-HVR1275 (PCIe, Retail, full height)
+ * ATSC/QAM and basic analog, IR Recv */
+ case 22129:
+ /* WinTV-HVR1210 (PCIe, Retail, full height)
+ * DVB-T and basic analog, IR Recv */
+ case 71009:
+ /* WinTV-HVR1200 (PCIe, Retail, full height)
+ * DVB-T and basic analog */
+ case 71359:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71439:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71449:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71939:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71949:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71959:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 71979:
+ /* WinTV-HVR1200 (PCIe, OEM, half height)
+ * DVB-T and basic analog */
+ case 71999:
+ /* WinTV-HVR1200 (PCIe, OEM, full height)
+ * DVB-T and basic analog */
+ case 76601:
+ /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual
+ channel ATSC and MPEG2 HW Encoder */
+ case 77001:
+ /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC
+ and Basic analog */
+ case 77011:
+ /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC
+ and Basic analog */
+ case 77041:
+ /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM
+ and Basic analog */
+ case 77051:
+ /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM
+ and Basic analog */
+ case 78011:
+ /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78501:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78521:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78531:
+ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 78631:
+ /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ case 79001:
+ /* WinTV-HVR1250 (PCIe, Retail, IR, full height,
+ ATSC and Basic analog */
+ case 79101:
+ /* WinTV-HVR1250 (PCIe, Retail, IR, half height,
+ ATSC and Basic analog */
+ case 79501:
+ /* WinTV-HVR1250 (PCIe, No IR, half height,
+ ATSC [at least] and Basic analog) */
+ case 79561:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+ ATSC and Basic analog */
+ case 79571:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, full height,
+ ATSC and Basic analog */
+ case 79671:
+ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
+ ATSC and Basic analog */
+ case 80019:
+ /* WinTV-HVR1400 (Express Card, Retail, IR,
+ * DVB-T and Basic analog */
+ case 81509:
+ /* WinTV-HVR1700 (PCIe, OEM, No IR, half height)
+ * DVB-T and MPEG2 HW Encoder */
+ case 81519:
+ /* WinTV-HVR1700 (PCIe, OEM, No IR, full height)
+ * DVB-T and MPEG2 HW Encoder */
+ break;
+ case 85021:
+ /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
+ Dual channel ATSC and MPEG2 HW Encoder */
+ break;
+ case 85721:
+ /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
+ Dual channel ATSC and Basic analog */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
+ dev->name, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ dev->name, tv.model);
+}
+
+int cx23885_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct cx23885_tsport *port = priv;
+ struct cx23885_dev *dev = port->dev;
+ u32 bitmask = 0;
+
+ if ((command == XC2028_RESET_CLK) || (command == XC2028_I2C_FLUSH))
+ return 0;
+
+ if (command != 0) {
+ printk(KERN_ERR "%s(): Unknown command 0x%x.\n",
+ __func__, command);
+ return -EINVAL;
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ /* Tuner Reset Command */
+ bitmask = 0x04;
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* Two identical tuners on two different i2c buses,
+ * we need to reset the correct gpio. */
+ if (port->nr == 1)
+ bitmask = 0x01;
+ else if (port->nr == 2)
+ bitmask = 0x04;
+ break;
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ /* Tuner Reset Command */
+ bitmask = 0x02;
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_tuner_reset(dev, port->nr);
+ break;
+ }
+
+ if (bitmask) {
+ /* Drive the tuner into reset and back out */
+ cx_clear(GP0_IO, bitmask);
+ mdelay(200);
+ cx_set(GP0_IO, bitmask);
+ }
+
+ return 0;
+}
+
+void cx23885_gpio_setup(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* GPIO-0 cx24227 demodulator reset */
+ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ /* GPIO-0 cx24227 demodulator */
+ /* GPIO-2 xc3028 tuner */
+
+ /* Put the parts into reset */
+ cx_set(GP0_IO, 0x00050000);
+ cx_clear(GP0_IO, 0x00000005);
+ msleep(5);
+
+ /* Bring the parts out of reset */
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ /* GPIO-0 cx24227 demodulator reset */
+ /* GPIO-2 xc5000 tuner reset */
+ cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ /* GPIO-0 656_CLK */
+ /* GPIO-1 656_D0 */
+ /* GPIO-2 8295A Reset */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+ /* GPIO-19 IR_RX */
+
+ /* CX23417 GPIO's */
+ /* EIO15 Zilog Reset */
+ /* EIO14 S5H1409/CX24227 Reset */
+ mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
+
+ /* Bring the demod and blaster out of reset */
+ mc417_gpio_set(dev, GPIO_15 | GPIO_14);
+ mdelay(100);
+
+ /* Force the TDA8295A into reset and back */
+ cx23885_gpio_enable(dev, GPIO_2, 1);
+ cx23885_gpio_set(dev, GPIO_2);
+ mdelay(20);
+ cx23885_gpio_clear(dev, GPIO_2);
+ mdelay(20);
+ cx23885_gpio_set(dev, GPIO_2);
+ mdelay(20);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ /* GPIO-0 tda10048 demodulator reset */
+ /* GPIO-2 tda18271 tuner reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ /* GPIO-0 TDA10048 demodulator reset */
+ /* GPIO-2 TDA8295A Reset */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+
+ /* The following GPIO's are on the interna AVCore (cx25840) */
+ /* GPIO-19 IR_RX */
+ /* GPIO-20 IR_TX 416/DVBT Select */
+ /* GPIO-21 IIS DAT */
+ /* GPIO-22 IIS WCLK */
+ /* GPIO-23 IIS BCLK */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ /* GPIO-0 Dibcom7000p demodulator reset */
+ /* GPIO-2 xc3028L tuner reset */
+ /* GPIO-13 LED */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00050000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000005);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00050005);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ /* GPIO-0 xc5000 tuner reset i2c bus 0 */
+ /* GPIO-1 s5h1409 demod reset i2c bus 0 */
+ /* GPIO-2 xc5000 tuner reset i2c bus 1 */
+ /* GPIO-3 s5h1409 demod reset i2c bus 0 */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x000f0000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x0000000f);
+ mdelay(20);
+ cx_set(GP0_IO, 0x000f000f);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ /* GPIO-0 portb xc3028 reset */
+ /* GPIO-1 portb zl10353 reset */
+ /* GPIO-2 portc xc3028 reset */
+ /* GPIO-3 portc zl10353 reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x000f0000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x0000000f);
+ mdelay(20);
+ cx_set(GP0_IO, 0x000f000f);
+ break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ /* GPIO-2 xc3028 tuner reset */
+
+ /* The following GPIO's are on the internal AVCore (cx25840) */
+ /* GPIO-? zl10353 demod reset */
+
+ /* Put the parts into reset and back */
+ cx_set(GP0_IO, 0x00040000);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ break;
+ case CX23885_BOARD_TBS_6920:
+ case CX23885_BOARD_PROF_8000:
+ cx_write(MC417_CTL, 0x00000036);
+ cx_write(MC417_OEN, 0x00001000);
+ cx_set(MC417_RWD, 0x00000002);
+ mdelay(200);
+ cx_clear(MC417_RWD, 0x00000800);
+ mdelay(200);
+ cx_set(MC417_RWD, 0x00000800);
+ mdelay(200);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ /* GPIO-0 INTA from CiMax1
+ GPIO-1 INTB from CiMax2
+ GPIO-2 reset chips
+ GPIO-3 to GPIO-10 data/addr for CA
+ GPIO-11 ~CS0 to CiMax1
+ GPIO-12 ~CS1 to CiMax2
+ GPIO-13 ADL0 load LSB addr
+ GPIO-14 ADL1 load MSB addr
+ GPIO-15 ~RDY from CiMax
+ GPIO-17 ~RD to CiMax
+ GPIO-18 ~WR to CiMax
+ */
+ cx_set(GP0_IO, 0x00040000); /* GPIO as out */
+ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
+ cx_clear(GP0_IO, 0x00030004);
+ mdelay(100);/* reset delay */
+ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
+ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
+ /* GPIO-15 IN as ~ACK, rest as OUT */
+ cx_write(MC417_OEN, 0x00001000);
+ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
+ cx_write(MC417_RWD, 0x0000c300);
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */
+ /* GPIO-6 I2C Gate which can isolate the demod from the bus */
+ /* GPIO-9 Demod reset */
+
+ /* Put the parts into reset and back */
+ cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1);
+ cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5);
+ cx23885_gpio_clear(dev, GPIO_9);
+ mdelay(20);
+ cx23885_gpio_set(dev, GPIO_9);
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ case CX23885_BOARD_MYGICA_X8507:
+ /* GPIO-0 (0)Analog / (1)Digital TV */
+ /* GPIO-1 reset XC5000 */
+ /* GPIO-2 reset LGS8GL5 / LGS8G75 */
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
+ cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
+ mdelay(100);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
+ mdelay(100);
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ /* GPIO-0 reset first ATBM8830 */
+ /* GPIO-1 reset second ATBM8830 */
+ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
+ cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
+ mdelay(100);
+ cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
+ mdelay(100);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ /* GPIO-0 656_CLK */
+ /* GPIO-1 656_D0 */
+ /* GPIO-2 Wake# */
+ /* GPIO-3-10 cx23417 data0-7 */
+ /* GPIO-11-14 cx23417 addr0-3 */
+ /* GPIO-15-18 cx23417 READY, CS, RD, WR */
+ /* GPIO-19 IR_RX */
+ /* GPIO-20 C_IR_TX */
+ /* GPIO-21 I2S DAT */
+ /* GPIO-22 I2S WCLK */
+ /* GPIO-23 I2S BCLK */
+ /* ALT GPIO: EXP GPIO LATCH */
+
+ /* CX23417 GPIO's */
+ /* GPIO-14 S5H1411/CX24228 Reset */
+ /* GPIO-13 EEPROM write protect */
+ mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1);
+
+ /* Put the demod into reset and protect the eeprom */
+ mc417_gpio_clear(dev, GPIO_14 | GPIO_13);
+ mdelay(100);
+
+ /* Bring the demod out of reset */
+ mc417_gpio_set(dev, GPIO_14);
+ mdelay(100);
+
+ /* CX24228 GPIO */
+ /* Connected to IF / Mux */
+ break;
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ /* GPIO-0 ~INT in
+ GPIO-1 TMS out
+ GPIO-2 ~reset chips out
+ GPIO-3 to GPIO-10 data/addr for CA in/out
+ GPIO-11 ~CS out
+ GPIO-12 ADDR out
+ GPIO-13 ~WR out
+ GPIO-14 ~RD out
+ GPIO-15 ~RDY in
+ GPIO-16 TCK out
+ GPIO-17 TDO in
+ GPIO-18 TDI out
+ */
+ cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */
+ /* GPIO-0 as INT, reset & TMS low */
+ cx_clear(GP0_IO, 0x00010006);
+ mdelay(100);/* reset delay */
+ cx_set(GP0_IO, 0x00000004); /* reset high */
+ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */
+ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */
+ cx_write(MC417_OEN, 0x00005000);
+ /* ~RD, ~WR high; ADDR low; ~CS high */
+ cx_write(MC417_RWD, 0x00000d00);
+ /* enable irq */
+ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
+ break;
+ }
+}
+
+int cx23885_ir_init(struct cx23885_dev *dev)
+{
+ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }, {
+ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
+ .pin = CX23885_PIN_IR_TX_GPIO20,
+ .function = CX23885_PAD_IR_TX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
+
+ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
+ {
+ .flags = V4L2_SUBDEV_IO_PIN_INPUT,
+ .pin = CX23885_PIN_IR_RX_GPIO19,
+ .function = CX23885_PAD_IR_RX,
+ .value = 0,
+ .strength = CX25840_PIN_DRIVE_MEDIUM,
+ }
+ };
+ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
+
+ struct v4l2_subdev_ir_parameters params;
+ int ret = 0;
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ /* FIXME: Implement me */
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ ret = cx23888_ir_probe(dev);
+ if (ret)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ /*
+ * For these boards we need to invert the Tx output via the
+ * IR controller to have the LED off while idle
+ */
+ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
+ params.enable = false;
+ params.shutdown = false;
+ params.invert_level = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ case CX23885_BOARD_TEVII_S470:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rx_pin_cfg_count, ir_rx_pin_cfg);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (!enable_885_ir)
+ break;
+ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
+ if (dev->sd_ir == NULL) {
+ ret = -ENODEV;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
+ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ request_module("ir-kbd-i2c");
+ break;
+ }
+
+ return ret;
+}
+
+void cx23885_ir_fini(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ cx23885_irq_remove(dev, PCI_MSK_IR);
+ cx23888_ir_remove(dev);
+ dev->sd_ir = NULL;
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
+ /* sd_ir is a duplicate pointer to the AV Core, just clear it */
+ dev->sd_ir = NULL;
+ break;
+ }
+}
+
+int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
+{
+ int data;
+ int tdo = 0;
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ /*TMS*/
+ data = ((cx_read(GP0_IO)) & (~0x00000002));
+ data |= (tms ? 0x00020002 : 0x00020000);
+ cx_write(GP0_IO, data);
+
+ /*TDI*/
+ data = ((cx_read(MC417_RWD)) & (~0x0000a000));
+ data |= (tdi ? 0x00008000 : 0);
+ cx_write(MC417_RWD, data);
+ if (read_tdo)
+ tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/
+
+ cx_write(MC417_RWD, data | 0x00002000);
+ udelay(1);
+ /*TCK*/
+ cx_write(MC417_RWD, data);
+
+ return tdo;
+}
+
+void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
+{
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_IR);
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->sd_ir)
+ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
+ break;
+ }
+}
+
+void cx23885_card_setup(struct cx23885_dev *dev)
+{
+ struct cx23885_tsport *ts1 = &dev->ts1;
+ struct cx23885_tsport *ts2 = &dev->ts2;
+
+ static u8 eeprom[256];
+
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client,
+ eeprom, sizeof(eeprom));
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ if (dev->i2c_bus[0].i2c_rc == 0) {
+ if (eeprom[0x80] != 0x84)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ else
+ hauppauge_eeprom(dev, eeprom+0x80);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0x80);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ if (dev->i2c_bus[0].i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0xc0);
+ break;
+ }
+
+ switch (dev->board) {
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ /* break omitted intentionally */
+ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ /* Defaults for VID B - Analog encoder */
+ /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */
+ ts1->gen_ctrl_val = 0x10e;
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+
+ /* APB_TSVALERR_POL (active low)*/
+ ts1->vld_misc_val = 0x2000;
+ ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc);
+ cx_write(0x130184, 0xc);
+
+ /* Defaults for VID C */
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_TBS_6920:
+ ts1->gen_ctrl_val = 0x4; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_TEVII_S471:
+ case CX23885_BOARD_DVBWORLD_2005:
+ case CX23885_BOARD_PROF_8000:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ ts1->gen_ctrl_val = 0x5; /* Parallel */
+ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ default:
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ }
+
+ /* Certain boards support analog, or require the avcore to be
+ * loaded, ensure this happens.
+ */
+ switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ /* Currently only enabled for the integrated IR controller */
+ if (!enable_885_ir)
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
+ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ case CX23885_BOARD_MPX885:
+ case CX23885_BOARD_MYGICA_X8507:
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[2].i2c_adap,
+ "cx25840", 0x88 >> 1, NULL);
+ if (dev->sd_cx25840) {
+ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
+ v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
+ }
+ break;
+ }
+
+ /* AUX-PLL 27MHz CLK */
+ switch (dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ netup_initialize(dev);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ int ret;
+ const struct firmware *fw;
+ const char *filename = "dvb-netup-altera-01.fw";
+ char *action = "configure";
+ static struct netup_card_info cinfo;
+ struct altera_config netup_config = {
+ .dev = dev,
+ .action = action,
+ .jtag_io = netup_jtag_io,
+ };
+
+ netup_initialize(dev);
+
+ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+ if (netup_card_rev)
+ cinfo.rev = netup_card_rev;
+
+ switch (cinfo.rev) {
+ case 0x4:
+ filename = "dvb-netup-altera-04.fw";
+ break;
+ default:
+ filename = "dvb-netup-altera-01.fw";
+ break;
+ }
+ printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
+ cinfo.rev, filename);
+
+ ret = request_firmware(&fw, filename, &dev->pci->dev);
+ if (ret != 0)
+ printk(KERN_ERR "did not find the firmware file. (%s) "
+ "Please see linux/Documentation/dvb/ for more details "
+ "on firmware-problems.", filename);
+ else
+ altera_init(&netup_config, fw);
+
+ release_firmware(fw);
+ break;
+ }
+ }
+}
+
+/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 697728f09430..697728f09430 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
new file mode 100644
index 000000000000..4379d8a6dad5
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -0,0 +1,1412 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+
+#include "dvb_ca_en50221.h"
+#include "s5h1409.h"
+#include "s5h1411.h"
+#include "mt2131.h"
+#include "tda8290.h"
+#include "tda18271.h"
+#include "lgdt330x.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "max2165.h"
+#include "tda10048.h"
+#include "tuner-xc2028.h"
+#include "tuner-simple.h"
+#include "dib7000p.h"
+#include "dibx000_common.h"
+#include "zl10353.h"
+#include "stv0900.h"
+#include "stv0900_reg.h"
+#include "stv6110.h"
+#include "lnbh24.h"
+#include "cx24116.h"
+#include "cimax2.h"
+#include "lgs8gxx.h"
+#include "netup-eeprom.h"
+#include "netup-init.h"
+#include "lgdt3305.h"
+#include "atbm8830.h"
+#include "ds3000.h"
+#include "cx23885-f300.h"
+#include "altera-ci.h"
+#include "stv0367.h"
+#include "drxk.h"
+#include "mt2063.h"
+#include "stv090x.h"
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+
+static unsigned int debug;
+
+#define dprintk(level, fmt, arg...)\
+ do { if (debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int alt_tuner;
+module_param(alt_tuner, int, 0644);
+MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx23885_tsport *port = q->priv_data;
+
+ port->ts_packet_size = 188 * 4;
+ port->ts_packet_count = 32;
+
+ *size = port->ts_packet_size * port->ts_packet_count;
+ *count = 32;
+ return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx23885_tsport *port = q->priv_data;
+ return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_tsport *port = q->priv_data;
+ cx23885_buf_queue(port, (struct cx23885_buffer *)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
+}
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe);
+
+static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
+{
+ struct videobuf_dvb_frontends *f;
+ struct videobuf_dvb_frontend *fe;
+
+ f = &port->frontends;
+
+ if (f->gate <= 1) /* undefined or fe0 */
+ fe = videobuf_dvb_get_frontend(f, 1);
+ else
+ fe = videobuf_dvb_get_frontend(f, f->gate);
+
+ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+
+ /*
+ * FIXME: Improve this path to avoid calling the
+ * cx23885_dvb_set_frontend() every time it passes here.
+ */
+ cx23885_dvb_set_frontend(fe->dvb.frontend);
+}
+
+static struct videobuf_queue_ops dvb_qops = {
+ .buf_setup = dvb_buf_setup,
+ .buf_prepare = dvb_buf_prepare,
+ .buf_queue = dvb_buf_queue,
+ .buf_release = dvb_buf_release,
+};
+
+static struct s5h1409_config hauppauge_generic_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct tda10048_config hauppauge_hvr1200_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+
+static struct tda10048_config hauppauge_hvr1210_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+};
+
+static struct s5h1409_config hauppauge_ezqam_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1800lp_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config hauppauge_hvr1500_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct mt2131_config hauppauge_generic_tunerconfig = {
+ 0x61
+};
+
+static struct lgdt330x_config fusionhdtv_5_express = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40,
+};
+
+static struct s5h1409_config hauppauge_hvr1500q_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1409_config dvico_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config dvico_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_ON,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct s5h1411_config hcw_s5h1411_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+};
+
+static struct xc5000_config dvico_xc5000_tunerconfig = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1200_tuner_config = {
+ .std_map = &hauppauge_hvr1200_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_config hauppauge_hvr1210_tuner_config = {
+ .gate = TDA18271_GATE_DIGITAL,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map hauppauge_hvr127x_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x58 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x58 },
+};
+
+static struct tda18271_config hauppauge_hvr127x_config = {
+ .std_map = &hauppauge_hvr127x_std_map,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+ .i2c_addr = 0x0e,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 1,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+};
+
+static struct dibx000_agc_config xc3028_agc_config = {
+ BAND_VHF | BAND_UHF, /* band_caps */
+
+ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0,
+ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
+ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0,
+ * P_agc_nb_est=2, P_agc_write=0
+ */
+ (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) |
+ (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */
+
+ 712, /* inv_gain */
+ 21, /* time_stabiliz */
+
+ 0, /* alpha_level */
+ 118, /* thlock */
+
+ 0, /* wbd_inv */
+ 2867, /* wbd_ref */
+ 0, /* wbd_sel */
+ 2, /* wbd_alpha */
+
+ 0, /* agc1_max */
+ 0, /* agc1_min */
+ 39718, /* agc2_max */
+ 9930, /* agc2_min */
+ 0, /* agc1_pt1 */
+ 0, /* agc1_pt2 */
+ 0, /* agc1_pt3 */
+ 0, /* agc1_slope1 */
+ 0, /* agc1_slope2 */
+ 0, /* agc2_pt1 */
+ 128, /* agc2_pt2 */
+ 29, /* agc2_slope1 */
+ 29, /* agc2_slope2 */
+
+ 17, /* alpha_mant */
+ 27, /* alpha_exp */
+ 23, /* beta_mant */
+ 51, /* beta_exp */
+
+ 1, /* perform_agc_softsplit */
+};
+
+/* PLL Configuration for COFDM BW_MHz = 8.000000
+ * With external clock = 30.000000 */
+static struct dibx000_bandwidth_config xc3028_bw_config = {
+ 60000, /* internal */
+ 30000, /* sampling */
+ 1, /* pll_cfg: prediv */
+ 8, /* pll_cfg: ratio */
+ 3, /* pll_cfg: range */
+ 1, /* pll_cfg: reset */
+ 0, /* pll_cfg: bypass */
+ 0, /* misc: refdiv */
+ 0, /* misc: bypclk_div */
+ 1, /* misc: IO_CLK_en_core */
+ 1, /* misc: ADClkSrc */
+ 0, /* misc: modulo */
+ (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
+ (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */
+ 20452225, /* timf */
+ 30000000 /* xtal_hz */
+};
+
+static struct dib7000p_config hauppauge_hvr1400_dib7000_config = {
+ .output_mpeg2_in_188_bytes = 1,
+ .hostbus_diversity = 1,
+ .tuner_is_baseband = 0,
+ .update_lna = NULL,
+
+ .agc_config_count = 1,
+ .agc = &xc3028_agc_config,
+ .bw = &xc3028_bw_config,
+
+ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
+ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
+ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
+
+ .pwm_freq_div = 0,
+ .agc_control = NULL,
+ .spur_protect = 0,
+
+ .output_mode = OUTMODE_MPEG2_SERIAL,
+};
+
+static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct stv0900_reg stv0900_ts_regs[] = {
+ { R0900_TSGENERAL, 0x00 },
+ { R0900_P1_TSSPEED, 0x40 },
+ { R0900_P2_TSSPEED, 0x40 },
+ { R0900_P1_TSCFGM, 0xc0 },
+ { R0900_P2_TSCFGM, 0xc0 },
+ { R0900_P1_TSCFGH, 0xe0 },
+ { R0900_P2_TSCFGH, 0xe0 },
+ { R0900_P1_TSCFGL, 0x20 },
+ { R0900_P2_TSCFGL, 0x20 },
+ { 0xffff, 0xff }, /* terminate */
+};
+
+static struct stv0900_config netup_stv0900_config = {
+ .demod_address = 0x68,
+ .demod_mode = 1, /* dual */
+ .xtal = 8000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .ts_config_regs = stv0900_ts_regs,
+ .tun1_maddress = 0,/* 0x60 */
+ .tun2_maddress = 3,/* 0x63 */
+ .tun1_adc = 1,/* 1 Vpp */
+ .tun2_adc = 1,/* 1 Vpp */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_a = {
+ .i2c_address = 0x60,
+ .mclk = 16000000,
+ .clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
+};
+
+static struct stv6110_config netup_stv6110_tunerconfig_b = {
+ .i2c_address = 0x63,
+ .mclk = 16000000,
+ .clk_div = 1,
+ .gain = 8, /* +16 dB - maximum gain */
+};
+
+static struct cx24116_config tbs_cx24116_config = {
+ .demod_address = 0x55,
+};
+
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
+};
+
+static struct cx24116_config dvbworld_cx24116_config = {
+ .demod_address = 0x05,
+};
+
+static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = {
+ .prod = LGS8GXX_PROD_LGS8GL5,
+ .demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 5380, /* 5.38 MHz */
+ .if_neg_center = 1,
+ .ext_adc = 0,
+ .adc_signed = 0,
+ .if_neg_edge = 0,
+};
+
+static struct xc5000_config mygica_x8506_xc5000_config = {
+ .i2c_address = 0x61,
+ .if_khz = 5380,
+};
+
+static struct stv090x_config prof_8000_stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+ .xtal = 27000000,
+ .address = 0x6A,
+ .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED,
+ .repeater_level = STV090x_RPTLEVEL_64,
+ .adc1_range = STV090x_ADC_2Vpp,
+ .diseqc_envelope_mode = false,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+};
+
+static struct stb6100_config prof_8000_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
+static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ if (voltage == SEC_VOLTAGE_18)
+ cx_write(MC417_RWD, 0x00001e00);
+ else if (voltage == SEC_VOLTAGE_13)
+ cx_write(MC417_RWD, 0x00001a00);
+ else
+ cx_write(MC417_RWD, 0x00001800);
+ return 0;
+}
+
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ switch (p->modulation) {
+ case VSB_8:
+ cx23885_gpio_clear(dev, GPIO_5);
+ break;
+ case QAM_64:
+ case QAM_256:
+ default:
+ cx23885_gpio_set(dev, GPIO_5);
+ break;
+ }
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ /* Select Digital TV */
+ cx23885_gpio_set(dev, GPIO_0);
+ break;
+ }
+ return 0;
+}
+
+static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
+ .prod = LGS8GXX_PROD_LGS8G75,
+ .demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 6500, /* 6.50 MHz */
+ .if_neg_center = 1,
+ .ext_adc = 0,
+ .adc_signed = 1,
+ .adc_vpp = 2, /* 1.6 Vpp */
+ .if_neg_edge = 1,
+};
+
+static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
+ .i2c_address = 0x61,
+ .if_khz = 6500,
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 0,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+
+static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
+ .prod = ATBM8830_PROD_8830,
+ .demod_address = 0x44,
+ .serial_ts = 1,
+ .ts_sampling_edge = 1,
+ .ts_clk_gated = 0,
+ .osc_clk_freq = 30400, /* in kHz */
+ .if_freq = 0, /* zero IF */
+ .zif_swap_iq = 1,
+ .agc_min = 0x2E,
+ .agc_max = 0xFF,
+ .agc_hold_loop = 0,
+};
+
+static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
+ .i2c_address = 0x60,
+ .osc_clk = 20
+};
+static struct stv0367_config netup_stv0367_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 27000000,
+ .if_khz = 4500,
+ .if_iq_mode = 0,
+ .ts_mode = 1,
+ .clk_pol = 0,
+ },
+};
+
+static struct xc5000_config netup_xc5000_config[] = {
+ {
+ .i2c_address = 0x61,
+ .if_khz = 4500,
+ }, {
+ .i2c_address = 0x64,
+ .if_khz = 4500,
+ },
+};
+
+static struct drxk_config terratec_drxk_config[] = {
+ {
+ .adr = 0x29,
+ .no_i2c_bridge = 1,
+ }, {
+ .adr = 0x2a,
+ .no_i2c_bridge = 1,
+ },
+};
+
+static struct mt2063_config terratec_mt2063_config[] = {
+ {
+ .tuner_address = 0x60,
+ }, {
+ .tuner_address = 0x67,
+ },
+};
+
+int netup_altera_fpga_rw(void *device, int flag, int data, int read)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)device;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ uint32_t mem = 0;
+
+ mem = cx_read(MC417_RWD);
+ if (read)
+ cx_set(MC417_OEN, ALT_DATA);
+ else {
+ cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */
+ mem &= ~ALT_DATA;
+ mem |= (data & ALT_DATA);
+ }
+
+ if (flag)
+ mem |= ALT_AD_RG;
+ else
+ mem &= ~ALT_AD_RG;
+
+ mem &= ~ALT_CS;
+ if (read)
+ mem = (mem & ~ALT_RD) | ALT_WR;
+ else
+ mem = (mem & ~ALT_WR) | ALT_RD;
+
+ cx_write(MC417_RWD, mem); /* start RW cycle */
+
+ for (;;) {
+ mem = cx_read(MC417_RWD);
+ if ((mem & ALT_RDY) == 0)
+ break;
+ if (time_after(jiffies, timeout))
+ break;
+ udelay(1);
+ }
+
+ cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
+ if (read)
+ return mem & ALT_DATA;
+
+ return 0;
+};
+
+static int dvb_register(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
+ struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ int mfe_shared = 0; /* bus not shared by default */
+ int ret;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ /* init struct videobuf_dvb */
+ fe0->dvb.name = dev->name;
+
+ /* multi-frontend gate control is undefined or defaults to fe0 */
+ port->frontends.gate = 0;
+
+ /* Sets the gate control callback to be used by i2c command calls */
+ port->gate_ctrl = cx23885_dvb_gate_ctrl;
+
+ /* init frontend */
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+ &hauppauge_lgdt3305_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr127x_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+
+ tda18271_attach(&dev->ts1.analog_fe,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (alt_tuner) {
+ case 1:
+ fe0->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_ezqam_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+ }
+ break;
+ case 0:
+ default:
+ fe0->dvb.frontend =
+ dvb_attach(s5h1409_attach,
+ &hauppauge_generic_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ break;
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1800lp_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(mt2131_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_generic_tunerconfig, 0);
+ }
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_5_express,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500q_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &hauppauge_hvr1500q_tunerconfig);
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1500:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &hauppauge_hvr1500_config,
+ &dev->i2c_bus[0].i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1200:
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr1200_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr1200_tuner_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1210:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hauppauge_hvr1210_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_hvr1210_tuner_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1400:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(dib7000p_attach,
+ &i2c_bus->i2c_adap,
+ 0x12, &hauppauge_hvr1400_dib7000_config);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_bus[1].i2c_adap,
+ .i2c_addr = 0x64,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC3028L_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_DIBCOM52,
+ /* This is true for all demods with
+ v36 firmware? */
+ .type = XC2028_D2633,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &dvico_s5h1409_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend == NULL)
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &dvico_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &dvico_xc5000_tunerconfig);
+ break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &i2c_bus->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ }
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_bus[1].i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ };
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
+ &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
+ &dev->i2c_bus[1].i2c_adap, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+ dev->name);
+ goto frontend_detach;
+ }
+ }
+ break;
+ case CX23885_BOARD_TBS_6920:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &tbs_cx24116_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
+
+ break;
+ case CX23885_BOARD_DVBWORLD_2005:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &dvbworld_cx24116_config,
+ &i2c_bus->i2c_adap);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ i2c_bus = &dev->i2c_bus[0];
+ switch (port->nr) {
+ /* port B */
+ case 1:
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &netup_stv0900_config,
+ &i2c_bus->i2c_adap, 0);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(stv6110_attach,
+ fe0->dvb.frontend,
+ &netup_stv6110_tunerconfig_a,
+ &i2c_bus->i2c_adap)) {
+ if (!dvb_attach(lnbh24_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x09))
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+
+ }
+ }
+ break;
+ /* port C */
+ case 2:
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &netup_stv0900_config,
+ &i2c_bus->i2c_adap, 1);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(stv6110_attach,
+ fe0->dvb.frontend,
+ &netup_stv6110_tunerconfig_b,
+ &i2c_bus->i2c_adap)) {
+ if (!dvb_attach(lnbh24_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x0a))
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+
+ }
+ }
+ break;
+ }
+ break;
+ case CX23885_BOARD_MYGICA_X8506:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &mygica_x8506_lgs8gl5_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &mygica_x8506_xc5000_config);
+ }
+ break;
+ case CX23885_BOARD_MAGICPRO_PROHDTVE2:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &magicpro_prohdtve2_lgs8g75_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus2->i2c_adap,
+ &magicpro_prohdtve2_xc5000_config);
+ }
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
+
+ tda18271_attach(&dev->ts1.analog_fe,
+ 0x60, &dev->i2c_bus[1].i2c_adap,
+ &hauppauge_tda18271_config);
+
+ break;
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &hcw_s5h1411_config,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_bus[0].i2c_adap,
+ &hauppauge_tda18271_config);
+ break;
+ case CX23885_BOARD_MYGICA_X8558PRO:
+ switch (port->nr) {
+ /* port B */
+ case 1:
+ i2c_bus = &dev->i2c_bus[0];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg1,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg1);
+ }
+ break;
+ /* port C */
+ case 2:
+ i2c_bus = &dev->i2c_bus[1];
+ fe0->dvb.frontend = dvb_attach(atbm8830_attach,
+ &mygica_x8558pro_atbm8830_cfg2,
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(max2165_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &mygic_x8558pro_max2165_cfg2);
+ }
+ break;
+ }
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ i2c_bus = &dev->i2c_bus[0];
+ mfe_shared = 1;/* MFE */
+ port->frontends.gate = 0;/* not clear for me yet */
+ /* ports B, C */
+ /* MFE frontend 1 DVB-T */
+ fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (NULL == dvb_attach(xc5000_attach,
+ fe0->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ /* load xc5000 firmware */
+ fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
+ if (fe1 == NULL)
+ goto frontend_detach;
+ /* DVB-C init */
+ fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
+ &netup_stv0367_config[port->nr - 1],
+ &i2c_bus->i2c_adap);
+ if (fe1->dvb.frontend != NULL) {
+ fe1->dvb.frontend->id = 1;
+ if (NULL == dvb_attach(xc5000_attach,
+ fe1->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &netup_xc5000_config[port->nr - 1]))
+ goto frontend_detach;
+ }
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ i2c_bus = &dev->i2c_bus[0];
+ i2c_bus2 = &dev->i2c_bus[1];
+
+ switch (port->nr) {
+ /* port b */
+ case 1:
+ fe0->dvb.frontend = dvb_attach(drxk_attach,
+ &terratec_drxk_config[0],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[0],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
+ }
+ break;
+ /* port c */
+ case 2:
+ fe0->dvb.frontend = dvb_attach(drxk_attach,
+ &terratec_drxk_config[1],
+ &i2c_bus->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(mt2063_attach,
+ fe0->dvb.frontend,
+ &terratec_mt2063_config[1],
+ &i2c_bus2->i2c_adap))
+ goto frontend_detach;
+ }
+ break;
+ }
+ break;
+ case CX23885_BOARD_TEVII_S471:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &i2c_bus->i2c_adap);
+ break;
+ case CX23885_BOARD_PROF_8000:
+ i2c_bus = &dev->i2c_bus[0];
+
+ fe0->dvb.frontend = dvb_attach(stv090x_attach,
+ &prof_8000_stv090x_config,
+ &i2c_bus->i2c_adap,
+ STV090x_DEMODULATOR_0);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6100_attach,
+ fe0->dvb.frontend,
+ &prof_8000_stb6100_config,
+ &i2c_bus->i2c_adap))
+ goto frontend_detach;
+
+ fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage;
+ }
+ break;
+ default:
+ printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
+ " isn't supported yet\n",
+ dev->name);
+ break;
+ }
+
+ if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) {
+ printk(KERN_ERR "%s: frontend initialization failed\n",
+ dev->name);
+ goto frontend_detach;
+ }
+
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = cx23885_tuner_callback;
+ if (fe1)
+ fe1->dvb.frontend->callback = cx23885_tuner_callback;
+#if 0
+ /* Ensure all frontends negotiate bus access */
+ fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+ if (fe1)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
+#endif
+
+ /* Put the analog decoder in standby to keep it quiet */
+ call_all(dev, core, s_power, 0);
+
+ if (fe0->dvb.frontend->ops.analog_ops.standby)
+ fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
+
+ /* register everything */
+ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+ &dev->pci->dev, adapter_nr, mfe_shared);
+ if (ret)
+ goto frontend_detach;
+
+ /* init CI & MAC */
+ switch (dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
+ static struct netup_card_info cinfo;
+
+ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
+ memcpy(port->frontends.adapter.proposed_mac,
+ cinfo.port[port->nr - 1].mac, 6);
+ printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
+ port->nr, port->frontends.adapter.proposed_mac);
+
+ netup_ci_init(port);
+ break;
+ }
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
+ struct altera_ci_config netup_ci_cfg = {
+ .dev = dev,/* magic number to identify*/
+ .adapter = &port->frontends.adapter,/* for CI */
+ .demux = &fe0->dvb.demux,/* for hw pid filter */
+ .fpga_rw = netup_altera_fpga_rw,
+ };
+
+ altera_ci_init(&netup_ci_cfg, port->nr);
+ break;
+ }
+ case CX23885_BOARD_TEVII_S470: {
+ u8 eeprom[256]; /* 24C02 i2c eeprom */
+
+ if (port->nr != 1)
+ break;
+
+ /* Read entire EEPROM */
+ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
+ printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
+ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
+ break;
+ }
+ }
+
+ return ret;
+
+frontend_detach:
+ port->gate_ctrl = NULL;
+ videobuf_dvb_dealloc_frontends(&port->frontends);
+ return -EINVAL;
+}
+
+int cx23885_dvb_register(struct cx23885_tsport *port)
+{
+
+ struct videobuf_dvb_frontend *fe0;
+ struct cx23885_dev *dev = port->dev;
+ int err, i;
+
+ /* Here we need to allocate the correct number of frontends,
+ * as reflected in the cards struct. The reality is that currently
+ * no cx23885 boards support this - yet. But, if we don't modify this
+ * code then the second frontend would never be allocated (later)
+ * and fail with error before the attach in dvb_register().
+ * Without these changes we risk an OOPS later. The changes here
+ * are for safety, and should provide a good foundation for the
+ * future addition of any multi-frontend cx23885 based boards.
+ */
+ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+ port->num_frontends);
+
+ for (i = 1; i <= port->num_frontends; i++) {
+ if (videobuf_dvb_alloc_frontend(
+ &port->frontends, i) == NULL) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, i);
+ if (!fe0)
+ err = -EINVAL;
+
+ dprintk(1, "%s\n", __func__);
+ dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ dev->board,
+ dev->name,
+ dev->pci_bus,
+ dev->pci_slot);
+
+ err = -ENODEV;
+
+ /* dvb stuff */
+ /* We have to init the queue for each frontend on a port. */
+ printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name);
+ videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &port->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
+ sizeof(struct cx23885_buffer), port, NULL);
+ }
+ err = dvb_register(port);
+ if (err != 0)
+ printk(KERN_ERR "%s() dvb_register failed err = %d\n",
+ __func__, err);
+
+ return err;
+}
+
+int cx23885_dvb_unregister(struct cx23885_tsport *port)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* FIXME: in an error condition where the we have
+ * an expected number of frontends (attach problem)
+ * then this might not clean up correctly, if 1
+ * is invalid.
+ * This comment only applies to future boards IF they
+ * implement MFE support.
+ */
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+ if (fe0 && fe0->dvb.frontend)
+ videobuf_dvb_unregister_bus(&port->frontends);
+
+ switch (port->dev->board) {
+ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
+ netup_ci_exit(port);
+ break;
+ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
+ altera_ci_release(port->dev, port->nr);
+ break;
+ }
+
+ port->gate_ctrl = NULL;
+
+ return 0;
+}
+
diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c
index 93998f220986..93998f220986 100644
--- a/drivers/media/video/cx23885/cx23885-f300.c
+++ b/drivers/media/pci/cx23885/cx23885-f300.c
diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/pci/cx23885/cx23885-f300.h
index e73344c94963..e73344c94963 100644
--- a/drivers/media/video/cx23885/cx23885-f300.h
+++ b/drivers/media/pci/cx23885/cx23885-f300.h
diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c
index 4887314339cb..4887314339cb 100644
--- a/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/drivers/media/pci/cx23885/cx23885-i2c.c
diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c
new file mode 100644
index 000000000000..56066721edc1
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-input.c
@@ -0,0 +1,365 @@
+/*
+ * Driver for the Conexant CX23885/7/8 PCIe bridge
+ *
+ * Infrared remote control input device
+ *
+ * Most of this file is
+ *
+ * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
+ *
+ * However, the cx23885_input_{init,fini} functions contained herein are
+ * derived from Linux kernel files linux/media/video/.../...-input.c marked as:
+ *
+ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ * Markus Rechberger <mrechberger@gmail.com>
+ * Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Sascha Sommer <saschasommer@freenet.de>
+ * Copyright (C) 2004, 2005 Chris Pascoe
+ * Copyright (C) 2003, 2004 Gerd Knorr
+ * Copyright (C) 2003 Pavel Machek
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <media/rc-core.h>
+#include <media/v4l2-subdev.h>
+
+#include "cx23885.h"
+
+#define MODULE_NAME "cx23885"
+
+static void cx23885_input_process_measurements(struct cx23885_dev *dev,
+ bool overrun)
+{
+ struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
+
+ ssize_t num;
+ int count, i;
+ bool handle = false;
+ struct ir_raw_event ir_core_event[64];
+
+ do {
+ num = 0;
+ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
+ sizeof(ir_core_event), &num);
+
+ count = num / sizeof(struct ir_raw_event);
+
+ for (i = 0; i < count; i++) {
+ ir_raw_event_store(kernel_ir->rc,
+ &ir_core_event[i]);
+ handle = true;
+ }
+ } while (num != 0);
+
+ if (overrun)
+ ir_raw_event_reset(kernel_ir->rc);
+ else if (handle)
+ ir_raw_event_handle(kernel_ir->rc);
+}
+
+void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+{
+ struct v4l2_subdev_ir_parameters params;
+ int overrun, data_available;
+
+ if (dev->sd_ir == NULL || events == 0)
+ return;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /*
+ * The only boards we handle right now. However other boards
+ * using the CX2388x integrated IR controller should be similar
+ */
+ break;
+ default:
+ return;
+ }
+
+ overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
+ V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
+
+ data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
+ V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, stop the device */
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ params.enable = false;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+
+ if (data_available)
+ cx23885_input_process_measurements(dev, overrun);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, clear & restart the device */
+ params.enable = true;
+ /* Mitigate race with cx23885_input_ir_stop() */
+ params.shutdown = atomic_read(&dev->ir_input_stopping);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ }
+}
+
+static int cx23885_input_ir_start(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return -ENODEV;
+
+ atomic_set(&dev->ir_input_stopping, 0);
+
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for baseband compatible with both RC-5 and RC-6A */
+ params.modulation = false;
+ /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
+ /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
+ params.max_pulse_width = 3333333; /* ns */
+ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
+ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
+ params.noise_filter_min_width = 333333; /* ns */
+ /*
+ * This board has inverted receive sense:
+ * mark is received as low logic level;
+ * falling edges are detected as rising edges; etc.
+ */
+ params.invert_level = true;
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ case CX23885_BOARD_TEVII_S470:
+ /*
+ * The IR controller on this board only returns pulse widths.
+ * Any other mode setting will fail to set up the device.
+ */
+ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
+ params.enable = true;
+ params.interrupt_enable = true;
+ params.shutdown = false;
+
+ /* Setup for a standard NEC protocol */
+ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
+ params.carrier_range_lower = 33000; /* Hz */
+ params.carrier_range_upper = 43000; /* Hz */
+ params.duty_cycle = 33; /* percent, 33 percent for NEC */
+
+ /*
+ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
+ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
+ */
+ params.max_pulse_width = 12378022; /* ns */
+
+ /*
+ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
+ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
+ */
+ params.noise_filter_min_width = 351648; /* ns */
+
+ params.modulation = false;
+ params.invert_level = true;
+ break;
+ }
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ return 0;
+}
+
+static int cx23885_input_ir_open(struct rc_dev *rc)
+{
+ struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+ if (kernel_ir->cx == NULL)
+ return -ENODEV;
+
+ return cx23885_input_ir_start(kernel_ir->cx);
+}
+
+static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+{
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+ return;
+
+ /*
+ * Stop the sd_ir subdevice from generating notifications and
+ * scheduling work.
+ * It is shutdown this way in order to mitigate a race with
+ * cx23885_input_rx_work_handler() in the overrun case, which could
+ * re-enable the subdevice.
+ */
+ atomic_set(&dev->ir_input_stopping, 1);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ while (params.shutdown == false) {
+ params.enable = false;
+ params.interrupt_enable = false;
+ params.shutdown = true;
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ }
+ flush_work_sync(&dev->cx25840_work);
+ flush_work_sync(&dev->ir_rx_work);
+ flush_work_sync(&dev->ir_tx_work);
+}
+
+static void cx23885_input_ir_close(struct rc_dev *rc)
+{
+ struct cx23885_kernel_ir *kernel_ir = rc->priv;
+
+ if (kernel_ir->cx != NULL)
+ cx23885_input_ir_stop(kernel_ir->cx);
+}
+
+int cx23885_input_init(struct cx23885_dev *dev)
+{
+ struct cx23885_kernel_ir *kernel_ir;
+ struct rc_dev *rc;
+ char *rc_map;
+ enum rc_driver_type driver_type;
+ unsigned long allowed_protos;
+
+ int ret;
+
+ /*
+ * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
+ * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
+ */
+ if (dev->sd_ir == NULL)
+ return -ENODEV;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1270:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+ case CX23885_BOARD_HAUPPAUGE_HVR1250:
+ /* Integrated CX2388[58] IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_TYPE_ALL;
+ /* The grey Hauppauge RC-5 remote */
+ rc_map = RC_MAP_HAUPPAUGE;
+ break;
+ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_TYPE_NEC;
+ /* The grey Terratec remote with orange buttons */
+ rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS;
+ break;
+ case CX23885_BOARD_TEVII_S470:
+ /* Integrated CX23885 IR controller */
+ driver_type = RC_DRIVER_IR_RAW;
+ allowed_protos = RC_TYPE_ALL;
+ /* A guess at the remote */
+ rc_map = RC_MAP_TEVII_NEC;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ /* cx23885 board instance kernel IR state */
+ kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
+ if (kernel_ir == NULL)
+ return -ENOMEM;
+
+ kernel_ir->cx = dev;
+ kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
+ cx23885_boards[dev->board].name);
+ kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
+ pci_name(dev->pci));
+
+ /* input device */
+ rc = rc_allocate_device();
+ if (!rc) {
+ ret = -ENOMEM;
+ goto err_out_free;
+ }
+
+ kernel_ir->rc = rc;
+ rc->input_name = kernel_ir->name;
+ rc->input_phys = kernel_ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ rc->input_id.vendor = dev->pci->subsystem_vendor;
+ rc->input_id.product = dev->pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = dev->pci->vendor;
+ rc->input_id.product = dev->pci->device;
+ }
+ rc->dev.parent = &dev->pci->dev;
+ rc->driver_type = driver_type;
+ rc->allowed_protos = allowed_protos;
+ rc->priv = kernel_ir;
+ rc->open = cx23885_input_ir_open;
+ rc->close = cx23885_input_ir_close;
+ rc->map_name = rc_map;
+ rc->driver_name = MODULE_NAME;
+
+ /* Go */
+ dev->kernel_ir = kernel_ir;
+ ret = rc_register_device(rc);
+ if (ret)
+ goto err_out_stop;
+
+ return 0;
+
+err_out_stop:
+ cx23885_input_ir_stop(dev);
+ dev->kernel_ir = NULL;
+ rc_free_device(rc);
+err_out_free:
+ kfree(kernel_ir->phys);
+ kfree(kernel_ir->name);
+ kfree(kernel_ir);
+ return ret;
+}
+
+void cx23885_input_fini(struct cx23885_dev *dev)
+{
+ /* Always stop the IR hardware from generating interrupts */
+ cx23885_input_ir_stop(dev);
+
+ if (dev->kernel_ir == NULL)
+ return;
+ rc_unregister_device(dev->kernel_ir->rc);
+ kfree(dev->kernel_ir->phys);
+ kfree(dev->kernel_ir->name);
+ kfree(dev->kernel_ir);
+ dev->kernel_ir = NULL;
+}
diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h
index 75ef15d3f523..75ef15d3f523 100644
--- a/drivers/media/video/cx23885/cx23885-input.h
+++ b/drivers/media/pci/cx23885/cx23885-input.h
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c
index 44812ca78899..44812ca78899 100644
--- a/drivers/media/video/cx23885/cx23885-ioctl.c
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.c
diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h
index 315be0ca5a04..315be0ca5a04 100644
--- a/drivers/media/video/cx23885/cx23885-ioctl.h
+++ b/drivers/media/pci/cx23885/cx23885-ioctl.h
diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c
index 7125247dd255..7125247dd255 100644
--- a/drivers/media/video/cx23885/cx23885-ir.c
+++ b/drivers/media/pci/cx23885/cx23885-ir.c
diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/pci/cx23885/cx23885-ir.h
index 0c9d8bda9e28..0c9d8bda9e28 100644
--- a/drivers/media/video/cx23885/cx23885-ir.h
+++ b/drivers/media/pci/cx23885/cx23885-ir.h
diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h
index a99936e0cbc2..a99936e0cbc2 100644
--- a/drivers/media/video/cx23885/cx23885-reg.h
+++ b/drivers/media/pci/cx23885/cx23885-reg.h
diff --git a/drivers/media/video/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c
index a1154f035bc1..a1154f035bc1 100644
--- a/drivers/media/video/cx23885/cx23885-vbi.c
+++ b/drivers/media/pci/cx23885/cx23885-vbi.c
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
new file mode 100644
index 000000000000..8c4a9a5f9a50
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -0,0 +1,1926 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "cx23885-ioctl.h"
+#include "tuner-xc2028.h"
+
+#include <media/cx25840.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+#define dprintk(level, fmt, arg...)\
+ do { if (video_debug >= level)\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+#if 0
+static struct cx23885_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+#else
+static struct cx23885_fmt formats[] = {
+ {
+#if 0
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+#endif
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }
+};
+#endif
+
+static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
+ (fourcc & 0xff),
+ ((fourcc >> 8) & 0xff),
+ ((fourcc >> 16) & 0xff),
+ ((fourcc >> 24) & 0xff)
+ );
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx23885_ctrl cx23885_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -127,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0x0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535 / 100,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+/* Must be sorted from low to high control ID! */
+static const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ cx23885_user_ctrls,
+ NULL
+};
+
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active))
+ del_timer(&q->timeout);
+ else
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ if (bc != 1)
+ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
+ __func__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __func__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ call_all(dev, core, s_std, norm);
+
+ return 0;
+}
+
+static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
+ cx23885_boards[dev->board].name, type);
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX23885_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx23885_ctls[i].v;
+ return 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __func__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct cx23885_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct cx23885_dev *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
+{
+ /* 8 bit registers, 8 bit values */
+ u8 buf[] = { reg, data };
+
+ struct i2c_msg msg = { .addr = 0x98 >> 1,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
+}
+
+static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
+{
+ /* 8 bit registers, 8 bit values */
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+
+ struct i2c_msg msg[] = {
+ { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
+ { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
+ };
+
+ ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
+ if (ret != 2)
+ printk(KERN_ERR "%s() error\n", __func__);
+
+ return b1[0];
+}
+
+static void cx23885_flatiron_dump(struct cx23885_dev *dev)
+{
+ int i;
+ dprintk(1, "Flatiron dump\n");
+ for (i = 0; i < 0x24; i++) {
+ dprintk(1, "FI[%02x] = %02x\n", i,
+ cx23885_flatiron_read(dev, i));
+ }
+}
+
+static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
+{
+ u8 val;
+ dprintk(1, "%s(input = %d)\n", __func__, input);
+
+ if (input == 1)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
+ else if (input == 2)
+ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
+ else
+ return -EINVAL;
+
+ val |= 0x20; /* Enable clock to delta-sigma and dec filter */
+
+ cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
+
+ /* Wake up */
+ cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
+
+ if (video_debug)
+ cx23885_flatiron_dump(dev);
+
+ return 0;
+}
+
+static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __func__,
+ input, INPUT(input)->vmux,
+ INPUT(input)->gpio0, INPUT(input)->gpio1,
+ INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
+ dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 ||
+ dev->board == CX23885_BOARD_MYGICA_X8507) {
+ /* Select Analog TV */
+ if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
+ cx23885_gpio_clear(dev, GPIO_0);
+ }
+
+ /* Tell the internal A/V decoder */
+ v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
+ INPUT(input)->vmux, 0, 0);
+
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
+ (dev->board == CX23885_BOARD_MPX885) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) {
+ /* Configure audio routing */
+ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
+ INPUT(input)->amux, 0, 0);
+
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ }
+
+ return 0;
+}
+
+static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ dprintk(1, "%s(input=%d)\n", __func__, input);
+
+ /* The baseband video core of the cx23885 has two audio inputs.
+ * LR1 and LR2. In almost every single case so far only HVR1xxx
+ * cards we've only ever supported LR1. Time to support LR2,
+ * which is available via the optional white breakout header on
+ * the board.
+ * We'll use a could of existing enums in the card struct to allow
+ * devs to specify which baseband input they need, or just default
+ * to what we've always used.
+ */
+ if (INPUT(input)->amux == CX25840_AUDIO7)
+ cx23885_flatiron_mux(dev, 1);
+ else if (INPUT(input)->amux == CX25840_AUDIO6)
+ cx23885_flatiron_mux(dev, 2);
+ else {
+ /* Not specifically defined, assume the default. */
+ cx23885_flatiron_mux(dev, 1);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* Stop the dma/fifo before we tamper with it's risc programs */
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ buf->bpl, buf->risc.dma);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx23885_irq_add_enable(dev, 0x01);
+ cx_set(VID_A_INT_MSK, 0x000011);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+
+static int cx23885_restart_video_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf, *prev;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __func__);
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_video_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer,
+ vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ int field_tff;
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ if (dev->tvnorm & V4L2_STD_NTSC)
+ /* NTSC or */
+ field_tff = 1;
+ else
+ field_tff = 0;
+
+ if (cx23885_boards[dev->board].force_bff)
+ /* PAL / SECAM OR 888 in NTSC MODE */
+ field_tff = 0;
+
+ if (field_tff) {
+ /* cx25840 transmits NTSC bottom field first */
+ dprintk(1, "%s() Creating TFF/NTSC risc\n",
+ __func__);
+ line0_offset = buf->bpl;
+ line1_offset = 0;
+ } else {
+ /* All other formats are top field first */
+ dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
+ __func__);
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ }
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ line1_offset,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx23885_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_resource(struct cx23885_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx23885_dev *dev = video_drvdata(file);
+ struct cx23885_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+ videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx23885_buffer),
+ fh, NULL);
+
+
+ dprintk(1, "post videobuf_queue_init()\n");
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_buffer *buf;
+ unsigned int rc = POLLERR;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ mutex_lock(&fh->vidq.vb_lock);
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ goto done;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx23885_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx23885_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ goto done;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ else
+ rc = 0;
+done:
+ mutex_unlock(&fh->vidq.vb_lock);
+ return rc;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbiq.streaming)
+ videobuf_streamoff(&fh->vbiq);
+ if (fh->vbiq.reading)
+ videobuf_read_stop(&fh->vbiq);
+ res_free(dev, fh, RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ videobuf_mmap_free(&fh->vbiq);
+
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* We are not putting the tuner to sleep here on exit, because
+ * we want to use the mpeg encoder in another session to capture
+ * tuner video. Closing this will result in no video to the encoder.
+ */
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx23885_get_control(struct cx23885_dev *dev,
+ struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
+ call_all(dev, core, g_ctrl, ctl);
+ return 0;
+}
+
+int cx23885_set_control(struct cx23885_dev *dev,
+ struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
+ call_all(dev, core, s_ctrl, ctl);
+
+ return 0;
+}
+
+static void init_controls(struct cx23885_dev *dev)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX23885_CTLS; i++) {
+ ctrl.id = cx23885_ctls[i].v.id;
+ ctrl.value = cx23885_ctls[i].v.default_value;
+
+ cx23885_set_control(dev, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct cx23885_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+ &f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int err;
+
+ dprintk(2, "%s()\n", __func__);
+ err = vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
+ fh->width, fh->height, fh->vidq.field);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_reqbufs(get_queue(fh), p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_querybuf(get_queue(fh), p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_qbuf(get_queue(fh), p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(fh))))
+ return -EBUSY;
+
+ /* Don't start VBI streaming unless vida streaming
+ * has already started.
+ */
+ if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
+ ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
+ return -EINVAL;
+
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int err, res;
+ dprintk(1, "%s()\n", __func__);
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_resource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ call_all(dev, core, g_std, id);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+{
+ static const char *iname[] = {
+ [CX23885_VMUX_COMPOSITE1] = "Composite1",
+ [CX23885_VMUX_COMPOSITE2] = "Composite2",
+ [CX23885_VMUX_COMPOSITE3] = "Composite3",
+ [CX23885_VMUX_COMPOSITE4] = "Composite4",
+ [CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_COMPONENT] = "Component",
+ [CX23885_VMUX_TELEVISION] = "Television",
+ [CX23885_VMUX_CABLE] = "Cable TV",
+ [CX23885_VMUX_DVB] = "DVB",
+ [CX23885_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= MAX_CX23885_INPUT)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX23885_VMUX_CABLE == INPUT(n)->type)) {
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX23885_NORMS;
+ }
+
+ /* Two selectable audio inputs for non-tv inputs */
+ if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
+ i->audioset = 0x3;
+
+ if (dev->input == n) {
+ /* enum'd input matches our configured input.
+ * Ask the video decoder to process the call
+ * and give it an oppertunity to update the
+ * status field.
+ */
+ call_all(dev, video, g_input_status, &i->status);
+ }
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx23885_enum_input(dev, i);
+}
+
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s() returns %d\n", __func__, *i);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ return cx23885_get_input(file, priv, i);
+}
+
+int cx23885_set_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ dprintk(1, "%s(%d)\n", __func__, i);
+
+ if (i >= MAX_CX23885_INPUT) {
+ dprintk(1, "%s() -EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (INPUT(i)->type == 0)
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ cx23885_video_mux(dev, i);
+
+ /* By default establish the default audio input for the card also */
+ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
+ cx23885_audio_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return cx23885_set_input(file, priv, i);
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ printk(KERN_INFO
+ "%s/0: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ printk(KERN_INFO
+ "%s/0: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int cx23885_query_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ static const char *iname[] = {
+ [0] = "Baseband L/R 1",
+ [1] = "Baseband L/R 2",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ strcpy(i->name, iname[n]);
+ i->capability = V4L2_AUDCAP_STEREO;
+ i->mode = V4L2_AUDMODE_AVL;
+ return 0;
+
+}
+
+static int vidioc_enum_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_g_audinput(struct file *file, void *priv,
+ struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ i->index = dev->audinput;
+ dprintk(1, "%s(input=%d)\n", __func__, i->index);
+
+ return cx23885_query_audinput(file, priv, i);
+}
+
+static int vidioc_s_audinput(struct file *file, void *priv,
+ const struct v4l2_audio *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ if (i->index >= 2)
+ return -EINVAL;
+
+ dprintk(1, "%s(%d)\n", __func__, i->index);
+
+ dev->audinput = i->index;
+
+ /* Skip the audio defaults from the cards struct, caller wants
+ * directly touch the audio mux hardware. */
+ cx23885_flatiron_mux(dev, dev->audinput + 1);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx23885_ctrl_query(qctrl);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+
+ call_all(dev, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+ /* Update the A/V core */
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+ struct v4l2_control ctrl;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ /* I need to mute audio here */
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = 1;
+ cx23885_set_control(dev, &ctrl);
+
+ call_all(dev, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(100);
+
+ /* I need to unmute audio here */
+ ctrl.value = 0;
+ cx23885_set_control(dev, &ctrl);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
+ struct v4l2_frequency *f)
+{
+ struct v4l2_control ctrl;
+ struct videobuf_dvb_frontend *vfe;
+ struct dvb_frontend *fe;
+
+ struct analog_parameters params = {
+ .mode = V4L2_TUNER_ANALOG_TV,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .std = dev->tvnorm,
+ .frequency = f->frequency
+ };
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ /* I need to mute audio here */
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = 1;
+ cx23885_set_control(dev, &ctrl);
+
+ /* If HVR1850 */
+ dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
+ params.frequency, f->tuner, params.std);
+
+ vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
+ if (!vfe) {
+ mutex_unlock(&dev->lock);
+ return -EINVAL;
+ }
+
+ fe = vfe->dvb.frontend;
+
+ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
+ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111))
+ fe = &dev->ts1.analog_fe;
+
+ if (fe && fe->ops.tuner_ops.set_analog_params) {
+ call_all(dev, core, s_std, dev->tvnorm);
+ fe->ops.tuner_ops.set_analog_params(fe, &params);
+ }
+ else
+ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(100);
+
+ /* I need to unmute audio here */
+ ctrl.value = 0;
+ cx23885_set_control(dev, &ctrl);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_set_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int ret;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1255:
+ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ ret = cx23885_set_freq_via_ops(dev, f);
+ break;
+ default:
+ ret = cx23885_set_freq(dev, f);
+ }
+
+ return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return cx23885_set_frequency(file, priv, f);
+}
+
+/* ----------------------------------------------------------- */
+
+static void cx23885_vid_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
+ dev->name, buf, buf->vb.i,
+ (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_video_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 mask, count;
+ int handled = 0;
+
+ mask = cx_read(VID_A_INT_MSK);
+ if (0 == (status & mask))
+ return handled;
+
+ cx_write(VID_A_INT_STAT, status);
+
+ /* risc op code error, fifo overflow or line sync detection error */
+ if ((status & VID_BC_MSK_OPC_ERR) ||
+ (status & VID_BC_MSK_SYNC) ||
+ (status & VID_BC_MSK_OF)) {
+
+ if (status & VID_BC_MSK_OPC_ERR) {
+ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
+ VID_BC_MSK_OPC_ERR);
+ printk(KERN_WARNING "%s: video risc op code error\n",
+ dev->name);
+ cx23885_sram_channel_dump(dev,
+ &dev->sram_channels[SRAM_CH01]);
+ }
+
+ if (status & VID_BC_MSK_SYNC)
+ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
+ "video lines miss-match\n",
+ VID_BC_MSK_SYNC);
+
+ if (status & VID_BC_MSK_OF)
+ dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
+ VID_BC_MSK_OF);
+
+ }
+
+ /* Video */
+ if (status & VID_BC_MSK_RISCI1) {
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ if (status & VID_BC_MSK_RISCI2) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_video_queue(dev, &dev->vidq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ /* Allow the VBI framework to process it's payload */
+ handled += cx23885_vbi_irq(dev, status);
+
+ return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct v4l2_file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_chip_ident = cx23885_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx23885_g_register,
+ .vidioc_s_register = cx23885_s_register,
+#endif
+ .vidioc_enumaudio = vidioc_enum_audinput,
+ .vidioc_g_audio = vidioc_g_audinput,
+ .vidioc_s_audio = vidioc_s_audinput,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+ .name = "cx23885-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+};
+
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+ cx23885_irq_remove(dev, 0x01);
+
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+ }
+ if (dev->video_dev) {
+ if (video_is_registered(dev->video_dev))
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
+ }
+
+ if (dev->audio_dev)
+ cx23885_audio_unregister(dev);
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+ int err;
+
+ dprintk(1, "%s()\n", __func__);
+ spin_lock_init(&dev->slock);
+
+ /* Initialize VBI template */
+ memcpy(&cx23885_vbi_template, &cx23885_video_template,
+ sizeof(cx23885_vbi_template));
+ strcpy(cx23885_vbi_template.name, "cx23885-vbi");
+
+ dev->tvnorm = cx23885_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx23885_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
+ VID_A_DMA_CTL, 0x11, 0x00);
+
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx23885_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+ VID_A_DMA_CTL, 0x22, 0x00);
+
+ cx23885_irq_add_enable(dev, 0x01);
+
+ if ((TUNER_ABSENT != dev->tuner_type) &&
+ ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
+ struct v4l2_subdev *sd = NULL;
+
+ if (dev->tuner_addr)
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
+ "tuner", dev->tuner_addr, NULL);
+ else
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_bus[dev->tuner_bus].i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
+ if (sd) {
+ struct tuner_setup tun_setup;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = v4l2_i2c_subdev_addr(sd);
+ tun_setup.tuner_callback = cx23885_tuner_callback;
+
+ v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
+
+ if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
+ struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = dev->tuner_type,
+ .priv = &ctrl
+ };
+ v4l2_subdev_call(sd, tuner, s_config, &cfg);
+ }
+ }
+ }
+
+ /* register Video device */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_video_template, "video");
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->video_dev));
+
+ /* register VBI device */
+ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_vbi_template, "vbi");
+ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register vbi device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ /* Register ALSA audio device */
+ dev->audio_dev = cx23885_audio_register(dev);
+
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, dev->tvnorm);
+ init_controls(dev);
+ cx23885_video_mux(dev, 0);
+ cx23885_audio_mux(dev, 0);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx23885_video_unregister(dev);
+ return err;
+}
+
diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h
new file mode 100644
index 000000000000..67f40d31450b
--- /dev/null
+++ b/drivers/media/pci/cx23885/cx23885.h
@@ -0,0 +1,654 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/kdev_t.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <media/rc-core.h>
+
+#include "btcx-risc.h"
+#include "cx23885-reg.h"
+#include "media/cx2341x.h"
+
+#include <linux/mutex.h>
+
+#define CX23885_VERSION "0.0.3"
+
+#define UNSET (-1U)
+
+#define CX23885_MAXBOARDS 8
+
+/* Max number of inputs by card */
+#define MAX_CX23885_INPUT 8
+#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
+
+#define CX23885_BOARD_NOAUTO UNSET
+#define CX23885_BOARD_UNKNOWN 0
+#define CX23885_BOARD_HAUPPAUGE_HVR1800lp 1
+#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
+#define CX23885_BOARD_HAUPPAUGE_HVR1250 3
+#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4
+#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
+#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
+#define CX23885_BOARD_HAUPPAUGE_HVR1200 7
+#define CX23885_BOARD_HAUPPAUGE_HVR1700 8
+#define CX23885_BOARD_HAUPPAUGE_HVR1400 9
+#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
+#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
+#define CX23885_BOARD_TBS_6920 14
+#define CX23885_BOARD_TEVII_S470 15
+#define CX23885_BOARD_DVBWORLD_2005 16
+#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17
+#define CX23885_BOARD_HAUPPAUGE_HVR1270 18
+#define CX23885_BOARD_HAUPPAUGE_HVR1275 19
+#define CX23885_BOARD_HAUPPAUGE_HVR1255 20
+#define CX23885_BOARD_HAUPPAUGE_HVR1210 21
+#define CX23885_BOARD_MYGICA_X8506 22
+#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
+#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
+#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
+#define CX23885_BOARD_HAUPPAUGE_HVR1290 26
+#define CX23885_BOARD_MYGICA_X8558PRO 27
+#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
+#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
+#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
+#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
+#define CX23885_BOARD_MPX885 32
+#define CX23885_BOARD_MYGICA_X8507 33
+#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
+#define CX23885_BOARD_TEVII_S471 35
+#define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36
+#define CX23885_BOARD_PROF_8000 37
+
+#define GPIO_0 0x00000001
+#define GPIO_1 0x00000002
+#define GPIO_2 0x00000004
+#define GPIO_3 0x00000008
+#define GPIO_4 0x00000010
+#define GPIO_5 0x00000020
+#define GPIO_6 0x00000040
+#define GPIO_7 0x00000080
+#define GPIO_8 0x00000100
+#define GPIO_9 0x00000200
+#define GPIO_10 0x00000400
+#define GPIO_11 0x00000800
+#define GPIO_12 0x00001000
+#define GPIO_13 0x00002000
+#define GPIO_14 0x00004000
+#define GPIO_15 0x00008000
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+#define CX23885_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+struct cx23885_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx23885_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx23885_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx23885_fh {
+ struct cx23885_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23885_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
+
+enum cx23885_itype {
+ CX23885_VMUX_COMPOSITE1 = 1,
+ CX23885_VMUX_COMPOSITE2,
+ CX23885_VMUX_COMPOSITE3,
+ CX23885_VMUX_COMPOSITE4,
+ CX23885_VMUX_SVIDEO,
+ CX23885_VMUX_COMPONENT,
+ CX23885_VMUX_TELEVISION,
+ CX23885_VMUX_CABLE,
+ CX23885_VMUX_DVB,
+ CX23885_VMUX_DEBUG,
+ CX23885_RADIO,
+};
+
+enum cx23885_src_sel_type {
+ CX23885_SRC_SEL_EXT_656_VIDEO = 0,
+ CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO
+};
+
+/* buffer for one video frame */
+struct cx23885_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* cx23885 specific */
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct cx23885_fmt *fmt;
+ u32 count;
+};
+
+struct cx23885_input {
+ enum cx23885_itype type;
+ unsigned int vmux;
+ unsigned int amux;
+ u32 gpio0, gpio1, gpio2, gpio3;
+};
+
+typedef enum {
+ CX23885_MPEG_UNDEFINED = 0,
+ CX23885_MPEG_DVB,
+ CX23885_ANALOG_VIDEO,
+ CX23885_MPEG_ENCODER,
+} port_t;
+
+struct cx23885_board {
+ char *name;
+ port_t porta, portb, portc;
+ int num_fds_portb, num_fds_portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ unsigned int tuner_bus;
+
+ /* Vendors can and do run the PCIe bridge at different
+ * clock rates, driven physically by crystals on the PCBs.
+ * The core has to accommodate this. This allows the user
+ * to add new boards with new frequencys. The value is
+ * expressed in Hz.
+ *
+ * The core framework will default this value based on
+ * current designs, but it can vary.
+ */
+ u32 clk_freq;
+ struct cx23885_input input[MAX_CX23885_INPUT];
+ int ci_type; /* for NetUP */
+ /* Force bottom field first during DMA (888 workaround) */
+ u32 force_bff;
+};
+
+struct cx23885_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+struct cx23885_i2c {
+ struct cx23885_dev *dev;
+
+ int nr;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* 885 registers used for raw addess */
+ u32 i2c_period;
+ u32 reg_ctrl;
+ u32 reg_stat;
+ u32 reg_addr;
+ u32 reg_rdata;
+ u32 reg_wdata;
+};
+
+struct cx23885_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+
+struct cx23885_tsport {
+ struct cx23885_dev *dev;
+
+ int nr;
+ int sram_chno;
+
+ struct videobuf_dvb_frontends frontends;
+
+ /* dma queues */
+ struct cx23885_dmaqueue mpegq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ int width;
+ int height;
+
+ spinlock_t slock;
+
+ /* registers */
+ u32 reg_gpcnt;
+ u32 reg_gpcnt_ctl;
+ u32 reg_dma_ctl;
+ u32 reg_lngth;
+ u32 reg_hw_sop_ctrl;
+ u32 reg_gen_ctrl;
+ u32 reg_bd_pkt_status;
+ u32 reg_sop_status;
+ u32 reg_fifo_ovfl_stat;
+ u32 reg_vld_misc;
+ u32 reg_ts_clk_en;
+ u32 reg_ts_int_msk;
+ u32 reg_ts_int_stat;
+ u32 reg_src_sel;
+
+ /* Default register vals */
+ int pci_irqmask;
+ u32 dma_ctl_val;
+ u32 ts_int_msk_val;
+ u32 gen_ctrl_val;
+ u32 ts_clk_en_val;
+ u32 src_sel_val;
+ u32 vld_misc_val;
+ u32 hw_sop_ctrl_val;
+
+ /* Allow a single tsport to have multiple frontends */
+ u32 num_frontends;
+ void (*gate_ctrl)(struct cx23885_tsport *port, int open);
+ void *port_priv;
+
+ /* Workaround for a temp dvb_frontend that the tuner can attached to */
+ struct dvb_frontend analog_fe;
+};
+
+struct cx23885_kernel_ir {
+ struct cx23885_dev *cx;
+ char *name;
+ char *phys;
+
+ struct rc_dev *rc;
+};
+
+struct cx23885_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx23885_audio_dev {
+ struct cx23885_dev *dev;
+
+ struct pci_dev *pci;
+
+ struct snd_card *card;
+
+ spinlock_t lock;
+
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx23885_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+
+struct cx23885_dev {
+ atomic_t refcount;
+ struct v4l2_device v4l2_dev;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+ unsigned char pci_rev, pci_lat;
+ int pci_bus, pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ int pci_irqmask;
+ spinlock_t pci_irqmask_lock; /* protects mask reg too */
+ int hwrevision;
+
+ /* This valud is board specific and is used to configure the
+ * AV core so we see nice clean and stable video and audio. */
+ u32 clk_freq;
+
+ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
+ struct cx23885_i2c i2c_bus[3];
+
+ int nr;
+ struct mutex lock;
+ struct mutex gpio_lock;
+
+ /* board details */
+ unsigned int board;
+ char name[32];
+
+ struct cx23885_tsport ts1, ts2;
+
+ /* sram configuration */
+ struct sram_channel *sram_channels;
+
+ enum {
+ CX23885_BRIDGE_UNDEFINED = 0,
+ CX23885_BRIDGE_885 = 885,
+ CX23885_BRIDGE_887 = 887,
+ CX23885_BRIDGE_888 = 888,
+ } bridge;
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ unsigned int audinput; /* Selectable audio input */
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int tuner_bus;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+ struct v4l2_subdev *sd_cx25840;
+ struct work_struct cx25840_work;
+
+ /* Infrared */
+ struct v4l2_subdev *sd_ir;
+ struct work_struct ir_rx_work;
+ unsigned long ir_rx_notifications;
+ struct work_struct ir_tx_work;
+ unsigned long ir_tx_notifications;
+
+ struct cx23885_kernel_ir *kernel_ir;
+ atomic_t ir_input_stopping;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ struct cx23885_dmaqueue vidq;
+ struct cx23885_dmaqueue vbiq;
+ spinlock_t slock;
+
+ /* MPEG Encoder ONLY settings */
+ u32 cx23417_mailbox;
+ struct cx2341x_mpeg_params mpeg_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+ struct cx23885_tvnorm encodernorm;
+
+ /* Analog raw audio */
+ struct cx23885_audio_dev *audio_dev;
+
+};
+
+static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev);
+}
+
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
+
+#define CX23885_HW_888_IR (1 << 0)
+#define CX23885_HW_AV_CORE (1 << 1)
+
+#define call_hw(dev, grpid, o, f, args...) \
+ v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
+
+extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
+
+#define SRAM_CH01 0 /* Video A */
+#define SRAM_CH02 1 /* VBI A */
+#define SRAM_CH03 2 /* Video B */
+#define SRAM_CH04 3 /* Transport via B */
+#define SRAM_CH05 4 /* VBI B */
+#define SRAM_CH06 5 /* Video C */
+#define SRAM_CH07 6 /* Transport via C */
+#define SRAM_CH08 7 /* Audio Internal A */
+#define SRAM_CH09 8 /* Audio Internal B */
+#define SRAM_CH10 9 /* Audio External */
+#define SRAM_CH11 10 /* COMB_3D_N */
+#define SRAM_CH12 11 /* Comb 3D N1 */
+#define SRAM_CH13 12 /* Comb 3D N2 */
+#define SRAM_CH14 13 /* MOE Vid */
+#define SRAM_CH15 14 /* MOE RSLT */
+
+struct sram_channel {
+ char *name;
+ u32 cmds_start;
+ u32 ctrl_start;
+ u32 cdt;
+ u32 fifo_start;
+ u32 fifo_size;
+ u32 ptr1_reg;
+ u32 ptr2_reg;
+ u32 cnt1_reg;
+ u32 cnt2_reg;
+ u32 jumponly;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
+#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
+
+#define cx_andor(reg, mask, value) \
+ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+((reg)>>2))
+
+#define cx_set(reg, bit) cx_andor((reg), (bit), (bit))
+#define cx_clear(reg, bit) cx_andor((reg), (bit), 0)
+
+/* ----------------------------------------------------------- */
+/* cx23885-core.c */
+
+extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+
+extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch);
+
+extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc, struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port);
+
+extern int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q);
+
+extern void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count);
+
+extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
+ int asoutput);
+
+extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
+extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
+
+/* ----------------------------------------------------------- */
+/* cx23885-cards.c */
+extern struct cx23885_board cx23885_boards[];
+extern const unsigned int cx23885_bcount;
+
+extern struct cx23885_subid cx23885_subids[];
+extern const unsigned int cx23885_idcount;
+
+extern int cx23885_tuner_callback(void *priv, int component,
+ int command, int arg);
+extern void cx23885_card_list(struct cx23885_dev *dev);
+extern int cx23885_ir_init(struct cx23885_dev *dev);
+extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
+extern void cx23885_ir_fini(struct cx23885_dev *dev);
+extern void cx23885_gpio_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup(struct cx23885_dev *dev);
+extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
+
+extern int cx23885_dvb_register(struct cx23885_tsport *port);
+extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
+
+extern int cx23885_buf_prepare(struct videobuf_queue *q,
+ struct cx23885_tsport *port,
+ struct cx23885_buffer *buf,
+ enum v4l2_field field);
+extern void cx23885_buf_queue(struct cx23885_tsport *port,
+ struct cx23885_buffer *buf);
+extern void cx23885_free_buffer(struct videobuf_queue *q,
+ struct cx23885_buffer *buf);
+
+/* ----------------------------------------------------------- */
+/* cx23885-video.c */
+/* Video */
+extern int cx23885_video_register(struct cx23885_dev *dev);
+extern void cx23885_video_unregister(struct cx23885_dev *dev);
+extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+extern void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count);
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
+int cx23885_set_input(struct file *file, void *priv, unsigned int i);
+int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
+int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
+
+/* ----------------------------------------------------------- */
+/* cx23885-vbi.c */
+extern int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern void cx23885_vbi_timeout(unsigned long data);
+extern struct videobuf_queue_ops cx23885_vbi_qops;
+extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q);
+extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
+
+/* cx23885-i2c.c */
+extern int cx23885_i2c_register(struct cx23885_i2c *bus);
+extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
+extern void cx23885_av_clk(struct cx23885_dev *dev, int enable);
+
+/* ----------------------------------------------------------- */
+/* cx23885-417.c */
+extern int cx23885_417_register(struct cx23885_dev *dev);
+extern void cx23885_417_unregister(struct cx23885_dev *dev);
+extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status);
+extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
+extern void cx23885_mc417_init(struct cx23885_dev *dev);
+extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
+extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
+extern int mc417_register_read(struct cx23885_dev *dev,
+ u16 address, u32 *value);
+extern int mc417_register_write(struct cx23885_dev *dev,
+ u16 address, u32 value);
+extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
+extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
+
+/* ----------------------------------------------------------- */
+/* cx23885-alsa.c */
+extern struct cx23885_audio_dev *cx23885_audio_register(
+ struct cx23885_dev *dev);
+extern void cx23885_audio_unregister(struct cx23885_dev *dev);
+extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
+extern int cx23885_risc_databuffer(struct pci_dev *pci,
+ struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int bpl,
+ unsigned int lines,
+ unsigned int lpi);
+
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static inline unsigned int norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+static inline unsigned int norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+static inline unsigned int norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c
index c2bc39c58f82..c2bc39c58f82 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/pci/cx23885/cx23888-ir.c
diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/pci/cx23885/cx23888-ir.h
index d2de41caaf1d..d2de41caaf1d 100644
--- a/drivers/media/video/cx23885/cx23888-ir.h
+++ b/drivers/media/pci/cx23885/cx23888-ir.h
diff --git a/drivers/media/video/cx23885/netup-eeprom.c b/drivers/media/pci/cx23885/netup-eeprom.c
index 98a48f500684..98a48f500684 100644
--- a/drivers/media/video/cx23885/netup-eeprom.c
+++ b/drivers/media/pci/cx23885/netup-eeprom.c
diff --git a/drivers/media/video/cx23885/netup-eeprom.h b/drivers/media/pci/cx23885/netup-eeprom.h
index 13926e18feba..13926e18feba 100644
--- a/drivers/media/video/cx23885/netup-eeprom.h
+++ b/drivers/media/pci/cx23885/netup-eeprom.h
diff --git a/drivers/media/video/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c
index f4893e69cd89..f4893e69cd89 100644
--- a/drivers/media/video/cx23885/netup-init.c
+++ b/drivers/media/pci/cx23885/netup-init.c
diff --git a/drivers/media/video/cx23885/netup-init.h b/drivers/media/pci/cx23885/netup-init.h
index d26ae4b1590e..d26ae4b1590e 100644
--- a/drivers/media/video/cx23885/netup-init.h
+++ b/drivers/media/pci/cx23885/netup-init.h
diff --git a/drivers/media/video/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig
index 5f6b54213713..5f6b54213713 100644
--- a/drivers/media/video/cx25821/Kconfig
+++ b/drivers/media/pci/cx25821/Kconfig
diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile
new file mode 100644
index 000000000000..5bf3ea4c1556
--- /dev/null
+++ b/drivers/media/pci/cx25821/Makefile
@@ -0,0 +1,13 @@
+cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \
+ cx25821-gpio.o cx25821-medusa-video.o \
+ cx25821-video.o cx25821-video-upstream.o \
+ cx25821-video-upstream-ch2.o \
+ cx25821-audio-upstream.o
+
+obj-$(CONFIG_VIDEO_CX25821) += cx25821.o
+obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c
index 1858a45dd081..1858a45dd081 100644
--- a/drivers/media/video/cx25821/cx25821-alsa.c
+++ b/drivers/media/pci/cx25821/cx25821-alsa.c
diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
index 8b2a99975c23..8b2a99975c23 100644
--- a/drivers/media/video/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c
diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.h b/drivers/media/pci/cx25821/cx25821-audio-upstream.h
index af2ae7c5815a..af2ae7c5815a 100644
--- a/drivers/media/video/cx25821/cx25821-audio-upstream.h
+++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.h
diff --git a/drivers/media/video/cx25821/cx25821-audio.h b/drivers/media/pci/cx25821/cx25821-audio.h
index 1fc2d24f5110..1fc2d24f5110 100644
--- a/drivers/media/video/cx25821/cx25821-audio.h
+++ b/drivers/media/pci/cx25821/cx25821-audio.h
diff --git a/drivers/media/video/cx25821/cx25821-biffuncs.h b/drivers/media/pci/cx25821/cx25821-biffuncs.h
index 9326a7c729ec..9326a7c729ec 100644
--- a/drivers/media/video/cx25821/cx25821-biffuncs.h
+++ b/drivers/media/pci/cx25821/cx25821-biffuncs.h
diff --git a/drivers/media/video/cx25821/cx25821-cards.c b/drivers/media/pci/cx25821/cx25821-cards.c
index 99988c988095..99988c988095 100644
--- a/drivers/media/video/cx25821/cx25821-cards.c
+++ b/drivers/media/pci/cx25821/cx25821-cards.c
diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c
index f11f6f07e915..f11f6f07e915 100644
--- a/drivers/media/video/cx25821/cx25821-core.c
+++ b/drivers/media/pci/cx25821/cx25821-core.c
diff --git a/drivers/media/video/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c
index 29e43b03c85e..29e43b03c85e 100644
--- a/drivers/media/video/cx25821/cx25821-gpio.c
+++ b/drivers/media/pci/cx25821/cx25821-gpio.c
diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c
index 9844549764c9..9844549764c9 100644
--- a/drivers/media/video/cx25821/cx25821-i2c.c
+++ b/drivers/media/pci/cx25821/cx25821-i2c.c
diff --git a/drivers/media/video/cx25821/cx25821-medusa-defines.h b/drivers/media/pci/cx25821/cx25821-medusa-defines.h
index 7a9e6470ba22..7a9e6470ba22 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-defines.h
+++ b/drivers/media/pci/cx25821/cx25821-medusa-defines.h
diff --git a/drivers/media/video/cx25821/cx25821-medusa-reg.h b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
index c98ac946b277..c98ac946b277 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-reg.h
+++ b/drivers/media/pci/cx25821/cx25821-medusa-reg.h
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/pci/cx25821/cx25821-medusa-video.c
index 6a92e5c70c2a..6a92e5c70c2a 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/pci/cx25821/cx25821-medusa-video.c
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.h b/drivers/media/pci/cx25821/cx25821-medusa-video.h
index 6175e0961855..6175e0961855 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-video.h
+++ b/drivers/media/pci/cx25821/cx25821-medusa-video.h
diff --git a/drivers/media/video/cx25821/cx25821-reg.h b/drivers/media/pci/cx25821/cx25821-reg.h
index a3fc25a4dc0b..a3fc25a4dc0b 100644
--- a/drivers/media/video/cx25821/cx25821-reg.h
+++ b/drivers/media/pci/cx25821/cx25821-reg.h
diff --git a/drivers/media/video/cx25821/cx25821-sram.h b/drivers/media/pci/cx25821/cx25821-sram.h
index 5f05d153bc4d..5f05d153bc4d 100644
--- a/drivers/media/video/cx25821/cx25821-sram.h
+++ b/drivers/media/pci/cx25821/cx25821-sram.h
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
index c8c94fbf5d8d..c8c94fbf5d8d 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h
index d42dab59b663..d42dab59b663 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.h
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c
index 52c13e0b6492..52c13e0b6492 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.h b/drivers/media/pci/cx25821/cx25821-video-upstream.h
index 268ec8aa6a61..268ec8aa6a61 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream.h
+++ b/drivers/media/pci/cx25821/cx25821-video-upstream.h
diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c
new file mode 100644
index 000000000000..0a80245165d0
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video.c
@@ -0,0 +1,1990 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ * Parts adapted/taken from Eduardo Moscoso Rubino
+ * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "cx25821-video.h"
+
+MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
+MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
+MODULE_LICENSE("GPL");
+
+static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug = VIDEO_DEBUG;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num);
+
+static const struct v4l2_file_operations video_fops;
+static const struct v4l2_ioctl_ops video_ioctl_ops;
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+struct cx25821_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:1:1, packed, Y41P",
+ .fourcc = V4L2_PIX_FMT_Y41P,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:0, YUV",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+int cx25821_get_format_size(void)
+{
+ return ARRAY_SIZE(formats);
+}
+
+struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P)
+ return formats + 1;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats + i;
+
+ pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc);
+ return NULL;
+}
+
+void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
+ u32 count)
+{
+ struct cx25821_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active)) {
+ dprintk(1, "bc=%d (=0: active empty)\n", bc);
+ break;
+ }
+
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+
+ /* count comes from the hw and it is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ do_gettimeofday(&buf->vb.ts);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+
+ if (list_empty(&q->active))
+ del_timer(&q->timeout);
+ else
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ if (bc != 1)
+ pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __func__, (unsigned int)norm, v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ /* Tell the internal A/V decoder */
+ cx25821_call_all(dev, core, s_std, norm);
+
+ return 0;
+}
+#endif
+
+struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __func__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type,
+ cx25821_boards[dev->board].name);
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+/*
+static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX25821_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx25821_ctls[i].v;
+ return 0;
+}
+*/
+
+/* resource management */
+int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __func__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->channels[fh->channel_id].resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->channels[fh->channel_id].resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit)
+{
+ return fh->dev->channels[fh->channel_id].resources & bit;
+}
+
+void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->channels[fh->channel_id].resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input)
+{
+ struct v4l2_routing route;
+ memset(&route, 0, sizeof(route));
+
+ dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0,
+ INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ route.input = INPUT(input)->vmux;
+
+ /* Tell the internal A/V decoder */
+ cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0);
+
+ return 0;
+}
+
+int cx25821_start_video_dma(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct cx25821_buffer *buf,
+ struct sram_channel *channel)
+{
+ int tmp = 0;
+
+ /* setup fifo + format */
+ cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma);
+
+ /* reset counter */
+ cx_write(channel->gpcnt_ctl, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
+ cx_set(channel->int_msk, 0x11);
+
+ /* start dma */
+ cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */
+
+ /* make sure upstream setting if any is reversed */
+ tmp = cx_read(VID_CH_MODE_SEL);
+ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
+
+ return 0;
+}
+
+int cx25821_restart_video_queue(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct sram_channel *channel)
+{
+ struct cx25821_buffer *buf, *prev;
+ struct list_head *item;
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+
+ cx25821_start_video_dma(dev, q, buf, channel);
+
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx25821_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+
+ buf = list_entry(q->queued.next, struct cx25821_buffer,
+ vb.queue);
+
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx25821_start_video_dma(dev, q, buf, channel);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+void cx25821_vid_timeout(unsigned long data)
+{
+ struct cx25821_data *timeout_data = (struct cx25821_data *)data;
+ struct cx25821_dev *dev = timeout_data->dev;
+ struct sram_channel *channel = timeout_data->channel;
+ struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
+ struct cx25821_buffer *buf;
+ unsigned long flags;
+
+ /* cx25821_sram_channel_dump(dev, channel); */
+ cx_clear(channel->dma_ctl, 0x11);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx25821_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ }
+
+ cx25821_restart_video_queue(dev, q, channel);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
+{
+ u32 count = 0;
+ int handled = 0;
+ u32 mask;
+ struct sram_channel *channel = dev->channels[chan_num].sram_channels;
+
+ mask = cx_read(channel->int_msk);
+ if (0 == (status & mask))
+ return handled;
+
+ cx_write(channel->int_stat, status);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ pr_warn("%s, %s: video risc op code error\n",
+ dev->name, channel->name);
+ cx_clear(channel->dma_ctl, 0x11);
+ cx25821_sram_channel_dump(dev, channel);
+ }
+
+ /* risc1 y */
+ if (status & FLD_VID_DST_RISC1) {
+ spin_lock(&dev->slock);
+ count = cx_read(channel->gpcnt);
+ cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq,
+ count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx25821_restart_video_queue(dev,
+ &dev->channels[channel->i].vidq, channel);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+ return handled;
+}
+
+void cx25821_videoioctl_unregister(struct cx25821_dev *dev)
+{
+ if (dev->ioctl_dev) {
+ if (video_is_registered(dev->ioctl_dev))
+ video_unregister_device(dev->ioctl_dev);
+ else
+ video_device_release(dev->ioctl_dev);
+
+ dev->ioctl_dev = NULL;
+ }
+}
+
+void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
+{
+ cx_clear(PCI_INT_MSK, 1);
+
+ if (dev->channels[chan_num].video_dev) {
+ if (video_is_registered(dev->channels[chan_num].video_dev))
+ video_unregister_device(
+ dev->channels[chan_num].video_dev);
+ else
+ video_device_release(
+ dev->channels[chan_num].video_dev);
+
+ dev->channels[chan_num].video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci,
+ &dev->channels[chan_num].vidq.stopper);
+
+ pr_warn("device %d released!\n", chan_num);
+ }
+
+}
+
+int cx25821_video_register(struct cx25821_dev *dev)
+{
+ int err;
+ int i;
+
+ struct video_device cx25821_video_device = {
+ .name = "cx25821-video",
+ .fops = &video_fops,
+ .minor = -1,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+ };
+
+ spin_lock_init(&dev->slock);
+
+ for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
+ cx25821_init_controls(dev, i);
+
+ cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper,
+ dev->channels[i].sram_channels->dma_ctl, 0x11, 0);
+
+ dev->channels[i].sram_channels = &cx25821_sram_channels[i];
+ dev->channels[i].video_dev = NULL;
+ dev->channels[i].resources = 0;
+
+ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
+
+ INIT_LIST_HEAD(&dev->channels[i].vidq.active);
+ INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
+
+ dev->channels[i].timeout_data.dev = dev;
+ dev->channels[i].timeout_data.channel =
+ &cx25821_sram_channels[i];
+ dev->channels[i].vidq.timeout.function = cx25821_vid_timeout;
+ dev->channels[i].vidq.timeout.data =
+ (unsigned long)&dev->channels[i].timeout_data;
+ init_timer(&dev->channels[i].vidq.timeout);
+
+ /* register v4l devices */
+ dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci,
+ &cx25821_video_device, "video");
+
+ err = video_register_device(dev->channels[i].video_dev,
+ VFL_TYPE_GRABBER, video_nr[dev->nr]);
+
+ if (err < 0)
+ goto fail_unreg;
+
+ }
+
+ /* set PCI interrupt */
+ cx_set(PCI_INT_MSK, 0xff);
+
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+#ifdef TUNER_FLAG
+ dev->tvnorm = cx25821_video_device.current_norm;
+ cx25821_set_tvnorm(dev, dev->tvnorm);
+#endif
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx25821_video_unregister(dev, i);
+ return err;
+}
+
+int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx25821_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+
+ if (0 == *count)
+ *count = 32;
+
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx25821_fh *fh = q->priv_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+ int bpl_local = LINE_SIZE_D1;
+ int channel_opened = fh->channel_id;
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > 720 ||
+ fh->height < 32 || fh->height > 576)
+ return -EINVAL;
+
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height || buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc) {
+ printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n"));
+ goto fail;
+ }
+ }
+
+ dprintk(1, "init_buffer=%d\n", init_buffer);
+
+ if (init_buffer) {
+
+ channel_opened = dev->channel_opened;
+ if (channel_opened < 0 || channel_opened > 7)
+ channel_opened = 7;
+
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411)
+ buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
+ else
+ buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
+
+ if (dev->channels[channel_opened].pixel_formats ==
+ PIXEL_FRMT_411) {
+ bpl_local = buf->bpl;
+ } else {
+ bpl_local = buf->bpl; /* Default */
+
+ if (channel_opened >= 0 && channel_opened <= 7) {
+ if (dev->channels[channel_opened]
+ .use_cif_resolution) {
+ if (dev->tvnorm & V4L2_STD_PAL_BG ||
+ dev->tvnorm & V4L2_STD_PAL_DK)
+ bpl_local = 352 << 1;
+ else
+ bpl_local = dev->channels[
+ channel_opened].
+ cif_width << 1;
+ }
+ }
+ }
+
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ /* All other formats are top field first */
+ line0_offset = 0;
+ dprintk(1, "top field first\n");
+
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ bpl_local, bpl_local, bpl_local,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0, buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx25821_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0, buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth,
+ fh->fmt->name, (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+
+ return 0;
+
+fail:
+ cx25821_free_buffer(q, buf);
+ return rc;
+}
+
+void cx25821_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+
+ cx25821_free_buffer(q, buf);
+}
+
+struct videobuf_queue *get_queue(struct cx25821_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &fh->vidq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+int cx25821_get_resource(struct cx25821_fh *fh, int resource)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return resource;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx25821_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx25821_buffer *buf =
+ container_of(vb, struct cx25821_buffer, vb);
+ struct cx25821_buffer *prev;
+ struct cx25821_fh *fh = vq->priv_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
+ buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx25821_start_video_dma(dev, q, buf,
+ dev->channels[fh->channel_id].sram_channels);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
+ buf, buf->vb.i, buf->count, q->count);
+ } else {
+ prev = list_entry(q->active.prev, struct cx25821_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width
+ && prev->vb.height == buf->vb.height
+ && prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n",
+ buf, buf->vb.i, buf->count);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
+ buf->vb.i);
+ }
+ }
+
+ if (list_empty(&q->active))
+ dprintk(2, "active queue empty!\n");
+}
+
+static struct videobuf_queue_ops cx25821_video_qops = {
+ .buf_setup = cx25821_buffer_setup,
+ .buf_prepare = cx25821_buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = cx25821_buffer_release,
+};
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx25821_dev *h, *dev = video_drvdata(file);
+ struct cx25821_fh *fh;
+ struct list_head *list;
+ int minor = video_devdata(file)->minor;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ u32 pix_format;
+ int ch_id = 0;
+ int i;
+
+ dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev),
+ v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ mutex_lock(&cx25821_devlist_mutex);
+
+ list_for_each(list, &cx25821_devlist)
+ {
+ h = list_entry(list, struct cx25821_dev, devlist);
+
+ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
+ if (h->channels[i].video_dev &&
+ h->channels[i].video_dev->minor == minor) {
+ dev = h;
+ ch_id = i;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ }
+ }
+
+ if (NULL == dev) {
+ mutex_unlock(&cx25821_devlist_mutex);
+ kfree(fh);
+ return -ENODEV;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = type;
+ fh->width = 720;
+ fh->channel_id = ch_id;
+
+ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
+ fh->height = 576;
+ else
+ fh->height = 480;
+
+ dev->channel_opened = fh->channel_id;
+ if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411)
+ pix_format = V4L2_PIX_FMT_Y41P;
+ else
+ pix_format = V4L2_PIX_FMT_YUYV;
+ fh->fmt = cx25821_format_by_fourcc(pix_format);
+
+ v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev,
+ &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer),
+ fh, NULL);
+
+ dprintk(1, "post videobuf_queue_init()\n");
+ mutex_unlock(&cx25821_devlist_mutex);
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user * data, size_t count,
+ loff_t *ppos)
+{
+ struct cx25821_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
+ return -EBUSY;
+
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_buffer *buf;
+
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx25821_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx25821_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) {
+ if (buf->vb.state == VIDEOBUF_DONE) {
+ struct cx25821_dev *dev = fh->dev;
+
+ if (dev && dev->channels[fh->channel_id]
+ .use_cif_resolution) {
+ u8 cam_id = *((char *)buf->vb.baddr + 3);
+ memcpy((char *)buf->vb.baddr,
+ (char *)buf->vb.baddr + (fh->width * 2),
+ (fh->width * 2));
+ *((char *)buf->vb.baddr + 3) = cam_id;
+ }
+ }
+
+ return POLLIN | POLLRDNORM;
+ }
+
+ return 0;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+
+ /* stop the risc engine and fifo */
+ cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
+
+ /* stop video capture */
+ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
+ videobuf_queue_cancel(&fh->vidq);
+ cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
+ }
+
+ if (fh->vidq.read_buf) {
+ cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+
+ v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh,
+ RESOURCE_VIDEO0))))
+ return -EBUSY;
+
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+ int err, res;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ cx25821_res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int err;
+ int pix_format = PIXEL_FRMT_422;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ dprintk(2, "%s()\n", __func__);
+ err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+
+ fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->vidq.field = f->fmt.pix.field;
+
+ /* check if width and height is valid based on set standard */
+ if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
+ fh->width = f->fmt.pix.width;
+
+ if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
+ fh->height = f->fmt.pix.height;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
+ pix_format = PIXEL_FRMT_411;
+ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
+ pix_format = PIXEL_FRMT_422;
+ else
+ return -EINVAL;
+
+ cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
+
+ /* check if cif resolution */
+ if (fh->width == 320 || fh->width == 352)
+ dev->channels[fh->channel_id].use_cif_resolution = 1;
+ else
+ dev->channels[fh->channel_id].use_cif_resolution = 0;
+
+ dev->channels[fh->channel_id].cif_width = fh->width;
+ medusa_set_resolution(dev, fh->width, SRAM_CH00);
+
+ dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width,
+ fh->height, fh->vidq.field);
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int ret_val = 0;
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
+
+ p->sequence = dev->channels[fh->channel_id].vidq.count;
+
+ return ret_val;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ char name[32 + 2];
+
+ struct sram_channel *sram_ch = dev->channels[fh->channel_id]
+ .sram_channels;
+ u32 tmp = 0;
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ pr_info("%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ cx25821_call_all(dev, core, log_status);
+ tmp = cx_read(sram_ch->dma_ctl);
+ pr_info("Video input 0 is %s\n",
+ (tmp & 0x11) ? "streaming" : "stopped");
+ pr_info("%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ return cx25821_set_control(dev, ctl, fh->channel_id);
+}
+
+/* VIDEO IOCTLS */
+int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx25821_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = 720;
+ maxh = 576;
+
+ if (V4L2_FIELD_ANY == field) {
+ if (f->fmt.pix.height > maxh / 2)
+ field = V4L2_FIELD_INTERLACED;
+ else
+ field = V4L2_FIELD_TOP;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+int cx25821_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx25821");
+ strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX25821_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name, sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_reqbufs(get_queue(fh), p);
+}
+
+int cx25821_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_querybuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx25821_fh *fh = priv;
+ return videobuf_qbuf(get_queue(fh), p);
+}
+
+int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+ struct cx25821_fh *fh = f;
+
+ *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct cx25821_fh *fh = f;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
+
+ return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio,
+ prio);
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ if (dev->tvnorm == *tvnorms)
+ return 0;
+
+ mutex_lock(&dev->lock);
+ cx25821_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ medusa_set_videostandard(dev);
+
+ return 0;
+}
+#endif
+
+int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
+{
+ static const char * const iname[] = {
+ [CX25821_VMUX_COMPOSITE] = "Composite",
+ [CX25821_VMUX_SVIDEO] = "S-Video",
+ [CX25821_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __func__);
+
+ n = i->index;
+ if (n >= 2)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ i->std = CX25821_NORMS;
+ return 0;
+}
+
+int cx25821_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __func__);
+ return cx25821_enum_input(dev, i);
+}
+
+int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s(): returns %d\n", __func__, *i);
+ return 0;
+}
+
+int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ int err;
+
+ dprintk(1, "%s(%d)\n", __func__, i);
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ if (i >= CX25821_NR_INPUT) {
+ dprintk(1, "%s(): -EINVAL\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ cx25821_video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev = fh->dev;
+
+ f->frequency = dev->freq;
+
+ cx25821_call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f)
+{
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ cx25821_call_all(dev, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx25821_fh *fh = priv;
+ struct cx25821_dev *dev;
+ int err;
+
+ if (fh) {
+ dev = fh->dev;
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ } else {
+ pr_err("Invalid fh pointer!\n");
+ return -EINVAL;
+ }
+
+ return cx25821_set_freq(dev, f);
+}
+#endif
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int cx25821_vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ cx25821_call_all(dev, core, g_register, reg);
+
+ return 0;
+}
+
+int cx25821_vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+ cx25821_call_all(dev, core, s_register, reg);
+
+ return 0;
+}
+
+#endif
+
+#ifdef TUNER_FLAG
+int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+
+ t->signal = 0xffff; /* LOCKED */
+ return 0;
+}
+
+int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+
+ dprintk(1, "%s()\n", __func__);
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ return 0;
+}
+
+#endif
+/*****************************************************************************/
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct v4l2_queryctrl cx25821_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 6200,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 10000,
+ .step = 1,
+ .default_value = 5000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls);
+
+static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].id == qctrl->id)
+ break;
+ if (i == CX25821_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx25821_ctls[i];
+ return 0;
+}
+
+int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ return cx25821_ctrl_query(qctrl);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CX25821_CTLS; i++)
+ if (cx25821_ctls[i].id == id)
+ return cx25821_ctls + i;
+ return NULL;
+}
+
+int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+
+ const struct v4l2_queryctrl *ctrl;
+
+ ctrl = ctrl_by_id(ctl->id);
+
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctl->value = dev->channels[fh->channel_id].ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ ctl->value = dev->channels[fh->channel_id].ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctl->value = dev->channels[fh->channel_id].ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctl->value = dev->channels[fh->channel_id].ctl_saturation;
+ break;
+ }
+ return 0;
+}
+
+int cx25821_set_control(struct cx25821_dev *dev,
+ struct v4l2_control *ctl, int chan_num)
+{
+ int err;
+ const struct v4l2_queryctrl *ctrl;
+
+ err = -EINVAL;
+
+ ctrl = ctrl_by_id(ctl->id);
+
+ if (NULL == ctrl)
+ return err;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (ctl->value < ctrl->minimum)
+ ctl->value = ctrl->minimum;
+ if (ctl->value > ctrl->maximum)
+ ctl->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */ ;
+ }
+
+ switch (ctl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->channels[chan_num].ctl_bright = ctl->value;
+ medusa_set_brightness(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_HUE:
+ dev->channels[chan_num].ctl_hue = ctl->value;
+ medusa_set_hue(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->channels[chan_num].ctl_contrast = ctl->value;
+ medusa_set_contrast(dev, ctl->value, chan_num);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->channels[chan_num].ctl_saturation = ctl->value;
+ medusa_set_saturation(dev, ctl->value, chan_num);
+ break;
+ }
+
+ err = 0;
+
+ return err;
+}
+
+static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num)
+{
+ struct v4l2_control ctrl;
+ int i;
+ for (i = 0; i < CX25821_CTLS; i++) {
+ ctrl.id = cx25821_ctls[i].id;
+ ctrl.value = cx25821_ctls[i].default_value;
+
+ cx25821_set_control(dev, &ctrl, chan_num);
+ }
+}
+
+int cx25821_vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480;
+ cropcap->pixelaspect.numerator =
+ dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
+ cropcap->pixelaspect.denominator =
+ dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop)
+{
+ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
+ struct cx25821_fh *fh = priv;
+ int err;
+
+ if (fh) {
+ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
+ fh->prio);
+ if (0 != err)
+ return err;
+ }
+ /* cx25821_vidioc_s_crop not supported */
+ return -EINVAL;
+}
+
+int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ /* cx25821_vidioc_g_crop not supported */
+ return -EINVAL;
+}
+
+int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm)
+{
+ /* medusa does not support video standard sensing of current input */
+ *norm = CX25821_NORMS;
+
+ return 0;
+}
+
+int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm)
+{
+ if (tvnorm == V4L2_STD_PAL_BG) {
+ if (width == 352 || width == 720)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (tvnorm == V4L2_STD_NTSC_M) {
+ if (width == 320 || width == 352 || width == 720)
+ return 1;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm)
+{
+ if (tvnorm == V4L2_STD_PAL_BG) {
+ if (height == 576 || height == 288)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (tvnorm == V4L2_STD_NTSC_M) {
+ if (height == 480 || height == 240)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream9(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
+
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch1(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch1(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream10(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
+ return 0;
+
+ dev->input_filename_ch2 = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname_ch2 = data_from_user->vid_stdname;
+ dev->pixel_format_ch2 = data_from_user->pixel_format;
+ dev->channel_select_ch2 = data_from_user->channel_select;
+ dev->command_ch2 = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_VIDEO:
+ cx25821_start_upstream_video_ch2(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_VIDEO:
+ cx25821_stop_upstream_video_ch2(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_upstream11(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ int command = 0;
+ struct upstream_user_struct *data_from_user;
+
+ data_from_user = (struct upstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO)
+ return 0;
+
+ dev->input_filename = data_from_user->input_filename;
+ dev->input_audiofilename = data_from_user->input_filename;
+ dev->vid_stdname = data_from_user->vid_stdname;
+ dev->pixel_format = data_from_user->pixel_format;
+ dev->channel_select = data_from_user->channel_select;
+ dev->command = data_from_user->command;
+
+ switch (command) {
+ case UPSTREAM_START_AUDIO:
+ cx25821_start_upstream_audio(dev, data_from_user);
+ break;
+
+ case UPSTREAM_STOP_AUDIO:
+ cx25821_stop_upstream_audio(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static long video_ioctl_set(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct cx25821_fh *fh = file->private_data;
+ struct cx25821_dev *dev = fh->dev;
+ struct downstream_user_struct *data_from_user;
+ int command;
+ int width = 720;
+ int selected_channel = 0;
+ int pix_format = 0;
+ int i = 0;
+ int cif_enable = 0;
+ int cif_width = 0;
+
+ data_from_user = (struct downstream_user_struct *)arg;
+
+ if (!data_from_user) {
+ pr_err("%s(): User data is INVALID. Returning\n", __func__);
+ return 0;
+ }
+
+ command = data_from_user->command;
+
+ if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
+ && command != ENABLE_CIF_RESOLUTION && command != REG_READ
+ && command != REG_WRITE && command != MEDUSA_READ
+ && command != MEDUSA_WRITE) {
+ return 0;
+ }
+
+ switch (command) {
+ case SET_VIDEO_STD:
+ if (!strcmp(data_from_user->vid_stdname, "PAL"))
+ dev->tvnorm = V4L2_STD_PAL_BG;
+ else
+ dev->tvnorm = V4L2_STD_NTSC_M;
+ medusa_set_videostandard(dev);
+ break;
+
+ case SET_PIXEL_FORMAT:
+ selected_channel = data_from_user->decoder_select;
+ pix_format = data_from_user->pixel_format;
+
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel >= 0)
+ cx25821_set_pixel_format(dev, selected_channel,
+ pix_format);
+
+ break;
+
+ case ENABLE_CIF_RESOLUTION:
+ selected_channel = data_from_user->decoder_select;
+ cif_enable = data_from_user->cif_resolution_enable;
+ cif_width = data_from_user->cif_width;
+
+ if (cif_enable) {
+ if (dev->tvnorm & V4L2_STD_PAL_BG
+ || dev->tvnorm & V4L2_STD_PAL_DK) {
+ width = 352;
+ } else {
+ width = cif_width;
+ if (cif_width != 320 && cif_width != 352)
+ width = 320;
+ }
+ }
+
+ if (!(selected_channel <= 7 && selected_channel >= 0)) {
+ selected_channel -= 4;
+ selected_channel = selected_channel % 8;
+ }
+
+ if (selected_channel <= 7 && selected_channel >= 0) {
+ dev->channels[selected_channel].use_cif_resolution =
+ cif_enable;
+ dev->channels[selected_channel].cif_width = width;
+ } else {
+ for (i = 0; i < VID_CHANNEL_NUM; i++) {
+ dev->channels[i].use_cif_resolution =
+ cif_enable;
+ dev->channels[i].cif_width = width;
+ }
+ }
+
+ medusa_set_resolution(dev, width, selected_channel);
+ break;
+ case REG_READ:
+ data_from_user->reg_data = cx_read(data_from_user->reg_address);
+ break;
+ case REG_WRITE:
+ cx_write(data_from_user->reg_address, data_from_user->reg_data);
+ break;
+ case MEDUSA_READ:
+ cx25821_i2c_read(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ &data_from_user->reg_data);
+ break;
+ case MEDUSA_WRITE:
+ cx25821_i2c_write(&dev->i2c_bus[0],
+ (u16) data_from_user->reg_address,
+ data_from_user->reg_data);
+ break;
+ }
+
+ return 0;
+}
+
+static long cx25821_video_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+
+ struct cx25821_fh *fh = file->private_data;
+
+ /* check to see if it's the video upstream */
+ if (fh->channel_id == SRAM_CH09) {
+ ret = video_ioctl_upstream9(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH10) {
+ ret = video_ioctl_upstream10(file, cmd, arg);
+ return ret;
+ } else if (fh->channel_id == SRAM_CH11) {
+ ret = video_ioctl_upstream11(file, cmd, arg);
+ ret = video_ioctl_set(file, cmd, arg);
+ return ret;
+ }
+
+ return video_ioctl2(file, cmd, arg);
+}
+
+/* exported stuff */
+static const struct v4l2_file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = cx25821_video_mmap,
+ .ioctl = cx25821_video_ioctl,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = cx25821_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = cx25821_vidioc_reqbufs,
+ .vidioc_querybuf = cx25821_vidioc_querybuf,
+ .vidioc_qbuf = cx25821_vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+#ifdef TUNER_FLAG
+ .vidioc_s_std = cx25821_vidioc_s_std,
+ .vidioc_querystd = cx25821_vidioc_querystd,
+#endif
+ .vidioc_cropcap = cx25821_vidioc_cropcap,
+ .vidioc_s_crop = cx25821_vidioc_s_crop,
+ .vidioc_g_crop = cx25821_vidioc_g_crop,
+ .vidioc_enum_input = cx25821_vidioc_enum_input,
+ .vidioc_g_input = cx25821_vidioc_g_input,
+ .vidioc_s_input = cx25821_vidioc_s_input,
+ .vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_queryctrl = cx25821_vidioc_queryctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_g_priority = cx25821_vidioc_g_priority,
+ .vidioc_s_priority = cx25821_vidioc_s_priority,
+#ifdef TUNER_FLAG
+ .vidioc_g_tuner = cx25821_vidioc_g_tuner,
+ .vidioc_s_tuner = cx25821_vidioc_s_tuner,
+ .vidioc_g_frequency = cx25821_vidioc_g_frequency,
+ .vidioc_s_frequency = cx25821_vidioc_s_frequency,
+#endif
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = cx25821_vidioc_g_register,
+ .vidioc_s_register = cx25821_vidioc_s_register,
+#endif
+};
+
+struct video_device cx25821_videoioctl_template = {
+ .name = "cx25821-videoioctl",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX25821_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h
new file mode 100644
index 000000000000..c265e35b37c3
--- /dev/null
+++ b/drivers/media/pci/cx25821/cx25821-video.h
@@ -0,0 +1,186 @@
+/*
+ * Driver for the Conexant CX25821 PCIe bridge
+ *
+ * Copyright (C) 2009 Conexant Systems Inc.
+ * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
+ * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef CX25821_VIDEO_H_
+#define CX25821_VIDEO_H_
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx25821.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#define TUNER_FLAG
+
+#define VIDEO_DEBUG 0
+
+#define dprintk(level, fmt, arg...) \
+do { \
+ if (VIDEO_DEBUG >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \
+} while (0)
+
+/* For IOCTL to identify running upstream */
+#define UPSTREAM_START_VIDEO 700
+#define UPSTREAM_STOP_VIDEO 701
+#define UPSTREAM_START_AUDIO 702
+#define UPSTREAM_STOP_AUDIO 703
+#define UPSTREAM_DUMP_REGISTERS 702
+#define SET_VIDEO_STD 800
+#define SET_PIXEL_FORMAT 1000
+#define ENABLE_CIF_RESOLUTION 1001
+
+#define REG_READ 900
+#define REG_WRITE 901
+#define MEDUSA_READ 910
+#define MEDUSA_WRITE 911
+
+extern struct sram_channel *channel0;
+extern struct sram_channel *channel1;
+extern struct sram_channel *channel2;
+extern struct sram_channel *channel3;
+extern struct sram_channel *channel4;
+extern struct sram_channel *channel5;
+extern struct sram_channel *channel6;
+extern struct sram_channel *channel7;
+extern struct sram_channel *channel9;
+extern struct sram_channel *channel10;
+extern struct sram_channel *channel11;
+extern struct video_device cx25821_videoioctl_template;
+/* extern const u32 *ctrl_classes[]; */
+
+extern unsigned int vid_limit;
+
+#define FORMAT_FLAGS_PACKED 0x01
+extern struct cx25821_fmt formats[];
+extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
+extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
+
+extern void cx25821_video_wakeup(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q, u32 count);
+
+#ifdef TUNER_FLAG
+extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
+#endif
+
+extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bit);
+extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit);
+extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit);
+extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
+ unsigned int bits);
+extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
+extern int cx25821_start_video_dma(struct cx25821_dev *dev,
+ struct cx25821_dmaqueue *q,
+ struct cx25821_buffer *buf,
+ struct sram_channel *channel);
+
+extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width,
+ unsigned int height, enum v4l2_field field);
+extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status);
+extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num);
+extern int cx25821_video_register(struct cx25821_dev *dev);
+extern int cx25821_get_format_size(void);
+
+extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size);
+extern int cx25821_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field);
+extern void cx25821_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb);
+extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
+extern int cx25821_get_resource(struct cx25821_fh *fh, int resource);
+extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma);
+extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern int cx25821_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap);
+extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f);
+extern int cx25821_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p);
+extern int cx25821_vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p);
+extern int cx25821_vidioc_s_std(struct file *file, void *priv,
+ v4l2_std_id *tvnorms);
+extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
+extern int cx25821_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i);
+extern int cx25821_vidioc_g_input(struct file *file, void *priv,
+ unsigned int *i);
+extern int cx25821_vidioc_s_input(struct file *file, void *priv,
+ unsigned int i);
+extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl);
+extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern int cx25821_vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f);
+extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
+extern int cx25821_vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f);
+extern int cx25821_vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg);
+extern int cx25821_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+extern int cx25821_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t);
+
+extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm);
+extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm);
+
+extern int cx25821_vidioc_g_priority(struct file *file, void *f,
+ enum v4l2_priority *p);
+extern int cx25821_vidioc_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio);
+
+extern int cx25821_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl);
+extern int cx25821_set_control(struct cx25821_dev *dev,
+ struct v4l2_control *ctrl, int chan_num);
+
+extern int cx25821_vidioc_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap);
+extern int cx25821_vidioc_s_crop(struct file *file, void *priv,
+ const struct v4l2_crop *crop);
+extern int cx25821_vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop);
+
+extern int cx25821_vidioc_querystd(struct file *file, void *priv,
+ v4l2_std_id *norm);
+#endif
diff --git a/drivers/media/video/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h
index 8a9c0c869412..8a9c0c869412 100644
--- a/drivers/media/video/cx25821/cx25821.h
+++ b/drivers/media/pci/cx25821/cx25821.h
diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig
new file mode 100644
index 000000000000..d27fccbf03c4
--- /dev/null
+++ b/drivers/media/pci/cx88/Kconfig
@@ -0,0 +1,86 @@
+config VIDEO_CX88
+ tristate "Conexant 2388x (bt878 successor) support"
+ depends on VIDEO_DEV && PCI && I2C && RC_CORE
+ select I2C_ALGOBIT
+ select VIDEO_BTCX
+ select VIDEOBUF_DMA_SG
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for Conexant 2388x based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx8800
+
+config VIDEO_CX88_ALSA
+ tristate "Conexant 2388x DMA audio support"
+ depends on VIDEO_CX88 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio on
+ Conexant 2388x based TV cards using ALSA.
+
+ It only works with boards with function 01 enabled.
+ To check if your board supports, use lspci -n.
+ If supported, you should see 14f1:8801 or 14f1:8811
+ PCI device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-alsa.
+
+config VIDEO_CX88_BLACKBIRD
+ tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
+ depends on VIDEO_CX88
+ select VIDEO_CX2341X
+ ---help---
+ This adds support for MPEG encoder cards based on the
+ Blackbird reference design, using the Conexant 2388x
+ and 23416 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-blackbird.
+
+config VIDEO_CX88_DVB
+ tristate "DVB/ATSC Support for cx2388x based TV cards"
+ depends on VIDEO_CX88 && DVB_CORE
+ select VIDEOBUF_DVB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This adds support for DVB/ATSC cards based on the
+ Conexant 2388x chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-dvb.
+
+config VIDEO_CX88_VP3054
+ tristate "VP-3054 Secondary I2C Bus Support"
+ default m
+ depends on VIDEO_CX88_DVB && DVB_MT352
+ ---help---
+ This adds DVB-T support for cards based on the
+ Conexant 2388x chip and the MT352 demodulator,
+ which also require support for the VP-3054
+ Secondary I2C bus, such at DNTV Live! DVB-T Pro.
+
+config VIDEO_CX88_MPEG
+ tristate
+ depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
+ default y
diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile
new file mode 100644
index 000000000000..d3679c3ee248
--- /dev/null
+++ b/drivers/media/pci/cx88/Makefile
@@ -0,0 +1,16 @@
+cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
+ cx88-dsp.o cx88-input.o
+cx8800-objs := cx88-video.o cx88-vbi.o
+cx8802-objs := cx88-mpeg.o
+
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
+obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
+obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
+obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
+obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
+obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c
new file mode 100644
index 000000000000..3aa6856ead3b
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-alsa.c
@@ -0,0 +1,975 @@
+/*
+ *
+ * Support for audio capture
+ * PCI function #1 of the cx2388x.
+ *
+ * (c) 2007 Trent Piepho <xyzzy@speakeasy.org>
+ * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org>
+ * (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org>
+ * Based on dummy.c by Jaroslav Kysela <perex@perex.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <asm/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <media/wm8775.h>
+
+#include "cx88.h"
+#include "cx88-reg.h"
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)
+
+#define dprintk_core(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)
+
+/****************************************************************************
+ Data type declarations - Can be moded to a header file later
+ ****************************************************************************/
+
+struct cx88_audio_buffer {
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ struct videobuf_dmabuf dma;
+};
+
+struct cx88_audio_dev {
+ struct cx88_core *core;
+ struct cx88_dmaqueue q;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+
+ /* audio controls */
+ int irq;
+
+ struct snd_card *card;
+
+ spinlock_t reg_lock;
+ atomic_t count;
+
+ unsigned int dma_size;
+ unsigned int period_size;
+ unsigned int num_periods;
+
+ struct videobuf_dmabuf *dma_risc;
+
+ struct cx88_audio_buffer *buf;
+
+ struct snd_pcm_substream *substream;
+};
+typedef struct cx88_audio_dev snd_cx88_card_t;
+
+
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
+
+
+/****************************************************************************
+ Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Ricardo Cerqueira");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
+ "{{Conexant,23882},"
+ "{{Conexant,23883}");
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+/****************************************************************************
+ Module specific funtions
+ ****************************************************************************/
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
+{
+ struct cx88_audio_buffer *buf = chip->buf;
+ struct cx88_core *core=chip->core;
+ const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
+
+ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+ /* setup fifo + format - out channel */
+ cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
+
+ /* sets bpl size */
+ cx_write(MO_AUDD_LNGTH, buf->bpl);
+
+ /* reset counter */
+ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ atomic_set(&chip->count, 0);
+
+ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
+ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1,
+ chip->num_periods, buf->bpl * chip->num_periods);
+
+ /* Enables corresponding bits at AUD_INT_STAT */
+ cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+ /* Clean any pending interrupt bits already set */
+ cx_write(MO_AUD_INTSTAT, ~0);
+
+ /* enable audio irqs */
+ cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
+ cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */
+
+ if (debug)
+ cx88_sram_channel_dump(chip->core, audio_ch);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
+{
+ struct cx88_core *core=chip->core;
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* stop dma */
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+ cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
+ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
+
+ if (debug)
+ cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);
+
+ return 0;
+}
+
+#define MAX_IRQ_LOOP 50
+
+/*
+ * BOARD Specific: IRQ dma bits
+ */
+static const char *cx88_aud_irqs[32] = {
+ "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
+ NULL, /* reserved */
+ "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
+ NULL, /* reserved */
+ "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */
+ NULL, /* reserved */
+ "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */
+ NULL, /* reserved */
+ "opc_err", "par_err", "rip_err", /* 16-18 */
+ "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */
+};
+
+/*
+ * BOARD Specific: Threats IRQ audio specific calls
+ */
+static void cx8801_aud_irq(snd_cx88_card_t *chip)
+{
+ struct cx88_core *core = chip->core;
+ u32 status, mask;
+
+ status = cx_read(MO_AUD_INTSTAT);
+ mask = cx_read(MO_AUD_INTMSK);
+ if (0 == (status & mask))
+ return;
+ cx_write(MO_AUD_INTSTAT, status);
+ if (debug > 1 || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq aud",
+ cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
+ status, mask);
+ /* risc op code error */
+ if (status & AUD_INT_OPC_ERR) {
+ printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name);
+ cx_clear(MO_AUD_DMACNTRL, 0x11);
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
+ }
+ if (status & AUD_INT_DN_SYNC) {
+ dprintk(1, "Downstream sync error\n");
+ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ return;
+ }
+ /* risc1 downstream */
+ if (status & AUD_INT_DN_RISCI1) {
+ atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
+ snd_pcm_period_elapsed(chip->substream);
+ }
+ /* FIXME: Any other status should deserve a special handling? */
+}
+
+/*
+ * BOARD Specific: Handles IRQ calls
+ */
+static irqreturn_t cx8801_irq(int irq, void *dev_id)
+{
+ snd_cx88_card_t *chip = dev_id;
+ struct cx88_core *core = chip->core;
+ u32 status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
+ status = cx_read(MO_PCI_INTSTAT) &
+ (core->pci_irqmask | PCI_INT_AUDINT);
+ if (0 == status)
+ goto out;
+ dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
+ loop, MAX_IRQ_LOOP, status);
+ handled = 1;
+ cx_write(MO_PCI_INTSTAT, status);
+
+ if (status & core->pci_irqmask)
+ cx88_core_irq(core, status);
+ if (status & PCI_INT_AUDINT)
+ cx8801_aud_irq(chip);
+ }
+
+ if (MAX_IRQ_LOOP == loop) {
+ printk(KERN_ERR
+ "%s/1: IRQ loop detected, disabling interrupts\n",
+ core->name);
+ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+
+static int dsp_buffer_free(snd_cx88_card_t *chip)
+{
+ BUG_ON(!chip->dma_size);
+
+ dprintk(2,"Freeing buffer\n");
+ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
+ videobuf_dma_free(chip->dma_risc);
+ btcx_riscmem_free(chip->pci,&chip->buf->risc);
+ kfree(chip->buf);
+
+ chip->dma_risc = NULL;
+ chip->dma_size = 0;
+
+ return 0;
+}
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+static const struct snd_pcm_hardware snd_cx88_digital_hw = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* Analog audio output will be full of clicks and pops if there
+ are not exactly four lines in the SRAM FIFO buffer. */
+ .period_bytes_min = DEFAULT_FIFO_SIZE/4,
+ .period_bytes_max = DEFAULT_FIFO_SIZE/4,
+ .periods_min = 1,
+ .periods_max = 1024,
+ .buffer_bytes_max = (1024*1024),
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ if (!chip) {
+ printk(KERN_ERR "BUG: cx88 can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_cx88_digital_hw;
+
+ if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
+ unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
+ bpl &= ~7; /* must be multiple of 8 */
+ runtime->hw.period_bytes_min = bpl;
+ runtime->hw.period_bytes_max = bpl;
+ }
+
+ return 0;
+_error:
+ dprintk(1,"Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_cx88_close(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
+ struct snd_pcm_hw_params * hw_params)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct videobuf_dmabuf *dma;
+
+ struct cx88_audio_buffer *buf;
+ int ret;
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ chip->period_size = params_period_bytes(hw_params);
+ chip->num_periods = params_periods(hw_params);
+ chip->dma_size = chip->period_size * params_periods(hw_params);
+
+ BUG_ON(!chip->dma_size);
+ BUG_ON(chip->num_periods & (chip->num_periods-1));
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (NULL == buf)
+ return -ENOMEM;
+
+ buf->bpl = chip->period_size;
+
+ dma = &buf->dma;
+ videobuf_dma_init(dma);
+ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
+ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
+ if (ret < 0)
+ goto error;
+
+ ret = videobuf_dma_map(&chip->pci->dev, dma);
+ if (ret < 0)
+ goto error;
+
+ ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
+ chip->period_size, chip->num_periods, 1);
+ if (ret < 0)
+ goto error;
+
+ /* Loop back to start of program */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+
+ chip->buf = buf;
+ chip->dma_risc = dma;
+
+ substream->runtime->dma_area = chip->dma_risc->vaddr;
+ substream->runtime->dma_bytes = chip->dma_size;
+ substream->runtime->dma_addr = 0;
+ return 0;
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_cx88_hw_free(struct snd_pcm_substream * substream)
+{
+
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+
+ if (substream->runtime->dma_area) {
+ dsp_buffer_free(chip);
+ substream->runtime->dma_area = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_cx88_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+/*
+ * trigger callback
+ */
+static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ int err;
+
+ /* Local interrupts are already disabled by ALSA */
+ spin_lock(&chip->reg_lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ err=_cx88_start_audio_dma(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ err=_cx88_stop_audio_dma(chip);
+ break;
+ default:
+ err=-EINVAL;
+ break;
+ }
+
+ spin_unlock(&chip->reg_lock);
+
+ return err;
+}
+
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
+{
+ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ u16 count;
+
+ count = atomic_read(&chip->count);
+
+// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,
+// count, new, count & (runtime->periods-1),
+// runtime->period_size * (count & (runtime->periods-1)));
+ return runtime->period_size * (count & (runtime->periods-1));
+}
+
+/*
+ * page callback (needed for mmap)
+ */
+static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
+ unsigned long offset)
+{
+ void *pageptr = substream->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_cx88_pcm_ops = {
+ .open = snd_cx88_pcm_open,
+ .close = snd_cx88_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_cx88_hw_params,
+ .hw_free = snd_cx88_hw_free,
+ .prepare = snd_cx88_prepare,
+ .trigger = snd_cx88_card_trigger,
+ .pointer = snd_cx88_pointer,
+ .page = snd_cx88_page,
+};
+
+/*
+ * create a PCM device
+ */
+static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = chip;
+ strcpy(pcm->name, name);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
+
+ return 0;
+}
+
+/****************************************************************************
+ CONTROL INTERFACE
+ ****************************************************************************/
+static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x3f;
+
+ return 0;
+}
+
+static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core=chip->core;
+ int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
+ bal = cx_read(AUD_BAL_CTL);
+
+ value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
+ vol -= (bal & 0x3f);
+ value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
+
+ return 0;
+}
+
+static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ int left = value->value.integer.value[0];
+ int right = value->value.integer.value[1];
+ int v, b;
+
+ /* Pass volume & balance onto any WM8775 */
+ if (left >= right) {
+ v = left << 10;
+ b = left ? (0x8000 * right) / left : 0x8000;
+ } else {
+ v = right << 10;
+ b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
+ }
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
+}
+
+/* OK - TODO: test it */
+static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core=chip->core;
+ int left, right, v, b;
+ int changed = 0;
+ u32 old;
+
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_cx88_wm8775_volume_put(kcontrol, value);
+
+ left = value->value.integer.value[0] & 0x3f;
+ right = value->value.integer.value[1] & 0x3f;
+ b = right - left;
+ if (b < 0) {
+ v = 0x3f - left;
+ b = (-b) | 0x40;
+ } else {
+ v = 0x3f - right;
+ }
+ /* Do we really know this will always be called with IRQs on? */
+ spin_lock_irq(&chip->reg_lock);
+ old = cx_read(AUD_VOL_CTL);
+ if (v != (old & 0x3f)) {
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
+ changed = 1;
+ }
+ if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
+ cx_write(AUD_BAL_CTL, b);
+ changed = 1;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+
+ return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
+
+static const struct snd_kcontrol_new snd_cx88_volume = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .name = "Analog-TV Volume",
+ .info = snd_cx88_volume_info,
+ .get = snd_cx88_volume_get,
+ .put = snd_cx88_volume_put,
+ .tlv.p = snd_cx88_db_scale,
+};
+
+static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ u32 bit = kcontrol->private_value;
+
+ value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
+ return 0;
+}
+
+static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ u32 bit = kcontrol->private_value;
+ int ret = 0;
+ u32 vol;
+
+ spin_lock_irq(&chip->reg_lock);
+ vol = cx_read(AUD_VOL_CTL);
+ if (value->value.integer.value[0] != !(vol & bit)) {
+ vol ^= bit;
+ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
+ /* Pass mute onto any WM8775 */
+ if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
+ ((1<<6) == bit))
+ wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
+ ret = 1;
+ }
+ spin_unlock_irq(&chip->reg_lock);
+ return ret;
+}
+
+static const struct snd_kcontrol_new snd_cx88_dac_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Audio-Out Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_switch_get,
+ .put = snd_cx88_switch_put,
+ .private_value = (1<<8),
+};
+
+static const struct snd_kcontrol_new snd_cx88_source_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog-TV Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_switch_get,
+ .put = snd_cx88_switch_put,
+ .private_value = (1<<6),
+};
+
+static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ s32 val;
+
+ val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
+ value->value.integer.value[0] = val ? 1 : 0;
+ return 0;
+}
+
+static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
+ struct cx88_core *core = chip->core;
+ struct v4l2_control client_ctl;
+
+ memset(&client_ctl, 0, sizeof(client_ctl));
+ client_ctl.value = 0 != value->value.integer.value[0];
+ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
+ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
+
+ return 0;
+}
+
+static struct snd_kcontrol_new snd_cx88_alc_switch = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Line-In ALC Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = snd_cx88_alc_get,
+ .put = snd_cx88_alc_put,
+};
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
+ * Only boards with eeprom and byte 1 at eeprom=1 have it
+ */
+
+static const struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = {
+ {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+ {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
+ {0, }
+};
+MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
+
+/*
+ * Chip-specific destructor
+ */
+
+static int snd_cx88_free(snd_cx88_card_t *chip)
+{
+
+ if (chip->irq >= 0)
+ free_irq(chip->irq, chip);
+
+ cx88_core_put(chip->core,chip->pci);
+
+ pci_disable_device(chip->pci);
+ return 0;
+}
+
+/*
+ * Component Destructor
+ */
+static void snd_cx88_dev_free(struct snd_card * card)
+{
+ snd_cx88_card_t *chip = card->private_data;
+
+ snd_cx88_free(chip);
+}
+
+
+/*
+ * Alsa Constructor - Component probe
+ */
+
+static int devno;
+static int __devinit snd_cx88_create(struct snd_card *card,
+ struct pci_dev *pci,
+ snd_cx88_card_t **rchip,
+ struct cx88_core **core_ptr)
+{
+ snd_cx88_card_t *chip;
+ struct cx88_core *core;
+ int err;
+ unsigned char pci_lat;
+
+ *rchip = NULL;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ return err;
+
+ pci_set_master(pci);
+
+ chip = card->private_data;
+
+ core = cx88_core_get(pci);
+ if (NULL == core) {
+ err = -EINVAL;
+ return err;
+ }
+
+ if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
+ dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
+ err = -EIO;
+ cx88_core_put(core, pci);
+ return err;
+ }
+
+
+ /* pci init */
+ chip->card = card;
+ chip->pci = pci;
+ chip->irq = -1;
+ spin_lock_init(&chip->reg_lock);
+
+ chip->core = core;
+
+ /* get irq */
+ err = request_irq(chip->pci->irq, cx8801_irq,
+ IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip);
+ if (err < 0) {
+ dprintk(0, "%s: can't get IRQ %d\n",
+ chip->core->name, chip->pci->irq);
+ return err;
+ }
+
+ /* print pci info */
+ pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
+
+ dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", core->name, devno,
+ pci_name(pci), pci->revision, pci->irq,
+ pci_lat, (unsigned long long)pci_resource_start(pci,0));
+
+ chip->irq = pci->irq;
+ synchronize_irq(chip->irq);
+
+ snd_card_set_dev(card, &pci->dev);
+
+ *rchip = chip;
+ *core_ptr = core;
+
+ return 0;
+}
+
+static int __devinit cx88_audio_initdev(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ struct snd_card *card;
+ snd_cx88_card_t *chip;
+ struct cx88_core *core = NULL;
+ int err;
+
+ if (devno >= SNDRV_CARDS)
+ return (-ENODEV);
+
+ if (!enable[devno]) {
+ ++devno;
+ return (-ENOENT);
+ }
+
+ err = snd_card_create(index[devno], id[devno], THIS_MODULE,
+ sizeof(snd_cx88_card_t), &card);
+ if (err < 0)
+ return err;
+
+ card->private_free = snd_cx88_dev_free;
+
+ err = snd_cx88_create(card, pci, &chip, &core);
+ if (err < 0)
+ goto error;
+
+ err = snd_cx88_pcm(chip, 0, "CX88 Digital");
+ if (err < 0)
+ goto error;
+
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
+ if (err < 0)
+ goto error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
+ if (err < 0)
+ goto error;
+ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
+ if (err < 0)
+ goto error;
+
+ /* If there's a wm8775 then add a Line-In ALC switch */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+ snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
+
+ strcpy (card->driver, "CX88x");
+ sprintf(card->shortname, "Conexant CX%x", pci->device);
+ sprintf(card->longname, "%s at %#llx",
+ card->shortname,(unsigned long long)pci_resource_start(pci, 0));
+ strcpy (card->mixername, "CX88");
+
+ dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
+ card->driver,devno);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto error;
+ pci_set_drvdata(pci,card);
+
+ devno++;
+ return 0;
+
+error:
+ snd_card_free(card);
+ return err;
+}
+/*
+ * ALSA destructor
+ */
+static void __devexit cx88_audio_finidev(struct pci_dev *pci)
+{
+ struct cx88_audio_dev *card = pci_get_drvdata(pci);
+
+ snd_card_free((void *)card);
+
+ pci_set_drvdata(pci, NULL);
+
+ devno--;
+}
+
+/*
+ * PCI driver definition
+ */
+
+static struct pci_driver cx88_audio_pci_driver = {
+ .name = "cx88_audio",
+ .id_table = cx88_audio_pci_tbl,
+ .probe = cx88_audio_initdev,
+ .remove = __devexit_p(cx88_audio_finidev),
+};
+
+/****************************************************************************
+ LINUX MODULE INIT
+ ****************************************************************************/
+
+/*
+ * module init
+ */
+static int __init cx88_audio_init(void)
+{
+ printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
+ CX88_VERSION);
+ return pci_register_driver(&cx88_audio_pci_driver);
+}
+
+/*
+ * module remove
+ */
+static void __exit cx88_audio_fini(void)
+{
+ pci_unregister_driver(&cx88_audio_pci_driver);
+}
+
+module_init(cx88_audio_init);
+module_exit(cx88_audio_fini);
diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c
new file mode 100644
index 000000000000..def363fb71c0
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-blackbird.c
@@ -0,0 +1,1299 @@
+/*
+ *
+ * Support for a cx23416 mpeg encoder via cx2388x host port.
+ * "blackbird" reference design.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - video_ioctl2 conversion
+ *
+ * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/cx2341x.h>
+
+#include "cx88.h"
+
+MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int mpegbufs = 32;
+module_param(mpegbufs,int,0644);
+MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
+
+static unsigned int debug;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+
+#define BLACKBIRD_FIRM_IMAGE_SIZE 376836
+
+/* defines below are from ivtv-driver.h */
+
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+enum blackbird_capture_type {
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_CAPTURE,
+ BLACKBIRD_RAW_PASSTHRU_CAPTURE
+};
+enum blackbird_capture_bits {
+ BLACKBIRD_RAW_BITS_NONE = 0x00,
+ BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01,
+ BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02,
+ BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04,
+ BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum blackbird_capture_end {
+ BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */
+ BLACKBIRD_END_NOW, /* stop immediately, no irq */
+};
+enum blackbird_framerate {
+ BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum blackbird_stream_port {
+ BLACKBIRD_OUTPUT_PORT_MEMORY,
+ BLACKBIRD_OUTPUT_PORT_STREAMING,
+ BLACKBIRD_OUTPUT_PORT_SERIAL
+};
+enum blackbird_data_xfer_status {
+ BLACKBIRD_MORE_BUFFERS_FOLLOW,
+ BLACKBIRD_LAST_BUFFER,
+};
+enum blackbird_picture_mask {
+ BLACKBIRD_PICTURE_MASK_NONE,
+ BLACKBIRD_PICTURE_MASK_I_FRAMES,
+ BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3,
+ BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum blackbird_vbi_mode_bits {
+ BLACKBIRD_VBI_BITS_SLICED,
+ BLACKBIRD_VBI_BITS_RAW,
+};
+enum blackbird_vbi_insertion_bits {
+ BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum blackbird_dma_unit {
+ BLACKBIRD_DMA_BYTES,
+ BLACKBIRD_DMA_FRAMES,
+};
+enum blackbird_dma_transfer_status_bits {
+ BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01,
+ BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04,
+ BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum blackbird_pause {
+ BLACKBIRD_PAUSE_ENCODING,
+ BLACKBIRD_RESUME_ENCODING,
+};
+enum blackbird_copyright {
+ BLACKBIRD_COPYRIGHT_OFF,
+ BLACKBIRD_COPYRIGHT_ON,
+};
+enum blackbird_notification_type {
+ BLACKBIRD_NOTIFICATION_REFRESH,
+};
+enum blackbird_notification_status {
+ BLACKBIRD_NOTIFICATION_OFF,
+ BLACKBIRD_NOTIFICATION_ON,
+};
+enum blackbird_notification_mailbox {
+ BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum blackbird_field1_lines {
+ BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum blackbird_field2_lines {
+ BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum blackbird_custom_data_type {
+ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
+ BLACKBIRD_CUSTOM_PRIVATE_PACKET,
+};
+enum blackbird_mute {
+ BLACKBIRD_UNMUTE,
+ BLACKBIRD_MUTE,
+};
+enum blackbird_mute_video_mask {
+ BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum blackbird_mute_video_shift {
+ BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8,
+ BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16,
+ BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
+#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
+
+/* ------------------------------------------------------------------ */
+
+static void host_setup(struct cx88_core *core)
+{
+ /* toggle reset of the host */
+ cx_write(MO_GPHST_SOFT_RST, 1);
+ udelay(100);
+ cx_write(MO_GPHST_SOFT_RST, 0);
+ udelay(100);
+
+ /* host port setup */
+ cx_write(MO_GPHST_WSC, 0x44444444U);
+ cx_write(MO_GPHST_XFR, 0);
+ cx_write(MO_GPHST_WDTH, 15);
+ cx_write(MO_GPHST_HDSHK, 0);
+ cx_write(MO_GPHST_MUX16, 0x44448888U);
+ cx_write(MO_GPHST_MODE, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+#define P1_MDATA0 0x390000
+#define P1_MDATA1 0x390001
+#define P1_MDATA2 0x390002
+#define P1_MDATA3 0x390003
+#define P1_MADDR2 0x390004
+#define P1_MADDR1 0x390005
+#define P1_MADDR0 0x390006
+#define P1_RDATA0 0x390008
+#define P1_RDATA1 0x390009
+#define P1_RDATA2 0x39000A
+#define P1_RDATA3 0x39000B
+#define P1_RADDR0 0x39000C
+#define P1_RADDR1 0x39000D
+#define P1_RRDWR 0x39000E
+
+static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ u32 gpio0,need;
+
+ need = state ? 2 : 0;
+ for (;;) {
+ gpio0 = cx_read(MO_GP0_IO) & 2;
+ if (need == gpio0)
+ return 0;
+ if (time_after(jiffies,timeout))
+ return -1;
+ udelay(1);
+ }
+}
+
+static int memory_write(struct cx88_core *core, u32 address, u32 value)
+{
+ /* Warning: address is dword address (4 bytes) */
+ cx_writeb(P1_MDATA0, (unsigned int)value);
+ cx_writeb(P1_MDATA1, (unsigned int)(value >> 8));
+ cx_writeb(P1_MDATA2, (unsigned int)(value >> 16));
+ cx_writeb(P1_MDATA3, (unsigned int)(value >> 24));
+ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40);
+ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_MADDR0, (unsigned int)address);
+ cx_read(P1_MDATA0);
+ cx_read(P1_MADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+}
+
+static int memory_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ /* Warning: address is dword address (4 bytes) */
+ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
+ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_MADDR0, (unsigned int)address);
+ cx_read(P1_MADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+
+ cx_writeb(P1_MDATA3, 0);
+ val = (unsigned char)cx_read(P1_MDATA3) << 24;
+ cx_writeb(P1_MDATA2, 0);
+ val |= (unsigned char)cx_read(P1_MDATA2) << 16;
+ cx_writeb(P1_MDATA1, 0);
+ val |= (unsigned char)cx_read(P1_MDATA1) << 8;
+ cx_writeb(P1_MDATA0, 0);
+ val |= (unsigned char)cx_read(P1_MDATA0);
+
+ *value = val;
+ return retval;
+}
+
+static int register_write(struct cx88_core *core, u32 address, u32 value)
+{
+ cx_writeb(P1_RDATA0, (unsigned int)value);
+ cx_writeb(P1_RDATA1, (unsigned int)(value >> 8));
+ cx_writeb(P1_RDATA2, (unsigned int)(value >> 16));
+ cx_writeb(P1_RDATA3, (unsigned int)(value >> 24));
+ cx_writeb(P1_RADDR0, (unsigned int)address);
+ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_RRDWR, 1);
+ cx_read(P1_RDATA0);
+ cx_read(P1_RADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+}
+
+
+static int register_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ cx_writeb(P1_RADDR0, (unsigned int)address);
+ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_RRDWR, 0);
+ cx_read(P1_RADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+ val = (unsigned char)cx_read(P1_RDATA0);
+ val |= (unsigned char)cx_read(P1_RDATA1) << 8;
+ val |= (unsigned char)cx_read(P1_RDATA2) << 16;
+ val |= (unsigned char)cx_read(P1_RDATA3) << 24;
+
+ *value = val;
+ return retval;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx8802_dev *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval;
+ int i;
+
+ dprintk(1,"%s: 0x%X\n", __func__, command);
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ memory_read(dev->core, dev->mailbox - 4, &value);
+ if (value != 0x12345678) {
+ dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
+ return -1;
+ }
+
+ memory_read(dev->core, dev->mailbox, &flag);
+ if (flag) {
+ dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ memory_write(dev->core, dev->mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ memory_write(dev->core, dev->mailbox + 1, command); /* command code */
+ memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ memory_write(dev->core, dev->mailbox + 4 + i, data[i]);
+ dprintk(1, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ memory_write(dev->core, dev->mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ memory_write(dev->core, dev->mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ memory_read(dev->core, dev->mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies,timeout)) {
+ dprintk(0, "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ memory_read(dev->core, dev->mailbox + 4 + i, data + i);
+ dprintk(1, "API Output %d = %d\n", i, data[i]);
+ }
+
+ memory_read(dev->core, dev->mailbox + 2, &retval);
+ dprintk(1, "API result = %d\n",retval);
+
+ flag = 0;
+ memory_write(dev->core, dev->mailbox, flag);
+ return retval;
+}
+/* ------------------------------------------------------------------ */
+
+/* We don't need to call the API often, so using just one mailbox will probably suffice */
+static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
+ u32 inputcnt, u32 outputcnt, ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ va_start(vargs, outputcnt);
+
+ for (i = 0; i < inputcnt; i++) {
+ data[i] = va_arg(vargs, int);
+ }
+ err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+ return err;
+}
+
+static int blackbird_find_mailbox(struct cx8802_dev *dev)
+{
+ u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
+ int signaturecnt=0;
+ u32 value;
+ int i;
+
+ for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
+ memory_read(dev->core, i, &value);
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found\n");
+ return i+1;
+ }
+ }
+ dprintk(0, "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static int blackbird_load_firmware(struct cx8802_dev *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 checksum = 0;
+ u32 *dataptr;
+
+ retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ msleep(1);
+ retval |= register_write(dev->core, IVTV_REG_APU, 0);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+
+ retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME,
+ &dev->pci->dev);
+
+
+ if (retval != 0) {
+ dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+ CX2341X_FIRM_ENC_FILENAME);
+ dprintk(0, "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
+ dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
+ firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ /* transfer to the chip */
+ dprintk(1,"Loading firmware ...\n");
+ dataptr = (u32*)firmware->data;
+ for (i = 0; i < (firmware->size >> 2); i++) {
+ value = le32_to_cpu(*dataptr);
+ checksum += ~value;
+ memory_write(dev->core, i, value);
+ dataptr++;
+ }
+
+ /* read back to verify with the checksum */
+ for (i--; i >= 0; i--) {
+ memory_read(dev->core, i, &value);
+ checksum -= ~value;
+ }
+ if (checksum) {
+ dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
+ release_firmware(firmware);
+ return -1;
+ }
+ release_firmware(firmware);
+ dprintk(0, "Firmware upload successful.\n");
+
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_read(dev->core, IVTV_REG_SPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
+ msleep(1);
+
+ retval |= register_read(dev->core, IVTV_REG_VPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+ return 0;
+}
+
+/**
+ Settings used by the windows tv app for PVR2000:
+=================================================================================================================
+Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode
+-----------------------------------------------------------------------------------------------------------------
+MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
+=================================================================================================================
+*DB: "DirectBurn"
+*/
+
+static void blackbird_codec_settings(struct cx8802_dev *dev)
+{
+ /* assign frame size */
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->height, dev->width);
+
+ dev->cxhdl.width = dev->width;
+ dev->cxhdl.height = dev->height;
+ cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
+ cx2341x_handler_setup(&dev->cxhdl);
+}
+
+static int blackbird_initialize_codec(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ int version;
+ int retval;
+
+ dprintk(1,"Initialize codec\n");
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+
+ dev->mpeg_active = 0;
+
+ /* ping was not successful, reset and upload firmware */
+ cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
+ cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
+ retval = blackbird_load_firmware(dev);
+ if (retval < 0)
+ return retval;
+
+ retval = blackbird_find_mailbox(dev);
+ if (retval < 0)
+ return -1;
+
+ dev->mailbox = retval;
+
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware ping failed!\n");
+ return -1;
+ }
+
+ retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version);
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware get encoder version failed!\n");
+ return -1;
+ }
+ dprintk(0, "Firmware version is 0x%08x\n", version);
+ }
+
+ cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
+ cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
+ cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */
+ cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
+
+ blackbird_codec_settings(dev);
+
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ BLACKBIRD_FIELD1_SAA7115,
+ BLACKBIRD_FIELD2_SAA7115
+ );
+
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static int blackbird_start_codec(struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ /* start capturing to the host interface */
+ u32 reg;
+
+ int i;
+ int lastchange = -1;
+ int lastval = 0;
+
+ for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) {
+ reg = cx_read(AUD_STATUS);
+
+ dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg);
+ if ((reg & 0x0F) != lastval) {
+ lastval = reg & 0x0F;
+ lastchange = i;
+ }
+ msleep(100);
+ }
+
+ /* unmute audio source */
+ cx_clear(AUD_VOL_CTL, (1 << 6));
+
+ blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0);
+
+ /* initialize the video input */
+ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+
+ cx2341x_handler_set_busy(&dev->cxhdl, 1);
+
+ /* start capturing to the host interface */
+ blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ dev->mpeg_active = 1;
+ return 0;
+}
+
+static int blackbird_stop_codec(struct cx8802_dev *dev)
+{
+ blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ BLACKBIRD_END_NOW,
+ BLACKBIRD_MPEG_CAPTURE,
+ BLACKBIRD_RAW_BITS_NONE
+ );
+
+ cx2341x_handler_set_busy(&dev->cxhdl, 0);
+
+ dev->mpeg_active = 0;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx8802_fh *fh = q->priv_data;
+
+ fh->dev->ts_packet_size = 188 * 4; /* was: 512 */
+ fh->dev->ts_packet_count = mpegbufs; /* was: 100 */
+
+ *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
+ *count = fh->dev->ts_packet_count;
+ return 0;
+}
+
+static int
+bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8802_fh *fh = q->priv_data;
+ return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field);
+}
+
+static void
+bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx8802_fh *fh = q->priv_data;
+ cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static struct videobuf_queue_ops blackbird_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+
+ strcpy(cap->driver, "cx88_blackbird");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cx88_querycap(file, core, cap);
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = fh->mpegq.field;
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->width, dev->height, fh->mpegq.field );
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->width, dev->height, fh->mpegq.field );
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ fh->mpegq.field = f->fmt.pix.field;
+ cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ f->fmt.pix.height, f->fmt.pix.width);
+ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
+ return 0;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_reqbufs(&fh->mpegq, p));
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_querybuf(&fh->mpegq, p));
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_qbuf(&fh->mpegq, p));
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct cx8802_fh *fh = priv;
+ return (videobuf_dqbuf(&fh->mpegq, p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
+ return videobuf_streamon(&fh->mpegq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+ return videobuf_streamoff(&fh->mpegq);
+}
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+ if (dev->mpeg_active)
+ blackbird_stop_codec(dev);
+
+ cx88_set_freq (core,f);
+ blackbird_initialize_codec(dev);
+ cx88_set_scale(dev->core, dev->width, dev->height,
+ fh->mpegq.field);
+ return 0;
+}
+
+static int vidioc_log_status (struct file *file, void *priv)
+{
+ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", core->name);
+ call_all(core, core, log_status);
+ v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
+ return 0;
+}
+
+static int vidioc_enum_input (struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8802_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ f->frequency = core->freq;
+ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ *i = core->input;
+ return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ if (i >= 4)
+ return -EINVAL;
+ if (0 == INPUT(i).type)
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ cx88_newstation(core);
+ cx88_video_mux(core,i);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+ u32 reg;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ call_all(core, tuner, g_tuner, t);
+
+ cx88_get_stereo(core ,t);
+ reg = cx_read(MO_DEVICE_STATUS);
+ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+ return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ if (UNSET == core->board.tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ cx88_set_stereo(core, t->audmode, 1);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ *tvnorm = core->tvnorm;
+ return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
+
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core,*id);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+/* FIXME: cx88_ioctl_hook not implemented */
+
+static int mpeg_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8802_dev *dev = video_drvdata(file);
+ struct cx8802_fh *fh;
+ struct cx8802_driver *drv = NULL;
+ int err;
+
+ dprintk( 1, "%s\n", __func__);
+
+ mutex_lock(&dev->core->lock);
+
+ /* Make sure we can acquire the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ if (!drv) {
+ dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
+ mutex_unlock(&dev->core->lock);
+ return -ENODEV;
+ }
+
+ err = drv->request_acquire(drv);
+ if (err != 0) {
+ dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
+ mutex_unlock(&dev->core->lock);
+ return err;
+ }
+
+ if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) {
+ drv->request_release(drv);
+ mutex_unlock(&dev->core->lock);
+ return -EINVAL;
+ }
+ dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh) {
+ drv->request_release(drv);
+ mutex_unlock(&dev->core->lock);
+ return -ENOMEM;
+ }
+ v4l2_fh_init(&fh->fh, vdev);
+ file->private_data = fh;
+ fh->dev = dev;
+
+ videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+
+ /* FIXME: locking against other video device */
+ cx88_set_scale(dev->core, dev->width, dev->height,
+ fh->mpegq.field);
+
+ dev->core->mpeg_users++;
+ mutex_unlock(&dev->core->lock);
+ v4l2_fh_add(&fh->fh);
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+ struct cx8802_driver *drv = NULL;
+
+ mutex_lock(&dev->core->lock);
+
+ if (dev->mpeg_active && dev->core->mpeg_users == 1)
+ blackbird_stop_codec(dev);
+
+ cx8802_cancel_buffers(fh->dev);
+ /* stop mpeg capture */
+ videobuf_stop(&fh->mpegq);
+
+ videobuf_mmap_free(&fh->mpegq);
+
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* Make sure we release the hardware */
+ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
+ WARN_ON(!drv);
+ if (drv)
+ drv->request_release(drv);
+
+ dev->core->mpeg_users--;
+
+ mutex_unlock(&dev->core->lock);
+
+ return 0;
+}
+
+static ssize_t
+mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active)
+ blackbird_start_codec(file, fh);
+
+ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int
+mpeg_poll(struct file *file, struct poll_table_struct *wait)
+{
+ unsigned long req_events = poll_requested_events(wait);
+ struct cx8802_fh *fh = file->private_data;
+ struct cx8802_dev *dev = fh->dev;
+
+ if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
+ blackbird_start_codec(file, fh);
+
+ return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
+}
+
+static int
+mpeg_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct cx8802_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(&fh->mpegq, vma);
+}
+
+static const struct v4l2_file_operations mpeg_fops =
+{
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device cx8802_mpeg_template = {
+ .name = "cx8802",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .tvnorms = CX88_NORMS,
+};
+
+/* ------------------------------------------------------------------ */
+
+/* The CX8802 MPEG API will call this when we can use the hardware */
+static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* By default, core setup will leave the cx22702 out of reset, on the bus.
+ * We left the hardware on power up with the cx22702 active.
+ * We're being given access to re-arrange the GPIOs.
+ * Take the bus off the cx22702 and put the cx23416 on it.
+ */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ udelay(1000);
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* The CX8802 MPEG API will call this when we need to release the hardware */
+static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Exit leaving the cx23416 on the bus */
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+static void blackbird_unregister_video(struct cx8802_dev *dev)
+{
+ if (dev->mpeg_dev) {
+ if (video_is_registered(dev->mpeg_dev))
+ video_unregister_device(dev->mpeg_dev);
+ else
+ video_device_release(dev->mpeg_dev);
+ dev->mpeg_dev = NULL;
+ }
+}
+
+static int blackbird_register_video(struct cx8802_dev *dev)
+{
+ int err;
+
+ dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
+ &cx8802_mpeg_template,"mpeg");
+ dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
+ video_set_drvdata(dev->mpeg_dev, dev);
+ err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ printk(KERN_INFO "%s/2: can't register mpeg device\n",
+ dev->core->name);
+ return err;
+ }
+ printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
+ dev->core->name, video_device_node_name(dev->mpeg_dev));
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int cx8802_blackbird_probe(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = core->dvbdev;
+ int err;
+
+ dprintk( 1, "%s\n", __func__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->boardnr,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
+
+ err = -ENODEV;
+ if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))
+ goto fail_core;
+
+ dev->width = 720;
+ if (core->tvnorm & V4L2_STD_525_60) {
+ dev->height = 480;
+ } else {
+ dev->height = 576;
+ }
+ dev->cxhdl.port = CX2341X_PORT_STREAMING;
+ dev->cxhdl.width = dev->width;
+ dev->cxhdl.height = dev->height;
+ dev->cxhdl.func = blackbird_mbox_func;
+ dev->cxhdl.priv = dev;
+ err = cx2341x_handler_init(&dev->cxhdl, 36);
+ if (err)
+ goto fail_core;
+ v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL);
+
+ /* blackbird stuff */
+ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
+ core->name);
+ host_setup(dev->core);
+
+ blackbird_initialize_codec(dev);
+
+ /* initial device configuration: needed ? */
+// init_controls(core);
+ cx88_set_tvnorm(core,core->tvnorm);
+ cx88_video_mux(core,0);
+ cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
+ cx2341x_handler_setup(&dev->cxhdl);
+ blackbird_register_video(dev);
+
+ return 0;
+
+ fail_core:
+ return err;
+}
+
+static int cx8802_blackbird_remove(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = core->dvbdev;
+
+ /* blackbird */
+ blackbird_unregister_video(drv->core->dvbdev);
+ v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
+
+ return 0;
+}
+
+static struct cx8802_driver cx8802_blackbird_driver = {
+ .type_id = CX88_MPEG_BLACKBIRD,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_blackbird_probe,
+ .remove = cx8802_blackbird_remove,
+ .advise_acquire = cx8802_blackbird_advise_acquire,
+ .advise_release = cx8802_blackbird_advise_release,
+};
+
+static int __init blackbird_init(void)
+{
+ printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
+ CX88_VERSION);
+ return cx8802_register_driver(&cx8802_blackbird_driver);
+}
+
+static void __exit blackbird_fini(void)
+{
+ cx8802_unregister_driver(&cx8802_blackbird_driver);
+}
+
+module_init(blackbird_init);
+module_exit(blackbird_fini);
+
+module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [video]");
diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c
new file mode 100644
index 000000000000..0c255248cbcd
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-cards.c
@@ -0,0 +1,3811 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * card-specific stuff.
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "cx88.h"
+#include "tea5767.h"
+#include "xc4000.h"
+
+static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(tuner, int, NULL, 0444);
+module_param_array(radio, int, NULL, 0444);
+module_param_array(card, int, NULL, 0444);
+
+MODULE_PARM_DESC(tuner,"tuner type");
+MODULE_PARM_DESC(radio,"radio tuner type");
+MODULE_PARM_DESC(card,"card type");
+
+static unsigned int latency = UNSET;
+module_param(latency,int,0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
+
+static int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "Disable IR support");
+
+#define info_printk(core, fmt, arg...) \
+ printk(KERN_INFO "%s: " fmt, core->name , ## arg)
+
+#define warn_printk(core, fmt, arg...) \
+ printk(KERN_WARNING "%s: " fmt, core->name , ## arg)
+
+#define err_printk(core, fmt, arg...) \
+ printk(KERN_ERR "%s: " fmt, core->name , ## arg)
+
+
+/* ------------------------------------------------------------------ */
+/* board config info */
+
+/* If radio_type !=UNSET, radio_addr should be specified
+ */
+
+static const struct cx88_board cx88_boards[] = {
+ [CX88_BOARD_UNKNOWN] = {
+ .name = "UNKNOWN/GENERIC",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE3,
+ .vmux = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE4,
+ .vmux = 3,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE] = {
+ .name = "Hauppauge WinTV 34xxx models",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00, // internal decoder
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01, // mono from tuner chip
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xff02,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xff01,
+ },
+ },
+ [CX88_BOARD_GDI] = {
+ .name = "GDI Black Gold",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ },
+ [CX88_BOARD_PIXELVIEW] = {
+ .name = "PixelView",
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xff00, // internal decoder
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xff10,
+ },
+ },
+ [CX88_BOARD_ATI_WONDER_PRO] = {
+ .name = "ATI TV Wonder Pro",
+ .tuner_type = TUNER_PHILIPS_4IN1,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x03ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x03fe,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x03fe,
+ }},
+ },
+ [CX88_BOARD_WINFAST2000XP_EXPERT] = {
+ .name = "Leadtek Winfast 2000XP Expert",
+ .tuner_type = TUNER_PHILIPS_4IN1,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00F5e700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5e700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00F5c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5c700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00F5c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5c700,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00F5d700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x00F5d700,
+ .gpio3 = 0x02000000,
+ },
+ },
+ [CX88_BOARD_AVERTV_STUDIO_303] = {
+ .name = "AverTV Studio 303 (M126)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio1 = 0xe09f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio1 = 0xe05f,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio1 = 0xe05f,
+ }},
+ .radio = {
+ .gpio1 = 0xe0df,
+ .type = CX88_RADIO,
+ },
+ },
+ [CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
+ // added gpio values thanks to Michal
+ // values for PAL from DScaler
+ .name = "MSI TV-@nywhere Master",
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff40,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .vmux = 3,
+ .gpio0 = 0x000040bf,
+ .gpio1 = 0x000080c0,
+ .gpio2 = 0x0000ff20,
+ },
+ },
+ [CX88_BOARD_WINFAST_DV2000] = {
+ .name = "Leadtek Winfast DV2000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0035e700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x0035e700,
+ .gpio3 = 0x02000000,
+ },{
+
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0035c700,
+ .gpio1 = 0x00003004,
+ .gpio2 = 0x0035c700,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0035c700,
+ .gpio1 = 0x0035c700,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0035d700,
+ .gpio1 = 0x00007004,
+ .gpio2 = 0x0035d700,
+ .gpio3 = 0x02000000,
+ },
+ },
+ [CX88_BOARD_LEADTEK_PVR2000] = {
+ // gpio values for PAL version from regspy by DScaler
+ .name = "Leadtek PVR 2000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000bde2,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0000bde6,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000bde6,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0000bd62,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_IODATA_GVVCP3PCI] = {
+ .name = "IODATA GV-VCP3/PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ },
+ [CX88_BOARD_PROLINK_PLAYTVPVR] = {
+ .name = "Prolink PlayTV PVR",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xbff0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xbff3,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xbff3,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xbff0,
+ },
+ },
+ [CX88_BOARD_ASUS_PVR_416] = {
+ .name = "ASUS PVR-416",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000fde6,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0000fde2,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_MSI_TVANYWHERE] = {
+ .name = "MSI TV-@nywhere",
+ .tuner_type = TUNER_MT2032,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc08,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc68,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000fbf,
+ .gpio2 = 0x0000fc68,
+ }},
+ },
+ [CX88_BOARD_KWORLD_DVB_T] = {
+ .name = "KWorld/VStream XPert DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
+ .name = "DViCO FusionHDTV DVB-T1",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_LTV883] = {
+ .name = "KWorld LTV883RF",
+ .tuner_type = TUNER_TNF_8831BGFF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x07f8,
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0x07f9, // mono from tuner chip
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000007fa,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000007fa,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000007f8,
+ },
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = {
+ .name = "DViCO FusionHDTV 3 Gold-Q",
+ .tuner_type = TUNER_MICROTUNE_4042FI5,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ /*
+ GPIO[0] resets DT3302 DTV receiver
+ 0 - reset asserted
+ 1 - normal operation
+ GPIO[1] mutes analog audio output connector
+ 0 - enable selected source
+ 1 - mute
+ GPIO[2] selects source for analog audio output connector
+ 0 - analog audio input connector on tab
+ 1 - analog DAC output from CX23881 chip
+ GPIO[3] selects RF input connector on tuner module
+ 0 - RF connector labeled CABLE
+ 1 - RF connector labeled ANT
+ GPIO[4] selects high RF for QAM256 mode
+ 0 - normal RF
+ 1 - high RF
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0f0d,
+ },{
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0f05,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0f00,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0f00,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_DVB_T1] = {
+ .name = "Hauppauge Nova-T DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_CONEXANT_DVB_T1] = {
+ .name = "Conexant DVB-T reference design",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROVIDEO_PV259] = {
+ .name = "Provideo PV259",
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
+ .name = "DViCO FusionHDTV DVB-T Plus",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DNTV_LIVE_DVB_T] = {
+ .name = "digitalnow DNTV Live! DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000700,
+ .gpio2 = 0x00000101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000700,
+ .gpio2 = 0x00000101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PCHDTV_HD3000] = {
+ .name = "pcHDTV HD3000 HDTV",
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ /* GPIO[2] = audio source for analog audio out connector
+ * 0 = analog audio input connector
+ * 1 = CX88 audio DACs
+ *
+ * GPIO[7] = input to CX88's audio/chroma ADC
+ * 0 = FM 10.7 MHz IF
+ * 1 = Sound 4.5 MHz IF
+ *
+ * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively
+ *
+ * GPIO[16] = Remote control input
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00008484,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00008400,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00008400,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00008404,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_ROSLYN] = {
+ // entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu>
+ // GPIO values obtained from regspy, courtesy Sean Covel
+ .name = "Hauppauge WinTV 28xxx (Roslyn) models",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xed1a,
+ .gpio2 = 0x00ff,
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0xff01,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xff02,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xed92,
+ .gpio2 = 0x00ff,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xed96,
+ .gpio2 = 0x00ff,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DIGITALLOGIC_MEC] = {
+ .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00009d80,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00009d76,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00009d76,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00009d00,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_IODATA_GVBCTV7E] = {
+ .name = "IODATA GV/BCTV7E",
+ .tuner_type = TUNER_PHILIPS_FQ1286,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 1,
+ .gpio1 = 0x0000e03f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 2,
+ .gpio1 = 0x0000e07f,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio1 = 0x0000e07f,
+ }}
+ },
+ [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = {
+ .name = "PixelView PlayTV Ultra Pro (Stereo)",
+ /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ /* Some variants use a tda9874 and so need the tvaudio module. */
+ .audio_chip = V4L2_IDENT_TVAUDIO,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xbf61, /* internal decoder */
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xbf63,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xbf63,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xbf60,
+ },
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = {
+ .name = "DViCO FusionHDTV 3 Gold-T",
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x97ed,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x97e9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x97e9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_ADSTECH_DVB_T_PCI] = {
+ .name = "ADS Tech Instant TV DVB-T PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
+ .name = "TerraTec Cinergy 1400 DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = {
+ .name = "DViCO FusionHDTV 5 Gold",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x87fd,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x87f9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x87f9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = {
+ .name = "AverMedia UltraTV Media Center PCI 550",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .gpio0 = 0x0000cd73,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 1,
+ .gpio0 = 0x0000cd73,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 3,
+ .gpio0 = 0x0000cdb3,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .vmux = 2,
+ .gpio0 = 0x0000cdf3,
+ .audioroute = 1,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
+ /* Alexander Wold <awold@bigfoot.com> */
+ .name = "Kworld V-Stream Xpert DVD",
+ .tuner_type = UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x03000000,
+ .gpio1 = 0x01000000,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x00100000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x03000000,
+ .gpio1 = 0x01000000,
+ .gpio2 = 0x02000000,
+ .gpio3 = 0x00100000,
+ }},
+ },
+ [CX88_BOARD_ATI_HDTVWONDER] = {
+ .name = "ATI HDTV Wonder",
+ .tuner_type = TUNER_PHILIPS_TUV1236D,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000ff7,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000ffe,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000ffe,
+ .gpio1 = 0x000000ff,
+ .gpio2 = 0x00000001,
+ .gpio3 = 0x00000000,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV1000] = {
+ .name = "WinFast DTV1000-T",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_AVERTV_303] = {
+ .name = "AVerTV 303 (M126)",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe09f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe05f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xe05f,
+ .gpio2 = 0x0010,
+ .gpio3 = 0x0000,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
+ .name = "Hauppauge Nova-S-Plus DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .i2sinputcntl = 2,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
+ .name = "Hauppauge Nova-SE2 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_DVBS_100] = {
+ .name = "KWorld DVB-S 100",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1100] = {
+ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1100LP] = {
+ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ }},
+ /* fixme: Add radio support */
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = {
+ .name = "digitalnow DNTV Live! DVB-T Pro",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xf80808,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xf80808,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xf80808,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xf80808,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_DVB_T_CX22702] = {
+ /* Kworld V-stream Xpert DVB-T with Thomson tuner */
+ /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */
+ /* Manenti Marco <marco_manenti@colman.it> */
+ .name = "KWorld/VStream XPert DVB-T with cx22702",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0700,
+ .gpio2 = 0x0101,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
+ .name = "DViCO FusionHDTV DVB-T Dual Digital",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000067df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000067df,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
+ .name = "KWorld HardwareMpegTV XPert",
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x3de2,
+ .gpio2 = 0x00ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x3de6,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x3de6,
+ .audioroute = 1,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x3de6,
+ .gpio2 = 0x00ff,
+ },
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = {
+ .name = "DViCO FusionHDTV DVB-T Hybrid",
+ .tuner_type = TUNER_THOMSON_FE6600,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000a75f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0000a75b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0000a75b,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PCHDTV_HD5500] = {
+ .name = "pcHDTV HD5500 HDTV",
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x87fd,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x87f9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x87f9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_KWORLD_MCE200_DELUXE] = {
+ /* FIXME: tested TV input only, disabled composite,
+ svideo and radio until they can be tested also. */
+ .name = "Kworld MCE 200 Deluxe",
+ .tuner_type = TUNER_TENA_9533_DI,
+ .radio_type = UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0000BDE6
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = {
+ /* FIXME: SVideo, Composite and FM inputs are untested */
+ .name = "PixelView PlayTV P7000",
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x5da6,
+ }},
+ .mpeg = CX88_MPEG_BLACKBIRD,
+ },
+ [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = {
+ .name = "NPG Tech Real TV FM Top 10",
+ .tuner_type = TUNER_TNF_5335MF, /* Actually a TNF9535 */
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0788,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x078b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x078b,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x074a,
+ },
+ },
+ [CX88_BOARD_WINFAST_DTV2000H] = {
+ .name = "WinFast DTV2000 H",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017304,
+ .gpio1 = 0x00008203,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE2,
+ .vmux = 2,
+ .gpio0 = 0x0001d503,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d503,
+ .gpio3 = 0x02000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 3,
+ .gpio0 = 0x0001d701,
+ .gpio1 = 0x0000b207,
+ .gpio2 = 0x0001d701,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV2000H_J] = {
+ .name = "WinFast DTV2000 H rev. J",
+ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017300,
+ .gpio1 = 0x00008207,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00018300,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_GENIATECH_DVBS] = {
+ .name = "Geniatech DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR3000] = {
+ .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x84bf,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x84bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x84bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x84bf,
+ /* 4: FM Stereo (untested) */
+ .audioroute = 8,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ .num_frontends = 2,
+ },
+ [CX88_BOARD_NORWOOD_MICRO] = {
+ .name = "Norwood Micro TV Tuner",
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0709,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x070b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x070b,
+ }},
+ },
+ [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+ .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1300] = {
+ .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xef88,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xef88,
+ /* 2: Line-In */
+ .audioroute = 2,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xef88,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }},
+ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xef88,
+ /* 4: FM Stereo (untested) */
+ .audioroute = 8,
+ },
+ },
+ [CX88_BOARD_SAMSUNG_SMT_7020] = {
+ .name = "Samsung SMT 7020 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_ADSTECH_PTV_390] = {
+ .name = "ADS Tech Instant Video PCI",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 3,
+ .gpio0 = 0x04ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x07fa,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x07fa,
+ }},
+ },
+ [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
+ .name = "Pinnacle PCTV HD 800i",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ff,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x10ef,
+ .audioroute = 1,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
+ .name = "DViCO FusionHDTV 5 PCI nano",
+ /* xc3008 tuner, digital only for now */
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000027df, /* Unconfirmed */
+ .audioroute = 1,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PINNACLE_HYBRID_PCTV] = {
+ .name = "Pinnacle Hybrid PCTV",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x0ff,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ /* Terry Wu <terrywu2009@gmail.com> */
+ /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */
+ /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */
+ /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */
+ /* Mute Audio : set GPIO 2 value to 1 */
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
+ .name = "Leadtek TV2000 XP Global",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36] = {
+ .name = "Leadtek TV2000 XP Global (SC4100)",
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x0000,
+ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43] = {
+ .name = "Leadtek TV2000 XP Global (XC4100)",
+ .tuner_type = TUNER_XC4000,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 14 = 1, pin 13 = 0 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 14 = 1, pin 13 = 0 */
+ .gpio2 = 0x0000,
+ .gpio3 = 0x0000,
+ },
+ },
+ [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = {
+ .name = "PowerColor RA330", /* Long names may confuse LIRC. */
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 3, /* Due to the way the cx88 driver is written, */
+ .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */
+ .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */
+ .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */
+ }, { /* from the tuner on boot for a little while. */
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xf35d,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00ff,
+ .gpio1 = 0xf37d,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000ff,
+ .gpio1 = 0x0f37d,
+ .gpio3 = 0x00000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000ff,
+ .gpio1 = 0x0f35d,
+ .gpio3 = 0x00000,
+ },
+ },
+ [CX88_BOARD_GENIATECH_X8000_MT] = {
+ /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */
+ .name = "Geniatech X8000-MT DVBT",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e341,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e361,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e361,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00000000,
+ .gpio1 = 0x00e3e341,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x00000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = {
+ .name = "DViCO FusionHDTV DVB-T PRO",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000067df,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000067df,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = {
+ .name = "DViCO FusionHDTV 7 Gold",
+ .tuner_type = TUNER_XC5000,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x10df,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x16d9,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x16d9,
+ }},
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROLINK_PV_8000GT] = {
+ .name = "Prolink Pixelview MPEG 8000GT",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0ff,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio2 = 0x0cfb,
+ },
+ },
+ [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
+ .name = "Prolink Pixelview Global Extreme",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x04fb,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cfb,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x04ff,
+ .gpio1 = 0x04080,
+ .gpio2 = 0x0cf7,
+ },
+ },
+ /* Both radio, analog and ATSC work with this board.
+ However, for analog to work, s5h1409 gate should be open,
+ otherwise, tuner-xc3028 won't be detected.
+ A proper fix require using the newer i2c methods to add
+ tuner-xc3028 without doing an i2c probe.
+ */
+ [CX88_BOARD_KWORLD_ATSC_120] = {
+ .name = "Kworld PlusTV HD PCI 120 (ATSC 120)",
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f35d,
+ .gpio2 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f37e,
+ .gpio2 = 0x00000000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f37e,
+ .gpio2 = 0x00000000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000000ff,
+ .gpio1 = 0x0000f35d,
+ .gpio2 = 0x00000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000] = {
+ .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .audio_chip = V4L2_IDENT_WM8775,
+ /*
+ * GPIO0 (WINTV2000)
+ *
+ * Analogue SAT DVB-T
+ * Antenna 0xc4bf 0xc4bb
+ * Composite 0xc4bf 0xc4bb
+ * S-Video 0xc4bf 0xc4bb
+ * Composite1 0xc4ff 0xc4fb
+ * S-Video1 0xc4ff 0xc4fb
+ *
+ * BIT VALUE FUNCTION GP{x}_IO
+ * 0 1 I:?
+ * 1 1 I:?
+ * 2 1 O:MPEG PORT 0=DVB-T 1=DVB-S
+ * 3 1 I:?
+ * 4 1 I:?
+ * 5 1 I:?
+ * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
+ * 7 1 O:DVB-T DEMOD RESET LOW
+ *
+ * BIT VALUE FUNCTION GP{x}_OE
+ * 8 0 I
+ * 9 0 I
+ * a 1 O
+ * b 0 I
+ * c 0 I
+ * d 0 I
+ * e 1 O
+ * f 1 O
+ *
+ * WM8775 ADC
+ *
+ * 1: TV Audio / FM Mono
+ * 2: Line-In
+ * 3: Line-In Expansion
+ * 4: FM Stereo
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xc4bf,
+ /* 1: TV Audio / FM Mono */
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xc4bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xc4bf,
+ /* 2: Line-In */
+ .audioroute = 2,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xc4bf,
+ /* 4: FM Stereo */
+ .audioroute = 8,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ .num_frontends = 2,
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
+ .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S420] = {
+ .name = "TeVii S420 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S460] = {
+ .name = "TeVii S460 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TEVII_S464] = {
+ .name = "TeVii S464 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_OMICOM_SS4_PCI] = {
+ .name = "Omicom SS4 DVB-S/S2 PCI",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8910] = {
+ .name = "TBS 8910 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TBS_8920] = {
+ .name = "TBS 8920 DVB-S/S2",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ .gpio0 = 0x8080,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_6200] = {
+ .name = "Prof 6200 DVB-S",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_7300] = {
+ .name = "PROF 7300 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_SATTRADE_ST4200] = {
+ .name = "SATTRADE ST4200 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = {
+ .name = "Terratec Cinergy HT PCI MKII",
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .radio_type = UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x00001,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x004fb,
+ .gpio1 = 0x010ef,
+ .audioroute = 1,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x004ff,
+ .gpio1 = 0x010ff,
+ .gpio2 = 0x0ff,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_HAUPPAUGE_IRONLY] = {
+ .name = "Hauppauge WinTV-IR Only",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ },
+ [CX88_BOARD_WINFAST_DTV1800H] = {
+ .name = "Leadtek WinFast DTV1800 Hybrid",
+ .tuner_type = TUNER_XC2028,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO setting
+ *
+ * 2: mute (0=off,1=on)
+ * 12: tuner reset pin
+ * 13: audio source (0=tuner audio,1=line in)
+ * 14: FM (0=on,1=off ???)
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ } },
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
+ .gpio2 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
+ .name = "Leadtek WinFast DTV1800 H (XC4000)",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO setting
+ *
+ * 2: mute (0=off,1=on)
+ * 12: tuner reset pin
+ * 13: audio source (0=tuner audio,1=line in)
+ * 14: FM (0=on,1=off ???)
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
+ .gpio2 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0400, /* pin 2 = 0 */
+ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
+ .gpio2 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
+ .name = "Leadtek WinFast DTV2000 H PLUS",
+ .tuner_type = TUNER_XC4000,
+ .radio_type = UNSET,
+ .tuner_addr = 0x61,
+ .radio_addr = ADDR_UNSET,
+ /*
+ * GPIO
+ * 2: 1: mute audio
+ * 12: 0: reset XC4000
+ * 13: 1: audio input is line in (0: tuner)
+ * 14: 0: FM radio
+ * 16: 0: RF input is cable
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF0D7,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }, {
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0403, /* was 0x0407 */
+ .gpio1 = 0xF0F7,
+ .gpio2 = 0x0101,
+ .gpio3 = 0x0000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x0403,
+ .gpio1 = 0xF097,
+ .gpio2 = 0x0100,
+ .gpio3 = 0x0000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_PROF_7301] = {
+ .name = "Prof 7301 DVB-S/S2",
+ .tuner_type = UNSET,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = { {
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+ [CX88_BOARD_TWINHAN_VP1027_DVBS] = {
+ .name = "Twinhan VP-1027 DVB-S",
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
+};
+
+/* ------------------------------------------------------------------ */
+/* PCI subsystem IDs */
+
+static const struct cx88_subid cx88_subids[] = {
+ {
+ .subvendor = 0x0070,
+ .subdevice = 0x3400,
+ .card = CX88_BOARD_HAUPPAUGE,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x3401,
+ .card = CX88_BOARD_HAUPPAUGE,
+ },{
+ .subvendor = 0x14c7,
+ .subdevice = 0x0106,
+ .card = CX88_BOARD_GDI,
+ },{
+ .subvendor = 0x14c7,
+ .subdevice = 0x0107, /* with mpeg encoder */
+ .card = CX88_BOARD_GDI,
+ },{
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f8,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0x00f9,
+ .card = CX88_BOARD_ATI_WONDER_PRO,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6611,
+ .card = CX88_BOARD_WINFAST2000XP_EXPERT,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6613, /* NTSC */
+ .card = CX88_BOARD_WINFAST2000XP_EXPERT,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6620,
+ .card = CX88_BOARD_WINFAST_DV2000,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x663b,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x663c,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x000b,
+ .card = CX88_BOARD_AVERTV_STUDIO_303,
+ },{
+ .subvendor = 0x1462,
+ .subdevice = 0x8606,
+ .card = CX88_BOARD_MSI_TVANYWHERE_MASTER,
+ },{
+ .subvendor = 0x10fc,
+ .subdevice = 0xd003,
+ .card = CX88_BOARD_IODATA_GVVCP3PCI,
+ },{
+ .subvendor = 0x1043,
+ .subdevice = 0x4823, /* with mpeg encoder */
+ .card = CX88_BOARD_ASUS_PVR_416,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08a6,
+ .card = CX88_BOARD_KWORLD_DVB_T,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd810,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd820,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb00,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9002,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0187,
+ .card = CX88_BOARD_CONEXANT_DVB_T1,
+ },{
+ .subvendor = 0x1540,
+ .subdevice = 0x2580,
+ .card = CX88_BOARD_PROVIDEO_PV259,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb10,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+ },{
+ .subvendor = 0x1554,
+ .subdevice = 0x4811,
+ .card = CX88_BOARD_PIXELVIEW,
+ },{
+ .subvendor = 0x7063,
+ .subdevice = 0x3000, /* HD-3000 card */
+ .card = CX88_BOARD_PCHDTV_HD3000,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0xa8a6,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x2801,
+ .card = CX88_BOARD_HAUPPAUGE_ROSLYN,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0342,
+ .card = CX88_BOARD_DIGITALLOGIC_MEC,
+ },{
+ .subvendor = 0x10fc,
+ .subdevice = 0xd035,
+ .card = CX88_BOARD_IODATA_GVBCTV7E,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0334,
+ .card = CX88_BOARD_ADSTECH_DVB_T_PCI,
+ },{
+ .subvendor = 0x153b,
+ .subdevice = 0x1166,
+ .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd500,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x8011,
+ .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550,
+ },{
+ .subvendor = PCI_VENDOR_ID_ATI,
+ .subdevice = 0xa101,
+ .card = CX88_BOARD_ATI_HDTVWONDER,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x665f,
+ .card = CX88_BOARD_WINFAST_DTV1000,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0x000a,
+ .card = CX88_BOARD_AVERTV_303,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9200,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9201,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9202,
+ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08b2,
+ .card = CX88_BOARD_KWORLD_DVBS_100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9400,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9402,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9800,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9802,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9001,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x1822,
+ .subdevice = 0x0025,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08a1,
+ .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb50,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb54,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
+ /* Re-branded DViCO: DigitalNow DVB-T Dual */
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb11,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
+ /* Re-branded DViCO: UltraView DVB-T Plus */
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb30,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO,
+ }, {
+ .subvendor = 0x17de,
+ .subdevice = 0x0840,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0305,
+ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb40,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xdb44,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
+ },{
+ .subvendor = 0x7063,
+ .subdevice = 0x5500,
+ .card = CX88_BOARD_PCHDTV_HD5500,
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x0841,
+ .card = CX88_BOARD_KWORLD_MCE200_DELUXE,
+ },{
+ .subvendor = 0x1822,
+ .subdevice = 0x0019,
+ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
+ },{
+ .subvendor = 0x1554,
+ .subdevice = 0x4813,
+ .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0842,
+ .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x665e,
+ .card = CX88_BOARD_WINFAST_DTV2000H,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6f2b,
+ .card = CX88_BOARD_WINFAST_DTV2000H_J,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0084,
+ .card = CX88_BOARD_GENIATECH_DVBS,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1404,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdc00,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xdccd,
+ .card = CX88_BOARD_SAMSUNG_SMT_7020,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0xc111, /* AverMedia M150-D */
+ /* This board is known to work with the ASUS PVR416 config */
+ .card = CX88_BOARD_ASUS_PVR_416,
+ },{
+ .subvendor = 0xc180,
+ .subdevice = 0xc980,
+ .card = CX88_BOARD_TE_DTV_250_OEM_SWANN,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9600,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9601,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9602,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6632,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ },{
+ .subvendor = 0x12ab,
+ .subdevice = 0x2300, /* Club3D Zap TV2100 */
+ .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9000,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1400,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1401,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1402,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
+ .card = CX88_BOARD_KWORLD_DVBS_100,
+ },{
+ .subvendor = 0x1421,
+ .subdevice = 0x0390,
+ .card = CX88_BOARD_ADSTECH_PTV_390,
+ },{
+ .subvendor = 0x11bd,
+ .subdevice = 0x0051,
+ .card = CX88_BOARD_PINNACLE_PCTV_HD_800i,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd530,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO,
+ }, {
+ .subvendor = 0x12ab,
+ .subdevice = 0x1788,
+ .card = CX88_BOARD_PINNACLE_HYBRID_PCTV,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0xea3d,
+ .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f18,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8852,
+ .card = CX88_BOARD_GENIATECH_X8000_MT,
+ }, {
+ .subvendor = 0x18ac,
+ .subdevice = 0xd610,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD,
+ }, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4935,
+ .card = CX88_BOARD_PROLINK_PV_8000GT,
+ }, {
+ .subvendor = 0x1554,
+ .subdevice = 0x4976,
+ .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
+ }, {
+ .subvendor = 0x17de,
+ .subdevice = 0x08c1,
+ .card = CX88_BOARD_KWORLD_ATSC_120,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6900,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6904,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6902,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6905,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x6906,
+ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
+ }, {
+ .subvendor = 0xd420,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S420,
+ }, {
+ .subvendor = 0xd460,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S460,
+ }, {
+ .subvendor = 0xd464,
+ .subdevice = 0x9022,
+ .card = CX88_BOARD_TEVII_S464,
+ }, {
+ .subvendor = 0xA044,
+ .subdevice = 0x2011,
+ .card = CX88_BOARD_OMICOM_SS4_PCI,
+ }, {
+ .subvendor = 0x8910,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8910,
+ }, {
+ .subvendor = 0x8920,
+ .subdevice = 0x8888,
+ .card = CX88_BOARD_TBS_8920,
+ }, {
+ .subvendor = 0xb022,
+ .subdevice = 0x3022,
+ .card = CX88_BOARD_PROF_6200,
+ }, {
+ .subvendor = 0xB033,
+ .subdevice = 0x3033,
+ .card = CX88_BOARD_PROF_7300,
+ }, {
+ .subvendor = 0xb200,
+ .subdevice = 0x4200,
+ .card = CX88_BOARD_SATTRADE_ST4200,
+ }, {
+ .subvendor = 0x153b,
+ .subdevice = 0x1177,
+ .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII,
+ }, {
+ .subvendor = 0x0070,
+ .subdevice = 0x9290,
+ .card = CX88_BOARD_HAUPPAUGE_IRONLY,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6654,
+ .card = CX88_BOARD_WINFAST_DTV1800H,
+ }, {
+ /* WinFast DTV1800 H with XC4000 tuner */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f38,
+ .card = CX88_BOARD_WINFAST_DTV1800H_XC4000,
+ }, {
+ .subvendor = 0x107d,
+ .subdevice = 0x6f42,
+ .card = CX88_BOARD_WINFAST_DTV2000H_PLUS,
+ }, {
+ /* PVR2000 PAL Model [107d:6630] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6630,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 PAL Model [107d:6638] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6638,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:6631] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6631,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:6637] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6637,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* PVR2000 NTSC Model [107d:663d] */
+ .subvendor = 0x107d,
+ .subdevice = 0x663d,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
+ }, {
+ /* DV2000 NTSC Model [107d:6621] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6621,
+ .card = CX88_BOARD_WINFAST_DV2000,
+ }, {
+ /* TV2000 XP Global [107d:6618] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6618,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ /* TV2000 XP Global [107d:6618] */
+ .subvendor = 0x107d,
+ .subdevice = 0x6619,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
+ }, {
+ /* WinFast TV2000 XP Global with XC4000 tuner */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f36,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36,
+ }, {
+ /* WinFast TV2000 XP Global with XC4000 tuner and different GPIOs */
+ .subvendor = 0x107d,
+ .subdevice = 0x6f43,
+ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43,
+ }, {
+ .subvendor = 0xb034,
+ .subdevice = 0x3034,
+ .card = CX88_BOARD_PROF_7301,
+ }, {
+ .subvendor = 0x1822,
+ .subdevice = 0x0023,
+ .card = CX88_BOARD_TWINHAN_VP1027_DVBS,
+ },
+};
+
+/* ----------------------------------------------------------------------- */
+/* some leadtek specific stuff */
+
+static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ if (eeprom_data[4] != 0x7d ||
+ eeprom_data[5] != 0x10 ||
+ eeprom_data[7] != 0x66) {
+ warn_printk(core, "Leadtek eeprom invalid.\n");
+ return;
+ }
+
+ /* Terry Wu <terrywu2009@gmail.com> */
+ switch (eeprom_data[6]) {
+ case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */
+ case 0x21: /* SSID 6621 for DV2000 NTSC Model */
+ case 0x31: /* SSID 6631 for PVR2000 NTSC Model */
+ case 0x37: /* SSID 6637 for PVR2000 NTSC Model */
+ case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */
+ core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3;
+ break;
+ default:
+ core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
+ break;
+ }
+
+ info_printk(core, "Leadtek Winfast 2000XP Expert config: "
+ "tuner=%d, eeprom[0]=0x%02x\n",
+ core->board.tuner_type, eeprom_data[0]);
+}
+
+static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data);
+ core->board.tuner_type = tv.tuner_type;
+ core->tuner_formats = tv.tuner_formats;
+ core->board.radio.type = tv.has_radio ? CX88_RADIO : 0;
+
+ /* Make sure we support the board model */
+ switch (tv.model)
+ {
+ case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */
+ case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */
+ case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */
+ case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */
+ case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */
+ case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */
+ case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */
+ case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */
+ case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
+ case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
+ case 34519: /* WinTV-PCI-FM */
+ case 69009:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
+ case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
+ case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
+ case 69559:
+ /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
+ case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
+ case 90002: /* Nova-T-PCI (9002) */
+ case 92001: /* Nova-S-Plus (Video and IR) */
+ case 92002: /* Nova-S-Plus (Video and IR) */
+ case 90003: /* Nova-T-PCI (9002 No RF out) */
+ case 90500: /* Nova-T-PCI (oem) */
+ case 90501: /* Nova-T-PCI (oem/IR) */
+ case 92000: /* Nova-SE2 (OEM, No Video or IR) */
+ case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */
+ case 94009: /* WinTV-HVR1100 (Video and IR Retail) */
+ case 94501: /* WinTV-HVR1100 (Video and IR OEM) */
+ case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */
+ case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */
+ case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */
+ case 96569: /* WinTV-HVR1300 () */
+ case 96659: /* WinTV-HVR1300 () */
+ case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
+ /* known */
+ break;
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ cx_set(MO_GP0_IO, 0x008989FF);
+ break;
+ default:
+ warn_printk(core, "warning: unknown hauppauge model #%d\n",
+ tv.model);
+ break;
+ }
+
+ info_printk(core, "hauppauge eeprom: model=%d\n", tv.model);
+}
+
+/* ----------------------------------------------------------------------- */
+/* some GDI (was: Modular Technology) specific stuff */
+
+static const struct {
+ int id;
+ int fm;
+ const char *name;
+} gdi_tuner[] = {
+ [ 0x01 ] = { .id = TUNER_ABSENT,
+ .name = "NTSC_M" },
+ [ 0x02 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_B" },
+ [ 0x03 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_I" },
+ [ 0x04 ] = { .id = TUNER_ABSENT,
+ .name = "PAL_D" },
+ [ 0x05 ] = { .id = TUNER_ABSENT,
+ .name = "SECAM" },
+
+ [ 0x10 ] = { .id = TUNER_ABSENT,
+ .fm = 1,
+ .name = "TEMIC_4049" },
+ [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5,
+ .name = "TEMIC_4136" },
+ [ 0x12 ] = { .id = TUNER_ABSENT,
+ .name = "TEMIC_4146" },
+
+ [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME,
+ .fm = 1,
+ .name = "PHILIPS_FQ1216_MK3" },
+ [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1,
+ .name = "PHILIPS_FQ1236_MK3" },
+ [ 0x22 ] = { .id = TUNER_ABSENT,
+ .name = "PHILIPS_FI1236_MK3" },
+ [ 0x23 ] = { .id = TUNER_ABSENT,
+ .name = "PHILIPS_FI1216_MK3" },
+};
+
+static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data)
+{
+ const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
+ ? gdi_tuner[eeprom_data[0x0d]].name : NULL;
+
+ info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown");
+ if (NULL == name)
+ return;
+ core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id;
+ core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ?
+ CX88_RADIO : 0;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff */
+static int cx88_dvico_xc2028_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ /* GPIO-4 xc3028 tuner */
+
+ cx_set(MO_GP0_IO, 0x00001000);
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ cx_set(MO_GP0_IO, 0x00000010);
+ msleep(100);
+ break;
+ default:
+ cx_write(MO_GP0_IO, 0x101000);
+ mdelay(5);
+ cx_set(MO_GP0_IO, 0x101010);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* some Geniatech specific stuff */
+
+static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core,
+ int command, int mode)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (INPUT(core->input).type) {
+ case CX88_RADIO:
+ break;
+ case CX88_VMUX_DVB:
+ cx_write(MO_GP1_IO, 0x030302);
+ mdelay(50);
+ break;
+ default:
+ cx_write(MO_GP1_IO, 0x030301);
+ mdelay(50);
+ }
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(50);
+ cx_write(MO_GP1_IO, 0x101000);
+ mdelay(50);
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(50);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ /* GPIO 12 (xc3028 tuner reset) */
+ cx_set(MO_GP1_IO, 0x1010);
+ mdelay(50);
+ cx_clear(MO_GP1_IO, 0x10);
+ mdelay(75);
+ cx_set(MO_GP1_IO, 0x10);
+ mdelay(75);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC4000_TUNER_RESET:
+ /* GPIO 12 (xc4000 tuner reset) */
+ cx_set(MO_GP1_IO, 0x1010);
+ mdelay(50);
+ cx_clear(MO_GP1_IO, 0x10);
+ mdelay(75);
+ cx_set(MO_GP1_IO, 0x10);
+ mdelay(75);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* ------------------------------------------------------------------- */
+/* some Divco specific stuff */
+static int cx88_pv_8000gt_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ cx_write(MO_GP2_IO, 0xcf7);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xef5);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xcf7);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+/* some DViCO specific stuff */
+
+static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
+{
+ struct i2c_msg msg = { .addr = 0x45, .flags = 0 };
+ int i, err;
+ static u8 init_bufs[13][5] = {
+ { 0x10, 0x00, 0x20, 0x01, 0x03 },
+ { 0x10, 0x10, 0x01, 0x00, 0x21 },
+ { 0x10, 0x10, 0x10, 0x00, 0xCA },
+ { 0x10, 0x10, 0x12, 0x00, 0x08 },
+ { 0x10, 0x10, 0x13, 0x00, 0x0A },
+ { 0x10, 0x10, 0x16, 0x01, 0xC0 },
+ { 0x10, 0x10, 0x22, 0x01, 0x3D },
+ { 0x10, 0x10, 0x73, 0x01, 0x2E },
+ { 0x10, 0x10, 0x72, 0x00, 0xC5 },
+ { 0x10, 0x10, 0x71, 0x01, 0x97 },
+ { 0x10, 0x10, 0x70, 0x00, 0x0F },
+ { 0x10, 0x10, 0xB0, 0x00, 0x01 },
+ { 0x03, 0x0C },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
+ msg.buf = init_bufs[i];
+ msg.len = (i != 12 ? 5 : 2);
+ err = i2c_transfer(&core->i2c_adap, &msg, 1);
+ if (err != 1) {
+ warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d "
+ "failed (err = %d)!\n", i, err);
+ return;
+ }
+ }
+}
+
+static int cx88_xc2028_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ /* Board-specific callbacks */
+ switch (core->boardnr) {
+ case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ case CX88_BOARD_KWORLD_ATSC_120:
+ return cx88_xc3028_geniatech_tuner_callback(core,
+ command, arg);
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ return cx88_pv_8000gt_callback(core, command, arg);
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ return cx88_dvico_xc2028_callback(core, command, arg);
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ return cx88_xc3028_winfast1800h_callback(core, command, arg);
+ }
+
+ switch (command) {
+ case XC2028_TUNER_RESET:
+ switch (INPUT(core->input).type) {
+ case CX88_RADIO:
+ info_printk(core, "setting GPIO to radio!\n");
+ cx_write(MO_GP0_IO, 0x4ff);
+ mdelay(250);
+ cx_write(MO_GP2_IO, 0xff);
+ mdelay(250);
+ break;
+ case CX88_VMUX_DVB: /* Digital TV*/
+ default: /* Analog TV */
+ info_printk(core, "setting GPIO to TV!\n");
+ break;
+ }
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(250);
+ cx_write(MO_GP1_IO, 0x101000);
+ mdelay(250);
+ cx_write(MO_GP1_IO, 0x101010);
+ mdelay(250);
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int cx88_xc4000_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ /* Board-specific callbacks */
+ switch (core->boardnr) {
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ return cx88_xc4000_winfast2000h_plus_callback(core,
+ command, arg);
+ }
+ return -EINVAL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* Tuner callback function. Currently only needed for the Pinnacle *
+ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
+ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */
+
+static int cx88_xc5000_tuner_callback(struct cx88_core *core,
+ int command, int arg)
+{
+ switch (core->boardnr) {
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ if (command == 0) { /* This is the reset command from xc5000 */
+
+ /* djh - According to the engineer at PCTV Systems,
+ the xc5000 reset pin is supposed to be on GPIO12.
+ However, despite three nights of effort, pulling
+ that GPIO low didn't reset the xc5000. While
+ pulling MO_SRST_IO low does reset the xc5000, this
+ also resets in the s5h1409 being reset as well.
+ This causes tuning to always fail since the internal
+ state of the s5h1409 does not match the driver's
+ state. Given that the only two conditions in which
+ the driver performs a reset is during firmware load
+ and powering down the chip, I am taking out the
+ reset. We know that the chip is being reset
+ when the cx88 comes online, and not being able to
+ do power management for this board is worse than
+ not having any tuning at all. */
+ return 0;
+ } else {
+ err_printk(core, "xc5000: unknown tuner "
+ "callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ if (command == 0) { /* This is the reset command from xc5000 */
+ cx_clear(MO_GP0_IO, 0x00000010);
+ msleep(10);
+ cx_set(MO_GP0_IO, 0x00000010);
+ return 0;
+ } else {
+ printk(KERN_ERR
+ "xc5000: unknown tuner callback command.\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0; /* Should never be here */
+}
+
+int cx88_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct i2c_algo_bit_data *i2c_algo = priv;
+ struct cx88_core *core;
+
+ if (!i2c_algo) {
+ printk(KERN_ERR "cx88: Error - i2c private data undefined.\n");
+ return -EINVAL;
+ }
+
+ core = i2c_algo->data;
+
+ if (!core) {
+ printk(KERN_ERR "cx88: Error - device struct undefined.\n");
+ return -EINVAL;
+ }
+
+ if (component != DVB_FRONTEND_COMPONENT_TUNER)
+ return -EINVAL;
+
+ switch (core->board.tuner_type) {
+ case TUNER_XC2028:
+ info_printk(core, "Calling XC2028/3028 callback\n");
+ return cx88_xc2028_tuner_callback(core, command, arg);
+ case TUNER_XC4000:
+ info_printk(core, "Calling XC4000 callback\n");
+ return cx88_xc4000_tuner_callback(core, command, arg);
+ case TUNER_XC5000:
+ info_printk(core, "Calling XC5000 callback\n");
+ return cx88_xc5000_tuner_callback(core, command, arg);
+ }
+ err_printk(core, "Error: Calling callback for tuner %d\n",
+ core->board.tuner_type);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(cx88_tuner_callback);
+
+/* ----------------------------------------------------------------------- */
+
+static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
+{
+ int i;
+
+ if (0 == pci->subsystem_vendor &&
+ 0 == pci->subsystem_device) {
+ printk(KERN_ERR
+ "%s: Your board has no valid PCI Subsystem ID and thus can't\n"
+ "%s: be autodetected. Please pass card=<n> insmod option to\n"
+ "%s: workaround that. Redirect complaints to the vendor of\n"
+ "%s: the TV card. Best regards,\n"
+ "%s: -- tux\n",
+ core->name,core->name,core->name,core->name,core->name);
+ } else {
+ printk(KERN_ERR
+ "%s: Your board isn't known (yet) to the driver. You can\n"
+ "%s: try to pick one of the existing card configs via\n"
+ "%s: card=<n> insmod option. Updating to the latest\n"
+ "%s: version might help as well.\n",
+ core->name,core->name,core->name,core->name);
+ }
+ err_printk(core, "Here is a list of valid choices for the card=<n> "
+ "insmod option:\n");
+ for (i = 0; i < ARRAY_SIZE(cx88_boards); i++)
+ printk(KERN_ERR "%s: card=%d -> %s\n",
+ core->name, i, cx88_boards[i].name);
+}
+
+static void cx88_card_setup_pre_i2c(struct cx88_core *core)
+{
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /*
+ * Bring the 702 demod up before i2c scanning/attach or devices are hidden
+ * We leave here with the 702 on the bus
+ *
+ * "reset the IR receiver on GPIO[3]"
+ * Reported by Mike Crash <mike AT mikecrash.com>
+ */
+ cx_write(MO_GP0_IO, 0x0000ef88);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000088);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ cx_write(MO_GP2_IO, 0xcf7);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xef5);
+ mdelay(50);
+ cx_write(MO_GP2_IO, 0xcf7);
+ msleep(10);
+ break;
+
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ /* Enable the xc5000 tuner */
+ cx_set(MO_GP0_IO, 0x00001010);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV2000H_J:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* Init GPIO */
+ cx_write(MO_GP0_IO, core->board.input[0].gpio0);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
+ cx88_xc4000_winfast2000h_plus_callback(core,
+ XC4000_TUNER_RESET, 0);
+ break;
+
+ case CX88_BOARD_TWINHAN_VP1027_DVBS:
+ cx_write(MO_GP0_IO, 0x00003230);
+ cx_write(MO_GP0_IO, 0x00003210);
+ msleep(1);
+ cx_write(MO_GP0_IO, 0x00001230);
+ break;
+ }
+}
+
+/*
+ * Sets board-dependent xc3028 configuration
+ */
+void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
+{
+ memset(ctl, 0, sizeof(*ctl));
+
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ ctl->max_len = 64;
+
+ switch (core->boardnr) {
+ case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
+ /* Now works with firmware version 2.7 */
+ if (core->i2c_algo.udelay < 16)
+ core->i2c_algo.udelay = 16;
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case CX88_BOARD_KWORLD_ATSC_120:
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ ctl->demod = XC3028_FE_OREN538;
+ break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ /* FIXME: For this board, the xc3028 never recovers after being
+ powered down (the reset GPIO probably is not set properly).
+ We don't have access to the hardware so we cannot determine
+ which GPIO is used for xc3028, so just disable power xc3028
+ power management for now */
+ ctl->disable_power_mgmt = 1;
+ break;
+ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
+ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
+ case CX88_BOARD_PROLINK_PV_8000GT:
+ /*
+ * Those boards uses non-MTS firmware
+ */
+ break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ ctl->mts = 1;
+ break;
+ default:
+ ctl->demod = XC3028_FE_OREN538;
+ ctl->mts = 1;
+ }
+}
+EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
+
+static void cx88_card_setup(struct cx88_core *core)
+{
+ static u8 eeprom[256];
+ struct tuner_setup tun_setup;
+ unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+
+ if (0 == core->i2c_rc) {
+ core->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
+ }
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE:
+ case CX88_BOARD_HAUPPAUGE_ROSLYN:
+ if (0 == core->i2c_rc)
+ hauppauge_eeprom(core, eeprom+8);
+ break;
+ case CX88_BOARD_GDI:
+ if (0 == core->i2c_rc)
+ gdi_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_LEADTEK_PVR2000:
+ case CX88_BOARD_WINFAST_DV2000:
+ case CX88_BOARD_WINFAST2000XP_EXPERT:
+ if (0 == core->i2c_rc)
+ leadtek_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ case CX88_BOARD_HAUPPAUGE_IRONLY:
+ if (0 == core->i2c_rc)
+ hauppauge_eeprom(core, eeprom);
+ break;
+ case CX88_BOARD_KWORLD_DVBS_100:
+ cx_write(MO_GP0_IO, 0x000007f8);
+ cx_write(MO_GP1_IO, 0x00000001);
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ /* GPIO0:0 is hooked to demod reset */
+ /* GPIO0:4 is hooked to xc3028 reset */
+ cx_write(MO_GP0_IO, 0x00111100);
+ msleep(1);
+ cx_write(MO_GP0_IO, 0x00111111);
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+ /* GPIO0:6 is hooked to FX2 reset pin */
+ cx_set(MO_GP0_IO, 0x00004040);
+ cx_clear(MO_GP0_IO, 0x00000040);
+ msleep(1000);
+ cx_set(MO_GP0_IO, 0x00004040);
+ /* FALLTHROUGH */
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+ /* GPIO0:0 is hooked to mt352 reset pin */
+ cx_set(MO_GP0_IO, 0x00000101);
+ cx_clear(MO_GP0_IO, 0x00000001);
+ msleep(1);
+ cx_set(MO_GP0_IO, 0x00000101);
+ if (0 == core->i2c_rc &&
+ core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID)
+ dvico_fusionhdtv_hybrid_init(core);
+ break;
+ case CX88_BOARD_KWORLD_DVB_T:
+ case CX88_BOARD_DNTV_LIVE_DVB_T:
+ cx_set(MO_GP0_IO, 0x00000707);
+ cx_set(MO_GP2_IO, 0x00000101);
+ cx_clear(MO_GP2_IO, 0x00000001);
+ msleep(1);
+ cx_clear(MO_GP0_IO, 0x00000007);
+ cx_set(MO_GP2_IO, 0x00000101);
+ break;
+ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+ cx_write(MO_GP0_IO, 0x00080808);
+ break;
+ case CX88_BOARD_ATI_HDTVWONDER:
+ if (0 == core->i2c_rc) {
+ /* enable tuner */
+ int i;
+ static const u8 buffer [][2] = {
+ {0x10,0x12},
+ {0x13,0x04},
+ {0x16,0x00},
+ {0x14,0x04},
+ {0x17,0x00}
+ };
+ core->i2c_client.addr = 0x0a;
+
+ for (i = 0; i < ARRAY_SIZE(buffer); i++)
+ if (2 != i2c_master_send(&core->i2c_client,
+ buffer[i],2))
+ warn_printk(core, "Unable to enable "
+ "tuner(%i).\n", i);
+ }
+ break;
+ case CX88_BOARD_MSI_TVANYWHERE_MASTER:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+ struct tea5767_ctrl ctl;
+
+ memset(&ctl, 0, sizeof(ctl));
+
+ ctl.high_cut = 1;
+ ctl.st_noise = 1;
+ ctl.deemph_75 = 1;
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+
+ call_all(core, tuner, s_config, &tea5767_cfg);
+ break;
+ }
+ case CX88_BOARD_TEVII_S420:
+ case CX88_BOARD_TEVII_S460:
+ case CX88_BOARD_TEVII_S464:
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_PROF_7301:
+ case CX88_BOARD_SATTRADE_ST4200:
+ cx_write(MO_GP0_IO, 0x8000);
+ msleep(100);
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_GP0_IO, 0x8080);
+ msleep(100);
+ cx_write(MO_SRST_IO, 1);
+ msleep(100);
+ break;
+ } /*end switch() */
+
+
+ /* Setup tuners */
+ if ((core->board.radio_type != UNSET)) {
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+ call_all(core, tuner, s_type_addr, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+ if (core->board.tuner_type != TUNER_ABSENT) {
+ tun_setup.mode_mask = mode_mask;
+ tun_setup.type = core->board.tuner_type;
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+
+ call_all(core, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (core->board.tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+ call_all(core, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (core->board.tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ /* Fills device-dependent initialization parameters */
+ cx88_setup_xc3028(core, &ctl);
+
+ /* Sends parameters to xc2028/3028 tuner */
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+ info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
+ ctl.fname);
+ call_all(core, tuner, s_config, &xc2028_cfg);
+ }
+ call_all(core, core, s_power, 0);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_pci_quirks(const char *name, struct pci_dev *pci)
+{
+ unsigned int lat = UNSET;
+ u8 ctrl = 0;
+ u8 value;
+
+ /* check pci quirks */
+ if (pci_pci_problems & PCIPCI_TRITON) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_NATOMA) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_VIAETBF) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n",
+ name);
+ ctrl |= CX88X_EN_TBFX;
+ }
+ if (pci_pci_problems & PCIPCI_VSFX) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n",
+ name);
+ ctrl |= CX88X_EN_VSFX;
+ }
+#ifdef PCIPCI_ALIMAGIK
+ if (pci_pci_problems & PCIPCI_ALIMAGIK) {
+ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
+ name);
+ lat = 0x0A;
+ }
+#endif
+
+ /* check insmod options */
+ if (UNSET != latency)
+ lat = latency;
+
+ /* apply stuff */
+ if (ctrl) {
+ pci_read_config_byte(pci, CX88X_DEVCTRL, &value);
+ value |= ctrl;
+ pci_write_config_byte(pci, CX88X_DEVCTRL, value);
+ }
+ if (UNSET != lat) {
+ printk(KERN_INFO "%s: setting pci latency timer to %d\n",
+ name, latency);
+ pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency);
+ }
+ return 0;
+}
+
+int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci)
+{
+ if (request_mem_region(pci_resource_start(pci,0),
+ pci_resource_len(pci,0),
+ core->name))
+ return 0;
+ printk(KERN_ERR
+ "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n",
+ core->name, PCI_FUNC(pci->devfn),
+ (unsigned long long)pci_resource_start(pci, 0),
+ pci->subsystem_vendor, pci->subsystem_device);
+ return -EBUSY;
+}
+
+/* Allocate and initialize the cx88 core struct. One should hold the
+ * devlist mutex before calling this. */
+struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+{
+ struct cx88_core *core;
+ int i;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
+ if (core == NULL)
+ return NULL;
+
+ atomic_inc(&core->refcount);
+ core->pci_bus = pci->bus->number;
+ core->pci_slot = PCI_SLOT(pci->devfn);
+ core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT |
+ PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT |
+ PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT;
+ mutex_init(&core->lock);
+
+ core->nr = nr;
+ sprintf(core->name, "cx88[%d]", core->nr);
+
+ strcpy(core->v4l2_dev.name, core->name);
+ if (v4l2_device_register(NULL, &core->v4l2_dev)) {
+ kfree(core);
+ return NULL;
+ }
+
+ if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) {
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) {
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ if (0 != cx88_get_resources(core, pci)) {
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ /* PCI stuff */
+ cx88_pci_quirks(core->name, pci);
+ core->lmmio = ioremap(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ core->bmmio = (u8 __iomem *)core->lmmio;
+
+ if (core->lmmio == NULL) {
+ release_mem_region(pci_resource_start(pci, 0),
+ pci_resource_len(pci, 0));
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+
+ /* board config */
+ core->boardnr = UNSET;
+ if (card[core->nr] < ARRAY_SIZE(cx88_boards))
+ core->boardnr = card[core->nr];
+ for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++)
+ if (pci->subsystem_vendor == cx88_subids[i].subvendor &&
+ pci->subsystem_device == cx88_subids[i].subdevice)
+ core->boardnr = cx88_subids[i].card;
+ if (UNSET == core->boardnr) {
+ core->boardnr = CX88_BOARD_UNKNOWN;
+ cx88_card_list(core, pci);
+ }
+
+ memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board));
+
+ if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB))
+ core->board.num_frontends = 1;
+
+ info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n",
+ pci->subsystem_vendor, pci->subsystem_device, core->board.name,
+ core->boardnr, card[core->nr] == core->boardnr ?
+ "insmod option" : "autodetected",
+ core->board.num_frontends);
+
+ if (tuner[core->nr] != UNSET)
+ core->board.tuner_type = tuner[core->nr];
+ if (radio[core->nr] != UNSET)
+ core->board.radio_type = radio[core->nr];
+
+ info_printk(core, "TV tuner type %d, Radio tuner type %d\n",
+ core->board.tuner_type, core->board.radio_type);
+
+ /* init hardware */
+ cx88_reset(core);
+ cx88_card_setup_pre_i2c(core);
+ cx88_i2c_init(core, pci);
+
+ /* load tuner module, if needed */
+ if (TUNER_ABSENT != core->board.tuner_type) {
+ /* Ignore 0x6b and 0x6f on cx88 boards.
+ * FusionHDTV5 RT Gold has an ir receiver at 0x6b
+ * and an RTC at 0x6f which can get corrupted if probed. */
+ static const unsigned short tv_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e,
+ I2C_CLIENT_END
+ };
+ int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT);
+
+ /* I don't trust the radio_type as is stored in the card
+ definitions, so we just probe for it.
+ The radio_type is sometimes missing, or set to UNSET but
+ later code configures a tea5767.
+ */
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
+ if (has_demod)
+ v4l2_i2c_new_subdev(&core->v4l2_dev,
+ &core->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ if (core->board.tuner_addr == ADDR_UNSET) {
+ v4l2_i2c_new_subdev(&core->v4l2_dev,
+ &core->i2c_adap, "tuner",
+ 0, has_demod ? tv_addrs + 4 : tv_addrs);
+ } else {
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tuner", core->board.tuner_addr, NULL);
+ }
+ }
+
+ cx88_card_setup(core);
+ if (!disable_ir) {
+ cx88_i2c_init_ir(core);
+ cx88_ir_init(core, pci);
+ }
+
+ return core;
+}
diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c
new file mode 100644
index 000000000000..c97b174be3ab
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-core.c
@@ -0,0 +1,1131 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * driver core
+ *
+ * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - Multituner support
+ * - video_ioctl2 conversion
+ * - PAL/M fixes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/sound.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int core_debug;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+
+static unsigned int nicam;
+module_param(nicam,int,0644);
+MODULE_PARM_DESC(nicam,"tv audio is nicam");
+
+static unsigned int nocomb;
+module_param(nocomb,int,0644);
+MODULE_PARM_DESC(nocomb,"disable comb filter");
+
+#define dprintk(level,fmt, arg...) if (core_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
+static unsigned int cx88_devcount;
+static LIST_HEAD(cx88_devlist);
+static DEFINE_MUTEX(devlist);
+
+#define NO_SYNC_LINE (-1U)
+
+/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
+ generated _after_ lpi lines are transferred. */
+static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
+ unsigned int offset, u32 sync_line,
+ unsigned int bpl, unsigned int padding,
+ unsigned int lines, unsigned int lpi)
+{
+ struct scatterlist *sg;
+ unsigned int line,todo,sol;
+
+ /* sync instruction */
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+
+ /* scan lines */
+ sg = sglist;
+ for (line = 0; line < lines; line++) {
+ while (offset && offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ sg++;
+ }
+ if (lpi && line>0 && !(line % lpi))
+ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
+ else
+ sol = RISC_SOL;
+ if (bpl <= sg_dma_len(sg)-offset) {
+ /* fits into current chunk */
+ *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ offset+=bpl;
+ } else {
+ /* scanline needs to be split */
+ todo = bpl;
+ *(rp++)=cpu_to_le32(RISC_WRITE|sol|
+ (sg_dma_len(sg)-offset));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+ todo -= (sg_dma_len(sg)-offset);
+ offset = 0;
+ sg++;
+ while (todo > sg_dma_len(sg)) {
+ *(rp++)=cpu_to_le32(RISC_WRITE|
+ sg_dma_len(sg));
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ todo -= sg_dma_len(sg);
+ sg++;
+ }
+ *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
+ *(rp++)=cpu_to_le32(sg_dma_address(sg));
+ offset += todo;
+ }
+ offset += padding;
+ }
+
+ return rp;
+}
+
+int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines)
+{
+ u32 instructions,fields;
+ __le32 *rp;
+ int rc;
+
+ fields = 0;
+ if (UNSET != top_offset)
+ fields++;
+ if (UNSET != bottom_offset)
+ fields++;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Padding
+ can cause next bpl to start close to a page border. First DMA
+ region may be smaller than PAGE_SIZE */
+ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
+ instructions += 2;
+ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ if (UNSET != top_offset)
+ rp = cx88_risc_field(rp, sglist, top_offset, 0,
+ bpl, padding, lines, 0);
+ if (UNSET != bottom_offset)
+ rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
+ bpl, padding, lines, 0);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines, unsigned int lpi)
+{
+ u32 instructions;
+ __le32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords). Here
+ there is no padding and no sync. First DMA region may be smaller
+ than PAGE_SIZE */
+ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
+ instructions += 1;
+ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
+ return 0;
+}
+
+int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value)
+{
+ __le32 *rp;
+ int rc;
+
+ if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
+ *(rp++) = cpu_to_le32(reg);
+ *(rp++) = cpu_to_le32(value);
+ *(rp++) = cpu_to_le32(mask);
+ *(rp++) = cpu_to_le32(RISC_JUMP);
+ *(rp++) = cpu_to_le32(risc->dma);
+ return 0;
+}
+
+void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
+{
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(q, &buf->vb, 0, 0);
+ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* ------------------------------------------------------------------ */
+/* our SRAM memory layout */
+
+/* we are going to put all thr risc programs into host memory, so we
+ * can use the whole SDRAM for the DMA fifos. To simplify things, we
+ * use a static memory layout. That surely will waste memory in case
+ * we don't use all DMA channels at the same time (which will be the
+ * case most of the time). But that still gives us enough FIFO space
+ * to be able to deal with insane long pci latencies ...
+ *
+ * FIFO space allocations:
+ * channel 21 (y video) - 10.0k
+ * channel 22 (u video) - 2.0k
+ * channel 23 (v video) - 2.0k
+ * channel 24 (vbi) - 4.0k
+ * channels 25+26 (audio) - 4.0k
+ * channel 28 (mpeg) - 4.0k
+ * channel 27 (audio rds)- 3.0k
+ * TOTAL = 29.0k
+ *
+ * Every channel has 160 bytes control data (64 bytes instruction
+ * queue and 6 CDT entries), which is close to 2k total.
+ *
+ * Address layout:
+ * 0x0000 - 0x03ff CMDs / reserved
+ * 0x0400 - 0x0bff instruction queues + CDs
+ * 0x0c00 - FIFOs
+ */
+
+const struct sram_channel cx88_sram_channels[] = {
+ [SRAM_CH21] = {
+ .name = "video y / packed",
+ .cmds_start = 0x180040,
+ .ctrl_start = 0x180400,
+ .cdt = 0x180400 + 64,
+ .fifo_start = 0x180c00,
+ .fifo_size = 0x002800,
+ .ptr1_reg = MO_DMA21_PTR1,
+ .ptr2_reg = MO_DMA21_PTR2,
+ .cnt1_reg = MO_DMA21_CNT1,
+ .cnt2_reg = MO_DMA21_CNT2,
+ },
+ [SRAM_CH22] = {
+ .name = "video u",
+ .cmds_start = 0x180080,
+ .ctrl_start = 0x1804a0,
+ .cdt = 0x1804a0 + 64,
+ .fifo_start = 0x183400,
+ .fifo_size = 0x000800,
+ .ptr1_reg = MO_DMA22_PTR1,
+ .ptr2_reg = MO_DMA22_PTR2,
+ .cnt1_reg = MO_DMA22_CNT1,
+ .cnt2_reg = MO_DMA22_CNT2,
+ },
+ [SRAM_CH23] = {
+ .name = "video v",
+ .cmds_start = 0x1800c0,
+ .ctrl_start = 0x180540,
+ .cdt = 0x180540 + 64,
+ .fifo_start = 0x183c00,
+ .fifo_size = 0x000800,
+ .ptr1_reg = MO_DMA23_PTR1,
+ .ptr2_reg = MO_DMA23_PTR2,
+ .cnt1_reg = MO_DMA23_CNT1,
+ .cnt2_reg = MO_DMA23_CNT2,
+ },
+ [SRAM_CH24] = {
+ .name = "vbi",
+ .cmds_start = 0x180100,
+ .ctrl_start = 0x1805e0,
+ .cdt = 0x1805e0 + 64,
+ .fifo_start = 0x184400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA24_PTR1,
+ .ptr2_reg = MO_DMA24_PTR2,
+ .cnt1_reg = MO_DMA24_CNT1,
+ .cnt2_reg = MO_DMA24_CNT2,
+ },
+ [SRAM_CH25] = {
+ .name = "audio from",
+ .cmds_start = 0x180140,
+ .ctrl_start = 0x180680,
+ .cdt = 0x180680 + 64,
+ .fifo_start = 0x185400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA25_PTR1,
+ .ptr2_reg = MO_DMA25_PTR2,
+ .cnt1_reg = MO_DMA25_CNT1,
+ .cnt2_reg = MO_DMA25_CNT2,
+ },
+ [SRAM_CH26] = {
+ .name = "audio to",
+ .cmds_start = 0x180180,
+ .ctrl_start = 0x180720,
+ .cdt = 0x180680 + 64, /* same as audio IN */
+ .fifo_start = 0x185400, /* same as audio IN */
+ .fifo_size = 0x001000, /* same as audio IN */
+ .ptr1_reg = MO_DMA26_PTR1,
+ .ptr2_reg = MO_DMA26_PTR2,
+ .cnt1_reg = MO_DMA26_CNT1,
+ .cnt2_reg = MO_DMA26_CNT2,
+ },
+ [SRAM_CH28] = {
+ .name = "mpeg",
+ .cmds_start = 0x180200,
+ .ctrl_start = 0x1807C0,
+ .cdt = 0x1807C0 + 64,
+ .fifo_start = 0x186400,
+ .fifo_size = 0x001000,
+ .ptr1_reg = MO_DMA28_PTR1,
+ .ptr2_reg = MO_DMA28_PTR2,
+ .cnt1_reg = MO_DMA28_CNT1,
+ .cnt2_reg = MO_DMA28_CNT2,
+ },
+ [SRAM_CH27] = {
+ .name = "audio rds",
+ .cmds_start = 0x1801C0,
+ .ctrl_start = 0x180860,
+ .cdt = 0x180860 + 64,
+ .fifo_start = 0x187400,
+ .fifo_size = 0x000C00,
+ .ptr1_reg = MO_DMA27_PTR1,
+ .ptr2_reg = MO_DMA27_PTR2,
+ .cnt1_reg = MO_DMA27_CNT1,
+ .cnt2_reg = MO_DMA27_CNT2,
+ },
+};
+
+int cx88_sram_channel_setup(struct cx88_core *core,
+ const struct sram_channel *ch,
+ unsigned int bpl, u32 risc)
+{
+ unsigned int i,lines;
+ u32 cdt;
+
+ bpl = (bpl + 7) & ~7; /* alignment */
+ cdt = ch->cdt;
+ lines = ch->fifo_size / bpl;
+ if (lines > 6)
+ lines = 6;
+ BUG_ON(lines < 2);
+
+ /* write CDT */
+ for (i = 0; i < lines; i++)
+ cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
+
+ /* write CMDS */
+ cx_write(ch->cmds_start + 0, risc);
+ cx_write(ch->cmds_start + 4, cdt);
+ cx_write(ch->cmds_start + 8, (lines*16) >> 3);
+ cx_write(ch->cmds_start + 12, ch->ctrl_start);
+ cx_write(ch->cmds_start + 16, 64 >> 2);
+ for (i = 20; i < 64; i += 4)
+ cx_write(ch->cmds_start + i, 0);
+
+ /* fill registers */
+ cx_write(ch->ptr1_reg, ch->fifo_start);
+ cx_write(ch->ptr2_reg, cdt);
+ cx_write(ch->cnt1_reg, (bpl >> 3) -1);
+ cx_write(ch->cnt2_reg, (lines*16) >> 3);
+
+ dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* debug helper code */
+
+static int cx88_risc_decode(u32 risc)
+{
+ static const char * const instr[16] = {
+ [ RISC_SYNC >> 28 ] = "sync",
+ [ RISC_WRITE >> 28 ] = "write",
+ [ RISC_WRITEC >> 28 ] = "writec",
+ [ RISC_READ >> 28 ] = "read",
+ [ RISC_READC >> 28 ] = "readc",
+ [ RISC_JUMP >> 28 ] = "jump",
+ [ RISC_SKIP >> 28 ] = "skip",
+ [ RISC_WRITERM >> 28 ] = "writerm",
+ [ RISC_WRITECM >> 28 ] = "writecm",
+ [ RISC_WRITECR >> 28 ] = "writecr",
+ };
+ static int const incr[16] = {
+ [ RISC_WRITE >> 28 ] = 2,
+ [ RISC_JUMP >> 28 ] = 2,
+ [ RISC_WRITERM >> 28 ] = 3,
+ [ RISC_WRITECM >> 28 ] = 3,
+ [ RISC_WRITECR >> 28 ] = 4,
+ };
+ static const char * const bits[] = {
+ "12", "13", "14", "resync",
+ "cnt0", "cnt1", "18", "19",
+ "20", "21", "22", "23",
+ "irq1", "irq2", "eol", "sol",
+ };
+ int i;
+
+ printk("0x%08x [ %s", risc,
+ instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
+ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
+ if (risc & (1 << (i + 12)))
+ printk(" %s",bits[i]);
+ printk(" count=%d ]\n", risc & 0xfff);
+ return incr[risc >> 28] ? incr[risc >> 28] : 1;
+}
+
+
+void cx88_sram_channel_dump(struct cx88_core *core,
+ const struct sram_channel *ch)
+{
+ static const char * const name[] = {
+ "initial risc",
+ "cdt base",
+ "cdt size",
+ "iq base",
+ "iq size",
+ "risc pc",
+ "iq wr ptr",
+ "iq rd ptr",
+ "cdt current",
+ "pci target",
+ "line / byte",
+ };
+ u32 risc;
+ unsigned int i,j,n;
+
+ printk("%s: %s - dma channel status dump\n",
+ core->name,ch->name);
+ for (i = 0; i < ARRAY_SIZE(name); i++)
+ printk("%s: cmds: %-12s: 0x%08x\n",
+ core->name,name[i],
+ cx_read(ch->cmds_start + 4*i));
+ for (n = 1, i = 0; i < 4; i++) {
+ risc = cx_read(ch->cmds_start + 4 * (i+11));
+ printk("%s: risc%d: ", core->name, i);
+ if (--n)
+ printk("0x%08x [ arg #%d ]\n", risc, n);
+ else
+ n = cx88_risc_decode(risc);
+ }
+ for (i = 0; i < 16; i += n) {
+ risc = cx_read(ch->ctrl_start + 4 * i);
+ printk("%s: iq %x: ", core->name, i);
+ n = cx88_risc_decode(risc);
+ for (j = 1; j < n; j++) {
+ risc = cx_read(ch->ctrl_start + 4 * (i+j));
+ printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
+ core->name, i+j, risc, j);
+ }
+ }
+
+ printk("%s: fifo: 0x%08x -> 0x%x\n",
+ core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
+ printk("%s: ctrl: 0x%08x -> 0x%x\n",
+ core->name, ch->ctrl_start, ch->ctrl_start+6*16);
+ printk("%s: ptr1_reg: 0x%08x\n",
+ core->name,cx_read(ch->ptr1_reg));
+ printk("%s: ptr2_reg: 0x%08x\n",
+ core->name,cx_read(ch->ptr2_reg));
+ printk("%s: cnt1_reg: 0x%08x\n",
+ core->name,cx_read(ch->cnt1_reg));
+ printk("%s: cnt2_reg: 0x%08x\n",
+ core->name,cx_read(ch->cnt2_reg));
+}
+
+static const char *cx88_pci_irqs[32] = {
+ "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
+ "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
+ "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
+ "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
+};
+
+void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+ int len, u32 bits, u32 mask)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
+ for (i = 0; i < len; i++) {
+ if (!(bits & (1 << i)))
+ continue;
+ if (strings[i])
+ printk(" %s", strings[i]);
+ else
+ printk(" %d", i);
+ if (!(mask & (1 << i)))
+ continue;
+ printk("*");
+ }
+ printk("\n");
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_core_irq(struct cx88_core *core, u32 status)
+{
+ int handled = 0;
+
+ if (status & PCI_INT_IR_SMPINT) {
+ cx88_ir_irq(core);
+ handled++;
+ }
+ if (!handled)
+ cx88_print_irqbits(core->name, "irq pci",
+ cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
+ status, core->pci_irqmask);
+ return handled;
+}
+
+void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count)
+{
+ struct cx88_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx88_buffer, vb.queue);
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
+ count, buf->count);
+ buf->vb.state = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ dprintk(2, "%s: %d buffers handled (should be 1)\n",
+ __func__, bc);
+}
+
+void cx88_shutdown(struct cx88_core *core)
+{
+ /* disable RISC controller + IRQs */
+ cx_write(MO_DEV_CNTRL2, 0);
+
+ /* stop dma transfers */
+ cx_write(MO_VID_DMACNTRL, 0x0);
+ cx_write(MO_AUD_DMACNTRL, 0x0);
+ cx_write(MO_TS_DMACNTRL, 0x0);
+ cx_write(MO_VIP_DMACNTRL, 0x0);
+ cx_write(MO_GPHST_DMACNTRL, 0x0);
+
+ /* stop interrupts */
+ cx_write(MO_PCI_INTMSK, 0x0);
+ cx_write(MO_VID_INTMSK, 0x0);
+ cx_write(MO_AUD_INTMSK, 0x0);
+ cx_write(MO_TS_INTMSK, 0x0);
+ cx_write(MO_VIP_INTMSK, 0x0);
+ cx_write(MO_GPHST_INTMSK, 0x0);
+
+ /* stop capturing */
+ cx_write(VID_CAPTURE_CONTROL, 0);
+}
+
+int cx88_reset(struct cx88_core *core)
+{
+ dprintk(1,"%s\n",__func__);
+ cx88_shutdown(core);
+
+ /* clear irq status */
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* wait a bit */
+ msleep(100);
+
+ /* init sram */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
+
+ /* misc init ... */
+ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
+ (1 << 12) | // agc gain
+ (1 << 11) | // adaptibe agc
+ (0 << 10) | // chroma agc
+ (0 << 9) | // ckillen
+ (7)));
+
+ /* setup image format */
+ cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
+
+ /* setup FIFO Thresholds */
+ cx_write(MO_PDMA_STHRSH, 0x0807);
+ cx_write(MO_PDMA_DTHRSH, 0x0807);
+
+ /* fixes flashing of image */
+ cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
+ cx_write(MO_AGC_BACK_VBI, 0x00E00555);
+
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* Reset on-board parts */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int inline norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+static unsigned int inline norm_hdelay(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
+}
+
+static unsigned int inline norm_vdelay(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
+}
+
+static unsigned int inline norm_fsc8(v4l2_std_id norm)
+{
+ if (norm & V4L2_STD_PAL_M)
+ return 28604892; // 3.575611 MHz
+
+ if (norm & (V4L2_STD_PAL_Nc))
+ return 28656448; // 3.582056 MHz
+
+ if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
+ return 28636360; // 3.57954545 MHz +/- 10 Hz
+
+ /* SECAM have also different sub carrier for chroma,
+ but step_db and step_dr, at cx88_set_tvnorm already handles that.
+
+ The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
+ */
+
+ return 35468950; // 4.43361875 MHz +/- 5 Hz
+}
+
+static unsigned int inline norm_htotal(v4l2_std_id norm)
+{
+
+ unsigned int fsc4=norm_fsc8(norm)/2;
+
+ /* returns 4*FSC / vtotal / frames per seconds */
+ return (norm & V4L2_STD_625_50) ?
+ ((fsc4+312)/625+12)/25 :
+ ((fsc4+262)/525*1001+15000)/30000;
+}
+
+static unsigned int inline norm_vbipack(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 511 : 400;
+}
+
+int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ unsigned int swidth = norm_swidth(core->tvnorm);
+ unsigned int sheight = norm_maxh(core->tvnorm);
+ u32 value;
+
+ dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
+ V4L2_FIELD_HAS_TOP(field) ? "T" : "",
+ V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
+ v4l2_norm_to_name(core->tvnorm));
+ if (!V4L2_FIELD_HAS_BOTH(field))
+ height *= 2;
+
+ // recalc H delay and scale registers
+ value = (width * norm_hdelay(core->tvnorm)) / swidth;
+ value &= 0x3fe;
+ cx_write(MO_HDELAY_EVEN, value);
+ cx_write(MO_HDELAY_ODD, value);
+ dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
+
+ value = (swidth * 4096 / width) - 4096;
+ cx_write(MO_HSCALE_EVEN, value);
+ cx_write(MO_HSCALE_ODD, value);
+ dprintk(1,"set_scale: hscale 0x%04x\n", value);
+
+ cx_write(MO_HACTIVE_EVEN, width);
+ cx_write(MO_HACTIVE_ODD, width);
+ dprintk(1,"set_scale: hactive 0x%04x\n", width);
+
+ // recalc V scale Register (delay is constant)
+ cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
+ cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
+ dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
+
+ value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
+ cx_write(MO_VSCALE_EVEN, value);
+ cx_write(MO_VSCALE_ODD, value);
+ dprintk(1,"set_scale: vscale 0x%04x\n", value);
+
+ cx_write(MO_VACTIVE_EVEN, sheight);
+ cx_write(MO_VACTIVE_ODD, sheight);
+ dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
+
+ // setup filters
+ value = 0;
+ value |= (1 << 19); // CFILT (default)
+ if (core->tvnorm & V4L2_STD_SECAM) {
+ value |= (1 << 15);
+ value |= (1 << 16);
+ }
+ if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
+ value |= (1 << 13) | (1 << 5);
+ if (V4L2_FIELD_INTERLACED == field)
+ value |= (1 << 3); // VINT (interlaced vertical scaling)
+ if (width < 385)
+ value |= (1 << 0); // 3-tap interpolation
+ if (width < 193)
+ value |= (1 << 1); // 5-tap interpolation
+ if (nocomb)
+ value |= (3 << 5); // disable comb filter
+
+ cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */
+ cx_andor(MO_FILTER_ODD, 0x7ffc7f, value);
+ dprintk(1,"set_scale: filter 0x%04x\n", value);
+
+ return 0;
+}
+
+static const u32 xtal = 28636363;
+
+static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
+{
+ static const u32 pre[] = { 0, 0, 0, 3, 2, 1 };
+ u64 pll;
+ u32 reg;
+ int i;
+
+ if (prescale < 2)
+ prescale = 2;
+ if (prescale > 5)
+ prescale = 5;
+
+ pll = ofreq * 8 * prescale * (u64)(1 << 20);
+ do_div(pll,xtal);
+ reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
+ if (((reg >> 20) & 0x3f) < 14) {
+ printk("%s/0: pll out of range\n",core->name);
+ return -1;
+ }
+
+ dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
+ reg, cx_read(MO_PLL_REG), ofreq);
+ cx_write(MO_PLL_REG, reg);
+ for (i = 0; i < 100; i++) {
+ reg = cx_read(MO_DEVICE_STATUS);
+ if (reg & (1<<2)) {
+ dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
+ prescale,ofreq);
+ return 0;
+ }
+ dprintk(1,"pll not locked yet, waiting ...\n");
+ msleep(10);
+ }
+ dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
+ return -1;
+}
+
+int cx88_start_audio_dma(struct cx88_core *core)
+{
+ /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
+ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
+
+ int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
+
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
+ rds_bpl, 0);
+
+ cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
+ cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
+
+ /* enable Up, Down and Audio RDS fifo */
+ cx_write(MO_AUD_DMACNTRL, 0x0007);
+
+ return 0;
+}
+
+int cx88_stop_audio_dma(struct cx88_core *core)
+{
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
+ /* stop dma */
+ cx_write(MO_AUD_DMACNTRL, 0x0000);
+
+ return 0;
+}
+
+static int set_tvaudio(struct cx88_core *core)
+{
+ v4l2_std_id norm = core->tvnorm;
+
+ if (CX88_VMUX_TELEVISION != INPUT(core->input).type &&
+ CX88_VMUX_CABLE != INPUT(core->input).type)
+ return 0;
+
+ if (V4L2_STD_PAL_BG & norm) {
+ core->tvaudio = WW_BG;
+
+ } else if (V4L2_STD_PAL_DK & norm) {
+ core->tvaudio = WW_DK;
+
+ } else if (V4L2_STD_PAL_I & norm) {
+ core->tvaudio = WW_I;
+
+ } else if (V4L2_STD_SECAM_L & norm) {
+ core->tvaudio = WW_L;
+
+ } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
+ core->tvaudio = WW_BG;
+
+ } else if (V4L2_STD_SECAM_DK & norm) {
+ core->tvaudio = WW_DK;
+
+ } else if ((V4L2_STD_NTSC_M & norm) ||
+ (V4L2_STD_PAL_M & norm)) {
+ core->tvaudio = WW_BTSC;
+
+ } else if (V4L2_STD_NTSC_M_JP & norm) {
+ core->tvaudio = WW_EIAJ;
+
+ } else {
+ printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
+ core->name, v4l2_norm_to_name(core->tvnorm));
+ core->tvaudio = WW_NONE;
+ return 0;
+ }
+
+ cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
+ cx88_set_tvaudio(core);
+ /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
+
+/*
+ This should be needed only on cx88-alsa. It seems that some cx88 chips have
+ bugs and does require DMA enabled for it to work.
+ */
+ cx88_start_audio_dma(core);
+ return 0;
+}
+
+
+
+int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
+{
+ u32 fsc8;
+ u32 adc_clock;
+ u32 vdec_clock;
+ u32 step_db,step_dr;
+ u64 tmp64;
+ u32 bdelay,agcdelay,htotal;
+ u32 cxiformat, cxoformat;
+
+ core->tvnorm = norm;
+ fsc8 = norm_fsc8(norm);
+ adc_clock = xtal;
+ vdec_clock = fsc8;
+ step_db = fsc8;
+ step_dr = fsc8;
+
+ if (norm & V4L2_STD_NTSC_M_JP) {
+ cxiformat = VideoFormatNTSCJapan;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_NTSC_443) {
+ cxiformat = VideoFormatNTSC443;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_PAL_M) {
+ cxiformat = VideoFormatPALM;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_N) {
+ cxiformat = VideoFormatPALN;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_Nc) {
+ cxiformat = VideoFormatPALNC;
+ cxoformat = 0x1c1f0008;
+ } else if (norm & V4L2_STD_PAL_60) {
+ cxiformat = VideoFormatPAL60;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_NTSC) {
+ cxiformat = VideoFormatNTSC;
+ cxoformat = 0x181f0008;
+ } else if (norm & V4L2_STD_SECAM) {
+ step_db = 4250000 * 8;
+ step_dr = 4406250 * 8;
+
+ cxiformat = VideoFormatSECAM;
+ cxoformat = 0x181f0008;
+ } else { /* PAL */
+ cxiformat = VideoFormatPAL;
+ cxoformat = 0x181f0008;
+ }
+
+ dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
+ v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
+ step_db, step_dr);
+ set_pll(core,2,vdec_clock);
+
+ dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
+ /* Chroma AGC must be disabled if SECAM is used, we enable it
+ by default on PAL and NTSC */
+ cx_andor(MO_INPUT_FORMAT, 0x40f,
+ norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
+
+ // FIXME: as-is from DScaler
+ dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ cxoformat, cx_read(MO_OUTPUT_FORMAT));
+ cx_write(MO_OUTPUT_FORMAT, cxoformat);
+
+ // MO_SCONV_REG = adc clock / video dec clock * 2^17
+ tmp64 = adc_clock * (u64)(1 << 17);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SCONV_REG));
+ cx_write(MO_SCONV_REG, (u32)tmp64);
+
+ // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
+ tmp64 = step_db * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP));
+ cx_write(MO_SUB_STEP, (u32)tmp64);
+
+ // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
+ tmp64 = step_dr * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP_DR));
+ cx_write(MO_SUB_STEP_DR, (u32)tmp64);
+
+ // bdelay + agcdelay
+ bdelay = vdec_clock * 65 / 20000000 + 21;
+ agcdelay = vdec_clock * 68 / 20000000 + 15;
+ dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
+ (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
+ cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
+
+ // htotal
+ tmp64 = norm_htotal(norm) * (u64)vdec_clock;
+ do_div(tmp64, fsc8);
+ htotal = (u32)tmp64;
+ dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
+ htotal, cx_read(MO_HTOTAL), (u32)tmp64);
+ cx_andor(MO_HTOTAL, 0x07ff, htotal);
+
+ // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
+ // the effective vbi offset ~244 samples, the same as the Bt8x8
+ cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
+
+ // this is needed as well to set all tvnorm parameter
+ cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
+
+ // audio
+ set_tvaudio(core);
+
+ // tell i2c chips
+ call_all(core, core, s_std, norm);
+
+ /* The chroma_agc control should be inaccessible if the video format is SECAM */
+ v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
+
+ // done
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+struct video_device *cx88_vdev_init(struct cx88_core *core,
+ struct pci_dev *pci,
+ const struct video_device *template_,
+ const char *type)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template_;
+ vfd->v4l2_dev = &core->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ core->name, type, core->board.name);
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ return vfd;
+}
+
+struct cx88_core* cx88_core_get(struct pci_dev *pci)
+{
+ struct cx88_core *core;
+
+ mutex_lock(&devlist);
+ list_for_each_entry(core, &cx88_devlist, devlist) {
+ if (pci->bus->number != core->pci_bus)
+ continue;
+ if (PCI_SLOT(pci->devfn) != core->pci_slot)
+ continue;
+
+ if (0 != cx88_get_resources(core, pci)) {
+ mutex_unlock(&devlist);
+ return NULL;
+ }
+ atomic_inc(&core->refcount);
+ mutex_unlock(&devlist);
+ return core;
+ }
+
+ core = cx88_core_create(pci, cx88_devcount);
+ if (NULL != core) {
+ cx88_devcount++;
+ list_add_tail(&core->devlist, &cx88_devlist);
+ }
+
+ mutex_unlock(&devlist);
+ return core;
+}
+
+void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
+{
+ release_mem_region(pci_resource_start(pci,0),
+ pci_resource_len(pci,0));
+
+ if (!atomic_dec_and_test(&core->refcount))
+ return;
+
+ mutex_lock(&devlist);
+ cx88_ir_fini(core);
+ if (0 == core->i2c_rc) {
+ if (core->i2c_rtc)
+ i2c_unregister_device(core->i2c_rtc);
+ i2c_del_adapter(&core->i2c_adap);
+ }
+ list_del(&core->devlist);
+ iounmap(core->lmmio);
+ cx88_devcount--;
+ mutex_unlock(&devlist);
+ v4l2_ctrl_handler_free(&core->video_hdl);
+ v4l2_ctrl_handler_free(&core->audio_hdl);
+ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+}
+
+/* ------------------------------------------------------------------ */
+
+EXPORT_SYMBOL(cx88_print_irqbits);
+
+EXPORT_SYMBOL(cx88_core_irq);
+EXPORT_SYMBOL(cx88_wakeup);
+EXPORT_SYMBOL(cx88_reset);
+EXPORT_SYMBOL(cx88_shutdown);
+
+EXPORT_SYMBOL(cx88_risc_buffer);
+EXPORT_SYMBOL(cx88_risc_databuffer);
+EXPORT_SYMBOL(cx88_risc_stopper);
+EXPORT_SYMBOL(cx88_free_buffer);
+
+EXPORT_SYMBOL(cx88_sram_channels);
+EXPORT_SYMBOL(cx88_sram_channel_setup);
+EXPORT_SYMBOL(cx88_sram_channel_dump);
+
+EXPORT_SYMBOL(cx88_set_tvnorm);
+EXPORT_SYMBOL(cx88_set_scale);
+
+EXPORT_SYMBOL(cx88_vdev_init);
+EXPORT_SYMBOL(cx88_core_get);
+EXPORT_SYMBOL(cx88_core_put);
+
+EXPORT_SYMBOL(cx88_ir_start);
+EXPORT_SYMBOL(cx88_ir_stop);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
+ */
diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/pci/cx88/cx88-dsp.c
index a9907265ff66..a9907265ff66 100644
--- a/drivers/media/video/cx88/cx88-dsp.c
+++ b/drivers/media/pci/cx88/cx88-dsp.c
diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c
new file mode 100644
index 000000000000..d803bba09525
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-dvb.c
@@ -0,0 +1,1778 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * MPEG Transport Stream (DVB) routines
+ *
+ * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx88.h"
+#include "dvb-pll.h"
+#include <media/v4l2-common.h>
+
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "cx88-vp3054-i2c.h"
+#include "zl10353.h"
+#include "cx22702.h"
+#include "or51132.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "xc4000.h"
+#include "xc5000.h"
+#include "nxt200x.h"
+#include "cx24123.h"
+#include "isl6421.h"
+#include "tuner-simple.h"
+#include "tda9887.h"
+#include "s5h1411.h"
+#include "stv0299.h"
+#include "z0194a.h"
+#include "stv0288.h"
+#include "stb6000.h"
+#include "cx24116.h"
+#include "stv0900.h"
+#include "stb6100.h"
+#include "stb6100_proc.h"
+#include "mb86a16.h"
+#include "ds3000.h"
+
+MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
+
+static unsigned int dvb_buf_tscnt = 32;
+module_param(dvb_buf_tscnt, int, 0644);
+MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx8802_dev *dev = q->priv_data;
+
+ dev->ts_packet_size = 188 * 4;
+ dev->ts_packet_count = dvb_buf_tscnt;
+
+ *size = dev->ts_packet_size * dev->ts_packet_count;
+ *count = dvb_buf_tscnt;
+ return 0;
+}
+
+static int dvb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx8802_dev *dev = q->priv_data;
+ return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
+}
+
+static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx8802_dev *dev = q->priv_data;
+ cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ cx88_free_buffer(q, (struct cx88_buffer*)vb);
+}
+
+static const struct videobuf_queue_ops dvb_qops = {
+ .buf_setup = dvb_buf_setup,
+ .buf_prepare = dvb_buf_prepare,
+ .buf_queue = dvb_buf_queue,
+ .buf_release = dvb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx8802_driver *drv = NULL;
+ int ret = 0;
+ int fe_id;
+
+ fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
+ if (!fe_id) {
+ printk(KERN_ERR "%s() No frontend found\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->core->lock);
+ drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
+ if (drv) {
+ if (acquire){
+ dev->frontends.active_fe_id = fe_id;
+ ret = drv->request_acquire(drv);
+ } else {
+ ret = drv->request_release(drv);
+ dev->frontends.active_fe_id = 0;
+ }
+ }
+ mutex_unlock(&dev->core->lock);
+
+ return ret;
+}
+
+static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open)
+{
+ struct videobuf_dvb_frontends *f;
+ struct videobuf_dvb_frontend *fe;
+
+ if (!core->dvbdev)
+ return;
+
+ f = &core->dvbdev->frontends;
+
+ if (!f)
+ return;
+
+ if (f->gate <= 1) /* undefined or fe0 */
+ fe = videobuf_dvb_get_frontend(f, 1);
+ else
+ fe = videobuf_dvb_get_frontend(f, f->gate);
+
+ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
+ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
+}
+
+/* ------------------------------------------------------------------ */
+
+static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
+ static const u8 reset [] = { RESET, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static const u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 };
+ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
+ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ return 0;
+}
+
+static int dvico_dual_demod_init(struct dvb_frontend *fe)
+{
+ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 };
+ static const u8 reset [] = { RESET, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static const u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
+ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
+ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { 0x89, 0x38, 0x39 };
+ static const u8 reset [] = { 0x50, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static const u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00, 0x40, 0x40 };
+ static const u8 dntv_extra[] = { 0xB5, 0x7A };
+ static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(2000);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ udelay(2000);
+ mt352_write(fe, dntv_extra, sizeof(dntv_extra));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static const struct mt352_config dvico_fusionhdtv = {
+ .demod_address = 0x0f,
+ .demod_init = dvico_fusionhdtv_demod_init,
+};
+
+static const struct mt352_config dntv_live_dvbt_config = {
+ .demod_address = 0x0f,
+ .demod_init = dntv_live_dvbt_demod_init,
+};
+
+static const struct mt352_config dvico_fusionhdtv_dual = {
+ .demod_address = 0x0f,
+ .demod_init = dvico_dual_demod_init,
+};
+
+static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+};
+
+static struct mb86a16_config twinhan_vp1027 = {
+ .demod_address = 0x08,
+};
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
+{
+ static const u8 clock_config [] = { 0x89, 0x38, 0x38 };
+ static const u8 reset [] = { 0x50, 0x80 };
+ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
+ static const u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00, 0x40, 0x40 };
+ static const u8 dntv_extra[] = { 0xB5, 0x7A };
+ static const u8 capt_range_cfg[] = { 0x75, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(2000);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ udelay(2000);
+ mt352_write(fe, dntv_extra, sizeof(dntv_extra));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static const struct mt352_config dntv_live_dvbt_pro_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .demod_init = dntv_live_dvbt_pro_demod_init,
+};
+#endif
+
+static const struct zl10353_config dvico_fusionhdtv_hybrid = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 45600,
+ .no_tuner = 1,
+};
+
+static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
+ .demod_address = 0x0f,
+ .if2 = 4560,
+ .no_tuner = 1,
+ .demod_init = dvico_fusionhdtv_demod_init,
+};
+
+static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
+ .demod_address = 0x0f,
+};
+
+static const struct cx22702_config connexant_refboard_config = {
+ .demod_address = 0x43,
+ .output_mode = CX22702_SERIAL_OUTPUT,
+};
+
+static const struct cx22702_config hauppauge_hvr_config = {
+ .demod_address = 0x63,
+ .output_mode = CX22702_SERIAL_OUTPUT,
+};
+
+static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+ return 0;
+}
+
+static const struct or51132_config pchdtv_hd3000 = {
+ .demod_address = 0x15,
+ .set_ts_params = or51132_set_ts_param,
+};
+
+static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ dprintk(1, "%s: index = %d\n", __func__, index);
+ if (index == 0)
+ cx_clear(MO_GP0_IO, 8);
+ else
+ cx_set(MO_GP0_IO, 8);
+ return 0;
+}
+
+static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ if (is_punctured)
+ dev->ts_gen_cntrl |= 0x04;
+ else
+ dev->ts_gen_cntrl &= ~0x04;
+ return 0;
+}
+
+static struct lgdt330x_config fusionhdtv_3_gold = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+ .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config fusionhdtv_5_gold = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static const struct lgdt330x_config pchdtv_hd5500 = {
+ .demod_address = 0x59,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
+};
+
+static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
+ return 0;
+}
+
+static const struct nxt200x_config ati_hdtvwonder = {
+ .demod_address = 0x0a,
+ .set_ts_params = nxt200x_set_ts_param,
+};
+
+static int cx24123_set_ts_param(struct dvb_frontend* fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x02;
+ return 0;
+}
+
+static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ if (voltage == SEC_VOLTAGE_OFF)
+ cx_write(MO_GP0_IO, 0x000006fb);
+ else
+ cx_write(MO_GP0_IO, 0x000006f9);
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ if (voltage == SEC_VOLTAGE_OFF) {
+ dprintk(1,"LNB Voltage OFF\n");
+ cx_write(MO_GP0_IO, 0x0000efff);
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev= fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ cx_set(MO_GP0_IO, 0x6040);
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ cx_clear(MO_GP0_IO, 0x20);
+ break;
+ case SEC_VOLTAGE_18:
+ cx_set(MO_GP0_IO, 0x20);
+ break;
+ case SEC_VOLTAGE_OFF:
+ cx_clear(MO_GP0_IO, 0x20);
+ break;
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static int vp1027_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ dprintk(1, "LNB SEC Voltage=13\n");
+ cx_write(MO_GP0_IO, 0x00001220);
+ break;
+ case SEC_VOLTAGE_18:
+ dprintk(1, "LNB SEC Voltage=18\n");
+ cx_write(MO_GP0_IO, 0x00001222);
+ break;
+ case SEC_VOLTAGE_OFF:
+ dprintk(1, "LNB Voltage OFF\n");
+ cx_write(MO_GP0_IO, 0x00001230);
+ break;
+ }
+
+ if (core->prev_set_voltage)
+ return core->prev_set_voltage(fe, voltage);
+ return 0;
+}
+
+static const struct cx24123_config geniatech_dvbs_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config hauppauge_novas_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
+};
+
+static const struct cx24123_config kworld_dvbs_100_config = {
+ .demod_address = 0x15,
+ .set_ts_params = cx24123_set_ts_param,
+ .lnb_polarity = 1,
+};
+
+static const struct s5h1409_config pinnacle_pctv_hd_800i_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct s5h1409_config kworld_atsc_120_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+};
+
+static const struct zl10353_config cx88_pinnacle_hybrid_pctv = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+};
+
+static const struct zl10353_config cx88_geniatech_x8000_mt = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static const struct s5h1411_config dvico_fusionhdtv7_config = {
+ .output_mode = S5H1411_SERIAL_OUTPUT,
+ .gpio = S5H1411_GPIO_ON,
+ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+ .qam_if = S5H1411_IF_44000,
+ .vsb_if = S5H1411_IF_44000,
+ .inversion = S5H1411_INVERSION_OFF,
+ .status_mode = S5H1411_DEMODLOCKING
+};
+
+static const struct xc5000_config dvico_fusionhdtv7_tuner_config = {
+ .i2c_address = 0xc2 >> 1,
+ .if_khz = 5380,
+};
+
+static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
+{
+ struct dvb_frontend *fe;
+ struct videobuf_dvb_frontend *fe0 = NULL;
+ struct xc2028_ctrl ctl;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->core->i2c_adap,
+ .i2c_addr = addr,
+ .ctrl = &ctl,
+ };
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ if (!fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc3028\n",
+ dev->core->name);
+ return -EINVAL;
+ }
+
+ /*
+ * Some xc3028 devices may be hidden by an I2C gate. This is known
+ * to happen with some s5h1409-based devices.
+ * Now that I2C gate is open, sets up xc3028 configuration
+ */
+ cx88_setup_xc3028(dev->core, &ctl);
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+ dev->core->name);
+ dvb_frontend_detach(fe0->dvb.frontend);
+ dvb_unregister_frontend(fe0->dvb.frontend);
+ fe0->dvb.frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc3028 attached\n",
+ dev->core->name);
+
+ return 0;
+}
+
+static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
+{
+ struct dvb_frontend *fe;
+ struct videobuf_dvb_frontend *fe0 = NULL;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ if (!fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/2: dvb frontend not attached. "
+ "Can't attach xc4000\n",
+ dev->core->name);
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
+ cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc4000 attach failed\n",
+ dev->core->name);
+ dvb_frontend_detach(fe0->dvb.frontend);
+ dvb_unregister_frontend(fe0->dvb.frontend);
+ fe0->dvb.frontend = NULL;
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
+
+ return 0;
+}
+
+static int cx24116_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0x2;
+
+ return 0;
+}
+
+static int stv0900_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 0;
+
+ return 0;
+}
+
+static int cx24116_reset_device(struct dvb_frontend *fe)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ /* Reset the part */
+ /* Put the cx24116 into reset */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ /* Take the cx24116 out of reset */
+ cx_write(MO_SRST_IO, 1);
+ msleep(10);
+
+ return 0;
+}
+
+static const struct cx24116_config hauppauge_hvr4000_config = {
+ .demod_address = 0x05,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static const struct cx24116_config tevii_s460_config = {
+ .demod_address = 0x55,
+ .set_ts_params = cx24116_set_ts_param,
+ .reset_device = cx24116_reset_device,
+};
+
+static int ds3000_set_ts_param(struct dvb_frontend *fe,
+ int is_punctured)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ dev->ts_gen_cntrl = 4;
+
+ return 0;
+}
+
+static struct ds3000_config tevii_ds3000_config = {
+ .demod_address = 0x68,
+ .set_ts_params = ds3000_set_ts_param,
+};
+
+static const struct stv0900_config prof_7301_stv0900_config = {
+ .demod_address = 0x6a,
+/* demod_mode = 0,*/
+ .xtal = 27000000,
+ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
+ .diseqc_mode = 2,/* 2/3 PWM */
+ .tun1_maddress = 0,/* 0x60 */
+ .tun1_adc = 0,/* 2 Vpp */
+ .path1_mode = 3,
+ .set_ts_params = stv0900_set_ts_param,
+};
+
+static const struct stb6100_config prof_7301_stb6100_config = {
+ .tuner_address = 0x60,
+ .refclock = 27000000,
+};
+
+static const struct stv0299_config tevii_tuner_sharp_config = {
+ .demod_address = 0x68,
+ .inittab = sharp_z0194a_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .skip_reinit = 0,
+ .lock_output = 1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = sharp_z0194a_set_symbol_rate,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static const struct stv0288_config tevii_tuner_earda_config = {
+ .demod_address = 0x68,
+ .min_delay_ms = 100,
+ .set_ts_params = cx24116_set_ts_param,
+};
+
+static int cx8802_alloc_frontends(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ struct videobuf_dvb_frontend *fe = NULL;
+ int i;
+
+ mutex_init(&dev->frontends.lock);
+ INIT_LIST_HEAD(&dev->frontends.felist);
+
+ if (!core->board.num_frontends)
+ return -ENODEV;
+
+ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
+ core->board.num_frontends);
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
+ if (!fe) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+
+
+static const u8 samsung_smt_7020_inittab[] = {
+ 0x01, 0x15,
+ 0x02, 0x00,
+ 0x03, 0x00,
+ 0x04, 0x7D,
+ 0x05, 0x0F,
+ 0x06, 0x02,
+ 0x07, 0x00,
+ 0x08, 0x60,
+
+ 0x0A, 0xC2,
+ 0x0B, 0x00,
+ 0x0C, 0x01,
+ 0x0D, 0x81,
+ 0x0E, 0x44,
+ 0x0F, 0x09,
+ 0x10, 0x3C,
+ 0x11, 0x84,
+ 0x12, 0xDA,
+ 0x13, 0x99,
+ 0x14, 0x8D,
+ 0x15, 0xCE,
+ 0x16, 0xE8,
+ 0x17, 0x43,
+ 0x18, 0x1C,
+ 0x19, 0x1B,
+ 0x1A, 0x1D,
+
+ 0x1C, 0x12,
+ 0x1D, 0x00,
+ 0x1E, 0x00,
+ 0x1F, 0x00,
+ 0x20, 0x00,
+ 0x21, 0x00,
+ 0x22, 0x00,
+ 0x23, 0x00,
+
+ 0x28, 0x02,
+ 0x29, 0x28,
+ 0x2A, 0x14,
+ 0x2B, 0x0F,
+ 0x2C, 0x09,
+ 0x2D, 0x05,
+
+ 0x31, 0x1F,
+ 0x32, 0x19,
+ 0x33, 0xFC,
+ 0x34, 0x13,
+ 0xff, 0xff,
+};
+
+
+static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct cx8802_dev *dev = fe->dvb->priv;
+ u8 buf[4];
+ u32 div;
+ struct i2c_msg msg = {
+ .addr = 0x61,
+ .flags = 0,
+ .buf = buf,
+ .len = sizeof(buf) };
+
+ div = c->frequency / 125;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x84; /* 0xC4 */
+ buf[3] = 0x00;
+
+ if (c->frequency < 1500000)
+ buf[3] |= 0x10;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t tone)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ cx_set(MO_GP0_IO, 0x0800);
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ cx_set(MO_GP0_IO, 0x08);
+ break;
+ case SEC_TONE_OFF:
+ cx_clear(MO_GP0_IO, 0x08);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct cx8802_dev *dev = fe->dvb->priv;
+ struct cx88_core *core = dev->core;
+
+ u8 data;
+ struct i2c_msg msg = {
+ .addr = 8,
+ .flags = 0,
+ .buf = &data,
+ .len = sizeof(data) };
+
+ cx_set(MO_GP0_IO, 0x8000);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_OFF:
+ break;
+ case SEC_VOLTAGE_13:
+ data = ISL6421_EN1 | ISL6421_LLC1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ case SEC_VOLTAGE_18:
+ data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1;
+ cx_clear(MO_GP0_IO, 0x80);
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO;
+}
+
+static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe,
+ u32 srate, u32 ratio)
+{
+ u8 aclk = 0;
+ u8 bclk = 0;
+
+ if (srate < 1500000) {
+ aclk = 0xb7;
+ bclk = 0x47;
+ } else if (srate < 3000000) {
+ aclk = 0xb7;
+ bclk = 0x4b;
+ } else if (srate < 7000000) {
+ aclk = 0xb7;
+ bclk = 0x4f;
+ } else if (srate < 14000000) {
+ aclk = 0xb7;
+ bclk = 0x53;
+ } else if (srate < 30000000) {
+ aclk = 0xb6;
+ bclk = 0x53;
+ } else if (srate < 45000000) {
+ aclk = 0xb4;
+ bclk = 0x51;
+ }
+
+ stv0299_writereg(fe, 0x13, aclk);
+ stv0299_writereg(fe, 0x14, bclk);
+ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
+ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
+ stv0299_writereg(fe, 0x21, ratio & 0xf0);
+
+ return 0;
+}
+
+
+static const struct stv0299_config samsung_stv0299_config = {
+ .demod_address = 0x68,
+ .inittab = samsung_smt_7020_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_LK,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate,
+};
+
+static int dvb_register(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
+ int mfe_shared = 0; /* bus not shared by default */
+ int res = -EINVAL;
+
+ if (0 != core->i2c_rc) {
+ printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name);
+ goto frontend_detach;
+ }
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ goto frontend_detach;
+
+ /* multi-frontend gate control is undefined or defaults to fe0 */
+ dev->frontends.gate = 0;
+
+ /* Sets the gate control callback to be used by i2c command calls */
+ core->gate_ctrl = cx88_dvb_gate_ctrl;
+
+ /* init frontend(s) */
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &connexant_refboard_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, &core->i2c_adap,
+ DVB_PLL_THOMSON_DTT759X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
+ case CX88_BOARD_CONEXANT_DVB_T1:
+ case CX88_BOARD_KWORLD_DVB_T_CX22702:
+ case CX88_BOARD_WINFAST_DTV1000:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &connexant_refboard_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, &core->i2c_adap,
+ DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV2000H_J:
+ fe0->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216MEX_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
+ /* DVB-S init */
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &hauppauge_novas_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ break;
+ }
+ /* ZL10353 replaces MT352 on later cards */
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+ /* The tin box says DEE1601, but it seems to be DTT7579
+ * compatible, with a slightly different MT352 AGC gain. */
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv_dual,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ break;
+ }
+ /* ZL10353 replaces MT352 on later cards */
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_LG_Z201))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_KWORLD_DVB_T:
+ case CX88_BOARD_DNTV_LIVE_DVB_T:
+ case CX88_BOARD_ADSTECH_DVB_T_PCI:
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dntv_live_dvbt_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
+ 0x61, NULL, DVB_PLL_UNKNOWN_1))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
+#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+ /* MT352 is on a secondary I2C bus made from some GPIO lines */
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
+ &dev->vp3054->adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+#else
+ printk(KERN_ERR "%s/2: built without vp3054 support\n",
+ core->name);
+#endif
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_hybrid,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_FE6600))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_xc3028,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend == NULL)
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv_mt352_xc3028,
+ &core->i2c_adap);
+ /*
+ * On this board, the demod provides the I2C bus pullup.
+ * We must not permit gate_ctrl to be performed, or
+ * the xc3028 cannot communicate on the bus.
+ */
+ if (fe0->dvb.frontend)
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_PCHDTV_HD3000:
+ fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_DTT761X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+
+ /* Select RF connector callback */
+ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_MICROTUNE_4042FI5))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 9);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_THOMSON_DTT761X))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_5_gold,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF))
+ goto frontend_detach;
+ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x43))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PCHDTV_HD5500:
+ dev->ts_gen_cntrl = 0x08;
+
+ /* Do a hardware reset of chip before using it. */
+ cx_clear(MO_GP0_IO, 1);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 1);
+ mdelay(200);
+ fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &pchdtv_hd5500,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF))
+ goto frontend_detach;
+ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x43))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_ATI_HDTVWONDER:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach,
+ &ati_hdtvwonder,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x61,
+ TUNER_PHILIPS_TUV1236D))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
+ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &hauppauge_novas_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_KWORLD_DVBS_100:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &kworld_dvbs_100_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
+ }
+ break;
+ case CX88_BOARD_GENIATECH_DVBS:
+ fe0->dvb.frontend = dvb_attach(cx24123_attach,
+ &geniatech_dvbs_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
+ }
+ break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &pinnacle_pctv_hd_800i_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &core->i2c_adap,
+ &pinnacle_pctv_hd_800i_tuner_config))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &dvico_hdtv5_pci_nano_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &core->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+ static struct xc2028_ctrl ctl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .scode_table = XC3028_FE_OREN538,
+ };
+
+ fe = dvb_attach(xc2028_attach,
+ fe0->dvb.frontend, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
+ case CX88_BOARD_WINFAST_DTV1800H:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_pinnacle_hybrid_pctv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_WINFAST_DTV1800H_XC4000:
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_pinnacle_hybrid_pctv,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct xc4000_config cfg = {
+ .i2c_address = 0x61,
+ .default_pm = 0,
+ .dvb_amplitude = 134,
+ .set_smoothedcvbs = 1,
+ .if_khz = 4560
+ };
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc4000(dev, &cfg) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_GENIATECH_X8000_MT:
+ dev->ts_gen_cntrl = 0x00;
+
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_geniatech_x8000_mt,
+ &core->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_KWORLD_ATSC_120:
+ fe0->dvb.frontend = dvb_attach(s5h1409_attach,
+ &kworld_atsc_120_config,
+ &core->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &dvico_fusionhdtv7_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &core->i2c_adap,
+ &dvico_fusionhdtv7_tuner_config))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* MFE frontend 1 */
+ mfe_shared = 1;
+ dev->frontends.gate = 2;
+ /* DVB-S/S2 Init */
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ /* MFE frontend 2 */
+ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
+ if (!fe1)
+ goto frontend_detach;
+ /* DVB-T Init */
+ fe1->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr_config,
+ &dev->core->i2c_adap);
+ if (fe1->dvb.frontend) {
+ fe1->dvb.frontend->id = 1;
+ if (!dvb_attach(simple_tuner_attach,
+ fe1->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (!dvb_attach(isl6421_attach,
+ fe0->dvb.frontend,
+ &dev->core->i2c_adap,
+ 0x08, ISL6421_DCL, 0x00))
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PROF_6200:
+ case CX88_BOARD_TBS_8910:
+ case CX88_BOARD_TEVII_S420:
+ fe0->dvb.frontend = dvb_attach(stv0299_attach,
+ &tevii_tuner_sharp_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+ &core->i2c_adap, DVB_PLL_OPERA1))
+ goto frontend_detach;
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+
+ } else {
+ fe0->dvb.frontend = dvb_attach(stv0288_attach,
+ &tevii_tuner_earda_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
+ &core->i2c_adap))
+ goto frontend_detach;
+ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ }
+ }
+ break;
+ case CX88_BOARD_TEVII_S460:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &tevii_s460_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_TEVII_S464:
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_OMICOM_SS4_PCI:
+ case CX88_BOARD_TBS_8920:
+ case CX88_BOARD_PROF_7300:
+ case CX88_BOARD_SATTRADE_ST4200:
+ fe0->dvb.frontend = dvb_attach(cx24116_attach,
+ &hauppauge_hvr4000_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ break;
+ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &cx88_terratec_cinergy_ht_pci_mkii_config,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
+ if (attach_xc3028(0x61, dev) < 0)
+ goto frontend_detach;
+ }
+ break;
+ case CX88_BOARD_PROF_7301:{
+ struct dvb_tuner_ops *tuner_ops = NULL;
+
+ fe0->dvb.frontend = dvb_attach(stv0900_attach,
+ &prof_7301_stv0900_config,
+ &core->i2c_adap, 0);
+ if (fe0->dvb.frontend != NULL) {
+ if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
+ &prof_7301_stb6100_config,
+ &core->i2c_adap))
+ goto frontend_detach;
+
+ tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
+ tuner_ops->set_frequency = stb6100_set_freq;
+ tuner_ops->get_frequency = stb6100_get_freq;
+ tuner_ops->set_bandwidth = stb6100_set_bandw;
+ tuner_ops->get_bandwidth = stb6100_get_bandw;
+
+ core->prev_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage =
+ tevii_dvbs_set_voltage;
+ }
+ break;
+ }
+ case CX88_BOARD_SAMSUNG_SMT_7020:
+ dev->ts_gen_cntrl = 0x08;
+
+ cx_set(MO_GP0_IO, 0x0101);
+
+ cx_clear(MO_GP0_IO, 0x01);
+ mdelay(100);
+ cx_set(MO_GP0_IO, 0x01);
+ mdelay(200);
+
+ fe0->dvb.frontend = dvb_attach(stv0299_attach,
+ &samsung_stv0299_config,
+ &dev->core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.set_params =
+ samsung_smt_7020_tuner_set_params;
+ fe0->dvb.frontend->tuner_priv =
+ &dev->core->i2c_adap;
+ fe0->dvb.frontend->ops.set_voltage =
+ samsung_smt_7020_set_voltage;
+ fe0->dvb.frontend->ops.set_tone =
+ samsung_smt_7020_set_tone;
+ }
+
+ break;
+ case CX88_BOARD_TWINHAN_VP1027_DVBS:
+ dev->ts_gen_cntrl = 0x00;
+ fe0->dvb.frontend = dvb_attach(mb86a16_attach,
+ &twinhan_vp1027,
+ &core->i2c_adap);
+ if (fe0->dvb.frontend) {
+ core->prev_set_voltage =
+ fe0->dvb.frontend->ops.set_voltage;
+ fe0->dvb.frontend->ops.set_voltage =
+ vp1027_set_voltage;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
+ core->name);
+ break;
+ }
+
+ if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
+ printk(KERN_ERR
+ "%s/2: frontend initialization failed\n",
+ core->name);
+ goto frontend_detach;
+ }
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = cx88_tuner_callback;
+
+ /* Ensure all frontends negotiate bus access */
+ fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+ if (fe1)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+
+ /* Put the analog decoder in standby to keep it quiet */
+ call_all(core, core, s_power, 0);
+
+ /* register everything */
+ res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ &dev->pci->dev, adapter_nr, mfe_shared);
+ if (res)
+ goto frontend_detach;
+ return res;
+
+frontend_detach:
+ core->gate_ctrl = NULL;
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return res;
+}
+
+/* ----------------------------------------------------------- */
+
+/* CX8802 MPEG -> mini driver - We have been given the hardware */
+static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __func__);
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* We arrive here with either the cx23416 or the cx22702
+ * on the bus. Take the bus from the cx23416 and enable the
+ * cx22702 demod
+ */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* enable the cx22702 pins */
+ cx_clear(MO_GP0_IO, 0x00000004);
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ switch (core->dvbdev->frontends.active_fe_id) {
+ case 1: /* DVB-S/S2 Enabled */
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ /* Take the cx24116/cx24123 out of reset */
+ cx_write(MO_SRST_IO, 1);
+ core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
+ break;
+ case 2: /* DVB-T Enabled */
+ /* Put the cx24116/cx24123 into reset */
+ cx_write(MO_SRST_IO, 0);
+ /* enable the cx22702 pins */
+ cx_clear(MO_GP0_IO, 0x00000004);
+ core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
+ break;
+ }
+ udelay(1000);
+ break;
+
+ case CX88_BOARD_WINFAST_DTV2000H_PLUS:
+ /* set RF input to AIR for DVB-T (GPIO 16) */
+ cx_write(MO_GP2_IO, 0x0101);
+ break;
+
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+/* CX8802 MPEG -> mini driver - We no longer have the hardware */
+static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ int err = 0;
+ dprintk( 1, "%s\n", __func__);
+
+ switch (core->boardnr) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Do Nothing, leave the cx22702 on the bus. */
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ break;
+ default:
+ err = -ENODEV;
+ }
+ return err;
+}
+
+static int cx8802_dvb_probe(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = drv->core->dvbdev;
+ int err;
+ struct videobuf_dvb_frontend *fe;
+ int i;
+
+ dprintk( 1, "%s\n", __func__);
+ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
+ core->boardnr,
+ core->name,
+ core->pci_bus,
+ core->pci_slot);
+
+ err = -ENODEV;
+ if (!(core->board.mpeg & CX88_MPEG_DVB))
+ goto fail_core;
+
+ /* If vp3054 isn't enabled, a stub will just return 0 */
+ err = vp3054_i2c_probe(dev);
+ if (0 != err)
+ goto fail_core;
+
+ /* dvb stuff */
+ printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
+ dev->ts_gen_cntrl = 0x0c;
+
+ err = cx8802_alloc_frontends(dev);
+ if (err)
+ goto fail_core;
+
+ err = -ENODEV;
+ for (i = 1; i <= core->board.num_frontends; i++) {
+ fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
+ if (fe == NULL) {
+ printk(KERN_ERR "%s() failed to get frontend(%d)\n",
+ __func__, i);
+ goto fail_probe;
+ }
+ videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_TOP,
+ sizeof(struct cx88_buffer),
+ dev, NULL);
+ /* init struct videobuf_dvb */
+ fe->dvb.name = dev->core->name;
+ }
+
+ err = dvb_register(dev);
+ if (err)
+ /* frontends/adapter de-allocated in dvb_register */
+ printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
+ core->name, err);
+ return err;
+fail_probe:
+ videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
+fail_core:
+ return err;
+}
+
+static int cx8802_dvb_remove(struct cx8802_driver *drv)
+{
+ struct cx88_core *core = drv->core;
+ struct cx8802_dev *dev = drv->core->dvbdev;
+
+ dprintk( 1, "%s\n", __func__);
+
+ videobuf_dvb_unregister_bus(&dev->frontends);
+
+ vp3054_i2c_remove(dev);
+
+ core->gate_ctrl = NULL;
+
+ return 0;
+}
+
+static struct cx8802_driver cx8802_dvb_driver = {
+ .type_id = CX88_MPEG_DVB,
+ .hw_access = CX8802_DRVCTL_SHARED,
+ .probe = cx8802_dvb_probe,
+ .remove = cx8802_dvb_remove,
+ .advise_acquire = cx8802_dvb_advise_acquire,
+ .advise_release = cx8802_dvb_advise_release,
+};
+
+static int __init dvb_init(void)
+{
+ printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
+ CX88_VERSION);
+ return cx8802_register_driver(&cx8802_dvb_driver);
+}
+
+static void __exit dvb_fini(void)
+{
+ cx8802_unregister_driver(&cx8802_dvb_driver);
+}
+
+module_init(dvb_init);
+module_exit(dvb_fini);
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c
index de0f1af74e41..de0f1af74e41 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/pci/cx88/cx88-i2c.c
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index ebf448c48ca3..ebf448c48ca3 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index cd5386ee210c..cd5386ee210c 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
diff --git a/drivers/media/video/cx88/cx88-reg.h b/drivers/media/pci/cx88/cx88-reg.h
index 2ec52d1cdea0..2ec52d1cdea0 100644
--- a/drivers/media/video/cx88/cx88-reg.h
+++ b/drivers/media/pci/cx88/cx88-reg.h
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c
index 770ec05b5e9b..770ec05b5e9b 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/pci/cx88/cx88-tvaudio.c
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c
index f8f8389c0362..f8f8389c0362 100644
--- a/drivers/media/video/cx88/cx88-vbi.c
+++ b/drivers/media/pci/cx88/cx88-vbi.c
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
new file mode 100644
index 000000000000..a146d50d7795
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -0,0 +1,2075 @@
+/*
+ *
+ * device driver for Conexant 2388x based TV cards
+ * video4linux video interface
+ *
+ * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * - Multituner support
+ * - video_ioctl2 conversion
+ * - PAL/M fixes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <asm/div64.h>
+
+#include "cx88.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/wm8775.h>
+
+MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX88_VERSION);
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(video_nr,"video device numbers");
+MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
+MODULE_PARM_DESC(radio_nr,"radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug,int,0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+
+static unsigned int irq_debug;
+module_param(irq_debug,int,0644);
+MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit,int,0644);
+MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
+
+#define dprintk(level,fmt, arg...) if (video_debug >= level) \
+ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+static const struct cx8800_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .cxformat = ColorFormatY8,
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .cxformat = ColorFormatRGB15,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .cxformat = ColorFormatRGB16,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .cxformat = ColorFormatRGB24,
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .cxformat = ColorFormatRGB32,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP,
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .cxformat = ColorFormatYUY2,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },{
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+struct cx88_ctrl {
+ /* control information */
+ u32 id;
+ s32 minimum;
+ s32 maximum;
+ u32 step;
+ s32 default_value;
+
+ /* control register information */
+ u32 off;
+ u32 reg;
+ u32 sreg;
+ u32 mask;
+ u32 shift;
+};
+
+static const struct cx88_ctrl cx8800_vid_ctls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 128,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0x00ff,
+ .shift = 0,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .off = 0,
+ .reg = MO_CONTR_BRIGHT,
+ .mask = 0xff00,
+ .shift = 8,
+ },{
+ .id = V4L2_CID_HUE,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 128,
+ .reg = MO_HUE,
+ .mask = 0x00ff,
+ .shift = 0,
+ },{
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .id = V4L2_CID_SATURATION,
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .off = 0,
+ .reg = MO_UV_SATURATION,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .id = V4L2_CID_SHARPNESS,
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+ .default_value = 0x0,
+ .off = 0,
+ /* NOTE: the value is converted and written to both even
+ and odd registers in the code */
+ .reg = MO_FILTER_ODD,
+ .mask = 7 << 7,
+ .shift = 7,
+ }, {
+ .id = V4L2_CID_CHROMA_AGC,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 10,
+ .shift = 10,
+ }, {
+ .id = V4L2_CID_COLOR_KILLER,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0x1,
+ .reg = MO_INPUT_FORMAT,
+ .mask = 1 << 9,
+ .shift = 9,
+ }, {
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0x0,
+ .off = 0,
+ .reg = MO_HTOTAL,
+ .mask = 3 << 11,
+ .shift = 11,
+ }
+};
+
+static const struct cx88_ctrl cx8800_aud_ctls[] = {
+ {
+ /* --- audio --- */
+ .id = V4L2_CID_AUDIO_MUTE,
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = (1 << 6),
+ .shift = 6,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .reg = AUD_VOL_CTL,
+ .sreg = SHADOW_AUD_VOL_CTL,
+ .mask = 0x3f,
+ .shift = 0,
+ },{
+ .id = V4L2_CID_AUDIO_BALANCE,
+ .minimum = 0,
+ .maximum = 0x7f,
+ .step = 1,
+ .default_value = 0x40,
+ .reg = AUD_BAL_CTL,
+ .sreg = SHADOW_AUD_BAL_CTL,
+ .mask = 0x7f,
+ .shift = 0,
+ }
+};
+
+enum {
+ CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
+ CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
+};
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit)
+{
+ struct cx88_core *core = dev->core;
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&core->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&core->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1,"res: get %d\n",bit);
+ mutex_unlock(&core->lock);
+ return 1;
+}
+
+static
+int res_check(struct cx8800_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static
+int res_locked(struct cx8800_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
+{
+ struct cx88_core *core = dev->core;
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&core->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1,"res: put %d\n",bits);
+ mutex_unlock(&core->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx88_video_mux(struct cx88_core *core, unsigned int input)
+{
+ /* struct cx88_core *core = dev->core; */
+
+ dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ input, INPUT(input).vmux,
+ INPUT(input).gpio0,INPUT(input).gpio1,
+ INPUT(input).gpio2,INPUT(input).gpio3);
+ core->input = input;
+ cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
+ cx_write(MO_GP3_IO, INPUT(input).gpio3);
+ cx_write(MO_GP0_IO, INPUT(input).gpio0);
+ cx_write(MO_GP1_IO, INPUT(input).gpio1);
+ cx_write(MO_GP2_IO, INPUT(input).gpio2);
+
+ switch (INPUT(input).type) {
+ case CX88_VMUX_SVIDEO:
+ cx_set(MO_AFECFG_IO, 0x00000001);
+ cx_set(MO_INPUT_FORMAT, 0x00010010);
+ cx_set(MO_FILTER_EVEN, 0x00002020);
+ cx_set(MO_FILTER_ODD, 0x00002020);
+ break;
+ default:
+ cx_clear(MO_AFECFG_IO, 0x00000001);
+ cx_clear(MO_INPUT_FORMAT, 0x00010010);
+ cx_clear(MO_FILTER_EVEN, 0x00002020);
+ cx_clear(MO_FILTER_ODD, 0x00002020);
+ break;
+ }
+
+ /* if there are audioroutes defined, we have an external
+ ADC to deal with audio */
+ if (INPUT(input).audioroute) {
+ /* The wm8775 module has the "2" route hardwired into
+ the initialization. Some boards may use different
+ routes for different inputs. HVR-1300 surely does */
+ if (core->board.audio_chip &&
+ core->board.audio_chip == V4L2_IDENT_WM8775) {
+ call_all(core, audio, s_routing,
+ INPUT(input).audioroute, 0, 0);
+ }
+ /* cx2388's C-ADC is connected to the tuner only.
+ When used with S-Video, that ADC is busy dealing with
+ chroma, so an external must be used for baseband audio */
+ if (INPUT(input).type != CX88_VMUX_TELEVISION &&
+ INPUT(input).type != CX88_VMUX_CABLE) {
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+ cx88_set_tvaudio(core);
+ } else {
+ /* Normal mode */
+ cx_write(AUD_I2SCNTL, 0x0);
+ cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(cx88_video_mux);
+
+/* ------------------------------------------------------------------ */
+
+static int start_video_dma(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf)
+{
+ struct cx88_core *core = dev->core;
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
+ buf->bpl, buf->risc.dma);
+ cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);
+ cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
+
+ /* reset counter */
+ cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
+
+ /* Enables corresponding bits at PCI_INT_STAT:
+ bits 0 to 4: video, audio, transport stream, VIP, Host
+ bit 7: timer
+ bits 8 and 9: DMA complete for: SRC, DST
+ bits 10 and 11: BERR signal asserted for RISC: RD, WR
+ bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
+ */
+ cx_set(MO_VID_INTMSK, 0x0f0011);
+
+ /* enable capture */
+ cx_set(VID_CAPTURE_CONTROL,0x06);
+
+ /* start dma */
+ cx_set(MO_DEV_CNTRL2, (1<<5));
+ cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int stop_video_dma(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+
+ /* disable capture */
+ cx_clear(VID_CAPTURE_CONTROL,0x06);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
+ cx_clear(MO_VID_INTMSK, 0x0f0011);
+ return 0;
+}
+#endif
+
+static int restart_video_queue(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q)
+{
+ struct cx88_core *core = dev->core;
+ struct cx88_buffer *buf, *prev;
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ dprintk(2,"restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ start_video_dma(dev, q, buf);
+ list_for_each_entry(buf, &q->active, vb.queue)
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2,"[%p/%d] restart_queue - first active\n",
+ buf,buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2,"[%p/%d] restart_queue - move to active\n",
+ buf,buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct cx8800_fh *fh = q->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+
+ *size = dev->fmt->depth * dev->width * dev->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+ return 0;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8800_fh *fh = q->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+ int rc, init_buffer = 0;
+
+ BUG_ON(NULL == dev->fmt);
+ if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) ||
+ dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != dev->fmt ||
+ buf->vb.width != dev->width ||
+ buf->vb.height != dev->height ||
+ buf->vb.field != field) {
+ buf->fmt = dev->fmt;
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, buf->bpl,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx88_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx88_free_buffer(q,buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+ struct cx88_buffer *prev;
+ struct cx8800_fh *fh = vq->priv_data;
+ struct cx8800_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue,&q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue,&q->active);
+ start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2,"[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue,&q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ dprintk(2,"[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue,&q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2,"[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
+
+ cx88_free_buffer(q,buf);
+}
+
+static const struct videobuf_queue_ops cx8800_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue *get_queue(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = file->private_data;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ return &fh->vidq;
+ case VFL_TYPE_VBI:
+ return &fh->vbiq;
+ default:
+ BUG();
+ return NULL;
+ }
+}
+
+static int get_resource(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ return RESOURCE_VIDEO;
+ case VFL_TYPE_VBI:
+ return RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_dev *dev = video_drvdata(file);
+ struct cx88_core *core = dev->core;
+ struct cx8800_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk(1, "open dev=%s radio=%d type=%s\n",
+ video_device_node_name(vdev), radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (unlikely(!fh))
+ return -ENOMEM;
+
+ v4l2_fh_init(&fh->fh, vdev);
+ file->private_data = fh;
+ fh->dev = dev;
+
+ mutex_lock(&core->lock);
+
+ videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+ videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx88_buffer),
+ fh, NULL);
+
+ if (vdev->vfl_type == VFL_TYPE_RADIO) {
+ dprintk(1,"video_open: setting radio device\n");
+ cx_write(MO_GP3_IO, core->board.radio.gpio3);
+ cx_write(MO_GP0_IO, core->board.radio.gpio0);
+ cx_write(MO_GP1_IO, core->board.radio.gpio1);
+ cx_write(MO_GP2_IO, core->board.radio.gpio2);
+ if (core->board.radio.audioroute) {
+ if(core->board.audio_chip &&
+ core->board.audio_chip == V4L2_IDENT_WM8775) {
+ call_all(core, audio, s_routing,
+ core->board.radio.audioroute, 0, 0);
+ }
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+ cx88_set_tvaudio(core);
+ } else {
+ /* FM Mode */
+ core->tvaudio = WW_FM;
+ cx88_set_tvaudio(core);
+ cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
+ }
+ call_all(core, tuner, s_radio);
+ }
+
+ core->users++;
+ mutex_unlock(&core->lock);
+ v4l2_fh_add(&fh->fh);
+
+ return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = file->private_data;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case VFL_TYPE_VBI:
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = file->private_data;
+ struct cx88_buffer *buf;
+ unsigned int rc = v4l2_ctrl_poll(file, wait);
+
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return rc | POLLERR;
+ return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+ mutex_lock(&fh->vidq.vb_lock);
+ if (res_check(fh,RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ goto done;
+ buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx88_buffer*)fh->vidq.read_buf;
+ if (NULL == buf)
+ goto done;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ rc |= POLLIN|POLLRDNORM;
+done:
+ mutex_unlock(&fh->vidq.vb_lock);
+ return rc;
+}
+
+static int video_release(struct file *file)
+{
+ struct cx8800_fh *fh = file->private_data;
+ struct cx8800_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev,fh,RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq,fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbiq);
+ res_free(dev,fh,RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+ videobuf_mmap_free(&fh->vbiq);
+
+ mutex_lock(&dev->core->lock);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
+ file->private_data = NULL;
+ kfree(fh);
+
+ dev->core->users--;
+ if (!dev->core->users)
+ call_all(dev->core, core, s_power, 0);
+ mutex_unlock(&dev->core->lock);
+
+ return 0;
+}
+
+static int
+video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ return videobuf_mmap_mapper(get_queue(file), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx88_core *core =
+ container_of(ctrl->handler, struct cx88_core, video_hdl);
+ const struct cx88_ctrl *cc = ctrl->priv;
+ u32 value, mask;
+
+ mask = cc->mask;
+ switch (ctrl->id) {
+ case V4L2_CID_SATURATION:
+ /* special v_sat handling */
+
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+
+ if (core->tvnorm & V4L2_STD_SECAM) {
+ /* For SECAM, both U and V sat should be equal */
+ value = value << 8 | value;
+ } else {
+ /* Keeps U Saturation proportional to V Sat */
+ value = (value * 0x5a) / 0x7f << 8 | value;
+ }
+ mask = 0xffff;
+ break;
+ case V4L2_CID_SHARPNESS:
+ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
+ value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
+ /* needs to be set for both fields */
+ cx_andor(MO_FILTER_EVEN, mask, value);
+ break;
+ case V4L2_CID_CHROMA_AGC:
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+ break;
+ default:
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+ break;
+ }
+ dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+ mask, cc->sreg ? " [shadowed]" : "");
+ if (cc->sreg)
+ cx_sandor(cc->sreg, cc->reg, mask, value);
+ else
+ cx_andor(cc->reg, mask, value);
+ return 0;
+}
+
+static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct cx88_core *core =
+ container_of(ctrl->handler, struct cx88_core, audio_hdl);
+ const struct cx88_ctrl *cc = ctrl->priv;
+ u32 value,mask;
+
+ /* Pass changes onto any WM8775 */
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
+ (0x90 + ctrl->val) << 8 : 0);
+ break;
+ case V4L2_CID_AUDIO_BALANCE:
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
+ break;
+ default:
+ break;
+ }
+ }
+
+ mask = cc->mask;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_BALANCE:
+ value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ value = 0x3f - (ctrl->val & 0x3f);
+ break;
+ default:
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
+ break;
+ }
+ dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
+ mask, cc->sreg ? " [shadowed]" : "");
+ if (cc->sreg)
+ cx_sandor(cc->sreg, cc->reg, mask, value);
+ else
+ cx_andor(cc->reg, mask, value);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ const struct cx8800_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(core->tvnorm);
+ maxh = norm_maxh(core->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
+ &f->fmt.pix.height, 32, maxh, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+ int err = vidioc_try_fmt_vid_cap (file,priv,f);
+
+ if (0 != err)
+ return err;
+ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ return 0;
+}
+
+void cx88_querycap(struct file *file, struct cx88_core *core,
+ struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
+ cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ if (UNSET != core->board.tuner_type)
+ cap->device_caps |= V4L2_CAP_TUNER;
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_RADIO:
+ cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
+ break;
+ case VFL_TYPE_GRABBER:
+ cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
+ break;
+ }
+ cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
+ if (core->board.radio.type == CX88_RADIO)
+ cap->capabilities |= V4L2_CAP_RADIO;
+}
+EXPORT_SYMBOL(cx88_querycap);
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
+ struct cx88_core *core = dev->core;
+
+ strcpy(cap->driver, "cx8800");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cx88_querycap(file, core, cap);
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description,formats[f->index].name,sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
+{
+ return videobuf_reqbufs(get_queue(file), p);
+}
+
+static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ return videobuf_querybuf(get_queue(file), p);
+}
+
+static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ return videobuf_qbuf(get_queue(file), p);
+}
+
+static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ return videobuf_dqbuf(get_queue(file), p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(file))))
+ return -EBUSY;
+ return videobuf_streamon(get_queue(file));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cx8800_fh *fh = priv;
+ struct cx8800_dev *dev = fh->dev;
+ int err, res;
+
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+
+ res = get_resource(file);
+ err = videobuf_streamoff(get_queue(file));
+ if (err < 0)
+ return err;
+ res_free(dev,fh,res);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ *tvnorm = core->tvnorm;
+ return 0;
+}
+
+static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core,*tvnorms);
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+
+/* only one input in this sample driver */
+int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
+{
+ static const char * const iname[] = {
+ [ CX88_VMUX_COMPOSITE1 ] = "Composite1",
+ [ CX88_VMUX_COMPOSITE2 ] = "Composite2",
+ [ CX88_VMUX_COMPOSITE3 ] = "Composite3",
+ [ CX88_VMUX_COMPOSITE4 ] = "Composite4",
+ [ CX88_VMUX_SVIDEO ] = "S-Video",
+ [ CX88_VMUX_TELEVISION ] = "Television",
+ [ CX88_VMUX_CABLE ] = "Cable TV",
+ [ CX88_VMUX_DVB ] = "DVB",
+ [ CX88_VMUX_DEBUG ] = "for debug only",
+ };
+ unsigned int n = i->index;
+
+ if (n >= 4)
+ return -EINVAL;
+ if (0 == INPUT(n).type)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name,iname[INPUT(n).type]);
+ if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
+ (CX88_VMUX_CABLE == INPUT(n).type)) {
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ }
+ i->std = CX88_NORMS;
+ return 0;
+}
+EXPORT_SYMBOL(cx88_enum_input);
+
+static int vidioc_enum_input (struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ return cx88_enum_input (core,i);
+}
+
+static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ *i = core->input;
+ return 0;
+}
+
+static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (i >= 4)
+ return -EINVAL;
+ if (0 == INPUT(i).type)
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ cx88_newstation(core);
+ cx88_video_mux(core,i);
+ mutex_unlock(&core->lock);
+ return 0;
+}
+
+static int vidioc_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+ u32 reg;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ call_all(core, tuner, g_tuner, t);
+
+ cx88_get_stereo(core ,t);
+ reg = cx_read(MO_DEVICE_STATUS);
+ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
+ return 0;
+}
+
+static int vidioc_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (UNSET == core->board.tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ cx88_set_stereo(core, t->audmode, 1);
+ return 0;
+}
+
+static int vidioc_g_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (f->tuner)
+ return -EINVAL;
+
+ f->frequency = core->freq;
+
+ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+}
+
+int cx88_set_freq (struct cx88_core *core,
+ struct v4l2_frequency *f)
+{
+ if (unlikely(UNSET == core->board.tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&core->lock);
+ cx88_newstation(core);
+ call_all(core, tuner, s_frequency, f);
+ call_all(core, tuner, g_frequency, f);
+ core->freq = f->frequency;
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep (10);
+ cx88_set_tvaudio(core);
+
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx88_set_freq);
+
+static int vidioc_s_frequency (struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx8800_fh *fh = priv;
+ struct cx88_core *core = fh->dev->core;
+
+ return cx88_set_freq(core, f);
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ if (!v4l2_chip_match_host(&chip->match))
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* cx2388x has a 24-bit register space */
+ reg->val = cx_read(reg->reg & 0xffffff);
+ reg->size = 4;
+ return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ cx_write(reg->reg & 0xffffff, reg->val);
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_g_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+
+ call_all(core, tuner, g_tuner, t);
+ return 0;
+}
+
+/* FIXME: Should add a standard for radio */
+
+static int radio_s_tuner (struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
+
+ if (0 != t->index)
+ return -EINVAL;
+ if (t->audmode > V4L2_TUNER_MODE_STEREO)
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+
+ call_all(core, tuner, s_tuner, t);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_vid_timeout(unsigned long data)
+{
+ struct cx8800_dev *dev = (struct cx8800_dev*)data;
+ struct cx88_core *core = dev->core;
+ struct cx88_dmaqueue *q = &dev->vidq;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+
+ spin_lock_irqsave(&dev->slock,flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ restart_video_queue(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+static const char *cx88_vid_irqs[32] = {
+ "y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
+ "y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
+ "y_oflow", "u_oflow", "v_oflow", "vbi_oflow",
+ "y_sync", "u_sync", "v_sync", "vbi_sync",
+ "opc_err", "par_err", "rip_err", "pci_abort",
+};
+
+static void cx8800_vid_irq(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ u32 status, mask, count;
+
+ status = cx_read(MO_VID_INTSTAT);
+ mask = cx_read(MO_VID_INTMSK);
+ if (0 == (status & mask))
+ return;
+ cx_write(MO_VID_INTSTAT, status);
+ if (irq_debug || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq vid",
+ cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
+ status, mask);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s/0: video risc op code error\n",core->name);
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+ cx_clear(VID_CAPTURE_CONTROL, 0x06);
+ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_VIDY_GPCNT);
+ cx88_wakeup(core, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc1 vbi */
+ if (status & 0x08) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_VBI_GPCNT);
+ cx88_wakeup(core, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2,"stopper video\n");
+ spin_lock(&dev->slock);
+ restart_video_queue(dev,&dev->vidq);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 vbi */
+ if (status & 0x80) {
+ dprintk(2,"stopper vbi\n");
+ spin_lock(&dev->slock);
+ cx8800_restart_vbi_queue(dev,&dev->vbiq);
+ spin_unlock(&dev->slock);
+ }
+}
+
+static irqreturn_t cx8800_irq(int irq, void *dev_id)
+{
+ struct cx8800_dev *dev = dev_id;
+ struct cx88_core *core = dev->core;
+ u32 status;
+ int loop, handled = 0;
+
+ for (loop = 0; loop < 10; loop++) {
+ status = cx_read(MO_PCI_INTSTAT) &
+ (core->pci_irqmask | PCI_INT_VIDINT);
+ if (0 == status)
+ goto out;
+ cx_write(MO_PCI_INTSTAT, status);
+ handled = 1;
+
+ if (status & core->pci_irqmask)
+ cx88_core_irq(core,status);
+ if (status & PCI_INT_VIDINT)
+ cx8800_vid_irq(dev);
+ };
+ if (10 == loop) {
+ printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
+ core->name);
+ cx_write(MO_PCI_INTMSK,0);
+ }
+
+ out:
+ return IRQ_RETVAL(handled);
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_video_template = {
+ .name = "cx8800-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = CX88_NORMS,
+};
+
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_vbi_template = {
+ .name = "cx8800-vbi",
+ .fops = &video_fops,
+ .ioctl_ops = &vbi_ioctl_ops,
+ .tvnorms = CX88_NORMS,
+};
+
+static const struct v4l2_file_operations radio_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .poll = v4l2_ctrl_poll,
+ .release = video_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct video_device cx8800_radio_template = {
+ .name = "cx8800-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
+ .s_ctrl = cx8800_s_vid_ctrl,
+};
+
+static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
+ .s_ctrl = cx8800_s_aud_ctrl,
+};
+
+/* ----------------------------------------------------------- */
+
+static void cx8800_unregister_video(struct cx8800_dev *dev)
+{
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->video_dev) {
+ if (video_is_registered(dev->video_dev))
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+ }
+}
+
+static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8800_dev *dev;
+ struct cx88_core *core;
+ int err;
+ int i;
+
+ dev = kzalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_free;
+ }
+ core = cx88_core_get(dev->pci);
+ if (NULL == core) {
+ err = -EINVAL;
+ goto fail_free;
+ }
+ dev->core = core;
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", core->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
+
+ pci_set_master(pci_dev);
+ if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) {
+ printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
+ err = -EIO;
+ goto fail_core;
+ }
+
+ /* initialize driver struct */
+ spin_lock_init(&dev->slock);
+ core->tvnorm = V4L2_STD_NTSC_M;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx8800_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
+ MO_VID_DMACNTRL,0x11,0x00);
+
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx8800_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
+ MO_VID_DMACNTRL,0x88,0x00);
+
+ /* get irq */
+ err = request_irq(pci_dev->irq, cx8800_irq,
+ IRQF_SHARED | IRQF_DISABLED, core->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't get IRQ %d\n",
+ core->name,pci_dev->irq);
+ goto fail_core;
+ }
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+ for (i = 0; i < CX8800_AUD_CTLS; i++) {
+ const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
+ struct v4l2_ctrl *vc;
+
+ vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+ if (vc == NULL) {
+ err = core->audio_hdl.error;
+ goto fail_core;
+ }
+ vc->priv = (void *)cc;
+ }
+
+ for (i = 0; i < CX8800_VID_CTLS; i++) {
+ const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
+ struct v4l2_ctrl *vc;
+
+ vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
+ if (vc == NULL) {
+ err = core->video_hdl.error;
+ goto fail_core;
+ }
+ vc->priv = (void *)cc;
+ if (vc->id == V4L2_CID_CHROMA_AGC)
+ core->chroma_agc = vc;
+ }
+ v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL);
+
+ /* load and configure helper modules */
+
+ if (core->board.audio_chip == V4L2_IDENT_WM8775) {
+ struct i2c_board_info wm8775_info = {
+ .type = "wm8775",
+ .addr = 0x36 >> 1,
+ .platform_data = &core->wm8775_data,
+ };
+ struct v4l2_subdev *sd;
+
+ if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
+ core->wm8775_data.is_nova_s = true;
+ else
+ core->wm8775_data.is_nova_s = false;
+
+ sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
+ &wm8775_info, NULL);
+ if (sd != NULL) {
+ core->sd_wm8775 = sd;
+ sd->grp_id = WM8775_GID;
+ }
+ }
+
+ if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
+ /* This probes for a tda9874 as is used on some
+ Pixelview Ultra boards. */
+ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
+ "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
+ }
+
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
+ static const struct i2c_board_info rtc_info = {
+ I2C_BOARD_INFO("isl1208", 0x6f)
+ };
+
+ request_module("rtc-isl1208");
+ core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
+ }
+ /* break intentionally omitted */
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ request_module("ir-kbd-i2c");
+ }
+
+ /* Sets device info at pci_dev */
+ pci_set_drvdata(pci_dev, dev);
+
+ dev->width = 320;
+ dev->height = 240;
+ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ /* initial device configuration */
+ mutex_lock(&core->lock);
+ cx88_set_tvnorm(core, core->tvnorm);
+ v4l2_ctrl_handler_setup(&core->video_hdl);
+ v4l2_ctrl_handler_setup(&core->audio_hdl);
+ cx88_video_mux(core, 0);
+
+ /* register v4l devices */
+ dev->video_dev = cx88_vdev_init(core,dev->pci,
+ &cx8800_video_template,"video");
+ video_set_drvdata(dev->video_dev, dev);
+ dev->video_dev->ctrl_handler = &core->video_hdl;
+ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
+ video_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register video device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
+ core->name, video_device_node_name(dev->video_dev));
+
+ dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
+ video_set_drvdata(dev->vbi_dev, dev);
+ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
+ vbi_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register vbi device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->vbi_dev));
+
+ if (core->board.radio.type == CX88_RADIO) {
+ dev->radio_dev = cx88_vdev_init(core,dev->pci,
+ &cx8800_radio_template,"radio");
+ video_set_drvdata(dev->radio_dev, dev);
+ dev->radio_dev->ctrl_handler = &core->audio_hdl;
+ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
+ radio_nr[core->nr]);
+ if (err < 0) {
+ printk(KERN_ERR "%s/0: can't register radio device\n",
+ core->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device %s\n",
+ core->name, video_device_node_name(dev->radio_dev));
+ }
+
+ /* start tvaudio thread */
+ if (core->board.tuner_type != TUNER_ABSENT) {
+ core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
+ if (IS_ERR(core->kthread)) {
+ err = PTR_ERR(core->kthread);
+ printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n",
+ core->name, err);
+ }
+ }
+ mutex_unlock(&core->lock);
+
+ return 0;
+
+fail_unreg:
+ cx8800_unregister_video(dev);
+ free_irq(pci_dev->irq, dev);
+ mutex_unlock(&core->lock);
+fail_core:
+ cx88_core_put(core,dev->pci);
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop thread */
+ if (core->kthread) {
+ kthread_stop(core->kthread);
+ core->kthread = NULL;
+ }
+
+ if (core->ir)
+ cx88_ir_stop(core);
+
+ cx88_shutdown(core); /* FIXME */
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+
+ free_irq(pci_dev->irq, dev);
+ cx8800_unregister_video(dev);
+ pci_set_drvdata(pci_dev, NULL);
+
+ /* free memory */
+ btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
+ cx88_core_put(core,dev->pci);
+ kfree(dev);
+}
+
+#ifdef CONFIG_PM
+static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s/0: suspend video\n", core->name);
+ stop_video_dma(dev);
+ del_timer(&dev->vidq.timeout);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s/0: suspend vbi\n", core->name);
+ cx8800_stop_vbi_dma(dev);
+ del_timer(&dev->vbiq.timeout);
+ }
+ spin_unlock(&dev->slock);
+
+ if (core->ir)
+ cx88_ir_stop(core);
+ /* FIXME -- shutdown device */
+ cx88_shutdown(core);
+
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+ }
+ return 0;
+}
+
+static int cx8800_resume(struct pci_dev *pci_dev)
+{
+ struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+ int err;
+
+ if (dev->state.disabled) {
+ err=pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR "%s/0: can't enable device\n",
+ core->name);
+ return err;
+ }
+
+ dev->state.disabled = 0;
+ }
+ err= pci_set_power_state(pci_dev, PCI_D0);
+ if (err) {
+ printk(KERN_ERR "%s/0: can't set power state\n", core->name);
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+
+ return err;
+ }
+ pci_restore_state(pci_dev);
+
+ /* FIXME: re-initialize hardware */
+ cx88_reset(core);
+ if (core->ir)
+ cx88_ir_start(core);
+
+ cx_set(MO_PCI_INTMSK, core->pci_irqmask);
+
+ /* restart video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s/0: resume video\n", core->name);
+ restart_video_queue(dev,&dev->vidq);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s/0: resume vbi\n", core->name);
+ cx8800_restart_vbi_queue(dev,&dev->vbiq);
+ }
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+#endif
+
+/* ----------------------------------------------------------- */
+
+static const struct pci_device_id cx8800_pci_tbl[] = {
+ {
+ .vendor = 0x14f1,
+ .device = 0x8800,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },{
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
+
+static struct pci_driver cx8800_pci_driver = {
+ .name = "cx8800",
+ .id_table = cx8800_pci_tbl,
+ .probe = cx8800_initdev,
+ .remove = __devexit_p(cx8800_finidev),
+#ifdef CONFIG_PM
+ .suspend = cx8800_suspend,
+ .resume = cx8800_resume,
+#endif
+};
+
+static int __init cx8800_init(void)
+{
+ printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
+ CX88_VERSION);
+ return pci_register_driver(&cx8800_pci_driver);
+}
+
+static void __exit cx8800_fini(void)
+{
+ pci_unregister_driver(&cx8800_pci_driver);
+}
+
+module_init(cx8800_init);
+module_exit(cx8800_fini);
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c
index d77f8ecab9d7..d77f8ecab9d7 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.c
+++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.h b/drivers/media/pci/cx88/cx88-vp3054-i2c.h
index be99c931dc3e..be99c931dc3e 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.h
+++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.h
diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h
new file mode 100644
index 000000000000..44ffc8b3d45f
--- /dev/null
+++ b/drivers/media/pci/cx88/cx88.h
@@ -0,0 +1,748 @@
+/*
+ *
+ * v4l2 device driver for cx2388x based TV cards
+ *
+ * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/cx2341x.h>
+#include <media/videobuf-dvb.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/wm8775.h>
+
+#include "btcx-risc.h"
+#include "cx88-reg.h"
+#include "tuner-xc2028.h"
+
+#include <linux/mutex.h>
+
+#define CX88_VERSION "0.0.9"
+
+#define UNSET (-1U)
+
+#define CX88_MAXBOARDS 8
+
+/* Max number of inputs by card */
+#define MAX_CX88_INPUT 8
+
+/* ----------------------------------------------------------- */
+/* defines and enums */
+
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
+#define CX88_NORMS (V4L2_STD_ALL \
+ & ~V4L2_STD_PAL_H \
+ & ~V4L2_STD_NTSC_M_KR \
+ & ~V4L2_STD_SECAM_LC)
+
+#define FORMAT_FLAGS_PACKED 0x01
+#define FORMAT_FLAGS_PLANAR 0x02
+
+#define VBI_LINE_COUNT 17
+#define VBI_LINE_LENGTH 2048
+
+#define AUD_RDS_LINES 4
+
+/* need "shadow" registers for some write-only ones ... */
+#define SHADOW_AUD_VOL_CTL 1
+#define SHADOW_AUD_BAL_CTL 2
+#define SHADOW_MAX 3
+
+/* FM Radio deemphasis type */
+enum cx88_deemph_type {
+ FM_NO_DEEMPH = 0,
+ FM_DEEMPH_50,
+ FM_DEEMPH_75
+};
+
+enum cx88_board_type {
+ CX88_BOARD_NONE = 0,
+ CX88_MPEG_DVB,
+ CX88_MPEG_BLACKBIRD
+};
+
+enum cx8802_board_access {
+ CX8802_DRVCTL_SHARED = 1,
+ CX8802_DRVCTL_EXCLUSIVE = 2,
+};
+
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static unsigned int inline norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+
+static unsigned int inline norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+/* ----------------------------------------------------------- */
+/* static data */
+
+struct cx8800_fmt {
+ const char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+/* ----------------------------------------------------------- */
+/* SRAM memory management data (see cx88-core.c) */
+
+#define SRAM_CH21 0 /* video */
+#define SRAM_CH22 1
+#define SRAM_CH23 2
+#define SRAM_CH24 3 /* vbi */
+#define SRAM_CH25 4 /* audio */
+#define SRAM_CH26 5
+#define SRAM_CH28 6 /* mpeg */
+#define SRAM_CH27 7 /* audio rds */
+/* more */
+
+struct sram_channel {
+ const char *name;
+ u32 cmds_start;
+ u32 ctrl_start;
+ u32 cdt;
+ u32 fifo_start;
+ u32 fifo_size;
+ u32 ptr1_reg;
+ u32 ptr2_reg;
+ u32 cnt1_reg;
+ u32 cnt2_reg;
+};
+extern const struct sram_channel cx88_sram_channels[];
+
+/* ----------------------------------------------------------- */
+/* card configuration */
+
+#define CX88_BOARD_NOAUTO UNSET
+#define CX88_BOARD_UNKNOWN 0
+#define CX88_BOARD_HAUPPAUGE 1
+#define CX88_BOARD_GDI 2
+#define CX88_BOARD_PIXELVIEW 3
+#define CX88_BOARD_ATI_WONDER_PRO 4
+#define CX88_BOARD_WINFAST2000XP_EXPERT 5
+#define CX88_BOARD_AVERTV_STUDIO_303 6
+#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
+#define CX88_BOARD_WINFAST_DV2000 8
+#define CX88_BOARD_LEADTEK_PVR2000 9
+#define CX88_BOARD_IODATA_GVVCP3PCI 10
+#define CX88_BOARD_PROLINK_PLAYTVPVR 11
+#define CX88_BOARD_ASUS_PVR_416 12
+#define CX88_BOARD_MSI_TVANYWHERE 13
+#define CX88_BOARD_KWORLD_DVB_T 14
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
+#define CX88_BOARD_KWORLD_LTV883 16
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17
+#define CX88_BOARD_HAUPPAUGE_DVB_T1 18
+#define CX88_BOARD_CONEXANT_DVB_T1 19
+#define CX88_BOARD_PROVIDEO_PV259 20
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21
+#define CX88_BOARD_PCHDTV_HD3000 22
+#define CX88_BOARD_DNTV_LIVE_DVB_T 23
+#define CX88_BOARD_HAUPPAUGE_ROSLYN 24
+#define CX88_BOARD_DIGITALLOGIC_MEC 25
+#define CX88_BOARD_IODATA_GVBCTV7E 26
+#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28
+#define CX88_BOARD_ADSTECH_DVB_T_PCI 29
+#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31
+#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32
+#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33
+#define CX88_BOARD_ATI_HDTVWONDER 34
+#define CX88_BOARD_WINFAST_DTV1000 35
+#define CX88_BOARD_AVERTV_303 36
+#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37
+#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38
+#define CX88_BOARD_KWORLD_DVBS_100 39
+#define CX88_BOARD_HAUPPAUGE_HVR1100 40
+#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41
+#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42
+#define CX88_BOARD_KWORLD_DVB_T_CX22702 43
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44
+#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46
+#define CX88_BOARD_PCHDTV_HD5500 47
+#define CX88_BOARD_KWORLD_MCE200_DELUXE 48
+#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49
+#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50
+#define CX88_BOARD_WINFAST_DTV2000H 51
+#define CX88_BOARD_GENIATECH_DVBS 52
+#define CX88_BOARD_HAUPPAUGE_HVR3000 53
+#define CX88_BOARD_NORWOOD_MICRO 54
+#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
+#define CX88_BOARD_HAUPPAUGE_HVR1300 56
+#define CX88_BOARD_ADSTECH_PTV_390 57
+#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
+#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59
+#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61
+#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62
+#define CX88_BOARD_GENIATECH_X8000_MT 63
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
+#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
+#define CX88_BOARD_PROLINK_PV_8000GT 66
+#define CX88_BOARD_KWORLD_ATSC_120 67
+#define CX88_BOARD_HAUPPAUGE_HVR4000 68
+#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
+#define CX88_BOARD_TEVII_S460 70
+#define CX88_BOARD_OMICOM_SS4_PCI 71
+#define CX88_BOARD_TBS_8920 72
+#define CX88_BOARD_TEVII_S420 73
+#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
+#define CX88_BOARD_PROF_7300 75
+#define CX88_BOARD_SATTRADE_ST4200 76
+#define CX88_BOARD_TBS_8910 77
+#define CX88_BOARD_PROF_6200 78
+#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
+#define CX88_BOARD_HAUPPAUGE_IRONLY 80
+#define CX88_BOARD_WINFAST_DTV1800H 81
+#define CX88_BOARD_WINFAST_DTV2000H_J 82
+#define CX88_BOARD_PROF_7301 83
+#define CX88_BOARD_SAMSUNG_SMT_7020 84
+#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
+#define CX88_BOARD_TEVII_S464 86
+#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87
+#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
+#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
+
+enum cx88_itype {
+ CX88_VMUX_COMPOSITE1 = 1,
+ CX88_VMUX_COMPOSITE2,
+ CX88_VMUX_COMPOSITE3,
+ CX88_VMUX_COMPOSITE4,
+ CX88_VMUX_SVIDEO,
+ CX88_VMUX_TELEVISION,
+ CX88_VMUX_CABLE,
+ CX88_VMUX_DVB,
+ CX88_VMUX_DEBUG,
+ CX88_RADIO,
+};
+
+struct cx88_input {
+ enum cx88_itype type;
+ u32 gpio0, gpio1, gpio2, gpio3;
+ unsigned int vmux:2;
+ unsigned int audioroute:4;
+};
+
+struct cx88_board {
+ const char *name;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ int tda9887_conf;
+ struct cx88_input input[MAX_CX88_INPUT];
+ struct cx88_input radio;
+ enum cx88_board_type mpeg;
+ unsigned int audio_chip;
+ int num_frontends;
+
+ /* Used for I2S devices */
+ int i2sinputcntl;
+};
+
+struct cx88_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+enum cx88_tvaudio {
+ WW_NONE = 1,
+ WW_BTSC,
+ WW_BG,
+ WW_DK,
+ WW_I,
+ WW_L,
+ WW_EIAJ,
+ WW_I2SPT,
+ WW_FM,
+ WW_I2SADC,
+ WW_M
+};
+
+#define INPUT(nr) (core->board.input[nr])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status */
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(2000)
+
+/* buffer for one video frame */
+struct cx88_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* cx88 specific */
+ unsigned int bpl;
+ struct btcx_riscmem risc;
+ const struct cx8800_fmt *fmt;
+ u32 count;
+};
+
+struct cx88_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+ struct btcx_riscmem stopper;
+ u32 count;
+};
+
+struct cx88_core {
+ struct list_head devlist;
+ atomic_t refcount;
+
+ /* board name */
+ int nr;
+ char name[32];
+
+ /* pci stuff */
+ int pci_bus;
+ int pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ u32 shadow[SHADOW_MAX];
+ int pci_irqmask;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_state, i2c_rc;
+
+ /* config info -- analog */
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler video_hdl;
+ struct v4l2_ctrl *chroma_agc;
+ struct v4l2_ctrl_handler audio_hdl;
+ struct v4l2_subdev *sd_wm8775;
+ struct i2c_client *i2c_rtc;
+ unsigned int boardnr;
+ struct cx88_board board;
+
+ /* Supported V4L _STD_ tuner formats */
+ unsigned int tuner_formats;
+
+ /* config info -- dvb */
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+ int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+#endif
+ void (*gate_ctrl)(struct cx88_core *core, int open);
+
+ /* state info */
+ struct task_struct *kthread;
+ v4l2_std_id tvnorm;
+ enum cx88_tvaudio tvaudio;
+ u32 audiomode_manual;
+ u32 audiomode_current;
+ u32 input;
+ u32 last_analog_input;
+ u32 astat;
+ u32 use_nicam;
+ unsigned long last_change;
+
+ /* IR remote control state */
+ struct cx88_IR *ir;
+
+ /* I2C remote data */
+ struct IR_i2c_init_data init_data;
+ struct wm8775_platform_data wm8775_data;
+
+ struct mutex lock;
+ /* various v4l controls */
+ u32 freq;
+ int users;
+ int mpeg_users;
+
+ /* cx88-video needs to access cx8802 for hybrid tuner pll access. */
+ struct cx8802_dev *dvbdev;
+ enum cx88_board_type active_type_id;
+ int active_ref;
+ int active_fe_id;
+};
+
+static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
+}
+
+#define call_hw(core, grpid, o, f, args...) \
+ do { \
+ if (!core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ } while (0)
+
+#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
+
+#define WM8775_GID (1 << 0)
+
+#define wm8775_s_ctrl(core, id, val) \
+ do { \
+ struct v4l2_ctrl *ctrl_ = \
+ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
+ if (ctrl_ && !core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ v4l2_ctrl_s_ctrl(ctrl_, val); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ } while (0)
+
+#define wm8775_g_ctrl(core, id) \
+ ({ \
+ struct v4l2_ctrl *ctrl_ = \
+ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
+ s32 val = 0; \
+ if (ctrl_ && !core->i2c_rc) { \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 1); \
+ val = v4l2_ctrl_g_ctrl(ctrl_); \
+ if (core->gate_ctrl) \
+ core->gate_ctrl(core, 0); \
+ } \
+ val; \
+ })
+
+struct cx8800_dev;
+struct cx8802_dev;
+
+/* ----------------------------------------------------------- */
+/* function 0: video stuff */
+
+struct cx8800_fh {
+ struct v4l2_fh fh;
+ struct cx8800_dev *dev;
+ unsigned int resources;
+
+ /* video capture */
+ struct videobuf_queue vidq;
+
+ /* vbi capture */
+ struct videobuf_queue vbiq;
+};
+
+struct cx8800_suspend_state {
+ int disabled;
+};
+
+struct cx8800_dev {
+ struct cx88_core *core;
+ spinlock_t slock;
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+
+ const struct cx8800_fmt *fmt;
+ unsigned int width, height;
+
+ /* capture queues */
+ struct cx88_dmaqueue vidq;
+ struct cx88_dmaqueue vbiq;
+
+ /* various v4l controls */
+
+ /* other global state info */
+ struct cx8800_suspend_state state;
+};
+
+/* ----------------------------------------------------------- */
+/* function 1: audio/alsa stuff */
+/* =============> moved to cx88-alsa.c <====================== */
+
+
+/* ----------------------------------------------------------- */
+/* function 2: mpeg stuff */
+
+struct cx8802_fh {
+ struct v4l2_fh fh;
+ struct cx8802_dev *dev;
+ struct videobuf_queue mpegq;
+};
+
+struct cx8802_suspend_state {
+ int disabled;
+};
+
+struct cx8802_driver {
+ struct cx88_core *core;
+
+ /* List of drivers attached to device */
+ struct list_head drvlist;
+
+ /* Type of driver and access required */
+ enum cx88_board_type type_id;
+ enum cx8802_board_access hw_access;
+
+ /* MPEG 8802 internal only */
+ int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
+ int (*resume)(struct pci_dev *pci_dev);
+
+ /* Callers to the following functions must hold core->lock */
+
+ /* MPEG 8802 -> mini driver - Driver probe and configuration */
+ int (*probe)(struct cx8802_driver *drv);
+ int (*remove)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 -> mini driver - Access for hardware control */
+ int (*advise_acquire)(struct cx8802_driver *drv);
+ int (*advise_release)(struct cx8802_driver *drv);
+
+ /* MPEG 8802 <- mini driver - Access for hardware control */
+ int (*request_acquire)(struct cx8802_driver *drv);
+ int (*request_release)(struct cx8802_driver *drv);
+};
+
+struct cx8802_dev {
+ struct cx88_core *core;
+ spinlock_t slock;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+
+ /* dma queues */
+ struct cx88_dmaqueue mpegq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ /* other global state info */
+ struct cx8802_suspend_state state;
+
+ /* for blackbird only */
+ struct list_head devlist;
+#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
+ defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
+ struct video_device *mpeg_dev;
+ u32 mailbox;
+ int width;
+ int height;
+ unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
+
+ /* mpeg params */
+ struct cx2341x_handler cxhdl;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
+ /* for dvb only */
+ struct videobuf_dvb_frontends frontends;
+#endif
+
+#if defined(CONFIG_VIDEO_CX88_VP3054) || \
+ defined(CONFIG_VIDEO_CX88_VP3054_MODULE)
+ /* For VP3045 secondary I2C bus support */
+ struct vp3054_i2c_state *vp3054;
+#endif
+ /* for switching modulation types */
+ unsigned char ts_gen_cntrl;
+
+ /* List of attached drivers; must hold core->lock to access */
+ struct list_head drvlist;
+
+ struct work_struct request_module_wk;
+};
+
+/* ----------------------------------------------------------- */
+
+#define cx_read(reg) readl(core->lmmio + ((reg)>>2))
+#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2))
+#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg))
+
+#define cx_andor(reg,mask,value) \
+ writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\
+ ((value) & (mask)), core->lmmio+((reg)>>2))
+#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
+#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
+
+#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
+
+/* shadow registers */
+#define cx_sread(sreg) (core->shadow[sreg])
+#define cx_swrite(sreg,reg,value) \
+ (core->shadow[sreg] = value, \
+ writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+#define cx_sandor(sreg,reg,mask,value) \
+ (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
+ writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
+
+/* ----------------------------------------------------------- */
+/* cx88-core.c */
+
+extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
+ int len, u32 bits, u32 mask);
+
+extern int cx88_core_irq(struct cx88_core *core, u32 status);
+extern void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count);
+extern void cx88_shutdown(struct cx88_core *core);
+extern int cx88_reset(struct cx88_core *core);
+
+extern int
+cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+extern int
+cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines, unsigned int lpi);
+extern int
+cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+extern void
+cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
+
+extern void cx88_risc_disasm(struct cx88_core *core,
+ struct btcx_riscmem *risc);
+extern int cx88_sram_channel_setup(struct cx88_core *core,
+ const struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+extern void cx88_sram_channel_dump(struct cx88_core *core,
+ const struct sram_channel *ch);
+
+extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
+ unsigned int height, enum v4l2_field field);
+extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
+
+extern struct video_device *cx88_vdev_init(struct cx88_core *core,
+ struct pci_dev *pci,
+ const struct video_device *template_,
+ const char *type);
+extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
+extern void cx88_core_put(struct cx88_core *core,
+ struct pci_dev *pci);
+
+extern int cx88_start_audio_dma(struct cx88_core *core);
+extern int cx88_stop_audio_dma(struct cx88_core *core);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-vbi.c */
+
+/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */
+int cx8800_vbi_fmt (struct file *file, void *priv,
+ struct v4l2_format *f);
+
+/*
+int cx8800_start_vbi_dma(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf);
+*/
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
+int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
+ struct cx88_dmaqueue *q);
+void cx8800_vbi_timeout(unsigned long data);
+
+extern const struct videobuf_queue_ops cx8800_vbi_qops;
+
+/* ----------------------------------------------------------- */
+/* cx88-i2c.c */
+
+extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
+
+
+/* ----------------------------------------------------------- */
+/* cx88-cards.c */
+
+extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
+extern int cx88_get_resources(const struct cx88_core *core,
+ struct pci_dev *pci);
+extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
+extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
+
+/* ----------------------------------------------------------- */
+/* cx88-tvaudio.c */
+
+void cx88_set_tvaudio(struct cx88_core *core);
+void cx88_newstation(struct cx88_core *core);
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
+void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
+int cx88_audio_thread(void *data);
+
+int cx8802_register_driver(struct cx8802_driver *drv);
+int cx8802_unregister_driver(struct cx8802_driver *drv);
+
+/* Caller must hold core->lock */
+struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
+
+/* ----------------------------------------------------------- */
+/* cx88-dsp.c */
+
+s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-input.c */
+
+int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);
+int cx88_ir_fini(struct cx88_core *core);
+void cx88_ir_irq(struct cx88_core *core);
+int cx88_ir_start(struct cx88_core *core);
+void cx88_ir_stop(struct cx88_core *core);
+extern void cx88_i2c_init_ir(struct cx88_core *core);
+
+/* ----------------------------------------------------------- */
+/* cx88-mpeg.c */
+
+int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
+ struct cx88_buffer *buf, enum v4l2_field field);
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
+void cx8802_cancel_buffers(struct cx8802_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* cx88-video.c*/
+int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
+int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f);
+int cx88_video_mux(struct cx88_core *core, unsigned int input);
+void cx88_querycap(struct file *file, struct cx88_core *core,
+ struct v4l2_capability *cap);
diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig
new file mode 100644
index 000000000000..44e5dc15e60a
--- /dev/null
+++ b/drivers/media/pci/ddbridge/Kconfig
@@ -0,0 +1,18 @@
+config DVB_DDBRIDGE
+ tristate "Digital Devices bridge support"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ Support for cards with the Digital Devices PCI express bridge:
+ - Octopus PCIe Bridge
+ - Octopus mini PCIe Bridge
+ - Octopus LE
+ - DuoFlex S2 Octopus
+ - DuoFlex CT Octopus
+ - cineS2(v6)
+
+ Say Y if you own such a card and want to use it.
diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile
new file mode 100644
index 000000000000..7446c8b677b5
--- /dev/null
+++ b/drivers/media/pci/ddbridge/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the ddbridge device driver
+#
+
+ddbridge-objs := ddbridge-core.o
+
+obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
+
+# For the staging CI driver cxd2099
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c
new file mode 100644
index 000000000000..feff57ee5a08
--- /dev/null
+++ b/drivers/media/pci/ddbridge/ddbridge-core.c
@@ -0,0 +1,1730 @@
+/*
+ * ddbridge.c: Digital Devices PCIe bridge driver
+ *
+ * Copyright (C) 2010-2011 Digital Devices GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <linux/swab.h>
+#include <linux/vmalloc.h>
+#include "ddbridge.h"
+
+#include "ddbridge-regs.h"
+
+#include "tda18271c2dd.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "drxk.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* MSI had problems with lost interrupts, fixed but needs testing */
+#undef CONFIG_PCI_MSI
+
+/******************************************************************************/
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val)
+{
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = &reg, .len = 1 },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+ u16 reg, u8 *val)
+{
+ u8 msg[2] = {reg>>8, reg&0xff};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd)
+{
+ struct ddb *dev = i2c->dev;
+ int stat;
+ u32 val;
+
+ i2c->done = 0;
+ ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND);
+ stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ);
+ if (stat <= 0) {
+ printk(KERN_ERR "I2C timeout\n");
+ { /* MSI debugging*/
+ u32 istat = ddbreadl(INTERRUPT_STATUS);
+ printk(KERN_ERR "IRS %08x\n", istat);
+ ddbwritel(istat, INTERRUPT_ACK);
+ }
+ return -EIO;
+ }
+ val = ddbreadl(i2c->regs+I2C_COMMAND);
+ if (val & 0x70000)
+ return -EIO;
+ return 0;
+}
+
+static int ddb_i2c_master_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg msg[], int num)
+{
+ struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter);
+ struct ddb *dev = i2c->dev;
+ u8 addr = 0;
+
+ if (num)
+ addr = msg[0].addr;
+
+ if (num == 2 && msg[1].flags & I2C_M_RD &&
+ !(msg[0].flags & I2C_M_RD)) {
+ memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf,
+ msg[0].buf, msg[0].len);
+ ddbwritel(msg[0].len|(msg[1].len << 16),
+ i2c->regs+I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 1)) {
+ memcpy_fromio(msg[1].buf,
+ dev->regs + I2C_TASKMEM_BASE + i2c->rbuf,
+ msg[1].len);
+ return num;
+ }
+ }
+
+ if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len);
+ ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 2))
+ return num;
+ }
+ if (num == 1 && (msg[0].flags & I2C_M_RD)) {
+ ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH);
+ if (!ddb_i2c_cmd(i2c, addr, 3)) {
+ ddbcpyfrom(msg[0].buf,
+ I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len);
+ return num;
+ }
+ }
+ return -EIO;
+}
+
+
+static u32 ddb_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+struct i2c_algorithm ddb_i2c_algo = {
+ .master_xfer = ddb_i2c_master_xfer,
+ .functionality = ddb_i2c_functionality,
+};
+
+static void ddb_i2c_release(struct ddb *dev)
+{
+ int i;
+ struct ddb_i2c *i2c;
+ struct i2c_adapter *adap;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ i2c = &dev->i2c[i];
+ adap = &i2c->adap;
+ i2c_del_adapter(adap);
+ }
+}
+
+static int ddb_i2c_init(struct ddb *dev)
+{
+ int i, j, stat = 0;
+ struct ddb_i2c *i2c;
+ struct i2c_adapter *adap;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ i2c = &dev->i2c[i];
+ i2c->dev = dev;
+ i2c->nr = i;
+ i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4);
+ i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8);
+ i2c->regs = 0x80 + i * 0x20;
+ ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING);
+ ddbwritel((i2c->rbuf << 16) | i2c->wbuf,
+ i2c->regs + I2C_TASKADDRESS);
+ init_waitqueue_head(&i2c->wq);
+
+ adap = &i2c->adap;
+ i2c_set_adapdata(adap, i2c);
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+ adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG;
+#else
+#ifdef I2C_CLASS_TV_ANALOG
+ adap->class = I2C_CLASS_TV_ANALOG;
+#endif
+#endif
+ strcpy(adap->name, "ddbridge");
+ adap->algo = &ddb_i2c_algo;
+ adap->algo_data = (void *)i2c;
+ adap->dev.parent = &dev->pdev->dev;
+ stat = i2c_add_adapter(adap);
+ if (stat)
+ break;
+ }
+ if (stat)
+ for (j = 0; j < i; j++) {
+ i2c = &dev->i2c[j];
+ adap = &i2c->adap;
+ i2c_del_adapter(adap);
+ }
+ return stat;
+}
+
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static void set_table(struct ddb *dev, u32 off,
+ dma_addr_t *pbuf, u32 num)
+{
+ u32 i, base;
+ u64 mem;
+
+ base = DMA_BASE_ADDRESS_TABLE + off;
+ for (i = 0; i < num; i++) {
+ mem = pbuf[i];
+ ddbwritel(mem & 0xffffffff, base + i * 8);
+ ddbwritel(mem >> 32, base + i * 8 + 4);
+ }
+}
+#endif
+
+static void ddb_address_table(struct ddb *dev)
+{
+ u32 i, j, base;
+ u64 mem;
+ dma_addr_t *pbuf;
+
+ for (i = 0; i < dev->info->port_num * 2; i++) {
+ base = DMA_BASE_ADDRESS_TABLE + i * 0x100;
+ pbuf = dev->input[i].pbuf;
+ for (j = 0; j < dev->input[i].dma_buf_num; j++) {
+ mem = pbuf[j];
+ ddbwritel(mem & 0xffffffff, base + j * 8);
+ ddbwritel(mem >> 32, base + j * 8 + 4);
+ }
+ }
+ for (i = 0; i < dev->info->port_num; i++) {
+ base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100;
+ pbuf = dev->output[i].pbuf;
+ for (j = 0; j < dev->output[i].dma_buf_num; j++) {
+ mem = pbuf[j];
+ ddbwritel(mem & 0xffffffff, base + j * 8);
+ ddbwritel(mem >> 32, base + j * 8 + 4);
+ }
+ }
+}
+
+static void io_free(struct pci_dev *pdev, u8 **vbuf,
+ dma_addr_t *pbuf, u32 size, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ if (vbuf[i]) {
+ pci_free_consistent(pdev, size, vbuf[i], pbuf[i]);
+ vbuf[i] = 0;
+ }
+ }
+}
+
+static int io_alloc(struct pci_dev *pdev, u8 **vbuf,
+ dma_addr_t *pbuf, u32 size, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]);
+ if (!vbuf[i])
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int ddb_buffers_alloc(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ if (io_alloc(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num) < 0)
+ return -1;
+ if (io_alloc(dev->pdev, port->input[1]->vbuf,
+ port->input[1]->pbuf,
+ port->input[1]->dma_buf_size,
+ port->input[1]->dma_buf_num) < 0)
+ return -1;
+ break;
+ case DDB_PORT_CI:
+ if (io_alloc(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num) < 0)
+ return -1;
+ if (io_alloc(dev->pdev, port->output->vbuf,
+ port->output->pbuf,
+ port->output->dma_buf_size,
+ port->output->dma_buf_num) < 0)
+ return -1;
+ break;
+ default:
+ break;
+ }
+ }
+ ddb_address_table(dev);
+ return 0;
+}
+
+static void ddb_buffers_free(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ io_free(dev->pdev, port->input[0]->vbuf,
+ port->input[0]->pbuf,
+ port->input[0]->dma_buf_size,
+ port->input[0]->dma_buf_num);
+ io_free(dev->pdev, port->input[1]->vbuf,
+ port->input[1]->pbuf,
+ port->input[1]->dma_buf_size,
+ port->input[1]->dma_buf_num);
+ io_free(dev->pdev, port->output->vbuf,
+ port->output->pbuf,
+ port->output->dma_buf_size,
+ port->output->dma_buf_num);
+ }
+}
+
+static void ddb_input_start(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+
+ spin_lock_irq(&input->lock);
+ input->cbuf = 0;
+ input->coff = 0;
+
+ /* reset */
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(2, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+
+ ddbwritel((1 << 16) |
+ (input->dma_buf_num << 11) |
+ (input->dma_buf_size >> 7),
+ DMA_BUFFER_SIZE(input->nr));
+ ddbwritel(0, DMA_BUFFER_ACK(input->nr));
+
+ ddbwritel(1, DMA_BASE_WRITE);
+ ddbwritel(3, DMA_BUFFER_CONTROL(input->nr));
+ ddbwritel(9, TS_INPUT_CONTROL(input->nr));
+ input->running = 1;
+ spin_unlock_irq(&input->lock);
+}
+
+static void ddb_input_stop(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+
+ spin_lock_irq(&input->lock);
+ ddbwritel(0, TS_INPUT_CONTROL(input->nr));
+ ddbwritel(0, DMA_BUFFER_CONTROL(input->nr));
+ input->running = 0;
+ spin_unlock_irq(&input->lock);
+}
+
+static void ddb_output_start(struct ddb_output *output)
+{
+ struct ddb *dev = output->port->dev;
+
+ spin_lock_irq(&output->lock);
+ output->cbuf = 0;
+ output->coff = 0;
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(2, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel((1 << 16) |
+ (output->dma_buf_num << 11) |
+ (output->dma_buf_size >> 7),
+ DMA_BUFFER_SIZE(output->nr + 8));
+ ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8));
+
+ ddbwritel(1, DMA_BASE_READ);
+ ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8));
+ /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */
+ ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr));
+ output->running = 1;
+ spin_unlock_irq(&output->lock);
+}
+
+static void ddb_output_stop(struct ddb_output *output)
+{
+ struct ddb *dev = output->port->dev;
+
+ spin_lock_irq(&output->lock);
+ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr));
+ ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8));
+ output->running = 0;
+ spin_unlock_irq(&output->lock);
+}
+
+static u32 ddb_output_free(struct ddb_output *output)
+{
+ u32 idx, off, stat = output->stat;
+ s32 diff;
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ if (output->cbuf != idx) {
+ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+ (output->dma_buf_size - output->coff <= 188))
+ return 0;
+ return 188;
+ }
+ diff = off - output->coff;
+ if (diff <= 0 || diff > 188)
+ return 188;
+ return 0;
+}
+
+static ssize_t ddb_output_write(struct ddb_output *output,
+ const u8 *buf, size_t count)
+{
+ struct ddb *dev = output->port->dev;
+ u32 idx, off, stat = output->stat;
+ u32 left = count, len;
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ while (left) {
+ len = output->dma_buf_size - output->coff;
+ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) &&
+ (off == 0)) {
+ if (len <= 188)
+ break;
+ len -= 188;
+ }
+ if (output->cbuf == idx) {
+ if (off > output->coff) {
+#if 1
+ len = off - output->coff;
+ len -= (len % 188);
+ if (len <= 188)
+
+#endif
+ break;
+ len -= 188;
+ }
+ }
+ if (len > left)
+ len = left;
+ if (copy_from_user(output->vbuf[output->cbuf] + output->coff,
+ buf, len))
+ return -EIO;
+ left -= len;
+ buf += len;
+ output->coff += len;
+ if (output->coff == output->dma_buf_size) {
+ output->coff = 0;
+ output->cbuf = ((output->cbuf + 1) % output->dma_buf_num);
+ }
+ ddbwritel((output->cbuf << 11) | (output->coff >> 7),
+ DMA_BUFFER_ACK(output->nr + 8));
+ }
+ return count - left;
+}
+
+static u32 ddb_input_avail(struct ddb_input *input)
+{
+ struct ddb *dev = input->port->dev;
+ u32 idx, off, stat = input->stat;
+ u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr));
+
+ idx = (stat >> 11) & 0x1f;
+ off = (stat & 0x7ff) << 7;
+
+ if (ctrl & 4) {
+ printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl);
+ ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr));
+ return 0;
+ }
+ if (input->cbuf != idx)
+ return 188;
+ return 0;
+}
+
+static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count)
+{
+ struct ddb *dev = input->port->dev;
+ u32 left = count;
+ u32 idx, free, stat = input->stat;
+ int ret;
+
+ idx = (stat >> 11) & 0x1f;
+
+ while (left) {
+ if (input->cbuf == idx)
+ return count - left;
+ free = input->dma_buf_size - input->coff;
+ if (free > left)
+ free = left;
+ ret = copy_to_user(buf, input->vbuf[input->cbuf] +
+ input->coff, free);
+ if (ret)
+ return -EFAULT;
+ input->coff += free;
+ if (input->coff == input->dma_buf_size) {
+ input->coff = 0;
+ input->cbuf = (input->cbuf+1) % input->dma_buf_num;
+ }
+ left -= free;
+ ddbwritel((input->cbuf << 11) | (input->coff >> 7),
+ DMA_BUFFER_ACK(input->nr));
+ }
+ return count;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+#if 0
+static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe)
+{
+ int i;
+
+ for (i = 0; i < dev->info->port_num * 2; i++) {
+ if (dev->input[i].fe == fe)
+ return &dev->input[i];
+ }
+ return NULL;
+}
+#endif
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct ddb_input *input = fe->sec_priv;
+ struct ddb_port *port = input->port;
+ int status;
+
+ if (enable) {
+ mutex_lock(&port->i2c_gate_lock);
+ status = input->gate_ctrl(fe, 1);
+ } else {
+ status = input->gate_ctrl(fe, 0);
+ mutex_unlock(&port->i2c_gate_lock);
+ }
+ return status;
+}
+
+static int demod_attach_drxk(struct ddb_input *input)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct dvb_frontend *fe;
+ struct drxk_config config;
+
+ memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
+ config.qam_demod_parameter_count = 4;
+ config.adr = 0x29 + (input->nr & 1);
+
+ fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
+ if (!input->fe) {
+ printk(KERN_ERR "No DRXK found!\n");
+ return -ENODEV;
+ }
+ fe->sec_priv = input;
+ input->gate_ctrl = fe->ops.i2c_gate_ctrl;
+ fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
+static int tuner_attach_tda18271(struct ddb_input *input)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct dvb_frontend *fe;
+
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 1);
+ fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60);
+ if (!fe) {
+ printk(KERN_ERR "No TDA18271 found!\n");
+ return -ENODEV;
+ }
+ if (input->fe->ops.i2c_gate_ctrl)
+ input->fe->ops.i2c_gate_ctrl(input->fe, 0);
+ return 0;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct stv090x_config stv0900 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+};
+
+static struct stv090x_config stv0900_aa = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+};
+
+static struct stv6110x_config stv6110a = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct stv6110x_config stv6110b = {
+ .addr = 0x63,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static int demod_attach_stv0900(struct ddb_input *input, int type)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+
+ input->fe = dvb_attach(stv090x_attach, feconf, i2c,
+ (input->nr & 1) ? STV090x_DEMODULATOR_1
+ : STV090x_DEMODULATOR_0);
+ if (!input->fe) {
+ printk(KERN_ERR "No STV0900 found!\n");
+ return -ENODEV;
+ }
+ if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0,
+ 0, (input->nr & 1) ?
+ (0x09 - type) : (0x0b - type))) {
+ printk(KERN_ERR "No LNBH24 found!\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tuner_attach_stv6110(struct ddb_input *input, int type)
+{
+ struct i2c_adapter *i2c = &input->port->i2c->adap;
+ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900;
+ struct stv6110x_config *tunerconf = (input->nr & 1) ?
+ &stv6110b : &stv6110a;
+ struct stv6110x_devctl *ctl;
+
+ ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c);
+ if (!ctl) {
+ printk(KERN_ERR "No STV6110X found!\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "attach tuner input %d adr %02x\n",
+ input->nr, tunerconf->addr);
+
+ feconf->tuner_init = ctl->tuner_init;
+ feconf->tuner_sleep = ctl->tuner_sleep;
+ feconf->tuner_set_mode = ctl->tuner_set_mode;
+ feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+ feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
+ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
+ feconf->tuner_set_refclk = ctl->tuner_set_refclk;
+ feconf->tuner_get_status = ctl->tuner_get_status;
+
+ return 0;
+}
+
+static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id,
+ int (*start_feed)(struct dvb_demux_feed *),
+ int (*stop_feed)(struct dvb_demux_feed *),
+ void *priv)
+{
+ dvbdemux->priv = priv;
+
+ dvbdemux->filternum = 256;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = start_feed;
+ dvbdemux->stop_feed = stop_feed;
+ dvbdemux->write_to_decoder = NULL;
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
+ return dvb_dmx_init(dvbdemux);
+}
+
+static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev,
+ struct dvb_demux *dvbdemux,
+ struct dmx_frontend *hw_frontend,
+ struct dmx_frontend *mem_frontend,
+ struct dvb_adapter *dvb_adapter)
+{
+ int ret;
+
+ dmxdev->filternum = 256;
+ dmxdev->demux = &dvbdemux->dmx;
+ dmxdev->capabilities = 0;
+ ret = dvb_dmxdev_init(dmxdev, dvb_adapter);
+ if (ret < 0)
+ return ret;
+
+ hw_frontend->source = DMX_FRONTEND_0;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend);
+ mem_frontend->source = DMX_MEMORY_FE;
+ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend);
+ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend);
+}
+
+static int start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ddb_input *input = dvbdmx->priv;
+
+ if (!input->users)
+ ddb_input_start(input);
+
+ return ++input->users;
+}
+
+static int stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct ddb_input *input = dvbdmx->priv;
+
+ if (--input->users)
+ return input->users;
+
+ ddb_input_stop(input);
+ return 0;
+}
+
+
+static void dvb_input_detach(struct ddb_input *input)
+{
+ struct dvb_adapter *adap = &input->adap;
+ struct dvb_demux *dvbdemux = &input->demux;
+
+ switch (input->attached) {
+ case 5:
+ if (input->fe2)
+ dvb_unregister_frontend(input->fe2);
+ if (input->fe) {
+ dvb_unregister_frontend(input->fe);
+ dvb_frontend_detach(input->fe);
+ input->fe = NULL;
+ }
+ case 4:
+ dvb_net_release(&input->dvbnet);
+
+ case 3:
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &input->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &input->mem_frontend);
+ dvb_dmxdev_release(&input->dmxdev);
+
+ case 2:
+ dvb_dmx_release(&input->demux);
+
+ case 1:
+ dvb_unregister_adapter(adap);
+ }
+ input->attached = 0;
+}
+
+static int dvb_input_attach(struct ddb_input *input)
+{
+ int ret;
+ struct ddb_port *port = input->port;
+ struct dvb_adapter *adap = &input->adap;
+ struct dvb_demux *dvbdemux = &input->demux;
+
+ ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE,
+ &input->port->dev->pdev->dev,
+ adapter_nr);
+ if (ret < 0) {
+ printk(KERN_ERR "ddbridge: Could not register adapter."
+ "Check if you enabled enough adapters in dvb-core!\n");
+ return ret;
+ }
+ input->attached = 1;
+
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+ start_feed,
+ stop_feed, input);
+ if (ret < 0)
+ return ret;
+ input->attached = 2;
+
+ ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux,
+ &input->hw_frontend,
+ &input->mem_frontend, adap);
+ if (ret < 0)
+ return ret;
+ input->attached = 3;
+
+ ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux);
+ if (ret < 0)
+ return ret;
+ input->attached = 4;
+
+ input->fe = 0;
+ switch (port->type) {
+ case DDB_TUNER_DVBS_ST:
+ if (demod_attach_stv0900(input, 0) < 0)
+ return -ENODEV;
+ if (tuner_attach_stv6110(input, 0) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_DVBS_ST_AA:
+ if (demod_attach_stv0900(input, 1) < 0)
+ return -ENODEV;
+ if (tuner_attach_stv6110(input, 1) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ break;
+ case DDB_TUNER_DVBCT_TR:
+ if (demod_attach_drxk(input) < 0)
+ return -ENODEV;
+ if (tuner_attach_tda18271(input) < 0)
+ return -ENODEV;
+ if (input->fe) {
+ if (dvb_register_frontend(adap, input->fe) < 0)
+ return -ENODEV;
+ }
+ if (input->fe2) {
+ if (dvb_register_frontend(adap, input->fe2) < 0)
+ return -ENODEV;
+ input->fe2->tuner_priv = input->fe->tuner_priv;
+ memcpy(&input->fe2->ops.tuner_ops,
+ &input->fe->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ }
+ break;
+ }
+ input->attached = 5;
+ return 0;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static ssize_t ts_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ size_t left = count;
+ int stat;
+
+ while (left) {
+ if (ddb_output_free(output) < 188) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ if (wait_event_interruptible(
+ output->wq, ddb_output_free(output) >= 188) < 0)
+ break;
+ }
+ stat = ddb_output_write(output, buf, left);
+ if (stat < 0)
+ break;
+ buf += stat;
+ left -= stat;
+ }
+ return (left == count) ? -EAGAIN : (count - left);
+}
+
+static ssize_t ts_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ struct ddb_input *input = output->port->input[0];
+ int left, read;
+
+ count -= count % 188;
+ left = count;
+ while (left) {
+ if (ddb_input_avail(input) < 188) {
+ if (file->f_flags & O_NONBLOCK)
+ break;
+ if (wait_event_interruptible(
+ input->wq, ddb_input_avail(input) >= 188) < 0)
+ break;
+ }
+ read = ddb_input_read(input, buf, left);
+ if (read < 0)
+ return read;
+ left -= read;
+ buf += read;
+ }
+ return (left == count) ? -EAGAIN : (count - left);
+}
+
+static unsigned int ts_poll(struct file *file, poll_table *wait)
+{
+ /*
+ struct dvb_device *dvbdev = file->private_data;
+ struct ddb_output *output = dvbdev->priv;
+ struct ddb_input *input = output->port->input[0];
+ */
+ unsigned int mask = 0;
+
+#if 0
+ if (data_avail_to_read)
+ mask |= POLLIN | POLLRDNORM;
+ if (data_avail_to_write)
+ mask |= POLLOUT | POLLWRNORM;
+
+ poll_wait(file, &read_queue, wait);
+ poll_wait(file, &write_queue, wait);
+#endif
+ return mask;
+}
+
+static const struct file_operations ci_fops = {
+ .owner = THIS_MODULE,
+ .read = ts_read,
+ .write = ts_write,
+ .open = dvb_generic_open,
+ .release = dvb_generic_release,
+ .poll = ts_poll,
+ .mmap = 0,
+};
+
+static struct dvb_device dvbdev_ci = {
+ .priv = 0,
+ .readers = -1,
+ .writers = -1,
+ .users = -1,
+ .fops = &ci_fops,
+};
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void input_tasklet(unsigned long data)
+{
+ struct ddb_input *input = (struct ddb_input *) data;
+ struct ddb *dev = input->port->dev;
+
+ spin_lock(&input->lock);
+ if (!input->running) {
+ spin_unlock(&input->lock);
+ return;
+ }
+ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+
+ if (input->port->class == DDB_PORT_TUNER) {
+ if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))
+ printk(KERN_ERR "Overflow input %d\n", input->nr);
+ while (input->cbuf != ((input->stat >> 11) & 0x1f)
+ || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) {
+ dvb_dmx_swfilter_packets(&input->demux,
+ input->vbuf[input->cbuf],
+ input->dma_buf_size / 188);
+
+ input->cbuf = (input->cbuf + 1) % input->dma_buf_num;
+ ddbwritel((input->cbuf << 11),
+ DMA_BUFFER_ACK(input->nr));
+ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr));
+ }
+ }
+ if (input->port->class == DDB_PORT_CI)
+ wake_up(&input->wq);
+ spin_unlock(&input->lock);
+}
+
+static void output_tasklet(unsigned long data)
+{
+ struct ddb_output *output = (struct ddb_output *) data;
+ struct ddb *dev = output->port->dev;
+
+ spin_lock(&output->lock);
+ if (!output->running) {
+ spin_unlock(&output->lock);
+ return;
+ }
+ output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8));
+ wake_up(&output->wq);
+ spin_unlock(&output->lock);
+}
+
+
+struct cxd2099_cfg cxd_cfg = {
+ .bitrate = 62000,
+ .adr = 0x40,
+ .polarity = 1,
+ .clock_mode = 1,
+};
+
+static int ddb_ci_attach(struct ddb_port *port)
+{
+ int ret;
+
+ ret = dvb_register_adapter(&port->output->adap,
+ "DDBridge",
+ THIS_MODULE,
+ &port->dev->pdev->dev,
+ adapter_nr);
+ if (ret < 0)
+ return ret;
+ port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap);
+ if (!port->en) {
+ dvb_unregister_adapter(&port->output->adap);
+ return -ENODEV;
+ }
+ ddb_input_start(port->input[0]);
+ ddb_output_start(port->output);
+ dvb_ca_en50221_init(&port->output->adap,
+ port->en, 0, 1);
+ ret = dvb_register_device(&port->output->adap, &port->output->dev,
+ &dvbdev_ci, (void *) port->output,
+ DVB_DEVICE_SEC);
+ return ret;
+}
+
+static int ddb_port_attach(struct ddb_port *port)
+{
+ int ret = 0;
+
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ ret = dvb_input_attach(port->input[0]);
+ if (ret < 0)
+ break;
+ ret = dvb_input_attach(port->input[1]);
+ break;
+ case DDB_PORT_CI:
+ ret = ddb_ci_attach(port);
+ break;
+ default:
+ break;
+ }
+ if (ret < 0)
+ printk(KERN_ERR "port_attach on port %d failed\n", port->nr);
+ return ret;
+}
+
+static int ddb_ports_attach(struct ddb *dev)
+{
+ int i, ret = 0;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ ret = ddb_port_attach(port);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static void ddb_ports_detach(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ switch (port->class) {
+ case DDB_PORT_TUNER:
+ dvb_input_detach(port->input[0]);
+ dvb_input_detach(port->input[1]);
+ break;
+ case DDB_PORT_CI:
+ if (port->output->dev)
+ dvb_unregister_device(port->output->dev);
+ if (port->en) {
+ ddb_input_stop(port->input[0]);
+ ddb_output_stop(port->output);
+ dvb_ca_en50221_release(port->en);
+ kfree(port->en);
+ port->en = 0;
+ dvb_unregister_adapter(&port->output->adap);
+ }
+ break;
+ }
+ }
+}
+
+/****************************************************************************/
+/****************************************************************************/
+
+static int port_has_ci(struct ddb_port *port)
+{
+ u8 val;
+ return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1;
+}
+
+static int port_has_stv0900(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_stv0900_aa(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_drxks(struct ddb_port *port)
+{
+ u8 val;
+ if (i2c_read(&port->i2c->adap, 0x29, &val) < 0)
+ return 0;
+ if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static void ddb_port_probe(struct ddb_port *port)
+{
+ struct ddb *dev = port->dev;
+ char *modname = "NO MODULE";
+
+ port->class = DDB_PORT_NONE;
+
+ if (port_has_ci(port)) {
+ modname = "CI";
+ port->class = DDB_PORT_CI;
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_stv0900(port)) {
+ modname = "DUAL DVB-S2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBS_ST;
+ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_stv0900_aa(port)) {
+ modname = "DUAL DVB-S2";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBS_ST_AA;
+ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING);
+ } else if (port_has_drxks(port)) {
+ modname = "DUAL DVB-C/T";
+ port->class = DDB_PORT_TUNER;
+ port->type = DDB_TUNER_DVBCT_TR;
+ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING);
+ }
+ printk(KERN_INFO "Port %d (TAB %d): %s\n",
+ port->nr, port->nr+1, modname);
+}
+
+static void ddb_input_init(struct ddb_port *port, int nr)
+{
+ struct ddb *dev = port->dev;
+ struct ddb_input *input = &dev->input[nr];
+
+ input->nr = nr;
+ input->port = port;
+ input->dma_buf_num = INPUT_DMA_BUFS;
+ input->dma_buf_size = INPUT_DMA_SIZE;
+ ddbwritel(0, TS_INPUT_CONTROL(nr));
+ ddbwritel(2, TS_INPUT_CONTROL(nr));
+ ddbwritel(0, TS_INPUT_CONTROL(nr));
+ ddbwritel(0, DMA_BUFFER_ACK(nr));
+ tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input);
+ spin_lock_init(&input->lock);
+ init_waitqueue_head(&input->wq);
+}
+
+static void ddb_output_init(struct ddb_port *port, int nr)
+{
+ struct ddb *dev = port->dev;
+ struct ddb_output *output = &dev->output[nr];
+ output->nr = nr;
+ output->port = port;
+ output->dma_buf_num = OUTPUT_DMA_BUFS;
+ output->dma_buf_size = OUTPUT_DMA_SIZE;
+
+ ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+ ddbwritel(2, TS_OUTPUT_CONTROL(nr));
+ ddbwritel(0, TS_OUTPUT_CONTROL(nr));
+ tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output);
+ init_waitqueue_head(&output->wq);
+}
+
+static void ddb_ports_init(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ port->dev = dev;
+ port->nr = i;
+ port->i2c = &dev->i2c[i];
+ port->input[0] = &dev->input[2 * i];
+ port->input[1] = &dev->input[2 * i + 1];
+ port->output = &dev->output[i];
+
+ mutex_init(&port->i2c_gate_lock);
+ ddb_port_probe(port);
+ ddb_input_init(port, 2 * i);
+ ddb_input_init(port, 2 * i + 1);
+ ddb_output_init(port, i);
+ }
+}
+
+static void ddb_ports_release(struct ddb *dev)
+{
+ int i;
+ struct ddb_port *port;
+
+ for (i = 0; i < dev->info->port_num; i++) {
+ port = &dev->port[i];
+ port->dev = dev;
+ tasklet_kill(&port->input[0]->tasklet);
+ tasklet_kill(&port->input[1]->tasklet);
+ tasklet_kill(&port->output->tasklet);
+ }
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void irq_handle_i2c(struct ddb *dev, int n)
+{
+ struct ddb_i2c *i2c = &dev->i2c[n];
+
+ i2c->done = 1;
+ wake_up(&i2c->wq);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+ struct ddb *dev = (struct ddb *) dev_id;
+ u32 s = ddbreadl(INTERRUPT_STATUS);
+
+ if (!s)
+ return IRQ_NONE;
+
+ do {
+ ddbwritel(s, INTERRUPT_ACK);
+
+ if (s & 0x00000001)
+ irq_handle_i2c(dev, 0);
+ if (s & 0x00000002)
+ irq_handle_i2c(dev, 1);
+ if (s & 0x00000004)
+ irq_handle_i2c(dev, 2);
+ if (s & 0x00000008)
+ irq_handle_i2c(dev, 3);
+
+ if (s & 0x00000100)
+ tasklet_schedule(&dev->input[0].tasklet);
+ if (s & 0x00000200)
+ tasklet_schedule(&dev->input[1].tasklet);
+ if (s & 0x00000400)
+ tasklet_schedule(&dev->input[2].tasklet);
+ if (s & 0x00000800)
+ tasklet_schedule(&dev->input[3].tasklet);
+ if (s & 0x00001000)
+ tasklet_schedule(&dev->input[4].tasklet);
+ if (s & 0x00002000)
+ tasklet_schedule(&dev->input[5].tasklet);
+ if (s & 0x00004000)
+ tasklet_schedule(&dev->input[6].tasklet);
+ if (s & 0x00008000)
+ tasklet_schedule(&dev->input[7].tasklet);
+
+ if (s & 0x00010000)
+ tasklet_schedule(&dev->output[0].tasklet);
+ if (s & 0x00020000)
+ tasklet_schedule(&dev->output[1].tasklet);
+ if (s & 0x00040000)
+ tasklet_schedule(&dev->output[2].tasklet);
+ if (s & 0x00080000)
+ tasklet_schedule(&dev->output[3].tasklet);
+
+ /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */
+ } while ((s = ddbreadl(INTERRUPT_STATUS)));
+
+ return IRQ_HANDLED;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen)
+{
+ u32 data, shift;
+
+ if (wlen > 4)
+ ddbwritel(1, SPI_CONTROL);
+ while (wlen > 4) {
+ /* FIXME: check for big-endian */
+ data = swab32(*(u32 *)wbuf);
+ wbuf += 4;
+ wlen -= 4;
+ ddbwritel(data, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+ }
+
+ if (rlen)
+ ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+ else
+ ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL);
+
+ data = 0;
+ shift = ((4 - wlen) * 8);
+ while (wlen) {
+ data <<= 8;
+ data |= *wbuf;
+ wlen--;
+ wbuf++;
+ }
+ if (shift)
+ data <<= shift;
+ ddbwritel(data, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+
+ if (!rlen) {
+ ddbwritel(0, SPI_CONTROL);
+ return 0;
+ }
+ if (rlen > 4)
+ ddbwritel(1, SPI_CONTROL);
+
+ while (rlen > 4) {
+ ddbwritel(0xffffffff, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+ data = ddbreadl(SPI_DATA);
+ *(u32 *) rbuf = swab32(data);
+ rbuf += 4;
+ rlen -= 4;
+ }
+ ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL);
+ ddbwritel(0xffffffff, SPI_DATA);
+ while (ddbreadl(SPI_CONTROL) & 0x0004)
+ ;
+
+ data = ddbreadl(SPI_DATA);
+ ddbwritel(0, SPI_CONTROL);
+
+ if (rlen < 4)
+ data <<= ((4 - rlen) * 8);
+
+ while (rlen > 0) {
+ *rbuf = ((data >> 24) & 0xff);
+ data <<= 8;
+ rbuf++;
+ rlen--;
+ }
+ return 0;
+}
+
+#define DDB_MAGIC 'd'
+
+struct ddb_flashio {
+ __u8 *write_buf;
+ __u32 write_len;
+ __u8 *read_buf;
+ __u32 read_len;
+};
+
+#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio)
+
+#define DDB_NAME "ddbridge"
+
+static u32 ddb_num;
+static struct ddb *ddbs[32];
+static struct class *ddb_class;
+static int ddb_major;
+
+static int ddb_open(struct inode *inode, struct file *file)
+{
+ struct ddb *dev = ddbs[iminor(inode)];
+
+ file->private_data = dev;
+ return 0;
+}
+
+static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct ddb *dev = file->private_data;
+ void *parg = (void *)arg;
+ int res;
+
+ switch (cmd) {
+ case IOCTL_DDB_FLASHIO:
+ {
+ struct ddb_flashio fio;
+ u8 *rbuf, *wbuf;
+
+ if (copy_from_user(&fio, parg, sizeof(fio)))
+ return -EFAULT;
+
+ if (fio.write_len > 1028 || fio.read_len > 1028)
+ return -EINVAL;
+ if (fio.write_len + fio.read_len > 1028)
+ return -EINVAL;
+
+ wbuf = &dev->iobuf[0];
+ rbuf = wbuf + fio.write_len;
+
+ if (copy_from_user(wbuf, fio.write_buf, fio.write_len))
+ return -EFAULT;
+ res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len);
+ if (res)
+ return res;
+ if (copy_to_user(fio.read_buf, rbuf, fio.read_len))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct file_operations ddb_fops = {
+ .unlocked_ioctl = ddb_ioctl,
+ .open = ddb_open,
+};
+
+static char *ddb_devnode(struct device *device, umode_t *mode)
+{
+ struct ddb *dev = dev_get_drvdata(device);
+
+ return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr);
+}
+
+static int ddb_class_create(void)
+{
+ ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops);
+ if (ddb_major < 0)
+ return ddb_major;
+
+ ddb_class = class_create(THIS_MODULE, DDB_NAME);
+ if (IS_ERR(ddb_class)) {
+ unregister_chrdev(ddb_major, DDB_NAME);
+ return PTR_ERR(ddb_class);
+ }
+ ddb_class->devnode = ddb_devnode;
+ return 0;
+}
+
+static void ddb_class_destroy(void)
+{
+ class_destroy(ddb_class);
+ unregister_chrdev(ddb_major, DDB_NAME);
+}
+
+static int ddb_device_create(struct ddb *dev)
+{
+ dev->nr = ddb_num++;
+ dev->ddb_dev = device_create(ddb_class, NULL,
+ MKDEV(ddb_major, dev->nr),
+ dev, "ddbridge%d", dev->nr);
+ ddbs[dev->nr] = dev;
+ if (IS_ERR(dev->ddb_dev))
+ return -1;
+ return 0;
+}
+
+static void ddb_device_destroy(struct ddb *dev)
+{
+ ddb_num--;
+ if (IS_ERR(dev->ddb_dev))
+ return;
+ device_destroy(ddb_class, MKDEV(ddb_major, 0));
+}
+
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void ddb_unmap(struct ddb *dev)
+{
+ if (dev->regs)
+ iounmap(dev->regs);
+ vfree(dev);
+}
+
+
+static void __devexit ddb_remove(struct pci_dev *pdev)
+{
+ struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev);
+
+ ddb_ports_detach(dev);
+ ddb_i2c_release(dev);
+
+ ddbwritel(0, INTERRUPT_ENABLE);
+ free_irq(dev->pdev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+#endif
+ ddb_ports_release(dev);
+ ddb_buffers_free(dev);
+ ddb_device_destroy(dev);
+
+ ddb_unmap(dev);
+ pci_set_drvdata(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+
+static int __devinit ddb_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct ddb *dev;
+ int stat = 0;
+ int irq_flag = IRQF_SHARED;
+
+ if (pci_enable_device(pdev) < 0)
+ return -ENODEV;
+
+ dev = vmalloc(sizeof(struct ddb));
+ if (dev == NULL)
+ return -ENOMEM;
+ memset(dev, 0, sizeof(struct ddb));
+
+ dev->pdev = pdev;
+ pci_set_drvdata(pdev, dev);
+ dev->info = (struct ddb_info *) id->driver_data;
+ printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name);
+
+ dev->regs = ioremap(pci_resource_start(dev->pdev, 0),
+ pci_resource_len(dev->pdev, 0));
+ if (!dev->regs) {
+ stat = -ENOMEM;
+ goto fail;
+ }
+ printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4));
+
+#ifdef CONFIG_PCI_MSI
+ if (pci_msi_enabled())
+ stat = pci_enable_msi(dev->pdev);
+ if (stat) {
+ printk(KERN_INFO ": MSI not available.\n");
+ } else {
+ irq_flag = 0;
+ dev->msi = 1;
+ }
+#endif
+ stat = request_irq(dev->pdev->irq, irq_handler,
+ irq_flag, "DDBridge", (void *) dev);
+ if (stat < 0)
+ goto fail1;
+ ddbwritel(0, DMA_BASE_WRITE);
+ ddbwritel(0, DMA_BASE_READ);
+ ddbwritel(0xffffffff, INTERRUPT_ACK);
+ ddbwritel(0xfff0f, INTERRUPT_ENABLE);
+ ddbwritel(0, MSI1_ENABLE);
+
+ if (ddb_i2c_init(dev) < 0)
+ goto fail1;
+ ddb_ports_init(dev);
+ if (ddb_buffers_alloc(dev) < 0) {
+ printk(KERN_INFO ": Could not allocate buffer memory\n");
+ goto fail2;
+ }
+ if (ddb_ports_attach(dev) < 0)
+ goto fail3;
+ ddb_device_create(dev);
+ return 0;
+
+fail3:
+ ddb_ports_detach(dev);
+ printk(KERN_ERR "fail3\n");
+ ddb_ports_release(dev);
+fail2:
+ printk(KERN_ERR "fail2\n");
+ ddb_buffers_free(dev);
+fail1:
+ printk(KERN_ERR "fail1\n");
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+ free_irq(dev->pdev->irq, dev);
+fail:
+ printk(KERN_ERR "fail\n");
+ ddb_unmap(dev);
+ pci_set_drvdata(pdev, 0);
+ pci_disable_device(pdev);
+ return -1;
+}
+
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+
+static struct ddb_info ddb_none = {
+ .type = DDB_NONE,
+ .name = "Digital Devices PCIe bridge",
+};
+
+static struct ddb_info ddb_octopus = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Octopus DVB adapter",
+ .port_num = 4,
+};
+
+static struct ddb_info ddb_octopus_le = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Octopus LE DVB adapter",
+ .port_num = 2,
+};
+
+static struct ddb_info ddb_v6 = {
+ .type = DDB_OCTOPUS,
+ .name = "Digital Devices Cine S2 V6 DVB adapter",
+ .port_num = 3,
+};
+
+#define DDVID 0xdd01 /* Digital Devices Vendor ID */
+
+#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \
+ .vendor = _vend, .device = _dev, \
+ .subvendor = _subvend, .subdevice = _subdev, \
+ .driver_data = (unsigned long)&_driverdata }
+
+static const struct pci_device_id ddb_id_tbl[] __devinitdata = {
+ DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus),
+ DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6),
+ /* in case sub-ids got deleted in flash */
+ DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, ddb_id_tbl);
+
+
+static struct pci_driver ddb_pci_driver = {
+ .name = "DDBridge",
+ .id_table = ddb_id_tbl,
+ .probe = ddb_probe,
+ .remove = __devexit_p(ddb_remove),
+};
+
+static __init int module_init_ddbridge(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Digital Devices PCIE bridge driver, "
+ "Copyright (C) 2010-11 Digital Devices GmbH\n");
+
+ ret = ddb_class_create();
+ if (ret < 0)
+ return ret;
+ ret = pci_register_driver(&ddb_pci_driver);
+ if (ret < 0)
+ ddb_class_destroy();
+ return ret;
+}
+
+static __exit void module_exit_ddbridge(void)
+{
+ pci_unregister_driver(&ddb_pci_driver);
+ ddb_class_destroy();
+}
+
+module_init(module_init_ddbridge);
+module_exit(module_exit_ddbridge);
+
+MODULE_DESCRIPTION("Digital Devices PCIe Bridge");
+MODULE_AUTHOR("Ralph Metzler");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.5");
diff --git a/drivers/media/dvb/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h
index a3ccb318b500..a3ccb318b500 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-regs.h
+++ b/drivers/media/pci/ddbridge/ddbridge-regs.h
diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h
index 8b1b41d2a52d..8b1b41d2a52d 100644
--- a/drivers/media/dvb/ddbridge/ddbridge.h
+++ b/drivers/media/pci/ddbridge/ddbridge.h
diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig
new file mode 100644
index 000000000000..013df4e015cd
--- /dev/null
+++ b/drivers/media/pci/dm1105/Kconfig
@@ -0,0 +1,20 @@
+config DVB_DM1105
+ tristate "SDMC DM1105 based PCI cards"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
+ depends on RC_CORE
+ help
+ Support for cards based on the SDMC DM1105 PCI chip like
+ DvbWorld 2002
+
+ Since these cards have no MPEG decoder onboard, they transmit
+ only compressed MPEG data over the PCI bus, so you need
+ an external software decoder to watch TV on your computer.
+
+ Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/dm1105/Makefile b/drivers/media/pci/dm1105/Makefile
new file mode 100644
index 000000000000..327585143c83
--- /dev/null
+++ b/drivers/media/pci/dm1105/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_DM1105) += dm1105.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends
diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index a609b3a9b146..a609b3a9b146 100644
--- a/drivers/media/dvb/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig
new file mode 100644
index 000000000000..dd6ee57e3a4c
--- /dev/null
+++ b/drivers/media/pci/ivtv/Kconfig
@@ -0,0 +1,61 @@
+config VIDEO_IVTV
+ tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
+ depends on VIDEO_V4L2 && PCI && I2C
+ select I2C_ALGOBIT
+ depends on RC_CORE
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_SAA711X
+ select VIDEO_SAA717X
+ select VIDEO_SAA7127
+ select VIDEO_CS53L32A
+ select VIDEO_M52790
+ select VIDEO_WM8775
+ select VIDEO_WM8739
+ select VIDEO_VP27SMPX
+ select VIDEO_UPD64031A
+ select VIDEO_UPD64083
+ ---help---
+ This is a video4linux driver for Conexant cx23416 or cx23415 based
+ PCI personal video recorder devices.
+
+ This is used in devices such as the Hauppauge PVR-150/250/350/500
+ cards. There is a driver homepage at <http://www.ivtvdriver.org>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtv.
+
+config VIDEO_IVTV_ALSA
+ tristate "Conexant cx23415/cx23416 ALSA interface for PCM audio capture"
+ depends on VIDEO_IVTV && SND
+ select SND_PCM
+ ---help---
+ This driver provides an ALSA interface as another method for user
+ applications to obtain PCM audio data from Conexant cx23415/cx23416
+ based PCI TV cards supported by the ivtv driver.
+
+ The ALSA interface has much wider use in user applications performing
+ PCM audio capture, than the V4L2 "/dev/video24" PCM audio interface
+ provided by the main ivtv driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtv-alsa.
+
+config VIDEO_FB_IVTV
+ tristate "Conexant cx23415 framebuffer support"
+ depends on VIDEO_IVTV && FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ This is a framebuffer driver for the Conexant cx23415 MPEG
+ encoder/decoder.
+
+ This is used in the Hauppauge PVR-350 card. There is a driver
+ homepage at <http://www.ivtvdriver.org>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ivtvfb.
diff --git a/drivers/media/pci/ivtv/Makefile b/drivers/media/pci/ivtv/Makefile
new file mode 100644
index 000000000000..0eaa88298b7e
--- /dev/null
+++ b/drivers/media/pci/ivtv/Makefile
@@ -0,0 +1,16 @@
+ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
+ ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
+ ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
+ ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
+ ivtv-vbi.o ivtv-yuv.o
+ivtv-alsa-objs := ivtv-alsa-main.o ivtv-alsa-pcm.o
+
+obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
+obj-$(CONFIG_VIDEO_IVTV_ALSA) += ivtv-alsa.o
+obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c
new file mode 100644
index 000000000000..8deab1629b3b
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c
@@ -0,0 +1,303 @@
+/*
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited for the cx18 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-alsa.h"
+#include "ivtv-alsa-mixer.h"
+#include "ivtv-alsa-pcm.h"
+
+int ivtv_alsa_debug;
+
+#define IVTV_DEBUG_ALSA_INFO(fmt, arg...) \
+ do { \
+ if (ivtv_alsa_debug & 2) \
+ pr_info("%s: " fmt, "ivtv-alsa", ## arg); \
+ } while (0)
+
+module_param_named(debug, ivtv_alsa_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n");
+
+MODULE_AUTHOR("Andy Walls");
+MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface");
+MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+static inline
+struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev)
+{
+ return to_ivtv(v4l2_dev)->alsa;
+}
+
+static inline
+struct snd_ivtv_card *p_to_snd_ivtv_card(struct v4l2_device **v4l2_dev)
+{
+ return container_of(v4l2_dev, struct snd_ivtv_card, v4l2_dev);
+}
+
+static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc)
+{
+ if (itvsc == NULL)
+ return;
+
+ if (itvsc->v4l2_dev != NULL)
+ to_ivtv(itvsc->v4l2_dev)->alsa = NULL;
+
+ /* FIXME - take any other stopping actions needed */
+
+ kfree(itvsc);
+}
+
+static void snd_ivtv_card_private_free(struct snd_card *sc)
+{
+ if (sc == NULL)
+ return;
+ snd_ivtv_card_free(sc->private_data);
+ sc->private_data = NULL;
+ sc->private_free = NULL;
+}
+
+static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev,
+ struct snd_card *sc,
+ struct snd_ivtv_card **itvsc)
+{
+ *itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL);
+ if (*itvsc == NULL)
+ return -ENOMEM;
+
+ (*itvsc)->v4l2_dev = v4l2_dev;
+ (*itvsc)->sc = sc;
+
+ sc->private_data = *itvsc;
+ sc->private_free = snd_ivtv_card_private_free;
+
+ return 0;
+}
+
+static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc)
+{
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+ struct snd_card *sc = itvsc->sc;
+
+ /* sc->driver is used by alsa-lib's configurator: simple, unique */
+ strlcpy(sc->driver, "CX2341[56]", sizeof(sc->driver));
+
+ /* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */
+ snprintf(sc->shortname, sizeof(sc->shortname), "IVTV-%d",
+ itv->instance);
+
+ /* sc->longname is read from /proc/asound/cards */
+ snprintf(sc->longname, sizeof(sc->longname),
+ "CX2341[56] #%d %s TV/FM Radio/Line-In Capture",
+ itv->instance, itv->card_name);
+
+ return 0;
+}
+
+static int snd_ivtv_init(struct v4l2_device *v4l2_dev)
+{
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ struct snd_card *sc = NULL;
+ struct snd_ivtv_card *itvsc;
+ int ret;
+
+ /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
+
+ /* (1) Check and increment the device index */
+ /* This is a no-op for us. We'll use the itv->instance */
+
+ /* (2) Create a card instance */
+ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
+ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
+ THIS_MODULE, 0, &sc);
+ if (ret) {
+ IVTV_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ /* (3) Create a main component */
+ ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc);
+ if (ret) {
+ IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ /* (4) Set the driver ID and name strings */
+ snd_ivtv_card_set_names(itvsc);
+
+ /* (5) Create other components: mixer, PCM, & proc files */
+#if 0
+ ret = snd_ivtv_mixer_create(itvsc);
+ if (ret) {
+ IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d:"
+ " proceeding anyway\n", __func__, ret);
+ }
+#endif
+
+ ret = snd_ivtv_pcm_create(itvsc);
+ if (ret) {
+ IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+ /* FIXME - proc files */
+
+ /* (7) Set the driver data and return 0 */
+ /* We do this out of normal order for PCI drivers to avoid races */
+ itv->alsa = itvsc;
+
+ /* (6) Register the card instance */
+ ret = snd_card_register(sc);
+ if (ret) {
+ itv->alsa = NULL;
+ IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
+ __func__, ret);
+ goto err_exit_free;
+ }
+
+ return 0;
+
+err_exit_free:
+ if (sc != NULL)
+ snd_card_free(sc);
+ kfree(itvsc);
+err_exit:
+ return ret;
+}
+
+int ivtv_alsa_load(struct ivtv *itv)
+{
+ struct v4l2_device *v4l2_dev = &itv->v4l2_dev;
+ struct ivtv_stream *s;
+
+ if (v4l2_dev == NULL) {
+ pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ itv = to_ivtv(v4l2_dev);
+ if (itv == NULL) {
+ pr_err("ivtv-alsa itv is NULL\n");
+ return 0;
+ }
+
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+ if (s->vdev == NULL) {
+ IVTV_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
+ "skipping\n", __func__);
+ return 0;
+ }
+
+ if (itv->alsa != NULL) {
+ IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n",
+ __func__);
+ return 0;
+ }
+
+ if (snd_ivtv_init(v4l2_dev)) {
+ IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n",
+ __func__);
+ } else {
+ IVTV_DEBUG_ALSA_INFO("%s: created ivtv ALSA interface instance "
+ "\n", __func__);
+ }
+ return 0;
+}
+
+static int __init ivtv_alsa_init(void)
+{
+ pr_info("ivtv-alsa: module loading...\n");
+ ivtv_ext_init = &ivtv_alsa_load;
+ return 0;
+}
+
+static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc)
+{
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+
+ /* FIXME - pointer checks & shutdown itvsc */
+
+ snd_card_free(itvsc->sc);
+ itv->alsa = NULL;
+}
+
+static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+ struct snd_ivtv_card *itvsc;
+
+ if (v4l2_dev == NULL) {
+ pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ itvsc = to_snd_ivtv_card(v4l2_dev);
+ if (itvsc == NULL) {
+ IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n",
+ __func__);
+ return 0;
+ }
+
+ snd_ivtv_exit(itvsc);
+ return 0;
+}
+
+static void __exit ivtv_alsa_exit(void)
+{
+ struct device_driver *drv;
+ int ret;
+
+ pr_info("ivtv-alsa: module unloading...\n");
+
+ drv = driver_find("ivtv", &pci_bus_type);
+ ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback);
+ (void)ret; /* suppress compiler warning */
+
+ ivtv_ext_init = NULL;
+ pr_info("ivtv-alsa: module unload complete\n");
+}
+
+module_init(ivtv_alsa_init);
+module_exit(ivtv_alsa_exit);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
new file mode 100644
index 000000000000..33ec05b09af3
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c
@@ -0,0 +1,175 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+
+#include "ivtv-alsa.h"
+#include "ivtv-driver.h"
+
+/*
+ * Note the cx25840-core volume scale is funny, due to the alignment of the
+ * scale with another chip's range:
+ *
+ * v4l2_control value /512 indicated dB actual dB reg 0x8d4
+ * 0x0000 - 0x01ff 0 -119 -96 228
+ * 0x0200 - 0x02ff 1 -118 -96 228
+ * ...
+ * 0x2c00 - 0x2dff 22 -97 -96 228
+ * 0x2e00 - 0x2fff 23 -96 -96 228
+ * 0x3000 - 0x31ff 24 -95 -95 226
+ * ...
+ * 0xee00 - 0xefff 119 0 0 36
+ * ...
+ * 0xfe00 - 0xffff 127 +8 +8 20
+ */
+static inline int dB_to_cx25840_vol(int dB)
+{
+ if (dB < -96)
+ dB = -96;
+ else if (dB > 8)
+ dB = 8;
+ return (dB + 119) << 9;
+}
+
+static inline int cx25840_vol_to_dB(int v)
+{
+ if (v < (23 << 9))
+ v = (23 << 9);
+ else if (v > (127 << 9))
+ v = (127 << 9);
+ return (v >> 9) - 119;
+}
+
+static int snd_ivtv_mixer_tv_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ /* We're already translating values, just keep this control in dB */
+ uinfo->value.integer.min = -96;
+ uinfo->value.integer.max = 8;
+ uinfo->value.integer.step = 1;
+ return 0;
+}
+
+static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+
+ snd_ivtv_lock(itvsc);
+ ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
+ snd_ivtv_unlock(itvsc);
+
+ if (!ret)
+ uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value);
+ return ret;
+}
+
+static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *uctl)
+{
+ struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl);
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+ struct v4l2_control vctrl;
+ int ret;
+
+ vctrl.id = V4L2_CID_AUDIO_VOLUME;
+ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+
+ snd_ivtv_lock(itvsc);
+
+ /* Fetch current state */
+ ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl);
+
+ if (ret ||
+ (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) {
+
+ /* Set, if needed */
+ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]);
+ ret = v4l2_subdev_call(itv->sd_audio, core, s_ctrl, &vctrl);
+ if (!ret)
+ ret = 1; /* Indicate control was changed w/o error */
+ }
+ snd_ivtv_unlock(itvsc);
+
+ return ret;
+}
+
+
+/* This is a bit of overkill, the slider is already in dB internally */
+static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0);
+
+static struct snd_kcontrol_new snd_ivtv_mixer_tv_vol __initdata = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Analog TV Capture Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_ivtv_mixer_tv_volume_info,
+ .get = snd_ivtv_mixer_tv_volume_get,
+ .put = snd_ivtv_mixer_tv_volume_put,
+ .tlv.p = snd_ivtv_mixer_tv_vol_db_scale
+};
+
+/* FIXME - add mute switch and balance, bass, treble sliders:
+ V4L2_CID_AUDIO_MUTE
+
+ V4L2_CID_AUDIO_BALANCE
+
+ V4L2_CID_AUDIO_BASS
+ V4L2_CID_AUDIO_TREBLE
+*/
+
+/* FIXME - add stereo, lang1, lang2, mono menu */
+/* FIXME - add I2S volume */
+
+int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc)
+{
+ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+ struct snd_card *sc = itvsc->sc;
+ int ret;
+
+ strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername));
+
+ ret = snd_ctl_add(sc, snd_ctl_new1(snd_ivtv_mixer_tv_vol, itvsc));
+ if (ret) {
+ IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n",
+ __func__, snd_ivtv_mixer_tv_vol.name, ret);
+ }
+ return ret;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
new file mode 100644
index 000000000000..cdde36704d53
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h
@@ -0,0 +1,23 @@
+/*
+ * ALSA mixer controls for the
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
new file mode 100644
index 000000000000..f7022bd58ffd
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c
@@ -0,0 +1,356 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * Portions of this work were sponsored by ONELAN Limited for the cx18 driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-streams.h"
+#include "ivtv-fileops.h"
+#include "ivtv-alsa.h"
+
+static unsigned int pcm_debug;
+module_param(pcm_debug, int, 0644);
+MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
+
+#define dprintk(fmt, arg...) \
+ do { \
+ if (pcm_debug) \
+ pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \
+ } while (0)
+
+static struct snd_pcm_hardware snd_ivtv_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, u8 *pcm_data,
+ size_t num_bytes)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ unsigned int oldptr;
+ unsigned int stride;
+ int period_elapsed = 0;
+ int length;
+
+ dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc,
+ pcm_data, num_bytes);
+
+ substream = itvsc->capture_pcm_substream;
+ if (substream == NULL) {
+ dprintk("substream was NULL\n");
+ return;
+ }
+
+ runtime = substream->runtime;
+ if (runtime == NULL) {
+ dprintk("runtime was NULL\n");
+ return;
+ }
+
+ stride = runtime->frame_bits >> 3;
+ if (stride == 0) {
+ dprintk("stride is zero\n");
+ return;
+ }
+
+ length = num_bytes / stride;
+ if (length == 0) {
+ dprintk("%s: length was zero\n", __func__);
+ return;
+ }
+
+ if (runtime->dma_area == NULL) {
+ dprintk("dma area was NULL - ignoring\n");
+ return;
+ }
+
+ oldptr = itvsc->hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ cnt * stride);
+ memcpy(runtime->dma_area, pcm_data + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, pcm_data,
+ length * stride);
+ }
+ snd_pcm_stream_lock(substream);
+
+ itvsc->hwptr_done_capture += length;
+ if (itvsc->hwptr_done_capture >=
+ runtime->buffer_size)
+ itvsc->hwptr_done_capture -=
+ runtime->buffer_size;
+
+ itvsc->capture_transfer_done += length;
+ if (itvsc->capture_transfer_done >=
+ runtime->period_size) {
+ itvsc->capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+}
+
+static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ struct ivtv_stream *s;
+ struct ivtv_open_id item;
+ int ret;
+
+ /* Instruct the CX2341[56] to start sending packets */
+ snd_ivtv_lock(itvsc);
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+
+ v4l2_fh_init(&item.fh, s->vdev);
+ item.itv = itv;
+ item.type = s->type;
+
+ /* See if the stream is available */
+ if (ivtv_claim_stream(&item, item.type)) {
+ /* No, it's already in use */
+ snd_ivtv_unlock(itvsc);
+ return -EBUSY;
+ }
+
+ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) ||
+ test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ /* We're already streaming. No additional action required */
+ snd_ivtv_unlock(itvsc);
+ return 0;
+ }
+
+
+ runtime->hw = snd_ivtv_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ itvsc->capture_pcm_substream = substream;
+ runtime->private_data = itv;
+
+ itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data;
+
+ /* Not currently streaming, so start it up */
+ set_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ret = ivtv_start_v4l2_encode_stream(s);
+ snd_ivtv_unlock(itvsc);
+
+ return ret;
+}
+
+static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ struct ivtv_stream *s;
+
+ /* Instruct the ivtv to stop sending packets */
+ snd_ivtv_lock(itvsc);
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+ ivtv_stop_v4l2_encode_stream(s, 0);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+ ivtv_release_stream(s);
+
+ itv->pcm_announce_callback = NULL;
+ snd_ivtv_unlock(itvsc);
+
+ return 0;
+}
+
+static int snd_ivtv_pcm_ioctl(struct snd_pcm_substream *substream,
+ unsigned int cmd, void *arg)
+{
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+ int ret;
+
+ snd_ivtv_lock(itvsc);
+ ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+ snd_ivtv_unlock(itvsc);
+ return ret;
+}
+
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static int snd_ivtv_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ dprintk("%s called\n", __func__);
+
+ return snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(params));
+}
+
+static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+ unsigned long flags;
+
+ spin_lock_irqsave(&itvsc->slock, flags);
+ if (substream->runtime->dma_area) {
+ dprintk("freeing pcm capture region\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
+ spin_unlock_irqrestore(&itvsc->slock, flags);
+
+ return 0;
+}
+
+static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+
+ itvsc->hwptr_done_capture = 0;
+ itvsc->capture_transfer_done = 0;
+
+ return 0;
+}
+
+static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ return 0;
+}
+
+static
+snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ unsigned long flags;
+ snd_pcm_uframes_t hwptr_done;
+ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream);
+
+ spin_lock_irqsave(&itvsc->slock, flags);
+ hwptr_done = itvsc->hwptr_done_capture;
+ spin_unlock_irqrestore(&itvsc->slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops snd_ivtv_pcm_capture_ops = {
+ .open = snd_ivtv_pcm_capture_open,
+ .close = snd_ivtv_pcm_capture_close,
+ .ioctl = snd_ivtv_pcm_ioctl,
+ .hw_params = snd_ivtv_pcm_hw_params,
+ .hw_free = snd_ivtv_pcm_hw_free,
+ .prepare = snd_ivtv_pcm_prepare,
+ .trigger = snd_ivtv_pcm_trigger,
+ .pointer = snd_ivtv_pcm_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc)
+{
+ struct snd_pcm *sp;
+ struct snd_card *sc = itvsc->sc;
+ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev;
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int ret;
+
+ ret = snd_pcm_new(sc, "CX2341[56] PCM",
+ 0, /* PCM device 0, the only one for this card */
+ 0, /* 0 playback substreams */
+ 1, /* 1 capture substream */
+ &sp);
+ if (ret) {
+ IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n",
+ __func__, ret);
+ goto err_exit;
+ }
+
+ spin_lock_init(&itvsc->slock);
+
+ snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
+ &snd_ivtv_pcm_capture_ops);
+ sp->info_flags = 0;
+ sp->private_data = itvsc;
+ strlcpy(sp->name, itv->card_name, sizeof(sp->name));
+
+ return 0;
+
+err_exit:
+ return ret;
+}
diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
new file mode 100644
index 000000000000..5ab18319ea4d
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h
@@ -0,0 +1,27 @@
+/*
+ * ALSA PCM device for the
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc);
+
+/* Used by ivtv driver to announce the PCM data to the module */
+void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *card, u8 *pcm_data,
+ size_t num_bytes);
diff --git a/drivers/media/pci/ivtv/ivtv-alsa.h b/drivers/media/pci/ivtv/ivtv-alsa.h
new file mode 100644
index 000000000000..4a0d8f2c254d
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-alsa.h
@@ -0,0 +1,75 @@
+/*
+ * ALSA interface to ivtv PCM capture streams
+ *
+ * Copyright (C) 2009,2012 Andy Walls <awalls@md.metrocast.net>
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ */
+
+struct snd_card;
+
+struct snd_ivtv_card {
+ struct v4l2_device *v4l2_dev;
+ struct snd_card *sc;
+ unsigned int capture_transfer_done;
+ unsigned int hwptr_done_capture;
+ struct snd_pcm_substream *capture_pcm_substream;
+ spinlock_t slock;
+};
+
+extern int ivtv_alsa_debug;
+
+/*
+ * File operations that manipulate the encoder or video or audio subdevices
+ * need to be serialized. Use the same lock we use for v4l2 file ops.
+ */
+static inline void snd_ivtv_lock(struct snd_ivtv_card *itvsc)
+{
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+ mutex_lock(&itv->serialize_lock);
+}
+
+static inline void snd_ivtv_unlock(struct snd_ivtv_card *itvsc)
+{
+ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev);
+ mutex_unlock(&itv->serialize_lock);
+}
+
+#define IVTV_ALSA_DBGFLG_WARN (1 << 0)
+#define IVTV_ALSA_DBGFLG_INFO (1 << 1)
+
+#define IVTV_ALSA_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_alsa_debug) \
+ pr_info("%s-alsa: " type ": " fmt, \
+ v4l2_dev->name , ## args); \
+ } while (0)
+
+#define IVTV_ALSA_DEBUG_WARN(fmt, args...) \
+ IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_WARN, "warning", fmt , ## args)
+
+#define IVTV_ALSA_DEBUG_INFO(fmt, args...) \
+ IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_INFO, "info", fmt , ## args)
+
+#define IVTV_ALSA_ERR(fmt, args...) \
+ pr_err("%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define IVTV_ALSA_WARN(fmt, args...) \
+ pr_warn("%s-alsa: " fmt, v4l2_dev->name , ## args)
+
+#define IVTV_ALSA_INFO(fmt, args...) \
+ pr_info("%s-alsa: " fmt, v4l2_dev->name , ## args)
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c
index 145e4749a69d..145e4749a69d 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/pci/ivtv/ivtv-cards.c
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/pci/ivtv/ivtv-cards.h
index e6f5c02981f1..e6f5c02981f1 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/pci/ivtv/ivtv-cards.h
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
index c60424601cb9..c60424601cb9 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/pci/ivtv/ivtv-controls.c
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/pci/ivtv/ivtv-controls.h
index 3999e6358312..3999e6358312 100644
--- a/drivers/media/video/ivtv/ivtv-controls.h
+++ b/drivers/media/pci/ivtv/ivtv-controls.h
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
new file mode 100644
index 000000000000..74e9a5032364
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -0,0 +1,1536 @@
+/*
+ ivtv driver initialization and card probing
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Main Driver file for the ivtv project:
+ * Driver for the Conexant CX23415/CX23416 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ *
+ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
+ * using information from T.Adachi,Takeru KOMORIYA and others :-)
+ *
+ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
+ * version by T.Adachi. Special thanks Mr.Suzuki
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-firmware.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-streams.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-controls.h"
+#include "ivtv-gpio.h"
+#include <linux/dma-mapping.h>
+#include <media/tveeprom.h>
+#include <media/saa7115.h>
+#include <media/v4l2-chip-ident.h>
+#include "tuner-xc2028.h"
+
+/* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a PVR-350 with. Normally this would give a
+ video1 device together with a radio0 device for the PVR. By
+ setting this to 1 you ensure that radio0 is now also radio1. */
+int ivtv_first_minor;
+
+/* Callback for registering extensions */
+int (*ivtv_ext_init)(struct ivtv *);
+EXPORT_SYMBOL(ivtv_ext_init);
+
+/* add your revision and whatnot here */
+static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
+/* Parameter declarations */
+static int cardtype[IVTV_MAX_CARDS];
+static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1 };
+
+static unsigned int cardtype_c = 1;
+static unsigned int tuner_c = 1;
+static int radio_c = 1;
+static unsigned int i2c_clock_period_c = 1;
+static char pal[] = "---";
+static char secam[] = "--";
+static char ntsc[] = "-";
+
+/* Buffers */
+
+/* DMA Buffers, Default size in MB allocated */
+#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
+#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
+#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
+/* Exception: size in kB for this stream (MB is overkill) */
+#define IVTV_DEFAULT_ENC_PCM_BUFFERS 320
+#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
+#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
+/* Exception: size in kB for this stream (MB is way overkill) */
+#define IVTV_DEFAULT_DEC_VBI_BUFFERS 64
+
+static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
+static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
+static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
+static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
+static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
+static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
+
+static int ivtv_yuv_mode;
+static int ivtv_yuv_threshold = -1;
+static int ivtv_pci_latency = 1;
+
+int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+int ivtv_fw_debug;
+#endif
+
+static int tunertype = -1;
+static int newi2c = -1;
+
+module_param_array(tuner, int, &tuner_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
+module_param_array(cardtype, int, &cardtype_c, 0644);
+module_param_string(pal, pal, sizeof(pal), 0644);
+module_param_string(secam, secam, sizeof(secam), 0644);
+module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
+module_param_named(debug,ivtv_debug, int, 0644);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+module_param_named(fw_debug, ivtv_fw_debug, int, 0644);
+#endif
+module_param(ivtv_pci_latency, int, 0644);
+module_param(ivtv_yuv_mode, int, 0644);
+module_param(ivtv_yuv_threshold, int, 0644);
+module_param(ivtv_first_minor, int, 0644);
+
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_yuv_buffers, int, 0644);
+module_param(enc_vbi_buffers, int, 0644);
+module_param(enc_pcm_buffers, int, 0644);
+module_param(dec_mpg_buffers, int, 0644);
+module_param(dec_yuv_buffers, int, 0644);
+module_param(dec_vbi_buffers, int, 0644);
+
+module_param(tunertype, int, 0644);
+module_param(newi2c, int, 0644);
+module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
+
+MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
+ "\t\t\tsee tuner.h for values");
+MODULE_PARM_DESC(radio,
+ "Enable or disable the radio. Use only if autodetection\n"
+ "\t\t\tfails. 0 = disable, 1 = enable");
+MODULE_PARM_DESC(cardtype,
+ "Only use this option if your card is not detected properly.\n"
+ "\t\tSpecify card type:\n"
+ "\t\t\t 1 = WinTV PVR 250\n"
+ "\t\t\t 2 = WinTV PVR 350\n"
+ "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
+ "\t\t\t 4 = AVerMedia M179\n"
+ "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
+ "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
+ "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
+ "\t\t\t 8 = Adaptec AVC-2410\n"
+ "\t\t\t 9 = Adaptec AVC-2010\n"
+ "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
+ "\t\t\t11 = AOpen VA2000MAX-STN6\n"
+ "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
+ "\t\t\t13 = I/O Data GV-MVP/RX\n"
+ "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
+ "\t\t\t15 = GOTVIEW PCI DVD\n"
+ "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
+ "\t\t\t17 = Yuan MPC622\n"
+ "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
+ "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite\n"
+ "\t\t\t20 = Club3D ZAP-TV1x01\n"
+ "\t\t\t21 = AverTV MCE 116 Plus\n"
+ "\t\t\t22 = ASUS Falcon2\n"
+ "\t\t\t23 = AverMedia PVR-150 Plus\n"
+ "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
+ "\t\t\t25 = AverMedia M104 (not yet working)\n"
+ "\t\t\t26 = Buffalo PC-MV5L/PCI\n"
+ "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n"
+ "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
+MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC");
+MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)");
+MODULE_PARM_DESC(tunertype,
+ "Specify tuner type:\n"
+ "\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n"
+ "\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n"
+ "\t\t\t-1 = Autodetect (default)\n");
+MODULE_PARM_DESC(debug,
+ "Debug level (bitmask). Default: 0\n"
+ "\t\t\t 1/0x0001: warning\n"
+ "\t\t\t 2/0x0002: info\n"
+ "\t\t\t 4/0x0004: mailbox\n"
+ "\t\t\t 8/0x0008: ioctl\n"
+ "\t\t\t 16/0x0010: file\n"
+ "\t\t\t 32/0x0020: dma\n"
+ "\t\t\t 64/0x0040: irq\n"
+ "\t\t\t 128/0x0080: decoder\n"
+ "\t\t\t 256/0x0100: yuv\n"
+ "\t\t\t 512/0x0200: i2c\n"
+ "\t\t\t1024/0x0400: high volume\n");
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+MODULE_PARM_DESC(fw_debug,
+ "Enable code for debugging firmware problems. Default: 0\n");
+#endif
+MODULE_PARM_DESC(ivtv_pci_latency,
+ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
+ "\t\t\tDefault: Yes");
+MODULE_PARM_DESC(ivtv_yuv_mode,
+ "Specify the yuv playback mode:\n"
+ "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
+ "\t\t\tDefault: 0 (interlaced)");
+MODULE_PARM_DESC(ivtv_yuv_threshold,
+ "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
+ "\t\t\tDefault: 480");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_buffers,
+ "Encoder YUV Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_buffers,
+ "Encoder VBI Buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_buffers,
+ "Encoder PCM buffers (in kB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(dec_mpg_buffers,
+ "Decoder MPG buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
+MODULE_PARM_DESC(dec_yuv_buffers,
+ "Decoder YUV buffers (in MB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
+MODULE_PARM_DESC(dec_vbi_buffers,
+ "Decoder VBI buffers (in kB)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
+MODULE_PARM_DESC(newi2c,
+ "Use new I2C implementation\n"
+ "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
+ "\t\t\tDefault is autodetect");
+MODULE_PARM_DESC(i2c_clock_period,
+ "Period of SCL for the I2C bus controlled by the CX23415/6\n"
+ "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
+ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
+
+MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
+
+MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
+MODULE_DESCRIPTION("CX23415/CX23416 driver");
+MODULE_SUPPORTED_DEVICE
+ ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
+ "\t\t\tYuan MPG series and similar)");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(IVTV_VERSION);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct ivtv *dev = container_of(work, struct ivtv, request_module_wk);
+
+ /* Make sure ivtv-alsa module is loaded */
+ request_module("ivtv-alsa");
+
+ /* Initialize ivtv-alsa for this instance of the cx18 device */
+ if (ivtv_ext_init != NULL)
+ ivtv_ext_init(dev);
+}
+
+static void request_modules(struct ivtv *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct ivtv *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask &= ~mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
+{
+ itv->irqmask |= mask;
+ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
+}
+
+int ivtv_set_output_mode(struct ivtv *itv, int mode)
+{
+ int old_mode;
+
+ spin_lock(&itv->lock);
+ old_mode = itv->output_mode;
+ if (old_mode == 0)
+ itv->output_mode = old_mode = mode;
+ spin_unlock(&itv->lock);
+ return old_mode;
+}
+
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
+{
+ switch (itv->output_mode) {
+ case OUT_MPG:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ case OUT_YUV:
+ return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ default:
+ return NULL;
+ }
+}
+
+int ivtv_waitq(wait_queue_head_t *waitq)
+{
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
+ schedule();
+ finish_wait(waitq, &wait);
+ return signal_pending(current) ? -EINTR : 0;
+}
+
+/* Generic utility functions */
+int ivtv_msleep_timeout(unsigned int msecs, int intr)
+{
+ int timeout = msecs_to_jiffies(msecs);
+
+ do {
+ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ timeout = schedule_timeout(timeout);
+ if (intr) {
+ int ret = signal_pending(current);
+
+ if (ret)
+ return ret;
+ }
+ } while (timeout);
+ return 0;
+}
+
+/* Release ioremapped memory */
+static void ivtv_iounmap(struct ivtv *itv)
+{
+ if (itv == NULL)
+ return;
+
+ /* Release registers memory */
+ if (itv->reg_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing reg_mem\n");
+ iounmap(itv->reg_mem);
+ itv->reg_mem = NULL;
+ }
+ /* Release io memory */
+ if (itv->has_cx23415 && itv->dec_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing dec_mem\n");
+ iounmap(itv->dec_mem);
+ }
+ itv->dec_mem = NULL;
+
+ /* Release io memory */
+ if (itv->enc_mem != NULL) {
+ IVTV_DEBUG_INFO("releasing enc_mem\n");
+ iounmap(itv->enc_mem);
+ itv->enc_mem = NULL;
+ }
+}
+
+/* Hauppauge card? get values from tveeprom */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+{
+ u8 eedata[256];
+
+ itv->i2c_client.addr = 0xA0 >> 1;
+ tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
+ tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
+}
+
+static void ivtv_process_eeprom(struct ivtv *itv)
+{
+ struct tveeprom tv;
+ int pci_slot = PCI_SLOT(itv->pdev->devfn);
+
+ ivtv_read_eeprom(itv, &tv);
+
+ /* Many thanks to Steven Toth from Hauppauge for providing the
+ model numbers */
+ switch (tv.model) {
+ /* In a few cases the PCI subsystem IDs do not correctly
+ identify the card. A better method is to check the
+ model number from the eeprom instead. */
+ case 30012 ... 30039: /* Low profile PVR250 */
+ case 32000 ... 32999:
+ case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */
+ case 48400 ... 48599:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
+ break;
+ case 48100 ... 48399:
+ case 48600 ... 48999:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
+ break;
+ case 23000 ... 23999: /* PVR500 */
+ case 25000 ... 25999: /* Low profile PVR150 */
+ case 26000 ... 26999: /* Regular PVR150 */
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ case 0:
+ IVTV_ERR("Invalid EEPROM\n");
+ return;
+ default:
+ IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ break;
+ }
+
+ switch (tv.model) {
+ /* Old style PVR350 (with an saa7114) uses this input for
+ the tuner. */
+ case 48254:
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
+ break;
+ default:
+ break;
+ }
+
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
+
+ /* If this is a PVR500 then it should be possible to detect whether it is the
+ first or second unit by looking at the subsystem device ID: is bit 4 is
+ set, then it is the second unit (according to info from Hauppauge).
+
+ However, while this works for most cards, I have seen a few PVR500 cards
+ where both units have the same subsystem ID.
+
+ So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
+ PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
+ it is the second unit. It is possible that it is a different slot when ivtv is
+ used in Xen, in that case I ignore this card here. The worst that can happen
+ is that the card presents itself with a non-working radio device.
+
+ This detection is needed since the eeprom reports incorrectly that a radio is
+ present on the second unit. */
+ if (tv.model / 1000 == 23) {
+ static const struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
+ .radio = { 0x60, I2C_CLIENT_END },
+ .demod = { 0x43, I2C_CLIENT_END },
+ .tv = { 0x61, I2C_CLIENT_END },
+ };
+
+ itv->card_name = "WinTV PVR 500";
+ itv->card_i2c = &ivtv_i2c_radio;
+ if (pci_slot == 8 || pci_slot == 9) {
+ int is_first = (pci_slot & 1) == 0;
+
+ itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
+ "WinTV PVR 500 (unit #2)";
+ if (!is_first) {
+ IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
+ tv.has_radio = 0;
+ }
+ }
+ }
+ IVTV_INFO("Autodetected %s\n", itv->card_name);
+
+ switch (tv.tuner_hauppauge_model) {
+ case 85:
+ case 99:
+ case 112:
+ itv->pvr150_workaround = 1;
+ break;
+ default:
+ break;
+ }
+ if (tv.tuner_type == TUNER_ABSENT)
+ IVTV_ERR("tveeprom cannot autodetect tuner!\n");
+
+ if (itv->options.tuner == -1)
+ itv->options.tuner = tv.tuner_type;
+ if (itv->options.radio == -1)
+ itv->options.radio = (tv.has_radio != 0);
+ /* only enable newi2c if an IR blaster is present */
+ if (itv->options.newi2c == -1 && tv.has_ir) {
+ itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0;
+ if (itv->options.newi2c) {
+ IVTV_INFO("Reopen i2c bus for IR-blaster support\n");
+ exit_ivtv_i2c(itv);
+ init_ivtv_i2c(itv);
+ }
+ }
+
+ if (itv->std != 0)
+ /* user specified tuner standard */
+ return;
+
+ /* autodetect tuner standard */
+ if (tv.tuner_formats & V4L2_STD_PAL) {
+ IVTV_DEBUG_INFO("PAL tuner detected\n");
+ itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ } else if (tv.tuner_formats & V4L2_STD_NTSC) {
+ IVTV_DEBUG_INFO("NTSC tuner detected\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ } else if (tv.tuner_formats & V4L2_STD_SECAM) {
+ IVTV_DEBUG_INFO("SECAM tuner detected\n");
+ itv->std |= V4L2_STD_SECAM_L;
+ } else {
+ IVTV_INFO("No tuner detected, default to NTSC-M\n");
+ itv->std |= V4L2_STD_NTSC_M;
+ }
+}
+
+static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
+{
+ switch (pal[0]) {
+ case '6':
+ tunertype = 0;
+ return V4L2_STD_PAL_60;
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ tunertype = 0;
+ return V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ case 'n':
+ case 'N':
+ tunertype = 1;
+ if (pal[1] == 'c' || pal[1] == 'C')
+ return V4L2_STD_PAL_Nc;
+ return V4L2_STD_PAL_N;
+ case 'i':
+ case 'I':
+ tunertype = 0;
+ return V4L2_STD_PAL_I;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ tunertype = 0;
+ return V4L2_STD_PAL_DK;
+ case 'M':
+ case 'm':
+ tunertype = 1;
+ return V4L2_STD_PAL_M;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("pal= argument not recognised\n");
+ return 0;
+ }
+
+ switch (secam[0]) {
+ case 'b':
+ case 'B':
+ case 'g':
+ case 'G':
+ case 'h':
+ case 'H':
+ tunertype = 0;
+ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
+ case 'd':
+ case 'D':
+ case 'k':
+ case 'K':
+ tunertype = 0;
+ return V4L2_STD_SECAM_DK;
+ case 'l':
+ case 'L':
+ tunertype = 0;
+ if (secam[1] == 'C' || secam[1] == 'c')
+ return V4L2_STD_SECAM_LC;
+ return V4L2_STD_SECAM_L;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("secam= argument not recognised\n");
+ return 0;
+ }
+
+ switch (ntsc[0]) {
+ case 'm':
+ case 'M':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M;
+ case 'j':
+ case 'J':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M_JP;
+ case 'k':
+ case 'K':
+ tunertype = 1;
+ return V4L2_STD_NTSC_M_KR;
+ case '-':
+ break;
+ default:
+ IVTV_WARN("ntsc= argument not recognised\n");
+ return 0;
+ }
+
+ /* no match found */
+ return 0;
+}
+
+static void ivtv_process_options(struct ivtv *itv)
+{
+ const char *chipname;
+ int i, j;
+
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers * 1024;
+ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
+ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
+ itv->options.cardtype = cardtype[itv->instance];
+ itv->options.tuner = tuner[itv->instance];
+ itv->options.radio = radio[itv->instance];
+
+ itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
+ if (itv->options.i2c_clock_period == -1)
+ itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
+ else if (itv->options.i2c_clock_period < 10)
+ itv->options.i2c_clock_period = 10;
+ else if (itv->options.i2c_clock_period > 4500)
+ itv->options.i2c_clock_period = 4500;
+
+ itv->options.newi2c = newi2c;
+ if (tunertype < -1 || tunertype > 1) {
+ IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
+ tunertype = -1;
+ }
+ itv->std = ivtv_parse_std(itv);
+ if (itv->std == 0 && tunertype >= 0)
+ itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN);
+ itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15);
+ chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+ if (itv->options.cardtype == -1) {
+ IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+ return;
+ }
+ if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
+ IVTV_INFO("User specified %s card (detected %s based chip)\n",
+ itv->card->name, chipname);
+ } else if (itv->options.cardtype != 0) {
+ IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+ }
+ if (itv->card == NULL) {
+ if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+ itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+ IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+ chipname);
+ }
+ }
+ if (itv->card == NULL) {
+ for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
+ if (itv->card->pci_list == NULL)
+ continue;
+ for (j = 0; itv->card->pci_list[j].device; j++) {
+ if (itv->pdev->device !=
+ itv->card->pci_list[j].device)
+ continue;
+ if (itv->pdev->subsystem_vendor !=
+ itv->card->pci_list[j].subsystem_vendor)
+ continue;
+ if (itv->pdev->subsystem_device !=
+ itv->card->pci_list[j].subsystem_device)
+ continue;
+ IVTV_INFO("Autodetected %s card (%s based)\n",
+ itv->card->name, chipname);
+ goto done;
+ }
+ }
+ }
+done:
+
+ if (itv->card == NULL) {
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+ itv->pdev->vendor, itv->pdev->device);
+ IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+ itv->pdev->subsystem_vendor, itv->pdev->subsystem_device);
+ IVTV_ERR(" %s based\n", chipname);
+ IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+ IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+ IVTV_ERR("Prefix your subject line with [UNKNOWN IVTV CARD].\n");
+ }
+ itv->v4l2_cap = itv->card->v4l2_capabilities;
+ itv->card_name = itv->card->name;
+ itv->card_i2c = itv->card->i2c;
+}
+
+/* Precondition: the ivtv structure has been memset to 0. Only
+ the dev and num fields have been filled in.
+ No assumptions on the card type may be made here (see ivtv_init_struct2
+ for that).
+ */
+static int __devinit ivtv_init_struct1(struct ivtv *itv)
+{
+ struct sched_param param = { .sched_priority = 99 };
+
+ itv->base_addr = pci_resource_start(itv->pdev, 0);
+ itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+ mutex_init(&itv->serialize_lock);
+ mutex_init(&itv->i2c_bus_lock);
+ mutex_init(&itv->udma.lock);
+
+ spin_lock_init(&itv->lock);
+ spin_lock_init(&itv->dma_reg_lock);
+
+ init_kthread_worker(&itv->irq_worker);
+ itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker,
+ itv->v4l2_dev.name);
+ if (IS_ERR(itv->irq_worker_task)) {
+ IVTV_ERR("Could not create ivtv task\n");
+ return -1;
+ }
+ /* must use the FIFO scheduler as it is realtime sensitive */
+ sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, &param);
+
+ init_kthread_work(&itv->irq_work, ivtv_irq_work_handler);
+
+ /* Initial settings */
+ itv->cxhdl.port = CX2341X_PORT_MEMORY;
+ itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
+ init_waitqueue_head(&itv->eos_waitq);
+ init_waitqueue_head(&itv->event_waitq);
+ init_waitqueue_head(&itv->vsync_waitq);
+ init_waitqueue_head(&itv->dma_waitq);
+ init_timer(&itv->dma_timer);
+ itv->dma_timer.function = ivtv_unfinished_dma;
+ itv->dma_timer.data = (unsigned long)itv;
+
+ itv->cur_dma_stream = -1;
+ itv->cur_pio_stream = -1;
+
+ /* Ctrls */
+ itv->speed = 1000;
+
+ /* VBI */
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
+
+ /* Init the sg table for osd/yuv output */
+ sg_init_table(itv->udma.SGlist, IVTV_DMA_SG_OSD_ENT);
+
+ /* OSD */
+ itv->osd_global_alpha_state = 1;
+ itv->osd_global_alpha = 255;
+
+ /* YUV */
+ atomic_set(&itv->yuv_info.next_dma_frame, -1);
+ itv->yuv_info.lace_mode = ivtv_yuv_mode;
+ itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
+ itv->yuv_info.max_frames_buffered = 3;
+ itv->yuv_info.track_osd = 1;
+ return 0;
+}
+
+/* Second initialization part. Here the card type has been
+ autodetected. */
+static void __devinit ivtv_init_struct2(struct ivtv *itv)
+{
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
+ if (itv->card->video_inputs[i].video_type == 0)
+ break;
+ itv->nof_inputs = i;
+ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
+ if (itv->card->audio_inputs[i].audio_type == 0)
+ break;
+ itv->nof_audio_inputs = i;
+
+ if (itv->card->hw_all & IVTV_HW_CX25840) {
+ itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */
+ } else {
+ itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */
+ }
+
+ /* Find tuner input */
+ for (i = 0; i < itv->nof_inputs; i++) {
+ if (itv->card->video_inputs[i].video_type ==
+ IVTV_CARD_INPUT_VID_TUNER)
+ break;
+ }
+ if (i == itv->nof_inputs)
+ i = 0;
+ itv->active_input = i;
+ itv->audio_input = itv->card->video_inputs[i].audio_index;
+}
+
+static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ u16 cmd;
+ unsigned char pci_latency;
+
+ IVTV_DEBUG_INFO("Enabling pci device\n");
+
+ if (pci_enable_device(pdev)) {
+ IVTV_ERR("Can't enable device!\n");
+ return -EIO;
+ }
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ IVTV_ERR("No suitable DMA available.\n");
+ return -EIO;
+ }
+ if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
+ IVTV_ERR("Cannot request encoder memory region.\n");
+ return -EIO;
+ }
+
+ if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
+ IVTV_REG_SIZE, "ivtv registers")) {
+ IVTV_ERR("Cannot request register memory region.\n");
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ return -EIO;
+ }
+
+ if (itv->has_cx23415 &&
+ !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE, "ivtv decoder")) {
+ IVTV_ERR("Cannot request decoder memory region.\n");
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ return -EIO;
+ }
+
+ /* Check for bus mastering */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+ pci_set_master(pdev);
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_ERR("Bus Mastering is not enabled\n");
+ return -ENXIO;
+ }
+ }
+ IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && ivtv_pci_latency) {
+ IVTV_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+ /* This config space value relates to DMA latencies. The
+ default value 0x8080 is too low however and will lead
+ to DMA errors. 0xffff is the max value which solves
+ these problems. */
+ pci_write_config_dword(pdev, 0x40, 0xffff);
+
+ IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%llx\n",
+ pdev->device, pdev->revision, pdev->bus->number,
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ pdev->irq, pci_latency, (u64)itv->base_addr);
+
+ return 0;
+}
+
+static void ivtv_load_and_init_modules(struct ivtv *itv)
+{
+ u32 hw = itv->card->hw_all;
+ unsigned i;
+
+ /* check which i2c devices are actually found */
+ for (i = 0; i < 32; i++) {
+ u32 device = 1 << i;
+
+ if (!(device & hw))
+ continue;
+ if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) {
+ /* GPIO and TVEEPROM do not use i2c probing */
+ itv->hw_flags |= device;
+ continue;
+ }
+ if (ivtv_i2c_register(itv, i) == 0)
+ itv->hw_flags |= device;
+ }
+
+ /* probe for legacy IR controllers that aren't in card definitions */
+ if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
+ ivtv_i2c_new_ir_legacy(itv);
+
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+ else
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
+ hw = itv->hw_flags;
+
+ if (itv->card->type == IVTV_CARD_CX23416GYC) {
+ /* Several variations of this card exist, detect which card
+ type should be used. */
+ if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
+ else if ((hw & IVTV_HW_UPD64031A) == 0)
+ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
+ }
+ else if (itv->card->type == IVTV_CARD_GV_MVPRX ||
+ itv->card->type == IVTV_CARD_GV_MVPRX2E) {
+ /* The crystal frequency of GVMVPRX is 24.576MHz */
+ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq,
+ SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC);
+ }
+
+ if (hw & IVTV_HW_CX25840) {
+ itv->vbi.raw_decoder_line_size = 1444;
+ itv->vbi.raw_decoder_sav_odd_field = 0x20;
+ itv->vbi.raw_decoder_sav_even_field = 0x60;
+ itv->vbi.sliced_decoder_line_size = 272;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
+ itv->vbi.sliced_decoder_sav_even_field = 0xF0;
+ }
+
+ if (hw & IVTV_HW_SAA711X) {
+ struct v4l2_dbg_chip_ident v;
+
+ /* determine the exact saa711x model */
+ itv->hw_flags &= ~IVTV_HW_SAA711X;
+
+ v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;
+ strlcpy(v.match.name, "saa7115", sizeof(v.match.name));
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
+ if (v.ident == V4L2_IDENT_SAA7114) {
+ itv->hw_flags |= IVTV_HW_SAA7114;
+ /* VBI is not yet supported by the saa7114 driver. */
+ itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
+ } else {
+ itv->hw_flags |= IVTV_HW_SAA7115;
+ }
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+
+ if (hw & IVTV_HW_SAA717X) {
+ itv->vbi.raw_decoder_line_size = 1443;
+ itv->vbi.raw_decoder_sav_odd_field = 0x25;
+ itv->vbi.raw_decoder_sav_even_field = 0x62;
+ itv->vbi.sliced_decoder_line_size = 51;
+ itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
+ itv->vbi.sliced_decoder_sav_even_field = 0xEC;
+ }
+}
+
+static int __devinit ivtv_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int retval = 0;
+ int vbi_buf_size;
+ struct ivtv *itv;
+
+ itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+ if (itv == NULL)
+ return -ENOMEM;
+ itv->pdev = pdev;
+ itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv",
+ &ivtv_instance);
+
+ retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev);
+ if (retval) {
+ kfree(itv);
+ return retval;
+ }
+ IVTV_INFO("Initializing card %d\n", itv->instance);
+
+ ivtv_process_options(itv);
+ if (itv->options.cardtype == -1) {
+ retval = -ENODEV;
+ goto err;
+ }
+ if (ivtv_init_struct1(itv)) {
+ retval = -ENOMEM;
+ goto err;
+ }
+ retval = cx2341x_handler_init(&itv->cxhdl, 50);
+ if (retval)
+ goto err;
+ itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
+ itv->cxhdl.ops = &ivtv_cxhdl_ops;
+ itv->cxhdl.priv = itv;
+ itv->cxhdl.func = ivtv_api_func;
+
+ IVTV_DEBUG_INFO("base addr: 0x%llx\n", (u64)itv->base_addr);
+
+ /* PCI Device Setup */
+ retval = ivtv_setup_pci(itv, pdev, pci_id);
+ if (retval == -EIO)
+ goto free_worker;
+ if (retval == -ENXIO)
+ goto free_mem;
+
+ /* map io memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+ (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
+ itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
+ IVTV_ENCODER_SIZE);
+ if (!itv->enc_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+ "encoder memory\n");
+ IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of "
+ "vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+ (u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+ itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
+ IVTV_DECODER_SIZE);
+ if (!itv->dec_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into "
+ "CX23415 decoder memory\n");
+ IVTV_ERR("Each capture card with a CX23415 needs 8 MB "
+ "of vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc "
+ "/proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option "
+ "to set VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_mem;
+ }
+ }
+ else {
+ itv->dec_mem = itv->enc_mem;
+ }
+
+ /* map registers memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
+ (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ itv->reg_mem =
+ ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (!itv->reg_mem) {
+ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
+ "register space\n");
+ IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of "
+ "vmalloc address space for this window\n");
+ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
+ IVTV_ERR("Use the vmalloc= kernel command line option to set "
+ "VmallocTotal to a larger value\n");
+ retval = -ENOMEM;
+ goto free_io;
+ }
+
+ retval = ivtv_gpio_init(itv);
+ if (retval)
+ goto free_io;
+
+ /* active i2c */
+ IVTV_DEBUG_INFO("activating i2c...\n");
+ if (init_ivtv_i2c(itv)) {
+ IVTV_ERR("Could not initialize i2c\n");
+ goto free_io;
+ }
+
+ if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+ ivtv_process_eeprom(itv);
+ }
+ if (itv->card->comment)
+ IVTV_INFO("%s", itv->card->comment);
+ if (itv->card->v4l2_capabilities == 0) {
+ /* card was detected but is not supported */
+ retval = -ENODEV;
+ goto free_i2c;
+ }
+
+ if (itv->std == 0) {
+ itv->std = V4L2_STD_NTSC_M;
+ }
+
+ if (itv->options.tuner == -1) {
+ int i;
+
+ for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
+ if ((itv->std & itv->card->tuners[i].std) == 0)
+ continue;
+ itv->options.tuner = itv->card->tuners[i].tuner;
+ break;
+ }
+ }
+ /* if no tuner was found, then pick the first tuner in the card list */
+ if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
+ itv->std = itv->card->tuners[0].std;
+ if (itv->std & V4L2_STD_PAL)
+ itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
+ else if (itv->std & V4L2_STD_NTSC)
+ itv->std = V4L2_STD_NTSC_M;
+ else if (itv->std & V4L2_STD_SECAM)
+ itv->std = V4L2_STD_SECAM_L;
+ itv->options.tuner = itv->card->tuners[0].tuner;
+ }
+ if (itv->options.radio == -1)
+ itv->options.radio = (itv->card->radio_input.audio_type != 0);
+
+ /* The card is now fully identified, continue with card-specific
+ initialization. */
+ ivtv_init_struct2(itv);
+
+ ivtv_load_and_init_modules(itv);
+
+ if (itv->std & V4L2_STD_525_60) {
+ itv->is_60hz = 1;
+ itv->is_out_60hz = 1;
+ } else {
+ itv->is_50hz = 1;
+ itv->is_out_50hz = 1;
+ }
+
+ itv->yuv_info.osd_full_w = 720;
+ itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480;
+ itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
+ itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
+
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000;
+
+ /* Setup VBI Raw Size. Should be big enough to hold PAL.
+ It is possible to switch between PAL and NTSC, so we need to
+ take the largest size here. */
+ /* 1456 is multiple of 16, real size = 1444 */
+ itv->vbi.raw_size = 1456;
+ /* We use a buffer size of 1/2 of the total size needed for a
+ frame. This is actually very useful, since we now receive
+ a field at a time and that makes 'compressing' the raw data
+ down to size by stripping off the SAV codes a lot easier.
+ Note: having two different buffer sizes prevents standard
+ switching on the fly. We need to find a better solution... */
+ vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
+ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+ if (itv->options.radio > 0)
+ itv->v4l2_cap |= V4L2_CAP_RADIO;
+
+ if (itv->options.tuner > -1) {
+ struct tuner_setup setup;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = itv->options.tuner;
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ if (itv->options.radio > 0)
+ setup.mode_mask |= T_RADIO;
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ ivtv_reset_tuner_gpio : NULL;
+ ivtv_call_all(itv, tuner, s_type_addr, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ };
+ struct v4l2_priv_tun_config cfg = {
+ .tuner = itv->options.tuner,
+ .priv = &ctrl,
+ };
+ ivtv_call_all(itv, tuner, s_config, &cfg);
+ }
+ }
+
+ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
+ are not. */
+ itv->tuner_std = itv->std;
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
+
+ itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0);
+ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0);
+ /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
+ mask that menu item. */
+ itv->ctrl_audio_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
+ itv->ctrl_audio_multilingual_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
+ if (hdl->error) {
+ retval = hdl->error;
+ goto free_i2c;
+ }
+ v4l2_ctrl_cluster(2, &itv->ctrl_pts);
+ v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
+ ivtv_call_all(itv, video, s_std_output, itv->std);
+ /* Turn off the output signal. The mpeg decoder is not yet
+ active so without this you would get a green image until the
+ mpeg decoder becomes active. */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+ }
+
+ /* clear interrupt mask, effectively disabling interrupts */
+ ivtv_set_irq_mask(itv, 0xffffffff);
+
+ /* Register IRQ */
+ retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
+ IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
+ if (retval) {
+ IVTV_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+ }
+
+ retval = ivtv_streams_setup(itv);
+ if (retval) {
+ IVTV_ERR("Error %d setting up streams\n", retval);
+ goto free_irq;
+ }
+ retval = ivtv_streams_register(itv);
+ if (retval) {
+ IVTV_ERR("Error %d registering devices\n", retval);
+ goto free_streams;
+ }
+ IVTV_INFO("Initialized card: %s\n", itv->card_name);
+
+ /* Load ivtv submodules (ivtv-alsa) */
+ request_modules(itv);
+ return 0;
+
+free_streams:
+ ivtv_streams_cleanup(itv, 1);
+free_irq:
+ free_irq(itv->pdev->irq, (void *)itv);
+free_i2c:
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+ exit_ivtv_i2c(itv);
+free_io:
+ ivtv_iounmap(itv);
+free_mem:
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+free_worker:
+ kthread_stop(itv->irq_worker_task);
+err:
+ if (retval == 0)
+ retval = -ENODEV;
+ IVTV_ERR("Error %d on initialization\n", retval);
+
+ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+ return retval;
+}
+
+int ivtv_init_on_first_open(struct ivtv *itv)
+{
+ struct v4l2_frequency vf;
+ /* Needed to call ioctls later */
+ struct ivtv_open_id fh;
+ int fw_retry_count = 3;
+ int video_input;
+
+ fh.itv = itv;
+ fh.type = IVTV_ENC_STREAM_TYPE_MPG;
+
+ if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
+ return -ENXIO;
+
+ if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags))
+ return 0;
+
+ while (--fw_retry_count > 0) {
+ /* load firmware */
+ if (ivtv_firmware_init(itv) == 0)
+ break;
+ if (fw_retry_count > 1)
+ IVTV_WARN("Retry loading firmware\n");
+ }
+
+ if (fw_retry_count == 0) {
+ set_bit(IVTV_F_I_FAILED, &itv->i_flags);
+ return -ENXIO;
+ }
+
+ /* Try and get firmware versions */
+ IVTV_DEBUG_INFO("Getting firmware version..\n");
+ ivtv_firmware_versions(itv);
+
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ v4l2_subdev_call(itv->sd_video, core, load_fw);
+
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+
+ /* Set initial frequency. For PAL/SECAM broadcasts no
+ 'default' channel exists AFAIK. */
+ if (itv->std == V4L2_STD_NTSC_M_JP) {
+ vf.frequency = 1460; /* ch. 1 91250*16/1000 */
+ }
+ else if (itv->std & V4L2_STD_NTSC_M) {
+ vf.frequency = 1076; /* ch. 4 67250*16/1000 */
+ }
+
+ video_input = itv->active_input;
+ itv->active_input++; /* Force update of input */
+ ivtv_s_input(NULL, &fh, video_input);
+
+ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
+ in one place. */
+ itv->std++; /* Force full standard initialization */
+ itv->std_out = itv->std;
+ ivtv_s_frequency(NULL, &fh, &vf);
+
+ if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
+ /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
+ the mpeg decoder so now the saa7127 receives a proper
+ signal. */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
+ ivtv_init_mpeg_decoder(itv);
+ }
+
+ /* On a cx23416 this seems to be able to enable DMA to the chip? */
+ if (!itv->has_cx23415)
+ write_reg_sync(0x03, IVTV_REG_DMACONTROL);
+
+ ivtv_s_std_enc(itv, &itv->tuner_std);
+
+ /* Default interrupts enabled. For the PVR350 this includes the
+ decoder VSYNC interrupt, which is always on. It is not only used
+ during decoding but also by the OSD.
+ Some old PVR250 cards had a cx23415, so testing for that is too
+ general. Instead test if the card has video output capability. */
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
+ ivtv_set_osd_alpha(itv);
+ ivtv_s_std_dec(itv, &itv->tuner_std);
+ } else {
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
+ }
+
+ /* Setup initial controls */
+ cx2341x_handler_setup(&itv->cxhdl);
+ return 0;
+}
+
+static void ivtv_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int i;
+
+ IVTV_DEBUG_INFO("Removing card\n");
+
+ flush_request_modules(itv);
+
+ if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
+ /* Stop all captures */
+ IVTV_DEBUG_INFO("Stopping all streams\n");
+ if (atomic_read(&itv->capturing) > 0)
+ ivtv_stop_all_captures(itv);
+
+ /* Stop all decoding */
+ IVTV_DEBUG_INFO("Stopping decoding\n");
+
+ /* Turn off the TV-out */
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
+ if (atomic_read(&itv->decoding) > 0) {
+ int type;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ type = IVTV_DEC_STREAM_TYPE_YUV;
+ else
+ type = IVTV_DEC_STREAM_TYPE_MPG;
+ ivtv_stop_v4l2_decode_stream(&itv->streams[type],
+ V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+ }
+ ivtv_halt_firmware(itv);
+ }
+
+ /* Interrupts */
+ ivtv_set_irq_mask(itv, 0xffffffff);
+ del_timer_sync(&itv->dma_timer);
+
+ /* Kill irq worker */
+ flush_kthread_worker(&itv->irq_worker);
+ kthread_stop(itv->irq_worker_task);
+
+ ivtv_streams_cleanup(itv, 1);
+ ivtv_udma_free(itv);
+
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+
+ exit_ivtv_i2c(itv);
+
+ free_irq(itv->pdev->irq, (void *)itv);
+ ivtv_iounmap(itv);
+
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+ pci_disable_device(itv->pdev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
+
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+
+ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+}
+
+/* define a pci_driver for card detection */
+static struct pci_driver ivtv_pci_driver = {
+ .name = "ivtv",
+ .id_table = ivtv_pci_tbl,
+ .probe = ivtv_probe,
+ .remove = ivtv_remove,
+};
+
+static int __init module_start(void)
+{
+ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
+
+ /* Validate parameters */
+ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
+ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ IVTV_MAX_CARDS - 1);
+ return -1;
+ }
+
+ if (ivtv_debug < 0 || ivtv_debug > 2047) {
+ ivtv_debug = 0;
+ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+ }
+
+ if (pci_register_driver(&ivtv_pci_driver)) {
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ return -ENODEV;
+ }
+ printk(KERN_INFO "ivtv: End initialization\n");
+ return 0;
+}
+
+static void __exit module_cleanup(void)
+{
+ pci_unregister_driver(&ivtv_pci_driver);
+}
+
+/* Note: These symbols are exported because they are used by the ivtvfb
+ framebuffer module and an infrared module for the IR-blaster. */
+EXPORT_SYMBOL(ivtv_set_irq_mask);
+EXPORT_SYMBOL(ivtv_api);
+EXPORT_SYMBOL(ivtv_vapi);
+EXPORT_SYMBOL(ivtv_vapi_result);
+EXPORT_SYMBOL(ivtv_clear_irq_mask);
+EXPORT_SYMBOL(ivtv_debug);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+EXPORT_SYMBOL(ivtv_fw_debug);
+#endif
+EXPORT_SYMBOL(ivtv_reset_ir_gpio);
+EXPORT_SYMBOL(ivtv_udma_setup);
+EXPORT_SYMBOL(ivtv_udma_unmap);
+EXPORT_SYMBOL(ivtv_udma_alloc);
+EXPORT_SYMBOL(ivtv_udma_prepare);
+EXPORT_SYMBOL(ivtv_init_on_first_open);
+EXPORT_SYMBOL(ivtv_firmware_check);
+
+module_init(module_start);
+module_exit(module_cleanup);
diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h
new file mode 100644
index 000000000000..bc309f42c8ed
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-driver.h
@@ -0,0 +1,850 @@
+/*
+ ivtv driver internal defines and structures
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_DRIVER_H
+#define IVTV_DRIVER_H
+
+/* Internal header for ivtv project:
+ * Driver for the cx23415/6 chip.
+ * Author: Kevin Thayer (nufan_wfk at yahoo.com)
+ * License: GPL
+ * http://www.ivtvdriver.org
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <linux/dvb/video.h>
+#include <linux/dvb/audio.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/tuner.h>
+#include <media/cx2341x.h>
+#include <media/ir-kbd-i2c.h>
+
+#include <linux/ivtv.h>
+
+/* Memory layout */
+#define IVTV_ENCODER_OFFSET 0x00000000
+#define IVTV_ENCODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
+#define IVTV_DECODER_OFFSET 0x01000000
+#define IVTV_DECODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
+#define IVTV_REG_OFFSET 0x02000000
+#define IVTV_REG_SIZE 0x00010000
+
+/* Maximum ivtv driver instances. Some people have a huge number of
+ capture cards, so set this to a high value. */
+#define IVTV_MAX_CARDS 32
+
+#define IVTV_ENC_STREAM_TYPE_MPG 0
+#define IVTV_ENC_STREAM_TYPE_YUV 1
+#define IVTV_ENC_STREAM_TYPE_VBI 2
+#define IVTV_ENC_STREAM_TYPE_PCM 3
+#define IVTV_ENC_STREAM_TYPE_RAD 4
+#define IVTV_DEC_STREAM_TYPE_MPG 5
+#define IVTV_DEC_STREAM_TYPE_VBI 6
+#define IVTV_DEC_STREAM_TYPE_VOUT 7
+#define IVTV_DEC_STREAM_TYPE_YUV 8
+#define IVTV_MAX_STREAMS 9
+
+#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */
+
+/* DMA Registers */
+#define IVTV_REG_DMAXFER (0x0000)
+#define IVTV_REG_DMASTATUS (0x0004)
+#define IVTV_REG_DECDMAADDR (0x0008)
+#define IVTV_REG_ENCDMAADDR (0x000c)
+#define IVTV_REG_DMACONTROL (0x0010)
+#define IVTV_REG_IRQSTATUS (0x0040)
+#define IVTV_REG_IRQMASK (0x0048)
+
+/* Setup Registers */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8)
+#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC)
+#define IVTV_REG_VDM (0x2800)
+#define IVTV_REG_AO (0x2D00)
+#define IVTV_REG_BYTEFLUSH (0x2D24)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/* Other registers */
+#define IVTV_REG_DEC_LINE_FIELD (0x28C0)
+
+/* debugging */
+extern int ivtv_debug;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+extern int ivtv_fw_debug;
+#endif
+
+#define IVTV_DBGFLG_WARN (1 << 0)
+#define IVTV_DBGFLG_INFO (1 << 1)
+#define IVTV_DBGFLG_MB (1 << 2)
+#define IVTV_DBGFLG_IOCTL (1 << 3)
+#define IVTV_DBGFLG_FILE (1 << 4)
+#define IVTV_DBGFLG_DMA (1 << 5)
+#define IVTV_DBGFLG_IRQ (1 << 6)
+#define IVTV_DBGFLG_DEC (1 << 7)
+#define IVTV_DBGFLG_YUV (1 << 8)
+#define IVTV_DBGFLG_I2C (1 << 9)
+/* Flag to turn on high volume debugging */
+#define IVTV_DBGFLG_HIGHVOL (1 << 10)
+
+#define IVTV_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_debug) \
+ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_DEBUG_MB(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_MB, "mb", fmt , ## args)
+#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_FILE(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_FILE, "file", fmt , ## args)
+#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+#define IVTV_DEBUG_HI_MB(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_MB, "mb", fmt , ## args)
+#define IVTV_DEBUG_HI_DMA(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
+#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
+#define IVTV_DEBUG_HI_FILE(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_FILE, "file", fmt , ## args)
+#define IVTV_DEBUG_HI_I2C(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
+#define IVTV_DEBUG_HI_IRQ(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
+#define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
+#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+/* Standard kernel messages */
+#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args)
+#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args)
+
+/* output modes (cx23415 only) */
+#define OUT_NONE 0
+#define OUT_MPG 1
+#define OUT_YUV 2
+#define OUT_UDMA_YUV 3
+#define OUT_PASSTHROUGH 4
+
+#define IVTV_MAX_PGM_INDEX (400)
+
+/* Default I2C SCL period in microseconds */
+#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20
+
+struct ivtv_options {
+ int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */
+ int cardtype; /* force card type on load */
+ int tuner; /* set tuner on load */
+ int radio; /* enable/disable radio */
+ int newi2c; /* new I2C algorithm */
+ int i2c_clock_period; /* period of SCL for I2C bus */
+};
+
+/* ivtv-specific mailbox template */
+struct ivtv_mailbox {
+ u32 flags;
+ u32 cmd;
+ u32 retval;
+ u32 timeout;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+};
+
+struct ivtv_api_cache {
+ unsigned long last_jiffies; /* when last command was issued */
+ u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */
+};
+
+struct ivtv_mailbox_data {
+ volatile struct ivtv_mailbox __iomem *mbox;
+ /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
+ If the bit is set, then the corresponding mailbox is in use by the driver. */
+ unsigned long busy;
+ u8 max_mbox;
+};
+
+/* per-buffer bit flags */
+#define IVTV_F_B_NEED_BUF_SWAP (1 << 0) /* this buffer should be byte swapped */
+
+/* per-stream, s_flags */
+#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */
+#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */
+#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */
+
+#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */
+#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
+#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
+#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */
+#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
+#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
+
+#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */
+#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */
+
+/* per-ivtv, i_flags */
+#define IVTV_F_I_DMA 0 /* DMA in progress */
+#define IVTV_F_I_UDMA 1 /* UDMA in progress */
+#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */
+#define IVTV_F_I_SPEED_CHANGE 3 /* a speed change is in progress */
+#define IVTV_F_I_EOS 4 /* end of encoder stream reached */
+#define IVTV_F_I_RADIO_USER 5 /* the radio tuner is selected */
+#define IVTV_F_I_DIG_RST 6 /* reset digitizer */
+#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */
+#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */
+#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */
+#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */
+#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
+#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
+#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
+#define IVTV_F_I_HAVE_WORK 15 /* used in the interrupt handler: there is work to be done */
+#define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */
+#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */
+#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */
+#define IVTV_F_I_PIO 19 /* PIO in progress */
+#define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */
+#define IVTV_F_I_INITED 21 /* set after first open */
+#define IVTV_F_I_FAILED 22 /* set if first open failed */
+#define IVTV_F_I_WORK_HANDLER_PCM 23 /* there is work to be done for PCM */
+
+/* Event notifications */
+#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
+#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */
+#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */
+#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */
+
+/* Scatter-Gather array element, used in DMA transfers */
+struct ivtv_sg_element {
+ __le32 src;
+ __le32 dst;
+ __le32 size;
+};
+
+struct ivtv_sg_host_element {
+ u32 src;
+ u32 dst;
+ u32 size;
+};
+
+struct ivtv_user_dma {
+ struct mutex lock;
+ int page_count;
+ struct page *map[IVTV_DMA_SG_OSD_ENT];
+ /* Needed when dealing with highmem userspace buffers */
+ struct page *bouncemap[IVTV_DMA_SG_OSD_ENT];
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
+ dma_addr_t SG_handle;
+ int SG_length;
+
+ /* SG List of Buffers */
+ struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
+};
+
+struct ivtv_dma_page_info {
+ unsigned long uaddr;
+ unsigned long first;
+ unsigned long last;
+ unsigned int offset;
+ unsigned int tail;
+ int page_count;
+};
+
+struct ivtv_buffer {
+ struct list_head list;
+ dma_addr_t dma_handle;
+ unsigned short b_flags;
+ unsigned short dma_xfer_cnt;
+ char *buf;
+ u32 bytesused;
+ u32 readpos;
+};
+
+struct ivtv_queue {
+ struct list_head list; /* the list of buffers in this queue */
+ u32 buffers; /* number of buffers in this queue */
+ u32 length; /* total number of bytes of available buffer space */
+ u32 bytesused; /* total number of bytes used in this queue */
+};
+
+struct ivtv; /* forward reference */
+
+struct ivtv_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+ struct video_device *vdev; /* NULL when stream not created */
+ struct ivtv *itv; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+ u32 caps; /* V4L2 capabilities */
+
+ struct v4l2_fh *fh; /* pointer to the streaming filehandle */
+ spinlock_t qlock; /* locks access to the queues */
+ unsigned long s_flags; /* status flags, see above */
+ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */
+ u32 pending_offset;
+ u32 pending_backup;
+ u64 pending_pts;
+
+ u32 dma_offset;
+ u32 dma_backup;
+ u64 dma_pts;
+
+ int subtype;
+ wait_queue_head_t waitq;
+ u32 dma_last_offset;
+
+ /* Buffer Stats */
+ u32 buffers;
+ u32 buf_size;
+ u32 buffers_stolen;
+
+ /* Buffer Queues */
+ struct ivtv_queue q_free; /* free buffers */
+ struct ivtv_queue q_full; /* full buffers */
+ struct ivtv_queue q_io; /* waiting for I/O */
+ struct ivtv_queue q_dma; /* waiting for DMA */
+ struct ivtv_queue q_predma; /* waiting for DMA */
+
+ /* DMA xfer counter, buffers belonging to the same DMA
+ xfer will have the same dma_xfer_cnt. */
+ u16 dma_xfer_cnt;
+
+ /* Base Dev SG Array for cx23415/6 */
+ struct ivtv_sg_host_element *sg_pending;
+ struct ivtv_sg_host_element *sg_processing;
+ struct ivtv_sg_element *sg_dma;
+ dma_addr_t sg_handle;
+ int sg_pending_size;
+ int sg_processing_size;
+ int sg_processed;
+
+ /* SG List of Buffers */
+ struct scatterlist *SGlist;
+};
+
+struct ivtv_open_id {
+ struct v4l2_fh fh;
+ int type; /* stream type */
+ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */
+ struct ivtv *itv;
+};
+
+static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct ivtv_open_id, fh);
+}
+
+struct yuv_frame_info
+{
+ u32 update;
+ s32 src_x;
+ s32 src_y;
+ u32 src_w;
+ u32 src_h;
+ s32 dst_x;
+ s32 dst_y;
+ u32 dst_w;
+ u32 dst_h;
+ s32 pan_x;
+ s32 pan_y;
+ u32 vis_w;
+ u32 vis_h;
+ u32 interlaced_y;
+ u32 interlaced_uv;
+ s32 tru_x;
+ u32 tru_w;
+ u32 tru_h;
+ u32 offset_y;
+ s32 lace_mode;
+ u32 sync_field;
+ u32 delay;
+ u32 interlaced;
+};
+
+#define IVTV_YUV_MODE_INTERLACED 0x00
+#define IVTV_YUV_MODE_PROGRESSIVE 0x01
+#define IVTV_YUV_MODE_AUTO 0x02
+#define IVTV_YUV_MODE_MASK 0x03
+
+#define IVTV_YUV_SYNC_EVEN 0x00
+#define IVTV_YUV_SYNC_ODD 0x04
+#define IVTV_YUV_SYNC_MASK 0x04
+
+#define IVTV_YUV_BUFFERS 8
+
+struct yuv_playback_info
+{
+ u32 reg_2834;
+ u32 reg_2838;
+ u32 reg_283c;
+ u32 reg_2840;
+ u32 reg_2844;
+ u32 reg_2848;
+ u32 reg_2854;
+ u32 reg_285c;
+ u32 reg_2864;
+
+ u32 reg_2870;
+ u32 reg_2874;
+ u32 reg_2890;
+ u32 reg_2898;
+ u32 reg_289c;
+
+ u32 reg_2918;
+ u32 reg_291c;
+ u32 reg_2920;
+ u32 reg_2924;
+ u32 reg_2928;
+ u32 reg_292c;
+ u32 reg_2930;
+
+ u32 reg_2934;
+
+ u32 reg_2938;
+ u32 reg_293c;
+ u32 reg_2940;
+ u32 reg_2944;
+ u32 reg_2948;
+ u32 reg_294c;
+ u32 reg_2950;
+ u32 reg_2954;
+ u32 reg_2958;
+ u32 reg_295c;
+ u32 reg_2960;
+ u32 reg_2964;
+ u32 reg_2968;
+ u32 reg_296c;
+
+ u32 reg_2970;
+
+ int v_filter_1;
+ int v_filter_2;
+ int h_filter;
+
+ u8 track_osd; /* Should yuv output track the OSD size & position */
+
+ u32 osd_x_offset;
+ u32 osd_y_offset;
+
+ u32 osd_x_pan;
+ u32 osd_y_pan;
+
+ u32 osd_vis_w;
+ u32 osd_vis_h;
+
+ u32 osd_full_w;
+ u32 osd_full_h;
+
+ int decode_height;
+
+ int lace_mode;
+ int lace_threshold;
+ int lace_sync_field;
+
+ atomic_t next_dma_frame;
+ atomic_t next_fill_frame;
+
+ u32 yuv_forced_update;
+ int update_frame;
+
+ u8 fields_lapsed; /* Counter used when delaying a frame */
+
+ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
+ struct yuv_frame_info old_frame_info;
+ struct yuv_frame_info old_frame_info_args;
+
+ void *blanking_ptr;
+ dma_addr_t blanking_dmaptr;
+
+ int stream_size;
+
+ u8 draw_frame; /* PVR350 buffer to draw into */
+ u8 max_frames_buffered; /* Maximum number of frames to buffer */
+
+ struct v4l2_rect main_rect;
+ u32 v4l2_src_w;
+ u32 v4l2_src_h;
+
+ u8 running; /* Have any frames been displayed */
+};
+
+#define IVTV_VBI_FRAMES 32
+
+/* VBI data */
+struct vbi_cc {
+ u8 odd[2]; /* two-byte payload of odd field */
+ u8 even[2]; /* two-byte payload of even field */;
+};
+
+struct vbi_vps {
+ u8 data[5]; /* five-byte VPS payload */
+};
+
+struct vbi_info {
+ /* VBI general data, does not change during streaming */
+
+ u32 raw_decoder_line_size; /* raw VBI line size from digitizer */
+ u8 raw_decoder_sav_odd_field; /* raw VBI Start Active Video digitizer code of odd field */
+ u8 raw_decoder_sav_even_field; /* raw VBI Start Active Video digitizer code of even field */
+ u32 sliced_decoder_line_size; /* sliced VBI line size from digitizer */
+ u8 sliced_decoder_sav_odd_field; /* sliced VBI Start Active Video digitizer code of odd field */
+ u8 sliced_decoder_sav_even_field; /* sliced VBI Start Active Video digitizer code of even field */
+
+ u32 start[2]; /* start of first VBI line in the odd/even fields */
+ u32 count; /* number of VBI lines per field */
+ u32 raw_size; /* size of raw VBI line from the digitizer */
+ u32 sliced_size; /* size of sliced VBI line from the digitizer */
+
+ u32 dec_start; /* start in decoder memory of VBI re-insertion buffers */
+ u32 enc_start; /* start in encoder memory of VBI capture buffers */
+ u32 enc_size; /* size of VBI capture area */
+ int fpi; /* number of VBI frames per interrupt */
+
+ struct v4l2_format in; /* current VBI capture format */
+ struct v4l2_sliced_vbi_format *sliced_in; /* convenience pointer to sliced struct in vbi.in union */
+ int insert_mpeg; /* if non-zero, then embed VBI data in MPEG stream */
+
+ /* Raw VBI compatibility hack */
+
+ u32 frame; /* frame counter hack needed for backwards compatibility
+ of old VBI software */
+
+ /* Sliced VBI output data */
+
+ struct vbi_cc cc_payload[256]; /* sliced VBI CC payload array: it is an array to
+ prevent dropping CC data if they couldn't be
+ processed fast enough */
+ int cc_payload_idx; /* index in cc_payload */
+ u8 cc_missing_cnt; /* counts number of frames without CC for passthrough mode */
+ int wss_payload; /* sliced VBI WSS payload */
+ u8 wss_missing_cnt; /* counts number of frames without WSS for passthrough mode */
+ struct vbi_vps vps_payload; /* sliced VBI VPS payload */
+
+ /* Sliced VBI capture data */
+
+ struct v4l2_sliced_vbi_data sliced_data[36]; /* sliced VBI storage for VBI encoder stream */
+ struct v4l2_sliced_vbi_data sliced_dec_data[36];/* sliced VBI storage for VBI decoder stream */
+
+ /* VBI Embedding data */
+
+ /* Buffer for VBI data inserted into MPEG stream.
+ The first byte is a dummy byte that's never used.
+ The next 16 bytes contain the MPEG header for the VBI data,
+ the remainder is the actual VBI data.
+ The max size accepted by the MPEG VBI reinsertion turns out
+ to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+ where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+ a single line header byte and 2 * 18 is the number of VBI lines per frame.
+
+ However, it seems that the data must be 1K aligned, so we have to
+ pad the data until the 1 or 2 K boundary.
+
+ This pointer array will allocate 2049 bytes to store each VBI frame. */
+ u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
+ u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
+ struct ivtv_buffer sliced_mpeg_buf; /* temporary buffer holding data from sliced_mpeg_data */
+ u32 inserted_frame; /* index in sliced_mpeg_size of next sliced data
+ to be inserted in the MPEG stream */
+};
+
+/* forward declaration of struct defined in ivtv-cards.h */
+struct ivtv_card;
+
+/* Struct to hold info about ivtv cards */
+struct ivtv {
+ /* General fixed card data */
+ struct pci_dev *pdev; /* PCI device */
+ const struct ivtv_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+ u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
+ u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u32 v4l2_cap; /* V4L2 capabilities of card */
+ u32 hw_flags; /* hardware description of the board */
+ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
+ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
+ struct v4l2_subdev *sd_audio; /* controlling audio subdev */
+ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
+ resource_size_t base_addr; /* PCI resource base address */
+ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
+ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
+ volatile void __iomem *reg_mem; /* pointer to mapped registers */
+ struct ivtv_options options; /* user options */
+
+ struct v4l2_device v4l2_dev;
+ struct cx2341x_handler cxhdl;
+ struct {
+ /* PTS/Frame count control cluster */
+ struct v4l2_ctrl *ctrl_pts;
+ struct v4l2_ctrl *ctrl_frame;
+ };
+ struct {
+ /* Audio Playback control cluster */
+ struct v4l2_ctrl *ctrl_audio_playback;
+ struct v4l2_ctrl *ctrl_audio_multilingual_playback;
+ };
+ struct v4l2_ctrl_handler hdl_gpio;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
+
+ /* High-level state info */
+ unsigned long i_flags; /* global ivtv flags */
+ u8 is_50hz; /* 1 if the current capture standard is 50 Hz */
+ u8 is_60hz /* 1 if the current capture standard is 60 Hz */;
+ u8 is_out_50hz /* 1 if the current TV output standard is 50 Hz */;
+ u8 is_out_60hz /* 1 if the current TV output standard is 60 Hz */;
+ int output_mode; /* decoder output mode: NONE, MPG, YUV, UDMA YUV, passthrough */
+ u32 audio_input; /* current audio input */
+ u32 active_input; /* current video input */
+ u32 active_output; /* current video output */
+ v4l2_std_id std; /* current capture TV standard */
+ v4l2_std_id std_out; /* current TV output standard */
+ u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
+ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
+
+ /* Locking */
+ spinlock_t lock; /* lock access to this struct */
+ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
+
+ /* Streams */
+ int stream_buf_size[IVTV_MAX_STREAMS]; /* stream buffer size */
+ struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* stream data */
+ atomic_t capturing; /* count number of active capture streams */
+ atomic_t decoding; /* count number of active decoding streams */
+
+ /* ALSA interface for PCM capture stream */
+ struct snd_ivtv_card *alsa;
+ void (*pcm_announce_callback)(struct snd_ivtv_card *card, u8 *pcm_data,
+ size_t num_bytes);
+
+ /* Used for ivtv-alsa module loading */
+ struct work_struct request_module_wk;
+
+ /* Interrupts & DMA */
+ u32 irqmask; /* active interrupts */
+ u32 irq_rr_idx; /* round-robin stream index */
+ struct kthread_worker irq_worker; /* kthread worker for PIO/YUV/VBI actions */
+ struct task_struct *irq_worker_task; /* task for irq_worker */
+ struct kthread_work irq_work; /* kthread work entry */
+ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
+ int cur_dma_stream; /* index of current stream doing DMA (-1 if none) */
+ int cur_pio_stream; /* index of current stream doing PIO (-1 if none) */
+ u32 dma_data_req_offset; /* store offset in decoder memory of current DMA request */
+ u32 dma_data_req_size; /* store size of current DMA request */
+ int dma_retries; /* current DMA retry attempt */
+ struct ivtv_user_dma udma; /* user based DMA for OSD */
+ struct timer_list dma_timer; /* timer used to catch unfinished DMAs */
+ u32 last_vsync_field; /* last seen vsync field */
+ wait_queue_head_t dma_waitq; /* wake up when the current DMA is finished */
+ wait_queue_head_t eos_waitq; /* wake up when EOS arrives */
+ wait_queue_head_t event_waitq; /* wake up when the next decoder event arrives */
+ wait_queue_head_t vsync_waitq; /* wake up when the next decoder vsync arrives */
+
+
+ /* Mailbox */
+ struct ivtv_mailbox_data enc_mbox; /* encoder mailboxes */
+ struct ivtv_mailbox_data dec_mbox; /* decoder mailboxes */
+ struct ivtv_api_cache api_cache[256]; /* cached API commands */
+
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algo_bit_data i2c_algo;
+ struct i2c_client i2c_client;
+ int i2c_state; /* i2c bit state */
+ struct mutex i2c_bus_lock; /* lock i2c bus */
+
+ struct IR_i2c_init_data ir_i2c_init_data;
+
+ /* Program Index information */
+ u32 pgm_info_offset; /* start of pgm info in encoder memory */
+ u32 pgm_info_num; /* number of elements in the pgm cyclic buffer in encoder memory */
+ u32 pgm_info_write_idx; /* last index written by the card that was transferred to pgm_info[] */
+ u32 pgm_info_read_idx; /* last index in pgm_info read by the application */
+ struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; /* filled from the pgm cyclic buffer on the card */
+
+
+ /* Miscellaneous */
+ u32 open_id; /* incremented each time an open occurs, is >= 1 */
+ int search_pack_header; /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */
+ int speed; /* current playback speed setting */
+ u8 speed_mute_audio; /* 1 if audio should be muted when fast forward */
+ u64 mpg_data_received; /* number of bytes received from the MPEG stream */
+ u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */
+ u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */
+ unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */
+ u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */
+
+
+ /* VBI state info */
+ struct vbi_info vbi; /* VBI-specific data */
+
+
+ /* YUV playback */
+ struct yuv_playback_info yuv_info; /* YUV playback data */
+
+
+ /* OSD support */
+ unsigned long osd_video_pbase;
+ int osd_global_alpha_state; /* 1 = global alpha is on */
+ int osd_local_alpha_state; /* 1 = local alpha is on */
+ int osd_chroma_key_state; /* 1 = chroma-keying is on */
+ u8 osd_global_alpha; /* current global alpha */
+ u32 osd_chroma_key; /* current chroma key */
+ struct v4l2_rect osd_rect; /* current OSD position and size */
+ struct v4l2_rect main_rect; /* current Main window position and size */
+ struct osd_info *osd_info; /* ivtvfb private OSD info */
+ void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */
+};
+
+static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct ivtv, v4l2_dev);
+}
+
+/* ivtv extensions to be loaded */
+extern int (*ivtv_ext_init)(struct ivtv *);
+
+/* Globals */
+extern int ivtv_first_minor;
+
+/*==============Prototypes==================*/
+
+/* Hardware/IRQ */
+void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
+void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
+
+/* try to set output mode, return current mode. */
+int ivtv_set_output_mode(struct ivtv *itv, int mode);
+
+/* return current output stream based on current mode */
+struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
+
+/* Return non-zero if a signal is pending */
+int ivtv_msleep_timeout(unsigned int msecs, int intr);
+
+/* Wait on queue, returns -EINTR if interrupted */
+int ivtv_waitq(wait_queue_head_t *waitq);
+
+/* Read Hauppauge eeprom */
+struct tveeprom; /* forward reference */
+void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
+
+/* First-open initialization: load firmware, init cx25840, etc. */
+int ivtv_init_on_first_open(struct ivtv *itv);
+
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int ivtv_raw_vbi(const struct ivtv *itv)
+{
+ return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
+/* This is a PCI post thing, where if the pci register is not read, then
+ the write doesn't always take effect right away. By reading back the
+ register any pending PCI writes will be performed (in order), and so
+ you can be sure that the writes are guaranteed to be done.
+
+ Rarely needed, only in some timing sensitive cases.
+ Apparently if this is not done some motherboards seem
+ to kill the firmware and get into the broken state until computer is
+ rebooted. */
+#define write_sync(val, reg) \
+ do { writel(val, reg); readl(reg); } while (0)
+
+#define read_reg(reg) readl(itv->reg_mem + (reg))
+#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
+#define write_reg_sync(val, reg) \
+ do { write_reg(val, reg); read_reg(reg); } while (0)
+
+#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
+#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
+#define write_enc_sync(val, addr) \
+ do { write_enc(val, addr); read_enc(addr); } while (0)
+
+#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
+#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
+#define write_dec_sync(val, addr) \
+ do { write_dec(val, addr); read_dec(addr); } while (0)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). Ignore any errors. */
+#define ivtv_call_hw(itv, hw, o, f, args...) \
+ do { \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \
+ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
+ } while (0)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. */
+#define ivtv_call_hw_err(itv, hw, o, f, args...) \
+({ \
+ struct v4l2_subdev *__sd; \
+ __v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \
+ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
+})
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c
new file mode 100644
index 000000000000..9caffd8aa995
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-fileops.c
@@ -0,0 +1,1073 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-vbi.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+#include <media/saa7115.h>
+
+/* This function tries to claim the stream for a specific file descriptor.
+ If no one else is using this stream then the stream is claimed and
+ associated VBI streams are also automatically claimed.
+ Possible error returns: -EBUSY if someone else has claimed
+ the stream or 0 on success. */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[type];
+ struct ivtv_stream *s_vbi;
+ int vbi_type;
+
+ if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ /* someone already claimed this stream */
+ if (s->fh == &id->fh) {
+ /* yes, this file descriptor did. So that's OK. */
+ return 0;
+ }
+ if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI ||
+ type == IVTV_ENC_STREAM_TYPE_VBI)) {
+ /* VBI is handled already internally, now also assign
+ the file descriptor to this stream for external
+ reading of the stream. */
+ s->fh = &id->fh;
+ IVTV_DEBUG_INFO("Start Read VBI\n");
+ return 0;
+ }
+ /* someone else is using this stream already */
+ IVTV_DEBUG_INFO("Stream %d is busy\n", type);
+ return -EBUSY;
+ }
+ s->fh = &id->fh;
+ if (type == IVTV_DEC_STREAM_TYPE_VBI) {
+ /* Enable reinsertion interrupt */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
+ (provided VBI insertion is on and sliced VBI is selected), for all
+ other streams we're done */
+ if (type == IVTV_DEC_STREAM_TYPE_MPG) {
+ vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
+ } else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
+ itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) {
+ vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
+ } else {
+ return 0;
+ }
+ s_vbi = &itv->streams[vbi_type];
+
+ if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
+ /* Enable reinsertion interrupt */
+ if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ }
+ /* mark that it is used internally */
+ set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
+ return 0;
+}
+EXPORT_SYMBOL(ivtv_claim_stream);
+
+/* This function releases a previously claimed stream. It will take into
+ account associated VBI streams. */
+void ivtv_release_stream(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi;
+
+ s->fh = NULL;
+ if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* this stream is still in use internally */
+ return;
+ }
+ if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
+ return;
+ }
+
+ ivtv_flush_queues(s);
+
+ /* disable reinsertion interrupt */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+
+ /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
+ IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
+ for all other streams we're done */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+ else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ else
+ return;
+
+ /* clear internal use flag */
+ if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
+ /* was already cleared */
+ return;
+ }
+ if (s_vbi->fh) {
+ /* VBI stream still claimed by a file descriptor */
+ return;
+ }
+ /* disable reinsertion interrupt */
+ if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
+ clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
+ ivtv_flush_queues(s_vbi);
+}
+EXPORT_SYMBOL(ivtv_release_stream);
+
+static void ivtv_dualwatch(struct ivtv *itv)
+{
+ struct v4l2_tuner vt;
+ u32 new_stereo_mode;
+ const u32 dual = 0x02;
+
+ new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+ memset(&vt, 0, sizeof(vt));
+ ivtv_call_all(itv, tuner, g_tuner, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+
+ if (new_stereo_mode == itv->dualwatch_stereo_mode)
+ return;
+
+ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
+ itv->dualwatch_stereo_mode, new_stereo_mode);
+ if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
+ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
+}
+
+static void ivtv_update_pgm_info(struct ivtv *itv)
+{
+ u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
+ int cnt;
+ int i = 0;
+
+ if (wr_idx >= itv->pgm_info_num) {
+ IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
+ return;
+ }
+ cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
+ while (i < cnt) {
+ int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+ struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
+ u32 addr = itv->pgm_info_offset + 4 + idx * 24;
+ const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1,
+ V4L2_ENC_IDX_FRAME_B, -1, -1, -1 };
+ // 1=I, 2=P, 4=B
+
+ e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
+ if (e->offset > itv->mpg_data_received) {
+ break;
+ }
+ e->offset += itv->vbi_data_inserted;
+ e->length = read_enc(addr);
+ e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
+ e->flags = mapping[read_enc(addr + 12) & 7];
+ i++;
+ }
+ itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
+}
+
+static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ struct ivtv_buffer *buf;
+ DEFINE_WAIT(wait);
+
+ *err = 0;
+ while (1) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
+ /* Process pending program info updates and pending VBI data */
+ ivtv_update_pgm_info(itv);
+
+ if (time_after(jiffies,
+ itv->dualwatch_jiffies +
+ msecs_to_jiffies(1000))) {
+ itv->dualwatch_jiffies = jiffies;
+ ivtv_dualwatch(itv);
+ }
+
+ if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
+ ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
+ }
+ }
+ buf = &itv->vbi.sliced_mpeg_buf;
+ if (buf->readpos != buf->bytesused) {
+ return buf;
+ }
+ }
+
+ /* do we have leftover data? */
+ buf = ivtv_dequeue(s, &s->q_io);
+ if (buf)
+ return buf;
+
+ /* do we have new data? */
+ buf = ivtv_dequeue(s, &s->q_full);
+ if (buf) {
+ if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
+ return buf;
+ buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ /* byteswap MPG data */
+ ivtv_buf_swap(buf);
+ else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
+ /* byteswap and process VBI data */
+ ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
+ }
+ return buf;
+ }
+
+ /* return if end of stream */
+ if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ IVTV_DEBUG_INFO("EOS %s\n", s->name);
+ return NULL;
+ }
+
+ /* return if file was opened with O_NONBLOCK */
+ if (non_block) {
+ *err = -EAGAIN;
+ return NULL;
+ }
+
+ /* wait for more data to arrive */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become available before we were added to the waitqueue */
+ if (!s->q_full.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ *err = -EINTR;
+ return NULL;
+ }
+ }
+}
+
+static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
+{
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+
+ itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
+ itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
+ itv->vbi.sliced_mpeg_buf.readpos = 0;
+}
+
+static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
+ char __user *ubuf, size_t ucount)
+{
+ struct ivtv *itv = s->itv;
+ size_t len = buf->bytesused - buf->readpos;
+
+ if (len > ucount) len = ucount;
+ if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) {
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+ u8 ch = itv->search_pack_header ? 0xba : 0xe0;
+ int stuffing, i;
+
+ while (start + len > p && (q = memchr(p, 0, start + len - p))) {
+ p = q + 1;
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch) {
+ continue;
+ }
+ if (!itv->search_pack_header) {
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+ if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
+ ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
+ ch = 0xba;
+ itv->search_pack_header = 1;
+ p = q + 9;
+ }
+ continue;
+ }
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+ if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
+ q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+ itv->search_pack_header = 0;
+ len = (char *)q - start;
+ ivtv_setup_sliced_vbi_buf(itv);
+ break;
+ }
+ }
+ }
+ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
+ IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
+ return -EFAULT;
+ }
+ /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
+ buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
+ buf == &itv->vbi.sliced_mpeg_buf); */
+ buf->readpos += len;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
+ itv->mpg_data_received += len;
+ return len;
+}
+
+static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
+{
+ struct ivtv *itv = s->itv;
+ size_t tot_written = 0;
+ int single_frame = 0;
+
+ if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) {
+ /* shouldn't happen */
+ IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
+ return -EIO;
+ }
+
+ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
+ arrive one-by-one, so make sure we never output more than one VBI frame at a time */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv)))
+ single_frame = 1;
+
+ for (;;) {
+ struct ivtv_buffer *buf;
+ int rc;
+
+ buf = ivtv_get_buffer(s, non_block, &rc);
+ /* if there is no data available... */
+ if (buf == NULL) {
+ /* if we got data, then return that regardless */
+ if (tot_written)
+ break;
+ /* EOS condition */
+ if (rc == 0) {
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ ivtv_release_stream(s);
+ }
+ /* set errno */
+ return rc;
+ }
+ rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
+ if (buf != &itv->vbi.sliced_mpeg_buf) {
+ ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
+ }
+ else if (buf->readpos == buf->bytesused) {
+ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
+ itv->vbi.sliced_mpeg_size[idx] = 0;
+ itv->vbi.inserted_frame++;
+ itv->vbi_data_inserted += buf->bytesused;
+ }
+ if (rc < 0)
+ return rc;
+ tot_written += rc;
+
+ if (tot_written == tot_count || single_frame)
+ break;
+ }
+ return tot_written;
+}
+
+static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
+ loff_t *pos, int non_block)
+{
+ ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
+ struct ivtv *itv = s->itv;
+
+ IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
+ if (rc > 0)
+ pos += rc;
+ return rc;
+}
+
+int ivtv_start_capture(struct ivtv_open_id *id)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct ivtv_stream *s_vbi;
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
+ s->type == IVTV_DEC_STREAM_TYPE_MPG ||
+ s->type == IVTV_DEC_STREAM_TYPE_YUV ||
+ s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ /* you cannot read from these stream types. */
+ return -EINVAL;
+ }
+
+ /* Try to claim this stream. */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start capturing */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* If capture is already in progress, then we also have to
+ do nothing extra. */
+ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return 0;
+ }
+
+ /* Start VBI capture if required */
+ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
+ !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
+ automatically when the MPG stream is claimed.
+ We only need to start the VBI capturing. */
+ if (ivtv_start_v4l2_encode_stream(s_vbi)) {
+ IVTV_DEBUG_WARN("VBI capture start failed\n");
+
+ /* Failure, clean up and return an error */
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ /* also releases the associated VBI stream */
+ ivtv_release_stream(s);
+ return -EIO;
+ }
+ IVTV_DEBUG_INFO("VBI insertion started\n");
+ }
+
+ /* Tell the card to start capturing */
+ if (!ivtv_start_v4l2_encode_stream(s)) {
+ /* We're done */
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ /* Resume a possibly paused encoder */
+ if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ return 0;
+ }
+
+ /* failure, clean up */
+ IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
+
+ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
+ automatically when the MPG stream is released.
+ We only need to stop the VBI capturing. */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
+ }
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_release_stream(s);
+ return -EIO;
+}
+
+ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ ssize_t rc;
+
+ IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
+
+ if (mutex_lock_interruptible(&itv->serialize_lock))
+ return -ERESTARTSYS;
+ rc = ivtv_start_capture(id);
+ if (!rc)
+ rc = ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&itv->serialize_lock);
+ return rc;
+}
+
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int rc;
+
+ if (atomic_read(&itv->decoding) == 0) {
+ if (ivtv_claim_stream(id, s->type)) {
+ /* someone else is using this stream already */
+ IVTV_DEBUG_WARN("start decode, stream already claimed\n");
+ return -EBUSY;
+ }
+ rc = ivtv_start_v4l2_decode_stream(s, 0);
+ if (rc < 0) {
+ if (rc == -EAGAIN)
+ rc = ivtv_start_v4l2_decode_stream(s, 0);
+ if (rc < 0)
+ return rc;
+ }
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ return ivtv_set_speed(itv, speed);
+ return 0;
+}
+
+static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ struct ivtv_buffer *buf;
+ struct ivtv_queue q;
+ int bytes_written = 0;
+ int mode;
+ int rc;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name);
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
+ s->type != IVTV_DEC_STREAM_TYPE_YUV &&
+ s->type != IVTV_DEC_STREAM_TYPE_VOUT)
+ /* not decoder streams */
+ return -EINVAL;
+
+ /* Try to claim this stream */
+ if (ivtv_claim_stream(id, s->type))
+ return -EBUSY;
+
+ /* This stream does not need to start any decoding */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
+ int elems = count / sizeof(struct v4l2_sliced_vbi_data);
+
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return ivtv_write_vbi_from_user(itv,
+ (const struct v4l2_sliced_vbi_data __user *)user_buf, elems);
+ }
+
+ mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
+
+ if (ivtv_set_output_mode(itv, mode) != mode) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ ivtv_queue_init(&q);
+ set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+
+ /* Start decoder (returns 0 if already started) */
+ rc = ivtv_start_decoding(id, itv->speed);
+ if (rc) {
+ IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
+
+ /* failure, clean up */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ return rc;
+ }
+
+retry:
+ /* If possible, just DMA the entire frame - Check the data transfer size
+ since we may get here before the stream has been fully set-up */
+ if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) {
+ while (count >= itv->dma_data_req_size) {
+ rc = ivtv_yuv_udma_stream_frame(itv, (void __user *)user_buf);
+
+ if (rc < 0)
+ return rc;
+
+ bytes_written += itv->dma_data_req_size;
+ user_buf += itv->dma_data_req_size;
+ count -= itv->dma_data_req_size;
+ }
+ if (count == 0) {
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+ }
+ }
+
+ for (;;) {
+ /* Gather buffers */
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
+ ivtv_enqueue(s, buf, &q);
+ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
+ ivtv_enqueue(s, buf, &q);
+ }
+ if (q.buffers)
+ break;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
+ /* New buffers might have become free before we were added to the waitqueue */
+ if (!s->q_free.buffers)
+ schedule();
+ finish_wait(&s->waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ IVTV_DEBUG_INFO("User stopped %s\n", s->name);
+ return -EINTR;
+ }
+ }
+
+ /* copy user data into buffers */
+ while ((buf = ivtv_dequeue(s, &q))) {
+ /* yuv is a pain. Don't copy more data than needed for a single
+ frame, otherwise we lose sync with the incoming stream */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ yi->stream_size + count > itv->dma_data_req_size)
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf,
+ itv->dma_data_req_size - yi->stream_size);
+ else
+ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
+
+ /* Make sure we really got all the user data */
+ if (rc < 0) {
+ ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
+ return rc;
+ }
+ user_buf += rc;
+ count -= rc;
+ bytes_written += rc;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->stream_size += rc;
+ /* If we have a complete yuv frame, break loop now */
+ if (yi->stream_size == itv->dma_data_req_size) {
+ ivtv_enqueue(s, buf, &s->q_full);
+ yi->stream_size = 0;
+ break;
+ }
+ }
+
+ if (buf->bytesused != s->buf_size) {
+ /* incomplete, leave in q_io for next time */
+ ivtv_enqueue(s, buf, &s->q_io);
+ break;
+ }
+ /* Byteswap MPEG buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_buf_swap(buf);
+ ivtv_enqueue(s, buf, &s->q_full);
+ }
+
+ if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
+ if (s->q_full.length >= itv->dma_data_req_size) {
+ int got_sig;
+
+ if (mode == OUT_YUV)
+ ivtv_yuv_setup_stream_frame(itv);
+
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (!(got_sig = signal_pending(current)) &&
+ test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (got_sig) {
+ IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
+ return -EINTR;
+ }
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
+ }
+ }
+ /* more user data is available, wait until buffers become free
+ to transfer the rest. */
+ if (count && !(filp->f_flags & O_NONBLOCK))
+ goto retry;
+ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
+ return bytes_written;
+}
+
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ ssize_t res;
+
+ if (mutex_lock_interruptible(&itv->serialize_lock))
+ return -ERESTARTSYS;
+ res = ivtv_write(filp, user_buf, count, pos);
+ mutex_unlock(&itv->serialize_lock);
+ return res;
+}
+
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int res = 0;
+
+ /* add stream's waitq to the poll list */
+ IVTV_DEBUG_HI_FILE("Decoder poll\n");
+
+ /* If there are subscribed events, then only use the new event
+ API instead of the old video.h based API. */
+ if (!list_empty(&id->fh.subscribed)) {
+ poll_wait(filp, &id->fh.wait, wait);
+ /* Turn off the old-style vsync events */
+ clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+ if (v4l2_event_pending(&id->fh))
+ res = POLLPRI;
+ } else {
+ /* This is the old-style API which is here only for backwards
+ compatibility. */
+ poll_wait(filp, &s->waitq, wait);
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+ if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
+ test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ res = POLLPRI;
+ }
+
+ /* Allow write if buffers are available for writing */
+ if (s->q_free.buffers)
+ res |= POLLOUT | POLLWRNORM;
+ return res;
+}
+
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait)
+{
+ unsigned long req_events = poll_requested_events(wait);
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ unsigned res = 0;
+
+ /* Start a capture if there is none */
+ if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) &&
+ s->type != IVTV_ENC_STREAM_TYPE_RAD &&
+ (req_events & (POLLIN | POLLRDNORM))) {
+ int rc;
+
+ mutex_lock(&itv->serialize_lock);
+ rc = ivtv_start_capture(id);
+ mutex_unlock(&itv->serialize_lock);
+ if (rc) {
+ IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
+ s->name, rc);
+ return POLLERR;
+ }
+ IVTV_DEBUG_FILE("Encoder poll started capture\n");
+ }
+
+ /* add stream's waitq to the poll list */
+ IVTV_DEBUG_HI_FILE("Encoder poll\n");
+ poll_wait(filp, &s->waitq, wait);
+ if (v4l2_event_pending(&id->fh))
+ res |= POLLPRI;
+ else
+ poll_wait(filp, &id->fh.wait, wait);
+
+ if (s->q_full.length || s->q_io.length)
+ return res | POLLIN | POLLRDNORM;
+ if (eof)
+ return res | POLLHUP;
+ return res;
+}
+
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+ /* 'Unclaim' this stream */
+
+ /* Stop capturing */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_INFO("close stopping capture\n");
+ /* Special case: a running VBI capture for VBI insertion
+ in the mpeg stream. Need to stop that too. */
+ if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
+ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
+ ivtv_stop_v4l2_encode_stream(s_vbi, 0);
+ }
+ if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
+ id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
+ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
+ /* Also used internally, don't stop capturing */
+ s->fh = NULL;
+ }
+ else {
+ ivtv_stop_v4l2_encode_stream(s, gop_end);
+ }
+ }
+ if (!gop_end) {
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ ivtv_release_stream(s);
+ }
+}
+
+static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
+{
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close() of %s\n", s->name);
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
+ /* Restore registers we've changed & clean up any mess */
+ ivtv_yuv_close(itv);
+ }
+
+ /* Stop decoding */
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ IVTV_DEBUG_INFO("close stopping decode\n");
+
+ ivtv_stop_v4l2_decode_stream(s, flags, pts);
+ itv->output_mode = OUT_NONE;
+ }
+ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames)
+ itv->output_mode = OUT_NONE;
+
+ itv->speed = 0;
+ clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+ ivtv_release_stream(s);
+}
+
+int ivtv_v4l2_close(struct file *filp)
+{
+ struct v4l2_fh *fh = filp->private_data;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ IVTV_DEBUG_FILE("close %s\n", s->name);
+
+ mutex_lock(&itv->serialize_lock);
+
+ /* Stop radio */
+ if (id->type == IVTV_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ /* Closing radio device, return to TV mode */
+ ivtv_mute(itv);
+ /* Mark that the radio is no longer in use */
+ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* Switch tuner to TV */
+ ivtv_call_all(itv, core, s_std, itv->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ ivtv_audio_set_io(itv);
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+ SAA7115_FREQ_32_11_MHZ, 0);
+ }
+ if (atomic_read(&itv->capturing) > 0) {
+ /* Undo video mute */
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+ v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
+ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+ }
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ }
+
+ v4l2_fh_del(fh);
+ v4l2_fh_exit(fh);
+
+ /* Easy case first: this stream was never claimed by us */
+ if (s->fh != &id->fh)
+ goto close_done;
+
+ /* 'Unclaim' this stream */
+
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
+
+ ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
+
+ /* If all output streams are closed, and if the user doesn't have
+ IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
+ if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) {
+ /* disable CC on TV-out */
+ ivtv_disable_cc(itv);
+ }
+ } else {
+ ivtv_stop_capture(id, 0);
+ }
+close_done:
+ kfree(id);
+ mutex_unlock(&itv->serialize_lock);
+ return 0;
+}
+
+static int ivtv_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ struct ivtv_stream *s = video_get_drvdata(vdev);
+ struct ivtv *itv = s->itv;
+ struct ivtv_open_id *item;
+ int res = 0;
+
+ IVTV_DEBUG_FILE("open %s\n", s->name);
+
+ if (ivtv_init_on_first_open(itv)) {
+ IVTV_ERR("Failed to initialize on device %s\n",
+ video_device_node_name(vdev));
+ return -ENXIO;
+ }
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ /* Unless ivtv_fw_debug is set, error out if firmware dead. */
+ if (ivtv_fw_debug) {
+ IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
+ video_device_node_name(vdev));
+ IVTV_WARN("Selected firmware errors will be ignored\n");
+ } else {
+#else
+ if (1) {
+#endif
+ res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+ if (res == -EAGAIN)
+ res = ivtv_firmware_check(itv, "ivtv_serialized_open");
+ if (res < 0)
+ return -EIO;
+ }
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
+ return -EBUSY;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
+ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
+ return -EBUSY;
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ if (read_reg(0x82c) == 0) {
+ IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
+ /* return -ENODEV; */
+ }
+ ivtv_udma_alloc(itv);
+ }
+
+ /* Allocate memory */
+ item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
+ if (NULL == item) {
+ IVTV_DEBUG_WARN("nomem on v4l2 open\n");
+ return -ENOMEM;
+ }
+ v4l2_fh_init(&item->fh, s->vdev);
+ item->itv = itv;
+ item->type = s->type;
+
+ filp->private_data = &item->fh;
+ v4l2_fh_add(&item->fh);
+
+ if (item->type == IVTV_ENC_STREAM_TYPE_RAD &&
+ v4l2_fh_is_singular_file(filp)) {
+ if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
+ if (atomic_read(&itv->capturing) > 0) {
+ /* switching to radio while capture is
+ in progress is not polite */
+ v4l2_fh_del(&item->fh);
+ v4l2_fh_exit(&item->fh);
+ kfree(item);
+ return -EBUSY;
+ }
+ }
+ /* Mark that the radio is being used. */
+ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
+ /* We have the radio */
+ ivtv_mute(itv);
+ /* Switch tuner to radio */
+ ivtv_call_all(itv, tuner, s_radio);
+ /* Select the correct audio input (i.e. radio tuner) */
+ ivtv_audio_set_io(itv);
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
+ SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL);
+ }
+ /* Done! Unmute and continue. */
+ ivtv_unmute(itv);
+ }
+
+ /* YUV or MPG Decoding Mode? */
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) {
+ clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
+ /* For yuv, we need to know the dma size before we start */
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->yuv_info.stream_size = 0;
+ }
+ return 0;
+}
+
+int ivtv_v4l2_open(struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int res;
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ res = ivtv_open(filp);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+void ivtv_mute(struct ivtv *itv)
+{
+ if (atomic_read(&itv->capturing))
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
+ IVTV_DEBUG_INFO("Mute\n");
+}
+
+void ivtv_unmute(struct ivtv *itv)
+{
+ if (atomic_read(&itv->capturing)) {
+ ivtv_msleep_timeout(100, 0);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
+ }
+ IVTV_DEBUG_INFO("Unmute\n");
+}
diff --git a/drivers/media/pci/ivtv/ivtv-fileops.h b/drivers/media/pci/ivtv/ivtv-fileops.h
new file mode 100644
index 000000000000..5e08800772ca
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-fileops.h
@@ -0,0 +1,44 @@
+/*
+ file operation functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef IVTV_FILEOPS_H
+#define IVTV_FILEOPS_H
+
+/* Testing/Debugging */
+int ivtv_v4l2_open(struct file *filp);
+ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t * pos);
+ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
+ loff_t * pos);
+int ivtv_v4l2_close(struct file *filp);
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
+unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
+int ivtv_start_capture(struct ivtv_open_id *id);
+void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
+int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
+void ivtv_mute(struct ivtv *itv);
+void ivtv_unmute(struct ivtv *itv);
+
+/* Utilities */
+/* Shared with ivtv-alsa module */
+int ivtv_claim_stream(struct ivtv_open_id *id, int type);
+void ivtv_release_stream(struct ivtv_stream *s);
+
+#endif
diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c
new file mode 100644
index 000000000000..6ec7705af555
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-firmware.c
@@ -0,0 +1,402 @@
+/*
+ ivtv firmware functions.
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-firmware.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-cards.h"
+#include <linux/firmware.h>
+#include <media/saa7127.h>
+
+#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
+#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
+#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
+#define IVTV_CMD_VDM_STOP 0x00000000
+#define IVTV_CMD_AO_STOP 0x00000005
+#define IVTV_CMD_APU_PING 0x00000000
+#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
+#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+#define IVTV_CMD_SPU_STOP 0x00000001
+#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
+#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
+#define IVTV_SDRAM_SLEEPTIME 600
+
+#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg"
+#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024)
+
+/* Encoder/decoder firmware sizes */
+#define IVTV_FW_ENC_SIZE (376836)
+#define IVTV_FW_DEC_SIZE (256*1024)
+
+static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
+{
+ const struct firmware *fw = NULL;
+ int retries = 3;
+
+retry:
+ if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) {
+ int i;
+ volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+ const u32 *src = (const u32 *)fw->data;
+
+ if (fw->size != size) {
+ /* Due to race conditions in firmware loading (esp. with udev <0.95)
+ the wrong file was sometimes loaded. So we check filesizes to
+ see if at least the right-sized file was loaded. If not, then we
+ retry. */
+ IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
+ release_firmware(fw);
+ retries--;
+ goto retry;
+ }
+ for (i = 0; i < fw->size; i += 4) {
+ /* no need for endianness conversion on the ppc */
+ __raw_writel(*src, dst);
+ dst++;
+ src++;
+ }
+ IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size);
+ release_firmware(fw);
+ return size;
+ }
+ IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size);
+ IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+}
+
+void ivtv_halt_firmware(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
+ if (itv->has_cx23415 && itv->dec_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
+ if (itv->enc_mbox.mbox)
+ ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
+
+ ivtv_msleep_timeout(10, 0);
+ itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
+
+ IVTV_DEBUG_INFO("Stopping VDM\n");
+ write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
+
+ IVTV_DEBUG_INFO("Stopping AO\n");
+ write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
+
+ IVTV_DEBUG_INFO("pinging (?) APU\n");
+ write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
+
+ IVTV_DEBUG_INFO("Stopping VPU\n");
+ if (!itv->has_cx23415)
+ write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
+ else
+ write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
+
+ IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
+ write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
+
+ IVTV_DEBUG_INFO("Stopping SPU\n");
+ write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
+
+ ivtv_msleep_timeout(10, 0);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
+
+ if (itv->has_cx23415) {
+ IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
+ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
+
+ IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
+ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
+ }
+
+ IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME);
+ ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
+}
+
+void ivtv_firmware_versions(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ /* Encoder */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
+ IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
+
+ if (data[0] != 0x02060039)
+ IVTV_WARN("Recommended firmware version is 0x02060039.\n");
+
+ if (itv->has_cx23415) {
+ /* Decoder */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
+ IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
+ }
+}
+
+static int ivtv_firmware_copy(struct ivtv *itv)
+{
+ IVTV_DEBUG_INFO("Loading encoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
+ itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading encoder firmware\n");
+ return -3;
+ }
+ if (!itv->has_cx23415)
+ return 0;
+
+ IVTV_DEBUG_INFO("Loading decoder image\n");
+ if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
+ itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
+ IVTV_DEBUG_WARN("failed loading decoder firmware\n");
+ return -1;
+ }
+ return 0;
+}
+
+static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
+{
+ int i;
+
+ /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte
+ address boundary */
+ for (i = 0; i < size; i += 0x100) {
+ if (readl(mem + i) == 0x12345678 &&
+ readl(mem + i + 4) == 0x34567812 &&
+ readl(mem + i + 8) == 0x56781234 &&
+ readl(mem + i + 12) == 0x78123456) {
+ return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
+ }
+ }
+ return NULL;
+}
+
+int ivtv_firmware_init(struct ivtv *itv)
+{
+ int err;
+
+ ivtv_halt_firmware(itv);
+
+ /* load firmware */
+ err = ivtv_firmware_copy(itv);
+ if (err) {
+ IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
+ return err;
+ }
+
+ /* start firmware */
+ write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
+ ivtv_msleep_timeout(100, 0);
+ if (itv->has_cx23415)
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
+ else
+ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
+ ivtv_msleep_timeout(100, 0);
+
+ /* find mailboxes and ping firmware */
+ itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
+ if (itv->enc_mbox.mbox == NULL)
+ IVTV_ERR("Encoder mailbox not found\n");
+ else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
+ IVTV_ERR("Encoder firmware dead!\n");
+ itv->enc_mbox.mbox = NULL;
+ }
+ if (itv->enc_mbox.mbox == NULL)
+ return -ENODEV;
+
+ if (!itv->has_cx23415)
+ return 0;
+
+ itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
+ if (itv->dec_mbox.mbox == NULL) {
+ IVTV_ERR("Decoder mailbox not found\n");
+ } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
+ IVTV_ERR("Decoder firmware dead!\n");
+ itv->dec_mbox.mbox = NULL;
+ } else {
+ /* Firmware okay, so check yuv output filter table */
+ ivtv_yuv_filter_check(itv);
+ }
+ return itv->dec_mbox.mbox ? 0 : -ENODEV;
+}
+
+void ivtv_init_mpeg_decoder(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ long readbytes;
+ volatile u8 __iomem *mem_offset;
+
+ data[0] = 0;
+ data[1] = itv->cxhdl.width; /* YUV source width */
+ data[2] = itv->cxhdl.height;
+ data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
+ bitmap. see docs. */
+ if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
+ return;
+ }
+
+ if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
+ IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
+ return;
+ }
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+ mem_offset = itv->dec_mem + data[1];
+
+ if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
+ mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
+ IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
+ IVTV_DECODE_INIT_MPEG_FILENAME);
+ } else {
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
+ ivtv_msleep_timeout(100, 0);
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
+}
+
+/* Try to restart the card & restore previous settings */
+int ivtv_firmware_restart(struct ivtv *itv)
+{
+ int rc = 0;
+ v4l2_std_id std;
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ /* Display test image during restart */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_TEST_IMAGE,
+ itv->card->video_outputs[itv->active_output].video_output,
+ 0);
+
+ mutex_lock(&itv->udma.lock);
+
+ rc = ivtv_firmware_init(itv);
+ if (rc) {
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+ }
+
+ /* Allow settings to reload */
+ ivtv_mailbox_cache_invalidate(itv);
+
+ /* Restore encoder video standard */
+ std = itv->std;
+ itv->std = 0;
+ ivtv_s_std_enc(itv, &std);
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ ivtv_init_mpeg_decoder(itv);
+
+ /* Restore decoder video standard */
+ std = itv->std_out;
+ itv->std_out = 0;
+ ivtv_s_std_dec(itv, &std);
+
+ /* Restore framebuffer if active */
+ if (itv->ivtvfb_restore)
+ itv->ivtvfb_restore(itv);
+
+ /* Restore alpha settings */
+ ivtv_set_osd_alpha(itv);
+
+ /* Restore normal output */
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_NORMAL,
+ itv->card->video_outputs[itv->active_output].video_output,
+ 0);
+ }
+
+ mutex_unlock(&itv->udma.lock);
+ return rc;
+}
+
+/* Check firmware running state. The checks fall through
+ allowing multiple failures to be logged. */
+int ivtv_firmware_check(struct ivtv *itv, char *where)
+{
+ int res = 0;
+
+ /* Check encoder is still running */
+ if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) {
+ IVTV_WARN("Encoder has died : %s\n", where);
+ res = -1;
+ }
+
+ /* Also check audio. Only check if not in use & encoder is okay */
+ if (!res && !atomic_read(&itv->capturing) &&
+ (!atomic_read(&itv->decoding) ||
+ (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV,
+ &itv->i_flags)))) {
+
+ if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) {
+ IVTV_WARN("Audio has died (Encoder OK) : %s\n", where);
+ res = -2;
+ }
+ }
+
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ /* Second audio check. Skip if audio already failed */
+ if (res != -2 && read_dec(0x100) != read_dec(0x104)) {
+ /* Wait & try again to be certain. */
+ ivtv_msleep_timeout(14, 0);
+ if (read_dec(0x100) != read_dec(0x104)) {
+ IVTV_WARN("Audio has died (Decoder) : %s\n",
+ where);
+ res = -1;
+ }
+ }
+
+ /* Check decoder is still running */
+ if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) {
+ IVTV_WARN("Decoder has died : %s\n", where);
+ res = -1;
+ }
+ }
+
+ /* If something failed & currently idle, try to reload */
+ if (res && !atomic_read(&itv->capturing) &&
+ !atomic_read(&itv->decoding)) {
+ IVTV_INFO("Detected in %s that firmware had failed - "
+ "Reloading\n", where);
+ res = ivtv_firmware_restart(itv);
+ /*
+ * Even if restarted ok, still signal a problem had occurred.
+ * The caller can come through this function again to check
+ * if things are really ok after the restart.
+ */
+ if (!res) {
+ IVTV_INFO("Firmware restart okay\n");
+ res = -EAGAIN;
+ } else {
+ IVTV_INFO("Firmware restart failed\n");
+ }
+ } else if (res) {
+ res = -EIO;
+ }
+
+ return res;
+}
+
+MODULE_FIRMWARE(CX2341X_FIRM_ENC_FILENAME);
+MODULE_FIRMWARE(CX2341X_FIRM_DEC_FILENAME);
+MODULE_FIRMWARE(IVTV_DECODE_INIT_MPEG_FILENAME);
diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/pci/ivtv/ivtv-firmware.h
index 52bb4e5598fd..52bb4e5598fd 100644
--- a/drivers/media/video/ivtv/ivtv-firmware.h
+++ b/drivers/media/pci/ivtv/ivtv-firmware.h
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c
index 8f0d07789053..8f0d07789053 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/pci/ivtv/ivtv-gpio.c
diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/pci/ivtv/ivtv-gpio.h
index 0b5d19c8ecb4..0b5d19c8ecb4 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/drivers/media/pci/ivtv/ivtv-gpio.h
diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c
index d47f41a0ef66..d47f41a0ef66 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/drivers/media/pci/ivtv/ivtv-i2c.c
diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/pci/ivtv/ivtv-i2c.h
index 7b9ec1cfeb80..7b9ec1cfeb80 100644
--- a/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/drivers/media/pci/ivtv/ivtv-i2c.h
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
new file mode 100644
index 000000000000..949ae230e119
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -0,0 +1,1927 @@
+/*
+ ioctl system call
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-version.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-i2c.h"
+#include "ivtv-queue.h"
+#include "ivtv-fileops.h"
+#include "ivtv-vbi.h"
+#include "ivtv-routing.h"
+#include "ivtv-streams.h"
+#include "ivtv-yuv.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-gpio.h"
+#include "ivtv-controls.h"
+#include "ivtv-cards.h"
+#include <media/saa7127.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-event.h>
+#include <linux/dvb/audio.h>
+
+u16 ivtv_service2vbi(int type)
+{
+ switch (type) {
+ case V4L2_SLICED_TELETEXT_B:
+ return IVTV_SLICED_TYPE_TELETEXT_B;
+ case V4L2_SLICED_CAPTION_525:
+ return IVTV_SLICED_TYPE_CAPTION_525;
+ case V4L2_SLICED_WSS_625:
+ return IVTV_SLICED_TYPE_WSS_625;
+ case V4L2_SLICED_VPS:
+ return IVTV_SLICED_TYPE_VPS;
+ default:
+ return 0;
+ }
+}
+
+static int valid_service_line(int field, int line, int is_pal)
+{
+ return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
+ (!is_pal && line >= 10 && line < 22);
+}
+
+static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+{
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+ int i;
+
+ set = set & valid_set;
+ if (set == 0 || !valid_service_line(field, line, is_pal)) {
+ return 0;
+ }
+ if (!is_pal) {
+ if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
+ return V4L2_SLICED_CAPTION_525;
+ }
+ else {
+ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
+ return V4L2_SLICED_VPS;
+ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
+ return V4L2_SLICED_WSS_625;
+ if (line == 23)
+ return 0;
+ }
+ for (i = 0; i < 32; i++) {
+ if ((1 << i) & set)
+ return 1 << i;
+ }
+ return 0;
+}
+
+void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ u16 set = fmt->service_set;
+ int f, l;
+
+ fmt->service_set = 0;
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
+ }
+ }
+}
+
+static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+{
+ int f, l;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
+ }
+ }
+}
+
+u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+{
+ int f, l;
+ u16 set = 0;
+
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ set |= fmt->service_lines[f][l];
+ }
+ }
+ return set;
+}
+
+void ivtv_set_osd_alpha(struct ivtv *itv)
+{
+ ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
+ itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
+ ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key);
+}
+
+int ivtv_set_speed(struct ivtv *itv, int speed)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int single_step = (speed == 1 || speed == -1);
+ DEFINE_WAIT(wait);
+
+ if (speed == 0) speed = 1000;
+
+ /* No change? */
+ if (speed == itv->speed && !single_step)
+ return 0;
+
+ if (single_step && (speed < 0) == (itv->speed < 0)) {
+ /* Single step video and no need to change direction */
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ itv->speed = speed;
+ return 0;
+ }
+ if (single_step)
+ /* Need to change direction */
+ speed = speed < 0 ? -1000 : 1000;
+
+ data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
+ data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
+ data[1] = (speed < 0);
+ data[2] = speed < 0 ? 3 : 7;
+ data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
+ data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
+ data[5] = 0;
+ data[6] = 0;
+
+ if (speed == 1500 || speed == -1500) data[0] |= 1;
+ else if (speed == 2000 || speed == -2000) data[0] |= 2;
+ else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
+ else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
+
+ /* If not decoding, just change speed setting */
+ if (atomic_read(&itv->decoding) > 0) {
+ int got_sig = 0;
+
+ /* Stop all DMA and decoding activity */
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
+
+ /* Wait for any DMA to finish */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
+ while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ got_sig = signal_pending(current);
+ if (got_sig)
+ break;
+ got_sig = 0;
+ schedule();
+ }
+ finish_wait(&itv->dma_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (got_sig)
+ return -EINTR;
+
+ /* Change Speed safely */
+ ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
+ IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
+ }
+ if (single_step) {
+ speed = (speed < 0) ? -1 : 1;
+ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
+ }
+ itv->speed = speed;
+ return 0;
+}
+
+static int ivtv_validate_speed(int cur_speed, int new_speed)
+{
+ int fact = new_speed < 0 ? -1 : 1;
+ int s;
+
+ if (cur_speed == 0)
+ cur_speed = 1000;
+ if (new_speed < 0)
+ new_speed = -new_speed;
+ if (cur_speed < 0)
+ cur_speed = -cur_speed;
+
+ if (cur_speed <= new_speed) {
+ if (new_speed > 1500)
+ return fact * 2000;
+ if (new_speed > 1000)
+ return fact * 1500;
+ }
+ else {
+ if (new_speed >= 2000)
+ return fact * 2000;
+ if (new_speed >= 1500)
+ return fact * 1500;
+ if (new_speed >= 1000)
+ return fact * 1000;
+ }
+ if (new_speed == 0)
+ return 1000;
+ if (new_speed == 1 || new_speed == 1000)
+ return fact * new_speed;
+
+ s = new_speed;
+ new_speed = 1000 / new_speed;
+ if (1000 / cur_speed == new_speed)
+ new_speed += (cur_speed < s) ? -1 : 1;
+ if (new_speed > 60) return 1000 / (fact * 60);
+ return 1000 / (fact * new_speed);
+}
+
+static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
+ struct v4l2_decoder_cmd *dc, int try)
+{
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ switch (dc->cmd) {
+ case V4L2_DEC_CMD_START: {
+ dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
+ dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
+ if (dc->start.speed < 0)
+ dc->start.format = V4L2_DEC_START_FMT_GOP;
+ else
+ dc->start.format = V4L2_DEC_START_FMT_NONE;
+ if (dc->start.speed != 500 && dc->start.speed != 1500)
+ dc->flags = dc->start.speed == 1000 ? 0 :
+ V4L2_DEC_CMD_START_MUTE_AUDIO;
+ if (try) break;
+
+ itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
+ if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
+ return -EBUSY;
+ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+ /* forces ivtv_set_speed to be called */
+ itv->speed = 0;
+ }
+ return ivtv_start_decoding(id, dc->start.speed);
+ }
+
+ case V4L2_DEC_CMD_STOP:
+ dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
+ if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
+ dc->stop.pts = 0;
+ if (try) break;
+ if (atomic_read(&itv->decoding) == 0)
+ return 0;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+
+ itv->output_mode = OUT_NONE;
+ return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
+
+ case V4L2_DEC_CMD_PAUSE:
+ dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
+ if (try) break;
+ if (!atomic_read(&itv->decoding))
+ return -EPERM;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (atomic_read(&itv->decoding) > 0) {
+ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
+ (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
+ set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
+ }
+ break;
+
+ case V4L2_DEC_CMD_RESUME:
+ dc->flags = 0;
+ if (try) break;
+ if (!atomic_read(&itv->decoding))
+ return -EPERM;
+ if (itv->output_mode != OUT_MPG)
+ return -EBUSY;
+ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
+ int speed = itv->speed;
+ itv->speed = 0;
+ return ivtv_start_decoding(id, speed);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
+ if (itv->is_60hz) {
+ vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ pixfmt->width = itv->cxhdl.width;
+ pixfmt->height = itv->cxhdl.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
+ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+ vbifmt->sampling_rate = 27000000;
+ vbifmt->offset = 248;
+ vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;
+ vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbifmt->start[0] = itv->vbi.start[0];
+ vbifmt->start[1] = itv->vbi.start[1];
+ vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;
+ vbifmt->flags = 0;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ return 0;
+}
+
+static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
+ V4L2_SLICED_VBI_525;
+ ivtv_expand_service_set(vbifmt, itv->is_50hz);
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+ }
+
+ v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt);
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ pixfmt->width = itv->main_rect.width;
+ pixfmt->height = itv->main_rect.height;
+ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ pixfmt->priv = 0;
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {
+ switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
+ case IVTV_YUV_MODE_INTERLACED:
+ pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
+ V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
+ break;
+ case IVTV_YUV_MODE_PROGRESSIVE:
+ pixfmt->field = V4L2_FIELD_NONE;
+ break;
+ default:
+ pixfmt->field = V4L2_FIELD_ANY;
+ break;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+ pixfmt->bytesperline = 720;
+ pixfmt->width = itv->yuv_info.v4l2_src_w;
+ pixfmt->height = itv->yuv_info.v4l2_src_h;
+ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+ pixfmt->sizeimage =
+ 1080 * ((pixfmt->height + 31) & ~31);
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+ pixfmt->sizeimage = 128 * 1024;
+ pixfmt->bytesperline = 0;
+ }
+ return 0;
+}
+
+static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_window *winfmt = &fmt->fmt.win;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ winfmt->chromakey = itv->osd_chroma_key;
+ winfmt->global_alpha = itv->osd_global_alpha;
+ winfmt->field = V4L2_FIELD_INTERLACED;
+ winfmt->clips = NULL;
+ winfmt->clipcount = 0;
+ winfmt->bitmap = NULL;
+ winfmt->w.top = winfmt->w.left = 0;
+ winfmt->w.width = itv->osd_rect.width;
+ winfmt->w.height = itv->osd_rect.height;
+ return 0;
+}
+
+static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+ int min_h = 2;
+
+ w = min(w, 720);
+ w = max(w, 2);
+ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ /* YUV height must be a multiple of 32 */
+ h &= ~0x1f;
+ min_h = 32;
+ }
+ h = min(h, itv->is_50hz ? 576 : 480);
+ h = max(h, min_h);
+ ivtv_g_fmt_vid_cap(file, fh, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return 0;
+}
+
+static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (id->type == IVTV_DEC_STREAM_TYPE_VBI)
+ return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ /* set sliced VBI capture format */
+ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
+ vbifmt->reserved[0] = 0;
+ vbifmt->reserved[1] = 0;
+
+ if (vbifmt->service_set)
+ ivtv_expand_service_set(vbifmt, itv->is_50hz);
+ check_service_set(vbifmt, itv->is_50hz);
+ vbifmt->service_set = ivtv_get_service_set(vbifmt);
+ return 0;
+}
+
+static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ s32 w = fmt->fmt.pix.width;
+ s32 h = fmt->fmt.pix.height;
+ int field = fmt->fmt.pix.field;
+ int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
+
+ w = min(w, 720);
+ w = max(w, 2);
+ /* Why can the height be 576 even when the output is NTSC?
+
+ Internally the buffers of the PVR350 are always set to 720x576. The
+ decoded video frame will always be placed in the top left corner of
+ this buffer. For any video which is not 720x576, the buffer will
+ then be cropped to remove the unused right and lower areas, with
+ the remaining image being scaled by the hardware to fit the display
+ area. The video can be scaled both up and down, so a 720x480 video
+ can be displayed full-screen on PAL and a 720x576 video can be
+ displayed without cropping on NTSC.
+
+ Note that the scaling only occurs on the video stream, the osd
+ resolution is locked to the broadcast standard and not scaled.
+
+ Thanks to Ian Armstrong for this explanation. */
+ h = min(h, 576);
+ h = max(h, 2);
+ if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
+ fmt->fmt.pix.field = field;
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+ return ret;
+}
+
+static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 chromakey = fmt->fmt.win.chromakey;
+ u8 global_alpha = fmt->fmt.win.global_alpha;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
+ fmt->fmt.win.chromakey = chromakey;
+ fmt->fmt.win.global_alpha = global_alpha;
+ return 0;
+}
+
+static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
+
+ if (ret)
+ return ret;
+
+ if (itv->cxhdl.width == w && itv->cxhdl.height == h)
+ return 0;
+
+ if (atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+
+ itv->cxhdl.width = w;
+ itv->cxhdl.height = h;
+ if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
+ fmt->fmt.pix.width /= 2;
+ mbus_fmt.width = fmt->fmt.pix.width;
+ mbus_fmt.height = h;
+ mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
+ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt);
+ return ivtv_g_fmt_vid_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+ itv->vbi.sliced_in->service_set = 0;
+ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi);
+ return ivtv_g_fmt_vbi_cap(file, fh, fmt);
+}
+
+static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt);
+
+ if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
+ return ret;
+
+ check_service_set(vbifmt, itv->is_50hz);
+ if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
+ return -EBUSY;
+ itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt);
+ memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
+ return 0;
+}
+
+static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int ret = ivtv_try_fmt_vid_out(file, fh, fmt);
+
+ if (ret)
+ return ret;
+
+ if (id->type != IVTV_DEC_STREAM_TYPE_YUV)
+ return 0;
+
+ /* Return now if we already have some frame data */
+ if (yi->stream_size)
+ return -EBUSY;
+
+ yi->v4l2_src_w = fmt->fmt.pix.width;
+ yi->v4l2_src_h = fmt->fmt.pix.height;
+
+ switch (fmt->fmt.pix.field) {
+ case V4L2_FIELD_NONE:
+ yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
+ break;
+ case V4L2_FIELD_ANY:
+ yi->lace_mode = IVTV_YUV_MODE_AUTO;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ yi->lace_mode =
+ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
+ break;
+ case V4L2_FIELD_INTERLACED_TB:
+ default:
+ yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
+ break;
+ }
+ yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ itv->dma_data_req_size =
+ 1080 * ((yi->v4l2_src_h + 31) & ~31);
+
+ return 0;
+}
+
+static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt);
+
+ if (ret == 0) {
+ itv->osd_chroma_key = fmt->fmt.win.chromakey;
+ itv->osd_global_alpha = fmt->fmt.win.global_alpha;
+ ivtv_set_osd_alpha(itv);
+ }
+ return ret;
+}
+
+static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(&chip->match))
+ chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
+ return 0;
+ }
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ /* TODO: is this correct? */
+ return ivtv_call_all_err(itv, core, g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
+{
+ struct v4l2_dbg_register *regs = arg;
+ volatile u8 __iomem *reg_start;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
+ reg_start = itv->reg_mem - IVTV_REG_OFFSET;
+ else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
+ regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
+ reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
+ else if (regs->reg < IVTV_ENCODER_SIZE)
+ reg_start = itv->enc_mem;
+ else
+ return -EINVAL;
+
+ regs->size = 4;
+ if (cmd == VIDIOC_DBG_G_REGISTER)
+ regs->val = readl(regs->reg + reg_start);
+ else
+ writel(regs->val, regs->reg + reg_start);
+ return 0;
+}
+
+static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, g_register, reg);
+ return 0;
+}
+
+static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (v4l2_chip_match_host(&reg->match))
+ return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, s_register, reg);
+ return 0;
+}
+#endif
+
+static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
+
+ strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
+ vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
+ vcap->device_caps = s->caps;
+ return 0;
+}
+
+static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ vin->index = itv->audio_input;
+ return ivtv_get_audio_input(itv, vin->index, vin);
+}
+
+static int ivtv_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vout->index >= itv->nof_audio_inputs)
+ return -EINVAL;
+
+ itv->audio_input = vout->index;
+ ivtv_audio_set_io(itv);
+
+ return 0;
+}
+
+static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ /* set it to defaults from our table */
+ return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ vin->index = 0;
+ return ivtv_get_audio_output(itv, vin->index, vin);
+}
+
+static int ivtv_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (itv->card->video_outputs == NULL || vout->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ /* set it to defaults from our table */
+ return ivtv_get_input(itv, vin->index, vin);
+}
+
+static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ return ivtv_get_output(itv, vout->index, vout);
+}
+
+static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = 720;
+ if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ cropcap->bounds.height = itv->is_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
+ } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ if (yi->track_osd) {
+ cropcap->bounds.width = yi->osd_full_w;
+ cropcap->bounds.height = yi->osd_full_h;
+ } else {
+ cropcap->bounds.width = 720;
+ cropcap->bounds.height =
+ itv->is_out_50hz ? 576 : 480;
+ }
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+ } else {
+ cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
+ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
+ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
+ }
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
+ yi->main_rect = crop->c;
+ return 0;
+ } else {
+ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
+ itv->main_rect = crop->c;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int streamtype;
+
+ streamtype = id->type;
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
+ crop->c = yi->main_rect;
+ else
+ crop->c = itv->main_rect;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static const struct v4l2_fmtdesc hm12 = {
+ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ };
+ static const struct v4l2_fmtdesc mpeg = {
+ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ };
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+ if (fmt->index)
+ return -EINVAL;
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ *fmt = mpeg;
+ else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+ *fmt = hm12;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
+{
+ static const struct v4l2_fmtdesc hm12 = {
+ 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0,
+ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
+ { 0, 0, 0, 0 }
+ };
+ static const struct v4l2_fmtdesc mpeg = {
+ 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FMT_FLAG_COMPRESSED,
+ "MPEG", V4L2_PIX_FMT_MPEG,
+ { 0, 0, 0, 0 }
+ };
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+ if (fmt->index)
+ return -EINVAL;
+ if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
+ *fmt = mpeg;
+ else if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+ *fmt = hm12;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ *i = itv->active_input;
+
+ return 0;
+}
+
+int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ v4l2_std_id std;
+ int i;
+
+ if (inp < 0 || inp >= itv->nof_inputs)
+ return -EINVAL;
+
+ if (inp == itv->active_input) {
+ IVTV_DEBUG_INFO("Input unchanged\n");
+ return 0;
+ }
+
+ if (atomic_read(&itv->capturing) > 0) {
+ return -EBUSY;
+ }
+
+ IVTV_DEBUG_INFO("Changing input from %d to %d\n",
+ itv->active_input, inp);
+
+ itv->active_input = inp;
+ /* Set the audio input to whatever is appropriate for the
+ input type. */
+ itv->audio_input = itv->card->video_inputs[inp].audio_index;
+
+ if (itv->card->video_inputs[inp].video_type == IVTV_CARD_INPUT_VID_TUNER)
+ std = itv->tuner_std;
+ else
+ std = V4L2_STD_ALL;
+ for (i = 0; i <= IVTV_ENC_STREAM_TYPE_VBI; i++)
+ itv->streams[i].vdev->tvnorms = std;
+
+ /* prevent others from messing with the streams until
+ we're finished changing inputs. */
+ ivtv_mute(itv);
+ ivtv_video_set_io(itv);
+ ivtv_audio_set_io(itv);
+ ivtv_unmute(itv);
+
+ return 0;
+}
+
+static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+
+ *i = itv->active_output;
+
+ return 0;
+}
+
+static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (outp >= itv->card->nof_outputs)
+ return -EINVAL;
+
+ if (outp == itv->active_output) {
+ IVTV_DEBUG_INFO("Output unchanged\n");
+ return 0;
+ }
+ IVTV_DEBUG_INFO("Changing output from %d to %d\n",
+ itv->active_output, outp);
+
+ itv->active_output = outp;
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
+ SAA7127_INPUT_TYPE_NORMAL,
+ itv->card->video_outputs[outp].video_output, 0);
+
+ return 0;
+}
+
+static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+ if (s->vdev->vfl_dir)
+ return -ENOTTY;
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, g_frequency, vf);
+ return 0;
+}
+
+int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type];
+
+ if (s->vdev->vfl_dir)
+ return -ENOTTY;
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+ ivtv_mute(itv);
+ IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+ ivtv_call_all(itv, tuner, s_frequency, vf);
+ ivtv_unmute(itv);
+ return 0;
+}
+
+static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ *std = itv->std;
+ return 0;
+}
+
+void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std)
+{
+ itv->std = *std;
+ itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ itv->is_50hz = !itv->is_60hz;
+ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
+ itv->cxhdl.width = 720;
+ itv->cxhdl.height = itv->is_50hz ? 576 : 480;
+ itv->vbi.count = itv->is_50hz ? 18 : 12;
+ itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
+ itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
+
+ if (itv->hw_flags & IVTV_HW_CX25840)
+ itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
+
+ /* Tuner */
+ ivtv_call_all(itv, core, s_std, itv->std);
+}
+
+void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std)
+{
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ DEFINE_WAIT(wait);
+ int f;
+
+ /* set display standard */
+ itv->std_out = *std;
+ itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
+ itv->is_out_50hz = !itv->is_out_60hz;
+ ivtv_call_all(itv, video, s_std_output, itv->std_out);
+
+ /*
+ * The next firmware call is time sensitive. Time it to
+ * avoid risk of a hard lock, by trying to ensure the call
+ * happens within the first 100 lines of the top field.
+ * Make 4 attempts to sync to the decoder before giving up.
+ */
+ mutex_unlock(&itv->serialize_lock);
+ for (f = 0; f < 4; f++) {
+ prepare_to_wait(&itv->vsync_waitq, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100)
+ break;
+ schedule_timeout(msecs_to_jiffies(25));
+ }
+ finish_wait(&itv->vsync_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+
+ if (f == 4)
+ IVTV_WARN("Mode change failed to sync to decoder\n");
+
+ ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
+ itv->main_rect.left = 0;
+ itv->main_rect.top = 0;
+ itv->main_rect.width = 720;
+ itv->main_rect.height = itv->is_out_50hz ? 576 : 480;
+ ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
+ 720, itv->main_rect.height, 0, 0);
+ yi->main_rect = itv->main_rect;
+ if (!itv->osd_info) {
+ yi->osd_full_w = 720;
+ yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
+ }
+}
+
+int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if ((*std & V4L2_STD_ALL) == 0)
+ return -EINVAL;
+
+ if (*std == itv->std)
+ return 0;
+
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
+ atomic_read(&itv->capturing) > 0 ||
+ atomic_read(&itv->decoding) > 0) {
+ /* Switching standard would mess with already running
+ streams, prevent that by returning EBUSY. */
+ return -EBUSY;
+ }
+
+ IVTV_DEBUG_INFO("Switching standard to %llx.\n",
+ (unsigned long long)itv->std);
+
+ ivtv_s_std_enc(itv, std);
+ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
+ ivtv_s_std_dec(itv, std);
+
+ return 0;
+}
+
+static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, s_tuner, vt);
+
+ return 0;
+}
+
+static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ ivtv_call_all(itv, tuner, g_tuner, vt);
+
+ if (vt->type == V4L2_TUNER_RADIO)
+ strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
+ else
+ strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
+ return 0;
+}
+
+static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
+ int f, l;
+
+ if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
+ for (f = 0; f < 2; f++) {
+ for (l = 0; l < 24; l++) {
+ if (valid_service_line(f, l, itv->is_50hz))
+ cap->service_lines[f][l] = set;
+ }
+ }
+ } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
+ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
+ return -EINVAL;
+ if (itv->is_60hz) {
+ cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
+ } else {
+ cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ cap->service_lines[0][16] = V4L2_SLICED_VPS;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ set = 0;
+ for (f = 0; f < 2; f++)
+ for (l = 0; l < 24; l++)
+ set |= cap->service_lines[f][l];
+ cap->service_set = set;
+ return 0;
+}
+
+static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ struct v4l2_enc_idx_entry *e = idx->entry;
+ int entries;
+ int i;
+
+ entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
+ IVTV_MAX_PGM_INDEX;
+ if (entries > V4L2_ENC_IDX_ENTRIES)
+ entries = V4L2_ENC_IDX_ENTRIES;
+ idx->entries = 0;
+ idx->entries_cap = IVTV_MAX_PGM_INDEX;
+ if (!atomic_read(&itv->capturing))
+ return 0;
+ for (i = 0; i < entries; i++) {
+ *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
+ if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
+ idx->entries++;
+ e++;
+ }
+ }
+ itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
+ return 0;
+}
+
+static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return ivtv_start_capture(id);
+
+ case V4L2_ENC_CMD_STOP:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+ if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+
+ ivtv_mute(itv);
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
+ break;
+
+ case V4L2_ENC_CMD_RESUME:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+
+ if (!atomic_read(&itv->capturing))
+ return -EPERM;
+
+ if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
+ return 0;
+
+ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
+ ivtv_unmute(itv);
+ break;
+ default:
+ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ switch (enc->cmd) {
+ case V4L2_ENC_CMD_START:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
+ enc->flags = 0;
+ return 0;
+
+ case V4L2_ENC_CMD_STOP:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
+ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
+ return 0;
+
+ case V4L2_ENC_CMD_PAUSE:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
+ enc->flags = 0;
+ return 0;
+
+ case V4L2_ENC_CMD_RESUME:
+ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
+ enc->flags = 0;
+ return 0;
+ default:
+ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
+ return -EINVAL;
+ }
+}
+
+static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ int pixfmt;
+ static u32 pixel_format[16] = {
+ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
+ V4L2_PIX_FMT_RGB565,
+ V4L2_PIX_FMT_RGB555,
+ V4L2_PIX_FMT_RGB444,
+ V4L2_PIX_FMT_RGB32,
+ 0,
+ 0,
+ 0,
+ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
+ V4L2_PIX_FMT_YUV565,
+ V4L2_PIX_FMT_YUV555,
+ V4L2_PIX_FMT_YUV444,
+ V4L2_PIX_FMT_YUV32,
+ 0,
+ 0,
+ 0,
+ };
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+ if (!itv->osd_video_pbase)
+ return -EINVAL;
+
+ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
+ V4L2_FBUF_CAP_GLOBAL_ALPHA;
+
+ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+ data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+ pixfmt = (data[0] >> 3) & 0xf;
+
+ fb->fmt.pixelformat = pixel_format[pixfmt];
+ fb->fmt.width = itv->osd_rect.width;
+ fb->fmt.height = itv->osd_rect.height;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->fmt.bytesperline = fb->fmt.width;
+ fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->fmt.priv = 0;
+ if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8)
+ fb->fmt.bytesperline *= 2;
+ if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
+ fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32)
+ fb->fmt.bytesperline *= 2;
+ fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height;
+ fb->base = (void *)itv->osd_video_pbase;
+ fb->flags = 0;
+
+ if (itv->osd_chroma_key_state)
+ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+
+ if (itv->osd_global_alpha_state)
+ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
+
+ if (yi->track_osd)
+ fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
+
+ pixfmt &= 7;
+
+ /* no local alpha for RGB565 or unknown formats */
+ if (pixfmt == 1 || pixfmt > 4)
+ return 0;
+
+ /* 16-bit formats have inverted local alpha */
+ if (pixfmt == 2 || pixfmt == 3)
+ fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
+ else
+ fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
+
+ if (itv->osd_local_alpha_state) {
+ /* 16-bit formats have inverted local alpha */
+ if (pixfmt == 2 || pixfmt == 3)
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
+ else
+ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ }
+
+ return 0;
+}
+
+static int ivtv_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+ if (!itv->osd_video_pbase)
+ return -EINVAL;
+
+ itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
+ itv->osd_local_alpha_state =
+ (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
+ itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
+ ivtv_set_osd_alpha(itv);
+ yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
+ return 0;
+}
+
+static int ivtv_overlay(struct file *file, void *fh, unsigned int on)
+{
+ struct ivtv_open_id *id = fh2id(fh);
+ struct ivtv *itv = id->itv;
+
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
+ return -EINVAL;
+
+ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
+
+ return 0;
+}
+
+static int ivtv_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_VSYNC:
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ivtv_log_status(struct file *file, void *fh)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
+ struct v4l2_input vidin;
+ struct v4l2_audio audin;
+ int i;
+
+ IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
+ if (itv->hw_flags & IVTV_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ ivtv_read_eeprom(itv, &tv);
+ }
+ ivtv_call_all(itv, core, log_status);
+ ivtv_get_input(itv, itv->active_input, &vidin);
+ ivtv_get_audio_input(itv, itv->audio_input, &audin);
+ IVTV_INFO("Video Input: %s\n", vidin.name);
+ IVTV_INFO("Audio Input: %s%s\n", audin.name,
+ (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
+ if (has_output) {
+ struct v4l2_output vidout;
+ struct v4l2_audioout audout;
+ int mode = itv->output_mode;
+ static const char * const output_modes[5] = {
+ "None",
+ "MPEG Streaming",
+ "YUV Streaming",
+ "YUV Frames",
+ "Passthrough",
+ };
+ static const char * const alpha_mode[4] = {
+ "None",
+ "Global",
+ "Local",
+ "Global and Local"
+ };
+ static const char * const pixel_format[16] = {
+ "ARGB Indexed",
+ "RGB 5:6:5",
+ "ARGB 1:5:5:5",
+ "ARGB 1:4:4:4",
+ "ARGB 8:8:8:8",
+ "5",
+ "6",
+ "7",
+ "AYUV Indexed",
+ "YUV 5:6:5",
+ "AYUV 1:5:5:5",
+ "AYUV 1:4:4:4",
+ "AYUV 8:8:8:8",
+ "13",
+ "14",
+ "15",
+ };
+
+ ivtv_get_output(itv, itv->active_output, &vidout);
+ ivtv_get_audio_output(itv, 0, &audout);
+ IVTV_INFO("Video Output: %s\n", vidout.name);
+ if (mode < 0 || mode > OUT_PASSTHROUGH)
+ mode = OUT_NONE;
+ IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
+ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
+ data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
+ IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n",
+ data[0] & 1 ? "On" : "Off",
+ alpha_mode[(data[0] >> 1) & 0x3],
+ pixel_format[(data[0] >> 3) & 0xf]);
+ }
+ IVTV_INFO("Tuner: %s\n",
+ test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
+ IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->vdev == NULL || s->buffers == 0)
+ continue;
+ IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+ (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+ (s->buffers * s->buf_size) / 1024, s->buffers);
+ }
+
+ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+ (long long)itv->mpg_data_received,
+ (long long)itv->vbi_data_inserted);
+ return 0;
+}
+
+static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, false);
+}
+
+static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, true);
+}
+
+static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
+{
+ struct ivtv_open_id *id = fh2id(filp->private_data);
+ struct ivtv *itv = id->itv;
+ int nonblocking = filp->f_flags & O_NONBLOCK;
+ struct ivtv_stream *s = &itv->streams[id->type];
+ unsigned long iarg = (unsigned long)arg;
+
+ switch (cmd) {
+ case IVTV_IOC_DMA_FRAME: {
+ struct ivtv_dma_frame *args = arg;
+
+ IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
+ return 0;
+ if (ivtv_start_decoding(id, id->type)) {
+ return -EBUSY;
+ }
+ if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
+ ivtv_release_stream(s);
+ return -EBUSY;
+ }
+ /* Mark that this file handle started the UDMA_YUV mode */
+ id->yuv_frames = 1;
+ if (args->y_source == NULL)
+ return 0;
+ return ivtv_yuv_prep_frame(itv, args);
+ }
+
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, *(int *)arg != 0);
+
+ case VIDEO_GET_PTS: {
+ s64 *pts = arg;
+ s64 frame;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *pts = s->dma_pts;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_g_pts_frame(itv, pts, &frame);
+ }
+
+ case VIDEO_GET_FRAME_COUNT: {
+ s64 *frame = arg;
+ s64 pts;
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
+ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
+ *frame = 0;
+ break;
+ }
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_g_pts_frame(itv, &pts, frame);
+ }
+
+ case VIDEO_PLAY: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_START;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_STOP: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_STOP;
+ dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_FREEZE: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_PAUSE;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_CONTINUE: {
+ struct v4l2_decoder_cmd dc;
+
+ IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_RESUME;
+ return ivtv_video_command(itv, id, &dc, 0);
+ }
+
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND: {
+ /* Note: struct v4l2_decoder_cmd has the same layout as
+ struct video_command */
+ struct v4l2_decoder_cmd *dc = arg;
+ int try = (cmd == VIDEO_TRY_COMMAND);
+
+ if (try)
+ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
+ else
+ IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
+ return ivtv_video_command(itv, id, dc, try);
+ }
+
+ case VIDEO_GET_EVENT: {
+ struct video_event *ev = arg;
+ DEFINE_WAIT(wait);
+
+ IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ memset(ev, 0, sizeof(*ev));
+ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
+
+ while (1) {
+ if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
+ ev->type = VIDEO_EVENT_DECODER_STOPPED;
+ else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
+ ev->type = VIDEO_EVENT_VSYNC;
+ ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
+ VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
+ if (itv->output_mode == OUT_UDMA_YUV &&
+ (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
+ IVTV_YUV_MODE_PROGRESSIVE) {
+ ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
+ }
+ }
+ if (ev->type)
+ return 0;
+ if (nonblocking)
+ return -EAGAIN;
+ /* Wait for event. Note that serialize_lock is locked,
+ so to allow other processes to access the driver while
+ we are waiting unlock first and later lock again. */
+ mutex_unlock(&itv->serialize_lock);
+ prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
+ if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) &&
+ !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags))
+ schedule();
+ finish_wait(&itv->event_waitq, &wait);
+ mutex_lock(&itv->serialize_lock);
+ if (signal_pending(current)) {
+ /* return if a signal was received */
+ IVTV_DEBUG_INFO("User stopped wait for event\n");
+ return -EINTR;
+ }
+ }
+ break;
+ }
+
+ case VIDEO_SELECT_SOURCE:
+ IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX);
+
+ case AUDIO_SET_MUTE:
+ IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
+ itv->speed_mute_audio = iarg;
+ return 0;
+
+ case AUDIO_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
+ if (iarg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1);
+
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
+ if (iarg > AUDIO_STEREO_SWAPPED)
+ return -EINVAL;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1);
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static long ivtv_default(struct file *file, void *fh, bool valid_prio,
+ int cmd, void *arg)
+{
+ struct ivtv *itv = fh2id(fh)->itv;
+
+ if (!valid_prio) {
+ switch (cmd) {
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ case VIDEO_PLAY:
+ case VIDEO_STOP:
+ case VIDEO_FREEZE:
+ case VIDEO_CONTINUE:
+ case VIDEO_COMMAND:
+ case VIDEO_SELECT_SOURCE:
+ case AUDIO_SET_MUTE:
+ case AUDIO_CHANNEL_SELECT:
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ return -EBUSY;
+ }
+ }
+
+ switch (cmd) {
+ case VIDIOC_INT_RESET: {
+ u32 val = *(u32 *)arg;
+
+ if ((val == 0 && itv->options.newi2c) || (val & 0x01))
+ ivtv_reset_ir_gpio(itv);
+ if (val & 0x02)
+ v4l2_subdev_call(itv->sd_video, core, reset, 0);
+ break;
+ }
+
+ case IVTV_IOC_DMA_FRAME:
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ case VIDEO_GET_PTS:
+ case VIDEO_GET_FRAME_COUNT:
+ case VIDEO_GET_EVENT:
+ case VIDEO_PLAY:
+ case VIDEO_STOP:
+ case VIDEO_FREEZE:
+ case VIDEO_CONTINUE:
+ case VIDEO_COMMAND:
+ case VIDEO_TRY_COMMAND:
+ case VIDEO_SELECT_SOURCE:
+ case AUDIO_SET_MUTE:
+ case AUDIO_CHANNEL_SELECT:
+ case AUDIO_BILINGUAL_CHANNEL_SELECT:
+ return ivtv_decoder_ioctls(file, cmd, (void *)arg);
+
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
+ .vidioc_querycap = ivtv_querycap,
+ .vidioc_s_audio = ivtv_s_audio,
+ .vidioc_g_audio = ivtv_g_audio,
+ .vidioc_enumaudio = ivtv_enumaudio,
+ .vidioc_s_audout = ivtv_s_audout,
+ .vidioc_g_audout = ivtv_g_audout,
+ .vidioc_enum_input = ivtv_enum_input,
+ .vidioc_enum_output = ivtv_enum_output,
+ .vidioc_enumaudout = ivtv_enumaudout,
+ .vidioc_cropcap = ivtv_cropcap,
+ .vidioc_s_crop = ivtv_s_crop,
+ .vidioc_g_crop = ivtv_g_crop,
+ .vidioc_g_input = ivtv_g_input,
+ .vidioc_s_input = ivtv_s_input,
+ .vidioc_g_output = ivtv_g_output,
+ .vidioc_s_output = ivtv_s_output,
+ .vidioc_g_frequency = ivtv_g_frequency,
+ .vidioc_s_frequency = ivtv_s_frequency,
+ .vidioc_s_tuner = ivtv_s_tuner,
+ .vidioc_g_tuner = ivtv_g_tuner,
+ .vidioc_g_enc_index = ivtv_g_enc_index,
+ .vidioc_g_fbuf = ivtv_g_fbuf,
+ .vidioc_s_fbuf = ivtv_s_fbuf,
+ .vidioc_g_std = ivtv_g_std,
+ .vidioc_s_std = ivtv_s_std,
+ .vidioc_overlay = ivtv_overlay,
+ .vidioc_log_status = ivtv_log_status,
+ .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap,
+ .vidioc_encoder_cmd = ivtv_encoder_cmd,
+ .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd,
+ .vidioc_decoder_cmd = ivtv_decoder_cmd,
+ .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd,
+ .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap,
+ .vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap,
+ .vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out,
+ .vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay,
+ .vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out,
+ .vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap,
+ .vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap,
+ .vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out,
+ .vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay,
+ .vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out,
+ .vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap,
+ .vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out,
+ .vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay,
+ .vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out,
+ .vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap,
+ .vidioc_g_chip_ident = ivtv_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = ivtv_g_register,
+ .vidioc_s_register = ivtv_s_register,
+#endif
+ .vidioc_default = ivtv_default,
+ .vidioc_subscribe_event = ivtv_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+void ivtv_set_funcs(struct video_device *vdev)
+{
+ vdev->ioctl_ops = &ivtv_ioctl_ops;
+}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/pci/ivtv/ivtv-ioctl.h
index 7c553d16579b..7c553d16579b 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.h
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.h
diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c
new file mode 100644
index 000000000000..19a7c9b990a3
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-irq.c
@@ -0,0 +1,1088 @@
+/* interrupt handling
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-queue.h"
+#include "ivtv-udma.h"
+#include "ivtv-irq.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-vbi.h"
+#include "ivtv-yuv.h"
+#include <media/v4l2-event.h>
+
+#define DMA_MAGIC_COOKIE 0x000001fe
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s);
+
+static const int ivtv_stream_map[] = {
+ IVTV_ENC_STREAM_TYPE_MPG,
+ IVTV_ENC_STREAM_TYPE_YUV,
+ IVTV_ENC_STREAM_TYPE_PCM,
+ IVTV_ENC_STREAM_TYPE_VBI,
+};
+
+static void ivtv_pcm_work_handler(struct ivtv *itv)
+{
+ struct ivtv_stream *s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM];
+ struct ivtv_buffer *buf;
+
+ /* Pass the PCM data to ivtv-alsa */
+
+ while (1) {
+ /*
+ * Users should not be using both the ALSA and V4L2 PCM audio
+ * capture interfaces at the same time. If the user is doing
+ * this, there maybe a buffer in q_io to grab, use, and put
+ * back in rotation.
+ */
+ buf = ivtv_dequeue(s, &s->q_io);
+ if (buf == NULL)
+ buf = ivtv_dequeue(s, &s->q_full);
+ if (buf == NULL)
+ break;
+
+ if (buf->readpos < buf->bytesused)
+ itv->pcm_announce_callback(itv->alsa,
+ (u8 *)(buf->buf + buf->readpos),
+ (size_t)(buf->bytesused - buf->readpos));
+
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+}
+
+static void ivtv_pio_work_handler(struct ivtv *itv)
+{
+ struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
+ struct ivtv_buffer *buf;
+ int i = 0;
+
+ IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
+ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
+ s->vdev == NULL || !ivtv_use_pio(s)) {
+ itv->cur_pio_stream = -1;
+ /* trigger PIO complete user interrupt */
+ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+ return;
+ }
+ IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name);
+ list_for_each_entry(buf, &s->q_dma.list, list) {
+ u32 size = s->sg_processing[i].size & 0x3ffff;
+
+ /* Copy the data from the card to the buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size);
+ }
+ else {
+ memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size);
+ }
+ i++;
+ if (i == s->sg_processing_size)
+ break;
+ }
+ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+}
+
+void ivtv_irq_work_handler(struct kthread_work *work)
+{
+ struct ivtv *itv = container_of(work, struct ivtv, irq_work);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
+ ivtv_pio_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
+ ivtv_vbi_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
+ ivtv_yuv_work_handler(itv);
+
+ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags))
+ ivtv_pcm_work_handler(itv);
+}
+
+/* Determine the required DMA size, setup enough buffers in the predma queue and
+ actually copy the data from the card to the buffers in case a PIO transfer is
+ required for this stream.
+ */
+static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf;
+ u32 bytes_needed = 0;
+ u32 offset, size;
+ u32 UVoffset = 0, UVsize = 0;
+ int skip_bufs = s->q_predma.buffers;
+ int idx = s->sg_pending_size;
+ int rc;
+
+ /* sanity checks */
+ if (s->vdev == NULL) {
+ IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+ return -1;
+ }
+ if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
+ IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
+ return -1;
+ }
+
+ /* determine offset, size and PTS for the various streams */
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ offset = data[1];
+ size = data[2];
+ s->pending_pts = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ offset = data[1];
+ size = data[2];
+ UVoffset = data[3];
+ UVsize = data[4];
+ s->pending_pts = ((u64) data[5] << 32) | data[6];
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ offset = data[1] + 12;
+ size = data[2] - 12;
+ s->pending_pts = read_dec(offset - 8) |
+ ((u64)(read_dec(offset - 12)) << 32);
+ if (itv->has_cx23415)
+ offset += IVTV_DECODER_OFFSET;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ size = itv->vbi.enc_size * itv->vbi.fpi;
+ offset = read_enc(itv->vbi.enc_start - 4) + 12;
+ if (offset == 12) {
+ IVTV_DEBUG_INFO("VBI offset == 0\n");
+ return -1;
+ }
+ s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
+ break;
+
+ case IVTV_DEC_STREAM_TYPE_VBI:
+ size = read_dec(itv->vbi.dec_start + 4) + 8;
+ offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
+ s->pending_pts = 0;
+ offset += IVTV_DECODER_OFFSET;
+ break;
+ default:
+ /* shouldn't happen */
+ return -1;
+ }
+
+ /* if this is the start of the DMA then fill in the magic cookie */
+ if (s->sg_pending_size == 0 && ivtv_use_dma(s)) {
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
+ write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ s->pending_backup = read_enc(offset);
+ write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
+ }
+ s->pending_offset = offset;
+ }
+
+ bytes_needed = size;
+ if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ /* The size for the Y samples needs to be rounded upwards to a
+ multiple of the buf_size. The UV samples then start in the
+ next buffer. */
+ bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
+ bytes_needed += UVsize;
+ }
+
+ IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
+ ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
+
+ rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
+ if (rc < 0) { /* Insufficient buffers */
+ IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
+ bytes_needed, s->name);
+ return -1;
+ }
+ if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) {
+ IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
+ IVTV_WARN("Cause: the application is not reading fast enough.\n");
+ }
+ s->buffers_stolen = rc;
+
+ /* got the buffers, now fill in sg_pending */
+ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
+ memset(buf->buf, 0, 128);
+ list_for_each_entry(buf, &s->q_predma.list, list) {
+ if (skip_bufs-- > 0)
+ continue;
+ s->sg_pending[idx].dst = buf->dma_handle;
+ s->sg_pending[idx].src = offset;
+ s->sg_pending[idx].size = s->buf_size;
+ buf->bytesused = min(size, s->buf_size);
+ buf->dma_xfer_cnt = s->dma_xfer_cnt;
+
+ s->q_predma.bytesused += buf->bytesused;
+ size -= buf->bytesused;
+ offset += s->buf_size;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+
+ if (size == 0) { /* YUV */
+ /* process the UV section */
+ offset = UVoffset;
+ size = UVsize;
+ }
+ idx++;
+ }
+ s->sg_pending_size = idx;
+ return 0;
+}
+
+static void dma_post(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_buffer *buf = NULL;
+ struct list_head *p;
+ u32 offset;
+ __le32 *u32buf;
+ int x = 0;
+
+ IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
+ s->name, s->dma_offset);
+ list_for_each(p, &s->q_dma.list) {
+ buf = list_entry(p, struct ivtv_buffer, list);
+ u32buf = (__le32 *)buf->buf;
+
+ /* Sync Buffer */
+ ivtv_buf_sync_for_cpu(s, buf);
+
+ if (x == 0 && ivtv_use_dma(s)) {
+ offset = s->dma_last_offset;
+ if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
+ {
+ for (offset = 0; offset < 64; offset++) {
+ if (u32buf[offset] == DMA_MAGIC_COOKIE) {
+ break;
+ }
+ }
+ offset *= 4;
+ if (offset == 256) {
+ IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
+ offset = s->dma_last_offset;
+ }
+ if (s->dma_last_offset != offset)
+ IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
+ s->dma_last_offset = offset;
+ }
+ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
+ s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
+ write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
+ }
+ else {
+ write_enc_sync(0, s->dma_offset);
+ }
+ if (offset) {
+ buf->bytesused -= offset;
+ memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
+ }
+ *u32buf = cpu_to_le32(s->dma_backup);
+ }
+ x++;
+ /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
+ s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP;
+ }
+ if (buf)
+ buf->bytesused += s->dma_last_offset;
+ if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ list_for_each_entry(buf, &s->q_dma.list, list) {
+ /* Parse and Groom VBI Data */
+ s->q_dma.bytesused -= buf->bytesused;
+ ivtv_process_vbi_data(itv, buf, 0, s->type);
+ s->q_dma.bytesused += buf->bytesused;
+ }
+ if (s->fh == NULL) {
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
+ return;
+ }
+ }
+
+ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_PCM &&
+ itv->pcm_announce_callback != NULL) {
+ /*
+ * Set up the work handler to pass the data to ivtv-alsa.
+ *
+ * We just use q_full and let the work handler race with users
+ * making ivtv-fileops.c calls on the PCM device node.
+ *
+ * Users should not be using both the ALSA and V4L2 PCM audio
+ * capture interfaces at the same time. If the user does this,
+ * fragments of data will just go out each interface as they
+ * race for PCM data.
+ */
+ set_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ }
+
+ if (s->fh)
+ wake_up(&s->waitq);
+}
+
+void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
+{
+ struct ivtv *itv = s->itv;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ u8 frame = yi->draw_frame;
+ struct yuv_frame_info *f = &yi->new_frame_info[frame];
+ struct ivtv_buffer *buf;
+ u32 y_size = 720 * ((f->src_h + 31) & ~31);
+ u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
+ int y_done = 0;
+ int bytes_written = 0;
+ unsigned long flags = 0;
+ int idx = 0;
+
+ IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
+
+ /* Insert buffer block for YUV if needed */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
+ if (yi->blanking_dmaptr) {
+ s->sg_pending[idx].src = yi->blanking_dmaptr;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = 720 * 16;
+ }
+ offset += 720 * 16;
+ idx++;
+ }
+
+ list_for_each_entry(buf, &s->q_predma.list, list) {
+ /* YUV UV Offset from Y Buffer */
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
+ (bytes_written + buf->bytesused) >= y_size) {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = y_size - bytes_written;
+ offset = uv_offset;
+ if (s->sg_pending[idx].size != buf->bytesused) {
+ idx++;
+ s->sg_pending[idx].src =
+ buf->dma_handle + s->sg_pending[idx - 1].size;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size =
+ buf->bytesused - s->sg_pending[idx - 1].size;
+ offset += s->sg_pending[idx].size;
+ }
+ y_done = 1;
+ } else {
+ s->sg_pending[idx].src = buf->dma_handle;
+ s->sg_pending[idx].dst = offset;
+ s->sg_pending[idx].size = buf->bytesused;
+ offset += buf->bytesused;
+ }
+ bytes_written += buf->bytesused;
+
+ /* Sync SG buffers */
+ ivtv_buf_sync_for_device(s, buf);
+ idx++;
+ }
+ s->sg_pending_size = idx;
+
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ if (lock)
+ spin_lock_irqsave(&itv->dma_reg_lock, flags);
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ ivtv_dma_dec_start(s);
+ }
+ else {
+ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+ if (lock)
+ spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
+}
+
+static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+ s->sg_processed++;
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+ add_timer(&itv->dma_timer);
+}
+
+static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
+ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
+ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
+ s->sg_processed++;
+ /* Sync Hardware SG List of buffers */
+ ivtv_stream_sync_for_device(s);
+ write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
+ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
+ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
+ add_timer(&itv->dma_timer);
+}
+
+/* start the encoder DMA */
+static void ivtv_dma_enc_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ int i;
+
+ IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+
+ if (ivtv_use_dma(s))
+ s->sg_pending[s->sg_pending_size - 1].size += 256;
+
+ /* If this is an MPEG stream, and VBI data is also pending, then append the
+ VBI DMA to the MPEG DMA and transfer both sets of data at once.
+
+ VBI DMA is a second class citizen compared to MPEG and mixing them together
+ will confuse the firmware (the end of a VBI DMA is seen as the end of a
+ MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
+ sure we only use the MPEG DMA to transfer the VBI DMA if both are in
+ use. This way no conflicts occur. */
+ clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&
+ s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {
+ ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
+ if (ivtv_use_dma(s_vbi))
+ s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;
+ for (i = 0; i < s_vbi->sg_pending_size; i++) {
+ s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];
+ }
+ s_vbi->dma_offset = s_vbi->pending_offset;
+ s_vbi->sg_pending_size = 0;
+ s_vbi->dma_xfer_cnt++;
+ set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
+ IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);
+ }
+
+ s->dma_xfer_cnt++;
+ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+ s->sg_processing_size = s->sg_pending_size;
+ s->sg_pending_size = 0;
+ s->sg_processed = 0;
+ s->dma_offset = s->pending_offset;
+ s->dma_backup = s->pending_backup;
+ s->dma_pts = s->pending_pts;
+
+ if (ivtv_use_pio(s)) {
+ set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ set_bit(IVTV_F_I_PIO, &itv->i_flags);
+ itv->cur_pio_stream = s->type;
+ }
+ else {
+ itv->dma_retries = 0;
+ ivtv_dma_enc_start_xfer(s);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+ }
+}
+
+static void ivtv_dma_dec_start(struct ivtv_stream *s)
+{
+ struct ivtv *itv = s->itv;
+
+ if (s->q_predma.bytesused)
+ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
+ s->dma_xfer_cnt++;
+ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
+ s->sg_processing_size = s->sg_pending_size;
+ s->sg_pending_size = 0;
+ s->sg_processed = 0;
+
+ IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);
+ itv->dma_retries = 0;
+ ivtv_dma_dec_start_xfer(s);
+ set_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = s->type;
+}
+
+static void ivtv_irq_dma_read(struct ivtv *itv)
+{
+ struct ivtv_stream *s = NULL;
+ struct ivtv_buffer *buf;
+ int hw_stream_type = 0;
+
+ IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");
+
+ del_timer(&itv->dma_timer);
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)
+ return;
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ s = &itv->streams[itv->cur_dma_stream];
+ ivtv_stream_sync_for_cpu(s);
+
+ if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
+ IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",
+ read_reg(IVTV_REG_DMASTATUS),
+ s->sg_processed, s->sg_processing_size, itv->dma_retries);
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ if (itv->dma_retries == 3) {
+ /* Too many retries, give up on this frame */
+ itv->dma_retries = 0;
+ s->sg_processed = s->sg_processing_size;
+ }
+ else {
+ /* Retry, starting with the first xfer segment.
+ Just retrying the current segment is not sufficient. */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ }
+ }
+ if (s->sg_processed < s->sg_processing_size) {
+ /* DMA next buffer */
+ ivtv_dma_dec_start_xfer(s);
+ return;
+ }
+ if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
+ hw_stream_type = 2;
+ IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
+
+ /* For some reason must kick the firmware, like PIO mode,
+ I think this tells the firmware we are done and the size
+ of the xfer so it can calculate what we need next.
+ I think we can do this part ourselves but would have to
+ fully calculate xfer info ourselves and not use interrupts
+ */
+ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
+ hw_stream_type);
+
+ /* Free last DMA call */
+ while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
+ ivtv_buf_sync_for_cpu(s, buf);
+ ivtv_enqueue(s, buf, &s->q_free);
+ }
+ wake_up(&s->waitq);
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+ IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
+
+ del_timer(&itv->dma_timer);
+
+ if (itv->cur_dma_stream < 0)
+ return;
+
+ s = &itv->streams[itv->cur_dma_stream];
+ ivtv_stream_sync_for_cpu(s);
+
+ if (data[0] & 0x18) {
+ IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],
+ s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ if (itv->dma_retries == 3) {
+ /* Too many retries, give up on this frame */
+ itv->dma_retries = 0;
+ s->sg_processed = s->sg_processing_size;
+ }
+ else {
+ /* Retry, starting with the first xfer segment.
+ Just retrying the current segment is not sufficient. */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ }
+ }
+ if (s->sg_processed < s->sg_processing_size) {
+ /* DMA next buffer */
+ ivtv_dma_enc_start_xfer(s);
+ return;
+ }
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ dma_post(s);
+ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ dma_post(s);
+ }
+ s->sg_processing_size = 0;
+ s->sg_processed = 0;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
+{
+ struct ivtv_stream *s;
+
+ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
+ itv->cur_pio_stream = -1;
+ return;
+ }
+ s = &itv->streams[itv->cur_pio_stream];
+ IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
+ clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+ itv->cur_pio_stream = -1;
+ dma_post(s);
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
+ else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
+ else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
+ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
+ clear_bit(IVTV_F_I_PIO, &itv->i_flags);
+ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+ dma_post(s);
+ }
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_dma_err(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ u32 status;
+
+ del_timer(&itv->dma_timer);
+
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
+ status = read_reg(IVTV_REG_DMASTATUS);
+ IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
+ status, itv->cur_dma_stream);
+ /*
+ * We do *not* write back to the IVTV_REG_DMASTATUS register to
+ * clear the error status, if either the encoder write (0x02) or
+ * decoder read (0x01) bus master DMA operation do not indicate
+ * completed. We can race with the DMA engine, which may have
+ * transitioned to completed status *after* we read the register.
+ * Setting a IVTV_REG_DMASTATUS flag back to "busy" status, after the
+ * DMA engine has completed, will cause the DMA engine to stop working.
+ */
+ status &= 0x3;
+ if (status == 0x3)
+ write_reg(status, IVTV_REG_DMASTATUS);
+
+ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
+ itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
+ struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
+
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
+ /* retry */
+ /*
+ * FIXME - handle cases of DMA error similar to
+ * encoder below, except conditioned on status & 0x1
+ */
+ ivtv_dma_dec_start(s);
+ return;
+ } else {
+ if ((status & 0x2) == 0) {
+ /*
+ * CX2341x Bus Master DMA write is ongoing.
+ * Reset the timer and let it complete.
+ */
+ itv->dma_timer.expires =
+ jiffies + msecs_to_jiffies(600);
+ add_timer(&itv->dma_timer);
+ return;
+ }
+
+ if (itv->dma_retries < 3) {
+ /*
+ * CX2341x Bus Master DMA write has ended.
+ * Retry the write, starting with the first
+ * xfer segment. Just retrying the current
+ * segment is not sufficient.
+ */
+ s->sg_processed = 0;
+ itv->dma_retries++;
+ ivtv_dma_enc_start_xfer(s);
+ return;
+ }
+ /* Too many retries, give up on this one */
+ }
+
+ }
+ if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
+ ivtv_udma_start(itv);
+ return;
+ }
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
+
+static void ivtv_irq_enc_start_cap(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* Get DMA destination and size arguments from card */
+ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data);
+ IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
+
+ if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
+ IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
+ data[0], data[1], data[2]);
+ return;
+ }
+ s = &itv->streams[ivtv_stream_map[data[0]]];
+ if (!stream_enc_dma_append(s, data)) {
+ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+ }
+}
+
+static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n");
+ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
+
+ if (!stream_enc_dma_append(s, data))
+ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
+}
+
+static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
+
+ IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n");
+ if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
+ !stream_enc_dma_append(s, data)) {
+ set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
+ }
+}
+
+static void ivtv_irq_dec_data_req(struct ivtv *itv)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv_stream *s;
+
+ /* YUV or MPG */
+
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
+ itv->dma_data_req_size =
+ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
+ itv->dma_data_req_offset = data[1];
+ if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
+ ivtv_yuv_frame_complete(itv);
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+ }
+ else {
+ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data);
+ itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
+ itv->dma_data_req_offset = data[1];
+ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
+ }
+ IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
+ itv->dma_data_req_offset, itv->dma_data_req_size);
+ if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
+ set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ }
+ else {
+ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
+ ivtv_yuv_setup_stream_frame(itv);
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
+ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
+ }
+}
+
+static void ivtv_irq_vsync(struct ivtv *itv)
+{
+ /* The vsync interrupt is unusual in that it won't clear until
+ * the end of the first line for the current field, at which
+ * point it clears itself. This can result in repeated vsync
+ * interrupts, or a missed vsync. Read some of the registers
+ * to determine the line being displayed and ensure we handle
+ * one vsync per frame.
+ */
+ unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1;
+ struct yuv_playback_info *yi = &itv->yuv_info;
+ int last_dma_frame = atomic_read(&yi->next_dma_frame);
+ struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
+
+ if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
+
+ if (((frame ^ f->sync_field) == 0 &&
+ ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
+ (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
+ int next_dma_frame = last_dma_frame;
+
+ if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
+ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
+ write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
+ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
+ next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
+ atomic_set(&yi->next_dma_frame, next_dma_frame);
+ yi->fields_lapsed = -1;
+ yi->running = 1;
+ }
+ }
+ }
+ if (frame != (itv->last_vsync_field & 1)) {
+ static const struct v4l2_event evtop = {
+ .type = V4L2_EVENT_VSYNC,
+ .u.vsync.field = V4L2_FIELD_TOP,
+ };
+ static const struct v4l2_event evbottom = {
+ .type = V4L2_EVENT_VSYNC,
+ .u.vsync.field = V4L2_FIELD_BOTTOM,
+ };
+ struct ivtv_stream *s = ivtv_get_output_stream(itv);
+
+ itv->last_vsync_field += 1;
+ if (frame == 0) {
+ clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ else {
+ set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
+ }
+ if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
+ set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+ if (s)
+ wake_up(&s->waitq);
+ }
+ if (s && s->vdev)
+ v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom);
+ wake_up(&itv->vsync_waitq);
+
+ /* Send VBI to saa7127 */
+ if (frame && (itv->output_mode == OUT_PASSTHROUGH ||
+ test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) ||
+ test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) {
+ set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ }
+
+ /* Check if we need to update the yuv registers */
+ if (yi->running && (yi->yuv_forced_update || f->update)) {
+ if (!f->update) {
+ last_dma_frame =
+ (u8)(atomic_read(&yi->next_dma_frame) -
+ 1) % IVTV_YUV_BUFFERS;
+ f = &yi->new_frame_info[last_dma_frame];
+ }
+
+ if (f->src_w) {
+ yi->update_frame = last_dma_frame;
+ f->update = 0;
+ yi->yuv_forced_update = 0;
+ set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
+ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
+ }
+ }
+
+ yi->fields_lapsed++;
+ }
+}
+
+#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT)
+
+irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
+{
+ struct ivtv *itv = (struct ivtv *)dev_id;
+ u32 combo;
+ u32 stat;
+ int i;
+ u8 vsync_force = 0;
+
+ spin_lock(&itv->dma_reg_lock);
+ /* get contents of irq status register */
+ stat = read_reg(IVTV_REG_IRQSTATUS);
+
+ combo = ~itv->irqmask & stat;
+
+ /* Clear out IRQ */
+ if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
+
+ if (0 == combo) {
+ /* The vsync interrupt is unusual and clears itself. If we
+ * took too long, we may have missed it. Do some checks
+ */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ /* vsync is enabled, see if we're in a new field */
+ if ((itv->last_vsync_field & 1) !=
+ (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) {
+ /* New field, looks like we missed it */
+ IVTV_DEBUG_YUV("VSync interrupt missed %d\n",
+ read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16);
+ vsync_force = 1;
+ }
+ }
+
+ if (!vsync_force) {
+ /* No Vsync expected, wasn't for us */
+ spin_unlock(&itv->dma_reg_lock);
+ return IRQ_NONE;
+ }
+ }
+
+ /* Exclude interrupts noted below from the output, otherwise the log is flooded with
+ these messages */
+ if (combo & ~0xff6d0400)
+ IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
+
+ if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
+ IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n");
+ }
+
+ if (combo & IVTV_IRQ_DMA_READ) {
+ ivtv_irq_dma_read(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
+ ivtv_irq_enc_dma_complete(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
+ ivtv_irq_enc_pio_complete(itv);
+ }
+
+ if (combo & IVTV_IRQ_DMA_ERR) {
+ ivtv_irq_dma_err(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_START_CAP) {
+ ivtv_irq_enc_start_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VBI_CAP) {
+ ivtv_irq_enc_vbi_cap(itv);
+ }
+
+ if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
+ ivtv_irq_dec_vbi_reinsert(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_EOS) {
+ IVTV_DEBUG_IRQ("ENC EOS\n");
+ set_bit(IVTV_F_I_EOS, &itv->i_flags);
+ wake_up(&itv->eos_waitq);
+ }
+
+ if (combo & IVTV_IRQ_DEC_DATA_REQ) {
+ ivtv_irq_dec_data_req(itv);
+ }
+
+ /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
+ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
+ ivtv_irq_vsync(itv);
+ }
+
+ if (combo & IVTV_IRQ_ENC_VIM_RST) {
+ IVTV_DEBUG_IRQ("VIM RST\n");
+ /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
+ }
+
+ if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
+ IVTV_DEBUG_INFO("Stereo mode changed\n");
+ }
+
+ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
+ itv->irq_rr_idx++;
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+ struct ivtv_stream *s = &itv->streams[idx];
+
+ if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
+ continue;
+ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_dec_start(s);
+ else
+ ivtv_dma_enc_start(s);
+ break;
+ }
+
+ if (i == IVTV_MAX_STREAMS &&
+ test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
+ ivtv_udma_start(itv);
+ }
+
+ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
+ itv->irq_rr_idx++;
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
+ struct ivtv_stream *s = &itv->streams[idx];
+
+ if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
+ continue;
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
+ ivtv_dma_enc_start(s);
+ break;
+ }
+ }
+
+ if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
+ queue_kthread_work(&itv->irq_worker, &itv->irq_work);
+ }
+
+ spin_unlock(&itv->dma_reg_lock);
+
+ /* If we've just handled a 'forced' vsync, it's safest to say it
+ * wasn't ours. Another device may have triggered it at just
+ * the right time.
+ */
+ return vsync_force ? IRQ_NONE : IRQ_HANDLED;
+}
+
+void ivtv_unfinished_dma(unsigned long arg)
+{
+ struct ivtv *itv = (struct ivtv *)arg;
+
+ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
+ return;
+ IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
+
+ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
+ clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
+ clear_bit(IVTV_F_I_DMA, &itv->i_flags);
+ itv->cur_dma_stream = -1;
+ wake_up(&itv->dma_waitq);
+}
diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/pci/ivtv/ivtv-irq.h
index 1e84433737cc..1e84433737cc 100644
--- a/drivers/media/video/ivtv/ivtv-irq.h
+++ b/drivers/media/pci/ivtv/ivtv-irq.h
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/pci/ivtv/ivtv-mailbox.c
index e3ce96763785..e3ce96763785 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.c
+++ b/drivers/media/pci/ivtv/ivtv-mailbox.c
diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/pci/ivtv/ivtv-mailbox.h
index 2c834d2cb56f..2c834d2cb56f 100644
--- a/drivers/media/video/ivtv/ivtv-mailbox.h
+++ b/drivers/media/pci/ivtv/ivtv-mailbox.h
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/pci/ivtv/ivtv-queue.c
index 7fde36e6d227..7fde36e6d227 100644
--- a/drivers/media/video/ivtv/ivtv-queue.c
+++ b/drivers/media/pci/ivtv/ivtv-queue.c
diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/pci/ivtv/ivtv-queue.h
index 91233839a26c..91233839a26c 100644
--- a/drivers/media/video/ivtv/ivtv-queue.h
+++ b/drivers/media/pci/ivtv/ivtv-queue.h
diff --git a/drivers/media/video/ivtv/ivtv-routing.c b/drivers/media/pci/ivtv/ivtv-routing.c
index 8898c569a1c9..8898c569a1c9 100644
--- a/drivers/media/video/ivtv/ivtv-routing.c
+++ b/drivers/media/pci/ivtv/ivtv-routing.c
diff --git a/drivers/media/video/ivtv/ivtv-routing.h b/drivers/media/pci/ivtv/ivtv-routing.h
index c72a9731ca01..c72a9731ca01 100644
--- a/drivers/media/video/ivtv/ivtv-routing.h
+++ b/drivers/media/pci/ivtv/ivtv-routing.h
diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c
new file mode 100644
index 000000000000..70dad588a677
--- /dev/null
+++ b/drivers/media/pci/ivtv/ivtv-streams.c
@@ -0,0 +1,1039 @@
+/*
+ init/start/stop/exit stream functions
+ Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
+ Copyright (C) 2004 Chris Kennedy <c@groovy.org>
+ Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* License: GPL
+ * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
+ *
+ * This file will hold API related functions, both internal (firmware api)
+ * and external (v4l2, etc)
+ *
+ * -----
+ * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
+ * and Takeru KOMORIYA<komoriya@paken.org>
+ *
+ * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
+ * using information provided by Jiun-Kuei Jung @ AVerMedia.
+ */
+
+#include "ivtv-driver.h"
+#include "ivtv-fileops.h"
+#include "ivtv-queue.h"
+#include "ivtv-mailbox.h"
+#include "ivtv-ioctl.h"
+#include "ivtv-irq.h"
+#include "ivtv-yuv.h"
+#include "ivtv-cards.h"
+#include "ivtv-streams.h"
+#include "ivtv-firmware.h"
+#include <media/v4l2-event.h>
+
+static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .unlocked_ioctl = video_ioctl2,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_enc_poll,
+};
+
+static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
+ .owner = THIS_MODULE,
+ .read = ivtv_v4l2_read,
+ .write = ivtv_v4l2_write,
+ .open = ivtv_v4l2_open,
+ .unlocked_ioctl = video_ioctl2,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_dec_poll,
+};
+
+static const struct v4l2_file_operations ivtv_v4l2_radio_fops = {
+ .owner = THIS_MODULE,
+ .open = ivtv_v4l2_open,
+ .unlocked_ioctl = video_ioctl2,
+ .release = ivtv_v4l2_close,
+ .poll = ivtv_v4l2_enc_poll,
+};
+
+#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */
+#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */
+#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */
+#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */
+#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */
+
+static struct {
+ const char *name;
+ int vfl_type;
+ int num_offset;
+ int dma, pio;
+ u32 v4l2_caps;
+ const struct v4l2_file_operations *fops;
+} ivtv_stream_info[] = {
+ { /* IVTV_ENC_STREAM_TYPE_MPG */
+ "encoder MPG",
+ VFL_TYPE_GRABBER, 0,
+ PCI_DMA_FROMDEVICE, 0,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_YUV */
+ "encoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
+ PCI_DMA_FROMDEVICE, 0,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_VBI */
+ "encoder VBI",
+ VFL_TYPE_VBI, 0,
+ PCI_DMA_FROMDEVICE, 0,
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_PCM */
+ "encoder PCM",
+ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
+ PCI_DMA_FROMDEVICE, 0,
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_ENC_STREAM_TYPE_RAD */
+ "encoder radio",
+ VFL_TYPE_RADIO, 0,
+ PCI_DMA_NONE, 1,
+ V4L2_CAP_RADIO | V4L2_CAP_TUNER,
+ &ivtv_v4l2_radio_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_MPG */
+ "decoder MPG",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
+ PCI_DMA_TODEVICE, 0,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VBI */
+ "decoder VBI",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
+ PCI_DMA_NONE, 1,
+ V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_enc_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_VOUT */
+ "decoder VOUT",
+ VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
+ PCI_DMA_NONE, 1,
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ },
+ { /* IVTV_DEC_STREAM_TYPE_YUV */
+ "decoder YUV",
+ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
+ PCI_DMA_TODEVICE, 0,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
+ &ivtv_v4l2_dec_fops
+ }
+};
+
+static void ivtv_stream_init(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ struct video_device *vdev = s->vdev;
+
+ /* we need to keep vdev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+ s->vdev = vdev;
+
+ /* initialize ivtv_stream fields */
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+ s->caps = ivtv_stream_info[type].v4l2_caps;
+
+ if (ivtv_stream_info[type].pio)
+ s->dma = PCI_DMA_NONE;
+ else
+ s->dma = ivtv_stream_info[type].dma;
+ s->buf_size = itv->stream_buf_size[type];
+ if (s->buf_size)
+ s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size;
+ spin_lock_init(&s->qlock);
+ init_waitqueue_head(&s->waitq);
+ s->sg_handle = IVTV_DMA_UNMAPPED;
+ ivtv_queue_init(&s->q_free);
+ ivtv_queue_init(&s->q_full);
+ ivtv_queue_init(&s->q_dma);
+ ivtv_queue_init(&s->q_predma);
+ ivtv_queue_init(&s->q_io);
+}
+
+static int ivtv_prep_dev(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ int num_offset = ivtv_stream_info[type].num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
+
+ /* These four fields are always initialized. If vdev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+ s->vdev = NULL;
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+
+ /* Check whether the radio is supported */
+ if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
+ return 0;
+ if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return 0;
+
+ /* User explicitly selected 0 buffers for these streams, so don't
+ create them. */
+ if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
+ itv->options.kilobytes[type] == 0) {
+ IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
+ return 0;
+ }
+
+ ivtv_stream_init(itv, type);
+
+ /* allocate and initialize the v4l2 video device structure */
+ s->vdev = video_device_alloc();
+ if (s->vdev == NULL) {
+ IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+ return -ENOMEM;
+ }
+
+ snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
+ itv->v4l2_dev.name, s->name);
+
+ s->vdev->num = num;
+ s->vdev->v4l2_dev = &itv->v4l2_dev;
+ if (ivtv_stream_info[type].v4l2_caps &
+ (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT))
+ s->vdev->vfl_dir = VFL_DIR_TX;
+ s->vdev->fops = ivtv_stream_info[type].fops;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
+ s->vdev->release = video_device_release;
+ s->vdev->tvnorms = V4L2_STD_ALL;
+ s->vdev->lock = &itv->serialize_lock;
+ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
+ v4l2_disable_ioctl(s->vdev, VIDIOC_S_AUDIO);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_G_AUDIO);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMAUDIO);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMINPUT);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_S_INPUT);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_G_INPUT);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_S_FREQUENCY);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_G_FREQUENCY);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_S_TUNER);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_G_TUNER);
+ v4l2_disable_ioctl(s->vdev, VIDIOC_S_STD);
+ }
+ set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags);
+ ivtv_set_funcs(s->vdev);
+ return 0;
+}
+
+/* Initialize v4l2 variables and prepare v4l2 devices */
+int ivtv_streams_setup(struct ivtv *itv)
+{
+ int type;
+
+ /* Setup V4L2 Devices */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ /* Prepare device */
+ if (ivtv_prep_dev(itv, type))
+ break;
+
+ if (itv->streams[type].vdev == NULL)
+ continue;
+
+ /* Allocate Stream */
+ if (ivtv_stream_alloc(&itv->streams[type]))
+ break;
+ }
+ if (type == IVTV_MAX_STREAMS)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ ivtv_streams_cleanup(itv, 0);
+ return -ENOMEM;
+}
+
+static int ivtv_reg_dev(struct ivtv *itv, int type)
+{
+ struct ivtv_stream *s = &itv->streams[type];
+ int vfl_type = ivtv_stream_info[type].vfl_type;
+ const char *name;
+ int num;
+
+ if (s->vdev == NULL)
+ return 0;
+
+ num = s->vdev->num;
+ /* card number + user defined offset + device offset */
+ if (type != IVTV_ENC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+
+ if (s_mpg->vdev)
+ num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
+ }
+ video_set_drvdata(s->vdev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+ if (video_register_device_no_warn(s->vdev, vfl_type, num)) {
+ IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
+ s->name, num);
+ video_device_release(s->vdev);
+ s->vdev = NULL;
+ return -ENOMEM;
+ }
+ name = video_device_node_name(s->vdev);
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
+ break;
+ case VFL_TYPE_RADIO:
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ case VFL_TYPE_VBI:
+ if (itv->options.kilobytes[type])
+ IVTV_INFO("Registered device %s for %s (%d kB)\n",
+ name, s->name, itv->options.kilobytes[type]);
+ else
+ IVTV_INFO("Registered device %s for %s\n",
+ name, s->name);
+ break;
+ }
+ return 0;
+}
+
+/* Register v4l2 devices */
+int ivtv_streams_register(struct ivtv *itv)
+{
+ int type;
+ int err = 0;
+
+ /* Register V4L2 devices */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++)
+ err |= ivtv_reg_dev(itv, type);
+
+ if (err == 0)
+ return 0;
+
+ /* One or more streams could not be initialized. Clean 'em all up. */
+ ivtv_streams_cleanup(itv, 1);
+ return -ENOMEM;
+}
+
+/* Unregister v4l2 devices */
+void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
+{
+ int type;
+
+ /* Teardown all streams */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+ struct video_device *vdev = itv->streams[type].vdev;
+
+ itv->streams[type].vdev = NULL;
+ if (vdev == NULL)
+ continue;
+
+ ivtv_stream_free(&itv->streams[type]);
+ /* Unregister or release device */
+ if (unregister)
+ video_unregister_device(vdev);
+ else
+ video_device_release(vdev);
+ }
+}
+
+static void ivtv_vbi_setup(struct ivtv *itv)
+{
+ int raw = ivtv_raw_vbi(itv);
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ int lines;
+ int i;
+
+ /* Reset VBI */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
+
+ /* setup VBI registers */
+ if (raw)
+ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi);
+ else
+ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced);
+
+ /* determine number of lines and total number of VBI bytes.
+ A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
+ The '- 1' byte is probably an unused U or V byte. Or something...
+ A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+ header, 42 data bytes + checksum (to be confirmed) */
+ if (raw) {
+ lines = itv->vbi.count * 2;
+ } else {
+ lines = itv->is_60hz ? 24 : 38;
+ if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
+ lines += 2;
+ }
+
+ itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+
+ /* Note: sliced vs raw flag doesn't seem to have any effect
+ TODO: check mode (0x02) value with older ivtv versions. */
+ data[0] = raw | 0x02 | (0xbd << 8);
+
+ /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
+ data[1] = 1;
+ /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
+ data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size);
+ /* The start/stop codes determine which VBI lines end up in the raw VBI data area.
+ The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
+ is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
+ code. These values for raw VBI are obtained from a driver disassembly. The sliced
+ start/stop codes was deduced from this, but they do not appear in the driver.
+ Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
+ However, I have no idea what these values are for. */
+ if (itv->hw_flags & IVTV_HW_CX25840) {
+ /* Setup VBI for the cx25840 digitizer */
+ if (raw) {
+ data[3] = 0x20602060;
+ data[4] = 0x30703070;
+ } else {
+ data[3] = 0xB0F0B0F0;
+ data[4] = 0xA0E0A0E0;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
+ } else {
+ /* Setup VBI for the saa7115 digitizer */
+ if (raw) {
+ data[3] = 0x25256262;
+ data[4] = 0x387F7F7F;
+ } else {
+ data[3] = 0xABABECEC;
+ data[4] = 0xB6F1F1F1;
+ }
+ /* Lines per frame */
+ data[5] = lines;
+ /* bytes per line */
+ data[6] = itv->vbi.enc_size / lines;
+ }
+
+ IVTV_DEBUG_INFO(
+ "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
+ data[0], data[1], data[2], data[5], data[6]);
+
+ ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
+
+ /* returns the VBI encoder memory area. */
+ itv->vbi.enc_start = data[2];
+ itv->vbi.fpi = data[0];
+ if (!itv->vbi.fpi)
+ itv->vbi.fpi = 1;
+
+ IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n",
+ itv->vbi.enc_start, data[1], itv->vbi.fpi);
+
+ /* select VBI lines.
+ Note that the sliced argument seems to have no effect. */
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ if (itv->is_60hz) {
+ valid = i >= 10 && i < 22;
+ } else {
+ valid = i >= 6 && i < 24;
+ }
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
+ valid, 0 , 0, 0);
+ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
+ valid, 0, 0, 0);
+ }
+
+ /* Remaining VBI questions:
+ - Is it possible to select particular VBI lines only for inclusion in the MPEG
+ stream? Currently you can only get the first X lines.
+ - Is mixed raw and sliced VBI possible?
+ - What's the meaning of the raw/sliced flag?
+ - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
+}
+
+int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int captype = 0, subtype = 0;
+ int enable_passthrough = 0;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ captype = 0;
+ subtype = 3;
+
+ /* Stop Passthrough */
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ ivtv_passthrough_mode(itv, 0);
+ enable_passthrough = 1;
+ }
+ itv->mpg_data_received = itv->vbi_data_inserted = 0;
+ itv->dualwatch_jiffies = jiffies;
+ itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
+ itv->search_pack_header = 0;
+ break;
+
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ captype = 2;
+ subtype = 11; /* video+audio+decoder */
+ break;
+ }
+ captype = 1;
+ subtype = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ captype = 1;
+ subtype = 2;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ captype = 1;
+ subtype = 4;
+
+ itv->vbi.frame = 0;
+ itv->vbi.inserted_frame = 0;
+ memset(itv->vbi.sliced_mpeg_size,
+ 0, sizeof(itv->vbi.sliced_mpeg_size));
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->subtype = subtype;
+ s->buffers_stolen = 0;
+
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ if (atomic_read(&itv->capturing) == 0) {
+ int digitizer;
+
+ /* Always use frame based mode. Experiments have demonstrated that byte
+ stream based mode results in dropped frames and corruption. Not often,
+ but occasionally. Many thanks go to Leonard Orb who spent a lot of
+ effort and time trying to trace the cause of the drop outs. */
+ /* 1 frame per DMA */
+ /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
+ ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
+
+ /* Stuff from Windows, we don't know what it is */
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
+ /* According to the docs, this should be correct. However, this is
+ untested. I don't dare enable this without having tested it.
+ Only very few old cards actually have this hardware combination.
+ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
+ ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
+ */
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
+ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
+
+ /* assign placeholder */
+ ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
+ digitizer = 0xF1;
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ digitizer = 0xEF;
+ else /* cx25840 */
+ digitizer = 0x140;
+
+ ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer);
+
+ /* Setup VBI */
+ if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
+ ivtv_vbi_setup(itv);
+ }
+
+ /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
+ ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
+ itv->pgm_info_offset = data[0];
+ itv->pgm_info_num = data[1];
+ itv->pgm_info_write_idx = 0;
+ itv->pgm_info_read_idx = 0;
+
+ IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
+ itv->pgm_info_offset, itv->pgm_info_num);
+
+ /* Setup API for Stream */
+ cx2341x_handler_setup(&itv->cxhdl);
+
+ /* mute if capturing radio */
+ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
+ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
+ 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
+ }
+
+ /* Vsync Setup */
+ if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* event notification (on) */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+
+ if (atomic_read(&itv->capturing) == 0) {
+ /* Clear all Pending Interrupts */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ clear_bit(IVTV_F_I_EOS, &itv->i_flags);
+
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+
+ /* Initialize Digitizer for Capture */
+ /* Avoid tinny audio problem - ensure audio clocks are going */
+ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
+ /* Avoid unpredictable PCI bus hang - disable video clocks */
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
+ ivtv_msleep_timeout(300, 0);
+ ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
+ }
+
+ /* begin_capture */
+ if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
+ {
+ IVTV_DEBUG_WARN( "Error starting capture!\n");
+ return -EINVAL;
+ }
+
+ /* Start Passthrough */
+ if (enable_passthrough) {
+ ivtv_passthrough_mode(itv, 1);
+ }
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+ else
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->capturing);
+ return 0;
+}
+EXPORT_SYMBOL(ivtv_start_v4l2_encode_stream);
+
+static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ struct ivtv *itv = s->itv;
+ int datatype;
+ u16 width;
+ u16 height;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+
+ width = itv->cxhdl.width;
+ height = itv->cxhdl.height;
+
+ /* set audio mode to left/stereo for dual/stereo mode. */
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+
+ /* set number of internal decoder buffers */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
+
+ /* prebuffering */
+ ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
+
+ /* extract from user packets */
+ ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
+ itv->vbi.dec_start = data[0];
+
+ IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
+ itv->vbi.dec_start, data[1]);
+
+ /* set decoder source settings */
+ /* Data type: 0 = mpeg from host,
+ 1 = yuv from encoder,
+ 2 = yuv_from_host */
+ switch (s->type) {
+ case IVTV_DEC_STREAM_TYPE_YUV:
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ datatype = 1;
+ } else {
+ /* Fake size to avoid switching video standard */
+ datatype = 2;
+ width = 720;
+ height = itv->is_out_50hz ? 576 : 480;
+ }
+ IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
+ break;
+ case IVTV_DEC_STREAM_TYPE_MPG:
+ default:
+ datatype = 0;
+ break;
+ }
+ if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
+ width, height, itv->cxhdl.audio_properties)) {
+ IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
+ }
+
+ /* Decoder sometimes dies here, so wait a moment */
+ ivtv_msleep_timeout(10, 0);
+
+ /* Known failure point for firmware, so check */
+ return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream");
+}
+
+int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+{
+ struct ivtv *itv = s->itv;
+ int rc;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0; /* already started */
+
+ IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
+
+ rc = ivtv_setup_v4l2_decode_stream(s);
+ if (rc < 0) {
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ return rc;
+ }
+
+ /* set dma size to 65536 bytes */
+ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
+
+ /* Clear Streamoff */
+ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+
+ /* Zero out decoder counters */
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
+ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
+
+ /* turn on notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ /* start playback */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
+
+ /* Let things settle before we actually start */
+ ivtv_msleep_timeout(10, 0);
+
+ /* Clear the following Interrupt mask bits for decoding */
+ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+ IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
+
+ /* you're live! sit back and await interrupts :) */
+ atomic_inc(&itv->decoding);
+ return 0;
+}
+
+void ivtv_stop_all_captures(struct ivtv *itv)
+{
+ int i;
+
+ for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+ if (s->vdev == NULL)
+ continue;
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s, 0);
+ }
+ }
+}
+
+int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+{
+ struct ivtv *itv = s->itv;
+ DECLARE_WAITQUEUE(wait, current);
+ int cap_type;
+ int stopmode;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+ and that we are actually capturing */
+
+ IVTV_DEBUG_INFO("Stop Capture\n");
+
+ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
+ return 0;
+ if (atomic_read(&itv->capturing) == 0)
+ return 0;
+
+ switch (s->type) {
+ case IVTV_ENC_STREAM_TYPE_YUV:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_PCM:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_VBI:
+ cap_type = 1;
+ break;
+ case IVTV_ENC_STREAM_TYPE_MPG:
+ default:
+ cap_type = 0;
+ break;
+ }
+
+ /* Stop Capture Mode */
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ stopmode = 0;
+ } else {
+ stopmode = 1;
+ }
+
+ /* end_capture */
+ /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
+
+ if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
+ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
+ /* only run these if we're shutting down the last cap */
+ unsigned long duration;
+ unsigned long then = jiffies;
+
+ add_wait_queue(&itv->eos_waitq, &wait);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* wait 2s for EOS interrupt */
+ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) &&
+ time_before(jiffies,
+ then + msecs_to_jiffies(2000))) {
+ schedule_timeout(msecs_to_jiffies(10));
+ }
+
+ /* To convert jiffies to ms, we must multiply by 1000
+ * and divide by HZ. To avoid runtime division, we
+ * convert this to multiplication by 1000/HZ.
+ * Since integer division truncates, we get the best
+ * accuracy if we do a rounding calculation of the constant.
+ * Think of the case where HZ is 1024.
+ */
+ duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
+
+ if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
+ IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
+ IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
+ } else {
+ IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&itv->eos_waitq, &wait);
+ set_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
+ }
+
+ /* Handle any pending interrupts */
+ ivtv_msleep_timeout(100, 0);
+ }
+
+ atomic_dec(&itv->capturing);
+
+ /* Clear capture and no-read bits */
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+
+ if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
+
+ if (atomic_read(&itv->capturing) > 0) {
+ return 0;
+ }
+
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+ /* Set the following Interrupt mask bits for capture */
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
+ del_timer(&itv->dma_timer);
+
+ /* event notification (off) */
+ if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
+ /* type: 0 = refresh */
+ /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
+ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
+ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
+ }
+
+ /* Raw-passthrough is implied on start. Make sure it's stopped so
+ the encoder will re-initialize when next started */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7);
+
+ wake_up(&s->waitq);
+
+ return 0;
+}
+EXPORT_SYMBOL(ivtv_stop_v4l2_encode_stream);
+
+int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+{
+ static const struct v4l2_event ev = {
+ .type = V4L2_EVENT_EOS,
+ };
+ struct ivtv *itv = s->itv;
+
+ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+ return -EINVAL;
+
+ if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
+ return 0;
+
+ IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
+
+ /* Stop Decoder */
+ if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
+ u32 tmp = 0;
+
+ /* Wait until the decoder is no longer running */
+ if (pts) {
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
+ 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
+ }
+ while (1) {
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
+ if (s->q_full.buffers + s->q_dma.buffers == 0) {
+ if (tmp == data[3])
+ break;
+ tmp = data[3];
+ }
+ if (ivtv_msleep_timeout(100, 1))
+ break;
+ }
+ }
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
+
+ /* turn off notification of dual/stereo mode change */
+ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
+
+ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
+ del_timer(&itv->dma_timer);
+
+ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
+ ivtv_flush_queues(s);
+
+ /* decoder needs time to settle */
+ ivtv_msleep_timeout(40, 0);
+
+ /* decrement decoding */
+ atomic_dec(&itv->decoding);
+
+ set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
+ wake_up(&itv->event_waitq);
+ v4l2_event_queue(s->vdev, &ev);
+
+ /* wake up wait queues */
+ wake_up(&s->waitq);
+
+ return 0;
+}
+
+int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+{
+ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+ struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+ if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+
+ /* Prevent others from starting/stopping streams while we
+ initiate/terminate passthrough mode */
+ if (enable) {
+ if (itv->output_mode == OUT_PASSTHROUGH) {
+ return 0;
+ }
+ if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
+ return -EBUSY;
+
+ /* Fully initialize stream, and then unflag init */
+ set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+
+ /* Setup YUV Decoder */
+ ivtv_setup_v4l2_decode_stream(dec_stream);
+
+ /* Start Decoder */
+ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
+ atomic_inc(&itv->decoding);
+
+ /* Setup capture if not already done */
+ if (atomic_read(&itv->capturing) == 0) {
+ cx2341x_handler_setup(&itv->cxhdl);
+ cx2341x_handler_set_busy(&itv->cxhdl, 1);
+ }
+
+ /* Start Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
+ atomic_inc(&itv->capturing);
+ return 0;
+ }
+
+ if (itv->output_mode != OUT_PASSTHROUGH)
+ return 0;
+
+ /* Stop Passthrough Mode */
+ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
+
+ atomic_dec(&itv->capturing);
+ atomic_dec(&itv->decoding);
+ clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
+ clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
+ itv->output_mode = OUT_NONE;
+ if (atomic_read(&itv->capturing) == 0)
+ cx2341x_handler_set_busy(&itv->cxhdl, 0);
+
+ return 0;
+}
diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/pci/ivtv/ivtv-streams.h
index a653a5136417..a653a5136417 100644
--- a/drivers/media/video/ivtv/ivtv-streams.h
+++ b/drivers/media/pci/ivtv/ivtv-streams.h
diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c
index 7338cb2d0a38..7338cb2d0a38 100644
--- a/drivers/media/video/ivtv/ivtv-udma.c
+++ b/drivers/media/pci/ivtv/ivtv-udma.c
diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/pci/ivtv/ivtv-udma.h
index ee3c9efb5b72..ee3c9efb5b72 100644
--- a/drivers/media/video/ivtv/ivtv-udma.h
+++ b/drivers/media/pci/ivtv/ivtv-udma.h
diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c
index 293db806d936..293db806d936 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/drivers/media/pci/ivtv/ivtv-vbi.c
diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/pci/ivtv/ivtv-vbi.h
index 166dd0b75d0f..166dd0b75d0f 100644
--- a/drivers/media/video/ivtv/ivtv-vbi.h
+++ b/drivers/media/pci/ivtv/ivtv-vbi.h
diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/pci/ivtv/ivtv-version.h
index a20f346fcad8..a20f346fcad8 100644
--- a/drivers/media/video/ivtv/ivtv-version.h
+++ b/drivers/media/pci/ivtv/ivtv-version.h
diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c
index 2ad65eb29832..2ad65eb29832 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.c
+++ b/drivers/media/pci/ivtv/ivtv-yuv.c
diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/pci/ivtv/ivtv-yuv.h
index ca5173fbf006..ca5173fbf006 100644
--- a/drivers/media/video/ivtv/ivtv-yuv.h
+++ b/drivers/media/pci/ivtv/ivtv-yuv.h
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c
index 05b94aa8ba32..05b94aa8ba32 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/pci/ivtv/ivtvfb.c
diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig
new file mode 100644
index 000000000000..d3cc21633b94
--- /dev/null
+++ b/drivers/media/pci/mantis/Kconfig
@@ -0,0 +1,38 @@
+config MANTIS_CORE
+ tristate "Mantis/Hopper PCI bridge based devices"
+ depends on PCI && I2C && INPUT && RC_CORE
+
+ help
+ Support for PCI cards based on the Mantis and Hopper PCi bridge.
+
+ Say Y if you own such a device and want to use it.
+
+config DVB_MANTIS
+ tristate "MANTIS based cards"
+ depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+ select DVB_MB86A16 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA665x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL
+ help
+ Support for PCI cards based on the Mantis PCI bridge.
+ Say Y when you have a Mantis based DVB card and want to use it.
+
+ If unsure say N.
+
+config DVB_HOPPER
+ tristate "HOPPER based cards"
+ depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL
+ help
+ Support for PCI cards based on the Hopper PCI bridge.
+ Say Y when you have a Hopper based DVB card and want to use it.
+
+ If unsure say N
diff --git a/drivers/media/pci/mantis/Makefile b/drivers/media/pci/mantis/Makefile
new file mode 100644
index 000000000000..f715051e4453
--- /dev/null
+++ b/drivers/media/pci/mantis/Makefile
@@ -0,0 +1,28 @@
+mantis_core-objs := mantis_ioc.o \
+ mantis_uart.o \
+ mantis_dma.o \
+ mantis_pci.o \
+ mantis_i2c.o \
+ mantis_dvb.o \
+ mantis_evm.o \
+ mantis_hif.o \
+ mantis_ca.o \
+ mantis_pcmcia.o \
+ mantis_input.o
+
+mantis-objs := mantis_cards.o \
+ mantis_vp1033.o \
+ mantis_vp1034.o \
+ mantis_vp1041.o \
+ mantis_vp2033.o \
+ mantis_vp2040.o \
+ mantis_vp3030.o
+
+hopper-objs := hopper_cards.o \
+ hopper_vp3028.o
+
+obj-$(CONFIG_MANTIS_CORE) += mantis_core.o
+obj-$(CONFIG_DVB_MANTIS) += mantis.o
+obj-$(CONFIG_DVB_HOPPER) += hopper.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c
index cc0251e01077..cc0251e01077 100644
--- a/drivers/media/dvb/mantis/hopper_cards.c
+++ b/drivers/media/pci/mantis/hopper_cards.c
diff --git a/drivers/media/dvb/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c
index 68a29f8bdf73..68a29f8bdf73 100644
--- a/drivers/media/dvb/mantis/hopper_vp3028.c
+++ b/drivers/media/pci/mantis/hopper_vp3028.c
diff --git a/drivers/media/dvb/mantis/hopper_vp3028.h b/drivers/media/pci/mantis/hopper_vp3028.h
index 57239498bc87..57239498bc87 100644
--- a/drivers/media/dvb/mantis/hopper_vp3028.h
+++ b/drivers/media/pci/mantis/hopper_vp3028.h
diff --git a/drivers/media/dvb/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c
index 3d7046909009..3d7046909009 100644
--- a/drivers/media/dvb/mantis/mantis_ca.c
+++ b/drivers/media/pci/mantis/mantis_ca.c
diff --git a/drivers/media/dvb/mantis/mantis_ca.h b/drivers/media/pci/mantis/mantis_ca.h
index dc63e55f7eca..dc63e55f7eca 100644
--- a/drivers/media/dvb/mantis/mantis_ca.h
+++ b/drivers/media/pci/mantis/mantis_ca.h
diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c
new file mode 100644
index 000000000000..0207d1f064e0
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_cards.c
@@ -0,0 +1,307 @@
+/*
+ Mantis PCI bridge driver
+
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+#include <linux/interrupt.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+
+#include "mantis_vp1033.h"
+#include "mantis_vp1034.h"
+#include "mantis_vp1041.h"
+#include "mantis_vp2033.h"
+#include "mantis_vp2040.h"
+#include "mantis_vp3030.h"
+
+#include "mantis_dma.h"
+#include "mantis_ca.h"
+#include "mantis_dvb.h"
+#include "mantis_uart.h"
+#include "mantis_ioc.h"
+#include "mantis_pci.h"
+#include "mantis_i2c.h"
+#include "mantis_reg.h"
+
+static unsigned int verbose;
+module_param(verbose, int, 0644);
+MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)");
+
+static int devs;
+
+#define DRIVER_NAME "Mantis"
+
+static char *label[10] = {
+ "DMA",
+ "IRQ-0",
+ "IRQ-1",
+ "OCERR",
+ "PABRT",
+ "RIPRR",
+ "PPERR",
+ "FTRGT",
+ "RISCI",
+ "RACK"
+};
+
+static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
+{
+ u32 stat = 0, mask = 0;
+ u32 rst_stat = 0, rst_mask = 0;
+
+ struct mantis_pci *mantis;
+ struct mantis_ca *ca;
+
+ mantis = (struct mantis_pci *) dev_id;
+ if (unlikely(mantis == NULL)) {
+ dprintk(MANTIS_ERROR, 1, "Mantis == NULL");
+ return IRQ_NONE;
+ }
+ ca = mantis->mantis_ca;
+
+ stat = mmread(MANTIS_INT_STAT);
+ mask = mmread(MANTIS_INT_MASK);
+ if (!(stat & mask))
+ return IRQ_NONE;
+
+ rst_mask = MANTIS_GPIF_WRACK |
+ MANTIS_GPIF_OTHERR |
+ MANTIS_SBUF_WSTO |
+ MANTIS_GPIF_EXTIRQ;
+
+ rst_stat = mmread(MANTIS_GPIF_STATUS);
+ rst_stat &= rst_mask;
+ mmwrite(rst_stat, MANTIS_GPIF_STATUS);
+
+ mantis->mantis_int_stat = stat;
+ mantis->mantis_int_mask = mask;
+ dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask);
+ if (stat & MANTIS_INT_RISCEN) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]);
+ }
+ if (stat & MANTIS_INT_IRQ0) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]);
+ mantis->gpif_status = rst_stat;
+ wake_up(&ca->hif_write_wq);
+ schedule_work(&ca->hif_evm_work);
+ }
+ if (stat & MANTIS_INT_IRQ1) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]);
+ schedule_work(&mantis->uart_work);
+ }
+ if (stat & MANTIS_INT_OCERR) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]);
+ }
+ if (stat & MANTIS_INT_PABORT) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]);
+ }
+ if (stat & MANTIS_INT_RIPERR) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]);
+ }
+ if (stat & MANTIS_INT_PPERR) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]);
+ }
+ if (stat & MANTIS_INT_FTRGT) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]);
+ }
+ if (stat & MANTIS_INT_RISCI) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]);
+ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28;
+ tasklet_schedule(&mantis->tasklet);
+ }
+ if (stat & MANTIS_INT_I2CDONE) {
+ dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]);
+ wake_up(&mantis->i2c_wq);
+ }
+ mmwrite(stat, MANTIS_INT_STAT);
+ stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE |
+ MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 |
+ MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 |
+ MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 |
+ MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 |
+ MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 |
+ MANTIS_INT_IRQ0 | MANTIS_INT_OCERR |
+ MANTIS_INT_PABORT | MANTIS_INT_RIPERR |
+ MANTIS_INT_PPERR | MANTIS_INT_FTRGT |
+ MANTIS_INT_RISCI);
+
+ if (stat)
+ dprintk(MANTIS_DEBUG, 0, "<Unknown> Stat=<%02x> Mask=<%02x>", stat, mask);
+
+ dprintk(MANTIS_DEBUG, 0, "\n");
+ return IRQ_HANDLED;
+}
+
+static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+ struct mantis_pci *mantis;
+ struct mantis_hwconfig *config;
+ int err = 0;
+
+ mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL);
+ if (mantis == NULL) {
+ printk(KERN_ERR "%s ERROR: Out of memory\n", __func__);
+ err = -ENOMEM;
+ goto fail0;
+ }
+
+ mantis->num = devs;
+ mantis->verbose = verbose;
+ mantis->pdev = pdev;
+ config = (struct mantis_hwconfig *) pci_id->driver_data;
+ config->irq_handler = &mantis_irq_handler;
+ mantis->hwconfig = config;
+
+ err = mantis_pci_init(mantis);
+ if (err) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err);
+ goto fail1;
+ }
+
+ err = mantis_stream_control(mantis, STREAM_TO_HIF);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err);
+ goto fail1;
+ }
+
+ err = mantis_i2c_init(mantis);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err);
+ goto fail2;
+ }
+
+ err = mantis_get_mac(mantis);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err);
+ goto fail2;
+ }
+
+ err = mantis_dma_init(mantis);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err);
+ goto fail3;
+ }
+
+ err = mantis_dvb_init(mantis);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err);
+ goto fail4;
+ }
+ err = mantis_uart_init(mantis);
+ if (err < 0) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err);
+ goto fail6;
+ }
+
+ devs++;
+
+ return err;
+
+
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err);
+ mantis_uart_exit(mantis);
+
+fail6:
+fail4:
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err);
+ mantis_dma_exit(mantis);
+
+fail3:
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err);
+ mantis_i2c_exit(mantis);
+
+fail2:
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err);
+ mantis_pci_exit(mantis);
+
+fail1:
+ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err);
+ kfree(mantis);
+
+fail0:
+ return err;
+}
+
+static void __devexit mantis_pci_remove(struct pci_dev *pdev)
+{
+ struct mantis_pci *mantis = pci_get_drvdata(pdev);
+
+ if (mantis) {
+
+ mantis_uart_exit(mantis);
+ mantis_dvb_exit(mantis);
+ mantis_dma_exit(mantis);
+ mantis_i2c_exit(mantis);
+ mantis_pci_exit(mantis);
+ kfree(mantis);
+ }
+ return;
+}
+
+static struct pci_device_id mantis_pci_table[] = {
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config),
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config),
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config),
+ MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config),
+ MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config),
+ MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config),
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config),
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config),
+ MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config),
+ MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config),
+ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config),
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, mantis_pci_table);
+
+static struct pci_driver mantis_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = mantis_pci_table,
+ .probe = mantis_pci_probe,
+ .remove = mantis_pci_remove,
+};
+
+static int __devinit mantis_init(void)
+{
+ return pci_register_driver(&mantis_pci_driver);
+}
+
+static void __devexit mantis_exit(void)
+{
+ return pci_unregister_driver(&mantis_pci_driver);
+}
+
+module_init(mantis_init);
+module_exit(mantis_exit);
+
+MODULE_DESCRIPTION("MANTIS driver");
+MODULE_AUTHOR("Manu Abraham");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h
index f2410cf0a6bf..f2410cf0a6bf 100644
--- a/drivers/media/dvb/mantis/mantis_common.h
+++ b/drivers/media/pci/mantis/mantis_common.h
diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c
new file mode 100644
index 000000000000..684d9061fe2a
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_core.c
@@ -0,0 +1,235 @@
+/*
+ Mantis PCI bridge driver
+
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "mantis_common.h"
+#include "mantis_core.h"
+#include "mantis_vp1033.h"
+#include "mantis_vp1034.h"
+#include "mantis_vp1041.h"
+#include "mantis_vp2033.h"
+#include "mantis_vp2040.h"
+#include "mantis_vp3030.h"
+
+static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
+{
+ int err;
+ struct i2c_msg msg[] = {
+ {
+ .addr = 0x50,
+ .flags = 0,
+ .buf = data,
+ .len = 1
+ }, {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .buf = data,
+ .len = length
+ },
+ };
+
+ err = i2c_transfer(&mantis->adapter, msg, 2);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1,
+ "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >",
+ err, data[0], data[1]);
+
+ return err;
+ }
+
+ return 0;
+}
+
+static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length)
+{
+ int err;
+
+ struct i2c_msg msg = {
+ .addr = 0x50,
+ .flags = 0,
+ .buf = data,
+ .len = length
+ };
+
+ err = i2c_transfer(&mantis->adapter, &msg, 1);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1,
+ "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >",
+ err, length, data[0], data[1]);
+
+ return err;
+ }
+
+ return 0;
+}
+
+static int get_mac_address(struct mantis_pci *mantis)
+{
+ int err;
+
+ mantis->mac_address[0] = 0x08;
+ err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error");
+
+ return err;
+ }
+ dprintk(verbose, MANTIS_ERROR, 0,
+ " MAC Address=[%pM]\n", mantis->mac_address);
+
+ return 0;
+}
+
+#define MANTIS_MODEL_UNKNOWN "UNKNOWN"
+#define MANTIS_DEV_UNKNOWN "UNKNOWN"
+
+struct mantis_hwconfig unknown_device = {
+ .model_name = MANTIS_MODEL_UNKNOWN,
+ .dev_type = MANTIS_DEV_UNKNOWN,
+};
+
+static void mantis_load_config(struct mantis_pci *mantis)
+{
+ switch (mantis->subsystem_device) {
+ case MANTIS_VP_1033_DVB_S: /* VP-1033 */
+ mantis->hwconfig = &vp1033_mantis_config;
+ break;
+ case MANTIS_VP_1034_DVB_S: /* VP-1034 */
+ mantis->hwconfig = &vp1034_mantis_config;
+ break;
+ case MANTIS_VP_1041_DVB_S2: /* VP-1041 */
+ case TECHNISAT_SKYSTAR_HD2:
+ mantis->hwconfig = &vp1041_mantis_config;
+ break;
+ case MANTIS_VP_2033_DVB_C: /* VP-2033 */
+ mantis->hwconfig = &vp2033_mantis_config;
+ break;
+ case MANTIS_VP_2040_DVB_C: /* VP-2040 */
+ case CINERGY_C: /* VP-2040 clone */
+ case TECHNISAT_CABLESTAR_HD2:
+ mantis->hwconfig = &vp2040_mantis_config;
+ break;
+ case MANTIS_VP_3030_DVB_T: /* VP-3030 */
+ mantis->hwconfig = &vp3030_mantis_config;
+ break;
+ default:
+ mantis->hwconfig = &unknown_device;
+ break;
+ }
+}
+
+int mantis_core_init(struct mantis_pci *mantis)
+{
+ int err = 0;
+
+ mantis_load_config(mantis);
+ dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n",
+ mantis->hwconfig->model_name, mantis->hwconfig->dev_type,
+ mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn));
+ dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ",
+ mantis->revision,
+ mantis->subsystem_vendor, mantis->subsystem_device);
+ dprintk(verbose, MANTIS_ERROR, 0,
+ "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n",
+ mantis->pdev->irq, mantis->latency,
+ mantis->mantis_addr, mantis->mantis_mmio);
+
+ err = mantis_i2c_init(mantis);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed");
+ return err;
+ }
+ err = get_mac_address(mantis);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed");
+ return err;
+ }
+ err = mantis_dma_init(mantis);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed");
+ return err;
+ }
+ err = mantis_dvb_init(mantis);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed");
+ return err;
+ }
+ err = mantis_uart_init(mantis);
+ if (err < 0) {
+ dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed");
+ return err;
+ }
+
+ return 0;
+}
+
+int mantis_core_exit(struct mantis_pci *mantis)
+{
+ mantis_dma_stop(mantis);
+ dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping");
+
+ mantis_uart_exit(mantis);
+ dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed");
+
+ if (mantis_dma_exit(mantis) < 0)
+ dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed");
+ if (mantis_dvb_exit(mantis) < 0)
+ dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed");
+ if (mantis_i2c_exit(mantis) < 0)
+ dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed");
+
+ return 0;
+}
+
+/* Turn the given bit on or off. */
+void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value)
+{
+ u32 cur;
+
+ cur = mmread(MANTIS_GPIF_ADDR);
+ if (value)
+ mantis->gpio_status = cur | (1 << bitpos);
+ else
+ mantis->gpio_status = cur & (~(1 << bitpos));
+
+ mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR);
+ mmwrite(0x00, MANTIS_GPIF_DOUT);
+ udelay(100);
+}
+
+/* direction = 0 , no CI passthrough ; 1 , CI passthrough */
+void mantis_set_direction(struct mantis_pci *mantis, int direction)
+{
+ u32 reg;
+
+ reg = mmread(0x28);
+ dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup");
+ if (direction == 0x01) {
+ /* to CI */
+ reg |= 0x04;
+ mmwrite(reg, 0x28);
+ reg &= 0xff - 0x04;
+ mmwrite(reg, 0x28);
+ } else {
+ reg &= 0xff - 0x04;
+ mmwrite(reg, 0x28);
+ reg |= 0x04;
+ mmwrite(reg, 0x28);
+ }
+}
diff --git a/drivers/media/dvb/mantis/mantis_core.h b/drivers/media/pci/mantis/mantis_core.h
index 833ee42e694e..833ee42e694e 100644
--- a/drivers/media/dvb/mantis/mantis_core.h
+++ b/drivers/media/pci/mantis/mantis_core.h
diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c
index 566c407175a4..566c407175a4 100644
--- a/drivers/media/dvb/mantis/mantis_dma.c
+++ b/drivers/media/pci/mantis/mantis_dma.c
diff --git a/drivers/media/dvb/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h
index 6be00fa82094..6be00fa82094 100644
--- a/drivers/media/dvb/mantis/mantis_dma.h
+++ b/drivers/media/pci/mantis/mantis_dma.h
diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c
new file mode 100644
index 000000000000..5d15c6b74d9b
--- /dev/null
+++ b/drivers/media/pci/mantis/mantis_dvb.c
@@ -0,0 +1,301 @@
+/*
+ Mantis PCI bridge driver
+ Copyright (C) Manu Abraham (abraham.manu@gmail.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "mantis_common.h"
+#include "mantis_dma.h"
+#include "mantis_ca.h"
+#include "mantis_ioc.h"
+#include "mantis_dvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power)
+{
+ struct mantis_hwconfig *config = mantis->hwconfig;
+
+ switch (power) {
+ case POWER_ON:
+ dprintk(MANTIS_DEBUG, 1, "Power ON");
+ mantis_gpio_set_bits(mantis, config->power, POWER_ON);
+ msleep(100);
+ mantis_gpio_set_bits(mantis, config->power, POWER_ON);
+ msleep(100);
+ break;
+
+ case POWER_OFF:
+ dprintk(MANTIS_DEBUG, 1, "Power OFF");
+ mantis_gpio_set_bits(mantis, config->power, POWER_OFF);
+ msleep(100);
+ break;
+
+ default:
+ dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power);
+ return -1;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_frontend_power);
+
+void mantis_frontend_soft_reset(struct mantis_pci *mantis)
+{
+ struct mantis_hwconfig *config = mantis->hwconfig;
+
+ dprintk(MANTIS_DEBUG, 1, "Frontend RESET");
+ mantis_gpio_set_bits(mantis, config->reset, 0);
+ msleep(100);
+ mantis_gpio_set_bits(mantis, config->reset, 0);
+ msleep(100);
+ mantis_gpio_set_bits(mantis, config->reset, 1);
+ msleep(100);
+ mantis_gpio_set_bits(mantis, config->reset, 1);
+ msleep(100);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset);
+
+static int mantis_frontend_shutdown(struct mantis_pci *mantis)
+{
+ int err;
+
+ mantis_frontend_soft_reset(mantis);
+ err = mantis_frontend_power(mantis, POWER_OFF);
+ if (err != 0) {
+ dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct mantis_pci *mantis = dvbdmx->priv;
+
+ dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed");
+ if (!dvbdmx->dmx.frontend) {
+ dprintk(MANTIS_DEBUG, 1, "no frontend ?");
+ return -EINVAL;
+ }
+
+ mantis->feeds++;
+ dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds);
+
+ if (mantis->feeds == 1) {
+ dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma");
+ mantis_dma_start(mantis);
+ tasklet_enable(&mantis->tasklet);
+ }
+
+ return mantis->feeds;
+}
+
+static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct mantis_pci *mantis = dvbdmx->priv;
+
+ dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed");
+ if (!dvbdmx->dmx.frontend) {
+ dprintk(MANTIS_DEBUG, 1, "no frontend ?");
+ return -EINVAL;
+ }
+
+ mantis->feeds--;
+ if (mantis->feeds == 0) {
+ dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma");
+ tasklet_disable(&mantis->tasklet);
+ mantis_dma_stop(mantis);
+ }
+
+ return 0;
+}
+
+int __devinit mantis_dvb_init(struct mantis_pci *mantis)
+{
+ struct mantis_hwconfig *config = mantis->hwconfig;
+ int result = -1;
+
+ dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter");
+
+ result = dvb_register_adapter(&mantis->dvb_adapter,
+ "Mantis DVB adapter",
+ THIS_MODULE,
+ &mantis->pdev->dev,
+ adapter_nr);
+
+ if (result < 0) {
+
+ dprintk(MANTIS_ERROR, 1, "Error registering adapter");
+ return -ENODEV;
+ }
+
+ mantis->dvb_adapter.priv = mantis;
+ mantis->demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+
+ mantis->demux.priv = mantis;
+ mantis->demux.filternum = 256;
+ mantis->demux.feednum = 256;
+ mantis->demux.start_feed = mantis_dvb_start_feed;
+ mantis->demux.stop_feed = mantis_dvb_stop_feed;
+ mantis->demux.write_to_decoder = NULL;
+
+ dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init");
+ result = dvb_dmx_init(&mantis->demux);
+ if (result < 0) {
+ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+
+ goto err0;
+ }
+
+ mantis->dmxdev.filternum = 256;
+ mantis->dmxdev.demux = &mantis->demux.dmx;
+ mantis->dmxdev.capabilities = 0;
+ dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init");
+
+ result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter);
+ if (result < 0) {
+
+ dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result);
+ goto err1;
+ }
+
+ mantis->fe_hw.source = DMX_FRONTEND_0;
+ result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+ if (result < 0) {
+
+ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+ goto err2;
+ }
+
+ mantis->fe_mem.source = DMX_MEMORY_FE;
+ result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+ if (result < 0) {
+ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+ goto err3;
+ }
+
+ result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+ if (result < 0) {
+ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result);
+ goto err4;
+ }
+
+ dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx);
+ tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis);
+ tasklet_disable(&mantis->tasklet);
+ if (mantis->hwconfig) {
+ result = config->frontend_init(mantis, mantis->fe);
+ if (result < 0) {
+ dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!");
+ goto err5;
+ } else {
+ if (mantis->fe == NULL) {
+ dprintk(MANTIS_ERROR, 1, "FE <NULL>");
+ goto err5;
+ }
+
+ if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) {
+ dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed");
+
+ if (mantis->fe->ops.release)
+ mantis->fe->ops.release(mantis->fe);
+
+ mantis->fe = NULL;
+ goto err5;
+ }
+ }
+ }
+
+ return 0;
+
+ /* Error conditions .. */
+err5:
+ tasklet_kill(&mantis->tasklet);
+ dvb_net_release(&mantis->dvbnet);
+ if (mantis->fe) {
+ dvb_unregister_frontend(mantis->fe);
+ dvb_frontend_detach(mantis->fe);
+ }
+err4:
+ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+
+err3:
+ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+
+err2:
+ dvb_dmxdev_release(&mantis->dmxdev);
+
+err1:
+ dvb_dmx_release(&mantis->demux);
+
+err0:
+ dvb_unregister_adapter(&mantis->dvb_adapter);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(mantis_dvb_init);
+
+int __devexit mantis_dvb_exit(struct mantis_pci *mantis)
+{
+ int err;
+
+ if (mantis->fe) {
+ /* mantis_ca_exit(mantis); */
+ err = mantis_frontend_shutdown(mantis);
+ if (err != 0)
+ dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err);
+ dvb_unregister_frontend(mantis->fe);
+ dvb_frontend_detach(mantis->fe);
+ }
+
+ tasklet_kill(&mantis->tasklet);
+ dvb_net_release(&mantis->dvbnet);
+
+ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem);
+ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw);
+
+ dvb_dmxdev_release(&mantis->dmxdev);
+ dvb_dmx_release(&mantis->demux);
+
+ dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter");
+ dvb_unregister_adapter(&mantis->dvb_adapter);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mantis_dvb_exit);
diff --git a/drivers/media/dvb/mantis/mantis_dvb.h b/drivers/media/pci/mantis/mantis_dvb.h
index 464199db304e..464199db304e 100644
--- a/drivers/media/dvb/mantis/mantis_dvb.h
+++ b/drivers/media/pci/mantis/mantis_dvb.h
diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/pci/mantis/mantis_evm.c
index 71ce52875c38..71ce52875c38 100644
--- a/drivers/media/dvb/mantis/mantis_evm.c
+++ b/drivers/media/pci/mantis/mantis_evm.c
diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/pci/mantis/mantis_hif.c
index 10c68df7e16f..10c68df7e16f 100644
--- a/drivers/media/dvb/mantis/mantis_hif.c
+++ b/drivers/media/pci/mantis/mantis_hif.c
diff --git a/drivers/media/dvb/mantis/mantis_hif.h b/drivers/media/pci/mantis/mantis_hif.h
index 9094f9ed2362..9094f9ed2362 100644
--- a/drivers/media/dvb/mantis/mantis_hif.h
+++ b/drivers/media/pci/mantis/mantis_hif.h
diff --git a/drivers/media/dvb/mantis/mantis_i2c.c b/drivers/media/pci/mantis/mantis_i2c.c
index e7794517fe26..e7794517fe26 100644
--- a/drivers/media/dvb/mantis/mantis_i2c.c
+++ b/drivers/media/pci/mantis/mantis_i2c.c
diff --git a/drivers/media/dvb/mantis/mantis_i2c.h b/drivers/media/pci/mantis/mantis_i2c.h
index 1342df2faed8..1342df2faed8 100644
--- a/drivers/media/dvb/mantis/mantis_i2c.h
+++ b/drivers/media/pci/mantis/mantis_i2c.h
diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c
index db6d54d3fec0..db6d54d3fec0 100644
--- a/drivers/media/dvb/mantis/mantis_input.c
+++ b/drivers/media/pci/mantis/mantis_input.c
diff --git a/drivers/media/dvb/mantis/mantis_ioc.c b/drivers/media/pci/mantis/mantis_ioc.c
index 24fcdc63d6d5..24fcdc63d6d5 100644
--- a/drivers/media/dvb/mantis/mantis_ioc.c
+++ b/drivers/media/pci/mantis/mantis_ioc.c
diff --git a/drivers/media/dvb/mantis/mantis_ioc.h b/drivers/media/pci/mantis/mantis_ioc.h
index d56e002b2955..d56e002b2955 100644
--- a/drivers/media/dvb/mantis/mantis_ioc.h
+++ b/drivers/media/pci/mantis/mantis_ioc.h
diff --git a/drivers/media/dvb/mantis/mantis_link.h b/drivers/media/pci/mantis/mantis_link.h
index 2a814774a001..2a814774a001 100644
--- a/drivers/media/dvb/mantis/mantis_link.h
+++ b/drivers/media/pci/mantis/mantis_link.h
diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/pci/mantis/mantis_pci.c
index 371558af2d96..371558af2d96 100644
--- a/drivers/media/dvb/mantis/mantis_pci.c
+++ b/drivers/media/pci/mantis/mantis_pci.c
diff --git a/drivers/media/dvb/mantis/mantis_pci.h b/drivers/media/pci/mantis/mantis_pci.h
index 65f004519086..65f004519086 100644
--- a/drivers/media/dvb/mantis/mantis_pci.h
+++ b/drivers/media/pci/mantis/mantis_pci.h
diff --git a/drivers/media/dvb/mantis/mantis_pcmcia.c b/drivers/media/pci/mantis/mantis_pcmcia.c
index 2f188c089666..2f188c089666 100644
--- a/drivers/media/dvb/mantis/mantis_pcmcia.c
+++ b/drivers/media/pci/mantis/mantis_pcmcia.c
diff --git a/drivers/media/dvb/mantis/mantis_reg.h b/drivers/media/pci/mantis/mantis_reg.h
index 7761f9dc7fe0..7761f9dc7fe0 100644
--- a/drivers/media/dvb/mantis/mantis_reg.h
+++ b/drivers/media/pci/mantis/mantis_reg.h
diff --git a/drivers/media/dvb/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c
index 18340dafa426..18340dafa426 100644
--- a/drivers/media/dvb/mantis/mantis_uart.c
+++ b/drivers/media/pci/mantis/mantis_uart.c
diff --git a/drivers/media/dvb/mantis/mantis_uart.h b/drivers/media/pci/mantis/mantis_uart.h
index ffb62a0a5a13..ffb62a0a5a13 100644
--- a/drivers/media/dvb/mantis/mantis_uart.h
+++ b/drivers/media/pci/mantis/mantis_uart.h
diff --git a/drivers/media/dvb/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c
index ad013e93ed11..ad013e93ed11 100644
--- a/drivers/media/dvb/mantis/mantis_vp1033.c
+++ b/drivers/media/pci/mantis/mantis_vp1033.c
diff --git a/drivers/media/dvb/mantis/mantis_vp1033.h b/drivers/media/pci/mantis/mantis_vp1033.h
index 7daaa1bf127d..7daaa1bf127d 100644
--- a/drivers/media/dvb/mantis/mantis_vp1033.h
+++ b/drivers/media/pci/mantis/mantis_vp1033.h
diff --git a/drivers/media/dvb/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c
index 430ae84ce528..430ae84ce528 100644
--- a/drivers/media/dvb/mantis/mantis_vp1034.c
+++ b/drivers/media/pci/mantis/mantis_vp1034.c
diff --git a/drivers/media/dvb/mantis/mantis_vp1034.h b/drivers/media/pci/mantis/mantis_vp1034.h
index 323f38ef8e3d..323f38ef8e3d 100644
--- a/drivers/media/dvb/mantis/mantis_vp1034.h
+++ b/drivers/media/pci/mantis/mantis_vp1034.h
diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c
index 07aa887a4b4a..07aa887a4b4a 100644
--- a/drivers/media/dvb/mantis/mantis_vp1041.c
+++ b/drivers/media/pci/mantis/mantis_vp1041.c
diff --git a/drivers/media/dvb/mantis/mantis_vp1041.h b/drivers/media/pci/mantis/mantis_vp1041.h
index 1ae5b3de8081..1ae5b3de8081 100644
--- a/drivers/media/dvb/mantis/mantis_vp1041.h
+++ b/drivers/media/pci/mantis/mantis_vp1041.h
diff --git a/drivers/media/dvb/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c
index 1ca6837fbe46..1ca6837fbe46 100644
--- a/drivers/media/dvb/mantis/mantis_vp2033.c
+++ b/drivers/media/pci/mantis/mantis_vp2033.c
diff --git a/drivers/media/dvb/mantis/mantis_vp2033.h b/drivers/media/pci/mantis/mantis_vp2033.h
index c55242b79d54..c55242b79d54 100644
--- a/drivers/media/dvb/mantis/mantis_vp2033.h
+++ b/drivers/media/pci/mantis/mantis_vp2033.h
diff --git a/drivers/media/dvb/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c
index d480741afd78..d480741afd78 100644
--- a/drivers/media/dvb/mantis/mantis_vp2040.c
+++ b/drivers/media/pci/mantis/mantis_vp2040.c
diff --git a/drivers/media/dvb/mantis/mantis_vp2040.h b/drivers/media/pci/mantis/mantis_vp2040.h
index d125e219b685..d125e219b685 100644
--- a/drivers/media/dvb/mantis/mantis_vp2040.h
+++ b/drivers/media/pci/mantis/mantis_vp2040.h
diff --git a/drivers/media/dvb/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c
index 4155c838a18a..4155c838a18a 100644
--- a/drivers/media/dvb/mantis/mantis_vp3028.c
+++ b/drivers/media/pci/mantis/mantis_vp3028.c
diff --git a/drivers/media/dvb/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h
index b07be6adc522..b07be6adc522 100644
--- a/drivers/media/dvb/mantis/mantis_vp3028.h
+++ b/drivers/media/pci/mantis/mantis_vp3028.h
diff --git a/drivers/media/dvb/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c
index c09308cd3ac6..c09308cd3ac6 100644
--- a/drivers/media/dvb/mantis/mantis_vp3030.c
+++ b/drivers/media/pci/mantis/mantis_vp3030.c
diff --git a/drivers/media/dvb/mantis/mantis_vp3030.h b/drivers/media/pci/mantis/mantis_vp3030.h
index 5f12c4266277..5f12c4266277 100644
--- a/drivers/media/dvb/mantis/mantis_vp3030.h
+++ b/drivers/media/pci/mantis/mantis_vp3030.h
diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig
new file mode 100644
index 000000000000..b4bf848be5a0
--- /dev/null
+++ b/drivers/media/pci/meye/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_MEYE
+ tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
+ depends on PCI && SONY_LAPTOP && VIDEO_V4L2
+ ---help---
+ This is the video4linux driver for the Motion Eye camera found
+ in the Vaio Picturebook laptops. Please read the material in
+ <file:Documentation/video4linux/meye.txt> for more information.
+
+ If you say Y or M here, you need to say Y or M to "Sony Laptop
+ Extras" in the misc device section.
+
+ To compile this driver as a module, choose M here: the
+ module will be called meye.
diff --git a/drivers/media/pci/meye/Makefile b/drivers/media/pci/meye/Makefile
new file mode 100644
index 000000000000..49388518cd01
--- /dev/null
+++ b/drivers/media/pci/meye/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_MEYE) += meye.o
diff --git a/drivers/media/video/meye.c b/drivers/media/pci/meye/meye.c
index 7bc775219f97..7bc775219f97 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/pci/meye/meye.c
diff --git a/drivers/media/video/meye.h b/drivers/media/pci/meye/meye.h
index 4bdeb03f1644..4bdeb03f1644 100644
--- a/drivers/media/video/meye.h
+++ b/drivers/media/pci/meye/meye.h
diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig
new file mode 100644
index 000000000000..637d506b23c5
--- /dev/null
+++ b/drivers/media/pci/ngene/Kconfig
@@ -0,0 +1,13 @@
+config DVB_NGENE
+ tristate "Micronas nGene support"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ Support for Micronas PCI express cards with nGene bridge.
+
diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile
new file mode 100644
index 000000000000..5c0b5d6b9d69
--- /dev/null
+++ b/drivers/media/pci/ngene/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the nGene device driver
+#
+
+ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o
+
+obj-$(CONFIG_DVB_NGENE) += ngene.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
+
+# For the staging CI driver cxd2099
+ccflags-y += -Idrivers/staging/media/cxd2099/
diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c
new file mode 100644
index 000000000000..a6cd6959ad19
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-cards.c
@@ -0,0 +1,823 @@
+/*
+ * ngene-cards.c: nGene PCIe bridge driver - card specific info
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ * Modifications for new nGene firmware,
+ * support for EEPROM-copying,
+ * support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include "ngene.h"
+
+/* demods/tuners */
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "lnbh24.h"
+#include "lgdt330x.h"
+#include "mt2131.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
+#include "drxd.h"
+#include "dvb-pll.h"
+
+
+/****************************************************************************/
+/* Demod/tuner attachment ***************************************************/
+/****************************************************************************/
+
+static int tuner_attach_stv6110(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct stv090x_config *feconf = (struct stv090x_config *)
+ chan->dev->card_info->fe_config[chan->number];
+ struct stv6110x_config *tunerconf = (struct stv6110x_config *)
+ chan->dev->card_info->tuner_config[chan->number];
+ struct stv6110x_devctl *ctl;
+
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c);
+ if (ctl == NULL) {
+ printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n");
+ return -ENODEV;
+ }
+
+ feconf->tuner_init = ctl->tuner_init;
+ feconf->tuner_sleep = ctl->tuner_sleep;
+ feconf->tuner_set_mode = ctl->tuner_set_mode;
+ feconf->tuner_set_frequency = ctl->tuner_set_frequency;
+ feconf->tuner_get_frequency = ctl->tuner_get_frequency;
+ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain;
+ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain;
+ feconf->tuner_set_refclk = ctl->tuner_set_refclk;
+ feconf->tuner_get_status = ctl->tuner_get_status;
+
+ return 0;
+}
+
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct ngene_channel *chan = fe->sec_priv;
+ int status;
+
+ if (enable) {
+ down(&chan->dev->pll_mutex);
+ status = chan->gate_ctrl(fe, 1);
+ } else {
+ status = chan->gate_ctrl(fe, 0);
+ up(&chan->dev->pll_mutex);
+ }
+ return status;
+}
+
+static int tuner_attach_tda18271(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct dvb_frontend *fe;
+
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 1);
+ fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60);
+ if (chan->fe->ops.i2c_gate_ctrl)
+ chan->fe->ops.i2c_gate_ctrl(chan->fe, 0);
+ if (!fe) {
+ printk(KERN_ERR "No TDA18271 found!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int tuner_attach_probe(struct ngene_channel *chan)
+{
+ if (chan->demod_type == 0)
+ return tuner_attach_stv6110(chan);
+ if (chan->demod_type == 1)
+ return tuner_attach_tda18271(chan);
+ return -EINVAL;
+}
+
+static int demod_attach_stv0900(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct stv090x_config *feconf = (struct stv090x_config *)
+ chan->dev->card_info->fe_config[chan->number];
+
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ /* Note: Both adapters share the same i2c bus, but the demod */
+ /* driver requires that each demod has its own i2c adapter */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ chan->fe = dvb_attach(stv090x_attach, feconf, i2c,
+ (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0
+ : STV090x_DEMODULATOR_1);
+ if (chan->fe == NULL) {
+ printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n");
+ return -ENODEV;
+ }
+
+ /* store channel info */
+ if (feconf->tuner_i2c_lock)
+ chan->fe->analog_demod_priv = chan;
+
+ if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0,
+ 0, chan->dev->card_info->lnb[chan->number])) {
+ printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n");
+ dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock)
+{
+ struct ngene_channel *chan = fe->analog_demod_priv;
+
+ if (lock)
+ down(&chan->dev->pll_mutex);
+ else
+ up(&chan->dev->pll_mutex);
+}
+
+static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val)
+{
+ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1 } };
+ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1;
+}
+
+static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr,
+ u16 reg, u8 *val)
+{
+ u8 msg[2] = {reg>>8, reg&0xff};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2},
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = val, .len = 1} };
+ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1;
+}
+
+static int port_has_stv0900(struct i2c_adapter *i2c, int port)
+{
+ u8 val;
+ if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int port_has_drxk(struct i2c_adapter *i2c, int port)
+{
+ u8 val;
+
+ if (i2c_read(i2c, 0x29+port, &val) < 0)
+ return 0;
+ return 1;
+}
+
+static int demod_attach_drxk(struct ngene_channel *chan,
+ struct i2c_adapter *i2c)
+{
+ struct drxk_config config;
+
+ memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
+ config.qam_demod_parameter_count = 4;
+ config.adr = 0x29 + (chan->number ^ 2);
+
+ chan->fe = dvb_attach(drxk_attach, &config, i2c);
+ if (!chan->fe) {
+ printk(KERN_ERR "No DRXK found!\n");
+ return -ENODEV;
+ }
+ chan->fe->sec_priv = chan;
+ chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl;
+ chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+ return 0;
+}
+
+static int cineS2_probe(struct ngene_channel *chan)
+{
+ struct i2c_adapter *i2c;
+ struct stv090x_config *fe_conf;
+ u8 buf[3];
+ struct i2c_msg i2c_msg = { .flags = 0, .buf = buf };
+ int rc;
+
+ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */
+ if (chan->number < 2)
+ i2c = &chan->dev->channel[0].i2c_adapter;
+ else
+ i2c = &chan->dev->channel[1].i2c_adapter;
+
+ if (port_has_stv0900(i2c, chan->number)) {
+ chan->demod_type = 0;
+ fe_conf = chan->dev->card_info->fe_config[chan->number];
+ /* demod found, attach it */
+ rc = demod_attach_stv0900(chan);
+ if (rc < 0 || chan->number < 2)
+ return rc;
+
+ /* demod #2: reprogram outputs DPN1 & DPN2 */
+ i2c_msg.addr = fe_conf->address;
+ i2c_msg.len = 3;
+ buf[0] = 0xf1;
+ switch (chan->number) {
+ case 2:
+ buf[1] = 0x5c;
+ buf[2] = 0xc2;
+ break;
+ case 3:
+ buf[1] = 0x61;
+ buf[2] = 0xcc;
+ break;
+ default:
+ return -ENODEV;
+ }
+ rc = i2c_transfer(i2c, &i2c_msg, 1);
+ if (rc != 1) {
+ printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n");
+ return -EIO;
+ }
+ } else if (port_has_drxk(i2c, chan->number^2)) {
+ chan->demod_type = 1;
+ demod_attach_drxk(chan, i2c);
+ } else {
+ printk(KERN_ERR "No demod found on chan %d\n", chan->number);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+static struct lgdt330x_config aver_m780 = {
+ .demod_address = 0xb2 >> 1,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x00, /* PARALLEL */
+ .clock_polarity_flip = 1,
+};
+
+static struct mt2131_config m780_tunerconfig = {
+ 0xc0 >> 1
+};
+
+/* A single func to attach the demo and tuner, rather than
+ * use two sep funcs like the current design mandates.
+ */
+static int demod_attach_lg330x(struct ngene_channel *chan)
+{
+ chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter);
+ if (chan->fe == NULL) {
+ printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n");
+ return -ENODEV;
+ }
+
+ dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter,
+ &m780_tunerconfig, 0);
+
+ return (chan->fe) ? 0 : -ENODEV;
+}
+
+static int demod_attach_drxd(struct ngene_channel *chan)
+{
+ struct drxd_config *feconf;
+
+ feconf = chan->dev->card_info->fe_config[chan->number];
+
+ chan->fe = dvb_attach(drxd_attach, feconf, chan,
+ &chan->i2c_adapter, &chan->dev->pci_dev->dev);
+ if (!chan->fe) {
+ pr_err("No DRXD found!\n");
+ return -ENODEV;
+ }
+
+ if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address,
+ &chan->i2c_adapter,
+ feconf->pll_type)) {
+ pr_err("No pll(%d) found!\n", feconf->pll_type);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/* EEPROM TAGS **************************************************************/
+/****************************************************************************/
+
+#define MICNG_EE_START 0x0100
+#define MICNG_EE_END 0x0FF0
+
+#define MICNG_EETAG_END0 0x0000
+#define MICNG_EETAG_END1 0xFFFF
+
+/* 0x0001 - 0x000F reserved for housekeeping */
+/* 0xFFFF - 0xFFFE reserved for housekeeping */
+
+/* Micronas assigned tags
+ EEProm tags for hardware support */
+
+#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */
+#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */
+
+#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */
+#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */
+
+/* Tag range for OEMs */
+
+#define MICNG_EETAG_OEM_FIRST 0xC000
+#define MICNG_EETAG_OEM_LAST 0xFFEF
+
+static int i2c_write_eeprom(struct i2c_adapter *adapter,
+ u8 adr, u16 reg, u8 data)
+{
+ u8 m[3] = {(reg >> 8), (reg & 0xff), data};
+ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m,
+ .len = sizeof(m)};
+
+ if (i2c_transfer(adapter, &msg, 1) != 1) {
+ pr_err(DEVICE_NAME ": Error writing EEPROM!\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int i2c_read_eeprom(struct i2c_adapter *adapter,
+ u8 adr, u16 reg, u8 *data, int len)
+{
+ u8 msg[2] = {(reg >> 8), (reg & 0xff)};
+ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0,
+ .buf = msg, .len = 2 },
+ {.addr = adr, .flags = I2C_M_RD,
+ .buf = data, .len = len} };
+
+ if (i2c_transfer(adapter, msgs, 2) != 2) {
+ pr_err(DEVICE_NAME ": Error reading EEPROM\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int ReadEEProm(struct i2c_adapter *adapter,
+ u16 Tag, u32 MaxLen, u8 *data, u32 *pLength)
+{
+ int status = 0;
+ u16 Addr = MICNG_EE_START, Length, tag = 0;
+ u8 EETag[3];
+
+ while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+ if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+ return -1;
+ tag = (EETag[0] << 8) | EETag[1];
+ if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+ return -1;
+ if (tag == Tag)
+ break;
+ Addr += sizeof(u16) + 1 + EETag[2];
+ }
+ if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+ pr_err(DEVICE_NAME
+ ": Reached EOEE @ Tag = %04x Length = %3d\n",
+ tag, EETag[2]);
+ return -1;
+ }
+ Length = EETag[2];
+ if (Length > MaxLen)
+ Length = (u16) MaxLen;
+ if (Length > 0) {
+ Addr += sizeof(u16) + 1;
+ status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length);
+ if (!status) {
+ *pLength = EETag[2];
+ if (Length < EETag[2])
+ ; /*status=STATUS_BUFFER_OVERFLOW; */
+ }
+ }
+ return status;
+}
+
+static int WriteEEProm(struct i2c_adapter *adapter,
+ u16 Tag, u32 Length, u8 *data)
+{
+ int status = 0;
+ u16 Addr = MICNG_EE_START;
+ u8 EETag[3];
+ u16 tag = 0;
+ int retry, i;
+
+ while (Addr + sizeof(u16) + 1 < MICNG_EE_END) {
+ if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag)))
+ return -1;
+ tag = (EETag[0] << 8) | EETag[1];
+ if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1)
+ return -1;
+ if (tag == Tag)
+ break;
+ Addr += sizeof(u16) + 1 + EETag[2];
+ }
+ if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) {
+ pr_err(DEVICE_NAME
+ ": Reached EOEE @ Tag = %04x Length = %3d\n",
+ tag, EETag[2]);
+ return -1;
+ }
+
+ if (Length > EETag[2])
+ return -EINVAL;
+ /* Note: We write the data one byte at a time to avoid
+ issues with page sizes. (which are different for
+ each manufacture and eeprom size)
+ */
+ Addr += sizeof(u16) + 1;
+ for (i = 0; i < Length; i++, Addr++) {
+ status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]);
+
+ if (status)
+ break;
+
+ /* Poll for finishing write cycle */
+ retry = 10;
+ while (retry) {
+ u8 Tmp;
+
+ msleep(50);
+ status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1);
+ if (status)
+ break;
+ if (Tmp != data[i])
+ pr_err(DEVICE_NAME
+ "eeprom write error\n");
+ retry -= 1;
+ }
+ if (status) {
+ pr_err(DEVICE_NAME
+ ": Timeout polling eeprom\n");
+ break;
+ }
+ }
+ return status;
+}
+
+static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data)
+{
+ int stat;
+ u8 buf[2];
+ u32 len = 0;
+
+ stat = ReadEEProm(adapter, tag, 2, buf, &len);
+ if (stat)
+ return stat;
+ if (len != 2)
+ return -EINVAL;
+
+ *data = (buf[0] << 8) | buf[1];
+ return 0;
+}
+
+static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data)
+{
+ int stat;
+ u8 buf[2];
+
+ buf[0] = data >> 8;
+ buf[1] = data & 0xff;
+ stat = WriteEEProm(adapter, tag, 2, buf);
+ if (stat)
+ return stat;
+ return 0;
+}
+
+static s16 osc_deviation(void *priv, s16 deviation, int flag)
+{
+ struct ngene_channel *chan = priv;
+ struct i2c_adapter *adap = &chan->i2c_adapter;
+ u16 data = 0;
+
+ if (flag) {
+ data = (u16) deviation;
+ pr_info(DEVICE_NAME ": write deviation %d\n",
+ deviation);
+ eeprom_write_ushort(adap, 0x1000 + chan->number, data);
+ } else {
+ if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data))
+ data = 0;
+ pr_info(DEVICE_NAME ": read deviation %d\n",
+ (s16) data);
+ }
+
+ return (s16) data;
+}
+
+/****************************************************************************/
+/* Switch control (I2C gates, etc.) *****************************************/
+/****************************************************************************/
+
+
+static struct stv090x_config fe_cineS2 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+
+ .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+};
+
+static struct stv090x_config fe_cineS2_2 = {
+ .device = STV0900,
+ .demod_mode = STV090x_DUAL,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 27000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .adc1_range = STV090x_ADC_1Vpp,
+ .adc2_range = STV090x_ADC_1Vpp,
+
+ .diseqc_envelope_mode = true,
+
+ .tuner_i2c_lock = cineS2_tuner_i2c_lock,
+};
+
+static struct stv6110x_config tuner_cineS2_0 = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct stv6110x_config tuner_cineS2_1 = {
+ .addr = 0x63,
+ .refclk = 27000000,
+ .clk_div = 1,
+};
+
+static struct ngene_info ngene_info_cineS2 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Linux4Media cineS2 DVB-S2 Twin Tuner",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0b, 0x08},
+ .tsf = {3, 3},
+ .fw_version = 18,
+ .msi_supported = true,
+};
+
+static struct ngene_info ngene_info_satixS2 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Mystique SaTiX-S2 Dual",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110},
+ .fe_config = {&fe_cineS2, &fe_cineS2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0b, 0x08},
+ .tsf = {3, 3},
+ .fw_version = 18,
+ .msi_supported = true,
+};
+
+static struct ngene_info ngene_info_satixS2v2 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Mystique SaTiX-S2 Dual (v2)",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
+ .tsf = {3, 3},
+ .fw_version = 18,
+ .msi_supported = true,
+};
+
+static struct ngene_info ngene_info_cineS2v5 = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
+ .tsf = {3, 3},
+ .fw_version = 18,
+ .msi_supported = true,
+};
+
+
+static struct ngene_info ngene_info_duoFlex = {
+ .type = NGENE_SIDEWINDER,
+ .name = "Digital Devices DuoFlex PCIe or miniPCIe",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN,
+ NGENE_IO_TSOUT},
+ .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe},
+ .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe},
+ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2},
+ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1},
+ .lnb = {0x0a, 0x08, 0x0b, 0x09},
+ .tsf = {3, 3},
+ .fw_version = 18,
+ .msi_supported = true,
+};
+
+static struct ngene_info ngene_info_m780 = {
+ .type = NGENE_APP,
+ .name = "Aver M780 ATSC/QAM-B",
+
+ /* Channel 0 is analog, which is currently unsupported */
+ .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN },
+ .demod_attach = { NULL, demod_attach_lg330x },
+
+ /* Ensure these are NULL else the frame will call them (as funcs) */
+ .tuner_attach = { 0, 0, 0, 0 },
+ .fe_config = { NULL, &aver_m780 },
+ .avf = { 0 },
+
+ /* A custom electrical interface config for the demod to bridge */
+ .tsf = { 4, 4 },
+ .fw_version = 15,
+};
+
+static struct drxd_config fe_terratec_dvbt_0 = {
+ .index = 0,
+ .demod_address = 0x70,
+ .demod_revision = 0xa2,
+ .demoda_address = 0x00,
+ .pll_address = 0x60,
+ .pll_type = DVB_PLL_THOMSON_DTT7520X,
+ .clock = 20000,
+ .osc_deviation = osc_deviation,
+};
+
+static struct drxd_config fe_terratec_dvbt_1 = {
+ .index = 1,
+ .demod_address = 0x71,
+ .demod_revision = 0xa2,
+ .demoda_address = 0x00,
+ .pll_address = 0x60,
+ .pll_type = DVB_PLL_THOMSON_DTT7520X,
+ .clock = 20000,
+ .osc_deviation = osc_deviation,
+};
+
+static struct ngene_info ngene_info_terratec = {
+ .type = NGENE_TERRATEC,
+ .name = "Terratec Integra/Cinergy2400i Dual DVB-T",
+ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN},
+ .demod_attach = {demod_attach_drxd, demod_attach_drxd},
+ .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1},
+ .i2c_access = 1,
+};
+
+/****************************************************************************/
+
+
+
+/****************************************************************************/
+/* PCI Subsystem ID *********************************************************/
+/****************************************************************************/
+
+#define NGENE_ID(_subvend, _subdev, _driverdata) { \
+ .vendor = NGENE_VID, .device = NGENE_PID, \
+ .subvendor = _subvend, .subdevice = _subdev, \
+ .driver_data = (unsigned long) &_driverdata }
+
+/****************************************************************************/
+
+static const struct pci_device_id ngene_id_tbl[] __devinitdata = {
+ NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2),
+ NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2),
+ NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2),
+ NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2),
+ NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5),
+ NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex),
+ NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex),
+ NGENE_ID(0x1461, 0x062e, ngene_info_m780),
+ NGENE_ID(0x153b, 0x1167, ngene_info_terratec),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, ngene_id_tbl);
+
+/****************************************************************************/
+/* Init/Exit ****************************************************************/
+/****************************************************************************/
+
+static pci_ers_result_t ngene_error_detected(struct pci_dev *dev,
+ enum pci_channel_state state)
+{
+ printk(KERN_ERR DEVICE_NAME ": PCI error\n");
+ if (state == pci_channel_io_perm_failure)
+ return PCI_ERS_RESULT_DISCONNECT;
+ if (state == pci_channel_io_frozen)
+ return PCI_ERS_RESULT_NEED_RESET;
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+static pci_ers_result_t ngene_link_reset(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": link reset\n");
+ return 0;
+}
+
+static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": slot reset\n");
+ return 0;
+}
+
+static void ngene_resume(struct pci_dev *dev)
+{
+ printk(KERN_INFO DEVICE_NAME ": resume\n");
+}
+
+static struct pci_error_handlers ngene_errors = {
+ .error_detected = ngene_error_detected,
+ .link_reset = ngene_link_reset,
+ .slot_reset = ngene_slot_reset,
+ .resume = ngene_resume,
+};
+
+static struct pci_driver ngene_pci_driver = {
+ .name = "ngene",
+ .id_table = ngene_id_tbl,
+ .probe = ngene_probe,
+ .remove = __devexit_p(ngene_remove),
+ .err_handler = &ngene_errors,
+ .shutdown = ngene_shutdown,
+};
+
+static __init int module_init_ngene(void)
+{
+ printk(KERN_INFO
+ "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n");
+ return pci_register_driver(&ngene_pci_driver);
+}
+
+static __exit void module_exit_ngene(void)
+{
+ pci_unregister_driver(&ngene_pci_driver);
+}
+
+module_init(module_init_ngene);
+module_exit(module_exit_ngene);
+
+MODULE_DESCRIPTION("nGene");
+MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c
new file mode 100644
index 000000000000..c8e0d5b99d4c
--- /dev/null
+++ b/drivers/media/pci/ngene/ngene-core.c
@@ -0,0 +1,1707 @@
+/*
+ * ngene.c: nGene PCIe bridge driver
+ *
+ * Copyright (C) 2005-2007 Micronas
+ *
+ * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de>
+ * Modifications for new nGene firmware,
+ * support for EEPROM-copying,
+ * support for new dual DVB-S2 card prototype
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 only, as published by the Free Software Foundation.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/io.h>
+#include <asm/div64.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <linux/byteorder/generic.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+
+#include "ngene.h"
+
+static int one_adapter;
+module_param(one_adapter, int, 0444);
+MODULE_PARM_DESC(one_adapter, "Use only one adapter.");
+
+static int shutdown_workaround;
+module_param(shutdown_workaround, int, 0644);
+MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets.");
+
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Print debugging information.");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk if (debug) printk
+
+#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr)))
+#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr)))
+#define ngreadl(adr) readl(dev->iomem + (adr))
+#define ngreadb(adr) readb(dev->iomem + (adr))
+#define ngcpyto(adr, src, count) memcpy_toio((char *) \
+ (dev->iomem + (adr)), (src), (count))
+#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \
+ (dev->iomem + (adr)), (count))
+
+/****************************************************************************/
+/* nGene interrupt handler **************************************************/
+/****************************************************************************/
+
+static void event_tasklet(unsigned long data)
+{
+ struct ngene *dev = (struct ngene *)data;
+
+ while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) {
+ struct EVENT_BUFFER Event =
+ dev->EventQueue[dev->EventQueueReadIndex];
+ dev->EventQueueReadIndex =
+ (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1);
+
+ if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify))
+ dev->TxEventNotify(dev, Event.TimeStamp);
+ if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify))
+ dev->RxEventNotify(dev, Event.TimeStamp,
+ Event.RXCharacter);
+ }
+}
+
+static void demux_tasklet(unsigned long data)
+{
+ struct ngene_channel *chan = (struct ngene_channel *)data;
+ struct SBufferHeader *Cur = chan->nextBuffer;
+
+ spin_lock_irq(&chan->state_lock);
+
+ while (Cur->ngeneBuffer.SR.Flags & 0x80) {
+ if (chan->mode & NGENE_IO_TSOUT) {
+ u32 Flags = chan->DataFormatFlags;
+ if (Cur->ngeneBuffer.SR.Flags & 0x20)
+ Flags |= BEF_OVERFLOW;
+ if (chan->pBufferExchange) {
+ if (!chan->pBufferExchange(chan,
+ Cur->Buffer1,
+ chan->Capture1Length,
+ Cur->ngeneBuffer.SR.
+ Clock, Flags)) {
+ /*
+ We didn't get data
+ Clear in service flag to make sure we
+ get called on next interrupt again.
+ leave fill/empty (0x80) flag alone
+ to avoid hardware running out of
+ buffers during startup, we hold only
+ in run state ( the source may be late
+ delivering data )
+ */
+
+ if (chan->HWState == HWSTATE_RUN) {
+ Cur->ngeneBuffer.SR.Flags &=
+ ~0x40;
+ break;
+ /* Stop processing stream */
+ }
+ } else {
+ /* We got a valid buffer,
+ so switch to run state */
+ chan->HWState = HWSTATE_RUN;
+ }
+ } else {
+ printk(KERN_ERR DEVICE_NAME ": OOPS\n");
+ if (chan->HWState == HWSTATE_RUN) {
+ Cur->ngeneBuffer.SR.Flags &= ~0x40;
+ break; /* Stop processing stream */
+ }
+ }
+ if (chan->AudioDTOUpdated) {
+ printk(KERN_INFO DEVICE_NAME
+ ": Update AudioDTO = %d\n",
+ chan->AudioDTOValue);
+ Cur->ngeneBuffer.SR.DTOUpdate =
+ chan->AudioDTOValue;
+ chan->AudioDTOUpdated = 0;
+ }
+ } else {
+ if (chan->HWState == HWSTATE_RUN) {
+ u32 Flags = chan->DataFormatFlags;
+ IBufferExchange *exch1 = chan->pBufferExchange;
+ IBufferExchange *exch2 = chan->pBufferExchange2;
+ if (Cur->ngeneBuffer.SR.Flags & 0x01)
+ Flags |= BEF_EVEN_FIELD;
+ if (Cur->ngeneBuffer.SR.Flags & 0x20)
+ Flags |= BEF_OVERFLOW;
+ spin_unlock_irq(&chan->state_lock);
+ if (exch1)
+ exch1(chan, Cur->Buffer1,
+ chan->Capture1Length,
+ Cur->ngeneBuffer.SR.Clock,
+ Flags);
+ if (exch2)
+ exch2(chan, Cur->Buffer2,
+ chan->Capture2Length,
+ Cur->ngeneBuffer.SR.Clock,
+ Flags);
+ spin_lock_irq(&chan->state_lock);
+ } else if (chan->HWState != HWSTATE_STOP)
+ chan->HWState = HWSTATE_RUN;
+ }
+ Cur->ngeneBuffer.SR.Flags = 0x00;
+ Cur = Cur->Next;
+ }
+ chan->nextBuffer = Cur;
+
+ spin_unlock_irq(&chan->state_lock);
+}
+
+static irqreturn_t irq_handler(int irq, void *dev_id)
+{
+ struct ngene *dev = (struct ngene *)dev_id;
+ u32 icounts = 0;
+ irqreturn_t rc = IRQ_NONE;
+ u32 i = MAX_STREAM;
+ u8 *tmpCmdDoneByte;
+
+ if (dev->BootFirmware) {
+ icounts = ngreadl(NGENE_INT_COUNTS);
+ if (icounts != dev->icounts) {
+ ngwritel(0, FORCE_NMI);
+ dev->cmd_done = 1;
+ wake_up(&dev->cmd_wq);
+ dev->icounts = icounts;
+ rc = IRQ_HANDLED;
+ }
+ return rc;
+ }
+
+ ngwritel(0, FORCE_NMI);
+
+ spin_lock(&dev->cmd_lock);
+ tmpCmdDoneByte = dev->CmdDoneByte;
+ if (tmpCmdDoneByte &&
+ (*tmpCmdDoneByte ||
+ (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) {
+ dev->CmdDoneByte = NULL;
+ dev->cmd_done = 1;
+ wake_up(&dev->cmd_wq);
+ rc = IRQ_HANDLED;
+ }
+ spin_unlock(&dev->cmd_lock);
+
+ if (dev->EventBuffer->EventStatus & 0x80) {
+ u8 nextWriteIndex =
+ (dev->EventQueueWriteIndex + 1) &
+ (EVENT_QUEUE_SIZE - 1);
+ if (nextWriteIndex != dev->EventQueueReadIndex) {
+ dev->EventQueue[dev->EventQueueWriteIndex] =
+ *(dev->EventBuffer);
+ dev->EventQueueWriteIndex = nextWriteIndex;
+ } else {
+ printk(KERN_ERR DEVICE_NAME ": event overflow\n");
+ dev->EventQueueOverflowCount += 1;
+ dev->EventQueueOverflowFlag = 1;
+ }
+ dev->EventBuffer->EventStatus &= ~0x80;
+ tasklet_schedule(&dev->event_tasklet);
+ rc = IRQ_HANDLED;
+ }
+
+ while (i > 0) {
+ i--;
+ spin_lock(&dev->channel[i].state_lock);
+ /* if (dev->channel[i].State>=KSSTATE_RUN) { */
+ if (dev->channel[i].nextBuffer) {
+ if ((dev->channel[i].nextBuffer->
+ ngeneBuffer.SR.Flags & 0xC0) == 0x80) {
+ dev->channel[i].nextBuffer->
+ ngeneBuffer.SR.Flags |= 0x40;
+ tasklet_schedule(
+ &dev->channel[i].demux_tasklet);
+ rc = IRQ_HANDLED;
+ }
+ }
+ spin_unlock(&dev->channel[i].state_lock);
+ }
+
+ /* Request might have been processed by a previous call. */
+ return IRQ_HANDLED;
+}
+
+/****************************************************************************/
+/* nGene command interface **************************************************/
+/****************************************************************************/
+
+static void dump_command_io(struct ngene *dev)
+{
+ u8 buf[8], *b;
+
+ ngcpyfrom(buf, HOST_TO_NGENE, 8);
+ printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf);
+
+ ngcpyfrom(buf, NGENE_TO_HOST, 8);
+ printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf);
+
+ b = dev->hosttongene;
+ printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b);
+
+ b = dev->ngenetohost;
+ printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b);
+}
+
+static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com)
+{
+ int ret;
+ u8 *tmpCmdDoneByte;
+
+ dev->cmd_done = 0;
+
+ if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) {
+ dev->BootFirmware = 1;
+ dev->icounts = ngreadl(NGENE_INT_COUNTS);
+ ngwritel(0, NGENE_COMMAND);
+ ngwritel(0, NGENE_COMMAND_HI);
+ ngwritel(0, NGENE_STATUS);
+ ngwritel(0, NGENE_STATUS_HI);
+ ngwritel(0, NGENE_EVENT);
+ ngwritel(0, NGENE_EVENT_HI);
+ } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) {
+ u64 fwio = dev->PAFWInterfaceBuffer;
+
+ ngwritel(fwio & 0xffffffff, NGENE_COMMAND);
+ ngwritel(fwio >> 32, NGENE_COMMAND_HI);
+ ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS);
+ ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI);
+ ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT);
+ ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI);
+ }
+
+ memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2);
+
+ if (dev->BootFirmware)
+ ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2);
+
+ spin_lock_irq(&dev->cmd_lock);
+ tmpCmdDoneByte = dev->ngenetohost + com->out_len;
+ if (!com->out_len)
+ tmpCmdDoneByte++;
+ *tmpCmdDoneByte = 0;
+ dev->ngenetohost[0] = 0;
+ dev->ngenetohost[1] = 0;
+ dev->CmdDoneByte = tmpCmdDoneByte;
+ spin_unlock_irq(&dev->cmd_lock);
+
+ /* Notify 8051. */
+ ngwritel(1, FORCE_INT);
+
+ ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ);
+ if (!ret) {
+ /*ngwritel(0, FORCE_NMI);*/
+
+ printk(KERN_ERR DEVICE_NAME
+ ": Command timeout cmd=%02x prev=%02x\n",
+ com->cmd.hdr.Opcode, dev->prev_cmd);
+ dump_command_io(dev);
+ return -1;
+ }
+ if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH)
+ dev->BootFirmware = 0;
+
+ dev->prev_cmd = com->cmd.hdr.Opcode;
+
+ if (!com->out_len)
+ return 0;
+
+ memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len);
+
+ return 0;
+}
+
+int ngene_command(struct ngene *dev, struct ngene_command *com)
+{
+ int result;
+
+ down(&dev->cmd_mutex);
+ result = ngene_command_mutex(dev, com);
+ up(&dev->cmd_mutex);
+ return result;
+}
+
+
+static int ngene_command_load_firmware(struct ngene *dev,
+ u8 *ngene_fw, u32 size)
+{
+#define FIRSTCHUNK (1024)
+ u32 cleft;
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE;
+ com.cmd.hdr.Length = 0;
+ com.in_len = 0;
+ com.out_len = 0;
+
+ ngene_command(dev, &com);
+
+ cleft = (size + 3) & ~3;
+ if (cleft > FIRSTCHUNK) {
+ ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK,
+ cleft - FIRSTCHUNK);
+ cleft = FIRSTCHUNK;
+ }
+ ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft);
+
+ memset(&com, 0, sizeof(struct ngene_command));
+ com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH;
+ com.cmd.hdr.Length = 4;
+ com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA;
+ com.cmd.FWLoadFinish.Length = (unsigned short)cleft;
+ com.in_len = 4;
+ com.out_len = 0;
+
+ return ngene_command(dev, &com);
+}
+
+
+static int ngene_command_config_buf(struct ngene *dev, u8 config)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER;
+ com.cmd.hdr.Length = 1;
+ com.cmd.ConfigureBuffers.config = config;
+ com.in_len = 1;
+ com.out_len = 0;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+ return 0;
+}
+
+static int ngene_command_config_free_buf(struct ngene *dev, u8 *config)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER;
+ com.cmd.hdr.Length = 6;
+ memcpy(&com.cmd.ConfigureBuffers.config, config, 6);
+ com.in_len = 6;
+ com.out_len = 0;
+
+ if (ngene_command(dev, &com) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN;
+ com.cmd.hdr.Length = 1;
+ com.cmd.SetGpioPin.select = select | (level << 7);
+ com.in_len = 1;
+ com.out_len = 0;
+
+ return ngene_command(dev, &com);
+}
+
+
+/*
+ 02000640 is sample on rising edge.
+ 02000740 is sample on falling edge.
+ 02000040 is ignore "valid" signal
+
+ 0: FD_CTL1 Bit 7,6 must be 0,1
+ 7 disable(fw controlled)
+ 6 0-AUX,1-TS
+ 5 0-par,1-ser
+ 4 0-lsb/1-msb
+ 3,2 reserved
+ 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both
+ 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge
+ 2: FD_STA is read-only. 0-sync
+ 3: FD_INSYNC is number of 47s to trigger "in sync".
+ 4: FD_OUTSYNC is number of 47s to trigger "out of sync".
+ 5: FD_MAXBYTE1 is low-order of bytes per packet.
+ 6: FD_MAXBYTE2 is high-order of bytes per packet.
+ 7: Top byte is unused.
+*/
+
+/****************************************************************************/
+
+static u8 TSFeatureDecoderSetup[8 * 5] = {
+ 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00,
+ 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */
+ 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */
+ 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */
+ 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */
+};
+
+/* Set NGENE I2S Config to 16 bit packed */
+static u8 I2SConfiguration[] = {
+ 0x00, 0x10, 0x00, 0x00,
+ 0x80, 0x10, 0x00, 0x00,
+};
+
+static u8 SPDIFConfiguration[10] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* Set NGENE I2S Config to transport stream compatible mode */
+
+static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 };
+
+static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 };
+
+static u8 ITUDecoderSetup[4][16] = {
+ {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */
+ 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00},
+ {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+ {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+ {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */
+ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00},
+};
+
+/*
+ * 50 48 60 gleich
+ * 27p50 9f 00 22 80 42 69 18 ...
+ * 27p60 93 00 22 80 82 69 1c ...
+ */
+
+/* Maxbyte to 1144 (for raw data) */
+static u8 ITUFeatureDecoderSetup[8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00
+};
+
+void FillTSBuffer(void *Buffer, int Length, u32 Flags)
+{
+ u32 *ptr = Buffer;
+
+ memset(Buffer, TS_FILLER, Length);
+ while (Length > 0) {
+ if (Flags & DF_SWAP32)
+ *ptr = 0x471FFF10;
+ else
+ *ptr = 0x10FF1F47;
+ ptr += (188 / 4);
+ Length -= 188;
+ }
+}
+
+
+static void flush_buffers(struct ngene_channel *chan)
+{
+ u8 val;
+
+ do {
+ msleep(1);
+ spin_lock_irq(&chan->state_lock);
+ val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80;
+ spin_unlock_irq(&chan->state_lock);
+ } while (val);
+}
+
+static void clear_buffers(struct ngene_channel *chan)
+{
+ struct SBufferHeader *Cur = chan->nextBuffer;
+
+ do {
+ memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR));
+ if (chan->mode & NGENE_IO_TSOUT)
+ FillTSBuffer(Cur->Buffer1,
+ chan->Capture1Length,
+ chan->DataFormatFlags);
+ Cur = Cur->Next;
+ } while (Cur != chan->nextBuffer);
+
+ if (chan->mode & NGENE_IO_TSOUT) {
+ chan->nextBuffer->ngeneBuffer.SR.DTOUpdate =
+ chan->AudioDTOValue;
+ chan->AudioDTOUpdated = 0;
+
+ Cur = chan->TSIdleBuffer.Head;
+
+ do {
+ memset(&Cur->ngeneBuffer.SR, 0,
+ sizeof(Cur->ngeneBuffer.SR));
+ FillTSBuffer(Cur->Buffer1,
+ chan->Capture1Length,
+ chan->DataFormatFlags);
+ Cur = Cur->Next;
+ } while (Cur != chan->TSIdleBuffer.Head);
+ }
+}
+
+static int ngene_command_stream_control(struct ngene *dev, u8 stream,
+ u8 control, u8 mode, u8 flags)
+{
+ struct ngene_channel *chan = &dev->channel[stream];
+ struct ngene_command com;
+ u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300);
+ u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500);
+ u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700);
+ u16 BsSDO = 0x9B00;
+
+ down(&dev->stream_mutex);
+ memset(&com, 0, sizeof(com));
+ com.cmd.hdr.Opcode = CMD_CONTROL;
+ com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2;
+ com.cmd.StreamControl.Stream = stream | (control ? 8 : 0);
+ if (chan->mode & NGENE_IO_TSOUT)
+ com.cmd.StreamControl.Stream |= 0x07;
+ com.cmd.StreamControl.Control = control |
+ (flags & SFLAG_ORDER_LUMA_CHROMA);
+ com.cmd.StreamControl.Mode = mode;
+ com.in_len = sizeof(struct FW_STREAM_CONTROL);
+ com.out_len = 0;
+
+ dprintk(KERN_INFO DEVICE_NAME
+ ": Stream=%02x, Control=%02x, Mode=%02x\n",
+ com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control,
+ com.cmd.StreamControl.Mode);
+
+ chan->Mode = mode;
+
+ if (!(control & 0x80)) {
+ spin_lock_irq(&chan->state_lock);
+ if (chan->State == KSSTATE_RUN) {
+ chan->State = KSSTATE_ACQUIRE;
+ chan->HWState = HWSTATE_STOP;
+ spin_unlock_irq(&chan->state_lock);
+ if (ngene_command(dev, &com) < 0) {
+ up(&dev->stream_mutex);
+ return -1;
+ }
+ /* clear_buffers(chan); */
+ flush_buffers(chan);
+ up(&dev->stream_mutex);
+ return 0;
+ }
+ spin_unlock_irq(&chan->state_lock);
+ up(&dev->stream_mutex);
+ return 0;
+ }
+
+ if (mode & SMODE_AUDIO_CAPTURE) {
+ com.cmd.StreamControl.CaptureBlockCount =
+ chan->Capture1Length / AUDIO_BLOCK_SIZE;
+ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+ } else if (mode & SMODE_TRANSPORT_STREAM) {
+ com.cmd.StreamControl.CaptureBlockCount =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.MaxLinesPerField =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.Buffer_Address =
+ chan->TSRingBuffer.PAHead;
+ if (chan->mode & NGENE_IO_TSOUT) {
+ com.cmd.StreamControl.BytesPerVBILine =
+ chan->Capture1Length / TS_BLOCK_SIZE;
+ com.cmd.StreamControl.Stream |= 0x07;
+ }
+ } else {
+ com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine;
+ com.cmd.StreamControl.MaxLinesPerField = chan->nLines;
+ com.cmd.StreamControl.MinLinesPerField = 100;
+ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead;
+
+ if (mode & SMODE_VBI_CAPTURE) {
+ com.cmd.StreamControl.MaxVBILinesPerField =
+ chan->nVBILines;
+ com.cmd.StreamControl.MinVBILinesPerField = 0;
+ com.cmd.StreamControl.BytesPerVBILine =
+ chan->nBytesPerVBILine;
+ }
+ if (flags & SFLAG_COLORBAR)
+ com.cmd.StreamControl.Stream |= 0x04;
+ }
+
+ spin_lock_irq(&chan->state_lock);
+ if (mode & SMODE_AUDIO_CAPTURE) {
+ chan->nextBuffer = chan->RingBuffer.Head;
+ if (mode & SMODE_AUDIO_SPDIF) {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(SPDIFConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSPI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ SPDIFConfiguration, sizeof(SPDIFConfiguration));
+ } else {
+ com.cmd.StreamControl.SetupDataLen = 4;
+ com.cmd.StreamControl.SetupDataAddr = BsSDI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ I2SConfiguration +
+ 4 * dev->card_info->i2s[stream], 4);
+ }
+ } else if (mode & SMODE_TRANSPORT_STREAM) {
+ chan->nextBuffer = chan->TSRingBuffer.Head;
+ if (stream >= STREAM_AUDIOIN1) {
+ if (chan->mode & NGENE_IO_TSOUT) {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(TS_I2SOutConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSDO;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TS_I2SOutConfiguration,
+ sizeof(TS_I2SOutConfiguration));
+ } else {
+ com.cmd.StreamControl.SetupDataLen =
+ sizeof(TS_I2SConfiguration);
+ com.cmd.StreamControl.SetupDataAddr = BsSDI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TS_I2SConfiguration,
+ sizeof(TS_I2SConfiguration));
+ }
+ } else {
+ com.cmd.StreamControl.SetupDataLen = 8;
+ com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10;
+ memcpy(com.cmd.StreamControl.SetupData,
+ TSFeatureDecoderSetup +
+ 8 * dev->card_info->tsf[stream], 8);
+ }
+ } else {
+ chan->nextBuffer = chan->RingBuffer.Head;
+ com.cmd.StreamControl.SetupDataLen =
+ 16 + sizeof(ITUFeatureDecoderSetup);
+ com.cmd.StreamControl.SetupDataAddr = BsUVI;
+ memcpy(com.cmd.StreamControl.SetupData,
+ ITUDecoderSetup[chan->itumode], 16);
+ memcpy(com.cmd.StreamControl.SetupData + 16,
+ ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup));
+ }
+ clear_buffers(chan);
+ chan->State = KSSTATE_RUN;
+ if (mode & SMODE_TRANSPORT_STREAM)
+ chan->HWState = HWSTATE_RUN;
+ else
+ chan->HWState = HWSTATE_STARTUP;
+ spin_unlock_irq(&chan->state_lock);
+
+ if (ngene_command(dev, &com) < 0) {
+ up(&dev->stream_mutex);
+ return -1;
+ }
+ up(&dev->stream_mutex);
+ return 0;
+}
+
+void set_transfer(struct ngene_channel *chan, int state)
+{
+ u8 control = 0, mode = 0, flags = 0;
+ struct ngene *dev = chan->dev;
+ int ret;
+
+ /*
+ printk(KERN_INFO DEVICE_NAME ": st %d\n", state);
+ msleep(100);
+ */
+
+ if (state) {
+ if (chan->running) {
+ printk(KERN_INFO DEVICE_NAME ": already running\n");
+ return;
+ }
+ } else {
+ if (!chan->running) {
+ printk(KERN_INFO DEVICE_NAME ": already stopped\n");
+ return;
+ }
+ }
+
+ if (dev->card_info->switch_ctrl)
+ dev->card_info->switch_ctrl(chan, 1, state ^ 1);
+
+ if (state) {
+ spin_lock_irq(&chan->state_lock);
+
+ /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ ngreadl(0x9310)); */
+ dvb_ringbuffer_flush(&dev->tsout_rbuf);
+ control = 0x80;
+ if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+ chan->Capture1Length = 512 * 188;
+ mode = SMODE_TRANSPORT_STREAM;
+ }
+ if (chan->mode & NGENE_IO_TSOUT) {
+ chan->pBufferExchange = tsout_exchange;
+ /* 0x66666666 = 50MHz *2^33 /250MHz */
+ chan->AudioDTOValue = 0x80000000;
+ chan->AudioDTOUpdated = 1;
+ }
+ if (chan->mode & NGENE_IO_TSIN)
+ chan->pBufferExchange = tsin_exchange;
+ spin_unlock_irq(&chan->state_lock);
+ } else
+ ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n",
+ ngreadl(0x9310)); */
+
+ ret = ngene_command_stream_control(dev, chan->number,
+ control, mode, flags);
+ if (!ret)
+ chan->running = state;
+ else
+ printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n",
+ state);
+ if (!state) {
+ spin_lock_irq(&chan->state_lock);
+ chan->pBufferExchange = NULL;
+ dvb_ringbuffer_flush(&dev->tsout_rbuf);
+ spin_unlock_irq(&chan->state_lock);
+ }
+}
+
+
+/****************************************************************************/
+/* nGene hardware init and release functions ********************************/
+/****************************************************************************/
+
+static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb)
+{
+ struct SBufferHeader *Cur = rb->Head;
+ u32 j;
+
+ if (!Cur)
+ return;
+
+ for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) {
+ if (Cur->Buffer1)
+ pci_free_consistent(dev->pci_dev,
+ rb->Buffer1Length,
+ Cur->Buffer1,
+ Cur->scList1->Address);
+
+ if (Cur->Buffer2)
+ pci_free_consistent(dev->pci_dev,
+ rb->Buffer2Length,
+ Cur->Buffer2,
+ Cur->scList2->Address);
+ }
+
+ if (rb->SCListMem)
+ pci_free_consistent(dev->pci_dev, rb->SCListMemSize,
+ rb->SCListMem, rb->PASCListMem);
+
+ pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead);
+}
+
+static void free_idlebuffer(struct ngene *dev,
+ struct SRingBufferDescriptor *rb,
+ struct SRingBufferDescriptor *tb)
+{
+ int j;
+ struct SBufferHeader *Cur = tb->Head;
+
+ if (!rb->Head)
+ return;
+ free_ringbuffer(dev, rb);
+ for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) {
+ Cur->Buffer2 = NULL;
+ Cur->scList2 = NULL;
+ Cur->ngeneBuffer.Address_of_first_entry_2 = 0;
+ Cur->ngeneBuffer.Number_of_entries_2 = 0;
+ }
+}
+
+static void free_common_buffers(struct ngene *dev)
+{
+ u32 i;
+ struct ngene_channel *chan;
+
+ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+ chan = &dev->channel[i];
+ free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer);
+ free_ringbuffer(dev, &chan->RingBuffer);
+ free_ringbuffer(dev, &chan->TSRingBuffer);
+ }
+
+ if (dev->OverflowBuffer)
+ pci_free_consistent(dev->pci_dev,
+ OVERFLOW_BUFFER_SIZE,
+ dev->OverflowBuffer, dev->PAOverflowBuffer);
+
+ if (dev->FWInterfaceBuffer)
+ pci_free_consistent(dev->pci_dev,
+ 4096,
+ dev->FWInterfaceBuffer,
+ dev->PAFWInterfaceBuffer);
+}
+
+/****************************************************************************/
+/* Ring buffer handling *****************************************************/
+/****************************************************************************/
+
+static int create_ring_buffer(struct pci_dev *pci_dev,
+ struct SRingBufferDescriptor *descr, u32 NumBuffers)
+{
+ dma_addr_t tmp;
+ struct SBufferHeader *Head;
+ u32 i;
+ u32 MemSize = SIZEOF_SBufferHeader * NumBuffers;
+ u64 PARingBufferHead;
+ u64 PARingBufferCur;
+ u64 PARingBufferNext;
+ struct SBufferHeader *Cur, *Next;
+
+ descr->Head = NULL;
+ descr->MemSize = 0;
+ descr->PAHead = 0;
+ descr->NumBuffers = 0;
+
+ if (MemSize < 4096)
+ MemSize = 4096;
+
+ Head = pci_alloc_consistent(pci_dev, MemSize, &tmp);
+ PARingBufferHead = tmp;
+
+ if (!Head)
+ return -ENOMEM;
+
+ memset(Head, 0, MemSize);
+
+ PARingBufferCur = PARingBufferHead;
+ Cur = Head;
+
+ for (i = 0; i < NumBuffers - 1; i++) {
+ Next = (struct SBufferHeader *)
+ (((u8 *) Cur) + SIZEOF_SBufferHeader);
+ PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader;
+ Cur->Next = Next;
+ Cur->ngeneBuffer.Next = PARingBufferNext;
+ Cur = Next;
+ PARingBufferCur = PARingBufferNext;
+ }
+ /* Last Buffer points back to first one */
+ Cur->Next = Head;
+ Cur->ngeneBuffer.Next = PARingBufferHead;
+
+ descr->Head = Head;
+ descr->MemSize = MemSize;
+ descr->PAHead = PARingBufferHead;
+ descr->NumBuffers = NumBuffers;
+
+ return 0;
+}
+
+static int AllocateRingBuffers(struct pci_dev *pci_dev,
+ dma_addr_t of,
+ struct SRingBufferDescriptor *pRingBuffer,
+ u32 Buffer1Length, u32 Buffer2Length)
+{
+ dma_addr_t tmp;
+ u32 i, j;
+ int status = 0;
+ u32 SCListMemSize = pRingBuffer->NumBuffers
+ * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) :
+ NUM_SCATTER_GATHER_ENTRIES)
+ * sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+ u64 PASCListMem;
+ struct HW_SCATTER_GATHER_ELEMENT *SCListEntry;
+ u64 PASCListEntry;
+ struct SBufferHeader *Cur;
+ void *SCListMem;
+
+ if (SCListMemSize < 4096)
+ SCListMemSize = 4096;
+
+ SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp);
+
+ PASCListMem = tmp;
+ if (SCListMem == NULL)
+ return -ENOMEM;
+
+ memset(SCListMem, 0, SCListMemSize);
+
+ pRingBuffer->SCListMem = SCListMem;
+ pRingBuffer->PASCListMem = PASCListMem;
+ pRingBuffer->SCListMemSize = SCListMemSize;
+ pRingBuffer->Buffer1Length = Buffer1Length;
+ pRingBuffer->Buffer2Length = Buffer2Length;
+
+ SCListEntry = SCListMem;
+ PASCListEntry = PASCListMem;
+ Cur = pRingBuffer->Head;
+
+ for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) {
+ u64 PABuffer;
+
+ void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length,
+ &tmp);
+ PABuffer = tmp;
+
+ if (Buffer == NULL)
+ return -ENOMEM;
+
+ Cur->Buffer1 = Buffer;
+
+ SCListEntry->Address = PABuffer;
+ SCListEntry->Length = Buffer1Length;
+
+ Cur->scList1 = SCListEntry;
+ Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry;
+ Cur->ngeneBuffer.Number_of_entries_1 =
+ NUM_SCATTER_GATHER_ENTRIES;
+
+ SCListEntry += 1;
+ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) {
+ SCListEntry->Address = of;
+ SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+ SCListEntry += 1;
+ PASCListEntry +=
+ sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+ }
+#endif
+
+ if (!Buffer2Length)
+ continue;
+
+ Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp);
+ PABuffer = tmp;
+
+ if (Buffer == NULL)
+ return -ENOMEM;
+
+ Cur->Buffer2 = Buffer;
+
+ SCListEntry->Address = PABuffer;
+ SCListEntry->Length = Buffer2Length;
+
+ Cur->scList2 = SCListEntry;
+ Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry;
+ Cur->ngeneBuffer.Number_of_entries_2 =
+ NUM_SCATTER_GATHER_ENTRIES;
+
+ SCListEntry += 1;
+ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+
+#if NUM_SCATTER_GATHER_ENTRIES > 1
+ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) {
+ SCListEntry->Address = of;
+ SCListEntry->Length = OVERFLOW_BUFFER_SIZE;
+ SCListEntry += 1;
+ PASCListEntry +=
+ sizeof(struct HW_SCATTER_GATHER_ELEMENT);
+ }
+#endif
+
+ }
+
+ return status;
+}
+
+static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer,
+ struct SRingBufferDescriptor *pRingBuffer)
+{
+ int status = 0;
+
+ /* Copy pointer to scatter gather list in TSRingbuffer
+ structure for buffer 2
+ Load number of buffer
+ */
+ u32 n = pRingBuffer->NumBuffers;
+
+ /* Point to first buffer entry */
+ struct SBufferHeader *Cur = pRingBuffer->Head;
+ int i;
+ /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */
+ for (i = 0; i < n; i++) {
+ Cur->Buffer2 = pIdleBuffer->Head->Buffer1;
+ Cur->scList2 = pIdleBuffer->Head->scList1;
+ Cur->ngeneBuffer.Address_of_first_entry_2 =
+ pIdleBuffer->Head->ngeneBuffer.
+ Address_of_first_entry_1;
+ Cur->ngeneBuffer.Number_of_entries_2 =
+ pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1;
+ Cur = Cur->Next;
+ }
+ return status;
+}
+
+static u32 RingBufferSizes[MAX_STREAM] = {
+ RING_SIZE_VIDEO,
+ RING_SIZE_VIDEO,
+ RING_SIZE_AUDIO,
+ RING_SIZE_AUDIO,
+ RING_SIZE_AUDIO,
+};
+
+static u32 Buffer1Sizes[MAX_STREAM] = {
+ MAX_VIDEO_BUFFER_SIZE,
+ MAX_VIDEO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE,
+ MAX_AUDIO_BUFFER_SIZE
+};
+
+static u32 Buffer2Sizes[MAX_STREAM] = {
+ MAX_VBI_BUFFER_SIZE,
+ MAX_VBI_BUFFER_SIZE,
+ 0,
+ 0,
+ 0
+};
+
+
+static int AllocCommonBuffers(struct ngene *dev)
+{
+ int status = 0, i;
+
+ dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096,
+ &dev->PAFWInterfaceBuffer);
+ if (!dev->FWInterfaceBuffer)
+ return -ENOMEM;
+ dev->hosttongene = dev->FWInterfaceBuffer;
+ dev->ngenetohost = dev->FWInterfaceBuffer + 256;
+ dev->EventBuffer = dev->FWInterfaceBuffer + 512;
+
+ dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev,
+ OVERFLOW_BUFFER_SIZE,
+ &dev->PAOverflowBuffer);
+ if (!dev->OverflowBuffer)
+ return -ENOMEM;
+ memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE);
+
+ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) {
+ int type = dev->card_info->io_type[i];
+
+ dev->channel[i].State = KSSTATE_STOP;
+
+ if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) {
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].RingBuffer,
+ RingBufferSizes[i]);
+ if (status < 0)
+ break;
+
+ if (type & (NGENE_IO_TV | NGENE_IO_AIN)) {
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->
+ PAOverflowBuffer,
+ &dev->channel[i].
+ RingBuffer,
+ Buffer1Sizes[i],
+ Buffer2Sizes[i]);
+ if (status < 0)
+ break;
+ } else if (type & NGENE_IO_HDTV) {
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->
+ PAOverflowBuffer,
+ &dev->channel[i].
+ RingBuffer,
+ MAX_HDTV_BUFFER_SIZE,
+ 0);
+ if (status < 0)
+ break;
+ }
+ }
+
+ if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].
+ TSRingBuffer, RING_SIZE_TS);
+ if (status < 0)
+ break;
+
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->PAOverflowBuffer,
+ &dev->channel[i].
+ TSRingBuffer,
+ MAX_TS_BUFFER_SIZE, 0);
+ if (status)
+ break;
+ }
+
+ if (type & NGENE_IO_TSOUT) {
+ status = create_ring_buffer(dev->pci_dev,
+ &dev->channel[i].
+ TSIdleBuffer, 1);
+ if (status < 0)
+ break;
+ status = AllocateRingBuffers(dev->pci_dev,
+ dev->PAOverflowBuffer,
+ &dev->channel[i].
+ TSIdleBuffer,
+ MAX_TS_BUFFER_SIZE, 0);
+ if (status)
+ break;
+ FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer,
+ &dev->channel[i].TSRingBuffer);
+ }
+ }
+ return status;
+}
+
+static void ngene_release_buffers(struct ngene *dev)
+{
+ if (dev->iomem)
+ iounmap(dev->iomem);
+ free_common_buffers(dev);
+ vfree(dev->tsout_buf);
+ vfree(dev->tsin_buf);
+ vfree(dev->ain_buf);
+ vfree(dev->vin_buf);
+ vfree(dev);
+}
+
+static int ngene_get_buffers(struct ngene *dev)
+{
+ if (AllocCommonBuffers(dev))
+ return -ENOMEM;
+ if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) {
+ dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE);
+ if (!dev->tsout_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->tsout_rbuf,
+ dev->tsout_buf, TSOUT_BUF_SIZE);
+ }
+ if (dev->card_info->io_type[2]&NGENE_IO_TSIN) {
+ dev->tsin_buf = vmalloc(TSIN_BUF_SIZE);
+ if (!dev->tsin_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->tsin_rbuf,
+ dev->tsin_buf, TSIN_BUF_SIZE);
+ }
+ if (dev->card_info->io_type[2] & NGENE_IO_AIN) {
+ dev->ain_buf = vmalloc(AIN_BUF_SIZE);
+ if (!dev->ain_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE);
+ }
+ if (dev->card_info->io_type[0] & NGENE_IO_HDTV) {
+ dev->vin_buf = vmalloc(VIN_BUF_SIZE);
+ if (!dev->vin_buf)
+ return -ENOMEM;
+ dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE);
+ }
+ dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0),
+ pci_resource_len(dev->pci_dev, 0));
+ if (!dev->iomem)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void ngene_init(struct ngene *dev)
+{
+ int i;
+
+ tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev);
+
+ memset_io(dev->iomem + 0xc000, 0x00, 0x220);
+ memset_io(dev->iomem + 0xc400, 0x00, 0x100);
+
+ for (i = 0; i < MAX_STREAM; i++) {
+ dev->channel[i].dev = dev;
+ dev->channel[i].number = i;
+ }
+
+ dev->fw_interface_version = 0;
+
+ ngwritel(0, NGENE_INT_ENABLE);
+
+ dev->icounts = ngreadl(NGENE_INT_COUNTS);
+
+ dev->device_version = ngreadl(DEV_VER) & 0x0f;
+ printk(KERN_INFO DEVICE_NAME ": Device version %d\n",
+ dev->device_version);
+}
+
+static int ngene_load_firm(struct ngene *dev)
+{
+ u32 size;
+ const struct firmware *fw = NULL;
+ u8 *ngene_fw;
+ char *fw_name;
+ int err, version;
+
+ version = dev->card_info->fw_version;
+
+ switch (version) {
+ default:
+ case 15:
+ version = 15;
+ size = 23466;
+ fw_name = "ngene_15.fw";
+ dev->cmd_timeout_workaround = true;
+ break;
+ case 16:
+ size = 23498;
+ fw_name = "ngene_16.fw";
+ dev->cmd_timeout_workaround = true;
+ break;
+ case 17:
+ size = 24446;
+ fw_name = "ngene_17.fw";
+ dev->cmd_timeout_workaround = true;
+ break;
+ case 18:
+ size = 0;
+ fw_name = "ngene_18.fw";
+ break;
+ }
+
+ if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) {
+ printk(KERN_ERR DEVICE_NAME
+ ": Could not load firmware file %s.\n", fw_name);
+ printk(KERN_INFO DEVICE_NAME
+ ": Copy %s to your hotplug directory!\n", fw_name);
+ return -1;
+ }
+ if (size == 0)
+ size = fw->size;
+ if (size != fw->size) {
+ printk(KERN_ERR DEVICE_NAME
+ ": Firmware %s has invalid size!", fw_name);
+ err = -1;
+ } else {
+ printk(KERN_INFO DEVICE_NAME
+ ": Loading firmware file %s.\n", fw_name);
+ ngene_fw = (u8 *) fw->data;
+ err = ngene_command_load_firmware(dev, ngene_fw, size);
+ }
+
+ release_firmware(fw);
+
+ return err;
+}
+
+static void ngene_stop(struct ngene *dev)
+{
+ down(&dev->cmd_mutex);
+ i2c_del_adapter(&(dev->channel[0].i2c_adapter));
+ i2c_del_adapter(&(dev->channel[1].i2c_adapter));
+ ngwritel(0, NGENE_INT_ENABLE);
+ ngwritel(0, NGENE_COMMAND);
+ ngwritel(0, NGENE_COMMAND_HI);
+ ngwritel(0, NGENE_STATUS);
+ ngwritel(0, NGENE_STATUS_HI);
+ ngwritel(0, NGENE_EVENT);
+ ngwritel(0, NGENE_EVENT_HI);
+ free_irq(dev->pci_dev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+ if (dev->msi_enabled)
+ pci_disable_msi(dev->pci_dev);
+#endif
+}
+
+static int ngene_buffer_config(struct ngene *dev)
+{
+ int stat;
+
+ if (dev->card_info->fw_version >= 17) {
+ u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 };
+ u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 };
+ u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 };
+ u8 *bconf = tsin12_config;
+
+ if (dev->card_info->io_type[2]&NGENE_IO_TSIN &&
+ dev->card_info->io_type[3]&NGENE_IO_TSIN) {
+ bconf = tsin1234_config;
+ if (dev->card_info->io_type[4]&NGENE_IO_TSOUT &&
+ dev->ci.en)
+ bconf = tsio1235_config;
+ }
+ stat = ngene_command_config_free_buf(dev, bconf);
+ } else {
+ int bconf = BUFFER_CONFIG_4422;
+
+ if (dev->card_info->io_type[3] == NGENE_IO_TSIN)
+ bconf = BUFFER_CONFIG_3333;
+ stat = ngene_command_config_buf(dev, bconf);
+ }
+ return stat;
+}
+
+
+static int ngene_start(struct ngene *dev)
+{
+ int stat;
+ int i;
+
+ pci_set_master(dev->pci_dev);
+ ngene_init(dev);
+
+ stat = request_irq(dev->pci_dev->irq, irq_handler,
+ IRQF_SHARED, "nGene",
+ (void *)dev);
+ if (stat < 0)
+ return stat;
+
+ init_waitqueue_head(&dev->cmd_wq);
+ init_waitqueue_head(&dev->tx_wq);
+ init_waitqueue_head(&dev->rx_wq);
+ sema_init(&dev->cmd_mutex, 1);
+ sema_init(&dev->stream_mutex, 1);
+ sema_init(&dev->pll_mutex, 1);
+ sema_init(&dev->i2c_switch_mutex, 1);
+ spin_lock_init(&dev->cmd_lock);
+ for (i = 0; i < MAX_STREAM; i++)
+ spin_lock_init(&dev->channel[i].state_lock);
+ ngwritel(1, TIMESTAMPS);
+
+ ngwritel(1, NGENE_INT_ENABLE);
+
+ stat = ngene_load_firm(dev);
+ if (stat < 0)
+ goto fail;
+
+#ifdef CONFIG_PCI_MSI
+ /* enable MSI if kernel and card support it */
+ if (pci_msi_enabled() && dev->card_info->msi_supported) {
+ unsigned long flags;
+
+ ngwritel(0, NGENE_INT_ENABLE);
+ free_irq(dev->pci_dev->irq, dev);
+ stat = pci_enable_msi(dev->pci_dev);
+ if (stat) {
+ printk(KERN_INFO DEVICE_NAME
+ ": MSI not available\n");
+ flags = IRQF_SHARED;
+ } else {
+ flags = 0;
+ dev->msi_enabled = true;
+ }
+ stat = request_irq(dev->pci_dev->irq, irq_handler,
+ flags, "nGene", dev);
+ if (stat < 0)
+ goto fail2;
+ ngwritel(1, NGENE_INT_ENABLE);
+ }
+#endif
+
+ stat = ngene_i2c_init(dev, 0);
+ if (stat < 0)
+ goto fail;
+
+ stat = ngene_i2c_init(dev, 1);
+ if (stat < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ ngwritel(0, NGENE_INT_ENABLE);
+ free_irq(dev->pci_dev->irq, dev);
+#ifdef CONFIG_PCI_MSI
+fail2:
+ if (dev->msi_enabled)
+ pci_disable_msi(dev->pci_dev);
+#endif
+ return stat;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+/****************************************************************************/
+
+static void release_channel(struct ngene_channel *chan)
+{
+ struct dvb_demux *dvbdemux = &chan->demux;
+ struct ngene *dev = chan->dev;
+
+ if (chan->running)
+ set_transfer(chan, 0);
+
+ tasklet_kill(&chan->demux_tasklet);
+
+ if (chan->ci_dev) {
+ dvb_unregister_device(chan->ci_dev);
+ chan->ci_dev = NULL;
+ }
+
+ if (chan->fe2)
+ dvb_unregister_frontend(chan->fe2);
+
+ if (chan->fe) {
+ dvb_unregister_frontend(chan->fe);
+ dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ }
+
+ if (chan->has_demux) {
+ dvb_net_release(&chan->dvbnet);
+ dvbdemux->dmx.close(&dvbdemux->dmx);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &chan->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx,
+ &chan->mem_frontend);
+ dvb_dmxdev_release(&chan->dmxdev);
+ dvb_dmx_release(&chan->demux);
+ chan->has_demux = false;
+ }
+
+ if (chan->has_adapter) {
+ dvb_unregister_adapter(&dev->adapter[chan->number]);
+ chan->has_adapter = false;
+ }
+}
+
+static int init_channel(struct ngene_channel *chan)
+{
+ int ret = 0, nr = chan->number;
+ struct dvb_adapter *adapter = NULL;
+ struct dvb_demux *dvbdemux = &chan->demux;
+ struct ngene *dev = chan->dev;
+ struct ngene_info *ni = dev->card_info;
+ int io = ni->io_type[nr];
+
+ tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan);
+ chan->users = 0;
+ chan->type = io;
+ chan->mode = chan->type; /* for now only one mode */
+
+ if (io & NGENE_IO_TSIN) {
+ chan->fe = NULL;
+ if (ni->demod_attach[nr]) {
+ ret = ni->demod_attach[nr](chan);
+ if (ret < 0)
+ goto err;
+ }
+ if (chan->fe && ni->tuner_attach[nr]) {
+ ret = ni->tuner_attach[nr](chan);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ if (!dev->ci.en && (io & NGENE_IO_TSOUT))
+ return 0;
+
+ if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) {
+ if (nr >= STREAM_AUDIOIN1)
+ chan->DataFormatFlags = DF_SWAP32;
+
+ if (nr == 0 || !one_adapter || dev->first_adapter == NULL) {
+ adapter = &dev->adapter[nr];
+ ret = dvb_register_adapter(adapter, "nGene",
+ THIS_MODULE,
+ &chan->dev->pci_dev->dev,
+ adapter_nr);
+ if (ret < 0)
+ goto err;
+ if (dev->first_adapter == NULL)
+ dev->first_adapter = adapter;
+ chan->has_adapter = true;
+ } else
+ adapter = dev->first_adapter;
+ }
+
+ if (dev->ci.en && (io & NGENE_IO_TSOUT)) {
+ dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1);
+ set_transfer(chan, 1);
+ chan->dev->channel[2].DataFormatFlags = DF_SWAP32;
+ set_transfer(&chan->dev->channel[2], 1);
+ dvb_register_device(adapter, &chan->ci_dev,
+ &ngene_dvbdev_ci, (void *) chan,
+ DVB_DEVICE_SEC);
+ if (!chan->ci_dev)
+ goto err;
+ }
+
+ if (chan->fe) {
+ if (dvb_register_frontend(adapter, chan->fe) < 0)
+ goto err;
+ chan->has_demux = true;
+ }
+ if (chan->fe2) {
+ if (dvb_register_frontend(adapter, chan->fe2) < 0)
+ goto err;
+ chan->fe2->tuner_priv = chan->fe->tuner_priv;
+ memcpy(&chan->fe2->ops.tuner_ops,
+ &chan->fe->ops.tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ }
+
+ if (chan->has_demux) {
+ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux",
+ ngene_start_feed,
+ ngene_stop_feed, chan);
+ ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux,
+ &chan->hw_frontend,
+ &chan->mem_frontend, adapter);
+ ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx);
+ }
+
+ return ret;
+
+err:
+ if (chan->fe) {
+ dvb_frontend_detach(chan->fe);
+ chan->fe = NULL;
+ }
+ release_channel(chan);
+ return 0;
+}
+
+static int init_channels(struct ngene *dev)
+{
+ int i, j;
+
+ for (i = 0; i < MAX_STREAM; i++) {
+ dev->channel[i].number = i;
+ if (init_channel(&dev->channel[i]) < 0) {
+ for (j = i - 1; j >= 0; j--)
+ release_channel(&dev->channel[j]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static struct cxd2099_cfg cxd_cfg = {
+ .bitrate = 62000,
+ .adr = 0x40,
+ .polarity = 0,
+ .clock_mode = 0,
+};
+
+static void cxd_attach(struct ngene *dev)
+{
+ struct ngene_ci *ci = &dev->ci;
+
+ ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter);
+ ci->dev = dev;
+ return;
+}
+
+static void cxd_detach(struct ngene *dev)
+{
+ struct ngene_ci *ci = &dev->ci;
+
+ dvb_ca_en50221_release(ci->en);
+ kfree(ci->en);
+ ci->en = 0;
+}
+
+/***********************************/
+/* workaround for shutdown failure */
+/***********************************/
+
+static void ngene_unlink(struct ngene *dev)
+{
+ struct ngene_command com;
+
+ com.cmd.hdr.Opcode = CMD_MEM_WRITE;
+ com.cmd.hdr.Length = 3;
+ com.cmd.MemoryWrite.address = 0x910c;
+ com.cmd.MemoryWrite.data = 0xff;
+ com.in_len = 3;
+ com.out_len = 1;
+
+ down(&dev->cmd_mutex);
+ ngwritel(0, NGENE_INT_ENABLE);
+ ngene_command_mutex(dev, &com);
+ up(&dev->cmd_mutex);
+}
+
+void ngene_shutdown(struct pci_dev *pdev)
+{
+ struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev);
+
+ if (!dev || !shutdown_workaround)
+ return;
+
+ printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n");
+ ngene_unlink(dev);
+ pci_disable_device(pdev);
+}
+
+/****************************************************************************/
+/* device probe/remove calls ************************************************/
+/****************************************************************************/
+
+void __devexit ngene_remove(struct pci_dev *pdev)
+{
+ struct ngene *dev = pci_get_drvdata(pdev);
+ int i;
+
+ tasklet_kill(&dev->event_tasklet);
+ for (i = MAX_STREAM - 1; i >= 0; i--)
+ release_channel(&dev->channel[i]);
+ if (dev->ci.en)
+ cxd_detach(dev);
+ ngene_stop(dev);
+ ngene_release_buffers(dev);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+}
+
+int __devinit ngene_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *id)
+{
+ struct ngene *dev;
+ int stat = 0;
+
+ if (pci_enable_device(pci_dev) < 0)
+ return -ENODEV;
+
+ dev = vzalloc(sizeof(struct ngene));
+ if (dev == NULL) {
+ stat = -ENOMEM;
+ goto fail0;
+ }
+
+ dev->pci_dev = pci_dev;
+ dev->card_info = (struct ngene_info *)id->driver_data;
+ printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name);
+
+ pci_set_drvdata(pci_dev, dev);
+
+ /* Alloc buffers and start nGene */
+ stat = ngene_get_buffers(dev);
+ if (stat < 0)
+ goto fail1;
+ stat = ngene_start(dev);
+ if (stat < 0)
+ goto fail1;
+
+ cxd_attach(dev);
+
+ stat = ngene_buffer_config(dev);
+ if (stat < 0)
+ goto fail1;
+
+
+ dev->i2c_current_bus = -1;
+
+ /* Register DVB adapters and devices for both channels */
+ if (init_channels(dev) < 0)
+ goto fail2;
+
+ return 0;
+
+fail2:
+ ngene_stop(dev);
+fail1:
+ ngene_release_buffers(dev);
+fail0:
+ pci_disable_device(pci_dev);
+ pci_set_drvdata(pci_dev, NULL);
+ return stat;
+}
diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c
index fcb16a615aab..fcb16a615aab 100644
--- a/drivers/media/dvb/ngene/ngene-dvb.c
+++ b/drivers/media/pci/ngene/ngene-dvb.c
diff --git a/drivers/media/dvb/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c
index d28554f8ce99..d28554f8ce99 100644
--- a/drivers/media/dvb/ngene/ngene-i2c.c
+++ b/drivers/media/pci/ngene/ngene-i2c.c
diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h
index 5443dc0caea5..5443dc0caea5 100644
--- a/drivers/media/dvb/ngene/ngene.h
+++ b/drivers/media/pci/ngene/ngene.h
diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/pci/pluto2/Kconfig
index 7d8e6e87bdbb..7d8e6e87bdbb 100644
--- a/drivers/media/dvb/pluto2/Kconfig
+++ b/drivers/media/pci/pluto2/Kconfig
diff --git a/drivers/media/pci/pluto2/Makefile b/drivers/media/pci/pluto2/Makefile
new file mode 100644
index 000000000000..524bf841f42b
--- /dev/null
+++ b/drivers/media/pci/pluto2/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_PLUTO2) += pluto2.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c
index f148b19a206a..f148b19a206a 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/pci/pluto2/pluto2.c
diff --git a/drivers/media/dvb/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig
index 24501d5bf70d..24501d5bf70d 100644
--- a/drivers/media/dvb/pt1/Kconfig
+++ b/drivers/media/pci/pt1/Kconfig
diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile
new file mode 100644
index 000000000000..98e391295afe
--- /dev/null
+++ b/drivers/media/pci/pt1/Makefile
@@ -0,0 +1,5 @@
+earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o
+
+obj-$(CONFIG_DVB_PT1) += earth-pt1.o
+
+ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index 15b35c4725f1..15b35c4725f1 100644
--- a/drivers/media/dvb/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c
new file mode 100644
index 000000000000..1b637b74ef58
--- /dev/null
+++ b/drivers/media/pci/pt1/va1j5jf8007s.c
@@ -0,0 +1,736 @@
+/*
+ * ISDB-S driver for VA1J5JF8007/VA1J5JF8011
+ *
+ * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
+ *
+ * based on pt1dvr - http://pt1dvr.sourceforge.jp/
+ * by Tomoaki Ishikawa <tomy@users.sourceforge.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+#include "va1j5jf8007s.h"
+
+enum va1j5jf8007s_tune_state {
+ VA1J5JF8007S_IDLE,
+ VA1J5JF8007S_SET_FREQUENCY_1,
+ VA1J5JF8007S_SET_FREQUENCY_2,
+ VA1J5JF8007S_SET_FREQUENCY_3,
+ VA1J5JF8007S_CHECK_FREQUENCY,
+ VA1J5JF8007S_SET_MODULATION,
+ VA1J5JF8007S_CHECK_MODULATION,
+ VA1J5JF8007S_SET_TS_ID,
+ VA1J5JF8007S_CHECK_TS_ID,
+ VA1J5JF8007S_TRACK,
+};
+
+struct va1j5jf8007s_state {
+ const struct va1j5jf8007s_config *config;
+ struct i2c_adapter *adap;
+ struct dvb_frontend fe;
+ enum va1j5jf8007s_tune_state tune_state;
+};
+
+static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct va1j5jf8007s_state *state;
+ u8 addr;
+ int i;
+ u8 write_buf[1], read_buf[1];
+ struct i2c_msg msgs[2];
+ s32 word, x1, x2, x3, x4, x5, y;
+
+ state = fe->demodulator_priv;
+ addr = state->config->demod_address;
+
+ word = 0;
+ for (i = 0; i < 2; i++) {
+ write_buf[0] = 0xbc + i;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ word <<= 8;
+ word |= read_buf[0];
+ }
+
+ word -= 3000;
+ if (word < 0)
+ word = 0;
+
+ x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000);
+ x2 = (s64)x1 * x1 >> 31;
+ x3 = (s64)x2 * x1 >> 31;
+ x4 = (s64)x2 * x2 >> 31;
+ x5 = (s64)x4 * x1 >> 31;
+
+ y = (58857ll << 23) / 1000;
+ y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30;
+ y += (s64)x2 * ((88977ll << 24) / 1000) >> 28;
+ y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27;
+ y += (s64)x4 * ((14341ll << 27) / 1000) >> 27;
+ y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28;
+
+ *snr = y < 0 ? 0 : y >> 15;
+ return 0;
+}
+
+static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+static int
+va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct va1j5jf8007s_state *state;
+
+ state = fe->demodulator_priv;
+
+ switch (state->tune_state) {
+ case VA1J5JF8007S_IDLE:
+ case VA1J5JF8007S_SET_FREQUENCY_1:
+ case VA1J5JF8007S_SET_FREQUENCY_2:
+ case VA1J5JF8007S_SET_FREQUENCY_3:
+ case VA1J5JF8007S_CHECK_FREQUENCY:
+ *status = 0;
+ return 0;
+
+
+ case VA1J5JF8007S_SET_MODULATION:
+ case VA1J5JF8007S_CHECK_MODULATION:
+ *status |= FE_HAS_SIGNAL;
+ return 0;
+
+ case VA1J5JF8007S_SET_TS_ID:
+ case VA1J5JF8007S_CHECK_TS_ID:
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ return 0;
+
+ case VA1J5JF8007S_TRACK:
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+ return 0;
+ }
+
+ BUG();
+}
+
+struct va1j5jf8007s_cb_map {
+ u32 frequency;
+ u8 cb;
+};
+
+static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = {
+ { 986000, 0xb2 },
+ { 1072000, 0xd2 },
+ { 1154000, 0xe2 },
+ { 1291000, 0x20 },
+ { 1447000, 0x40 },
+ { 1615000, 0x60 },
+ { 1791000, 0x80 },
+ { 1972000, 0xa0 },
+};
+
+static u8 va1j5jf8007s_lookup_cb(u32 frequency)
+{
+ int i;
+ const struct va1j5jf8007s_cb_map *map;
+
+ for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) {
+ map = &va1j5jf8007s_cb_maps[i];
+ if (frequency < map->frequency)
+ return map->cb;
+ }
+ return 0xc0;
+}
+
+static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state)
+{
+ u32 frequency;
+ u16 word;
+ u8 buf[6];
+ struct i2c_msg msg;
+
+ frequency = state->fe.dtv_property_cache.frequency;
+
+ word = (frequency + 500) / 1000;
+ if (frequency < 1072000)
+ word = (word << 1 & ~0x1f) | (word & 0x0f);
+
+ buf[0] = 0xfe;
+ buf[1] = 0xc0;
+ buf[2] = 0x40 | word >> 8;
+ buf[3] = word;
+ buf[4] = 0xe0;
+ buf[5] = va1j5jf8007s_lookup_cb(frequency);
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state)
+{
+ u8 buf[3];
+ struct i2c_msg msg;
+
+ buf[0] = 0xfe;
+ buf[1] = 0xc0;
+ buf[2] = 0xe4;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state)
+{
+ u32 frequency;
+ u8 buf[4];
+ struct i2c_msg msg;
+
+ frequency = state->fe.dtv_property_cache.frequency;
+
+ buf[0] = 0xfe;
+ buf[1] = 0xc0;
+ buf[2] = 0xf4;
+ buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int
+va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock)
+{
+ u8 addr;
+ u8 write_buf[2], read_buf[1];
+ struct i2c_msg msgs[2];
+
+ addr = state->config->demod_address;
+
+ write_buf[0] = 0xfe;
+ write_buf[1] = 0xc1;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ *lock = read_buf[0] & 0x40;
+ return 0;
+}
+
+static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state)
+{
+ u8 buf[2];
+ struct i2c_msg msg;
+
+ buf[0] = 0x03;
+ buf[1] = 0x01;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int
+va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock)
+{
+ u8 addr;
+ u8 write_buf[1], read_buf[1];
+ struct i2c_msg msgs[2];
+
+ addr = state->config->demod_address;
+
+ write_buf[0] = 0xc3;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ *lock = !(read_buf[0] & 0x10);
+ return 0;
+}
+
+static int
+va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state)
+{
+ u32 ts_id;
+ u8 buf[3];
+ struct i2c_msg msg;
+
+ ts_id = state->fe.dtv_property_cache.stream_id;
+ if (!ts_id || ts_id == NO_STREAM_ID_FILTER)
+ return 0;
+
+ buf[0] = 0x8f;
+ buf[1] = ts_id >> 8;
+ buf[2] = ts_id;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int
+va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock)
+{
+ u8 addr;
+ u8 write_buf[1], read_buf[2];
+ struct i2c_msg msgs[2];
+ u32 ts_id;
+
+ ts_id = state->fe.dtv_property_cache.stream_id;
+ if (!ts_id || ts_id == NO_STREAM_ID_FILTER) {
+ *lock = 1;
+ return 0;
+ }
+
+ addr = state->config->demod_address;
+
+ write_buf[0] = 0xe6;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id;
+ return 0;
+}
+
+static int
+va1j5jf8007s_tune(struct dvb_frontend *fe,
+ bool re_tune,
+ unsigned int mode_flags, unsigned int *delay,
+ fe_status_t *status)
+{
+ struct va1j5jf8007s_state *state;
+ int ret;
+ int lock = 0;
+
+ state = fe->demodulator_priv;
+
+ if (re_tune)
+ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1;
+
+ switch (state->tune_state) {
+ case VA1J5JF8007S_IDLE:
+ *delay = 3 * HZ;
+ *status = 0;
+ return 0;
+
+ case VA1J5JF8007S_SET_FREQUENCY_1:
+ ret = va1j5jf8007s_set_frequency_1(state);
+ if (ret < 0)
+ return ret;
+
+ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2;
+ *delay = 0;
+ *status = 0;
+ return 0;
+
+ case VA1J5JF8007S_SET_FREQUENCY_2:
+ ret = va1j5jf8007s_set_frequency_2(state);
+ if (ret < 0)
+ return ret;
+
+ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3;
+ *delay = (HZ + 99) / 100;
+ *status = 0;
+ return 0;
+
+ case VA1J5JF8007S_SET_FREQUENCY_3:
+ ret = va1j5jf8007s_set_frequency_3(state);
+ if (ret < 0)
+ return ret;
+
+ state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY;
+ *delay = 0;
+ *status = 0;
+ return 0;
+
+ case VA1J5JF8007S_CHECK_FREQUENCY:
+ ret = va1j5jf8007s_check_frequency(state, &lock);
+ if (ret < 0)
+ return ret;
+
+ if (!lock) {
+ *delay = (HZ + 999) / 1000;
+ *status = 0;
+ return 0;
+ }
+
+ state->tune_state = VA1J5JF8007S_SET_MODULATION;
+ *delay = 0;
+ *status = FE_HAS_SIGNAL;
+ return 0;
+
+ case VA1J5JF8007S_SET_MODULATION:
+ ret = va1j5jf8007s_set_modulation(state);
+ if (ret < 0)
+ return ret;
+
+ state->tune_state = VA1J5JF8007S_CHECK_MODULATION;
+ *delay = 0;
+ *status = FE_HAS_SIGNAL;
+ return 0;
+
+ case VA1J5JF8007S_CHECK_MODULATION:
+ ret = va1j5jf8007s_check_modulation(state, &lock);
+ if (ret < 0)
+ return ret;
+
+ if (!lock) {
+ *delay = (HZ + 49) / 50;
+ *status = FE_HAS_SIGNAL;
+ return 0;
+ }
+
+ state->tune_state = VA1J5JF8007S_SET_TS_ID;
+ *delay = 0;
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ return 0;
+
+ case VA1J5JF8007S_SET_TS_ID:
+ ret = va1j5jf8007s_set_ts_id(state);
+ if (ret < 0)
+ return ret;
+
+ state->tune_state = VA1J5JF8007S_CHECK_TS_ID;
+ return 0;
+
+ case VA1J5JF8007S_CHECK_TS_ID:
+ ret = va1j5jf8007s_check_ts_id(state, &lock);
+ if (ret < 0)
+ return ret;
+
+ if (!lock) {
+ *delay = (HZ + 99) / 100;
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ return 0;
+ }
+
+ state->tune_state = VA1J5JF8007S_TRACK;
+ /* fall through */
+
+ case VA1J5JF8007S_TRACK:
+ *delay = 3 * HZ;
+ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+ return 0;
+ }
+
+ BUG();
+}
+
+static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state)
+{
+ u8 buf[4];
+ struct i2c_msg msg;
+
+ buf[0] = 0xfe;
+ buf[1] = 0xc0;
+ buf[2] = 0xf0;
+ buf[3] = 0x04;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep)
+{
+ u8 buf[2];
+ struct i2c_msg msg;
+
+ buf[0] = 0x17;
+ buf[1] = sleep ? 0x01 : 0x00;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+
+ return 0;
+}
+
+static int va1j5jf8007s_sleep(struct dvb_frontend *fe)
+{
+ struct va1j5jf8007s_state *state;
+ int ret;
+
+ state = fe->demodulator_priv;
+
+ ret = va1j5jf8007s_init_frequency(state);
+ if (ret < 0)
+ return ret;
+
+ return va1j5jf8007s_set_sleep(state, 1);
+}
+
+static int va1j5jf8007s_init(struct dvb_frontend *fe)
+{
+ struct va1j5jf8007s_state *state;
+
+ state = fe->demodulator_priv;
+ state->tune_state = VA1J5JF8007S_IDLE;
+
+ return va1j5jf8007s_set_sleep(state, 0);
+}
+
+static void va1j5jf8007s_release(struct dvb_frontend *fe)
+{
+ struct va1j5jf8007s_state *state;
+ state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops va1j5jf8007s_ops = {
+ .delsys = { SYS_ISDBS },
+ .info = {
+ .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000,
+ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_MULTISTREAM,
+ },
+
+ .read_snr = va1j5jf8007s_read_snr,
+ .get_frontend_algo = va1j5jf8007s_get_frontend_algo,
+ .read_status = va1j5jf8007s_read_status,
+ .tune = va1j5jf8007s_tune,
+ .sleep = va1j5jf8007s_sleep,
+ .init = va1j5jf8007s_init,
+ .release = va1j5jf8007s_release,
+};
+
+static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state)
+{
+ u8 addr;
+ u8 write_buf[1], read_buf[1];
+ struct i2c_msg msgs[2];
+
+ addr = state->config->demod_address;
+
+ write_buf[0] = 0x07;
+
+ msgs[0].addr = addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(write_buf);
+ msgs[0].buf = write_buf;
+
+ msgs[1].addr = addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(read_buf);
+ msgs[1].buf = read_buf;
+
+ if (i2c_transfer(state->adap, msgs, 2) != 2)
+ return -EREMOTEIO;
+
+ if (read_buf[0] != 0x41)
+ return -EIO;
+
+ return 0;
+}
+
+static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = {
+ {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01},
+ {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0},
+ {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69},
+ {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = {
+ {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a},
+ {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89},
+ {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04},
+ {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0},
+};
+
+static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state)
+{
+ const u8 (*bufs)[2];
+ int size;
+ u8 addr;
+ u8 buf[2];
+ struct i2c_msg msg;
+ int i;
+
+ switch (state->config->frequency) {
+ case VA1J5JF8007S_20MHZ:
+ bufs = va1j5jf8007s_20mhz_prepare_bufs;
+ size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs);
+ break;
+ case VA1J5JF8007S_25MHZ:
+ bufs = va1j5jf8007s_25mhz_prepare_bufs;
+ size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ addr = state->config->demod_address;
+
+ msg.addr = addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = buf;
+ for (i = 0; i < size; i++) {
+ memcpy(buf, bufs[i], sizeof(buf));
+ if (i2c_transfer(state->adap, &msg, 1) != 1)
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
+/* must be called after va1j5jf8007t_attach */
+int va1j5jf8007s_prepare(struct dvb_frontend *fe)
+{
+ struct va1j5jf8007s_state *state;
+ int ret;
+
+ state = fe->demodulator_priv;
+
+ ret = va1j5jf8007s_prepare_1(state);
+ if (ret < 0)
+ return ret;
+
+ ret = va1j5jf8007s_prepare_2(state);
+ if (ret < 0)
+ return ret;
+
+ return va1j5jf8007s_init_frequency(state);
+}
+
+struct dvb_frontend *
+va1j5jf8007s_attach(const struct va1j5jf8007s_config *config,
+ struct i2c_adapter *adap)
+{
+ struct va1j5jf8007s_state *state;
+ struct dvb_frontend *fe;
+ u8 buf[2];
+ struct i2c_msg msg;
+
+ state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ state->config = config;
+ state->adap = adap;
+
+ fe = &state->fe;
+ memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops));
+ fe->demodulator_priv = state;
+
+ buf[0] = 0x01;
+ buf[1] = 0x80;
+
+ msg.addr = state->config->demod_address;
+ msg.flags = 0;
+ msg.len = sizeof(buf);
+ msg.buf = buf;
+
+ if (i2c_transfer(state->adap, &msg, 1) != 1) {
+ kfree(state);
+ return NULL;
+ }
+
+ return fe;
+}
diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h
index b7d6f05a0e02..b7d6f05a0e02 100644
--- a/drivers/media/dvb/pt1/va1j5jf8007s.h
+++ b/drivers/media/pci/pt1/va1j5jf8007s.h
diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c
index 2db15159d514..2db15159d514 100644
--- a/drivers/media/dvb/pt1/va1j5jf8007t.c
+++ b/drivers/media/pci/pt1/va1j5jf8007t.c
diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h
index 2903be519ef5..2903be519ef5 100644
--- a/drivers/media/dvb/pt1/va1j5jf8007t.h
+++ b/drivers/media/pci/pt1/va1j5jf8007t.h
diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig
new file mode 100644
index 000000000000..15b90d6e9130
--- /dev/null
+++ b/drivers/media/pci/saa7134/Kconfig
@@ -0,0 +1,64 @@
+config VIDEO_SAA7134
+ tristate "Philips SAA7134 support"
+ depends on VIDEO_DEV && PCI && I2C
+ select VIDEOBUF_DMA_SG
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select CRC32
+ select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for Philips SAA713x based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134.
+
+config VIDEO_SAA7134_ALSA
+ tristate "Philips SAA7134 DMA audio support"
+ depends on VIDEO_SAA7134 && SND
+ select SND_PCM
+ ---help---
+ This is a video4linux driver for direct (DMA) audio in
+ Philips SAA713x based TV cards using ALSA
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-alsa.
+
+config VIDEO_SAA7134_RC
+ bool "Philips SAA7134 Remote Controller support"
+ depends on RC_CORE
+ depends on VIDEO_SAA7134
+ depends on !(RC_CORE=m && VIDEO_SAA7134=y)
+ default y
+ ---help---
+ Enables Remote Controller support on saa7134 driver.
+
+config VIDEO_SAA7134_DVB
+ tristate "DVB/ATSC Support for saa7134 based TV cards"
+ depends on VIDEO_SAA7134 && DVB_CORE
+ select VIDEOBUF_DVB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6405 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10036 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This adds support for DVB cards based on the
+ Philips saa7134 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7134-dvb.
diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile
new file mode 100644
index 000000000000..35375480ed4d
--- /dev/null
+++ b/drivers/media/pci/saa7134/Makefile
@@ -0,0 +1,16 @@
+
+saa7134-y += saa7134-cards.o saa7134-core.o saa7134-i2c.o
+saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
+saa7134-y += saa7134-video.o
+saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
+
+obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o
+
+obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
+
+obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c
index f147b05bd860..f147b05bd860 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/pci/saa7134/saa6752hs.c
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c
index 10460fd3ce39..10460fd3ce39 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/pci/saa7134/saa7134-alsa.c
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c
index bc08f1dbc293..bc08f1dbc293 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/pci/saa7134/saa7134-cards.c
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index 5fbb4e49495c..5fbb4e49495c 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c
new file mode 100644
index 000000000000..b209de40a4f8
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-dvb.c
@@ -0,0 +1,1936 @@
+/*
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * Extended 3 / 2005 by Hartmut Hackmann to support various
+ * cards with the tda10046 DVB-T channel decoder
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/suspend.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include "dvb-pll.h"
+#include <dvb_frontend.h>
+
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1004x.h"
+#include "nxt200x.h"
+#include "tuner-xc2028.h"
+#include "xc5000.h"
+
+#include "tda10086.h"
+#include "tda826x.h"
+#include "tda827x.h"
+#include "isl6421.h"
+#include "isl6405.h"
+#include "lnbp21.h"
+#include "tuner-simple.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "lgdt3305.h"
+#include "tda8290.h"
+#include "mb86a20s.h"
+#include "lgs8gxx.h"
+
+#include "zl10353.h"
+#include "qt1010.h"
+
+#include "zl10036.h"
+#include "zl10039.h"
+#include "mt312.h"
+#include "s5h1411.h"
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int antenna_pwr;
+
+module_param(antenna_pwr, int, 0444);
+MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
+
+static int use_frontend;
+module_param(use_frontend, int, 0644);
+MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(fmt, arg...) do { if (debug) \
+ printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
+
+/* Print a warning */
+#define wprintk(fmt, arg...) \
+ printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------
+ * mt352 based DVB-T cards
+ */
+
+static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
+{
+ u32 ok;
+
+ if (!on) {
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return 0;
+ }
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ udelay(10);
+
+ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28));
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
+ udelay(10);
+ ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
+ dprintk("%s %s\n", __func__, ok ? "on" : "off");
+
+ if (!ok)
+ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
+ return ok;
+}
+
+static int mt352_pinnacle_init(struct dvb_frontend* fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 };
+ static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 };
+ static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f };
+ static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d };
+ static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
+ struct saa7134_dev *dev= fe->dvb->priv;
+
+ dprintk("%s called\n", __func__);
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+
+ mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg));
+ mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg));
+ mt352_write(fe, irq_cfg, sizeof(irq_cfg));
+
+ return 0;
+}
+
+static int mt352_aver777_init(struct dvb_frontend* fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0xe };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ return 0;
+}
+
+static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u8 off[] = { 0x00, 0xf1};
+ u8 on[] = { 0x00, 0x71};
+ struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)};
+
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct v4l2_frequency f;
+
+ /* set frequency (mt2050) */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_DIGITAL_TV;
+ f.frequency = c->frequency / 1000 * 16 / 1000;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ saa_call_all(dev, tuner, s_frequency, &f);
+ msg.buf = on;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+
+ pinnacle_antenna_pwr(dev, antenna_pwr);
+
+ /* mt352 setup */
+ return mt352_pinnacle_init(fe);
+}
+
+static struct mt352_config pinnacle_300i = {
+ .demod_address = 0x3c >> 1,
+ .adc_clock = 20333,
+ .if2 = 36150,
+ .no_tuner = 1,
+ .demod_init = mt352_pinnacle_init,
+};
+
+static struct mt352_config avermedia_777 = {
+ .demod_address = 0xf,
+ .demod_init = mt352_aver777_init,
+};
+
+static struct mt352_config avermedia_xc3028_mt352_dev = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .demod_init = mt352_avermedia_xc3028_init,
+};
+
+static struct tda18271_std_map mb86a20s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 7, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config kworld_tda18271_config = {
+ .std_map = &mb86a20s_tda18271_std_map,
+ .gate = TDA18271_GATE_DIGITAL,
+ .config = 3, /* Use tuner callback for AGC */
+
+};
+
+static const struct mb86a20s_config kworld_mb86a20s_config = {
+ .demod_address = 0x10,
+};
+
+static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ unsigned char initmsg[] = {0x45, 0x97};
+ unsigned char msg_enable[] = {0x45, 0xc1};
+ unsigned char msg_disable[] = {0x45, 0x81};
+ struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2};
+
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+ wprintk("could not access the I2C gate\n");
+ return -EIO;
+ }
+ if (enable)
+ msg.buf = msg_enable;
+ else
+ msg.buf = msg_disable;
+ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
+ wprintk("could not access the I2C gate\n");
+ return -EIO;
+ }
+ msleep(20);
+ return 0;
+}
+
+/* ==================================================================
+ * tda1004x based DVB-T cards, helper functions
+ */
+
+static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
+ const struct firmware **fw, char *name)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ return request_firmware(fw, name, &dev->pci->dev);
+}
+
+/* ------------------------------------------------------------------
+ * these tuners are tu1216, td1316(a)
+ */
+
+static int philips_tda6651_pll_set(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ u8 tuner_buf[4];
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
+ sizeof(tuner_buf) };
+ int tuner_frequency = 0;
+ u8 band, cp, filter;
+
+ /* determine charge pump */
+ tuner_frequency = c->frequency + 36166000;
+ if (tuner_frequency < 87000000)
+ return -EINVAL;
+ else if (tuner_frequency < 130000000)
+ cp = 3;
+ else if (tuner_frequency < 160000000)
+ cp = 5;
+ else if (tuner_frequency < 200000000)
+ cp = 6;
+ else if (tuner_frequency < 290000000)
+ cp = 3;
+ else if (tuner_frequency < 420000000)
+ cp = 5;
+ else if (tuner_frequency < 480000000)
+ cp = 6;
+ else if (tuner_frequency < 620000000)
+ cp = 3;
+ else if (tuner_frequency < 830000000)
+ cp = 5;
+ else if (tuner_frequency < 895000000)
+ cp = 7;
+ else
+ return -EINVAL;
+
+ /* determine band */
+ if (c->frequency < 49000000)
+ return -EINVAL;
+ else if (c->frequency < 161000000)
+ band = 1;
+ else if (c->frequency < 444000000)
+ band = 2;
+ else if (c->frequency < 861000000)
+ band = 4;
+ else
+ return -EINVAL;
+
+ /* setup PLL filter */
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ filter = 0;
+ break;
+
+ case 7000000:
+ filter = 0;
+ break;
+
+ case 8000000:
+ filter = 1;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* calculate divisor
+ * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
+ */
+ tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
+
+ /* setup tuner buffer */
+ tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
+ tuner_buf[1] = tuner_frequency & 0xff;
+ tuner_buf[2] = 0xca;
+ tuner_buf[3] = (cp << 5) | (filter << 3) | band;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
+ wprintk("could not write to tuner at addr: 0x%02x\n",
+ addr << 1);
+ return -EIO;
+ }
+ msleep(1);
+ return 0;
+}
+
+static int philips_tu1216_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
+ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
+
+ /* setup PLL configuration */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
+ return -EIO;
+ msleep(1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config philips_tu1216_60_config = {
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tu1216_61_config = {
+
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------ */
+
+static int philips_td1316_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
+ struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ /* setup PLL configuration */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+static int philips_td1316_tuner_set_params(struct dvb_frontend *fe)
+{
+ return philips_tda6651_pll_set(fe);
+}
+
+static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+ u8 addr = state->config->tuner_address;
+ static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
+ struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ /* switch the tuner to analog mode */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
+ return -EIO;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int philips_europa_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 msg[] = { 0x00, 0x40};
+ struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+
+ if (philips_td1316_tuner_init(fe))
+ return -EIO;
+ msleep(1);
+ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ static u8 msg[] = { 0x00, 0x14 };
+ struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
+
+ if (philips_td1316_tuner_sleep(fe))
+ return -EIO;
+
+ /* switch the board to analog mode */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
+ return 0;
+}
+
+static int philips_europa_demod_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+
+ if (dev->original_demod_sleep)
+ dev->original_demod_sleep(fe);
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ return 0;
+}
+
+static struct tda1004x_config philips_europa_config = {
+
+ .demod_address = 0x8,
+ .invert = 0,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_IFO_AUTO_POS,
+ .if_freq = TDA10046_FREQ_052,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config medion_cardbus = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_IFO_AUTO_NEG,
+ .if_freq = TDA10046_FREQ_3613,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config technotrend_budget_t3000_config = {
+ .demod_address = 0x8,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_4M,
+ .agc_config = TDA10046_AGC_DEFAULT,
+ .if_freq = TDA10046_FREQ_3617,
+ .tuner_address = 0x63,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * tda 1004x based cards with philips silicon tuner
+ */
+
+static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
+{
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ u8 addr = state->config->i2c_gate;
+ static u8 tda8290_close[] = { 0x21, 0xc0};
+ static u8 tda8290_open[] = { 0x21, 0x80};
+ struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
+ if (enable) {
+ tda8290_msg.buf = tda8290_close;
+ } else {
+ tda8290_msg.buf = tda8290_open;
+ }
+ if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
+ struct saa7134_dev *dev = fe->dvb->priv;
+ wprintk("could not access tda8290 I2C gate\n");
+ return -EIO;
+ }
+ msleep(20);
+ return 0;
+}
+
+static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ }
+ return 0;
+}
+
+static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ struct tda1004x_state *state = fe->demodulator_priv;
+
+ switch (state->config->antenna_switch) {
+ case 0: break;
+ case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
+ saa7134_set_gpio(dev, 21, 1);
+ break;
+ case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
+ saa7134_set_gpio(dev, 21, 0);
+ break;
+ }
+ return 0;
+}
+
+static int configure_tda827x_fe(struct saa7134_dev *dev,
+ struct tda1004x_config *cdec_conf,
+ struct tda827x_config *tuner_conf)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+
+ fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (cdec_conf->i2c_gate)
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
+ if (dvb_attach(tda827x_attach, fe0->dvb.frontend,
+ cdec_conf->tuner_address,
+ &dev->i2c_adap, tuner_conf))
+ return 0;
+
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ cdec_conf->tuner_address);
+ }
+ return -EINVAL;
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct tda827x_config tda827x_cfg_0 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 0,
+ .switch_addr = 0
+};
+
+static struct tda827x_config tda827x_cfg_1 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 1,
+ .switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 2,
+ .switch_addr = 0x4b
+};
+
+static struct tda827x_config tda827x_cfg_2_sw42 = {
+ .init = philips_tda827x_tuner_init,
+ .sleep = philips_tda827x_tuner_sleep,
+ .config = 2,
+ .switch_addr = 0x42
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct tda1004x_config tda827x_lifeview_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config cinergy_ht_pci_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config philips_tiger_s_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config pinnacle_pctv_310i_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config hauppauge_hvr_1110_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_dual_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config lifeview_trio_config = {
+ .demod_address = 0x09,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config tevion_dvbt220rf_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config md8800_dvbt_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_4871_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_p7131_hybrid_lna_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 2,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config kworld_dvb_t_210_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config avermedia_super_007_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x60,
+ .antenna_switch= 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config twinhan_dtv_dvb_3056_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP01_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x42,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_tiger_3in1_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct tda1004x_config asus_ps3_100_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+/* ------------------------------------------------------------------
+ * special case: this card uses saa713x GPIO22 for the mode switch
+ */
+
+static int ads_duo_tuner_init(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ philips_tda827x_tuner_init(fe);
+ /* route TDA8275a AGC input to the channel decoder */
+ saa7134_set_gpio(dev, 22, 1);
+ return 0;
+}
+
+static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ /* route TDA8275a AGC input to the analog IF chip*/
+ saa7134_set_gpio(dev, 22, 0);
+ philips_tda827x_tuner_sleep(fe);
+ return 0;
+}
+
+static struct tda827x_config ads_duo_cfg = {
+ .init = ads_duo_tuner_init,
+ .sleep = ads_duo_tuner_sleep,
+ .config = 0
+};
+
+static struct tda1004x_config ads_tech_duo_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP00_I,
+ .if_freq = TDA10046_FREQ_045,
+ .tuner_address = 0x61,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
+static struct zl10353_config behold_h6_config = {
+ .demod_address = 0x1e>>1,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct xc5000_config behold_x7_tunerconfig = {
+ .i2c_address = 0xc2>>1,
+ .if_khz = 4560,
+ .radio_input = XC5000_RADIO_FM1,
+};
+
+static struct zl10353_config behold_x7_config = {
+ .demod_address = 0x1e>>1,
+ .if2 = 45600,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct zl10353_config videomate_t750_zl10353_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct qt1010_config videomate_t750_qt1010_config = {
+ .i2c_address = 0x62
+};
+
+
+/* ==================================================================
+ * tda10086 based DVB-S cards, helper functions
+ */
+
+static struct tda10086_config flydvbs = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 0,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct tda10086_config sd1878_4m = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 0,
+ .xtal_freq = TDA10086_XTAL_4M,
+};
+
+/* ------------------------------------------------------------------
+ * special case: lnb supply is connected to the gated i2c
+ */
+
+static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ int res = -EIO;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dev->original_set_voltage)
+ res = dev->original_set_voltage(fe, voltage);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ return res;
+};
+
+static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg)
+{
+ int res = -EIO;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dev->original_set_high_voltage)
+ res = dev->original_set_high_voltage(fe, arg);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ return res;
+};
+
+static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ u8 wbuf[2] = { 0x1f, 00 };
+ u8 rbuf;
+ struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 },
+ { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } };
+
+ if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2)
+ return -EIO;
+ /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */
+ if (voltage == SEC_VOLTAGE_18)
+ wbuf[1] = rbuf | 0x10;
+ else
+ wbuf[1] = rbuf & 0xef;
+ msg[0].len = 2;
+ i2c_transfer(&dev->i2c_adap, msg, 1);
+ return 0;
+}
+
+static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__);
+ return -EIO;
+}
+
+/* ==================================================================
+ * nxt200x based ATSC cards, helper functions
+ */
+
+static struct nxt200x_config avertvhda180 = {
+ .demod_address = 0x0a,
+};
+
+static struct nxt200x_config kworldatsc110 = {
+ .demod_address = 0x0a,
+};
+
+/* ------------------------------------------------------------------ */
+
+static struct mt312_config avertv_a700_mt312 = {
+ .demod_address = 0x0e,
+ .voltage_inverted = 1,
+};
+
+static struct zl10036_config avertv_a700_tuner = {
+ .tuner_address = 0x60,
+};
+
+static struct mt312_config zl10313_compro_s350_config = {
+ .demod_address = 0x0e,
+};
+
+static struct lgdt3305_config hcw_lgdt3305_config = {
+ .i2c_addr = 0x0e,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 1,
+ .qam_if_khz = 4000,
+ .vsb_if_khz = 3250,
+};
+
+static struct tda10048_config hcw_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_SERIAL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x58, },
+};
+
+static struct tda18271_config hcw_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .config = 3,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda10048_config zolid_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3500,
+ .dtv8_if_freq_khz = TDA10048_IF_4000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_config zolid_tda18271_config = {
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static struct tda10048_config dtv1000s_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_200,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda18271_std_map dtv1000s_tda18271_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config dtv1000s_tda18271_config = {
+ .std_map = &dtv1000s_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = {
+ .prod = LGS8GXX_PROD_LGS8G75,
+ .demod_address = 0x1d,
+ .serial_ts = 0,
+ .ts_clk_pol = 1,
+ .ts_clk_gated = 0,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 4000, /* 4.00 MHz */
+ .if_neg_center = 0,
+ .ext_adc = 0,
+ .adc_signed = 1,
+ .adc_vpp = 3, /* 2.0 Vpp */
+ .if_neg_edge = 1,
+};
+
+static struct tda18271_config prohdtv_pro2_tda18271_config = {
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static struct tda18271_std_map kworld_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config kworld_pc150u_tda18271_config = {
+ .std_map = &kworld_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .config = 3, /* Use tuner callback for AGC */
+ .rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config kworld_s5h1411_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .qam_if = S5H1411_IF_4000,
+ .vsb_if = S5H1411_IF_3250,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing =
+ S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+
+/* ==================================================================
+ * Core code
+ */
+
+static int dvb_init(struct saa7134_dev *dev)
+{
+ int ret;
+ int attach_xc3028 = 0;
+ struct videobuf_dvb_frontend *fe0;
+
+ /* FIXME: add support for multi-frontend */
+ mutex_init(&dev->frontends.lock);
+ INIT_LIST_HEAD(&dev->frontends.felist);
+
+ printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
+ fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
+ if (!fe0) {
+ printk(KERN_ERR "%s() failed to alloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* init struct videobuf_dvb */
+ dev->ts.nr_bufs = 32;
+ dev->ts.nr_packets = 32*4;
+ fe0->dvb.name = dev->name;
+ videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_ALTERNATE,
+ sizeof(struct saa7134_buf),
+ dev, NULL);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
+ dprintk("pinnacle 300i dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ dprintk("avertv 777 dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_TD1316);
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ dprintk("AverMedia A16D dvb setup\n");
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_MD7134:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &medion_cardbus,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, medion_cardbus.tuner_address,
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ }
+ break;
+ case SAA7134_BOARD_PHILIPS_TOUGH:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_60_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+ }
+ break;
+ case SAA7134_BOARD_FLYDVBTDUO:
+ case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
+ if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_EUROPA:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &technotrend_budget_t3000_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_61_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
+ }
+ break;
+ case SAA7134_BOARD_KWORLD_DVBT_210:
+ if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &hcw_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &hcw_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_PHILIPS_TIGER:
+ if (configure_tda827x_fe(dev, &philips_tiger_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
+ &tda827x_cfg_1) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
+ &tda827x_cfg_1) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
+ &hcw_lgdt3305_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &hcw_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_FLYDVBT_LR301:
+ if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &lifeview_trio_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__);
+ goto detach_frontend;
+ }
+ if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
+ 0x08, 0, 0) == NULL) {
+ wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
+ goto detach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
+ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &ads_tech_duo_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda827x_attach,fe0->dvb.frontend,
+ ads_tech_duo_config.tuner_address, &dev->i2c_adap,
+ &ads_duo_cfg) == NULL) {
+ wprintk("no tda827x tuner found at addr: %02x\n",
+ ads_tech_duo_config.tuner_address);
+ goto detach_frontend;
+ }
+ } else
+ wprintk("failed to attach tda10046\n");
+ break;
+ case SAA7134_BOARD_TEVION_DVBT_220RF:
+ if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct dvb_frontend *fe = fe0->dvb.frontend;
+ u8 dev_id = dev->eedata[2];
+ u8 data = 0xc4;
+ struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1};
+
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Medion Quadro, no tda826x "
+ "found !\n", __func__);
+ goto detach_frontend;
+ }
+ if (dev_id != 0x08) {
+ /* we need to open the i2c gate (we know it exists) */
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dvb_attach(isl6405_attach, fe,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: Medion Quadro, no ISL6405 "
+ "found !\n", __func__);
+ goto detach_frontend;
+ }
+ if (dev_id == 0x07) {
+ /* fire up the 2nd section of the LNB supply since
+ we can't do this from the other section */
+ msg.buf = &data;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ }
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ dev->original_set_voltage = fe->ops.set_voltage;
+ fe->ops.set_voltage = md8800_set_voltage;
+ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+ } else {
+ fe->ops.set_voltage = md8800_set_voltage2;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61,
+ NULL, DVB_PLL_TDHU2);
+ break;
+ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+ case SAA7134_BOARD_KWORLD_ATSC110:
+ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_TUV1236D);
+ break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
+ saa7134_tuner_callback(dev, 0,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &kworld_s5h1411_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &kworld_pc150u_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: No tda826x found!\n", __func__);
+ goto detach_frontend;
+ }
+ if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: No ISL6421 found!\n", __func__);
+ goto detach_frontend;
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &medion_cardbus,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
+ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
+
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, medion_cardbus.tuner_address,
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ fe0->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
+ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
+ }
+ break;
+ case SAA7134_BOARD_CINERGY_HT_PCMCIA:
+ if (configure_tda827x_fe(dev, &cinergy_ht_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_CINERGY_HT_PCI:
+ if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_TIGER_S:
+ if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_ASUS_P7131_4871:
+ if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_SUPER_007:
+ if (configure_tda827x_fe(dev, &avermedia_super_007_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
+ if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
+ &tda827x_cfg_2_sw42) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_PHILIPS_SNAKE:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: No tda826x found!\n", __func__);
+ goto detach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: No lnbp21 found!\n", __func__);
+ goto detach_frontend;
+ }
+ }
+ break;
+ case SAA7134_BOARD_CREATIX_CTX953:
+ if (configure_tda827x_fe(dev, &md8800_dvbt_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
+ if (configure_tda827x_fe(dev, &philips_tiger_s_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ dprintk("AverMedia E506R dvb setup\n");
+ saa7134_set_gpio(dev, 25, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 25, 1);
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_MD7134_BRIDGE_2:
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &sd1878_4m, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ struct dvb_frontend *fe;
+ if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
+ wprintk("%s: MD7134 DVB-S, no SD1878 "
+ "found !\n", __func__);
+ goto detach_frontend;
+ }
+ /* we need to open the i2c gate (we know it exists) */
+ fe = fe0->dvb.frontend;
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (dvb_attach(isl6405_attach, fe,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ wprintk("%s: MD7134 DVB-S, no ISL6405 "
+ "found !\n", __func__);
+ goto detach_frontend;
+ }
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ dev->original_set_voltage = fe->ops.set_voltage;
+ fe->ops.set_voltage = md8800_set_voltage;
+ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
+ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ saa7134_set_gpio(dev, 25, 0);
+ msleep(10);
+ saa7134_set_gpio(dev, 25, 1);
+ fe0->dvb.frontend = dvb_attach(mt352_attach,
+ &avermedia_xc3028_mt352_dev,
+ &dev->i2c_adap);
+ attach_xc3028 = 1;
+ break;
+ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no "
+ "tda826x found!\n", __func__);
+ goto detach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus Tiger 3in1, no lnbp21"
+ " found!\n", __func__);
+ goto detach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_ps3_100_config,
+ &tda827x_cfg_2) < 0)
+ goto detach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus My Cinema PS3-100, no "
+ "tda826x found!\n", __func__);
+ goto detach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus My Cinema PS3-100, no lnbp21"
+ " found!\n", __func__);
+ goto detach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_TIGER:
+ if (configure_tda827x_fe(dev, &philips_tiger_config,
+ &tda827x_cfg_0) < 0)
+ goto detach_frontend;
+ break;
+ case SAA7134_BOARD_BEHOLD_H6:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_h6_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x61,
+ TUNER_PHILIPS_FMD1216MEX_MK3);
+ }
+ break;
+ case SAA7134_BOARD_BEHOLD_X7:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_x7_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, &behold_x7_tunerconfig);
+ }
+ break;
+ case SAA7134_BOARD_BEHOLD_H7:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &behold_x7_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ dvb_attach(xc5000_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, &behold_x7_tunerconfig);
+ }
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+ /* Zarlink ZL10313 */
+ fe0->dvb.frontend = dvb_attach(mt312_attach,
+ &avertv_a700_mt312, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(zl10036_attach, fe0->dvb.frontend,
+ &avertv_a700_tuner, &dev->i2c_adap) == NULL) {
+ wprintk("%s: No zl10036 found!\n",
+ __func__);
+ }
+ }
+ break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ fe0->dvb.frontend = dvb_attach(mt312_attach,
+ &zl10313_compro_s350_config, &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ if (dvb_attach(zl10039_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap) == NULL)
+ wprintk("%s: No zl10039 found!\n",
+ __func__);
+
+ break;
+ case SAA7134_BOARD_VIDEOMATE_T750:
+ fe0->dvb.frontend = dvb_attach(zl10353_attach,
+ &videomate_t750_zl10353_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ if (dvb_attach(qt1010_attach,
+ fe0->dvb.frontend,
+ &dev->i2c_adap,
+ &videomate_t750_qt1010_config) == NULL)
+ wprintk("error attaching QT1010\n");
+ }
+ break;
+ case SAA7134_BOARD_ZOLID_HYBRID_PCI:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &zolid_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &zolid_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ fe0->dvb.frontend = dvb_attach(tda10048_attach,
+ &dtv1000s_tda10048_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &dtv1000s_tda18271_config);
+ }
+ break;
+ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ /* Switch to digital mode */
+ saa7134_tuner_callback(dev, 0,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+ fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
+ &kworld_mb86a20s_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl;
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &kworld_tda18271_config);
+ }
+
+ /* mb86a20s need to use the I2C gateway */
+ break;
+ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
+ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
+ &prohdtv_pro2_lgs8g75_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &prohdtv_pro2_tda18271_config);
+ }
+ break;
+ default:
+ wprintk("Huh? unknown DVB card?\n");
+ break;
+ }
+
+ if (attach_xc3028) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &dev->i2c_adap,
+ .i2c_addr = 0x61,
+ };
+
+ if (!fe0->dvb.frontend)
+ goto detach_frontend;
+
+ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
+ if (!fe) {
+ printk(KERN_ERR "%s/2: xc3028 attach failed\n",
+ dev->name);
+ goto detach_frontend;
+ }
+ }
+
+ if (NULL == fe0->dvb.frontend) {
+ printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
+ goto detach_frontend;
+ }
+ /* define general-purpose callback pointer */
+ fe0->dvb.frontend->callback = saa7134_tuner_callback;
+
+ /* register everything else */
+ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+ &dev->pci->dev, adapter_nr, 0);
+
+ /* this sequence is necessary to make the tda1004x load its firmware
+ * and to enter analog mode of hybrid boards
+ */
+ if (!ret) {
+ if (fe0->dvb.frontend->ops.init)
+ fe0->dvb.frontend->ops.init(fe0->dvb.frontend);
+ if (fe0->dvb.frontend->ops.sleep)
+ fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend);
+ if (fe0->dvb.frontend->ops.tuner_ops.sleep)
+ fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend);
+ }
+ return ret;
+
+detach_frontend:
+ videobuf_dvb_dealloc_frontends(&dev->frontends);
+ return -EINVAL;
+}
+
+static int dvb_fini(struct saa7134_dev *dev)
+{
+ struct videobuf_dvb_frontend *fe0;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
+ if (!fe0)
+ return -EINVAL;
+
+ /* FIXME: I suspect that this code is bogus, since the entry for
+ Pinnacle 300I DVB-T PAL already defines the proper init to allow
+ the detection of mt2032 (TDA9887_PORT2_INACTIVE)
+ */
+ if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &on;
+
+ /* otherwise we don't detect the tuner on next insmod */
+ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) {
+ if ((dev->eedata[2] == 0x07) && use_frontend) {
+ /* turn off the 2nd lnb supply */
+ u8 data = 0x80;
+ struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1};
+ struct dvb_frontend *fe;
+ fe = fe0->dvb.frontend;
+ if (fe->ops.i2c_gate_ctrl) {
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+ }
+ }
+ videobuf_dvb_unregister_bus(&dev->frontends);
+ return 0;
+}
+
+static struct saa7134_mpeg_ops dvb_ops = {
+ .type = SAA7134_MPEG_DVB,
+ .init = dvb_init,
+ .fini = dvb_fini,
+};
+
+static int __init dvb_register(void)
+{
+ return saa7134_ts_register(&dvb_ops);
+}
+
+static void __exit dvb_unregister(void)
+{
+ saa7134_ts_unregister(&dvb_ops);
+}
+
+module_init(dvb_register);
+module_exit(dvb_unregister);
+
+/* ------------------------------------------------------------------ */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index dde361a9194e..dde361a9194e 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c
index a176ec3285e0..a176ec3285e0 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/pci/saa7134/saa7134-i2c.c
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
new file mode 100644
index 000000000000..0f78f5e537e2
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -0,0 +1,1041 @@
+/*
+ *
+ * handle saa7134 IR remotes via linux kernel input layer.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+
+#define MODULE_NAME "saa7134"
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
+
+static int pinnacle_remote;
+module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */
+MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)");
+
+#define dprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
+#define i2cdprintk(fmt, arg...) if (ir_debug) \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg)
+
+/* Helper function for raw decoding at GPIO16 or GPIO18 */
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev);
+
+/* -------------------- GPIO generic keycode builder -------------------- */
+
+static int build_key(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir = dev->remote;
+ u32 gpio, data;
+
+ /* here comes the additional handshake steps for some cards */
+ switch (dev->board) {
+ case SAA7134_BOARD_GOTVIEW_7135:
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80);
+ saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80);
+ break;
+ }
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+ if (ir->polling) {
+ if (ir->last_gpio == gpio)
+ return 0;
+ ir->last_gpio = gpio;
+ }
+
+ data = ir_extract_bits(gpio, ir->mask_keycode);
+ dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
+ gpio, ir->mask_keycode, data);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ if (data == ir->mask_keycode)
+ rc_keyup(ir->dev);
+ else
+ rc_keydown_notimeout(ir->dev, data, 0);
+ return 0;
+ }
+
+ if (ir->polling) {
+ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
+ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ } else {
+ rc_keyup(ir->dev);
+ }
+ }
+ else { /* IRQ driven mode - handle key press and release in one go */
+ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
+ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
+ rc_keydown_notimeout(ir->dev, data, 0);
+ rc_keyup(ir->dev);
+ }
+ }
+
+ return 0;
+}
+
+/* --------------------- Chip specific I2C key builders ----------------- */
+
+static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ int gpio;
+ int attempt = 0;
+ unsigned char b;
+
+ /* We need this to access GPI Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+ if (dev == NULL) {
+ i2cdprintk("get_key_flydvb_trio: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIGPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x40000 & ~gpio)
+ return 0; /* No button press */
+
+ /* No button press - only before first key pressed */
+ if (b == 0xFF)
+ return 0;
+
+ /* poll IR chip */
+ /* weak up the IR chip */
+ b = 0;
+
+ while (1 != i2c_master_send(ir->c, &b, 1)) {
+ if ((attempt++) < 10) {
+ /*
+ * wait a bit for next attempt -
+ * I don't know how make it better
+ */
+ msleep(10);
+ continue;
+ }
+ i2cdprintk("send wake up byte to pic16C505 (IR chip)"
+ "failed %dx\n", attempt);
+ return -EIO;
+ }
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+ if (dev == NULL) {
+ i2cdprintk("get_key_msi_tvanywhere_plus: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x40 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x40 is not low. */
+
+ if (gpio & 0x40)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+/* copied and modified from get_key_msi_tvanywhere_plus() */
+static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ unsigned int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+ if (dev == NULL) {
+ i2cdprintk("get_key_kworld_pc150u: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x100 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x100 is not low. */
+
+ if (gpio & 0x100)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char b;
+
+ /* poll IR chip */
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* no button press */
+ if (b==0)
+ return 0;
+
+ /* repeating */
+ if (b & 0x80)
+ return 1;
+
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
+static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char buf[5];
+
+ /* poll IR chip */
+ if (5 != i2c_master_recv(ir->c, buf, 5))
+ return -EIO;
+
+ /* Check if some key were pressed */
+ if (!(buf[0] & 0x80))
+ return 0;
+
+ /*
+ * buf[3] & 0x80 is always high.
+ * buf[3] & 0x40 is a parity bit. A repeat event is marked
+ * by preserving it into two separate readings
+ * buf[4] bits 0 and 1, and buf[1] and buf[2] are always
+ * zero.
+ */
+ *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2));
+ *ir_raw = *ir_key;
+ return 1;
+}
+
+
+static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ unsigned char data[12];
+ u32 gpio;
+
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ if (0x400000 & ~gpio)
+ return 0; /* No button press */
+
+ ir->c->addr = 0x5a >> 1;
+
+ if (12 != i2c_master_recv(ir->c, data, 12)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ if (data[9] != (unsigned char)(~data[8]))
+ return 0;
+
+ *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0));
+ *ir_key = *ir_raw;
+
+ return 1;
+}
+
+/* Common (grey or coloured) pinnacle PCTV remote handling
+ *
+ */
+static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
+ int parity_offset, int marker, int code_modulo)
+{
+ unsigned char b[4];
+ unsigned int start = 0,parity = 0,code = 0;
+
+ /* poll IR chip */
+ if (4 != i2c_master_recv(ir->c, b, 4)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ for (start = 0; start < ARRAY_SIZE(b); start++) {
+ if (b[start] == marker) {
+ code=b[(start+parity_offset + 1) % 4];
+ parity=b[(start+parity_offset) % 4];
+ }
+ }
+
+ /* Empty Request */
+ if (parity == 0)
+ return 0;
+
+ /* Repeating... */
+ if (ir->old == parity)
+ return 0;
+
+ ir->old = parity;
+
+ /* drop special codes when a key is held down a long time for the grey controller
+ In this case, the second bit of the code is asserted */
+ if (marker == 0xfe && (code & 0x40))
+ return 0;
+
+ code %= code_modulo;
+
+ *ir_raw = code;
+ *ir_key = code;
+
+ i2cdprintk("Pinnacle PCTV key %02x\n", code);
+
+ return 1;
+}
+
+/* The grey pinnacle PCTV remote
+ *
+ * There are one issue with this remote:
+ * - I2c packet does not change when the same key is pressed quickly. The workaround
+ * is to hold down each key for about half a second, so that another code is generated
+ * in the i2c packet, and the function can distinguish key presses.
+ *
+ * Sylvain Pasche <sylvain.pasche@gmail.com>
+ */
+static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+
+ return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
+}
+
+
+/* The new pinnacle PCTV remote (with the colored buttons)
+ *
+ * Ricardo Cerqueira <v4l@cerqueira.org>
+ */
+static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+{
+ /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
+ *
+ * this is the only value that results in 42 unique
+ * codes < 128
+ */
+
+ return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
+}
+
+void saa7134_input_irq(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return;
+
+ ir = dev->remote;
+ if (!ir->running)
+ return;
+
+ if (!ir->polling && !ir->raw_decode) {
+ build_key(dev);
+ } else if (ir->raw_decode) {
+ saa7134_raw_decode_irq(dev);
+ }
+}
+
+static void saa7134_input_timer(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *)data;
+ struct saa7134_card_ir *ir = dev->remote;
+
+ build_key(dev);
+ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
+}
+
+static void ir_raw_decode_timer_end(unsigned long data)
+{
+ struct saa7134_dev *dev = (struct saa7134_dev *)data;
+
+ ir_raw_event_handle(dev->remote->dev);
+}
+
+static int __saa7134_ir_start(void *priv)
+{
+ struct saa7134_dev *dev = priv;
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return -EINVAL;
+
+ ir = dev->remote;
+ if (ir->running)
+ return 0;
+
+ /* Moved here from saa7134_input_init1() because the latter
+ * is not called on device resume */
+ switch (dev->board) {
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
+ saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ /* Without this we won't receive key up events */
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
+ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
+ break;
+ case SAA7134_BOARD_GOTVIEW_7135:
+ saa_setb(SAA7134_GPIO_GPMODE1, 0x80);
+ break;
+ }
+
+ ir->running = true;
+
+ if (ir->polling) {
+ setup_timer(&ir->timer, saa7134_input_timer,
+ (unsigned long)dev);
+ ir->timer.expires = jiffies + HZ;
+ add_timer(&ir->timer);
+ } else if (ir->raw_decode) {
+ /* set timer_end for code completion */
+ setup_timer(&ir->timer, ir_raw_decode_timer_end,
+ (unsigned long)dev);
+ }
+
+ return 0;
+}
+
+static void __saa7134_ir_stop(void *priv)
+{
+ struct saa7134_dev *dev = priv;
+ struct saa7134_card_ir *ir;
+
+ if (!dev || !dev->remote)
+ return;
+
+ ir = dev->remote;
+ if (!ir->running)
+ return;
+
+ if (ir->polling || ir->raw_decode)
+ del_timer_sync(&ir->timer);
+
+ ir->running = false;
+
+ return;
+}
+
+int saa7134_ir_start(struct saa7134_dev *dev)
+{
+ if (dev->remote->users)
+ return __saa7134_ir_start(dev);
+
+ return 0;
+}
+
+void saa7134_ir_stop(struct saa7134_dev *dev)
+{
+ if (dev->remote->users)
+ __saa7134_ir_stop(dev);
+}
+
+static int saa7134_ir_open(struct rc_dev *rc)
+{
+ struct saa7134_dev *dev = rc->priv;
+
+ dev->remote->users++;
+ return __saa7134_ir_start(dev);
+}
+
+static void saa7134_ir_close(struct rc_dev *rc)
+{
+ struct saa7134_dev *dev = rc->priv;
+
+ dev->remote->users--;
+ if (!dev->remote->users)
+ __saa7134_ir_stop(dev);
+}
+
+int saa7134_input_init1(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir;
+ struct rc_dev *rc;
+ char *ir_codes = NULL;
+ u32 mask_keycode = 0;
+ u32 mask_keydown = 0;
+ u32 mask_keyup = 0;
+ unsigned polling = 0;
+ bool raw_decode = false;
+ int err;
+
+ if (dev->has_remote != SAA7134_REMOTE_GPIO)
+ return -ENODEV;
+ if (disable_ir)
+ return -ENODEV;
+
+ /* detect & configure */
+ switch (dev->board) {
+ case SAA7134_BOARD_FLYVIDEO2000:
+ case SAA7134_BOARD_FLYVIDEO3000:
+ case SAA7134_BOARD_FLYTVPLATINUM_FM:
+ case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
+ case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
+ ir_codes = RC_MAP_FLYVIDEO;
+ mask_keycode = 0xEC00000;
+ mask_keydown = 0x0040000;
+ break;
+ case SAA7134_BOARD_CINERGY400:
+ case SAA7134_BOARD_CINERGY600:
+ case SAA7134_BOARD_CINERGY600_MK3:
+ ir_codes = RC_MAP_CINERGY;
+ mask_keycode = 0x00003f;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_ECS_TVP3XP:
+ case SAA7134_BOARD_ECS_TVP3XP_4CB5:
+ ir_codes = RC_MAP_EZTV;
+ mask_keycode = 0x00017c;
+ mask_keyup = 0x000002;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_KWORLD_XPERT:
+ case SAA7134_BOARD_AVACSSMARTTV:
+ ir_codes = RC_MAP_PIXELVIEW;
+ mask_keycode = 0x00001F;
+ mask_keyup = 0x000020;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_MD2819:
+ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
+ case SAA7134_BOARD_AVERMEDIA_305:
+ case SAA7134_BOARD_AVERMEDIA_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
+ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
+ case SAA7134_BOARD_AVERMEDIA_M102:
+ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
+ ir_codes = RC_MAP_AVERMEDIA;
+ mask_keycode = 0x0007C8;
+ mask_keydown = 0x000010;
+ polling = 50; // ms
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M135A:
+ ir_codes = RC_MAP_AVERMEDIA_M135A;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_M733A:
+ ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6;
+ mask_keydown = 0x0040000;
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_777:
+ case SAA7134_BOARD_AVERMEDIA_A16AR:
+ ir_codes = RC_MAP_AVERMEDIA;
+ mask_keycode = 0x02F200;
+ mask_keydown = 0x000400;
+ polling = 50; // ms
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_AVERMEDIA_A16D:
+ ir_codes = RC_MAP_AVERMEDIA_A16D;
+ mask_keycode = 0x02F200;
+ mask_keydown = 0x000400;
+ polling = 50; /* ms */
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_KWORLD_TERMINATOR:
+ ir_codes = RC_MAP_PIXELVIEW;
+ mask_keycode = 0x00001f;
+ mask_keyup = 0x000060;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_MANLI_MTV001:
+ case SAA7134_BOARD_MANLI_MTV002:
+ ir_codes = RC_MAP_MANLI;
+ mask_keycode = 0x001f00;
+ mask_keyup = 0x004000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_BEHOLD_409FM:
+ case SAA7134_BOARD_BEHOLD_401:
+ case SAA7134_BOARD_BEHOLD_403:
+ case SAA7134_BOARD_BEHOLD_403FM:
+ case SAA7134_BOARD_BEHOLD_405:
+ case SAA7134_BOARD_BEHOLD_405FM:
+ case SAA7134_BOARD_BEHOLD_407:
+ case SAA7134_BOARD_BEHOLD_407FM:
+ case SAA7134_BOARD_BEHOLD_409:
+ case SAA7134_BOARD_BEHOLD_505FM:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_505RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507_9FM:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_507RDS_MK5:
+ ir_codes = RC_MAP_MANLI;
+ mask_keycode = 0x003f00;
+ mask_keyup = 0x004000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+ ir_codes = RC_MAP_BEHOLD_COLUMBUS;
+ mask_keycode = 0x003f00;
+ mask_keyup = 0x004000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
+ ir_codes = RC_MAP_PCTV_SEDNA;
+ mask_keycode = 0x001f00;
+ mask_keyup = 0x004000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_GOTVIEW_7135:
+ ir_codes = RC_MAP_GOTVIEW7135;
+ mask_keycode = 0x0003CC;
+ mask_keydown = 0x000010;
+ polling = 5; /* ms */
+ /* GPIO stuff moved to __saa7134_ir_start() */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_TV_PVR:
+ case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
+ case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
+ ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
+ mask_keycode = 0x00003F;
+ mask_keyup = 0x400000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_PROTEUS_2309:
+ ir_codes = RC_MAP_PROTEUS_2309;
+ mask_keycode = 0x00007F;
+ mask_keyup = 0x000080;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
+ mask_keycode = 0x003F00;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ case SAA7134_BOARD_FLYDVBT_LR301:
+ case SAA7134_BOARD_FLYDVBTDUO:
+ ir_codes = RC_MAP_FLYDVB;
+ mask_keycode = 0x0001F00;
+ mask_keydown = 0x0040000;
+ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
+ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+ ir_codes = RC_MAP_ASUS_PC39;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
+ ir_codes = RC_MAP_ASUS_PS3_100;
+ mask_keydown = 0x0040000;
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_ENCORE_ENLTV:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM:
+ ir_codes = RC_MAP_ENCORE_ENLTV;
+ mask_keycode = 0x00007f;
+ mask_keyup = 0x040000;
+ polling = 50; // ms
+ break;
+ case SAA7134_BOARD_ENCORE_ENLTV_FM53:
+ case SAA7134_BOARD_ENCORE_ENLTV_FM3:
+ ir_codes = RC_MAP_ENCORE_ENLTV_FM53;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ case SAA7134_BOARD_10MOONSTVMASTER3:
+ ir_codes = RC_MAP_ENCORE_ENLTV;
+ mask_keycode = 0x5f80000;
+ mask_keyup = 0x8000000;
+ polling = 50; //ms
+ break;
+ case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
+ ir_codes = RC_MAP_GENIUS_TVGO_A11MCE;
+ mask_keycode = 0xff;
+ mask_keydown = 0xf00000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_REAL_ANGEL_220:
+ ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS;
+ mask_keycode = 0x3f00;
+ mask_keyup = 0x4000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
+ ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG;
+ mask_keycode = 0x7f;
+ polling = 40; /* ms */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ ir_codes = RC_MAP_VIDEOMATE_S350;
+ mask_keycode = 0x003f00;
+ mask_keydown = 0x040000;
+ break;
+ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
+ ir_codes = RC_MAP_WINFAST;
+ mask_keycode = 0x5f00;
+ mask_keyup = 0x020000;
+ polling = 50; /* ms */
+ break;
+ case SAA7134_BOARD_VIDEOMATE_M1F:
+ ir_codes = RC_MAP_VIDEOMATE_K100;
+ mask_keycode = 0x0ff00;
+ mask_keyup = 0x040000;
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1150:
+ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
+ ir_codes = RC_MAP_HAUPPAUGE;
+ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
+ }
+ if (NULL == ir_codes) {
+ printk("%s: Oops: IR config error [card=%d]\n",
+ dev->name, dev->board);
+ return -ENODEV;
+ }
+
+ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!ir || !rc) {
+ err = -ENOMEM;
+ goto err_out_free;
+ }
+
+ ir->dev = rc;
+ dev->remote = ir;
+
+ /* init hardware-specific stuff */
+ ir->mask_keycode = mask_keycode;
+ ir->mask_keydown = mask_keydown;
+ ir->mask_keyup = mask_keyup;
+ ir->polling = polling;
+ ir->raw_decode = raw_decode;
+
+ /* init input device */
+ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
+ saa7134_boards[dev->board].name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
+ pci_name(dev->pci));
+
+ rc->priv = dev;
+ rc->open = saa7134_ir_open;
+ rc->close = saa7134_ir_close;
+ if (raw_decode)
+ rc->driver_type = RC_DRIVER_IR_RAW;
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_PCI;
+ rc->input_id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+ rc->input_id.vendor = dev->pci->subsystem_vendor;
+ rc->input_id.product = dev->pci->subsystem_device;
+ } else {
+ rc->input_id.vendor = dev->pci->vendor;
+ rc->input_id.product = dev->pci->device;
+ }
+ rc->dev.parent = &dev->pci->dev;
+ rc->map_name = ir_codes;
+ rc->driver_name = MODULE_NAME;
+
+ err = rc_register_device(rc);
+ if (err)
+ goto err_out_free;
+
+ return 0;
+
+err_out_free:
+ rc_free_device(rc);
+ dev->remote = NULL;
+ kfree(ir);
+ return err;
+}
+
+void saa7134_input_fini(struct saa7134_dev *dev)
+{
+ if (NULL == dev->remote)
+ return;
+
+ saa7134_ir_stop(dev);
+ rc_unregister_device(dev->remote->dev);
+ kfree(dev->remote);
+ dev->remote = NULL;
+}
+
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
+{
+ struct i2c_board_info info;
+ struct i2c_msg msg_msi = {
+ .addr = 0x50,
+ .flags = I2C_M_RD,
+ .len = 0,
+ .buf = NULL,
+ };
+ int rc;
+
+ if (disable_ir) {
+ dprintk("IR has been disabled, not probing for i2c remote\n");
+ return;
+ }
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&dev->init_data, 0, sizeof(dev->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ switch (dev->board) {
+ case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+ dev->init_data.name = "Pinnacle PCTV";
+ if (pinnacle_remote == 0) {
+ dev->init_data.get_key = get_key_pinnacle_color;
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR;
+ info.addr = 0x47;
+ } else {
+ dev->init_data.get_key = get_key_pinnacle_grey;
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
+ info.addr = 0x47;
+ }
+ break;
+ case SAA7134_BOARD_UPMOST_PURPLE_TV:
+ dev->init_data.name = "Purple TV";
+ dev->init_data.get_key = get_key_purpletv;
+ dev->init_data.ir_codes = RC_MAP_PURPLETV;
+ info.addr = 0x7a;
+ break;
+ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
+ dev->init_data.name = "MSI TV@nywhere Plus";
+ dev->init_data.get_key = get_key_msi_tvanywhere_plus;
+ dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
+ /*
+ * MSI TV@nyware Plus requires more frequent polling
+ * otherwise it will miss some keypresses
+ */
+ dev->init_data.polling_interval = 50;
+ info.addr = 0x30;
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird...
+ REVISIT: might no longer be needed */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (1 == rc) ? "yes" : "no");
+ break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ /* copied and modified from MSI TV@nywhere Plus */
+ dev->init_data.name = "Kworld PC150-U";
+ dev->init_data.get_key = get_key_kworld_pc150u;
+ dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
+ info.addr = 0x30;
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird...
+ REVISIT: might no longer be needed */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (1 == rc) ? "yes" : "no");
+ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ dev->init_data.name = "HVR 1110";
+ dev->init_data.get_key = get_key_hvr1110;
+ dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+ info.addr = 0x71;
+ break;
+ case SAA7134_BOARD_BEHOLD_607FM_MK3:
+ case SAA7134_BOARD_BEHOLD_607FM_MK5:
+ case SAA7134_BOARD_BEHOLD_609FM_MK3:
+ case SAA7134_BOARD_BEHOLD_609FM_MK5:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_607RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK3:
+ case SAA7134_BOARD_BEHOLD_609RDS_MK5:
+ case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
+ case SAA7134_BOARD_BEHOLD_H6:
+ case SAA7134_BOARD_BEHOLD_X7:
+ case SAA7134_BOARD_BEHOLD_H7:
+ case SAA7134_BOARD_BEHOLD_A7:
+ dev->init_data.name = "BeholdTV";
+ dev->init_data.get_key = get_key_beholdm6xx;
+ dev->init_data.ir_codes = RC_MAP_BEHOLD;
+ dev->init_data.type = RC_TYPE_NEC;
+ info.addr = 0x2d;
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
+ info.addr = 0x40;
+ break;
+ case SAA7134_BOARD_FLYDVB_TRIO:
+ dev->init_data.name = "FlyDVB Trio";
+ dev->init_data.get_key = get_key_flydvb_trio;
+ dev->init_data.ir_codes = RC_MAP_FLYDVB;
+ info.addr = 0x0b;
+ break;
+ default:
+ dprintk("No I2C IR support for board %x\n", dev->board);
+ return;
+ }
+
+ if (dev->init_data.name)
+ info.platform_data = &dev->init_data;
+ i2c_new_device(&dev->i2c_adap, &info);
+}
+
+static int saa7134_raw_decode_irq(struct saa7134_dev *dev)
+{
+ struct saa7134_card_ir *ir = dev->remote;
+ unsigned long timeout;
+ int space;
+
+ /* Generate initial event */
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
+ ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE);
+
+ /*
+ * Wait 15 ms from the start of the first IR event before processing
+ * the event. This time is enough for NEC protocol. May need adjustments
+ * to work with other protocols.
+ */
+ smp_mb();
+
+ if (!timer_pending(&ir->timer)) {
+ timeout = jiffies + msecs_to_jiffies(15);
+ mod_timer(&ir->timer, timeout);
+ }
+
+ return 1;
+}
diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h
index e7e0af101fa7..e7e0af101fa7 100644
--- a/drivers/media/video/saa7134/saa7134-reg.h
+++ b/drivers/media/pci/saa7134/saa7134-reg.h
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c
index 2e3f4b412d8c..2e3f4b412d8c 100644
--- a/drivers/media/video/saa7134/saa7134-ts.c
+++ b/drivers/media/pci/saa7134/saa7134-ts.c
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c
index b7a99bee2f98..b7a99bee2f98 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c
diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c
index e9aa94b807f1..e9aa94b807f1 100644
--- a/drivers/media/video/saa7134/saa7134-vbi.c
+++ b/drivers/media/pci/saa7134/saa7134-vbi.c
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
new file mode 100644
index 000000000000..22f8758d047f
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -0,0 +1,2661 @@
+/*
+ *
+ * device driver for philips saa7134 based TV cards
+ * video4linux video interface
+ *
+ * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include <media/v4l2-common.h>
+#include <media/saa6588.h>
+
+/* ------------------------------------------------------------------ */
+
+unsigned int video_debug;
+static unsigned int gbuffers = 8;
+static unsigned int noninterlaced; /* 0 */
+static unsigned int gbufsize = 720*576*4;
+static unsigned int gbufsize_max = 720*576*4;
+static char secam[] = "--";
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
+module_param(gbuffers, int, 0444);
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
+module_param(noninterlaced, int, 0644);
+MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
+module_param_string(secam, secam, sizeof(secam), 0644);
+MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
+
+
+#define dprintk(fmt, arg...) if (video_debug&0x04) \
+ printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x191 */
+
+/* Bit 0: VIP code T bit polarity */
+
+#define VP_T_CODE_P_NON_INVERTED 0x00
+#define VP_T_CODE_P_INVERTED 0x01
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x195 */
+
+/* Bit 2: Video output clock delay control */
+
+#define VP_CLK_CTRL2_NOT_DELAYED 0x00
+#define VP_CLK_CTRL2_DELAYED 0x04
+
+/* Bit 1: Video output clock invert control */
+
+#define VP_CLK_CTRL1_NON_INVERTED 0x00
+#define VP_CLK_CTRL1_INVERTED 0x02
+
+/* ------------------------------------------------------------------ */
+/* Defines for Video Output Port Register at address 0x196 */
+
+/* Bits 2 to 0: VSYNC pin video vertical sync type */
+
+#define VP_VS_TYPE_MASK 0x07
+
+#define VP_VS_TYPE_OFF 0x00
+#define VP_VS_TYPE_V123 0x01
+#define VP_VS_TYPE_V_ITU 0x02
+#define VP_VS_TYPE_VGATE_L 0x03
+#define VP_VS_TYPE_RESERVED1 0x04
+#define VP_VS_TYPE_RESERVED2 0x05
+#define VP_VS_TYPE_F_ITU 0x06
+#define VP_VS_TYPE_SC_FID 0x07
+
+/* ------------------------------------------------------------------ */
+/* data structs for video */
+
+static int video_out[][9] = {
+ [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
+};
+
+static struct saa7134_format formats[] = {
+ {
+ .name = "8 bpp gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8,
+ .pm = 0x06,
+ },{
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ },{
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .pm = 0x13 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ },{
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .pm = 0x10 | 0x80,
+ .bswap = 1,
+ },{
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .depth = 24,
+ .pm = 0x11,
+ },{
+ .name = "24 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .pm = 0x11,
+ .bswap = 1,
+ },{
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .depth = 32,
+ .pm = 0x12,
+ },{
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .pm = 0x12,
+ .bswap = 1,
+ .wswap = 1,
+ },{
+ .name = "4:2:2 packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .pm = 0x00,
+ .bswap = 1,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .pm = 0x00,
+ .yuv = 1,
+ },{
+ .name = "4:2:2 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16,
+ .pm = 0x09,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 0,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .hshift = 1,
+ .vshift = 1,
+ },{
+ .name = "4:2:0 planar, Y-Cb-Cr",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .depth = 12,
+ .pm = 0x0a,
+ .yuv = 1,
+ .planar = 1,
+ .uvswap = 1,
+ .hshift = 1,
+ .vshift = 1,
+ }
+};
+#define FORMATS ARRAY_SIZE(formats)
+
+#define NORM_625_50 \
+ .h_start = 0, \
+ .h_stop = 719, \
+ .video_v_start = 24, \
+ .video_v_stop = 311, \
+ .vbi_v_start_0 = 7, \
+ .vbi_v_stop_0 = 22, \
+ .vbi_v_start_1 = 319, \
+ .src_timing = 4
+
+#define NORM_525_60 \
+ .h_start = 0, \
+ .h_stop = 719, \
+ .video_v_start = 23, \
+ .video_v_stop = 262, \
+ .vbi_v_start_0 = 10, \
+ .vbi_v_stop_0 = 21, \
+ .vbi_v_start_1 = 273, \
+ .src_timing = 7
+
+static struct saa7134_tvnorm tvnorms[] = {
+ {
+ .name = "PAL", /* autodetect */
+ .id = V4L2_STD_PAL,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "NTSC",
+ .id = V4L2_STD_NTSC,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x89,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "SECAM",
+ .id = V4L2_STD_SECAM,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "SECAM-Lc",
+ .id = V4L2_STD_SECAM_LC,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x1b,
+ .chroma_ctrl1 = 0xd1,
+ .chroma_gain = 0x80,
+ .chroma_ctrl2 = 0x00,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ NORM_525_60,
+
+ .sync_control = 0x59,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xb9,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x0e,
+ .vgate_misc = 0x18,
+
+ },{
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ NORM_625_50,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0xa1,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+
+ },{
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+
+ .h_start = 0,
+ .h_stop = 719,
+ .video_v_start = 23,
+ .video_v_stop = 262,
+ .vbi_v_start_0 = 10,
+ .vbi_v_stop_0 = 21,
+ .vbi_v_start_1 = 273,
+ .src_timing = 7,
+
+ .sync_control = 0x18,
+ .luma_control = 0x40,
+ .chroma_ctrl1 = 0x81,
+ .chroma_gain = 0x2a,
+ .chroma_ctrl2 = 0x06,
+ .vgate_misc = 0x1c,
+ }
+};
+#define TVNORMS ARRAY_SIZE(tvnorms)
+
+#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4)
+
+static const struct v4l2_queryctrl no_ctrl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl video_ctrls[] = {
+ /* --- video --- */
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 128,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 68,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 64,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_HFLIP,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ /* --- audio --- */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = -15,
+ .maximum = 15,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ /* --- private --- */
+ {
+ .id = V4L2_CID_PRIVATE_INVERT,
+ .name = "Invert",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_ODD,
+ .name = "y offset odd field",
+ .minimum = 0,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_Y_EVEN,
+ .name = "y offset even field",
+ .minimum = 0,
+ .maximum = 128,
+ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_PRIVATE_AUTOMUTE,
+ .name = "automute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
+static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
+
+static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < CTRLS; i++)
+ if (video_ctrls[i].id == id)
+ return video_ctrls+i;
+ return NULL;
+}
+
+static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < FORMATS; i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+ return NULL;
+}
+
+/* ----------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
+{
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk("res: get %d\n",bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct saa7134_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct saa7134_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static
+void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk("res: put %d\n",bits);
+ mutex_unlock(&dev->lock);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+{
+ dprintk("set tv norm = %s\n",norm->name);
+ dev->tvnorm = norm;
+
+ /* setup cropping */
+ dev->crop_bounds.left = norm->h_start;
+ dev->crop_defrect.left = norm->h_start;
+ dev->crop_bounds.width = norm->h_stop - norm->h_start +1;
+ dev->crop_defrect.width = norm->h_stop - norm->h_start +1;
+
+ dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2;
+ dev->crop_defrect.top = norm->video_v_start*2;
+ dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
+ - dev->crop_bounds.top;
+ dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
+
+ dev->crop_current = dev->crop_defrect;
+
+ saa7134_set_tvnorm_hw(dev);
+}
+
+static void video_mux(struct saa7134_dev *dev, int input)
+{
+ dprintk("video input = %d [%s]\n", input, card_in(dev, input).name);
+ dev->ctl_input = input;
+ set_tvnorm(dev, dev->tvnorm);
+ saa7134_tvaudio_setinput(dev, &card_in(dev, input));
+}
+
+
+static void saa7134_set_decoder(struct saa7134_dev *dev)
+{
+ int luma_control, sync_control, mux;
+
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+ mux = card_in(dev, dev->ctl_input).vmux;
+
+ luma_control = norm->luma_control;
+ sync_control = norm->sync_control;
+
+ if (mux > 5)
+ luma_control |= 0x80; /* svideo */
+ if (noninterlaced || dev->nosignal)
+ sync_control |= 0x20;
+
+ /* setup video decoder */
+ saa_writeb(SAA7134_INCR_DELAY, 0x08);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90);
+ saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90);
+ saa_writeb(SAA7134_HSYNC_START, 0xeb);
+ saa_writeb(SAA7134_HSYNC_STOP, 0xe0);
+ saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing);
+
+ saa_writeb(SAA7134_SYNC_CTRL, sync_control);
+ saa_writeb(SAA7134_LUMA_CTRL, luma_control);
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1);
+ saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain);
+
+ saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2);
+ saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00);
+
+ saa_writeb(SAA7134_ANALOG_ADC, 0x01);
+ saa_writeb(SAA7134_VGATE_START, 0x11);
+ saa_writeb(SAA7134_VGATE_STOP, 0xfe);
+ saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc);
+ saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
+ saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
+}
+
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
+{
+ saa7134_set_decoder(dev);
+
+ if (card_in(dev, dev->ctl_input).tv)
+ saa_call_all(dev, core, s_std, dev->tvnorm->id);
+ /* Set the correct norm for the saa6752hs. This function
+ does nothing if there is no saa6752hs. */
+ saa_call_empress(dev, core, s_std, dev->tvnorm->id);
+}
+
+static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
+{
+ static const struct {
+ int xpsc;
+ int xacl;
+ int xc2_1;
+ int xdcg;
+ int vpfy;
+ } vals[] = {
+ /* XPSC XACL XC2_1 XDCG VPFY */
+ { 1, 0, 0, 0, 0 },
+ { 2, 2, 1, 2, 2 },
+ { 3, 4, 1, 3, 2 },
+ { 4, 8, 1, 4, 2 },
+ { 5, 8, 1, 4, 2 },
+ { 6, 8, 1, 4, 3 },
+ { 7, 8, 1, 4, 3 },
+ { 8, 15, 0, 4, 3 },
+ { 9, 15, 0, 4, 3 },
+ { 10, 16, 1, 5, 3 },
+ };
+ static const int count = ARRAY_SIZE(vals);
+ int i;
+
+ for (i = 0; i < count; i++)
+ if (vals[i].xpsc == prescale)
+ break;
+ if (i == count)
+ return;
+
+ saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
+ saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
+ saa_writeb(SAA7134_LEVEL_CTRL(task),
+ (vals[i].xc2_1 << 3) | (vals[i].xdcg));
+ saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
+ (vals[i].vpfy << 2) | vals[i].vpfy);
+}
+
+static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
+{
+ int val,mirror;
+
+ saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff);
+ saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
+
+ mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
+ if (yscale < 2048) {
+ /* LPI */
+ dprintk("yscale LPI yscale=%d\n",yscale);
+ saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
+ } else {
+ /* ACM */
+ val = 0x40 * 1024 / yscale;
+ dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val);
+ saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
+ saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
+ saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
+ }
+ saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);
+}
+
+static void set_size(struct saa7134_dev *dev, int task,
+ int width, int height, int interlace)
+{
+ int prescale,xscale,yscale,y_even,y_odd;
+ int h_start, h_stop, v_start, v_stop;
+ int div = interlace ? 2 : 1;
+
+ /* setup video scaler */
+ h_start = dev->crop_current.left;
+ v_start = dev->crop_current.top/2;
+ h_stop = (dev->crop_current.left + dev->crop_current.width -1);
+ v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2;
+
+ saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
+ saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8);
+ saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
+ saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff);
+ saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8);
+
+ prescale = dev->crop_current.width / width;
+ if (0 == prescale)
+ prescale = 1;
+ xscale = 1024 * dev->crop_current.width / prescale / width;
+ yscale = 512 * div * dev->crop_current.height / height;
+ dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale);
+ set_h_prescale(dev,task,prescale);
+ saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff);
+ saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8);
+ set_v_scale(dev,task,yscale);
+
+ saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff);
+ saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8);
+ saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff);
+ saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8);
+
+ /* deinterlace y offsets */
+ y_odd = dev->ctl_y_odd;
+ y_even = dev->ctl_y_even;
+ saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
+ saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
+ saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct cliplist {
+ __u16 position;
+ __u8 enable;
+ __u8 disable;
+};
+
+static void set_cliplist(struct saa7134_dev *dev, int reg,
+ struct cliplist *cl, int entries, char *name)
+{
+ __u8 winbits = 0;
+ int i;
+
+ for (i = 0; i < entries; i++) {
+ winbits |= cl[i].enable;
+ winbits &= ~cl[i].disable;
+ if (i < 15 && cl[i].position == cl[i+1].position)
+ continue;
+ saa_writeb(reg + 0, winbits);
+ saa_writeb(reg + 2, cl[i].position & 0xff);
+ saa_writeb(reg + 3, cl[i].position >> 8);
+ dprintk("clip: %s winbits=%02x pos=%d\n",
+ name,winbits,cl[i].position);
+ reg += 8;
+ }
+ for (; reg < 0x400; reg += 8) {
+ saa_writeb(reg+ 0, 0);
+ saa_writeb(reg + 1, 0);
+ saa_writeb(reg + 2, 0);
+ saa_writeb(reg + 3, 0);
+ }
+}
+
+static int clip_range(int val)
+{
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+/* Sort into smallest position first order */
+static int cliplist_cmp(const void *a, const void *b)
+{
+ const struct cliplist *cla = a;
+ const struct cliplist *clb = b;
+ if (cla->position < clb->position)
+ return -1;
+ if (cla->position > clb->position)
+ return 1;
+ return 0;
+}
+
+static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
+ int nclips, int interlace)
+{
+ struct cliplist col[16], row[16];
+ int cols = 0, rows = 0, i;
+ int div = interlace ? 2 : 1;
+
+ memset(col, 0, sizeof(col));
+ memset(row, 0, sizeof(row));
+ for (i = 0; i < nclips && i < 8; i++) {
+ col[cols].position = clip_range(clips[i].c.left);
+ col[cols].enable = (1 << i);
+ cols++;
+ col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
+ col[cols].disable = (1 << i);
+ cols++;
+ row[rows].position = clip_range(clips[i].c.top / div);
+ row[rows].enable = (1 << i);
+ rows++;
+ row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
+ / div);
+ row[rows].disable = (1 << i);
+ rows++;
+ }
+ sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
+ sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
+ set_cliplist(dev,0x380,col,cols,"cols");
+ set_cliplist(dev,0x384,row,rows,"rows");
+ return 0;
+}
+
+static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ if (NULL == dev->ovbuf.base)
+ return -EINVAL;
+ if (NULL == dev->ovfmt)
+ return -EINVAL;
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+ if (win->clipcount > 2048)
+ return -EINVAL;
+
+ field = win->field;
+ maxw = dev->crop_current.width;
+ maxh = dev->crop_current.height;
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+ return 0;
+}
+
+static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ unsigned long base,control,bpl;
+ int err;
+
+ err = verify_preview(dev,&fh->win);
+ if (0 != err)
+ return err;
+
+ dev->ovfield = fh->win.field;
+ dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
+ fh->win.w.width,fh->win.w.height,
+ fh->win.w.left,fh->win.w.top,
+ dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
+
+ /* setup window + clipping */
+ set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ setup_clipping(dev,fh->clips,fh->nclips,
+ V4L2_FIELD_HAS_BOTH(dev->ovfield));
+ if (dev->ovfmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
+
+ /* dma: setup channel 1 (= Video Task B) */
+ base = (unsigned long)dev->ovbuf.base;
+ base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
+ base += dev->ovfmt->depth/8 * fh->win.w.left;
+ bpl = dev->ovbuf.fmt.bytesperline;
+ control = SAA7134_RS_CONTROL_BURST_16;
+ if (dev->ovfmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (dev->ovfmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(1),bpl*2);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ } else {
+ saa_writel(SAA7134_RS_BA1(1),base);
+ saa_writel(SAA7134_RS_BA2(1),base);
+ saa_writel(SAA7134_RS_PITCH(1),bpl);
+ saa_writel(SAA7134_RS_CONTROL(1),control);
+ }
+
+ /* start dma */
+ dev->ovenable = 1;
+ saa7134_set_dmabits(dev);
+
+ return 0;
+}
+
+static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+ dev->ovenable = 0;
+ saa7134_set_dmabits(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int buffer_activate(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next)
+{
+ unsigned long base,control,bpl;
+ unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
+
+ dprintk("buffer_activate buf=%p\n",buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->top_seen = 0;
+
+ set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
+ V4L2_FIELD_HAS_BOTH(buf->vb.field));
+ if (buf->fmt->yuv)
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
+ else
+ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
+ saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
+
+ /* DMA: setup channel 0 (= Video Task A0) */
+ base = saa7134_buffer_base(buf);
+ if (buf->fmt->planar)
+ bpl = buf->vb.width;
+ else
+ bpl = (buf->vb.width * buf->fmt->depth) / 8;
+ control = SAA7134_RS_CONTROL_BURST_16 |
+ SAA7134_RS_CONTROL_ME |
+ (buf->pt->dma >> 12);
+ if (buf->fmt->bswap)
+ control |= SAA7134_RS_CONTROL_BSWAP;
+ if (buf->fmt->wswap)
+ control |= SAA7134_RS_CONTROL_WSWAP;
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base+bpl);
+ saa_writel(SAA7134_RS_PITCH(0),bpl*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(0),base);
+ saa_writel(SAA7134_RS_BA2(0),base);
+ saa_writel(SAA7134_RS_PITCH(0),bpl);
+ }
+ saa_writel(SAA7134_RS_CONTROL(0),control);
+
+ if (buf->fmt->planar) {
+ /* DMA: setup channel 4+5 (= planar task A) */
+ bpl_uv = bpl >> buf->fmt->hshift;
+ lines_uv = buf->vb.height >> buf->fmt->vshift;
+ base2 = base + bpl * buf->vb.height;
+ base3 = base2 + bpl_uv * lines_uv;
+ if (buf->fmt->uvswap)
+ tmp = base2, base2 = base3, base3 = tmp;
+ dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
+ bpl_uv,lines_uv,base2,base3);
+ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
+ /* interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
+ } else {
+ /* non-interlaced */
+ saa_writel(SAA7134_RS_BA1(4),base2);
+ saa_writel(SAA7134_RS_BA2(4),base2);
+ saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
+ saa_writel(SAA7134_RS_BA1(5),base3);
+ saa_writel(SAA7134_RS_BA2(5),base3);
+ saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
+ }
+ saa_writel(SAA7134_RS_CONTROL(4),control);
+ saa_writel(SAA7134_RS_CONTROL(5),control);
+ }
+
+ /* start DMA */
+ saa7134_set_dmabits(dev);
+ mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+ unsigned int size;
+ int err;
+
+ /* sanity checks */
+ if (NULL == fh->fmt)
+ return -EINVAL;
+ if (fh->width < 48 ||
+ fh->height < 32 ||
+ fh->width/4 > dev->crop_current.width ||
+ fh->height/4 > dev->crop_current.height ||
+ fh->width > dev->crop_bounds.width ||
+ fh->height > dev->crop_bounds.height)
+ return -EINVAL;
+ size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
+ vb->i,fh->width,fh->height,size,v4l2_field_names[field],
+ fh->fmt->name);
+ if (buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.size != size ||
+ buf->vb.field != field ||
+ buf->fmt != fh->fmt) {
+ saa7134_dma_free(q,buf);
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
+
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.size = size;
+ buf->vb.field = field;
+ buf->fmt = fh->fmt;
+ buf->pt = &fh->pt_cap;
+ dev->video_q.curr = NULL;
+
+ err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
+ if (err)
+ goto oops;
+ err = saa7134_pgtable_build(dev->pci,buf->pt,
+ dma->sglist,
+ dma->sglen,
+ saa7134_buffer_startpage(buf));
+ if (err)
+ goto oops;
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ buf->activate = buffer_activate;
+ return 0;
+
+ oops:
+ saa7134_dma_free(q,buf);
+ return err;
+}
+
+static int
+buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ struct saa7134_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ if (0 == *count)
+ *count = gbuffers;
+ *count = saa7134_buffer_count(*size,*count);
+ return 0;
+}
+
+static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_fh *fh = q->priv_data;
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
+}
+
+static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
+
+ saa7134_dma_free(q,buf);
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ return -EINVAL;
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ c->value = dev->ctl_bright;
+ break;
+ case V4L2_CID_HUE:
+ c->value = dev->ctl_hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ c->value = dev->ctl_contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ c->value = dev->ctl_saturation;
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ c->value = dev->ctl_mute;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ c->value = dev->ctl_volume;
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ c->value = dev->ctl_invert;
+ break;
+ case V4L2_CID_HFLIP:
+ c->value = dev->ctl_mirror;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ c->value = dev->ctl_y_even;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ c->value = dev->ctl_y_odd;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ c->value = dev->ctl_automute;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);
+
+static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
+{
+ struct saa7134_fh *fh = priv;
+
+ return saa7134_g_ctrl_internal(fh->dev, fh, c);
+}
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
+{
+ const struct v4l2_queryctrl* ctrl;
+ unsigned long flags;
+ int restart_overlay = 0;
+ int err;
+
+ /* When called from the empress code fh == NULL.
+ That needs to be fixed somehow, but for now this is
+ good enough. */
+ if (fh) {
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+ }
+ err = -EINVAL;
+
+ mutex_lock(&dev->lock);
+
+ ctrl = ctrl_by_id(c->id);
+ if (NULL == ctrl)
+ goto error;
+
+ dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER:
+ if (c->value < ctrl->minimum)
+ c->value = ctrl->minimum;
+ if (c->value > ctrl->maximum)
+ c->value = ctrl->maximum;
+ break;
+ default:
+ /* nothing */;
+ };
+ switch (c->id) {
+ case V4L2_CID_BRIGHTNESS:
+ dev->ctl_bright = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
+ break;
+ case V4L2_CID_HUE:
+ dev->ctl_hue = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
+ break;
+ case V4L2_CID_CONTRAST:
+ dev->ctl_contrast = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ dev->ctl_saturation = c->value;
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_AUDIO_MUTE:
+ dev->ctl_mute = c->value;
+ saa7134_tvaudio_setmute(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->ctl_volume = c->value;
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ break;
+ case V4L2_CID_PRIVATE_INVERT:
+ dev->ctl_invert = c->value;
+ saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
+ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
+ saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
+ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
+ break;
+ case V4L2_CID_HFLIP:
+ dev->ctl_mirror = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_EVEN:
+ dev->ctl_y_even = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_Y_ODD:
+ dev->ctl_y_odd = c->value;
+ restart_overlay = 1;
+ break;
+ case V4L2_CID_PRIVATE_AUTOMUTE:
+ {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ dev->ctl_automute = c->value;
+ if (dev->tda9887_conf) {
+ if (dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ else
+ dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
+
+ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ }
+ break;
+ }
+ default:
+ goto error;
+ }
+ if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ start_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ }
+ err = 0;
+
+error:
+ mutex_unlock(&dev->lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);
+
+static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
+{
+ struct saa7134_fh *fh = f;
+
+ return saa7134_s_ctrl_internal(fh->dev, fh, c);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
+{
+ struct videobuf_queue* q = NULL;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ q = &fh->cap;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ q = &fh->vbi;
+ break;
+ default:
+ BUG();
+ }
+ return q;
+}
+
+static int saa7134_resource(struct saa7134_fh *fh)
+{
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return RESOURCE_VIDEO;
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ return RESOURCE_VBI;
+
+ BUG();
+ return 0;
+}
+
+static int video_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct saa7134_dev *dev = video_drvdata(file);
+ struct saa7134_fh *fh;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
+ radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh),GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+ fh->width = 720;
+ fh->height = 576;
+ v4l2_prio_open(&dev->prio, &fh->prio);
+
+ videobuf_queue_sg_init(&fh->cap, &video_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct saa7134_buf),
+ fh, NULL);
+ videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
+ &dev->pci->dev, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct saa7134_buf),
+ fh, NULL);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
+
+ if (fh->radio) {
+ /* switch to radio mode */
+ saa7134_tvaudio_setinput(dev,&card(dev).radio);
+ saa_call_all(dev, tuner, s_radio);
+ } else {
+ /* switch to video/vbi mode */
+ video_mux(dev,dev->ctl_input);
+ }
+ return 0;
+}
+
+static ssize_t
+video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(saa7134_queue(fh),
+ data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(saa7134_queue(fh),
+ data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int
+video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct videobuf_buffer *buf = NULL;
+ unsigned int rc = 0;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
+ return videobuf_poll_stream(file, &fh->vbi, wait);
+
+ if (res_check(fh,RESOURCE_VIDEO)) {
+ mutex_lock(&fh->cap.vb_lock);
+ if (!list_empty(&fh->cap.stream))
+ buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
+ } else {
+ mutex_lock(&fh->cap.vb_lock);
+ if (UNSET == fh->cap.read_off) {
+ /* need to capture a new frame */
+ if (res_locked(fh->dev,RESOURCE_VIDEO))
+ goto err;
+ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field))
+ goto err;
+ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
+ fh->cap.read_off = 0;
+ }
+ buf = fh->cap.read_buf;
+ }
+
+ if (!buf)
+ goto err;
+
+ poll_wait(file, &buf->done, wait);
+ if (buf->state == VIDEOBUF_DONE ||
+ buf->state == VIDEOBUF_ERROR)
+ rc = POLLIN|POLLRDNORM;
+ mutex_unlock(&fh->cap.vb_lock);
+ return rc;
+
+err:
+ mutex_unlock(&fh->cap.vb_lock);
+ return POLLERR;
+}
+
+static int video_release(struct file *file)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+ unsigned long flags;
+
+ saa7134_tvaudio_close(dev);
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock,flags);
+ stop_preview(dev,fh);
+ spin_unlock_irqrestore(&dev->slock,flags);
+ res_free(dev,fh,RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->cap);
+ res_free(dev,fh,RESOURCE_VIDEO);
+ }
+ if (fh->cap.read_buf) {
+ buffer_release(&fh->cap,fh->cap.read_buf);
+ kfree(fh->cap.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ videobuf_stop(&fh->vbi);
+ res_free(dev,fh,RESOURCE_VBI);
+ }
+
+ /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
+ saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
+
+ saa_call_all(dev, core, s_power, 0);
+ if (fh->radio)
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
+
+ /* free stuff */
+ videobuf_mmap_free(&fh->cap);
+ videobuf_mmap_free(&fh->vbi);
+ saa7134_pgtable_free(dev->pci,&fh->pt_cap);
+ saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
+
+ v4l2_prio_close(&dev->prio, fh->prio);
+ file->private_data = NULL;
+ kfree(fh);
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct saa7134_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+}
+
+static ssize_t radio_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+
+ cmd.block_count = count/3;
+ cmd.buffer = data;
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
+
+ return cmd.result;
+}
+
+static unsigned int radio_poll(struct file *file, poll_table *wait)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa6588_command cmd;
+
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+ saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
+
+ return cmd.result;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_tvnorm *norm = dev->tvnorm;
+
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 64 * 4;
+ f->fmt.vbi.start[0] = norm->vbi_v_start_0;
+ f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
+ f->fmt.vbi.start[1] = norm->vbi_v_start_1;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+ f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
+
+ return 0;
+}
+
+static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->cap.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ f->fmt.win = fh->win;
+
+ return 0;
+}
+
+static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
+ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ return verify_preview(dev, &f->fmt.win);
+}
+
+static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ int err;
+
+ err = saa7134_try_fmt_vid_cap(file, priv, f);
+ if (0 != err)
+ return err;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->cap.field = f->fmt.pix.field;
+ return 0;
+}
+
+static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+ unsigned long flags;
+
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+ err = verify_preview(dev, &f->fmt.win);
+ if (0 != err)
+ return err;
+
+ mutex_lock(&dev->lock);
+
+ fh->win = f->fmt.win;
+ fh->nclips = f->fmt.win.clipcount;
+
+ if (fh->nclips > 8)
+ fh->nclips = 8;
+
+ if (copy_from_user(fh->clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip)*fh->nclips)) {
+ mutex_unlock(&dev->lock);
+ return -EFAULT;
+ }
+
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ }
+
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if ((c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1) &&
+ (c->id < V4L2_CID_PRIVATE_BASE ||
+ c->id >= V4L2_CID_PRIVATE_LASTP1))
+ return -EINVAL;
+ ctrl = ctrl_by_id(c->id);
+ *c = (NULL != ctrl) ? *ctrl : no_ctrl;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_queryctrl);
+
+static int saa7134_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i->index).name)
+ return -EINVAL;
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, card_in(dev, n).name);
+ if (card_in(dev, n).tv)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->audioset = 1;
+ if (n == dev->ctl_input) {
+ int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
+
+ if (0 != (v1 & 0x40))
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ if (0 != (v2 & 0x40))
+ i->status |= V4L2_IN_ST_NO_SYNC;
+ if (0 != (v2 & 0x0e))
+ i->status |= V4L2_IN_ST_MACROVISION;
+ }
+ i->std = SAA7134_NORMS;
+ return 0;
+}
+
+static int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ if (i >= SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL == card_in(dev, i).name)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int saa7134_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ unsigned int tuner_type = dev->tuner_type;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ if (dev->has_rds)
+ cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
+ if (saa7134_no_overlay <= 0)
+ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
+
+ if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
+ cap->capabilities &= ~V4L2_CAP_TUNER;
+ return 0;
+}
+
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
+{
+ unsigned long flags;
+ unsigned int i;
+ v4l2_std_id fixup;
+ int err;
+
+ /* When called from the empress code fh == NULL.
+ That needs to be fixed somehow, but for now this is
+ good enough. */
+ if (fh) {
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+ } else if (res_locked(dev, RESOURCE_OVERLAY)) {
+ /* Don't change the std from the mpeg device
+ if overlay is active. */
+ return -EBUSY;
+ }
+
+ for (i = 0; i < TVNORMS; i++)
+ if (*id == tvnorms[i].id)
+ break;
+
+ if (i == TVNORMS)
+ for (i = 0; i < TVNORMS; i++)
+ if (*id & tvnorms[i].id)
+ break;
+ if (i == TVNORMS)
+ return -EINVAL;
+
+ if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
+ if (secam[0] == 'L' || secam[0] == 'l') {
+ if (secam[1] == 'C' || secam[1] == 'c')
+ fixup = V4L2_STD_SECAM_LC;
+ else
+ fixup = V4L2_STD_SECAM_L;
+ } else {
+ if (secam[0] == 'D' || secam[0] == 'd')
+ fixup = V4L2_STD_SECAM_DK;
+ else
+ fixup = V4L2_STD_SECAM;
+ }
+ for (i = 0; i < TVNORMS; i++) {
+ if (fixup == tvnorms[i].id)
+ break;
+ }
+ if (i == TVNORMS)
+ return -EINVAL;
+ }
+
+ *id = tvnorms[i].id;
+
+ mutex_lock(&dev->lock);
+ if (fh && res_check(fh, RESOURCE_OVERLAY)) {
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ set_tvnorm(dev, &tvnorms[i]);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else
+ set_tvnorm(dev, &tvnorms[i]);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
+
+static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+
+ return saa7134_s_std_internal(fh->dev, fh, id);
+}
+
+static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ *id = dev->tvnorm->id;
+ return 0;
+}
+
+static int saa7134_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cap)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ cap->bounds = dev->crop_bounds;
+ cap->defrect = dev->crop_defrect;
+ cap->pixelaspect.numerator = 1;
+ cap->pixelaspect.denominator = 1;
+ if (dev->tvnorm->id & V4L2_STD_525_60) {
+ cap->pixelaspect.numerator = 11;
+ cap->pixelaspect.denominator = 10;
+ }
+ if (dev->tvnorm->id & V4L2_STD_625_50) {
+ cap->pixelaspect.numerator = 54;
+ cap->pixelaspect.denominator = 59;
+ }
+ return 0;
+}
+
+static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ crop->c = dev->crop_current;
+ return 0;
+}
+
+static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct v4l2_rect *b = &dev->crop_bounds;
+ struct v4l2_rect *c = &dev->crop_current;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+ if (crop->c.height < 0)
+ return -EINVAL;
+ if (crop->c.width < 0)
+ return -EINVAL;
+
+ if (res_locked(fh->dev, RESOURCE_OVERLAY))
+ return -EBUSY;
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+
+ *c = crop->c;
+ if (c->top < b->top)
+ c->top = b->top;
+ if (c->top > b->top + b->height)
+ c->top = b->top + b->height;
+ if (c->height > b->top - c->top + b->height)
+ c->height = b->top - c->top + b->height;
+
+ if (c->left < b->left)
+ c->left = b->left;
+ if (c->left > b->left + b->width)
+ c->left = b->left + b->width;
+ if (c->width > b->left - c->left + b->width)
+ c->width = b->left - c->left + b->width;
+ return 0;
+}
+
+static int saa7134_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int n;
+
+ if (0 != t->index)
+ return -EINVAL;
+ memset(t, 0, sizeof(*t));
+ for (n = 0; n < SAA7134_INPUT_MAX; n++) {
+ if (card_in(dev, n).tv)
+ break;
+ }
+ if (n == SAA7134_INPUT_MAX)
+ return -EINVAL;
+ if (NULL != card_in(dev, n).name) {
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = saa7134_tvaudio_getstereo(dev);
+ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ }
+ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
+ t->signal = 0xffff;
+ return 0;
+}
+
+static int saa7134_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int rx, mode, err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ mode = dev->thread.mode;
+ if (UNSET == mode) {
+ rx = saa7134_tvaudio_getstereo(dev);
+ mode = saa7134_tvaudio_rx2mode(rx);
+ }
+ if (mode != t->audmode)
+ dev->thread.mode = t->audmode;
+
+ return 0;
+}
+
+static int saa7134_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ return 0;
+}
+
+static int saa7134_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int err;
+
+ err = v4l2_prio_check(&dev->prio, fh->prio);
+ if (0 != err)
+ return err;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
+ return -EINVAL;
+ mutex_lock(&dev->lock);
+ dev->ctl_freq = f->frequency;
+
+ saa_call_all(dev, tuner, s_frequency, f);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ strcpy(a->name, "audio");
+ return 0;
+}
+
+static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *p = v4l2_prio_max(&dev->prio);
+ return 0;
+}
+
+static int saa7134_s_priority(struct file *file, void *f,
+ enum v4l2_priority prio)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ return v4l2_prio_change(&dev->prio, &fh->prio, prio);
+}
+
+static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (saa7134_no_overlay > 0) {
+ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
+ return -EINVAL;
+ }
+
+ if ((f->index >= FORMATS) || formats[f->index].planar)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+static int saa7134_g_fbuf(struct file *file, void *f,
+ struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int saa7134_s_fbuf(struct file *file, void *f,
+ const struct v4l2_framebuffer *fb)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ struct saa7134_format *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) &&
+ !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (0 == dev->ovbuf.fmt.bytesperline)
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width*fmt->depth/8;
+ return 0;
+}
+
+static int saa7134_overlay(struct file *file, void *f, unsigned int on)
+{
+ struct saa7134_fh *fh = f;
+ struct saa7134_dev *dev = fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ if (saa7134_no_overlay > 0) {
+ dprintk("no_overlay\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(dev, fh, RESOURCE_OVERLAY))
+ return -EBUSY;
+ spin_lock_irqsave(&dev->slock, flags);
+ start_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ }
+ if (!on) {
+ if (!res_check(fh, RESOURCE_OVERLAY))
+ return -EINVAL;
+ spin_lock_irqsave(&dev->slock, flags);
+ stop_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+ return 0;
+}
+
+static int saa7134_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_reqbufs(saa7134_queue(fh), p);
+}
+
+static int saa7134_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_querybuf(saa7134_queue(fh), b);
+}
+
+static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_qbuf(saa7134_queue(fh), b);
+}
+
+static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct saa7134_fh *fh = priv;
+ return videobuf_dqbuf(saa7134_queue(fh), b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int saa7134_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ if (!res_get(dev, fh, res))
+ return -EBUSY;
+
+ return videobuf_streamon(saa7134_queue(fh));
+}
+
+static int saa7134_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ int err;
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+ int res = saa7134_resource(fh);
+
+ err = videobuf_streamoff(saa7134_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int saa7134_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *parm)
+{
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register (struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ reg->val = saa_readb(reg->reg);
+ reg->size = 1;
+ return 0;
+}
+
+static int vidioc_s_register (struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct saa7134_fh *fh = priv;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ saa_writeb(reg->reg&0xffffff, reg->val);
+ return 0;
+}
+#endif
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ strcpy(cap->driver, "saa7134");
+ strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ saa_call_all(dev, tuner, g_tuner, t);
+ if (dev->input->amux == TV) {
+ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
+ t->rxsubchans = (saa_readb(0x529) & 0x08) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ }
+ return 0;
+}
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ saa_call_all(dev, tuner, s_tuner, t);
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ const struct v4l2_queryctrl *ctrl;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl = ctrl_by_id(c->id);
+ *c = *ctrl;
+ } else
+ *c = no_ctrl;
+ return 0;
+}
+
+static const struct v4l2_file_operations video_fops =
+{
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = saa7134_querycap,
+ .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
+ .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_g_audio = saa7134_g_audio,
+ .vidioc_s_audio = saa7134_s_audio,
+ .vidioc_cropcap = saa7134_cropcap,
+ .vidioc_reqbufs = saa7134_reqbufs,
+ .vidioc_querybuf = saa7134_querybuf,
+ .vidioc_qbuf = saa7134_qbuf,
+ .vidioc_dqbuf = saa7134_dqbuf,
+ .vidioc_s_std = saa7134_s_std,
+ .vidioc_g_std = saa7134_g_std,
+ .vidioc_enum_input = saa7134_enum_input,
+ .vidioc_g_input = saa7134_g_input,
+ .vidioc_s_input = saa7134_s_input,
+ .vidioc_queryctrl = saa7134_queryctrl,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_streamon = saa7134_streamon,
+ .vidioc_streamoff = saa7134_streamoff,
+ .vidioc_g_tuner = saa7134_g_tuner,
+ .vidioc_s_tuner = saa7134_s_tuner,
+ .vidioc_g_crop = saa7134_g_crop,
+ .vidioc_s_crop = saa7134_s_crop,
+ .vidioc_g_fbuf = saa7134_g_fbuf,
+ .vidioc_s_fbuf = saa7134_s_fbuf,
+ .vidioc_overlay = saa7134_overlay,
+ .vidioc_g_priority = saa7134_g_priority,
+ .vidioc_s_priority = saa7134_s_priority,
+ .vidioc_g_parm = saa7134_g_parm,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .read = radio_read,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+ .poll = radio_poll,
+};
+
+static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_s_std = radio_s_std,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_input = radio_g_input,
+ .vidioc_g_ctrl = saa7134_g_ctrl,
+ .vidioc_s_ctrl = saa7134_s_ctrl,
+ .vidioc_g_frequency = saa7134_g_frequency,
+ .vidioc_s_frequency = saa7134_s_frequency,
+};
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+struct video_device saa7134_video_template = {
+ .name = "saa7134-video",
+ .fops = &video_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = SAA7134_NORMS,
+ .current_norm = V4L2_STD_PAL,
+};
+
+struct video_device saa7134_radio_template = {
+ .name = "saa7134-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+int saa7134_video_init1(struct saa7134_dev *dev)
+{
+ /* sanitycheck insmod options */
+ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
+ gbuffers = 2;
+ if (gbufsize < 0 || gbufsize > gbufsize_max)
+ gbufsize = gbufsize_max;
+ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
+
+ /* put some sensible defaults into the data structures ... */
+ dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
+ dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
+ dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value;
+ dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
+ dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
+ dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
+ dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
+ dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
+
+ if (dev->tda9887_conf && dev->ctl_automute)
+ dev->tda9887_conf |= TDA9887_AUTOMUTE;
+ dev->automute = 0;
+
+ INIT_LIST_HEAD(&dev->video_q.queue);
+ init_timer(&dev->video_q.timeout);
+ dev->video_q.timeout.function = saa7134_buffer_timeout;
+ dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
+ dev->video_q.dev = dev;
+
+ if (saa7134_boards[dev->board].video_out)
+ saa7134_videoport_init(dev);
+
+ return 0;
+}
+
+int saa7134_videoport_init(struct saa7134_dev *dev)
+{
+ /* enable video output */
+ int vo = saa7134_boards[dev->board].video_out;
+ int video_reg;
+ unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+
+ /* Configure videoport */
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
+ video_reg = video_out[vo][1];
+ if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
+ video_reg &= ~VP_T_CODE_P_INVERTED;
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
+ video_reg = video_out[vo][5];
+ if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
+ video_reg &= ~VP_CLK_CTRL2_DELAYED;
+ if (vid_port_opts & SET_CLOCK_INVERTED)
+ video_reg |= VP_CLK_CTRL1_INVERTED;
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
+ video_reg = video_out[vo][6];
+ if (vid_port_opts & SET_VSYNC_OFF) {
+ video_reg &= ~VP_VS_TYPE_MASK;
+ video_reg |= VP_VS_TYPE_OFF;
+ }
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+
+ /* Start videoport */
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+
+ return 0;
+}
+
+int saa7134_video_init2(struct saa7134_dev *dev)
+{
+ /* init video hw */
+ set_tvnorm(dev,&tvnorms[0]);
+ video_mux(dev,0);
+ saa7134_tvaudio_setmute(dev);
+ saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+ return 0;
+}
+
+void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
+{
+ static const char *st[] = {
+ "(no signal)", "NTSC", "PAL", "SECAM" };
+ u32 st1,st2;
+
+ st1 = saa_readb(SAA7134_STATUS_VIDEO1);
+ st2 = saa_readb(SAA7134_STATUS_VIDEO2);
+ dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n",
+ (st1 & 0x40) ? "not locked" : "locked",
+ (st2 & 0x40) ? "no" : "yes",
+ st[st1 & 0x03]);
+ dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1);
+
+ if (dev->nosignal) {
+ /* no video signal -> mute audio */
+ if (dev->ctl_automute)
+ dev->automute = 1;
+ saa7134_tvaudio_setmute(dev);
+ } else {
+ /* wake up tvaudio audio carrier scan thread */
+ saa7134_tvaudio_do_scan(dev);
+ }
+
+ if ((st2 & 0x80) && !noninterlaced && !dev->nosignal)
+ saa_clearb(SAA7134_SYNC_CTRL, 0x20);
+ else
+ saa_setb(SAA7134_SYNC_CTRL, 0x20);
+
+ if (dev->mops && dev->mops->signal_change)
+ dev->mops->signal_change(dev);
+}
+
+
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
+{
+ enum v4l2_field field;
+
+ spin_lock(&dev->slock);
+ if (dev->video_q.curr) {
+ dev->video_fieldcount++;
+ field = dev->video_q.curr->vb.field;
+ if (V4L2_FIELD_HAS_BOTH(field)) {
+ /* make sure we have seen both fields */
+ if ((status & 0x10) == 0x00) {
+ dev->video_q.curr->top_seen = 1;
+ goto done;
+ }
+ if (!dev->video_q.curr->top_seen)
+ goto done;
+ } else if (field == V4L2_FIELD_TOP) {
+ if ((status & 0x10) != 0x10)
+ goto done;
+ } else if (field == V4L2_FIELD_BOTTOM) {
+ if ((status & 0x10) != 0x00)
+ goto done;
+ }
+ dev->video_q.curr->vb.field_count = dev->video_fieldcount;
+ saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
+ }
+ saa7134_buffer_next(dev,&dev->video_q);
+
+ done:
+ spin_unlock(&dev->slock);
+}
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
new file mode 100644
index 000000000000..c24b6512bd8f
--- /dev/null
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -0,0 +1,855 @@
+/*
+ *
+ * v4l2 device driver for philips saa7134 based TV cards
+ *
+ * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define SAA7134_VERSION "0, 2, 17"
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/kdev_t.h>
+#include <linux/input.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+#include <asm/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/tuner.h>
+#include <media/rc-core.h>
+#include <media/ir-kbd-i2c.h>
+#include <media/videobuf-dma-sg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+#include <media/videobuf-dvb.h>
+#endif
+
+#define UNSET (-1U)
+
+/* ----------------------------------------------------------- */
+/* enums */
+
+enum saa7134_tvaudio_mode {
+ TVAUDIO_FM_MONO = 1,
+ TVAUDIO_FM_BG_STEREO = 2,
+ TVAUDIO_FM_SAT_STEREO = 3,
+ TVAUDIO_FM_K_STEREO = 4,
+ TVAUDIO_NICAM_AM = 5,
+ TVAUDIO_NICAM_FM = 6,
+};
+
+enum saa7134_audio_in {
+ TV = 1,
+ LINE1 = 2,
+ LINE2 = 3,
+ LINE2_LEFT,
+};
+
+enum saa7134_video_out {
+ CCIR656 = 1,
+};
+
+/* ----------------------------------------------------------- */
+/* static data */
+
+struct saa7134_tvnorm {
+ char *name;
+ v4l2_std_id id;
+
+ /* video decoder */
+ unsigned int sync_control;
+ unsigned int luma_control;
+ unsigned int chroma_ctrl1;
+ unsigned int chroma_gain;
+ unsigned int chroma_ctrl2;
+ unsigned int vgate_misc;
+
+ /* video scaler */
+ unsigned int h_start;
+ unsigned int h_stop;
+ unsigned int video_v_start;
+ unsigned int video_v_stop;
+ unsigned int vbi_v_start_0;
+ unsigned int vbi_v_stop_0;
+ unsigned int src_timing;
+ unsigned int vbi_v_start_1;
+};
+
+struct saa7134_tvaudio {
+ char *name;
+ v4l2_std_id std;
+ enum saa7134_tvaudio_mode mode;
+ int carr1;
+ int carr2;
+};
+
+struct saa7134_format {
+ char *name;
+ unsigned int fourcc;
+ unsigned int depth;
+ unsigned int pm;
+ unsigned int vshift; /* vertical downsampling (for planar yuv) */
+ unsigned int hshift; /* horizontal downsampling (for planar yuv) */
+ unsigned int bswap:1;
+ unsigned int wswap:1;
+ unsigned int yuv:1;
+ unsigned int planar:1;
+ unsigned int uvswap:1;
+};
+
+struct saa7134_card_ir {
+ struct rc_dev *dev;
+
+ char name[32];
+ char phys[32];
+ unsigned users;
+
+ u32 polling;
+ u32 last_gpio;
+ u32 mask_keycode, mask_keydown, mask_keyup;
+
+ bool running;
+
+ struct timer_list timer;
+
+ /* IR core raw decoding */
+ u32 raw_decode;
+};
+
+/* ----------------------------------------------------------- */
+/* card configuration */
+
+#define SAA7134_BOARD_NOAUTO UNSET
+#define SAA7134_BOARD_UNKNOWN 0
+#define SAA7134_BOARD_PROTEUS_PRO 1
+#define SAA7134_BOARD_FLYVIDEO3000 2
+#define SAA7134_BOARD_FLYVIDEO2000 3
+#define SAA7134_BOARD_EMPRESS 4
+#define SAA7134_BOARD_MONSTERTV 5
+#define SAA7134_BOARD_MD9717 6
+#define SAA7134_BOARD_TVSTATION_RDS 7
+#define SAA7134_BOARD_CINERGY400 8
+#define SAA7134_BOARD_MD5044 9
+#define SAA7134_BOARD_KWORLD 10
+#define SAA7134_BOARD_CINERGY600 11
+#define SAA7134_BOARD_MD7134 12
+#define SAA7134_BOARD_TYPHOON_90031 13
+#define SAA7134_BOARD_ELSA 14
+#define SAA7134_BOARD_ELSA_500TV 15
+#define SAA7134_BOARD_ASUSTeK_TVFM7134 16
+#define SAA7134_BOARD_VA1000POWER 17
+#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18
+#define SAA7134_BOARD_VIDEOMATE_TV 19
+#define SAA7134_BOARD_CRONOS_PLUS 20
+#define SAA7134_BOARD_10MOONSTVMASTER 21
+#define SAA7134_BOARD_MD2819 22
+#define SAA7134_BOARD_BMK_MPEX_TUNER 23
+#define SAA7134_BOARD_TVSTATION_DVR 24
+#define SAA7134_BOARD_ASUSTEK_TVFM7133 25
+#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26
+#define SAA7134_BOARD_MANLI_MTV002 27
+#define SAA7134_BOARD_MANLI_MTV001 28
+#define SAA7134_BOARD_TG3000TV 29
+#define SAA7134_BOARD_ECS_TVP3XP 30
+#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31
+#define SAA7134_BOARD_AVACSSMARTTV 32
+#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
+#define SAA7134_BOARD_NOVAC_PRIMETV7133 34
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35
+#define SAA7134_BOARD_UPMOST_PURPLE_TV 36
+#define SAA7134_BOARD_ITEMS_MTV005 37
+#define SAA7134_BOARD_CINERGY200 38
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39
+#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41
+#define SAA7134_BOARD_SABRENT_SBTTVFM 42
+#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43
+#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46
+#define SAA7134_BOARD_CINERGY400_CARDBUS 47
+#define SAA7134_BOARD_CINERGY600_MK3 48
+#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49
+#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50
+#define SAA7134_BOARD_PROVIDEO_PV952 51
+#define SAA7134_BOARD_AVERMEDIA_305 52
+#define SAA7134_BOARD_ASUSTeK_TVFM7135 53
+#define SAA7134_BOARD_FLYTVPLATINUM_FM 54
+#define SAA7134_BOARD_FLYDVBTDUO 55
+#define SAA7134_BOARD_AVERMEDIA_307 56
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57
+#define SAA7134_BOARD_ADS_INSTANT_TV 58
+#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59
+#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60
+#define SAA7134_BOARD_PHILIPS_TOUGH 61
+#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62
+#define SAA7134_BOARD_KWORLD_XPERT 63
+#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64
+#define SAA7134_BOARD_KWORLD_TERMINATOR 65
+#define SAA7134_BOARD_YUAN_TUN900 66
+#define SAA7134_BOARD_BEHOLD_409FM 67
+#define SAA7134_BOARD_GOTVIEW_7135 68
+#define SAA7134_BOARD_PHILIPS_EUROPA 69
+#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71
+#define SAA7134_BOARD_RTD_VFG7350 72
+#define SAA7134_BOARD_RTD_VFG7330 73
+#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74
+#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75
+#define SAA7134_BOARD_MONSTERTV_MOBILE 76
+#define SAA7134_BOARD_PINNACLE_PCTV_110i 77
+#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78
+#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79
+#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80
+#define SAA7134_BOARD_PHILIPS_TIGER 81
+#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82
+#define SAA7134_BOARD_CINERGY250PCI 83
+#define SAA7134_BOARD_FLYDVB_TRIO 84
+#define SAA7134_BOARD_AVERMEDIA_777 85
+#define SAA7134_BOARD_FLYDVBT_LR301 86
+#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87
+#define SAA7134_BOARD_TEVION_DVBT_220RF 88
+#define SAA7134_BOARD_ELSA_700TV 89
+#define SAA7134_BOARD_KWORLD_ATSC110 90
+#define SAA7134_BOARD_AVERMEDIA_A169_B 91
+#define SAA7134_BOARD_AVERMEDIA_A169_B1 92
+#define SAA7134_BOARD_MD7134_BRIDGE_2 93
+#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
+#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95
+#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96
+#define SAA7134_BOARD_FLYDVBS_LR300 97
+#define SAA7134_BOARD_PROTEUS_2309 98
+#define SAA7134_BOARD_AVERMEDIA_A16AR 99
+#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
+#define SAA7134_BOARD_PINNACLE_PCTV_310i 101
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
+#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103
+#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104
+#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105
+#define SAA7134_BOARD_ENCORE_ENLTV 106
+#define SAA7134_BOARD_ENCORE_ENLTV_FM 107
+#define SAA7134_BOARD_CINERGY_HT_PCI 108
+#define SAA7134_BOARD_PHILIPS_TIGER_S 109
+#define SAA7134_BOARD_AVERMEDIA_M102 110
+#define SAA7134_BOARD_ASUS_P7131_4871 111
+#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
+#define SAA7134_BOARD_ECS_TVP3XP_4CB6 113
+#define SAA7134_BOARD_KWORLD_DVBT_210 114
+#define SAA7134_BOARD_SABRENT_TV_PCB05 115
+#define SAA7134_BOARD_10MOONSTVMASTER3 116
+#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117
+#define SAA7134_BOARD_BEHOLD_401 118
+#define SAA7134_BOARD_BEHOLD_403 119
+#define SAA7134_BOARD_BEHOLD_403FM 120
+#define SAA7134_BOARD_BEHOLD_405 121
+#define SAA7134_BOARD_BEHOLD_405FM 122
+#define SAA7134_BOARD_BEHOLD_407 123
+#define SAA7134_BOARD_BEHOLD_407FM 124
+#define SAA7134_BOARD_BEHOLD_409 125
+#define SAA7134_BOARD_BEHOLD_505FM 126
+#define SAA7134_BOARD_BEHOLD_507_9FM 127
+#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128
+#define SAA7134_BOARD_BEHOLD_607FM_MK3 129
+#define SAA7134_BOARD_BEHOLD_M6 130
+#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131
+#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132
+#define SAA7134_BOARD_PHILIPS_SNAKE 133
+#define SAA7134_BOARD_CREATIX_CTX953 134
+#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136
+#define SAA7134_BOARD_AVERMEDIA_A16D 137
+#define SAA7134_BOARD_AVERMEDIA_M115 138
+#define SAA7134_BOARD_VIDEOMATE_T750 139
+#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140
+#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
+#define SAA7134_BOARD_BEHOLD_H6 142
+#define SAA7134_BOARD_BEHOLD_M63 143
+#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
+#define SAA7134_BOARD_AVERMEDIA_M103 145
+#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
+#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147
+#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
+#define SAA7134_BOARD_AVERMEDIA_M135A 149
+#define SAA7134_BOARD_REAL_ANGEL_220 150
+#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
+#define SAA7134_BOARD_ASUSTeK_TIGER 152
+#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
+#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
+#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155
+#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
+#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
+#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159
+#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160
+#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161
+#define SAA7134_BOARD_BEHOLD_607FM_MK5 162
+#define SAA7134_BOARD_BEHOLD_609FM_MK3 163
+#define SAA7134_BOARD_BEHOLD_609FM_MK5 164
+#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165
+#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166
+#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167
+#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168
+#define SAA7134_BOARD_VIDEOMATE_S350 169
+#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170
+#define SAA7134_BOARD_BEHOLD_X7 171
+#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
+#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173
+#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174
+#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
+#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176
+#define SAA7134_BOARD_HAWELL_HW_404M7 177
+#define SAA7134_BOARD_BEHOLD_H7 178
+#define SAA7134_BOARD_BEHOLD_A7 179
+#define SAA7134_BOARD_AVERMEDIA_M733A 180
+#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181
+#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182
+#define SAA7134_BOARD_VIDEOMATE_M1F 183
+#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184
+#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185
+#define SAA7134_BOARD_BEHOLD_501 186
+#define SAA7134_BOARD_BEHOLD_503FM 187
+#define SAA7134_BOARD_SENSORAY811_911 188
+#define SAA7134_BOARD_KWORLD_PC150U 189
+#define SAA7134_BOARD_ASUSTeK_PS3_100 190
+
+#define SAA7134_MAXBOARDS 32
+#define SAA7134_INPUT_MAX 8
+
+/* ----------------------------------------------------------- */
+/* Since we support 2 remote types, lets tell them apart */
+
+#define SAA7134_REMOTE_GPIO 1
+#define SAA7134_REMOTE_I2C 2
+
+/* ----------------------------------------------------------- */
+/* Video Output Port Register Initialization Options */
+
+#define SET_T_CODE_POLARITY_NON_INVERTED (1 << 0)
+#define SET_CLOCK_NOT_DELAYED (1 << 1)
+#define SET_CLOCK_INVERTED (1 << 2)
+#define SET_VSYNC_OFF (1 << 3)
+
+struct saa7134_input {
+ char *name;
+ unsigned int vmux;
+ enum saa7134_audio_in amux;
+ unsigned int gpio;
+ unsigned int tv:1;
+};
+
+enum saa7134_mpeg_type {
+ SAA7134_MPEG_UNUSED,
+ SAA7134_MPEG_EMPRESS,
+ SAA7134_MPEG_DVB,
+};
+
+enum saa7134_mpeg_ts_type {
+ SAA7134_MPEG_TS_PARALLEL = 0,
+ SAA7134_MPEG_TS_SERIAL,
+};
+
+struct saa7134_board {
+ char *name;
+ unsigned int audio_clock;
+
+ /* input switching */
+ unsigned int gpiomask;
+ struct saa7134_input inputs[SAA7134_INPUT_MAX];
+ struct saa7134_input radio;
+ struct saa7134_input mute;
+
+ /* i2c chip info */
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+ unsigned char empress_addr;
+ unsigned char rds_addr;
+
+ unsigned int tda9887_conf;
+ unsigned int tuner_config;
+
+ /* peripheral I/O */
+ enum saa7134_video_out video_out;
+ enum saa7134_mpeg_type mpeg;
+ enum saa7134_mpeg_ts_type ts_type;
+ unsigned int vid_port_opts;
+ unsigned int ts_force_val:1;
+};
+
+#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name)
+#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
+#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg)
+#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg)
+#define card(dev) (saa7134_boards[dev->board])
+#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n])
+
+/* ----------------------------------------------------------- */
+/* device / file handle status */
+
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
+
+#define INTERLACE_AUTO 0
+#define INTERLACE_ON 1
+#define INTERLACE_OFF 2
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */
+
+struct saa7134_dev;
+struct saa7134_dma;
+
+/* saa7134 page table */
+struct saa7134_pgtable {
+ unsigned int size;
+ __le32 *cpu;
+ dma_addr_t dma;
+};
+
+/* tvaudio thread status */
+struct saa7134_thread {
+ struct task_struct *thread;
+ unsigned int scan1;
+ unsigned int scan2;
+ unsigned int mode;
+ unsigned int stopped;
+};
+
+/* buffer for one video/vbi/ts frame */
+struct saa7134_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ /* saa7134 specific */
+ struct saa7134_format *fmt;
+ unsigned int top_seen;
+ int (*activate)(struct saa7134_dev *dev,
+ struct saa7134_buf *buf,
+ struct saa7134_buf *next);
+
+ /* page tables */
+ struct saa7134_pgtable *pt;
+};
+
+struct saa7134_dmaqueue {
+ struct saa7134_dev *dev;
+ struct saa7134_buf *curr;
+ struct list_head queue;
+ struct timer_list timeout;
+ unsigned int need_two;
+};
+
+/* video filehandle status */
+struct saa7134_fh {
+ struct saa7134_dev *dev;
+ unsigned int radio;
+ enum v4l2_buf_type type;
+ unsigned int resources;
+ enum v4l2_priority prio;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip clips[8];
+ unsigned int nclips;
+
+ /* video capture */
+ struct saa7134_format *fmt;
+ unsigned int width,height;
+ struct videobuf_queue cap;
+ struct saa7134_pgtable pt_cap;
+
+ /* vbi capture */
+ struct videobuf_queue vbi;
+ struct saa7134_pgtable pt_vbi;
+};
+
+/* dmasound dsp status */
+struct saa7134_dmasound {
+ struct mutex lock;
+ int minor_mixer;
+ int minor_dsp;
+ unsigned int users_dsp;
+
+ /* mixer */
+ enum saa7134_audio_in input;
+ unsigned int count;
+ unsigned int line1;
+ unsigned int line2;
+
+ /* dsp */
+ unsigned int afmt;
+ unsigned int rate;
+ unsigned int channels;
+ unsigned int recording_on;
+ unsigned int dma_running;
+ unsigned int blocks;
+ unsigned int blksize;
+ unsigned int bufsize;
+ struct saa7134_pgtable pt;
+ struct videobuf_dmabuf dma;
+ unsigned int dma_blk;
+ unsigned int read_offset;
+ unsigned int read_count;
+ void * priv_data;
+ struct snd_pcm_substream *substream;
+};
+
+/* ts/mpeg status */
+struct saa7134_ts {
+ /* TS capture */
+ struct saa7134_pgtable pt_ts;
+ int nr_packets;
+ int nr_bufs;
+};
+
+/* ts/mpeg ops */
+struct saa7134_mpeg_ops {
+ enum saa7134_mpeg_type type;
+ struct list_head next;
+ int (*init)(struct saa7134_dev *dev);
+ int (*fini)(struct saa7134_dev *dev);
+ void (*signal_change)(struct saa7134_dev *dev);
+};
+
+/* global device status */
+struct saa7134_dev {
+ struct list_head devlist;
+ struct mutex lock;
+ spinlock_t slock;
+ struct v4l2_prio_state prio;
+ struct v4l2_device v4l2_dev;
+ /* workstruct for loading modules */
+ struct work_struct request_module_wk;
+
+ /* insmod option/autodetected */
+ int autodetected;
+
+ /* various device info */
+ unsigned int resources;
+ struct video_device *video_dev;
+ struct video_device *radio_dev;
+ struct video_device *vbi_dev;
+ struct saa7134_dmasound dmasound;
+
+ /* infrared remote */
+ int has_remote;
+ struct saa7134_card_ir *remote;
+
+ /* pci i/o */
+ char name[32];
+ int nr;
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+ __u32 __iomem *lmmio;
+ __u8 __iomem *bmmio;
+
+ /* config info */
+ unsigned int board;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ unsigned int tda9887_conf;
+ unsigned int gpio_value;
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ unsigned char eedata[256];
+ int has_rds;
+
+ /* video overlay */
+ struct v4l2_framebuffer ovbuf;
+ struct saa7134_format *ovfmt;
+ unsigned int ovenable;
+ enum v4l2_field ovfield;
+
+ /* video+ts+vbi capture */
+ struct saa7134_dmaqueue video_q;
+ struct saa7134_dmaqueue vbi_q;
+ unsigned int video_fieldcount;
+ unsigned int vbi_fieldcount;
+
+ /* various v4l controls */
+ struct saa7134_tvnorm *tvnorm; /* video */
+ struct saa7134_tvaudio *tvaudio;
+ unsigned int ctl_input;
+ int ctl_bright;
+ int ctl_contrast;
+ int ctl_hue;
+ int ctl_saturation;
+ int ctl_freq;
+ int ctl_mute; /* audio */
+ int ctl_volume;
+ int ctl_invert; /* private */
+ int ctl_mirror;
+ int ctl_y_odd;
+ int ctl_y_even;
+ int ctl_automute;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+ /* other global state info */
+ unsigned int automute;
+ struct saa7134_thread thread;
+ struct saa7134_input *input;
+ struct saa7134_input *hw_input;
+ unsigned int hw_mute;
+ int last_carrier;
+ int nosignal;
+ unsigned int insuspend;
+
+ /* I2C keyboard data */
+ struct IR_i2c_init_data init_data;
+
+ /* SAA7134_MPEG_* */
+ struct saa7134_ts ts;
+ struct saa7134_dmaqueue ts_q;
+ int ts_started;
+ struct saa7134_mpeg_ops *mops;
+
+ /* SAA7134_MPEG_EMPRESS only */
+ struct video_device *empress_dev;
+ struct videobuf_queue empress_tsq;
+ atomic_t empress_users;
+ struct work_struct empress_workqueue;
+ int empress_started;
+
+#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
+ /* SAA7134_MPEG_DVB only */
+ struct videobuf_dvb_frontends frontends;
+ int (*original_demod_sleep)(struct dvb_frontend *fe);
+ int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+ int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
+#endif
+ void (*gate_ctrl)(struct saa7134_dev *dev, int open);
+};
+
+/* ----------------------------------------------------------- */
+
+#define saa_readl(reg) readl(dev->lmmio + (reg))
+#define saa_writel(reg,value) writel((value), dev->lmmio + (reg));
+#define saa_andorl(reg,mask,value) \
+ writel((readl(dev->lmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->lmmio+(reg))
+#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit))
+#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0)
+
+#define saa_readb(reg) readb(dev->bmmio + (reg))
+#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg));
+#define saa_andorb(reg,mask,value) \
+ writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\
+ ((value) & (mask)), dev->bmmio+(reg))
+#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit))
+#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0)
+
+#define saa_wait(us) { udelay(us); }
+
+#define SAA7134_NORMS (\
+ V4L2_STD_PAL | V4L2_STD_PAL_N | \
+ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+
+#define GRP_EMPRESS (1)
+#define saa_call_all(dev, o, f, args...) do { \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 1); \
+ v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 0); \
+} while (0)
+
+#define saa_call_empress(dev, o, f, args...) ({ \
+ long _rc; \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 1); \
+ _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \
+ GRP_EMPRESS, o, f , ##args); \
+ if (dev->gate_ctrl) \
+ dev->gate_ctrl(dev, 0); \
+ _rc; \
+})
+
+/* ----------------------------------------------------------- */
+/* saa7134-core.c */
+
+extern struct list_head saa7134_devlist;
+extern struct mutex saa7134_devlist_lock;
+extern int saa7134_no_overlay;
+
+void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
+
+#define SAA7134_PGTABLE_SIZE 4096
+
+int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt);
+int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
+ struct scatterlist *list, unsigned int length,
+ unsigned int startpage);
+void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt);
+
+int saa7134_buffer_count(unsigned int size, unsigned int count);
+int saa7134_buffer_startpage(struct saa7134_buf *buf);
+unsigned long saa7134_buffer_base(struct saa7134_buf *buf);
+
+int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ struct saa7134_buf *buf);
+void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
+ unsigned int state);
+void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
+void saa7134_buffer_timeout(unsigned long data);
+void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
+
+int saa7134_set_dmabits(struct saa7134_dev *dev);
+
+extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
+extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-cards.c */
+
+extern struct saa7134_board saa7134_boards[];
+extern const unsigned int saa7134_bcount;
+extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
+
+extern int saa7134_board_init1(struct saa7134_dev *dev);
+extern int saa7134_board_init2(struct saa7134_dev *dev);
+int saa7134_tuner_callback(void *priv, int component, int command, int arg);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-i2c.c */
+
+int saa7134_i2c_register(struct saa7134_dev *dev);
+int saa7134_i2c_unregister(struct saa7134_dev *dev);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-video.c */
+
+extern unsigned int video_debug;
+extern struct video_device saa7134_video_template;
+extern struct video_device saa7134_radio_template;
+
+int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
+int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
+int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id);
+
+int saa7134_videoport_init(struct saa7134_dev *dev);
+void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
+
+int saa7134_video_init1(struct saa7134_dev *dev);
+int saa7134_video_init2(struct saa7134_dev *dev);
+void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
+void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-ts.c */
+
+#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
+
+extern struct videobuf_queue_ops saa7134_ts_qops;
+
+int saa7134_ts_init1(struct saa7134_dev *dev);
+int saa7134_ts_fini(struct saa7134_dev *dev);
+void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
+
+int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
+void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
+
+int saa7134_ts_init_hw(struct saa7134_dev *dev);
+
+int saa7134_ts_start(struct saa7134_dev *dev);
+int saa7134_ts_stop(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-vbi.c */
+
+extern struct videobuf_queue_ops saa7134_vbi_qops;
+extern struct video_device saa7134_vbi_template;
+
+int saa7134_vbi_init1(struct saa7134_dev *dev);
+int saa7134_vbi_fini(struct saa7134_dev *dev);
+void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status);
+
+
+/* ----------------------------------------------------------- */
+/* saa7134-tvaudio.c */
+
+int saa7134_tvaudio_rx2mode(u32 rx);
+
+void saa7134_tvaudio_setmute(struct saa7134_dev *dev);
+void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
+ struct saa7134_input *in);
+void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
+int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
+
+void saa7134_tvaudio_init(struct saa7134_dev *dev);
+int saa7134_tvaudio_init2(struct saa7134_dev *dev);
+int saa7134_tvaudio_fini(struct saa7134_dev *dev);
+int saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
+int saa7134_tvaudio_close(struct saa7134_dev *dev);
+
+int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
+
+void saa7134_enable_i2s(struct saa7134_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7134-oss.c */
+
+extern const struct file_operations saa7134_dsp_fops;
+extern const struct file_operations saa7134_mixer_fops;
+
+int saa7134_oss_init1(struct saa7134_dev *dev);
+int saa7134_oss_fini(struct saa7134_dev *dev);
+void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status);
+
+/* ----------------------------------------------------------- */
+/* saa7134-input.c */
+
+#if defined(CONFIG_VIDEO_SAA7134_RC)
+int saa7134_input_init1(struct saa7134_dev *dev);
+void saa7134_input_fini(struct saa7134_dev *dev);
+void saa7134_input_irq(struct saa7134_dev *dev);
+void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
+int saa7134_ir_start(struct saa7134_dev *dev);
+void saa7134_ir_stop(struct saa7134_dev *dev);
+#else
+#define saa7134_input_init1(dev) ((void)0)
+#define saa7134_input_fini(dev) ((void)0)
+#define saa7134_input_irq(dev) ((void)0)
+#define saa7134_probe_i2c_ir(dev) ((void)0)
+#define saa7134_ir_start(dev) ((void)0)
+#define saa7134_ir_stop(dev) ((void)0)
+#endif
diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig
new file mode 100644
index 000000000000..da88b77a916c
--- /dev/null
+++ b/drivers/media/pci/saa7146/Kconfig
@@ -0,0 +1,38 @@
+config VIDEO_HEXIUM_GEMINI
+ tristate "Hexium Gemini frame grabber"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ ---help---
+ This is a video4linux driver for the Hexium Gemini frame
+ grabber card by Hexium. Please note that the Gemini Dual
+ card is *not* fully supported.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_gemini.
+
+config VIDEO_HEXIUM_ORION
+ tristate "Hexium HV-PCI6 and Orion frame grabber"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ ---help---
+ This is a video4linux driver for the Hexium HV-PCI6 and
+ Orion frame grabber cards by Hexium.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hexium_orion.
+
+config VIDEO_MXB
+ tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
+ depends on PCI && VIDEO_V4L2 && I2C
+ select VIDEO_SAA7146_VV
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for the 'Multimedia eXtension Board'
+ TV card by Siemens-Nixdorf.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxb.
diff --git a/drivers/media/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile
new file mode 100644
index 000000000000..f3566a95e4aa
--- /dev/null
+++ b/drivers/media/pci/saa7146/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_VIDEO_MXB) += mxb.o
+obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
+obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c
index 366434f5647e..366434f5647e 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/pci/saa7146/hexium_gemini.c
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c
index a1eb26d11070..a1eb26d11070 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/pci/saa7146/hexium_orion.c
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
new file mode 100644
index 000000000000..91369daad722
--- /dev/null
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -0,0 +1,886 @@
+/*
+ mxb - v4l2 driver for the Multimedia eXtension Board
+
+ Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de>
+
+ Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html
+ for further details about this card.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG_VARIABLE debug
+
+#include <media/saa7146_vv.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+#include <media/saa7115.h>
+#include <linux/module.h>
+
+#include "tea6415c.h"
+#include "tea6420.h"
+
+#define MXB_AUDIOS 6
+
+#define I2C_SAA7111A 0x24
+#define I2C_TDA9840 0x42
+#define I2C_TEA6415C 0x43
+#define I2C_TEA6420_1 0x4c
+#define I2C_TEA6420_2 0x4d
+#define I2C_TUNER 0x60
+
+#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+/* global variable */
+static int mxb_num;
+
+/* initial frequence the tuner will be tuned to.
+ in verden (lower saxony, germany) 4148 is a
+ channel called "phoenix" */
+static int freq = 4148;
+module_param(freq, int, 0644);
+MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
+
+#define MXB_INPUTS 4
+enum { TUNER, AUX1, AUX3, AUX3_YC };
+
+static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
+ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0,
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD },
+ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+};
+
+/* this array holds the information, which port of the saa7146 each
+ input actually uses. the mxb uses port 0 for every input */
+static struct {
+ int hps_source;
+ int hps_sync;
+} input_port_selection[MXB_INPUTS] = {
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
+};
+
+/* this array holds the information of the audio source (mxb_audios),
+ which has to be switched corresponding to the video source (mxb_channels) */
+static int video_audio_connect[MXB_INPUTS] =
+ { 0, 1, 3, 3 };
+
+struct mxb_routing {
+ u32 input;
+ u32 output;
+};
+
+/* these are the available audio sources, which can switched
+ to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+ {
+ .index = 0,
+ .name = "Tuner",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 1,
+ .name = "AUX1",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 2,
+ .name = "AUX2",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 3,
+ .name = "AUX3",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 4,
+ .name = "Radio (X9)",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 5,
+ .name = "CD-ROM (X10)",
+ .capability = V4L2_AUDCAP_STEREO,
+ }
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the CD-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
+ { { 1, 1 }, { 1, 1 } }, /* Tuner */
+ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
+ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
+ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
+ { { 1, 1 }, { 3, 1 } }, /* Radio */
+ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
+ { { 6, 1 }, { 6, 1 } } /* Mute */
+};
+
+/* These are the necessary input-output-pins for bringing one audio source
+ (see above) to the line-output. Note that gain is set to 0 in this table. */
+static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
+ { { 2, 3 }, { 1, 2 } },
+ { { 5, 3 }, { 6, 2 } },
+ { { 4, 3 }, { 6, 2 } },
+ { { 3, 3 }, { 6, 2 } },
+ { { 2, 3 }, { 3, 2 } },
+ { { 2, 3 }, { 2, 2 } },
+ { { 6, 3 }, { 6, 2 } } /* Mute */
+};
+
+struct mxb
+{
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+
+ struct i2c_adapter i2c_adapter;
+
+ struct v4l2_subdev *saa7111a;
+ struct v4l2_subdev *tda9840;
+ struct v4l2_subdev *tea6415c;
+ struct v4l2_subdev *tuner;
+ struct v4l2_subdev *tea6420_1;
+ struct v4l2_subdev *tea6420_2;
+
+ int cur_mode; /* current audio mode (mono, stereo, ...) */
+ int cur_input; /* current input */
+ int cur_audinput; /* current audio input */
+ int cur_mute; /* current mute status */
+ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
+};
+
+#define saa7111a_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
+#define tda9840_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tda9840, o, f, ##args)
+#define tea6415c_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
+#define tuner_call(mxb, o, f, args...) \
+ v4l2_subdev_call(mxb->tuner, o, f, ##args)
+#define call_all(dev, o, f, args...) \
+ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+static void mxb_update_audmode(struct mxb *mxb)
+{
+ struct v4l2_tuner t = {
+ .audmode = mxb->cur_mode,
+ };
+
+ tda9840_call(mxb, tuner, s_tuner, &t);
+}
+
+static inline void tea6420_route(struct mxb *mxb, int idx)
+{
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
+ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
+ TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
+ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
+ TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0);
+}
+
+static struct saa7146_extension extension;
+
+static int mxb_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct mxb *mxb = dev->ext_priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mxb->cur_mute = ctrl->val;
+ /* switch the audio-source */
+ tea6420_route(mxb, ctrl->val ? 6 :
+ video_audio_connect[mxb->cur_input]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mxb_ctrl_ops = {
+ .s_ctrl = mxb_s_ctrl,
+};
+
+static int mxb_probe(struct saa7146_dev *dev)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct mxb *mxb = NULL;
+
+ v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (hdl->error)
+ return hdl->error;
+ mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
+ if (mxb == NULL) {
+ DEB_D("not enough kernel memory\n");
+ return -ENOMEM;
+ }
+
+
+ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+ if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+ DEB_S("cannot register i2c-device. skipping.\n");
+ kfree(mxb);
+ return -EFAULT;
+ }
+
+ mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "saa7111", I2C_SAA7111A, NULL);
+ mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_1, NULL);
+ mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6420", I2C_TEA6420_2, NULL);
+ mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tea6415c", I2C_TEA6415C, NULL);
+ mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tda9840", I2C_TDA9840, NULL);
+ mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
+ "tuner", I2C_TUNER, NULL);
+
+ /* check if all devices are present */
+ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
+ !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
+ pr_err("did not find all i2c devices. aborting\n");
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+ return -ENODEV;
+ }
+
+ /* all devices are present, probe was successful */
+
+ /* we store the pointer in our private data field */
+ dev->ext_priv = mxb;
+
+ v4l2_ctrl_handler_setup(hdl);
+
+ return 0;
+}
+
+/* some init data for the saa7740, the so-called 'sound arena module'.
+ there are no specs available, so we simply use some init values */
+static struct {
+ int length;
+ char data[9];
+} mxb_saa7740_init[] = {
+ { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
+ { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
+ { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
+ { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
+ { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
+ { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
+ { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
+ { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
+ { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
+ { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
+ { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
+ { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
+ { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
+ { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
+ { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
+ { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
+ { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
+ { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
+ { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
+ { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
+ { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
+ { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
+ { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
+ { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
+ { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
+ { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
+ { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
+ { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
+ { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
+ { 3, { 0x48, 0x00, 0x01 } },
+ { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
+ { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
+ { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
+ { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
+ { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
+ { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
+ { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
+ { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
+ { 3, { 0x80, 0xb3, 0x0a } },
+ {-1, { 0 } }
+};
+
+/* bring hardware to a sane state. this has to be done, just in case someone
+ wants to capture from this device before it has been properly initialized.
+ the capture engine would badly fail, because no valid signal arrives on the
+ saa7146, thus leading to timeouts and stuff. */
+static int mxb_init_done(struct saa7146_dev* dev)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+ struct i2c_msg msg;
+ struct tuner_setup tun_setup;
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ int i = 0, err = 0;
+
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
+ /* select video mode in saa7111a */
+ saa7111a_call(mxb, core, s_std, std);
+
+ /* select tuner-output on saa7111a */
+ i = 0;
+ saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
+ SAA7111_FMT_CCIR, 0);
+
+ /* select a tuner type */
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.addr = ADDR_UNSET;
+ tun_setup.type = TUNER_PHILIPS_PAL;
+ tuner_call(mxb, tuner, s_type_addr, &tun_setup);
+ /* tune in some frequency on tuner */
+ mxb->cur_freq.tuner = 0;
+ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+ mxb->cur_freq.frequency = freq;
+ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+ /* set a default video standard */
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 1);
+ saa7111a_call(mxb, core, s_std, std);
+ tuner_call(mxb, core, s_std, std);
+
+ /* switch to tuner-channel on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* select tuner-output on multicable on tea6415c */
+ tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+
+ /* the rest for mxb */
+ mxb->cur_input = 0;
+ mxb->cur_audinput = video_audio_connect[mxb->cur_input];
+ mxb->cur_mute = 1;
+
+ mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+ mxb_update_audmode(mxb);
+
+ /* check if the saa7740 (aka 'sound arena module') is present
+ on the mxb. if so, we must initialize it. due to lack of
+ informations about the saa7740, the values were reverse
+ engineered. */
+ msg.addr = 0x1b;
+ msg.flags = 0;
+ msg.len = mxb_saa7740_init[0].length;
+ msg.buf = &mxb_saa7740_init[0].data[0];
+
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err == 1) {
+ /* the sound arena module is a pos, that's probably the reason
+ philips refuses to hand out a datasheet for the saa7740...
+ it seems to screw up the i2c bus, so we disable fast irq
+ based i2c transactions here and rely on the slow and safe
+ polling method ... */
+ extension.flags &= ~SAA7146_USE_I2C_IRQ;
+ for (i = 1; ; i++) {
+ if (-1 == mxb_saa7740_init[i].length)
+ break;
+
+ msg.len = mxb_saa7740_init[i].length;
+ msg.buf = &mxb_saa7740_init[i].data[0];
+ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
+ if (err != 1) {
+ DEB_D("failed to initialize 'sound arena module'\n");
+ goto err;
+ }
+ }
+ pr_info("'sound arena module' detected\n");
+ }
+err:
+ /* the rest for saa7146: you should definitely set some basic values
+ for the input-port handling of the saa7146. */
+
+ /* ext->saa has been filled by the core driver */
+
+ /* some stuff is done via variables */
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
+ input_port_selection[mxb->cur_input].hps_sync);
+
+ /* some stuff is done via direct write to the registers */
+
+ /* this is ugly, but because of the fact that this is completely
+ hardware dependend, it should be done directly... */
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_INIT, 0x02000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ return 0;
+}
+
+/* interrupt-handler. this gets called when irq_mask is != 0.
+ it must clear the interrupt-bits in irq_mask it has handled */
+/*
+void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+{
+ struct mxb* mxb = (struct mxb*)dev->ext_priv;
+}
+*/
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
+ if (i->index >= MXB_INPUTS)
+ return -EINVAL;
+ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ *i = mxb->cur_input;
+
+ DEB_EE("VIDIOC_G_INPUT %d\n", *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ int err = 0;
+ int i = 0;
+
+ DEB_EE("VIDIOC_S_INPUT %d\n", input);
+
+ if (input >= MXB_INPUTS)
+ return -EINVAL;
+
+ mxb->cur_input = input;
+
+ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+ input_port_selection[input].hps_sync);
+
+ /* prepare switching of tea6415c and saa7111a;
+ have a look at the 'background'-file for further informations */
+ switch (input) {
+ case TUNER:
+ i = SAA7115_COMPOSITE0;
+
+ err = tea6415c_call(mxb, video, s_routing, 3, 17, 0);
+
+ /* connect tuner-output always to multicable */
+ if (!err)
+ err = tea6415c_call(mxb, video, s_routing, 3, 13, 0);
+ break;
+ case AUX3_YC:
+ /* nothing to be done here. aux3_yc is
+ directly connected to the saa711a */
+ i = SAA7115_SVIDEO1;
+ break;
+ case AUX3:
+ /* nothing to be done here. aux3 is
+ directly connected to the saa711a */
+ i = SAA7115_COMPOSITE1;
+ break;
+ case AUX1:
+ i = SAA7115_COMPOSITE0;
+ err = tea6415c_call(mxb, video, s_routing, 1, 17, 0);
+ break;
+ }
+
+ if (err)
+ return err;
+
+ /* switch video in saa7111a */
+ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
+ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
+
+ mxb->cur_audinput = video_audio_connect[input];
+ /* switch the audio-source only if necessary */
+ if (0 == mxb->cur_mute)
+ tea6420_route(mxb, mxb->cur_audinput);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
+
+ memset(t, 0, sizeof(*t));
+ strlcpy(t->name, "TV Tuner", sizeof(t->name));
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ t->audmode = mxb->cur_mode;
+ return call_all(dev, tuner, g_tuner, t);
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (t->index) {
+ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
+ t->index);
+ return -EINVAL;
+ }
+
+ mxb->cur_mode = t->audmode;
+ return call_all(dev, tuner, s_tuner, t);
+}
+
+static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ return call_all(dev, video, querystd, norm);
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (f->tuner)
+ return -EINVAL;
+ *f = mxb->cur_freq;
+
+ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ if (f->tuner)
+ return -EINVAL;
+
+ if (V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+
+ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
+
+ /* tune in desired frequency */
+ tuner_call(mxb, tuner, s_frequency, f);
+ /* let the tuner subdev clamp the frequency to the tuner range */
+ tuner_call(mxb, tuner, g_frequency, f);
+ mxb->cur_freq = *f;
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+
+ if (mxb->cur_input)
+ return 0;
+
+ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+ spin_lock(&dev->slock);
+ vv->vbi_fieldcount = 0;
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (a->index >= MXB_AUDIOS)
+ return -EINVAL;
+ *a = mxb_audios[a->index];
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_EE("VIDIOC_G_AUDIO\n");
+ *a = mxb_audios[mxb->cur_audinput];
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
+ if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) {
+ if (mxb->cur_audinput != a->index) {
+ mxb->cur_audinput = a->index;
+ tea6420_route(mxb, a->index);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+ }
+ return 0;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (v4l2_chip_match_host(&reg->match)) {
+ reg->val = saa7146_read(dev, reg->reg);
+ reg->size = 4;
+ return 0;
+ }
+ call_all(dev, core, g_register, reg);
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (v4l2_chip_match_host(&reg->match)) {
+ saa7146_write(dev, reg->reg, reg->val);
+ reg->size = 4;
+ return 0;
+ }
+ return call_all(dev, core, s_register, reg);
+}
+#endif
+
+static struct saa7146_ext_vv vv_data;
+
+/* this function only gets called when the probing was successful */
+static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+{
+ struct mxb *mxb;
+
+ DEB_EE("dev:%p\n", dev);
+
+ saa7146_vv_init(dev, &vv_data);
+ if (mxb_probe(dev)) {
+ saa7146_vv_release(dev);
+ return -1;
+ }
+ mxb = (struct mxb *)dev->ext_priv;
+
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+ vv_data.vid_ops.vidioc_querystd = vidioc_querystd;
+ vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+ vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ vv_data.vid_ops.vidioc_g_register = vidioc_g_register;
+ vv_data.vid_ops.vidioc_s_register = vidioc_s_register;
+#endif
+ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+ ERR("cannot register capture v4l2 device. skipping.\n");
+ saa7146_vv_release(dev);
+ return -1;
+ }
+
+ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+ if (MXB_BOARD_CAN_DO_VBI(dev)) {
+ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+ ERR("cannot register vbi v4l2 device. skipping.\n");
+ }
+ }
+
+ pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
+
+ mxb_num++;
+ mxb_init_done(dev);
+ return 0;
+}
+
+static int mxb_detach(struct saa7146_dev *dev)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ DEB_EE("dev:%p\n", dev);
+
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
+ saa7146_unregister_device(&mxb->video_dev,dev);
+ if (MXB_BOARD_CAN_DO_VBI(dev))
+ saa7146_unregister_device(&mxb->vbi_dev, dev);
+ saa7146_vv_release(dev);
+
+ mxb_num--;
+
+ i2c_del_adapter(&mxb->i2c_adapter);
+ kfree(mxb);
+
+ return 0;
+}
+
+static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
+{
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+ if (V4L2_STD_PAL_I == standard->id) {
+ v4l2_std_id std = V4L2_STD_PAL_I;
+
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 0);
+ saa7111a_call(mxb, core, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, core, s_std, std);
+ } else {
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+ if (mxb->cur_input)
+ std = standard->id;
+ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 1);
+ saa7111a_call(mxb, core, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, core, s_std, std);
+ }
+ return 0;
+}
+
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL-BG", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "PAL-I", .id = V4L2_STD_PAL_I,
+ .v_offset = 0x17, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 680,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x16, .v_field = 240,
+ .h_offset = 0x06, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }, {
+ .name = "SECAM", .id = V4L2_STD_SECAM,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x14, .h_pixels = 720,
+ .v_max_out = 576, .h_max_out = 768,
+ }
+};
+
+static struct saa7146_pci_extension_data mxb = {
+ .ext_priv = "Multimedia eXtension Board",
+ .ext = &extension,
+};
+
+static struct pci_device_id pci_tbl[] = {
+ {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ .driver_data = (unsigned long)&mxb,
+ }, {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_ext_vv vv_data = {
+ .inputs = MXB_INPUTS,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO,
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+};
+
+static struct saa7146_extension extension = {
+ .name = "Multimedia eXtension Board",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .pci_tbl = &pci_tbl[0],
+ .module = THIS_MODULE,
+
+ .attach = mxb_attach,
+ .detach = mxb_detach,
+
+ .irq_mask = 0,
+ .irq_func = NULL,
+};
+
+static int __init mxb_init_module(void)
+{
+ if (saa7146_register_extension(&extension)) {
+ DEB_S("failed to register extension\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit mxb_cleanup_module(void)
+{
+ saa7146_unregister_extension(&extension);
+}
+
+module_init(mxb_init_module);
+module_exit(mxb_cleanup_module);
+
+MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
+MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/saa7164/Kconfig b/drivers/media/pci/saa7164/Kconfig
new file mode 100644
index 000000000000..a53db7d1c96e
--- /dev/null
+++ b/drivers/media/pci/saa7164/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_SAA7164
+ tristate "NXP SAA7164 support"
+ depends on DVB_CORE && VIDEO_DEV && PCI && I2C
+ select I2C_ALGOBIT
+ select FW_LOADER
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_DVB
+ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for NXP SAA7164 based
+ TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7164
+
diff --git a/drivers/media/pci/saa7164/Makefile b/drivers/media/pci/saa7164/Makefile
new file mode 100644
index 000000000000..ba0e33a1ee24
--- /dev/null
+++ b/drivers/media/pci/saa7164/Makefile
@@ -0,0 +1,12 @@
+saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
+ saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \
+ saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o
+
+obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c
new file mode 100644
index 000000000000..eff7135cf0e8
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-api.c
@@ -0,0 +1,1524 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#include "saa7164.h"
+
+int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i)
+{
+ int ret;
+
+ if (!(saa_debug & DBGLVL_CPU))
+ return 0;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ i->deviceinst = 0;
+ i->devicespec = 0;
+ i->mode = 0;
+ i->status = 0;
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad);
+
+ return ret;
+}
+
+int saa7164_api_collect_debug(struct saa7164_dev *dev)
+{
+ struct tmComResDebugGetData d;
+ u8 more = 255;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ while (more--) {
+
+ memset(&d, 0, sizeof(d));
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_DEBUG_DATA_CONTROL, sizeof(d), &d);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n",
+ __func__, ret);
+
+ if (d.dwResult != SAA_OK)
+ break;
+
+ printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr,
+ d.ucDebugData);
+ }
+
+ return 0;
+}
+
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level)
+{
+ struct tmComResDebugSetLevel lvl;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level);
+
+ /* Retrieve current state */
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel);
+
+ lvl.dwDebugLevel = level;
+
+ /* set new state */
+ ret = saa7164_cmd_send(dev, 0, SET_CUR,
+ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_vbi_format(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResProbeCommit fmt, rsp;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__,
+ port->nr, port->hwcfg.unitid);
+
+ fmt.bmHint = 0;
+ fmt.bFormatIndex = 1;
+ fmt.bFrameIndex = 1;
+
+ /* Probe, see if it can support this format */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret);
+
+ /* See of the format change was successful */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret);
+ } else {
+ /* Compare requested vs received, should be same */
+ if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) {
+ dprintk(DBGLVL_API, "SET/PROBE Verified\n");
+
+ /* Ask the device to select the negotiated format */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() commit error, ret = 0x%x\n",
+ __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
+ GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n",
+ __func__, ret);
+
+ if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) {
+ printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n",
+ __func__, ret);
+ } else
+ dprintk(DBGLVL_API, "SET/COMMIT Verified\n");
+
+ dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint);
+ dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n",
+ rsp.bFormatIndex);
+ dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n",
+ rsp.bFrameIndex);
+ } else
+ printk(KERN_ERR "%s() compare failed\n", __func__);
+ }
+
+ if (ret == SAA_OK)
+ dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr);
+
+ return ret;
+}
+
+int saa7164_api_set_gop_size(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoGopStructure gs;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s()\n", __func__);
+
+ gs.ucRefFrameDist = port->encoder_params.refdist;
+ gs.ucGOPSize = port->encoder_params.gop_size;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_GOP_STRUCTURE_CONTROL,
+ sizeof(gs), &gs);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoBitRate vb;
+ struct tmComResEncAudioBitRate ab;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+ port->hwcfg.sourceid);
+
+ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+ port->encoder_profile = EU_PROFILE_PS_DVD;
+ else
+ port->encoder_profile = EU_PROFILE_TS_HQ;
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Resolution */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Establish video bitrates */
+ if (port->encoder_params.bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT;
+ else
+ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK;
+ vb.dwVideoBitRate = port->encoder_params.bitrate;
+ vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_BIT_RATE_CONTROL,
+ sizeof(struct tmComResEncVideoBitRate),
+ &vb);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Establish audio bitrates */
+ ab.ucAudioBitRateMode = 0;
+ ab.dwAudioBitRate = 384000;
+ ab.dwAudioBitRatePeak = ab.dwAudioBitRate;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_AUDIO_BIT_RATE_CONTROL,
+ sizeof(struct tmComResEncAudioBitRate),
+ &ab);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
+ ret);
+
+ saa7164_api_set_aspect_ratio(port);
+ saa7164_api_set_gop_size(port);
+
+ return ret;
+}
+
+int saa7164_api_get_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoBitRate v;
+ struct tmComResEncAudioBitRate a;
+ struct tmComResEncVideoInputAspectRatio ar;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
+ port->hwcfg.sourceid);
+
+ port->encoder_profile = 0;
+ port->video_format = 0;
+ port->video_resolution = 0;
+ port->audio_format = 0;
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8),
+ &port->video_resolution);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Aspect Ratio */
+ ar.width = 0;
+ ar.height = 0;
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
+ EU_VIDEO_INPUT_ASPECT_CONTROL,
+ sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile);
+ dprintk(DBGLVL_ENC, "video_format = %d\n", port->video_format);
+ dprintk(DBGLVL_ENC, "audio_format = %d\n", port->audio_format);
+ dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution);
+ dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n",
+ v.ucVideoBitRateMode);
+ dprintk(DBGLVL_ENC, "v.dwVideoBitRate = %d\n",
+ v.dwVideoBitRate);
+ dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n",
+ v.dwVideoBitRatePeak);
+ dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n",
+ a.ucAudioBitRateMode);
+ dprintk(DBGLVL_ENC, "a.dwVideoBitRate = %d\n",
+ a.dwAudioBitRate);
+ dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n",
+ a.dwAudioBitRatePeak);
+ dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n",
+ ar.width, ar.height);
+
+ return ret;
+}
+
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResEncVideoInputAspectRatio ar;
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s(%d)\n", __func__,
+ port->encoder_params.ctl_aspect);
+
+ switch (port->encoder_params.ctl_aspect) {
+ case V4L2_MPEG_VIDEO_ASPECT_1x1:
+ ar.width = 1;
+ ar.height = 1;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_4x3:
+ ar.width = 4;
+ ar.height = 3;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ ar.width = 16;
+ ar.height = 9;
+ break;
+ case V4L2_MPEG_VIDEO_ASPECT_221x100:
+ ar.width = 221;
+ ar.height = 100;
+ break;
+ default:
+ BUG();
+ }
+
+ dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__,
+ port->encoder_params.ctl_aspect,
+ ar.width, ar.height);
+
+ /* Aspect Ratio */
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
+ EU_VIDEO_INPUT_ASPECT_CONTROL,
+ sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+ u16 val;
+
+ if (ctl == PU_BRIGHTNESS_CONTROL)
+ val = port->ctl_brightness;
+ else
+ if (ctl == PU_CONTRAST_CONTROL)
+ val = port->ctl_contrast;
+ else
+ if (ctl == PU_HUE_CONTROL)
+ val = port->ctl_hue;
+ else
+ if (ctl == PU_SATURATION_CONTROL)
+ val = port->ctl_saturation;
+ else
+ if (ctl == PU_SHARPNESS_CONTROL)
+ val = port->ctl_sharpness;
+ else
+ return -EINVAL;
+
+ dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n",
+ __func__, port->encunit.vsourceid, ctl, val);
+
+ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR,
+ ctl, sizeof(u16), &val);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+ u16 val;
+
+ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR,
+ ctl, sizeof(u16), &val);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+ return ret;
+ }
+
+ dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n",
+ __func__, ctl, val);
+
+ if (ctl == PU_BRIGHTNESS_CONTROL)
+ port->ctl_brightness = val;
+ else
+ if (ctl == PU_CONTRAST_CONTROL)
+ port->ctl_contrast = val;
+ else
+ if (ctl == PU_HUE_CONTROL)
+ port->ctl_hue = val;
+ else
+ if (ctl == PU_SATURATION_CONTROL)
+ port->ctl_saturation = val;
+ else
+ if (ctl == PU_SHARPNESS_CONTROL)
+ port->ctl_sharpness = val;
+
+ return ret;
+}
+
+int saa7164_api_set_videomux(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 };
+ int ret;
+
+ dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n",
+ __func__, port->mux_input, inputs[port->mux_input - 1]);
+
+ /* Audio Mute */
+ ret = saa7164_api_audio_mute(port, 1);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Video Mux */
+ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Audio Mux */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8),
+ &inputs[port->mux_input - 1]);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Audio UnMute */
+ ret = saa7164_api_audio_mute(port, 0);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute)
+{
+ struct saa7164_dev *dev = port->dev;
+ u8 v = mute;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ MUTE_CONTROL, sizeof(u8), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+/* 0 = silence, 0xff = full */
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level)
+{
+ struct saa7164_dev *dev = port->dev;
+ s16 v, min, max;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, level);
+
+ /* Obtain the min/max ranges */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN,
+ VOLUME_CONTROL, sizeof(u16), &min);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX,
+ VOLUME_CONTROL, sizeof(u16), &max);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+ level, min, max, v);
+
+ v = level;
+ if (v < min)
+ v = min;
+ if (v > max)
+ v = max;
+
+ /* Left */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Right */
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ (0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
+ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
+ level, min, max, v);
+
+ return ret;
+}
+
+int saa7164_api_set_audio_std(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResAudioDefaults lvl;
+ struct tmComResTunerStandard tvaudio;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ /* Establish default levels */
+ lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+ lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT;
+ lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT;
+ lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT;
+ lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT;
+ lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT;
+ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
+ AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults),
+ &lvl);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ /* Manually select the appropriate TV audio standard */
+ if (port->encodernorm.id & V4L2_STD_NTSC) {
+ tvaudio.std = TU_STANDARD_NTSC_M;
+ tvaudio.country = 1;
+ } else {
+ tvaudio.std = TU_STANDARD_PAL_I;
+ tvaudio.country = 44;
+ }
+
+ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+ TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n",
+ __func__, ret);
+ return ret;
+}
+
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct tmComResTunerStandardAuto p;
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect);
+
+ /* Disable TV Audio autodetect if not already set (buggy) */
+ if (autodetect)
+ p.mode = TU_STANDARD_AUTO;
+ else
+ p.mode = TU_STANDARD_MANUAL;
+ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
+ TU_STANDARD_AUTO_CONTROL, sizeof(p), &p);
+ if (ret != SAA_OK)
+ printk(KERN_ERR
+ "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_videomux(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret;
+
+ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR,
+ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_ENC, "%s() v_mux=%d\n",
+ __func__, port->mux_input);
+
+ return ret;
+}
+
+int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ u16 len = 0;
+ u8 buf[256];
+ int ret;
+ u8 mas;
+
+ dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__,
+ port->nr, port->type, val);
+
+ if (port->nr == 0)
+ mas = 0xd0;
+ else
+ mas = 0xe0;
+
+ memset(buf, 0, sizeof(buf));
+
+ buf[0x00] = 0x04;
+ buf[0x01] = 0x00;
+ buf[0x02] = 0x00;
+ buf[0x03] = 0x00;
+
+ buf[0x04] = 0x04;
+ buf[0x05] = 0x00;
+ buf[0x06] = 0x00;
+ buf[0x07] = 0x00;
+
+ buf[0x08] = reg;
+ buf[0x09] = 0x26;
+ buf[0x0a] = mas;
+ buf[0x0b] = 0xb0;
+
+ buf[0x0c] = val;
+ buf[0x0d] = 0x00;
+ buf[0x0e] = 0x00;
+ buf[0x0f] = 0x00;
+
+ ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+#if 0
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf, 16,
+ false);
+#endif
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* Disable the IF block AGC controls */
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
+{
+ struct saa7164_dev *dev = port->dev;
+ int ret = 0;
+ u8 agc_disable;
+
+ dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
+
+ if (std & V4L2_STD_NTSC) {
+ dprintk(DBGLVL_API, " NTSC\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_I) {
+ dprintk(DBGLVL_API, " PAL-I\n");
+ saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_M) {
+ dprintk(DBGLVL_API, " PAL-M\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_N) {
+ dprintk(DBGLVL_API, " PAL-N\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_Nc) {
+ dprintk(DBGLVL_API, " PAL-Nc\n");
+ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_B) {
+ dprintk(DBGLVL_API, " PAL-B\n");
+ saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_PAL_DK) {
+ dprintk(DBGLVL_API, " PAL-DK\n");
+ saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */
+ agc_disable = 0;
+ } else if (std & V4L2_STD_SECAM_L) {
+ dprintk(DBGLVL_API, " SECAM-L\n");
+ saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */
+ agc_disable = 0;
+ } else {
+ /* Unknown standard, assume DTV */
+ dprintk(DBGLVL_API, " Unknown (assuming DTV)\n");
+ /* Undefinded Video Standard */
+ saa7164_api_set_dif(port, 0x00, 0x80);
+ agc_disable = 1;
+ }
+
+ saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */
+ saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */
+ saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */
+ saa7164_api_set_dif(port, 0x04, 0x01); /* Active */
+ msleep(100);
+ saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
+ msleep(100);
+
+ return ret;
+}
+
+/* Ensure the dif is in the correct state for the operating mode
+ * (analog / dtv). We only configure the diff through the analog encoder
+ * so when we're in digital mode we need to find the appropriate encoder
+ * and use it to configure the DIF.
+ */
+int saa7164_api_initialize_dif(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_port *p = NULL;
+ int ret = -EINVAL;
+ u32 std = 0;
+
+ dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__,
+ port->nr, port->type);
+
+ if (port->type == SAA7164_MPEG_ENCODER) {
+ /* Pick any analog standard to init the diff.
+ * we'll come back during encoder_init'
+ * and set the correct standard if requried.
+ */
+ std = V4L2_STD_NTSC;
+ } else
+ if (port->type == SAA7164_MPEG_DVB) {
+ if (port->nr == SAA7164_PORT_TS1)
+ p = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ p = &dev->ports[SAA7164_PORT_ENC2];
+ } else
+ if (port->type == SAA7164_MPEG_VBI) {
+ std = V4L2_STD_NTSC;
+ if (port->nr == SAA7164_PORT_VBI1)
+ p = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ p = &dev->ports[SAA7164_PORT_ENC2];
+ } else
+ BUG();
+
+ if (p)
+ ret = saa7164_api_configure_dif(p, std);
+
+ return ret;
+}
+
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ int ret;
+
+ dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n",
+ __func__, port->nr, port->hwcfg.unitid, mode);
+
+ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
+ SAA_STATE_CONTROL, sizeof(mode), &mode);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n",
+ __func__, port->nr, port->hwcfg.unitid, ret);
+
+ return ret;
+}
+
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version)
+{
+ int ret;
+
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_FW_VERSION_CONTROL, sizeof(u32), version);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
+{
+ u8 reg[] = { 0x0f, 0x00 };
+
+ if (buflen < 128)
+ return -ENOMEM;
+
+ /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */
+ /* TODO: Pull the details from the boards struct */
+ return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg),
+ &reg[0], 128, buf);
+}
+
+int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
+ struct saa7164_port *port)
+{
+ struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
+
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+ dprintk(DBGLVL_API, " VideoStandard = 0x%x\n", fmt->VideoStandard);
+ dprintk(DBGLVL_API, " StartLine = %d\n", fmt->StartLine);
+ dprintk(DBGLVL_API, " EndLine = %d\n", fmt->EndLine);
+ dprintk(DBGLVL_API, " FieldRate = %d\n", fmt->FieldRate);
+ dprintk(DBGLVL_API, " bNumLines = %d\n", fmt->bNumLines);
+
+ /* Cache the hardware configuration in the port */
+
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_VBI (becomes dev->en[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResTSFormatDescrHeader *tsfmt)
+{
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
+ dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset);
+ dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength);
+ dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength);
+ dprintk(DBGLVL_API, " bguid = (....)\n");
+
+ /* Cache the hardware configuration in the port */
+
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
+ struct saa7164_port *port,
+ struct tmComResPSFormatDescrHeader *fmt)
+{
+ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
+ dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength);
+ dprintk(DBGLVL_API, " wPackLength= 0x%x\n", fmt->wPackLength);
+ dprintk(DBGLVL_API, " bPackDataType= 0x%x\n", fmt->bPackDataType);
+
+ /* Cache the hardware configuration in the port */
+ /* TODO: CHECK THIS in the port config */
+ port->bufcounter = port->hwcfg.BARLocation;
+ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
+ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
+ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
+ port->bufptr32l = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
+ port->bufptr32h = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ port->bufptr64 = port->hwcfg.BARLocation +
+ (4 * sizeof(u32)) +
+ (sizeof(u32) * port->hwcfg.buffercount);
+ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
+ port->hwcfg.BARLocation);
+
+ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n",
+ port->nr);
+
+ return 0;
+}
+
+int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
+{
+ struct saa7164_port *tsport = NULL;
+ struct saa7164_port *encport = NULL;
+ struct saa7164_port *vbiport = NULL;
+ u32 idx, next_offset;
+ int i;
+ struct tmComResDescrHeader *hdr, *t;
+ struct tmComResExtDevDescrHeader *exthdr;
+ struct tmComResPathDescrHeader *pathhdr;
+ struct tmComResAntTermDescrHeader *anttermhdr;
+ struct tmComResTunerDescrHeader *tunerunithdr;
+ struct tmComResDMATermDescrHeader *vcoutputtermhdr;
+ struct tmComResTSFormatDescrHeader *tsfmt;
+ struct tmComResPSFormatDescrHeader *psfmt;
+ struct tmComResSelDescrHeader *psel;
+ struct tmComResProcDescrHeader *pdh;
+ struct tmComResAFeatureDescrHeader *afd;
+ struct tmComResEncoderDescrHeader *edh;
+ struct tmComResVBIFormatDescrHeader *vbifmt;
+ u32 currpath = 0;
+
+ dprintk(DBGLVL_API,
+ "%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n",
+ __func__, len, (u32)sizeof(struct tmComResDescrHeader));
+
+ for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) {
+
+ hdr = (struct tmComResDescrHeader *)(buf + idx);
+
+ if (hdr->type != CS_INTERFACE)
+ return SAA_ERR_NOT_SUPPORTED;
+
+ dprintk(DBGLVL_API, "@ 0x%x =\n", idx);
+ switch (hdr->subtype) {
+ case GENERAL_REQUEST:
+ dprintk(DBGLVL_API, " GENERAL_REQUEST\n");
+ break;
+ case VC_TUNER_PATH:
+ dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
+ pathhdr = (struct tmComResPathDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " pathid = 0x%x\n",
+ pathhdr->pathid);
+ currpath = pathhdr->pathid;
+ break;
+ case VC_INPUT_TERMINAL:
+ dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
+ anttermhdr =
+ (struct tmComResAntTermDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " terminalid = 0x%x\n",
+ anttermhdr->terminalid);
+ dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
+ anttermhdr->terminaltype);
+ switch (anttermhdr->terminaltype) {
+ case ITT_ANTENNA:
+ dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
+ break;
+ case LINE_CONNECTOR:
+ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
+ break;
+ case SPDIF_CONNECTOR:
+ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
+ break;
+ case COMPOSITE_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPOSITE_CONNECTOR\n");
+ break;
+ case SVIDEO_CONNECTOR:
+ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
+ break;
+ case COMPONENT_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPONENT_CONNECTOR\n");
+ break;
+ case STANDARD_DMA:
+ dprintk(DBGLVL_API, " = STANDARD_DMA\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, " = undefined (0x%x)\n",
+ anttermhdr->terminaltype);
+ }
+ dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
+ anttermhdr->assocterminal);
+ dprintk(DBGLVL_API, " iterminal = 0x%x\n",
+ anttermhdr->iterminal);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ anttermhdr->controlsize);
+ break;
+ case VC_OUTPUT_TERMINAL:
+ dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
+ vcoutputtermhdr =
+ (struct tmComResDMATermDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ vcoutputtermhdr->unitid);
+ dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
+ vcoutputtermhdr->terminaltype);
+ switch (vcoutputtermhdr->terminaltype) {
+ case ITT_ANTENNA:
+ dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
+ break;
+ case LINE_CONNECTOR:
+ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
+ break;
+ case SPDIF_CONNECTOR:
+ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
+ break;
+ case COMPOSITE_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPOSITE_CONNECTOR\n");
+ break;
+ case SVIDEO_CONNECTOR:
+ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
+ break;
+ case COMPONENT_CONNECTOR:
+ dprintk(DBGLVL_API,
+ " = COMPONENT_CONNECTOR\n");
+ break;
+ case STANDARD_DMA:
+ dprintk(DBGLVL_API, " = STANDARD_DMA\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, " = undefined (0x%x)\n",
+ vcoutputtermhdr->terminaltype);
+ }
+ dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
+ vcoutputtermhdr->assocterminal);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ vcoutputtermhdr->sourceid);
+ dprintk(DBGLVL_API, " iterminal = 0x%x\n",
+ vcoutputtermhdr->iterminal);
+ dprintk(DBGLVL_API, " BARLocation = 0x%x\n",
+ vcoutputtermhdr->BARLocation);
+ dprintk(DBGLVL_API, " flags = 0x%x\n",
+ vcoutputtermhdr->flags);
+ dprintk(DBGLVL_API, " interruptid = 0x%x\n",
+ vcoutputtermhdr->interruptid);
+ dprintk(DBGLVL_API, " buffercount = 0x%x\n",
+ vcoutputtermhdr->buffercount);
+ dprintk(DBGLVL_API, " metadatasize = 0x%x\n",
+ vcoutputtermhdr->metadatasize);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ vcoutputtermhdr->controlsize);
+ dprintk(DBGLVL_API, " numformats = 0x%x\n",
+ vcoutputtermhdr->numformats);
+
+ t = (struct tmComResDescrHeader *)
+ ((struct tmComResDMATermDescrHeader *)(buf + idx));
+ next_offset = idx + (vcoutputtermhdr->len);
+ for (i = 0; i < vcoutputtermhdr->numformats; i++) {
+ t = (struct tmComResDescrHeader *)
+ (buf + next_offset);
+ switch (t->subtype) {
+ case VS_FORMAT_MPEG2TS:
+ tsfmt =
+ (struct tmComResTSFormatDescrHeader *)t;
+ if (currpath == 1)
+ tsport = &dev->ports[SAA7164_PORT_TS1];
+ else
+ tsport = &dev->ports[SAA7164_PORT_TS2];
+ memcpy(&tsport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ saa7164_api_configure_port_mpeg2ts(dev,
+ tsport, tsfmt);
+ break;
+ case VS_FORMAT_MPEG2PS:
+ psfmt =
+ (struct tmComResPSFormatDescrHeader *)t;
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ saa7164_api_configure_port_mpeg2ps(dev,
+ encport, psfmt);
+ break;
+ case VS_FORMAT_VBI:
+ vbifmt =
+ (struct tmComResVBIFormatDescrHeader *)t;
+ if (currpath == 1)
+ vbiport = &dev->ports[SAA7164_PORT_VBI1];
+ else
+ vbiport = &dev->ports[SAA7164_PORT_VBI2];
+ memcpy(&vbiport->hwcfg, vcoutputtermhdr,
+ sizeof(*vcoutputtermhdr));
+ memcpy(&vbiport->vbi_fmt_ntsc, vbifmt,
+ sizeof(*vbifmt));
+ saa7164_api_configure_port_vbi(dev,
+ vbiport);
+ break;
+ case VS_FORMAT_RDS:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_RDS\n");
+ break;
+ case VS_FORMAT_UNCOMPRESSED:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_UNCOMPRESSED\n");
+ break;
+ case VS_FORMAT_TYPE:
+ dprintk(DBGLVL_API,
+ " = VS_FORMAT_TYPE\n");
+ break;
+ default:
+ dprintk(DBGLVL_API,
+ " = undefined (0x%x)\n",
+ t->subtype);
+ }
+ next_offset += t->len;
+ }
+
+ break;
+ case TUNER_UNIT:
+ dprintk(DBGLVL_API, " TUNER_UNIT\n");
+ tunerunithdr =
+ (struct tmComResTunerDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ tunerunithdr->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ tunerunithdr->sourceid);
+ dprintk(DBGLVL_API, " iunit = 0x%x\n",
+ tunerunithdr->iunit);
+ dprintk(DBGLVL_API, " tuningstandards = 0x%x\n",
+ tunerunithdr->tuningstandards);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ tunerunithdr->controlsize);
+ dprintk(DBGLVL_API, " controls = 0x%x\n",
+ tunerunithdr->controls);
+
+ if (tunerunithdr->unitid == tunerunithdr->iunit) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->tunerunit, tunerunithdr,
+ sizeof(struct tmComResTunerDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d] tuner)\n",
+ encport->nr);
+ }
+ break;
+ case VC_SELECTOR_UNIT:
+ psel = (struct tmComResSelDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ psel->unitid);
+ dprintk(DBGLVL_API, " nrinpins = 0x%x\n",
+ psel->nrinpins);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ psel->sourceid);
+ break;
+ case VC_PROCESSING_UNIT:
+ pdh = (struct tmComResProcDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ pdh->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ pdh->sourceid);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ pdh->controlsize);
+ if (pdh->controlsize == 0x04) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->vidproc, pdh,
+ sizeof(struct tmComResProcDescrHeader));
+ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case FEATURE_UNIT:
+ afd = (struct tmComResAFeatureDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " FEATURE_UNIT\n");
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ afd->unitid);
+ dprintk(DBGLVL_API, " sourceid = 0x%x\n",
+ afd->sourceid);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ afd->controlsize);
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->audfeat, afd,
+ sizeof(struct tmComResAFeatureDescrHeader));
+ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
+ encport->nr);
+ break;
+ case ENCODER_UNIT:
+ edh = (struct tmComResEncoderDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " ENCODER_UNIT\n");
+ dprintk(DBGLVL_API, " subtype = 0x%x\n", edh->subtype);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n", edh->unitid);
+ dprintk(DBGLVL_API, " vsourceid = 0x%x\n",
+ edh->vsourceid);
+ dprintk(DBGLVL_API, " asourceid = 0x%x\n",
+ edh->asourceid);
+ dprintk(DBGLVL_API, " iunit = 0x%x\n", edh->iunit);
+ if (edh->iunit == edh->unitid) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->encunit, edh,
+ sizeof(struct tmComResEncoderDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case EXTENSION_UNIT:
+ dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
+ exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx);
+ dprintk(DBGLVL_API, " unitid = 0x%x\n",
+ exthdr->unitid);
+ dprintk(DBGLVL_API, " deviceid = 0x%x\n",
+ exthdr->deviceid);
+ dprintk(DBGLVL_API, " devicetype = 0x%x\n",
+ exthdr->devicetype);
+ if (exthdr->devicetype & 0x1)
+ dprintk(DBGLVL_API, " = Decoder Device\n");
+ if (exthdr->devicetype & 0x2)
+ dprintk(DBGLVL_API, " = GPIO Source\n");
+ if (exthdr->devicetype & 0x4)
+ dprintk(DBGLVL_API, " = Video Decoder\n");
+ if (exthdr->devicetype & 0x8)
+ dprintk(DBGLVL_API, " = Audio Decoder\n");
+ if (exthdr->devicetype & 0x20)
+ dprintk(DBGLVL_API, " = Crossbar\n");
+ if (exthdr->devicetype & 0x40)
+ dprintk(DBGLVL_API, " = Tuner\n");
+ if (exthdr->devicetype & 0x80)
+ dprintk(DBGLVL_API, " = IF PLL\n");
+ if (exthdr->devicetype & 0x100)
+ dprintk(DBGLVL_API, " = Demodulator\n");
+ if (exthdr->devicetype & 0x200)
+ dprintk(DBGLVL_API, " = RDS Decoder\n");
+ if (exthdr->devicetype & 0x400)
+ dprintk(DBGLVL_API, " = Encoder\n");
+ if (exthdr->devicetype & 0x800)
+ dprintk(DBGLVL_API, " = IR Decoder\n");
+ if (exthdr->devicetype & 0x1000)
+ dprintk(DBGLVL_API, " = EEPROM\n");
+ if (exthdr->devicetype & 0x2000)
+ dprintk(DBGLVL_API,
+ " = VBI Decoder\n");
+ if (exthdr->devicetype & 0x10000)
+ dprintk(DBGLVL_API,
+ " = Streaming Device\n");
+ if (exthdr->devicetype & 0x20000)
+ dprintk(DBGLVL_API,
+ " = DRM Device\n");
+ if (exthdr->devicetype & 0x40000000)
+ dprintk(DBGLVL_API,
+ " = Generic Device\n");
+ if (exthdr->devicetype & 0x80000000)
+ dprintk(DBGLVL_API,
+ " = Config Space Device\n");
+ dprintk(DBGLVL_API, " numgpiopins = 0x%x\n",
+ exthdr->numgpiopins);
+ dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n",
+ exthdr->numgpiogroups);
+ dprintk(DBGLVL_API, " controlsize = 0x%x\n",
+ exthdr->controlsize);
+ if (exthdr->devicetype & 0x80) {
+ if (currpath == 1)
+ encport = &dev->ports[SAA7164_PORT_ENC1];
+ else
+ encport = &dev->ports[SAA7164_PORT_ENC2];
+ memcpy(&encport->ifunit, exthdr,
+ sizeof(struct tmComResExtDevDescrHeader));
+ dprintk(DBGLVL_API,
+ " (becomes dev->enc[%d])\n",
+ encport->nr);
+ }
+ break;
+ case PVC_INFRARED_UNIT:
+ dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
+ break;
+ case DRM_UNIT:
+ dprintk(DBGLVL_API, " DRM_UNIT\n");
+ break;
+ default:
+ dprintk(DBGLVL_API, "default %d\n", hdr->subtype);
+ }
+
+ dprintk(DBGLVL_API, " 1.%x\n", hdr->len);
+ dprintk(DBGLVL_API, " 2.%x\n", hdr->type);
+ dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype);
+ dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid);
+
+ idx += hdr->len;
+ }
+
+ return 0;
+}
+
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
+{
+ int ret;
+ u32 buflen = 0;
+ u8 *buf;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ /* Get the total descriptor length */
+ ret = saa7164_cmd_send(dev, 0, GET_LEN,
+ GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+
+ dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n",
+ __func__, buflen);
+
+ /* Allocate enough storage for all of the descs */
+ buf = kzalloc(buflen, GFP_KERNEL);
+ if (!buf)
+ return SAA_ERR_NO_RESOURCES;
+
+ /* Retrieve them */
+ ret = saa7164_cmd_send(dev, 0, GET_CUR,
+ GET_DESCRIPTORS_CONTROL, buflen, buf);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
+ goto out;
+ }
+
+ if (saa_debug & DBGLVL_API)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf,
+ buflen & ~15, false);
+
+ saa7164_api_dump_subdevs(dev, buf, buflen);
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+ u32 datalen, u8 *data)
+{
+ struct saa7164_dev *dev = bus->dev;
+ u16 len = 0;
+ int unitid;
+ u8 buf[256];
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ if (reglen > 4)
+ return -EIO;
+
+ /* Prepare the send buffer */
+ /* Bytes 00-03 source register length
+ * 04-07 source bytes to read
+ * 08... register address
+ */
+ memset(buf, 0, sizeof(buf));
+ memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen);
+ *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+ *((u32 *)(buf + 1 * sizeof(u32))) = datalen;
+
+ unitid = saa7164_i2caddr_to_unitid(bus, addr);
+ if (unitid < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr 0x%x to unitid\n",
+ __func__, addr);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+ if (saa_debug & DBGLVL_I2C)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf,
+ 32, false);
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+ else {
+ if (saa_debug & DBGLVL_I2C)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+ buf, sizeof(buf), false);
+ memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen);
+ }
+
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+/* For a given 8 bit i2c address device, write the buffer */
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
+ u8 *data)
+{
+ struct saa7164_dev *dev = bus->dev;
+ u16 len = 0;
+ int unitid;
+ int reglen;
+ u8 buf[256];
+ int ret;
+
+ dprintk(DBGLVL_API, "%s()\n", __func__);
+
+ if ((datalen == 0) || (datalen > 232))
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ unitid = saa7164_i2caddr_to_unitid(bus, addr);
+ if (unitid < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr 0x%x to unitid\n",
+ __func__, addr);
+ return -EIO;
+ }
+
+ reglen = saa7164_i2caddr_to_reglen(bus, addr);
+ if (reglen < 0) {
+ printk(KERN_ERR
+ "%s() error, cannot translate regaddr to reglen\n",
+ __func__);
+ return -EIO;
+ }
+
+ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
+ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
+ if (ret != SAA_OK) {
+ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
+ return -EIO;
+ }
+
+ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
+
+ /* Prepare the send buffer */
+ /* Bytes 00-03 dest register length
+ * 04-07 dest bytes to write
+ * 08... register address
+ */
+ *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
+ *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen;
+ memcpy((buf + 2 * sizeof(u32)), data, datalen);
+
+ if (saa_debug & DBGLVL_I2C)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+ buf, sizeof(buf), false);
+
+ ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR,
+ EXU_REGISTER_ACCESS_CONTROL, len, &buf);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
+
+ return ret == SAA_OK ? 0 : -EIO;
+}
+
+int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
+ u8 pin, u8 state)
+{
+ int ret;
+ struct tmComResGPIO t;
+
+ dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
+ __func__, unitid, pin, state);
+
+ if ((pin > 7) || (state > 2))
+ return SAA_ERR_BAD_PARAMETER;
+
+ t.pin = pin;
+ t.state = state;
+
+ ret = saa7164_cmd_send(dev, unitid, SET_CUR,
+ EXU_GPIO_CONTROL, sizeof(t), &t);
+ if (ret != SAA_OK)
+ printk(KERN_ERR "%s() error, ret = 0x%x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid,
+ u8 pin)
+{
+ return saa7164_api_modify_gpio(dev, unitid, pin, 1);
+}
+
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid,
+ u8 pin)
+{
+ return saa7164_api_modify_gpio(dev, unitid, pin, 0);
+}
+
diff --git a/drivers/media/video/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
index 66696fa8341d..66696fa8341d 100644
--- a/drivers/media/video/saa7164/saa7164-buffer.c
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
diff --git a/drivers/media/video/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index a7f58a998752..a7f58a998752 100644
--- a/drivers/media/video/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
diff --git a/drivers/media/video/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c
index 5b72da5ce418..5b72da5ce418 100644
--- a/drivers/media/video/saa7164/saa7164-cards.c
+++ b/drivers/media/pci/saa7164/saa7164-cards.c
diff --git a/drivers/media/video/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index 62fac7f9d04e..62fac7f9d04e 100644
--- a/drivers/media/video/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
new file mode 100644
index 000000000000..2c9ad878bef3
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -0,0 +1,1488 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/div64.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+#include "saa7164.h"
+
+MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ * 1 Basic
+ * 2
+ * 4 i2c
+ * 8 api
+ * 16 cmd
+ * 32 bus
+ */
+
+unsigned int saa_debug;
+module_param_named(debug, saa_debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+unsigned int fw_debug;
+module_param(fw_debug, int, 0644);
+MODULE_PARM_DESC(fw_debug, "Firware debug level def:2");
+
+unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS;
+module_param(encoder_buffers, int, 0644);
+MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS;
+module_param(vbi_buffers, int, 0644);
+MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64");
+
+unsigned int waitsecs = 10;
+module_param(waitsecs, int, 0644);
+MODULE_PARM_DESC(waitsecs, "timeout on firmware messages");
+
+static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+unsigned int print_histogram = 64;
+module_param(print_histogram, int, 0644);
+MODULE_PARM_DESC(print_histogram, "print histogram values once");
+
+unsigned int crc_checking = 1;
+module_param(crc_checking, int, 0644);
+MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
+
+unsigned int guard_checking = 1;
+module_param(guard_checking, int, 0644);
+MODULE_PARM_DESC(guard_checking,
+ "enable dma sanity checking for buffer overruns");
+
+static unsigned int saa7164_devcount;
+
+static DEFINE_MUTEX(devlist);
+LIST_HEAD(saa7164_devlist);
+
+#define INT_SIZE 16
+
+static void saa7164_pack_verifier(struct saa7164_buffer *buf)
+{
+ u8 *p = (u8 *)buf->cpu;
+ int i;
+
+ for (i = 0; i < buf->actual_size; i += 2048) {
+
+ if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
+ (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) {
+ printk(KERN_ERR "No pack at 0x%x\n", i);
+#if 0
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+ p + 1, 32, false);
+#endif
+ }
+ }
+}
+
+#define FIXED_VIDEO_PID 0xf1
+#define FIXED_AUDIO_PID 0xf2
+
+static void saa7164_ts_verifier(struct saa7164_buffer *buf)
+{
+ struct saa7164_port *port = buf->port;
+ u32 i;
+ u8 cc, a;
+ u16 pid;
+ u8 __iomem *bufcpu = (u8 *)buf->cpu;
+
+ port->sync_errors = 0;
+ port->v_cc_errors = 0;
+ port->a_cc_errors = 0;
+
+ for (i = 0; i < buf->actual_size; i += 188) {
+ if (*(bufcpu + i) != 0x47)
+ port->sync_errors++;
+
+ /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
+ pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
+ cc = *(bufcpu + i + 3) & 0x0f;
+
+ if (pid == FIXED_VIDEO_PID) {
+ a = ((port->last_v_cc + 1) & 0x0f);
+ if (a != cc) {
+ printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
+ port->last_v_cc, cc, i);
+ port->v_cc_errors++;
+ }
+
+ port->last_v_cc = cc;
+ } else
+ if (pid == FIXED_AUDIO_PID) {
+ a = ((port->last_a_cc + 1) & 0x0f);
+ if (a != cc) {
+ printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
+ port->last_a_cc, cc, i);
+ port->a_cc_errors++;
+ }
+
+ port->last_a_cc = cc;
+ }
+
+ }
+
+ /* Only report errors if we've been through this function atleast
+ * once already and the cached cc values are primed. First time through
+ * always generates errors.
+ */
+ if (port->v_cc_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors);
+
+ if (port->a_cc_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors);
+
+ if (port->sync_errors && (port->done_first_interrupt > 1))
+ printk(KERN_ERR "sync_errors = %d\n", port->sync_errors);
+
+ if (port->done_first_interrupt == 1)
+ port->done_first_interrupt++;
+}
+
+static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
+{
+ int i;
+
+ memset(hg, 0, sizeof(struct saa7164_histogram));
+ strcpy(hg->name, name);
+
+ /* First 30ms x 1ms */
+ for (i = 0; i < 30; i++)
+ hg->counter1[0 + i].val = i;
+
+ /* 30 - 200ms x 10ms */
+ for (i = 0; i < 18; i++)
+ hg->counter1[30 + i].val = 30 + (i * 10);
+
+ /* 200 - 2000ms x 100ms */
+ for (i = 0; i < 15; i++)
+ hg->counter1[48 + i].val = 200 + (i * 200);
+
+ /* Catch all massive value (2secs) */
+ hg->counter1[55].val = 2000;
+
+ /* Catch all massive value (4secs) */
+ hg->counter1[56].val = 4000;
+
+ /* Catch all massive value (8secs) */
+ hg->counter1[57].val = 8000;
+
+ /* Catch all massive value (15secs) */
+ hg->counter1[58].val = 15000;
+
+ /* Catch all massive value (30secs) */
+ hg->counter1[59].val = 30000;
+
+ /* Catch all massive value (60secs) */
+ hg->counter1[60].val = 60000;
+
+ /* Catch all massive value (5mins) */
+ hg->counter1[61].val = 300000;
+
+ /* Catch all massive value (15mins) */
+ hg->counter1[62].val = 900000;
+
+ /* Catch all massive values (1hr) */
+ hg->counter1[63].val = 3600000;
+}
+
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
+{
+ int i;
+ for (i = 0; i < 64; i++) {
+ if (val <= hg->counter1[i].val) {
+ hg->counter1[i].count++;
+ hg->counter1[i].update_time = jiffies;
+ break;
+ }
+ }
+}
+
+static void saa7164_histogram_print(struct saa7164_port *port,
+ struct saa7164_histogram *hg)
+{
+ u32 entries = 0;
+ int i;
+
+ printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name);
+ for (i = 0; i < 64; i++) {
+ if (hg->counter1[i].count == 0)
+ continue;
+
+ printk(KERN_ERR " %4d %12d %Ld\n",
+ hg->counter1[i].val,
+ hg->counter1[i].count,
+ hg->counter1[i].update_time);
+
+ entries++;
+ }
+ printk(KERN_ERR "Total: %d\n", entries);
+}
+
+static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf = NULL;
+ struct saa7164_user_buffer *ubuf = NULL;
+ struct list_head *c, *n;
+ int i = 0;
+ u8 __iomem *p;
+
+ mutex_lock(&port->dmaqueue_lock);
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+
+ buf = list_entry(c, struct saa7164_buffer, list);
+ if (i++ > port->hwcfg.buffercount) {
+ printk(KERN_ERR "%s() illegal i count %d\n",
+ __func__, i);
+ break;
+ }
+
+ if (buf->idx == bufnr) {
+
+ /* Found the buffer, deal with it */
+ dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
+
+ if (crc_checking) {
+ /* Throw a new checksum on the dma buffer */
+ buf->crc = crc32(0, buf->cpu, buf->actual_size);
+ }
+
+ if (guard_checking) {
+ p = (u8 *)buf->cpu;
+ if ((*(p + buf->actual_size + 0) != 0xff) ||
+ (*(p + buf->actual_size + 1) != 0xff) ||
+ (*(p + buf->actual_size + 2) != 0xff) ||
+ (*(p + buf->actual_size + 3) != 0xff) ||
+ (*(p + buf->actual_size + 0x10) != 0xff) ||
+ (*(p + buf->actual_size + 0x11) != 0xff) ||
+ (*(p + buf->actual_size + 0x12) != 0xff) ||
+ (*(p + buf->actual_size + 0x13) != 0xff)) {
+ printk(KERN_ERR "%s() buf %p guard buffer breach\n",
+ __func__, buf);
+#if 0
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+ p + buf->actual_size - 32, 64, false);
+#endif
+ }
+ }
+
+ if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) {
+ /* Validate the incoming buffer content */
+ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+ saa7164_ts_verifier(buf);
+ else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
+ saa7164_pack_verifier(buf);
+ }
+
+ /* find a free user buffer and clone to it */
+ if (!list_empty(&port->list_buf_free.list)) {
+
+ /* Pull the first buffer from the used list */
+ ubuf = list_first_entry(&port->list_buf_free.list,
+ struct saa7164_user_buffer, list);
+
+ if (buf->actual_size <= ubuf->actual_size) {
+
+ memcpy_fromio(ubuf->data, buf->cpu,
+ ubuf->actual_size);
+
+ if (crc_checking) {
+ /* Throw a new checksum on the read buffer */
+ ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
+ }
+
+ /* Requeue the buffer on the free list */
+ ubuf->pos = 0;
+
+ list_move_tail(&ubuf->list,
+ &port->list_buf_used.list);
+
+ /* Flag any userland waiters */
+ wake_up_interruptible(&port->wait_read);
+
+ } else {
+ printk(KERN_ERR "buf %p bufsize fails match\n", buf);
+ }
+
+ } else
+ printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n");
+
+ /* Ensure offset into buffer remains 0, fill buffer
+ * with known bad data. We check for this data at a later point
+ * in time. */
+ saa7164_buffer_zero_offsets(port, bufnr);
+ memset_io(buf->cpu, 0xff, buf->pci_size);
+ if (crc_checking) {
+ /* Throw yet aanother new checksum on the dma buffer */
+ buf->crc = crc32(0, buf->cpu, buf->actual_size);
+ }
+
+ break;
+ }
+ }
+ mutex_unlock(&port->dmaqueue_lock);
+}
+
+static void saa7164_work_enchandler(struct work_struct *w)
+{
+ struct saa7164_port *port =
+ container_of(w, struct saa7164_port, workenc);
+ struct saa7164_dev *dev = port->dev;
+
+ u32 wp, mcb, rp, cnt = 0;
+
+ port->last_svc_msecs_diff = port->last_svc_msecs;
+ port->last_svc_msecs = jiffies_to_msecs(jiffies);
+
+ port->last_svc_msecs_diff = port->last_svc_msecs -
+ port->last_svc_msecs_diff;
+
+ saa7164_histogram_update(&port->svc_interval,
+ port->last_svc_msecs_diff);
+
+ port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+ port->last_irq_msecs;
+
+ saa7164_histogram_update(&port->irq_svc_interval,
+ port->last_irq_svc_msecs_diff);
+
+ dprintk(DBGLVL_IRQ,
+ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+ __func__,
+ port->last_svc_msecs_diff,
+ port->last_irq_svc_msecs_diff,
+ port->last_svc_wp,
+ port->last_svc_rp
+ );
+
+ /* Current write position */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1)) {
+ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+ return;
+ }
+
+ /* Most current complete buffer */
+ if (wp == 0)
+ mcb = (port->hwcfg.buffercount - 1);
+ else
+ mcb = wp - 1;
+
+ while (1) {
+ if (port->done_first_interrupt == 0) {
+ port->done_first_interrupt++;
+ rp = mcb;
+ } else
+ rp = (port->last_svc_rp + 1) % 8;
+
+ if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+ break;
+ }
+
+ saa7164_work_enchandler_helper(port, rp);
+ port->last_svc_rp = rp;
+ cnt++;
+
+ if (rp == mcb)
+ break;
+ }
+
+ /* TODO: Convert this into a /proc/saa7164 style readable file */
+ if (print_histogram == port->nr) {
+ saa7164_histogram_print(port, &port->irq_interval);
+ saa7164_histogram_print(port, &port->svc_interval);
+ saa7164_histogram_print(port, &port->irq_svc_interval);
+ saa7164_histogram_print(port, &port->read_interval);
+ saa7164_histogram_print(port, &port->poll_interval);
+ /* TODO: fix this to preserve any previous state */
+ print_histogram = 64 + port->nr;
+ }
+}
+
+static void saa7164_work_vbihandler(struct work_struct *w)
+{
+ struct saa7164_port *port =
+ container_of(w, struct saa7164_port, workenc);
+ struct saa7164_dev *dev = port->dev;
+
+ u32 wp, mcb, rp, cnt = 0;
+
+ port->last_svc_msecs_diff = port->last_svc_msecs;
+ port->last_svc_msecs = jiffies_to_msecs(jiffies);
+ port->last_svc_msecs_diff = port->last_svc_msecs -
+ port->last_svc_msecs_diff;
+
+ saa7164_histogram_update(&port->svc_interval,
+ port->last_svc_msecs_diff);
+
+ port->last_irq_svc_msecs_diff = port->last_svc_msecs -
+ port->last_irq_msecs;
+
+ saa7164_histogram_update(&port->irq_svc_interval,
+ port->last_irq_svc_msecs_diff);
+
+ dprintk(DBGLVL_IRQ,
+ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
+ __func__,
+ port->last_svc_msecs_diff,
+ port->last_irq_svc_msecs_diff,
+ port->last_svc_wp,
+ port->last_svc_rp
+ );
+
+ /* Current write position */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1)) {
+ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
+ return;
+ }
+
+ /* Most current complete buffer */
+ if (wp == 0)
+ mcb = (port->hwcfg.buffercount - 1);
+ else
+ mcb = wp - 1;
+
+ while (1) {
+ if (port->done_first_interrupt == 0) {
+ port->done_first_interrupt++;
+ rp = mcb;
+ } else
+ rp = (port->last_svc_rp + 1) % 8;
+
+ if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
+ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
+ break;
+ }
+
+ saa7164_work_enchandler_helper(port, rp);
+ port->last_svc_rp = rp;
+ cnt++;
+
+ if (rp == mcb)
+ break;
+ }
+
+ /* TODO: Convert this into a /proc/saa7164 style readable file */
+ if (print_histogram == port->nr) {
+ saa7164_histogram_print(port, &port->irq_interval);
+ saa7164_histogram_print(port, &port->svc_interval);
+ saa7164_histogram_print(port, &port->irq_svc_interval);
+ saa7164_histogram_print(port, &port->read_interval);
+ saa7164_histogram_print(port, &port->poll_interval);
+ /* TODO: fix this to preserve any previous state */
+ print_histogram = 64 + port->nr;
+ }
+}
+
+static void saa7164_work_cmdhandler(struct work_struct *w)
+{
+ struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
+
+ /* Wake up any complete commands */
+ saa7164_irq_dequeue(dev);
+}
+
+static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
+{
+ struct saa7164_port *port = buf->port;
+
+ /* Feed the transport payload into the kernel demux */
+ dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
+ SAA7164_TS_NUMBER_OF_LINES);
+
+}
+
+static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ /* Store old time */
+ port->last_irq_msecs_diff = port->last_irq_msecs;
+
+ /* Collect new stats */
+ port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+ /* Calculate stats */
+ port->last_irq_msecs_diff = port->last_irq_msecs -
+ port->last_irq_msecs_diff;
+
+ saa7164_histogram_update(&port->irq_interval,
+ port->last_irq_msecs_diff);
+
+ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+ port->last_irq_msecs_diff);
+
+ /* Tis calls the vbi irq handler */
+ schedule_work(&port->workenc);
+ return 0;
+}
+
+static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+
+ /* Store old time */
+ port->last_irq_msecs_diff = port->last_irq_msecs;
+
+ /* Collect new stats */
+ port->last_irq_msecs = jiffies_to_msecs(jiffies);
+
+ /* Calculate stats */
+ port->last_irq_msecs_diff = port->last_irq_msecs -
+ port->last_irq_msecs_diff;
+
+ saa7164_histogram_update(&port->irq_interval,
+ port->last_irq_msecs_diff);
+
+ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
+ port->last_irq_msecs_diff);
+
+ schedule_work(&port->workenc);
+ return 0;
+}
+
+static irqreturn_t saa7164_irq_ts(struct saa7164_port *port)
+{
+ struct saa7164_dev *dev = port->dev;
+ struct saa7164_buffer *buf;
+ struct list_head *c, *n;
+ int wp, i = 0, rp;
+
+ /* Find the current write point from the hardware */
+ wp = saa7164_readl(port->bufcounter);
+ if (wp > (port->hwcfg.buffercount - 1))
+ BUG();
+
+ /* Find the previous buffer to the current write point */
+ if (wp == 0)
+ rp = (port->hwcfg.buffercount - 1);
+ else
+ rp = wp - 1;
+
+ /* Lookup the WP in the buffer list */
+ /* TODO: turn this into a worker thread */
+ list_for_each_safe(c, n, &port->dmaqueue.list) {
+ buf = list_entry(c, struct saa7164_buffer, list);
+ if (i++ > port->hwcfg.buffercount)
+ BUG();
+
+ if (buf->idx == rp) {
+ /* Found the buffer, deal with it */
+ dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
+ __func__, wp, rp);
+ saa7164_buffer_deliver(buf);
+ break;
+ }
+
+ }
+ return 0;
+}
+
+/* Primary IRQ handler and dispatch mechanism */
+static irqreturn_t saa7164_irq(int irq, void *dev_id)
+{
+ struct saa7164_dev *dev = dev_id;
+ struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1];
+ struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2];
+ struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1];
+ struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2];
+ struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1];
+ struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2];
+
+ u32 intid, intstat[INT_SIZE/4];
+ int i, handled = 0, bit;
+
+ if (dev == NULL) {
+ printk(KERN_ERR "%s() No device specified\n", __func__);
+ handled = 0;
+ goto out;
+ }
+
+ /* Check that the hardware is accessible. If the status bytes are
+ * 0xFF then the device is not accessible, the the IRQ belongs
+ * to another driver.
+ * 4 x u32 interrupt registers.
+ */
+ for (i = 0; i < INT_SIZE/4; i++) {
+
+ /* TODO: Convert into saa7164_readl() */
+ /* Read the 4 hardware interrupt registers */
+ intstat[i] = saa7164_readl(dev->int_status + (i * 4));
+
+ if (intstat[i])
+ handled = 1;
+ }
+ if (handled == 0)
+ goto out;
+
+ /* For each of the HW interrupt registers */
+ for (i = 0; i < INT_SIZE/4; i++) {
+
+ if (intstat[i]) {
+ /* Each function of the board has it's own interruptid.
+ * Find the function that triggered then call
+ * it's handler.
+ */
+ for (bit = 0; bit < 32; bit++) {
+
+ if (((intstat[i] >> bit) & 0x00000001) == 0)
+ continue;
+
+ /* Calculate the interrupt id (0x00 to 0x7f) */
+
+ intid = (i * 32) + bit;
+ if (intid == dev->intfdesc.bInterruptId) {
+ /* A response to an cmd/api call */
+ schedule_work(&dev->workcmd);
+ } else if (intid == porta->hwcfg.interruptid) {
+
+ /* Transport path 1 */
+ saa7164_irq_ts(porta);
+
+ } else if (intid == portb->hwcfg.interruptid) {
+
+ /* Transport path 2 */
+ saa7164_irq_ts(portb);
+
+ } else if (intid == portc->hwcfg.interruptid) {
+
+ /* Encoder path 1 */
+ saa7164_irq_encoder(portc);
+
+ } else if (intid == portd->hwcfg.interruptid) {
+
+ /* Encoder path 2 */
+ saa7164_irq_encoder(portd);
+
+ } else if (intid == porte->hwcfg.interruptid) {
+
+ /* VBI path 1 */
+ saa7164_irq_vbi(porte);
+
+ } else if (intid == portf->hwcfg.interruptid) {
+
+ /* VBI path 2 */
+ saa7164_irq_vbi(portf);
+
+ } else {
+ /* Find the function */
+ dprintk(DBGLVL_IRQ,
+ "%s() unhandled interrupt "
+ "reg 0x%x bit 0x%x "
+ "intid = 0x%x\n",
+ __func__, i, bit, intid);
+ }
+ }
+
+ /* Ack it */
+ saa7164_writel(dev->int_ack + (i * 4), intstat[i]);
+
+ }
+ }
+out:
+ return IRQ_RETVAL(handled);
+}
+
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev)
+{
+ struct saa7164_fw_status *s = &dev->fw_status;
+
+ dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS);
+ dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE);
+ dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC);
+ dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST);
+ dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD);
+ dev->fw_status.remainheap =
+ saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP);
+
+ dprintk(1, "Firmware status:\n");
+ dprintk(1, " .status = 0x%08x\n", s->status);
+ dprintk(1, " .mode = 0x%08x\n", s->mode);
+ dprintk(1, " .spec = 0x%08x\n", s->spec);
+ dprintk(1, " .inst = 0x%08x\n", s->inst);
+ dprintk(1, " .cpuload = 0x%08x\n", s->cpuload);
+ dprintk(1, " .remainheap = 0x%08x\n", s->remainheap);
+}
+
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev)
+{
+ u32 reg;
+
+ reg = saa7164_readl(SAA_DEVICE_VERSION);
+ dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n",
+ (reg & 0x0000fc00) >> 10,
+ (reg & 0x000003e0) >> 5,
+ (reg & 0x0000001f),
+ (reg & 0xffff0000) >> 16,
+ reg);
+
+ return reg;
+}
+
+/* TODO: Debugging func, remove */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr)
+{
+ int i;
+
+ dprintk(1, "--------------------> "
+ "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+
+ for (i = 0; i < 0x100; i += 16)
+ dprintk(1, "region0[0x%08x] = "
+ "%02x %02x %02x %02x %02x %02x %02x %02x"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n", i,
+ (u8)saa7164_readb(addr + i + 0),
+ (u8)saa7164_readb(addr + i + 1),
+ (u8)saa7164_readb(addr + i + 2),
+ (u8)saa7164_readb(addr + i + 3),
+ (u8)saa7164_readb(addr + i + 4),
+ (u8)saa7164_readb(addr + i + 5),
+ (u8)saa7164_readb(addr + i + 6),
+ (u8)saa7164_readb(addr + i + 7),
+ (u8)saa7164_readb(addr + i + 8),
+ (u8)saa7164_readb(addr + i + 9),
+ (u8)saa7164_readb(addr + i + 10),
+ (u8)saa7164_readb(addr + i + 11),
+ (u8)saa7164_readb(addr + i + 12),
+ (u8)saa7164_readb(addr + i + 13),
+ (u8)saa7164_readb(addr + i + 14),
+ (u8)saa7164_readb(addr + i + 15)
+ );
+}
+
+static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
+ &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr));
+
+ dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength);
+ dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType);
+ dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+ dev->hwdesc.bDescriptorSubtype);
+
+ dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion);
+ dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency);
+ dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes);
+ dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities);
+ dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n",
+ dev->hwdesc.dwDeviceRegistersLocation);
+
+ dprintk(1, " .dwHostMemoryRegion = 0x%x\n",
+ dev->hwdesc.dwHostMemoryRegion);
+
+ dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n",
+ dev->hwdesc.dwHostMemoryRegionSize);
+
+ dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n",
+ dev->hwdesc.dwHostHibernatMemRegion);
+
+ dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n",
+ dev->hwdesc.dwHostHibernatMemRegionSize);
+}
+
+static void saa7164_dump_intfdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p intfdesc "
+ "sizeof(struct tmComResInterfaceDescr) = %d bytes\n",
+ &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr));
+
+ dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength);
+ dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType);
+ dprintk(1, " .bDescriptorSubtype = 0x%x\n",
+ dev->intfdesc.bDescriptorSubtype);
+
+ dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags);
+ dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType);
+ dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId);
+ dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface);
+ dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId);
+ dprintk(1, " .bDebugInterruptId = 0x%x\n",
+ dev->intfdesc.bDebugInterruptId);
+
+ dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation);
+}
+
+static void saa7164_dump_busdesc(struct saa7164_dev *dev)
+{
+ dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n",
+ &dev->busdesc, (u32)sizeof(struct tmComResBusDescr));
+
+ dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing);
+ dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing);
+ dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite);
+ dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead);
+ dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite);
+ dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead);
+}
+
+/* Much of the hardware configuration and PCI registers are configured
+ * dynamically depending on firmware. We have to cache some initial
+ * structures then use these to locate other important structures
+ * from PCI space.
+ */
+static void saa7164_get_descriptors(struct saa7164_dev *dev)
+{
+ memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr));
+ memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr),
+ sizeof(struct tmComResInterfaceDescr));
+ memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
+ sizeof(struct tmComResBusDescr));
+
+ if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) {
+ printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n");
+ printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength,
+ (u32)sizeof(struct tmComResHWDescr));
+ } else
+ saa7164_dump_hwdesc(dev);
+
+ if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
+ printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
+ printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
+ (u32)sizeof(struct tmComResInterfaceDescr));
+ } else
+ saa7164_dump_intfdesc(dev);
+
+ saa7164_dump_busdesc(dev);
+}
+
+static int saa7164_pci_quirks(struct saa7164_dev *dev)
+{
+ return 0;
+}
+
+static int get_resources(struct saa7164_dev *dev)
+{
+ if (request_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0), dev->name)) {
+
+ if (request_mem_region(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2), dev->name))
+ return 0;
+ }
+
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
+ dev->name,
+ (u64)pci_resource_start(dev->pci, 0),
+ (u64)pci_resource_start(dev->pci, 2));
+
+ return -EBUSY;
+}
+
+static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
+{
+ struct saa7164_port *port = NULL;
+
+ if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
+ BUG();
+
+ port = &dev->ports[portnr];
+
+ port->dev = dev;
+ port->nr = portnr;
+
+ if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2))
+ port->type = SAA7164_MPEG_DVB;
+ else
+ if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) {
+ port->type = SAA7164_MPEG_ENCODER;
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&port->workenc, saa7164_work_enchandler);
+ } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) {
+ port->type = SAA7164_MPEG_VBI;
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&port->workenc, saa7164_work_vbihandler);
+ } else
+ BUG();
+
+ /* Init all the critical resources */
+ mutex_init(&port->dvb.lock);
+ INIT_LIST_HEAD(&port->dmaqueue.list);
+ mutex_init(&port->dmaqueue_lock);
+
+ INIT_LIST_HEAD(&port->list_buf_used.list);
+ INIT_LIST_HEAD(&port->list_buf_free.list);
+ init_waitqueue_head(&port->wait_read);
+
+
+ saa7164_histogram_reset(&port->irq_interval, "irq intervals");
+ saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
+ saa7164_histogram_reset(&port->irq_svc_interval,
+ "irq to deferred intervals");
+ saa7164_histogram_reset(&port->read_interval,
+ "encoder/vbi read() intervals");
+ saa7164_histogram_reset(&port->poll_interval,
+ "encoder/vbi poll() intervals");
+
+ return 0;
+}
+
+static int saa7164_dev_setup(struct saa7164_dev *dev)
+{
+ int i;
+
+ mutex_init(&dev->lock);
+ atomic_inc(&dev->refcount);
+ dev->nr = saa7164_devcount++;
+
+ snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
+
+ mutex_lock(&devlist);
+ list_add_tail(&dev->devlist, &saa7164_devlist);
+ mutex_unlock(&devlist);
+
+ /* board config */
+ dev->board = UNSET;
+ if (card[dev->nr] < saa7164_bcount)
+ dev->board = card[dev->nr];
+
+ for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++)
+ if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor &&
+ dev->pci->subsystem_device ==
+ saa7164_subids[i].subdevice)
+ dev->board = saa7164_subids[i].card;
+
+ if (UNSET == dev->board) {
+ dev->board = SAA7164_BOARD_UNKNOWN;
+ saa7164_card_list(dev);
+ }
+
+ dev->pci_bus = dev->pci->bus->number;
+ dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+
+ /* I2C Defaults / setup */
+ dev->i2c_bus[0].dev = dev;
+ dev->i2c_bus[0].nr = 0;
+ dev->i2c_bus[1].dev = dev;
+ dev->i2c_bus[1].nr = 1;
+ dev->i2c_bus[2].dev = dev;
+ dev->i2c_bus[2].nr = 2;
+
+ /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */
+ saa7164_port_init(dev, SAA7164_PORT_TS1);
+ saa7164_port_init(dev, SAA7164_PORT_TS2);
+ saa7164_port_init(dev, SAA7164_PORT_ENC1);
+ saa7164_port_init(dev, SAA7164_PORT_ENC2);
+ saa7164_port_init(dev, SAA7164_PORT_VBI1);
+ saa7164_port_init(dev, SAA7164_PORT_VBI2);
+
+ if (get_resources(dev) < 0) {
+ printk(KERN_ERR "CORE %s No more PCIe resources for "
+ "subsystem: %04x:%04x\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device);
+
+ saa7164_devcount--;
+ return -ENODEV;
+ }
+
+ /* PCI/e allocations */
+ dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2));
+
+ dev->bmmio = (u8 __iomem *)dev->lmmio;
+ dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
+
+ /* Inerrupt and ack register locations offset of bmmio */
+ dev->int_status = 0x183000 + 0xf80;
+ dev->int_ack = 0x183000 + 0xf90;
+
+ printk(KERN_INFO
+ "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
+ dev->name, dev->pci->subsystem_vendor,
+ dev->pci->subsystem_device, saa7164_boards[dev->board].name,
+ dev->board, card[dev->nr] == dev->board ?
+ "insmod option" : "autodetected");
+
+ saa7164_pci_quirks(dev);
+
+ return 0;
+}
+
+static void saa7164_dev_unregister(struct saa7164_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+
+ release_mem_region(pci_resource_start(dev->pci, 2),
+ pci_resource_len(dev->pci, 2));
+
+ if (!atomic_dec_and_test(&dev->refcount))
+ return;
+
+ iounmap(dev->lmmio);
+ iounmap(dev->lmmio2);
+
+ return;
+}
+
+#ifdef CONFIG_PROC_FS
+static int saa7164_proc_show(struct seq_file *m, void *v)
+{
+ struct saa7164_dev *dev;
+ struct tmComResBusInfo *b;
+ struct list_head *list;
+ int i, c;
+
+ if (saa7164_devcount == 0)
+ return 0;
+
+ list_for_each(list, &saa7164_devlist) {
+ dev = list_entry(list, struct saa7164_dev, devlist);
+ seq_printf(m, "%s = %p\n", dev->name, dev);
+
+ /* Lock the bus from any other access */
+ b = &dev->bus;
+ mutex_lock(&b->lock);
+
+ seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
+
+ seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
+
+ seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n",
+ b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
+
+ seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n",
+ b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
+ c = 0;
+ seq_printf(m, "\n Set Ring:\n");
+ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+ for (i = 0; i < b->m_dwSizeSetRing; i++) {
+ if (c == 0)
+ seq_printf(m, " %04x:", i);
+
+ seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
+
+ if (++c == 16) {
+ seq_printf(m, "\n");
+ c = 0;
+ }
+ }
+
+ c = 0;
+ seq_printf(m, "\n Get Ring:\n");
+ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
+ for (i = 0; i < b->m_dwSizeGetRing; i++) {
+ if (c == 0)
+ seq_printf(m, " %04x:", i);
+
+ seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
+
+ if (++c == 16) {
+ seq_printf(m, "\n");
+ c = 0;
+ }
+ }
+
+ mutex_unlock(&b->lock);
+
+ }
+
+ return 0;
+}
+
+static int saa7164_proc_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, saa7164_proc_show, NULL);
+}
+
+static const struct file_operations saa7164_proc_fops = {
+ .open = saa7164_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int saa7164_proc_create(void)
+{
+ struct proc_dir_entry *pe;
+
+ pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops);
+ if (!pe)
+ return -ENOMEM;
+
+ return 0;
+}
+#endif
+
+static int saa7164_thread_function(void *data)
+{
+ struct saa7164_dev *dev = data;
+ struct tmFwInfoStruct fwinfo;
+ u64 last_poll_time = 0;
+
+ dprintk(DBGLVL_THR, "thread started\n");
+
+ set_freezable();
+
+ while (1) {
+ msleep_interruptible(100);
+ if (kthread_should_stop())
+ break;
+ try_to_freeze();
+
+ dprintk(DBGLVL_THR, "thread running\n");
+
+ /* Dump the firmware debug message to console */
+ /* Polling this costs us 1-2% of the arm CPU */
+ /* convert this into a respnde to interrupt 0x7a */
+ saa7164_api_collect_debug(dev);
+
+ /* Monitor CPU load every 1 second */
+ if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) {
+ saa7164_api_get_load_info(dev, &fwinfo);
+ last_poll_time = jiffies_to_msecs(jiffies);
+ }
+
+ }
+
+ dprintk(DBGLVL_THR, "thread exiting\n");
+ return 0;
+}
+
+static int __devinit saa7164_initdev(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct saa7164_dev *dev;
+ int err, i;
+ u32 version;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (NULL == dev)
+ return -ENOMEM;
+
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+ goto fail_free;
+ }
+
+ if (saa7164_dev_setup(dev) < 0) {
+ err = -EINVAL;
+ goto fail_free;
+ }
+
+ /* print pci info */
+ dev->pci_rev = pci_dev->revision;
+ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%llx\n", dev->name,
+ pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
+ dev->pci_lat,
+ (unsigned long long)pci_resource_start(pci_dev, 0));
+
+ pci_set_master(pci_dev);
+ /* TODO */
+ if (!pci_dma_supported(pci_dev, 0xffffffff)) {
+ printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ err = request_irq(pci_dev->irq, saa7164_irq,
+ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name,
+ pci_dev->irq);
+ err = -EIO;
+ goto fail_irq;
+ }
+
+ pci_set_drvdata(pci_dev, dev);
+
+ /* Init the internal command list */
+ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
+ dev->cmds[i].seqno = i;
+ dev->cmds[i].inuse = 0;
+ mutex_init(&dev->cmds[i].lock);
+ init_waitqueue_head(&dev->cmds[i].wait);
+ }
+
+ /* We need a deferred interrupt handler for cmd handling */
+ INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler);
+
+ /* Only load the firmware if we know the board */
+ if (dev->board != SAA7164_BOARD_UNKNOWN) {
+
+ err = saa7164_downloadfirmware(dev);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Failed to boot firmware, no features "
+ "registered\n");
+ goto fail_fw;
+ }
+
+ saa7164_get_descriptors(dev);
+ saa7164_dumpregs(dev, 0);
+ saa7164_getcurrentfirmwareversion(dev);
+ saa7164_getfirmwarestatus(dev);
+ err = saa7164_bus_setup(dev);
+ if (err < 0)
+ printk(KERN_ERR
+ "Failed to setup the bus, will continue\n");
+ saa7164_bus_dump(dev);
+
+ /* Ping the running firmware via the command bus and get the
+ * firmware version, this checks the bus is running OK.
+ */
+ version = 0;
+ if (saa7164_api_get_fw_version(dev, &version) == SAA_OK)
+ dprintk(1, "Bus is operating correctly using "
+ "version %d.%d.%d.%d (0x%x)\n",
+ (version & 0x0000fc00) >> 10,
+ (version & 0x000003e0) >> 5,
+ (version & 0x0000001f),
+ (version & 0xffff0000) >> 16,
+ version);
+ else
+ printk(KERN_ERR
+ "Failed to communicate with the firmware\n");
+
+ /* Bring up the I2C buses */
+ saa7164_i2c_register(&dev->i2c_bus[0]);
+ saa7164_i2c_register(&dev->i2c_bus[1]);
+ saa7164_i2c_register(&dev->i2c_bus[2]);
+ saa7164_gpio_setup(dev);
+ saa7164_card_setup(dev);
+
+ /* Parse the dynamic device configuration, find various
+ * media endpoints (MPEG, WMV, PS, TS) and cache their
+ * configuration details into the driver, so we can
+ * reference them later during simething_register() func,
+ * interrupt handlers, deferred work handlers etc.
+ */
+ saa7164_api_enum_subdevs(dev);
+
+ /* Begin to create the video sub-systems and register funcs */
+ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) {
+ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) {
+ printk(KERN_ERR "%s() Failed to register "
+ "dvb adapters on porta\n",
+ __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) {
+ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "dvb adapters on portb\n",
+ __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) {
+ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "mpeg encoder\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) {
+ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "mpeg encoder\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) {
+ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "vbi device\n", __func__);
+ }
+ }
+
+ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) {
+ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) {
+ printk(KERN_ERR"%s() Failed to register "
+ "vbi device\n", __func__);
+ }
+ }
+ saa7164_api_set_debug(dev, fw_debug);
+
+ if (fw_debug) {
+ dev->kthread = kthread_run(saa7164_thread_function, dev,
+ "saa7164 debug");
+ if (!dev->kthread)
+ printk(KERN_ERR "%s() Failed to create "
+ "debug kernel thread\n", __func__);
+ }
+
+ } /* != BOARD_UNKNOWN */
+ else
+ printk(KERN_ERR "%s() Unsupported board detected, "
+ "registering without firmware\n", __func__);
+
+ dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug);
+ dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs);
+
+fail_fw:
+ return 0;
+
+fail_irq:
+ saa7164_dev_unregister(dev);
+fail_free:
+ kfree(dev);
+ return err;
+}
+
+static void saa7164_shutdown(struct saa7164_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+}
+
+static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
+{
+ struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
+
+ if (dev->board != SAA7164_BOARD_UNKNOWN) {
+ if (fw_debug && dev->kthread) {
+ kthread_stop(dev->kthread);
+ dev->kthread = NULL;
+ }
+ if (dev->firmwareloaded)
+ saa7164_api_set_debug(dev, 0x00);
+ }
+
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].irq_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].svc_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].read_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
+ &dev->ports[SAA7164_PORT_ENC1].poll_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1],
+ &dev->ports[SAA7164_PORT_VBI1].read_interval);
+ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2],
+ &dev->ports[SAA7164_PORT_VBI2].poll_interval);
+
+ saa7164_shutdown(dev);
+
+ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
+ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]);
+
+ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB)
+ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]);
+
+ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER)
+ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]);
+
+ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER)
+ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]);
+
+ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI)
+ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]);
+
+ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI)
+ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]);
+
+ saa7164_i2c_unregister(&dev->i2c_bus[0]);
+ saa7164_i2c_unregister(&dev->i2c_bus[1]);
+ saa7164_i2c_unregister(&dev->i2c_bus[2]);
+
+ pci_disable_device(pci_dev);
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, dev);
+ pci_set_drvdata(pci_dev, NULL);
+
+ mutex_lock(&devlist);
+ list_del(&dev->devlist);
+ mutex_unlock(&devlist);
+
+ saa7164_dev_unregister(dev);
+ kfree(dev);
+}
+
+static struct pci_device_id saa7164_pci_tbl[] = {
+ {
+ /* SAA7164 */
+ .vendor = 0x1131,
+ .device = 0x7164,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ }, {
+ /* --- end of list --- */
+ }
+};
+MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl);
+
+static struct pci_driver saa7164_pci_driver = {
+ .name = "saa7164",
+ .id_table = saa7164_pci_tbl,
+ .probe = saa7164_initdev,
+ .remove = __devexit_p(saa7164_finidev),
+ /* TODO */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init saa7164_init(void)
+{
+ printk(KERN_INFO "saa7164 driver loaded\n");
+
+#ifdef CONFIG_PROC_FS
+ saa7164_proc_create();
+#endif
+ return pci_register_driver(&saa7164_pci_driver);
+}
+
+static void __exit saa7164_fini(void)
+{
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("saa7164", NULL);
+#endif
+ pci_unregister_driver(&saa7164_pci_driver);
+}
+
+module_init(saa7164_init);
+module_exit(saa7164_fini);
+
diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c
index 5c5cc3ebf9bd..5c5cc3ebf9bd 100644
--- a/drivers/media/video/saa7164/saa7164-dvb.c
+++ b/drivers/media/pci/saa7164/saa7164-dvb.c
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c
index a9ed686ad08a..a9ed686ad08a 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/pci/saa7164/saa7164-encoder.c
diff --git a/drivers/media/video/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c
index a266bf0169e6..a266bf0169e6 100644
--- a/drivers/media/video/saa7164/saa7164-fw.c
+++ b/drivers/media/pci/saa7164/saa7164-fw.c
diff --git a/drivers/media/video/saa7164/saa7164-i2c.c b/drivers/media/pci/saa7164/saa7164-i2c.c
index 4f7e3b42263f..4f7e3b42263f 100644
--- a/drivers/media/video/saa7164/saa7164-i2c.c
+++ b/drivers/media/pci/saa7164/saa7164-i2c.c
diff --git a/drivers/media/video/saa7164/saa7164-reg.h b/drivers/media/pci/saa7164/saa7164-reg.h
index 2bbf81583d33..2bbf81583d33 100644
--- a/drivers/media/video/saa7164/saa7164-reg.h
+++ b/drivers/media/pci/saa7164/saa7164-reg.h
diff --git a/drivers/media/video/saa7164/saa7164-types.h b/drivers/media/pci/saa7164/saa7164-types.h
index 1d2140a3eb38..1d2140a3eb38 100644
--- a/drivers/media/video/saa7164/saa7164-types.h
+++ b/drivers/media/pci/saa7164/saa7164-types.h
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c
index d8e6c8f14079..d8e6c8f14079 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/pci/saa7164/saa7164-vbi.c
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
new file mode 100644
index 000000000000..437284e747c9
--- /dev/null
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -0,0 +1,616 @@
+/*
+ * Driver for the NXP SAA7164 PCIe bridge
+ *
+ * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ Driver architecture
+ *******************
+
+ saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c
+ | : Standard Linux driver framework for creating
+ | : exposing and managing interfaces to the rest
+ | : of the kernel or userland. Also uses _fw.c to load
+ | : firmware direct into the PCIe bus, bypassing layers.
+ V
+ saa7164_api..() : Translate kernel specific functions/features
+ | : into command buffers.
+ V
+ saa7164_cmd..() : Manages the flow of command packets on/off,
+ | : the bus. Deal with bus errors, timeouts etc.
+ V
+ saa7164_bus..() : Manage a read/write memory ring buffer in the
+ | : PCIe Address space.
+ |
+ | saa7164_fw...() : Load any frimware
+ | | : direct into the device
+ V V
+ <- ----------------- PCIe address space -------------------- ->
+*/
+
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/kdev_t.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+#include <media/tuner.h>
+#include <media/tveeprom.h>
+#include <media/videobuf-dma-sg.h>
+#include <media/videobuf-dvb.h>
+#include <dvb_demux.h>
+#include <dvb_frontend.h>
+#include <dvb_net.h>
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "saa7164-reg.h"
+#include "saa7164-types.h"
+
+#define SAA7164_MAXBOARDS 8
+
+#define UNSET (-1U)
+#define SAA7164_BOARD_NOAUTO UNSET
+#define SAA7164_BOARD_UNKNOWN 0
+#define SAA7164_BOARD_UNKNOWN_REV2 1
+#define SAA7164_BOARD_UNKNOWN_REV3 2
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250 3
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200 4
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7
+#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9
+#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10
+
+#define SAA7164_MAX_UNITS 8
+#define SAA7164_TS_NUMBER_OF_LINES 312
+#define SAA7164_PS_NUMBER_OF_LINES 256
+#define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */
+#define SAA7164_MAX_ENCODER_BUFFERS 64 /* max 5secs of latency at 6Mbps */
+#define SAA7164_MAX_VBI_BUFFERS 64
+
+/* Port related defines */
+#define SAA7164_PORT_TS1 (0)
+#define SAA7164_PORT_TS2 (SAA7164_PORT_TS1 + 1)
+#define SAA7164_PORT_ENC1 (SAA7164_PORT_TS2 + 1)
+#define SAA7164_PORT_ENC2 (SAA7164_PORT_ENC1 + 1)
+#define SAA7164_PORT_VBI1 (SAA7164_PORT_ENC2 + 1)
+#define SAA7164_PORT_VBI2 (SAA7164_PORT_VBI1 + 1)
+#define SAA7164_MAX_PORTS (SAA7164_PORT_VBI2 + 1)
+
+#define DBGLVL_FW 4
+#define DBGLVL_DVB 8
+#define DBGLVL_I2C 16
+#define DBGLVL_API 32
+#define DBGLVL_CMD 64
+#define DBGLVL_BUS 128
+#define DBGLVL_IRQ 256
+#define DBGLVL_BUF 512
+#define DBGLVL_ENC 1024
+#define DBGLVL_VBI 2048
+#define DBGLVL_THR 4096
+#define DBGLVL_CPU 8192
+
+#define SAA7164_NORMS \
+ (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443)
+
+enum port_t {
+ SAA7164_MPEG_UNDEFINED = 0,
+ SAA7164_MPEG_DVB,
+ SAA7164_MPEG_ENCODER,
+ SAA7164_MPEG_VBI,
+};
+
+enum saa7164_i2c_bus_nr {
+ SAA7164_I2C_BUS_0 = 0,
+ SAA7164_I2C_BUS_1,
+ SAA7164_I2C_BUS_2,
+};
+
+enum saa7164_buffer_flags {
+ SAA7164_BUFFER_UNDEFINED = 0,
+ SAA7164_BUFFER_FREE,
+ SAA7164_BUFFER_BUSY,
+ SAA7164_BUFFER_FULL
+};
+
+enum saa7164_unit_type {
+ SAA7164_UNIT_UNDEFINED = 0,
+ SAA7164_UNIT_DIGITAL_DEMODULATOR,
+ SAA7164_UNIT_ANALOG_DEMODULATOR,
+ SAA7164_UNIT_TUNER,
+ SAA7164_UNIT_EEPROM,
+ SAA7164_UNIT_ZILOG_IRBLASTER,
+ SAA7164_UNIT_ENCODER,
+};
+
+/* The PCIe bridge doesn't grant direct access to i2c.
+ * Instead, you address i2c devices using a uniqely
+ * allocated 'unitid' value via a messaging API. This
+ * is a problem. The kernel and existing demod/tuner
+ * drivers expect to talk 'i2c', so we have to maintain
+ * a translation layer, and a series of functions to
+ * convert i2c bus + device address into a unit id.
+ */
+struct saa7164_unit {
+ enum saa7164_unit_type type;
+ u8 id;
+ char *name;
+ enum saa7164_i2c_bus_nr i2c_bus_nr;
+ u8 i2c_bus_addr;
+ u8 i2c_reg_len;
+};
+
+struct saa7164_board {
+ char *name;
+ enum port_t porta, portb, portc,
+ portd, porte, portf;
+ enum {
+ SAA7164_CHIP_UNDEFINED = 0,
+ SAA7164_CHIP_REV2,
+ SAA7164_CHIP_REV3,
+ } chiprev;
+ struct saa7164_unit unit[SAA7164_MAX_UNITS];
+};
+
+struct saa7164_subid {
+ u16 subvendor;
+ u16 subdevice;
+ u32 card;
+};
+
+struct saa7164_encoder_fh {
+ struct saa7164_port *port;
+ atomic_t v4l_reading;
+};
+
+struct saa7164_vbi_fh {
+ struct saa7164_port *port;
+ atomic_t v4l_reading;
+};
+
+struct saa7164_histogram_bucket {
+ u32 val;
+ u32 count;
+ u64 update_time;
+};
+
+struct saa7164_histogram {
+ char name[32];
+ struct saa7164_histogram_bucket counter1[64];
+};
+
+struct saa7164_user_buffer {
+ struct list_head list;
+
+ /* Attributes */
+ u8 *data;
+ u32 pos;
+ u32 actual_size;
+
+ u32 crc;
+};
+
+struct saa7164_fw_status {
+
+ /* RISC Core details */
+ u32 status;
+ u32 mode;
+ u32 spec;
+ u32 inst;
+ u32 cpuload;
+ u32 remainheap;
+
+ /* Firmware version */
+ u32 version;
+ u32 major;
+ u32 sub;
+ u32 rel;
+ u32 buildnr;
+};
+
+struct saa7164_dvb {
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct dvb_frontend *frontend;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+ int feeding;
+};
+
+struct saa7164_i2c {
+ struct saa7164_dev *dev;
+
+ enum saa7164_i2c_bus_nr nr;
+
+ /* I2C I/O */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+};
+
+struct saa7164_ctrl {
+ struct v4l2_queryctrl v;
+};
+
+struct saa7164_tvnorm {
+ char *name;
+ v4l2_std_id id;
+};
+
+struct saa7164_encoder_params {
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 is_50hz;
+ u32 bitrate; /* bps */
+ u32 bitrate_peak; /* bps */
+ u32 bitrate_mode;
+ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+ u32 audio_sampling_freq;
+ u32 ctl_mute;
+ u32 ctl_aspect;
+ u32 refdist;
+ u32 gop_size;
+};
+
+struct saa7164_vbi_params {
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 is_50hz;
+ u32 bitrate; /* bps */
+ u32 bitrate_peak; /* bps */
+ u32 bitrate_mode;
+ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
+
+ u32 audio_sampling_freq;
+ u32 ctl_mute;
+ u32 ctl_aspect;
+ u32 refdist;
+ u32 gop_size;
+};
+
+struct saa7164_port;
+
+struct saa7164_buffer {
+ struct list_head list;
+
+ /* Note of which h/w buffer list index position we occupy */
+ int idx;
+
+ struct saa7164_port *port;
+
+ /* Hardware Specific */
+ /* PCI Memory allocations */
+ enum saa7164_buffer_flags flags; /* Free, Busy, Full */
+
+ /* A block of page align PCI memory */
+ u32 pci_size; /* PCI allocation size in bytes */
+ u64 __iomem *cpu; /* Virtual address */
+ dma_addr_t dma; /* Physical address */
+ u32 crc; /* Checksum for the entire buffer data */
+
+ /* A page table that splits the block into a number of entries */
+ u32 pt_size; /* PCI allocation size in bytes */
+ u64 __iomem *pt_cpu; /* Virtual address */
+ dma_addr_t pt_dma; /* Physical address */
+
+ /* Encoder fops */
+ u32 pos;
+ u32 actual_size;
+};
+
+struct saa7164_port {
+
+ struct saa7164_dev *dev;
+ enum port_t type;
+ int nr;
+
+ /* --- Generic port attributes --- */
+
+ /* HW stream parameters */
+ struct tmHWStreamParameters hw_streamingparams;
+
+ /* DMA configuration values, is seeded during initialization */
+ struct tmComResDMATermDescrHeader hwcfg;
+
+ /* hardware specific registers */
+ u32 bufcounter;
+ u32 pitch;
+ u32 bufsize;
+ u32 bufoffset;
+ u32 bufptr32l;
+ u32 bufptr32h;
+ u64 bufptr64;
+
+ u32 numpte; /* Number of entries in array, only valid in head */
+
+ struct mutex dmaqueue_lock;
+ struct saa7164_buffer dmaqueue;
+
+ u64 last_irq_msecs, last_svc_msecs;
+ u64 last_irq_msecs_diff, last_svc_msecs_diff;
+ u32 last_svc_wp;
+ u32 last_svc_rp;
+ u64 last_irq_svc_msecs_diff;
+ u64 last_read_msecs, last_read_msecs_diff;
+ u64 last_poll_msecs, last_poll_msecs_diff;
+
+ struct saa7164_histogram irq_interval;
+ struct saa7164_histogram svc_interval;
+ struct saa7164_histogram irq_svc_interval;
+ struct saa7164_histogram read_interval;
+ struct saa7164_histogram poll_interval;
+
+ /* --- DVB Transport Specific --- */
+ struct saa7164_dvb dvb;
+
+ /* --- Encoder/V4L related attributes --- */
+ /* Encoder */
+ /* Defaults established in saa7164-encoder.c */
+ struct saa7164_tvnorm encodernorm;
+ u32 height;
+ u32 width;
+ u32 freq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+ u8 mux_input;
+ u8 encoder_profile;
+ u8 video_format;
+ u8 audio_format;
+ u8 video_resolution;
+ u16 ctl_brightness;
+ u16 ctl_contrast;
+ u16 ctl_hue;
+ u16 ctl_saturation;
+ u16 ctl_sharpness;
+ s8 ctl_volume;
+
+ struct tmComResAFeatureDescrHeader audfeat;
+ struct tmComResEncoderDescrHeader encunit;
+ struct tmComResProcDescrHeader vidproc;
+ struct tmComResExtDevDescrHeader ifunit;
+ struct tmComResTunerDescrHeader tunerunit;
+
+ struct work_struct workenc;
+
+ /* V4L Encoder Video */
+ struct saa7164_encoder_params encoder_params;
+ struct video_device *v4l_device;
+ atomic_t v4l_reader_count;
+
+ struct saa7164_buffer list_buf_used;
+ struct saa7164_buffer list_buf_free;
+ wait_queue_head_t wait_read;
+
+ /* V4L VBI */
+ struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc;
+ struct saa7164_vbi_params vbi_params;
+
+ /* Debug */
+ u32 sync_errors;
+ u32 v_cc_errors;
+ u32 a_cc_errors;
+ u8 last_v_cc;
+ u8 last_a_cc;
+ u32 done_first_interrupt;
+};
+
+struct saa7164_dev {
+ struct list_head devlist;
+ atomic_t refcount;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+ unsigned char pci_rev, pci_lat;
+ int pci_bus, pci_slot;
+ u32 __iomem *lmmio;
+ u8 __iomem *bmmio;
+ u32 __iomem *lmmio2;
+ u8 __iomem *bmmio2;
+ int pci_irqmask;
+
+ /* board details */
+ int nr;
+ int hwrevision;
+ u32 board;
+ char name[16];
+
+ /* firmware status */
+ struct saa7164_fw_status fw_status;
+ u32 firmwareloaded;
+
+ struct tmComResHWDescr hwdesc;
+ struct tmComResInterfaceDescr intfdesc;
+ struct tmComResBusDescr busdesc;
+
+ struct tmComResBusInfo bus;
+
+ /* Interrupt status and ack registers */
+ u32 int_status;
+ u32 int_ack;
+
+ struct cmd cmds[SAA_CMD_MAX_MSG_UNITS];
+ struct mutex lock;
+
+ /* I2c related */
+ struct saa7164_i2c i2c_bus[3];
+
+ /* Transport related */
+ struct saa7164_port ports[SAA7164_MAX_PORTS];
+
+ /* Deferred command/api interrupts handling */
+ struct work_struct workcmd;
+
+ /* A kernel thread to monitor the firmware log, used
+ * only in debug mode.
+ */
+ struct task_struct *kthread;
+
+};
+
+extern struct list_head saa7164_devlist;
+extern unsigned int waitsecs;
+extern unsigned int encoder_buffers;
+extern unsigned int vbi_buffers;
+
+/* ----------------------------------------------------------- */
+/* saa7164-core.c */
+void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr);
+void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
+u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
+void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
+
+/* ----------------------------------------------------------- */
+/* saa7164-fw.c */
+int saa7164_downloadfirmware(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-i2c.c */
+extern int saa7164_i2c_register(struct saa7164_i2c *bus);
+extern int saa7164_i2c_unregister(struct saa7164_i2c *bus);
+extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus,
+ unsigned int cmd, void *arg);
+
+/* ----------------------------------------------------------- */
+/* saa7164-bus.c */
+int saa7164_bus_setup(struct saa7164_dev *dev);
+void saa7164_bus_dump(struct saa7164_dev *dev);
+int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf);
+int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
+ void *buf, int peekonly);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cmd.c */
+int saa7164_cmd_send(struct saa7164_dev *dev,
+ u8 id, enum tmComResCmd command, u16 controlselector,
+ u16 size, void *buf);
+void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
+int saa7164_irq_dequeue(struct saa7164_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* saa7164-api.c */
+int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version);
+int saa7164_api_enum_subdevs(struct saa7164_dev *dev);
+int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
+ u32 datalen, u8 *data);
+int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr,
+ u32 datalen, u8 *data);
+int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr,
+ u32 datalen, u8 *data);
+int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen);
+int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
+int saa7164_api_transition_port(struct saa7164_port *port, u8 mode);
+int saa7164_api_initialize_dif(struct saa7164_port *port);
+int saa7164_api_configure_dif(struct saa7164_port *port, u32 std);
+int saa7164_api_set_encoder(struct saa7164_port *port);
+int saa7164_api_get_encoder(struct saa7164_port *port);
+int saa7164_api_set_aspect_ratio(struct saa7164_port *port);
+int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl);
+int saa7164_api_set_videomux(struct saa7164_port *port);
+int saa7164_api_audio_mute(struct saa7164_port *port, int mute);
+int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level);
+int saa7164_api_set_audio_std(struct saa7164_port *port);
+int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect);
+int saa7164_api_get_videomux(struct saa7164_port *port);
+int saa7164_api_set_vbi_format(struct saa7164_port *port);
+int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level);
+int saa7164_api_collect_debug(struct saa7164_dev *dev);
+int saa7164_api_get_load_info(struct saa7164_dev *dev,
+ struct tmFwInfoStruct *i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-cards.c */
+extern struct saa7164_board saa7164_boards[];
+extern const unsigned int saa7164_bcount;
+
+extern struct saa7164_subid saa7164_subids[];
+extern const unsigned int saa7164_idcount;
+
+extern void saa7164_card_list(struct saa7164_dev *dev);
+extern void saa7164_gpio_setup(struct saa7164_dev *dev);
+extern void saa7164_card_setup(struct saa7164_dev *dev);
+
+extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr);
+extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr);
+extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid);
+
+/* ----------------------------------------------------------- */
+/* saa7164-dvb.c */
+extern int saa7164_dvb_register(struct saa7164_port *port);
+extern int saa7164_dvb_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-buffer.c */
+extern struct saa7164_buffer *saa7164_buffer_alloc(
+ struct saa7164_port *port, u32 len);
+extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
+extern void saa7164_buffer_display(struct saa7164_buffer *buf);
+extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
+extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
+extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
+ struct saa7164_dev *dev, u32 len);
+extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
+extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
+
+/* ----------------------------------------------------------- */
+/* saa7164-encoder.c */
+int saa7164_encoder_register(struct saa7164_port *port);
+void saa7164_encoder_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+/* saa7164-vbi.c */
+int saa7164_vbi_register(struct saa7164_port *port);
+void saa7164_vbi_unregister(struct saa7164_port *port);
+
+/* ----------------------------------------------------------- */
+
+extern unsigned int crc_checking;
+
+extern unsigned int saa_debug;
+#define dprintk(level, fmt, arg...)\
+ do { if (saa_debug & level)\
+ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define log_warn(fmt, arg...)\
+ do { \
+ printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\
+ } while (0)
+
+#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
+#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
+
+#define saa7164_readb(reg) readl(dev->bmmio + (reg))
+#define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg))
+
diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig
new file mode 100644
index 000000000000..6749f67cab8a
--- /dev/null
+++ b/drivers/media/pci/sta2x11/Kconfig
@@ -0,0 +1,12 @@
+config STA2X11_VIP
+ tristate "STA2X11 VIP Video For Linux"
+ depends on STA2X11
+ select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEOBUF_DMA_CONTIG
+ depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
+ help
+ Say Y for support for STA2X11 VIP (Video Input Port) capture
+ device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sta2x11_vip.
diff --git a/drivers/media/pci/sta2x11/Makefile b/drivers/media/pci/sta2x11/Makefile
new file mode 100644
index 000000000000..d6c471d1d1b4
--- /dev/null
+++ b/drivers/media/pci/sta2x11/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
diff --git a/drivers/media/video/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c
index 4c10205264d4..4c10205264d4 100644
--- a/drivers/media/video/sta2x11_vip.c
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.c
diff --git a/drivers/media/video/sta2x11_vip.h b/drivers/media/pci/sta2x11/sta2x11_vip.h
index 4f81a13666eb..4f81a13666eb 100644
--- a/drivers/media/video/sta2x11_vip.h
+++ b/drivers/media/pci/sta2x11/sta2x11_vip.h
diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig
new file mode 100644
index 000000000000..314e417addae
--- /dev/null
+++ b/drivers/media/pci/ttpci/Kconfig
@@ -0,0 +1,159 @@
+config TTPCI_EEPROM
+ tristate
+ depends on I2C
+ default n
+
+config DVB_AV7110
+ tristate "AV7110 cards"
+ depends on DVB_CORE && PCI && I2C
+ select TTPCI_EEPROM
+ select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
+ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for SAA7146 and AV7110 based DVB cards as produced
+ by Fujitsu-Siemens, Technotrend, Hauppauge and others.
+
+ This driver only supports the fullfeatured cards with
+ onboard MPEG2 decoder.
+
+ This driver needs an external firmware. Please use the script
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware av7110" to
+ download/extract it, and then copy it to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
+ Alternatively, you can download the file and use the kernel's
+ EXTRA_FIRMWARE configuration option to build it into your
+ kernel image by adding the filename to the EXTRA_FIRMWARE
+ configuration option string.
+
+ Say Y if you own such a card and want to use it.
+
+config DVB_AV7110_OSD
+ bool "AV7110 OSD support"
+ depends on DVB_AV7110
+ default y if DVB_AV7110=y || DVB_AV7110=m
+ help
+ The AV7110 firmware provides some code to generate an OnScreenDisplay
+ on the video output. This is kind of nonstandard and not guaranteed to
+ be maintained.
+
+ Anyway, some popular DVB software like VDR uses this OSD to render
+ its menus, so say Y if you want to use this software.
+
+ All other people say N.
+
+config DVB_BUDGET_CORE
+ tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)"
+ depends on DVB_CORE && PCI && I2C
+ select VIDEO_SAA7146
+ select TTPCI_EEPROM
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder.
+
+config DVB_BUDGET
+ tristate "Budget cards"
+ depends on DVB_BUDGET_CORE && I2C
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for simple SAA7146 based DVB cards (so called Budget-
+ or Nova-PCI cards) without onboard MPEG2 decoder, and without
+ analog inputs or an onboard Common Interface connector.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget.
+
+config DVB_BUDGET_CI
+ tristate "Budget cards with onboard CI connector"
+ depends on DVB_BUDGET_CORE && I2C
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+ depends on RC_CORE
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with onboard Common Interface connector.
+
+ Note: The Common Interface is not yet supported by this driver
+ due to lack of information from the vendor.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget-ci.
+
+config DVB_BUDGET_AV
+ tristate "Budget cards with analog video inputs"
+ depends on DVB_BUDGET_CORE && I2C
+ select VIDEO_SAA7146_VV
+ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for simple SAA7146 based DVB cards
+ (so called Budget- or Nova-PCI cards) without onboard
+ MPEG2 decoder, but with one or more analog video inputs.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget-av.
+
+config DVB_BUDGET_PATCH
+ tristate "AV7110 cards with Budget Patch"
+ depends on DVB_BUDGET_CORE && I2C
+ depends on DVB_AV7110
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for Budget Patch (full TS) modification on
+ SAA7146+AV7110 based cards (DVB-S cards). This
+ driver doesn't use onboard MPEG2 decoder. The
+ card is driven in Budget-only mode. Card is
+ required to have loaded firmware to tune properly.
+ Firmware can be loaded by insertion and removal of
+ standard AV7110 driver prior to loading this
+ driver.
+
+ Say Y if you own such a card and want to use it.
+
+ To compile this driver as a module, choose M here: the
+ module will be called budget-patch.
diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile
new file mode 100644
index 000000000000..98905963ff08
--- /dev/null
+++ b/drivers/media/pci/ttpci/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the kernel SAA7146 FULL TS DVB device driver
+# and the AV7110 DVB device driver
+#
+
+dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o
+
+ifdef CONFIG_INPUT_EVDEV
+dvb-ttpci-objs += av7110_ir.o
+endif
+
+obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o
+obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o
+obj-$(CONFIG_DVB_BUDGET) += budget.o
+obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o
+obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o
+obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o
+obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c
index 4bd8bd56befc..4bd8bd56befc 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/pci/ttpci/av7110.c
diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h
index 88b3b2d6cc0e..88b3b2d6cc0e 100644
--- a/drivers/media/dvb/ttpci/av7110.h
+++ b/drivers/media/pci/ttpci/av7110.h
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c
index 952b33dbac4f..952b33dbac4f 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/pci/ttpci/av7110_av.c
diff --git a/drivers/media/dvb/ttpci/av7110_av.h b/drivers/media/pci/ttpci/av7110_av.h
index 5f02ef85e47d..5f02ef85e47d 100644
--- a/drivers/media/dvb/ttpci/av7110_av.h
+++ b/drivers/media/pci/ttpci/av7110_av.h
diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c
index 9fc1dd0ba4c3..9fc1dd0ba4c3 100644
--- a/drivers/media/dvb/ttpci/av7110_ca.c
+++ b/drivers/media/pci/ttpci/av7110_ca.c
diff --git a/drivers/media/dvb/ttpci/av7110_ca.h b/drivers/media/pci/ttpci/av7110_ca.h
index 70ee855ece1b..70ee855ece1b 100644
--- a/drivers/media/dvb/ttpci/av7110_ca.h
+++ b/drivers/media/pci/ttpci/av7110_ca.h
diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
index f1cbfe526989..f1cbfe526989 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.c
+++ b/drivers/media/pci/ttpci/av7110_hw.c
diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h
index 1634aba5cb84..1634aba5cb84 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.h
+++ b/drivers/media/pci/ttpci/av7110_hw.h
diff --git a/drivers/media/dvb/ttpci/av7110_ipack.c b/drivers/media/pci/ttpci/av7110_ipack.c
index 699ef8b5b99a..699ef8b5b99a 100644
--- a/drivers/media/dvb/ttpci/av7110_ipack.c
+++ b/drivers/media/pci/ttpci/av7110_ipack.c
diff --git a/drivers/media/dvb/ttpci/av7110_ipack.h b/drivers/media/pci/ttpci/av7110_ipack.h
index becf94d3fdfa..becf94d3fdfa 100644
--- a/drivers/media/dvb/ttpci/av7110_ipack.h
+++ b/drivers/media/pci/ttpci/av7110_ipack.h
diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c
index 908f272fe26c..908f272fe26c 100644
--- a/drivers/media/dvb/ttpci/av7110_ir.c
+++ b/drivers/media/pci/ttpci/av7110_ir.c
diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c
new file mode 100644
index 000000000000..730e906ea912
--- /dev/null
+++ b/drivers/media/pci/ttpci/av7110_v4l.c
@@ -0,0 +1,966 @@
+/*
+ * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * originally based on code by:
+ * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * the project's page is at http://www.linuxtv.org/
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/poll.h>
+
+#include "av7110.h"
+#include "av7110_hw.h"
+#include "av7110_av.h"
+
+int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
+{
+ u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
+ struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg };
+
+ switch (av7110->adac_type) {
+ case DVB_ADAC_MSP34x0:
+ msgs.addr = 0x40;
+ break;
+ case DVB_ADAC_MSP34x5:
+ msgs.addr = 0x42;
+ break;
+ default:
+ return 0;
+ }
+
+ if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
+ dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
+ av7110->dvb_adapter.num, reg, val);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
+{
+ u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
+ u8 msg2[2];
+ struct i2c_msg msgs[2] = {
+ { .flags = 0 , .len = 3, .buf = msg1 },
+ { .flags = I2C_M_RD, .len = 2, .buf = msg2 }
+ };
+
+ switch (av7110->adac_type) {
+ case DVB_ADAC_MSP34x0:
+ msgs[0].addr = 0x40;
+ msgs[1].addr = 0x40;
+ break;
+ case DVB_ADAC_MSP34x5:
+ msgs[0].addr = 0x42;
+ msgs[1].addr = 0x42;
+ break;
+ default:
+ return 0;
+ }
+
+ if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
+ dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
+ av7110->dvb_adapter.num, reg);
+ return -EIO;
+ }
+ *val = (msg2[0] << 8) | msg2[1];
+ return 0;
+}
+
+static struct v4l2_input inputs[4] = {
+ {
+ .index = 0,
+ .name = "DVB",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 1,
+ .tuner = 0, /* ignored */
+ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+ .status = 0,
+ .capabilities = V4L2_IN_CAP_STD,
+ }, {
+ .index = 1,
+ .name = "Television",
+ .type = V4L2_INPUT_TYPE_TUNER,
+ .audioset = 1,
+ .tuner = 0,
+ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+ .status = 0,
+ .capabilities = V4L2_IN_CAP_STD,
+ }, {
+ .index = 2,
+ .name = "Video",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+ .status = 0,
+ .capabilities = V4L2_IN_CAP_STD,
+ }, {
+ .index = 3,
+ .name = "Y/C",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 0,
+ .tuner = 0,
+ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
+ .status = 0,
+ .capabilities = V4L2_IN_CAP_STD,
+ }
+};
+
+static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data)
+{
+ struct av7110 *av7110 = dev->ext_priv;
+ u8 buf[] = { 0x00, reg, data };
+ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 };
+
+ dprintk(4, "dev: %p\n", dev);
+
+ if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
+ return -1;
+ return 0;
+}
+
+static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4])
+{
+ struct av7110 *av7110 = dev->ext_priv;
+ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 };
+
+ dprintk(4, "dev: %p\n", dev);
+
+ if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1))
+ return -1;
+ return 0;
+}
+
+static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+ u32 div;
+ u8 config;
+ u8 buf[4];
+
+ dprintk(4, "freq: 0x%08x\n", freq);
+
+ /* magic number: 614. tuning with the frequency given by v4l2
+ is always off by 614*62.5 = 38375 kHz...*/
+ div = freq + 614;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = 0x8e;
+
+ if (freq < (u32) (16 * 168.25))
+ config = 0xa0;
+ else if (freq < (u32) (16 * 447.25))
+ config = 0x90;
+ else
+ config = 0x30;
+ config &= ~0x02;
+
+ buf[3] = config;
+
+ return tuner_write(dev, 0x61, buf);
+}
+
+static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq)
+{
+ struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
+ u32 div;
+ u8 data[4];
+
+ div = (freq + 38900000 + 31250) / 62500;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0xce;
+
+ if (freq < 45000000)
+ return -EINVAL;
+ else if (freq < 137000000)
+ data[3] = 0x01;
+ else if (freq < 403000000)
+ data[3] = 0x02;
+ else if (freq < 860000000)
+ data[3] = 0x04;
+ else
+ return -EINVAL;
+
+ if (av7110->fe->ops.i2c_gate_ctrl)
+ av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1);
+ return tuner_write(dev, 0x63, data);
+}
+
+
+
+static struct saa7146_standard analog_standard[];
+static struct saa7146_standard dvb_standard[];
+static struct saa7146_standard standard[];
+
+static struct v4l2_audio msp3400_v4l2_audio = {
+ .index = 0,
+ .name = "Television",
+ .capability = V4L2_AUDCAP_STEREO
+};
+
+static int av7110_dvb_c_switch(struct saa7146_fh *fh)
+{
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+ struct av7110 *av7110 = (struct av7110*)dev->ext_priv;
+ u16 adswitch;
+ int source, sync, err;
+
+ dprintk(4, "%p\n", av7110);
+
+ if ((vv->video_status & STATUS_OVERLAY) != 0) {
+ vv->ov_suspend = vv->video_fh;
+ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+ if (err != 0) {
+ dprintk(2, "suspending video failed\n");
+ vv->ov_suspend = NULL;
+ }
+ }
+
+ if (0 != av7110->current_input) {
+ dprintk(1, "switching to analog TV:\n");
+ adswitch = 1;
+ source = SAA7146_HPS_SOURCE_PORT_B;
+ sync = SAA7146_HPS_SYNC_PORT_B;
+ memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2);
+
+ switch (av7110->current_input) {
+ case 1:
+ dprintk(1, "switching SAA7113 to Analog Tuner Input\n");
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source
+ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source
+ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source
+ msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
+ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
+
+ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+ if (ves1820_writereg(dev, 0x09, 0x0f, 0x60))
+ dprintk(1, "setting band in demodulator failed\n");
+ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD)
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF)
+ }
+ if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1)
+ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+ break;
+ case 2:
+ dprintk(1, "switching SAA7113 to Video AV CVBS Input\n");
+ if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1)
+ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+ break;
+ case 3:
+ dprintk(1, "switching SAA7113 to Video AV Y/C Input\n");
+ if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1)
+ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num);
+ break;
+ default:
+ dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n");
+ }
+ } else {
+ adswitch = 0;
+ source = SAA7146_HPS_SOURCE_PORT_A;
+ sync = SAA7146_HPS_SYNC_PORT_A;
+ memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+ dprintk(1, "switching DVB mode\n");
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+ msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+
+ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+ if (ves1820_writereg(dev, 0x09, 0x0f, 0x20))
+ dprintk(1, "setting band in demodulator failed\n");
+ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
+ }
+ }
+
+ /* hmm, this does not do anything!? */
+ if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch))
+ dprintk(1, "ADSwitch error\n");
+
+ saa7146_set_hps_source_and_sync(dev, source, sync);
+
+ if (vv->ov_suspend != NULL) {
+ saa7146_start_preview(vv->ov_suspend);
+ vv->ov_suspend = NULL;
+ }
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+ u16 stereo_det;
+ s8 stereo;
+
+ dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
+
+ if (!av7110->analog_tuner_flags || t->index != 0)
+ return -EINVAL;
+
+ memset(t, 0, sizeof(*t));
+ strcpy((char *)t->name, "Television");
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+ t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
+ /* FIXME: add the real signal strength here */
+ t->signal = 0xffff;
+ t->afc = 0;
+
+ /* FIXME: standard / stereo detection is still broken */
+ msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
+ dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
+ msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
+ dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
+ stereo = (s8)(stereo_det >> 8);
+ if (stereo > 0x10) {
+ /* stereo */
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ } else if (stereo < -0x10) {
+ /* bilingual */
+ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ t->audmode = V4L2_TUNER_MODE_LANG1;
+ } else /* mono */
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+ u16 fm_matrix, src;
+ dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
+
+ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+ return -EINVAL;
+
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_STEREO:
+ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
+ fm_matrix = 0x3001; /* stereo */
+ src = 0x0020;
+ break;
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
+ fm_matrix = 0x3000; /* bilingual */
+ src = 0x0020;
+ break;
+ case V4L2_TUNER_MODE_LANG1:
+ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
+ fm_matrix = 0x3000; /* mono */
+ src = 0x0000;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
+ fm_matrix = 0x3000; /* mono */
+ src = 0x0010;
+ break;
+ default: /* case V4L2_TUNER_MODE_MONO: */
+ dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
+ fm_matrix = 0x3000; /* mono */
+ src = 0x0030;
+ break;
+ }
+ msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
+ msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency);
+
+ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+ return -EINVAL;
+
+ memset(f, 0, sizeof(*f));
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = av7110->current_freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency);
+
+ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+ return -EINVAL;
+
+ if (V4L2_TUNER_ANALOG_TV != f->type)
+ return -EINVAL;
+
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
+ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
+
+ /* tune in desired frequency */
+ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
+ ves1820_set_tv_freq(dev, f->frequency);
+ else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
+ stv0297_set_tv_freq(dev, f->frequency);
+ av7110->current_freq = f->frequency;
+
+ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
+ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
+ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
+
+ if (av7110->analog_tuner_flags) {
+ if (i->index >= 4)
+ return -EINVAL;
+ } else {
+ if (i->index != 0)
+ return -EINVAL;
+ }
+
+ memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ *input = av7110->current_input;
+ dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
+
+ if (!av7110->analog_tuner_flags)
+ return input ? -EINVAL : 0;
+
+ if (input >= 4)
+ return -EINVAL;
+
+ av7110->current_input = input;
+ return av7110_dvb_c_switch(fh);
+}
+
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+ if (a->index != 0)
+ return -EINVAL;
+ *a = msp3400_v4l2_audio;
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+ if (a->index != 0)
+ return -EINVAL;
+ if (av7110->current_input >= 2)
+ return -EINVAL;
+ *a = msp3400_v4l2_audio;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
+ if (av7110->current_input >= 2)
+ return -EINVAL;
+ return a->index ? -EINVAL : 0;
+}
+
+static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_sliced_vbi_cap *cap)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
+ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
+ return -EINVAL;
+ if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+ cap->service_set = V4L2_SLICED_WSS_625;
+ cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ }
+ return 0;
+}
+
+static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_G_FMT:\n");
+ if (FW_VERSION(av7110->arm_app) < 0x2623)
+ return -EINVAL;
+ memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+ if (av7110->wssMode) {
+ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+ }
+ return 0;
+}
+
+static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+ dprintk(2, "VIDIOC_S_FMT\n");
+ if (FW_VERSION(av7110->arm_app) < 0x2623)
+ return -EINVAL;
+ if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
+ f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
+ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+ /* WSS controlled by firmware */
+ av7110->wssMode = 0;
+ av7110->wssData = 0;
+ return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ SetWSSConfig, 1, 0);
+ } else {
+ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+ /* WSS controlled by userspace */
+ av7110->wssMode = 1;
+ av7110->wssData = 0;
+ }
+ return 0;
+}
+
+static int av7110_vbi_reset(struct file *file)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+
+ dprintk(2, "%s\n", __func__);
+ av7110->wssMode = 0;
+ av7110->wssData = 0;
+ if (FW_VERSION(av7110->arm_app) < 0x2623)
+ return 0;
+ else
+ return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0);
+}
+
+static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
+{
+ struct saa7146_fh *fh = file->private_data;
+ struct saa7146_dev *dev = fh->dev;
+ struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+ struct v4l2_sliced_vbi_data d;
+ int rc;
+
+ dprintk(2, "%s\n", __func__);
+ if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d)
+ return -EINVAL;
+ if (copy_from_user(&d, data, count))
+ return -EFAULT;
+ if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
+ return -EINVAL;
+ if (d.id)
+ av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
+ else
+ av7110->wssData = 0x8000;
+ rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData);
+ return (rc < 0) ? rc : count;
+}
+
+/****************************************************************************
+ * INITIALIZATION
+ ****************************************************************************/
+
+static u8 saa7113_init_regs[] = {
+ 0x02, 0xd0,
+ 0x03, 0x23,
+ 0x04, 0x00,
+ 0x05, 0x00,
+ 0x06, 0xe9,
+ 0x07, 0x0d,
+ 0x08, 0x98,
+ 0x09, 0x02,
+ 0x0a, 0x80,
+ 0x0b, 0x40,
+ 0x0c, 0x40,
+ 0x0d, 0x00,
+ 0x0e, 0x01,
+ 0x0f, 0x7c,
+ 0x10, 0x48,
+ 0x11, 0x0c,
+ 0x12, 0x8b,
+ 0x13, 0x1a,
+ 0x14, 0x00,
+ 0x15, 0x00,
+ 0x16, 0x00,
+ 0x17, 0x00,
+ 0x18, 0x00,
+ 0x19, 0x00,
+ 0x1a, 0x00,
+ 0x1b, 0x00,
+ 0x1c, 0x00,
+ 0x1d, 0x00,
+ 0x1e, 0x00,
+
+ 0x41, 0x77,
+ 0x42, 0x77,
+ 0x43, 0x77,
+ 0x44, 0x77,
+ 0x45, 0x77,
+ 0x46, 0x77,
+ 0x47, 0x77,
+ 0x48, 0x77,
+ 0x49, 0x77,
+ 0x4a, 0x77,
+ 0x4b, 0x77,
+ 0x4c, 0x77,
+ 0x4d, 0x77,
+ 0x4e, 0x77,
+ 0x4f, 0x77,
+ 0x50, 0x77,
+ 0x51, 0x77,
+ 0x52, 0x77,
+ 0x53, 0x77,
+ 0x54, 0x77,
+ 0x55, 0x77,
+ 0x56, 0x77,
+ 0x57, 0xff,
+
+ 0xff
+};
+
+
+static struct saa7146_ext_vv av7110_vv_data_st;
+static struct saa7146_ext_vv av7110_vv_data_c;
+
+int av7110_init_analog_module(struct av7110 *av7110)
+{
+ u16 version1, version2;
+
+ if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
+ i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n",
+ av7110->dvb_adapter.num);
+ av7110->adac_type = DVB_ADAC_MSP34x0;
+ } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
+ i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
+ pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n",
+ av7110->dvb_adapter.num);
+ av7110->adac_type = DVB_ADAC_MSP34x5;
+ } else
+ return -ENODEV;
+
+ msleep(100); // the probing above resets the msp...
+ msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
+ msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
+ dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n",
+ av7110->dvb_adapter.num, version1, version2);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source
+ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source
+ msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
+ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
+ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
+ msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
+
+ if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
+ pr_info("saa7113 not accessible\n");
+ } else {
+ u8 *i = saa7113_init_regs;
+
+ if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) {
+ /* Fujitsu/Siemens DVB-Cable */
+ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+ } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) {
+ /* Hauppauge/TT DVB-C premium */
+ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820;
+ } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) {
+ /* Hauppauge/TT DVB-C premium */
+ av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297;
+ }
+
+ /* setup for DVB by default */
+ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+ if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20))
+ dprintk(1, "setting band in demodulator failed\n");
+ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+ saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD)
+ saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF)
+ }
+
+ /* init the saa7113 */
+ while (*i != 0xff) {
+ if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) {
+ dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num);
+ break;
+ }
+ i += 2;
+ }
+ /* setup msp for analog sound: B/G Dual-FM */
+ msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2
+ msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG
+ msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz
+ msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI
+ msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz
+ msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI
+ msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2
+ }
+
+ memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2);
+ /* set dd1 stream a & b */
+ saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(av7110->dev, DD1_INIT, 0x03000700);
+ saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+
+ return 0;
+}
+
+int av7110_init_v4l(struct av7110 *av7110)
+{
+ struct saa7146_dev* dev = av7110->dev;
+ struct saa7146_ext_vv *vv_data;
+ int ret;
+
+ /* special case DVB-C: these cards have an analog tuner
+ plus need some special handling, so we have separate
+ saa7146_ext_vv data for these... */
+ if (av7110->analog_tuner_flags)
+ vv_data = &av7110_vv_data_c;
+ else
+ vv_data = &av7110_vv_data_st;
+ ret = saa7146_vv_init(dev, vv_data);
+
+ if (ret) {
+ ERR("cannot init capture device. skipping\n");
+ return -ENODEV;
+ }
+ vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data->vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data->vid_ops.vidioc_s_input = vidioc_s_input;
+ vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+ vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio;
+ vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL;
+
+ vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL;
+ vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
+ vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
+ vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
+
+ if (FW_VERSION(av7110->arm_app) < 0x2623)
+ vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT;
+
+ if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
+ ERR("cannot register capture device. skipping\n");
+ saa7146_vv_release(dev);
+ return -ENODEV;
+ }
+ if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+ if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
+ ERR("cannot register vbi v4l2 device. skipping\n");
+ }
+ return 0;
+}
+
+int av7110_exit_v4l(struct av7110 *av7110)
+{
+ struct saa7146_dev* dev = av7110->dev;
+
+ saa7146_unregister_device(&av7110->v4l_dev, av7110->dev);
+ saa7146_unregister_device(&av7110->vbi_dev, av7110->dev);
+
+ saa7146_vv_release(dev);
+
+ return 0;
+}
+
+
+
+/* FIXME: these values are experimental values that look better than the
+ values from the latest "official" driver -- at least for me... (MiHu) */
+static struct saa7146_standard standard[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x15, .v_field = 288,
+ .h_offset = 0x48, .h_pixels = 708,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }
+};
+
+static struct saa7146_standard analog_standard[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x1b, .v_field = 288,
+ .h_offset = 0x08, .h_pixels = 708,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }
+};
+
+static struct saa7146_standard dvb_standard[] = {
+ {
+ .name = "PAL", .id = V4L2_STD_PAL_BG,
+ .v_offset = 0x14, .v_field = 288,
+ .h_offset = 0x48, .h_pixels = 708,
+ .v_max_out = 576, .h_max_out = 768,
+ }, {
+ .name = "NTSC", .id = V4L2_STD_NTSC,
+ .v_offset = 0x10, .v_field = 244,
+ .h_offset = 0x40, .h_pixels = 708,
+ .v_max_out = 480, .h_max_out = 640,
+ }
+};
+
+static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
+{
+ struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+
+ if (std->id & V4L2_STD_PAL) {
+ av7110->vidmode = AV7110_VIDEO_MODE_PAL;
+ av7110_set_vidmode(av7110, av7110->vidmode);
+ }
+ else if (std->id & V4L2_STD_NTSC) {
+ av7110->vidmode = AV7110_VIDEO_MODE_NTSC;
+ av7110_set_vidmode(av7110, av7110->vidmode);
+ }
+ else
+ return -1;
+
+ return 0;
+}
+
+
+static struct saa7146_ext_vv av7110_vv_data_st = {
+ .inputs = 1,
+ .audios = 1,
+ .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
+ .flags = 0,
+
+ .stds = &standard[0],
+ .num_stds = ARRAY_SIZE(standard),
+ .std_callback = &std_callback,
+
+ .vbi_fops.open = av7110_vbi_reset,
+ .vbi_fops.release = av7110_vbi_reset,
+ .vbi_fops.write = av7110_vbi_write,
+};
+
+static struct saa7146_ext_vv av7110_vv_data_c = {
+ .inputs = 1,
+ .audios = 1,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
+ .flags = SAA7146_USE_PORT_B_FOR_VBI,
+
+ .stds = &standard[0],
+ .num_stds = ARRAY_SIZE(standard),
+ .std_callback = &std_callback,
+
+ .vbi_fops.open = av7110_vbi_reset,
+ .vbi_fops.release = av7110_vbi_reset,
+ .vbi_fops.write = av7110_vbi_write,
+};
+
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c
index 12ddb53c58dc..12ddb53c58dc 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/pci/ttpci/budget-av.c
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c
index 98e524178765..98e524178765 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/pci/ttpci/budget-ci.c
diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c
index 37d02fe09137..37d02fe09137 100644
--- a/drivers/media/dvb/ttpci/budget-core.c
+++ b/drivers/media/pci/ttpci/budget-core.c
diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c
index 2cb35c23d2ac..2cb35c23d2ac 100644
--- a/drivers/media/dvb/ttpci/budget-patch.c
+++ b/drivers/media/pci/ttpci/budget-patch.c
diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c
new file mode 100644
index 000000000000..7e6e43ae5c51
--- /dev/null
+++ b/drivers/media/pci/ttpci/budget.c
@@ -0,0 +1,871 @@
+/*
+ * budget.c: driver for the SAA7146 based Budget DVB cards
+ *
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 26feb2004 Support for FS Activy Card (Grundig tuner) by
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de> and
+ * Andreas 'randy' Weinberger
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ *
+ * the project's page is at http://www.linuxtv.org/
+ */
+
+#include "budget.h"
+#include "stv0299.h"
+#include "ves1x93.h"
+#include "ves1820.h"
+#include "l64781.h"
+#include "tda8083.h"
+#include "s5h1420.h"
+#include "tda10086.h"
+#include "tda826x.h"
+#include "lnbp21.h"
+#include "bsru6.h"
+#include "bsbe1.h"
+#include "tdhd1.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "isl6423.h"
+#include "lnbh24.h"
+
+
+static int diseqc_method;
+module_param(diseqc_method, int, 0444);
+MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static void Set22K (struct budget *budget, int state)
+{
+ struct saa7146_dev *dev=budget->dev;
+ dprintk(2, "budget: %p\n", budget);
+ saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
+}
+
+/* Diseqc functions only for TT Budget card */
+/* taken from the Skyvision DVB driver by
+ Ralph Metzler <rjkm@metzlerbros.de> */
+
+static void DiseqcSendBit (struct budget *budget, int data)
+{
+ struct saa7146_dev *dev=budget->dev;
+ dprintk(2, "budget: %p\n", budget);
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ udelay(data ? 500 : 1000);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ udelay(data ? 1000 : 500);
+}
+
+static void DiseqcSendByte (struct budget *budget, int data)
+{
+ int i, par=1, d;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ for (i=7; i>=0; i--) {
+ d = (data>>i)&1;
+ par ^= d;
+ DiseqcSendBit(budget, d);
+ }
+
+ DiseqcSendBit(budget, par);
+}
+
+static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
+{
+ struct saa7146_dev *dev=budget->dev;
+ int i;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ mdelay(16);
+
+ for (i=0; i<len; i++)
+ DiseqcSendByte(budget, msg[i]);
+
+ mdelay(16);
+
+ if (burst!=-1) {
+ if (burst)
+ DiseqcSendByte(budget, 0xff);
+ else {
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
+ mdelay(12);
+ udelay(500);
+ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
+ }
+ msleep(20);
+ }
+
+ return 0;
+}
+
+/*
+ * Routines for the Fujitsu Siemens Activy budget card
+ * 22 kHz tone and DiSEqC are handled by the frontend.
+ * Voltage must be set here.
+ * GPIO 1: LNBP EN, GPIO 2: LNBP VSEL
+ */
+static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
+{
+ struct saa7146_dev *dev=budget->dev;
+
+ dprintk(2, "budget: %p\n", budget);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_13:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
+ break;
+ case SEC_VOLTAGE_18:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI);
+ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
+ break;
+ case SEC_VOLTAGE_OFF:
+ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ return SetVoltage_Activy (budget, voltage);
+}
+
+static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ Set22K (budget, 1);
+ break;
+
+ case SEC_TONE_OFF:
+ Set22K (budget, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
+
+ return 0;
+}
+
+static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+
+ SendDiSEqCMsg (budget, 0, NULL, minicmd);
+
+ return 0;
+}
+
+static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u8 pwr = 0;
+ u8 buf[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) };
+ u32 div = (c->frequency + 479500) / 125;
+
+ if (c->frequency > 2000000)
+ pwr = 3;
+ else if (c->frequency > 1800000)
+ pwr = 2;
+ else if (c->frequency > 1600000)
+ pwr = 1;
+ else if (c->frequency > 1200000)
+ pwr = 0;
+ else if (c->frequency >= 1100000)
+ pwr = 1;
+ else pwr = 2;
+
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = div & 0xff;
+ buf[2] = ((div & 0x18000) >> 10) | 0x95;
+ buf[3] = (pwr << 6) | 0x30;
+
+ // NOTE: since we're using a prescaler of 2, we set the
+ // divisor frequency to 62.5kHz and divide by 125 above
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct ves1x93_config alps_bsrv2_config =
+{
+ .demod_address = 0x08,
+ .xin = 90100000UL,
+ .invert_pwm = 0,
+};
+
+static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = (c->frequency + 35937500 + 31250) / 62500;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x85 | ((div >> 10) & 0x60);
+ data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct ves1820_config alps_tdbe2_config = {
+ .demod_address = 0x09,
+ .xin = 57840000UL,
+ .invert = 1,
+ .selagc = VES1820_SELAGC_SIGNAMPERR,
+};
+
+static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget *budget = fe->dvb->priv;
+ u8 *tuner_addr = fe->tuner_priv;
+ u32 div;
+ u8 cfg, cpump, band_select;
+ u8 data[4];
+ struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) };
+
+ if (tuner_addr)
+ msg.addr = *tuner_addr;
+ else
+ msg.addr = 0x61;
+
+ div = (36125000 + c->frequency) / 166666;
+
+ cfg = 0x88;
+
+ if (c->frequency < 175000000)
+ cpump = 2;
+ else if (c->frequency < 390000000)
+ cpump = 1;
+ else if (c->frequency < 470000000)
+ cpump = 2;
+ else if (c->frequency < 750000000)
+ cpump = 1;
+ else
+ cpump = 3;
+
+ if (c->frequency < 175000000)
+ band_select = 0x0e;
+ else if (c->frequency < 470000000)
+ band_select = 0x05;
+ else
+ band_select = 0x03;
+
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = ((div >> 10) & 0x60) | cfg;
+ data[3] = (cpump << 6) | band_select;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct l64781_config grundig_29504_401_config = {
+ .demod_address = 0x55,
+};
+
+static struct l64781_config grundig_29504_401_config_activy = {
+ .demod_address = 0x54,
+};
+
+static u8 tuner_address_grundig_29504_401_activy = 0x60;
+
+static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = c->frequency / 125;
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0x8e;
+ data[3] = 0x00;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+ return 0;
+}
+
+static struct tda8083_config grundig_29504_451_config = {
+ .demod_address = 0x68,
+};
+
+static int s5h1420_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct budget* budget = (struct budget*) fe->dvb->priv;
+ u32 div;
+ u8 data[4];
+ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) };
+
+ div = c->frequency / 1000;
+ data[0] = (div >> 8) & 0x7f;
+ data[1] = div & 0xff;
+ data[2] = 0xc2;
+
+ if (div < 1450)
+ data[3] = 0x00;
+ else if (div < 1850)
+ data[3] = 0x40;
+ else if (div < 2000)
+ data[3] = 0x80;
+ else
+ data[3] = 0xc0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO;
+
+ return 0;
+}
+
+static struct s5h1420_config s5h1420_config = {
+ .demod_address = 0x53,
+ .invert = 1,
+ .cdclk_polarity = 1,
+};
+
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 1,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct stv0299_config alps_bsru6_config_activy = {
+ .demod_address = 0x68,
+ .inittab = alps_bsru6_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .op0_off = 1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = alps_bsru6_set_symbol_rate,
+};
+
+static struct stv0299_config alps_bsbe1_config_activy = {
+ .demod_address = 0x68,
+ .inittab = alps_bsbe1_inittab,
+ .mclk = 88000000UL,
+ .invert = 1,
+ .op0_off = 1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = alps_bsbe1_set_symbol_rate,
+};
+
+static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name)
+{
+ struct budget *budget = (struct budget *)fe->dvb->priv;
+
+ return request_firmware(fw, name, &budget->dev->pci->dev);
+}
+
+
+static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg)
+{
+ u8 val;
+ struct i2c_msg msg[] = {
+ { .addr = adr, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 }
+ };
+
+ return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val;
+}
+
+static u8 read_pwm(struct budget* budget)
+{
+ u8 b = 0xff;
+ u8 pwm;
+ struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 },
+ { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} };
+
+ if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff))
+ pwm = 0x48;
+
+ return pwm;
+}
+
+static struct stv090x_config tt1600_stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+
+ .xtal = 13500000,
+ .address = 0x68,
+
+ .ts1_mode = STV090x_TSMODE_DVBCI,
+ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+
+ .repeater_level = STV090x_RPTLEVEL_16,
+
+ .tuner_init = NULL,
+ .tuner_sleep = NULL,
+ .tuner_set_mode = NULL,
+ .tuner_set_frequency = NULL,
+ .tuner_get_frequency = NULL,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = NULL,
+ .tuner_set_bbgain = NULL,
+ .tuner_get_bbgain = NULL,
+ .tuner_set_refclk = NULL,
+ .tuner_get_status = NULL,
+};
+
+static struct stv6110x_config tt1600_stv6110x_config = {
+ .addr = 0x60,
+ .refclk = 27000000,
+ .clk_div = 2,
+};
+
+static struct isl6423_config tt1600_isl6423_config = {
+ .current_max = SEC_CURRENT_515m,
+ .curlim = SEC_CURRENT_LIM_ON,
+ .mod_extern = 1,
+ .addr = 0x08,
+};
+
+static void frontend_init(struct budget *budget)
+{
+ (void)alps_bsbe1_config; /* avoid warning */
+
+ switch(budget->dev->pci->subsystem_device) {
+ case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
+ case 0x1013:
+ // try the ALPS BSRV2 first of all
+ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
+ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+ budget->dvb_frontend->ops.set_tone = budget_set_tone;
+ break;
+ }
+
+ // try the ALPS BSRU6 now
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) {
+ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
+ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst;
+ budget->dvb_frontend->ops.set_tone = budget_set_tone;
+ }
+ break;
+ }
+ break;
+
+ case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
+
+ budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
+ break;
+ }
+ break;
+
+ case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
+
+ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = NULL;
+ break;
+ }
+ break;
+
+ case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */
+ {
+ int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67);
+
+ if (subtype < 0)
+ break;
+ /* fixme: find a better way to identify the card */
+ if (subtype < 0x36) {
+ /* assume ALPS BSRU6 */
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n");
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ break;
+ }
+ } else {
+ /* assume ALPS BSBE1 */
+ /* reset tuner */
+ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI);
+ msleep(250);
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n");
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ break;
+ }
+ }
+ break;
+ }
+
+ case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
+ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
+ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
+ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
+ }
+ break;
+
+ case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */
+ budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params;
+ budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
+ }
+ break;
+
+ case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */
+ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy;
+ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
+ }
+ break;
+
+ case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
+ budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
+ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ goto error_out;
+ }
+ break;
+ }
+
+ case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
+ // gpio2 is connected to CLB - reset it + leave it high
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(1);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(1);
+
+ budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
+ printk("%s: No tda826x found!\n", __func__);
+ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __func__);
+ goto error_out;
+ }
+ break;
+ }
+
+ case 0x101c: { /* TT S2-1600 */
+ struct stv6110x_devctl *ctl;
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(250);
+
+ budget->dvb_frontend = dvb_attach(stv090x_attach,
+ &tt1600_stv090x_config,
+ &budget->i2c_adap,
+ STV090x_DEMODULATOR_0);
+
+ if (budget->dvb_frontend) {
+
+ ctl = dvb_attach(stv6110x_attach,
+ budget->dvb_frontend,
+ &tt1600_stv6110x_config,
+ &budget->i2c_adap);
+
+ if (ctl) {
+ tt1600_stv090x_config.tuner_init = ctl->tuner_init;
+ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
+ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
+ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
+ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
+ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
+ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
+
+ /* call the init function once to initialize
+ tuner's clock output divider and demod's
+ master clock */
+ if (budget->dvb_frontend->ops.init)
+ budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+ if (dvb_attach(isl6423_attach,
+ budget->dvb_frontend,
+ &budget->i2c_adap,
+ &tt1600_isl6423_config) == NULL) {
+ printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__);
+ goto error_out;
+ }
+ } else {
+ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ goto error_out;
+ }
+ }
+ }
+ break;
+
+ case 0x1020: { /* Omicom S2 */
+ struct stv6110x_devctl *ctl;
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(50);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(250);
+
+ budget->dvb_frontend = dvb_attach(stv090x_attach,
+ &tt1600_stv090x_config,
+ &budget->i2c_adap,
+ STV090x_DEMODULATOR_0);
+
+ if (budget->dvb_frontend) {
+ printk(KERN_INFO "budget: Omicom S2 detected\n");
+
+ ctl = dvb_attach(stv6110x_attach,
+ budget->dvb_frontend,
+ &tt1600_stv6110x_config,
+ &budget->i2c_adap);
+
+ if (ctl) {
+ tt1600_stv090x_config.tuner_init = ctl->tuner_init;
+ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep;
+ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode;
+ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency;
+ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency;
+ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth;
+ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth;
+ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain;
+ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain;
+ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk;
+ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status;
+
+ /* call the init function once to initialize
+ tuner's clock output divider and demod's
+ master clock */
+ if (budget->dvb_frontend->ops.init)
+ budget->dvb_frontend->ops.init(budget->dvb_frontend);
+
+ if (dvb_attach(lnbh24_attach,
+ budget->dvb_frontend,
+ &budget->i2c_adap,
+ LNBH24_PCL | LNBH24_TTX,
+ LNBH24_TEN, 0x14>>1) == NULL) {
+ printk(KERN_ERR
+ "No LNBH24 found!\n");
+ goto error_out;
+ }
+ } else {
+ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__);
+ goto error_out;
+ }
+ }
+ }
+ break;
+ }
+
+ if (budget->dvb_frontend == NULL) {
+ printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n",
+ budget->dev->pci->vendor,
+ budget->dev->pci->device,
+ budget->dev->pci->subsystem_vendor,
+ budget->dev->pci->subsystem_device);
+ } else {
+ if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend))
+ goto error_out;
+ }
+ return;
+
+error_out:
+ printk("budget: Frontend registration failed!\n");
+ dvb_frontend_detach(budget->dvb_frontend);
+ budget->dvb_frontend = NULL;
+ return;
+}
+
+static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
+{
+ struct budget *budget = NULL;
+ int err;
+
+ budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
+ if( NULL == budget ) {
+ return -ENOMEM;
+ }
+
+ dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
+
+ dev->ext_priv = budget;
+
+ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr);
+ if (err) {
+ printk("==> failed\n");
+ kfree (budget);
+ return err;
+ }
+
+ budget->dvb_adapter.priv = budget;
+ frontend_init(budget);
+
+ ttpci_budget_init_hooks(budget);
+
+ return 0;
+}
+
+static int budget_detach (struct saa7146_dev* dev)
+{
+ struct budget *budget = (struct budget*) dev->ext_priv;
+ int err;
+
+ if (budget->dvb_frontend) {
+ dvb_unregister_frontend(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
+ }
+
+ err = ttpci_budget_deinit (budget);
+
+ kfree (budget);
+ dev->ext_priv = NULL;
+
+ return err;
+}
+
+static struct saa7146_extension budget_extension;
+
+MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY);
+MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT);
+
+static struct pci_device_id pci_tbl[] = {
+ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
+ MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),
+ MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
+ MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
+ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
+ MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
+ MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c),
+ MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
+ MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
+ MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60),
+ MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61),
+ MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020),
+ {
+ .vendor = 0,
+ }
+};
+
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct saa7146_extension budget_extension = {
+ .name = "budget dvb",
+ .flags = SAA7146_USE_I2C_IRQ,
+
+ .module = THIS_MODULE,
+ .pci_tbl = pci_tbl,
+ .attach = budget_attach,
+ .detach = budget_detach,
+
+ .irq_mask = MASK_10,
+ .irq_func = ttpci_budget_irq10_handler,
+};
+
+static int __init budget_init(void)
+{
+ return saa7146_register_extension(&budget_extension);
+}
+
+static void __exit budget_exit(void)
+{
+ saa7146_unregister_extension(&budget_extension);
+}
+
+module_init(budget_init);
+module_exit(budget_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
+MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
+ "budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h
index 3d8a806c20bb..3d8a806c20bb 100644
--- a/drivers/media/dvb/ttpci/budget.h
+++ b/drivers/media/pci/ttpci/budget.h
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c
index 32d43156c548..32d43156c548 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.c
+++ b/drivers/media/pci/ttpci/ttpci-eeprom.c
diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/pci/ttpci/ttpci-eeprom.h
index dcc33d5a5cb1..dcc33d5a5cb1 100644
--- a/drivers/media/dvb/ttpci/ttpci-eeprom.h
+++ b/drivers/media/pci/ttpci/ttpci-eeprom.h
diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig
new file mode 100644
index 000000000000..26ca8702e33f
--- /dev/null
+++ b/drivers/media/pci/zoran/Kconfig
@@ -0,0 +1,74 @@
+config VIDEO_ZORAN
+ tristate "Zoran ZR36057/36067 Video For Linux"
+ depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
+ help
+ Say Y for support for MJPEG capture cards based on the Zoran
+ 36057/36067 PCI controller chipset. This includes the Iomega
+ Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
+ a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
+ more information, check <file:Documentation/video4linux/Zoran>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr36067.
+
+config VIDEO_ZORAN_DC30
+ tristate "Pinnacle/Miro DC30(+) support"
+ depends on VIDEO_ZORAN
+ select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
+ card. This also supports really old DC10 cards based on the
+ zr36050 MJPEG codec and zr36016 VFE.
+
+config VIDEO_ZORAN_ZR36060
+ tristate "Zoran ZR36060"
+ depends on VIDEO_ZORAN
+ help
+ Say Y to support Zoran boards based on 36060 chips.
+ This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
+ and 33 R10 and AverMedia 6 boards.
+
+config VIDEO_ZORAN_BUZ
+ tristate "Iomega Buz support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the Iomega Buz MJPEG capture/playback card.
+
+config VIDEO_ZORAN_DC10
+ tristate "Pinnacle/Miro DC10(+) support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33
+ tristate "Linux Media Labs LML33 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the Linux Media Labs LML33 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_LML33R10
+ tristate "Linux Media Labs LML33R10 support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ support for the Linux Media Labs LML33R10 MJPEG capture/playback
+ card.
+
+config VIDEO_ZORAN_AVS6EYES
+ tristate "AverMedia 6 Eyes support"
+ depends on VIDEO_ZORAN_ZR36060
+ select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/video/zoran/Makefile b/drivers/media/pci/zoran/Makefile
index 44cc13352c88..44cc13352c88 100644
--- a/drivers/media/video/zoran/Makefile
+++ b/drivers/media/pci/zoran/Makefile
diff --git a/drivers/media/video/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c
index c01071635290..c01071635290 100644
--- a/drivers/media/video/zoran/videocodec.c
+++ b/drivers/media/pci/zoran/videocodec.c
diff --git a/drivers/media/video/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h
index def55585ad23..def55585ad23 100644
--- a/drivers/media/video/zoran/videocodec.h
+++ b/drivers/media/pci/zoran/videocodec.h
diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h
index ca2754a3cd63..ca2754a3cd63 100644
--- a/drivers/media/video/zoran/zoran.h
+++ b/drivers/media/pci/zoran/zoran.h
diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c
new file mode 100644
index 000000000000..fffc54b452c8
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_card.c
@@ -0,0 +1,1528 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * This part handles card-specific data and detection
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Currently maintained by:
+ * Ronald Bultje <rbultje@ronald.bitfreak.net>
+ * Laurent Pinchart <laurent.pinchart@skynet.be>
+ * Mailinglist <mjpeg-users@lists.sf.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+#include <linux/proc_fs.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/videodev2.h>
+#include <linux/spinlock.h>
+#include <linux/sem.h>
+#include <linux/kmod.h>
+#include <linux/wait.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <media/v4l2-common.h>
+#include <media/bt819.h>
+
+#include "videocodec.h"
+#include "zoran.h"
+#include "zoran_card.h"
+#include "zoran_device.h"
+#include "zoran_procfs.h"
+
+extern const struct zoran_format zoran_formats[];
+
+static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "Card type");
+
+/*
+ The video mem address of the video card.
+ The driver has a little database for some videocards
+ to determine it from there. If your video card is not in there
+ you have either to give it to the driver as a parameter
+ or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem; /* default = 0 - Video memory base address */
+module_param(vidmem, ulong, 0444);
+MODULE_PARM_DESC(vidmem, "Default video memory base address");
+
+/*
+ Default input and video norm at startup of the driver.
+*/
+
+static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */
+module_param(default_input, uint, 0444);
+MODULE_PARM_DESC(default_input,
+ "Default input (0=Composite, 1=S-Video, 2=Internal)");
+
+static int default_mux = 1; /* 6 Eyes input selection */
+module_param(default_mux, int, 0644);
+MODULE_PARM_DESC(default_mux,
+ "Default 6 Eyes mux setting (Input selection)");
+
+static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */
+module_param(default_norm, int, 0444);
+MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
+
+/* /dev/videoN, -1 for autodetect */
+static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
+
+int v4l_nbufs = 4;
+int v4l_bufsize = 864; /* Everybody should be able to work with this setting */
+module_param(v4l_nbufs, int, 0644);
+MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
+module_param(v4l_bufsize, int, 0644);
+MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
+
+int jpg_nbufs = 32;
+int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
+module_param(jpg_nbufs, int, 0644);
+MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
+module_param(jpg_bufsize, int, 0644);
+MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
+
+int pass_through = 0; /* 1=Pass through TV signal when device is not used */
+ /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+module_param(pass_through, int, 0644);
+MODULE_PARM_DESC(pass_through,
+ "Pass TV signal through to TV-out when idling");
+
+int zr36067_debug = 1;
+module_param_named(debug, zr36067_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-5)");
+
+#define ZORAN_VERSION "0.10.1"
+
+MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
+MODULE_AUTHOR("Serguei Miridonov");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ZORAN_VERSION);
+
+#define ZR_DEVICE(subven, subdev, data) { \
+ .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
+ .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) }
+
+static struct pci_device_id zr36067_pci_tbl[] = {
+ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus),
+ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus),
+ ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10),
+ ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ),
+ ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS),
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
+
+static unsigned int zoran_num; /* number of cards found */
+
+/* videocodec bus functions ZR36060 */
+static u32
+zr36060_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, 3) & 0xff;
+ return data;
+}
+
+static void
+zr36060_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 0, 1, reg >> 8)
+ || post_office_write(zr, 0, 2, reg & 0xff)) {
+ return;
+ }
+
+ post_office_write(zr, 0, 3, val & 0xff);
+}
+
+/* videocodec bus functions ZR36050 */
+static u32
+zr36050_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return -1;
+ }
+
+ data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
+ return data;
+}
+
+static void
+zr36050_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)
+ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
+ return;
+ }
+
+ post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
+}
+
+/* videocodec bus functions ZR36016 */
+static u32
+zr36016_read (struct videocodec *codec,
+ u16 reg)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+ __u32 data;
+
+ if (post_office_wait(zr)) {
+ return -1;
+ }
+
+ data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
+ return data;
+}
+
+/* hack for in zoran_device.c */
+void
+zr36016_write (struct videocodec *codec,
+ u16 reg,
+ u32 val)
+{
+ struct zoran *zr = (struct zoran *) codec->master_data->data;
+
+ if (post_office_wait(zr)) {
+ return;
+ }
+
+ post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
+}
+
+/*
+ * Board specific information
+ */
+
+static void
+dc10_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* Pixel clock selection */
+ GPIO(zr, 4, 0);
+ GPIO(zr, 5, 1);
+ /* Enable the video bus sync signals */
+ GPIO(zr, 7, 0);
+}
+
+static void
+dc10plus_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+}
+
+static void
+buz_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* some stuff from Iomega */
+ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
+ pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
+ pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
+}
+
+static void
+lml33_init (struct zoran *zr)
+{
+ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ GPIO(zr, 2, 1); // Set Composite input/output
+}
+
+static void
+avs6eyes_init (struct zoran *zr)
+{
+ // AverMedia 6-Eyes original driver by Christer Weinigel
+
+ // Lifted straight from Christer's old driver and
+ // modified slightly by Martin Samuelsson.
+
+ int mux = default_mux; /* 1 = BT866, 7 = VID1 */
+
+ GPIO(zr, 4, 1); /* Bt866 SLEEP on */
+ udelay(2);
+
+ GPIO(zr, 0, 1); /* ZR36060 /RESET on */
+ GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */
+ GPIO(zr, 2, mux & 1); /* MUX S0 */
+ GPIO(zr, 3, 0); /* /FRAME on */
+ GPIO(zr, 4, 0); /* Bt866 SLEEP off */
+ GPIO(zr, 5, mux & 2); /* MUX S1 */
+ GPIO(zr, 6, 0); /* ? */
+ GPIO(zr, 7, mux & 4); /* MUX S2 */
+
+}
+
+static char *
+codecid_to_modulename (u16 codecid)
+{
+ char *name = NULL;
+
+ switch (codecid) {
+ case CODEC_TYPE_ZR36060:
+ name = "zr36060";
+ break;
+ case CODEC_TYPE_ZR36050:
+ name = "zr36050";
+ break;
+ case CODEC_TYPE_ZR36016:
+ name = "zr36016";
+ break;
+ }
+
+ return name;
+}
+
+// struct tvnorm {
+// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+// };
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
+
+/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
+static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
+static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
+
+/* FIXME: I cannot swap U and V in saa7114, so i do one
+ * pixel left shift in zoran (75 -> 74)
+ * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
+static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
+
+/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I
+ * copy Maxim's left shift hack for the 6 Eyes.
+ *
+ * Christer's driver used the unshifted norms, though...
+ * /Sam */
+static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
+
+static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
+static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
+static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
+static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
+static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
+static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
+static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
+static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
+static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
+
+static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ {
+ .type = DC10_old,
+ .name = "DC10(old)",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC10_new,
+ .name = "DC10(new)",
+ .i2c_decoder = "saa7110",
+ .addrs_decoder = saa7110_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel},
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1},
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC10plus,
+ .name = "DC10plus",
+ .i2c_decoder = "saa7110",
+ .addrs_decoder = saa7110_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+ &f50sqpixel
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { -1, 1 },
+ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10plus_init,
+ }, {
+ .type = DC30,
+ .name = "DC30",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = DC30plus,
+ .name = "DC30plus",
+ .i2c_decoder = "vpx3220a",
+ .addrs_decoder = vpx3220_addrs,
+ .i2c_encoder = "adv7175",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+ .inputs = 3,
+ .input = {
+ { 1, "Composite" },
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+ &f50sqpixel_dc10
+ },
+ .jpeg_int = 0,
+ .vsync_int = ZR36057_ISR_GIRQ1,
+ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
+ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
+ .gpcs = { -1, 0 },
+ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gws_not_connected = 0,
+ .input_mux = 0,
+ .init = &dc10_init,
+ }, {
+ .type = LML33,
+ .name = "LML33",
+ .i2c_decoder = "bt819a",
+ .addrs_decoder = bt819_addrs,
+ .i2c_encoder = "bt856",
+ .addrs_encoder = bt856_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lml33,
+ &f60ccir601_lml33,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &lml33_init,
+ }, {
+ .type = LML33R10,
+ .name = "LML33R10",
+ .i2c_decoder = "saa7114",
+ .addrs_decoder = saa7114_addrs,
+ .i2c_encoder = "adv7170",
+ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lm33r10,
+ &f60ccir601_lm33r10,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &lml33_init,
+ }, {
+ .type = BUZ,
+ .name = "Buz",
+ .i2c_decoder = "saa7111",
+ .addrs_decoder = saa7111_addrs,
+ .i2c_encoder = "saa7185",
+ .addrs_encoder = saa7185_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+ .input = {
+ { 3, "Composite" },
+ { 7, "S-Video" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50ccir601,
+ &f60ccir601,
+ &f50ccir601
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
+ .gpcs = { 3, 1 },
+ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
+ .gws_not_connected = 1,
+ .input_mux = 0,
+ .init = &buz_init,
+ }, {
+ .type = AVS6EYES,
+ .name = "6-Eyes",
+ /* AverMedia chose not to brand the 6-Eyes. Thus it
+ can't be autodetected, and requires card=x. */
+ .i2c_decoder = "ks0127",
+ .addrs_decoder = ks0127_addrs,
+ .i2c_encoder = "bt866",
+ .addrs_encoder = bt866_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 10,
+ .input = {
+ { 0, "Composite 1" },
+ { 1, "Composite 2" },
+ { 2, "Composite 3" },
+ { 4, "Composite 4" },
+ { 5, "Composite 5" },
+ { 6, "Composite 6" },
+ { 8, "S-Video 1" },
+ { 9, "S-Video 2" },
+ {10, "S-Video 3" },
+ {15, "YCbCr" }
+ },
+ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_avs6eyes,
+ &f60ccir601_avs6eyes,
+ NULL
+ },
+ .jpeg_int = ZR36057_ISR_GIRQ1,
+ .vsync_int = ZR36057_ISR_GIRQ0,
+ .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam
+ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam
+ .gpcs = { 3, 1 }, // Validity unknown /Sam
+ .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam
+ .gws_not_connected = 1,
+ .input_mux = 1,
+ .init = &avs6eyes_init,
+ }
+
+};
+
+/*
+ * I2C functions
+ */
+/* software I2C functions */
+static int
+zoran_i2c_getsda (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static int
+zoran_i2c_getscl (void *data)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ return btread(ZR36057_I2CBR) & 1;
+}
+
+static void
+zoran_i2c_setsda (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 2;
+ else
+ zr->i2cbr &= ~2;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static void
+zoran_i2c_setscl (void *data,
+ int state)
+{
+ struct zoran *zr = (struct zoran *) data;
+
+ if (state)
+ zr->i2cbr |= 1;
+ else
+ zr->i2cbr &= ~1;
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+}
+
+static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+ .setsda = zoran_i2c_setsda,
+ .setscl = zoran_i2c_setscl,
+ .getsda = zoran_i2c_getsda,
+ .getscl = zoran_i2c_getscl,
+ .udelay = 10,
+ .timeout = 100,
+};
+
+static int
+zoran_register_i2c (struct zoran *zr)
+{
+ memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
+ sizeof(struct i2c_algo_bit_data));
+ zr->i2c_algo.data = zr;
+ strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
+ sizeof(zr->i2c_adapter.name));
+ i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
+ zr->i2c_adapter.algo_data = &zr->i2c_algo;
+ zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
+ return i2c_bit_add_bus(&zr->i2c_adapter);
+}
+
+static void
+zoran_unregister_i2c (struct zoran *zr)
+{
+ i2c_del_adapter(&zr->i2c_adapter);
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+int
+zoran_check_jpg_settings (struct zoran *zr,
+ struct zoran_jpg_settings *settings,
+ int try)
+{
+ int err = 0, err0 = 0;
+
+ dprintk(4,
+ KERN_DEBUG
+ "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
+ ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
+ settings->VerDcm, settings->TmpDcm);
+ dprintk(4,
+ KERN_DEBUG
+ "%s: %s - x: %d, y: %d, w: %d, y: %d\n",
+ ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
+ settings->img_width, settings->img_height);
+ /* Check decimation, set default values for decimation = 1, 2, 4 */
+ switch (settings->decimation) {
+ case 1:
+
+ settings->HorDcm = 1;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 1;
+ settings->field_per_buff = 2;
+ settings->img_x = 0;
+ settings->img_y = 0;
+ settings->img_width = BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 2:
+
+ settings->HorDcm = 2;
+ settings->VerDcm = 1;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 4:
+
+ if (zr->card.type == DC10_new) {
+ dprintk(1,
+ KERN_DEBUG
+ "%s: %s - HDec by 4 is not supported on the DC10\n",
+ ZR_DEVNAME(zr), __func__);
+ err0++;
+ break;
+ }
+
+ settings->HorDcm = 4;
+ settings->VerDcm = 2;
+ settings->TmpDcm = 2;
+ settings->field_per_buff = 1;
+ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings->img_y = 0;
+ settings->img_width =
+ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ settings->img_height = BUZ_MAX_HEIGHT / 2;
+ break;
+ case 0:
+
+ /* We have to check the data the user has set */
+
+ if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
+ (zr->card.type == DC10_new || settings->HorDcm != 4)) {
+ settings->HorDcm = clamp(settings->HorDcm, 1, 2);
+ err0++;
+ }
+ if (settings->VerDcm != 1 && settings->VerDcm != 2) {
+ settings->VerDcm = clamp(settings->VerDcm, 1, 2);
+ err0++;
+ }
+ if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
+ settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
+ err0++;
+ }
+ if (settings->field_per_buff != 1 &&
+ settings->field_per_buff != 2) {
+ settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
+ err0++;
+ }
+ if (settings->img_x < 0) {
+ settings->img_x = 0;
+ err0++;
+ }
+ if (settings->img_y < 0) {
+ settings->img_y = 0;
+ err0++;
+ }
+ if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
+ settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
+ err0++;
+ }
+ if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
+ settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
+ err0++;
+ }
+ if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
+ settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
+ err0++;
+ }
+ if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
+ settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
+ err0++;
+ }
+ if (settings->img_width % (16 * settings->HorDcm) != 0) {
+ settings->img_width -= settings->img_width % (16 * settings->HorDcm);
+ if (settings->img_width == 0)
+ settings->img_width = 16 * settings->HorDcm;
+ err0++;
+ }
+ if (settings->img_height % (8 * settings->VerDcm) != 0) {
+ settings->img_height -= settings->img_height % (8 * settings->VerDcm);
+ if (settings->img_height == 0)
+ settings->img_height = 8 * settings->VerDcm;
+ err0++;
+ }
+
+ if (!try && err0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - error in params for decimation = 0\n",
+ ZR_DEVNAME(zr), __func__);
+ err++;
+ }
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
+ ZR_DEVNAME(zr), __func__, settings->decimation);
+ err++;
+ break;
+ }
+
+ if (settings->jpg_comp.quality > 100)
+ settings->jpg_comp.quality = 100;
+ if (settings->jpg_comp.quality < 5)
+ settings->jpg_comp.quality = 5;
+ if (settings->jpg_comp.APPn < 0)
+ settings->jpg_comp.APPn = 0;
+ if (settings->jpg_comp.APPn > 15)
+ settings->jpg_comp.APPn = 15;
+ if (settings->jpg_comp.APP_len < 0)
+ settings->jpg_comp.APP_len = 0;
+ if (settings->jpg_comp.APP_len > 60)
+ settings->jpg_comp.APP_len = 60;
+ if (settings->jpg_comp.COM_len < 0)
+ settings->jpg_comp.COM_len = 0;
+ if (settings->jpg_comp.COM_len > 60)
+ settings->jpg_comp.COM_len = 60;
+ if (err)
+ return -EINVAL;
+ return 0;
+}
+
+void
+zoran_open_init_params (struct zoran *zr)
+{
+ int i;
+
+ /* User must explicitly set a window */
+ zr->overlay_settings.is_set = 0;
+ zr->overlay_mask = NULL;
+ zr->overlay_active = ZORAN_FREE;
+
+ zr->v4l_memgrab_active = 0;
+ zr->v4l_overlay_active = 0;
+ zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+ zr->v4l_grab_seq = 0;
+ zr->v4l_settings.width = 192;
+ zr->v4l_settings.height = 144;
+ zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */
+ zr->v4l_settings.bytesperline =
+ zr->v4l_settings.width *
+ ((zr->v4l_settings.format->depth + 7) / 8);
+
+ /* DMA ring stuff for V4L */
+ zr->v4l_pend_tail = 0;
+ zr->v4l_pend_head = 0;
+ zr->v4l_sync_tail = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->v4l_buffers.allocated = 0;
+
+ for (i = 0; i < BUZ_MAX_FRAME; i++) {
+ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ }
+ zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ /* Set necessary params and call zoran_check_jpg_settings to set the defaults */
+ zr->jpg_settings.decimation = 1;
+ zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
+ if (zr->card.type != BUZ)
+ zr->jpg_settings.odd_even = 1;
+ else
+ zr->jpg_settings.odd_even = 0;
+ zr->jpg_settings.jpg_comp.APPn = 0;
+ zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
+ memset(zr->jpg_settings.jpg_comp.APP_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.APP_data));
+ zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
+ memset(zr->jpg_settings.jpg_comp.COM_data, 0,
+ sizeof(zr->jpg_settings.jpg_comp.COM_data));
+ zr->jpg_settings.jpg_comp.jpeg_markers =
+ V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT;
+ i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
+ if (i)
+ dprintk(1, KERN_ERR "%s: %s internal error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ clear_interrupt_counters(zr);
+ zr->testing = 0;
+}
+
+static void __devinit
+test_interrupts (struct zoran *zr)
+{
+ DEFINE_WAIT(wait);
+ int timeout, icr;
+
+ clear_interrupt_counters(zr);
+
+ zr->testing = 1;
+ icr = btread(ZR36057_ICR);
+ btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
+ timeout = schedule_timeout(HZ);
+ finish_wait(&zr->test_q, &wait);
+ btwrite(0, ZR36057_ICR);
+ btwrite(0x78000000, ZR36057_ISR);
+ zr->testing = 0;
+ dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
+ if (timeout) {
+ dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
+ }
+ if (zr36067_debug > 1)
+ print_interrupts(zr);
+ btwrite(icr, ZR36057_ICR);
+}
+
+static int __devinit
+zr36057_init (struct zoran *zr)
+{
+ int j, err;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: %s - initializing card[%d], zr=%p\n",
+ ZR_DEVNAME(zr), __func__, zr->id, zr);
+
+ /* default setup of all parameters which will persist between opens */
+ zr->user = 0;
+
+ init_waitqueue_head(&zr->v4l_capq);
+ init_waitqueue_head(&zr->jpg_capq);
+ init_waitqueue_head(&zr->test_q);
+ zr->jpg_buffers.allocated = 0;
+ zr->v4l_buffers.allocated = 0;
+
+ zr->vbuf_base = (void *) vidmem;
+ zr->vbuf_width = 0;
+ zr->vbuf_height = 0;
+ zr->vbuf_depth = 0;
+ zr->vbuf_bytesperline = 0;
+
+ /* Avoid nonsense settings from user for default input/norm */
+ if (default_norm < 0 || default_norm > 2)
+ default_norm = 0;
+ if (default_norm == 0) {
+ zr->norm = V4L2_STD_PAL;
+ zr->timing = zr->card.tvn[0];
+ } else if (default_norm == 1) {
+ zr->norm = V4L2_STD_NTSC;
+ zr->timing = zr->card.tvn[1];
+ } else {
+ zr->norm = V4L2_STD_SECAM;
+ zr->timing = zr->card.tvn[2];
+ }
+ if (zr->timing == NULL) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
+ ZR_DEVNAME(zr), __func__);
+ zr->norm = V4L2_STD_PAL;
+ zr->timing = zr->card.tvn[0];
+ }
+
+ if (default_input > zr->card.inputs-1) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: default_input value %d out of range (0-%d)\n",
+ ZR_DEVNAME(zr), default_input, zr->card.inputs-1);
+ default_input = 0;
+ }
+ zr->input = default_input;
+
+ /* default setup (will be repeated at every open) */
+ zoran_open_init_params(zr);
+
+ /* allocate memory *before* doing anything to the hardware
+ * in case allocation fails */
+ zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
+ zr->video_dev = video_device_alloc();
+ if (!zr->stat_com || !zr->video_dev) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc (STAT_COM) failed\n",
+ ZR_DEVNAME(zr), __func__);
+ err = -ENOMEM;
+ goto exit_free;
+ }
+ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+ zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
+ }
+
+ /*
+ * Now add the template and register the device unit.
+ */
+ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
+ zr->video_dev->parent = &zr->pci_dev->dev;
+ strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
+ /* It's not a mem2mem device, but you can both capture and output from
+ one and the same device. This should really be split up into two
+ device nodes, but that's a job for another day. */
+ zr->video_dev->vfl_dir = VFL_DIR_M2M;
+ err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
+ if (err < 0)
+ goto exit_free;
+ video_set_drvdata(zr->video_dev, zr);
+
+ zoran_init_hardware(zr);
+ if (zr36067_debug > 2)
+ detect_guest_activity(zr);
+ test_interrupts(zr);
+ if (!pass_through) {
+ decoder_call(zr, video, s_stream, 0);
+ encoder_call(zr, video, s_routing, 2, 0, 0);
+ }
+
+ zr->zoran_proc = NULL;
+ zr->initialized = 1;
+ return 0;
+
+exit_free:
+ kfree(zr->stat_com);
+ kfree(zr->video_dev);
+ return err;
+}
+
+static void __devexit zoran_remove(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
+ struct zoran *zr = to_zoran(v4l2_dev);
+
+ if (!zr->initialized)
+ goto exit_free;
+
+ /* unregister videocodec bus */
+ if (zr->codec) {
+ struct videocodec_master *master = zr->codec->master_data;
+
+ videocodec_detach(zr->codec);
+ kfree(master);
+ }
+ if (zr->vfe) {
+ struct videocodec_master *master = zr->vfe->master_data;
+
+ videocodec_detach(zr->vfe);
+ kfree(master);
+ }
+
+ /* unregister i2c bus */
+ zoran_unregister_i2c(zr);
+ /* disable PCI bus-mastering */
+ zoran_set_pci_master(zr, 0);
+ /* put chip into reset */
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+ /* unmap and free memory */
+ kfree(zr->stat_com);
+ zoran_proc_cleanup(zr);
+ iounmap(zr->zr36057_mem);
+ pci_disable_device(zr->pci_dev);
+ video_unregister_device(zr->video_dev);
+exit_free:
+ v4l2_device_unregister(&zr->v4l2_dev);
+ kfree(zr);
+}
+
+void
+zoran_vdev_release (struct video_device *vdev)
+{
+ kfree(vdev);
+}
+
+static struct videocodec_master * __devinit
+zoran_setup_videocodec (struct zoran *zr,
+ int type)
+{
+ struct videocodec_master *m = NULL;
+
+ m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
+ if (!m) {
+ dprintk(1, KERN_ERR "%s: %s - no memory\n",
+ ZR_DEVNAME(zr), __func__);
+ return m;
+ }
+
+ /* magic and type are unused for master struct. Makes sense only at
+ codec structs.
+ In the past, .type were initialized to the old V4L1 .hardware
+ value, as VID_HARDWARE_ZR36067
+ */
+ m->magic = 0L;
+ m->type = 0;
+
+ m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
+ strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+ m->data = zr;
+
+ switch (type)
+ {
+ case CODEC_TYPE_ZR36060:
+ m->readreg = zr36060_read;
+ m->writereg = zr36060_write;
+ m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
+ break;
+ case CODEC_TYPE_ZR36050:
+ m->readreg = zr36050_read;
+ m->writereg = zr36050_write;
+ m->flags |= CODEC_FLAG_JPEG;
+ break;
+ case CODEC_TYPE_ZR36016:
+ m->readreg = zr36016_read;
+ m->writereg = zr36016_write;
+ m->flags |= CODEC_FLAG_VFE;
+ break;
+ }
+
+ return m;
+}
+
+static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct zoran *zr = to_zoran(sd->v4l2_dev);
+
+ /* Bt819 needs to reset its FIFO buffer using #FRST pin and
+ LML33 card uses GPIO(7) for that. */
+ if (cmd == BT819_FIFO_RESET_LOW)
+ GPIO(zr, 7, 0);
+ else if (cmd == BT819_FIFO_RESET_HIGH)
+ GPIO(zr, 7, 1);
+}
+
+/*
+ * Scan for a Buz card (actually for the PCI controller ZR36057),
+ * request the irq and map the io memory
+ */
+static int __devinit zoran_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned char latency, need_latency;
+ struct zoran *zr;
+ int result;
+ struct videocodec_master *master_vfe = NULL;
+ struct videocodec_master *master_codec = NULL;
+ int card_num;
+ char *codec_name, *vfe_name;
+ unsigned int nr;
+
+
+ nr = zoran_num++;
+ if (nr >= BUZ_MAX) {
+ dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
+ ZORAN_NAME, BUZ_MAX);
+ return -ENOENT;
+ }
+
+ zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
+ if (!zr) {
+ dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
+ ZORAN_NAME, __func__);
+ return -ENOMEM;
+ }
+ zr->v4l2_dev.notify = zoran_subdev_notify;
+ if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
+ goto zr_free_mem;
+ zr->pci_dev = pdev;
+ zr->id = nr;
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
+ spin_lock_init(&zr->spinlock);
+ mutex_init(&zr->resource_lock);
+ mutex_init(&zr->other_lock);
+ if (pci_enable_device(pdev))
+ goto zr_unreg;
+ zr->revision = zr->pci_dev->revision;
+
+ dprintk(1,
+ KERN_INFO
+ "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n",
+ ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision,
+ zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0));
+ if (zr->revision >= 2) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Subsystem vendor=0x%04x id=0x%04x\n",
+ ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor,
+ zr->pci_dev->subsystem_device);
+ }
+
+ /* Use auto-detected card type? */
+ if (card[nr] == -1) {
+ if (zr->revision < 2) {
+ dprintk(1,
+ KERN_ERR
+ "%s: No card type specified, please use the card=X module parameter\n",
+ ZR_DEVNAME(zr));
+ dprintk(1,
+ KERN_ERR
+ "%s: It is not possible to auto-detect ZR36057 based cards\n",
+ ZR_DEVNAME(zr));
+ goto zr_unreg;
+ }
+
+ card_num = ent->driver_data;
+ if (card_num >= NUM_CARDS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: Unknown card, try specifying card=X module parameter\n",
+ ZR_DEVNAME(zr));
+ goto zr_unreg;
+ }
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() - card %s detected\n",
+ ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name);
+ } else {
+ card_num = card[nr];
+ if (card_num >= NUM_CARDS || card_num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: User specified card type %d out of range (0 .. %d)\n",
+ ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
+ goto zr_unreg;
+ }
+ }
+
+ /* even though we make this a non pointer and thus
+ * theoretically allow for making changes to this struct
+ * on a per-individual card basis at runtime, this is
+ * strongly discouraged. This structure is intended to
+ * keep general card information, no settings or anything */
+ zr->card = zoran_cards[card_num];
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
+ "%s[%u]", zr->card.name, zr->id);
+
+ zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
+ if (!zr->zr36057_mem) {
+ dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_unreg;
+ }
+
+ result = request_irq(zr->pci_dev->irq, zoran_irq,
+ IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr);
+ if (result < 0) {
+ if (result == -EINVAL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - bad irq number or handler\n",
+ ZR_DEVNAME(zr), __func__);
+ } else if (result == -EBUSY) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
+ ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - can't assign irq, error code %d\n",
+ ZR_DEVNAME(zr), __func__, result);
+ }
+ goto zr_unmap;
+ }
+
+ /* set PCI latency timer */
+ pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ &latency);
+ need_latency = zr->revision > 1 ? 32 : 48;
+ if (latency != need_latency) {
+ dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
+ ZR_DEVNAME(zr), latency, need_latency);
+ pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ need_latency);
+ }
+
+ zr36057_restart(zr);
+ /* i2c */
+ dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
+ ZR_DEVNAME(zr));
+
+ if (zoran_register_i2c(zr) < 0) {
+ dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_irq;
+ }
+
+ zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+ &zr->i2c_adapter, zr->card.i2c_decoder,
+ 0, zr->card.addrs_decoder);
+
+ if (zr->card.i2c_encoder)
+ zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
+ &zr->i2c_adapter, zr->card.i2c_encoder,
+ 0, zr->card.addrs_encoder);
+
+ dprintk(2,
+ KERN_INFO "%s: Initializing videocodec bus...\n",
+ ZR_DEVNAME(zr));
+
+ if (zr->card.video_codec) {
+ codec_name = codecid_to_modulename(zr->card.video_codec);
+ if (codec_name) {
+ result = request_module(codec_name);
+ if (result) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), codec_name, result);
+ }
+ }
+ }
+ if (zr->card.video_vfe) {
+ vfe_name = codecid_to_modulename(zr->card.video_vfe);
+ if (vfe_name) {
+ result = request_module(vfe_name);
+ if (result < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: failed to load modules %s: %d\n",
+ ZR_DEVNAME(zr), vfe_name, result);
+ }
+ }
+ }
+
+ /* reset JPEG codec */
+ jpeg_codec_sleep(zr, 1);
+ jpeg_codec_reset(zr);
+ /* video bus enabled */
+ /* display codec revision */
+ if (zr->card.video_codec != 0) {
+ master_codec = zoran_setup_videocodec(zr, zr->card.video_codec);
+ if (!master_codec)
+ goto zr_unreg_i2c;
+ zr->codec = videocodec_attach(master_codec);
+ if (!zr->codec) {
+ dprintk(1, KERN_ERR "%s: %s - no codec found\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_codec;
+ }
+ if (zr->codec->type != zr->card.video_codec) {
+ dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_codec;
+ }
+ }
+ if (zr->card.video_vfe != 0) {
+ master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe);
+ if (!master_vfe)
+ goto zr_detach_codec;
+ zr->vfe = videocodec_attach(master_vfe);
+ if (!zr->vfe) {
+ dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_free_vfe;
+ }
+ if (zr->vfe->type != zr->card.video_vfe) {
+ dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
+ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_vfe;
+ }
+ }
+
+ /* take care of Natoma chipset and a revision 1 zr36057 */
+ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
+ zr->jpg_buffers.need_contiguous = 1;
+ dprintk(1, KERN_INFO
+ "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+ ZR_DEVNAME(zr));
+ }
+
+ if (zr36057_init(zr) < 0)
+ goto zr_detach_vfe;
+
+ zoran_proc_init(zr);
+
+ return 0;
+
+zr_detach_vfe:
+ videocodec_detach(zr->vfe);
+zr_free_vfe:
+ kfree(master_vfe);
+zr_detach_codec:
+ videocodec_detach(zr->codec);
+zr_free_codec:
+ kfree(master_codec);
+zr_unreg_i2c:
+ zoran_unregister_i2c(zr);
+zr_free_irq:
+ btwrite(0, ZR36057_SPGPPCR);
+ free_irq(zr->pci_dev->irq, zr);
+zr_unmap:
+ iounmap(zr->zr36057_mem);
+zr_unreg:
+ v4l2_device_unregister(&zr->v4l2_dev);
+zr_free_mem:
+ kfree(zr);
+
+ return -ENODEV;
+}
+
+static struct pci_driver zoran_driver = {
+ .name = "zr36067",
+ .id_table = zr36067_pci_tbl,
+ .probe = zoran_probe,
+ .remove = __devexit_p(zoran_remove),
+};
+
+static int __init zoran_init(void)
+{
+ int res;
+
+ printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
+ ZORAN_VERSION);
+
+ /* check the parameters we have been given, adjust if necessary */
+ if (v4l_nbufs < 2)
+ v4l_nbufs = 2;
+ if (v4l_nbufs > VIDEO_MAX_FRAME)
+ v4l_nbufs = VIDEO_MAX_FRAME;
+ /* The user specfies the in KB, we want them in byte
+ * (and page aligned) */
+ v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+ if (v4l_bufsize < 32768)
+ v4l_bufsize = 32768;
+ /* 2 MB is arbitrary but sufficient for the maximum possible images */
+ if (v4l_bufsize > 2048 * 1024)
+ v4l_bufsize = 2048 * 1024;
+ if (jpg_nbufs < 4)
+ jpg_nbufs = 4;
+ if (jpg_nbufs > BUZ_MAX_FRAME)
+ jpg_nbufs = BUZ_MAX_FRAME;
+ jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
+ if (jpg_bufsize < 8192)
+ jpg_bufsize = 8192;
+ if (jpg_bufsize > (512 * 1024))
+ jpg_bufsize = 512 * 1024;
+ /* Use parameter for vidmem or try to find a video card */
+ if (vidmem) {
+ dprintk(1,
+ KERN_INFO
+ "%s: Using supplied video memory base address @ 0x%lx\n",
+ ZORAN_NAME, vidmem);
+ }
+
+ /* some mainboards might not do PCI-PCI data transfer well */
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: chipset does not support reliable PCI-PCI DMA\n",
+ ZORAN_NAME);
+ }
+
+ res = pci_register_driver(&zoran_driver);
+ if (res) {
+ dprintk(1,
+ KERN_ERR
+ "%s: Unable to register ZR36057 driver\n",
+ ZORAN_NAME);
+ return res;
+ }
+
+ return 0;
+}
+
+static void __exit zoran_exit(void)
+{
+ pci_unregister_driver(&zoran_driver);
+}
+
+module_init(zoran_init);
+module_exit(zoran_exit);
diff --git a/drivers/media/video/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h
index 4936fead73e8..4936fead73e8 100644
--- a/drivers/media/video/zoran/zoran_card.h
+++ b/drivers/media/pci/zoran/zoran_card.h
diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c
index a4cd504b8eee..a4cd504b8eee 100644
--- a/drivers/media/video/zoran/zoran_device.c
+++ b/drivers/media/pci/zoran/zoran_device.c
diff --git a/drivers/media/video/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h
index 07f2c23ff740..07f2c23ff740 100644
--- a/drivers/media/video/zoran/zoran_device.h
+++ b/drivers/media/pci/zoran/zoran_device.h
diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c
new file mode 100644
index 000000000000..53f12c7466b0
--- /dev/null
+++ b/drivers/media/pci/zoran/zoran_driver.c
@@ -0,0 +1,3090 @@
+/*
+ * Zoran zr36057/zr36067 PCI controller driver, for the
+ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
+ * Media Labs LML33/LML33R10.
+ *
+ * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+ *
+ * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
+ *
+ * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
+ *
+ * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * Based on
+ *
+ * Miro DC10 driver
+ * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+ *
+ * Iomega Buz driver version 1.0
+ * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+ *
+ * buz.0.0.3
+ * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+ *
+ * bttv - Bt848 frame grabber driver
+ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ * & Marcus Metzler (mocm@thp.uni-koeln.de)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <linux/spinlock.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "videocodec.h"
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include <linux/mutex.h>
+#include "zoran.h"
+#include "zoran_device.h"
+#include "zoran_card.h"
+
+
+const struct zoran_format zoran_formats[] = {
+ {
+ .name = "15-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif|
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "15-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "16-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif|
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "16-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "24-bit RGB",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 24,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
+ }, {
+ .name = "32-bit RGB LE",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "32-bit RGB BE",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "Hardware-encoded Motion-JPEG",
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 0,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_PLAYBACK |
+ ZORAN_FORMAT_COMPRESSED,
+ }
+};
+#define NUM_FORMATS ARRAY_SIZE(zoran_formats)
+
+ /* small helper function for calculating buffersizes for v4l2
+ * we calculate the nearest higher power-of-two, which
+ * will be the recommended buffersize */
+static __u32
+zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
+{
+ __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
+ __u32 num = (1024 * 512) / (div);
+ __u32 result = 2;
+
+ num--;
+ while (num) {
+ num >>= 1;
+ result <<= 1;
+ }
+
+ if (result > jpg_bufsize)
+ return jpg_bufsize;
+ if (result < 8192)
+ return 8192;
+ return result;
+}
+
+/* forward references */
+static void v4l_fbuffer_free(struct zoran_fh *fh);
+static void jpg_fbuffer_free(struct zoran_fh *fh);
+
+/* Set mapping mode */
+static void map_mode_raw(struct zoran_fh *fh)
+{
+ fh->map_mode = ZORAN_MAP_MODE_RAW;
+ fh->buffers.buffer_size = v4l_bufsize;
+ fh->buffers.num_buffers = v4l_nbufs;
+}
+static void map_mode_jpg(struct zoran_fh *fh, int play)
+{
+ fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
+ fh->buffers.buffer_size = jpg_bufsize;
+ fh->buffers.num_buffers = jpg_nbufs;
+}
+static inline const char *mode_name(enum zoran_map_mode mode)
+{
+ return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
+}
+
+/*
+ * Allocate the V4L grab buffers
+ *
+ * These have to be pysically contiguous.
+ */
+
+static int v4l_fbuffer_alloc(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].v4l.fbuffer)
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - buffer %d already allocated!?\n",
+ ZR_DEVNAME(zr), __func__, i);
+
+ //udelay(20);
+ mem = kmalloc(fh->buffers.buffer_size,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!mem) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc for V4L buf %d failed\n",
+ ZR_DEVNAME(zr), __func__, i);
+ v4l_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].v4l.fbuffer = mem;
+ fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
+ fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
+ for (off = 0; off < fh->buffers.buffer_size;
+ off += PAGE_SIZE)
+ SetPageReserved(virt_to_page(mem + off));
+ dprintk(4,
+ KERN_INFO
+ "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
+ ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
+ (unsigned long long)virt_to_bus(mem));
+ }
+
+ fh->buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the V4L grab buffers */
+static void v4l_fbuffer_free(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+ dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (!fh->buffers.buffer[i].v4l.fbuffer)
+ continue;
+
+ mem = fh->buffers.buffer[i].v4l.fbuffer;
+ for (off = 0; off < fh->buffers.buffer_size;
+ off += PAGE_SIZE)
+ ClearPageReserved(virt_to_page(mem + off));
+ kfree(fh->buffers.buffer[i].v4l.fbuffer);
+ fh->buffers.buffer[i].v4l.fbuffer = NULL;
+ }
+
+ fh->buffers.allocated = 0;
+}
+
+/*
+ * Allocate the MJPEG grab buffers.
+ *
+ * If a Natoma chipset is present and this is a revision 1 zr36057,
+ * each MJPEG buffer needs to be physically contiguous.
+ * (RJ: This statement is from Dave Perks' original driver,
+ * I could never check it because I have a zr36067)
+ *
+ * RJ: The contents grab buffers needs never be accessed in the driver.
+ * Therefore there is no need to allocate them with vmalloc in order
+ * to get a contiguous virtual memory space.
+ * I don't understand why many other drivers first allocate them with
+ * vmalloc (which uses internally also get_zeroed_page, but delivers you
+ * virtual addresses) and then again have to make a lot of efforts
+ * to get the physical address.
+ *
+ * Ben Capper:
+ * On big-endian architectures (such as ppc) some extra steps
+ * are needed. When reading and writing to the stat_com array
+ * and fragment buffers, the device expects to see little-
+ * endian values. The use of cpu_to_le32() and le32_to_cpu()
+ * in this function (and one or two others in zoran_device.c)
+ * ensure that these values are always stored in little-endian
+ * form, regardless of architecture. The zr36057 does Very Bad
+ * Things on big endian architectures if the stat_com array
+ * and fragment buffers are not little-endian.
+ */
+
+static int jpg_fbuffer_alloc(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ u8 *mem;
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].jpg.frag_tab)
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - buffer %d already allocated!?\n",
+ ZR_DEVNAME(zr), __func__, i);
+
+ /* Allocate fragment table for this buffer */
+
+ mem = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!mem) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
+ fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
+
+ if (fh->buffers.need_contiguous) {
+ mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
+ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - kmalloc failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+ fh->buffers.buffer[i].jpg.frag_tab[0] =
+ cpu_to_le32(virt_to_bus(mem));
+ fh->buffers.buffer[i].jpg.frag_tab[1] =
+ cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
+ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+ SetPageReserved(virt_to_page(mem + off));
+ } else {
+ /* jpg_bufsize is already page aligned */
+ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+ mem = (void *)get_zeroed_page(GFP_KERNEL);
+ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - get_zeroed_page failed for buffer %d\n",
+ ZR_DEVNAME(zr), __func__, i);
+ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
+ cpu_to_le32(virt_to_bus(mem));
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
+ cpu_to_le32((PAGE_SIZE >> 2) << 1);
+ SetPageReserved(virt_to_page(mem));
+ }
+
+ fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
+ }
+ }
+
+ dprintk(4,
+ KERN_DEBUG "%s: %s - %d KB allocated\n",
+ ZR_DEVNAME(zr), __func__,
+ (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
+
+ fh->buffers.allocated = 1;
+
+ return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void jpg_fbuffer_free(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ unsigned char *mem;
+ __le32 frag_tab;
+ struct zoran_buffer *buffer;
+
+ dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ for (i = 0, buffer = &fh->buffers.buffer[0];
+ i < fh->buffers.num_buffers; i++, buffer++) {
+ if (!buffer->jpg.frag_tab)
+ continue;
+
+ if (fh->buffers.need_contiguous) {
+ frag_tab = buffer->jpg.frag_tab[0];
+
+ if (frag_tab) {
+ mem = bus_to_virt(le32_to_cpu(frag_tab));
+ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
+ ClearPageReserved(virt_to_page(mem + off));
+ kfree(mem);
+ buffer->jpg.frag_tab[0] = 0;
+ buffer->jpg.frag_tab[1] = 0;
+ }
+ } else {
+ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
+ frag_tab = buffer->jpg.frag_tab[2 * j];
+
+ if (!frag_tab)
+ break;
+ ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
+ free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
+ buffer->jpg.frag_tab[2 * j] = 0;
+ buffer->jpg.frag_tab[2 * j + 1] = 0;
+ }
+ }
+
+ free_page((unsigned long)buffer->jpg.frag_tab);
+ buffer->jpg.frag_tab = NULL;
+ }
+
+ fh->buffers.allocated = 0;
+}
+
+/*
+ * V4L Buffer grabbing
+ */
+
+static int
+zoran_v4l_set_format (struct zoran_fh *fh,
+ int width,
+ int height,
+ const struct zoran_format *format)
+{
+ struct zoran *zr = fh->zr;
+ int bpp;
+
+ /* Check size and format of the grab wanted */
+
+ if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
+ height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong frame size (%dx%d)\n",
+ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+ bpp = (format->depth + 7) / 8;
+
+ /* Check against available buffer size */
+ if (height * width * bpp > fh->buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - video buffer size (%d kB) is too small\n",
+ ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
+ return -EINVAL;
+ }
+
+ /* The video front end needs 4-byte alinged line sizes */
+
+ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong frame alignment\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ fh->v4l_settings.width = width;
+ fh->v4l_settings.height = height;
+ fh->v4l_settings.format = format;
+ fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
+
+ return 0;
+}
+
+static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer %d is out of range\n",
+ ZR_DEVNAME(zr), __func__, num);
+ res = -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ zr->v4l_buffers = fh->buffers;
+ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - another session is already capturing\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+
+ /* make sure a grab isn't going on currently with this buffer */
+ if (!res) {
+ switch (zr->v4l_buffers.buffer[num].state) {
+ default:
+ case BUZ_STATE_PEND:
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+ fh->buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - queueing buffer %d in state DONE!?\n",
+ ZR_DEVNAME(zr), __func__, num);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at least
+ * one more pend[] entry */
+ zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
+ zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
+ zr->v4l_buffers.buffer[num].bs.length =
+ fh->v4l_settings.bytesperline *
+ zr->v4l_settings.height;
+ fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->v4l_buffers.active == ZORAN_FREE)
+ zr->v4l_buffers.active = fh->buffers.active;
+
+ return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int v4l_sync(struct zoran_fh *fh, int frame)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no grab active for this session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ /* check passed-in frame number */
+ if (frame >= fh->buffers.num_buffers || frame < 0) {
+ dprintk(1,
+ KERN_ERR "%s: %s - frame %d is invalid\n",
+ ZR_DEVNAME(zr), __func__, frame);
+ return -EINVAL;
+ }
+
+ /* Check if is buffer was queued at all */
+ if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - attempt to sync on a buffer which was not queued?\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EPROTO;
+ }
+
+ /* wait on this buffer to get ready */
+ if (!wait_event_interruptible_timeout(zr->v4l_capq,
+ (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ))
+ return -ETIME;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR "%s: %s - internal state error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ /* Check if streaming capture has finished */
+ if (zr->v4l_pend_tail == zr->v4l_pend_head) {
+ zr36057_set_memgrab(zr, 0);
+ if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
+ fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+/*
+ * Queue a MJPEG buffer for capture/playback
+ */
+
+static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
+ enum zoran_codec_mode mode)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ /* Check if buffers are allocated */
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer %d out of range\n",
+ ZR_DEVNAME(zr), __func__, num);
+ return -EINVAL;
+ }
+
+ /* what is the codec mode right now? */
+ if (zr->codec_mode == BUZ_MODE_IDLE) {
+ zr->jpg_settings = fh->jpg_settings;
+ } else if (zr->codec_mode != mode) {
+ /* wrong codec mode active - invalid */
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - codec in wrong mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ zr->jpg_buffers = fh->buffers;
+ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - another session is already capturing\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+
+ if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
+ /* Ok load up the jpeg codec */
+ zr36057_enable_jpg(zr, mode);
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (!res) {
+ switch (zr->jpg_buffers.buffer[num].state) {
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+ "%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
+ ZR_DEVNAME(zr), __func__);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at
+ *least one more pend[] entry */
+ zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
+ zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
+ fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
+ zoran_feed_stat_com(zr);
+ break;
+ default:
+ case BUZ_STATE_DMA:
+ case BUZ_STATE_PEND:
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+ fh->buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->jpg_buffers.active == ZORAN_FREE)
+ zr->jpg_buffers.active = fh->buffers.active;
+
+ return res;
+}
+
+static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
+{
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ /* Does the user want to stop streaming? */
+ if (frame < 0) {
+ if (zr->codec_mode == mode) {
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(-1) - session not active\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ return 0;
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - stop streaming but not in streaming mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ }
+
+ if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
+ return res;
+
+ /* Start the jpeg codec when the first frame is queued */
+ if (!res && zr->jpg_que_head == 1)
+ jpeg_start(zr);
+
+ return res;
+}
+
+/*
+ * Sync on a MJPEG buffer
+ */
+
+static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int frame;
+
+ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - capture is not currently active\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
+ zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - codec not in streaming mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!wait_event_interruptible_timeout(zr->jpg_capq,
+ (zr->jpg_que_tail != zr->jpg_dma_tail ||
+ zr->jpg_dma_tail == zr->jpg_dma_head),
+ 10*HZ)) {
+ int isr;
+
+ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+ udelay(1);
+ zr->codec->control(zr->codec, CODEC_G_STATUS,
+ sizeof(isr), &isr);
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - timeout: codec isr=0x%02x\n",
+ ZR_DEVNAME(zr), __func__, isr);
+
+ return -ETIME;
+
+ }
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+ if (zr->jpg_dma_tail != zr->jpg_dma_head)
+ frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+ else
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+ KERN_ERR "%s: %s - internal state error\n",
+ ZR_DEVNAME(zr), __func__);
+
+ *bs = zr->jpg_buffers.buffer[frame].bs;
+ bs->frame = frame;
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
+ fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+}
+
+static void zoran_open_init_session(struct zoran_fh *fh)
+{
+ int i;
+ struct zoran *zr = fh->zr;
+
+ /* Per default, map the V4L Buffers */
+ map_mode_raw(fh);
+
+ /* take over the card's current settings */
+ fh->overlay_settings = zr->overlay_settings;
+ fh->overlay_settings.is_set = 0;
+ fh->overlay_settings.format = zr->overlay_settings.format;
+ fh->overlay_active = ZORAN_FREE;
+
+ /* v4l settings */
+ fh->v4l_settings = zr->v4l_settings;
+ /* jpg settings */
+ fh->jpg_settings = zr->jpg_settings;
+
+ /* buffers */
+ memset(&fh->buffers, 0, sizeof(fh->buffers));
+ for (i = 0; i < MAX_FRAME; i++) {
+ fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+ fh->buffers.buffer[i].bs.frame = i;
+ }
+ fh->buffers.allocated = 0;
+ fh->buffers.active = ZORAN_FREE;
+}
+
+static void zoran_close_end_session(struct zoran_fh *fh)
+{
+ struct zoran *zr = fh->zr;
+
+ /* overlay */
+ if (fh->overlay_active != ZORAN_FREE) {
+ fh->overlay_active = zr->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ }
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ /* v4l capture */
+ if (fh->buffers.active != ZORAN_FREE) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ }
+
+ /* v4l buffers */
+ if (fh->buffers.allocated)
+ v4l_fbuffer_free(fh);
+ } else {
+ /* jpg capture */
+ if (fh->buffers.active != ZORAN_FREE) {
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+ }
+
+ /* jpg buffers */
+ if (fh->buffers.allocated)
+ jpg_fbuffer_free(fh);
+ }
+}
+
+/*
+ * Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int zoran_open(struct file *file)
+{
+ struct zoran *zr = video_drvdata(file);
+ struct zoran_fh *fh;
+ int res, first_open = 0;
+
+ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
+ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
+
+ mutex_lock(&zr->other_lock);
+
+ if (zr->user >= 2048) {
+ dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
+ ZR_DEVNAME(zr), zr->user);
+ res = -EBUSY;
+ goto fail_unlock;
+ }
+
+ /* now, create the open()-specific file_ops struct */
+ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
+ if (!fh) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - allocation of zoran_fh failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ goto fail_unlock;
+ }
+ /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
+ * on norm-change! */
+ fh->overlay_mask =
+ kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+ if (!fh->overlay_mask) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - allocation of overlay_mask failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ goto fail_fh;
+ }
+
+ if (zr->user++ == 0)
+ first_open = 1;
+
+ /*mutex_unlock(&zr->resource_lock);*/
+
+ /* default setup - TODO: look at flags */
+ if (first_open) { /* First device open */
+ zr36057_restart(zr);
+ zoran_open_init_params(zr);
+ zoran_init_hardware(zr);
+
+ btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+ }
+
+ /* set file_ops stuff */
+ file->private_data = fh;
+ fh->zr = zr;
+ zoran_open_init_session(fh);
+ mutex_unlock(&zr->other_lock);
+
+ return 0;
+
+fail_fh:
+ kfree(fh);
+fail_unlock:
+ mutex_unlock(&zr->other_lock);
+
+ dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n",
+ ZR_DEVNAME(zr), res, zr->user);
+
+ return res;
+}
+
+static int
+zoran_close(struct file *file)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
+ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
+
+ /* kernel locks (fs/device.c), so don't do that ourselves
+ * (prevents deadlocks) */
+ mutex_lock(&zr->other_lock);
+
+ zoran_close_end_session(fh);
+
+ if (zr->user-- == 1) { /* Last process */
+ /* Clean up JPEG process */
+ wake_up_interruptible(&zr->jpg_capq);
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = ZORAN_FREE;
+
+ /* disable interrupts */
+ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+ if (zr36067_debug > 1)
+ print_interrupts(zr);
+
+ /* Overlay off */
+ zr->v4l_overlay_active = 0;
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+
+ /* capture off */
+ wake_up_interruptible(&zr->v4l_capq);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = ZORAN_FREE;
+ zoran_set_pci_master(zr, 0);
+
+ if (!pass_through) { /* Switch to color bar */
+ decoder_call(zr, video, s_stream, 0);
+ encoder_call(zr, video, s_routing, 2, 0, 0);
+ }
+ }
+ mutex_unlock(&zr->other_lock);
+
+ file->private_data = NULL;
+ kfree(fh->overlay_mask);
+ kfree(fh);
+
+ dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
+
+ return 0;
+}
+
+
+static ssize_t
+zoran_read (struct file *file,
+ char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* we simply don't support read() (yet)... */
+
+ return -EINVAL;
+}
+
+static ssize_t
+zoran_write (struct file *file,
+ const char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ /* ...and the same goes for write() */
+
+ return -EINVAL;
+}
+
+static int setup_fbuffer(struct zoran_fh *fh,
+ void *base,
+ const struct zoran_format *fmt,
+ int width,
+ int height,
+ int bytesperline)
+{
+ struct zoran *zr = fh->zr;
+
+ /* (Ronald) v4l/v4l2 guidelines */
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
+ ALi Magik (that needs very low latency while the card needs a
+ higher value always) */
+
+ if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
+ return -ENXIO;
+
+ /* we need a bytesperline value, even if not given */
+ if (!bytesperline)
+ bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
+
+#if 0
+ if (zr->overlay_active) {
+ /* dzjee... stupid users... don't even bother to turn off
+ * overlay before changing the memory location...
+ * normally, we would return errors here. However, one of
+ * the tools that does this is... xawtv! and since xawtv
+ * is used by +/- 99% of the users, we'd rather be user-
+ * friendly and silently do as if nothing went wrong */
+ dprintk(3,
+ KERN_ERR
+ "%s: %s - forced overlay turnoff because framebuffer changed\n",
+ ZR_DEVNAME(zr), __func__);
+ zr36057_overlay(zr, 0);
+ }
+#endif
+
+ if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no valid overlay format given\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (height <= 0 || width <= 0 || bytesperline <= 0) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
+ ZR_DEVNAME(zr), __func__, width, height, bytesperline);
+ return -EINVAL;
+ }
+ if (bytesperline & 3) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - bytesperline (%d) must be 4-byte aligned\n",
+ ZR_DEVNAME(zr), __func__, bytesperline);
+ return -EINVAL;
+ }
+
+ zr->vbuf_base = (void *) ((unsigned long) base & ~3);
+ zr->vbuf_height = height;
+ zr->vbuf_width = width;
+ zr->vbuf_depth = fmt->depth;
+ zr->overlay_settings.format = fmt;
+ zr->vbuf_bytesperline = bytesperline;
+
+ /* The user should set new window parameters */
+ zr->overlay_settings.is_set = 0;
+
+ return 0;
+}
+
+
+static int setup_window(struct zoran_fh *fh,
+ int x,
+ int y,
+ int width,
+ int height,
+ struct v4l2_clip __user *clips,
+ unsigned int clipcount,
+ void __user *bitmap)
+{
+ struct zoran *zr = fh->zr;
+ struct v4l2_clip *vcp = NULL;
+ int on, end;
+
+
+ if (!zr->vbuf_base) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - frame buffer has to be set first\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no overlay format set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (clipcount > 2048) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - invalid clipcount\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * The video front end needs 4-byte alinged line sizes, we correct that
+ * silently here if necessary
+ */
+ if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
+ end = (x + width) & ~1; /* round down */
+ x = (x + 1) & ~1; /* round up */
+ width = end - x;
+ }
+
+ if (zr->vbuf_depth == 24) {
+ end = (x + width) & ~3; /* round down */
+ x = (x + 3) & ~3; /* round up */
+ width = end - x;
+ }
+
+ if (width > BUZ_MAX_WIDTH)
+ width = BUZ_MAX_WIDTH;
+ if (height > BUZ_MAX_HEIGHT)
+ height = BUZ_MAX_HEIGHT;
+
+ /* Check for invalid parameters */
+ if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
+ width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - width = %d or height = %d invalid\n",
+ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+ fh->overlay_settings.x = x;
+ fh->overlay_settings.y = y;
+ fh->overlay_settings.width = width;
+ fh->overlay_settings.height = height;
+ fh->overlay_settings.clipcount = clipcount;
+
+ /*
+ * If an overlay is running, we have to switch it off
+ * and switch it on again in order to get the new settings in effect.
+ *
+ * We also want to avoid that the overlay mask is written
+ * when an overlay is running.
+ */
+
+ on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
+ zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active != ZORAN_FREE;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ /*
+ * Write the overlay mask if clips are wanted.
+ * We prefer a bitmap.
+ */
+ if (bitmap) {
+ /* fake value - it just means we want clips */
+ fh->overlay_settings.clipcount = 1;
+
+ if (copy_from_user(fh->overlay_mask, bitmap,
+ (width * height + 7) / 8)) {
+ return -EFAULT;
+ }
+ } else if (clipcount) {
+ /* write our own bitmap from the clips */
+ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
+ if (vcp == NULL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - Alloc of clip mask failed\n",
+ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user
+ (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
+ vfree(vcp);
+ return -EFAULT;
+ }
+ write_overlay_mask(fh, vcp, clipcount);
+ vfree(vcp);
+ }
+
+ fh->overlay_settings.is_set = 1;
+ if (fh->overlay_active != ZORAN_FREE &&
+ zr->overlay_active != ZORAN_FREE)
+ zr->overlay_settings = fh->overlay_settings;
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+static int setup_overlay(struct zoran_fh *fh, int on)
+{
+ struct zoran *zr = fh->zr;
+
+ /* If there is nothing to do, return immediately */
+ if ((on && fh->overlay_active != ZORAN_FREE) ||
+ (!on && fh->overlay_active == ZORAN_FREE))
+ return 0;
+
+ /* check whether we're touching someone else's overlay */
+ if (on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - overlay is already active for another session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+ if (!on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - you cannot cancel someone else's session\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EPERM;
+ }
+
+ if (on == 0) {
+ zr->overlay_active = fh->overlay_active = ZORAN_FREE;
+ zr->v4l_overlay_active = 0;
+ /* When a grab is running, the video simply
+ * won't be switched on any more */
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ } else {
+ if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - buffer or window not set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no overlay format set\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
+ zr->v4l_overlay_active = 1;
+ zr->overlay_mask = fh->overlay_mask;
+ zr->overlay_settings = fh->overlay_settings;
+ if (!zr->v4l_memgrab_active)
+ zr36057_overlay(zr, 1);
+ /* When a grab is running, the video will be
+ * switched on when grab is finished */
+ }
+
+ /* Make sure the changes come into effect */
+ return wait_grab_pending(zr);
+}
+
+/* get the status of a buffer in the clients buffer queue */
+static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
+ struct v4l2_buffer *buf, int num)
+{
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ /* check range */
+ if (num < 0 || num >= fh->buffers.num_buffers ||
+ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], num,
+ "UPMD"[zr->v4l_buffers.buffer[num].state],
+ fh->buffers.buffer[num].map ? 'Y' : 'N');
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf->length = fh->buffers.buffer_size;
+
+ /* get buffer */
+ buf->bytesused = fh->buffers.buffer[num].bs.length;
+ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->buffers.buffer[num].bs.seq;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
+ buf->field = V4L2_FIELD_TOP;
+ else
+ buf->field = V4L2_FIELD_INTERLACED;
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ /* check range */
+ if (num < 0 || num >= fh->buffers.num_buffers ||
+ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - wrong number or buffers not allocated\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ buf->length = fh->buffers.buffer_size;
+
+ /* these variables are only written after frame has been captured */
+ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
+ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
+ buf->sequence = fh->buffers.buffer[num].bs.seq;
+ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ buf->bytesused = fh->buffers.buffer[num].bs.length;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+
+ /* which fields are these? */
+ if (fh->jpg_settings.TmpDcm != 1)
+ buf->field = fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+ else
+ buf->field = fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
+
+ break;
+
+ default:
+
+ dprintk(5,
+ KERN_ERR
+ "%s: %s - invalid buffer type|map_mode (%d|%d)\n",
+ ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
+ return -EINVAL;
+ }
+
+ buf->memory = V4L2_MEMORY_MMAP;
+ buf->index = num;
+ buf->m.offset = buf->length * num;
+
+ return 0;
+}
+
+static int
+zoran_set_norm (struct zoran *zr,
+ v4l2_std_id norm)
+{
+ int on;
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s called while in playback/capture mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+ if (!(norm & zr->card.norms)) {
+ dprintk(1,
+ KERN_ERR "%s: %s - unsupported norm %llx\n",
+ ZR_DEVNAME(zr), __func__, norm);
+ return -EINVAL;
+ }
+
+ if (norm == V4L2_STD_ALL) {
+ unsigned int status = 0;
+ v4l2_std_id std = 0;
+
+ decoder_call(zr, video, querystd, &std);
+ decoder_call(zr, core, s_std, std);
+
+ /* let changes come into effect */
+ ssleep(2);
+
+ decoder_call(zr, video, g_input_status, &status);
+ if (status & V4L2_IN_ST_NO_SIGNAL) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no norm detected\n",
+ ZR_DEVNAME(zr), __func__);
+ /* reset norm */
+ decoder_call(zr, core, s_std, zr->norm);
+ return -EIO;
+ }
+
+ norm = std;
+ }
+ if (norm & V4L2_STD_SECAM)
+ zr->timing = zr->card.tvn[2];
+ else if (norm & V4L2_STD_NTSC)
+ zr->timing = zr->card.tvn[1];
+ else
+ zr->timing = zr->card.tvn[0];
+
+ /* We switch overlay off and on since a change in the
+ * norm needs different VFE settings */
+ on = zr->overlay_active && !zr->v4l_memgrab_active;
+ if (on)
+ zr36057_overlay(zr, 0);
+
+ decoder_call(zr, core, s_std, norm);
+ encoder_call(zr, video, s_std_output, norm);
+
+ if (on)
+ zr36057_overlay(zr, 1);
+
+ /* Make sure the changes come into effect */
+ zr->norm = norm;
+
+ return 0;
+}
+
+static int
+zoran_set_input (struct zoran *zr,
+ int input)
+{
+ if (input == zr->input) {
+ return 0;
+ }
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+ "%s: %s called while in playback/capture mode\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+ if (input < 0 || input >= zr->card.inputs) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - unnsupported input %d\n",
+ ZR_DEVNAME(zr), __func__, input);
+ return -EINVAL;
+ }
+
+ zr->input = input;
+
+ decoder_call(zr, video, s_routing,
+ zr->card.input[input].muxsel, 0, 0);
+
+ return 0;
+}
+
+/*
+ * ioctl routine
+ */
+
+static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ memset(cap, 0, sizeof(*cap));
+ strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
+ strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+ pci_name(zr->pci_dev));
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
+ return 0;
+}
+
+static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
+{
+ unsigned int num, i;
+
+ for (num = i = 0; i < NUM_FORMATS; i++) {
+ if (zoran_formats[i].flags & flag && num++ == fmt->index) {
+ strncpy(fmt->description, zoran_formats[i].name,
+ sizeof(fmt->description) - 1);
+ /* fmt struct pre-zeroed, so adding '\0' not needed */
+ fmt->pixelformat = zoran_formats[i].fourcc;
+ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
+}
+
+static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
+}
+
+static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
+}
+
+static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
+ fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
+ (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
+ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+ if (fh->jpg_settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ if (fh->map_mode != ZORAN_MAP_MODE_RAW)
+ return zoran_g_fmt_vid_out(file, fh, fmt);
+
+ mutex_lock(&zr->resource_lock);
+ fmt->fmt.pix.width = fh->v4l_settings.width;
+ fmt->fmt.pix.height = fh->v4l_settings.height;
+ fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
+ fh->v4l_settings.height;
+ fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
+ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field = V4L2_FIELD_TOP;
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ fmt->fmt.win.w.left = fh->overlay_settings.x;
+ fmt->fmt.win.w.top = fh->overlay_settings.y;
+ fmt->fmt.win.w.width = fh->overlay_settings.width;
+ fmt->fmt.win.w.height = fh->overlay_settings.height;
+ if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
+ fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.win.field = V4L2_FIELD_TOP;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
+ if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
+ fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
+ if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
+ if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
+ fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
+
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ struct zoran_jpg_settings settings;
+ int res = 0;
+
+ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ settings = fh->jpg_settings;
+
+ /* we actually need to set 'real' parameters now */
+ if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ if (settings.HorDcm > 1) {
+ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ } else {
+ settings.img_x = 0;
+ settings.img_width = BUZ_MAX_WIDTH;
+ }
+
+ /* check */
+ res = zoran_check_jpg_settings(zr, &settings, 1);
+ if (res)
+ goto tryfmt_unlock_and_return;
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height = settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+tryfmt_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int bpp;
+ int i;
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+ return zoran_try_fmt_vid_out(file, fh, fmt);
+
+ mutex_lock(&zr->resource_lock);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
+ break;
+
+ if (i == NUM_FORMATS) {
+ mutex_unlock(&zr->resource_lock);
+ return -EINVAL;
+ }
+
+ bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8);
+ v4l_bound_align_image(
+ &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2,
+ &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
+ fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width,
+ fmt->fmt.win.w.height,
+ fmt->fmt.win.clipcount,
+ fmt->fmt.win.bitmap);
+ mutex_lock(&zr->resource_lock);
+ res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+ fmt->fmt.win.w.width, fmt->fmt.win.w.height,
+ (struct v4l2_clip __user *)fmt->fmt.win.clips,
+ fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
+ struct zoran_jpg_settings settings;
+ int res = 0;
+
+ dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
+ fmt->fmt.pix.width, fmt->fmt.pix.height,
+ fmt->fmt.pix.pixelformat,
+ (char *) &printformat);
+ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.allocated) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtjpg_unlock_and_return;
+ }
+
+ settings = fh->jpg_settings;
+
+ /* we actually need to set 'real' parameters now */
+ if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
+ settings.TmpDcm = 1;
+ else
+ settings.TmpDcm = 2;
+ settings.decimation = 0;
+ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
+ settings.VerDcm = 2;
+ else
+ settings.VerDcm = 1;
+ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
+ settings.HorDcm = 4;
+ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
+ settings.HorDcm = 2;
+ else
+ settings.HorDcm = 1;
+ if (settings.TmpDcm == 1)
+ settings.field_per_buff = 2;
+ else
+ settings.field_per_buff = 1;
+
+ if (settings.HorDcm > 1) {
+ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
+ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
+ } else {
+ settings.img_x = 0;
+ settings.img_width = BUZ_MAX_WIDTH;
+ }
+
+ /* check */
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto sfmtjpg_unlock_and_return;
+
+ /* it's ok, so set them */
+ fh->jpg_settings = settings;
+
+ map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+ /* tell the user what we actually did */
+ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
+ fmt->fmt.pix.height = settings.img_height * 2 /
+ (settings.TmpDcm * settings.VerDcm);
+ if (settings.TmpDcm == 1)
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
+ else
+ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
+ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+ fmt->fmt.pix.bytesperline = 0;
+ fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
+ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+sfmtjpg_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *fmt)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i;
+ int res = 0;
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
+ return zoran_s_fmt_vid_out(file, fh, fmt);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
+ break;
+ if (i == NUM_FORMATS) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
+ ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
+ fh->buffers.active != ZORAN_FREE) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sfmtv4l_unlock_and_return;
+ }
+ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+ fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
+ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+
+ map_mode_raw(fh);
+
+ res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
+ &zoran_formats[i]);
+ if (res)
+ goto sfmtv4l_unlock_and_return;
+
+ /* tell the user the results/missing stuff */
+ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
+ fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
+ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
+ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
+ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else
+ fmt->fmt.pix.field = V4L2_FIELD_TOP;
+
+sfmtv4l_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_fbuf(struct file *file, void *__fh,
+ struct v4l2_framebuffer *fb)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ memset(fb, 0, sizeof(*fb));
+ mutex_lock(&zr->resource_lock);
+ fb->base = zr->vbuf_base;
+ fb->fmt.width = zr->vbuf_width;
+ fb->fmt.height = zr->vbuf_height;
+ if (zr->overlay_settings.format)
+ fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
+ fb->fmt.bytesperline = zr->vbuf_bytesperline;
+ mutex_unlock(&zr->resource_lock);
+ fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+ fb->fmt.field = V4L2_FIELD_INTERLACED;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ return 0;
+}
+
+static int zoran_s_fbuf(struct file *file, void *__fh,
+ const struct v4l2_framebuffer *fb)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i, res = 0;
+ __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+
+ for (i = 0; i < NUM_FORMATS; i++)
+ if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+ break;
+ if (i == NUM_FORMATS) {
+ dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
+ ZR_DEVNAME(zr), fb->fmt.pixelformat,
+ (char *)&printformat);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
+ fb->fmt.height, fb->fmt.bytesperline);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = setup_overlay(fh, on);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
+
+static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ if (req->memory != V4L2_MEMORY_MMAP) {
+ dprintk(2,
+ KERN_ERR
+ "%s: only MEMORY_MMAP capture is supported, not %d\n",
+ ZR_DEVNAME(zr), req->memory);
+ return -EINVAL;
+ }
+
+ if (req->count == 0)
+ return zoran_streamoff(file, fh, req->type);
+
+ mutex_lock(&zr->resource_lock);
+ if (fh->buffers.allocated) {
+ dprintk(2,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - buffers already allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
+ req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ /* control user input */
+ if (req->count < 2)
+ req->count = 2;
+ if (req->count > v4l_nbufs)
+ req->count = v4l_nbufs;
+
+ /* The next mmap will map the V4L buffers */
+ map_mode_raw(fh);
+ fh->buffers.num_buffers = req->count;
+
+ if (v4l_fbuffer_alloc(fh)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+ } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
+ fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+ /* we need to calculate size ourselves now */
+ if (req->count < 4)
+ req->count = 4;
+ if (req->count > jpg_nbufs)
+ req->count = jpg_nbufs;
+
+ /* The next mmap will map the MJPEG buffers */
+ map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ fh->buffers.num_buffers = req->count;
+ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+ if (jpg_fbuffer_alloc(fh)) {
+ res = -ENOMEM;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+ } else {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_REQBUFS - unknown type %d\n",
+ ZR_DEVNAME(zr), req->type);
+ res = -EINVAL;
+ goto v4l2reqbuf_unlock_and_return;
+ }
+v4l2reqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_v4l2_buffer_status(fh, buf, buf->index);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0, codec_mode, buf_type;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res = zoran_v4l_queue_frame(fh, buf->index);
+ if (res)
+ goto qbuf_unlock_and_return;
+ if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
+ zr36057_set_memgrab(zr, 1);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
+ } else {
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ codec_mode = BUZ_MODE_MOTION_COMPRESS;
+ }
+
+ if (buf->type != buf_type) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto qbuf_unlock_and_return;
+ }
+
+ res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
+ if (res != 0)
+ goto qbuf_unlock_and_return;
+ if (zr->codec_mode == BUZ_MODE_IDLE &&
+ fh->buffers.active == ZORAN_LOCKED)
+ zr36057_enable_jpg(zr, codec_mode);
+
+ break;
+
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ break;
+ }
+qbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0, buf_type, num = -1; /* compiler borks here (?) */
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+ if (file->f_flags & O_NONBLOCK &&
+ zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ res = v4l_sync(fh, num);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ zr->v4l_sync_tail++;
+ res = zoran_v4l2_buffer_status(fh, buf, num);
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ {
+ struct zoran_sync bs;
+
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (buf->type != buf_type) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+ goto dqbuf_unlock_and_return;
+ }
+
+ num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ if (file->f_flags & O_NONBLOCK &&
+ zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
+ res = -EAGAIN;
+ goto dqbuf_unlock_and_return;
+ }
+ bs.frame = 0; /* suppress compiler warning */
+ res = jpg_sync(fh, &bs);
+ if (res)
+ goto dqbuf_unlock_and_return;
+ res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
+ break;
+ }
+
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_DQBUF - unsupported type %d\n",
+ ZR_DEVNAME(zr), buf->type);
+ res = -EINVAL;
+ break;
+ }
+dqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
+ fh->buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+ zr->v4l_settings = fh->v4l_settings;
+
+ zr->v4l_sync_tail = zr->v4l_pend_tail;
+ if (!zr->v4l_memgrab_active &&
+ zr->v4l_pend_head != zr->v4l_pend_tail) {
+ zr36057_set_memgrab(zr, 1);
+ }
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ /* what is the codec mode right now? */
+ if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
+ fh->buffers.active != ZORAN_ACTIVE) {
+ res = -EBUSY;
+ goto strmon_unlock_and_return;
+ }
+
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+
+ if (zr->jpg_que_head != zr->jpg_que_tail) {
+ /* Start the jpeg codec when the first frame is queued */
+ jpeg_start(zr);
+ }
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_STREAMON - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ break;
+ }
+strmon_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int i, res = 0;
+ unsigned long flags;
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW: /* raw capture */
+ if (fh->buffers.active == ZORAN_FREE &&
+ zr->v4l_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->v4l_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ /* unload capture */
+ if (zr->v4l_memgrab_active) {
+
+ zr36057_set_memgrab(zr, 0);
+ }
+
+ for (i = 0; i < fh->buffers.num_buffers; i++)
+ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
+ fh->buffers = zr->v4l_buffers;
+
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+
+ zr->v4l_grab_seq = 0;
+ zr->v4l_pend_head = zr->v4l_pend_tail = 0;
+ zr->v4l_sync_tail = 0;
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ if (fh->buffers.active == ZORAN_FREE &&
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ res = -EPERM; /* stay off other's settings! */
+ goto strmoff_unlock_and_return;
+ }
+ if (zr->jpg_buffers.active == ZORAN_FREE)
+ goto strmoff_unlock_and_return;
+
+ res = jpg_qbuf(fh, -1,
+ (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ BUZ_MODE_MOTION_COMPRESS :
+ BUZ_MODE_MOTION_DECOMPRESS);
+ if (res)
+ goto strmoff_unlock_and_return;
+ break;
+ default:
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
+ ZR_DEVNAME(zr), fh->map_mode);
+ res = -EINVAL;
+ break;
+ }
+strmoff_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_queryctrl(struct file *file, void *__fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ decoder_call(zr, core, queryctrl, ctrl);
+
+ return 0;
+}
+
+static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, core, g_ctrl, ctrl);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ /* we only support hue/saturation/contrast/brightness */
+ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+ ctrl->id > V4L2_CID_HUE)
+ return -EINVAL;
+
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, core, s_ctrl, ctrl);
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+ *std = zr->norm;
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_set_norm(zr, *std);
+ if (res)
+ goto sstd_unlock_and_return;
+
+ res = wait_grab_pending(zr);
+sstd_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_enum_input(struct file *file, void *__fh,
+ struct v4l2_input *inp)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ if (inp->index >= zr->card.inputs)
+ return -EINVAL;
+
+ strncpy(inp->name, zr->card.input[inp->index].name,
+ sizeof(inp->name) - 1);
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+
+ /* Get status of video decoder */
+ mutex_lock(&zr->resource_lock);
+ decoder_call(zr, video, g_input_status, &inp->status);
+ mutex_unlock(&zr->resource_lock);
+ return 0;
+}
+
+static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+
+ mutex_lock(&zr->resource_lock);
+ *input = zr->input;
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res;
+
+ mutex_lock(&zr->resource_lock);
+ res = zoran_set_input(zr, input);
+ if (res)
+ goto sinput_unlock_and_return;
+
+ /* Make sure the changes come into effect */
+ res = wait_grab_pending(zr);
+sinput_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_enum_output(struct file *file, void *__fh,
+ struct v4l2_output *outp)
+{
+ if (outp->index != 0)
+ return -EINVAL;
+
+ outp->index = 0;
+ outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
+ strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
+
+ return 0;
+}
+
+static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
+{
+ *output = 0;
+
+ return 0;
+}
+
+static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
+{
+ if (output != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* cropping (sub-frame capture) */
+static int zoran_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int type = cropcap->type, res = 0;
+
+ memset(cropcap, 0, sizeof(*cropcap));
+ cropcap->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto cropcap_unlock_and_return;
+ }
+
+ cropcap->bounds.top = cropcap->bounds.left = 0;
+ cropcap->bounds.width = BUZ_MAX_WIDTH;
+ cropcap->bounds.height = BUZ_MAX_HEIGHT;
+ cropcap->defrect.top = cropcap->defrect.left = 0;
+ cropcap->defrect.width = BUZ_MIN_WIDTH;
+ cropcap->defrect.height = BUZ_MIN_HEIGHT;
+cropcap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int type = crop->type, res = 0;
+
+ memset(crop, 0, sizeof(*crop));
+ crop->type = type;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto gcrop_unlock_and_return;
+ }
+
+ crop->c.top = fh->jpg_settings.img_y;
+ crop->c.left = fh->jpg_settings.img_x;
+ crop->c.width = fh->jpg_settings.img_width;
+ crop->c.height = fh->jpg_settings.img_height;
+
+gcrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+ struct zoran_jpg_settings settings;
+
+ settings = fh->jpg_settings;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.allocated) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_S_CROP - cannot change settings while active\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto scrop_unlock_and_return;
+ }
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+ dprintk(1, KERN_ERR
+ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
+ res = -EINVAL;
+ goto scrop_unlock_and_return;
+ }
+
+ /* move into a form that we understand */
+ settings.img_x = crop->c.left;
+ settings.img_y = crop->c.top;
+ settings.img_width = crop->c.width;
+ settings.img_height = crop->c.height;
+
+ /* check validity */
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto scrop_unlock_and_return;
+
+ /* accept */
+ fh->jpg_settings = settings;
+
+scrop_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+ return res;
+}
+
+static int zoran_g_jpegcomp(struct file *file, void *__fh,
+ struct v4l2_jpegcompression *params)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ memset(params, 0, sizeof(*params));
+
+ mutex_lock(&zr->resource_lock);
+
+ params->quality = fh->jpg_settings.jpg_comp.quality;
+ params->APPn = fh->jpg_settings.jpg_comp.APPn;
+ memcpy(params->APP_data,
+ fh->jpg_settings.jpg_comp.APP_data,
+ fh->jpg_settings.jpg_comp.APP_len);
+ params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+ memcpy(params->COM_data,
+ fh->jpg_settings.jpg_comp.COM_data,
+ fh->jpg_settings.jpg_comp.COM_len);
+ params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
+ params->jpeg_markers =
+ fh->jpg_settings.jpg_comp.jpeg_markers;
+
+ mutex_unlock(&zr->resource_lock);
+
+ return 0;
+}
+
+static int zoran_s_jpegcomp(struct file *file, void *__fh,
+ const struct v4l2_jpegcompression *params)
+{
+ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+ struct zoran_jpg_settings settings;
+
+ settings = fh->jpg_settings;
+
+ settings.jpg_comp = *params;
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->buffers.active != ZORAN_FREE) {
+ dprintk(1, KERN_WARNING
+ "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto sjpegc_unlock_and_return;
+ }
+
+ res = zoran_check_jpg_settings(zr, &settings, 0);
+ if (res)
+ goto sjpegc_unlock_and_return;
+ if (!fh->buffers.allocated)
+ fh->buffers.buffer_size =
+ zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+ fh->jpg_settings.jpg_comp = settings.jpg_comp;
+sjpegc_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static unsigned int
+zoran_poll (struct file *file,
+ poll_table *wait)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int res = 0, frame;
+ unsigned long flags;
+
+ /* we should check whether buffers are ready to be synced on
+ * (w/o waits - O_NONBLOCK) here
+ * if ready for read (sync), return POLLIN|POLLRDNORM,
+ * if ready for write (sync), return POLLOUT|POLLWRNORM,
+ * if error, return POLLERR,
+ * if no buffers queued or so, return POLLNVAL
+ */
+
+ mutex_lock(&zr->resource_lock);
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+ poll_wait(file, &zr->v4l_capq, wait);
+ frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], zr->v4l_sync_tail,
+ "UPMD"[zr->v4l_buffers.buffer[frame].state],
+ zr->v4l_pend_tail, zr->v4l_pend_head);
+ /* Process is the one capturing? */
+ if (fh->buffers.active != ZORAN_FREE &&
+ /* Buffer ready to DQBUF? */
+ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
+ res = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ case ZORAN_MAP_MODE_JPG_REC:
+ case ZORAN_MAP_MODE_JPG_PLAY:
+ poll_wait(file, &zr->jpg_capq, wait);
+ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ dprintk(3,
+ KERN_DEBUG
+ "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
+ ZR_DEVNAME(zr), __func__,
+ "FAL"[fh->buffers.active], zr->jpg_que_tail,
+ "UPMD"[zr->jpg_buffers.buffer[frame].state],
+ zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
+ if (fh->buffers.active != ZORAN_FREE &&
+ zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
+ res = POLLIN | POLLRDNORM;
+ else
+ res = POLLOUT | POLLWRNORM;
+ }
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ break;
+
+ default:
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - internal error, unknown map_mode=%d\n",
+ ZR_DEVNAME(zr), __func__, fh->map_mode);
+ res = POLLNVAL;
+ }
+
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+
+/*
+ * This maps the buffers to user space.
+ *
+ * Depending on the state of fh->map_mode
+ * the V4L or the MJPEG buffers are mapped
+ * per buffer or all together
+ *
+ * Note that we need to connect to some
+ * unmap signal event to unmap the de-allocate
+ * the buffer accordingly (zoran_vm_close())
+ */
+
+static void
+zoran_vm_open (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+
+ map->count++;
+}
+
+static void
+zoran_vm_close (struct vm_area_struct *vma)
+{
+ struct zoran_mapping *map = vma->vm_private_data;
+ struct zoran_fh *fh = map->fh;
+ struct zoran *zr = fh->zr;
+ int i;
+
+ if (--map->count > 0)
+ return;
+
+ dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
+ __func__, mode_name(fh->map_mode));
+
+ for (i = 0; i < fh->buffers.num_buffers; i++) {
+ if (fh->buffers.buffer[i].map == map)
+ fh->buffers.buffer[i].map = NULL;
+ }
+ kfree(map);
+
+ /* Any buffers still mapped? */
+ for (i = 0; i < fh->buffers.num_buffers; i++)
+ if (fh->buffers.buffer[i].map)
+ return;
+
+ dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
+ __func__, mode_name(fh->map_mode));
+
+ mutex_lock(&zr->resource_lock);
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ if (fh->buffers.active != ZORAN_FREE) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+ zr36057_set_memgrab(zr, 0);
+ zr->v4l_buffers.allocated = 0;
+ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+ }
+ v4l_fbuffer_free(fh);
+ } else {
+ if (fh->buffers.active != ZORAN_FREE) {
+ jpg_qbuf(fh, -1, zr->codec_mode);
+ zr->jpg_buffers.allocated = 0;
+ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
+ }
+ jpg_fbuffer_free(fh);
+ }
+
+ mutex_unlock(&zr->resource_lock);
+}
+
+static const struct vm_operations_struct zoran_vm_ops = {
+ .open = zoran_vm_open,
+ .close = zoran_vm_close,
+};
+
+static int
+zoran_mmap (struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long size = (vma->vm_end - vma->vm_start);
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int i, j;
+ unsigned long page, start = vma->vm_start, todo, pos, fraglen;
+ int first, last;
+ struct zoran_mapping *map;
+ int res = 0;
+
+ dprintk(3,
+ KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
+ ZR_DEVNAME(zr), __func__,
+ mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
+
+ if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
+ !(vma->vm_flags & VM_WRITE)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&zr->resource_lock);
+
+ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - buffers not yet allocated\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
+ res = -ENOMEM;
+ goto mmap_unlock_and_return;
+ }
+
+ first = offset / fh->buffers.buffer_size;
+ last = first - 1 + size / fh->buffers.buffer_size;
+ if (offset % fh->buffers.buffer_size != 0 ||
+ size % fh->buffers.buffer_size != 0 || first < 0 ||
+ last < 0 || first >= fh->buffers.num_buffers ||
+ last >= fh->buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
+ fh->buffers.buffer_size,
+ fh->buffers.num_buffers);
+ res = -EINVAL;
+ goto mmap_unlock_and_return;
+ }
+
+ /* Check if any buffers are already mapped */
+ for (i = first; i <= last; i++) {
+ if (fh->buffers.buffer[i].map) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(%s) - buffer %d already mapped\n",
+ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
+ res = -EBUSY;
+ goto mmap_unlock_and_return;
+ }
+ }
+
+ /* map these buffers */
+ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+ if (!map) {
+ res = -ENOMEM;
+ goto mmap_unlock_and_return;
+ }
+ map->fh = fh;
+ map->count = 1;
+
+ vma->vm_ops = &zoran_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_private_data = map;
+
+ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ for (i = first; i <= last; i++) {
+ todo = size;
+ if (todo > fh->buffers.buffer_size)
+ todo = fh->buffers.buffer_size;
+ page = fh->buffers.buffer[i].v4l.fbuffer_phys;
+ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(V4L) - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EAGAIN;
+ goto mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ fh->buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+ }
+ } else {
+ for (i = first; i <= last; i++) {
+ for (j = 0;
+ j < fh->buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ fraglen =
+ (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & ~1) << 1;
+ todo = size;
+ if (todo > fraglen)
+ todo = fraglen;
+ pos =
+ le32_to_cpu(fh->buffers.
+ buffer[i].jpg.frag_tab[2 * j]);
+ /* should just be pos on i386 */
+ page = virt_to_phys(bus_to_virt(pos))
+ >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, page,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s(V4L) - remap_pfn_range failed\n",
+ ZR_DEVNAME(zr), __func__);
+ res = -EAGAIN;
+ goto mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ if (size == 0)
+ break;
+ if (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & 1)
+ break; /* was last fragment */
+ }
+ fh->buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+
+ }
+ }
+
+mmap_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+}
+
+static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
+ .vidioc_querycap = zoran_querycap,
+ .vidioc_cropcap = zoran_cropcap,
+ .vidioc_s_crop = zoran_s_crop,
+ .vidioc_g_crop = zoran_g_crop,
+ .vidioc_enum_input = zoran_enum_input,
+ .vidioc_g_input = zoran_g_input,
+ .vidioc_s_input = zoran_s_input,
+ .vidioc_enum_output = zoran_enum_output,
+ .vidioc_g_output = zoran_g_output,
+ .vidioc_s_output = zoran_s_output,
+ .vidioc_g_fbuf = zoran_g_fbuf,
+ .vidioc_s_fbuf = zoran_s_fbuf,
+ .vidioc_g_std = zoran_g_std,
+ .vidioc_s_std = zoran_s_std,
+ .vidioc_g_jpegcomp = zoran_g_jpegcomp,
+ .vidioc_s_jpegcomp = zoran_s_jpegcomp,
+ .vidioc_overlay = zoran_overlay,
+ .vidioc_reqbufs = zoran_reqbufs,
+ .vidioc_querybuf = zoran_querybuf,
+ .vidioc_qbuf = zoran_qbuf,
+ .vidioc_dqbuf = zoran_dqbuf,
+ .vidioc_streamon = zoran_streamon,
+ .vidioc_streamoff = zoran_streamoff,
+ .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,
+ .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,
+ .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,
+ .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay,
+ .vidioc_queryctrl = zoran_queryctrl,
+ .vidioc_s_ctrl = zoran_s_ctrl,
+ .vidioc_g_ctrl = zoran_g_ctrl,
+};
+
+/* please use zr->resource_lock consistently and kill this wrapper */
+static long zoran_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int ret;
+
+ mutex_lock(&zr->other_lock);
+ ret = video_ioctl2(file, cmd, arg);
+ mutex_unlock(&zr->other_lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations zoran_fops = {
+ .owner = THIS_MODULE,
+ .open = zoran_open,
+ .release = zoran_close,
+ .unlocked_ioctl = zoran_ioctl,
+ .read = zoran_read,
+ .write = zoran_write,
+ .mmap = zoran_mmap,
+ .poll = zoran_poll,
+};
+
+struct video_device zoran_template __devinitdata = {
+ .name = ZORAN_NAME,
+ .fops = &zoran_fops,
+ .ioctl_ops = &zoran_ioctl_ops,
+ .release = &zoran_vdev_release,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
diff --git a/drivers/media/video/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c
index f1423b777db1..f1423b777db1 100644
--- a/drivers/media/video/zoran/zoran_procfs.c
+++ b/drivers/media/pci/zoran/zoran_procfs.c
diff --git a/drivers/media/video/zoran/zoran_procfs.h b/drivers/media/pci/zoran/zoran_procfs.h
index f2d5b1ba448f..f2d5b1ba448f 100644
--- a/drivers/media/video/zoran/zoran_procfs.h
+++ b/drivers/media/pci/zoran/zoran_procfs.h
diff --git a/drivers/media/video/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c
index b87ddba8608f..b87ddba8608f 100644
--- a/drivers/media/video/zoran/zr36016.c
+++ b/drivers/media/pci/zoran/zr36016.c
diff --git a/drivers/media/video/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h
index 8c79229f69d1..8c79229f69d1 100644
--- a/drivers/media/video/zoran/zr36016.h
+++ b/drivers/media/pci/zoran/zr36016.h
diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c
index e1985609af4b..e1985609af4b 100644
--- a/drivers/media/video/zoran/zr36050.c
+++ b/drivers/media/pci/zoran/zr36050.c
diff --git a/drivers/media/video/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h
index 9f52f0cdde50..9f52f0cdde50 100644
--- a/drivers/media/video/zoran/zr36050.h
+++ b/drivers/media/pci/zoran/zr36050.h
diff --git a/drivers/media/video/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h
index 54c9362aa980..54c9362aa980 100644
--- a/drivers/media/video/zoran/zr36057.h
+++ b/drivers/media/pci/zoran/zr36057.h
diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c
index f08546fe2234..f08546fe2234 100644
--- a/drivers/media/video/zoran/zr36060.c
+++ b/drivers/media/pci/zoran/zr36060.c
diff --git a/drivers/media/video/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h
index 914ffa4ad8d3..914ffa4ad8d3 100644
--- a/drivers/media/video/zoran/zr36060.h
+++ b/drivers/media/pci/zoran/zr36060.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
new file mode 100644
index 000000000000..f588d6296c76
--- /dev/null
+++ b/drivers/media/platform/Kconfig
@@ -0,0 +1,223 @@
+#
+# Platform drivers
+# All drivers here are currently for webcam support
+
+menuconfig V4L_PLATFORM_DRIVERS
+ bool "V4L platform devices"
+ depends on MEDIA_CAMERA_SUPPORT
+ default n
+ ---help---
+ Say Y here to enable support for platform-specific V4L drivers.
+
+if V4L_PLATFORM_DRIVERS
+
+source "drivers/media/platform/marvell-ccic/Kconfig"
+
+config VIDEO_VIA_CAMERA
+ tristate "VIAFB camera controller support"
+ depends on FB_VIA
+ select VIDEOBUF_DMA_SG
+ select VIDEO_OV7670
+ help
+ Driver support for the integrated camera controller in VIA
+ Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
+ with ov7670 sensors.
+
+#
+# Platform multimedia device configuration
+#
+
+source "drivers/media/platform/davinci/Kconfig"
+
+source "drivers/media/platform/omap/Kconfig"
+
+source "drivers/media/platform/blackfin/Kconfig"
+
+config VIDEO_SH_VOU
+ tristate "SuperH VOU video output driver"
+ depends on MEDIA_CAMERA_SUPPORT
+ depends on VIDEO_DEV && ARCH_SHMOBILE
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Support for the Video Output Unit (VOU) on SuperH SoCs.
+
+config VIDEO_VIU
+ tristate "Freescale VIU Video Driver"
+ depends on VIDEO_V4L2 && PPC_MPC512x
+ select VIDEOBUF_DMA_CONTIG
+ default y
+ ---help---
+ Support for Freescale VIU video driver. This device captures
+ video data, or overlays video on DIU frame buffer.
+
+ Say Y here if you want to enable VIU device on MPC5121e Rev2+.
+ In doubt, say N.
+
+config VIDEO_TIMBERDALE
+ tristate "Support for timberdale Video In/LogiWIN"
+ depends on VIDEO_V4L2 && I2C && DMADEVICES
+ select DMA_ENGINE
+ select TIMB_DMA
+ select VIDEO_ADV7180
+ select VIDEOBUF_DMA_CONTIG
+ ---help---
+ Add support for the Video In peripherial of the timberdale FPGA.
+
+config VIDEO_VINO
+ tristate "SGI Vino Video For Linux"
+ depends on I2C && SGI_IP22 && VIDEO_V4L2
+ select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to build in support for the Vino video input system found
+ on SGI Indy machines.
+
+config VIDEO_M32R_AR
+ tristate "AR devices"
+ depends on M32R && VIDEO_V4L2
+ ---help---
+ This is a video4linux driver for the Renesas AR (Artificial Retina)
+ camera module.
+
+config VIDEO_M32R_AR_M64278
+ tristate "AR device with color module M64278(VGA)"
+ depends on PLAT_M32700UT
+ select VIDEO_M32R_AR
+ ---help---
+ This is a video4linux driver for the Renesas AR (Artificial
+ Retina) with M64278E-800 camera module.
+ This module supports VGA(640x480 pixels) resolutions.
+
+ To compile this driver as a module, choose M here: the
+ module will be called arv.
+
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
+config VIDEO_OMAP3
+ tristate "OMAP 3 Camera support (EXPERIMENTAL)"
+ depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
+ ---help---
+ Driver for an OMAP 3 camera controller.
+
+config VIDEO_OMAP3_DEBUG
+ bool "OMAP 3 Camera debug messages"
+ depends on VIDEO_OMAP3
+ ---help---
+ Enable debug messages on OMAP 3 camera controller driver.
+
+source "drivers/media/platform/soc_camera/Kconfig"
+source "drivers/media/platform/s5p-fimc/Kconfig"
+source "drivers/media/platform/s5p-tv/Kconfig"
+
+endif # V4L_PLATFORM_DRIVERS
+
+menuconfig V4L_MEM2MEM_DRIVERS
+ bool "Memory-to-memory multimedia devices"
+ depends on VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ default n
+ ---help---
+ Say Y here to enable selecting drivers for V4L devices that
+ use system memory for both source and destination buffers, as opposed
+ to capture and output drivers, which use memory buffers for just
+ one of those.
+
+if V4L_MEM2MEM_DRIVERS
+
+config VIDEO_CODA
+ tristate "Chips&Media Coda multi-standard codec IP"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select IRAM_ALLOC if SOC_IMX53
+ ---help---
+ Coda is a range of video codec IPs that supports
+ H.264, MPEG-4, and other video formats.
+
+config VIDEO_MEM2MEM_DEINTERLACE
+ tristate "Deinterlace support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Generic deinterlacing V4L2 driver.
+
+config VIDEO_SAMSUNG_S5P_G2D
+ tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ default n
+ ---help---
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
+ 2d graphics accelerator.
+
+config VIDEO_SAMSUNG_S5P_JPEG
+ tristate "Samsung S5P/Exynos4 JPEG codec driver (EXPERIMENTAL)"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P && EXPERIMENTAL
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 JPEG codec
+
+config VIDEO_SAMSUNG_S5P_MFC
+ tristate "Samsung S5P MFC 5.1 Video Codec"
+ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
+ select VIDEOBUF2_DMA_CONTIG
+ default n
+ help
+ MFC 5.1 driver for V4L2.
+
+config VIDEO_MX2_EMMAPRP
+ tristate "MX2 eMMa-PrP support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
+config VIDEO_SAMSUNG_EXYNOS_GSC
+ tristate "Samsung Exynos G-Scaler driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
+
+endif # V4L_MEM2MEM_DRIVERS
+
+menuconfig V4L_TEST_DRIVERS
+ bool "Media test drivers"
+ depends on MEDIA_CAMERA_SUPPORT
+
+if V4L_TEST_DRIVERS
+config VIDEO_VIVI
+ tristate "Virtual Video Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
+ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+ select FONT_8x16
+ select VIDEOBUF2_VMALLOC
+ default n
+ ---help---
+ Enables a virtual video driver. This device shows a color bar
+ and a timestamp, as a real device would generate by using V4L2
+ api.
+ Say Y here if you want to test video apps or debug V4L devices.
+ In doubt, say N.
+
+config VIDEO_MEM2MEM_TESTDEV
+ tristate "Virtual test device for mem2mem framework"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ default n
+ ---help---
+ This is a virtual test device for the memory-to-memory driver
+ framework.
+endif #V4L_TEST_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
new file mode 100644
index 000000000000..baaa55026c8e
--- /dev/null
+++ b/drivers/media/platform/Makefile
@@ -0,0 +1,50 @@
+#
+# Makefile for the video capture/playback device drivers.
+#
+
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
+
+obj-$(CONFIG_VIDEO_VINO) += indycam.o
+obj-$(CONFIG_VIDEO_VINO) += vino.o
+
+obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
+obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
+
+obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
+obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
+obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
+
+obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
+obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
+
+obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
+obj-$(CONFIG_VIDEO_VIVI) += vivi.o
+
+obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
+
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+obj-$(CONFIG_VIDEO_CODA) += coda.o
+
+obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/
+
+obj-$(CONFIG_BLACKFIN) += blackfin/
+
+obj-$(CONFIG_ARCH_DAVINCI) += davinci/
+
+obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
+
+obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+
+obj-y += davinci/
+
+obj-$(CONFIG_ARCH_OMAP) += omap/
+
+ccflags-y += -I$(srctree)/drivers/media/i2c
diff --git a/drivers/media/video/arv.c b/drivers/media/platform/arv.c
index e346d32d08ce..e346d32d08ce 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/platform/arv.c
diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig
index ecd5323768b7..ecd5323768b7 100644
--- a/drivers/media/video/blackfin/Kconfig
+++ b/drivers/media/platform/blackfin/Kconfig
diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile
index aa3a0a216387..aa3a0a216387 100644
--- a/drivers/media/video/blackfin/Makefile
+++ b/drivers/media/platform/blackfin/Makefile
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
new file mode 100644
index 000000000000..cb2eb26850b1
--- /dev/null
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -0,0 +1,1069 @@
+/*
+ * Analog Devices video capture driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <asm/dma.h>
+
+#include <media/blackfin/bfin_capture.h>
+#include <media/blackfin/ppi.h>
+
+#define CAPTURE_DRV_NAME "bfin_capture"
+#define BCAP_MIN_NUM_BUF 2
+
+struct bcap_format {
+ char *desc;
+ u32 pixelformat;
+ enum v4l2_mbus_pixelcode mbus_code;
+ int bpp; /* bits per pixel */
+};
+
+struct bcap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct bcap_device {
+ /* capture device instance */
+ struct v4l2_device v4l2_dev;
+ /* v4l2 control handler */
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* device node data */
+ struct video_device *video_dev;
+ /* sub device instance */
+ struct v4l2_subdev *sd;
+ /* capture config */
+ struct bfin_capture_config *cfg;
+ /* ppi interface */
+ struct ppi_if *ppi;
+ /* current input */
+ unsigned int cur_input;
+ /* current selected standard */
+ v4l2_std_id std;
+ /* used to store pixel format */
+ struct v4l2_pix_format fmt;
+ /* bits per pixel*/
+ int bpp;
+ /* used to store sensor supported format */
+ struct bcap_format *sensor_formats;
+ /* number of sensor formats array */
+ int num_sensor_formats;
+ /* pointing to current video buffer */
+ struct bcap_buffer *cur_frm;
+ /* pointing to next video buffer */
+ struct bcap_buffer *next_frm;
+ /* buffer queue used in videobuf2 */
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* queue of filled frames */
+ struct list_head dma_queue;
+ /* used in videobuf2 callback */
+ spinlock_t lock;
+ /* used to access capture device */
+ struct mutex mutex;
+ /* used to wait ppi to complete one transfer */
+ struct completion comp;
+ /* prepare to stop */
+ bool stop;
+};
+
+struct bcap_fh {
+ struct v4l2_fh fh;
+ /* indicates whether this file handle is doing IO */
+ bool io_allowed;
+};
+
+static const struct bcap_format bcap_formats[] = {
+ {
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "YCbCr 4:2:2 Interleaved YUYV",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .bpp = 16,
+ },
+
+};
+#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
+
+static irqreturn_t bcap_isr(int irq, void *dev_id);
+
+static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct bcap_buffer, vb);
+}
+
+static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
+{
+ enum v4l2_mbus_pixelcode code;
+ struct bcap_format *sf;
+ unsigned int num_formats = 0;
+ int i, j;
+
+ while (!v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, num_formats, &code))
+ num_formats++;
+ if (!num_formats)
+ return -ENXIO;
+
+ sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);
+ if (!sf)
+ return -ENOMEM;
+
+ for (i = 0; i < num_formats; i++) {
+ v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, i, &code);
+ for (j = 0; j < BCAP_MAX_FMTS; j++)
+ if (code == bcap_formats[j].mbus_code)
+ break;
+ if (j == BCAP_MAX_FMTS) {
+ /* we don't allow this sensor working with our bridge */
+ kfree(sf);
+ return -EINVAL;
+ }
+ sf[i] = bcap_formats[j];
+ }
+ bcap_dev->sensor_formats = sf;
+ bcap_dev->num_sensor_formats = num_formats;
+ return 0;
+}
+
+static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
+{
+ bcap_dev->num_sensor_formats = 0;
+ kfree(bcap_dev->sensor_formats);
+ bcap_dev->sensor_formats = NULL;
+}
+
+static int bcap_open(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct video_device *vfd = bcap_dev->video_dev;
+ struct bcap_fh *bcap_fh;
+
+ if (!bcap_dev->sd) {
+ v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
+ return -ENODEV;
+ }
+
+ bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
+ if (!bcap_fh) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ v4l2_fh_init(&bcap_fh->fh, vfd);
+
+ /* store pointer to v4l2_fh in private_data member of file */
+ file->private_data = &bcap_fh->fh;
+ v4l2_fh_add(&bcap_fh->fh);
+ bcap_fh->io_allowed = false;
+ return 0;
+}
+
+static int bcap_release(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ /* if this instance is doing IO */
+ if (bcap_fh->io_allowed)
+ vb2_queue_release(&bcap_dev->buffer_queue);
+
+ file->private_data = NULL;
+ v4l2_fh_del(&bcap_fh->fh);
+ v4l2_fh_exit(&bcap_fh->fh);
+ kfree(bcap_fh);
+ return 0;
+}
+
+static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&bcap_dev->mutex))
+ return -ERESTARTSYS;
+ ret = vb2_mmap(&bcap_dev->buffer_queue, vma);
+ mutex_unlock(&bcap_dev->mutex);
+ return ret;
+}
+
+#ifndef CONFIG_MMU
+static unsigned long bcap_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
+ addr,
+ len,
+ pgoff,
+ flags);
+}
+#endif
+
+static unsigned int bcap_poll(struct file *file, poll_table *wait)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ unsigned int res;
+
+ mutex_lock(&bcap_dev->mutex);
+ res = vb2_poll(&bcap_dev->buffer_queue, file, wait);
+ mutex_unlock(&bcap_dev->mutex);
+ return res;
+}
+
+static int bcap_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+
+ if (*nbuffers < BCAP_MIN_NUM_BUF)
+ *nbuffers = BCAP_MIN_NUM_BUF;
+
+ *nplanes = 1;
+ sizes[0] = bcap_dev->fmt.sizeimage;
+ alloc_ctxs[0] = bcap_dev->alloc_ctx;
+
+ return 0;
+}
+
+static int bcap_buffer_init(struct vb2_buffer *vb)
+{
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+static int bcap_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long size;
+
+ size = bcap_dev->fmt.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ return 0;
+}
+
+static void bcap_buffer_queue(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_add_tail(&buf->list, &bcap_dev->dma_queue);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_lock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_lock(&bcap_dev->mutex);
+}
+
+static void bcap_unlock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&bcap_dev->mutex);
+}
+
+static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ struct ppi_params params;
+ int ret;
+
+ /* enable streamon on the sub device */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ /* set ppi params */
+ params.width = bcap_dev->fmt.width;
+ params.height = bcap_dev->fmt.height;
+ params.bpp = bcap_dev->bpp;
+ params.ppi_control = bcap_dev->cfg->ppi_control;
+ params.int_mask = bcap_dev->cfg->int_mask;
+ params.blank_clocks = bcap_dev->cfg->blank_clocks;
+ ret = ppi->ops->set_params(ppi, &params);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in setting ppi params\n");
+ return ret;
+ }
+
+ /* attach ppi DMA irq handler */
+ ret = ppi->ops->attach_irq(ppi, bcap_isr);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in attaching interrupt handler\n");
+ return ret;
+ }
+
+ INIT_COMPLETION(bcap_dev->comp);
+ bcap_dev->stop = false;
+ return 0;
+}
+
+static int bcap_stop_streaming(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ int ret;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ bcap_dev->stop = true;
+ wait_for_completion(&bcap_dev->comp);
+ ppi->ops->stop(ppi);
+ ppi->ops->detach_irq(ppi);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "stream off failed in subdev\n");
+
+ /* release all active buffers */
+ while (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+ return 0;
+}
+
+static struct vb2_ops bcap_video_qops = {
+ .queue_setup = bcap_queue_setup,
+ .buf_init = bcap_buffer_init,
+ .buf_prepare = bcap_buffer_prepare,
+ .buf_cleanup = bcap_buffer_cleanup,
+ .buf_queue = bcap_buffer_queue,
+ .wait_prepare = bcap_unlock,
+ .wait_finish = bcap_lock,
+ .start_streaming = bcap_start_streaming,
+ .stop_streaming = bcap_stop_streaming,
+};
+
+static int bcap_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct vb2_queue *vq = &bcap_dev->buffer_queue;
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ bcap_fh->io_allowed = true;
+
+ return vb2_reqbufs(vq, req_buf);
+}
+
+static int bcap_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_querybuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_qbuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_dqbuf(&bcap_dev->buffer_queue,
+ buf, file->f_flags & O_NONBLOCK);
+}
+
+static irqreturn_t bcap_isr(int irq, void *dev_id)
+{
+ struct ppi_if *ppi = dev_id;
+ struct bcap_device *bcap_dev = ppi->priv;
+ struct timeval timevalue;
+ struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
+ dma_addr_t addr;
+
+ spin_lock(&bcap_dev->lock);
+
+ if (bcap_dev->cur_frm != bcap_dev->next_frm) {
+ do_gettimeofday(&timevalue);
+ vb->v4l2_buf.timestamp = timevalue;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ }
+
+ ppi->ops->stop(ppi);
+
+ if (bcap_dev->stop) {
+ complete(&bcap_dev->comp);
+ } else {
+ if (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ }
+ ppi->ops->start(ppi);
+ }
+
+ spin_unlock(&bcap_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int bcap_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+ struct ppi_if *ppi = bcap_dev->ppi;
+ dma_addr_t addr;
+ int ret;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ /* call streamon to start streaming in videobuf */
+ ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
+ if (ret)
+ return ret;
+
+ /* if dma queue is empty, return error */
+ if (list_empty(&bcap_dev->dma_queue)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* get the next frame from the dma queue */
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ /* remove buffer from the dma queue */
+ list_del(&bcap_dev->cur_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
+ /* update DMA address */
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ /* enable ppi */
+ ppi->ops->start(ppi);
+
+ return 0;
+err:
+ vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+ return ret;
+}
+
+static int bcap_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+}
+
+static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
+}
+
+static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *std = bcap_dev->std;
+ return 0;
+}
+
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
+ if (ret < 0)
+ return ret;
+
+ bcap_dev->std = *std;
+ return 0;
+}
+
+static int bcap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ int ret;
+ u32 status;
+
+ if (input->index >= config->num_inputs)
+ return -EINVAL;
+
+ *input = config->inputs[input->index];
+ /* get input status */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
+ if (!ret)
+ input->status = status;
+ return 0;
+}
+
+static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *index = bcap_dev->cur_input;
+ return 0;
+}
+
+static int bcap_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ struct bcap_route *route;
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ if (index >= config->num_inputs)
+ return -EINVAL;
+
+ route = &config->routes[index];
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
+ route->input, route->output, 0);
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
+ return ret;
+ }
+ bcap_dev->cur_input = index;
+ return 0;
+}
+
+static int bcap_try_format(struct bcap_device *bcap,
+ struct v4l2_pix_format *pixfmt,
+ enum v4l2_mbus_pixelcode *mbus_code,
+ int *bpp)
+{
+ struct bcap_format *sf = bcap->sensor_formats;
+ struct bcap_format *fmt = NULL;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret, i;
+
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
+ fmt = &sf[i];
+ if (pixfmt->pixelformat == fmt->pixelformat)
+ break;
+ }
+ if (i == bcap->num_sensor_formats)
+ fmt = &sf[0];
+
+ if (mbus_code)
+ *mbus_code = fmt->mbus_code;
+ if (bpp)
+ *bpp = fmt->bpp;
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
+ ret = v4l2_subdev_call(bcap->sd, video,
+ try_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+ pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+ return 0;
+}
+
+static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_format *sf = bcap_dev->sensor_formats;
+
+ if (fmt->index >= bcap_dev->num_sensor_formats)
+ return -EINVAL;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strlcpy(fmt->description,
+ sf[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = sf[fmt->index].pixelformat;
+ return 0;
+}
+
+static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
+}
+
+static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ fmt->fmt.pix = bcap_dev->fmt;
+ return 0;
+}
+
+static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ enum v4l2_mbus_pixelcode mbus_code;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ int ret, bpp;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ /* see if format works */
+ ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
+ if (ret < 0)
+ return ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ bcap_dev->fmt = *pixfmt;
+ bcap_dev->bpp = bpp;
+ return 0;
+}
+
+static int bcap_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
+ return 0;
+}
+
+static int bcap_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
+}
+
+static int bcap_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
+}
+
+static int bcap_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bcap_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_register, reg);
+}
+
+static int bcap_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ s_register, reg);
+}
+#endif
+
+static int bcap_log_status(struct file *file, void *priv)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ /* status for sub devices */
+ v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
+ .vidioc_querycap = bcap_querycap,
+ .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
+ .vidioc_enum_input = bcap_enum_input,
+ .vidioc_g_input = bcap_g_input,
+ .vidioc_s_input = bcap_s_input,
+ .vidioc_querystd = bcap_querystd,
+ .vidioc_s_std = bcap_s_std,
+ .vidioc_g_std = bcap_g_std,
+ .vidioc_reqbufs = bcap_reqbufs,
+ .vidioc_querybuf = bcap_querybuf,
+ .vidioc_qbuf = bcap_qbuf,
+ .vidioc_dqbuf = bcap_dqbuf,
+ .vidioc_streamon = bcap_streamon,
+ .vidioc_streamoff = bcap_streamoff,
+ .vidioc_g_parm = bcap_g_parm,
+ .vidioc_s_parm = bcap_s_parm,
+ .vidioc_g_chip_ident = bcap_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bcap_dbg_g_register,
+ .vidioc_s_register = bcap_dbg_s_register,
+#endif
+ .vidioc_log_status = bcap_log_status,
+};
+
+static struct v4l2_file_operations bcap_fops = {
+ .owner = THIS_MODULE,
+ .open = bcap_open,
+ .release = bcap_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = bcap_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = bcap_get_unmapped_area,
+#endif
+ .poll = bcap_poll
+};
+
+static int __devinit bcap_probe(struct platform_device *pdev)
+{
+ struct bcap_device *bcap_dev;
+ struct video_device *vfd;
+ struct i2c_adapter *i2c_adap;
+ struct bfin_capture_config *config;
+ struct vb2_queue *q;
+ int ret;
+
+ config = pdev->dev.platform_data;
+ if (!config) {
+ v4l2_err(pdev->dev.driver, "Unable to get board config\n");
+ return -ENODEV;
+ }
+
+ bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
+ if (!bcap_dev) {
+ v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
+ return -ENOMEM;
+ }
+
+ bcap_dev->cfg = config;
+
+ bcap_dev->ppi = ppi_create_instance(config->ppi_info);
+ if (!bcap_dev->ppi) {
+ v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
+ ret = -ENODEV;
+ goto err_free_dev;
+ }
+ bcap_dev->ppi->priv = bcap_dev;
+
+ bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(bcap_dev->alloc_ctx)) {
+ ret = PTR_ERR(bcap_dev->alloc_ctx);
+ goto err_free_ppi;
+ }
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ ret = -ENOMEM;
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+ goto err_cleanup_ctx;
+ }
+
+ /* initialize field of video device */
+ vfd->release = video_device_release;
+ vfd->fops = &bcap_fops;
+ vfd->ioctl_ops = &bcap_ioctl_ops;
+ vfd->tvnorms = 0;
+ vfd->v4l2_dev = &bcap_dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
+ bcap_dev->video_dev = vfd;
+
+ ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register v4l2 device\n");
+ goto err_release_vdev;
+ }
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
+
+ bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to init control handler\n");
+ goto err_unreg_v4l2;
+ }
+
+ spin_lock_init(&bcap_dev->lock);
+ /* initialize queue */
+ q = &bcap_dev->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = bcap_dev;
+ q->buf_struct_size = sizeof(struct bcap_buffer);
+ q->ops = &bcap_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+
+ vb2_queue_init(q);
+
+ mutex_init(&bcap_dev->mutex);
+ init_completion(&bcap_dev->comp);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&bcap_dev->dma_queue);
+
+ vfd->lock = &bcap_dev->mutex;
+
+ /* register video device */
+ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register video device\n");
+ goto err_free_handler;
+ }
+ video_set_drvdata(bcap_dev->video_dev, bcap_dev);
+ v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
+ video_device_node_name(vfd));
+
+ /* load up the subdevice */
+ i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
+ if (!i2c_adap) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to find i2c adapter\n");
+ ret = -ENODEV;
+ goto err_unreg_vdev;
+
+ }
+ bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
+ i2c_adap,
+ &config->board_info,
+ NULL);
+ if (bcap_dev->sd) {
+ int i;
+ /* update tvnorms from the sub devices */
+ for (i = 0; i < config->num_inputs; i++)
+ vfd->tvnorms |= config->inputs[i].std;
+ } else {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register sub device\n");
+ goto err_unreg_vdev;
+ }
+
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
+
+ /* now we can probe the default state */
+ if (vfd->tvnorms) {
+ v4l2_std_id std;
+ ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to get std\n");
+ goto err_unreg_vdev;
+ }
+ bcap_dev->std = std;
+ }
+ ret = bcap_init_sensor_formats(bcap_dev);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to create sensor formats table\n");
+ goto err_unreg_vdev;
+ }
+ return 0;
+err_unreg_vdev:
+ video_unregister_device(bcap_dev->video_dev);
+ bcap_dev->video_dev = NULL;
+err_free_handler:
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+err_unreg_v4l2:
+ v4l2_device_unregister(&bcap_dev->v4l2_dev);
+err_release_vdev:
+ if (bcap_dev->video_dev)
+ video_device_release(bcap_dev->video_dev);
+err_cleanup_ctx:
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+err_free_ppi:
+ ppi_delete_instance(bcap_dev->ppi);
+err_free_dev:
+ kfree(bcap_dev);
+ return ret;
+}
+
+static int __devexit bcap_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct bcap_device *bcap_dev = container_of(v4l2_dev,
+ struct bcap_device, v4l2_dev);
+
+ bcap_free_sensor_formats(bcap_dev);
+ video_unregister_device(bcap_dev->video_dev);
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+ v4l2_device_unregister(v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+ ppi_delete_instance(bcap_dev->ppi);
+ kfree(bcap_dev);
+ return 0;
+}
+
+static struct platform_driver bcap_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = bcap_probe,
+ .remove = __devexit_p(bcap_remove),
+};
+
+static __init int bcap_init(void)
+{
+ return platform_driver_register(&bcap_driver);
+}
+
+static __exit void bcap_exit(void)
+{
+ platform_driver_unregister(&bcap_driver);
+}
+
+module_init(bcap_init);
+module_exit(bcap_exit);
+
+MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c
index d29592186b02..d29592186b02 100644
--- a/drivers/media/video/blackfin/ppi.c
+++ b/drivers/media/platform/blackfin/ppi.c
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
new file mode 100644
index 000000000000..cd04ae252c30
--- /dev/null
+++ b/drivers/media/platform/coda.c
@@ -0,0 +1,2049 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2012 Vista Silicon S.L.
+ * Javier Martin, <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/clk.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/of.h>
+
+#include <mach/iram.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "coda.h"
+
+#define CODA_NAME "coda"
+
+#define CODA_MAX_INSTANCES 4
+
+#define CODA_FMO_BUF_SIZE 32
+#define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
+#define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
+#define CODA_PARA_BUF_SIZE (10 * 1024)
+#define CODA_ISRAM_SIZE (2048 * 2)
+#define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */
+
+#define CODA_MAX_FRAMEBUFFERS 2
+
+#define MAX_W 720
+#define MAX_H 576
+#define CODA_MAX_FRAME_SIZE 0x90000
+#define FMO_SLICE_SAVE_BUF_SIZE (32)
+#define CODA_DEFAULT_GAMMA 4096
+
+#define MIN_W 176
+#define MIN_H 144
+#define MAX_W 720
+#define MAX_H 576
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN 1 /* multiple of 2 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh)
+
+static int coda_debug;
+module_param(coda_debug, int, 0);
+MODULE_PARM_DESC(coda_debug, "Debug level (0-1)");
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+enum coda_fmt_type {
+ CODA_FMT_ENC,
+ CODA_FMT_RAW,
+};
+
+enum coda_inst_type {
+ CODA_INST_ENCODER,
+ CODA_INST_DECODER,
+};
+
+enum coda_product {
+ CODA_DX6 = 0xf001,
+ CODA_7541 = 0xf012,
+};
+
+struct coda_fmt {
+ char *name;
+ u32 fourcc;
+ enum coda_fmt_type type;
+};
+
+struct coda_devtype {
+ char *firmware;
+ enum coda_product product;
+ struct coda_fmt *formats;
+ unsigned int num_formats;
+ size_t workbuf_size;
+};
+
+/* Per-queue, driver-specific private data */
+struct coda_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct coda_fmt *fmt;
+};
+
+struct coda_aux_buf {
+ void *vaddr;
+ dma_addr_t paddr;
+ u32 size;
+};
+
+struct coda_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd;
+ struct platform_device *plat_dev;
+ const struct coda_devtype *devtype;
+
+ void __iomem *regs_base;
+ struct clk *clk_per;
+ struct clk *clk_ahb;
+
+ struct coda_aux_buf codebuf;
+ struct coda_aux_buf workbuf;
+ long unsigned int iram_paddr;
+
+ spinlock_t irqlock;
+ struct mutex dev_mutex;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct list_head instances;
+ unsigned long instance_mask;
+ struct delayed_work timeout;
+ struct completion done;
+};
+
+struct coda_params {
+ u8 rot_mode;
+ u8 h264_intra_qp;
+ u8 h264_inter_qp;
+ u8 mpeg4_intra_qp;
+ u8 mpeg4_inter_qp;
+ u8 gop_size;
+ int codec_mode;
+ enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+ u32 framerate;
+ u16 bitrate;
+ u32 slice_max_bits;
+ u32 slice_max_mb;
+};
+
+struct coda_ctx {
+ struct coda_dev *dev;
+ struct list_head list;
+ int aborting;
+ int rawstreamon;
+ int compstreamon;
+ u32 isequence;
+ struct coda_q_data q_data[2];
+ enum coda_inst_type inst_type;
+ enum v4l2_colorspace colorspace;
+ struct coda_params params;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_fh fh;
+ int gopcounter;
+ char vpu_header[3][64];
+ int vpu_header_size[3];
+ struct coda_aux_buf parabuf;
+ struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
+ int num_internal_frames;
+ int idx;
+};
+
+static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg)
+{
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+ writel(data, dev->regs_base + reg);
+}
+
+static inline unsigned int coda_read(struct coda_dev *dev, u32 reg)
+{
+ u32 data;
+ data = readl(dev->regs_base + reg);
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg);
+ return data;
+}
+
+static inline unsigned long coda_isbusy(struct coda_dev *dev)
+{
+ return coda_read(dev, CODA_REG_BIT_BUSY);
+}
+
+static inline int coda_is_initialized(struct coda_dev *dev)
+{
+ return (coda_read(dev, CODA_REG_BIT_CUR_PC) != 0);
+}
+
+static int coda_wait_timeout(struct coda_dev *dev)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+
+ while (coda_isbusy(dev)) {
+ if (time_after(jiffies, timeout))
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static void coda_command_async(struct coda_ctx *ctx, int cmd)
+{
+ struct coda_dev *dev = ctx->dev;
+ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+
+ coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
+ coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
+}
+
+static int coda_command_sync(struct coda_ctx *ctx, int cmd)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ coda_command_async(ctx, cmd);
+ return coda_wait_timeout(dev);
+}
+
+static struct coda_q_data *get_q_data(struct coda_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &(ctx->q_data[V4L2_M2M_SRC]);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &(ctx->q_data[V4L2_M2M_DST]);
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/*
+ * Add one array of supported formats for each version of Coda:
+ * i.MX27 -> codadx6
+ * i.MX51 -> coda7
+ * i.MX6 -> coda960
+ */
+static struct coda_fmt codadx6_formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .type = CODA_FMT_RAW,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .type = CODA_FMT_ENC,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .type = CODA_FMT_ENC,
+ },
+};
+
+static struct coda_fmt coda7_formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .type = CODA_FMT_RAW,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .type = CODA_FMT_ENC,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .type = CODA_FMT_ENC,
+ },
+};
+
+static struct coda_fmt *find_format(struct coda_dev *dev, struct v4l2_format *f)
+{
+ struct coda_fmt *formats = dev->devtype->formats;
+ int num_formats = dev->devtype->num_formats;
+ unsigned int k;
+
+ for (k = 0; k < num_formats; k++) {
+ if (formats[k].fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == num_formats)
+ return NULL;
+
+ return &formats[k];
+}
+
+/*
+ * V4L2 ioctl() operations.
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, CODA_NAME, sizeof(cap->card));
+ strlcpy(cap->bus_info, CODA_NAME, sizeof(cap->bus_info));
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
+ enum coda_fmt_type type)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct coda_dev *dev = ctx->dev;
+ struct coda_fmt *formats = dev->devtype->formats;
+ struct coda_fmt *fmt;
+ int num_formats = dev->devtype->num_formats;
+ int i, num = 0;
+
+ for (i = 0; i < num_formats; i++) {
+ if (formats[i].type == type) {
+ if (num == f->index)
+ break;
+ ++num;
+ }
+ }
+
+ if (i < num_formats) {
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(priv, f, CODA_FMT_ENC);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(priv, f, CODA_FMT_RAW);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct coda_q_data *q_data;
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
+ else /* encoded formats h.264/mpeg4 */
+ f->fmt.pix.bytesperline = 0;
+
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return 0;
+}
+
+static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f)
+{
+ enum v4l2_field field;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2);
+ f->fmt.pix.sizeimage = f->fmt.pix.width *
+ f->fmt.pix.height * 3 / 2;
+ } else { /*encoded formats h.264/mpeg4 */
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE;
+ }
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+ struct coda_fmt *fmt;
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ fmt = find_format(ctx->dev, f);
+ /*
+ * Since decoding support is not implemented yet do not allow
+ * CODA_FMT_RAW formats in the capture interface.
+ */
+ if (!fmt || !(fmt->type == CODA_FMT_ENC))
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
+
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ ret = vidioc_try_fmt(ctx->dev, f);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct coda_fmt *fmt;
+ int ret;
+
+ fmt = find_format(ctx->dev, f);
+ /*
+ * Since decoding support is not implemented yet do not allow
+ * CODA_FMT formats in the capture interface.
+ */
+ if (!fmt || !(fmt->type == CODA_FMT_RAW))
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+
+ if (!f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+ ret = vidioc_try_fmt(ctx->dev, f);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
+{
+ struct coda_q_data *q_data;
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = vidioc_try_fmt(ctx->dev, f);
+ if (ret)
+ return ret;
+
+ q_data->fmt = find_format(ctx->dev, f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ q_data->sizeimage = f->fmt.pix.sizeimage;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(fh_to_ctx(priv), f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ ret = vidioc_s_fmt(ctx, f);
+ if (ret)
+ ctx->colorspace = f->fmt.pix.colorspace;
+
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops coda_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+/*
+ * Mem-to-mem operations.
+ */
+static void coda_device_run(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_q_data *q_data_src, *q_data_dst;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ int force_ipicture;
+ int quant_param = 0;
+ u32 picture_y, picture_cb, picture_cr;
+ u32 pic_stream_buffer_addr, pic_stream_buffer_size;
+ u32 dst_fourcc;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ dst_fourcc = q_data_dst->fmt->fourcc;
+
+ src_buf->v4l2_buf.sequence = ctx->isequence;
+ dst_buf->v4l2_buf.sequence = ctx->isequence;
+ ctx->isequence++;
+
+ /*
+ * Workaround coda firmware BUG that only marks the first
+ * frame as IDR. This is a problem for some decoders that can't
+ * recover when a frame is lost.
+ */
+ if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) {
+ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ } else {
+ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ }
+
+ /*
+ * Copy headers at the beginning of the first frame for H.264 only.
+ * In MPEG4 they are already copied by the coda.
+ */
+ if (src_buf->v4l2_buf.sequence == 0) {
+ pic_stream_buffer_addr =
+ vb2_dma_contig_plane_dma_addr(dst_buf, 0) +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2];
+ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE -
+ ctx->vpu_header_size[0] -
+ ctx->vpu_header_size[1] -
+ ctx->vpu_header_size[2];
+ memcpy(vb2_plane_vaddr(dst_buf, 0),
+ &ctx->vpu_header[0][0], ctx->vpu_header_size[0]);
+ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0],
+ &ctx->vpu_header[1][0], ctx->vpu_header_size[1]);
+ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1], &ctx->vpu_header[2][0],
+ ctx->vpu_header_size[2]);
+ } else {
+ pic_stream_buffer_addr =
+ vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE;
+ }
+
+ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+ force_ipicture = 1;
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ quant_param = ctx->params.h264_intra_qp;
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ quant_param = ctx->params.mpeg4_intra_qp;
+ break;
+ default:
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "cannot set intra qp, fmt not supported\n");
+ break;
+ }
+ } else {
+ force_ipicture = 0;
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ quant_param = ctx->params.h264_inter_qp;
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ quant_param = ctx->params.mpeg4_inter_qp;
+ break;
+ default:
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "cannot set inter qp, fmt not supported\n");
+ break;
+ }
+ }
+
+ /* submit */
+ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_ENC_PIC_ROT_MODE);
+ coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS);
+
+
+ picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ picture_cb = picture_y + q_data_src->width * q_data_src->height;
+ picture_cr = picture_cb + q_data_src->width / 2 *
+ q_data_src->height / 2;
+
+ coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y);
+ coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB);
+ coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR);
+ coda_write(dev, force_ipicture << 1 & 0x2,
+ CODA_CMD_ENC_PIC_OPTION);
+
+ coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
+ coda_write(dev, pic_stream_buffer_size / 1024,
+ CODA_CMD_ENC_PIC_BB_SIZE);
+
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE |
+ CODA7_USE_ME_ENABLE | CODA7_USE_HOST_ME_ENABLE,
+ CODA7_REG_BIT_AXI_SRAM_USE);
+ }
+
+ /* 1 second timeout in case CODA locks up */
+ schedule_delayed_work(&dev->timeout, HZ);
+
+ INIT_COMPLETION(dev->done);
+ coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
+}
+
+static int coda_job_ready(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+
+ /*
+ * For both 'P' and 'key' frame cases 1 picture
+ * and 1 frame are needed.
+ */
+ if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) ||
+ !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: not enough video buffers.\n");
+ return 0;
+ }
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "job ready\n");
+ return 1;
+}
+
+static void coda_job_abort(void *priv)
+{
+ struct coda_ctx *ctx = priv;
+ struct coda_dev *dev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Aborting task\n");
+
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void coda_lock(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void coda_unlock(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static struct v4l2_m2m_ops coda_m2m_ops = {
+ .device_run = coda_device_run,
+ .job_ready = coda_job_ready,
+ .job_abort = coda_job_abort,
+ .lock = coda_lock,
+ .unlock = coda_unlock,
+};
+
+static void set_default_params(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ ctx->params.codec_mode = CODA_MODE_INVALID;
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+ ctx->params.framerate = 30;
+ ctx->aborting = 0;
+
+ /* Default formats for output and input queues */
+ ctx->q_data[V4L2_M2M_SRC].fmt = &dev->devtype->formats[0];
+ ctx->q_data[V4L2_M2M_DST].fmt = &dev->devtype->formats[1];
+ ctx->q_data[V4L2_M2M_SRC].width = MAX_W;
+ ctx->q_data[V4L2_M2M_SRC].height = MAX_H;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage = (MAX_W * MAX_H * 3) / 2;
+ ctx->q_data[V4L2_M2M_DST].width = MAX_W;
+ ctx->q_data[V4L2_M2M_DST].height = MAX_H;
+ ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE;
+}
+
+/*
+ * Queue operations
+ */
+static int coda_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vq);
+ struct coda_q_data *q_data;
+ unsigned int size;
+
+ q_data = get_q_data(ctx, vq->type);
+ size = q_data->sizeimage;
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "get %d buffer(s) of size %d each.\n", *nbuffers, size);
+
+ return 0;
+}
+
+static int coda_buf_prepare(struct vb2_buffer *vb)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct coda_q_data *q_data;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ v4l2_warn(&ctx->dev->v4l2_dev,
+ "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void coda_buf_queue(struct vb2_buffer *vb)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void coda_wait_prepare(struct vb2_queue *q)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ coda_unlock(ctx);
+}
+
+static void coda_wait_finish(struct vb2_queue *q)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ coda_lock(ctx);
+}
+
+static void coda_free_framebuffers(struct coda_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) {
+ if (ctx->internal_frames[i].vaddr) {
+ dma_free_coherent(&ctx->dev->plat_dev->dev,
+ ctx->internal_frames[i].size,
+ ctx->internal_frames[i].vaddr,
+ ctx->internal_frames[i].paddr);
+ ctx->internal_frames[i].vaddr = NULL;
+ }
+ }
+}
+
+static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ int height = q_data->height;
+ int width = q_data->width;
+ u32 *p;
+ int i;
+
+ /* Allocate frame buffers */
+ ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS;
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ ctx->internal_frames[i].size = q_data->sizeimage;
+ if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6)
+ ctx->internal_frames[i].size += width / 2 * height / 2;
+ ctx->internal_frames[i].vaddr = dma_alloc_coherent(
+ &dev->plat_dev->dev, ctx->internal_frames[i].size,
+ &ctx->internal_frames[i].paddr, GFP_KERNEL);
+ if (!ctx->internal_frames[i].vaddr) {
+ coda_free_framebuffers(ctx);
+ return -ENOMEM;
+ }
+ }
+
+ /* Register frame buffers in the parameter buffer */
+ p = ctx->parabuf.vaddr;
+
+ if (dev->devtype->product == CODA_DX6) {
+ for (i = 0; i < ctx->num_internal_frames; i++) {
+ p[i * 3] = ctx->internal_frames[i].paddr; /* Y */
+ p[i * 3 + 1] = p[i * 3] + width * height; /* Cb */
+ p[i * 3 + 2] = p[i * 3 + 1] + width / 2 * height / 2; /* Cr */
+ }
+ } else {
+ for (i = 0; i < ctx->num_internal_frames; i += 2) {
+ p[i * 3 + 1] = ctx->internal_frames[i].paddr; /* Y */
+ p[i * 3] = p[i * 3 + 1] + width * height; /* Cb */
+ p[i * 3 + 3] = p[i * 3] + (width / 2) * (height / 2); /* Cr */
+
+ if (fourcc == V4L2_PIX_FMT_H264)
+ p[96 + i + 1] = p[i * 3 + 3] + (width / 2) * (height / 2);
+
+ if (i + 1 < ctx->num_internal_frames) {
+ p[i * 3 + 2] = ctx->internal_frames[i+1].paddr; /* Y */
+ p[i * 3 + 5] = p[i * 3 + 2] + width * height ; /* Cb */
+ p[i * 3 + 4] = p[i * 3 + 5] + (width / 2) * (height / 2); /* Cr */
+
+ if (fourcc == V4L2_PIX_FMT_H264)
+ p[96 + i] = p[i * 3 + 4] + (width / 2) * (height / 2);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev;
+ u32 bitstream_buf, bitstream_size;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_src, *q_data_dst;
+ struct vb2_buffer *buf;
+ u32 dst_fourcc;
+ u32 value;
+ int ret;
+
+ if (count < 1)
+ return -EINVAL;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ctx->rawstreamon = 1;
+ else
+ ctx->compstreamon = 1;
+
+ /* Don't start the coda unless both queues are on */
+ if (!(ctx->rawstreamon & ctx->compstreamon))
+ return 0;
+
+ if (coda_isbusy(dev))
+ if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0)
+ return -EBUSY;
+
+ ctx->gopcounter = ctx->params.gop_size - 1;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ bitstream_size = q_data_dst->sizeimage;
+ dst_fourcc = q_data_dst->fmt->fourcc;
+
+ /* Find out whether coda must encode or decode */
+ if (q_data_src->fmt->type == CODA_FMT_RAW &&
+ q_data_dst->fmt->type == CODA_FMT_ENC) {
+ ctx->inst_type = CODA_INST_ENCODER;
+ } else if (q_data_src->fmt->type == CODA_FMT_ENC &&
+ q_data_dst->fmt->type == CODA_FMT_RAW) {
+ ctx->inst_type = CODA_INST_DECODER;
+ v4l2_err(v4l2_dev, "decoding not supported.\n");
+ return -EINVAL;
+ } else {
+ v4l2_err(v4l2_dev, "couldn't tell instance type.\n");
+ return -EINVAL;
+ }
+
+ if (!coda_is_initialized(dev)) {
+ v4l2_err(v4l2_dev, "coda is not initialized.\n");
+ return -EFAULT;
+ }
+ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx));
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
+ CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+ break;
+ default:
+ coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN |
+ CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL);
+ }
+
+ if (dev->devtype->product == CODA_DX6) {
+ /* Configure the coda */
+ coda_write(dev, dev->iram_paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR);
+ }
+
+ /* Could set rotation here if needed */
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET;
+ break;
+ default:
+ value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET;
+ }
+ value |= (q_data_src->height & CODA_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE);
+ coda_write(dev, ctx->params.framerate,
+ CODA_CMD_ENC_SEQ_SRC_F_RATE);
+
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_MPEG4:
+ if (dev->devtype->product == CODA_DX6)
+ ctx->params.codec_mode = CODADX6_MODE_ENCODE_MP4;
+ else
+ ctx->params.codec_mode = CODA7_MODE_ENCODE_MP4;
+
+ coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA);
+ break;
+ case V4L2_PIX_FMT_H264:
+ if (dev->devtype->product == CODA_DX6)
+ ctx->params.codec_mode = CODADX6_MODE_ENCODE_H264;
+ else
+ ctx->params.codec_mode = CODA7_MODE_ENCODE_H264;
+
+ coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA);
+ break;
+ default:
+ v4l2_err(v4l2_dev,
+ "dst format (0x%08x) invalid.\n", dst_fourcc);
+ return -EINVAL;
+ }
+
+ switch (ctx->params.slice_mode) {
+ case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+ value = 0;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+ value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
+ value |= (1 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+ value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET;
+ value |= (0 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET;
+ value |= 1 & CODA_SLICING_MODE_MASK;
+ break;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE);
+ value = ctx->params.gop_size & CODA_GOP_SIZE_MASK;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE);
+
+ if (ctx->params.bitrate) {
+ /* Rate control enabled */
+ value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET;
+ value |= 1 & CODA_RATECONTROL_ENABLE_MASK;
+ } else {
+ value = 0;
+ }
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
+
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
+ coda_write(dev, 0, CODA_CMD_ENC_SEQ_INTRA_REFRESH);
+
+ coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START);
+ coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE);
+
+ /* set default gamma */
+ value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA);
+
+ value = (CODA_DEFAULT_GAMMA > 0) << CODA_OPTION_GAMMA_OFFSET;
+ value |= (0 & CODA_OPTION_SLICEREPORT_MASK) << CODA_OPTION_SLICEREPORT_OFFSET;
+ coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
+
+ if (dst_fourcc == V4L2_PIX_FMT_H264) {
+ value = (FMO_SLICE_SAVE_BUF_SIZE << 7);
+ value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET;
+ value |= 0 & CODA_FMOPARAM_SLICENUM_MASK;
+ if (dev->devtype->product == CODA_DX6) {
+ coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
+ } else {
+ coda_write(dev, dev->iram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE);
+ coda_write(dev, 48 * 1024, CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
+ }
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0)
+ return -EFAULT;
+
+ ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
+ if (ret < 0)
+ return ret;
+
+ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+ coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE);
+ if (dev->devtype->product != CODA_DX6) {
+ coda_write(dev, round_up(q_data_src->width, 8), CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
+ coda_write(dev, dev->iram_paddr + 48 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, dev->iram_paddr + 53 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, dev->iram_paddr + 58 * 1024, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ }
+ if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* Save stream headers */
+ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ switch (dst_fourcc) {
+ case V4L2_PIX_FMT_H264:
+ /*
+ * Get SPS in the first frame and copy it to an
+ * intermediate buffer.
+ */
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, CODA_HEADER_H264_SPS, CODA_CMD_ENC_HEADER_CODE);
+ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return -ETIMEDOUT;
+ }
+ ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
+ ctx->vpu_header_size[0]);
+
+ /*
+ * Get PPS in the first frame and copy it to an
+ * intermediate buffer.
+ */
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, CODA_HEADER_H264_PPS, CODA_CMD_ENC_HEADER_CODE);
+ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return -ETIMEDOUT;
+ }
+ ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
+ ctx->vpu_header_size[1]);
+ ctx->vpu_header_size[2] = 0;
+ break;
+ case V4L2_PIX_FMT_MPEG4:
+ /*
+ * Get VOS in the first frame and copy it to an
+ * intermediate buffer
+ */
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, CODA_HEADER_MP4V_VOS, CODA_CMD_ENC_HEADER_CODE);
+ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
+ return -ETIMEDOUT;
+ }
+ ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0),
+ ctx->vpu_header_size[0]);
+
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, CODA_HEADER_MP4V_VIS, CODA_CMD_ENC_HEADER_CODE);
+ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
+ return -ETIMEDOUT;
+ }
+ ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0),
+ ctx->vpu_header_size[1]);
+
+ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START);
+ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE);
+ coda_write(dev, CODA_HEADER_MP4V_VOL, CODA_CMD_ENC_HEADER_CODE);
+ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) {
+ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n");
+ return -ETIMEDOUT;
+ }
+ ctx->vpu_header_size[2] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
+ memcpy(&ctx->vpu_header[2][0], vb2_plane_vaddr(buf, 0),
+ ctx->vpu_header_size[2]);
+ break;
+ default:
+ /* No more formats need to save headers at the moment */
+ break;
+ }
+
+ return 0;
+}
+
+static int coda_stop_streaming(struct vb2_queue *q)
+{
+ struct coda_ctx *ctx = vb2_get_drv_priv(q);
+ struct coda_dev *dev = ctx->dev;
+
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%s: output\n", __func__);
+ ctx->rawstreamon = 0;
+ } else {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%s: capture\n", __func__);
+ ctx->compstreamon = 0;
+ }
+
+ /* Don't stop the coda unless both queues are off */
+ if (ctx->rawstreamon || ctx->compstreamon)
+ return 0;
+
+ if (coda_isbusy(dev)) {
+ if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) {
+ v4l2_warn(&dev->v4l2_dev,
+ "%s: timeout, sending SEQ_END anyway\n", __func__);
+ }
+ }
+
+ cancel_delayed_work(&dev->timeout);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%s: sent command 'SEQ_END' to coda\n", __func__);
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ return -ETIMEDOUT;
+ }
+
+ coda_free_framebuffers(ctx);
+
+ return 0;
+}
+
+static struct vb2_ops coda_qops = {
+ .queue_setup = coda_queue_setup,
+ .buf_prepare = coda_buf_prepare,
+ .buf_queue = coda_buf_queue,
+ .wait_prepare = coda_wait_prepare,
+ .wait_finish = coda_wait_finish,
+ .start_streaming = coda_start_streaming,
+ .stop_streaming = coda_stop_streaming,
+};
+
+static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct coda_ctx *ctx =
+ container_of(ctrl->handler, struct coda_ctx, ctrls);
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ ctx->params.rot_mode |= CODA_MIR_HOR;
+ else
+ ctx->params.rot_mode &= ~CODA_MIR_HOR;
+ break;
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ ctx->params.rot_mode |= CODA_MIR_VER;
+ else
+ ctx->params.rot_mode &= ~CODA_MIR_VER;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctx->params.bitrate = ctrl->val / 1000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ ctx->params.gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ ctx->params.h264_intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ ctx->params.h264_inter_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+ ctx->params.mpeg4_intra_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+ ctx->params.mpeg4_inter_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ ctx->params.slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ ctx->params.slice_max_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ ctx->params.slice_max_bits = ctrl->val * 8;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ break;
+ default:
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "Invalid control, id=%d, val=%d\n",
+ ctrl->id, ctrl->val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops coda_ctrl_ops = {
+ .s_ctrl = coda_s_ctrl,
+};
+
+static int coda_ctrls_setup(struct coda_ctx *ctx)
+{
+ v4l2_ctrl_handler_init(&ctx->ctrls, 9);
+
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 25);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 25);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2);
+ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0,
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 500);
+ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE),
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+
+ if (ctx->ctrls.error) {
+ v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)",
+ ctx->ctrls.error);
+ return -EINVAL;
+ }
+
+ return v4l2_ctrl_handler_setup(&ctx->ctrls);
+}
+
+static int coda_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct coda_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &coda_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &coda_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int coda_next_free_instance(struct coda_dev *dev)
+{
+ return ffz(dev->instance_mask);
+}
+
+static int coda_open(struct file *file)
+{
+ struct coda_dev *dev = video_drvdata(file);
+ struct coda_ctx *ctx = NULL;
+ int ret = 0;
+ int idx;
+
+ idx = coda_next_free_instance(dev);
+ if (idx >= CODA_MAX_INSTANCES)
+ return -EBUSY;
+ set_bit(idx, &dev->instance_mask);
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ ctx->idx = idx;
+
+ set_default_params(ctx);
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
+ &coda_queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = coda_ctrls_setup(ctx);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
+ goto err;
+ }
+
+ ctx->fh.ctrl_handler = &ctx->ctrls;
+
+ ctx->parabuf.vaddr = dma_alloc_coherent(&dev->plat_dev->dev,
+ CODA_PARA_BUF_SIZE, &ctx->parabuf.paddr, GFP_KERNEL);
+ if (!ctx->parabuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ coda_lock(ctx);
+ list_add(&ctx->list, &dev->instances);
+ coda_unlock(ctx);
+
+ clk_prepare_enable(dev->clk_per);
+ clk_prepare_enable(dev->clk_ahb);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
+ ctx->idx, ctx);
+
+ return 0;
+
+err:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+static int coda_release(struct file *file)
+{
+ struct coda_dev *dev = video_drvdata(file);
+ struct coda_ctx *ctx = fh_to_ctx(file->private_data);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
+ ctx);
+
+ coda_lock(ctx);
+ list_del(&ctx->list);
+ coda_unlock(ctx);
+
+ dma_free_coherent(&dev->plat_dev->dev, CODA_PARA_BUF_SIZE,
+ ctx->parabuf.vaddr, ctx->parabuf.paddr);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrls);
+ clk_disable_unprepare(dev->clk_per);
+ clk_disable_unprepare(dev->clk_ahb);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ clear_bit(ctx->idx, &dev->instance_mask);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int coda_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct coda_ctx *ctx = fh_to_ctx(file->private_data);
+ int ret;
+
+ coda_lock(ctx);
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ coda_unlock(ctx);
+ return ret;
+}
+
+static int coda_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct coda_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations coda_fops = {
+ .owner = THIS_MODULE,
+ .open = coda_open,
+ .release = coda_release,
+ .poll = coda_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = coda_mmap,
+};
+
+static irqreturn_t coda_irq_handler(int irq, void *data)
+{
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = data;
+ u32 wr_ptr, start_ptr;
+ struct coda_ctx *ctx;
+
+ __cancel_delayed_work(&dev->timeout);
+
+ /* read status register to attend the IRQ */
+ coda_read(dev, CODA_REG_BIT_INT_STATUS);
+ coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
+ CODA_REG_BIT_INT_CLEAR);
+
+ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev);
+ if (ctx == NULL) {
+ v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n");
+ return IRQ_HANDLED;
+ }
+
+ if (ctx->aborting) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "task has been aborted\n");
+ return IRQ_HANDLED;
+ }
+
+ if (coda_isbusy(ctx->dev)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "coda is still busy!!!!\n");
+ return IRQ_NONE;
+ }
+
+ complete(&dev->done);
+
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ /* Get results from the coda */
+ coda_read(dev, CODA_RET_ENC_PIC_TYPE);
+ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
+ wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx));
+ /* Calculate bytesused field */
+ if (dst_buf->v4l2_buf.sequence == 0) {
+ dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr) +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2];
+ } else {
+ dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr);
+ }
+
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
+ wr_ptr - start_ptr);
+
+ coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM);
+ coda_read(dev, CODA_RET_ENC_PIC_FLAG);
+
+ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ } else {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ }
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+
+ ctx->gopcounter--;
+ if (ctx->gopcounter < 0)
+ ctx->gopcounter = ctx->params.gop_size - 1;
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: encoding frame (%d) (%s)\n",
+ dst_buf->v4l2_buf.sequence,
+ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+ "KEYFRAME" : "PFRAME");
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
+
+ return IRQ_HANDLED;
+}
+
+static void coda_timeout(struct work_struct *work)
+{
+ struct coda_ctx *ctx;
+ struct coda_dev *dev = container_of(to_delayed_work(work),
+ struct coda_dev, timeout);
+
+ if (completion_done(&dev->done))
+ return;
+
+ complete(&dev->done);
+
+ v4l2_err(&dev->v4l2_dev, "CODA PIC_RUN timeout, stopping all streams\n");
+
+ mutex_lock(&dev->dev_mutex);
+ list_for_each_entry(ctx, &dev->instances, list) {
+ v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ }
+ mutex_unlock(&dev->dev_mutex);
+}
+
+static u32 coda_supported_firmwares[] = {
+ CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
+ CODA_FIRMWARE_VERNUM(CODA_7541, 13, 4, 29),
+};
+
+static bool coda_firmware_supported(u32 vernum)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++)
+ if (vernum == coda_supported_firmwares[i])
+ return true;
+ return false;
+}
+
+static char *coda_product_name(int product)
+{
+ static char buf[9];
+
+ switch (product) {
+ case CODA_DX6:
+ return "CodaDx6";
+ case CODA_7541:
+ return "CODA7541";
+ default:
+ snprintf(buf, sizeof(buf), "(0x%04x)", product);
+ return buf;
+ }
+}
+
+static int coda_hw_init(struct coda_dev *dev)
+{
+ u16 product, major, minor, release;
+ u32 data;
+ u16 *p;
+ int i;
+
+ clk_prepare_enable(dev->clk_per);
+ clk_prepare_enable(dev->clk_ahb);
+
+ /*
+ * Copy the first CODA_ISRAM_SIZE in the internal SRAM.
+ * The 16-bit chars in the code buffer are in memory access
+ * order, re-sort them to CODA order for register download.
+ * Data in this SRAM survives a reboot.
+ */
+ p = (u16 *)dev->codebuf.vaddr;
+ if (dev->devtype->product == CODA_DX6) {
+ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
+ data = CODA_DOWN_ADDRESS_SET(i) |
+ CODA_DOWN_DATA_SET(p[i ^ 1]);
+ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+ }
+ } else {
+ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) {
+ data = CODA_DOWN_ADDRESS_SET(i) |
+ CODA_DOWN_DATA_SET(p[round_down(i, 4) +
+ 3 - (i % 4)]);
+ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN);
+ }
+ }
+
+ /* Tell the BIT where to find everything it needs */
+ coda_write(dev, dev->workbuf.paddr,
+ CODA_REG_BIT_WORK_BUF_ADDR);
+ coda_write(dev, dev->codebuf.paddr,
+ CODA_REG_BIT_CODE_BUF_ADDR);
+ coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
+
+ /* Set default values */
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
+ break;
+ default:
+ coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL);
+ }
+ coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL);
+
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE);
+
+ coda_write(dev, CODA_INT_INTERRUPT_ENABLE,
+ CODA_REG_BIT_INT_ENABLE);
+
+ /* Reset VPU and start processor */
+ data = coda_read(dev, CODA_REG_BIT_CODE_RESET);
+ data |= CODA_REG_RESET_ENABLE;
+ coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+ udelay(10);
+ data &= ~CODA_REG_RESET_ENABLE;
+ coda_write(dev, data, CODA_REG_BIT_CODE_RESET);
+ coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN);
+
+ /* Load firmware */
+ coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM);
+ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
+ coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX);
+ coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND);
+ if (coda_wait_timeout(dev)) {
+ clk_disable_unprepare(dev->clk_per);
+ clk_disable_unprepare(dev->clk_ahb);
+ v4l2_err(&dev->v4l2_dev, "firmware get command error\n");
+ return -EIO;
+ }
+
+ /* Check we are compatible with the loaded firmware */
+ data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM);
+ product = CODA_FIRMWARE_PRODUCT(data);
+ major = CODA_FIRMWARE_MAJOR(data);
+ minor = CODA_FIRMWARE_MINOR(data);
+ release = CODA_FIRMWARE_RELEASE(data);
+
+ clk_disable_unprepare(dev->clk_per);
+ clk_disable_unprepare(dev->clk_ahb);
+
+ if (product != dev->devtype->product) {
+ v4l2_err(&dev->v4l2_dev, "Wrong firmware. Hw: %s, Fw: %s,"
+ " Version: %u.%u.%u\n",
+ coda_product_name(dev->devtype->product),
+ coda_product_name(product), major, minor, release);
+ return -EINVAL;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "Initialized %s.\n",
+ coda_product_name(product));
+
+ if (coda_firmware_supported(data)) {
+ v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n",
+ major, minor, release);
+ } else {
+ v4l2_warn(&dev->v4l2_dev, "Unsupported firmware version: "
+ "%u.%u.%u\n", major, minor, release);
+ }
+
+ return 0;
+}
+
+static void coda_fw_callback(const struct firmware *fw, void *context)
+{
+ struct coda_dev *dev = context;
+ struct platform_device *pdev = dev->plat_dev;
+ int ret;
+
+ if (!fw) {
+ v4l2_err(&dev->v4l2_dev, "firmware request failed\n");
+ return;
+ }
+
+ /* allocate auxiliary per-device code buffer for the BIT processor */
+ dev->codebuf.size = fw->size;
+ dev->codebuf.vaddr = dma_alloc_coherent(&pdev->dev, fw->size,
+ &dev->codebuf.paddr,
+ GFP_KERNEL);
+ if (!dev->codebuf.vaddr) {
+ dev_err(&pdev->dev, "failed to allocate code buffer\n");
+ return;
+ }
+
+ /* Copy the whole firmware image to the code buffer */
+ memcpy(dev->codebuf.vaddr, fw->data, fw->size);
+ release_firmware(fw);
+
+ ret = coda_hw_init(dev);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "HW initialization failed\n");
+ return;
+ }
+
+ dev->vfd.fops = &coda_fops,
+ dev->vfd.ioctl_ops = &coda_ioctl_ops;
+ dev->vfd.release = video_device_release_empty,
+ dev->vfd.lock = &dev->dev_mutex;
+ dev->vfd.v4l2_dev = &dev->v4l2_dev;
+ dev->vfd.vfl_dir = VFL_DIR_M2M;
+ snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME);
+ video_set_drvdata(&dev->vfd, dev);
+
+ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n");
+ return;
+ }
+
+ dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ goto rel_ctx;
+ }
+
+ ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_m2m;
+ }
+ v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video%d\n",
+ dev->vfd.num);
+
+ return;
+
+rel_m2m:
+ v4l2_m2m_release(dev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+}
+
+static int coda_firmware_request(struct coda_dev *dev)
+{
+ char *fw = dev->devtype->firmware;
+
+ dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw,
+ coda_product_name(dev->devtype->product));
+
+ return request_firmware_nowait(THIS_MODULE, true,
+ fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback);
+}
+
+enum coda_platform {
+ CODA_IMX27,
+ CODA_IMX53,
+};
+
+static const struct coda_devtype coda_devdata[] = {
+ [CODA_IMX27] = {
+ .firmware = "v4l-codadx6-imx27.bin",
+ .product = CODA_DX6,
+ .formats = codadx6_formats,
+ .num_formats = ARRAY_SIZE(codadx6_formats),
+ },
+ [CODA_IMX53] = {
+ .firmware = "v4l-coda7541-imx53.bin",
+ .product = CODA_7541,
+ .formats = coda7_formats,
+ .num_formats = ARRAY_SIZE(coda7_formats),
+ },
+};
+
+static struct platform_device_id coda_platform_ids[] = {
+ { .name = "coda-imx27", .driver_data = CODA_IMX27 },
+ { .name = "coda-imx53", .driver_data = CODA_7541 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, coda_platform_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id coda_dt_ids[] = {
+ { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] },
+ { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, coda_dt_ids);
+#endif
+
+static int __devinit coda_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev);
+ const struct platform_device_id *pdev_id;
+ struct coda_dev *dev;
+ struct resource *res;
+ int ret, irq;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for %s\n",
+ CODA_NAME);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->irqlock);
+ INIT_LIST_HEAD(&dev->instances);
+ INIT_DELAYED_WORK(&dev->timeout, coda_timeout);
+ init_completion(&dev->done);
+ complete(&dev->done);
+
+ dev->plat_dev = pdev;
+ dev->clk_per = devm_clk_get(&pdev->dev, "per");
+ if (IS_ERR(dev->clk_per)) {
+ dev_err(&pdev->dev, "Could not get per clock\n");
+ return PTR_ERR(dev->clk_per);
+ }
+
+ dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(dev->clk_ahb)) {
+ dev_err(&pdev->dev, "Could not get ahb clock\n");
+ return PTR_ERR(dev->clk_ahb);
+ }
+
+ /* Get memory for physical registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ if (devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), CODA_NAME) == NULL) {
+ dev_err(&pdev->dev, "failed to request memory region\n");
+ return -ENOENT;
+ }
+ dev->regs_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!dev->regs_base) {
+ dev_err(&pdev->dev, "failed to ioremap address region\n");
+ return -ENOENT;
+ }
+
+ /* IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ return -ENOENT;
+ }
+
+ if (devm_request_irq(&pdev->dev, irq, coda_irq_handler,
+ 0, CODA_NAME, dev) < 0) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return -ENOENT;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&dev->dev_mutex);
+
+ pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
+
+ if (of_id) {
+ dev->devtype = of_id->data;
+ } else if (pdev_id) {
+ dev->devtype = &coda_devdata[pdev_id->driver_data];
+ } else {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -EINVAL;
+ }
+
+ /* allocate auxiliary per-device buffers for the BIT processor */
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ dev->workbuf.size = CODADX6_WORK_BUF_SIZE;
+ break;
+ default:
+ dev->workbuf.size = CODA7_WORK_BUF_SIZE;
+ }
+ dev->workbuf.vaddr = dma_alloc_coherent(&pdev->dev, dev->workbuf.size,
+ &dev->workbuf.paddr,
+ GFP_KERNEL);
+ if (!dev->workbuf.vaddr) {
+ dev_err(&pdev->dev, "failed to allocate work buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return -ENOMEM;
+ }
+
+ if (dev->devtype->product == CODA_DX6) {
+ dev->iram_paddr = 0xffff4c00;
+ } else {
+ void __iomem *iram_vaddr;
+
+ iram_vaddr = iram_alloc(CODA7_IRAM_SIZE,
+ &dev->iram_paddr);
+ if (!iram_vaddr) {
+ dev_err(&pdev->dev, "unable to alloc iram\n");
+ return -ENOMEM;
+ }
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ return coda_firmware_request(dev);
+}
+
+static int coda_remove(struct platform_device *pdev)
+{
+ struct coda_dev *dev = platform_get_drvdata(pdev);
+
+ video_unregister_device(&dev->vfd);
+ if (dev->m2m_dev)
+ v4l2_m2m_release(dev->m2m_dev);
+ if (dev->alloc_ctx)
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ if (dev->iram_paddr)
+ iram_free(dev->iram_paddr, CODA7_IRAM_SIZE);
+ if (dev->codebuf.vaddr)
+ dma_free_coherent(&pdev->dev, dev->codebuf.size,
+ &dev->codebuf.vaddr, dev->codebuf.paddr);
+ if (dev->workbuf.vaddr)
+ dma_free_coherent(&pdev->dev, dev->workbuf.size, &dev->workbuf.vaddr,
+ dev->workbuf.paddr);
+ return 0;
+}
+
+static struct platform_driver coda_driver = {
+ .probe = coda_probe,
+ .remove = __devexit_p(coda_remove),
+ .driver = {
+ .name = CODA_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(coda_dt_ids),
+ },
+ .id_table = coda_platform_ids,
+};
+
+module_platform_driver(coda_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver");
diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h
new file mode 100644
index 000000000000..f3f5e43c1ac2
--- /dev/null
+++ b/drivers/media/platform/coda.h
@@ -0,0 +1,238 @@
+/*
+ * linux/drivers/media/platform/coda/coda_regs.h
+ *
+ * Copyright (C) 2012 Vista Silicon SL
+ * Javier Martin <javier.martin@vista-silicon.com>
+ * Xavier Duret
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 _REGS_CODA_H_
+#define _REGS_CODA_H_
+
+/* HW registers */
+#define CODA_REG_BIT_CODE_RUN 0x000
+#define CODA_REG_RUN_ENABLE (1 << 0)
+#define CODA_REG_BIT_CODE_DOWN 0x004
+#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16)
+#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff)
+#define CODA_REG_BIT_HOST_IN_REQ 0x008
+#define CODA_REG_BIT_INT_CLEAR 0x00c
+#define CODA_REG_BIT_INT_CLEAR_SET 0x1
+#define CODA_REG_BIT_INT_STATUS 0x010
+#define CODA_REG_BIT_CODE_RESET 0x014
+#define CODA_REG_RESET_ENABLE (1 << 0)
+#define CODA_REG_BIT_CUR_PC 0x018
+
+/* Static SW registers */
+#define CODA_REG_BIT_CODE_BUF_ADDR 0x100
+#define CODA_REG_BIT_WORK_BUF_ADDR 0x104
+#define CODA_REG_BIT_PARA_BUF_ADDR 0x108
+#define CODA_REG_BIT_STREAM_CTRL 0x10c
+#define CODA7_STREAM_BUF_PIC_RESET (1 << 4)
+#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3)
+#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3)
+#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2)
+#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5)
+#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4)
+#define CODA_STREAM_CHKDIS_OFFSET (1 << 1)
+#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
+#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
+#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
+#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x))
+#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x))
+#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140
+#define CODA7_REG_BIT_AXI_SRAM_USE 0x140
+#define CODA7_USE_BIT_ENABLE (1 << 0)
+#define CODA7_USE_HOST_BIT_ENABLE (1 << 7)
+#define CODA7_USE_ME_ENABLE (1 << 4)
+#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
+#define CODA_REG_BIT_BUSY 0x160
+#define CODA_REG_BIT_BUSY_FLAG 1
+#define CODA_REG_BIT_RUN_COMMAND 0x164
+#define CODA_COMMAND_SEQ_INIT 1
+#define CODA_COMMAND_SEQ_END 2
+#define CODA_COMMAND_PIC_RUN 3
+#define CODA_COMMAND_SET_FRAME_BUF 4
+#define CODA_COMMAND_ENCODE_HEADER 5
+#define CODA_COMMAND_ENC_PARA_SET 6
+#define CODA_COMMAND_DEC_PARA_SET 7
+#define CODA_COMMAND_DEC_BUF_FLUSH 8
+#define CODA_COMMAND_RC_CHANGE_PARAMETER 9
+#define CODA_COMMAND_FIRMWARE_GET 0xf
+#define CODA_REG_BIT_RUN_INDEX 0x168
+#define CODA_INDEX_SET(x) ((x) & 0x3)
+#define CODA_REG_BIT_RUN_COD_STD 0x16c
+#define CODADX6_MODE_DECODE_MP4 0
+#define CODADX6_MODE_ENCODE_MP4 1
+#define CODADX6_MODE_DECODE_H264 2
+#define CODADX6_MODE_ENCODE_H264 3
+#define CODA7_MODE_DECODE_H264 0
+#define CODA7_MODE_DECODE_VC1 1
+#define CODA7_MODE_DECODE_MP2 2
+#define CODA7_MODE_DECODE_MP4 3
+#define CODA7_MODE_DECODE_DV3 3
+#define CODA7_MODE_DECODE_RV 4
+#define CODA7_MODE_DECODE_MJPG 5
+#define CODA7_MODE_ENCODE_H264 8
+#define CODA7_MODE_ENCODE_MP4 11
+#define CODA7_MODE_ENCODE_MJPG 13
+#define CODA_MODE_INVALID 0xffff
+#define CODA_REG_BIT_INT_ENABLE 0x170
+#define CODA_INT_INTERRUPT_ENABLE (1 << 3)
+
+/*
+ * Commands' mailbox:
+ * registers with offsets in the range 0x180-0x1d0
+ * have different meaning depending on the command being
+ * issued.
+ */
+
+/* Encoder Sequence Initialization */
+#define CODA_CMD_ENC_SEQ_BB_START 0x180
+#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
+#define CODA_CMD_ENC_SEQ_OPTION 0x188
+#define CODA_OPTION_GAMMA_OFFSET 7
+#define CODA_OPTION_GAMMA_MASK 0x01
+#define CODA_OPTION_LIMITQP_OFFSET 6
+#define CODA_OPTION_LIMITQP_MASK 0x01
+#define CODA_OPTION_RCINTRAQP_OFFSET 5
+#define CODA_OPTION_RCINTRAQP_MASK 0x01
+#define CODA_OPTION_FMO_OFFSET 4
+#define CODA_OPTION_FMO_MASK 0x01
+#define CODA_OPTION_SLICEREPORT_OFFSET 1
+#define CODA_OPTION_SLICEREPORT_MASK 0x01
+#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
+#define CODA_STD_MPEG4 0
+#define CODA_STD_H263 1
+#define CODA_STD_H264 2
+#define CODA_STD_MJPG 3
+#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190
+#define CODA7_PICWIDTH_OFFSET 16
+#define CODA7_PICWIDTH_MASK 0xffff
+#define CODADX6_PICWIDTH_OFFSET 10
+#define CODADX6_PICWIDTH_MASK 0x3ff
+#define CODA_PICHEIGHT_OFFSET 0
+#define CODA_PICHEIGHT_MASK 0x3ff
+#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194
+#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198
+#define CODA_MP4PARAM_VERID_OFFSET 6
+#define CODA_MP4PARAM_VERID_MASK 0x01
+#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2
+#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07
+#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1
+#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01
+#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0
+#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01
+#define CODA_CMD_ENC_SEQ_263_PARA 0x19c
+#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2
+#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01
+#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1
+#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01
+#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0
+#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01
+#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0
+#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12
+#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f
+#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8
+#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f
+#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6
+#define CODA_264PARAM_DISABLEDEBLK_MASK 0x01
+#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5
+#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01
+#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0
+#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f
+#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4
+#define CODA_SLICING_SIZE_OFFSET 2
+#define CODA_SLICING_SIZE_MASK 0x3fffffff
+#define CODA_SLICING_UNIT_OFFSET 1
+#define CODA_SLICING_UNIT_MASK 0x01
+#define CODA_SLICING_MODE_OFFSET 0
+#define CODA_SLICING_MODE_MASK 0x01
+#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8
+#define CODA_GOP_SIZE_OFFSET 0
+#define CODA_GOP_SIZE_MASK 0x3f
+#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac
+#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31
+#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01
+#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16
+#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7f
+#define CODA_RATECONTROL_BITRATE_OFFSET 1
+#define CODA_RATECONTROL_BITRATE_MASK 0x7f
+#define CODA_RATECONTROL_ENABLE_OFFSET 0
+#define CODA_RATECONTROL_ENABLE_MASK 0x01
+#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0
+#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4
+#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8
+#define CODA_FMOPARAM_TYPE_OFFSET 4
+#define CODA_FMOPARAM_TYPE_MASK 1
+#define CODA_FMOPARAM_SLICENUM_OFFSET 0
+#define CODA_FMOPARAM_SLICENUM_MASK 0x0f
+#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8
+#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc
+#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8
+#define CODA_QPMAX_OFFSET 0
+#define CODA_QPMAX_MASK 0x3f
+#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc
+#define CODA_GAMMA_OFFSET 0
+#define CODA_GAMMA_MASK 0xffff
+#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0
+
+/* Encoder Picture Run */
+#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180
+#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184
+#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188
+#define CODA_CMD_ENC_PIC_QS 0x18c
+#define CODA_CMD_ENC_PIC_ROT_MODE 0x190
+#define CODA_ROT_MIR_ENABLE (1 << 4)
+#define CODA_ROT_0 (0x0 << 0)
+#define CODA_ROT_90 (0x1 << 0)
+#define CODA_ROT_180 (0x2 << 0)
+#define CODA_ROT_270 (0x3 << 0)
+#define CODA_MIR_NONE (0x0 << 2)
+#define CODA_MIR_VER (0x1 << 2)
+#define CODA_MIR_HOR (0x2 << 2)
+#define CODA_MIR_VER_HOR (0x3 << 2)
+#define CODA_CMD_ENC_PIC_OPTION 0x194
+#define CODA_CMD_ENC_PIC_BB_START 0x198
+#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c
+#define CODA_RET_ENC_PIC_TYPE 0x1c4
+#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc
+#define CODA_RET_ENC_PIC_FLAG 0x1d0
+
+/* Set Frame Buffer */
+#define CODA_CMD_SET_FRAME_BUF_NUM 0x180
+#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184
+#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190
+#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194
+#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198
+#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c
+#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0
+#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8
+
+/* Encoder Header */
+#define CODA_CMD_ENC_HEADER_CODE 0x180
+#define CODA_GAMMA_OFFSET 0
+#define CODA_HEADER_H264_SPS 0
+#define CODA_HEADER_H264_PPS 1
+#define CODA_HEADER_MP4V_VOL 0
+#define CODA_HEADER_MP4V_VOS 1
+#define CODA_HEADER_MP4V_VIS 2
+#define CODA_CMD_ENC_HEADER_BB_START 0x184
+#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188
+
+/* Get Version */
+#define CODA_CMD_FIRMWARE_VERNUM 0x1c0
+#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff)
+#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f)
+#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f)
+#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff)
+#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \
+ ((product) << 16 | ((major) << 12) | \
+ ((minor) << 8) | (release))
+
+#endif
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig
new file mode 100644
index 000000000000..78e26d24f637
--- /dev/null
+++ b/drivers/media/platform/davinci/Kconfig
@@ -0,0 +1,121 @@
+config VIDEO_DAVINCI_VPIF_DISPLAY
+ tristate "DM646x/DA850/OMAPL138 EVM Video Display"
+ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_DAVINCI_VPIF
+ select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Enables Davinci VPIF module used for display devices.
+ This module is common for following DM6467/DA850/OMAPL138
+ based display devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif_display.
+
+config VIDEO_DAVINCI_VPIF_CAPTURE
+ tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
+ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_DAVINCI_VPIF
+ help
+ Enables Davinci VPIF module used for captur devices.
+ This module is common for following DM6467/DA850/OMAPL138
+ based capture devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif_capture.
+
+config VIDEO_DAVINCI_VPIF
+ tristate "DaVinci VPIF Driver"
+ depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
+ help
+ Support for DaVinci VPIF Driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpif.
+
+config VIDEO_VPSS_SYSTEM
+ tristate "VPSS System module driver"
+ depends on ARCH_DAVINCI
+ help
+ Support for vpss system module for video driver
+
+config VIDEO_VPFE_CAPTURE
+ tristate "VPFE Video Capture Driver"
+ depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
+ depends on I2C
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Support for DMx/AMx VPFE based frame grabber. This is the
+ common V4L2 module for following DMx/AMx SoCs from Texas
+ Instruments:- DM6446, DM365, DM355 & AM3517/05.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe-capture.
+
+config VIDEO_DM6446_CCDC
+ tristate "DM6446 CCDC HW module"
+ depends on VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
+ with decoder modules such as TVP5146 over BT656 or
+ sensor module such as MT9T001 over a raw interface. This
+ module configures the interface and CCDC/ISIF to do
+ video frame capture from slave decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_DM355_CCDC
+ tristate "DM355 CCDC HW module"
+ depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables DM355 CCD hw module. DM355 CCDC hw interfaces
+ with decoder modules such as TVP5146 over BT656 or
+ sensor module such as MT9T001 over a raw interface. This
+ module configures the interface and CCDC/ISIF to do
+ video frame capture from a slave decoders
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_ISIF
+ tristate "ISIF HW module"
+ depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE
+ select VIDEO_VPSS_SYSTEM
+ default y
+ help
+ Enables ISIF hw module. This is the hardware module for
+ configuring ISIF in VPFE to capture Raw Bayer RGB data from
+ a image sensor or YUV data from a YUV source.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpfe.
+
+config VIDEO_DM644X_VPBE
+ tristate "DM644X VPBE HW module"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_VPSS_SYSTEM
+ select VIDEOBUF_DMA_CONTIG
+ help
+ Enables VPBE modules used for display on a DM644x
+ SoC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe.
+
+
+config VIDEO_VPBE_DISPLAY
+ tristate "VPBE V4L2 Display driver"
+ depends on ARCH_DAVINCI_DM644x
+ select VIDEO_DM644X_VPBE
+ help
+ Enables VPBE V4L2 Display driver on a DM644x device
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpbe_display.
diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/platform/davinci/Makefile
index 74ed92d09257..74ed92d09257 100644
--- a/drivers/media/video/davinci/Makefile
+++ b/drivers/media/platform/davinci/Makefile
diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/platform/davinci/ccdc_hw_device.h
index 86b9b3518965..86b9b3518965 100644
--- a/drivers/media/video/davinci/ccdc_hw_device.h
+++ b/drivers/media/platform/davinci/ccdc_hw_device.h
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c
new file mode 100644
index 000000000000..ce0e4131c067
--- /dev/null
+++ b/drivers/media/platform/davinci/dm355_ccdc.c
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2005-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * CCDC hardware module for DM355
+ * ------------------------------
+ *
+ * This module is for configuring DM355 CCD controller of VPFE to capture
+ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
+ * such as Defect Pixel Correction, Color Space Conversion etc to
+ * pre-process the Bayer RGB data, before writing it to SDRAM. This
+ * module also allows application to configure individual
+ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
+ * To do so, application include dm355_ccdc.h and vpfe_capture.h header
+ * files. The setparams() API is called by vpfe_capture driver
+ * to configure module parameters
+ *
+ * TODO: 1) Raw bayer parameter settings and bayer capture
+ * 2) Split module parameter structure to module specific ioctl structs
+ * 3) add support for lense shading correction
+ * 4) investigate if enum used for user space type definition
+ * to be replaced by #defines or integer
+ */
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <media/davinci/dm355_ccdc.h>
+#include <media/davinci/vpss.h>
+
+#include "dm355_ccdc_regs.h"
+#include "ccdc_hw_device.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CCDC Driver for DM355");
+MODULE_AUTHOR("Texas Instruments");
+
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = 256,
+ .gb_g = 256,
+ .gr_cy = 256,
+ .b_mg = 256
+ },
+ .config_params = {
+ .datasft = 2,
+ .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
+ .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
+ .alaw = {
+ .gama_wd = 2,
+ },
+ .blk_clamp = {
+ .sample_pixel = 1,
+ .dc_sub = 25
+ },
+ .col_pat_field0 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
+ .col_pat_field1 = {
+ .olop = CCDC_GREEN_BLUE,
+ .olep = CCDC_BLUE,
+ .elop = CCDC_RED,
+ .elep = CCDC_GREEN_RED
+ },
+ },
+ },
+ /* YCbCr configuration */
+ .ycbcr = {
+ .win = CCDC_WIN_PAL,
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
+ },
+};
+
+
+/* Raw Bayer formats */
+static u32 ccdc_raw_bayer_pix_formats[] =
+ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static u32 ccdc_raw_yuv_pix_formats[] =
+ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(ccdc_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
+}
+
+static void ccdc_enable(int en)
+{
+ unsigned int temp;
+ temp = regr(SYNCEN);
+ temp &= (~CCDC_SYNCEN_VDHDEN_MASK);
+ temp |= (en & CCDC_SYNCEN_VDHDEN_MASK);
+ regw(temp, SYNCEN);
+}
+
+static void ccdc_enable_output_to_sdram(int en)
+{
+ unsigned int temp;
+ temp = regr(SYNCEN);
+ temp &= (~(CCDC_SYNCEN_WEN_MASK));
+ temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK);
+ regw(temp, SYNCEN);
+}
+
+static void ccdc_config_gain_offset(void)
+{
+ /* configure gain */
+ regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN);
+ regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN);
+ regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN);
+ regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN);
+ /* configure offset */
+ regw(ccdc_cfg.bayer.ccdc_offset, OFFSET);
+}
+
+/*
+ * ccdc_restore_defaults()
+ * This function restore power on defaults in the ccdc registers
+ */
+static int ccdc_restore_defaults(void)
+{
+ int i;
+
+ dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults...");
+ /* set all registers to zero */
+ for (i = 0; i <= CCDC_REG_LAST; i += 4)
+ regw(0, i);
+
+ /* now override the values with power on defaults in registers */
+ regw(MODESET_DEFAULT, MODESET);
+ /* no culling support */
+ regw(CULH_DEFAULT, CULH);
+ regw(CULV_DEFAULT, CULV);
+ /* Set default Gain and Offset */
+ ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT;
+ ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT;
+ ccdc_config_gain_offset();
+ regw(OUTCLIP_DEFAULT, OUTCLIP);
+ regw(LSCCFG2_DEFAULT, LSCCFG2);
+ /* select ccdc input */
+ if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source");
+ return -EFAULT;
+ }
+ /* select ccdc clock */
+ if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
+ dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock");
+ return -EFAULT;
+ }
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults...");
+ return 0;
+}
+
+static int ccdc_open(struct device *device)
+{
+ return ccdc_restore_defaults();
+}
+
+static int ccdc_close(struct device *device)
+{
+ /* disable clock */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 0);
+ /* do nothing for now */
+ return 0;
+}
+/*
+ * ccdc_setwin()
+ * This function will configure the window size to
+ * be capture in CCDC reg.
+ */
+static void ccdc_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt, int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int mid_img = 0;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
+
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ regw(horz_start, SPH);
+ regw(horz_nr_pixels, NPH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ /* configure VDINT0 and VDINT1 */
+ regw(vert_start, VDINT0);
+ } else {
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ regw(vert_start, VDINT0);
+ regw(mid_img, VDINT1);
+ }
+ regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
+ regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
+ regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
+}
+
+static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
+{
+ if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
+ ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
+ ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
+ ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
+ return -EINVAL;
+ }
+
+ if ((ccdcparam->med_filt_thres < 0) ||
+ (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of median filter threshold\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
+ ccdcparam->data_sz > CCDC_DATA_8BITS) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
+ return -EINVAL;
+ }
+
+ if (ccdcparam->alaw.enable) {
+ if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
+ ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
+ dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
+ return -EINVAL;
+ }
+ }
+
+ if (ccdcparam->blk_clamp.b_clamp_enable) {
+ if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
+ ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample pixel\n");
+ return -EINVAL;
+ }
+ if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
+ ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
+ dev_dbg(ccdc_cfg.dev,
+ "Invalid value of sample lines\n");
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/* Parameter operations */
+static int ccdc_set_params(void __user *params)
+{
+ struct ccdc_config_params_raw ccdc_raw_params;
+ int x;
+
+ /* only raw module parameters can be set through the IOCTL */
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
+ return -EINVAL;
+
+ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
+ if (x) {
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc"
+ "params, %d\n", x);
+ return -EFAULT;
+ }
+
+ if (!validate_ccdc_param(&ccdc_raw_params)) {
+ memcpy(&ccdc_cfg.bayer.config_params,
+ &ccdc_raw_params,
+ sizeof(ccdc_raw_params));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/* This function will configure CCDC for YCbCr video capture */
+static void ccdc_config_ycbcr(void)
+{
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
+ u32 temp;
+
+ /* first set the CCDC power on defaults values in all registers */
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
+ ccdc_restore_defaults();
+
+ /* configure pixel format & video frame format */
+ temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) <<
+ CCDC_INPUT_MODE_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) <<
+ CCDC_FRM_FMT_SHIFT));
+
+ /* setup BT.656 sync mode */
+ if (params->bt656_enable) {
+ regw(CCDC_REC656IF_BT656_EN, REC656IF);
+ /*
+ * configure the FID, VD, HD pin polarity fld,hd pol positive,
+ * vd negative, 8-bit pack mode
+ */
+ temp |= CCDC_VD_POL_NEGATIVE;
+ } else { /* y/c external sync mode */
+ temp |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
+ CCDC_FID_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) <<
+ CCDC_HD_POL_SHIFT) |
+ ((params->vd_pol & CCDC_VD_POL_MASK) <<
+ CCDC_VD_POL_SHIFT));
+ }
+
+ /* pack the data to 8-bit */
+ temp |= CCDC_DATA_PACK_ENABLE;
+
+ regw(temp, MODESET);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 2);
+
+ /* configure the order of y cb cr in SD-RAM */
+ temp = (params->pix_order << CCDC_Y8POS_SHIFT);
+ temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC;
+ regw(temp, CCDCFG);
+
+ /*
+ * configure the horizontal line offset. This is done by rounding up
+ * width to a multiple of 16 pixels and multiply by two to account for
+ * y:cb:cr 4:2:2 data
+ */
+ regw(((params->win.width * 2 + 31) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
+ /* two fields are interleaved in memory */
+ regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
+ }
+
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
+}
+
+/*
+ * ccdc_config_black_clamp()
+ * configure parameters for Optical Black Clamp
+ */
+static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
+{
+ u32 val;
+
+ if (!bclamp->b_clamp_enable) {
+ /* configure DCSub */
+ regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB);
+ regw(0x0000, CLAMP);
+ return;
+ }
+ /* Enable the Black clamping, set sample lines and pixels */
+ val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) |
+ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
+ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE;
+ regw(val, CLAMP);
+
+ /* If Black clamping is enable then make dcsub 0 */
+ val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK)
+ << CCDC_NUM_LINE_CALC_SHIFT;
+ regw(val, DCSUB);
+}
+
+/*
+ * ccdc_config_black_compense()
+ * configure parameters for Black Compensation
+ */
+static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
+{
+ u32 val;
+
+ val = (bcomp->b & CCDC_BLK_COMP_MASK) |
+ ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GB_COMP_SHIFT);
+ regw(val, BLKCMP1);
+
+ val = ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GR_COMP_SHIFT) |
+ ((bcomp->r & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_R_COMP_SHIFT);
+ regw(val, BLKCMP0);
+}
+
+/*
+ * ccdc_write_dfc_entry()
+ * write an entry in the dfc table.
+ */
+int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
+{
+/* TODO This is to be re-visited and adjusted */
+#define DFC_WRITE_WAIT_COUNT 1000
+ u32 val, count = DFC_WRITE_WAIT_COUNT;
+
+ regw(dfc->dft_corr_vert[index], DFCMEM0);
+ regw(dfc->dft_corr_horz[index], DFCMEM1);
+ regw(dfc->dft_corr_sub1[index], DFCMEM2);
+ regw(dfc->dft_corr_sub2[index], DFCMEM3);
+ regw(dfc->dft_corr_sub3[index], DFCMEM4);
+ /* set WR bit to write */
+ val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK;
+ regw(val, DFCMEMCTL);
+
+ /*
+ * Assume, it is very short. If we get an error, we need to
+ * adjust this value
+ */
+ while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK)
+ count--;
+ /*
+ * TODO We expect the count to be non-zero to be successful. Adjust
+ * the count if write requires more time
+ */
+
+ if (count) {
+ dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * ccdc_config_vdfc()
+ * configure parameters for Vertical Defect Correction
+ */
+static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
+{
+ u32 val;
+ int i;
+
+ /* Configure General Defect Correction. The table used is from IPIPE */
+ val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK;
+
+ /* Configure Vertical Defect Correction if needed */
+ if (!dfc->ver_dft_en) {
+ /* Enable only General Defect Correction */
+ regw(val, DFCCTL);
+ return 0;
+ }
+
+ if (dfc->table_size > CCDC_DFT_TABLE_SIZE)
+ return -EINVAL;
+
+ val |= CCDC_DFCCTL_VDFC_DISABLE;
+ val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) <<
+ CCDC_DFCCTL_VDFCSL_SHIFT;
+ val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) <<
+ CCDC_DFCCTL_VDFCUDA_SHIFT;
+ val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) <<
+ CCDC_DFCCTL_VDFLSFT_SHIFT;
+ regw(val , DFCCTL);
+
+ /* clear address ptr to offset 0 */
+ val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT;
+
+ /* write defect table entries */
+ for (i = 0; i < dfc->table_size; i++) {
+ /* increment address for non zero index */
+ if (i != 0)
+ val = CCDC_DFCMEMCTL_INC_ADDR;
+ regw(val, DFCMEMCTL);
+ if (ccdc_write_dfc_entry(i, dfc) < 0)
+ return -EFAULT;
+ }
+
+ /* update saturation level and enable dfc */
+ regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT);
+ val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK <<
+ CCDC_DFCCTL_VDFCEN_SHIFT);
+ regw(val, DFCCTL);
+ return 0;
+}
+
+/*
+ * ccdc_config_csc()
+ * configure parameters for color space conversion
+ * Each register CSCM0-7 has two values in S8Q5 format.
+ */
+static void ccdc_config_csc(struct ccdc_csc *csc)
+{
+ u32 val1, val2;
+ int i;
+
+ if (!csc->enable)
+ return;
+
+ /* Enable the CSC sub-module */
+ regw(CCDC_CSC_ENABLE, CSCCTL);
+
+ /* Converting the co-eff as per the format of the register */
+ for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = (csc->coeff[i].integer &
+ CCDC_CSC_COEF_INTEG_MASK)
+ << CCDC_CSC_COEF_INTEG_SHIFT;
+ /*
+ * convert decimal part to binary. Use 2 decimal
+ * precision, user values range from .00 - 0.99
+ */
+ val1 |= (((csc->coeff[i].decimal &
+ CCDC_CSC_COEF_DECIMAL_MASK) *
+ CCDC_CSC_DEC_MAX) / 100);
+ } else {
+
+ /* CSCM - MSB */
+ val2 = (csc->coeff[i].integer &
+ CCDC_CSC_COEF_INTEG_MASK)
+ << CCDC_CSC_COEF_INTEG_SHIFT;
+ val2 |= (((csc->coeff[i].decimal &
+ CCDC_CSC_COEF_DECIMAL_MASK) *
+ CCDC_CSC_DEC_MAX) / 100);
+ val2 <<= CCDC_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ regw(val2, (CSCM0 + ((i - 1) << 1)));
+ }
+ }
+}
+
+/*
+ * ccdc_config_color_patterns()
+ * configure parameters for color patterns
+ */
+static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
+ struct ccdc_col_pat *pat1)
+{
+ u32 val;
+
+ val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) |
+ (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) |
+ (pat1->elop << 12) | (pat1->elep << 14));
+ regw(val, COLPTN);
+}
+
+/* This function will configure CCDC for Raw mode image capture */
+static int ccdc_config_raw(void)
+{
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int val;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
+
+ /* restore power on defaults to register */
+ ccdc_restore_defaults();
+
+ /* CCDCFG register:
+ * set CCD Not to swap input since input is RAW data
+ * set FID detection function to Latch at V-Sync
+ * set WENLOG - ccdc valid area to AND
+ * set TRGSEL to WENBIT
+ * set EXTRG to DISABLE
+ * disable latching function on VSYNC - shadowed registers
+ */
+ regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC |
+ CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN |
+ CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG);
+
+ /*
+ * Set VDHD direction to input, input type to raw input
+ * normal data polarity, do not use external WEN
+ */
+ val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL |
+ CCDC_EXWEN_DISABLE);
+
+ /*
+ * Configure the vertical sync polarity (MODESET.VDPOL), horizontal
+ * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL),
+ * frame format(progressive or interlace), & pixel format (Input mode)
+ */
+ val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
+ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
+ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT));
+
+ /* set pack for alaw compression */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ val |= CCDC_DATA_PACK_ENABLE;
+
+ /* Configure for LPF */
+ if (config_params->lpf_enable)
+ val |= (config_params->lpf_enable & CCDC_LPF_MASK) <<
+ CCDC_LPF_SHIFT;
+
+ /* Configure the data shift */
+ val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
+ CCDC_DATASFT_SHIFT;
+ regw(val , MODESET);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val);
+
+ /* Configure the Median Filter threshold */
+ regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
+
+ /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */
+ val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT |
+ CCDC_CFA_MOSAIC;
+
+ /* Enable and configure aLaw register if needed */
+ if (config_params->alaw.enable) {
+ val |= (CCDC_ALAW_ENABLE |
+ ((config_params->alaw.gama_wd &
+ CCDC_ALAW_GAMA_WD_MASK) <<
+ CCDC_GAMMAWD_INPUT_SHIFT));
+ }
+
+ /* Configure Median filter1 & filter2 */
+ val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) |
+ (config_params->mfilt2 << CCDC_MFILT2_SHIFT));
+
+ regw(val, GAMMAWD);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 1);
+
+ /* Optical Clamp Averaging */
+ ccdc_config_black_clamp(&config_params->blk_clamp);
+
+ /* Black level compensation */
+ ccdc_config_black_compense(&config_params->blk_comp);
+
+ /* Vertical Defect Correction if needed */
+ if (ccdc_config_vdfc(&config_params->vertical_dft) < 0)
+ return -EFAULT;
+
+ /* color space conversion */
+ ccdc_config_csc(&config_params->csc);
+
+ /* color pattern */
+ ccdc_config_color_patterns(&config_params->col_pat_field0,
+ &config_params->col_pat_field1);
+
+ /* Configure the Gain & offset control */
+ ccdc_config_gain_offset();
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val);
+
+ /* Configure DATAOFST register */
+ val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
+ CCDC_DATAOFST_H_SHIFT;
+ val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) <<
+ CCDC_DATAOFST_V_SHIFT;
+ regw(val, DATAOFST);
+
+ /* configuring HSIZE register */
+ val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) <<
+ CCDC_HSIZE_FLIP_SHIFT;
+
+ /* If pack 8 is enable then 1 pixel will take 1 byte */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable) {
+ val |= (((params->win.width) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK;
+
+ /* adjust to multiple of 32 */
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
+ (((params->win.width) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK);
+ } else {
+ /* else one pixel will take 2 byte */
+ val |= (((params->win.width * 2) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK;
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
+ (((params->win.width * 2) + 31) >> 5) &
+ CCDC_HSIZE_VAL_MASK);
+ }
+ regw(val, HSIZE);
+
+ /* Configure SDOFST register */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_enable) {
+ /* For interlace inverse mode */
+ regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_INTERLACE_INVERSE);
+ } else {
+ /* For interlace non inverse mode */
+ regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_INTERLACE_NORMAL);
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_enable) {
+ /* For progessive inverse mode */
+ regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_PROGRESSIVE_INVERSE);
+ } else {
+ /* For progessive non inverse mode */
+ regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
+ CCDC_SDOFST_PROGRESSIVE_NORMAL);
+ }
+ }
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
+ return 0;
+}
+
+static int ccdc_configure(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_config_raw();
+ else
+ ccdc_config_ycbcr();
+ return 0;
+}
+
+static int ccdc_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
+ else
+ ccdc_cfg.ycbcr.buf_type = buf_type;
+ return 0;
+}
+static enum ccdc_buftype ccdc_get_buftype(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
+}
+
+static int ccdc_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
+ *pix = ccdc_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
+ *pix = ccdc_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int ccdc_set_pixel_format(u32 pixfmt)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8)
+ alaw->enable = 1;
+ else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
+ return -EINVAL;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+static u32 ccdc_get_pixel_format(void)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+ u32 pixfmt;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ if (alaw->enable)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+static int ccdc_set_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
+ else
+ ccdc_cfg.ycbcr.win = *win;
+ return 0;
+}
+
+static void ccdc_get_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
+ else
+ *win = ccdc_cfg.ycbcr.win;
+}
+
+static unsigned int ccdc_get_line_length(void)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int len;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if ((config_params->alaw.enable) ||
+ (config_params->data_sz == CCDC_DATA_8BITS))
+ len = ccdc_cfg.bayer.win.width;
+ else
+ len = ccdc_cfg.bayer.win.width * 2;
+ } else
+ len = ccdc_cfg.ycbcr.win.width * 2;
+ return ALIGN(len, 32);
+}
+
+static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+
+static enum ccdc_frmfmt ccdc_get_frame_format(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
+ else
+ return ccdc_cfg.ycbcr.frm_fmt;
+}
+
+static int ccdc_getfid(void)
+{
+ return (regr(MODESET) >> 15) & 1;
+}
+
+/* misc operations */
+static inline void ccdc_setfbaddr(unsigned long addr)
+{
+ regw((addr >> 21) & 0x007f, STADRH);
+ regw((addr >> 5) & 0x0ffff, STADRL);
+}
+
+static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ ccdc_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_YCBCR_SYNC_16:
+ case VPFE_YCBCR_SYNC_8:
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
+ break;
+ default:
+ /* TODO add support for raw bayer here */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static struct ccdc_hw_device ccdc_hw_dev = {
+ .name = "DM355 CCDC",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = ccdc_open,
+ .close = ccdc_close,
+ .enable = ccdc_enable,
+ .enable_out_to_sdram = ccdc_enable_output_to_sdram,
+ .set_hw_if_params = ccdc_set_hw_if_params,
+ .set_params = ccdc_set_params,
+ .configure = ccdc_configure,
+ .set_buftype = ccdc_set_buftype,
+ .get_buftype = ccdc_get_buftype,
+ .enum_pix = ccdc_enum_pix,
+ .set_pixel_format = ccdc_set_pixel_format,
+ .get_pixel_format = ccdc_get_pixel_format,
+ .set_frame_format = ccdc_set_frame_format,
+ .get_frame_format = ccdc_get_frame_format,
+ .set_image_window = ccdc_set_image_window,
+ .get_image_window = ccdc_get_image_window,
+ .get_line_length = ccdc_get_line_length,
+ .setfbaddr = ccdc_setfbaddr,
+ .getfid = ccdc_getfid,
+ },
+};
+
+static int __devinit dm355_ccdc_probe(struct platform_device *pdev)
+{
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
+ return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
+}
+
+static int dm355_ccdc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static struct platform_driver dm355_ccdc_driver = {
+ .driver = {
+ .name = "dm355_ccdc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(dm355_ccdc_remove),
+ .probe = dm355_ccdc_probe,
+};
+
+module_platform_driver(dm355_ccdc_driver);
diff --git a/drivers/media/video/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h
index d6d2ef0533b5..d6d2ef0533b5 100644
--- a/drivers/media/video/davinci/dm355_ccdc_regs.h
+++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c
new file mode 100644
index 000000000000..ee7942b1996e
--- /dev/null
+++ b/drivers/media/platform/davinci/dm644x_ccdc.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (C) 2006-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * CCDC hardware module for DM6446
+ * ------------------------------
+ *
+ * This module is for configuring CCD controller of DM6446 VPFE to capture
+ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
+ * such as Defect Pixel Correction, Color Space Conversion etc to
+ * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This
+ * module also allows application to configure individual
+ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
+ * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header
+ * files. The setparams() API is called by vpfe_capture driver
+ * to configure module parameters. This file is named DM644x so that other
+ * variants such DM6443 may be supported using the same module.
+ *
+ * TODO: Test Raw bayer parameter settings and bayer capture
+ * Split module parameter structure to module specific ioctl structs
+ * investigate if enum used for user space type definition
+ * to be replaced by #defines or integer
+ */
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+#include <linux/gfp.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <media/davinci/dm644x_ccdc.h>
+#include <media/davinci/vpss.h>
+
+#include "dm644x_ccdc_regs.h"
+#include "ccdc_hw_device.h"
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("CCDC Driver for DM6446");
+MODULE_AUTHOR("Texas Instruments");
+
+static struct ccdc_oper_config {
+ struct device *dev;
+ /* CCDC interface type */
+ enum vpfe_hw_if_type if_type;
+ /* Raw Bayer configuration */
+ struct ccdc_params_raw bayer;
+ /* YCbCr configuration */
+ struct ccdc_params_ycbcr ycbcr;
+ /* Master clock */
+ struct clk *mclk;
+ /* slave clock */
+ struct clk *sclk;
+ /* ccdc base address */
+ void __iomem *base_addr;
+} ccdc_cfg = {
+ /* Raw configurations */
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = CCDC_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .config_params = {
+ .data_sz = CCDC_DATA_10BITS,
+ },
+ },
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = CCDC_WIN_PAL,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .bt656_enable = 1,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
+ },
+};
+
+#define CCDC_MAX_RAW_YUV_FORMATS 2
+
+/* Raw Bayer formats */
+static u32 ccdc_raw_bayer_pix_formats[] =
+ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static u32 ccdc_raw_yuv_pix_formats[] =
+ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* CCDC Save/Restore context */
+static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)];
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(ccdc_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, ccdc_cfg.base_addr + offset);
+}
+
+static void ccdc_enable(int flag)
+{
+ regw(flag, CCDC_PCR);
+}
+
+static void ccdc_enable_vport(int flag)
+{
+ if (flag)
+ /* enable video port */
+ regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG);
+ else
+ regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG);
+}
+
+/*
+ * ccdc_setwin()
+ * This function will configure the window size
+ * to be capture in CCDC reg
+ */
+void ccdc_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt,
+ int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int val = 0, mid_img = 0;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
+ regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels,
+ CCDC_HORZ_INFO);
+
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ /* configure VDINT0 */
+ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT);
+ regw(val, CCDC_VDINT);
+
+ } else {
+ /* Since first line doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /*
+ * configure VDINT0 and VDINT1. VDINT1 will be at half
+ * of image height
+ */
+ mid_img = vert_start + (image_win->height / 2);
+ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) |
+ (mid_img & CCDC_VDINT_VDINT1_MASK);
+ regw(val, CCDC_VDINT);
+
+ }
+ regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
+ CCDC_VERT_START);
+ regw(vert_nr_lines, CCDC_VERT_LINES);
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
+}
+
+static void ccdc_readregs(void)
+{
+ unsigned int val = 0;
+
+ val = regr(CCDC_ALAW);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val);
+ val = regr(CCDC_CLAMP);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val);
+ val = regr(CCDC_DCSUB);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val);
+ val = regr(CCDC_BLKCMP);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val);
+ val = regr(CCDC_FPC_ADDR);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val);
+ val = regr(CCDC_FPC);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val);
+ val = regr(CCDC_FMTCFG);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val);
+ val = regr(CCDC_COLPTN);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val);
+ val = regr(CCDC_FMT_HORZ);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val);
+ val = regr(CCDC_FMT_VERT);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val);
+ val = regr(CCDC_HSIZE_OFF);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
+ val = regr(CCDC_SDOFST);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val);
+ val = regr(CCDC_VP_OUT);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val);
+ val = regr(CCDC_SYN_MODE);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val);
+ val = regr(CCDC_HORZ_INFO);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val);
+ val = regr(CCDC_VERT_START);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val);
+ val = regr(CCDC_VERT_LINES);
+ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
+}
+
+static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
+{
+ if (ccdcparam->alaw.enable) {
+ if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
+ (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
+ (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
+ dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int *fpc_virtaddr = NULL;
+ unsigned int *fpc_physaddr = NULL;
+
+ memcpy(config_params, raw_params, sizeof(*raw_params));
+ /*
+ * allocate memory for fault pixel table and copy the user
+ * values to the table
+ */
+ if (!config_params->fault_pxl.enable)
+ return 0;
+
+ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
+ fpc_virtaddr = (unsigned int *)phys_to_virt(
+ (unsigned long)fpc_physaddr);
+ /*
+ * Allocate memory for FPC table if current
+ * FPC table buffer is not big enough to
+ * accommodate FPC Number requested
+ */
+ if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
+ if (fpc_physaddr != NULL) {
+ free_pages((unsigned long)fpc_physaddr,
+ get_order
+ (config_params->fault_pxl.fp_num *
+ FP_NUM_BYTES));
+ }
+
+ /* Allocate memory for FPC table */
+ fpc_virtaddr =
+ (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA,
+ get_order(raw_params->
+ fault_pxl.fp_num *
+ FP_NUM_BYTES));
+
+ if (fpc_virtaddr == NULL) {
+ dev_dbg(ccdc_cfg.dev,
+ "\nUnable to allocate memory for FPC");
+ return -EFAULT;
+ }
+ fpc_physaddr =
+ (unsigned int *)virt_to_phys((void *)fpc_virtaddr);
+ }
+
+ /* Copy number of fault pixels and FPC table */
+ config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num;
+ if (copy_from_user(fpc_virtaddr,
+ (void __user *)raw_params->fault_pxl.fpc_table_addr,
+ config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
+ dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
+ return -EFAULT;
+ }
+ config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
+ return 0;
+}
+
+static int ccdc_close(struct device *dev)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
+
+ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
+
+ if (fpc_physaddr != NULL) {
+ fpc_virtaddr = (unsigned int *)
+ phys_to_virt((unsigned long)fpc_physaddr);
+ free_pages((unsigned long)fpc_virtaddr,
+ get_order(config_params->fault_pxl.fp_num *
+ FP_NUM_BYTES));
+ }
+ return 0;
+}
+
+/*
+ * ccdc_restore_defaults()
+ * This function will write defaults to all CCDC registers
+ */
+static void ccdc_restore_defaults(void)
+{
+ int i;
+
+ /* disable CCDC */
+ ccdc_enable(0);
+ /* set all registers to default value */
+ for (i = 4; i <= 0x94; i += 4)
+ regw(0, i);
+ regw(CCDC_NO_CULLING, CCDC_CULLING);
+ regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW);
+}
+
+static int ccdc_open(struct device *device)
+{
+ ccdc_restore_defaults();
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_enable_vport(1);
+ return 0;
+}
+
+static void ccdc_sbl_reset(void)
+{
+ vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
+}
+
+/* Parameter operations */
+static int ccdc_set_params(void __user *params)
+{
+ struct ccdc_config_params_raw ccdc_raw_params;
+ int x;
+
+ if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
+ return -EINVAL;
+
+ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
+ if (x) {
+ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying"
+ "ccdc params, %d\n", x);
+ return -EFAULT;
+ }
+
+ if (!validate_ccdc_param(&ccdc_raw_params)) {
+ if (!ccdc_update_raw_params(&ccdc_raw_params))
+ return 0;
+ }
+ return -EINVAL;
+}
+
+/*
+ * ccdc_config_ycbcr()
+ * This function will configure CCDC for YCbCr video capture
+ */
+void ccdc_config_ycbcr(void)
+{
+ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
+ u32 syn_mode;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
+ /*
+ * first restore the CCDC registers to default values
+ * This is important since we assume default values to be set in
+ * a lot of registers that we didn't touch
+ */
+ ccdc_restore_defaults();
+
+ /*
+ * configure pixel format, frame format, configure video frame
+ * format, enable output to SDRAM, enable internal timing generator
+ * and 8bit pack mode
+ */
+ syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) <<
+ CCDC_SYN_MODE_INPMOD_SHIFT) |
+ ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) <<
+ CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE |
+ CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE);
+
+ /* setup BT.656 sync mode */
+ if (params->bt656_enable) {
+ regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF);
+
+ /*
+ * configure the FID, VD, HD pin polarity,
+ * fld,hd pol positive, vd negative, 8-bit data
+ */
+ syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE;
+ if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
+ syn_mode |= CCDC_SYN_MODE_10BITS;
+ else
+ syn_mode |= CCDC_SYN_MODE_8BITS;
+ } else {
+ /* y/c external sync mode */
+ syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
+ CCDC_FID_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) <<
+ CCDC_HD_POL_SHIFT) |
+ ((params->vd_pol & CCDC_VD_POL_MASK) <<
+ CCDC_VD_POL_SHIFT));
+ }
+ regw(syn_mode, CCDC_SYN_MODE);
+
+ /* configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, 2);
+
+ /*
+ * configure the order of y cb cr in SDRAM, and disable latch
+ * internal register on vsync
+ */
+ if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
+ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
+ CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT,
+ CCDC_CCDCFG);
+ else
+ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
+ CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
+
+ /*
+ * configure the horizontal line offset. This should be a
+ * on 32 byte boundary. So clear LSB 5 bits
+ */
+ regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF);
+
+ /* configure the memory line offset */
+ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
+ /* two fields are interleaved in memory */
+ regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
+
+ ccdc_sbl_reset();
+ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
+}
+
+static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
+{
+ u32 val;
+
+ if (!bclamp->enable) {
+ /* configure DCSub */
+ val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
+ regw(val, CCDC_DCSUB);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val);
+ regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n");
+ return;
+ }
+ /*
+ * Configure gain, Start pixel, No of line to be avg,
+ * No of pixel/line to be avg, & Enable the Black clamping
+ */
+ val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) |
+ ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) <<
+ CCDC_BLK_ST_PXL_SHIFT) |
+ ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) <<
+ CCDC_BLK_SAMPLE_LINE_SHIFT) |
+ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
+ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
+ regw(val, CCDC_CLAMP);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val);
+ /* If Black clamping is enable then make dcsub 0 */
+ regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n");
+}
+
+static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
+{
+ u32 val;
+
+ val = ((bcomp->b & CCDC_BLK_COMP_MASK) |
+ ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GB_COMP_SHIFT) |
+ ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_GR_COMP_SHIFT) |
+ ((bcomp->r & CCDC_BLK_COMP_MASK) <<
+ CCDC_BLK_COMP_R_COMP_SHIFT));
+ regw(val, CCDC_BLKCMP);
+}
+
+static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
+{
+ u32 val;
+
+ /* Initially disable FPC */
+ val = CCDC_FPC_DISABLE;
+ regw(val, CCDC_FPC);
+
+ if (!fpc->enable)
+ return;
+
+ /* Configure Fault pixel if needed */
+ regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
+ (fpc->fpc_table_addr));
+ /* Write the FPC params with FPC disable */
+ val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
+ regw(val, CCDC_FPC);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
+ /* read the FPC register */
+ val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
+ regw(val, CCDC_FPC);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
+}
+
+/*
+ * ccdc_config_raw()
+ * This function will configure CCDC for Raw capture mode
+ */
+void ccdc_config_raw(void)
+{
+ struct ccdc_params_raw *params = &ccdc_cfg.bayer;
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int syn_mode = 0;
+ unsigned int val;
+
+ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
+
+ /* Reset CCDC */
+ ccdc_restore_defaults();
+
+ /* Disable latching function registers on VSYNC */
+ regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
+
+ /*
+ * Configure the vertical sync polarity(SYN_MODE.VDPOL),
+ * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
+ * (SYN_MODE.FLDPOL), frame format(progressive or interlace),
+ * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
+ * SDRAM, enable internal timing generator
+ */
+ syn_mode =
+ (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
+ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
+ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
+ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
+ ((config_params->data_sz & CCDC_DATA_SZ_MASK) <<
+ CCDC_DATA_SZ_SHIFT) |
+ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) |
+ CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE);
+
+ /* Enable and configure aLaw register if needed */
+ if (config_params->alaw.enable) {
+ val = ((config_params->alaw.gama_wd &
+ CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
+ regw(val, CCDC_ALAW);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
+ }
+
+ /* Configure video window */
+ ccdc_setwin(&params->win, params->frm_fmt, CCDC_PPC_RAW);
+
+ /* Configure Black Clamp */
+ ccdc_config_black_clamp(&config_params->blk_clamp);
+
+ /* Configure Black level compensation */
+ ccdc_config_black_compense(&config_params->blk_comp);
+
+ /* Configure Fault Pixel Correction */
+ ccdc_config_fpc(&config_params->fault_pxl);
+
+ /* If data size is 8 bit then pack the data */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ syn_mode |= CCDC_DATA_PACK_ENABLE;
+
+#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE
+ /* enable video port */
+ val = CCDC_ENABLE_VIDEO_PORT;
+#else
+ /* disable video port */
+ val = CCDC_DISABLE_VIDEO_PORT;
+#endif
+
+ if (config_params->data_sz == CCDC_DATA_8BITS)
+ val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK)
+ << CCDC_FMTCFG_VPIN_SHIFT;
+ else
+ val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK)
+ << CCDC_FMTCFG_VPIN_SHIFT;
+ /* Write value in FMTCFG */
+ regw(val, CCDC_FMTCFG);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val);
+ /* Configure the color pattern according to mt9t001 sensor */
+ regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
+ /*
+ * Configure Data formatter(Video port) pixel selection
+ * (FMT_HORZ, FMT_VERT)
+ */
+ val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) <<
+ CCDC_FMT_HORZ_FMTSPH_SHIFT) |
+ (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
+ regw(val, CCDC_FMT_HORZ);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
+ val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
+ << CCDC_FMT_VERT_FMTSLV_SHIFT;
+ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK;
+ else
+ val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
+
+ dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n",
+ params->win.height);
+ regw(val, CCDC_FMT_VERT);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val);
+
+ dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)...");
+
+ /*
+ * Configure Horizontal offset register. If pack 8 is enabled then
+ * 1 pixel will take 1 byte
+ */
+ if ((config_params->data_sz == CCDC_DATA_8BITS) ||
+ config_params->alaw.enable)
+ regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) &
+ CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF);
+ else
+ /* else one pixel will take 2 byte */
+ regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) +
+ CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK,
+ CCDC_HSIZE_OFF);
+
+ /* Set value for SDOFST */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_enable) {
+ /* For intelace inverse mode */
+ regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n");
+ }
+
+ else {
+ /* For intelace non inverse mode */
+ regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n");
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n");
+ }
+
+ /*
+ * Configure video port pixel selection (VPOUT)
+ * Here -1 is to make the height value less than FMT_VERT.FMTLNV
+ */
+ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK))
+ << CCDC_VP_OUT_VERT_NUM_SHIFT;
+ else
+ val =
+ ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) -
+ 1) & CCDC_VP_OUT_VERT_NUM_MASK)) <<
+ CCDC_VP_OUT_VERT_NUM_SHIFT;
+
+ val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK)
+ << CCDC_VP_OUT_HORZ_NUM_SHIFT;
+ val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
+ regw(val, CCDC_VP_OUT);
+
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val);
+ regw(syn_mode, CCDC_SYN_MODE);
+ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
+
+ ccdc_sbl_reset();
+ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
+ ccdc_readregs();
+}
+
+static int ccdc_configure(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_config_raw();
+ else
+ ccdc_config_ycbcr();
+ return 0;
+}
+
+static int ccdc_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.buf_type = buf_type;
+ else
+ ccdc_cfg.ycbcr.buf_type = buf_type;
+ return 0;
+}
+
+static enum ccdc_buftype ccdc_get_buftype(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.buf_type;
+ return ccdc_cfg.ycbcr.buf_type;
+}
+
+static int ccdc_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
+ *pix = ccdc_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
+ *pix = ccdc_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int ccdc_set_pixel_format(u32 pixfmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8)
+ ccdc_cfg.bayer.config_params.alaw.enable = 1;
+ else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
+ return -EINVAL;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static u32 ccdc_get_pixel_format(void)
+{
+ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
+ u32 pixfmt;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ if (alaw->enable)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+
+static int ccdc_set_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.win = *win;
+ else
+ ccdc_cfg.ycbcr.win = *win;
+ return 0;
+}
+
+static void ccdc_get_image_window(struct v4l2_rect *win)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ *win = ccdc_cfg.bayer.win;
+ else
+ *win = ccdc_cfg.ycbcr.win;
+}
+
+static unsigned int ccdc_get_line_length(void)
+{
+ struct ccdc_config_params_raw *config_params =
+ &ccdc_cfg.bayer.config_params;
+ unsigned int len;
+
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
+ if ((config_params->alaw.enable) ||
+ (config_params->data_sz == CCDC_DATA_8BITS))
+ len = ccdc_cfg.bayer.win.width;
+ else
+ len = ccdc_cfg.bayer.win.width * 2;
+ } else
+ len = ccdc_cfg.ycbcr.win.width * 2;
+ return ALIGN(len, 32);
+}
+
+static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ ccdc_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+
+static enum ccdc_frmfmt ccdc_get_frame_format(void)
+{
+ if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
+ return ccdc_cfg.bayer.frm_fmt;
+ else
+ return ccdc_cfg.ycbcr.frm_fmt;
+}
+
+static int ccdc_getfid(void)
+{
+ return (regr(CCDC_SYN_MODE) >> 15) & 1;
+}
+
+/* misc operations */
+static inline void ccdc_setfbaddr(unsigned long addr)
+{
+ regw(addr & 0xffffffe0, CCDC_SDR_ADDR);
+}
+
+static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ ccdc_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_YCBCR_SYNC_16:
+ case VPFE_YCBCR_SYNC_8:
+ case VPFE_BT656_10BIT:
+ ccdc_cfg.ycbcr.vd_pol = params->vdpol;
+ ccdc_cfg.ycbcr.hd_pol = params->hdpol;
+ break;
+ default:
+ /* TODO add support for raw bayer here */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void ccdc_save_context(void)
+{
+ ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR);
+ ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE);
+ ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID);
+ ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES);
+ ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO);
+ ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START);
+ ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES);
+ ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING);
+ ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF);
+ ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST);
+ ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR);
+ ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP);
+ ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB);
+ ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN);
+ ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP);
+ ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC);
+ ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR);
+ ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT);
+ ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW);
+ ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF);
+ ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG);
+ ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG);
+ ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ);
+ ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT);
+ ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0);
+ ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1);
+ ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2);
+ ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3);
+ ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4);
+ ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5);
+ ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6);
+ ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7);
+ ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0);
+ ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1);
+ ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0);
+ ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1);
+ ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT);
+}
+
+static void ccdc_restore_context(void)
+{
+ regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE);
+ regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID);
+ regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES);
+ regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO);
+ regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START);
+ regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES);
+ regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING);
+ regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF);
+ regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST);
+ regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR);
+ regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP);
+ regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB);
+ regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN);
+ regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP);
+ regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC);
+ regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR);
+ regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT);
+ regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW);
+ regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF);
+ regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG);
+ regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG);
+ regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ);
+ regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT);
+ regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0);
+ regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1);
+ regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2);
+ regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3);
+ regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4);
+ regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5);
+ regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6);
+ regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7);
+ regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0);
+ regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1);
+ regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0);
+ regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1);
+ regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT);
+ regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR);
+}
+static struct ccdc_hw_device ccdc_hw_dev = {
+ .name = "DM6446 CCDC",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = ccdc_open,
+ .close = ccdc_close,
+ .reset = ccdc_sbl_reset,
+ .enable = ccdc_enable,
+ .set_hw_if_params = ccdc_set_hw_if_params,
+ .set_params = ccdc_set_params,
+ .configure = ccdc_configure,
+ .set_buftype = ccdc_set_buftype,
+ .get_buftype = ccdc_get_buftype,
+ .enum_pix = ccdc_enum_pix,
+ .set_pixel_format = ccdc_set_pixel_format,
+ .get_pixel_format = ccdc_get_pixel_format,
+ .set_frame_format = ccdc_set_frame_format,
+ .get_frame_format = ccdc_get_frame_format,
+ .set_image_window = ccdc_set_image_window,
+ .get_image_window = ccdc_get_image_window,
+ .get_line_length = ccdc_get_line_length,
+ .setfbaddr = ccdc_setfbaddr,
+ .getfid = ccdc_getfid,
+ },
+};
+
+static int __devinit dm644x_ccdc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int status = 0;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&ccdc_hw_dev);
+ if (status < 0)
+ return status;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nores;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nores;
+ }
+
+ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
+ if (!ccdc_cfg.base_addr) {
+ status = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ /* Get and enable Master clock */
+ ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(ccdc_cfg.mclk)) {
+ status = PTR_ERR(ccdc_cfg.mclk);
+ goto fail_nomap;
+ }
+ if (clk_enable(ccdc_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Get and enable Slave clock */
+ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
+ if (IS_ERR(ccdc_cfg.sclk)) {
+ status = PTR_ERR(ccdc_cfg.sclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(ccdc_cfg.sclk)) {
+ status = -ENODEV;
+ goto fail_sclk;
+ }
+ ccdc_cfg.dev = &pdev->dev;
+ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
+ return 0;
+fail_sclk:
+ clk_put(ccdc_cfg.sclk);
+fail_mclk:
+ clk_put(ccdc_cfg.mclk);
+fail_nomap:
+ iounmap(ccdc_cfg.base_addr);
+fail_nomem:
+ release_mem_region(res->start, resource_size(res));
+fail_nores:
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return status;
+}
+
+static int dm644x_ccdc_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ clk_put(ccdc_cfg.mclk);
+ clk_put(ccdc_cfg.sclk);
+ iounmap(ccdc_cfg.base_addr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ vpfe_unregister_ccdc_device(&ccdc_hw_dev);
+ return 0;
+}
+
+static int dm644x_ccdc_suspend(struct device *dev)
+{
+ /* Save CCDC context */
+ ccdc_save_context();
+ /* Disable CCDC */
+ ccdc_enable(0);
+ /* Disable both master and slave clock */
+ clk_disable(ccdc_cfg.mclk);
+ clk_disable(ccdc_cfg.sclk);
+
+ return 0;
+}
+
+static int dm644x_ccdc_resume(struct device *dev)
+{
+ /* Enable both master and slave clock */
+ clk_enable(ccdc_cfg.mclk);
+ clk_enable(ccdc_cfg.sclk);
+ /* Restore CCDC context */
+ ccdc_restore_context();
+
+ return 0;
+}
+
+static const struct dev_pm_ops dm644x_ccdc_pm_ops = {
+ .suspend = dm644x_ccdc_suspend,
+ .resume = dm644x_ccdc_resume,
+};
+
+static struct platform_driver dm644x_ccdc_driver = {
+ .driver = {
+ .name = "dm644x_ccdc",
+ .owner = THIS_MODULE,
+ .pm = &dm644x_ccdc_pm_ops,
+ },
+ .remove = __devexit_p(dm644x_ccdc_remove),
+ .probe = dm644x_ccdc_probe,
+};
+
+module_platform_driver(dm644x_ccdc_driver);
diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h
index 90370e414e2c..90370e414e2c 100644
--- a/drivers/media/video/davinci/dm644x_ccdc_regs.h
+++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c
new file mode 100644
index 000000000000..b99d5423e3a8
--- /dev/null
+++ b/drivers/media/platform/davinci/isif.c
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * Image Sensor Interface (ISIF) driver
+ *
+ * This driver is for configuring the ISIF IP available on DM365 or any other
+ * TI SoCs. This is used for capturing yuv or bayer video or image data
+ * from a decoder or sensor. This IP is similar to the CCDC IP on DM355
+ * and DM6446, but with enhanced or additional ip blocks. The driver
+ * configures the ISIF upon commands from the vpfe bridge driver through
+ * ccdc_hw_device interface.
+ *
+ * TODO: 1) Raw bayer parameter settings and bayer capture
+ * 2) Add support for control ioctl
+ */
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <mach/mux.h>
+
+#include <media/davinci/isif.h>
+#include <media/davinci/vpss.h>
+
+#include "isif_regs.h"
+#include "ccdc_hw_device.h"
+
+/* Defaults for module configuration parameters */
+static struct isif_config_params_raw isif_config_defaults = {
+ .linearize = {
+ .en = 0,
+ .corr_shft = ISIF_NO_SHIFT,
+ .scale_fact = {1, 0},
+ },
+ .df_csc = {
+ .df_or_csc = 0,
+ .csc = {
+ .en = 0,
+ },
+ },
+ .dfc = {
+ .en = 0,
+ },
+ .bclamp = {
+ .en = 0,
+ },
+ .gain_offset = {
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ },
+ .culling = {
+ .hcpat_odd = 0xff,
+ .hcpat_even = 0xff,
+ .vcpat = 0xff,
+ },
+ .compress = {
+ .alg = ISIF_ALAW,
+ },
+};
+
+/* ISIF operation configuration */
+static struct isif_oper_config {
+ struct device *dev;
+ enum vpfe_hw_if_type if_type;
+ struct isif_ycbcr_config ycbcr;
+ struct isif_params_raw bayer;
+ enum isif_data_pack data_pack;
+ /* Master clock */
+ struct clk *mclk;
+ /* ISIF base address */
+ void __iomem *base_addr;
+ /* ISIF Linear Table 0 */
+ void __iomem *linear_tbl0_addr;
+ /* ISIF Linear Table 1 */
+ void __iomem *linear_tbl1_addr;
+} isif_cfg = {
+ .ycbcr = {
+ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
+ .frm_fmt = CCDC_FRMFMT_INTERLACED,
+ .win = ISIF_WIN_NTSC,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .pix_order = CCDC_PIXORDER_CBYCRY,
+ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
+ },
+ .bayer = {
+ .pix_fmt = CCDC_PIXFMT_RAW,
+ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
+ .win = ISIF_WIN_VGA,
+ .fid_pol = VPFE_PINPOL_POSITIVE,
+ .vd_pol = VPFE_PINPOL_POSITIVE,
+ .hd_pol = VPFE_PINPOL_POSITIVE,
+ .gain = {
+ .r_ye = {1, 0},
+ .gr_cy = {1, 0},
+ .gb_g = {1, 0},
+ .b_mg = {1, 0},
+ },
+ .cfa_pat = ISIF_CFA_PAT_MOSAIC,
+ .data_msb = ISIF_BIT_MSB_11,
+ .config_params = {
+ .data_shift = ISIF_NO_SHIFT,
+ .col_pat_field0 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .col_pat_field1 = {
+ .olop = ISIF_GREEN_BLUE,
+ .olep = ISIF_BLUE,
+ .elop = ISIF_RED,
+ .elep = ISIF_GREEN_RED,
+ },
+ .test_pat_gen = 0,
+ },
+ },
+ .data_pack = ISIF_DATA_PACK8,
+};
+
+/* Raw Bayer formats */
+static const u32 isif_raw_bayer_pix_formats[] = {
+ V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
+
+/* Raw YUV formats */
+static const u32 isif_raw_yuv_pix_formats[] = {
+ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
+
+/* register access routines */
+static inline u32 regr(u32 offset)
+{
+ return __raw_readl(isif_cfg.base_addr + offset);
+}
+
+static inline void regw(u32 val, u32 offset)
+{
+ __raw_writel(val, isif_cfg.base_addr + offset);
+}
+
+/* reg_modify() - read, modify and write register */
+static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
+{
+ u32 new_val = (regr(offset) & ~mask) | (val & mask);
+
+ regw(new_val, offset);
+ return new_val;
+}
+
+static inline void regw_lin_tbl(u32 val, u32 offset, int i)
+{
+ if (!i)
+ __raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
+ else
+ __raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
+}
+
+static void isif_disable_all_modules(void)
+{
+ /* disable BC */
+ regw(0, CLAMPCFG);
+ /* disable vdfc */
+ regw(0, DFCCTL);
+ /* disable CSC */
+ regw(0, CSCCTL);
+ /* disable linearization */
+ regw(0, LINCFG0);
+ /* disable other modules here as they are supported */
+}
+
+static void isif_enable(int en)
+{
+ if (!en) {
+ /* Before disable isif, disable all ISIF modules */
+ isif_disable_all_modules();
+ /*
+ * wait for next VD. Assume lowest scan rate is 12 Hz. So
+ * 100 msec delay is good enough
+ */
+ msleep(100);
+ }
+ reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
+}
+
+static void isif_enable_output_to_sdram(int en)
+{
+ reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
+}
+
+static void isif_config_culling(struct isif_cul *cul)
+{
+ u32 val;
+
+ /* Horizontal pattern */
+ val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
+ regw(val, CULH);
+
+ /* vertical pattern */
+ regw(cul->vcpat, CULV);
+
+ /* LPF */
+ reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
+ cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
+}
+
+static void isif_config_gain_offset(void)
+{
+ struct isif_gain_offsets_adj *gain_off_p =
+ &isif_cfg.bayer.config_params.gain_offset;
+ u32 val;
+
+ val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
+ (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
+ (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
+ (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
+
+ reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
+
+ val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.r_ye.decimal;
+ regw(val, CRGAIN);
+
+ val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gr_cy.decimal;
+ regw(val, CGRGAIN);
+
+ val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.gb_g.decimal;
+ regw(val, CGBGAIN);
+
+ val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
+ gain_off_p->gain.b_mg.decimal;
+ regw(val, CBGAIN);
+
+ regw(gain_off_p->offset, COFSTA);
+}
+
+static void isif_restore_defaults(void)
+{
+ enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
+
+ dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ /* Enable clock to ISIF, IPIPEIF and BL */
+ vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
+ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
+ vpss_enable_clock(VPSS_BL_CLOCK, 1);
+ /* Set default offset and gain */
+ isif_config_gain_offset();
+ vpss_select_ccdc_source(source);
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
+}
+
+static int isif_open(struct device *device)
+{
+ isif_restore_defaults();
+ return 0;
+}
+
+/* This function will configure the window size to be capture in ISIF reg */
+static void isif_setwin(struct v4l2_rect *image_win,
+ enum ccdc_frmfmt frm_fmt, int ppc)
+{
+ int horz_start, horz_nr_pixels;
+ int vert_start, vert_nr_lines;
+ int mid_img = 0;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
+ /*
+ * ppc - per pixel count. indicates how many pixels per cell
+ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
+ * raw capture this is 1
+ */
+ horz_start = image_win->left << (ppc - 1);
+ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
+
+ /* Writing the horizontal info into the registers */
+ regw(horz_start & START_PX_HOR_MASK, SPH);
+ regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
+ vert_start = image_win->top;
+
+ if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ vert_nr_lines = (image_win->height >> 1) - 1;
+ vert_start >>= 1;
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ } else {
+ /* To account for VD since line 0 doesn't have any data */
+ vert_start += 1;
+ vert_nr_lines = image_win->height - 1;
+ /* configure VDINT0 and VDINT1 */
+ mid_img = vert_start + (image_win->height / 2);
+ regw(mid_img, VDINT1);
+ }
+
+ regw(0, VDINT0);
+ regw(vert_start & START_VER_ONE_MASK, SLV0);
+ regw(vert_start & START_VER_TWO_MASK, SLV1);
+ regw(vert_nr_lines & NUM_LINES_VER, LNV);
+}
+
+static void isif_config_bclamp(struct isif_black_clamp *bc)
+{
+ u32 val;
+
+ /*
+ * DC Offset is always added to image data irrespective of bc enable
+ * status
+ */
+ regw(bc->dc_offset, CLDCOFST);
+
+ if (bc->en) {
+ val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
+
+ /* Enable BC and horizontal clamp caculation paramaters */
+ val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
+
+ regw(val, CLAMPCFG);
+
+ if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
+ /*
+ * Window count for calculation
+ * Base window selection
+ * pixel limit
+ * Horizontal size of window
+ * vertical size of the window
+ * Horizontal start position of the window
+ * Vertical start position of the window
+ */
+ val = bc->horz.win_count_calc |
+ ((!!bc->horz.base_win_sel_calc) <<
+ ISIF_HORZ_BC_WIN_SEL_SHIFT) |
+ ((!!bc->horz.clamp_pix_limit) <<
+ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
+ (bc->horz.win_h_sz_calc <<
+ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
+ (bc->horz.win_v_sz_calc <<
+ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
+ regw(val, CLHWIN0);
+
+ regw(bc->horz.win_start_h_calc, CLHWIN1);
+ regw(bc->horz.win_start_v_calc, CLHWIN2);
+ }
+
+ /* vertical clamp caculation paramaters */
+
+ /* Reset clamp value sel for previous line */
+ val |=
+ (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) |
+ (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT);
+ regw(val, CLVWIN0);
+
+ /* Optical Black horizontal start position */
+ regw(bc->vert.ob_start_h, CLVWIN1);
+ /* Optical Black vertical start position */
+ regw(bc->vert.ob_start_v, CLVWIN2);
+ /* Optical Black vertical size for calculation */
+ regw(bc->vert.ob_v_sz_calc, CLVWIN3);
+ /* Vertical start position for BC subtraction */
+ regw(bc->vert_start_sub, CLSV);
+ }
+}
+
+static void isif_config_linearization(struct isif_linearize *linearize)
+{
+ u32 val, i;
+
+ if (!linearize->en) {
+ regw(0, LINCFG0);
+ return;
+ }
+
+ /* shift value for correction & enable linearization (set lsb) */
+ val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1;
+ regw(val, LINCFG0);
+
+ /* Scale factor */
+ val = ((!!linearize->scale_fact.integer) <<
+ ISIF_LIN_SCALE_FACT_INTEG_SHIFT) |
+ linearize->scale_fact.decimal;
+ regw(val, LINCFG1);
+
+ for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) {
+ if (i % 2)
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1);
+ else
+ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0);
+ }
+}
+
+static int isif_config_dfc(struct isif_dfc *vdfc)
+{
+ /* initialize retries to loop for max ~ 250 usec */
+ u32 val, count, retries = loops_per_jiffy / (4000/HZ);
+ int i;
+
+ if (!vdfc->en)
+ return 0;
+
+ /* Correction mode */
+ val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT);
+
+ /* Correct whole line or partial */
+ if (vdfc->corr_whole_line)
+ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
+
+ /* level shift value */
+ val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT;
+
+ regw(val, DFCCTL);
+
+ /* Defect saturation level */
+ regw(vdfc->def_sat_level, VDFSATLV);
+
+ regw(vdfc->table[0].pos_vert, DFCMEM0);
+ regw(vdfc->table[0].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[0].level_at_pos, DFCMEM2);
+ regw(vdfc->table[0].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[0].level_low_pixels, DFCMEM4);
+ }
+
+ /* set DFCMARST and set DFCMWR */
+ val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n");
+ return -1;
+ }
+
+ for (i = 1; i < vdfc->num_vdefects; i++) {
+ regw(vdfc->table[i].pos_vert, DFCMEM0);
+ regw(vdfc->table[i].pos_horz, DFCMEM1);
+ if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
+ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
+ regw(vdfc->table[i].level_at_pos, DFCMEM2);
+ regw(vdfc->table[i].level_up_pixels, DFCMEM3);
+ regw(vdfc->table[i].level_low_pixels, DFCMEM4);
+ }
+ val = regr(DFCMEMCTL);
+ /* clear DFCMARST and set DFCMWR */
+ val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT);
+ val |= 1;
+ regw(val, DFCMEMCTL);
+
+ count = retries;
+ while (count && (regr(DFCMEMCTL) & 0x1))
+ count--;
+
+ if (!count) {
+ dev_err(isif_cfg.dev,
+ "defect table write timeout !!!\n");
+ return -1;
+ }
+ }
+ if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) {
+ /* Extra cycle needed */
+ regw(0, DFCMEM0);
+ regw(0x1FFF, DFCMEM1);
+ regw(1, DFCMEMCTL);
+ }
+
+ /* enable VDFC */
+ reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT),
+ DFCCTL);
+ return 0;
+}
+
+static void isif_config_csc(struct isif_df_csc *df_csc)
+{
+ u32 val1 = 0, val2 = 0, i;
+
+ if (!df_csc->csc.en) {
+ regw(0, CSCCTL);
+ return;
+ }
+ for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) {
+ if ((i % 2) == 0) {
+ /* CSCM - LSB */
+ val1 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ } else {
+
+ /* CSCM - MSB */
+ val2 = (df_csc->csc.coeff[i].integer <<
+ ISIF_CSC_COEF_INTEG_SHIFT) |
+ df_csc->csc.coeff[i].decimal;
+ val2 <<= ISIF_CSCM_MSB_SHIFT;
+ val2 |= val1;
+ regw(val2, (CSCM0 + ((i - 1) << 1)));
+ }
+ }
+
+ /* program the active area */
+ regw(df_csc->start_pix, FMTSPH);
+ /*
+ * one extra pixel as required for CSC. Actually number of
+ * pixel - 1 should be configured in this register. So we
+ * need to subtract 1 before writing to FMTSPH, but we will
+ * not do this since csc requires one extra pixel
+ */
+ regw(df_csc->num_pixels, FMTLNH);
+ regw(df_csc->start_line, FMTSLV);
+ /*
+ * one extra line as required for CSC. See reason documented for
+ * num_pixels
+ */
+ regw(df_csc->num_lines, FMTLNV);
+
+ /* Enable CSC */
+ regw(1, CSCCTL);
+}
+
+static int isif_config_raw(void)
+{
+ struct isif_params_raw *params = &isif_cfg.bayer;
+ struct isif_config_params_raw *module_params =
+ &isif_cfg.bayer.config_params;
+ struct vpss_pg_frame_size frame_size;
+ struct vpss_sync_pol sync;
+ u32 val;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n");
+
+ /*
+ * Configure CCDCFG register:-
+ * Set CCD Not to swap input since input is RAW data
+ * Set FID detection function to Latch at V-Sync
+ * Set WENLOG - isif valid area
+ * Set TRGSEL
+ * Set EXTRG
+ * Packed to 8 or 16 bits
+ */
+
+ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
+ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
+ ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack;
+
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val);
+ regw(val, CCDCFG);
+
+ /*
+ * Configure the vertical sync polarity(MODESET.VDPOL)
+ * Configure the horizontal sync polarity (MODESET.HDPOL)
+ * Configure frame id polarity (MODESET.FLDPOL)
+ * Configure data polarity
+ * Configure External WEN Selection
+ * Configure frame format(progressive or interlace)
+ * Configure pixel format (Input mode)
+ * Configure the data shift
+ */
+
+ val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) |
+ (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->config_params.data_shift << ISIF_DATASFT_SHIFT);
+
+ regw(val, MODESET);
+ dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val);
+
+ /*
+ * Configure GAMMAWD register
+ * CFA pattern setting
+ */
+ val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT;
+
+ /* Gamma msb */
+ if (module_params->compress.alg == ISIF_ALAW)
+ val |= ISIF_ALAW_ENABLE;
+
+ val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT);
+ regw(val, CGAMMAWD);
+
+ /* Configure DPCM compression settings */
+ if (module_params->compress.alg == ISIF_DPCM) {
+ val = BIT(ISIF_DPCM_EN_SHIFT) |
+ (module_params->compress.pred <<
+ ISIF_DPCM_PREDICTOR_SHIFT);
+ }
+
+ regw(val, MISC);
+
+ /* Configure Gain & Offset */
+ isif_config_gain_offset();
+
+ /* Configure Color pattern */
+ val = (params->config_params.col_pat_field0.olop) |
+ (params->config_params.col_pat_field0.olep << 2) |
+ (params->config_params.col_pat_field0.elop << 4) |
+ (params->config_params.col_pat_field0.elep << 6) |
+ (params->config_params.col_pat_field1.olop << 8) |
+ (params->config_params.col_pat_field1.olep << 10) |
+ (params->config_params.col_pat_field1.elop << 12) |
+ (params->config_params.col_pat_field1.elep << 14);
+ regw(val, CCOLP);
+ dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val);
+
+ /* Configure HSIZE register */
+ val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT;
+
+ /* calculate line offset in 32 bytes based on pack value */
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ val |= ((params->win.width + 31) >> 5);
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ val |= (((params->win.width +
+ (params->win.width >> 2)) + 31) >> 5);
+ else
+ val |= (((params->win.width * 2) + 31) >> 5);
+ regw(val, HSIZE);
+
+ /* Configure SDOFST register */
+ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (params->image_invert_en) {
+ /* For interlace inverse mode */
+ regw(0x4B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n");
+ } else {
+ /* For interlace non inverse mode */
+ regw(0x0B6D, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n");
+ }
+ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
+ if (params->image_invert_en) {
+ /* For progressive inverse mode */
+ regw(0x4000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n");
+ } else {
+ /* For progressive non inverse mode */
+ regw(0x0000, SDOFST);
+ dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n");
+ }
+ }
+
+ /* Configure video window */
+ isif_setwin(&params->win, params->frm_fmt, 1);
+
+ /* Configure Black Clamp */
+ isif_config_bclamp(&module_params->bclamp);
+
+ /* Configure Vertical Defection Pixel Correction */
+ if (isif_config_dfc(&module_params->dfc) < 0)
+ return -EFAULT;
+
+ if (!module_params->df_csc.df_or_csc)
+ /* Configure Color Space Conversion */
+ isif_config_csc(&module_params->df_csc);
+
+ isif_config_linearization(&module_params->linearize);
+
+ /* Configure Culling */
+ isif_config_culling(&module_params->culling);
+
+ /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
+ regw(module_params->horz_offset, DATAHOFST);
+ regw(module_params->vert_offset, DATAVOFST);
+
+ /* Setup test pattern if enabled */
+ if (params->config_params.test_pat_gen) {
+ /* Use the HD/VD pol settings from user */
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ frame_size.hlpfr = isif_cfg.bayer.win.width;
+ frame_size.pplen = isif_cfg.bayer.win.height;
+ dm365_vpss_set_pg_frame_size(frame_size);
+ vpss_select_ccdc_source(VPSS_PGLPBK);
+ }
+
+ dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n");
+ return 0;
+}
+
+static int isif_set_buftype(enum ccdc_buftype buf_type)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.buf_type = buf_type;
+ else
+ isif_cfg.ycbcr.buf_type = buf_type;
+
+ return 0;
+
+}
+static enum ccdc_buftype isif_get_buftype(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.buf_type;
+
+ return isif_cfg.ycbcr.buf_type;
+}
+
+static int isif_enum_pix(u32 *pix, int i)
+{
+ int ret = -EINVAL;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) {
+ *pix = isif_raw_bayer_pix_formats[i];
+ ret = 0;
+ }
+ } else {
+ if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) {
+ *pix = isif_raw_yuv_pix_formats[i];
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int isif_set_pixel_format(unsigned int pixfmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (pixfmt == V4L2_PIX_FMT_SBGGR8) {
+ if ((isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_ALAW) &&
+ (isif_cfg.bayer.config_params.compress.alg !=
+ ISIF_DPCM)) {
+ dev_dbg(isif_cfg.dev,
+ "Either configure A-Law or DPCM\n");
+ return -EINVAL;
+ }
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) {
+ isif_cfg.bayer.config_params.compress.alg =
+ ISIF_NO_COMPRESSION;
+ isif_cfg.data_pack = ISIF_PACK_16BIT;
+ } else
+ return -EINVAL;
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ } else {
+ if (pixfmt == V4L2_PIX_FMT_YUYV)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
+ else if (pixfmt == V4L2_PIX_FMT_UYVY)
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ else
+ return -EINVAL;
+ isif_cfg.data_pack = ISIF_PACK_8BIT;
+ }
+ return 0;
+}
+
+static u32 isif_get_pixel_format(void)
+{
+ u32 pixfmt;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW ||
+ isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM)
+ pixfmt = V4L2_PIX_FMT_SBGGR8;
+ else
+ pixfmt = V4L2_PIX_FMT_SBGGR16;
+ else {
+ if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
+ pixfmt = V4L2_PIX_FMT_YUYV;
+ else
+ pixfmt = V4L2_PIX_FMT_UYVY;
+ }
+ return pixfmt;
+}
+
+static int isif_set_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ isif_cfg.bayer.win.top = win->top;
+ isif_cfg.bayer.win.left = win->left;
+ isif_cfg.bayer.win.width = win->width;
+ isif_cfg.bayer.win.height = win->height;
+ } else {
+ isif_cfg.ycbcr.win.top = win->top;
+ isif_cfg.ycbcr.win.left = win->left;
+ isif_cfg.ycbcr.win.width = win->width;
+ isif_cfg.ycbcr.win.height = win->height;
+ }
+ return 0;
+}
+
+static void isif_get_image_window(struct v4l2_rect *win)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ *win = isif_cfg.bayer.win;
+ else
+ *win = isif_cfg.ycbcr.win;
+}
+
+static unsigned int isif_get_line_length(void)
+{
+ unsigned int len;
+
+ if (isif_cfg.if_type == VPFE_RAW_BAYER) {
+ if (isif_cfg.data_pack == ISIF_PACK_8BIT)
+ len = ((isif_cfg.bayer.win.width));
+ else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
+ len = (((isif_cfg.bayer.win.width * 2) +
+ (isif_cfg.bayer.win.width >> 2)));
+ else
+ len = (((isif_cfg.bayer.win.width * 2)));
+ } else
+ len = (((isif_cfg.ycbcr.win.width * 2)));
+ return ALIGN(len, 32);
+}
+
+static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ isif_cfg.bayer.frm_fmt = frm_fmt;
+ else
+ isif_cfg.ycbcr.frm_fmt = frm_fmt;
+ return 0;
+}
+static enum ccdc_frmfmt isif_get_frame_format(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_cfg.bayer.frm_fmt;
+ return isif_cfg.ycbcr.frm_fmt;
+}
+
+static int isif_getfid(void)
+{
+ return (regr(MODESET) >> 15) & 0x1;
+}
+
+/* misc operations */
+static void isif_setfbaddr(unsigned long addr)
+{
+ regw((addr >> 21) & 0x07ff, CADU);
+ regw((addr >> 5) & 0x0ffff, CADL);
+}
+
+static int isif_set_hw_if_params(struct vpfe_hw_if_param *params)
+{
+ isif_cfg.if_type = params->if_type;
+
+ switch (params->if_type) {
+ case VPFE_BT656:
+ case VPFE_BT656_10BIT:
+ case VPFE_YCBCR_SYNC_8:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_BT1120:
+ case VPFE_YCBCR_SYNC_16:
+ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT;
+ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
+ break;
+ case VPFE_RAW_BAYER:
+ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
+ break;
+ default:
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function will configure ISIF for YCbCr parameters. */
+static int isif_config_ycbcr(void)
+{
+ struct isif_ycbcr_config *params = &isif_cfg.ycbcr;
+ struct vpss_pg_frame_size frame_size;
+ u32 modeset = 0, ccdcfg = 0;
+ struct vpss_sync_pol sync;
+
+ dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr...");
+
+ /* configure pixel format or input mode */
+ modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) |
+ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
+ (params->fid_pol << ISIF_FID_POL_SHIFT) |
+ (params->hd_pol << ISIF_HD_POL_SHIFT) |
+ (params->vd_pol << ISIF_VD_POL_SHIFT);
+
+ /* pack the data to 8-bit ISIFCFG */
+ switch (isif_cfg.if_type) {
+ case VPFE_BT656:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT);
+ regw(3, REC656IF);
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR;
+ break;
+ case VPFE_BT656_10BIT:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ /* setup BT.656, embedded sync */
+ regw(3, REC656IF);
+ /* enable 10 bit mode in ccdcfg */
+ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
+ ISIF_BW656_ENABLE;
+ break;
+ case VPFE_BT1120:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ regw(3, REC656IF);
+ break;
+
+ case VPFE_YCBCR_SYNC_8:
+ ccdcfg |= ISIF_DATA_PACK8;
+ ccdcfg |= ISIF_YCINSWP_YCBCR;
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ case VPFE_YCBCR_SYNC_16:
+ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
+ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* should never come here */
+ dev_dbg(isif_cfg.dev, "Invalid interface type\n");
+ return -EINVAL;
+ }
+
+ regw(modeset, MODESET);
+
+ /* Set up pix order */
+ ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT;
+
+ regw(ccdcfg, CCDCFG);
+
+ /* configure video window */
+ if ((isif_cfg.if_type == VPFE_BT1120) ||
+ (isif_cfg.if_type == VPFE_YCBCR_SYNC_16))
+ isif_setwin(&params->win, params->frm_fmt, 1);
+ else
+ isif_setwin(&params->win, params->frm_fmt, 2);
+
+ /*
+ * configure the horizontal line offset
+ * this is done by rounding up width to a multiple of 16 pixels
+ * and multiply by two to account for y:cb:cr 4:2:2 data
+ */
+ regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE);
+
+ /* configure the memory line offset */
+ if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) &&
+ (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED))
+ /* two fields are interleaved in memory */
+ regw(0x00000249, SDOFST);
+
+ /* Setup test pattern if enabled */
+ if (isif_cfg.bayer.config_params.test_pat_gen) {
+ sync.ccdpg_hdpol = params->hd_pol;
+ sync.ccdpg_vdpol = params->vd_pol;
+ dm365_vpss_set_sync_pol(sync);
+ dm365_vpss_set_pg_frame_size(frame_size);
+ }
+ return 0;
+}
+
+static int isif_configure(void)
+{
+ if (isif_cfg.if_type == VPFE_RAW_BAYER)
+ return isif_config_raw();
+ return isif_config_ycbcr();
+}
+
+static int isif_close(struct device *device)
+{
+ /* copy defaults to module params */
+ isif_cfg.bayer.config_params = isif_config_defaults;
+ return 0;
+}
+
+static struct ccdc_hw_device isif_hw_dev = {
+ .name = "ISIF",
+ .owner = THIS_MODULE,
+ .hw_ops = {
+ .open = isif_open,
+ .close = isif_close,
+ .enable = isif_enable,
+ .enable_out_to_sdram = isif_enable_output_to_sdram,
+ .set_hw_if_params = isif_set_hw_if_params,
+ .configure = isif_configure,
+ .set_buftype = isif_set_buftype,
+ .get_buftype = isif_get_buftype,
+ .enum_pix = isif_enum_pix,
+ .set_pixel_format = isif_set_pixel_format,
+ .get_pixel_format = isif_get_pixel_format,
+ .set_frame_format = isif_set_frame_format,
+ .get_frame_format = isif_get_frame_format,
+ .set_image_window = isif_set_image_window,
+ .get_image_window = isif_get_image_window,
+ .get_line_length = isif_get_line_length,
+ .setfbaddr = isif_setfbaddr,
+ .getfid = isif_getfid,
+ },
+};
+
+static int __devinit isif_probe(struct platform_device *pdev)
+{
+ void (*setup_pinmux)(void);
+ struct resource *res;
+ void *__iomem addr;
+ int status = 0, i;
+
+ /*
+ * first try to register with vpfe. If not correct platform, then we
+ * don't have to iomap
+ */
+ status = vpfe_register_ccdc_device(&isif_hw_dev);
+ if (status < 0)
+ return status;
+
+ /* Get and enable Master clock */
+ isif_cfg.mclk = clk_get(&pdev->dev, "master");
+ if (IS_ERR(isif_cfg.mclk)) {
+ status = PTR_ERR(isif_cfg.mclk);
+ goto fail_mclk;
+ }
+ if (clk_enable(isif_cfg.mclk)) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+
+ /* Platform data holds setup_pinmux function ptr */
+ if (NULL == pdev->dev.platform_data) {
+ status = -ENODEV;
+ goto fail_mclk;
+ }
+ setup_pinmux = pdev->dev.platform_data;
+ /*
+ * setup Mux configuration for ccdc which may be different for
+ * different SoCs using this CCDC
+ */
+ setup_pinmux();
+
+ i = 0;
+ /* Get the ISIF base address, linearization table0 and table1 addr. */
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ status = -ENODEV;
+ goto fail_nobase_res;
+ }
+ res = request_mem_region(res->start, resource_size(res),
+ res->name);
+ if (!res) {
+ status = -EBUSY;
+ goto fail_nobase_res;
+ }
+ addr = ioremap_nocache(res->start, resource_size(res));
+ if (!addr) {
+ status = -ENOMEM;
+ goto fail_base_iomap;
+ }
+ switch (i) {
+ case 0:
+ /* ISIF base address */
+ isif_cfg.base_addr = addr;
+ break;
+ case 1:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl0_addr = addr;
+ break;
+ default:
+ /* ISIF linear tbl0 address */
+ isif_cfg.linear_tbl1_addr = addr;
+ break;
+ }
+ i++;
+ }
+ isif_cfg.dev = &pdev->dev;
+
+ printk(KERN_NOTICE "%s is registered with vpfe.\n",
+ isif_hw_dev.name);
+ return 0;
+fail_base_iomap:
+ release_mem_region(res->start, resource_size(res));
+ i--;
+fail_nobase_res:
+ if (isif_cfg.base_addr)
+ iounmap(isif_cfg.base_addr);
+ if (isif_cfg.linear_tbl0_addr)
+ iounmap(isif_cfg.linear_tbl0_addr);
+
+ while (i >= 0) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ release_mem_region(res->start, resource_size(res));
+ i--;
+ }
+fail_mclk:
+ clk_put(isif_cfg.mclk);
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return status;
+}
+
+static int isif_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ int i = 0;
+
+ iounmap(isif_cfg.base_addr);
+ iounmap(isif_cfg.linear_tbl0_addr);
+ iounmap(isif_cfg.linear_tbl1_addr);
+ while (i < 3) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+ i++;
+ }
+ vpfe_unregister_ccdc_device(&isif_hw_dev);
+ return 0;
+}
+
+static struct platform_driver isif_driver = {
+ .driver = {
+ .name = "isif",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(isif_remove),
+ .probe = isif_probe,
+};
+
+module_platform_driver(isif_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h
index aa69a463c122..aa69a463c122 100644
--- a/drivers/media/video/davinci/isif_regs.h
+++ b/drivers/media/platform/davinci/isif_regs.h
diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c
index c4a82a1a8a97..c4a82a1a8a97 100644
--- a/drivers/media/video/davinci/vpbe.c
+++ b/drivers/media/platform/davinci/vpbe.c
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
new file mode 100644
index 000000000000..239f37bfa313
--- /dev/null
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -0,0 +1,1839 @@
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/slab.h>
+
+#include <asm/pgtable.h>
+#include <mach/cputype.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/davinci/vpbe_display.h>
+#include <media/davinci/vpbe_types.h>
+#include <media/davinci/vpbe.h>
+#include <media/davinci/vpbe_venc.h>
+#include <media/davinci/vpbe_osd.h>
+#include "vpbe_venc_regs.h"
+
+#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
+
+static int debug;
+
+#define VPBE_DEFAULT_NUM_BUFS 3
+
+module_param(debug, int, 0644);
+
+static int venc_is_second_field(struct vpbe_display *disp_dev)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int ret;
+ int val;
+
+ ret = v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ ioctl,
+ VENC_GET_FLD,
+ &val);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in getting Field ID 0\n");
+ }
+ return val;
+}
+
+static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct timespec timevalue;
+
+ if (layer->cur_frm == layer->next_frm)
+ return;
+ ktime_get_ts(&timevalue);
+ layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
+ layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
+ layer->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&layer->cur_frm->done);
+ /* Make cur_frm pointing to next_frm */
+ layer->cur_frm = layer->next_frm;
+}
+
+static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
+ struct vpbe_layer *layer)
+{
+ struct osd_state *osd_device = disp_obj->osd_device;
+ unsigned long addr;
+
+ spin_lock(&disp_obj->dma_queue_lock);
+ if (list_empty(&layer->dma_queue) ||
+ (layer->cur_frm != layer->next_frm)) {
+ spin_unlock(&disp_obj->dma_queue_lock);
+ return;
+ }
+ /*
+ * one field is displayed configure
+ * the next frame if it is available
+ * otherwise hold on current frame
+ * Get next from the buffer queue
+ */
+ layer->next_frm = list_entry(
+ layer->dma_queue.next,
+ struct videobuf_buffer,
+ queue);
+ /* Remove that from the buffer queue */
+ list_del(&layer->next_frm->queue);
+ spin_unlock(&disp_obj->dma_queue_lock);
+ /* Mark state of the frame to active */
+ layer->next_frm->state = VIDEOBUF_ACTIVE;
+ addr = videobuf_to_dma_contig(layer->next_frm);
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_obj->cbcr_ofst);
+}
+
+/* interrupt service routine */
+static irqreturn_t venc_isr(int irq, void *arg)
+{
+ struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
+ struct vpbe_layer *layer;
+ static unsigned last_event;
+ unsigned event = 0;
+ int fid;
+ int i;
+
+ if ((NULL == arg) || (NULL == disp_dev->dev[0]))
+ return IRQ_HANDLED;
+
+ if (venc_is_second_field(disp_dev))
+ event |= VENC_SECOND_FIELD;
+ else
+ event |= VENC_FIRST_FIELD;
+
+ if (event == (last_event & ~VENC_END_OF_FRAME)) {
+ /*
+ * If the display is non-interlaced, then we need to flag the
+ * end-of-frame event at every interrupt regardless of the
+ * value of the FIDST bit. We can conclude that the display is
+ * non-interlaced if the value of the FIDST bit is unchanged
+ * from the previous interrupt.
+ */
+ event |= VENC_END_OF_FRAME;
+ } else if (event == VENC_SECOND_FIELD) {
+ /* end-of-frame for interlaced display */
+ event |= VENC_END_OF_FRAME;
+ }
+ last_event = event;
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ layer = disp_dev->dev[i];
+ /* If streaming is started in this layer */
+ if (!layer->started)
+ continue;
+
+ if (layer->layer_first_int) {
+ layer->layer_first_int = 0;
+ continue;
+ }
+ /* Check the field format */
+ if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
+ (event & VENC_END_OF_FRAME)) {
+ /* Progressive mode */
+
+ vpbe_isr_even_field(disp_dev, layer);
+ vpbe_isr_odd_field(disp_dev, layer);
+ } else {
+ /* Interlaced mode */
+
+ layer->field_id ^= 1;
+ if (event & VENC_FIRST_FIELD)
+ fid = 0;
+ else
+ fid = 1;
+
+ /*
+ * If field id does not match with store
+ * field id
+ */
+ if (fid != layer->field_id) {
+ /* Make them in sync */
+ layer->field_id = fid;
+ continue;
+ }
+ /*
+ * device field id and local field id are
+ * in sync. If this is even field
+ */
+ if (0 == fid)
+ vpbe_isr_even_field(disp_dev, layer);
+ else /* odd field */
+ vpbe_isr_odd_field(disp_dev, layer);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * vpbe_buffer_prepare()
+ * This is the callback function called from videobuf_qbuf() function
+ * the buffer is prepared and user space virtual address is converted into
+ * physical address
+ */
+static int vpbe_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_prepare\n");
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = layer->pix_fmt.width;
+ vb->height = layer->pix_fmt.height;
+ vb->size = layer->pix_fmt.sizeimage;
+ vb->field = field;
+
+ ret = videobuf_iolock(q, vb, NULL);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
+ user address\n");
+ return -EINVAL;
+ }
+
+ addr = videobuf_to_dma_contig(vb);
+
+ if (q->streaming) {
+ if (!IS_ALIGNED(addr, 8)) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "buffer_prepare:offset is \
+ not aligned to 32 bytes\n");
+ return -EINVAL;
+ }
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ return 0;
+}
+
+/*
+ * vpbe_buffer_setup()
+ * This function allocates memory for the buffers
+ */
+static int vpbe_buffer_setup(struct videobuf_queue *q,
+ unsigned int *count,
+ unsigned int *size)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
+
+ *size = layer->pix_fmt.sizeimage;
+
+ /* Store number of buffers allocated in numbuffer member */
+ if (*count < VPBE_DEFAULT_NUM_BUFS)
+ *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
+
+ return 0;
+}
+
+/*
+ * vpbe_buffer_queue()
+ * This function adds the buffer to DMA queue
+ */
+static void vpbe_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ spin_lock_irqsave(&disp->dma_queue_lock, flags);
+ list_add_tail(&vb->queue, &layer->dma_queue);
+ spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * vpbe_buffer_release()
+ * This function is called from the videobuf layer to free memory allocated to
+ * the buffers
+ */
+static void vpbe_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and layer object */
+ struct vpbe_fh *fh = q->priv_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe_buffer_release\n");
+
+ if (V4L2_MEMORY_USERPTR != layer->memory)
+ videobuf_dma_contig_free(q, vb);
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops video_qops = {
+ .buf_setup = vpbe_buffer_setup,
+ .buf_prepare = vpbe_buffer_prepare,
+ .buf_queue = vpbe_buffer_queue,
+ .buf_release = vpbe_buffer_release,
+};
+
+static
+struct vpbe_layer*
+_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ enum vpbe_display_device_id thiswin, otherwin;
+ thiswin = layer->device_id;
+
+ otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
+ VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
+ return disp_dev->dev[otherwin];
+}
+
+static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ unsigned long addr;
+ int ret;
+
+ addr = videobuf_to_dma_contig(layer->cur_frm);
+ /* Set address in the display registers */
+ osd_device->ops.start_layer(osd_device,
+ layer->layer_info.id,
+ addr,
+ disp_dev->cbcr_ofst);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ layer->layer_info.id, 0);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 0\n");
+ return -1;
+ }
+
+ /* Enable the window */
+ layer->layer_info.enable = 1;
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+
+ ret = osd_device->ops.enable_layer(osd_device,
+ otherlayer->layer_info.id, 1);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in enabling osd window layer 1\n");
+ return -1;
+ }
+ otherlayer->layer_info.enable = 1;
+ }
+ return 0;
+}
+
+static void
+vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int expected_xsize, int expected_ysize)
+{
+ struct display_layer_info *layer_info = &layer->layer_info;
+ struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int calculated_xsize;
+ int h_exp = 0;
+ int v_exp = 0;
+ int h_scale;
+ int v_scale;
+
+ v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id;
+
+ /*
+ * Application initially set the image format. Current display
+ * size is obtained from the vpbe display controller. expected_xsize
+ * and expected_ysize are set through S_CROP ioctl. Based on this,
+ * driver will calculate the scale factors for vertical and
+ * horizontal direction so that the image is displayed scaled
+ * and expanded. Application uses expansion to display the image
+ * in a square pixel. Otherwise it is displayed using displays
+ * pixel aspect ratio.It is expected that application chooses
+ * the crop coordinates for cropped or scaled display. if crop
+ * size is less than the image size, it is displayed cropped or
+ * it is displayed scaled and/or expanded.
+ *
+ * to begin with, set the crop window same as expected. Later we
+ * will override with scaled window size
+ */
+
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */
+ layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */
+ layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */
+
+ if (pixfmt->width < expected_xsize) {
+ h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
+ if (h_scale < 2)
+ h_scale = 1;
+ else if (h_scale >= 4)
+ h_scale = 4;
+ else
+ h_scale = 2;
+ cfg->xsize *= h_scale;
+ if (cfg->xsize < expected_xsize) {
+ if ((standard_id & V4L2_STD_525_60) ||
+ (standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->xsize *
+ VPBE_DISPLAY_H_EXP_RATIO_N) /
+ VPBE_DISPLAY_H_EXP_RATIO_D;
+ if (calculated_xsize <= expected_xsize) {
+ h_exp = 1;
+ cfg->xsize = calculated_xsize;
+ }
+ }
+ }
+ if (h_scale == 2)
+ layer_info->h_zoom = ZOOM_X2;
+ else if (h_scale == 4)
+ layer_info->h_zoom = ZOOM_X4;
+ if (h_exp)
+ layer_info->h_exp = H_EXP_9_OVER_8;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->xsize = expected_xsize;
+ }
+
+ if (pixfmt->height < expected_ysize) {
+ v_scale = expected_ysize / pixfmt->height;
+ if (v_scale < 2)
+ v_scale = 1;
+ else if (v_scale >= 4)
+ v_scale = 4;
+ else
+ v_scale = 2;
+ cfg->ysize *= v_scale;
+ if (cfg->ysize < expected_ysize) {
+ if ((standard_id & V4L2_STD_625_50)) {
+ calculated_xsize = (cfg->ysize *
+ VPBE_DISPLAY_V_EXP_RATIO_N) /
+ VPBE_DISPLAY_V_EXP_RATIO_D;
+ if (calculated_xsize <= expected_ysize) {
+ v_exp = 1;
+ cfg->ysize = calculated_xsize;
+ }
+ }
+ }
+ if (v_scale == 2)
+ layer_info->v_zoom = ZOOM_X2;
+ else if (v_scale == 4)
+ layer_info->v_zoom = ZOOM_X4;
+ if (v_exp)
+ layer_info->h_exp = V_EXP_6_OVER_5;
+ } else {
+ /* no scaling, only cropping. Set display area to crop area */
+ cfg->ysize = expected_ysize;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "crop display xsize = %d, ysize = %d\n",
+ cfg->xsize, cfg->ysize);
+}
+
+static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
+ struct vpbe_layer *layer,
+ int top, int left)
+{
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ cfg->xpos = min((unsigned int)left,
+ vpbe_dev->current_timings.xres - cfg->xsize);
+ cfg->ypos = min((unsigned int)top,
+ vpbe_dev->current_timings.yres - cfg->ysize);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "new xpos = %d, ypos = %d\n",
+ cfg->xpos, cfg->ypos);
+}
+
+static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
+ struct v4l2_rect *c)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+
+ if ((c->width == 0) ||
+ ((c->width + c->left) > vpbe_dev->current_timings.xres))
+ c->width = vpbe_dev->current_timings.xres - c->left;
+
+ if ((c->height == 0) || ((c->height + c->top) >
+ vpbe_dev->current_timings.yres))
+ c->height = vpbe_dev->current_timings.yres - c->top;
+
+ /* window height must be even for interlaced display */
+ if (vpbe_dev->current_timings.interlaced)
+ c->height &= (~0x01);
+
+}
+
+/**
+ * vpbe_try_format()
+ * If user application provides width and height, and have bytesperline set
+ * to zero, driver calculates bytesperline and sizeimage based on hardware
+ * limits.
+ */
+static int vpbe_try_format(struct vpbe_display *disp_dev,
+ struct v4l2_pix_format *pixfmt, int check)
+{
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ int min_height = 1;
+ int min_width = 32;
+ int max_height;
+ int max_width;
+ int bpp;
+
+ if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
+ (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
+ /* choose default as V4L2_PIX_FMT_UYVY */
+ pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ /* Check the field format */
+ if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
+ (pixfmt->field != V4L2_FIELD_NONE)) {
+ if (vpbe_dev->current_timings.interlaced)
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ else
+ pixfmt->field = V4L2_FIELD_NONE;
+ }
+
+ if (pixfmt->field == V4L2_FIELD_INTERLACED)
+ min_height = 2;
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ bpp = 1;
+ else
+ bpp = 2;
+
+ max_width = vpbe_dev->current_timings.xres;
+ max_height = vpbe_dev->current_timings.yres;
+
+ min_width /= bpp;
+
+ if (!pixfmt->width || (pixfmt->width < min_width) ||
+ (pixfmt->width > max_width)) {
+ pixfmt->width = vpbe_dev->current_timings.xres;
+ }
+
+ if (!pixfmt->height || (pixfmt->height < min_height) ||
+ (pixfmt->height > max_height)) {
+ pixfmt->height = vpbe_dev->current_timings.yres;
+ }
+
+ if (pixfmt->bytesperline < (pixfmt->width * bpp))
+ pixfmt->bytesperline = pixfmt->width * bpp;
+
+ /* Make the bytesperline 32 byte aligned */
+ pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
+
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
+ (pixfmt->bytesperline * pixfmt->height >> 1);
+ else
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ return 0;
+}
+
+static int vpbe_display_g_priority(struct file *file, void *priv,
+ enum v4l2_priority *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+
+ *p = v4l2_prio_max(&layer->prio);
+
+ return 0;
+}
+
+static int vpbe_display_s_priority(struct file *file, void *priv,
+ enum v4l2_priority p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ int ret;
+
+ ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
+
+ return ret;
+}
+
+static int vpbe_display_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ cap->version = VPBE_DISPLAY_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vpbe_display_s_crop(struct file *file, void *priv,
+ const struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_CROP, layer id = %d\n", layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (rect->top < 0)
+ rect->top = 0;
+ if (rect->left < 0)
+ rect->left = 0;
+
+ vpbe_disp_check_window_params(disp_dev, rect);
+
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ vpbe_disp_calculate_scale_factor(disp_dev, layer,
+ rect->width,
+ rect->height);
+ vpbe_disp_adj_position(disp_dev, layer, rect->top,
+ rect->left);
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set layer config:\n");
+ return -EINVAL;
+ }
+
+ /* apply zooming and h or v expansion */
+ osd_device->ops.set_zoom(osd_device,
+ layer->layer_info.id,
+ layer->layer_info.h_zoom,
+ layer->layer_info.v_zoom);
+ ret = osd_device->ops.set_vid_expansion(osd_device,
+ layer->layer_info.h_exp,
+ layer->layer_info.v_exp);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in set vid expansion:\n");
+ return -EINVAL;
+ }
+
+ if ((layer->layer_info.h_zoom != ZOOM_X1) ||
+ (layer->layer_info.v_zoom != ZOOM_X1) ||
+ (layer->layer_info.h_exp != H_EXP_OFF) ||
+ (layer->layer_info.v_exp != V_EXP_OFF))
+ /* Enable expansion filter */
+ osd_device->ops.set_interpolation_filter(osd_device, 1);
+ else
+ osd_device->ops.set_interpolation_filter(osd_device, 0);
+
+ return 0;
+}
+
+static int vpbe_display_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ struct v4l2_rect *rect = &crop->c;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_CROP, layer id = %d\n",
+ layer->device_id);
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
+ ret = -EINVAL;
+ }
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ rect->top = cfg->ypos;
+ rect->left = cfg->xpos;
+ rect->width = cfg->xsize;
+ rect->height = cfg->ysize;
+
+ return 0;
+}
+
+static int vpbe_display_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cropcap)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
+
+ cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ cropcap->bounds.left = 0;
+ cropcap->bounds.top = 0;
+ cropcap->bounds.width = vpbe_dev->current_timings.xres;
+ cropcap->bounds.height = vpbe_dev->current_timings.yres;
+ cropcap->pixelaspect = vpbe_dev->current_timings.aspect;
+ cropcap->defrect = cropcap->bounds;
+ return 0;
+}
+
+static int vpbe_display_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_G_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If buffer type is video output */
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Fill in the information about format */
+ fmt->fmt.pix = layer->pix_fmt;
+
+ return 0;
+}
+
+static int vpbe_display_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int index = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_ENUM_FMT, layer id = %d\n",
+ layer->device_id);
+ if (fmt->index > 1) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ index = fmt->index;
+ memset(fmt, 0, sizeof(*fmt));
+ fmt->index = index;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ if (index == 0) {
+ strcpy(fmt->description, "YUV 4:2:2 - UYVY");
+ fmt->pixelformat = V4L2_PIX_FMT_UYVY;
+ } else {
+ strcpy(fmt->description, "Y/CbCr 4:2:0");
+ fmt->pixelformat = V4L2_PIX_FMT_NV12;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_S_FMT, layer id = %d\n",
+ layer->device_id);
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+ /* Check for valid pixel format */
+ ret = vpbe_try_format(disp_dev, pixfmt, 1);
+ if (ret)
+ return ret;
+
+ /* YUV420 is requested, check availability of the
+ other video window */
+
+ layer->pix_fmt = *pixfmt;
+
+ /* Get osd layer config */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ /* Store the pixel format in the layer object */
+ cfg->xsize = pixfmt->width;
+ cfg->ysize = pixfmt->height;
+ cfg->line_length = pixfmt->bytesperline;
+ cfg->ypos = 0;
+ cfg->xpos = 0;
+ cfg->interlaced = vpbe_dev->current_timings.interlaced;
+
+ if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
+ cfg->pixfmt = PIXFMT_YCbCrI;
+
+ /* Change of the default pixel format for both video windows */
+ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
+ struct vpbe_layer *otherlayer;
+ cfg->pixfmt = PIXFMT_NV12;
+ otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
+ layer);
+ otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
+ }
+
+ /* Set the layer config in the osd window */
+ ret = osd_device->ops.set_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+ if (ret < 0) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Error in S_FMT params:\n");
+ return -EINVAL;
+ }
+
+ /* Readback and fill the local copy of current pix format */
+ osd_device->ops.get_layer_config(osd_device,
+ layer->layer_info.id, cfg);
+
+ return 0;
+}
+
+static int vpbe_display_try_fmt(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
+ return -EINVAL;
+ }
+
+ /* Check for valid field format */
+ return vpbe_try_format(disp_dev, pixfmt, 0);
+
+}
+
+/**
+ * vpbe_display_s_std - Set the given standard in the encoder
+ *
+ * Sets the standard if supported by the current encoder. Return the status.
+ * 0 - success & -EINVAL on error
+ */
+static int vpbe_display_s_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL != vpbe_dev->ops.s_std) {
+ ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set standard for sub devices\n");
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_std - Get the standard in the current encoder
+ *
+ * Get the standard in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int vpbe_display_g_std(struct file *file, void *priv,
+ v4l2_std_id *std_id)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
+
+ /* Get the standard from the current encoder */
+ if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
+ *std_id = vpbe_dev->current_timings.timings.std_id;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * vpbe_display_enum_output - enumerate outputs
+ *
+ * Enumerates the outputs available at the vpbe display
+ * returns the status, -EINVAL if end of output list
+ */
+static int vpbe_display_enum_output(struct file *file, void *priv,
+ struct v4l2_output *output)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
+
+ /* Enumerate outputs */
+
+ if (NULL == vpbe_dev->ops.enum_outputs)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "Failed to enumerate outputs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_output - Set output to
+ * the output specified by the index
+ */
+static int vpbe_display_s_output(struct file *file, void *priv,
+ unsigned int i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+ if (NULL == vpbe_dev->ops.set_output)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.set_output(vpbe_dev, i);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set output for sub devices\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_output - Get output from subdevice
+ * for a given by the index
+ */
+static int vpbe_display_g_output(struct file *file, void *priv,
+ unsigned int *i)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
+ /* Get the standard from the current encoder */
+ *i = vpbe_dev->current_out_index;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_enum_dv_presets - Enumerate the dv presets
+ *
+ * enum the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_enum_dv_presets(struct file *file, void *priv,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n");
+
+ /* Enumerate outputs */
+ if (NULL == vpbe_dev->ops.enum_dv_presets)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to enumerate dv presets info\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * vpbe_display_s_dv_preset - Set the dv presets
+ *
+ * Set the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_s_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n");
+
+
+ /* If streaming is started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ /* Set the given standard in the encoder */
+ if (!vpbe_dev->ops.s_dv_preset)
+ return -EINVAL;
+
+ ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Failed to set the dv presets info\n");
+ return -EINVAL;
+ }
+ /* set the current norm to zero to be consistent. If STD is used
+ * v4l2 layer will set the norm properly on successful s_std call
+ */
+ layer->video_dev.current_norm = 0;
+
+ return 0;
+}
+
+/**
+ * vpbe_display_g_dv_preset - Set the dv presets
+ *
+ * Get the preset in the current encoder. Return the status. 0 - success
+ * -EINVAL on error
+ */
+static int
+vpbe_display_g_dv_preset(struct file *file, void *priv,
+ struct v4l2_dv_preset *dv_preset)
+{
+ struct vpbe_fh *fh = priv;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n");
+
+ /* Get the given standard in the encoder */
+
+ if (vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_DV_PRESET) {
+ dv_preset->preset =
+ vpbe_dev->current_timings.timings.dv_preset;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpbe_display_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = fh->disp_dev->osd_device;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_STREAMOFF,layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
+ " id = %d\n", layer->device_id);
+ return -EINVAL;
+ }
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ ret = videobuf_streamoff(&layer->buffer_queue);
+
+ return ret;
+}
+
+static int vpbe_display_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int ret;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ /* If Streaming is already started, return error */
+ if (layer->started) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Call videobuf_streamon to start streaming
+ * in videobuf
+ */
+ ret = videobuf_streamon(&layer->buffer_queue);
+ if (ret) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "error in videobuf_streamon\n");
+ return ret;
+ }
+ /* If buffer queue is empty, return error */
+ if (list_empty(&layer->dma_queue)) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
+ goto streamoff;
+ }
+ /* Get the next frame from the buffer queue */
+ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove buffer from the buffer queue */
+ list_del(&layer->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ layer->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ layer->field_id = 0;
+
+ /* Set parameters in OSD and VENC */
+ ret = vpbe_set_osd_display_params(disp_dev, layer);
+ if (ret < 0)
+ goto streamoff;
+
+ /*
+ * if request format is yuv420 semiplanar, need to
+ * enable both video windows
+ */
+ layer->started = 1;
+
+ layer->layer_first_int = 1;
+
+ return ret;
+streamoff:
+ ret = videobuf_streamoff(&layer->buffer_queue);
+ return ret;
+}
+
+static int vpbe_display_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_DQBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+ if (file->f_flags & O_NONBLOCK)
+ /* Call videobuf_dqbuf for non blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
+ else
+ /* Call videobuf_dqbuf for blocking mode */
+ ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
+
+ return ret;
+}
+
+static int vpbe_display_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If this file handle is not allowed to do IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
+ return -EACCES;
+ }
+
+ return videobuf_qbuf(&layer->buffer_queue, p);
+}
+
+static int vpbe_display_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "VIDIOC_QUERYBUF, layer id = %d\n",
+ layer->device_id);
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* Call videobuf_querybuf to get information */
+ ret = videobuf_querybuf(&layer->buffer_queue, buf);
+
+ return ret;
+}
+
+static int vpbe_display_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ /* If io users of the layer is not zero, return error */
+ if (0 != layer->io_usrs) {
+ v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
+ return -EBUSY;
+ }
+ /* Initialize videobuf queue as per the buffer type */
+ videobuf_queue_dma_contig_init(&layer->buffer_queue,
+ &video_qops,
+ vpbe_dev->pdev,
+ &layer->irqlock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ layer->pix_fmt.field,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed = 1;
+ /* Increment io usrs member of layer object to 1 */
+ layer->io_usrs = 1;
+ /* Store type of memory requested in layer object */
+ layer->memory = req_buf->memory;
+ /* Initialize buffer queue */
+ INIT_LIST_HEAD(&layer->dma_queue);
+ /* Allocate buffers */
+ ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
+
+ return ret;
+}
+
+/*
+ * vpbe_display_mmap()
+ * It is used to map kernel space buffers into user spaces
+ */
+static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
+
+ if (mutex_lock_interruptible(&layer->opslock))
+ return -ERESTARTSYS;
+ ret = videobuf_mmap_mapper(&layer->buffer_queue, vma);
+ mutex_unlock(&layer->opslock);
+ return ret;
+}
+
+/* vpbe_display_poll(): It is used for select/poll system call
+ */
+static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
+{
+ struct vpbe_fh *fh = filep->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+ unsigned int err = 0;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
+ if (layer->started) {
+ mutex_lock(&layer->opslock);
+ err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
+ mutex_unlock(&layer->opslock);
+ }
+ return err;
+}
+
+/*
+ * vpbe_display_open()
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpbe_display_open(struct file *file)
+{
+ struct vpbe_fh *fh = NULL;
+ struct vpbe_layer *layer = video_drvdata(file);
+ struct vpbe_display *disp_dev = layer->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+ int err;
+
+ /* Allocate memory for the file handle object */
+ fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
+ if (fh == NULL) {
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display open plane = %d\n",
+ layer->device_id);
+
+ /* store pointer to fh in private_data member of filep */
+ file->private_data = fh;
+ fh->layer = layer;
+ fh->disp_dev = disp_dev;
+
+ if (!layer->usrs) {
+ if (mutex_lock_interruptible(&layer->opslock))
+ return -ERESTARTSYS;
+ /* First claim the layer for this device */
+ err = osd_device->ops.request_layer(osd_device,
+ layer->layer_info.id);
+ mutex_unlock(&layer->opslock);
+ if (err < 0) {
+ /* Couldn't get layer */
+ v4l2_err(&vpbe_dev->v4l2_dev,
+ "Display Manager failed to allocate layer\n");
+ kfree(fh);
+ return -EINVAL;
+ }
+ }
+ /* Increment layer usrs counter */
+ layer->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&layer->prio, &fh->prio);
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
+ "vpbe display device opened successfully\n");
+ return 0;
+}
+
+/*
+ * vpbe_display_release()
+ * This function deletes buffer queue, frees the buffers and the davinci
+ * display file * handle
+ */
+static int vpbe_display_release(struct file *file)
+{
+ /* Get the layer object and file handle object */
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_layer *layer = fh->layer;
+ struct osd_layer_config *cfg = &layer->layer_info.config;
+ struct vpbe_display *disp_dev = fh->disp_dev;
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct osd_state *osd_device = disp_dev->osd_device;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
+
+ mutex_lock(&layer->opslock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ /* Reset io_usrs member of layer object */
+ layer->io_usrs = 0;
+
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ layer->started = 0;
+ /* Free buffers allocated */
+ videobuf_queue_cancel(&layer->buffer_queue);
+ videobuf_mmap_free(&layer->buffer_queue);
+ }
+
+ /* Decrement layer usrs counter */
+ layer->usrs--;
+ /* If this file handle has initialize encoder device, reset it */
+ if (!layer->usrs) {
+ if (cfg->pixfmt == PIXFMT_NV12) {
+ struct vpbe_layer *otherlayer;
+ otherlayer =
+ _vpbe_display_get_other_win_layer(disp_dev, layer);
+ osd_device->ops.disable_layer(osd_device,
+ otherlayer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ otherlayer->layer_info.id);
+ }
+ osd_device->ops.disable_layer(osd_device,
+ layer->layer_info.id);
+ osd_device->ops.release_layer(osd_device,
+ layer->layer_info.id);
+ }
+ /* Close the priority */
+ v4l2_prio_close(&layer->prio, fh->prio);
+ file->private_data = NULL;
+ mutex_unlock(&layer->opslock);
+
+ /* Free memory allocated to file handle object */
+ kfree(fh);
+
+ disp_dev->cbcr_ofst = 0;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vpbe_display_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct v4l2_dbg_match *match = &reg->match;
+ struct vpbe_fh *fh = file->private_data;
+ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
+
+ if (match->type >= 2) {
+ v4l2_subdev_call(vpbe_dev->venc,
+ core,
+ g_register,
+ reg);
+ }
+
+ return 0;
+}
+
+static int vpbe_display_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ return 0;
+}
+#endif
+
+/* vpbe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
+ .vidioc_querycap = vpbe_display_querycap,
+ .vidioc_g_fmt_vid_out = vpbe_display_g_fmt,
+ .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
+ .vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
+ .vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
+ .vidioc_reqbufs = vpbe_display_reqbufs,
+ .vidioc_querybuf = vpbe_display_querybuf,
+ .vidioc_qbuf = vpbe_display_qbuf,
+ .vidioc_dqbuf = vpbe_display_dqbuf,
+ .vidioc_streamon = vpbe_display_streamon,
+ .vidioc_streamoff = vpbe_display_streamoff,
+ .vidioc_cropcap = vpbe_display_cropcap,
+ .vidioc_g_crop = vpbe_display_g_crop,
+ .vidioc_s_crop = vpbe_display_s_crop,
+ .vidioc_g_priority = vpbe_display_g_priority,
+ .vidioc_s_priority = vpbe_display_s_priority,
+ .vidioc_s_std = vpbe_display_s_std,
+ .vidioc_g_std = vpbe_display_g_std,
+ .vidioc_enum_output = vpbe_display_enum_output,
+ .vidioc_s_output = vpbe_display_s_output,
+ .vidioc_g_output = vpbe_display_g_output,
+ .vidioc_s_dv_preset = vpbe_display_s_dv_preset,
+ .vidioc_g_dv_preset = vpbe_display_g_dv_preset,
+ .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpbe_display_g_register,
+ .vidioc_s_register = vpbe_display_s_register,
+#endif
+};
+
+static struct v4l2_file_operations vpbe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpbe_display_open,
+ .release = vpbe_display_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpbe_display_mmap,
+ .poll = vpbe_display_poll
+};
+
+static int vpbe_device_get(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vpbe_display *vpbe_disp = data;
+
+ if (strcmp("vpbe_controller", pdev->name) == 0)
+ vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
+
+ if (strcmp("vpbe-osd", pdev->name) == 0)
+ vpbe_disp->osd_device = platform_get_drvdata(pdev);
+
+ return 0;
+}
+
+static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
+ struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer = NULL;
+ struct video_device *vbd = NULL;
+
+ /* Allocate memory for four plane display objects */
+
+ disp_dev->dev[i] =
+ kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL);
+
+ /* If memory allocation fails, return error */
+ if (!disp_dev->dev[i]) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+ spin_lock_init(&disp_dev->dev[i]->irqlock);
+ mutex_init(&disp_dev->dev[i]->opslock);
+
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ vbd = &vpbe_display_layer->video_dev;
+ /* Initialize field of video device */
+ vbd->release = video_device_release_empty;
+ vbd->fops = &vpbe_fops;
+ vbd->ioctl_ops = &vpbe_ioctl_ops;
+ vbd->minor = -1;
+ vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
+ vbd->lock = &vpbe_display_layer->opslock;
+ vbd->vfl_dir = VFL_DIR_TX;
+
+ if (disp_dev->vpbe_dev->current_timings.timings_type &
+ VPBE_ENC_STD) {
+ vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
+ vbd->current_norm =
+ disp_dev->vpbe_dev->
+ current_timings.timings.std_id;
+ } else
+ vbd->current_norm = 0;
+
+ snprintf(vbd->name, sizeof(vbd->name),
+ "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
+ (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
+ (VPBE_DISPLAY_VERSION_CODE) & 0xff);
+
+ vpbe_display_layer->device_id = i;
+
+ vpbe_display_layer->layer_info.id =
+ ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
+
+ /* Initialize prio member of layer object */
+ v4l2_prio_init(&vpbe_display_layer->prio);
+
+ return 0;
+}
+
+static __devinit int register_device(struct vpbe_layer *vpbe_display_layer,
+ struct vpbe_display *disp_dev,
+ struct platform_device *pdev) {
+ int err;
+
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "Trying to register VPBE display device.\n");
+ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
+ "layer=%x,layer->video_dev=%x\n",
+ (int)vpbe_display_layer,
+ (int)&vpbe_display_layer->video_dev);
+
+ err = video_register_device(&vpbe_display_layer->video_dev,
+ VFL_TYPE_GRABBER,
+ -1);
+ if (err)
+ return -ENODEV;
+
+ vpbe_display_layer->disp_dev = disp_dev;
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, disp_dev);
+ video_set_drvdata(&vpbe_display_layer->video_dev,
+ vpbe_display_layer);
+
+ return 0;
+}
+
+
+
+/*
+ * vpbe_display_probe()
+ * This function creates device entries by register itself to the V4L2 driver
+ * and initializes fields of each layer objects
+ */
+static __devinit int vpbe_display_probe(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev;
+ struct resource *res = NULL;
+ int k;
+ int i;
+ int err;
+ int irq;
+
+ printk(KERN_DEBUG "vpbe_display_probe\n");
+ /* Allocate memory for vpbe_display */
+ disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
+ if (!disp_dev) {
+ printk(KERN_ERR "ran out of memory\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&disp_dev->dma_queue_lock);
+ /*
+ * Scan all the platform devices to find the vpbe
+ * controller device and get the vpbe_dev object
+ */
+ err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
+ vpbe_device_get);
+ if (err < 0)
+ return err;
+ /* Initialize the vpbe display controller */
+ if (NULL != disp_dev->vpbe_dev->ops.initialize) {
+ err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
+ disp_dev->vpbe_dev);
+ if (err) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Error initing vpbe\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (init_vpbe_layer(i, disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to get VENC interrupt resource\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ irq = res->start;
+ if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
+ disp_dev)) {
+ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
+ "Unable to request interrupt\n");
+ err = -ENODEV;
+ goto probe_out;
+ }
+
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
+ err = -ENODEV;
+ goto probe_out_irq;
+ }
+ }
+
+ printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
+ return 0;
+
+probe_out_irq:
+ free_irq(res->start, disp_dev);
+probe_out:
+ for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[k];
+ /* Unregister video device */
+ if (vpbe_display_layer) {
+ video_unregister_device(
+ &vpbe_display_layer->video_dev);
+ kfree(disp_dev->dev[k]);
+ }
+ }
+ kfree(disp_dev);
+ return err;
+}
+
+/*
+ * vpbe_display_remove()
+ * It un-register hardware layer from V4L2 driver
+ */
+static int vpbe_display_remove(struct platform_device *pdev)
+{
+ struct vpbe_layer *vpbe_display_layer;
+ struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
+ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
+ struct resource *res;
+ int i;
+
+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
+
+ /* unregister irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ free_irq(res->start, disp_dev);
+
+ /* deinitialize the vpbe display controller */
+ if (NULL != vpbe_dev->ops.deinitialize)
+ vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
+ /* un-register device */
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the layer object */
+ vpbe_display_layer = disp_dev->dev[i];
+ /* Unregister video device */
+ video_unregister_device(&vpbe_display_layer->video_dev);
+
+ }
+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
+ kfree(disp_dev->dev[i]);
+ disp_dev->dev[i] = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver vpbe_display_driver = {
+ .driver = {
+ .name = VPBE_DISPLAY_DRIVER,
+ .owner = THIS_MODULE,
+ .bus = &platform_bus_type,
+ },
+ .probe = vpbe_display_probe,
+ .remove = __devexit_p(vpbe_display_remove),
+};
+
+module_platform_driver(vpbe_display_driver);
+
+MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index bba299dbf396..bba299dbf396 100644
--- a/drivers/media/video/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/platform/davinci/vpbe_osd_regs.h
index 584520f3af60..584520f3af60 100644
--- a/drivers/media/video/davinci/vpbe_osd_regs.h
+++ b/drivers/media/platform/davinci/vpbe_osd_regs.h
diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c
index b21ecc8d134d..b21ecc8d134d 100644
--- a/drivers/media/video/davinci/vpbe_venc.c
+++ b/drivers/media/platform/davinci/vpbe_venc.c
diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/platform/davinci/vpbe_venc_regs.h
index 947cb1510776..947cb1510776 100644
--- a/drivers/media/video/davinci/vpbe_venc_regs.h
+++ b/drivers/media/platform/davinci/vpbe_venc_regs.h
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
new file mode 100644
index 000000000000..48052cbffc2b
--- /dev/null
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -0,0 +1,2080 @@
+/*
+ * Copyright (C) 2008-2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * Driver name : VPFE Capture driver
+ * VPFE Capture driver allows applications to capture and stream video
+ * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
+ * TVP5146 or Raw Bayer RGB image data from an image sensor
+ * such as Microns' MT9T001, MT9T031 etc.
+ *
+ * These SoCs have, in common, a Video Processing Subsystem (VPSS) that
+ * consists of a Video Processing Front End (VPFE) for capturing
+ * video/raw image data and Video Processing Back End (VPBE) for displaying
+ * YUV data through an in-built analog encoder or Digital LCD port. This
+ * driver is for capture through VPFE. A typical EVM using these SoCs have
+ * following high level configuration.
+ *
+ *
+ * decoder(TVP5146/ YUV/
+ * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
+ * data input | |
+ * V |
+ * SDRAM |
+ * V
+ * Image Processor
+ * |
+ * V
+ * SDRAM
+ * The data flow happens from a decoder connected to the VPFE over a
+ * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
+ * and to the input of VPFE through an optional MUX (if more inputs are
+ * to be interfaced on the EVM). The input data is first passed through
+ * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
+ * does very little or no processing on YUV data and does pre-process Raw
+ * Bayer RGB data through modules such as Defect Pixel Correction (DFC)
+ * Color Space Conversion (CSC), data gain/offset etc. After this, data
+ * can be written to SDRAM or can be connected to the image processing
+ * block such as IPIPE (on DM355 only).
+ *
+ * Features supported
+ * - MMAP IO
+ * - Capture using TVP5146 over BT.656
+ * - support for interfacing decoders using sub device model
+ * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV
+ * data capture to SDRAM.
+ * TODO list
+ * - Support multiple REQBUF after open
+ * - Support for de-allocating buffers through REQBUF
+ * - Support for Raw Bayer RGB capture
+ * - Support for chaining Image Processor
+ * - Support for static allocation of buffers
+ * - Support for USERPTR IO
+ * - Support for STREAMON before QBUF
+ * - Support for control ioctls
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <media/v4l2-common.h>
+#include <linux/io.h>
+#include <media/davinci/vpfe_capture.h>
+#include "ccdc_hw_device.h"
+
+static int debug;
+static u32 numbuffers = 3;
+static u32 bufsize = (720 * 576 * 2);
+
+module_param(numbuffers, uint, S_IRUGO);
+module_param(bufsize, uint, S_IRUGO);
+module_param(debug, int, 0644);
+
+MODULE_PARM_DESC(numbuffers, "buffer count (default:3)");
+MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)");
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+
+MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+
+/* standard information */
+struct vpfe_standard {
+ v4l2_std_id std_id;
+ unsigned int width;
+ unsigned int height;
+ struct v4l2_fract pixelaspect;
+ /* 0 - progressive, 1 - interlaced */
+ int frame_format;
+};
+
+/* ccdc configuration */
+struct ccdc_config {
+ /* This make sure vpfe is probed and ready to go */
+ int vpfe_probed;
+ /* name of ccdc device */
+ char name[32];
+};
+
+/* data structures */
+static struct vpfe_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers = 3,
+ .min_bufsize = 720 * 480 * 2,
+ .device_bufsize = 720 * 576 * 2,
+};
+
+/* ccdc device registered */
+static struct ccdc_hw_device *ccdc_dev;
+/* lock for accessing ccdc information */
+static DEFINE_MUTEX(ccdc_lock);
+/* ccdc configuration */
+static struct ccdc_config *ccdc_cfg;
+
+const struct vpfe_standard vpfe_standards[] = {
+ {V4L2_STD_525_60, 720, 480, {11, 10}, 1},
+ {V4L2_STD_625_50, 720, 576, {54, 59}, 1},
+};
+
+/* Used when raw Bayer image from ccdc is directly captured to SDRAM */
+static const struct vpfe_pixel_format vpfe_pix_fmts[] = {
+ {
+ .fmtdesc = {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb 8bit A-Law compr.",
+ .pixelformat = V4L2_PIX_FMT_SBGGR8,
+ },
+ .bpp = 1,
+ },
+ {
+ .fmtdesc = {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb - 16bit",
+ .pixelformat = V4L2_PIX_FMT_SBGGR16,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Bayer GrRBGb 8bit DPCM compr.",
+ .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
+ },
+ .bpp = 1,
+ },
+ {
+ .fmtdesc = {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 4,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "YCbCr 4:2:2 Interleaved YUYV",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ },
+ .bpp = 2,
+ },
+ {
+ .fmtdesc = {
+ .index = 5,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .description = "Y/CbCr 4:2:0 - Semi planar",
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ },
+ .bpp = 1,
+ },
+};
+
+/*
+ * vpfe_lookup_pix_format()
+ * lookup an entry in the vpfe pix format table based on pix_format
+ */
+static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) {
+ if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat)
+ return &vpfe_pix_fmts[i];
+ }
+ return NULL;
+}
+
+/*
+ * vpfe_register_ccdc_device. CCDC module calls this to
+ * register with vpfe capture
+ */
+int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
+{
+ int ret = 0;
+ printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name);
+
+ BUG_ON(!dev->hw_ops.open);
+ BUG_ON(!dev->hw_ops.enable);
+ BUG_ON(!dev->hw_ops.set_hw_if_params);
+ BUG_ON(!dev->hw_ops.configure);
+ BUG_ON(!dev->hw_ops.set_buftype);
+ BUG_ON(!dev->hw_ops.get_buftype);
+ BUG_ON(!dev->hw_ops.enum_pix);
+ BUG_ON(!dev->hw_ops.set_frame_format);
+ BUG_ON(!dev->hw_ops.get_frame_format);
+ BUG_ON(!dev->hw_ops.get_pixel_format);
+ BUG_ON(!dev->hw_ops.set_pixel_format);
+ BUG_ON(!dev->hw_ops.set_image_window);
+ BUG_ON(!dev->hw_ops.get_image_window);
+ BUG_ON(!dev->hw_ops.get_line_length);
+ BUG_ON(!dev->hw_ops.getfid);
+
+ mutex_lock(&ccdc_lock);
+ if (NULL == ccdc_cfg) {
+ /*
+ * TODO. Will this ever happen? if so, we need to fix it.
+ * Proabably we need to add the request to a linked list and
+ * walk through it during vpfe probe
+ */
+ printk(KERN_ERR "vpfe capture not initialized\n");
+ ret = -EFAULT;
+ goto unlock;
+ }
+
+ if (strcmp(dev->name, ccdc_cfg->name)) {
+ /* ignore this ccdc */
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (ccdc_dev) {
+ printk(KERN_ERR "ccdc already registered\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ccdc_dev = dev;
+unlock:
+ mutex_unlock(&ccdc_lock);
+ return ret;
+}
+EXPORT_SYMBOL(vpfe_register_ccdc_device);
+
+/*
+ * vpfe_unregister_ccdc_device. CCDC module calls this to
+ * unregister with vpfe capture
+ */
+void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev)
+{
+ if (NULL == dev) {
+ printk(KERN_ERR "invalid ccdc device ptr\n");
+ return;
+ }
+
+ printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n",
+ dev->name);
+
+ if (strcmp(dev->name, ccdc_cfg->name)) {
+ /* ignore this ccdc */
+ return;
+ }
+
+ mutex_lock(&ccdc_lock);
+ ccdc_dev = NULL;
+ mutex_unlock(&ccdc_lock);
+ return;
+}
+EXPORT_SYMBOL(vpfe_unregister_ccdc_device);
+
+/*
+ * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
+ */
+static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev,
+ struct v4l2_format *f)
+{
+ struct v4l2_rect image_win;
+ enum ccdc_buftype buf_type;
+ enum ccdc_frmfmt frm_fmt;
+
+ memset(f, 0, sizeof(*f));
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ccdc_dev->hw_ops.get_image_window(&image_win);
+ f->fmt.pix.width = image_win.width;
+ f->fmt.pix.height = image_win.height;
+ f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length();
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height;
+ buf_type = ccdc_dev->hw_ops.get_buftype();
+ f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format();
+ frm_fmt = ccdc_dev->hw_ops.get_frame_format();
+ if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
+ if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED)
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+ else {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n");
+ return -EINVAL;
+ }
+ } else {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * vpfe_config_ccdc_image_format()
+ * For a pix format, configure ccdc to setup the capture
+ */
+static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
+ int ret = 0;
+
+ if (ccdc_dev->hw_ops.set_pixel_format(
+ vpfe_dev->fmt.fmt.pix.pixelformat) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "couldn't set pix format in ccdc\n");
+ return -EINVAL;
+ }
+ /* configure the image window */
+ ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop);
+
+ switch (vpfe_dev->fmt.fmt.pix.field) {
+ case V4L2_FIELD_INTERLACED:
+ /* do nothing, since it is default */
+ ret = ccdc_dev->hw_ops.set_buftype(
+ CCDC_BUFTYPE_FLD_INTERLEAVED);
+ break;
+ case V4L2_FIELD_NONE:
+ frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
+ /* buffer type only applicable for interlaced scan */
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ ret = ccdc_dev->hw_ops.set_buftype(
+ CCDC_BUFTYPE_FLD_SEPARATED);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set the frame format */
+ if (!ret)
+ ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt);
+ return ret;
+}
+/*
+ * vpfe_config_image_format()
+ * For a given standard, this functions sets up the default
+ * pix format & crop values in the vpfe device and ccdc. It first
+ * starts with defaults based values from the standard table.
+ * It then checks if sub device support g_mbus_fmt and then override the
+ * values based on that.Sets crop values to match with scan resolution
+ * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
+ * values in ccdc
+ */
+static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
+ const v4l2_std_id *std_id)
+{
+ struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix;
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
+ if (vpfe_standards[i].std_id & *std_id) {
+ vpfe_dev->std_info.active_pixels =
+ vpfe_standards[i].width;
+ vpfe_dev->std_info.active_lines =
+ vpfe_standards[i].height;
+ vpfe_dev->std_info.frame_format =
+ vpfe_standards[i].frame_format;
+ vpfe_dev->std_index = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(vpfe_standards)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n");
+ return -EINVAL;
+ }
+
+ vpfe_dev->crop.top = 0;
+ vpfe_dev->crop.left = 0;
+ vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels;
+ vpfe_dev->crop.height = vpfe_dev->std_info.active_lines;
+ pix->width = vpfe_dev->crop.width;
+ pix->height = vpfe_dev->crop.height;
+
+ /* first field and frame format based on standard frame format */
+ if (vpfe_dev->std_info.frame_format) {
+ pix->field = V4L2_FIELD_INTERLACED;
+ /* assume V4L2_PIX_FMT_UYVY as default */
+ pix->pixelformat = V4L2_PIX_FMT_UYVY;
+ v4l2_fill_mbus_format(&mbus_fmt, pix,
+ V4L2_MBUS_FMT_YUYV10_2X10);
+ } else {
+ pix->field = V4L2_FIELD_NONE;
+ /* assume V4L2_PIX_FMT_SBGGR8 */
+ pix->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ v4l2_fill_mbus_format(&mbus_fmt, pix,
+ V4L2_MBUS_FMT_SBGGR8_1X8);
+ }
+
+ /* if sub device supports g_mbus_fmt, override the defaults */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt);
+
+ if (ret && ret != -ENOIOCTLCMD) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "error in getting g_mbus_fmt from sub device\n");
+ return ret;
+ }
+ v4l2_fill_pix_format(pix, &mbus_fmt);
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+
+ /* Sets the values in CCDC */
+ ret = vpfe_config_ccdc_image_format(vpfe_dev);
+ if (ret)
+ return ret;
+
+ /* Update the values of sizeimage and bytesperline */
+ if (!ret) {
+ pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
+ pix->sizeimage = pix->bytesperline * pix->height;
+ }
+ return ret;
+}
+
+static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
+{
+ int ret = 0;
+
+ /* set first input of current subdevice as the current input */
+ vpfe_dev->current_input = 0;
+
+ /* set default standard */
+ vpfe_dev->std_index = 0;
+
+ /* Configure the default format information */
+ ret = vpfe_config_image_format(vpfe_dev,
+ &vpfe_standards[vpfe_dev->std_index].std_id);
+ if (ret)
+ return ret;
+
+ /* now open the ccdc device to initialize it */
+ mutex_lock(&ccdc_lock);
+ if (NULL == ccdc_dev) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n");
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ if (!try_module_get(ccdc_dev->owner)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n");
+ ret = -ENODEV;
+ goto unlock;
+ }
+ ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev);
+ if (!ret)
+ vpfe_dev->initialized = 1;
+
+ /* Clear all VPFE/CCDC interrupts */
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(-1);
+
+unlock:
+ mutex_unlock(&ccdc_lock);
+ return ret;
+}
+
+/*
+ * vpfe_open : It creates object of file handle structure and
+ * stores it in private_data member of filepointer
+ */
+static int vpfe_open(struct file *file)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n");
+
+ if (!vpfe_dev->cfg->num_subdevs) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n");
+ return -ENODEV;
+ }
+
+ /* Allocate memory for the file handle object */
+ fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+ /* store pointer to fh in private_data member of file */
+ file->private_data = fh;
+ fh->vpfe_dev = vpfe_dev;
+ mutex_lock(&vpfe_dev->lock);
+ /* If decoder is not initialized. initialize it */
+ if (!vpfe_dev->initialized) {
+ if (vpfe_initialize_device(vpfe_dev)) {
+ mutex_unlock(&vpfe_dev->lock);
+ return -ENODEV;
+ }
+ }
+ /* Increment device usrs counter */
+ vpfe_dev->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&vpfe_dev->prio, &fh->prio);
+ mutex_unlock(&vpfe_dev->lock);
+ return 0;
+}
+
+static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev)
+{
+ unsigned long addr;
+
+ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
+ struct videobuf_buffer, queue);
+ list_del(&vpfe_dev->next_frm->queue);
+ vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE;
+ addr = videobuf_to_dma_contig(vpfe_dev->next_frm);
+
+ ccdc_dev->hw_ops.setfbaddr(addr);
+}
+
+static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev)
+{
+ unsigned long addr;
+
+ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
+ addr += vpfe_dev->field_off;
+ ccdc_dev->hw_ops.setfbaddr(addr);
+}
+
+static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev)
+{
+ struct timeval timevalue;
+
+ do_gettimeofday(&timevalue);
+ vpfe_dev->cur_frm->ts = timevalue;
+ vpfe_dev->cur_frm->state = VIDEOBUF_DONE;
+ vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ wake_up_interruptible(&vpfe_dev->cur_frm->done);
+ vpfe_dev->cur_frm = vpfe_dev->next_frm;
+}
+
+/* ISR for VINT0*/
+static irqreturn_t vpfe_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+ enum v4l2_field field;
+ int fid;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n");
+ field = vpfe_dev->fmt.fmt.pix.field;
+
+ /* if streaming not started, don't do anything */
+ if (!vpfe_dev->started)
+ goto clear_intr;
+
+ /* only for 6446 this will be applicable */
+ if (NULL != ccdc_dev->hw_ops.reset)
+ ccdc_dev->hw_ops.reset();
+
+ if (field == V4L2_FIELD_NONE) {
+ /* handle progressive frame capture */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "frame format is progressive...\n");
+ if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
+ vpfe_process_buffer_complete(vpfe_dev);
+ goto clear_intr;
+ }
+
+ /* interlaced or TB capture check which field we are in hardware */
+ fid = ccdc_dev->hw_ops.getfid();
+
+ /* switch the software maintained field id */
+ vpfe_dev->field_id ^= 1;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n",
+ fid, vpfe_dev->field_id);
+ if (fid == vpfe_dev->field_id) {
+ /* we are in-sync here,continue */
+ if (fid == 0) {
+ /*
+ * One frame is just being captured. If the next frame
+ * is available, release the current frame and move on
+ */
+ if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
+ vpfe_process_buffer_complete(vpfe_dev);
+ /*
+ * based on whether the two fields are stored
+ * interleavely or separately in memory, reconfigure
+ * the CCDC memory address
+ */
+ if (field == V4L2_FIELD_SEQ_TB) {
+ vpfe_schedule_bottom_field(vpfe_dev);
+ }
+ goto clear_intr;
+ }
+ /*
+ * if one field is just being captured configure
+ * the next frame get the next frame from the empty
+ * queue if no frame is available hold on to the
+ * current buffer
+ */
+ spin_lock(&vpfe_dev->dma_queue_lock);
+ if (!list_empty(&vpfe_dev->dma_queue) &&
+ vpfe_dev->cur_frm == vpfe_dev->next_frm)
+ vpfe_schedule_next_buffer(vpfe_dev);
+ spin_unlock(&vpfe_dev->dma_queue_lock);
+ } else if (fid == 0) {
+ /*
+ * out of sync. Recover from any hardware out-of-sync.
+ * May loose one frame
+ */
+ vpfe_dev->field_id = fid;
+ }
+clear_intr:
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+
+ return IRQ_HANDLED;
+}
+
+/* vdint1_isr - isr handler for VINT1 interrupt */
+static irqreturn_t vdint1_isr(int irq, void *dev_id)
+{
+ struct vpfe_device *vpfe_dev = dev_id;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n");
+
+ /* if streaming not started, don't do anything */
+ if (!vpfe_dev->started) {
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&vpfe_dev->dma_queue_lock);
+ if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) &&
+ !list_empty(&vpfe_dev->dma_queue) &&
+ vpfe_dev->cur_frm == vpfe_dev->next_frm)
+ vpfe_schedule_next_buffer(vpfe_dev);
+ spin_unlock(&vpfe_dev->dma_queue_lock);
+
+ if (vpfe_dev->cfg->clr_intr)
+ vpfe_dev->cfg->clr_intr(irq);
+
+ return IRQ_HANDLED;
+}
+
+static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frame_format;
+
+ frame_format = ccdc_dev->hw_ops.get_frame_format();
+ if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
+ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
+}
+
+static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
+{
+ enum ccdc_frmfmt frame_format;
+
+ frame_format = ccdc_dev->hw_ops.get_frame_format();
+ if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {
+ return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr,
+ IRQF_DISABLED, "vpfe_capture1",
+ vpfe_dev);
+ }
+ return 0;
+}
+
+/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */
+static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev)
+{
+ vpfe_dev->started = 0;
+ ccdc_dev->hw_ops.enable(0);
+ if (ccdc_dev->hw_ops.enable_out_to_sdram)
+ ccdc_dev->hw_ops.enable_out_to_sdram(0);
+}
+
+/*
+ * vpfe_release : This function deletes buffer queue, frees the
+ * buffers and the vpfe file handle
+ */
+static int vpfe_release(struct file *file)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n");
+
+ /* Get the device lock */
+ mutex_lock(&vpfe_dev->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed) {
+ if (vpfe_dev->started) {
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
+ sdinfo->grp_id,
+ video, s_stream, 0);
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "stream off failed in subdev\n");
+ vpfe_stop_ccdc_capture(vpfe_dev);
+ vpfe_detach_irq(vpfe_dev);
+ videobuf_streamoff(&vpfe_dev->buffer_queue);
+ }
+ vpfe_dev->io_usrs = 0;
+ vpfe_dev->numbuffers = config_params.numbuffers;
+ }
+
+ /* Decrement device usrs counter */
+ vpfe_dev->usrs--;
+ /* Close the priority */
+ v4l2_prio_close(&vpfe_dev->prio, fh->prio);
+ /* If this is the last file handle */
+ if (!vpfe_dev->usrs) {
+ vpfe_dev->initialized = 0;
+ if (ccdc_dev->hw_ops.close)
+ ccdc_dev->hw_ops.close(vpfe_dev->pdev);
+ module_put(ccdc_dev->owner);
+ }
+ mutex_unlock(&vpfe_dev->lock);
+ file->private_data = NULL;
+ /* Free memory allocated to file handle object */
+ kfree(fh);
+ return 0;
+}
+
+/*
+ * vpfe_mmap : It is used to map kernel space buffers
+ * into user spaces
+ */
+static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /* Get the device object and file handle object */
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n");
+
+ return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma);
+}
+
+/*
+ * vpfe_poll: It is used for select/poll system call
+ */
+static unsigned int vpfe_poll(struct file *file, poll_table *wait)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n");
+
+ if (vpfe_dev->started)
+ return videobuf_poll_stream(file,
+ &vpfe_dev->buffer_queue, wait);
+ return 0;
+}
+
+/* vpfe capture driver file operations */
+static const struct v4l2_file_operations vpfe_fops = {
+ .owner = THIS_MODULE,
+ .open = vpfe_open,
+ .release = vpfe_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpfe_mmap,
+ .poll = vpfe_poll
+};
+
+/*
+ * vpfe_check_format()
+ * This function adjust the input pixel format as per hardware
+ * capabilities and update the same in pixfmt.
+ * Following algorithm used :-
+ *
+ * If given pixformat is not in the vpfe list of pix formats or not
+ * supported by the hardware, current value of pixformat in the device
+ * is used
+ * If given field is not supported, then current field is used. If field
+ * is different from current, then it is matched with that from sub device.
+ * Minimum height is 2 lines for interlaced or tb field and 1 line for
+ * progressive. Maximum height is clamped to active active lines of scan
+ * Minimum width is 32 bytes in memory and width is clamped to active
+ * pixels of scan.
+ * bytesperline is a multiple of 32.
+ */
+static const struct vpfe_pixel_format *
+ vpfe_check_format(struct vpfe_device *vpfe_dev,
+ struct v4l2_pix_format *pixfmt)
+{
+ u32 min_height = 1, min_width = 32, max_width, max_height;
+ const struct vpfe_pixel_format *vpfe_pix_fmt;
+ u32 pix;
+ int temp, found;
+
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ if (NULL == vpfe_pix_fmt) {
+ /*
+ * use current pixel format in the vpfe device. We
+ * will find this pix format in the table
+ */
+ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ }
+
+ /* check if hw supports it */
+ temp = 0;
+ found = 0;
+ while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) {
+ if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) {
+ found = 1;
+ break;
+ }
+ temp++;
+ }
+
+ if (!found) {
+ /* use current pixel format */
+ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
+ /*
+ * Since this is currently used in the vpfe device, we
+ * will find this pix format in the table
+ */
+ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
+ }
+
+ /* check what field format is supported */
+ if (pixfmt->field == V4L2_FIELD_ANY) {
+ /* if field is any, use current value as default */
+ pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
+ }
+
+ /*
+ * if field is not same as current field in the vpfe device
+ * try matching the field with the sub device field
+ */
+ if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) {
+ /*
+ * If field value is not in the supported fields, use current
+ * field used in the device as default
+ */
+ switch (pixfmt->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_SEQ_TB:
+ /* if sub device is supporting progressive, use that */
+ if (!vpfe_dev->std_info.frame_format)
+ pixfmt->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ if (vpfe_dev->std_info.frame_format)
+ pixfmt->field = V4L2_FIELD_INTERLACED;
+ break;
+
+ default:
+ /* use current field as default */
+ pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
+ break;
+ }
+ }
+
+ /* Now adjust image resolutions supported */
+ if (pixfmt->field == V4L2_FIELD_INTERLACED ||
+ pixfmt->field == V4L2_FIELD_SEQ_TB)
+ min_height = 2;
+
+ max_width = vpfe_dev->std_info.active_pixels;
+ max_height = vpfe_dev->std_info.active_lines;
+ min_width /= vpfe_pix_fmt->bpp;
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n",
+ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp);
+
+ pixfmt->width = clamp((pixfmt->width), min_width, max_width);
+ pixfmt->height = clamp((pixfmt->height), min_height, max_height);
+
+ /* If interlaced, adjust height to be a multiple of 2 */
+ if (pixfmt->field == V4L2_FIELD_INTERLACED)
+ pixfmt->height &= (~1);
+ /*
+ * recalculate bytesperline and sizeimage since width
+ * and height might have changed
+ */
+ pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31)
+ & ~31);
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
+ pixfmt->sizeimage =
+ pixfmt->bytesperline * pixfmt->height +
+ ((pixfmt->bytesperline * pixfmt->height) >> 1);
+ else
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height ="
+ " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n",
+ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp,
+ pixfmt->bytesperline, pixfmt->sizeimage);
+ return vpfe_pix_fmt;
+}
+
+static int vpfe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
+
+ cap->version = VPFE_CAPTURE_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
+ strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
+ return 0;
+}
+
+static int vpfe_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n");
+ /* Fill in the information about format */
+ *fmt = vpfe_dev->fmt;
+ return ret;
+}
+
+static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmt;
+ int temp_index;
+ u32 pix;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n");
+
+ if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0)
+ return -EINVAL;
+
+ /* Fill in the information about format */
+ pix_fmt = vpfe_lookup_pix_format(pix);
+ if (NULL != pix_fmt) {
+ temp_index = fmt->index;
+ *fmt = pix_fmt->fmtdesc;
+ fmt->index = temp_index;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int vpfe_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmts;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n");
+
+ /* If streaming is started, return error */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ /* Check for valid frame format */
+ pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix);
+
+ if (NULL == pix_fmts)
+ return -EINVAL;
+
+ /* store the pixel format in the device object */
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ /* First detach any IRQ if currently attached */
+ vpfe_detach_irq(vpfe_dev);
+ vpfe_dev->fmt = *fmt;
+ /* set image capture parameters in the ccdc */
+ ret = vpfe_config_ccdc_image_format(vpfe_dev);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ const struct vpfe_pixel_format *pix_fmts;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n");
+
+ pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix);
+ if (NULL == pix_fmts)
+ return -EINVAL;
+ return 0;
+}
+
+/*
+ * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a
+ * given app input index
+ */
+static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev,
+ int *subdev_index,
+ int *subdev_input_index,
+ int app_input_index)
+{
+ struct vpfe_config *cfg = vpfe_dev->cfg;
+ struct vpfe_subdev_info *sdinfo;
+ int i, j = 0;
+
+ for (i = 0; i < cfg->num_subdevs; i++) {
+ sdinfo = &cfg->sub_devs[i];
+ if (app_input_index < (j + sdinfo->num_inputs)) {
+ *subdev_index = i;
+ *subdev_input_index = app_input_index - j;
+ return 0;
+ }
+ j += sdinfo->num_inputs;
+ }
+ return -EINVAL;
+}
+
+/*
+ * vpfe_get_app_input - Get app input index for a given subdev input index
+ * driver stores the input index of the current sub device and translate it
+ * when application request the current input
+ */
+static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev,
+ int *app_input_index)
+{
+ struct vpfe_config *cfg = vpfe_dev->cfg;
+ struct vpfe_subdev_info *sdinfo;
+ int i, j = 0;
+
+ for (i = 0; i < cfg->num_subdevs; i++) {
+ sdinfo = &cfg->sub_devs[i];
+ if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) {
+ if (vpfe_dev->current_input >= sdinfo->num_inputs)
+ return -1;
+ *app_input_index = j + vpfe_dev->current_input;
+ return 0;
+ }
+ j += sdinfo->num_inputs;
+ }
+ return -EINVAL;
+}
+
+static int vpfe_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int subdev, index ;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n");
+
+ if (vpfe_get_subdev_input_index(vpfe_dev,
+ &subdev,
+ &index,
+ inp->index) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "input information not found"
+ " for the subdev\n");
+ return -EINVAL;
+ }
+ sdinfo = &vpfe_dev->cfg->sub_devs[subdev];
+ memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input));
+ return 0;
+}
+
+static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n");
+
+ return vpfe_get_app_input_index(vpfe_dev, index);
+}
+
+
+static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int subdev_index, inp_index;
+ struct vpfe_route *route;
+ u32 input = 0, output = 0;
+ int ret = -EINVAL;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n");
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ /*
+ * If streaming is started return device busy
+ * error
+ */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+ ret = vpfe_get_subdev_input_index(vpfe_dev,
+ &subdev_index,
+ &inp_index,
+ index);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n");
+ goto unlock_out;
+ }
+
+ sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index];
+ route = &sdinfo->routes[inp_index];
+ if (route && sdinfo->can_route) {
+ input = route->input;
+ output = route->output;
+ }
+
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_routing, input, output, 0);
+
+ if (ret) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "vpfe_doioctl:error in setting input in decoder\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ vpfe_dev->current_subdev = sdinfo;
+ vpfe_dev->current_input = index;
+ vpfe_dev->std_index = 0;
+
+ /* set the bus/interface parameter for the sub device in ccdc */
+ ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params);
+ if (ret)
+ goto unlock_out;
+
+ /* set the default image parameters in the device */
+ ret = vpfe_config_image_format(vpfe_dev,
+ &vpfe_standards[vpfe_dev->std_index].std_id);
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n");
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ sdinfo = vpfe_dev->current_subdev;
+ if (ret)
+ return ret;
+ /* Call querystd function of decoder device */
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, querystd, std_id);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n");
+
+ /* Call decoder driver function to set the standard */
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ sdinfo = vpfe_dev->current_subdev;
+ /* If streaming is started, return device busy error */
+ if (vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_std, *std_id);
+ if (ret < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
+ goto unlock_out;
+ }
+ ret = vpfe_config_image_format(vpfe_dev, std_id);
+
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n");
+
+ *std_id = vpfe_standards[vpfe_dev->std_index].std_id;
+ return 0;
+}
+/*
+ * Videobuf operations
+ */
+static int vpfe_videobuf_setup(struct videobuf_queue *vq,
+ unsigned int *count,
+ unsigned int *size)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n");
+ *size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ if (vpfe_dev->memory == V4L2_MEMORY_MMAP &&
+ vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize)
+ *size = config_params.device_bufsize;
+
+ if (*count < config_params.min_numbuffers)
+ *count = config_params.min_numbuffers;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "count=%d, size=%d\n", *count, *size);
+ return 0;
+}
+
+static int vpfe_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long addr;
+ int ret;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
+
+ /* If buffer is not initialized, initialize it */
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = vpfe_dev->fmt.fmt.pix.width;
+ vb->height = vpfe_dev->fmt.fmt.pix.height;
+ vb->size = vpfe_dev->fmt.fmt.pix.sizeimage;
+ vb->field = field;
+
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret < 0)
+ return ret;
+
+ addr = videobuf_to_dma_contig(vb);
+ /* Make sure user addresses are aligned to 32 bytes */
+ if (!ALIGN(addr, 32))
+ return -EINVAL;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ return 0;
+}
+
+static void vpfe_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ /* Get the file handle object and device object */
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
+ list_add_tail(&vb->queue, &vpfe_dev->dma_queue);
+ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
+
+ /* Change state of the buffer */
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+static void vpfe_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct vpfe_fh *fh = vq->priv_data;
+ struct vpfe_device *vpfe_dev = fh->vpfe_dev;
+ unsigned long flags;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n");
+
+ /*
+ * We need to flush the buffer from the dma queue since
+ * they are de-allocated
+ */
+ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
+ INIT_LIST_HEAD(&vpfe_dev->dma_queue);
+ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops vpfe_videobuf_qops = {
+ .buf_setup = vpfe_videobuf_setup,
+ .buf_prepare = vpfe_videobuf_prepare,
+ .buf_queue = vpfe_videobuf_queue,
+ .buf_release = vpfe_videobuf_release,
+};
+
+/*
+ * vpfe_reqbufs. currently support REQBUF only once opening
+ * the device.
+ */
+static int vpfe_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ if (vpfe_dev->io_usrs != 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n");
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ vpfe_dev->memory = req_buf->memory;
+ videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue,
+ &vpfe_videobuf_qops,
+ vpfe_dev->pdev,
+ &vpfe_dev->irqlock,
+ req_buf->type,
+ vpfe_dev->fmt.fmt.pix.field,
+ sizeof(struct videobuf_buffer),
+ fh, NULL);
+
+ fh->io_allowed = 1;
+ vpfe_dev->io_usrs = 1;
+ INIT_LIST_HEAD(&vpfe_dev->dma_queue);
+ ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf);
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ if (vpfe_dev->memory != V4L2_MEMORY_MMAP) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n");
+ return -EINVAL;
+ }
+ /* Call videobuf_querybuf to get information */
+ return videobuf_querybuf(&vpfe_dev->buffer_queue, buf);
+}
+
+static int vpfe_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /*
+ * If this file handle is not allowed to do IO,
+ * return error
+ */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+ return videobuf_qbuf(&vpfe_dev->buffer_queue, p);
+}
+
+static int vpfe_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+ return videobuf_dqbuf(&vpfe_dev->buffer_queue,
+ buf, file->f_flags & O_NONBLOCK);
+}
+
+static int vpfe_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, queryctrl, qctrl);
+
+}
+
+static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, g_ctrl, ctrl);
+}
+
+static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_subdev_info *sdinfo;
+
+ sdinfo = vpfe_dev->current_subdev;
+
+ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ core, s_ctrl, ctrl);
+}
+
+/*
+ * vpfe_calculate_offsets : This function calculates buffers offset
+ * for top and bottom field
+ */
+static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev)
+{
+ struct v4l2_rect image_win;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n");
+
+ ccdc_dev->hw_ops.get_image_window(&image_win);
+ vpfe_dev->field_off = image_win.height * image_win.width;
+}
+
+/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */
+static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev)
+{
+ ccdc_dev->hw_ops.enable(1);
+ if (ccdc_dev->hw_ops.enable_out_to_sdram)
+ ccdc_dev->hw_ops.enable_out_to_sdram(1);
+ vpfe_dev->started = 1;
+}
+
+/*
+ * vpfe_streamon. Assume the DMA queue is not empty.
+ * application is expected to call QBUF before calling
+ * this ioctl. If not, driver returns error
+ */
+static int vpfe_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ unsigned long addr;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_stream, 1);
+
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n");
+ return -EINVAL;
+ }
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&vpfe_dev->buffer_queue.stream)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Call videobuf_streamon to start streaming * in videobuf */
+ ret = videobuf_streamon(&vpfe_dev->buffer_queue);
+ if (ret)
+ return ret;
+
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ goto streamoff;
+ /* Get the next frame from the buffer queue */
+ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
+ struct videobuf_buffer, queue);
+ vpfe_dev->cur_frm = vpfe_dev->next_frm;
+ /* Remove buffer from the buffer queue */
+ list_del(&vpfe_dev->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ vpfe_dev->field_id = 0;
+ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
+
+ /* Calculate field offset */
+ vpfe_calculate_offsets(vpfe_dev);
+
+ if (vpfe_attach_irq(vpfe_dev) < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error in attaching interrupt handle\n");
+ ret = -EFAULT;
+ goto unlock_out;
+ }
+ if (ccdc_dev->hw_ops.configure() < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Error in configuring ccdc\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr));
+ vpfe_start_ccdc_capture(vpfe_dev);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+streamoff:
+ ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
+ return ret;
+}
+
+static int vpfe_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ struct vpfe_fh *fh = file->private_data;
+ struct vpfe_subdev_info *sdinfo;
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n");
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!vpfe_dev->started) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "device started\n");
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ vpfe_stop_ccdc_capture(vpfe_dev);
+ vpfe_detach_irq(vpfe_dev);
+
+ sdinfo = vpfe_dev->current_subdev;
+ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
+ video, s_stream, 0);
+
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n");
+ ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+static int vpfe_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n");
+
+ if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards))
+ return -EINVAL;
+
+ memset(crop, 0, sizeof(struct v4l2_cropcap));
+ crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop->bounds.width = crop->defrect.width =
+ vpfe_standards[vpfe_dev->std_index].width;
+ crop->bounds.height = crop->defrect.height =
+ vpfe_standards[vpfe_dev->std_index].height;
+ crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect;
+ return 0;
+}
+
+static int vpfe_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n");
+
+ crop->c = vpfe_dev->crop;
+ return 0;
+}
+
+static int vpfe_s_crop(struct file *file, void *priv,
+ const struct v4l2_crop *crop)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n");
+
+ if (vpfe_dev->started) {
+ /* make sure streaming is not started */
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "Cannot change crop when streaming is ON\n");
+ return -EBUSY;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ if (crop->c.top < 0 || crop->c.left < 0) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+
+ /* adjust the width to 16 pixel boundary */
+ crop->c.width = ((crop->c.width + 15) & ~0xf);
+
+ /* make sure parameters are valid */
+ if ((crop->c.left + crop->c.width >
+ vpfe_dev->std_info.active_pixels) ||
+ (crop->c.top + crop->c.height >
+ vpfe_dev->std_info.active_lines)) {
+ v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n");
+ ret = -EINVAL;
+ goto unlock_out;
+ }
+ ccdc_dev->hw_ops.set_image_window(&crop->c);
+ vpfe_dev->fmt.fmt.pix.width = crop->c.width;
+ vpfe_dev->fmt.fmt.pix.height = crop->c.height;
+ vpfe_dev->fmt.fmt.pix.bytesperline =
+ ccdc_dev->hw_ops.get_line_length();
+ vpfe_dev->fmt.fmt.pix.sizeimage =
+ vpfe_dev->fmt.fmt.pix.bytesperline *
+ vpfe_dev->fmt.fmt.pix.height;
+ vpfe_dev->crop = crop->c;
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+
+static long vpfe_param_handler(struct file *file, void *priv,
+ bool valid_prio, int cmd, void *param)
+{
+ struct vpfe_device *vpfe_dev = video_drvdata(file);
+ int ret = 0;
+
+ v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n");
+
+ if (vpfe_dev->started) {
+ /* only allowed if streaming is not started */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "device already started\n");
+ return -EBUSY;
+ }
+
+ ret = mutex_lock_interruptible(&vpfe_dev->lock);
+ if (ret)
+ return ret;
+
+ switch (cmd) {
+ case VPFE_CMD_S_CCDC_RAW_PARAMS:
+ v4l2_warn(&vpfe_dev->v4l2_dev,
+ "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
+ if (ccdc_dev->hw_ops.set_params) {
+ ret = ccdc_dev->hw_ops.set_params(param);
+ if (ret) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "Error setting parameters in CCDC\n");
+ goto unlock_out;
+ }
+ ret = vpfe_get_ccdc_image_format(vpfe_dev,
+ &vpfe_dev->fmt);
+ if (ret < 0) {
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "Invalid image format at CCDC\n");
+ goto unlock_out;
+ }
+ } else {
+ ret = -EINVAL;
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
+ }
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+unlock_out:
+ mutex_unlock(&vpfe_dev->lock);
+ return ret;
+}
+
+
+/* vpfe capture ioctl operations */
+static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
+ .vidioc_querycap = vpfe_querycap,
+ .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap,
+ .vidioc_enum_input = vpfe_enum_input,
+ .vidioc_g_input = vpfe_g_input,
+ .vidioc_s_input = vpfe_s_input,
+ .vidioc_querystd = vpfe_querystd,
+ .vidioc_s_std = vpfe_s_std,
+ .vidioc_g_std = vpfe_g_std,
+ .vidioc_queryctrl = vpfe_queryctrl,
+ .vidioc_g_ctrl = vpfe_g_ctrl,
+ .vidioc_s_ctrl = vpfe_s_ctrl,
+ .vidioc_reqbufs = vpfe_reqbufs,
+ .vidioc_querybuf = vpfe_querybuf,
+ .vidioc_qbuf = vpfe_qbuf,
+ .vidioc_dqbuf = vpfe_dqbuf,
+ .vidioc_streamon = vpfe_streamon,
+ .vidioc_streamoff = vpfe_streamoff,
+ .vidioc_cropcap = vpfe_cropcap,
+ .vidioc_g_crop = vpfe_g_crop,
+ .vidioc_s_crop = vpfe_s_crop,
+ .vidioc_default = vpfe_param_handler,
+};
+
+static struct vpfe_device *vpfe_initialize(void)
+{
+ struct vpfe_device *vpfe_dev;
+
+ /* Default number of buffers should be 3 */
+ if ((numbuffers > 0) &&
+ (numbuffers < config_params.min_numbuffers))
+ numbuffers = config_params.min_numbuffers;
+
+ /*
+ * Set buffer size to min buffers size if invalid buffer size is
+ * given
+ */
+ if (bufsize < config_params.min_bufsize)
+ bufsize = config_params.min_bufsize;
+
+ config_params.numbuffers = numbuffers;
+
+ if (numbuffers)
+ config_params.device_bufsize = bufsize;
+
+ /* Allocate memory for device objects */
+ vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL);
+
+ return vpfe_dev;
+}
+
+/*
+ * vpfe_probe : This function creates device entries by register
+ * itself to the V4L2 driver and initializes fields of each
+ * device objects
+ */
+static __devinit int vpfe_probe(struct platform_device *pdev)
+{
+ struct vpfe_subdev_info *sdinfo;
+ struct vpfe_config *vpfe_cfg;
+ struct resource *res1;
+ struct vpfe_device *vpfe_dev;
+ struct i2c_adapter *i2c_adap;
+ struct video_device *vfd;
+ int ret = -ENOMEM, i, j;
+ int num_subdevs = 0;
+
+ /* Get the pointer to the device object */
+ vpfe_dev = vpfe_initialize();
+
+ if (!vpfe_dev) {
+ v4l2_err(pdev->dev.driver,
+ "Failed to allocate memory for vpfe_dev\n");
+ return ret;
+ }
+
+ vpfe_dev->pdev = &pdev->dev;
+
+ if (NULL == pdev->dev.platform_data) {
+ v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
+ ret = -ENODEV;
+ goto probe_free_dev_mem;
+ }
+
+ vpfe_cfg = pdev->dev.platform_data;
+ vpfe_dev->cfg = vpfe_cfg;
+ if (NULL == vpfe_cfg->ccdc ||
+ NULL == vpfe_cfg->card_name ||
+ NULL == vpfe_cfg->sub_devs) {
+ v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n");
+ ret = -ENOENT;
+ goto probe_free_dev_mem;
+ }
+
+ /* Allocate memory for ccdc configuration */
+ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL);
+ if (NULL == ccdc_cfg) {
+ v4l2_err(pdev->dev.driver,
+ "Memory allocation failed for ccdc_cfg\n");
+ goto probe_free_lock;
+ }
+
+ mutex_lock(&ccdc_lock);
+
+ strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32);
+ /* Get VINT0 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT0\n");
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
+ }
+ vpfe_dev->ccdc_irq0 = res1->start;
+
+ /* Get VINT1 irq resource */
+ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res1) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to get interrupt for VINT1\n");
+ ret = -ENODEV;
+ goto probe_free_ccdc_cfg_mem;
+ }
+ vpfe_dev->ccdc_irq1 = res1->start;
+
+ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
+ "vpfe_capture0", vpfe_dev);
+
+ if (0 != ret) {
+ v4l2_err(pdev->dev.driver, "Unable to request interrupt\n");
+ goto probe_free_ccdc_cfg_mem;
+ }
+
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (NULL == vfd) {
+ ret = -ENOMEM;
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+ goto probe_out_release_irq;
+ }
+
+ /* Initialize field of video device */
+ vfd->release = video_device_release;
+ vfd->fops = &vpfe_fops;
+ vfd->ioctl_ops = &vpfe_ioctl_ops;
+ vfd->tvnorms = 0;
+ vfd->current_norm = V4L2_STD_PAL;
+ vfd->v4l2_dev = &vpfe_dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "%s_V%d.%d.%d",
+ CAPTURE_DRV_NAME,
+ (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff,
+ (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff,
+ (VPFE_CAPTURE_VERSION_CODE) & 0xff);
+ /* Set video_dev to the video device */
+ vpfe_dev->video_dev = vfd;
+
+ ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register v4l2 device.\n");
+ goto probe_out_video_release;
+ }
+ v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
+ spin_lock_init(&vpfe_dev->irqlock);
+ spin_lock_init(&vpfe_dev->dma_queue_lock);
+ mutex_init(&vpfe_dev->lock);
+
+ /* Initialize field of the device objects */
+ vpfe_dev->numbuffers = config_params.numbuffers;
+
+ /* Initialize prio member of device object */
+ v4l2_prio_init(&vpfe_dev->prio);
+ /* register video device */
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "trying to register vpfe device.\n");
+ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
+ "video_dev=%x\n", (int)&vpfe_dev->video_dev);
+ vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = video_register_device(vpfe_dev->video_dev,
+ VFL_TYPE_GRABBER, -1);
+
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register video device.\n");
+ goto probe_out_v4l2_unregister;
+ }
+
+ v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n");
+ /* set the driver data in platform device */
+ platform_set_drvdata(pdev, vpfe_dev);
+ /* set driver private data */
+ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
+ i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
+ num_subdevs = vpfe_cfg->num_subdevs;
+ vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
+ GFP_KERNEL);
+ if (NULL == vpfe_dev->sd) {
+ v4l2_err(&vpfe_dev->v4l2_dev,
+ "unable to allocate memory for subdevice pointers\n");
+ ret = -ENOMEM;
+ goto probe_out_video_unregister;
+ }
+
+ for (i = 0; i < num_subdevs; i++) {
+ struct v4l2_input *inps;
+
+ sdinfo = &vpfe_cfg->sub_devs[i];
+
+ /* Load up the subdevice */
+ vpfe_dev->sd[i] =
+ v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
+ i2c_adap,
+ &sdinfo->board_info,
+ NULL);
+ if (vpfe_dev->sd[i]) {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s registered\n",
+ sdinfo->name);
+ vpfe_dev->sd[i]->grp_id = sdinfo->grp_id;
+ /* update tvnorms from the sub devices */
+ for (j = 0; j < sdinfo->num_inputs; j++) {
+ inps = &sdinfo->inputs[j];
+ vfd->tvnorms |= inps->std;
+ }
+ } else {
+ v4l2_info(&vpfe_dev->v4l2_dev,
+ "v4l2 sub device %s register fails\n",
+ sdinfo->name);
+ goto probe_sd_out;
+ }
+ }
+
+ /* set first sub device as current one */
+ vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0];
+
+ /* We have at least one sub device to work with */
+ mutex_unlock(&ccdc_lock);
+ return 0;
+
+probe_sd_out:
+ kfree(vpfe_dev->sd);
+probe_out_video_unregister:
+ video_unregister_device(vpfe_dev->video_dev);
+probe_out_v4l2_unregister:
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+probe_out_video_release:
+ if (!video_is_registered(vpfe_dev->video_dev))
+ video_device_release(vpfe_dev->video_dev);
+probe_out_release_irq:
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+probe_free_ccdc_cfg_mem:
+ kfree(ccdc_cfg);
+probe_free_lock:
+ mutex_unlock(&ccdc_lock);
+probe_free_dev_mem:
+ kfree(vpfe_dev);
+ return ret;
+}
+
+/*
+ * vpfe_remove : It un-register device from V4L2 driver
+ */
+static int __devexit vpfe_remove(struct platform_device *pdev)
+{
+ struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
+
+ v4l2_info(pdev->dev.driver, "vpfe_remove\n");
+
+ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
+ kfree(vpfe_dev->sd);
+ v4l2_device_unregister(&vpfe_dev->v4l2_dev);
+ video_unregister_device(vpfe_dev->video_dev);
+ kfree(vpfe_dev);
+ kfree(ccdc_cfg);
+ return 0;
+}
+
+static int vpfe_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int vpfe_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops vpfe_dev_pm_ops = {
+ .suspend = vpfe_suspend,
+ .resume = vpfe_resume,
+};
+
+static struct platform_driver vpfe_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &vpfe_dev_pm_ops,
+ },
+ .probe = vpfe_probe,
+ .remove = __devexit_p(vpfe_remove),
+};
+
+module_platform_driver(vpfe_driver);
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c
new file mode 100644
index 000000000000..cff3c0ab501f
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif.c
@@ -0,0 +1,516 @@
+/*
+ * vpif - Video Port Interface driver
+ * VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
+ * that receiveing video byte stream and two channels(2, 3) for video output.
+ * The hardware supports SDTV, HDTV formats, raw data capture.
+ * Currently, the driver supports NTSC and PAL standards.
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/v4l2-dv-timings.h>
+
+#include <mach/hardware.h>
+
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
+MODULE_LICENSE("GPL");
+
+#define VPIF_CH0_MAX_MODES (22)
+#define VPIF_CH1_MAX_MODES (02)
+#define VPIF_CH2_MAX_MODES (15)
+#define VPIF_CH3_MAX_MODES (02)
+
+static resource_size_t res_len;
+static struct resource *res;
+spinlock_t vpif_lock;
+
+void __iomem *vpif_base;
+struct clk *vpif_clk;
+
+/**
+ * ch_params: video standard configuration parameters for vpif
+ * The table must include all presets from supported subdevices.
+ */
+const struct vpif_channel_config_params ch_params[] = {
+ /* HDTV formats */
+ {
+ .name = "480p59_94",
+ .width = 720,
+ .height = 480,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 138-8,
+ .sav2eav = 720,
+ .l1 = 1,
+ .l3 = 43,
+ .l5 = 523,
+ .vsize = 525,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_720X480P59_94,
+ },
+ {
+ .name = "576p50",
+ .width = 720,
+ .height = 576,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 144-8,
+ .sav2eav = 720,
+ .l1 = 1,
+ .l3 = 45,
+ .l5 = 621,
+ .vsize = 625,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_720X576P50,
+ },
+ {
+ .name = "720p50",
+ .width = 1280,
+ .height = 720,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 700-8,
+ .sav2eav = 1280,
+ .l1 = 1,
+ .l3 = 26,
+ .l5 = 746,
+ .vsize = 750,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_1280X720P50,
+ },
+ {
+ .name = "720p60",
+ .width = 1280,
+ .height = 720,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 370 - 8,
+ .sav2eav = 1280,
+ .l1 = 1,
+ .l3 = 26,
+ .l5 = 746,
+ .vsize = 750,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_1280X720P60,
+ },
+ {
+ .name = "1080I50",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 0,
+ .ycmux_mode = 0,
+ .eav2sav = 720 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 21,
+ .l5 = 561,
+ .l7 = 563,
+ .l9 = 584,
+ .l11 = 1124,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_1920X1080I50,
+ },
+ {
+ .name = "1080I60",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 0,
+ .ycmux_mode = 0,
+ .eav2sav = 280 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 21,
+ .l5 = 561,
+ .l7 = 563,
+ .l9 = 584,
+ .l11 = 1124,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_1920X1080I60,
+ },
+ {
+ .name = "1080p60",
+ .width = 1920,
+ .height = 1080,
+ .frm_fmt = 1,
+ .ycmux_mode = 0,
+ .eav2sav = 280 - 8,
+ .sav2eav = 1920,
+ .l1 = 1,
+ .l3 = 42,
+ .l5 = 1122,
+ .vsize = 1125,
+ .capture_format = 0,
+ .vbi_supported = 0,
+ .hd_sd = 1,
+ .dv_timings = V4L2_DV_BT_CEA_1920X1080P60,
+ },
+
+ /* SDTV formats */
+ {
+ .name = "NTSC_M",
+ .width = 720,
+ .height = 480,
+ .frm_fmt = 0,
+ .ycmux_mode = 1,
+ .eav2sav = 268,
+ .sav2eav = 1440,
+ .l1 = 1,
+ .l3 = 23,
+ .l5 = 263,
+ .l7 = 266,
+ .l9 = 286,
+ .l11 = 525,
+ .vsize = 525,
+ .capture_format = 0,
+ .vbi_supported = 1,
+ .hd_sd = 0,
+ .stdid = V4L2_STD_525_60,
+ },
+ {
+ .name = "PAL_BDGHIK",
+ .width = 720,
+ .height = 576,
+ .frm_fmt = 0,
+ .ycmux_mode = 1,
+ .eav2sav = 280,
+ .sav2eav = 1440,
+ .l1 = 1,
+ .l3 = 23,
+ .l5 = 311,
+ .l7 = 313,
+ .l9 = 336,
+ .l11 = 624,
+ .vsize = 625,
+ .capture_format = 0,
+ .vbi_supported = 1,
+ .hd_sd = 0,
+ .stdid = V4L2_STD_625_50,
+ },
+};
+
+const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params);
+
+static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
+{
+ if (val)
+ vpif_set_bit(reg, bit);
+ else
+ vpif_clr_bit(reg, bit);
+}
+
+/* This structure is used to keep track of VPIF size register's offsets */
+struct vpif_registers {
+ u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl;
+ u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt;
+ u32 vanc1_size, width_mask, len_mask;
+ u8 max_modes;
+};
+
+static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = {
+ /* Channel0 */
+ {
+ VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01,
+ VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL,
+ VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
+ VPIF_CH0_MAX_MODES,
+ },
+ /* Channel1 */
+ {
+ VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01,
+ VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL,
+ VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
+ VPIF_CH1_MAX_MODES,
+ },
+ /* Channel2 */
+ {
+ VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01,
+ VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL,
+ VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE,
+ VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF,
+ VPIF_CH2_MAX_MODES
+ },
+ /* Channel3 */
+ {
+ VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01,
+ VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL,
+ VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE,
+ VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF,
+ VPIF_CH3_MAX_MODES
+ },
+};
+
+/* vpif_set_mode_info:
+ * This function is used to set horizontal and vertical config parameters
+ * As per the standard in the channel, configure the values of L1, L3,
+ * L5, L7 L9, L11 in VPIF Register , also write width and height
+ */
+static void vpif_set_mode_info(const struct vpif_channel_config_params *config,
+ u8 channel_id, u8 config_channel_id)
+{
+ u32 value;
+
+ value = (config->eav2sav & vpifregs[config_channel_id].width_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->sav2eav & vpifregs[config_channel_id].width_mask);
+ regw(value, vpifregs[channel_id].h_cfg);
+
+ value = (config->l1 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l3 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_00);
+
+ value = (config->l5 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l7 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_01);
+
+ value = (config->l9 & vpifregs[config_channel_id].len_mask);
+ value <<= VPIF_CH_LEN_SHIFT;
+ value |= (config->l11 & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg_02);
+
+ value = (config->vsize & vpifregs[config_channel_id].len_mask);
+ regw(value, vpifregs[channel_id].v_cfg);
+}
+
+/* config_vpif_params
+ * Function to set the parameters of a channel
+ * Mainly modifies the channel ciontrol register
+ * It sets frame format, yc mux mode
+ */
+static void config_vpif_params(struct vpif_params *vpifparams,
+ u8 channel_id, u8 found)
+{
+ const struct vpif_channel_config_params *config = &vpifparams->std_info;
+ u32 value, ch_nip, reg;
+ u8 start, end;
+ int i;
+
+ start = channel_id;
+ end = channel_id + found;
+
+ for (i = start; i < end; i++) {
+ reg = vpifregs[i].ch_ctrl;
+ if (channel_id < 2)
+ ch_nip = VPIF_CAPTURE_CH_NIP;
+ else
+ ch_nip = VPIF_DISPLAY_CH_NIP;
+
+ vpif_wr_bit(reg, ch_nip, config->frm_fmt);
+ vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode);
+ vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT,
+ vpifparams->video_params.storage_mode);
+
+ /* Set raster scanning SDR Format */
+ vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT);
+ vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format);
+
+ if (channel_id > 1) /* Set the Pixel enable bit */
+ vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT);
+ else if (config->capture_format) {
+ /* Set the polarity of various pins */
+ vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT,
+ vpifparams->iface.fid_pol);
+ vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT,
+ vpifparams->iface.vd_pol);
+ vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT,
+ vpifparams->iface.hd_pol);
+
+ value = regr(reg);
+ /* Set data width */
+ value &= ~(0x3u <<
+ VPIF_CH_DATA_WIDTH_BIT);
+ value |= ((vpifparams->params.data_sz) <<
+ VPIF_CH_DATA_WIDTH_BIT);
+ regw(value, reg);
+ }
+
+ /* Write the pitch in the driver */
+ regw((vpifparams->video_params.hpitch),
+ vpifregs[i].line_offset);
+ }
+}
+
+/* vpif_set_video_params
+ * This function is used to set video parameters in VPIF register
+ */
+int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id)
+{
+ const struct vpif_channel_config_params *config = &vpifparams->std_info;
+ int found = 1;
+
+ vpif_set_mode_info(config, channel_id, channel_id);
+ if (!config->ycmux_mode) {
+ /* YC are on separate channels (HDTV formats) */
+ vpif_set_mode_info(config, channel_id + 1, channel_id);
+ found = 2;
+ }
+
+ config_vpif_params(vpifparams, channel_id, found);
+
+ regw(0x80, VPIF_REQ_SIZE);
+ regw(0x01, VPIF_EMULATION_CTRL);
+
+ return found;
+}
+EXPORT_SYMBOL(vpif_set_video_params);
+
+void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
+ u8 channel_id)
+{
+ u32 value;
+
+ value = 0x3F8 & (vbiparams->hstart0);
+ value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16);
+ regw(value, vpifregs[channel_id].vanc0_strt);
+
+ value = 0x3F8 & (vbiparams->hstart1);
+ value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16);
+ regw(value, vpifregs[channel_id].vanc1_strt);
+
+ value = 0x3F8 & (vbiparams->hsize0);
+ value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16);
+ regw(value, vpifregs[channel_id].vanc0_size);
+
+ value = 0x3F8 & (vbiparams->hsize1);
+ value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16);
+ regw(value, vpifregs[channel_id].vanc1_size);
+
+}
+EXPORT_SYMBOL(vpif_set_vbi_display_params);
+
+int vpif_channel_getfid(u8 channel_id)
+{
+ return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK)
+ >> VPIF_CH_FID_SHIFT;
+}
+EXPORT_SYMBOL(vpif_channel_getfid);
+
+static int __devinit vpif_probe(struct platform_device *pdev)
+{
+ int status = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+
+ res_len = resource_size(res);
+
+ res = request_mem_region(res->start, res_len, res->name);
+ if (!res)
+ return -EBUSY;
+
+ vpif_base = ioremap(res->start, res_len);
+ if (!vpif_base) {
+ status = -EBUSY;
+ goto fail;
+ }
+
+ vpif_clk = clk_get(&pdev->dev, "vpif");
+ if (IS_ERR(vpif_clk)) {
+ status = PTR_ERR(vpif_clk);
+ goto clk_fail;
+ }
+ clk_enable(vpif_clk);
+
+ spin_lock_init(&vpif_lock);
+ dev_info(&pdev->dev, "vpif probe success\n");
+ return 0;
+
+clk_fail:
+ iounmap(vpif_base);
+fail:
+ release_mem_region(res->start, res_len);
+ return status;
+}
+
+static int __devexit vpif_remove(struct platform_device *pdev)
+{
+ if (vpif_clk) {
+ clk_disable(vpif_clk);
+ clk_put(vpif_clk);
+ }
+
+ iounmap(vpif_base);
+ release_mem_region(res->start, res_len);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+ clk_disable(vpif_clk);
+ return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+ clk_enable(vpif_clk);
+ return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
+static struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif",
+ .owner = THIS_MODULE,
+ .pm = vpif_pm_ops,
+ },
+ .remove = __devexit_p(vpif_remove),
+ .probe = vpif_probe,
+};
+
+static void vpif_exit(void)
+{
+ platform_driver_unregister(&vpif_driver);
+}
+
+static int __init vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+subsys_initcall(vpif_init);
+module_exit(vpif_exit);
+
diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h
new file mode 100644
index 000000000000..a1ab6a0f4e9e
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif.h
@@ -0,0 +1,688 @@
+/*
+ * VPIF header file
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef VPIF_H
+#define VPIF_H
+
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <media/davinci/vpif_types.h>
+
+/* Maximum channel allowed */
+#define VPIF_NUM_CHANNELS (4)
+#define VPIF_CAPTURE_NUM_CHANNELS (2)
+#define VPIF_DISPLAY_NUM_CHANNELS (2)
+
+/* Macros to read/write registers */
+extern void __iomem *vpif_base;
+extern spinlock_t vpif_lock;
+
+#define regr(reg) readl((reg) + vpif_base)
+#define regw(value, reg) writel(value, (reg + vpif_base))
+
+/* Register Address Offsets */
+#define VPIF_PID (0x0000)
+#define VPIF_CH0_CTRL (0x0004)
+#define VPIF_CH1_CTRL (0x0008)
+#define VPIF_CH2_CTRL (0x000C)
+#define VPIF_CH3_CTRL (0x0010)
+
+#define VPIF_INTEN (0x0020)
+#define VPIF_INTEN_SET (0x0024)
+#define VPIF_INTEN_CLR (0x0028)
+#define VPIF_STATUS (0x002C)
+#define VPIF_STATUS_CLR (0x0030)
+#define VPIF_EMULATION_CTRL (0x0034)
+#define VPIF_REQ_SIZE (0x0038)
+
+#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040)
+#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044)
+#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048)
+#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c)
+#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050)
+#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054)
+#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058)
+#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c)
+#define VPIF_CH0_SP_CFG (0x0060)
+#define VPIF_CH0_IMG_ADD_OFST (0x0064)
+#define VPIF_CH0_HANC_ADD_OFST (0x0068)
+#define VPIF_CH0_H_CFG (0x006c)
+#define VPIF_CH0_V_CFG_00 (0x0070)
+#define VPIF_CH0_V_CFG_01 (0x0074)
+#define VPIF_CH0_V_CFG_02 (0x0078)
+#define VPIF_CH0_V_CFG_03 (0x007c)
+
+#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080)
+#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084)
+#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088)
+#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c)
+#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090)
+#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094)
+#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098)
+#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c)
+#define VPIF_CH1_SP_CFG (0x00a0)
+#define VPIF_CH1_IMG_ADD_OFST (0x00a4)
+#define VPIF_CH1_HANC_ADD_OFST (0x00a8)
+#define VPIF_CH1_H_CFG (0x00ac)
+#define VPIF_CH1_V_CFG_00 (0x00b0)
+#define VPIF_CH1_V_CFG_01 (0x00b4)
+#define VPIF_CH1_V_CFG_02 (0x00b8)
+#define VPIF_CH1_V_CFG_03 (0x00bc)
+
+#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0)
+#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4)
+#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8)
+#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc)
+#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0)
+#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4)
+#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8)
+#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc)
+#define VPIF_CH2_SP_CFG (0x00e0)
+#define VPIF_CH2_IMG_ADD_OFST (0x00e4)
+#define VPIF_CH2_HANC_ADD_OFST (0x00e8)
+#define VPIF_CH2_H_CFG (0x00ec)
+#define VPIF_CH2_V_CFG_00 (0x00f0)
+#define VPIF_CH2_V_CFG_01 (0x00f4)
+#define VPIF_CH2_V_CFG_02 (0x00f8)
+#define VPIF_CH2_V_CFG_03 (0x00fc)
+#define VPIF_CH2_HANC0_STRT (0x0100)
+#define VPIF_CH2_HANC0_SIZE (0x0104)
+#define VPIF_CH2_HANC1_STRT (0x0108)
+#define VPIF_CH2_HANC1_SIZE (0x010c)
+#define VPIF_CH2_VANC0_STRT (0x0110)
+#define VPIF_CH2_VANC0_SIZE (0x0114)
+#define VPIF_CH2_VANC1_STRT (0x0118)
+#define VPIF_CH2_VANC1_SIZE (0x011c)
+
+#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140)
+#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144)
+#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148)
+#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c)
+#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150)
+#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154)
+#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158)
+#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c)
+#define VPIF_CH3_SP_CFG (0x0160)
+#define VPIF_CH3_IMG_ADD_OFST (0x0164)
+#define VPIF_CH3_HANC_ADD_OFST (0x0168)
+#define VPIF_CH3_H_CFG (0x016c)
+#define VPIF_CH3_V_CFG_00 (0x0170)
+#define VPIF_CH3_V_CFG_01 (0x0174)
+#define VPIF_CH3_V_CFG_02 (0x0178)
+#define VPIF_CH3_V_CFG_03 (0x017c)
+#define VPIF_CH3_HANC0_STRT (0x0180)
+#define VPIF_CH3_HANC0_SIZE (0x0184)
+#define VPIF_CH3_HANC1_STRT (0x0188)
+#define VPIF_CH3_HANC1_SIZE (0x018c)
+#define VPIF_CH3_VANC0_STRT (0x0190)
+#define VPIF_CH3_VANC0_SIZE (0x0194)
+#define VPIF_CH3_VANC1_STRT (0x0198)
+#define VPIF_CH3_VANC1_SIZE (0x019c)
+
+#define VPIF_IODFT_CTRL (0x01c0)
+
+/* Functions for bit Manipulation */
+static inline void vpif_set_bit(u32 reg, u32 bit)
+{
+ regw((regr(reg)) | (0x01 << bit), reg);
+}
+
+static inline void vpif_clr_bit(u32 reg, u32 bit)
+{
+ regw(((regr(reg)) & ~(0x01 << bit)), reg);
+}
+
+/* Macro for Generating mask */
+#ifdef GENERATE_MASK
+#undef GENERATE_MASK
+#endif
+
+#define GENERATE_MASK(bits, pos) \
+ ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos)
+
+/* Bit positions in the channel control registers */
+#define VPIF_CH_DATA_MODE_BIT (2)
+#define VPIF_CH_YC_MUX_BIT (3)
+#define VPIF_CH_SDR_FMT_BIT (4)
+#define VPIF_CH_HANC_EN_BIT (8)
+#define VPIF_CH_VANC_EN_BIT (9)
+
+#define VPIF_CAPTURE_CH_NIP (10)
+#define VPIF_DISPLAY_CH_NIP (11)
+
+#define VPIF_DISPLAY_PIX_EN_BIT (10)
+
+#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12)
+
+#define VPIF_CH_FID_POLARITY_BIT (15)
+#define VPIF_CH_V_VALID_POLARITY_BIT (14)
+#define VPIF_CH_H_VALID_POLARITY_BIT (13)
+#define VPIF_CH_DATA_WIDTH_BIT (28)
+
+#define VPIF_CH_CLK_EDGE_CTRL_BIT (31)
+
+/* Mask various length */
+#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0)
+#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0)
+#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0)
+#define VPIF_CH_LEN_SHIFT (16)
+
+/* VPIF masks for registers */
+#define VPIF_REQ_SIZE_MASK (0x1ff)
+
+/* bit posotion of interrupt vpif_ch_intr register */
+#define VPIF_INTEN_FRAME_CH0 (0x00000001)
+#define VPIF_INTEN_FRAME_CH1 (0x00000002)
+#define VPIF_INTEN_FRAME_CH2 (0x00000004)
+#define VPIF_INTEN_FRAME_CH3 (0x00000008)
+
+/* bit position of clock and channel enable in vpif_chn_ctrl register */
+
+#define VPIF_CH0_CLK_EN (0x00000002)
+#define VPIF_CH0_EN (0x00000001)
+#define VPIF_CH1_CLK_EN (0x00000002)
+#define VPIF_CH1_EN (0x00000001)
+#define VPIF_CH2_CLK_EN (0x00000002)
+#define VPIF_CH2_EN (0x00000001)
+#define VPIF_CH3_CLK_EN (0x00000002)
+#define VPIF_CH3_EN (0x00000001)
+#define VPIF_CH_CLK_EN (0x00000002)
+#define VPIF_CH_EN (0x00000001)
+
+#define VPIF_INT_TOP (0x00)
+#define VPIF_INT_BOTTOM (0x01)
+#define VPIF_INT_BOTH (0x02)
+
+#define VPIF_CH0_INT_CTRL_SHIFT (6)
+#define VPIF_CH1_INT_CTRL_SHIFT (6)
+#define VPIF_CH2_INT_CTRL_SHIFT (6)
+#define VPIF_CH3_INT_CTRL_SHIFT (6)
+#define VPIF_CH_INT_CTRL_SHIFT (6)
+
+#define VPIF_CH2_CLIP_ANC_EN 14
+#define VPIF_CH2_CLIP_ACTIVE_EN 13
+
+#define VPIF_CH3_CLIP_ANC_EN 14
+#define VPIF_CH3_CLIP_ACTIVE_EN 13
+
+/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
+#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
+#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
+#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL))
+
+/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
+#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\
+ (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL))
+
+#define VPIF_CH_FID_MASK (0x20)
+#define VPIF_CH_FID_SHIFT (5)
+
+#define VPIF_NTSC_VBI_START_FIELD0 (1)
+#define VPIF_NTSC_VBI_START_FIELD1 (263)
+#define VPIF_PAL_VBI_START_FIELD0 (624)
+#define VPIF_PAL_VBI_START_FIELD1 (311)
+
+#define VPIF_NTSC_HBI_START_FIELD0 (1)
+#define VPIF_NTSC_HBI_START_FIELD1 (263)
+#define VPIF_PAL_HBI_START_FIELD0 (624)
+#define VPIF_PAL_HBI_START_FIELD1 (311)
+
+#define VPIF_NTSC_VBI_COUNT_FIELD0 (20)
+#define VPIF_NTSC_VBI_COUNT_FIELD1 (19)
+#define VPIF_PAL_VBI_COUNT_FIELD0 (24)
+#define VPIF_PAL_VBI_COUNT_FIELD1 (25)
+
+#define VPIF_NTSC_HBI_COUNT_FIELD0 (263)
+#define VPIF_NTSC_HBI_COUNT_FIELD1 (262)
+#define VPIF_PAL_HBI_COUNT_FIELD0 (312)
+#define VPIF_PAL_HBI_COUNT_FIELD1 (313)
+
+#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720)
+#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720)
+#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268)
+#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280)
+
+#define VPIF_CH_VANC_EN (0x20)
+#define VPIF_DMA_REQ_SIZE (0x080)
+#define VPIF_EMULATION_DISABLE (0x01)
+
+extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS];
+
+/* inline function to enable/disable channel0 */
+static inline void enable_channel0(int enable)
+{
+ if (enable)
+ regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL);
+ else
+ regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL);
+}
+
+/* inline function to enable/disable channel1 */
+static inline void enable_channel1(int enable)
+{
+ if (enable)
+ regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL);
+ else
+ regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL);
+}
+
+/* inline function to enable interrupt for channel0 */
+static inline void channel0_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable interrupt for channel1 */
+static inline void channel1_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to set buffer addresses in case of Y/C non mux mode */
+static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for video data */
+static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+
+ regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch0_set_vbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC);
+ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch0_set_hbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC);
+ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC);
+}
+
+static inline void ch1_set_vbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC);
+ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch1_set_hbi_addr(unsigned long top_vbi,
+ unsigned long btm_vbi, unsigned long a, unsigned long b)
+{
+ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC);
+ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC);
+}
+
+/* Inline function to enable raw vbi in the given channel */
+static inline void disable_raw_feature(u8 channel_id, u8 index)
+{
+ u32 ctrl_reg;
+ if (0 == channel_id)
+ ctrl_reg = VPIF_CH0_CTRL;
+ else
+ ctrl_reg = VPIF_CH1_CTRL;
+
+ if (1 == index)
+ vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
+ else
+ vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
+}
+
+static inline void enable_raw_feature(u8 channel_id, u8 index)
+{
+ u32 ctrl_reg;
+ if (0 == channel_id)
+ ctrl_reg = VPIF_CH0_CTRL;
+ else
+ ctrl_reg = VPIF_CH1_CTRL;
+
+ if (1 == index)
+ vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
+ else
+ vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
+}
+
+/* inline function to enable/disable channel2 */
+static inline void enable_channel2(int enable)
+{
+ if (enable) {
+ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
+ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL);
+ } else {
+ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
+ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL);
+ }
+}
+
+/* inline function to enable/disable channel3 */
+static inline void enable_channel3(int enable)
+{
+ if (enable) {
+ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
+ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL);
+ } else {
+ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
+ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL);
+ }
+}
+
+/* inline function to enable interrupt for channel2 */
+static inline void channel2_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable interrupt for channel3 */
+static inline void channel3_intr_enable(int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vpif_lock, flags);
+
+ if (enable) {
+ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
+
+ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
+ VPIF_INTEN_SET);
+ } else {
+ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN);
+ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
+ VPIF_INTEN_SET);
+ }
+ spin_unlock_irqrestore(&vpif_lock, flags);
+}
+
+/* inline function to enable raw vbi data for channel2 */
+static inline void channel2_raw_enable(int enable, u8 index)
+{
+ u32 mask;
+
+ if (1 == index)
+ mask = VPIF_CH_VANC_EN_BIT;
+ else
+ mask = VPIF_CH_HANC_EN_BIT;
+
+ if (enable)
+ vpif_set_bit(VPIF_CH2_CTRL, mask);
+ else
+ vpif_clr_bit(VPIF_CH2_CTRL, mask);
+}
+
+/* inline function to enable raw vbi data for channel3*/
+static inline void channel3_raw_enable(int enable, u8 index)
+{
+ u32 mask;
+
+ if (1 == index)
+ mask = VPIF_CH_VANC_EN_BIT;
+ else
+ mask = VPIF_CH_HANC_EN_BIT;
+
+ if (enable)
+ vpif_set_bit(VPIF_CH3_CTRL, mask);
+ else
+ vpif_clr_bit(VPIF_CH3_CTRL, mask);
+}
+
+/* function to enable clipping (for both active and blanking regions) on ch 2 */
+static inline void channel2_clipping_enable(int enable)
+{
+ if (enable) {
+ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+ } else {
+ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
+ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
+ }
+}
+
+/* function to enable clipping (for both active and blanking regions) on ch 3 */
+static inline void channel3_clipping_enable(int enable)
+{
+ if (enable) {
+ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+ } else {
+ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
+ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
+ }
+}
+
+/* inline function to set buffer addresses in case of Y/C non mux mode */
+static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for video data */
+static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA);
+}
+
+static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA);
+ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA);
+ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
+ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
+}
+
+/* inline function to set buffer addresses in VPIF registers for vbi data */
+static inline void ch2_set_vbi_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC);
+ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC);
+}
+
+static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
+ unsigned long btm_strt_luma,
+ unsigned long top_strt_chroma,
+ unsigned long btm_strt_chroma)
+{
+ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC);
+ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
+}
+
+static inline int vpif_intr_status(int channel)
+{
+ int status = 0;
+ int mask;
+
+ if (channel < 0 || channel > 3)
+ return 0;
+
+ mask = 1 << channel;
+ status = regr(VPIF_STATUS) & mask;
+ regw(status, VPIF_STATUS_CLR);
+
+ return status;
+}
+
+#define VPIF_MAX_NAME (30)
+
+/* This structure will store size parameters as per the mode selected by user */
+struct vpif_channel_config_params {
+ char name[VPIF_MAX_NAME]; /* Name of the mode */
+ u16 width; /* Indicates width of the image */
+ u16 height; /* Indicates height of the image */
+ u8 frm_fmt; /* Interlaced (0) or progressive (1) */
+ u8 ycmux_mode; /* This mode requires one (0) or two (1)
+ channels */
+ u16 eav2sav; /* length of eav 2 sav */
+ u16 sav2eav; /* length of sav 2 eav */
+ u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */
+ u16 vsize; /* Vertical size of the image */
+ u8 capture_format; /* Indicates whether capture format
+ * is in BT or in CCD/CMOS */
+ u8 vbi_supported; /* Indicates whether this mode
+ * supports capturing vbi or not */
+ u8 hd_sd; /* HDTV (1) or SDTV (0) format */
+ v4l2_std_id stdid; /* SDTV format */
+ struct v4l2_dv_timings dv_timings; /* HDTV format */
+};
+
+extern const unsigned int vpif_ch_params_count;
+extern const struct vpif_channel_config_params ch_params[];
+
+struct vpif_video_params;
+struct vpif_params;
+struct vpif_vbi_params;
+
+int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id);
+void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
+ u8 channel_id);
+int vpif_channel_getfid(u8 channel_id);
+
+enum data_size {
+ _8BITS = 0,
+ _10BITS,
+ _12BITS,
+};
+
+/* Structure for vpif parameters for raw vbi data */
+struct vpif_vbi_params {
+ __u32 hstart0; /* Horizontal start of raw vbi data for first field */
+ __u32 vstart0; /* Vertical start of raw vbi data for first field */
+ __u32 hsize0; /* Horizontal size of raw vbi data for first field */
+ __u32 vsize0; /* Vertical size of raw vbi data for first field */
+ __u32 hstart1; /* Horizontal start of raw vbi data for second field */
+ __u32 vstart1; /* Vertical start of raw vbi data for second field */
+ __u32 hsize1; /* Horizontal size of raw vbi data for second field */
+ __u32 vsize1; /* Vertical size of raw vbi data for second field */
+};
+
+/* structure for vpif parameters */
+struct vpif_video_params {
+ __u8 storage_mode; /* Indicates field or frame mode */
+ unsigned long hpitch;
+ v4l2_std_id stdid;
+};
+
+struct vpif_params {
+ struct vpif_interface iface;
+ struct vpif_video_params video_params;
+ struct vpif_channel_config_params std_info;
+ union param {
+ struct vpif_vbi_params vbi_params;
+ enum data_size data_sz;
+ } params;
+};
+
+#endif /* End of #ifndef VPIF_H */
+
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
new file mode 100644
index 000000000000..0bafecac4923
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -0,0 +1,2380 @@
+/*
+ * Copyright (C) 2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ *
+ * TODO : add support for VBI & HBI data service
+ * add static buffer allocation
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "vpif_capture.h"
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_CAPTURE_VERSION);
+
+#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
+#define vpif_dbg(level, debug, fmt, arg...) \
+ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
+
+static int debug = 1;
+static u32 ch0_numbuffers = 3;
+static u32 ch1_numbuffers = 3;
+static u32 ch0_bufsize = 1920 * 1080 * 2;
+static u32 ch1_bufsize = 720 * 576 * 2;
+
+module_param(debug, int, 0644);
+module_param(ch0_numbuffers, uint, S_IRUGO);
+module_param(ch1_numbuffers, uint, S_IRUGO);
+module_param(ch0_bufsize, uint, S_IRUGO);
+module_param(ch1_bufsize, uint, S_IRUGO);
+
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)");
+MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)");
+MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)");
+MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)");
+
+static struct vpif_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers[0] = 3,
+ .numbuffers[1] = 3,
+ .min_bufsize[0] = 720 * 480 * 2,
+ .min_bufsize[1] = 720 * 480 * 2,
+ .channel_bufsize[0] = 1920 * 1080 * 2,
+ .channel_bufsize[1] = 720 * 576 * 2,
+};
+
+/* global variables */
+static struct vpif_device vpif_obj = { {NULL} };
+static struct device *vpif_dev;
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
+
+/**
+ * buffer_prepare : callback function for buffer prepare
+ * @vb: ptr to vb2_buffer
+ *
+ * This is the callback function for buffer prepare when vb2_qbuf()
+ * function is called. The buffer is prepared and user space virtual address
+ * or user address is converted into physical address
+ */
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long addr;
+
+ vpif_dbg(2, debug, "vpif_buffer_prepare\n");
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED) {
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ goto exit;
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (q->streaming) {
+ if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
+ !IS_ALIGNED((addr + common->ybtm_off), 8) ||
+ !IS_ALIGNED((addr + common->ctop_off), 8) ||
+ !IS_ALIGNED((addr + common->cbtm_off), 8))
+ goto exit;
+ }
+ }
+ return 0;
+exit:
+ vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
+ return -EINVAL;
+}
+
+/**
+ * vpif_buffer_queue_setup : Callback function for buffer setup.
+ * @vq: vb2_queue ptr
+ * @fmt: v4l2 format
+ * @nbuffers: ptr to number of buffers requested by application
+ * @nplanes:: contains number of distinct video planes needed to hold a frame
+ * @sizes[]: contains the size (in bytes) of each plane.
+ * @alloc_ctxs: ptr to allocation context
+ *
+ * This callback function is called when reqbuf() is called to adjust
+ * the buffer count and buffer size
+ */
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long size;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_buffer_setup\n");
+
+ /* If memory type is not mmap, return */
+ if (V4L2_MEMORY_MMAP == common->memory) {
+ /* Calculate the size of the buffer */
+ size = config_params.channel_bufsize[ch->channel_id];
+ /*
+ * Checking if the buffer size exceeds the available buffer
+ * ycmux_mode = 0 means 1 channel mode HD and
+ * ycmux_mode = 1 means 2 channels mode SD
+ */
+ if (ch->vpifparams.std_info.ycmux_mode == 0) {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ (config_params.video_limit[0]
+ + config_params.video_limit[1]))
+ (*nbuffers)--;
+ } else {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ config_params.video_limit[ch->channel_id])
+ (*nbuffers)--;
+ }
+
+ } else {
+ size = common->fmt.fmt.pix.sizeimage;
+ }
+
+ if (*nbuffers < config_params.min_numbuffers)
+ *nbuffers = config_params.min_numbuffers;
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = common->alloc_ctx;
+
+ return 0;
+}
+
+/**
+ * vpif_buffer_queue : Callback function to add buffer to DMA queue
+ * @vb: ptr to vb2_buffer
+ */
+static void vpif_buffer_queue(struct vb2_buffer *vb)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct channel_obj *ch = fh->channel;
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_buffer_queue\n");
+
+ /* add the buffer to the DMA queue */
+ list_add_tail(&buf->list, &common->dma_queue);
+}
+
+/**
+ * vpif_buf_cleanup : Callback function to free buffer
+ * @vb: ptr to vb2_buffer
+ *
+ * This function is called from the videobuf2 layer to free memory
+ * allocated to the buffers
+ */
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
+{
+ /* Get the file handle object and channel object */
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long flags;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ spin_lock_irqsave(&common->irqlock, flags);
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+
+}
+
+static void vpif_wait_prepare(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_unlock(&common->lock);
+}
+
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+}
+
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpif_cap_buffer *buf = container_of(vb,
+ struct vpif_cap_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
+ { {1, 1} };
+
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpif_capture_config *vpif_config_data =
+ vpif_dev->platform_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpif = &ch->vpifparams;
+ unsigned long addr = 0;
+ int ret;
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_dbg(1, debug, "buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ /* Remove buffer from the buffer queue */
+ list_del(&common->cur_frm->list);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((vpif->std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
+ (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
+ (!vpif->std_info.frm_fmt &&
+ (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_dbg(1, debug, "conflict in field format and std format\n");
+ return -EINVAL;
+ }
+
+ /* configure 1 or 2 channel mode */
+ ret = vpif_config_data->setup_input_channel_mode
+ (vpif->std_info.ycmux_mode);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set vpif channel mode\n");
+ return ret;
+ }
+
+ /* Call vpif_set_params function to set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id);
+
+ if (ret < 0) {
+ vpif_dbg(1, debug, "can't set video params\n");
+ return ret;
+ }
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+
+ /**
+ * Set interrupt for both the fields in VPIF Register enable channel in
+ * VPIF register
+ */
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
+ channel0_intr_assert();
+ channel0_intr_enable(1);
+ enable_channel0(1);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (common->started == 2)) {
+ channel1_intr_assert();
+ channel1_intr_enable(1);
+ enable_channel1(1);
+ }
+
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* release all active buffers */
+ while (!list_empty(&common->dma_queue)) {
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ list_del(&common->next_frm->list);
+ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpif_buffer_queue_setup,
+ .wait_prepare = vpif_wait_prepare,
+ .wait_finish = vpif_wait_finish,
+ .buf_init = vpif_buffer_init,
+ .buf_prepare = vpif_buffer_prepare,
+ .start_streaming = vpif_start_streaming,
+ .stop_streaming = vpif_stop_streaming,
+ .buf_cleanup = vpif_buf_cleanup,
+ .buf_queue = vpif_buffer_queue,
+};
+
+/**
+ * vpif_process_buffer_complete: process a completed buffer
+ * @common: ptr to common channel object
+ *
+ * This function time stamp the buffer and mark it as DONE. It also
+ * wake up any process waiting on the QUEUE and set the next buffer
+ * as current
+ */
+static void vpif_process_buffer_complete(struct common_obj *common)
+{
+ do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
+ /* Make curFrm pointing to nextFrm */
+ common->cur_frm = common->next_frm;
+}
+
+/**
+ * vpif_schedule_next_buffer: set next buffer address for capture
+ * @common : ptr to common channel object
+ *
+ * This function will get next buffer from the dma queue and
+ * set the buffer address in the vpif register for capture.
+ * the buffer is marked active
+ */
+static void vpif_schedule_next_buffer(struct common_obj *common)
+{
+ unsigned long addr = 0;
+
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_cap_buffer, list);
+ /* Remove that buffer from the buffer queue */
+ list_del(&common->next_frm->list);
+ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
+
+ /* Set top and bottom field addresses in VPIF registers */
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+}
+
+/**
+ * vpif_channel_isr : ISR handler for vpif capture
+ * @irq: irq number
+ * @dev_id: dev_id ptr
+ *
+ * It changes status of the captured buffer, takes next buffer from the queue
+ * and sets its address in VPIF registers
+ */
+static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
+{
+ struct vpif_device *dev = &vpif_obj;
+ struct common_obj *common;
+ struct channel_obj *ch;
+ enum v4l2_field field;
+ int channel_id = 0;
+ int fid = -1, i;
+
+ channel_id = *(int *)(dev_id);
+ if (!vpif_intr_status(channel_id))
+ return IRQ_NONE;
+
+ ch = dev->dev[channel_id];
+
+ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
+
+ for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
+ common = &ch->common[i];
+ /* skip If streaming is not started in this channel */
+ if (0 == common->started)
+ continue;
+
+ /* Check the field format */
+ if (1 == ch->vpifparams.std_info.frm_fmt) {
+ /* Progressive mode */
+ if (list_empty(&common->dma_queue))
+ continue;
+
+ if (!channel_first_int[i][channel_id])
+ vpif_process_buffer_complete(common);
+
+ channel_first_int[i][channel_id] = 0;
+
+ vpif_schedule_next_buffer(common);
+
+
+ channel_first_int[i][channel_id] = 0;
+ } else {
+ /**
+ * Interlaced mode. If it is first interrupt, ignore
+ * it
+ */
+ if (channel_first_int[i][channel_id]) {
+ channel_first_int[i][channel_id] = 0;
+ continue;
+ }
+ if (0 == i) {
+ ch->field_id ^= 1;
+ /* Get field id from VPIF registers */
+ fid = vpif_channel_getfid(ch->channel_id);
+ if (fid != ch->field_id) {
+ /**
+ * If field id does not match stored
+ * field id, make them in sync
+ */
+ if (0 == fid)
+ ch->field_id = fid;
+ return IRQ_HANDLED;
+ }
+ }
+ /* device field id and local field id are in sync */
+ if (0 == fid) {
+ /* this is even field */
+ if (common->cur_frm == common->next_frm)
+ continue;
+
+ /* mark the current buffer as done */
+ vpif_process_buffer_complete(common);
+ } else if (1 == fid) {
+ /* odd field */
+ if (list_empty(&common->dma_queue) ||
+ (common->cur_frm != common->next_frm))
+ continue;
+
+ vpif_schedule_next_buffer(common);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * vpif_update_std_info() - update standard related info
+ * @ch: ptr to channel object
+ *
+ * For a given standard selected by application, update values
+ * in the device data structures
+ */
+static int vpif_update_std_info(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ const struct vpif_channel_config_params *config;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ int index;
+
+ vpif_dbg(2, debug, "vpif_update_std_info\n");
+
+ for (index = 0; index < vpif_ch_params_count; index++) {
+ config = &ch_params[index];
+ if (config->hd_sd == 0) {
+ vpif_dbg(2, debug, "SD format\n");
+ if (config->stdid & vid_ch->stdid) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ } else {
+ vpif_dbg(2, debug, "HD format\n");
+ if (!memcmp(&config->dv_timings, &vid_ch->dv_timings,
+ sizeof(vid_ch->dv_timings))) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ }
+ }
+
+ /* standard not found */
+ if (index == vpif_ch_params_count)
+ return -EINVAL;
+
+ common->fmt.fmt.pix.width = std_info->width;
+ common->width = std_info->width;
+ common->fmt.fmt.pix.height = std_info->height;
+ common->height = std_info->height;
+ common->fmt.fmt.pix.bytesperline = std_info->width;
+ vpifparams->video_params.hpitch = std_info->width;
+ vpifparams->video_params.storage_mode = std_info->frm_fmt;
+
+ return 0;
+}
+
+/**
+ * vpif_calculate_offsets : This function calculates buffers offsets
+ * @ch : ptr to channel object
+ *
+ * This function calculates buffer offsets for Y and C in the top and
+ * bottom field
+ */
+static void vpif_calculate_offsets(struct channel_obj *ch)
+{
+ unsigned int hpitch, vpitch, sizeimage;
+ struct video_obj *vid_ch = &(ch->video);
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ enum v4l2_field field = common->fmt.fmt.pix.field;
+
+ vpif_dbg(2, debug, "vpif_calculate_offsets\n");
+
+ if (V4L2_FIELD_ANY == field) {
+ if (vpifparams->std_info.frm_fmt)
+ vid_ch->buf_field = V4L2_FIELD_NONE;
+ else
+ vid_ch->buf_field = V4L2_FIELD_INTERLACED;
+ } else
+ vid_ch->buf_field = common->fmt.fmt.pix.field;
+
+ sizeimage = common->fmt.fmt.pix.sizeimage;
+
+ hpitch = common->fmt.fmt.pix.bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ytop_off = 0;
+ common->ybtm_off = hpitch;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = sizeimage / 2 + hpitch;
+ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ytop_off = 0;
+ common->ybtm_off = sizeimage / 4;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = common->ctop_off + sizeimage / 4;
+ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
+ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
+ common->ybtm_off = 0;
+ common->ytop_off = sizeimage / 4;
+ common->cbtm_off = sizeimage / 2;
+ common->ctop_off = common->cbtm_off + sizeimage / 4;
+ }
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field))
+ vpifparams->video_params.storage_mode = 1;
+ else
+ vpifparams->video_params.storage_mode = 0;
+
+ if (1 == vpifparams->std_info.frm_fmt)
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ else {
+ if ((field == V4L2_FIELD_ANY)
+ || (field == V4L2_FIELD_INTERLACED))
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline * 2;
+ else
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ }
+
+ ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid;
+}
+
+/**
+ * vpif_config_format: configure default frame format in the device
+ * ch : ptr to channel object
+ */
+static void vpif_config_format(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_config_format\n");
+
+ common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (config_params.numbuffers[ch->channel_id] == 0)
+ common->memory = V4L2_MEMORY_USERPTR;
+ else
+ common->memory = V4L2_MEMORY_MMAP;
+
+ common->fmt.fmt.pix.sizeimage
+ = config_params.channel_bufsize[ch->channel_id];
+
+ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER)
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
+ else
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+/**
+ * vpif_get_default_field() - Get default field type based on interface
+ * @vpif_params - ptr to vpif params
+ */
+static inline enum v4l2_field vpif_get_default_field(
+ struct vpif_interface *iface)
+{
+ return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE :
+ V4L2_FIELD_INTERLACED;
+}
+
+/**
+ * vpif_check_format() - check given pixel format for compatibility
+ * @ch - channel ptr
+ * @pixfmt - Given pixel format
+ * @update - update the values as per hardware requirement
+ *
+ * Check the application pixel format for S_FMT and update the input
+ * values as per hardware limits for TRY_FMT. The default pixel and
+ * field format is selected based on interface type.
+ */
+static int vpif_check_format(struct channel_obj *ch,
+ struct v4l2_pix_format *pixfmt,
+ int update)
+{
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+ struct vpif_params *vpif_params = &ch->vpifparams;
+ enum v4l2_field field = pixfmt->field;
+ u32 sizeimage, hpitch, vpitch;
+ int ret = -EINVAL;
+
+ vpif_dbg(2, debug, "vpif_check_format\n");
+ /**
+ * first check for the pixel format. If if_type is Raw bayer,
+ * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only
+ * V4L2_PIX_FMT_YUV422P is supported
+ */
+ if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) {
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pix format\n");
+ goto exit;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ }
+ } else {
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pixel format\n");
+ goto exit;
+ }
+ pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+ }
+ }
+
+ if (!(VPIF_VALID_FIELD(field))) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid field format\n");
+ goto exit;
+ }
+ /**
+ * By default use FIELD_NONE for RAW Bayer capture
+ * and FIELD_INTERLACED for other interfaces
+ */
+ field = vpif_get_default_field(&vpif_params->iface);
+ } else if (field == V4L2_FIELD_ANY)
+ /* unsupported field. Use default */
+ field = vpif_get_default_field(&vpif_params->iface);
+
+ /* validate the hpitch */
+ hpitch = pixfmt->bytesperline;
+ if (hpitch < vpif_params->std_info.width) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid hpitch\n");
+ goto exit;
+ }
+ hpitch = vpif_params->std_info.width;
+ }
+
+ sizeimage = pixfmt->sizeimage;
+
+ vpitch = sizeimage / (hpitch * 2);
+
+ /* validate the vpitch */
+ if (vpitch < vpif_params->std_info.height) {
+ if (!update) {
+ vpif_dbg(2, debug, "Invalid vpitch\n");
+ goto exit;
+ }
+ vpitch = vpif_params->std_info.height;
+ }
+
+ /* Check for 8 byte alignment */
+ if (!ALIGN(hpitch, 8)) {
+ if (!update) {
+ vpif_dbg(2, debug, "invalid pitch alignment\n");
+ goto exit;
+ }
+ /* adjust to next 8 byte boundary */
+ hpitch = (((hpitch + 7) / 8) * 8);
+ }
+ /* if update is set, modify the bytesperline and sizeimage */
+ if (update) {
+ pixfmt->bytesperline = hpitch;
+ pixfmt->sizeimage = hpitch * vpitch * 2;
+ }
+ /**
+ * Image width and height is always based on current standard width and
+ * height
+ */
+ pixfmt->width = common->fmt.fmt.pix.width;
+ pixfmt->height = common->fmt.fmt.pix.height;
+ return 0;
+exit:
+ return ret;
+}
+
+/**
+ * vpif_config_addr() - function to configure buffer address in vpif
+ * @ch - channel ptr
+ * @muxmode - channel mux mode
+ */
+static void vpif_config_addr(struct channel_obj *ch, int muxmode)
+{
+ struct common_obj *common;
+
+ vpif_dbg(2, debug, "vpif_config_addr\n");
+
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+
+ if (VPIF_CHANNEL1_VIDEO == ch->channel_id)
+ common->set_addr = ch1_set_videobuf_addr;
+ else if (2 == muxmode)
+ common->set_addr = ch0_set_videobuf_addr_yc_nmux;
+ else
+ common->set_addr = ch0_set_videobuf_addr;
+}
+
+/**
+ * vpif_mmap : It is used to map kernel space buffers into user spaces
+ * @filep: file pointer
+ * @vma: ptr to vm_area_struct
+ */
+static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ /* Get the channel object and file handle object */
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+ int ret;
+
+ vpif_dbg(2, debug, "vpif_mmap\n");
+
+ if (mutex_lock_interruptible(&common->lock))
+ return -ERESTARTSYS;
+ ret = vb2_mmap(&common->buffer_queue, vma);
+ mutex_unlock(&common->lock);
+ return ret;
+}
+
+/**
+ * vpif_poll: It is used for select/poll system call
+ * @filep: file pointer
+ * @wait: poll table to wait
+ */
+static unsigned int vpif_poll(struct file *filep, poll_table * wait)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *channel = fh->channel;
+ struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
+ unsigned int res = 0;
+
+ vpif_dbg(2, debug, "vpif_poll\n");
+
+ if (common->started) {
+ mutex_lock(&common->lock);
+ res = vb2_poll(&common->buffer_queue, filep, wait);
+ mutex_unlock(&common->lock);
+ }
+ return res;
+}
+
+/**
+ * vpif_open : vpif open handler
+ * @filep: file ptr
+ *
+ * It creates object of file handle structure and stores it in private_data
+ * member of filepointer
+ */
+static int vpif_open(struct file *filep)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct video_device *vdev = video_devdata(filep);
+ struct common_obj *common;
+ struct video_obj *vid_ch;
+ struct channel_obj *ch;
+ struct vpif_fh *fh;
+ int i;
+
+ vpif_dbg(2, debug, "vpif_open\n");
+
+ ch = video_get_drvdata(vdev);
+
+ vid_ch = &ch->video;
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (NULL == ch->curr_subdev_info) {
+ /**
+ * search through the sub device to see a registered
+ * sub device and make it as current sub device
+ */
+ for (i = 0; i < config->subdev_count; i++) {
+ if (vpif_obj.sd[i]) {
+ /* the sub device is registered */
+ ch->curr_subdev_info = &config->subdev_info[i];
+ /* make first input as the current input */
+ vid_ch->input_idx = 0;
+ break;
+ }
+ }
+ if (i == config->subdev_count) {
+ vpif_err("No sub device registered\n");
+ return -ENOENT;
+ }
+ }
+
+ /* Allocate memory for the file handle object */
+ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ vpif_err("unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ if (mutex_lock_interruptible(&common->lock)) {
+ kfree(fh);
+ return -ERESTARTSYS;
+ }
+ /* store pointer to fh in private_data member of filep */
+ filep->private_data = fh;
+ fh->channel = ch;
+ fh->initialized = 0;
+ /* If decoder is not initialized. initialize it */
+ if (!ch->initialized) {
+ fh->initialized = 1;
+ ch->initialized = 1;
+ memset(&(ch->vpifparams), 0, sizeof(struct vpif_params));
+ }
+ /* Increment channel usrs counter */
+ ch->usrs++;
+ /* Set io_allowed member to false */
+ fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&ch->prio, &fh->prio);
+ mutex_unlock(&common->lock);
+ return 0;
+}
+
+/**
+ * vpif_release : function to clean up file close
+ * @filep: file pointer
+ *
+ * This function deletes buffer queue, frees the buffers and the vpif file
+ * handle
+ */
+static int vpif_release(struct file *filep)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ vpif_dbg(2, debug, "vpif_release\n");
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ mutex_lock(&common->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ /* Reset io_usrs member of channel object */
+ common->io_usrs = 0;
+ /* Disable channel as per its device type and channel id */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+ common->started = 0;
+ /* Free buffers allocated */
+ vb2_queue_release(&common->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+ }
+
+ /* Decrement channel usrs counter */
+ ch->usrs--;
+
+ /* Close the priority */
+ v4l2_prio_close(&ch->prio, fh->prio);
+
+ if (fh->initialized)
+ ch->initialized = 0;
+
+ mutex_unlock(&common->lock);
+ filep->private_data = NULL;
+ kfree(fh);
+ return 0;
+}
+
+/**
+ * vpif_reqbufs() - request buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @reqbuf: request buffer structure ptr
+ */
+static int vpif_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ u8 index = 0;
+ struct vb2_queue *q;
+
+ vpif_dbg(2, debug, "vpif_reqbufs\n");
+
+ /**
+ * This file handle has not initialized the channel,
+ * It is not allowed to do settings
+ */
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
+ return -EINVAL;
+
+ index = VPIF_VIDEO_INDEX;
+
+ common = &ch->common[index];
+
+ if (0 != common->io_usrs)
+ return -EBUSY;
+
+ /* Initialize videobuf2 queue as per the buffer type */
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (!common->alloc_ctx) {
+ vpif_err("Failed to get the context\n");
+ return -EINVAL;
+ }
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_cap_buffer);
+
+ vb2_queue_init(q);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed[index] = 1;
+ /* Increment io usrs member of channel object to 1 */
+ common->io_usrs = 1;
+ /* Store type of memory requested in channel object */
+ common->memory = reqbuf->memory;
+ INIT_LIST_HEAD(&common->dma_queue);
+
+ /* Allocate buffers */
+ return vb2_reqbufs(&common->buffer_queue, reqbuf);
+}
+
+/**
+ * vpif_querybuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_querybuf\n");
+
+ if (common->fmt.type != buf->type)
+ return -EINVAL;
+
+ if (common->memory != V4L2_MEMORY_MMAP) {
+ vpif_dbg(1, debug, "Invalid memory\n");
+ return -EINVAL;
+ }
+
+ return vb2_querybuf(&common->buffer_queue, buf);
+}
+
+/**
+ * vpif_qbuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_buffer tbuf = *buf;
+
+ vpif_dbg(2, debug, "vpif_qbuf\n");
+
+ if (common->fmt.type != tbuf.type) {
+ vpif_err("invalid buffer type\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh io not allowed\n");
+ return -EACCES;
+ }
+
+ return vb2_qbuf(&common->buffer_queue, buf);
+}
+
+/**
+ * vpif_dqbuf() - query buffer handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buf: v4l2 buffer structure ptr
+ */
+static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ vpif_dbg(2, debug, "vpif_dqbuf\n");
+
+ return vb2_dqbuf(&common->buffer_queue, buf,
+ (file->f_flags & O_NONBLOCK));
+}
+
+/**
+ * vpif_streamon() - streamon handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buftype: v4l2 buffer type
+ */
+static int vpif_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
+ struct vpif_params *vpif;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_streamon\n");
+
+ vpif = &ch->vpifparams;
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ vpif_dbg(1, debug, "buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ /* If file handle is not allowed IO, return error */
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_dbg(1, debug, "io not allowed\n");
+ return -EACCES;
+ }
+
+ /* If Streaming is already started, return error */
+ if (common->started) {
+ vpif_dbg(1, debug, "channel->started\n");
+ return -EBUSY;
+ }
+
+ if ((ch->channel_id == VPIF_CHANNEL0_VIDEO &&
+ oth_ch->common[VPIF_VIDEO_INDEX].started &&
+ vpif->std_info.ycmux_mode == 0) ||
+ ((ch->channel_id == VPIF_CHANNEL1_VIDEO) &&
+ (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
+ vpif_dbg(1, debug, "other channel is being used\n");
+ return -EBUSY;
+ }
+
+ ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0);
+ if (ret)
+ return ret;
+
+ /* Enable streamon on the sub device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ s_stream, 1);
+
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ vpif_dbg(1, debug, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ /* Call vb2_streamon to start streaming in videobuf2 */
+ ret = vb2_streamon(&common->buffer_queue, buftype);
+ if (ret) {
+ vpif_dbg(1, debug, "vb2_streamon\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * vpif_streamoff() - streamoff handler
+ * @file: file ptr
+ * @priv: file handle
+ * @buftype: v4l2 buffer type
+ */
+static int vpif_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret;
+
+ vpif_dbg(2, debug, "vpif_streamoff\n");
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ vpif_dbg(1, debug, "buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ /* If io is allowed for this file handle, return error */
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_dbg(1, debug, "io not allowed\n");
+ return -EACCES;
+ }
+
+ /* If streaming is not started, return error */
+ if (!common->started) {
+ vpif_dbg(1, debug, "channel->started\n");
+ return -EINVAL;
+ }
+
+ /* disable channel */
+ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ } else {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+
+ common->started = 0;
+
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ s_stream, 0);
+
+ if (ret && (ret != -ENOIOCTLCMD))
+ vpif_dbg(1, debug, "stream off failed in subdev\n");
+
+ return vb2_streamoff(&common->buffer_queue, buftype);
+}
+
+/**
+ * vpif_map_sub_device_to_input() - Maps sub device to input
+ * @ch - ptr to channel
+ * @config - ptr to capture configuration
+ * @input_index - Given input index from application
+ * @sub_device_index - index into sd table
+ *
+ * lookup the sub device information for a given input index.
+ * we report all the inputs to application. inputs table also
+ * has sub device name for the each input
+ */
+static struct vpif_subdev_info *vpif_map_sub_device_to_input(
+ struct channel_obj *ch,
+ struct vpif_capture_config *vpif_cfg,
+ int input_index,
+ int *sub_device_index)
+{
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_subdev_info *subdev_info = NULL;
+ const char *subdev_name = NULL;
+ int i;
+
+ vpif_dbg(2, debug, "vpif_map_sub_device_to_input\n");
+
+ chan_cfg = &vpif_cfg->chan_config[ch->channel_id];
+
+ /**
+ * search through the inputs to find the sub device supporting
+ * the input
+ */
+ for (i = 0; i < chan_cfg->input_count; i++) {
+ /* For each sub device, loop through input */
+ if (i == input_index) {
+ subdev_name = chan_cfg->inputs[i].subdev_name;
+ break;
+ }
+ }
+
+ /* if reached maximum. return null */
+ if (i == chan_cfg->input_count || (NULL == subdev_name))
+ return subdev_info;
+
+ /* loop through the sub device list to get the sub device info */
+ for (i = 0; i < vpif_cfg->subdev_count; i++) {
+ subdev_info = &vpif_cfg->subdev_info[i];
+ if (!strcmp(subdev_info->name, subdev_name))
+ break;
+ }
+
+ if (i == vpif_cfg->subdev_count)
+ return subdev_info;
+
+ /* check if the sub device is registered */
+ if (NULL == vpif_obj.sd[i])
+ return NULL;
+
+ *sub_device_index = i;
+ return subdev_info;
+}
+
+/**
+ * vpif_querystd() - querystd handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ *
+ * This function is called to detect standard at the selected input
+ */
+static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_querystd\n");
+
+ /* Call querystd function of decoder device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
+ querystd, std_id);
+ if (ret < 0)
+ vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
+
+ return ret;
+}
+
+/**
+ * vpif_g_std() - get STD handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ */
+static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ vpif_dbg(2, debug, "vpif_g_std\n");
+
+ *std = ch->video.stdid;
+ return 0;
+}
+
+/**
+ * vpif_s_std() - set STD handler
+ * @file: file ptr
+ * @priv: file handle
+ * @std_id: ptr to std id
+ */
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ vpif_dbg(2, debug, "vpif_s_std\n");
+
+ if (common->started) {
+ vpif_err("streaming in progress\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+
+ /* Call encoder subdevice function to set the standard */
+ ch->video.stdid = *std_id;
+ memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
+
+ /* Get the information about the standard */
+ if (vpif_update_std_info(ch)) {
+ vpif_err("Error getting the standard info\n");
+ return -EINVAL;
+ }
+
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ /* set standard in the sub device */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ s_std, *std_id);
+ if (ret < 0)
+ vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
+ return ret;
+}
+
+/**
+ * vpif_enum_input() - ENUMINPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @input: ptr to input structure
+ */
+static int vpif_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+
+ if (input->index >= chan_cfg->input_count) {
+ vpif_dbg(1, debug, "Invalid input index\n");
+ return -EINVAL;
+ }
+
+ memcpy(input, &chan_cfg->inputs[input->index].input,
+ sizeof(*input));
+ return 0;
+}
+
+/**
+ * vpif_g_input() - Get INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: ptr to input index
+ */
+static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *index = vid_ch->input_idx;
+
+ return 0;
+}
+
+/**
+ * vpif_s_input() - Set INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: input index
+ */
+static int vpif_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+ struct vpif_capture_chan_config *chan_cfg;
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_subdev_info *subdev_info;
+ int ret = 0, sd_index = 0;
+ u32 input = 0, output = 0;
+
+ chan_cfg = &config->chan_config[ch->channel_id];
+
+ if (common->started) {
+ vpif_err("Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+ subdev_info = vpif_map_sub_device_to_input(ch, config, index,
+ &sd_index);
+ if (NULL == subdev_info) {
+ vpif_dbg(1, debug,
+ "couldn't lookup sub device for the input index\n");
+ return -EINVAL;
+ }
+
+ /* first setup input path from sub device to vpif */
+ if (config->setup_input_path) {
+ ret = config->setup_input_path(ch->channel_id,
+ subdev_info->name);
+ if (ret < 0) {
+ vpif_dbg(1, debug, "couldn't setup input path for the"
+ " sub device %s, for input index %d\n",
+ subdev_info->name, index);
+ return ret;
+ }
+ }
+
+ if (subdev_info->can_route) {
+ input = subdev_info->input;
+ output = subdev_info->output;
+ ret = v4l2_subdev_call(vpif_obj.sd[sd_index], video, s_routing,
+ input, output, 0);
+ if (ret < 0) {
+ vpif_dbg(1, debug, "Failed to set input\n");
+ return ret;
+ }
+ }
+ vid_ch->input_idx = index;
+ ch->curr_subdev_info = subdev_info;
+ ch->curr_sd_index = sd_index;
+ /* copy interface parameters to vpif */
+ ch->vpifparams.iface = subdev_info->vpif_if;
+
+ /* update tvnorms from the sub device input info */
+ ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std;
+ return ret;
+}
+
+/**
+ * vpif_enum_fmt_vid_cap() - ENUM_FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @index: input index
+ */
+static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ if (fmt->index != 0) {
+ vpif_dbg(1, debug, "Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) {
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb");
+ fmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
+ } else {
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
+ fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+ }
+ return 0;
+}
+
+/**
+ * vpif_try_fmt_vid_cap() - TRY_FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ return vpif_check_format(ch, pixfmt, 1);
+}
+
+
+/**
+ * vpif_g_fmt_vid_cap() - Set INPUT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* Check the validity of the buffer type */
+ if (common->fmt.type != fmt->type)
+ return -EINVAL;
+
+ /* Fill in the information about format */
+ *fmt = common->fmt;
+ return 0;
+}
+
+/**
+ * vpif_s_fmt_vid_cap() - Set FMT handler
+ * @file: file ptr
+ * @priv: file handle
+ * @fmt: ptr to v4l2 format structure
+ */
+static int vpif_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_pix_format *pixfmt;
+ int ret = 0;
+
+ vpif_dbg(2, debug, "%s\n", __func__);
+
+ /* If streaming is started, return error */
+ if (common->started) {
+ vpif_dbg(1, debug, "Streaming is started\n");
+ return -EBUSY;
+ }
+
+ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
+ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+
+ fh->initialized = 1;
+
+ pixfmt = &fmt->fmt.pix;
+ /* Check for valid field format */
+ ret = vpif_check_format(ch, pixfmt, 0);
+
+ if (ret)
+ return ret;
+ /* store the format in the channel object */
+ common->fmt = *fmt;
+ return 0;
+}
+
+/**
+ * vpif_querycap() - QUERYCAP handler
+ * @file: file ptr
+ * @priv: file handle
+ * @cap: ptr to v4l2_capability structure
+ */
+static int vpif_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpif_capture_config *config = vpif_dev->platform_data;
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, config->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+/**
+ * vpif_g_priority() - get priority handler
+ * @file: file ptr
+ * @priv: file handle
+ * @prio: ptr to v4l2_priority structure
+ */
+static int vpif_g_priority(struct file *file, void *priv,
+ enum v4l2_priority *prio)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *prio = v4l2_prio_max(&ch->prio);
+
+ return 0;
+}
+
+/**
+ * vpif_s_priority() - set priority handler
+ * @file: file ptr
+ * @priv: file handle
+ * @prio: ptr to v4l2_priority structure
+ */
+static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_prio_change(&ch->prio, &fh->prio, p);
+}
+
+/**
+ * vpif_cropcap() - cropcap handler
+ * @file: file ptr
+ * @priv: file handle
+ * @crop: ptr to v4l2_cropcap structure
+ */
+static int vpif_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type)
+ return -EINVAL;
+
+ crop->bounds.left = 0;
+ crop->bounds.top = 0;
+ crop->bounds.height = common->height;
+ crop->bounds.width = common->width;
+ crop->defrect = crop->bounds;
+ return 0;
+}
+
+/**
+ * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: input timings
+ */
+static int
+vpif_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, enum_dv_timings, timings);
+}
+
+/**
+ * vpif_query_dv_timings() - QUERY_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: input timings
+ */
+static int
+vpif_query_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, query_dv_timings, timings);
+}
+
+/**
+ * vpif_s_dv_timings() - S_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+ int ret;
+
+ if (timings->type != V4L2_DV_BT_656_1120) {
+ vpif_dbg(2, debug, "Timing type not defined\n");
+ return -EINVAL;
+ }
+
+ /* Configure subdevice timings, if any */
+ ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
+ video, s_dv_timings, timings);
+ if (ret == -ENOIOCTLCMD) {
+ vpif_dbg(2, debug, "Custom DV timings not supported by "
+ "subdevice\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ vpif_dbg(2, debug, "Error setting custom DV timings\n");
+ return ret;
+ }
+
+ if (!(timings->bt.width && timings->bt.height &&
+ (timings->bt.hbackporch ||
+ timings->bt.hfrontporch ||
+ timings->bt.hsync) &&
+ timings->bt.vfrontporch &&
+ (timings->bt.vbackporch ||
+ timings->bt.vsync))) {
+ vpif_dbg(2, debug, "Timings for width, height, "
+ "horizontal back porch, horizontal sync, "
+ "horizontal front porch, vertical back porch, "
+ "vertical sync and vertical back porch "
+ "must be defined\n");
+ return -EINVAL;
+ }
+
+ vid_ch->dv_timings = *timings;
+
+ /* Configure video port timings */
+
+ std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
+ bt->hsync - 8;
+ std_info->sav2eav = bt->width;
+
+ std_info->l1 = 1;
+ std_info->l3 = bt->vsync + bt->vbackporch + 1;
+
+ if (bt->interlaced) {
+ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
+ std_info->vsize = bt->height * 2 +
+ bt->vfrontporch + bt->vsync + bt->vbackporch +
+ bt->il_vfrontporch + bt->il_vsync +
+ bt->il_vbackporch;
+ std_info->l5 = std_info->vsize/2 -
+ (bt->vfrontporch - 1);
+ std_info->l7 = std_info->vsize/2 + 1;
+ std_info->l9 = std_info->l7 + bt->il_vsync +
+ bt->il_vbackporch + 1;
+ std_info->l11 = std_info->vsize -
+ (bt->il_vfrontporch - 1);
+ } else {
+ vpif_dbg(2, debug, "Required timing values for "
+ "interlaced BT format missing\n");
+ return -EINVAL;
+ }
+ } else {
+ std_info->vsize = bt->height + bt->vfrontporch +
+ bt->vsync + bt->vbackporch;
+ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
+ }
+ strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
+ std_info->width = bt->width;
+ std_info->height = bt->height;
+ std_info->frm_fmt = bt->interlaced ? 0 : 1;
+ std_info->ycmux_mode = 0;
+ std_info->capture_format = 0;
+ std_info->vbi_supported = 0;
+ std_info->hd_sd = 1;
+ std_info->stdid = 0;
+
+ vid_ch->stdid = 0;
+ return 0;
+}
+
+/**
+ * vpif_g_dv_timings() - G_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *timings = vid_ch->dv_timings;
+
+ return 0;
+}
+
+/*
+ * vpif_g_chip_ident() - Identify the chip
+ * @file: file ptr
+ * @priv: file handle
+ * @chip: chip identity
+ *
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
+ vpif_dbg(2, debug, "match_type is invalid.\n");
+ return -EINVAL;
+ }
+
+ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * vpif_dbg_g_register() - Read register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be read
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ g_register, reg);
+}
+
+/*
+ * vpif_dbg_s_register() - Write to register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be modified
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if write operations fails.
+ */
+static int vpif_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
+ s_register, reg);
+}
+#endif
+
+/*
+ * vpif_log_status() - Status information
+ * @file: file ptr
+ * @priv: file handle
+ *
+ * Returns zero.
+ */
+static int vpif_log_status(struct file *filep, void *priv)
+{
+ /* status for sub devices */
+ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
+
+ return 0;
+}
+
+/* vpif capture ioctl operations */
+static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
+ .vidioc_querycap = vpif_querycap,
+ .vidioc_g_priority = vpif_g_priority,
+ .vidioc_s_priority = vpif_s_priority,
+ .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap,
+ .vidioc_enum_input = vpif_enum_input,
+ .vidioc_s_input = vpif_s_input,
+ .vidioc_g_input = vpif_g_input,
+ .vidioc_reqbufs = vpif_reqbufs,
+ .vidioc_querybuf = vpif_querybuf,
+ .vidioc_querystd = vpif_querystd,
+ .vidioc_s_std = vpif_s_std,
+ .vidioc_g_std = vpif_g_std,
+ .vidioc_qbuf = vpif_qbuf,
+ .vidioc_dqbuf = vpif_dqbuf,
+ .vidioc_streamon = vpif_streamon,
+ .vidioc_streamoff = vpif_streamoff,
+ .vidioc_cropcap = vpif_cropcap,
+ .vidioc_enum_dv_timings = vpif_enum_dv_timings,
+ .vidioc_query_dv_timings = vpif_query_dv_timings,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+ .vidioc_g_chip_ident = vpif_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpif_dbg_g_register,
+ .vidioc_s_register = vpif_dbg_s_register,
+#endif
+ .vidioc_log_status = vpif_log_status,
+};
+
+/* vpif file operations */
+static struct v4l2_file_operations vpif_fops = {
+ .owner = THIS_MODULE,
+ .open = vpif_open,
+ .release = vpif_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpif_mmap,
+ .poll = vpif_poll
+};
+
+/* vpif video template */
+static struct video_device vpif_video_template = {
+ .name = "vpif",
+ .fops = &vpif_fops,
+ .minor = -1,
+ .ioctl_ops = &vpif_ioctl_ops,
+};
+
+/**
+ * initialize_vpif() - Initialize vpif data structures
+ *
+ * Allocate memory for data structures and initialize them
+ */
+static int initialize_vpif(void)
+{
+ int err = 0, i, j;
+ int free_channel_objects_index;
+
+ /* Default number of buffers should be 3 */
+ if ((ch0_numbuffers > 0) &&
+ (ch0_numbuffers < config_params.min_numbuffers))
+ ch0_numbuffers = config_params.min_numbuffers;
+ if ((ch1_numbuffers > 0) &&
+ (ch1_numbuffers < config_params.min_numbuffers))
+ ch1_numbuffers = config_params.min_numbuffers;
+
+ /* Set buffer size to min buffers size if it is invalid */
+ if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO])
+ ch0_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL0_VIDEO];
+ if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO])
+ ch1_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL1_VIDEO];
+
+ config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers;
+ config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers;
+ if (ch0_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO]
+ = ch0_bufsize;
+ }
+ if (ch1_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO]
+ = ch1_bufsize;
+ }
+
+ /* Allocate memory for six channel objects */
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ vpif_obj.dev[i] =
+ kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL);
+ /* If memory allocation fails, return error */
+ if (!vpif_obj.dev[i]) {
+ free_channel_objects_index = i;
+ err = -ENOMEM;
+ goto vpif_init_free_channel_objects;
+ }
+ }
+ return 0;
+
+vpif_init_free_channel_objects:
+ for (j = 0; j < free_channel_objects_index; j++)
+ kfree(vpif_obj.dev[j]);
+ return err;
+}
+
+/**
+ * vpif_probe : This function probes the vpif capture driver
+ * @pdev: platform device pointer
+ *
+ * This creates device entries by register itself to the V4L2 driver and
+ * initializes fields of each channel objects
+ */
+static __init int vpif_probe(struct platform_device *pdev)
+{
+ struct vpif_subdev_info *subdevdata;
+ struct vpif_capture_config *config;
+ int i, j, k, m, q, err;
+ struct i2c_adapter *i2c_adap;
+ struct channel_obj *ch;
+ struct common_obj *common;
+ struct video_device *vfd;
+ struct resource *res;
+ int subdev_count;
+ size_t size;
+
+ vpif_dev = &pdev->dev;
+
+ err = initialize_vpif();
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
+ return err;
+ }
+
+ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
+ return err;
+ }
+
+ k = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
+ for (i = res->start; i <= res->end; i++) {
+ if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+ "VPIF_Capture",
+ (void *)(&vpif_obj.dev[k]->channel_id))) {
+ err = -EBUSY;
+ i--;
+ goto vpif_int_err;
+ }
+ }
+ k++;
+ }
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (NULL == vfd) {
+ for (j = 0; j < i; j++) {
+ ch = vpif_obj.dev[j];
+ video_device_release(ch->video_dev);
+ }
+ err = -ENOMEM;
+ goto vpif_dev_alloc_err;
+ }
+
+ /* Initialize field of video device */
+ *vfd = vpif_video_template;
+ vfd->v4l2_dev = &vpif_obj.v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "VPIF_Capture_DRIVER_V%s",
+ VPIF_CAPTURE_VERSION);
+ /* Set video_dev to the video device */
+ ch->video_dev = vfd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ size = resource_size(res);
+ /* The resources are divided into two equal memory and when we
+ * have HD output we can add them together
+ */
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ /* only enabled if second resource exists */
+ config_params.video_limit[ch->channel_id] = 0;
+ if (size)
+ config_params.video_limit[ch->channel_id] =
+ size/2;
+ }
+ }
+
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ ch->video_dev->lock = &common->lock;
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 1 : 0));
+ if (err)
+ goto probe_out;
+
+ video_set_drvdata(ch->video_dev, ch);
+
+ }
+
+ i2c_adap = i2c_get_adapter(1);
+ config = pdev->dev.platform_data;
+
+ subdev_count = config->subdev_count;
+ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+ GFP_KERNEL);
+ if (vpif_obj.sd == NULL) {
+ vpif_err("unable to allocate memory for subdevice pointers\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+
+ for (i = 0; i < subdev_count; i++) {
+ subdevdata = &config->subdev_info[i];
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata->board_info,
+ NULL);
+
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ goto probe_subdev_out;
+ }
+ v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
+ subdevdata->name);
+
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
+ return 0;
+
+probe_subdev_out:
+ /* free sub devices memory */
+ kfree(vpif_obj.sd);
+
+ j = VPIF_CAPTURE_MAX_DEVICES;
+probe_out:
+ for (k = 0; k < j; k++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[k];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+
+vpif_dev_alloc_err:
+ k = VPIF_CAPTURE_MAX_DEVICES-1;
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, k);
+ i = res->end;
+
+vpif_int_err:
+ for (q = k; q >= 0; q--) {
+ for (m = i; m >= (int)res->start; m--)
+ free_irq(m, (void *)(&vpif_obj.dev[q]->channel_id));
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, q-1);
+ if (res)
+ i = res->end;
+ }
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ return err;
+}
+
+/**
+ * vpif_remove() - driver remove handler
+ * @device: ptr to platform device structure
+ *
+ * The vidoe device is unregistered
+ */
+static int vpif_remove(struct platform_device *device)
+{
+ int i;
+ struct channel_obj *ch;
+
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ /* un-register device */
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * vpif_suspend: vpif device suspend
+ */
+static int vpif_suspend(struct device *dev)
+{
+
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (ch->usrs && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(0);
+ channel0_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ common->started == 2) {
+ enable_channel1(0);
+ channel1_intr_enable(0);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+/*
+ * vpif_resume: vpif device suspend
+ */
+static int vpif_resume(struct device *dev)
+{
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (ch->usrs && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
+ enable_channel0(1);
+ channel0_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
+ common->started == 2) {
+ enable_channel1(1);
+ channel1_intr_enable(1);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops vpif_dev_pm_ops = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_dev_pm_ops)
+#else
+#define vpif_pm_ops NULL
+#endif
+
+static __refdata struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif_capture",
+ .owner = THIS_MODULE,
+ .pm = vpif_pm_ops,
+ },
+ .probe = vpif_probe,
+ .remove = vpif_remove,
+};
+
+/**
+ * vpif_init: initialize the vpif driver
+ *
+ * This function registers device and driver to the kernel, requests irq
+ * handler and allocates memory
+ * for channel objects
+ */
+static __init int vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+
+/**
+ * vpif_cleanup : This function clean up the vpif capture resources
+ *
+ * This will un-registers device and driver to the kernel, frees
+ * requested irq handler and de-allocates memory allocated for channel
+ * objects.
+ */
+static void vpif_cleanup(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq_num;
+ int i = 0;
+
+ pdev = container_of(vpif_dev, struct platform_device, dev);
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
+ for (irq_num = res->start; irq_num <= res->end; irq_num++)
+ free_irq(irq_num,
+ (void *)(&vpif_obj.dev[i]->channel_id));
+ i++;
+ }
+
+ platform_driver_unregister(&vpif_driver);
+
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++)
+ kfree(vpif_obj.dev[i]);
+}
+
+/* Function for module initialization and cleanup */
+module_init(vpif_init);
+module_exit(vpif_cleanup);
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h
new file mode 100644
index 000000000000..d24efc17e4c8
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif_capture.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2009 Texas Instruments Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 VPIF_CAPTURE_H
+#define VPIF_CAPTURE_H
+
+#ifdef __KERNEL__
+
+/* Header files */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/davinci/vpif_types.h>
+
+#include "vpif.h"
+
+/* Macros */
+#define VPIF_CAPTURE_VERSION "0.0.2"
+
+#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
+ (V4L2_FIELD_NONE == field)) || \
+ (((V4L2_FIELD_INTERLACED == field) || \
+ (V4L2_FIELD_SEQ_TB == field)) || \
+ (V4L2_FIELD_SEQ_BT == field)))
+
+#define VPIF_CAPTURE_MAX_DEVICES 2
+#define VPIF_VIDEO_INDEX 0
+#define VPIF_NUMBER_OF_OBJECTS 1
+
+/* Enumerated data type to give id to each device per channel */
+enum vpif_channel_id {
+ VPIF_CHANNEL0_VIDEO = 0,
+ VPIF_CHANNEL1_VIDEO,
+};
+
+struct video_obj {
+ enum v4l2_field buf_field;
+ /* Currently selected or default standard */
+ v4l2_std_id stdid;
+ struct v4l2_dv_timings dv_timings;
+ /* This is to track the last input that is passed to application */
+ u32 input_idx;
+};
+
+struct vpif_cap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct common_obj {
+ /* Pointer pointing to current v4l2_buffer */
+ struct vpif_cap_buffer *cur_frm;
+ /* Pointer pointing to current v4l2_buffer */
+ struct vpif_cap_buffer *next_frm;
+ /*
+ * This field keeps track of type of buffer exchange mechanism
+ * user has selected
+ */
+ enum v4l2_memory memory;
+ /* Used to store pixel format */
+ struct v4l2_format fmt;
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ /* Used in video-buf */
+ spinlock_t irqlock;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* number of users performing IO */
+ u32 io_usrs;
+ /* Indicates whether streaming started */
+ u8 started;
+ /* Function pointer to set the addresses */
+ void (*set_addr) (unsigned long, unsigned long, unsigned long,
+ unsigned long);
+ /* offset where Y top starts from the starting of the buffer */
+ u32 ytop_off;
+ /* offset where Y bottom starts from the starting of the buffer */
+ u32 ybtm_off;
+ /* offset where C top starts from the starting of the buffer */
+ u32 ctop_off;
+ /* offset where C bottom starts from the starting of the buffer */
+ u32 cbtm_off;
+ /* Indicates width of the image data */
+ u32 width;
+ /* Indicates height of the image data */
+ u32 height;
+};
+
+struct channel_obj {
+ /* Identifies video device for this channel */
+ struct video_device *video_dev;
+ /* Used to keep track of state of the priority */
+ struct v4l2_prio_state prio;
+ /* number of open instances of the channel */
+ int usrs;
+ /* Indicates id of the field which is being displayed */
+ u32 field_id;
+ /* flag to indicate whether decoder is initialized */
+ u8 initialized;
+ /* Identifies channel */
+ enum vpif_channel_id channel_id;
+ /* index into sd table */
+ int curr_sd_index;
+ /* ptr to current sub device information */
+ struct vpif_subdev_info *curr_subdev_info;
+ /* vpif configuration params */
+ struct vpif_params vpifparams;
+ /* common object array */
+ struct common_obj common[VPIF_NUMBER_OF_OBJECTS];
+ /* video object */
+ struct video_obj video;
+};
+
+/* File handle structure */
+struct vpif_fh {
+ /* pointer to channel object for opened device */
+ struct channel_obj *channel;
+ /* Indicates whether this file handle is doing IO */
+ u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
+ /* Used to keep track priority of this instance */
+ enum v4l2_priority prio;
+ /* Used to indicate channel is initialize or not */
+ u8 initialized;
+};
+
+struct vpif_device {
+ struct v4l2_device v4l2_dev;
+ struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
+ struct v4l2_subdev **sd;
+};
+
+struct vpif_config_params {
+ u8 min_numbuffers;
+ u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS];
+ s8 device_type;
+ u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
+ u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
+ u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
+ u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
+ u8 max_device_type;
+};
+/* Struct which keeps track of the line numbers for the sliced vbi service */
+struct vpif_service_line {
+ u16 service_id;
+ u16 service_line[2];
+};
+#endif /* End of __KERNEL__ */
+#endif /* VPIF_CAPTURE_H */
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
new file mode 100644
index 000000000000..a5b88689abad
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -0,0 +1,1933 @@
+/*
+ * vpif-display - VPIF display driver
+ * Display driver for TI DaVinci VPIF
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+#include <asm/page.h>
+
+#include <media/adv7343.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "vpif_display.h"
+#include "vpif.h"
+
+MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VPIF_DISPLAY_VERSION);
+
+#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
+
+#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
+#define vpif_dbg(level, debug, fmt, arg...) \
+ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
+
+static int debug = 1;
+static u32 ch2_numbuffers = 3;
+static u32 ch3_numbuffers = 3;
+static u32 ch2_bufsize = 1920 * 1080 * 2;
+static u32 ch3_bufsize = 720 * 576 * 2;
+
+module_param(debug, int, 0644);
+module_param(ch2_numbuffers, uint, S_IRUGO);
+module_param(ch3_numbuffers, uint, S_IRUGO);
+module_param(ch2_bufsize, uint, S_IRUGO);
+module_param(ch3_bufsize, uint, S_IRUGO);
+
+MODULE_PARM_DESC(debug, "Debug level 0-1");
+MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)");
+MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)");
+MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)");
+MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)");
+
+static struct vpif_config_params config_params = {
+ .min_numbuffers = 3,
+ .numbuffers[0] = 3,
+ .numbuffers[1] = 3,
+ .min_bufsize[0] = 720 * 480 * 2,
+ .min_bufsize[1] = 720 * 480 * 2,
+ .channel_bufsize[0] = 1920 * 1080 * 2,
+ .channel_bufsize[1] = 720 * 576 * 2,
+};
+
+static struct vpif_device vpif_obj = { {NULL} };
+static struct device *vpif_dev;
+static void vpif_calculate_offsets(struct channel_obj *ch);
+static void vpif_config_addr(struct channel_obj *ch, int muxmode);
+
+/*
+ * buffer_prepare: This is the callback function called from vb2_qbuf()
+ * function the buffer is prepared and user space virtual address is converted
+ * into physical address
+ */
+static int vpif_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
+ struct common_obj *common;
+ unsigned long addr;
+
+ common = &fh->channel->common[VPIF_VIDEO_INDEX];
+ if (vb->state != VB2_BUF_STATE_ACTIVE &&
+ vb->state != VB2_BUF_STATE_PREPARED) {
+ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
+ goto buf_align_exit;
+
+ addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (q->streaming &&
+ (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
+ if (!ISALIGNED(addr + common->ytop_off) ||
+ !ISALIGNED(addr + common->ybtm_off) ||
+ !ISALIGNED(addr + common->ctop_off) ||
+ !ISALIGNED(addr + common->cbtm_off))
+ goto buf_align_exit;
+ }
+ }
+ return 0;
+
+buf_align_exit:
+ vpif_err("buffer offset not aligned to 8 bytes\n");
+ return -EINVAL;
+}
+
+/*
+ * vpif_buffer_queue_setup: This function allocates memory for the buffers
+ */
+static int vpif_buffer_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ unsigned long size;
+
+ if (V4L2_MEMORY_MMAP == common->memory) {
+ size = config_params.channel_bufsize[ch->channel_id];
+ /*
+ * Checking if the buffer size exceeds the available buffer
+ * ycmux_mode = 0 means 1 channel mode HD and
+ * ycmux_mode = 1 means 2 channels mode SD
+ */
+ if (ch->vpifparams.std_info.ycmux_mode == 0) {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ (config_params.video_limit[0]
+ + config_params.video_limit[1]))
+ (*nbuffers)--;
+ } else {
+ if (config_params.video_limit[ch->channel_id])
+ while (size * *nbuffers >
+ config_params.video_limit[ch->channel_id])
+ (*nbuffers)--;
+ }
+ } else {
+ size = common->fmt.fmt.pix.sizeimage;
+ }
+
+ if (*nbuffers < config_params.min_numbuffers)
+ *nbuffers = config_params.min_numbuffers;
+
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = common->alloc_ctx;
+ return 0;
+}
+
+/*
+ * vpif_buffer_queue: This function adds the buffer to DMA queue
+ */
+static void vpif_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* add the buffer to the DMA queue */
+ list_add_tail(&buf->list, &common->dma_queue);
+}
+
+/*
+ * vpif_buf_cleanup: This function is called from the videobuf2 layer to
+ * free memory allocated to the buffers
+ */
+static void vpif_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ unsigned long flags;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ spin_lock_irqsave(&common->irqlock, flags);
+ if (vb->state == VB2_BUF_STATE_ACTIVE)
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&common->irqlock, flags);
+}
+
+static void vpif_wait_prepare(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_unlock(&common->lock);
+}
+
+static void vpif_wait_finish(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+}
+
+static int vpif_buffer_init(struct vb2_buffer *vb)
+{
+ struct vpif_disp_buffer *buf = container_of(vb,
+ struct vpif_disp_buffer, vb);
+
+ INIT_LIST_HEAD(&buf->list);
+
+ return 0;
+}
+
+static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
+
+static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vpif_display_config *vpif_config_data =
+ vpif_dev->platform_data;
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpif = &ch->vpifparams;
+ unsigned long addr = 0;
+ int ret;
+
+ /* If buffer queue is empty, return error */
+ if (list_empty(&common->dma_queue)) {
+ vpif_err("buffer queue is empty\n");
+ return -EIO;
+ }
+
+ /* Get the next frame from the buffer queue */
+ common->next_frm = common->cur_frm =
+ list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+
+ list_del(&common->cur_frm->list);
+ /* Mark state of the current frame to active */
+ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ /* Initialize field_id and started member */
+ ch->field_id = 0;
+ common->started = 1;
+ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
+ /* Calculate the offset for Y and C data in the buffer */
+ vpif_calculate_offsets(ch);
+
+ if ((ch->vpifparams.std_info.frm_fmt &&
+ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
+ && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
+ || (!ch->vpifparams.std_info.frm_fmt
+ && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
+ vpif_err("conflict in field format and std format\n");
+ return -EINVAL;
+ }
+
+ /* clock settings */
+ ret =
+ vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
+ ch->vpifparams.std_info.hd_sd);
+ if (ret < 0) {
+ vpif_err("can't set clock\n");
+ return ret;
+ }
+
+ /* set the parameters and addresses */
+ ret = vpif_set_video_params(vpif, ch->channel_id + 2);
+ if (ret < 0)
+ return ret;
+
+ common->started = ret;
+ vpif_config_addr(ch, ret);
+ common->set_addr((addr + common->ytop_off),
+ (addr + common->ybtm_off),
+ (addr + common->ctop_off),
+ (addr + common->cbtm_off));
+
+ /* Set interrupt for both the fields in VPIF
+ Register enable channel in VPIF register */
+ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ channel2_intr_assert();
+ channel2_intr_enable(1);
+ enable_channel2(1);
+ if (vpif_config_data->ch2_clip_en)
+ channel2_clipping_enable(1);
+ }
+
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
+ || (common->started == 2)) {
+ channel3_intr_assert();
+ channel3_intr_enable(1);
+ enable_channel3(1);
+ if (vpif_config_data->ch3_clip_en)
+ channel3_clipping_enable(1);
+ }
+
+ return 0;
+}
+
+/* abort streaming and wait for last buffer */
+static int vpif_stop_streaming(struct vb2_queue *vq)
+{
+ struct vpif_fh *fh = vb2_get_drv_priv(vq);
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* release all active buffers */
+ while (!list_empty(&common->dma_queue)) {
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+ list_del(&common->next_frm->list);
+ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops video_qops = {
+ .queue_setup = vpif_buffer_queue_setup,
+ .wait_prepare = vpif_wait_prepare,
+ .wait_finish = vpif_wait_finish,
+ .buf_init = vpif_buffer_init,
+ .buf_prepare = vpif_buffer_prepare,
+ .start_streaming = vpif_start_streaming,
+ .stop_streaming = vpif_stop_streaming,
+ .buf_cleanup = vpif_buf_cleanup,
+ .buf_queue = vpif_buffer_queue,
+};
+
+static void process_progressive_mode(struct common_obj *common)
+{
+ unsigned long addr = 0;
+
+ /* Get the next buffer from buffer queue */
+ common->next_frm = list_entry(common->dma_queue.next,
+ struct vpif_disp_buffer, list);
+ /* Remove that buffer from the buffer queue */
+ list_del(&common->next_frm->list);
+ /* Mark status of the buffer as active */
+ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
+
+ /* Set top and bottom field addrs in VPIF registers */
+ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
+ common->set_addr(addr + common->ytop_off,
+ addr + common->ybtm_off,
+ addr + common->ctop_off,
+ addr + common->cbtm_off);
+}
+
+static void process_interlaced_mode(int fid, struct common_obj *common)
+{
+ /* device field id and local field id are in sync */
+ /* If this is even field */
+ if (0 == fid) {
+ if (common->cur_frm == common->next_frm)
+ return;
+
+ /* one frame is displayed If next frame is
+ * available, release cur_frm and move on */
+ /* Copy frame display time */
+ do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
+ /* Change status of the cur_frm */
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
+ /* Make cur_frm pointing to next_frm */
+ common->cur_frm = common->next_frm;
+
+ } else if (1 == fid) { /* odd field */
+ if (list_empty(&common->dma_queue)
+ || (common->cur_frm != common->next_frm)) {
+ return;
+ }
+ /* one field is displayed configure the next
+ * frame if it is available else hold on current
+ * frame */
+ /* Get next from the buffer queue */
+ process_progressive_mode(common);
+
+ }
+}
+
+/*
+ * vpif_channel_isr: It changes status of the displayed buffer, takes next
+ * buffer from the queue and sets its address in VPIF registers
+ */
+static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
+{
+ struct vpif_device *dev = &vpif_obj;
+ struct channel_obj *ch;
+ struct common_obj *common;
+ enum v4l2_field field;
+ int fid = -1, i;
+ int channel_id = 0;
+
+ channel_id = *(int *)(dev_id);
+ if (!vpif_intr_status(channel_id + 2))
+ return IRQ_NONE;
+
+ ch = dev->dev[channel_id];
+ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
+ for (i = 0; i < VPIF_NUMOBJECTS; i++) {
+ common = &ch->common[i];
+ /* If streaming is started in this channel */
+ if (0 == common->started)
+ continue;
+
+ if (1 == ch->vpifparams.std_info.frm_fmt) {
+ if (list_empty(&common->dma_queue))
+ continue;
+
+ /* Progressive mode */
+ if (!channel_first_int[i][channel_id]) {
+ /* Mark status of the cur_frm to
+ * done and unlock semaphore on it */
+ do_gettimeofday(&common->cur_frm->vb.
+ v4l2_buf.timestamp);
+ vb2_buffer_done(&common->cur_frm->vb,
+ VB2_BUF_STATE_DONE);
+ /* Make cur_frm pointing to next_frm */
+ common->cur_frm = common->next_frm;
+ }
+
+ channel_first_int[i][channel_id] = 0;
+ process_progressive_mode(common);
+ } else {
+ /* Interlaced mode */
+ /* If it is first interrupt, ignore it */
+
+ if (channel_first_int[i][channel_id]) {
+ channel_first_int[i][channel_id] = 0;
+ continue;
+ }
+
+ if (0 == i) {
+ ch->field_id ^= 1;
+ /* Get field id from VPIF registers */
+ fid = vpif_channel_getfid(ch->channel_id + 2);
+ /* If fid does not match with stored field id */
+ if (fid != ch->field_id) {
+ /* Make them in sync */
+ if (0 == fid)
+ ch->field_id = fid;
+
+ return IRQ_HANDLED;
+ }
+ }
+ process_interlaced_mode(fid, common);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int vpif_update_std_info(struct channel_obj *ch)
+{
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ const struct vpif_channel_config_params *config;
+
+ int i;
+
+ for (i = 0; i < vpif_ch_params_count; i++) {
+ config = &ch_params[i];
+ if (config->hd_sd == 0) {
+ vpif_dbg(2, debug, "SD format\n");
+ if (config->stdid & vid_ch->stdid) {
+ memcpy(std_info, config, sizeof(*config));
+ break;
+ }
+ }
+ }
+
+ if (i == vpif_ch_params_count) {
+ vpif_dbg(1, debug, "Format not found\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpif_update_resolution(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct video_obj *vid_ch = &ch->video;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+
+ if (!vid_ch->stdid && !vid_ch->dv_timings.bt.height)
+ return -EINVAL;
+
+ if (vid_ch->stdid) {
+ if (vpif_update_std_info(ch))
+ return -EINVAL;
+ }
+
+ common->fmt.fmt.pix.width = std_info->width;
+ common->fmt.fmt.pix.height = std_info->height;
+ vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n",
+ common->fmt.fmt.pix.width, common->fmt.fmt.pix.height);
+
+ /* Set height and width paramateres */
+ common->height = std_info->height;
+ common->width = std_info->width;
+
+ return 0;
+}
+
+/*
+ * vpif_calculate_offsets: This function calculates buffers offset for Y and C
+ * in the top and bottom field
+ */
+static void vpif_calculate_offsets(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ enum v4l2_field field = common->fmt.fmt.pix.field;
+ struct video_obj *vid_ch = &ch->video;
+ unsigned int hpitch, vpitch, sizeimage;
+
+ if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) {
+ if (ch->vpifparams.std_info.frm_fmt)
+ vid_ch->buf_field = V4L2_FIELD_NONE;
+ else
+ vid_ch->buf_field = V4L2_FIELD_INTERLACED;
+ } else {
+ vid_ch->buf_field = common->fmt.fmt.pix.field;
+ }
+
+ sizeimage = common->fmt.fmt.pix.sizeimage;
+
+ hpitch = common->fmt.fmt.pix.bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ common->ytop_off = 0;
+ common->ybtm_off = hpitch;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = sizeimage / 2 + hpitch;
+ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
+ common->ytop_off = 0;
+ common->ybtm_off = sizeimage / 4;
+ common->ctop_off = sizeimage / 2;
+ common->cbtm_off = common->ctop_off + sizeimage / 4;
+ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
+ common->ybtm_off = 0;
+ common->ytop_off = sizeimage / 4;
+ common->cbtm_off = sizeimage / 2;
+ common->ctop_off = common->cbtm_off + sizeimage / 4;
+ }
+
+ if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
+ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
+ vpifparams->video_params.storage_mode = 1;
+ } else {
+ vpifparams->video_params.storage_mode = 0;
+ }
+
+ if (ch->vpifparams.std_info.frm_fmt == 1) {
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ } else {
+ if ((field == V4L2_FIELD_ANY) ||
+ (field == V4L2_FIELD_INTERLACED))
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline * 2;
+ else
+ vpifparams->video_params.hpitch =
+ common->fmt.fmt.pix.bytesperline;
+ }
+
+ ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;
+}
+
+static void vpif_config_format(struct channel_obj *ch)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (config_params.numbuffers[ch->channel_id] == 0)
+ common->memory = V4L2_MEMORY_USERPTR;
+ else
+ common->memory = V4L2_MEMORY_MMAP;
+
+ common->fmt.fmt.pix.sizeimage =
+ config_params.channel_bufsize[ch->channel_id];
+ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
+ common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+}
+
+static int vpif_check_format(struct channel_obj *ch,
+ struct v4l2_pix_format *pixfmt)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ enum v4l2_field field = pixfmt->field;
+ u32 sizeimage, hpitch, vpitch;
+
+ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
+ goto invalid_fmt_exit;
+
+ if (!(VPIF_VALID_FIELD(field)))
+ goto invalid_fmt_exit;
+
+ if (pixfmt->bytesperline <= 0)
+ goto invalid_pitch_exit;
+
+ sizeimage = pixfmt->sizeimage;
+
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+
+ hpitch = pixfmt->bytesperline;
+ vpitch = sizeimage / (hpitch * 2);
+
+ /* Check for valid value of pitch */
+ if ((hpitch < ch->vpifparams.std_info.width) ||
+ (vpitch < ch->vpifparams.std_info.height))
+ goto invalid_pitch_exit;
+
+ /* Check for 8 byte alignment */
+ if (!ISALIGNED(hpitch)) {
+ vpif_err("invalid pitch alignment\n");
+ return -EINVAL;
+ }
+ pixfmt->width = common->fmt.fmt.pix.width;
+ pixfmt->height = common->fmt.fmt.pix.height;
+
+ return 0;
+
+invalid_fmt_exit:
+ vpif_err("invalid field format\n");
+ return -EINVAL;
+
+invalid_pitch_exit:
+ vpif_err("invalid pitch\n");
+ return -EINVAL;
+}
+
+static void vpif_config_addr(struct channel_obj *ch, int muxmode)
+{
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (VPIF_CHANNEL3_VIDEO == ch->channel_id) {
+ common->set_addr = ch3_set_videobuf_addr;
+ } else {
+ if (2 == muxmode)
+ common->set_addr = ch2_set_videobuf_addr_yc_nmux;
+ else
+ common->set_addr = ch2_set_videobuf_addr;
+ }
+}
+
+/*
+ * vpif_mmap: It is used to map kernel space buffers into user spaces
+ */
+static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
+ int ret;
+
+ vpif_dbg(2, debug, "vpif_mmap\n");
+
+ if (mutex_lock_interruptible(&common->lock))
+ return -ERESTARTSYS;
+ ret = vb2_mmap(&common->buffer_queue, vma);
+ mutex_unlock(&common->lock);
+ return ret;
+}
+
+/*
+ * vpif_poll: It is used for select/poll system call
+ */
+static unsigned int vpif_poll(struct file *filep, poll_table *wait)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ unsigned int res = 0;
+
+ if (common->started) {
+ mutex_lock(&common->lock);
+ res = vb2_poll(&common->buffer_queue, filep, wait);
+ mutex_unlock(&common->lock);
+ }
+
+ return res;
+}
+
+/*
+ * vpif_open: It creates object of file handle structure and stores it in
+ * private_data member of filepointer
+ */
+static int vpif_open(struct file *filep)
+{
+ struct video_device *vdev = video_devdata(filep);
+ struct channel_obj *ch = video_get_drvdata(vdev);
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_fh *fh;
+
+ /* Allocate memory for the file handle object */
+ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
+ if (fh == NULL) {
+ vpif_err("unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ if (mutex_lock_interruptible(&common->lock)) {
+ kfree(fh);
+ return -ERESTARTSYS;
+ }
+ /* store pointer to fh in private_data member of filep */
+ filep->private_data = fh;
+ fh->channel = ch;
+ fh->initialized = 0;
+ if (!ch->initialized) {
+ fh->initialized = 1;
+ ch->initialized = 1;
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+ }
+
+ /* Increment channel usrs counter */
+ atomic_inc(&ch->usrs);
+ /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
+ fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
+ /* Initialize priority of this instance to default priority */
+ fh->prio = V4L2_PRIORITY_UNSET;
+ v4l2_prio_open(&ch->prio, &fh->prio);
+ mutex_unlock(&common->lock);
+
+ return 0;
+}
+
+/*
+ * vpif_release: This function deletes buffer queue, frees the buffers and
+ * the vpif file handle
+ */
+static int vpif_release(struct file *filep)
+{
+ struct vpif_fh *fh = filep->private_data;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ mutex_lock(&common->lock);
+ /* if this instance is doing IO */
+ if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ /* Reset io_usrs member of channel object */
+ common->io_usrs = 0;
+ /* Disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ common->started = 0;
+
+ /* Free buffers allocated */
+ vb2_queue_release(&common->buffer_queue);
+ vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
+
+ common->numbuffers =
+ config_params.numbuffers[ch->channel_id];
+ }
+
+ /* Decrement channel usrs counter */
+ atomic_dec(&ch->usrs);
+ /* If this file handle has initialize encoder device, reset it */
+ if (fh->initialized)
+ ch->initialized = 0;
+
+ /* Close the priority */
+ v4l2_prio_close(&ch->prio, fh->prio);
+ filep->private_data = NULL;
+ fh->initialized = 0;
+ mutex_unlock(&common->lock);
+ kfree(fh);
+
+ return 0;
+}
+
+/* functions implementing ioctls */
+/**
+ * vpif_querycap() - QUERYCAP handler
+ * @file: file ptr
+ * @priv: file handle
+ * @cap: ptr to v4l2_capability structure
+ */
+static int vpif_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vpif_display_config *config = vpif_dev->platform_data;
+
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, "vpif display", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, config->card_name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int vpif_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index != 0) {
+ vpif_err("Invalid format index\n");
+ return -EINVAL;
+ }
+
+ /* Fill in the information about format */
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
+ fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
+
+ return 0;
+}
+
+static int vpif_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ /* Check the validity of the buffer type */
+ if (common->fmt.type != fmt->type)
+ return -EINVAL;
+
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+ *fmt = common->fmt;
+ return 0;
+}
+
+static int vpif_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct v4l2_pix_format *pixfmt;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_dbg(1, debug, "Channel Busy\n");
+ return -EBUSY;
+ }
+
+ /* Check for the priority */
+ ret = v4l2_prio_check(&ch->prio, fh->prio);
+ if (0 != ret)
+ return ret;
+ fh->initialized = 1;
+ }
+
+ if (common->started) {
+ vpif_dbg(1, debug, "Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ pixfmt = &fmt->fmt.pix;
+ /* Check for valid field format */
+ ret = vpif_check_format(ch, pixfmt);
+ if (ret)
+ return ret;
+
+ /* store the pix format in the channel object */
+ common->fmt.fmt.pix = *pixfmt;
+ /* store the format in the channel object */
+ common->fmt = *fmt;
+ return 0;
+}
+
+static int vpif_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ int ret = 0;
+
+ ret = vpif_check_format(ch, pixfmt);
+ if (ret) {
+ *pixfmt = common->fmt.fmt.pix;
+ pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2;
+ }
+
+ return ret;
+}
+
+static int vpif_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common;
+ enum v4l2_field field;
+ struct vb2_queue *q;
+ u8 index = 0;
+
+ /* This file handle has not initialized the channel,
+ It is not allowed to do settings */
+ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
+ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
+ if (!fh->initialized) {
+ vpif_err("Channel Busy\n");
+ return -EBUSY;
+ }
+ }
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type)
+ return -EINVAL;
+
+ index = VPIF_VIDEO_INDEX;
+
+ common = &ch->common[index];
+
+ if (common->fmt.type != reqbuf->type || !vpif_dev)
+ return -EINVAL;
+ if (0 != common->io_usrs)
+ return -EBUSY;
+
+ if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_INTERLACED;
+ else
+ field = common->fmt.fmt.pix.field;
+ } else {
+ field = V4L2_VBI_INTERLACED;
+ }
+ /* Initialize videobuf2 queue as per the buffer type */
+ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
+ if (!common->alloc_ctx) {
+ vpif_err("Failed to get the context\n");
+ return -EINVAL;
+ }
+ q = &common->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = fh;
+ q->ops = &video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct vpif_disp_buffer);
+
+ vb2_queue_init(q);
+
+ /* Set io allowed member of file handle to TRUE */
+ fh->io_allowed[index] = 1;
+ /* Increment io usrs member of channel object to 1 */
+ common->io_usrs = 1;
+ /* Store type of memory requested in channel object */
+ common->memory = reqbuf->memory;
+ INIT_LIST_HEAD(&common->dma_queue);
+ /* Allocate buffers */
+ return vb2_reqbufs(&common->buffer_queue, reqbuf);
+}
+
+static int vpif_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *tbuf)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ if (common->fmt.type != tbuf->type)
+ return -EINVAL;
+
+ return vb2_querybuf(&common->buffer_queue, tbuf);
+}
+
+static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct vpif_fh *fh = NULL;
+ struct channel_obj *ch = NULL;
+ struct common_obj *common = NULL;
+
+ if (!buf || !priv)
+ return -EINVAL;
+
+ fh = priv;
+ ch = fh->channel;
+ if (!ch)
+ return -EINVAL;
+
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ if (common->fmt.type != buf->type)
+ return -EINVAL;
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ return vb2_qbuf(&common->buffer_queue, buf);
+}
+
+static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if (!(*std_id & VPIF_V4L2_STD))
+ return -EINVAL;
+
+ if (common->started) {
+ vpif_err("streaming in progress\n");
+ return -EBUSY;
+ }
+
+ /* Call encoder subdevice function to set the standard */
+ ch->video.stdid = *std_id;
+ memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings));
+ /* Get the information about the standard */
+ if (vpif_update_resolution(ch))
+ return -EINVAL;
+
+ if ((ch->vpifparams.std_info.width *
+ ch->vpifparams.std_info.height * 2) >
+ config_params.channel_bufsize[ch->channel_id]) {
+ vpif_err("invalid std for this size\n");
+ return -EINVAL;
+ }
+
+ common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width;
+ /* Configure the default format information */
+ vpif_config_format(ch);
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
+ s_std_output, *std_id);
+ if (ret < 0) {
+ vpif_err("Failed to set output standard\n");
+ return ret;
+ }
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core,
+ s_std, *std_id);
+ if (ret < 0)
+ vpif_err("Failed to set standard for sub devices\n");
+ return ret;
+}
+
+static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *std = ch->video.stdid;
+ return 0;
+}
+
+static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+
+ return vb2_dqbuf(&common->buffer_queue, p,
+ (file->f_flags & O_NONBLOCK));
+}
+
+static int vpif_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
+ int ret = 0;
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ vpif_err("buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ /* If Streaming is already started, return error */
+ if (common->started) {
+ vpif_err("channel->started\n");
+ return -EBUSY;
+ }
+
+ if ((ch->channel_id == VPIF_CHANNEL2_VIDEO
+ && oth_ch->common[VPIF_VIDEO_INDEX].started &&
+ ch->vpifparams.std_info.ycmux_mode == 0)
+ || ((ch->channel_id == VPIF_CHANNEL3_VIDEO)
+ && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
+ vpif_err("other channel is using\n");
+ return -EBUSY;
+ }
+
+ ret = vpif_check_format(ch, &common->fmt.fmt.pix);
+ if (ret < 0)
+ return ret;
+
+ /* Call vb2_streamon to start streaming in videobuf2 */
+ ret = vb2_streamon(&common->buffer_queue, buftype);
+ if (ret < 0) {
+ vpif_err("vb2_streamon\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int vpif_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ struct vpif_display_config *vpif_config_data =
+ vpif_dev->platform_data;
+
+ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ vpif_err("buffer type not supported\n");
+ return -EINVAL;
+ }
+
+ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
+ vpif_err("fh->io_allowed\n");
+ return -EACCES;
+ }
+
+ if (!common->started) {
+ vpif_err("channel->started\n");
+ return -EINVAL;
+ }
+
+ if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /* disable channel */
+ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
+ if (vpif_config_data->ch2_clip_en)
+ channel2_clipping_enable(0);
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
+ (2 == common->started)) {
+ if (vpif_config_data->ch3_clip_en)
+ channel3_clipping_enable(0);
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ }
+
+ common->started = 0;
+ return vb2_streamoff(&common->buffer_queue, buftype);
+}
+
+static int vpif_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *crop)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type)
+ return -EINVAL;
+
+ crop->bounds.left = crop->bounds.top = 0;
+ crop->defrect.left = crop->defrect.top = 0;
+ crop->defrect.height = crop->bounds.height = common->height;
+ crop->defrect.width = crop->bounds.width = common->width;
+
+ return 0;
+}
+
+static int vpif_enum_output(struct file *file, void *fh,
+ struct v4l2_output *output)
+{
+
+ struct vpif_display_config *config = vpif_dev->platform_data;
+
+ if (output->index >= config->output_count) {
+ vpif_dbg(1, debug, "Invalid output index\n");
+ return -EINVAL;
+ }
+
+ strcpy(output->name, config->output[output->index]);
+ output->type = V4L2_OUTPUT_TYPE_ANALOG;
+ output->std = VPIF_V4L2_STD;
+
+ return 0;
+}
+
+static int vpif_s_output(struct file *file, void *priv, unsigned int i)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
+ int ret = 0;
+
+ if (common->started) {
+ vpif_err("Streaming in progress\n");
+ return -EBUSY;
+ }
+
+ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
+ s_routing, 0, i, 0);
+
+ if (ret < 0)
+ vpif_err("Failed to set output standard\n");
+
+ vid_ch->output_id = i;
+ return ret;
+}
+
+static int vpif_g_output(struct file *file, void *priv, unsigned int *i)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *i = vid_ch->output_id;
+
+ return 0;
+}
+
+static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ *p = v4l2_prio_max(&ch->prio);
+
+ return 0;
+}
+
+static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+
+ return v4l2_prio_change(&ch->prio, &fh->prio, p);
+}
+
+/**
+ * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: input timings
+ */
+static int
+vpif_enum_dv_timings(struct file *file, void *priv,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
+ video, enum_dv_timings, timings);
+}
+
+/**
+ * vpif_s_dv_timings() - S_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_s_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct vpif_params *vpifparams = &ch->vpifparams;
+ struct vpif_channel_config_params *std_info = &vpifparams->std_info;
+ struct video_obj *vid_ch = &ch->video;
+ struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt;
+ int ret;
+
+ if (timings->type != V4L2_DV_BT_656_1120) {
+ vpif_dbg(2, debug, "Timing type not defined\n");
+ return -EINVAL;
+ }
+
+ /* Configure subdevice timings, if any */
+ ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
+ video, s_dv_timings, timings);
+ if (ret == -ENOIOCTLCMD) {
+ vpif_dbg(2, debug, "Custom DV timings not supported by "
+ "subdevice\n");
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ vpif_dbg(2, debug, "Error setting custom DV timings\n");
+ return ret;
+ }
+
+ if (!(timings->bt.width && timings->bt.height &&
+ (timings->bt.hbackporch ||
+ timings->bt.hfrontporch ||
+ timings->bt.hsync) &&
+ timings->bt.vfrontporch &&
+ (timings->bt.vbackporch ||
+ timings->bt.vsync))) {
+ vpif_dbg(2, debug, "Timings for width, height, "
+ "horizontal back porch, horizontal sync, "
+ "horizontal front porch, vertical back porch, "
+ "vertical sync and vertical back porch "
+ "must be defined\n");
+ return -EINVAL;
+ }
+
+ vid_ch->dv_timings = *timings;
+
+ /* Configure video port timings */
+
+ std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
+ bt->hsync - 8;
+ std_info->sav2eav = bt->width;
+
+ std_info->l1 = 1;
+ std_info->l3 = bt->vsync + bt->vbackporch + 1;
+
+ if (bt->interlaced) {
+ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
+ std_info->vsize = bt->height * 2 +
+ bt->vfrontporch + bt->vsync + bt->vbackporch +
+ bt->il_vfrontporch + bt->il_vsync +
+ bt->il_vbackporch;
+ std_info->l5 = std_info->vsize/2 -
+ (bt->vfrontporch - 1);
+ std_info->l7 = std_info->vsize/2 + 1;
+ std_info->l9 = std_info->l7 + bt->il_vsync +
+ bt->il_vbackporch + 1;
+ std_info->l11 = std_info->vsize -
+ (bt->il_vfrontporch - 1);
+ } else {
+ vpif_dbg(2, debug, "Required timing values for "
+ "interlaced BT format missing\n");
+ return -EINVAL;
+ }
+ } else {
+ std_info->vsize = bt->height + bt->vfrontporch +
+ bt->vsync + bt->vbackporch;
+ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
+ }
+ strncpy(std_info->name, "Custom timings BT656/1120",
+ VPIF_MAX_NAME);
+ std_info->width = bt->width;
+ std_info->height = bt->height;
+ std_info->frm_fmt = bt->interlaced ? 0 : 1;
+ std_info->ycmux_mode = 0;
+ std_info->capture_format = 0;
+ std_info->vbi_supported = 0;
+ std_info->hd_sd = 1;
+ std_info->stdid = 0;
+ vid_ch->stdid = 0;
+
+ return 0;
+}
+
+/**
+ * vpif_g_dv_timings() - G_DV_TIMINGS handler
+ * @file: file ptr
+ * @priv: file handle
+ * @timings: digital video timings
+ */
+static int vpif_g_dv_timings(struct file *file, void *priv,
+ struct v4l2_dv_timings *timings)
+{
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ *timings = vid_ch->dv_timings;
+
+ return 0;
+}
+
+/*
+ * vpif_g_chip_ident() - Identify the chip
+ * @file: file ptr
+ * @priv: file handle
+ * @chip: chip identity
+ *
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
+ vpif_dbg(2, debug, "match_type is invalid.\n");
+ return -EINVAL;
+ }
+
+ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/*
+ * vpif_dbg_g_register() - Read register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be read
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if read operations fails.
+ */
+static int vpif_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
+ g_register, reg);
+}
+
+/*
+ * vpif_dbg_s_register() - Write to register
+ * @file: file ptr
+ * @priv: file handle
+ * @reg: register to be modified
+ *
+ * Debugging only
+ * Returns zero or -EINVAL if write operations fails.
+ */
+static int vpif_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg){
+ struct vpif_fh *fh = priv;
+ struct channel_obj *ch = fh->channel;
+ struct video_obj *vid_ch = &ch->video;
+
+ return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
+ s_register, reg);
+}
+#endif
+
+/*
+ * vpif_log_status() - Status information
+ * @file: file ptr
+ * @priv: file handle
+ *
+ * Returns zero.
+ */
+static int vpif_log_status(struct file *filep, void *priv)
+{
+ /* status for sub devices */
+ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
+
+ return 0;
+}
+
+/* vpif display ioctl operations */
+static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
+ .vidioc_querycap = vpif_querycap,
+ .vidioc_g_priority = vpif_g_priority,
+ .vidioc_s_priority = vpif_s_priority,
+ .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out,
+ .vidioc_reqbufs = vpif_reqbufs,
+ .vidioc_querybuf = vpif_querybuf,
+ .vidioc_qbuf = vpif_qbuf,
+ .vidioc_dqbuf = vpif_dqbuf,
+ .vidioc_streamon = vpif_streamon,
+ .vidioc_streamoff = vpif_streamoff,
+ .vidioc_s_std = vpif_s_std,
+ .vidioc_g_std = vpif_g_std,
+ .vidioc_enum_output = vpif_enum_output,
+ .vidioc_s_output = vpif_s_output,
+ .vidioc_g_output = vpif_g_output,
+ .vidioc_cropcap = vpif_cropcap,
+ .vidioc_enum_dv_timings = vpif_enum_dv_timings,
+ .vidioc_s_dv_timings = vpif_s_dv_timings,
+ .vidioc_g_dv_timings = vpif_g_dv_timings,
+ .vidioc_g_chip_ident = vpif_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vpif_dbg_g_register,
+ .vidioc_s_register = vpif_dbg_s_register,
+#endif
+ .vidioc_log_status = vpif_log_status,
+};
+
+static const struct v4l2_file_operations vpif_fops = {
+ .owner = THIS_MODULE,
+ .open = vpif_open,
+ .release = vpif_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vpif_mmap,
+ .poll = vpif_poll
+};
+
+static struct video_device vpif_video_template = {
+ .name = "vpif",
+ .fops = &vpif_fops,
+ .ioctl_ops = &vpif_ioctl_ops,
+ .tvnorms = VPIF_V4L2_STD,
+ .current_norm = V4L2_STD_625_50,
+
+};
+
+/*Configure the channels, buffer sizei, request irq */
+static int initialize_vpif(void)
+{
+ int free_channel_objects_index;
+ int free_buffer_channel_index;
+ int free_buffer_index;
+ int err = 0, i, j;
+
+ /* Default number of buffers should be 3 */
+ if ((ch2_numbuffers > 0) &&
+ (ch2_numbuffers < config_params.min_numbuffers))
+ ch2_numbuffers = config_params.min_numbuffers;
+ if ((ch3_numbuffers > 0) &&
+ (ch3_numbuffers < config_params.min_numbuffers))
+ ch3_numbuffers = config_params.min_numbuffers;
+
+ /* Set buffer size to min buffers size if invalid buffer size is
+ * given */
+ if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO])
+ ch2_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL2_VIDEO];
+ if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO])
+ ch3_bufsize =
+ config_params.min_bufsize[VPIF_CHANNEL3_VIDEO];
+
+ config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers;
+
+ if (ch2_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] =
+ ch2_bufsize;
+ }
+ config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers;
+
+ if (ch3_numbuffers) {
+ config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] =
+ ch3_bufsize;
+ }
+
+ /* Allocate memory for six channel objects */
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ vpif_obj.dev[i] =
+ kzalloc(sizeof(struct channel_obj), GFP_KERNEL);
+ /* If memory allocation fails, return error */
+ if (!vpif_obj.dev[i]) {
+ free_channel_objects_index = i;
+ err = -ENOMEM;
+ goto vpif_init_free_channel_objects;
+ }
+ }
+
+ free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES;
+ free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS;
+ free_buffer_index = config_params.numbuffers[i - 1];
+
+ return 0;
+
+vpif_init_free_channel_objects:
+ for (j = 0; j < free_channel_objects_index; j++)
+ kfree(vpif_obj.dev[j]);
+ return err;
+}
+
+/*
+ * vpif_probe: This function creates device entries by register itself to the
+ * V4L2 driver and initializes fields of each channel objects
+ */
+static __init int vpif_probe(struct platform_device *pdev)
+{
+ struct vpif_subdev_info *subdevdata;
+ struct vpif_display_config *config;
+ int i, j = 0, k, q, m, err = 0;
+ struct i2c_adapter *i2c_adap;
+ struct common_obj *common;
+ struct channel_obj *ch;
+ struct video_device *vfd;
+ struct resource *res;
+ int subdev_count;
+ size_t size;
+
+ vpif_dev = &pdev->dev;
+ err = initialize_vpif();
+
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
+ return err;
+ }
+
+ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
+ if (err) {
+ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
+ return err;
+ }
+
+ k = 0;
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
+ for (i = res->start; i <= res->end; i++) {
+ if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
+ "VPIF_Display",
+ (void *)(&vpif_obj.dev[k]->channel_id))) {
+ err = -EBUSY;
+ goto vpif_int_err;
+ }
+ }
+ k++;
+ }
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+
+ /* Allocate memory for video device */
+ vfd = video_device_alloc();
+ if (vfd == NULL) {
+ for (j = 0; j < i; j++) {
+ ch = vpif_obj.dev[j];
+ video_device_release(ch->video_dev);
+ }
+ err = -ENOMEM;
+ goto vpif_int_err;
+ }
+
+ /* Initialize field of video device */
+ *vfd = vpif_video_template;
+ vfd->v4l2_dev = &vpif_obj.v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->vfl_dir = VFL_DIR_TX;
+ snprintf(vfd->name, sizeof(vfd->name),
+ "VPIF_Display_DRIVER_V%s",
+ VPIF_DISPLAY_VERSION);
+
+ /* Set video_dev to the video device */
+ ch->video_dev = vfd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res) {
+ size = resource_size(res);
+ /* The resources are divided into two equal memory and when
+ * we have HD output we can add them together
+ */
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+
+ /* only enabled if second resource exists */
+ config_params.video_limit[ch->channel_id] = 0;
+ if (size)
+ config_params.video_limit[ch->channel_id] =
+ size/2;
+ }
+ }
+
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ /* Initialize field of the channel objects */
+ atomic_set(&ch->usrs, 0);
+ for (k = 0; k < VPIF_NUMOBJECTS; k++) {
+ ch->common[k].numbuffers = 0;
+ common = &ch->common[k];
+ common->io_usrs = 0;
+ common->started = 0;
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ common->numbuffers = 0;
+ common->set_addr = NULL;
+ common->ytop_off = common->ybtm_off = 0;
+ common->ctop_off = common->cbtm_off = 0;
+ common->cur_frm = common->next_frm = NULL;
+ memset(&common->fmt, 0, sizeof(common->fmt));
+ common->numbuffers = config_params.numbuffers[k];
+
+ }
+ ch->initialized = 0;
+ ch->channel_id = j;
+ if (j < 2)
+ ch->common[VPIF_VIDEO_INDEX].numbuffers =
+ config_params.numbuffers[ch->channel_id];
+ else
+ ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
+
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ ch->common[VPIF_VIDEO_INDEX].fmt.type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ch->video_dev->lock = &common->lock;
+
+ /* register video device */
+ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
+ (int)ch, (int)&ch->video_dev);
+
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 3 : 2));
+ if (err < 0)
+ goto probe_out;
+
+ video_set_drvdata(ch->video_dev, ch);
+ }
+
+ i2c_adap = i2c_get_adapter(1);
+ config = pdev->dev.platform_data;
+ subdev_count = config->subdev_count;
+ subdevdata = config->subdevinfo;
+ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
+ GFP_KERNEL);
+ if (vpif_obj.sd == NULL) {
+ vpif_err("unable to allocate memory for subdevice pointers\n");
+ err = -ENOMEM;
+ goto probe_out;
+ }
+
+ for (i = 0; i < subdev_count; i++) {
+ vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata[i].board_info,
+ NULL);
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ goto probe_subdev_out;
+ }
+
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev,
+ " VPIF display driver initialized\n");
+ return 0;
+
+probe_subdev_out:
+ kfree(vpif_obj.sd);
+probe_out:
+ for (k = 0; k < j; k++) {
+ ch = vpif_obj.dev[k];
+ video_unregister_device(ch->video_dev);
+ video_device_release(ch->video_dev);
+ ch->video_dev = NULL;
+ }
+vpif_int_err:
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+ vpif_err("VPIF IRQ request failed\n");
+ for (q = k; k >= 0; k--) {
+ for (m = i; m >= res->start; m--)
+ free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id));
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1);
+ m = res->end;
+ }
+
+ return err;
+}
+
+/*
+ * vpif_remove: It un-register channels from V4L2 driver
+ */
+static int vpif_remove(struct platform_device *device)
+{
+ struct channel_obj *ch;
+ int i;
+
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ /* un-register device */
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+
+ ch->video_dev = NULL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int vpif_suspend(struct device *dev)
+{
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (atomic_read(&ch->usrs) && common->io_usrs) {
+ /* Disable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(0);
+ channel2_intr_enable(0);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ common->started == 2) {
+ enable_channel3(0);
+ channel3_intr_enable(0);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+static int vpif_resume(struct device *dev)
+{
+
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i;
+
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[i];
+ common = &ch->common[VPIF_VIDEO_INDEX];
+ mutex_lock(&common->lock);
+ if (atomic_read(&ch->usrs) && common->io_usrs) {
+ /* Enable channel */
+ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
+ enable_channel2(1);
+ channel2_intr_enable(1);
+ }
+ if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
+ common->started == 2) {
+ enable_channel3(1);
+ channel3_intr_enable(1);
+ }
+ }
+ mutex_unlock(&common->lock);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops vpif_pm = {
+ .suspend = vpif_suspend,
+ .resume = vpif_resume,
+};
+
+#define vpif_pm_ops (&vpif_pm)
+#else
+#define vpif_pm_ops NULL
+#endif
+
+static __refdata struct platform_driver vpif_driver = {
+ .driver = {
+ .name = "vpif_display",
+ .owner = THIS_MODULE,
+ .pm = vpif_pm_ops,
+ },
+ .probe = vpif_probe,
+ .remove = vpif_remove,
+};
+
+static __init int vpif_init(void)
+{
+ return platform_driver_register(&vpif_driver);
+}
+
+/*
+ * vpif_cleanup: This function un-registers device and driver to the kernel,
+ * frees requested irq handler and de-allocates memory allocated for channel
+ * objects.
+ */
+static void vpif_cleanup(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int irq_num;
+ int i = 0;
+
+ pdev = container_of(vpif_dev, struct platform_device, dev);
+
+ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
+ for (irq_num = res->start; irq_num <= res->end; irq_num++)
+ free_irq(irq_num,
+ (void *)(&vpif_obj.dev[i]->channel_id));
+ i++;
+ }
+
+ platform_driver_unregister(&vpif_driver);
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++)
+ kfree(vpif_obj.dev[i]);
+}
+
+module_init(vpif_init);
+module_exit(vpif_cleanup);
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
new file mode 100644
index 000000000000..f628ebcf3674
--- /dev/null
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -0,0 +1,179 @@
+/*
+ * VPIF display header file
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DAVINCIHD_DISPLAY_H
+#define DAVINCIHD_DISPLAY_H
+
+/* Header files */
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/davinci/vpif_types.h>
+
+#include "vpif.h"
+
+/* Macros */
+#define VPIF_DISPLAY_VERSION "0.0.2"
+
+#define VPIF_VALID_FIELD(field) \
+ (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
+ (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \
+ (V4L2_FIELD_SEQ_BT == field)))
+
+#define VPIF_DISPLAY_MAX_DEVICES (2)
+#define VPIF_SLICED_BUF_SIZE (256)
+#define VPIF_SLICED_MAX_SERVICES (3)
+#define VPIF_VIDEO_INDEX (0)
+#define VPIF_VBI_INDEX (1)
+#define VPIF_HBI_INDEX (2)
+
+/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/
+#define VPIF_NUMOBJECTS (1)
+
+/* Macros */
+#define ISALIGNED(a) (0 == ((a) & 7))
+
+/* enumerated data types */
+/* Enumerated data type to give id to each device per channel */
+enum vpif_channel_id {
+ VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */
+ VPIF_CHANNEL3_VIDEO, /* Channel3 Video */
+};
+
+/* structures */
+
+struct video_obj {
+ enum v4l2_field buf_field;
+ u32 latest_only; /* indicate whether to return
+ * most recent displayed frame only */
+ v4l2_std_id stdid; /* Currently selected or default
+ * standard */
+ struct v4l2_dv_timings dv_timings;
+ u32 output_id; /* Current output id */
+};
+
+struct vbi_obj {
+ int num_services;
+ struct vpif_vbi_params vbiparams; /* vpif parameters for the raw
+ * vbi data */
+};
+
+struct vpif_disp_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct common_obj {
+ /* Buffer specific parameters */
+ u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
+ * storing frames */
+ u32 numbuffers; /* number of buffers */
+ struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
+ * vb2_buffer */
+ struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
+ * vb2_buffer */
+ enum v4l2_memory memory; /* This field keeps track of
+ * type of buffer exchange
+ * method user has selected */
+ struct v4l2_format fmt; /* Used to store the format */
+ struct vb2_queue buffer_queue; /* Buffer queue used in
+ * video-buf */
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ struct list_head dma_queue; /* Queue of filled frames */
+ spinlock_t irqlock; /* Used in video-buf */
+
+ /* channel specific parameters */
+ struct mutex lock; /* lock used to access this
+ * structure */
+ u32 io_usrs; /* number of users performing
+ * IO */
+ u8 started; /* Indicates whether streaming
+ * started */
+ u32 ytop_off; /* offset of Y top from the
+ * starting of the buffer */
+ u32 ybtm_off; /* offset of Y bottom from the
+ * starting of the buffer */
+ u32 ctop_off; /* offset of C top from the
+ * starting of the buffer */
+ u32 cbtm_off; /* offset of C bottom from the
+ * starting of the buffer */
+ /* Function pointer to set the addresses */
+ void (*set_addr) (unsigned long, unsigned long,
+ unsigned long, unsigned long);
+ u32 height;
+ u32 width;
+};
+
+struct channel_obj {
+ /* V4l2 specific parameters */
+ struct video_device *video_dev; /* Identifies video device for
+ * this channel */
+ struct v4l2_prio_state prio; /* Used to keep track of state of
+ * the priority */
+ atomic_t usrs; /* number of open instances of
+ * the channel */
+ u32 field_id; /* Indicates id of the field
+ * which is being displayed */
+ u8 initialized; /* flag to indicate whether
+ * encoder is initialized */
+
+ enum vpif_channel_id channel_id;/* Identifies channel */
+ struct vpif_params vpifparams;
+ struct common_obj common[VPIF_NUMOBJECTS];
+ struct video_obj video;
+ struct vbi_obj vbi;
+};
+
+/* File handle structure */
+struct vpif_fh {
+ struct channel_obj *channel; /* pointer to channel object for
+ * opened device */
+ u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
+ * is doing IO */
+ enum v4l2_priority prio; /* Used to keep track priority of
+ * this instance */
+ u8 initialized; /* Used to keep track of whether this
+ * file handle has initialized
+ * channel or not */
+};
+
+/* vpif device structure */
+struct vpif_device {
+ struct v4l2_device v4l2_dev;
+ struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
+ struct v4l2_subdev **sd;
+
+};
+
+struct vpif_config_params {
+ u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
+ u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
+ u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
+ u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
+ u8 min_numbuffers;
+};
+
+/* Struct which keeps track of the line numbers for the sliced vbi service */
+struct vpif_service_line {
+ u16 service_id;
+ u16 service_line[2];
+ u16 enc_service_id;
+ u8 bytestowrite;
+};
+
+#endif /* DAVINCIHD_DISPLAY_H */
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c
new file mode 100644
index 000000000000..146e4b01ac17
--- /dev/null
+++ b/drivers/media/platform/davinci/vpss.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2009 Texas Instruments.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 vpss system module platform driver for all video drivers.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/compiler.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <media/davinci/vpss.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VPSS Driver");
+MODULE_AUTHOR("Texas Instruments");
+
+/* DM644x defines */
+#define DM644X_SBL_PCR_VPSS (4)
+
+#define DM355_VPSSBL_INTSEL 0x10
+#define DM355_VPSSBL_EVTSEL 0x14
+/* vpss BL register offsets */
+#define DM355_VPSSBL_CCDCMUX 0x1c
+/* vpss CLK register offsets */
+#define DM355_VPSSCLK_CLKCTRL 0x04
+/* masks and shifts */
+#define VPSS_HSSISEL_SHIFT 4
+/*
+ * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
+ * IPIPE_INT1_SDR - vpss_int5
+ */
+#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
+/* VENCINT - vpss_int8 */
+#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
+
+#define DM365_ISP5_PCCR 0x04
+#define DM365_ISP5_INTSEL1 0x10
+#define DM365_ISP5_INTSEL2 0x14
+#define DM365_ISP5_INTSEL3 0x18
+#define DM365_ISP5_CCDCMUX 0x20
+#define DM365_ISP5_PG_FRAME_SIZE 0x28
+#define DM365_VPBE_CLK_CTRL 0x00
+/*
+ * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
+ * AF - vpss_int3
+ */
+#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
+/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
+#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
+/* VENC - vpss_int8 */
+#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
+
+/* masks and shifts for DM365*/
+#define DM365_CCDC_PG_VD_POL_SHIFT 0
+#define DM365_CCDC_PG_HD_POL_SHIFT 1
+
+#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
+#define CCD_SRC_SEL_SHIFT 4
+
+/* Different SoC platforms supported by this driver */
+enum vpss_platform_type {
+ DM644X,
+ DM355,
+ DM365,
+};
+
+/*
+ * vpss operations. Depends on platform. Not all functions are available
+ * on all platforms. The api, first check if a functio is available before
+ * invoking it. In the probe, the function ptrs are initialized based on
+ * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
+ */
+struct vpss_hw_ops {
+ /* enable clock */
+ int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
+ /* select input to ccdc */
+ void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
+ /* clear wbl overflow bit */
+ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
+};
+
+/* vpss configuration */
+struct vpss_oper_config {
+ __iomem void *vpss_regs_base0;
+ __iomem void *vpss_regs_base1;
+ enum vpss_platform_type platform;
+ spinlock_t vpss_lock;
+ struct vpss_hw_ops hw_ops;
+};
+
+static struct vpss_oper_config oper_cfg;
+
+/* register access routines */
+static inline u32 bl_regr(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
+}
+
+static inline void bl_regw(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
+}
+
+static inline u32 vpss_regr(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
+}
+
+static inline void vpss_regw(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
+}
+
+/* For DM365 only */
+static inline u32 isp5_read(u32 offset)
+{
+ return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
+}
+
+/* For DM365 only */
+static inline void isp5_write(u32 val, u32 offset)
+{
+ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
+}
+
+static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
+
+ /* if we are using pattern generator, enable it */
+ if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
+ temp |= 0x08;
+
+ temp |= (src_sel << CCD_SRC_SEL_SHIFT);
+ isp5_write(temp, DM365_ISP5_CCDCMUX);
+}
+
+static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
+}
+
+int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
+{
+ if (!oper_cfg.hw_ops.select_ccdc_source)
+ return -EINVAL;
+
+ oper_cfg.hw_ops.select_ccdc_source(src_sel);
+ return 0;
+}
+EXPORT_SYMBOL(vpss_select_ccdc_source);
+
+static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
+{
+ u32 mask = 1, val;
+
+ if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
+ wbl_sel > VPSS_PCR_CCDC_WBL_O)
+ return -EINVAL;
+
+ /* writing a 0 clear the overflow */
+ mask = ~(mask << wbl_sel);
+ val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
+ bl_regw(val, DM644X_SBL_PCR_VPSS);
+ return 0;
+}
+
+int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
+{
+ if (!oper_cfg.hw_ops.clear_wbl_overflow)
+ return -EINVAL;
+
+ return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
+}
+EXPORT_SYMBOL(vpss_clear_wbl_overflow);
+
+/*
+ * dm355_enable_clock - Enable VPSS Clock
+ * @clock_sel: CLock to be enabled/disabled
+ * @en: enable/disable flag
+ *
+ * This is called to enable or disable a vpss clock
+ */
+static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ unsigned long flags;
+ u32 utemp, mask = 0x1, shift = 0;
+
+ switch (clock_sel) {
+ case VPSS_VPBE_CLOCK:
+ /* nothing since lsb */
+ break;
+ case VPSS_VENC_CLOCK_SEL:
+ shift = 2;
+ break;
+ case VPSS_CFALD_CLOCK:
+ shift = 3;
+ break;
+ case VPSS_H3A_CLOCK:
+ shift = 4;
+ break;
+ case VPSS_IPIPE_CLOCK:
+ shift = 5;
+ break;
+ case VPSS_CCDC_CLOCK:
+ shift = 6;
+ break;
+ default:
+ printk(KERN_ERR "dm355_enable_clock:"
+ " Invalid selector: %d\n", clock_sel);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
+ utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
+ if (!en)
+ utemp &= ~(mask << shift);
+ else
+ utemp |= (mask << shift);
+
+ vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
+ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
+ return 0;
+}
+
+static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ unsigned long flags;
+ u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
+ u32 (*read)(u32 offset) = isp5_read;
+ void(*write)(u32 val, u32 offset) = isp5_write;
+
+ switch (clock_sel) {
+ case VPSS_BL_CLOCK:
+ break;
+ case VPSS_CCDC_CLOCK:
+ shift = 1;
+ break;
+ case VPSS_H3A_CLOCK:
+ shift = 2;
+ break;
+ case VPSS_RSZ_CLOCK:
+ shift = 3;
+ break;
+ case VPSS_IPIPE_CLOCK:
+ shift = 4;
+ break;
+ case VPSS_IPIPEIF_CLOCK:
+ shift = 5;
+ break;
+ case VPSS_PCLK_INTERNAL:
+ shift = 6;
+ break;
+ case VPSS_PSYNC_CLOCK_SEL:
+ shift = 7;
+ break;
+ case VPSS_VPBE_CLOCK:
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_VENC_CLOCK_SEL:
+ shift = 2;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK:
+ shift = 3;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_FDIF_CLOCK:
+ shift = 4;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_OSD_CLOCK_SEL:
+ shift = 6;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ case VPSS_LDC_CLOCK_SEL:
+ shift = 7;
+ read = vpss_regr;
+ write = vpss_regw;
+ offset = DM365_VPBE_CLK_CTRL;
+ break;
+ default:
+ printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
+ clock_sel);
+ return -1;
+ }
+
+ spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
+ utemp = read(offset);
+ if (!en) {
+ mask = ~mask;
+ utemp &= (mask << shift);
+ } else
+ utemp |= (mask << shift);
+
+ write(utemp, offset);
+ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
+
+ return 0;
+}
+
+int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
+{
+ if (!oper_cfg.hw_ops.enable_clock)
+ return -EINVAL;
+
+ return oper_cfg.hw_ops.enable_clock(clock_sel, en);
+}
+EXPORT_SYMBOL(vpss_enable_clock);
+
+void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
+{
+ int val = 0;
+ val = isp5_read(DM365_ISP5_CCDCMUX);
+
+ val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
+ val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
+
+ isp5_write(val, DM365_ISP5_CCDCMUX);
+}
+EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
+
+void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
+{
+ int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
+
+ current_reg |= (frame_size.pplen - 1);
+ isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
+}
+EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
+
+static int __devinit vpss_probe(struct platform_device *pdev)
+{
+ struct resource *r1, *r2;
+ char *platform_name;
+ int status;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -ENOENT;
+ }
+
+ platform_name = pdev->dev.platform_data;
+ if (!strcmp(platform_name, "dm355_vpss"))
+ oper_cfg.platform = DM355;
+ else if (!strcmp(platform_name, "dm365_vpss"))
+ oper_cfg.platform = DM365;
+ else if (!strcmp(platform_name, "dm644x_vpss"))
+ oper_cfg.platform = DM644X;
+ else {
+ dev_err(&pdev->dev, "vpss driver not supported on"
+ " this platform\n");
+ return -ENODEV;
+ }
+
+ dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
+ r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r1)
+ return -ENOENT;
+
+ r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
+ if (!r1)
+ return -EBUSY;
+
+ oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
+ if (!oper_cfg.vpss_regs_base0) {
+ status = -EBUSY;
+ goto fail1;
+ }
+
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r2) {
+ status = -ENOENT;
+ goto fail2;
+ }
+ r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
+ if (!r2) {
+ status = -EBUSY;
+ goto fail2;
+ }
+
+ oper_cfg.vpss_regs_base1 = ioremap(r2->start,
+ resource_size(r2));
+ if (!oper_cfg.vpss_regs_base1) {
+ status = -EBUSY;
+ goto fail3;
+ }
+ }
+
+ if (oper_cfg.platform == DM355) {
+ oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
+ oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
+ /* Setup vpss interrupts */
+ bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
+ bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
+ } else if (oper_cfg.platform == DM365) {
+ oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
+ oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
+ /* Setup vpss interrupts */
+ isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
+ isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
+ isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
+ } else
+ oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
+
+ spin_lock_init(&oper_cfg.vpss_lock);
+ dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
+ return 0;
+
+fail3:
+ release_mem_region(r2->start, resource_size(r2));
+fail2:
+ iounmap(oper_cfg.vpss_regs_base0);
+fail1:
+ release_mem_region(r1->start, resource_size(r1));
+ return status;
+}
+
+static int __devexit vpss_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+
+ iounmap(oper_cfg.vpss_regs_base0);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
+ iounmap(oper_cfg.vpss_regs_base1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ release_mem_region(res->start, resource_size(res));
+ }
+ return 0;
+}
+
+static struct platform_driver vpss_driver = {
+ .driver = {
+ .name = "vpss",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(vpss_remove),
+ .probe = vpss_probe,
+};
+
+static void vpss_exit(void)
+{
+ platform_driver_unregister(&vpss_driver);
+}
+
+static int __init vpss_init(void)
+{
+ return platform_driver_register(&vpss_driver);
+}
+subsys_initcall(vpss_init);
+module_exit(vpss_exit);
diff --git a/drivers/media/platform/exynos-gsc/Makefile b/drivers/media/platform/exynos-gsc/Makefile
new file mode 100644
index 000000000000..6d1411c6d49f
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/Makefile
@@ -0,0 +1,3 @@
+exynos-gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o
+
+obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc.o
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
new file mode 100644
index 000000000000..bfec9e65aefb
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-Scaler 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <media/v4l2-ioctl.h>
+
+#include "gsc-core.h"
+
+#define GSC_CLOCK_GATE_NAME "gscl"
+
+static const struct gsc_fmt gsc_formats[] = {
+ {
+ .name = "RGB565",
+ .pixelformat = V4L2_PIX_FMT_RGB565X,
+ .depth = { 16 },
+ .color = GSC_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ }, {
+ .name = "XRGB-8-8-8-8, 32 bpp",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .depth = { 32 },
+ .color = GSC_RGB,
+ .num_planes = 1,
+ .num_comp = 1,
+ }, {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 1,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_C,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 1,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .pixelformat = V4L2_PIX_FMT_VYUY,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_C,
+ .corder = GSC_CRCB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .pixelformat = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CRCB,
+ .num_planes = 1,
+ .num_comp = 1,
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ }, {
+ .name = "YUV 4:4:4 planar, YCbYCr",
+ .pixelformat = V4L2_PIX_FMT_YUV32,
+ .depth = { 32 },
+ .color = GSC_YUV444,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 1,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 3,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV16,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 2,
+ }, {
+ .name = "YUV 4:2:2 planar, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV61,
+ .depth = { 16 },
+ .color = GSC_YUV422,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CRCB,
+ .num_planes = 1,
+ .num_comp = 2,
+ }, {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .depth = { 12 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 3,
+ }, {
+ .name = "YUV 4:2:0 planar, YCrCb",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .depth = { 12 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CRCB,
+ .num_planes = 1,
+ .num_comp = 3,
+
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV12,
+ .depth = { 12 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 1,
+ .num_comp = 2,
+ }, {
+ .name = "YUV 4:2:0 planar, Y/CrCb",
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .depth = { 12 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CRCB,
+ .num_planes = 1,
+ .num_comp = 2,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr",
+ .pixelformat = V4L2_PIX_FMT_NV12M,
+ .depth = { 8, 4 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 2,
+ .num_comp = 2,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV420M,
+ .depth = { 8, 2, 2 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CBCR,
+ .num_planes = 3,
+ .num_comp = 3,
+ }, {
+ .name = "YUV 4:2:0 non-contig. 3p, Y/Cr/Cb",
+ .pixelformat = V4L2_PIX_FMT_YVU420M,
+ .depth = { 8, 2, 2 },
+ .color = GSC_YUV420,
+ .yorder = GSC_LSB_Y,
+ .corder = GSC_CRCB,
+ .num_planes = 3,
+ .num_comp = 3,
+ }
+};
+
+const struct gsc_fmt *get_format(int index)
+{
+ if (index >= ARRAY_SIZE(gsc_formats))
+ return NULL;
+
+ return (struct gsc_fmt *)&gsc_formats[index];
+}
+
+const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index)
+{
+ const struct gsc_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+
+ if (index >= ARRAY_SIZE(gsc_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) {
+ fmt = get_format(i);
+ if (pixelformat && fmt->pixelformat == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == i)
+ def_fmt = fmt;
+ }
+ return def_fmt;
+
+}
+
+void gsc_set_frame_size(struct gsc_frame *frame, int width, int height)
+{
+ frame->f_width = width;
+ frame->f_height = height;
+ frame->crop.width = width;
+ frame->crop.height = height;
+ frame->crop.left = 0;
+ frame->crop.top = 0;
+}
+
+int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
+ u32 *ratio)
+{
+ if ((dst > src) || (dst >= src / var->poly_sc_down_max)) {
+ *ratio = 1;
+ return 0;
+ }
+
+ if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) {
+ pr_err("Exceeded maximum downscaling ratio (1/16))");
+ return -EINVAL;
+ }
+
+ *ratio = (dst > (src / 8)) ? 2 : 4;
+
+ return 0;
+}
+
+void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh)
+{
+ if (hratio == 4 && vratio == 4)
+ *sh = 4;
+ else if ((hratio == 4 && vratio == 2) ||
+ (hratio == 2 && vratio == 4))
+ *sh = 3;
+ else if ((hratio == 4 && vratio == 1) ||
+ (hratio == 1 && vratio == 4) ||
+ (hratio == 2 && vratio == 2))
+ *sh = 2;
+ else if (hratio == 1 && vratio == 1)
+ *sh = 0;
+ else
+ *sh = 1;
+}
+
+void gsc_check_src_scale_info(struct gsc_variant *var,
+ struct gsc_frame *s_frame, u32 *wratio,
+ u32 tx, u32 ty, u32 *hratio)
+{
+ int remainder = 0, walign, halign;
+
+ if (is_yuv420(s_frame->fmt->color)) {
+ walign = GSC_SC_ALIGN_4;
+ halign = GSC_SC_ALIGN_4;
+ } else if (is_yuv422(s_frame->fmt->color)) {
+ walign = GSC_SC_ALIGN_4;
+ halign = GSC_SC_ALIGN_2;
+ } else {
+ walign = GSC_SC_ALIGN_2;
+ halign = GSC_SC_ALIGN_2;
+ }
+
+ remainder = s_frame->crop.width % (*wratio * walign);
+ if (remainder) {
+ s_frame->crop.width -= remainder;
+ gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio);
+ pr_info("cropped src width size is recalculated from %d to %d",
+ s_frame->crop.width + remainder, s_frame->crop.width);
+ }
+
+ remainder = s_frame->crop.height % (*hratio * halign);
+ if (remainder) {
+ s_frame->crop.height -= remainder;
+ gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio);
+ pr_info("cropped src height size is recalculated from %d to %d",
+ s_frame->crop.height + remainder, s_frame->crop.height);
+ }
+}
+
+int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f)
+{
+ const struct gsc_fmt *fmt;
+
+ fmt = find_fmt(NULL, NULL, f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->pixelformat;
+
+ return 0;
+}
+
+static u32 get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index)
+{
+ if (frm->addr.y == addr) {
+ *index = 0;
+ return frm->addr.y;
+ } else if (frm->addr.cb == addr) {
+ *index = 1;
+ return frm->addr.cb;
+ } else if (frm->addr.cr == addr) {
+ *index = 2;
+ return frm->addr.cr;
+ } else {
+ pr_err("Plane address is wrong");
+ return -EINVAL;
+ }
+}
+
+void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm)
+{
+ u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len;
+ f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0;
+
+ f_chk_addr = frm->addr.y;
+ f_chk_len = frm->payload[0];
+ if (frm->fmt->num_planes == 2) {
+ s_chk_addr = frm->addr.cb;
+ s_chk_len = frm->payload[1];
+ } else if (frm->fmt->num_planes == 3) {
+ u32 low_addr, low_plane, mid_addr, mid_plane;
+ u32 high_addr, high_plane;
+ u32 t_min, t_max;
+
+ t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr);
+ low_addr = get_plane_info(frm, t_min, &low_plane);
+ t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr);
+ high_addr = get_plane_info(frm, t_max, &high_plane);
+
+ mid_plane = 3 - (low_plane + high_plane);
+ if (mid_plane == 0)
+ mid_addr = frm->addr.y;
+ else if (mid_plane == 1)
+ mid_addr = frm->addr.cb;
+ else if (mid_plane == 2)
+ mid_addr = frm->addr.cr;
+ else
+ return;
+
+ f_chk_addr = low_addr;
+ if (mid_addr + frm->payload[mid_plane] - low_addr >
+ high_addr + frm->payload[high_plane] - mid_addr) {
+ f_chk_len = frm->payload[low_plane];
+ s_chk_addr = mid_addr;
+ s_chk_len = high_addr +
+ frm->payload[high_plane] - mid_addr;
+ } else {
+ f_chk_len = mid_addr +
+ frm->payload[mid_plane] - low_addr;
+ s_chk_addr = high_addr;
+ s_chk_len = frm->payload[high_plane];
+ }
+ }
+ pr_debug("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n",
+ f_chk_addr, f_chk_len, s_chk_addr, s_chk_len);
+}
+
+int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
+{
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ struct gsc_variant *variant = gsc->variant;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ const struct gsc_fmt *fmt;
+ u32 max_w, max_h, mod_x, mod_y;
+ u32 min_w, min_h, tmp_w, tmp_h;
+ int i;
+
+ pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height);
+
+ fmt = find_fmt(&pix_mp->pixelformat, NULL, 0);
+ if (!fmt) {
+ pr_err("pixelformat format (0x%X) invalid\n",
+ pix_mp->pixelformat);
+ return -EINVAL;
+ }
+
+ if (pix_mp->field == V4L2_FIELD_ANY)
+ pix_mp->field = V4L2_FIELD_NONE;
+ else if (pix_mp->field != V4L2_FIELD_NONE) {
+ pr_err("Not supported field order(%d)\n", pix_mp->field);
+ return -EINVAL;
+ }
+
+ max_w = variant->pix_max->target_rot_dis_w;
+ max_h = variant->pix_max->target_rot_dis_h;
+
+ mod_x = ffs(variant->pix_align->org_w) - 1;
+ if (is_yuv420(fmt->color))
+ mod_y = ffs(variant->pix_align->org_h) - 1;
+ else
+ mod_y = ffs(variant->pix_align->org_h) - 2;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ min_w = variant->pix_min->org_w;
+ min_h = variant->pix_min->org_h;
+ } else {
+ min_w = variant->pix_min->target_rot_dis_w;
+ min_h = variant->pix_min->target_rot_dis_h;
+ }
+
+ pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d",
+ mod_x, mod_y, max_w, max_h);
+
+ /* To check if image size is modified to adjust parameter against
+ hardware abilities */
+ tmp_w = pix_mp->width;
+ tmp_h = pix_mp->height;
+
+ v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x,
+ &pix_mp->height, min_h, max_h, mod_y, 0);
+ if (tmp_w != pix_mp->width || tmp_h != pix_mp->height)
+ pr_info("Image size has been modified from %dx%d to %dx%d",
+ tmp_w, tmp_h, pix_mp->width, pix_mp->height);
+
+ pix_mp->num_planes = fmt->num_planes;
+
+ if (pix_mp->width >= 1280) /* HD */
+ pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+ else /* SD */
+ pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+
+ for (i = 0; i < pix_mp->num_planes; ++i) {
+ int bpl = (pix_mp->width * fmt->depth[i]) >> 3;
+ pix_mp->plane_fmt[i].bytesperline = bpl;
+ pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height;
+
+ pr_debug("[%d]: bpl: %d, sizeimage: %d",
+ i, bpl, pix_mp->plane_fmt[i].sizeimage);
+ }
+
+ return 0;
+}
+
+int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f)
+{
+ struct gsc_frame *frame;
+ struct v4l2_pix_format_mplane *pix_mp;
+ int i;
+
+ frame = ctx_get_frame(ctx, f->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ pix_mp = &f->fmt.pix_mp;
+
+ pix_mp->width = frame->f_width;
+ pix_mp->height = frame->f_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->pixelformat = frame->fmt->pixelformat;
+ pix_mp->colorspace = V4L2_COLORSPACE_REC709;
+ pix_mp->num_planes = frame->fmt->num_planes;
+
+ for (i = 0; i < pix_mp->num_planes; ++i) {
+ pix_mp->plane_fmt[i].bytesperline = (frame->f_width *
+ frame->fmt->depth[i]) / 8;
+ pix_mp->plane_fmt[i].sizeimage =
+ pix_mp->plane_fmt[i].bytesperline * frame->f_height;
+ }
+
+ return 0;
+}
+
+void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h)
+{
+ if (tmp_w != *w || tmp_h != *h) {
+ pr_info("Cropped size has been modified from %dx%d to %dx%d",
+ *w, *h, tmp_w, tmp_h);
+ *w = tmp_w;
+ *h = tmp_h;
+ }
+}
+
+int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct gsc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c = frame->crop;
+
+ return 0;
+}
+
+int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct gsc_frame *f;
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ struct gsc_variant *variant = gsc->variant;
+ u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h;
+ u32 min_w, min_h, max_w, max_h;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ pr_err("doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+ pr_debug("user put w: %d, h: %d", cr->c.width, cr->c.height);
+
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ f = &ctx->s_frame;
+ else
+ return -EINVAL;
+
+ max_w = f->f_width;
+ max_h = f->f_height;
+ tmp_w = cr->c.width;
+ tmp_h = cr->c.height;
+
+ if (V4L2_TYPE_IS_OUTPUT(cr->type)) {
+ if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) ||
+ is_rgb(f->fmt->color))
+ min_w = 32;
+ else
+ min_w = 64;
+ if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) ||
+ is_yuv420(f->fmt->color))
+ min_h = 32;
+ else
+ min_h = 16;
+ } else {
+ if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color))
+ mod_x = ffs(variant->pix_align->target_w) - 1;
+ if (is_yuv420(f->fmt->color))
+ mod_y = ffs(variant->pix_align->target_h) - 1;
+ if (ctx->gsc_ctrls.rotate->val == 90 ||
+ ctx->gsc_ctrls.rotate->val == 270) {
+ max_w = f->f_height;
+ max_h = f->f_width;
+ min_w = variant->pix_min->target_rot_en_w;
+ min_h = variant->pix_min->target_rot_en_h;
+ tmp_w = cr->c.height;
+ tmp_h = cr->c.width;
+ } else {
+ min_w = variant->pix_min->target_rot_dis_w;
+ min_h = variant->pix_min->target_rot_dis_h;
+ }
+ }
+ pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d",
+ mod_x, mod_y, min_w, min_h);
+ pr_debug("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h);
+
+ v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x,
+ &tmp_h, min_h, max_h, mod_y, 0);
+
+ if (!V4L2_TYPE_IS_OUTPUT(cr->type) &&
+ (ctx->gsc_ctrls.rotate->val == 90 ||
+ ctx->gsc_ctrls.rotate->val == 270))
+ gsc_check_crop_change(tmp_h, tmp_w,
+ &cr->c.width, &cr->c.height);
+ else
+ gsc_check_crop_change(tmp_w, tmp_h,
+ &cr->c.width, &cr->c.height);
+
+
+ /* adjust left/top if cropping rectangle is out of bounds */
+ /* Need to add code to algin left value with 2's multiple */
+ if (cr->c.left + tmp_w > max_w)
+ cr->c.left = max_w - tmp_w;
+ if (cr->c.top + tmp_h > max_h)
+ cr->c.top = max_h - tmp_h;
+
+ if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) &&
+ cr->c.left & 1)
+ cr->c.left -= 1;
+
+ pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+ cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h);
+
+ return 0;
+}
+
+int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
+ int dh, int rot, int out_path)
+{
+ int tmp_w, tmp_h, sc_down_max;
+
+ if (out_path == GSC_DMA)
+ sc_down_max = var->sc_down_max;
+ else
+ sc_down_max = var->local_sc_down;
+
+ if (rot == 90 || rot == 270) {
+ tmp_w = dh;
+ tmp_h = dw;
+ } else {
+ tmp_w = dw;
+ tmp_h = dh;
+ }
+
+ if ((sw / tmp_w) > sc_down_max ||
+ (sh / tmp_h) > sc_down_max ||
+ (tmp_w / sw) > var->sc_up_max ||
+ (tmp_h / sh) > var->sc_up_max)
+ return -EINVAL;
+
+ return 0;
+}
+
+int gsc_set_scaler_info(struct gsc_ctx *ctx)
+{
+ struct gsc_scaler *sc = &ctx->scaler;
+ struct gsc_frame *s_frame = &ctx->s_frame;
+ struct gsc_frame *d_frame = &ctx->d_frame;
+ struct gsc_variant *variant = ctx->gsc_dev->variant;
+ struct device *dev = &ctx->gsc_dev->pdev->dev;
+ int tx, ty;
+ int ret;
+
+ ret = gsc_check_scaler_ratio(variant, s_frame->crop.width,
+ s_frame->crop.height, d_frame->crop.width, d_frame->crop.height,
+ ctx->gsc_ctrls.rotate->val, ctx->out_path);
+ if (ret) {
+ pr_err("out of scaler range");
+ return ret;
+ }
+
+ if (ctx->gsc_ctrls.rotate->val == 90 ||
+ ctx->gsc_ctrls.rotate->val == 270) {
+ ty = d_frame->crop.width;
+ tx = d_frame->crop.height;
+ } else {
+ tx = d_frame->crop.width;
+ ty = d_frame->crop.height;
+ }
+
+ if (tx <= 0 || ty <= 0) {
+ dev_err(dev, "Invalid target size: %dx%d", tx, ty);
+ return -EINVAL;
+ }
+
+ ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width,
+ tx, &sc->pre_hratio);
+ if (ret) {
+ pr_err("Horizontal scale ratio is out of range");
+ return ret;
+ }
+
+ ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height,
+ ty, &sc->pre_vratio);
+ if (ret) {
+ pr_err("Vertical scale ratio is out of range");
+ return ret;
+ }
+
+ gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio,
+ tx, ty, &sc->pre_vratio);
+
+ gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio,
+ &sc->pre_shfactor);
+
+ sc->main_hratio = (s_frame->crop.width << 16) / tx;
+ sc->main_vratio = (s_frame->crop.height << 16) / ty;
+
+ pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d",
+ s_frame->crop.width, s_frame->crop.height, tx, ty);
+ pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d",
+ sc->pre_shfactor, sc->pre_hratio);
+ pr_debug("pre_v :%d, main_h : %d, main_v : %d",
+ sc->pre_vratio, sc->main_hratio, sc->main_vratio);
+
+ return 0;
+}
+
+static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ struct gsc_variant *variant = gsc->variant;
+ unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT;
+ int ret = 0;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ ctx->hflip = ctrl->val;
+ break;
+
+ case V4L2_CID_VFLIP:
+ ctx->vflip = ctrl->val;
+ break;
+
+ case V4L2_CID_ROTATE:
+ if ((ctx->state & flags) == flags) {
+ ret = gsc_check_scaler_ratio(variant,
+ ctx->s_frame.crop.width,
+ ctx->s_frame.crop.height,
+ ctx->d_frame.crop.width,
+ ctx->d_frame.crop.height,
+ ctx->gsc_ctrls.rotate->val,
+ ctx->out_path);
+
+ if (ret)
+ return -EINVAL;
+ }
+
+ ctx->rotation = ctrl->val;
+ break;
+
+ case V4L2_CID_ALPHA_COMPONENT:
+ ctx->d_frame.alpha = ctrl->val;
+ break;
+ }
+
+ ctx->state |= GSC_PARAMS;
+ return 0;
+}
+
+static int gsc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gsc_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
+ ret = __gsc_s_ctrl(ctx, ctrl);
+ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops gsc_ctrl_ops = {
+ .s_ctrl = gsc_s_ctrl,
+};
+
+int gsc_ctrls_create(struct gsc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ pr_err("Control handler of this context was created already");
+ return 0;
+ }
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM);
+
+ ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
+ ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
+
+ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ pr_err("Failed to create G-Scaler control handlers");
+ return err;
+ }
+
+ return 0;
+}
+
+void gsc_ctrls_delete(struct gsc_ctx *ctx)
+{
+ if (ctx->ctrls_rdy) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ ctx->ctrls_rdy = false;
+ }
+}
+
+/* The color format (num_comp, num_planes) must be already configured. */
+int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
+ struct gsc_frame *frame, struct gsc_addr *addr)
+{
+ int ret = 0;
+ u32 pix_size;
+
+ if ((vb == NULL) || (frame == NULL))
+ return -EINVAL;
+
+ pix_size = frame->f_width * frame->f_height;
+
+ pr_debug("num_planes= %d, num_comp= %d, pix_size= %d",
+ frame->fmt->num_planes, frame->fmt->num_comp, pix_size);
+
+ addr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (frame->fmt->num_planes == 1) {
+ switch (frame->fmt->num_comp) {
+ case 1:
+ addr->cb = 0;
+ addr->cr = 0;
+ break;
+ case 2:
+ /* decompose Y into Y/Cb */
+ addr->cb = (dma_addr_t)(addr->y + pix_size);
+ addr->cr = 0;
+ break;
+ case 3:
+ /* decompose Y into Y/Cb/Cr */
+ addr->cb = (dma_addr_t)(addr->y + pix_size);
+ if (GSC_YUV420 == frame->fmt->color)
+ addr->cr = (dma_addr_t)(addr->cb
+ + (pix_size >> 2));
+ else /* 422 */
+ addr->cr = (dma_addr_t)(addr->cb
+ + (pix_size >> 1));
+ break;
+ default:
+ pr_err("Invalid the number of color planes");
+ return -EINVAL;
+ }
+ } else {
+ if (frame->fmt->num_planes >= 2)
+ addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
+
+ if (frame->fmt->num_planes == 3)
+ addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
+ }
+
+ if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) ||
+ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) ||
+ (frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) ||
+ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) ||
+ (frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) ||
+ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
+ swap(addr->cb, addr->cr);
+
+ pr_debug("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d",
+ addr->y, addr->cb, addr->cr, ret);
+
+ return ret;
+}
+
+static irqreturn_t gsc_irq_handler(int irq, void *priv)
+{
+ struct gsc_dev *gsc = priv;
+ struct gsc_ctx *ctx;
+ int gsc_irq;
+
+ gsc_irq = gsc_hw_get_irq_status(gsc);
+ gsc_hw_clear_irq(gsc, gsc_irq);
+
+ if (gsc_irq == GSC_IRQ_OVERRUN) {
+ pr_err("Local path input over-run interrupt has occurred!\n");
+ return IRQ_HANDLED;
+ }
+
+ spin_lock(&gsc->slock);
+
+ if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) {
+
+ gsc_hw_enable_control(gsc, false);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) {
+ set_bit(ST_M2M_SUSPENDED, &gsc->state);
+ wake_up(&gsc->irq_queue);
+ goto isr_unlock;
+ }
+ ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
+
+ if (!ctx || !ctx->m2m_ctx)
+ goto isr_unlock;
+
+ spin_unlock(&gsc->slock);
+ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
+
+ /* wake_up job_abort, stop_streaming */
+ if (ctx->state & GSC_CTX_STOP_REQ) {
+ ctx->state &= ~GSC_CTX_STOP_REQ;
+ wake_up(&gsc->irq_queue);
+ }
+ return IRQ_HANDLED;
+ }
+
+isr_unlock:
+ spin_unlock(&gsc->slock);
+ return IRQ_HANDLED;
+}
+
+static struct gsc_pix_max gsc_v_100_max = {
+ .org_scaler_bypass_w = 8192,
+ .org_scaler_bypass_h = 8192,
+ .org_scaler_input_w = 4800,
+ .org_scaler_input_h = 3344,
+ .real_rot_dis_w = 4800,
+ .real_rot_dis_h = 3344,
+ .real_rot_en_w = 2047,
+ .real_rot_en_h = 2047,
+ .target_rot_dis_w = 4800,
+ .target_rot_dis_h = 3344,
+ .target_rot_en_w = 2016,
+ .target_rot_en_h = 2016,
+};
+
+static struct gsc_pix_min gsc_v_100_min = {
+ .org_w = 64,
+ .org_h = 32,
+ .real_w = 64,
+ .real_h = 32,
+ .target_rot_dis_w = 64,
+ .target_rot_dis_h = 32,
+ .target_rot_en_w = 32,
+ .target_rot_en_h = 16,
+};
+
+static struct gsc_pix_align gsc_v_100_align = {
+ .org_h = 16,
+ .org_w = 16, /* yuv420 : 16, others : 8 */
+ .offset_h = 2, /* yuv420/422 : 2, others : 1 */
+ .real_w = 16, /* yuv420/422 : 4~16, others : 2~8 */
+ .real_h = 16, /* yuv420 : 4~16, others : 1 */
+ .target_w = 2, /* yuv420/422 : 2, others : 1 */
+ .target_h = 2, /* yuv420 : 2, others : 1 */
+};
+
+static struct gsc_variant gsc_v_100_variant = {
+ .pix_max = &gsc_v_100_max,
+ .pix_min = &gsc_v_100_min,
+ .pix_align = &gsc_v_100_align,
+ .in_buf_cnt = 8,
+ .out_buf_cnt = 16,
+ .sc_up_max = 8,
+ .sc_down_max = 16,
+ .poly_sc_down_max = 4,
+ .pre_sc_down_max = 4,
+ .local_sc_down = 2,
+};
+
+static struct gsc_driverdata gsc_v_100_drvdata = {
+ .variant = {
+ [0] = &gsc_v_100_variant,
+ [1] = &gsc_v_100_variant,
+ [2] = &gsc_v_100_variant,
+ [3] = &gsc_v_100_variant,
+ },
+ .num_entities = 4,
+ .lclk_frequency = 266000000UL,
+};
+
+static struct platform_device_id gsc_driver_ids[] = {
+ {
+ .name = "exynos-gsc",
+ .driver_data = (unsigned long)&gsc_v_100_drvdata,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, gsc_driver_ids);
+
+static const struct of_device_id exynos_gsc_match[] = {
+ { .compatible = "samsung,exynos5250-gsc",
+ .data = &gsc_v_100_drvdata, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_gsc_match);
+
+static void *gsc_get_drv_data(struct platform_device *pdev)
+{
+ struct gsc_driverdata *driver_data = NULL;
+
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(of_match_ptr(exynos_gsc_match),
+ pdev->dev.of_node);
+ if (match)
+ driver_data = match->data;
+ } else {
+ driver_data = (struct gsc_driverdata *)
+ platform_get_device_id(pdev)->driver_data;
+ }
+
+ return driver_data;
+}
+
+static void gsc_clk_put(struct gsc_dev *gsc)
+{
+ if (IS_ERR_OR_NULL(gsc->clock))
+ return;
+
+ clk_unprepare(gsc->clock);
+ clk_put(gsc->clock);
+ gsc->clock = NULL;
+}
+
+static int gsc_clk_get(struct gsc_dev *gsc)
+{
+ int ret;
+
+ dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n");
+
+ gsc->clock = clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME);
+ if (IS_ERR(gsc->clock))
+ goto err_print;
+
+ ret = clk_prepare(gsc->clock);
+ if (ret < 0) {
+ clk_put(gsc->clock);
+ gsc->clock = NULL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n",
+ GSC_CLOCK_GATE_NAME);
+ gsc_clk_put(gsc);
+err_print:
+ dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n",
+ GSC_CLOCK_GATE_NAME);
+ return -ENXIO;
+}
+
+static int gsc_m2m_suspend(struct gsc_dev *gsc)
+{
+ unsigned long flags;
+ int timeout;
+
+ spin_lock_irqsave(&gsc->slock, flags);
+ if (!gsc_m2m_pending(gsc)) {
+ spin_unlock_irqrestore(&gsc->slock, flags);
+ return 0;
+ }
+ clear_bit(ST_M2M_SUSPENDED, &gsc->state);
+ set_bit(ST_M2M_SUSPENDING, &gsc->state);
+ spin_unlock_irqrestore(&gsc->slock, flags);
+
+ timeout = wait_event_timeout(gsc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &gsc->state),
+ GSC_SHUTDOWN_TIMEOUT);
+
+ clear_bit(ST_M2M_SUSPENDING, &gsc->state);
+ return timeout == 0 ? -EAGAIN : 0;
+}
+
+static int gsc_m2m_resume(struct gsc_dev *gsc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&gsc->slock, flags);
+ /* Clear for full H/W setup in first run after resume */
+ gsc->m2m.ctx = NULL;
+ spin_unlock_irqrestore(&gsc->slock, flags);
+
+ if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state))
+ gsc_m2m_job_finish(gsc->m2m.ctx,
+ VB2_BUF_STATE_ERROR);
+ return 0;
+}
+
+static int gsc_probe(struct platform_device *pdev)
+{
+ struct gsc_dev *gsc;
+ struct resource *res;
+ struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev);
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+
+ gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL);
+ if (!gsc)
+ return -ENOMEM;
+
+ if (dev->of_node)
+ gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc");
+ else
+ gsc->id = pdev->id;
+
+ if (gsc->id < 0 || gsc->id >= drv_data->num_entities) {
+ dev_err(dev, "Invalid platform device id: %d\n", gsc->id);
+ return -EINVAL;
+ }
+
+ gsc->variant = drv_data->variant[gsc->id];
+ gsc->pdev = pdev;
+ gsc->pdata = dev->platform_data;
+
+ init_waitqueue_head(&gsc->irq_queue);
+ spin_lock_init(&gsc->slock);
+ mutex_init(&gsc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ gsc->regs = devm_request_and_ioremap(dev, res);
+ if (!gsc->regs) {
+ dev_err(dev, "failed to map registers\n");
+ return -ENOENT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(dev, "failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = gsc_clk_get(gsc);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, res->start, gsc_irq_handler,
+ 0, pdev->name, gsc);
+ if (ret) {
+ dev_err(dev, "failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ ret = gsc_register_m2m_device(gsc);
+ if (ret)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, gsc);
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_m2m;
+
+ /* Initialize continious memory allocator */
+ gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev);
+ if (IS_ERR(gsc->alloc_ctx)) {
+ ret = PTR_ERR(gsc->alloc_ctx);
+ goto err_pm;
+ }
+
+ dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id);
+
+ pm_runtime_put(dev);
+ return 0;
+err_pm:
+ pm_runtime_put(dev);
+err_m2m:
+ gsc_unregister_m2m_device(gsc);
+err_clk:
+ gsc_clk_put(gsc);
+ return ret;
+}
+
+static int __devexit gsc_remove(struct platform_device *pdev)
+{
+ struct gsc_dev *gsc = platform_get_drvdata(pdev);
+
+ gsc_unregister_m2m_device(gsc);
+
+ vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
+ pm_runtime_disable(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
+ return 0;
+}
+
+static int gsc_runtime_resume(struct device *dev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
+
+ ret = clk_enable(gsc->clock);
+ if (ret)
+ return ret;
+
+ gsc_hw_set_sw_reset(gsc);
+ gsc_wait_reset(gsc);
+
+ return gsc_m2m_resume(gsc);
+}
+
+static int gsc_runtime_suspend(struct device *dev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = gsc_m2m_suspend(gsc);
+ if (!ret)
+ clk_disable(gsc->clock);
+
+ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
+ return ret;
+}
+
+static int gsc_resume(struct device *dev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
+
+ /* Do not resume if the device was idle before system suspend */
+ spin_lock_irqsave(&gsc->slock, flags);
+ if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) ||
+ !gsc_m2m_active(gsc)) {
+ spin_unlock_irqrestore(&gsc->slock, flags);
+ return 0;
+ }
+ gsc_hw_set_sw_reset(gsc);
+ gsc_wait_reset(gsc);
+
+ spin_unlock_irqrestore(&gsc->slock, flags);
+
+ return gsc_m2m_resume(gsc);
+}
+
+static int gsc_suspend(struct device *dev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+
+ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state);
+
+ if (test_and_set_bit(ST_SUSPEND, &gsc->state))
+ return 0;
+
+ return gsc_m2m_suspend(gsc);
+}
+
+static const struct dev_pm_ops gsc_pm_ops = {
+ .suspend = gsc_suspend,
+ .resume = gsc_resume,
+ .runtime_suspend = gsc_runtime_suspend,
+ .runtime_resume = gsc_runtime_resume,
+};
+
+static struct platform_driver gsc_driver = {
+ .probe = gsc_probe,
+ .remove = __devexit_p(gsc_remove),
+ .id_table = gsc_driver_ids,
+ .driver = {
+ .name = GSC_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = &gsc_pm_ops,
+ .of_match_table = exynos_gsc_match,
+ }
+};
+
+module_platform_driver(gsc_driver);
+
+MODULE_AUTHOR("Hyunwong Kim <khw0178.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
new file mode 100644
index 000000000000..5f157efd24f0
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * header file for Samsung EXYNOS5 SoC series G-Scaler 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.
+ */
+
+#ifndef GSC_CORE_H_
+#define GSC_CORE_H_
+
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "gsc-regs.h"
+
+#define CONFIG_VB2_GSC_DMA_CONTIG 1
+#define GSC_MODULE_NAME "exynos-gsc"
+
+#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
+#define GSC_MAX_DEVS 4
+#define GSC_M2M_BUF_NUM 0
+#define GSC_MAX_CTRL_NUM 10
+#define GSC_SC_ALIGN_4 4
+#define GSC_SC_ALIGN_2 2
+#define DEFAULT_CSC_EQ 1
+#define DEFAULT_CSC_RANGE 1
+
+#define GSC_PARAMS (1 << 0)
+#define GSC_SRC_FMT (1 << 1)
+#define GSC_DST_FMT (1 << 2)
+#define GSC_CTX_M2M (1 << 3)
+#define GSC_CTX_STOP_REQ (1 << 6)
+
+enum gsc_dev_flags {
+ /* for global */
+ ST_SUSPEND,
+
+ /* for m2m node */
+ ST_M2M_OPEN,
+ ST_M2M_RUN,
+ ST_M2M_PEND,
+ ST_M2M_SUSPENDED,
+ ST_M2M_SUSPENDING,
+};
+
+enum gsc_irq {
+ GSC_IRQ_DONE,
+ GSC_IRQ_OVERRUN
+};
+
+/**
+ * enum gsc_datapath - the path of data used for G-Scaler
+ * @GSC_CAMERA: from camera
+ * @GSC_DMA: from/to DMA
+ * @GSC_LOCAL: to local path
+ * @GSC_WRITEBACK: from FIMD
+ */
+enum gsc_datapath {
+ GSC_CAMERA = 0x1,
+ GSC_DMA,
+ GSC_MIXER,
+ GSC_FIMD,
+ GSC_WRITEBACK,
+};
+
+enum gsc_color_fmt {
+ GSC_RGB = 0x1,
+ GSC_YUV420 = 0x2,
+ GSC_YUV422 = 0x4,
+ GSC_YUV444 = 0x8,
+};
+
+enum gsc_yuv_fmt {
+ GSC_LSB_Y = 0x10,
+ GSC_LSB_C,
+ GSC_CBCR = 0x20,
+ GSC_CRCB,
+};
+
+#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh)
+#define is_rgb(x) (!!((x) & 0x1))
+#define is_yuv420(x) (!!((x) & 0x2))
+#define is_yuv422(x) (!!((x) & 0x4))
+
+#define gsc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
+#define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state)
+
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler)
+/**
+ * struct gsc_fmt - the driver's internal color format data
+ * @mbus_code: Media Bus pixel code, -1 if not applicable
+ * @name: format description
+ * @pixelformat: the fourcc code for this format, 0 if not applicable
+ * @yorder: Y/C order
+ * @corder: Chrominance order control
+ * @num_planes: number of physically non-contiguous data planes
+ * @nr_comp: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @flags: flags indicating which operation mode format applies to
+ */
+struct gsc_fmt {
+ enum v4l2_mbus_pixelcode mbus_code;
+ char *name;
+ u32 pixelformat;
+ u32 color;
+ u32 yorder;
+ u32 corder;
+ u16 num_planes;
+ u16 num_comp;
+ u8 depth[VIDEO_MAX_PLANES];
+ u32 flags;
+};
+
+/**
+ * struct gsc_input_buf - the driver's video buffer
+ * @vb: videobuf2 buffer
+ * @list : linked list structure for buffer queue
+ * @idx : index of G-Scaler input buffer
+ */
+struct gsc_input_buf {
+ struct vb2_buffer vb;
+ struct list_head list;
+ int idx;
+};
+
+/**
+ * struct gsc_addr - the G-Scaler physical address set
+ * @y: luminance plane address
+ * @cb: Cb plane address
+ * @cr: Cr plane address
+ */
+struct gsc_addr {
+ dma_addr_t y;
+ dma_addr_t cb;
+ dma_addr_t cr;
+};
+
+/* struct gsc_ctrls - the G-Scaler control set
+ * @rotate: rotation degree
+ * @hflip: horizontal flip
+ * @vflip: vertical flip
+ * @global_alpha: the alpha value of current frame
+ */
+struct gsc_ctrls {
+ struct v4l2_ctrl *rotate;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *global_alpha;
+};
+
+/**
+ * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler
+ * @pre_shfactor: pre sclaer shift factor
+ * @pre_hratio: horizontal ratio of the prescaler
+ * @pre_vratio: vertical ratio of the prescaler
+ * @main_hratio: the main scaler's horizontal ratio
+ * @main_vratio: the main scaler's vertical ratio
+ */
+struct gsc_scaler {
+ u32 pre_shfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 main_hratio;
+ u32 main_vratio;
+};
+
+struct gsc_dev;
+
+struct gsc_ctx;
+
+/**
+ * struct gsc_frame - source/target frame properties
+ * @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH
+ * @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT
+ * @crop: cropped(source)/scaled(destination) size
+ * @payload: image size in bytes (w x h x bpp)
+ * @addr: image frame buffer physical addresses
+ * @fmt: G-Scaler color format pointer
+ * @colorspace: value indicating v4l2_colorspace
+ * @alpha: frame's alpha value
+ */
+struct gsc_frame {
+ u32 f_width;
+ u32 f_height;
+ struct v4l2_rect crop;
+ unsigned long payload[VIDEO_MAX_PLANES];
+ struct gsc_addr addr;
+ const struct gsc_fmt *fmt;
+ u32 colorspace;
+ u8 alpha;
+};
+
+/**
+ * struct gsc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct gsc_m2m_device {
+ struct video_device *vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct gsc_ctx *ctx;
+ int refcnt;
+};
+
+/**
+ * struct gsc_pix_max - image pixel size limits in various IP configurations
+ *
+ * @org_scaler_bypass_w: max pixel width when the scaler is disabled
+ * @org_scaler_bypass_h: max pixel height when the scaler is disabled
+ * @org_scaler_input_w: max pixel width when the scaler is enabled
+ * @org_scaler_input_h: max pixel height when the scaler is enabled
+ * @real_rot_dis_w: max pixel src cropped height with the rotator is off
+ * @real_rot_dis_h: max pixel src croppped width with the rotator is off
+ * @real_rot_en_w: max pixel src cropped width with the rotator is on
+ * @real_rot_en_h: max pixel src cropped height with the rotator is on
+ * @target_rot_dis_w: max pixel dst scaled width with the rotator is off
+ * @target_rot_dis_h: max pixel dst scaled height with the rotator is off
+ * @target_rot_en_w: max pixel dst scaled width with the rotator is on
+ * @target_rot_en_h: max pixel dst scaled height with the rotator is on
+ */
+struct gsc_pix_max {
+ u16 org_scaler_bypass_w;
+ u16 org_scaler_bypass_h;
+ u16 org_scaler_input_w;
+ u16 org_scaler_input_h;
+ u16 real_rot_dis_w;
+ u16 real_rot_dis_h;
+ u16 real_rot_en_w;
+ u16 real_rot_en_h;
+ u16 target_rot_dis_w;
+ u16 target_rot_dis_h;
+ u16 target_rot_en_w;
+ u16 target_rot_en_h;
+};
+
+/**
+ * struct gsc_pix_min - image pixel size limits in various IP configurations
+ *
+ * @org_w: minimum source pixel width
+ * @org_h: minimum source pixel height
+ * @real_w: minimum input crop pixel width
+ * @real_h: minimum input crop pixel height
+ * @target_rot_dis_w: minimum output scaled pixel height when rotator is off
+ * @target_rot_dis_h: minimum output scaled pixel height when rotator is off
+ * @target_rot_en_w: minimum output scaled pixel height when rotator is on
+ * @target_rot_en_h: minimum output scaled pixel height when rotator is on
+ */
+struct gsc_pix_min {
+ u16 org_w;
+ u16 org_h;
+ u16 real_w;
+ u16 real_h;
+ u16 target_rot_dis_w;
+ u16 target_rot_dis_h;
+ u16 target_rot_en_w;
+ u16 target_rot_en_h;
+};
+
+struct gsc_pix_align {
+ u16 org_h;
+ u16 org_w;
+ u16 offset_h;
+ u16 real_w;
+ u16 real_h;
+ u16 target_w;
+ u16 target_h;
+};
+
+/**
+ * struct gsc_variant - G-Scaler variant information
+ */
+struct gsc_variant {
+ struct gsc_pix_max *pix_max;
+ struct gsc_pix_min *pix_min;
+ struct gsc_pix_align *pix_align;
+ u16 in_buf_cnt;
+ u16 out_buf_cnt;
+ u16 sc_up_max;
+ u16 sc_down_max;
+ u16 poly_sc_down_max;
+ u16 pre_sc_down_max;
+ u16 local_sc_down;
+};
+
+/**
+ * struct gsc_driverdata - per device type driver data for init time.
+ *
+ * @variant: the variant information for this driver.
+ * @lclk_frequency: G-Scaler clock frequency
+ * @num_entities: the number of g-scalers
+ */
+struct gsc_driverdata {
+ struct gsc_variant *variant[GSC_MAX_DEVS];
+ unsigned long lclk_frequency;
+ int num_entities;
+};
+
+/**
+ * struct gsc_dev - abstraction for G-Scaler entity
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the G-Scaler platform device
+ * @variant: the IP variant information
+ * @id: G-Scaler device index (0..GSC_MAX_DEVS)
+ * @clock: clocks required for G-Scaler operation
+ * @regs: the mapped hardware registers
+ * @irq_queue: interrupt handler waitqueue
+ * @m2m: memory-to-memory V4L2 device information
+ * @state: flags used to synchronize m2m and capture mode operation
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @vdev: video device for G-Scaler instance
+ */
+struct gsc_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct gsc_variant *variant;
+ u16 id;
+ struct clk *clock;
+ void __iomem *regs;
+ wait_queue_head_t irq_queue;
+ struct gsc_m2m_device m2m;
+ struct exynos_platform_gscaler *pdata;
+ unsigned long state;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct video_device vdev;
+};
+
+/**
+ * gsc_ctx - the device context data
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @in_path: input mode (DMA or camera)
+ * @out_path: output mode (DMA or FIFO)
+ * @scaler: image scaler properties
+ * @flags: additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @gsc_dev: the G-Scaler device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrl_handler: v4l2 controls handler
+ * @gsc_ctrls G-Scaler control set
+ * @ctrls_rdy: true if the control handler is initialized
+ */
+struct gsc_ctx {
+ struct gsc_frame s_frame;
+ struct gsc_frame d_frame;
+ enum gsc_datapath in_path;
+ enum gsc_datapath out_path;
+ struct gsc_scaler scaler;
+ u32 flags;
+ u32 state;
+ int rotation;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ struct gsc_dev *gsc_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct gsc_ctrls gsc_ctrls;
+ bool ctrls_rdy;
+};
+
+void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm);
+int gsc_register_m2m_device(struct gsc_dev *gsc);
+void gsc_unregister_m2m_device(struct gsc_dev *gsc);
+void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state);
+
+u32 get_plane_size(struct gsc_frame *fr, unsigned int plane);
+const struct gsc_fmt *get_format(int index);
+const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index);
+int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f);
+int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
+void gsc_set_frame_size(struct gsc_frame *frame, int width, int height);
+int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f);
+void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h);
+int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
+int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr);
+int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst,
+ u32 *ratio);
+void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh);
+void gsc_check_src_scale_info(struct gsc_variant *var,
+ struct gsc_frame *s_frame,
+ u32 *wratio, u32 tx, u32 ty, u32 *hratio);
+int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw,
+ int dh, int rot, int out_path);
+int gsc_set_scaler_info(struct gsc_ctx *ctx);
+int gsc_ctrls_create(struct gsc_ctx *ctx);
+void gsc_ctrls_delete(struct gsc_ctx *ctx);
+int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb,
+ struct gsc_frame *frame, struct gsc_addr *addr);
+
+static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
+ ctx->state |= state;
+ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
+}
+
+static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
+ ctx->state &= ~state;
+ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
+}
+
+static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + GSC_ENABLE);
+
+ if (on)
+ cfg |= GSC_ENABLE_ON;
+ else
+ cfg &= ~GSC_ENABLE_ON;
+
+ writel(cfg, dev->regs + GSC_ENABLE);
+}
+
+static inline int gsc_hw_get_irq_status(struct gsc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + GSC_IRQ);
+ if (cfg & GSC_IRQ_STATUS_OR_IRQ)
+ return GSC_IRQ_OVERRUN;
+ else
+ return GSC_IRQ_DONE;
+
+}
+
+static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq)
+{
+ u32 cfg = readl(dev->regs + GSC_IRQ);
+ if (irq == GSC_IRQ_OVERRUN)
+ cfg |= GSC_IRQ_STATUS_OR_IRQ;
+ else if (irq == GSC_IRQ_DONE)
+ cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ;
+ writel(cfg, dev->regs + GSC_IRQ);
+}
+
+static inline void gsc_lock(struct vb2_queue *vq)
+{
+ struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->gsc_dev->lock);
+}
+
+static inline void gsc_unlock(struct vb2_queue *vq)
+{
+ struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->gsc_dev->lock);
+}
+
+static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&ctx->gsc_dev->slock, flags);
+ ret = (ctx->state & mask) == mask;
+ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags);
+ return ret;
+}
+
+static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ struct gsc_frame *frame;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
+ frame = &ctx->s_frame;
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
+ frame = &ctx->d_frame;
+ } else {
+ pr_err("Wrong buffer/video queue type (%d)", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return frame;
+}
+
+void gsc_hw_set_sw_reset(struct gsc_dev *dev);
+int gsc_wait_reset(struct gsc_dev *dev);
+
+void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask);
+void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
+void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable);
+void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+ int index);
+void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+ int index);
+void gsc_hw_set_input_path(struct gsc_ctx *ctx);
+void gsc_hw_set_in_size(struct gsc_ctx *ctx);
+void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx);
+void gsc_hw_set_in_image_format(struct gsc_ctx *ctx);
+void gsc_hw_set_output_path(struct gsc_ctx *ctx);
+void gsc_hw_set_out_size(struct gsc_ctx *ctx);
+void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx);
+void gsc_hw_set_out_image_format(struct gsc_ctx *ctx);
+void gsc_hw_set_prescaler(struct gsc_ctx *ctx);
+void gsc_hw_set_mainscaler(struct gsc_ctx *ctx);
+void gsc_hw_set_rotation(struct gsc_ctx *ctx);
+void gsc_hw_set_global_alpha(struct gsc_ctx *ctx);
+void gsc_hw_set_sfr_update(struct gsc_ctx *ctx);
+
+#endif /* GSC_CORE_H_ */
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
new file mode 100644
index 000000000000..3c7f00577bd9
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-Scaler 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-ioctl.h>
+
+#include "gsc-core.h"
+
+static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx)
+{
+ struct gsc_ctx *curr_ctx;
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ int ret;
+
+ curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev);
+ if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx))
+ return 0;
+
+ gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx);
+ ret = wait_event_timeout(gsc->irq_queue,
+ !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx),
+ GSC_SHUTDOWN_TIMEOUT);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct gsc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
+}
+
+static int gsc_m2m_stop_streaming(struct vb2_queue *q)
+{
+ struct gsc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = gsc_m2m_ctx_stop_req(ctx);
+ if (ret == -ETIMEDOUT)
+ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put(&ctx->gsc_dev->pdev->dev);
+
+ return 0;
+}
+
+void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state)
+{
+ struct vb2_buffer *src_vb, *dst_vb;
+
+ if (!ctx || !ctx->m2m_ctx)
+ return;
+
+ 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, vb_state);
+ v4l2_m2m_buf_done(dst_vb, vb_state);
+
+ v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
+ }
+}
+
+
+static void gsc_m2m_job_abort(void *priv)
+{
+ struct gsc_ctx *ctx = priv;
+ int ret;
+
+ ret = gsc_m2m_ctx_stop_req(ctx);
+ if (ret == -ETIMEDOUT)
+ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+}
+
+static int gsc_fill_addr(struct gsc_ctx *ctx)
+{
+ struct gsc_frame *s_frame, *d_frame;
+ struct vb2_buffer *vb = NULL;
+ int ret;
+
+ s_frame = &ctx->s_frame;
+ d_frame = &ctx->d_frame;
+
+ vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = gsc_prepare_addr(ctx, vb, s_frame, &s_frame->addr);
+ if (ret)
+ return ret;
+
+ vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ return gsc_prepare_addr(ctx, vb, d_frame, &d_frame->addr);
+}
+
+static void gsc_m2m_device_run(void *priv)
+{
+ struct gsc_ctx *ctx = priv;
+ struct gsc_dev *gsc;
+ unsigned long flags;
+ u32 ret;
+ bool is_set = false;
+
+ if (WARN(!ctx, "null hardware context\n"))
+ return;
+
+ gsc = ctx->gsc_dev;
+ spin_lock_irqsave(&gsc->slock, flags);
+
+ set_bit(ST_M2M_PEND, &gsc->state);
+
+ /* Reconfigure hardware if the context has changed. */
+ if (gsc->m2m.ctx != ctx) {
+ pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p",
+ gsc->m2m.ctx, ctx);
+ ctx->state |= GSC_PARAMS;
+ gsc->m2m.ctx = ctx;
+ }
+
+ is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0;
+ ctx->state &= ~GSC_CTX_STOP_REQ;
+ if (is_set) {
+ wake_up(&gsc->irq_queue);
+ goto put_device;
+ }
+
+ ret = gsc_fill_addr(ctx);
+ if (ret) {
+ pr_err("Wrong address");
+ goto put_device;
+ }
+
+ gsc_set_prefbuf(gsc, &ctx->s_frame);
+ gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM);
+ gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM);
+
+ if (ctx->state & GSC_PARAMS) {
+ gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
+ gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false);
+ gsc_hw_set_frm_done_irq_mask(gsc, false);
+ gsc_hw_set_gsc_irq_enable(gsc, true);
+
+ if (gsc_set_scaler_info(ctx)) {
+ pr_err("Scaler setup error");
+ goto put_device;
+ }
+
+ gsc_hw_set_input_path(ctx);
+ gsc_hw_set_in_size(ctx);
+ gsc_hw_set_in_image_format(ctx);
+
+ gsc_hw_set_output_path(ctx);
+ gsc_hw_set_out_size(ctx);
+ gsc_hw_set_out_image_format(ctx);
+
+ gsc_hw_set_prescaler(ctx);
+ gsc_hw_set_mainscaler(ctx);
+ gsc_hw_set_rotation(ctx);
+ gsc_hw_set_global_alpha(ctx);
+ }
+
+ /* update shadow registers */
+ gsc_hw_set_sfr_update(ctx);
+
+ ctx->state &= ~GSC_PARAMS;
+ gsc_hw_enable_control(gsc, true);
+
+ spin_unlock_irqrestore(&gsc->slock, flags);
+ return;
+
+put_device:
+ ctx->state &= ~GSC_PARAMS;
+ spin_unlock_irqrestore(&gsc->slock, flags);
+}
+
+static int gsc_m2m_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct gsc_ctx *ctx = vb2_get_drv_priv(vq);
+ struct gsc_frame *frame;
+ int i;
+
+ frame = ctx_get_frame(ctx, vq->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ if (!frame->fmt)
+ return -EINVAL;
+
+ *num_planes = frame->fmt->num_planes;
+ for (i = 0; i < frame->fmt->num_planes; i++) {
+ sizes[i] = frame->payload[i];
+ allocators[i] = ctx->gsc_dev->alloc_ctx;
+ }
+ return 0;
+}
+
+static int gsc_m2m_buf_prepare(struct vb2_buffer *vb)
+{
+ struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct gsc_frame *frame;
+ int i;
+
+ frame = ctx_get_frame(ctx, vb->vb2_queue->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ for (i = 0; i < frame->fmt->num_planes; i++)
+ vb2_set_plane_payload(vb, i, frame->payload[i]);
+ }
+
+ return 0;
+}
+
+static void gsc_m2m_buf_queue(struct vb2_buffer *vb)
+{
+ struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops gsc_m2m_qops = {
+ .queue_setup = gsc_m2m_queue_setup,
+ .buf_prepare = gsc_m2m_buf_prepare,
+ .buf_queue = gsc_m2m_buf_queue,
+ .wait_prepare = gsc_unlock,
+ .wait_finish = gsc_lock,
+ .stop_streaming = gsc_m2m_stop_streaming,
+ .start_streaming = gsc_m2m_start_streaming,
+};
+
+static int gsc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ struct gsc_dev *gsc = ctx->gsc_dev;
+
+ strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver));
+ strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return gsc_enum_fmt_mplane(f);
+}
+
+static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+ return gsc_g_fmt_mplane(ctx, f);
+}
+
+static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+ return gsc_try_fmt_mplane(ctx, f);
+}
+
+static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ struct vb2_queue *vq;
+ struct gsc_frame *frame;
+ struct v4l2_pix_format_mplane *pix;
+ int i, ret = 0;
+
+ ret = gsc_m2m_try_fmt_mplane(file, fh, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+
+ if (vb2_is_streaming(vq)) {
+ pr_err("queue (%d) busy", f->type);
+ return -EBUSY;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ frame = &ctx->s_frame;
+ else
+ frame = &ctx->d_frame;
+
+ pix = &f->fmt.pix_mp;
+ frame->fmt = find_fmt(&pix->pixelformat, NULL, 0);
+ frame->colorspace = pix->colorspace;
+ if (!frame->fmt)
+ return -EINVAL;
+
+ for (i = 0; i < frame->fmt->num_planes; i++)
+ frame->payload[i] = pix->plane_fmt[i].sizeimage;
+
+ gsc_set_frame_size(frame, pix->width, pix->height);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx);
+ else
+ gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx);
+
+ pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
+
+ return 0;
+}
+
+static int gsc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ struct gsc_frame *frame;
+ u32 max_cnt;
+
+ max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
+ gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt;
+ if (reqbufs->count > max_cnt) {
+ return -EINVAL;
+ } else if (reqbufs->count == 0) {
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx);
+ else
+ gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx);
+ }
+
+ frame = ctx_get_frame(ctx, reqbufs->type);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int gsc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int gsc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+ /* The source and target color format need to be set */
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx))
+ return -EINVAL;
+ } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) {
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int gsc_m2m_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int gsc_m2m_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct gsc_frame *frame;
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ return -EINVAL;
+
+ frame = ctx_get_frame(ctx, s->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = frame->f_width;
+ s->r.height = frame->f_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = frame->crop.left;
+ s->r.top = frame->crop.top;
+ s->r.width = frame->crop.width;
+ s->r.height = frame->crop.height;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int gsc_m2m_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct gsc_frame *frame;
+ struct gsc_ctx *ctx = fh_to_ctx(fh);
+ struct v4l2_crop cr;
+ struct gsc_variant *variant = ctx->gsc_dev->variant;
+ int ret;
+
+ cr.type = s->type;
+ cr.c = s->r;
+
+ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) &&
+ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ return -EINVAL;
+
+ ret = gsc_try_crop(ctx, &cr);
+ if (ret)
+ return ret;
+
+ if (s->flags & V4L2_SEL_FLAG_LE &&
+ !is_rectangle_enclosed(&cr.c, &s->r))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE &&
+ !is_rectangle_enclosed(&s->r, &cr.c))
+ return -ERANGE;
+
+ s->r = cr.c;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ frame = &ctx->s_frame;
+ break;
+
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ frame = &ctx->d_frame;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if scaling ratio is within supported range */
+ if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) {
+ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = gsc_check_scaler_ratio(variant, cr.c.width,
+ cr.c.height, ctx->d_frame.crop.width,
+ ctx->d_frame.crop.height,
+ ctx->gsc_ctrls.rotate->val, ctx->out_path);
+ } else {
+ ret = gsc_check_scaler_ratio(variant,
+ ctx->s_frame.crop.width,
+ ctx->s_frame.crop.height, cr.c.width,
+ cr.c.height, ctx->gsc_ctrls.rotate->val,
+ ctx->out_path);
+ }
+
+ if (ret) {
+ pr_err("Out of scaler range");
+ return -EINVAL;
+ }
+ }
+
+ frame->crop = cr.c;
+
+ gsc_ctx_state_lock_set(GSC_PARAMS, ctx);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = {
+ .vidioc_querycap = gsc_m2m_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane,
+ .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane,
+ .vidioc_reqbufs = gsc_m2m_reqbufs,
+ .vidioc_querybuf = gsc_m2m_querybuf,
+ .vidioc_qbuf = gsc_m2m_qbuf,
+ .vidioc_dqbuf = gsc_m2m_dqbuf,
+ .vidioc_streamon = gsc_m2m_streamon,
+ .vidioc_streamoff = gsc_m2m_streamoff,
+ .vidioc_g_selection = gsc_m2m_g_selection,
+ .vidioc_s_selection = gsc_m2m_s_selection
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct gsc_ctx *ctx = priv;
+ 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 = &gsc_m2m_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ 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 = &gsc_m2m_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 gsc_m2m_open(struct file *file)
+{
+ struct gsc_dev *gsc = video_drvdata(file);
+ struct gsc_ctx *ctx = NULL;
+ int ret;
+
+ pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state);
+
+ if (mutex_lock_interruptible(&gsc->lock))
+ return -ERESTARTSYS;
+
+ ctx = kzalloc(sizeof (*ctx), GFP_KERNEL);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+
+ v4l2_fh_init(&ctx->fh, gsc->m2m.vfd);
+ ret = gsc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->gsc_dev = gsc;
+ /* Default color format */
+ ctx->s_frame.fmt = get_format(0);
+ ctx->d_frame.fmt = get_format(0);
+ /* Setup the device context for mem2mem mode. */
+ ctx->state = GSC_CTX_M2M;
+ ctx->flags = 0;
+ ctx->in_path = GSC_DMA;
+ ctx->out_path = GSC_DMA;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ pr_err("Failed to initialize m2m context");
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_ctrls;
+ }
+
+ if (gsc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_OPEN, &gsc->state);
+
+ pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx);
+
+ mutex_unlock(&gsc->lock);
+ return 0;
+
+error_ctrls:
+ gsc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+unlock:
+ mutex_unlock(&gsc->lock);
+ return ret;
+}
+
+static int gsc_m2m_release(struct file *file)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct gsc_dev *gsc = ctx->gsc_dev;
+
+ pr_debug("pid: %d, state: 0x%lx, refcnt= %d",
+ task_pid_nr(current), gsc->state, gsc->m2m.refcnt);
+
+ if (mutex_lock_interruptible(&gsc->lock))
+ return -ERESTARTSYS;
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ gsc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ if (--gsc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_OPEN, &gsc->state);
+ kfree(ctx);
+
+ mutex_unlock(&gsc->lock);
+ return 0;
+}
+
+static unsigned int gsc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&gsc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&gsc->lock);
+
+ return ret;
+}
+
+static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct gsc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct gsc_dev *gsc = ctx->gsc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&gsc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&gsc->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations gsc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = gsc_m2m_open,
+ .release = gsc_m2m_release,
+ .poll = gsc_m2m_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = gsc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops gsc_m2m_ops = {
+ .device_run = gsc_m2m_device_run,
+ .job_abort = gsc_m2m_job_abort,
+};
+
+int gsc_register_m2m_device(struct gsc_dev *gsc)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ if (!gsc)
+ return -ENODEV;
+
+ pdev = gsc->pdev;
+
+ gsc->vdev.fops = &gsc_m2m_fops;
+ gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops;
+ gsc->vdev.release = video_device_release_empty;
+ gsc->vdev.lock = &gsc->lock;
+ snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
+ GSC_MODULE_NAME, gsc->id);
+
+ video_set_drvdata(&gsc->vdev, gsc);
+
+ gsc->m2m.vfd = &gsc->vdev;
+ gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops);
+ if (IS_ERR(gsc->m2m.m2m_dev)) {
+ dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n");
+ ret = PTR_ERR(gsc->m2m.m2m_dev);
+ goto err_m2m_r1;
+ }
+
+ ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "%s(): failed to register video device\n", __func__);
+ goto err_m2m_r2;
+ }
+
+ pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num);
+ return 0;
+
+err_m2m_r2:
+ v4l2_m2m_release(gsc->m2m.m2m_dev);
+err_m2m_r1:
+ video_device_release(gsc->m2m.vfd);
+
+ return ret;
+}
+
+void gsc_unregister_m2m_device(struct gsc_dev *gsc)
+{
+ if (gsc)
+ v4l2_m2m_release(gsc->m2m.m2m_dev);
+}
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c
new file mode 100644
index 000000000000..0d8625f03a32
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/gsc-regs.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Samsung EXYNOS5 SoC series G-Scaler 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.
+ */
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <mach/map.h>
+
+#include "gsc-core.h"
+
+void gsc_hw_set_sw_reset(struct gsc_dev *dev)
+{
+ writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET);
+}
+
+int gsc_wait_reset(struct gsc_dev *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(50);
+ u32 cfg;
+
+ while (time_before(jiffies, end)) {
+ cfg = readl(dev->regs + GSC_SW_RESET);
+ if (!cfg)
+ return 0;
+ usleep_range(10, 20);
+ }
+
+ return -EBUSY;
+}
+
+void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask)
+{
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_IRQ);
+ if (mask)
+ cfg |= GSC_IRQ_FRMDONE_MASK;
+ else
+ cfg &= ~GSC_IRQ_FRMDONE_MASK;
+ writel(cfg, dev->regs + GSC_IRQ);
+}
+
+void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask)
+{
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_IRQ);
+ if (mask)
+ cfg |= GSC_IRQ_ENABLE;
+ else
+ cfg &= ~GSC_IRQ_ENABLE;
+ writel(cfg, dev->regs + GSC_IRQ);
+}
+
+void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift,
+ bool enable)
+{
+ u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+ u32 mask = 1 << shift;
+
+ cfg &= ~mask;
+ cfg |= enable << shift;
+
+ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK);
+ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK);
+ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK);
+}
+
+void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift,
+ bool enable)
+{
+ u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+ u32 mask = 1 << shift;
+
+ cfg &= ~mask;
+ cfg |= enable << shift;
+
+ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK);
+ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK);
+ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK);
+}
+
+void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr,
+ int index)
+{
+ pr_debug("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index,
+ addr->y, addr->cb, addr->cr);
+ writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index));
+ writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index));
+ writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index));
+
+}
+
+void gsc_hw_set_output_addr(struct gsc_dev *dev,
+ struct gsc_addr *addr, int index)
+{
+ pr_debug("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
+ index, addr->y, addr->cb, addr->cr);
+ writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index));
+ writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index));
+ writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index));
+}
+
+void gsc_hw_set_input_path(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+
+ u32 cfg = readl(dev->regs + GSC_IN_CON);
+ cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK);
+
+ if (ctx->in_path == GSC_DMA)
+ cfg |= GSC_IN_PATH_MEMORY;
+
+ writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_in_size(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->s_frame;
+ u32 cfg;
+
+ /* Set input pixel offset */
+ cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left);
+ cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top);
+ writel(cfg, dev->regs + GSC_SRCIMG_OFFSET);
+
+ /* Set input original size */
+ cfg = GSC_SRCIMG_WIDTH(frame->f_width);
+ cfg |= GSC_SRCIMG_HEIGHT(frame->f_height);
+ writel(cfg, dev->regs + GSC_SRCIMG_SIZE);
+
+ /* Set input cropped size */
+ cfg = GSC_CROPPED_WIDTH(frame->crop.width);
+ cfg |= GSC_CROPPED_HEIGHT(frame->crop.height);
+ writel(cfg, dev->regs + GSC_CROPPED_SIZE);
+}
+
+void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->s_frame;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_IN_CON);
+ if (frame->colorspace == V4L2_COLORSPACE_REC709)
+ cfg |= GSC_IN_RGB_HD_WIDE;
+ else
+ cfg |= GSC_IN_RGB_SD_WIDE;
+
+ if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
+ cfg |= GSC_IN_RGB565;
+ else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
+ cfg |= GSC_IN_XRGB8888;
+
+ writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_in_image_format(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->s_frame;
+ u32 i, depth = 0;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_IN_CON);
+ cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK |
+ GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK |
+ GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE);
+ writel(cfg, dev->regs + GSC_IN_CON);
+
+ if (is_rgb(frame->fmt->color)) {
+ gsc_hw_set_in_image_rgb(ctx);
+ return;
+ }
+ for (i = 0; i < frame->fmt->num_planes; i++)
+ depth += frame->fmt->depth[i];
+
+ switch (frame->fmt->num_comp) {
+ case 1:
+ cfg |= GSC_IN_YUV422_1P;
+ if (frame->fmt->yorder == GSC_LSB_Y)
+ cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y;
+ else
+ cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C;
+ if (frame->fmt->corder == GSC_CBCR)
+ cfg |= GSC_IN_CHROMA_ORDER_CBCR;
+ else
+ cfg |= GSC_IN_CHROMA_ORDER_CRCB;
+ break;
+ case 2:
+ if (depth == 12)
+ cfg |= GSC_IN_YUV420_2P;
+ else
+ cfg |= GSC_IN_YUV422_2P;
+ if (frame->fmt->corder == GSC_CBCR)
+ cfg |= GSC_IN_CHROMA_ORDER_CBCR;
+ else
+ cfg |= GSC_IN_CHROMA_ORDER_CRCB;
+ break;
+ case 3:
+ if (depth == 12)
+ cfg |= GSC_IN_YUV420_3P;
+ else
+ cfg |= GSC_IN_YUV422_3P;
+ break;
+ };
+
+ writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_output_path(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+
+ u32 cfg = readl(dev->regs + GSC_OUT_CON);
+ cfg &= ~GSC_OUT_PATH_MASK;
+
+ if (ctx->out_path == GSC_DMA)
+ cfg |= GSC_OUT_PATH_MEMORY;
+ else
+ cfg |= GSC_OUT_PATH_LOCAL;
+
+ writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_out_size(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->d_frame;
+ u32 cfg;
+
+ /* Set output original size */
+ if (ctx->out_path == GSC_DMA) {
+ cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left);
+ cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top);
+ writel(cfg, dev->regs + GSC_DSTIMG_OFFSET);
+
+ cfg = GSC_DSTIMG_WIDTH(frame->f_width);
+ cfg |= GSC_DSTIMG_HEIGHT(frame->f_height);
+ writel(cfg, dev->regs + GSC_DSTIMG_SIZE);
+ }
+
+ /* Set output scaled size */
+ if (ctx->gsc_ctrls.rotate->val == 90 ||
+ ctx->gsc_ctrls.rotate->val == 270) {
+ cfg = GSC_SCALED_WIDTH(frame->crop.height);
+ cfg |= GSC_SCALED_HEIGHT(frame->crop.width);
+ } else {
+ cfg = GSC_SCALED_WIDTH(frame->crop.width);
+ cfg |= GSC_SCALED_HEIGHT(frame->crop.height);
+ }
+ writel(cfg, dev->regs + GSC_SCALED_SIZE);
+}
+
+void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->d_frame;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_OUT_CON);
+ if (frame->colorspace == V4L2_COLORSPACE_REC709)
+ cfg |= GSC_OUT_RGB_HD_WIDE;
+ else
+ cfg |= GSC_OUT_RGB_SD_WIDE;
+
+ if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X)
+ cfg |= GSC_OUT_RGB565;
+ else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32)
+ cfg |= GSC_OUT_XRGB8888;
+
+ writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_out_image_format(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->d_frame;
+ u32 i, depth = 0;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_OUT_CON);
+ cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK |
+ GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK |
+ GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE);
+ writel(cfg, dev->regs + GSC_OUT_CON);
+
+ if (is_rgb(frame->fmt->color)) {
+ gsc_hw_set_out_image_rgb(ctx);
+ return;
+ }
+
+ if (ctx->out_path != GSC_DMA) {
+ cfg |= GSC_OUT_YUV444;
+ goto end_set;
+ }
+
+ for (i = 0; i < frame->fmt->num_planes; i++)
+ depth += frame->fmt->depth[i];
+
+ switch (frame->fmt->num_comp) {
+ case 1:
+ cfg |= GSC_OUT_YUV422_1P;
+ if (frame->fmt->yorder == GSC_LSB_Y)
+ cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y;
+ else
+ cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C;
+ if (frame->fmt->corder == GSC_CBCR)
+ cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
+ else
+ cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
+ break;
+ case 2:
+ if (depth == 12)
+ cfg |= GSC_OUT_YUV420_2P;
+ else
+ cfg |= GSC_OUT_YUV422_2P;
+ if (frame->fmt->corder == GSC_CBCR)
+ cfg |= GSC_OUT_CHROMA_ORDER_CBCR;
+ else
+ cfg |= GSC_OUT_CHROMA_ORDER_CRCB;
+ break;
+ case 3:
+ cfg |= GSC_OUT_YUV420_3P;
+ break;
+ };
+
+end_set:
+ writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_prescaler(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_scaler *sc = &ctx->scaler;
+ u32 cfg;
+
+ cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor);
+ cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio);
+ cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio);
+ writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO);
+}
+
+void gsc_hw_set_mainscaler(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_scaler *sc = &ctx->scaler;
+ u32 cfg;
+
+ cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio);
+ writel(cfg, dev->regs + GSC_MAIN_H_RATIO);
+
+ cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio);
+ writel(cfg, dev->regs + GSC_MAIN_V_RATIO);
+}
+
+void gsc_hw_set_rotation(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_IN_CON);
+ cfg &= ~GSC_IN_ROT_MASK;
+
+ switch (ctx->gsc_ctrls.rotate->val) {
+ case 270:
+ cfg |= GSC_IN_ROT_270;
+ break;
+ case 180:
+ cfg |= GSC_IN_ROT_180;
+ break;
+ case 90:
+ if (ctx->gsc_ctrls.hflip->val)
+ cfg |= GSC_IN_ROT_90_XFLIP;
+ else if (ctx->gsc_ctrls.vflip->val)
+ cfg |= GSC_IN_ROT_90_YFLIP;
+ else
+ cfg |= GSC_IN_ROT_90;
+ break;
+ case 0:
+ if (ctx->gsc_ctrls.hflip->val)
+ cfg |= GSC_IN_ROT_XFLIP;
+ else if (ctx->gsc_ctrls.vflip->val)
+ cfg |= GSC_IN_ROT_YFLIP;
+ }
+
+ writel(cfg, dev->regs + GSC_IN_CON);
+}
+
+void gsc_hw_set_global_alpha(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ struct gsc_frame *frame = &ctx->d_frame;
+ u32 cfg;
+
+ if (!is_rgb(frame->fmt->color)) {
+ pr_debug("Not a RGB format");
+ return;
+ }
+
+ cfg = readl(dev->regs + GSC_OUT_CON);
+ cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK;
+
+ cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val);
+ writel(cfg, dev->regs + GSC_OUT_CON);
+}
+
+void gsc_hw_set_sfr_update(struct gsc_ctx *ctx)
+{
+ struct gsc_dev *dev = ctx->gsc_dev;
+ u32 cfg;
+
+ cfg = readl(dev->regs + GSC_ENABLE);
+ cfg |= GSC_ENABLE_SFR_UPDATE;
+ writel(cfg, dev->regs + GSC_ENABLE);
+}
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.h b/drivers/media/platform/exynos-gsc/gsc-regs.h
new file mode 100644
index 000000000000..533e9947a925
--- /dev/null
+++ b/drivers/media/platform/exynos-gsc/gsc-regs.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Register definition file for Samsung G-Scaler 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.
+ */
+
+#ifndef REGS_GSC_H_
+#define REGS_GSC_H_
+
+/* G-Scaler enable */
+#define GSC_ENABLE 0x00
+#define GSC_ENABLE_OP_STATUS (1 << 2)
+#define GSC_ENABLE_SFR_UPDATE (1 << 1)
+#define GSC_ENABLE_ON (1 << 0)
+
+/* G-Scaler S/W reset */
+#define GSC_SW_RESET 0x04
+#define GSC_SW_RESET_SRESET (1 << 0)
+
+/* G-Scaler IRQ */
+#define GSC_IRQ 0x08
+#define GSC_IRQ_STATUS_OR_IRQ (1 << 17)
+#define GSC_IRQ_STATUS_FRM_DONE_IRQ (1 << 16)
+#define GSC_IRQ_FRMDONE_MASK (1 << 1)
+#define GSC_IRQ_ENABLE (1 << 0)
+
+/* G-Scaler input control */
+#define GSC_IN_CON 0x10
+#define GSC_IN_ROT_MASK (7 << 16)
+#define GSC_IN_ROT_270 (7 << 16)
+#define GSC_IN_ROT_90_YFLIP (6 << 16)
+#define GSC_IN_ROT_90_XFLIP (5 << 16)
+#define GSC_IN_ROT_90 (4 << 16)
+#define GSC_IN_ROT_180 (3 << 16)
+#define GSC_IN_ROT_YFLIP (2 << 16)
+#define GSC_IN_ROT_XFLIP (1 << 16)
+#define GSC_IN_RGB_TYPE_MASK (3 << 14)
+#define GSC_IN_RGB_HD_WIDE (3 << 14)
+#define GSC_IN_RGB_HD_NARROW (2 << 14)
+#define GSC_IN_RGB_SD_WIDE (1 << 14)
+#define GSC_IN_RGB_SD_NARROW (0 << 14)
+#define GSC_IN_YUV422_1P_ORDER_MASK (1 << 13)
+#define GSC_IN_YUV422_1P_ORDER_LSB_Y (0 << 13)
+#define GSC_IN_YUV422_1P_OEDER_LSB_C (1 << 13)
+#define GSC_IN_CHROMA_ORDER_MASK (1 << 12)
+#define GSC_IN_CHROMA_ORDER_CBCR (0 << 12)
+#define GSC_IN_CHROMA_ORDER_CRCB (1 << 12)
+#define GSC_IN_FORMAT_MASK (7 << 8)
+#define GSC_IN_XRGB8888 (0 << 8)
+#define GSC_IN_RGB565 (1 << 8)
+#define GSC_IN_YUV420_2P (2 << 8)
+#define GSC_IN_YUV420_3P (3 << 8)
+#define GSC_IN_YUV422_1P (4 << 8)
+#define GSC_IN_YUV422_2P (5 << 8)
+#define GSC_IN_YUV422_3P (6 << 8)
+#define GSC_IN_TILE_TYPE_MASK (1 << 4)
+#define GSC_IN_TILE_C_16x8 (0 << 4)
+#define GSC_IN_TILE_MODE (1 << 3)
+#define GSC_IN_LOCAL_SEL_MASK (3 << 1)
+#define GSC_IN_PATH_MASK (1 << 0)
+#define GSC_IN_PATH_MEMORY (0 << 0)
+
+/* G-Scaler source image size */
+#define GSC_SRCIMG_SIZE 0x14
+#define GSC_SRCIMG_HEIGHT(x) ((x) << 16)
+#define GSC_SRCIMG_WIDTH(x) ((x) << 0)
+
+/* G-Scaler source image offset */
+#define GSC_SRCIMG_OFFSET 0x18
+#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16)
+#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0)
+
+/* G-Scaler cropped source image size */
+#define GSC_CROPPED_SIZE 0x1c
+#define GSC_CROPPED_HEIGHT(x) ((x) << 16)
+#define GSC_CROPPED_WIDTH(x) ((x) << 0)
+
+/* G-Scaler output control */
+#define GSC_OUT_CON 0x20
+#define GSC_OUT_GLOBAL_ALPHA_MASK (0xff << 24)
+#define GSC_OUT_GLOBAL_ALPHA(x) ((x) << 24)
+#define GSC_OUT_RGB_TYPE_MASK (3 << 10)
+#define GSC_OUT_RGB_HD_NARROW (3 << 10)
+#define GSC_OUT_RGB_HD_WIDE (2 << 10)
+#define GSC_OUT_RGB_SD_NARROW (1 << 10)
+#define GSC_OUT_RGB_SD_WIDE (0 << 10)
+#define GSC_OUT_YUV422_1P_ORDER_MASK (1 << 9)
+#define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0 << 9)
+#define GSC_OUT_YUV422_1P_OEDER_LSB_C (1 << 9)
+#define GSC_OUT_CHROMA_ORDER_MASK (1 << 8)
+#define GSC_OUT_CHROMA_ORDER_CBCR (0 << 8)
+#define GSC_OUT_CHROMA_ORDER_CRCB (1 << 8)
+#define GSC_OUT_FORMAT_MASK (7 << 4)
+#define GSC_OUT_XRGB8888 (0 << 4)
+#define GSC_OUT_RGB565 (1 << 4)
+#define GSC_OUT_YUV420_2P (2 << 4)
+#define GSC_OUT_YUV420_3P (3 << 4)
+#define GSC_OUT_YUV422_1P (4 << 4)
+#define GSC_OUT_YUV422_2P (5 << 4)
+#define GSC_OUT_YUV444 (7 << 4)
+#define GSC_OUT_TILE_TYPE_MASK (1 << 2)
+#define GSC_OUT_TILE_C_16x8 (0 << 2)
+#define GSC_OUT_TILE_MODE (1 << 1)
+#define GSC_OUT_PATH_MASK (1 << 0)
+#define GSC_OUT_PATH_LOCAL (1 << 0)
+#define GSC_OUT_PATH_MEMORY (0 << 0)
+
+/* G-Scaler scaled destination image size */
+#define GSC_SCALED_SIZE 0x24
+#define GSC_SCALED_HEIGHT(x) ((x) << 16)
+#define GSC_SCALED_WIDTH(x) ((x) << 0)
+
+/* G-Scaler pre scale ratio */
+#define GSC_PRE_SCALE_RATIO 0x28
+#define GSC_PRESC_SHFACTOR(x) ((x) << 28)
+#define GSC_PRESC_V_RATIO(x) ((x) << 16)
+#define GSC_PRESC_H_RATIO(x) ((x) << 0)
+
+/* G-Scaler main scale horizontal ratio */
+#define GSC_MAIN_H_RATIO 0x2c
+#define GSC_MAIN_H_RATIO_VALUE(x) ((x) << 0)
+
+/* G-Scaler main scale vertical ratio */
+#define GSC_MAIN_V_RATIO 0x30
+#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0)
+
+/* G-Scaler destination image size */
+#define GSC_DSTIMG_SIZE 0x40
+#define GSC_DSTIMG_HEIGHT(x) ((x) << 16)
+#define GSC_DSTIMG_WIDTH(x) ((x) << 0)
+
+/* G-Scaler destination image offset */
+#define GSC_DSTIMG_OFFSET 0x44
+#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16)
+#define GSC_DSTIMG_OFFSET_X(x) ((x) << 0)
+
+/* G-Scaler input y address mask */
+#define GSC_IN_BASE_ADDR_Y_MASK 0x4c
+/* G-Scaler input y base address */
+#define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4)
+
+/* G-Scaler input cb address mask */
+#define GSC_IN_BASE_ADDR_CB_MASK 0x7c
+/* G-Scaler input cb base address */
+#define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4)
+
+/* G-Scaler input cr address mask */
+#define GSC_IN_BASE_ADDR_CR_MASK 0xac
+/* G-Scaler input cr base address */
+#define GSC_IN_BASE_ADDR_CR(n) (0xb0 + (n) * 0x4)
+
+/* G-Scaler output y address mask */
+#define GSC_OUT_BASE_ADDR_Y_MASK 0x10c
+/* G-Scaler output y base address */
+#define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4)
+
+/* G-Scaler output cb address mask */
+#define GSC_OUT_BASE_ADDR_CB_MASK 0x15c
+/* G-Scaler output cb base address */
+#define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4)
+
+/* G-Scaler output cr address mask */
+#define GSC_OUT_BASE_ADDR_CR_MASK 0x1ac
+/* G-Scaler output cr base address */
+#define GSC_OUT_BASE_ADDR_CR(n) (0x1b0 + (n) * 0x4)
+
+#endif /* REGS_GSC_H_ */
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
new file mode 100644
index 000000000000..897250b88647
--- /dev/null
+++ b/drivers/media/platform/fsl-viu.c
@@ -0,0 +1,1690 @@
+/*
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Freescale VIU video driver
+ *
+ * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
+ * Porting to 2.6.35 by DENX Software Engineering,
+ * Anatolij Gustschin <agust@denx.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#define DRV_NAME "fsl_viu"
+#define VIU_VERSION "0.5.1"
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+
+#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */
+
+/* I2C address of video decoder chip is 0x4A */
+#define VIU_VIDEO_DECODER_ADDR 0x25
+
+/* supported controls */
+static struct v4l2_queryctrl viu_qctrl[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 0x10,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 127,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .step = 0x1,
+ .default_value = 0,
+ .flags = 0,
+ }
+};
+
+static int qctl_regs[ARRAY_SIZE(viu_qctrl)];
+
+static int info_level;
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (level <= info_level) \
+ printk(KERN_DEBUG "viu: " fmt , ## arg); \
+ } while (0)
+
+/*
+ * Basic structures
+ */
+struct viu_fmt {
+ char name[32];
+ u32 fourcc; /* v4l2 format id */
+ u32 pixelformat;
+ int depth;
+};
+
+static struct viu_fmt formats[] = {
+ {
+ .name = "RGB-16 (5/B-6/G-5/R)",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ }, {
+ .name = "RGB-32 (A-R-G-B)",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ }
+};
+
+struct viu_dev;
+struct viu_buf;
+
+/* buffer for one video frame */
+struct viu_buf {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ struct viu_fmt *fmt;
+};
+
+struct viu_dmaqueue {
+ struct viu_dev *dev;
+ struct list_head active;
+ struct list_head queued;
+ struct timer_list timeout;
+};
+
+struct viu_status {
+ u32 field_irq;
+ u32 vsync_irq;
+ u32 hsync_irq;
+ u32 vstart_irq;
+ u32 dma_end_irq;
+ u32 error_irq;
+};
+
+struct viu_reg {
+ u32 status_cfg;
+ u32 luminance;
+ u32 chroma_r;
+ u32 chroma_g;
+ u32 chroma_b;
+ u32 field_base_addr;
+ u32 dma_inc;
+ u32 picture_count;
+ u32 req_alarm;
+ u32 alpha;
+} __attribute__ ((packed));
+
+struct viu_dev {
+ struct v4l2_device v4l2_dev;
+ struct mutex lock;
+ spinlock_t slock;
+ int users;
+
+ struct device *dev;
+ /* various device info */
+ struct video_device *vdev;
+ struct viu_dmaqueue vidq;
+ enum v4l2_field capfield;
+ int field;
+ int first;
+ int dma_done;
+
+ /* Hardware register area */
+ struct viu_reg *vr;
+
+ /* Interrupt vector */
+ int irq;
+ struct viu_status irqs;
+
+ /* video overlay */
+ struct v4l2_framebuffer ovbuf;
+ struct viu_fmt *ovfmt;
+ unsigned int ovenable;
+ enum v4l2_field ovfield;
+
+ /* crop */
+ struct v4l2_rect crop_current;
+
+ /* clock pointer */
+ struct clk *clk;
+
+ /* decoder */
+ struct v4l2_subdev *decoder;
+
+ v4l2_std_id std;
+};
+
+struct viu_fh {
+ struct viu_dev *dev;
+
+ /* video capture */
+ struct videobuf_queue vb_vidq;
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip clips[1];
+
+ /* video capture */
+ struct viu_fmt *fmt;
+ int width, height, sizeimage;
+ enum v4l2_buf_type type;
+};
+
+static struct viu_reg reg_val;
+
+/*
+ * Macro definitions of VIU registers
+ */
+
+/* STATUS_CONFIG register */
+enum status_config {
+ SOFT_RST = 1 << 0,
+
+ ERR_MASK = 0x0f << 4, /* Error code mask */
+ ERR_NO = 0x00, /* No error */
+ ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */
+ ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */
+ ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */
+ ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */
+ ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */
+ ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */
+ ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */
+ ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */
+ ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */
+ ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */
+
+ INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */
+ INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */
+ INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */
+ INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */
+ INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */
+ INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */
+ INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */
+
+ INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */
+ INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */
+ INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */
+ INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */
+ INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */
+ INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */
+
+ DMA_ACT = 0x01 << 27, /* Enable DMA transfer */
+ FIELD_NO = 0x01 << 28, /* Field number */
+ DITHER_ON = 0x01 << 29, /* Dithering is on */
+ ROUND_ON = 0x01 << 30, /* Round is on */
+ MODE_32BIT = 0x01 << 31, /* Data in RGBa888,
+ * 0 in RGB565
+ */
+};
+
+#define norm_maxw() 720
+#define norm_maxh() 576
+
+#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \
+ INT_HSYNC_STATUS | INT_VSTART_STATUS | \
+ INT_DMA_END_STATUS | INT_ERROR_STATUS)
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static irqreturn_t viu_intr(int irq, void *dev_id);
+
+struct viu_fmt *format_by_fourcc(int fourcc)
+{
+ int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].pixelformat == fourcc)
+ return formats + i;
+ }
+
+ dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
+ return NULL;
+}
+
+void viu_start_dma(struct viu_dev *dev)
+{
+ struct viu_reg *vr = dev->vr;
+
+ dev->field = 0;
+
+ /* Enable DMA operation */
+ out_be32(&vr->status_cfg, SOFT_RST);
+ out_be32(&vr->status_cfg, INT_FIELD_EN);
+}
+
+void viu_stop_dma(struct viu_dev *dev)
+{
+ struct viu_reg *vr = dev->vr;
+ int cnt = 100;
+ u32 status_cfg;
+
+ out_be32(&vr->status_cfg, 0);
+
+ /* Clear pending interrupts */
+ status_cfg = in_be32(&vr->status_cfg);
+ if (status_cfg & 0x3f0000)
+ out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
+
+ if (status_cfg & DMA_ACT) {
+ do {
+ status_cfg = in_be32(&vr->status_cfg);
+ if (status_cfg & INT_DMA_END_STATUS)
+ break;
+ } while (cnt--);
+
+ if (cnt < 0) {
+ /* timed out, issue soft reset */
+ out_be32(&vr->status_cfg, SOFT_RST);
+ out_be32(&vr->status_cfg, 0);
+ } else {
+ /* clear DMA_END and other pending irqs */
+ out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
+ }
+ }
+
+ dev->field = 0;
+}
+
+static int restart_video_queue(struct viu_dmaqueue *vidq)
+{
+ struct viu_buf *buf, *prev;
+
+ dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq);
+ if (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+
+ viu_stop_dma(vidq->dev);
+
+ /* cancel all outstanding capture requests */
+ list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) {
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ }
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&vidq->queued))
+ return 0;
+ buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue);
+ if (prev == NULL) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+ dprintk(1, "Restarting video dma\n");
+ viu_stop_dma(vidq->dev);
+ viu_start_dma(vidq->dev);
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_del(&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static void viu_vid_timeout(unsigned long data)
+{
+ struct viu_dev *dev = (struct viu_dev *)data;
+ struct viu_buf *buf;
+ struct viu_dmaqueue *vidq = &dev->vidq;
+
+ while (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i);
+ }
+
+ restart_video_queue(vidq);
+}
+
+/*
+ * Videobuf operations
+ */
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct viu_fh *fh = vq->priv_data;
+
+ *size = fh->width * fh->height * fh->fmt->depth >> 3;
+ if (*count == 0)
+ *count = 32;
+
+ while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024)
+ (*count)--;
+
+ dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size);
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+ void *vaddr = NULL;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+
+ if (vq->int_ops && vq->int_ops->vaddr)
+ vaddr = vq->int_ops->vaddr(vb);
+
+ if (vaddr)
+ videobuf_dma_contig_free(vq, &buf->vb);
+
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf)
+{
+ struct viu_reg *vr = dev->vr;
+ int bpp;
+
+ /* setup the DMA base address */
+ reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb);
+
+ dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n",
+ buf, buf->vb.i, (unsigned long)reg_val.field_base_addr);
+
+ /* interlace is on by default, set horizontal DMA increment */
+ reg_val.status_cfg = 0;
+ bpp = buf->fmt->depth >> 3;
+ switch (bpp) {
+ case 2:
+ reg_val.status_cfg &= ~MODE_32BIT;
+ reg_val.dma_inc = buf->vb.width * 2;
+ break;
+ case 4:
+ reg_val.status_cfg |= MODE_32BIT;
+ reg_val.dma_inc = buf->vb.width * 4;
+ break;
+ default:
+ dprintk(0, "doesn't support color depth(%d)\n",
+ bpp * 8);
+ return -EINVAL;
+ }
+
+ /* setup picture_count register */
+ reg_val.picture_count = (buf->vb.height / 2) << 16 |
+ buf->vb.width;
+
+ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
+
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dev->capfield = buf->vb.field;
+
+ /* reset dma increment if needed */
+ if (!V4L2_FIELD_HAS_BOTH(buf->vb.field))
+ reg_val.dma_inc = 0;
+
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->picture_count, reg_val.picture_count);
+ out_be32(&vr->field_base_addr, reg_val.field_base_addr);
+ mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT);
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ int rc;
+
+ BUG_ON(fh->fmt == NULL);
+
+ if (fh->width < 48 || fh->width > norm_maxw() ||
+ fh->height < 32 || fh->height > norm_maxh())
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ }
+
+ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ buf->fmt = fh->fmt;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_dev *dev = fh->dev;
+ struct viu_dmaqueue *vidq = &dev->vidq;
+ struct viu_buf *prev;
+
+ if (!list_empty(&vidq->queued)) {
+ dprintk(1, "adding vb queue=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ dprintk(1, "vidq pointer 0x%p, queued 0x%p\n",
+ vidq, &vidq->queued);
+ dprintk(1, "dev %p, queued: self %p, next %p, head %p\n",
+ dev, &vidq->queued, vidq->queued.next,
+ vidq->queued.prev);
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+ } else if (list_empty(&vidq->active)) {
+ dprintk(1, "adding vb active=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ buffer_activate(dev, buf);
+ } else {
+ dprintk(1, "adding vb queue2=0x%08lx\n",
+ (unsigned long)&buf->vb.queue);
+ prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ } else {
+ list_add_tail(&buf->vb.queue, &vidq->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
+ struct viu_fh *fh = vq->priv_data;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+
+ viu_stop_dma(dev);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops viu_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/*
+ * IOCTL vidioc handling
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strcpy(cap->driver, "viu");
+ strcpy(cap->card, "viu");
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_OVERLAY |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (f->index > NUM_FORMATS)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[index].name, sizeof(f->description));
+ f->pixelformat = formats[index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->pixelformat;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = fh->sizeimage;
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ dprintk(1, "Fourcc format (0x%08x) invalid.",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY) {
+ field = V4L2_FIELD_INTERLACED;
+ } else if (field != V4L2_FIELD_INTERLACED) {
+ dprintk(1, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ maxw = norm_maxw();
+ maxh = norm_maxh();
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+ int ret;
+
+ ret = vidioc_try_fmt_cap(file, fh, f);
+ if (ret < 0)
+ return ret;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->sizeimage = f->fmt.pix.sizeimage;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+ dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name);
+ return 0;
+}
+
+static int vidioc_g_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+
+ f->fmt.win = fh->win;
+ return 0;
+}
+
+static int verify_preview(struct viu_dev *dev, struct v4l2_window *win)
+{
+ enum v4l2_field field;
+ int maxw, maxh;
+
+ if (dev->ovbuf.base == NULL)
+ return -EINVAL;
+ if (dev->ovfmt == NULL)
+ return -EINVAL;
+ if (win->w.width < 48 || win->w.height < 32)
+ return -EINVAL;
+
+ field = win->field;
+ maxw = dev->crop_current.width;
+ maxh = dev->crop_current.height;
+
+ if (field == V4L2_FIELD_ANY) {
+ field = (win->w.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_TOP;
+ }
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ win->field = field;
+ if (win->w.width > maxw)
+ win->w.width = maxw;
+ if (win->w.height > maxh)
+ win->w.height = maxh;
+ return 0;
+}
+
+inline void viu_activate_overlay(struct viu_reg *viu_reg)
+{
+ struct viu_reg *vr = viu_reg;
+
+ out_be32(&vr->field_base_addr, reg_val.field_base_addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->picture_count, reg_val.picture_count);
+}
+
+static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh)
+{
+ int bpp;
+
+ dprintk(1, "%s %dx%d %s\n", __func__,
+ fh->win.w.width, fh->win.w.height, dev->ovfmt->name);
+
+ reg_val.status_cfg = 0;
+
+ /* setup window */
+ reg_val.picture_count = (fh->win.w.height / 2) << 16 |
+ fh->win.w.width;
+
+ /* setup color depth and dma increment */
+ bpp = dev->ovfmt->depth / 8;
+ switch (bpp) {
+ case 2:
+ reg_val.status_cfg &= ~MODE_32BIT;
+ reg_val.dma_inc = fh->win.w.width * 2;
+ break;
+ case 4:
+ reg_val.status_cfg |= MODE_32BIT;
+ reg_val.dma_inc = fh->win.w.width * 4;
+ break;
+ default:
+ dprintk(0, "device doesn't support color depth(%d)\n",
+ bpp * 8);
+ return -EINVAL;
+ }
+
+ dev->ovfield = fh->win.field;
+ if (!V4L2_FIELD_HAS_BOTH(dev->ovfield))
+ reg_val.dma_inc = 0;
+
+ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
+
+ /* setup the base address of the overlay buffer */
+ reg_val.field_base_addr = (u32)dev->ovbuf.base;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+ unsigned long flags;
+ int err;
+
+ err = verify_preview(dev, &f->fmt.win);
+ if (err)
+ return err;
+
+ fh->win = f->fmt.win;
+
+ spin_lock_irqsave(&dev->slock, flags);
+ viu_setup_preview(dev, fh);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return 0;
+}
+
+static int vidioc_try_fmt_overlay(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+static int vidioc_overlay(struct file *file, void *priv, unsigned int on)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = (struct viu_dev *)fh->dev;
+ unsigned long flags;
+
+ if (on) {
+ spin_lock_irqsave(&dev->slock, flags);
+ viu_activate_overlay(dev->vr);
+ dev->ovenable = 1;
+
+ /* start dma */
+ viu_start_dma(dev);
+ spin_unlock_irqrestore(&dev->slock, flags);
+ } else {
+ viu_stop_dma(dev);
+ dev->ovenable = 0;
+ }
+
+ return 0;
+}
+
+int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+ struct v4l2_framebuffer *fb = arg;
+
+ *fb = dev->ovbuf;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ return 0;
+}
+
+int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+ struct v4l2_framebuffer *fb = arg;
+ struct viu_fmt *fmt;
+
+ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* check args */
+ fmt = format_by_fourcc(fb->fmt.pixelformat);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ /* ok, accept it */
+ dev->ovbuf = *fb;
+ dev->ovfmt = fmt;
+ if (dev->ovbuf.fmt.bytesperline == 0) {
+ dev->ovbuf.fmt.bytesperline =
+ dev->ovbuf.fmt.width * fmt->depth / 8;
+ }
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_reqbufs(&fh->vb_vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_querybuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_qbuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct viu_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vb_vidq, p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct viu_fh *fh = priv;
+ struct viu_dev *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (fh->type != i)
+ return -EINVAL;
+
+ if (dev->ovenable)
+ dev->ovenable = 0;
+
+ viu_start_dma(fh->dev);
+
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct viu_fh *fh = priv;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (fh->type != i)
+ return -EINVAL;
+
+ viu_stop_dma(fh->dev);
+
+ return videobuf_streamoff(&fh->vb_vidq);
+}
+
+#define decoder_call(viu, o, f, args...) \
+ v4l2_subdev_call(viu->decoder, o, f, ##args)
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct viu_fh *fh = priv;
+
+ decoder_call(fh->dev, video, querystd, std_id);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct viu_fh *fh = priv;
+
+ fh->dev->std = *id;
+ decoder_call(fh->dev, core, s_std, *id);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct viu_fh *fh = priv;
+
+ *std_id = fh->dev->std;
+ return 0;
+}
+
+/* only one input in this driver */
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct viu_fh *fh = priv;
+
+ if (inp->index != 0)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = fh->dev->vdev->tvnorms;
+ strcpy(inp->name, "Camera");
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct viu_fh *fh = priv;
+
+ if (i > 1)
+ return -EINVAL;
+
+ decoder_call(fh->dev, video, s_routing, i, 0, 0);
+ return 0;
+}
+
+/* Controls */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (qc->id && qc->id == viu_qctrl[i].id) {
+ memcpy(qc, &(viu_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (ctrl->id == viu_qctrl[i].id) {
+ ctrl->value = qctl_regs[i];
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
+ if (ctrl->id == viu_qctrl[i].id) {
+ if (ctrl->value < viu_qctrl[i].minimum
+ || ctrl->value > viu_qctrl[i].maximum)
+ return -ERANGE;
+ qctl_regs[i] = ctrl->value;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+inline void viu_activate_next_buf(struct viu_dev *dev,
+ struct viu_dmaqueue *viuq)
+{
+ struct viu_dmaqueue *vidq = viuq;
+ struct viu_buf *buf;
+
+ /* launch another DMA operation for an active/queued buffer */
+ if (!list_empty(&vidq->active)) {
+ buf = list_entry(vidq->active.next, struct viu_buf,
+ vb.queue);
+ dprintk(1, "start another queued buffer: 0x%p\n", buf);
+ buffer_activate(dev, buf);
+ } else if (!list_empty(&vidq->queued)) {
+ buf = list_entry(vidq->queued.next, struct viu_buf,
+ vb.queue);
+ list_del(&buf->vb.queue);
+
+ dprintk(1, "start another queued buffer: 0x%p\n", buf);
+ list_add_tail(&buf->vb.queue, &vidq->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buffer_activate(dev, buf);
+ }
+}
+
+inline void viu_default_settings(struct viu_reg *viu_reg)
+{
+ struct viu_reg *vr = viu_reg;
+
+ out_be32(&vr->luminance, 0x9512A254);
+ out_be32(&vr->chroma_r, 0x03310000);
+ out_be32(&vr->chroma_g, 0x06600F38);
+ out_be32(&vr->chroma_b, 0x00000409);
+ out_be32(&vr->alpha, 0x000000ff);
+ out_be32(&vr->req_alarm, 0x00000090);
+ dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n",
+ in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr));
+}
+
+static void viu_overlay_intr(struct viu_dev *dev, u32 status)
+{
+ struct viu_reg *vr = dev->vr;
+
+ if (status & INT_DMA_END_STATUS)
+ dev->dma_done = 1;
+
+ if (status & INT_FIELD_STATUS) {
+ if (dev->dma_done) {
+ u32 addr = reg_val.field_base_addr;
+
+ dev->dma_done = 0;
+ if (status & FIELD_NO)
+ addr += reg_val.dma_inc;
+
+ out_be32(&vr->field_base_addr, addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ } else if (status & INT_VSYNC_STATUS) {
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ }
+ }
+}
+
+static void viu_capture_intr(struct viu_dev *dev, u32 status)
+{
+ struct viu_dmaqueue *vidq = &dev->vidq;
+ struct viu_reg *vr = dev->vr;
+ struct viu_buf *buf;
+ int field_num;
+ int need_two;
+ int dma_done = 0;
+
+ field_num = status & FIELD_NO;
+ need_two = V4L2_FIELD_HAS_BOTH(dev->capfield);
+
+ if (status & INT_DMA_END_STATUS) {
+ dma_done = 1;
+ if (((field_num == 0) && (dev->field == 0)) ||
+ (field_num && (dev->field == 1)))
+ dev->field++;
+ }
+
+ if (status & INT_FIELD_STATUS) {
+ dprintk(1, "irq: field %d, done %d\n",
+ !!field_num, dma_done);
+ if (unlikely(dev->first)) {
+ if (field_num == 0) {
+ dev->first = 0;
+ dprintk(1, "activate first buf\n");
+ viu_activate_next_buf(dev, vidq);
+ } else
+ dprintk(1, "wait field 0\n");
+ return;
+ }
+
+ /* setup buffer address for next dma operation */
+ if (!list_empty(&vidq->active)) {
+ u32 addr = reg_val.field_base_addr;
+
+ if (field_num && need_two) {
+ addr += reg_val.dma_inc;
+ dprintk(1, "field 1, 0x%lx, dev field %d\n",
+ (unsigned long)addr, dev->field);
+ }
+ out_be32(&vr->field_base_addr, addr);
+ out_be32(&vr->dma_inc, reg_val.dma_inc);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) |
+ (status & INT_ALL_STATUS) |
+ reg_val.status_cfg);
+ return;
+ }
+ }
+
+ if (dma_done && field_num && (dev->field == 2)) {
+ dev->field = 0;
+ buf = list_entry(vidq->active.next,
+ struct viu_buf, vb.queue);
+ dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n",
+ buf, buf->vb.i,
+ (unsigned long)videobuf_to_dma_contig(&buf->vb),
+ (unsigned long)in_be32(&vr->field_base_addr));
+
+ if (waitqueue_active(&buf->vb.done)) {
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ wake_up(&buf->vb.done);
+ }
+ /* activate next dma buffer */
+ viu_activate_next_buf(dev, vidq);
+ }
+}
+
+static irqreturn_t viu_intr(int irq, void *dev_id)
+{
+ struct viu_dev *dev = (struct viu_dev *)dev_id;
+ struct viu_reg *vr = dev->vr;
+ u32 status;
+ u32 error;
+
+ status = in_be32(&vr->status_cfg);
+
+ if (status & INT_ERROR_STATUS) {
+ dev->irqs.error_irq++;
+ error = status & ERR_MASK;
+ if (error)
+ dprintk(1, "Err: error(%d), times:%d!\n",
+ error >> 4, dev->irqs.error_irq);
+ /* Clear interrupt error bit and error flags */
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) | INT_ERROR_STATUS);
+ }
+
+ if (status & INT_DMA_END_STATUS) {
+ dev->irqs.dma_end_irq++;
+ dev->dma_done = 1;
+ dprintk(2, "VIU DMA end interrupt times: %d\n",
+ dev->irqs.dma_end_irq);
+ }
+
+ if (status & INT_HSYNC_STATUS)
+ dev->irqs.hsync_irq++;
+
+ if (status & INT_FIELD_STATUS) {
+ dev->irqs.field_irq++;
+ dprintk(2, "VIU field interrupt times: %d\n",
+ dev->irqs.field_irq);
+ }
+
+ if (status & INT_VSTART_STATUS)
+ dev->irqs.vstart_irq++;
+
+ if (status & INT_VSYNC_STATUS) {
+ dev->irqs.vsync_irq++;
+ dprintk(2, "VIU vsync interrupt times: %d\n",
+ dev->irqs.vsync_irq);
+ }
+
+ /* clear all pending irqs */
+ status = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg,
+ (status & 0xffc0ffff) | (status & INT_ALL_STATUS));
+
+ if (dev->ovenable) {
+ viu_overlay_intr(dev, status);
+ return IRQ_HANDLED;
+ }
+
+ /* Capture mode */
+ viu_capture_intr(dev, status);
+ return IRQ_HANDLED;
+}
+
+/*
+ * File operations for the device
+ */
+static int viu_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct viu_dev *dev = video_get_drvdata(vdev);
+ struct viu_fh *fh;
+ struct viu_reg *vr;
+ int minor = vdev->minor;
+ u32 status_cfg;
+ int i;
+
+ dprintk(1, "viu: open (minor=%d)\n", minor);
+
+ dev->users++;
+ if (dev->users > 1) {
+ dev->users--;
+ return -EBUSY;
+ }
+
+ vr = dev->vr;
+
+ dprintk(1, "open minor=%d type=%s users=%d\n", minor,
+ v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
+
+ if (mutex_lock_interruptible(&dev->lock)) {
+ dev->users--;
+ return -ERESTARTSYS;
+ }
+
+ /* allocate and initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (!fh) {
+ dev->users--;
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32);
+ fh->width = norm_maxw();
+ fh->height = norm_maxh();
+ dev->crop_current.width = fh->width;
+ dev->crop_current.height = fh->height;
+
+ /* Put all controls at a sane state */
+ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++)
+ qctl_regs[i] = viu_qctrl[i].default_value;
+
+ dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
+ dprintk(1, "Open: list_empty queued=%d\n",
+ list_empty(&dev->vidq.queued));
+ dprintk(1, "Open: list_empty active=%d\n",
+ list_empty(&dev->vidq.active));
+
+ viu_default_settings(vr);
+
+ status_cfg = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg,
+ status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN |
+ INT_FIELD_EN | INT_VSTART_EN |
+ INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN));
+
+ status_cfg = in_be32(&vr->status_cfg);
+ out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS);
+
+ spin_lock_init(&fh->vbq_lock);
+ videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops,
+ dev->dev, &fh->vbq_lock,
+ fh->type, V4L2_FIELD_INTERLACED,
+ sizeof(struct viu_buf), fh,
+ &fh->dev->lock);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static ssize_t viu_read(struct file *file, char __user *data, size_t count,
+ loff_t *ppos)
+{
+ struct viu_fh *fh = file->private_data;
+ struct viu_dev *dev = fh->dev;
+ int ret = 0;
+
+ dprintk(2, "%s\n", __func__);
+ if (dev->ovenable)
+ dev->ovenable = 0;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ viu_start_dma(dev);
+ ret = videobuf_read_stream(&fh->vb_vidq, data, count,
+ ppos, 0, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&dev->lock);
+ return ret;
+ }
+ return 0;
+}
+
+static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct viu_fh *fh = file->private_data;
+ struct videobuf_queue *q = &fh->vb_vidq;
+ struct viu_dev *dev = fh->dev;
+ unsigned int res;
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+
+ mutex_lock(&dev->lock);
+ res = videobuf_poll_stream(file, q, wait);
+ mutex_unlock(&dev->lock);
+ return res;
+}
+
+static int viu_release(struct file *file)
+{
+ struct viu_fh *fh = file->private_data;
+ struct viu_dev *dev = fh->dev;
+ int minor = video_devdata(file)->minor;
+
+ mutex_lock(&dev->lock);
+ viu_stop_dma(dev);
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+ mutex_unlock(&dev->lock);
+
+ kfree(fh);
+
+ dev->users--;
+ dprintk(1, "close (minor=%d, users=%d)\n",
+ minor, dev->users);
+ return 0;
+}
+
+void viu_reset(struct viu_reg *reg)
+{
+ out_be32(&reg->status_cfg, 0);
+ out_be32(&reg->luminance, 0x9512a254);
+ out_be32(&reg->chroma_r, 0x03310000);
+ out_be32(&reg->chroma_g, 0x06600f38);
+ out_be32(&reg->chroma_b, 0x00000409);
+ out_be32(&reg->field_base_addr, 0);
+ out_be32(&reg->dma_inc, 0);
+ out_be32(&reg->picture_count, 0x01e002d0);
+ out_be32(&reg->req_alarm, 0x00000090);
+ out_be32(&reg->alpha, 0x000000ff);
+}
+
+static int viu_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct viu_fh *fh = file->private_data;
+ struct viu_dev *dev = fh->dev;
+ int ret;
+
+ dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ mutex_unlock(&dev->lock);
+
+ dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
+ ret);
+
+ return ret;
+}
+
+static struct v4l2_file_operations viu_fops = {
+ .owner = THIS_MODULE,
+ .open = viu_open,
+ .release = viu_release,
+ .read = viu_read,
+ .poll = viu_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = viu_mmap,
+};
+
+static const struct v4l2_ioctl_ops viu_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay,
+ .vidioc_overlay = vidioc_overlay,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+static struct video_device viu_template = {
+ .name = "FSL viu",
+ .fops = &viu_fops,
+ .minor = -1,
+ .ioctl_ops = &viu_ioctl_ops,
+ .release = video_device_release,
+
+ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int __devinit viu_of_probe(struct platform_device *op)
+{
+ struct viu_dev *viu_dev;
+ struct video_device *vdev;
+ struct resource r;
+ struct viu_reg __iomem *viu_regs;
+ struct i2c_adapter *ad;
+ int ret, viu_irq;
+
+ ret = of_address_to_resource(op->dev.of_node, 0, &r);
+ if (ret) {
+ dev_err(&op->dev, "Can't parse device node resource\n");
+ return -ENODEV;
+ }
+
+ viu_irq = irq_of_parse_and_map(op->dev.of_node, 0);
+ if (viu_irq == NO_IRQ) {
+ dev_err(&op->dev, "Error while mapping the irq\n");
+ return -EINVAL;
+ }
+
+ /* request mem region */
+ if (!devm_request_mem_region(&op->dev, r.start,
+ sizeof(struct viu_reg), DRV_NAME)) {
+ dev_err(&op->dev, "Error while requesting mem region\n");
+ ret = -EBUSY;
+ goto err;
+ }
+
+ /* remap registers */
+ viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg));
+ if (!viu_regs) {
+ dev_err(&op->dev, "Can't map register set\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Prepare our private structure */
+ viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC);
+ if (!viu_dev) {
+ dev_err(&op->dev, "Can't allocate private structure\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ viu_dev->vr = viu_regs;
+ viu_dev->irq = viu_irq;
+ viu_dev->dev = &op->dev;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&viu_dev->vidq.active);
+ INIT_LIST_HEAD(&viu_dev->vidq.queued);
+
+ snprintf(viu_dev->v4l2_dev.name,
+ sizeof(viu_dev->v4l2_dev.name), "%s", "VIU");
+ ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret);
+ goto err;
+ }
+
+ ad = i2c_get_adapter(0);
+ viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad,
+ "saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
+
+ viu_dev->vidq.timeout.function = viu_vid_timeout;
+ viu_dev->vidq.timeout.data = (unsigned long)viu_dev;
+ init_timer(&viu_dev->vidq.timeout);
+ viu_dev->first = 1;
+
+ /* Allocate memory for video device */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ ret = -ENOMEM;
+ goto err_vdev;
+ }
+
+ memcpy(vdev, &viu_template, sizeof(viu_template));
+
+ vdev->v4l2_dev = &viu_dev->v4l2_dev;
+
+ viu_dev->vdev = vdev;
+
+ /* initialize locks */
+ mutex_init(&viu_dev->lock);
+ viu_dev->vdev->lock = &viu_dev->lock;
+ spin_lock_init(&viu_dev->slock);
+
+ video_set_drvdata(viu_dev->vdev, viu_dev);
+
+ mutex_lock(&viu_dev->lock);
+
+ ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ video_device_release(viu_dev->vdev);
+ goto err_vdev;
+ }
+
+ /* enable VIU clock */
+ viu_dev->clk = clk_get(&op->dev, "viu_clk");
+ if (IS_ERR(viu_dev->clk)) {
+ dev_err(&op->dev, "failed to find the clock module!\n");
+ ret = -ENODEV;
+ goto err_clk;
+ } else {
+ clk_enable(viu_dev->clk);
+ }
+
+ /* reset VIU module */
+ viu_reset(viu_dev->vr);
+
+ /* install interrupt handler */
+ if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) {
+ dev_err(&op->dev, "Request VIU IRQ failed.\n");
+ ret = -ENODEV;
+ goto err_irq;
+ }
+
+ mutex_unlock(&viu_dev->lock);
+
+ dev_info(&op->dev, "Freescale VIU Video Capture Board\n");
+ return ret;
+
+err_irq:
+ clk_disable(viu_dev->clk);
+ clk_put(viu_dev->clk);
+err_clk:
+ video_unregister_device(viu_dev->vdev);
+err_vdev:
+ mutex_unlock(&viu_dev->lock);
+ i2c_put_adapter(ad);
+ v4l2_device_unregister(&viu_dev->v4l2_dev);
+err:
+ irq_dispose_mapping(viu_irq);
+ return ret;
+}
+
+static int __devexit viu_of_remove(struct platform_device *op)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+ struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next,
+ struct v4l2_subdev, list);
+ struct i2c_client *client = v4l2_get_subdevdata(sdev);
+
+ free_irq(dev->irq, (void *)dev);
+ irq_dispose_mapping(dev->irq);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+
+ video_unregister_device(dev->vdev);
+ i2c_put_adapter(client->adapter);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int viu_suspend(struct platform_device *op, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+
+ clk_disable(dev->clk);
+ return 0;
+}
+
+static int viu_resume(struct platform_device *op)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
+ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
+
+ clk_enable(dev->clk);
+ return 0;
+}
+#endif
+
+/*
+ * Initialization and module stuff
+ */
+static struct of_device_id mpc512x_viu_of_match[] = {
+ {
+ .compatible = "fsl,mpc5121-viu",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match);
+
+static struct platform_driver viu_of_platform_driver = {
+ .probe = viu_of_probe,
+ .remove = __devexit_p(viu_of_remove),
+#ifdef CONFIG_PM
+ .suspend = viu_suspend,
+ .resume = viu_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mpc512x_viu_of_match,
+ },
+};
+
+module_platform_driver(viu_of_platform_driver);
+
+MODULE_DESCRIPTION("Freescale Video-In(VIU)");
+MODULE_AUTHOR("Hongjun Chen");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(VIU_VERSION);
diff --git a/drivers/media/video/indycam.c b/drivers/media/platform/indycam.c
index 548236333cce..548236333cce 100644
--- a/drivers/media/video/indycam.c
+++ b/drivers/media/platform/indycam.c
diff --git a/drivers/media/video/indycam.h b/drivers/media/platform/indycam.h
index 881f21c474c4..881f21c474c4 100644
--- a/drivers/media/video/indycam.h
+++ b/drivers/media/platform/indycam.h
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
new file mode 100644
index 000000000000..45164c4f8452
--- /dev/null
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -0,0 +1,1124 @@
+/*
+ * V4L2 deinterlacing support.
+ *
+ * Copyright (c) 2012 Vista Silicon S.L.
+ * Javier Martin <javier.martin@vista-silicon.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
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define MEM2MEM_TEST_MODULE_NAME "mem2mem-deinterlace"
+
+MODULE_DESCRIPTION("mem2mem device which supports deinterlacing using dmaengine");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+
+static bool debug = true;
+module_param(debug, bool, 0644);
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-deinterlace"
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+struct deinterlace_fmt {
+ char *name;
+ u32 fourcc;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct deinterlace_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ },
+ {
+ .name = "YUYV 4:2:2",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Per-queue, driver-specific private data */
+struct deinterlace_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct deinterlace_fmt *fmt;
+ enum v4l2_field field;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+enum {
+ YUV420_DMA_Y_ODD,
+ YUV420_DMA_Y_EVEN,
+ YUV420_DMA_U_ODD,
+ YUV420_DMA_U_EVEN,
+ YUV420_DMA_V_ODD,
+ YUV420_DMA_V_EVEN,
+ YUV420_DMA_Y_ODD_DOUBLING,
+ YUV420_DMA_U_ODD_DOUBLING,
+ YUV420_DMA_V_ODD_DOUBLING,
+ YUYV_DMA_ODD,
+ YUYV_DMA_EVEN,
+ YUYV_DMA_EVEN_DOUBLING,
+};
+
+/* Source and destination queue data */
+static struct deinterlace_q_data q_data[2];
+
+static struct deinterlace_q_data *get_q_data(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &q_data[V4L2_M2M_SRC];
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &q_data[V4L2_M2M_DST];
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+static struct deinterlace_fmt *find_format(struct v4l2_format *f)
+{
+ struct deinterlace_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if ((fmt->types & f->type) &&
+ (fmt->fourcc == f->fmt.pix.pixelformat))
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct deinterlace_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ atomic_t busy;
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ struct dma_chan *dma_chan;
+
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+struct deinterlace_ctx {
+ struct deinterlace_dev *dev;
+
+ /* Abort requested by m2m */
+ int aborting;
+ enum v4l2_colorspace colorspace;
+ dma_cookie_t cookie;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct dma_interleaved_template *xt;
+};
+
+/*
+ * mem2mem callbacks
+ */
+static int deinterlace_job_ready(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_dev *pcdev = ctx->dev;
+
+ if ((v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0)
+ && (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0)
+ && (atomic_read(&ctx->dev->busy) == 0)) {
+ dprintk(pcdev, "Task ready\n");
+ return 1;
+ }
+
+ dprintk(pcdev, "Task not ready to run\n");
+
+ return 0;
+}
+
+static void deinterlace_job_abort(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_dev *pcdev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ dprintk(pcdev, "Aborting task\n");
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void deinterlace_lock(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void deinterlace_unlock(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static void dma_callback(void *data)
+{
+ struct deinterlace_ctx *curr_ctx = data;
+ struct deinterlace_dev *pcdev = curr_ctx->dev;
+ struct vb2_buffer *src_vb, *dst_vb;
+
+ atomic_set(&pcdev->busy, 0);
+
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
+
+ dprintk(pcdev, "dma transfers completed.\n");
+}
+
+static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
+ int do_callback)
+{
+ struct deinterlace_q_data *s_q_data, *d_q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct deinterlace_dev *pcdev = ctx->dev;
+ struct dma_chan *chan = pcdev->dma_chan;
+ struct dma_device *dmadev = chan->device;
+ struct dma_async_tx_descriptor *tx;
+ unsigned int s_width, s_height;
+ unsigned int d_width, d_height;
+ unsigned int d_size, s_size;
+ dma_addr_t p_in, p_out;
+ enum dma_ctrl_flags flags;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ s_width = s_q_data->width;
+ s_height = s_q_data->height;
+ s_size = s_width * s_height;
+
+ d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ d_width = d_q_data->width;
+ d_height = d_q_data->height;
+ d_size = d_width * d_height;
+
+ p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&pcdev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return;
+ }
+
+ switch (op) {
+ case YUV420_DMA_Y_ODD:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width;
+ ctx->xt->sgl[0].icg = s_width;
+ ctx->xt->src_start = p_in;
+ ctx->xt->dst_start = p_out;
+ break;
+ case YUV420_DMA_Y_EVEN:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width;
+ ctx->xt->sgl[0].icg = s_width;
+ ctx->xt->src_start = p_in + s_size / 2;
+ ctx->xt->dst_start = p_out + s_width;
+ break;
+ case YUV420_DMA_U_ODD:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + s_size;
+ ctx->xt->dst_start = p_out + s_size;
+ break;
+ case YUV420_DMA_U_EVEN:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + (9 * s_size) / 8;
+ ctx->xt->dst_start = p_out + s_size + s_width / 2;
+ break;
+ case YUV420_DMA_V_ODD:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + (5 * s_size) / 4;
+ ctx->xt->dst_start = p_out + (5 * s_size) / 4;
+ break;
+ case YUV420_DMA_V_EVEN:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + (11 * s_size) / 8;
+ ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2;
+ break;
+ case YUV420_DMA_Y_ODD_DOUBLING:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width;
+ ctx->xt->sgl[0].icg = s_width;
+ ctx->xt->src_start = p_in;
+ ctx->xt->dst_start = p_out + s_width;
+ break;
+ case YUV420_DMA_U_ODD_DOUBLING:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + s_size;
+ ctx->xt->dst_start = p_out + s_size + s_width / 2;
+ break;
+ case YUV420_DMA_V_ODD_DOUBLING:
+ ctx->xt->numf = s_height / 4;
+ ctx->xt->sgl[0].size = s_width / 2;
+ ctx->xt->sgl[0].icg = s_width / 2;
+ ctx->xt->src_start = p_in + (5 * s_size) / 4;
+ ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2;
+ break;
+ case YUYV_DMA_ODD:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width * 2;
+ ctx->xt->sgl[0].icg = s_width * 2;
+ ctx->xt->src_start = p_in;
+ ctx->xt->dst_start = p_out;
+ break;
+ case YUYV_DMA_EVEN:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width * 2;
+ ctx->xt->sgl[0].icg = s_width * 2;
+ ctx->xt->src_start = p_in + s_size;
+ ctx->xt->dst_start = p_out + s_width * 2;
+ break;
+ case YUYV_DMA_EVEN_DOUBLING:
+ default:
+ ctx->xt->numf = s_height / 2;
+ ctx->xt->sgl[0].size = s_width * 2;
+ ctx->xt->sgl[0].icg = s_width * 2;
+ ctx->xt->src_start = p_in;
+ ctx->xt->dst_start = p_out + s_width * 2;
+ break;
+ }
+
+ /* Common parameters for al transfers */
+ ctx->xt->frame_size = 1;
+ ctx->xt->dir = DMA_MEM_TO_MEM;
+ ctx->xt->src_sgl = false;
+ ctx->xt->dst_sgl = true;
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP;
+
+ tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
+ if (tx == NULL) {
+ v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep error\n");
+ return;
+ }
+
+ if (do_callback) {
+ tx->callback = dma_callback;
+ tx->callback_param = ctx;
+ }
+
+ ctx->cookie = dmaengine_submit(tx);
+ if (dma_submit_error(ctx->cookie)) {
+ v4l2_warn(&pcdev->v4l2_dev,
+ "DMA submit error %d with src=0x%x dst=0x%x len=0x%x\n",
+ ctx->cookie, (unsigned)p_in, (unsigned)p_out,
+ s_size * 3/2);
+ return;
+ }
+
+ dma_async_issue_pending(chan);
+}
+
+static void deinterlace_device_run(void *priv)
+{
+ struct deinterlace_ctx *ctx = priv;
+ struct deinterlace_q_data *dst_q_data;
+
+ atomic_set(&ctx->dev->busy, 1);
+
+ dprintk(ctx->dev, "%s: DMA try issue.\n", __func__);
+
+ dst_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ /*
+ * 4 possible field conversions are possible at the moment:
+ * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_INTERLACED_TB:
+ * two separate fields in the same input buffer are interlaced
+ * in the output buffer using weaving. Top field comes first.
+ * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_NONE:
+ * top field from the input buffer is copied to the output buffer
+ * using line doubling. Bottom field from the input buffer is discarded.
+ * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_INTERLACED_BT:
+ * two separate fields in the same input buffer are interlaced
+ * in the output buffer using weaving. Bottom field comes first.
+ * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_NONE:
+ * bottom field from the input buffer is copied to the output buffer
+ * using line doubling. Top field from the input buffer is discarded.
+ */
+ switch (dst_q_data->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ switch (dst_q_data->field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ dprintk(ctx->dev, "%s: yuv420 interlaced tb.\n",
+ __func__);
+ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_Y_EVEN, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_U_EVEN, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_V_EVEN, 1);
+ break;
+ case V4L2_FIELD_NONE:
+ default:
+ dprintk(ctx->dev, "%s: yuv420 interlaced line doubling.\n",
+ __func__);
+ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD_DOUBLING, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD_DOUBLING, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0);
+ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD_DOUBLING, 1);
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ switch (dst_q_data->field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ dprintk(ctx->dev, "%s: yuyv interlaced_tb.\n",
+ __func__);
+ deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0);
+ deinterlace_issue_dma(ctx, YUYV_DMA_EVEN, 1);
+ break;
+ case V4L2_FIELD_NONE:
+ default:
+ dprintk(ctx->dev, "%s: yuyv interlaced line doubling.\n",
+ __func__);
+ deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0);
+ deinterlace_issue_dma(ctx, YUYV_DMA_EVEN_DOUBLING, 1);
+ break;
+ }
+ break;
+ }
+
+ dprintk(ctx->dev, "%s: DMA issue done.\n", __func__);
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, MEM2MEM_NAME, sizeof(cap->card));
+ strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->card));
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct deinterlace_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct deinterlace_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = q_data->field;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+
+ switch (q_data->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ f->fmt.pix.bytesperline = q_data->width * 2;
+ }
+
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct deinterlace_fmt *fmt)
+{
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_fmt *fmt;
+ struct deinterlace_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE))
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB &&
+ f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT &&
+ f->fmt.pix.field != V4L2_FIELD_NONE)
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED_TB;
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_fmt *fmt;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT))
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
+
+ if (!f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+ if (f->fmt.pix.field != V4L2_FIELD_SEQ_TB &&
+ f->fmt.pix.field != V4L2_FIELD_SEQ_BT)
+ f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_s_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
+{
+ struct deinterlace_q_data *q_data;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fmt = find_format(f);
+ if (!q_data->fmt) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Couldn't set format type %d, wxh: %dx%d. fmt: %d, field: %d\n",
+ f->type, f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.pixelformat, f->fmt.pix.field);
+ return -EINVAL;
+ }
+
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ q_data->field = f->fmt.pix.field;
+
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ q_data->sizeimage = (q_data->width * q_data->height * 3) / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ q_data->sizeimage = q_data->width * q_data->height * 2;
+ }
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d, field: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc,
+ q_data->field);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct deinterlace_ctx *ctx = priv;
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ ret = vidioc_s_fmt(priv, f);
+ if (!ret)
+ ctx->colorspace = f->fmt.pix.colorspace;
+
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct deinterlace_q_data *s_q_data, *d_q_data;
+ struct deinterlace_ctx *ctx = priv;
+
+ s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ /* Check that src and dst queues have the same pix format */
+ if (s_q_data->fmt->fourcc != d_q_data->fmt->fourcc) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "src and dst formats don't match.\n");
+ return -EINVAL;
+ }
+
+ /* Check that input and output deinterlacing types are compatible */
+ switch (s_q_data->field) {
+ case V4L2_FIELD_SEQ_BT:
+ if (d_q_data->field != V4L2_FIELD_NONE &&
+ d_q_data->field != V4L2_FIELD_INTERLACED_BT) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "src and dst field conversion [(%d)->(%d)] not supported.\n",
+ s_q_data->field, d_q_data->field);
+ return -EINVAL;
+ }
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ if (d_q_data->field != V4L2_FIELD_NONE &&
+ d_q_data->field != V4L2_FIELD_INTERLACED_TB) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "src and dst field conversion [(%d)->(%d)] not supported.\n",
+ s_q_data->field, d_q_data->field);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct deinterlace_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+
+/*
+ * Queue operations
+ */
+struct vb2_dc_conf {
+ struct device *dev;
+};
+
+static int deinterlace_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq);
+ struct deinterlace_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(vq->type);
+
+ switch (q_data->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUV420:
+ size = q_data->width * q_data->height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ default:
+ size = q_data->width * q_data->height * 2;
+ }
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int deinterlace_buf_prepare(struct vb2_buffer *vb)
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct deinterlace_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(vb->vb2_queue->type);
+
+ 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;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void deinterlace_buf_queue(struct vb2_buffer *vb)
+{
+ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops deinterlace_qops = {
+ .queue_setup = deinterlace_queue_setup,
+ .buf_prepare = deinterlace_buf_prepare,
+ .buf_queue = deinterlace_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct deinterlace_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &deinterlace_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ q_data[V4L2_M2M_SRC].fmt = &formats[0];
+ q_data[V4L2_M2M_SRC].width = 640;
+ q_data[V4L2_M2M_SRC].height = 480;
+ q_data[V4L2_M2M_SRC].sizeimage = (640 * 480 * 3) / 2;
+ q_data[V4L2_M2M_SRC].field = V4L2_FIELD_SEQ_TB;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &deinterlace_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ q_data[V4L2_M2M_DST].fmt = &formats[0];
+ q_data[V4L2_M2M_DST].width = 640;
+ q_data[V4L2_M2M_DST].height = 480;
+ q_data[V4L2_M2M_DST].sizeimage = (640 * 480 * 3) / 2;
+ q_data[V4L2_M2M_SRC].field = V4L2_FIELD_INTERLACED_TB;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int deinterlace_open(struct file *file)
+{
+ struct deinterlace_dev *pcdev = video_drvdata(file);
+ struct deinterlace_ctx *ctx = NULL;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = pcdev;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) +
+ sizeof(struct data_chunk), GFP_KERNEL);
+ if (!ctx->xt) {
+ int ret = PTR_ERR(ctx->xt);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int deinterlace_release(struct file *file)
+{
+ struct deinterlace_dev *pcdev = video_drvdata(file);
+ struct deinterlace_ctx *ctx = file->private_data;
+
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx->xt);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int deinterlace_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct deinterlace_ctx *ctx = file->private_data;
+ int ret;
+
+ deinterlace_lock(ctx);
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ deinterlace_unlock(ctx);
+
+ return ret;
+}
+
+static int deinterlace_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct deinterlace_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations deinterlace_fops = {
+ .owner = THIS_MODULE,
+ .open = deinterlace_open,
+ .release = deinterlace_release,
+ .poll = deinterlace_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = deinterlace_mmap,
+};
+
+static struct video_device deinterlace_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &deinterlace_fops,
+ .ioctl_ops = &deinterlace_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = deinterlace_device_run,
+ .job_ready = deinterlace_job_ready,
+ .job_abort = deinterlace_job_abort,
+ .lock = deinterlace_lock,
+ .unlock = deinterlace_unlock,
+};
+
+static int deinterlace_probe(struct platform_device *pdev)
+{
+ struct deinterlace_dev *pcdev;
+ struct video_device *vfd;
+ dma_cap_mask_t mask;
+ int ret = 0;
+
+ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
+ if (!pcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&pcdev->irqlock);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_INTERLEAVE, mask);
+ pcdev->dma_chan = dma_request_channel(mask, NULL, pcdev);
+ if (!pcdev->dma_chan)
+ goto free_dev;
+
+ if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) {
+ v4l2_err(&pcdev->v4l2_dev, "DMA does not support INTERLEAVE\n");
+ goto rel_dma;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
+ if (ret)
+ goto rel_dma;
+
+ atomic_set(&pcdev->busy, 0);
+ mutex_init(&pcdev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = deinterlace_videodev;
+ vfd->lock = &pcdev->dev_mutex;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+
+ video_set_drvdata(vfd, pcdev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name);
+ pcdev->vfd = vfd;
+ v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
+ " Device registered as /dev/video%d\n", vfd->num);
+
+ platform_set_drvdata(pdev, pcdev);
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(pcdev->alloc_ctx);
+ goto err_ctx;
+ }
+
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(pcdev->m2m_dev)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(pcdev->m2m_dev);
+ goto err_m2m;
+ }
+
+ return 0;
+
+ v4l2_m2m_release(pcdev->m2m_dev);
+err_m2m:
+ video_unregister_device(pcdev->vfd);
+err_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+rel_dma:
+ dma_release_channel(pcdev->dma_chan);
+free_dev:
+ kfree(pcdev);
+
+ return ret;
+}
+
+static int deinterlace_remove(struct platform_device *pdev)
+{
+ struct deinterlace_dev *pcdev =
+ (struct deinterlace_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
+ v4l2_m2m_release(pcdev->m2m_dev);
+ video_unregister_device(pcdev->vfd);
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ dma_release_channel(pcdev->dma_chan);
+ kfree(pcdev);
+
+ return 0;
+}
+
+static struct platform_driver deinterlace_pdrv = {
+ .probe = deinterlace_probe,
+ .remove = deinterlace_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit deinterlace_exit(void)
+{
+ platform_driver_unregister(&deinterlace_pdrv);
+}
+
+static int __init deinterlace_init(void)
+{
+ return platform_driver_register(&deinterlace_pdrv);
+}
+
+module_init(deinterlace_init);
+module_exit(deinterlace_exit);
+
diff --git a/drivers/media/video/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig
index bf739e3b3398..bf739e3b3398 100644
--- a/drivers/media/video/marvell-ccic/Kconfig
+++ b/drivers/media/platform/marvell-ccic/Kconfig
diff --git a/drivers/media/video/marvell-ccic/Makefile b/drivers/media/platform/marvell-ccic/Makefile
index 05a792c579a2..05a792c579a2 100644
--- a/drivers/media/video/marvell-ccic/Makefile
+++ b/drivers/media/platform/marvell-ccic/Makefile
diff --git a/drivers/media/video/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index d030f9beae88..d030f9beae88 100644
--- a/drivers/media/video/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index ce2b7b4788d6..ce2b7b4788d6 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index bd6acba9fb37..bd6acba9fb37 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index c4c17fe76c0d..c4c17fe76c0d 100644
--- a/drivers/media/video/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c
new file mode 100644
index 000000000000..d03637537118
--- /dev/null
+++ b/drivers/media/platform/mem2mem_testdev.c
@@ -0,0 +1,1130 @@
+/*
+ * A virtual v4l2-mem2mem example device.
+ *
+ * This is a virtual device driver for testing mem-to-mem videobuf framework.
+ * It simulates a device that uses memory buffers for both source and
+ * destination, processes the data and issues an "irq" (simulated by a timer).
+ * The device is capable of multi-instance, multi-buffer-per-transaction
+ * operation (via the mem2mem framework).
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.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; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-vmalloc.h>
+
+#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
+
+MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
+MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1.1");
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 640
+#define MAX_H 480
+#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-testdev"
+
+/* Per queue */
+#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
+
+/* Default transaction time in msec */
+#define MEM2MEM_DEF_TRANSTIME 1000
+/* Default number of buffers per transaction */
+#define MEM2MEM_DEF_TRANSLEN 1
+#define MEM2MEM_COLOR_STEP (0xff >> 4)
+#define MEM2MEM_NUM_TILES 8
+
+/* Flags that indicate processing mode */
+#define MEM2MEM_HFLIP (1 << 0)
+#define MEM2MEM_VFLIP (1 << 1)
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+
+static void m2mtest_dev_release(struct device *dev)
+{}
+
+static struct platform_device m2mtest_pdev = {
+ .name = MEM2MEM_NAME,
+ .dev.release = m2mtest_dev_release,
+};
+
+struct m2mtest_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct m2mtest_fmt formats[] = {
+ {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .depth = 16,
+ /* Both capture and output format */
+ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ /* Output-only format */
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Per-queue, driver-specific private data */
+struct m2mtest_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct m2mtest_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
+#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
+
+static struct m2mtest_fmt *find_format(struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct m2mtest_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ atomic_t num_inst;
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ struct timer_list timer;
+
+ struct v4l2_m2m_dev *m2m_dev;
+};
+
+struct m2mtest_ctx {
+ struct v4l2_fh fh;
+ struct m2mtest_dev *dev;
+
+ struct v4l2_ctrl_handler hdl;
+
+ /* Processed buffers in this transaction */
+ u8 num_processed;
+
+ /* Transaction length (i.e. how many buffers per transaction) */
+ u32 translen;
+ /* Transaction time (i.e. simulated processing time) in milliseconds */
+ u32 transtime;
+
+ /* Abort requested by m2m */
+ int aborting;
+
+ /* Processing mode */
+ int mode;
+
+ enum v4l2_colorspace colorspace;
+
+ struct v4l2_m2m_ctx *m2m_ctx;
+
+ /* Source and destination queue data */
+ struct m2mtest_q_data q_data[2];
+};
+
+static inline struct m2mtest_ctx *file2ctx(struct file *file)
+{
+ return container_of(file->private_data, struct m2mtest_ctx, fh);
+}
+
+static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->q_data[V4L2_M2M_SRC];
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->q_data[V4L2_M2M_DST];
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+
+static int device_process(struct m2mtest_ctx *ctx,
+ 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;
+ int width, height, bytesperline;
+
+ q_data = get_q_data(ctx, 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 (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 = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
+ / MEM2MEM_NUM_TILES;
+ bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
+ w = 0;
+
+ switch (ctx->mode) {
+ case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
+ p_out += bytesperline * height - bytes_left;
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out -= bytes_left;
+ }
+ break;
+
+ case MEM2MEM_HFLIP:
+ for (y = 0; y < height; ++y) {
+ p_out += MEM2MEM_NUM_TILES * tile_w;
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x01) {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *--p_out = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytesperline;
+ }
+ break;
+
+ case MEM2MEM_VFLIP:
+ p_out += bytesperline * (height - 1);
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytes_left - 2 * bytesperline;
+ }
+ break;
+
+ default:
+ for (y = 0; y < height; ++y) {
+ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
+ if (w & 0x1) {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ +
+ MEM2MEM_COLOR_STEP;
+ } else {
+ for (x = 0; x < tile_w; ++x)
+ *p_out++ = *p_in++ -
+ MEM2MEM_COLOR_STEP;
+ }
+ ++w;
+ }
+ p_in += bytes_left;
+ p_out += bytes_left;
+ }
+ }
+
+ return 0;
+}
+
+static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)
+{
+ dprintk(dev, "Scheduling a simulated irq\n");
+ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
+}
+
+/*
+ * mem2mem callbacks
+ */
+
+/**
+ * job_ready() - check whether an instance is ready to be scheduled to run
+ */
+static int job_ready(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen
+ || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {
+ dprintk(ctx->dev, "Not enough buffers available\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static void job_abort(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+
+ /* Will cancel the transaction in the next interrupt handler */
+ 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
+ * a device. This will be called by the framework when it decides to schedule
+ * a particular instance.
+ */
+static void device_run(void *priv)
+{
+ struct m2mtest_ctx *ctx = priv;
+ struct m2mtest_dev *dev = ctx->dev;
+ 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);
+
+ device_process(ctx, src_buf, dst_buf);
+
+ /* Run a timer, which simulates a hardware irq */
+ 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 vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+
+ curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
+
+ if (NULL == curr_ctx) {
+ printk(KERN_ERR
+ "Instance released before the end of transaction\n");
+ return;
+ }
+
+ 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;
+ v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
+ } else {
+ device_run(curr_ctx);
+ }
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", MEM2MEM_NAME);
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct m2mtest_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct m2mtest_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ 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;
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(file2ctx(file), f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
+{
+ enum v4l2_field field;
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.height < MIN_H)
+ f->fmt.pix.height = MIN_H;
+ else if (f->fmt.pix.height > MAX_H)
+ f->fmt.pix.height = MAX_H;
+
+ if (f->fmt.pix.width < MIN_W)
+ f->fmt.pix.width = MIN_W;
+ else if (f->fmt.pix.width > MAX_W)
+ f->fmt.pix.width = MAX_W;
+
+ f->fmt.pix.width &= ~DIM_ALIGN_MASK;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+ f->fmt.pix.colorspace = ctx->colorspace;
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct m2mtest_fmt *fmt;
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+ if (!f->fmt.pix.colorspace)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+
+ return vidioc_try_fmt(f, fmt);
+}
+
+static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
+{
+ struct m2mtest_q_data *q_data;
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ q_data->sizeimage = q_data->width * q_data->height
+ * q_data->fmt->depth >> 3;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(file2ctx(file), f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ ret = vidioc_s_fmt(file2ctx(file), f);
+ if (!ret)
+ ctx->colorspace = f->fmt.pix.colorspace;
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct m2mtest_ctx *ctx =
+ container_of(ctrl->handler, struct m2mtest_ctx, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ if (ctrl->val)
+ ctx->mode |= MEM2MEM_HFLIP;
+ else
+ ctx->mode &= ~MEM2MEM_HFLIP;
+ break;
+
+ case V4L2_CID_VFLIP:
+ if (ctrl->val)
+ ctx->mode |= MEM2MEM_VFLIP;
+ else
+ ctx->mode &= ~MEM2MEM_VFLIP;
+ break;
+
+ case V4L2_CID_TRANS_TIME_MSEC:
+ ctx->transtime = ctrl->val;
+ break;
+
+ case V4L2_CID_TRANS_NUM_BUFS:
+ ctx->translen = ctrl->val;
+ break;
+
+ default:
+ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
+ .s_ctrl = m2mtest_s_ctrl,
+};
+
+
+static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+
+/*
+ * Queue operations
+ */
+
+static int m2mtest_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ 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(ctx, vq->type);
+
+ size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = 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 vb2_buffer *vb)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct m2mtest_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ 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;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void m2mtest_buf_queue(struct vb2_buffer *vb)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void m2mtest_wait_prepare(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_unlock(ctx);
+}
+
+static void m2mtest_wait_finish(struct vb2_queue *q)
+{
+ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
+ m2mtest_lock(ctx);
+}
+
+static struct vb2_ops m2mtest_qops = {
+ .queue_setup = m2mtest_queue_setup,
+ .buf_prepare = m2mtest_buf_prepare,
+ .buf_queue = m2mtest_buf_queue,
+ .wait_prepare = m2mtest_wait_prepare,
+ .wait_finish = m2mtest_wait_finish,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct m2mtest_ctx *ctx = priv;
+ int ret;
+
+ 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;
+
+ 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);
+}
+
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
+ .ops = &m2mtest_ctrl_ops,
+ .id = V4L2_CID_TRANS_TIME_MSEC,
+ .name = "Transaction Time (msec)",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 1001,
+ .min = 1,
+ .max = 10001,
+ .step = 100,
+};
+
+static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
+ .ops = &m2mtest_ctrl_ops,
+ .id = V4L2_CID_TRANS_NUM_BUFS,
+ .name = "Buffers Per Transaction",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .def = 1,
+ .min = 1,
+ .max = MEM2MEM_DEF_NUM_BUFS,
+ .step = 1,
+};
+
+/*
+ * File operations
+ */
+static int m2mtest_open(struct file *file)
+{
+ struct m2mtest_dev *dev = video_drvdata(file);
+ struct m2mtest_ctx *ctx = NULL;
+ struct v4l2_ctrl_handler *hdl;
+ int rc = 0;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto open_unlock;
+ }
+
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ ctx->dev = dev;
+ hdl = &ctx->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
+ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
+ if (hdl->error) {
+ rc = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ goto open_unlock;
+ }
+ ctx->fh.ctrl_handler = hdl;
+ v4l2_ctrl_handler_setup(hdl);
+
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
+ ctx->q_data[V4L2_M2M_SRC].width = 640;
+ ctx->q_data[V4L2_M2M_SRC].height = 480;
+ ctx->q_data[V4L2_M2M_SRC].sizeimage =
+ ctx->q_data[V4L2_M2M_SRC].width *
+ ctx->q_data[V4L2_M2M_SRC].height *
+ (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
+ ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
+ ctx->colorspace = V4L2_COLORSPACE_REC709;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ rc = PTR_ERR(ctx->m2m_ctx);
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(ctx);
+ goto open_unlock;
+ }
+
+ v4l2_fh_add(&ctx->fh);
+ atomic_inc(&dev->num_inst);
+
+ dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+open_unlock:
+ mutex_unlock(&dev->dev_mutex);
+ return rc;
+}
+
+static int m2mtest_release(struct file *file)
+{
+ struct m2mtest_dev *dev = video_drvdata(file);
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ dprintk(dev, "Releasing instance %p\n", ctx);
+
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ v4l2_ctrl_handler_free(&ctx->hdl);
+ mutex_lock(&dev->dev_mutex);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ mutex_unlock(&dev->dev_mutex);
+ kfree(ctx);
+
+ atomic_dec(&dev->num_inst);
+
+ return 0;
+}
+
+static unsigned int m2mtest_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct m2mtest_ctx *ctx = file2ctx(file);
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct m2mtest_dev *dev = video_drvdata(file);
+ struct m2mtest_ctx *ctx = file2ctx(file);
+ int res;
+
+ if (mutex_lock_interruptible(&dev->dev_mutex))
+ return -ERESTARTSYS;
+ res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&dev->dev_mutex);
+ return res;
+}
+
+static const struct v4l2_file_operations m2mtest_fops = {
+ .owner = THIS_MODULE,
+ .open = m2mtest_open,
+ .release = m2mtest_release,
+ .poll = m2mtest_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = m2mtest_mmap,
+};
+
+static struct video_device m2mtest_videodev = {
+ .name = MEM2MEM_NAME,
+ .vfl_dir = VFL_DIR_M2M,
+ .fops = &m2mtest_fops,
+ .ioctl_ops = &m2mtest_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+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)
+{
+ struct m2mtest_dev *dev;
+ struct video_device *vfd;
+ int ret;
+
+ dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->irqlock);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto free_dev;
+
+ atomic_set(&dev->num_inst, 0);
+ mutex_init(&dev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = m2mtest_videodev;
+ vfd->lock = &dev->dev_mutex;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+
+ video_set_drvdata(vfd, dev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
+ "Device registered as /dev/video%d\n", vfd->num);
+
+ setup_timer(&dev->timer, device_isr, (long)dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto err_m2m;
+ }
+
+ return 0;
+
+ v4l2_m2m_release(dev->m2m_dev);
+err_m2m:
+ video_unregister_device(dev->vfd);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+ kfree(dev);
+
+ return ret;
+}
+
+static int m2mtest_remove(struct platform_device *pdev)
+{
+ struct m2mtest_dev *dev =
+ (struct m2mtest_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
+ v4l2_m2m_release(dev->m2m_dev);
+ del_timer_sync(&dev->timer);
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev);
+
+ return 0;
+}
+
+static struct platform_driver m2mtest_pdrv = {
+ .probe = m2mtest_probe,
+ .remove = m2mtest_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit m2mtest_exit(void)
+{
+ platform_driver_unregister(&m2mtest_pdrv);
+ platform_device_unregister(&m2mtest_pdev);
+}
+
+static int __init m2mtest_init(void)
+{
+ int ret;
+
+ ret = platform_device_register(&m2mtest_pdev);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&m2mtest_pdrv);
+ if (ret)
+ platform_device_unregister(&m2mtest_pdev);
+
+ return 0;
+}
+
+module_init(m2mtest_init);
+module_exit(m2mtest_exit);
+
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c
new file mode 100644
index 000000000000..8f22ce543cf7
--- /dev/null
+++ b/drivers/media/platform/mx2_emmaprp.c
@@ -0,0 +1,1028 @@
+/*
+ * Support eMMa-PrP through mem2mem framework.
+ *
+ * eMMa-PrP is a piece of HW that allows fetching buffers
+ * from one memory location and do several operations on
+ * them such as scaling or format conversion giving, as a result
+ * a new processed buffer in another memory location.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2011 Vista Silicon S.L.
+ * Javier Martin <javier.martin@vista-silicon.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
+ */
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <asm/sizes.h>
+
+#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
+
+MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 2040
+#define MAX_H 2046
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN_YUV420 3 /* multiple of 8 */
+#define W_ALIGN_OTHERS 2 /* multiple of 4 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-emmaprp"
+
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT SZ_16M
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+#define PRP_SIZE_HEIGHT(x) (x)
+#define PRP_SIZE_WIDTH(x) ((x) << 16)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+#define PRP_INTR_ST_RDERR (1 << 0)
+#define PRP_INTR_ST_CH1WERR (1 << 1)
+#define PRP_INTR_ST_CH2WERR (1 << 2)
+#define PRP_INTR_ST_CH2B2CI (1 << 3)
+#define PRP_INTR_ST_CH2B1CI (1 << 4)
+#define PRP_INTR_ST_CH1B2CI (1 << 5)
+#define PRP_INTR_ST_CH1B1CI (1 << 6)
+#define PRP_INTR_ST_LBOVF (1 << 7)
+#define PRP_INTR_ST_CH2OVF (1 << 8)
+
+struct emmaprp_fmt {
+ char *name;
+ u32 fourcc;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct emmaprp_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+/* Per-queue, driver-specific private data */
+struct emmaprp_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct emmaprp_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct emmaprp_fmt *find_format(struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct emmaprp_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ int irq_emma;
+ void __iomem *base_emma;
+ struct clk *clk_emma_ahb, *clk_emma_ipg;
+ struct resource *res_emma;
+
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+struct emmaprp_ctx {
+ struct emmaprp_dev *dev;
+ /* Abort requested by m2m */
+ int aborting;
+ struct emmaprp_q_data q_data[2];
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &(ctx->q_data[V4L2_M2M_SRC]);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &(ctx->q_data[V4L2_M2M_DST]);
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+static void emmaprp_job_abort(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ dprintk(pcdev, "Aborting task\n");
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void emmaprp_lock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void emmaprp_unlock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
+{
+ dprintk(pcdev,
+ "eMMa-PrP Registers:\n"
+ " SOURCE_Y_PTR = 0x%08X\n"
+ " SRC_FRAME_SIZE = 0x%08X\n"
+ " DEST_Y_PTR = 0x%08X\n"
+ " DEST_CR_PTR = 0x%08X\n"
+ " DEST_CB_PTR = 0x%08X\n"
+ " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
+ " CNTL = 0x%08X\n",
+ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
+ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
+ readl(pcdev->base_emma + PRP_DEST_Y_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CR_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CB_PTR),
+ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
+ readl(pcdev->base_emma + PRP_CNTL));
+}
+
+static void emmaprp_device_run(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_q_data *s_q_data, *d_q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ unsigned int s_width, s_height;
+ unsigned int d_width, d_height;
+ unsigned int d_size;
+ dma_addr_t p_in, p_out;
+ u32 tmp;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ s_width = s_q_data->width;
+ s_height = s_q_data->height;
+
+ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ d_width = d_q_data->width;
+ d_height = d_q_data->height;
+ d_size = d_width * d_height;
+
+ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&pcdev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return;
+ }
+
+ /* Input frame parameters */
+ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
+ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+
+ /* Output frame parameters */
+ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
+ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
+ writel(p_out + d_size + (d_size >> 2),
+ pcdev->base_emma + PRP_DEST_CR_PTR);
+ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+
+ /* IRQ configuration */
+ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
+ writel(tmp | PRP_INTR_RDERR |
+ PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC,
+ pcdev->base_emma + PRP_INTR_CNTL);
+
+ emmaprp_dump_regs(pcdev);
+
+ /* Enable transfer */
+ tmp = readl(pcdev->base_emma + PRP_CNTL);
+ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
+ PRP_CNTL_DATA_IN_YUV422 |
+ PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+}
+
+static irqreturn_t emmaprp_irq(int irq_emma, void *data)
+{
+ struct emmaprp_dev *pcdev = data;
+ struct emmaprp_ctx *curr_ctx;
+ struct vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+ u32 irqst;
+
+ /* Check irq flags and clear irq */
+ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
+ dprintk(pcdev, "irqst = 0x%08x\n", irqst);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
+ if (curr_ctx == NULL) {
+ pr_err("Instance released before the end of transaction\n");
+ return IRQ_HANDLED;
+ }
+
+ if (!curr_ctx->aborting) {
+ if ((irqst & PRP_INTR_ST_RDERR) ||
+ (irqst & PRP_INTR_ST_CH2WERR)) {
+ pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ spin_lock_irqsave(&pcdev->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(&pcdev->irqlock, flags);
+ }
+ }
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct emmaprp_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct emmaprp_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
+ else /* YUYV */
+ f->fmt.pix.bytesperline = q_data->width * 2;
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f)
+{
+ enum v4l2_field field;
+
+
+ if (!find_format(f))
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_YUV420, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ } else {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_OTHERS, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct emmaprp_q_data *q_data;
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = vidioc_try_fmt(f);
+ if (ret)
+ return ret;
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
+ else /* YUYV */
+ q_data->sizeimage = q_data->width * q_data->height * 2;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+
+/*
+ * Queue operations
+ */
+static int emmaprp_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
+ struct emmaprp_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ size = q_data->width * q_data->height * 3 / 2;
+ else
+ size = q_data->width * q_data->height * 2;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int emmaprp_buf_prepare(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct emmaprp_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ 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;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void emmaprp_buf_queue(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops emmaprp_qops = {
+ .queue_setup = emmaprp_queue_setup,
+ .buf_prepare = emmaprp_buf_prepare,
+ .buf_queue = emmaprp_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct emmaprp_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &emmaprp_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &emmaprp_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int emmaprp_open(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = pcdev;
+
+ if (mutex_lock_interruptible(&pcdev->dev_mutex)) {
+ kfree(ctx);
+ return -ERESTARTSYS;
+ }
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ mutex_unlock(&pcdev->dev_mutex);
+ kfree(ctx);
+ return ret;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ipg);
+ clk_prepare_enable(pcdev->clk_emma_ahb);
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
+ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+ mutex_unlock(&pcdev->dev_mutex);
+
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int emmaprp_release(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
+
+ mutex_lock(&pcdev->dev_mutex);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ mutex_unlock(&pcdev->dev_mutex);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int emmaprp_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+ unsigned int res;
+
+ mutex_lock(&pcdev->dev_mutex);
+ res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&pcdev->dev_mutex);
+ return res;
+}
+
+static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+ int ret;
+
+ if (mutex_lock_interruptible(&pcdev->dev_mutex))
+ return -ERESTARTSYS;
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&pcdev->dev_mutex);
+ return ret;
+}
+
+static const struct v4l2_file_operations emmaprp_fops = {
+ .owner = THIS_MODULE,
+ .open = emmaprp_open,
+ .release = emmaprp_release,
+ .poll = emmaprp_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = emmaprp_mmap,
+};
+
+static struct video_device emmaprp_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &emmaprp_fops,
+ .ioctl_ops = &emmaprp_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = emmaprp_device_run,
+ .job_abort = emmaprp_job_abort,
+ .lock = emmaprp_lock,
+ .unlock = emmaprp_unlock,
+};
+
+static int emmaprp_probe(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev;
+ struct video_device *vfd;
+ struct resource *res_emma;
+ int irq_emma;
+ int ret;
+
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&pcdev->irqlock);
+
+ pcdev->clk_emma_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(pcdev->clk_emma_ipg)) {
+ return PTR_ERR(pcdev->clk_emma_ipg);
+ }
+
+ pcdev->clk_emma_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(pcdev->clk_emma_ahb))
+ return PTR_ERR(pcdev->clk_emma_ahb);
+
+ irq_emma = platform_get_irq(pdev, 0);
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (irq_emma < 0 || res_emma == NULL) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ return -ENODEV;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
+ if (ret)
+ return ret;
+
+ mutex_init(&pcdev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = emmaprp_videodev;
+ vfd->lock = &pcdev->dev_mutex;
+
+ video_set_drvdata(vfd, pcdev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
+ pcdev->vfd = vfd;
+ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
+ " Device registered as /dev/video%d\n", vfd->num);
+
+ platform_set_drvdata(pdev, pcdev);
+
+ pcdev->base_emma = devm_request_and_ioremap(&pdev->dev, res_emma);
+ if (!pcdev->base_emma) {
+ ret = -ENXIO;
+ goto rel_vdev;
+ }
+
+ pcdev->irq_emma = irq_emma;
+ pcdev->res_emma = res_emma;
+
+ if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
+ 0, MEM2MEM_NAME, pcdev) < 0) {
+ ret = -ENODEV;
+ goto rel_vdev;
+ }
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(pcdev->alloc_ctx);
+ goto rel_vdev;
+ }
+
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(pcdev->m2m_dev)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(pcdev->m2m_dev);
+ goto rel_ctx;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
+ goto rel_m2m;
+ }
+
+ return 0;
+
+
+rel_m2m:
+ v4l2_m2m_release(pcdev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+
+ return ret;
+}
+
+static int emmaprp_remove(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
+
+ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
+
+ video_unregister_device(pcdev->vfd);
+ v4l2_m2m_release(pcdev->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+
+ return 0;
+}
+
+static struct platform_driver emmaprp_pdrv = {
+ .probe = emmaprp_probe,
+ .remove = emmaprp_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit emmaprp_exit(void)
+{
+ platform_driver_unregister(&emmaprp_pdrv);
+}
+
+static int __init emmaprp_init(void)
+{
+ return platform_driver_register(&emmaprp_pdrv);
+}
+
+module_init(emmaprp_init);
+module_exit(emmaprp_exit);
diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index 390ab094f9f2..390ab094f9f2 100644
--- a/drivers/media/video/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/omap/Makefile
new file mode 100644
index 000000000000..d80df41fde28
--- /dev/null
+++ b/drivers/media/platform/omap/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the omap video device drivers.
+#
+
+# OMAP2/3 Display driver
+omap-vout-y += omap_vout.o omap_voutlib.o
+omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o
+obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
new file mode 100644
index 000000000000..196e51670050
--- /dev/null
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -0,0 +1,2290 @@
+/*
+ * omap_vout.c
+ *
+ * Copyright (C) 2005-2010 Texas Instruments.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Leveraged code from the OMAP2 camera driver
+ * Video-for-Linux (Version 2) camera capture driver for
+ * the OMAP24xx camera controller.
+ *
+ * Author: Andy Lowe (source@mvista.com)
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * History:
+ * 20-APR-2006 Khasim Modified VRFB based Rotation,
+ * The image data is always read from 0 degree
+ * view and written
+ * to the virtual space of desired rotation angle
+ * 4-DEC-2006 Jian Changed to support better memory management
+ *
+ * 17-Nov-2008 Hardik Changed driver to use video_ioctl2
+ *
+ * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/videodev2.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+#include <media/videobuf-dma-contig.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#include <plat/dma.h>
+#include <plat/vrfb.h>
+#include <video/omapdss.h>
+
+#include "omap_voutlib.h"
+#include "omap_voutdef.h"
+#include "omap_vout_vrfb.h"
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
+MODULE_LICENSE("GPL");
+
+/* Driver Configuration macros */
+#define VOUT_NAME "omap_vout"
+
+enum omap_vout_channels {
+ OMAP_VIDEO1,
+ OMAP_VIDEO2,
+};
+
+static struct videobuf_queue_ops video_vbq_ops;
+/* Variables configurable through module params*/
+static u32 video1_numbuffers = 3;
+static u32 video2_numbuffers = 3;
+static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
+static bool vid1_static_vrfb_alloc;
+static bool vid2_static_vrfb_alloc;
+static bool debug;
+
+/* Module parameters */
+module_param(video1_numbuffers, uint, S_IRUGO);
+MODULE_PARM_DESC(video1_numbuffers,
+ "Number of buffers to be allocated at init time for Video1 device.");
+
+module_param(video2_numbuffers, uint, S_IRUGO);
+MODULE_PARM_DESC(video2_numbuffers,
+ "Number of buffers to be allocated at init time for Video2 device.");
+
+module_param(video1_bufsize, uint, S_IRUGO);
+MODULE_PARM_DESC(video1_bufsize,
+ "Size of the buffer to be allocated for video1 device");
+
+module_param(video2_bufsize, uint, S_IRUGO);
+MODULE_PARM_DESC(video2_bufsize,
+ "Size of the buffer to be allocated for video2 device");
+
+module_param(vid1_static_vrfb_alloc, bool, S_IRUGO);
+MODULE_PARM_DESC(vid1_static_vrfb_alloc,
+ "Static allocation of the VRFB buffer for video1 device");
+
+module_param(vid2_static_vrfb_alloc, bool, S_IRUGO);
+MODULE_PARM_DESC(vid2_static_vrfb_alloc,
+ "Static allocation of the VRFB buffer for video2 device");
+
+module_param(debug, bool, S_IRUGO);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/* list of image formats supported by OMAP2 video pipelines */
+static const struct v4l2_fmtdesc omap_formats[] = {
+ {
+ /* Note: V4L2 defines RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
+ *
+ * We interpret RGB565 as:
+ *
+ * Byte 0 Byte 1
+ * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
+ */
+ .description = "RGB565, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use
+ * this for RGB24 unpack mode, the last 8 bits are ignored
+ * */
+ .description = "RGB32, le",
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ },
+ {
+ /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use
+ * this for RGB24 packed mode
+ *
+ */
+ .description = "RGB24, le",
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ },
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .description = "UYVY, packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
+
+/*
+ * Try format
+ */
+static int omap_vout_try_format(struct v4l2_pix_format *pix)
+{
+ int ifmt, bpp = 0;
+
+ pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT,
+ (u32)VID_MAX_HEIGHT);
+ pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH);
+
+ for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) {
+ if (pix->pixelformat == omap_formats[ifmt].pixelformat)
+ break;
+ }
+
+ if (ifmt == NUM_OUTPUT_FORMATS)
+ ifmt = 0;
+
+ pix->pixelformat = omap_formats[ifmt].pixelformat;
+ pix->field = V4L2_FIELD_ANY;
+ pix->priv = 0;
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+ bpp = YUYV_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB565_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB24_BPP;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ bpp = RGB32_BPP;
+ break;
+ }
+ pix->bytesperline = pix->width * bpp;
+ pix->sizeimage = pix->bytesperline * pix->height;
+
+ return bpp;
+}
+
+/*
+ * omap_vout_uservirt_to_phys: This inline function is used to convert user
+ * space virtual address to physical address.
+ */
+static u32 omap_vout_uservirt_to_phys(u32 virtp)
+{
+ unsigned long physp = 0;
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+
+ vma = find_vma(mm, virtp);
+ /* For kernel direct-mapped memory, take the easy way */
+ if (virtp >= PAGE_OFFSET) {
+ physp = virt_to_phys((void *) virtp);
+ } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
+ /* this will catch, kernel-allocated, mmaped-to-usermode
+ addresses */
+ physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
+ } else {
+ /* otherwise, use get_user_pages() for general userland pages */
+ int res, nr_pages = 1;
+ struct page *pages;
+ down_read(&current->mm->mmap_sem);
+
+ res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
+ 0, &pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (res == nr_pages) {
+ physp = __pa(page_address(&pages[0]) +
+ (virtp & ~PAGE_MASK));
+ } else {
+ printk(KERN_WARNING VOUT_NAME
+ "get_user_pages failed\n");
+ return 0;
+ }
+ }
+
+ return physp;
+}
+
+/*
+ * Free the V4L2 buffers
+ */
+void omap_vout_free_buffers(struct omap_vout_device *vout)
+{
+ int i, numbuffers;
+
+ /* Allocate memory for the buffers */
+ numbuffers = (vout->vid) ? video2_numbuffers : video1_numbuffers;
+ vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize;
+
+ for (i = 0; i < numbuffers; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_phy_addr[i] = 0;
+ vout->buf_virt_addr[i] = 0;
+ }
+}
+
+/*
+ * Convert V4L2 rotation to DSS rotation
+ * V4L2 understand 0, 90, 180, 270.
+ * Convert to 0, 1, 2 and 3 respectively for DSS
+ */
+static int v4l2_rot_to_dss_rot(int v4l2_rotation,
+ enum dss_rotation *rotation, bool mirror)
+{
+ int ret = 0;
+
+ switch (v4l2_rotation) {
+ case 90:
+ *rotation = dss_rotation_90_degree;
+ break;
+ case 180:
+ *rotation = dss_rotation_180_degree;
+ break;
+ case 270:
+ *rotation = dss_rotation_270_degree;
+ break;
+ case 0:
+ *rotation = dss_rotation_0_degree;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int omap_vout_calculate_offset(struct omap_vout_device *vout)
+{
+ struct omapvideo_info *ovid;
+ struct v4l2_rect *crop = &vout->crop;
+ struct v4l2_pix_format *pix = &vout->pix;
+ int *cropped_offset = &vout->cropped_offset;
+ int ps = 2, line_length = 0;
+
+ ovid = &vout->vid_info;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_calculate_vrfb_offset(vout);
+ } else {
+ vout->line_length = line_length = pix->width;
+
+ if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
+ V4L2_PIX_FMT_UYVY == pix->pixelformat)
+ ps = 2;
+ else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
+ ps = 4;
+ else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
+ ps = 3;
+
+ vout->ps = ps;
+
+ *cropped_offset = (line_length * ps) *
+ crop->top + crop->left * ps;
+ }
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
+ __func__, vout->cropped_offset);
+
+ return 0;
+}
+
+/*
+ * Convert V4L2 pixel format to DSS pixel format
+ */
+static int video_mode_to_dss_mode(struct omap_vout_device *vout)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct v4l2_pix_format *pix = &vout->pix;
+ enum omap_color_mode mode;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ switch (pix->pixelformat) {
+ case 0:
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ mode = OMAP_DSS_COLOR_YUV2;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ mode = OMAP_DSS_COLOR_UYVY;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ mode = OMAP_DSS_COLOR_RGB16;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ mode = OMAP_DSS_COLOR_RGB24P;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ mode = (ovl->id == OMAP_DSS_VIDEO1) ?
+ OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ mode = OMAP_DSS_COLOR_RGBX32;
+ break;
+ default:
+ mode = -EINVAL;
+ }
+ return mode;
+}
+
+/*
+ * Setup the overlay
+ */
+static int omapvid_setup_overlay(struct omap_vout_device *vout,
+ struct omap_overlay *ovl, int posx, int posy, int outw,
+ int outh, u32 addr)
+{
+ int ret = 0;
+ struct omap_overlay_info info;
+ int cropheight, cropwidth, pixheight, pixwidth;
+
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
+ (outw != vout->pix.width || outh != vout->pix.height)) {
+ ret = -EINVAL;
+ goto setup_ovl_err;
+ }
+
+ vout->dss_mode = video_mode_to_dss_mode(vout);
+ if (vout->dss_mode == -EINVAL) {
+ ret = -EINVAL;
+ goto setup_ovl_err;
+ }
+
+ /* Setup the input plane parameters according to
+ * rotation value selected.
+ */
+ if (is_rotation_90_or_270(vout)) {
+ cropheight = vout->crop.width;
+ cropwidth = vout->crop.height;
+ pixheight = vout->pix.width;
+ pixwidth = vout->pix.height;
+ } else {
+ cropheight = vout->crop.height;
+ cropwidth = vout->crop.width;
+ pixheight = vout->pix.height;
+ pixwidth = vout->pix.width;
+ }
+
+ ovl->get_overlay_info(ovl, &info);
+ info.paddr = addr;
+ info.width = cropwidth;
+ info.height = cropheight;
+ info.color_mode = vout->dss_mode;
+ info.mirror = vout->mirror;
+ info.pos_x = posx;
+ info.pos_y = posy;
+ info.out_width = outw;
+ info.out_height = outh;
+ info.global_alpha = vout->win.global_alpha;
+ if (!is_rotation_enabled(vout)) {
+ info.rotation = 0;
+ info.rotation_type = OMAP_DSS_ROT_DMA;
+ info.screen_width = pixwidth;
+ } else {
+ info.rotation = vout->rotation;
+ info.rotation_type = OMAP_DSS_ROT_VRFB;
+ info.screen_width = 2048;
+ }
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
+ "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
+ "out_height=%d rotation_type=%d screen_width=%d\n",
+ __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
+ info.color_mode, info.rotation, info.mirror, info.pos_x,
+ info.pos_y, info.out_width, info.out_height, info.rotation_type,
+ info.screen_width);
+
+ ret = ovl->set_overlay_info(ovl, &info);
+ if (ret)
+ goto setup_ovl_err;
+
+ return 0;
+
+setup_ovl_err:
+ v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n");
+ return ret;
+}
+
+/*
+ * Initialize the overlay structure
+ */
+static int omapvid_init(struct omap_vout_device *vout, u32 addr)
+{
+ int ret = 0, i;
+ struct v4l2_window *win;
+ struct omap_overlay *ovl;
+ int posx, posy, outw, outh, temp;
+ struct omap_video_timings *timing;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ win = &vout->win;
+ for (i = 0; i < ovid->num_overlays; i++) {
+ ovl = ovid->overlays[i];
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+
+ timing = &ovl->manager->device->panel.timings;
+
+ outw = win->w.width;
+ outh = win->w.height;
+ switch (vout->rotation) {
+ case dss_rotation_90_degree:
+ /* Invert the height and width for 90
+ * and 270 degree rotation
+ */
+ temp = outw;
+ outw = outh;
+ outh = temp;
+ posy = (timing->y_res - win->w.width) - win->w.left;
+ posx = win->w.top;
+ break;
+
+ case dss_rotation_180_degree:
+ posx = (timing->x_res - win->w.width) - win->w.left;
+ posy = (timing->y_res - win->w.height) - win->w.top;
+ break;
+
+ case dss_rotation_270_degree:
+ temp = outw;
+ outw = outh;
+ outh = temp;
+ posy = win->w.left;
+ posx = (timing->x_res - win->w.height) - win->w.top;
+ break;
+
+ default:
+ posx = win->w.left;
+ posy = win->w.top;
+ break;
+ }
+
+ ret = omapvid_setup_overlay(vout, ovl, posx, posy,
+ outw, outh, addr);
+ if (ret)
+ goto omapvid_init_err;
+ }
+ return 0;
+
+omapvid_init_err:
+ v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n");
+ return ret;
+}
+
+/*
+ * Apply the changes set the go bit of DSS
+ */
+static int omapvid_apply_changes(struct omap_vout_device *vout)
+{
+ int i;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ for (i = 0; i < ovid->num_overlays; i++) {
+ ovl = ovid->overlays[i];
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+ ovl->manager->apply(ovl->manager);
+ }
+
+ return 0;
+}
+
+static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
+ unsigned int irqstatus, struct timeval timevalue)
+{
+ u32 fid;
+
+ if (vout->first_int) {
+ vout->first_int = 0;
+ goto err;
+ }
+
+ if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
+ fid = 1;
+ else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
+ fid = 0;
+ else
+ goto err;
+
+ vout->field_id ^= 1;
+ if (fid != vout->field_id) {
+ if (fid == 0)
+ vout->field_id = fid;
+ } else if (0 == fid) {
+ if (vout->cur_frm == vout->next_frm)
+ goto err;
+
+ vout->cur_frm->ts = timevalue;
+ vout->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&vout->cur_frm->done);
+ vout->cur_frm = vout->next_frm;
+ } else {
+ if (list_empty(&vout->dma_queue) ||
+ (vout->cur_frm != vout->next_frm))
+ goto err;
+ }
+
+ return vout->field_id;
+err:
+ return 0;
+}
+
+static void omap_vout_isr(void *arg, unsigned int irqstatus)
+{
+ int ret, fid, mgr_id;
+ u32 addr, irq;
+ struct omap_overlay *ovl;
+ struct timeval timevalue;
+ struct omapvideo_info *ovid;
+ struct omap_dss_device *cur_display;
+ struct omap_vout_device *vout = (struct omap_vout_device *)arg;
+
+ if (!vout->streaming)
+ return;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+ /* get the display device attached to the overlay */
+ if (!ovl->manager || !ovl->manager->device)
+ return;
+
+ mgr_id = ovl->manager->id;
+ cur_display = ovl->manager->device;
+
+ spin_lock(&vout->vbq_lock);
+ do_gettimeofday(&timevalue);
+
+ switch (cur_display->type) {
+ case OMAP_DISPLAY_TYPE_DSI:
+ case OMAP_DISPLAY_TYPE_DPI:
+ if (mgr_id == OMAP_DSS_CHANNEL_LCD)
+ irq = DISPC_IRQ_VSYNC;
+ else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
+ irq = DISPC_IRQ_VSYNC2;
+ else
+ goto vout_isr_err;
+
+ if (!(irqstatus & irq))
+ goto vout_isr_err;
+ break;
+ case OMAP_DISPLAY_TYPE_VENC:
+ fid = omapvid_handle_interlace_display(vout, irqstatus,
+ timevalue);
+ if (!fid)
+ goto vout_isr_err;
+ break;
+ case OMAP_DISPLAY_TYPE_HDMI:
+ if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
+ goto vout_isr_err;
+ break;
+ default:
+ goto vout_isr_err;
+ }
+
+ if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
+ vout->cur_frm->ts = timevalue;
+ vout->cur_frm->state = VIDEOBUF_DONE;
+ wake_up_interruptible(&vout->cur_frm->done);
+ vout->cur_frm = vout->next_frm;
+ }
+
+ vout->first_int = 0;
+ if (list_empty(&vout->dma_queue))
+ goto vout_isr_err;
+
+ vout->next_frm = list_entry(vout->dma_queue.next,
+ struct videobuf_buffer, queue);
+ list_del(&vout->next_frm->queue);
+
+ vout->next_frm->state = VIDEOBUF_ACTIVE;
+
+ addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i]
+ + vout->cropped_offset;
+
+ /* First save the configuration in ovelray structure */
+ ret = omapvid_init(vout, addr);
+ if (ret)
+ printk(KERN_ERR VOUT_NAME
+ "failed to set overlay info\n");
+ /* Enable the pipeline and set the Go bit */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ printk(KERN_ERR VOUT_NAME "failed to change mode\n");
+
+vout_isr_err:
+ spin_unlock(&vout->vbq_lock);
+}
+
+/* Video buffer call backs */
+
+/*
+ * Buffer setup function is called by videobuf layer when REQBUF ioctl is
+ * called. This is used to setup buffers and return size and count of
+ * buffers allocated. After the call to this buffer, videobuf layer will
+ * setup buffer queue depending on the size and count of buffers
+ */
+static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ int startindex = 0, i, j;
+ u32 phy_addr = 0, virt_addr = 0;
+ struct omap_vout_device *vout = q->priv_data;
+ struct omapvideo_info *ovid = &vout->vid_info;
+ int vid_max_buf_size;
+
+ if (!vout)
+ return -EINVAL;
+
+ vid_max_buf_size = vout->vid == OMAP_VIDEO1 ? video1_bufsize :
+ video2_bufsize;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
+ return -EINVAL;
+
+ startindex = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+ if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
+ *count = startindex;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
+ return -ENOMEM;
+ }
+
+ if (V4L2_MEMORY_MMAP != vout->memory)
+ return 0;
+
+ /* Now allocated the V4L2 buffers */
+ *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp);
+ startindex = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+
+ /* Check the size of the buffer */
+ if (*size > vid_max_buf_size) {
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "buffer allocation mismatch [%u] [%u]\n",
+ *size, vout->buffer_size);
+ return -ENOMEM;
+ }
+
+ for (i = startindex; i < *count; i++) {
+ vout->buffer_size = *size;
+
+ virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
+ &phy_addr);
+ if (!virt_addr) {
+ if (ovid->rotation_type == VOUT_ROT_NONE) {
+ break;
+ } else {
+ if (!is_rotation_enabled(vout))
+ break;
+ /* Free the VRFB buffers if no space for V4L2 buffers */
+ for (j = i; j < *count; j++) {
+ omap_vout_free_buffer(
+ vout->smsshado_virt_addr[j],
+ vout->smsshado_size);
+ vout->smsshado_virt_addr[j] = 0;
+ vout->smsshado_phy_addr[j] = 0;
+ }
+ }
+ }
+ vout->buf_virt_addr[i] = virt_addr;
+ vout->buf_phy_addr[i] = phy_addr;
+ }
+ *count = vout->buffer_allocated = i;
+
+ return 0;
+}
+
+/*
+ * Free the V4L2 buffers additionally allocated than default
+ * number of buffers
+ */
+static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
+{
+ int num_buffers = 0, i;
+
+ num_buffers = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+
+ for (i = num_buffers; i < vout->buffer_allocated; i++) {
+ if (vout->buf_virt_addr[i])
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ vout->buffer_allocated = num_buffers;
+}
+
+/*
+ * This function will be called when VIDIOC_QBUF ioctl is called.
+ * It prepare buffers before give out for the display. This function
+ * converts user space virtual address into physical address if userptr memory
+ * exchange mechanism is used. If rotation is enabled, it copies entire
+ * buffer into VRFB memory space before giving it to the DSS.
+ */
+static int omap_vout_buffer_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap_vout_device *vout = q->priv_data;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->width = vout->pix.width;
+ vb->height = vout->pix.height;
+ vb->size = vb->width * vb->height * vout->bpp;
+ vb->field = field;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ /* if user pointer memory mechanism is used, get the physical
+ * address of the buffer
+ */
+ if (V4L2_MEMORY_USERPTR == vb->memory) {
+ if (0 == vb->baddr)
+ return -EINVAL;
+ /* Physical address */
+ vout->queued_buf_addr[vb->i] = (u8 *)
+ omap_vout_uservirt_to_phys(vb->baddr);
+ } else {
+ u32 addr, dma_addr;
+ unsigned long size;
+
+ addr = (unsigned long) vout->buf_virt_addr[vb->i];
+ size = (unsigned long) vb->size;
+
+ dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
+ size, DMA_TO_DEVICE);
+ if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
+ v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
+
+ vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
+ }
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB)
+ return omap_vout_prepare_vrfb(vout, vb);
+ else
+ return 0;
+}
+
+/*
+ * Buffer queue function will be called from the videobuf layer when _QBUF
+ * ioctl is called. It is used to enqueue buffer, which is ready to be
+ * displayed.
+ */
+static void omap_vout_buffer_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct omap_vout_device *vout = q->priv_data;
+
+ /* Driver is also maintainig a queue. So enqueue buffer in the driver
+ * queue */
+ list_add_tail(&vb->queue, &vout->dma_queue);
+
+ vb->state = VIDEOBUF_QUEUED;
+}
+
+/*
+ * Buffer release function is called from videobuf layer to release buffer
+ * which are already allocated
+ */
+static void omap_vout_buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct omap_vout_device *vout = q->priv_data;
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+
+ if (V4L2_MEMORY_MMAP != vout->memory)
+ return;
+}
+
+/*
+ * File operations
+ */
+static unsigned int omap_vout_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ return videobuf_poll_stream(file, q, wait);
+}
+
+static void omap_vout_vm_open(struct vm_area_struct *vma)
+{
+ struct omap_vout_device *vout = vma->vm_private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+ vout->mmap_count++;
+}
+
+static void omap_vout_vm_close(struct vm_area_struct *vma)
+{
+ struct omap_vout_device *vout = vma->vm_private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
+ vout->mmap_count--;
+}
+
+static struct vm_operations_struct omap_vout_vm_ops = {
+ .open = omap_vout_vm_open,
+ .close = omap_vout_vm_close,
+};
+
+static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int i;
+ void *pos;
+ unsigned long start = vma->vm_start;
+ unsigned long size = (vma->vm_end - vma->vm_start);
+ struct omap_vout_device *vout = file->private_data;
+ struct videobuf_queue *q = &vout->vbq;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__,
+ vma->vm_pgoff, vma->vm_start, vma->vm_end);
+
+ /* look for the buffer to map */
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == q->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
+ continue;
+ if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ if (VIDEO_MAX_FRAME == i) {
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
+ "offset invalid [offset=0x%lx]\n",
+ (vma->vm_pgoff << PAGE_SHIFT));
+ return -EINVAL;
+ }
+ /* Check the size of the buffer */
+ if (size > vout->buffer_size) {
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "insufficient memory [%lu] [%u]\n",
+ size, vout->buffer_size);
+ return -ENOMEM;
+ }
+
+ q->bufs[i]->baddr = vma->vm_start;
+
+ vma->vm_flags |= VM_RESERVED;
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+ vma->vm_ops = &omap_vout_vm_ops;
+ vma->vm_private_data = (void *) vout;
+ pos = (void *)vout->buf_virt_addr[i];
+ vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT;
+ while (size > 0) {
+ unsigned long pfn;
+ pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT;
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vout->mmap_count++;
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+
+ return 0;
+}
+
+static int omap_vout_release(struct file *file)
+{
+ unsigned int ret, i;
+ struct videobuf_queue *q;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = file->private_data;
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+ ovid = &vout->vid_info;
+
+ if (!vout)
+ return 0;
+
+ q = &vout->vbq;
+ /* Disable all the overlay managers connected with this interface */
+ for (i = 0; i < ovid->num_overlays; i++) {
+ struct omap_overlay *ovl = ovid->overlays[i];
+ if (ovl->manager && ovl->manager->device)
+ ovl->disable(ovl);
+ }
+ /* Turn off the pipeline */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_warn(&vout->vid_dev->v4l2_dev,
+ "Unable to apply changes\n");
+
+ /* Free all buffers */
+ omap_vout_free_extra_buffers(vout);
+
+ /* Free the VRFB buffers only if they are allocated
+ * during reqbufs. Don't free if init time allocated
+ */
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ if (!vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
+ videobuf_mmap_free(q);
+
+ /* Even if apply changes fails we should continue
+ freeing allocated memory */
+ if (vout->streaming) {
+ u32 mask = 0;
+
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
+ DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
+ omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+ vout->streaming = 0;
+
+ videobuf_streamoff(q);
+ videobuf_queue_cancel(q);
+ }
+
+ if (vout->mmap_count != 0)
+ vout->mmap_count = 0;
+
+ vout->opened -= 1;
+ file->private_data = NULL;
+
+ if (vout->buffer_allocated)
+ videobuf_mmap_free(q);
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+ return ret;
+}
+
+static int omap_vout_open(struct file *file)
+{
+ struct videobuf_queue *q;
+ struct omap_vout_device *vout = NULL;
+
+ vout = video_drvdata(file);
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
+
+ if (vout == NULL)
+ return -ENODEV;
+
+ /* for now, we only support single open */
+ if (vout->opened)
+ return -EBUSY;
+
+ vout->opened += 1;
+
+ file->private_data = vout;
+ vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ q = &vout->vbq;
+ video_vbq_ops.buf_setup = omap_vout_buffer_setup;
+ video_vbq_ops.buf_prepare = omap_vout_buffer_prepare;
+ video_vbq_ops.buf_release = omap_vout_buffer_release;
+ video_vbq_ops.buf_queue = omap_vout_buffer_queue;
+ spin_lock_init(&vout->vbq_lock);
+
+ videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
+ &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), vout, NULL);
+
+ v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
+ return 0;
+}
+
+/*
+ * V4L2 ioctls
+ */
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap_vout_device *vout = fh;
+
+ strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
+ cap->bus_info[0] = '\0';
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ if (index >= NUM_OUTPUT_FORMATS)
+ return -EINVAL;
+
+ fmt->flags = omap_formats[index].flags;
+ strlcpy(fmt->description, omap_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = omap_formats[index].pixelformat;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap_vout_device *vout = fh;
+
+ f->fmt.pix = vout->pix;
+ return 0;
+
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_video_timings *timing;
+ struct omap_vout_device *vout = fh;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ if (!ovl->manager || !ovl->manager->device)
+ return -EINVAL;
+ /* get the display device attached to the overlay */
+ timing = &ovl->manager->device->panel.timings;
+
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+
+ omap_vout_try_format(&f->fmt.pix);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret, bpp;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_video_timings *timing;
+ struct omap_vout_device *vout = fh;
+
+ if (vout->streaming)
+ return -EBUSY;
+
+ mutex_lock(&vout->lock);
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* get the display device attached to the overlay */
+ if (!ovl->manager || !ovl->manager->device) {
+ ret = -EINVAL;
+ goto s_fmt_vid_out_exit;
+ }
+ timing = &ovl->manager->device->panel.timings;
+
+ /* We dont support RGB24-packed mode if vrfb rotation
+ * is enabled*/
+ if ((is_rotation_enabled(vout)) &&
+ f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ ret = -EINVAL;
+ goto s_fmt_vid_out_exit;
+ }
+
+ /* get the framebuffer parameters */
+
+ if (is_rotation_90_or_270(vout)) {
+ vout->fbuf.fmt.height = timing->x_res;
+ vout->fbuf.fmt.width = timing->y_res;
+ } else {
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+ }
+
+ /* change to samller size is OK */
+
+ bpp = omap_vout_try_format(&f->fmt.pix);
+ f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
+
+ /* try & set the new output format */
+ vout->bpp = bpp;
+ vout->pix = f->fmt.pix;
+ vout->vrfb_bpp = 1;
+
+ /* If YUYV then vrfb bpp is 2, for others its 1 */
+ if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat ||
+ V4L2_PIX_FMT_UYVY == vout->pix.pixelformat)
+ vout->vrfb_bpp = 2;
+
+ /* set default crop and win */
+ omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
+
+ /* Save the changes in the overlay strcuture */
+ ret = omapvid_init(vout, 0);
+ if (ret) {
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+ goto s_fmt_vid_out_exit;
+ }
+
+ ret = 0;
+
+s_fmt_vid_out_exit:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct v4l2_window *win = &f->fmt.win;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ ret = omap_vout_try_window(&vout->fbuf, win);
+
+ if (!ret) {
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+ win->global_alpha = 255;
+ else
+ win->global_alpha = f->fmt.win.global_alpha;
+ }
+
+ return ret;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ int ret = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct v4l2_window *win = &f->fmt.win;
+
+ mutex_lock(&vout->lock);
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
+ if (!ret) {
+ /* Video1 plane does not support global alpha on OMAP3 */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
+ vout->win.global_alpha = 255;
+ else
+ vout->win.global_alpha = f->fmt.win.global_alpha;
+
+ vout->win.chromakey = f->fmt.win.chromakey;
+ }
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_fmtdesc *fmt)
+{
+ int index = fmt->index;
+
+ if (index >= NUM_OUTPUT_FORMATS)
+ return -EINVAL;
+
+ fmt->flags = omap_formats[index].flags;
+ strlcpy(fmt->description, omap_formats[index].description,
+ sizeof(fmt->description));
+ fmt->pixelformat = omap_formats[index].pixelformat;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ u32 key_value = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+ struct v4l2_window *win = &f->fmt.win;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ win->w = vout->win.w;
+ win->field = vout->win.field;
+ win->global_alpha = vout->win.global_alpha;
+
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ key_value = info.trans_key;
+ }
+ win->chromakey = key_value;
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cropcap)
+{
+ struct omap_vout_device *vout = fh;
+ struct v4l2_pix_format *pix = &vout->pix;
+
+ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ /* Width and height are always even */
+ cropcap->bounds.width = pix->width & ~1;
+ cropcap->bounds.height = pix->height & ~1;
+
+ omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect);
+ cropcap->pixelaspect.numerator = 1;
+ cropcap->pixelaspect.denominator = 1;
+ return 0;
+}
+
+static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct omap_vout_device *vout = fh;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ crop->c = vout->crop;
+ return 0;
+}
+
+static int vidioc_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop)
+{
+ int ret = -EINVAL;
+ struct omap_vout_device *vout = fh;
+ struct omapvideo_info *ovid;
+ struct omap_overlay *ovl;
+ struct omap_video_timings *timing;
+
+ if (vout->streaming)
+ return -EBUSY;
+
+ mutex_lock(&vout->lock);
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ if (!ovl->manager || !ovl->manager->device) {
+ ret = -EINVAL;
+ goto s_crop_err;
+ }
+ /* get the display device attached to the overlay */
+ timing = &ovl->manager->device->panel.timings;
+
+ if (is_rotation_90_or_270(vout)) {
+ vout->fbuf.fmt.height = timing->x_res;
+ vout->fbuf.fmt.width = timing->y_res;
+ } else {
+ vout->fbuf.fmt.height = timing->y_res;
+ vout->fbuf.fmt.width = timing->x_res;
+ }
+
+ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win,
+ &vout->fbuf, &crop->c);
+
+s_crop_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *ctrl)
+{
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ROTATE:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
+ break;
+ case V4L2_CID_BG_COLOR:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0);
+ break;
+ case V4L2_CID_VFLIP:
+ ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
+ break;
+ default:
+ ctrl->name[0] = '\0';
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ROTATE:
+ ctrl->value = vout->control[0].value;
+ break;
+ case V4L2_CID_BG_COLOR:
+ {
+ struct omap_overlay_manager_info info;
+ struct omap_overlay *ovl;
+
+ ovl = vout->vid_info.overlays[0];
+ if (!ovl->manager || !ovl->manager->get_manager_info) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ ctrl->value = info.default_color;
+ break;
+ }
+ case V4L2_CID_VFLIP:
+ ctrl->value = vout->control[2].value;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ int ret = 0;
+ struct omap_vout_device *vout = fh;
+
+ switch (a->id) {
+ case V4L2_CID_ROTATE:
+ {
+ struct omapvideo_info *ovid;
+ int rotation = a->value;
+
+ ovid = &vout->vid_info;
+
+ mutex_lock(&vout->lock);
+ if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
+
+ if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (v4l2_rot_to_dss_rot(rotation, &vout->rotation,
+ vout->mirror)) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ vout->control[0].value = rotation;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ case V4L2_CID_BG_COLOR:
+ {
+ struct omap_overlay *ovl;
+ unsigned int color = a->value;
+ struct omap_overlay_manager_info info;
+
+ ovl = vout->vid_info.overlays[0];
+
+ mutex_lock(&vout->lock);
+ if (!ovl->manager || !ovl->manager->get_manager_info) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ info.default_color = color;
+ if (ovl->manager->set_manager_info(ovl->manager, &info)) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+
+ vout->control[1].value = color;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ case V4L2_CID_VFLIP:
+ {
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ unsigned int mirror = a->value;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ mutex_lock(&vout->lock);
+ if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
+ mutex_unlock(&vout->lock);
+ ret = -ERANGE;
+ break;
+ }
+
+ if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
+ mutex_unlock(&vout->lock);
+ ret = -EINVAL;
+ break;
+ }
+ vout->mirror = mirror;
+ vout->control[2].value = mirror;
+ mutex_unlock(&vout->lock);
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *req)
+{
+ int ret = 0;
+ unsigned int i, num_buffers = 0;
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
+ return -EINVAL;
+ /* if memory is not mmp or userptr
+ return error */
+ if ((V4L2_MEMORY_MMAP != req->memory) &&
+ (V4L2_MEMORY_USERPTR != req->memory))
+ return -EINVAL;
+
+ mutex_lock(&vout->lock);
+ /* Cannot be requested when streaming is on */
+ if (vout->streaming) {
+ ret = -EBUSY;
+ goto reqbuf_err;
+ }
+
+ /* If buffers are already allocated free them */
+ if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) {
+ if (vout->mmap_count) {
+ ret = -EBUSY;
+ goto reqbuf_err;
+ }
+ num_buffers = (vout->vid == OMAP_VIDEO1) ?
+ video1_numbuffers : video2_numbuffers;
+ for (i = num_buffers; i < vout->buffer_allocated; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ vout->buffer_allocated = num_buffers;
+ videobuf_mmap_free(q);
+ } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) {
+ if (vout->buffer_allocated) {
+ videobuf_mmap_free(q);
+ for (i = 0; i < vout->buffer_allocated; i++) {
+ kfree(q->bufs[i]);
+ q->bufs[i] = NULL;
+ }
+ vout->buffer_allocated = 0;
+ }
+ }
+
+ /*store the memory type in data structure */
+ vout->memory = req->memory;
+
+ INIT_LIST_HEAD(&vout->dma_queue);
+
+ /* call videobuf_reqbufs api */
+ ret = videobuf_reqbufs(q, req);
+ if (ret < 0)
+ goto reqbuf_err;
+
+ vout->buffer_allocated = req->count;
+
+reqbuf_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap_vout_device *vout = fh;
+
+ return videobuf_querybuf(&vout->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buffer)
+{
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) ||
+ (buffer->index >= vout->buffer_allocated) ||
+ (q->bufs[buffer->index]->memory != buffer->memory)) {
+ return -EINVAL;
+ }
+ if (V4L2_MEMORY_USERPTR == buffer->memory) {
+ if ((buffer->length < vout->pix.sizeimage) ||
+ (0 == buffer->m.userptr)) {
+ return -EINVAL;
+ }
+ }
+
+ if ((is_rotation_enabled(vout)) &&
+ vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
+ v4l2_warn(&vout->vid_dev->v4l2_dev,
+ "DMA Channel not allocated for Rotation\n");
+ return -EINVAL;
+ }
+
+ return videobuf_qbuf(q, buffer);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+
+ int ret;
+ u32 addr;
+ unsigned long size;
+ struct videobuf_buffer *vb;
+
+ vb = q->bufs[b->index];
+
+ if (!vout->streaming)
+ return -EINVAL;
+
+ if (file->f_flags & O_NONBLOCK)
+ /* Call videobuf_dqbuf for non blocking mode */
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
+ else
+ /* Call videobuf_dqbuf for blocking mode */
+ ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
+
+ addr = (unsigned long) vout->buf_phy_addr[vb->i];
+ size = (unsigned long) vb->size;
+ dma_unmap_single(vout->vid_dev->v4l2_dev.dev, addr,
+ size, DMA_TO_DEVICE);
+ return ret;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ int ret = 0, j;
+ u32 addr = 0, mask = 0;
+ struct omap_vout_device *vout = fh;
+ struct videobuf_queue *q = &vout->vbq;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ mutex_lock(&vout->lock);
+
+ if (vout->streaming) {
+ ret = -EBUSY;
+ goto streamon_err;
+ }
+
+ ret = videobuf_streamon(q);
+ if (ret)
+ goto streamon_err;
+
+ if (list_empty(&vout->dma_queue)) {
+ ret = -EIO;
+ goto streamon_err1;
+ }
+
+ /* Get the next frame from the buffer queue */
+ vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next,
+ struct videobuf_buffer, queue);
+ /* Remove buffer from the buffer queue */
+ list_del(&vout->cur_frm->queue);
+ /* Mark state of the current frame to active */
+ vout->cur_frm->state = VIDEOBUF_ACTIVE;
+ /* Initialize field_id and started member */
+ vout->field_id = 0;
+
+ /* set flag here. Next QBUF will start DMA */
+ vout->streaming = 1;
+
+ vout->first_int = 1;
+
+ if (omap_vout_calculate_offset(vout)) {
+ ret = -EINVAL;
+ goto streamon_err1;
+ }
+ addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
+ + vout->cropped_offset;
+
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
+
+ omap_dispc_register_isr(omap_vout_isr, vout, mask);
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device) {
+ struct omap_overlay_info info;
+ ovl->get_overlay_info(ovl, &info);
+ info.paddr = addr;
+ if (ovl->set_overlay_info(ovl, &info)) {
+ ret = -EINVAL;
+ goto streamon_err1;
+ }
+ }
+ }
+
+ /* First save the configuration in ovelray structure */
+ ret = omapvid_init(vout, addr);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev,
+ "failed to set overlay info\n");
+ /* Enable the pipeline and set the Go bit */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device) {
+ ret = ovl->enable(ovl);
+ if (ret)
+ goto streamon_err1;
+ }
+ }
+
+ ret = 0;
+
+streamon_err1:
+ if (ret)
+ ret = videobuf_streamoff(q);
+streamon_err:
+ mutex_unlock(&vout->lock);
+ return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ u32 mask = 0;
+ int ret = 0, j;
+ struct omap_vout_device *vout = fh;
+ struct omapvideo_info *ovid = &vout->vid_info;
+
+ if (!vout->streaming)
+ return -EINVAL;
+
+ vout->streaming = 0;
+ mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
+ | DISPC_IRQ_VSYNC2;
+
+ omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
+
+ for (j = 0; j < ovid->num_overlays; j++) {
+ struct omap_overlay *ovl = ovid->overlays[j];
+
+ if (ovl->manager && ovl->manager->device)
+ ovl->disable(ovl);
+ }
+
+ /* Turn of the pipeline */
+ ret = omapvid_apply_changes(vout);
+ if (ret)
+ v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in"
+ " streamoff\n");
+
+ INIT_LIST_HEAD(&vout->dma_queue);
+ ret = videobuf_streamoff(&vout->vbq);
+
+ return ret;
+}
+
+static int vidioc_s_fbuf(struct file *file, void *fh,
+ const struct v4l2_framebuffer *a)
+{
+ int enable = 0;
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+ enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* OMAP DSS doesn't support Source and Destination color
+ key together */
+ if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
+ (a->flags & V4L2_FBUF_FLAG_CHROMAKEY))
+ return -EINVAL;
+ /* OMAP DSS Doesn't support the Destination color key
+ and alpha blending together */
+ if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
+ (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA))
+ return -EINVAL;
+
+ if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ key_type = OMAP_DSS_COLOR_KEY_VID_SRC;
+ } else
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+
+ if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+ } else
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY;
+
+ if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY |
+ V4L2_FBUF_FLAG_SRC_CHROMAKEY))
+ enable = 1;
+ else
+ enable = 0;
+ if (ovl->manager && ovl->manager->get_manager_info &&
+ ovl->manager->set_manager_info) {
+
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ info.trans_enabled = enable;
+ info.trans_key_type = key_type;
+ info.trans_key = vout->win.chromakey;
+
+ if (ovl->manager->set_manager_info(ovl->manager, &info))
+ return -EINVAL;
+ }
+ if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) {
+ vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ enable = 1;
+ } else {
+ vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ enable = 0;
+ }
+ if (ovl->manager && ovl->manager->get_manager_info &&
+ ovl->manager->set_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ /* enable this only if there is no zorder cap */
+ if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
+ info.partial_alpha_enabled = enable;
+ if (ovl->manager->set_manager_info(ovl->manager, &info))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh,
+ struct v4l2_framebuffer *a)
+{
+ struct omap_overlay *ovl;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout = fh;
+ struct omap_overlay_manager_info info;
+
+ ovid = &vout->vid_info;
+ ovl = ovid->overlays[0];
+
+ /* The video overlay must stay within the framebuffer and can't be
+ positioned independently. */
+ a->flags = V4L2_FBUF_FLAG_OVERLAY;
+ a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY
+ | V4L2_FBUF_CAP_SRC_CHROMAKEY;
+
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC)
+ a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
+ if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST)
+ a->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
+ }
+ if (ovl->manager && ovl->manager->get_manager_info) {
+ ovl->manager->get_manager_info(ovl->manager, &info);
+ if (info.partial_alpha_enabled)
+ a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops vout_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_fbuf = vidioc_s_fbuf,
+ .vidioc_g_fbuf = vidioc_g_fbuf,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+static const struct v4l2_file_operations omap_vout_fops = {
+ .owner = THIS_MODULE,
+ .poll = omap_vout_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = omap_vout_mmap,
+ .open = omap_vout_open,
+ .release = omap_vout_release,
+};
+
+/* Init functions used during driver initialization */
+/* Initial setup of video_data */
+static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
+{
+ struct video_device *vfd;
+ struct v4l2_pix_format *pix;
+ struct v4l2_control *control;
+ struct omap_dss_device *display =
+ vout->vid_info.overlays[0]->manager->device;
+
+ /* set the default pix */
+ pix = &vout->pix;
+
+ /* Set the default picture of QVGA */
+ pix->width = QQVGA_WIDTH;
+ pix->height = QQVGA_HEIGHT;
+
+ /* Default pixel format is RGB 5-6-5 */
+ pix->pixelformat = V4L2_PIX_FMT_RGB565;
+ pix->field = V4L2_FIELD_ANY;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->priv = 0;
+ pix->colorspace = V4L2_COLORSPACE_JPEG;
+
+ vout->bpp = RGB565_BPP;
+ vout->fbuf.fmt.width = display->panel.timings.x_res;
+ vout->fbuf.fmt.height = display->panel.timings.y_res;
+
+ /* Set the data structures for the overlay parameters*/
+ vout->win.global_alpha = 255;
+ vout->fbuf.flags = 0;
+ vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA |
+ V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY;
+ vout->win.chromakey = 0;
+
+ omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win);
+
+ /*Initialize the control variables for
+ rotation, flipping and background color. */
+ control = vout->control;
+ control[0].id = V4L2_CID_ROTATE;
+ control[0].value = 0;
+ vout->rotation = 0;
+ vout->mirror = 0;
+ vout->control[2].id = V4L2_CID_HFLIP;
+ vout->control[2].value = 0;
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ vout->vrfb_bpp = 2;
+
+ control[1].id = V4L2_CID_BG_COLOR;
+ control[1].value = 0;
+
+ /* initialize the video_device struct */
+ vfd = vout->vfd = video_device_alloc();
+
+ if (!vfd) {
+ printk(KERN_ERR VOUT_NAME ": could not allocate"
+ " video device struct\n");
+ return -ENOMEM;
+ }
+ vfd->release = video_device_release;
+ vfd->ioctl_ops = &vout_ioctl_ops;
+
+ strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
+
+ vfd->fops = &omap_vout_fops;
+ vfd->v4l2_dev = &vout->vid_dev->v4l2_dev;
+ vfd->vfl_dir = VFL_DIR_TX;
+ mutex_init(&vout->lock);
+
+ vfd->minor = -1;
+ return 0;
+
+}
+
+/* Setup video buffers */
+static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
+ int vid_num)
+{
+ u32 numbuffers;
+ int ret = 0, i;
+ struct omapvideo_info *ovid;
+ struct omap_vout_device *vout;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev =
+ container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
+
+ vout = vid_dev->vouts[vid_num];
+ ovid = &vout->vid_info;
+
+ numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
+ vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
+ dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size);
+
+ for (i = 0; i < numbuffers; i++) {
+ vout->buf_virt_addr[i] =
+ omap_vout_alloc_buffer(vout->buffer_size,
+ (u32 *) &vout->buf_phy_addr[i]);
+ if (!vout->buf_virt_addr[i]) {
+ numbuffers = i;
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ vout->cropped_offset = 0;
+
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ int static_vrfb_allocation = (vid_num == 0) ?
+ vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
+ ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
+ static_vrfb_allocation);
+ }
+
+ return ret;
+
+free_buffers:
+ for (i = 0; i < numbuffers; i++) {
+ omap_vout_free_buffer(vout->buf_virt_addr[i],
+ vout->buffer_size);
+ vout->buf_virt_addr[i] = 0;
+ vout->buf_phy_addr[i] = 0;
+ }
+ return ret;
+
+}
+
+/* Create video out devices */
+static int __init omap_vout_create_video_devices(struct platform_device *pdev)
+{
+ int ret = 0, k;
+ struct omap_vout_device *vout;
+ struct video_device *vfd = NULL;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev = container_of(v4l2_dev,
+ struct omap2video_device, v4l2_dev);
+
+ for (k = 0; k < pdev->num_resources; k++) {
+
+ vout = kzalloc(sizeof(struct omap_vout_device), GFP_KERNEL);
+ if (!vout) {
+ dev_err(&pdev->dev, ": could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ vout->vid = k;
+ vid_dev->vouts[k] = vout;
+ vout->vid_dev = vid_dev;
+ /* Select video2 if only 1 overlay is controlled by V4L2 */
+ if (pdev->num_resources == 1)
+ vout->vid_info.overlays[0] = vid_dev->overlays[k + 2];
+ else
+ /* Else select video1 and video2 one by one. */
+ vout->vid_info.overlays[0] = vid_dev->overlays[k + 1];
+ vout->vid_info.num_overlays = 1;
+ vout->vid_info.id = k + 1;
+
+ /* Set VRFB as rotation_type for omap2 and omap3 */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ vout->vid_info.rotation_type = VOUT_ROT_VRFB;
+
+ /* Setup the default configuration for the video devices
+ */
+ if (omap_vout_setup_video_data(vout) != 0) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Allocate default number of buffers for the video streaming
+ * and reserve the VRFB space for rotation
+ */
+ if (omap_vout_setup_video_bufs(pdev, k) != 0) {
+ ret = -ENOMEM;
+ goto error1;
+ }
+
+ /* Register the Video device with V4L2
+ */
+ vfd = vout->vfd;
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
+ dev_err(&pdev->dev, ": Could not register "
+ "Video for Linux device\n");
+ vfd->minor = -1;
+ ret = -ENODEV;
+ goto error2;
+ }
+ video_set_drvdata(vfd, vout);
+
+ /* Configure the overlay structure */
+ ret = omapvid_init(vid_dev->vouts[k], 0);
+ if (!ret)
+ goto success;
+
+error2:
+ if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
+ omap_vout_release_vrfb(vout);
+ omap_vout_free_buffers(vout);
+error1:
+ video_device_release(vfd);
+error:
+ kfree(vout);
+ return ret;
+
+success:
+ dev_info(&pdev->dev, ": registered and initialized"
+ " video device %d\n", vfd->minor);
+ if (k == (pdev->num_resources - 1))
+ return 0;
+ }
+
+ return -ENODEV;
+}
+/* Driver functions */
+static void omap_vout_cleanup_device(struct omap_vout_device *vout)
+{
+ struct video_device *vfd;
+ struct omapvideo_info *ovid;
+
+ if (!vout)
+ return;
+
+ vfd = vout->vfd;
+ ovid = &vout->vid_info;
+ if (vfd) {
+ if (!video_is_registered(vfd)) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(vfd);
+ } else {
+ /*
+ * The unregister function will release the video_device
+ * struct as well as unregistering it.
+ */
+ video_unregister_device(vfd);
+ }
+ }
+ if (ovid->rotation_type == VOUT_ROT_VRFB) {
+ omap_vout_release_vrfb(vout);
+ /* Free the VRFB buffer if allocated
+ * init time
+ */
+ if (vout->vrfb_static_allocation)
+ omap_vout_free_vrfb_buffers(vout);
+ }
+ omap_vout_free_buffers(vout);
+
+ kfree(vout);
+}
+
+static int omap_vout_remove(struct platform_device *pdev)
+{
+ int k;
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct omap2video_device *vid_dev = container_of(v4l2_dev, struct
+ omap2video_device, v4l2_dev);
+
+ v4l2_device_unregister(v4l2_dev);
+ for (k = 0; k < pdev->num_resources; k++)
+ omap_vout_cleanup_device(vid_dev->vouts[k]);
+
+ for (k = 0; k < vid_dev->num_displays; k++) {
+ if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED)
+ vid_dev->displays[k]->driver->disable(vid_dev->displays[k]);
+
+ omap_dss_put_device(vid_dev->displays[k]);
+ }
+ kfree(vid_dev);
+ return 0;
+}
+
+static int __init omap_vout_probe(struct platform_device *pdev)
+{
+ int ret = 0, i;
+ struct omap_overlay *ovl;
+ struct omap_dss_device *dssdev = NULL;
+ struct omap_dss_device *def_display;
+ struct omap2video_device *vid_dev = NULL;
+
+ if (pdev->num_resources == 0) {
+ dev_err(&pdev->dev, "probed for an unknown device\n");
+ return -ENODEV;
+ }
+
+ vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
+ if (vid_dev == NULL)
+ return -ENOMEM;
+
+ vid_dev->num_displays = 0;
+ for_each_dss_dev(dssdev) {
+ omap_dss_get_device(dssdev);
+
+ if (!dssdev->driver) {
+ dev_warn(&pdev->dev, "no driver for display: %s\n",
+ dssdev->name);
+ omap_dss_put_device(dssdev);
+ continue;
+ }
+
+ vid_dev->displays[vid_dev->num_displays++] = dssdev;
+ }
+
+ if (vid_dev->num_displays == 0) {
+ dev_err(&pdev->dev, "no displays\n");
+ ret = -EINVAL;
+ goto probe_err0;
+ }
+
+ vid_dev->num_overlays = omap_dss_get_num_overlays();
+ for (i = 0; i < vid_dev->num_overlays; i++)
+ vid_dev->overlays[i] = omap_dss_get_overlay(i);
+
+ vid_dev->num_managers = omap_dss_get_num_overlay_managers();
+ for (i = 0; i < vid_dev->num_managers; i++)
+ vid_dev->managers[i] = omap_dss_get_overlay_manager(i);
+
+ /* Get the Video1 overlay and video2 overlay.
+ * Setup the Display attached to that overlays
+ */
+ for (i = 1; i < vid_dev->num_overlays; i++) {
+ ovl = omap_dss_get_overlay(i);
+ if (ovl->manager && ovl->manager->device) {
+ def_display = ovl->manager->device;
+ } else {
+ dev_warn(&pdev->dev, "cannot find display\n");
+ def_display = NULL;
+ }
+ if (def_display) {
+ struct omap_dss_driver *dssdrv = def_display->driver;
+
+ ret = dssdrv->enable(def_display);
+ if (ret) {
+ /* Here we are not considering a error
+ * as display may be enabled by frame
+ * buffer driver
+ */
+ dev_warn(&pdev->dev,
+ "'%s' Display already enabled\n",
+ def_display->name);
+ }
+ }
+ }
+
+ if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) {
+ dev_err(&pdev->dev, "v4l2_device_register failed\n");
+ ret = -ENODEV;
+ goto probe_err1;
+ }
+
+ ret = omap_vout_create_video_devices(pdev);
+ if (ret)
+ goto probe_err2;
+
+ for (i = 0; i < vid_dev->num_displays; i++) {
+ struct omap_dss_device *display = vid_dev->displays[i];
+
+ if (display->driver->update)
+ display->driver->update(display, 0, 0,
+ display->panel.timings.x_res,
+ display->panel.timings.y_res);
+ }
+ return 0;
+
+probe_err2:
+ v4l2_device_unregister(&vid_dev->v4l2_dev);
+probe_err1:
+ for (i = 1; i < vid_dev->num_overlays; i++) {
+ def_display = NULL;
+ ovl = omap_dss_get_overlay(i);
+ if (ovl->manager && ovl->manager->device)
+ def_display = ovl->manager->device;
+
+ if (def_display && def_display->driver)
+ def_display->driver->disable(def_display);
+ }
+probe_err0:
+ kfree(vid_dev);
+ return ret;
+}
+
+static struct platform_driver omap_vout_driver = {
+ .driver = {
+ .name = VOUT_NAME,
+ },
+ .remove = omap_vout_remove,
+};
+
+static int __init omap_vout_init(void)
+{
+ if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
+ printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void omap_vout_cleanup(void)
+{
+ platform_driver_unregister(&omap_vout_driver);
+}
+
+late_initcall(omap_vout_init);
+module_exit(omap_vout_cleanup);
diff --git a/drivers/media/video/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c
index 4be26abf6cea..4be26abf6cea 100644
--- a/drivers/media/video/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/omap/omap_vout_vrfb.c
diff --git a/drivers/media/video/omap/omap_vout_vrfb.h b/drivers/media/platform/omap/omap_vout_vrfb.h
index ffde741e0590..ffde741e0590 100644
--- a/drivers/media/video/omap/omap_vout_vrfb.h
+++ b/drivers/media/platform/omap/omap_vout_vrfb.h
diff --git a/drivers/media/video/omap/omap_voutdef.h b/drivers/media/platform/omap/omap_voutdef.h
index 27a95d23b913..27a95d23b913 100644
--- a/drivers/media/video/omap/omap_voutdef.h
+++ b/drivers/media/platform/omap/omap_voutdef.h
diff --git a/drivers/media/video/omap/omap_voutlib.c b/drivers/media/platform/omap/omap_voutlib.c
index 115408b9274f..115408b9274f 100644
--- a/drivers/media/video/omap/omap_voutlib.c
+++ b/drivers/media/platform/omap/omap_voutlib.c
diff --git a/drivers/media/video/omap/omap_voutlib.h b/drivers/media/platform/omap/omap_voutlib.h
index e51750a597e3..e51750a597e3 100644
--- a/drivers/media/video/omap/omap_voutlib.h
+++ b/drivers/media/platform/omap/omap_voutlib.h
diff --git a/drivers/media/platform/omap24xxcam-dma.c b/drivers/media/platform/omap24xxcam-dma.c
new file mode 100644
index 000000000000..9c00776d6583
--- /dev/null
+++ b/drivers/media/platform/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/platform/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(void __iomem *base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(void __iomem *base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(void __iomem *base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine
+ * might start a new DMA transfer, and we only want to abort
+ * transfers that were started before this routine was called.
+ */
+ free_dmach = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. If the completion
+ * routines attempt to start a new DMA transfer it will fail, so the
+ * DMA controller will be idle after this routine completes.
+ */
+static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels
+ * are idle, so we'll acknowledge the interrupt in the
+ * IRQSTATUS register and exit.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ void __iomem *base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or non-zero
+ * if all of the scatter-gather slots are already in use.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) && !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ void __iomem *base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c
new file mode 100644
index 000000000000..fde2e66f6f33
--- /dev/null
+++ b/drivers/media/platform/omap24xxcam.c
@@ -0,0 +1,1881 @@
+/*
+ * drivers/media/platform/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION "0.0.1"
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+/*
+ * Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get camera fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get camera ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+ CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ
+ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+}
+
+/*
+ * Enable the camera core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vbq, vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/*
+ * Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ if (*size * *cnt > fh->cam->capture_mem)
+ *cnt = fh->cam->capture_mem / *size;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > vb->size) {
+ /*
+ * The image size has been changed to
+ * a larger size since this buffer was
+ * allocated, so we need to free and
+ * reallocate it.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * Oops. We're not supposed to get any errors here.
+ * The only way we could get an error is if we ran out
+ * of scatter-gather DMA slots, but we are supposed to
+ * have at least as many scatter-gather DMA slots as
+ * video buffers so that can't happen.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a camera
+ * interface error, but it doesn't seem to be enough. If we only do
+ * that then subsequent image captures are out of sync by either one
+ * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+ * entire camera subsystem prevents the problem with frame
+ * synchronization.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_work_sync(&cam->sensor_reset_work);
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static unsigned int omap24xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct file *file)
+{
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd)
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh, NULL);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_work_sync(&cam->sensor_reset_work);
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_work_sync(&cam->sensor_reset_work);
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct v4l2_file_operations omap24xxcam_fops = {
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+ vfd->fops = &omap24xxcam_fops;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device %s\n",
+ video_device_node_name(vfd));
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (!video_is_registered(cam->vfd)) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __devinit omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * Impose a lower limit on the amount of memory allocated for
+ * capture. We require at least enough memory to double-buffer
+ * QVGA (300KB).
+ */
+ if (capture_mem < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = resource_size(mem);
+
+ /* map the region */
+ cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(omap24xxcam_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(OMAP24XXCAM_VERSION);
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+ "buffers (default 4800kiB)");
diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h
new file mode 100644
index 000000000000..c4395956a493
--- /dev/null
+++ b/drivers/media/platform/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/platform/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
+ * the camera DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+
+/*
+ * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
+ * be queued. (We don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ void __iomem *base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ void __iomem *mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 __iomem *addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ void __iomem *base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/drivers/media/video/omap3isp/Makefile b/drivers/media/platform/omap3isp/Makefile
index e8847e79e31a..e8847e79e31a 100644
--- a/drivers/media/video/omap3isp/Makefile
+++ b/drivers/media/platform/omap3isp/Makefile
diff --git a/drivers/media/platform/omap3isp/cfa_coef_table.h b/drivers/media/platform/omap3isp/cfa_coef_table.h
new file mode 100644
index 000000000000..c84df0706f3e
--- /dev/null
+++ b/drivers/media/platform/omap3isp/cfa_coef_table.h
@@ -0,0 +1,61 @@
+/*
+ * cfa_coef_table.h
+ *
+ * TI OMAP3 ISP - CFA coefficients table
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+{ 244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
+247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248 },
+{ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
+ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
+ 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
+ 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
+ 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0 },
+{ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
+ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
+ 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
+ 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
+ 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0 },
+{ 244,12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
+244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
+248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
+250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248 },
diff --git a/drivers/media/video/omap3isp/gamma_table.h b/drivers/media/platform/omap3isp/gamma_table.h
index 78deebf7d965..78deebf7d965 100644
--- a/drivers/media/video/omap3isp/gamma_table.h
+++ b/drivers/media/platform/omap3isp/gamma_table.h
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
new file mode 100644
index 000000000000..d7aa513dcc8d
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -0,0 +1,2243 @@
+/*
+ * isp.c
+ *
+ * TI OMAP3 ISP - Core
+ *
+ * Copyright (C) 2006-2010 Nokia Corporation
+ * Copyright (C) 2007-2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * Contributors:
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ * David Cohen <dacohen@gmail.com>
+ * Stanimir Varbanov <svarbanov@mm-sol.com>
+ * Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
+ * Tuukka Toivonen <tuukkat76@gmail.com>
+ * Sergio Aguirre <saaguirre@ti.com>
+ * Antti Koskipaa <akoskipa@gmail.com>
+ * Ivan T. Ivanov <iivanov@mm-sol.com>
+ * RaniSuneela <r-m@ti.com>
+ * Atanas Filipov <afilipov@mm-sol.com>
+ * Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
+ * Hiroshi DOYU <hiroshi.doyu@nokia.com>
+ * Nayden Kanchev <nkanchev@mm-sol.com>
+ * Phil Carmody <ext-phil.2.carmody@nokia.com>
+ * Artem Bityutskiy <artem.bityutskiy@nokia.com>
+ * Dominic Curran <dcurran@ti.com>
+ * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
+ * Pallavi Kulkarni <p-kulkarni@ti.com>
+ * Vaibhav Hiremath <hvaibhav@ti.com>
+ * Mohit Jalori <mjalori@ti.com>
+ * Sameer Venkatraman <sameerv@ti.com>
+ * Senthilvadivu Guruswamy <svadivu@ti.com>
+ * Thara Gopinath <thara@ti.com>
+ * Toni Leinonen <toni.leinonen@nokia.com>
+ * Troy Laramy <t-laramy@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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispccdc.h"
+#include "isppreview.h"
+#include "ispresizer.h"
+#include "ispcsi2.h"
+#include "ispccp2.h"
+#include "isph3a.h"
+#include "isphist.h"
+
+static unsigned int autoidle;
+module_param(autoidle, int, 0444);
+MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
+
+static void isp_save_ctx(struct isp_device *isp);
+
+static void isp_restore_ctx(struct isp_device *isp);
+
+static const struct isp_res_mapping isp_res_maps[] = {
+ {
+ .isp_rev = ISP_REVISION_2_0,
+ .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+ 1 << OMAP3_ISP_IOMEM_CCP2 |
+ 1 << OMAP3_ISP_IOMEM_CCDC |
+ 1 << OMAP3_ISP_IOMEM_HIST |
+ 1 << OMAP3_ISP_IOMEM_H3A |
+ 1 << OMAP3_ISP_IOMEM_PREV |
+ 1 << OMAP3_ISP_IOMEM_RESZ |
+ 1 << OMAP3_ISP_IOMEM_SBL |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY2,
+ },
+ {
+ .isp_rev = ISP_REVISION_15_0,
+ .map = 1 << OMAP3_ISP_IOMEM_MAIN |
+ 1 << OMAP3_ISP_IOMEM_CCP2 |
+ 1 << OMAP3_ISP_IOMEM_CCDC |
+ 1 << OMAP3_ISP_IOMEM_HIST |
+ 1 << OMAP3_ISP_IOMEM_H3A |
+ 1 << OMAP3_ISP_IOMEM_PREV |
+ 1 << OMAP3_ISP_IOMEM_RESZ |
+ 1 << OMAP3_ISP_IOMEM_SBL |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
+ 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
+ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
+ 1 << OMAP3_ISP_IOMEM_CSIPHY1 |
+ 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ },
+};
+
+/* Structure for saving/restoring ISP module registers */
+static struct isp_reg isp_reg_list[] = {
+ {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
+ {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
+ {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
+ {0, ISP_TOK_TERM, 0}
+};
+
+/*
+ * omap3isp_flush - Post pending L3 bus writes by doing a register readback
+ * @isp: OMAP3 ISP device
+ *
+ * In order to force posting of pending writes, we need to write and
+ * readback the same register, in this case the revision register.
+ *
+ * See this link for reference:
+ * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
+ */
+void omap3isp_flush(struct isp_device *isp)
+{
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+}
+
+/*
+ * isp_enable_interrupts - Enable ISP interrupts.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_enable_interrupts(struct isp_device *isp)
+{
+ static const u32 irq = IRQ0ENABLE_CSIA_IRQ
+ | IRQ0ENABLE_CSIB_IRQ
+ | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ
+ | IRQ0ENABLE_CCDC_LSC_DONE_IRQ
+ | IRQ0ENABLE_CCDC_VD0_IRQ
+ | IRQ0ENABLE_CCDC_VD1_IRQ
+ | IRQ0ENABLE_HS_VS_IRQ
+ | IRQ0ENABLE_HIST_DONE_IRQ
+ | IRQ0ENABLE_H3A_AWB_DONE_IRQ
+ | IRQ0ENABLE_H3A_AF_DONE_IRQ
+ | IRQ0ENABLE_PRV_DONE_IRQ
+ | IRQ0ENABLE_RSZ_DONE_IRQ;
+
+ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+}
+
+/*
+ * isp_disable_interrupts - Disable ISP interrupts.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_disable_interrupts(struct isp_device *isp)
+{
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
+}
+
+/**
+ * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
+ * @isp: OMAP3 ISP device
+ * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
+ * @xclksel: XCLK to configure (0 = A, 1 = B).
+ *
+ * Configures the specified MCLK divisor in the ISP timing control register
+ * (TCTRL_CTRL) to generate the desired xclk clock value.
+ *
+ * Divisor = cam_mclk_hz / xclk
+ *
+ * Returns the final frequency that is actually being generated
+ **/
+static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
+{
+ u32 divisor;
+ u32 currentxclk;
+ unsigned long mclk_hz;
+
+ if (!omap3isp_get(isp))
+ return 0;
+
+ mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+
+ if (xclk >= mclk_hz) {
+ divisor = ISPTCTRL_CTRL_DIV_BYPASS;
+ currentxclk = mclk_hz;
+ } else if (xclk >= 2) {
+ divisor = mclk_hz / xclk;
+ if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
+ divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
+ currentxclk = mclk_hz / divisor;
+ } else {
+ divisor = xclk;
+ currentxclk = 0;
+ }
+
+ switch (xclksel) {
+ case ISP_XCLK_A:
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVA_MASK,
+ divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
+ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
+ currentxclk);
+ break;
+ case ISP_XCLK_B:
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
+ ISPTCTRL_CTRL_DIVB_MASK,
+ divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
+ dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
+ currentxclk);
+ break;
+ case ISP_XCLK_NONE:
+ default:
+ omap3isp_put(isp);
+ dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
+ "xclk. Must be 0 (A) or 1 (B).\n");
+ return -EINVAL;
+ }
+
+ /* Do we go from stable whatever to clock? */
+ if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
+ omap3isp_get(isp);
+ /* Stopping the clock. */
+ else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
+ omap3isp_put(isp);
+
+ isp->xclk_divisor[xclksel - 1] = divisor;
+
+ omap3isp_put(isp);
+
+ return currentxclk;
+}
+
+/*
+ * isp_core_init - ISP core settings
+ * @isp: OMAP3 ISP device
+ * @idle: Consider idle state.
+ *
+ * Set the power settings for the ISP and SBL bus and cConfigure the HS/VS
+ * interrupt source.
+ *
+ * We need to configure the HS/VS interrupt source before interrupts get
+ * enabled, as the sensor might be free-running and the ISP default setting
+ * (HS edge) would put an unnecessary burden on the CPU.
+ */
+static void isp_core_init(struct isp_device *isp, int idle)
+{
+ isp_reg_writel(isp,
+ ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY :
+ ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) <<
+ ISP_SYSCONFIG_MIDLEMODE_SHIFT) |
+ ((isp->revision == ISP_REVISION_15_0) ?
+ ISP_SYSCONFIG_AUTOIDLE : 0),
+ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+
+ isp_reg_writel(isp,
+ (isp->autoidle ? ISPCTRL_SBL_AUTOIDLE : 0) |
+ ISPCTRL_SYNC_DETECT_VSRISE,
+ OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+}
+
+/*
+ * Configure the bridge and lane shifter. Valid inputs are
+ *
+ * CCDC_INPUT_PARALLEL: Parallel interface
+ * CCDC_INPUT_CSI2A: CSI2a receiver
+ * CCDC_INPUT_CCP2B: CCP2b receiver
+ * CCDC_INPUT_CSI2C: CSI2c receiver
+ *
+ * The bridge and lane shifter are configured according to the selected input
+ * and the ISP platform data.
+ */
+void omap3isp_configure_bridge(struct isp_device *isp,
+ enum ccdc_input_entity input,
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift, unsigned int bridge)
+{
+ u32 ispctrl_val;
+
+ ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+ ispctrl_val &= ~ISPCTRL_SHIFT_MASK;
+ ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
+ ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
+ ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
+ ispctrl_val |= bridge;
+
+ switch (input) {
+ case CCDC_INPUT_PARALLEL:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
+ ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
+ shift += pdata->data_lane_shift * 2;
+ break;
+
+ case CCDC_INPUT_CSI2A:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
+ break;
+
+ case CCDC_INPUT_CCP2B:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
+ break;
+
+ case CCDC_INPUT_CSI2C:
+ ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC;
+ break;
+
+ default:
+ return;
+ }
+
+ ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
+
+ isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
+}
+
+void omap3isp_hist_dma_done(struct isp_device *isp)
+{
+ if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
+ omap3isp_stat_pcr_busy(&isp->isp_hist)) {
+ /* Histogram cannot be enabled in this frame anymore */
+ atomic_set(&isp->isp_hist.buf_err, 1);
+ dev_dbg(isp->dev, "hist: Out of synchronization with "
+ "CCDC. Ignoring next buffer.\n");
+ }
+}
+
+static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus)
+{
+ static const char *name[] = {
+ "CSIA_IRQ",
+ "res1",
+ "res2",
+ "CSIB_LCM_IRQ",
+ "CSIB_IRQ",
+ "res5",
+ "res6",
+ "res7",
+ "CCDC_VD0_IRQ",
+ "CCDC_VD1_IRQ",
+ "CCDC_VD2_IRQ",
+ "CCDC_ERR_IRQ",
+ "H3A_AF_DONE_IRQ",
+ "H3A_AWB_DONE_IRQ",
+ "res14",
+ "res15",
+ "HIST_DONE_IRQ",
+ "CCDC_LSC_DONE",
+ "CCDC_LSC_PREFETCH_COMPLETED",
+ "CCDC_LSC_PREFETCH_ERROR",
+ "PRV_DONE_IRQ",
+ "CBUFF_IRQ",
+ "res22",
+ "res23",
+ "RSZ_DONE_IRQ",
+ "OVF_IRQ",
+ "res26",
+ "res27",
+ "MMU_ERR_IRQ",
+ "OCP_ERR_IRQ",
+ "SEC_ERR_IRQ",
+ "HS_VS_IRQ",
+ };
+ int i;
+
+ dev_dbg(isp->dev, "ISP IRQ: ");
+
+ for (i = 0; i < ARRAY_SIZE(name); i++) {
+ if ((1 << i) & irqstatus)
+ printk(KERN_CONT "%s ", name[i]);
+ }
+ printk(KERN_CONT "\n");
+}
+
+static void isp_isr_sbl(struct isp_device *isp)
+{
+ struct device *dev = isp->dev;
+ struct isp_pipeline *pipe;
+ u32 sbl_pcr;
+
+ /*
+ * Handle shared buffer logic overflows for video buffers.
+ * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored.
+ */
+ sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+ isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
+ sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF;
+
+ if (sbl_pcr)
+ dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr);
+
+ if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) {
+ pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF
+ | ISPSBL_PCR_RSZ2_WBL_OVF
+ | ISPSBL_PCR_RSZ3_WBL_OVF
+ | ISPSBL_PCR_RSZ4_WBL_OVF)) {
+ pipe = to_isp_pipeline(&isp->isp_res.subdev.entity);
+ if (pipe != NULL)
+ pipe->error = true;
+ }
+
+ if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF)
+ omap3isp_stat_sbl_overflow(&isp->isp_af);
+
+ if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF)
+ omap3isp_stat_sbl_overflow(&isp->isp_aewb);
+}
+
+/*
+ * isp_isr - Interrupt Service Routine for Camera ISP module.
+ * @irq: Not used currently.
+ * @_isp: Pointer to the OMAP3 ISP device
+ *
+ * Handles the corresponding callback if plugged in.
+ *
+ * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
+ * IRQ wasn't handled.
+ */
+static irqreturn_t isp_isr(int irq, void *_isp)
+{
+ static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ |
+ IRQ0STATUS_CCDC_LSC_DONE_IRQ |
+ IRQ0STATUS_CCDC_VD0_IRQ |
+ IRQ0STATUS_CCDC_VD1_IRQ |
+ IRQ0STATUS_HS_VS_IRQ;
+ struct isp_device *isp = _isp;
+ u32 irqstatus;
+
+ irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+
+ isp_isr_sbl(isp);
+
+ if (irqstatus & IRQ0STATUS_CSIA_IRQ)
+ omap3isp_csi2_isr(&isp->isp_csi2a);
+
+ if (irqstatus & IRQ0STATUS_CSIB_IRQ)
+ omap3isp_ccp2_isr(&isp->isp_ccp2);
+
+ if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) {
+ if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW)
+ omap3isp_preview_isr_frame_sync(&isp->isp_prev);
+ if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)
+ omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+ omap3isp_stat_isr_frame_sync(&isp->isp_aewb);
+ omap3isp_stat_isr_frame_sync(&isp->isp_af);
+ omap3isp_stat_isr_frame_sync(&isp->isp_hist);
+ }
+
+ if (irqstatus & ccdc_events)
+ omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events);
+
+ if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) {
+ if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER)
+ omap3isp_resizer_isr_frame_sync(&isp->isp_res);
+ omap3isp_preview_isr(&isp->isp_prev);
+ }
+
+ if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ)
+ omap3isp_resizer_isr(&isp->isp_res);
+
+ if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_aewb);
+
+ if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_af);
+
+ if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ)
+ omap3isp_stat_isr(&isp->isp_hist);
+
+ omap3isp_flush(isp);
+
+#if defined(DEBUG) && defined(ISP_ISR_DEBUG)
+ isp_isr_dbg(isp, irqstatus);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline power management
+ *
+ * Entities must be powered up when part of a pipeline that contains at least
+ * one open video device node.
+ *
+ * To achieve this use the entity use_count field to track the number of users.
+ * For entities corresponding to video device nodes the use_count field stores
+ * the users count of the node. For entities corresponding to subdevs the
+ * use_count field stores the total number of users of all video device nodes
+ * in the pipeline.
+ *
+ * The omap3isp_pipeline_pm_use() function must be called in the open() and
+ * close() handlers of video device nodes. It increments or decrements the use
+ * count of all subdev entities in the pipeline.
+ *
+ * To react to link management on powered pipelines, the link setup notification
+ * callback updates the use count of all entities in the source and sink sides
+ * of the link.
+ */
+
+/*
+ * isp_pipeline_pm_use_count - Count the number of users of a pipeline
+ * @entity: The entity
+ *
+ * Return the total number of users of all video device nodes in the pipeline.
+ */
+static int isp_pipeline_pm_use_count(struct media_entity *entity)
+{
+ struct media_entity_graph graph;
+ int use = 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
+ use += entity->use_count;
+ }
+
+ return use;
+}
+
+/*
+ * isp_pipeline_pm_power_one - Apply power change to an entity
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Change the entity use count by @change. If the entity is a subdev update its
+ * power state by calling the core::s_power operation when the use count goes
+ * from 0 to != 0 or from != 0 to 0.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
+{
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
+ ? media_entity_to_v4l2_subdev(entity) : NULL;
+
+ if (entity->use_count == 0 && change > 0 && subdev != NULL) {
+ ret = v4l2_subdev_call(subdev, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ if (entity->use_count == 0 && change < 0 && subdev != NULL)
+ v4l2_subdev_call(subdev, core, s_power, 0);
+
+ return 0;
+}
+
+/*
+ * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
+ * @entity: The entity
+ * @change: Use count change
+ *
+ * Walk the pipeline to update the use count and the power state of all non-node
+ * entities.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+static int isp_pipeline_pm_power(struct media_entity *entity, int change)
+{
+ struct media_entity_graph graph;
+ struct media_entity *first = entity;
+ int ret = 0;
+
+ if (!change)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, entity);
+
+ while (!ret && (entity = media_entity_graph_walk_next(&graph)))
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ ret = isp_pipeline_pm_power_one(entity, change);
+
+ if (!ret)
+ return 0;
+
+ media_entity_graph_walk_start(&graph, first);
+
+ while ((first = media_entity_graph_walk_next(&graph))
+ && first != entity)
+ if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
+ isp_pipeline_pm_power_one(first, -change);
+
+ return ret;
+}
+
+/*
+ * omap3isp_pipeline_pm_use - Update the use count of an entity
+ * @entity: The entity
+ * @use: Use (1) or stop using (0) the entity
+ *
+ * Update the use count of all entities in the pipeline and power entities on or
+ * off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. No failure can occur when the use parameter is
+ * set to 0.
+ */
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
+{
+ int change = use ? 1 : -1;
+ int ret;
+
+ mutex_lock(&entity->parent->graph_mutex);
+
+ /* Apply use count to node. */
+ entity->use_count += change;
+ WARN_ON(entity->use_count < 0);
+
+ /* Apply power change to connected non-nodes. */
+ ret = isp_pipeline_pm_power(entity, change);
+ if (ret < 0)
+ entity->use_count -= change;
+
+ mutex_unlock(&entity->parent->graph_mutex);
+
+ return ret;
+}
+
+/*
+ * isp_pipeline_link_notify - Link management notification callback
+ * @source: Pad at the start of the link
+ * @sink: Pad at the end of the link
+ * @flags: New link flags that will be applied
+ *
+ * React to link management on powered pipelines by updating the use count of
+ * all entities in the source and sink sides of the link. Entities are powered
+ * on or off accordingly.
+ *
+ * Return 0 on success or a negative error code on failure. Powering entities
+ * off is assumed to never fail. This function will not fail for disconnection
+ * events.
+ */
+static int isp_pipeline_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ int source_use = isp_pipeline_pm_use_count(source->entity);
+ int sink_use = isp_pipeline_pm_use_count(sink->entity);
+ int ret;
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ /* Powering off entities is assumed to never fail. */
+ isp_pipeline_pm_power(source->entity, -sink_use);
+ isp_pipeline_pm_power(sink->entity, -source_use);
+ return 0;
+ }
+
+ ret = isp_pipeline_pm_power(source->entity, sink_use);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_pipeline_pm_power(sink->entity, source_use);
+ if (ret < 0)
+ isp_pipeline_pm_power(source->entity, -sink_use);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline stream management
+ */
+
+/*
+ * isp_pipeline_enable - Enable streaming on a pipeline
+ * @pipe: ISP pipeline
+ * @mode: Stream mode (single shot or continuous)
+ *
+ * Walk the entities chain starting at the pipeline output video node and start
+ * all modules in the chain in the given mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise.
+ */
+static int isp_pipeline_enable(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state mode)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ unsigned long flags;
+ int ret;
+
+ /* If the preview engine crashed it might not respond to read/write
+ * operations on the L4 bus. This would result in a bus fault and a
+ * kernel oops. Refuse to start streaming in that case. This check must
+ * be performed before the loop below to avoid starting entities if the
+ * pipeline won't start anyway (those entities would then likely fail to
+ * stop, making the problem worse).
+ */
+ if ((pipe->entities & isp->crashed) &
+ (1U << isp->isp_prev.subdev.entity.id))
+ return -EIO;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ pipe->do_propagation = false;
+
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, mode);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (subdev == &isp->isp_ccdc.subdev) {
+ v4l2_subdev_call(&isp->isp_aewb.subdev, video,
+ s_stream, mode);
+ v4l2_subdev_call(&isp->isp_af.subdev, video,
+ s_stream, mode);
+ v4l2_subdev_call(&isp->isp_hist.subdev, video,
+ s_stream, mode);
+ pipe->do_propagation = true;
+ }
+ }
+
+ return 0;
+}
+
+static int isp_pipeline_wait_resizer(struct isp_device *isp)
+{
+ return omap3isp_resizer_busy(&isp->isp_res);
+}
+
+static int isp_pipeline_wait_preview(struct isp_device *isp)
+{
+ return omap3isp_preview_busy(&isp->isp_prev);
+}
+
+static int isp_pipeline_wait_ccdc(struct isp_device *isp)
+{
+ return omap3isp_stat_busy(&isp->isp_af)
+ || omap3isp_stat_busy(&isp->isp_aewb)
+ || omap3isp_stat_busy(&isp->isp_hist)
+ || omap3isp_ccdc_busy(&isp->isp_ccdc);
+}
+
+#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000)
+
+static int isp_pipeline_wait(struct isp_device *isp,
+ int(*busy)(struct isp_device *isp))
+{
+ unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
+
+ while (!time_after(jiffies, timeout)) {
+ if (!busy(isp))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * isp_pipeline_disable - Disable streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Walk the entities chain starting at the pipeline output video node and stop
+ * all modules in the chain. Wait synchronously for the modules to be stopped if
+ * necessary.
+ *
+ * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
+ * can't be stopped (in which case a software reset of the ISP is probably
+ * necessary).
+ */
+static int isp_pipeline_disable(struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct media_entity *entity;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+ int failure = 0;
+ int ret;
+
+ /*
+ * We need to stop all the modules after CCDC first or they'll
+ * never stop since they may not get a full frame from CCDC.
+ */
+ entity = &pipe->output->video.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ if (subdev == &isp->isp_ccdc.subdev) {
+ v4l2_subdev_call(&isp->isp_aewb.subdev,
+ video, s_stream, 0);
+ v4l2_subdev_call(&isp->isp_af.subdev,
+ video, s_stream, 0);
+ v4l2_subdev_call(&isp->isp_hist.subdev,
+ video, s_stream, 0);
+ }
+
+ v4l2_subdev_call(subdev, video, s_stream, 0);
+
+ if (subdev == &isp->isp_res.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
+ else if (subdev == &isp->isp_prev.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
+ else if (subdev == &isp->isp_ccdc.subdev)
+ ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
+ else
+ ret = 0;
+
+ if (ret) {
+ dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+ /* If the entity failed to stopped, assume it has
+ * crashed. Mark it as such, the ISP will be reset when
+ * applications will release it.
+ */
+ isp->crashed |= 1U << subdev->entity.id;
+ failure = -ETIMEDOUT;
+ }
+ }
+
+ return failure;
+}
+
+/*
+ * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline
+ * @pipe: ISP pipeline
+ * @state: Stream state (stopped, single shot or continuous)
+ *
+ * Set the pipeline to the given stream state. Pipelines can be started in
+ * single-shot or continuous mode.
+ *
+ * Return 0 if successful, or the return value of the failed video::s_stream
+ * operation otherwise. The pipeline state is not updated when the operation
+ * fails, except when stopping the pipeline.
+ */
+int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state state)
+{
+ int ret;
+
+ if (state == ISP_PIPELINE_STREAM_STOPPED)
+ ret = isp_pipeline_disable(pipe);
+ else
+ ret = isp_pipeline_enable(pipe, state);
+
+ if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
+ pipe->stream_state = state;
+
+ return ret;
+}
+
+/*
+ * isp_pipeline_resume - Resume streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Resume video output and input and re-enable pipeline.
+ */
+static void isp_pipeline_resume(struct isp_pipeline *pipe)
+{
+ int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT;
+
+ omap3isp_video_resume(pipe->output, !singleshot);
+ if (singleshot)
+ omap3isp_video_resume(pipe->input, 0);
+ isp_pipeline_enable(pipe, pipe->stream_state);
+}
+
+/*
+ * isp_pipeline_suspend - Suspend streaming on a pipeline
+ * @pipe: ISP pipeline
+ *
+ * Suspend pipeline.
+ */
+static void isp_pipeline_suspend(struct isp_pipeline *pipe)
+{
+ isp_pipeline_disable(pipe);
+}
+
+/*
+ * isp_pipeline_is_last - Verify if entity has an enabled link to the output
+ * video node
+ * @me: ISP module's media entity
+ *
+ * Returns 1 if the entity has an enabled link to the output video node or 0
+ * otherwise. It's true only while pipeline can have no more than one output
+ * node.
+ */
+static int isp_pipeline_is_last(struct media_entity *me)
+{
+ struct isp_pipeline *pipe;
+ struct media_pad *pad;
+
+ if (!me->pipe)
+ return 0;
+ pipe = to_isp_pipeline(me);
+ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+ pad = media_entity_remote_source(&pipe->output->pad);
+ return pad->entity == me;
+}
+
+/*
+ * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module
+ * @me: ISP module's media entity
+ *
+ * Suspend the whole pipeline if module's entity has an enabled link to the
+ * output video node. It works only while pipeline can have no more than one
+ * output node.
+ */
+static void isp_suspend_module_pipeline(struct media_entity *me)
+{
+ if (isp_pipeline_is_last(me))
+ isp_pipeline_suspend(to_isp_pipeline(me));
+}
+
+/*
+ * isp_resume_module_pipeline - Resume pipeline to which belongs the module
+ * @me: ISP module's media entity
+ *
+ * Resume the whole pipeline if module's entity has an enabled link to the
+ * output video node. It works only while pipeline can have no more than one
+ * output node.
+ */
+static void isp_resume_module_pipeline(struct media_entity *me)
+{
+ if (isp_pipeline_is_last(me))
+ isp_pipeline_resume(to_isp_pipeline(me));
+}
+
+/*
+ * isp_suspend_modules - Suspend ISP submodules.
+ * @isp: OMAP3 ISP device
+ *
+ * Returns 0 if suspend left in idle state all the submodules properly,
+ * or returns 1 if a general Reset is required to suspend the submodules.
+ */
+static int isp_suspend_modules(struct isp_device *isp)
+{
+ unsigned long timeout;
+
+ omap3isp_stat_suspend(&isp->isp_aewb);
+ omap3isp_stat_suspend(&isp->isp_af);
+ omap3isp_stat_suspend(&isp->isp_hist);
+ isp_suspend_module_pipeline(&isp->isp_res.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity);
+ isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity);
+
+ timeout = jiffies + ISP_STOP_TIMEOUT;
+ while (omap3isp_stat_busy(&isp->isp_af)
+ || omap3isp_stat_busy(&isp->isp_aewb)
+ || omap3isp_stat_busy(&isp->isp_hist)
+ || omap3isp_preview_busy(&isp->isp_prev)
+ || omap3isp_resizer_busy(&isp->isp_res)
+ || omap3isp_ccdc_busy(&isp->isp_ccdc)) {
+ if (time_after(jiffies, timeout)) {
+ dev_info(isp->dev, "can't stop modules.\n");
+ return 1;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+/*
+ * isp_resume_modules - Resume ISP submodules.
+ * @isp: OMAP3 ISP device
+ */
+static void isp_resume_modules(struct isp_device *isp)
+{
+ omap3isp_stat_resume(&isp->isp_aewb);
+ omap3isp_stat_resume(&isp->isp_af);
+ omap3isp_stat_resume(&isp->isp_hist);
+ isp_resume_module_pipeline(&isp->isp_res.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_prev.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity);
+ isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity);
+}
+
+/*
+ * isp_reset - Reset ISP with a timeout wait for idle.
+ * @isp: OMAP3 ISP device
+ */
+static int isp_reset(struct isp_device *isp)
+{
+ unsigned long timeout = 0;
+
+ isp_reg_writel(isp,
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
+ | ISP_SYSCONFIG_SOFTRESET,
+ OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
+ while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN,
+ ISP_SYSSTATUS) & 0x1)) {
+ if (timeout++ > 10000) {
+ dev_alert(isp->dev, "cannot reset ISP\n");
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ isp->crashed = 0;
+ return 0;
+}
+
+/*
+ * isp_save_context - Saves the values of the ISP module registers.
+ * @isp: OMAP3 ISP device
+ * @reg_list: Structure containing pairs of register address and value to
+ * modify on OMAP.
+ */
+static void
+isp_save_context(struct isp_device *isp, struct isp_reg *reg_list)
+{
+ struct isp_reg *next = reg_list;
+
+ for (; next->reg != ISP_TOK_TERM; next++)
+ next->val = isp_reg_readl(isp, next->mmio_range, next->reg);
+}
+
+/*
+ * isp_restore_context - Restores the values of the ISP module registers.
+ * @isp: OMAP3 ISP device
+ * @reg_list: Structure containing pairs of register address and value to
+ * modify on OMAP.
+ */
+static void
+isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
+{
+ struct isp_reg *next = reg_list;
+
+ for (; next->reg != ISP_TOK_TERM; next++)
+ isp_reg_writel(isp, next->val, next->mmio_range, next->reg);
+}
+
+/*
+ * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+ * @isp: OMAP3 ISP device
+ *
+ * Routine for saving the context of each module in the ISP.
+ * CCDC, HIST, H3A, PREV, RESZ and MMU.
+ */
+static void isp_save_ctx(struct isp_device *isp)
+{
+ isp_save_context(isp, isp_reg_list);
+ omap_iommu_save_ctx(isp->dev);
+}
+
+/*
+ * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
+ * @isp: OMAP3 ISP device
+ *
+ * Routine for restoring the context of each module in the ISP.
+ * CCDC, HIST, H3A, PREV, RESZ and MMU.
+ */
+static void isp_restore_ctx(struct isp_device *isp)
+{
+ isp_restore_context(isp, isp_reg_list);
+ omap_iommu_restore_ctx(isp->dev);
+ omap3isp_ccdc_restore_context(isp);
+ omap3isp_preview_restore_context(isp);
+}
+
+/* -----------------------------------------------------------------------------
+ * SBL resources management
+ */
+#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \
+ OMAP3_ISP_SBL_CCDC_LSC_READ | \
+ OMAP3_ISP_SBL_PREVIEW_READ | \
+ OMAP3_ISP_SBL_RESIZER_READ)
+#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \
+ OMAP3_ISP_SBL_CSI2A_WRITE | \
+ OMAP3_ISP_SBL_CSI2C_WRITE | \
+ OMAP3_ISP_SBL_CCDC_WRITE | \
+ OMAP3_ISP_SBL_PREVIEW_WRITE)
+
+void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res)
+{
+ u32 sbl = 0;
+
+ isp->sbl_resources |= res;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)
+ sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)
+ sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)
+ sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)
+ sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE)
+ sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+
+ if (isp->sbl_resources & OMAP3_ISP_SBL_READ)
+ sbl |= ISPCTRL_SBL_RD_RAM_EN;
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+}
+
+void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res)
+{
+ u32 sbl = 0;
+
+ isp->sbl_resources &= ~res;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ))
+ sbl |= ISPCTRL_SBL_SHARED_RPORTA;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ))
+ sbl |= ISPCTRL_SBL_SHARED_RPORTB;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE))
+ sbl |= ISPCTRL_SBL_SHARED_WPORTC;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE))
+ sbl |= ISPCTRL_SBL_WR0_RAM_EN;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE))
+ sbl |= ISPCTRL_SBL_WR1_RAM_EN;
+
+ if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ))
+ sbl |= ISPCTRL_SBL_RD_RAM_EN;
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
+}
+
+/*
+ * isp_module_sync_idle - Helper to sync module with its idle state
+ * @me: ISP submodule's media entity
+ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISP submodule needs to wait for next interrupt. If
+ * yes, makes the caller to sleep while waiting for such event.
+ */
+int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(me);
+
+ if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED ||
+ (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+ !isp_pipeline_ready(pipe)))
+ return 0;
+
+ /*
+ * atomic_set() doesn't include memory barrier on ARM platform for SMP
+ * scenario. We'll call it here to avoid race conditions.
+ */
+ atomic_set(stopping, 1);
+ smp_mb();
+
+ /*
+ * If module is the last one, it's writing to memory. In this case,
+ * it's necessary to check if the module is already paused due to
+ * DMA queue underrun or if it has to wait for next interrupt to be
+ * idle.
+ * If it isn't the last one, the function won't sleep but *stopping
+ * will still be set to warn next submodule caller's interrupt the
+ * module wants to be idle.
+ */
+ if (isp_pipeline_is_last(me)) {
+ struct isp_video *video = pipe->output;
+ unsigned long flags;
+ spin_lock_irqsave(&video->queue->irqlock, flags);
+ if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ atomic_set(stopping, 0);
+ smp_mb();
+ return 0;
+ }
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ if (!wait_event_timeout(*wait, !atomic_read(stopping),
+ msecs_to_jiffies(1000))) {
+ atomic_set(stopping, 0);
+ smp_mb();
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
+ * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
+ * @stopping: flag which tells module wants to stop
+ *
+ * This function checks if ISP submodule was stopping. In case of yes, it
+ * notices the caller by setting stopping to 0 and waking up the wait queue.
+ * Returns 1 if it was stopping or 0 otherwise.
+ */
+int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping)
+{
+ if (atomic_cmpxchg(stopping, 1, 0)) {
+ wake_up(wait);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Clock management
+ */
+
+#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \
+ ISPCTRL_HIST_CLK_EN | \
+ ISPCTRL_RSZ_CLK_EN | \
+ (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \
+ (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN))
+
+static void __isp_subclk_update(struct isp_device *isp)
+{
+ u32 clk = 0;
+
+ /* AEWB and AF share the same clock. */
+ if (isp->subclk_resources &
+ (OMAP3_ISP_SUBCLK_AEWB | OMAP3_ISP_SUBCLK_AF))
+ clk |= ISPCTRL_H3A_CLK_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST)
+ clk |= ISPCTRL_HIST_CLK_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER)
+ clk |= ISPCTRL_RSZ_CLK_EN;
+
+ /* NOTE: For CCDC & Preview submodules, we need to affect internal
+ * RAM as well.
+ */
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC)
+ clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN;
+
+ if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW)
+ clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN;
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
+ ISPCTRL_CLKS_MASK, clk);
+}
+
+void omap3isp_subclk_enable(struct isp_device *isp,
+ enum isp_subclk_resource res)
+{
+ isp->subclk_resources |= res;
+
+ __isp_subclk_update(isp);
+}
+
+void omap3isp_subclk_disable(struct isp_device *isp,
+ enum isp_subclk_resource res)
+{
+ isp->subclk_resources &= ~res;
+
+ __isp_subclk_update(isp);
+}
+
+/*
+ * isp_enable_clocks - Enable ISP clocks
+ * @isp: OMAP3 ISP device
+ *
+ * Return 0 if successful, or clk_enable return value if any of tthem fails.
+ */
+static int isp_enable_clocks(struct isp_device *isp)
+{
+ int r;
+ unsigned long rate;
+ int divisor;
+
+ /*
+ * cam_mclk clock chain:
+ * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
+ *
+ * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
+ * set to the same value. Hence the rate set for dpll4_m5
+ * has to be twice of what is set on OMAP3430 to get
+ * the required value for cam_mclk
+ */
+ if (cpu_is_omap3630())
+ divisor = 1;
+ else
+ divisor = 2;
+
+ r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable cam_ick failed\n");
+ goto out_clk_enable_ick;
+ }
+ r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
+ CM_CAM_MCLK_HZ/divisor);
+ if (r) {
+ dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
+ goto out_clk_enable_mclk;
+ }
+ r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable cam_mclk failed\n");
+ goto out_clk_enable_mclk;
+ }
+ rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
+ if (rate != CM_CAM_MCLK_HZ)
+ dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
+ " expected : %d\n"
+ " actual : %ld\n", CM_CAM_MCLK_HZ, rate);
+ r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
+ if (r) {
+ dev_err(isp->dev, "clk_enable csi2_fck failed\n");
+ goto out_clk_enable_csi2_fclk;
+ }
+ return 0;
+
+out_clk_enable_csi2_fclk:
+ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+out_clk_enable_mclk:
+ clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+out_clk_enable_ick:
+ return r;
+}
+
+/*
+ * isp_disable_clocks - Disable ISP clocks
+ * @isp: OMAP3 ISP device
+ */
+static void isp_disable_clocks(struct isp_device *isp)
+{
+ clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
+ clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
+ clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
+}
+
+static const char *isp_clocks[] = {
+ "cam_ick",
+ "cam_mclk",
+ "dpll4_m5_ck",
+ "csi2_96m_fck",
+ "l3_ick",
+};
+
+static void isp_put_clocks(struct isp_device *isp)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+ if (isp->clock[i]) {
+ clk_put(isp->clock[i]);
+ isp->clock[i] = NULL;
+ }
+ }
+}
+
+static int isp_get_clocks(struct isp_device *isp)
+{
+ struct clk *clk;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
+ clk = clk_get(isp->dev, isp_clocks[i]);
+ if (IS_ERR(clk)) {
+ dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
+ isp_put_clocks(isp);
+ return PTR_ERR(clk);
+ }
+
+ isp->clock[i] = clk;
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_get - Acquire the ISP resource.
+ *
+ * Initializes the clocks for the first acquire.
+ *
+ * Increment the reference count on the ISP. If the first reference is taken,
+ * enable clocks and power-up all submodules.
+ *
+ * Return a pointer to the ISP device structure, or NULL if an error occurred.
+ */
+static struct isp_device *__omap3isp_get(struct isp_device *isp, bool irq)
+{
+ struct isp_device *__isp = isp;
+
+ if (isp == NULL)
+ return NULL;
+
+ mutex_lock(&isp->isp_mutex);
+ if (isp->ref_count > 0)
+ goto out;
+
+ if (isp_enable_clocks(isp) < 0) {
+ __isp = NULL;
+ goto out;
+ }
+
+ /* We don't want to restore context before saving it! */
+ if (isp->has_context)
+ isp_restore_ctx(isp);
+
+ if (irq)
+ isp_enable_interrupts(isp);
+
+out:
+ if (__isp != NULL)
+ isp->ref_count++;
+ mutex_unlock(&isp->isp_mutex);
+
+ return __isp;
+}
+
+struct isp_device *omap3isp_get(struct isp_device *isp)
+{
+ return __omap3isp_get(isp, true);
+}
+
+/*
+ * omap3isp_put - Release the ISP
+ *
+ * Decrement the reference count on the ISP. If the last reference is released,
+ * power-down all submodules, disable clocks and free temporary buffers.
+ */
+void omap3isp_put(struct isp_device *isp)
+{
+ if (isp == NULL)
+ return;
+
+ mutex_lock(&isp->isp_mutex);
+ BUG_ON(isp->ref_count == 0);
+ if (--isp->ref_count == 0) {
+ isp_disable_interrupts(isp);
+ if (isp->domain) {
+ isp_save_ctx(isp);
+ isp->has_context = 1;
+ }
+ /* Reset the ISP if an entity has failed to stop. This is the
+ * only way to recover from such conditions.
+ */
+ if (isp->crashed)
+ isp_reset(isp);
+ isp_disable_clocks(isp);
+ }
+ mutex_unlock(&isp->isp_mutex);
+}
+
+/* --------------------------------------------------------------------------
+ * Platform device driver
+ */
+
+/*
+ * omap3isp_print_status - Prints the values of the ISP Control Module registers
+ * @isp: OMAP3 ISP device
+ */
+#define ISP_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
+#define SBL_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
+
+void omap3isp_print_status(struct isp_device *isp)
+{
+ dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
+
+ ISP_PRINT_REGISTER(isp, SYSCONFIG);
+ ISP_PRINT_REGISTER(isp, SYSSTATUS);
+ ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
+ ISP_PRINT_REGISTER(isp, IRQ0STATUS);
+ ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
+ ISP_PRINT_REGISTER(isp, CTRL);
+ ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
+ ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
+ ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
+ ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
+
+ SBL_PRINT_REGISTER(isp, PCR);
+ SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * Power management support.
+ *
+ * As the ISP can't properly handle an input video stream interruption on a non
+ * frame boundary, the ISP pipelines need to be stopped before sensors get
+ * suspended. However, as suspending the sensors can require a running clock,
+ * which can be provided by the ISP, the ISP can't be completely suspended
+ * before the sensor.
+ *
+ * To solve this problem power management support is split into prepare/complete
+ * and suspend/resume operations. The pipelines are stopped in prepare() and the
+ * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
+ * resume(), and the the pipelines are restarted in complete().
+ *
+ * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
+ * yet.
+ */
+static int isp_pm_prepare(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+ int reset;
+
+ WARN_ON(mutex_is_locked(&isp->isp_mutex));
+
+ if (isp->ref_count == 0)
+ return 0;
+
+ reset = isp_suspend_modules(isp);
+ isp_disable_interrupts(isp);
+ isp_save_ctx(isp);
+ if (reset)
+ isp_reset(isp);
+
+ return 0;
+}
+
+static int isp_pm_suspend(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&isp->isp_mutex));
+
+ if (isp->ref_count)
+ isp_disable_clocks(isp);
+
+ return 0;
+}
+
+static int isp_pm_resume(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ if (isp->ref_count == 0)
+ return 0;
+
+ return isp_enable_clocks(isp);
+}
+
+static void isp_pm_complete(struct device *dev)
+{
+ struct isp_device *isp = dev_get_drvdata(dev);
+
+ if (isp->ref_count == 0)
+ return;
+
+ isp_restore_ctx(isp);
+ isp_enable_interrupts(isp);
+ isp_resume_modules(isp);
+}
+
+#else
+
+#define isp_pm_prepare NULL
+#define isp_pm_suspend NULL
+#define isp_pm_resume NULL
+#define isp_pm_complete NULL
+
+#endif /* CONFIG_PM */
+
+static void isp_unregister_entities(struct isp_device *isp)
+{
+ omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
+ omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
+ omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
+ omap3isp_preview_unregister_entities(&isp->isp_prev);
+ omap3isp_resizer_unregister_entities(&isp->isp_res);
+ omap3isp_stat_unregister_entities(&isp->isp_aewb);
+ omap3isp_stat_unregister_entities(&isp->isp_af);
+ omap3isp_stat_unregister_entities(&isp->isp_hist);
+
+ v4l2_device_unregister(&isp->v4l2_dev);
+ media_device_unregister(&isp->media_dev);
+}
+
+/*
+ * isp_register_subdev_group - Register a group of subdevices
+ * @isp: OMAP3 ISP device
+ * @board_info: I2C subdevs board information array
+ *
+ * Register all I2C subdevices in the board_info array. The array must be
+ * terminated by a NULL entry, and the first entry must be the sensor.
+ *
+ * Return a pointer to the sensor media entity if it has been successfully
+ * registered, or NULL otherwise.
+ */
+static struct v4l2_subdev *
+isp_register_subdev_group(struct isp_device *isp,
+ struct isp_subdev_i2c_board_info *board_info)
+{
+ struct v4l2_subdev *sensor = NULL;
+ unsigned int first;
+
+ if (board_info->board_info == NULL)
+ return NULL;
+
+ for (first = 1; board_info->board_info; ++board_info, first = 0) {
+ struct v4l2_subdev *subdev;
+ struct i2c_adapter *adapter;
+
+ adapter = i2c_get_adapter(board_info->i2c_adapter_id);
+ if (adapter == NULL) {
+ printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
+ "device %s\n", __func__,
+ board_info->i2c_adapter_id,
+ board_info->board_info->type);
+ continue;
+ }
+
+ subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
+ board_info->board_info, NULL);
+ if (subdev == NULL) {
+ printk(KERN_ERR "%s: Unable to register subdev %s\n",
+ __func__, board_info->board_info->type);
+ continue;
+ }
+
+ if (first)
+ sensor = subdev;
+ }
+
+ return sensor;
+}
+
+static int isp_register_entities(struct isp_device *isp)
+{
+ struct isp_platform_data *pdata = isp->pdata;
+ struct isp_v4l2_subdevs_group *subdevs;
+ int ret;
+
+ isp->media_dev.dev = isp->dev;
+ strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
+ sizeof(isp->media_dev.model));
+ isp->media_dev.hw_revision = isp->revision;
+ isp->media_dev.link_notify = isp_pipeline_link_notify;
+ ret = media_device_register(&isp->media_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: Media device registration failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ isp->v4l2_dev.mdev = &isp->media_dev;
+ ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
+ __func__, ret);
+ goto done;
+ }
+
+ /* Register internal entities */
+ ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_preview_register_entities(&isp->isp_prev,
+ &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev);
+ if (ret < 0)
+ goto done;
+
+ /* Register external entities */
+ for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
+ struct v4l2_subdev *sensor;
+ struct media_entity *input;
+ unsigned int flags;
+ unsigned int pad;
+
+ sensor = isp_register_subdev_group(isp, subdevs->subdevs);
+ if (sensor == NULL)
+ continue;
+
+ sensor->host_priv = subdevs;
+
+ /* Connect the sensor to the correct interface module. Parallel
+ * sensors are connected directly to the CCDC, while serial
+ * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
+ * through CSIPHY1 or CSIPHY2.
+ */
+ switch (subdevs->interface) {
+ case ISP_INTERFACE_PARALLEL:
+ input = &isp->isp_ccdc.subdev.entity;
+ pad = CCDC_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2A_PHY2:
+ input = &isp->isp_csi2a.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ case ISP_INTERFACE_CCP2B_PHY1:
+ case ISP_INTERFACE_CCP2B_PHY2:
+ input = &isp->isp_ccp2.subdev.entity;
+ pad = CCP2_PAD_SINK;
+ flags = 0;
+ break;
+
+ case ISP_INTERFACE_CSI2C_PHY1:
+ input = &isp->isp_csi2c.subdev.entity;
+ pad = CSI2_PAD_SINK;
+ flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: invalid interface type %u\n",
+ __func__, subdevs->interface);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = media_entity_create_link(&sensor->entity, 0, input, pad,
+ flags);
+ if (ret < 0)
+ goto done;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+
+done:
+ if (ret < 0)
+ isp_unregister_entities(isp);
+
+ return ret;
+}
+
+static void isp_cleanup_modules(struct isp_device *isp)
+{
+ omap3isp_h3a_aewb_cleanup(isp);
+ omap3isp_h3a_af_cleanup(isp);
+ omap3isp_hist_cleanup(isp);
+ omap3isp_resizer_cleanup(isp);
+ omap3isp_preview_cleanup(isp);
+ omap3isp_ccdc_cleanup(isp);
+ omap3isp_ccp2_cleanup(isp);
+ omap3isp_csi2_cleanup(isp);
+}
+
+static int isp_initialize_modules(struct isp_device *isp)
+{
+ int ret;
+
+ ret = omap3isp_csiphy_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CSI PHY initialization failed\n");
+ goto error_csiphy;
+ }
+
+ ret = omap3isp_csi2_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CSI2 initialization failed\n");
+ goto error_csi2;
+ }
+
+ ret = omap3isp_ccp2_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CCP2 initialization failed\n");
+ goto error_ccp2;
+ }
+
+ ret = omap3isp_ccdc_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "CCDC initialization failed\n");
+ goto error_ccdc;
+ }
+
+ ret = omap3isp_preview_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Preview initialization failed\n");
+ goto error_preview;
+ }
+
+ ret = omap3isp_resizer_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Resizer initialization failed\n");
+ goto error_resizer;
+ }
+
+ ret = omap3isp_hist_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "Histogram initialization failed\n");
+ goto error_hist;
+ }
+
+ ret = omap3isp_h3a_aewb_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "H3A AEWB initialization failed\n");
+ goto error_h3a_aewb;
+ }
+
+ ret = omap3isp_h3a_af_init(isp);
+ if (ret < 0) {
+ dev_err(isp->dev, "H3A AF initialization failed\n");
+ goto error_h3a_af;
+ }
+
+ /* Connect the submodules. */
+ ret = media_entity_create_link(
+ &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
+ &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_aewb.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_af.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(
+ &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
+ &isp->isp_hist.subdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_h3a_af_cleanup(isp);
+error_h3a_af:
+ omap3isp_h3a_aewb_cleanup(isp);
+error_h3a_aewb:
+ omap3isp_hist_cleanup(isp);
+error_hist:
+ omap3isp_resizer_cleanup(isp);
+error_resizer:
+ omap3isp_preview_cleanup(isp);
+error_preview:
+ omap3isp_ccdc_cleanup(isp);
+error_ccdc:
+ omap3isp_ccp2_cleanup(isp);
+error_ccp2:
+ omap3isp_csi2_cleanup(isp);
+error_csi2:
+error_csiphy:
+ return ret;
+}
+
+/*
+ * isp_remove - Remove ISP platform device
+ * @pdev: Pointer to ISP platform device
+ *
+ * Always returns 0.
+ */
+static int __devexit isp_remove(struct platform_device *pdev)
+{
+ struct isp_device *isp = platform_get_drvdata(pdev);
+ int i;
+
+ isp_unregister_entities(isp);
+ isp_cleanup_modules(isp);
+
+ __omap3isp_get(isp, false);
+ iommu_detach_device(isp->domain, &pdev->dev);
+ iommu_domain_free(isp->domain);
+ isp->domain = NULL;
+ omap3isp_put(isp);
+
+ free_irq(isp->irq_num, isp);
+ isp_put_clocks(isp);
+
+ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp->mmio_base[i]) {
+ iounmap(isp->mmio_base[i]);
+ isp->mmio_base[i] = NULL;
+ }
+
+ if (isp->mmio_base_phys[i]) {
+ release_mem_region(isp->mmio_base_phys[i],
+ isp->mmio_size[i]);
+ isp->mmio_base_phys[i] = 0;
+ }
+ }
+
+ regulator_put(isp->isp_csiphy1.vdd);
+ regulator_put(isp->isp_csiphy2.vdd);
+ kfree(isp);
+
+ return 0;
+}
+
+static int isp_map_mem_resource(struct platform_device *pdev,
+ struct isp_device *isp,
+ enum isp_mem_resources res)
+{
+ struct resource *mem;
+
+ /* request the mem region for the camera registers */
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
+ if (!mem) {
+ dev_err(isp->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
+ dev_err(isp->dev,
+ "cannot reserve camera register I/O region\n");
+ return -ENODEV;
+ }
+ isp->mmio_base_phys[res] = mem->start;
+ isp->mmio_size[res] = resource_size(mem);
+
+ /* map the region */
+ isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
+ isp->mmio_size[res]);
+ if (!isp->mmio_base[res]) {
+ dev_err(isp->dev, "cannot map camera register I/O region\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * isp_probe - Probe ISP platform device
+ * @pdev: Pointer to ISP platform device
+ *
+ * Returns 0 if successful,
+ * -ENOMEM if no memory available,
+ * -ENODEV if no platform device resources found
+ * or no space for remapping registers,
+ * -EINVAL if couldn't install ISR,
+ * or clk_get return error value.
+ */
+static int __devinit isp_probe(struct platform_device *pdev)
+{
+ struct isp_platform_data *pdata = pdev->dev.platform_data;
+ struct isp_device *isp;
+ int ret;
+ int i, m;
+
+ if (pdata == NULL)
+ return -EINVAL;
+
+ isp = kzalloc(sizeof(*isp), GFP_KERNEL);
+ if (!isp) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ return -ENOMEM;
+ }
+
+ isp->autoidle = autoidle;
+ isp->platform_cb.set_xclk = isp_set_xclk;
+
+ mutex_init(&isp->isp_mutex);
+ spin_lock_init(&isp->stat_lock);
+
+ isp->dev = &pdev->dev;
+ isp->pdata = pdata;
+ isp->ref_count = 0;
+
+ isp->raw_dmamask = DMA_BIT_MASK(32);
+ isp->dev->dma_mask = &isp->raw_dmamask;
+ isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, isp);
+
+ /* Regulators */
+ isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
+ isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
+
+ /* Clocks */
+ ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
+ if (ret < 0)
+ goto error;
+
+ ret = isp_get_clocks(isp);
+ if (ret < 0)
+ goto error;
+
+ if (__omap3isp_get(isp, false) == NULL) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ ret = isp_reset(isp);
+ if (ret < 0)
+ goto error_isp;
+
+ /* Memory resources */
+ isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
+ dev_info(isp->dev, "Revision %d.%d found\n",
+ (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
+
+ for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
+ if (isp->revision == isp_res_maps[m].isp_rev)
+ break;
+
+ if (m == ARRAY_SIZE(isp_res_maps)) {
+ dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n",
+ (isp->revision & 0xf0) >> 4, isp->revision & 0xf);
+ ret = -ENODEV;
+ goto error_isp;
+ }
+
+ for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp_res_maps[m].map & 1 << i) {
+ ret = isp_map_mem_resource(pdev, isp, i);
+ if (ret)
+ goto error_isp;
+ }
+ }
+
+ isp->domain = iommu_domain_alloc(pdev->dev.bus);
+ if (!isp->domain) {
+ dev_err(isp->dev, "can't alloc iommu domain\n");
+ ret = -ENOMEM;
+ goto error_isp;
+ }
+
+ ret = iommu_attach_device(isp->domain, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
+ /* Interrupt */
+ isp->irq_num = platform_get_irq(pdev, 0);
+ if (isp->irq_num <= 0) {
+ dev_err(isp->dev, "No IRQ resource\n");
+ ret = -ENODEV;
+ goto detach_dev;
+ }
+
+ if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
+ dev_err(isp->dev, "Unable to request IRQ\n");
+ ret = -EINVAL;
+ goto detach_dev;
+ }
+
+ /* Entities */
+ ret = isp_initialize_modules(isp);
+ if (ret < 0)
+ goto error_irq;
+
+ ret = isp_register_entities(isp);
+ if (ret < 0)
+ goto error_modules;
+
+ isp_core_init(isp, 1);
+ omap3isp_put(isp);
+
+ return 0;
+
+error_modules:
+ isp_cleanup_modules(isp);
+error_irq:
+ free_irq(isp->irq_num, isp);
+detach_dev:
+ iommu_detach_device(isp->domain, &pdev->dev);
+free_domain:
+ iommu_domain_free(isp->domain);
+error_isp:
+ omap3isp_put(isp);
+error:
+ isp_put_clocks(isp);
+
+ for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
+ if (isp->mmio_base[i]) {
+ iounmap(isp->mmio_base[i]);
+ isp->mmio_base[i] = NULL;
+ }
+
+ if (isp->mmio_base_phys[i]) {
+ release_mem_region(isp->mmio_base_phys[i],
+ isp->mmio_size[i]);
+ isp->mmio_base_phys[i] = 0;
+ }
+ }
+ regulator_put(isp->isp_csiphy2.vdd);
+ regulator_put(isp->isp_csiphy1.vdd);
+ platform_set_drvdata(pdev, NULL);
+
+ mutex_destroy(&isp->isp_mutex);
+ kfree(isp);
+
+ return ret;
+}
+
+static const struct dev_pm_ops omap3isp_pm_ops = {
+ .prepare = isp_pm_prepare,
+ .suspend = isp_pm_suspend,
+ .resume = isp_pm_resume,
+ .complete = isp_pm_complete,
+};
+
+static struct platform_device_id omap3isp_id_table[] = {
+ { "omap3isp", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
+
+static struct platform_driver omap3isp_driver = {
+ .probe = isp_probe,
+ .remove = __devexit_p(isp_remove),
+ .id_table = omap3isp_id_table,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap3isp",
+ .pm = &omap3isp_pm_ops,
+ },
+};
+
+module_platform_driver(omap3isp_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("TI OMAP3 ISP driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
new file mode 100644
index 000000000000..8be7487c326f
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -0,0 +1,352 @@
+/*
+ * isp.h
+ *
+ * TI OMAP3 ISP - Core
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CORE_H
+#define OMAP3_ISP_CORE_H
+
+#include <media/omap3isp.h>
+#include <media/v4l2-device.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/iommu.h>
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+
+#include "ispstat.h"
+#include "ispccdc.h"
+#include "ispreg.h"
+#include "ispresizer.h"
+#include "isppreview.h"
+#include "ispcsiphy.h"
+#include "ispcsi2.h"
+#include "ispccp2.h"
+
+#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
+
+#define ISP_TOK_TERM 0xFFFFFFFF /*
+ * terminating token for ISP
+ * modules reg list
+ */
+#define to_isp_device(ptr_module) \
+ container_of(ptr_module, struct isp_device, isp_##ptr_module)
+#define to_device(ptr_module) \
+ (to_isp_device(ptr_module)->dev)
+
+enum isp_mem_resources {
+ OMAP3_ISP_IOMEM_MAIN,
+ OMAP3_ISP_IOMEM_CCP2,
+ OMAP3_ISP_IOMEM_CCDC,
+ OMAP3_ISP_IOMEM_HIST,
+ OMAP3_ISP_IOMEM_H3A,
+ OMAP3_ISP_IOMEM_PREV,
+ OMAP3_ISP_IOMEM_RESZ,
+ OMAP3_ISP_IOMEM_SBL,
+ OMAP3_ISP_IOMEM_CSI2A_REGS1,
+ OMAP3_ISP_IOMEM_CSIPHY2,
+ OMAP3_ISP_IOMEM_CSI2A_REGS2,
+ OMAP3_ISP_IOMEM_CSI2C_REGS1,
+ OMAP3_ISP_IOMEM_CSIPHY1,
+ OMAP3_ISP_IOMEM_CSI2C_REGS2,
+ OMAP3_ISP_IOMEM_LAST
+};
+
+enum isp_sbl_resource {
+ OMAP3_ISP_SBL_CSI1_READ = 0x1,
+ OMAP3_ISP_SBL_CSI1_WRITE = 0x2,
+ OMAP3_ISP_SBL_CSI2A_WRITE = 0x4,
+ OMAP3_ISP_SBL_CSI2C_WRITE = 0x8,
+ OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10,
+ OMAP3_ISP_SBL_CCDC_WRITE = 0x20,
+ OMAP3_ISP_SBL_PREVIEW_READ = 0x40,
+ OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80,
+ OMAP3_ISP_SBL_RESIZER_READ = 0x100,
+ OMAP3_ISP_SBL_RESIZER_WRITE = 0x200,
+};
+
+enum isp_subclk_resource {
+ OMAP3_ISP_SUBCLK_CCDC = (1 << 0),
+ OMAP3_ISP_SUBCLK_AEWB = (1 << 1),
+ OMAP3_ISP_SUBCLK_AF = (1 << 2),
+ OMAP3_ISP_SUBCLK_HIST = (1 << 3),
+ OMAP3_ISP_SUBCLK_PREVIEW = (1 << 4),
+ OMAP3_ISP_SUBCLK_RESIZER = (1 << 5),
+};
+
+/* ISP: OMAP 34xx ES 1.0 */
+#define ISP_REVISION_1_0 0x10
+/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */
+#define ISP_REVISION_2_0 0x20
+/* ISP2P: OMAP 36xx */
+#define ISP_REVISION_15_0 0xF0
+
+/*
+ * struct isp_res_mapping - Map ISP io resources to ISP revision.
+ * @isp_rev: ISP_REVISION_x_x
+ * @map: bitmap for enum isp_mem_resources
+ */
+struct isp_res_mapping {
+ u32 isp_rev;
+ u32 map;
+};
+
+/*
+ * struct isp_reg - Structure for ISP register values.
+ * @reg: 32-bit Register address.
+ * @val: 32-bit Register value.
+ */
+struct isp_reg {
+ enum isp_mem_resources mmio_range;
+ u32 reg;
+ u32 val;
+};
+
+struct isp_platform_callback {
+ u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
+ int (*csiphy_config)(struct isp_csiphy *phy,
+ struct isp_csiphy_dphy_cfg *dphy,
+ struct isp_csiphy_lanes_cfg *lanes);
+};
+
+/*
+ * struct isp_device - ISP device structure.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @revision: Stores current ISP module revision.
+ * @irq_num: Currently used IRQ number.
+ * @mmio_base: Array with kernel base addresses for ioremapped ISP register
+ * regions.
+ * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
+ * regions.
+ * @mmio_size: Array with ISP register regions size in bytes.
+ * @raw_dmamask: Raw DMA mask
+ * @stat_lock: Spinlock for handling statistics
+ * @isp_mutex: Mutex for serializing requests to ISP.
+ * @crashed: Bitmask of crashed entities (indexed by entity ID)
+ * @has_context: Context has been saved at least once and can be restored.
+ * @ref_count: Reference count for handling multiple ISP requests.
+ * @cam_ick: Pointer to camera interface clock structure.
+ * @cam_mclk: Pointer to camera functional clock structure.
+ * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure.
+ * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
+ * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
+ * @irq: Currently attached ISP ISR callbacks information structure.
+ * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
+ * @isp_hist: Pointer to current settings for ISP Histogram SCM.
+ * @isp_h3a: Pointer to current settings for ISP Auto Exposure and
+ * White Balance SCM.
+ * @isp_res: Pointer to current settings for ISP Resizer.
+ * @isp_prev: Pointer to current settings for ISP Preview.
+ * @isp_ccdc: Pointer to current settings for ISP CCDC.
+ * @iommu: Pointer to requested IOMMU instance for ISP.
+ * @platform_cb: ISP driver callback function pointers for platform code
+ *
+ * This structure is used to store the OMAP ISP Information.
+ */
+struct isp_device {
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct device *dev;
+ u32 revision;
+
+ /* platform HW resources */
+ struct isp_platform_data *pdata;
+ unsigned int irq_num;
+
+ void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
+ unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
+ resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST];
+
+ u64 raw_dmamask;
+
+ /* ISP Obj */
+ spinlock_t stat_lock; /* common lock for statistic drivers */
+ struct mutex isp_mutex; /* For handling ref_count field */
+ u32 crashed;
+ int has_context;
+ int ref_count;
+ unsigned int autoidle;
+ u32 xclk_divisor[2]; /* Two clocks, a and b. */
+#define ISP_CLK_CAM_ICK 0
+#define ISP_CLK_CAM_MCLK 1
+#define ISP_CLK_DPLL4_M5_CK 2
+#define ISP_CLK_CSI2_FCK 3
+#define ISP_CLK_L3_ICK 4
+ struct clk *clock[5];
+
+ /* ISP modules */
+ struct ispstat isp_af;
+ struct ispstat isp_aewb;
+ struct ispstat isp_hist;
+ struct isp_res_device isp_res;
+ struct isp_prev_device isp_prev;
+ struct isp_ccdc_device isp_ccdc;
+ struct isp_csi2_device isp_csi2a;
+ struct isp_csi2_device isp_csi2c;
+ struct isp_ccp2_device isp_ccp2;
+ struct isp_csiphy isp_csiphy1;
+ struct isp_csiphy isp_csiphy2;
+
+ unsigned int sbl_resources;
+ unsigned int subclk_resources;
+
+ struct iommu_domain *domain;
+
+ struct isp_platform_callback platform_cb;
+};
+
+#define v4l2_dev_to_isp_device(dev) \
+ container_of(dev, struct isp_device, v4l2_dev)
+
+void omap3isp_hist_dma_done(struct isp_device *isp);
+
+void omap3isp_flush(struct isp_device *isp);
+
+int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
+ atomic_t *stopping);
+
+int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
+ enum isp_pipeline_stream_state state);
+void omap3isp_configure_bridge(struct isp_device *isp,
+ enum ccdc_input_entity input,
+ const struct isp_parallel_platform_data *pdata,
+ unsigned int shift, unsigned int bridge);
+
+struct isp_device *omap3isp_get(struct isp_device *isp);
+void omap3isp_put(struct isp_device *isp);
+
+void omap3isp_print_status(struct isp_device *isp);
+
+void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res);
+void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res);
+
+void omap3isp_subclk_enable(struct isp_device *isp,
+ enum isp_subclk_resource res);
+void omap3isp_subclk_disable(struct isp_device *isp,
+ enum isp_subclk_resource res);
+
+int omap3isp_pipeline_pm_use(struct media_entity *entity, int use);
+
+int omap3isp_register_entities(struct platform_device *pdev,
+ struct v4l2_device *v4l2_dev);
+void omap3isp_unregister_entities(struct platform_device *pdev);
+
+/*
+ * isp_reg_readl - Read value of an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @isp_mmio_range: Range to which the register offset refers to.
+ * @reg_offset: Register offset to read from.
+ *
+ * Returns an unsigned 32 bit value with the required register contents.
+ */
+static inline
+u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,
+ u32 reg_offset)
+{
+ return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset);
+}
+
+/*
+ * isp_reg_writel - Write value to an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @reg_value: 32 bit value to write to the register.
+ * @isp_mmio_range: Range to which the register offset refers to.
+ * @reg_offset: Register offset to write into.
+ */
+static inline
+void isp_reg_writel(struct isp_device *isp, u32 reg_value,
+ enum isp_mem_resources isp_mmio_range, u32 reg_offset)
+{
+ __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset);
+}
+
+/*
+ * isp_reg_and - Clear individual bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @clr_bits: 32 bit value which would be cleared in the register.
+ */
+static inline
+void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 clr_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg);
+}
+
+/*
+ * isp_reg_set - Set individual bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @set_bits: 32 bit value which would be set in the register.
+ */
+static inline
+void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 set_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, v | set_bits, mmio_range, reg);
+}
+
+/*
+ * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @mmio_range: Range to which the register offset refers to.
+ * @reg: Register offset to work on.
+ * @clr_bits: 32 bit value which would be cleared in the register.
+ * @set_bits: 32 bit value which would be set in the register.
+ *
+ * The clear operation is done first, and then the set operation.
+ */
+static inline
+void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
+ u32 reg, u32 clr_bits, u32 set_bits)
+{
+ u32 v = isp_reg_readl(isp, mmio_range, reg);
+
+ isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg);
+}
+
+static inline enum v4l2_buf_type
+isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
+{
+ if (pad >= subdev->entity.num_pads)
+ return 0;
+
+ if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
+ return V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ else
+ return V4L2_BUF_TYPE_VIDEO_CAPTURE;
+}
+
+#endif /* OMAP3_ISP_CORE_H */
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c
new file mode 100644
index 000000000000..60181ab96063
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispccdc.c
@@ -0,0 +1,2583 @@
+/*
+ * ispccdc.c
+ *
+ * TI OMAP3 ISP - CCDC module
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/v4l2-event.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispccdc.h"
+
+#define CCDC_MIN_WIDTH 32
+#define CCDC_MIN_HEIGHT 32
+
+static struct v4l2_mbus_framefmt *
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which);
+
+static const unsigned int ccdc_fmts[] = {
+ V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12,
+ V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGRBG12_1X12,
+ V4L2_MBUS_FMT_SRGGB12_1X12,
+ V4L2_MBUS_FMT_SBGGR12_1X12,
+ V4L2_MBUS_FMT_SGBRG12_1X12,
+ V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8,
+};
+
+/*
+ * ccdc_print_status - Print current CCDC Module register values.
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Also prints other debug information stored in the CCDC module.
+ */
+#define CCDC_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name))
+
+static void ccdc_print_status(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n");
+
+ CCDC_PRINT_REGISTER(isp, PCR);
+ CCDC_PRINT_REGISTER(isp, SYN_MODE);
+ CCDC_PRINT_REGISTER(isp, HD_VD_WID);
+ CCDC_PRINT_REGISTER(isp, PIX_LINES);
+ CCDC_PRINT_REGISTER(isp, HORZ_INFO);
+ CCDC_PRINT_REGISTER(isp, VERT_START);
+ CCDC_PRINT_REGISTER(isp, VERT_LINES);
+ CCDC_PRINT_REGISTER(isp, CULLING);
+ CCDC_PRINT_REGISTER(isp, HSIZE_OFF);
+ CCDC_PRINT_REGISTER(isp, SDOFST);
+ CCDC_PRINT_REGISTER(isp, SDR_ADDR);
+ CCDC_PRINT_REGISTER(isp, CLAMP);
+ CCDC_PRINT_REGISTER(isp, DCSUB);
+ CCDC_PRINT_REGISTER(isp, COLPTN);
+ CCDC_PRINT_REGISTER(isp, BLKCMP);
+ CCDC_PRINT_REGISTER(isp, FPC);
+ CCDC_PRINT_REGISTER(isp, FPC_ADDR);
+ CCDC_PRINT_REGISTER(isp, VDINT);
+ CCDC_PRINT_REGISTER(isp, ALAW);
+ CCDC_PRINT_REGISTER(isp, REC656IF);
+ CCDC_PRINT_REGISTER(isp, CFG);
+ CCDC_PRINT_REGISTER(isp, FMTCFG);
+ CCDC_PRINT_REGISTER(isp, FMT_HORZ);
+ CCDC_PRINT_REGISTER(isp, FMT_VERT);
+ CCDC_PRINT_REGISTER(isp, PRGEVEN0);
+ CCDC_PRINT_REGISTER(isp, PRGEVEN1);
+ CCDC_PRINT_REGISTER(isp, PRGODD0);
+ CCDC_PRINT_REGISTER(isp, PRGODD1);
+ CCDC_PRINT_REGISTER(isp, VP_OUT);
+ CCDC_PRINT_REGISTER(isp, LSC_CONFIG);
+ CCDC_PRINT_REGISTER(isp, LSC_INITIAL);
+ CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE);
+ CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * omap3isp_ccdc_busy - Get busy state of the CCDC.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) &
+ ISPCCDC_PCR_BUSY;
+}
+
+/* -----------------------------------------------------------------------------
+ * Lens Shading Compensation
+ */
+
+/*
+ * ccdc_lsc_validate_config - Check that LSC configuration is valid.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @lsc_cfg: the LSC configuration to check.
+ *
+ * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid.
+ */
+static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_lsc_config *lsc_cfg)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct v4l2_mbus_framefmt *format;
+ unsigned int paxel_width, paxel_height;
+ unsigned int paxel_shift_x, paxel_shift_y;
+ unsigned int min_width, min_height, min_size;
+ unsigned int input_width, input_height;
+
+ paxel_shift_x = lsc_cfg->gain_mode_m;
+ paxel_shift_y = lsc_cfg->gain_mode_n;
+
+ if ((paxel_shift_x < 2) || (paxel_shift_x > 6) ||
+ (paxel_shift_y < 2) || (paxel_shift_y > 6)) {
+ dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n");
+ return -EINVAL;
+ }
+
+ if (lsc_cfg->offset & 3) {
+ dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of "
+ "4\n");
+ return -EINVAL;
+ }
+
+ if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) {
+ dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n");
+ return -EINVAL;
+ }
+
+ format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ input_width = format->width;
+ input_height = format->height;
+
+ /* Calculate minimum bytesize for validation */
+ paxel_width = 1 << paxel_shift_x;
+ min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1)
+ >> paxel_shift_x) + 1;
+
+ paxel_height = 1 << paxel_shift_y;
+ min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1)
+ >> paxel_shift_y) + 1;
+
+ min_size = 4 * min_width * min_height;
+ if (min_size > lsc_cfg->size) {
+ dev_dbg(isp->dev, "CCDC: LSC: too small table\n");
+ return -EINVAL;
+ }
+ if (lsc_cfg->offset < (min_width * 4)) {
+ dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n");
+ return -EINVAL;
+ }
+ if ((lsc_cfg->size / lsc_cfg->offset) < min_height) {
+ dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
+{
+ isp_reg_writel(to_isp_device(ccdc), addr,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
+}
+
+/*
+ * ccdc_lsc_setup_regs - Configures the lens shading compensation module
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_lsc_config *cfg)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ int reg;
+
+ isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_TABLE_OFFSET);
+
+ reg = 0;
+ reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT;
+ reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT;
+ reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT;
+ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG);
+
+ reg = 0;
+ reg &= ~ISPCCDC_LSC_INITIAL_X_MASK;
+ reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT;
+ reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK;
+ reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT;
+ isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_INITIAL);
+}
+
+static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ unsigned int wait;
+
+ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
+ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+
+ /* timeout 1 ms */
+ for (wait = 0; wait < 1000; wait++) {
+ if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) &
+ IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) {
+ isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
+ OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
+ return 0;
+ }
+
+ rmb();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+/*
+ * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @enable: 0 Disables LSC, 1 Enables LSC.
+ */
+static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ const struct v4l2_mbus_framefmt *format =
+ __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+
+ if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) &&
+ (format->code != V4L2_MBUS_FMT_SGBRG10_1X10))
+ return -EINVAL;
+
+ if (enable)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
+ ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0);
+
+ if (enable) {
+ if (ccdc_lsc_wait_prefetch(ccdc) < 0) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+ dev_warn(to_device(ccdc), "LSC prefecth timeout\n");
+ return -ETIMEDOUT;
+ }
+ ccdc->lsc.state = LSC_STATE_RUNNING;
+ } else {
+ ccdc->lsc.state = LSC_STATE_STOPPING;
+ }
+
+ return 0;
+}
+
+static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) &
+ ISPCCDC_LSC_BUSY;
+}
+
+/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine
+ * @ccdc: Pointer to ISP CCDC device
+ * @req: New configuration request
+ *
+ * context: in_interrupt()
+ */
+static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
+ struct ispccdc_lsc_config_req *req)
+{
+ if (!req->enable)
+ return -EINVAL;
+
+ if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) {
+ dev_dbg(to_device(ccdc), "Discard LSC configuration\n");
+ return -EINVAL;
+ }
+
+ if (ccdc_lsc_busy(ccdc))
+ return -EBUSY;
+
+ ccdc_lsc_setup_regs(ccdc, &req->config);
+ ccdc_lsc_program_table(ccdc, req->table);
+ return 0;
+}
+
+/*
+ * ccdc_lsc_error_handler - Handle LSC prefetch error scenario.
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Disables LSC, and defers enablement to shadow registers update time.
+ */
+static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ /*
+ * From OMAP3 TRM: When this event is pending, the module
+ * goes into transparent mode (output =input). Normal
+ * operation can be resumed at the start of the next frame
+ * after:
+ * 1) Clearing this event
+ * 2) Disabling the LSC module
+ * 3) Enabling it
+ */
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
+ ISPCCDC_LSC_ENABLE);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+}
+
+static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
+ struct ispccdc_lsc_config_req *req)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ if (req == NULL)
+ return;
+
+ if (req->iovm)
+ dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+ if (req->table)
+ omap_iommu_vfree(isp->domain, isp->dev, req->table);
+ kfree(req);
+}
+
+static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc,
+ struct list_head *queue)
+{
+ struct ispccdc_lsc_config_req *req, *n;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ list_for_each_entry_safe(req, n, queue, list) {
+ list_del(&req->list);
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ ccdc_lsc_free_request(ccdc, req);
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ }
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+static void ccdc_lsc_free_table_work(struct work_struct *work)
+{
+ struct isp_ccdc_device *ccdc;
+ struct ispccdc_lsc *lsc;
+
+ lsc = container_of(work, struct ispccdc_lsc, table_work);
+ ccdc = container_of(lsc, struct isp_ccdc_device, lsc);
+
+ ccdc_lsc_free_queue(ccdc, &lsc->free_queue);
+}
+
+/*
+ * ccdc_lsc_config - Configure the LSC module from a userspace request
+ *
+ * Store the request LSC configuration in the LSC engine request pointer. The
+ * configuration will be applied to the hardware when the CCDC will be enabled,
+ * or at the next LSC interrupt if the CCDC is already running.
+ */
+static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_update_config *config)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct ispccdc_lsc_config_req *req;
+ unsigned long flags;
+ void *table;
+ u16 update;
+ int ret;
+
+ update = config->update &
+ (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC);
+ if (!update)
+ return 0;
+
+ if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) {
+ dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table "
+ "need to be supplied\n", __func__);
+ return -EINVAL;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (req == NULL)
+ return -ENOMEM;
+
+ if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) {
+ if (copy_from_user(&req->config, config->lsc_cfg,
+ sizeof(req->config))) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ req->enable = 1;
+
+ req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
+ req->config.size, IOMMU_FLAG);
+ if (IS_ERR_VALUE(req->table)) {
+ req->table = 0;
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ req->iovm = omap_find_iovm_area(isp->dev, req->table);
+ if (req->iovm == NULL) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE)) {
+ ret = -ENOMEM;
+ req->iovm = NULL;
+ goto done;
+ }
+
+ dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+
+ table = omap_da_to_va(isp->dev, req->table);
+ if (copy_from_user(table, config->lsc, req->config.size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
+ req->iovm->sgt->nents, DMA_TO_DEVICE);
+ }
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.request) {
+ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
+ schedule_work(&ccdc->lsc.table_work);
+ }
+ ccdc->lsc.request = req;
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+
+ ret = 0;
+
+done:
+ if (ret < 0)
+ ccdc_lsc_free_request(ccdc, req);
+
+ return ret;
+}
+
+static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.active) {
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+ return 0;
+}
+
+static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
+{
+ struct ispccdc_lsc *lsc = &ccdc->lsc;
+
+ if (lsc->state != LSC_STATE_STOPPED)
+ return -EINVAL;
+
+ if (lsc->active) {
+ list_add_tail(&lsc->active->list, &lsc->free_queue);
+ lsc->active = NULL;
+ }
+
+ if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) {
+ omap3isp_sbl_disable(to_isp_device(ccdc),
+ OMAP3_ISP_SBL_CCDC_LSC_READ);
+ list_add_tail(&lsc->request->list, &lsc->free_queue);
+ lsc->request = NULL;
+ goto done;
+ }
+
+ lsc->active = lsc->request;
+ lsc->request = NULL;
+ __ccdc_lsc_enable(ccdc, 1);
+
+done:
+ if (!list_empty(&lsc->free_queue))
+ schedule_work(&lsc->table_work);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Parameters configuration
+ */
+
+/*
+ * ccdc_configure_clamp - Configure optical-black or digital clamping
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * The CCDC performs either optical-black or digital clamp. Configure and enable
+ * the selected clamp method.
+ */
+static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 clamp;
+
+ if (ccdc->obclamp) {
+ clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT;
+ clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT;
+ clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT;
+ clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT;
+ isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP);
+ } else {
+ isp_reg_writel(isp, ccdc->clamp.dcsubval,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB);
+ }
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP,
+ ISPCCDC_CLAMP_CLAMPEN,
+ ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0);
+}
+
+/*
+ * ccdc_configure_fpc - Configure Faulty Pixel Correction
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN);
+
+ if (!ccdc->fpc_en)
+ return;
+
+ isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
+ ISPCCDC_FPC_ADDR);
+ /* The FPNUM field must be set before enabling FPC. */
+ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
+ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) |
+ ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
+}
+
+/*
+ * ccdc_configure_black_comp - Configure Black Level Compensation.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ u32 blcomp;
+
+ blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT;
+ blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT;
+ blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT;
+ blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT;
+
+ isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP);
+}
+
+/*
+ * ccdc_configure_lpf - Configure Low-Pass Filter (LPF).
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE,
+ ISPCCDC_SYN_MODE_LPF,
+ ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0);
+}
+
+/*
+ * ccdc_configure_alaw - Configure A-law compression.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ const struct isp_format_info *info;
+ u32 alaw = 0;
+
+ info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
+
+ switch (info->width) {
+ case 8:
+ return;
+
+ case 10:
+ alaw = ISPCCDC_ALAW_GWDI_9_0;
+ break;
+ case 11:
+ alaw = ISPCCDC_ALAW_GWDI_10_1;
+ break;
+ case 12:
+ alaw = ISPCCDC_ALAW_GWDI_11_2;
+ break;
+ case 13:
+ alaw = ISPCCDC_ALAW_GWDI_12_3;
+ break;
+ }
+
+ if (ccdc->alaw)
+ alaw |= ISPCCDC_ALAW_CCDTBL;
+
+ isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW);
+}
+
+/*
+ * ccdc_config_imgattr - Configure sensor image specific attributes.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @colptn: Color pattern of the sensor.
+ */
+static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN);
+}
+
+/*
+ * ccdc_config - Set CCDC configuration from userspace
+ * @ccdc: Pointer to ISP CCDC device.
+ * @userspace_add: Structure containing CCDC configuration sent from userspace.
+ *
+ * Returns 0 if successful, -EINVAL if the pointer to the configuration
+ * structure is null, or the copy_from_user function fails to copy user space
+ * memory to kernel space memory.
+ */
+static int ccdc_config(struct isp_ccdc_device *ccdc,
+ struct omap3isp_ccdc_update_config *ccdc_struct)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ ccdc->shadow_update = 1;
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) {
+ ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_ALAW;
+ }
+
+ if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) {
+ ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_LPF;
+ }
+
+ if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) {
+ if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp,
+ sizeof(ccdc->clamp))) {
+ ccdc->shadow_update = 0;
+ return -EFAULT;
+ }
+
+ ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag);
+ ccdc->update |= OMAP3ISP_CCDC_BLCLAMP;
+ }
+
+ if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) {
+ if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp,
+ sizeof(ccdc->blcomp))) {
+ ccdc->shadow_update = 0;
+ return -EFAULT;
+ }
+
+ ccdc->update |= OMAP3ISP_CCDC_BCOMP;
+ }
+
+ ccdc->shadow_update = 0;
+
+ if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
+ u32 table_old = 0;
+ u32 table_new;
+ u32 size;
+
+ if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
+
+ if (ccdc->fpc_en) {
+ if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
+ sizeof(ccdc->fpc)))
+ return -EFAULT;
+
+ /*
+ * table_new must be 64-bytes aligned, but it's
+ * already done by omap_iommu_vmalloc().
+ */
+ size = ccdc->fpc.fpnum * 4;
+ table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
+ 0, size, IOMMU_FLAG);
+ if (IS_ERR_VALUE(table_new))
+ return -ENOMEM;
+
+ if (copy_from_user(omap_da_to_va(isp->dev, table_new),
+ (__force void __user *)
+ ccdc->fpc.fpcaddr, size)) {
+ omap_iommu_vfree(isp->domain, isp->dev,
+ table_new);
+ return -EFAULT;
+ }
+
+ table_old = ccdc->fpc.fpcaddr;
+ ccdc->fpc.fpcaddr = table_new;
+ }
+
+ ccdc_configure_fpc(ccdc);
+ if (table_old != 0)
+ omap_iommu_vfree(isp->domain, isp->dev, table_old);
+ }
+
+ return ccdc_lsc_config(ccdc, ccdc_struct);
+}
+
+static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
+{
+ if (ccdc->update & OMAP3ISP_CCDC_ALAW) {
+ ccdc_configure_alaw(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_ALAW;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_LPF) {
+ ccdc_configure_lpf(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_LPF;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) {
+ ccdc_configure_clamp(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP;
+ }
+
+ if (ccdc->update & OMAP3ISP_CCDC_BCOMP) {
+ ccdc_configure_black_comp(ccdc);
+ ccdc->update &= ~OMAP3ISP_CCDC_BCOMP;
+ }
+}
+
+/*
+ * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
+ * @dev: Pointer to ISP device
+ */
+void omap3isp_ccdc_restore_context(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC);
+
+ ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF
+ | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP;
+ ccdc_apply_controls(ccdc);
+ ccdc_configure_fpc(ccdc);
+}
+
+/* -----------------------------------------------------------------------------
+ * Format- and pipeline-related configuration helpers
+ */
+
+/*
+ * ccdc_config_vp - Configure the Video Port.
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccdc);
+ const struct isp_format_info *info;
+ unsigned long l3_ick = pipe->l3_ick;
+ unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
+ unsigned int div = 0;
+ u32 fmtcfg_vp;
+
+ fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)
+ & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK);
+
+ info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code);
+
+ switch (info->width) {
+ case 8:
+ case 10:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0;
+ break;
+ case 11:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1;
+ break;
+ case 12:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2;
+ break;
+ case 13:
+ fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3;
+ break;
+ }
+
+ if (pipe->input)
+ div = DIV_ROUND_UP(l3_ick, pipe->max_rate);
+ else if (pipe->external_rate)
+ div = l3_ick / pipe->external_rate;
+
+ div = clamp(div, 2U, max_div);
+ fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
+
+ isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
+}
+
+/*
+ * ccdc_enable_vp - Enable Video Port.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @enable: 0 Disables VP, 1 Enables VP
+ *
+ * This is needed for outputting image to Preview, H3A and HIST ISP submodules.
+ */
+static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG,
+ ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0);
+}
+
+/*
+ * ccdc_config_outlineoffset - Configure memory saving output line offset
+ * @ccdc: Pointer to ISP CCDC device.
+ * @offset: Address offset to start a new line. Must be twice the
+ * Output width and aligned on 32 byte boundary
+ * @oddeven: Specifies the odd/even line pattern to be chosen to store the
+ * output.
+ * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines.
+ *
+ * - Configures the output line offset when stored in memory
+ * - Sets the odd/even line pattern to store the output
+ * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4))
+ * - Configures the number of even and odd line fields in case of rearranging
+ * the lines.
+ */
+static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
+ u32 offset, u8 oddeven, u8 numlines)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, offset & 0xffff,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ ISPCCDC_SDOFST_FINV);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ ISPCCDC_SDOFST_FOFST_4L);
+
+ switch (oddeven) {
+ case EVENEVEN:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT);
+ break;
+ case ODDEVEN:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT);
+ break;
+ case EVENODD:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT);
+ break;
+ case ODDODD:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
+ (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * ccdc_set_outaddr - Set memory address to save output image
+ * @ccdc: Pointer to ISP CCDC device.
+ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ */
+static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR);
+}
+
+/*
+ * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input
+ * @ccdc: Pointer to ISP CCDC device.
+ * @max_rate: Maximum calculated data rate.
+ *
+ * Returns in *max_rate less value between calculated and passed
+ */
+void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
+ unsigned int *max_rate)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ unsigned int rate;
+
+ if (pipe == NULL)
+ return;
+
+ /*
+ * TRM says that for parallel sensors the maximum data rate
+ * should be 90% form L3/2 clock, otherwise just L3/2.
+ */
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
+ rate = pipe->l3_ick / 2 * 9 / 10;
+ else
+ rate = pipe->l3_ick / 2;
+
+ *max_rate = min(*max_rate, rate);
+}
+
+/*
+ * ccdc_config_sync_if - Set CCDC sync interface configuration
+ * @ccdc: Pointer to ISP CCDC device.
+ * @pdata: Parallel interface platform data (may be NULL)
+ * @data_size: Data size
+ */
+static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
+ struct isp_parallel_platform_data *pdata,
+ unsigned int data_size)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ const struct v4l2_mbus_framefmt *format;
+ u32 syn_mode = ISPCCDC_SYN_MODE_VDHDEN;
+
+ format = &ccdc->formats[CCDC_PAD_SINK];
+
+ if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ /* The bridge is enabled for YUV8 formats. Configure the input
+ * mode accordingly.
+ */
+ syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16;
+ }
+
+ switch (data_size) {
+ case 8:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8;
+ break;
+ case 10:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10;
+ break;
+ case 11:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11;
+ break;
+ case 12:
+ syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12;
+ break;
+ }
+
+ if (pdata && pdata->data_pol)
+ syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
+
+ if (pdata && pdata->hs_pol)
+ syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
+
+ if (pdata && pdata->vs_pol)
+ syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
+
+ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ /* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The
+ * hardware seems to ignore it in all other input modes.
+ */
+ if (format->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_Y8POS);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_Y8POS);
+
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
+ ISPCCDC_REC656IF_R656ON);
+}
+
+/* CCDC formats descriptions */
+static const u32 ccdc_sgrbg_pattern =
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_srggb_pattern =
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_sbggr_pattern =
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static const u32 ccdc_sgbrg_pattern =
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
+ ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
+ ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
+ ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
+ ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
+
+static void ccdc_configure(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct isp_parallel_platform_data *pdata = NULL;
+ struct v4l2_subdev *sensor;
+ struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ const struct isp_format_info *fmt_info;
+ struct v4l2_subdev_format fmt_src;
+ unsigned int depth_out;
+ unsigned int depth_in = 0;
+ struct media_pad *pad;
+ unsigned long flags;
+ unsigned int bridge;
+ unsigned int shift;
+ u32 syn_mode;
+ u32 ccdc_pattern;
+
+ pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ if (ccdc->input == CCDC_INPUT_PARALLEL)
+ pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
+ ->bus.parallel;
+
+ /* Compute the lane shifter shift value and enable the bridge when the
+ * input format is YUV.
+ */
+ fmt_src.pad = pad->index;
+ fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
+ fmt_info = omap3isp_video_format_info(fmt_src.format.code);
+ depth_in = fmt_info->width;
+ }
+
+ fmt_info = omap3isp_video_format_info
+ (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
+ depth_out = fmt_info->width;
+ shift = depth_in - depth_out;
+
+ if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8)
+ bridge = ISPCTRL_PAR_BRIDGE_LENDIAN;
+ else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ bridge = ISPCTRL_PAR_BRIDGE_BENDIAN;
+ else
+ bridge = ISPCTRL_PAR_BRIDGE_DISABLE;
+
+ omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge);
+
+ ccdc_config_sync_if(ccdc, pdata, depth_out);
+
+ syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ /* Use the raw, unprocessed data when writing to memory. The H3A and
+ * histogram modules are still fed with lens shading corrected data.
+ */
+ syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
+
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ syn_mode |= ISPCCDC_SYN_MODE_WEN;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_WEN;
+
+ if (ccdc->output & CCDC_OUTPUT_RESIZER)
+ syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
+
+ /* CCDC_PAD_SINK */
+ format = &ccdc->formats[CCDC_PAD_SINK];
+
+ /* Mosaic filter */
+ switch (format->code) {
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ case V4L2_MBUS_FMT_SRGGB12_1X12:
+ ccdc_pattern = ccdc_srggb_pattern;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SBGGR12_1X12:
+ ccdc_pattern = ccdc_sbggr_pattern;
+ break;
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ case V4L2_MBUS_FMT_SGBRG12_1X12:
+ ccdc_pattern = ccdc_sgbrg_pattern;
+ break;
+ default:
+ /* Use GRBG */
+ ccdc_pattern = ccdc_sgrbg_pattern;
+ break;
+ }
+ ccdc_config_imgattr(ccdc, ccdc_pattern);
+
+ /* Generate VD0 on the last line of the image and VD1 on the
+ * 2/3 height line.
+ */
+ isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
+ ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
+
+ /* CCDC_PAD_SOURCE_OF */
+ format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
+ crop = &ccdc->crop;
+
+ isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
+ ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
+ isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
+ isp_reg_writel(isp, (crop->height - 1)
+ << ISPCCDC_VERT_LINES_NLV_SHIFT,
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
+
+ ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
+
+ /* The CCDC outputs data in UYVY order by default. Swap bytes to get
+ * YUYV.
+ */
+ if (format->code == V4L2_MBUS_FMT_YUYV8_1X16)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_BSWD);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_BSWD);
+
+ /* Use PACK8 mode for 1byte per pixel formats. */
+ if (omap3isp_video_format_info(format->code)->width <= 8)
+ syn_mode |= ISPCCDC_SYN_MODE_PACK8;
+ else
+ syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
+
+ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
+
+ /* CCDC_PAD_SOURCE_VP */
+ format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
+
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
+ (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
+ isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
+ ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
+
+ isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
+ (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
+ OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
+
+ /* Lens shading correction. */
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+ if (ccdc->lsc.request == NULL)
+ goto unlock;
+
+ WARN_ON(ccdc->lsc.active);
+
+ /* Get last good LSC configuration. If it is not supported for
+ * the current active resolution discard it.
+ */
+ if (ccdc->lsc.active == NULL &&
+ __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) {
+ ccdc->lsc.active = ccdc->lsc.request;
+ } else {
+ list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
+ schedule_work(&ccdc->lsc.table_work);
+ }
+
+ ccdc->lsc.request = NULL;
+
+unlock:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+
+ ccdc_apply_controls(ccdc);
+}
+
+static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
+ ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
+}
+
+static int ccdc_disable(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ ccdc->stopping = CCDC_STOP_REQUEST;
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ ret = wait_event_timeout(ccdc->wait,
+ ccdc->stopping == CCDC_STOP_FINISHED,
+ msecs_to_jiffies(2000));
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ dev_warn(to_device(ccdc), "CCDC stop timeout!\n");
+ }
+
+ omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ);
+
+ mutex_lock(&ccdc->ioctl_lock);
+ ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
+ ccdc->lsc.request = ccdc->lsc.active;
+ ccdc->lsc.active = NULL;
+ cancel_work_sync(&ccdc->lsc.table_work);
+ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
+ mutex_unlock(&ccdc->ioctl_lock);
+
+ ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
+
+ return ret > 0 ? 0 : ret;
+}
+
+static void ccdc_enable(struct isp_ccdc_device *ccdc)
+{
+ if (ccdc_lsc_is_configured(ccdc))
+ __ccdc_lsc_enable(ccdc, 1);
+ __ccdc_enable(ccdc, 1);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Returns zero if the CCDC is idle and the image has been written to
+ * memory, too.
+ */
+static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc)
+{
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ return omap3isp_ccdc_busy(ccdc)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) &
+ ISPSBL_CCDC_WR_0_DATA_READY)
+ | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) &
+ ISPSBL_CCDC_WR_0_DATA_READY);
+}
+
+/*
+ * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle
+ * @ccdc: Pointer to ISP CCDC device.
+ * @max_wait: Max retry count in us for wait for idle/busy transition.
+ */
+static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
+ unsigned int max_wait)
+{
+ unsigned int wait = 0;
+
+ if (max_wait == 0)
+ max_wait = 10000; /* 10 ms */
+
+ for (wait = 0; wait <= max_wait; wait++) {
+ if (!ccdc_sbl_busy(ccdc))
+ return 0;
+
+ rmb();
+ udelay(1);
+ }
+
+ return -EBUSY;
+}
+
+/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
+ * @ccdc: Pointer to ISP CCDC device.
+ * @event: Pointing which event trigger handler
+ *
+ * Return 1 when the event and stopping request combination is satisfied,
+ * zero otherwise.
+ */
+static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
+{
+ int rval = 0;
+
+ switch ((ccdc->stopping & 3) | event) {
+ case CCDC_STOP_REQUEST | CCDC_EVENT_VD1:
+ if (ccdc->lsc.state != LSC_STATE_STOPPED)
+ __ccdc_lsc_enable(ccdc, 0);
+ __ccdc_enable(ccdc, 0);
+ ccdc->stopping = CCDC_STOP_EXECUTED;
+ return 1;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0:
+ ccdc->stopping |= CCDC_STOP_CCDC_FINISHED;
+ if (ccdc->lsc.state == LSC_STATE_STOPPED)
+ ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
+ rval = 1;
+ break;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE:
+ ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
+ rval = 1;
+ break;
+
+ case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1:
+ return 1;
+ }
+
+ if (ccdc->stopping == CCDC_STOP_FINISHED) {
+ wake_up(&ccdc->wait);
+ rval = 1;
+ }
+
+ return rval;
+}
+
+static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct video_device *vdev = ccdc->subdev.devnode;
+ struct v4l2_event event;
+
+ /* Frame number propagation */
+ atomic_inc(&pipe->frame_number);
+
+ memset(&event, 0, sizeof(event));
+ event.type = V4L2_EVENT_FRAME_SYNC;
+ event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
+
+ v4l2_event_queue(vdev, &event);
+}
+
+/*
+ * ccdc_lsc_isr - Handle LSC events
+ * @ccdc: Pointer to ISP CCDC device.
+ * @events: LSC events
+ */
+static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
+{
+ unsigned long flags;
+
+ if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) {
+ struct isp_pipeline *pipe =
+ to_isp_pipeline(&ccdc->subdev.entity);
+
+ ccdc_lsc_error_handler(ccdc);
+ pipe->error = true;
+ dev_dbg(to_device(ccdc), "lsc prefetch error\n");
+ }
+
+ if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ))
+ return;
+
+ /* LSC_DONE interrupt occur, there are two cases
+ * 1. stopping for reconfiguration
+ * 2. stopping because of STREAM OFF command
+ */
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+
+ if (ccdc->lsc.state == LSC_STATE_STOPPING)
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
+ goto done;
+
+ if (ccdc->lsc.state != LSC_STATE_RECONFIG)
+ goto done;
+
+ /* LSC is in STOPPING state, change to the new state */
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+
+ /* This is an exception. Start of frame and LSC_DONE interrupt
+ * have been received on the same time. Skip this event and wait
+ * for better times.
+ */
+ if (events & IRQ0STATUS_HS_VS_IRQ)
+ goto done;
+
+ /* The LSC engine is stopped at this point. Enable it if there's a
+ * pending request.
+ */
+ if (ccdc->lsc.request == NULL)
+ goto done;
+
+ ccdc_lsc_enable(ccdc);
+
+done:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
+ struct isp_device *isp = to_isp_device(ccdc);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ /* The CCDC generates VD0 interrupts even when disabled (the datasheet
+ * doesn't explicitly state if that's supposed to happen or not, so it
+ * can be considered as a hardware bug or as a feature, but we have to
+ * deal with it anyway). Disabling the CCDC when no buffer is available
+ * would thus not be enough, we need to handle the situation explicitly.
+ */
+ if (list_empty(&ccdc->video_out.dmaqueue))
+ goto done;
+
+ /* We're in continuous mode, and memory writes were disabled due to a
+ * buffer underrun. Reenable them now that we have a buffer. The buffer
+ * address has been set in ccdc_video_queue.
+ */
+ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
+ restart = 1;
+ ccdc->underrun = 0;
+ goto done;
+ }
+
+ if (ccdc_sbl_wait_idle(ccdc, 1000)) {
+ dev_info(isp->dev, "CCDC won't become idle!\n");
+ goto done;
+ }
+
+ buffer = omap3isp_video_buffer_next(&ccdc->video_out);
+ if (buffer != NULL) {
+ ccdc_set_outaddr(ccdc, buffer->isp_addr);
+ restart = 1;
+ }
+
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+
+ if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT &&
+ isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+
+done:
+ return restart;
+}
+
+/*
+ * ccdc_vd0_isr - Handle VD0 event
+ * @ccdc: Pointer to ISP CCDC device.
+ *
+ * Executes LSC deferred enablement before next frame starts.
+ */
+static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+ int restart = 0;
+
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ restart = ccdc_isr_buffer(ccdc);
+
+ spin_lock_irqsave(&ccdc->lock, flags);
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+ return;
+ }
+
+ if (!ccdc->shadow_update)
+ ccdc_apply_controls(ccdc);
+ spin_unlock_irqrestore(&ccdc->lock, flags);
+
+ if (restart)
+ ccdc_enable(ccdc);
+}
+
+/*
+ * ccdc_vd1_isr - Handle VD1 event
+ * @ccdc: Pointer to ISP CCDC device.
+ */
+static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
+
+ /*
+ * Depending on the CCDC pipeline state, CCDC stopping should be
+ * handled differently. In SINGLESHOT we emulate an internal CCDC
+ * stopping because the CCDC hw works only in continuous mode.
+ * When CONTINUOUS pipeline state is used and the CCDC writes it's
+ * data to memory the CCDC and LSC are stopped immediately but
+ * without change the CCDC stopping state machine. The CCDC
+ * stopping state machine should be used only when user request
+ * for stopping is received (SINGLESHOT is an exeption).
+ */
+ switch (ccdc->state) {
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ ccdc->stopping = CCDC_STOP_REQUEST;
+ break;
+
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY) {
+ if (ccdc->lsc.state != LSC_STATE_STOPPED)
+ __ccdc_lsc_enable(ccdc, 0);
+ __ccdc_enable(ccdc, 0);
+ }
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ break;
+ }
+
+ if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
+ goto done;
+
+ if (ccdc->lsc.request == NULL)
+ goto done;
+
+ /*
+ * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ
+ * do the appropriate changes in registers
+ */
+ if (ccdc->lsc.state == LSC_STATE_RUNNING) {
+ __ccdc_lsc_enable(ccdc, 0);
+ ccdc->lsc.state = LSC_STATE_RECONFIG;
+ goto done;
+ }
+
+ /* LSC has been in STOPPED state, enable it */
+ if (ccdc->lsc.state == LSC_STATE_STOPPED)
+ ccdc_lsc_enable(ccdc);
+
+done:
+ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
+}
+
+/*
+ * omap3isp_ccdc_isr - Configure CCDC during interframe time.
+ * @ccdc: Pointer to ISP CCDC device.
+ * @events: CCDC events
+ */
+int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
+{
+ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ if (events & IRQ0STATUS_CCDC_VD1_IRQ)
+ ccdc_vd1_isr(ccdc);
+
+ ccdc_lsc_isr(ccdc, events);
+
+ if (events & IRQ0STATUS_CCDC_VD0_IRQ)
+ ccdc_vd0_isr(ccdc);
+
+ if (events & IRQ0STATUS_HS_VS_IRQ)
+ ccdc_hs_vs_isr(ccdc);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
+{
+ struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
+
+ if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
+ return -ENODEV;
+
+ ccdc_set_outaddr(ccdc, buffer->isp_addr);
+
+ /* We now have a buffer queued on the output, restart the pipeline
+ * on the next CCDC interrupt if running in continuous mode (or when
+ * starting the stream).
+ */
+ ccdc->underrun = 1;
+
+ return 0;
+}
+
+static const struct isp_video_operations ccdc_video_ops = {
+ .queue = ccdc_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * ccdc_ioctl - CCDC module private ioctl's
+ * @sd: ISP CCDC V4L2 subdevice
+ * @cmd: ioctl command
+ * @arg: ioctl argument
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_CCDC_CFG:
+ mutex_lock(&ccdc->ioctl_lock);
+ ret = ccdc_config(ccdc, arg);
+ mutex_unlock(&ccdc->ioctl_lock);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return ret;
+}
+
+static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* line number is zero at frame start */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL);
+}
+
+static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+/*
+ * ccdc_set_stream - Enable/Disable streaming on the CCDC module
+ * @sd: ISP CCDC V4L2 subdevice
+ * @enable: Enable/disable stream
+ *
+ * When writing to memory, the CCDC hardware can't be enabled without a memory
+ * buffer to write to. As the s_stream operation is called in response to a
+ * STREAMON call without any buffer queued yet, just update the enabled field
+ * and return immediately. The CCDC will be enabled in ccdc_isr_buffer().
+ *
+ * When not writing to memory enable the CCDC immediately.
+ */
+static int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(ccdc);
+ int ret = 0;
+
+ if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC);
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
+ ISPCCDC_CFG_VDLC);
+
+ ccdc_configure(ccdc);
+
+ /* TODO: Don't configure the video port if all of its output
+ * links are inactive.
+ */
+ ccdc_config_vp(ccdc);
+ ccdc_enable_vp(ccdc, 1);
+ ccdc_print_status(ccdc);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+
+ if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY))
+ ccdc_enable(ccdc);
+
+ ccdc->underrun = 0;
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (ccdc->output & CCDC_OUTPUT_MEMORY &&
+ ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+
+ ccdc_enable(ccdc);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ ret = ccdc_disable(ccdc);
+ if (ccdc->output & CCDC_OUTPUT_MEMORY)
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC);
+ ccdc->underrun = 0;
+ break;
+ }
+
+ ccdc->state = enable;
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &ccdc->formats[pad];
+}
+
+static struct v4l2_rect *
+__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF);
+ else
+ return &ccdc->crop;
+}
+
+/*
+ * ccdc_try_format - Try video format on a pad
+ * @ccdc: ISP CCDC device
+ * @fh : V4L2 subdev file handle
+ * @pad: Pad number
+ * @fmt: Format
+ */
+static void
+ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ const struct isp_format_info *info;
+ enum v4l2_mbus_pixelcode pixelcode;
+ unsigned int width = fmt->width;
+ unsigned int height = fmt->height;
+ struct v4l2_rect *crop;
+ unsigned int i;
+
+ switch (pad) {
+ case CCDC_PAD_SINK:
+ for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) {
+ if (fmt->code == ccdc_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(ccdc_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ /* Clamp the input size. */
+ fmt->width = clamp_t(u32, width, 32, 4096);
+ fmt->height = clamp_t(u32, height, 32, 4096);
+ break;
+
+ case CCDC_PAD_SOURCE_OF:
+ pixelcode = fmt->code;
+ *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+
+ /* YUV formats are converted from 2X8 to 1X16 by the bridge and
+ * can be byte-swapped.
+ */
+ if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ /* Use the user requested format if YUV. */
+ if (pixelcode == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ pixelcode == V4L2_MBUS_FMT_UYVY8_2X8 ||
+ pixelcode == V4L2_MBUS_FMT_YUYV8_1X16 ||
+ pixelcode == V4L2_MBUS_FMT_UYVY8_1X16)
+ fmt->code = pixelcode;
+
+ if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8)
+ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ else if (fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ fmt->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ }
+
+ /* Hardcode the output size to the crop rectangle size. */
+ crop = __ccdc_get_crop(ccdc, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
+ break;
+
+ case CCDC_PAD_SOURCE_VP:
+ *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
+
+ /* The video port interface truncates the data to 10 bits. */
+ info = omap3isp_video_format_info(fmt->code);
+ fmt->code = info->truncated;
+
+ /* YUV formats are not supported by the video port. */
+ if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ fmt->code == V4L2_MBUS_FMT_UYVY8_2X8)
+ fmt->code = 0;
+
+ /* The number of lines that can be clocked out from the video
+ * port output must be at least one line less than the number
+ * of input lines.
+ */
+ fmt->width = clamp_t(u32, width, 32, fmt->width);
+ fmt->height = clamp_t(u32, height, 32, fmt->height - 1);
+ break;
+ }
+
+ /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is
+ * stored on 2 bytes.
+ */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * ccdc_try_crop - Validate a crop rectangle
+ * @ccdc: ISP CCDC device
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ */
+static void ccdc_try_crop(struct isp_ccdc_device *ccdc,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ const struct isp_format_info *info;
+ unsigned int max_width;
+
+ /* For Bayer formats, restrict left/top and width/height to even values
+ * to keep the Bayer pattern.
+ */
+ info = omap3isp_video_format_info(sink->code);
+ if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ crop->left &= ~1;
+ crop->top &= ~1;
+ }
+
+ crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH);
+ crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT);
+
+ /* The data formatter truncates the number of horizontal output pixels
+ * to a multiple of 16. To avoid clipping data, allow callers to request
+ * an output size bigger than the input size up to the nearest multiple
+ * of 16.
+ */
+ max_width = (sink->width - crop->left + 15) & ~15;
+ crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width)
+ & ~15;
+ crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT,
+ sink->height - crop->top);
+
+ /* Odd width/height values don't make sense for Bayer formats. */
+ if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ crop->width &= ~1;
+ crop->height &= ~1;
+ }
+}
+
+/*
+ * ccdc_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ switch (code->pad) {
+ case CCDC_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(ccdc_fmts))
+ return -EINVAL;
+
+ code->code = ccdc_fmts[code->index];
+ break;
+
+ case CCDC_PAD_SOURCE_OF:
+ format = __ccdc_get_format(ccdc, fh, code->pad,
+ V4L2_SUBDEV_FORMAT_TRY);
+
+ if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ format->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ /* In YUV mode the CCDC can swap bytes. */
+ if (code->index == 0)
+ code->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ else if (code->index == 1)
+ code->code = V4L2_MBUS_FMT_UYVY8_1X16;
+ else
+ return -EINVAL;
+ } else {
+ /* In raw mode, no configurable format confversion is
+ * available.
+ */
+ if (code->index == 0)
+ code->code = format->code;
+ else
+ return -EINVAL;
+ }
+ break;
+
+ case CCDC_PAD_SOURCE_VP:
+ /* The CCDC supports no configurable format conversion
+ * compatible with the video port. Enumerate a single output
+ * format code.
+ */
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __ccdc_get_format(ccdc, fh, code->pad,
+ V4L2_SUBDEV_FORMAT_TRY);
+
+ /* A pixel code equal to 0 means that the video port doesn't
+ * support the input format. Don't enumerate any pixel code.
+ */
+ if (format->code == 0)
+ return -EINVAL;
+
+ code->code = format->code;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * ccdc_get_selection - Retrieve a selection rectangle on a pad
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the output formatter
+ * source pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->pad != CCDC_PAD_SOURCE_OF)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+ ccdc_try_crop(ccdc, format, &sel->r);
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ccdc_set_selection - Set a selection rectangle on a pad
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the output
+ * formatter source pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->target != V4L2_SEL_TGT_CROP ||
+ sel->pad != CCDC_PAD_SOURCE_OF)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ /* Modifying the crop rectangle always changes the format on the source
+ * pad. If the KEEP_CONFIG flag is set, just return the current crop
+ * rectangle.
+ */
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+ return 0;
+ }
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+ ccdc_try_crop(ccdc, format, &sel->r);
+ *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r;
+
+ /* Update the source format. */
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which);
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which);
+
+ return 0;
+}
+
+/*
+ * ccdc_get_format - Retrieve the video format on a pad
+ * @sd : ISP CCDC V4L2 subdevice
+ * @fh : V4L2 subdev file handle
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * ccdc_set_format - Set the video format on a pad
+ * @sd : ISP CCDC V4L2 subdevice
+ * @fh : V4L2 subdev file handle
+ * @fmt: Format
+ *
+ * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
+ * to the format type.
+ */
+static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CCDC_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __ccdc_get_crop(ccdc, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ ccdc_try_crop(ccdc, &fmt->format, crop);
+
+ /* Update the source formats. */
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
+ fmt->which);
+ *format = fmt->format;
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format,
+ fmt->which);
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP,
+ fmt->which);
+ *format = fmt->format;
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * Decide whether desired output pixel code can be obtained with
+ * the lane shifter by shifting the input pixel code.
+ * @in: input pixelcode to shifter
+ * @out: output pixelcode from shifter
+ * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
+ *
+ * return true if the combination is possible
+ * return false otherwise
+ */
+static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in,
+ enum v4l2_mbus_pixelcode out,
+ unsigned int additional_shift)
+{
+ const struct isp_format_info *in_info, *out_info;
+
+ if (in == out)
+ return true;
+
+ in_info = omap3isp_video_format_info(in);
+ out_info = omap3isp_video_format_info(out);
+
+ if ((in_info->flavor == 0) || (out_info->flavor == 0))
+ return false;
+
+ if (in_info->flavor != out_info->flavor)
+ return false;
+
+ return in_info->width - out_info->width + additional_shift <= 6;
+}
+
+static int ccdc_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ unsigned long parallel_shift;
+
+ /* Check if the two ends match */
+ if (source_fmt->format.width != sink_fmt->format.width ||
+ source_fmt->format.height != sink_fmt->format.height)
+ return -EPIPE;
+
+ /* We've got a parallel sensor here. */
+ if (ccdc->input == CCDC_INPUT_PARALLEL) {
+ struct isp_parallel_platform_data *pdata =
+ &((struct isp_v4l2_subdevs_group *)
+ media_entity_to_v4l2_subdev(link->source->entity)
+ ->host_priv)->bus.parallel;
+ parallel_shift = pdata->data_lane_shift * 2;
+ } else {
+ parallel_shift = 0;
+ }
+
+ /* Lane shifter may be used to drop bits on CCDC sink pad */
+ if (!ccdc_is_shiftable(source_fmt->format.code,
+ sink_fmt->format.code, parallel_shift))
+ return -EPIPE;
+
+ return 0;
+}
+
+/*
+ * ccdc_init_formats - Initialize formats on all pads
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CCDC_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ ccdc_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* V4L2 subdev core operations */
+static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = {
+ .ioctl = ccdc_ioctl,
+ .subscribe_event = ccdc_subscribe_event,
+ .unsubscribe_event = ccdc_unsubscribe_event,
+};
+
+/* V4L2 subdev video operations */
+static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = {
+ .s_stream = ccdc_set_stream,
+};
+
+/* V4L2 subdev pad operations */
+static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
+ .enum_mbus_code = ccdc_enum_mbus_code,
+ .enum_frame_size = ccdc_enum_frame_size,
+ .get_fmt = ccdc_get_format,
+ .set_fmt = ccdc_set_format,
+ .get_selection = ccdc_get_selection,
+ .set_selection = ccdc_set_selection,
+ .link_validate = ccdc_link_validate,
+};
+
+/* V4L2 subdev operations */
+static const struct v4l2_subdev_ops ccdc_v4l2_ops = {
+ .core = &ccdc_v4l2_core_ops,
+ .video = &ccdc_v4l2_video_ops,
+ .pad = &ccdc_v4l2_pad_ops,
+};
+
+/* V4L2 subdev internal operations */
+static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = {
+ .open = ccdc_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * ccdc_link_setup - Setup CCDC connections
+ * @entity: CCDC media entity
+ * @local: Pad at the local end of the link
+ * @remote: Pad at the remote end of the link
+ * @flags: Link flags
+ *
+ * return -EINVAL or zero on success
+ */
+static int ccdc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(ccdc);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Read from the sensor (parallel interface), CCP2, CSI2a or
+ * CSI2c.
+ */
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ccdc->input = CCDC_INPUT_NONE;
+ break;
+ }
+
+ if (ccdc->input != CCDC_INPUT_NONE)
+ return -EBUSY;
+
+ if (remote->entity == &isp->isp_ccp2.subdev.entity)
+ ccdc->input = CCDC_INPUT_CCP2B;
+ else if (remote->entity == &isp->isp_csi2a.subdev.entity)
+ ccdc->input = CCDC_INPUT_CSI2A;
+ else if (remote->entity == &isp->isp_csi2c.subdev.entity)
+ ccdc->input = CCDC_INPUT_CSI2C;
+ else
+ ccdc->input = CCDC_INPUT_PARALLEL;
+
+ break;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Write to preview engine, histogram and H3A. When none of
+ * those links are active, the video port can be disabled.
+ */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_PREVIEW)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_PREVIEW;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_PREVIEW;
+ }
+ break;
+
+ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE:
+ /* Write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_MEMORY)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_MEMORY;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_MEMORY;
+ }
+ break;
+
+ case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* Write to resizer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (ccdc->output & ~CCDC_OUTPUT_RESIZER)
+ return -EBUSY;
+ ccdc->output |= CCDC_OUTPUT_RESIZER;
+ } else {
+ ccdc->output &= ~CCDC_OUTPUT_RESIZER;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations ccdc_media_ops = {
+ .link_setup = ccdc_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
+{
+ v4l2_device_unregister_subdev(&ccdc->subdev);
+ omap3isp_video_unregister(&ccdc->video_out);
+}
+
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video node. */
+ ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&ccdc->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_ccdc_unregister_entities(ccdc);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CCDC initialisation and cleanup
+ */
+
+/*
+ * ccdc_init_entities - Initialize V4L2 subdev and media entity
+ * @ccdc: ISP CCDC module
+ *
+ * Return 0 on success and a negative error code on failure.
+ */
+static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
+{
+ struct v4l2_subdev *sd = &ccdc->subdev;
+ struct media_pad *pads = ccdc->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ ccdc->input = CCDC_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &ccdc_v4l2_ops);
+ sd->internal_ops = &ccdc_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, ccdc);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
+ pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &ccdc_media_ops;
+ ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ ccdc_init_formats(sd, NULL);
+
+ ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ccdc->video_out.ops = &ccdc_video_ops;
+ ccdc->video_out.isp = to_isp_device(ccdc);
+ ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+ ccdc->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the CCDC subdev to the video node. */
+ ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
+ &ccdc->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&ccdc->video_out);
+error_video:
+ media_entity_cleanup(me);
+ return ret;
+}
+
+/*
+ * omap3isp_ccdc_init - CCDC module initialization.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ *
+ * TODO: Get the initialisation values from platform data.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int omap3isp_ccdc_init(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+ int ret;
+
+ spin_lock_init(&ccdc->lock);
+ init_waitqueue_head(&ccdc->wait);
+ mutex_init(&ccdc->ioctl_lock);
+
+ ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
+
+ INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work);
+ ccdc->lsc.state = LSC_STATE_STOPPED;
+ INIT_LIST_HEAD(&ccdc->lsc.free_queue);
+ spin_lock_init(&ccdc->lsc.req_lock);
+
+ ccdc->clamp.oblen = 0;
+ ccdc->clamp.dcsubval = 0;
+
+ ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
+ ccdc_apply_controls(ccdc);
+
+ ret = ccdc_init_entities(ccdc);
+ if (ret < 0) {
+ mutex_destroy(&ccdc->ioctl_lock);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_ccdc_cleanup - CCDC module cleanup.
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ */
+void omap3isp_ccdc_cleanup(struct isp_device *isp)
+{
+ struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
+
+ omap3isp_video_cleanup(&ccdc->video_out);
+ media_entity_cleanup(&ccdc->subdev.entity);
+
+ /* Free LSC requests. As the CCDC is stopped there's no active request,
+ * so only the pending request and the free queue need to be handled.
+ */
+ ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
+ cancel_work_sync(&ccdc->lsc.table_work);
+ ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
+
+ if (ccdc->fpc.fpcaddr != 0)
+ omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
+
+ mutex_destroy(&ccdc->ioctl_lock);
+}
diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/omap3isp/ispccdc.h
new file mode 100644
index 000000000000..a5da9e19edbf
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispccdc.h
@@ -0,0 +1,172 @@
+/*
+ * ispccdc.h
+ *
+ * TI OMAP3 ISP - CCDC module
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_CCDC_H
+#define OMAP3_ISP_CCDC_H
+
+#include <linux/omap3isp.h>
+#include <linux/workqueue.h>
+
+#include "ispvideo.h"
+
+enum ccdc_input_entity {
+ CCDC_INPUT_NONE,
+ CCDC_INPUT_PARALLEL,
+ CCDC_INPUT_CSI2A,
+ CCDC_INPUT_CCP2B,
+ CCDC_INPUT_CSI2C
+};
+
+#define CCDC_OUTPUT_MEMORY (1 << 0)
+#define CCDC_OUTPUT_PREVIEW (1 << 1)
+#define CCDC_OUTPUT_RESIZER (1 << 2)
+
+#define OMAP3ISP_CCDC_NEVENTS 16
+
+enum ispccdc_lsc_state {
+ LSC_STATE_STOPPED = 0,
+ LSC_STATE_STOPPING = 1,
+ LSC_STATE_RUNNING = 2,
+ LSC_STATE_RECONFIG = 3,
+};
+
+struct ispccdc_lsc_config_req {
+ struct list_head list;
+ struct omap3isp_ccdc_lsc_config config;
+ unsigned char enable;
+ u32 table;
+ struct iovm_struct *iovm;
+};
+
+/*
+ * ispccdc_lsc - CCDC LSC parameters
+ * @update_config: Set when user changes config
+ * @request_enable: Whether LSC is requested to be enabled
+ * @config: LSC config set by user
+ * @update_table: Set when user provides a new LSC table to table_new
+ * @table_new: LSC table set by user, ISP address
+ * @table_inuse: LSC table currently in use, ISP address
+ */
+struct ispccdc_lsc {
+ enum ispccdc_lsc_state state;
+ struct work_struct table_work;
+
+ /* LSC queue of configurations */
+ spinlock_t req_lock;
+ struct ispccdc_lsc_config_req *request; /* requested configuration */
+ struct ispccdc_lsc_config_req *active; /* active configuration */
+ struct list_head free_queue; /* configurations for freeing */
+};
+
+#define CCDC_STOP_NOT_REQUESTED 0x00
+#define CCDC_STOP_REQUEST 0x01
+#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST)
+#define CCDC_STOP_CCDC_FINISHED 0x04
+#define CCDC_STOP_LSC_FINISHED 0x08
+#define CCDC_STOP_FINISHED \
+ (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED)
+
+#define CCDC_EVENT_VD1 0x10
+#define CCDC_EVENT_VD0 0x20
+#define CCDC_EVENT_LSC_DONE 0x40
+
+/* Sink and source CCDC pads */
+#define CCDC_PAD_SINK 0
+#define CCDC_PAD_SOURCE_OF 1
+#define CCDC_PAD_SOURCE_VP 2
+#define CCDC_PADS_NUM 3
+
+/*
+ * struct isp_ccdc_device - Structure for the CCDC module to store its own
+ * information
+ * @subdev: V4L2 subdevice
+ * @pads: Sink and source media entity pads
+ * @formats: Active video formats
+ * @crop: Active crop rectangle on the OF source pad
+ * @input: Active input
+ * @output: Active outputs
+ * @video_out: Output video node
+ * @alaw: A-law compression enabled (1) or disabled (0)
+ * @lpf: Low pass filter enabled (1) or disabled (0)
+ * @obclamp: Optical-black clamp enabled (1) or disabled (0)
+ * @fpc_en: Faulty pixels correction enabled (1) or disabled (0)
+ * @blcomp: Black level compensation configuration
+ * @clamp: Optical-black or digital clamp configuration
+ * @fpc: Faulty pixels correction configuration
+ * @lsc: Lens shading compensation configuration
+ * @update: Bitmask of controls to update during the next interrupt
+ * @shadow_update: Controls update in progress by userspace
+ * @underrun: A buffer underrun occurred and a new buffer has been queued
+ * @state: Streaming state
+ * @lock: Serializes shadow_update with interrupt handler
+ * @wait: Wait queue used to stop the module
+ * @stopping: Stopping state
+ * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
+ */
+struct isp_ccdc_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[CCDC_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM];
+ struct v4l2_rect crop;
+
+ enum ccdc_input_entity input;
+ unsigned int output;
+ struct isp_video video_out;
+
+ unsigned int alaw:1,
+ lpf:1,
+ obclamp:1,
+ fpc_en:1;
+ struct omap3isp_ccdc_blcomp blcomp;
+ struct omap3isp_ccdc_bclamp clamp;
+ struct omap3isp_ccdc_fpc fpc;
+ struct ispccdc_lsc lsc;
+ unsigned int update;
+ unsigned int shadow_update;
+
+ unsigned int underrun:1;
+ enum isp_pipeline_stream_state state;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ unsigned int stopping;
+ struct mutex ioctl_lock;
+};
+
+struct isp_device;
+
+int omap3isp_ccdc_init(struct isp_device *isp);
+void omap3isp_ccdc_cleanup(struct isp_device *isp);
+int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
+ struct v4l2_device *vdev);
+void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc);
+
+int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc);
+int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events);
+void omap3isp_ccdc_restore_context(struct isp_device *isp);
+void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
+ unsigned int *max_rate);
+
+#endif /* OMAP3_ISP_CCDC_H */
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c
index 85f0de85f37c..85f0de85f37c 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/platform/omap3isp/ispccp2.c
diff --git a/drivers/media/video/omap3isp/ispccp2.h b/drivers/media/platform/omap3isp/ispccp2.h
index 76d65f4576ef..76d65f4576ef 100644
--- a/drivers/media/video/omap3isp/ispccp2.h
+++ b/drivers/media/platform/omap3isp/ispccp2.h
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c
new file mode 100644
index 000000000000..6a3ff792af7d
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispcsi2.c
@@ -0,0 +1,1328 @@
+/*
+ * ispcsi2.c
+ *
+ * TI OMAP3 ISP - CSI2 module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/mm.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispcsi2.h"
+
+/*
+ * csi2_if_enable - Enable CSI2 Receiver interface.
+ * @enable: enable flag
+ *
+ */
+static void csi2_if_enable(struct isp_device *isp,
+ struct isp_csi2_device *csi2, u8 enable)
+{
+ struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
+
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN,
+ enable ? ISPCSI2_CTRL_IF_EN : 0);
+
+ currctrl->if_enable = enable;
+}
+
+/*
+ * csi2_recv_config - CSI2 receiver module configuration.
+ * @currctrl: isp_csi2_ctrl_cfg structure
+ *
+ */
+static void csi2_recv_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_ctrl_cfg *currctrl)
+{
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL);
+
+ if (currctrl->frame_mode)
+ reg |= ISPCSI2_CTRL_FRAME;
+ else
+ reg &= ~ISPCSI2_CTRL_FRAME;
+
+ if (currctrl->vp_clk_enable)
+ reg |= ISPCSI2_CTRL_VP_CLK_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_VP_CLK_EN;
+
+ if (currctrl->vp_only_enable)
+ reg |= ISPCSI2_CTRL_VP_ONLY_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_VP_ONLY_EN;
+
+ reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK;
+ reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT;
+
+ if (currctrl->ecc_enable)
+ reg |= ISPCSI2_CTRL_ECC_EN;
+ else
+ reg &= ~ISPCSI2_CTRL_ECC_EN;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL);
+}
+
+static const unsigned int csi2_input_fmts[] = {
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_YUYV8_2X8,
+};
+
+/* To set the format on the CSI2 requires a mapping function that takes
+ * the following inputs:
+ * - 3 different formats (at this time)
+ * - 2 destinations (mem, vp+mem) (vp only handled separately)
+ * - 2 decompression options (on, off)
+ * - 2 isp revisions (certain format must be handled differently on OMAP3630)
+ * Output should be CSI2 frame format code
+ * Array indices as follows: [format][dest][decompr][is_3630]
+ * Not all combinations are valid. 0 means invalid.
+ */
+static const u16 __csi2_fmt_map[3][2][2][2] = {
+ /* RAW10 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW10_EXP16_VP,
+ CSI2_PIX_FMT_RAW10_EXP16_VP },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ },
+ /* RAW10 DPCM8 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 },
+ /* DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_DPCM10_EXP16,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10 },
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_VP,
+ CSI2_PIX_FMT_RAW8_VP },
+ /* DPCM decompression */
+ { CSI2_PIX_FMT_RAW8_DPCM10_VP,
+ CSI2_USERDEF_8BIT_DATA1_DPCM10_VP },
+ },
+ },
+ /* YUYV8 2X8 formats */
+ {
+ /* Output to memory */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_YUV422_8BIT,
+ CSI2_PIX_FMT_YUV422_8BIT },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ /* Output to both */
+ {
+ /* No DPCM decompression */
+ { CSI2_PIX_FMT_YUV422_8BIT_VP,
+ CSI2_PIX_FMT_YUV422_8BIT_VP },
+ /* DPCM decompression */
+ { 0, 0 },
+ },
+ },
+};
+
+/*
+ * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
+ * @csi2: ISP CSI2 device
+ *
+ * Returns CSI2 physical format id
+ */
+static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2)
+{
+ const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
+ int fmtidx, destidx, is_3630;
+
+ switch (fmt->code) {
+ case V4L2_MBUS_FMT_SGRBG10_1X10:
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ fmtidx = 0;
+ break;
+ case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
+ case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
+ fmtidx = 1;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ fmtidx = 2;
+ break;
+ default:
+ WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
+ fmt->code);
+ return 0;
+ }
+
+ if (!(csi2->output & CSI2_OUTPUT_CCDC) &&
+ !(csi2->output & CSI2_OUTPUT_MEMORY)) {
+ /* Neither output enabled is a valid combination */
+ return CSI2_PIX_FMT_OTHERS;
+ }
+
+ /* If we need to skip frames at the beginning of the stream disable the
+ * video port to avoid sending the skipped frames to the CCDC.
+ */
+ destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC);
+ is_3630 = csi2->isp->revision == ISP_REVISION_15_0;
+
+ return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630];
+}
+
+/*
+ * csi2_set_outaddr - Set memory address to save output image
+ * @csi2: Pointer to ISP CSI2a device.
+ * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
+ *
+ * Sets the memory address where the output will be saved.
+ *
+ * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
+ * boundary.
+ */
+static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr)
+{
+ struct isp_device *isp = csi2->isp;
+ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0];
+
+ ctx->ping_addr = addr;
+ ctx->pong_addr = addr;
+ isp_reg_writel(isp, ctx->ping_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
+ isp_reg_writel(isp, ctx->pong_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
+ * be enabled by CSI2.
+ * @format_id: mapped format id
+ *
+ */
+static inline int is_usr_def_mapping(u32 format_id)
+{
+ return (format_id & 0x40) ? 1 : 0;
+}
+
+/*
+ * csi2_ctx_enable - Enable specified CSI2 context
+ * @ctxnum: Context number, valid between 0 and 7 values.
+ * @enable: enable
+ *
+ */
+static void csi2_ctx_enable(struct isp_device *isp,
+ struct isp_csi2_device *csi2, u8 ctxnum, u8 enable)
+{
+ struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
+ unsigned int skip = 0;
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
+
+ if (enable) {
+ if (csi2->frame_skip)
+ skip = csi2->frame_skip;
+ else if (csi2->output & CSI2_OUTPUT_MEMORY)
+ skip = 1;
+
+ reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK;
+ reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK
+ | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT)
+ | ISPCSI2_CTX_CTRL1_CTX_EN;
+ } else {
+ reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN;
+ }
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
+ ctx->enabled = enable;
+}
+
+/*
+ * csi2_ctx_config - CSI2 context configuration.
+ * @ctx: context configuration
+ *
+ */
+static void csi2_ctx_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_ctx_cfg *ctx)
+{
+ u32 reg;
+
+ /* Set up CSI2_CTx_CTRL1 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
+
+ if (ctx->eof_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_EOF_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN;
+
+ if (ctx->eol_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_EOL_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN;
+
+ if (ctx->checksum_enabled)
+ reg |= ISPCSI2_CTX_CTRL1_CS_EN;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL1_CS_EN;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_CTRL2 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
+
+ reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
+ reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
+
+ reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK);
+ reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT;
+
+ if (ctx->dpcm_decompress) {
+ if (ctx->dpcm_predictor)
+ reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED;
+ else
+ reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED;
+ }
+
+ if (is_usr_def_mapping(ctx->format_id)) {
+ reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
+ reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
+ }
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_CTRL3 */
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
+ reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK);
+ reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT);
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
+
+ /* Set up CSI2_CTx_DAT_OFST */
+ reg = isp_reg_readl(isp, csi2->regs1,
+ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
+ reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK;
+ reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT;
+ isp_reg_writel(isp, reg, csi2->regs1,
+ ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
+
+ isp_reg_writel(isp, ctx->ping_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
+
+ isp_reg_writel(isp, ctx->pong_addr,
+ csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
+}
+
+/*
+ * csi2_timing_config - CSI2 timing configuration.
+ * @timing: csi2_timing_cfg structure
+ */
+static void csi2_timing_config(struct isp_device *isp,
+ struct isp_csi2_device *csi2,
+ struct isp_csi2_timing_cfg *timing)
+{
+ u32 reg;
+
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING);
+
+ if (timing->force_rx_mode)
+ reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
+
+ if (timing->stop_state_16x)
+ reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
+
+ if (timing->stop_state_4x)
+ reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
+ else
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
+
+ reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum);
+ reg |= timing->stop_state_counter <<
+ ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum);
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING);
+}
+
+/*
+ * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
+ * @enable: Enable/disable CSI2 Context interrupts
+ */
+static void csi2_irq_ctx_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1,
+ ISPCSI2_CTX_IRQSTATUS(i));
+ if (enable)
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
+ ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
+ else
+ isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
+ ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
+ }
+}
+
+/*
+ * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
+ * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
+ */
+static void csi2_irq_complexio1_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ u32 reg;
+ reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT |
+ ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM5 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC5 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM4 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC4 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM3 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC3 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM2 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC2 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 |
+ ISPCSI2_PHY_IRQENABLE_STATEULPM1 |
+ ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 |
+ ISPCSI2_PHY_IRQENABLE_ERRESC1 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 |
+ ISPCSI2_PHY_IRQENABLE_ERRSOTHS1;
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
+ if (enable)
+ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
+ else
+ reg = 0;
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
+}
+
+/*
+ * csi2_irq_status_set - Enables CSI2 Status IRQs.
+ * @enable: Enable/disable CSI2 Status interrupts
+ */
+static void csi2_irq_status_set(struct isp_device *isp,
+ struct isp_csi2_device *csi2, int enable)
+{
+ u32 reg;
+ reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ |
+ ISPCSI2_IRQSTATUS_CONTEXT(0);
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS);
+ if (enable)
+ reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE);
+ else
+ reg = 0;
+
+ isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE);
+}
+
+/*
+ * omap3isp_csi2_reset - Resets the CSI2 module.
+ *
+ * Must be called with the phy lock held.
+ *
+ * Returns 0 if successful, or -EBUSY if power command didn't respond.
+ */
+int omap3isp_csi2_reset(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+ u8 soft_reset_retries = 0;
+ u32 reg;
+ int i;
+
+ if (!csi2->available)
+ return -ENODEV;
+
+ if (csi2->phy->phy_in_use)
+ return -EBUSY;
+
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_SOFT_RESET);
+
+ do {
+ reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) &
+ ISPCSI2_SYSSTATUS_RESET_DONE;
+ if (reg == ISPCSI2_SYSSTATUS_RESET_DONE)
+ break;
+ soft_reset_retries++;
+ if (soft_reset_retries < 5)
+ udelay(100);
+ } while (soft_reset_retries < 5);
+
+ if (soft_reset_retries == 5) {
+ printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
+ return -EBUSY;
+ }
+
+ if (isp->revision == ISP_REVISION_15_0)
+ isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG,
+ ISPCSI2_PHY_CFG_RESET_CTRL);
+
+ i = 100;
+ do {
+ reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1)
+ & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK;
+ if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK)
+ break;
+ udelay(100);
+ } while (--i > 0);
+
+ if (i == 0) {
+ printk(KERN_ERR
+ "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
+ return -EBUSY;
+ }
+
+ if (isp->autoidle)
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+ ISPCSI2_SYSCONFIG_AUTO_IDLE,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART |
+ ((isp->revision == ISP_REVISION_15_0) ?
+ ISPCSI2_SYSCONFIG_AUTO_IDLE : 0));
+ else
+ isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
+ ISPCSI2_SYSCONFIG_AUTO_IDLE,
+ ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO);
+
+ return 0;
+}
+
+static int csi2_configure(struct isp_csi2_device *csi2)
+{
+ const struct isp_v4l2_subdevs_group *pdata;
+ struct isp_device *isp = csi2->isp;
+ struct isp_csi2_timing_cfg *timing = &csi2->timing[0];
+ struct v4l2_subdev *sensor;
+ struct media_pad *pad;
+
+ /*
+ * CSI2 fields that can be updated while the context has
+ * been enabled or the interface has been enabled are not
+ * updated dynamically currently. So we do not allow to
+ * reconfigure if either has been enabled
+ */
+ if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
+ return -EBUSY;
+
+ pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
+ sensor = media_entity_to_v4l2_subdev(pad->entity);
+ pdata = sensor->host_priv;
+
+ csi2->frame_skip = 0;
+ v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
+
+ csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
+ csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE;
+ csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
+
+ timing->ionum = 1;
+ timing->force_rx_mode = 1;
+ timing->stop_state_16x = 1;
+ timing->stop_state_4x = 1;
+ timing->stop_state_counter = 0x1FF;
+
+ /*
+ * The CSI2 receiver can't do any format conversion except DPCM
+ * decompression, so every set_format call configures both pads
+ * and enables DPCM decompression as a special case:
+ */
+ if (csi2->formats[CSI2_PAD_SINK].code !=
+ csi2->formats[CSI2_PAD_SOURCE].code)
+ csi2->dpcm_decompress = true;
+ else
+ csi2->dpcm_decompress = false;
+
+ csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
+
+ if (csi2->video_out.bpl_padding == 0)
+ csi2->contexts[0].data_offset = 0;
+ else
+ csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
+
+ /*
+ * Enable end of frame and end of line signals generation for
+ * context 0. These signals are generated from CSI2 receiver to
+ * qualify the last pixel of a frame and the last pixel of a line.
+ * Without enabling the signals CSI2 receiver writes data to memory
+ * beyond buffer size and/or data line offset is not handled correctly.
+ */
+ csi2->contexts[0].eof_enabled = 1;
+ csi2->contexts[0].eol_enabled = 1;
+
+ csi2_irq_complexio1_set(isp, csi2, 1);
+ csi2_irq_ctx_set(isp, csi2, 1);
+ csi2_irq_status_set(isp, csi2, 1);
+
+ /* Set configuration (timings, format and links) */
+ csi2_timing_config(isp, csi2, timing);
+ csi2_recv_config(isp, csi2, &csi2->ctrl);
+ csi2_ctx_config(isp, csi2, &csi2->contexts[0]);
+
+ return 0;
+}
+
+/*
+ * csi2_print_status - Prints CSI2 debug information.
+ */
+#define CSI2_PRINT_REGISTER(isp, regs, name)\
+ dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \
+ isp_reg_readl(isp, regs, ISPCSI2_##name))
+
+static void csi2_print_status(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+
+ if (!csi2->available)
+ return;
+
+ dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n");
+
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING);
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0));
+ CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0));
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+/*
+ * csi2_isr_buffer - Does buffer handling at end-of-frame
+ * when writing to memory.
+ */
+static void csi2_isr_buffer(struct isp_csi2_device *csi2)
+{
+ struct isp_device *isp = csi2->isp;
+ struct isp_buffer *buffer;
+
+ csi2_ctx_enable(isp, csi2, 0, 0);
+
+ buffer = omap3isp_video_buffer_next(&csi2->video_out);
+
+ /*
+ * Let video queue operation restart engine if there is an underrun
+ * condition.
+ */
+ if (buffer == NULL)
+ return;
+
+ csi2_set_outaddr(csi2, buffer->isp_addr);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+}
+
+static void csi2_isr_ctx(struct isp_csi2_device *csi2,
+ struct isp_csi2_ctx_cfg *ctx)
+{
+ struct isp_device *isp = csi2->isp;
+ unsigned int n = ctx->ctxnum;
+ u32 status;
+
+ status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
+ isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
+
+ if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ))
+ return;
+
+ /* Skip interrupts until we reach the frame skip count. The CSI2 will be
+ * automatically disabled, as the frame skip count has been programmed
+ * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
+ *
+ * It would have been nice to rely on the FRAME_NUMBER interrupt instead
+ * but it turned out that the interrupt is only generated when the CSI2
+ * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
+ * correctly and reaches 0 when data is forwarded to the video port only
+ * but no interrupt arrives). Maybe a CSI2 hardware bug.
+ */
+ if (csi2->frame_skip) {
+ csi2->frame_skip--;
+ if (csi2->frame_skip == 0) {
+ ctx->format_id = csi2_ctx_map_format(csi2);
+ csi2_ctx_config(isp, csi2, ctx);
+ csi2_ctx_enable(isp, csi2, n, 1);
+ }
+ return;
+ }
+
+ if (csi2->output & CSI2_OUTPUT_MEMORY)
+ csi2_isr_buffer(csi2);
+}
+
+/*
+ * omap3isp_csi2_isr - CSI2 interrupt handling.
+ */
+void omap3isp_csi2_isr(struct isp_csi2_device *csi2)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
+ u32 csi2_irqstatus, cpxio1_irqstatus;
+ struct isp_device *isp = csi2->isp;
+
+ if (!csi2->available)
+ return;
+
+ csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS);
+ isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS);
+
+ /* Failure Cases */
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) {
+ cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1,
+ ISPCSI2_PHY_IRQSTATUS);
+ isp_reg_writel(isp, cpxio1_irqstatus,
+ csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
+ dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ "
+ "%x\n", cpxio1_irqstatus);
+ pipe->error = true;
+ }
+
+ if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) {
+ dev_dbg(isp->dev, "CSI2 Err:"
+ " OCP:%d,"
+ " Short_pack:%d,"
+ " ECC:%d,"
+ " CPXIO2:%d,"
+ " FIFO_OVF:%d,"
+ "\n",
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0,
+ (csi2_irqstatus &
+ ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0);
+ pipe->error = true;
+ }
+
+ if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
+ return;
+
+ /* Successful cases */
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0))
+ csi2_isr_ctx(csi2, &csi2->contexts[0]);
+
+ if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ)
+ dev_dbg(isp->dev, "CSI2: ECC correction done\n");
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+/*
+ * csi2_queue - Queues the first buffer when using memory output
+ * @video: The video node
+ * @buffer: buffer to queue
+ */
+static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
+{
+ struct isp_device *isp = video->isp;
+ struct isp_csi2_device *csi2 = &isp->isp_csi2a;
+
+ csi2_set_outaddr(csi2, buffer->isp_addr);
+
+ /*
+ * If streaming was enabled before there was a buffer queued
+ * or underrun happened in the ISR, the hardware was not enabled
+ * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set.
+ * Enable it now.
+ */
+ if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
+ /* Enable / disable context 0 and IRQs */
+ csi2_if_enable(isp, csi2, 1);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+ isp_video_dmaqueue_flags_clr(&csi2->video_out);
+ }
+
+ return 0;
+}
+
+static const struct isp_video_operations csi2_ispvideo_ops = {
+ .queue = csi2_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct v4l2_mbus_framefmt *
+__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &csi2->formats[pad];
+}
+
+static void
+csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *info;
+ unsigned int i;
+
+ switch (pad) {
+ case CSI2_PAD_SINK:
+ /* Clamp the width and height to valid range (1-8191). */
+ for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
+ if (fmt->code == csi2_input_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(csi2_input_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+
+ fmt->width = clamp_t(u32, fmt->width, 1, 8191);
+ fmt->height = clamp_t(u32, fmt->height, 1, 8191);
+ break;
+
+ case CSI2_PAD_SOURCE:
+ /* Source format same as sink format, except for DPCM
+ * compression.
+ */
+ pixelcode = fmt->code;
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
+ memcpy(fmt, format, sizeof(*fmt));
+
+ /*
+ * Only Allow DPCM decompression, and check that the
+ * pattern is preserved
+ */
+ info = omap3isp_video_format_info(fmt->code);
+ if (info->uncompressed == pixelcode)
+ fmt->code = pixelcode;
+ break;
+ }
+
+ /* RGB, non-interlaced */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * csi2_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct isp_format_info *info;
+
+ if (code->pad == CSI2_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(csi2_input_fmts))
+ return -EINVAL;
+
+ code->code = csi2_input_fmts[code->index];
+ } else {
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+ switch (code->index) {
+ case 0:
+ /* Passthrough sink pad code */
+ code->code = format->code;
+ break;
+ case 1:
+ /* Uncompressed code */
+ info = omap3isp_video_format_info(format->code);
+ if (info->uncompressed == format->code)
+ return -EINVAL;
+
+ code->code = info->uncompressed;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int csi2_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * csi2_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * csi2_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == CSI2_PAD_SINK) {
+ format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * csi2_init_formats - Initialize formats on all pads
+ * @sd: ISP CSI2 V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = CSI2_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ csi2_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/*
+ * csi2_set_stream - Enable/Disable streaming on the CSI2 module
+ * @sd: ISP CSI2 V4L2 subdevice
+ * @enable: ISP pipeline stream state
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = csi2->isp;
+ struct isp_video *video_out = &csi2->video_out;
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (omap3isp_csiphy_acquire(csi2->phy) < 0)
+ return -ENODEV;
+ if (csi2->output & CSI2_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
+ csi2_configure(csi2);
+ csi2_print_status(csi2);
+
+ /*
+ * When outputting to memory with no buffer available, let the
+ * buffer queue handler start the hardware. A DMA queue flag
+ * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
+ * a buffer available.
+ */
+ if (csi2->output & CSI2_OUTPUT_MEMORY &&
+ !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED))
+ break;
+ /* Enable context 0 and IRQs */
+ atomic_set(&csi2->stopping, 0);
+ csi2_ctx_enable(isp, csi2, 0, 1);
+ csi2_if_enable(isp, csi2, 1);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (csi2->state == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+ if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait,
+ &csi2->stopping))
+ dev_dbg(isp->dev, "%s: module stop timeout.\n",
+ sd->name);
+ csi2_ctx_enable(isp, csi2, 0, 0);
+ csi2_if_enable(isp, csi2, 0);
+ csi2_irq_ctx_set(isp, csi2, 0);
+ omap3isp_csiphy_release(csi2->phy);
+ isp_video_dmaqueue_flags_clr(video_out);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
+ break;
+ }
+
+ csi2->state = enable;
+ return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops csi2_video_ops = {
+ .s_stream = csi2_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
+ .enum_mbus_code = csi2_enum_mbus_code,
+ .enum_frame_size = csi2_enum_frame_size,
+ .get_fmt = csi2_get_format,
+ .set_fmt = csi2_set_format,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops csi2_ops = {
+ .video = &csi2_video_ops,
+ .pad = &csi2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .open = csi2_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * csi2_link_setup - Setup CSI2 connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int csi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
+ struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_MEMORY)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_MEMORY;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_MEMORY;
+ }
+ break;
+
+ case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (csi2->output & ~CSI2_OUTPUT_CCDC)
+ return -EBUSY;
+ csi2->output |= CSI2_OUTPUT_CCDC;
+ } else {
+ csi2->output &= ~CSI2_OUTPUT_CCDC;
+ }
+ break;
+
+ default:
+ /* Link from camera to CSI2 is fixed... */
+ return -EINVAL;
+ }
+
+ ctrl->vp_only_enable =
+ (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
+ ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC);
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations csi2_media_ops = {
+ .link_setup = csi2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->subdev);
+ omap3isp_video_unregister(&csi2->video_out);
+}
+
+int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&csi2->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_csi2_unregister_entities(csi2);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP CSI2 initialisation and cleanup
+ */
+
+/*
+ * csi2_init_entities - Initialize subdev and media entity.
+ * @csi2: Pointer to csi2 structure.
+ * return -ENOMEM or zero on success
+ */
+static int csi2_init_entities(struct isp_csi2_device *csi2)
+{
+ struct v4l2_subdev *sd = &csi2->subdev;
+ struct media_pad *pads = csi2->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ v4l2_subdev_init(sd, &csi2_ops);
+ sd->internal_ops = &csi2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name));
+
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, csi2);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ me->ops = &csi2_media_ops;
+ ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ csi2_init_formats(sd, NULL);
+
+ /* Video device node */
+ csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ csi2->video_out.ops = &csi2_ispvideo_ops;
+ csi2->video_out.bpl_alignment = 32;
+ csi2->video_out.bpl_zero_padding = 1;
+ csi2->video_out.bpl_max = 0x1ffe0;
+ csi2->video_out.isp = csi2->isp;
+ csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
+
+ ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the CSI2 subdev to the video node. */
+ ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
+ &csi2->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&csi2->video_out);
+error_video:
+ media_entity_cleanup(&csi2->subdev.entity);
+ return ret;
+}
+
+/*
+ * omap3isp_csi2_init - Routine for module driver init
+ */
+int omap3isp_csi2_init(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+ struct isp_csi2_device *csi2c = &isp->isp_csi2c;
+ int ret;
+
+ csi2a->isp = isp;
+ csi2a->available = 1;
+ csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1;
+ csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2;
+ csi2a->phy = &isp->isp_csiphy2;
+ csi2a->state = ISP_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2a->wait);
+
+ ret = csi2_init_entities(csi2a);
+ if (ret < 0)
+ return ret;
+
+ if (isp->revision == ISP_REVISION_15_0) {
+ csi2c->isp = isp;
+ csi2c->available = 1;
+ csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1;
+ csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2;
+ csi2c->phy = &isp->isp_csiphy1;
+ csi2c->state = ISP_PIPELINE_STREAM_STOPPED;
+ init_waitqueue_head(&csi2c->wait);
+ }
+
+ return 0;
+}
+
+/*
+ * omap3isp_csi2_cleanup - Routine for module driver cleanup
+ */
+void omap3isp_csi2_cleanup(struct isp_device *isp)
+{
+ struct isp_csi2_device *csi2a = &isp->isp_csi2a;
+
+ omap3isp_video_cleanup(&csi2a->video_out);
+ media_entity_cleanup(&csi2a->subdev.entity);
+}
diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/platform/omap3isp/ispcsi2.h
index c57729b7e86e..c57729b7e86e 100644
--- a/drivers/media/video/omap3isp/ispcsi2.h
+++ b/drivers/media/platform/omap3isp/ispcsi2.h
diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/platform/omap3isp/ispcsiphy.c
index 348f67ebbbc9..348f67ebbbc9 100644
--- a/drivers/media/video/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/omap3isp/ispcsiphy.c
diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h
index e93a661e65d9..e93a661e65d9 100644
--- a/drivers/media/video/omap3isp/ispcsiphy.h
+++ b/drivers/media/platform/omap3isp/ispcsiphy.h
diff --git a/drivers/media/video/omap3isp/isph3a.h b/drivers/media/platform/omap3isp/isph3a.h
index fb09fd4ca755..fb09fd4ca755 100644
--- a/drivers/media/video/omap3isp/isph3a.h
+++ b/drivers/media/platform/omap3isp/isph3a.h
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/omap3isp/isph3a_aewb.c
new file mode 100644
index 000000000000..036e9961d027
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isph3a_aewb.c
@@ -0,0 +1,368 @@
+/*
+ * isph3a.c
+ *
+ * TI OMAP3 ISP - H3A module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+#include "isph3a.h"
+#include "ispstat.h"
+
+/*
+ * h3a_aewb_update_regs - Helper function to update h3a registers.
+ */
+static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
+{
+ struct omap3isp_h3a_aewb_config *conf = priv;
+ u32 pcr;
+ u32 win1;
+ u32 start;
+ u32 blk;
+ u32 subwin;
+
+ if (aewb->state == ISPSTAT_DISABLED)
+ return;
+
+ isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
+
+ if (!aewb->update)
+ return;
+
+ /* Converting config metadata into reg values */
+ pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT;
+ pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT;
+
+ win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT;
+ win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT;
+ win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT;
+ win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT;
+
+ start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT;
+ start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT;
+
+ blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT;
+ blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT;
+
+ subwin = ((conf->subsample_ver_inc >> 1) - 1) <<
+ ISPH3A_AEWSUBWIN_AEWINCV_SHIFT;
+ subwin |= ((conf->subsample_hor_inc >> 1) - 1) <<
+ ISPH3A_AEWSUBWIN_AEWINCH_SHIFT;
+
+ isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1);
+ isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AEWINSTART);
+ isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK);
+ isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AEWSUBWIN);
+ isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_MASK, pcr);
+
+ aewb->update = 0;
+ aewb->config_counter += aewb->inc_config;
+ aewb->inc_config = 0;
+ aewb->buf_size = conf->buf_size;
+}
+
+static void h3a_aewb_enable(struct ispstat *aewb, int enable)
+{
+ if (enable) {
+ isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_EN);
+ omap3isp_subclk_enable(aewb->isp, OMAP3_ISP_SUBCLK_AEWB);
+ } else {
+ isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AEW_EN);
+ omap3isp_subclk_disable(aewb->isp, OMAP3_ISP_SUBCLK_AEWB);
+ }
+}
+
+static int h3a_aewb_busy(struct ispstat *aewb)
+{
+ return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
+ & ISPH3A_PCR_BUSYAEAWB;
+}
+
+static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf)
+{
+ /* Number of configured windows + extra row for black data */
+ u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count;
+
+ /*
+ * Unsaturated block counts for each 8 windows.
+ * 1 extra for the last (win_count % 8) windows if win_count is not
+ * divisible by 8.
+ */
+ win_count += (win_count + 7) / 8;
+
+ return win_count * AEWB_PACKET_SIZE;
+}
+
+static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf)
+{
+ struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
+ u32 buf_size;
+
+ if (unlikely(user_cfg->saturation_limit >
+ OMAP3ISP_AEWB_MAX_SATURATION_LIM))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
+ user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
+ user_cfg->win_height & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W ||
+ user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W ||
+ user_cfg->win_width & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC ||
+ user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC ||
+ user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
+ user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
+ user_cfg->blk_win_height & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
+ user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
+ user_cfg->subsample_ver_inc & 0x01))
+ return -EINVAL;
+
+ if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
+ user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
+ user_cfg->subsample_hor_inc & 0x01))
+ return -EINVAL;
+
+ buf_size = h3a_aewb_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+/*
+ * h3a_aewb_set_params - Helper function to check & store user given params.
+ * @new_conf: Pointer to AE and AWB parameters struct.
+ *
+ * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to
+ * program them during ISR.
+ */
+static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf)
+{
+ struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
+ struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv;
+ int update = 0;
+
+ if (cur_cfg->saturation_limit != user_cfg->saturation_limit) {
+ cur_cfg->saturation_limit = user_cfg->saturation_limit;
+ update = 1;
+ }
+ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
+ cur_cfg->alaw_enable = user_cfg->alaw_enable;
+ update = 1;
+ }
+ if (cur_cfg->win_height != user_cfg->win_height) {
+ cur_cfg->win_height = user_cfg->win_height;
+ update = 1;
+ }
+ if (cur_cfg->win_width != user_cfg->win_width) {
+ cur_cfg->win_width = user_cfg->win_width;
+ update = 1;
+ }
+ if (cur_cfg->ver_win_count != user_cfg->ver_win_count) {
+ cur_cfg->ver_win_count = user_cfg->ver_win_count;
+ update = 1;
+ }
+ if (cur_cfg->hor_win_count != user_cfg->hor_win_count) {
+ cur_cfg->hor_win_count = user_cfg->hor_win_count;
+ update = 1;
+ }
+ if (cur_cfg->ver_win_start != user_cfg->ver_win_start) {
+ cur_cfg->ver_win_start = user_cfg->ver_win_start;
+ update = 1;
+ }
+ if (cur_cfg->hor_win_start != user_cfg->hor_win_start) {
+ cur_cfg->hor_win_start = user_cfg->hor_win_start;
+ update = 1;
+ }
+ if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) {
+ cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start;
+ update = 1;
+ }
+ if (cur_cfg->blk_win_height != user_cfg->blk_win_height) {
+ cur_cfg->blk_win_height = user_cfg->blk_win_height;
+ update = 1;
+ }
+ if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) {
+ cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc;
+ update = 1;
+ }
+ if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) {
+ cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc;
+ update = 1;
+ }
+
+ if (update || !aewb->configured) {
+ aewb->inc_config++;
+ aewb->update = 1;
+ cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg);
+ }
+}
+
+static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_AEWB_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ unsigned long *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static const struct ispstat_ops h3a_aewb_ops = {
+ .validate_params = h3a_aewb_validate_params,
+ .set_params = h3a_aewb_set_params,
+ .setup_regs = h3a_aewb_setup_regs,
+ .enable = h3a_aewb_enable,
+ .busy = h3a_aewb_busy,
+};
+
+static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = {
+ .ioctl = h3a_aewb_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = {
+ .core = &h3a_aewb_subdev_core_ops,
+ .video = &h3a_aewb_subdev_video_ops,
+};
+
+/*
+ * omap3isp_h3a_aewb_init - Module Initialisation.
+ */
+int omap3isp_h3a_aewb_init(struct isp_device *isp)
+{
+ struct ispstat *aewb = &isp->isp_aewb;
+ struct omap3isp_h3a_aewb_config *aewb_cfg;
+ struct omap3isp_h3a_aewb_config *aewb_recover_cfg;
+ int ret;
+
+ aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL);
+ if (!aewb_cfg)
+ return -ENOMEM;
+
+ memset(aewb, 0, sizeof(*aewb));
+ aewb->ops = &h3a_aewb_ops;
+ aewb->priv = aewb_cfg;
+ aewb->dma_ch = -1;
+ aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB;
+ aewb->isp = isp;
+
+ /* Set recover state configuration */
+ aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL);
+ if (!aewb_recover_cfg) {
+ dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for "
+ "recover configuration.\n");
+ ret = -ENOMEM;
+ goto err_recover_alloc;
+ }
+
+ aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM;
+ aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H;
+ aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W;
+ aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC;
+ aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC;
+ aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start +
+ aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count;
+ aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H;
+ aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
+ aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
+
+ if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) {
+ dev_err(aewb->isp->dev, "AEWB: recover configuration is "
+ "invalid.\n");
+ ret = -EINVAL;
+ goto err_conf;
+ }
+
+ aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg);
+ aewb->recover_priv = aewb_recover_cfg;
+
+ ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
+ if (ret)
+ goto err_conf;
+
+ return 0;
+
+err_conf:
+ kfree(aewb_recover_cfg);
+err_recover_alloc:
+ kfree(aewb_cfg);
+
+ return ret;
+}
+
+/*
+ * omap3isp_h3a_aewb_cleanup - Module exit.
+ */
+void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
+{
+ kfree(isp->isp_aewb.priv);
+ kfree(isp->isp_aewb.recover_priv);
+ omap3isp_stat_cleanup(&isp->isp_aewb);
+}
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/omap3isp/isph3a_af.c
new file mode 100644
index 000000000000..42ccce318d5d
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isph3a_af.c
@@ -0,0 +1,423 @@
+/*
+ * isph3a_af.c
+ *
+ * TI OMAP3 ISP - H3A AF module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/* Linux specific include files */
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#include "isp.h"
+#include "isph3a.h"
+#include "ispstat.h"
+
+#define IS_OUT_OF_BOUNDS(value, min, max) \
+ (((value) < (min)) || ((value) > (max)))
+
+static void h3a_af_setup_regs(struct ispstat *af, void *priv)
+{
+ struct omap3isp_h3a_af_config *conf = priv;
+ u32 pcr;
+ u32 pax1;
+ u32 pax2;
+ u32 paxstart;
+ u32 coef;
+ u32 base_coef_set0;
+ u32 base_coef_set1;
+ int index;
+
+ if (af->state == ISPSTAT_DISABLED)
+ return;
+
+ isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AFBUFST);
+
+ if (!af->update)
+ return;
+
+ /* Configure Hardware Registers */
+ pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT;
+ /* Set height in AFPAX1 */
+ pax1 |= (conf->paxel.height >> 1) - 1;
+ isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
+
+ /* Configure AFPAX2 Register */
+ /* Set Line Increment in AFPAX2 Register */
+ pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT;
+ /* Set Vertical Count */
+ pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT;
+ /* Set Horizontal Count */
+ pax2 |= (conf->paxel.h_cnt - 1);
+ isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
+
+ /* Configure PAXSTART Register */
+ /*Configure Horizontal Start */
+ paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT;
+ /* Configure Vertical Start */
+ paxstart |= conf->paxel.v_start;
+ isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A,
+ ISPH3A_AFPAXSTART);
+
+ /*SetIIRSH Register */
+ isp_reg_writel(af->isp, conf->iir.h_start,
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
+
+ base_coef_set0 = ISPH3A_AFCOEF010;
+ base_coef_set1 = ISPH3A_AFCOEF110;
+ for (index = 0; index <= 8; index += 2) {
+ /*Set IIR Filter0 Coefficients */
+ coef = 0;
+ coef |= conf->iir.coeff_set0[index];
+ coef |= conf->iir.coeff_set0[index + 1] <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
+ base_coef_set0);
+ base_coef_set0 += AFCOEF_OFFSET;
+
+ /*Set IIR Filter1 Coefficients */
+ coef = 0;
+ coef |= conf->iir.coeff_set1[index];
+ coef |= conf->iir.coeff_set1[index + 1] <<
+ AF_COEF_SHIFT;
+ isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
+ base_coef_set1);
+ base_coef_set1 += AFCOEF_OFFSET;
+ }
+ /* set AFCOEF0010 Register */
+ isp_reg_writel(af->isp, conf->iir.coeff_set0[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
+ /* set AFCOEF1010 Register */
+ isp_reg_writel(af->isp, conf->iir.coeff_set1[10],
+ OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
+
+ /* PCR Register */
+ /* Set RGB Position */
+ pcr = conf->rgb_pos << AF_RGBPOS_SHIFT;
+ /* Set Accumulator Mode */
+ if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK)
+ pcr |= AF_FVMODE;
+ /* Set A-law */
+ if (conf->alaw_enable)
+ pcr |= AF_ALAW_EN;
+ /* HMF Configurations */
+ if (conf->hmf.enable) {
+ /* Enable HMF */
+ pcr |= AF_MED_EN;
+ /* Set Median Threshold */
+ pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT;
+ }
+ /* Set PCR Register */
+ isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ AF_PCR_MASK, pcr);
+
+ af->update = 0;
+ af->config_counter += af->inc_config;
+ af->inc_config = 0;
+ af->buf_size = conf->buf_size;
+}
+
+static void h3a_af_enable(struct ispstat *af, int enable)
+{
+ if (enable) {
+ isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AF_EN);
+ omap3isp_subclk_enable(af->isp, OMAP3_ISP_SUBCLK_AF);
+ } else {
+ isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
+ ISPH3A_PCR_AF_EN);
+ omap3isp_subclk_disable(af->isp, OMAP3_ISP_SUBCLK_AF);
+ }
+}
+
+static int h3a_af_busy(struct ispstat *af)
+{
+ return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
+ & ISPH3A_PCR_BUSYAF;
+}
+
+static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf)
+{
+ return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE;
+}
+
+/* Function to check paxel parameters */
+static int h3a_af_validate_params(struct ispstat *af, void *new_conf)
+{
+ struct omap3isp_h3a_af_config *user_cfg = new_conf;
+ struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel;
+ struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir;
+ int index;
+ u32 buf_size;
+
+ /* Check horizontal Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt,
+ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN,
+ OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX))
+ return -EINVAL;
+
+ /* Check Vertical Count */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt,
+ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN,
+ OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX))
+ return -EINVAL;
+
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN,
+ OMAP3ISP_AF_PAXEL_HEIGHT_MAX) ||
+ paxel_cfg->height % 2)
+ return -EINVAL;
+
+ /* Check width */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN,
+ OMAP3ISP_AF_PAXEL_WIDTH_MAX) ||
+ paxel_cfg->width % 2)
+ return -EINVAL;
+
+ /* Check Line Increment */
+ if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc,
+ OMAP3ISP_AF_PAXEL_INCREMENT_MIN,
+ OMAP3ISP_AF_PAXEL_INCREMENT_MAX) ||
+ paxel_cfg->line_inc % 2)
+ return -EINVAL;
+
+ /* Check Horizontal Start */
+ if ((paxel_cfg->h_start < iir_cfg->h_start) ||
+ IS_OUT_OF_BOUNDS(paxel_cfg->h_start,
+ OMAP3ISP_AF_PAXEL_HZSTART_MIN,
+ OMAP3ISP_AF_PAXEL_HZSTART_MAX))
+ return -EINVAL;
+
+ /* Check IIR */
+ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
+ if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX)
+ return -EINVAL;
+
+ if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX)
+ return -EINVAL;
+ }
+
+ if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN,
+ OMAP3ISP_AF_IIRSH_MAX))
+ return -EINVAL;
+
+ /* Hack: If paxel size is 12, the 10th AF window may be corrupted */
+ if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) &&
+ (paxel_cfg->width * paxel_cfg->height == 12))
+ return -EINVAL;
+
+ buf_size = h3a_af_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ /* User buf_size request wasn't enough */
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+/* Update local parameters */
+static void h3a_af_set_params(struct ispstat *af, void *new_conf)
+{
+ struct omap3isp_h3a_af_config *user_cfg = new_conf;
+ struct omap3isp_h3a_af_config *cur_cfg = af->priv;
+ int update = 0;
+ int index;
+
+ /* alaw */
+ if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
+ update = 1;
+ goto out;
+ }
+
+ /* hmf */
+ if (cur_cfg->hmf.enable != user_cfg->hmf.enable) {
+ update = 1;
+ goto out;
+ }
+ if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) {
+ update = 1;
+ goto out;
+ }
+
+ /* rgbpos */
+ if (cur_cfg->rgb_pos != user_cfg->rgb_pos) {
+ update = 1;
+ goto out;
+ }
+
+ /* iir */
+ if (cur_cfg->iir.h_start != user_cfg->iir.h_start) {
+ update = 1;
+ goto out;
+ }
+ for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
+ if (cur_cfg->iir.coeff_set0[index] !=
+ user_cfg->iir.coeff_set0[index]) {
+ update = 1;
+ goto out;
+ }
+ if (cur_cfg->iir.coeff_set1[index] !=
+ user_cfg->iir.coeff_set1[index]) {
+ update = 1;
+ goto out;
+ }
+ }
+
+ /* paxel */
+ if ((cur_cfg->paxel.width != user_cfg->paxel.width) ||
+ (cur_cfg->paxel.height != user_cfg->paxel.height) ||
+ (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) ||
+ (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) ||
+ (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) ||
+ (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) ||
+ (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) {
+ update = 1;
+ goto out;
+ }
+
+ /* af_mode */
+ if (cur_cfg->fvmode != user_cfg->fvmode)
+ update = 1;
+
+out:
+ if (update || !af->configured) {
+ memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg));
+ af->inc_config++;
+ af->update = 1;
+ /*
+ * User might be asked for a bigger buffer than necessary for
+ * this configuration. In order to return the right amount of
+ * data during buffer request, let's calculate the size here
+ * instead of stick with user_cfg->buf_size.
+ */
+ cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg);
+ }
+}
+
+static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_AF_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ int *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+
+}
+
+static const struct ispstat_ops h3a_af_ops = {
+ .validate_params = h3a_af_validate_params,
+ .set_params = h3a_af_set_params,
+ .setup_regs = h3a_af_setup_regs,
+ .enable = h3a_af_enable,
+ .busy = h3a_af_busy,
+};
+
+static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = {
+ .ioctl = h3a_af_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops h3a_af_subdev_ops = {
+ .core = &h3a_af_subdev_core_ops,
+ .video = &h3a_af_subdev_video_ops,
+};
+
+/* Function to register the AF character device driver. */
+int omap3isp_h3a_af_init(struct isp_device *isp)
+{
+ struct ispstat *af = &isp->isp_af;
+ struct omap3isp_h3a_af_config *af_cfg;
+ struct omap3isp_h3a_af_config *af_recover_cfg;
+ int ret;
+
+ af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL);
+ if (af_cfg == NULL)
+ return -ENOMEM;
+
+ memset(af, 0, sizeof(*af));
+ af->ops = &h3a_af_ops;
+ af->priv = af_cfg;
+ af->dma_ch = -1;
+ af->event_type = V4L2_EVENT_OMAP3ISP_AF;
+ af->isp = isp;
+
+ /* Set recover state configuration */
+ af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL);
+ if (!af_recover_cfg) {
+ dev_err(af->isp->dev, "AF: cannot allocate memory for recover "
+ "configuration.\n");
+ ret = -ENOMEM;
+ goto err_recover_alloc;
+ }
+
+ af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
+ af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN;
+ af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN;
+ af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN;
+ af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN;
+ af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN;
+ if (h3a_af_validate_params(af, af_recover_cfg)) {
+ dev_err(af->isp->dev, "AF: recover configuration is "
+ "invalid.\n");
+ ret = -EINVAL;
+ goto err_conf;
+ }
+
+ af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
+ af->recover_priv = af_recover_cfg;
+
+ ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
+ if (ret)
+ goto err_conf;
+
+ return 0;
+
+err_conf:
+ kfree(af_recover_cfg);
+err_recover_alloc:
+ kfree(af_cfg);
+
+ return ret;
+}
+
+void omap3isp_h3a_af_cleanup(struct isp_device *isp)
+{
+ kfree(isp->isp_af.priv);
+ kfree(isp->isp_af.recover_priv);
+ omap3isp_stat_cleanup(&isp->isp_af);
+}
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/omap3isp/isphist.c
new file mode 100644
index 000000000000..d1a8dee5e1ca
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isphist.c
@@ -0,0 +1,518 @@
+/*
+ * isphist.c
+ *
+ * TI OMAP3 ISP - Histogram module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isphist.h"
+
+#define HIST_CONFIG_DMA 1
+
+#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0)
+
+/*
+ * hist_reset_mem - clear Histogram memory before start stats engine.
+ */
+static void hist_reset_mem(struct ispstat *hist)
+{
+ struct isp_device *isp = hist->isp;
+ struct omap3isp_hist_config *conf = hist->priv;
+ unsigned int i;
+
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+
+ /*
+ * By setting it, the histogram internal buffer is being cleared at the
+ * same time it's being read. This bit must be cleared afterwards.
+ */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ /*
+ * We'll clear 4 words at each iteration for optimization. It avoids
+ * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4.
+ */
+ for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) {
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ }
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ hist->wait_acc_frames = conf->num_acc_frames;
+}
+
+static void hist_dma_config(struct ispstat *hist)
+{
+ hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32;
+ hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT;
+ hist->dma_config.frame_count = 1;
+ hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT;
+ hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA;
+ hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC;
+ hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
+}
+
+/*
+ * hist_setup_regs - Helper function to update Histogram registers.
+ */
+static void hist_setup_regs(struct ispstat *hist, void *priv)
+{
+ struct isp_device *isp = hist->isp;
+ struct omap3isp_hist_config *conf = priv;
+ int c;
+ u32 cnt;
+ u32 wb_gain;
+ u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS];
+ u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS];
+
+ if (!hist->update || hist->state == ISPSTAT_DISABLED ||
+ hist->state == ISPSTAT_DISABLING)
+ return;
+
+ cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT;
+
+ wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT;
+ wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT;
+ wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT;
+ if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER)
+ wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT;
+
+ /* Regions size and position */
+ for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) {
+ if (c < conf->num_regions) {
+ reg_hor[c] = conf->region[c].h_start <<
+ ISPHIST_REG_START_SHIFT;
+ reg_hor[c] = conf->region[c].h_end <<
+ ISPHIST_REG_END_SHIFT;
+ reg_ver[c] = conf->region[c].v_start <<
+ ISPHIST_REG_START_SHIFT;
+ reg_ver[c] = conf->region[c].v_end <<
+ ISPHIST_REG_END_SHIFT;
+ } else {
+ reg_hor[c] = 0;
+ reg_ver[c] = 0;
+ }
+ }
+
+ cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT;
+ switch (conf->hist_bins) {
+ case OMAP3ISP_HIST_BINS_256:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ case OMAP3ISP_HIST_BINS_128:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ case OMAP3ISP_HIST_BINS_64:
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ default: /* OMAP3ISP_HIST_BINS_32 */
+ cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) <<
+ ISPHIST_CNT_SHIFT_SHIFT;
+ break;
+ }
+
+ hist_reset_mem(hist);
+
+ isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT);
+ isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN);
+ isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ);
+ isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT);
+ isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ);
+ isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT);
+ isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ);
+ isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT);
+ isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ);
+ isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT);
+
+ hist->update = 0;
+ hist->config_counter += hist->inc_config;
+ hist->inc_config = 0;
+ hist->buf_size = conf->buf_size;
+}
+
+static void hist_enable(struct ispstat *hist, int enable)
+{
+ if (enable) {
+ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
+ ISPHIST_PCR_ENABLE);
+ omap3isp_subclk_enable(hist->isp, OMAP3_ISP_SUBCLK_HIST);
+ } else {
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
+ ISPHIST_PCR_ENABLE);
+ omap3isp_subclk_disable(hist->isp, OMAP3_ISP_SUBCLK_HIST);
+ }
+}
+
+static int hist_busy(struct ispstat *hist)
+{
+ return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR)
+ & ISPHIST_PCR_BUSY;
+}
+
+static void hist_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct ispstat *hist = data;
+
+ if (ch_status & ~OMAP_DMA_BLOCK_IRQ) {
+ dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n",
+ ch_status);
+ omap_stop_dma(lch);
+ hist_reset_mem(hist);
+ atomic_set(&hist->buf_err, 1);
+ }
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+
+ omap3isp_stat_dma_isr(hist);
+ if (hist->state != ISPSTAT_DISABLED)
+ omap3isp_hist_dma_done(hist->isp);
+}
+
+static int hist_buf_dma(struct ispstat *hist)
+{
+ dma_addr_t dma_addr = hist->active_buf->dma_addr;
+
+ if (unlikely(!dma_addr)) {
+ dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n");
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+ isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+ omap3isp_flush(hist->isp);
+ hist->dma_config.dst_start = dma_addr;
+ hist->dma_config.elem_count = hist->buf_size / sizeof(u32);
+ omap_set_dma_params(hist->dma_ch, &hist->dma_config);
+
+ omap_start_dma(hist->dma_ch);
+
+ return STAT_BUF_WAITING_DMA;
+}
+
+static int hist_buf_pio(struct ispstat *hist)
+{
+ struct isp_device *isp = hist->isp;
+ u32 *buf = hist->active_buf->virt_addr;
+ unsigned int i;
+
+ if (!buf) {
+ dev_dbg(isp->dev, "hist: invalid PIO buffer address\n");
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
+
+ /*
+ * By setting it, the histogram internal buffer is being cleared at the
+ * same time it's being read. This bit must be cleared just after all
+ * data is acquired.
+ */
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
+
+ /*
+ * We'll read 4 times a 4-bytes-word at each iteration for
+ * optimization. It avoids 3/4 of the jumps. We also know buf_size is
+ * divisible by 16.
+ */
+ for (i = hist->buf_size / 16; i > 0; i--) {
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
+ }
+ isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
+ ISPHIST_CNT_CLEAR);
+
+ return STAT_BUF_DONE;
+}
+
+/*
+ * hist_buf_process - Callback from ISP driver for HIST interrupt.
+ */
+static int hist_buf_process(struct ispstat *hist)
+{
+ struct omap3isp_hist_config *user_cfg = hist->priv;
+ int ret;
+
+ if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) {
+ hist_reset_mem(hist);
+ return STAT_NO_BUF;
+ }
+
+ if (--(hist->wait_acc_frames))
+ return STAT_NO_BUF;
+
+ if (HIST_USING_DMA(hist))
+ ret = hist_buf_dma(hist);
+ else
+ ret = hist_buf_pio(hist);
+
+ hist->wait_acc_frames = user_cfg->num_acc_frames;
+
+ return ret;
+}
+
+static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)
+{
+ return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions;
+}
+
+/*
+ * hist_validate_params - Helper function to check user given params.
+ * @user_cfg: Pointer to user configuration structure.
+ *
+ * Returns 0 on success configuration.
+ */
+static int hist_validate_params(struct ispstat *hist, void *new_conf)
+{
+ struct omap3isp_hist_config *user_cfg = new_conf;
+ int c;
+ u32 buf_size;
+
+ if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3)
+ return -EINVAL;
+
+ /* Regions size and position */
+
+ if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) ||
+ (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS))
+ return -EINVAL;
+
+ /* Regions */
+ for (c = 0; c < user_cfg->num_regions; c++) {
+ if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK)
+ return -EINVAL;
+ if (user_cfg->region[c].h_start > user_cfg->region[c].h_end)
+ return -EINVAL;
+ if (user_cfg->region[c].v_start > user_cfg->region[c].v_end)
+ return -EINVAL;
+ }
+
+ switch (user_cfg->num_regions) {
+ case 1:
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256)
+ return -EINVAL;
+ break;
+ case 2:
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128)
+ return -EINVAL;
+ break;
+ default: /* 3 or 4 */
+ if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64)
+ return -EINVAL;
+ break;
+ }
+
+ buf_size = hist_get_buf_size(user_cfg);
+ if (buf_size > user_cfg->buf_size)
+ /* User's buf_size request wasn't enoght */
+ user_cfg->buf_size = buf_size;
+ else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)
+ user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE;
+
+ return 0;
+}
+
+static int hist_comp_params(struct ispstat *hist,
+ struct omap3isp_hist_config *user_cfg)
+{
+ struct omap3isp_hist_config *cur_cfg = hist->priv;
+ int c;
+
+ if (cur_cfg->cfa != user_cfg->cfa)
+ return 1;
+
+ if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames)
+ return 1;
+
+ if (cur_cfg->hist_bins != user_cfg->hist_bins)
+ return 1;
+
+ for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) {
+ if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3)
+ break;
+ else if (cur_cfg->wg[c] != user_cfg->wg[c])
+ return 1;
+ }
+
+ if (cur_cfg->num_regions != user_cfg->num_regions)
+ return 1;
+
+ /* Regions */
+ for (c = 0; c < user_cfg->num_regions; c++) {
+ if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start)
+ return 1;
+ if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end)
+ return 1;
+ if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start)
+ return 1;
+ if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * hist_update_params - Helper function to check and store user given params.
+ * @new_conf: Pointer to user configuration structure.
+ */
+static void hist_set_params(struct ispstat *hist, void *new_conf)
+{
+ struct omap3isp_hist_config *user_cfg = new_conf;
+ struct omap3isp_hist_config *cur_cfg = hist->priv;
+
+ if (!hist->configured || hist_comp_params(hist, user_cfg)) {
+ memcpy(cur_cfg, user_cfg, sizeof(*user_cfg));
+ if (user_cfg->num_acc_frames == 0)
+ user_cfg->num_acc_frames = 1;
+ hist->inc_config++;
+ hist->update = 1;
+ /*
+ * User might be asked for a bigger buffer than necessary for
+ * this configuration. In order to return the right amount of
+ * data during buffer request, let's calculate the size here
+ * instead of stick with user_cfg->buf_size.
+ */
+ cur_cfg->buf_size = hist_get_buf_size(cur_cfg);
+
+ }
+}
+
+static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_HIST_CFG:
+ return omap3isp_stat_config(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_REQ:
+ return omap3isp_stat_request_statistics(stat, arg);
+ case VIDIOC_OMAP3ISP_STAT_EN: {
+ int *en = arg;
+ return omap3isp_stat_enable(stat, !!*en);
+ }
+ }
+
+ return -ENOIOCTLCMD;
+
+}
+
+static const struct ispstat_ops hist_ops = {
+ .validate_params = hist_validate_params,
+ .set_params = hist_set_params,
+ .setup_regs = hist_setup_regs,
+ .enable = hist_enable,
+ .busy = hist_busy,
+ .buf_process = hist_buf_process,
+};
+
+static const struct v4l2_subdev_core_ops hist_subdev_core_ops = {
+ .ioctl = hist_ioctl,
+ .subscribe_event = omap3isp_stat_subscribe_event,
+ .unsubscribe_event = omap3isp_stat_unsubscribe_event,
+};
+
+static const struct v4l2_subdev_video_ops hist_subdev_video_ops = {
+ .s_stream = omap3isp_stat_s_stream,
+};
+
+static const struct v4l2_subdev_ops hist_subdev_ops = {
+ .core = &hist_subdev_core_ops,
+ .video = &hist_subdev_video_ops,
+};
+
+/*
+ * omap3isp_hist_init - Module Initialization.
+ */
+int omap3isp_hist_init(struct isp_device *isp)
+{
+ struct ispstat *hist = &isp->isp_hist;
+ struct omap3isp_hist_config *hist_cfg;
+ int ret = -1;
+
+ hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL);
+ if (hist_cfg == NULL)
+ return -ENOMEM;
+
+ memset(hist, 0, sizeof(*hist));
+ if (HIST_CONFIG_DMA)
+ ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST",
+ hist_dma_cb, hist, &hist->dma_ch);
+ if (ret) {
+ if (HIST_CONFIG_DMA)
+ dev_warn(isp->dev, "hist: DMA request channel failed. "
+ "Using PIO only.\n");
+ hist->dma_ch = -1;
+ } else {
+ dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch);
+ hist_dma_config(hist);
+ omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ);
+ }
+
+ hist->ops = &hist_ops;
+ hist->priv = hist_cfg;
+ hist->event_type = V4L2_EVENT_OMAP3ISP_HIST;
+ hist->isp = isp;
+
+ ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
+ if (ret) {
+ kfree(hist_cfg);
+ if (HIST_USING_DMA(hist))
+ omap_free_dma(hist->dma_ch);
+ }
+
+ return ret;
+}
+
+/*
+ * omap3isp_hist_cleanup - Module cleanup.
+ */
+void omap3isp_hist_cleanup(struct isp_device *isp)
+{
+ if (HIST_USING_DMA(&isp->isp_hist))
+ omap_free_dma(isp->isp_hist.dma_ch);
+ kfree(isp->isp_hist.priv);
+ omap3isp_stat_cleanup(&isp->isp_hist);
+}
diff --git a/drivers/media/video/omap3isp/isphist.h b/drivers/media/platform/omap3isp/isphist.h
index 0b2a38ec94c4..0b2a38ec94c4 100644
--- a/drivers/media/video/omap3isp/isphist.h
+++ b/drivers/media/platform/omap3isp/isphist.h
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/omap3isp/isppreview.c
new file mode 100644
index 000000000000..1ae1c0909ed1
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isppreview.c
@@ -0,0 +1,2348 @@
+/*
+ * isppreview.c
+ *
+ * TI OMAP3 ISP driver - Preview module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "isppreview.h"
+
+/* Default values in Office Fluorescent Light for RGBtoRGB Blending */
+static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
+ { /* RGB-RGB Matrix */
+ {0x01E2, 0x0F30, 0x0FEE},
+ {0x0F9B, 0x01AC, 0x0FB9},
+ {0x0FE0, 0x0EC0, 0x0260}
+ }, /* RGB Offset */
+ {0x0000, 0x0000, 0x0000}
+};
+
+/* Default values in Office Fluorescent Light for RGB to YUV Conversion*/
+static struct omap3isp_prev_csc flr_prev_csc = {
+ { /* CSC Coef Matrix */
+ {66, 129, 25},
+ {-38, -75, 112},
+ {112, -94 , -18}
+ }, /* CSC Offset */
+ {0x0, 0x0, 0x0}
+};
+
+/* Default values in Office Fluorescent Light for CFA Gradient*/
+#define FLR_CFA_GRADTHRS_HORZ 0x28
+#define FLR_CFA_GRADTHRS_VERT 0x28
+
+/* Default values in Office Fluorescent Light for Chroma Suppression*/
+#define FLR_CSUP_GAIN 0x0D
+#define FLR_CSUP_THRES 0xEB
+
+/* Default values in Office Fluorescent Light for Noise Filter*/
+#define FLR_NF_STRGTH 0x03
+
+/* Default values for White Balance */
+#define FLR_WBAL_DGAIN 0x100
+#define FLR_WBAL_COEF 0x20
+
+/* Default values in Office Fluorescent Light for Black Adjustment*/
+#define FLR_BLKADJ_BLUE 0x0
+#define FLR_BLKADJ_GREEN 0x0
+#define FLR_BLKADJ_RED 0x0
+
+#define DEF_DETECT_CORRECT_VAL 0xe
+
+/*
+ * Margins and image size limits.
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which filters are enabled. To avoid format changes when the filters are
+ * enabled or disabled (which would prevent them from being turned on or off
+ * during streaming), the driver assumes all the filters are enabled when
+ * computing sink crop and source format limits.
+ *
+ * If a filter is disabled, additional cropping is automatically added at the
+ * preview engine input by the driver to avoid overflow at line and frame end.
+ * This is completely transparent for applications.
+ *
+ * Median filter 4 pixels
+ * Noise filter,
+ * Faulty pixels correction 4 pixels, 4 lines
+ * CFA filter 4 pixels, 4 lines in Bayer mode
+ * 2 lines in other modes
+ * Color suppression 2 pixels
+ * or luma enhancement
+ * -------------------------------------------------------------
+ * Maximum total 14 pixels, 8 lines
+ *
+ * The color suppression and luma enhancement filters are applied after bayer to
+ * YUV conversion. They thus can crop one pixel on the left and one pixel on the
+ * right side of the image without changing the color pattern. When both those
+ * filters are disabled, the driver must crop the two pixels on the same side of
+ * the image to avoid changing the bayer pattern. The left margin is thus set to
+ * 8 pixels and the right margin to 6 pixels.
+ */
+
+#define PREV_MARGIN_LEFT 8
+#define PREV_MARGIN_RIGHT 6
+#define PREV_MARGIN_TOP 4
+#define PREV_MARGIN_BOTTOM 4
+
+#define PREV_MIN_IN_WIDTH 64
+#define PREV_MIN_IN_HEIGHT 8
+#define PREV_MAX_IN_HEIGHT 16384
+
+#define PREV_MIN_OUT_WIDTH 0
+#define PREV_MIN_OUT_HEIGHT 0
+#define PREV_MAX_OUT_WIDTH_REV_1 1280
+#define PREV_MAX_OUT_WIDTH_REV_2 3300
+#define PREV_MAX_OUT_WIDTH_REV_15 4096
+
+/*
+ * Coeficient Tables for the submodules in Preview.
+ * Array is initialised with the values from.the tables text file.
+ */
+
+/*
+ * CFA Filter Coefficient Table
+ *
+ */
+static u32 cfa_coef_table[4][OMAP3ISP_PREV_CFA_BLK_SIZE] = {
+#include "cfa_coef_table.h"
+};
+
+/*
+ * Default Gamma Correction Table - All components
+ */
+static u32 gamma_table[] = {
+#include "gamma_table.h"
+};
+
+/*
+ * Noise Filter Threshold table
+ */
+static u32 noise_filter_table[] = {
+#include "noise_filter_table.h"
+};
+
+/*
+ * Luminance Enhancement Table
+ */
+static u32 luma_enhance_table[] = {
+#include "luma_enhance_table.h"
+};
+
+/*
+ * preview_config_luma_enhancement - Configure the Luminance Enhancement table
+ */
+static void
+preview_config_luma_enhancement(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_luma *yt = &params->luma;
+ unsigned int i;
+
+ isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) {
+ isp_reg_writel(isp, yt->table[i],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_enable_luma_enhancement - Enable/disable Luminance Enhancement
+ */
+static void
+preview_enable_luma_enhancement(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YNENHEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YNENHEN);
+}
+
+/*
+ * preview_enable_invalaw - Enable/disable Inverse A-Law decompression
+ */
+static void preview_enable_invalaw(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
+}
+
+/*
+ * preview_config_hmed - Configure the Horizontal Median Filter
+ */
+static void preview_config_hmed(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_hmed *hmed = &params->hmed;
+
+ isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) |
+ (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) |
+ (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED);
+}
+
+/*
+ * preview_enable_hmed - Enable/disable the Horizontal Median Filter
+ */
+static void preview_enable_hmed(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_HMEDEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_HMEDEN);
+}
+
+/*
+ * preview_config_cfa - Configure CFA Interpolation for Bayer formats
+ *
+ * The CFA table is organised in four blocks, one per Bayer component. The
+ * hardware expects blocks to follow the Bayer order of the input data, while
+ * the driver stores the table in GRBG order in memory. The blocks need to be
+ * reordered to support non-GRBG Bayer patterns.
+ */
+static void preview_config_cfa(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ static const unsigned int cfa_coef_order[4][4] = {
+ { 0, 1, 2, 3 }, /* GRBG */
+ { 1, 0, 3, 2 }, /* RGGB */
+ { 2, 3, 0, 1 }, /* BGGR */
+ { 3, 2, 1, 0 }, /* GBRG */
+ };
+ const unsigned int *order = cfa_coef_order[prev->params.cfa_order];
+ const struct omap3isp_prev_cfa *cfa = &params->cfa;
+ struct isp_device *isp = to_isp_device(prev);
+ unsigned int i;
+ unsigned int j;
+
+ isp_reg_writel(isp,
+ (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) |
+ (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA);
+
+ isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+
+ for (i = 0; i < 4; ++i) {
+ const __u32 *block = cfa->table[order[i]];
+
+ for (j = 0; j < OMAP3ISP_PREV_CFA_BLK_SIZE; ++j)
+ isp_reg_writel(isp, block[j], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_config_chroma_suppression - Configure Chroma Suppression
+ */
+static void
+preview_config_chroma_suppression(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_csup *cs = &params->csup;
+
+ isp_reg_writel(isp,
+ cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) |
+ (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP);
+}
+
+/*
+ * preview_enable_chroma_suppression - Enable/disable Chrominance Suppression
+ */
+static void
+preview_enable_chroma_suppression(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SUPEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SUPEN);
+}
+
+/*
+ * preview_config_whitebalance - Configure White Balance parameters
+ *
+ * Coefficient matrix always with default values.
+ */
+static void
+preview_config_whitebalance(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_wbal *wbal = &params->wbal;
+ u32 val;
+
+ isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN);
+
+ val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT;
+ val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT;
+ val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT;
+ val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN);
+
+ isp_reg_writel(isp,
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT |
+ ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT |
+ ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT |
+ ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT |
+ ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL);
+}
+
+/*
+ * preview_config_blkadj - Configure Black Adjustment
+ */
+static void
+preview_config_blkadj(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_blkadj *blkadj = &params->blkadj;
+
+ isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) |
+ (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) |
+ (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT),
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF);
+}
+
+/*
+ * preview_config_rgb_blending - Configure RGB-RGB Blending
+ */
+static void
+preview_config_rgb_blending(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_rgbtorgb *rgbrgb = &params->rgb2rgb;
+ u32 val;
+
+ val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT;
+ val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1);
+
+ val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT;
+ val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2);
+
+ val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT;
+ val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3);
+
+ val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT;
+ val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4);
+
+ val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5);
+
+ val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT;
+ val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1);
+
+ val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2);
+}
+
+/*
+ * preview_config_csc - Configure Color Space Conversion (RGB to YCbYCr)
+ */
+static void
+preview_config_csc(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_csc *csc = &params->csc;
+ u32 val;
+
+ val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT;
+ val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT;
+ val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0);
+
+ val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT;
+ val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT;
+ val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1);
+
+ val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT;
+ val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT;
+ val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2);
+
+ val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT;
+ val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT;
+ val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT;
+ isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET);
+}
+
+/*
+ * preview_config_yc_range - Configure the max and min Y and C values
+ */
+static void
+preview_config_yc_range(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_yclimit *yc = &params->yclimit;
+
+ isp_reg_writel(isp,
+ yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT |
+ yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT |
+ yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT |
+ yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC);
+}
+
+/*
+ * preview_config_dcor - Configure Couplet Defect Correction
+ */
+static void
+preview_config_dcor(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_dcor *dcor = &params->dcor;
+
+ isp_reg_writel(isp, dcor->detect_correct[0],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0);
+ isp_reg_writel(isp, dcor->detect_correct[1],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1);
+ isp_reg_writel(isp, dcor->detect_correct[2],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2);
+ isp_reg_writel(isp, dcor->detect_correct[3],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCCOUP,
+ dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0);
+}
+
+/*
+ * preview_enable_dcor - Enable/disable Couplet Defect Correction
+ */
+static void preview_enable_dcor(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCOREN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DCOREN);
+}
+
+/*
+ * preview_enable_drkframe_capture - Enable/disable Dark Frame Capture
+ */
+static void
+preview_enable_drkframe_capture(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFCAP);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFCAP);
+}
+
+/*
+ * preview_enable_drkframe - Enable/disable Dark Frame Subtraction
+ */
+static void preview_enable_drkframe(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_DRKFEN);
+}
+
+/*
+ * preview_config_noisefilter - Configure the Noise Filter
+ */
+static void
+preview_config_noisefilter(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_nf *nf = &params->nf;
+ unsigned int i;
+
+ isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF);
+ isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) {
+ isp_reg_writel(isp, nf->table[i],
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
+ }
+}
+
+/*
+ * preview_enable_noisefilter - Enable/disable the Noise Filter
+ */
+static void
+preview_enable_noisefilter(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_NFEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_NFEN);
+}
+
+/*
+ * preview_config_gammacorrn - Configure the Gamma Correction tables
+ */
+static void
+preview_config_gammacorrn(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ const struct omap3isp_prev_gtables *gt = &params->gamma;
+ unsigned int i;
+
+ isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+
+ isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+
+ isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
+ for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
+ isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_SET_TBL_DATA);
+}
+
+/*
+ * preview_enable_gammacorrn - Enable/disable Gamma Correction
+ *
+ * When gamma correction is disabled, the module is bypassed and its output is
+ * the 8 MSB of the 10-bit input .
+ */
+static void
+preview_enable_gammacorrn(struct isp_prev_device *prev, bool enable)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (enable)
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_GAMMA_BYPASS);
+ else
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_GAMMA_BYPASS);
+}
+
+/*
+ * preview_config_contrast - Configure the Contrast
+ *
+ * Value should be programmed before enabling the module.
+ */
+static void
+preview_config_contrast(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
+ 0xff << ISPPRV_CNT_BRT_CNT_SHIFT,
+ params->contrast << ISPPRV_CNT_BRT_CNT_SHIFT);
+}
+
+/*
+ * preview_config_brightness - Configure the Brightness
+ */
+static void
+preview_config_brightness(struct isp_prev_device *prev,
+ const struct prev_params *params)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
+ 0xff << ISPPRV_CNT_BRT_BRT_SHIFT,
+ params->brightness << ISPPRV_CNT_BRT_BRT_SHIFT);
+}
+
+/*
+ * preview_update_contrast - Updates the contrast.
+ * @contrast: Pointer to hold the current programmed contrast value.
+ *
+ * Value should be programmed before enabling the module.
+ */
+static void
+preview_update_contrast(struct isp_prev_device *prev, u8 contrast)
+{
+ struct prev_params *params;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ params = (prev->params.active & OMAP3ISP_PREV_CONTRAST)
+ ? &prev->params.params[0] : &prev->params.params[1];
+
+ if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) {
+ params->contrast = contrast * ISPPRV_CONTRAST_UNITS;
+ params->update |= OMAP3ISP_PREV_CONTRAST;
+ }
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+}
+
+/*
+ * preview_update_brightness - Updates the brightness in preview module.
+ * @brightness: Pointer to hold the current programmed brightness value.
+ *
+ */
+static void
+preview_update_brightness(struct isp_prev_device *prev, u8 brightness)
+{
+ struct prev_params *params;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS)
+ ? &prev->params.params[0] : &prev->params.params[1];
+
+ if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) {
+ params->brightness = brightness * ISPPRV_BRIGHT_UNITS;
+ params->update |= OMAP3ISP_PREV_BRIGHTNESS;
+ }
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+}
+
+static u32
+preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+ u32 active = prev->params.active;
+
+ if (shadow) {
+ /* Mark all shadow parameters we are going to touch as busy. */
+ prev->params.params[0].busy |= ~active & update;
+ prev->params.params[1].busy |= active & update;
+ } else {
+ /* Mark all active parameters we are going to touch as busy. */
+ update = (prev->params.params[0].update & active)
+ | (prev->params.params[1].update & ~active);
+
+ prev->params.params[0].busy |= active & update;
+ prev->params.params[1].busy |= ~active & update;
+ }
+
+ return update;
+}
+
+static void
+preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+ u32 active = prev->params.active;
+
+ if (shadow) {
+ /* Set the update flag for shadow parameters that have been
+ * updated and clear the busy flag for all shadow parameters.
+ */
+ prev->params.params[0].update |= (~active & update);
+ prev->params.params[1].update |= (active & update);
+ prev->params.params[0].busy &= active;
+ prev->params.params[1].busy &= ~active;
+ } else {
+ /* Clear the update flag for active parameters that have been
+ * applied and the busy flag for all active parameters.
+ */
+ prev->params.params[0].update &= ~(active & update);
+ prev->params.params[1].update &= ~(~active & update);
+ prev->params.params[0].busy &= ~active;
+ prev->params.params[1].busy &= active;
+ }
+}
+
+static void preview_params_switch(struct isp_prev_device *prev)
+{
+ u32 to_switch;
+
+ /* Switch active parameters with updated shadow parameters when the
+ * shadow parameter has been updated and neither the active not the
+ * shadow parameter is busy.
+ */
+ to_switch = (prev->params.params[0].update & ~prev->params.active)
+ | (prev->params.params[1].update & prev->params.active);
+ to_switch &= ~(prev->params.params[0].busy |
+ prev->params.params[1].busy);
+ if (to_switch == 0)
+ return;
+
+ prev->params.active ^= to_switch;
+
+ /* Remove the update flag for the shadow copy of parameters we have
+ * switched.
+ */
+ prev->params.params[0].update &= ~(~prev->params.active & to_switch);
+ prev->params.params[1].update &= ~(prev->params.active & to_switch);
+}
+
+/* preview parameters update structure */
+struct preview_update {
+ void (*config)(struct isp_prev_device *, const struct prev_params *);
+ void (*enable)(struct isp_prev_device *, bool);
+ unsigned int param_offset;
+ unsigned int param_size;
+ unsigned int config_offset;
+ bool skip;
+};
+
+/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */
+static const struct preview_update update_attrs[] = {
+ /* OMAP3ISP_PREV_LUMAENH */ {
+ preview_config_luma_enhancement,
+ preview_enable_luma_enhancement,
+ offsetof(struct prev_params, luma),
+ FIELD_SIZEOF(struct prev_params, luma),
+ offsetof(struct omap3isp_prev_update_config, luma),
+ }, /* OMAP3ISP_PREV_INVALAW */ {
+ NULL,
+ preview_enable_invalaw,
+ }, /* OMAP3ISP_PREV_HRZ_MED */ {
+ preview_config_hmed,
+ preview_enable_hmed,
+ offsetof(struct prev_params, hmed),
+ FIELD_SIZEOF(struct prev_params, hmed),
+ offsetof(struct omap3isp_prev_update_config, hmed),
+ }, /* OMAP3ISP_PREV_CFA */ {
+ preview_config_cfa,
+ NULL,
+ offsetof(struct prev_params, cfa),
+ FIELD_SIZEOF(struct prev_params, cfa),
+ offsetof(struct omap3isp_prev_update_config, cfa),
+ }, /* OMAP3ISP_PREV_CHROMA_SUPP */ {
+ preview_config_chroma_suppression,
+ preview_enable_chroma_suppression,
+ offsetof(struct prev_params, csup),
+ FIELD_SIZEOF(struct prev_params, csup),
+ offsetof(struct omap3isp_prev_update_config, csup),
+ }, /* OMAP3ISP_PREV_WB */ {
+ preview_config_whitebalance,
+ NULL,
+ offsetof(struct prev_params, wbal),
+ FIELD_SIZEOF(struct prev_params, wbal),
+ offsetof(struct omap3isp_prev_update_config, wbal),
+ }, /* OMAP3ISP_PREV_BLKADJ */ {
+ preview_config_blkadj,
+ NULL,
+ offsetof(struct prev_params, blkadj),
+ FIELD_SIZEOF(struct prev_params, blkadj),
+ offsetof(struct omap3isp_prev_update_config, blkadj),
+ }, /* OMAP3ISP_PREV_RGB2RGB */ {
+ preview_config_rgb_blending,
+ NULL,
+ offsetof(struct prev_params, rgb2rgb),
+ FIELD_SIZEOF(struct prev_params, rgb2rgb),
+ offsetof(struct omap3isp_prev_update_config, rgb2rgb),
+ }, /* OMAP3ISP_PREV_COLOR_CONV */ {
+ preview_config_csc,
+ NULL,
+ offsetof(struct prev_params, csc),
+ FIELD_SIZEOF(struct prev_params, csc),
+ offsetof(struct omap3isp_prev_update_config, csc),
+ }, /* OMAP3ISP_PREV_YC_LIMIT */ {
+ preview_config_yc_range,
+ NULL,
+ offsetof(struct prev_params, yclimit),
+ FIELD_SIZEOF(struct prev_params, yclimit),
+ offsetof(struct omap3isp_prev_update_config, yclimit),
+ }, /* OMAP3ISP_PREV_DEFECT_COR */ {
+ preview_config_dcor,
+ preview_enable_dcor,
+ offsetof(struct prev_params, dcor),
+ FIELD_SIZEOF(struct prev_params, dcor),
+ offsetof(struct omap3isp_prev_update_config, dcor),
+ }, /* Previously OMAP3ISP_PREV_GAMMABYPASS, not used anymore */ {
+ NULL,
+ NULL,
+ }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ {
+ NULL,
+ preview_enable_drkframe_capture,
+ }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ {
+ NULL,
+ preview_enable_drkframe,
+ }, /* OMAP3ISP_PREV_LENS_SHADING */ {
+ NULL,
+ preview_enable_drkframe,
+ }, /* OMAP3ISP_PREV_NF */ {
+ preview_config_noisefilter,
+ preview_enable_noisefilter,
+ offsetof(struct prev_params, nf),
+ FIELD_SIZEOF(struct prev_params, nf),
+ offsetof(struct omap3isp_prev_update_config, nf),
+ }, /* OMAP3ISP_PREV_GAMMA */ {
+ preview_config_gammacorrn,
+ preview_enable_gammacorrn,
+ offsetof(struct prev_params, gamma),
+ FIELD_SIZEOF(struct prev_params, gamma),
+ offsetof(struct omap3isp_prev_update_config, gamma),
+ }, /* OMAP3ISP_PREV_CONTRAST */ {
+ preview_config_contrast,
+ NULL,
+ 0, 0, 0, true,
+ }, /* OMAP3ISP_PREV_BRIGHTNESS */ {
+ preview_config_brightness,
+ NULL,
+ 0, 0, 0, true,
+ },
+};
+
+/*
+ * preview_config - Copy and update local structure with userspace preview
+ * configuration.
+ * @prev: ISP preview engine
+ * @cfg: Configuration
+ *
+ * Return zero if success or -EFAULT if the configuration can't be copied from
+ * userspace.
+ */
+static int preview_config(struct isp_prev_device *prev,
+ struct omap3isp_prev_update_config *cfg)
+{
+ unsigned long flags;
+ unsigned int i;
+ int rval = 0;
+ u32 update;
+ u32 active;
+
+ if (cfg->update == 0)
+ return 0;
+
+ /* Mark the shadow parameters we're going to update as busy. */
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_lock(prev, cfg->update, true);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
+ update = 0;
+
+ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
+ const struct preview_update *attr = &update_attrs[i];
+ struct prev_params *params;
+ unsigned int bit = 1 << i;
+
+ if (attr->skip || !(cfg->update & bit))
+ continue;
+
+ params = &prev->params.params[!!(active & bit)];
+
+ if (cfg->flag & bit) {
+ void __user *from = *(void * __user *)
+ ((void *)cfg + attr->config_offset);
+ void *to = (void *)params + attr->param_offset;
+ size_t size = attr->param_size;
+
+ if (to && from && size) {
+ if (copy_from_user(to, from, size)) {
+ rval = -EFAULT;
+ break;
+ }
+ }
+ params->features |= bit;
+ } else {
+ params->features &= ~bit;
+ }
+
+ update |= bit;
+ }
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, true);
+ preview_params_switch(prev);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
+ return rval;
+}
+
+/*
+ * preview_setup_hw - Setup preview registers and/or internal memory
+ * @prev: pointer to preview private structure
+ * @update: Bitmask of parameters to setup
+ * @active: Bitmask of parameters active in set 0
+ * Note: can be called from interrupt context
+ * Return none
+ */
+static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
+ u32 active)
+{
+ unsigned int i;
+ u32 features;
+
+ if (update == 0)
+ return;
+
+ features = (prev->params.params[0].features & active)
+ | (prev->params.params[1].features & ~active);
+
+ for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
+ const struct preview_update *attr = &update_attrs[i];
+ struct prev_params *params;
+ unsigned int bit = 1 << i;
+
+ if (!(update & bit))
+ continue;
+
+ params = &prev->params.params[!(active & bit)];
+
+ if (params->features & bit) {
+ if (attr->config)
+ attr->config(prev, params);
+ if (attr->enable)
+ attr->enable(prev, true);
+ } else {
+ if (attr->enable)
+ attr->enable(prev, false);
+ }
+ }
+}
+
+/*
+ * preview_config_ycpos - Configure byte layout of YUV image.
+ * @mode: Indicates the required byte layout.
+ */
+static void
+preview_config_ycpos(struct isp_prev_device *prev,
+ enum v4l2_mbus_pixelcode pixelcode)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ enum preview_ycpos_mode mode;
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ mode = YCPOS_CrYCbY;
+ break;
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ mode = YCPOS_YCrYCb;
+ break;
+ default:
+ return;
+ }
+
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_YCPOS_CrYCbY,
+ mode << ISPPRV_PCR_YCPOS_SHIFT);
+}
+
+/*
+ * preview_config_averager - Enable / disable / configure averager
+ * @average: Average value to be configured.
+ */
+static void preview_config_averager(struct isp_prev_device *prev, u8 average)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT |
+ ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT |
+ average, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE);
+}
+
+
+/*
+ * preview_config_input_format - Configure the input format
+ * @prev: The preview engine
+ * @format: Format on the preview engine sink pad
+ *
+ * Enable and configure CFA interpolation for Bayer formats and disable it for
+ * greyscale formats.
+ *
+ * The CFA table is organised in four blocks, one per Bayer component. The
+ * hardware expects blocks to follow the Bayer order of the input data, while
+ * the driver stores the table in GRBG order in memory. The blocks need to be
+ * reordered to support non-GRBG Bayer patterns.
+ */
+static void preview_config_input_format(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *format)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ struct prev_params *params;
+
+ switch (format->code) {
+ case V4L2_MBUS_FMT_SGRBG10_1X10:
+ prev->params.cfa_order = 0;
+ break;
+ case V4L2_MBUS_FMT_SRGGB10_1X10:
+ prev->params.cfa_order = 1;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ prev->params.cfa_order = 2;
+ break;
+ case V4L2_MBUS_FMT_SGBRG10_1X10:
+ prev->params.cfa_order = 3;
+ break;
+ default:
+ /* Disable CFA for non-Bayer formats. */
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAEN);
+ return;
+ }
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, ISPPRV_PCR_CFAEN);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAFMT_MASK, ISPPRV_PCR_CFAFMT_BAYER);
+
+ params = (prev->params.active & OMAP3ISP_PREV_CFA)
+ ? &prev->params.params[0] : &prev->params.params[1];
+
+ preview_config_cfa(prev, params);
+}
+
+/*
+ * preview_config_input_size - Configure the input frame size
+ *
+ * The preview engine crops several rows and columns internally depending on
+ * which processing blocks are enabled. The driver assumes all those blocks are
+ * enabled when reporting source pad formats to userspace. If this assumption is
+ * not true, rows and columns must be manually cropped at the preview engine
+ * input to avoid overflows at the end of lines and frames.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
+{
+ const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
+ struct isp_device *isp = to_isp_device(prev);
+ unsigned int sph = prev->crop.left;
+ unsigned int eph = prev->crop.left + prev->crop.width - 1;
+ unsigned int slv = prev->crop.top;
+ unsigned int elv = prev->crop.top + prev->crop.height - 1;
+ u32 features;
+
+ if (format->code != V4L2_MBUS_FMT_Y10_1X10) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
+ }
+
+ features = (prev->params.params[0].features & active)
+ | (prev->params.params[1].features & ~active);
+
+ if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) {
+ sph -= 2;
+ eph += 2;
+ slv -= 2;
+ elv += 2;
+ }
+ if (features & OMAP3ISP_PREV_HRZ_MED) {
+ sph -= 2;
+ eph += 2;
+ }
+ if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH))
+ sph -= 2;
+
+ isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
+ isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv,
+ OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO);
+}
+
+/*
+ * preview_config_inlineoffset - Configures the Read address line offset.
+ * @prev: Preview module
+ * @offset: Line offset
+ *
+ * According to the TRM, the line offset must be aligned on a 32 bytes boundary.
+ * However, a hardware bug requires the memory start address to be aligned on a
+ * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as
+ * well.
+ */
+static void
+preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_RADR_OFFSET);
+}
+
+/*
+ * preview_set_inaddr - Sets memory address of input frame.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ *
+ * Configures the memory address from which the input frame is to be read.
+ */
+static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR);
+}
+
+/*
+ * preview_config_outlineoffset - Configures the Write address line offset.
+ * @offset: Line Offset for the preview output.
+ *
+ * The offset must be a multiple of 32 bytes.
+ */
+static void preview_config_outlineoffset(struct isp_prev_device *prev,
+ u32 offset)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
+ ISPPRV_WADD_OFFSET);
+}
+
+/*
+ * preview_set_outaddr - Sets the memory address to store output frame
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ *
+ * Configures the memory address to which the output frame is written.
+ */
+static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR);
+}
+
+static void preview_adjust_bandwidth(struct isp_prev_device *prev)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
+ struct isp_device *isp = to_isp_device(prev);
+ const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK];
+ unsigned long l3_ick = pipe->l3_ick;
+ struct v4l2_fract *timeperframe;
+ unsigned int cycles_per_frame;
+ unsigned int requests_per_frame;
+ unsigned int cycles_per_request;
+ unsigned int minimum;
+ unsigned int maximum;
+ unsigned int value;
+
+ if (prev->input != PREVIEW_INPUT_MEMORY) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_PRV_EXP_MASK);
+ return;
+ }
+
+ /* Compute the minimum number of cycles per request, based on the
+ * pipeline maximum data rate. This is an absolute lower bound if we
+ * don't want SBL overflows, so round the value up.
+ */
+ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
+ pipe->max_rate);
+ minimum = DIV_ROUND_UP(cycles_per_request, 32);
+
+ /* Compute the maximum number of cycles per request, based on the
+ * requested frame rate. This is a soft upper bound to achieve a frame
+ * rate equal or higher than the requested value, so round the value
+ * down.
+ */
+ timeperframe = &pipe->max_timeperframe;
+
+ requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height;
+ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
+ timeperframe->denominator);
+ cycles_per_request = cycles_per_frame / requests_per_frame;
+
+ maximum = cycles_per_request / 32;
+
+ value = max(minimum, maximum);
+
+ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_PRV_EXP_MASK,
+ value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT);
+}
+
+/*
+ * omap3isp_preview_busy - Gets busy state of preview module.
+ */
+int omap3isp_preview_busy(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR)
+ & ISPPRV_PCR_BUSY;
+}
+
+/*
+ * omap3isp_preview_restore_context - Restores the values of preview registers
+ */
+void omap3isp_preview_restore_context(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
+ const u32 update = OMAP3ISP_PREV_FEATURES_END - 1;
+
+ prev->params.params[0].update = prev->params.active & update;
+ prev->params.params[1].update = ~prev->params.active & update;
+
+ preview_setup_hw(prev, update, prev->params.active);
+
+ prev->params.params[0].update = 0;
+ prev->params.params[1].update = 0;
+}
+
+/*
+ * preview_print_status - Dump preview module registers to the kernel log
+ */
+#define PREV_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name))
+
+static void preview_print_status(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ dev_dbg(isp->dev, "-------------Preview Register dump----------\n");
+
+ PREV_PRINT_REGISTER(isp, PCR);
+ PREV_PRINT_REGISTER(isp, HORZ_INFO);
+ PREV_PRINT_REGISTER(isp, VERT_INFO);
+ PREV_PRINT_REGISTER(isp, RSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, RADR_OFFSET);
+ PREV_PRINT_REGISTER(isp, DSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, DRKF_OFFSET);
+ PREV_PRINT_REGISTER(isp, WSDR_ADDR);
+ PREV_PRINT_REGISTER(isp, WADD_OFFSET);
+ PREV_PRINT_REGISTER(isp, AVE);
+ PREV_PRINT_REGISTER(isp, HMED);
+ PREV_PRINT_REGISTER(isp, NF);
+ PREV_PRINT_REGISTER(isp, WB_DGAIN);
+ PREV_PRINT_REGISTER(isp, WBGAIN);
+ PREV_PRINT_REGISTER(isp, WBSEL);
+ PREV_PRINT_REGISTER(isp, CFA);
+ PREV_PRINT_REGISTER(isp, BLKADJOFF);
+ PREV_PRINT_REGISTER(isp, RGB_MAT1);
+ PREV_PRINT_REGISTER(isp, RGB_MAT2);
+ PREV_PRINT_REGISTER(isp, RGB_MAT3);
+ PREV_PRINT_REGISTER(isp, RGB_MAT4);
+ PREV_PRINT_REGISTER(isp, RGB_MAT5);
+ PREV_PRINT_REGISTER(isp, RGB_OFF1);
+ PREV_PRINT_REGISTER(isp, RGB_OFF2);
+ PREV_PRINT_REGISTER(isp, CSC0);
+ PREV_PRINT_REGISTER(isp, CSC1);
+ PREV_PRINT_REGISTER(isp, CSC2);
+ PREV_PRINT_REGISTER(isp, CSC_OFFSET);
+ PREV_PRINT_REGISTER(isp, CNT_BRT);
+ PREV_PRINT_REGISTER(isp, CSUP);
+ PREV_PRINT_REGISTER(isp, SETUP_YC);
+ PREV_PRINT_REGISTER(isp, SET_TBL_ADDR);
+ PREV_PRINT_REGISTER(isp, CDC_THR0);
+ PREV_PRINT_REGISTER(isp, CDC_THR1);
+ PREV_PRINT_REGISTER(isp, CDC_THR2);
+ PREV_PRINT_REGISTER(isp, CDC_THR3);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * preview_init_params - init image processing parameters.
+ * @prev: pointer to previewer private structure
+ */
+static void preview_init_params(struct isp_prev_device *prev)
+{
+ struct prev_params *params;
+ unsigned int i;
+
+ spin_lock_init(&prev->params.lock);
+
+ prev->params.active = ~0;
+ prev->params.params[0].busy = 0;
+ prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1;
+ prev->params.params[1].busy = 0;
+ prev->params.params[1].update = 0;
+
+ params = &prev->params.params[0];
+
+ /* Init values */
+ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
+ params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
+ params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
+ memcpy(params->cfa.table, cfa_coef_table,
+ sizeof(params->cfa.table));
+ params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ;
+ params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT;
+ params->csup.gain = FLR_CSUP_GAIN;
+ params->csup.thres = FLR_CSUP_THRES;
+ params->csup.hypf_en = 0;
+ memcpy(params->luma.table, luma_enhance_table,
+ sizeof(params->luma.table));
+ params->nf.spread = FLR_NF_STRGTH;
+ memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table));
+ params->dcor.couplet_mode_en = 1;
+ for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++)
+ params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL;
+ memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue));
+ memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green));
+ memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red));
+ params->wbal.dgain = FLR_WBAL_DGAIN;
+ params->wbal.coef0 = FLR_WBAL_COEF;
+ params->wbal.coef1 = FLR_WBAL_COEF;
+ params->wbal.coef2 = FLR_WBAL_COEF;
+ params->wbal.coef3 = FLR_WBAL_COEF;
+ params->blkadj.red = FLR_BLKADJ_RED;
+ params->blkadj.green = FLR_BLKADJ_GREEN;
+ params->blkadj.blue = FLR_BLKADJ_BLUE;
+ params->rgb2rgb = flr_rgb2rgb;
+ params->csc = flr_prev_csc;
+ params->yclimit.minC = ISPPRV_YC_MIN;
+ params->yclimit.maxC = ISPPRV_YC_MAX;
+ params->yclimit.minY = ISPPRV_YC_MIN;
+ params->yclimit.maxY = ISPPRV_YC_MAX;
+
+ params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR
+ | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA
+ | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT
+ | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV
+ | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS
+ | OMAP3ISP_PREV_CONTRAST;
+}
+
+/*
+ * preview_max_out_width - Handle previewer hardware ouput limitations
+ * @isp_revision : ISP revision
+ * returns maximum width output for current isp revision
+ */
+static unsigned int preview_max_out_width(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ return PREV_MAX_OUT_WIDTH_REV_1;
+
+ case ISP_REVISION_2_0:
+ default:
+ return PREV_MAX_OUT_WIDTH_REV_2;
+
+ case ISP_REVISION_15_0:
+ return PREV_MAX_OUT_WIDTH_REV_15;
+ }
+}
+
+static void preview_configure(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+ struct v4l2_mbus_framefmt *format;
+ unsigned long flags;
+ u32 update;
+ u32 active;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ /* Mark all active parameters we are going to touch as busy. */
+ update = preview_params_lock(prev, 0, false);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
+ /* PREV_PAD_SINK */
+ format = &prev->formats[PREV_PAD_SINK];
+
+ preview_adjust_bandwidth(prev);
+
+ preview_config_input_format(prev, format);
+ preview_config_input_size(prev, active);
+
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ preview_config_inlineoffset(prev, 0);
+ else
+ preview_config_inlineoffset(prev,
+ ALIGN(format->width, 0x20) * 2);
+
+ preview_setup_hw(prev, update, active);
+
+ /* PREV_PAD_SOURCE */
+ format = &prev->formats[PREV_PAD_SOURCE];
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SDRPORT);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SDRPORT);
+
+ if (prev->output & PREVIEW_OUTPUT_RESIZER)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_RSZPORT);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_RSZPORT);
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ preview_config_outlineoffset(prev,
+ ALIGN(format->width, 0x10) * 2);
+
+ preview_config_averager(prev, 0);
+ preview_config_ycpos(prev, format->code);
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, false);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void preview_enable_oneshot(struct isp_prev_device *prev)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE
+ * bit is set. As the preview engine is used in single-shot mode, we
+ * need to set PCR.SOURCE before enabling the preview engine.
+ */
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_SOURCE);
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT);
+}
+
+void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev)
+{
+ /*
+ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
+ * condition, the module was paused and now we have a buffer queued
+ * on the output again. Restart the pipeline if running in continuous
+ * mode.
+ */
+ if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ preview_enable_oneshot(prev);
+ isp_video_dmaqueue_flags_clr(&prev->video_out);
+ }
+}
+
+static void preview_isr_buffer(struct isp_prev_device *prev)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ if (prev->input == PREVIEW_INPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&prev->video_in);
+ if (buffer != NULL)
+ preview_set_inaddr(prev, buffer->isp_addr);
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+ }
+
+ if (prev->output & PREVIEW_OUTPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&prev->video_out);
+ if (buffer != NULL) {
+ preview_set_outaddr(prev, buffer->isp_addr);
+ restart = 1;
+ }
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+ }
+
+ switch (prev->state) {
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ break;
+
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ /* If an underrun occurs, the video queue operation handler will
+ * restart the preview engine. Otherwise restart it immediately.
+ */
+ if (restart)
+ preview_enable_oneshot(prev);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ default:
+ return;
+ }
+}
+
+/*
+ * omap3isp_preview_isr - ISP preview engine interrupt handler
+ *
+ * Manage the preview engine video buffers and configure shadowed registers.
+ */
+void omap3isp_preview_isr(struct isp_prev_device *prev)
+{
+ unsigned long flags;
+ u32 update;
+ u32 active;
+
+ if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping))
+ return;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_switch(prev);
+ update = preview_params_lock(prev, 0, false);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
+ preview_setup_hw(prev, update, active);
+ preview_config_input_size(prev, active);
+
+ if (prev->input == PREVIEW_INPUT_MEMORY ||
+ prev->output & PREVIEW_OUTPUT_MEMORY)
+ preview_isr_buffer(prev);
+ else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ preview_enable_oneshot(prev);
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, false);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int preview_video_queue(struct isp_video *video,
+ struct isp_buffer *buffer)
+{
+ struct isp_prev_device *prev = &video->isp->isp_prev;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ preview_set_inaddr(prev, buffer->isp_addr);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ preview_set_outaddr(prev, buffer->isp_addr);
+
+ return 0;
+}
+
+static const struct isp_video_operations preview_video_ops = {
+ .queue = preview_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * preview_s_ctrl - Handle set control subdev method
+ * @ctrl: pointer to v4l2 control structure
+ */
+static int preview_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct isp_prev_device *prev =
+ container_of(ctrl->handler, struct isp_prev_device, ctrls);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ preview_update_brightness(prev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ preview_update_contrast(prev, ctrl->val);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops preview_ctrl_ops = {
+ .s_ctrl = preview_s_ctrl,
+};
+
+/*
+ * preview_ioctl - Handle preview module private ioctl's
+ * @prev: pointer to preview context structure
+ * @cmd: configuration command
+ * @arg: configuration argument
+ * return -EINVAL or zero on success
+ */
+static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ switch (cmd) {
+ case VIDIOC_OMAP3ISP_PRV_CFG:
+ return preview_config(prev, arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+/*
+ * preview_set_stream - Enable/Disable streaming on preview subdev
+ * @sd : pointer to v4l2 subdev structure
+ * @enable: 1 == Enable, 0 == Disable
+ * return -EINVAL or zero on success
+ */
+static int preview_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct isp_video *video_out = &prev->video_out;
+ struct isp_device *isp = to_isp_device(prev);
+ struct device *dev = to_device(prev);
+
+ if (prev->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
+ preview_configure(prev);
+ atomic_set(&prev->stopping, 0);
+ preview_print_status(prev);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+
+ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED ||
+ !(prev->output & PREVIEW_OUTPUT_MEMORY))
+ preview_enable_oneshot(prev);
+
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
+ if (prev->output & PREVIEW_OUTPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+
+ preview_enable_oneshot(prev);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (omap3isp_module_sync_idle(&sd->entity, &prev->wait,
+ &prev->stopping))
+ dev_dbg(dev, "%s: stop timeout.\n", sd->name);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ prev->state = enable;
+ return 0;
+}
+
+static struct v4l2_mbus_framefmt *
+__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &prev->formats[pad];
+}
+
+static struct v4l2_rect *
+__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
+ else
+ return &prev->crop;
+}
+
+/* previewer format descriptions */
+static const unsigned int preview_input_fmts[] = {
+ V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10,
+};
+
+static const unsigned int preview_output_fmts[] = {
+ V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+/*
+ * preview_try_format - Validate a format
+ * @prev: ISP preview engine
+ * @fh: V4L2 subdev file handle
+ * @pad: pad number
+ * @fmt: format to be validated
+ * @which: try/active format selector
+ *
+ * Validate and adjust the given format for the given pad based on the preview
+ * engine limits and the format and crop rectangles on other pads.
+ */
+static void preview_try_format(struct isp_prev_device *prev,
+ struct v4l2_subdev_fh *fh, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ enum v4l2_mbus_pixelcode pixelcode;
+ struct v4l2_rect *crop;
+ unsigned int i;
+
+ switch (pad) {
+ case PREV_PAD_SINK:
+ /* When reading data from the CCDC, the input size has already
+ * been mangled by the CCDC output pad so it can be accepted
+ * as-is.
+ *
+ * When reading data from memory, clamp the requested width and
+ * height. The TRM doesn't specify a minimum input height, make
+ * sure we got enough lines to enable the noise filter and color
+ * filter array interpolation.
+ */
+ if (prev->input == PREVIEW_INPUT_MEMORY) {
+ fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
+ preview_max_out_width(prev));
+ fmt->height = clamp_t(u32, fmt->height,
+ PREV_MIN_IN_HEIGHT,
+ PREV_MAX_IN_HEIGHT);
+ }
+
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) {
+ if (fmt->code == preview_input_fmts[i])
+ break;
+ }
+
+ /* If not found, use SGRBG10 as default */
+ if (i >= ARRAY_SIZE(preview_input_fmts))
+ fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ break;
+
+ case PREV_PAD_SOURCE:
+ pixelcode = fmt->code;
+ *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ fmt->code = pixelcode;
+ break;
+
+ default:
+ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+ break;
+ }
+
+ /* The preview module output size is configurable through the
+ * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
+ * is not supported yet, hardcode the output size to the crop
+ * rectangle size.
+ */
+ crop = __preview_get_crop(prev, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
+
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * preview_try_crop - Validate a crop rectangle
+ * @prev: ISP preview engine
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ *
+ * The preview engine crops lines and columns for its internal operation,
+ * depending on which filters are enabled. Enforce minimum crop margins to
+ * handle that transparently for userspace.
+ *
+ * See the explanation at the PREV_MARGIN_* definitions for more details.
+ */
+static void preview_try_crop(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ unsigned int left = PREV_MARGIN_LEFT;
+ unsigned int right = sink->width - PREV_MARGIN_RIGHT;
+ unsigned int top = PREV_MARGIN_TOP;
+ unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
+
+ /* When processing data on-the-fly from the CCDC, at least 2 pixels must
+ * be cropped from the left and right sides of the image. As we don't
+ * know which filters will be enabled, increase the left and right
+ * margins by two.
+ */
+ if (prev->input == PREVIEW_INPUT_CCDC) {
+ left += 2;
+ right -= 2;
+ }
+
+ /* Restrict left/top to even values to keep the Bayer pattern. */
+ crop->left &= ~1;
+ crop->top &= ~1;
+
+ crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
+ crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
+ crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
+ right - crop->left);
+ crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
+ bottom - crop->top);
+}
+
+/*
+ * preview_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int preview_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ switch (code->pad) {
+ case PREV_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(preview_input_fmts))
+ return -EINVAL;
+
+ code->code = preview_input_fmts[code->index];
+ break;
+ case PREV_PAD_SOURCE:
+ if (code->index >= ARRAY_SIZE(preview_output_fmts))
+ return -EINVAL;
+
+ code->code = preview_output_fmts[code->index];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int preview_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * preview_get_selection - Retrieve a selection rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the sink pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK,
+ sel->which);
+ preview_try_crop(prev, format, &sel->r);
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *__preview_get_crop(prev, fh, sel->which);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * preview_set_selection - Set a selection rectangle on a pad
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the sink pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int preview_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->target != V4L2_SEL_TGT_CROP ||
+ sel->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ /* Modifying the crop rectangle always changes the format on the source
+ * pad. If the KEEP_CONFIG flag is set, just return the current crop
+ * rectangle.
+ */
+ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = *__preview_get_crop(prev, fh, sel->which);
+ return 0;
+ }
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which);
+ preview_try_crop(prev, format, &sel->r);
+ *__preview_get_crop(prev, fh, sel->which) = sel->r;
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which);
+
+ return 0;
+}
+
+/*
+ * preview_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * preview_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt: pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ /* Propagate the format from sink to source */
+ if (fmt->pad == PREV_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __preview_get_crop(prev, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ preview_try_crop(prev, &fmt->format, crop);
+
+ /* Update the source format. */
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
+ fmt->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
+ fmt->which);
+ }
+
+ return 0;
+}
+
+/*
+ * preview_init_formats - Initialize formats on all pads
+ * @sd: ISP preview V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int preview_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = PREV_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ preview_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* subdev core operations */
+static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = {
+ .ioctl = preview_ioctl,
+};
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = {
+ .s_stream = preview_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
+ .enum_mbus_code = preview_enum_mbus_code,
+ .enum_frame_size = preview_enum_frame_size,
+ .get_fmt = preview_get_format,
+ .set_fmt = preview_set_format,
+ .get_selection = preview_get_selection,
+ .set_selection = preview_set_selection,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops preview_v4l2_ops = {
+ .core = &preview_v4l2_core_ops,
+ .video = &preview_v4l2_video_ops,
+ .pad = &preview_v4l2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops preview_v4l2_internal_ops = {
+ .open = preview_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * preview_link_setup - Setup previewer connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int preview_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* read from memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ return -EBUSY;
+ prev->input = PREVIEW_INPUT_MEMORY;
+ } else {
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ prev->input = PREVIEW_INPUT_NONE;
+ }
+ break;
+
+ case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from ccdc */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->input == PREVIEW_INPUT_MEMORY)
+ return -EBUSY;
+ prev->input = PREVIEW_INPUT_CCDC;
+ } else {
+ if (prev->input == PREVIEW_INPUT_CCDC)
+ prev->input = PREVIEW_INPUT_NONE;
+ }
+ break;
+
+ /*
+ * The ISP core doesn't support pipelines with multiple video outputs.
+ * Revisit this when it will be implemented, and return -EBUSY for now.
+ */
+
+ case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* write to memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->output & ~PREVIEW_OUTPUT_MEMORY)
+ return -EBUSY;
+ prev->output |= PREVIEW_OUTPUT_MEMORY;
+ } else {
+ prev->output &= ~PREVIEW_OUTPUT_MEMORY;
+ }
+ break;
+
+ case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* write to resizer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (prev->output & ~PREVIEW_OUTPUT_RESIZER)
+ return -EBUSY;
+ prev->output |= PREVIEW_OUTPUT_RESIZER;
+ } else {
+ prev->output &= ~PREVIEW_OUTPUT_RESIZER;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations preview_media_ops = {
+ .link_setup = preview_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
+{
+ v4l2_device_unregister_subdev(&prev->subdev);
+ omap3isp_video_unregister(&prev->video_in);
+ omap3isp_video_unregister(&prev->video_out);
+}
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prev,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &prev->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&prev->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_preview_unregister_entities(prev);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP previewer initialisation and cleanup
+ */
+
+/*
+ * preview_init_entities - Initialize subdev and media entity.
+ * @prev : Pointer to preview structure
+ * return -ENOMEM or zero on success
+ */
+static int preview_init_entities(struct isp_prev_device *prev)
+{
+ struct v4l2_subdev *sd = &prev->subdev;
+ struct media_pad *pads = prev->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ prev->input = PREVIEW_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &preview_v4l2_ops);
+ sd->internal_ops = &preview_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, prev);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ v4l2_ctrl_handler_init(&prev->ctrls, 2);
+ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS,
+ ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH,
+ ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF);
+ v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST,
+ ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH,
+ ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF);
+ v4l2_ctrl_handler_setup(&prev->ctrls);
+ sd->ctrl_handler = &prev->ctrls;
+
+ pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &preview_media_ops;
+ ret = media_entity_init(me, PREV_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ preview_init_formats(sd, NULL);
+
+ /* According to the OMAP34xx TRM, video buffers need to be aligned on a
+ * 32 bytes boundary. However, an undocumented hardware bug requires a
+ * 64 bytes boundary at the preview engine input.
+ */
+ prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ prev->video_in.ops = &preview_video_ops;
+ prev->video_in.isp = to_isp_device(prev);
+ prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ prev->video_in.bpl_alignment = 64;
+ prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ prev->video_out.ops = &preview_video_ops;
+ prev->video_out.isp = to_isp_device(prev);
+ prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ prev->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&prev->video_in, "preview");
+ if (ret < 0)
+ goto error_video_in;
+
+ ret = omap3isp_video_init(&prev->video_out, "preview");
+ if (ret < 0)
+ goto error_video_out;
+
+ /* Connect the video nodes to the previewer subdev. */
+ ret = media_entity_create_link(&prev->video_in.video.entity, 0,
+ &prev->subdev.entity, PREV_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
+ &prev->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&prev->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&prev->video_in);
+error_video_in:
+ media_entity_cleanup(&prev->subdev.entity);
+ return ret;
+}
+
+/*
+ * omap3isp_preview_init - Previewer initialization.
+ * @dev : Pointer to ISP device
+ * return -ENOMEM or zero on success
+ */
+int omap3isp_preview_init(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
+
+ init_waitqueue_head(&prev->wait);
+
+ preview_init_params(prev);
+
+ return preview_init_entities(prev);
+}
+
+void omap3isp_preview_cleanup(struct isp_device *isp)
+{
+ struct isp_prev_device *prev = &isp->isp_prev;
+
+ v4l2_ctrl_handler_free(&prev->ctrls);
+ omap3isp_video_cleanup(&prev->video_in);
+ omap3isp_video_cleanup(&prev->video_out);
+ media_entity_cleanup(&prev->subdev.entity);
+}
diff --git a/drivers/media/platform/omap3isp/isppreview.h b/drivers/media/platform/omap3isp/isppreview.h
new file mode 100644
index 000000000000..f66923407f8c
--- /dev/null
+++ b/drivers/media/platform/omap3isp/isppreview.h
@@ -0,0 +1,174 @@
+/*
+ * isppreview.h
+ *
+ * TI OMAP3 ISP - Preview module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_PREVIEW_H
+#define OMAP3_ISP_PREVIEW_H
+
+#include <linux/omap3isp.h>
+#include <linux/types.h>
+#include <media/v4l2-ctrls.h>
+
+#include "ispvideo.h"
+
+#define ISPPRV_BRIGHT_STEP 0x1
+#define ISPPRV_BRIGHT_DEF 0x0
+#define ISPPRV_BRIGHT_LOW 0x0
+#define ISPPRV_BRIGHT_HIGH 0xFF
+#define ISPPRV_BRIGHT_UNITS 0x1
+
+#define ISPPRV_CONTRAST_STEP 0x1
+#define ISPPRV_CONTRAST_DEF 0x10
+#define ISPPRV_CONTRAST_LOW 0x0
+#define ISPPRV_CONTRAST_HIGH 0xFF
+#define ISPPRV_CONTRAST_UNITS 0x1
+
+/* Additional features not listed in linux/omap3isp.h */
+#define OMAP3ISP_PREV_CONTRAST (1 << 17)
+#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18)
+#define OMAP3ISP_PREV_FEATURES_END (1 << 19)
+
+enum preview_input_entity {
+ PREVIEW_INPUT_NONE,
+ PREVIEW_INPUT_CCDC,
+ PREVIEW_INPUT_MEMORY,
+};
+
+#define PREVIEW_OUTPUT_RESIZER (1 << 1)
+#define PREVIEW_OUTPUT_MEMORY (1 << 2)
+
+/* Configure byte layout of YUV image */
+enum preview_ycpos_mode {
+ YCPOS_YCrYCb = 0,
+ YCPOS_YCbYCr = 1,
+ YCPOS_CbYCrY = 2,
+ YCPOS_CrYCbY = 3
+};
+
+/*
+ * struct prev_params - Structure for all configuration
+ * @busy: Bitmask of busy parameters (being updated or used)
+ * @update: Bitmask of the parameters to be updated
+ * @features: Set of features enabled.
+ * @cfa: CFA coefficients.
+ * @csup: Chroma suppression coefficients.
+ * @luma: Luma enhancement coefficients.
+ * @nf: Noise filter coefficients.
+ * @dcor: Noise filter coefficients.
+ * @gamma: Gamma coefficients.
+ * @wbal: White Balance parameters.
+ * @blkadj: Black adjustment parameters.
+ * @rgb2rgb: RGB blending parameters.
+ * @csc: Color space conversion (RGB to YCbCr) parameters.
+ * @hmed: Horizontal median filter.
+ * @yclimit: YC limits parameters.
+ * @contrast: Contrast.
+ * @brightness: Brightness.
+ */
+struct prev_params {
+ u32 busy;
+ u32 update;
+ u32 features;
+ struct omap3isp_prev_cfa cfa;
+ struct omap3isp_prev_csup csup;
+ struct omap3isp_prev_luma luma;
+ struct omap3isp_prev_nf nf;
+ struct omap3isp_prev_dcor dcor;
+ struct omap3isp_prev_gtables gamma;
+ struct omap3isp_prev_wbal wbal;
+ struct omap3isp_prev_blkadj blkadj;
+ struct omap3isp_prev_rgbtorgb rgb2rgb;
+ struct omap3isp_prev_csc csc;
+ struct omap3isp_prev_hmed hmed;
+ struct omap3isp_prev_yclimit yclimit;
+ u8 contrast;
+ u8 brightness;
+};
+
+/* Sink and source previewer pads */
+#define PREV_PAD_SINK 0
+#define PREV_PAD_SOURCE 1
+#define PREV_PADS_NUM 2
+
+/*
+ * struct isp_prev_device - Structure for storing ISP Preview module information
+ * @subdev: V4L2 subdevice
+ * @pads: Media entity pads
+ * @formats: Active formats at the subdev pad
+ * @crop: Active crop rectangle
+ * @input: Module currently connected to the input pad
+ * @output: Bitmask of the active output
+ * @video_in: Input video entity
+ * @video_out: Output video entity
+ * @params.params : Active and shadow parameters sets
+ * @params.active: Bitmask of parameters active in set 0
+ * @params.lock: Parameters lock, protects params.active and params.shadow
+ * @underrun: Whether the preview entity has queued buffers on the output
+ * @state: Current preview pipeline state
+ *
+ * This structure is used to store the OMAP ISP Preview module Information.
+ */
+struct isp_prev_device {
+ struct v4l2_subdev subdev;
+ struct media_pad pads[PREV_PADS_NUM];
+ struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
+ struct v4l2_rect crop;
+
+ struct v4l2_ctrl_handler ctrls;
+
+ enum preview_input_entity input;
+ unsigned int output;
+ struct isp_video video_in;
+ struct isp_video video_out;
+
+ struct {
+ unsigned int cfa_order;
+ struct prev_params params[2];
+ u32 active;
+ spinlock_t lock;
+ } params;
+
+ enum isp_pipeline_stream_state state;
+ wait_queue_head_t wait;
+ atomic_t stopping;
+};
+
+struct isp_device;
+
+int omap3isp_preview_init(struct isp_device *isp);
+void omap3isp_preview_cleanup(struct isp_device *isp);
+
+int omap3isp_preview_register_entities(struct isp_prev_device *prv,
+ struct v4l2_device *vdev);
+void omap3isp_preview_unregister_entities(struct isp_prev_device *prv);
+
+void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev);
+void omap3isp_preview_isr(struct isp_prev_device *prev);
+
+int omap3isp_preview_busy(struct isp_prev_device *isp_prev);
+
+void omap3isp_preview_restore_context(struct isp_device *isp);
+
+#endif /* OMAP3_ISP_PREVIEW_H */
diff --git a/drivers/media/platform/omap3isp/ispqueue.c b/drivers/media/platform/omap3isp/ispqueue.c
new file mode 100644
index 000000000000..15bf3eab2224
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispqueue.c
@@ -0,0 +1,1158 @@
+/*
+ * ispqueue.c
+ *
+ * TI OMAP3 ISP - Video buffers queue handling
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/poll.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "ispqueue.h"
+
+/* -----------------------------------------------------------------------------
+ * Video buffers management
+ */
+
+/*
+ * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
+ *
+ * The typical operation required here is Cache Invalidation across
+ * the (user space) buffer address range. And this _must_ be done
+ * at QBUF stage (and *only* at QBUF).
+ *
+ * We try to use optimal cache invalidation function:
+ * - dmac_map_area:
+ * - used when the number of pages are _low_.
+ * - it becomes quite slow as the number of pages increase.
+ * - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
+ * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
+ *
+ * - flush_cache_all:
+ * - used when the number of pages are _high_.
+ * - time taken in the range of 500-900 us.
+ * - has a higher penalty but, as whole dcache + icache is invalidated
+ */
+/*
+ * FIXME: dmac_inv_range crashes randomly on the user space buffer
+ * address. Fall back to flush_cache_all for now.
+ */
+#define ISP_CACHE_FLUSH_PAGES_MAX 0
+
+static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
+{
+ if (buf->skip_cache)
+ return;
+
+ if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
+ buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
+ flush_cache_all();
+ else {
+ dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
+ DMA_FROM_DEVICE);
+ outer_inv_range(buf->vbuf.m.userptr,
+ buf->vbuf.m.userptr + buf->vbuf.length);
+ }
+}
+
+/*
+ * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
+ *
+ * Lock the VMAs underlying the given buffer into memory. This avoids the
+ * userspace buffer mapping from being swapped out, making VIPT cache handling
+ * easier.
+ *
+ * Note that the pages will not be freed as the buffers have been locked to
+ * memory using by a call to get_user_pages(), but the userspace mapping could
+ * still disappear if the VMAs are not locked. This is caused by the memory
+ * management code trying to be as lock-less as possible, which results in the
+ * userspace mapping manager not finding out that the pages are locked under
+ * some conditions.
+ */
+static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
+{
+ struct vm_area_struct *vma;
+ unsigned long start;
+ unsigned long end;
+ int ret = 0;
+
+ if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
+ return 0;
+
+ /* We can be called from workqueue context if the current task dies to
+ * unlock the VMAs. In that case there's no current memory management
+ * context so unlocking can't be performed, but the VMAs have been or
+ * are getting destroyed anyway so it doesn't really matter.
+ */
+ if (!current || !current->mm)
+ return lock ? -EINVAL : 0;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ down_write(&current->mm->mmap_sem);
+ spin_lock(&current->mm->page_table_lock);
+
+ do {
+ vma = find_vma(current->mm, start);
+ if (vma == NULL) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (lock)
+ vma->vm_flags |= VM_LOCKED;
+ else
+ vma->vm_flags &= ~VM_LOCKED;
+
+ start = vma->vm_end + 1;
+ } while (vma->vm_end < end);
+
+ if (lock)
+ buf->vm_flags |= VM_LOCKED;
+ else
+ buf->vm_flags &= ~VM_LOCKED;
+
+out:
+ spin_unlock(&current->mm->page_table_lock);
+ up_write(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
+ *
+ * Iterate over the vmalloc'ed area and create a scatter list entry for every
+ * page.
+ */
+static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int npages;
+ unsigned int i;
+ void *addr;
+
+ addr = buf->vaddr;
+ npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
+
+ sglist = vmalloc(npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, npages);
+
+ for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
+ struct page *page = vmalloc_to_page(addr);
+
+ if (page == NULL || PageHighMem(page)) {
+ vfree(sglist);
+ return -EINVAL;
+ }
+
+ sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
+ }
+
+ buf->sglen = npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
+ *
+ * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
+ */
+static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int offset = buf->offset;
+ unsigned int i;
+
+ sglist = vmalloc(buf->npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, buf->npages);
+
+ for (i = 0; i < buf->npages; ++i) {
+ if (PageHighMem(buf->pages[i])) {
+ vfree(sglist);
+ return -EINVAL;
+ }
+
+ sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
+ offset);
+ offset = 0;
+ }
+
+ buf->sglen = buf->npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
+ *
+ * Create a scatter list of physically contiguous pages starting at the buffer
+ * memory physical address.
+ */
+static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
+{
+ struct scatterlist *sglist;
+ unsigned int offset = buf->offset;
+ unsigned long pfn = buf->paddr >> PAGE_SHIFT;
+ unsigned int i;
+
+ sglist = vmalloc(buf->npages * sizeof(*sglist));
+ if (sglist == NULL)
+ return -ENOMEM;
+
+ sg_init_table(sglist, buf->npages);
+
+ for (i = 0; i < buf->npages; ++i, ++pfn) {
+ sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
+ offset);
+ /* PFNMAP buffers will not get DMA-mapped, set the DMA address
+ * manually.
+ */
+ sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
+ offset = 0;
+ }
+
+ buf->sglen = buf->npages;
+ buf->sglist = sglist;
+
+ return 0;
+}
+
+/*
+ * isp_video_buffer_cleanup - Release pages for a userspace VMA.
+ *
+ * Release pages locked by a call isp_video_buffer_prepare_user and free the
+ * pages table.
+ */
+static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
+{
+ enum dma_data_direction direction;
+ unsigned int i;
+
+ if (buf->queue->ops->buffer_cleanup)
+ buf->queue->ops->buffer_cleanup(buf);
+
+ if (!(buf->vm_flags & VM_PFNMAP)) {
+ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
+ direction);
+ }
+
+ vfree(buf->sglist);
+ buf->sglist = NULL;
+ buf->sglen = 0;
+
+ if (buf->pages != NULL) {
+ isp_video_buffer_lock_vma(buf, 0);
+
+ for (i = 0; i < buf->npages; ++i)
+ page_cache_release(buf->pages[i]);
+
+ vfree(buf->pages);
+ buf->pages = NULL;
+ }
+
+ buf->npages = 0;
+ buf->skip_cache = false;
+}
+
+/*
+ * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
+ *
+ * This function creates a list of pages for a userspace VMA. The number of
+ * pages is first computed based on the buffer size, and pages are then
+ * retrieved by a call to get_user_pages.
+ *
+ * Pages are pinned to memory by get_user_pages, making them available for DMA
+ * transfers. However, due to memory management optimization, it seems the
+ * get_user_pages doesn't guarantee that the pinned pages will not be written
+ * to swap and removed from the userspace mapping(s). When this happens, a page
+ * fault can be generated when accessing those unmapped pages.
+ *
+ * If the fault is triggered by a page table walk caused by VIPT cache
+ * management operations, the page fault handler might oops if the MM semaphore
+ * is held, as it can't handle kernel page faults in that case. To fix that, a
+ * fixup entry needs to be added to the cache management code, or the userspace
+ * VMA must be locked to avoid removing pages from the userspace mapping in the
+ * first place.
+ *
+ * If the number of pages retrieved is smaller than the number required by the
+ * buffer size, the function returns -EFAULT.
+ */
+static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
+{
+ unsigned long data;
+ unsigned int first;
+ unsigned int last;
+ int ret;
+
+ data = buf->vbuf.m.userptr;
+ first = (data & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
+
+ buf->offset = data & ~PAGE_MASK;
+ buf->npages = last - first + 1;
+ buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
+ if (buf->pages == NULL)
+ return -ENOMEM;
+
+ down_read(&current->mm->mmap_sem);
+ ret = get_user_pages(current, current->mm, data & PAGE_MASK,
+ buf->npages,
+ buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
+ buf->pages, NULL);
+ up_read(&current->mm->mmap_sem);
+
+ if (ret != buf->npages) {
+ buf->npages = ret < 0 ? 0 : ret;
+ isp_video_buffer_cleanup(buf);
+ return -EFAULT;
+ }
+
+ ret = isp_video_buffer_lock_vma(buf, 1);
+ if (ret < 0)
+ isp_video_buffer_cleanup(buf);
+
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
+ *
+ * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
+ * memory and if they span a single VMA.
+ *
+ * Return 0 if the buffer is valid, or -EFAULT otherwise.
+ */
+static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
+{
+ struct vm_area_struct *vma;
+ unsigned long prev_pfn;
+ unsigned long this_pfn;
+ unsigned long start;
+ unsigned long end;
+ dma_addr_t pa;
+ int ret = -EFAULT;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ buf->offset = start & ~PAGE_MASK;
+ buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
+ buf->pages = NULL;
+
+ down_read(&current->mm->mmap_sem);
+ vma = find_vma(current->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;
+ }
+
+ buf->paddr = pa + buf->offset;
+ ret = 0;
+
+done:
+ up_read(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
+ *
+ * This function locates the VMAs for the buffer's userspace address and checks
+ * that their flags match. The only flag that we need to care for at the moment
+ * is VM_PFNMAP.
+ *
+ * The buffer vm_flags field is set to the first VMA flags.
+ *
+ * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
+ * have incompatible flags.
+ */
+static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
+{
+ struct vm_area_struct *vma;
+ pgprot_t vm_page_prot;
+ unsigned long start;
+ unsigned long end;
+ int ret = -EFAULT;
+
+ start = buf->vbuf.m.userptr;
+ end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
+
+ down_read(&current->mm->mmap_sem);
+
+ do {
+ vma = find_vma(current->mm, start);
+ if (vma == NULL)
+ goto done;
+
+ if (start == buf->vbuf.m.userptr) {
+ buf->vm_flags = vma->vm_flags;
+ vm_page_prot = vma->vm_page_prot;
+ }
+
+ if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
+ goto done;
+
+ if (vm_page_prot != vma->vm_page_prot)
+ goto done;
+
+ start = vma->vm_end + 1;
+ } while (vma->vm_end < end);
+
+ /* Skip cache management to enhance performances for non-cached or
+ * write-combining buffers.
+ */
+ if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
+ vm_page_prot == pgprot_writecombine(vm_page_prot))
+ buf->skip_cache = true;
+
+ ret = 0;
+
+done:
+ up_read(&current->mm->mmap_sem);
+ return ret;
+}
+
+/*
+ * isp_video_buffer_prepare - Make a buffer ready for operation
+ *
+ * Preparing a buffer involves:
+ *
+ * - validating VMAs (userspace buffers only)
+ * - locking pages and VMAs into memory (userspace buffers only)
+ * - building page and scatter-gather lists
+ * - mapping buffers for DMA operation
+ * - performing driver-specific preparation
+ *
+ * The function must be called in userspace context with a valid mm context
+ * (this excludes cleanup paths such as sys_close when the userspace process
+ * segfaults).
+ */
+static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+{
+ enum dma_data_direction direction;
+ int ret;
+
+ switch (buf->vbuf.memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = isp_video_buffer_sglist_kernel(buf);
+ break;
+
+ case V4L2_MEMORY_USERPTR:
+ ret = isp_video_buffer_prepare_vm_flags(buf);
+ if (ret < 0)
+ return ret;
+
+ if (buf->vm_flags & VM_PFNMAP) {
+ ret = isp_video_buffer_prepare_pfnmap(buf);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_buffer_sglist_pfnmap(buf);
+ } else {
+ ret = isp_video_buffer_prepare_user(buf);
+ if (ret < 0)
+ return ret;
+
+ ret = isp_video_buffer_sglist_user(buf);
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ goto done;
+
+ if (!(buf->vm_flags & VM_PFNMAP)) {
+ direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
+ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
+ direction);
+ if (ret != buf->sglen) {
+ ret = -EFAULT;
+ goto done;
+ }
+ }
+
+ if (buf->queue->ops->buffer_prepare)
+ ret = buf->queue->ops->buffer_prepare(buf);
+
+done:
+ if (ret < 0) {
+ isp_video_buffer_cleanup(buf);
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * isp_video_queue_query - Query the status of a given buffer
+ *
+ * Locking: must be called with the queue lock held.
+ */
+static void isp_video_buffer_query(struct isp_video_buffer *buf,
+ struct v4l2_buffer *vbuf)
+{
+ memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
+
+ if (buf->vma_use_count)
+ vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ switch (buf->state) {
+ case ISP_BUF_STATE_ERROR:
+ vbuf->flags |= V4L2_BUF_FLAG_ERROR;
+ case ISP_BUF_STATE_DONE:
+ vbuf->flags |= V4L2_BUF_FLAG_DONE;
+ case ISP_BUF_STATE_QUEUED:
+ case ISP_BUF_STATE_ACTIVE:
+ vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
+ break;
+ case ISP_BUF_STATE_IDLE:
+ default:
+ break;
+ }
+}
+
+/*
+ * isp_video_buffer_wait - Wait for a buffer to be ready
+ *
+ * In non-blocking mode, return immediately with 0 if the buffer is ready or
+ * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
+ *
+ * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
+ * queue using the same condition.
+ */
+static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
+{
+ if (nonblocking) {
+ return (buf->state != ISP_BUF_STATE_QUEUED &&
+ buf->state != ISP_BUF_STATE_ACTIVE)
+ ? 0 : -EAGAIN;
+ }
+
+ return wait_event_interruptible(buf->wait,
+ buf->state != ISP_BUF_STATE_QUEUED &&
+ buf->state != ISP_BUF_STATE_ACTIVE);
+}
+
+/* -----------------------------------------------------------------------------
+ * Queue management
+ */
+
+/*
+ * isp_video_queue_free - Free video buffers memory
+ *
+ * Buffers can only be freed if the queue isn't streaming and if no buffer is
+ * mapped to userspace. Return -EBUSY if those conditions aren't statisfied.
+ *
+ * This function must be called with the queue lock held.
+ */
+static int isp_video_queue_free(struct isp_video_queue *queue)
+{
+ unsigned int i;
+
+ if (queue->streaming)
+ return -EBUSY;
+
+ for (i = 0; i < queue->count; ++i) {
+ if (queue->buffers[i]->vma_use_count != 0)
+ return -EBUSY;
+ }
+
+ for (i = 0; i < queue->count; ++i) {
+ struct isp_video_buffer *buf = queue->buffers[i];
+
+ isp_video_buffer_cleanup(buf);
+
+ vfree(buf->vaddr);
+ buf->vaddr = NULL;
+
+ kfree(buf);
+ queue->buffers[i] = NULL;
+ }
+
+ INIT_LIST_HEAD(&queue->queue);
+ queue->count = 0;
+ return 0;
+}
+
+/*
+ * isp_video_queue_alloc - Allocate video buffers memory
+ *
+ * This function must be called with the queue lock held.
+ */
+static int isp_video_queue_alloc(struct isp_video_queue *queue,
+ unsigned int nbuffers,
+ unsigned int size, enum v4l2_memory memory)
+{
+ struct isp_video_buffer *buf;
+ unsigned int i;
+ void *mem;
+ int ret;
+
+ /* Start by freeing the buffers. */
+ ret = isp_video_queue_free(queue);
+ if (ret < 0)
+ return ret;
+
+ /* Bail out if no buffers should be allocated. */
+ if (nbuffers == 0)
+ return 0;
+
+ /* Initialize the allocated buffers. */
+ for (i = 0; i < nbuffers; ++i) {
+ buf = kzalloc(queue->bufsize, GFP_KERNEL);
+ if (buf == NULL)
+ break;
+
+ if (memory == V4L2_MEMORY_MMAP) {
+ /* Allocate video buffers memory for mmap mode. Align
+ * the size to the page size.
+ */
+ mem = vmalloc_32_user(PAGE_ALIGN(size));
+ if (mem == NULL) {
+ kfree(buf);
+ break;
+ }
+
+ buf->vbuf.m.offset = i * PAGE_ALIGN(size);
+ buf->vaddr = mem;
+ }
+
+ buf->vbuf.index = i;
+ buf->vbuf.length = size;
+ buf->vbuf.type = queue->type;
+ buf->vbuf.field = V4L2_FIELD_NONE;
+ buf->vbuf.memory = memory;
+
+ buf->queue = queue;
+ init_waitqueue_head(&buf->wait);
+
+ queue->buffers[i] = buf;
+ }
+
+ if (i == 0)
+ return -ENOMEM;
+
+ queue->count = i;
+ return nbuffers;
+}
+
+/**
+ * omap3isp_video_queue_cleanup - Clean up the video buffers queue
+ * @queue: Video buffers queue
+ *
+ * Free all allocated resources and clean up the video buffers queue. The queue
+ * must not be busy (no ongoing video stream) and buffers must have been
+ * unmapped.
+ *
+ * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
+ * unmapped.
+ */
+int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
+{
+ return isp_video_queue_free(queue);
+}
+
+/**
+ * omap3isp_video_queue_init - Initialize the video buffers queue
+ * @queue: Video buffers queue
+ * @type: V4L2 buffer type (capture or output)
+ * @ops: Driver-specific queue operations
+ * @dev: Device used for DMA operations
+ * @bufsize: Size of the driver-specific buffer structure
+ *
+ * Initialize the video buffers queue with the supplied parameters.
+ *
+ * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
+ * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
+ *
+ * Buffer objects will be allocated using the given buffer size to allow room
+ * for driver-specific fields. Driver-specific buffer structures must start
+ * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
+ * structure must pass the size of the isp_video_buffer structure in the bufsize
+ * parameter.
+ *
+ * Return 0 on success.
+ */
+int omap3isp_video_queue_init(struct isp_video_queue *queue,
+ enum v4l2_buf_type type,
+ const struct isp_video_queue_operations *ops,
+ struct device *dev, unsigned int bufsize)
+{
+ INIT_LIST_HEAD(&queue->queue);
+ mutex_init(&queue->lock);
+ spin_lock_init(&queue->irqlock);
+
+ queue->type = type;
+ queue->ops = ops;
+ queue->dev = dev;
+ queue->bufsize = bufsize;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 operations
+ */
+
+/**
+ * omap3isp_video_queue_reqbufs - Allocate video buffers memory
+ *
+ * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
+ * allocated video buffer objects and, for MMAP buffers, buffer memory.
+ *
+ * If the number of buffers is 0, all buffers are freed and the function returns
+ * without performing any allocation.
+ *
+ * If the number of buffers is not 0, currently allocated buffers (if any) are
+ * freed and the requested number of buffers are allocated. Depending on
+ * driver-specific requirements and on memory availability, a number of buffer
+ * smaller or bigger than requested can be allocated. This isn't considered as
+ * an error.
+ *
+ * Return 0 on success or one of the following error codes:
+ *
+ * -EINVAL if the buffer type or index are invalid
+ * -EBUSY if the queue is busy (streaming or buffers mapped)
+ * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
+ */
+int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
+ struct v4l2_requestbuffers *rb)
+{
+ unsigned int nbuffers = rb->count;
+ unsigned int size;
+ int ret;
+
+ if (rb->type != queue->type)
+ return -EINVAL;
+
+ queue->ops->queue_prepare(queue, &nbuffers, &size);
+ if (size == 0)
+ return -EINVAL;
+
+ nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
+
+ mutex_lock(&queue->lock);
+
+ ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
+ if (ret < 0)
+ goto done;
+
+ rb->count = ret;
+ ret = 0;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
+ *
+ * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
+ * returns the status of a given video buffer.
+ *
+ * Return 0 on success or -EINVAL if the buffer type or index are invalid.
+ */
+int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf)
+{
+ struct isp_video_buffer *buf;
+ int ret = 0;
+
+ if (vbuf->type != queue->type)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+
+ if (vbuf->index >= queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = queue->buffers[vbuf->index];
+ isp_video_buffer_query(buf, vbuf);
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_qbuf - Queue a buffer
+ *
+ * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
+ *
+ * The v4l2_buffer structure passed from userspace is first sanity tested. If
+ * sane, the buffer is then processed and added to the main queue and, if the
+ * queue is streaming, to the IRQ queue.
+ *
+ * Before being enqueued, USERPTR buffers are checked for address changes. If
+ * the buffer has a different userspace address, the old memory area is unlocked
+ * and the new memory area is locked.
+ */
+int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (vbuf->type != queue->type)
+ goto done;
+
+ mutex_lock(&queue->lock);
+
+ if (vbuf->index >= queue->count)
+ goto done;
+
+ buf = queue->buffers[vbuf->index];
+
+ if (vbuf->memory != buf->vbuf.memory)
+ goto done;
+
+ if (buf->state != ISP_BUF_STATE_IDLE)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->length < buf->vbuf.length)
+ goto done;
+
+ if (vbuf->memory == V4L2_MEMORY_USERPTR &&
+ vbuf->m.userptr != buf->vbuf.m.userptr) {
+ isp_video_buffer_cleanup(buf);
+ buf->vbuf.m.userptr = vbuf->m.userptr;
+ buf->prepared = 0;
+ }
+
+ if (!buf->prepared) {
+ ret = isp_video_buffer_prepare(buf);
+ if (ret < 0)
+ goto done;
+ buf->prepared = 1;
+ }
+
+ isp_video_buffer_cache_sync(buf);
+
+ buf->state = ISP_BUF_STATE_QUEUED;
+ list_add_tail(&buf->stream, &queue->queue);
+
+ if (queue->streaming) {
+ spin_lock_irqsave(&queue->irqlock, flags);
+ queue->ops->buffer_queue(buf);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ }
+
+ ret = 0;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_dqbuf - Dequeue a buffer
+ *
+ * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
+ *
+ * Wait until a buffer is ready to be dequeued, remove it from the queue and
+ * copy its information to the v4l2_buffer structure.
+ *
+ * If the nonblocking argument is not zero and no buffer is ready, return
+ * -EAGAIN immediately instead of waiting.
+ *
+ * If no buffer has been enqueued, or if the requested buffer type doesn't match
+ * the queue type, return -EINVAL.
+ */
+int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
+ struct v4l2_buffer *vbuf, int nonblocking)
+{
+ struct isp_video_buffer *buf;
+ int ret;
+
+ if (vbuf->type != queue->type)
+ return -EINVAL;
+
+ mutex_lock(&queue->lock);
+
+ if (list_empty(&queue->queue)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
+ ret = isp_video_buffer_wait(buf, nonblocking);
+ if (ret < 0)
+ goto done;
+
+ list_del(&buf->stream);
+
+ isp_video_buffer_query(buf, vbuf);
+ buf->state = ISP_BUF_STATE_IDLE;
+ vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_streamon - Start streaming
+ *
+ * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
+ * starts streaming on the queue and calls the buffer_queue operation for all
+ * queued buffers.
+ *
+ * Return 0 on success.
+ */
+int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+
+ mutex_lock(&queue->lock);
+
+ if (queue->streaming)
+ goto done;
+
+ queue->streaming = 1;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ list_for_each_entry(buf, &queue->queue, stream)
+ queue->ops->buffer_queue(buf);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+done:
+ mutex_unlock(&queue->lock);
+ return 0;
+}
+
+/**
+ * omap3isp_video_queue_streamoff - Stop streaming
+ *
+ * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
+ * stops streaming on the queue and wakes up all the buffers.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ unsigned int i;
+
+ mutex_lock(&queue->lock);
+
+ if (!queue->streaming)
+ goto done;
+
+ queue->streaming = 0;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+
+ if (buf->state == ISP_BUF_STATE_ACTIVE)
+ wake_up(&buf->wait);
+
+ buf->state = ISP_BUF_STATE_IDLE;
+ }
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ INIT_LIST_HEAD(&queue->queue);
+
+done:
+ mutex_unlock(&queue->lock);
+}
+
+/**
+ * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
+ *
+ * This function is intended to be used with suspend/resume operations. It
+ * discards all 'done' buffers as they would be too old to be requested after
+ * resume.
+ *
+ * Drivers must stop the hardware and synchronize with interrupt handlers and/or
+ * delayed works before calling this function to make sure no buffer will be
+ * touched by the driver and/or hardware.
+ */
+void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
+{
+ struct isp_video_buffer *buf;
+ unsigned int i;
+
+ mutex_lock(&queue->lock);
+
+ if (!queue->streaming)
+ goto done;
+
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+
+ if (buf->state == ISP_BUF_STATE_DONE)
+ buf->state = ISP_BUF_STATE_ERROR;
+ }
+
+done:
+ mutex_unlock(&queue->lock);
+}
+
+static void isp_video_queue_vm_open(struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *buf = vma->vm_private_data;
+
+ buf->vma_use_count++;
+}
+
+static void isp_video_queue_vm_close(struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *buf = vma->vm_private_data;
+
+ buf->vma_use_count--;
+}
+
+static const struct vm_operations_struct isp_video_queue_vm_ops = {
+ .open = isp_video_queue_vm_open,
+ .close = isp_video_queue_vm_close,
+};
+
+/**
+ * omap3isp_video_queue_mmap - Map buffers to userspace
+ *
+ * This function is intended to be used as an mmap() file operation handler. It
+ * maps a buffer to userspace based on the VMA offset.
+ *
+ * Only buffers of memory type MMAP are supported.
+ */
+int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
+ struct vm_area_struct *vma)
+{
+ struct isp_video_buffer *uninitialized_var(buf);
+ unsigned long size;
+ unsigned int i;
+ int ret = 0;
+
+ mutex_lock(&queue->lock);
+
+ for (i = 0; i < queue->count; ++i) {
+ buf = queue->buffers[i];
+ if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+ break;
+ }
+
+ if (i == queue->count) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
+ size != PAGE_ALIGN(buf->vbuf.length)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret < 0)
+ goto done;
+
+ vma->vm_ops = &isp_video_queue_vm_ops;
+ vma->vm_private_data = buf;
+ isp_video_queue_vm_open(vma);
+
+done:
+ mutex_unlock(&queue->lock);
+ return ret;
+}
+
+/**
+ * omap3isp_video_queue_poll - Poll video queue state
+ *
+ * This function is intended to be used as a poll() file operation handler. It
+ * polls the state of the video buffer at the front of the queue and returns an
+ * events mask.
+ *
+ * If no buffer is present at the front of the queue, POLLERR is returned.
+ */
+unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
+ struct file *file, poll_table *wait)
+{
+ struct isp_video_buffer *buf;
+ unsigned int mask = 0;
+
+ mutex_lock(&queue->lock);
+ if (list_empty(&queue->queue)) {
+ mask |= POLLERR;
+ goto done;
+ }
+ buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
+
+ poll_wait(file, &buf->wait, wait);
+ if (buf->state == ISP_BUF_STATE_DONE ||
+ buf->state == ISP_BUF_STATE_ERROR) {
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ mask |= POLLIN | POLLRDNORM;
+ else
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
+done:
+ mutex_unlock(&queue->lock);
+ return mask;
+}
diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h
index 908dfd712e8e..908dfd712e8e 100644
--- a/drivers/media/video/omap3isp/ispqueue.h
+++ b/drivers/media/platform/omap3isp/ispqueue.h
diff --git a/drivers/media/video/omap3isp/ispreg.h b/drivers/media/platform/omap3isp/ispreg.h
index 084ea77d65a7..084ea77d65a7 100644
--- a/drivers/media/video/omap3isp/ispreg.h
+++ b/drivers/media/platform/omap3isp/ispreg.h
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/omap3isp/ispresizer.c
new file mode 100644
index 000000000000..d11fb261d530
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispresizer.c
@@ -0,0 +1,1778 @@
+/*
+ * ispresizer.c
+ *
+ * TI OMAP3 ISP - Resizer module
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+
+#include "isp.h"
+#include "ispreg.h"
+#include "ispresizer.h"
+
+/*
+ * Resizer Constants
+ */
+#define MIN_RESIZE_VALUE 64
+#define MID_RESIZE_VALUE 512
+#define MAX_RESIZE_VALUE 1024
+
+#define MIN_IN_WIDTH 32
+#define MIN_IN_HEIGHT 32
+#define MAX_IN_WIDTH_MEMORY_MODE 4095
+#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280
+#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095
+#define MAX_IN_HEIGHT 4095
+
+#define MIN_OUT_WIDTH 16
+#define MIN_OUT_HEIGHT 2
+#define MAX_OUT_HEIGHT 4095
+
+/*
+ * Resizer Use Constraints
+ * "TRM ES3.1, table 12-46"
+ */
+#define MAX_4TAP_OUT_WIDTH_ES1 1280
+#define MAX_7TAP_OUT_WIDTH_ES1 640
+#define MAX_4TAP_OUT_WIDTH_ES2 3312
+#define MAX_7TAP_OUT_WIDTH_ES2 1650
+#define MAX_4TAP_OUT_WIDTH_3630 4096
+#define MAX_7TAP_OUT_WIDTH_3630 2048
+
+/*
+ * Constants for ratio calculation
+ */
+#define RESIZE_DIVISOR 256
+#define DEFAULT_PHASE 1
+
+/*
+ * Default (and only) configuration of filter coefficients.
+ * 7-tap mode is for scale factors 0.25x to 0.5x.
+ * 4-tap mode is for scale factors 0.5x to 4.0x.
+ * There shouldn't be any reason to recalculate these, EVER.
+ */
+static const struct isprsz_coef filter_coefs = {
+ /* For 8-phase 4-tap horizontal filter: */
+ {
+ 0x0000, 0x0100, 0x0000, 0x0000,
+ 0x03FA, 0x00F6, 0x0010, 0x0000,
+ 0x03F9, 0x00DB, 0x002C, 0x0000,
+ 0x03FB, 0x00B3, 0x0053, 0x03FF,
+ 0x03FD, 0x0082, 0x0084, 0x03FD,
+ 0x03FF, 0x0053, 0x00B3, 0x03FB,
+ 0x0000, 0x002C, 0x00DB, 0x03F9,
+ 0x0000, 0x0010, 0x00F6, 0x03FA
+ },
+ /* For 8-phase 4-tap vertical filter: */
+ {
+ 0x0000, 0x0100, 0x0000, 0x0000,
+ 0x03FA, 0x00F6, 0x0010, 0x0000,
+ 0x03F9, 0x00DB, 0x002C, 0x0000,
+ 0x03FB, 0x00B3, 0x0053, 0x03FF,
+ 0x03FD, 0x0082, 0x0084, 0x03FD,
+ 0x03FF, 0x0053, 0x00B3, 0x03FB,
+ 0x0000, 0x002C, 0x00DB, 0x03F9,
+ 0x0000, 0x0010, 0x00F6, 0x03FA
+ },
+ /* For 4-phase 7-tap horizontal filter: */
+ #define DUMMY 0
+ {
+ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
+ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
+ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
+ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
+ },
+ /* For 4-phase 7-tap vertical filter: */
+ {
+ 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
+ 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
+ 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
+ 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
+ }
+ /*
+ * The dummy padding is required in 7-tap mode because of how the
+ * registers are arranged physically.
+ */
+ #undef DUMMY
+};
+
+/*
+ * __resizer_get_format - helper function for getting resizer format
+ * @res : pointer to resizer private structure
+ * @pad : pad number
+ * @fh : V4L2 subdev file handle
+ * @which : wanted subdev format
+ * return zero
+ */
+static struct v4l2_mbus_framefmt *
+__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(fh, pad);
+ else
+ return &res->formats[pad];
+}
+
+/*
+ * __resizer_get_crop - helper function for getting resizer crop rectangle
+ * @res : pointer to resizer private structure
+ * @fh : V4L2 subdev file handle
+ * @which : wanted subdev crop rectangle
+ */
+static struct v4l2_rect *
+__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK);
+ else
+ return &res->crop.request;
+}
+
+/*
+ * resizer_set_filters - Set resizer filters
+ * @res: Device context.
+ * @h_coeff: horizontal coefficient
+ * @v_coeff: vertical coefficient
+ * Return none
+ */
+static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff,
+ const u16 *v_coeff)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 startaddr_h, startaddr_v, tmp_h, tmp_v;
+ int i;
+
+ startaddr_h = ISPRSZ_HFILT10;
+ startaddr_v = ISPRSZ_VFILT10;
+
+ for (i = 0; i < COEFF_CNT; i += 2) {
+ tmp_h = h_coeff[i] |
+ (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT);
+ tmp_v = v_coeff[i] |
+ (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT);
+ isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h);
+ isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v);
+ startaddr_h += 4;
+ startaddr_v += 4;
+ }
+}
+
+/*
+ * resizer_set_bilinear - Chrominance horizontal algorithm select
+ * @res: Device context.
+ * @type: Filtering interpolation type.
+ *
+ * Filtering that is same as luminance processing is
+ * intended only for downsampling, and bilinear interpolation
+ * is intended only for upsampling.
+ */
+static void resizer_set_bilinear(struct isp_res_device *res,
+ enum resizer_chroma_algo type)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (type == RSZ_BILINEAR)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_CBILIN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_CBILIN);
+}
+
+/*
+ * resizer_set_ycpos - Luminance and chrominance order
+ * @res: Device context.
+ * @order: order type.
+ */
+static void resizer_set_ycpos(struct isp_res_device *res,
+ enum v4l2_mbus_pixelcode pixelcode)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ switch (pixelcode) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_YCPOS);
+ break;
+ case V4L2_MBUS_FMT_UYVY8_1X16:
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_YCPOS);
+ break;
+ default:
+ return;
+ }
+}
+
+/*
+ * resizer_set_phase - Setup horizontal and vertical starting phase
+ * @res: Device context.
+ * @h_phase: horizontal phase parameters.
+ * @v_phase: vertical phase parameters.
+ *
+ * Horizontal and vertical phase range is 0 to 7
+ */
+static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
+ u32 v_phase)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
+ ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
+ rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK;
+ rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
+}
+
+/*
+ * resizer_set_luma - Setup luminance enhancer parameters
+ * @res: Device context.
+ * @luma: Structure for luminance enhancer parameters.
+ *
+ * Algorithm select:
+ * 0x0: Disable
+ * 0x1: [-1 2 -1]/2 high-pass filter
+ * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter
+ *
+ * Maximum gain:
+ * The data is coded in U4Q4 representation.
+ *
+ * Slope:
+ * The data is coded in U4Q4 representation.
+ *
+ * Coring offset:
+ * The data is coded in U8Q0 representation.
+ *
+ * The new luminance value is computed as:
+ * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4.
+ */
+static void resizer_set_luma(struct isp_res_device *res,
+ struct resizer_luma_yenh *luma)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
+ & ISPRSZ_YENH_ALGO_MASK;
+ rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT)
+ & ISPRSZ_YENH_GAIN_MASK;
+ rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT)
+ & ISPRSZ_YENH_SLOP_MASK;
+ rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT)
+ & ISPRSZ_YENH_CORE_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH);
+}
+
+/*
+ * resizer_set_source - Input source select
+ * @res: Device context.
+ * @source: Input source type
+ *
+ * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from
+ * Preview/CCDC engine, otherwise from memory.
+ */
+static void resizer_set_source(struct isp_res_device *res,
+ enum resizer_input_entity source)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (source == RESIZER_INPUT_MEMORY)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPSRC);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPSRC);
+}
+
+/*
+ * resizer_set_ratio - Setup horizontal and vertical resizing value
+ * @res: Device context.
+ * @ratio: Structure for ratio parameters.
+ *
+ * Resizing range from 64 to 1024
+ */
+static void resizer_set_ratio(struct isp_res_device *res,
+ const struct resizer_ratio *ratio)
+{
+ struct isp_device *isp = to_isp_device(res);
+ const u16 *h_filter, *v_filter;
+ u32 rgval = 0;
+
+ rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
+ ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
+ rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT)
+ & ISPRSZ_CNT_HRSZ_MASK;
+ rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT)
+ & ISPRSZ_CNT_VRSZ_MASK;
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
+
+ /* prepare horizontal filter coefficients */
+ if (ratio->horz > MID_RESIZE_VALUE)
+ h_filter = &filter_coefs.h_filter_coef_7tap[0];
+ else
+ h_filter = &filter_coefs.h_filter_coef_4tap[0];
+
+ /* prepare vertical filter coefficients */
+ if (ratio->vert > MID_RESIZE_VALUE)
+ v_filter = &filter_coefs.v_filter_coef_7tap[0];
+ else
+ v_filter = &filter_coefs.v_filter_coef_4tap[0];
+
+ resizer_set_filters(res, h_filter, v_filter);
+}
+
+/*
+ * resizer_set_dst_size - Setup the output height and width
+ * @res: Device context.
+ * @width: Output width.
+ * @height: Output height.
+ *
+ * Width :
+ * The value must be EVEN.
+ *
+ * Height:
+ * The number of bytes written to SDRAM must be
+ * a multiple of 16-bytes if the vertical resizing factor
+ * is greater than 1x (upsizing)
+ */
+static void resizer_set_output_size(struct isp_res_device *res,
+ u32 width, u32 height)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
+ rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
+ & ISPRSZ_OUT_SIZE_HORZ_MASK;
+ rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
+ & ISPRSZ_OUT_SIZE_VERT_MASK;
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE);
+}
+
+/*
+ * resizer_set_output_offset - Setup memory offset for the output lines.
+ * @res: Device context.
+ * @offset: Memory offset.
+ *
+ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
+ * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth,
+ * the SDRAM line offset must be set on a 256-byte boundary
+ */
+static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF);
+}
+
+/*
+ * resizer_set_start - Setup vertical and horizontal start position
+ * @res: Device context.
+ * @left: Horizontal start position.
+ * @top: Vertical start position.
+ *
+ * Vertical start line:
+ * This field makes sense only when the resizer obtains its input
+ * from the preview engine/CCDC
+ *
+ * Horizontal start pixel:
+ * Pixels are coded on 16 bits for YUV and 8 bits for color separate data.
+ * When the resizer gets its input from SDRAM, this field must be set
+ * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data
+ */
+static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
+ & ISPRSZ_IN_START_HORZ_ST_MASK;
+ rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT)
+ & ISPRSZ_IN_START_VERT_ST_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START);
+}
+
+/*
+ * resizer_set_input_size - Setup the input size
+ * @res: Device context.
+ * @width: The range is 0 to 4095 pixels
+ * @height: The range is 0 to 4095 lines
+ */
+static void resizer_set_input_size(struct isp_res_device *res,
+ u32 width, u32 height)
+{
+ struct isp_device *isp = to_isp_device(res);
+ u32 rgval = 0;
+
+ dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
+
+ rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
+ & ISPRSZ_IN_SIZE_HORZ_MASK;
+ rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT)
+ & ISPRSZ_IN_SIZE_VERT_MASK;
+
+ isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE);
+}
+
+/*
+ * resizer_set_src_offs - Setup the memory offset for the input lines
+ * @res: Device context.
+ * @offset: Memory offset.
+ *
+ * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
+ * boundary; the 5 LSBs are read-only. This field must be programmed to be
+ * 0x0 if the resizer input is from preview engine/CCDC.
+ */
+static void resizer_set_input_offset(struct isp_res_device *res, u32 offset)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF);
+}
+
+/*
+ * resizer_set_intype - Input type select
+ * @res: Device context.
+ * @type: Pixel format type.
+ */
+static void resizer_set_intype(struct isp_res_device *res,
+ enum resizer_colors_type type)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (type == RSZ_COLOR8)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPTYP);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
+ ISPRSZ_CNT_INPTYP);
+}
+
+/*
+ * __resizer_set_inaddr - Helper function for set input address
+ * @res : pointer to resizer private data structure
+ * @addr: input address
+ * return none
+ */
+static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD);
+}
+
+/*
+ * The data rate at the horizontal resizer output must not exceed half the
+ * functional clock or 100 MP/s, whichever is lower. According to the TRM
+ * there's no similar requirement for the vertical resizer output. However
+ * experience showed that vertical upscaling by 4 leads to SBL overflows (with
+ * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer
+ * output data rate to the functional clock or 200 MP/s, whichever is lower,
+ * seems to get rid of SBL overflows.
+ *
+ * The maximum data rate at the output of the horizontal resizer can thus be
+ * computed with
+ *
+ * max intermediate rate <= L3 clock * input height / output height
+ * max intermediate rate <= L3 clock / 2
+ *
+ * The maximum data rate at the resizer input is then
+ *
+ * max input rate <= max intermediate rate * input width / output width
+ *
+ * where the input width and height are the resizer input crop rectangle size.
+ * The TRM doesn't clearly explain if that's a maximum instant data rate or a
+ * maximum average data rate.
+ */
+void omap3isp_resizer_max_rate(struct isp_res_device *res,
+ unsigned int *max_rate)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE];
+ unsigned long limit = min(pipe->l3_ick, 200000000UL);
+ unsigned long clock;
+
+ clock = div_u64((u64)limit * res->crop.active.height, ofmt->height);
+ clock = min(clock, limit / 2);
+ *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width);
+}
+
+/*
+ * When the resizer processes images from memory, the driver must slow down read
+ * requests on the input to at least comply with the internal data rate
+ * requirements. If the application real-time requirements can cope with slower
+ * processing, the resizer can be slowed down even more to put less pressure on
+ * the overall system.
+ *
+ * When the resizer processes images on the fly (either from the CCDC or the
+ * preview module), the same data rate requirements apply but they can't be
+ * enforced at the resizer level. The image input module (sensor, CCP2 or
+ * preview module) must not provide image data faster than the resizer can
+ * process.
+ *
+ * For live image pipelines, the data rate is set by the frame format, size and
+ * rate. The sensor output frame rate must not exceed the maximum resizer data
+ * rate.
+ *
+ * The resizer slows down read requests by inserting wait cycles in the SBL
+ * requests. The maximum number of 256-byte requests per second can be computed
+ * as (the data rate is multiplied by 2 to convert from pixels per second to
+ * bytes per second)
+ *
+ * request per second = data rate * 2 / 256
+ * cycles per request = cycles per second / requests per second
+ *
+ * The number of cycles per second is controlled by the L3 clock, leading to
+ *
+ * cycles per request = L3 frequency / 2 * 256 / data rate
+ */
+static void resizer_adjust_bandwidth(struct isp_res_device *res)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ struct isp_device *isp = to_isp_device(res);
+ unsigned long l3_ick = pipe->l3_ick;
+ struct v4l2_fract *timeperframe;
+ unsigned int cycles_per_frame;
+ unsigned int requests_per_frame;
+ unsigned int cycles_per_request;
+ unsigned int granularity;
+ unsigned int minimum;
+ unsigned int maximum;
+ unsigned int value;
+
+ if (res->input != RESIZER_INPUT_MEMORY) {
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_RSZ_EXP_MASK);
+ return;
+ }
+
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ case ISP_REVISION_2_0:
+ default:
+ granularity = 1024;
+ break;
+
+ case ISP_REVISION_15_0:
+ granularity = 32;
+ break;
+ }
+
+ /* Compute the minimum number of cycles per request, based on the
+ * pipeline maximum data rate. This is an absolute lower bound if we
+ * don't want SBL overflows, so round the value up.
+ */
+ cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
+ pipe->max_rate);
+ minimum = DIV_ROUND_UP(cycles_per_request, granularity);
+
+ /* Compute the maximum number of cycles per request, based on the
+ * requested frame rate. This is a soft upper bound to achieve a frame
+ * rate equal or higher than the requested value, so round the value
+ * down.
+ */
+ timeperframe = &pipe->max_timeperframe;
+
+ requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256)
+ * res->crop.active.height;
+ cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
+ timeperframe->denominator);
+ cycles_per_request = cycles_per_frame / requests_per_frame;
+
+ maximum = cycles_per_request / granularity;
+
+ value = max(minimum, maximum);
+
+ dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
+ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
+ ISPSBL_SDR_REQ_RSZ_EXP_MASK,
+ value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT);
+}
+
+/*
+ * omap3isp_resizer_busy - Checks if ISP resizer is busy.
+ *
+ * Returns busy field from ISPRSZ_PCR register.
+ */
+int omap3isp_resizer_busy(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) &
+ ISPRSZ_PCR_BUSY;
+}
+
+/*
+ * resizer_set_inaddr - Sets the memory address of the input frame.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ */
+static void resizer_set_inaddr(struct isp_res_device *res, u32 addr)
+{
+ res->addr_base = addr;
+
+ /* This will handle crop settings in stream off state */
+ if (res->crop_offset)
+ addr += res->crop_offset & ~0x1f;
+
+ __resizer_set_inaddr(res, addr);
+}
+
+/*
+ * Configures the memory address to which the output frame is written.
+ * @addr: 32bit memory address aligned on 32byte boundary.
+ * Note: For SBL efficiency reasons the address should be on a 256-byte
+ * boundary.
+ */
+static void resizer_set_outaddr(struct isp_res_device *res, u32 addr)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ /*
+ * Set output address. This needs to be in its own function
+ * because it changes often.
+ */
+ isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT,
+ OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD);
+}
+
+/*
+ * resizer_print_status - Prints the values of the resizer module registers.
+ */
+#define RSZ_PRINT_REGISTER(isp, name)\
+ dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \
+ isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name))
+
+static void resizer_print_status(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ dev_dbg(isp->dev, "-------------Resizer Register dump----------\n");
+
+ RSZ_PRINT_REGISTER(isp, PCR);
+ RSZ_PRINT_REGISTER(isp, CNT);
+ RSZ_PRINT_REGISTER(isp, OUT_SIZE);
+ RSZ_PRINT_REGISTER(isp, IN_START);
+ RSZ_PRINT_REGISTER(isp, IN_SIZE);
+ RSZ_PRINT_REGISTER(isp, SDR_INADD);
+ RSZ_PRINT_REGISTER(isp, SDR_INOFF);
+ RSZ_PRINT_REGISTER(isp, SDR_OUTADD);
+ RSZ_PRINT_REGISTER(isp, SDR_OUTOFF);
+ RSZ_PRINT_REGISTER(isp, YENH);
+
+ dev_dbg(isp->dev, "--------------------------------------------\n");
+}
+
+/*
+ * resizer_calc_ratios - Helper function for calculating resizer ratios
+ * @res: pointer to resizer private data structure
+ * @input: input frame size
+ * @output: output frame size
+ * @ratio : return calculated ratios
+ * return none
+ *
+ * The resizer uses a polyphase sample rate converter. The upsampling filter
+ * has a fixed number of phases that depend on the resizing ratio. As the ratio
+ * computation depends on the number of phases, we need to compute a first
+ * approximation and then refine it.
+ *
+ * The input/output/ratio relationship is given by the OMAP34xx TRM:
+ *
+ * - 8-phase, 4-tap mode (RSZ = 64 ~ 512)
+ * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7
+ * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4
+ * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024)
+ * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7
+ * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7
+ *
+ * iw and ih are the input width and height after cropping. Those equations need
+ * to be satisfied exactly for the resizer to work correctly.
+ *
+ * The equations can't be easily reverted, as the >> 8 operation is not linear.
+ * In addition, not all input sizes can be achieved for a given output size. To
+ * get the highest input size lower than or equal to the requested input size,
+ * we need to compute the highest resizing ratio that satisfies the following
+ * inequality (taking the 4-tap mode width equation as an example)
+ *
+ * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7
+ *
+ * (where iw is the requested input width) which can be rewritten as
+ *
+ * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8
+ * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b
+ * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16
+ *
+ * where b is the value of the 8 least significant bits of the right hand side
+ * expression of the last inequality. The highest resizing ratio value will be
+ * achieved when b is equal to its maximum value of 255. That resizing ratio
+ * value will still satisfy the original inequality, as b will disappear when
+ * the expression will be shifted right by 8.
+ *
+ * The reverted equations thus become
+ *
+ * - 8-phase, 4-tap mode
+ * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1)
+ * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1)
+ * - 4-phase, 7-tap mode
+ * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1)
+ * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1)
+ *
+ * The ratios are integer values, and are rounded down to ensure that the
+ * cropped input size is not bigger than the uncropped input size.
+ *
+ * As the number of phases/taps, used to select the correct equations to compute
+ * the ratio, depends on the ratio, we start with the 4-tap mode equations to
+ * compute an approximation of the ratio, and switch to the 7-tap mode equations
+ * if the approximation is higher than the ratio threshold.
+ *
+ * As the 7-tap mode equations will return a ratio smaller than or equal to the
+ * 4-tap mode equations, the resulting ratio could become lower than or equal to
+ * the ratio threshold. This 'equations loop' isn't an issue as long as the
+ * correct equations are used to compute the final input size. Starting with the
+ * 4-tap mode equations ensure that, in case of values resulting in a 'ratio
+ * loop', the smallest of the ratio values will be used, never exceeding the
+ * requested input size.
+ *
+ * We first clamp the output size according to the hardware capability to avoid
+ * auto-cropping the input more than required to satisfy the TRM equations. The
+ * minimum output size is achieved with a scaling factor of 1024. It is thus
+ * computed using the 7-tap equations.
+ *
+ * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1
+ * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1
+ *
+ * Similarly, the maximum output size is achieved with a scaling factor of 64
+ * and computed using the 4-tap equations.
+ *
+ * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1
+ * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1
+ *
+ * The additional +255 term compensates for the round down operation performed
+ * by the TRM equations when shifting the value right by 8 bits.
+ *
+ * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to
+ * the maximum value guarantees that the ratio value will never be smaller than
+ * the minimum, but it could still slightly exceed the maximum. Clamping the
+ * ratio will thus result in a resizing factor slightly larger than the
+ * requested value.
+ *
+ * To accommodate that, and make sure the TRM equations are satisfied exactly, we
+ * compute the input crop rectangle as the last step.
+ *
+ * As if the situation wasn't complex enough, the maximum output width depends
+ * on the vertical resizing ratio. Fortunately, the output height doesn't
+ * depend on the horizontal resizing ratio. We can then start by computing the
+ * output height and the vertical ratio, and then move to computing the output
+ * width and the horizontal ratio.
+ */
+static void resizer_calc_ratios(struct isp_res_device *res,
+ struct v4l2_rect *input,
+ struct v4l2_mbus_framefmt *output,
+ struct resizer_ratio *ratio)
+{
+ struct isp_device *isp = to_isp_device(res);
+ const unsigned int spv = DEFAULT_PHASE;
+ const unsigned int sph = DEFAULT_PHASE;
+ unsigned int upscaled_width;
+ unsigned int upscaled_height;
+ unsigned int min_width;
+ unsigned int min_height;
+ unsigned int max_width;
+ unsigned int max_height;
+ unsigned int width_alignment;
+ unsigned int width;
+ unsigned int height;
+
+ /*
+ * Clamp the output height based on the hardware capabilities and
+ * compute the vertical resizing ratio.
+ */
+ min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1;
+ min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT);
+ max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1;
+ max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
+ output->height = clamp(output->height, min_height, max_height);
+
+ ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv)
+ / (output->height - 1);
+ if (ratio->vert > MID_RESIZE_VALUE)
+ ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv)
+ / (output->height - 1);
+ ratio->vert = clamp_t(unsigned int, ratio->vert,
+ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
+
+ if (ratio->vert <= MID_RESIZE_VALUE) {
+ upscaled_height = (output->height - 1) * ratio->vert
+ + 32 * spv + 16;
+ height = (upscaled_height >> 8) + 4;
+ } else {
+ upscaled_height = (output->height - 1) * ratio->vert
+ + 64 * spv + 32;
+ height = (upscaled_height >> 8) + 7;
+ }
+
+ /*
+ * Compute the minimum and maximum output widths based on the hardware
+ * capabilities. The maximum depends on the vertical resizing ratio.
+ */
+ min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1;
+ min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH);
+
+ if (ratio->vert <= MID_RESIZE_VALUE) {
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ max_width = MAX_4TAP_OUT_WIDTH_ES1;
+ break;
+
+ case ISP_REVISION_2_0:
+ default:
+ max_width = MAX_4TAP_OUT_WIDTH_ES2;
+ break;
+
+ case ISP_REVISION_15_0:
+ max_width = MAX_4TAP_OUT_WIDTH_3630;
+ break;
+ }
+ } else {
+ switch (isp->revision) {
+ case ISP_REVISION_1_0:
+ max_width = MAX_7TAP_OUT_WIDTH_ES1;
+ break;
+
+ case ISP_REVISION_2_0:
+ default:
+ max_width = MAX_7TAP_OUT_WIDTH_ES2;
+ break;
+
+ case ISP_REVISION_15_0:
+ max_width = MAX_7TAP_OUT_WIDTH_3630;
+ break;
+ }
+ }
+ max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64
+ + 1, max_width);
+
+ /*
+ * The output width must be even, and must be a multiple of 16 bytes
+ * when upscaling vertically. Clamp the output width to the valid range.
+ * Take the alignment into account (the maximum width in 7-tap mode on
+ * ES2 isn't a multiple of 8) and align the result up to make sure it
+ * won't be smaller than the minimum.
+ */
+ width_alignment = ratio->vert < 256 ? 8 : 2;
+ output->width = clamp(output->width, min_width,
+ max_width & ~(width_alignment - 1));
+ output->width = ALIGN(output->width, width_alignment);
+
+ ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph)
+ / (output->width - 1);
+ if (ratio->horz > MID_RESIZE_VALUE)
+ ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph)
+ / (output->width - 1);
+ ratio->horz = clamp_t(unsigned int, ratio->horz,
+ MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
+
+ if (ratio->horz <= MID_RESIZE_VALUE) {
+ upscaled_width = (output->width - 1) * ratio->horz
+ + 32 * sph + 16;
+ width = (upscaled_width >> 8) + 7;
+ } else {
+ upscaled_width = (output->width - 1) * ratio->horz
+ + 64 * sph + 32;
+ width = (upscaled_width >> 8) + 7;
+ }
+
+ /* Center the new crop rectangle. */
+ input->left += (input->width - width) / 2;
+ input->top += (input->height - height) / 2;
+ input->width = width;
+ input->height = height;
+}
+
+/*
+ * resizer_set_crop_params - Setup hardware with cropping parameters
+ * @res : resizer private structure
+ * @crop_rect : current crop rectangle
+ * @ratio : resizer ratios
+ * return none
+ */
+static void resizer_set_crop_params(struct isp_res_device *res,
+ const struct v4l2_mbus_framefmt *input,
+ const struct v4l2_mbus_framefmt *output)
+{
+ resizer_set_ratio(res, &res->ratio);
+
+ /* Set chrominance horizontal algorithm */
+ if (res->ratio.horz >= RESIZE_DIVISOR)
+ resizer_set_bilinear(res, RSZ_THE_SAME);
+ else
+ resizer_set_bilinear(res, RSZ_BILINEAR);
+
+ resizer_adjust_bandwidth(res);
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ /* Calculate additional offset for crop */
+ res->crop_offset = (res->crop.active.top * input->width +
+ res->crop.active.left) * 2;
+ /*
+ * Write lowest 4 bits of horizontal pixel offset (in pixels),
+ * vertical start must be 0.
+ */
+ resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0);
+
+ /*
+ * Set start (read) address for cropping, in bytes.
+ * Lowest 5 bits must be zero.
+ */
+ __resizer_set_inaddr(res,
+ res->addr_base + (res->crop_offset & ~0x1f));
+ } else {
+ /*
+ * Set vertical start line and horizontal starting pixel.
+ * If the input is from CCDC/PREV, horizontal start field is
+ * in bytes (twice number of pixels).
+ */
+ resizer_set_start(res, res->crop.active.left * 2,
+ res->crop.active.top);
+ /* Input address and offset must be 0 for preview/ccdc input */
+ __resizer_set_inaddr(res, 0);
+ resizer_set_input_offset(res, 0);
+ }
+
+ /* Set the input size */
+ resizer_set_input_size(res, res->crop.active.width,
+ res->crop.active.height);
+}
+
+static void resizer_configure(struct isp_res_device *res)
+{
+ struct v4l2_mbus_framefmt *informat, *outformat;
+ struct resizer_luma_yenh luma = {0, 0, 0, 0};
+
+ resizer_set_source(res, res->input);
+
+ informat = &res->formats[RESZ_PAD_SINK];
+ outformat = &res->formats[RESZ_PAD_SOURCE];
+
+ /* RESZ_PAD_SINK */
+ if (res->input == RESIZER_INPUT_VP)
+ resizer_set_input_offset(res, 0);
+ else
+ resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2);
+
+ /* YUV422 interleaved, default phase, no luma enhancement */
+ resizer_set_intype(res, RSZ_YUV422);
+ resizer_set_ycpos(res, informat->code);
+ resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE);
+ resizer_set_luma(res, &luma);
+
+ /* RESZ_PAD_SOURCE */
+ resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32));
+ resizer_set_output_size(res, outformat->width, outformat->height);
+
+ resizer_set_crop_params(res, informat, outformat);
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void resizer_enable_oneshot(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR,
+ ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT);
+}
+
+void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res)
+{
+ /*
+ * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
+ * condition, the module was paused and now we have a buffer queued
+ * on the output again. Restart the pipeline if running in continuous
+ * mode.
+ */
+ if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
+ res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ resizer_enable_oneshot(res);
+ isp_video_dmaqueue_flags_clr(&res->video_out);
+ }
+}
+
+static void resizer_isr_buffer(struct isp_res_device *res)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
+ struct isp_buffer *buffer;
+ int restart = 0;
+
+ if (res->state == ISP_PIPELINE_STREAM_STOPPED)
+ return;
+
+ /* Complete the output buffer and, if reading from memory, the input
+ * buffer.
+ */
+ buffer = omap3isp_video_buffer_next(&res->video_out);
+ if (buffer != NULL) {
+ resizer_set_outaddr(res, buffer->isp_addr);
+ restart = 1;
+ }
+
+ pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ buffer = omap3isp_video_buffer_next(&res->video_in);
+ if (buffer != NULL)
+ resizer_set_inaddr(res, buffer->isp_addr);
+ pipe->state |= ISP_PIPELINE_IDLE_INPUT;
+ }
+
+ if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
+ if (isp_pipeline_ready(pipe))
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ } else {
+ /* If an underrun occurs, the video queue operation handler will
+ * restart the resizer. Otherwise restart it immediately.
+ */
+ if (restart)
+ resizer_enable_oneshot(res);
+ }
+}
+
+/*
+ * omap3isp_resizer_isr - ISP resizer interrupt handler
+ *
+ * Manage the resizer video buffers and configure shadowed and busy-locked
+ * registers.
+ */
+void omap3isp_resizer_isr(struct isp_res_device *res)
+{
+ struct v4l2_mbus_framefmt *informat, *outformat;
+
+ if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
+ return;
+
+ if (res->applycrop) {
+ outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_ACTIVE);
+ resizer_set_crop_params(res, informat, outformat);
+ res->applycrop = 0;
+ }
+
+ resizer_isr_buffer(res);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP video operations
+ */
+
+static int resizer_video_queue(struct isp_video *video,
+ struct isp_buffer *buffer)
+{
+ struct isp_res_device *res = &video->isp->isp_res;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ resizer_set_inaddr(res, buffer->isp_addr);
+
+ /*
+ * We now have a buffer queued on the output. Despite what the
+ * TRM says, the resizer can't be restarted immediately.
+ * Enabling it in one shot mode in the middle of a frame (or at
+ * least asynchronously to the frame) results in the output
+ * being shifted randomly left/right and up/down, as if the
+ * hardware didn't synchronize itself to the beginning of the
+ * frame correctly.
+ *
+ * Restart the resizer on the next sync interrupt if running in
+ * continuous mode or when starting the stream.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ resizer_set_outaddr(res, buffer->isp_addr);
+
+ return 0;
+}
+
+static const struct isp_video_operations resizer_video_ops = {
+ .queue = resizer_video_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+/*
+ * resizer_set_stream - Enable/Disable streaming on resizer subdev
+ * @sd: ISP resizer V4L2 subdev
+ * @enable: 1 == Enable, 0 == Disable
+ *
+ * The resizer hardware can't be enabled without a memory buffer to write to.
+ * As the s_stream operation is called in response to a STREAMON call without
+ * any buffer queued yet, just update the state field and return immediately.
+ * The resizer will be enabled in resizer_video_queue().
+ */
+static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct isp_video *video_out = &res->video_out;
+ struct isp_device *isp = to_isp_device(res);
+ struct device *dev = to_device(res);
+
+ if (res->state == ISP_PIPELINE_STREAM_STOPPED) {
+ if (enable == ISP_PIPELINE_STREAM_STOPPED)
+ return 0;
+
+ omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER);
+ resizer_configure(res);
+ resizer_print_status(res);
+ }
+
+ switch (enable) {
+ case ISP_PIPELINE_STREAM_CONTINUOUS:
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
+ if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
+ resizer_enable_oneshot(res);
+ isp_video_dmaqueue_flags_clr(video_out);
+ }
+ break;
+
+ case ISP_PIPELINE_STREAM_SINGLESHOT:
+ if (res->input == RESIZER_INPUT_MEMORY)
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ);
+ omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
+
+ resizer_enable_oneshot(res);
+ break;
+
+ case ISP_PIPELINE_STREAM_STOPPED:
+ if (omap3isp_module_sync_idle(&sd->entity, &res->wait,
+ &res->stopping))
+ dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
+ omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ |
+ OMAP3_ISP_SBL_RESIZER_WRITE);
+ omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER);
+ isp_video_dmaqueue_flags_clr(video_out);
+ break;
+ }
+
+ res->state = enable;
+ return 0;
+}
+
+/*
+ * resizer_try_crop - mangles crop parameters.
+ */
+static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
+ const struct v4l2_mbus_framefmt *source,
+ struct v4l2_rect *crop)
+{
+ const unsigned int spv = DEFAULT_PHASE;
+ const unsigned int sph = DEFAULT_PHASE;
+
+ /* Crop rectangle is constrained by the output size so that zoom ratio
+ * cannot exceed +/-4.0.
+ */
+ unsigned int min_width =
+ ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7;
+ unsigned int min_height =
+ ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4;
+ unsigned int max_width =
+ ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7;
+ unsigned int max_height =
+ ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7;
+
+ crop->width = clamp_t(u32, crop->width, min_width, max_width);
+ crop->height = clamp_t(u32, crop->height, min_height, max_height);
+
+ /* Crop can not go beyond of the input rectangle */
+ crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH);
+ crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH,
+ sink->width - crop->left);
+ crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT);
+ crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT,
+ sink->height - crop->top);
+}
+
+/*
+ * resizer_get_selection - Retrieve a selection rectangle on a pad
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the sink pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int resizer_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format_source;
+ struct v4l2_mbus_framefmt *format_sink;
+ struct resizer_ratio ratio;
+
+ if (sel->pad != RESZ_PAD_SINK)
+ return -EINVAL;
+
+ format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ sel->which);
+ format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ sel->which);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ resizer_try_crop(format_sink, format_source, &sel->r);
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *__resizer_get_crop(res, fh, sel->which);
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_set_selection - Set a selection rectangle on a pad
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the sink pad.
+ *
+ * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag
+ * was always set.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int resizer_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct isp_device *isp = to_isp_device(res);
+ struct v4l2_mbus_framefmt *format_sink, *format_source;
+ struct resizer_ratio ratio;
+
+ if (sel->target != V4L2_SEL_TGT_CROP ||
+ sel->pad != RESZ_PAD_SINK)
+ return -EINVAL;
+
+ format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ sel->which);
+ format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ sel->which);
+
+ dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ sel->which);
+
+ dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
+ format_sink->width, format_sink->height,
+ format_source->width, format_source->height);
+
+ /* Clamp the crop rectangle to the bounds, and then mangle it further to
+ * fulfill the TRM equations. Store the clamped but otherwise unmangled
+ * rectangle to avoid cropping the input multiple times: when an
+ * application sets the output format, the current crop rectangle is
+ * mangled during crop rectangle computation, which would lead to a new,
+ * smaller input crop rectangle every time the output size is set if we
+ * stored the mangled rectangle.
+ */
+ resizer_try_crop(format_sink, format_source, &sel->r);
+ *__resizer_get_crop(res, fh, sel->which) = sel->r;
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ res->ratio = ratio;
+ res->crop.active = sel->r;
+
+ /*
+ * set_selection can be called while streaming is on. In this case the
+ * crop values will be set in the next IRQ.
+ */
+ if (res->state != ISP_PIPELINE_STREAM_STOPPED)
+ res->applycrop = 1;
+
+ return 0;
+}
+
+/* resizer pixel formats */
+static const unsigned int resizer_formats[] = {
+ V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16,
+};
+
+static unsigned int resizer_max_in_width(struct isp_res_device *res)
+{
+ struct isp_device *isp = to_isp_device(res);
+
+ if (res->input == RESIZER_INPUT_MEMORY) {
+ return MAX_IN_WIDTH_MEMORY_MODE;
+ } else {
+ if (isp->revision == ISP_REVISION_1_0)
+ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1;
+ else
+ return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2;
+ }
+}
+
+/*
+ * resizer_try_format - Handle try format by pad subdev method
+ * @res : ISP resizer device
+ * @fh : V4L2 subdev file handle
+ * @pad : pad num
+ * @fmt : pointer to v4l2 format structure
+ * @which : wanted subdev format
+ */
+static void resizer_try_format(struct isp_res_device *res,
+ struct v4l2_subdev_fh *fh, unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ struct resizer_ratio ratio;
+ struct v4l2_rect crop;
+
+ switch (pad) {
+ case RESZ_PAD_SINK:
+ if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 &&
+ fmt->code != V4L2_MBUS_FMT_UYVY8_1X16)
+ fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
+
+ fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
+ resizer_max_in_width(res));
+ fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
+ MAX_IN_HEIGHT);
+ break;
+
+ case RESZ_PAD_SOURCE:
+ format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which);
+ fmt->code = format->code;
+
+ crop = *__resizer_get_crop(res, fh, which);
+ resizer_calc_ratios(res, &crop, fmt, &ratio);
+ break;
+ }
+
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+/*
+ * resizer_enum_mbus_code - Handle pixel format enumeration
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @code : pointer to v4l2_subdev_mbus_code_enum structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (code->pad == RESZ_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(resizer_formats))
+ return -EINVAL;
+
+ code->code = resizer_formats[code->index];
+ } else {
+ if (code->index != 0)
+ return -EINVAL;
+
+ format = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ V4L2_SUBDEV_FORMAT_TRY);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int resizer_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt format;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = 1;
+ format.height = 1;
+ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->min_width = format.width;
+ fse->min_height = format.height;
+
+ if (format.code != fse->code)
+ return -EINVAL;
+
+ format.code = fse->code;
+ format.width = -1;
+ format.height = -1;
+ resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
+ fse->max_width = format.width;
+ fse->max_height = format.height;
+
+ return 0;
+}
+
+/*
+ * resizer_get_format - Handle get format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ fmt->format = *format;
+ return 0;
+}
+
+/*
+ * resizer_set_format - Handle set format by pads subdev method
+ * @sd : pointer to v4l2 subdev structure
+ * @fh : V4L2 subdev file handle
+ * @fmt : pointer to v4l2 subdev format structure
+ * return -EINVAL or zero on success
+ */
+static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
+
+ format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
+ if (format == NULL)
+ return -EINVAL;
+
+ resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which);
+ *format = fmt->format;
+
+ if (fmt->pad == RESZ_PAD_SINK) {
+ /* reset crop rectangle */
+ crop = __resizer_get_crop(res, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ /* Propagate the format from sink to source */
+ format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+ resizer_try_format(res, fh, RESZ_PAD_SOURCE, format,
+ fmt->which);
+ }
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ /* Compute and store the active crop rectangle and resizer
+ * ratios. format already points to the source pad active
+ * format.
+ */
+ res->crop.active = res->crop.request;
+ resizer_calc_ratios(res, &res->crop.active, format,
+ &res->ratio);
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_init_formats - Initialize formats on all pads
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+static int resizer_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+
+ memset(&format, 0, sizeof(format));
+ format.pad = RESZ_PAD_SINK;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
+ format.format.width = 4096;
+ format.format.height = 4096;
+ resizer_set_format(sd, fh, &format);
+
+ return 0;
+}
+
+/* subdev video operations */
+static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
+ .s_stream = resizer_set_stream,
+};
+
+/* subdev pad operations */
+static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
+ .enum_mbus_code = resizer_enum_mbus_code,
+ .enum_frame_size = resizer_enum_frame_size,
+ .get_fmt = resizer_get_format,
+ .set_fmt = resizer_set_format,
+ .get_selection = resizer_get_selection,
+ .set_selection = resizer_set_selection,
+};
+
+/* subdev operations */
+static const struct v4l2_subdev_ops resizer_v4l2_ops = {
+ .video = &resizer_v4l2_video_ops,
+ .pad = &resizer_v4l2_pad_ops,
+};
+
+/* subdev internal operations */
+static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
+ .open = resizer_init_formats,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+/*
+ * resizer_link_setup - Setup resizer connections.
+ * @entity : Pointer to media entity structure
+ * @local : Pointer to local pad array
+ * @remote : Pointer to remote pad array
+ * @flags : Link flags
+ * return -EINVAL or zero on success
+ */
+static int resizer_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+
+ switch (local->index | media_entity_type(remote->entity)) {
+ case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE:
+ /* read from memory */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (res->input == RESIZER_INPUT_VP)
+ return -EBUSY;
+ res->input = RESIZER_INPUT_MEMORY;
+ } else {
+ if (res->input == RESIZER_INPUT_MEMORY)
+ res->input = RESIZER_INPUT_NONE;
+ }
+ break;
+
+ case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
+ /* read from ccdc or previewer */
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (res->input == RESIZER_INPUT_MEMORY)
+ return -EBUSY;
+ res->input = RESIZER_INPUT_VP;
+ } else {
+ if (res->input == RESIZER_INPUT_VP)
+ res->input = RESIZER_INPUT_NONE;
+ }
+ break;
+
+ case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
+ /* resizer always write to memory */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* media operations */
+static const struct media_entity_operations resizer_media_ops = {
+ .link_setup = resizer_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
+{
+ v4l2_device_unregister_subdev(&res->subdev);
+ omap3isp_video_unregister(&res->video_in);
+ omap3isp_video_unregister(&res->video_out);
+}
+
+int omap3isp_resizer_register_entities(struct isp_res_device *res,
+ struct v4l2_device *vdev)
+{
+ int ret;
+
+ /* Register the subdev and video nodes. */
+ ret = v4l2_device_register_subdev(vdev, &res->subdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_in, vdev);
+ if (ret < 0)
+ goto error;
+
+ ret = omap3isp_video_register(&res->video_out, vdev);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ omap3isp_resizer_unregister_entities(res);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP resizer initialization and cleanup
+ */
+
+/*
+ * resizer_init_entities - Initialize resizer subdev and media entity.
+ * @res : Pointer to resizer device structure
+ * return -ENOMEM or zero on success
+ */
+static int resizer_init_entities(struct isp_res_device *res)
+{
+ struct v4l2_subdev *sd = &res->subdev;
+ struct media_pad *pads = res->pads;
+ struct media_entity *me = &sd->entity;
+ int ret;
+
+ res->input = RESIZER_INPUT_NONE;
+
+ v4l2_subdev_init(sd, &resizer_v4l2_ops);
+ sd->internal_ops = &resizer_v4l2_internal_ops;
+ strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name));
+ sd->grp_id = 1 << 16; /* group ID for isp subdevs */
+ v4l2_set_subdevdata(sd, res);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ me->ops = &resizer_media_ops;
+ ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0);
+ if (ret < 0)
+ return ret;
+
+ resizer_init_formats(sd, NULL);
+
+ res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ res->video_in.ops = &resizer_video_ops;
+ res->video_in.isp = to_isp_device(res);
+ res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ res->video_in.bpl_alignment = 32;
+ res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ res->video_out.ops = &resizer_video_ops;
+ res->video_out.isp = to_isp_device(res);
+ res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
+ res->video_out.bpl_alignment = 32;
+
+ ret = omap3isp_video_init(&res->video_in, "resizer");
+ if (ret < 0)
+ goto error_video_in;
+
+ ret = omap3isp_video_init(&res->video_out, "resizer");
+ if (ret < 0)
+ goto error_video_out;
+
+ res->video_out.video.entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+ /* Connect the video nodes to the resizer subdev. */
+ ret = media_entity_create_link(&res->video_in.video.entity, 0,
+ &res->subdev.entity, RESZ_PAD_SINK, 0);
+ if (ret < 0)
+ goto error_link;
+
+ ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
+ &res->video_out.video.entity, 0, 0);
+ if (ret < 0)
+ goto error_link;
+
+ return 0;
+
+error_link:
+ omap3isp_video_cleanup(&res->video_out);
+error_video_out:
+ omap3isp_video_cleanup(&res->video_in);
+error_video_in:
+ media_entity_cleanup(&res->subdev.entity);
+ return ret;
+}
+
+/*
+ * isp_resizer_init - Resizer initialization.
+ * @isp : Pointer to ISP device
+ * return -ENOMEM or zero on success
+ */
+int omap3isp_resizer_init(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
+
+ init_waitqueue_head(&res->wait);
+ atomic_set(&res->stopping, 0);
+ return resizer_init_entities(res);
+}
+
+void omap3isp_resizer_cleanup(struct isp_device *isp)
+{
+ struct isp_res_device *res = &isp->isp_res;
+
+ omap3isp_video_cleanup(&res->video_in);
+ omap3isp_video_cleanup(&res->video_out);
+ media_entity_cleanup(&res->subdev.entity);
+}
diff --git a/drivers/media/video/omap3isp/ispresizer.h b/drivers/media/platform/omap3isp/ispresizer.h
index 70c1c0e1bbdf..70c1c0e1bbdf 100644
--- a/drivers/media/video/omap3isp/ispresizer.h
+++ b/drivers/media/platform/omap3isp/ispresizer.h
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c
new file mode 100644
index 000000000000..d7ac76b5c2ae
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispstat.c
@@ -0,0 +1,1102 @@
+/*
+ * ispstat.c
+ *
+ * TI OMAP3 ISP - Statistics core
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "isp.h"
+
+#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0)
+
+/*
+ * MAGIC_SIZE must always be the greatest common divisor of
+ * AEWB_PACKET_SIZE and AF_PAXEL_SIZE.
+ */
+#define MAGIC_SIZE 16
+#define MAGIC_NUM 0x55
+
+/* HACK: AF module seems to be writing one more paxel data than it should. */
+#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE
+
+/*
+ * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes
+ * the next buffer to start to be written in the same point where the overflow
+ * occurred instead of the configured address. The only known way to make it to
+ * go back to a valid state is having a valid buffer processing. Of course it
+ * requires at least a doubled buffer size to avoid an access to invalid memory
+ * region. But it does not fix everything. It may happen more than one
+ * consecutive SBL overflows. In that case, it might be unpredictable how many
+ * buffers the allocated memory should fit. For that case, a recover
+ * configuration was created. It produces the minimum buffer size for each H3A
+ * module and decrease the change for more SBL overflows. This recover state
+ * will be enabled every time a SBL overflow occur. As the output buffer size
+ * isn't big, it's possible to have an extra size able to fit many recover
+ * buffers making it extreamily unlikely to have an access to invalid memory
+ * region.
+ */
+#define NUM_H3A_RECOVER_BUFS 10
+
+/*
+ * HACK: Because of HW issues the generic layer sometimes need to have
+ * different behaviour for different statistic modules.
+ */
+#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af)
+#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb)
+#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat))
+
+static void __isp_stat_buf_sync_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size, enum dma_data_direction dir,
+ void (*dma_sync)(struct device *,
+ dma_addr_t, unsigned long, size_t,
+ enum dma_data_direction))
+{
+ struct device *dev = stat->isp->dev;
+ struct page *pg;
+ dma_addr_t dma_addr;
+ u32 offset;
+
+ /* Initial magic words */
+ pg = vmalloc_to_page(buf->virt_addr);
+ dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
+ dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
+
+ /* Final magic words */
+ pg = vmalloc_to_page(buf->virt_addr + buf_size);
+ dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
+ offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
+ dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
+}
+
+static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size,
+ enum dma_data_direction dir)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
+ dma_sync_single_range_for_device);
+}
+
+static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
+ struct ispstat_buffer *buf,
+ u32 buf_size,
+ enum dma_data_direction dir)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
+ dma_sync_single_range_for_cpu);
+}
+
+static int isp_stat_buf_check_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ const u32 buf_size = IS_H3A_AF(stat) ?
+ buf->buf_size + AF_EXTRA_DATA : buf->buf_size;
+ u8 *w;
+ u8 *end;
+ int ret = -EINVAL;
+
+ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
+
+ /* Checking initial magic numbers. They shouldn't be here anymore. */
+ for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++)
+ if (likely(*w != MAGIC_NUM))
+ ret = 0;
+
+ if (ret) {
+ dev_dbg(stat->isp->dev, "%s: beginning magic check does not "
+ "match.\n", stat->subdev.name);
+ return ret;
+ }
+
+ /* Checking magic numbers at the end. They must be still here. */
+ for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;
+ w < end; w++) {
+ if (unlikely(*w != MAGIC_NUM)) {
+ dev_dbg(stat->isp->dev, "%s: endding magic check does "
+ "not match.\n", stat->subdev.name);
+ return -EINVAL;
+ }
+ }
+
+ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
+ DMA_FROM_DEVICE);
+
+ return 0;
+}
+
+static void isp_stat_buf_insert_magic(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ const u32 buf_size = IS_H3A_AF(stat) ?
+ stat->buf_size + AF_EXTRA_DATA : stat->buf_size;
+
+ isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
+
+ /*
+ * Inserting MAGIC_NUM at the beginning and end of the buffer.
+ * buf->buf_size is set only after the buffer is queued. For now the
+ * right buf_size for the current configuration is pointed by
+ * stat->buf_size.
+ */
+ memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE);
+ memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE);
+
+ isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
+ DMA_BIDIRECTIONAL);
+}
+
+static void isp_stat_buf_sync_for_device(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+}
+
+static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
+ struct ispstat_buffer *buf)
+{
+ if (IS_COHERENT_BUF(stat))
+ return;
+
+ dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents, DMA_FROM_DEVICE);
+}
+
+static void isp_stat_buf_clear(struct ispstat *stat)
+{
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++)
+ stat->buf[i].empty = 1;
+}
+
+static struct ispstat_buffer *
+__isp_stat_buf_find(struct ispstat *stat, int look_empty)
+{
+ struct ispstat_buffer *found = NULL;
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *curr = &stat->buf[i];
+
+ /*
+ * Don't select the buffer which is being copied to
+ * userspace or used by the module.
+ */
+ if (curr == stat->locked_buf || curr == stat->active_buf)
+ continue;
+
+ /* Don't select uninitialised buffers if it's not required */
+ if (!look_empty && curr->empty)
+ continue;
+
+ /* Pick uninitialised buffer over anything else if look_empty */
+ if (curr->empty) {
+ found = curr;
+ break;
+ }
+
+ /* Choose the oldest buffer */
+ if (!found ||
+ (s32)curr->frame_number - (s32)found->frame_number < 0)
+ found = curr;
+ }
+
+ return found;
+}
+
+static inline struct ispstat_buffer *
+isp_stat_buf_find_oldest(struct ispstat *stat)
+{
+ return __isp_stat_buf_find(stat, 0);
+}
+
+static inline struct ispstat_buffer *
+isp_stat_buf_find_oldest_or_empty(struct ispstat *stat)
+{
+ return __isp_stat_buf_find(stat, 1);
+}
+
+static int isp_stat_buf_queue(struct ispstat *stat)
+{
+ if (!stat->active_buf)
+ return STAT_NO_BUF;
+
+ do_gettimeofday(&stat->active_buf->ts);
+
+ stat->active_buf->buf_size = stat->buf_size;
+ if (isp_stat_buf_check_magic(stat, stat->active_buf)) {
+ dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n",
+ stat->subdev.name);
+ return STAT_NO_BUF;
+ }
+ stat->active_buf->config_counter = stat->config_counter;
+ stat->active_buf->frame_number = stat->frame_number;
+ stat->active_buf->empty = 0;
+ stat->active_buf = NULL;
+
+ return STAT_BUF_DONE;
+}
+
+/* Get next free buffer to write the statistics to and mark it active. */
+static void isp_stat_buf_next(struct ispstat *stat)
+{
+ if (unlikely(stat->active_buf))
+ /* Overwriting unused active buffer */
+ dev_dbg(stat->isp->dev, "%s: new buffer requested without "
+ "queuing active one.\n",
+ stat->subdev.name);
+ else
+ stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat);
+}
+
+static void isp_stat_buf_release(struct ispstat *stat)
+{
+ unsigned long flags;
+
+ isp_stat_buf_sync_for_device(stat, stat->locked_buf);
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+ stat->locked_buf = NULL;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+}
+
+/* Get buffer to userspace. */
+static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
+ struct omap3isp_stat_data *data)
+{
+ int rval = 0;
+ unsigned long flags;
+ struct ispstat_buffer *buf;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ while (1) {
+ buf = isp_stat_buf_find_oldest(stat);
+ if (!buf) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n",
+ stat->subdev.name);
+ return ERR_PTR(-EBUSY);
+ }
+ if (isp_stat_buf_check_magic(stat, buf)) {
+ dev_dbg(stat->isp->dev, "%s: current buffer has "
+ "corrupted data\n.", stat->subdev.name);
+ /* Mark empty because it doesn't have valid data. */
+ buf->empty = 1;
+ } else {
+ /* Buffer isn't corrupted. */
+ break;
+ }
+ }
+
+ stat->locked_buf = buf;
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ if (buf->buf_size > data->buf_size) {
+ dev_warn(stat->isp->dev, "%s: userspace's buffer size is "
+ "not enough.\n", stat->subdev.name);
+ isp_stat_buf_release(stat);
+ return ERR_PTR(-EINVAL);
+ }
+
+ isp_stat_buf_sync_for_cpu(stat, buf);
+
+ rval = copy_to_user(data->buf,
+ buf->virt_addr,
+ buf->buf_size);
+
+ if (rval) {
+ dev_info(stat->isp->dev,
+ "%s: failed copying %d bytes of stat data\n",
+ stat->subdev.name, rval);
+ buf = ERR_PTR(-EFAULT);
+ isp_stat_buf_release(stat);
+ }
+
+ return buf;
+}
+
+static void isp_stat_bufs_free(struct ispstat *stat)
+{
+ struct isp_device *isp = stat->isp;
+ int i;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+
+ if (!IS_COHERENT_BUF(stat)) {
+ if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
+ continue;
+ if (buf->iovm)
+ dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
+ buf->iovm->sgt->nents,
+ DMA_FROM_DEVICE);
+ omap_iommu_vfree(isp->domain, isp->dev,
+ buf->iommu_addr);
+ } else {
+ if (!buf->virt_addr)
+ continue;
+ dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
+ buf->virt_addr, buf->dma_addr);
+ }
+ buf->iommu_addr = 0;
+ buf->iovm = NULL;
+ buf->dma_addr = 0;
+ buf->virt_addr = NULL;
+ buf->empty = 1;
+ }
+
+ dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n",
+ stat->subdev.name);
+
+ stat->buf_alloc_size = 0;
+ stat->active_buf = NULL;
+}
+
+static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
+{
+ struct isp_device *isp = stat->isp;
+ int i;
+
+ stat->buf_alloc_size = size;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+ struct iovm_struct *iovm;
+
+ WARN_ON(buf->dma_addr);
+ buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
+ size, IOMMU_FLAG);
+ if (IS_ERR((void *)buf->iommu_addr)) {
+ dev_err(stat->isp->dev,
+ "%s: Can't acquire memory for "
+ "buffer %d\n", stat->subdev.name, i);
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+
+ iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
+ if (!iovm ||
+ !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
+ DMA_FROM_DEVICE)) {
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+ buf->iovm = iovm;
+
+ buf->virt_addr = omap_da_to_va(stat->isp->dev,
+ (u32)buf->iommu_addr);
+ buf->empty = 1;
+ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
+ "iommu_addr=0x%08lx virt_addr=0x%08lx",
+ stat->subdev.name, i, buf->iommu_addr,
+ (unsigned long)buf->virt_addr);
+ }
+
+ return 0;
+}
+
+static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
+{
+ int i;
+
+ stat->buf_alloc_size = size;
+
+ for (i = 0; i < STAT_MAX_BUFS; i++) {
+ struct ispstat_buffer *buf = &stat->buf[i];
+
+ WARN_ON(buf->iommu_addr);
+ buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
+ &buf->dma_addr, GFP_KERNEL | GFP_DMA);
+
+ if (!buf->virt_addr || !buf->dma_addr) {
+ dev_info(stat->isp->dev,
+ "%s: Can't acquire memory for "
+ "DMA buffer %d\n", stat->subdev.name, i);
+ isp_stat_bufs_free(stat);
+ return -ENOMEM;
+ }
+ buf->empty = 1;
+
+ dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
+ "dma_addr=0x%08lx virt_addr=0x%08lx\n",
+ stat->subdev.name, i, (unsigned long)buf->dma_addr,
+ (unsigned long)buf->virt_addr);
+ }
+
+ return 0;
+}
+
+static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ BUG_ON(stat->locked_buf != NULL);
+
+ /* Are the old buffers big enough? */
+ if (stat->buf_alloc_size >= size) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ return 0;
+ }
+
+ if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) {
+ dev_info(stat->isp->dev,
+ "%s: trying to allocate memory when busy\n",
+ stat->subdev.name);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+ return -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ isp_stat_bufs_free(stat);
+
+ if (IS_COHERENT_BUF(stat))
+ return isp_stat_bufs_alloc_dma(stat, size);
+ else
+ return isp_stat_bufs_alloc_iommu(stat, size);
+}
+
+static void isp_stat_queue_event(struct ispstat *stat, int err)
+{
+ struct video_device *vdev = stat->subdev.devnode;
+ struct v4l2_event event;
+ struct omap3isp_stat_event_status *status = (void *)event.u.data;
+
+ memset(&event, 0, sizeof(event));
+ if (!err) {
+ status->frame_number = stat->frame_number;
+ status->config_counter = stat->config_counter;
+ } else {
+ status->buf_err = 1;
+ }
+ event.type = stat->event_type;
+ v4l2_event_queue(vdev, &event);
+}
+
+
+/*
+ * omap3isp_stat_request_statistics - Request statistics.
+ * @data: Pointer to return statistics data.
+ *
+ * Returns 0 if successful.
+ */
+int omap3isp_stat_request_statistics(struct ispstat *stat,
+ struct omap3isp_stat_data *data)
+{
+ struct ispstat_buffer *buf;
+
+ if (stat->state != ISPSTAT_ENABLED) {
+ dev_dbg(stat->isp->dev, "%s: engine not enabled.\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ mutex_lock(&stat->ioctl_lock);
+ buf = isp_stat_buf_get(stat, data);
+ if (IS_ERR(buf)) {
+ mutex_unlock(&stat->ioctl_lock);
+ return PTR_ERR(buf);
+ }
+
+ data->ts = buf->ts;
+ data->config_counter = buf->config_counter;
+ data->frame_number = buf->frame_number;
+ data->buf_size = buf->buf_size;
+
+ buf->empty = 1;
+ isp_stat_buf_release(stat);
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+/*
+ * omap3isp_stat_config - Receives new statistic engine configuration.
+ * @new_conf: Pointer to config structure.
+ *
+ * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if
+ * was unable to allocate memory for the buffer, or other errors if parameters
+ * are invalid.
+ */
+int omap3isp_stat_config(struct ispstat *stat, void *new_conf)
+{
+ int ret;
+ unsigned long irqflags;
+ struct ispstat_generic_config *user_cfg = new_conf;
+ u32 buf_size = user_cfg->buf_size;
+
+ if (!new_conf) {
+ dev_dbg(stat->isp->dev, "%s: configuration is NULL\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ mutex_lock(&stat->ioctl_lock);
+
+ dev_dbg(stat->isp->dev, "%s: configuring module with buffer "
+ "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size);
+
+ ret = stat->ops->validate_params(stat, new_conf);
+ if (ret) {
+ mutex_unlock(&stat->ioctl_lock);
+ dev_dbg(stat->isp->dev, "%s: configuration values are "
+ "invalid.\n", stat->subdev.name);
+ return ret;
+ }
+
+ if (buf_size != user_cfg->buf_size)
+ dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size "
+ "request to 0x%08lx\n", stat->subdev.name,
+ (unsigned long)user_cfg->buf_size);
+
+ /*
+ * Hack: H3A modules may need a doubled buffer size to avoid access
+ * to a invalid memory address after a SBL overflow.
+ * The buffer size is always PAGE_ALIGNED.
+ * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be
+ * inserted at the end to data integrity check purpose.
+ * Hack 3: AF module writes one paxel data more than it should, so
+ * the buffer allocation must consider it to avoid invalid memory
+ * access.
+ * Hack 4: H3A need to allocate extra space for the recover state.
+ */
+ if (IS_H3A(stat)) {
+ buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE;
+ if (IS_H3A_AF(stat))
+ /*
+ * Adding one extra paxel data size for each recover
+ * buffer + 2 regular ones.
+ */
+ buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2);
+ if (stat->recover_priv) {
+ struct ispstat_generic_config *recover_cfg =
+ stat->recover_priv;
+ buf_size += recover_cfg->buf_size *
+ NUM_H3A_RECOVER_BUFS;
+ }
+ buf_size = PAGE_ALIGN(buf_size);
+ } else { /* Histogram */
+ buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE);
+ }
+
+ ret = isp_stat_bufs_alloc(stat, buf_size);
+ if (ret) {
+ mutex_unlock(&stat->ioctl_lock);
+ return ret;
+ }
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ stat->ops->set_params(stat, new_conf);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+
+ /*
+ * Returning the right future config_counter for this setup, so
+ * userspace can *know* when it has been applied.
+ */
+ user_cfg->config_counter = stat->config_counter + stat->inc_config;
+
+ /* Module has a valid configuration. */
+ stat->configured = 1;
+ dev_dbg(stat->isp->dev, "%s: module has been successfully "
+ "configured.\n", stat->subdev.name);
+
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+/*
+ * isp_stat_buf_process - Process statistic buffers.
+ * @buf_state: points out if buffer is ready to be processed. It's necessary
+ * because histogram needs to copy the data from internal memory
+ * before be able to process the buffer.
+ */
+static int isp_stat_buf_process(struct ispstat *stat, int buf_state)
+{
+ int ret = STAT_NO_BUF;
+
+ if (!atomic_add_unless(&stat->buf_err, -1, 0) &&
+ buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) {
+ ret = isp_stat_buf_queue(stat);
+ isp_stat_buf_next(stat);
+ }
+
+ return ret;
+}
+
+int omap3isp_stat_pcr_busy(struct ispstat *stat)
+{
+ return stat->ops->busy(stat);
+}
+
+int omap3isp_stat_busy(struct ispstat *stat)
+{
+ return omap3isp_stat_pcr_busy(stat) | stat->buf_processing |
+ (stat->state != ISPSTAT_DISABLED);
+}
+
+/*
+ * isp_stat_pcr_enable - Disables/Enables statistic engines.
+ * @pcr_enable: 0/1 - Disables/Enables the engine.
+ *
+ * Must be called from ISP driver when the module is idle and synchronized
+ * with CCDC.
+ */
+static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable)
+{
+ if ((stat->state != ISPSTAT_ENABLING &&
+ stat->state != ISPSTAT_ENABLED) && pcr_enable)
+ /* Userspace has disabled the module. Aborting. */
+ return;
+
+ stat->ops->enable(stat, pcr_enable);
+ if (stat->state == ISPSTAT_DISABLING && !pcr_enable)
+ stat->state = ISPSTAT_DISABLED;
+ else if (stat->state == ISPSTAT_ENABLING && pcr_enable)
+ stat->state = ISPSTAT_ENABLED;
+}
+
+void omap3isp_stat_suspend(struct ispstat *stat)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+
+ if (stat->state != ISPSTAT_DISABLED)
+ stat->ops->enable(stat, 0);
+ if (stat->state == ISPSTAT_ENABLED)
+ stat->state = ISPSTAT_SUSPENDED;
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+}
+
+void omap3isp_stat_resume(struct ispstat *stat)
+{
+ /* Module will be re-enabled with its pipeline */
+ if (stat->state == ISPSTAT_SUSPENDED)
+ stat->state = ISPSTAT_ENABLING;
+}
+
+static void isp_stat_try_enable(struct ispstat *stat)
+{
+ unsigned long irqflags;
+
+ if (stat->priv == NULL)
+ /* driver wasn't initialised */
+ return;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing &&
+ stat->buf_alloc_size) {
+ /*
+ * Userspace's requested to enable the engine but it wasn't yet.
+ * Let's do that now.
+ */
+ stat->update = 1;
+ isp_stat_buf_next(stat);
+ stat->ops->setup_regs(stat, stat->priv);
+ isp_stat_buf_insert_magic(stat, stat->active_buf);
+
+ /*
+ * H3A module has some hw issues which forces the driver to
+ * ignore next buffers even if it was disabled in the meantime.
+ * On the other hand, Histogram shouldn't ignore buffers anymore
+ * if it's being enabled.
+ */
+ if (!IS_H3A(stat))
+ atomic_set(&stat->buf_err, 0);
+
+ isp_stat_pcr_enable(stat, 1);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ dev_dbg(stat->isp->dev, "%s: module is enabled.\n",
+ stat->subdev.name);
+ } else {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ }
+}
+
+void omap3isp_stat_isr_frame_sync(struct ispstat *stat)
+{
+ isp_stat_try_enable(stat);
+}
+
+void omap3isp_stat_sbl_overflow(struct ispstat *stat)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ /*
+ * Due to a H3A hw issue which prevents the next buffer to start from
+ * the correct memory address, 2 buffers must be ignored.
+ */
+ atomic_set(&stat->buf_err, 2);
+
+ /*
+ * If more than one SBL overflow happen in a row, H3A module may access
+ * invalid memory region.
+ * stat->sbl_ovl_recover is set to tell to the driver to temporarily use
+ * a soft configuration which helps to avoid consecutive overflows.
+ */
+ if (stat->recover_priv)
+ stat->sbl_ovl_recover = 1;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+}
+
+/*
+ * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible
+ * @enable: 0/1 - Disables/Enables the engine.
+ *
+ * Client should configure all the module registers before this.
+ * This function can be called from a userspace request.
+ */
+int omap3isp_stat_enable(struct ispstat *stat, u8 enable)
+{
+ unsigned long irqflags;
+
+ dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n",
+ stat->subdev.name, enable ? "enable" : "disable");
+
+ /* Prevent enabling while configuring */
+ mutex_lock(&stat->ioctl_lock);
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+
+ if (!stat->configured && enable) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ mutex_unlock(&stat->ioctl_lock);
+ dev_dbg(stat->isp->dev, "%s: cannot enable module as it's "
+ "never been successfully configured so far.\n",
+ stat->subdev.name);
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if (stat->state == ISPSTAT_DISABLING)
+ /* Previous disabling request wasn't done yet */
+ stat->state = ISPSTAT_ENABLED;
+ else if (stat->state == ISPSTAT_DISABLED)
+ /* Module is now being enabled */
+ stat->state = ISPSTAT_ENABLING;
+ } else {
+ if (stat->state == ISPSTAT_ENABLING) {
+ /* Previous enabling request wasn't done yet */
+ stat->state = ISPSTAT_DISABLED;
+ } else if (stat->state == ISPSTAT_ENABLED) {
+ /* Module is now being disabled */
+ stat->state = ISPSTAT_DISABLING;
+ isp_stat_buf_clear(stat);
+ }
+ }
+
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ mutex_unlock(&stat->ioctl_lock);
+
+ return 0;
+}
+
+int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(subdev);
+
+ if (enable) {
+ /*
+ * Only set enable PCR bit if the module was previously
+ * enabled through ioct.
+ */
+ isp_stat_try_enable(stat);
+ } else {
+ unsigned long flags;
+ /* Disable PCR bit and config enable field */
+ omap3isp_stat_enable(stat, 0);
+ spin_lock_irqsave(&stat->isp->stat_lock, flags);
+ stat->ops->enable(stat, 0);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
+
+ /*
+ * If module isn't busy, a new interrupt may come or not to
+ * set the state to DISABLED. As Histogram needs to read its
+ * internal memory to clear it, let interrupt handler
+ * responsible of changing state to DISABLED. If the last
+ * interrupt is coming, it's still safe as the handler will
+ * ignore the second time when state is already set to DISABLED.
+ * It's necessary to synchronize Histogram with streamoff, once
+ * the module may be considered idle before last SDMA transfer
+ * starts if we return here.
+ */
+ if (!omap3isp_stat_pcr_busy(stat))
+ omap3isp_stat_isr(stat);
+
+ dev_dbg(stat->isp->dev, "%s: module is being disabled\n",
+ stat->subdev.name);
+ }
+
+ return 0;
+}
+
+/*
+ * __stat_isr - Interrupt handler for statistic drivers
+ */
+static void __stat_isr(struct ispstat *stat, int from_dma)
+{
+ int ret = STAT_BUF_DONE;
+ int buf_processing;
+ unsigned long irqflags;
+ struct isp_pipeline *pipe;
+
+ /*
+ * stat->buf_processing must be set before disable module. It's
+ * necessary to not inform too early the buffers aren't busy in case
+ * of SDMA is going to be used.
+ */
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+ if (stat->state == ISPSTAT_DISABLED) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ return;
+ }
+ buf_processing = stat->buf_processing;
+ stat->buf_processing = 1;
+ stat->ops->enable(stat, 0);
+
+ if (buf_processing && !from_dma) {
+ if (stat->state == ISPSTAT_ENABLED) {
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ dev_err(stat->isp->dev,
+ "%s: interrupt occurred when module was still "
+ "processing a buffer.\n", stat->subdev.name);
+ ret = STAT_NO_BUF;
+ goto out;
+ } else {
+ /*
+ * Interrupt handler was called from streamoff when
+ * the module wasn't busy anymore to ensure it is being
+ * disabled after process last buffer. If such buffer
+ * processing has already started, no need to do
+ * anything else.
+ */
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+
+ /* If it's busy we can't process this buffer anymore */
+ if (!omap3isp_stat_pcr_busy(stat)) {
+ if (!from_dma && stat->ops->buf_process)
+ /* Module still need to copy data to buffer. */
+ ret = stat->ops->buf_process(stat);
+ if (ret == STAT_BUF_WAITING_DMA)
+ /* Buffer is not ready yet */
+ return;
+
+ spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
+
+ /*
+ * Histogram needs to read its internal memory to clear it
+ * before be disabled. For that reason, common statistic layer
+ * can return only after call stat's buf_process() operator.
+ */
+ if (stat->state == ISPSTAT_DISABLING) {
+ stat->state = ISPSTAT_DISABLED;
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ stat->buf_processing = 0;
+ return;
+ }
+ pipe = to_isp_pipeline(&stat->subdev.entity);
+ stat->frame_number = atomic_read(&pipe->frame_number);
+
+ /*
+ * Before this point, 'ret' stores the buffer's status if it's
+ * ready to be processed. Afterwards, it holds the status if
+ * it was processed successfully.
+ */
+ ret = isp_stat_buf_process(stat, ret);
+
+ if (likely(!stat->sbl_ovl_recover)) {
+ stat->ops->setup_regs(stat, stat->priv);
+ } else {
+ /*
+ * Using recover config to increase the chance to have
+ * a good buffer processing and make the H3A module to
+ * go back to a valid state.
+ */
+ stat->update = 1;
+ stat->ops->setup_regs(stat, stat->recover_priv);
+ stat->sbl_ovl_recover = 0;
+
+ /*
+ * Set 'update' in case of the module needs to use
+ * regular configuration after next buffer.
+ */
+ stat->update = 1;
+ }
+
+ isp_stat_buf_insert_magic(stat, stat->active_buf);
+
+ /*
+ * Hack: H3A modules may access invalid memory address or send
+ * corrupted data to userspace if more than 1 SBL overflow
+ * happens in a row without re-writing its buffer's start memory
+ * address in the meantime. Such situation is avoided if the
+ * module is not immediately re-enabled when the ISR misses the
+ * timing to process the buffer and to setup the registers.
+ * Because of that, pcr_enable(1) was moved to inside this 'if'
+ * block. But the next interruption will still happen as during
+ * pcr_enable(0) the module was busy.
+ */
+ isp_stat_pcr_enable(stat, 1);
+ spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
+ } else {
+ /*
+ * If a SBL overflow occurs and the H3A driver misses the timing
+ * to process the buffer, stat->buf_err is set and won't be
+ * cleared now. So the next buffer will be correctly ignored.
+ * It's necessary due to a hw issue which makes the next H3A
+ * buffer to start from the memory address where the previous
+ * one stopped, instead of start where it was configured to.
+ * Do not "stat->buf_err = 0" here.
+ */
+
+ if (stat->ops->buf_process)
+ /*
+ * Driver may need to erase current data prior to
+ * process a new buffer. If it misses the timing, the
+ * next buffer might be wrong. So should be ignored.
+ * It happens only for Histogram.
+ */
+ atomic_set(&stat->buf_err, 1);
+
+ ret = STAT_NO_BUF;
+ dev_dbg(stat->isp->dev, "%s: cannot process buffer, "
+ "device is busy.\n", stat->subdev.name);
+ }
+
+out:
+ stat->buf_processing = 0;
+ isp_stat_queue_event(stat, ret != STAT_BUF_DONE);
+}
+
+void omap3isp_stat_isr(struct ispstat *stat)
+{
+ __stat_isr(stat, 0);
+}
+
+void omap3isp_stat_dma_isr(struct ispstat *stat)
+{
+ __stat_isr(stat, 1);
+}
+
+int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct ispstat *stat = v4l2_get_subdevdata(subdev);
+
+ if (sub->type != stat->event_type)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL);
+}
+
+int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ return v4l2_event_unsubscribe(fh, sub);
+}
+
+void omap3isp_stat_unregister_entities(struct ispstat *stat)
+{
+ v4l2_device_unregister_subdev(&stat->subdev);
+}
+
+int omap3isp_stat_register_entities(struct ispstat *stat,
+ struct v4l2_device *vdev)
+{
+ return v4l2_device_register_subdev(vdev, &stat->subdev);
+}
+
+static int isp_stat_init_entities(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ struct v4l2_subdev *subdev = &stat->subdev;
+ struct media_entity *me = &subdev->entity;
+
+ v4l2_subdev_init(subdev, sd_ops);
+ snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
+ subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+ v4l2_set_subdevdata(subdev, stat);
+
+ stat->pad.flags = MEDIA_PAD_FL_SINK;
+ me->ops = NULL;
+
+ return media_entity_init(me, 1, &stat->pad, 0);
+}
+
+int omap3isp_stat_init(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops)
+{
+ int ret;
+
+ stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
+ if (!stat->buf)
+ return -ENOMEM;
+
+ isp_stat_buf_clear(stat);
+ mutex_init(&stat->ioctl_lock);
+ atomic_set(&stat->buf_err, 0);
+
+ ret = isp_stat_init_entities(stat, name, sd_ops);
+ if (ret < 0) {
+ mutex_destroy(&stat->ioctl_lock);
+ kfree(stat->buf);
+ }
+
+ return ret;
+}
+
+void omap3isp_stat_cleanup(struct ispstat *stat)
+{
+ media_entity_cleanup(&stat->subdev.entity);
+ mutex_destroy(&stat->ioctl_lock);
+ isp_stat_bufs_free(stat);
+ kfree(stat->buf);
+}
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h
new file mode 100644
index 000000000000..a6fe653eb237
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispstat.h
@@ -0,0 +1,169 @@
+/*
+ * ispstat.h
+ *
+ * TI OMAP3 ISP - Statistics core
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Contacts: David Cohen <dacohen@gmail.com>
+ * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_STAT_H
+#define OMAP3_ISP_STAT_H
+
+#include <linux/types.h>
+#include <linux/omap3isp.h>
+#include <plat/dma.h>
+#include <media/v4l2-event.h>
+
+#include "isp.h"
+#include "ispvideo.h"
+
+#define STAT_MAX_BUFS 5
+#define STAT_NEVENTS 8
+
+#define STAT_BUF_DONE 0 /* Buffer is ready */
+#define STAT_NO_BUF 1 /* An error has occurred */
+#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */
+
+struct ispstat;
+
+struct ispstat_buffer {
+ unsigned long iommu_addr;
+ struct iovm_struct *iovm;
+ void *virt_addr;
+ dma_addr_t dma_addr;
+ struct timeval ts;
+ u32 buf_size;
+ u32 frame_number;
+ u16 config_counter;
+ u8 empty;
+};
+
+struct ispstat_ops {
+ /*
+ * Validate new params configuration.
+ * new_conf->buf_size value must be changed to the exact buffer size
+ * necessary for the new configuration if it's smaller.
+ */
+ int (*validate_params)(struct ispstat *stat, void *new_conf);
+
+ /*
+ * Save new params configuration.
+ * stat->priv->buf_size value must be set to the exact buffer size for
+ * the new configuration.
+ * stat->update is set to 1 if new configuration is different than
+ * current one.
+ */
+ void (*set_params)(struct ispstat *stat, void *new_conf);
+
+ /* Apply stored configuration. */
+ void (*setup_regs)(struct ispstat *stat, void *priv);
+
+ /* Enable/Disable module. */
+ void (*enable)(struct ispstat *stat, int enable);
+
+ /* Verify is module is busy. */
+ int (*busy)(struct ispstat *stat);
+
+ /* Used for specific operations during generic buf process task. */
+ int (*buf_process)(struct ispstat *stat);
+};
+
+enum ispstat_state_t {
+ ISPSTAT_DISABLED = 0,
+ ISPSTAT_DISABLING,
+ ISPSTAT_ENABLED,
+ ISPSTAT_ENABLING,
+ ISPSTAT_SUSPENDED,
+};
+
+struct ispstat {
+ struct v4l2_subdev subdev;
+ struct media_pad pad; /* sink pad */
+
+ /* Control */
+ unsigned configured:1;
+ unsigned update:1;
+ unsigned buf_processing:1;
+ unsigned sbl_ovl_recover:1;
+ u8 inc_config;
+ atomic_t buf_err;
+ enum ispstat_state_t state; /* enabling/disabling state */
+ struct omap_dma_channel_params dma_config;
+ struct isp_device *isp;
+ void *priv; /* pointer to priv config struct */
+ void *recover_priv; /* pointer to recover priv configuration */
+ struct mutex ioctl_lock; /* serialize private ioctl */
+
+ const struct ispstat_ops *ops;
+
+ /* Buffer */
+ u8 wait_acc_frames;
+ u16 config_counter;
+ u32 frame_number;
+ u32 buf_size;
+ u32 buf_alloc_size;
+ int dma_ch;
+ unsigned long event_type;
+ struct ispstat_buffer *buf;
+ struct ispstat_buffer *active_buf;
+ struct ispstat_buffer *locked_buf;
+};
+
+struct ispstat_generic_config {
+ /*
+ * Fields must be in the same order as in:
+ * - omap3isp_h3a_aewb_config
+ * - omap3isp_h3a_af_config
+ * - omap3isp_hist_config
+ */
+ u32 buf_size;
+ u16 config_counter;
+};
+
+int omap3isp_stat_config(struct ispstat *stat, void *new_conf);
+int omap3isp_stat_request_statistics(struct ispstat *stat,
+ struct omap3isp_stat_data *data);
+int omap3isp_stat_init(struct ispstat *stat, const char *name,
+ const struct v4l2_subdev_ops *sd_ops);
+void omap3isp_stat_cleanup(struct ispstat *stat);
+int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub);
+int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
+ struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub);
+int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable);
+
+int omap3isp_stat_busy(struct ispstat *stat);
+int omap3isp_stat_pcr_busy(struct ispstat *stat);
+void omap3isp_stat_suspend(struct ispstat *stat);
+void omap3isp_stat_resume(struct ispstat *stat);
+int omap3isp_stat_enable(struct ispstat *stat, u8 enable);
+void omap3isp_stat_sbl_overflow(struct ispstat *stat);
+void omap3isp_stat_isr(struct ispstat *stat);
+void omap3isp_stat_isr_frame_sync(struct ispstat *stat);
+void omap3isp_stat_dma_isr(struct ispstat *stat);
+int omap3isp_stat_register_entities(struct ispstat *stat,
+ struct v4l2_device *vdev);
+void omap3isp_stat_unregister_entities(struct ispstat *stat);
+
+#endif /* OMAP3_ISP_STAT_H */
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
new file mode 100644
index 000000000000..a0b737fecf13
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -0,0 +1,1404 @@
+/*
+ * ispvideo.c
+ *
+ * TI OMAP3 ISP - Generic video node
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <asm/cacheflush.h>
+#include <linux/clk.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/scatterlist.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <plat/iommu.h>
+#include <plat/iovmm.h>
+#include <plat/omap-pm.h>
+
+#include "ispvideo.h"
+#include "isp.h"
+
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+/*
+ * NOTE: When adding new media bus codes, always remember to add
+ * corresponding in-memory formats to the table below!!!
+ */
+static struct isp_format_info formats[] = {
+ { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_GREY, 8, 1, },
+ { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y10, 10, 2, },
+ { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
+ V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
+ V4L2_PIX_FMT_Y12, 12, 2, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR8, 8, 1, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG8, 8, 1, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG8, 8, 1, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB8, 8, 1, },
+ { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SBGGR10_1X10, 0,
+ V4L2_PIX_FMT_SBGGR10DPCM8, 8, 1, },
+ { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGBRG10_1X10, 0,
+ V4L2_PIX_FMT_SGBRG10DPCM8, 8, 1, },
+ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGRBG10_1X10, 0,
+ V4L2_PIX_FMT_SGRBG10DPCM8, 8, 1, },
+ { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SRGGB10_1X10, 0,
+ V4L2_PIX_FMT_SRGGB10DPCM8, 8, 1, },
+ { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR10, 10, 2, },
+ { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG10, 10, 2, },
+ { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG10, 10, 2, },
+ { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB10, 10, 2, },
+ { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
+ V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
+ V4L2_PIX_FMT_SBGGR12, 12, 2, },
+ { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
+ V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
+ V4L2_PIX_FMT_SGBRG12, 12, 2, },
+ { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
+ V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
+ V4L2_PIX_FMT_SGRBG12, 12, 2, },
+ { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
+ V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
+ V4L2_PIX_FMT_SRGGB12, 12, 2, },
+ { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
+ V4L2_MBUS_FMT_UYVY8_1X16, 0,
+ V4L2_PIX_FMT_UYVY, 16, 2, },
+ { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
+ V4L2_MBUS_FMT_YUYV8_1X16, 0,
+ V4L2_PIX_FMT_YUYV, 16, 2, },
+ { V4L2_MBUS_FMT_UYVY8_2X8, V4L2_MBUS_FMT_UYVY8_2X8,
+ V4L2_MBUS_FMT_UYVY8_2X8, 0,
+ V4L2_PIX_FMT_UYVY, 8, 2, },
+ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_MBUS_FMT_YUYV8_2X8,
+ V4L2_MBUS_FMT_YUYV8_2X8, 0,
+ V4L2_PIX_FMT_YUYV, 8, 2, },
+ /* Empty entry to catch the unsupported pixel code (0) used by the CCDC
+ * module and avoid NULL pointer dereferences.
+ */
+ { 0, }
+};
+
+const struct isp_format_info *
+omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
+ * @video: ISP video instance
+ * @mbus: v4l2_mbus_framefmt format (input)
+ * @pix: v4l2_pix_format format (output)
+ *
+ * Fill the output pix structure with information from the input mbus format.
+ * The bytesperline and sizeimage fields are computed from the requested bytes
+ * per line value in the pix format and information from the video instance.
+ *
+ * Return the number of padding bytes at end of line.
+ */
+static unsigned int isp_video_mbus_to_pix(const struct isp_video *video,
+ const struct v4l2_mbus_framefmt *mbus,
+ struct v4l2_pix_format *pix)
+{
+ unsigned int bpl = pix->bytesperline;
+ unsigned int min_bpl;
+ unsigned int i;
+
+ memset(pix, 0, sizeof(*pix));
+ pix->width = mbus->width;
+ pix->height = mbus->height;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (formats[i].code == mbus->code)
+ break;
+ }
+
+ if (WARN_ON(i == ARRAY_SIZE(formats)))
+ return 0;
+
+ min_bpl = pix->width * formats[i].bpp;
+
+ /* Clamp the requested bytes per line value. If the maximum bytes per
+ * line value is zero, the module doesn't support user configurable line
+ * sizes. Override the requested value with the minimum in that case.
+ */
+ if (video->bpl_max)
+ bpl = clamp(bpl, min_bpl, video->bpl_max);
+ else
+ bpl = min_bpl;
+
+ if (!video->bpl_zero_padding || bpl != min_bpl)
+ bpl = ALIGN(bpl, video->bpl_alignment);
+
+ pix->pixelformat = formats[i].pixelformat;
+ pix->bytesperline = bpl;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = mbus->colorspace;
+ pix->field = mbus->field;
+
+ return bpl - min_bpl;
+}
+
+static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix,
+ struct v4l2_mbus_framefmt *mbus)
+{
+ unsigned int i;
+
+ memset(mbus, 0, sizeof(*mbus));
+ mbus->width = pix->width;
+ mbus->height = pix->height;
+
+ /* Skip the last format in the loop so that it will be selected if no
+ * match is found.
+ */
+ for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
+ if (formats[i].pixelformat == pix->pixelformat)
+ break;
+ }
+
+ mbus->code = formats[i].code;
+ mbus->colorspace = pix->colorspace;
+ mbus->field = pix->field;
+}
+
+static struct v4l2_subdev *
+isp_video_remote_subdev(struct isp_video *video, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_source(&video->pad);
+
+ if (remote == NULL ||
+ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+/* Return a pointer to the ISP video instance at the far end of the pipeline. */
+static int isp_video_get_graph_data(struct isp_video *video,
+ struct isp_pipeline *pipe)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity = &video->video.entity;
+ struct media_device *mdev = entity->parent;
+ struct isp_video *far_end = NULL;
+
+ mutex_lock(&mdev->graph_mutex);
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ struct isp_video *__video;
+
+ pipe->entities |= 1 << entity->id;
+
+ if (far_end != NULL)
+ continue;
+
+ if (entity == &video->video.entity)
+ continue;
+
+ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
+ continue;
+
+ __video = to_isp_video(media_entity_to_video_device(entity));
+ if (__video->type != video->type)
+ far_end = __video;
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pipe->input = far_end;
+ pipe->output = video;
+ } else {
+ if (far_end == NULL)
+ return -EPIPE;
+
+ pipe->input = video;
+ pipe->output = far_end;
+ }
+
+ return 0;
+}
+
+/*
+ * Validate a pipeline by checking both ends of all links for format
+ * discrepancies.
+ *
+ * Compute the minimum time per frame value as the maximum of time per frame
+ * limits reported by every block in the pipeline.
+ *
+ * Return 0 if all formats match, or -EPIPE if at least one link is found with
+ * different formats on its two ends or if the pipeline doesn't start with a
+ * video source (either a subdev with no input pad, or a non-subdev entity).
+ */
+static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = pipe->output->isp;
+ struct media_pad *pad;
+ struct v4l2_subdev *subdev;
+
+ subdev = isp_video_remote_subdev(pipe->output, NULL);
+ if (subdev == NULL)
+ return -EPIPE;
+
+ while (1) {
+ /* Retrieve the sink format */
+ pad = &subdev->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ /* Update the maximum frame rate */
+ if (subdev == &isp->isp_res.subdev)
+ omap3isp_resizer_max_rate(&isp->isp_res,
+ &pipe->max_rate);
+
+ /* Retrieve the source format. Return an error if no source
+ * entity can be found, and stop checking the pipeline if the
+ * source entity isn't a subdev.
+ */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL)
+ return -EPIPE;
+
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ subdev = media_entity_to_v4l2_subdev(pad->entity);
+ }
+
+ return 0;
+}
+
+static int
+__isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret == -ENOIOCTLCMD)
+ ret = -EINVAL;
+
+ mutex_unlock(&video->mutex);
+
+ if (ret)
+ return ret;
+
+ format->type = video->type;
+ return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+}
+
+static int
+isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
+{
+ struct v4l2_format format;
+ int ret;
+
+ memcpy(&format, &vfh->format, sizeof(format));
+ ret = __isp_video_get_format(video, &format);
+ if (ret < 0)
+ return ret;
+
+ if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
+ vfh->format.fmt.pix.height != format.fmt.pix.height ||
+ vfh->format.fmt.pix.width != format.fmt.pix.width ||
+ vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
+ vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
+ return -EINVAL;
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * IOMMU management
+ */
+
+#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
+
+/*
+ * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @sglist: Pointer to source Scatter gather list to allocate.
+ * @sglen: Number of elements of the scatter-gatter list.
+ *
+ * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
+ * we ran out of memory.
+ */
+static dma_addr_t
+ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
+{
+ struct sg_table *sgt;
+ u32 da;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (sgt == NULL)
+ return -ENOMEM;
+
+ sgt->sgl = (struct scatterlist *)sglist;
+ sgt->nents = sglen;
+ sgt->orig_nents = sglen;
+
+ da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
+ if (IS_ERR_VALUE(da))
+ kfree(sgt);
+
+ return da;
+}
+
+/*
+ * ispmmu_vunmap - Unmap a device address from the ISP MMU
+ * @dev: Device pointer specific to the OMAP3 ISP.
+ * @da: Device address generated from a ispmmu_vmap call.
+ */
+static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
+{
+ struct sg_table *sgt;
+
+ sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
+ kfree(sgt);
+}
+
+/* -----------------------------------------------------------------------------
+ * Video queue operations
+ */
+
+static void isp_video_queue_prepare(struct isp_video_queue *queue,
+ unsigned int *nbuffers, unsigned int *size)
+{
+ struct isp_video_fh *vfh =
+ container_of(queue, struct isp_video_fh, queue);
+ struct isp_video *video = vfh->video;
+
+ *size = vfh->format.fmt.pix.sizeimage;
+ if (*size == 0)
+ return;
+
+ *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
+}
+
+static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+
+ if (buffer->isp_addr) {
+ ispmmu_vunmap(video->isp, buffer->isp_addr);
+ buffer->isp_addr = 0;
+ }
+}
+
+static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+ unsigned long addr;
+
+ addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
+ if (IS_ERR_VALUE(addr))
+ return -EIO;
+
+ if (!IS_ALIGNED(addr, 32)) {
+ dev_dbg(video->isp->dev, "Buffer address must be "
+ "aligned to 32 bytes boundary.\n");
+ ispmmu_vunmap(video->isp, buffer->isp_addr);
+ return -EINVAL;
+ }
+
+ buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
+ buffer->isp_addr = addr;
+ return 0;
+}
+
+/*
+ * isp_video_buffer_queue - Add buffer to streaming queue
+ * @buf: Video buffer
+ *
+ * In memory-to-memory mode, start streaming on the pipeline if buffers are
+ * queued on both the input and the output, if the pipeline isn't already busy.
+ * If the pipeline is busy, it will be restarted in the output module interrupt
+ * handler.
+ */
+static void isp_video_buffer_queue(struct isp_video_buffer *buf)
+{
+ struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
+ struct isp_buffer *buffer = to_isp_buffer(buf);
+ struct isp_video *video = vfh->video;
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ enum isp_pipeline_state state;
+ unsigned long flags;
+ unsigned int empty;
+ unsigned int start;
+
+ empty = list_empty(&video->dmaqueue);
+ list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
+
+ if (empty) {
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISP_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state |= state;
+ video->ops->queue(video, buffer);
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
+
+ start = isp_pipeline_ready(pipe);
+ if (start)
+ pipe->state |= ISP_PIPELINE_STREAM;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ if (start)
+ omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_SINGLESHOT);
+ }
+}
+
+static const struct isp_video_queue_operations isp_video_queue_ops = {
+ .queue_prepare = &isp_video_queue_prepare,
+ .buffer_prepare = &isp_video_buffer_prepare,
+ .buffer_queue = &isp_video_buffer_queue,
+ .buffer_cleanup = &isp_video_buffer_cleanup,
+};
+
+/*
+ * omap3isp_video_buffer_next - Complete the current buffer and return the next
+ * @video: ISP video object
+ *
+ * Remove the current video buffer from the DMA queue and fill its timestamp,
+ * field count and state fields before waking up its completion handler.
+ *
+ * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
+ * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
+ * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
+ *
+ * The DMA queue is expected to contain at least one buffer.
+ *
+ * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
+ * empty.
+ */
+struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
+{
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ struct isp_video_queue *queue = video->queue;
+ enum isp_pipeline_state state;
+ struct isp_video_buffer *buf;
+ unsigned long flags;
+ struct timespec ts;
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (WARN_ON(list_empty(&video->dmaqueue))) {
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+ return NULL;
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ irqlist);
+ list_del(&buf->irqlist);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ ktime_get_ts(&ts);
+ buf->vbuf.timestamp.tv_sec = ts.tv_sec;
+ buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+ /* Do frame number propagation only if this is the output video node.
+ * Frame number either comes from the CSI receivers or it gets
+ * incremented here if H3A is not active.
+ * Note: There is no guarantee that the output buffer will finish
+ * first, so the input number might lag behind by 1 in some cases.
+ */
+ if (video == pipe->output && !pipe->do_propagation)
+ buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
+ else
+ buf->vbuf.sequence = atomic_read(&pipe->frame_number);
+
+ /* Report pipeline errors to userspace on the capture device side. */
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
+ buf->state = ISP_BUF_STATE_ERROR;
+ pipe->error = false;
+ } else {
+ buf->state = ISP_BUF_STATE_DONE;
+ }
+
+ wake_up(&buf->wait);
+
+ if (list_empty(&video->dmaqueue)) {
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_QUEUE_OUTPUT
+ | ISP_PIPELINE_STREAM;
+ else
+ state = ISP_PIPELINE_QUEUE_INPUT
+ | ISP_PIPELINE_STREAM;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS)
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+ return NULL;
+ }
+
+ if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_STREAM;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+ }
+
+ buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
+ irqlist);
+ buf->state = ISP_BUF_STATE_ACTIVE;
+ return to_isp_buffer(buf);
+}
+
+/*
+ * omap3isp_video_resume - Perform resume operation on the buffers
+ * @video: ISP video object
+ * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise
+ *
+ * This function is intended to be used on suspend/resume scenario. It
+ * requests video queue layer to discard buffers marked as DONE if it's in
+ * continuous mode and requests ISP modules to queue again the ACTIVE buffer
+ * if there's any.
+ */
+void omap3isp_video_resume(struct isp_video *video, int continuous)
+{
+ struct isp_buffer *buf = NULL;
+
+ if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ omap3isp_video_queue_discard_done(video->queue);
+
+ if (!list_empty(&video->dmaqueue)) {
+ buf = list_first_entry(&video->dmaqueue,
+ struct isp_buffer, buffer.irqlist);
+ video->ops->queue(video, buf);
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
+ } else {
+ if (continuous)
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct isp_video *video = video_drvdata(file);
+
+ strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, video->video.name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ else
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int
+isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ *format = vfh->format;
+ mutex_unlock(&video->mutex);
+
+ return 0;
+}
+
+static int
+isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_mbus_framefmt fmt;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+
+ /* Fill the bytesperline and sizeimage fields by converting to media bus
+ * format and back to pixel format.
+ */
+ isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
+ isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
+
+ vfh->format = *format;
+
+ mutex_unlock(&video->mutex);
+ return 0;
+}
+
+static int
+isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ if (format->type != video->type)
+ return -EINVAL;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
+
+ fmt.pad = pad;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret)
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+
+ isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
+ return 0;
+}
+
+static int
+isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+}
+
+static int
+isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev_format format;
+ struct v4l2_subdev *subdev;
+ u32 pad;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, &pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ /* Try the get crop operation first and fallback to get format if not
+ * implemented.
+ */
+ ret = v4l2_subdev_call(subdev, video, g_crop, crop);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ format.pad = pad;
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+
+ crop->c.left = 0;
+ crop->c.top = 0;
+ crop->c.width = format.format.width;
+ crop->c.height = format.format.height;
+
+ return 0;
+}
+
+static int
+isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = isp_video_remote_subdev(video, NULL);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&video->mutex);
+ ret = v4l2_subdev_call(subdev, video, s_crop, crop);
+ mutex_unlock(&video->mutex);
+
+ return ret == -ENOIOCTLCMD ? -ENOTTY : ret;
+}
+
+static int
+isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ a->parm.output.timeperframe = vfh->timeperframe;
+
+ return 0;
+}
+
+static int
+isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+
+ if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ video->type != a->type)
+ return -EINVAL;
+
+ if (a->parm.output.timeperframe.denominator == 0)
+ a->parm.output.timeperframe.denominator = 1;
+
+ vfh->timeperframe = a->parm.output.timeperframe;
+
+ return 0;
+}
+
+static int
+isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
+}
+
+static int
+isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_querybuf(&vfh->queue, b);
+}
+
+static int
+isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_qbuf(&vfh->queue, b);
+}
+
+static int
+isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+
+ return omap3isp_video_queue_dqbuf(&vfh->queue, b,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int isp_video_check_external_subdevs(struct isp_video *video,
+ struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = video->isp;
+ struct media_entity *ents[] = {
+ &isp->isp_csi2a.subdev.entity,
+ &isp->isp_csi2c.subdev.entity,
+ &isp->isp_ccp2.subdev.entity,
+ &isp->isp_ccdc.subdev.entity
+ };
+ struct media_pad *source_pad;
+ struct media_entity *source = NULL;
+ struct media_entity *sink;
+ struct v4l2_subdev_format fmt;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ents); i++) {
+ /* Is the entity part of the pipeline? */
+ if (!(pipe->entities & (1 << ents[i]->id)))
+ continue;
+
+ /* ISP entities have always sink pad == 0. Find source. */
+ source_pad = media_entity_remote_source(&ents[i]->pads[0]);
+ if (source_pad == NULL)
+ continue;
+
+ source = source_pad->entity;
+ sink = ents[i];
+ break;
+ }
+
+ if (!source) {
+ dev_warn(isp->dev, "can't find source, failing now\n");
+ return ret;
+ }
+
+ if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ pipe->external = media_entity_to_v4l2_subdev(source);
+
+ fmt.pad = source_pad->index;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink),
+ pad, get_fmt, NULL, &fmt);
+ if (unlikely(ret < 0)) {
+ dev_warn(isp->dev, "get_fmt returned null!\n");
+ return ret;
+ }
+
+ pipe->external_width =
+ omap3isp_video_format_info(fmt.format.code)->width;
+
+ memset(&ctrls, 0, sizeof(ctrls));
+ memset(&ctrl, 0, sizeof(ctrl));
+
+ ctrl.id = V4L2_CID_PIXEL_RATE;
+
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+
+ ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
+ if (ret < 0) {
+ dev_warn(isp->dev, "no pixel rate control in subdev %s\n",
+ pipe->external->name);
+ return ret;
+ }
+
+ pipe->external_rate = ctrl.value64;
+
+ if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) {
+ unsigned int rate = UINT_MAX;
+ /*
+ * Check that maximum allowed CCDC pixel rate isn't
+ * exceeded by the pixel rate.
+ */
+ omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate);
+ if (pipe->external_rate > rate)
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+/*
+ * Stream management
+ *
+ * Every ISP pipeline has a single input and a single output. The input can be
+ * either a sensor or a video node. The output is always a video node.
+ *
+ * As every pipeline has an output video node, the ISP video objects at the
+ * pipeline output stores the pipeline state. It tracks the streaming state of
+ * both the input and output, as well as the availability of buffers.
+ *
+ * In sensor-to-memory mode, frames are always available at the pipeline input.
+ * Starting the sensor usually requires I2C transfers and must be done in
+ * interruptible context. The pipeline is started and stopped synchronously
+ * to the stream on/off commands. All modules in the pipeline will get their
+ * subdev set stream handler called. The module at the end of the pipeline must
+ * delay starting the hardware until buffers are available at its output.
+ *
+ * In memory-to-memory mode, starting/stopping the stream requires
+ * synchronization between the input and output. ISP modules can't be stopped
+ * in the middle of a frame, and at least some of the modules seem to become
+ * busy as soon as they're started, even if they don't receive a frame start
+ * event. For that reason frames need to be processed in single-shot mode. The
+ * driver needs to wait until a frame is completely processed and written to
+ * memory before restarting the pipeline for the next frame. Pipelined
+ * processing might be possible but requires more testing.
+ *
+ * Stream start must be delayed until buffers are available at both the input
+ * and output. The pipeline must be started in the videobuf queue callback with
+ * the buffers queue spinlock held. The modules subdev set stream operation must
+ * not sleep.
+ */
+static int
+isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ enum isp_pipeline_state state;
+ struct isp_pipeline *pipe;
+ unsigned long flags;
+ int ret;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ if (video->streaming) {
+ mutex_unlock(&video->stream_lock);
+ return -EBUSY;
+ }
+
+ /* Start streaming on the pipeline. No link touching an entity in the
+ * pipeline can be activated or deactivated once streaming is started.
+ */
+ pipe = video->video.entity.pipe
+ ? to_isp_pipeline(&video->video.entity) : &video->pipe;
+
+ pipe->entities = 0;
+
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, true);
+ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
+ pipe->max_rate = pipe->l3_ick;
+
+ ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ if (ret < 0)
+ goto err_pipeline_start;
+
+ /* Verify that the currently configured format matches the output of
+ * the connected subdev.
+ */
+ ret = isp_video_check_format(video, vfh);
+ if (ret < 0)
+ goto err_check_format;
+
+ video->bpl_padding = ret;
+ video->bpl_value = vfh->format.fmt.pix.bytesperline;
+
+ ret = isp_video_get_graph_data(video, pipe);
+ if (ret < 0)
+ goto err_check_format;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT;
+ else
+ state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT;
+
+ ret = isp_video_check_external_subdevs(video, pipe);
+ if (ret < 0)
+ goto err_check_format;
+
+ /* Validate the pipeline and update its state. */
+ ret = isp_video_validate_pipeline(pipe);
+ if (ret < 0)
+ goto err_check_format;
+
+ pipe->error = false;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~ISP_PIPELINE_STREAM;
+ pipe->state |= state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Set the maximum time per frame as the value requested by userspace.
+ * This is a soft limit that can be overridden if the hardware doesn't
+ * support the request limit.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ pipe->max_timeperframe = vfh->timeperframe;
+
+ video->queue = &vfh->queue;
+ INIT_LIST_HEAD(&video->dmaqueue);
+ atomic_set(&pipe->frame_number, -1);
+
+ ret = omap3isp_video_queue_streamon(&vfh->queue);
+ if (ret < 0)
+ goto err_check_format;
+
+ /* In sensor-to-memory mode, the stream can be started synchronously
+ * to the stream on command. In memory-to-memory mode, it will be
+ * started when buffers are queued on both the input and output.
+ */
+ if (pipe->input == NULL) {
+ ret = omap3isp_pipeline_set_stream(pipe,
+ ISP_PIPELINE_STREAM_CONTINUOUS);
+ if (ret < 0)
+ goto err_set_stream;
+ spin_lock_irqsave(&video->queue->irqlock, flags);
+ if (list_empty(&video->dmaqueue))
+ video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
+ spin_unlock_irqrestore(&video->queue->irqlock, flags);
+ }
+
+ video->streaming = 1;
+
+ mutex_unlock(&video->stream_lock);
+ return 0;
+
+err_set_stream:
+ omap3isp_video_queue_streamoff(&vfh->queue);
+err_check_format:
+ media_entity_pipeline_stop(&video->video.entity);
+err_pipeline_start:
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, false);
+ /* The DMA queue must be emptied here, otherwise CCDC interrupts that
+ * will get triggered the next time the CCDC is powered up will try to
+ * access buffers that might have been freed but still present in the
+ * DMA queue. This can easily get triggered if the above
+ * omap3isp_pipeline_set_stream() call fails on a system with a
+ * free-running sensor.
+ */
+ INIT_LIST_HEAD(&video->dmaqueue);
+ video->queue = NULL;
+
+ mutex_unlock(&video->stream_lock);
+ return ret;
+}
+
+static int
+isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(fh);
+ struct isp_video *video = video_drvdata(file);
+ struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
+ enum isp_pipeline_state state;
+ unsigned int streaming;
+ unsigned long flags;
+
+ if (type != video->type)
+ return -EINVAL;
+
+ mutex_lock(&video->stream_lock);
+
+ /* Make sure we're not streaming yet. */
+ mutex_lock(&vfh->queue.lock);
+ streaming = vfh->queue.streaming;
+ mutex_unlock(&vfh->queue.lock);
+
+ if (!streaming)
+ goto done;
+
+ /* Update the pipeline state. */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ state = ISP_PIPELINE_STREAM_OUTPUT
+ | ISP_PIPELINE_QUEUE_OUTPUT;
+ else
+ state = ISP_PIPELINE_STREAM_INPUT
+ | ISP_PIPELINE_QUEUE_INPUT;
+
+ spin_lock_irqsave(&pipe->lock, flags);
+ pipe->state &= ~state;
+ spin_unlock_irqrestore(&pipe->lock, flags);
+
+ /* Stop the stream. */
+ omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
+ omap3isp_video_queue_streamoff(&vfh->queue);
+ video->queue = NULL;
+ video->streaming = 0;
+
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, false);
+ media_entity_pipeline_stop(&video->video.entity);
+
+done:
+ mutex_unlock(&video->stream_lock);
+ return 0;
+}
+
+static int
+isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ strlcpy(input->name, "camera", sizeof(input->name));
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int
+isp_video_g_input(struct file *file, void *fh, unsigned int *input)
+{
+ *input = 0;
+
+ return 0;
+}
+
+static int
+isp_video_s_input(struct file *file, void *fh, unsigned int input)
+{
+ return input == 0 ? 0 : -EINVAL;
+}
+
+static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
+ .vidioc_querycap = isp_video_querycap,
+ .vidioc_g_fmt_vid_cap = isp_video_get_format,
+ .vidioc_s_fmt_vid_cap = isp_video_set_format,
+ .vidioc_try_fmt_vid_cap = isp_video_try_format,
+ .vidioc_g_fmt_vid_out = isp_video_get_format,
+ .vidioc_s_fmt_vid_out = isp_video_set_format,
+ .vidioc_try_fmt_vid_out = isp_video_try_format,
+ .vidioc_cropcap = isp_video_cropcap,
+ .vidioc_g_crop = isp_video_get_crop,
+ .vidioc_s_crop = isp_video_set_crop,
+ .vidioc_g_parm = isp_video_get_param,
+ .vidioc_s_parm = isp_video_set_param,
+ .vidioc_reqbufs = isp_video_reqbufs,
+ .vidioc_querybuf = isp_video_querybuf,
+ .vidioc_qbuf = isp_video_qbuf,
+ .vidioc_dqbuf = isp_video_dqbuf,
+ .vidioc_streamon = isp_video_streamon,
+ .vidioc_streamoff = isp_video_streamoff,
+ .vidioc_enum_input = isp_video_enum_input,
+ .vidioc_g_input = isp_video_g_input,
+ .vidioc_s_input = isp_video_s_input,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int isp_video_open(struct file *file)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct isp_video_fh *handle;
+ int ret = 0;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (handle == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(&handle->vfh, &video->video);
+ v4l2_fh_add(&handle->vfh);
+
+ /* If this is the first user, initialise the pipeline. */
+ if (omap3isp_get(video->isp) == NULL) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ret = omap3isp_pipeline_pm_use(&video->video.entity, 1);
+ if (ret < 0) {
+ omap3isp_put(video->isp);
+ goto done;
+ }
+
+ omap3isp_video_queue_init(&handle->queue, video->type,
+ &isp_video_queue_ops, video->isp->dev,
+ sizeof(struct isp_buffer));
+
+ memset(&handle->format, 0, sizeof(handle->format));
+ handle->format.type = video->type;
+ handle->timeperframe.denominator = 1;
+
+ handle->video = video;
+ file->private_data = &handle->vfh;
+
+done:
+ if (ret < 0) {
+ v4l2_fh_del(&handle->vfh);
+ kfree(handle);
+ }
+
+ return ret;
+}
+
+static int isp_video_release(struct file *file)
+{
+ struct isp_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+ struct isp_video_fh *handle = to_isp_video_fh(vfh);
+
+ /* Disable streaming and free the buffers queue resources. */
+ isp_video_streamoff(file, vfh, video->type);
+
+ mutex_lock(&handle->queue.lock);
+ omap3isp_video_queue_cleanup(&handle->queue);
+ mutex_unlock(&handle->queue.lock);
+
+ omap3isp_pipeline_pm_use(&video->video.entity, 0);
+
+ /* Release the file handle. */
+ v4l2_fh_del(vfh);
+ kfree(handle);
+ file->private_data = NULL;
+
+ omap3isp_put(video->isp);
+
+ return 0;
+}
+
+static unsigned int isp_video_poll(struct file *file, poll_table *wait)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+ struct isp_video_queue *queue = &vfh->queue;
+
+ return omap3isp_video_queue_poll(queue, file, wait);
+}
+
+static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
+
+ return omap3isp_video_queue_mmap(&vfh->queue, vma);
+}
+
+static struct v4l2_file_operations isp_video_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = isp_video_open,
+ .release = isp_video_release,
+ .poll = isp_video_poll,
+ .mmap = isp_video_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * ISP video core
+ */
+
+static const struct isp_video_operations isp_video_dummy_ops = {
+};
+
+int omap3isp_video_init(struct isp_video *video, const char *name)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->video.vfl_dir = VFL_DIR_TX;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&video->mutex);
+ atomic_set(&video->active, 0);
+
+ spin_lock_init(&video->pipe.lock);
+ mutex_init(&video->stream_lock);
+
+ /* Initialize the video device. */
+ if (video->ops == NULL)
+ video->ops = &isp_video_dummy_ops;
+
+ video->video.fops = &isp_video_fops;
+ snprintf(video->video.name, sizeof(video->video.name),
+ "OMAP3 ISP %s %s", name, direction);
+ video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.release = video_device_release_empty;
+ video->video.ioctl_ops = &isp_video_ioctl_ops;
+ video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;
+
+ video_set_drvdata(&video->video, video);
+
+ return 0;
+}
+
+void omap3isp_video_cleanup(struct isp_video *video)
+{
+ media_entity_cleanup(&video->video.entity);
+ mutex_destroy(&video->stream_lock);
+ mutex_destroy(&video->mutex);
+}
+
+int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
+{
+ int ret;
+
+ video->video.v4l2_dev = vdev;
+
+ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ printk(KERN_ERR "%s: could not register video device (%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+void omap3isp_video_unregister(struct isp_video *video)
+{
+ if (video_is_registered(&video->video))
+ video_unregister_device(&video->video);
+}
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/omap3isp/ispvideo.h
new file mode 100644
index 000000000000..1ad470ec2b9d
--- /dev/null
+++ b/drivers/media/platform/omap3isp/ispvideo.h
@@ -0,0 +1,216 @@
+/*
+ * ispvideo.h
+ *
+ * TI OMAP3 ISP - Generic video node
+ *
+ * Copyright (C) 2009-2010 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP3_ISP_VIDEO_H
+#define OMAP3_ISP_VIDEO_H
+
+#include <linux/v4l2-mediabus.h>
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+
+#include "ispqueue.h"
+
+#define ISP_VIDEO_DRIVER_NAME "ispvideo"
+#define ISP_VIDEO_DRIVER_VERSION "0.0.2"
+
+struct isp_device;
+struct isp_video;
+struct v4l2_mbus_framefmt;
+struct v4l2_pix_format;
+
+/*
+ * struct isp_format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @truncated: V4L2 media bus format code for the same format truncated to 10
+ * bits. Identical to @code if the format is 10 bits wide or less.
+ * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
+ * format. Identical to @code if the format is not DPCM compressed.
+ * @flavor: V4L2 media bus format code for the same pixel layout but
+ * shifted to be 8 bits per pixel. =0 if format is not shiftable.
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @width: Bits per pixel (when transferred over a bus)
+ * @bpp: Bytes per pixel (when stored in memory)
+ */
+struct isp_format_info {
+ enum v4l2_mbus_pixelcode code;
+ enum v4l2_mbus_pixelcode truncated;
+ enum v4l2_mbus_pixelcode uncompressed;
+ enum v4l2_mbus_pixelcode flavor;
+ u32 pixelformat;
+ unsigned int width;
+ unsigned int bpp;
+};
+
+enum isp_pipeline_stream_state {
+ ISP_PIPELINE_STREAM_STOPPED = 0,
+ ISP_PIPELINE_STREAM_CONTINUOUS = 1,
+ ISP_PIPELINE_STREAM_SINGLESHOT = 2,
+};
+
+enum isp_pipeline_state {
+ /* The stream has been started on the input video node. */
+ ISP_PIPELINE_STREAM_INPUT = 1,
+ /* The stream has been started on the output video node. */
+ ISP_PIPELINE_STREAM_OUTPUT = 2,
+ /* At least one buffer is queued on the input video node. */
+ ISP_PIPELINE_QUEUE_INPUT = 4,
+ /* At least one buffer is queued on the output video node. */
+ ISP_PIPELINE_QUEUE_OUTPUT = 8,
+ /* The input entity is idle, ready to be started. */
+ ISP_PIPELINE_IDLE_INPUT = 16,
+ /* The output entity is idle, ready to be started. */
+ ISP_PIPELINE_IDLE_OUTPUT = 32,
+ /* The pipeline is currently streaming. */
+ ISP_PIPELINE_STREAM = 64,
+};
+
+/*
+ * struct isp_pipeline - An ISP hardware pipeline
+ * @error: A hardware error occurred during capture
+ * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
+ */
+struct isp_pipeline {
+ struct media_pipeline pipe;
+ spinlock_t lock; /* Pipeline state and queue flags */
+ unsigned int state;
+ enum isp_pipeline_stream_state stream_state;
+ struct isp_video *input;
+ struct isp_video *output;
+ u32 entities;
+ unsigned long l3_ick;
+ unsigned int max_rate;
+ atomic_t frame_number;
+ bool do_propagation; /* of frame number */
+ bool error;
+ struct v4l2_fract max_timeperframe;
+ struct v4l2_subdev *external;
+ unsigned int external_rate;
+ unsigned int external_width;
+};
+
+#define to_isp_pipeline(__e) \
+ container_of((__e)->pipe, struct isp_pipeline, pipe)
+
+static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
+{
+ return pipe->state == (ISP_PIPELINE_STREAM_INPUT |
+ ISP_PIPELINE_STREAM_OUTPUT |
+ ISP_PIPELINE_QUEUE_INPUT |
+ ISP_PIPELINE_QUEUE_OUTPUT |
+ ISP_PIPELINE_IDLE_INPUT |
+ ISP_PIPELINE_IDLE_OUTPUT);
+}
+
+/*
+ * struct isp_buffer - ISP buffer
+ * @buffer: ISP video buffer
+ * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
+ */
+struct isp_buffer {
+ struct isp_video_buffer buffer;
+ dma_addr_t isp_addr;
+};
+
+#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer)
+
+enum isp_video_dmaqueue_flags {
+ /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
+ ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
+ /* Set when queuing buffer to an empty DMA queue */
+ ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
+};
+
+#define isp_video_dmaqueue_flags_clr(video) \
+ ({ (video)->dmaqueue_flags = 0; })
+
+/*
+ * struct isp_video_operations - ISP video operations
+ * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
+ * if there was no buffer previously queued.
+ */
+struct isp_video_operations {
+ int(*queue)(struct isp_video *video, struct isp_buffer *buffer);
+};
+
+struct isp_video {
+ struct video_device video;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+
+ struct mutex mutex; /* format and crop settings */
+ atomic_t active;
+
+ struct isp_device *isp;
+
+ unsigned int capture_mem;
+ unsigned int bpl_alignment; /* alignment value */
+ unsigned int bpl_zero_padding; /* whether the alignment is optional */
+ unsigned int bpl_max; /* maximum bytes per line value */
+ unsigned int bpl_value; /* bytes per line value */
+ unsigned int bpl_padding; /* padding at end of line */
+
+ /* Entity video node streaming */
+ unsigned int streaming:1;
+
+ /* Pipeline state */
+ struct isp_pipeline pipe;
+ struct mutex stream_lock; /* pipeline and stream states */
+
+ /* Video buffers queue */
+ struct isp_video_queue *queue;
+ struct list_head dmaqueue;
+ enum isp_video_dmaqueue_flags dmaqueue_flags;
+
+ const struct isp_video_operations *ops;
+};
+
+#define to_isp_video(vdev) container_of(vdev, struct isp_video, video)
+
+struct isp_video_fh {
+ struct v4l2_fh vfh;
+ struct isp_video *video;
+ struct isp_video_queue queue;
+ struct v4l2_format format;
+ struct v4l2_fract timeperframe;
+};
+
+#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh)
+#define isp_video_queue_to_isp_video_fh(q) \
+ container_of(q, struct isp_video_fh, queue)
+
+int omap3isp_video_init(struct isp_video *video, const char *name);
+void omap3isp_video_cleanup(struct isp_video *video);
+int omap3isp_video_register(struct isp_video *video,
+ struct v4l2_device *vdev);
+void omap3isp_video_unregister(struct isp_video *video);
+struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video);
+void omap3isp_video_resume(struct isp_video *video, int continuous);
+struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);
+
+const struct isp_format_info *
+omap3isp_video_format_info(enum v4l2_mbus_pixelcode code);
+
+#endif /* OMAP3_ISP_VIDEO_H */
diff --git a/drivers/media/video/omap3isp/luma_enhance_table.h b/drivers/media/platform/omap3isp/luma_enhance_table.h
index 098b45e2280f..098b45e2280f 100644
--- a/drivers/media/video/omap3isp/luma_enhance_table.h
+++ b/drivers/media/platform/omap3isp/luma_enhance_table.h
diff --git a/drivers/media/video/omap3isp/noise_filter_table.h b/drivers/media/platform/omap3isp/noise_filter_table.h
index d50451a4a242..d50451a4a242 100644
--- a/drivers/media/video/omap3isp/noise_filter_table.h
+++ b/drivers/media/platform/omap3isp/noise_filter_table.h
diff --git a/drivers/media/video/s5p-fimc/Kconfig b/drivers/media/platform/s5p-fimc/Kconfig
index 8f090a8f270e..8f090a8f270e 100644
--- a/drivers/media/video/s5p-fimc/Kconfig
+++ b/drivers/media/platform/s5p-fimc/Kconfig
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/platform/s5p-fimc/Makefile
index 46485143e1ca..46485143e1ca 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/platform/s5p-fimc/Makefile
diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c
new file mode 100644
index 000000000000..dded98815220
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-capture.c
@@ -0,0 +1,1734 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver
+ *
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-mdevice.h"
+#include "fimc-core.h"
+#include "fimc-reg.h"
+
+static int fimc_capture_hw_init(struct fimc_dev *fimc)
+{
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_pipeline *p = &fimc->pipeline;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+ int ret = 0;
+
+ if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL)
+ return -ENXIO;
+ if (ctx->s_frame.fmt == NULL)
+ return -EINVAL;
+
+ sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]);
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_set_yuv_order(ctx);
+
+ fimc_hw_set_camera_polarity(fimc, &sensor->pdata);
+ fimc_hw_set_camera_type(fimc, &sensor->pdata);
+ fimc_hw_set_camera_source(fimc, &sensor->pdata);
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
+ ret = fimc_set_scaler_info(ctx);
+ if (!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);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+/*
+ * Reinitialize the driver so it is ready to start the streaming again.
+ * Set fimc->state to indicate stream off and the hardware shut down state.
+ * If not suspending (@suspend is false), return any buffers to videobuf2.
+ * Otherwise put any owned buffers onto the pending buffers queue, so they
+ * can be re-spun when the device is being resumed. Also perform FIMC
+ * software reset and disable streaming on the whole pipeline if required.
+ */
+static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
+{
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ unsigned long flags;
+ bool streaming;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
+
+ fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
+ 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
+ if (suspend)
+ fimc->state |= (1 << ST_CAPT_SUSPENDED);
+ else
+ fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
+
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&cap->pending_buf_q)) {
+ buf = fimc_pending_queue_pop(cap);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&cap->active_buf_q)) {
+ buf = fimc_active_queue_pop(cap);
+ if (suspend)
+ fimc_pending_queue_add(cap, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ fimc_hw_reset(fimc);
+ cap->buf_index = 0;
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (streaming)
+ return fimc_pipeline_call(fimc, set_stream,
+ &fimc->pipeline, 0);
+ else
+ return 0;
+}
+
+static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
+{
+ unsigned long flags;
+
+ if (!fimc_capture_active(fimc))
+ return 0;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_CAPT_SHUT, &fimc->state);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_CAPT_SHUT, &fimc->state),
+ (2*HZ/10)); /* 200 ms */
+
+ return fimc_capture_state_cleanup(fimc, suspend);
+}
+
+/**
+ * fimc_capture_config_update - apply the camera interface configuration
+ *
+ * To be called from within the interrupt handler with fimc.slock
+ * spinlock held. It updates the camera pixel crop, rotation and
+ * image flip in H/W.
+ */
+static int fimc_capture_config_update(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
+ ret = fimc_set_scaler_info(ctx);
+ if (ret)
+ return ret;
+
+ 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_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ return ret;
+}
+
+void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
+{
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *v_buf;
+ struct timeval *tv;
+ struct timespec ts;
+
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ goto done;
+ }
+
+ if (!list_empty(&cap->active_buf_q) &&
+ test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) {
+ ktime_get_real_ts(&ts);
+
+ v_buf = fimc_active_queue_pop(cap);
+
+ tv = &v_buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
+
+ vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (!list_empty(&cap->pending_buf_q)) {
+
+ v_buf = fimc_pending_queue_pop(cap);
+ fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
+ v_buf->index = cap->buf_index;
+
+ /* Move the buffer to the capture active queue */
+ fimc_active_queue_add(cap, v_buf);
+
+ dbg("next frame: %d, done frame: %d",
+ fimc_hw_get_frame_index(fimc), v_buf->index);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ }
+
+ if (cap->active_buf_cnt == 0) {
+ if (deq_buf)
+ clear_bit(ST_CAPT_RUN, &fimc->state);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ } else {
+ set_bit(ST_CAPT_RUN, &fimc->state);
+ }
+
+ if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ fimc_capture_config_update(cap->ctx);
+done:
+ if (cap->active_buf_cnt == 1) {
+ fimc_deactivate_capture(fimc);
+ clear_bit(ST_CAPT_STREAM, &fimc->state);
+ }
+
+ dbg("frame: %d, active_buf_cnt: %d",
+ fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
+}
+
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int min_bufs;
+ int ret;
+
+ vid_cap->frame_count = 0;
+
+ ret = fimc_capture_hw_init(fimc);
+ if (ret) {
+ fimc_capture_state_cleanup(fimc, false);
+ return ret;
+ }
+
+ set_bit(ST_CAPT_PEND, &fimc->state);
+
+ min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
+
+ if (vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
+
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_call(fimc, set_stream,
+ &fimc->pipeline, 1);
+ }
+
+ 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_capture_active(fimc))
+ return -EINVAL;
+
+ return fimc_stop_capture(fimc, false);
+}
+
+int fimc_capture_suspend(struct fimc_dev *fimc)
+{
+ bool suspend = fimc_capture_busy(fimc);
+
+ int ret = fimc_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+ return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+}
+
+static void buffer_queue(struct vb2_buffer *vb);
+
+int fimc_capture_resume(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *buf;
+ int i;
+
+ if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
+ vid_cap->buf_index = 0;
+ fimc_pipeline_call(fimc, open, &fimc->pipeline,
+ &vid_cap->vfd.entity, false);
+ fimc_capture_hw_init(fimc);
+
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < vid_cap->reqbufs_count; i++) {
+ if (list_empty(&vid_cap->pending_buf_q))
+ break;
+ buf = fimc_pending_queue_pop(vid_cap);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+
+}
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ struct fimc_ctx *ctx = vq->drv_priv;
+ struct fimc_frame *frame = &ctx->d_frame;
+ struct fimc_fmt *fmt = frame->fmt;
+ unsigned long wh;
+ int i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_find_format(&pixm->pixelformat, NULL,
+ FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ wh = frame->f_width * frame->f_height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = max_t(u32, size, frame->payload[i]);
+
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_ctx *ctx = vq->drv_priv;
+ int i;
+
+ if (ctx->d_frame.fmt == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
+ unsigned long size = ctx->d_frame.payload[i];
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&ctx->fimc_dev->vid_cap.vfd,
+ "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_vid_buffer *buf
+ = container_of(vb, struct fimc_vid_buffer, vb);
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ unsigned long flags;
+ int min_bufs;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
+
+ if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_CAPT_STREAM, &fimc->state) &&
+ vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
+ /* Setup the buffer directly for processing. */
+ int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
+ vid_cap->buf_index;
+
+ fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
+ buf->index = vid_cap->buf_index;
+ fimc_active_queue_add(vid_cap, buf);
+
+ if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ vid_cap->buf_index = 0;
+ } else {
+ fimc_pending_queue_add(vid_cap, buf);
+ }
+
+ min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
+
+
+ if (vb2_is_streaming(&vid_cap->vbq) &&
+ vid_cap->active_buf_cnt >= min_bufs &&
+ !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
+ fimc_activate_capture(ctx);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
+ fimc_pipeline_call(fimc, set_stream,
+ &fimc->pipeline, 1);
+ return;
+ }
+ 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,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/**
+ * fimc_capture_ctrls_create - initialize the control handler
+ * Initialize the capture video node control handler and fill it
+ * with the FIMC controls. Inherit any sensor's controls if the
+ * 'user_subdev_api' flag is false (default behaviour).
+ * This function need to be called with the graph mutex held.
+ */
+int fimc_capture_ctrls_create(struct fimc_dev *fimc)
+{
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ int ret;
+
+ if (WARN_ON(vid_cap->ctx == NULL))
+ return -ENXIO;
+ if (vid_cap->ctx->ctrls.ready)
+ return 0;
+
+ ret = fimc_ctrls_create(vid_cap->ctx);
+ if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready)
+ return ret;
+
+ return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler,
+ fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler, NULL);
+}
+
+static int fimc_capture_set_default_format(struct fimc_dev *fimc);
+
+static int fimc_capture_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = -EBUSY;
+
+ dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ if (fimc_m2m_active(fimc))
+ goto unlock;
+
+ set_bit(ST_CAPT_BUSY, &fimc->state);
+ ret = pm_runtime_get_sync(&fimc->pdev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ ret = v4l2_fh_open(file);
+ if (ret) {
+ pm_runtime_put(&fimc->pdev->dev);
+ goto unlock;
+ }
+
+ if (++fimc->vid_cap.refcnt == 1) {
+ ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
+ &fimc->vid_cap.vfd.entity, true);
+
+ if (!ret && !fimc->vid_cap.user_subdev_api)
+ ret = fimc_capture_set_default_format(fimc);
+
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+
+ if (ret < 0) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ fimc->vid_cap.refcnt--;
+ v4l2_fh_release(file);
+ }
+ }
+unlock:
+ mutex_unlock(&fimc->lock);
+ return ret;
+}
+
+static int fimc_capture_close(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
+
+ dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ if (--fimc->vid_cap.refcnt == 0) {
+ clear_bit(ST_CAPT_BUSY, &fimc->state);
+ fimc_stop_capture(fimc, false);
+ fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
+ }
+
+ pm_runtime_put(&fimc->pdev->dev);
+
+ if (fimc->vid_cap.refcnt == 0) {
+ vb2_queue_release(&fimc->vid_cap.vbq);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ }
+
+ ret = v4l2_fh_release(file);
+
+ mutex_unlock(&fimc->lock);
+ return ret;
+}
+
+static unsigned int fimc_capture_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return POLL_ERR;
+
+ ret = vb2_poll(&fimc->vid_cap.vbq, file, wait);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = vb2_mmap(&fimc->vid_cap.vbq, vma);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations fimc_capture_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_capture_open,
+ .release = fimc_capture_close,
+ .poll = fimc_capture_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_capture_mmap,
+};
+
+/*
+ * Format and crop negotiation helpers
+ */
+
+static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *dst = &ctx->d_frame;
+ u32 depth, min_w, max_w, min_h, align_h = 3;
+ u32 mask = FMT_FLAGS_CAM;
+ struct fimc_fmt *ffmt;
+
+ /* Color conversion from/to JPEG is not supported */
+ if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
+ fimc_fmt_is_jpeg(ctx->s_frame.fmt->color))
+ *code = V4L2_MBUS_FMT_JPEG_1X8;
+
+ if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
+ mask |= FMT_FLAGS_M2M;
+
+ ffmt = fimc_find_format(fourcc, code, mask, 0);
+ if (WARN_ON(!ffmt))
+ return NULL;
+ if (code)
+ *code = ffmt->mbus_code;
+ if (fourcc)
+ *fourcc = ffmt->fourcc;
+
+ if (pad == FIMC_SD_PAD_SINK) {
+ max_w = fimc_fmt_is_jpeg(ffmt->color) ?
+ pl->scaler_dis_w : pl->scaler_en_w;
+ /* Apply the camera input interface pixel constraints */
+ v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
+ height, max_t(u32, *height, 32),
+ FIMC_CAMIF_MAX_HEIGHT,
+ fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1,
+ 0);
+ return ffmt;
+ }
+ /* Can't scale or crop in transparent (JPEG) transfer mode */
+ if (fimc_fmt_is_jpeg(ffmt->color)) {
+ *width = ctx->s_frame.f_width;
+ *height = ctx->s_frame.f_height;
+ return ffmt;
+ }
+ /* Apply the scaler and the output DMA constraints */
+ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
+ if (ctx->state & FIMC_COMPOSE) {
+ min_w = dst->offs_h + dst->width;
+ min_h = dst->offs_v + dst->height;
+ } else {
+ min_w = var->min_out_pixsize;
+ min_h = var->min_out_pixsize;
+ }
+ if (var->min_vsize_align == 1 && !rotation)
+ align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
+
+ depth = fimc_get_format_depth(ffmt);
+ v4l_bound_align_image(width, min_w, max_w,
+ ffs(var->min_out_pixsize) - 1,
+ height, min_h, FIMC_CAMIF_MAX_HEIGHT,
+ align_h,
+ 64/(ALIGN(depth, 8)));
+
+ dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
+ pad, code ? *code : 0, *width, *height,
+ dst->f_width, dst->f_height);
+
+ return ffmt;
+}
+
+static void fimc_capture_try_selection(struct fimc_ctx *ctx,
+ struct v4l2_rect *r,
+ int target)
+{
+ bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_variant *var = fimc->variant;
+ struct fimc_pix_limit *pl = var->pix_limit;
+ struct fimc_frame *sink = &ctx->s_frame;
+ u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
+ u32 align_sz = 0, align_h = 4;
+ u32 max_sc_h, max_sc_v;
+
+ /* In JPEG transparent transfer mode cropping is not supported */
+ if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) {
+ r->width = sink->f_width;
+ r->height = sink->f_height;
+ r->left = r->top = 0;
+ return;
+ }
+ if (target == V4L2_SEL_TGT_COMPOSE) {
+ if (ctx->rotation != 90 && ctx->rotation != 270)
+ align_h = 1;
+ max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
+ max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
+ min_sz = var->min_out_pixsize;
+ } else {
+ u32 depth = fimc_get_format_depth(sink->fmt);
+ align_sz = 64/ALIGN(depth, 8);
+ min_sz = var->min_inp_pixsize;
+ min_w = min_h = min_sz;
+ max_sc_h = max_sc_v = 1;
+ }
+ /*
+ * For the compose rectangle the following constraints must be met:
+ * - it must fit in the sink pad format rectangle (f_width/f_height);
+ * - maximum downscaling ratio is 64;
+ * - maximum crop size depends if the rotator is used or not;
+ * - the sink pad format width/height must be 4 multiple of the
+ * prescaler ratios determined by sink pad size and source pad crop,
+ * the prescaler ratio is returned by fimc_get_scaler_factor().
+ */
+ max_w = min_t(u32,
+ rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
+ rotate ? sink->f_height : sink->f_width);
+ max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
+
+ if (target == V4L2_SEL_TGT_COMPOSE) {
+ min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
+ min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
+ if (rotate) {
+ swap(max_sc_h, max_sc_v);
+ swap(min_w, min_h);
+ }
+ }
+ v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
+ &r->height, min_h, max_h, align_h,
+ align_sz);
+ /* Adjust left/top if crop/compose rectangle is out of bounds */
+ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
+ r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
+ r->left = round_down(r->left, var->hor_offs_align);
+
+ dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ target, r->left, r->top, r->width, r->height,
+ sink->f_width, sink->f_height);
+}
+
+/*
+ * The video node ioctl operations
+ */
+static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
+
+ return 0;
+}
+
+static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
+ f->flags |= V4L2_FMT_FLAG_COMPRESSED;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
+ * elements
+ * @ctx: FIMC capture context
+ * @tfmt: media bus format to try/set on subdevs
+ * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
+ * @set: true to set format on subdevs, false to try only
+ */
+static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
+ struct v4l2_mbus_framefmt *tfmt,
+ struct fimc_fmt **fmt_id,
+ bool set)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
+ struct v4l2_subdev_format sfmt;
+ struct v4l2_mbus_framefmt *mf = &sfmt.format;
+ struct fimc_fmt *ffmt = NULL;
+ int ret, i = 0;
+
+ if (WARN_ON(!sd || !tfmt))
+ return -EINVAL;
+
+ memset(&sfmt, 0, sizeof(sfmt));
+ sfmt.format = *tfmt;
+
+ sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
+ while (1) {
+ ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
+ FMT_FLAGS_CAM, i++);
+ if (ffmt == NULL) {
+ /*
+ * Notify user-space if common pixel code for
+ * host and sensor does not exist.
+ */
+ return -EINVAL;
+ }
+ mf->code = tfmt->code = ffmt->mbus_code;
+
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ if (ret)
+ return ret;
+ if (mf->code != tfmt->code) {
+ mf->code = 0;
+ continue;
+ }
+ if (mf->width != tfmt->width || mf->height != tfmt->height) {
+ u32 fcc = ffmt->fourcc;
+ tfmt->width = mf->width;
+ tfmt->height = mf->height;
+ ffmt = fimc_capture_try_format(ctx,
+ &tfmt->width, &tfmt->height,
+ NULL, &fcc, FIMC_SD_PAD_SOURCE);
+ if (ffmt && ffmt->mbus_code)
+ mf->code = ffmt->mbus_code;
+ if (mf->width != tfmt->width ||
+ mf->height != tfmt->height)
+ continue;
+ tfmt->code = mf->code;
+ }
+ if (csis)
+ ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
+
+ if (mf->code == tfmt->code &&
+ mf->width == tfmt->width && mf->height == tfmt->height)
+ break;
+ }
+
+ if (fmt_id && ffmt)
+ *fmt_id = ffmt;
+ *tfmt = *mf;
+
+ dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
+ return 0;
+}
+
+static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+
+ return fimc_fill_format(&ctx->d_frame, f);
+}
+
+static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt mf;
+ struct fimc_fmt *ffmt = NULL;
+
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ffmt)
+ return -EINVAL;
+
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.code = ffmt->mbus_code;
+ fimc_md_graph_lock(fimc);
+ fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
+ fimc_md_graph_unlock(fimc);
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ if (ffmt)
+ pix->pixelformat = ffmt->fourcc;
+ }
+
+ fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
+ return 0;
+}
+
+static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
+{
+ ctx->scaler.enabled = !jpeg;
+ fimc_ctrls_activate(ctx, !jpeg);
+
+ if (jpeg)
+ set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+ else
+ clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
+}
+
+static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
+ struct fimc_frame *ff = &ctx->d_frame;
+ struct fimc_fmt *s_fmt = NULL;
+ int ret, i;
+
+ if (vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ /* Pre-configure format at camera interface input, for JPEG only */
+ if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
+ fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SINK);
+ ctx->s_frame.f_width = pix->width;
+ ctx->s_frame.f_height = pix->height;
+ }
+ /* Try the format at the scaler and the DMA output */
+ ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
+ NULL, &pix->pixelformat,
+ FIMC_SD_PAD_SOURCE);
+ if (!ff->fmt)
+ return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ /* Try to match format at the host and the sensor */
+ if (!fimc->vid_cap.user_subdev_api) {
+ mf->code = ff->fmt->mbus_code;
+ mf->width = pix->width;
+ mf->height = pix->height;
+
+ fimc_md_graph_lock(fimc);
+ ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
+ fimc_md_graph_unlock(fimc);
+ if (ret)
+ return ret;
+ pix->width = mf->width;
+ pix->height = mf->height;
+ }
+
+ fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
+ for (i = 0; i < ff->fmt->colplanes; i++)
+ ff->payload[i] = pix->plane_fmt[i].sizeimage;
+
+ set_frame_bounds(ff, pix->width, pix->height);
+ /* Reset the composition rectangle if not yet configured */
+ if (!(ctx->state & FIMC_COMPOSE))
+ set_frame_crop(ff, 0, 0, pix->width, pix->height);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
+
+ /* Reset cropping and set format at the camera interface input */
+ if (!fimc->vid_cap.user_subdev_api) {
+ ctx->s_frame.fmt = s_fmt;
+ set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
+ set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
+ }
+
+ return ret;
+}
+
+static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return fimc_capture_set_format(fimc, f);
+}
+
+static int fimc_cap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+
+ if (i->index != 0)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ if (sd)
+ strlcpy(i->name, sd->name, sizeof(i->name));
+ return 0;
+}
+
+static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i == 0 ? i : -EINVAL;
+}
+
+static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+/**
+ * fimc_pipeline_validate - check for formats inconsistencies
+ * between source and sink pad of each link
+ *
+ * Return 0 if all formats match or -EPIPE otherwise.
+ */
+static int fimc_pipeline_validate(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ struct v4l2_subdev *sd;
+ struct media_pad *pad;
+ int ret;
+
+ /* Start with the video capture node pad */
+ pad = media_entity_remote_source(&vid_cap->vd_pad);
+ if (pad == NULL)
+ return -EPIPE;
+ /* FIMC.{N} subdevice */
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == &fimc->vid_cap.subdev) {
+ struct fimc_frame *ff = &vid_cap->ctx->s_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+static int fimc_cap_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_pipeline *p = &fimc->pipeline;
+ struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
+ int ret;
+
+ if (fimc_capture_active(fimc))
+ return -EBUSY;
+
+ ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline);
+ if (ret < 0)
+ return ret;
+
+ if (fimc->vid_cap.user_subdev_api) {
+ ret = fimc_pipeline_validate(fimc);
+ if (ret < 0) {
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
+ }
+ }
+ return vb2_streamon(&fimc->vid_cap.vbq, type);
+}
+
+static int fimc_cap_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ int ret;
+
+ ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
+}
+
+static int fimc_cap_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
+
+ if (!ret)
+ fimc->vid_cap.reqbufs_count = reqbufs->count;
+ return ret;
+}
+
+static int fimc_cap_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_querybuf(&fimc->vid_cap.vbq, buf);
+}
+
+static int fimc_cap_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vid_cap.vbq, buf);
+}
+
+static int fimc_cap_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int fimc_cap_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_create_bufs(&fimc->vid_cap.vbq, create);
+}
+
+static int fimc_cap_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+
+ return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
+}
+
+static int fimc_cap_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = f->o_width;
+ s->r.height = f->o_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = f->offs_h;
+ s->r.top = f->offs_v;
+ s->r.width = f->width;
+ s->r.height = f->height;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_cap_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_rect rect = s->r;
+ struct fimc_frame *f;
+ unsigned long flags;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ if (s->target == V4L2_SEL_TGT_COMPOSE)
+ f = &ctx->d_frame;
+ else if (s->target == V4L2_SEL_TGT_CROP)
+ f = &ctx->s_frame;
+ else
+ return -EINVAL;
+
+ fimc_capture_try_selection(ctx, &rect, s->target);
+
+ if (s->flags & V4L2_SEL_FLAG_LE &&
+ !enclosed_rectangle(&rect, &s->r))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE &&
+ !enclosed_rectangle(&s->r, &rect))
+ return -ERANGE;
+
+ s->r = rect;
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(f, s->r.left, s->r.top, s->r.width,
+ s->r.height);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
+ .vidioc_querycap = fimc_vidioc_querycap_capture,
+
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
+
+ .vidioc_reqbufs = fimc_cap_reqbufs,
+ .vidioc_querybuf = fimc_cap_querybuf,
+
+ .vidioc_qbuf = fimc_cap_qbuf,
+ .vidioc_dqbuf = fimc_cap_dqbuf,
+
+ .vidioc_prepare_buf = fimc_cap_prepare_buf,
+ .vidioc_create_bufs = fimc_cap_create_bufs,
+
+ .vidioc_streamon = fimc_cap_streamon,
+ .vidioc_streamoff = fimc_cap_streamoff,
+
+ .vidioc_g_selection = fimc_cap_g_selection,
+ .vidioc_s_selection = fimc_cap_s_selection,
+
+ .vidioc_enum_input = fimc_cap_enum_input,
+ .vidioc_s_input = fimc_cap_s_input,
+ .vidioc_g_input = fimc_cap_g_input,
+};
+
+/* Capture subdev media entity operations */
+static int fimc_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ dbg("%s --> %s, flags: 0x%x. input: 0x%x",
+ local->entity->name, remote->entity->name, flags,
+ fimc->vid_cap.input);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->vid_cap.input != 0)
+ return -EBUSY;
+ fimc->vid_cap.input = sd->grp_id;
+ return 0;
+ }
+
+ fimc->vid_cap.input = 0;
+ return 0;
+}
+
+static const struct media_entity_operations fimc_sd_media_ops = {
+ .link_setup = fimc_link_setup,
+};
+
+/**
+ * fimc_sensor_notify - v4l2_device notification from a sensor subdev
+ * @sd: pointer to a subdev generating the notification
+ * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
+ * @arg: pointer to an u32 type integer that stores the frame payload value
+ *
+ * The End Of Frame notification sent by sensor subdev in its still capture
+ * mode. If there is only a single VSYNC generated by the sensor at the
+ * beginning of a frame transmission, FIMC does not issue the LastIrq
+ * (end of frame) interrupt. And this notification is used to complete the
+ * frame capture and returning a buffer to user-space. Subdev drivers should
+ * call this notification from their last 'End of frame capture' interrupt.
+ */
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct fimc_sensor_info *sensor;
+ struct fimc_vid_buffer *buf;
+ struct fimc_md *fmd;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+
+ if (sd == NULL)
+ return;
+
+ sensor = v4l2_get_subdev_hostdata(sd);
+ fmd = entity_to_fimc_mdev(&sd->entity);
+
+ spin_lock_irqsave(&fmd->slock, flags);
+ fimc = sensor ? sensor->host : NULL;
+
+ if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
+ test_bit(ST_CAPT_PEND, &fimc->state)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fimc->slock, irq_flags);
+ if (!list_empty(&fimc->vid_cap.active_buf_q)) {
+ buf = list_entry(fimc->vid_cap.active_buf_q.next,
+ struct fimc_vid_buffer, list);
+ vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
+ }
+ fimc_capture_irq_handler(fimc, 1);
+ fimc_deactivate_capture(fimc);
+ spin_unlock_irqrestore(&fimc->slock, irq_flags);
+ }
+ spin_unlock_irqrestore(&fmd->slock, flags);
+}
+
+static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct v4l2_mbus_framefmt *mf;
+ struct fimc_frame *ff;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf = &fmt->format;
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ /* The pixel code is same on both input and output pad */
+ if (!WARN_ON(ctx->s_frame.fmt == NULL))
+ mf->code = ctx->s_frame.fmt->mbus_code;
+ mf->width = ff->f_width;
+ mf->height = ff->f_height;
+ mutex_unlock(&fimc->lock);
+
+ return 0;
+}
+
+static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *ff;
+ struct fimc_fmt *ffmt;
+
+ dbg("pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SOURCE &&
+ vb2_is_busy(&fimc->vid_cap.vbq))
+ return -EBUSY;
+
+ mutex_lock(&fimc->lock);
+ ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+ mutex_unlock(&fimc->lock);
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ return 0;
+ }
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
+
+ ff = fmt->pad == FIMC_SD_PAD_SINK ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ mutex_lock(&fimc->lock);
+ set_frame_bounds(ff, mf->width, mf->height);
+ fimc->vid_cap.mf = *mf;
+ ff->fmt = ffmt;
+
+ /* Reset the crop rectangle if required. */
+ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
+ set_frame_crop(ff, 0, 0, mf->width, mf->height);
+
+ if (fmt->pad == FIMC_SD_PAD_SINK)
+ ctx->state &= ~FIMC_COMPOSE;
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
+ struct v4l2_rect *r = &sel->r;
+ struct v4l2_rect *try_sel;
+
+ if (sel->pad != FIMC_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&fimc->lock);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ r->width = f->o_width;
+ r->height = f->o_height;
+ r->left = 0;
+ r->top = 0;
+ mutex_unlock(&fimc->lock);
+ return 0;
+
+ case V4L2_SEL_TGT_CROP:
+ try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+ f = &ctx->d_frame;
+ break;
+ default:
+ mutex_unlock(&fimc->lock);
+ return -EINVAL;
+ }
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sel->r = *try_sel;
+ } else {
+ r->left = f->offs_h;
+ r->top = f->offs_v;
+ r->width = f->width;
+ r->height = f->height;
+ }
+
+ dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ sel->pad, r->left, r->top, r->width, r->height,
+ f->f_width, f->f_height);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
+ struct v4l2_rect *r = &sel->r;
+ struct v4l2_rect *try_sel;
+ unsigned long flags;
+
+ if (sel->pad != FIMC_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&fimc->lock);
+ fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ r->width = f->o_width;
+ r->height = f->o_height;
+ r->left = 0;
+ r->top = 0;
+ mutex_unlock(&fimc->lock);
+ return 0;
+
+ case V4L2_SEL_TGT_CROP:
+ try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+ f = &ctx->d_frame;
+ break;
+ default:
+ mutex_unlock(&fimc->lock);
+ return -EINVAL;
+ }
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *try_sel = sel->r;
+ } else {
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(f, r->left, r->top, r->width, r->height);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ if (sel->target == V4L2_SEL_TGT_COMPOSE)
+ ctx->state |= FIMC_COMPOSE;
+ }
+
+ dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
+ r->width, r->height);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
+ .enum_mbus_code = fimc_subdev_enum_mbus_code,
+ .get_selection = fimc_subdev_get_selection,
+ .set_selection = fimc_subdev_set_selection,
+ .get_fmt = fimc_subdev_get_fmt,
+ .set_fmt = fimc_subdev_set_fmt,
+};
+
+static struct v4l2_subdev_ops fimc_subdev_ops = {
+ .pad = &fimc_subdev_pad_ops,
+};
+
+/* Set default format at the sensor and host interface */
+static int fimc_capture_set_default_format(struct fimc_dev *fimc)
+{
+ struct v4l2_format fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .fmt.pix_mp = {
+ .width = 640,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ };
+
+ return fimc_capture_set_format(fimc, &fmt);
+}
+
+/* fimc->lock must be already initialized */
+static int fimc_register_capture_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vfd = &fimc->vid_cap.vfd;
+ struct fimc_vid_cap *vid_cap;
+ struct fimc_ctx *ctx;
+ struct vb2_queue *q;
+ int ret = -ENOMEM;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->fimc_dev = fimc;
+ ctx->in_path = FIMC_IO_CAMERA;
+ ctx->out_path = FIMC_IO_DMA;
+ ctx->state = FIMC_CTX_CAP;
+ ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ ctx->d_frame.fmt = ctx->s_frame.fmt;
+
+ memset(vfd, 0, sizeof(*vfd));
+ snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id);
+
+ vfd->fops = &fimc_capture_fops;
+ vfd->ioctl_ops = &fimc_capture_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &fimc->lock;
+
+ video_set_drvdata(vfd, fimc);
+
+ vid_cap = &fimc->vid_cap;
+ vid_cap->active_buf_cnt = 0;
+ vid_cap->reqbufs_count = 0;
+ vid_cap->refcnt = 0;
+
+ INIT_LIST_HEAD(&vid_cap->pending_buf_q);
+ INIT_LIST_HEAD(&vid_cap->active_buf_q);
+ vid_cap->ctx = ctx;
+
+ 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);
+
+ vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
+ if (ret)
+ goto err_ent;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vd;
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+
+ vfd->ctrl_handler = &ctx->ctrls.handler;
+ return 0;
+
+err_vd:
+ media_entity_cleanup(&vfd->entity);
+err_ent:
+ kfree(ctx);
+ return ret;
+}
+
+static int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (fimc == NULL)
+ return -ENXIO;
+
+ ret = fimc_register_m2m_device(fimc, sd->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
+ if (ret)
+ fimc_unregister_m2m_device(fimc);
+
+ return ret;
+}
+
+static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc == NULL)
+ return;
+
+ fimc_unregister_m2m_device(fimc);
+
+ if (video_is_registered(&fimc->vid_cap.vfd)) {
+ video_unregister_device(&fimc->vid_cap.vfd);
+ media_entity_cleanup(&fimc->vid_cap.vfd.entity);
+ }
+ kfree(fimc->vid_cap.ctx);
+ fimc->vid_cap.ctx = NULL;
+}
+
+static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
+ .registered = fimc_capture_subdev_registered,
+ .unregistered = fimc_capture_subdev_unregistered,
+};
+
+int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
+ int ret;
+
+ v4l2_subdev_init(sd, &fimc_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads, 0);
+ if (ret)
+ return ret;
+
+ sd->entity.ops = &fimc_sd_media_ops;
+ sd->internal_ops = &fimc_capture_sd_internal_ops;
+ v4l2_set_subdevdata(sd, fimc);
+ return 0;
+}
+
+void fimc_unregister_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_set_subdevdata(sd, NULL);
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/platform/s5p-fimc/fimc-core.c
index 1a445404e73d..1a445404e73d 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/platform/s5p-fimc/fimc-core.c
diff --git a/drivers/media/platform/s5p-fimc/fimc-core.h b/drivers/media/platform/s5p-fimc/fimc-core.h
new file mode 100644
index 000000000000..cd716ba6015f
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-core.h
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef FIMC_CORE_H_
+#define FIMC_CORE_H_
+
+/*#define DEBUG*/
+
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5p_fimc.h>
+
+#define dbg(fmt, args...) \
+ pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
+
+/* Time to wait for next frame VSYNC interrupt while stopping operation. */
+#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
+#define MAX_FIMC_CLOCKS 2
+#define FIMC_MODULE_NAME "s5p-fimc"
+#define FIMC_MAX_DEVS 4
+#define FIMC_MAX_OUT_BUFS 4
+#define SCALER_MAX_HRATIO 64
+#define SCALER_MAX_VRATIO 64
+#define DMA_MIN_SIZE 8
+#define FIMC_CAMIF_MAX_HEIGHT 0x2000
+
+/* indices to the clocks array */
+enum {
+ CLK_BUS,
+ CLK_GATE,
+};
+
+enum fimc_dev_flags {
+ ST_LPM,
+ /* m2m node */
+ ST_M2M_RUN,
+ ST_M2M_PEND,
+ ST_M2M_SUSPENDING,
+ ST_M2M_SUSPENDED,
+ /* capture node */
+ ST_CAPT_PEND,
+ ST_CAPT_RUN,
+ ST_CAPT_STREAM,
+ ST_CAPT_ISP_STREAM,
+ ST_CAPT_SUSPENDED,
+ ST_CAPT_SHUT,
+ ST_CAPT_BUSY,
+ ST_CAPT_APPLY_CFG,
+ ST_CAPT_JPEG,
+};
+
+#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
+#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
+
+#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
+#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
+#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
+
+enum fimc_datapath {
+ FIMC_IO_NONE,
+ FIMC_IO_CAMERA,
+ FIMC_IO_DMA,
+ FIMC_IO_LCDFIFO,
+ FIMC_IO_WRITEBACK,
+ FIMC_IO_ISP,
+};
+
+enum fimc_color_fmt {
+ FIMC_FMT_RGB444 = 0x10,
+ FIMC_FMT_RGB555,
+ FIMC_FMT_RGB565,
+ FIMC_FMT_RGB666,
+ FIMC_FMT_RGB888,
+ FIMC_FMT_RGB30_LOCAL,
+ FIMC_FMT_YCBCR420 = 0x20,
+ FIMC_FMT_YCBYCR422,
+ FIMC_FMT_YCRYCB422,
+ FIMC_FMT_CBYCRY422,
+ FIMC_FMT_CRYCBY422,
+ FIMC_FMT_YCBCR444_LOCAL,
+ FIMC_FMT_JPEG = 0x40,
+ FIMC_FMT_RAW8 = 0x80,
+ FIMC_FMT_RAW10,
+ FIMC_FMT_RAW12,
+};
+
+#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
+#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40))
+
+#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
+ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+
+/* The hardware context state. */
+#define FIMC_PARAMS (1 << 0)
+#define FIMC_SRC_FMT (1 << 3)
+#define FIMC_DST_FMT (1 << 4)
+#define FIMC_COMPOSE (1 << 5)
+#define FIMC_CTX_M2M (1 << 16)
+#define FIMC_CTX_CAP (1 << 17)
+#define FIMC_CTX_SHUT (1 << 18)
+
+/* Image conversion flags */
+#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
+#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
+#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
+#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
+#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
+#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
+/*
+ * YCbCr data dynamic range for RGB-YUV color conversion.
+ * Y/Cb/Cr: (0 ~ 255) */
+#define FIMC_COLOR_RANGE_WIDE (0 << 3)
+/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
+#define FIMC_COLOR_RANGE_NARROW (1 << 3)
+
+/**
+ * struct fimc_fmt - the driver's internal color format data
+ * @mbus_code: Media Bus pixel code, -1 if not applicable
+ * @name: format description
+ * @fourcc: the fourcc code for this format, 0 if not applicable
+ * @color: the corresponding fimc_color_fmt
+ * @memplanes: number of physically non-contiguous data planes
+ * @colplanes: number of physically contiguous data planes
+ * @depth: per plane driver's private 'number of bits per pixel'
+ * @flags: flags indicating which operation mode format applies to
+ */
+struct fimc_fmt {
+ enum v4l2_mbus_pixelcode mbus_code;
+ char *name;
+ u32 fourcc;
+ u32 color;
+ u16 memplanes;
+ u16 colplanes;
+ u8 depth[VIDEO_MAX_PLANES];
+ u16 flags;
+#define FMT_FLAGS_CAM (1 << 0)
+#define FMT_FLAGS_M2M_IN (1 << 1)
+#define FMT_FLAGS_M2M_OUT (1 << 2)
+#define FMT_FLAGS_M2M (1 << 1 | 1 << 2)
+#define FMT_HAS_ALPHA (1 << 3)
+};
+
+/**
+ * struct fimc_dma_offset - pixel offset information for DMA
+ * @y_h: y value horizontal offset
+ * @y_v: y value vertical offset
+ * @cb_h: cb value horizontal offset
+ * @cb_v: cb value vertical offset
+ * @cr_h: cr value horizontal offset
+ * @cr_v: cr value vertical offset
+ */
+struct fimc_dma_offset {
+ int y_h;
+ int y_v;
+ int cb_h;
+ int cb_v;
+ int cr_h;
+ int cr_v;
+};
+
+/**
+ * struct fimc_effect - color effect information
+ * @type: effect type
+ * @pat_cb: cr value when type is "arbitrary"
+ * @pat_cr: cr value when type is "arbitrary"
+ */
+struct fimc_effect {
+ u32 type;
+ u8 pat_cb;
+ u8 pat_cr;
+};
+
+/**
+ * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
+ * @scaleup_h: flag indicating scaling up horizontally
+ * @scaleup_v: flag indicating scaling up vertically
+ * @copy_mode: flag indicating transparent DMA transfer (no scaling
+ * and color format conversion)
+ * @enabled: flag indicating if the scaler is used
+ * @hfactor: horizontal shift factor
+ * @vfactor: vertical shift factor
+ * @pre_hratio: horizontal ratio of the prescaler
+ * @pre_vratio: vertical ratio of the prescaler
+ * @pre_dst_width: the prescaler's destination width
+ * @pre_dst_height: the prescaler's destination height
+ * @main_hratio: the main scaler's horizontal ratio
+ * @main_vratio: the main scaler's vertical ratio
+ * @real_width: source pixel (width - offset)
+ * @real_height: source pixel (height - offset)
+ */
+struct fimc_scaler {
+ unsigned int scaleup_h:1;
+ unsigned int scaleup_v:1;
+ unsigned int copy_mode:1;
+ unsigned int enabled:1;
+ u32 hfactor;
+ u32 vfactor;
+ u32 pre_hratio;
+ u32 pre_vratio;
+ u32 pre_dst_width;
+ u32 pre_dst_height;
+ u32 main_hratio;
+ u32 main_vratio;
+ u32 real_width;
+ u32 real_height;
+};
+
+/**
+ * struct fimc_addr - the FIMC physical address set for DMA
+ * @y: luminance plane physical address
+ * @cb: Cb plane physical address
+ * @cr: Cr plane physical address
+ */
+struct fimc_addr {
+ u32 y;
+ u32 cb;
+ u32 cr;
+};
+
+/**
+ * struct fimc_vid_buffer - the driver's video buffer
+ * @vb: v4l videobuf buffer
+ * @list: linked list structure for buffer queue
+ * @paddr: precalculated physical address set
+ * @index: buffer index for the output DMA engine
+ */
+struct fimc_vid_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+ struct fimc_addr paddr;
+ int index;
+};
+
+/**
+ * struct fimc_frame - source/target frame properties
+ * @f_width: image full width (virtual screen size)
+ * @f_height: image full height (virtual screen size)
+ * @o_width: original image width as set by S_FMT
+ * @o_height: original image height as set by S_FMT
+ * @offs_h: image horizontal pixel offset
+ * @offs_v: image vertical pixel offset
+ * @width: image pixel width
+ * @height: image pixel weight
+ * @payload: image size in bytes (w x h x bpp)
+ * @paddr: image frame buffer physical addresses
+ * @dma_offset: DMA offset in bytes
+ * @fmt: fimc color format pointer
+ */
+struct fimc_frame {
+ u32 f_width;
+ u32 f_height;
+ u32 o_width;
+ u32 o_height;
+ u32 offs_h;
+ u32 offs_v;
+ u32 width;
+ u32 height;
+ unsigned long payload[VIDEO_MAX_PLANES];
+ struct fimc_addr paddr;
+ struct fimc_dma_offset dma_offset;
+ struct fimc_fmt *fmt;
+ u8 alpha;
+};
+
+/**
+ * struct fimc_m2m_device - v4l2 memory-to-memory device data
+ * @vfd: the video device node for v4l2 m2m mode
+ * @m2m_dev: v4l2 memory-to-memory device data
+ * @ctx: hardware context data
+ * @refcnt: the reference counter
+ */
+struct fimc_m2m_device {
+ struct video_device vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct fimc_ctx *ctx;
+ int refcnt;
+};
+
+#define FIMC_SD_PAD_SINK 0
+#define FIMC_SD_PAD_SOURCE 1
+#define FIMC_SD_PADS_NUM 2
+
+/**
+ * struct fimc_vid_cap - camera capture device information
+ * @ctx: hardware context data
+ * @vfd: video device node for camera capture mode
+ * @subdev: subdev exposing the FIMC processing block
+ * @vd_pad: fimc video capture node pad
+ * @sd_pads: fimc video processing block pads
+ * @mf: media bus format at the FIMC camera input (and the scaler output) pad
+ * @pending_buf_q: the pending buffer queue head
+ * @active_buf_q: the queue head of buffers scheduled in hardware
+ * @vbq: the capture am video buffer queue
+ * @active_buf_cnt: number of video buffers scheduled in hardware
+ * @buf_index: index for managing the output DMA buffers
+ * @frame_count: the frame counter for statistics
+ * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
+ * @input_index: input (camera sensor) index
+ * @refcnt: driver's private reference counter
+ * @input: capture input type, grp_id of the attached subdev
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ */
+struct fimc_vid_cap {
+ struct fimc_ctx *ctx;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct video_device vfd;
+ struct v4l2_subdev subdev;
+ struct media_pad vd_pad;
+ struct v4l2_mbus_framefmt mf;
+ struct media_pad sd_pads[FIMC_SD_PADS_NUM];
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ struct vb2_queue vbq;
+ int active_buf_cnt;
+ int buf_index;
+ unsigned int frame_count;
+ unsigned int reqbufs_count;
+ int input_index;
+ int refcnt;
+ u32 input;
+ bool user_subdev_api;
+};
+
+/**
+ * struct fimc_pix_limit - image pixel size limits in various IP configurations
+ *
+ * @scaler_en_w: max input pixel width when the scaler is enabled
+ * @scaler_dis_w: max input pixel width when the scaler is disabled
+ * @in_rot_en_h: max input width with the input rotator is on
+ * @in_rot_dis_w: max input width with the input rotator is off
+ * @out_rot_en_w: max output width with the output rotator on
+ * @out_rot_dis_w: max output width with the output rotator off
+ */
+struct fimc_pix_limit {
+ u16 scaler_en_w;
+ u16 scaler_dis_w;
+ u16 in_rot_en_h;
+ u16 in_rot_dis_w;
+ u16 out_rot_en_w;
+ u16 out_rot_dis_w;
+};
+
+/**
+ * struct fimc_variant - FIMC device variant information
+ * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
+ * @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
+ * @has_cam_if: set if this instance has a camera input interface
+ * @pix_limit: pixel size constraints for the scaler
+ * @min_inp_pixsize: minimum input pixel size
+ * @min_out_pixsize: minimum output pixel size
+ * @hor_offs_align: horizontal pixel offset aligment
+ * @min_vsize_align: minimum vertical pixel size alignment
+ * @out_buf_count: the number of buffers in output DMA sequence
+ */
+struct fimc_variant {
+ unsigned int pix_hoff:1;
+ unsigned int has_inp_rot:1;
+ unsigned int has_out_rot:1;
+ unsigned int has_cistatus2:1;
+ unsigned int has_mainscaler_ext:1;
+ unsigned int has_cam_if:1;
+ unsigned int has_alpha:1;
+ struct fimc_pix_limit *pix_limit;
+ u16 min_inp_pixsize;
+ u16 min_out_pixsize;
+ u16 hor_offs_align;
+ u16 min_vsize_align;
+ u16 out_buf_count;
+};
+
+/**
+ * struct fimc_drvdata - per device type driver data
+ * @variant: variant information for this device
+ * @num_entities: number of fimc instances available in a SoC
+ * @lclk_frequency: local bus clock frequency
+ */
+struct fimc_drvdata {
+ struct fimc_variant *variant[FIMC_MAX_DEVS];
+ int num_entities;
+ unsigned long lclk_frequency;
+};
+
+#define fimc_get_drvdata(_pdev) \
+ ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data)
+
+struct fimc_ctx;
+
+/**
+ * struct fimc_dev - abstraction for FIMC entity
+ * @slock: the spinlock protecting this data structure
+ * @lock: the mutex protecting this data structure
+ * @pdev: pointer to the FIMC platform device
+ * @pdata: pointer to the device platform data
+ * @variant: the IP variant information
+ * @id: FIMC device index (0..FIMC_MAX_DEVS)
+ * @clock: clocks required for FIMC operation
+ * @regs: the mapped hardware registers
+ * @irq_queue: interrupt handler waitqueue
+ * @v4l2_dev: root v4l2_device
+ * @m2m: memory-to-memory V4L2 device information
+ * @vid_cap: camera capture device information
+ * @state: flags used to synchronize m2m and capture mode operation
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @pipeline: fimc video capture pipeline data structure
+ */
+struct fimc_dev {
+ spinlock_t slock;
+ struct mutex lock;
+ struct platform_device *pdev;
+ struct s5p_platform_fimc *pdata;
+ struct fimc_variant *variant;
+ u16 id;
+ struct clk *clock[MAX_FIMC_CLOCKS];
+ void __iomem *regs;
+ wait_queue_head_t irq_queue;
+ struct v4l2_device *v4l2_dev;
+ struct fimc_m2m_device m2m;
+ struct fimc_vid_cap vid_cap;
+ unsigned long state;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct fimc_pipeline pipeline;
+ const struct fimc_pipeline_ops *pipeline_ops;
+};
+
+/**
+ * struct fimc_ctrls - v4l2 controls structure
+ * @handler: the control handler
+ * @colorfx: image effect control
+ * @colorfx_cbcr: Cb/Cr coefficients control
+ * @rotate: image rotation control
+ * @hflip: horizontal flip control
+ * @vflip: vertical flip control
+ * @alpha: RGB alpha control
+ * @ready: true if @handler is initialized
+ */
+struct fimc_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct {
+ struct v4l2_ctrl *colorfx;
+ struct v4l2_ctrl *colorfx_cbcr;
+ };
+ struct v4l2_ctrl *rotate;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *alpha;
+ bool ready;
+};
+
+/**
+ * fimc_ctx - the device context data
+ * @s_frame: source frame properties
+ * @d_frame: destination frame properties
+ * @out_order_1p: output 1-plane YCBCR order
+ * @out_order_2p: output 2-plane YCBCR order
+ * @in_order_1p input 1-plane YCBCR order
+ * @in_order_2p: input 2-plane YCBCR order
+ * @in_path: input mode (DMA or camera)
+ * @out_path: output mode (DMA or FIFO)
+ * @scaler: image scaler properties
+ * @effect: image effect
+ * @rotation: image clockwise rotation in degrees
+ * @hflip: indicates image horizontal flip if set
+ * @vflip: indicates image vertical flip if set
+ * @flags: additional flags for image conversion
+ * @state: flags to keep track of user configuration
+ * @fimc_dev: the FIMC device this context applies to
+ * @m2m_ctx: memory-to-memory device context
+ * @fh: v4l2 file handle
+ * @ctrls: v4l2 controls structure
+ */
+struct fimc_ctx {
+ struct fimc_frame s_frame;
+ struct fimc_frame d_frame;
+ u32 out_order_1p;
+ u32 out_order_2p;
+ u32 in_order_1p;
+ u32 in_order_2p;
+ enum fimc_datapath in_path;
+ enum fimc_datapath out_path;
+ struct fimc_scaler scaler;
+ struct fimc_effect effect;
+ int rotation;
+ unsigned int hflip:1;
+ unsigned int vflip:1;
+ u32 flags;
+ u32 state;
+ struct fimc_dev *fimc_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct v4l2_fh fh;
+ struct fimc_ctrls ctrls;
+};
+
+#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
+
+static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height)
+{
+ f->o_width = width;
+ f->o_height = height;
+ f->f_width = width;
+ f->f_height = height;
+}
+
+static inline void set_frame_crop(struct fimc_frame *f,
+ u32 left, u32 top, u32 width, u32 height)
+{
+ f->offs_h = left;
+ f->offs_v = top;
+ f->width = width;
+ f->height = height;
+}
+
+static inline u32 fimc_get_format_depth(struct fimc_fmt *ff)
+{
+ u32 i, depth = 0;
+
+ if (ff != NULL)
+ for (i = 0; i < ff->colplanes; i++)
+ depth += ff->depth[i];
+ return depth;
+}
+
+static inline bool fimc_capture_active(struct fimc_dev *fimc)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ ret = !!(fimc->state & (1 << ST_CAPT_RUN) ||
+ fimc->state & (1 << ST_CAPT_PEND));
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
+ ctx->state |= state;
+ spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
+}
+
+static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
+ ret = (ctx->state & mask) == mask;
+ spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
+ return ret;
+}
+
+static inline int tiled_fmt(struct fimc_fmt *fmt)
+{
+ return fmt->fourcc == V4L2_PIX_FMT_NV12MT;
+}
+
+/* Return the alpha component bit mask */
+static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt)
+{
+ switch (fmt->color) {
+ case FIMC_FMT_RGB444: return 0x0f;
+ case FIMC_FMT_RGB555: return 0x01;
+ case FIMC_FMT_RGB888: return 0xff;
+ default: return 0;
+ };
+}
+
+static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ struct fimc_frame *frame;
+
+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
+ if (fimc_ctx_state_is_set(FIMC_CTX_M2M, ctx))
+ frame = &ctx->s_frame;
+ else
+ return ERR_PTR(-EINVAL);
+ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
+ frame = &ctx->d_frame;
+ } else {
+ v4l2_err(ctx->fimc_dev->v4l2_dev,
+ "Wrong buffer/video queue type (%d)\n", type);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return frame;
+}
+
+/* -----------------------------------------------------*/
+/* fimc-core.c */
+int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f);
+int fimc_ctrls_create(struct fimc_ctx *ctx);
+void fimc_ctrls_delete(struct fimc_ctx *ctx);
+void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
+void fimc_alpha_ctrl_update(struct fimc_ctx *ctx);
+int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
+ struct v4l2_pix_format_mplane *pix);
+struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
+ unsigned int mask, int index);
+struct fimc_fmt *fimc_get_format(unsigned int index);
+
+int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
+ int dw, int dh, int rotation);
+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 vb2_buffer *vb,
+ struct fimc_frame *frame, struct fimc_addr *paddr);
+void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
+void fimc_set_yuv_order(struct fimc_ctx *ctx);
+void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf);
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev);
+void fimc_unregister_m2m_device(struct fimc_dev *fimc);
+int fimc_register_driver(void);
+void fimc_unregister_driver(void);
+
+/* -----------------------------------------------------*/
+/* fimc-m2m.c */
+void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state);
+
+/* -----------------------------------------------------*/
+/* fimc-capture.c */
+int fimc_initialize_capture_subdev(struct fimc_dev *fimc);
+void fimc_unregister_capture_subdev(struct fimc_dev *fimc);
+int fimc_capture_ctrls_create(struct fimc_dev *fimc);
+void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg);
+int fimc_capture_suspend(struct fimc_dev *fimc);
+int fimc_capture_resume(struct fimc_dev *fimc);
+
+/*
+ * Buffer list manipulation functions. Must be called with fimc.slock held.
+ */
+
+/**
+ * fimc_active_queue_add - add buffer to the capture active buffers queue
+ * @buf: buffer to add to the active buffers list
+ */
+static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
+{
+ list_add_tail(&buf->list, &vid_cap->active_buf_q);
+ vid_cap->active_buf_cnt++;
+}
+
+/**
+ * fimc_active_queue_pop - pop buffer from the capture active buffers queue
+ *
+ * The caller must assure the active_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_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, list);
+ list_del(&buf->list);
+ vid_cap->active_buf_cnt--;
+ return buf;
+}
+
+/**
+ * fimc_pending_queue_add - add buffer to the capture pending buffers queue
+ * @buf: buffer to add to the pending buffers list
+ */
+static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
+ struct fimc_vid_buffer *buf)
+{
+ list_add_tail(&buf->list, &vid_cap->pending_buf_q);
+}
+
+/**
+ * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
+ *
+ * The caller must assure the pending_buf_q list is not empty.
+ */
+static inline struct fimc_vid_buffer *fimc_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, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite-reg.c b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c
new file mode 100644
index 000000000000..a22d7eb05c82
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.c
@@ -0,0 +1,300 @@
+/*
+ * Register interface file for EXYNOS FIMC-LITE (camera interface) driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-lite-reg.h"
+#include "fimc-lite.h"
+#include "fimc-core.h"
+
+#define FLITE_RESET_TIMEOUT 50 /* in ms */
+
+void flite_hw_reset(struct fimc_lite *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT);
+ u32 cfg;
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg |= FLITE_REG_CIGCTRL_SWRST_REQ;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ while (time_is_after_jiffies(end)) {
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY)
+ break;
+ usleep_range(1000, 5000);
+ }
+
+ cfg |= FLITE_REG_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+void flite_hw_clear_pending_irq(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS);
+ cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM;
+ writel(cfg, dev->regs + FLITE_REG_CISTATUS);
+}
+
+u32 flite_hw_get_interrupt_source(struct fimc_lite *dev)
+{
+ u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS);
+ return intsrc & FLITE_REG_CISTATUS_IRQ_MASK;
+}
+
+void flite_hw_clear_last_capture_end(struct fimc_lite *dev)
+{
+
+ u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2);
+ cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND;
+ writel(cfg, dev->regs + FLITE_REG_CISTATUS2);
+}
+
+void flite_hw_set_interrupt_mask(struct fimc_lite *dev)
+{
+ u32 cfg, intsrc;
+
+ /* Select interrupts to be enabled for each output mode */
+ if (dev->out_path == FIMC_IO_DMA) {
+ intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
+ FLITE_REG_CIGCTRL_IRQ_LASTEN |
+ FLITE_REG_CIGCTRL_IRQ_STARTEN;
+ } else {
+ /* An output to the FIMC-IS */
+ intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
+ FLITE_REG_CIGCTRL_IRQ_LASTEN;
+ }
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK;
+ cfg &= ~intsrc;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+void flite_hw_capture_start(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
+ cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN;
+ writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
+}
+
+void flite_hw_capture_stop(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
+ cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN;
+ writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
+}
+
+/*
+ * Test pattern (color bars) enable/disable. External sensor
+ * pixel clock must be active for the test pattern to work.
+ */
+void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ if (on)
+ cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
+ else
+ cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+static const u32 src_pixfmt_map[8][3] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, 0, FLITE_REG_CIGCTRL_RAW8 },
+ { V4L2_MBUS_FMT_SGRBG10_1X10, 0, FLITE_REG_CIGCTRL_RAW10 },
+ { V4L2_MBUS_FMT_SGRBG12_1X12, 0, FLITE_REG_CIGCTRL_RAW12 },
+ { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) },
+};
+
+/* Set camera input pixel format and resolution */
+void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
+{
+ enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code;
+ unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ u32 cfg;
+
+ while (i-- >= 0) {
+ if (src_pixfmt_map[i][0] == pixelcode)
+ break;
+ }
+
+ if (i == 0 && src_pixfmt_map[i][0] != pixelcode) {
+ v4l2_err(&dev->vfd,
+ "Unsupported pixel code, falling back to %#08x\n",
+ src_pixfmt_map[i][0]);
+ }
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK;
+ cfg |= src_pixfmt_map[i][2];
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ cfg = readl(dev->regs + FLITE_REG_CISRCSIZE);
+ cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK |
+ FLITE_REG_CISRCSIZE_SIZE_CAM_MASK);
+ cfg |= (f->f_width << 16) | f->f_height;
+ cfg |= src_pixfmt_map[i][1];
+ writel(cfg, dev->regs + FLITE_REG_CISRCSIZE);
+}
+
+/* Set the camera host input window offsets (cropping) */
+void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f)
+{
+ u32 hoff2, voff2;
+ u32 cfg;
+
+ cfg = readl(dev->regs + FLITE_REG_CIWDOFST);
+ cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK;
+ cfg |= (f->rect.left << 16) | f->rect.top;
+ cfg |= FLITE_REG_CIWDOFST_WINOFSEN;
+ writel(cfg, dev->regs + FLITE_REG_CIWDOFST);
+
+ hoff2 = f->f_width - f->rect.width - f->rect.left;
+ voff2 = f->f_height - f->rect.height - f->rect.top;
+
+ cfg = (hoff2 << 16) | voff2;
+ writel(cfg, dev->regs + FLITE_REG_CIWDOFST2);
+}
+
+/* Select camera port (A, B) */
+static void flite_hw_set_camera_port(struct fimc_lite *dev, int id)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL);
+ if (id == 0)
+ cfg &= ~FLITE_REG_CIGENERAL_CAM_B;
+ else
+ cfg |= FLITE_REG_CIGENERAL_CAM_B;
+ writel(cfg, dev->regs + FLITE_REG_CIGENERAL);
+}
+
+/* Select serial or parallel bus, camera port (A,B) and set signals polarity */
+void flite_hw_set_camera_bus(struct fimc_lite *dev,
+ struct s5p_fimc_isp_info *s_info)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ unsigned int flags = s_info->flags;
+
+ if (s_info->bus_type != FIMC_MIPI_CSI2) {
+ cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI |
+ FLITE_REG_CIGCTRL_INVPOLPCLK |
+ FLITE_REG_CIGCTRL_INVPOLVSYNC |
+ FLITE_REG_CIGCTRL_INVPOLHREF);
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLHREF;
+ } else {
+ cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI;
+ }
+
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ flite_hw_set_camera_port(dev, s_info->mux_id);
+}
+
+static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
+{
+ static const u32 pixcode[4][2] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
+ { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB },
+ { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY },
+ { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
+ };
+ u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
+ unsigned int i = ARRAY_SIZE(pixcode);
+
+ while (i-- >= 0)
+ if (pixcode[i][0] == dev->fmt->mbus_code)
+ break;
+ cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK;
+ writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT);
+}
+
+void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f)
+{
+ u32 cfg;
+
+ /* Maximum output pixel size */
+ cfg = readl(dev->regs + FLITE_REG_CIOCAN);
+ cfg &= ~FLITE_REG_CIOCAN_MASK;
+ cfg = (f->f_height << 16) | f->f_width;
+ writel(cfg, dev->regs + FLITE_REG_CIOCAN);
+
+ /* DMA offsets */
+ cfg = readl(dev->regs + FLITE_REG_CIOOFF);
+ cfg &= ~FLITE_REG_CIOOFF_MASK;
+ cfg |= (f->rect.top << 16) | f->rect.left;
+ writel(cfg, dev->regs + FLITE_REG_CIOOFF);
+}
+
+/* Enable/disable output DMA, set output pixel size and offsets (composition) */
+void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
+ bool enable)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+
+ if (!enable) {
+ cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+ return;
+ }
+
+ cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ flite_hw_set_out_order(dev, f);
+ flite_hw_set_dma_window(dev, f);
+}
+
+void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
+{
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x00, "CISRCSIZE" },
+ { 0x04, "CIGCTRL" },
+ { 0x08, "CIIMGCPT" },
+ { 0x0c, "CICPTSEQ" },
+ { 0x10, "CIWDOFST" },
+ { 0x14, "CIWDOFST2" },
+ { 0x18, "CIODMAFMT" },
+ { 0x20, "CIOCAN" },
+ { 0x24, "CIOOFF" },
+ { 0x30, "CIOSA" },
+ { 0x40, "CISTATUS" },
+ { 0x44, "CISTATUS2" },
+ { 0xf0, "CITHOLD" },
+ { 0xfc, "CIGENERAL" },
+ };
+ u32 i;
+
+ pr_info("--- %s ---\n", label);
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ u32 cfg = readl(dev->regs + registers[i].offset);
+ pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg);
+ }
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.h b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h
index adb9e9e6f3c2..adb9e9e6f3c2 100644
--- a/drivers/media/video/s5p-fimc/fimc-lite-reg.h
+++ b/drivers/media/platform/s5p-fimc/fimc-lite-reg.h
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c
new file mode 100644
index 000000000000..70bcf39de879
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-lite.c
@@ -0,0 +1,1604 @@
+/*
+ * Samsung EXYNOS FIMC-LITE (camera host interface) driver
+*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-mdevice.h"
+#include "fimc-core.h"
+#include "fimc-lite.h"
+#include "fimc-lite-reg.h"
+
+static int debug;
+module_param(debug, int, 0644);
+
+static const struct fimc_fmt fimc_lite_formats[] = {
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = FIMC_FMT_YCBYCR422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = FIMC_FMT_CBYCRY422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = { 16 },
+ .color = FIMC_FMT_CRYCBY422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = FIMC_FMT_YCRYCB422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ }, {
+ .name = "RAW8 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = { 8 },
+ .color = FIMC_FMT_RAW8,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ }, {
+ .name = "RAW10 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .depth = { 10 },
+ .color = FIMC_FMT_RAW10,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ }, {
+ .name = "RAW12 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .depth = { 12 },
+ .color = FIMC_FMT_RAW12,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ },
+};
+
+/**
+ * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @index: index to the fimc_lite_formats array, ignored if negative
+ */
+static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
+ const u32 *mbus_code, int index)
+{
+ const struct fimc_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+ int id = 0;
+
+ if (index >= (int)ARRAY_SIZE(fimc_lite_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) {
+ fmt = &fimc_lite_formats[i];
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
+ }
+ return def_fmt;
+}
+
+static int fimc_lite_hw_init(struct fimc_lite *fimc)
+{
+ struct fimc_pipeline *pipeline = &fimc->pipeline;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+
+ if (pipeline->subdevs[IDX_SENSOR] == NULL)
+ return -ENXIO;
+
+ if (fimc->fmt == NULL)
+ return -EINVAL;
+
+ sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ flite_hw_set_camera_bus(fimc, &sensor->pdata);
+ flite_hw_set_source_format(fimc, &fimc->inp_frame);
+ flite_hw_set_window_offset(fimc, &fimc->inp_frame);
+ flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
+ flite_hw_set_interrupt_mask(fimc);
+ flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
+
+ if (debug > 0)
+ flite_hw_dump_regs(fimc, __func__);
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+}
+
+/*
+ * Reinitialize the driver so it is ready to start the streaming again.
+ * Set fimc->state to indicate stream off and the hardware shut down state.
+ * If not suspending (@suspend is false), return any buffers to videobuf2.
+ * Otherwise put any owned buffers onto the pending buffers queue, so they
+ * can be re-spun when the device is being resumed. Also perform FIMC
+ * software reset and disable streaming on the whole pipeline if required.
+ */
+static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend)
+{
+ struct flite_buffer *buf;
+ unsigned long flags;
+ bool streaming;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_SENSOR_STREAM);
+
+ fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF |
+ 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM);
+ if (suspend)
+ fimc->state |= (1 << ST_FLITE_SUSPENDED);
+ else
+ fimc->state &= ~(1 << ST_FLITE_PENDING |
+ 1 << ST_FLITE_SUSPENDED);
+
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&fimc->pending_buf_q)) {
+ buf = fimc_lite_pending_queue_pop(fimc);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&fimc->active_buf_q)) {
+ buf = fimc_lite_active_queue_pop(fimc);
+ if (suspend)
+ fimc_lite_pending_queue_add(fimc, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ flite_hw_reset(fimc);
+
+ if (!streaming)
+ return 0;
+
+ return fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 0);
+}
+
+static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend)
+{
+ unsigned long flags;
+
+ if (!fimc_lite_active(fimc))
+ return 0;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_FLITE_OFF, &fimc->state);
+ flite_hw_capture_stop(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_FLITE_OFF, &fimc->state),
+ (2*HZ/10)); /* 200 ms */
+
+ return fimc_lite_reinit(fimc, suspend);
+}
+
+/* Must be called with fimc.slock spinlock held. */
+static void fimc_lite_config_update(struct fimc_lite *fimc)
+{
+ flite_hw_set_window_offset(fimc, &fimc->inp_frame);
+ flite_hw_set_dma_window(fimc, &fimc->out_frame);
+ flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
+ clear_bit(ST_FLITE_CONFIG, &fimc->state);
+}
+
+static irqreturn_t flite_irq_handler(int irq, void *priv)
+{
+ struct fimc_lite *fimc = priv;
+ struct flite_buffer *vbuf;
+ unsigned long flags;
+ struct timeval *tv;
+ struct timespec ts;
+ u32 intsrc;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ intsrc = flite_hw_get_interrupt_source(fimc);
+ flite_hw_clear_pending_irq(fimc);
+
+ if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ goto done;
+ }
+
+ if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) {
+ clear_bit(ST_FLITE_RUN, &fimc->state);
+ fimc->events.data_overflow++;
+ }
+
+ if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) {
+ flite_hw_clear_last_capture_end(fimc);
+ clear_bit(ST_FLITE_STREAM, &fimc->state);
+ wake_up(&fimc->irq_queue);
+ }
+
+ if (fimc->out_path != FIMC_IO_DMA)
+ goto done;
+
+ if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
+ test_bit(ST_FLITE_RUN, &fimc->state) &&
+ !list_empty(&fimc->active_buf_q) &&
+ !list_empty(&fimc->pending_buf_q)) {
+ vbuf = fimc_lite_active_queue_pop(fimc);
+ ktime_get_ts(&ts);
+ tv = &vbuf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ vbuf->vb.v4l2_buf.sequence = fimc->frame_count++;
+ vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
+
+ vbuf = fimc_lite_pending_queue_pop(fimc);
+ flite_hw_set_output_addr(fimc, vbuf->paddr);
+ fimc_lite_active_queue_add(fimc, vbuf);
+ }
+
+ if (test_bit(ST_FLITE_CONFIG, &fimc->state))
+ fimc_lite_config_update(fimc);
+
+ if (list_empty(&fimc->pending_buf_q)) {
+ flite_hw_capture_stop(fimc);
+ clear_bit(ST_FLITE_STREAM, &fimc->state);
+ }
+done:
+ set_bit(ST_FLITE_RUN, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return IRQ_HANDLED;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_lite *fimc = q->drv_priv;
+ int ret;
+
+ fimc->frame_count = 0;
+
+ ret = fimc_lite_hw_init(fimc);
+ if (ret) {
+ fimc_lite_reinit(fimc, false);
+ return ret;
+ }
+
+ set_bit(ST_FLITE_PENDING, &fimc->state);
+
+ if (!list_empty(&fimc->active_buf_q) &&
+ !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
+ flite_hw_capture_start(fimc);
+
+ if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
+ fimc_pipeline_call(fimc, set_stream,
+ &fimc->pipeline, 1);
+ }
+ if (debug > 0)
+ flite_hw_dump_regs(fimc, __func__);
+
+ return 0;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_lite *fimc = q->drv_priv;
+
+ if (!fimc_lite_active(fimc))
+ return -EINVAL;
+
+ return fimc_lite_stop_capture(fimc, false);
+}
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ struct fimc_lite *fimc = vq->drv_priv;
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = fimc->fmt;
+ unsigned long wh;
+ int i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ wh = frame->f_width * frame->f_height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
+ allocators[i] = fimc->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_lite *fimc = vq->drv_priv;
+ int i;
+
+ if (fimc->fmt == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < fimc->fmt->memplanes; i++) {
+ unsigned long size = fimc->payload[i];
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&fimc->vfd,
+ "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 flite_buffer *buf
+ = container_of(vb, struct flite_buffer, vb);
+ struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_FLITE_STREAM, &fimc->state) &&
+ list_empty(&fimc->active_buf_q)) {
+ flite_hw_set_output_addr(fimc, buf->paddr);
+ fimc_lite_active_queue_add(fimc, buf);
+ } else {
+ fimc_lite_pending_queue_add(fimc, buf);
+ }
+
+ if (vb2_is_streaming(&fimc->vb_queue) &&
+ !list_empty(&fimc->pending_buf_q) &&
+ !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
+ flite_hw_capture_start(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
+ fimc_pipeline_call(fimc, set_stream,
+ &fimc->pipeline, 1);
+ return;
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_lite *fimc = vb2_get_drv_priv(vq);
+ mutex_lock(&fimc->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_lite *fimc = vb2_get_drv_priv(vq);
+ mutex_unlock(&fimc->lock);
+}
+
+static const struct vb2_ops fimc_lite_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+static void fimc_lite_clear_event_counters(struct fimc_lite *fimc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ memset(&fimc->events, 0, sizeof(fimc->events));
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static int fimc_lite_open(struct file *file)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ set_bit(ST_FLITE_IN_USE, &fimc->state);
+ ret = pm_runtime_get_sync(&fimc->pdev->dev);
+ if (ret < 0)
+ goto done;
+
+ ret = v4l2_fh_open(file);
+ if (ret < 0)
+ goto done;
+
+ if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) {
+ ret = fimc_pipeline_call(fimc, open, &fimc->pipeline,
+ &fimc->vfd.entity, true);
+ if (ret < 0) {
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ fimc->ref_count--;
+ v4l2_fh_release(file);
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+ }
+
+ fimc_lite_clear_event_counters(fimc);
+ }
+done:
+ mutex_unlock(&fimc->lock);
+ return ret;
+}
+
+static int fimc_lite_close(struct file *file)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) {
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+ fimc_lite_stop_capture(fimc, false);
+ fimc_pipeline_call(fimc, close, &fimc->pipeline);
+ clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
+ }
+
+ pm_runtime_put(&fimc->pdev->dev);
+
+ if (fimc->ref_count == 0)
+ vb2_queue_release(&fimc->vb_queue);
+
+ ret = v4l2_fh_release(file);
+
+ mutex_unlock(&fimc->lock);
+ return ret;
+}
+
+static unsigned int fimc_lite_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return POLL_ERR;
+
+ ret = vb2_poll(&fimc->vb_queue, file, wait);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = vb2_mmap(&fimc->vb_queue, vma);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations fimc_lite_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_lite_open,
+ .release = fimc_lite_close,
+ .poll = fimc_lite_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_lite_mmap,
+};
+
+/*
+ * Format and crop negotiation helpers
+ */
+
+static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ struct flite_variant *variant = fimc->variant;
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(fourcc, code, 0);
+ if (WARN_ON(!fmt))
+ return NULL;
+
+ if (code)
+ *code = fmt->mbus_code;
+ if (fourcc)
+ *fourcc = fmt->fourcc;
+
+ if (pad == FLITE_SD_PAD_SINK) {
+ v4l_bound_align_image(width, 8, variant->max_width,
+ ffs(variant->out_width_align) - 1,
+ height, 0, variant->max_height, 0, 0);
+ } else {
+ v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width,
+ ffs(variant->out_width_align) - 1,
+ height, 0, fimc->inp_frame.rect.height,
+ 0, 0);
+ }
+
+ v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n",
+ code ? *code : 0, *width, *height);
+
+ return fmt;
+}
+
+static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
+{
+ struct flite_frame *frame = &fimc->inp_frame;
+
+ v4l_bound_align_image(&r->width, 0, frame->f_width, 0,
+ &r->height, 0, frame->f_height, 0, 0);
+
+ /* Adjust left/top if cropping rectangle got out of bounds */
+ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
+ r->left = round_down(r->left, fimc->variant->win_hor_offs_align);
+ r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
+
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d",
+ r->left, r->top, r->width, r->height,
+ frame->f_width, frame->f_height);
+}
+
+static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
+{
+ struct flite_frame *frame = &fimc->out_frame;
+ struct v4l2_rect *crop_rect = &fimc->inp_frame.rect;
+
+ /* Scaling is not supported so we enforce compose rectangle size
+ same as size of the sink crop rectangle. */
+ r->width = crop_rect->width;
+ r->height = crop_rect->height;
+
+ /* Adjust left/top if the composing rectangle got out of bounds */
+ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
+ r->left = round_down(r->left, fimc->variant->out_hor_offs_align);
+ r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
+
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d",
+ r->left, r->top, r->width, r->height,
+ frame->f_width, frame->f_height);
+}
+
+/*
+ * Video node ioctl operations
+ */
+static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver));
+ cap->bus_info[0] = 0;
+ cap->card[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(fimc_lite_formats))
+ return -EINVAL;
+
+ fmt = &fimc_lite_formats[f->index];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int fimc_lite_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = fimc->fmt;
+
+ plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8;
+ plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height;
+
+ pixm->num_planes = fmt->memplanes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->width = frame->f_width;
+ pixm->height = frame->f_height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static int fimc_lite_try_fmt(struct fimc_lite *fimc,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct fimc_fmt **ffmt)
+{
+ struct flite_variant *variant = fimc->variant;
+ u32 bpl = pixm->plane_fmt[0].bytesperline;
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0);
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+ if (ffmt)
+ *ffmt = fmt;
+ v4l_bound_align_image(&pixm->width, 8, variant->max_width,
+ ffs(variant->out_width_align) - 1,
+ &pixm->height, 0, variant->max_height, 0, 0);
+
+ if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width))
+ pixm->plane_fmt[0].bytesperline = (pixm->width *
+ fmt->depth[0]) / 8;
+
+ if (pixm->plane_fmt[0].sizeimage == 0)
+ pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height *
+ fmt->depth[0]) / 8;
+ pixm->num_planes = fmt->memplanes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static int fimc_lite_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL);
+}
+
+static int fimc_lite_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = NULL;
+ int ret;
+
+ if (vb2_is_busy(&fimc->vb_queue))
+ return -EBUSY;
+
+ ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt);
+ if (ret < 0)
+ return ret;
+
+ fimc->fmt = fmt;
+ fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8,
+ pixm->plane_fmt[0].sizeimage);
+ frame->f_width = pixm->width;
+ frame->f_height = pixm->height;
+
+ return 0;
+}
+
+static int fimc_pipeline_validate(struct fimc_lite *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->subdev;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == &fimc->subdev) {
+ struct flite_frame *ff = &fimc->out_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = fimc->fmt->mbus_code;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL,
+ &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+static int fimc_lite_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret;
+
+ if (fimc_lite_active(fimc))
+ return -EBUSY;
+
+ ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
+ if (ret < 0)
+ return ret;
+
+ ret = fimc_pipeline_validate(fimc);
+ if (ret) {
+ media_entity_pipeline_stop(&sensor->entity);
+ return ret;
+ }
+
+ return vb2_streamon(&fimc->vb_queue, type);
+}
+
+static int fimc_lite_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ int ret;
+
+ ret = vb2_streamoff(&fimc->vb_queue, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
+}
+
+static int fimc_lite_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count);
+ ret = vb2_reqbufs(&fimc->vb_queue, reqbufs);
+ if (!ret)
+ fimc->reqbufs_count = reqbufs->count;
+
+ return ret;
+}
+
+static int fimc_lite_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_querybuf(&fimc->vb_queue, buf);
+}
+
+static int fimc_lite_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vb_queue, buf);
+}
+
+static int fimc_lite_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int fimc_lite_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_create_bufs(&fimc->vb_queue, create);
+}
+
+static int fimc_lite_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_prepare_buf(&fimc->vb_queue, b);
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_lite_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *f = &fimc->out_frame;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = f->f_width;
+ sel->r.height = f->f_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = f->rect;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int fimc_lite_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *f = &fimc->out_frame;
+ struct v4l2_rect rect = sel->r;
+ unsigned long flags;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ sel->target != V4L2_SEL_TGT_COMPOSE)
+ return -EINVAL;
+
+ fimc_lite_try_compose(fimc, &rect);
+
+ if ((sel->flags & V4L2_SEL_FLAG_LE) &&
+ !enclosed_rectangle(&rect, &sel->r))
+ return -ERANGE;
+
+ if ((sel->flags & V4L2_SEL_FLAG_GE) &&
+ !enclosed_rectangle(&sel->r, &rect))
+ return -ERANGE;
+
+ sel->r = rect;
+ spin_lock_irqsave(&fimc->slock, flags);
+ f->rect = rect;
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
+ .vidioc_querycap = fimc_vidioc_querycap_capture,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane,
+ .vidioc_g_selection = fimc_lite_g_selection,
+ .vidioc_s_selection = fimc_lite_s_selection,
+ .vidioc_reqbufs = fimc_lite_reqbufs,
+ .vidioc_querybuf = fimc_lite_querybuf,
+ .vidioc_prepare_buf = fimc_lite_prepare_buf,
+ .vidioc_create_bufs = fimc_lite_create_bufs,
+ .vidioc_qbuf = fimc_lite_qbuf,
+ .vidioc_dqbuf = fimc_lite_dqbuf,
+ .vidioc_streamon = fimc_lite_streamon,
+ .vidioc_streamoff = fimc_lite_streamoff,
+};
+
+/* Capture subdev media entity operations */
+static int fimc_lite_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ unsigned int remote_ent_type = media_entity_type(remote->entity);
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
+ __func__, local->entity->name, remote->entity->name,
+ flags, fimc->source_subdev_grp_id);
+
+ switch (local->index) {
+ case FIMC_SD_PAD_SINK:
+ if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->source_subdev_grp_id != 0)
+ return -EBUSY;
+ fimc->source_subdev_grp_id = sd->grp_id;
+ return 0;
+ }
+
+ fimc->source_subdev_grp_id = 0;
+ break;
+
+ case FIMC_SD_PAD_SOURCE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ fimc->out_path = FIMC_IO_NONE;
+ return 0;
+ }
+ if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
+ fimc->out_path = FIMC_IO_ISP;
+ else
+ fimc->out_path = FIMC_IO_DMA;
+ break;
+
+ default:
+ v4l2_err(sd, "Invalid pad index\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations fimc_lite_subdev_media_ops = {
+ .link_setup = fimc_lite_link_setup,
+};
+
+static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(NULL, NULL, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct flite_frame *f = &fimc->out_frame;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ mutex_lock(&fimc->lock);
+ mf->code = fimc->fmt->mbus_code;
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ /* full camera input frame size */
+ mf->width = f->f_width;
+ mf->height = f->f_height;
+ } else {
+ /* crop size */
+ mf->width = f->rect.width;
+ mf->height = f->rect.height;
+ }
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct flite_frame *sink = &fimc->inp_frame;
+ struct flite_frame *source = &fimc->out_frame;
+ const struct fimc_fmt *ffmt;
+
+ v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mutex_lock(&fimc->lock);
+
+ if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) ||
+ (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) {
+ mutex_unlock(&fimc->lock);
+ return -EBUSY;
+ }
+
+ ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ mutex_unlock(&fimc->lock);
+ return 0;
+ }
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ sink->f_width = mf->width;
+ sink->f_height = mf->height;
+ fimc->fmt = ffmt;
+ /* Set sink crop rectangle */
+ sink->rect.width = mf->width;
+ sink->rect.height = mf->height;
+ sink->rect.left = 0;
+ sink->rect.top = 0;
+ /* Reset source format and crop rectangle */
+ source->rect = sink->rect;
+ source->f_width = mf->width;
+ source->f_height = mf->height;
+ } else {
+ /* Allow changing format only on sink pad */
+ mf->code = fimc->fmt->mbus_code;
+ mf->width = sink->rect.width;
+ mf->height = sink->rect.height;
+ }
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct flite_frame *f = &fimc->inp_frame;
+
+ if ((sel->target != V4L2_SEL_TGT_CROP &&
+ sel->target != V4L2_SEL_TGT_CROP_BOUNDS) ||
+ sel->pad != FLITE_SD_PAD_SINK)
+ return -EINVAL;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+ return 0;
+ }
+
+ mutex_lock(&fimc->lock);
+ if (sel->target == V4L2_SEL_TGT_CROP) {
+ sel->r = f->rect;
+ } else {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = f->f_width;
+ sel->r.height = f->f_height;
+ }
+ mutex_unlock(&fimc->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ __func__, f->rect.left, f->rect.top, f->rect.width,
+ f->rect.height, f->f_width, f->f_height);
+
+ return 0;
+}
+
+static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct flite_frame *f = &fimc->inp_frame;
+ int ret = 0;
+
+ if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&fimc->lock);
+ fimc_lite_try_crop(fimc, &sel->r);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
+ } else {
+ unsigned long flags;
+ spin_lock_irqsave(&fimc->slock, flags);
+ f->rect = sel->r;
+ /* Same crop rectangle on the source pad */
+ fimc->out_frame.rect = sel->r;
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ }
+ mutex_unlock(&fimc->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ __func__, f->rect.left, f->rect.top, f->rect.width,
+ f->rect.height, f->f_width, f->f_height);
+
+ return ret;
+}
+
+static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc->out_path == FIMC_IO_DMA)
+ return -ENOIOCTLCMD;
+
+ /* TODO: */
+
+ return 0;
+}
+
+static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc->out_path == FIMC_IO_DMA)
+ return -ENOIOCTLCMD;
+
+ /* TODO: */
+
+ return 0;
+}
+
+static int fimc_lite_log_status(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ flite_hw_dump_regs(fimc, __func__);
+ return 0;
+}
+
+static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct vb2_queue *q = &fimc->vb_queue;
+ struct video_device *vfd = &fimc->vfd;
+ int ret;
+
+ memset(vfd, 0, sizeof(*vfd));
+
+ fimc->fmt = &fimc_lite_formats[0];
+ fimc->out_path = FIMC_IO_DMA;
+
+ snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
+ fimc->index);
+
+ vfd->fops = &fimc_lite_fops;
+ vfd->ioctl_ops = &fimc_lite_ioctl_ops;
+ vfd->v4l2_dev = sd->v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &fimc->lock;
+ fimc->ref_count = 0;
+ fimc->reqbufs_count = 0;
+
+ INIT_LIST_HEAD(&fimc->pending_buf_q);
+ INIT_LIST_HEAD(&fimc->active_buf_q);
+
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &fimc_lite_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct flite_buffer);
+ q->drv_priv = fimc;
+
+ vb2_queue_init(q);
+
+ fimc->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0);
+ if (ret < 0)
+ return ret;
+
+ video_set_drvdata(vfd, fimc);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ media_entity_cleanup(&vfd->entity);
+ return ret;
+ }
+
+ v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+}
+
+static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc == NULL)
+ return;
+
+ if (video_is_registered(&fimc->vfd)) {
+ video_unregister_device(&fimc->vfd);
+ media_entity_cleanup(&fimc->vfd.entity);
+ }
+}
+
+static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = {
+ .registered = fimc_lite_subdev_registered,
+ .unregistered = fimc_lite_subdev_unregistered,
+};
+
+static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = {
+ .enum_mbus_code = fimc_lite_subdev_enum_mbus_code,
+ .get_selection = fimc_lite_subdev_get_selection,
+ .set_selection = fimc_lite_subdev_set_selection,
+ .get_fmt = fimc_lite_subdev_get_fmt,
+ .set_fmt = fimc_lite_subdev_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = {
+ .s_stream = fimc_lite_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops fimc_lite_core_ops = {
+ .s_power = fimc_lite_subdev_s_power,
+ .log_status = fimc_lite_log_status,
+};
+
+static struct v4l2_subdev_ops fimc_lite_subdev_ops = {
+ .core = &fimc_lite_core_ops,
+ .video = &fimc_lite_subdev_video_ops,
+ .pad = &fimc_lite_subdev_pad_ops,
+};
+
+static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite,
+ ctrl_handler);
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = {
+ .s_ctrl = fimc_lite_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config fimc_lite_ctrl = {
+ .ops = &fimc_lite_ctrl_ops,
+ .id = V4L2_CTRL_CLASS_USER | 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Test Pattern 640x480",
+};
+
+static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
+{
+ struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler;
+ struct v4l2_subdev *sd = &fimc->subdev;
+ int ret;
+
+ v4l2_subdev_init(sd, &fimc_lite_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);
+
+ fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->subdev_pads, 0);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(handler, 1);
+ fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl,
+ NULL);
+ if (handler->error) {
+ media_entity_cleanup(&sd->entity);
+ return handler->error;
+ }
+
+ sd->ctrl_handler = handler;
+ sd->internal_ops = &fimc_lite_subdev_internal_ops;
+ sd->entity.ops = &fimc_lite_subdev_media_ops;
+ v4l2_set_subdevdata(sd, fimc);
+
+ return 0;
+}
+
+static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&fimc->ctrl_handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
+
+static void fimc_lite_clk_put(struct fimc_lite *fimc)
+{
+ if (IS_ERR_OR_NULL(fimc->clock))
+ return;
+
+ clk_unprepare(fimc->clock);
+ clk_put(fimc->clock);
+ fimc->clock = NULL;
+}
+
+static int fimc_lite_clk_get(struct fimc_lite *fimc)
+{
+ int ret;
+
+ fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME);
+ if (IS_ERR(fimc->clock))
+ return PTR_ERR(fimc->clock);
+
+ ret = clk_prepare(fimc->clock);
+ if (ret < 0) {
+ clk_put(fimc->clock);
+ fimc->clock = NULL;
+ }
+ return ret;
+}
+
+static int __devinit fimc_lite_probe(struct platform_device *pdev)
+{
+ struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev);
+ struct fimc_lite *fimc;
+ struct resource *res;
+ int ret;
+
+ fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
+ if (!fimc)
+ return -ENOMEM;
+
+ fimc->index = pdev->id;
+ fimc->variant = drv_data->variant[fimc->index];
+ fimc->pdev = pdev;
+
+ init_waitqueue_head(&fimc->irq_queue);
+ spin_lock_init(&fimc->slock);
+ mutex_init(&fimc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (fimc->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = fimc_lite_clk_get(fimc);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler,
+ 0, dev_name(&pdev->dev), fimc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ /* The video node will be created within the subdev's registered() op */
+ ret = fimc_lite_create_capture_subdev(fimc);
+ if (ret)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, fimc);
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_sd;
+
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(fimc->alloc_ctx)) {
+ ret = PTR_ERR(fimc->alloc_ctx);
+ goto err_pm;
+ }
+ pm_runtime_put(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n",
+ fimc->index);
+ return 0;
+err_pm:
+ pm_runtime_put(&pdev->dev);
+err_sd:
+ fimc_lite_unregister_capture_subdev(fimc);
+err_clk:
+ fimc_lite_clk_put(fimc);
+ return ret;
+}
+
+static int fimc_lite_runtime_resume(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+
+ clk_enable(fimc->clock);
+ return 0;
+}
+
+static int fimc_lite_runtime_suspend(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+
+ clk_disable(fimc->clock);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_lite_resume(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ struct flite_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
+ !test_bit(ST_FLITE_IN_USE, &fimc->state)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ flite_hw_reset(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->active_buf_q);
+ fimc_pipeline_call(fimc, open, &fimc->pipeline,
+ &fimc->vfd.entity, false);
+ fimc_lite_hw_init(fimc);
+ clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < fimc->reqbufs_count; i++) {
+ if (list_empty(&fimc->pending_buf_q))
+ break;
+ buf = fimc_lite_pending_queue_pop(fimc);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+}
+
+static int fimc_lite_suspend(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state);
+ int ret;
+
+ if (test_and_set_bit(ST_LPM, &fimc->state))
+ return 0;
+
+ ret = fimc_lite_stop_capture(fimc, suspend);
+ if (ret < 0 || !fimc_lite_active(fimc))
+ return ret;
+
+ return fimc_pipeline_call(fimc, close, &fimc->pipeline);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int __devexit fimc_lite_remove(struct platform_device *pdev)
+{
+ struct fimc_lite *fimc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ fimc_lite_unregister_capture_subdev(fimc);
+ vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ fimc_lite_clk_put(fimc);
+
+ dev_info(dev, "Driver unloaded\n");
+ return 0;
+}
+
+static struct flite_variant fimc_lite0_variant_exynos4 = {
+ .max_width = 8192,
+ .max_height = 8192,
+ .out_width_align = 8,
+ .win_hor_offs_align = 2,
+ .out_hor_offs_align = 8,
+};
+
+/* EXYNOS4212, EXYNOS4412 */
+static struct flite_drvdata fimc_lite_drvdata_exynos4 = {
+ .variant = {
+ [0] = &fimc_lite0_variant_exynos4,
+ [1] = &fimc_lite0_variant_exynos4,
+ },
+};
+
+static struct platform_device_id fimc_lite_driver_ids[] = {
+ {
+ .name = "exynos-fimc-lite",
+ .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids);
+
+static const struct dev_pm_ops fimc_lite_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume)
+ SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver fimc_lite_driver = {
+ .probe = fimc_lite_probe,
+ .remove = __devexit_p(fimc_lite_remove),
+ .id_table = fimc_lite_driver_ids,
+ .driver = {
+ .name = FIMC_LITE_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_lite_pm_ops,
+ }
+};
+module_platform_driver(fimc_lite_driver);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME);
diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.h b/drivers/media/platform/s5p-fimc/fimc-lite.h
new file mode 100644
index 000000000000..3081db35c5b0
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-lite.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef FIMC_LITE_H_
+#define FIMC_LITE_H_
+
+#include <linux/sizes.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-core.h"
+
+#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
+#define FLITE_CLK_NAME "flite"
+#define FIMC_LITE_MAX_DEVS 2
+#define FLITE_REQ_BUFS_MIN 2
+
+/* Bit index definitions for struct fimc_lite::state */
+enum {
+ ST_FLITE_LPM,
+ ST_FLITE_PENDING,
+ ST_FLITE_RUN,
+ ST_FLITE_STREAM,
+ ST_FLITE_SUSPENDED,
+ ST_FLITE_OFF,
+ ST_FLITE_IN_USE,
+ ST_FLITE_CONFIG,
+ ST_SENSOR_STREAM,
+};
+
+#define FLITE_SD_PAD_SINK 0
+#define FLITE_SD_PAD_SOURCE 1
+#define FLITE_SD_PADS_NUM 2
+
+struct flite_variant {
+ unsigned short max_width;
+ unsigned short max_height;
+ unsigned short out_width_align;
+ unsigned short win_hor_offs_align;
+ unsigned short out_hor_offs_align;
+};
+
+struct flite_drvdata {
+ struct flite_variant *variant[FIMC_LITE_MAX_DEVS];
+};
+
+#define fimc_lite_get_drvdata(_pdev) \
+ ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data)
+
+struct fimc_lite_events {
+ unsigned int data_overflow;
+};
+
+#define FLITE_MAX_PLANES 1
+
+/**
+ * struct flite_frame - source/target frame properties
+ * @f_width: full pixel width
+ * @f_height: full pixel height
+ * @rect: crop/composition rectangle
+ */
+struct flite_frame {
+ u16 f_width;
+ u16 f_height;
+ struct v4l2_rect rect;
+};
+
+/**
+ * struct flite_buffer - video buffer structure
+ * @vb: vb2 buffer
+ * @list: list head for the buffers queue
+ * @paddr: precalculated physical address
+ */
+struct flite_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+ dma_addr_t paddr;
+};
+
+/**
+ * struct fimc_lite - fimc lite structure
+ * @pdev: pointer to FIMC-LITE platform device
+ * @variant: variant information for this IP
+ * @v4l2_dev: pointer to top the level v4l2_device
+ * @vfd: video device node
+ * @fh: v4l2 file handle
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @subdev: FIMC-LITE subdev
+ * @vd_pad: media (sink) pad for the capture video node
+ * @subdev_pads: the subdev media pads
+ * @ctrl_handler: v4l2 control handler
+ * @test_pattern: test pattern controls
+ * @index: FIMC-LITE platform device index
+ * @pipeline: video capture pipeline data structure
+ * @pipeline_ops: media pipeline ops for the video node driver
+ * @slock: spinlock protecting this data structure and the hw registers
+ * @lock: mutex serializing video device and the subdev operations
+ * @clock: FIMC-LITE gate clock
+ * @regs: memory mapped io registers
+ * @irq_queue: interrupt handler waitqueue
+ * @fmt: pointer to color format description structure
+ * @payload: image size in bytes (w x h x bpp)
+ * @inp_frame: camera input frame structure
+ * @out_frame: DMA output frame structure
+ * @out_path: output data path (DMA or FIFO)
+ * @source_subdev_grp_id: source subdev group id
+ * @state: driver state flags
+ * @pending_buf_q: pending buffers queue head
+ * @active_buf_q: the queue head of buffers scheduled in hardware
+ * @vb_queue: vb2 buffers queue
+ * @active_buf_count: number of video buffers scheduled in hardware
+ * @frame_count: the captured frames counter
+ * @reqbufs_count: the number of buffers requested with REQBUFS ioctl
+ * @ref_count: driver's private reference counter
+ */
+struct fimc_lite {
+ struct platform_device *pdev;
+ struct flite_variant *variant;
+ struct v4l2_device *v4l2_dev;
+ struct video_device vfd;
+ struct v4l2_fh fh;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_subdev subdev;
+ struct media_pad vd_pad;
+ struct media_pad subdev_pads[FLITE_SD_PADS_NUM];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *test_pattern;
+ u32 index;
+ struct fimc_pipeline pipeline;
+ const struct fimc_pipeline_ops *pipeline_ops;
+
+ struct mutex lock;
+ spinlock_t slock;
+
+ struct clk *clock;
+ void __iomem *regs;
+ wait_queue_head_t irq_queue;
+
+ const struct fimc_fmt *fmt;
+ unsigned long payload[FLITE_MAX_PLANES];
+ struct flite_frame inp_frame;
+ struct flite_frame out_frame;
+ enum fimc_datapath out_path;
+ unsigned int source_subdev_grp_id;
+
+ unsigned long state;
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ struct vb2_queue vb_queue;
+ unsigned int frame_count;
+ unsigned int reqbufs_count;
+ int ref_count;
+
+ struct fimc_lite_events events;
+};
+
+static inline bool fimc_lite_active(struct fimc_lite *fimc)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ ret = fimc->state & (1 << ST_FLITE_RUN) ||
+ fimc->state & (1 << ST_FLITE_PENDING);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+static inline void fimc_lite_active_queue_add(struct fimc_lite *dev,
+ struct flite_buffer *buf)
+{
+ list_add_tail(&buf->list, &dev->active_buf_q);
+}
+
+static inline struct flite_buffer *fimc_lite_active_queue_pop(
+ struct fimc_lite *dev)
+{
+ struct flite_buffer *buf = list_entry(dev->active_buf_q.next,
+ struct flite_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev,
+ struct flite_buffer *buf)
+{
+ list_add_tail(&buf->list, &dev->pending_buf_q);
+}
+
+static inline struct flite_buffer *fimc_lite_pending_queue_pop(
+ struct fimc_lite *dev)
+{
+ struct flite_buffer *buf = list_entry(dev->pending_buf_q.next,
+ struct flite_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+#endif /* FIMC_LITE_H_ */
diff --git a/drivers/media/platform/s5p-fimc/fimc-m2m.c b/drivers/media/platform/s5p-fimc/fimc-m2m.c
new file mode 100644
index 000000000000..6b71d953fd15
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-m2m.c
@@ -0,0 +1,839 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-core.h"
+#include "fimc-reg.h"
+#include "fimc-mdevice.h"
+
+
+static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
+{
+ if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return FMT_FLAGS_M2M_IN;
+ else
+ return FMT_FLAGS_M2M_OUT;
+}
+
+void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
+{
+ struct vb2_buffer *src_vb, *dst_vb;
+
+ if (!ctx || !ctx->m2m_ctx)
+ return;
+
+ 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, vb_state);
+ v4l2_m2m_buf_done(dst_vb, vb_state);
+ v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
+ }
+}
+
+/* Complete the transaction which has been scheduled for execution. */
+static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (!fimc_m2m_pending(fimc))
+ return 0;
+
+ fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
+
+ ret = wait_event_timeout(fimc->irq_queue,
+ !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = fimc_m2m_shutdown(ctx);
+ if (ret == -ETIMEDOUT)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put(&ctx->fimc_dev->pdev->dev);
+ return 0;
+}
+
+static void fimc_device_run(void *priv)
+{
+ struct vb2_buffer *vb = NULL;
+ struct fimc_ctx *ctx = priv;
+ struct fimc_frame *sf, *df;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+ u32 ret;
+
+ if (WARN(!ctx, "Null context\n"))
+ return;
+
+ fimc = ctx->fimc_dev;
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ set_bit(ST_M2M_PEND, &fimc->state);
+ sf = &ctx->s_frame;
+ df = &ctx->d_frame;
+
+ if (ctx->state & FIMC_PARAMS) {
+ /* Prepare the DMA offsets for scaler */
+ fimc_prepare_dma_offset(ctx, sf);
+ fimc_prepare_dma_offset(ctx, df);
+ }
+
+ vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr);
+ if (ret)
+ goto dma_unlock;
+
+ vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, df, &df->paddr);
+ if (ret)
+ goto dma_unlock;
+
+ /* Reconfigure hardware if the context has changed. */
+ if (fimc->m2m.ctx != ctx) {
+ ctx->state |= FIMC_PARAMS;
+ fimc->m2m.ctx = ctx;
+ }
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_set_yuv_order(ctx);
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_in_dma(ctx);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret)
+ goto dma_unlock;
+ 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_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ fimc_hw_set_output_path(ctx);
+ }
+ fimc_hw_set_input_addr(fimc, &sf->paddr);
+ fimc_hw_set_output_addr(fimc, &df->paddr, -1);
+
+ fimc_activate_capture(ctx);
+ ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
+ FIMC_SRC_FMT | FIMC_DST_FMT);
+ fimc_hw_activate_input_dma(fimc, true);
+
+dma_unlock:
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+ fimc_m2m_shutdown(priv);
+}
+
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ struct fimc_frame *f;
+ int i;
+
+ 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)
+ return -EINVAL;
+
+ *num_planes = f->fmt->memplanes;
+ for (i = 0; i < f->fmt->memplanes; i++) {
+ sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+ return 0;
+}
+
+static int fimc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_frame *frame;
+ int i;
+
+ frame = ctx_get_frame(ctx, vb->vb2_queue->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ 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 vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+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_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,
+ .start_streaming = start_streaming,
+};
+
+/*
+ * V4L2 ioctl handlers
+ */
+static int fimc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ return 0;
+}
+
+static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
+
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ return fimc_fill_format(frame, f);
+}
+
+static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_variant *variant = fimc->variant;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_fmt *fmt;
+ u32 max_w, mod_x, mod_y;
+
+ if (!IS_M2M(f->type))
+ return -EINVAL;
+
+ dbg("w: %d, h: %d", pix->width, pix->height);
+
+ fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (WARN(fmt == NULL, "Pixel format lookup failed"))
+ return -EINVAL;
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ max_w = variant->pix_limit->scaler_dis_w;
+ mod_x = ffs(variant->min_inp_pixsize) - 1;
+ } else {
+ max_w = variant->pix_limit->out_rot_dis_w;
+ mod_x = ffs(variant->min_out_pixsize) - 1;
+ }
+
+ if (tiled_fmt(fmt)) {
+ mod_x = 6; /* 64 x 32 pixels tile */
+ mod_y = 5;
+ } else {
+ if (variant->min_vsize_align == 1)
+ mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
+ else
+ mod_y = ffs(variant->min_vsize_align) - 1;
+ }
+
+ v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
+ &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
+
+ fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
+ return 0;
+}
+
+static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return fimc_try_fmt_mplane(ctx, f);
+}
+
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct vb2_queue *vq;
+ struct fimc_frame *frame;
+ struct v4l2_pix_format_mplane *pix;
+ int i, ret = 0;
+
+ ret = fimc_try_fmt_mplane(ctx, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
+ return -EBUSY;
+ }
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ frame = &ctx->s_frame;
+ else
+ frame = &ctx->d_frame;
+
+ pix = &f->fmt.pix_mp;
+ frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (!frame->fmt)
+ return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) / 8;
+ }
+
+ fimc_fill_frame(frame, f);
+
+ ctx->scaler.enabled = 1;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
+ else
+ fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
+
+ dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ /* The source and target color format need to be set */
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
+ return -EINVAL;
+ } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = frame->o_width;
+ cr->bounds.height = frame->o_height;
+ cr->defrect = cr->bounds;
+
+ return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c.left = frame->offs_h;
+ cr->c.top = frame->offs_v;
+ cr->c.width = frame->width;
+ cr->c.height = frame->height;
+
+ return 0;
+}
+
+static int fimc_m2m_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, depth = 0;
+ int i;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(&fimc->m2m.vfd,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ f = &ctx->s_frame;
+ else
+ return -EINVAL;
+
+ min_size = (f == &ctx->s_frame) ?
+ fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+ /* Get pixel alignment constraints. */
+ if (fimc->variant->min_vsize_align == 1)
+ halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+ else
+ halign = ffs(fimc->variant->min_vsize_align) - 1;
+
+ 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(depth, 8)));
+
+ /* adjust left/top if cropping rectangle is out of bounds */
+ if (cr->c.left + cr->c.width > f->o_width)
+ cr->c.left = f->o_width - cr->c.width;
+ if (cr->c.top + cr->c.height > f->o_height)
+ cr->c.top = f->o_height - cr->c.height;
+
+ cr->c.left = round_down(cr->c.left, min_size);
+ cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
+
+ dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+ cr->c.left, cr->c.top, cr->c.width, cr->c.height,
+ f->f_width, f->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, const struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_frame *f;
+ int ret;
+
+ ret = fimc_m2m_try_crop(ctx, cr);
+ if (ret)
+ return ret;
+
+ f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ /* Check to see if scaling ratio is within supported range */
+ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+ cr->c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
+ } else {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr->c.width,
+ cr->c.height, ctx->rotation);
+ }
+ if (ret) {
+ v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
+ }
+ }
+
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+
+ fimc_ctx_state_set(FIMC_PARAMS, ctx);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+ .vidioc_querycap = fimc_m2m_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
+ .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,
+ .vidioc_qbuf = fimc_m2m_qbuf,
+ .vidioc_dqbuf = fimc_m2m_dqbuf,
+ .vidioc_streamon = fimc_m2m_streamon,
+ .vidioc_streamoff = fimc_m2m_streamoff,
+ .vidioc_g_crop = fimc_m2m_g_crop,
+ .vidioc_s_crop = fimc_m2m_s_crop,
+ .vidioc_cropcap = fimc_m2m_cropcap
+
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct fimc_ctx *ctx = priv;
+ int ret;
+
+ 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);
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ 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;
+ int ret = -EBUSY;
+
+ dbg("pid: %d, state: 0x%lx, refcnt: %d",
+ task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+ /*
+ * Return if the corresponding video capture node
+ * is already opened.
+ */
+ if (fimc->vid_cap.refcnt > 0)
+ goto unlock;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd);
+ ctx->fimc_dev = fimc;
+
+ /* Default color format */
+ ctx->s_frame.fmt = fimc_get_format(0);
+ ctx->d_frame.fmt = fimc_get_format(0);
+
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrls.handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ /* Setup the device context for memory-to-memory mode */
+ ctx->state = FIMC_CTX_M2M;
+ ctx->flags = 0;
+ ctx->in_path = FIMC_IO_DMA;
+ ctx->out_path = FIMC_IO_DMA;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_c;
+ }
+
+ if (fimc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_RUN, &fimc->state);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+
+error_c:
+ fimc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+unlock:
+ mutex_unlock(&fimc->lock);
+ return ret;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ dbg("pid: %d, state: 0x%lx, refcnt= %d",
+ task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ fimc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_RUN, &fimc->state);
+ kfree(ctx);
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&fimc->lock))
+ return -ERESTARTSYS;
+
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&fimc->lock);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_m2m_open,
+ .release = fimc_m2m_release,
+ .poll = fimc_m2m_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = fimc_device_run,
+ .job_abort = fimc_job_abort,
+};
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vfd = &fimc->m2m.vfd;
+ int ret;
+
+ fimc->v4l2_dev = v4l2_dev;
+
+ memset(vfd, 0, sizeof(*vfd));
+ vfd->fops = &fimc_m2m_fops;
+ vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release_empty;
+ vfd->lock = &fimc->lock;
+ vfd->vfl_dir = VFL_DIR_M2M;
+
+ snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
+ video_set_drvdata(vfd, fimc);
+
+ fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(fimc->m2m.m2m_dev)) {
+ v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+ return PTR_ERR(fimc->m2m.m2m_dev);
+ }
+
+ ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ if (ret)
+ goto err_me;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vd;
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+
+err_vd:
+ media_entity_cleanup(&vfd->entity);
+err_me:
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+ return ret;
+}
+
+void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+ if (!fimc)
+ return;
+
+ if (fimc->m2m.m2m_dev)
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+
+ if (video_is_registered(&fimc->m2m.vfd)) {
+ video_unregister_device(&fimc->m2m.vfd);
+ media_entity_cleanup(&fimc->m2m.vfd.entity);
+ }
+}
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
new file mode 100644
index 000000000000..80ada5882f62
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c
@@ -0,0 +1,1050 @@
+/*
+ * S5P/EXYNOS4 SoC series camera host interface media device driver
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <media/v4l2-ctrls.h>
+#include <media/media-device.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-core.h"
+#include "fimc-lite.h"
+#include "fimc-mdevice.h"
+#include "mipi-csis.h"
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on);
+/**
+ * fimc_pipeline_prepare - update pipeline information with subdevice pointers
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Caller holds the graph mutex.
+ */
+static void fimc_pipeline_prepare(struct fimc_pipeline *p,
+ struct media_entity *me)
+{
+ struct media_pad *pad = &me->pads[0];
+ struct v4l2_subdev *sd;
+ int i;
+
+ for (i = 0; i < IDX_MAX; i++)
+ p->subdevs[i] = NULL;
+
+ while (1) {
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ /* source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+
+ switch (sd->grp_id) {
+ case SENSOR_GROUP_ID:
+ p->subdevs[IDX_SENSOR] = sd;
+ break;
+ case CSIS_GROUP_ID:
+ p->subdevs[IDX_CSIS] = sd;
+ break;
+ case FLITE_GROUP_ID:
+ p->subdevs[IDX_FLITE] = sd;
+ break;
+ case FIMC_GROUP_ID:
+ /* No need to control FIMC subdev through subdev ops */
+ break;
+ default:
+ pr_warn("%s: Unknown subdev grp_id: %#x\n",
+ __func__, sd->grp_id);
+ }
+ /* sink pad */
+ pad = &sd->entity.pads[0];
+ }
+}
+
+/**
+ * __subdev_set_power - change power state of a single subdev
+ * @sd: subdevice to change power state for
+ * @on: 1 to enable power or 0 to disable
+ *
+ * Return result of s_power subdev operation or -ENXIO if sd argument
+ * is NULL. Return 0 if the subdevice does not implement s_power.
+ */
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int *use_count;
+ int ret;
+
+ if (sd == NULL)
+ return -ENXIO;
+
+ use_count = &sd->entity.use_count;
+ if (on && (*use_count)++ > 0)
+ return 0;
+ else if (!on && (*use_count == 0 || --(*use_count) > 0))
+ return 0;
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+/**
+ * fimc_pipeline_s_power - change power state of all pipeline subdevs
+ * @fimc: fimc device terminating the pipeline
+ * @state: true to power on, false to power off
+ *
+ * Needs to be called with the graph mutex held.
+ */
+static int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state)
+{
+ unsigned int i;
+ int ret;
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
+ return -ENXIO;
+
+ for (i = 0; i < IDX_MAX; i++) {
+ unsigned int idx = state ? (IDX_MAX - 1) - i : i;
+
+ ret = __subdev_set_power(p->subdevs[idx], state);
+ if (ret < 0 && ret != -ENXIO)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * __fimc_pipeline_open - update the pipeline information, enable power
+ * of all pipeline subdevs and the sensor clock
+ * @me: media entity to start graph walk with
+ * @prep: true to acquire sensor (and csis) subdevs
+ *
+ * This function must be called with the graph mutex held.
+ */
+static int __fimc_pipeline_open(struct fimc_pipeline *p,
+ struct media_entity *me, bool prep)
+{
+ int ret;
+
+ if (prep)
+ fimc_pipeline_prepare(p, me);
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
+ return -EINVAL;
+
+ ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true);
+ if (ret)
+ return ret;
+
+ return fimc_pipeline_s_power(p, 1);
+}
+
+static int fimc_pipeline_open(struct fimc_pipeline *p,
+ struct media_entity *me, bool prep)
+{
+ int ret;
+
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_open(p, me, prep);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * __fimc_pipeline_close - disable the sensor clock and pipeline power
+ * @fimc: fimc device terminating the pipeline
+ *
+ * Disable power of all subdevs in the pipeline and turn off the external
+ * sensor clock.
+ * Called with the graph mutex held.
+ */
+static int __fimc_pipeline_close(struct fimc_pipeline *p)
+{
+ int ret = 0;
+
+ if (p->subdevs[IDX_SENSOR]) {
+ ret = fimc_pipeline_s_power(p, 0);
+ fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false);
+ }
+ return ret == -ENXIO ? 0 : ret;
+}
+
+static int fimc_pipeline_close(struct fimc_pipeline *p)
+{
+ struct media_entity *me;
+ int ret;
+
+ if (!p || !p->subdevs[IDX_SENSOR])
+ return -EINVAL;
+
+ me = &p->subdevs[IDX_SENSOR]->entity;
+ mutex_lock(&me->parent->graph_mutex);
+ ret = __fimc_pipeline_close(p);
+ mutex_unlock(&me->parent->graph_mutex);
+
+ return ret;
+}
+
+/**
+ * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
+ * @pipeline: video pipeline structure
+ * @on: passed as the s_stream call argument
+ */
+int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
+{
+ int i, ret;
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
+ return -ENODEV;
+
+ for (i = 0; i < IDX_MAX; i++) {
+ unsigned int idx = on ? (IDX_MAX - 1) - i : i;
+
+ ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+ }
+
+ return 0;
+
+}
+
+/* Media pipeline operations for the FIMC/FIMC-LITE video device driver */
+static const struct fimc_pipeline_ops fimc_pipeline_ops = {
+ .open = fimc_pipeline_open,
+ .close = fimc_pipeline_close,
+ .set_stream = fimc_pipeline_s_stream,
+};
+
+/*
+ * Sensor subdevice helper functions
+ */
+static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info)
+{
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd = NULL;
+
+ if (!s_info || !fmd)
+ return NULL;
+
+ adapter = i2c_get_adapter(s_info->pdata.i2c_bus_num);
+ if (!adapter) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "Failed to get I2C adapter %d, deferring probe\n",
+ s_info->pdata.i2c_bus_num);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+ sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
+ s_info->pdata.board_info, NULL);
+ if (IS_ERR_OR_NULL(sd)) {
+ i2c_put_adapter(adapter);
+ v4l2_warn(&fmd->v4l2_dev,
+ "Failed to acquire subdev %s, deferring probe\n",
+ s_info->pdata.board_info->type);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+ v4l2_set_subdev_hostdata(sd, s_info);
+ sd->grp_id = SENSOR_GROUP_ID;
+
+ v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
+ s_info->pdata.board_info->type);
+ return sd;
+}
+
+static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct i2c_adapter *adapter;
+
+ if (!client)
+ return;
+ v4l2_device_unregister_subdev(sd);
+ adapter = client->adapter;
+ i2c_unregister_device(client);
+ if (adapter)
+ i2c_put_adapter(adapter);
+}
+
+static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
+{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
+ struct fimc_dev *fd = NULL;
+ int num_clients, ret, i;
+
+ /*
+ * Runtime resume one of the FIMC entities to make sure
+ * the sclk_cam clocks are not globally disabled.
+ */
+ for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
+ if (fmd->fimc[i])
+ fd = fmd->fimc[i];
+ if (!fd)
+ return -ENXIO;
+ ret = pm_runtime_get_sync(&fd->pdev->dev);
+ if (ret < 0)
+ return ret;
+
+ WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
+ num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
+
+ fmd->num_sensors = num_clients;
+ for (i = 0; i < num_clients; i++) {
+ struct v4l2_subdev *sd;
+
+ fmd->sensor[i].pdata = pdata->isp_info[i];
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
+ if (ret)
+ break;
+ sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
+ ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
+
+ if (!IS_ERR(sd)) {
+ fmd->sensor[i].subdev = sd;
+ } else {
+ fmd->sensor[i].subdev = NULL;
+ ret = PTR_ERR(sd);
+ break;
+ }
+ if (ret)
+ break;
+ }
+ pm_runtime_put(&fd->pdev->dev);
+ return ret;
+}
+
+/*
+ * MIPI CSIS and FIMC platform devices registration.
+ */
+static int fimc_register_callback(struct device *dev, void *p)
+{
+ struct fimc_dev *fimc = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
+ struct fimc_md *fmd = p;
+ int ret = 0;
+
+ if (!fimc || !fimc->pdev)
+ return 0;
+
+ if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
+ return 0;
+
+ fimc->pipeline_ops = &fimc_pipeline_ops;
+ fmd->fimc[fimc->pdev->id] = fimc;
+ sd->grp_id = FIMC_GROUP_ID;
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
+ fimc->id, ret);
+ }
+
+ return ret;
+}
+
+static int fimc_lite_register_callback(struct device *dev, void *p)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = &fimc->subdev;
+ struct fimc_md *fmd = p;
+ int ret;
+
+ if (fimc == NULL)
+ return 0;
+
+ if (fimc->index >= FIMC_LITE_MAX_DEVS)
+ return 0;
+
+ fimc->pipeline_ops = &fimc_pipeline_ops;
+ fmd->fimc_lite[fimc->index] = fimc;
+ sd->grp_id = FLITE_GROUP_ID;
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret) {
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register FIMC-LITE.%d (%d)\n",
+ fimc->index, ret);
+ }
+ return ret;
+}
+
+static int csis_register_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct platform_device *pdev;
+ struct fimc_md *fmd = p;
+ int id, ret;
+
+ if (!sd)
+ return 0;
+ pdev = v4l2_get_subdevdata(sd);
+ if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
+ return 0;
+ v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
+
+ id = pdev->id < 0 ? 0 : pdev->id;
+ fmd->csis[id].sd = sd;
+ sd->grp_id = CSIS_GROUP_ID;
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret)
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register CSIS subdevice: %d\n", ret);
+ return ret;
+}
+
+/**
+ * fimc_md_register_platform_entities - register FIMC and CSIS media entities
+ */
+static int fimc_md_register_platform_entities(struct fimc_md *fmd)
+{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
+ struct device_driver *driver;
+ int ret, i;
+
+ driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
+ if (!driver) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "%s driver not found, deffering probe\n",
+ FIMC_MODULE_NAME);
+ return -EPROBE_DEFER;
+ }
+
+ ret = driver_for_each_device(driver, NULL, fmd,
+ fimc_register_callback);
+ if (ret)
+ return ret;
+
+ driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
+ if (driver && try_module_get(driver->owner)) {
+ ret = driver_for_each_device(driver, NULL, fmd,
+ fimc_lite_register_callback);
+ if (ret)
+ return ret;
+ module_put(driver->owner);
+ }
+ /*
+ * Check if there is any sensor on the MIPI-CSI2 bus and
+ * if not skip the s5p-csis module loading.
+ */
+ if (pdata == NULL)
+ return 0;
+ for (i = 0; i < pdata->num_clients; i++) {
+ if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
+ ret = 1;
+ break;
+ }
+ }
+ if (!ret)
+ return 0;
+
+ driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
+ if (!driver || !try_module_get(driver->owner)) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "%s driver not found, deffering probe\n",
+ CSIS_DRIVER_NAME);
+ return -EPROBE_DEFER;
+ }
+
+ return driver_for_each_device(driver, NULL, fmd,
+ csis_register_callback);
+}
+
+static void fimc_md_unregister_entities(struct fimc_md *fmd)
+{
+ int i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (fmd->fimc[i] == NULL)
+ continue;
+ v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
+ fmd->fimc[i]->pipeline_ops = NULL;
+ fmd->fimc[i] = NULL;
+ }
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ if (fmd->fimc_lite[i] == NULL)
+ continue;
+ v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
+ fmd->fimc[i]->pipeline_ops = NULL;
+ fmd->fimc_lite[i] = NULL;
+ }
+ for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ module_put(fmd->csis[i].sd->owner);
+ fmd->csis[i].sd = NULL;
+ }
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+ fimc_md_unregister_sensor(fmd->sensor[i].subdev);
+ fmd->sensor[i].subdev = NULL;
+ }
+}
+
+/**
+ * __fimc_md_create_fimc_links - create links to all FIMC entities
+ * @fmd: fimc media device
+ * @source: the source entity to create links to all fimc entities from
+ * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
+ * @pad: the source entity pad index
+ * @link_mask: bitmask of the fimc devices for which link should be enabled
+ */
+static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int link_mask)
+{
+ struct fimc_sensor_info *s_info;
+ struct media_entity *sink;
+ unsigned int flags = 0;
+ int ret, i;
+
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ /*
+ * Some FIMC variants are not fitted with camera capture
+ * interface. Skip creating a link from sensor for those.
+ */
+ if (!fmd->fimc[i]->variant->has_cam_if)
+ continue;
+
+ flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
+
+ sink = &fmd->fimc[i]->vid_cap.subdev.entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FIMC_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC capture subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+
+ if (flags == 0 || sensor == NULL)
+ continue;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!WARN_ON(s_info == NULL)) {
+ unsigned long irq_flags;
+ spin_lock_irqsave(&fmd->slock, irq_flags);
+ s_info->host = fmd->fimc[i];
+ spin_unlock_irqrestore(&fmd->slock, irq_flags);
+ }
+ }
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ if (!fmd->fimc_lite[i])
+ continue;
+
+ if (link_mask & (1 << (i + FIMC_MAX_DEVS)))
+ flags = MEDIA_LNK_FL_ENABLED;
+ else
+ flags = 0;
+
+ sink = &fmd->fimc_lite[i]->subdev.entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FLITE_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC-LITE subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+ }
+ return 0;
+}
+
+/* Create links from FIMC-LITE source pads to other entities */
+static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
+{
+ struct media_entity *source, *sink;
+ unsigned int flags = MEDIA_LNK_FL_ENABLED;
+ int i, ret;
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ struct fimc_lite *fimc = fmd->fimc_lite[i];
+ if (fimc == NULL)
+ continue;
+ source = &fimc->subdev.entity;
+ sink = &fimc->vfd.entity;
+ /* FIMC-LITE's subdev and video node */
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ /* TODO: create links to other entities */
+ }
+
+ return ret;
+}
+
+/**
+ * fimc_md_create_links - create default links between registered entities
+ *
+ * Parallel interface sensor entities are connected directly to FIMC capture
+ * entities. The sensors using MIPI CSIS bus are connected through immutable
+ * link with CSI receiver entity specified by mux_id. Any registered CSIS
+ * entity has a link to each registered FIMC capture entity. Enabled links
+ * are created by default between each subsequent registered sensor and
+ * subsequent FIMC capture entity. The number of default active links is
+ * determined by the number of available sensors or FIMC entities,
+ * whichever is less.
+ */
+static int fimc_md_create_links(struct fimc_md *fmd)
+{
+ struct v4l2_subdev *csi_sensors[2] = { NULL };
+ struct v4l2_subdev *sensor, *csis;
+ struct s5p_fimc_isp_info *pdata;
+ struct fimc_sensor_info *s_info;
+ struct media_entity *source, *sink;
+ int i, pad, fimc_id = 0, ret = 0;
+ u32 flags, link_mask = 0;
+
+ for (i = 0; i < fmd->num_sensors; i++) {
+ if (fmd->sensor[i].subdev == NULL)
+ continue;
+
+ sensor = fmd->sensor[i].subdev;
+ s_info = v4l2_get_subdev_hostdata(sensor);
+ if (!s_info)
+ continue;
+
+ source = NULL;
+ pdata = &s_info->pdata;
+
+ switch (pdata->bus_type) {
+ case FIMC_MIPI_CSI2:
+ if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
+ "Wrong CSI channel id: %d\n", pdata->mux_id))
+ return -EINVAL;
+
+ csis = fmd->csis[pdata->mux_id].sd;
+ if (WARN(csis == NULL,
+ "MIPI-CSI interface specified "
+ "but s5p-csis module is not loaded!\n"))
+ return -EINVAL;
+
+ ret = media_entity_create_link(&sensor->entity, 0,
+ &csis->entity, CSIS_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
+ sensor->entity.name, csis->entity.name);
+
+ source = NULL;
+ csi_sensors[pdata->mux_id] = sensor;
+ break;
+
+ case FIMC_ITU_601...FIMC_ITU_656:
+ source = &sensor->entity;
+ pad = 0;
+ break;
+
+ default:
+ v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
+ pdata->bus_type);
+ return -EINVAL;
+ }
+ if (source == NULL)
+ continue;
+
+ link_mask = 1 << fimc_id++;
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
+ pad, link_mask);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ source = &fmd->csis[i].sd->entity;
+ pad = CSIS_PAD_SOURCE;
+ sensor = csi_sensors[i];
+
+ link_mask = 1 << fimc_id++;
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
+ pad, link_mask);
+ }
+
+ /* Create immutable links between each FIMC's subdev and video node */
+ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+ for (i = 0; i < FIMC_MAX_DEVS; i++) {
+ if (!fmd->fimc[i])
+ continue;
+ source = &fmd->fimc[i]->vid_cap.subdev.entity;
+ sink = &fmd->fimc[i]->vid_cap.vfd.entity;
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ }
+
+ return __fimc_md_create_flite_source_links(fmd);
+}
+
+/*
+ * The peripheral sensor clock management.
+ */
+static int fimc_md_get_clocks(struct fimc_md *fmd)
+{
+ char clk_name[32];
+ struct clk *clock;
+ int i;
+
+ for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
+ snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
+ clock = clk_get(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clock)) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
+ clk_name);
+ return -ENXIO;
+ }
+ fmd->camclk[i].clock = clock;
+ }
+ return 0;
+}
+
+static void fimc_md_put_clocks(struct fimc_md *fmd)
+{
+ int i = FIMC_MAX_CAMCLKS;
+
+ while (--i >= 0) {
+ if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
+ continue;
+ clk_put(fmd->camclk[i].clock);
+ fmd->camclk[i].clock = NULL;
+ }
+}
+
+static int __fimc_md_set_camclk(struct fimc_md *fmd,
+ struct fimc_sensor_info *s_info,
+ bool on)
+{
+ struct s5p_fimc_isp_info *pdata = &s_info->pdata;
+ struct fimc_camclk_info *camclk;
+ int ret = 0;
+
+ if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
+ return -EINVAL;
+
+ camclk = &fmd->camclk[pdata->clk_id];
+
+ dbg("camclk %d, f: %lu, use_count: %d, on: %d",
+ pdata->clk_id, pdata->clk_frequency, camclk->use_count, on);
+
+ if (on) {
+ if (camclk->use_count > 0 &&
+ camclk->frequency != pdata->clk_frequency)
+ return -EINVAL;
+
+ if (camclk->use_count++ == 0) {
+ clk_set_rate(camclk->clock, pdata->clk_frequency);
+ camclk->frequency = pdata->clk_frequency;
+ ret = clk_enable(camclk->clock);
+ dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
+ clk_get_rate(camclk->clock));
+ }
+ return ret;
+ }
+
+ if (WARN_ON(camclk->use_count == 0))
+ return 0;
+
+ if (--camclk->use_count == 0) {
+ clk_disable(camclk->clock);
+ dbg("Disabled camclk %d", pdata->clk_id);
+ }
+ return ret;
+}
+
+/**
+ * fimc_md_set_camclk - peripheral sensor clock setup
+ * @sd: sensor subdev to configure sclk_cam clock for
+ * @on: 1 to enable or 0 to disable the clock
+ *
+ * There are 2 separate clock outputs available in the SoC for external
+ * image processors. These clocks are shared between all registered FIMC
+ * devices to which sensors can be attached, either directly or through
+ * the MIPI CSI receiver. The clock is allowed here to be used by
+ * multiple sensors concurrently if they use same frequency.
+ * This function should only be called when the graph mutex is held.
+ */
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
+{
+ struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
+ struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
+
+ return __fimc_md_set_camclk(fmd, s_info, on);
+}
+
+static int fimc_md_link_notify(struct media_pad *source,
+ struct media_pad *sink, u32 flags)
+{
+ struct fimc_lite *fimc_lite = NULL;
+ struct fimc_dev *fimc = NULL;
+ struct fimc_pipeline *pipeline;
+ struct v4l2_subdev *sd;
+ int ret = 0;
+
+ if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ sd = media_entity_to_v4l2_subdev(sink->entity);
+
+ switch (sd->grp_id) {
+ case FLITE_GROUP_ID:
+ fimc_lite = v4l2_get_subdevdata(sd);
+ pipeline = &fimc_lite->pipeline;
+ break;
+ case FIMC_GROUP_ID:
+ fimc = v4l2_get_subdevdata(sd);
+ pipeline = &fimc->pipeline;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_pipeline_close(pipeline);
+ pipeline->subdevs[IDX_SENSOR] = NULL;
+ pipeline->subdevs[IDX_CSIS] = NULL;
+
+ if (fimc) {
+ mutex_lock(&fimc->lock);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ mutex_unlock(&fimc->lock);
+ }
+ return ret;
+ }
+ /*
+ * Link activation. Enable power of pipeline elements only if the
+ * pipeline is already in use, i.e. its video node is opened.
+ * Recreate the controls destroyed during the link deactivation.
+ */
+ if (fimc) {
+ mutex_lock(&fimc->lock);
+ if (fimc->vid_cap.refcnt > 0) {
+ ret = __fimc_pipeline_open(pipeline,
+ source->entity, true);
+ if (!ret)
+ ret = fimc_capture_ctrls_create(fimc);
+ }
+ mutex_unlock(&fimc->lock);
+ } else {
+ mutex_lock(&fimc_lite->lock);
+ if (fimc_lite->ref_count > 0) {
+ ret = __fimc_pipeline_open(pipeline,
+ source->entity, true);
+ }
+ mutex_unlock(&fimc_lite->lock);
+ }
+ return ret ? -EPIPE : ret;
+}
+
+static ssize_t fimc_md_sysfs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (fmd->user_subdev_api)
+ return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
+
+ return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
+}
+
+static ssize_t fimc_md_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+ bool subdev_api;
+ int i;
+
+ if (!strcmp(buf, "vid-dev\n"))
+ subdev_api = false;
+ else if (!strcmp(buf, "sub-dev\n"))
+ subdev_api = true;
+ else
+ return count;
+
+ fmd->user_subdev_api = subdev_api;
+ for (i = 0; i < FIMC_MAX_DEVS; i++)
+ if (fmd->fimc[i])
+ fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
+ return count;
+}
+/*
+ * This device attribute is to select video pipeline configuration method.
+ * There are following valid values:
+ * vid-dev - for V4L2 video node API only, subdevice will be configured
+ * by the host driver.
+ * sub-dev - for media controller API, subdevs must be configured in user
+ * space before starting streaming.
+ */
+static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
+ fimc_md_sysfs_show, fimc_md_sysfs_store);
+
+static int fimc_md_probe(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev;
+ struct fimc_md *fmd;
+ int ret;
+
+ fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
+ if (!fmd)
+ return -ENOMEM;
+
+ spin_lock_init(&fmd->slock);
+ fmd->pdev = pdev;
+
+ strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
+ sizeof(fmd->media_dev.model));
+ fmd->media_dev.link_notify = fimc_md_link_notify;
+ fmd->media_dev.dev = &pdev->dev;
+
+ v4l2_dev = &fmd->v4l2_dev;
+ v4l2_dev->mdev = &fmd->media_dev;
+ v4l2_dev->notify = fimc_sensor_notify;
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
+ dev_name(&pdev->dev));
+
+ ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
+ return ret;
+ }
+ ret = media_device_register(&fmd->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
+ goto err_md;
+ }
+ ret = fimc_md_get_clocks(fmd);
+ if (ret)
+ goto err_clk;
+
+ fmd->user_subdev_api = false;
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
+ ret = fimc_md_register_platform_entities(fmd);
+ if (ret)
+ goto err_unlock;
+
+ if (pdev->dev.platform_data) {
+ ret = fimc_md_register_sensor_entities(fmd);
+ if (ret)
+ goto err_unlock;
+ }
+ ret = fimc_md_create_links(fmd);
+ if (ret)
+ goto err_unlock;
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
+ if (ret)
+ goto err_unlock;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ if (ret)
+ goto err_unlock;
+
+ platform_set_drvdata(pdev, fmd);
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return 0;
+
+err_unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk:
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ fimc_md_unregister_entities(fmd);
+err_md:
+ v4l2_device_unregister(&fmd->v4l2_dev);
+ return ret;
+}
+
+static int __devexit fimc_md_remove(struct platform_device *pdev)
+{
+ struct fimc_md *fmd = platform_get_drvdata(pdev);
+
+ if (!fmd)
+ return 0;
+ device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
+ fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
+ fimc_md_put_clocks(fmd);
+ return 0;
+}
+
+static struct platform_driver fimc_md_driver = {
+ .probe = fimc_md_probe,
+ .remove = __devexit_p(fimc_md_remove),
+ .driver = {
+ .name = "s5p-fimc-md",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init fimc_md_init(void)
+{
+ int ret;
+
+ request_module("s5p-csis");
+ ret = fimc_register_driver();
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&fimc_md_driver);
+}
+
+static void __exit fimc_md_exit(void)
+{
+ platform_driver_unregister(&fimc_md_driver);
+ fimc_unregister_driver();
+}
+
+module_init(fimc_md_init);
+module_exit(fimc_md_exit);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0.1");
diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.h b/drivers/media/platform/s5p-fimc/fimc-mdevice.h
new file mode 100644
index 000000000000..2d8d41d82620
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef FIMC_MDEVICE_H_
+#define FIMC_MDEVICE_H_
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "fimc-core.h"
+#include "fimc-lite.h"
+#include "mipi-csis.h"
+
+/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */
+#define SENSOR_GROUP_ID (1 << 8)
+#define CSIS_GROUP_ID (1 << 9)
+#define WRITEBACK_GROUP_ID (1 << 10)
+#define FIMC_GROUP_ID (1 << 11)
+#define FLITE_GROUP_ID (1 << 12)
+
+#define FIMC_MAX_SENSORS 8
+#define FIMC_MAX_CAMCLKS 2
+
+struct fimc_csis_info {
+ struct v4l2_subdev *sd;
+ int id;
+};
+
+struct fimc_camclk_info {
+ struct clk *clock;
+ int use_count;
+ unsigned long frequency;
+};
+
+/**
+ * struct fimc_sensor_info - image data source subdev information
+ * @pdata: sensor's atrributes passed as media device's platform data
+ * @subdev: image sensor v4l2 subdev
+ * @host: fimc device the sensor is currently linked to
+ *
+ * This data structure applies to image sensor and the writeback subdevs.
+ */
+struct fimc_sensor_info {
+ struct s5p_fimc_isp_info pdata;
+ struct v4l2_subdev *subdev;
+ struct fimc_dev *host;
+};
+
+/**
+ * struct fimc_md - fimc media device information
+ * @csis: MIPI CSIS subdevs data
+ * @sensor: array of registered sensor subdevs
+ * @num_sensors: actual number of registered sensors
+ * @camclk: external sensor clock information
+ * @fimc: array of registered fimc devices
+ * @media_dev: top level media device
+ * @v4l2_dev: top level v4l2_device holding up the subdevs
+ * @pdev: platform device this media device is hooked up into
+ * @user_subdev_api: true if subdevs are not configured by the host driver
+ * @slock: spinlock protecting @sensor array
+ */
+struct fimc_md {
+ struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
+ struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
+ int num_sensors;
+ struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
+ struct fimc_dev *fimc[FIMC_MAX_DEVS];
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct platform_device *pdev;
+ bool user_subdev_api;
+ spinlock_t slock;
+};
+
+#define is_subdev_pad(pad) (pad == NULL || \
+ media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
+
+#define me_subtype(me) \
+ ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
+
+#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
+
+static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
+{
+ return me->parent == NULL ? NULL :
+ container_of(me->parent, struct fimc_md, media_dev);
+}
+
+static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
+{
+ mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
+}
+
+static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
+{
+ mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
+}
+
+int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
+
+#endif
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/platform/s5p-fimc/fimc-reg.c
index 783408fd7d56..783408fd7d56 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/platform/s5p-fimc/fimc-reg.c
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.h b/drivers/media/platform/s5p-fimc/fimc-reg.h
index 579ac8ac03de..579ac8ac03de 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.h
+++ b/drivers/media/platform/s5p-fimc/fimc-reg.h
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c
new file mode 100644
index 000000000000..8e4eed8414bd
--- /dev/null
+++ b/drivers/media/platform/s5p-fimc/mipi-csis.c
@@ -0,0 +1,842 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
+ *
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <plat/mipi_csis.h>
+#include "mipi-csis.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* Register map definition */
+
+/* CSIS global control */
+#define S5PCSIS_CTRL 0x00
+#define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31)
+#define S5PCSIS_CTRL_DPDN_SWAP (1 << 31)
+#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20)
+#define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16)
+#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8)
+#define S5PCSIS_CTRL_RESET (1 << 4)
+#define S5PCSIS_CTRL_ENABLE (1 << 0)
+
+/* D-PHY control */
+#define S5PCSIS_DPHYCTRL 0x04
+#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27)
+#define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0)
+
+#define S5PCSIS_CONFIG 0x08
+#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
+#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2)
+#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2)
+#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2)
+/* User defined formats, x = 1...4 */
+#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2)
+#define S5PCSIS_CFG_FMT_MASK (0x3f << 2)
+#define S5PCSIS_CFG_NR_LANE_MASK 3
+
+/* Interrupt mask */
+#define S5PCSIS_INTMSK 0x10
+#define S5PCSIS_INTMSK_EN_ALL 0xf000103f
+#define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31)
+#define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30)
+#define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29)
+#define S5PCSIS_INTMSK_ODD_AFTER (1 << 28)
+#define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12)
+#define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5)
+#define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4)
+#define S5PCSIS_INTMSK_ERR_OVER (1 << 3)
+#define S5PCSIS_INTMSK_ERR_ECC (1 << 2)
+#define S5PCSIS_INTMSK_ERR_CRC (1 << 1)
+#define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0)
+
+/* Interrupt source */
+#define S5PCSIS_INTSRC 0x14
+#define S5PCSIS_INTSRC_EVEN_BEFORE (1 << 31)
+#define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30)
+#define S5PCSIS_INTSRC_EVEN (0x3 << 30)
+#define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29)
+#define S5PCSIS_INTSRC_ODD_AFTER (1 << 28)
+#define S5PCSIS_INTSRC_ODD (0x3 << 28)
+#define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28)
+#define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12)
+#define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5)
+#define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4)
+#define S5PCSIS_INTSRC_ERR_OVER (1 << 3)
+#define S5PCSIS_INTSRC_ERR_ECC (1 << 2)
+#define S5PCSIS_INTSRC_ERR_CRC (1 << 1)
+#define S5PCSIS_INTSRC_ERR_UNKNOWN (1 << 0)
+#define S5PCSIS_INTSRC_ERRORS 0xf03f
+
+/* Pixel resolution */
+#define S5PCSIS_RESOL 0x2c
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+enum {
+ CSIS_CLK_MUX,
+ CSIS_CLK_GATE,
+};
+
+static char *csi_clock_name[] = {
+ [CSIS_CLK_MUX] = "sclk_csis",
+ [CSIS_CLK_GATE] = "csis",
+};
+#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
+
+static const char * const csis_supply_name[] = {
+ "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
+ "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
+};
+#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+struct s5pcsis_event {
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+static const struct s5pcsis_event s5pcsis_events[] = {
+ /* Errors */
+ { S5PCSIS_INTSRC_ERR_SOT_HS, "SOT Error" },
+ { S5PCSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" },
+ { S5PCSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" },
+ { S5PCSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" },
+ { S5PCSIS_INTSRC_ERR_ECC, "ECC Error" },
+ { S5PCSIS_INTSRC_ERR_CRC, "CRC Error" },
+ { S5PCSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" },
+ /* Non-image data receive events */
+ { S5PCSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
+ { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
+ { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
+ { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+};
+#define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
+
+/**
+ * struct csis_state - the driver's internal state data structure
+ * @lock: mutex serializing the subdev and power management operations,
+ * protecting @format and @flags members
+ * @pads: CSIS pads array
+ * @sd: v4l2_subdev associated with CSIS device instance
+ * @pdev: CSIS platform device
+ * @regs: mmaped I/O registers memory
+ * @supplies: CSIS regulator supplies
+ * @clock: CSIS clocks
+ * @irq: requested s5p-mipi-csis irq number
+ * @flags: the state variable for power and streaming control
+ * @csis_fmt: current CSIS pixel format
+ * @format: common media bus format for the source and sink pad
+ * @slock: spinlock protecting structure members below
+ * @events: MIPI-CSIS event (error) counters
+ */
+struct csis_state {
+ struct mutex lock;
+ struct media_pad pads[CSIS_PADS_NUM];
+ struct v4l2_subdev sd;
+ struct platform_device *pdev;
+ void __iomem *regs;
+ struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
+ struct clk *clock[NUM_CSIS_CLOCKS];
+ int irq;
+ u32 flags;
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format;
+
+ struct spinlock slock;
+ struct s5pcsis_event events[S5PCSIS_NUM_EVENTS];
+};
+
+/**
+ * struct csis_pix_format - CSIS pixel format description
+ * @pix_width_alignment: horizontal pixel alignment, width will be
+ * multiple of 2^pix_width_alignment
+ * @code: corresponding media bus code
+ * @fmt_reg: S5PCSIS_CONFIG register value
+ * @data_alignment: MIPI-CSI data alignment in bits
+ */
+struct csis_pix_format {
+ unsigned int pix_width_alignment;
+ enum v4l2_mbus_pixelcode code;
+ u32 fmt_reg;
+ u8 data_alignment;
+};
+
+static const struct csis_pix_format s5pcsis_formats[] = {
+ {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 32,
+ }, {
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .fmt_reg = S5PCSIS_CFG_FMT_USER(1),
+ .data_alignment = 32,
+ },
+};
+
+#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
+
+static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csis_state, sd);
+}
+
+static const struct csis_pix_format *find_csis_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
+ if (mf->code == s5pcsis_formats[i].code)
+ return &s5pcsis_formats[i];
+ return NULL;
+}
+
+static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
+
+ val = on ? val | S5PCSIS_INTMSK_EN_ALL :
+ val & ~S5PCSIS_INTMSK_EN_ALL;
+ s5pcsis_write(state, S5PCSIS_INTMSK, val);
+}
+
+static void s5pcsis_reset(struct csis_state *state)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
+
+ s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
+ udelay(10);
+}
+
+static void s5pcsis_system_enable(struct csis_state *state, int on)
+{
+ u32 val;
+
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ if (on)
+ val |= S5PCSIS_CTRL_ENABLE;
+ else
+ val &= ~S5PCSIS_CTRL_ENABLE;
+ s5pcsis_write(state, S5PCSIS_CTRL, val);
+
+ val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+ if (on)
+ val |= S5PCSIS_DPHYCTRL_ENABLE;
+ else
+ val &= ~S5PCSIS_DPHYCTRL_ENABLE;
+ s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __s5pcsis_set_format(struct csis_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format;
+ u32 val;
+
+ v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n",
+ mf->code, mf->width, mf->height);
+
+ /* Color format */
+ val = s5pcsis_read(state, S5PCSIS_CONFIG);
+ val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ s5pcsis_write(state, S5PCSIS_CONFIG, val);
+
+ /* Pixel resolution */
+ val = (mf->width << 16) | mf->height;
+ s5pcsis_write(state, S5PCSIS_RESOL, val);
+}
+
+static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
+{
+ u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
+
+ val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
+ s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
+}
+
+static void s5pcsis_set_params(struct csis_state *state)
+{
+ struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
+ u32 val;
+
+ val = s5pcsis_read(state, S5PCSIS_CONFIG);
+ val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
+ s5pcsis_write(state, S5PCSIS_CONFIG, val);
+
+ __s5pcsis_set_format(state);
+ s5pcsis_set_hsync_settle(state, pdata->hs_settle);
+
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ if (state->csis_fmt->data_alignment == 32)
+ val |= S5PCSIS_CTRL_ALIGN_32BIT;
+ else /* 24-bits */
+ val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
+ /* Not using external clock. */
+ val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
+ s5pcsis_write(state, S5PCSIS_CTRL, val);
+
+ /* Update the shadow register. */
+ val = s5pcsis_read(state, S5PCSIS_CTRL);
+ s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
+}
+
+static void s5pcsis_clk_put(struct csis_state *state)
+{
+ int i;
+
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+ if (IS_ERR_OR_NULL(state->clock[i]))
+ continue;
+ clk_unprepare(state->clock[i]);
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ }
+}
+
+static int s5pcsis_clk_get(struct csis_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ int i, ret;
+
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+ state->clock[i] = clk_get(dev, csi_clock_name[i]);
+ if (IS_ERR(state->clock[i]))
+ goto err;
+ ret = clk_prepare(state->clock[i]);
+ if (ret < 0) {
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ goto err;
+ }
+ }
+ return 0;
+err:
+ s5pcsis_clk_put(state);
+ dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
+ return -ENXIO;
+}
+
+static void s5pcsis_start_stream(struct csis_state *state)
+{
+ s5pcsis_reset(state);
+ s5pcsis_set_params(state);
+ s5pcsis_system_enable(state, true);
+ s5pcsis_enable_interrupts(state, true);
+}
+
+static void s5pcsis_stop_stream(struct csis_state *state)
+{
+ s5pcsis_enable_interrupts(state, false);
+ s5pcsis_system_enable(state, false);
+}
+
+static void s5pcsis_clear_counters(struct csis_state *state)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+ for (i = 0; i < S5PCSIS_NUM_EVENTS; i++)
+ state->events[i].counter = 0;
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static void s5pcsis_log_counters(struct csis_state *state, bool non_errors)
+{
+ int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ for (i--; i >= 0; i--)
+ if (state->events[i].counter >= 0)
+ v4l2_info(&state->sd, "%s events: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct device *dev = &state->pdev->dev;
+
+ if (on)
+ return pm_runtime_get_sync(dev);
+
+ return pm_runtime_put_sync(dev);
+}
+
+static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
+ __func__, enable, state->flags);
+
+ if (enable) {
+ s5pcsis_clear_counters(state);
+ ret = pm_runtime_get_sync(&state->pdev->dev);
+ if (ret && ret != 1)
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+ if (enable) {
+ if (state->flags & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+ s5pcsis_start_stream(state);
+ state->flags |= ST_STREAMING;
+ } else {
+ s5pcsis_stop_stream(state);
+ state->flags &= ~ST_STREAMING;
+ if (debug > 0)
+ s5pcsis_log_counters(state, true);
+ }
+unlock:
+ mutex_unlock(&state->lock);
+ if (!enable)
+ pm_runtime_put(&state->pdev->dev);
+
+ return ret == 1 ? 0 : ret;
+}
+
+static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(s5pcsis_formats))
+ return -EINVAL;
+
+ code->code = s5pcsis_formats[code->index].code;
+ return 0;
+}
+
+static struct csis_pix_format const *s5pcsis_try_format(
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct csis_pix_format const *csis_fmt;
+
+ csis_fmt = find_csis_format(mf);
+ if (csis_fmt == NULL)
+ csis_fmt = &s5pcsis_formats[0];
+
+ mf->code = csis_fmt->code;
+ v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+ csis_fmt->pix_width_alignment,
+ &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+ 0);
+ return csis_fmt;
+}
+
+static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
+ struct csis_state *state, struct v4l2_subdev_fh *fh,
+ u32 pad, enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
+
+ return &state->format;
+}
+
+static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+
+ if (fmt->pad == CSIS_PAD_SOURCE) {
+ if (mf) {
+ mutex_lock(&state->lock);
+ fmt->format = *mf;
+ mutex_unlock(&state->lock);
+ }
+ return 0;
+ }
+ csis_fmt = s5pcsis_try_format(&fmt->format);
+ if (mf) {
+ mutex_lock(&state->lock);
+ *mf = fmt->format;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+ mutex_unlock(&state->lock);
+ }
+ return 0;
+}
+
+static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
+ if (!mf)
+ return -EINVAL;
+
+ mutex_lock(&state->lock);
+ fmt->format = *mf;
+ mutex_unlock(&state->lock);
+ return 0;
+}
+
+static int s5pcsis_log_status(struct v4l2_subdev *sd)
+{
+ struct csis_state *state = sd_to_csis_state(sd);
+
+ s5pcsis_log_counters(state, true);
+ return 0;
+}
+
+static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
+
+ format->colorspace = V4L2_COLORSPACE_JPEG;
+ format->code = s5pcsis_formats[0].code;
+ format->width = S5PCSIS_DEF_PIX_WIDTH;
+ format->height = S5PCSIS_DEF_PIX_HEIGHT;
+ format->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
+ .open = s5pcsis_open,
+};
+
+static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
+ .s_power = s5pcsis_s_power,
+ .log_status = s5pcsis_log_status,
+};
+
+static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
+ .enum_mbus_code = s5pcsis_enum_mbus_code,
+ .get_fmt = s5pcsis_get_fmt,
+ .set_fmt = s5pcsis_set_fmt,
+};
+
+static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
+ .s_stream = s5pcsis_s_stream,
+};
+
+static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
+ .core = &s5pcsis_core_ops,
+ .pad = &s5pcsis_pad_ops,
+ .video = &s5pcsis_video_ops,
+};
+
+static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
+{
+ struct csis_state *state = dev_id;
+ unsigned long flags;
+ u32 status;
+
+ status = s5pcsis_read(state, S5PCSIS_INTSRC);
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ /* Update the event/error counters */
+ if ((status & S5PCSIS_INTSRC_ERRORS) || debug) {
+ int i;
+ for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) {
+ if (!(status & state->events[i].mask))
+ continue;
+ state->events[i].counter++;
+ v4l2_dbg(2, debug, &state->sd, "%s: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+ }
+ v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ s5pcsis_write(state, S5PCSIS_INTSRC, status);
+ return IRQ_HANDLED;
+}
+
+static int __devinit s5pcsis_probe(struct platform_device *pdev)
+{
+ struct s5p_platform_mipi_csis *pdata;
+ struct resource *mem_res;
+ struct csis_state *state;
+ int ret = -ENOMEM;
+ int i;
+
+ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ spin_lock_init(&state->slock);
+
+ state->pdev = pdev;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata == NULL || pdata->phy_enable == NULL) {
+ dev_err(&pdev->dev, "Platform data not fully specified\n");
+ return -EINVAL;
+ }
+
+ if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
+ pdata->lanes > CSIS0_MAX_LANES) {
+ dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
+ pdata->lanes);
+ return -EINVAL;
+ }
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
+ if (state->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to request and remap io memory\n");
+ return -ENXIO;
+ }
+
+ state->irq = platform_get_irq(pdev, 0);
+ if (state->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
+ return state->irq;
+ }
+
+ for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
+ state->supplies[i].supply = csis_supply_name[i];
+
+ ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ return ret;
+
+ ret = s5pcsis_clk_get(state);
+ if (ret)
+ goto e_clkput;
+
+ clk_enable(state->clock[CSIS_CLK_MUX]);
+ if (pdata->clk_rate)
+ clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+ else
+ dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+
+ ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
+ 0, dev_name(&pdev->dev), state);
+ if (ret) {
+ dev_err(&pdev->dev, "Interrupt request failed\n");
+ goto e_regput;
+ }
+
+ v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
+ state->sd.owner = THIS_MODULE;
+ strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
+ state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ state->csis_fmt = &s5pcsis_formats[0];
+
+ state->format.code = s5pcsis_formats[0].code;
+ state->format.width = S5PCSIS_DEF_PIX_WIDTH;
+ state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
+
+ state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&state->sd.entity,
+ CSIS_PADS_NUM, state->pads, 0);
+ if (ret < 0)
+ goto e_clkput;
+
+ /* This allows to retrieve the platform device id by the host driver */
+ v4l2_set_subdevdata(&state->sd, pdev);
+
+ /* .. and a pointer to the subdev. */
+ platform_set_drvdata(pdev, &state->sd);
+
+ memcpy(state->events, s5pcsis_events, sizeof(state->events));
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+
+e_regput:
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+e_clkput:
+ clk_disable(state->clock[CSIS_CLK_MUX]);
+ s5pcsis_clk_put(state);
+ return ret;
+}
+
+static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (state->flags & ST_POWERED) {
+ s5pcsis_stop_stream(state);
+ ret = pdata->phy_enable(state->pdev, false);
+ if (ret)
+ goto unlock;
+ ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
+ clk_disable(state->clock[CSIS_CLK_GATE]);
+ state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
+ }
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+static int s5pcsis_pm_resume(struct device *dev, bool runtime)
+{
+ struct s5p_platform_mipi_csis *pdata = dev->platform_data;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+ int ret = 0;
+
+ v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
+ __func__, state->flags);
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->flags & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->flags & ST_POWERED)) {
+ ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ if (ret)
+ goto unlock;
+ ret = pdata->phy_enable(state->pdev, true);
+ if (!ret) {
+ state->flags |= ST_POWERED;
+ } else {
+ regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ goto unlock;
+ }
+ clk_enable(state->clock[CSIS_CLK_GATE]);
+ }
+ if (state->flags & ST_STREAMING)
+ s5pcsis_start_stream(state);
+
+ state->flags &= ~ST_SUSPENDED;
+ unlock:
+ mutex_unlock(&state->lock);
+ return ret ? -EAGAIN : 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s5pcsis_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, false);
+}
+
+static int s5pcsis_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, false);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5pcsis_runtime_suspend(struct device *dev)
+{
+ return s5pcsis_pm_suspend(dev, true);
+}
+
+static int s5pcsis_runtime_resume(struct device *dev)
+{
+ return s5pcsis_pm_resume(dev, true);
+}
+#endif
+
+static int __devexit s5pcsis_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csis_state *state = sd_to_csis_state(sd);
+
+ pm_runtime_disable(&pdev->dev);
+ s5pcsis_pm_suspend(&pdev->dev, false);
+ clk_disable(state->clock[CSIS_CLK_MUX]);
+ pm_runtime_set_suspended(&pdev->dev);
+ s5pcsis_clk_put(state);
+ regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
+
+ media_entity_cleanup(&state->sd.entity);
+
+ return 0;
+}
+
+static const struct dev_pm_ops s5pcsis_pm_ops = {
+ SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
+};
+
+static struct platform_driver s5pcsis_driver = {
+ .probe = s5pcsis_probe,
+ .remove = __devexit_p(s5pcsis_remove),
+ .driver = {
+ .name = CSIS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5pcsis_pm_ops,
+ },
+};
+
+module_platform_driver(s5pcsis_driver);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.h b/drivers/media/platform/s5p-fimc/mipi-csis.h
index 2709286396e1..2709286396e1 100644
--- a/drivers/media/video/s5p-fimc/mipi-csis.h
+++ b/drivers/media/platform/s5p-fimc/mipi-csis.h
diff --git a/drivers/media/video/s5p-g2d/Makefile b/drivers/media/platform/s5p-g2d/Makefile
index 2c48c416a804..2c48c416a804 100644
--- a/drivers/media/video/s5p-g2d/Makefile
+++ b/drivers/media/platform/s5p-g2d/Makefile
diff --git a/drivers/media/video/s5p-g2d/g2d-hw.c b/drivers/media/platform/s5p-g2d/g2d-hw.c
index 5b86cbe408e2..5b86cbe408e2 100644
--- a/drivers/media/video/s5p-g2d/g2d-hw.c
+++ b/drivers/media/platform/s5p-g2d/g2d-hw.c
diff --git a/drivers/media/video/s5p-g2d/g2d-regs.h b/drivers/media/platform/s5p-g2d/g2d-regs.h
index 02e1cf50da4e..02e1cf50da4e 100644
--- a/drivers/media/video/s5p-g2d/g2d-regs.h
+++ b/drivers/media/platform/s5p-g2d/g2d-regs.h
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c
new file mode 100644
index 000000000000..1e3b9dd014c0
--- /dev/null
+++ b/drivers/media/platform/s5p-g2d/g2d.c
@@ -0,0 +1,846 @@
+/*
+ * Samsung S5P G2D - 2D Graphics Accelerator Driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@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
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "g2d.h"
+#include "g2d-regs.h"
+
+#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh)
+
+static struct g2d_fmt formats[] = {
+ {
+ .name = "XRGB_8888",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .depth = 32,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888),
+ },
+ {
+ .name = "RGB_565",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565),
+ },
+ {
+ .name = "XRGB_1555",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555),
+ },
+ {
+ .name = "XRGB_4444",
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .depth = 16,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444),
+ },
+ {
+ .name = "PACKED_RGB_888",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .depth = 24,
+ .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888),
+ },
+};
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct g2d_frame def_frame = {
+ .width = DEFAULT_WIDTH,
+ .height = DEFAULT_HEIGHT,
+ .c_width = DEFAULT_WIDTH,
+ .c_height = DEFAULT_HEIGHT,
+ .o_width = 0,
+ .o_height = 0,
+ .fmt = &formats[0],
+ .right = DEFAULT_WIDTH,
+ .bottom = DEFAULT_HEIGHT,
+};
+
+static struct g2d_fmt *find_fmt(struct v4l2_format *f)
+{
+ unsigned int i;
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix.pixelformat)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+
+static struct g2d_frame *get_frame(struct g2d_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &ctx->in;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &ctx->out;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+}
+
+static int g2d_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vq);
+ struct g2d_frame *f = get_frame(ctx, vq->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ sizes[0] = f->size;
+ *nplanes = 1;
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static int g2d_buf_prepare(struct vb2_buffer *vb)
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ vb2_set_plane_payload(vb, 0, f->size);
+ return 0;
+}
+
+static void g2d_buf_queue(struct vb2_buffer *vb)
+{
+ struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+
+static struct vb2_ops g2d_qops = {
+ .queue_setup = g2d_queue_setup,
+ .buf_prepare = g2d_buf_prepare,
+ .buf_queue = g2d_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct g2d_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &g2d_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &g2d_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 g2d_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
+ ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
+ switch (ctrl->id) {
+ case V4L2_CID_COLORFX:
+ if (ctrl->val == V4L2_COLORFX_NEGATIVE)
+ ctx->rop = ROP4_INVERT;
+ else
+ ctx->rop = ROP4_COPY;
+ break;
+
+ case V4L2_CID_HFLIP:
+ ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
+ break;
+
+ }
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops g2d_ctrl_ops = {
+ .s_ctrl = g2d_s_ctrl,
+};
+
+static int g2d_setup_ctrls(struct g2d_ctx *ctx)
+{
+ struct g2d_dev *dev = ctx->dev;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std_menu(
+ &ctx->ctrl_handler,
+ &g2d_ctrl_ops,
+ V4L2_CID_COLORFX,
+ V4L2_COLORFX_NEGATIVE,
+ ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)),
+ V4L2_COLORFX_NONE);
+
+ if (ctx->ctrl_handler.error) {
+ int err = ctx->ctrl_handler.error;
+ v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return err;
+ }
+
+ v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
+
+ return 0;
+}
+
+static int g2d_open(struct file *file)
+{
+ struct g2d_dev *dev = video_drvdata(file);
+ struct g2d_ctx *ctx = NULL;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ ctx->dev = dev;
+ /* Set default formats */
+ ctx->in = def_frame;
+ ctx->out = def_frame;
+
+ if (mutex_lock_interruptible(&dev->mutex)) {
+ kfree(ctx);
+ return -ERESTARTSYS;
+ }
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ mutex_unlock(&dev->mutex);
+ kfree(ctx);
+ return ret;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ g2d_setup_ctrls(ctx);
+
+ /* Write the default values to the ctx struct */
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ mutex_unlock(&dev->mutex);
+
+ v4l2_info(&dev->v4l2_dev, "instance opened\n");
+ return 0;
+}
+
+static int g2d_release(struct file *file)
+{
+ struct g2d_dev *dev = video_drvdata(file);
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ v4l2_info(&dev->v4l2_dev, "instance closed\n");
+ return 0;
+}
+
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
+ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
+{
+ struct g2d_fmt *fmt;
+ if (f->index >= NUM_FORMATS)
+ return -EINVAL;
+ fmt = &formats[f->index];
+ f->pixelformat = fmt->fourcc;
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_ctx *ctx = prv;
+ struct vb2_queue *vq;
+ struct g2d_frame *frm;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ f->fmt.pix.width = frm->width;
+ f->fmt.pix.height = frm->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = frm->fmt->fourcc;
+ f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = frm->size;
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_fmt *fmt;
+ enum v4l2_field *field;
+
+ fmt = find_fmt(f);
+ if (!fmt)
+ return -EINVAL;
+
+ field = &f->fmt.pix.field;
+ if (*field == V4L2_FIELD_ANY)
+ *field = V4L2_FIELD_NONE;
+ else if (*field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->fmt.pix.width > MAX_WIDTH)
+ f->fmt.pix.width = MAX_WIDTH;
+ if (f->fmt.pix.height > MAX_HEIGHT)
+ f->fmt.pix.height = MAX_HEIGHT;
+
+ if (f->fmt.pix.width < 1)
+ f->fmt.pix.width = 1;
+ if (f->fmt.pix.height < 1)
+ f->fmt.pix.height = 1;
+
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct vb2_queue *vq;
+ struct g2d_frame *frm;
+ struct g2d_fmt *fmt;
+ int ret = 0;
+
+ /* Adjust all values accordingly to the hardware capabilities
+ * and chosen format. */
+ ret = vidioc_try_fmt(file, prv, f);
+ if (ret)
+ return ret;
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
+ return -EBUSY;
+ }
+ frm = get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+ fmt = find_fmt(f);
+ if (!fmt)
+ return -EINVAL;
+ frm->width = f->fmt.pix.width;
+ frm->height = f->fmt.pix.height;
+ frm->size = f->fmt.pix.sizeimage;
+ /* Reset crop settings */
+ frm->o_width = 0;
+ frm->o_height = 0;
+ frm->c_width = frm->width;
+ frm->c_height = frm->height;
+ frm->right = frm->width;
+ frm->bottom = frm->height;
+ frm->fmt = fmt;
+ frm->stride = f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+ struct g2d_dev *dev = ctx->dev;
+ unsigned int res;
+
+ mutex_lock(&dev->mutex);
+ res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&dev->mutex);
+ return res;
+}
+
+static int g2d_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct g2d_ctx *ctx = fh2ctx(file->private_data);
+ struct g2d_dev *dev = ctx->dev;
+ int ret;
+
+ if (mutex_lock_interruptible(&dev->mutex))
+ return -ERESTARTSYS;
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&dev->mutex);
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct g2d_ctx *ctx = priv;
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cr)
+{
+ struct g2d_ctx *ctx = priv;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = f->width;
+ cr->bounds.height = f->height;
+ cr->defrect = cr->bounds;
+ return 0;
+}
+
+static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ cr->c.left = f->o_height;
+ cr->c.top = f->o_width;
+ cr->c.width = f->c_width;
+ cr->c.height = f->c_height;
+ return 0;
+}
+
+static int vidioc_try_crop(struct file *file, void *prv, struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct g2d_frame *f;
+
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_crop(struct file *file, void *prv, const struct v4l2_crop *cr)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_frame *f;
+ int ret;
+
+ ret = vidioc_try_crop(file, prv, cr);
+ if (ret)
+ return ret;
+ f = get_frame(ctx, cr->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ f->c_width = cr->c.width;
+ f->c_height = cr->c.height;
+ f->o_width = cr->c.left;
+ f->o_height = cr->c.top;
+ f->bottom = f->o_height + f->c_height;
+ f->right = f->o_width + f->c_width;
+ return 0;
+}
+
+static void g2d_lock(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ mutex_lock(&dev->mutex);
+}
+
+static void g2d_unlock(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ mutex_unlock(&dev->mutex);
+}
+
+static void job_abort(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ int ret;
+
+ if (dev->curr == NULL) /* No job currently running */
+ return;
+
+ ret = wait_event_timeout(dev->irq_queue,
+ dev->curr == NULL,
+ msecs_to_jiffies(G2D_TIMEOUT));
+}
+
+static void device_run(void *prv)
+{
+ struct g2d_ctx *ctx = prv;
+ struct g2d_dev *dev = ctx->dev;
+ struct vb2_buffer *src, *dst;
+ unsigned long flags;
+ u32 cmd = 0;
+
+ dev->curr = ctx;
+
+ src = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ clk_enable(dev->gate);
+ g2d_reset(dev);
+
+ spin_lock_irqsave(&dev->ctrl_lock, flags);
+
+ g2d_set_src_size(dev, &ctx->in);
+ g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
+
+ g2d_set_dst_size(dev, &ctx->out);
+ g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
+
+ g2d_set_rop4(dev, ctx->rop);
+ g2d_set_flip(dev, ctx->flip);
+
+ if (ctx->in.c_width != ctx->out.c_width ||
+ ctx->in.c_height != ctx->out.c_height)
+ cmd |= g2d_cmd_stretch(1);
+ g2d_set_cmd(dev, cmd);
+ g2d_start(dev);
+
+ spin_unlock_irqrestore(&dev->ctrl_lock, flags);
+}
+
+static irqreturn_t g2d_isr(int irq, void *prv)
+{
+ struct g2d_dev *dev = prv;
+ struct g2d_ctx *ctx = dev->curr;
+ struct vb2_buffer *src, *dst;
+
+ g2d_clear_int(dev);
+ clk_disable(dev->gate);
+
+ BUG_ON(ctx == NULL);
+
+ src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ BUG_ON(src == NULL);
+ BUG_ON(dst == NULL);
+
+ v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
+ v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
+
+ dev->curr = NULL;
+ wake_up(&dev->irq_queue);
+ return IRQ_HANDLED;
+}
+
+static const struct v4l2_file_operations g2d_fops = {
+ .owner = THIS_MODULE,
+ .open = g2d_open,
+ .release = g2d_release,
+ .poll = g2d_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = g2d_mmap,
+};
+
+static const struct v4l2_ioctl_ops g2d_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ .vidioc_g_crop = vidioc_g_crop,
+ .vidioc_s_crop = vidioc_s_crop,
+ .vidioc_cropcap = vidioc_cropcap,
+};
+
+static struct video_device g2d_videodev = {
+ .name = G2D_NAME,
+ .fops = &g2d_fops,
+ .ioctl_ops = &g2d_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+ .vfl_dir = VFL_DIR_M2M,
+};
+
+static struct v4l2_m2m_ops g2d_m2m_ops = {
+ .device_run = device_run,
+ .job_abort = job_abort,
+ .lock = g2d_lock,
+ .unlock = g2d_unlock,
+};
+
+static int g2d_probe(struct platform_device *pdev)
+{
+ struct g2d_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret = 0;
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->ctrl_lock);
+ mutex_init(&dev->mutex);
+ atomic_set(&dev->num_inst, 0);
+ init_waitqueue_head(&dev->irq_queue);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (dev->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
+ if (IS_ERR_OR_NULL(dev->clk)) {
+ dev_err(&pdev->dev, "failed to get g2d clock\n");
+ return -ENXIO;
+ }
+
+ ret = clk_prepare(dev->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock\n");
+ goto put_clk;
+ }
+
+ dev->gate = clk_get(&pdev->dev, "fimg2d");
+ if (IS_ERR_OR_NULL(dev->gate)) {
+ dev_err(&pdev->dev, "failed to get g2d clock gate\n");
+ ret = -ENXIO;
+ goto unprep_clk;
+ }
+
+ ret = clk_prepare(dev->gate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
+ goto put_clk_gate;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to find IRQ\n");
+ ret = -ENXIO;
+ goto unprep_clk_gate;
+ }
+
+ dev->irq = res->start;
+
+ ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr,
+ 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install IRQ\n");
+ goto put_clk_gate;
+ }
+
+ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(dev->alloc_ctx)) {
+ ret = PTR_ERR(dev->alloc_ctx);
+ goto unprep_clk_gate;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto alloc_ctx_cleanup;
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_v4l2_dev;
+ }
+ *vfd = g2d_videodev;
+ vfd->lock = &dev->mutex;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ goto rel_vdev;
+ }
+ video_set_drvdata(vfd, dev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name);
+ dev->vfd = vfd;
+ v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
+ vfd->num);
+ platform_set_drvdata(pdev, dev);
+ dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(dev->m2m_dev);
+ goto unreg_video_dev;
+ }
+
+ def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
+
+ return 0;
+
+unreg_video_dev:
+ video_unregister_device(dev->vfd);
+rel_vdev:
+ video_device_release(vfd);
+unreg_v4l2_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+alloc_ctx_cleanup:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+unprep_clk_gate:
+ clk_unprepare(dev->gate);
+put_clk_gate:
+ clk_put(dev->gate);
+unprep_clk:
+ clk_unprepare(dev->clk);
+put_clk:
+ clk_put(dev->clk);
+
+ return ret;
+}
+
+static int g2d_remove(struct platform_device *pdev)
+{
+ struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
+ v4l2_m2m_release(dev->m2m_dev);
+ video_unregister_device(dev->vfd);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
+ clk_unprepare(dev->gate);
+ clk_put(dev->gate);
+ clk_unprepare(dev->clk);
+ clk_put(dev->clk);
+ return 0;
+}
+
+static struct platform_driver g2d_pdrv = {
+ .probe = g2d_probe,
+ .remove = g2d_remove,
+ .driver = {
+ .name = G2D_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(g2d_pdrv);
+
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/platform/s5p-g2d/g2d.h
index 6b765b0216c5..6b765b0216c5 100644
--- a/drivers/media/video/s5p-g2d/g2d.h
+++ b/drivers/media/platform/s5p-g2d/g2d.h
diff --git a/drivers/media/video/s5p-jpeg/Makefile b/drivers/media/platform/s5p-jpeg/Makefile
index ddc2900d88a2..ddc2900d88a2 100644
--- a/drivers/media/video/s5p-jpeg/Makefile
+++ b/drivers/media/platform/s5p-jpeg/Makefile
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
new file mode 100644
index 000000000000..394775ae5774
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -0,0 +1,1528 @@
+/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "jpeg-core.h"
+#include "jpeg-hw.h"
+
+static struct s5p_jpeg_fmt formats_enc[] = {
+ {
+ .name = "JPEG JFIF",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+ {
+ .name = "RGB565",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc)
+
+static struct s5p_jpeg_fmt formats_dec[] = {
+ {
+ .name = "YUV 4:2:0 planar, YCbCr",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .depth = 12,
+ .colplanes = 3,
+ .h_align = 4,
+ .v_align = 4,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .colplanes = 1,
+ .h_align = 4,
+ .v_align = 3,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "JPEG JFIF",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec)
+
+static const unsigned char qtbl_luminance[4][64] = {
+ {/* level 1 - high quality */
+ 8, 6, 6, 8, 12, 14, 16, 17,
+ 6, 6, 6, 8, 10, 13, 12, 15,
+ 6, 6, 7, 8, 13, 14, 18, 24,
+ 8, 8, 8, 14, 13, 19, 24, 35,
+ 12, 10, 13, 13, 20, 26, 34, 39,
+ 14, 13, 14, 19, 26, 34, 39, 39,
+ 16, 12, 18, 24, 34, 39, 39, 39,
+ 17, 15, 24, 35, 39, 39, 39, 39
+ },
+ {/* level 2 */
+ 12, 8, 8, 12, 17, 21, 24, 23,
+ 8, 9, 9, 11, 15, 19, 18, 23,
+ 8, 9, 10, 12, 19, 20, 27, 36,
+ 12, 11, 12, 21, 20, 28, 36, 53,
+ 17, 15, 19, 20, 30, 39, 51, 59,
+ 21, 19, 20, 28, 39, 51, 59, 59,
+ 24, 18, 27, 36, 51, 59, 59, 59,
+ 23, 23, 36, 53, 59, 59, 59, 59
+ },
+ {/* level 3 */
+ 16, 11, 11, 16, 23, 27, 31, 30,
+ 11, 12, 12, 15, 20, 23, 23, 30,
+ 11, 12, 13, 16, 23, 26, 35, 47,
+ 16, 15, 16, 23, 26, 37, 47, 64,
+ 23, 20, 23, 26, 39, 51, 64, 64,
+ 27, 23, 26, 37, 51, 64, 64, 64,
+ 31, 23, 35, 47, 64, 64, 64, 64,
+ 30, 30, 47, 64, 64, 64, 64, 64
+ },
+ {/*level 4 - low quality */
+ 20, 16, 25, 39, 50, 46, 62, 68,
+ 16, 18, 23, 38, 38, 53, 65, 68,
+ 25, 23, 31, 38, 53, 65, 68, 68,
+ 39, 38, 38, 53, 65, 68, 68, 68,
+ 50, 38, 53, 65, 68, 68, 68, 68,
+ 46, 53, 65, 68, 68, 68, 68, 68,
+ 62, 65, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ }
+};
+
+static const unsigned char qtbl_chrominance[4][64] = {
+ {/* level 1 - high quality */
+ 9, 8, 9, 11, 14, 17, 19, 24,
+ 8, 10, 9, 11, 14, 13, 17, 22,
+ 9, 9, 13, 14, 13, 15, 23, 26,
+ 11, 11, 14, 14, 15, 20, 26, 33,
+ 14, 14, 13, 15, 20, 24, 33, 39,
+ 17, 13, 15, 20, 24, 32, 39, 39,
+ 19, 17, 23, 26, 33, 39, 39, 39,
+ 24, 22, 26, 33, 39, 39, 39, 39
+ },
+ {/* level 2 */
+ 13, 11, 13, 16, 20, 20, 29, 37,
+ 11, 14, 14, 14, 16, 20, 26, 32,
+ 13, 14, 15, 17, 20, 23, 35, 40,
+ 16, 14, 17, 21, 23, 30, 40, 50,
+ 20, 16, 20, 23, 30, 37, 50, 59,
+ 20, 20, 23, 30, 37, 48, 59, 59,
+ 29, 26, 35, 40, 50, 59, 59, 59,
+ 37, 32, 40, 50, 59, 59, 59, 59
+ },
+ {/* level 3 */
+ 17, 15, 17, 21, 20, 26, 38, 48,
+ 15, 19, 18, 17, 20, 26, 35, 43,
+ 17, 18, 20, 22, 26, 30, 46, 53,
+ 21, 17, 22, 28, 30, 39, 53, 64,
+ 20, 20, 26, 30, 39, 48, 64, 64,
+ 26, 26, 30, 39, 48, 63, 64, 64,
+ 38, 35, 46, 53, 64, 64, 64, 64,
+ 48, 43, 53, 64, 64, 64, 64, 64
+ },
+ {/*level 4 - low quality */
+ 21, 25, 32, 38, 54, 68, 68, 68,
+ 25, 28, 24, 38, 54, 68, 68, 68,
+ 32, 24, 32, 43, 66, 68, 68, 68,
+ 38, 38, 43, 53, 68, 68, 68, 68,
+ 54, 54, 66, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68
+ }
+};
+
+static const unsigned char hdctbl0[16] = {
+ 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char hdctblg0[12] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb
+};
+static const unsigned char hactbl0[16] = {
+ 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
+};
+static const unsigned char hactblg0[162] = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+};
+
+static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
+{
+ return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
+}
+
+static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct s5p_jpeg_ctx, fh);
+}
+
+static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
+ unsigned long tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ writel((unsigned int)qtbl[i], regs + tab + (i * 0x04));
+}
+
+static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 0 with data for luma */
+ jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0),
+ ARRAY_SIZE(qtbl_luminance[quality]));
+}
+
+static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality)
+{
+ /* this driver fills quantisation table 1 with data for chroma */
+ jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1),
+ ARRAY_SIZE(qtbl_chrominance[quality]));
+}
+
+static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,
+ unsigned long tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ writel((unsigned int)htbl[i], regs + tab + (i * 0x04));
+}
+
+static inline void jpeg_set_hdctbl(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0));
+}
+
+static inline void jpeg_set_hdctblg(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0));
+}
+
+static inline void jpeg_set_hactbl(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0));
+}
+
+static inline void jpeg_set_hactblg(void __iomem *regs)
+{
+ /* this driver fills table 0 for this component */
+ jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0));
+}
+
+/*
+ * ============================================================================
+ * Device file operations
+ * ============================================================================
+ */
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq);
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
+ __u32 pixelformat);
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
+
+static int s5p_jpeg_open(struct file *file)
+{
+ struct s5p_jpeg *jpeg = video_drvdata(file);
+ struct video_device *vfd = video_devdata(file);
+ struct s5p_jpeg_ctx *ctx;
+ struct s5p_jpeg_fmt *out_fmt;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&jpeg->lock)) {
+ ret = -ERESTARTSYS;
+ goto free;
+ }
+
+ v4l2_fh_init(&ctx->fh, vfd);
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->jpeg = jpeg;
+ if (vfd == jpeg->vfd_encoder) {
+ ctx->mode = S5P_JPEG_ENCODE;
+ out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565);
+ } else {
+ ctx->mode = S5P_JPEG_DECODE;
+ out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
+ }
+
+ ret = s5p_jpeg_controls_create(ctx);
+ if (ret < 0)
+ goto error;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error;
+ }
+
+ ctx->out_q.fmt = out_fmt;
+ ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
+ mutex_unlock(&jpeg->lock);
+ return 0;
+
+error:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ mutex_unlock(&jpeg->lock);
+free:
+ kfree(ctx);
+ return ret;
+}
+
+static int s5p_jpeg_release(struct file *file)
+{
+ struct s5p_jpeg *jpeg = video_drvdata(file);
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+
+ mutex_lock(&jpeg->lock);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ mutex_unlock(&jpeg->lock);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int s5p_jpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s5p_jpeg *jpeg = video_drvdata(file);
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+ unsigned int res;
+
+ mutex_lock(&jpeg->lock);
+ res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+ mutex_unlock(&jpeg->lock);
+ return res;
+}
+
+static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct s5p_jpeg *jpeg = video_drvdata(file);
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
+ int ret;
+
+ if (mutex_lock_interruptible(&jpeg->lock))
+ return -ERESTARTSYS;
+ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+ mutex_unlock(&jpeg->lock);
+ return ret;
+}
+
+static const struct v4l2_file_operations s5p_jpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_jpeg_open,
+ .release = s5p_jpeg_release,
+ .poll = s5p_jpeg_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s5p_jpeg_mmap,
+};
+
+/*
+ * ============================================================================
+ * video ioctl operations
+ * ============================================================================
+ */
+
+static int get_byte(struct s5p_jpeg_buffer *buf)
+{
+ if (buf->curr >= buf->size)
+ return -1;
+
+ return ((unsigned char *)buf->data)[buf->curr++];
+}
+
+static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word)
+{
+ unsigned int temp;
+ int byte;
+
+ byte = get_byte(buf);
+ if (byte == -1)
+ return -1;
+ temp = byte << 8;
+ byte = get_byte(buf);
+ if (byte == -1)
+ return -1;
+ *word = (unsigned int)byte | temp;
+ return 0;
+}
+
+static void skip(struct s5p_jpeg_buffer *buf, long len)
+{
+ if (len <= 0)
+ return;
+
+ while (len--)
+ get_byte(buf);
+}
+
+static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
+ unsigned long buffer, unsigned long size)
+{
+ int c, components, notfound;
+ unsigned int height, width, word;
+ long length;
+ struct s5p_jpeg_buffer jpeg_buffer;
+
+ jpeg_buffer.size = size;
+ jpeg_buffer.data = buffer;
+ jpeg_buffer.curr = 0;
+
+ notfound = 1;
+ while (notfound) {
+ c = get_byte(&jpeg_buffer);
+ if (c == -1)
+ break;
+ if (c != 0xff)
+ continue;
+ do
+ c = get_byte(&jpeg_buffer);
+ while (c == 0xff);
+ if (c == -1)
+ break;
+ if (c == 0)
+ continue;
+ length = 0;
+ switch (c) {
+ /* SOF0: baseline JPEG */
+ case SOF0:
+ if (get_word_be(&jpeg_buffer, &word))
+ break;
+ if (get_byte(&jpeg_buffer) == -1)
+ break;
+ if (get_word_be(&jpeg_buffer, &height))
+ break;
+ if (get_word_be(&jpeg_buffer, &width))
+ break;
+ components = get_byte(&jpeg_buffer);
+ if (components == -1)
+ break;
+ notfound = 0;
+
+ skip(&jpeg_buffer, components * 3);
+ break;
+
+ /* skip payload-less markers */
+ case RST ... RST + 7:
+ case SOI:
+ case EOI:
+ case TEM:
+ break;
+
+ /* skip uninteresting payload markers */
+ default:
+ if (get_word_be(&jpeg_buffer, &word))
+ break;
+ length = (long)word - 2;
+ skip(&jpeg_buffer, length);
+ break;
+ }
+ }
+ result->w = width;
+ result->h = height;
+ result->size = components;
+ return !notfound;
+}
+
+static int s5p_jpeg_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder",
+ sizeof(cap->driver));
+ strlcpy(cap->card, S5P_JPEG_M2M_NAME " encoder",
+ sizeof(cap->card));
+ } else {
+ strlcpy(cap->driver, S5P_JPEG_M2M_NAME " decoder",
+ sizeof(cap->driver));
+ strlcpy(cap->card, S5P_JPEG_M2M_NAME " decoder",
+ sizeof(cap->card));
+ }
+ cap->bus_info[0] = 0;
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
+ return 0;
+}
+
+static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
+ struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num = 0;
+
+ for (i = 0; i < n; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ /* Format not found */
+ if (i >= n)
+ return -EINVAL;
+
+ strlcpy(f->description, formats[i].name, sizeof(f->description));
+ f->pixelformat = formats[i].fourcc;
+
+ return 0;
+}
+
+static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
+ MEM2MEM_CAPTURE);
+
+ return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE);
+}
+
+static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->mode == S5P_JPEG_ENCODE)
+ return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
+ MEM2MEM_OUTPUT);
+
+ return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT);
+}
+
+static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return &ctx->out_q;
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return &ctx->cap_q;
+
+ return NULL;
+}
+
+static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct s5p_jpeg_q_data *q_data = NULL;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
+
+ vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed)
+ return -EINVAL;
+ q_data = get_q_data(ct, f->type);
+ BUG_ON(q_data == NULL);
+
+ pix->width = q_data->w;
+ pix->height = q_data->h;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = q_data->fmt->fourcc;
+ pix->bytesperline = 0;
+ if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) {
+ u32 bpl = q_data->w;
+ if (q_data->fmt->colplanes == 1)
+ bpl = (bpl * q_data->fmt->depth) >> 3;
+ pix->bytesperline = bpl;
+ }
+ pix->sizeimage = q_data->size;
+
+ return 0;
+}
+
+static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
+ u32 pixelformat)
+{
+ unsigned int k;
+ struct s5p_jpeg_fmt *formats;
+ int n;
+
+ if (mode == S5P_JPEG_ENCODE) {
+ formats = formats_enc;
+ n = NUM_FORMATS_ENC;
+ } else {
+ formats = formats_dec;
+ n = NUM_FORMATS_DEC;
+ }
+
+ for (k = 0; k < n; k++) {
+ struct s5p_jpeg_fmt *fmt = &formats[k];
+ if (fmt->fourcc == pixelformat)
+ return fmt;
+ }
+
+ return NULL;
+
+}
+
+static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+ unsigned int walign,
+ u32 *h, unsigned int hmin, unsigned int hmax,
+ unsigned int halign)
+{
+ int width, height, w_step, h_step;
+
+ width = *w;
+ height = *h;
+
+ w_step = 1 << walign;
+ h_step = 1 << halign;
+ v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
+
+ if (*w < width && (*w + w_step) < wmax)
+ *w += w_step;
+ if (*h < height && (*h + h_step) < hmax)
+ *h += h_step;
+
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
+ struct s5p_jpeg_ctx *ctx, int q_type)
+{
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ if (q_type == MEM2MEM_OUTPUT)
+ jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, 0,
+ &pix->height, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, 0);
+ else
+ jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, fmt->h_align,
+ &pix->height, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, fmt->v_align);
+
+ if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
+ if (pix->sizeimage <= 0)
+ pix->sizeimage = PAGE_SIZE;
+ pix->bytesperline = 0;
+ } else {
+ u32 bpl = pix->bytesperline;
+
+ if (fmt->colplanes > 1 && bpl < pix->width)
+ bpl = pix->width; /* planar */
+
+ if (fmt->colplanes == 1 && /* packed */
+ (bpl << 3) * fmt->depth < pix->width)
+ bpl = (pix->width * fmt->depth) >> 3;
+
+ pix->bytesperline = bpl;
+ pix->sizeimage = (pix->width * pix->height * fmt->depth) >> 3;
+ }
+
+ return 0;
+}
+
+static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_jpeg_fmt *fmt;
+
+ fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->jpeg->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE);
+}
+
+static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_jpeg_fmt *fmt;
+
+ fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->jpeg->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT);
+}
+
+static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct s5p_jpeg_q_data *q_data = NULL;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ct, f->type);
+ BUG_ON(q_data == NULL);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ct->jpeg->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat);
+ q_data->w = pix->width;
+ q_data->h = pix->height;
+ if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)
+ q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
+ else
+ q_data->size = pix->sizeimage;
+
+ return 0;
+}
+
+static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = s5p_jpeg_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
+}
+
+static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = s5p_jpeg_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
+}
+
+static int s5p_jpeg_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int s5p_jpeg_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int s5p_jpeg_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int s5p_jpeg_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int s5p_jpeg_g_selection(struct file *file, void *priv,
+ struct v4l2_selection *s)
+{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* For JPEG blob active == default == bounds */
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ s->r.width = ctx->out_q.w;
+ s->r.height = ctx->out_q.h;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r.width = ctx->cap_q.w;
+ s->r.height = ctx->cap_q.h;
+ break;
+ default:
+ return -EINVAL;
+ }
+ s->r.left = 0;
+ s->r.top = 0;
+ return 0;
+}
+
+/*
+ * V4L2 controls
+ */
+
+static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ unsigned long flags;
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ spin_lock_irqsave(&jpeg->slock, flags);
+
+ WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
+ if (ctx->subsampling > 2)
+ ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
+ else
+ ctrl->val = ctx->subsampling;
+ spin_unlock_irqrestore(&jpeg->slock, flags);
+ break;
+ }
+
+ return 0;
+}
+
+static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
+ break;
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
+ ctx->restart_interval = ctrl->val;
+ break;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ ctx->subsampling = ctrl->val;
+ break;
+ }
+
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
+ .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
+ .s_ctrl = s5p_jpeg_s_ctrl,
+};
+
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
+{
+ unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, 3, 1, 3);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_RESTART_INTERVAL,
+ 0, 3, 0xffff, 0);
+ mask = ~0x06; /* 422, 420 */
+ }
+
+ ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422);
+
+ if (ctx->ctrl_handler.error)
+ return ctx->ctrl_handler.error;
+
+ if (ctx->mode == S5P_JPEG_DECODE)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
+ .vidioc_querycap = s5p_jpeg_querycap,
+
+ .vidioc_enum_fmt_vid_cap = s5p_jpeg_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_out = s5p_jpeg_enum_fmt_vid_out,
+
+ .vidioc_g_fmt_vid_cap = s5p_jpeg_g_fmt,
+ .vidioc_g_fmt_vid_out = s5p_jpeg_g_fmt,
+
+ .vidioc_try_fmt_vid_cap = s5p_jpeg_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out = s5p_jpeg_try_fmt_vid_out,
+
+ .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out,
+
+ .vidioc_reqbufs = s5p_jpeg_reqbufs,
+ .vidioc_querybuf = s5p_jpeg_querybuf,
+
+ .vidioc_qbuf = s5p_jpeg_qbuf,
+ .vidioc_dqbuf = s5p_jpeg_dqbuf,
+
+ .vidioc_streamon = s5p_jpeg_streamon,
+ .vidioc_streamoff = s5p_jpeg_streamoff,
+
+ .vidioc_g_selection = s5p_jpeg_g_selection,
+};
+
+/*
+ * ============================================================================
+ * mem2mem callbacks
+ * ============================================================================
+ */
+
+static void s5p_jpeg_device_run(void *priv)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned long src_addr, dst_addr;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+
+ jpeg_reset(jpeg->regs);
+ jpeg_poweron(jpeg->regs);
+ jpeg_proc_mode(jpeg->regs, ctx->mode);
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565)
+ jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
+ else
+ jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
+ jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
+ jpeg_dri(jpeg->regs, ctx->restart_interval);
+ jpeg_x(jpeg->regs, ctx->out_q.w);
+ jpeg_y(jpeg->regs, ctx->out_q.h);
+ jpeg_imgadr(jpeg->regs, src_addr);
+ jpeg_jpgadr(jpeg->regs, dst_addr);
+
+ /* ultimately comes from sizeimage from userspace */
+ jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);
+
+ /* JPEG RGB to YCbCr conversion matrix */
+ jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11);
+ jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12);
+ jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13);
+ jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21);
+ jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22);
+ jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23);
+ jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31);
+ jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32);
+ jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);
+
+ /*
+ * JPEG IP allows storing 4 quantization tables
+ * We fill table 0 for luma and table 1 for chroma
+ */
+ jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
+ jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
+ /* use table 0 for Y */
+ jpeg_qtbl(jpeg->regs, 1, 0);
+ /* use table 1 for Cb and Cr*/
+ jpeg_qtbl(jpeg->regs, 2, 1);
+ jpeg_qtbl(jpeg->regs, 3, 1);
+
+ /* Y, Cb, Cr use Huffman table 0 */
+ jpeg_htbl_ac(jpeg->regs, 1);
+ jpeg_htbl_dc(jpeg->regs, 1);
+ jpeg_htbl_ac(jpeg->regs, 2);
+ jpeg_htbl_dc(jpeg->regs, 2);
+ jpeg_htbl_ac(jpeg->regs, 3);
+ jpeg_htbl_dc(jpeg->regs, 3);
+ } else { /* S5P_JPEG_DECODE */
+ jpeg_rst_int_enable(jpeg->regs, true);
+ jpeg_data_num_int_enable(jpeg->regs, true);
+ jpeg_final_mcu_num_int_enable(jpeg->regs, true);
+ if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
+ else
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
+ jpeg_jpgadr(jpeg->regs, src_addr);
+ jpeg_imgadr(jpeg->regs, dst_addr);
+ }
+
+ jpeg_start(jpeg->regs);
+}
+
+static int s5p_jpeg_job_ready(void *priv)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+
+ if (ctx->mode == S5P_JPEG_DECODE)
+ return ctx->hdr_parsed;
+ return 1;
+}
+
+static void s5p_jpeg_job_abort(void *priv)
+{
+}
+
+static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {
+ .device_run = s5p_jpeg_device_run,
+ .job_ready = s5p_jpeg_job_ready,
+ .job_abort = s5p_jpeg_job_abort,
+};
+
+/*
+ * ============================================================================
+ * Queue operations
+ * ============================================================================
+ */
+
+static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+ struct s5p_jpeg_q_data *q_data = NULL;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+ BUG_ON(q_data == NULL);
+
+ size = q_data->size;
+
+ /*
+ * header is parsed during decoding and parsed information stored
+ * in the context so we do not allow another buffer to overwrite it
+ */
+ if (ctx->mode == S5P_JPEG_DECODE)
+ count = 1;
+
+ *nbuffers = count;
+ *nplanes = 1;
+ sizes[0] = size;
+ alloc_ctxs[0] = ctx->jpeg->alloc_ctx;
+
+ return 0;
+}
+
+static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct s5p_jpeg_q_data *q_data = NULL;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+ BUG_ON(q_data == NULL);
+
+ if (vb2_plane_size(vb, 0) < q_data->size) {
+ pr_err("%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->size);
+
+ return 0;
+}
+
+static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (ctx->mode == S5P_JPEG_DECODE &&
+ vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ struct s5p_jpeg_q_data tmp, *q_data;
+ ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp,
+ (unsigned long)vb2_plane_vaddr(vb, 0),
+ min((unsigned long)ctx->out_q.size,
+ vb2_get_plane_payload(vb, 0)));
+ if (!ctx->hdr_parsed) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ q_data = &ctx->out_q;
+ q_data->w = tmp.w;
+ q_data->h = tmp.h;
+
+ q_data = &ctx->cap_q;
+ q_data->w = tmp.w;
+ q_data->h = tmp.h;
+
+ jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH,
+ S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align,
+ &q_data->h, S5P_JPEG_MIN_HEIGHT,
+ S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align
+ );
+ q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
+ }
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void s5p_jpeg_wait_prepare(struct vb2_queue *vq)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+
+ mutex_unlock(&ctx->jpeg->lock);
+}
+
+static void s5p_jpeg_wait_finish(struct vb2_queue *vq)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
+
+ mutex_lock(&ctx->jpeg->lock);
+}
+
+static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+ int ret;
+
+ ret = pm_runtime_get_sync(ctx->jpeg->dev);
+
+ return ret > 0 ? 0 : ret;
+}
+
+static int s5p_jpeg_stop_streaming(struct vb2_queue *q)
+{
+ struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
+
+ pm_runtime_put(ctx->jpeg->dev);
+
+ return 0;
+}
+
+static struct vb2_ops s5p_jpeg_qops = {
+ .queue_setup = s5p_jpeg_queue_setup,
+ .buf_prepare = s5p_jpeg_buf_prepare,
+ .buf_queue = s5p_jpeg_buf_queue,
+ .wait_prepare = s5p_jpeg_wait_prepare,
+ .wait_finish = s5p_jpeg_wait_finish,
+ .start_streaming = s5p_jpeg_start_streaming,
+ .stop_streaming = s5p_jpeg_stop_streaming,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct s5p_jpeg_ctx *ctx = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &s5p_jpeg_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &s5p_jpeg_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * ============================================================================
+ * ISR
+ * ============================================================================
+ */
+
+static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
+{
+ struct s5p_jpeg *jpeg = dev_id;
+ struct s5p_jpeg_ctx *curr_ctx;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned long payload_size = 0;
+ enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
+ bool enc_jpeg_too_large = false;
+ bool timer_elapsed = false;
+ bool op_completed = false;
+
+ spin_lock(&jpeg->slock);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
+
+ src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ if (curr_ctx->mode == S5P_JPEG_ENCODE)
+ enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs);
+ timer_elapsed = jpeg_timer_stat(jpeg->regs);
+ op_completed = jpeg_result_stat_ok(jpeg->regs);
+ if (curr_ctx->mode == S5P_JPEG_DECODE)
+ op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs);
+
+ if (enc_jpeg_too_large) {
+ state = VB2_BUF_STATE_ERROR;
+ jpeg_clear_enc_stream_stat(jpeg->regs);
+ } else if (timer_elapsed) {
+ state = VB2_BUF_STATE_ERROR;
+ jpeg_clear_timer_stat(jpeg->regs);
+ } else if (!op_completed) {
+ state = VB2_BUF_STATE_ERROR;
+ } else {
+ payload_size = jpeg_compressed_size(jpeg->regs);
+ }
+
+ v4l2_m2m_buf_done(src_buf, state);
+ if (curr_ctx->mode == S5P_JPEG_ENCODE)
+ vb2_set_plane_payload(dst_buf, 0, payload_size);
+ v4l2_m2m_buf_done(dst_buf, state);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
+
+ curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
+ spin_unlock(&jpeg->slock);
+
+ jpeg_clear_int(jpeg->regs);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * ============================================================================
+ * Driver basic infrastructure
+ * ============================================================================
+ */
+
+static int s5p_jpeg_probe(struct platform_device *pdev)
+{
+ struct s5p_jpeg *jpeg;
+ struct resource *res;
+ int ret;
+
+ /* JPEG IP abstraction struct */
+ jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL);
+ if (!jpeg)
+ return -ENOMEM;
+
+ mutex_init(&jpeg->lock);
+ spin_lock_init(&jpeg->slock);
+ jpeg->dev = &pdev->dev;
+
+ /* memory-mapped registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ jpeg->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (jpeg->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ /* interrupt service routine registration */
+ jpeg->irq = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0,
+ dev_name(&pdev->dev), jpeg);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);
+ return ret;
+ }
+
+ /* clocks */
+ jpeg->clk = clk_get(&pdev->dev, "jpeg");
+ if (IS_ERR(jpeg->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ ret = PTR_ERR(jpeg->clk);
+ return ret;
+ }
+ dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk);
+ clk_enable(jpeg->clk);
+
+ /* v4l2 device */
+ ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ goto clk_get_rollback;
+ }
+
+ /* mem2mem device */
+ jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops);
+ if (IS_ERR(jpeg->m2m_dev)) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(jpeg->m2m_dev);
+ goto device_register_rollback;
+ }
+
+ jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(jpeg->alloc_ctx)) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n");
+ ret = PTR_ERR(jpeg->alloc_ctx);
+ goto m2m_init_rollback;
+ }
+
+ /* JPEG encoder /dev/videoX node */
+ jpeg->vfd_encoder = video_device_alloc();
+ if (!jpeg->vfd_encoder) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto vb2_allocator_rollback;
+ }
+ strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME,
+ sizeof(jpeg->vfd_encoder->name));
+ jpeg->vfd_encoder->fops = &s5p_jpeg_fops;
+ jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
+ jpeg->vfd_encoder->minor = -1;
+ jpeg->vfd_encoder->release = video_device_release;
+ jpeg->vfd_encoder->lock = &jpeg->lock;
+ jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev;
+ jpeg->vfd_encoder->vfl_dir = VFL_DIR_M2M;
+
+ ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
+ goto enc_vdev_alloc_rollback;
+ }
+
+ video_set_drvdata(jpeg->vfd_encoder, jpeg);
+ v4l2_info(&jpeg->v4l2_dev,
+ "encoder device registered as /dev/video%d\n",
+ jpeg->vfd_encoder->num);
+
+ /* JPEG decoder /dev/videoX node */
+ jpeg->vfd_decoder = video_device_alloc();
+ if (!jpeg->vfd_decoder) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto enc_vdev_register_rollback;
+ }
+ strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME,
+ sizeof(jpeg->vfd_decoder->name));
+ jpeg->vfd_decoder->fops = &s5p_jpeg_fops;
+ jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
+ jpeg->vfd_decoder->minor = -1;
+ jpeg->vfd_decoder->release = video_device_release;
+ jpeg->vfd_decoder->lock = &jpeg->lock;
+ jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev;
+
+ ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
+ goto dec_vdev_alloc_rollback;
+ }
+
+ video_set_drvdata(jpeg->vfd_decoder, jpeg);
+ v4l2_info(&jpeg->v4l2_dev,
+ "decoder device registered as /dev/video%d\n",
+ jpeg->vfd_decoder->num);
+
+ /* final statements & power management */
+ platform_set_drvdata(pdev, jpeg);
+
+ pm_runtime_enable(&pdev->dev);
+
+ v4l2_info(&jpeg->v4l2_dev, "Samsung S5P JPEG codec\n");
+
+ return 0;
+
+dec_vdev_alloc_rollback:
+ video_device_release(jpeg->vfd_decoder);
+
+enc_vdev_register_rollback:
+ video_unregister_device(jpeg->vfd_encoder);
+
+enc_vdev_alloc_rollback:
+ video_device_release(jpeg->vfd_encoder);
+
+vb2_allocator_rollback:
+ vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+
+m2m_init_rollback:
+ v4l2_m2m_release(jpeg->m2m_dev);
+
+device_register_rollback:
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+
+clk_get_rollback:
+ clk_disable(jpeg->clk);
+ clk_put(jpeg->clk);
+
+ return ret;
+}
+
+static int s5p_jpeg_remove(struct platform_device *pdev)
+{
+ struct s5p_jpeg *jpeg = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(jpeg->dev);
+
+ video_unregister_device(jpeg->vfd_decoder);
+ video_device_release(jpeg->vfd_decoder);
+ video_unregister_device(jpeg->vfd_encoder);
+ video_device_release(jpeg->vfd_encoder);
+ vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
+ v4l2_m2m_release(jpeg->m2m_dev);
+ v4l2_device_unregister(&jpeg->v4l2_dev);
+
+ clk_disable(jpeg->clk);
+ clk_put(jpeg->clk);
+
+ return 0;
+}
+
+static int s5p_jpeg_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int s5p_jpeg_runtime_resume(struct device *dev)
+{
+ struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
+ /*
+ * JPEG IP allows storing two Huffman tables for each component
+ * We fill table 0 for each component
+ */
+ jpeg_set_hdctbl(jpeg->regs);
+ jpeg_set_hdctblg(jpeg->regs);
+ jpeg_set_hactbl(jpeg->regs);
+ jpeg_set_hactblg(jpeg->regs);
+ return 0;
+}
+
+static const struct dev_pm_ops s5p_jpeg_pm_ops = {
+ .runtime_suspend = s5p_jpeg_runtime_suspend,
+ .runtime_resume = s5p_jpeg_runtime_resume,
+};
+
+static struct platform_driver s5p_jpeg_driver = {
+ .probe = s5p_jpeg_probe,
+ .remove = s5p_jpeg_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = S5P_JPEG_M2M_NAME,
+ .pm = &s5p_jpeg_pm_ops,
+ },
+};
+
+module_platform_driver(s5p_jpeg_driver);
+
+MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
+MODULE_DESCRIPTION("Samsung JPEG codec driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/s5p-jpeg/jpeg-core.h
new file mode 100644
index 000000000000..022b9b9baff9
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.h
@@ -0,0 +1,150 @@
+/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef JPEG_CORE_H_
+#define JPEG_CORE_H_
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+
+#define S5P_JPEG_M2M_NAME "s5p-jpeg"
+
+/* JPEG compression quality setting */
+#define S5P_JPEG_COMPR_QUAL_BEST 0
+#define S5P_JPEG_COMPR_QUAL_WORST 3
+
+/* JPEG RGB to YCbCr conversion matrix coefficients */
+#define S5P_JPEG_COEF11 0x4d
+#define S5P_JPEG_COEF12 0x97
+#define S5P_JPEG_COEF13 0x1e
+#define S5P_JPEG_COEF21 0x2c
+#define S5P_JPEG_COEF22 0x57
+#define S5P_JPEG_COEF23 0x83
+#define S5P_JPEG_COEF31 0x83
+#define S5P_JPEG_COEF32 0x6e
+#define S5P_JPEG_COEF33 0x13
+
+/* a selection of JPEG markers */
+#define TEM 0x01
+#define SOF0 0xc0
+#define RST 0xd0
+#define SOI 0xd8
+#define EOI 0xd9
+#define DHP 0xde
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+/**
+ * struct s5p_jpeg - JPEG IP abstraction
+ * @lock: the mutex protecting this structure
+ * @slock: spinlock protecting the device contexts
+ * @v4l2_dev: v4l2 device for mem2mem mode
+ * @vfd_encoder: video device node for encoder mem2mem mode
+ * @vfd_decoder: video device node for decoder mem2mem mode
+ * @m2m_dev: v4l2 mem2mem device data
+ * @regs: JPEG IP registers mapping
+ * @irq: JPEG IP irq
+ * @clk: JPEG IP clock
+ * @dev: JPEG IP struct device
+ * @alloc_ctx: videobuf2 memory allocator's context
+ */
+struct s5p_jpeg {
+ struct mutex lock;
+ struct spinlock slock;
+
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_encoder;
+ struct video_device *vfd_decoder;
+ struct v4l2_m2m_dev *m2m_dev;
+
+ void __iomem *regs;
+ unsigned int irq;
+ struct clk *clk;
+ struct device *dev;
+ void *alloc_ctx;
+};
+
+/**
+ * struct jpeg_fmt - driver's internal color format data
+ * @name: format descritpion
+ * @fourcc: the fourcc code, 0 if not applicable
+ * @depth: number of bits per pixel
+ * @colplanes: number of color planes (1 for packed formats)
+ * @h_align: horizontal alignment order (align to 2^h_align)
+ * @v_align: vertical alignment order (align to 2^v_align)
+ * @types: types of queue this format is applicable to
+ */
+struct s5p_jpeg_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+ int colplanes;
+ int h_align;
+ int v_align;
+ u32 types;
+};
+
+/**
+ * s5p_jpeg_q_data - parameters of one queue
+ * @fmt: driver-specific format of this queue
+ * @w: image width
+ * @h: image height
+ * @size: image buffer size in bytes
+ */
+struct s5p_jpeg_q_data {
+ struct s5p_jpeg_fmt *fmt;
+ u32 w;
+ u32 h;
+ u32 size;
+};
+
+/**
+ * s5p_jpeg_ctx - the device context data
+ * @jpeg: JPEG IP device for this context
+ * @mode: compression (encode) operation or decompression (decode)
+ * @compr_quality: destination image quality in compression (encode) mode
+ * @m2m_ctx: mem2mem device context
+ * @out_q: source (output) queue information
+ * @cap_fmt: destination (capture) queue queue information
+ * @hdr_parsed: set if header has been parsed during decompression
+ * @ctrl_handler: controls handler
+ */
+struct s5p_jpeg_ctx {
+ struct s5p_jpeg *jpeg;
+ unsigned int mode;
+ unsigned short compr_quality;
+ unsigned short restart_interval;
+ unsigned short subsampling;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct s5p_jpeg_q_data out_q;
+ struct s5p_jpeg_q_data cap_q;
+ struct v4l2_fh fh;
+ bool hdr_parsed;
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/**
+ * s5p_jpeg_buffer - description of memory containing input JPEG data
+ * @size: buffer size
+ * @curr: current position in the buffer
+ * @data: pointer to the data
+ */
+struct s5p_jpeg_buffer {
+ unsigned long size;
+ unsigned long curr;
+ unsigned long data;
+};
+
+#endif /* JPEG_CORE_H */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw.h b/drivers/media/platform/s5p-jpeg/jpeg-hw.h
new file mode 100644
index 000000000000..b47e887b6138
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-hw.h
@@ -0,0 +1,357 @@
+/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef JPEG_HW_H_
+#define JPEG_HW_H_
+
+#include <linux/io.h>
+#include <linux/videodev2.h>
+
+#include "jpeg-hw.h"
+#include "jpeg-regs.h"
+
+#define S5P_JPEG_MIN_WIDTH 32
+#define S5P_JPEG_MIN_HEIGHT 32
+#define S5P_JPEG_MAX_WIDTH 8192
+#define S5P_JPEG_MAX_HEIGHT 8192
+#define S5P_JPEG_ENCODE 0
+#define S5P_JPEG_DECODE 1
+#define S5P_JPEG_RAW_IN_565 0
+#define S5P_JPEG_RAW_IN_422 1
+#define S5P_JPEG_RAW_OUT_422 0
+#define S5P_JPEG_RAW_OUT_420 1
+
+static inline void jpeg_reset(void __iomem *regs)
+{
+ unsigned long reg;
+
+ writel(1, regs + S5P_JPG_SW_RESET);
+ reg = readl(regs + S5P_JPG_SW_RESET);
+ /* no other way but polling for when JPEG IP becomes operational */
+ while (reg != 0) {
+ cpu_relax();
+ reg = readl(regs + S5P_JPG_SW_RESET);
+ }
+}
+
+static inline void jpeg_poweron(void __iomem *regs)
+{
+ writel(S5P_POWER_ON, regs + S5P_JPGCLKCON);
+}
+
+static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
+{
+ unsigned long reg, m;
+
+ m = S5P_MOD_SEL_565;
+ if (mode == S5P_JPEG_RAW_IN_565)
+ m = S5P_MOD_SEL_565;
+ else if (mode == S5P_JPEG_RAW_IN_422)
+ m = S5P_MOD_SEL_422;
+
+ reg = readl(regs + S5P_JPGCMOD);
+ reg &= ~S5P_MOD_SEL_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGCMOD);
+}
+
+static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGCMOD);
+ if (y16)
+ reg |= S5P_MODE_Y16;
+ else
+ reg &= ~S5P_MODE_Y16_MASK;
+ writel(reg, regs + S5P_JPGCMOD);
+}
+
+static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
+{
+ unsigned long reg, m;
+
+ m = S5P_PROC_MODE_DECOMPR;
+ if (mode == S5P_JPEG_ENCODE)
+ m = S5P_PROC_MODE_COMPR;
+ else
+ m = S5P_PROC_MODE_DECOMPR;
+ reg = readl(regs + S5P_JPGMOD);
+ reg &= ~S5P_PROC_MODE_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGMOD);
+}
+
+static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
+{
+ unsigned long reg, m;
+
+ if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
+ m = S5P_SUBSAMPLING_MODE_420;
+ else
+ m = S5P_SUBSAMPLING_MODE_422;
+
+ reg = readl(regs + S5P_JPGMOD);
+ reg &= ~S5P_SUBSAMPLING_MODE_MASK;
+ reg |= m;
+ writel(reg, regs + S5P_JPGMOD);
+}
+
+static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
+{
+ return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
+}
+
+static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGDRI_U);
+ reg &= ~0xff;
+ reg |= (dri >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGDRI_U);
+
+ reg = readl(regs + S5P_JPGDRI_L);
+ reg &= ~0xff;
+ reg |= dri & 0xff;
+ writel(reg, regs + S5P_JPGDRI_L);
+}
+
+static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_QTBL);
+ reg &= ~S5P_QT_NUMt_MASK(t);
+ reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t);
+ writel(reg, regs + S5P_JPG_QTBL);
+}
+
+static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_HTBL);
+ reg &= ~S5P_HT_NUMt_AC_MASK(t);
+ /* this driver uses table 0 for all color components */
+ reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t);
+ writel(reg, regs + S5P_JPG_HTBL);
+}
+
+static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_HTBL);
+ reg &= ~S5P_HT_NUMt_DC_MASK(t);
+ /* this driver uses table 0 for all color components */
+ reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t);
+ writel(reg, regs + S5P_JPG_HTBL);
+}
+
+static inline void jpeg_y(void __iomem *regs, unsigned int y)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGY_U);
+ reg &= ~0xff;
+ reg |= (y >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGY_U);
+
+ reg = readl(regs + S5P_JPGY_L);
+ reg &= ~0xff;
+ reg |= y & 0xff;
+ writel(reg, regs + S5P_JPGY_L);
+}
+
+static inline void jpeg_x(void __iomem *regs, unsigned int x)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGX_U);
+ reg &= ~0xff;
+ reg |= (x >> 8) & 0xff;
+ writel(reg, regs + S5P_JPGX_U);
+
+ reg = readl(regs + S5P_JPGX_L);
+ reg &= ~0xff;
+ reg |= x & 0xff;
+ writel(reg, regs + S5P_JPGX_L);
+}
+
+static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_RSTm_INT_EN_MASK;
+ if (enable)
+ reg |= S5P_RSTm_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_DATA_NUM_INT_EN_MASK;
+ if (enable)
+ reg |= S5P_DATA_NUM_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTSE);
+ reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK;
+ if (enbl)
+ reg |= S5P_FINAL_MCU_NUM_INT_EN;
+ writel(reg, regs + S5P_JPGINTSE);
+}
+
+static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg |= S5P_TIMER_INT_EN;
+ reg &= ~S5P_TIMER_INIT_MASK;
+ reg |= val & S5P_TIMER_INIT_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline void jpeg_timer_disable(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg &= ~S5P_TIMER_INT_EN_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline int jpeg_timer_stat(void __iomem *regs)
+{
+ return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)
+ >> S5P_TIMER_INT_STAT_SHIFT);
+}
+
+static inline void jpeg_clear_timer_stat(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_TIMER_SE);
+ reg &= ~S5P_TIMER_INT_STAT_MASK;
+ writel(reg, regs + S5P_JPG_TIMER_SE);
+}
+
+static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
+ reg &= ~S5P_ENC_STREAM_BOUND_MASK;
+ reg |= S5P_ENC_STREAM_INT_EN;
+ reg |= size & S5P_ENC_STREAM_BOUND_MASK;
+ writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
+}
+
+static inline int jpeg_enc_stream_stat(void __iomem *regs)
+{
+ return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) &
+ S5P_ENC_STREAM_INT_STAT_MASK);
+}
+
+static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
+ reg &= ~S5P_ENC_STREAM_INT_MASK;
+ writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
+}
+
+static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)
+{
+ unsigned long reg, f;
+
+ f = S5P_DEC_OUT_FORMAT_422;
+ if (format == S5P_JPEG_RAW_OUT_422)
+ f = S5P_DEC_OUT_FORMAT_422;
+ else if (format == S5P_JPEG_RAW_OUT_420)
+ f = S5P_DEC_OUT_FORMAT_420;
+ reg = readl(regs + S5P_JPG_OUTFORM);
+ reg &= ~S5P_DEC_OUT_FORMAT_MASK;
+ reg |= f;
+ writel(reg, regs + S5P_JPG_OUTFORM);
+}
+
+static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr)
+{
+ writel(addr, regs + S5P_JPG_JPGADR);
+}
+
+static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr)
+{
+ writel(addr, regs + S5P_JPG_IMGADR);
+}
+
+static inline void jpeg_coef(void __iomem *regs, unsigned int i,
+ unsigned int j, unsigned int coef)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPG_COEF(i));
+ reg &= ~S5P_COEFn_MASK(j);
+ reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j);
+ writel(reg, regs + S5P_JPG_COEF(i));
+}
+
+static inline void jpeg_start(void __iomem *regs)
+{
+ writel(1, regs + S5P_JSTART);
+}
+
+static inline int jpeg_result_stat_ok(void __iomem *regs)
+{
+ return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK)
+ >> S5P_RESULT_STAT_SHIFT);
+}
+
+static inline int jpeg_stream_stat_ok(void __iomem *regs)
+{
+ return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK)
+ >> S5P_STREAM_STAT_SHIFT);
+}
+
+static inline void jpeg_clear_int(void __iomem *regs)
+{
+ unsigned long reg;
+
+ reg = readl(regs + S5P_JPGINTST);
+ writel(S5P_INT_RELEASE, regs + S5P_JPGCOM);
+ reg = readl(regs + S5P_JPGOPR);
+}
+
+static inline unsigned int jpeg_compressed_size(void __iomem *regs)
+{
+ unsigned long jpeg_size = 0;
+
+ jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16;
+ jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8;
+ jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff);
+
+ return (unsigned int)jpeg_size;
+}
+
+#endif /* JPEG_HW_H_ */
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
new file mode 100644
index 000000000000..38e50815668c
--- /dev/null
+++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h
@@ -0,0 +1,170 @@
+/* linux/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+ *
+ * Register definition file for Samsung JPEG codec driver
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef JPEG_REGS_H_
+#define JPEG_REGS_H_
+
+/* JPEG mode register */
+#define S5P_JPGMOD 0x00
+#define S5P_PROC_MODE_MASK (0x1 << 3)
+#define S5P_PROC_MODE_DECOMPR (0x1 << 3)
+#define S5P_PROC_MODE_COMPR (0x0 << 3)
+#define S5P_SUBSAMPLING_MODE_MASK 0x7
+#define S5P_SUBSAMPLING_MODE_444 (0x0 << 0)
+#define S5P_SUBSAMPLING_MODE_422 (0x1 << 0)
+#define S5P_SUBSAMPLING_MODE_420 (0x2 << 0)
+#define S5P_SUBSAMPLING_MODE_GRAY (0x3 << 0)
+
+/* JPEG operation status register */
+#define S5P_JPGOPR 0x04
+
+/* Quantization tables*/
+#define S5P_JPG_QTBL 0x08
+#define S5P_QT_NUMt_SHIFT(t) (((t) - 1) << 1)
+#define S5P_QT_NUMt_MASK(t) (0x3 << S5P_QT_NUMt_SHIFT(t))
+
+/* Huffman tables */
+#define S5P_JPG_HTBL 0x0c
+#define S5P_HT_NUMt_AC_SHIFT(t) (((t) << 1) - 1)
+#define S5P_HT_NUMt_AC_MASK(t) (0x1 << S5P_HT_NUMt_AC_SHIFT(t))
+
+#define S5P_HT_NUMt_DC_SHIFT(t) (((t) - 1) << 1)
+#define S5P_HT_NUMt_DC_MASK(t) (0x1 << S5P_HT_NUMt_DC_SHIFT(t))
+
+/* JPEG restart interval register upper byte */
+#define S5P_JPGDRI_U 0x10
+
+/* JPEG restart interval register lower byte */
+#define S5P_JPGDRI_L 0x14
+
+/* JPEG vertical resolution register upper byte */
+#define S5P_JPGY_U 0x18
+
+/* JPEG vertical resolution register lower byte */
+#define S5P_JPGY_L 0x1c
+
+/* JPEG horizontal resolution register upper byte */
+#define S5P_JPGX_U 0x20
+
+/* JPEG horizontal resolution register lower byte */
+#define S5P_JPGX_L 0x24
+
+/* JPEG byte count register upper byte */
+#define S5P_JPGCNT_U 0x28
+
+/* JPEG byte count register middle byte */
+#define S5P_JPGCNT_M 0x2c
+
+/* JPEG byte count register lower byte */
+#define S5P_JPGCNT_L 0x30
+
+/* JPEG interrupt setting register */
+#define S5P_JPGINTSE 0x34
+#define S5P_RSTm_INT_EN_MASK (0x1 << 7)
+#define S5P_RSTm_INT_EN (0x1 << 7)
+#define S5P_DATA_NUM_INT_EN_MASK (0x1 << 6)
+#define S5P_DATA_NUM_INT_EN (0x1 << 6)
+#define S5P_FINAL_MCU_NUM_INT_EN_MASK (0x1 << 5)
+#define S5P_FINAL_MCU_NUM_INT_EN (0x1 << 5)
+
+/* JPEG interrupt status register */
+#define S5P_JPGINTST 0x38
+#define S5P_RESULT_STAT_SHIFT 6
+#define S5P_RESULT_STAT_MASK (0x1 << S5P_RESULT_STAT_SHIFT)
+#define S5P_STREAM_STAT_SHIFT 5
+#define S5P_STREAM_STAT_MASK (0x1 << S5P_STREAM_STAT_SHIFT)
+
+/* JPEG command register */
+#define S5P_JPGCOM 0x4c
+#define S5P_INT_RELEASE (0x1 << 2)
+
+/* Raw image data r/w address register */
+#define S5P_JPG_IMGADR 0x50
+
+/* JPEG file r/w address register */
+#define S5P_JPG_JPGADR 0x58
+
+/* Coefficient for RGB-to-YCbCr converter register */
+#define S5P_JPG_COEF(n) (0x5c + (((n) - 1) << 2))
+#define S5P_COEFn_SHIFT(j) ((3 - (j)) << 3)
+#define S5P_COEFn_MASK(j) (0xff << S5P_COEFn_SHIFT(j))
+
+/* JPEG color mode register */
+#define S5P_JPGCMOD 0x68
+#define S5P_MOD_SEL_MASK (0x7 << 5)
+#define S5P_MOD_SEL_422 (0x1 << 5)
+#define S5P_MOD_SEL_565 (0x2 << 5)
+#define S5P_MODE_Y16_MASK (0x1 << 1)
+#define S5P_MODE_Y16 (0x1 << 1)
+
+/* JPEG clock control register */
+#define S5P_JPGCLKCON 0x6c
+#define S5P_CLK_DOWN_READY (0x1 << 1)
+#define S5P_POWER_ON (0x1 << 0)
+
+/* JPEG start register */
+#define S5P_JSTART 0x70
+
+/* JPEG SW reset register */
+#define S5P_JPG_SW_RESET 0x78
+
+/* JPEG timer setting register */
+#define S5P_JPG_TIMER_SE 0x7c
+#define S5P_TIMER_INT_EN_MASK (0x1 << 31)
+#define S5P_TIMER_INT_EN (0x1 << 31)
+#define S5P_TIMER_INIT_MASK 0x7fffffff
+
+/* JPEG timer status register */
+#define S5P_JPG_TIMER_ST 0x80
+#define S5P_TIMER_INT_STAT_SHIFT 31
+#define S5P_TIMER_INT_STAT_MASK (0x1 << S5P_TIMER_INT_STAT_SHIFT)
+#define S5P_TIMER_CNT_SHIFT 0
+#define S5P_TIMER_CNT_MASK 0x7fffffff
+
+/* JPEG decompression output format register */
+#define S5P_JPG_OUTFORM 0x88
+#define S5P_DEC_OUT_FORMAT_MASK (0x1 << 0)
+#define S5P_DEC_OUT_FORMAT_422 (0x0 << 0)
+#define S5P_DEC_OUT_FORMAT_420 (0x1 << 0)
+
+/* JPEG version register */
+#define S5P_JPG_VERSION 0x8c
+
+/* JPEG compressed stream size interrupt setting register */
+#define S5P_JPG_ENC_STREAM_INTSE 0x98
+#define S5P_ENC_STREAM_INT_MASK (0x1 << 24)
+#define S5P_ENC_STREAM_INT_EN (0x1 << 24)
+#define S5P_ENC_STREAM_BOUND_MASK 0xffffff
+
+/* JPEG compressed stream size interrupt status register */
+#define S5P_JPG_ENC_STREAM_INTST 0x9c
+#define S5P_ENC_STREAM_INT_STAT_MASK 0x1
+
+/* JPEG quantizer table register */
+#define S5P_JPG_QTBL_CONTENT(n) (0x400 + (n) * 0x100)
+
+/* JPEG DC Huffman table register */
+#define S5P_JPG_HDCTBL(n) (0x800 + (n) * 0x400)
+
+/* JPEG DC Huffman table register */
+#define S5P_JPG_HDCTBLG(n) (0x840 + (n) * 0x400)
+
+/* JPEG AC Huffman table register */
+#define S5P_JPG_HACTBL(n) (0x880 + (n) * 0x400)
+
+/* JPEG AC Huffman table register */
+#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400)
+
+#endif /* JPEG_REGS_H_ */
+
diff --git a/drivers/media/video/s5p-mfc/Makefile b/drivers/media/platform/s5p-mfc/Makefile
index d0663409af00..d0663409af00 100644
--- a/drivers/media/video/s5p-mfc/Makefile
+++ b/drivers/media/platform/s5p-mfc/Makefile
diff --git a/drivers/media/video/s5p-mfc/regs-mfc.h b/drivers/media/platform/s5p-mfc/regs-mfc.h
index a19bece41ba9..a19bece41ba9 100644
--- a/drivers/media/video/s5p-mfc/regs-mfc.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc.h
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
new file mode 100644
index 000000000000..5587ef15ca4f
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -0,0 +1,1298 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.1
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-event.h>
+#include <linux/workqueue.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+#define S5P_MFC_NAME "s5p-mfc"
+#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
+#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
+
+int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
+
+/* Helper functions for interrupt processing */
+
+/* Remove from hw execution round robin */
+void clear_work_bit(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ spin_lock(&dev->condlock);
+ __clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+}
+
+/* Add to hw execution round robin */
+void set_work_bit(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ spin_lock(&dev->condlock);
+ __set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock(&dev->condlock);
+}
+
+/* Remove from hw execution round robin */
+void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ __clear_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+}
+
+/* Add to hw execution round robin */
+void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ __set_bit(ctx->num, &dev->ctx_work_bits);
+ spin_unlock_irqrestore(&dev->condlock, flags);
+}
+
+/* Wake up context wait_queue */
+static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
+ unsigned int err)
+{
+ ctx->int_cond = 1;
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ wake_up(&ctx->queue);
+}
+
+/* Wake up device wait_queue */
+static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
+ unsigned int err)
+{
+ dev->int_cond = 1;
+ dev->int_type = reason;
+ dev->int_err = err;
+ wake_up(&dev->queue);
+}
+
+static void s5p_mfc_watchdog(unsigned long arg)
+{
+ struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
+
+ if (test_bit(0, &dev->hw_lock))
+ atomic_inc(&dev->watchdog_cnt);
+ if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
+ /* This means that hw is busy and no interrupts were
+ * generated by hw for the Nth time of running this
+ * watchdog timer. This usually means a serious hw
+ * error. Now it is time to kill all instances and
+ * reset the MFC. */
+ mfc_err("Time out during waiting for HW\n");
+ queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
+ }
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+}
+
+static void s5p_mfc_watchdog_worker(struct work_struct *work)
+{
+ struct s5p_mfc_dev *dev;
+ struct s5p_mfc_ctx *ctx;
+ unsigned long flags;
+ int mutex_locked;
+ int i, ret;
+
+ dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
+
+ mfc_err("Driver timeout error handling\n");
+ /* Lock the mutex that protects open and release.
+ * This is necessary as they may load and unload firmware. */
+ mutex_locked = mutex_trylock(&dev->mfc_mutex);
+ if (!mutex_locked)
+ mfc_err("Error: some instance may be closing/opening\n");
+ spin_lock_irqsave(&dev->irqlock, flags);
+
+ s5p_mfc_clock_off();
+
+ for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
+ ctx = dev->ctx[i];
+ if (!ctx)
+ continue;
+ ctx->state = MFCINST_ERROR;
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_ERR_RET, 0);
+ }
+ clear_bit(0, &dev->hw_lock);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ /* Double check if there is at least one instance running.
+ * If no instance is in memory than no firmware should be present */
+ if (dev->num_inst > 0) {
+ ret = s5p_mfc_reload_firmware(dev);
+ if (ret) {
+ mfc_err("Failed to reload FW\n");
+ goto unlock;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ mfc_err("Failed to reinit FW\n");
+ }
+unlock:
+ if (mutex_locked)
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (!vdev) {
+ mfc_err("failed to get video_device");
+ return MFCNODE_INVALID;
+ }
+ if (vdev->index == 0)
+ return MFCNODE_DECODER;
+ else if (vdev->index == 1)
+ return MFCNODE_ENCODER;
+ return MFCNODE_INVALID;
+}
+
+static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
+}
+
+static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *dst_buf;
+
+ ctx->state = MFCINST_FINISHED;
+ ctx->sequence++;
+ while (!list_empty(&ctx->dst_queue)) {
+ dst_buf = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ mfc_debug(2, "Cleaning up buffer: %d\n",
+ dst_buf->b->v4l2_buf.index);
+ vb2_set_plane_payload(dst_buf->b, 0, 0);
+ vb2_set_plane_payload(dst_buf->b, 1, 0);
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
+
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
+
+ ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
+ vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
+ }
+}
+
+static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf, *src_buf;
+ size_t dec_y_addr = s5p_mfc_get_dec_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+
+ /* Copy timestamp / timecode from decoded src to dst and set
+ appropraite flags */
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
+ memcpy(&dst_buf->b->v4l2_buf.timecode,
+ &src_buf->b->v4l2_buf.timecode,
+ sizeof(struct v4l2_timecode));
+ memcpy(&dst_buf->b->v4l2_buf.timestamp,
+ &src_buf->b->v4l2_buf.timestamp,
+ sizeof(struct timeval));
+ switch (frame_type) {
+ case S5P_FIMV_DECODE_FRAME_I_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_P_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_DECODE_FRAME_B_FRAME:
+ dst_buf->b->v4l2_buf.flags |=
+ V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_buf;
+ size_t dspl_y_addr = s5p_mfc_get_dspl_y_adr();
+ unsigned int frame_type = s5p_mfc_get_frame_type();
+ unsigned int index;
+
+ /* If frame is same as previous then skip and do not dequeue */
+ if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
+ if (!ctx->after_packed_pb)
+ ctx->sequence++;
+ ctx->after_packed_pb = 0;
+ return;
+ }
+ ctx->sequence++;
+ /* The MFC returns address of the buffer, now we have to
+ * check which videobuf does it correspond to */
+ list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
+ /* Check if this is the buffer we're looking for */
+ if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) {
+ list_del(&dst_buf->list);
+ ctx->dst_queue_cnt--;
+ dst_buf->b->v4l2_buf.sequence = ctx->sequence;
+ if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
+ s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
+ dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
+ else
+ dst_buf->b->v4l2_buf.field =
+ V4L2_FIELD_INTERLACED;
+ vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
+ vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
+ clear_bit(dst_buf->b->v4l2_buf.index,
+ &ctx->dec_dst_flag);
+
+ vb2_buffer_done(dst_buf->b,
+ err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
+
+ index = dst_buf->b->v4l2_buf.index;
+ break;
+ }
+ }
+}
+
+/* Handle frame decoding interrupt */
+static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dst_frame_status;
+ struct s5p_mfc_buf *src_buf;
+ unsigned long flags;
+ unsigned int res_change;
+
+ unsigned int index;
+
+ dst_frame_status = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
+ res_change = s5p_mfc_get_dspl_status()
+ & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK;
+ mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
+ if (ctx->state == MFCINST_RES_CHANGE_INIT)
+ ctx->state = MFCINST_RES_CHANGE_FLUSH;
+ if (res_change) {
+ ctx->state = MFCINST_RES_CHANGE_INIT;
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ return;
+ }
+ if (ctx->dpb_flush_flag)
+ ctx->dpb_flush_flag = 0;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* All frames remaining in the buffer have been extracted */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
+ if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ ctx->state = MFCINST_RES_CHANGE_END;
+ goto leave_handle_frame;
+ } else {
+ s5p_mfc_handle_frame_all_extracted(ctx);
+ }
+ }
+
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
+ s5p_mfc_handle_frame_copy_time(ctx);
+
+ /* A frame has been decoded and is in the buffer */
+ if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
+ dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
+ s5p_mfc_handle_frame_new(ctx, err);
+ } else {
+ mfc_debug(2, "No frame decode\n");
+ }
+ /* Mark source buffer as complete */
+ if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
+ && !list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ ctx->consumed_stream += s5p_mfc_get_consumed_stream();
+ if (ctx->codec_mode != S5P_FIMV_CODEC_H264_DEC &&
+ s5p_mfc_get_frame_type() == S5P_FIMV_DECODE_FRAME_P_FRAME
+ && ctx->consumed_stream + STUFF_BYTE <
+ src_buf->b->v4l2_planes[0].bytesused) {
+ /* Run MFC again on the same buffer */
+ mfc_debug(2, "Running again the same buffer\n");
+ ctx->after_packed_pb = 1;
+ } else {
+ index = src_buf->b->v4l2_buf.index;
+ mfc_debug(2, "MFC needs next buffer\n");
+ ctx->consumed_stream = 0;
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ if (s5p_mfc_err_dec(err) > 0)
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
+ }
+ }
+leave_handle_frame:
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
+ || ctx->dst_queue_cnt < ctx->dpb_count)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+}
+
+/* Error handling for interrupt */
+static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ /* If no context is available then all necessary
+ * processing has been done. */
+ if (ctx == NULL)
+ return;
+
+ dev = ctx->dev;
+ mfc_err("Interrupt Error: %08x\n", err);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+
+ /* Error recovery is dependent on the state of context */
+ switch (ctx->state) {
+ case MFCINST_INIT:
+ /* This error had to happen while acquireing instance */
+ case MFCINST_GOT_INST:
+ /* This error had to happen while parsing the header */
+ case MFCINST_HEAD_PARSED:
+ /* This error had to happen while setting dst buffers */
+ case MFCINST_RETURN_INST:
+ /* This error had to happen while releasing instance */
+ clear_work_bit(ctx);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ ctx->state = MFCINST_ERROR;
+ break;
+ case MFCINST_FINISHING:
+ case MFCINST_FINISHED:
+ case MFCINST_RUNNING:
+ /* It is higly probable that an error occured
+ * while decoding a frame */
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_ERROR;
+ /* Mark all dst buffers as having an error */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ /* Mark all src buffers as having an error */
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ break;
+ default:
+ mfc_err("Encountered an error interrupt which had not been handled\n");
+ break;
+ }
+ return;
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev;
+ unsigned int guard_width, guard_height;
+
+ if (ctx == NULL)
+ return;
+ dev = ctx->dev;
+ if (ctx->c_ops->post_seq_start) {
+ if (ctx->c_ops->post_seq_start(ctx))
+ mfc_err("post_seq_start() failed\n");
+ } else {
+ ctx->img_width = s5p_mfc_get_img_width();
+ ctx->img_height = s5p_mfc_get_img_height();
+
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->buf_height = ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ mfc_debug(2, "SEQ Done: Movie dimensions %dx%d, "
+ "buffer dimensions: %dx%d\n", ctx->img_width,
+ ctx->img_height, ctx->buf_width,
+ ctx->buf_height);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ ctx->luma_size = ALIGN(ctx->buf_width *
+ ctx->buf_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->chroma_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->img_height >> 1),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = ALIGN(ctx->buf_width *
+ ALIGN((ctx->buf_height >> 2),
+ S5P_FIMV_NV12MT_VALIGN),
+ S5P_FIMV_DEC_BUF_ALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 24,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN(ctx->img_height + 16,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(guard_width *
+ guard_height, S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->mv_size = 0;
+ }
+ ctx->dpb_count = s5p_mfc_get_dpb_count();
+ if (ctx->img_width == 0 || ctx->img_height == 0)
+ ctx->state = MFCINST_ERROR;
+ else
+ ctx->state = MFCINST_HEAD_PARSED;
+ }
+ s5p_mfc_clear_int_flags(dev);
+ clear_work_bit(ctx);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ wake_up_ctx(ctx, reason, err);
+}
+
+/* Header parsing interrupt handling */
+static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_buf *src_buf;
+ struct s5p_mfc_dev *dev;
+ unsigned long flags;
+
+ if (ctx == NULL)
+ return;
+ dev = ctx->dev;
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ clear_work_bit(ctx);
+ if (err == 0) {
+ ctx->state = MFCINST_RUNNING;
+ if (!ctx->dpb_flush_flag) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (!list_empty(&ctx->src_queue)) {
+ src_buf = list_entry(ctx->src_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&src_buf->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(src_buf->b,
+ VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ ctx->dpb_flush_flag = 0;
+ }
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ s5p_mfc_try_run(dev);
+ } else {
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+
+ s5p_mfc_clock_off();
+
+ wake_up(&ctx->queue);
+ }
+}
+
+static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx,
+ unsigned int reason, unsigned int err)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *mb_entry;
+
+ mfc_debug(2, "Stream completed");
+
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->state = MFCINST_FINISHED;
+
+ spin_lock(&dev->irqlock);
+ if (!list_empty(&ctx->dst_queue)) {
+ mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
+ list);
+ list_del(&mb_entry->list);
+ ctx->dst_queue_cnt--;
+ vb2_set_plane_payload(mb_entry->b, 0, 0);
+ vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
+ }
+ spin_unlock(&dev->irqlock);
+
+ clear_work_bit(ctx);
+
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ WARN_ON(1);
+
+ s5p_mfc_clock_off();
+ wake_up(&ctx->queue);
+ s5p_mfc_try_run(dev);
+}
+
+/* Interrupt processing */
+static irqreturn_t s5p_mfc_irq(int irq, void *priv)
+{
+ struct s5p_mfc_dev *dev = priv;
+ struct s5p_mfc_ctx *ctx;
+ unsigned int reason;
+ unsigned int err;
+
+ mfc_debug_enter();
+ /* Reset the timeout watchdog */
+ atomic_set(&dev->watchdog_cnt, 0);
+ ctx = dev->ctx[dev->curr_ctx];
+ /* Get the reason of interrupt and the error code */
+ reason = s5p_mfc_get_int_reason();
+ err = s5p_mfc_get_int_err();
+ mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
+ switch (reason) {
+ case S5P_FIMV_R2H_CMD_ERR_RET:
+ /* An error has occured */
+ if (ctx->state == MFCINST_RUNNING &&
+ s5p_mfc_err_dec(err) >= S5P_FIMV_ERR_WARNINGS_START)
+ s5p_mfc_handle_frame(ctx, reason, err);
+ else
+ s5p_mfc_handle_error(ctx, reason, err);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
+ case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
+ if (ctx->c_ops->post_frame_start) {
+ if (ctx->c_ops->post_frame_start(ctx))
+ mfc_err("post_frame_start() failed\n");
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_ctx(ctx, reason, err);
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ BUG();
+ s5p_mfc_clock_off();
+ s5p_mfc_try_run(dev);
+ } else {
+ s5p_mfc_handle_frame(ctx, reason, err);
+ }
+ break;
+
+ case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
+ s5p_mfc_handle_seq_done(ctx, reason, err);
+ break;
+
+ case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
+ ctx->inst_no = s5p_mfc_get_inst_no();
+ ctx->state = MFCINST_GOT_INST;
+ clear_work_bit(ctx);
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
+ clear_work_bit(ctx);
+ ctx->state = MFCINST_FREE;
+ wake_up(&ctx->queue);
+ goto irq_cleanup_hw;
+
+ case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
+ case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
+ case S5P_FIMV_R2H_CMD_SLEEP_RET:
+ case S5P_FIMV_R2H_CMD_WAKEUP_RET:
+ if (ctx)
+ clear_work_bit(ctx);
+ s5p_mfc_clear_int_flags(dev);
+ wake_up_dev(dev, reason, err);
+ clear_bit(0, &dev->hw_lock);
+ clear_bit(0, &dev->enter_suspend);
+ break;
+
+ case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
+ s5p_mfc_handle_init_buffers(ctx, reason, err);
+ break;
+
+ case S5P_FIMV_R2H_CMD_ENC_COMPLETE_RET:
+ s5p_mfc_handle_stream_complete(ctx, reason, err);
+ break;
+
+ default:
+ mfc_debug(2, "Unknown int reason\n");
+ s5p_mfc_clear_int_flags(dev);
+ }
+ mfc_debug_leave();
+ return IRQ_HANDLED;
+irq_cleanup_hw:
+ s5p_mfc_clear_int_flags(dev);
+ ctx->int_type = reason;
+ ctx->int_err = err;
+ ctx->int_cond = 1;
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hw\n");
+
+ s5p_mfc_clock_off();
+
+ s5p_mfc_try_run(dev);
+ mfc_debug(2, "Exit via irq_cleanup_hw\n");
+ return IRQ_HANDLED;
+}
+
+/* Open an MFC node */
+static int s5p_mfc_open(struct file *file)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = NULL;
+ struct vb2_queue *q;
+ int ret = 0;
+
+ mfc_debug_enter();
+ if (mutex_lock_interruptible(&dev->mfc_mutex))
+ return -ERESTARTSYS;
+ dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
+ /* Allocate memory for context */
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ mfc_err("Not enough memory\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+ ctx->dev = dev;
+ INIT_LIST_HEAD(&ctx->src_queue);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->src_queue_cnt = 0;
+ ctx->dst_queue_cnt = 0;
+ /* Get context number */
+ ctx->num = 0;
+ while (dev->ctx[ctx->num]) {
+ ctx->num++;
+ if (ctx->num >= MFC_NUM_CONTEXTS) {
+ mfc_err("Too many open contexts\n");
+ ret = -EBUSY;
+ goto err_no_ctx;
+ }
+ }
+ /* Mark context as idle */
+ clear_work_bit_irqsave(ctx);
+ dev->ctx[ctx->num] = ctx;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ ctx->type = MFCINST_DECODER;
+ ctx->c_ops = get_dec_codec_ops();
+ /* Setup ctrl handler */
+ ret = s5p_mfc_dec_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ ctx->type = MFCINST_ENCODER;
+ ctx->c_ops = get_enc_codec_ops();
+ /* only for encoder */
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+ /* Setup ctrl handler */
+ ret = s5p_mfc_enc_ctrls_setup(ctx);
+ if (ret) {
+ mfc_err("Failed to setup mfc controls\n");
+ goto err_ctrls_setup;
+ }
+ } else {
+ ret = -ENOENT;
+ goto err_bad_node;
+ }
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ ctx->inst_no = -1;
+ /* Load firmware if this is the first instance */
+ if (dev->num_inst == 1) {
+ dev->watchdog_timer.expires = jiffies +
+ msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
+ add_timer(&dev->watchdog_timer);
+ ret = s5p_mfc_power_on();
+ if (ret < 0) {
+ mfc_err("power on failed\n");
+ goto err_pwr_enable;
+ }
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_alloc_and_load_firmware(dev);
+ if (ret)
+ goto err_alloc_fw;
+ /* Init the FW */
+ ret = s5p_mfc_init_hw(dev);
+ if (ret)
+ goto err_init_hw;
+ s5p_mfc_clock_off();
+ }
+ /* Init videobuf2 queue for CAPTURE */
+ q = &ctx->vq_dst;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(capture)\n");
+ goto err_queue_init;
+ }
+ /* Init videobuf2 queue for OUTPUT */
+ q = &ctx->vq_src;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = &ctx->fh;
+ if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
+ q->io_modes = VB2_MMAP;
+ q->ops = get_dec_queue_ops();
+ } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = get_enc_queue_ops();
+ } else {
+ ret = -ENOENT;
+ goto err_queue_init;
+ }
+ q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ mfc_err("Failed to initialize videobuf2 queue(output)\n");
+ goto err_queue_init;
+ }
+ init_waitqueue_head(&ctx->queue);
+ mutex_unlock(&dev->mfc_mutex);
+ mfc_debug_leave();
+ return ret;
+ /* Deinit when failure occured */
+err_queue_init:
+err_init_hw:
+ s5p_mfc_release_firmware(dev);
+err_alloc_fw:
+ dev->ctx[ctx->num] = NULL;
+ del_timer_sync(&dev->watchdog_timer);
+ s5p_mfc_clock_off();
+err_pwr_enable:
+ if (dev->num_inst == 1) {
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("power off failed\n");
+ s5p_mfc_release_firmware(dev);
+ }
+err_ctrls_setup:
+ s5p_mfc_dec_ctrls_delete(ctx);
+err_bad_node:
+err_no_ctx:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+err_alloc:
+ dev->num_inst--;
+ mutex_unlock(&dev->mfc_mutex);
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Release MFC context */
+static int s5p_mfc_release(struct file *file)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_debug_enter();
+ mutex_lock(&dev->mfc_mutex);
+ s5p_mfc_clock_on();
+ vb2_queue_release(&ctx->vq_src);
+ vb2_queue_release(&ctx->vq_dst);
+ /* Mark context as idle */
+ clear_work_bit_irqsave(ctx);
+ /* If instance was initialised then
+ * return instance and free reosurces */
+ if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
+ mfc_debug(2, "Has to free instance\n");
+ ctx->state = MFCINST_RETURN_INST;
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ /* Wait until instance is returned or timeout occured */
+ if (s5p_mfc_wait_for_done_ctx
+ (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
+ s5p_mfc_clock_off();
+ mfc_err("Err returning instance\n");
+ }
+ mfc_debug(2, "After free instance\n");
+ /* Free resources */
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_release_instance_buffer(ctx);
+ if (ctx->type == MFCINST_DECODER)
+ s5p_mfc_release_dec_desc_buffer(ctx);
+
+ ctx->inst_no = MFC_NO_INSTANCE_SET;
+ }
+ /* hardware locking scheme */
+ if (dev->curr_ctx == ctx->num)
+ clear_bit(0, &dev->hw_lock);
+ dev->num_inst--;
+ if (dev->num_inst == 0) {
+ mfc_debug(2, "Last instance - release firmware\n");
+ /* reset <-> F/W release */
+ s5p_mfc_reset(dev);
+ s5p_mfc_release_firmware(dev);
+ del_timer_sync(&dev->watchdog_timer);
+ if (s5p_mfc_power_off() < 0)
+ mfc_err("Power off failed\n");
+ }
+ mfc_debug(2, "Shutting down clock\n");
+ s5p_mfc_clock_off();
+ dev->ctx[ctx->num] = NULL;
+ s5p_mfc_dec_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mfc_debug_leave();
+ mutex_unlock(&dev->mfc_mutex);
+ return 0;
+}
+
+/* Poll */
+static unsigned int s5p_mfc_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+ unsigned int rc = 0;
+ unsigned long flags;
+
+ mutex_lock(&dev->mfc_mutex);
+ src_q = &ctx->vq_src;
+ dst_q = &ctx->vq_dst;
+ /*
+ * 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;
+ }
+ mutex_unlock(&dev->mfc_mutex);
+ poll_wait(file, &ctx->fh.wait, wait);
+ poll_wait(file, &src_q->done_wq, wait);
+ poll_wait(file, &dst_q->done_wq, wait);
+ mutex_lock(&dev->mfc_mutex);
+ if (v4l2_event_pending(&ctx->fh))
+ rc |= POLLPRI;
+ 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:
+ mutex_unlock(&dev->mfc_mutex);
+ return rc;
+}
+
+/* Mmap */
+static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ int ret;
+
+ if (mutex_lock_interruptible(&dev->mfc_mutex))
+ return -ERESTARTSYS;
+ if (offset < DST_QUEUE_OFF_BASE) {
+ mfc_debug(2, "mmaping source\n");
+ ret = vb2_mmap(&ctx->vq_src, vma);
+ } else { /* capture */
+ mfc_debug(2, "mmaping destination\n");
+ vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ ret = vb2_mmap(&ctx->vq_dst, vma);
+ }
+ mutex_unlock(&dev->mfc_mutex);
+ return ret;
+}
+
+/* v4l2 ops */
+static const struct v4l2_file_operations s5p_mfc_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_mfc_open,
+ .release = s5p_mfc_release,
+ .poll = s5p_mfc_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = s5p_mfc_mmap,
+};
+
+static int match_child(struct device *dev, void *data)
+{
+ if (!dev_name(dev))
+ return 0;
+ return !strcmp(dev_name(dev), (char *)data);
+}
+
+/* MFC probe function */
+static int s5p_mfc_probe(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev;
+ struct video_device *vfd;
+ struct resource *res;
+ int ret;
+
+ pr_debug("%s++\n", __func__);
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for MFC device\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&dev->irqlock);
+ spin_lock_init(&dev->condlock);
+ dev->plat_dev = pdev;
+ if (!dev->plat_dev) {
+ dev_err(&pdev->dev, "No platform data specified\n");
+ return -ENODEV;
+ }
+
+ ret = s5p_mfc_init_pm(dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get mfc clock source\n");
+ return ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev->regs_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (dev->regs_base == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get irq resource\n");
+ ret = -ENOENT;
+ goto err_res;
+ }
+ dev->irq = res->start;
+ ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
+ IRQF_DISABLED, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+ goto err_res;
+ }
+
+ dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
+ match_child);
+ if (!dev->mem_dev_l) {
+ mfc_err("Mem child (L) device get failed\n");
+ ret = -ENODEV;
+ goto err_res;
+ }
+ dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
+ match_child);
+ if (!dev->mem_dev_r) {
+ mfc_err("Mem child (R) device get failed\n");
+ ret = -ENODEV;
+ goto err_res;
+ }
+
+ dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
+ ret = PTR_ERR(dev->alloc_ctx[0]);
+ goto err_res;
+ }
+ dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
+ if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
+ ret = PTR_ERR(dev->alloc_ctx[1]);
+ goto err_mem_init_ctx_1;
+ }
+
+ mutex_init(&dev->mfc_mutex);
+
+ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
+ if (ret)
+ goto err_v4l2_dev_reg;
+ init_waitqueue_head(&dev->queue);
+
+ /* decoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_dec_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->vfl_dir = VFL_DIR_M2M;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
+ dev->vfd_dec = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_dec_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "decoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+
+ /* encoder */
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto err_enc_alloc;
+ }
+ vfd->fops = &s5p_mfc_fops,
+ vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
+ vfd->release = video_device_release,
+ vfd->lock = &dev->mfc_mutex;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
+ dev->vfd_enc = vfd;
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
+ video_device_release(vfd);
+ goto err_enc_reg;
+ }
+ v4l2_info(&dev->v4l2_dev,
+ "encoder registered as /dev/video%d\n", vfd->num);
+ video_set_drvdata(vfd, dev);
+ platform_set_drvdata(pdev, dev);
+
+ dev->hw_lock = 0;
+ dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME);
+ INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
+ atomic_set(&dev->watchdog_cnt, 0);
+ init_timer(&dev->watchdog_timer);
+ dev->watchdog_timer.data = (unsigned long)dev;
+ dev->watchdog_timer.function = s5p_mfc_watchdog;
+
+ pr_debug("%s--\n", __func__);
+ return 0;
+
+/* Deinit MFC if probe had failed */
+err_enc_reg:
+ video_device_release(dev->vfd_enc);
+err_enc_alloc:
+ video_unregister_device(dev->vfd_dec);
+err_dec_reg:
+ video_device_release(dev->vfd_dec);
+err_dec_alloc:
+ v4l2_device_unregister(&dev->v4l2_dev);
+err_v4l2_dev_reg:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+err_mem_init_ctx_1:
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+err_res:
+ s5p_mfc_final_pm(dev);
+
+ pr_debug("%s-- with error\n", __func__);
+ return ret;
+
+}
+
+/* Remove the driver */
+static int __devexit s5p_mfc_remove(struct platform_device *pdev)
+{
+ struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
+
+ v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
+
+ del_timer_sync(&dev->watchdog_timer);
+ flush_workqueue(dev->watchdog_workqueue);
+ destroy_workqueue(dev->watchdog_workqueue);
+
+ video_unregister_device(dev->vfd_enc);
+ video_unregister_device(dev->vfd_dec);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
+ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
+
+ s5p_mfc_final_pm(dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int s5p_mfc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ if (m_dev->num_inst == 0)
+ return 0;
+
+ if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
+ mfc_err("Error: going to suspend for a second time\n");
+ return -EIO;
+ }
+
+ /* Check if we're processing then wait if it necessary. */
+ while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
+ /* Try and lock the HW */
+ /* Wait on the interrupt waitqueue */
+ ret = wait_event_interruptible_timeout(m_dev->queue,
+ m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+
+ if (ret == 0) {
+ mfc_err("Waiting for hardware to finish timed out\n");
+ return -EIO;
+ }
+ }
+
+ return s5p_mfc_sleep(m_dev);
+}
+
+static int s5p_mfc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ if (m_dev->num_inst == 0)
+ return 0;
+ return s5p_mfc_wakeup(m_dev);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int s5p_mfc_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+
+ atomic_set(&m_dev->pm.power, 0);
+ return 0;
+}
+
+static int s5p_mfc_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
+ int pre_power;
+
+ if (!m_dev->alloc_ctx)
+ return 0;
+ pre_power = atomic_read(&m_dev->pm.power);
+ atomic_set(&m_dev->pm.power, 1);
+ return 0;
+}
+#endif
+
+/* Power management */
+static const struct dev_pm_ops s5p_mfc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
+ SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver s5p_mfc_driver = {
+ .probe = s5p_mfc_probe,
+ .remove = __devexit_p(s5p_mfc_remove),
+ .driver = {
+ .name = S5P_MFC_NAME,
+ .owner = THIS_MODULE,
+ .pm = &s5p_mfc_pm_ops
+ },
+};
+
+module_platform_driver(s5p_mfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
new file mode 100644
index 000000000000..91a415573bd2
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
@@ -0,0 +1,120 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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.
+ */
+
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+/* This function is used to send a command to the MFC */
+static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd,
+ struct s5p_mfc_cmd_args *args)
+{
+ int cur_cmd;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while waiting for hardware\n");
+ return -EIO;
+ }
+ cur_cmd = mfc_read(dev, S5P_FIMV_HOST2RISC_CMD);
+ } while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
+ mfc_write(dev, args->arg[0], S5P_FIMV_HOST2RISC_ARG1);
+ mfc_write(dev, args->arg[1], S5P_FIMV_HOST2RISC_ARG2);
+ mfc_write(dev, args->arg[2], S5P_FIMV_HOST2RISC_ARG3);
+ mfc_write(dev, args->arg[3], S5P_FIMV_HOST2RISC_ARG4);
+ /* Issue the command */
+ mfc_write(dev, cmd, S5P_FIMV_HOST2RISC_CMD);
+ return 0;
+}
+
+/* Initialize the MFC */
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = dev->fw_size;
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT, &h2r_args);
+}
+
+/* Suspend the MFC hardware */
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP, &h2r_args);
+}
+
+/* Wake up the MFC hardware */
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_cmd_args h2r_args;
+
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP, &h2r_args);
+}
+
+
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ /* Preparing decoding - getting instance number */
+ mfc_debug(2, "Getting instance number (codec: %d)\n", ctx->codec_mode);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->codec_mode;
+ h2r_args.arg[1] = 0; /* no crc & no pixelcache */
+ h2r_args.arg[2] = ctx->ctx_ofs;
+ h2r_args.arg[3] = ctx->ctx_size;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to create a new instance\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_cmd_args h2r_args;
+ int ret;
+
+ if (ctx->state == MFCINST_FREE) {
+ mfc_err("Instance already returned\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ /* Closing decoding instance */
+ mfc_debug(2, "Returning instance number %d\n", ctx->inst_no);
+ dev->curr_ctx = ctx->num;
+ memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
+ h2r_args.arg[0] = ctx->inst_no;
+ ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE,
+ &h2r_args);
+ if (ret) {
+ mfc_err("Failed to return an instance\n");
+ ctx->state = MFCINST_ERROR;
+ return -EINVAL;
+ }
+ return 0;
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h
new file mode 100644
index 000000000000..8b090d3723e7
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h
@@ -0,0 +1,30 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_CMD_H_
+#define S5P_MFC_CMD_H_
+
+#include "s5p_mfc_common.h"
+
+#define MAX_H2R_ARG 4
+
+struct s5p_mfc_cmd_args {
+ unsigned int arg[MAX_H2R_ARG];
+};
+
+int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev);
+int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_CMD_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
new file mode 100644
index 000000000000..519b0d66d8d1
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -0,0 +1,578 @@
+/*
+ * Samsung S5P Multi Format Codec v 5.0
+ *
+ * This file contains definitions of enums and structs used by the codec
+ * driver.
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Kamil Debski, <k.debski@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 S5P_MFC_COMMON_H_
+#define S5P_MFC_COMMON_H_
+
+#include "regs-mfc.h"
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+
+/* Definitions related to MFC memory */
+
+/* Offset base used to differentiate between CAPTURE and OUTPUT
+* while mmaping */
+#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2)
+
+/* Offset used by the hardware to store addresses */
+#define MFC_OFFSET_SHIFT 11
+
+#define FIRMWARE_ALIGN 0x20000 /* 128KB */
+#define MFC_H264_CTX_BUF_SIZE 0x96000 /* 600KB per H264 instance */
+#define MFC_CTX_BUF_SIZE 0x2800 /* 10KB per instance */
+#define DESC_BUF_SIZE 0x20000 /* 128KB for DESC buffer */
+#define SHARED_BUF_SIZE 0x2000 /* 8KB for shared buffer */
+
+#define DEF_CPB_SIZE 0x40000 /* 512KB */
+
+#define MFC_BANK1_ALLOC_CTX 0
+#define MFC_BANK2_ALLOC_CTX 1
+
+#define MFC_BANK1_ALIGN_ORDER 13
+#define MFC_BANK2_ALIGN_ORDER 13
+#define MFC_BASE_ALIGN_ORDER 17
+
+#include <media/videobuf2-dma-contig.h>
+
+static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
+{
+ /* Same functionality as the vb2_dma_contig_plane_paddr */
+ dma_addr_t *paddr = vb2_dma_contig_memops.cookie(b);
+
+ return *paddr;
+}
+
+/* MFC definitions */
+#define MFC_MAX_EXTRA_DPB 5
+#define MFC_MAX_BUFFERS 32
+#define MFC_NUM_CONTEXTS 4
+/* Interrupt timeout */
+#define MFC_INT_TIMEOUT 2000
+/* Busy wait timeout */
+#define MFC_BW_TIMEOUT 500
+/* Watchdog interval */
+#define MFC_WATCHDOG_INTERVAL 1000
+/* After how many executions watchdog should assume lock up */
+#define MFC_WATCHDOG_CNT 10
+#define MFC_NO_INSTANCE_SET -1
+#define MFC_ENC_CAP_PLANE_COUNT 1
+#define MFC_ENC_OUT_PLANE_COUNT 2
+#define STUFF_BYTE 4
+#define MFC_MAX_CTRLS 64
+
+#define mfc_read(dev, offset) readl(dev->regs_base + (offset))
+#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
+ (offset))
+
+/**
+ * enum s5p_mfc_fmt_type - type of the pixelformat
+ */
+enum s5p_mfc_fmt_type {
+ MFC_FMT_DEC,
+ MFC_FMT_ENC,
+ MFC_FMT_RAW,
+};
+
+/**
+ * enum s5p_mfc_node_type - The type of an MFC device node.
+ */
+enum s5p_mfc_node_type {
+ MFCNODE_INVALID = -1,
+ MFCNODE_DECODER = 0,
+ MFCNODE_ENCODER = 1,
+};
+
+/**
+ * enum s5p_mfc_inst_type - The type of an MFC instance.
+ */
+enum s5p_mfc_inst_type {
+ MFCINST_INVALID,
+ MFCINST_DECODER,
+ MFCINST_ENCODER,
+};
+
+/**
+ * enum s5p_mfc_inst_state - The state of an MFC instance.
+ */
+enum s5p_mfc_inst_state {
+ MFCINST_FREE = 0,
+ MFCINST_INIT = 100,
+ MFCINST_GOT_INST,
+ MFCINST_HEAD_PARSED,
+ MFCINST_BUFS_SET,
+ MFCINST_RUNNING,
+ MFCINST_FINISHING,
+ MFCINST_FINISHED,
+ MFCINST_RETURN_INST,
+ MFCINST_ERROR,
+ MFCINST_ABORT,
+ MFCINST_RES_CHANGE_INIT,
+ MFCINST_RES_CHANGE_FLUSH,
+ MFCINST_RES_CHANGE_END,
+};
+
+/**
+ * enum s5p_mfc_queue_state - The state of buffer queue.
+ */
+enum s5p_mfc_queue_state {
+ QUEUE_FREE,
+ QUEUE_BUFS_REQUESTED,
+ QUEUE_BUFS_QUERIED,
+ QUEUE_BUFS_MMAPED,
+};
+
+/**
+ * enum s5p_mfc_decode_arg - type of frame decoding
+ */
+enum s5p_mfc_decode_arg {
+ MFC_DEC_FRAME,
+ MFC_DEC_LAST_FRAME,
+ MFC_DEC_RES_CHANGE,
+};
+
+#define MFC_BUF_FLAG_USED (1 << 0)
+#define MFC_BUF_FLAG_EOS (1 << 1)
+
+struct s5p_mfc_ctx;
+
+/**
+ * struct s5p_mfc_buf - MFC buffer
+ */
+struct s5p_mfc_buf {
+ struct list_head list;
+ struct vb2_buffer *b;
+ union {
+ struct {
+ size_t luma;
+ size_t chroma;
+ } raw;
+ size_t stream;
+ } cookie;
+ int flags;
+};
+
+/**
+ * struct s5p_mfc_pm - power management data structure
+ */
+struct s5p_mfc_pm {
+ struct clk *clock;
+ struct clk *clock_gate;
+ atomic_t power;
+ struct device *device;
+};
+
+/**
+ * struct s5p_mfc_dev - The struct containing driver internal parameters.
+ *
+ * @v4l2_dev: v4l2_device
+ * @vfd_dec: video device for decoding
+ * @vfd_enc: video device for encoding
+ * @plat_dev: platform device
+ * @mem_dev_l: child device of the left memory bank (0)
+ * @mem_dev_r: child device of the right memory bank (1)
+ * @regs_base: base address of the MFC hw registers
+ * @irq: irq resource
+ * @dec_ctrl_handler: control framework handler for decoding
+ * @enc_ctrl_handler: control framework handler for encoding
+ * @pm: power management control
+ * @num_inst: couter of active MFC instances
+ * @irqlock: lock for operations on videobuf2 queues
+ * @condlock: lock for changing/checking if a context is ready to be
+ * processed
+ * @mfc_mutex: lock for video_device
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of last interrupt
+ * @int_err: error number for last interrupt
+ * @queue: waitqueue for waiting for completion of device commands
+ * @fw_size: size of firmware
+ * @bank1: address of the beggining of bank 1 memory
+ * @bank2: address of the beggining of bank 2 memory
+ * @hw_lock: used for hardware locking
+ * @ctx: array of driver contexts
+ * @curr_ctx: number of the currently running context
+ * @ctx_work_bits: used to mark which contexts are waiting for hardware
+ * @watchdog_cnt: counter for the watchdog
+ * @watchdog_workqueue: workqueue for the watchdog
+ * @watchdog_work: worker for the watchdog
+ * @alloc_ctx: videobuf2 allocator contexts for two memory banks
+ * @enter_suspend: flag set when entering suspend
+ *
+ */
+struct s5p_mfc_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd_dec;
+ struct video_device *vfd_enc;
+ struct platform_device *plat_dev;
+ struct device *mem_dev_l;
+ struct device *mem_dev_r;
+ void __iomem *regs_base;
+ int irq;
+ struct v4l2_ctrl_handler dec_ctrl_handler;
+ struct v4l2_ctrl_handler enc_ctrl_handler;
+ struct s5p_mfc_pm pm;
+ int num_inst;
+ spinlock_t irqlock; /* lock when operating on videobuf2 queues */
+ spinlock_t condlock; /* lock when changing/checking if a context is
+ ready to be processed */
+ struct mutex mfc_mutex; /* video_device lock */
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+ size_t fw_size;
+ size_t bank1;
+ size_t bank2;
+ unsigned long hw_lock;
+ struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
+ int curr_ctx;
+ unsigned long ctx_work_bits;
+ atomic_t watchdog_cnt;
+ struct timer_list watchdog_timer;
+ struct workqueue_struct *watchdog_workqueue;
+ struct work_struct watchdog_work;
+ void *alloc_ctx[2];
+ unsigned long enter_suspend;
+};
+
+/**
+ * struct s5p_mfc_h264_enc_params - encoding parameters for h264
+ */
+struct s5p_mfc_h264_enc_params {
+ enum v4l2_mpeg_video_h264_profile profile;
+ enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
+ s8 loop_filter_alpha;
+ s8 loop_filter_beta;
+ enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
+ u8 max_ref_pic;
+ u8 num_ref_pic_4p;
+ int _8x8_transform;
+ int rc_mb;
+ int rc_mb_dark;
+ int rc_mb_smooth;
+ int rc_mb_static;
+ int rc_mb_activity;
+ int vui_sar;
+ u8 vui_sar_idc;
+ u16 vui_ext_sar_width;
+ u16 vui_ext_sar_height;
+ int open_gop;
+ u16 open_gop_size;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_h264_level level_v4l2;
+ int level;
+ u16 cpb_size;
+};
+
+/**
+ * struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
+ */
+struct s5p_mfc_mpeg4_enc_params {
+ /* MPEG4 Only */
+ enum v4l2_mpeg_video_mpeg4_profile profile;
+ int quarter_pixel;
+ /* Common for MPEG4, H263 */
+ u16 vop_time_res;
+ u16 vop_frm_delta;
+ u8 rc_frame_qp;
+ u8 rc_min_qp;
+ u8 rc_max_qp;
+ u8 rc_p_frame_qp;
+ u8 rc_b_frame_qp;
+ enum v4l2_mpeg_video_mpeg4_level level_v4l2;
+ int level;
+};
+
+/**
+ * struct s5p_mfc_enc_params - general encoding parameters
+ */
+struct s5p_mfc_enc_params {
+ u16 width;
+ u16 height;
+
+ u16 gop_size;
+ enum v4l2_mpeg_video_multi_slice_mode slice_mode;
+ u16 slice_mb;
+ u32 slice_bit;
+ u16 intra_refresh_mb;
+ int pad;
+ u8 pad_luma;
+ u8 pad_cb;
+ u8 pad_cr;
+ int rc_frame;
+ u32 rc_bitrate;
+ u16 rc_reaction_coeff;
+ u16 vbv_size;
+
+ enum v4l2_mpeg_video_header_mode seq_hdr_mode;
+ enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
+ int fixed_target_bit;
+
+ u8 num_b_frame;
+ u32 rc_framerate_num;
+ u32 rc_framerate_denom;
+ int interlace;
+
+ union {
+ struct s5p_mfc_h264_enc_params h264;
+ struct s5p_mfc_mpeg4_enc_params mpeg4;
+ } codec;
+
+};
+
+/**
+ * struct s5p_mfc_codec_ops - codec ops, used by encoding
+ */
+struct s5p_mfc_codec_ops {
+ /* initialization routines */
+ int (*pre_seq_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_seq_start) (struct s5p_mfc_ctx *ctx);
+ /* execution routines */
+ int (*pre_frame_start) (struct s5p_mfc_ctx *ctx);
+ int (*post_frame_start) (struct s5p_mfc_ctx *ctx);
+};
+
+#define call_cop(c, op, args...) \
+ (((c)->c_ops->op) ? \
+ ((c)->c_ops->op(args)) : 0)
+
+/**
+ * struct s5p_mfc_ctx - This struct contains the instance context
+ *
+ * @dev: pointer to the s5p_mfc_dev of the device
+ * @fh: struct v4l2_fh
+ * @num: number of the context that this structure describes
+ * @int_cond: variable used by the waitqueue
+ * @int_type: type of the last interrupt
+ * @int_err: error number received from MFC hw in the interrupt
+ * @queue: waitqueue that can be used to wait for this context to
+ * finish
+ * @src_fmt: source pixelformat information
+ * @dst_fmt: destination pixelformat information
+ * @vq_src: vb2 queue for source buffers
+ * @vq_dst: vb2 queue for destination buffers
+ * @src_queue: driver internal queue for source buffers
+ * @dst_queue: driver internal queue for destination buffers
+ * @src_queue_cnt: number of buffers queued on the source internal queue
+ * @dst_queue_cnt: number of buffers queued on the dest internal queue
+ * @type: type of the instance - decoder or encoder
+ * @state: state of the context
+ * @inst_no: number of hw instance associated with the context
+ * @img_width: width of the image that is decoded or encoded
+ * @img_height: height of the image that is decoded or encoded
+ * @buf_width: width of the buffer for processed image
+ * @buf_height: height of the buffer for processed image
+ * @luma_size: size of a luma plane
+ * @chroma_size: size of a chroma plane
+ * @mv_size: size of a motion vectors buffer
+ * @consumed_stream: number of bytes that have been used so far from the
+ * decoding buffer
+ * @dpb_flush_flag: flag used to indicate that a DPB buffers are being
+ * flushed
+ * @bank1_buf: handle to memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank1_phys: address of the temporary buffers from memory bank 1
+ * @bank1_size: size of the memory allocated for temporary buffers from
+ * memory bank 1
+ * @bank2_buf: handle to memory allocated for temporary buffers from
+ * memory bank 2
+ * @bank2_phys: address of the temporary buffers from memory bank 2
+ * @bank2_size: size of the memory allocated for temporary buffers from
+ * memory bank 2
+ * @capture_state: state of the capture buffers queue
+ * @output_state: state of the output buffers queue
+ * @src_bufs: information on allocated source buffers
+ * @dst_bufs: information on allocated destination buffers
+ * @sequence: counter for the sequence number for v4l2
+ * @dec_dst_flag: flags for buffers queued in the hardware
+ * @dec_src_buf_size: size of the buffer for source buffers in decoding
+ * @codec_mode: number of codec mode used by MFC hw
+ * @slice_interface: slice interface flag
+ * @loop_filter_mpeg4: loop filter for MPEG4 flag
+ * @display_delay: value of the display delay for H264
+ * @display_delay_enable: display delay for H264 enable flag
+ * @after_packed_pb: flag used to track buffer when stream is in
+ * Packed PB format
+ * @dpb_count: count of the DPB buffers required by MFC hw
+ * @total_dpb_count: count of DPB buffers with additional buffers
+ * requested by the application
+ * @ctx_buf: handle to the memory associated with this context
+ * @ctx_phys: address of the memory associated with this context
+ * @ctx_size: size of the memory associated with this context
+ * @desc_buf: description buffer for decoding handle
+ * @desc_phys: description buffer for decoding address
+ * @shm_alloc: handle for the shared memory buffer
+ * @shm: virtual address for the shared memory buffer
+ * @shm_ofs: address offset for shared memory
+ * @enc_params: encoding parameters for MFC
+ * @enc_dst_buf_size: size of the buffers for encoder output
+ * @frame_type: used to force the type of the next encoded frame
+ * @ref_queue: list of the reference buffers for encoding
+ * @ref_queue_cnt: number of the buffers in the reference list
+ * @c_ops: ops for encoding
+ * @ctrls: array of controls, used when adding controls to the
+ * v4l2 control framework
+ * @ctrl_handler: handler for v4l2 framework
+ */
+struct s5p_mfc_ctx {
+ struct s5p_mfc_dev *dev;
+ struct v4l2_fh fh;
+
+ int num;
+
+ int int_cond;
+ int int_type;
+ unsigned int int_err;
+ wait_queue_head_t queue;
+
+ struct s5p_mfc_fmt *src_fmt;
+ struct s5p_mfc_fmt *dst_fmt;
+
+ struct vb2_queue vq_src;
+ struct vb2_queue vq_dst;
+
+ struct list_head src_queue;
+ struct list_head dst_queue;
+
+ unsigned int src_queue_cnt;
+ unsigned int dst_queue_cnt;
+
+ enum s5p_mfc_inst_type type;
+ enum s5p_mfc_inst_state state;
+ int inst_no;
+
+ /* Image parameters */
+ int img_width;
+ int img_height;
+ int buf_width;
+ int buf_height;
+
+ int luma_size;
+ int chroma_size;
+ int mv_size;
+
+ unsigned long consumed_stream;
+
+ unsigned int dpb_flush_flag;
+
+ /* Buffers */
+ void *bank1_buf;
+ size_t bank1_phys;
+ size_t bank1_size;
+
+ void *bank2_buf;
+ size_t bank2_phys;
+ size_t bank2_size;
+
+ enum s5p_mfc_queue_state capture_state;
+ enum s5p_mfc_queue_state output_state;
+
+ struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
+ int src_bufs_cnt;
+ struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
+ int dst_bufs_cnt;
+
+ unsigned int sequence;
+ unsigned long dec_dst_flag;
+ size_t dec_src_buf_size;
+
+ /* Control values */
+ int codec_mode;
+ int slice_interface;
+ int loop_filter_mpeg4;
+ int display_delay;
+ int display_delay_enable;
+ int after_packed_pb;
+
+ int dpb_count;
+ int total_dpb_count;
+
+ /* Buffers */
+ void *ctx_buf;
+ size_t ctx_phys;
+ size_t ctx_ofs;
+ size_t ctx_size;
+
+ void *desc_buf;
+ size_t desc_phys;
+
+
+ void *shm_alloc;
+ void *shm;
+ size_t shm_ofs;
+
+ struct s5p_mfc_enc_params enc_params;
+
+ size_t enc_dst_buf_size;
+
+ enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
+
+ struct list_head ref_queue;
+ unsigned int ref_queue_cnt;
+
+ struct s5p_mfc_codec_ops *c_ops;
+
+ struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct s5p_mfc_fmt - structure used to store information about pixelformats
+ * used by the MFC
+ */
+struct s5p_mfc_fmt {
+ char *name;
+ u32 fourcc;
+ u32 codec_mode;
+ enum s5p_mfc_fmt_type type;
+ u32 num_planes;
+};
+
+/**
+ * struct mfc_control - structure used to store information about MFC controls
+ * it is used to initialize the control framework.
+ */
+struct mfc_control {
+ __u32 id;
+ enum v4l2_ctrl_type type;
+ __u8 name[32]; /* Whatever */
+ __s32 minimum; /* Note signedness */
+ __s32 maximum;
+ __s32 step;
+ __u32 menu_skip_mask;
+ __s32 default_value;
+ __u32 flags;
+ __u32 reserved[2];
+ __u8 is_volatile;
+};
+
+
+#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
+#define ctrl_to_ctx(__ctrl) \
+ container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
+
+void clear_work_bit(struct s5p_mfc_ctx *ctx);
+void set_work_bit(struct s5p_mfc_ctx *ctx);
+void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
+void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
new file mode 100644
index 000000000000..0deba6bc687c
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -0,0 +1,347 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_pm.h"
+
+static void *s5p_mfc_bitproc_buf;
+static size_t s5p_mfc_bitproc_phys;
+static unsigned char *s5p_mfc_bitproc_virt;
+
+/* Allocate and load firmware */
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ size_t bank2_base_phys;
+ void *b_base;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
+ if (s5p_mfc_bitproc_buf) {
+ mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
+ if (IS_ERR(s5p_mfc_bitproc_buf)) {
+ s5p_mfc_bitproc_buf = NULL;
+ mfc_err("Allocating bitprocessor buffer failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
+ if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = NULL;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
+ if (!s5p_mfc_bitproc_virt) {
+ mfc_err("Bitprocessor memory remap failed\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = NULL;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ dev->bank1 = s5p_mfc_bitproc_phys;
+ b_base = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BASE_ALIGN_ORDER);
+ if (IS_ERR(b_base)) {
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = NULL;
+ mfc_err("Allocating bank2 base failed\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ bank2_base_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
+ vb2_dma_contig_memops.put(b_base);
+ if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
+ mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = NULL;
+ release_firmware(fw_blob);
+ return -EIO;
+ }
+ /* Valid buffers passed to MFC encoder with LAST_FRAME command
+ * should not have address of bank2 - MFC will treat it as a null frame.
+ * To avoid such situation we set bank2 address below the pool address.
+ */
+ dev->bank2 = bank2_base_phys - (1 << MFC_BASE_ALIGN_ORDER);
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Reload firmware to MFC */
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
+{
+ struct firmware *fw_blob;
+ int err;
+
+ /* Firmare has to be present as a separate file or compiled
+ * into kernel. */
+ mfc_debug_enter();
+ err = request_firmware((const struct firmware **)&fw_blob,
+ "s5p-mfc.fw", dev->v4l2_dev.dev);
+ if (err != 0) {
+ mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
+ return -EINVAL;
+ }
+ if (fw_blob->size > dev->fw_size) {
+ mfc_err("MFC firmware is too big to be loaded\n");
+ release_firmware(fw_blob);
+ return -ENOMEM;
+ }
+ if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) {
+ mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
+ release_firmware(fw_blob);
+ return -EINVAL;
+ }
+ memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
+ wmb();
+ release_firmware(fw_blob);
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Release firmware memory */
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
+{
+ /* Before calling this function one has to make sure
+ * that MFC is no longer processing */
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+ vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
+ s5p_mfc_bitproc_virt = NULL;
+ s5p_mfc_bitproc_phys = 0;
+ s5p_mfc_bitproc_buf = NULL;
+ return 0;
+}
+
+/* Reset the device */
+int s5p_mfc_reset(struct s5p_mfc_dev *dev)
+{
+ unsigned int mc_status;
+ unsigned long timeout;
+
+ mfc_debug_enter();
+ /* Stop procedure */
+ /* reset RISC */
+ mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
+ /* All reset except for MC */
+ mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
+ mdelay(10);
+
+ timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
+ /* Check MC status */
+ do {
+ if (time_after(jiffies, timeout)) {
+ mfc_err("Timeout while resetting MFC\n");
+ return -EIO;
+ }
+
+ mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
+
+ } while (mc_status & 0x3);
+
+ mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
+ mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
+ mfc_debug_leave();
+ return 0;
+}
+
+static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
+ mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
+ mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
+}
+
+static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
+{
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
+ mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
+ mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
+ mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
+}
+
+/* Initialize hardware */
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
+{
+ unsigned int ver;
+ int ret;
+
+ mfc_debug_enter();
+ if (!s5p_mfc_bitproc_buf)
+ return -EINVAL;
+
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ /* 3. Release reset signal to the RISC */
+ s5p_mfc_clean_dev_int_flags(dev);
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Will now wait for completion of firmware transfer\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 4. Initialize firmware */
+ ret = s5p_mfc_sys_init_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ mfc_debug(2, "Ok, now will write a command to init the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
+ mfc_err("Failed to load firmware\n");
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
+ /* Failure. */
+ mfc_err("Failed to init firmware - error: %d int: %d\n",
+ dev->int_err, dev->int_type);
+ s5p_mfc_reset(dev);
+ s5p_mfc_clock_off();
+ return -EIO;
+ }
+ ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
+ mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
+ (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
+ s5p_mfc_clock_off();
+ mfc_debug_leave();
+ return 0;
+}
+
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ s5p_mfc_clock_on();
+ s5p_mfc_clean_dev_int_flags(dev);
+ ret = s5p_mfc_sleep_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
+ mfc_err("Failed to sleep\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_SLEEP_RET) {
+ /* Failure. */
+ mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
+{
+ int ret;
+
+ mfc_debug_enter();
+ /* 0. MFC reset */
+ mfc_debug(2, "MFC reset..\n");
+ s5p_mfc_clock_on();
+ ret = s5p_mfc_reset(dev);
+ if (ret) {
+ mfc_err("Failed to reset MFC - timeout\n");
+ return ret;
+ }
+ mfc_debug(2, "Done MFC reset..\n");
+ /* 1. Set DRAM base Addr */
+ s5p_mfc_init_memctrl(dev);
+ /* 2. Initialize registers of channel I/F */
+ s5p_mfc_clear_cmds(dev);
+ s5p_mfc_clean_dev_int_flags(dev);
+ /* 3. Initialize firmware */
+ ret = s5p_mfc_wakeup_cmd(dev);
+ if (ret) {
+ mfc_err("Failed to send command to MFC - timeout\n");
+ return ret;
+ }
+ /* 4. Release reset signal to the RISC */
+ mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
+ mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
+ if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
+ mfc_err("Failed to load firmware\n");
+ return -EIO;
+ }
+ s5p_mfc_clock_off();
+ dev->int_cond = 0;
+ if (dev->int_err != 0 || dev->int_type !=
+ S5P_FIMV_R2H_CMD_WAKEUP_RET) {
+ /* Failure. */
+ mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
+ dev->int_type);
+ return -EIO;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
new file mode 100644
index 000000000000..e1e0c544b6a2
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_CTRL_H
+#define S5P_MFC_CTRL_H
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
+int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
+int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_reset(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
new file mode 100644
index 000000000000..bd5cd4ae993c
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/media/platform/samsung/mfc5/s5p_mfc_debug.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains debug macros
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_DEBUG_H_
+#define S5P_MFC_DEBUG_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+extern int debug;
+
+#define mfc_debug(level, fmt, args...) \
+ do { \
+ if (debug >= level) \
+ printk(KERN_DEBUG "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+#else
+#define mfc_debug(level, fmt, args...)
+#endif
+
+#define mfc_debug_enter() mfc_debug(5, "enter")
+#define mfc_debug_leave() mfc_debug(5, "leave")
+
+#define mfc_err(fmt, args...) \
+ do { \
+ printk(KERN_ERR "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#define mfc_info(fmt, args...) \
+ do { \
+ printk(KERN_INFO "%s:%d: " fmt, \
+ __func__, __LINE__, ##args); \
+ } while (0)
+
+#endif /* S5P_MFC_DEBUG_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
new file mode 100644
index 000000000000..6ee21bb71398
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -0,0 +1,1030 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ * Kamil Debski, <k.debski@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_dec.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H263 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG1,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG2 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG2,
+ .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "XviD Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_XVID,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .codec_mode = S5P_FIMV_CODEC_VC1_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+ {
+ .name = "VC1 RCV Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
+ .type = MFC_FMT_DEC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+/* Find selected format description */
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H264 Display Delay",
+ .minimum = 0,
+ .maximum = 16383,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Display Delay Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mpeg4 Loop Filter Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Slice Interface Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Minimum number of cap bufs",
+ .minimum = 1,
+ .maximum = 32,
+ .step = 1,
+ .default_value = 1,
+ .is_volatile = 1,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+
+/* Check whether a context should be run on hardware */
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ /* Context is to parse header */
+ if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_GOT_INST)
+ return 1;
+ /* Context is to decode a frame */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_RUNNING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to return last frame */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ /* Context is to set buffers */
+ if (ctx->src_queue_cnt >= 1 &&
+ ctx->state == MFCINST_HEAD_PARSED &&
+ ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 1;
+ /* Resolution change */
+ if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
+ ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
+ ctx->dst_queue_cnt >= ctx->dpb_count)
+ return 1;
+ if (ctx->state == MFCINST_RES_CHANGE_END &&
+ ctx->src_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops decoder_codec_ops = {
+ .pre_seq_start = NULL,
+ .post_seq_start = NULL,
+ .pre_frame_start = NULL,
+ .post_frame_start = NULL,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ return 0;
+}
+
+/* Enumerate format */
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_DEC)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_RAW)
+ continue;
+
+ if (j == f->index)
+ break;
+ ++j;
+ }
+ if (i == ARRAY_SIZE(formats))
+ return -EINVAL;
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+/* Get format */
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ pix_mp = &f->fmt.pix_mp;
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ (ctx->state == MFCINST_GOT_INST || ctx->state ==
+ MFCINST_RES_CHANGE_END)) {
+ /* If the MFC is parsing the header,
+ * so wait until it is finished */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
+ 0);
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ /* This is run on CAPTURE (decode output) */
+ /* Width and height are set to the dimensions
+ of the movie, the buffer is bigger and
+ further processing stages should crop to this
+ rectangle. */
+ pix_mp->width = ctx->buf_width;
+ pix_mp->height = ctx->buf_height;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->num_planes = 2;
+ /* Set pixelformat to the format in which MFC
+ outputs the decoded frame */
+ pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
+ pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on OUTPUT
+ The buffer contains compressed image
+ so width and height have no meaning */
+ pix_mp->width = 0;
+ pix_mp->height = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
+ pix_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_mp->num_planes = ctx->src_fmt->num_planes;
+ } else {
+ mfc_err("Format could not be read\n");
+ mfc_debug(2, "%s-- with error\n", __func__);
+ return -EINVAL;
+ }
+ mfc_debug_leave();
+ return 0;
+}
+
+/* Try format */
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_err("This node supports decoding only\n");
+ return -EINVAL;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt) {
+ mfc_err("Unsupported format\n");
+ return -EINVAL;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Set format */
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_mp;
+
+ mfc_debug_enter();
+ ret = vidioc_try_fmt(file, priv, f);
+ pix_mp = &f->fmt.pix_mp;
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ fmt = find_format(f, MFC_FMT_DEC);
+ if (!fmt || fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
+ mfc_err("Unknown codec\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (fmt->type != MFC_FMT_DEC) {
+ mfc_err("Wrong format selected, you should choose "
+ "format for decoding\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->codec_mode = fmt->codec_mode;
+ mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
+ pix_mp->height = 0;
+ pix_mp->width = 0;
+ if (pix_mp->plane_fmt[0].sizeimage)
+ ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
+ else
+ pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
+ DEF_CPB_SIZE;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ ctx->state = MFCINST_INIT;
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Reqeust buffers */
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ if (reqbufs->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only V4L2_MEMORY_MAP is supported\n");
+ return -EINVAL;
+ }
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* Can only request buffers after an instance has been opened.*/
+ if (ctx->state == MFCINST_INIT) {
+ ctx->src_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ /* Decoding */
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on output failed\n");
+ return ret;
+ }
+ mfc_debug(2, "vb2_reqbufs: %d\n", ret);
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ctx->dst_bufs_cnt = 0;
+ if (reqbufs->count == 0) {
+ mfc_debug(2, "Freeing buffers\n");
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return ret;
+ }
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("Bufs have already been requested\n");
+ return -EINVAL;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ if (ret) {
+ mfc_err("vb2_reqbufs on capture failed\n");
+ return ret;
+ }
+ if (reqbufs->count < ctx->dpb_count) {
+ mfc_err("Not enough buffers allocated\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ ctx->total_dpb_count = reqbufs->count;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate decoding buffers\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
+ ctx->capture_state = QUEUE_BUFS_MMAPED;
+ } else {
+ mfc_err("Not all buffers passed to buf_init\n");
+ reqbufs->count = 0;
+ s5p_mfc_clock_on();
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ s5p_mfc_release_codec_buffers(ctx);
+ s5p_mfc_clock_off();
+ return -ENOMEM;
+ }
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET, 0);
+ }
+ return ret;
+}
+
+/* Query buffer */
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+ int i;
+
+ if (buf->memory != V4L2_MEMORY_MMAP) {
+ mfc_err("Only mmaped buffers can be used\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
+ if (ctx->state == MFCINST_INIT &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ } else if (ctx->state == MFCINST_RUNNING &&
+ buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ for (i = 0; i < buf->length; i++)
+ buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else {
+ mfc_err("vidioc_querybuf called in an inappropriate state\n");
+ ret = -EINVAL;
+ }
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_qbuf(&ctx->vq_src, buf);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ return -EINVAL;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ int ret = -EINVAL;
+
+ mfc_debug_enter();
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+
+ if (ctx->state == MFCINST_INIT) {
+ ctx->dst_bufs_cnt = 0;
+ ctx->src_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ ctx->output_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ s5p_mfc_alloc_dec_temp_buffers(ctx);
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+
+ if (s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ s5p_mfc_release_dec_desc_buffer(ctx);
+ return -EIO;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ }
+ ret = vb2_streamon(&ctx->vq_src, type);
+ }
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ ret = vb2_streamon(&ctx->vq_dst, type);
+ mfc_debug_leave();
+ return ret;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Set controls - v4l2 control framework */
+static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
+ ctx->display_delay = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
+ ctx->display_delay_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ ctx->loop_filter_mpeg4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ ctx->slice_interface = ctrl->val;
+ break;
+ default:
+ mfc_err("Invalid control 0x%08x\n", ctrl->id);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ break;
+ } else if (ctx->state != MFCINST_INIT) {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ /* Should wait for the header to be parsed */
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 0);
+ if (ctx->state >= MFCINST_HEAD_PARSED &&
+ ctx->state < MFCINST_ABORT) {
+ ctrl->val = ctx->dpb_count;
+ } else {
+ v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
+ .s_ctrl = s5p_mfc_dec_s_ctrl,
+ .g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
+};
+
+/* Get cropping information */
+static int vidioc_g_crop(struct file *file, void *priv,
+ struct v4l2_crop *cr)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ u32 left, right, top, bottom;
+
+ if (ctx->state != MFCINST_HEAD_PARSED &&
+ ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
+ && ctx->state != MFCINST_FINISHED) {
+ mfc_err("Cannont set crop\n");
+ return -EINVAL;
+ }
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
+ left = s5p_mfc_read_shm(ctx, CROP_INFO_H);
+ right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
+ left = left & S5P_FIMV_SHARED_CROP_LEFT_MASK;
+ top = s5p_mfc_read_shm(ctx, CROP_INFO_V);
+ bottom = top >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
+ top = top & S5P_FIMV_SHARED_CROP_TOP_MASK;
+ cr->c.left = left;
+ cr->c.top = top;
+ cr->c.width = ctx->img_width - left - right;
+ cr->c.height = ctx->img_height - top - bottom;
+ mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
+ "w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
+ cr->c.width, cr->c.height, right, bottom,
+ ctx->buf_width, ctx->buf_height);
+ } else {
+ cr->c.left = 0;
+ cr->c.top = 0;
+ cr->c.width = ctx->img_width;
+ cr->c.height = ctx->img_height;
+ mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
+ "fh=%d\n", cr->c.width, cr->c.height, ctx->buf_width,
+ ctx->buf_height);
+ }
+ return 0;
+}
+
+/* v4l2_ioctl_ops */
+static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_crop = vidioc_g_crop,
+};
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt, unsigned int *buf_count,
+ unsigned int *plane_count, unsigned int psize[],
+ void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ /* Video output for decoding (source)
+ * this can be set after getting an instance */
+ if (ctx->state == MFCINST_INIT &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* A single plane is required for input */
+ *plane_count = 1;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ /* Video capture for decoding (destination)
+ * this can be set after the header was parsed */
+ } else if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* Output plane count is 2 - one for Y and one for CbCr */
+ *plane_count = 2;
+ /* Setup buffer count */
+ if (*buf_count < ctx->dpb_count)
+ *buf_count = ctx->dpb_count;
+ if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
+ *buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ } else {
+ mfc_err("State seems invalid. State = %d, vq->type = %d\n",
+ ctx->state, vq->type);
+ return -EINVAL;
+ }
+ mfc_debug(2, "Buffer count=%d, plane count=%d\n",
+ *buf_count, *plane_count);
+ if (ctx->state == MFCINST_HEAD_PARSED &&
+ vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ ctx->state == MFCINST_INIT) {
+ psize[0] = ctx->dec_src_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else {
+ mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state == QUEUE_BUFS_MMAPED)
+ return 0;
+ for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, i)))) {
+ mfc_err("Plane mem not allocated\n");
+ return -EINVAL;
+ }
+ }
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("Plane buffer (CAPTURE) is too small\n");
+ return -EINVAL;
+ }
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (IS_ERR_OR_NULL(ERR_PTR(
+ vb2_dma_contig_plane_dma_addr(vb, 0)))) {
+ mfc_err("Plane memory not allocated\n");
+ return -EINVAL;
+ }
+ if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
+ mfc_err("Plane buffer (OUTPUT) is too small\n");
+ return -EINVAL;
+ }
+
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("s5p_mfc_buf_init: unknown queue type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_FINISHED)
+ ctx->state = MFCINST_RUNNING;
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ int aborted = 0;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx,
+ S5P_FIMV_R2H_CMD_FRAME_DONE_RET, 0);
+ aborted = 1;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ ctx->dpb_flush_flag = 1;
+ ctx->dec_dst_flag = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ if (aborted)
+ ctx->state = MFCINST_RUNNING;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("Unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_dec_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
+{
+ return &decoder_codec_ops;
+}
+
+struct vb2_ops *get_dec_queue_ops(void)
+{
+ return &s5p_mfc_dec_qops;
+}
+
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_dec_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
+ cfg.ops = &s5p_mfc_dec_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
+ &s5p_mfc_dec_ctrl_ops,
+ controls[i].id, controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
new file mode 100644
index 000000000000..fdf1d99a9d15
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_DEC_H_
+#define S5P_MFC_DEC_H_
+
+struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
+struct vb2_ops *get_dec_queue_ops(void);
+const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
+int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_DEC_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
new file mode 100644
index 000000000000..179e4db60b15
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -0,0 +1,1882 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Jeongtae Park <jtp.park@samsung.com>
+ * Kamil Debski <k.debski@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-event.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_enc.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+
+static struct s5p_mfc_fmt formats[] = {
+ {
+ .name = "4:2:0 2 Planes 64x32 Tiles",
+ .fourcc = V4L2_PIX_FMT_NV12MT,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "4:2:0 2 Planes",
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .codec_mode = S5P_FIMV_CODEC_NONE,
+ .type = MFC_FMT_RAW,
+ .num_planes = 2,
+ },
+ {
+ .name = "H264 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H264,
+ .codec_mode = S5P_FIMV_CODEC_H264_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "MPEG4 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_MPEG4,
+ .codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+ {
+ .name = "H263 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_H263,
+ .codec_mode = S5P_FIMV_CODEC_H263_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
+{
+ unsigned int i;
+
+ for (i = 0; i < NUM_FORMATS; i++) {
+ if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ formats[i].type == t)
+ return &formats[i];
+ }
+ return NULL;
+}
+
+static struct mfc_control controls[] = {
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+ .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1900,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1900,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Padding Control Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Padding Color YUV Value",
+ .minimum = 0,
+ .maximum = (1 << 25) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = (1 << 30) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Rate Control Reaction Coeff.",
+ .minimum = 1,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Force frame type",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Frame Skip Enable",
+ .minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ .maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
+ .menu_skip_mask = 0,
+ .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Fixed Target Bit Enable",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .menu_skip_mask = ~(
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
+ ),
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
+ .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
+ .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
+ .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = -6,
+ .maximum = 6,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+ .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "The Number of Ref. Pic for P",
+ .minimum = 1,
+ .maximum = 2,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 Maximum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "H263 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 I-Frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Minimum QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 Maximum QP value",
+ .minimum = 0,
+ .maximum = 51,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 P frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "MPEG4 B frame QP value",
+ .minimum = 1,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Dark Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Smooth Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Static Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "H264 Activity Reg Adaptive RC",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+ .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+ .default_value = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+ .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
+ .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
+#define NUM_CTRLS ARRAY_SIZE(controls)
+static const char * const *mfc51_get_menu(u32 id)
+{
+ static const char * const mfc51_video_frame_skip[] = {
+ "Disabled",
+ "Level Limit",
+ "VBV/CPB Limit",
+ NULL,
+ };
+ static const char * const mfc51_video_force_frame[] = {
+ "Disabled",
+ "I Frame",
+ "Not Coded",
+ NULL,
+ };
+ switch (id) {
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ return mfc51_video_frame_skip;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ return mfc51_video_force_frame;
+ }
+ return NULL;
+}
+
+static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
+{
+ mfc_debug(2, "src=%d, dst=%d, state=%d\n",
+ ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
+ /* context is ready to make header */
+ if (ctx->state == MFCINST_GOT_INST && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode a frame */
+ if (ctx->state == MFCINST_RUNNING &&
+ ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
+ return 1;
+ /* context is ready to encode remaining frames */
+ if (ctx->state == MFCINST_FINISHING &&
+ ctx->dst_queue_cnt >= 1)
+ return 1;
+ mfc_debug(2, "ctx is not ready\n");
+ return 0;
+}
+
+static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long mb_y_addr, mb_c_addr;
+
+ /* move buffers in ref queue to src queue */
+ while (!list_empty(&ctx->ref_queue)) {
+ mb_entry = list_entry((&ctx->ref_queue)->next,
+ struct s5p_mfc_buf, list);
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ INIT_LIST_HEAD(&ctx->ref_queue);
+ ctx->ref_queue_cnt = 0;
+}
+
+static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long flags;
+
+ if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next,
+ struct s5p_mfc_buf, list);
+ list_del(&dst_mb->list);
+ ctx->dst_queue_cnt--;
+ vb2_set_plane_payload(dst_mb->b, 0,
+ s5p_mfc_get_enc_strm_size());
+ vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ }
+ ctx->state = MFCINST_RUNNING;
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long flags;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+
+ return 0;
+}
+
+static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *mb_entry;
+ unsigned long enc_y_addr, enc_c_addr;
+ unsigned long mb_y_addr, mb_c_addr;
+ int slice_type;
+ unsigned int strm_size;
+ unsigned long flags;
+
+ slice_type = s5p_mfc_get_enc_slice_type();
+ strm_size = s5p_mfc_get_enc_strm_size();
+ mfc_debug(2, "Encoded slice type: %d", slice_type);
+ mfc_debug(2, "Encoded stream size: %d", strm_size);
+ mfc_debug(2, "Display order: %d",
+ mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (slice_type >= 0) {
+ s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
+ list_for_each_entry(mb_entry, &ctx->src_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
+ mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
+ mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
+ if ((enc_y_addr == mb_y_addr) &&
+ (enc_c_addr == mb_c_addr)) {
+ list_del(&mb_entry->list);
+ ctx->ref_queue_cnt--;
+ vb2_buffer_done(mb_entry->b,
+ VB2_BUF_STATE_DONE);
+ break;
+ }
+ }
+ }
+ if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) {
+ mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ if (mb_entry->flags & MFC_BUF_FLAG_USED) {
+ list_del(&mb_entry->list);
+ ctx->src_queue_cnt--;
+ list_add_tail(&mb_entry->list, &ctx->ref_queue);
+ ctx->ref_queue_cnt++;
+ }
+ mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
+ ctx->src_queue_cnt, ctx->ref_queue_cnt);
+ }
+ if (strm_size > 0) {
+ /* at least one more dest. buffers exist always */
+ mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
+ list);
+ list_del(&mb_entry->list);
+ ctx->dst_queue_cnt--;
+ switch (slice_type) {
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_I:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_P:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case S5P_FIMV_ENC_SI_SLICE_TYPE_B:
+ mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ }
+ vb2_set_plane_payload(mb_entry->b, 0, strm_size);
+ vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0))
+ clear_work_bit(ctx);
+ return 0;
+}
+
+static struct s5p_mfc_codec_ops encoder_codec_ops = {
+ .pre_seq_start = enc_pre_seq_start,
+ .post_seq_start = enc_post_seq_start,
+ .pre_frame_start = enc_pre_frame_start,
+ .post_frame_start = enc_post_frame_start,
+};
+
+/* Query capabilities of the device */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+
+ strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->version = KERNEL_VERSION(1, 0, 0);
+ /*
+ * This is only a mem-to-mem video device. The capture and output
+ * device capability flags are left only for backward compatibility
+ * and are scheduled for removal.
+ */
+ cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ return 0;
+}
+
+static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
+{
+ struct s5p_mfc_fmt *fmt;
+ int i, j = 0;
+
+ for (i = 0; i < ARRAY_SIZE(formats); ++i) {
+ if (mplane && formats[i].num_planes == 1)
+ continue;
+ else if (!mplane && formats[i].num_planes > 1)
+ continue;
+ if (out && formats[i].type != MFC_FMT_RAW)
+ continue;
+ else if (!out && formats[i].type != MFC_FMT_ENC)
+ continue;
+ if (j == f->index) {
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name,
+ sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+ ++j;
+ }
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, false);
+}
+
+static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, false);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, false, true);
+}
+
+static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
+ struct v4l2_fmtdesc *f)
+{
+ return vidioc_enum_fmt(f, true, true);
+}
+
+static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ /* This is run on output (encoder dest) */
+ pix_fmt_mp->width = 0;
+ pix_fmt_mp->height = 0;
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->dst_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->enc_dst_buf_size;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->enc_dst_buf_size;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ /* This is run on capture (encoder src) */
+ pix_fmt_mp->width = ctx->img_width;
+ pix_fmt_mp->height = ctx->img_height;
+
+ pix_fmt_mp->field = V4L2_FIELD_NONE;
+ pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
+ pix_fmt_mp->num_planes = ctx->src_fmt->num_planes;
+
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
+ mfc_err("must be set encoding output size\n");
+ return -EINVAL;
+ }
+
+ pix_fmt_mp->plane_fmt[0].bytesperline =
+ pix_fmt_mp->plane_fmt[0].sizeimage;
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to try output format\n");
+ return -EINVAL;
+ }
+ v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1,
+ &pix_fmt_mp->height, 4, 1080, 1, 0);
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct s5p_mfc_dev *dev = video_drvdata(file);
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
+ int ret = 0;
+
+ ret = vidioc_try_fmt(file, priv, f);
+ if (ret)
+ return ret;
+ if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ ret = -EBUSY;
+ goto out;
+ }
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ fmt = find_format(f, MFC_FMT_ENC);
+ if (!fmt) {
+ mfc_err("failed to set capture format\n");
+ return -EINVAL;
+ }
+ ctx->state = MFCINST_INIT;
+ ctx->dst_fmt = fmt;
+ ctx->codec_mode = ctx->dst_fmt->codec_mode;
+ ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
+ pix_fmt_mp->plane_fmt[0].bytesperline = 0;
+ ctx->dst_bufs_cnt = 0;
+ ctx->capture_state = QUEUE_FREE;
+ s5p_mfc_alloc_instance_buffer(ctx);
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_try_run(dev);
+ if (s5p_mfc_wait_for_done_ctx(ctx, \
+ S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
+ /* Error or timeout */
+ mfc_err("Error getting instance from hardware\n");
+ s5p_mfc_release_instance_buffer(ctx);
+ ret = -EIO;
+ goto out;
+ }
+ mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
+ } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ fmt = find_format(f, MFC_FMT_RAW);
+ if (!fmt) {
+ mfc_err("failed to set output format\n");
+ return -EINVAL;
+ }
+ if (fmt->num_planes != pix_fmt_mp->num_planes) {
+ mfc_err("failed to set output format\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ ctx->src_fmt = fmt;
+ ctx->img_width = pix_fmt_mp->width;
+ ctx->img_height = pix_fmt_mp->height;
+ mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
+ mfc_debug(2, "fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
+ pix_fmt_mp->width, pix_fmt_mp->height,
+ ctx->img_width, ctx->img_height);
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12M_LVALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12M_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12M_CVALIGN);
+
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12M_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12M_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+
+ } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
+ ctx->buf_width = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN);
+ ctx->luma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height,
+ S5P_FIMV_NV12MT_VALIGN);
+ ctx->chroma_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN) * ALIGN((ctx->img_height
+ >> 1), S5P_FIMV_NV12MT_VALIGN);
+ ctx->luma_size = ALIGN(ctx->luma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ ctx->chroma_size = ALIGN(ctx->chroma_size,
+ S5P_FIMV_NV12MT_SALIGN);
+
+ pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
+ pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
+ pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
+ pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
+ }
+ ctx->src_bufs_cnt = 0;
+ ctx->output_state = QUEUE_FREE;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+out:
+ mfc_debug_leave();
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
+ (reqbufs->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->capture_state != QUEUE_FREE) {
+ mfc_err("invalid capture state: %d\n",
+ ctx->capture_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(D)\n");
+ return ret;
+ }
+ ctx->capture_state = QUEUE_BUFS_REQUESTED;
+ ret = s5p_mfc_alloc_codec_buffers(ctx);
+ if (ret) {
+ mfc_err("Failed to allocate encoding buffers\n");
+ reqbufs->count = 0;
+ ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
+ return -ENOMEM;
+ }
+ } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->output_state != QUEUE_FREE) {
+ mfc_err("invalid output state: %d\n",
+ ctx->output_state);
+ return -EINVAL;
+ }
+ ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
+ if (ret != 0) {
+ mfc_err("error in vb2_reqbufs() for E(S)\n");
+ return ret;
+ }
+ ctx->output_state = QUEUE_BUFS_REQUESTED;
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret = 0;
+
+ /* if memory is not mmp or userptr return error */
+ if ((buf->memory != V4L2_MEMORY_MMAP) &&
+ (buf->memory != V4L2_MEMORY_USERPTR))
+ return -EINVAL;
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("invalid context state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ ret = vb2_querybuf(&ctx->vq_dst, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(D)\n");
+ return ret;
+ }
+ buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_querybuf(&ctx->vq_src, buf);
+ if (ret != 0) {
+ mfc_err("error in vb2_querybuf() for E(S)\n");
+ return ret;
+ }
+ } else {
+ mfc_err("invalid buf type\n");
+ return -EINVAL;
+ }
+ return ret;
+}
+
+/* Queue a buffer */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on QBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->state == MFCINST_FINISHING) {
+ mfc_err("Call on QBUF after EOS command\n");
+ return -EIO;
+ }
+ return vb2_qbuf(&ctx->vq_src, buf);
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ return vb2_qbuf(&ctx->vq_dst, buf);
+ }
+ return -EINVAL;
+}
+
+/* Dequeue a buffer */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ const struct v4l2_event ev = {
+ .type = V4L2_EVENT_EOS
+ };
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ if (ctx->state == MFCINST_ERROR) {
+ mfc_err("Call on DQBUF after unrecoverable error\n");
+ return -EIO;
+ }
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
+ } else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
+ if (ret == 0 && ctx->state == MFCINST_FINISHED
+ && list_empty(&ctx->vq_dst.done_list))
+ v4l2_event_queue_fh(&ctx->fh, &ev);
+ } else {
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* Stream on */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamon(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamon(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+/* Stream off, which equals to a pause */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return vb2_streamoff(&ctx->vq_src, type);
+ else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return vb2_streamoff(&ctx->vq_dst, type);
+ return -EINVAL;
+}
+
+static inline int h264_level(enum v4l2_mpeg_video_h264_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_4_0 + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1B */ 9,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0 */ 20,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1 */ 21,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2 */ 22,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0 */ 30,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1 */ 31,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2 */ 32,
+ /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0 */ 40,
+ };
+ return t[lvl];
+}
+
+static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 + 1] = {
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 */ 0,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B */ 9,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 */ 1,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 */ 2,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 */ 3,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B */ 7,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 */ 4,
+ /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 */ 5,
+ };
+ return t[lvl];
+}
+
+static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
+{
+ static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED */ 0,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 */ 1,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 */ 2,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 */ 3,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 */ 4,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 */ 5,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 */ 6,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 */ 7,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 */ 8,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 */ 9,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 */ 10,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 */ 11,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 */ 12,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 */ 13,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 */ 14,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 */ 15,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 */ 16,
+ /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED */ 255,
+ };
+ return t[sar];
+}
+
+static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ p->gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ p->slice_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+ p->slice_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+ p->slice_bit = ctrl->val * 8;
+ break;
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
+ p->intra_refresh_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
+ p->pad = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
+ p->pad_luma = (ctrl->val >> 16) & 0xff;
+ p->pad_cb = (ctrl->val >> 8) & 0xff;
+ p->pad_cr = (ctrl->val >> 0) & 0xff;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ p->rc_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ p->rc_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
+ p->rc_reaction_coeff = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
+ ctx->force_frame_type = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+ p->vbv_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ p->codec.h264.cpb_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ p->seq_hdr_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
+ p->frame_skip_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
+ p->fixed_target_bit = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ p->num_b_frame = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_MAIN;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_HIGH;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ p->codec.h264.profile =
+ S5P_FIMV_ENC_PROFILE_H264_BASELINE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ p->codec.h264.level_v4l2 = ctrl->val;
+ p->codec.h264.level = h264_level(ctrl->val);
+ if (p->codec.h264.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.h264.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ p->codec.mpeg4.level_v4l2 = ctrl->val;
+ p->codec.mpeg4.level = mpeg4_level(ctrl->val);
+ if (p->codec.mpeg4.level < 0) {
+ mfc_err("Level number is wrong\n");
+ ret = p->codec.mpeg4.level;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ p->codec.h264.loop_filter_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+ p->codec.h264.loop_filter_alpha = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+ p->codec.h264.loop_filter_beta = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ p->codec.h264.entropy_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
+ p->codec.h264.num_ref_pic_4p = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ p->codec.h264._8x8_transform = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ p->codec.h264.rc_mb = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ p->codec.h264.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
+ p->codec.h264.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
+ p->codec.h264.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ p->codec.h264.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ p->codec.h264.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
+ p->codec.mpeg4.rc_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
+ p->codec.mpeg4.rc_min_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
+ p->codec.mpeg4.rc_max_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
+ p->codec.mpeg4.rc_p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
+ p->codec.mpeg4.rc_b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
+ p->codec.h264.rc_mb_dark = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
+ p->codec.h264.rc_mb_smooth = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
+ p->codec.h264.rc_mb_static = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
+ p->codec.h264.rc_mb_activity = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ p->codec.h264.vui_sar = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ p->codec.h264.vui_sar_idc = vui_sar_idc(ctrl->val);
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ p->codec.h264.vui_ext_sar_width = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ p->codec.h264.vui_ext_sar_height = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ p->codec.h264.open_gop = !ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
+ p->codec.h264.open_gop_size = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ switch (ctrl->val) {
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE;
+ break;
+ case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
+ p->codec.mpeg4.profile =
+ S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ p->codec.mpeg4.quarter_pixel = ctrl->val;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
+ ctrl->id, ctrl->val);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
+ .s_ctrl = s5p_mfc_enc_s_ctrl,
+};
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ctx->enc_params.rc_framerate_num =
+ a->parm.output.timeperframe.denominator;
+ ctx->enc_params.rc_framerate_denom =
+ a->parm.output.timeperframe.numerator;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+
+ if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ a->parm.output.timeperframe.denominator =
+ ctx->enc_params.rc_framerate_num;
+ a->parm.output.timeperframe.numerator =
+ ctx->enc_params.rc_framerate_denom;
+ } else {
+ mfc_err("Setting FPS is only possible for the output queue\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int vidioc_encoder_cmd(struct file *file, void *priv,
+ struct v4l2_encoder_cmd *cmd)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *buf;
+ unsigned long flags;
+
+ switch (cmd->cmd) {
+ case V4L2_ENC_CMD_STOP:
+ if (cmd->flags != 0)
+ return -EINVAL;
+
+ if (!ctx->vq_src.streaming)
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "EOS: empty src queue, entering finishing state");
+ ctx->state = MFCINST_FINISHING;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ s5p_mfc_try_run(dev);
+ } else {
+ mfc_debug(2, "EOS: marking last buffer of stream");
+ buf = list_entry(ctx->src_queue.prev,
+ struct s5p_mfc_buf, list);
+ if (buf->flags & MFC_BUF_FLAG_USED)
+ ctx->state = MFCINST_FINISHING;
+ else
+ buf->flags |= MFC_BUF_FLAG_EOS;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ }
+ break;
+ default:
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_encoder_cmd = vidioc_encoder_cmd,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
+{
+ int i;
+
+ if (!fmt)
+ return -EINVAL;
+ if (fmt->num_planes != vb->num_planes) {
+ mfc_err("invalid plane number for the format\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < fmt->num_planes; i++) {
+ if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
+ mfc_err("failed to get plane cookie\n");
+ return -EINVAL;
+ }
+ mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
+ vb->v4l2_buf.index, i,
+ vb2_dma_contig_plane_dma_addr(vb, i));
+ }
+ return 0;
+}
+
+static int s5p_mfc_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *buf_count, unsigned int *plane_count,
+ unsigned int psize[], void *allocators[])
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("inavlid state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->dst_fmt)
+ *plane_count = ctx->dst_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_CAP_PLANE_COUNT;
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->enc_dst_buf_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ctx->src_fmt)
+ *plane_count = ctx->src_fmt->num_planes;
+ else
+ *plane_count = MFC_ENC_OUT_PLANE_COUNT;
+
+ if (*buf_count < 1)
+ *buf_count = 1;
+ if (*buf_count > MFC_MAX_BUFFERS)
+ *buf_count = MFC_MAX_BUFFERS;
+ psize[0] = ctx->luma_size;
+ psize[1] = ctx->chroma_size;
+ allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ allocators[1] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void s5p_mfc_unlock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_unlock(&dev->mfc_mutex);
+}
+
+static void s5p_mfc_lock(struct vb2_queue *q)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mutex_lock(&dev->mfc_mutex);
+}
+
+static int s5p_mfc_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ unsigned int i;
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->dst_bufs[i].b = vb;
+ ctx->dst_bufs[i].cookie.stream =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->dst_bufs_cnt++;
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ i = vb->v4l2_buf.index;
+ ctx->src_bufs[i].b = vb;
+ ctx->src_bufs[i].cookie.raw.luma =
+ vb2_dma_contig_plane_dma_addr(vb, 0);
+ ctx->src_bufs[i].cookie.raw.chroma =
+ vb2_dma_contig_plane_dma_addr(vb, 1);
+ ctx->src_bufs_cnt++;
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ int ret;
+
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ret = check_vb_with_fmt(ctx->dst_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, dst size: %d\n",
+ vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
+ if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
+ mfc_err("plane size is too small for capture\n");
+ return -EINVAL;
+ }
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = check_vb_with_fmt(ctx->src_fmt, vb);
+ if (ret < 0)
+ return ret;
+ mfc_debug(2, "plane size: %ld, luma size: %d\n",
+ vb2_plane_size(vb, 0), ctx->luma_size);
+ mfc_debug(2, "plane size: %ld, chroma size: %d\n",
+ vb2_plane_size(vb, 1), ctx->chroma_size);
+ if (vb2_plane_size(vb, 0) < ctx->luma_size ||
+ vb2_plane_size(vb, 1) < ctx->chroma_size) {
+ mfc_err("plane size is too small for output\n");
+ return -EINVAL;
+ }
+ } else {
+ mfc_err("inavlid queue type: %d\n", vq->type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ /* If context is ready then dev = work->data;schedule it to run */
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+ return 0;
+}
+
+static int s5p_mfc_stop_streaming(struct vb2_queue *q)
+{
+ unsigned long flags;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if ((ctx->state == MFCINST_FINISHING ||
+ ctx->state == MFCINST_RUNNING) &&
+ dev->curr_ctx == ctx->num && dev->hw_lock) {
+ ctx->state = MFCINST_ABORT;
+ s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_FRAME_DONE_RET,
+ 0);
+ }
+ ctx->state = MFCINST_FINISHED;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ INIT_LIST_HEAD(&ctx->dst_queue);
+ ctx->dst_queue_cnt = 0;
+ }
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ cleanup_ref_queue(ctx);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ INIT_LIST_HEAD(&ctx->src_queue);
+ ctx->src_queue_cnt = 0;
+ }
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return 0;
+}
+
+static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *mfc_buf;
+
+ if (ctx->state == MFCINST_ERROR) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ cleanup_ref_queue(ctx);
+ return;
+ }
+ if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
+ mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
+ /* Mark destination as available for use by MFC */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->dst_queue);
+ ctx->dst_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
+ mfc_buf->flags &= ~MFC_BUF_FLAG_USED;
+ spin_lock_irqsave(&dev->irqlock, flags);
+ list_add_tail(&mfc_buf->list, &ctx->src_queue);
+ ctx->src_queue_cnt++;
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ } else {
+ mfc_err("unsupported buffer type (%d)\n", vq->type);
+ }
+ if (s5p_mfc_ctx_ready(ctx))
+ set_work_bit_irqsave(ctx);
+ s5p_mfc_try_run(dev);
+}
+
+static struct vb2_ops s5p_mfc_enc_qops = {
+ .queue_setup = s5p_mfc_queue_setup,
+ .wait_prepare = s5p_mfc_unlock,
+ .wait_finish = s5p_mfc_lock,
+ .buf_init = s5p_mfc_buf_init,
+ .buf_prepare = s5p_mfc_buf_prepare,
+ .start_streaming = s5p_mfc_start_streaming,
+ .stop_streaming = s5p_mfc_stop_streaming,
+ .buf_queue = s5p_mfc_buf_queue,
+};
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
+{
+ return &encoder_codec_ops;
+}
+
+struct vb2_ops *get_enc_queue_ops(void)
+{
+ return &s5p_mfc_enc_qops;
+}
+
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
+{
+ return &s5p_mfc_enc_ioctl_ops;
+}
+
+#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
+ && V4L2_CTRL_DRIVER_PRIV(x))
+
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
+{
+ struct v4l2_ctrl_config cfg;
+ int i;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
+ if (ctx->ctrl_handler.error) {
+ mfc_err("v4l2_ctrl_handler_init failed\n");
+ return ctx->ctrl_handler.error;
+ }
+ for (i = 0; i < NUM_CTRLS; i++) {
+ if (IS_MFC51_PRIV(controls[i].id)) {
+ memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
+ cfg.ops = &s5p_mfc_enc_ctrl_ops;
+ cfg.id = controls[i].id;
+ cfg.min = controls[i].minimum;
+ cfg.max = controls[i].maximum;
+ cfg.def = controls[i].default_value;
+ cfg.name = controls[i].name;
+ cfg.type = controls[i].type;
+ cfg.flags = 0;
+
+ if (cfg.type == V4L2_CTRL_TYPE_MENU) {
+ cfg.step = 0;
+ cfg.menu_skip_mask = cfg.menu_skip_mask;
+ cfg.qmenu = mfc51_get_menu(cfg.id);
+ } else {
+ cfg.step = controls[i].step;
+ cfg.menu_skip_mask = 0;
+ }
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
+ &cfg, NULL);
+ } else {
+ if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+ ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].maximum, 0,
+ controls[i].default_value);
+ } else {
+ ctx->ctrls[i] = v4l2_ctrl_new_std(
+ &ctx->ctrl_handler,
+ &s5p_mfc_enc_ctrl_ops, controls[i].id,
+ controls[i].minimum,
+ controls[i].maximum, controls[i].step,
+ controls[i].default_value);
+ }
+ }
+ if (ctx->ctrl_handler.error) {
+ mfc_err("Adding control (%d) failed\n", i);
+ return ctx->ctrl_handler.error;
+ }
+ if (controls[i].is_volatile && ctx->ctrls[i])
+ ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ return 0;
+}
+
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx)
+{
+ int i;
+
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ for (i = 0; i < NUM_CTRLS; i++)
+ ctx->ctrls[i] = NULL;
+}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
new file mode 100644
index 000000000000..ca9fd66bd310
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_ENC_H_
+#define S5P_MFC_ENC_H_
+
+struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
+struct vb2_ops *get_enc_queue_ops(void);
+const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
+struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
+int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx);
+
+#endif /* S5P_MFC_ENC_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c
new file mode 100644
index 000000000000..37860e299021
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c
@@ -0,0 +1,92 @@
+/*
+ * drivers/media/platform/samsung/mfc5/s5p_mfc_intr.c
+ *
+ * C file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains functions used to wait for command completion.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "regs-mfc.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
+{
+ int ret;
+
+ ret = wait_event_interruptible_timeout(dev->queue,
+ (dev->int_cond && (dev->int_type == command
+ || dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ if (ret == 0) {
+ mfc_err("Interrupt (dev->int_type:%d, command:%d) timed out\n",
+ dev->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (dev->int_type:%d, command: %d)\n",
+ dev->int_type, command);
+ if (dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
+{
+ dev->int_cond = 0;
+ dev->int_type = 0;
+ dev->int_err = 0;
+}
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt)
+{
+ int ret;
+
+ if (interrupt) {
+ ret = wait_event_interruptible_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ } else {
+ ret = wait_event_timeout(ctx->queue,
+ (ctx->int_cond && (ctx->int_type == command
+ || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
+ msecs_to_jiffies(MFC_INT_TIMEOUT));
+ }
+ if (ret == 0) {
+ mfc_err("Interrupt (ctx->int_type:%d, command:%d) timed out\n",
+ ctx->int_type, command);
+ return 1;
+ } else if (ret == -ERESTARTSYS) {
+ mfc_err("Interrupted by a signal\n");
+ return 1;
+ }
+ mfc_debug(1, "Finished waiting (ctx->int_type:%d, command: %d)\n",
+ ctx->int_type, command);
+ if (ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
+ return 1;
+ return 0;
+}
+
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
+{
+ ctx->int_cond = 0;
+ ctx->int_type = 0;
+ ctx->int_err = 0;
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h
new file mode 100644
index 000000000000..18341a88514e
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h
@@ -0,0 +1,26 @@
+/*
+ * drivers/media/platform/samsung/mfc5/s5p_mfc_intr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * It contains waiting functions declarations.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_INTR_H_
+#define S5P_MFC_INTR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
+ int command, int interrupt);
+int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
+void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
+
+#endif /* S5P_MFC_INTR_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
new file mode 100644
index 000000000000..767a51271dc2
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -0,0 +1,1425 @@
+/*
+ * drivers/media/platform/samsung/mfc5/s5p_mfc_opr.c
+ *
+ * Samsung MFC (Multi Function Codec - FIMV) driver
+ * This file contains hw related functions.
+ *
+ * Kamil Debski, Copyright (c) 2011 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#include "regs-mfc.h"
+#include "s5p_mfc_cmd.h"
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_ctrl.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_intr.h"
+#include "s5p_mfc_opr.h"
+#include "s5p_mfc_pm.h"
+#include "s5p_mfc_shm.h"
+#include <asm/cacheflush.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#define OFFSETA(x) (((x) - dev->bank1) >> MFC_OFFSET_SHIFT)
+#define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
+
+/* Allocate temporary buffers for decoding */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
+{
+ void *desc_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ ctx->desc_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
+ if (IS_ERR_VALUE((int)ctx->desc_buf)) {
+ ctx->desc_buf = NULL;
+ mfc_err("Allocating DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ ctx->desc_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->desc_buf);
+ BUG_ON(ctx->desc_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ desc_virt = vb2_dma_contig_memops.vaddr(ctx->desc_buf);
+ if (desc_virt == NULL) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = NULL;
+ mfc_err("Remapping DESC buffer failed\n");
+ return -ENOMEM;
+ }
+ memset(desc_virt, 0, DESC_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
+/* Release temporary buffers for decoding */
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->desc_phys) {
+ vb2_dma_contig_memops.put(ctx->desc_buf);
+ ctx->desc_phys = 0;
+ ctx->desc_buf = NULL;
+ }
+}
+
+/* Allocate codec buffers */
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int enc_ref_y_size = 0;
+ unsigned int enc_ref_c_size = 0;
+ unsigned int guard_width, guard_height;
+
+ if (ctx->type == MFCINST_DECODER) {
+ mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
+ ctx->luma_size, ctx->chroma_size, ctx->mv_size);
+ mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
+ } else if (ctx->type == MFCINST_ENCODER) {
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width,
+ S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height >> 1,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size,
+ S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "recon luma size: %d chroma size: %d\n",
+ enc_ref_y_size, enc_ref_c_size);
+ } else {
+ return -EINVAL;
+ }
+ /* Codecs have different memory requirements */
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
+ S5P_FIMV_DEC_VERT_NB_MV_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_STX_PARSER_SIZE +
+ S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ case S5P_FIMV_CODEC_VC1_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE +
+ 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ ctx->bank1_size = 0;
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ ctx->bank1_size =
+ ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
+ S5P_FIMV_DEC_UPNB_MV_SIZE +
+ S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
+ S5P_FIMV_DEC_NB_DCAC_SIZE,
+ S5P_FIMV_DEC_BUF_ALIGN);
+ ctx->bank2_size = 0;
+ break;
+ case S5P_FIMV_CODEC_H264_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_INTRAMD_SIZE +
+ S5P_FIMV_ENC_NBORINFO_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4) +
+ S5P_FIMV_ENC_INTRAPRED_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_COLFLG_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ ctx->bank1_size = (enc_ref_y_size * 2) +
+ S5P_FIMV_ENC_UPMV_SIZE +
+ S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ ctx->bank2_size = (enc_ref_y_size * 2) +
+ (enc_ref_c_size * 4);
+ break;
+ default:
+ break;
+ }
+ /* Allocate only if memory from bank 1 is necessary */
+ if (ctx->bank1_size > 0) {
+ ctx->bank1_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
+ if (IS_ERR(ctx->bank1_buf)) {
+ ctx->bank1_buf = NULL;
+ printk(KERN_ERR
+ "Buf alloc for decoding failed (port A)\n");
+ return -ENOMEM;
+ }
+ ctx->bank1_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
+ BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ }
+ /* Allocate only if memory from bank 2 is necessary */
+ if (ctx->bank2_size > 0) {
+ ctx->bank2_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
+ if (IS_ERR(ctx->bank2_buf)) {
+ ctx->bank2_buf = NULL;
+ mfc_err("Buf alloc for decoding failed (port B)\n");
+ return -ENOMEM;
+ }
+ ctx->bank2_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
+ BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
+ }
+ return 0;
+}
+
+/* Release buffers allocated for codec */
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->bank1_buf) {
+ vb2_dma_contig_memops.put(ctx->bank1_buf);
+ ctx->bank1_buf = NULL;
+ ctx->bank1_phys = 0;
+ ctx->bank1_size = 0;
+ }
+ if (ctx->bank2_buf) {
+ vb2_dma_contig_memops.put(ctx->bank2_buf);
+ ctx->bank2_buf = NULL;
+ ctx->bank2_phys = 0;
+ ctx->bank2_size = 0;
+ }
+}
+
+/* Allocate memory for instance data buffer */
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ void *context_virt;
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
+ ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ ctx->ctx_size = MFC_H264_CTX_BUF_SIZE;
+ else
+ ctx->ctx_size = MFC_CTX_BUF_SIZE;
+ ctx->ctx_buf = vb2_dma_contig_memops.alloc(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_size);
+ if (IS_ERR(ctx->ctx_buf)) {
+ mfc_err("Allocating context buffer failed\n");
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = NULL;
+ return -ENOMEM;
+ }
+ ctx->ctx_phys = s5p_mfc_mem_cookie(
+ dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_buf);
+ BUG_ON(ctx->ctx_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->ctx_ofs = OFFSETA(ctx->ctx_phys);
+ context_virt = vb2_dma_contig_memops.vaddr(ctx->ctx_buf);
+ if (context_virt == NULL) {
+ mfc_err("Remapping instance buffer failed\n");
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = NULL;
+ return -ENOMEM;
+ }
+ /* Zero content of the allocated memory */
+ memset(context_virt, 0, ctx->ctx_size);
+ wmb();
+ if (s5p_mfc_init_shm(ctx) < 0) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+/* Release instance buffer */
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
+{
+ if (ctx->ctx_buf) {
+ vb2_dma_contig_memops.put(ctx->ctx_buf);
+ ctx->ctx_phys = 0;
+ ctx->ctx_buf = NULL;
+ }
+ if (ctx->shm_alloc) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_alloc = NULL;
+ ctx->shm = NULL;
+ }
+}
+
+/* Set registers for decoding temporary buffers */
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
+ mfc_write(dev, DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
+}
+
+/* Set registers for shared buffer */
+static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
+}
+
+/* Set registers for decoding stream buffer */
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte, unsigned int buf_size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
+ mfc_write(dev, ctx->dec_src_buf_size, S5P_FIMV_SI_CH0_CPB_SIZE);
+ mfc_write(dev, buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
+ s5p_mfc_write_shm(ctx, start_num_byte, START_BYTE_NUM);
+ return 0;
+}
+
+/* Set decoding frame buffer */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx)
+{
+ unsigned int frame_size, i;
+ unsigned int frame_size_ch, frame_size_mv;
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+ size_t buf_addr1, buf_addr2;
+ int buf_size1, buf_size2;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~S5P_FIMV_DPB_COUNT_MASK;
+ mfc_write(dev, ctx->total_dpb_count | dpb,
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ s5p_mfc_set_shared_buffer(ctx);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_VERT_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_NB_IP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG4_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SP_ADR);
+ buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ break;
+ case S5P_FIMV_CODEC_H263_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ break;
+ case S5P_FIMV_CODEC_VC1_DEC:
+ case S5P_FIMV_CODEC_VC1RCV_DEC:
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_NB_DCAC_ADR);
+ buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_OT_LINE_ADR);
+ buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_UP_NB_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_SA_MV_ADR);
+ buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE3_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE2_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE1_ADR);
+ buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
+ break;
+ case S5P_FIMV_CODEC_MPEG2_DEC:
+ break;
+ default:
+ mfc_err("Unknown codec for decoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ break;
+ }
+ frame_size = ctx->luma_size;
+ frame_size_ch = ctx->chroma_size;
+ frame_size_mv = ctx->mv_size;
+ mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
+ frame_size_mv);
+ for (i = 0; i < ctx->total_dpb_count; i++) {
+ /* Bank2 */
+ mfc_debug(2, "Luma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.luma);
+ mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
+ S5P_FIMV_DEC_LUMA_ADR + i * 4);
+ mfc_debug(2, "\tChroma %d: %x\n", i,
+ ctx->dst_bufs[i].cookie.raw.chroma);
+ mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
+ S5P_FIMV_DEC_CHROMA_ADR + i * 4);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
+ mfc_debug(2, "\tBuf2: %x, size: %d\n",
+ buf_addr2, buf_size2);
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_MV_ADR + i * 4);
+ buf_addr2 += frame_size_mv;
+ buf_size2 -= frame_size_mv;
+ }
+ }
+ mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
+ mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
+ buf_size1, buf_size2, ctx->total_dpb_count);
+ if (buf_size1 < 0 || buf_size2 < 0) {
+ mfc_debug(2, "Not enough memory has been allocated\n");
+ return -ENOMEM;
+ }
+ s5p_mfc_write_shm(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
+ s5p_mfc_write_shm(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
+ s5p_mfc_write_shm(ctx, frame_size_mv, ALLOC_MV_SIZE);
+ mfc_write(dev, ((S5P_FIMV_CH_INIT_BUFS & S5P_FIMV_CH_MASK)
+ << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Set registers for encoding stream buffer */
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETA(addr), S5P_FIMV_ENC_SI_CH0_SB_ADR);
+ mfc_write(dev, size, S5P_FIMV_ENC_SI_CH0_SB_SIZE);
+ return 0;
+}
+
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, OFFSETB(y_addr), S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR);
+ mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
+}
+
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ *y_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_Y_ADDR)
+ << MFC_OFFSET_SHIFT);
+ *c_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_C_ADDR)
+ << MFC_OFFSET_SHIFT);
+}
+
+/* Set encoding ref & codec buffer */
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ size_t buf_addr1, buf_addr2;
+ size_t buf_size1, buf_size2;
+ unsigned int enc_ref_y_size, enc_ref_c_size;
+ unsigned int guard_width, guard_height;
+ int i;
+
+ buf_addr1 = ctx->bank1_phys;
+ buf_size1 = ctx->bank1_size;
+ buf_addr2 = ctx->bank2_phys;
+ buf_size2 = ctx->bank2_size;
+ enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
+ enc_ref_c_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
+ * ALIGN((ctx->img_height >> 1), S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(enc_ref_c_size, S5P_FIMV_NV12MT_SALIGN);
+ } else {
+ guard_width = ALIGN(ctx->img_width + 16,
+ S5P_FIMV_NV12MT_HALIGN);
+ guard_height = ALIGN((ctx->img_height >> 1) + 4,
+ S5P_FIMV_NV12MT_VALIGN);
+ enc_ref_c_size = ALIGN(guard_width * guard_height,
+ S5P_FIMV_NV12MT_SALIGN);
+ }
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
+ switch (ctx->codec_mode) {
+ case S5P_FIMV_CODEC_H264_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_UP_INTRA_MD_ADR);
+ buf_addr1 += S5P_FIMV_ENC_INTRAMD_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_INTRAMD_SIZE;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_H264_UP_INTRA_PRED_ADR);
+ buf_addr2 += S5P_FIMV_ENC_INTRAPRED_SIZE;
+ buf_size2 -= S5P_FIMV_ENC_INTRAPRED_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_H264_NBOR_INFO_ADR);
+ buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_MPEG4_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_COZERO_FLAG_ADR);
+ buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_MPEG4_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ case S5P_FIMV_CODEC_H263_ENC:
+ for (i = 0; i < 2; i++) {
+ mfc_write(dev, OFFSETA(buf_addr1),
+ S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
+ buf_addr1 += enc_ref_y_size;
+ buf_size1 -= enc_ref_y_size;
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_y_size;
+ buf_size2 -= enc_ref_y_size;
+ }
+ for (i = 0; i < 4; i++) {
+ mfc_write(dev, OFFSETB(buf_addr2),
+ S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
+ buf_addr2 += enc_ref_c_size;
+ buf_size2 -= enc_ref_c_size;
+ }
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_MV_ADR);
+ buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
+ mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
+ buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
+ mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
+ buf_size1, buf_size2);
+ break;
+ default:
+ mfc_err("Unknown codec set for encoding: %d\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ unsigned int reg;
+ unsigned int shm;
+
+ /* width */
+ mfc_write(dev, ctx->img_width, S5P_FIMV_ENC_HSIZE_PX);
+ /* height */
+ mfc_write(dev, ctx->img_height, S5P_FIMV_ENC_VSIZE_PX);
+ /* pictype : enable, IDR period */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ reg |= (1 << 18);
+ reg &= ~(0xFFFF);
+ reg |= p->gop_size;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ mfc_write(dev, 0, S5P_FIMV_ENC_B_RECON_WRITE_ON);
+ /* multi-slice control */
+ /* multi-slice MB number or bit size */
+ mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL);
+ if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+ mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB);
+ } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
+ mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT);
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB);
+ mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_BIT);
+ }
+ /* cyclic intra refresh */
+ mfc_write(dev, p->intra_refresh_mb, S5P_FIMV_ENC_CIR_CTRL);
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ /* padding control & value */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PADDING_CTRL);
+ if (p->pad) {
+ /** enable */
+ reg |= (1 << 31);
+ /** cr value */
+ reg &= ~(0xFF << 16);
+ reg |= (p->pad_cr << 16);
+ /** cb value */
+ reg &= ~(0xFF << 8);
+ reg |= (p->pad_cb << 8);
+ /** y value */
+ reg &= ~(0xFF);
+ reg |= (p->pad_luma);
+ } else {
+ /** disable & all value clear */
+ reg = 0;
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_PADDING_CTRL);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /** frame-level rate control */
+ reg &= ~(0x1 << 9);
+ reg |= (p->rc_frame << 9);
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* bit rate */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_bitrate,
+ S5P_FIMV_ENC_RC_BIT_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_BIT_RATE);
+ /* reaction coefficient */
+ if (p->rc_frame)
+ mfc_write(dev, p->rc_reaction_coeff, S5P_FIMV_ENC_RC_RPARA);
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* seq header ctrl */
+ shm &= ~(0x1 << 3);
+ shm |= (p->seq_hdr_mode << 3);
+ /* frame skip mode */
+ shm &= ~(0x3 << 1);
+ shm |= (p->frame_skip_mode << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ /* fixed target bit */
+ s5p_mfc_write_shm(ctx, p->fixed_target_bit, RC_CONTROL_CONFIG);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_264->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_264->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* interlace */
+ mfc_write(dev, p->interlace, S5P_FIMV_ENC_PIC_STRUCT);
+ /* height */
+ if (p->interlace)
+ mfc_write(dev, ctx->img_height >> 1, S5P_FIMV_ENC_VSIZE_PX);
+ /* loopfilter ctrl */
+ mfc_write(dev, p_264->loop_filter_mode, S5P_FIMV_ENC_LF_CTRL);
+ /* loopfilter alpha offset */
+ if (p_264->loop_filter_alpha < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_alpha) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_alpha & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_ALPHA_OFF);
+ /* loopfilter beta offset */
+ if (p_264->loop_filter_beta < 0) {
+ reg = 0x10;
+ reg |= (0xFF - p_264->loop_filter_beta) + 1;
+ } else {
+ reg = 0x00;
+ reg |= (p_264->loop_filter_beta & 0xF);
+ }
+ mfc_write(dev, reg, S5P_FIMV_ENC_BETA_OFF);
+ /* entropy coding mode */
+ if (p_264->entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+ mfc_write(dev, 1, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_H264_ENTROPY_MODE);
+ /* number of ref. picture */
+ reg = mfc_read(dev, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* num of ref. pictures of P */
+ reg &= ~(0x3 << 5);
+ reg |= (p_264->num_ref_pic_4p << 5);
+ /* max number of ref. pictures */
+ reg &= ~(0x1F);
+ reg |= p_264->max_ref_pic;
+ mfc_write(dev, reg, S5P_FIMV_ENC_H264_NUM_OF_REF);
+ /* 8x8 transform enable */
+ mfc_write(dev, p_264->_8x8_transform, S5P_FIMV_ENC_H264_TRANS_FLAG);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= (p_264->rc_mb << 8);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_264->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_264->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* macroblock adaptive scaling features */
+ if (p_264->rc_mb) {
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_MB_CTRL);
+ /* dark region */
+ reg &= ~(0x1 << 3);
+ reg |= (p_264->rc_mb_dark << 3);
+ /* smooth region */
+ reg &= ~(0x1 << 2);
+ reg |= (p_264->rc_mb_smooth << 2);
+ /* static region */
+ reg &= ~(0x1 << 1);
+ reg |= (p_264->rc_mb_static << 1);
+ /* high activity region */
+ reg &= ~(0x1);
+ reg |= p_264->rc_mb_activity;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_MB_CTRL);
+ }
+ if (!p->rc_frame &&
+ !p_264->rc_mb) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_264->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_264->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* AR VUI control */
+ shm &= ~(0x1 << 15);
+ shm |= (p_264->vui_sar << 1);
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ if (p_264->vui_sar) {
+ /* aspect ration IDC */
+ shm = s5p_mfc_read_shm(ctx, SAMPLE_ASPECT_RATIO_IDC);
+ shm &= ~(0xFF);
+ shm |= p_264->vui_sar_idc;
+ s5p_mfc_write_shm(ctx, shm, SAMPLE_ASPECT_RATIO_IDC);
+ if (p_264->vui_sar_idc == 0xFF) {
+ /* sample AR info */
+ shm = s5p_mfc_read_shm(ctx, EXTENDED_SAR);
+ shm &= ~(0xFFFFFFFF);
+ shm |= p_264->vui_ext_sar_width << 16;
+ shm |= p_264->vui_ext_sar_height;
+ s5p_mfc_write_shm(ctx, shm, EXTENDED_SAR);
+ }
+ }
+ /* intra picture period for H.264 */
+ shm = s5p_mfc_read_shm(ctx, H264_I_PERIOD);
+ /* control */
+ shm &= ~(0x1 << 16);
+ shm |= (p_264->open_gop << 16);
+ /* value */
+ if (p_264->open_gop) {
+ shm &= ~(0xFFFF);
+ shm |= p_264->open_gop_size;
+ }
+ s5p_mfc_write_shm(ctx, shm, H264_I_PERIOD);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p_264->cpb_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+ unsigned int framerate;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* pictype : number of B */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* num_b_frame - 0 ~ 2 */
+ reg &= ~(0x3 << 16);
+ reg |= (p->num_b_frame << 16);
+ mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
+ /* profile & level */
+ reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
+ /* level */
+ reg &= ~(0xFF << 8);
+ reg |= (p_mpeg4->level << 8);
+ /* profile - 0 ~ 2 */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->profile;
+ mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
+ /* quarter_pixel */
+ mfc_write(dev, p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 6);
+ shm |= (p_mpeg4->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame) {
+ if (p->rc_framerate_denom > 0) {
+ framerate = p->rc_framerate_num * 1000 /
+ p->rc_framerate_denom;
+ mfc_write(dev, framerate,
+ S5P_FIMV_ENC_RC_FRAME_RATE);
+ shm = s5p_mfc_read_shm(ctx, RC_VOP_TIMING);
+ shm &= ~(0xFFFFFFFF);
+ shm |= (1 << 31);
+ shm |= ((p->rc_framerate_num & 0x7FFF) << 16);
+ shm |= (p->rc_framerate_denom & 0xFFFF);
+ s5p_mfc_write_shm(ctx, shm, RC_VOP_TIMING);
+ }
+ } else {
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ }
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_mpeg4->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_mpeg4->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
+ unsigned int reg;
+ unsigned int shm;
+
+ s5p_mfc_set_enc_params(ctx);
+ /* qp */
+ if (!p->rc_frame) {
+ shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
+ shm &= ~(0xFFF);
+ shm |= (p_h263->rc_p_frame_qp & 0x3F);
+ s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
+ }
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_denom)
+ mfc_write(dev, p->rc_framerate_num * 1000
+ / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
+ /* rate control config. */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
+ /* frame QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_frame_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
+ /* max & min value of QP */
+ reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
+ /* max QP */
+ reg &= ~(0x3F << 8);
+ reg |= (p_h263->rc_max_qp << 8);
+ /* min QP */
+ reg &= ~(0x3F);
+ reg |= p_h263->rc_min_qp;
+ mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
+ /* extended encoder ctrl */
+ shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ shm &= ~(0xFFFF << 16);
+ shm |= (p->vbv_size << 16);
+ }
+ s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
+ return 0;
+}
+
+/* Initialize decoding */
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_shared_buffer(ctx);
+ /* Setup loop filter, for decoding this is only valid for MPEG4 */
+ if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
+ mfc_write(dev, ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
+ else
+ mfc_write(dev, 0, S5P_FIMV_ENC_LF_CTRL);
+ mfc_write(dev, ((ctx->slice_interface & S5P_FIMV_SLICE_INT_MASK) <<
+ S5P_FIMV_SLICE_INT_SHIFT) | (ctx->display_delay_enable <<
+ S5P_FIMV_DDELAY_ENA_SHIFT) | ((ctx->display_delay &
+ S5P_FIMV_DDELAY_VAL_MASK) << S5P_FIMV_DDELAY_VAL_SHIFT),
+ S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+ mfc_write(dev,
+ ((S5P_FIMV_CH_SEQ_HEADER & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
+ | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned int dpb;
+
+ if (flush)
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (
+ S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ else
+ dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
+ ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
+ mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
+}
+
+/* Decode a single frame */
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ mfc_write(dev, ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
+ s5p_mfc_set_shared_buffer(ctx);
+ s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
+ /* Issue different commands to instance basing on whether it
+ * is the last frame or not. */
+ switch (last_frame) {
+ case MFC_DEC_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_LAST_FRAME:
+ mfc_write(dev, ((S5P_FIMV_CH_LAST_FRAME & S5P_FIMV_CH_MASK) <<
+ S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ case MFC_DEC_RES_CHANGE:
+ mfc_write(dev, ((S5P_FIMV_CH_FRAME_START_REALLOC &
+ S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
+ S5P_FIMV_SI_CH0_INST_ID);
+ break;
+ }
+ mfc_debug(2, "Decoding a usual frame\n");
+ return 0;
+}
+
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
+ s5p_mfc_set_enc_params_h264(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
+ s5p_mfc_set_enc_params_mpeg4(ctx);
+ else if (ctx->codec_mode == S5P_FIMV_CODEC_H263_ENC)
+ s5p_mfc_set_enc_params_h263(ctx);
+ else {
+ mfc_err("Unknown codec for encoding (%x)\n",
+ ctx->codec_mode);
+ return -EINVAL;
+ }
+ s5p_mfc_set_shared_buffer(ctx);
+ mfc_write(dev, ((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) |
+ (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+ return 0;
+}
+
+/* Encode a single frame */
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ int cmd;
+ /* memory structure cur. frame */
+ if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
+ mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
+ else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
+ mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
+ s5p_mfc_set_shared_buffer(ctx);
+
+ if (ctx->state == MFCINST_FINISHING)
+ cmd = S5P_FIMV_CH_LAST_FRAME;
+ else
+ cmd = S5P_FIMV_CH_FRAME_START;
+ mfc_write(dev, ((cmd & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
+ | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
+
+ return 0;
+}
+
+static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
+{
+ unsigned long flags;
+ int new_ctx;
+ int cnt;
+
+ spin_lock_irqsave(&dev->condlock, flags);
+ new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
+ cnt = 0;
+ while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
+ new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
+ if (++cnt > MFC_NUM_CONTEXTS) {
+ /* No contexts to run */
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return -EAGAIN;
+ }
+ }
+ spin_unlock_irqrestore(&dev->condlock, flags);
+ return new_ctx;
+}
+
+static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+
+ s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_decode_one_frame(ctx, MFC_DEC_RES_CHANGE);
+}
+
+static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_buf *temp_vb;
+ unsigned long flags;
+ unsigned int index;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ /* Frames are being decoded */
+ if (list_empty(&ctx->src_queue)) {
+ mfc_debug(2, "No src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ /* Get the next source buffer */
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ temp_vb->flags |= MFC_BUF_FLAG_USED;
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream,
+ temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ index = temp_vb->b->v4l2_buf.index;
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
+ last_frame = MFC_DEC_LAST_FRAME;
+ mfc_debug(2, "Setting ctx->state to FINISHING\n");
+ ctx->state = MFCINST_FINISHING;
+ }
+ s5p_mfc_decode_one_frame(ctx, last_frame);
+ return 0;
+}
+
+static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ struct s5p_mfc_buf *src_mb;
+ unsigned long src_y_addr, src_c_addr, dst_addr;
+ unsigned int dst_size;
+
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue) && ctx->state != MFCINST_FINISHING) {
+ mfc_debug(2, "no src buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ if (list_empty(&ctx->dst_queue)) {
+ mfc_debug(2, "no dst buffers\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EAGAIN;
+ }
+ if (list_empty(&ctx->src_queue)) {
+ /* send null frame */
+ s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2, dev->bank2);
+ src_mb = NULL;
+ } else {
+ src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
+ list);
+ src_mb->flags |= MFC_BUF_FLAG_USED;
+ if (src_mb->b->v4l2_planes[0].bytesused == 0) {
+ /* send null frame */
+ s5p_mfc_set_enc_frame_buffer(ctx, dev->bank2,
+ dev->bank2);
+ ctx->state = MFCINST_FINISHING;
+ } else {
+ src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b,
+ 0);
+ src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b,
+ 1);
+ s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr,
+ src_c_addr);
+ if (src_mb->flags & MFC_BUF_FLAG_EOS)
+ ctx->state = MFCINST_FINISHING;
+ }
+ }
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_mb->flags |= MFC_BUF_FLAG_USED;
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ mfc_debug(2, "encoding buffer with index=%d state=%d",
+ src_mb ? src_mb->b->v4l2_buf.index : -1, ctx->state);
+ s5p_mfc_encode_one_frame(ctx);
+ return 0;
+}
+
+static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+
+ /* Initializing decoding - parsing header */
+ spin_lock_irqsave(&dev->irqlock, flags);
+ mfc_debug(2, "Preparing to init decoding\n");
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_decode(ctx);
+}
+
+static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *dst_mb;
+ unsigned long dst_addr;
+ unsigned int dst_size;
+
+ s5p_mfc_set_enc_ref_buffer(ctx);
+ spin_lock_irqsave(&dev->irqlock, flags);
+ dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
+ dst_size = vb2_plane_size(dst_mb->b, 0);
+ s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ s5p_mfc_init_encode(ctx);
+}
+
+static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ unsigned long flags;
+ struct s5p_mfc_buf *temp_vb;
+ int ret;
+
+ /*
+ * Header was parsed now starting processing
+ * First set the output frame buffers
+ */
+ if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
+ mfc_err("It seems that not all destionation buffers were "
+ "mmaped\nMFC requires that all destination are mmaped "
+ "before starting processing\n");
+ return -EAGAIN;
+ }
+ spin_lock_irqsave(&dev->irqlock, flags);
+ if (list_empty(&ctx->src_queue)) {
+ mfc_err("Header has been deallocated in the middle of"
+ " initialization\n");
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ return -EIO;
+ }
+ temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
+ mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
+ s5p_mfc_set_dec_stream_buffer(ctx,
+ vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
+ 0, temp_vb->b->v4l2_planes[0].bytesused);
+ spin_unlock_irqrestore(&dev->irqlock, flags);
+ dev->curr_ctx = ctx->num;
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_set_dec_frame_buffer(ctx);
+ if (ret) {
+ mfc_err("Failed to alloc frame mem\n");
+ ctx->state = MFCINST_ERROR;
+ }
+ return ret;
+}
+
+/* Try running an operation on hardware */
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
+{
+ struct s5p_mfc_ctx *ctx;
+ int new_ctx;
+ unsigned int ret = 0;
+
+ if (test_bit(0, &dev->enter_suspend)) {
+ mfc_debug(1, "Entering suspend so do not schedule any jobs\n");
+ return;
+ }
+ /* Check whether hardware is not running */
+ if (test_and_set_bit(0, &dev->hw_lock) != 0) {
+ /* This is perfectly ok, the scheduled ctx should wait */
+ mfc_debug(1, "Couldn't lock HW\n");
+ return;
+ }
+ /* Choose the context to run */
+ new_ctx = s5p_mfc_get_new_ctx(dev);
+ if (new_ctx < 0) {
+ /* No contexts to run */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
+ mfc_err("Failed to unlock hardware\n");
+ return;
+ }
+ mfc_debug(1, "No ctx is scheduled to be run\n");
+ return;
+ }
+ ctx = dev->ctx[new_ctx];
+ /* Got context to run in ctx */
+ /*
+ * Last frame has already been sent to MFC.
+ * Now obtaining frames from MFC buffer
+ */
+ s5p_mfc_clock_on();
+ if (ctx->type == MFCINST_DECODER) {
+ s5p_mfc_set_dec_desc_buffer(ctx);
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_LAST_FRAME);
+ break;
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ case MFCINST_HEAD_PARSED:
+ ret = s5p_mfc_run_init_dec_buffers(ctx);
+ mfc_debug(1, "head parsed\n");
+ break;
+ case MFCINST_RES_CHANGE_INIT:
+ s5p_mfc_run_res_change(ctx);
+ break;
+ case MFCINST_RES_CHANGE_FLUSH:
+ s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
+ break;
+ case MFCINST_RES_CHANGE_END:
+ mfc_debug(2, "Finished remaining frames after resolution change\n");
+ ctx->capture_state = QUEUE_FREE;
+ mfc_debug(2, "Will re-init the codec\n");
+ s5p_mfc_run_init_dec(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else if (ctx->type == MFCINST_ENCODER) {
+ switch (ctx->state) {
+ case MFCINST_FINISHING:
+ case MFCINST_RUNNING:
+ ret = s5p_mfc_run_enc_frame(ctx);
+ break;
+ case MFCINST_INIT:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_open_inst_cmd(ctx);
+ break;
+ case MFCINST_RETURN_INST:
+ s5p_mfc_clean_ctx_int_flags(ctx);
+ ret = s5p_mfc_close_inst_cmd(ctx);
+ break;
+ case MFCINST_GOT_INST:
+ s5p_mfc_run_init_enc(ctx);
+ break;
+ default:
+ ret = -EAGAIN;
+ }
+ } else {
+ mfc_err("Invalid context type: %d\n", ctx->type);
+ ret = -EAGAIN;
+ }
+
+ if (ret) {
+ /* Free hardware lock */
+ if (test_and_clear_bit(0, &dev->hw_lock) == 0)
+ mfc_err("Failed to unlock hardware\n");
+
+ /* This is in deed imporant, as no operation has been
+ * scheduled, reduce the clock count as no one will
+ * ever do this, because no interrupt related to this try_run
+ * will ever come from hardware. */
+ s5p_mfc_clock_off();
+ }
+}
+
+
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
+{
+ struct s5p_mfc_buf *b;
+ int i;
+
+ while (!list_empty(lh)) {
+ b = list_entry(lh->next, struct s5p_mfc_buf, list);
+ for (i = 0; i < b->b->num_planes; i++)
+ vb2_set_plane_payload(b->b, i, 0);
+ vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR);
+ list_del(&b->list);
+ }
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
new file mode 100644
index 000000000000..2ad3def052f8
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -0,0 +1,93 @@
+/*
+ * drivers/media/platform/samsung/mfc5/s5p_mfc_opr.h
+ *
+ * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
+ * Contains declarations of hw related functions.
+ *
+ * Kamil Debski, Copyright (C) 2011 Samsung Electronics
+ * http://www.samsung.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.
+ */
+
+#ifndef S5P_MFC_OPR_H_
+#define S5P_MFC_OPR_H_
+
+#include "s5p_mfc_common.h"
+
+int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_init_encode(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Decoding functions */
+int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx);
+int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
+ unsigned int start_num_byte,
+ unsigned int buf_size);
+
+/* Encoding functions */
+void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long y_addr, unsigned long c_addr);
+int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long addr, unsigned int size);
+void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
+ unsigned long *y_addr, unsigned long *c_addr);
+int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *mfc_ctx);
+
+int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
+ enum s5p_mfc_decode_arg last_frame);
+int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *mfc_ctx);
+
+/* Memory allocation */
+int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
+
+int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx);
+void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx);
+
+void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
+void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
+
+#define s5p_mfc_get_dspl_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dec_y_adr() (readl(dev->regs_base + \
+ S5P_FIMV_SI_DECODE_Y_ADR) << \
+ MFC_OFFSET_SHIFT)
+#define s5p_mfc_get_dspl_status() readl(dev->regs_base + \
+ S5P_FIMV_SI_DISPLAY_STATUS)
+#define s5p_mfc_get_dec_status() readl(dev->regs_base + \
+ S5P_FIMV_SI_DECODE_STATUS)
+#define s5p_mfc_get_frame_type() (readl(dev->regs_base + \
+ S5P_FIMV_DECODE_FRAME_TYPE) \
+ & S5P_FIMV_DECODE_FRAME_MASK)
+#define s5p_mfc_get_consumed_stream() readl(dev->regs_base + \
+ S5P_FIMV_SI_CONSUMED_BYTES)
+#define s5p_mfc_get_int_reason() (readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_CMD) & \
+ S5P_FIMV_RISC2HOST_CMD_MASK)
+#define s5p_mfc_get_int_err() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG2)
+#define s5p_mfc_err_dec(x) (((x) & S5P_FIMV_ERR_DEC_MASK) >> \
+ S5P_FIMV_ERR_DEC_SHIFT)
+#define s5p_mfc_err_dspl(x) (((x) & S5P_FIMV_ERR_DSPL_MASK) >> \
+ S5P_FIMV_ERR_DSPL_SHIFT)
+#define s5p_mfc_get_img_width() readl(dev->regs_base + \
+ S5P_FIMV_SI_HRESOL)
+#define s5p_mfc_get_img_height() readl(dev->regs_base + \
+ S5P_FIMV_SI_VRESOL)
+#define s5p_mfc_get_dpb_count() readl(dev->regs_base + \
+ S5P_FIMV_SI_BUF_NUMBER)
+#define s5p_mfc_get_inst_no() readl(dev->regs_base + \
+ S5P_FIMV_RISC2HOST_ARG1)
+#define s5p_mfc_get_enc_strm_size() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_STRM_SIZE)
+#define s5p_mfc_get_enc_slice_type() readl(dev->regs_base + \
+ S5P_FIMV_ENC_SI_SLICE_TYPE)
+
+#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
new file mode 100644
index 000000000000..0503d14ac94e
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
@@ -0,0 +1,137 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.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.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_PM_RUNTIME
+#include <linux/pm_runtime.h>
+#endif
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+#include "s5p_mfc_pm.h"
+
+#define MFC_CLKNAME "sclk_mfc"
+#define MFC_GATE_CLK_NAME "mfc"
+
+#define CLK_DEBUG
+
+static struct s5p_mfc_pm *pm;
+static struct s5p_mfc_dev *p_dev;
+
+#ifdef CLK_DEBUG
+atomic_t clk_ref;
+#endif
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
+{
+ int ret = 0;
+
+ pm = &dev->pm;
+ p_dev = dev;
+ pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
+ if (IS_ERR(pm->clock_gate)) {
+ mfc_err("Failed to get clock-gating control\n");
+ ret = PTR_ERR(pm->clock_gate);
+ goto err_g_ip_clk;
+ }
+
+ ret = clk_prepare(pm->clock_gate);
+ if (ret) {
+ mfc_err("Failed to preapre clock-gating control\n");
+ goto err_p_ip_clk;
+ }
+
+ pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
+ if (IS_ERR(pm->clock)) {
+ mfc_err("Failed to get MFC clock\n");
+ ret = PTR_ERR(pm->clock);
+ goto err_g_ip_clk_2;
+ }
+
+ ret = clk_prepare(pm->clock);
+ if (ret) {
+ mfc_err("Failed to prepare MFC clock\n");
+ goto err_p_ip_clk_2;
+ }
+
+ atomic_set(&pm->power, 0);
+#ifdef CONFIG_PM_RUNTIME
+ pm->device = &dev->plat_dev->dev;
+ pm_runtime_enable(pm->device);
+#endif
+#ifdef CLK_DEBUG
+ atomic_set(&clk_ref, 0);
+#endif
+ return 0;
+err_p_ip_clk_2:
+ clk_put(pm->clock);
+err_g_ip_clk_2:
+ clk_unprepare(pm->clock_gate);
+err_p_ip_clk:
+ clk_put(pm->clock_gate);
+err_g_ip_clk:
+ return ret;
+}
+
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
+{
+ clk_unprepare(pm->clock_gate);
+ clk_put(pm->clock_gate);
+ clk_unprepare(pm->clock);
+ clk_put(pm->clock);
+#ifdef CONFIG_PM_RUNTIME
+ pm_runtime_disable(pm->device);
+#endif
+}
+
+int s5p_mfc_clock_on(void)
+{
+ int ret;
+#ifdef CLK_DEBUG
+ atomic_inc(&clk_ref);
+ mfc_debug(3, "+ %d", atomic_read(&clk_ref));
+#endif
+ ret = clk_enable(pm->clock_gate);
+ return ret;
+}
+
+void s5p_mfc_clock_off(void)
+{
+#ifdef CLK_DEBUG
+ atomic_dec(&clk_ref);
+ mfc_debug(3, "- %d", atomic_read(&clk_ref));
+#endif
+ clk_disable(pm->clock_gate);
+}
+
+int s5p_mfc_power_on(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_get_sync(pm->device);
+#else
+ atomic_set(&pm->power, 1);
+ return 0;
+#endif
+}
+
+int s5p_mfc_power_off(void)
+{
+#ifdef CONFIG_PM_RUNTIME
+ return pm_runtime_put_sync(pm->device);
+#else
+ atomic_set(&pm->power, 0);
+ return 0;
+#endif
+}
+
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h
new file mode 100644
index 000000000000..875c5346bc85
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h
@@ -0,0 +1,24 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_PM_H_
+#define S5P_MFC_PM_H_
+
+int s5p_mfc_init_pm(struct s5p_mfc_dev *dev);
+void s5p_mfc_final_pm(struct s5p_mfc_dev *dev);
+
+int s5p_mfc_clock_on(void);
+void s5p_mfc_clock_off(void);
+int s5p_mfc_power_on(void);
+int s5p_mfc_power_off(void);
+
+#endif /* S5P_MFC_PM_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_shm.c b/drivers/media/platform/s5p-mfc/s5p_mfc_shm.c
new file mode 100644
index 000000000000..b5933d233a4b
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_shm.c
@@ -0,0 +1,47 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_shm.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.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.
+ */
+
+#ifdef CONFIG_ARCH_EXYNOS4
+#include <linux/dma-mapping.h>
+#endif
+#include <linux/io.h>
+#include "s5p_mfc_common.h"
+#include "s5p_mfc_debug.h"
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ void *shm_alloc_ctx = dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
+
+ ctx->shm_alloc = vb2_dma_contig_memops.alloc(shm_alloc_ctx,
+ SHARED_BUF_SIZE);
+ if (IS_ERR(ctx->shm_alloc)) {
+ mfc_err("failed to allocate shared memory\n");
+ return PTR_ERR(ctx->shm_alloc);
+ }
+ /* shm_ofs only keeps the offset from base (port a) */
+ ctx->shm_ofs = s5p_mfc_mem_cookie(shm_alloc_ctx, ctx->shm_alloc)
+ - dev->bank1;
+ BUG_ON(ctx->shm_ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
+ ctx->shm = vb2_dma_contig_memops.vaddr(ctx->shm_alloc);
+ if (!ctx->shm) {
+ vb2_dma_contig_memops.put(ctx->shm_alloc);
+ ctx->shm_ofs = 0;
+ ctx->shm_alloc = NULL;
+ mfc_err("failed to virt addr of shared memory\n");
+ return -ENOMEM;
+ }
+ memset((void *)ctx->shm, 0, SHARED_BUF_SIZE);
+ wmb();
+ return 0;
+}
+
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_shm.h b/drivers/media/platform/s5p-mfc/s5p_mfc_shm.h
new file mode 100644
index 000000000000..416ebd7ba35a
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_shm.h
@@ -0,0 +1,90 @@
+/*
+ * linux/drivers/media/platform/s5p-mfc/s5p_mfc_shm.h
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ * http://www.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 S5P_MFC_SHM_H_
+#define S5P_MFC_SHM_H_
+
+enum MFC_SHM_OFS {
+ EXTENEDED_DECODE_STATUS = 0x00, /* D */
+ SET_FRAME_TAG = 0x04, /* D */
+ GET_FRAME_TAG_TOP = 0x08, /* D */
+ GET_FRAME_TAG_BOT = 0x0C, /* D */
+ PIC_TIME_TOP = 0x10, /* D */
+ PIC_TIME_BOT = 0x14, /* D */
+ START_BYTE_NUM = 0x18, /* D */
+
+ CROP_INFO_H = 0x20, /* D */
+ CROP_INFO_V = 0x24, /* D */
+ EXT_ENC_CONTROL = 0x28, /* E */
+ ENC_PARAM_CHANGE = 0x2C, /* E */
+ RC_VOP_TIMING = 0x30, /* E, MPEG4 */
+ HEC_PERIOD = 0x34, /* E, MPEG4 */
+ METADATA_ENABLE = 0x38, /* C */
+ METADATA_STATUS = 0x3C, /* C */
+ METADATA_DISPLAY_INDEX = 0x40, /* C */
+ EXT_METADATA_START_ADDR = 0x44, /* C */
+ PUT_EXTRADATA = 0x48, /* C */
+ EXTRADATA_ADDR = 0x4C, /* C */
+
+ ALLOC_LUMA_DPB_SIZE = 0x64, /* D */
+ ALLOC_CHROMA_DPB_SIZE = 0x68, /* D */
+ ALLOC_MV_SIZE = 0x6C, /* D */
+ P_B_FRAME_QP = 0x70, /* E */
+ SAMPLE_ASPECT_RATIO_IDC = 0x74, /* E, H.264, depend on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ EXTENDED_SAR = 0x78, /* E, H.264, depned on
+ ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
+ DISP_PIC_PROFILE = 0x7C, /* D */
+ FLUSH_CMD_TYPE = 0x80, /* C */
+ FLUSH_CMD_INBUF1 = 0x84, /* C */
+ FLUSH_CMD_INBUF2 = 0x88, /* C */
+ FLUSH_CMD_OUTBUF = 0x8C, /* E */
+ NEW_RC_BIT_RATE = 0x90, /* E, format as RC_BIT_RATE(0xC5A8)
+ depend on RC_BIT_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_RC_FRAME_RATE = 0x94, /* E, format as RC_FRAME_RATE(0xD0D0)
+ depend on RC_FRAME_RATE_CHANGE in ENC_PARAM_CHANGE */
+ NEW_I_PERIOD = 0x98, /* E, format as I_FRM_CTRL(0xC504)
+ depend on I_PERIOD_CHANGE in ENC_PARAM_CHANGE */
+ H264_I_PERIOD = 0x9C, /* E, H.264, open GOP */
+ RC_CONTROL_CONFIG = 0xA0, /* E */
+ BATCH_INPUT_ADDR = 0xA4, /* E */
+ BATCH_OUTPUT_ADDR = 0xA8, /* E */
+ BATCH_OUTPUT_SIZE = 0xAC, /* E */
+ MIN_LUMA_DPB_SIZE = 0xB0, /* D */
+ DEVICE_FORMAT_ID = 0xB4, /* C */
+ H264_POC_TYPE = 0xB8, /* D */
+ MIN_CHROMA_DPB_SIZE = 0xBC, /* D */
+ DISP_PIC_FRAME_TYPE = 0xC0, /* D */
+ FREE_LUMA_DPB = 0xC4, /* D, VC1 MPEG4 */
+ ASPECT_RATIO_INFO = 0xC8, /* D, MPEG4 */
+ EXTENDED_PAR = 0xCC, /* D, MPEG4 */
+ DBG_HISTORY_INPUT0 = 0xD0, /* C */
+ DBG_HISTORY_INPUT1 = 0xD4, /* C */
+ DBG_HISTORY_OUTPUT = 0xD8, /* C */
+ HIERARCHICAL_P_QP = 0xE0, /* E, H.264 */
+};
+
+int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx);
+
+#define s5p_mfc_write_shm(ctx, x, ofs) \
+ do { \
+ writel(x, (ctx->shm + ofs)); \
+ wmb(); \
+ } while (0)
+
+static inline u32 s5p_mfc_read_shm(struct s5p_mfc_ctx *ctx, unsigned int ofs)
+{
+ rmb();
+ return readl(ctx->shm + ofs);
+}
+
+#endif /* S5P_MFC_SHM_H_ */
diff --git a/drivers/media/platform/s5p-tv/Kconfig b/drivers/media/platform/s5p-tv/Kconfig
new file mode 100644
index 000000000000..ea11a513033f
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/Kconfig
@@ -0,0 +1,86 @@
+# drivers/media/platform/s5p-tv/Kconfig
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+config VIDEO_SAMSUNG_S5P_TV
+ bool "Samsung TV driver for S5P platform (experimental)"
+ depends on PLAT_S5P && PM_RUNTIME
+ depends on EXPERIMENTAL
+ default n
+ ---help---
+ Say Y here to enable selecting the TV output devices for
+ Samsung S5P platform.
+
+if VIDEO_SAMSUNG_S5P_TV
+
+config VIDEO_SAMSUNG_S5P_HDMI
+ tristate "Samsung HDMI Driver"
+ depends on VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEO_SAMSUNG_S5P_HDMIPHY
+ help
+ Say Y here if you want support for the HDMI output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+ bool "Enable debug for HDMI Driver"
+ depends on VIDEO_SAMSUNG_S5P_HDMI
+ default n
+ help
+ Enables debugging for HDMI driver.
+
+config VIDEO_SAMSUNG_S5P_HDMIPHY
+ tristate "Samsung HDMIPHY Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the physical HDMI
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SII9234
+ tristate "Samsung SII9234 Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the MHL interface
+ in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
+config VIDEO_SAMSUNG_S5P_SDO
+ tristate "Samsung Analog TV Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the analog TV output
+ interface in S5P Samsung SoC. The driver can be compiled
+ as module. It is an auxiliary driver, that exposes a V4L2
+ subdev for use by other drivers. This driver requires
+ hdmiphy driver to work correctly.
+
+config VIDEO_SAMSUNG_S5P_MIXER
+ tristate "Samsung Mixer and Video Processor Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_SAMSUNG_S5P_TV
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here if you want support for the Mixer in Samsung S5P SoCs.
+ This device produce image data to one of output interfaces.
+
+config VIDEO_SAMSUNG_S5P_MIXER_DEBUG
+ bool "Enable debug for Mixer Driver"
+ depends on VIDEO_SAMSUNG_S5P_MIXER
+ default n
+ help
+ Enables debugging for Mixer driver.
+
+endif # VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/media/platform/s5p-tv/Makefile b/drivers/media/platform/s5p-tv/Makefile
new file mode 100644
index 000000000000..7cd47902e269
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/Makefile
@@ -0,0 +1,19 @@
+# drivers/media/platform/samsung/tvout/Makefile
+#
+# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+# http://www.samsung.com/
+# Tomasz Stanislawski <t.stanislaws@samsung.com>
+#
+# Licensed under GPL
+
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
+s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
+s5p-sii9234-y += sii9234_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
+s5p-hdmi-y += hdmi_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
+s5p-sdo-y += sdo_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o
+s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o
+
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
new file mode 100644
index 000000000000..8a9cf43018f6
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -0,0 +1,1009 @@
+/*
+ * Samsung HDMI interface driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#define pr_fmt(fmt) "s5p-tv (hdmi_drv): " fmt
+
+#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
+#define DEBUG
+#endif
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-subdev.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+
+#include <media/s5p_hdmi.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+
+#include "regs-hdmi.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung HDMI");
+MODULE_LICENSE("GPL");
+
+/* default preset configured on probe */
+#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94
+
+struct hdmi_pulse {
+ u32 beg;
+ u32 end;
+};
+
+struct hdmi_timings {
+ struct hdmi_pulse hact;
+ u32 hsyn_pol; /* 0 - high, 1 - low */
+ struct hdmi_pulse hsyn;
+ u32 interlaced;
+ struct hdmi_pulse vact[2];
+ u32 vsyn_pol; /* 0 - high, 1 - low */
+ u32 vsyn_off;
+ struct hdmi_pulse vsyn[2];
+};
+
+struct hdmi_resources {
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *hdmiphy;
+ struct regulator_bulk_data *regul_bulk;
+ int regul_count;
+};
+
+struct hdmi_device {
+ /** base address of HDMI registers */
+ void __iomem *regs;
+ /** HDMI interrupt */
+ unsigned int irq;
+ /** pointer to device parent */
+ struct device *dev;
+ /** subdev generated by HDMI device */
+ struct v4l2_subdev sd;
+ /** V4L2 device structure */
+ struct v4l2_device v4l2_dev;
+ /** subdev of HDMIPHY interface */
+ struct v4l2_subdev *phy_sd;
+ /** subdev of MHL interface */
+ struct v4l2_subdev *mhl_sd;
+ /** configuration of current graphic mode */
+ const struct hdmi_timings *cur_conf;
+ /** flag indicating that timings are dirty */
+ int cur_conf_dirty;
+ /** current preset */
+ u32 cur_preset;
+ /** other resources */
+ struct hdmi_resources res;
+};
+
+static struct platform_device_id hdmi_driver_types[] = {
+ {
+ .name = "s5pv210-hdmi",
+ }, {
+ .name = "exynos4-hdmi",
+ }, {
+ /* end node */
+ }
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops;
+
+static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct hdmi_device, sd);
+}
+
+static inline
+void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
+{
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(hdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
+{
+ writeb(value, hdev->regs + reg_id);
+}
+
+static inline
+void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value)
+{
+ switch (n) {
+ default:
+ writeb(value >> 24, hdev->regs + reg_id + 12);
+ case 3:
+ writeb(value >> 16, hdev->regs + reg_id + 8);
+ case 2:
+ writeb(value >> 8, hdev->regs + reg_id + 4);
+ case 1:
+ writeb(value >> 0, hdev->regs + reg_id + 0);
+ }
+}
+
+static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
+{
+ return readl(hdev->regs + reg_id);
+}
+
+static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
+{
+ struct hdmi_device *hdev = dev_data;
+ u32 intc_flag;
+
+ (void)irq;
+ intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG);
+ /* clearing flags for HPD plug/unplug */
+ if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+ pr_info("unplugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_UNPLUG);
+ }
+ if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+ pr_info("plugged\n");
+ hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_PLUG);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void hdmi_reg_init(struct hdmi_device *hdev)
+{
+ /* enable HPD interrupts */
+ hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
+ HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
+ /* choose DVI mode */
+ hdmi_write_mask(hdev, HDMI_MODE_SEL,
+ HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
+ hdmi_write_mask(hdev, HDMI_CON_2, ~0,
+ HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN);
+ /* disable bluescreen */
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+ /* choose bluescreen (fecal) color */
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
+ hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
+}
+
+static void hdmi_timing_apply(struct hdmi_device *hdev,
+ const struct hdmi_timings *t)
+{
+ /* setting core registers */
+ hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg);
+ hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3,
+ (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg);
+ hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol);
+ hdmi_writebn(hdev, HDMI_V_BLANK_0, 3,
+ (t->vact[0].beg << 11) | t->vact[0].end);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3,
+ (t->vsyn[0].beg << 12) | t->vsyn[0].end);
+ if (t->interlaced) {
+ u32 vsyn_trans = t->hsyn.beg + t->vsyn_off;
+
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1);
+ hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
+ (t->hact.end << 12) | t->vact[1].end);
+ hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3,
+ (t->vact[1].end << 11) | t->vact[1].beg);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3,
+ (t->vsyn[1].beg << 12) | t->vsyn[1].end);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3,
+ (vsyn_trans << 12) | vsyn_trans);
+ } else {
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0);
+ hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
+ (t->hact.end << 12) | t->vact[0].end);
+ }
+
+ /* Timing generator registers */
+ hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end);
+ hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg);
+ hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2,
+ t->vact[0].end - t->vact[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg);
+ if (t->interlaced) {
+ hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN);
+ hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end);
+ hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg);
+ } else {
+ hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN);
+ hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end);
+ }
+}
+
+static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
+{
+ struct device *dev = hdmi_dev->dev;
+ const struct hdmi_timings *conf = hdmi_dev->cur_conf;
+ struct v4l2_dv_preset preset;
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* skip if conf is already synchronized with HW */
+ if (!hdmi_dev->cur_conf_dirty)
+ return 0;
+
+ /* reset hdmiphy */
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ mdelay(10);
+
+ /* configure presets */
+ preset.preset = hdmi_dev->cur_preset;
+ ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
+ if (ret) {
+ dev_err(dev, "failed to set preset (%u)\n", preset.preset);
+ return ret;
+ }
+
+ /* resetting HDMI core */
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+ hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+
+ hdmi_reg_init(hdmi_dev);
+
+ /* setting core registers */
+ hdmi_timing_apply(hdmi_dev, conf);
+
+ hdmi_dev->cur_conf_dirty = 0;
+
+ return 0;
+}
+
+static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
+{
+#define DUMPREG(reg_id) \
+ dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \
+ readl(hdev->regs + reg_id))
+
+ dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_INTC_FLAG);
+ DUMPREG(HDMI_INTC_CON);
+ DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_PHY_RSTOUT);
+ DUMPREG(HDMI_PHY_VPLL);
+ DUMPREG(HDMI_PHY_CMU);
+ DUMPREG(HDMI_CORE_RSTOUT);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_CON_0);
+ DUMPREG(HDMI_CON_1);
+ DUMPREG(HDMI_CON_2);
+ DUMPREG(HDMI_SYS_STATUS);
+ DUMPREG(HDMI_PHY_STATUS);
+ DUMPREG(HDMI_STATUS_EN);
+ DUMPREG(HDMI_HPD);
+ DUMPREG(HDMI_MODE_SEL);
+ DUMPREG(HDMI_HPD_GEN);
+ DUMPREG(HDMI_DC_CONTROL);
+ DUMPREG(HDMI_VIDEO_PATTERN_GEN);
+
+ dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_H_BLANK_0);
+ DUMPREG(HDMI_H_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_0);
+ DUMPREG(HDMI_V_BLANK_1);
+ DUMPREG(HDMI_V_BLANK_2);
+ DUMPREG(HDMI_H_V_LINE_0);
+ DUMPREG(HDMI_H_V_LINE_1);
+ DUMPREG(HDMI_H_V_LINE_2);
+ DUMPREG(HDMI_VSYNC_POL);
+ DUMPREG(HDMI_INT_PRO_MODE);
+ DUMPREG(HDMI_V_BLANK_F_0);
+ DUMPREG(HDMI_V_BLANK_F_1);
+ DUMPREG(HDMI_V_BLANK_F_2);
+ DUMPREG(HDMI_H_SYNC_GEN_0);
+ DUMPREG(HDMI_H_SYNC_GEN_1);
+ DUMPREG(HDMI_H_SYNC_GEN_2);
+ DUMPREG(HDMI_V_SYNC_GEN_1_0);
+ DUMPREG(HDMI_V_SYNC_GEN_1_1);
+ DUMPREG(HDMI_V_SYNC_GEN_1_2);
+ DUMPREG(HDMI_V_SYNC_GEN_2_0);
+ DUMPREG(HDMI_V_SYNC_GEN_2_1);
+ DUMPREG(HDMI_V_SYNC_GEN_2_2);
+ DUMPREG(HDMI_V_SYNC_GEN_3_0);
+ DUMPREG(HDMI_V_SYNC_GEN_3_1);
+ DUMPREG(HDMI_V_SYNC_GEN_3_2);
+
+ dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_TG_CMD);
+ DUMPREG(HDMI_TG_H_FSZ_L);
+ DUMPREG(HDMI_TG_H_FSZ_H);
+ DUMPREG(HDMI_TG_HACT_ST_L);
+ DUMPREG(HDMI_TG_HACT_ST_H);
+ DUMPREG(HDMI_TG_HACT_SZ_L);
+ DUMPREG(HDMI_TG_HACT_SZ_H);
+ DUMPREG(HDMI_TG_V_FSZ_L);
+ DUMPREG(HDMI_TG_V_FSZ_H);
+ DUMPREG(HDMI_TG_VSYNC_L);
+ DUMPREG(HDMI_TG_VSYNC_H);
+ DUMPREG(HDMI_TG_VSYNC2_L);
+ DUMPREG(HDMI_TG_VSYNC2_H);
+ DUMPREG(HDMI_TG_VACT_ST_L);
+ DUMPREG(HDMI_TG_VACT_ST_H);
+ DUMPREG(HDMI_TG_VACT_SZ_L);
+ DUMPREG(HDMI_TG_VACT_SZ_H);
+ DUMPREG(HDMI_TG_FIELD_CHG_L);
+ DUMPREG(HDMI_TG_FIELD_CHG_H);
+ DUMPREG(HDMI_TG_VACT_ST2_L);
+ DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static const struct hdmi_timings hdmi_timings_480p = {
+ .hact = { .beg = 138, .end = 858 },
+ .hsyn_pol = 1,
+ .hsyn = { .beg = 16, .end = 16 + 62 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 42 + 3, .end = 522 + 3 },
+ .vsyn_pol = 1,
+ .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3},
+};
+
+static const struct hdmi_timings hdmi_timings_576p50 = {
+ .hact = { .beg = 144, .end = 864 },
+ .hsyn_pol = 1,
+ .hsyn = { .beg = 12, .end = 12 + 64 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 44 + 5, .end = 620 + 5 },
+ .vsyn_pol = 1,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
+};
+
+static const struct hdmi_timings hdmi_timings_720p60 = {
+ .hact = { .beg = 370, .end = 1650 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 110, .end = 110 + 40 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
+};
+
+static const struct hdmi_timings hdmi_timings_720p50 = {
+ .hact = { .beg = 700, .end = 1980 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 440, .end = 440 + 40 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p24 = {
+ .hact = { .beg = 830, .end = 2750 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 638, .end = 638 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p60 = {
+ .hact = { .beg = 280, .end = 2200 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 88, .end = 88 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
+};
+
+static const struct hdmi_timings hdmi_timings_1080i60 = {
+ .hact = { .beg = 280, .end = 2200 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 88, .end = 88 + 44 },
+ .interlaced = 1,
+ .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
+ .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
+ .vsyn_pol = 0,
+ .vsyn_off = 1100,
+ .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
+ .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
+};
+
+static const struct hdmi_timings hdmi_timings_1080i50 = {
+ .hact = { .beg = 720, .end = 2640 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 528, .end = 528 + 44 },
+ .interlaced = 1,
+ .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
+ .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
+ .vsyn_pol = 0,
+ .vsyn_off = 1320,
+ .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
+ .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p50 = {
+ .hact = { .beg = 720, .end = 2640 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 528, .end = 528 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
+};
+
+static const struct {
+ u32 preset;
+ const struct hdmi_timings *timings;
+} hdmi_timings[] = {
+ { V4L2_DV_480P59_94, &hdmi_timings_480p },
+ { V4L2_DV_576P50, &hdmi_timings_576p50 },
+ { V4L2_DV_720P50, &hdmi_timings_720p50 },
+ { V4L2_DV_720P59_94, &hdmi_timings_720p60 },
+ { V4L2_DV_720P60, &hdmi_timings_720p60 },
+ { V4L2_DV_1080P24, &hdmi_timings_1080p24 },
+ { V4L2_DV_1080P30, &hdmi_timings_1080p60 },
+ { V4L2_DV_1080P50, &hdmi_timings_1080p50 },
+ { V4L2_DV_1080I50, &hdmi_timings_1080i50 },
+ { V4L2_DV_1080I60, &hdmi_timings_1080i60 },
+ { V4L2_DV_1080P60, &hdmi_timings_1080p60 },
+};
+
+static const struct hdmi_timings *hdmi_preset2timings(u32 preset)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i)
+ if (hdmi_timings[i].preset == preset)
+ return hdmi_timings[i].timings;
+ return NULL;
+}
+
+static int hdmi_streamon(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ int ret, tries;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ ret = hdmi_conf_apply(hdev);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
+ if (ret)
+ return ret;
+
+ /* waiting for HDMIPHY's PLL to get to steady state */
+ for (tries = 100; tries; --tries) {
+ u32 val = hdmi_read(hdev, HDMI_PHY_STATUS);
+ if (val & HDMI_PHY_STATUS_READY)
+ break;
+ mdelay(1);
+ }
+ /* steady state not achieved */
+ if (tries == 0) {
+ dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "hdmiphy - s_stream");
+ return -EIO;
+ }
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
+ if (hdev->mhl_sd && ret) {
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "mhl - s_stream");
+ return -EIO;
+ }
+
+ /* hdmiphy clock is used for HDMI in streaming mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
+ clk_enable(res->sclk_hdmi);
+
+ /* enable HDMI and timing generator */
+ hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+ hdmi_dumpregs(hdev, "streamon");
+ return 0;
+}
+
+static int hdmi_streamoff(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN);
+ hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN);
+
+ /* pixel(vpll) clock is used for HDMI in config mode */
+ clk_disable(res->sclk_hdmi);
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ clk_enable(res->sclk_hdmi);
+
+ v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+
+ hdmi_dumpregs(hdev, "streamoff");
+ return 0;
+}
+
+static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+
+ dev_dbg(dev, "%s(%d)\n", __func__, enable);
+ if (enable)
+ return hdmi_streamon(hdev);
+ return hdmi_streamoff(hdev);
+}
+
+static void hdmi_resource_poweron(struct hdmi_resources *res)
+{
+ /* turn HDMI power on */
+ regulator_bulk_enable(res->regul_count, res->regul_bulk);
+ /* power-on hdmi physical interface */
+ clk_enable(res->hdmiphy);
+ /* use VPP as parent clock; HDMIPHY is not working yet */
+ clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+ /* turn clocks on */
+ clk_enable(res->sclk_hdmi);
+}
+
+static void hdmi_resource_poweroff(struct hdmi_resources *res)
+{
+ /* turn clocks off */
+ clk_disable(res->sclk_hdmi);
+ /* power-off hdmiphy */
+ clk_disable(res->hdmiphy);
+ /* turn HDMI power off */
+ regulator_bulk_disable(res->regul_count, res->regul_bulk);
+}
+
+static int hdmi_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(hdev->dev);
+ else
+ ret = pm_runtime_put_sync(hdev->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ struct device *dev = hdev->dev;
+ const struct hdmi_timings *conf;
+
+ conf = hdmi_preset2timings(preset->preset);
+ if (conf == NULL) {
+ dev_err(dev, "preset (%u) not supported\n", preset->preset);
+ return -EINVAL;
+ }
+ hdev->cur_conf = conf;
+ hdev->cur_conf_dirty = 1;
+ hdev->cur_preset = preset->preset;
+ return 0;
+}
+
+static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *preset)
+{
+ memset(preset, 0, sizeof(*preset));
+ preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
+ return 0;
+}
+
+static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ const struct hdmi_timings *t = hdev->cur_conf;
+
+ dev_dbg(hdev->dev, "%s\n", __func__);
+ if (!hdev->cur_conf)
+ return -EINVAL;
+ memset(fmt, 0, sizeof *fmt);
+ fmt->width = t->hact.end - t->hact.beg;
+ fmt->height = t->vact[0].end - t->vact[0].beg;
+ fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ if (t->interlaced) {
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->height *= 2;
+ } else {
+ fmt->field = V4L2_FIELD_NONE;
+ }
+ return 0;
+}
+
+static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
+ struct v4l2_dv_enum_preset *preset)
+{
+ if (preset->index >= ARRAY_SIZE(hdmi_timings))
+ return -EINVAL;
+ return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset,
+ preset);
+}
+
+static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
+ .s_power = hdmi_s_power,
+};
+
+static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
+ .s_dv_preset = hdmi_s_dv_preset,
+ .g_dv_preset = hdmi_g_dv_preset,
+ .enum_dv_presets = hdmi_enum_dv_presets,
+ .g_mbus_fmt = hdmi_g_mbus_fmt,
+ .s_stream = hdmi_s_stream,
+};
+
+static const struct v4l2_subdev_ops hdmi_sd_ops = {
+ .core = &hdmi_sd_core_ops,
+ .video = &hdmi_sd_video_ops,
+};
+
+static int hdmi_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+
+ dev_dbg(dev, "%s\n", __func__);
+ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
+ hdmi_resource_poweroff(&hdev->res);
+ /* flag that device context is lost */
+ hdev->cur_conf_dirty = 1;
+ return 0;
+}
+
+static int hdmi_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
+ int ret = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ hdmi_resource_poweron(&hdev->res);
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
+ if (hdev->mhl_sd && ret)
+ goto fail;
+
+ dev_dbg(dev, "poweron succeed\n");
+
+ return 0;
+
+fail:
+ hdmi_resource_poweroff(&hdev->res);
+ dev_err(dev, "poweron failed\n");
+
+ return ret;
+}
+
+static const struct dev_pm_ops hdmi_pm_ops = {
+ .runtime_suspend = hdmi_runtime_suspend,
+ .runtime_resume = hdmi_runtime_resume,
+};
+
+static void hdmi_resources_cleanup(struct hdmi_device *hdev)
+{
+ struct hdmi_resources *res = &hdev->res;
+
+ dev_dbg(hdev->dev, "HDMI resource cleanup\n");
+ /* put clocks, power */
+ if (res->regul_count)
+ regulator_bulk_free(res->regul_count, res->regul_bulk);
+ /* kfree is NULL-safe */
+ kfree(res->regul_bulk);
+ if (!IS_ERR_OR_NULL(res->hdmiphy))
+ clk_put(res->hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
+ clk_put(res->sclk_hdmiphy);
+ if (!IS_ERR_OR_NULL(res->sclk_pixel))
+ clk_put(res->sclk_pixel);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->hdmi))
+ clk_put(res->hdmi);
+ memset(res, 0, sizeof *res);
+}
+
+static int hdmi_resources_init(struct hdmi_device *hdev)
+{
+ struct device *dev = hdev->dev;
+ struct hdmi_resources *res = &hdev->res;
+ static char *supply[] = {
+ "hdmi-en",
+ "vdd",
+ "vdd_osc",
+ "vdd_pll",
+ };
+ int i, ret;
+
+ dev_dbg(dev, "HDMI resource init\n");
+
+ memset(res, 0, sizeof *res);
+ /* get clocks, power */
+
+ res->hdmi = clk_get(dev, "hdmi");
+ if (IS_ERR_OR_NULL(res->hdmi)) {
+ dev_err(dev, "failed to get clock 'hdmi'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_pixel = clk_get(dev, "sclk_pixel");
+ if (IS_ERR_OR_NULL(res->sclk_pixel)) {
+ dev_err(dev, "failed to get clock 'sclk_pixel'\n");
+ goto fail;
+ }
+ res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
+ if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
+ dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
+ goto fail;
+ }
+ res->hdmiphy = clk_get(dev, "hdmiphy");
+ if (IS_ERR_OR_NULL(res->hdmiphy)) {
+ dev_err(dev, "failed to get clock 'hdmiphy'\n");
+ goto fail;
+ }
+ res->regul_bulk = kcalloc(ARRAY_SIZE(supply),
+ sizeof(res->regul_bulk[0]), GFP_KERNEL);
+ if (!res->regul_bulk) {
+ dev_err(dev, "failed to get memory for regulators\n");
+ goto fail;
+ }
+ for (i = 0; i < ARRAY_SIZE(supply); ++i) {
+ res->regul_bulk[i].supply = supply[i];
+ res->regul_bulk[i].consumer = NULL;
+ }
+
+ ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
+ if (ret) {
+ dev_err(dev, "failed to get regulators\n");
+ goto fail;
+ }
+ res->regul_count = ARRAY_SIZE(supply);
+
+ return 0;
+fail:
+ dev_err(dev, "HDMI resource init - failed\n");
+ hdmi_resources_cleanup(hdev);
+ return -ENODEV;
+}
+
+static int __devinit hdmi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct i2c_adapter *adapter;
+ struct v4l2_subdev *sd;
+ struct hdmi_device *hdmi_dev = NULL;
+ struct s5p_hdmi_platform_data *pdata = dev->platform_data;
+ int ret;
+
+ dev_dbg(dev, "probe start\n");
+
+ if (!pdata) {
+ dev_err(dev, "platform data is missing\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
+ if (!hdmi_dev) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ hdmi_dev->dev = dev;
+
+ ret = hdmi_resources_init(hdmi_dev);
+ if (ret)
+ goto fail;
+
+ /* mapping HDMI registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (hdmi_dev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_init;
+ }
+
+ ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
+ "hdmi", hdmi_dev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail_init;
+ }
+ hdmi_dev->irq = res->start;
+
+ /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
+ strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
+ sizeof(hdmi_dev->v4l2_dev.name));
+ /* passing NULL owner prevents driver from erasing drvdata */
+ ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
+ if (ret) {
+ dev_err(dev, "could not register v4l2 device.\n");
+ goto fail_init;
+ }
+
+ /* testing if hdmiphy info is present */
+ if (!pdata->hdmiphy_info) {
+ dev_err(dev, "hdmiphy info is missing in platform data\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ adapter = i2c_get_adapter(pdata->hdmiphy_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "hdmiphy adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
+ adapter, pdata->hdmiphy_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->phy_sd == NULL) {
+ dev_err(dev, "missing subdev for hdmiphy\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+
+ /* initialization of MHL interface if present */
+ if (pdata->mhl_info) {
+ adapter = i2c_get_adapter(pdata->mhl_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "MHL adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
+ &hdmi_dev->v4l2_dev, adapter,
+ pdata->mhl_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->mhl_sd == NULL) {
+ dev_err(dev, "missing subdev for MHL\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+ }
+
+ clk_enable(hdmi_dev->res.hdmi);
+
+ pm_runtime_enable(dev);
+
+ sd = &hdmi_dev->sd;
+ v4l2_subdev_init(sd, &hdmi_sd_ops);
+ sd->owner = THIS_MODULE;
+
+ strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
+ hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
+ /* FIXME: missing fail preset is not supported */
+ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset);
+ hdmi_dev->cur_conf_dirty = 1;
+
+ /* storing subdev for call that have only access to struct device */
+ dev_set_drvdata(dev, sd);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_vdev:
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+
+fail_init:
+ hdmi_resources_cleanup(hdmi_dev);
+
+fail:
+ dev_err(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit hdmi_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd);
+
+ pm_runtime_disable(dev);
+ clk_disable(hdmi_dev->res.hdmi);
+ v4l2_device_unregister(&hdmi_dev->v4l2_dev);
+ disable_irq(hdmi_dev->irq);
+ hdmi_resources_cleanup(hdmi_dev);
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+static struct platform_driver hdmi_driver __refdata = {
+ .probe = hdmi_probe,
+ .remove = __devexit_p(hdmi_remove),
+ .id_table = hdmi_driver_types,
+ .driver = {
+ .name = "s5p-hdmi",
+ .owner = THIS_MODULE,
+ .pm = &hdmi_pm_ops,
+ }
+};
+
+module_platform_driver(hdmi_driver);
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
index f67b38631801..f67b38631801 100644
--- a/drivers/media/video/s5p-tv/hdmiphy_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/platform/s5p-tv/mixer.h
index ddb422e23550..ddb422e23550 100644
--- a/drivers/media/video/s5p-tv/mixer.h
+++ b/drivers/media/platform/s5p-tv/mixer.h
diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c
new file mode 100644
index 000000000000..ca0f29717448
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/mixer_drv.c
@@ -0,0 +1,487 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include "mixer.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MIXER");
+MODULE_LICENSE("GPL");
+
+/* --------- DRIVER PARAMETERS ---------- */
+
+static struct mxr_output_conf mxr_output_conf[] = {
+ {
+ .output_name = "S5P HDMI connector",
+ .module_name = "s5p-hdmi",
+ .cookie = 1,
+ },
+ {
+ .output_name = "S5P SDO connector",
+ .module_name = "s5p-sdo",
+ .cookie = 0,
+ },
+};
+
+void mxr_get_mbus_fmt(struct mxr_device *mdev,
+ struct v4l2_mbus_framefmt *mbus_fmt)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ mutex_lock(&mdev->mutex);
+ sd = to_outsd(mdev);
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_streamer_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 1) {
+ struct v4l2_subdev *sd = to_outsd(mdev);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct mxr_resources *res = &mdev->res;
+ int ret;
+
+ if (to_output(mdev)->cookie == 0)
+ clk_set_parent(res->sclk_mixer, res->sclk_dac);
+ else
+ clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
+ mxr_reg_s_output(mdev, to_output(mdev)->cookie);
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt);
+ WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ WARN(ret, "starting stream failed for output %s\n", sd->name);
+
+ mxr_reg_set_mbus_fmt(mdev, &mbus_fmt);
+ mxr_reg_streamon(mdev);
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ }
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+ /* FIXME: what to do when streaming fails? */
+}
+
+void mxr_streamer_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_streamer;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
+ if (mdev->n_streamer == 0) {
+ int ret;
+ struct v4l2_subdev *sd = to_outsd(mdev);
+
+ mxr_reg_streamoff(mdev);
+ /* vsync applies Mixer setup */
+ ret = mxr_reg_wait4vsync(mdev);
+ WARN(ret, "failed to get vsync (%d) from output\n", ret);
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ WARN(ret, "stopping stream failed for output %s\n", sd->name);
+ }
+ WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
+ mdev->n_streamer);
+ mutex_unlock(&mdev->mutex);
+ mxr_reg_dump(mdev);
+}
+
+void mxr_output_get(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ ++mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 1)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
+ mutex_unlock(&mdev->mutex);
+}
+
+void mxr_output_put(struct mxr_device *mdev)
+{
+ mutex_lock(&mdev->mutex);
+ --mdev->n_output;
+ mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
+ /* turn on auxiliary driver */
+ if (mdev->n_output == 0)
+ v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
+ WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
+ mdev->n_output);
+ mutex_unlock(&mdev->mutex);
+}
+
+int mxr_power_get(struct mxr_device *mdev)
+{
+ int ret = pm_runtime_get_sync(mdev->dev);
+
+ /* returning 1 means that power is already enabled,
+ * so zero success be returned */
+ if (IS_ERR_VALUE(ret))
+ return ret;
+ return 0;
+}
+
+void mxr_power_put(struct mxr_device *mdev)
+{
+ pm_runtime_put_sync(mdev->dev);
+}
+
+/* --------- RESOURCE MANAGEMENT -------------*/
+
+static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.mxr_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
+ if (res == NULL) {
+ mxr_err(mdev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ mdev->res.vp_regs = ioremap(res->start, resource_size(res));
+ if (mdev->res.vp_regs == NULL) {
+ mxr_err(mdev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail_mxr_regs;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
+ if (res == NULL) {
+ mxr_err(mdev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail_vp_regs;
+ }
+
+ ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
+ if (ret) {
+ mxr_err(mdev, "request interrupt failed.\n");
+ goto fail_vp_regs;
+ }
+ mdev->res.irq = res->start;
+
+ return 0;
+
+fail_vp_regs:
+ iounmap(mdev->res.vp_regs);
+
+fail_mxr_regs:
+ iounmap(mdev->res.mxr_regs);
+
+fail:
+ return ret;
+}
+
+static void mxr_release_plat_resources(struct mxr_device *mdev)
+{
+ free_irq(mdev->res.irq, mdev);
+ iounmap(mdev->res.vp_regs);
+ iounmap(mdev->res.mxr_regs);
+}
+
+static void mxr_release_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+
+ if (!IS_ERR_OR_NULL(res->sclk_dac))
+ clk_put(res->sclk_dac);
+ if (!IS_ERR_OR_NULL(res->sclk_hdmi))
+ clk_put(res->sclk_hdmi);
+ if (!IS_ERR_OR_NULL(res->sclk_mixer))
+ clk_put(res->sclk_mixer);
+ if (!IS_ERR_OR_NULL(res->vp))
+ clk_put(res->vp);
+ if (!IS_ERR_OR_NULL(res->mixer))
+ clk_put(res->mixer);
+}
+
+static int mxr_acquire_clocks(struct mxr_device *mdev)
+{
+ struct mxr_resources *res = &mdev->res;
+ struct device *dev = mdev->dev;
+
+ res->mixer = clk_get(dev, "mixer");
+ if (IS_ERR_OR_NULL(res->mixer)) {
+ mxr_err(mdev, "failed to get clock 'mixer'\n");
+ goto fail;
+ }
+ res->vp = clk_get(dev, "vp");
+ if (IS_ERR_OR_NULL(res->vp)) {
+ mxr_err(mdev, "failed to get clock 'vp'\n");
+ goto fail;
+ }
+ res->sclk_mixer = clk_get(dev, "sclk_mixer");
+ if (IS_ERR_OR_NULL(res->sclk_mixer)) {
+ mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
+ goto fail;
+ }
+ res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
+ if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
+ mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
+ goto fail;
+ }
+ res->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(res->sclk_dac)) {
+ mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
+ goto fail;
+ }
+
+ return 0;
+fail:
+ mxr_release_clocks(mdev);
+ return -ENODEV;
+}
+
+static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
+ struct platform_device *pdev)
+{
+ int ret;
+ ret = mxr_acquire_plat_resources(mdev, pdev);
+
+ if (ret)
+ goto fail;
+
+ ret = mxr_acquire_clocks(mdev);
+ if (ret)
+ goto fail_plat;
+
+ mxr_info(mdev, "resources acquired\n");
+ return 0;
+
+fail_plat:
+ mxr_release_plat_resources(mdev);
+fail:
+ mxr_err(mdev, "resources acquire failed\n");
+ return ret;
+}
+
+static void mxr_release_resources(struct mxr_device *mdev)
+{
+ mxr_release_clocks(mdev);
+ mxr_release_plat_resources(mdev);
+ memset(&mdev->res, 0, sizeof mdev->res);
+}
+
+static void mxr_release_layers(struct mxr_device *mdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
+ if (mdev->layer[i])
+ mxr_layer_release(mdev->layer[i]);
+}
+
+static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
+ struct mxr_platform_data *pdata)
+{
+ mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
+ mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
+ mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
+
+ if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
+ mxr_err(mdev, "failed to acquire layers\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ mxr_release_layers(mdev);
+ return -ENODEV;
+}
+
+/* ---------- POWER MANAGEMENT ----------- */
+
+static int mxr_runtime_resume(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+
+ mxr_dbg(mdev, "resume - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks on */
+ clk_enable(res->mixer);
+ clk_enable(res->vp);
+ clk_enable(res->sclk_mixer);
+ /* apply default configuration */
+ mxr_reg_reset(mdev);
+ mxr_dbg(mdev, "resume - finished\n");
+
+ mutex_unlock(&mdev->mutex);
+ return 0;
+}
+
+static int mxr_runtime_suspend(struct device *dev)
+{
+ struct mxr_device *mdev = to_mdev(dev);
+ struct mxr_resources *res = &mdev->res;
+ mxr_dbg(mdev, "suspend - start\n");
+ mutex_lock(&mdev->mutex);
+ /* turn clocks off */
+ clk_disable(res->sclk_mixer);
+ clk_disable(res->vp);
+ clk_disable(res->mixer);
+ mutex_unlock(&mdev->mutex);
+ mxr_dbg(mdev, "suspend - finished\n");
+ return 0;
+}
+
+static const struct dev_pm_ops mxr_pm_ops = {
+ .runtime_suspend = mxr_runtime_suspend,
+ .runtime_resume = mxr_runtime_resume,
+};
+
+/* --------- DRIVER INITIALIZATION ---------- */
+
+static int __devinit mxr_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_platform_data *pdata = dev->platform_data;
+ struct mxr_device *mdev;
+ int ret;
+
+ /* mdev does not exist yet so no mxr_dbg is used */
+ dev_info(dev, "probe start\n");
+
+ mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
+ if (!mdev) {
+ dev_err(dev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /* setup pointer to master device */
+ mdev->dev = dev;
+
+ mutex_init(&mdev->mutex);
+ spin_lock_init(&mdev->reg_slock);
+ init_waitqueue_head(&mdev->event_queue);
+
+ /* acquire resources: regs, irqs, clocks, regulators */
+ ret = mxr_acquire_resources(mdev, pdev);
+ if (ret)
+ goto fail_mem;
+
+ /* configure resources for video output */
+ ret = mxr_acquire_video(mdev, mxr_output_conf,
+ ARRAY_SIZE(mxr_output_conf));
+ if (ret)
+ goto fail_resources;
+
+ /* configure layers */
+ ret = mxr_acquire_layers(mdev, pdata);
+ if (ret)
+ goto fail_video;
+
+ pm_runtime_enable(dev);
+
+ mxr_info(mdev, "probe successful\n");
+ return 0;
+
+fail_video:
+ mxr_release_video(mdev);
+
+fail_resources:
+ mxr_release_resources(mdev);
+
+fail_mem:
+ kfree(mdev);
+
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit mxr_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxr_device *mdev = to_mdev(dev);
+
+ pm_runtime_disable(dev);
+
+ mxr_release_layers(mdev);
+ mxr_release_video(mdev);
+ mxr_release_resources(mdev);
+
+ kfree(mdev);
+
+ dev_info(dev, "remove successful\n");
+ return 0;
+}
+
+static struct platform_driver mxr_driver __refdata = {
+ .probe = mxr_probe,
+ .remove = __devexit_p(mxr_remove),
+ .driver = {
+ .name = MXR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mxr_pm_ops,
+ }
+};
+
+static int __init mxr_init(void)
+{
+ int i, ret;
+ static const char banner[] __initconst =
+ "Samsung TV Mixer driver, "
+ "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
+ pr_info("%s\n", banner);
+
+ /* Loading auxiliary modules */
+ for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
+ request_module(mxr_output_conf[i].module_name);
+
+ ret = platform_driver_register(&mxr_driver);
+ if (ret != 0) {
+ pr_err("s5p-tv: registration of MIXER driver failed\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+module_init(mxr_init);
+
+static void __exit mxr_exit(void)
+{
+ platform_driver_unregister(&mxr_driver);
+}
+module_exit(mxr_exit);
diff --git a/drivers/media/video/s5p-tv/mixer_grp_layer.c b/drivers/media/platform/s5p-tv/mixer_grp_layer.c
index b93a21f5aa13..b93a21f5aa13 100644
--- a/drivers/media/video/s5p-tv/mixer_grp_layer.c
+++ b/drivers/media/platform/s5p-tv/mixer_grp_layer.c
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c
index 3b1670a045f4..3b1670a045f4 100644
--- a/drivers/media/video/s5p-tv/mixer_reg.c
+++ b/drivers/media/platform/s5p-tv/mixer_reg.c
diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c
new file mode 100644
index 000000000000..0c1cd895ff66
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/mixer_video.c
@@ -0,0 +1,1127 @@
+/*
+ * Samsung TV Mixer driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@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
+ */
+
+#define pr_fmt(fmt) "s5p-tv (mixer): " fmt
+
+#include "mixer.h"
+
+#include <media/v4l2-ioctl.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/timer.h>
+#include <media/videobuf2-dma-contig.h>
+
+static int find_reg_callback(struct device *dev, void *p)
+{
+ struct v4l2_subdev **sd = p;
+
+ *sd = dev_get_drvdata(dev);
+ /* non-zero value stops iteration */
+ return 1;
+}
+
+static struct v4l2_subdev *find_and_register_subdev(
+ struct mxr_device *mdev, char *module_name)
+{
+ struct device_driver *drv;
+ struct v4l2_subdev *sd = NULL;
+ int ret;
+
+ /* TODO: add waiting until probe is finished */
+ drv = driver_find(module_name, &platform_bus_type);
+ if (!drv) {
+ mxr_warn(mdev, "module %s is missing\n", module_name);
+ return NULL;
+ }
+ /* driver refcnt is increased, it is safe to iterate over devices */
+ ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback);
+ /* ret == 0 means that find_reg_callback was never executed */
+ if (sd == NULL) {
+ mxr_warn(mdev, "module %s provides no subdev!\n", module_name);
+ goto done;
+ }
+ /* v4l2_device_register_subdev detects if sd is NULL */
+ ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd);
+ if (ret) {
+ mxr_warn(mdev, "failed to register subdev %s\n", sd->name);
+ sd = NULL;
+ }
+
+done:
+ return sd;
+}
+
+int __devinit mxr_acquire_video(struct mxr_device *mdev,
+ struct mxr_output_conf *output_conf, int output_count)
+{
+ struct device *dev = mdev->dev;
+ struct v4l2_device *v4l2_dev = &mdev->v4l2_dev;
+ int i;
+ int ret = 0;
+ struct v4l2_subdev *sd;
+
+ strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name));
+ /* prepare context for V4L2 device */
+ ret = v4l2_device_register(dev, v4l2_dev);
+ if (ret) {
+ mxr_err(mdev, "could not register v4l2 device.\n");
+ goto fail;
+ }
+
+ mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
+ if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
+ mxr_err(mdev, "could not acquire vb2 allocator\n");
+ goto fail_v4l2_dev;
+ }
+
+ /* registering outputs */
+ mdev->output_cnt = 0;
+ for (i = 0; i < output_count; ++i) {
+ struct mxr_output_conf *conf = &output_conf[i];
+ struct mxr_output *out;
+
+ sd = find_and_register_subdev(mdev, conf->module_name);
+ /* trying to register next output */
+ if (sd == NULL)
+ continue;
+ out = kzalloc(sizeof *out, GFP_KERNEL);
+ if (out == NULL) {
+ mxr_err(mdev, "no memory for '%s'\n",
+ conf->output_name);
+ ret = -ENOMEM;
+ /* registered subdevs are removed in fail_v4l2_dev */
+ goto fail_output;
+ }
+ strlcpy(out->name, conf->output_name, sizeof(out->name));
+ out->sd = sd;
+ out->cookie = conf->cookie;
+ mdev->output[mdev->output_cnt++] = out;
+ mxr_info(mdev, "added output '%s' from module '%s'\n",
+ conf->output_name, conf->module_name);
+ /* checking if maximal number of outputs is reached */
+ if (mdev->output_cnt >= MXR_MAX_OUTPUTS)
+ break;
+ }
+
+ if (mdev->output_cnt == 0) {
+ mxr_err(mdev, "failed to register any output\n");
+ ret = -ENODEV;
+ /* skipping fail_output because there is nothing to free */
+ goto fail_vb2_allocator;
+ }
+
+ return 0;
+
+fail_output:
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+ memset(mdev->output, 0, sizeof mdev->output);
+
+fail_vb2_allocator:
+ /* freeing allocator context */
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+
+fail_v4l2_dev:
+ /* NOTE: automatically unregister all subdevs */
+ v4l2_device_unregister(v4l2_dev);
+
+fail:
+ return ret;
+}
+
+void mxr_release_video(struct mxr_device *mdev)
+{
+ int i;
+
+ /* kfree is NULL-safe */
+ for (i = 0; i < mdev->output_cnt; ++i)
+ kfree(mdev->output[i]);
+
+ vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
+ v4l2_device_unregister(&mdev->v4l2_dev);
+}
+
+static int mxr_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
+ strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
+ sprintf(cap->bus_info, "%d", layer->idx);
+ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
+{
+ mxr_dbg(mdev, "src.full_size = (%u, %u)\n",
+ geo->src.full_width, geo->src.full_height);
+ mxr_dbg(mdev, "src.size = (%u, %u)\n",
+ geo->src.width, geo->src.height);
+ mxr_dbg(mdev, "src.offset = (%u, %u)\n",
+ geo->src.x_offset, geo->src.y_offset);
+ mxr_dbg(mdev, "dst.full_size = (%u, %u)\n",
+ geo->dst.full_width, geo->dst.full_height);
+ mxr_dbg(mdev, "dst.size = (%u, %u)\n",
+ geo->dst.width, geo->dst.height);
+ mxr_dbg(mdev, "dst.offset = (%u, %u)\n",
+ geo->dst.x_offset, geo->dst.y_offset);
+ mxr_dbg(mdev, "ratio = (%u, %u)\n",
+ geo->x_ratio, geo->y_ratio);
+}
+
+static void mxr_layer_default_geo(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ memset(&layer->geo, 0, sizeof layer->geo);
+
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.width = layer->geo.dst.full_width;
+ layer->geo.dst.height = layer->geo.dst.full_height;
+ layer->geo.dst.field = mbus_fmt.field;
+
+ layer->geo.src.full_width = mbus_fmt.width;
+ layer->geo.src.full_height = mbus_fmt.height;
+ layer->geo.src.width = layer->geo.src.full_width;
+ layer->geo.src.height = layer->geo.src.full_height;
+
+ mxr_geometry_dump(mdev, &layer->geo);
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+}
+
+static void mxr_layer_update_output(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ mxr_get_mbus_fmt(mdev, &mbus_fmt);
+ /* checking if update is needed */
+ if (layer->geo.dst.full_width == mbus_fmt.width &&
+ layer->geo.dst.full_height == mbus_fmt.width)
+ return;
+
+ layer->geo.dst.full_width = mbus_fmt.width;
+ layer->geo.dst.full_height = mbus_fmt.height;
+ layer->geo.dst.field = mbus_fmt.field;
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
+
+ mxr_geometry_dump(mdev, &layer->geo);
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc);
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index);
+
+static int mxr_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ const struct mxr_format *fmt;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ fmt = find_format_by_index(layer, f->index);
+ if (fmt == NULL)
+ return -EINVAL;
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static unsigned int divup(unsigned int divident, unsigned int divisor)
+{
+ return (divident + divisor - 1) / divisor;
+}
+
+unsigned long mxr_get_plane_size(const struct mxr_block *blk,
+ unsigned int width, unsigned int height)
+{
+ unsigned int bl_width = divup(width, blk->width);
+ unsigned int bl_height = divup(height, blk->height);
+
+ return bl_width * bl_height * blk->size;
+}
+
+static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes,
+ const struct mxr_format *fmt, u32 width, u32 height)
+{
+ int i;
+
+ /* checking if nothing to fill */
+ if (!planes)
+ return;
+
+ memset(planes, 0, sizeof(*planes) * fmt->num_subframes);
+ for (i = 0; i < fmt->num_planes; ++i) {
+ struct v4l2_plane_pix_format *plane = planes
+ + fmt->plane2subframe[i];
+ const struct mxr_block *blk = &fmt->plane[i];
+ u32 bl_width = divup(width, blk->width);
+ u32 bl_height = divup(height, blk->height);
+ u32 sizeimage = bl_width * bl_height * blk->size;
+ u16 bytesperline = bl_width * blk->size / blk->height;
+
+ plane->sizeimage += sizeimage;
+ plane->bytesperline = max(plane->bytesperline, bytesperline);
+ }
+}
+
+static int mxr_g_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix->width = layer->geo.src.full_width;
+ pix->height = layer->geo.src.full_height;
+ pix->field = V4L2_FIELD_NONE;
+ pix->pixelformat = layer->fmt->fourcc;
+ pix->colorspace = layer->fmt->colorspace;
+ mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mxr_s_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ const struct mxr_format *fmt;
+ struct v4l2_pix_format_mplane *pix;
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_geometry *geo = &layer->geo;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+
+ pix = &f->fmt.pix_mp;
+ fmt = find_format_by_fourcc(layer, pix->pixelformat);
+ if (fmt == NULL) {
+ mxr_warn(mdev, "not recognized fourcc: %08x\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+ layer->fmt = fmt;
+ /* set source size to highest accepted value */
+ geo->src.full_width = max(geo->dst.full_width, pix->width);
+ geo->src.full_height = max(geo->dst.full_height, pix->height);
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+ /* set cropping to total visible screen */
+ geo->src.width = pix->width;
+ geo->src.height = pix->height;
+ geo->src.x_offset = 0;
+ geo->src.y_offset = 0;
+ /* assure consistency of geometry */
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET);
+ mxr_geometry_dump(mdev, &layer->geo);
+ /* set full size to lowest possible value */
+ geo->src.full_width = 0;
+ geo->src.full_height = 0;
+ layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
+ mxr_geometry_dump(mdev, &layer->geo);
+
+ /* returning results */
+ mxr_g_fmt(file, priv, f);
+
+ return 0;
+}
+
+static int mxr_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_geometry *geo = &layer->geo;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = geo->src.x_offset;
+ s->r.top = geo->src.y_offset;
+ s->r.width = geo->src.width;
+ s->r.height = geo->src.height;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = geo->src.full_width;
+ s->r.height = geo->src.full_height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r.left = geo->dst.x_offset;
+ s->r.top = geo->dst.y_offset;
+ s->r.width = geo->dst.width;
+ s->r.height = geo->dst.height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = geo->dst.full_width;
+ s->r.height = geo->dst.full_height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* returns 1 if rectangle 'a' is inside 'b' */
+static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left)
+ return 0;
+ if (a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+ return 1;
+}
+
+static int mxr_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_geometry *geo = &layer->geo;
+ struct mxr_crop *target = NULL;
+ enum mxr_geometry_stage stage;
+ struct mxr_geometry tmp;
+ struct v4l2_rect res;
+
+ memset(&res, 0, sizeof res);
+
+ mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__,
+ s->r.width, s->r.height, s->r.left, s->r.top);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ /* ignore read-only targets */
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ res.width = geo->src.full_width;
+ res.height = geo->src.full_height;
+ break;
+
+ /* ignore read-only targets */
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ res.width = geo->dst.full_width;
+ res.height = geo->dst.full_height;
+ break;
+
+ case V4L2_SEL_TGT_CROP:
+ target = &geo->src;
+ stage = MXR_GEOMETRY_CROP;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ target = &geo->dst;
+ stage = MXR_GEOMETRY_COMPOSE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* apply change and update geometry if needed */
+ if (target) {
+ /* backup current geometry if setup fails */
+ memcpy(&tmp, geo, sizeof tmp);
+
+ /* apply requested selection */
+ target->x_offset = s->r.left;
+ target->y_offset = s->r.top;
+ target->width = s->r.width;
+ target->height = s->r.height;
+
+ layer->ops.fix_geometry(layer, stage, s->flags);
+
+ /* retrieve update selection rectangle */
+ res.left = target->x_offset;
+ res.top = target->y_offset;
+ res.width = target->width;
+ res.height = target->height;
+
+ mxr_geometry_dump(layer->mdev, &layer->geo);
+ }
+
+ /* checking if the rectangle satisfies constraints */
+ if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r))
+ goto fail;
+ if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res))
+ goto fail;
+
+ /* return result rectangle */
+ s->r = res;
+
+ return 0;
+fail:
+ /* restore old geometry, which is not touched if target is NULL */
+ if (target)
+ memcpy(geo, &tmp, sizeof tmp);
+ return -ERANGE;
+}
+
+static int mxr_enum_dv_presets(struct file *file, void *fh,
+ struct v4l2_dv_enum_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* preset change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
+
+ mutex_unlock(&mdev->mutex);
+
+ mxr_layer_update_output(layer);
+
+ /* any failure should return EINVAL according to V4L2 doc */
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_dv_preset(struct file *file, void *fh,
+ struct v4l2_dv_preset *preset)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+
+ /* standard change cannot be done while there is an entity
+ * dependant on output configuration
+ */
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+
+ ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
+
+ mutex_unlock(&mdev->mutex);
+
+ mxr_layer_update_output(layer);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ /* lock protects from changing sd_out */
+ mutex_lock(&mdev->mutex);
+ ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm);
+ mutex_unlock(&mdev->mutex);
+
+ return ret ? -EINVAL : 0;
+}
+
+static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ struct mxr_output *out;
+ struct v4l2_subdev *sd;
+
+ if (a->index >= mdev->output_cnt)
+ return -EINVAL;
+ out = mdev->output[a->index];
+ BUG_ON(out == NULL);
+ sd = out->sd;
+ strlcpy(a->name, out->name, sizeof(a->name));
+
+ /* try to obtain supported tv norms */
+ v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
+ a->capabilities = 0;
+ if (sd->ops->video && sd->ops->video->s_dv_preset)
+ a->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (sd->ops->video && sd->ops->video->s_std_output)
+ a->capabilities |= V4L2_OUT_CAP_STD;
+ a->type = V4L2_OUTPUT_TYPE_ANALOG;
+
+ return 0;
+}
+
+static int mxr_s_output(struct file *file, void *fh, unsigned int i)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+
+ if (i >= mdev->output_cnt || mdev->output[i] == NULL)
+ return -EINVAL;
+
+ mutex_lock(&mdev->mutex);
+ if (mdev->n_output > 0) {
+ mutex_unlock(&mdev->mutex);
+ return -EBUSY;
+ }
+ mdev->current_output = i;
+ vfd->tvnorms = 0;
+ v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output,
+ &vfd->tvnorms);
+ mutex_unlock(&mdev->mutex);
+
+ /* update layers geometry */
+ mxr_layer_update_output(layer);
+
+ mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms);
+
+ return 0;
+}
+
+static int mxr_g_output(struct file *file, void *fh, unsigned int *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+
+ mutex_lock(&mdev->mutex);
+ *p = mdev->current_output;
+ mutex_unlock(&mdev->mutex);
+
+ return 0;
+}
+
+static int mxr_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_reqbufs(&layer->vb_queue, p);
+}
+
+static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_querybuf(&layer->vb_queue, p);
+}
+
+static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index);
+ return vb2_qbuf(&layer->vb_queue, p);
+}
+
+static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
+}
+
+static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamon(&layer->vb_queue, i);
+}
+
+static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ return vb2_streamoff(&layer->vb_queue, i);
+}
+
+static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
+ .vidioc_querycap = mxr_querycap,
+ /* format handling */
+ .vidioc_enum_fmt_vid_out_mplane = mxr_enum_fmt,
+ .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt,
+ .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt,
+ /* buffer control */
+ .vidioc_reqbufs = mxr_reqbufs,
+ .vidioc_querybuf = mxr_querybuf,
+ .vidioc_qbuf = mxr_qbuf,
+ .vidioc_dqbuf = mxr_dqbuf,
+ /* Streaming control */
+ .vidioc_streamon = mxr_streamon,
+ .vidioc_streamoff = mxr_streamoff,
+ /* Preset functions */
+ .vidioc_enum_dv_presets = mxr_enum_dv_presets,
+ .vidioc_s_dv_preset = mxr_s_dv_preset,
+ .vidioc_g_dv_preset = mxr_g_dv_preset,
+ /* analog TV standard functions */
+ .vidioc_s_std = mxr_s_std,
+ .vidioc_g_std = mxr_g_std,
+ /* Output handling */
+ .vidioc_enum_output = mxr_enum_output,
+ .vidioc_s_output = mxr_s_output,
+ .vidioc_g_output = mxr_g_output,
+ /* selection ioctls */
+ .vidioc_g_selection = mxr_g_selection,
+ .vidioc_s_selection = mxr_s_selection,
+};
+
+static int mxr_video_open(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ struct mxr_device *mdev = layer->mdev;
+ int ret = 0;
+
+ mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
+ if (mutex_lock_interruptible(&layer->mutex))
+ return -ERESTARTSYS;
+ /* assure device probe is finished */
+ wait_for_device_probe();
+ /* creating context for file descriptor */
+ ret = v4l2_fh_open(file);
+ if (ret) {
+ mxr_err(mdev, "v4l2_fh_open failed\n");
+ goto unlock;
+ }
+
+ /* leaving if layer is already initialized */
+ if (!v4l2_fh_is_singular_file(file))
+ goto unlock;
+
+ /* FIXME: should power be enabled on open? */
+ ret = mxr_power_get(mdev);
+ if (ret) {
+ mxr_err(mdev, "power on failed\n");
+ goto fail_fh_open;
+ }
+
+ ret = vb2_queue_init(&layer->vb_queue);
+ if (ret != 0) {
+ mxr_err(mdev, "failed to initialize vb2 queue\n");
+ goto fail_power;
+ }
+ /* set default format, first on the list */
+ layer->fmt = layer->fmt_array[0];
+ /* setup default geometry */
+ mxr_layer_default_geo(layer);
+ mutex_unlock(&layer->mutex);
+
+ return 0;
+
+fail_power:
+ mxr_power_put(mdev);
+
+fail_fh_open:
+ v4l2_fh_release(file);
+
+unlock:
+ mutex_unlock(&layer->mutex);
+
+ return ret;
+}
+
+static unsigned int
+mxr_video_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ unsigned int res;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ mutex_lock(&layer->mutex);
+ res = vb2_poll(&layer->vb_queue, file, wait);
+ mutex_unlock(&layer->mutex);
+ return res;
+}
+
+static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+ int ret;
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+
+ if (mutex_lock_interruptible(&layer->mutex))
+ return -ERESTARTSYS;
+ ret = vb2_mmap(&layer->vb_queue, vma);
+ mutex_unlock(&layer->mutex);
+ return ret;
+}
+
+static int mxr_video_release(struct file *file)
+{
+ struct mxr_layer *layer = video_drvdata(file);
+
+ mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
+ mutex_lock(&layer->mutex);
+ if (v4l2_fh_is_singular_file(file)) {
+ vb2_queue_release(&layer->vb_queue);
+ mxr_power_put(layer->mdev);
+ }
+ v4l2_fh_release(file);
+ mutex_unlock(&layer->mutex);
+ return 0;
+}
+
+static const struct v4l2_file_operations mxr_fops = {
+ .owner = THIS_MODULE,
+ .open = mxr_video_open,
+ .poll = mxr_video_poll,
+ .mmap = mxr_video_mmap,
+ .release = mxr_video_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
+ void *alloc_ctxs[])
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ const struct mxr_format *fmt = layer->fmt;
+ int i;
+ struct mxr_device *mdev = layer->mdev;
+ struct v4l2_plane_pix_format planes[3];
+
+ mxr_dbg(mdev, "%s\n", __func__);
+ /* checking if format was configured */
+ if (fmt == NULL)
+ return -EINVAL;
+ mxr_dbg(mdev, "fmt = %s\n", fmt->name);
+ mxr_mplane_fill(planes, fmt, layer->geo.src.full_width,
+ layer->geo.src.full_height);
+
+ *nplanes = fmt->num_subframes;
+ for (i = 0; i < fmt->num_subframes; ++i) {
+ alloc_ctxs[i] = layer->mdev->alloc_ctx;
+ sizes[i] = planes[i].sizeimage;
+ mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]);
+ }
+
+ if (*nbuffers == 0)
+ *nbuffers = 1;
+
+ return 0;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
+ struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ list_add_tail(&buffer->list, &layer->enq_list);
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ mxr_dbg(mdev, "queuing buffer\n");
+}
+
+static void wait_lock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_lock(&layer->mutex);
+}
+
+static void wait_unlock(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+
+ mxr_dbg(layer->mdev, "%s\n", __func__);
+ mutex_unlock(&layer->mutex);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ if (count == 0) {
+ mxr_dbg(mdev, "no output buffers queued\n");
+ return -EINVAL;
+ }
+
+ /* block any changes in output configuration */
+ mxr_output_get(mdev);
+
+ mxr_layer_update_output(layer);
+ layer->ops.format_set(layer);
+ /* enabling layer in hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_STREAMING;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ layer->ops.stream_set(layer, MXR_ENABLE);
+ mxr_streamer_get(mdev);
+
+ return 0;
+}
+
+static void mxr_watchdog(unsigned long arg)
+{
+ struct mxr_layer *layer = (struct mxr_layer *) arg;
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+
+ mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ if (layer->update_buf == layer->shadow_buf)
+ layer->update_buf = NULL;
+ if (layer->update_buf) {
+ vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->update_buf = NULL;
+ }
+ if (layer->shadow_buf) {
+ vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
+ layer->shadow_buf = NULL;
+ }
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct mxr_layer *layer = vb2_get_drv_priv(vq);
+ struct mxr_device *mdev = layer->mdev;
+ unsigned long flags;
+ struct timer_list watchdog;
+ struct mxr_buffer *buf, *buf_tmp;
+
+ mxr_dbg(mdev, "%s\n", __func__);
+
+ spin_lock_irqsave(&layer->enq_slock, flags);
+
+ /* reset list */
+ layer->state = MXR_LAYER_STREAMING_FINISH;
+
+ /* set all buffer to be done */
+ list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* give 1 seconds to complete to complete last buffers */
+ setup_timer_on_stack(&watchdog, mxr_watchdog,
+ (unsigned long)layer);
+ mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
+
+ /* wait until all buffers are goes to done state */
+ vb2_wait_for_all_buffers(vq);
+
+ /* stop timer if all synchronization is done */
+ del_timer_sync(&watchdog);
+ destroy_timer_on_stack(&watchdog);
+
+ /* stopping hardware */
+ spin_lock_irqsave(&layer->enq_slock, flags);
+ layer->state = MXR_LAYER_IDLE;
+ spin_unlock_irqrestore(&layer->enq_slock, flags);
+
+ /* disabling layer in hardware */
+ layer->ops.stream_set(layer, MXR_DISABLE);
+ /* remove one streamer */
+ mxr_streamer_put(mdev);
+ /* allow changes in output configuration */
+ mxr_output_put(mdev);
+ return 0;
+}
+
+static struct vb2_ops mxr_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buf_queue,
+ .wait_prepare = wait_unlock,
+ .wait_finish = wait_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+/* FIXME: try to put this functions to mxr_base_layer_create */
+int mxr_base_layer_register(struct mxr_layer *layer)
+{
+ struct mxr_device *mdev = layer->mdev;
+ int ret;
+
+ ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ mxr_err(mdev, "failed to register video device\n");
+ else
+ mxr_info(mdev, "registered layer %s as /dev/video%d\n",
+ layer->vfd.name, layer->vfd.num);
+ return ret;
+}
+
+void mxr_base_layer_unregister(struct mxr_layer *layer)
+{
+ video_unregister_device(&layer->vfd);
+}
+
+void mxr_layer_release(struct mxr_layer *layer)
+{
+ if (layer->ops.release)
+ layer->ops.release(layer);
+}
+
+void mxr_base_layer_release(struct mxr_layer *layer)
+{
+ kfree(layer);
+}
+
+static void mxr_vfd_release(struct video_device *vdev)
+{
+ pr_info("video device release\n");
+}
+
+struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
+ int idx, char *name, struct mxr_layer_ops *ops)
+{
+ struct mxr_layer *layer;
+
+ layer = kzalloc(sizeof *layer, GFP_KERNEL);
+ if (layer == NULL) {
+ mxr_err(mdev, "not enough memory for layer.\n");
+ goto fail;
+ }
+
+ layer->mdev = mdev;
+ layer->idx = idx;
+ layer->ops = *ops;
+
+ spin_lock_init(&layer->enq_slock);
+ INIT_LIST_HEAD(&layer->enq_list);
+ mutex_init(&layer->mutex);
+
+ layer->vfd = (struct video_device) {
+ .minor = -1,
+ .release = mxr_vfd_release,
+ .fops = &mxr_fops,
+ .vfl_dir = VFL_DIR_TX,
+ .ioctl_ops = &mxr_ioctl_ops,
+ };
+ strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name));
+ /* let framework control PRIORITY */
+ set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
+
+ video_set_drvdata(&layer->vfd, layer);
+ layer->vfd.lock = &layer->mutex;
+ layer->vfd.v4l2_dev = &mdev->v4l2_dev;
+
+ layer->vb_queue = (struct vb2_queue) {
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .io_modes = VB2_MMAP | VB2_USERPTR,
+ .drv_priv = layer,
+ .buf_struct_size = sizeof(struct mxr_buffer),
+ .ops = &mxr_video_qops,
+ .mem_ops = &vb2_dma_contig_memops,
+ };
+
+ return layer;
+
+fail:
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_fourcc(
+ struct mxr_layer *layer, unsigned long fourcc)
+{
+ int i;
+
+ for (i = 0; i < layer->fmt_array_size; ++i)
+ if (layer->fmt_array[i]->fourcc == fourcc)
+ return layer->fmt_array[i];
+ return NULL;
+}
+
+static const struct mxr_format *find_format_by_index(
+ struct mxr_layer *layer, unsigned long index)
+{
+ if (index >= layer->fmt_array_size)
+ return NULL;
+ return layer->fmt_array[index];
+}
+
diff --git a/drivers/media/video/s5p-tv/mixer_vp_layer.c b/drivers/media/platform/s5p-tv/mixer_vp_layer.c
index 3d13a636877b..3d13a636877b 100644
--- a/drivers/media/video/s5p-tv/mixer_vp_layer.c
+++ b/drivers/media/platform/s5p-tv/mixer_vp_layer.c
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/platform/s5p-tv/regs-hdmi.h
index a889d1f57f28..a889d1f57f28 100644
--- a/drivers/media/video/s5p-tv/regs-hdmi.h
+++ b/drivers/media/platform/s5p-tv/regs-hdmi.h
diff --git a/drivers/media/video/s5p-tv/regs-mixer.h b/drivers/media/platform/s5p-tv/regs-mixer.h
index 158abb43d0a4..158abb43d0a4 100644
--- a/drivers/media/video/s5p-tv/regs-mixer.h
+++ b/drivers/media/platform/s5p-tv/regs-mixer.h
diff --git a/drivers/media/platform/s5p-tv/regs-sdo.h b/drivers/media/platform/s5p-tv/regs-sdo.h
new file mode 100644
index 000000000000..6f22fbfe2f6c
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/regs-sdo.h
@@ -0,0 +1,63 @@
+/* drivers/media/platform/s5p-tv/regs-sdo.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * SDO register description file
+ *
+ * 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.
+ */
+
+#ifndef SAMSUNG_REGS_SDO_H
+#define SAMSUNG_REGS_SDO_H
+
+/*
+ * Register part
+ */
+
+#define SDO_CLKCON 0x0000
+#define SDO_CONFIG 0x0008
+#define SDO_VBI 0x0014
+#define SDO_DAC 0x003C
+#define SDO_CCCON 0x0180
+#define SDO_IRQ 0x0280
+#define SDO_IRQMASK 0x0284
+#define SDO_VERSION 0x03D8
+
+/*
+ * Bit definition part
+ */
+
+/* SDO Clock Control Register (SDO_CLKCON) */
+#define SDO_TVOUT_SW_RESET (1 << 4)
+#define SDO_TVOUT_CLOCK_READY (1 << 1)
+#define SDO_TVOUT_CLOCK_ON (1 << 0)
+
+/* SDO Video Standard Configuration Register (SDO_CONFIG) */
+#define SDO_PROGRESSIVE (1 << 4)
+#define SDO_NTSC_M 0
+#define SDO_PAL_M 1
+#define SDO_PAL_BGHID 2
+#define SDO_PAL_N 3
+#define SDO_PAL_NC 4
+#define SDO_NTSC_443 8
+#define SDO_PAL_60 9
+#define SDO_STANDARD_MASK 0xf
+
+/* SDO VBI Configuration Register (SDO_VBI) */
+#define SDO_CVBS_WSS_INS (1 << 14)
+#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12)
+
+/* SDO DAC Configuration Register (SDO_DAC) */
+#define SDO_POWER_ON_DAC (1 << 0)
+
+/* SDO Color Compensation On/Off Control (SDO_CCCON) */
+#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4)
+#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0)
+
+/* SDO Interrupt Request Register (SDO_IRQ) */
+#define SDO_VSYNC_IRQ_PEND (1 << 0)
+
+#endif /* SAMSUNG_REGS_SDO_H */
diff --git a/drivers/media/video/s5p-tv/regs-vp.h b/drivers/media/platform/s5p-tv/regs-vp.h
index 6c63984e11e8..6c63984e11e8 100644
--- a/drivers/media/video/s5p-tv/regs-vp.h
+++ b/drivers/media/platform/s5p-tv/regs-vp.h
diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c
new file mode 100644
index 000000000000..ad68bbed014e
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/sdo_drv.c
@@ -0,0 +1,448 @@
+/*
+ * Samsung Standard Definition Output (SDO) driver
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *
+ * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
+ * or (at your option) any later version
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "regs-sdo.h"
+
+MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)");
+MODULE_LICENSE("GPL");
+
+#define SDO_DEFAULT_STD V4L2_STD_PAL
+
+struct sdo_format {
+ v4l2_std_id id;
+ /* all modes are 720 pixels wide */
+ unsigned int height;
+ unsigned int cookie;
+};
+
+struct sdo_device {
+ /** pointer to device parent */
+ struct device *dev;
+ /** base address of SDO registers */
+ void __iomem *regs;
+ /** SDO interrupt */
+ unsigned int irq;
+ /** DAC source clock */
+ struct clk *sclk_dac;
+ /** DAC clock */
+ struct clk *dac;
+ /** DAC physical interface */
+ struct clk *dacphy;
+ /** clock for control of VPLL */
+ struct clk *fout_vpll;
+ /** regulator for SDO IP power */
+ struct regulator *vdac;
+ /** regulator for SDO plug detection */
+ struct regulator *vdet;
+ /** subdev used as device interface */
+ struct v4l2_subdev sd;
+ /** current format */
+ const struct sdo_format *fmt;
+};
+
+static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sdo_device, sd);
+}
+
+static inline
+void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask)
+{
+ u32 old = readl(sdev->regs + reg_id);
+ value = (value & mask) | (old & ~mask);
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value)
+{
+ writel(value, sdev->regs + reg_id);
+}
+
+static inline
+u32 sdo_read(struct sdo_device *sdev, u32 reg_id)
+{
+ return readl(sdev->regs + reg_id);
+}
+
+static irqreturn_t sdo_irq_handler(int irq, void *dev_data)
+{
+ struct sdo_device *sdev = dev_data;
+
+ /* clear interrupt */
+ sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND);
+ return IRQ_HANDLED;
+}
+
+static void sdo_reg_debug(struct sdo_device *sdev)
+{
+#define DBGREG(reg_id) \
+ dev_info(sdev->dev, #reg_id " = %08x\n", \
+ sdo_read(sdev, reg_id))
+
+ DBGREG(SDO_CLKCON);
+ DBGREG(SDO_CONFIG);
+ DBGREG(SDO_VBI);
+ DBGREG(SDO_DAC);
+ DBGREG(SDO_IRQ);
+ DBGREG(SDO_IRQMASK);
+ DBGREG(SDO_VERSION);
+}
+
+static const struct sdo_format sdo_format[] = {
+ { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N },
+ { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC },
+ { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M },
+ { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 },
+ { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 },
+ { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID },
+ { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M },
+};
+
+static const struct sdo_format *sdo_find_format(v4l2_std_id id)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(sdo_format); ++i)
+ if (sdo_format[i].id & id)
+ return &sdo_format[i];
+ return NULL;
+}
+
+static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL |
+ V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
+ V4L2_STD_NTSC_443 | V4L2_STD_PAL_60;
+ return 0;
+}
+
+static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ const struct sdo_format *fmt;
+ fmt = sdo_find_format(std);
+ if (fmt == NULL)
+ return -EINVAL;
+ sdev->fmt = fmt;
+ return 0;
+}
+
+static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ *std = sd_to_sdev(sd)->fmt->id;
+ return 0;
+}
+
+static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ if (!sdev->fmt)
+ return -ENXIO;
+ /* all modes are 720 pixels wide */
+ fmt->width = 720;
+ fmt->height = sdev->fmt->height;
+ fmt->code = V4L2_MBUS_FMT_FIXED;
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static int sdo_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ struct device *dev = sdev->dev;
+ int ret;
+
+ dev_info(dev, "sdo_s_power(%d)\n", on);
+
+ if (on)
+ ret = pm_runtime_get_sync(dev);
+ else
+ ret = pm_runtime_put_sync(dev);
+
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sdo_streamon(struct sdo_device *sdev)
+{
+ /* set proper clock for Timing Generator */
+ clk_set_rate(sdev->fout_vpll, 54000000);
+ dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
+ clk_get_rate(sdev->fout_vpll));
+ /* enable clock in SDO */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
+ clk_enable(sdev->dacphy);
+ /* enable DAC */
+ sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static int sdo_streamoff(struct sdo_device *sdev)
+{
+ int tries;
+
+ sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
+ clk_disable(sdev->dacphy);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
+ for (tries = 100; tries; --tries) {
+ if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
+ break;
+ mdelay(1);
+ }
+ if (tries == 0)
+ dev_err(sdev->dev, "failed to stop streaming\n");
+ return tries ? 0 : -EIO;
+}
+
+static int sdo_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct sdo_device *sdev = sd_to_sdev(sd);
+ return on ? sdo_streamon(sdev) : sdo_streamoff(sdev);
+}
+
+static const struct v4l2_subdev_core_ops sdo_sd_core_ops = {
+ .s_power = sdo_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sdo_sd_video_ops = {
+ .s_std_output = sdo_s_std_output,
+ .g_std_output = sdo_g_std_output,
+ .g_tvnorms_output = sdo_g_tvnorms_output,
+ .g_mbus_fmt = sdo_g_mbus_fmt,
+ .s_stream = sdo_s_stream,
+};
+
+static const struct v4l2_subdev_ops sdo_sd_ops = {
+ .core = &sdo_sd_core_ops,
+ .video = &sdo_sd_video_ops,
+};
+
+static int sdo_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "suspend\n");
+ regulator_disable(sdev->vdet);
+ regulator_disable(sdev->vdac);
+ clk_disable(sdev->sclk_dac);
+ return 0;
+}
+
+static int sdo_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ dev_info(dev, "resume\n");
+ clk_enable(sdev->sclk_dac);
+ regulator_enable(sdev->vdac);
+ regulator_enable(sdev->vdet);
+
+ /* software reset */
+ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
+ mdelay(10);
+ sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET);
+
+ /* setting TV mode */
+ sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK);
+ /* XXX: forcing interlaced mode using undocumented bit */
+ sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE);
+ /* turn all VBI off */
+ sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS |
+ SDO_CVBS_CLOSED_CAPTION_MASK);
+ /* turn all post processing off */
+ sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF |
+ SDO_COMPENSATION_CVBS_COMP_OFF);
+ sdo_reg_debug(sdev);
+ return 0;
+}
+
+static const struct dev_pm_ops sdo_pm_ops = {
+ .runtime_suspend = sdo_runtime_suspend,
+ .runtime_resume = sdo_runtime_resume,
+};
+
+static int __devinit sdo_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sdo_device *sdev;
+ struct resource *res;
+ int ret = 0;
+ struct clk *sclk_vpll;
+
+ dev_info(dev, "probe start\n");
+ sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
+ if (!sdev) {
+ dev_err(dev, "not enough memory.\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ sdev->dev = dev;
+
+ /* mapping registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(dev, "get memory resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (sdev->regs == NULL) {
+ dev_err(dev, "register mapping failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ /* acquiring interrupt */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(dev, "get interrupt resource failed.\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+ ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
+ "s5p-sdo", sdev);
+ if (ret) {
+ dev_err(dev, "request interrupt failed.\n");
+ goto fail;
+ }
+ sdev->irq = res->start;
+
+ /* acquire clocks */
+ sdev->sclk_dac = clk_get(dev, "sclk_dac");
+ if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
+ dev_err(dev, "failed to get clock 'sclk_dac'\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+ sdev->dac = clk_get(dev, "dac");
+ if (IS_ERR_OR_NULL(sdev->dac)) {
+ dev_err(dev, "failed to get clock 'dac'\n");
+ ret = -ENXIO;
+ goto fail_sclk_dac;
+ }
+ sdev->dacphy = clk_get(dev, "dacphy");
+ if (IS_ERR_OR_NULL(sdev->dacphy)) {
+ dev_err(dev, "failed to get clock 'dacphy'\n");
+ ret = -ENXIO;
+ goto fail_dac;
+ }
+ sclk_vpll = clk_get(dev, "sclk_vpll");
+ if (IS_ERR_OR_NULL(sclk_vpll)) {
+ dev_err(dev, "failed to get clock 'sclk_vpll'\n");
+ ret = -ENXIO;
+ goto fail_dacphy;
+ }
+ clk_set_parent(sdev->sclk_dac, sclk_vpll);
+ clk_put(sclk_vpll);
+ sdev->fout_vpll = clk_get(dev, "fout_vpll");
+ if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
+ dev_err(dev, "failed to get clock 'fout_vpll'\n");
+ goto fail_dacphy;
+ }
+ dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
+
+ /* acquire regulator */
+ sdev->vdac = devm_regulator_get(dev, "vdd33a_dac");
+ if (IS_ERR_OR_NULL(sdev->vdac)) {
+ dev_err(dev, "failed to get regulator 'vdac'\n");
+ goto fail_fout_vpll;
+ }
+ sdev->vdet = devm_regulator_get(dev, "vdet");
+ if (IS_ERR_OR_NULL(sdev->vdet)) {
+ dev_err(dev, "failed to get regulator 'vdet'\n");
+ goto fail_fout_vpll;
+ }
+
+ /* enable gate for dac clock, because mixer uses it */
+ clk_enable(sdev->dac);
+
+ /* configure power management */
+ pm_runtime_enable(dev);
+
+ /* configuration of interface subdevice */
+ v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
+ sdev->sd.owner = THIS_MODULE;
+ strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
+
+ /* set default format */
+ sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
+ BUG_ON(sdev->fmt == NULL);
+
+ /* keeping subdev in device's private for use by other drivers */
+ dev_set_drvdata(dev, &sdev->sd);
+
+ dev_info(dev, "probe succeeded\n");
+ return 0;
+
+fail_fout_vpll:
+ clk_put(sdev->fout_vpll);
+fail_dacphy:
+ clk_put(sdev->dacphy);
+fail_dac:
+ clk_put(sdev->dac);
+fail_sclk_dac:
+ clk_put(sdev->sclk_dac);
+fail:
+ dev_info(dev, "probe failed\n");
+ return ret;
+}
+
+static int __devexit sdo_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
+ struct sdo_device *sdev = sd_to_sdev(sd);
+
+ pm_runtime_disable(&pdev->dev);
+ clk_disable(sdev->dac);
+ clk_put(sdev->fout_vpll);
+ clk_put(sdev->dacphy);
+ clk_put(sdev->dac);
+ clk_put(sdev->sclk_dac);
+
+ dev_info(&pdev->dev, "remove successful\n");
+ return 0;
+}
+
+static struct platform_driver sdo_driver __refdata = {
+ .probe = sdo_probe,
+ .remove = __devexit_p(sdo_remove),
+ .driver = {
+ .name = "s5p-sdo",
+ .owner = THIS_MODULE,
+ .pm = &sdo_pm_ops,
+ }
+};
+
+module_platform_driver(sdo_driver);
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
new file mode 100644
index 000000000000..716d4846f8bd
--- /dev/null
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -0,0 +1,413 @@
+/*
+ * Samsung MHL interface driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <media/sii9234.h>
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MHL interface driver");
+MODULE_LICENSE("GPL");
+
+struct sii9234_context {
+ struct i2c_client *client;
+ struct regulator *power;
+ int gpio_n_reset;
+ struct v4l2_subdev sd;
+};
+
+static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sii9234_context, sd);
+}
+
+static inline int sii9234_readb(struct i2c_client *client, int addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return i2c_smbus_write_byte_data(client, addr, ret);
+}
+
+static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_read_byte_data(client, 0xbe);
+}
+
+static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
+ int value)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbe, value);
+ return ret;
+}
+
+static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = sii9234_readb_idx(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return sii9234_writeb_idx(client, addr, ret);
+}
+
+static int sii9234_reset(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ struct device *dev = &client->dev;
+ int ret, tries;
+
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 0);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+
+ /* going to TTPI mode */
+ ret = sii9234_writeb(client, 0xc7, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to set TTPI mode\n");
+ return ret;
+ }
+ for (tries = 0; tries < 100 ; ++tries) {
+ ret = sii9234_readb(client, 0x1b);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ dev_err(dev, "failed to reset device\n");
+ return -EIO;
+ }
+ mdelay(1);
+ }
+ if (tries == 100) {
+ dev_err(dev, "maximal number of tries reached\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sii9234_verify_version(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
+
+ family = sii9234_readb(client, 0x1b);
+ rev = sii9234_readb(client, 0x1c) & 0x0f;
+ tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
+ dev_id = sii9234_readb_idx(client, 0x0103);
+ sub_id = sii9234_readb_idx(client, 0x0102);
+ hdcp = sii9234_readb(client, 0x30);
+
+ if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
+ sub_id < 0 || hdcp < 0) {
+ dev_err(dev, "failed to read chip's version\n");
+ return -EIO;
+ }
+
+ id = (dev_id << 8) | sub_id;
+
+ dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
+ id, family, rev);
+ dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
+ if (id != 0x9234) {
+ dev_err(dev, "not supported chip\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static u8 data[][3] = {
+/* setup from driver created by doonsoo45.kim */
+ { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
+ { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
+ { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
+ { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
+ { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
+ { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
+ { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
+ { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
+ { 0x01, 0x92, 0x46 }, /* Force MHD mode */
+ { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
+ { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
+ { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
+ { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
+ { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
+ { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
+ { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
+ { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
+ { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
+ { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
+ { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
+ { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
+ { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
+ { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
+ { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
+ { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
+ { 0x03, 0x1a, 0x20 }, /* VCO Cal */
+ { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
+ { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
+ { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
+ { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
+ { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
+ { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
+ { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
+ { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
+ { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
+ { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
+};
+
+static int sii9234_set_internal(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(data); ++i) {
+ int addr = (data[i][0] << 8) | data[i][1];
+ ret = sii9234_writeb_idx(client, addr, data[i][2]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int sii9234_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+
+ dev_info(dev, "suspend start\n");
+
+ sii9234_writeb_mask(client, 0x1e, 3, 3);
+ regulator_disable(ctx->power);
+
+ return 0;
+}
+
+static int sii9234_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+ int ret;
+
+ dev_info(dev, "resume start\n");
+ regulator_enable(ctx->power);
+
+ ret = sii9234_reset(ctx);
+ if (ret)
+ goto fail;
+
+ /* enable tpi */
+ ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
+ if (ret < 0)
+ goto fail;
+ ret = sii9234_set_internal(ctx);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ dev_err(dev, "failed to resume\n");
+ regulator_disable(ctx->power);
+
+ return ret;
+}
+
+static const struct dev_pm_ops sii9234_pm_ops = {
+ .runtime_suspend = sii9234_runtime_suspend,
+ .runtime_resume = sii9234_runtime_resume,
+};
+
+static int sii9234_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(&ctx->client->dev);
+ else
+ ret = pm_runtime_put(&ctx->client->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ /* (dis/en)able TDMS output */
+ sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops sii9234_core_ops = {
+ .s_power = sii9234_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sii9234_video_ops = {
+ .s_stream = sii9234_s_stream,
+};
+
+static const struct v4l2_subdev_ops sii9234_ops = {
+ .core = &sii9234_core_ops,
+ .video = &sii9234_video_ops,
+};
+
+static int __devinit sii9234_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct sii9234_platform_data *pdata = dev->platform_data;
+ struct sii9234_context *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ctx->client = client;
+
+ ctx->power = devm_regulator_get(dev, "hdmi-en");
+ if (IS_ERR(ctx->power)) {
+ dev_err(dev, "failed to acquire regulator hdmi-en\n");
+ return PTR_ERR(ctx->power);
+ }
+
+ ctx->gpio_n_reset = pdata->gpio_n_reset;
+ ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
+ if (ret) {
+ dev_err(dev, "failed to acquire MHL_RST gpio\n");
+ return ret;
+ }
+
+ v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
+
+ pm_runtime_enable(dev);
+
+ /* enable device */
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ goto fail_pm;
+
+ /* verify chip version */
+ ret = sii9234_verify_version(client);
+ if (ret)
+ goto fail_pm_get;
+
+ /* stop processing */
+ pm_runtime_put(dev);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_pm_get:
+ pm_runtime_put_sync(dev);
+
+fail_pm:
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+
+fail:
+ dev_err(dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit sii9234_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+
+static const struct i2c_device_id sii9234_id[] = {
+ { "SII9234", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
+static struct i2c_driver sii9234_driver = {
+ .driver = {
+ .name = "sii9234",
+ .owner = THIS_MODULE,
+ .pm = &sii9234_pm_ops,
+ },
+ .probe = sii9234_probe,
+ .remove = __devexit_p(sii9234_remove),
+ .id_table = sii9234_id,
+};
+
+module_i2c_driver(sii9234_driver);
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
new file mode 100644
index 000000000000..85fd312f0a82
--- /dev/null
+++ b/drivers/media/platform/sh_vou.c
@@ -0,0 +1,1513 @@
+/*
+ * SuperH Video Output Unit (VOU) driver
+ *
+ * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/module.h>
+
+#include <media/sh_vou.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mediabus.h>
+#include <media/videobuf-dma-contig.h>
+
+/* Mirror addresses are not available for all registers */
+#define VOUER 0
+#define VOUCR 4
+#define VOUSTR 8
+#define VOUVCR 0xc
+#define VOUISR 0x10
+#define VOUBCR 0x14
+#define VOUDPR 0x18
+#define VOUDSR 0x1c
+#define VOUVPR 0x20
+#define VOUIR 0x24
+#define VOUSRR 0x28
+#define VOUMSR 0x2c
+#define VOUHIR 0x30
+#define VOUDFR 0x34
+#define VOUAD1R 0x38
+#define VOUAD2R 0x3c
+#define VOUAIR 0x40
+#define VOUSWR 0x44
+#define VOURCR 0x48
+#define VOURPR 0x50
+
+enum sh_vou_status {
+ SH_VOU_IDLE,
+ SH_VOU_INITIALISING,
+ SH_VOU_RUNNING,
+};
+
+#define VOU_MAX_IMAGE_WIDTH 720
+#define VOU_MAX_IMAGE_HEIGHT 576
+
+struct sh_vou_device {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev;
+ atomic_t use_count;
+ struct sh_vou_pdata *pdata;
+ spinlock_t lock;
+ void __iomem *base;
+ /* State information */
+ struct v4l2_pix_format pix;
+ struct v4l2_rect rect;
+ struct list_head queue;
+ v4l2_std_id std;
+ int pix_idx;
+ struct videobuf_buffer *active;
+ enum sh_vou_status status;
+ struct mutex fop_lock;
+};
+
+struct sh_vou_file {
+ struct videobuf_queue vbq;
+};
+
+/* Register access routines for sides A, B and mirror addresses */
+static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg);
+}
+
+static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg);
+ __raw_writel(value, vou_dev->base + reg + 0x1000);
+}
+
+static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value)
+{
+ __raw_writel(value, vou_dev->base + reg + 0x2000);
+}
+
+static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg)
+{
+ return __raw_readl(vou_dev->base + reg);
+}
+
+static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ u32 old = __raw_readl(vou_dev->base + reg);
+
+ value = (value & mask) | (old & ~mask);
+ __raw_writel(value, vou_dev->base + reg);
+}
+
+static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask);
+}
+
+static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg,
+ u32 value, u32 mask)
+{
+ sh_vou_reg_a_set(vou_dev, reg, value, mask);
+ sh_vou_reg_b_set(vou_dev, reg, value, mask);
+}
+
+struct sh_vou_fmt {
+ u32 pfmt;
+ char *desc;
+ unsigned char bpp;
+ unsigned char rgb;
+ unsigned char yf;
+ unsigned char pkf;
+};
+
+/* Further pixel formats can be added */
+static struct sh_vou_fmt vou_fmt[] = {
+ {
+ .pfmt = V4L2_PIX_FMT_NV12,
+ .bpp = 12,
+ .desc = "YVU420 planar",
+ .yf = 0,
+ .rgb = 0,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_NV16,
+ .bpp = 16,
+ .desc = "YVYU planar",
+ .yf = 1,
+ .rgb = 0,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB24,
+ .bpp = 24,
+ .desc = "RGB24",
+ .pkf = 2,
+ .rgb = 1,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB565,
+ .bpp = 16,
+ .desc = "RGB565",
+ .pkf = 3,
+ .rgb = 1,
+ },
+ {
+ .pfmt = V4L2_PIX_FMT_RGB565X,
+ .bpp = 16,
+ .desc = "RGB565 byteswapped",
+ .pkf = 3,
+ .rgb = 1,
+ },
+};
+
+static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
+ struct videobuf_buffer *vb)
+{
+ dma_addr_t addr1, addr2;
+
+ addr1 = videobuf_to_dma_contig(vb);
+ switch (vou_dev->pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height;
+ break;
+ default:
+ addr2 = 0;
+ }
+
+ sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1);
+ sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
+}
+
+static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
+ struct videobuf_buffer *vb)
+{
+ unsigned int row_coeff;
+#ifdef __LITTLE_ENDIAN
+ u32 dataswap = 7;
+#else
+ u32 dataswap = 0;
+#endif
+
+ switch (vou_dev->pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ row_coeff = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ dataswap ^= 1;
+ case V4L2_PIX_FMT_RGB565X:
+ row_coeff = 2;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ row_coeff = 3;
+ break;
+ }
+
+ sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
+ sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
+ sh_vou_schedule_next(vou_dev, vb);
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ BUG_ON(in_interrupt());
+
+ /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
+ videobuf_waiton(vq, vb, 0, 0);
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/* Locking: caller holds fop_lock mutex */
+static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
+ vou_dev->pix.height / 8;
+
+ if (*count < 2)
+ *count = 2;
+
+ /* Taking into account maximum frame size, *count will stay >= 2 */
+ if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
+ *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
+
+ dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+/* Locking: caller holds fop_lock mutex */
+static int sh_vou_buf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
+ int ret;
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ if (vb->width != pix->width ||
+ vb->height != pix->height ||
+ vb->field != pix->field) {
+ vb->width = pix->width;
+ vb->height = pix->height;
+ vb->field = field;
+ if (vb->state != VIDEOBUF_NEEDS_INIT)
+ free_buffer(vq, vb);
+ }
+
+ vb->size = vb->height * bytes_per_line;
+ if (vb->baddr && vb->bsize < vb->size) {
+ /* User buffer too small */
+ dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n",
+ vb->bsize, vb->baddr);
+ return -EINVAL;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret < 0) {
+ dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
+ vb->memory, ret);
+ return ret;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ }
+
+ dev_dbg(vq->dev,
+ "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n",
+ __func__, vou_dev->pix_idx, bytes_per_line,
+ videobuf_to_dma_contig(vb), vb->memory, vb->state);
+
+ return 0;
+}
+
+/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
+static void sh_vou_buf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &vou_dev->queue);
+
+ if (vou_dev->status == SH_VOU_RUNNING) {
+ return;
+ } else if (!vou_dev->active) {
+ vou_dev->active = vb;
+ /* Start from side A: we use mirror addresses, so, set B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 1);
+ dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__,
+ sh_vou_reg_a_read(vou_dev, VOUSTR));
+ sh_vou_schedule_next(vou_dev, vb);
+ /* Only activate VOU after the second buffer */
+ } else if (vou_dev->active->queue.next == &vb->queue) {
+ /* Second buffer - initialise register side B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 0);
+ sh_vou_stream_start(vou_dev, vb);
+
+ /* Register side switching with frame VSYNC */
+ sh_vou_reg_a_write(vou_dev, VOURCR, 5);
+ dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__,
+ sh_vou_reg_a_read(vou_dev, VOUSTR));
+
+ /* Enable End-of-Frame (VSYNC) interrupts */
+ sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
+ /* Two buffers on the queue - activate the hardware */
+
+ vou_dev->status = SH_VOU_RUNNING;
+ sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+ }
+}
+
+static void sh_vou_buf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct video_device *vdev = vq->priv_data;
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ unsigned long flags;
+
+ dev_dbg(vq->dev, "%s()\n", __func__);
+
+ spin_lock_irqsave(&vou_dev->lock, flags);
+
+ if (vou_dev->active == vb) {
+ /* disable output */
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+ /* ...but the current frame will complete */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ vou_dev->active = NULL;
+ }
+
+ if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
+ vb->state = VIDEOBUF_ERROR;
+ list_del(&vb->queue);
+ }
+
+ spin_unlock_irqrestore(&vou_dev->lock, flags);
+
+ free_buffer(vq, vb);
+}
+
+static struct videobuf_queue_ops sh_vou_video_qops = {
+ .buf_setup = sh_vou_buf_setup,
+ .buf_prepare = sh_vou_buf_prepare,
+ .buf_queue = sh_vou_buf_queue,
+ .buf_release = sh_vou_buf_release,
+};
+
+/* Video IOCTLs */
+static int sh_vou_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+/* Enumerate formats, that the device can accept from the user */
+static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ if (fmt->index >= ARRAY_SIZE(vou_fmt))
+ return -EINVAL;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ strlcpy(fmt->description, vou_fmt[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = vou_fmt[fmt->index].pfmt;
+
+ return 0;
+}
+
+static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ fmt->fmt.pix = vou_dev->pix;
+
+ return 0;
+}
+
+static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4};
+static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1};
+static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};
+static const unsigned char vou_scale_v_num[] = {1, 2, 4};
+static const unsigned char vou_scale_v_den[] = {1, 1, 1};
+static const unsigned char vou_scale_v_fld[] = {0, 1};
+
+static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
+ int pix_idx, int w_idx, int h_idx)
+{
+ struct sh_vou_fmt *fmt = vou_fmt + pix_idx;
+ unsigned int black_left, black_top, width_max, height_max,
+ frame_in_height, frame_out_height, frame_out_top;
+ struct v4l2_rect *rect = &vou_dev->rect;
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ u32 vouvcr = 0, dsr_h, dsr_v;
+
+ if (vou_dev->std & V4L2_STD_525_60) {
+ width_max = 858;
+ height_max = 262;
+ } else {
+ width_max = 864;
+ height_max = 312;
+ }
+
+ frame_in_height = pix->height / 2;
+ frame_out_height = rect->height / 2;
+ frame_out_top = rect->top / 2;
+
+ /*
+ * Cropping scheme: max useful image is 720x480, and the total video
+ * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts
+ * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock,
+ * of which the first 33 / 25 clocks HSYNC must be held active. This
+ * has to be configured in CR[HW]. 1 pixel equals 2 clock periods.
+ * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives
+ * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area,
+ * beyond DSR, specified on the left and top by the VPR register "black
+ * pixels" and out-of-image area (DPR) "background pixels." We fix VPR
+ * at 138 / 144 : 20, because that's the HSYNC timing, that our first
+ * client requires, and that's exactly what leaves us 720 pixels for the
+ * image; we leave VPR[VVP] at default 20 for now, because the client
+ * doesn't seem to have any special requirements for it. Otherwise we
+ * could also set it to max - 240 = 22 / 72. Thus VPR depends only on
+ * the selected standard, and DPR and DSR are selected according to
+ * cropping. Q: how does the client detect the first valid line? Does
+ * HSYNC stay inactive during invalid (black) lines?
+ */
+ black_left = width_max - VOU_MAX_IMAGE_WIDTH;
+ black_top = 20;
+
+ dsr_h = rect->width + rect->left;
+ dsr_v = frame_out_height + frame_out_top;
+
+ dev_dbg(vou_dev->v4l2_dev.dev,
+ "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n",
+ pix->width, frame_in_height, black_left, black_top,
+ rect->left, frame_out_top, dsr_h, dsr_v);
+
+ /* VOUISR height - half of a frame height in frame mode */
+ sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height);
+ sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top);
+ sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top);
+ sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v);
+
+ /*
+ * if necessary, we could set VOUHIR to
+ * max(black_left + dsr_h, width_max) here
+ */
+
+ if (w_idx)
+ vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4);
+ if (h_idx)
+ vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1];
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr);
+
+ /* To produce a colour bar for testing set bit 23 of VOUVCR */
+ sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr);
+ sh_vou_reg_ab_write(vou_dev, VOUDFR,
+ fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16));
+}
+
+struct sh_vou_geometry {
+ struct v4l2_rect output;
+ unsigned int in_width;
+ unsigned int in_height;
+ int scale_idx_h;
+ int scale_idx_v;
+};
+
+/*
+ * Find input geometry, that we can use to produce output, closest to the
+ * requested rectangle, using VOU scaling
+ */
+static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
+{
+ /* The compiler cannot know, that best and idx will indeed be set */
+ unsigned int best_err = UINT_MAX, best = 0, img_height_max;
+ int i, idx = 0;
+
+ if (std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ /* Image width must be a multiple of 4 */
+ v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
+ &geo->in_height, 0, img_height_max, 1, 0);
+
+ /* Select scales to come as close as possible to the output image */
+ for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
+ unsigned int err;
+ unsigned int found = geo->output.width * vou_scale_h_den[i] /
+ vou_scale_h_num[i];
+
+ if (found > VOU_MAX_IMAGE_WIDTH)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->in_width);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->in_width = best;
+ geo->scale_idx_h = idx;
+
+ best_err = UINT_MAX;
+
+ /* This loop can be replaced with one division */
+ for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) {
+ unsigned int err;
+ unsigned int found = geo->output.height * vou_scale_v_den[i] /
+ vou_scale_v_num[i];
+
+ if (found > img_height_max)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->in_height);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->in_height = best;
+ geo->scale_idx_v = idx;
+}
+
+/*
+ * Find output geometry, that we can produce, using VOU scaling, closest to
+ * the requested rectangle
+ */
+static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
+{
+ unsigned int best_err = UINT_MAX, best, width_max, height_max,
+ img_height_max;
+ int i, idx;
+
+ if (std & V4L2_STD_525_60) {
+ width_max = 858;
+ height_max = 262 * 2;
+ img_height_max = 480;
+ } else {
+ width_max = 864;
+ height_max = 312 * 2;
+ img_height_max = 576;
+ }
+
+ /* Select scales to come as close as possible to the output image */
+ for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) {
+ unsigned int err;
+ unsigned int found = geo->in_width * vou_scale_h_num[i] /
+ vou_scale_h_den[i];
+
+ if (found > VOU_MAX_IMAGE_WIDTH)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->output.width);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->output.width = best;
+ geo->scale_idx_h = idx;
+ if (geo->output.left + best > width_max)
+ geo->output.left = width_max - best;
+
+ pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,
+ vou_scale_h_num[idx], vou_scale_h_den[idx], best);
+
+ best_err = UINT_MAX;
+
+ /* This loop can be replaced with one division */
+ for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) {
+ unsigned int err;
+ unsigned int found = geo->in_height * vou_scale_v_num[i] /
+ vou_scale_v_den[i];
+
+ if (found > img_height_max)
+ /* scales increase */
+ break;
+
+ err = abs(found - geo->output.height);
+ if (err < best_err) {
+ best_err = err;
+ idx = i;
+ best = found;
+ }
+ if (!err)
+ break;
+ }
+
+ geo->output.height = best;
+ geo->scale_idx_v = idx;
+ if (geo->output.top + best > height_max)
+ geo->output.top = height_max - best;
+
+ pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,
+ vou_scale_v_num[idx], vou_scale_v_den[idx], best);
+}
+
+static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ unsigned int img_height_max;
+ int pix_idx;
+ struct sh_vou_geometry geo;
+ struct v4l2_mbus_framefmt mbfmt = {
+ /* Revisit: is this the correct code? */
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .field = V4L2_FIELD_INTERLACED,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ };
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
+ vou_dev->rect.width, vou_dev->rect.height,
+ pix->width, pix->height);
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
+ if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
+ break;
+
+ if (pix_idx == ARRAY_SIZE(vou_fmt))
+ return -EINVAL;
+
+ if (vou_dev->std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ /* Image width must be a multiple of 4 */
+ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
+ &pix->height, 0, img_height_max, 1, 0);
+
+ geo.in_width = pix->width;
+ geo.in_height = pix->height;
+ geo.output = vou_dev->rect;
+
+ vou_adjust_output(&geo, vou_dev->std);
+
+ mbfmt.width = geo.output.width;
+ mbfmt.height = geo.output.height;
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_mbus_fmt, &mbfmt);
+ /* Must be implemented, so, don't check for -ENOIOCTLCMD */
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
+ geo.output.width, geo.output.height, mbfmt.width, mbfmt.height);
+
+ /* Sanity checks */
+ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
+ (unsigned)mbfmt.height > img_height_max ||
+ mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ return -EIO;
+
+ if (mbfmt.width != geo.output.width ||
+ mbfmt.height != geo.output.height) {
+ geo.output.width = mbfmt.width;
+ geo.output.height = mbfmt.height;
+
+ vou_adjust_input(&geo, vou_dev->std);
+ }
+
+ /* We tried to preserve output rectangle, but it could have changed */
+ vou_dev->rect = geo.output;
+ pix->width = geo.in_width;
+ pix->height = geo.in_height;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__,
+ pix->width, pix->height);
+
+ vou_dev->pix_idx = pix_idx;
+
+ vou_dev->pix = *pix;
+
+ sh_vou_configure_geometry(vou_dev, pix_idx,
+ geo.scale_idx_h, geo.scale_idx_v);
+
+ return 0;
+}
+
+static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct sh_vou_file *vou_file = priv;
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ int i;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ pix->field = V4L2_FIELD_NONE;
+
+ v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
+ &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
+
+ for (i = 0; ARRAY_SIZE(vou_fmt); i++)
+ if (vou_fmt[i].pfmt == pix->pixelformat)
+ return 0;
+
+ pix->pixelformat = vou_fmt[0].pfmt;
+
+ return 0;
+}
+
+static int sh_vou_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ return videobuf_reqbufs(&vou_file->vbq, req);
+}
+
+static int sh_vou_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_querybuf(&vou_file->vbq, b);
+}
+
+static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_qbuf(&vou_file->vbq, b);
+}
+
+static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
+}
+
+static int sh_vou_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = priv;
+ int ret;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+ video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* This calls our .buf_queue() (== sh_vou_buf_queue) */
+ return videobuf_streamon(&vou_file->vbq);
+}
+
+static int sh_vou_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buftype)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop streaming
+ */
+ videobuf_streamoff(&vou_file->vbq);
+ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
+
+ return 0;
+}
+
+static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
+{
+ switch (bus_fmt) {
+ default:
+ pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n",
+ __func__, bus_fmt);
+ case SH_VOU_BUS_8BIT:
+ return 1;
+ case SH_VOU_BUS_16BIT:
+ return 0;
+ case SH_VOU_BUS_BT656:
+ return 3;
+ }
+}
+
+static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id);
+
+ if (*std_id & ~vdev->tvnorms)
+ return -EINVAL;
+
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_std_output, *std_id);
+ /* Shall we continue, if the subdev doesn't support .s_std_output()? */
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (*std_id & V4L2_STD_525_60)
+ sh_vou_reg_ab_set(vou_dev, VOUCR,
+ sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
+ else
+ sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
+
+ vou_dev->std = *std_id;
+
+ return 0;
+}
+
+static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ *std = vou_dev->std;
+
+ return 0;
+}
+
+static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->c = vou_dev->rect;
+
+ return 0;
+}
+
+/* Assume a dull encoder, do all the work ourselves. */
+static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ struct sh_vou_geometry geo;
+ struct v4l2_mbus_framefmt mbfmt = {
+ /* Revisit: is this the correct code? */
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .field = V4L2_FIELD_INTERLACED,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ };
+ unsigned int img_height_max;
+ int ret;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ if (vou_dev->std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
+ v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
+ &rect->height, 0, img_height_max, 1, 0);
+
+ if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
+ rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
+
+ if (rect->height + rect->top > img_height_max)
+ rect->top = img_height_max - rect->height;
+
+ geo.output = *rect;
+ geo.in_width = pix->width;
+ geo.in_height = pix->height;
+
+ /* Configure the encoder one-to-one, position at 0, ignore errors */
+ sd_crop.c.width = geo.output.width;
+ sd_crop.c.height = geo.output.height;
+ /*
+ * We first issue a S_CROP, so that the subsequent S_FMT delivers the
+ * final encoder configuration.
+ */
+ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_crop, &sd_crop);
+ mbfmt.width = geo.output.width;
+ mbfmt.height = geo.output.height;
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
+ s_mbus_fmt, &mbfmt);
+ /* Must be implemented, so, don't check for -ENOIOCTLCMD */
+ if (ret < 0)
+ return ret;
+
+ /* Sanity checks */
+ if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
+ (unsigned)mbfmt.height > img_height_max ||
+ mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
+ return -EIO;
+
+ geo.output.width = mbfmt.width;
+ geo.output.height = mbfmt.height;
+
+ /*
+ * No down-scaling. According to the API, current call has precedence:
+ * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two.
+ */
+ vou_adjust_input(&geo, vou_dev->std);
+
+ /* We tried to preserve output rectangle, but it could have changed */
+ vou_dev->rect = geo.output;
+ pix->width = geo.in_width;
+ pix->height = geo.in_height;
+
+ sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx,
+ geo.scale_idx_h, geo.scale_idx_v);
+
+ return 0;
+}
+
+/*
+ * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
+ * is the initial register values, height takes the interlaced format into
+ * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
+ * actually only meaningfully contain values <= 720 and <= 240 respectively, and
+ * not <= 864 and <= 312.
+ */
+static int sh_vou_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *a)
+{
+ struct sh_vou_file *vou_file = priv;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = VOU_MAX_IMAGE_WIDTH;
+ a->bounds.height = VOU_MAX_IMAGE_HEIGHT;
+ /* Default = max, set VOUDPR = 0, which is not hardware default */
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = VOU_MAX_IMAGE_WIDTH;
+ a->defrect.height = VOU_MAX_IMAGE_HEIGHT;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static irqreturn_t sh_vou_isr(int irq, void *dev_id)
+{
+ struct sh_vou_device *vou_dev = dev_id;
+ static unsigned long j;
+ struct videobuf_buffer *vb;
+ static int cnt;
+ static int side;
+ u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
+ u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
+
+ if (!(irq_status & 0x300)) {
+ if (printk_timed_ratelimit(&j, 500))
+ dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n",
+ irq_status);
+ return IRQ_NONE;
+ }
+
+ spin_lock(&vou_dev->lock);
+ if (!vou_dev->active || list_empty(&vou_dev->queue)) {
+ if (printk_timed_ratelimit(&j, 500))
+ dev_warn(vou_dev->v4l2_dev.dev,
+ "IRQ without active buffer: %x!\n", irq_status);
+ /* Just ack: buf_release will disable further interrupts */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300);
+ spin_unlock(&vou_dev->lock);
+ return IRQ_HANDLED;
+ }
+
+ masked = ~(0x300 & irq_status) & irq_status & 0x30304;
+ dev_dbg(vou_dev->v4l2_dev.dev,
+ "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n",
+ irq_status, masked, vou_status, cnt);
+
+ cnt++;
+ side = vou_status & 0x10000;
+
+ /* Clear only set interrupts */
+ sh_vou_reg_a_write(vou_dev, VOUIR, masked);
+
+ vb = vou_dev->active;
+ list_del(&vb->queue);
+
+ vb->state = VIDEOBUF_DONE;
+ do_gettimeofday(&vb->ts);
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ if (list_empty(&vou_dev->queue)) {
+ /* Stop VOU */
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
+ __func__, cnt);
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+ vou_dev->active = NULL;
+ vou_dev->status = SH_VOU_INITIALISING;
+ /* Disable End-of-Frame (VSYNC) interrupts */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ spin_unlock(&vou_dev->lock);
+ return IRQ_HANDLED;
+ }
+
+ vou_dev->active = list_entry(vou_dev->queue.next,
+ struct videobuf_buffer, queue);
+
+ if (vou_dev->active->queue.next != &vou_dev->queue) {
+ struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
+ struct videobuf_buffer, queue);
+ sh_vou_schedule_next(vou_dev, new);
+ }
+
+ spin_unlock(&vou_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
+{
+ struct sh_vou_pdata *pdata = vou_dev->pdata;
+ u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29;
+ int i = 100;
+
+ /* Disable all IRQs */
+ sh_vou_reg_a_write(vou_dev, VOUIR, 0);
+
+ /* Reset VOU interfaces - registers unaffected */
+ sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101);
+ while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101))
+ udelay(1);
+
+ if (!i)
+ return -ETIMEDOUT;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i);
+
+ if (pdata->flags & SH_VOU_PCLK_FALLING)
+ voucr |= 1 << 28;
+ if (pdata->flags & SH_VOU_HSYNC_LOW)
+ voucr |= 1 << 27;
+ if (pdata->flags & SH_VOU_VSYNC_LOW)
+ voucr |= 1 << 26;
+ sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000);
+
+ /* Manual register side switching at first */
+ sh_vou_reg_a_write(vou_dev, VOURCR, 4);
+ /* Default - fixed HSYNC length, can be made configurable is required */
+ sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
+
+ return 0;
+}
+
+/* File operations */
+static int sh_vou_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
+ GFP_KERNEL);
+
+ if (!vou_file)
+ return -ENOMEM;
+
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+
+ file->private_data = vou_file;
+
+ if (mutex_lock_interruptible(&vou_dev->fop_lock))
+ return -ERESTARTSYS;
+ if (atomic_inc_return(&vou_dev->use_count) == 1) {
+ int ret;
+ /* First open */
+ vou_dev->status = SH_VOU_INITIALISING;
+ pm_runtime_get_sync(vdev->v4l2_dev->dev);
+ ret = sh_vou_hw_init(vou_dev);
+ if (ret < 0) {
+ atomic_dec(&vou_dev->use_count);
+ pm_runtime_put(vdev->v4l2_dev->dev);
+ vou_dev->status = SH_VOU_IDLE;
+ mutex_unlock(&vou_dev->fop_lock);
+ return ret;
+ }
+ }
+
+ videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
+ vou_dev->v4l2_dev.dev, &vou_dev->lock,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), vdev,
+ &vou_dev->fop_lock);
+ mutex_unlock(&vou_dev->fop_lock);
+
+ return 0;
+}
+
+static int sh_vou_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = file->private_data;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ if (!atomic_dec_return(&vou_dev->use_count)) {
+ mutex_lock(&vou_dev->fop_lock);
+ /* Last close */
+ vou_dev->status = SH_VOU_IDLE;
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
+ pm_runtime_put(vdev->v4l2_dev->dev);
+ mutex_unlock(&vou_dev->fop_lock);
+ }
+
+ file->private_data = NULL;
+ kfree(vou_file);
+
+ return 0;
+}
+
+static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = file->private_data;
+ int ret;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ if (mutex_lock_interruptible(&vou_dev->fop_lock))
+ return -ERESTARTSYS;
+ ret = videobuf_mmap_mapper(&vou_file->vbq, vma);
+ mutex_unlock(&vou_dev->fop_lock);
+ return ret;
+}
+
+static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_file *vou_file = file->private_data;
+ unsigned int res;
+
+ dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
+
+ mutex_lock(&vou_dev->fop_lock);
+ res = videobuf_poll_stream(file, &vou_file->vbq, wait);
+ mutex_unlock(&vou_dev->fop_lock);
+ return res;
+}
+
+static int sh_vou_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sh_vou_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
+}
+
+static int sh_vou_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+
+ return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
+}
+#endif
+
+/* sh_vou display ioctl operations */
+static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
+ .vidioc_querycap = sh_vou_querycap,
+ .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,
+ .vidioc_reqbufs = sh_vou_reqbufs,
+ .vidioc_querybuf = sh_vou_querybuf,
+ .vidioc_qbuf = sh_vou_qbuf,
+ .vidioc_dqbuf = sh_vou_dqbuf,
+ .vidioc_streamon = sh_vou_streamon,
+ .vidioc_streamoff = sh_vou_streamoff,
+ .vidioc_s_std = sh_vou_s_std,
+ .vidioc_g_std = sh_vou_g_std,
+ .vidioc_cropcap = sh_vou_cropcap,
+ .vidioc_g_crop = sh_vou_g_crop,
+ .vidioc_s_crop = sh_vou_s_crop,
+ .vidioc_g_chip_ident = sh_vou_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = sh_vou_g_register,
+ .vidioc_s_register = sh_vou_s_register,
+#endif
+};
+
+static const struct v4l2_file_operations sh_vou_fops = {
+ .owner = THIS_MODULE,
+ .open = sh_vou_open,
+ .release = sh_vou_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = sh_vou_mmap,
+ .poll = sh_vou_poll,
+};
+
+static const struct video_device sh_vou_video_template = {
+ .name = "sh_vou",
+ .fops = &sh_vou_fops,
+ .ioctl_ops = &sh_vou_ioctl_ops,
+ .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
+ .current_norm = V4L2_STD_NTSC_M,
+ .vfl_dir = VFL_DIR_TX,
+};
+
+static int __devinit sh_vou_probe(struct platform_device *pdev)
+{
+ struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data;
+ struct v4l2_rect *rect;
+ struct v4l2_pix_format *pix;
+ struct i2c_adapter *i2c_adap;
+ struct video_device *vdev;
+ struct sh_vou_device *vou_dev;
+ struct resource *reg_res, *region;
+ struct v4l2_subdev *subdev;
+ int irq, ret;
+
+ reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ if (!vou_pdata || !reg_res || irq <= 0) {
+ dev_err(&pdev->dev, "Insufficient VOU platform information.\n");
+ return -ENODEV;
+ }
+
+ vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
+ if (!vou_dev)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&vou_dev->queue);
+ spin_lock_init(&vou_dev->lock);
+ mutex_init(&vou_dev->fop_lock);
+ atomic_set(&vou_dev->use_count, 0);
+ vou_dev->pdata = vou_pdata;
+ vou_dev->status = SH_VOU_IDLE;
+
+ rect = &vou_dev->rect;
+ pix = &vou_dev->pix;
+
+ /* Fill in defaults */
+ vou_dev->std = sh_vou_video_template.current_norm;
+ rect->left = 0;
+ rect->top = 0;
+ rect->width = VOU_MAX_IMAGE_WIDTH;
+ rect->height = 480;
+ pix->width = VOU_MAX_IMAGE_WIDTH;
+ pix->height = 480;
+ pix->pixelformat = V4L2_PIX_FMT_YVYU;
+ pix->field = V4L2_FIELD_NONE;
+ pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2;
+ pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ region = request_mem_region(reg_res->start, resource_size(reg_res),
+ pdev->name);
+ if (!region) {
+ dev_err(&pdev->dev, "VOU region already claimed\n");
+ ret = -EBUSY;
+ goto ereqmemreg;
+ }
+
+ vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
+ if (!vou_dev->base) {
+ ret = -ENOMEM;
+ goto emap;
+ }
+
+ ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
+ if (ret < 0)
+ goto ereqirq;
+
+ ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error registering v4l2 device\n");
+ goto ev4l2devreg;
+ }
+
+ /* Allocate memory for video device */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ ret = -ENOMEM;
+ goto evdevalloc;
+ }
+
+ *vdev = sh_vou_video_template;
+ if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)
+ vdev->tvnorms |= V4L2_STD_PAL;
+ vdev->v4l2_dev = &vou_dev->v4l2_dev;
+ vdev->release = video_device_release;
+ vdev->lock = &vou_dev->fop_lock;
+
+ vou_dev->vdev = vdev;
+ video_set_drvdata(vdev, vou_dev);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap);
+ if (!i2c_adap) {
+ ret = -ENODEV;
+ goto ei2cgadap;
+ }
+
+ ret = sh_vou_hw_init(vou_dev);
+ if (ret < 0)
+ goto ereset;
+
+ subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,
+ vou_pdata->board_info, NULL);
+ if (!subdev) {
+ ret = -ENOMEM;
+ goto ei2cnd;
+ }
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0)
+ goto evregdev;
+
+ return 0;
+
+evregdev:
+ei2cnd:
+ereset:
+ i2c_put_adapter(i2c_adap);
+ei2cgadap:
+ video_device_release(vdev);
+ pm_runtime_disable(&pdev->dev);
+evdevalloc:
+ v4l2_device_unregister(&vou_dev->v4l2_dev);
+ev4l2devreg:
+ free_irq(irq, vou_dev);
+ereqirq:
+ iounmap(vou_dev->base);
+emap:
+ release_mem_region(reg_res->start, resource_size(reg_res));
+ereqmemreg:
+ kfree(vou_dev);
+ return ret;
+}
+
+static int __devexit sh_vou_remove(struct platform_device *pdev)
+{
+ int irq = platform_get_irq(pdev, 0);
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct sh_vou_device *vou_dev = container_of(v4l2_dev,
+ struct sh_vou_device, v4l2_dev);
+ struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
+ struct v4l2_subdev, list);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct resource *reg_res;
+
+ if (irq > 0)
+ free_irq(irq, vou_dev);
+ pm_runtime_disable(&pdev->dev);
+ video_unregister_device(vou_dev->vdev);
+ i2c_put_adapter(client->adapter);
+ v4l2_device_unregister(&vou_dev->v4l2_dev);
+ iounmap(vou_dev->base);
+ reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (reg_res)
+ release_mem_region(reg_res->start, resource_size(reg_res));
+ kfree(vou_dev);
+ return 0;
+}
+
+static struct platform_driver __refdata sh_vou = {
+ .remove = __devexit_p(sh_vou_remove),
+ .driver = {
+ .name = "sh-vou",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sh_vou_init(void)
+{
+ return platform_driver_probe(&sh_vou, sh_vou_probe);
+}
+
+static void __exit sh_vou_exit(void)
+{
+ platform_driver_unregister(&sh_vou);
+}
+
+module_init(sh_vou_init);
+module_exit(sh_vou_exit);
+
+MODULE_DESCRIPTION("SuperH VOU driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS("platform:sh-vou");
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
new file mode 100644
index 000000000000..9afe1e7bde74
--- /dev/null
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -0,0 +1,87 @@
+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
+ directly to the data bus of an SoC.
+
+config SOC_CAMERA_PLATFORM
+ tristate "platform camera support"
+ depends on SOC_CAMERA
+ help
+ This is a generic SoC camera platform driver, useful for testing
+
+config MX1_VIDEO
+ bool
+
+config VIDEO_MX1
+ tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
+ depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
+ select FIQ
+ select VIDEOBUF_DMA_CONTIG
+ select MX1_VIDEO
+ ---help---
+ This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
+
+config MX3_VIDEO
+ bool
+
+config VIDEO_MX3
+ tristate "i.MX3x Camera Sensor Interface driver"
+ depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
+ select VIDEOBUF2_DMA_CONTIG
+ select MX3_VIDEO
+ ---help---
+ This is a v4l2 driver for the i.MX3x Camera Sensor Interface
+
+config VIDEO_PXA27x
+ tristate "PXA27x Quick Capture Interface driver"
+ depends on VIDEO_DEV && PXA27x && SOC_CAMERA
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the PXA27x Quick Capture Interface
+
+config VIDEO_SH_MOBILE_CSI2
+ tristate "SuperH Mobile MIPI CSI-2 Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
+ ---help---
+ This is a v4l2 driver for the SuperH MIPI CSI-2 Interface
+
+config VIDEO_SH_MOBILE_CEU
+ tristate "SuperH Mobile CEU Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a v4l2 driver for the SuperH Mobile CEU Interface
+
+config VIDEO_OMAP1
+ tristate "OMAP1 Camera Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
+ select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP1 camera interface
+
+config VIDEO_MX2_HOSTSUPPORT
+ bool
+
+config VIDEO_MX2
+ tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
+ depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || (ARCH_MX25 && BROKEN))
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEO_MX2_HOSTSUPPORT
+ ---help---
+ This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
+ Interface
+
+config VIDEO_ATMEL_ISI
+ tristate "ATMEL Image Sensor Interface (ISI) support"
+ depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This module makes the ATMEL Image Sensor Interface available
+ as a v4l2 device.
+
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
new file mode 100644
index 000000000000..136b7f8ff10d
--- /dev/null
+++ b/drivers/media/platform/soc_camera/Makefile
@@ -0,0 +1,14 @@
+obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
+obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
+
+# soc-camera host drivers have to be linked after camera drivers
+obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
+obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
+obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
+obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
+obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
+obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
+obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
+
+ccflags-y += -I$(srctree)/drivers/media/i2c/soc_camera
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 6274a91c25c7..6274a91c25c7 100644
--- a/drivers/media/video/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c
index 560a65aa7038..560a65aa7038 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/platform/soc_camera/mx1_camera.c
diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c
new file mode 100644
index 000000000000..619df35de3da
--- /dev/null
+++ b/drivers/media/platform/soc_camera/mx2_camera.c
@@ -0,0 +1,1863 @@
+/*
+ * V4L2 Driver for i.MX27/i.MX25 camera host
+ *
+ * Copyright (C) 2008, Sascha Hauer, Pengutronix
+ * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gcd.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+
+#include <linux/videodev2.h>
+
+#include <mach/mx2_cam.h>
+#include <mach/hardware.h>
+
+#include <asm/dma.h>
+
+#define MX2_CAM_DRV_NAME "mx2-camera"
+#define MX2_CAM_VERSION "0.0.6"
+#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
+
+/* reset values */
+#define CSICR1_RESET_VAL 0x40000800
+#define CSICR2_RESET_VAL 0x0
+#define CSICR3_RESET_VAL 0x0
+
+/* csi control reg 1 */
+#define CSICR1_SWAP16_EN (1 << 31)
+#define CSICR1_EXT_VSYNC (1 << 30)
+#define CSICR1_EOF_INTEN (1 << 29)
+#define CSICR1_PRP_IF_EN (1 << 28)
+#define CSICR1_CCIR_MODE (1 << 27)
+#define CSICR1_COF_INTEN (1 << 26)
+#define CSICR1_SF_OR_INTEN (1 << 25)
+#define CSICR1_RF_OR_INTEN (1 << 24)
+#define CSICR1_STATFF_LEVEL (3 << 22)
+#define CSICR1_STATFF_INTEN (1 << 21)
+#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */
+#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */
+#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */
+#define CSICR1_RXFF_INTEN (1 << 18)
+#define CSICR1_SOF_POL (1 << 17)
+#define CSICR1_SOF_INTEN (1 << 16)
+#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
+#define CSICR1_HSYNC_POL (1 << 11)
+#define CSICR1_CCIR_EN (1 << 10)
+#define CSICR1_MCLKEN (1 << 9)
+#define CSICR1_FCC (1 << 8)
+#define CSICR1_PACK_DIR (1 << 7)
+#define CSICR1_CLR_STATFIFO (1 << 6)
+#define CSICR1_CLR_RXFIFO (1 << 5)
+#define CSICR1_GCLK_MODE (1 << 4)
+#define CSICR1_INV_DATA (1 << 3)
+#define CSICR1_INV_PCLK (1 << 2)
+#define CSICR1_REDGE (1 << 1)
+#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
+
+#define SHIFT_STATFF_LEVEL 22
+#define SHIFT_RXFF_LEVEL 19
+#define SHIFT_MCLKDIV 12
+
+/* control reg 3 */
+#define CSICR3_FRMCNT (0xFFFF << 16)
+#define CSICR3_FRMCNT_RST (1 << 15)
+#define CSICR3_DMA_REFLASH_RFF (1 << 14)
+#define CSICR3_DMA_REFLASH_SFF (1 << 13)
+#define CSICR3_DMA_REQ_EN_RFF (1 << 12)
+#define CSICR3_DMA_REQ_EN_SFF (1 << 11)
+#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */
+#define CSICR3_CSI_SUP (1 << 3)
+#define CSICR3_ZERO_PACK_EN (1 << 2)
+#define CSICR3_ECC_INT_EN (1 << 1)
+#define CSICR3_ECC_AUTO_EN (1 << 0)
+
+#define SHIFT_FRMCNT 16
+
+/* csi status reg */
+#define CSISR_SFF_OR_INT (1 << 25)
+#define CSISR_RFF_OR_INT (1 << 24)
+#define CSISR_STATFF_INT (1 << 21)
+#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */
+#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */
+#define CSISR_RXFF_INT (1 << 18)
+#define CSISR_EOF_INT (1 << 17)
+#define CSISR_SOF_INT (1 << 16)
+#define CSISR_F2_INT (1 << 15)
+#define CSISR_F1_INT (1 << 14)
+#define CSISR_COF_INT (1 << 13)
+#define CSISR_ECC_INT (1 << 1)
+#define CSISR_DRDY (1 << 0)
+
+#define CSICR1 0x00
+#define CSICR2 0x04
+#define CSISR (cpu_is_mx27() ? 0x08 : 0x18)
+#define CSISTATFIFO 0x0c
+#define CSIRFIFO 0x10
+#define CSIRXCNT 0x14
+#define CSICR3 (cpu_is_mx27() ? 0x1C : 0x08)
+#define CSIDMASA_STATFIFO 0x20
+#define CSIDMATA_STATFIFO 0x24
+#define CSIDMASA_FB1 0x28
+#define CSIDMASA_FB2 0x2c
+#define CSIFBUF_PARA 0x30
+#define CSIIMAG_PARA 0x34
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+/* Resizing registers */
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
+
+#define MAX_VIDEO_MEM 16
+
+#define RESIZE_NUM_MIN 1
+#define RESIZE_NUM_MAX 20
+#define BC_COEF 3
+#define SZ_COEF (1 << BC_COEF)
+
+#define RESIZE_DIR_H 0
+#define RESIZE_DIR_V 1
+
+#define RESIZE_ALGO_BILINEAR 0
+#define RESIZE_ALGO_AVERAGING 1
+
+struct mx2_prp_cfg {
+ int channel;
+ u32 in_fmt;
+ u32 out_fmt;
+ u32 src_pixel;
+ u32 ch1_pixel;
+ u32 irq_flags;
+ u32 csicr1;
+};
+
+/* prp resizing parameters */
+struct emma_prp_resize {
+ int algo; /* type of algorithm used */
+ int len; /* number of coefficients */
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
+};
+
+/* prp configuration for a client-host fmt pair */
+struct mx2_fmt_cfg {
+ enum v4l2_mbus_pixelcode in_fmt;
+ u32 out_fmt;
+ struct mx2_prp_cfg cfg;
+};
+
+enum mx2_buffer_state {
+ MX2_STATE_QUEUED,
+ MX2_STATE_ACTIVE,
+ MX2_STATE_DONE,
+};
+
+struct mx2_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx2_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ enum mx2_buffer_state state;
+ struct mx2_buf_internal internal;
+};
+
+struct mx2_camera_dev {
+ struct device *dev;
+ struct soc_camera_host soc_host;
+ struct soc_camera_device *icd;
+ struct clk *clk_csi, *clk_emma_ahb, *clk_emma_ipg;
+
+ void __iomem *base_csi, *base_emma;
+
+ struct mx2_camera_platform_data *pdata;
+ unsigned long platform_flags;
+
+ struct list_head capture;
+ struct list_head active_bufs;
+ struct list_head discard;
+
+ spinlock_t lock;
+
+ int dma;
+ struct mx2_buffer *active;
+ struct mx2_buffer *fb1_active;
+ struct mx2_buffer *fb2_active;
+
+ u32 csicr1;
+
+ struct mx2_buf_internal buf_discard[2];
+ void *discard_buffer;
+ dma_addr_t discard_buffer_dma;
+ size_t discard_size;
+ struct mx2_fmt_cfg *emma_prp;
+ struct emma_prp_resize resizing[2];
+ unsigned int s_width, s_height;
+ u32 frame_count;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx2_buffer, internal);
+}
+
+static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
+ /*
+ * This is a generic configuration which is valid for most
+ * prp input-output format combinations.
+ * We set the incomming and outgoing pixelformat to a
+ * 16 Bit wide format and adjust the bytesperline
+ * accordingly. With this configuration the inputdata
+ * will not be changed by the emma and could be any type
+ * of 16 Bit Pixelformat.
+ */
+ {
+ .in_fmt = 0,
+ .out_fmt = 0,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_RGB16,
+ .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
+ .src_pixel = 0x2ca00565, /* RGB565 */
+ .ch1_pixel = 0x2ca00565, /* RGB565 */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = 0,
+ }
+ },
+ {
+ .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+ {
+ .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUYV,
+ .cfg = {
+ .channel = 1,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH1_OUT_YUV422,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .ch1_pixel = 0x62000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
+ PRP_INTR_CH1FC | PRP_INTR_LBOVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_PACK_DIR,
+ }
+ },
+ {
+ .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ .csicr1 = CSICR1_SWAP16_EN,
+ }
+ },
+};
+
+static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
+ enum v4l2_mbus_pixelcode in_fmt,
+ u32 out_fmt)
+{
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
+ if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
+ (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
+ return &mx27_emma_prp_table[i];
+ }
+ /* If no match return the most generic configuration */
+ return &mx27_emma_prp_table[0];
+};
+
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+ unsigned long phys, int bufnum)
+{
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ if (prp->cfg.channel == 1) {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
+ } else {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+ u32 imgsize = pcdev->icd->user_height *
+ pcdev->icd->user_width;
+
+ writel(phys + imgsize, pcdev->base_emma +
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
+ }
+ }
+}
+
+static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
+{
+ unsigned long flags;
+
+ clk_disable_unprepare(pcdev->clk_csi);
+ writel(0, pcdev->base_csi + CSICR1);
+ if (cpu_is_mx27()) {
+ writel(0, pcdev->base_emma + PRP_CNTL);
+ } else if (cpu_is_mx25()) {
+ spin_lock_irqsave(&pcdev->lock, flags);
+ pcdev->fb1_active = NULL;
+ pcdev->fb2_active = NULL;
+ writel(0, pcdev->base_csi + CSIDMASA_FB1);
+ writel(0, pcdev->base_csi + CSIDMASA_FB2);
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on mx2 camera sensor interface
+ */
+static int mx2_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ int ret;
+ u32 csicr1;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ ret = clk_prepare_enable(pcdev->clk_csi);
+ if (ret < 0)
+ return ret;
+
+ csicr1 = CSICR1_MCLKEN;
+
+ if (cpu_is_mx27())
+ csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
+ CSICR1_RXFF_LEVEL(0);
+
+ pcdev->csicr1 = csicr1;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ pcdev->icd = icd;
+ pcdev->frame_count = 0;
+
+ dev_info(icd->parent, "Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void mx2_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ BUG_ON(icd != pcdev->icd);
+
+ dev_info(icd->parent, "Camera driver detached from camera %d\n",
+ icd->devnum);
+
+ mx2_camera_deactivate(pcdev);
+
+ pcdev->icd = NULL;
+}
+
+static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
+ int state)
+{
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
+ &pcdev->fb2_active;
+ u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (*fb_active == NULL)
+ goto out;
+
+ vb = &(*fb_active)->vb;
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+
+ if (list_empty(&pcdev->capture)) {
+ buf = NULL;
+ writel(0, pcdev->base_csi + fb_reg);
+ } else {
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ vb = &buf->vb;
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + fb_reg);
+ }
+
+ *fb_active = buf;
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ u32 status = readl(pcdev->base_csi + CSISR);
+
+ if (status & CSISR_DMA_TSF_FB1_INT)
+ mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
+ else if (status & CSISR_DMA_TSF_FB2_INT)
+ mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
+
+ /* FIXME: handle CSISR_RFF_OR_INT */
+
+ writel(status, pcdev->base_csi + CSISR);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Videobuf operations
+ */
+static int mx2_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+
+ /* TODO: support for VIDIOC_CREATE_BUFS not ready */
+ if (fmt != NULL)
+ return -ENOTTY;
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ sizes[0] = icd->sizeimage;
+
+ if (0 == *count)
+ *count = 32;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ int ret = 0;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ vb2_set_plane_payload(vb, 0, icd->sizeimage);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ return 0;
+
+out:
+ return ret;
+}
+
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+ unsigned long flags;
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf->state = MX2_STATE_QUEUED;
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
+
+ if (cpu_is_mx25()) {
+ u32 csicr3, dma_inten = 0;
+
+ if (pcdev->fb1_active == NULL) {
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + CSIDMASA_FB1);
+ pcdev->fb1_active = buf;
+ dma_inten = CSICR1_FB1_DMA_INTEN;
+ } else if (pcdev->fb2_active == NULL) {
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + CSIDMASA_FB2);
+ pcdev->fb2_active = buf;
+ dma_inten = CSICR1_FB2_DMA_INTEN;
+ }
+
+ if (dma_inten) {
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
+
+ csicr3 = readl(pcdev->base_csi + CSICR3);
+
+ /* Reflash DMA */
+ writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
+ pcdev->base_csi + CSICR3);
+
+ /* clear & enable interrupts */
+ writel(dma_inten, pcdev->base_csi + CSISR);
+ pcdev->csicr1 |= dma_inten;
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ /* enable DMA */
+ csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
+ writel(csicr3, pcdev->base_csi + CSICR3);
+ }
+ }
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx2_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+ unsigned long flags;
+
+#ifdef DEBUG
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ switch (buf->state) {
+ case MX2_STATE_ACTIVE:
+ dev_info(icd->parent, "%s (active)\n", __func__);
+ break;
+ case MX2_STATE_QUEUED:
+ dev_info(icd->parent, "%s (queued)\n", __func__);
+ break;
+ default:
+ dev_info(icd->parent, "%s (unknown) %d\n", __func__,
+ buf->state);
+ break;
+ }
+#endif
+
+ /*
+ * Terminate only queued but inactive buffers. Active buffers are
+ * released when they become inactive after videobuf_waiton().
+ *
+ * FIXME: implement forced termination of active buffers for mx27 and
+ * mx27 eMMA, so that the user won't get stuck in an uninterruptible
+ * state. This requires a specific handling for each of the these DMA
+ * types.
+ */
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+ if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
+ if (pcdev->fb1_active == buf) {
+ pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
+ writel(0, pcdev->base_csi + CSIDMASA_FB1);
+ pcdev->fb1_active = NULL;
+ } else if (pcdev->fb2_active == buf) {
+ pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
+ writel(0, pcdev->base_csi + CSIDMASA_FB2);
+ pcdev->fb2_active = NULL;
+ }
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+ int bytesperline)
+{
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ writel((pcdev->s_width << 16) | pcdev->s_height,
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ writel(prp->cfg.src_pixel,
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+ writel(bytesperline,
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+ writel(prp->cfg.ch1_pixel,
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+ } else { /* channel 2 */
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ }
+
+ /* Enable interrupts */
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
+
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
+{
+ int dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ unsigned char *s = pcdev->resizing[dir].s;
+ int len = pcdev->resizing[dir].len;
+ unsigned int coeff[2] = {0, 0};
+ unsigned int valid = 0;
+ int i;
+
+ if (len == 0)
+ continue;
+
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (s[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (s[i] >> BC_COEF);
+ }
+
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
+
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+ valid |= PRP_RZ_VALID_BILINEAR;
+
+ if (pcdev->emma_prp->cfg.channel == 1) {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_VERT_VALID);
+ }
+ } else {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_VERT_VALID);
+ }
+ }
+ }
+}
+
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ unsigned long phys;
+ int bytesperline;
+
+ if (cpu_is_mx27()) {
+ unsigned long flags;
+ if (count < 2)
+ return -EINVAL;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, &pcdev->discard_buffer_dma,
+ GFP_KERNEL);
+ if (!pcdev->discard_buffer)
+ return -ENOMEM;
+
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
+
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
+
+ mx2_prp_resize_commit(pcdev);
+
+ mx27_camera_emma_buf_init(icd, bytesperline);
+
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH1_LEN |
+ PRP_CNTL_CH1BYP |
+ PRP_CNTL_CH1_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
+
+ return 0;
+}
+
+static int mx2_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ unsigned long flags;
+ void *b;
+ u32 cntl;
+
+ if (cpu_is_mx27()) {
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+ }
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
+ }
+
+ return 0;
+}
+
+static struct vb2_ops mx2_videobuf_ops = {
+ .queue_setup = mx2_videobuf_setup,
+ .buf_prepare = mx2_videobuf_prepare,
+ .buf_queue = mx2_videobuf_queue,
+ .buf_cleanup = mx2_videobuf_release,
+ .start_streaming = mx2_start_streaming,
+ .stop_streaming = mx2_stop_streaming,
+};
+
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx2_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx2_buffer);
+
+ return vb2_queue_init(q);
+}
+
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
+
+static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+{
+ u32 cntl;
+ int count = 0;
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ while (count++ < 100) {
+ if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+ return 0;
+ barrier();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ int ret;
+ int bytesperline;
+ u32 csicr1 = pcdev->csicr1;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, MX2_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = MX2_BUS_FLAGS;
+ }
+
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ csicr1 |= CSICR1_REDGE;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_SOF_POL;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ csicr1 |= CSICR1_HSYNC_POL;
+ if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
+ csicr1 |= CSICR1_EXT_VSYNC;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR)
+ csicr1 |= CSICR1_CCIR_EN;
+ if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
+ csicr1 |= CSICR1_CCIR_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
+ csicr1 |= CSICR1_GCLK_MODE;
+ if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
+ csicr1 |= CSICR1_INV_DATA;
+
+ pcdev->csicr1 = csicr1;
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ if (cpu_is_mx27()) {
+ ret = mx27_camera_emma_prp_reset(pcdev);
+ if (ret)
+ return ret;
+ } else if (cpu_is_mx25()) {
+ writel((bytesperline * icd->user_height) >> 2,
+ pcdev->base_csi + CSIRXCNT);
+ writel((bytesperline << 16) | icd->user_height,
+ pcdev->base_csi + CSIIMAG_PARA);
+ }
+
+ writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
+
+ return 0;
+}
+
+static int mx2_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf.width, mf.height);
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return ret;
+}
+
+static int mx2_camera_get_formats(struct soc_camera_device *icd,
+ unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_mbus_pixelfmt *fmt;
+ struct device *dev = icd->parent;
+ enum v4l2_mbus_pixelcode code;
+ int ret, formats = 0;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* no more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ /*
+ * CH2 can output YUV420 which is a standard format in
+ * soc_mediabus.c
+ */
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
+ xlate->code = code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code);
+ xlate++;
+ }
+ }
+
+ if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8);
+ xlate->code = code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code);
+ xlate++;
+ }
+ }
+
+ /* Generic pass-trough */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+ return formats;
+}
+
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+ struct v4l2_mbus_framefmt *mf_in,
+ struct v4l2_pix_format *pix_out, bool apply)
+{
+ int num, den;
+ unsigned long m;
+ int i, dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ struct emma_prp_resize tmprsz;
+ unsigned char *s = tmprsz.s;
+ int len = 0;
+ int in, out;
+
+ if (dir == RESIZE_DIR_H) {
+ in = mf_in->width;
+ out = pix_out->width;
+ } else {
+ in = mf_in->height;
+ out = pix_out->height;
+ }
+
+ if (in < out)
+ return -EINVAL;
+ else if (in == out)
+ continue;
+
+ /* Calculate ratio */
+ m = gcd(in, out);
+ num = in / m;
+ den = out / m;
+ if (num > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ if ((num >= 2 * den) && (den == 1) &&
+ (num < 9) && (!(num & 0x01))) {
+ int sum = 0;
+ int j;
+
+ /* Average scaling for >= 2:1 ratios */
+ /* Support can be added for num >=9 and odd values */
+
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
+ len = num;
+
+ for (i = 0; i < (len / 2); i++)
+ s[i] = 8;
+
+ do {
+ for (i = 0; i < (len / 2); i++) {
+ s[i] = s[i] >> 1;
+ sum = 0;
+ for (j = 0; j < (len / 2); j++)
+ sum += s[j];
+ if (sum == 4)
+ break;
+ }
+ } while (sum != 4);
+
+ for (i = (len / 2); i < len; i++)
+ s[i] = s[len - i - 1];
+
+ s[len - 1] |= SZ_COEF;
+ } else {
+ /* bilinear scaling for < 2:1 ratios */
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+ int in_pos_inc = 2 * den;
+ int out_pos = num;
+ int out_pos_inc = 2 * num;
+ int init_carry = num - den;
+ int carry = init_carry;
+
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
+ v = den + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+
+ if (len > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ coeff = ((coeff << BC_COEF) +
+ (in_pos_inc >> 1)) / in_pos_inc;
+
+ if (coeff >= (SZ_COEF - 1))
+ coeff--;
+
+ coeff |= SZ_COEF;
+ s[len] = (unsigned char)coeff;
+ len++;
+
+ for (i = 1; i < nxt; i++) {
+ if (len >= RESIZE_NUM_MAX)
+ return -EINVAL;
+ s[len] = 0;
+ len++;
+ }
+ } while (carry != init_carry);
+ }
+ tmprsz.len = len;
+ if (dir == RESIZE_DIR_H)
+ mf_in->width = pix_out->width;
+ else
+ mf_in->height = pix_out->height;
+
+ if (apply)
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+ }
+ return 0;
+}
+
+static int mx2_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* Store width and height returned by the sensor for resizing */
+ pcdev->s_width = mf.width;
+ pcdev->s_height = mf.height;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ unsigned int width_limit;
+ int ret;
+
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* FIXME: implement MX27 limits */
+
+ /* limit to MX25 hardware capabilities */
+ if (cpu_is_mx25()) {
+ if (xlate->host_fmt->bits_per_sample <= 8)
+ width_limit = 0xffff * 4;
+ else
+ width_limit = 0xffff * 2;
+ /* CSIIMAG_PARA limit */
+ if (pix->width > width_limit)
+ pix->width = width_limit;
+ if (pix->height > 0xffff)
+ pix->height = 0xffff;
+
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ if (pix->bytesperline < 0)
+ return pix->bytesperline;
+ pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+ pix->bytesperline, pix->height);
+ /* Check against the CSIRXCNT limit */
+ if (pix->sizeimage > 4 * 0x3ffff) {
+ /* Adjust geometry, preserve aspect ratio */
+ unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
+ 4 * pix->height, pix->bytesperline));
+ pix->width = new_height * pix->width / pix->height;
+ pix->height = new_height;
+ pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
+ xlate->host_fmt);
+ BUG_ON(pix->bytesperline < 0);
+ pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+ pix->bytesperline, pix->height);
+ }
+ }
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ /* If the sensor does not support image size try PrP resizing */
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
+ if (mf.field == V4L2_FIELD_ANY)
+ mf.field = V4L2_FIELD_NONE;
+ /*
+ * Driver supports interlaced images provided they have
+ * both fields so that they can be processed as if they
+ * were progressive.
+ */
+ if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) {
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ return -EINVAL;
+ }
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
+ return 0;
+}
+
+static int mx2_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx2_camera_add_device,
+ .remove = mx2_camera_remove_device,
+ .set_fmt = mx2_camera_set_fmt,
+ .set_crop = mx2_camera_set_crop,
+ .get_formats = mx2_camera_get_formats,
+ .try_fmt = mx2_camera_try_fmt,
+ .init_videobuf2 = mx2_camera_init_videobuf,
+ .poll = mx2_camera_poll,
+ .querycap = mx2_camera_querycap,
+ .set_bus_param = mx2_camera_set_bus_param,
+};
+
+static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
+ int bufnum, bool err)
+{
+#ifdef DEBUG
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+#endif
+ struct mx2_buf_internal *ibuf;
+ struct mx2_buffer *buf;
+ struct vb2_buffer *vb;
+ unsigned long phys;
+
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+ queue);
+
+ BUG_ON(ibuf->bufnum != bufnum);
+
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+ } else {
+ buf = mx2_ibuf_to_buf(ibuf);
+
+ vb = &buf->vb;
+#ifdef DEBUG
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (prp->cfg.channel == 1) {
+ if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
+ 4 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
+ }
+ } else {
+ if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
+ 0x14 * bufnum) != phys) {
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
+ }
+ }
+#endif
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
+
+ list_del_init(&buf->internal.queue);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence = pcdev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+
+ pcdev->frame_count++;
+
+ if (list_empty(&pcdev->capture)) {
+ if (list_empty(&pcdev->discard)) {
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+ __func__);
+ return;
+ }
+
+ ibuf = list_first_entry(&pcdev->discard,
+ struct mx2_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
+ return;
+ }
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+
+ buf->internal.bufnum = bufnum;
+
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, bufnum);
+}
+
+static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
+{
+ struct mx2_camera_dev *pcdev = data;
+ unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ struct mx2_buf_internal *ibuf;
+
+ spin_lock(&pcdev->lock);
+
+ if (list_empty(&pcdev->active_bufs)) {
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+ __func__);
+
+ if (!status) {
+ spin_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+ }
+
+ if (status & (1 << 7)) { /* overflow */
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
+ pcdev->base_emma + PRP_CNTL);
+ writel(cntl, pcdev->base_emma + PRP_CNTL);
+
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev,
+ ibuf->bufnum, true);
+
+ status &= ~(1 << 7);
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
+ ((status & (3 << 3)) == (3 << 3))) {
+ /*
+ * Both buffers have triggered, process the one we're expecting
+ * to first
+ */
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+ mx27_camera_frame_done_emma(pcdev, 0, false);
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+ mx27_camera_frame_done_emma(pcdev, 1, false);
+ }
+
+ spin_unlock(&pcdev->lock);
+ writel(status, pcdev->base_emma + PRP_INTRSTATUS);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mx27_camera_emma_init(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev = platform_get_drvdata(pdev);
+ struct resource *res_emma;
+ int irq_emma;
+ int err = 0;
+
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq_emma = platform_get_irq(pdev, 1);
+ if (!res_emma || !irq_emma) {
+ dev_err(pcdev->dev, "no EMMA resources\n");
+ goto out;
+ }
+
+ pcdev->base_emma = devm_request_and_ioremap(pcdev->dev, res_emma);
+ if (!pcdev->base_emma) {
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ err = devm_request_irq(pcdev->dev, irq_emma, mx27_camera_emma_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
+ goto out;
+ }
+
+ pcdev->clk_emma_ipg = devm_clk_get(pcdev->dev, "emma-ipg");
+ if (IS_ERR(pcdev->clk_emma_ipg)) {
+ err = PTR_ERR(pcdev->clk_emma_ipg);
+ goto out;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ipg);
+
+ pcdev->clk_emma_ahb = devm_clk_get(pcdev->dev, "emma-ahb");
+ if (IS_ERR(pcdev->clk_emma_ahb)) {
+ err = PTR_ERR(pcdev->clk_emma_ahb);
+ goto exit_clk_emma_ipg;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ahb);
+
+ err = mx27_camera_emma_prp_reset(pcdev);
+ if (err)
+ goto exit_clk_emma_ahb;
+
+ return err;
+
+exit_clk_emma_ahb:
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+exit_clk_emma_ipg:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+out:
+ return err;
+}
+
+static int __devinit mx2_camera_probe(struct platform_device *pdev)
+{
+ struct mx2_camera_dev *pcdev;
+ struct resource *res_csi;
+ int irq_csi;
+ int err = 0;
+
+ dev_dbg(&pdev->dev, "initialising\n");
+
+ res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_csi = platform_get_irq(pdev, 0);
+ if (res_csi == NULL || irq_csi < 0) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pcdev->clk_csi = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(pcdev->clk_csi)) {
+ dev_err(&pdev->dev, "Could not get csi clock\n");
+ err = PTR_ERR(pcdev->clk_csi);
+ goto exit;
+ }
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ long rate;
+
+ pcdev->platform_flags = pcdev->pdata->flags;
+
+ rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2);
+ if (rate <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+ err = clk_set_rate(pcdev->clk_csi, rate);
+ if (err < 0)
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
+ spin_lock_init(&pcdev->lock);
+
+ pcdev->base_csi = devm_request_and_ioremap(&pdev->dev, res_csi);
+ if (!pcdev->base_csi) {
+ err = -EADDRNOTAVAIL;
+ goto exit;
+ }
+
+ pcdev->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcdev);
+
+ if (cpu_is_mx25()) {
+ err = devm_request_irq(&pdev->dev, irq_csi, mx25_camera_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera interrupt register failed \n");
+ goto exit;
+ }
+ }
+
+ if (cpu_is_mx27()) {
+ err = mx27_camera_emma_init(pdev);
+ if (err)
+ goto exit;
+ }
+
+ /*
+ * We're done with drvdata here. Clear the pointer so that
+ * v4l2 core can start using drvdata on its purpose.
+ */
+ platform_set_drvdata(pdev, NULL);
+
+ pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
+ pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+ if (cpu_is_mx25())
+ pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto eallocctx;
+ }
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_emma;
+
+ dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
+ clk_get_rate(pcdev->clk_csi));
+
+ return 0;
+
+exit_free_emma:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+eallocctx:
+ if (cpu_is_mx27()) {
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ }
+exit:
+ return err;
+}
+
+static int __devexit mx2_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx2_camera_dev *pcdev = container_of(soc_host,
+ struct mx2_camera_dev, soc_host);
+
+ soc_camera_host_unregister(&pcdev->soc_host);
+
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+
+ if (cpu_is_mx27()) {
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ }
+
+ dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver mx2_camera_driver = {
+ .driver = {
+ .name = MX2_CAM_DRV_NAME,
+ },
+ .remove = __devexit_p(mx2_camera_remove),
+};
+
+
+static int __init mx2_camera_init(void)
+{
+ return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
+}
+
+static void __exit mx2_camera_exit(void)
+{
+ return platform_driver_unregister(&mx2_camera_driver);
+}
+
+module_init(mx2_camera_init);
+module_exit(mx2_camera_exit);
+
+MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
+MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
new file mode 100644
index 000000000000..16975c6e6905
--- /dev/null
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -0,0 +1,1290 @@
+/*
+ * V4L2 Driver for i.MX3x camera host
+ *
+ * Copyright (C) 2008
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * 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/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+
+#include <mach/ipu.h>
+#include <mach/mx3_camera.h>
+#include <mach/dma.h>
+
+#define MX3_CAM_DRV_NAME "mx3-camera"
+
+/* CMOS Sensor Interface Registers */
+#define CSI_REG_START 0x60
+
+#define CSI_SENS_CONF (0x60 - CSI_REG_START)
+#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
+#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
+#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
+#define CSI_TST_CTRL (0x70 - CSI_REG_START)
+#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
+#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
+#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
+#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
+#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
+
+#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
+#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
+#define CSI_SENS_CONF_DATA_POL_SHIFT 2
+#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
+#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
+#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
+#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
+#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
+#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
+#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
+
+#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
+
+#define MAX_VIDEO_MEM 16
+
+struct mx3_camera_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head queue;
+
+ /* One descriptot per scatterlist (per frame) */
+ struct dma_async_tx_descriptor *txd;
+
+ /* We have to "build" a scatterlist ourselves - one element per frame */
+ struct scatterlist sg;
+};
+
+/**
+ * struct mx3_camera_dev - i.MX3x camera (CSI) object
+ * @dev: camera device, to which the coherent buffer is attached
+ * @icd: currently attached camera sensor
+ * @clk: pointer to clock
+ * @base: remapped register base address
+ * @pdata: platform data
+ * @platform_flags: platform flags
+ * @mclk: master clock frequency in Hz
+ * @capture: list of capture videobuffers
+ * @lock: protects video buffer lists
+ * @active: active video buffer
+ * @idmac_channel: array of pointers to IPU DMAC DMA channels
+ * @soc_host: embedded soc_host object
+ */
+struct mx3_camera_dev {
+ /*
+ * i.MX3x is only supposed to handle one camera on its Camera Sensor
+ * Interface. If anyone ever builds hardware to enable more than one
+ * camera _simultaneously_, they will have to modify this driver too
+ */
+ struct soc_camera_device *icd;
+ struct clk *clk;
+
+ void __iomem *base;
+
+ struct mx3_camera_pdata *pdata;
+
+ unsigned long platform_flags;
+ unsigned long mclk;
+ u16 width_flags; /* max 15 bits */
+
+ struct list_head capture;
+ spinlock_t lock; /* Protects video buffer lists */
+ struct mx3_camera_buffer *active;
+ size_t buf_total;
+ 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 */
+
+ struct soc_camera_host soc_host;
+};
+
+struct dma_chan_request {
+ struct mx3_camera_dev *mx3_cam;
+ enum ipu_channel id;
+};
+
+static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
+{
+ return __raw_readl(mx3->base + reg);
+}
+
+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)
+{
+ struct idmac_tx_desc *desc = to_tx_desc(arg);
+ struct dma_chan *chan = desc->txd.chan;
+ struct idmac_channel *ichannel = to_idmac_chan(chan);
+ struct mx3_camera_dev *mx3_cam = ichannel->client;
+
+ 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) {
+ 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)) {
+ mx3_cam->active = NULL;
+ spin_unlock(&mx3_cam->lock);
+
+ /*
+ * stop capture - without further buffers IPU_CHA_BUF0_RDY will
+ * not get updated
+ */
+ return;
+ }
+
+ mx3_cam->active = list_entry(mx3_cam->capture.next,
+ struct mx3_camera_buffer, queue);
+ spin_unlock(&mx3_cam->lock);
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * Calculate the __buffer__ (not data) size and number of buffers.
+ */
+static int mx3_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ if (!mx3_cam->idmac_channel[0])
+ return -EINVAL;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ unsigned int bytes_per_line;
+ int ret;
+
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ sizes[0] = icd->sizeimage;
+ }
+
+ alloc_ctxs[0] = mx3_cam->alloc_ctx;
+
+ if (!vq->num_buffers)
+ mx3_cam->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (!*num_planes &&
+ sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
+ sizes[0];
+
+ *num_planes = 1;
+
+ 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_RGB24:
+ return IPU_PIX_FMT_RGB24;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_RGB565:
+ default:
+ return IPU_PIX_FMT_GENERIC;
+ }
+}
+
+static void mx3_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ struct scatterlist *sg = &buf->sg;
+ struct dma_async_tx_descriptor *txd;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct idmac_video_param *video = &ichan->params.video;
+ const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
+ unsigned long flags;
+ dma_cookie_t cookie;
+ size_t new_size;
+
+ new_size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < new_size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
+ goto error;
+ }
+
+ if (!buf->txd) {
+ sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
+ sg_dma_len(sg) = new_size;
+
+ txd = dmaengine_prep_slave_sg(
+ &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!txd)
+ goto error;
+
+ txd->callback_param = txd;
+ txd->callback = mx3_cam_dma_done;
+
+ buf->txd = txd;
+ } else {
+ txd = buf->txd;
+ }
+
+ vb2_set_plane_payload(vb, 0, new_size);
+
+ /* This is the configuration of one sg-element */
+ video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ /*
+ * If the IPU DMA channel is configured to transfer generic
+ * 8-bit data, we have to set up the geometry parameters
+ * correctly, according to the current pixel format. The DMA
+ * horizontal parameters in this case are expressed in bytes,
+ * not in pixels.
+ */
+ video->out_width = icd->bytesperline;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->bytesperline;
+ } 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 */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+ list_add_tail(&buf->queue, &mx3_cam->capture);
+
+ if (!mx3_cam->active)
+ mx3_cam->active = buf;
+
+ spin_unlock_irq(&mx3_cam->lock);
+
+ cookie = txd->tx_submit(txd);
+ dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
+ cookie, sg_dma_address(&buf->sg));
+
+ if (cookie >= 0)
+ return;
+
+ spin_lock_irq(&mx3_cam->lock);
+
+ /* Submit error */
+ list_del_init(&buf->queue);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void mx3_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+ struct dma_async_tx_descriptor *txd = buf->txd;
+ unsigned long flags;
+
+ dev_dbg(icd->parent,
+ "Release%s DMA 0x%08x, queue %sempty\n",
+ mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
+ list_empty(&buf->queue) ? "" : "not ");
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ if (mx3_cam->active == buf)
+ mx3_cam->active = NULL;
+
+ /* Doesn't hurt also if the list is empty */
+ list_del_init(&buf->queue);
+
+ if (txd) {
+ buf->txd = NULL;
+ if (mx3_cam->idmac_channel[0])
+ async_tx_ack(txd);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ mx3_cam->buf_total -= vb2_plane_size(vb, 0);
+}
+
+static int mx3_videobuf_init(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct mx3_camera_buffer *buf = to_mx3_vb(vb);
+
+ if (!buf->txd) {
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
+
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
+ }
+
+ return 0;
+}
+
+static int mx3_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
+ struct mx3_camera_buffer *buf, *tmp;
+ unsigned long flags;
+
+ if (ichan) {
+ struct dma_chan *chan = &ichan->dma_chan;
+ chan->device->device_control(chan, DMA_PAUSE, 0);
+ }
+
+ spin_lock_irqsave(&mx3_cam->lock, flags);
+
+ mx3_cam->active = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
+ list_del_init(&buf->queue);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&mx3_cam->lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops mx3_videobuf_ops = {
+ .queue_setup = mx3_videobuf_setup,
+ .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,
+ .stop_streaming = mx3_stop_streaming,
+};
+
+static int mx3_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ 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() */
+static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
+ struct soc_camera_device *icd)
+{
+ u32 conf;
+ long rate;
+
+ /* Set default size: ipu_csi_set_window_size() */
+ csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
+ /* ...and position to 0:0: ipu_csi_set_window_pos() */
+ conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
+
+ /* We use only gated clock synchronisation mode so far */
+ conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
+
+ /* Set generic data, platform-biggest bus-width */
+ conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
+ conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+
+ if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
+ conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
+ conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+
+ /* ipu_csi_init_interface() */
+ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
+
+ clk_prepare_enable(mx3_cam->clk);
+ rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
+ dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ if (rate)
+ clk_set_rate(mx3_cam->clk, rate);
+}
+
+/* Called with .video_lock held */
+static int mx3_camera_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+
+ if (mx3_cam->icd)
+ return -EBUSY;
+
+ mx3_camera_activate(mx3_cam, icd);
+
+ mx3_cam->buf_total = 0;
+ mx3_cam->icd = icd;
+
+ dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+/* Called with .video_lock held */
+static void mx3_camera_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+
+ BUG_ON(icd != mx3_cam->icd);
+
+ if (*ichan) {
+ dma_release_channel(&(*ichan)->dma_chan);
+ *ichan = NULL;
+ }
+
+ clk_disable_unprepare(mx3_cam->clk);
+
+ mx3_cam->icd = NULL;
+
+ dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
+ icd->devnum);
+}
+
+static int test_platform_param(struct mx3_camera_dev *mx3_cam,
+ unsigned char buswidth, unsigned long *flags)
+{
+ /*
+ * If requested data width is supported by the platform, use it or any
+ * possible lower value - i.MX31 is smart enough to shift bits
+ */
+ if (buswidth > fls(mx3_cam->width_flags))
+ return -EINVAL;
+
+ /*
+ * Platform specified synchronization and pixel clock polarities are
+ * only a recommendation and are only used during probing. MX3x
+ * camera interface only works in master mode, i.e., uses HSYNC and
+ * VSYNC signals from the sensor
+ */
+ *flags = V4L2_MBUS_MASTER |
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_HSYNC_ACTIVE_LOW |
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH |
+ V4L2_MBUS_VSYNC_ACTIVE_LOW |
+ V4L2_MBUS_PCLK_SAMPLE_RISING |
+ V4L2_MBUS_PCLK_SAMPLE_FALLING |
+ V4L2_MBUS_DATA_ACTIVE_HIGH |
+ V4L2_MBUS_DATA_ACTIVE_LOW;
+
+ return 0;
+}
+
+static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
+ const unsigned int depth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long bus_flags, common_flags;
+ int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+
+ dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ struct dma_chan_request *rq = arg;
+ struct mx3_camera_pdata *pdata;
+
+ if (!imx_dma_is_ipu(chan))
+ return false;
+
+ if (!rq)
+ return false;
+
+ pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
+
+ return rq->id == chan->chan_id &&
+ pdata->dma_dev == chan->device->dev;
+}
+
+static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer BGGR (sRGB) 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ }, {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .name = "Monochrome 8 bit",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(icd->parent,
+ "Unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ /* This also checks support for the requested bits-per-sample */
+ ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[0];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[0].name, code);
+ }
+ break;
+ case V4L2_MBUS_FMT_Y10_1X10:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = &mx3_camera_formats[1];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ mx3_camera_formats[1].name, code);
+ }
+ break;
+ default:
+ if (!mx3_camera_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ 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++;
+ }
+
+ return formats;
+}
+
+static void configure_geometry(struct mx3_camera_dev *mx3_cam,
+ unsigned int width, unsigned int height,
+ const struct soc_mbus_pixelfmt *fmt)
+{
+ u32 ctrl, width_field, height_field;
+
+ 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.
+ */
+ unsigned int num, den;
+ int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
+ BUG_ON(ret < 0);
+ width = width * num / den;
+ }
+
+ /* Setup frame size - this cannot be changed on-the-fly... */
+ width_field = width - 1;
+ height_field = height - 1;
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
+
+ csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
+ csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
+
+ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
+
+ /* ...and position */
+ ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
+ /* Sensor does the cropping */
+ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
+}
+
+static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
+ /* We have to use IDMAC_IC_7 for Bayer / generic data */
+ struct dma_chan_request rq = {.mx3_cam = mx3_cam,
+ .id = IDMAC_IC_7};
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, &rq);
+ if (!chan)
+ return -EBUSY;
+
+ *ichan = to_idmac_chan(chan);
+ (*ichan)->client = mx3_cam;
+
+ return 0;
+}
+
+/*
+ * FIXME: learn to use stride != width, then we can keep stride properly aligned
+ * and support arbitrary (even) widths.
+ */
+static inline void stride_align(__u32 *width)
+{
+ if (ALIGN(*width, 8) < 4096)
+ *width = ALIGN(*width, 8);
+ else
+ *width = *width & ~7;
+}
+
+/*
+ * As long as we don't implement host-side cropping and scaling, we can use
+ * default g_crop and cropcap from soc_camera.c
+ */
+static int mx3_camera_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output sizes */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != icd->current_fmt->code)
+ return -EINVAL;
+
+ if (mf.width & 7) {
+ /* Ouch! We can only handle 8-byte aligned width... */
+ stride_align(&mf.width);
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mf.width != icd->user_width || mf.height != icd->user_height)
+ configure_geometry(mx3_cam, mf.width, mf.height,
+ icd->current_fmt->host_fmt);
+
+ dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
+ mf.width, mf.height);
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return ret;
+}
+
+static int mx3_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ stride_align(&pix->width);
+ dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
+
+ /*
+ * 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, xlate->host_fmt);
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ 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;
+
+ dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
+
+ return ret;
+}
+
+static int mx3_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (pixfmt && !xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* limit to MX3 hardware capabilities */
+ if (pix->height > 4096)
+ pix->height = 4096;
+ if (pix->width > 4096)
+ pix->width = 4096;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->colorspace = mf.colorspace;
+
+ switch (mf.field) {
+ case V4L2_FIELD_ANY:
+ pix->field = V4L2_FIELD_NONE;
+ break;
+ case V4L2_FIELD_NONE:
+ break;
+ default:
+ dev_err(icd->parent, "Field type %d unsupported.\n",
+ mf.field);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int mx3_camera_reqbufs(struct soc_camera_device *icd,
+ struct v4l2_requestbuffers *p)
+{
+ return 0;
+}
+
+static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int mx3_camera_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the firendly caller:-> */
+ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ unsigned long bus_flags, common_flags;
+ u32 dw, sens_conf;
+ const struct soc_mbus_pixelfmt *fmt;
+ int buswidth;
+ int ret;
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+
+ fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ buswidth = fmt->bits_per_sample;
+ ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
+
+ dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
+
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ bus_flags);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "Flags incompatible: camera 0x%x, host 0x%lx\n",
+ cfg.flags, bus_flags);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = bus_flags;
+ }
+
+ dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
+ cfg.flags, bus_flags, common_flags);
+
+ /* Make choices, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ /*
+ * So far only gated clock mode is supported. Add a line
+ * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
+ * below and select the required mode when supporting other
+ * synchronisation protocols.
+ */
+ sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
+ ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
+ (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
+ (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
+
+ /* TODO: Support RGB and YUV formats */
+
+ /* This has been set in mx3_camera_activate(), but we clear it above */
+ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
+ if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
+ sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
+
+ /* Just do what we're asked to do */
+ switch (xlate->host_fmt->bits_per_sample) {
+ case 4:
+ dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 8:
+ dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ case 10:
+ dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ break;
+ default:
+ /*
+ * Actually it can only be 15 now, default is just to silence
+ * compiler warnings
+ */
+ case 15:
+ dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
+ }
+
+ csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
+
+ dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+
+ return 0;
+}
+
+static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
+ .owner = THIS_MODULE,
+ .add = mx3_camera_add_device,
+ .remove = mx3_camera_remove_device,
+ .set_crop = mx3_camera_set_crop,
+ .set_fmt = mx3_camera_set_fmt,
+ .try_fmt = mx3_camera_try_fmt,
+ .get_formats = mx3_camera_get_formats,
+ .init_videobuf2 = mx3_camera_init_videobuf,
+ .reqbufs = mx3_camera_reqbufs,
+ .poll = mx3_camera_poll,
+ .querycap = mx3_camera_querycap,
+ .set_bus_param = mx3_camera_set_bus_param,
+};
+
+static int __devinit mx3_camera_probe(struct platform_device *pdev)
+{
+ struct mx3_camera_dev *mx3_cam;
+ struct resource *res;
+ void __iomem *base;
+ int err = 0;
+ struct soc_camera_host *soc_host;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto egetres;
+ }
+
+ mx3_cam = vzalloc(sizeof(*mx3_cam));
+ if (!mx3_cam) {
+ dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
+ err = -ENOMEM;
+ goto ealloc;
+ }
+
+ mx3_cam->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk)) {
+ err = PTR_ERR(mx3_cam->clk);
+ goto eclkget;
+ }
+
+ mx3_cam->pdata = pdev->dev.platform_data;
+ mx3_cam->platform_flags = mx3_cam->pdata->flags;
+ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
+ /*
+ * Platform hasn't set available data widths. This is bad.
+ * Warn and use a default.
+ */
+ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
+ "data widths, using default 8 bit\n");
+ mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
+ }
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
+ mx3_cam->width_flags = 1 << 3;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
+ mx3_cam->width_flags |= 1 << 7;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
+ mx3_cam->width_flags |= 1 << 9;
+ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
+ mx3_cam->width_flags |= 1 << 14;
+
+ mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+ if (!mx3_cam->mclk) {
+ dev_warn(&pdev->dev,
+ "mclk_10khz == 0! Please, fix your platform data. "
+ "Using default 20MHz\n");
+ mx3_cam->mclk = 20000000;
+ }
+
+ /* list of video-buffers */
+ INIT_LIST_HEAD(&mx3_cam->capture);
+ spin_lock_init(&mx3_cam->lock);
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
+ err = -ENOMEM;
+ goto eioremap;
+ }
+
+ mx3_cam->base = base;
+
+ soc_host = &mx3_cam->soc_host;
+ soc_host->drv_name = MX3_CAM_DRV_NAME;
+ soc_host->ops = &mx3_soc_camera_host_ops;
+ soc_host->priv = mx3_cam;
+ 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;
+
+ /* IDMAC interface */
+ dmaengine_get();
+
+ return 0;
+
+ecamhostreg:
+ vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
+eallocctx:
+ iounmap(base);
+eioremap:
+ clk_put(mx3_cam->clk);
+eclkget:
+ vfree(mx3_cam);
+ealloc:
+egetres:
+ return err;
+}
+
+static int __devexit mx3_camera_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct mx3_camera_dev *mx3_cam = container_of(soc_host,
+ struct mx3_camera_dev, soc_host);
+
+ clk_put(mx3_cam->clk);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(mx3_cam->base);
+
+ /*
+ * The channel has either not been allocated,
+ * or should have been released
+ */
+ 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();
+
+ return 0;
+}
+
+static struct platform_driver mx3_camera_driver = {
+ .driver = {
+ .name = MX3_CAM_DRV_NAME,
+ },
+ .probe = mx3_camera_probe,
+ .remove = __devexit_p(mx3_camera_remove),
+};
+
+module_platform_driver(mx3_camera_driver);
+
+MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.2.3");
+MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c
new file mode 100644
index 000000000000..fa08c7695ccb
--- /dev/null
+++ b/drivers/media/platform/soc_camera/omap1_camera.c
@@ -0,0 +1,1723 @@
+/*
+ * V4L2 SoC Camera driver for OMAP1 Camera Interface
+ *
+ * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
+ * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
+ *
+ * Based on PXA SoC camera driver
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * Hardware specific bits initialy based on former work by Matt Callow
+ * drivers/media/platform/omap/omap1510cam.c
+ * Copyright (C) 2006 Matt Callow
+ *
+ * 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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/omap1_camera.h>
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/videobuf-dma-contig.h>
+#include <media/videobuf-dma-sg.h>
+
+#include <plat/dma.h>
+
+
+#define DRIVER_NAME "omap1-camera"
+#define DRIVER_VERSION "0.0.2"
+
+
+/*
+ * ---------------------------------------------------------------------------
+ * OMAP1 Camera Interface registers
+ * ---------------------------------------------------------------------------
+ */
+
+#define REG_CTRLCLOCK 0x00
+#define REG_IT_STATUS 0x04
+#define REG_MODE 0x08
+#define REG_STATUS 0x0C
+#define REG_CAMDATA 0x10
+#define REG_GPIO 0x14
+#define REG_PEAK_COUNTER 0x18
+
+/* CTRLCLOCK bit shifts */
+#define LCLK_EN BIT(7)
+#define DPLL_EN BIT(6)
+#define MCLK_EN BIT(5)
+#define CAMEXCLK_EN BIT(4)
+#define POLCLK BIT(3)
+#define FOSCMOD_SHIFT 0
+#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
+#define FOSCMOD_12MHz 0x0
+#define FOSCMOD_6MHz 0x2
+#define FOSCMOD_9_6MHz 0x4
+#define FOSCMOD_24MHz 0x5
+#define FOSCMOD_8MHz 0x6
+
+/* IT_STATUS bit shifts */
+#define DATA_TRANSFER BIT(5)
+#define FIFO_FULL BIT(4)
+#define H_DOWN BIT(3)
+#define H_UP BIT(2)
+#define V_DOWN BIT(1)
+#define V_UP BIT(0)
+
+/* MODE bit shifts */
+#define RAZ_FIFO BIT(18)
+#define EN_FIFO_FULL BIT(17)
+#define EN_NIRQ BIT(16)
+#define THRESHOLD_SHIFT 9
+#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
+#define DMA BIT(8)
+#define EN_H_DOWN BIT(7)
+#define EN_H_UP BIT(6)
+#define EN_V_DOWN BIT(5)
+#define EN_V_UP BIT(4)
+#define ORDERCAMD BIT(3)
+
+#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
+ EN_NIRQ | EN_FIFO_FULL)
+
+/* STATUS bit shifts */
+#define HSTATUS BIT(1)
+#define VSTATUS BIT(0)
+
+/* GPIO bit shifts */
+#define CAM_RST BIT(0)
+
+/* end of OMAP1 Camera Interface registers */
+
+
+#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+
+#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
+#define FIFO_SHIFT __fls(FIFO_SIZE)
+
+#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
+#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
+
+#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
+#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
+
+#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
+#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
+
+#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
+ DMA_FRAME_SHIFT_CONTIG : \
+ DMA_FRAME_SHIFT_SG)
+#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
+#define DMA_SYNC OMAP_DMA_SYNC_FRAME
+#define THRESHOLD_LEVEL DMA_FRAME_SIZE
+
+
+#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
+
+
+/*
+ * Structures
+ */
+
+/* buffer for one video frame */
+struct omap1_cam_buf {
+ struct videobuf_buffer vb;
+ enum v4l2_mbus_pixelcode code;
+ int inwork;
+ struct scatterlist *sgbuf;
+ int sgcount;
+ int bytes_left;
+ enum videobuf_state result;
+};
+
+struct omap1_cam_dev {
+ struct soc_camera_host soc_host;
+ struct soc_camera_device *icd;
+ struct clk *clk;
+
+ unsigned int irq;
+ void __iomem *base;
+
+ int dma_ch;
+
+ struct omap1_cam_platform_data *pdata;
+ struct resource *res;
+ unsigned long pflags;
+ unsigned long camexclk;
+
+ struct list_head capture;
+
+ /* lock used to protect videobuf */
+ spinlock_t lock;
+
+ /* Pointers to DMA buffers */
+ struct omap1_cam_buf *active;
+ struct omap1_cam_buf *ready;
+
+ enum omap1_cam_vb_mode vb_mode;
+ int (*mmap_mapper)(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma);
+
+ u32 reg_cache[0];
+};
+
+
+static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
+{
+ pcdev->reg_cache[reg / sizeof(u32)] = val;
+ __raw_writel(val, pcdev->base + reg);
+}
+
+static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
+{
+ return !from_cache ? __raw_readl(pcdev->base + reg) :
+ pcdev->reg_cache[reg / sizeof(u32)];
+}
+
+#define CAM_READ(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, false)
+#define CAM_WRITE(pcdev, reg, val) \
+ cam_write(pcdev, REG_##reg, val)
+#define CAM_READ_CACHE(pcdev, reg) \
+ cam_read(pcdev, REG_##reg, true)
+
+/*
+ * Videobuf operations
+ */
+static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ *size = icd->sizeimage;
+
+ if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
+ *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
+
+ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+
+ dev_dbg(icd->parent,
+ "%s: count=%d, size=%d\n", __func__, *count, *size);
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ struct videobuf_buffer *vb = &buf->vb;
+
+ BUG_ON(in_interrupt());
+
+ videobuf_waiton(vq, vb, 0, 0);
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ videobuf_dma_contig_free(vq, vb);
+ } else {
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ videobuf_dma_unmap(dev, dma);
+ videobuf_dma_free(dma);
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int omap1_videobuf_prepare(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ WARN_ON(!list_empty(&vb->queue));
+
+ BUG_ON(NULL == icd->current_fmt);
+
+ buf->inwork = 1;
+
+ if (buf->code != icd->current_fmt->code || vb->field != field ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height) {
+ buf->code = icd->current_fmt->code;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
+ vb->field = field;
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ vb->size = icd->sizeimage;
+
+ if (vb->baddr && vb->bsize < vb->size) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+
+ vb->state = VIDEOBUF_PREPARED;
+ }
+ buf->inwork = 0;
+
+ return 0;
+fail:
+ free_buffer(vq, buf, pcdev->vb_mode);
+out:
+ buf->inwork = 0;
+ return ret;
+}
+
+static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ dma_addr_t dma_addr;
+ unsigned int block_size;
+
+ if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ dma_addr = videobuf_to_dma_contig(&buf->vb);
+ block_size = buf->vb.size;
+ } else {
+ if (WARN_ON(!buf->sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ dma_addr = sg_dma_address(buf->sgbuf);
+ if (WARN_ON(!dma_addr)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ block_size = sg_dma_len(buf->sgbuf);
+ if (WARN_ON(!block_size)) {
+ buf->sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ return;
+ }
+ if (unlikely(buf->bytes_left < block_size))
+ block_size = buf->bytes_left;
+ if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1))) {
+ dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE);
+ block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
+ DMA_ELEMENT_SIZE - 1);
+ }
+ buf->bytes_left -= block_size;
+ buf->sgcount++;
+ }
+
+ omap_set_dma_dest_params(dma_ch,
+ OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
+ omap_set_dma_transfer_params(dma_ch,
+ OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
+ block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
+ DMA_SYNC, 0, 0);
+}
+
+static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf;
+
+ /*
+ * If there is already a buffer pointed out by the pcdev->ready,
+ * (re)use it, otherwise try to fetch and configure a new one.
+ */
+ buf = pcdev->ready;
+ if (!buf) {
+ if (list_empty(&pcdev->capture))
+ return buf;
+ buf = list_entry(pcdev->capture.next,
+ struct omap1_cam_buf, vb.queue);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ pcdev->ready = buf;
+ list_del_init(&buf->vb.queue);
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we can safely enter next buffer parameters
+ * into the DMA programming register set after the DMA
+ * has already been activated on the previous buffer
+ */
+ set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
+ } else {
+ /*
+ * In SG mode, the above is not safe since there are probably
+ * a bunch of sgbufs from previous sglist still pending.
+ * Instead, mark the sglist fresh for the upcoming
+ * try_next_sgbuf().
+ */
+ buf->sgbuf = NULL;
+ }
+
+ return buf;
+}
+
+static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
+{
+ struct scatterlist *sgbuf;
+
+ if (likely(buf->sgbuf)) {
+ /* current sglist is active */
+ if (unlikely(!buf->bytes_left)) {
+ /* indicate sglist complete */
+ sgbuf = NULL;
+ } else {
+ /* process next sgbuf */
+ sgbuf = sg_next(buf->sgbuf);
+ if (WARN_ON(!sgbuf)) {
+ buf->result = VIDEOBUF_ERROR;
+ } else if (WARN_ON(!sg_dma_len(sgbuf))) {
+ sgbuf = NULL;
+ buf->result = VIDEOBUF_ERROR;
+ }
+ }
+ buf->sgbuf = sgbuf;
+ } else {
+ /* sglist is fresh, initialize it before using */
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ sgbuf = dma->sglist;
+ if (!(WARN_ON(!sgbuf))) {
+ buf->sgbuf = sgbuf;
+ buf->sgcount = 0;
+ buf->bytes_left = buf->vb.size;
+ buf->result = VIDEOBUF_DONE;
+ }
+ }
+ if (sgbuf)
+ /*
+ * Put our next sgbuf parameters (address, size)
+ * into the DMA programming register set.
+ */
+ set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
+
+ return sgbuf;
+}
+
+static void start_capture(struct omap1_cam_dev *pcdev)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
+
+ if (WARN_ON(!buf))
+ return;
+
+ /*
+ * Enable start of frame interrupt, which we will use for activating
+ * our end of frame watchdog when capture actually starts.
+ */
+ mode |= EN_V_UP;
+
+ if (unlikely(ctrlclock & LCLK_EN))
+ /* stop pixel clock before FIFO reset */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ /* reset FIFO */
+ CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
+
+ omap_start_dma(pcdev->dma_ch);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, it's a good moment for fetching next sgbuf
+ * from the current sglist and, if available, already putting
+ * its parameters into the DMA programming register set.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ /* (re)enable pixel clock */
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
+ /* release FIFO reset */
+ CAM_WRITE(pcdev, MODE, mode);
+}
+
+static void suspend_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+ omap_stop_dma(pcdev->dma_ch);
+}
+
+static void disable_capture(struct omap1_cam_dev *pcdev)
+{
+ u32 mode = CAM_READ_CACHE(pcdev, MODE);
+
+ CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
+}
+
+static void omap1_videobuf_queue(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct omap1_cam_buf *buf;
+ u32 mode;
+
+ list_add_tail(&vb->queue, &pcdev->capture);
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (pcdev->active) {
+ /*
+ * Capture in progress, so don't touch pcdev->ready even if
+ * empty. Since the transfer of the DMA programming register set
+ * content to the DMA working register set is done automatically
+ * by the DMA hardware, this can pretty well happen while we
+ * are keeping the lock here. Leave fetching it from the queue
+ * to be done when a next DMA interrupt occures instead.
+ */
+ return;
+ }
+
+ WARN_ON(pcdev->ready);
+
+ buf = prepare_next_vb(pcdev);
+ if (WARN_ON(!buf))
+ return;
+
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ dev_dbg(icd->parent,
+ "%s: capture not active, setup FIFO, start DMA\n", __func__);
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
+ mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
+ CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, the above prepare_next_vb() didn't actually
+ * put anything into the DMA programming register set,
+ * so we have to do it now, before activating DMA.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ start_capture(pcdev);
+}
+
+static void omap1_videobuf_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct omap1_cam_buf *buf =
+ container_of(vb, struct omap1_cam_buf, vb);
+ struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ switch (vb->state) {
+ case VIDEOBUF_DONE:
+ dev_dbg(dev, "%s (done)\n", __func__);
+ break;
+ case VIDEOBUF_ACTIVE:
+ dev_dbg(dev, "%s (active)\n", __func__);
+ break;
+ case VIDEOBUF_QUEUED:
+ dev_dbg(dev, "%s (queued)\n", __func__);
+ break;
+ case VIDEOBUF_PREPARED:
+ dev_dbg(dev, "%s (prepared)\n", __func__);
+ break;
+ default:
+ dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
+ break;
+ }
+
+ free_buffer(vq, buf, pcdev->vb_mode);
+}
+
+static void videobuf_done(struct omap1_cam_dev *pcdev,
+ enum videobuf_state result)
+{
+ struct omap1_cam_buf *buf = pcdev->active;
+ struct videobuf_buffer *vb;
+ struct device *dev = pcdev->icd->parent;
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (result == VIDEOBUF_ERROR)
+ suspend_capture(pcdev);
+
+ vb = &buf->vb;
+ if (waitqueue_active(&vb->done)) {
+ if (!pcdev->ready && result != VIDEOBUF_ERROR) {
+ /*
+ * No next buffer has been entered into the DMA
+ * programming register set on time (could be done only
+ * while the previous DMA interurpt was processed, not
+ * later), so the last DMA block, be it a whole buffer
+ * if in CONTIG or its last sgbuf if in SG mode, is
+ * about to be reused by the just autoreinitialized DMA
+ * engine, and overwritten with next frame data. Best we
+ * can do is stopping the capture as soon as possible,
+ * hopefully before the next frame start.
+ */
+ suspend_capture(pcdev);
+ }
+ vb->state = result;
+ do_gettimeofday(&vb->ts);
+ if (result != VIDEOBUF_ERROR)
+ vb->field_count++;
+ wake_up(&vb->done);
+
+ /* shift in next buffer */
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+
+ if (!buf) {
+ /*
+ * No next buffer was ready on time (see above), so
+ * indicate error condition to force capture restart or
+ * stop, depending on next buffer already queued or not.
+ */
+ result = VIDEOBUF_ERROR;
+ prepare_next_vb(pcdev);
+
+ buf = pcdev->ready;
+ pcdev->active = buf;
+ pcdev->ready = NULL;
+ }
+ } else if (pcdev->ready) {
+ /*
+ * In both CONTIG and SG mode, the DMA engine has possibly
+ * been already autoreinitialized with the preprogrammed
+ * pcdev->ready buffer. We can either accept this fact
+ * and just swap the buffers, or provoke an error condition
+ * and restart capture. The former seems less intrusive.
+ */
+ dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
+ __func__);
+ pcdev->active = pcdev->ready;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
+ /*
+ * In SG mode, we have to make sure that the buffer we
+ * are putting back into the pcdev->ready is marked
+ * fresh.
+ */
+ buf->sgbuf = NULL;
+ }
+ pcdev->ready = buf;
+
+ buf = pcdev->active;
+ } else {
+ /*
+ * No next buffer has been entered into
+ * the DMA programming register set on time.
+ */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the DMA engine has already been
+ * reinitialized with the current buffer. Best we can do
+ * is not touching it.
+ */
+ dev_dbg(dev,
+ "%s: nobody waiting on videobuf, reuse it\n",
+ __func__);
+ } else {
+ /*
+ * In SG mode, the DMA engine has just been
+ * autoreinitialized with the last sgbuf from the
+ * current list. Restart capture in order to transfer
+ * next frame start into the first sgbuf, not the last
+ * one.
+ */
+ if (result != VIDEOBUF_ERROR) {
+ suspend_capture(pcdev);
+ result = VIDEOBUF_ERROR;
+ }
+ }
+ }
+
+ if (!buf) {
+ dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
+ disable_capture(pcdev);
+ return;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the current buffer parameters had already
+ * been entered into the DMA programming register set while the
+ * buffer was fetched with prepare_next_vb(), they may have also
+ * been transferred into the runtime set and already active if
+ * the DMA still running.
+ */
+ } else {
+ /* In SG mode, extra steps are required */
+ if (result == VIDEOBUF_ERROR)
+ /* make sure we (re)use sglist from start on error */
+ buf->sgbuf = NULL;
+
+ /*
+ * In any case, enter the next sgbuf parameters into the DMA
+ * programming register set. They will be used either during
+ * nearest DMA autoreinitialization or, in case of an error,
+ * on DMA startup below.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ }
+
+ if (result == VIDEOBUF_ERROR) {
+ dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
+ __func__);
+ start_capture(pcdev);
+ /*
+ * In SG mode, the above also resulted in the next sgbuf
+ * parameters being entered into the DMA programming register
+ * set, making them ready for next DMA autoreinitialization.
+ */
+ }
+
+ /*
+ * Finally, try fetching next buffer.
+ * In CONTIG mode, it will also enter it into the DMA programming
+ * register set, making it ready for next DMA autoreinitialization.
+ */
+ prepare_next_vb(pcdev);
+}
+
+static void dma_isr(int channel, unsigned short status, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct omap1_cam_buf *buf = pcdev->active;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, assume we have just managed to collect the
+ * whole frame, hopefully before our end of frame watchdog is
+ * triggered. Then, all we have to do is disabling the watchdog
+ * for this frame, and calling videobuf_done() with success
+ * indicated.
+ */
+ CAM_WRITE(pcdev, MODE,
+ CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
+ videobuf_done(pcdev, VIDEOBUF_DONE);
+ } else {
+ /*
+ * In SG mode, we have to process every sgbuf from the current
+ * sglist, one after another.
+ */
+ if (buf->sgbuf) {
+ /*
+ * Current sglist not completed yet, try fetching next
+ * sgbuf, hopefully putting it into the DMA programming
+ * register set, making it ready for next DMA
+ * autoreinitialization.
+ */
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ if (buf->sgbuf)
+ goto out;
+
+ /*
+ * No more sgbufs left in the current sglist. This
+ * doesn't mean that the whole videobuffer is already
+ * complete, but only that the last sgbuf from the
+ * current sglist is about to be filled. It will be
+ * ready on next DMA interrupt, signalled with the
+ * buf->sgbuf set back to NULL.
+ */
+ if (buf->result != VIDEOBUF_ERROR) {
+ /*
+ * Video frame collected without errors so far,
+ * we can prepare for collecting a next one
+ * as soon as DMA gets autoreinitialized
+ * after the current (last) sgbuf is completed.
+ */
+ buf = prepare_next_vb(pcdev);
+ if (!buf)
+ goto out;
+
+ try_next_sgbuf(pcdev->dma_ch, buf);
+ goto out;
+ }
+ }
+ /* end of videobuf */
+ videobuf_done(pcdev, buf->result);
+ }
+
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+}
+
+static irqreturn_t cam_isr(int irq, void *data)
+{
+ struct omap1_cam_dev *pcdev = data;
+ struct device *dev = pcdev->icd->parent;
+ struct omap1_cam_buf *buf = pcdev->active;
+ u32 it_status;
+ unsigned long flags;
+
+ it_status = CAM_READ(pcdev, IT_STATUS);
+ if (!it_status)
+ return IRQ_NONE;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (WARN_ON(!buf)) {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+ goto out;
+ }
+
+ if (unlikely(it_status & FIFO_FULL)) {
+ dev_warn(dev, "%s: FIFO overflow\n", __func__);
+
+ } else if (it_status & V_DOWN) {
+ /* end of video frame watchdog */
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, the watchdog is disabled with
+ * successful DMA end of block interrupt, and reenabled
+ * on next frame start. If we get here, there is nothing
+ * to check, we must be out of sync.
+ */
+ } else {
+ if (buf->sgcount == 2) {
+ /*
+ * If exactly 2 sgbufs from the next sglist have
+ * been programmed into the DMA engine (the
+ * first one already transferred into the DMA
+ * runtime register set, the second one still
+ * in the programming set), then we are in sync.
+ */
+ goto out;
+ }
+ }
+ dev_notice(dev, "%s: unexpected end of video frame\n",
+ __func__);
+
+ } else if (it_status & V_UP) {
+ u32 mode;
+
+ if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
+ /*
+ * In CONTIG mode, we need this interrupt every frame
+ * in oredr to reenable our end of frame watchdog.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE);
+ } else {
+ /*
+ * In SG mode, the below enabled end of frame watchdog
+ * is kept on permanently, so we can turn this one shot
+ * setup off.
+ */
+ mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
+ }
+
+ if (!(mode & EN_V_DOWN)) {
+ /* (re)enable end of frame watchdog interrupt */
+ mode |= EN_V_DOWN;
+ }
+ CAM_WRITE(pcdev, MODE, mode);
+ goto out;
+
+ } else {
+ dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
+ __func__, it_status);
+ goto out;
+ }
+
+ videobuf_done(pcdev, VIDEOBUF_ERROR);
+out:
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static struct videobuf_queue_ops omap1_videobuf_ops = {
+ .buf_setup = omap1_videobuf_setup,
+ .buf_prepare = omap1_videobuf_prepare,
+ .buf_queue = omap1_videobuf_queue,
+ .buf_release = omap1_videobuf_release,
+};
+
+
+/*
+ * SOC Camera host operations
+ */
+
+static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
+{
+ /* apply/release camera sensor reset if requested by platform data */
+ if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
+ CAM_WRITE(pcdev, GPIO, reset);
+ else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
+ CAM_WRITE(pcdev, GPIO, !reset);
+}
+
+/*
+ * The following two functions absolutely depend on the fact, that
+ * there can be only one camera on OMAP1 camera sensor interface
+ */
+static int omap1_cam_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ clk_enable(pcdev->clk);
+
+ /* setup sensor clock */
+ ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock &= ~FOSCMOD_MASK;
+ switch (pcdev->camexclk) {
+ case 6000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
+ break;
+ case 8000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
+ break;
+ case 9600000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
+ break;
+ case 12000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
+ break;
+ case 24000000:
+ ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
+ default:
+ break;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
+
+ /* enable internal clock */
+ ctrlclock |= MCLK_EN;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ sensor_reset(pcdev, false);
+
+ pcdev->icd = icd;
+
+ dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
+ icd->devnum);
+ return 0;
+}
+
+static void omap1_cam_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 ctrlclock;
+
+ BUG_ON(icd != pcdev->icd);
+
+ suspend_capture(pcdev);
+ disable_capture(pcdev);
+
+ sensor_reset(pcdev, true);
+
+ /* disable and release system clocks */
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
+
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
+
+ clk_disable(pcdev->clk);
+
+ pcdev->icd = NULL;
+
+ dev_dbg(icd->parent,
+ "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
+}
+
+/* Duplicate standard formats based on host capability of byte swapping */
+static const struct soc_mbus_lookup omap1_cam_formats[] = {
+{
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+},
+};
+
+static int omap1_cam_get_formats(struct soc_camera_device *icd,
+ unsigned int idx, struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int formats = 0, ret;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
+ idx, code);
+ return 0;
+ }
+
+ /* Check support for the requested bits-per-sample */
+ if (fmt->bits_per_sample != 8)
+ return 0;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
+ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
+ case V4L2_MBUS_FMT_RGB565_2X8_BE:
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = soc_mbus_find_fmtdesc(code,
+ omap1_cam_formats,
+ ARRAY_SIZE(omap1_cam_formats));
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev,
+ "%s: providing format %s as byte swapped code #%d\n",
+ __func__, xlate->host_fmt->name, code);
+ }
+ default:
+ if (xlate)
+ dev_dbg(dev,
+ "%s: providing format %s in pass-through mode\n",
+ __func__, fmt->name);
+ }
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
+ enum omap1_cam_vb_mode vb_mode)
+{
+ int size = bytes_per_line * height;
+
+ return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
+ IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
+}
+
+static int dma_align(int *width, int *height,
+ const struct soc_mbus_pixelfmt *fmt,
+ enum omap1_cam_vb_mode vb_mode, bool enlarge)
+{
+ s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
+
+ if (bytes_per_line < 0)
+ return bytes_per_line;
+
+ if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
+ unsigned int pxalign = __fls(bytes_per_line / *width);
+ unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
+ DMA_ELEMENT_SHIFT - pxalign;
+ unsigned int incr = enlarge << salign;
+
+ v4l_bound_align_image(width, 1, *width + incr, 0,
+ height, 1, *height + incr, 0, salign);
+ return 0;
+ }
+ 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; \
+})
+
+static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev,
+ struct soc_camera_device *icd, struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf,
+ const struct soc_camera_format_xlate *xlate)
+{
+ s32 bytes_per_line;
+ int ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, mf);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: s_mbus_fmt failed\n", __func__);
+ return ret;
+ }
+
+ if (mf->code != xlate->code) {
+ dev_err(dev, "%s: unexpected pixel code change\n", __func__);
+ return -EINVAL;
+ }
+
+ bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
+ if (bytes_per_line < 0) {
+ dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
+ __func__);
+ return bytes_per_line;
+ }
+
+ if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
+ dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
+ __func__, mf->width, mf->height);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int omap1_cam_set_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *crop)
+{
+ struct v4l2_rect *rect = &crop->c;
+ const struct soc_camera_format_xlate *xlate = icd->current_fmt;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
+ rect->width, rect->height, rect->left, rect->top);
+ return ret;
+ }
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0) {
+ dev_warn(dev, "%s: failed to fetch current format\n", __func__);
+ return ret;
+ }
+
+ ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+ false);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, mf.width, mf.height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ if (!ret) {
+ /* sensor returned geometry not DMA aligned, trying to fix */
+ ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+ }
+
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+
+ return 0;
+}
+
+static int omap1_cam_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(dev, "%s: format %#x not found\n", __func__,
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
+ true);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
+ __func__, pix->width, pix->height,
+ xlate->host_fmt->name);
+ return ret;
+ }
+
+ ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
+ if (ret < 0) {
+ dev_err(dev, "%s: failed to set format\n", __func__);
+ return ret;
+ }
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+ icd->current_fmt = xlate;
+
+ return 0;
+}
+
+static int omap1_cam_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+ /* TODO: limit to mx1 hardware capabilities */
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %#x not found\n",
+ pix->pixelformat);
+ return -EINVAL;
+ }
+
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ /* limit to sensor capabilities */
+ ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ return 0;
+}
+
+static bool sg_mode;
+
+/*
+ * Local mmap_mapper wrapper,
+ * used for detecting videobuf-dma-contig buffer allocation failures
+ * and switching to videobuf-dma-sg automatically for future attempts.
+ */
+static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
+ struct videobuf_buffer *buf,
+ struct vm_area_struct *vma)
+{
+ struct soc_camera_device *icd = q->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ int ret;
+
+ ret = pcdev->mmap_mapper(q, buf, vma);
+
+ if (ret == -ENOMEM)
+ sg_mode = true;
+
+ return ret;
+}
+
+static void omap1_cam_init_videobuf(struct videobuf_queue *q,
+ struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct omap1_cam_dev *pcdev = ici->priv;
+
+ if (!sg_mode)
+ videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+ else
+ videobuf_queue_sg_init(q, &omap1_videobuf_ops,
+ icd->parent, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
+ sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
+
+ /* use videobuf mode (auto)selected with the module parameter */
+ pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
+
+ /*
+ * Ensure we substitute the videobuf-dma-contig version of the
+ * mmap_mapper() callback with our own wrapper, used for switching
+ * automatically to videobuf-dma-sg on buffer allocation failure.
+ */
+ if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
+ pcdev->mmap_mapper = q->int_ops->mmap_mapper;
+ q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
+ }
+}
+
+static int omap1_cam_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 omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
+ struct omap1_cam_buf, vb);
+ buf->inwork = 0;
+ INIT_LIST_HEAD(&buf->vb.queue);
+ }
+
+ return 0;
+}
+
+static int omap1_cam_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ /* cap->name is set by the friendly caller:-> */
+ strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct omap1_cam_dev *pcdev = ici->priv;
+ u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
+ const struct soc_camera_format_xlate *xlate;
+ const struct soc_mbus_pixelfmt *fmt;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long common_flags;
+ u32 ctrlclock, mode;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(dev,
+ "Flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, SOCAM_BUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = SOCAM_BUS_FLAGS;
+ }
+
+ /* Make choices, possibly based on platform configuration */
+ if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
+ (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
+ if (!pcdev->pdata ||
+ pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ else
+ common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
+ common_flags, ret);
+ return ret;
+ }
+
+ ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
+ dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
+ ctrlclock |= POLCLK;
+ } else {
+ dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
+ ctrlclock &= ~POLCLK;
+ }
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
+
+ if (ctrlclock & LCLK_EN)
+ CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
+
+ /* select bus endianess */
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ fmt = xlate->host_fmt;
+
+ mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
+ if (fmt->order == SOC_MBUS_ORDER_LE) {
+ dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
+ } else {
+ dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
+ CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
+ }
+
+ return 0;
+}
+
+static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct omap1_cam_buf *buf;
+
+ buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
+ 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;
+}
+
+static struct soc_camera_host_ops omap1_host_ops = {
+ .owner = THIS_MODULE,
+ .add = omap1_cam_add_device,
+ .remove = omap1_cam_remove_device,
+ .get_formats = omap1_cam_get_formats,
+ .set_crop = omap1_cam_set_crop,
+ .set_fmt = omap1_cam_set_fmt,
+ .try_fmt = omap1_cam_try_fmt,
+ .init_videobuf = omap1_cam_init_videobuf,
+ .reqbufs = omap1_cam_reqbufs,
+ .querycap = omap1_cam_querycap,
+ .set_bus_param = omap1_cam_set_bus_param,
+ .poll = omap1_cam_poll,
+};
+
+static int __init omap1_cam_probe(struct platform_device *pdev)
+{
+ struct omap1_cam_dev *pcdev;
+ struct resource *res;
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ clk = clk_get(&pdev->dev, "armper_ck");
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit_put_clk;
+ }
+
+ pcdev->res = res;
+ pcdev->clk = clk;
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (pcdev->pdata) {
+ pcdev->pflags = pcdev->pdata->flags;
+ pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
+ }
+
+ switch (pcdev->camexclk) {
+ case 6000000:
+ case 8000000:
+ case 9600000:
+ case 12000000:
+ case 24000000:
+ break;
+ default:
+ /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
+ dev_warn(&pdev->dev,
+ "Incorrect sensor clock frequency %ld kHz, "
+ "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
+ "please correct your platform data\n",
+ pcdev->pdata->camexclk_khz);
+ pcdev->camexclk = 0;
+ case 0:
+ dev_info(&pdev->dev, "Not providing sensor clock\n");
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+
+ /*
+ * Request the region.
+ */
+ if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
+ err = -EBUSY;
+ goto exit_kfree;
+ }
+
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ err = -ENOMEM;
+ goto exit_release;
+ }
+ pcdev->irq = irq;
+ pcdev->base = base;
+
+ sensor_reset(pcdev, true);
+
+ err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
+ dma_isr, (void *)pcdev, &pcdev->dma_ch);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
+ err = -EBUSY;
+ goto exit_iounmap;
+ }
+ dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
+
+ /* preconfigure DMA */
+ omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
+ 0, 0);
+ omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
+ /* setup DMA autoinitialization */
+ omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
+
+ err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Camera interrupt register failed\n");
+ goto exit_free_dma;
+ }
+
+ pcdev->soc_host.drv_name = DRIVER_NAME;
+ pcdev->soc_host.ops = &omap1_host_ops;
+ pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
+ pcdev->soc_host.nr = pdev->id;
+
+ err = soc_camera_host_register(&pcdev->soc_host);
+ if (err)
+ goto exit_free_irq;
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
+
+ return 0;
+
+exit_free_irq:
+ free_irq(pcdev->irq, pcdev);
+exit_free_dma:
+ omap_free_dma(pcdev->dma_ch);
+exit_iounmap:
+ iounmap(base);
+exit_release:
+ release_mem_region(res->start, resource_size(res));
+exit_kfree:
+ kfree(pcdev);
+exit_put_clk:
+ clk_put(clk);
+exit:
+ return err;
+}
+
+static int __exit omap1_cam_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct omap1_cam_dev *pcdev = container_of(soc_host,
+ struct omap1_cam_dev, soc_host);
+ struct resource *res;
+
+ free_irq(pcdev->irq, pcdev);
+
+ omap_free_dma(pcdev->dma_ch);
+
+ soc_camera_host_unregister(soc_host);
+
+ iounmap(pcdev->base);
+
+ res = pcdev->res;
+ release_mem_region(res->start, resource_size(res));
+
+ clk_put(pcdev->clk);
+
+ kfree(pcdev);
+
+ dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
+
+ return 0;
+}
+
+static struct platform_driver omap1_cam_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = omap1_cam_probe,
+ .remove = __exit_p(omap1_cam_remove),
+};
+
+module_platform_driver(omap1_cam_driver);
+
+module_param(sg_mode, bool, 0644);
+MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
+
+MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c
index 9c21e01f2c24..9c21e01f2c24 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
new file mode 100644
index 000000000000..0a24253dcda2
--- /dev/null
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -0,0 +1,2331 @@
+/*
+ * V4L2 Driver for SuperH Mobile CEU interface
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
+ *
+ * Copyright (C) 2006, Sascha Hauer, Pengutronix
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/soc_camera.h>
+#include <media/sh_mobile_ceu.h>
+#include <media/sh_mobile_csi2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-mediabus.h>
+#include <media/soc_mediabus.h>
+
+/* register offsets for sh7722 / sh7723 */
+
+#define CAPSR 0x00 /* Capture start register */
+#define CAPCR 0x04 /* Capture control register */
+#define CAMCR 0x08 /* Capture interface control register */
+#define CMCYR 0x0c /* Capture interface cycle register */
+#define CAMOR 0x10 /* Capture interface offset register */
+#define CAPWR 0x14 /* Capture interface width register */
+#define CAIFR 0x18 /* Capture interface input format register */
+#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */
+#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */
+#define CRCNTR 0x28 /* CEU register control register */
+#define CRCMPR 0x2c /* CEU register forcible control register */
+#define CFLCR 0x30 /* Capture filter control register */
+#define CFSZR 0x34 /* Capture filter size clip register */
+#define CDWDR 0x38 /* Capture destination width register */
+#define CDAYR 0x3c /* Capture data address Y register */
+#define CDACR 0x40 /* Capture data address C register */
+#define CDBYR 0x44 /* Capture data bottom-field address Y register */
+#define CDBCR 0x48 /* Capture data bottom-field address C register */
+#define CBDSR 0x4c /* Capture bundle destination size register */
+#define CFWCR 0x5c /* Firewall operation control register */
+#define CLFCR 0x60 /* Capture low-pass filter control register */
+#define CDOCR 0x64 /* Capture data output control register */
+#define CDDCR 0x68 /* Capture data complexity level register */
+#define CDDAR 0x6c /* Capture data complexity level address register */
+#define CEIER 0x70 /* Capture event interrupt enable register */
+#define CETCR 0x74 /* Capture event flag clear register */
+#define CSTSR 0x7c /* Capture status register */
+#define CSRTR 0x80 /* Capture software reset register */
+#define CDSSR 0x84 /* Capture data size register */
+#define CDAYR2 0x90 /* Capture data address Y register 2 */
+#define CDACR2 0x94 /* Capture data address C register 2 */
+#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
+#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
+
+#undef DEBUG_GEOMETRY
+#ifdef DEBUG_GEOMETRY
+#define dev_geo dev_info
+#else
+#define dev_geo dev_dbg
+#endif
+
+/* per video frame buffer */
+struct sh_mobile_ceu_buffer {
+ struct vb2_buffer vb; /* v4l buffer must be first */
+ struct list_head queue;
+};
+
+struct sh_mobile_ceu_dev {
+ struct soc_camera_host ici;
+ struct soc_camera_device *icd;
+ struct platform_device *csi2_pdev;
+
+ unsigned int irq;
+ void __iomem *base;
+ size_t video_limit;
+ size_t buf_total;
+
+ spinlock_t lock; /* Protects video buffer lists */
+ struct list_head capture;
+ struct vb2_buffer *active;
+ struct vb2_alloc_ctx *alloc_ctx;
+
+ struct sh_mobile_ceu_info *pdata;
+ struct completion complete;
+
+ u32 cflcr;
+
+ /* static max sizes either from platform data or default */
+ int max_width;
+ int max_height;
+
+ enum v4l2_field field;
+ int sequence;
+
+ unsigned int image_mode:1;
+ unsigned int is_16bit:1;
+ unsigned int frozen:1;
+};
+
+struct sh_mobile_ceu_cam {
+ /* CEU offsets within the camera output, before the CEU scaler */
+ unsigned int ceu_left;
+ unsigned int ceu_top;
+ /* Client output, as seen by the CEU */
+ unsigned int width;
+ unsigned int height;
+ /*
+ * User window from S_CROP / G_CROP, produced by client cropping and
+ * scaling, CEU scaling and CEU cropping, mapped back onto the client
+ * input window
+ */
+ struct v4l2_rect subrect;
+ /* Camera cropping rectangle */
+ struct v4l2_rect rect;
+ const struct soc_mbus_pixelfmt *extra_fmt;
+ 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 void ceu_write(struct sh_mobile_ceu_dev *priv,
+ unsigned long reg_offs, u32 data)
+{
+ iowrite32(data, priv->base + reg_offs);
+}
+
+static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
+{
+ return ioread32(priv->base + reg_offs);
+}
+
+static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ int i, success = 0;
+ struct soc_camera_device *icd = pcdev->icd;
+
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
+
+ /* wait CSTSR.CPTON bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CSTSR) & 1)) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+ /* wait CAPSR.CPKIL bit */
+ for (i = 0; i < 1000; i++) {
+ if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
+ success++;
+ break;
+ }
+ udelay(1);
+ }
+
+
+ if (2 != success) {
+ dev_warn(icd->pdev, "soft reset time out\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Videobuf operations
+ */
+
+/*
+ * .queue_setup() is called to check, whether the driver can accept the
+ * requested number of buffers and to fill in plane sizes
+ * for the current frame format if required
+ */
+static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ unsigned int bytes_per_line;
+ int ret;
+
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ sizes[0] = icd->sizeimage;
+ }
+
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ if (!vq->num_buffers)
+ pcdev->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+
+ /* If *num_planes != 0, we have already verified *count. */
+ if (pcdev->video_limit && !*num_planes) {
+ size_t size = PAGE_ALIGN(sizes[0]) * *count;
+
+ if (size + pcdev->buf_total > pcdev->video_limit)
+ *count = (pcdev->video_limit - pcdev->buf_total) /
+ PAGE_ALIGN(sizes[0]);
+ }
+
+ *num_planes = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
+
+ return 0;
+}
+
+#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 */
+#define CEU_CEIER_VBP (1 << 20) /* vbp error */
+#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
+#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
+
+
+/*
+ * return value doesn't reflex the success/failure to queue the new buffer,
+ * but rather the status of the previous buffer.
+ */
+static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+{
+ struct soc_camera_device *icd = pcdev->icd;
+ dma_addr_t phys_addr_top, phys_addr_bottom;
+ unsigned long top1, top2;
+ unsigned long bottom1, bottom2;
+ u32 status;
+ bool planar;
+ int ret = 0;
+
+ /*
+ * The hardware is _very_ picky about this sequence. Especially
+ * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
+ * several not-so-well documented interrupt sources in CETCR.
+ */
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
+ status = ceu_read(pcdev, CETCR);
+ ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
+ if (!pcdev->frozen)
+ ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
+ ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
+ ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
+
+ /*
+ * When a VBP interrupt occurs, a capture end interrupt does not occur
+ * and the image of that frame is not captured correctly. So, soft reset
+ * is needed here.
+ */
+ if (status & CEU_CEIER_VBP) {
+ sh_mobile_ceu_soft_reset(pcdev);
+ ret = -EIO;
+ }
+
+ if (pcdev->frozen) {
+ complete(&pcdev->complete);
+ return ret;
+ }
+
+ if (!pcdev->active)
+ return ret;
+
+ if (V4L2_FIELD_INTERLACED_BT == pcdev->field) {
+ top1 = CDBYR;
+ top2 = CDBCR;
+ bottom1 = CDAYR;
+ bottom2 = CDACR;
+ } else {
+ top1 = CDAYR;
+ top2 = CDACR;
+ bottom1 = CDBYR;
+ bottom2 = CDBCR;
+ }
+
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
+
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ planar = true;
+ break;
+ default:
+ planar = false;
+ }
+
+ ceu_write(pcdev, top1, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ phys_addr_bottom = phys_addr_top + icd->bytesperline;
+ ceu_write(pcdev, bottom1, phys_addr_bottom);
+ }
+
+ if (planar) {
+ phys_addr_top += icd->bytesperline * icd->user_height;
+ ceu_write(pcdev, top2, phys_addr_top);
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ phys_addr_bottom = phys_addr_top + icd->bytesperline;
+ ceu_write(pcdev, bottom2, phys_addr_bottom);
+ }
+ }
+
+ ceu_write(pcdev, CAPSR, 0x1); /* start capture */
+
+ return ret;
+}
+
+static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
+{
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+
+ /* Added list head initialization on alloc */
+ WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
+
+ return 0;
+}
+
+static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
+{
+ 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->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ unsigned long size;
+
+ size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+#ifdef DEBUG
+ /*
+ * This can be useful if you want to see if we actually fill
+ * the buffer with something
+ */
+ if (vb2_plane_vaddr(vb, 0))
+ memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
+#endif
+
+ spin_lock_irq(&pcdev->lock);
+ list_add_tail(&buf->queue, &pcdev->capture);
+
+ if (!pcdev->active) {
+ /*
+ * Because there were no active buffer at this moment,
+ * we are not interested in the return value of
+ * sh_mobile_ceu_capture here.
+ */
+ pcdev->active = vb;
+ sh_mobile_ceu_capture(pcdev);
+ }
+ spin_unlock_irq(&pcdev->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
+{
+ 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->parent);
+ struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ spin_lock_irq(&pcdev->lock);
+
+ if (pcdev->active == vb) {
+ /* disable capture (release DMA buffer), reset */
+ ceu_write(pcdev, CAPSR, 1 << 16);
+ pcdev->active = NULL;
+ }
+
+ /*
+ * Doesn't hurt also if the list is empty, but it hurts, if queuing the
+ * buffer failed, and .buf_init() hasn't been called
+ */
+ if (buf->queue.next)
+ list_del_init(&buf->queue);
+
+ pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
+ spin_unlock_irq(&pcdev->lock);
+}
+
+static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
+{
+ 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->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
+ dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
+ pcdev->buf_total);
+
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
+ return 0;
+}
+
+static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct list_head *buf_head, *tmp;
+
+ spin_lock_irq(&pcdev->lock);
+
+ pcdev->active = NULL;
+
+ list_for_each_safe(buf_head, tmp, &pcdev->capture)
+ list_del_init(buf_head);
+
+ spin_unlock_irq(&pcdev->lock);
+
+ return sh_mobile_ceu_soft_reset(pcdev);
+}
+
+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,
+ .stop_streaming = sh_mobile_ceu_stop_streaming,
+};
+
+static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
+{
+ struct sh_mobile_ceu_dev *pcdev = data;
+ struct vb2_buffer *vb;
+ int ret;
+
+ spin_lock(&pcdev->lock);
+
+ vb = pcdev->active;
+ if (!vb)
+ /* Stale interrupt from a released buffer */
+ goto out;
+
+ list_del_init(&to_ceu_vb(vb)->queue);
+
+ if (!list_empty(&pcdev->capture))
+ pcdev->active = &list_entry(pcdev->capture.next,
+ struct sh_mobile_ceu_buffer, queue)->vb;
+ else
+ pcdev->active = NULL;
+
+ 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(&pcdev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
+{
+ struct v4l2_subdev *sd;
+
+ if (!pcdev->csi2_pdev)
+ return NULL;
+
+ v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
+ if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
+ return sd;
+
+ return NULL;
+}
+
+/* Called with .video_lock held */
+static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd;
+ int ret;
+
+ if (pcdev->icd)
+ return -EBUSY;
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver attached to camera %d\n",
+ icd->devnum);
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ pcdev->buf_total = 0;
+
+ ret = sh_mobile_ceu_soft_reset(pcdev);
+
+ csi2_sd = find_csi2(pcdev);
+ if (csi2_sd) {
+ csi2_sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(csi2_sd, icd);
+ }
+
+ ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
+ return ret;
+ }
+
+ /*
+ * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
+ * has not found this soc-camera device among its clients
+ */
+ if (ret == -ENODEV && csi2_sd)
+ csi2_sd->grp_id = 0;
+ pcdev->icd = icd;
+
+ return 0;
+}
+
+/* Called with .video_lock held */
+static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+
+ BUG_ON(icd != pcdev->icd);
+
+ v4l2_subdev_call(csi2_sd, core, s_power, 0);
+ if (csi2_sd)
+ csi2_sd->grp_id = 0;
+ /* disable capture, disable interrupts */
+ ceu_write(pcdev, CEIER, 0);
+ sh_mobile_ceu_soft_reset(pcdev);
+
+ /* make sure active buffer is canceled */
+ spin_lock_irq(&pcdev->lock);
+ if (pcdev->active) {
+ list_del_init(&to_ceu_vb(pcdev->active)->queue);
+ vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR);
+ pcdev->active = NULL;
+ }
+ spin_unlock_irq(&pcdev->lock);
+
+ pm_runtime_put_sync(ici->v4l2_dev.dev);
+
+ dev_info(icd->parent,
+ "SuperH Mobile CEU driver detached from camera %d\n",
+ icd->devnum);
+
+ pcdev->icd = NULL;
+}
+
+/*
+ * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)"
+ * in SH7722 Hardware Manual
+ */
+static unsigned int size_dst(unsigned int src, unsigned int scale)
+{
+ unsigned int mant_pre = scale >> 12;
+ if (!src || !scale)
+ return src;
+ return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) *
+ mant_pre * 4096 / scale + 1;
+}
+
+static u16 calc_scale(unsigned int src, unsigned int *dst)
+{
+ u16 scale;
+
+ if (src == *dst)
+ return 0;
+
+ scale = (src * 4096 / *dst) & ~7;
+
+ while (scale > 4096 && size_dst(src, scale) < *dst)
+ scale -= 8;
+
+ *dst = size_dst(src, scale);
+
+ return scale;
+}
+
+/* rect is guaranteed to not exceed the scaled camera rectangle */
+static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ unsigned int height, width, cdwdr_width, in_width, in_height;
+ unsigned int left_offset, top_offset;
+ u32 camor;
+
+ dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
+
+ left_offset = cam->ceu_left;
+ top_offset = cam->ceu_top;
+
+ WARN_ON(icd->user_width & 3 || icd->user_height & 3);
+
+ width = icd->user_width;
+
+ if (pcdev->image_mode) {
+ in_width = cam->width;
+ if (!pcdev->is_16bit) {
+ in_width *= 2;
+ left_offset *= 2;
+ }
+ } else {
+ unsigned int w_factor;
+
+ switch (icd->current_fmt->host_fmt->packing) {
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ w_factor = 2;
+ break;
+ default:
+ w_factor = 1;
+ }
+
+ in_width = cam->width * w_factor;
+ left_offset *= w_factor;
+ }
+
+ cdwdr_width = icd->bytesperline;
+
+ height = icd->user_height;
+ in_height = cam->height;
+ if (V4L2_FIELD_NONE != pcdev->field) {
+ height = (height / 2) & ~3;
+ in_height /= 2;
+ top_offset /= 2;
+ cdwdr_width *= 2;
+ }
+
+ /* CSI2 special configuration */
+ if (pcdev->pdata->csi2) {
+ in_width = ((in_width - 2) * 2);
+ left_offset *= 2;
+ }
+
+ /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
+ camor = left_offset | (top_offset << 16);
+
+ dev_geo(icd->parent,
+ "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
+ (in_height << 16) | in_width, (height << 16) | width,
+ cdwdr_width);
+
+ ceu_write(pcdev, CAMOR, camor);
+ ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+ /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
+ ceu_write(pcdev, CFSZR, (height << 16) | width);
+ ceu_write(pcdev, CDWDR, cdwdr_width);
+}
+
+static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ u32 capsr = ceu_read(pcdev, CAPSR);
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */
+ return capsr;
+}
+
+static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
+{
+ unsigned long timeout = jiffies + 10 * HZ;
+
+ /*
+ * Wait until the end of the current frame. It can take a long time,
+ * but if it has been aborted by a CAPSR reset, it shoule exit sooner.
+ */
+ while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(pcdev->ici.v4l2_dev.dev,
+ "Timeout waiting for frame end! Interface problem?\n");
+ return;
+ }
+
+ /* Wait until reset clears, this shall not hang... */
+ while (ceu_read(pcdev, CAPSR) & (1 << 16))
+ udelay(10);
+
+ /* Anything to restore? */
+ if (capsr & ~(1 << 16))
+ ceu_write(pcdev, CAPSR, capsr);
+}
+
+/* Find the bus subdevice driver, e.g., CSI2 */
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
+ struct soc_camera_device *icd)
+{
+ if (pcdev->csi2_pdev) {
+ struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
+ if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
+ return csi2_sd;
+ }
+
+ return soc_camera_to_subdev(icd);
+}
+
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+/* Capture is not running, no interrupts, no locking needed */
+static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ unsigned long value, common_flags = CEU_BUS_FLAGS;
+ u32 capsr = capture_save_reset(pcdev);
+ unsigned int yuv_lineskip;
+ int ret;
+
+ /*
+ * If the client doesn't implement g_mbus_config, we just use our
+ * platform data
+ */
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ if (!common_flags)
+ return -EINVAL;
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ }
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (icd->current_fmt->host_fmt->bits_per_sample > 8)
+ pcdev->is_16bit = 1;
+ else
+ pcdev->is_16bit = 0;
+
+ ceu_write(pcdev, CRCNTR, 0);
+ ceu_write(pcdev, CRCMPR, 0);
+
+ value = 0x00000010; /* data fetch by default */
+ yuv_lineskip = 0x10;
+
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ /* convert 4:2:2 -> 4:2:0 */
+ yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
+ /* fall-through */
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ switch (cam->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
+ break;
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
+ break;
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
+ icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
+ value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
+
+ value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
+ value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
+
+ if (pcdev->pdata->csi2) /* CSI2 mode */
+ value |= 3 << 12;
+ else if (pcdev->is_16bit)
+ value |= 1 << 12;
+ else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+ value |= 2 << 12;
+
+ ceu_write(pcdev, CAMCR, value);
+
+ ceu_write(pcdev, CAPCR, 0x00300000);
+
+ switch (pcdev->field) {
+ case V4L2_FIELD_INTERLACED_TB:
+ value = 0x101;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ value = 0x102;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ ceu_write(pcdev, CAIFR, value);
+
+ sh_mobile_ceu_set_rect(icd);
+ mdelay(1);
+
+ dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
+ ceu_write(pcdev, CFLCR, pcdev->cflcr);
+
+ /*
+ * A few words about byte order (observed in Big Endian mode)
+ *
+ * In data fetch mode bytes are received in chunks of 8 bytes.
+ * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
+ *
+ * The data is however by default written to memory in reverse order:
+ * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
+ *
+ * The lowest three bits of CDOCR allows us to do swapping,
+ * using 7 we swap the data bytes to match the incoming order:
+ * D0, D1, D2, D3, D4, D5, D6, D7
+ */
+ value = 0x00000007 | yuv_lineskip;
+
+ ceu_write(pcdev, CDOCR, value);
+ ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
+
+ capture_restore(pcdev, capsr);
+
+ /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
+ return 0;
+}
+
+static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
+ unsigned long common_flags = CEU_BUS_FLAGS;
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret)
+ common_flags = soc_mbus_config_compatible(&cfg,
+ common_flags);
+ else if (ret != -ENOIOCTLCMD)
+ return ret;
+
+ if (!common_flags || buswidth > 16)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .name = "NV12",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .name = "NV21",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .name = "NV16",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .name = "NV61",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
+ },
+};
+
+/* This will be corrected as we get more formats */
+static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
+ (fmt->bits_per_sample == 8 &&
+ fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
+
+static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
+{
+ return container_of(ctrl->handler, struct soc_camera_device,
+ ctrl_handler);
+}
+
+static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct soc_camera_device *icd = ctrl_to_icd(ctrl);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ ceu_write(pcdev, CLFCR, !ctrl->val);
+ return 0;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
+ .s_ctrl = sh_mobile_ceu_s_ctrl,
+};
+
+static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ int ret, k, n;
+ int formats = 0;
+ struct sh_mobile_ceu_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ /* No more formats */
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ if (!pcdev->pdata->csi2) {
+ /* Are there any restrictions in the CSI-2 case? */
+ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+ }
+
+ if (!icd->host_priv) {
+ struct v4l2_mbus_framefmt mf;
+ struct v4l2_rect rect;
+ int shift = 0;
+
+ /* Add our control */
+ v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 1, 1, 0);
+ if (icd->ctrl_handler.error)
+ return icd->ctrl_handler.error;
+
+ /* FIXME: subwindow is lost between close / open */
+
+ /* Cache current client geometry */
+ ret = client_g_rect(sd, &rect);
+ if (ret < 0)
+ return ret;
+
+ /* First time */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * All currently existing CEU implementations support 2560x1920
+ * or larger frames. If the sensor is proposing too big a frame,
+ * don't bother with possibly supportred by the CEU larger
+ * sizes, just try VGA multiples. If needed, this can be
+ * adjusted in the future.
+ */
+ while ((mf.width > pcdev->max_width ||
+ mf.height > pcdev->max_height) && shift < 4) {
+ /* Try 2560x1920, 1280x960, 640x480, 320x240 */
+ mf.width = 2560 >> shift;
+ mf.height = 1920 >> shift;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+ shift++;
+ }
+
+ if (shift == 4) {
+ dev_err(dev, "Failed to configure the client below %ux%x\n",
+ mf.width, mf.height);
+ return -EIO;
+ }
+
+ dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height);
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ /* We are called with current camera crop, initialise subrect with it */
+ cam->rect = rect;
+ cam->subrect = rect;
+
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
+ /* Beginning of a pass */
+ if (!idx)
+ cam->extra_fmt = NULL;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ case V4L2_MBUS_FMT_VYUY8_2X8:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ case V4L2_MBUS_FMT_YVYU8_2X8:
+ if (cam->extra_fmt)
+ break;
+
+ /*
+ * Our case is simple so far: for any of the above four camera
+ * formats we add all our four synthesized NV* formats, so,
+ * just marking the device with a single flag suffices. If
+ * the format generation rules are more complex, you would have
+ * to actually hang your already added / counted formats onto
+ * the host_priv pointer and check whether the format you're
+ * going to add now is already there.
+ */
+ cam->extra_fmt = sh_mobile_ceu_formats;
+
+ n = ARRAY_SIZE(sh_mobile_ceu_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+ xlate->host_fmt = &sh_mobile_ceu_formats[k];
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ sh_mobile_ceu_formats[k].name, code);
+ }
+ break;
+ default:
+ if (!sh_mobile_ceu_packing_supported(fmt))
+ return 0;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ dev_dbg(dev, "Providing format %s in pass-through mode\n",
+ fmt->name);
+ }
+
+ return formats;
+}
+
+static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+/* Check if any dimension of r1 is smaller than respective one of r2 */
+static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+/* Check if r1 fails to cover r2 */
+static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+static unsigned int scale_down(unsigned int size, unsigned int scale)
+{
+ return (size * 4096 + scale / 2) / scale;
+}
+
+static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
+{
+ return (input * 4096 + output / 2) / output;
+}
+
+/* Get and store current client crop */
+static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
+{
+ struct v4l2_crop crop;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+ if (!ret) {
+ *rect = crop.c;
+ return ret;
+ }
+
+ /* Camera driver doesn't support .g_crop(), assume default rectangle */
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (!ret)
+ *rect = cap.defrect;
+
+ return ret;
+}
+
+/* Client crop has changed, update our sub-rectangle to remain within the area */
+static void update_subrect(struct sh_mobile_ceu_cam *cam)
+{
+ struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect;
+
+ if (rect->width < subrect->width)
+ subrect->width = rect->width;
+
+ if (rect->height < subrect->height)
+ subrect->height = rect->height;
+
+ if (rect->left > subrect->left)
+ subrect->left = rect->left;
+ else if (rect->left + rect->width >
+ subrect->left + subrect->width)
+ subrect->left = rect->left + rect->width -
+ subrect->width;
+
+ if (rect->top > subrect->top)
+ subrect->top = rect->top;
+ else if (rect->top + rect->height >
+ subrect->top + subrect->height)
+ subrect->top = rect->top + rect->height -
+ subrect->height;
+}
+
+/*
+ * The common for both scaling and cropping iterative approach is:
+ * 1. try if the client can produce exactly what requested by the user
+ * 2. if (1) failed, try to double the client image until we get one big enough
+ * 3. if (2) failed, try to request the maximum image
+ */
+static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
+ const struct v4l2_crop *cam_crop)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
+ rect->width, rect->height, rect->left, rect->top);
+ cam->rect = *cam_rect;
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ /* Put user requested rectangle within sensor bounds */
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max(cam_rect->width, 2);
+ height = max(cam_rect->height, 2);
+
+ /*
+ * Loop as long as sensor is not covering the requested rectangle and
+ * is still within its bounds
+ */
+ while (!ret && (is_smaller(cam_rect, rect) ||
+ is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ * Instead we just drop to the left and top bounds.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ if (!ret) {
+ cam->rect = *cam_rect;
+ update_subrect(cam);
+ }
+
+ return ret;
+}
+
+/* Iterative s_mbus_fmt, also updates cached client crop on success */
+static int client_s_fmt(struct soc_camera_device *icd,
+ struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
+ unsigned int max_width, max_height;
+ struct v4l2_cropcap cap;
+ bool ceu_1to1;
+ int ret;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
+
+ if (width == mf->width && height == mf->height) {
+ /* Perfect! The client has done it all. */
+ ceu_1to1 = true;
+ goto update_cache;
+ }
+
+ ceu_1to1 = false;
+ if (!ceu_can_scale)
+ goto update_cache;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ max_width = min(cap.bounds.width, pcdev->max_width);
+ max_height = min(cap.bounds.height, pcdev->max_height);
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = mf->width;
+ tmp_h = mf->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ mf->width = tmp_w;
+ mf->height = tmp_h;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, mf);
+ dev_geo(dev, "Camera scaled to %ux%u\n",
+ mf->width, mf->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+update_cache:
+ /* Update cache */
+ ret = client_g_rect(sd, &cam->rect);
+ if (ret < 0)
+ return ret;
+
+ if (ceu_1to1)
+ cam->subrect = cam->rect;
+ else
+ update_subrect(cam);
+
+ return 0;
+}
+
+/**
+ * @width - on output: user width, mapped back to input
+ * @height - on output: user height, mapped back to input
+ * @mf - in- / output camera output window
+ */
+static int client_scale(struct soc_camera_device *icd,
+ struct v4l2_mbus_framefmt *mf,
+ unsigned int *width, unsigned int *height,
+ bool ceu_can_scale)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf_tmp = *mf;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /*
+ * 5. Apply iterative camera S_FMT for camera user window (also updates
+ * client crop cache and the imaginary sub-rectangle).
+ */
+ ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "5: camera scaled to %ux%u\n",
+ mf_tmp.width, mf_tmp.height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "mf_tmp" */
+
+ /* 7. Calculate new client scales. */
+ scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width);
+ scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height);
+
+ mf->width = mf_tmp.width;
+ mf->height = mf_tmp.height;
+ mf->colorspace = mf_tmp.colorspace;
+
+ /*
+ * 8. Calculate new CEU crop - apply camera scales to previously
+ * updated "effective" crop.
+ */
+ *width = scale_down(cam->subrect.width, scale_h);
+ *height = scale_down(cam->subrect.height, scale_v);
+
+ dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
+
+ return 0;
+}
+
+/*
+ * CEU can scale and crop, but we don't want to waste bandwidth and kill the
+ * framerate by always requesting the maximum image from the client. See
+ * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of
+ * scaling and cropping algorithms and for the meaning of referenced here steps.
+ */
+static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_rect *rect = &a->c;
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_crop cam_crop;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_rect *cam_rect = &cam_crop.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
+ out_width, out_height;
+ int interm_width, interm_height;
+ u32 capsr, cflcr;
+ int ret;
+
+ dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height,
+ rect->left, rect->top);
+
+ /* During camera cropping its output window can change too, stop CEU */
+ capsr = capture_save_reset(pcdev);
+ dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
+
+ /*
+ * 1. - 2. Apply iterative camera S_CROP for new input window, read back
+ * actual camera rectangle.
+ */
+ ret = client_s_crop(icd, a, &cam_crop);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+
+ /* On success cam_crop contains current camera crop */
+
+ /* 3. Retrieve camera output window */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.width > pcdev->max_width || mf.height > pcdev->max_height)
+ return -EINVAL;
+
+ /* 4. Calculate camera scales */
+ scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
+ scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
+
+ /* Calculate intermediate window */
+ interm_width = scale_down(rect->width, scale_cam_h);
+ interm_height = scale_down(rect->height, scale_cam_v);
+
+ if (interm_width < icd->user_width) {
+ u32 new_scale_h;
+
+ new_scale_h = calc_generic_scale(rect->width, icd->user_width);
+
+ mf.width = scale_down(cam_rect->width, new_scale_h);
+ }
+
+ if (interm_height < icd->user_height) {
+ u32 new_scale_v;
+
+ new_scale_v = calc_generic_scale(rect->height, icd->user_height);
+
+ mf.height = scale_down(cam_rect->height, new_scale_v);
+ }
+
+ if (interm_width < icd->user_width || interm_height < icd->user_height) {
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ s_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height);
+ scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
+ scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
+ interm_width = scale_down(rect->width, scale_cam_h);
+ interm_height = scale_down(rect->height, scale_cam_v);
+ }
+
+ /* Cache camera output window */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ if (pcdev->image_mode) {
+ out_width = min(interm_width, icd->user_width);
+ out_height = min(interm_height, icd->user_height);
+ } else {
+ out_width = interm_width;
+ out_height = interm_height;
+ }
+
+ /*
+ * 5. Calculate CEU scales from camera scales from results of (5) and
+ * the user window
+ */
+ scale_ceu_h = calc_scale(interm_width, &out_width);
+ scale_ceu_v = calc_scale(interm_height, &out_height);
+
+ dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
+
+ /* Apply CEU scales. */
+ cflcr = scale_ceu_h | (scale_ceu_v << 16);
+ if (cflcr != pcdev->cflcr) {
+ pcdev->cflcr = cflcr;
+ ceu_write(pcdev, CFLCR, cflcr);
+ }
+
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ /* Offsets are applied at the CEU scaling filter input */
+ 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);
+
+ cam->subrect = *rect;
+
+ dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height,
+ cam->ceu_left, cam->ceu_top);
+
+ /* Restore capture. The CE bit can be cleared by the hardware */
+ if (pcdev->active)
+ capsr |= 1;
+ capture_restore(pcdev, capsr);
+
+ /* Even if only camera cropping succeeded */
+ return ret;
+}
+
+static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = cam->subrect;
+
+ return 0;
+}
+
+/*
+ * Calculate real client output window by applying new scales to the current
+ * client crop. New scales are calculated from the requested output format and
+ * CEU crop, mapped backed onto the client input (subrect).
+ */
+static void calculate_client_output(struct soc_camera_device *icd,
+ const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct device *dev = icd->parent;
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+ unsigned int scale_v, scale_h;
+
+ if (cam_subrect->width == cam->rect.width &&
+ cam_subrect->height == cam->rect.height) {
+ /* No sub-cropping */
+ mf->width = pix->width;
+ mf->height = pix->height;
+ return;
+ }
+
+ /* 1.-2. Current camera scales and subwin - cached. */
+
+ dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
+ cam_subrect->width, cam_subrect->height,
+ cam_subrect->left, cam_subrect->top);
+
+ /*
+ * 3. Calculate new combined scales from input sub-window to requested
+ * user window.
+ */
+
+ /*
+ * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
+ * (128x96) or larger than VGA
+ */
+ scale_h = calc_generic_scale(cam_subrect->width, pix->width);
+ scale_v = calc_generic_scale(cam_subrect->height, pix->height);
+
+ dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
+
+ /*
+ * 4. Calculate desired client output window by applying combined scales
+ * to client (real) input window.
+ */
+ mf->width = scale_down(cam->rect.width, scale_h);
+ mf->height = scale_down(cam->rect.height, scale_v);
+}
+
+/* Similar to set_crop multistage iterative algorithm */
+static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct device *dev = icd->parent;
+ struct soc_camera_host *ici = to_soc_camera_host(dev);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ const struct soc_camera_format_xlate *xlate;
+ /* Keep Compiler Happy */
+ unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
+ u16 scale_v, scale_h;
+ int ret;
+ bool image_mode;
+ enum v4l2_field field;
+
+ switch (pix->field) {
+ default:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ case V4L2_FIELD_NONE:
+ field = pix->field;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ field = V4L2_FIELD_INTERLACED_TB;
+ break;
+ }
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ /* 1.-4. Calculate desired client output geometry */
+ calculate_client_output(icd, pix, &mf);
+ mf.field = pix->field;
+ mf.colorspace = pix->colorspace;
+ mf.code = xlate->code;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ image_mode = true;
+ break;
+ default:
+ image_mode = false;
+ }
+
+ dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
+ pix->width, pix->height);
+
+ dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
+
+ /* 5. - 9. */
+ ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height,
+ image_mode && V4L2_FIELD_NONE == field);
+
+ dev_geo(dev, "5-9: client scale return %d\n", ret);
+
+ /* Done with the camera. Now see if we can improve the result */
+
+ dev_geo(dev, "fmt %ux%u, requested %ux%u\n",
+ mf.width, mf.height, pix->width, pix->height);
+ if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ /* 9. Prepare CEU crop */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ /* 10. Use CEU scaling to scale to the requested user window. */
+
+ /* We cannot scale up */
+ if (pix->width > ceu_sub_width)
+ ceu_sub_width = pix->width;
+
+ if (pix->height > ceu_sub_height)
+ ceu_sub_height = pix->height;
+
+ pix->colorspace = mf.colorspace;
+
+ if (image_mode) {
+ /* Scale pix->{width x height} down to width x height */
+ scale_h = calc_scale(ceu_sub_width, &pix->width);
+ scale_v = calc_scale(ceu_sub_height, &pix->height);
+ } else {
+ pix->width = ceu_sub_width;
+ pix->height = ceu_sub_height;
+ scale_h = 0;
+ scale_v = 0;
+ }
+
+ pcdev->cflcr = scale_h | (scale_v << 16);
+
+ /*
+ * We have calculated CFLCR, the actual configuration will be performed
+ * in sh_mobile_ceu_set_bus_param()
+ */
+
+ dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
+ ceu_sub_width, scale_h, pix->width,
+ ceu_sub_height, scale_v, pix->height);
+
+ cam->code = xlate->code;
+ icd->current_fmt = xlate;
+
+ pcdev->field = field;
+ pcdev->image_mode = image_mode;
+
+ /* CFSZR requirement */
+ pix->width &= ~3;
+ pix->height &= ~3;
+
+ return 0;
+}
+
+#define CEU_CHDW_MAX 8188U /* Maximum line stride */
+
+static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int width, height;
+ int ret;
+
+ dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
+ pixfmt, pix->width, pix->height);
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ xlate = icd->current_fmt;
+ dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
+ pixfmt, xlate->host_fmt->fourcc);
+ pixfmt = xlate->host_fmt->fourcc;
+ pix->pixelformat = pixfmt;
+ pix->colorspace = icd->colorspace;
+ }
+
+ /* FIXME: calculate using depth and bus width */
+
+ /* CFSZR requires height and width to be 4-pixel aligned */
+ v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
+ &pix->height, 4, pcdev->max_height, 2, 0);
+
+ width = pix->width;
+ height = pix->height;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.code = xlate->code;
+ mf.colorspace = pix->colorspace;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
+ video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ /* FIXME: check against rect_max after converting soc-camera */
+ /* We can scale precisely, need a bigger image from camera */
+ if (pix->width < width || pix->height < height) {
+ /*
+ * We presume, the sensor behaves sanely, i.e., if
+ * requested a bigger rectangle, it will not return a
+ * smaller one.
+ */
+ mf.width = pcdev->max_width;
+ mf.height = pcdev->max_height;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd), video,
+ try_mbus_fmt, &mf);
+ if (ret < 0) {
+ /* Shouldn't actually happen... */
+ dev_err(icd->parent,
+ "FIXME: client try_fmt() = %d\n", ret);
+ return ret;
+ }
+ }
+ /* We will scale exactly */
+ if (mf.width > width)
+ pix->width = width;
+ if (mf.height > height)
+ pix->height = height;
+
+ pix->bytesperline = max(pix->bytesperline, pix->width);
+ pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX);
+ pix->bytesperline &= ~3;
+ break;
+
+ default:
+ /* Configurable stride isn't supported in pass-through mode. */
+ pix->bytesperline = 0;
+ }
+
+ pix->width &= ~3;
+ pix->height &= ~3;
+ pix->sizeimage = 0;
+
+ dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
+ __func__, ret, pix->pixelformat, pix->width, pix->height);
+
+ return ret;
+}
+
+static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ u32 out_width = icd->user_width, out_height = icd->user_height;
+ int ret;
+
+ /* Freeze queue */
+ pcdev->frozen = 1;
+ /* Wait for frame */
+ ret = wait_for_completion_interruptible(&pcdev->complete);
+ /* Stop the client */
+ ret = v4l2_subdev_call(sd, video, s_stream, 0);
+ if (ret < 0)
+ dev_warn(icd->parent,
+ "Client failed to stop the stream: %d\n", ret);
+ else
+ /* Do the crop, if it fails, there's nothing more we can do */
+ sh_mobile_ceu_set_crop(icd, a);
+
+ dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
+
+ if (icd->user_width != out_width || icd->user_height != out_height) {
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = out_width,
+ .height = out_height,
+ .pixelformat = icd->current_fmt->host_fmt->fourcc,
+ .field = pcdev->field,
+ .colorspace = icd->colorspace,
+ },
+ };
+ ret = sh_mobile_ceu_set_fmt(icd, &f);
+ if (!ret && (out_width != f.fmt.pix.width ||
+ out_height != f.fmt.pix.height))
+ ret = -EINVAL;
+ if (!ret) {
+ icd->user_width = out_width & ~3;
+ icd->user_height = out_height & ~3;
+ ret = sh_mobile_ceu_set_bus_param(icd);
+ }
+ }
+
+ /* Thaw the queue */
+ pcdev->frozen = 0;
+ spin_lock_irq(&pcdev->lock);
+ sh_mobile_ceu_capture(pcdev);
+ spin_unlock_irq(&pcdev->lock);
+ /* Start the client */
+ ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ return ret;
+}
+
+static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ 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 struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
+ .owner = THIS_MODULE,
+ .add = sh_mobile_ceu_add_device,
+ .remove = sh_mobile_ceu_remove_device,
+ .get_formats = sh_mobile_ceu_get_formats,
+ .put_formats = sh_mobile_ceu_put_formats,
+ .get_crop = sh_mobile_ceu_get_crop,
+ .set_crop = sh_mobile_ceu_set_crop,
+ .set_livecrop = sh_mobile_ceu_set_livecrop,
+ .set_fmt = sh_mobile_ceu_set_fmt,
+ .try_fmt = sh_mobile_ceu_try_fmt,
+ .poll = sh_mobile_ceu_poll,
+ .querycap = sh_mobile_ceu_querycap,
+ .set_bus_param = sh_mobile_ceu_set_bus_param,
+ .init_videobuf2 = sh_mobile_ceu_init_videobuf,
+};
+
+struct bus_wait {
+ struct notifier_block notifier;
+ struct completion completion;
+ struct device *dev;
+};
+
+static int bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct bus_wait *wait = container_of(nb, struct bus_wait, notifier);
+
+ if (wait->dev != dev)
+ return NOTIFY_DONE;
+
+ switch (action) {
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ /* Protect from module unloading */
+ wait_for_completion(&wait->completion);
+ return NOTIFY_OK;
+ }
+ return NOTIFY_DONE;
+}
+
+static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
+{
+ struct sh_mobile_ceu_dev *pcdev;
+ struct resource *res;
+ void __iomem *base;
+ unsigned int irq;
+ int err = 0;
+ struct bus_wait wait = {
+ .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
+ .notifier.notifier_call = bus_notify,
+ };
+ struct sh_mobile_ceu_companion *csi2;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
+ err = -ENODEV;
+ goto exit;
+ }
+
+ pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
+ if (!pcdev) {
+ dev_err(&pdev->dev, "Could not allocate pcdev\n");
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&pcdev->capture);
+ spin_lock_init(&pcdev->lock);
+ init_completion(&pcdev->complete);
+
+ pcdev->pdata = pdev->dev.platform_data;
+ if (!pcdev->pdata) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "CEU platform data not set.\n");
+ goto exit_kfree;
+ }
+
+ pcdev->max_width = pcdev->pdata->max_width ? : 2560;
+ pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+
+ base = ioremap_nocache(res->start, resource_size(res));
+ if (!base) {
+ err = -ENXIO;
+ dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n");
+ goto exit_kfree;
+ }
+
+ pcdev->irq = irq;
+ pcdev->base = base;
+ pcdev->video_limit = 0; /* only enabled if second resource exists */
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ err = dma_declare_coherent_memory(&pdev->dev, res->start,
+ res->start,
+ resource_size(res),
+ DMA_MEMORY_MAP |
+ DMA_MEMORY_EXCLUSIVE);
+ if (!err) {
+ dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
+ err = -ENXIO;
+ goto exit_iounmap;
+ }
+
+ pcdev->video_limit = resource_size(res);
+ }
+
+ /* request irq */
+ err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
+ dev_name(&pdev->dev), pcdev);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
+ goto exit_release_mem;
+ }
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_resume(&pdev->dev);
+
+ pcdev->ici.priv = pcdev;
+ pcdev->ici.v4l2_dev.dev = &pdev->dev;
+ pcdev->ici.nr = pdev->id;
+ pcdev->ici.drv_name = dev_name(&pdev->dev);
+ pcdev->ici.ops = &sh_mobile_ceu_host_ops;
+ pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto exit_free_clk;
+ }
+
+ err = soc_camera_host_register(&pcdev->ici);
+ if (err)
+ goto exit_free_ctx;
+
+ /* CSI2 interfacing */
+ csi2 = pcdev->pdata->csi2;
+ if (csi2) {
+ struct platform_device *csi2_pdev =
+ platform_device_alloc("sh-mobile-csi2", csi2->id);
+ struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
+
+ if (!csi2_pdev) {
+ err = -ENOMEM;
+ goto exit_host_unregister;
+ }
+
+ pcdev->csi2_pdev = csi2_pdev;
+
+ err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
+ if (err < 0)
+ goto exit_pdev_put;
+
+ csi2_pdata = csi2_pdev->dev.platform_data;
+ csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
+
+ csi2_pdev->resource = csi2->resource;
+ csi2_pdev->num_resources = csi2->num_resources;
+
+ err = platform_device_add(csi2_pdev);
+ if (err < 0)
+ goto exit_pdev_put;
+
+ wait.dev = &csi2_pdev->dev;
+
+ err = bus_register_notifier(&platform_bus_type, &wait.notifier);
+ if (err < 0)
+ goto exit_pdev_unregister;
+
+ /*
+ * From this point the driver module will not unload, until
+ * we complete the completion.
+ */
+
+ if (!csi2_pdev->dev.driver) {
+ complete(&wait.completion);
+ /* Either too late, or probing failed */
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
+ err = -ENXIO;
+ goto exit_pdev_unregister;
+ }
+
+ /*
+ * The module is still loaded, in the worst case it is hanging
+ * in device release on our completion. So, _now_ dereferencing
+ * the "owner" is safe!
+ */
+
+ err = try_module_get(csi2_pdev->dev.driver->owner);
+
+ /* Let notifier complete, if it has been locked */
+ complete(&wait.completion);
+ bus_unregister_notifier(&platform_bus_type, &wait.notifier);
+ if (!err) {
+ err = -ENODEV;
+ goto exit_pdev_unregister;
+ }
+ }
+
+ return 0;
+
+exit_pdev_unregister:
+ platform_device_del(pcdev->csi2_pdev);
+exit_pdev_put:
+ pcdev->csi2_pdev->resource = NULL;
+ platform_device_put(pcdev->csi2_pdev);
+exit_host_unregister:
+ soc_camera_host_unregister(&pcdev->ici);
+exit_free_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+exit_free_clk:
+ pm_runtime_disable(&pdev->dev);
+ free_irq(pcdev->irq, pcdev);
+exit_release_mem:
+ if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+ dma_release_declared_memory(&pdev->dev);
+exit_iounmap:
+ iounmap(base);
+exit_kfree:
+ kfree(pcdev);
+exit:
+ return err;
+}
+
+static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
+ struct sh_mobile_ceu_dev, ici);
+ struct platform_device *csi2_pdev = pcdev->csi2_pdev;
+
+ soc_camera_host_unregister(soc_host);
+ pm_runtime_disable(&pdev->dev);
+ free_irq(pcdev->irq, pcdev);
+ 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_pdev && csi2_pdev->dev.driver) {
+ struct module *csi2_drv = csi2_pdev->dev.driver->owner;
+ platform_device_del(csi2_pdev);
+ csi2_pdev->resource = NULL;
+ platform_device_put(csi2_pdev);
+ module_put(csi2_drv);
+ }
+ kfree(pcdev);
+
+ return 0;
+}
+
+static int sh_mobile_ceu_runtime_nop(struct device *dev)
+{
+ /* Runtime PM callback shared between ->runtime_suspend()
+ * and ->runtime_resume(). Simply returns success.
+ *
+ * This driver re-initializes all registers after
+ * pm_runtime_get_sync() anyway so there is no need
+ * to save and restore registers here.
+ */
+ return 0;
+}
+
+static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
+ .runtime_suspend = sh_mobile_ceu_runtime_nop,
+ .runtime_resume = sh_mobile_ceu_runtime_nop,
+};
+
+static struct platform_driver sh_mobile_ceu_driver = {
+ .driver = {
+ .name = "sh_mobile_ceu",
+ .pm = &sh_mobile_ceu_dev_pm_ops,
+ },
+ .probe = sh_mobile_ceu_probe,
+ .remove = __devexit_p(sh_mobile_ceu_remove),
+};
+
+static int __init sh_mobile_ceu_init(void)
+{
+ /* Whatever return code */
+ request_module("sh_mobile_csi2");
+ return platform_driver_register(&sh_mobile_ceu_driver);
+}
+
+static void __exit sh_mobile_ceu_exit(void)
+{
+ platform_driver_unregister(&sh_mobile_ceu_driver);
+}
+
+module_init(sh_mobile_ceu_init);
+module_exit(sh_mobile_ceu_exit);
+
+MODULE_DESCRIPTION("SuperH Mobile CEU driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.6");
+MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/video/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
index 05286500b4d4..05286500b4d4 100644
--- a/drivers/media/video/sh_mobile_csi2.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
new file mode 100644
index 000000000000..3be92944f8e7
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -0,0 +1,1605 @@
+/*
+ * camera image capture (abstract) bus driver
+ *
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * This driver provides an interface between platform-specific camera
+ * busses and camera devices. It should be used if the camera is
+ * connected not over a "proper" bus like PCI or USB, but over a
+ * special bus, like, for example, the Quick Capture interface on PXA270
+ * SoCs. Later it should also be used for i.MX31 SoCs from Freescale.
+ * It can handle multiple cameras and / or multiple busses, which can
+ * be used, e.g., in stereo-vision applications.
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/vmalloc.h>
+
+#include <media/soc_camera.h>
+#include <media/v4l2-common.h>
+#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 */
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+
+#define is_streaming(ici, icd) \
+ (((ici)->ops->init_videobuf) ? \
+ (icd)->vb_vidq.streaming : \
+ vb2_is_streaming(&(icd)->vb2_vidq))
+
+static LIST_HEAD(hosts);
+static LIST_HEAD(devices);
+static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
+
+int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl)
+{
+ int ret = regulator_bulk_enable(icl->num_regulators,
+ icl->regulators);
+ if (ret < 0) {
+ dev_err(dev, "Cannot enable regulators\n");
+ return ret;
+ }
+
+ if (icl->power) {
+ ret = icl->power(dev, 1);
+ if (ret < 0) {
+ dev_err(dev,
+ "Platform failed to power-on the camera.\n");
+ regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_power_on);
+
+int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl)
+{
+ int ret = 0;
+ int err;
+
+ if (icl->power) {
+ err = icl->power(dev, 0);
+ if (err < 0) {
+ dev_err(dev,
+ "Platform failed to power-off the camera.\n");
+ ret = err;
+ }
+ }
+
+ err = regulator_bulk_disable(icl->num_regulators,
+ icl->regulators);
+ if (err < 0) {
+ dev_err(dev, "Cannot disable regulators\n");
+ ret = ret ? : err;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_power_off);
+
+static int __soc_camera_power_on(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ return 0;
+}
+
+static int __soc_camera_power_off(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
+
+ ret = v4l2_subdev_call(sd, core, s_power, 0);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+
+ return 0;
+}
+
+const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
+ struct soc_camera_device *icd, unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < icd->num_user_formats; i++)
+ if (icd->user_formats[i].host_fmt->fourcc == fourcc)
+ return icd->user_formats + i;
+ return NULL;
+}
+EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
+
+/**
+ * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
+ * @icl: camera platform parameters
+ * @cfg: media bus configuration
+ * @return: resulting flags
+ */
+unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
+ const struct v4l2_mbus_config *cfg)
+{
+ unsigned long f, flags = cfg->flags;
+
+ /* If only one of the two polarities is supported, switch to the opposite */
+ if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
+ f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
+ f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
+ if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
+ f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
+ if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
+ }
+
+ return flags;
+}
+EXPORT_SYMBOL(soc_camera_apply_board_flags);
+
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
+static int soc_camera_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
+ if (pix->pixelformat != V4L2_PIX_FMT_JPEG &&
+ !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) {
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+ }
+
+ ret = ici->ops->try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ pix->bytesperline = max_t(u32, pix->bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline,
+ pix->height);
+ if (ret < 0)
+ return ret;
+
+ pix->sizeimage = max_t(u32, pix->sizeimage, ret);
+
+ return 0;
+}
+
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ WARN_ON(priv != file->private_data);
+
+ /* Only single-plane capture is supported so far */
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* limit format to hardware capabilities */
+ return soc_camera_try_fmt(icd, f);
+}
+
+static int soc_camera_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index != 0)
+ return -EINVAL;
+
+ /* default is camera */
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_UNKNOWN;
+ strcpy(inp->name, "Camera");
+
+ return 0;
+}
+
+static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, s_std, *a);
+}
+
+static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_std, a);
+}
+
+static int soc_camera_enum_framesizes(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->parent);
+
+ return ici->ops->enum_framesizes(icd, fsize);
+}
+
+static int soc_camera_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int ret;
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+
+ 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);
+ }
+
+ if (!ret && !icd->streamer)
+ icd->streamer = file;
+
+ return ret;
+}
+
+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->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ 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->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ 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->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ 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);
+}
+
+static int soc_camera_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_create_bufs(&icd->vb2_vidq, create);
+}
+
+static int soc_camera_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* videobuf2 only */
+ if (ici->ops->init_videobuf)
+ return -EINVAL;
+ else
+ return vb2_prepare_buf(&icd->vb2_vidq, b);
+}
+
+/* Always entered with .video_lock held */
+static int soc_camera_init_user_formats(struct soc_camera_device *icd)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ unsigned int i, fmts = 0, raw_fmts = 0;
+ int ret;
+ enum v4l2_mbus_pixelcode code;
+
+ while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code))
+ raw_fmts++;
+
+ if (!ici->ops->get_formats)
+ /*
+ * Fallback mode - the host will have to serve all
+ * sensor-provided formats one-to-one to the user
+ */
+ fmts = raw_fmts;
+ else
+ /*
+ * First pass - only count formats this host-sensor
+ * configuration can provide
+ */
+ for (i = 0; i < raw_fmts; i++) {
+ ret = ici->ops->get_formats(icd, i, NULL);
+ if (ret < 0)
+ return ret;
+ fmts += ret;
+ }
+
+ if (!fmts)
+ return -ENXIO;
+
+ icd->user_formats =
+ vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
+ if (!icd->user_formats)
+ return -ENOMEM;
+
+ dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts);
+
+ /* Second pass - actually fill data formats */
+ fmts = 0;
+ for (i = 0; i < raw_fmts; i++)
+ if (!ici->ops->get_formats) {
+ v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code);
+ icd->user_formats[fmts].host_fmt =
+ soc_mbus_get_fmtdesc(code);
+ if (icd->user_formats[fmts].host_fmt)
+ icd->user_formats[fmts++].code = code;
+ } else {
+ ret = ici->ops->get_formats(icd, i,
+ &icd->user_formats[fmts]);
+ if (ret < 0)
+ goto egfmt;
+ fmts += ret;
+ }
+
+ icd->num_user_formats = fmts;
+ icd->current_fmt = &icd->user_formats[0];
+
+ return 0;
+
+egfmt:
+ vfree(icd->user_formats);
+ return ret;
+}
+
+/* Always entered with .video_lock held */
+static void soc_camera_free_user_formats(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->put_formats)
+ ici->ops->put_formats(icd);
+ icd->current_fmt = NULL;
+ icd->num_user_formats = 0;
+ vfree(icd->user_formats);
+ icd->user_formats = NULL;
+}
+
+/* Called with .vb_lock held, or from the first open(2), see comment there */
+static int soc_camera_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ int ret;
+
+ dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
+ /* We always call try_fmt() before set_fmt() or set_crop() */
+ ret = soc_camera_try_fmt(icd, f);
+ if (ret < 0)
+ return ret;
+
+ ret = ici->ops->set_fmt(icd, f);
+ if (ret < 0) {
+ return ret;
+ } else if (!icd->current_fmt ||
+ icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
+ dev_err(icd->pdev,
+ "Host driver hasn't set up current format correctly!\n");
+ return -EINVAL;
+ }
+
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
+ icd->bytesperline = pix->bytesperline;
+ icd->sizeimage = pix->sizeimage;
+ icd->colorspace = pix->colorspace;
+ icd->field = pix->field;
+ if (ici->ops->init_videobuf)
+ icd->vb_vidq.field = pix->field;
+
+ dev_dbg(icd->pdev, "set width: %d height: %d\n",
+ icd->user_width, icd->user_height);
+
+ /* set physical bus parameters */
+ return ici->ops->set_bus_param(icd);
+}
+
+static int soc_camera_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct soc_camera_host *ici;
+ int ret;
+
+ if (!to_soc_camera_control(icd))
+ /* No device driver attached */
+ return -ENODEV;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ if (mutex_lock_interruptible(&icd->video_lock))
+ return -ERESTARTSYS;
+ if (!try_module_get(ici->ops->owner)) {
+ dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
+ ret = -EINVAL;
+ goto emodule;
+ }
+
+ icd->use_count++;
+
+ /* Now we really have to activate the camera */
+ if (icd->use_count == 1) {
+ /* Restore parameters before the last close() per V4L2 API */
+ struct v4l2_format f = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = icd->user_width,
+ .height = icd->user_height,
+ .field = icd->field,
+ .colorspace = icd->colorspace,
+ .pixelformat =
+ icd->current_fmt->host_fmt->fourcc,
+ },
+ };
+
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ /* Don't mess with the host during probe */
+ mutex_lock(&ici->host_lock);
+ ret = ici->ops->add(icd);
+ mutex_unlock(&ici->host_lock);
+ if (ret < 0) {
+ dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
+ goto eiciadd;
+ }
+
+ ret = __soc_camera_power_on(icd);
+ if (ret < 0)
+ goto epower;
+
+ pm_runtime_enable(&icd->vdev->dev);
+ ret = pm_runtime_resume(&icd->vdev->dev);
+ if (ret < 0 && ret != -ENOSYS)
+ goto eresume;
+
+ /*
+ * Try to configure with default parameters. Notice: this is the
+ * very first open, so, we cannot race against other calls,
+ * apart from someone else calling open() simultaneously, but
+ * .video_lock is protecting us against it.
+ */
+ ret = soc_camera_set_fmt(icd, &f);
+ if (ret < 0)
+ goto esfmt;
+
+ 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;
+ }
+ v4l2_ctrl_handler_setup(&icd->ctrl_handler);
+ }
+ mutex_unlock(&icd->video_lock);
+
+ file->private_data = icd;
+ dev_dbg(icd->pdev, "camera device open\n");
+
+ return 0;
+
+ /*
+ * First four errors are entered with the .video_lock held
+ * and use_count == 1
+ */
+einitvb:
+esfmt:
+ pm_runtime_disable(&icd->vdev->dev);
+eresume:
+ __soc_camera_power_off(icd);
+epower:
+ ici->ops->remove(icd);
+eiciadd:
+ icd->use_count--;
+ module_put(ici->ops->owner);
+emodule:
+ mutex_unlock(&icd->video_lock);
+
+ return ret;
+}
+
+static int soc_camera_close(struct file *file)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ mutex_lock(&icd->video_lock);
+ icd->use_count--;
+ if (!icd->use_count) {
+ pm_runtime_suspend(&icd->vdev->dev);
+ pm_runtime_disable(&icd->vdev->dev);
+
+ if (ici->ops->init_videobuf2)
+ vb2_queue_release(&icd->vb2_vidq);
+ ici->ops->remove(icd);
+
+ __soc_camera_power_off(icd);
+ }
+
+ if (icd->streamer == file)
+ icd->streamer = NULL;
+ mutex_unlock(&icd->video_lock);
+
+ module_put(ici->ops->owner);
+
+ dev_dbg(icd->pdev, "camera device close\n");
+
+ return 0;
+}
+
+static ssize_t soc_camera_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct soc_camera_device *icd = file->private_data;
+ int err = -EINVAL;
+
+ dev_err(icd->pdev, "camera device read not implemented\n");
+
+ return err;
+}
+
+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->parent);
+ int err;
+
+ dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ if (mutex_lock_interruptible(&icd->video_lock))
+ return -ERESTARTSYS;
+ if (ici->ops->init_videobuf)
+ err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
+ else
+ err = vb2_mmap(&icd->vb2_vidq, vma);
+ mutex_unlock(&icd->video_lock);
+
+ dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
+ err);
+
+ return err;
+}
+
+static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ unsigned res = POLLERR;
+
+ if (icd->streamer != file)
+ return POLLERR;
+
+ mutex_lock(&icd->video_lock);
+ if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream))
+ dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
+ else
+ res = ici->ops->poll(file, pt);
+ mutex_unlock(&icd->video_lock);
+ return res;
+}
+
+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,
+ .release = soc_camera_close,
+ .unlocked_ioctl = video_ioctl2,
+ .read = soc_camera_read,
+ .mmap = soc_camera_mmap,
+ .poll = soc_camera_poll,
+};
+
+static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type);
+ return -EINVAL;
+ }
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+
+ if (is_streaming(to_soc_camera_host(icd->parent), icd)) {
+ dev_err(icd->pdev, "S_FMT denied: queue initialised\n");
+ return -EBUSY;
+ }
+
+ ret = soc_camera_set_fmt(icd, f);
+
+ if (!ret && !icd->streamer)
+ icd->streamer = file;
+
+ return ret;
+}
+
+static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ const struct soc_mbus_pixelfmt *format;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->index >= icd->num_user_formats)
+ return -EINVAL;
+
+ format = icd->user_formats[f->index].host_fmt;
+
+ if (format->name)
+ strlcpy(f->description, format->name, sizeof(f->description));
+ f->pixelformat = format->fourcc;
+ return 0;
+}
+
+static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ WARN_ON(priv != file->private_data);
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ pix->width = icd->user_width;
+ pix->height = icd->user_height;
+ pix->bytesperline = icd->bytesperline;
+ pix->sizeimage = icd->sizeimage;
+ pix->field = icd->field;
+ pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
+ pix->colorspace = icd->colorspace;
+ dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n",
+ icd->current_fmt->host_fmt->fourcc);
+ return 0;
+}
+
+static int soc_camera_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ strlcpy(cap->driver, ici->drv_name, sizeof(cap->driver));
+ return ici->ops->querycap(ici, cap);
+}
+
+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->parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ /* This calls buf_queue from host driver's videobuf_queue_ops */
+ 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;
+}
+
+static int soc_camera_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ 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->parent);
+
+ WARN_ON(priv != file->private_data);
+
+ if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (icd->streamer != file)
+ return -EBUSY;
+
+ /*
+ * This calls buf_release from host driver's videobuf_queue_ops for all
+ * remaining buffers. When the last buffer is freed, stop capture
+ */
+ 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);
+
+ return 0;
+}
+
+static int soc_camera_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ return ici->ops->cropcap(icd, a);
+}
+
+static int soc_camera_g_crop(struct file *file, void *fh,
+ struct v4l2_crop *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ ret = ici->ops->get_crop(icd, a);
+
+ return ret;
+}
+
+/*
+ * According to the V4L2 API, drivers shall not update the struct v4l2_crop
+ * argument with the actual geometry, instead, the user shall use G_CROP to
+ * retrieve it.
+ */
+static int soc_camera_s_crop(struct file *file, void *fh,
+ const struct v4l2_crop *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ const struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop current_crop;
+ int ret;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* If get_crop fails, we'll let host and / or client drivers decide */
+ ret = ici->ops->get_crop(icd, &current_crop);
+
+ /* Prohibit window size change with initialised buffers */
+ if (ret < 0) {
+ dev_err(icd->pdev,
+ "S_CROP denied: getting current crop failed\n");
+ } else if ((a->c.width == current_crop.c.width &&
+ a->c.height == current_crop.c.height) ||
+ !is_streaming(ici, icd)) {
+ /* same size or not streaming - use .set_crop() */
+ ret = ici->ops->set_crop(icd, a);
+ } else if (ici->ops->set_livecrop) {
+ ret = ici->ops->set_livecrop(icd, a);
+ } else {
+ dev_err(icd->pdev,
+ "S_CROP denied: queue initialised and sizes differ\n");
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+static int soc_camera_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ /* With a wrong type no need to try to fall back to cropping */
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!ici->ops->get_selection)
+ return -ENOTTY;
+
+ return ici->ops->get_selection(icd, s);
+}
+
+static int soc_camera_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
+
+ /* In all these cases cropping emulation will not help */
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ (s->target != V4L2_SEL_TGT_COMPOSE_ACTIVE &&
+ s->target != V4L2_SEL_TGT_CROP_ACTIVE))
+ return -EINVAL;
+
+ if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+ /* No output size change during a running capture! */
+ if (is_streaming(ici, icd) &&
+ (icd->user_width != s->r.width ||
+ icd->user_height != s->r.height))
+ return -EBUSY;
+
+ /*
+ * Only one user is allowed to change the output format, touch
+ * buffers, start / stop streaming, poll for data
+ */
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+ }
+
+ if (!ici->ops->set_selection)
+ return -ENOTTY;
+
+ ret = ici->ops->set_selection(icd, s);
+ if (!ret &&
+ s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
+ icd->user_width = s->r.width;
+ icd->user_height = s->r.height;
+ if (!icd->streamer)
+ icd->streamer = file;
+ }
+
+ return ret;
+}
+
+static int soc_camera_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->get_parm)
+ return ici->ops->get_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
+static int soc_camera_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+
+ if (ici->ops->set_parm)
+ return ici->ops->set_parm(icd, a);
+
+ return -ENOIOCTLCMD;
+}
+
+static int soc_camera_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *id)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_chip_ident, id);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int soc_camera_g_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, g_register, reg);
+}
+
+static int soc_camera_s_register(struct file *file, void *fh,
+ struct v4l2_dbg_register *reg)
+{
+ struct soc_camera_device *icd = file->private_data;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, core, s_register, reg);
+}
+#endif
+
+static int soc_camera_probe(struct soc_camera_device *icd);
+
+/* So far this function cannot fail */
+static void scan_add_host(struct soc_camera_host *ici)
+{
+ struct soc_camera_device *icd;
+
+ mutex_lock(&ici->host_lock);
+
+ list_for_each_entry(icd, &devices, list) {
+ if (icd->iface == ici->nr) {
+ int ret;
+
+ icd->parent = ici->v4l2_dev.dev;
+ ret = soc_camera_probe(icd);
+ }
+ }
+
+ mutex_unlock(&ici->host_lock);
+}
+
+#ifdef CONFIG_I2C_BOARDINFO
+static int soc_camera_init_i2c(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
+{
+ struct i2c_client *client;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
+ struct v4l2_subdev *subdev;
+
+ if (!adap) {
+ dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
+ icl->i2c_adapter_id);
+ goto ei2cga;
+ }
+
+ icl->board_info->platform_data = icl;
+
+ subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
+ icl->board_info, NULL);
+ if (!subdev)
+ goto ei2cnd;
+
+ client = v4l2_get_subdevdata(subdev);
+
+ /* Use to_i2c_client(dev) to recover the i2c client */
+ icd->control = &client->dev;
+
+ return 0;
+ei2cnd:
+ i2c_put_adapter(adap);
+ei2cga:
+ return -ENODEV;
+}
+
+static void soc_camera_free_i2c(struct soc_camera_device *icd)
+{
+ struct i2c_client *client =
+ to_i2c_client(to_soc_camera_control(icd));
+ struct i2c_adapter *adap = client->adapter;
+
+ icd->control = NULL;
+ v4l2_device_unregister_subdev(i2c_get_clientdata(client));
+ i2c_unregister_device(client);
+ i2c_put_adapter(adap);
+}
+#else
+#define soc_camera_init_i2c(icd, icl) (-ENODEV)
+#define soc_camera_free_i2c(icd) do {} while (0)
+#endif
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
+/* Called during host-driver probe */
+static int soc_camera_probe(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct device *control = NULL;
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_framefmt mf;
+ int ret;
+
+ dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
+
+ /*
+ * Currently the subdev with the largest number of controls (13) is
+ * ov6550. So let's pick 16 as a hint for the control handler. Note
+ * that this is a hint only: too large and you waste some memory, too
+ * small and there is a (very) small performance hit when looking up
+ * controls in the internal hash.
+ */
+ ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
+ icl->regulators);
+ if (ret < 0)
+ goto ereg;
+
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+
+ /* Must have icd->vdev before registering the device */
+ ret = video_dev_create(icd);
+ if (ret < 0)
+ goto evdc;
+
+ /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
+ if (icl->board_info) {
+ ret = soc_camera_init_i2c(icd, icl);
+ if (ret < 0)
+ goto eadddev;
+ } else if (!icl->add_device || !icl->del_device) {
+ ret = -EINVAL;
+ goto eadddev;
+ } else {
+ if (icl->module_name)
+ ret = request_module(icl->module_name);
+
+ ret = icl->add_device(icd);
+ if (ret < 0)
+ goto eadddev;
+
+ /*
+ * FIXME: this is racy, have to use driver-binding notification,
+ * when it is available
+ */
+ control = to_soc_camera_control(icd);
+ if (!control || !control->driver || !dev_get_drvdata(control) ||
+ !try_module_get(control->driver->owner)) {
+ icl->del_device(icd);
+ ret = -ENODEV;
+ goto enodrv;
+ }
+ }
+
+ sd = soc_camera_to_subdev(icd);
+ sd->grp_id = soc_camera_grp_id(icd);
+ v4l2_set_subdev_hostdata(sd, icd);
+
+ if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL))
+ goto ectrl;
+
+ /* At this point client .probe() should have run already */
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
+
+ icd->field = V4L2_FIELD_ANY;
+
+ /*
+ * ..._video_start() will create a device node, video_register_device()
+ * itself is protected against concurrent open() calls, but we also have
+ * to protect our data.
+ */
+ mutex_lock(&icd->video_lock);
+
+ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ goto evidstart;
+
+ /* Try to improve our guess of a reasonable window format */
+ if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
+ icd->user_width = mf.width;
+ icd->user_height = mf.height;
+ icd->colorspace = mf.colorspace;
+ icd->field = mf.field;
+ }
+
+ ici->ops->remove(icd);
+
+ mutex_unlock(&icd->video_lock);
+
+ return 0;
+
+evidstart:
+ mutex_unlock(&icd->video_lock);
+ soc_camera_free_user_formats(icd);
+eiufmt:
+ectrl:
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ icl->del_device(icd);
+ module_put(control->driver->owner);
+ }
+enodrv:
+eadddev:
+ video_device_release(icd->vdev);
+ icd->vdev = NULL;
+evdc:
+ ici->ops->remove(icd);
+eadd:
+ regulator_bulk_free(icl->num_regulators, icl->regulators);
+ereg:
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
+ return ret;
+}
+
+/*
+ * This is called on device_unregister, which only means we have to disconnect
+ * from the host, but not remove ourselves from the device list
+ */
+static int soc_camera_remove(struct soc_camera_device *icd)
+{
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct video_device *vdev = icd->vdev;
+
+ BUG_ON(!icd->parent);
+
+ v4l2_ctrl_handler_free(&icd->ctrl_handler);
+ if (vdev) {
+ video_unregister_device(vdev);
+ icd->vdev = NULL;
+ }
+
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ struct device_driver *drv = to_soc_camera_control(icd)->driver;
+ if (drv) {
+ icl->del_device(icd);
+ module_put(drv->owner);
+ }
+ }
+ soc_camera_free_user_formats(icd);
+
+ regulator_bulk_free(icl->num_regulators, icl->regulators);
+
+ return 0;
+}
+
+static int default_cropcap(struct soc_camera_device *icd,
+ struct v4l2_cropcap *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, cropcap, a);
+}
+
+static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_crop, a);
+}
+
+static int default_s_crop(struct soc_camera_device *icd, const struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_crop, a);
+}
+
+static int default_g_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_parm, parm);
+}
+
+static int default_s_parm(struct soc_camera_device *icd,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_parm, parm);
+}
+
+static int default_enum_framesizes(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_framesizes, &fsize_mbus);
+ if (ret < 0)
+ return ret;
+
+ *fsize = fsize_mbus;
+ fsize->pixel_format = pixfmt;
+
+ return 0;
+}
+
+int soc_camera_host_register(struct soc_camera_host *ici)
+{
+ struct soc_camera_host *ix;
+ int ret;
+
+ if (!ici || !ici->ops ||
+ !ici->ops->try_fmt ||
+ !ici->ops->set_fmt ||
+ !ici->ops->set_bus_param ||
+ !ici->ops->querycap ||
+ ((!ici->ops->init_videobuf ||
+ !ici->ops->reqbufs) &&
+ !ici->ops->init_videobuf2) ||
+ !ici->ops->add ||
+ !ici->ops->remove ||
+ !ici->ops->poll ||
+ !ici->v4l2_dev.dev)
+ return -EINVAL;
+
+ if (!ici->ops->set_crop)
+ ici->ops->set_crop = default_s_crop;
+ if (!ici->ops->get_crop)
+ ici->ops->get_crop = default_g_crop;
+ if (!ici->ops->cropcap)
+ ici->ops->cropcap = default_cropcap;
+ if (!ici->ops->set_parm)
+ ici->ops->set_parm = default_s_parm;
+ if (!ici->ops->get_parm)
+ ici->ops->get_parm = default_g_parm;
+ if (!ici->ops->enum_framesizes)
+ ici->ops->enum_framesizes = default_enum_framesizes;
+
+ mutex_lock(&list_lock);
+ list_for_each_entry(ix, &hosts, list) {
+ if (ix->nr == ici->nr) {
+ ret = -EBUSY;
+ goto edevreg;
+ }
+ }
+
+ ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
+ if (ret < 0)
+ goto edevreg;
+
+ list_add_tail(&ici->list, &hosts);
+ mutex_unlock(&list_lock);
+
+ mutex_init(&ici->host_lock);
+ scan_add_host(ici);
+
+ return 0;
+
+edevreg:
+ mutex_unlock(&list_lock);
+ return ret;
+}
+EXPORT_SYMBOL(soc_camera_host_register);
+
+/* Unregister all clients! */
+void soc_camera_host_unregister(struct soc_camera_host *ici)
+{
+ struct soc_camera_device *icd;
+
+ mutex_lock(&list_lock);
+
+ list_del(&ici->list);
+ list_for_each_entry(icd, &devices, list)
+ if (icd->iface == ici->nr && to_soc_camera_control(icd))
+ soc_camera_remove(icd);
+
+ mutex_unlock(&list_lock);
+
+ v4l2_device_unregister(&ici->v4l2_dev);
+}
+EXPORT_SYMBOL(soc_camera_host_unregister);
+
+/* Image capture device */
+static int soc_camera_device_register(struct soc_camera_device *icd)
+{
+ struct soc_camera_device *ix;
+ int num = -1, i;
+
+ for (i = 0; i < 256 && num < 0; i++) {
+ num = i;
+ /* Check if this index is available on this interface */
+ list_for_each_entry(ix, &devices, list) {
+ if (ix->iface == icd->iface && ix->devnum == i) {
+ num = -1;
+ break;
+ }
+ }
+ }
+
+ if (num < 0)
+ /*
+ * ok, we have 256 cameras on this host...
+ * man, stay reasonable...
+ */
+ return -ENOMEM;
+
+ icd->devnum = num;
+ icd->use_count = 0;
+ icd->host_priv = NULL;
+ mutex_init(&icd->video_lock);
+
+ list_add_tail(&icd->list, &devices);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
+ .vidioc_querycap = soc_camera_querycap,
+ .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
+ .vidioc_enum_input = soc_camera_enum_input,
+ .vidioc_g_input = soc_camera_g_input,
+ .vidioc_s_input = soc_camera_s_input,
+ .vidioc_s_std = soc_camera_s_std,
+ .vidioc_g_std = soc_camera_g_std,
+ .vidioc_enum_framesizes = soc_camera_enum_framesizes,
+ .vidioc_reqbufs = soc_camera_reqbufs,
+ .vidioc_querybuf = soc_camera_querybuf,
+ .vidioc_qbuf = soc_camera_qbuf,
+ .vidioc_dqbuf = soc_camera_dqbuf,
+ .vidioc_create_bufs = soc_camera_create_bufs,
+ .vidioc_prepare_buf = soc_camera_prepare_buf,
+ .vidioc_streamon = soc_camera_streamon,
+ .vidioc_streamoff = soc_camera_streamoff,
+ .vidioc_cropcap = soc_camera_cropcap,
+ .vidioc_g_crop = soc_camera_g_crop,
+ .vidioc_s_crop = soc_camera_s_crop,
+ .vidioc_g_selection = soc_camera_g_selection,
+ .vidioc_s_selection = soc_camera_s_selection,
+ .vidioc_g_parm = soc_camera_g_parm,
+ .vidioc_s_parm = soc_camera_s_parm,
+ .vidioc_g_chip_ident = soc_camera_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = soc_camera_g_register,
+ .vidioc_s_register = soc_camera_s_register,
+#endif
+};
+
+static int video_dev_create(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct video_device *vdev = video_device_alloc();
+
+ if (!vdev)
+ return -ENOMEM;
+
+ strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
+
+ vdev->parent = icd->pdev;
+ vdev->current_norm = V4L2_STD_UNKNOWN;
+ vdev->fops = &soc_camera_fops;
+ vdev->ioctl_ops = &soc_camera_ioctl_ops;
+ vdev->release = video_device_release;
+ vdev->tvnorms = V4L2_STD_UNKNOWN;
+ vdev->ctrl_handler = &icd->ctrl_handler;
+ vdev->lock = &icd->video_lock;
+
+ icd->vdev = vdev;
+
+ return 0;
+}
+
+/*
+ * Called from soc_camera_probe() above (with .video_lock held???)
+ */
+static int soc_camera_video_start(struct soc_camera_device *icd)
+{
+ const struct device_type *type = icd->vdev->dev.type;
+ int ret;
+
+ if (!icd->parent)
+ return -ENODEV;
+
+ ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Restore device type, possibly set by the subdevice driver */
+ icd->vdev->dev.type = type;
+
+ return 0;
+}
+
+static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
+{
+ struct soc_camera_link *icl = pdev->dev.platform_data;
+ struct soc_camera_device *icd;
+ int ret;
+
+ if (!icl)
+ return -EINVAL;
+
+ icd = kzalloc(sizeof(*icd), GFP_KERNEL);
+ if (!icd)
+ return -ENOMEM;
+
+ icd->iface = icl->bus_id;
+ icd->link = icl;
+ icd->pdev = &pdev->dev;
+ platform_set_drvdata(pdev, icd);
+
+ ret = soc_camera_device_register(icd);
+ if (ret < 0)
+ goto escdevreg;
+
+ icd->user_width = DEFAULT_WIDTH;
+ icd->user_height = DEFAULT_HEIGHT;
+
+ return 0;
+
+escdevreg:
+ kfree(icd);
+
+ return ret;
+}
+
+/*
+ * Only called on rmmod for each platform device, since they are not
+ * hot-pluggable. Now we know, that all our users - hosts and devices have
+ * been unloaded already
+ */
+static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
+{
+ struct soc_camera_device *icd = platform_get_drvdata(pdev);
+
+ if (!icd)
+ return -EINVAL;
+
+ list_del(&icd->list);
+
+ kfree(icd);
+
+ return 0;
+}
+
+static struct platform_driver __refdata soc_camera_pdrv = {
+ .probe = soc_camera_pdrv_probe,
+ .remove = __devexit_p(soc_camera_pdrv_remove),
+ .driver = {
+ .name = "soc-camera-pdrv",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init soc_camera_init(void)
+{
+ return platform_driver_register(&soc_camera_pdrv);
+}
+
+static void __exit soc_camera_exit(void)
+{
+ platform_driver_unregister(&soc_camera_pdrv);
+}
+
+module_init(soc_camera_init);
+module_exit(soc_camera_exit);
+
+MODULE_DESCRIPTION("Image capture bus driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-camera-pdrv");
diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c
new file mode 100644
index 000000000000..7cf7fd16481f
--- /dev/null
+++ b/drivers/media/platform/soc_camera/soc_camera_platform.c
@@ -0,0 +1,206 @@
+/*
+ * Generic Platform Camera Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ * Based on mt9m001 driver,
+ * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
+ *
+ * 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/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+#include <media/soc_camera.h>
+#include <media/soc_camera_platform.h>
+
+struct soc_camera_platform_priv {
+ struct v4l2_subdev subdev;
+};
+
+static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
+{
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ return container_of(subdev, struct soc_camera_platform_priv, subdev);
+}
+
+static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+ return p->set_capture(p, enable);
+}
+
+static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ mf->width = p->format.width;
+ mf->height = p->format.height;
+ mf->code = p->format.code;
+ mf->colorspace = p->format.colorspace;
+ mf->field = p->format.field;
+
+ return 0;
+}
+
+static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ return soc_camera_set_power(p->icd->control, p->icd->link, on);
+}
+
+static struct v4l2_subdev_core_ops platform_subdev_core_ops = {
+ .s_power = soc_camera_platform_s_power,
+};
+
+static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ if (index)
+ return -EINVAL;
+
+ *code = p->format.code;
+ return 0;
+}
+
+static int soc_camera_platform_g_crop(struct v4l2_subdev *sd,
+ struct v4l2_crop *a)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = p->format.width;
+ a->c.height = p->format.height;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
+ struct v4l2_cropcap *a)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = p->format.width;
+ a->bounds.height = p->format.height;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *cfg)
+{
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+
+ cfg->flags = p->mbus_param;
+ cfg->type = p->mbus_type;
+
+ return 0;
+}
+
+static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
+ .s_stream = soc_camera_platform_s_stream,
+ .enum_mbus_fmt = soc_camera_platform_enum_fmt,
+ .cropcap = soc_camera_platform_cropcap,
+ .g_crop = soc_camera_platform_g_crop,
+ .try_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_fmt = soc_camera_platform_fill_fmt,
+ .s_mbus_fmt = soc_camera_platform_fill_fmt,
+ .g_mbus_config = soc_camera_platform_g_mbus_config,
+};
+
+static struct v4l2_subdev_ops platform_subdev_ops = {
+ .core = &platform_subdev_core_ops,
+ .video = &platform_subdev_video_ops,
+};
+
+static int soc_camera_platform_probe(struct platform_device *pdev)
+{
+ struct soc_camera_host *ici;
+ struct soc_camera_platform_priv *priv;
+ struct soc_camera_platform_info *p = pdev->dev.platform_data;
+ struct soc_camera_device *icd;
+ int ret;
+
+ if (!p)
+ return -EINVAL;
+
+ if (!p->icd) {
+ dev_err(&pdev->dev,
+ "Platform has not set soc_camera_device pointer!\n");
+ return -EINVAL;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ icd = p->icd;
+
+ /* soc-camera convention: control's drvdata points to the subdev */
+ platform_set_drvdata(pdev, &priv->subdev);
+ /* Set the control device reference */
+ icd->control = &pdev->dev;
+
+ ici = to_soc_camera_host(icd->parent);
+
+ v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, p);
+ strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
+
+ ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
+ if (ret)
+ goto evdrs;
+
+ return ret;
+
+evdrs:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return ret;
+}
+
+static int soc_camera_platform_remove(struct platform_device *pdev)
+{
+ struct soc_camera_platform_priv *priv = get_priv(pdev);
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
+
+ p->icd->control = NULL;
+ v4l2_device_unregister_subdev(&priv->subdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+static struct platform_driver soc_camera_platform_driver = {
+ .driver = {
+ .name = "soc_camera_platform",
+ .owner = THIS_MODULE,
+ },
+ .probe = soc_camera_platform_probe,
+ .remove = soc_camera_platform_remove,
+};
+
+module_platform_driver(soc_camera_platform_driver);
+
+MODULE_DESCRIPTION("SoC Camera Platform driver");
+MODULE_AUTHOR("Magnus Damm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:soc_camera_platform");
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/platform/soc_camera/soc_mediabus.c
index a397812635d6..a397812635d6 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/platform/soc_camera/soc_mediabus.c
diff --git a/drivers/media/video/timblogiw.c b/drivers/media/platform/timblogiw.c
index 02194c056b00..02194c056b00 100644
--- a/drivers/media/video/timblogiw.c
+++ b/drivers/media/platform/timblogiw.c
diff --git a/drivers/media/video/via-camera.c b/drivers/media/platform/via-camera.c
index eb404c2ce270..eb404c2ce270 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/platform/via-camera.c
diff --git a/drivers/media/video/via-camera.h b/drivers/media/platform/via-camera.h
index b12a4b3d616f..b12a4b3d616f 100644
--- a/drivers/media/video/via-camera.h
+++ b/drivers/media/platform/via-camera.h
diff --git a/drivers/media/platform/vino.c b/drivers/media/platform/vino.c
new file mode 100644
index 000000000000..790d96cffeea
--- /dev/null
+++ b/drivers/media/platform/vino.c
@@ -0,0 +1,4349 @@
+/*
+ * Driver for the VINO (Video In No Out) system found in SGI Indys.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ *
+ * Based on the previous version of the driver for 2.4 kernels by:
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ *
+ * v4l2_device/v4l2_subdev conversion by:
+ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * Note: this conversion is untested! Please contact the linux-media
+ * mailinglist if you can test this, together with the test results.
+ */
+
+/*
+ * TODO:
+ * - remove "mark pages reserved-hacks" from memory allocation code
+ * and implement fault()
+ * - check decimation, calculating and reporting image size when
+ * using decimation
+ * - implement read(), user mode buffers and overlay (?)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/kmod.h>
+
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/mutex.h>
+
+#include <asm/paccess.h>
+#include <asm/io.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/mc.h>
+
+#include "vino.h"
+#include "saa7191.h"
+#include "indycam.h"
+
+/* Uncomment the following line to get lots and lots of (mostly useless)
+ * debug info.
+ * Note that the debug output also slows down the driver significantly */
+// #define VINO_DEBUG
+// #define VINO_DEBUG_INT
+
+#define VINO_MODULE_VERSION "0.0.7"
+
+MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
+MODULE_VERSION(VINO_MODULE_VERSION);
+MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+MODULE_LICENSE("GPL");
+
+#ifdef VINO_DEBUG
+#define dprintk(x...) printk("VINO: " x);
+#else
+#define dprintk(x...)
+#endif
+
+#define VINO_NO_CHANNEL 0
+#define VINO_CHANNEL_A 1
+#define VINO_CHANNEL_B 2
+
+#define VINO_PAL_WIDTH 768
+#define VINO_PAL_HEIGHT 576
+#define VINO_NTSC_WIDTH 640
+#define VINO_NTSC_HEIGHT 480
+
+#define VINO_MIN_WIDTH 32
+#define VINO_MIN_HEIGHT 32
+
+#define VINO_CLIPPING_START_ODD_D1 1
+#define VINO_CLIPPING_START_ODD_PAL 15
+#define VINO_CLIPPING_START_ODD_NTSC 12
+
+#define VINO_CLIPPING_START_EVEN_D1 2
+#define VINO_CLIPPING_START_EVEN_PAL 15
+#define VINO_CLIPPING_START_EVEN_NTSC 12
+
+#define VINO_INPUT_CHANNEL_COUNT 3
+
+/* the number is the index for vino_inputs */
+#define VINO_INPUT_NONE -1
+#define VINO_INPUT_COMPOSITE 0
+#define VINO_INPUT_SVIDEO 1
+#define VINO_INPUT_D1 2
+
+#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE)
+
+#define VINO_FIFO_THRESHOLD_DEFAULT 16
+
+#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \
+ * VINO_PAL_HEIGHT * 4 \
+ + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
+
+#define VINO_FRAMEBUFFER_COUNT_MAX 8
+
+#define VINO_FRAMEBUFFER_UNUSED 0
+#define VINO_FRAMEBUFFER_IN_USE 1
+#define VINO_FRAMEBUFFER_READY 2
+
+#define VINO_QUEUE_ERROR -1
+#define VINO_QUEUE_MAGIC 0x20050125
+
+#define VINO_MEMORY_NONE 0
+#define VINO_MEMORY_MMAP 1
+#define VINO_MEMORY_USERPTR 2
+
+#define VINO_DUMMY_DESC_COUNT 4
+#define VINO_DESC_FETCH_DELAY 5 /* microseconds */
+
+#define VINO_MAX_FRAME_SKIP_COUNT 128
+
+/* the number is the index for vino_data_formats */
+#define VINO_DATA_FMT_NONE -1
+#define VINO_DATA_FMT_GREY 0
+#define VINO_DATA_FMT_RGB332 1
+#define VINO_DATA_FMT_RGB32 2
+#define VINO_DATA_FMT_YUV 3
+
+#define VINO_DATA_FMT_COUNT 4
+
+/* the number is the index for vino_data_norms */
+#define VINO_DATA_NORM_NONE -1
+#define VINO_DATA_NORM_NTSC 0
+#define VINO_DATA_NORM_PAL 1
+#define VINO_DATA_NORM_SECAM 2
+#define VINO_DATA_NORM_D1 3
+
+#define VINO_DATA_NORM_COUNT 4
+
+/* I2C controller flags */
+#define SGI_I2C_FORCE_IDLE (0 << 0)
+#define SGI_I2C_NOT_IDLE (1 << 0)
+#define SGI_I2C_WRITE (0 << 1)
+#define SGI_I2C_READ (1 << 1)
+#define SGI_I2C_RELEASE_BUS (0 << 2)
+#define SGI_I2C_HOLD_BUS (1 << 2)
+#define SGI_I2C_XFER_DONE (0 << 4)
+#define SGI_I2C_XFER_BUSY (1 << 4)
+#define SGI_I2C_ACK (0 << 5)
+#define SGI_I2C_NACK (1 << 5)
+#define SGI_I2C_BUS_OK (0 << 7)
+#define SGI_I2C_BUS_ERR (1 << 7)
+
+/* Internal data structure definitions */
+
+struct vino_input {
+ char *name;
+ v4l2_std_id std;
+};
+
+struct vino_clipping {
+ unsigned int left, right, top, bottom;
+};
+
+struct vino_data_format {
+ /* the description */
+ char *description;
+ /* bytes per pixel */
+ unsigned int bpp;
+ /* V4L2 fourcc code */
+ __u32 pixelformat;
+ /* V4L2 colorspace (duh!) */
+ enum v4l2_colorspace colorspace;
+};
+
+struct vino_data_norm {
+ char *description;
+ unsigned int width, height;
+ struct vino_clipping odd;
+ struct vino_clipping even;
+
+ v4l2_std_id std;
+ unsigned int fps_min, fps_max;
+ __u32 framelines;
+};
+
+struct vino_descriptor_table {
+ /* the number of PAGE_SIZE sized pages in the buffer */
+ unsigned int page_count;
+ /* virtual (kmalloc'd) pointers to the actual data
+ * (in PAGE_SIZE chunks, used with mmap streaming) */
+ unsigned long *virtual;
+
+ /* cpu address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ unsigned long *dma_cpu;
+ /* dma address for the VINO descriptor table
+ * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
+ dma_addr_t dma;
+};
+
+struct vino_framebuffer {
+ /* identifier nubmer */
+ unsigned int id;
+ /* the length of the whole buffer */
+ unsigned int size;
+ /* the length of actual data in buffer */
+ unsigned int data_size;
+ /* the data format */
+ unsigned int data_format;
+ /* the state of buffer data */
+ unsigned int state;
+ /* is the buffer mapped in user space? */
+ unsigned int map_count;
+ /* memory offset for mmap() */
+ unsigned int offset;
+ /* frame counter */
+ unsigned int frame_counter;
+ /* timestamp (written when image capture finishes) */
+ struct timeval timestamp;
+
+ struct vino_descriptor_table desc_table;
+
+ spinlock_t state_lock;
+};
+
+struct vino_framebuffer_fifo {
+ unsigned int length;
+
+ unsigned int used;
+ unsigned int head;
+ unsigned int tail;
+
+ unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
+};
+
+struct vino_framebuffer_queue {
+ unsigned int magic;
+
+ /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
+ unsigned int type;
+ unsigned int length;
+
+ /* data field of in and out contain index numbers for buffer */
+ struct vino_framebuffer_fifo in;
+ struct vino_framebuffer_fifo out;
+
+ struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
+
+ spinlock_t queue_lock;
+ struct mutex queue_mutex;
+ wait_queue_head_t frame_wait_queue;
+};
+
+struct vino_interrupt_data {
+ struct timeval timestamp;
+ unsigned int frame_counter;
+ unsigned int skip_count;
+ unsigned int skip;
+};
+
+struct vino_channel_settings {
+ unsigned int channel;
+
+ int input;
+ unsigned int data_format;
+ unsigned int data_norm;
+ struct vino_clipping clipping;
+ unsigned int decimation;
+ unsigned int line_size;
+ unsigned int alpha;
+ unsigned int fps;
+ unsigned int framert_reg;
+
+ unsigned int fifo_threshold;
+
+ struct vino_framebuffer_queue fb_queue;
+
+ /* number of the current field */
+ unsigned int field;
+
+ /* read in progress */
+ int reading;
+ /* streaming is active */
+ int streaming;
+ /* the driver is currently processing the queue */
+ int capturing;
+
+ struct mutex mutex;
+ spinlock_t capture_lock;
+
+ unsigned int users;
+
+ struct vino_interrupt_data int_data;
+
+ /* V4L support */
+ struct video_device *vdev;
+};
+
+struct vino_settings {
+ struct v4l2_device v4l2_dev;
+ struct vino_channel_settings a;
+ struct vino_channel_settings b;
+
+ /* the channel which owns this client:
+ * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
+ unsigned int decoder_owner;
+ struct v4l2_subdev *decoder;
+ unsigned int camera_owner;
+ struct v4l2_subdev *camera;
+
+ /* a lock for vino register access */
+ spinlock_t vino_lock;
+ /* a lock for channel input changes */
+ spinlock_t input_lock;
+
+ unsigned long dummy_page;
+ struct vino_descriptor_table dummy_desc_table;
+};
+
+/* Module parameters */
+
+/*
+ * Using vino_pixel_conversion the ABGR32-format pixels supplied
+ * by the VINO chip can be converted to more common formats
+ * like RGBA32 (or probably RGB24 in the future). This way we
+ * can give out data that can be specified correctly with
+ * the V4L2-definitions.
+ *
+ * The pixel format is specified as RGBA32 when no conversion
+ * is used.
+ *
+ * Note that this only affects the 32-bit bit depth.
+ *
+ * Use non-zero value to enable conversion.
+ */
+static int vino_pixel_conversion;
+
+module_param_named(pixelconv, vino_pixel_conversion, int, 0);
+
+MODULE_PARM_DESC(pixelconv,
+ "enable pixel conversion (non-zero value enables)");
+
+/* Internal data structures */
+
+static struct sgi_vino *vino;
+
+static struct vino_settings *vino_drvdata;
+
+#define camera_call(o, f, args...) \
+ v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
+#define decoder_call(o, f, args...) \
+ v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
+
+static const char *vino_driver_name = "vino";
+static const char *vino_driver_description = "SGI VINO";
+static const char *vino_bus_name = "GIO64 bus";
+static const char *vino_vdev_name_a = "SGI VINO Channel A";
+static const char *vino_vdev_name_b = "SGI VINO Channel B";
+
+static void vino_capture_tasklet(unsigned long channel);
+
+DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
+DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
+
+static const struct vino_input vino_inputs[] = {
+ {
+ .name = "Composite",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ }, {
+ .name = "S-Video",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+ }, {
+ .name = "D1/IndyCam",
+ .std = V4L2_STD_NTSC,
+ }
+};
+
+static const struct vino_data_format vino_data_formats[] = {
+ {
+ .description = "8-bit greyscale",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }, {
+ .description = "8-bit dithered RGB 3-3-2",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }, {
+ .description = "32-bit RGB",
+ .bpp = 4,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ }, {
+ .description = "YUV 4:2:2",
+ .bpp = 2,
+ .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ }
+};
+
+static const struct vino_data_norm vino_data_norms[] = {
+ {
+ .description = "NTSC",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_NTSC,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_NTSC
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ }, {
+ .description = "PAL",
+ .std = V4L2_STD_PAL,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ }, {
+ .description = "SECAM",
+ .std = V4L2_STD_SECAM,
+ .fps_min = 5,
+ .fps_max = 25,
+ .framelines = 625,
+ .width = VINO_PAL_WIDTH,
+ .height = VINO_PAL_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_PAL,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_PAL
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+ }, {
+ .description = "NTSC/D1",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+ .fps_max = 30,
+ .framelines = 525,
+ .width = VINO_NTSC_WIDTH,
+ .height = VINO_NTSC_HEIGHT,
+ .odd = {
+ .top = VINO_CLIPPING_START_ODD_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_ODD_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ .even = {
+ .top = VINO_CLIPPING_START_EVEN_D1,
+ .left = 0,
+ .bottom = VINO_CLIPPING_START_EVEN_D1
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+ }
+};
+
+#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic Gain Control",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AGC_DEFAULT,
+ }, {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic White Balance",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AWB_DEFAULT,
+ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = INDYCAM_GAIN_MIN,
+ .maximum = INDYCAM_GAIN_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAIN_DEFAULT,
+ }, {
+ .id = INDYCAM_CONTROL_RED_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Saturation",
+ .minimum = INDYCAM_RED_SATURATION_MIN,
+ .maximum = INDYCAM_RED_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_SATURATION_DEFAULT,
+ }, {
+ .id = INDYCAM_CONTROL_BLUE_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Saturation",
+ .minimum = INDYCAM_BLUE_SATURATION_MIN,
+ .maximum = INDYCAM_BLUE_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
+ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = INDYCAM_RED_BALANCE_MIN,
+ .maximum = INDYCAM_RED_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_BALANCE_DEFAULT,
+ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = INDYCAM_BLUE_BALANCE_MIN,
+ .maximum = INDYCAM_BLUE_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
+ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Shutter Control",
+ .minimum = INDYCAM_SHUTTER_MIN,
+ .maximum = INDYCAM_SHUTTER_MAX,
+ .step = 1,
+ .default_value = INDYCAM_SHUTTER_DEFAULT,
+ }, {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = INDYCAM_GAMMA_MIN,
+ .maximum = INDYCAM_GAMMA_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAMMA_DEFAULT,
+ }
+};
+
+#define VINO_SAA7191_V4L2_CONTROL_COUNT 9
+
+struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = SAA7191_HUE_MIN,
+ .maximum = SAA7191_HUE_MAX,
+ .step = 1,
+ .default_value = SAA7191_HUE_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_BANDPASS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass",
+ .minimum = SAA7191_BANDPASS_MIN,
+ .maximum = SAA7191_BANDPASS_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass Weight",
+ .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
+ .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_CORING,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HF Luminance Coring",
+ .minimum = SAA7191_CORING_MIN,
+ .maximum = SAA7191_CORING_MAX,
+ .step = 1,
+ .default_value = SAA7191_CORING_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_FORCE_COLOUR,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Force Colour",
+ .minimum = SAA7191_FORCE_COLOUR_MIN,
+ .maximum = SAA7191_FORCE_COLOUR_MAX,
+ .step = 1,
+ .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_CHROMA_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Chrominance Gain Control",
+ .minimum = SAA7191_CHROMA_GAIN_MIN,
+ .maximum = SAA7191_CHROMA_GAIN_MAX,
+ .step = 1,
+ .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_VTRC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VTR Time Constant",
+ .minimum = SAA7191_VTRC_MIN,
+ .maximum = SAA7191_VTRC_MAX,
+ .step = 1,
+ .default_value = SAA7191_VTRC_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_LUMA_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Delay Compensation",
+ .minimum = SAA7191_LUMA_DELAY_MIN,
+ .maximum = SAA7191_LUMA_DELAY_MAX,
+ .step = 1,
+ .default_value = SAA7191_LUMA_DELAY_DEFAULT,
+ }, {
+ .id = SAA7191_CONTROL_VNR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Noise Reduction",
+ .minimum = SAA7191_VNR_MIN,
+ .maximum = SAA7191_VNR_MAX,
+ .step = 1,
+ .default_value = SAA7191_VNR_DEFAULT,
+ }
+};
+
+/* VINO framebuffer/DMA descriptor management */
+
+static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
+ unsigned int count)
+{
+ unsigned int i;
+
+ dprintk("vino_free_buffer_with_count(): count = %d\n", count);
+
+ for (i = 0; i < count; i++) {
+ ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ dma_unmap_single(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ free_page(fb->desc_table.virtual[i]);
+ }
+
+ dma_free_coherent(NULL,
+ VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
+ sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
+ fb->desc_table.dma);
+ kfree(fb->desc_table.virtual);
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+}
+
+static void vino_free_buffer(struct vino_framebuffer *fb)
+{
+ vino_free_buffer_with_count(fb, fb->desc_table.page_count);
+}
+
+static int vino_allocate_buffer(struct vino_framebuffer *fb,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_allocate_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE) + 4) & ~3;
+
+ dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual =
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+ fb->data_format = VINO_DATA_FMT_NONE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+
+#if 0
+/* user buffers not fully implemented yet */
+static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
+ void *user,
+ unsigned int size)
+{
+ unsigned int count, i, j;
+ int ret = 0;
+
+ dprintk("vino_prepare_user_buffer():\n");
+
+ if (size < 1)
+ return -EINVAL;
+
+ memset(fb, 0, sizeof(struct vino_framebuffer));
+
+ count = ((size / PAGE_SIZE)) & ~3;
+
+ dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
+ size, count);
+
+ /* allocate memory for table with virtual (page) addresses */
+ fb->desc_table.virtual = (unsigned long *)
+ kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
+ if (!fb->desc_table.virtual)
+ return -ENOMEM;
+
+ /* allocate memory for table with dma addresses
+ * (has space for four extra descriptors) */
+ fb->desc_table.dma_cpu =
+ dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
+ sizeof(dma_addr_t), &fb->desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.dma_cpu) {
+ ret = -ENOMEM;
+ goto out_free_virtual;
+ }
+
+ /* allocate pages for the buffer and acquire the according
+ * dma addresses */
+ for (i = 0; i < count; i++) {
+ dma_addr_t dma_data_addr;
+
+ fb->desc_table.virtual[i] =
+ get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!fb->desc_table.virtual[i]) {
+ ret = -ENOBUFS;
+ break;
+ }
+
+ dma_data_addr =
+ dma_map_single(NULL,
+ (void *)fb->desc_table.virtual[i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+
+ for (j = 0; j < VINO_PAGE_RATIO; j++) {
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
+ dma_data_addr + VINO_PAGE_SIZE * j;
+ }
+
+ SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
+ }
+
+ /* page_count needs to be set anyway, because the descriptor table has
+ * been allocated according to this number */
+ fb->desc_table.page_count = count;
+
+ if (ret) {
+ /* the descriptor with index i doesn't contain
+ * a valid address yet */
+ vino_free_buffer_with_count(fb, i);
+ return ret;
+ }
+
+ //fb->size = size;
+ fb->size = count * PAGE_SIZE;
+
+ /* set the dma stop-bit for the last (count+1)th descriptor */
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
+ return 0;
+
+ out_free_virtual:
+ kfree(fb->desc_table.virtual);
+ return ret;
+}
+#endif
+
+static void vino_sync_buffer(struct vino_framebuffer *fb)
+{
+ int i;
+
+ dprintk("vino_sync_buffer():\n");
+
+ for (i = 0; i < fb->desc_table.page_count; i++)
+ dma_sync_single_for_cpu(NULL,
+ fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+}
+
+/* Framebuffer fifo functions (need to be locked externally) */
+
+static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
+ unsigned int length)
+{
+ f->length = 0;
+ f->used = 0;
+ f->head = 0;
+ f->tail = 0;
+
+ if (length > VINO_FRAMEBUFFER_COUNT_MAX)
+ length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ f->length = length;
+}
+
+/* returns true/false */
+static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
+ unsigned int id)
+{
+ unsigned int i;
+
+ for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
+ if (f->data[i] == id)
+ return 1;
+ }
+
+ return 0;
+}
+
+#if 0
+/* returns true/false */
+static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
+{
+ return (f->used == f->length);
+}
+#endif
+
+static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
+{
+ return f->used;
+}
+
+static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
+{
+ if (id >= f->length) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (vino_fifo_has_id(f, id)) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ if (f->used < f->length) {
+ f->data[f->tail] = id;
+ f->tail = (f->tail + 1) % f->length;
+ f->used++;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
+{
+ if (f->used > 0) {
+ *id = f->data[f->head];
+ f->head = (f->head + 1) % f->length;
+ f->used--;
+ } else {
+ return VINO_QUEUE_ERROR;
+ }
+
+ return 0;
+}
+
+/* Framebuffer queue functions */
+
+/* execute with queue_lock locked */
+static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
+ unsigned int length)
+{
+ unsigned int i;
+
+ q->length = 0;
+ memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
+ memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
+ for (i = 0; i < length; i++) {
+ dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
+ i);
+ vino_free_buffer(q->buffer[i]);
+ kfree(q->buffer[i]);
+ }
+
+ q->type = VINO_MEMORY_NONE;
+ q->magic = 0;
+}
+
+static void vino_queue_free(struct vino_framebuffer_queue *q)
+{
+ dprintk("vino_queue_free():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC)
+ return;
+ if (q->type != VINO_MEMORY_MMAP)
+ return;
+
+ mutex_lock(&q->queue_mutex);
+
+ vino_queue_free_with_count(q, q->length);
+
+ mutex_unlock(&q->queue_mutex);
+}
+
+static int vino_queue_init(struct vino_framebuffer_queue *q,
+ unsigned int *length)
+{
+ unsigned int i;
+ int ret = 0;
+
+ dprintk("vino_queue_init(): length = %d\n", *length);
+
+ if (q->magic == VINO_QUEUE_MAGIC) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (q->type != VINO_MEMORY_NONE) {
+ dprintk("vino_queue_init(): queue already initialized!\n");
+ return -EINVAL;
+ }
+
+ if (*length < 1)
+ return -EINVAL;
+
+ mutex_lock(&q->queue_mutex);
+
+ if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
+ *length = VINO_FRAMEBUFFER_COUNT_MAX;
+
+ q->length = 0;
+
+ for (i = 0; i < *length; i++) {
+ dprintk("vino_queue_init(): allocating buffer %d\n", i);
+ q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
+ GFP_KERNEL);
+ if (!q->buffer[i]) {
+ dprintk("vino_queue_init(): kmalloc() failed\n");
+ ret = -ENOMEM;
+ break;
+ }
+
+ ret = vino_allocate_buffer(q->buffer[i],
+ VINO_FRAMEBUFFER_SIZE);
+ if (ret) {
+ kfree(q->buffer[i]);
+ dprintk("vino_queue_init(): "
+ "vino_allocate_buffer() failed\n");
+ break;
+ }
+
+ q->buffer[i]->id = i;
+ if (i > 0) {
+ q->buffer[i]->offset = q->buffer[i - 1]->offset +
+ q->buffer[i - 1]->size;
+ } else {
+ q->buffer[i]->offset = 0;
+ }
+
+ spin_lock_init(&q->buffer[i]->state_lock);
+
+ dprintk("vino_queue_init(): buffer = %d, offset = %d, "
+ "size = %d\n", i, q->buffer[i]->offset,
+ q->buffer[i]->size);
+ }
+
+ if (ret) {
+ vino_queue_free_with_count(q, i);
+ *length = 0;
+ } else {
+ q->length = *length;
+ vino_fifo_init(&q->in, q->length);
+ vino_fifo_init(&q->out, q->length);
+ q->type = VINO_MEMORY_MMAP;
+ q->magic = VINO_QUEUE_MAGIC;
+ }
+
+ mutex_unlock(&q->queue_mutex);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_add(struct
+ vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned int total;
+ unsigned long flags;
+
+ dprintk("vino_queue_add(): id = %d\n", id);
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ /* not needed?: if (vino_fifo_full(&q->out)) {
+ goto out;
+ }*/
+ /* check that outgoing queue isn't already full
+ * (or that it won't become full) */
+ total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+ if (total >= q->length)
+ goto out;
+
+ if (vino_fifo_enqueue(&q->in, id))
+ goto out;
+
+ ret = q->buffer[id];
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_transfer(struct
+ vino_framebuffer_queue *q)
+{
+ struct vino_framebuffer *ret = NULL;
+ struct vino_framebuffer *fb;
+ int id;
+ unsigned long flags;
+
+ dprintk("vino_queue_transfer():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ // now this actually removes an entry from the incoming queue
+ if (vino_fifo_dequeue(&q->in, &id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_transfer(): id = %d\n", id);
+ fb = q->buffer[id];
+
+ // we have already checked that the outgoing queue is not full, but...
+ if (vino_fifo_enqueue(&q->out, id)) {
+ printk(KERN_ERR "vino_queue_transfer(): "
+ "outgoing queue is full, this shouldn't happen!\n");
+ goto out;
+ }
+
+ ret = fb;
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->in, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* returns true/false */
+static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ ret = vino_fifo_has_id(&q->out, id);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->in);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
+ unsigned int *used)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *used = vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+#if 0
+static int vino_queue_get_total(struct vino_framebuffer_queue *q,
+ unsigned int *total)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return VINO_QUEUE_ERROR;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0) {
+ ret = VINO_QUEUE_ERROR;
+ goto out;
+ }
+
+ *total = vino_fifo_get_used(&q->in) +
+ vino_fifo_get_used(&q->out);
+
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+#endif
+
+static struct vino_framebuffer *vino_queue_peek(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_peek(&q->in, id)) {
+ goto out;
+ }
+
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct vino_framebuffer *vino_queue_remove(struct
+ vino_framebuffer_queue *q,
+ unsigned int *id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+ dprintk("vino_queue_remove():\n");
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (vino_fifo_dequeue(&q->out, id)) {
+ goto out;
+ }
+
+ dprintk("vino_queue_remove(): id = %d\n", *id);
+ ret = q->buffer[*id];
+out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static struct
+vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
+ unsigned int id)
+{
+ struct vino_framebuffer *ret = NULL;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+
+ if (q->length == 0)
+ goto out;
+
+ if (id >= q->length)
+ goto out;
+
+ ret = q->buffer[id];
+ out:
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
+{
+ unsigned int length = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return length;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ length = q->length;
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return length;
+}
+
+static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
+{
+ unsigned int i;
+ int ret = 0;
+ unsigned long flags;
+
+ if (q->magic != VINO_QUEUE_MAGIC) {
+ return ret;
+ }
+
+ spin_lock_irqsave(&q->queue_lock, flags);
+ for (i = 0; i < q->length; i++) {
+ if (q->buffer[i]->map_count > 0) {
+ ret = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&q->queue_lock, flags);
+
+ return ret;
+}
+
+/* VINO functions */
+
+/* execute with input_lock locked */
+static void vino_update_line_size(struct vino_channel_settings *vcs)
+{
+ unsigned int w = vcs->clipping.right - vcs->clipping.left;
+ unsigned int d = vcs->decimation;
+ unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
+ unsigned int lsize;
+
+ dprintk("update_line_size(): before: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+
+ /* line size must be multiple of 8 bytes */
+ lsize = (bpp * (w / d)) & ~7;
+ w = (lsize / bpp) * d;
+
+ vcs->clipping.right = vcs->clipping.left + w;
+ vcs->line_size = lsize;
+
+ dprintk("update_line_size(): after: w = %d, d = %d, "
+ "line_size = %d\n", w, d, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static void vino_set_clipping(struct vino_channel_settings *vcs,
+ unsigned int x, unsigned int y,
+ unsigned int w, unsigned int h)
+{
+ unsigned int maxwidth, maxheight;
+ unsigned int d;
+
+ maxwidth = vino_data_norms[vcs->data_norm].width;
+ maxheight = vino_data_norms[vcs->data_norm].height;
+ d = vcs->decimation;
+
+ y &= ~1; /* odd/even fields */
+
+ if (x > maxwidth) {
+ x = 0;
+ }
+ if (y > maxheight) {
+ y = 0;
+ }
+
+ if (((w / d) < VINO_MIN_WIDTH)
+ || ((h / d) < VINO_MIN_HEIGHT)) {
+ w = VINO_MIN_WIDTH * d;
+ h = VINO_MIN_HEIGHT * d;
+ }
+
+ if ((x + w) > maxwidth) {
+ w = maxwidth - x;
+ if ((w / d) < VINO_MIN_WIDTH)
+ x = maxwidth - VINO_MIN_WIDTH * d;
+ }
+ if ((y + h) > maxheight) {
+ h = maxheight - y;
+ if ((h / d) < VINO_MIN_HEIGHT)
+ y = maxheight - VINO_MIN_HEIGHT * d;
+ }
+
+ vcs->clipping.left = x;
+ vcs->clipping.top = y;
+ vcs->clipping.right = x + w;
+ vcs->clipping.bottom = y + h;
+
+ vino_update_line_size(vcs);
+
+ dprintk("clipping %d, %d, %d, %d / %d - %d\n",
+ vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
+ vcs->clipping.bottom, vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
+{
+ vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
+ vino_data_norms[vcs->data_norm].height);
+}
+
+/* execute with input_lock locked */
+static void vino_set_scaling(struct vino_channel_settings *vcs,
+ unsigned int w, unsigned int h)
+{
+ unsigned int x, y, curw, curh, d;
+
+ x = vcs->clipping.left;
+ y = vcs->clipping.top;
+ curw = vcs->clipping.right - vcs->clipping.left;
+ curh = vcs->clipping.bottom - vcs->clipping.top;
+
+ d = max(curw / w, curh / h);
+
+ dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
+ w, h, curw, curh, d);
+
+ if (d < 1) {
+ d = 1;
+ } else if (d > 8) {
+ d = 8;
+ }
+
+ vcs->decimation = d;
+ vino_set_clipping(vcs, x, y, w * d, h * d);
+
+ dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
+ vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
+ vcs->decimation, vcs->line_size);
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
+{
+ vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
+ vcs->clipping.bottom - vcs->clipping.top);
+}
+
+/* execute with input_lock locked */
+static void vino_set_framerate(struct vino_channel_settings *vcs,
+ unsigned int fps)
+{
+ unsigned int mask;
+
+ switch (vcs->data_norm) {
+ case VINO_DATA_NORM_NTSC:
+ case VINO_DATA_NORM_D1:
+ fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 6:
+ mask = 0x003;
+ break;
+ case 12:
+ mask = 0x0c3;
+ break;
+ case 18:
+ mask = 0x333;
+ break;
+ case 24:
+ mask = 0x3ff;
+ break;
+ case 30:
+ mask = 0xfff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask);
+ break;
+ case VINO_DATA_NORM_PAL:
+ case VINO_DATA_NORM_SECAM:
+ fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
+
+ if (fps < vino_data_norms[vcs->data_norm].fps_min)
+ fps = vino_data_norms[vcs->data_norm].fps_min;
+ if (fps > vino_data_norms[vcs->data_norm].fps_max)
+ fps = vino_data_norms[vcs->data_norm].fps_max;
+
+ switch (fps) {
+ case 5:
+ mask = 0x003;
+ break;
+ case 10:
+ mask = 0x0c3;
+ break;
+ case 15:
+ mask = 0x333;
+ break;
+ case 20:
+ mask = 0x0ff;
+ break;
+ case 25:
+ mask = 0x3ff;
+ break;
+ default:
+ mask = VINO_FRAMERT_FULL;
+ }
+ vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
+ break;
+ }
+
+ vcs->fps = fps;
+}
+
+/* execute with input_lock locked */
+static inline void vino_set_default_framerate(struct
+ vino_channel_settings *vcs)
+{
+ vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
+}
+
+/* VINO I2C bus functions */
+
+struct i2c_algo_sgi_data {
+ void *data; /* private data for lowlevel routines */
+ unsigned (*getctrl)(void *data);
+ void (*setctrl)(void *data, unsigned val);
+ unsigned (*rdata)(void *data);
+ void (*wdata)(void *data, unsigned val);
+
+ int xfer_timeout;
+ int ack_timeout;
+};
+
+static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ for (i = 0; i < adap->xfer_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int wait_ack(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ if (wait_xfer_done(adap))
+ return -ETIMEDOUT;
+ for (i = 0; i < adap->ack_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
+ return 0;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int force_idle(struct i2c_algo_sgi_data *adap)
+{
+ int i;
+
+ adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
+ for (i = 0; i < adap->xfer_timeout; i++) {
+ if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
+ goto out;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+out:
+ if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
+ return -EIO;
+ return 0;
+}
+
+static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
+ int rd)
+{
+ if (rd)
+ adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
+ /* Check if bus is idle, eventually force it to do so */
+ if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
+ if (force_idle(adap))
+ return -EIO;
+ /* Write out the i2c chip address and specify operation */
+ adap->setctrl(adap->data,
+ SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
+ if (rd)
+ addr |= 1;
+ adap->wdata(adap->data, addr);
+ if (wait_ack(adap))
+ return -EIO;
+ return 0;
+}
+
+static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+ unsigned int len)
+{
+ int i;
+
+ adap->setctrl(adap->data,
+ SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
+ for (i = 0; i < len; i++) {
+ if (wait_xfer_done(adap))
+ return -EIO;
+ buf[i] = adap->rdata(adap->data);
+ }
+ adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
+
+ return 0;
+
+}
+
+static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
+ unsigned int len)
+{
+ int i;
+
+ /* We are already in write state */
+ for (i = 0; i < len; i++) {
+ adap->wdata(adap->data, buf[i]);
+ if (wait_ack(adap))
+ return -EIO;
+ }
+ return 0;
+}
+
+static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
+ struct i2c_msg *p;
+ int i, err = 0;
+
+ for (i = 0; !err && i < num; i++) {
+ p = &msgs[i];
+ err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+ if (err || !p->len)
+ continue;
+ if (p->flags & I2C_M_RD)
+ err = i2c_read(adap, p->buf, p->len);
+ else
+ err = i2c_write(adap, p->buf, p->len);
+ }
+
+ return (err < 0) ? err : i;
+}
+
+static u32 sgi_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm sgi_algo = {
+ .master_xfer = sgi_xfer,
+ .functionality = sgi_func,
+};
+
+static unsigned i2c_vino_getctrl(void *data)
+{
+ return vino->i2c_control;
+}
+
+static void i2c_vino_setctrl(void *data, unsigned val)
+{
+ vino->i2c_control = val;
+}
+
+static unsigned i2c_vino_rdata(void *data)
+{
+ return vino->i2c_data;
+}
+
+static void i2c_vino_wdata(void *data, unsigned val)
+{
+ vino->i2c_data = val;
+}
+
+static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
+ .getctrl = &i2c_vino_getctrl,
+ .setctrl = &i2c_vino_setctrl,
+ .rdata = &i2c_vino_rdata,
+ .wdata = &i2c_vino_wdata,
+ .xfer_timeout = 200,
+ .ack_timeout = 1000,
+};
+
+static struct i2c_adapter vino_i2c_adapter = {
+ .name = "VINO I2C bus",
+ .algo = &sgi_algo,
+ .algo_data = &i2c_sgi_vino_data,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Prepare VINO for DMA transfer...
+ * (execute only with vino_lock and input_lock locked)
+ */
+static int vino_dma_setup(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ u32 ctrl, intr;
+ struct sgi_vino_channel *ch;
+ const struct vino_data_norm *norm;
+
+ dprintk("vino_dma_setup():\n");
+
+ vcs->field = 0;
+ fb->frame_counter = 0;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+ norm = &vino_data_norms[vcs->data_norm];
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ /* VINO line size register is set 8 bytes less than actual */
+ ch->line_size = vcs->line_size - 8;
+
+ /* let VINO know where to transfer data */
+ ch->start_desc_tbl = fb->desc_table.dma;
+ ch->next_4_desc = fb->desc_table.dma;
+
+ /* give vino time to fetch the first four descriptors, 5 usec
+ * should be more than enough time */
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
+ ch->start_desc_tbl, ch->next_4_desc);
+
+ /* set the alpha register */
+ ch->alpha = vcs->alpha;
+
+ /* set clipping registers */
+ ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.top / 2) |
+ VINO_CLIP_X(vcs->clipping.left);
+ ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_EVEN(norm->even.top +
+ vcs->clipping.bottom / 2 - 1) |
+ VINO_CLIP_X(vcs->clipping.right);
+
+ /* set the size of actual content in the buffer (DECIMATION !) */
+ fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation) *
+ ((vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation) *
+ vino_data_formats[vcs->data_format].bpp;
+
+ ch->frame_rate = vcs->framert_reg;
+
+ ctrl = vino->control;
+ intr = vino->intr_status;
+
+ if (vcs->channel == VINO_CHANNEL_A) {
+ /* All interrupt conditions for this channel was cleared
+ * so clear the interrupt status register and enable
+ * interrupts */
+ intr &= ~VINO_INTSTAT_A;
+ ctrl |= VINO_CTRL_A_INT;
+
+ /* enable synchronization */
+ ctrl |= VINO_CTRL_A_SYNC_ENBL;
+
+ /* enable frame assembly */
+ ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
+
+ /* set decimation used */
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_A_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_A_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_A_DEC_SCALE_SHIFT;
+ }
+
+ /* select input interface */
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_A_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_A_SELECT;
+
+ /* palette */
+ ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
+ VINO_CTRL_A_DITHER);
+ } else {
+ intr &= ~VINO_INTSTAT_B;
+ ctrl |= VINO_CTRL_B_INT;
+
+ ctrl |= VINO_CTRL_B_SYNC_ENBL;
+ ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
+
+ if (vcs->decimation < 2)
+ ctrl &= ~VINO_CTRL_B_DEC_ENBL;
+ else {
+ ctrl |= VINO_CTRL_B_DEC_ENBL;
+ ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
+ ctrl |= (vcs->decimation - 1) <<
+ VINO_CTRL_B_DEC_SCALE_SHIFT;
+
+ }
+ if (vcs->input == VINO_INPUT_D1)
+ ctrl |= VINO_CTRL_B_SELECT;
+ else
+ ctrl &= ~VINO_CTRL_B_SELECT;
+
+ ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
+ VINO_CTRL_B_DITHER);
+ }
+
+ /* set palette */
+ fb->data_format = vcs->data_format;
+
+ switch (vcs->data_format) {
+ case VINO_DATA_FMT_GREY:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
+ break;
+ case VINO_DATA_FMT_RGB32:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
+ break;
+ case VINO_DATA_FMT_YUV:
+ /* nothing needs to be done */
+ break;
+ case VINO_DATA_FMT_RGB332:
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
+ VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
+ break;
+ }
+
+ vino->intr_status = intr;
+ vino->control = ctrl;
+
+ return 0;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_start(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ dprintk("vino_dma_start():\n");
+ ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
+ VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
+ vino->control = ctrl;
+}
+
+/* (execute only with vino_lock locked) */
+static inline void vino_dma_stop(struct vino_channel_settings *vcs)
+{
+ u32 ctrl = vino->control;
+
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
+ ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
+ ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
+ vino->control = ctrl;
+ dprintk("vino_dma_stop():\n");
+}
+
+/*
+ * Load dummy page to descriptor registers. This prevents generating of
+ * spurious interrupts. (execute only with vino_lock locked)
+ */
+static void vino_clear_interrupt(struct vino_channel_settings *vcs)
+{
+ struct sgi_vino_channel *ch;
+
+ ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
+
+ ch->page_index = 0;
+ ch->line_count = 0;
+
+ ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
+ ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
+
+ udelay(VINO_DESC_FETCH_DELAY);
+ dprintk("channel %c clear interrupt condition\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
+}
+
+static int vino_capture(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags, flags2;
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ err = -EBUSY;
+ fb->state = VINO_FRAMEBUFFER_IN_USE;
+
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (err)
+ return err;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
+
+ vino_dma_setup(vcs, fb);
+ vino_dma_start(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ return err;
+}
+
+static
+struct vino_framebuffer *vino_capture_enqueue(struct
+ vino_channel_settings *vcs,
+ unsigned int index)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ dprintk("vino_capture_enqueue():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ fb = vino_queue_add(&vcs->fb_queue, index);
+ if (fb == NULL) {
+ dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
+ "queue full?\n");
+ goto out;
+ }
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return fb;
+}
+
+static int vino_capture_next(struct vino_channel_settings *vcs, int start)
+{
+ struct vino_framebuffer *fb;
+ unsigned int incoming, id;
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_capture_next():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ if (start) {
+ /* start capture only if capture isn't in progress already */
+ if (vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+
+ } else {
+ /* capture next frame:
+ * stop capture if capturing is not set */
+ if (!vcs->capturing) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ return 0;
+ }
+ }
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_capture_next(): vino_queue_get_incoming() "
+ "failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+ if (incoming == 0) {
+ dprintk("vino_capture_next(): no buffers available\n");
+ goto out;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (fb == NULL) {
+ dprintk("vino_capture_next(): vino_queue_peek() failed\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (start) {
+ vcs->capturing = 1;
+ }
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ err = vino_capture(vcs, fb);
+
+ return err;
+
+out:
+ vcs->capturing = 0;
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return err;
+}
+
+static inline int vino_is_capturing(struct vino_channel_settings *vcs)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ ret = vcs->capturing;
+
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ return ret;
+}
+
+/* waits until a frame is captured */
+static int vino_wait_for_frame(struct vino_channel_settings *vcs)
+{
+ wait_queue_t wait;
+ int err = 0;
+
+ dprintk("vino_wait_for_frame():\n");
+
+ init_waitqueue_entry(&wait, current);
+ /* add ourselves into wait queue */
+ add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+ /* to ensure that schedule_timeout will return immediately
+ * if VINO interrupt was triggered meanwhile */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if (signal_pending(current))
+ err = -EINTR;
+
+ remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
+
+ dprintk("vino_wait_for_frame(): waiting for frame %s\n",
+ err ? "failed" : "ok");
+
+ return err;
+}
+
+/* the function assumes that PAGE_SIZE % 4 == 0 */
+static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
+ unsigned char *pageptr;
+ unsigned int page, i;
+ unsigned char a;
+
+ for (page = 0; page < fb->desc_table.page_count; page++) {
+ pageptr = (unsigned char *)fb->desc_table.virtual[page];
+
+ for (i = 0; i < PAGE_SIZE; i += 4) {
+ a = pageptr[0];
+ pageptr[0] = pageptr[3];
+ pageptr[1] = pageptr[2];
+ pageptr[2] = pageptr[1];
+ pageptr[3] = a;
+ pageptr += 4;
+ }
+ }
+}
+
+/* checks if the buffer is in correct state and syncs data */
+static int vino_check_buffer(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb)
+{
+ int err = 0;
+ unsigned long flags;
+
+ dprintk("vino_check_buffer():\n");
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ switch (fb->state) {
+ case VINO_FRAMEBUFFER_IN_USE:
+ err = -EIO;
+ break;
+ case VINO_FRAMEBUFFER_READY:
+ vino_sync_buffer(fb);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ if (!err) {
+ if (vino_pixel_conversion
+ && (fb->data_format == VINO_DATA_FMT_RGB32)) {
+ vino_convert_to_rgba(fb);
+ }
+ } else if (err && (err != -EINVAL)) {
+ dprintk("vino_check_buffer(): buffer not ready\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+ }
+
+ return err;
+}
+
+/* forcefully terminates capture */
+static void vino_capture_stop(struct vino_channel_settings *vcs)
+{
+ unsigned int incoming = 0, outgoing = 0, id;
+ unsigned long flags, flags2;
+
+ dprintk("vino_capture_stop():\n");
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+
+ /* unset capturing to stop queue processing */
+ vcs->capturing = 0;
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
+
+ /* remove all items from the queue */
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ while (incoming > 0) {
+ vino_queue_transfer(&vcs->fb_queue);
+
+ if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_incoming() failed\n");
+ goto out;
+ }
+ }
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ while (outgoing > 0) {
+ vino_queue_remove(&vcs->fb_queue, &id);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("vino_capture_stop(): "
+ "vino_queue_get_outgoing() failed\n");
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+}
+
+#if 0
+static int vino_capture_failed(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ dprintk("vino_capture_failed():\n");
+
+ spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
+
+ vino_dma_stop(vcs);
+ vino_clear_interrupt(vcs);
+
+ spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
+ if (ret == VINO_QUEUE_ERROR) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ if (i == 0) {
+ /* no buffers to process */
+ return 0;
+ }
+
+ fb = vino_queue_peek(&vcs->fb_queue, &i);
+ if (fb == NULL) {
+ dprintk("vino_queue_peek() failed\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ vino_queue_transfer(&vcs->fb_queue);
+ vino_queue_remove(&vcs->fb_queue, &i);
+ /* we should actually discard the newest frame,
+ * but who cares ... */
+ }
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ return 0;
+}
+#endif
+
+static void vino_skip_frame(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+ unsigned int id;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_peek(&vcs->fb_queue, &id);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ fb->state = VINO_FRAMEBUFFER_UNUSED;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_frame_done(struct vino_channel_settings *vcs)
+{
+ struct vino_framebuffer *fb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vcs->capture_lock, flags);
+ fb = vino_queue_transfer(&vcs->fb_queue);
+ if (!fb) {
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+ dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
+ return;
+ }
+ spin_unlock_irqrestore(&vcs->capture_lock, flags);
+
+ fb->frame_counter = vcs->int_data.frame_counter;
+ memcpy(&fb->timestamp, &vcs->int_data.timestamp,
+ sizeof(struct timeval));
+
+ spin_lock_irqsave(&fb->state_lock, flags);
+ if (fb->state == VINO_FRAMEBUFFER_IN_USE)
+ fb->state = VINO_FRAMEBUFFER_READY;
+ spin_unlock_irqrestore(&fb->state_lock, flags);
+
+ wake_up(&vcs->fb_queue.frame_wait_queue);
+
+ vino_capture_next(vcs, 0);
+}
+
+static void vino_capture_tasklet(unsigned long channel) {
+ struct vino_channel_settings *vcs;
+
+ vcs = (channel == VINO_CHANNEL_A)
+ ? &vino_drvdata->a : &vino_drvdata->b;
+
+ if (vcs->int_data.skip)
+ vcs->int_data.skip_count++;
+
+ if (vcs->int_data.skip && (vcs->int_data.skip_count
+ <= VINO_MAX_FRAME_SKIP_COUNT)) {
+ vino_skip_frame(vcs);
+ } else {
+ vcs->int_data.skip_count = 0;
+ vino_frame_done(vcs);
+ }
+}
+
+static irqreturn_t vino_interrupt(int irq, void *dev_id)
+{
+ u32 ctrl, intr;
+ unsigned int fc_a, fc_b;
+ int handled_a = 0, skip_a = 0, done_a = 0;
+ int handled_b = 0, skip_b = 0, done_b = 0;
+
+#ifdef VINO_DEBUG_INT
+ int loop = 0;
+ unsigned int line_count = vino->a.line_count,
+ page_index = vino->a.page_index,
+ field_counter = vino->a.field_counter,
+ start_desc_tbl = vino->a.start_desc_tbl,
+ next_4_desc = vino->a.next_4_desc;
+ unsigned int line_count_2,
+ page_index_2,
+ field_counter_2,
+ start_desc_tbl_2,
+ next_4_desc_2;
+#endif
+
+ spin_lock(&vino_drvdata->vino_lock);
+
+ while ((intr = vino->intr_status)) {
+ fc_a = vino->a.field_counter >> 1;
+ fc_b = vino->b.field_counter >> 1;
+
+ /* handle error-interrupts in some special way ?
+ * --> skips frames */
+ if (intr & VINO_INTSTAT_A) {
+ if (intr & VINO_INTSTAT_A_EOF) {
+ vino_drvdata->a.field++;
+ if (vino_drvdata->a.field > 1) {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ done_a = 1;
+ } else {
+ if (vino->a.page_index
+ != vino_drvdata->a.line_size) {
+ vino->a.line_count = 0;
+ vino->a.page_index =
+ vino_drvdata->
+ a.line_size;
+ vino->a.next_4_desc =
+ vino->a.start_desc_tbl;
+ }
+ }
+ dprintk("channel A end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->a);
+ vino_clear_interrupt(&vino_drvdata->a);
+ vino_drvdata->a.field = 0;
+ skip_a = 1;
+ dprintk("channel A error interrupt: %04x\n",
+ intr);
+ }
+
+#ifdef VINO_DEBUG_INT
+ line_count_2 = vino->a.line_count;
+ page_index_2 = vino->a.page_index;
+ field_counter_2 = vino->a.field_counter;
+ start_desc_tbl_2 = vino->a.start_desc_tbl;
+ next_4_desc_2 = vino->a.next_4_desc;
+
+ printk("intr = %04x, loop = %d, field = %d\n",
+ intr, loop, vino_drvdata->a.field);
+ printk("1- line count = %04d, page index = %04d, "
+ "start = %08x, next = %08x\n"
+ " fieldc = %d, framec = %d\n",
+ line_count, page_index, start_desc_tbl,
+ next_4_desc, field_counter, fc_a);
+ printk("12-line count = %04d, page index = %04d, "
+ " start = %08x, next = %08x\n",
+ line_count_2, page_index_2, start_desc_tbl_2,
+ next_4_desc_2);
+
+ if (done_a)
+ printk("\n");
+#endif
+ }
+
+ if (intr & VINO_INTSTAT_B) {
+ if (intr & VINO_INTSTAT_B_EOF) {
+ vino_drvdata->b.field++;
+ if (vino_drvdata->b.field > 1) {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ done_b = 1;
+ }
+ dprintk("channel B end-of-field "
+ "interrupt: %04x\n", intr);
+ } else {
+ vino_dma_stop(&vino_drvdata->b);
+ vino_clear_interrupt(&vino_drvdata->b);
+ vino_drvdata->b.field = 0;
+ skip_b = 1;
+ dprintk("channel B error interrupt: %04x\n",
+ intr);
+ }
+ }
+
+ /* Always remember to clear interrupt status.
+ * Disable VINO interrupts while we do this. */
+ ctrl = vino->control;
+ vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
+ vino->intr_status = ~intr;
+ vino->control = ctrl;
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ if ((!handled_a) && (done_a || skip_a)) {
+ if (!skip_a) {
+ do_gettimeofday(&vino_drvdata->
+ a.int_data.timestamp);
+ vino_drvdata->a.int_data.frame_counter = fc_a;
+ }
+ vino_drvdata->a.int_data.skip = skip_a;
+
+ dprintk("channel A %s, interrupt: %d\n",
+ skip_a ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_a);
+ handled_a = 1;
+ }
+
+ if ((!handled_b) && (done_b || skip_b)) {
+ if (!skip_b) {
+ do_gettimeofday(&vino_drvdata->
+ b.int_data.timestamp);
+ vino_drvdata->b.int_data.frame_counter = fc_b;
+ }
+ vino_drvdata->b.int_data.skip = skip_b;
+
+ dprintk("channel B %s, interrupt: %d\n",
+ skip_b ? "skipping frame" : "frame done",
+ intr);
+ tasklet_hi_schedule(&vino_tasklet_b);
+ handled_b = 1;
+ }
+
+#ifdef VINO_DEBUG_INT
+ loop++;
+#endif
+ spin_lock(&vino_drvdata->vino_lock);
+ }
+
+ spin_unlock(&vino_drvdata->vino_lock);
+
+ return IRQ_HANDLED;
+}
+
+/* VINO video input management */
+
+static int vino_get_saa7191_input(int input)
+{
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ return SAA7191_INPUT_COMPOSITE;
+ case VINO_INPUT_SVIDEO:
+ return SAA7191_INPUT_SVIDEO;
+ default:
+ printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
+ "invalid input!\n");
+ return -1;
+ }
+}
+
+/* execute with input_lock locked */
+static int vino_is_input_owner(struct vino_channel_settings *vcs)
+{
+ switch(vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ return vino_drvdata->decoder_owner == vcs->channel;
+ case VINO_INPUT_D1:
+ return vino_drvdata->camera_owner == vcs->channel;
+ default:
+ return 0;
+ }
+}
+
+static int vino_acquire_input(struct vino_channel_settings *vcs)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_acquire_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* First try D1 and then SAA7191 */
+ if (vino_drvdata->camera
+ && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
+ vino_drvdata->camera_owner = vcs->channel;
+ vcs->input = VINO_INPUT_D1;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ } else if (vino_drvdata->decoder
+ && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
+ int input;
+ int data_norm = 0;
+ v4l2_std_id norm;
+
+ input = VINO_INPUT_COMPOSITE;
+
+ ret = decoder_call(video, s_routing,
+ vino_get_saa7191_input(input), 0, 0);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ ret = decoder_call(video, querystd, &norm);
+ if (!ret) {
+ for (data_norm = 0; data_norm < 3; data_norm++) {
+ if (vino_data_norms[data_norm].std & norm)
+ break;
+ }
+ if (data_norm == 3)
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = decoder_call(core, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_drvdata->decoder_owner = vcs->channel;
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.input : vino_drvdata->a.input;
+ vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
+ vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
+ }
+
+ if (vcs->input == VINO_INPUT_NONE) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_set_input(struct vino_channel_settings *vcs, int input)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+ int ret = 0;
+
+ dprintk("vino_set_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (vcs->input == input)
+ goto out;
+
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ if (!vino_drvdata->decoder) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
+ vino_drvdata->decoder_owner = vcs->channel;
+ }
+
+ if (vino_drvdata->decoder_owner == vcs->channel) {
+ int data_norm = 0;
+ v4l2_std_id norm;
+
+ ret = decoder_call(video, s_routing,
+ vino_get_saa7191_input(input), 0, 0);
+ if (ret) {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+ ret = decoder_call(video, querystd, &norm);
+ if (!ret) {
+ for (data_norm = 0; data_norm < 3; data_norm++) {
+ if (vino_data_norms[data_norm].std & norm)
+ break;
+ }
+ if (data_norm == 3)
+ data_norm = VINO_DATA_NORM_PAL;
+ ret = decoder_call(core, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+ } else {
+ if (input != vcs2->input) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ vcs->input = input;
+ vcs->data_norm = vcs2->data_norm;
+ }
+
+ if (vino_drvdata->camera_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+ }
+ break;
+ case VINO_INPUT_D1:
+ if (!vino_drvdata->camera) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
+ vino_drvdata->camera_owner = vcs->channel;
+
+ if (vino_drvdata->decoder_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+
+ vcs->input = input;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+
+ dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static void vino_release_input(struct vino_channel_settings *vcs)
+{
+ struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
+ &vino_drvdata->b : &vino_drvdata->a;
+ unsigned long flags;
+
+ dprintk("vino_release_input():\n");
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* Release ownership of the channel
+ * and if the other channel takes input from
+ * the same source, transfer the ownership */
+ if (vino_drvdata->camera_owner == vcs->channel) {
+ if (vcs2->input == VINO_INPUT_D1) {
+ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+ } else if (vino_drvdata->decoder_owner == vcs->channel) {
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+ vcs->input = VINO_INPUT_NONE;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+}
+
+/* execute with input_lock locked */
+static int vino_set_data_norm(struct vino_channel_settings *vcs,
+ unsigned int data_norm,
+ unsigned long *flags)
+{
+ int err = 0;
+
+ if (data_norm == vcs->data_norm)
+ return 0;
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ /* only one "norm" supported */
+ if (data_norm != VINO_DATA_NORM_D1)
+ return -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ v4l2_std_id norm;
+
+ if ((data_norm != VINO_DATA_NORM_PAL)
+ && (data_norm != VINO_DATA_NORM_NTSC)
+ && (data_norm != VINO_DATA_NORM_SECAM))
+ return -EINVAL;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
+
+ /* Don't hold spinlocks while setting norm
+ * as it may take a while... */
+
+ norm = vino_data_norms[data_norm].std;
+ err = decoder_call(core, s_std, norm);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
+
+ if (err)
+ goto out;
+
+ vcs->data_norm = data_norm;
+
+ vino_set_default_clipping(vcs);
+ vino_set_default_scaling(vcs);
+ vino_set_default_framerate(vcs);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+out:
+ return err;
+}
+
+/* V4L2 helper functions */
+
+static int vino_find_data_format(__u32 pixelformat)
+{
+ int i;
+
+ for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
+ if (vino_data_formats[i].pixelformat == pixelformat)
+ return i;
+ }
+
+ return VINO_DATA_FMT_NONE;
+}
+
+static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
+{
+ int input = VINO_INPUT_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ case 2:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ } else if (vino_drvdata->decoder) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+ break;
+ case 1:
+ input = VINO_INPUT_SVIDEO;
+ break;
+ }
+ } else if (vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_D1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return input;
+}
+
+/* execute with input_lock locked */
+static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+{
+ __u32 index = 0;
+ // FIXME: detect when no inputs available
+
+ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ case VINO_INPUT_D1:
+ index = 2;
+ break;
+ }
+ } else if (vino_drvdata->decoder) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+ break;
+ case VINO_INPUT_SVIDEO:
+ index = 1;
+ break;
+ }
+ } else if (vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ index = 0;
+ break;
+ }
+ }
+
+ return index;
+}
+
+/* V4L2 ioctls */
+
+static int vino_querycap(struct file *file, void *__fh,
+ struct v4l2_capability *cap)
+{
+ memset(cap, 0, sizeof(struct v4l2_capability));
+
+ strcpy(cap->driver, vino_driver_name);
+ strcpy(cap->card, vino_driver_description);
+ strcpy(cap->bus_info, vino_bus_name);
+ cap->capabilities =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
+ return 0;
+}
+
+static int vino_enum_input(struct file *file, void *__fh,
+ struct v4l2_input *i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index = i->index;
+ int input;
+ dprintk("requested index = %d\n", index);
+
+ input = vino_int_enum_input(vcs, index);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = vino_inputs[input].std;
+ strcpy(i->name, vino_inputs[input].name);
+
+ if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
+ decoder_call(video, g_input_status, &i->status);
+ return 0;
+}
+
+static int vino_g_input(struct file *file, void *__fh,
+ unsigned int *i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index;
+ int input;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ input = vcs->input;
+ index = vino_find_input_index(vcs);
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ dprintk("input = %d\n", input);
+
+ if (input == VINO_INPUT_NONE) {
+ return -EINVAL;
+ }
+
+ *i = index;
+
+ return 0;
+}
+
+static int vino_s_input(struct file *file, void *__fh,
+ unsigned int i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int input;
+ dprintk("requested input = %d\n", i);
+
+ input = vino_int_enum_input(vcs, i);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ return vino_set_input(vcs, input);
+}
+
+static int vino_querystd(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ *std = vino_inputs[vcs->input].std;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ decoder_call(video, querystd, std);
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_g_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ *std = vino_data_norms[vcs->data_norm].std;
+ dprintk("current standard = %d\n", vcs->data_norm);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return 0;
+}
+
+static int vino_s_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* check if the standard is valid for the current input */
+ if ((*std) & vino_inputs[vcs->input].std) {
+ dprintk("standard accepted\n");
+
+ /* change the video norm for SAA7191
+ * and accept NTSC for D1 (do nothing) */
+
+ if (vcs->input == VINO_INPUT_D1)
+ goto out;
+
+ if ((*std) & V4L2_STD_PAL) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
+ &flags);
+ } else if ((*std) & V4L2_STD_NTSC) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
+ &flags);
+ } else if ((*std) & V4L2_STD_SECAM) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
+ &flags);
+ } else {
+ ret = -EINVAL;
+ }
+
+ if (ret) {
+ ret = -EINVAL;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return ret;
+}
+
+static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *fd)
+{
+ dprintk("format index = %d\n", fd->index);
+
+ if (fd->index >= VINO_DATA_FMT_COUNT)
+ return -EINVAL;
+ dprintk("format name = %s\n", vino_data_formats[fd->index].description);
+
+ fd->pixelformat = vino_data_formats[fd->index].pixelformat;
+ strcpy(fd->description, vino_data_formats[fd->index].description);
+ return 0;
+}
+
+static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_channel_settings tempvcs;
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ dprintk("requested: w = %d, h = %d\n",
+ pf->width, pf->height);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+ memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ tempvcs.data_format = vino_find_data_format(pf->pixelformat);
+ if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
+ tempvcs.data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[tempvcs.data_format].
+ pixelformat;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(&tempvcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[tempvcs.data_format].description);
+
+ pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
+ tempvcs.decimation;
+ pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = tempvcs.line_size;
+ pf->sizeimage = tempvcs.line_size *
+ (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+ tempvcs.decimation;
+ pf->colorspace =
+ vino_data_formats[tempvcs.data_format].colorspace;
+
+ pf->priv = 0;
+ return 0;
+}
+
+static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ pf->width = (vcs->clipping.right - vcs->clipping.left) /
+ vcs->decimation;
+ pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].pixelformat;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+}
+
+static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int data_format;
+ unsigned long flags;
+ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ data_format = vino_find_data_format(pf->pixelformat);
+
+ if (data_format == VINO_DATA_FMT_NONE) {
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ pf->pixelformat =
+ vino_data_formats[vcs->data_format].
+ pixelformat;
+ } else {
+ vcs->data_format = data_format;
+ }
+
+ /* data format must be set before clipping/scaling */
+ vino_set_scaling(vcs, pf->width, pf->height);
+
+ dprintk("data format = %s\n",
+ vino_data_formats[vcs->data_format].description);
+
+ pf->width = vcs->clipping.right - vcs->clipping.left;
+ pf->height = vcs->clipping.bottom - vcs->clipping.top;
+
+ pf->field = V4L2_FIELD_INTERLACED;
+ pf->bytesperline = vcs->line_size;
+ pf->sizeimage = vcs->line_size *
+ (vcs->clipping.bottom - vcs->clipping.top) /
+ vcs->decimation;
+ pf->colorspace =
+ vino_data_formats[vcs->data_format].colorspace;
+
+ pf->priv = 0;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+}
+
+static int vino_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *ccap)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ const struct vino_data_norm *norm;
+ unsigned long flags;
+
+ switch (ccap->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ norm = &vino_data_norms[vcs->data_norm];
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ ccap->bounds.left = 0;
+ ccap->bounds.top = 0;
+ ccap->bounds.width = norm->width;
+ ccap->bounds.height = norm->height;
+ memcpy(&ccap->defrect, &ccap->bounds,
+ sizeof(struct v4l2_rect));
+
+ ccap->pixelaspect.numerator = 1;
+ ccap->pixelaspect.denominator = 1;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_g_crop(struct file *file, void *__fh,
+ struct v4l2_crop *c)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ c->c.left = vcs->clipping.left;
+ c->c.top = vcs->clipping.top;
+ c->c.width = vcs->clipping.right - vcs->clipping.left;
+ c->c.height = vcs->clipping.bottom - vcs->clipping.top;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_s_crop(struct file *file, void *__fh,
+ const struct v4l2_crop *c)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ vino_set_clipping(vcs, c->c.left, c->c.top,
+ c->c.width, c->c.height);
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vino_g_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ cp->timeperframe.denominator = vcs->fps;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ /* TODO: cp->readbuffers = xxx; */
+
+ return 0;
+}
+
+static int vino_s_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if ((cp->timeperframe.numerator == 0) ||
+ (cp->timeperframe.denominator == 0)) {
+ /* reset framerate */
+ vino_set_default_framerate(vcs);
+ } else {
+ vino_set_framerate(vcs, cp->timeperframe.denominator /
+ cp->timeperframe.numerator);
+ }
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return 0;
+}
+
+static int vino_reqbufs(struct file *file, void *__fh,
+ struct v4l2_requestbuffers *rb)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (rb->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ dprintk("count = %d\n", rb->count);
+ if (rb->count > 0) {
+ if (vino_is_capturing(vcs)) {
+ dprintk("busy, capturing\n");
+ return -EBUSY;
+ }
+
+ if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
+ dprintk("busy, buffers still mapped\n");
+ return -EBUSY;
+ } else {
+ vcs->streaming = 0;
+ vino_queue_free(&vcs->fb_queue);
+ vino_queue_init(&vcs->fb_queue, &rb->count);
+ }
+ } else {
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+
+ return 0;
+}
+
+static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
+ struct vino_framebuffer *fb,
+ struct v4l2_buffer *b)
+{
+ if (vino_queue_outgoing_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_QUEUED;
+ b->flags |= V4L2_BUF_FLAG_DONE;
+ } else if (vino_queue_incoming_contains(&vcs->fb_queue,
+ fb->id)) {
+ b->flags &= ~V4L2_BUF_FLAG_DONE;
+ b->flags |= V4L2_BUF_FLAG_QUEUED;
+ } else {
+ b->flags &= ~(V4L2_BUF_FLAG_DONE |
+ V4L2_BUF_FLAG_QUEUED);
+ }
+
+ b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
+
+ if (fb->map_count > 0)
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ b->index = fb->id;
+ b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
+ V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
+ b->m.offset = fb->offset;
+ b->bytesused = fb->data_size;
+ b->length = fb->size;
+ b->field = V4L2_FIELD_INTERLACED;
+ b->sequence = fb->frame_counter;
+ memcpy(&b->timestamp, &fb->timestamp,
+ sizeof(struct timeval));
+ // b->input ?
+
+ dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
+ fb->id, fb->size, fb->data_size, fb->offset);
+}
+
+static int vino_querybuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_framebuffer *fb;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
+ dprintk("invalid index = %d\n",
+ b->index);
+ return -EINVAL;
+ }
+
+ fb = vino_queue_get_buffer(&vcs->fb_queue,
+ b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_get_buffer() failed");
+ return -EINVAL;
+ }
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ return 0;
+}
+
+static int vino_qbuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_framebuffer *fb;
+ int ret;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+ if (b->memory != V4L2_MEMORY_MMAP) {
+ dprintk("type not mmap\n");
+ return -EINVAL;
+ }
+
+ fb = vino_capture_enqueue(vcs, b->index);
+ if (fb == NULL)
+ return -EINVAL;
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (vcs->streaming) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vino_dqbuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int nonblocking = file->f_flags & O_NONBLOCK;
+ struct vino_framebuffer *fb;
+ unsigned int incoming, outgoing;
+ int err;
+
+ if (vcs->reading)
+ return -EBUSY;
+
+ /* TODO: check queue type */
+
+ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (err) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+ err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
+ if (err) {
+ dprintk("vino_queue_get_outgoing() failed\n");
+ return -EINVAL;
+ }
+
+ dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
+
+ if (outgoing == 0) {
+ if (incoming == 0) {
+ dprintk("no incoming or outgoing buffers\n");
+ return -EINVAL;
+ }
+ if (nonblocking) {
+ dprintk("non-blocking I/O was selected and "
+ "there are no buffers to dequeue\n");
+ return -EAGAIN;
+ }
+
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+ /* interrupted or no frames captured because of
+ * frame skipping */
+ /* vino_capture_failed(vcs); */
+ return -EIO;
+ }
+ }
+ }
+
+ fb = vino_queue_remove(&vcs->fb_queue, &b->index);
+ if (fb == NULL) {
+ dprintk("vino_queue_remove() failed\n");
+ return -EINVAL;
+ }
+
+ err = vino_check_buffer(vcs, fb);
+
+ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+ if (err)
+ return -EIO;
+
+ return 0;
+}
+
+static int vino_streamon(struct file *file, void *__fh,
+ enum v4l2_buf_type i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int incoming;
+ int ret;
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (vcs->streaming)
+ return 0;
+
+ // TODO: check queue type
+
+ if (vino_queue_get_length(&vcs->fb_queue) < 1) {
+ dprintk("no buffers allocated\n");
+ return -EINVAL;
+ }
+
+ ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+ if (ret) {
+ dprintk("vino_queue_get_incoming() failed\n");
+ return -EINVAL;
+ }
+
+ vcs->streaming = 1;
+
+ if (incoming > 0) {
+ ret = vino_capture_next(vcs, 1);
+ if (ret) {
+ vcs->streaming = 0;
+
+ dprintk("couldn't start capture\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int vino_streamoff(struct file *file, void *__fh,
+ enum v4l2_buf_type i)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ if (vcs->reading)
+ return -EBUSY;
+
+ if (!vcs->streaming)
+ return 0;
+
+ vcs->streaming = 0;
+ vino_capture_stop(vcs);
+
+ return 0;
+}
+
+static int vino_queryctrl(struct file *file, void *__fh,
+ struct v4l2_queryctrl *queryctrl)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_indycam_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id ==
+ queryctrl->id) {
+ memcpy(queryctrl,
+ &vino_saa7191_v4l2_controls[i],
+ sizeof(struct v4l2_queryctrl));
+ queryctrl->reserved[0] = 0;
+ goto found;
+ }
+ }
+
+ err = -EINVAL;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ found:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_g_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+
+ if (err)
+ goto out;
+
+ err = camera_call(core, g_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+
+ if (err)
+ goto out;
+
+ err = decoder_call(core, g_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+static int vino_s_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (!vino_is_input_owner(vcs)) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+ if (vino_indycam_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+ if (err)
+ goto out;
+ if (control->value < vino_indycam_v4l2_controls[i].minimum ||
+ control->value > vino_indycam_v4l2_controls[i].maximum) {
+ err = -ERANGE;
+ goto out;
+ }
+ err = camera_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+ if (vino_saa7191_v4l2_controls[i].id == control->id) {
+ err = 0;
+ break;
+ }
+ }
+ if (err)
+ goto out;
+ if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
+ control->value > vino_saa7191_v4l2_controls[i].maximum) {
+ err = -ERANGE;
+ goto out;
+ }
+
+ err = decoder_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+ return err;
+}
+
+/* File operations */
+
+static int vino_open(struct file *file)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ int ret = 0;
+ dprintk("open(): channel = %c\n",
+ (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
+
+ mutex_lock(&vcs->mutex);
+
+ if (vcs->users) {
+ dprintk("open(): driver busy\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = vino_acquire_input(vcs);
+ if (ret) {
+ dprintk("open(): vino_acquire_input() failed\n");
+ goto out;
+ }
+
+ vcs->users++;
+
+ out:
+ mutex_unlock(&vcs->mutex);
+
+ dprintk("open(): %s!\n", ret ? "failed" : "complete");
+
+ return ret;
+}
+
+static int vino_close(struct file *file)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ dprintk("close():\n");
+
+ mutex_lock(&vcs->mutex);
+
+ vcs->users--;
+
+ if (!vcs->users) {
+ vino_release_input(vcs);
+
+ /* stop DMA and free buffers */
+ vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
+ }
+
+ mutex_unlock(&vcs->mutex);
+
+ return 0;
+}
+
+static void vino_vm_open(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count++;
+ dprintk("vino_vm_open(): count = %d\n", fb->map_count);
+}
+
+static void vino_vm_close(struct vm_area_struct *vma)
+{
+ struct vino_framebuffer *fb = vma->vm_private_data;
+
+ fb->map_count--;
+ dprintk("vino_vm_close(): count = %d\n", fb->map_count);
+}
+
+static const struct vm_operations_struct vino_vm_ops = {
+ .open = vino_vm_open,
+ .close = vino_vm_close,
+};
+
+static int vino_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+
+ struct vino_framebuffer *fb = NULL;
+ unsigned int i, length;
+ int ret = 0;
+
+ dprintk("mmap():\n");
+
+ // TODO: reject mmap if already mapped
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ if (vcs->reading) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ // TODO: check queue type
+
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk("mmap(): app bug: PROT_WRITE please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!(vma->vm_flags & VM_SHARED)) {
+ dprintk("mmap(): app bug: MAP_SHARED please\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* find the correct buffer using offset */
+ length = vino_queue_get_length(&vcs->fb_queue);
+ if (length == 0) {
+ dprintk("mmap(): queue not initialized\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < length; i++) {
+ fb = vino_queue_get_buffer(&vcs->fb_queue, i);
+ if (fb == NULL) {
+ dprintk("mmap(): vino_queue_get_buffer() failed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (fb->offset == offset)
+ goto found;
+ }
+
+ dprintk("mmap(): invalid offset = %lu\n", offset);
+ ret = -EINVAL;
+ goto out;
+
+found:
+ dprintk("mmap(): buffer = %d\n", i);
+
+ if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
+ dprintk("mmap(): failed: size = %lu > %lu\n",
+ size, fb->desc_table.page_count * PAGE_SIZE);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < fb->desc_table.page_count; i++) {
+ unsigned long pfn =
+ virt_to_phys((void *)fb->desc_table.virtual[i]) >>
+ PAGE_SHIFT;
+
+ if (size < PAGE_SIZE)
+ break;
+
+ // protection was: PAGE_READONLY
+ if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
+ vma->vm_page_prot)) {
+ dprintk("mmap(): remap_pfn_range() failed\n");
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ start += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ fb->map_count = 1;
+
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO;
+ vma->vm_private_data = fb;
+ vma->vm_file = file;
+ vma->vm_ops = &vino_vm_ops;
+
+out:
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+static unsigned int vino_poll(struct file *file, poll_table *pt)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int outgoing;
+ unsigned int ret = 0;
+
+ // lock mutex (?)
+ // TODO: this has to be corrected for different read modes
+
+ dprintk("poll():\n");
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+ if (outgoing > 0)
+ goto over;
+
+ poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
+
+ if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
+ dprintk("poll(): vino_queue_get_outgoing() failed\n");
+ ret = POLLERR;
+ goto error;
+ }
+
+over:
+ dprintk("poll(): data %savailable\n",
+ (outgoing > 0) ? "" : "not ");
+
+ if (outgoing > 0)
+ ret = POLLIN | POLLRDNORM;
+
+error:
+ return ret;
+}
+
+static long vino_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vino_channel_settings *vcs = video_drvdata(file);
+ long ret;
+
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+ ret = video_ioctl2(file, cmd, arg);
+
+ mutex_unlock(&vcs->mutex);
+
+ return ret;
+}
+
+/* Initialization and cleanup */
+
+/* __initdata */
+static int vino_init_stage;
+
+const struct v4l2_ioctl_ops vino_ioctl_ops = {
+ .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap,
+ .vidioc_querycap = vino_querycap,
+ .vidioc_enum_input = vino_enum_input,
+ .vidioc_g_input = vino_g_input,
+ .vidioc_s_input = vino_s_input,
+ .vidioc_g_std = vino_g_std,
+ .vidioc_s_std = vino_s_std,
+ .vidioc_querystd = vino_querystd,
+ .vidioc_cropcap = vino_cropcap,
+ .vidioc_s_crop = vino_s_crop,
+ .vidioc_g_crop = vino_g_crop,
+ .vidioc_s_parm = vino_s_parm,
+ .vidioc_g_parm = vino_g_parm,
+ .vidioc_reqbufs = vino_reqbufs,
+ .vidioc_querybuf = vino_querybuf,
+ .vidioc_qbuf = vino_qbuf,
+ .vidioc_dqbuf = vino_dqbuf,
+ .vidioc_streamon = vino_streamon,
+ .vidioc_streamoff = vino_streamoff,
+ .vidioc_queryctrl = vino_queryctrl,
+ .vidioc_g_ctrl = vino_g_ctrl,
+ .vidioc_s_ctrl = vino_s_ctrl,
+};
+
+static const struct v4l2_file_operations vino_fops = {
+ .owner = THIS_MODULE,
+ .open = vino_open,
+ .release = vino_close,
+ .unlocked_ioctl = vino_ioctl,
+ .mmap = vino_mmap,
+ .poll = vino_poll,
+};
+
+static struct video_device vdev_template = {
+ .name = "NOT SET",
+ .fops = &vino_fops,
+ .ioctl_ops = &vino_ioctl_ops,
+ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+};
+
+static void vino_module_cleanup(int stage)
+{
+ switch(stage) {
+ case 11:
+ video_unregister_device(vino_drvdata->b.vdev);
+ vino_drvdata->b.vdev = NULL;
+ case 10:
+ video_unregister_device(vino_drvdata->a.vdev);
+ vino_drvdata->a.vdev = NULL;
+ case 9:
+ i2c_del_adapter(&vino_i2c_adapter);
+ case 8:
+ free_irq(SGI_VINO_IRQ, NULL);
+ case 7:
+ if (vino_drvdata->b.vdev) {
+ video_device_release(vino_drvdata->b.vdev);
+ vino_drvdata->b.vdev = NULL;
+ }
+ case 6:
+ if (vino_drvdata->a.vdev) {
+ video_device_release(vino_drvdata->a.vdev);
+ vino_drvdata->a.vdev = NULL;
+ }
+ case 5:
+ /* all entries in dma_cpu dummy table have the same address */
+ dma_unmap_single(NULL,
+ vino_drvdata->dummy_desc_table.dma_cpu[0],
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
+ * sizeof(dma_addr_t),
+ (void *)vino_drvdata->
+ dummy_desc_table.dma_cpu,
+ vino_drvdata->dummy_desc_table.dma);
+ case 4:
+ free_page(vino_drvdata->dummy_page);
+ case 3:
+ v4l2_device_unregister(&vino_drvdata->v4l2_dev);
+ case 2:
+ kfree(vino_drvdata);
+ case 1:
+ iounmap(vino);
+ case 0:
+ break;
+ default:
+ dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
+ stage);
+ }
+}
+
+static int vino_probe(void)
+{
+ unsigned long rev_id;
+
+ if (ip22_is_fullhouse()) {
+ printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
+ return -ENODEV;
+ }
+
+ if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+ printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
+ return -ENODEV;
+ }
+
+ vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
+ if (!vino) {
+ printk(KERN_ERR "VINO: ioremap() failed\n");
+ return -EIO;
+ }
+ vino_init_stage++;
+
+ if (get_dbe(rev_id, &(vino->rev_id))) {
+ printk(KERN_ERR "Failed to read VINO revision register\n");
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
+ printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
+ rev_id);
+ vino_module_cleanup(vino_init_stage);
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
+
+ return 0;
+}
+
+static int vino_init(void)
+{
+ dma_addr_t dma_dummy_address;
+ int err;
+ int i;
+
+ vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
+ if (!vino_drvdata) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+ strlcpy(vino_drvdata->v4l2_dev.name, "vino",
+ sizeof(vino_drvdata->v4l2_dev.name));
+ err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
+ if (err)
+ return err;
+ vino_init_stage++;
+
+ /* create a dummy dma descriptor */
+ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_page) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ // TODO: use page_count in dummy_desc_table
+
+ vino_drvdata->dummy_desc_table.dma_cpu =
+ dma_alloc_coherent(NULL,
+ VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
+ &vino_drvdata->dummy_desc_table.dma,
+ GFP_KERNEL | GFP_DMA);
+ if (!vino_drvdata->dummy_desc_table.dma_cpu) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ dma_dummy_address = dma_map_single(NULL,
+ (void *)vino_drvdata->dummy_page,
+ PAGE_SIZE, DMA_FROM_DEVICE);
+ for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
+ vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
+ }
+
+ /* initialize VINO */
+
+ vino->control = 0;
+ vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
+ udelay(VINO_DESC_FETCH_DELAY);
+
+ vino->intr_status = 0;
+
+ vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+ vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
+
+ return 0;
+}
+
+static int vino_init_channel_settings(struct vino_channel_settings *vcs,
+ unsigned int channel, const char *name)
+{
+ vcs->channel = channel;
+ vcs->input = VINO_INPUT_NONE;
+ vcs->alpha = 0;
+ vcs->users = 0;
+ vcs->data_format = VINO_DATA_FMT_GREY;
+ vcs->data_norm = VINO_DATA_NORM_NTSC;
+ vcs->decimation = 1;
+ vino_set_default_clipping(vcs);
+ vino_set_default_framerate(vcs);
+
+ vcs->capturing = 0;
+
+ mutex_init(&vcs->mutex);
+ spin_lock_init(&vcs->capture_lock);
+
+ mutex_init(&vcs->fb_queue.queue_mutex);
+ spin_lock_init(&vcs->fb_queue.queue_lock);
+ init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
+
+ vcs->vdev = video_device_alloc();
+ if (!vcs->vdev) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+ memcpy(vcs->vdev, &vdev_template,
+ sizeof(struct video_device));
+ strcpy(vcs->vdev->name, name);
+ vcs->vdev->release = video_device_release;
+ vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
+
+ video_set_drvdata(vcs->vdev, vcs);
+
+ return 0;
+}
+
+static int __init vino_module_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "SGI VINO driver version %s\n",
+ VINO_MODULE_VERSION);
+
+ ret = vino_probe();
+ if (ret)
+ return ret;
+
+ ret = vino_init();
+ if (ret)
+ return ret;
+
+ /* initialize data structures */
+
+ spin_lock_init(&vino_drvdata->vino_lock);
+ spin_lock_init(&vino_drvdata->input_lock);
+
+ ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
+ vino_vdev_name_a);
+ if (ret)
+ return ret;
+
+ ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
+ vino_vdev_name_b);
+ if (ret)
+ return ret;
+
+ /* initialize hardware and register V4L devices */
+
+ ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
+ vino_driver_description, NULL);
+ if (ret) {
+ printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
+ SGI_VINO_IRQ);
+ vino_module_cleanup(vino_init_stage);
+ return -EAGAIN;
+ }
+ vino_init_stage++;
+
+ ret = i2c_add_adapter(&vino_i2c_adapter);
+ if (ret) {
+ printk(KERN_ERR "VINO I2C bus registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return ret;
+ }
+ i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->a.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel A Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+ ret = video_register_device(vino_drvdata->b.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel B Video4Linux-device "
+ "registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return -EINVAL;
+ }
+ vino_init_stage++;
+
+ vino_drvdata->decoder =
+ v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+ "saa7191", 0, I2C_ADDRS(0x45));
+ vino_drvdata->camera =
+ v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
+ "indycam", 0, I2C_ADDRS(0x2b));
+
+ dprintk("init complete!\n");
+
+ return 0;
+}
+
+static void __exit vino_module_exit(void)
+{
+ dprintk("exiting, stage = %d ...\n", vino_init_stage);
+ vino_module_cleanup(vino_init_stage);
+ dprintk("cleanup complete, exit!\n");
+}
+
+module_init(vino_module_init);
+module_exit(vino_module_exit);
diff --git a/drivers/media/video/vino.h b/drivers/media/platform/vino.h
index de2d615ae7c9..de2d615ae7c9 100644
--- a/drivers/media/video/vino.h
+++ b/drivers/media/platform/vino.h
diff --git a/drivers/media/platform/vivi.c b/drivers/media/platform/vivi.c
new file mode 100644
index 000000000000..b366b050a3dd
--- /dev/null
+++ b/drivers/media/platform/vivi.c
@@ -0,0 +1,1411 @@
+/*
+ * Virtual Video driver - This code emulates a real video device with v4l2 api
+ *
+ * Copyright (c) 2006 by:
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * 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
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/mutex.h>
+#include <linux/videodev2.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-common.h>
+
+#define VIVI_MODULE_NAME "vivi"
+
+/* Wake up at about 30 fps */
+#define WAKE_NUMERATOR 30
+#define WAKE_DENOMINATOR 1001
+#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1200
+
+#define VIVI_VERSION "0.8.1"
+
+MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
+MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(VIVI_VERSION);
+
+static unsigned video_nr = -1;
+module_param(video_nr, uint, 0644);
+MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
+
+static unsigned n_devs = 1;
+module_param(n_devs, uint, 0644);
+MODULE_PARM_DESC(n_devs, "number of video devices to create");
+
+static unsigned debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, uint, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+/* Global font descriptor */
+static const u8 *font8x16;
+
+#define dprintk(dev, level, fmt, arg...) \
+ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
+
+/* ------------------------------------------------------------------
+ Basic structures
+ ------------------------------------------------------------------*/
+
+struct vivi_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ u8 depth;
+ bool is_yuv;
+};
+
+static struct vivi_fmt formats[] = {
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .is_yuv = true,
+ },
+ {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ .is_yuv = true,
+ },
+ {
+ .name = "4:2:2, packed, YVYU",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ .is_yuv = true,
+ },
+ {
+ .name = "4:2:2, packed, VYUY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ .is_yuv = true,
+ },
+ {
+ .name = "RGB565 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .depth = 16,
+ },
+ {
+ .name = "RGB565 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .depth = 16,
+ },
+ {
+ .name = "RGB555 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .depth = 16,
+ },
+ {
+ .name = "RGB555 (BE)",
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .depth = 16,
+ },
+ {
+ .name = "RGB24 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .depth = 24,
+ },
+ {
+ .name = "RGB24 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .depth = 24,
+ },
+ {
+ .name = "RGB32 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .depth = 32,
+ },
+ {
+ .name = "RGB32 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */
+ .depth = 32,
+ },
+};
+
+static struct vivi_fmt *get_format(struct v4l2_format *f)
+{
+ struct vivi_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < ARRAY_SIZE(formats); k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == ARRAY_SIZE(formats))
+ return NULL;
+
+ return &formats[k];
+}
+
+/* buffer for one video frame */
+struct vivi_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+ struct vivi_fmt *fmt;
+};
+
+struct vivi_dmaqueue {
+ struct list_head active;
+
+ /* thread for generating video stream*/
+ struct task_struct *kthread;
+ wait_queue_head_t wq;
+ /* Counters to control fps rate */
+ int frame;
+ int ini_jiffies;
+};
+
+static LIST_HEAD(vivi_devlist);
+
+struct vivi_dev {
+ struct list_head vivi_devlist;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct video_device vdev;
+
+ /* controls */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ struct {
+ /* autogain/gain cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *alpha;
+ struct v4l2_ctrl *button;
+ struct v4l2_ctrl *boolean;
+ struct v4l2_ctrl *int32;
+ struct v4l2_ctrl *int64;
+ struct v4l2_ctrl *menu;
+ struct v4l2_ctrl *string;
+ struct v4l2_ctrl *bitmask;
+ struct v4l2_ctrl *int_menu;
+
+ spinlock_t slock;
+ struct mutex mutex;
+
+ struct vivi_dmaqueue vidq;
+
+ /* Several counters */
+ unsigned ms;
+ unsigned long jiffies;
+ unsigned button_pressed;
+
+ int mv_count; /* Controls bars movement */
+
+ /* Input Number */
+ int input;
+
+ /* video capture */
+ struct vivi_fmt *fmt;
+ unsigned int width, height;
+ struct vb2_queue vb_vidq;
+ unsigned int field_count;
+
+ u8 bars[9][3];
+ u8 line[MAX_WIDTH * 8];
+ unsigned int pixelsize;
+ u8 alpha_component;
+};
+
+/* ------------------------------------------------------------------
+ DMA and thread functions
+ ------------------------------------------------------------------*/
+
+/* Bars and Colors should match positions */
+
+enum colors {
+ WHITE,
+ AMBER,
+ CYAN,
+ GREEN,
+ MAGENTA,
+ RED,
+ BLUE,
+ BLACK,
+ TEXT_BLACK,
+};
+
+/* R G B */
+#define COLOR_WHITE {204, 204, 204}
+#define COLOR_AMBER {208, 208, 0}
+#define COLOR_CYAN { 0, 206, 206}
+#define COLOR_GREEN { 0, 239, 0}
+#define COLOR_MAGENTA {239, 0, 239}
+#define COLOR_RED {205, 0, 0}
+#define COLOR_BLUE { 0, 0, 255}
+#define COLOR_BLACK { 0, 0, 0}
+
+struct bar_std {
+ u8 bar[9][3];
+};
+
+/* Maximum number of bars are 10 - otherwise, the input print code
+ should be modified */
+static struct bar_std bars[] = {
+ { /* Standard ITU-R color bar sequence */
+ { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
+ COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
+ COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
+ COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
+ }, {
+ { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
+ COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
+ },
+};
+
+#define NUM_INPUTS ARRAY_SIZE(bars)
+
+#define TO_Y(r, g, b) \
+ (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
+/* RGB to V(Cr) Color transform */
+#define TO_V(r, g, b) \
+ (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128)
+/* RGB to U(Cb) Color transform */
+#define TO_U(r, g, b) \
+ (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
+
+/* precalculate color bar values to speed up rendering */
+static void precalculate_bars(struct vivi_dev *dev)
+{
+ u8 r, g, b;
+ int k, is_yuv;
+
+ for (k = 0; k < 9; k++) {
+ r = bars[dev->input].bar[k][0];
+ g = bars[dev->input].bar[k][1];
+ b = bars[dev->input].bar[k][2];
+ is_yuv = dev->fmt->is_yuv;
+
+ switch (dev->fmt->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ r >>= 3;
+ g >>= 2;
+ b >>= 3;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ r >>= 3;
+ g >>= 3;
+ b >>= 3;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ break;
+ }
+
+ if (is_yuv) {
+ dev->bars[k][0] = TO_Y(r, g, b); /* Luma */
+ dev->bars[k][1] = TO_U(r, g, b); /* Cb */
+ dev->bars[k][2] = TO_V(r, g, b); /* Cr */
+ } else {
+ dev->bars[k][0] = r;
+ dev->bars[k][1] = g;
+ dev->bars[k][2] = b;
+ }
+ }
+}
+
+#define TSTAMP_MIN_Y 24
+#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
+#define TSTAMP_INPUT_X 10
+#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
+
+/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
+static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
+{
+ u8 r_y, g_u, b_v;
+ u8 alpha = dev->alpha_component;
+ int color;
+ u8 *p;
+
+ r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
+ g_u = dev->bars[colorpos][1]; /* G or precalculated U */
+ b_v = dev->bars[colorpos][2]; /* B or precalculated V */
+
+ for (color = 0; color < dev->pixelsize; color++) {
+ p = buf + color;
+
+ switch (dev->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ switch (color) {
+ case 0:
+ *p = r_y;
+ break;
+ case 1:
+ *p = odd ? b_v : g_u;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ switch (color) {
+ case 0:
+ *p = odd ? b_v : g_u;
+ break;
+ case 1:
+ *p = r_y;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ switch (color) {
+ case 0:
+ *p = r_y;
+ break;
+ case 1:
+ *p = odd ? g_u : b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ switch (color) {
+ case 0:
+ *p = odd ? g_u : b_v;
+ break;
+ case 1:
+ *p = r_y;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ switch (color) {
+ case 0:
+ *p = (g_u << 5) | b_v;
+ break;
+ case 1:
+ *p = (r_y << 3) | (g_u >> 3);
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ switch (color) {
+ case 0:
+ *p = (r_y << 3) | (g_u >> 3);
+ break;
+ case 1:
+ *p = (g_u << 5) | b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ switch (color) {
+ case 0:
+ *p = (g_u << 5) | b_v;
+ break;
+ case 1:
+ *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ switch (color) {
+ case 0:
+ *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+ break;
+ case 1:
+ *p = (g_u << 5) | b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ switch (color) {
+ case 0:
+ *p = r_y;
+ break;
+ case 1:
+ *p = g_u;
+ break;
+ case 2:
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ switch (color) {
+ case 0:
+ *p = b_v;
+ break;
+ case 1:
+ *p = g_u;
+ break;
+ case 2:
+ *p = r_y;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ switch (color) {
+ case 0:
+ *p = alpha;
+ break;
+ case 1:
+ *p = r_y;
+ break;
+ case 2:
+ *p = g_u;
+ break;
+ case 3:
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ switch (color) {
+ case 0:
+ *p = b_v;
+ break;
+ case 1:
+ *p = g_u;
+ break;
+ case 2:
+ *p = r_y;
+ break;
+ case 3:
+ *p = alpha;
+ break;
+ }
+ break;
+ }
+ }
+}
+
+static void precalculate_line(struct vivi_dev *dev)
+{
+ int w;
+
+ for (w = 0; w < dev->width * 2; w++) {
+ int colorpos = w / (dev->width / 8) % 8;
+
+ gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1);
+ }
+}
+
+static void gen_text(struct vivi_dev *dev, char *basep,
+ int y, int x, char *text)
+{
+ int line;
+
+ /* Checks if it is possible to show string */
+ if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width)
+ return;
+
+ /* Print stream time */
+ for (line = y; line < y + 16; line++) {
+ int j = 0;
+ char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize;
+ char *s;
+
+ for (s = text; *s; s++) {
+ u8 chr = font8x16[*s * 16 + line - y];
+ int i;
+
+ for (i = 0; i < 7; i++, j++) {
+ /* Draw white font on black background */
+ if (chr & (1 << (7 - i)))
+ gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1);
+ else
+ gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1);
+ }
+ }
+ }
+}
+
+static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
+{
+ int wmax = dev->width;
+ int hmax = dev->height;
+ struct timeval ts;
+ void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
+ unsigned ms;
+ char str[100];
+ int h, line = 1;
+ s32 gain;
+
+ if (!vbuf)
+ return;
+
+ for (h = 0; h < hmax; h++)
+ memcpy(vbuf + h * wmax * dev->pixelsize,
+ dev->line + (dev->mv_count % wmax) * dev->pixelsize,
+ wmax * dev->pixelsize);
+
+ /* Updates stream time */
+
+ dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
+ dev->jiffies = jiffies;
+ ms = dev->ms;
+ snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
+ (ms / (60 * 60 * 1000)) % 24,
+ (ms / (60 * 1000)) % 60,
+ (ms / 1000) % 60,
+ ms % 1000);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " %dx%d, input %d ",
+ dev->width, dev->height, dev->input);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+
+ gain = v4l2_ctrl_g_ctrl(dev->gain);
+ mutex_lock(dev->ctrl_handler.lock);
+ snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
+ 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), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ",
+ dev->autogain->cur.val, gain, dev->volume->cur.val,
+ dev->alpha->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
+ dev->int32->cur.val,
+ dev->int64->cur.val64,
+ dev->bitmask->cur.val);
+ 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);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
+ dev->int_menu->qmenu_int[dev->int_menu->cur.val],
+ dev->int_menu->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ mutex_unlock(dev->ctrl_handler.lock);
+ 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;
+
+ buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ dev->field_count++;
+ buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
+ do_gettimeofday(&ts);
+ buf->vb.v4l2_buf.timestamp = ts;
+}
+
+static void vivi_thread_tick(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+ struct vivi_buffer *buf;
+ unsigned long flags = 0;
+
+ dprintk(dev, 1, "Thread tick\n");
+
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ dprintk(dev, 1, "No active queue to serve\n");
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return;
+ }
+
+ buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
+
+ /* Fill buffer */
+ vivi_fillbuff(dev, buf);
+ dprintk(dev, 1, "filled buffer %p\n", buf);
+
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
+}
+
+#define frames_to_ms(frames) \
+ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
+
+static void vivi_sleep(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+ int timeout;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
+ (unsigned long)dma_q);
+
+ add_wait_queue(&dma_q->wq, &wait);
+ if (kthread_should_stop())
+ goto stop_task;
+
+ /* Calculate time to wake up */
+ timeout = msecs_to_jiffies(frames_to_ms(1));
+
+ vivi_thread_tick(dev);
+
+ schedule_timeout_interruptible(timeout);
+
+stop_task:
+ remove_wait_queue(&dma_q->wq, &wait);
+ try_to_freeze();
+}
+
+static int vivi_thread(void *data)
+{
+ struct vivi_dev *dev = data;
+
+ dprintk(dev, 1, "thread started\n");
+
+ set_freezable();
+
+ for (;;) {
+ vivi_sleep(dev);
+
+ if (kthread_should_stop())
+ break;
+ }
+ dprintk(dev, 1, "thread: exit\n");
+ return 0;
+}
+
+static int vivi_start_generating(struct vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ /* Resets frame counters */
+ dev->ms = 0;
+ dev->mv_count = 0;
+ dev->jiffies = jiffies;
+
+ dma_q->frame = 0;
+ dma_q->ini_jiffies = jiffies;
+ dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
+
+ if (IS_ERR(dma_q->kthread)) {
+ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ 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 vivi_dev *dev)
+{
+ struct vivi_dmaqueue *dma_q = &dev->vidq;
+
+ dprintk(dev, 1, "%s\n", __func__);
+
+ /* shutdown control thread */
+ if (dma_q->kthread) {
+ kthread_stop(dma_q->kthread);
+ dma_q->kthread = NULL;
+ }
+
+ /*
+ * 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 queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ if (fmt)
+ size = fmt->fmt.pix.sizeimage;
+ else
+ size = dev->width * dev->height * dev->pixelsize;
+
+ if (size == 0)
+ return -EINVAL;
+
+ if (0 == *nbuffers)
+ *nbuffers = 32;
+
+ while (size * *nbuffers > vid_limit * 1024 * 1024)
+ (*nbuffers)--;
+
+ *nplanes = 1;
+
+ sizes[0] = size;
+
+ /*
+ * videobuf2-vmalloc allocator is context-less so no need to set
+ * alloc_ctxs array.
+ */
+
+ dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
+ *nbuffers, size);
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
+ unsigned long size;
+
+ 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;
+
+ size = dev->width * dev->height * dev->pixelsize;
+ 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);
+
+ buf->fmt = dev->fmt;
+
+ precalculate_bars(dev);
+ precalculate_line(dev);
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ 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__);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ list_add_tail(&buf->list, &vidq->active);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ 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);
+}
+
+static void vivi_unlock(struct vb2_queue *vq)
+{
+ struct vivi_dev *dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&dev->mutex);
+}
+
+
+static struct vb2_ops vivi_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vivi_unlock,
+ .wait_finish = vivi_lock,
+};
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "vivi");
+ strcpy(cap->card, "vivi");
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev->v4l2_dev.name);
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct vivi_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ fmt = &formats[f->index];
+
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (dev->fmt->is_yuv)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+ struct vivi_fmt *fmt;
+
+ fmt = get_format(f);
+ if (!fmt) {
+ dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
+ f->fmt.pix.pixelformat);
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ fmt = get_format(f);
+ }
+
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
+ &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+ if (fmt->is_yuv)
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ else
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+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 (vb2_is_busy(q)) {
+ dprintk(dev, 1, "%s device busy\n", __func__);
+ return -EBUSY;
+ }
+
+ dev->fmt = get_format(f);
+ dev->pixelsize = dev->fmt->depth / 8;
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+
+ return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ static const struct v4l2_frmsize_stepwise sizes = {
+ 48, MAX_WIDTH, 4,
+ 32, MAX_HEIGHT, 1
+ };
+ int i;
+
+ if (fsize->index)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fsize->pixel_format)
+ break;
+ if (i == ARRAY_SIZE(formats))
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = sizes;
+ return 0;
+}
+
+/* only one input in this sample driver */
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index >= NUM_INPUTS)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ sprintf(inp->name, "Camera %u", inp->index);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ *i = dev->input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct vivi_dev *dev = video_drvdata(file);
+
+ if (i >= NUM_INPUTS)
+ return -EINVAL;
+
+ if (i == dev->input)
+ return 0;
+
+ dev->input = i;
+ precalculate_bars(dev);
+ precalculate_line(dev);
+ return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+
+static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+ if (ctrl == dev->autogain)
+ dev->gain->val = jiffies & 0xff;
+ return 0;
+}
+
+static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_ALPHA_COMPONENT:
+ dev->alpha_component = ctrl->val;
+ break;
+ default:
+ if (ctrl == dev->button)
+ dev->button_pressed = 30;
+ break;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ File operations for the device
+ ------------------------------------------------------------------*/
+
+static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
+ .g_volatile_ctrl = vivi_g_volatile_ctrl,
+ .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_ctrl_config vivi_ctrl_bitmask = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 6,
+ .name = "Bitmask",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .def = 0x80002000,
+ .min = 0,
+ .max = 0x80402010,
+ .step = 0,
+};
+
+static const s64 vivi_ctrl_int_menu_values[] = {
+ 1, 1, 2, 3, 5, 8, 13, 21, 42,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 7,
+ .name = "Integer menu",
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .min = 1,
+ .max = 8,
+ .def = 4,
+ .menu_skip_mask = 0x02,
+ .qmenu_int = vivi_ctrl_int_menu_values,
+};
+
+static const struct v4l2_file_operations vivi_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = vb2_fop_mmap,
+};
+
+static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static struct video_device vivi_template = {
+ .name = "vivi",
+ .fops = &vivi_fops,
+ .ioctl_ops = &vivi_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+/* -----------------------------------------------------------------
+ Initialization and module stuff
+ ------------------------------------------------------------------*/
+
+static int vivi_release(void)
+{
+ struct vivi_dev *dev;
+ struct list_head *list;
+
+ while (!list_empty(&vivi_devlist)) {
+ list = vivi_devlist.next;
+ list_del(list);
+ dev = list_entry(list, struct vivi_dev, vivi_devlist);
+
+ v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
+ video_device_node_name(&dev->vdev));
+ video_unregister_device(&dev->vdev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ kfree(dev);
+ }
+
+ return 0;
+}
+
+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);
+ if (!dev)
+ return -ENOMEM;
+
+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
+ "%s-%03d", VIVI_MODULE_NAME, inst);
+ ret = v4l2_device_register(NULL, &dev->v4l2_dev);
+ if (ret)
+ goto free_dev;
+
+ dev->fmt = &formats[0];
+ dev->width = 640;
+ dev->height = 480;
+ dev->pixelsize = dev->fmt->depth / 8;
+ 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->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 100);
+ dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT, 0, 255, 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);
+ dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
+ dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL);
+ if (hdl->error) {
+ ret = hdl->error;
+ goto unreg_dev;
+ }
+ v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
+ dev->v4l2_dev.ctrl_handler = hdl;
+
+ /* initialize locks */
+ spin_lock_init(&dev->slock);
+
+ /* initialize queue */
+ q = &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;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto unreg_dev;
+
+ mutex_init(&dev->mutex);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ init_waitqueue_head(&dev->vidq.wq);
+
+ vfd = &dev->vdev;
+ *vfd = vivi_template;
+ vfd->debug = debug;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->queue = q;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+
+ /*
+ * Provide a mutex to v4l2 core. It will be used to protect
+ * all fops and v4l2 ioctls.
+ */
+ vfd->lock = &dev->mutex;
+ video_set_drvdata(vfd, dev);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+ if (ret < 0)
+ goto unreg_dev;
+
+ /* Now that everything is fine, let's add it to device list */
+ list_add_tail(&dev->vivi_devlist, &vivi_devlist);
+
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(vfd));
+ return 0;
+
+unreg_dev:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_dev:
+ kfree(dev);
+ return ret;
+}
+
+/* This routine allocates from 1 to n_devs virtual drivers.
+
+ The real maximum number of virtual drivers will depend on how many drivers
+ will succeed. This is limited to the maximum number of devices that
+ videodev supports, which is equal to VIDEO_NUM_DEVICES.
+ */
+static int __init vivi_init(void)
+{
+ const struct font_desc *font = find_font("VGA8x16");
+ int ret = 0, i;
+
+ if (font == NULL) {
+ printk(KERN_ERR "vivi: could not find font\n");
+ return -ENODEV;
+ }
+ font8x16 = font->data;
+
+ if (n_devs <= 0)
+ n_devs = 1;
+
+ for (i = 0; i < n_devs; i++) {
+ ret = vivi_create_instance(i);
+ if (ret) {
+ /* If some instantiations succeeded, keep driver */
+ if (i)
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
+ return ret;
+ }
+
+ printk(KERN_INFO "Video Technology Magazine Virtual Video "
+ "Capture Board ver %s successfully loaded.\n",
+ VIVI_VERSION);
+
+ /* n_devs will reflect the actual number of allocated devices */
+ n_devs = i;
+
+ return ret;
+}
+
+static void __exit vivi_exit(void)
+{
+ vivi_release();
+}
+
+module_init(vivi_init);
+module_exit(vivi_exit);
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
index 79adf3e654e5..e10e525f33e5 100644
--- a/drivers/media/radio/radio-keene.c
+++ b/drivers/media/radio/radio-keene.c
@@ -203,7 +203,7 @@ static int vidioc_g_modulator(struct file *file, void *priv,
}
static int vidioc_s_modulator(struct file *file, void *priv,
- struct v4l2_modulator *v)
+ const struct v4l2_modulator *v)
{
struct keene_device *radio = video_drvdata(file);
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c
index 87c1ee13b058..11f76ed4c6fb 100644
--- a/drivers/media/radio/radio-miropcm20.c
+++ b/drivers/media/radio/radio-miropcm20.c
@@ -197,7 +197,7 @@ static int vidioc_g_audio(struct file *file, void *priv,
}
static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+ const struct v4l2_audio *a)
{
return a->index ? -EINVAL : 0;
}
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index 3182b26d6efa..9c5a267b60b4 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -348,7 +348,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
}
static int vidioc_s_hw_freq_seek(struct file *file, void *priv,
- struct v4l2_hw_freq_seek *seek)
+ const struct v4l2_hw_freq_seek *seek)
{
static u8 buf[8] = {
0x3d, 0x32, 0x0f, 0x08, 0x3d, 0x32, 0x0f, 0x08
@@ -360,6 +360,9 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *priv,
if (seek->tuner != 0 || !seek->wrap_around)
return -EINVAL;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
retval = amradio_send_cmd(radio,
AMRADIO_SET_SEARCH_LVL, 0, buf, 8, false);
if (retval)
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 8185d5fbfa89..227dcdb54df3 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -239,7 +239,7 @@ static int vidioc_g_audio(struct file *file, void *priv,
}
static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+ const struct v4l2_audio *a)
{
return a->index ? -EINVAL : 0;
}
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
index 72ded29728bb..8c309c7134d7 100644
--- a/drivers/media/radio/radio-shark.c
+++ b/drivers/media/radio/radio-shark.c
@@ -59,7 +59,8 @@ MODULE_LICENSE("GPL");
#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
-enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
+/* Note BLUE_IS_PULSE comes after NO_LEDS as it is a status bit, not a LED */
+enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS, BLUE_IS_PULSE };
struct shark_device {
struct usb_device *usbdev;
@@ -190,6 +191,7 @@ static void shark_led_set_blue(struct led_classdev *led_cdev,
atomic_set(&shark->brightness[BLUE_LED], value);
set_bit(BLUE_LED, &shark->brightness_new);
+ clear_bit(BLUE_IS_PULSE, &shark->brightness_new);
schedule_work(&shark->led_work);
}
@@ -201,6 +203,7 @@ static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
set_bit(BLUE_PULSE_LED, &shark->brightness_new);
+ set_bit(BLUE_IS_PULSE, &shark->brightness_new);
schedule_work(&shark->led_work);
}
@@ -240,6 +243,7 @@ static int shark_register_leds(struct shark_device *shark, struct device *dev)
{
int i, retval;
+ atomic_set(&shark->brightness[BLUE_LED], 127);
INIT_WORK(&shark->led_work, shark_led_work);
for (i = 0; i < NO_LEDS; i++) {
shark->leds[i] = shark_led_templates[i];
@@ -266,6 +270,16 @@ static void shark_unregister_leds(struct shark_device *shark)
cancel_work_sync(&shark->led_work);
}
+
+static void shark_resume_leds(struct shark_device *shark)
+{
+ if (test_bit(BLUE_IS_PULSE, &shark->brightness_new))
+ set_bit(BLUE_PULSE_LED, &shark->brightness_new);
+ else
+ set_bit(BLUE_LED, &shark->brightness_new);
+ set_bit(RED_LED, &shark->brightness_new);
+ schedule_work(&shark->led_work);
+}
#else
static int shark_register_leds(struct shark_device *shark, struct device *dev)
{
@@ -274,6 +288,7 @@ static int shark_register_leds(struct shark_device *shark, struct device *dev)
return 0;
}
static inline void shark_unregister_leds(struct shark_device *shark) { }
+static inline void shark_resume_leds(struct shark_device *shark) { }
#endif
static void usb_shark_disconnect(struct usb_interface *intf)
@@ -333,6 +348,7 @@ static int usb_shark_probe(struct usb_interface *intf,
shark->tea.radio_nr = -1;
shark->tea.ops = &shark_tea_ops;
shark->tea.cannot_mute = true;
+ shark->tea.has_am = true;
strlcpy(shark->tea.card, "Griffin radioSHARK",
sizeof(shark->tea.card));
usb_make_path(shark->usbdev, shark->tea.bus_info,
@@ -358,6 +374,27 @@ err_alloc_buffer:
return retval;
}
+#ifdef CONFIG_PM
+static int usb_shark_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return 0;
+}
+
+static int usb_shark_resume(struct usb_interface *intf)
+{
+ struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+
+ mutex_lock(&shark->tea.mutex);
+ snd_tea575x_set_freq(&shark->tea);
+ mutex_unlock(&shark->tea.mutex);
+
+ shark_resume_leds(shark);
+
+ return 0;
+}
+#endif
+
/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
static struct usb_device_id usb_shark_device_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
@@ -377,5 +414,10 @@ static struct usb_driver usb_shark_driver = {
.probe = usb_shark_probe,
.disconnect = usb_shark_disconnect,
.id_table = usb_shark_device_table,
+#ifdef CONFIG_PM
+ .suspend = usb_shark_suspend,
+ .resume = usb_shark_resume,
+ .reset_resume = usb_shark_resume,
+#endif
};
module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
index 7b4efdfaae28..ef65ebbd5364 100644
--- a/drivers/media/radio/radio-shark2.c
+++ b/drivers/media/radio/radio-shark2.c
@@ -86,12 +86,8 @@ static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
for (i = 0; i < 6; i++)
shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
- v4l2_dbg(1, debug, tea->v4l2_dev,
- "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
- shark->transfer_buffer[0], shark->transfer_buffer[1],
- shark->transfer_buffer[2], shark->transfer_buffer[3],
- shark->transfer_buffer[4], shark->transfer_buffer[5],
- shark->transfer_buffer[6]);
+ v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-write: %*ph\n",
+ 7, shark->transfer_buffer);
res = usb_interrupt_msg(shark->usbdev,
usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
@@ -134,9 +130,8 @@ static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
for (i = 0; i < 3; i++)
reg |= shark->transfer_buffer[i] << (16 - i * 8);
- v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
- shark->transfer_buffer[0], shark->transfer_buffer[1],
- shark->transfer_buffer[2]);
+ v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %*ph\n",
+ 3, shark->transfer_buffer);
*reg_ret = reg;
return 0;
@@ -214,6 +209,7 @@ static int shark_register_leds(struct shark_device *shark, struct device *dev)
{
int i, retval;
+ atomic_set(&shark->brightness[BLUE_LED], 127);
INIT_WORK(&shark->led_work, shark_led_work);
for (i = 0; i < NO_LEDS; i++) {
shark->leds[i] = shark_led_templates[i];
@@ -240,6 +236,16 @@ static void shark_unregister_leds(struct shark_device *shark)
cancel_work_sync(&shark->led_work);
}
+
+static void shark_resume_leds(struct shark_device *shark)
+{
+ int i;
+
+ for (i = 0; i < NO_LEDS; i++)
+ set_bit(i, &shark->brightness_new);
+
+ schedule_work(&shark->led_work);
+}
#else
static int shark_register_leds(struct shark_device *shark, struct device *dev)
{
@@ -248,6 +254,7 @@ static int shark_register_leds(struct shark_device *shark, struct device *dev)
return 0;
}
static inline void shark_unregister_leds(struct shark_device *shark) { }
+static inline void shark_resume_leds(struct shark_device *shark) { }
#endif
static void usb_shark_disconnect(struct usb_interface *intf)
@@ -332,6 +339,28 @@ err_alloc_buffer:
return retval;
}
+#ifdef CONFIG_PM
+static int usb_shark_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return 0;
+}
+
+static int usb_shark_resume(struct usb_interface *intf)
+{
+ struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
+ struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
+ int ret;
+
+ mutex_lock(&shark->tea.mutex);
+ ret = radio_tea5777_set_freq(&shark->tea);
+ mutex_unlock(&shark->tea.mutex);
+
+ shark_resume_leds(shark);
+
+ return ret;
+}
+#endif
+
/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
static struct usb_device_id usb_shark_device_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
@@ -351,5 +380,10 @@ static struct usb_driver usb_shark_driver = {
.probe = usb_shark_probe,
.disconnect = usb_shark_disconnect,
.id_table = usb_shark_device_table,
+#ifdef CONFIG_PM
+ .suspend = usb_shark_suspend,
+ .resume = usb_shark_resume,
+ .reset_resume = usb_shark_resume,
+#endif
};
module_usb_driver(usb_shark_driver);
diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c
index c54210c7fef9..a082e400ed0f 100644
--- a/drivers/media/radio/radio-si4713.c
+++ b/drivers/media/radio/radio-si4713.c
@@ -83,7 +83,7 @@ static int radio_si4713_g_audout(struct file *file, void *priv,
}
static int radio_si4713_s_audout(struct file *file, void *priv,
- struct v4l2_audioout *vao)
+ const struct v4l2_audioout *vao)
{
return vao->index ? -EINVAL : 0;
}
@@ -200,7 +200,7 @@ static int radio_si4713_g_modulator(struct file *file, void *p,
}
static int radio_si4713_s_modulator(struct file *file, void *p,
- struct v4l2_modulator *vm)
+ const struct v4l2_modulator *vm)
{
return v4l2_device_call_until_err(get_v4l2_dev(file), 0, tuner,
s_modulator, vm);
@@ -268,7 +268,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev)
goto exit;
}
- rsdev = kzalloc(sizeof *rsdev, GFP_KERNEL);
+ rsdev = devm_kzalloc(&pdev->dev, sizeof(*rsdev), GFP_KERNEL);
if (!rsdev) {
dev_err(&pdev->dev, "Failed to alloc video device.\n");
rval = -ENOMEM;
@@ -278,7 +278,7 @@ static int radio_si4713_pdriver_probe(struct platform_device *pdev)
rval = v4l2_device_register(&pdev->dev, &rsdev->v4l2_dev);
if (rval) {
dev_err(&pdev->dev, "Failed to register v4l2 device.\n");
- goto free_rsdev;
+ goto exit;
}
adapter = i2c_get_adapter(pdata->i2c_bus);
@@ -322,8 +322,6 @@ put_adapter:
i2c_put_adapter(adapter);
unregister_v4l2_dev:
v4l2_device_unregister(&rsdev->v4l2_dev);
-free_rsdev:
- kfree(rsdev);
exit:
return rval;
}
@@ -342,7 +340,6 @@ static int __exit radio_si4713_pdriver_remove(struct platform_device *pdev)
video_unregister_device(rsdev->radio_dev);
i2c_put_adapter(client->adapter);
v4l2_device_unregister(&rsdev->v4l2_dev);
- kfree(rsdev);
return 0;
}
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 6b1fae32b483..d0c905310071 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -151,8 +151,11 @@ int tea5764_i2c_read(struct tea5764_device *radio)
u16 *p = (u16 *) &radio->regs;
struct i2c_msg msgs[1] = {
- { radio->i2c_client->addr, I2C_M_RD, sizeof(radio->regs),
- (void *)&radio->regs },
+ { .addr = radio->i2c_client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(radio->regs),
+ .buf = (void *)&radio->regs
+ },
};
if (i2c_transfer(radio->i2c_client->adapter, msgs, 1) != 1)
return -EIO;
@@ -167,7 +170,11 @@ int tea5764_i2c_write(struct tea5764_device *radio)
struct tea5764_write_regs wr;
struct tea5764_regs *r = &radio->regs;
struct i2c_msg msgs[1] = {
- { radio->i2c_client->addr, 0, sizeof(wr), (void *) &wr },
+ {
+ .addr = radio->i2c_client->addr,
+ .len = sizeof(wr),
+ .buf = (void *)&wr
+ },
};
wr.intreg = r->intreg & 0xff;
wr.frqset = __cpu_to_be16(r->frqset);
@@ -448,7 +455,7 @@ static int vidioc_g_audio(struct file *file, void *priv,
}
static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+ const struct v4l2_audio *a)
{
if (a->index != 0)
return -EINVAL;
diff --git a/drivers/media/radio/radio-tea5777.c b/drivers/media/radio/radio-tea5777.c
index 5bc9fa62720b..4b5190d4fea5 100644
--- a/drivers/media/radio/radio-tea5777.c
+++ b/drivers/media/radio/radio-tea5777.c
@@ -33,20 +33,17 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
-#include <asm/div64.h>
#include "radio-tea5777.h"
MODULE_AUTHOR("Hans de Goede <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for control of TEA5777 Philips AM/FM radio tuner chips");
MODULE_LICENSE("GPL");
-/* Fixed FM only band for now, will implement multi-band support when the
- VIDIOC_ENUM_FREQ_BANDS API is upstream */
-#define TEA5777_FM_RANGELOW (76000 * 16)
-#define TEA5777_FM_RANGEHIGH (108000 * 16)
-
#define TEA5777_FM_IF 150 /* kHz */
-#define TEA5777_FM_FREQ_STEP 50 /* kHz */
+#define TEA5777_FM_FREQ_STEP 50 /* kHz */
+
+#define TEA5777_AM_IF 21 /* kHz */
+#define TEA5777_AM_FREQ_STEP 1 /* kHz */
/* Write reg, common bits */
#define TEA5777_W_MUTE_MASK (1LL << 47)
@@ -95,7 +92,7 @@ MODULE_LICENSE("GPL");
#define TEA5777_W_FM_PLL_SHIFT 32
#define TEA5777_W_FM_FREF_MASK (0x03LL << 30)
#define TEA5777_W_FM_FREF_SHIFT 30
-#define TEA5777_W_FM_FREF_VALUE 0 /* 50 kHz tune steps, 150 kHz IF */
+#define TEA5777_W_FM_FREF_VALUE 0LL /* 50k steps, 150k IF */
#define TEA5777_W_FM_FORCEMONO_MASK (1LL << 15)
#define TEA5777_W_FM_FORCEMONO_SHIFT 15
@@ -116,6 +113,8 @@ MODULE_LICENSE("GPL");
#define TEA5777_W_AM_AGCIF_SHIFT 32
#define TEA5777_W_AM_MWLW_MASK (1LL << 31)
#define TEA5777_W_AM_MWLW_SHIFT 31
+#define TEA5777_W_AM_LW 0LL
+#define TEA5777_W_AM_MW 1LL
#define TEA5777_W_AM_LNA_MASK (1LL << 30)
#define TEA5777_W_AM_LNA_SHIFT 30
@@ -148,26 +147,82 @@ MODULE_LICENSE("GPL");
#define TEA5777_R_FM_PLL_MASK 0x1fff
#define TEA5777_R_FM_PLL_SHIFT 0
+enum { BAND_FM, BAND_AM };
+
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_PROG_LIM,
+ .rangelow = 76000 * 16,
+ .rangehigh = 108000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_PROG_LIM,
+ .rangelow = 530 * 16,
+ .rangehigh = 1710 * 16,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+};
+
static u32 tea5777_freq_to_v4l2_freq(struct radio_tea5777 *tea, u32 freq)
{
- return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
+ switch (tea->band) {
+ case BAND_FM:
+ return (freq * TEA5777_FM_FREQ_STEP + TEA5777_FM_IF) * 16;
+ case BAND_AM:
+ return (freq * TEA5777_AM_FREQ_STEP + TEA5777_AM_IF) * 16;
+ }
+ return 0; /* Never reached */
}
-static int radio_tea5777_set_freq(struct radio_tea5777 *tea)
+int radio_tea5777_set_freq(struct radio_tea5777 *tea)
{
- u64 freq;
+ u32 freq;
int res;
- freq = clamp_t(u32, tea->freq,
- TEA5777_FM_RANGELOW, TEA5777_FM_RANGEHIGH) + 8;
- do_div(freq, 16); /* to kHz */
-
- freq -= TEA5777_FM_IF;
- do_div(freq, TEA5777_FM_FREQ_STEP);
-
- tea->write_reg &= ~(TEA5777_W_FM_PLL_MASK | TEA5777_W_FM_FREF_MASK);
- tea->write_reg |= freq << TEA5777_W_FM_PLL_SHIFT;
- tea->write_reg |= TEA5777_W_FM_FREF_VALUE << TEA5777_W_FM_FREF_SHIFT;
+ freq = clamp(tea->freq, bands[tea->band].rangelow,
+ bands[tea->band].rangehigh);
+ freq = (freq + 8) / 16; /* to kHz */
+
+ switch (tea->band) {
+ case BAND_FM:
+ tea->write_reg &= ~TEA5777_W_AM_FM_MASK;
+ freq = (freq - TEA5777_FM_IF) / TEA5777_FM_FREQ_STEP;
+ tea->write_reg &= ~TEA5777_W_FM_PLL_MASK;
+ tea->write_reg |= (u64)freq << TEA5777_W_FM_PLL_SHIFT;
+ tea->write_reg &= ~TEA5777_W_FM_FREF_MASK;
+ tea->write_reg |= TEA5777_W_FM_FREF_VALUE <<
+ TEA5777_W_FM_FREF_SHIFT;
+ tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
+ if (tea->audmode == V4L2_TUNER_MODE_MONO)
+ tea->write_reg |= 1LL << TEA5777_W_FM_FORCEMONO_SHIFT;
+ break;
+ case BAND_AM:
+ tea->write_reg &= ~TEA5777_W_AM_FM_MASK;
+ tea->write_reg |= (1LL << TEA5777_W_AM_FM_SHIFT);
+ freq = (freq - TEA5777_AM_IF) / TEA5777_AM_FREQ_STEP;
+ tea->write_reg &= ~TEA5777_W_AM_PLL_MASK;
+ tea->write_reg |= (u64)freq << TEA5777_W_AM_PLL_SHIFT;
+ tea->write_reg &= ~TEA5777_W_AM_AGCRF_MASK;
+ tea->write_reg &= ~TEA5777_W_AM_AGCRF_MASK;
+ tea->write_reg &= ~TEA5777_W_AM_MWLW_MASK;
+ tea->write_reg |= TEA5777_W_AM_MW << TEA5777_W_AM_MWLW_SHIFT;
+ tea->write_reg &= ~TEA5777_W_AM_LNA_MASK;
+ tea->write_reg |= 1LL << TEA5777_W_AM_LNA_SHIFT;
+ tea->write_reg &= ~TEA5777_W_AM_PEAK_MASK;
+ tea->write_reg |= 1LL << TEA5777_W_AM_PEAK_SHIFT;
+ tea->write_reg &= ~TEA5777_W_AM_CALLIGN_MASK;
+ break;
+ }
res = tea->ops->write_reg(tea, tea->write_reg);
if (res)
@@ -225,6 +280,19 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct radio_tea5777 *tea = video_drvdata(file);
+
+ if (band->tuner != 0 || band->index >= ARRAY_SIZE(bands) ||
+ (!tea->has_am && band->index == BAND_AM))
+ return -EINVAL;
+
+ *band = bands[band->index];
+ return 0;
+}
+
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
@@ -245,13 +313,18 @@ static int vidioc_g_tuner(struct file *file, void *priv,
strlcpy(v->name, "FM", sizeof(v->name));
v->type = V4L2_TUNER_RADIO;
v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_HWSEEK_BOUNDED;
- v->rangelow = TEA5777_FM_RANGELOW;
- v->rangehigh = TEA5777_FM_RANGEHIGH;
- v->rxsubchans = (tea->read_reg & TEA5777_R_FM_STEREO_MASK) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
- v->audmode = (tea->write_reg & TEA5777_W_FM_FORCEMONO_MASK) ?
- V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
+ V4L2_TUNER_CAP_FREQ_BANDS |
+ V4L2_TUNER_CAP_HWSEEK_BOUNDED |
+ V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
+ v->rangelow = tea->has_am ? bands[BAND_AM].rangelow :
+ bands[BAND_FM].rangelow;
+ v->rangehigh = bands[BAND_FM].rangehigh;
+ if (tea->band == BAND_FM &&
+ (tea->read_reg & TEA5777_R_FM_STEREO_MASK))
+ v->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ else
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->audmode = tea->audmode;
/* shift - 12 to convert 4-bits (0-15) scale to 16-bits (0-65535) */
v->signal = (tea->read_reg & TEA5777_R_LEVEL_MASK) >>
(TEA5777_R_LEVEL_SHIFT - 12);
@@ -266,16 +339,20 @@ static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct radio_tea5777 *tea = video_drvdata(file);
+ u32 orig_audmode = tea->audmode;
if (v->index)
return -EINVAL;
- if (v->audmode == V4L2_TUNER_MODE_MONO)
- tea->write_reg |= TEA5777_W_FM_FORCEMONO_MASK;
- else
- tea->write_reg &= ~TEA5777_W_FM_FORCEMONO_MASK;
+ if (v->audmode > V4L2_TUNER_MODE_STEREO)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
- return radio_tea5777_set_freq(tea);
+ tea->audmode = v->audmode;
+
+ if (tea->audmode != orig_audmode && tea->band == BAND_FM)
+ return radio_tea5777_set_freq(tea);
+
+ return 0;
}
static int vidioc_g_frequency(struct file *file, void *priv,
@@ -298,40 +375,74 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
+ if (tea->has_am && f->frequency < (20000 * 16))
+ tea->band = BAND_AM;
+ else
+ tea->band = BAND_FM;
+
tea->freq = f->frequency;
return radio_tea5777_set_freq(tea);
}
static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
- struct v4l2_hw_freq_seek *a)
+ const struct v4l2_hw_freq_seek *a)
{
struct radio_tea5777 *tea = video_drvdata(file);
- u32 orig_freq = tea->freq;
unsigned long timeout;
- int res, spacing = 200 * 16; /* 200 kHz */
- /* These are fixed *for now* */
- const u32 seek_rangelow = TEA5777_FM_RANGELOW;
- const u32 seek_rangehigh = TEA5777_FM_RANGEHIGH;
+ u32 rangelow = a->rangelow;
+ u32 rangehigh = a->rangehigh;
+ int i, res, spacing;
+ u32 orig_freq;
if (a->tuner || a->wrap_around)
return -EINVAL;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ if (rangelow || rangehigh) {
+ for (i = 0; i < ARRAY_SIZE(bands); i++) {
+ if (i == BAND_AM && !tea->has_am)
+ continue;
+ if (bands[i].rangelow >= rangelow &&
+ bands[i].rangehigh <= rangehigh)
+ break;
+ }
+ if (i == ARRAY_SIZE(bands))
+ return -EINVAL; /* No matching band found */
+
+ tea->band = i;
+ if (tea->freq < rangelow || tea->freq > rangehigh) {
+ tea->freq = clamp(tea->freq, rangelow,
+ rangehigh);
+ res = radio_tea5777_set_freq(tea);
+ if (res)
+ return res;
+ }
+ } else {
+ rangelow = bands[tea->band].rangelow;
+ rangehigh = bands[tea->band].rangehigh;
+ }
+
+ spacing = (tea->band == BAND_AM) ? (5 * 16) : (200 * 16); /* kHz */
+ orig_freq = tea->freq;
+
tea->write_reg |= TEA5777_W_PROGBLIM_MASK;
- if (seek_rangelow != tea->seek_rangelow) {
+ if (tea->seek_rangelow != rangelow) {
tea->write_reg &= ~TEA5777_W_UPDWN_MASK;
- tea->freq = seek_rangelow;
+ tea->freq = rangelow;
res = radio_tea5777_set_freq(tea);
if (res)
goto leave;
- tea->seek_rangelow = tea->freq;
+ tea->seek_rangelow = rangelow;
}
- if (seek_rangehigh != tea->seek_rangehigh) {
+ if (tea->seek_rangehigh != rangehigh) {
tea->write_reg |= TEA5777_W_UPDWN_MASK;
- tea->freq = seek_rangehigh;
+ tea->freq = rangehigh;
res = radio_tea5777_set_freq(tea);
if (res)
goto leave;
- tea->seek_rangehigh = tea->freq;
+ tea->seek_rangehigh = rangehigh;
}
tea->write_reg &= ~TEA5777_W_PROGBLIM_MASK;
@@ -419,6 +530,7 @@ static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+ .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -441,8 +553,9 @@ int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner)
(1LL << TEA5777_W_IFW_SHIFT) |
(1LL << TEA5777_W_INTEXT_SHIFT) |
(1LL << TEA5777_W_CHP0_SHIFT) |
- (2LL << TEA5777_W_SLEV_SHIFT);
+ (1LL << TEA5777_W_SLEV_SHIFT);
tea->freq = 90500 * 16; /* 90.5Mhz default */
+ tea->audmode = V4L2_TUNER_MODE_STEREO;
res = radio_tea5777_set_freq(tea);
if (res) {
v4l2_err(tea->v4l2_dev, "can't set initial freq (%d)\n", res);
diff --git a/drivers/media/radio/radio-tea5777.h b/drivers/media/radio/radio-tea5777.h
index 55cbd78df5ed..4ea43a90a151 100644
--- a/drivers/media/radio/radio-tea5777.h
+++ b/drivers/media/radio/radio-tea5777.h
@@ -68,7 +68,9 @@ struct radio_tea5777 {
bool has_am; /* Device can tune to AM freqs */
bool write_before_read; /* must write before read quirk */
bool needs_write; /* for write before read quirk */
+ u32 band; /* current band */
u32 freq; /* current frequency */
+ u32 audmode; /* last set audmode */
u32 seek_rangelow; /* current hwseek limits */
u32 seek_rangehigh;
u32 read_reg;
@@ -83,5 +85,6 @@ struct radio_tea5777 {
int radio_tea5777_init(struct radio_tea5777 *tea, struct module *owner);
void radio_tea5777_exit(struct radio_tea5777 *tea);
+int radio_tea5777_set_freq(struct radio_tea5777 *tea);
#endif /* __RADIO_TEA5777_H */
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index 7052adc0c0b0..5cf07779f4bb 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -85,7 +85,7 @@ static int timbradio_vidioc_g_audio(struct file *file, void *priv,
}
static int timbradio_vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+ const struct v4l2_audio *a)
{
return a->index ? -EINVAL : 0;
}
@@ -157,7 +157,7 @@ static int __devinit timbradio_probe(struct platform_device *pdev)
goto err;
}
- tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+ tr = devm_kzalloc(&pdev->dev, sizeof(*tr), GFP_KERNEL);
if (!tr) {
err = -ENOMEM;
goto err;
@@ -177,7 +177,7 @@ static int __devinit timbradio_probe(struct platform_device *pdev)
strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name));
err = v4l2_device_register(NULL, &tr->v4l2_dev);
if (err)
- goto err_v4l2_dev;
+ goto err;
tr->video_dev.v4l2_dev = &tr->v4l2_dev;
@@ -195,8 +195,6 @@ static int __devinit timbradio_probe(struct platform_device *pdev)
err_video_req:
video_device_release_empty(&tr->video_dev);
v4l2_device_unregister(&tr->v4l2_dev);
-err_v4l2_dev:
- kfree(tr);
err:
dev_err(&pdev->dev, "Failed to register: %d\n", err);
@@ -212,8 +210,6 @@ static int __devexit timbradio_remove(struct platform_device *pdev)
v4l2_device_unregister(&tr->v4l2_dev);
- kfree(tr);
-
return 0;
}
diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c
index e8428f573ccd..9b0c9fa0beb8 100644
--- a/drivers/media/radio/radio-wl1273.c
+++ b/drivers/media/radio/radio-wl1273.c
@@ -1479,7 +1479,7 @@ static int wl1273_fm_vidioc_g_audio(struct file *file, void *priv,
}
static int wl1273_fm_vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *audio)
+ const struct v4l2_audio *audio)
{
struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
@@ -1682,7 +1682,7 @@ static int wl1273_fm_vidioc_s_frequency(struct file *file, void *priv,
#define WL1273_DEFAULT_SEEK_LEVEL 7
static int wl1273_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv,
- struct v4l2_hw_freq_seek *seek)
+ const struct v4l2_hw_freq_seek *seek)
{
struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
struct wl1273_core *core = radio->core;
@@ -1693,6 +1693,9 @@ static int wl1273_fm_vidioc_s_hw_freq_seek(struct file *file, void *priv,
if (seek->tuner != 0 || seek->type != V4L2_TUNER_RADIO)
return -EINVAL;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
if (mutex_lock_interruptible(&core->lock))
return -EINTR;
@@ -1715,7 +1718,7 @@ out:
}
static int wl1273_fm_vidioc_s_modulator(struct file *file, void *priv,
- struct v4l2_modulator *modulator)
+ const struct v4l2_modulator *modulator)
{
struct wl1273_device *radio = video_get_drvdata(video_devdata(file));
struct wl1273_core *core = radio->core;
@@ -1983,9 +1986,6 @@ static int wl1273_fm_radio_remove(struct platform_device *pdev)
v4l2_ctrl_handler_free(&radio->ctrl_handler);
video_unregister_device(&radio->videodev);
v4l2_device_unregister(&radio->v4l2dev);
- kfree(radio->buffer);
- kfree(radio->write_buf);
- kfree(radio);
return 0;
}
@@ -2005,7 +2005,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
goto pdata_err;
}
- radio = kzalloc(sizeof(*radio), GFP_KERNEL);
+ radio = devm_kzalloc(&pdev->dev, sizeof(*radio), GFP_KERNEL);
if (!radio) {
r = -ENOMEM;
goto pdata_err;
@@ -2013,11 +2013,11 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
/* RDS buffer allocation */
radio->buf_size = rds_buf * RDS_BLOCK_SIZE;
- radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ radio->buffer = devm_kzalloc(&pdev->dev, radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
pr_err("Cannot allocate memory for RDS buffer.\n");
r = -ENOMEM;
- goto err_kmalloc;
+ goto pdata_err;
}
radio->core = *core;
@@ -2043,7 +2043,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
if (r) {
dev_err(radio->dev, WL1273_FM_DRIVER_NAME
": Cannot get platform data\n");
- goto err_resources;
+ goto pdata_err;
}
dev_dbg(radio->dev, "irq: %d\n", radio->core->client->irq);
@@ -2061,13 +2061,13 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
dev_err(radio->dev, WL1273_FM_DRIVER_NAME ": Core WL1273 IRQ"
" not configured");
r = -EINVAL;
- goto err_resources;
+ goto pdata_err;
}
init_completion(&radio->busy);
init_waitqueue_head(&radio->read_queue);
- radio->write_buf = kmalloc(256, GFP_KERNEL);
+ radio->write_buf = devm_kzalloc(&pdev->dev, 256, GFP_KERNEL);
if (!radio->write_buf) {
r = -ENOMEM;
goto write_buf_err;
@@ -2080,7 +2080,7 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
r = v4l2_device_register(&pdev->dev, &radio->v4l2dev);
if (r) {
dev_err(&pdev->dev, "Cannot register v4l2_device.\n");
- goto device_register_err;
+ goto write_buf_err;
}
/* V4L2 configuration */
@@ -2135,16 +2135,10 @@ static int __devinit wl1273_fm_radio_probe(struct platform_device *pdev)
handler_init_err:
v4l2_ctrl_handler_free(&radio->ctrl_handler);
v4l2_device_unregister(&radio->v4l2dev);
-device_register_err:
- kfree(radio->write_buf);
write_buf_err:
free_irq(radio->core->client->irq, radio);
err_request_irq:
radio->core->pdata->free_resources();
-err_resources:
- kfree(radio->buffer);
-err_kmalloc:
- kfree(radio);
pdata_err:
return r;
}
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index bb953ef75f61..54db36ccb9ee 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -199,8 +199,19 @@ static int saa7706h_get_reg16(struct v4l2_subdev *sd, u16 reg)
u8 buf[2];
int err;
u8 regaddr[] = {reg >> 8, reg};
- struct i2c_msg msg[] = { {client->addr, 0, sizeof(regaddr), regaddr},
- {client->addr, I2C_M_RD, sizeof(buf), buf} };
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(regaddr),
+ .buf = regaddr
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf
+ }
+ };
err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg));
if (err)
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 9bb65e170d99..18989388ddc1 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -296,7 +296,7 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
* si470x_set_seek - set seek
*/
static int si470x_set_seek(struct si470x_device *radio,
- struct v4l2_hw_freq_seek *seek)
+ const struct v4l2_hw_freq_seek *seek)
{
int band, retval;
unsigned int freq;
@@ -701,13 +701,16 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
* si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
*/
static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
- struct v4l2_hw_freq_seek *seek)
+ const struct v4l2_hw_freq_seek *seek)
{
struct si470x_device *radio = video_drvdata(file);
if (seek->tuner != 0)
return -EINVAL;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
return si470x_set_seek(radio, seek);
}
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index f867f04cccc9..e5024cfd27a7 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -98,8 +98,12 @@ int si470x_get_register(struct si470x_device *radio, int regnr)
{
u16 buf[READ_REG_NUM];
struct i2c_msg msgs[1] = {
- { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
- (void *)buf },
+ {
+ .addr = radio->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(u16) * READ_REG_NUM,
+ .buf = (void *)buf
+ },
};
if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
@@ -119,8 +123,11 @@ int si470x_set_register(struct si470x_device *radio, int regnr)
int i;
u16 buf[WRITE_REG_NUM];
struct i2c_msg msgs[1] = {
- { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM,
- (void *)buf },
+ {
+ .addr = radio->client->addr,
+ .len = sizeof(u16) * WRITE_REG_NUM,
+ .buf = (void *)buf
+ },
};
for (i = 0; i < WRITE_REG_NUM; i++)
@@ -146,8 +153,12 @@ static int si470x_get_all_registers(struct si470x_device *radio)
int i;
u16 buf[READ_REG_NUM];
struct i2c_msg msgs[1] = {
- { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
- (void *)buf },
+ {
+ .addr = radio->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(u16) * READ_REG_NUM,
+ .buf = (void *)buf
+ },
};
if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index b898c8925ab7..a9e6d17015ef 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -1213,7 +1213,7 @@ exit:
}
static int si4713_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f);
-static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *);
+static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *);
/*
* si4713_setup - Sets the device up with current configuration.
* @sdev: si4713_device structure for the device we are communicating
@@ -1873,7 +1873,7 @@ exit:
}
/* si4713_s_modulator - set modulator attributes */
-static int si4713_s_modulator(struct v4l2_subdev *sd, struct v4l2_modulator *vm)
+static int si4713_s_modulator(struct v4l2_subdev *sd, const struct v4l2_modulator *vm)
{
struct si4713_device *sdev = to_si4713_device(sd);
int rval = 0;
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 49a11ec1f449..048de4536036 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -56,23 +56,29 @@ static ssize_t fm_v4l2_fops_read(struct file *file, char __user * buf,
return -EIO;
}
- /* Turn on RDS mode , if it is disabled */
+ if (mutex_lock_interruptible(&fmdev->mutex))
+ return -ERESTARTSYS;
+
+ /* 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;
+ goto read_unlock;
}
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;
+ goto read_unlock;
}
}
/* Copy RDS data from internal buffer to user buffer */
- return fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count);
+ ret = fmc_transfer_rds_from_internal_buff(fmdev, file, buf, count);
+read_unlock:
+ mutex_unlock(&fmdev->mutex);
+ return ret;
}
/* Write TX RDS data */
@@ -91,8 +97,11 @@ static ssize_t fm_v4l2_fops_write(struct file *file, const char __user * buf,
return -EFAULT;
fmdev = video_drvdata(file);
+ if (mutex_lock_interruptible(&fmdev->mutex))
+ return -ERESTARTSYS;
fm_tx_set_radio_text(fmdev, rds.text, rds.text_type);
fm_tx_set_af(fmdev, rds.af_freq);
+ mutex_unlock(&fmdev->mutex);
return sizeof(rds);
}
@@ -103,7 +112,9 @@ static u32 fm_v4l2_fops_poll(struct file *file, struct poll_table_struct *pts)
struct fmdev *fmdev;
fmdev = video_drvdata(file);
+ mutex_lock(&fmdev->mutex);
ret = fmc_is_rds_data_available(fmdev, file, pts);
+ mutex_unlock(&fmdev->mutex);
if (ret < 0)
return POLLIN | POLLRDNORM;
@@ -127,10 +138,12 @@ static int fm_v4l2_fops_open(struct file *file)
fmdev = video_drvdata(file);
+ if (mutex_lock_interruptible(&fmdev->mutex))
+ return -ERESTARTSYS;
ret = fmc_prepare(fmdev);
if (ret < 0) {
fmerr("Unable to prepare FM CORE\n");
- return ret;
+ goto open_unlock;
}
fmdbg("Load FM RX firmware..\n");
@@ -138,10 +151,12 @@ static int fm_v4l2_fops_open(struct file *file)
ret = fmc_set_mode(fmdev, FM_MODE_RX);
if (ret < 0) {
fmerr("Unable to load FM RX firmware\n");
- return ret;
+ goto open_unlock;
}
radio_disconnected = 1;
+open_unlock:
+ mutex_unlock(&fmdev->mutex);
return ret;
}
@@ -156,19 +171,22 @@ static int fm_v4l2_fops_release(struct file *file)
return 0;
}
+ mutex_lock(&fmdev->mutex);
ret = fmc_set_mode(fmdev, FM_MODE_OFF);
if (ret < 0) {
fmerr("Unable to turn off the chip\n");
- return ret;
+ goto release_unlock;
}
ret = fmc_release(fmdev);
if (ret < 0) {
fmerr("FM CORE release failed\n");
- return ret;
+ goto release_unlock;
}
radio_disconnected = 0;
+release_unlock:
+ mutex_unlock(&fmdev->mutex);
return ret;
}
@@ -240,7 +258,7 @@ static int fm_v4l2_vidioc_g_audio(struct file *file, void *priv,
}
static int fm_v4l2_vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *audio)
+ const struct v4l2_audio *audio)
{
if (audio->index != 0)
return -EINVAL;
@@ -385,11 +403,14 @@ static int fm_v4l2_vidioc_s_freq(struct file *file, void *priv,
/* 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)
+ const struct v4l2_hw_freq_seek *seek)
{
struct fmdev *fmdev = video_drvdata(file);
int ret;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
if (fmdev->curr_fmmode != FM_MODE_RX) {
ret = fmc_set_mode(fmdev, FM_MODE_RX);
if (ret != 0) {
@@ -430,7 +451,7 @@ static int fm_v4l2_vidioc_g_modulator(struct file *file, void *priv,
/* 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)
+ const struct v4l2_modulator *mod)
{
struct fmdev *fmdev = video_drvdata(file);
u8 rds_mode;
@@ -520,10 +541,6 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
video_set_drvdata(gradio_dev, fmdev);
gradio_dev->lock = &fmdev->mutex;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &gradio_dev->flags);
/* Register with V4L2 subsystem as RADIO device */
if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 8be57634ba60..79ba242fe263 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -265,12 +265,40 @@ config IR_IGUANA
depends on RC_CORE
select USB
---help---
- Say Y here if you want to use the IgaunaWorks USB IR Transceiver.
- Both infrared receive and send are supported.
+ Say Y here if you want to use the IguanaWorks USB IR Transceiver.
+ Both infrared receive and send are supported. If you want to
+ change the ID or the pin config, use the user space driver from
+ IguanaWorks.
+
+ Only firmware 0x0205 and later is supported.
To compile this driver as a module, choose M here: the module will
be called iguanair.
+config IR_TTUSBIR
+ tristate "TechnoTrend USB IR Receiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ select NEW_LEDS
+ select LEDS_CLASS
+ ---help---
+ Say Y here if you want to use the TechnoTrend USB IR Receiver. The
+ driver can control the led.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ttusbir.
+
+config IR_RX51
+ tristate "Nokia N900 IR transmitter diode"
+ depends on OMAP_DM_TIMER && LIRC
+ ---help---
+ Say Y or M here if you want to enable support for the IR
+ transmitter diode built in the Nokia N900 (RX51) device.
+
+ The driver uses omap DM timers for generating the carrier
+ wave and pulses.
+
config RC_LOOPBACK
tristate "Remote Control Loopback Driver"
depends on RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index f871d1986c21..56bacf07b361 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -23,8 +23,10 @@ obj-$(CONFIG_IR_FINTEK) += fintek-cir.o
obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
obj-$(CONFIG_IR_REDRAT3) += redrat3.o
+obj-$(CONFIG_IR_RX51) += ir-rx51.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
obj-$(CONFIG_IR_IGUANA) += iguanair.o
+obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index 8fa72e2dacb1..49bb356ed14c 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -331,13 +331,9 @@ static void ati_remote_dump(struct device *dev, unsigned char *data,
if (data[0] != (unsigned char)0xff && data[0] != 0x00)
dev_warn(dev, "Weird byte 0x%02x\n", data[0]);
} else if (len == 4)
- dev_warn(dev, "Weird key %02x %02x %02x %02x\n",
- data[0], data[1], data[2], data[3]);
+ dev_warn(dev, "Weird key %*ph\n", 4, data);
else
- dev_warn(dev,
- "Weird data, len=%d %02x %02x %02x %02x %02x %02x ...\n",
- len, data[0], data[1], data[2], data[3], data[4],
- data[5]);
+ dev_warn(dev, "Weird data, len=%d %*ph ...\n", len, 6, data);
}
/*
@@ -519,8 +515,7 @@ static void ati_remote_input_report(struct urb *urb)
if (data[1] != ((data[2] + data[3] + 0xd5) & 0xff)) {
dbginfo(&ati_remote->interface->dev,
- "wrong checksum in input: %02x %02x %02x %02x\n",
- data[0], data[1], data[2], data[3]);
+ "wrong checksum in input: %*ph\n", 4, data);
return;
}
@@ -942,8 +937,10 @@ static int ati_remote_probe(struct usb_interface *interface,
/* Set up and register mouse input device */
if (mouse) {
input_dev = input_allocate_device();
- if (!input_dev)
+ if (!input_dev) {
+ err = -ENOMEM;
goto fail4;
+ }
ati_remote->idev = input_dev;
ati_remote_input_init(ati_remote);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index ab30c64f8124..52fd7696b1ba 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -295,6 +295,7 @@ static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
{
DEFINE_IR_RAW_EVENT(rawir);
u8 sample;
+ bool event = false;
int i;
for (i = 0; i < fintek->pkts; i++) {
@@ -332,7 +333,9 @@ static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
fit_dbg("Storing %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
- ir_raw_event_store_with_filter(fintek->rdev, &rawir);
+ if (ir_raw_event_store_with_filter(fintek->rdev,
+ &rawir))
+ event = true;
break;
}
@@ -342,8 +345,10 @@ static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
fintek->pkts = 0;
- fit_dbg("Calling ir_raw_event_handle");
- ir_raw_event_handle(fintek->rdev);
+ if (event) {
+ fit_dbg("Calling ir_raw_event_handle");
+ ir_raw_event_handle(fintek->rdev);
+ }
}
/* copy data from hardware rx register into driver buffer */
diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 5e2eaf8ba73e..1e4c68a5cecf 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -35,9 +35,9 @@ struct iguanair {
struct device *dev;
struct usb_device *udev;
- int pipe_in, pipe_out;
+ int pipe_out;
+ uint16_t version;
uint8_t bufsize;
- uint8_t version[2];
struct mutex lock;
@@ -75,6 +75,7 @@ struct iguanair {
#define MAX_PACKET_SIZE 8u
#define TIMEOUT 1000
+#define RX_RESOLUTION 21333
struct packet {
uint16_t start;
@@ -82,11 +83,6 @@ struct packet {
uint8_t cmd;
};
-struct response_packet {
- struct packet header;
- uint8_t data[4];
-};
-
struct send_packet {
struct packet header;
uint8_t length;
@@ -100,6 +96,25 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
{
if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
switch (ir->buf_in[3]) {
+ case CMD_GET_VERSION:
+ if (len == 6) {
+ ir->version = (ir->buf_in[5] << 8) |
+ ir->buf_in[4];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_BUFSIZE:
+ if (len >= 5) {
+ ir->bufsize = ir->buf_in[4];
+ complete(&ir->completion);
+ }
+ break;
+ case CMD_GET_FEATURES:
+ if (len > 5) {
+ ir->cycle_overhead = ir->buf_in[5];
+ complete(&ir->completion);
+ }
+ break;
case CMD_TX_OVERFLOW:
ir->tx_overflow = true;
case CMD_RECEIVER_OFF:
@@ -109,6 +124,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
break;
case CMD_RX_OVERFLOW:
dev_warn(ir->dev, "receive overflow\n");
+ ir_raw_event_reset(ir->rc);
break;
default:
dev_warn(ir->dev, "control code %02x received\n",
@@ -118,6 +134,7 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
} else if (len >= 7) {
DEFINE_IR_RAW_EVENT(rawir);
unsigned i;
+ bool event = false;
init_ir_raw_event(&rawir);
@@ -128,19 +145,22 @@ static void process_ir_data(struct iguanair *ir, unsigned len)
} else {
rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
- 21330;
+ RX_RESOLUTION;
}
- ir_raw_event_store_with_filter(ir->rc, &rawir);
+ if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+ event = true;
}
- ir_raw_event_handle(ir->rc);
+ if (event)
+ ir_raw_event_handle(ir->rc);
}
}
static void iguanair_rx(struct urb *urb)
{
struct iguanair *ir;
+ int rc;
if (!urb)
return;
@@ -166,34 +186,27 @@ static void iguanair_rx(struct urb *urb)
break;
}
- usb_submit_urb(urb, GFP_ATOMIC);
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc && rc != -ENODEV)
+ dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
}
-static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
- struct response_packet *response, unsigned *res_len)
+static int iguanair_send(struct iguanair *ir, void *data, unsigned size)
{
- unsigned offset, len;
int rc, transferred;
- for (offset = 0; offset < size; offset += MAX_PACKET_SIZE) {
- len = min(size - offset, MAX_PACKET_SIZE);
-
- if (ir->tx_overflow)
- return -EOVERFLOW;
+ INIT_COMPLETION(ir->completion);
- rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data + offset,
- len, &transferred, TIMEOUT);
- if (rc)
- return rc;
+ rc = usb_interrupt_msg(ir->udev, ir->pipe_out, data, size,
+ &transferred, TIMEOUT);
+ if (rc)
+ return rc;
- if (transferred != len)
- return -EIO;
- }
+ if (transferred != size)
+ return -EIO;
- if (response) {
- rc = usb_interrupt_msg(ir->udev, ir->pipe_in, response,
- sizeof(*response), res_len, TIMEOUT);
- }
+ if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0)
+ return -ETIMEDOUT;
return rc;
}
@@ -201,66 +214,43 @@ static int iguanair_send(struct iguanair *ir, void *data, unsigned size,
static int iguanair_get_features(struct iguanair *ir)
{
struct packet packet;
- struct response_packet response;
- int rc, len;
+ int rc;
packet.start = 0;
packet.direction = DIR_OUT;
packet.cmd = CMD_GET_VERSION;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get version\n");
goto out;
}
- if (len != 6) {
- dev_info(ir->dev, "failed to get version\n");
- rc = -EIO;
+ if (ir->version < 0x205) {
+ dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
+ rc = -ENODEV;
goto out;
}
- ir->version[0] = response.data[0];
- ir->version[1] = response.data[1];
ir->bufsize = 150;
ir->cycle_overhead = 65;
packet.cmd = CMD_GET_BUFSIZE;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get buffer size\n");
goto out;
}
- if (len != 5) {
- dev_info(ir->dev, "failed to get buffer size\n");
- rc = -EIO;
- goto out;
- }
-
- ir->bufsize = response.data[0];
-
- if (ir->version[0] == 0 || ir->version[1] == 0)
- goto out;
-
packet.cmd = CMD_GET_FEATURES;
- rc = iguanair_send(ir, &packet, sizeof(packet), &response, &len);
+ rc = iguanair_send(ir, &packet, sizeof(packet));
if (rc) {
dev_info(ir->dev, "failed to get features\n");
goto out;
}
- if (len < 5) {
- dev_info(ir->dev, "failed to get features\n");
- rc = -EIO;
- goto out;
- }
-
- if (len > 5 && ir->version[0] >= 4)
- ir->cycle_overhead = response.data[1];
-
out:
return rc;
}
@@ -269,17 +259,11 @@ static int iguanair_receiver(struct iguanair *ir, bool enable)
{
struct packet packet = { 0, DIR_OUT, enable ?
CMD_RECEIVER_ON : CMD_RECEIVER_OFF };
- int rc;
-
- INIT_COMPLETION(ir->completion);
- rc = iguanair_send(ir, &packet, sizeof(packet), NULL, NULL);
- if (rc)
- return rc;
-
- wait_for_completion_timeout(&ir->completion, TIMEOUT);
+ if (enable)
+ ir_raw_event_reset(ir->rc);
- return 0;
+ return iguanair_send(ir, &packet, sizeof(packet));
}
/*
@@ -350,26 +334,38 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct iguanair *ir = dev->priv;
- uint8_t space, *payload;
- unsigned i, size, rc;
+ uint8_t space;
+ unsigned i, size, periods, bytes;
+ int rc;
struct send_packet *packet;
mutex_lock(&ir->lock);
- /* convert from us to carrier periods */
- for (i = size = 0; i < count; i++) {
- txbuf[i] = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
- size += (txbuf[i] + 126) / 127;
- }
-
- packet = kmalloc(sizeof(*packet) + size, GFP_KERNEL);
+ packet = kmalloc(sizeof(*packet) + ir->bufsize, GFP_KERNEL);
if (!packet) {
rc = -ENOMEM;
goto out;
}
- if (size > ir->bufsize) {
- rc = -E2BIG;
+ /* convert from us to carrier periods */
+ for (i = space = size = 0; i < count; i++) {
+ periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
+ bytes = DIV_ROUND_UP(periods, 127);
+ if (size + bytes > ir->bufsize) {
+ count = i;
+ break;
+ }
+ while (periods > 127) {
+ packet->payload[size++] = 127 | space;
+ periods -= 127;
+ }
+
+ packet->payload[size++] = periods | space;
+ space ^= 0x80;
+ }
+
+ if (count == 0) {
+ rc = -EINVAL;
goto out;
}
@@ -381,21 +377,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
packet->busy7 = ir->busy7;
packet->busy4 = ir->busy4;
- space = 0;
- payload = packet->payload;
-
- for (i = 0; i < count; i++) {
- unsigned periods = txbuf[i];
-
- while (periods > 127) {
- *payload++ = 127 | space;
- periods -= 127;
- }
-
- *payload++ = periods | space;
- space ^= 0x80;
- }
-
if (ir->receiver_on) {
rc = iguanair_receiver(ir, false);
if (rc) {
@@ -406,17 +387,10 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
ir->tx_overflow = false;
- INIT_COMPLETION(ir->completion);
+ rc = iguanair_send(ir, packet, size + 8);
- rc = iguanair_send(ir, packet, size + 8, NULL, NULL);
-
- if (rc == 0) {
- wait_for_completion_timeout(&ir->completion, TIMEOUT);
- if (ir->tx_overflow)
- rc = -EOVERFLOW;
- }
-
- ir->tx_overflow = false;
+ if (rc == 0 && ir->tx_overflow)
+ rc = -EOVERFLOW;
if (ir->receiver_on) {
if (iguanair_receiver(ir, true))
@@ -424,10 +398,10 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
}
out:
- mutex_unlock(&ir->lock);
kfree(packet);
+ mutex_unlock(&ir->lock);
- return rc;
+ return rc ? rc : count;
}
static int iguanair_open(struct rc_dev *rdev)
@@ -437,8 +411,6 @@ static int iguanair_open(struct rc_dev *rdev)
mutex_lock(&ir->lock);
- usb_submit_urb(ir->urb_in, GFP_KERNEL);
-
BUG_ON(ir->receiver_on);
rc = iguanair_receiver(ir, true);
@@ -459,11 +431,9 @@ static void iguanair_close(struct rc_dev *rdev)
rc = iguanair_receiver(ir, false);
ir->receiver_on = false;
- if (rc)
+ if (rc && rc != -ENODEV)
dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
- usb_kill_urb(ir->urb_in);
-
mutex_unlock(&ir->lock);
}
@@ -473,22 +443,22 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
struct usb_device *udev = interface_to_usbdev(intf);
struct iguanair *ir;
struct rc_dev *rc;
- int ret;
+ int ret, pipein;
struct usb_host_interface *idesc;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
rc = rc_allocate_device();
if (!ir || !rc) {
- ret = ENOMEM;
+ ret = -ENOMEM;
goto out;
}
- ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_ATOMIC,
+ ir->buf_in = usb_alloc_coherent(udev, MAX_PACKET_SIZE, GFP_KERNEL,
&ir->dma_in);
ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
if (!ir->buf_in || !ir->urb_in) {
- ret = ENOMEM;
+ ret = -ENOMEM;
goto out;
}
@@ -502,28 +472,29 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
ir->rc = rc;
ir->dev = &intf->dev;
ir->udev = udev;
- ir->pipe_in = usb_rcvintpipe(udev,
- idesc->endpoint[0].desc.bEndpointAddress);
ir->pipe_out = usb_sndintpipe(udev,
idesc->endpoint[1].desc.bEndpointAddress);
mutex_init(&ir->lock);
init_completion(&ir->completion);
- ret = iguanair_get_features(ir);
+ pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
+ usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_PACKET_SIZE,
+ iguanair_rx, ir, 1);
+ ir->urb_in->transfer_dma = ir->dma_in;
+ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
if (ret) {
- dev_warn(&intf->dev, "failed to get device features");
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
goto out;
}
- usb_fill_int_urb(ir->urb_in, ir->udev, ir->pipe_in, ir->buf_in,
- MAX_PACKET_SIZE, iguanair_rx, ir,
- idesc->endpoint[0].desc.bInterval);
- ir->urb_in->transfer_dma = ir->dma_in;
- ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ ret = iguanair_get_features(ir);
+ if (ret)
+ goto out2;
snprintf(ir->name, sizeof(ir->name),
- "IguanaWorks USB IR Transceiver version %d.%d",
- ir->version[0], ir->version[1]);
+ "IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
@@ -540,21 +511,23 @@ static int __devinit iguanair_probe(struct usb_interface *intf,
rc->s_tx_carrier = iguanair_set_tx_carrier;
rc->tx_ir = iguanair_tx;
rc->driver_name = DRIVER_NAME;
- rc->map_name = RC_MAP_EMPTY;
+ rc->map_name = RC_MAP_RC6_MCE;
+ rc->timeout = MS_TO_NS(100);
+ rc->rx_resolution = RX_RESOLUTION;
iguanair_set_tx_carrier(rc, 38000);
ret = rc_register_device(rc);
if (ret < 0) {
dev_err(&intf->dev, "failed to register rc device %d", ret);
- goto out;
+ goto out2;
}
usb_set_intfdata(intf, ir);
- dev_info(&intf->dev, "Registered %s", ir->name);
-
return 0;
+out2:
+ usb_kill_urb(ir->urb_in);
out:
if (ir) {
usb_free_urb(ir->urb_in);
@@ -570,12 +543,11 @@ static void __devexit iguanair_disconnect(struct usb_interface *intf)
{
struct iguanair *ir = usb_get_intfdata(intf);
+ rc_unregister_device(ir->rc);
usb_set_intfdata(intf, NULL);
-
usb_kill_urb(ir->urb_in);
usb_free_urb(ir->urb_in);
usb_free_coherent(ir->udev, MAX_PACKET_SIZE, ir->buf_in, ir->dma_in);
- rc_unregister_device(ir->rc);
kfree(ir);
}
@@ -592,6 +564,8 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
dev_warn(ir->dev, "failed to disable receiver for suspend\n");
}
+ usb_kill_urb(ir->urb_in);
+
mutex_unlock(&ir->lock);
return rc;
@@ -604,6 +578,10 @@ static int iguanair_resume(struct usb_interface *intf)
mutex_lock(&ir->lock);
+ rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
+ if (rc)
+ dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
+
if (ir->receiver_on) {
rc = iguanair_receiver(ir, true);
if (rc)
@@ -627,7 +605,8 @@ static struct usb_driver iguanair_driver = {
.suspend = iguanair_suspend,
.resume = iguanair_resume,
.reset_resume = iguanair_resume,
- .id_table = iguanair_table
+ .id_table = iguanair_table,
+ .soft_unbind = 1 /* we want to disable receiver on unbind */
};
module_usb_driver(iguanair_driver);
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index 5faba2a2fdd3..569124b03de3 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -105,8 +105,14 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
struct lirc_codec *lirc;
struct rc_dev *dev;
unsigned int *txbuf; /* buffer with values to transmit */
- ssize_t ret = 0;
+ ssize_t ret = -EINVAL;
size_t count;
+ ktime_t start;
+ s64 towait;
+ unsigned int duration = 0; /* signal duration in us */
+ int i;
+
+ start = ktime_get();
lirc = lirc_get_pdata(file);
if (!lirc)
@@ -129,11 +135,30 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
goto out;
}
- if (dev->tx_ir)
- ret = dev->tx_ir(dev, txbuf, count);
+ if (!dev->tx_ir) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ ret = dev->tx_ir(dev, txbuf, count);
+ if (ret < 0)
+ goto out;
+
+ for (i = 0; i < ret; i++)
+ duration += txbuf[i];
- if (ret > 0)
- ret *= sizeof(unsigned);
+ ret *= sizeof(unsigned int);
+
+ /*
+ * The lircd gap calculation expects the write function to
+ * wait for the actual IR signal to be transmitted before
+ * returning.
+ */
+ towait = ktime_us_delta(ktime_add_us(start, duration), ktime_get());
+ if (towait > 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(towait));
+ }
out:
kfree(txbuf);
diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c
index 3c9431a9f62d..2ca509e6e16b 100644
--- a/drivers/media/rc/ir-nec-decoder.c
+++ b/drivers/media/rc/ir-nec-decoder.c
@@ -70,7 +70,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (!ev.pulse)
break;
- if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT / 2)) {
+ if (eq_margin(ev.duration, NEC_HEADER_PULSE, NEC_UNIT * 2)) {
data->is_nec_x = false;
data->necx_repeat = false;
} else if (eq_margin(ev.duration, NECX_HEADER_PULSE, NEC_UNIT / 2))
@@ -86,7 +86,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev)
if (ev.pulse)
break;
- if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT / 2)) {
+ if (eq_margin(ev.duration, NEC_HEADER_SPACE, NEC_UNIT)) {
data->state = STATE_BIT_PULSE;
return 0;
} else if (eq_margin(ev.duration, NEC_REPEAT_SPACE, NEC_UNIT / 2)) {
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index a82025121345..97dc8d13b06b 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -157,7 +157,9 @@ EXPORT_SYMBOL_GPL(ir_raw_event_store_edge);
* This routine (which may be called from an interrupt context) works
* in similar manner to ir_raw_event_store_edge.
* This routine is intended for devices with limited internal buffer
- * It automerges samples of same type, and handles timeouts
+ * It automerges samples of same type, and handles timeouts. Returns non-zero
+ * if the event was added, and zero if the event was ignored due to idle
+ * processing.
*/
int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
{
@@ -184,7 +186,7 @@ int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
dev->raw->this_ev.duration >= dev->timeout)
ir_raw_event_set_idle(dev, true);
- return 0;
+ return 1;
}
EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
new file mode 100644
index 000000000000..546199e9ccc7
--- /dev/null
+++ b/drivers/media/rc/ir-rx51.c
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Based on lirc_serial.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include <plat/dmtimer.h>
+#include <plat/clock.h>
+#include <plat/omap-pm.h>
+
+#include <media/lirc.h>
+#include <media/lirc_dev.h>
+#include <media/ir-rx51.h>
+
+#define LIRC_RX51_DRIVER_FEATURES (LIRC_CAN_SET_SEND_DUTY_CYCLE | \
+ LIRC_CAN_SET_SEND_CARRIER | \
+ LIRC_CAN_SEND_PULSE)
+
+#define DRIVER_NAME "lirc_rx51"
+
+#define WBUF_LEN 256
+
+#define TIMER_MAX_VALUE 0xffffffff
+
+struct lirc_rx51 {
+ struct omap_dm_timer *pwm_timer;
+ struct omap_dm_timer *pulse_timer;
+ struct device *dev;
+ struct lirc_rx51_platform_data *pdata;
+ wait_queue_head_t wqueue;
+
+ unsigned long fclk_khz;
+ unsigned int freq; /* carrier frequency */
+ unsigned int duty_cycle; /* carrier duty cycle */
+ unsigned int irq_num;
+ unsigned int match;
+ int wbuf[WBUF_LEN];
+ int wbuf_index;
+ unsigned long device_is_open;
+ int pwm_timer_num;
+};
+
+static void lirc_rx51_on(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+}
+
+static void lirc_rx51_off(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_pwm(lirc_rx51->pwm_timer, 0, 1,
+ OMAP_TIMER_TRIGGER_NONE);
+}
+
+static int init_timing_params(struct lirc_rx51 *lirc_rx51)
+{
+ u32 load, match;
+
+ load = -(lirc_rx51->fclk_khz * 1000 / lirc_rx51->freq);
+ match = -(lirc_rx51->duty_cycle * -load / 100);
+ omap_dm_timer_set_load(lirc_rx51->pwm_timer, 1, load);
+ omap_dm_timer_set_match(lirc_rx51->pwm_timer, 1, match);
+ omap_dm_timer_write_counter(lirc_rx51->pwm_timer, TIMER_MAX_VALUE - 2);
+ omap_dm_timer_start(lirc_rx51->pwm_timer);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ omap_dm_timer_start(lirc_rx51->pulse_timer);
+
+ lirc_rx51->match = 0;
+
+ return 0;
+}
+
+#define tics_after(a, b) ((long)(b) - (long)(a) < 0)
+
+static int pulse_timer_set_timeout(struct lirc_rx51 *lirc_rx51, int usec)
+{
+ int counter;
+
+ BUG_ON(usec < 0);
+
+ if (lirc_rx51->match == 0)
+ counter = omap_dm_timer_read_counter(lirc_rx51->pulse_timer);
+ else
+ counter = lirc_rx51->match;
+
+ counter += (u32)(lirc_rx51->fclk_khz * usec / (1000));
+ omap_dm_timer_set_match(lirc_rx51->pulse_timer, 1, counter);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer,
+ OMAP_TIMER_INT_MATCH);
+ if (tics_after(omap_dm_timer_read_counter(lirc_rx51->pulse_timer),
+ counter)) {
+ return 1;
+ }
+ return 0;
+}
+
+static irqreturn_t lirc_rx51_interrupt_handler(int irq, void *ptr)
+{
+ unsigned int retval;
+ struct lirc_rx51 *lirc_rx51 = ptr;
+
+ retval = omap_dm_timer_read_status(lirc_rx51->pulse_timer);
+ if (!retval)
+ return IRQ_NONE;
+
+ if (retval & ~OMAP_TIMER_INT_MATCH)
+ dev_err_ratelimited(lirc_rx51->dev,
+ ": Unexpected interrupt source: %x\n", retval);
+
+ omap_dm_timer_write_status(lirc_rx51->pulse_timer,
+ OMAP_TIMER_INT_MATCH |
+ OMAP_TIMER_INT_OVERFLOW |
+ OMAP_TIMER_INT_CAPTURE);
+ if (lirc_rx51->wbuf_index < 0) {
+ dev_err_ratelimited(lirc_rx51->dev,
+ ": BUG wbuf_index has value of %i\n",
+ lirc_rx51->wbuf_index);
+ goto end;
+ }
+
+ /*
+ * If we happen to hit an odd latency spike, loop through the
+ * pulses until we catch up.
+ */
+ do {
+ if (lirc_rx51->wbuf_index >= WBUF_LEN)
+ goto end;
+ if (lirc_rx51->wbuf[lirc_rx51->wbuf_index] == -1)
+ goto end;
+
+ if (lirc_rx51->wbuf_index % 2)
+ lirc_rx51_off(lirc_rx51);
+ else
+ lirc_rx51_on(lirc_rx51);
+
+ retval = pulse_timer_set_timeout(lirc_rx51,
+ lirc_rx51->wbuf[lirc_rx51->wbuf_index]);
+ lirc_rx51->wbuf_index++;
+
+ } while (retval);
+
+ return IRQ_HANDLED;
+end:
+ /* Stop TX here */
+ lirc_rx51_off(lirc_rx51);
+ lirc_rx51->wbuf_index = -1;
+ omap_dm_timer_stop(lirc_rx51->pwm_timer);
+ omap_dm_timer_stop(lirc_rx51->pulse_timer);
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ wake_up_interruptible(&lirc_rx51->wqueue);
+
+ return IRQ_HANDLED;
+}
+
+static int lirc_rx51_init_port(struct lirc_rx51 *lirc_rx51)
+{
+ struct clk *clk_fclk;
+ int retval, pwm_timer = lirc_rx51->pwm_timer_num;
+
+ lirc_rx51->pwm_timer = omap_dm_timer_request_specific(pwm_timer);
+ if (lirc_rx51->pwm_timer == NULL) {
+ dev_err(lirc_rx51->dev, ": Error requesting GPT%d timer\n",
+ pwm_timer);
+ return -EBUSY;
+ }
+
+ lirc_rx51->pulse_timer = omap_dm_timer_request();
+ if (lirc_rx51->pulse_timer == NULL) {
+ dev_err(lirc_rx51->dev, ": Error requesting pulse timer\n");
+ retval = -EBUSY;
+ goto err1;
+ }
+
+ omap_dm_timer_set_source(lirc_rx51->pwm_timer, OMAP_TIMER_SRC_SYS_CLK);
+ omap_dm_timer_set_source(lirc_rx51->pulse_timer,
+ OMAP_TIMER_SRC_SYS_CLK);
+
+ omap_dm_timer_enable(lirc_rx51->pwm_timer);
+ omap_dm_timer_enable(lirc_rx51->pulse_timer);
+
+ lirc_rx51->irq_num = omap_dm_timer_get_irq(lirc_rx51->pulse_timer);
+ retval = request_irq(lirc_rx51->irq_num, lirc_rx51_interrupt_handler,
+ IRQF_DISABLED | IRQF_SHARED,
+ "lirc_pulse_timer", lirc_rx51);
+ if (retval) {
+ dev_err(lirc_rx51->dev, ": Failed to request interrupt line\n");
+ goto err2;
+ }
+
+ clk_fclk = omap_dm_timer_get_fclk(lirc_rx51->pwm_timer);
+ lirc_rx51->fclk_khz = clk_fclk->rate / 1000;
+
+ return 0;
+
+err2:
+ omap_dm_timer_free(lirc_rx51->pulse_timer);
+err1:
+ omap_dm_timer_free(lirc_rx51->pwm_timer);
+
+ return retval;
+}
+
+static int lirc_rx51_free_port(struct lirc_rx51 *lirc_rx51)
+{
+ omap_dm_timer_set_int_enable(lirc_rx51->pulse_timer, 0);
+ free_irq(lirc_rx51->irq_num, lirc_rx51);
+ lirc_rx51_off(lirc_rx51);
+ omap_dm_timer_disable(lirc_rx51->pwm_timer);
+ omap_dm_timer_disable(lirc_rx51->pulse_timer);
+ omap_dm_timer_free(lirc_rx51->pwm_timer);
+ omap_dm_timer_free(lirc_rx51->pulse_timer);
+ lirc_rx51->wbuf_index = -1;
+
+ return 0;
+}
+
+static ssize_t lirc_rx51_write(struct file *file, const char *buf,
+ size_t n, loff_t *ppos)
+{
+ int count, i;
+ struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+ if (n % sizeof(int))
+ return -EINVAL;
+
+ count = n / sizeof(int);
+ if ((count > WBUF_LEN) || (count % 2 == 0))
+ return -EINVAL;
+
+ /* Wait any pending transfers to finish */
+ wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+ if (copy_from_user(lirc_rx51->wbuf, buf, n))
+ return -EFAULT;
+
+ /* Sanity check the input pulses */
+ for (i = 0; i < count; i++)
+ if (lirc_rx51->wbuf[i] < 0)
+ return -EINVAL;
+
+ init_timing_params(lirc_rx51);
+ if (count < WBUF_LEN)
+ lirc_rx51->wbuf[count] = -1; /* Insert termination mark */
+
+ /*
+ * Adjust latency requirements so the device doesn't go in too
+ * deep sleep states
+ */
+ lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, 50);
+
+ lirc_rx51_on(lirc_rx51);
+ lirc_rx51->wbuf_index = 1;
+ pulse_timer_set_timeout(lirc_rx51, lirc_rx51->wbuf[0]);
+
+ /*
+ * Don't return back to the userspace until the transfer has
+ * finished
+ */
+ wait_event_interruptible(lirc_rx51->wqueue, lirc_rx51->wbuf_index < 0);
+
+ /* We can sleep again */
+ lirc_rx51->pdata->set_max_mpu_wakeup_lat(lirc_rx51->dev, -1);
+
+ return n;
+}
+
+static long lirc_rx51_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ int result;
+ unsigned long value;
+ unsigned int ivalue;
+ struct lirc_rx51 *lirc_rx51 = filep->private_data;
+
+ switch (cmd) {
+ case LIRC_GET_SEND_MODE:
+ result = put_user(LIRC_MODE_PULSE, (unsigned long *)arg);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_SET_SEND_MODE:
+ result = get_user(value, (unsigned long *)arg);
+ if (result)
+ return result;
+
+ /* only LIRC_MODE_PULSE supported */
+ if (value != LIRC_MODE_PULSE)
+ return -ENOSYS;
+ break;
+
+ case LIRC_GET_REC_MODE:
+ result = put_user(0, (unsigned long *) arg);
+ if (result)
+ return result;
+ break;
+
+ case LIRC_GET_LENGTH:
+ return -ENOSYS;
+ break;
+
+ case LIRC_SET_SEND_DUTY_CYCLE:
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
+
+ if (ivalue <= 0 || ivalue > 100) {
+ dev_err(lirc_rx51->dev, ": invalid duty cycle %d\n",
+ ivalue);
+ return -EINVAL;
+ }
+
+ lirc_rx51->duty_cycle = ivalue;
+ break;
+
+ case LIRC_SET_SEND_CARRIER:
+ result = get_user(ivalue, (unsigned int *) arg);
+ if (result)
+ return result;
+
+ if (ivalue > 500000 || ivalue < 20000) {
+ dev_err(lirc_rx51->dev, ": invalid carrier freq %d\n",
+ ivalue);
+ return -EINVAL;
+ }
+
+ lirc_rx51->freq = ivalue;
+ break;
+
+ case LIRC_GET_FEATURES:
+ result = put_user(LIRC_RX51_DRIVER_FEATURES,
+ (unsigned long *) arg);
+ if (result)
+ return result;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int lirc_rx51_open(struct inode *inode, struct file *file)
+{
+ struct lirc_rx51 *lirc_rx51 = lirc_get_pdata(file);
+ BUG_ON(!lirc_rx51);
+
+ file->private_data = lirc_rx51;
+
+ if (test_and_set_bit(1, &lirc_rx51->device_is_open))
+ return -EBUSY;
+
+ return lirc_rx51_init_port(lirc_rx51);
+}
+
+static int lirc_rx51_release(struct inode *inode, struct file *file)
+{
+ struct lirc_rx51 *lirc_rx51 = file->private_data;
+
+ lirc_rx51_free_port(lirc_rx51);
+
+ clear_bit(1, &lirc_rx51->device_is_open);
+
+ return 0;
+}
+
+static struct lirc_rx51 lirc_rx51 = {
+ .freq = 38000,
+ .duty_cycle = 50,
+ .wbuf_index = -1,
+};
+
+static const struct file_operations lirc_fops = {
+ .owner = THIS_MODULE,
+ .write = lirc_rx51_write,
+ .unlocked_ioctl = lirc_rx51_ioctl,
+ .read = lirc_dev_fop_read,
+ .poll = lirc_dev_fop_poll,
+ .open = lirc_rx51_open,
+ .release = lirc_rx51_release,
+};
+
+static struct lirc_driver lirc_rx51_driver = {
+ .name = DRIVER_NAME,
+ .minor = -1,
+ .code_length = 1,
+ .data = &lirc_rx51,
+ .fops = &lirc_fops,
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+static int lirc_rx51_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /*
+ * In case the device is still open, do not suspend. Normally
+ * this should not be a problem as lircd only keeps the device
+ * open only for short periods of time. We also don't want to
+ * get involved with race conditions that might happen if we
+ * were in a middle of a transmit. Thus, we defer any suspend
+ * actions until transmit has completed.
+ */
+ if (test_and_set_bit(1, &lirc_rx51.device_is_open))
+ return -EAGAIN;
+
+ clear_bit(1, &lirc_rx51.device_is_open);
+
+ return 0;
+}
+
+static int lirc_rx51_resume(struct platform_device *dev)
+{
+ return 0;
+}
+
+#else
+
+#define lirc_rx51_suspend NULL
+#define lirc_rx51_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int __devinit lirc_rx51_probe(struct platform_device *dev)
+{
+ lirc_rx51_driver.features = LIRC_RX51_DRIVER_FEATURES;
+ lirc_rx51.pdata = dev->dev.platform_data;
+ lirc_rx51.pwm_timer_num = lirc_rx51.pdata->pwm_timer;
+ lirc_rx51.dev = &dev->dev;
+ lirc_rx51_driver.dev = &dev->dev;
+ lirc_rx51_driver.minor = lirc_register_driver(&lirc_rx51_driver);
+ init_waitqueue_head(&lirc_rx51.wqueue);
+
+ if (lirc_rx51_driver.minor < 0) {
+ dev_err(lirc_rx51.dev, ": lirc_register_driver failed: %d\n",
+ lirc_rx51_driver.minor);
+ return lirc_rx51_driver.minor;
+ }
+ dev_info(lirc_rx51.dev, "registration ok, minor: %d, pwm: %d\n",
+ lirc_rx51_driver.minor, lirc_rx51.pwm_timer_num);
+
+ return 0;
+}
+
+static int __exit lirc_rx51_remove(struct platform_device *dev)
+{
+ return lirc_unregister_driver(lirc_rx51_driver.minor);
+}
+
+struct platform_driver lirc_rx51_platform_driver = {
+ .probe = lirc_rx51_probe,
+ .remove = __exit_p(lirc_rx51_remove),
+ .suspend = lirc_rx51_suspend,
+ .resume = lirc_rx51_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init lirc_rx51_init(void)
+{
+ return platform_driver_register(&lirc_rx51_platform_driver);
+}
+module_init(lirc_rx51_init);
+
+static void __exit lirc_rx51_exit(void)
+{
+ platform_driver_unregister(&lirc_rx51_platform_driver);
+}
+module_exit(lirc_rx51_exit);
+
+MODULE_DESCRIPTION("LIRC TX driver for Nokia RX51");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 36fe5a349b95..24c77a42fc36 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1473,6 +1473,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
rdev = rc_allocate_device();
if (!rdev)
goto failure;
+ itdev->rdev = rdev;
ret = -ENODEV;
@@ -1604,7 +1605,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
if (ret)
goto failure3;
- itdev->rdev = rdev;
ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
return 0;
diff --git a/drivers/media/rc/keymaps/rc-tt-1500.c b/drivers/media/rc/keymaps/rc-tt-1500.c
index caeff85603e3..80217ffc91db 100644
--- a/drivers/media/rc/keymaps/rc-tt-1500.c
+++ b/drivers/media/rc/keymaps/rc-tt-1500.c
@@ -61,7 +61,7 @@ static struct rc_map_list tt_1500_map = {
.map = {
.scan = tt_1500,
.size = ARRAY_SIZE(tt_1500),
- .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .rc_type = RC_TYPE_RC5,
.name = RC_MAP_TT_1500,
}
};
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index f38d9a8c6880..850547fe711c 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -627,7 +627,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
break;
case MCE_RSP_EQIRCFS:
period = DIV_ROUND_CLOSEST(
- (1 << data1 * 2) * (data2 + 1), 10);
+ (1U << data1 * 2) * (data2 + 1), 10);
if (!period)
break;
carrier = (1000 * 1000) / period;
@@ -791,10 +791,6 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
int i, ret = 0;
int cmdcount = 0;
unsigned char *cmdbuf; /* MCE command buffer */
- long signal_duration = 0; /* Singnal length in us */
- struct timeval start_time, end_time;
-
- do_gettimeofday(&start_time);
cmdbuf = kzalloc(sizeof(unsigned) * MCE_CMDBUF_SIZE, GFP_KERNEL);
if (!cmdbuf)
@@ -807,7 +803,6 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
/* Generate mce packet data */
for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
- signal_duration += txbuf[i];
txbuf[i] = txbuf[i] / MCE_TIME_UNIT;
do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
@@ -850,19 +845,6 @@ static int mceusb_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
/* Transmit the command to the mce device */
mce_async_out(ir, cmdbuf, cmdcount);
- /*
- * The lircd gap calculation expects the write function to
- * wait the time it takes for the ircommand to be sent before
- * it returns.
- */
- do_gettimeofday(&end_time);
- signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
- (end_time.tv_sec - start_time.tv_sec) * 1000000;
-
- /* delay with the closest number of ticks */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(signal_duration));
-
out:
kfree(cmdbuf);
return ret ? ret : count;
@@ -974,6 +956,7 @@ static void mceusb_handle_command(struct mceusb_dev *ir, int index)
static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
{
DEFINE_IR_RAW_EVENT(rawir);
+ bool event = false;
int i = 0;
/* skip meaningless 0xb1 0x60 header bytes on orig receiver */
@@ -1004,7 +987,8 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
rawir.pulse ? "pulse" : "space",
rawir.duration);
- ir_raw_event_store_with_filter(ir->rc, &rawir);
+ if (ir_raw_event_store_with_filter(ir->rc, &rawir))
+ event = true;
break;
case CMD_DATA:
ir->rem--;
@@ -1032,8 +1016,10 @@ static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
if (ir->parser_state != CMD_HEADER && !ir->rem)
ir->parser_state = CMD_HEADER;
}
- mce_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n");
- ir_raw_event_handle(ir->rc);
+ if (event) {
+ mce_dbg(ir->dev, "processed IR data, calling ir_raw_event_handle\n");
+ ir_raw_event_handle(ir->rc);
+ }
}
static void mceusb_dev_recv(struct urb *urb)
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index fae1615e0ff2..f9be68132c67 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -105,18 +105,9 @@ static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
{
struct loopback_dev *lodev = dev->priv;
u32 rxmask;
- unsigned total_duration = 0;
unsigned i;
DEFINE_IR_RAW_EVENT(rawir);
- for (i = 0; i < count; i++)
- total_duration += abs(txbuf[i]);
-
- if (total_duration == 0) {
- dprintk("invalid tx data, total duration zero\n");
- return -EINVAL;
- }
-
if (lodev->txcarrier < lodev->rxcarriermin ||
lodev->txcarrier > lodev->rxcarriermax) {
dprintk("ignoring tx, carrier out of range\n");
@@ -148,9 +139,6 @@ static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
ir_raw_event_handle(dev);
out:
- /* Lirc expects this function to take as long as the total duration */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(total_duration));
return count;
}
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 2878b0ed9741..49731b1a9c57 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -1217,9 +1217,10 @@ static int __devinit redrat3_dev_probe(struct usb_interface *intf,
rr3->carrier = 38000;
rr3->rc = redrat3_init_rc_dev(rr3);
- if (!rr3->rc)
+ if (!rr3->rc) {
+ retval = -ENOMEM;
goto error;
-
+ }
setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3);
/* we can register the device now, as it is ready */
diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c
new file mode 100644
index 000000000000..fef05235234a
--- /dev/null
+++ b/drivers/media/rc/ttusbir.c
@@ -0,0 +1,447 @@
+/*
+ * TechnoTrend USB IR Receiver
+ *
+ * Copyright (C) 2012 Sean Young <sean@mess.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME "ttusbir"
+#define DRIVER_DESC "TechnoTrend USB IR Receiver"
+/*
+ * The Windows driver uses 8 URBS, the original lirc drivers has a
+ * configurable amount (2 default, 4 max). This device generates about 125
+ * messages per second (!), whether IR is idle or not.
+ */
+#define NUM_URBS 4
+#define NS_PER_BYTE 62500
+#define NS_PER_BIT (NS_PER_BYTE/8)
+
+struct ttusbir {
+ struct rc_dev *rc;
+ struct device *dev;
+ struct usb_device *udev;
+
+ struct urb *urb[NUM_URBS];
+
+ struct led_classdev led;
+ struct urb *bulk_urb;
+ uint8_t bulk_buffer[5];
+ int bulk_out_endp, iso_in_endp;
+ bool led_on, is_led_on;
+ atomic_t led_complete;
+
+ char phys[64];
+};
+
+static enum led_brightness ttusbir_brightness_get(struct led_classdev *led_dev)
+{
+ struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+ return tt->led_on ? LED_FULL : LED_OFF;
+}
+
+static void ttusbir_set_led(struct ttusbir *tt)
+{
+ int ret;
+
+ smp_mb();
+
+ if (tt->led_on != tt->is_led_on && tt->udev &&
+ atomic_add_unless(&tt->led_complete, 1, 1)) {
+ tt->bulk_buffer[4] = tt->is_led_on = tt->led_on;
+ ret = usb_submit_urb(tt->bulk_urb, GFP_ATOMIC);
+ if (ret) {
+ dev_warn(tt->dev, "failed to submit bulk urb: %d\n",
+ ret);
+ atomic_dec(&tt->led_complete);
+ }
+ }
+}
+
+static void ttusbir_brightness_set(struct led_classdev *led_dev, enum
+ led_brightness brightness)
+{
+ struct ttusbir *tt = container_of(led_dev, struct ttusbir, led);
+
+ tt->led_on = brightness != LED_OFF;
+
+ ttusbir_set_led(tt);
+}
+
+/*
+ * The urb cannot be reused until the urb completes
+ */
+static void ttusbir_bulk_complete(struct urb *urb)
+{
+ struct ttusbir *tt = urb->context;
+
+ atomic_dec(&tt->led_complete);
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ ttusbir_set_led(tt);
+}
+
+/*
+ * The data is one bit per sample, a set bit signifying silence and samples
+ * being MSB first. Bit 0 can contain garbage so take it to be whatever
+ * bit 1 is, so we don't have unexpected edges.
+ */
+static void ttusbir_process_ir_data(struct ttusbir *tt, uint8_t *buf)
+{
+ struct ir_raw_event rawir;
+ unsigned i, v, b;
+ bool event = false;
+
+ init_ir_raw_event(&rawir);
+
+ for (i = 0; i < 128; i++) {
+ v = buf[i] & 0xfe;
+ switch (v) {
+ case 0xfe:
+ rawir.pulse = false;
+ rawir.duration = NS_PER_BYTE;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ case 0:
+ rawir.pulse = true;
+ rawir.duration = NS_PER_BYTE;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ default:
+ /* one edge per byte */
+ if (v & 2) {
+ b = ffz(v | 1);
+ rawir.pulse = true;
+ } else {
+ b = ffs(v) - 1;
+ rawir.pulse = false;
+ }
+
+ rawir.duration = NS_PER_BIT * (8 - b);
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+
+ rawir.pulse = !rawir.pulse;
+ rawir.duration = NS_PER_BIT * b;
+ if (ir_raw_event_store_with_filter(tt->rc, &rawir))
+ event = true;
+ break;
+ }
+ }
+
+ /* don't wakeup when there's nothing to do */
+ if (event)
+ ir_raw_event_handle(tt->rc);
+}
+
+static void ttusbir_urb_complete(struct urb *urb)
+{
+ struct ttusbir *tt = urb->context;
+ int rc;
+
+ switch (urb->status) {
+ case 0:
+ ttusbir_process_ir_data(tt, urb->transfer_buffer);
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+ case -EPIPE:
+ default:
+ dev_dbg(tt->dev, "Error: urb status = %d\n", urb->status);
+ break;
+ }
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc && rc != -ENODEV)
+ dev_warn(tt->dev, "failed to resubmit urb: %d\n", rc);
+}
+
+static int __devinit ttusbir_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct ttusbir *tt;
+ struct usb_interface_descriptor *idesc;
+ struct usb_endpoint_descriptor *desc;
+ struct rc_dev *rc;
+ int i, j, ret;
+ int altsetting = -1;
+
+ tt = kzalloc(sizeof(*tt), GFP_KERNEL);
+ rc = rc_allocate_device();
+ if (!tt || !rc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* find the correct alt setting */
+ for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) {
+ int bulk_out_endp = -1, iso_in_endp = -1;
+
+ idesc = &intf->altsetting[i].desc;
+
+ for (j = 0; j < idesc->bNumEndpoints; j++) {
+ desc = &intf->altsetting[i].endpoint[j].desc;
+ if (usb_endpoint_dir_in(desc) &&
+ usb_endpoint_xfer_isoc(desc) &&
+ desc->wMaxPacketSize == 0x10)
+ iso_in_endp = j;
+ else if (usb_endpoint_dir_out(desc) &&
+ usb_endpoint_xfer_bulk(desc) &&
+ desc->wMaxPacketSize == 0x20)
+ bulk_out_endp = j;
+
+ if (bulk_out_endp != -1 && iso_in_endp != -1) {
+ tt->bulk_out_endp = bulk_out_endp;
+ tt->iso_in_endp = iso_in_endp;
+ altsetting = i;
+ break;
+ }
+ }
+ }
+
+ if (altsetting == -1) {
+ dev_err(&intf->dev, "cannot find expected altsetting\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ tt->dev = &intf->dev;
+ tt->udev = interface_to_usbdev(intf);
+ tt->rc = rc;
+
+ ret = usb_set_interface(tt->udev, 0, altsetting);
+ if (ret)
+ goto out;
+
+ for (i = 0; i < NUM_URBS; i++) {
+ struct urb *urb = usb_alloc_urb(8, GFP_KERNEL);
+ void *buffer;
+
+ if (!urb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ urb->dev = tt->udev;
+ urb->context = tt;
+ urb->pipe = usb_rcvisocpipe(tt->udev, tt->iso_in_endp);
+ urb->interval = 1;
+ buffer = usb_alloc_coherent(tt->udev, 128, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buffer) {
+ usb_free_urb(urb);
+ ret = -ENOMEM;
+ goto out;
+ }
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP;
+ urb->transfer_buffer = buffer;
+ urb->complete = ttusbir_urb_complete;
+ urb->number_of_packets = 8;
+ urb->transfer_buffer_length = 128;
+
+ for (j = 0; j < 8; j++) {
+ urb->iso_frame_desc[j].offset = j * 16;
+ urb->iso_frame_desc[j].length = 16;
+ }
+
+ tt->urb[i] = urb;
+ }
+
+ tt->bulk_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!tt->bulk_urb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tt->bulk_buffer[0] = 0xaa;
+ tt->bulk_buffer[1] = 0x01;
+ tt->bulk_buffer[2] = 0x05;
+ tt->bulk_buffer[3] = 0x01;
+
+ usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev,
+ tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer),
+ ttusbir_bulk_complete, tt);
+
+ tt->led.name = "ttusbir:green:power";
+ tt->led.brightness_set = ttusbir_brightness_set;
+ tt->led.brightness_get = ttusbir_brightness_get;
+ tt->is_led_on = tt->led_on = true;
+ atomic_set(&tt->led_complete, 0);
+ ret = led_classdev_register(&intf->dev, &tt->led);
+ if (ret)
+ goto out;
+
+ usb_make_path(tt->udev, tt->phys, sizeof(tt->phys));
+
+ rc->input_name = DRIVER_DESC;
+ rc->input_phys = tt->phys;
+ usb_to_input_id(tt->udev, &rc->input_id);
+ rc->dev.parent = &intf->dev;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc->allowed_protos = RC_TYPE_ALL;
+ rc->priv = tt;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_TT_1500;
+ rc->timeout = MS_TO_NS(100);
+ /*
+ * The precision is NS_PER_BIT, but since every 8th bit can be
+ * overwritten with garbage the accuracy is at best 2 * NS_PER_BIT.
+ */
+ rc->rx_resolution = NS_PER_BIT;
+
+ ret = rc_register_device(rc);
+ if (ret) {
+ dev_err(&intf->dev, "failed to register rc device %d\n", ret);
+ goto out2;
+ }
+
+ usb_set_intfdata(intf, tt);
+
+ for (i = 0; i < NUM_URBS; i++) {
+ ret = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+ if (ret) {
+ dev_err(tt->dev, "failed to submit urb %d\n", ret);
+ goto out3;
+ }
+ }
+
+ return 0;
+out3:
+ rc_unregister_device(rc);
+out2:
+ led_classdev_unregister(&tt->led);
+out:
+ if (tt) {
+ for (i = 0; i < NUM_URBS && tt->urb[i]; i++) {
+ struct urb *urb = tt->urb[i];
+
+ usb_kill_urb(urb);
+ usb_free_coherent(tt->udev, 128, urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+ usb_kill_urb(tt->bulk_urb);
+ usb_free_urb(tt->bulk_urb);
+ kfree(tt);
+ }
+ rc_free_device(rc);
+
+ return ret;
+}
+
+static void __devexit ttusbir_disconnect(struct usb_interface *intf)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ struct usb_device *udev = tt->udev;
+ int i;
+
+ tt->udev = NULL;
+
+ rc_unregister_device(tt->rc);
+ led_classdev_unregister(&tt->led);
+ for (i = 0; i < NUM_URBS; i++) {
+ usb_kill_urb(tt->urb[i]);
+ usb_free_coherent(udev, 128, tt->urb[i]->transfer_buffer,
+ tt->urb[i]->transfer_dma);
+ usb_free_urb(tt->urb[i]);
+ }
+ usb_kill_urb(tt->bulk_urb);
+ usb_free_urb(tt->bulk_urb);
+ usb_set_intfdata(intf, NULL);
+ kfree(tt);
+}
+
+static int ttusbir_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ int i;
+
+ for (i = 0; i < NUM_URBS; i++)
+ usb_kill_urb(tt->urb[i]);
+
+ led_classdev_suspend(&tt->led);
+ usb_kill_urb(tt->bulk_urb);
+
+ return 0;
+}
+
+static int ttusbir_resume(struct usb_interface *intf)
+{
+ struct ttusbir *tt = usb_get_intfdata(intf);
+ int i, rc;
+
+ led_classdev_resume(&tt->led);
+ tt->is_led_on = true;
+ ttusbir_set_led(tt);
+
+ for (i = 0; i < NUM_URBS; i++) {
+ rc = usb_submit_urb(tt->urb[i], GFP_KERNEL);
+ if (rc) {
+ dev_warn(tt->dev, "failed to submit urb: %d\n", rc);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static const struct usb_device_id ttusbir_table[] = {
+ { USB_DEVICE(0x0b48, 0x2003) },
+ { }
+};
+
+static struct usb_driver ttusbir_driver = {
+ .name = DRIVER_NAME,
+ .id_table = ttusbir_table,
+ .probe = ttusbir_probe,
+ .suspend = ttusbir_suspend,
+ .resume = ttusbir_resume,
+ .reset_resume = ttusbir_resume,
+ .disconnect = __devexit_p(ttusbir_disconnect)
+};
+
+module_usb_driver(ttusbir_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, ttusbir_table);
+
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 54ee34872d14..30ae1f24abc3 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -180,7 +180,6 @@ enum wbcir_rxstate {
enum wbcir_txstate {
WBCIR_TXSTATE_INACTIVE = 0,
WBCIR_TXSTATE_ACTIVE,
- WBCIR_TXSTATE_DONE,
WBCIR_TXSTATE_ERROR
};
@@ -216,7 +215,6 @@ struct wbcir_data {
u32 txlen;
u32 txoff;
u32 *txbuf;
- wait_queue_head_t txwaitq;
u8 txmask;
u32 txcarrier;
};
@@ -358,7 +356,7 @@ wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
if (data->rxstate == WBCIR_RXSTATE_ERROR)
continue;
rawir.pulse = irdata & 0x80 ? false : true;
- rawir.duration = US_TO_NS((irdata & 0x7F) * 10);
+ rawir.duration = US_TO_NS(((irdata & 0x7F) + 1) * 10);
ir_raw_event_store_with_filter(data->dev, &rawir);
}
@@ -424,11 +422,11 @@ wbcir_irq_tx(struct wbcir_data *data)
if (data->txstate == WBCIR_TXSTATE_ERROR)
/* Clear TX underrun bit */
outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR);
- else
- data->txstate = WBCIR_TXSTATE_DONE;
wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
led_trigger_event(data->txtrigger, LED_OFF);
- wake_up(&data->txwaitq);
+ kfree(data->txbuf);
+ data->txbuf = NULL;
+ data->txstate = WBCIR_TXSTATE_INACTIVE;
} else if (data->txoff == data->txlen) {
/* At the end of transmission, tell the hw before last byte */
outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1);
@@ -579,43 +577,37 @@ wbcir_txmask(struct rc_dev *dev, u32 mask)
}
static int
-wbcir_tx(struct rc_dev *dev, unsigned *buf, unsigned count)
+wbcir_tx(struct rc_dev *dev, unsigned *b, unsigned count)
{
struct wbcir_data *data = dev->priv;
+ unsigned *buf;
unsigned i;
unsigned long flags;
+ buf = kmalloc(count * sizeof(*b), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Convert values to multiples of 10us */
+ for (i = 0; i < count; i++)
+ buf[i] = DIV_ROUND_CLOSEST(b[i], 10);
+
/* Not sure if this is possible, but better safe than sorry */
spin_lock_irqsave(&data->spinlock, flags);
if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
spin_unlock_irqrestore(&data->spinlock, flags);
+ kfree(buf);
return -EBUSY;
}
- /* Convert values to multiples of 10us */
- for (i = 0; i < count; i++)
- buf[i] = DIV_ROUND_CLOSEST(buf[i], 10);
-
/* Fill the TX fifo once, the irq handler will do the rest */
data->txbuf = buf;
data->txlen = count;
data->txoff = 0;
wbcir_irq_tx(data);
- /* Wait for the TX to complete */
- while (data->txstate == WBCIR_TXSTATE_ACTIVE) {
- spin_unlock_irqrestore(&data->spinlock, flags);
- wait_event(data->txwaitq, data->txstate != WBCIR_TXSTATE_ACTIVE);
- spin_lock_irqsave(&data->spinlock, flags);
- }
-
/* We're done */
- if (data->txstate == WBCIR_TXSTATE_ERROR)
- count = -EAGAIN;
- data->txstate = WBCIR_TXSTATE_INACTIVE;
- data->txbuf = NULL;
spin_unlock_irqrestore(&data->spinlock, flags);
-
return count;
}
@@ -927,13 +919,11 @@ wbcir_init_hw(struct wbcir_data *data)
ir_raw_event_reset(data->dev);
ir_raw_event_handle(data->dev);
- /*
- * Check TX state, if we did a suspend/resume cycle while TX was
- * active, we will have a process waiting in txwaitq.
- */
+ /* Clear TX state */
if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
- data->txstate = WBCIR_TXSTATE_ERROR;
- wake_up(&data->txwaitq);
+ kfree(data->txbuf);
+ data->txbuf = NULL;
+ data->txstate = WBCIR_TXSTATE_INACTIVE;
}
/* Enable interrupts */
@@ -974,7 +964,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
pnp_set_drvdata(device, data);
spin_lock_init(&data->spinlock);
- init_waitqueue_head(&data->txwaitq);
data->ebase = pnp_port_start(device, 0);
data->wbase = pnp_port_start(device, 1);
data->sbase = pnp_port_start(device, 2);
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
new file mode 100644
index 000000000000..e8fdf713fce8
--- /dev/null
+++ b/drivers/media/tuners/Kconfig
@@ -0,0 +1,244 @@
+config MEDIA_ATTACH
+ bool "Load and attach frontend and tuner driver modules as needed"
+ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
+ depends on MODULES
+ default y if !EXPERT
+ help
+ Remove the static dependency of DVB card drivers on all
+ frontend modules for all possible card variants. Instead,
+ allow the card drivers to only load the frontend modules
+ they require.
+
+ Also, tuner module will automatically load a tuner driver
+ when needed, for analog mode.
+
+ This saves several KBytes of memory.
+
+ Note: You will need module-init-tools v3.2 or later for this feature.
+
+ If unsure say Y.
+
+# Analog TV tuners, auto-loaded via tuner.ko
+config MEDIA_TUNER
+ tristate
+ depends on (MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT) && I2C
+ default y
+ select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT20XX if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TEA5761 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT
+ select MEDIA_TUNER_TEA5767 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_RADIO_SUPPORT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA9887 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT
+
+menu "Customize TV tuners"
+ visible if !MEDIA_SUBDRV_AUTOSELECT
+ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_RADIO_SUPPORT
+
+config MEDIA_TUNER_SIMPLE
+ tristate "Simple tuner support"
+ depends on MEDIA_SUPPORT && I2C
+ select MEDIA_TUNER_TDA9887
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for various simple tuners.
+
+config MEDIA_TUNER_TDA8290
+ tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
+ depends on MEDIA_SUPPORT && I2C
+ select MEDIA_TUNER_TDA827X
+ select MEDIA_TUNER_TDA18271
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for Philips TDA8290+8275(a) tuner.
+
+config MEDIA_TUNER_TDA827X
+ tristate "Philips TDA827X silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+
+config MEDIA_TUNER_TDA18271
+ tristate "NXP TDA18271 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A silicon tuner module. Say Y when you want to support this tuner.
+
+config MEDIA_TUNER_TDA9887
+ tristate "TDA 9885/6/7 analog IF demodulator"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for Philips TDA9885/6/7
+ analog IF demodulator.
+
+config MEDIA_TUNER_TEA5761
+ tristate "TEA 5761 radio tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for the Philips TEA5761 radio tuner.
+
+config MEDIA_TUNER_TEA5767
+ tristate "TEA 5767 radio tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for the Philips TEA5767 radio tuner.
+
+config MEDIA_TUNER_MT20XX
+ tristate "Microtune 2032 / 2050 tuners"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for the MT2032 / MT2050 tuner.
+
+config MEDIA_TUNER_MT2060
+ tristate "Microtune MT2060 silicon IF tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon IF tuner MT2060 from Microtune.
+
+config MEDIA_TUNER_MT2063
+ tristate "Microtune MT2063 silicon IF tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon IF tuner MT2063 from Microtune.
+
+config MEDIA_TUNER_MT2266
+ tristate "Microtune MT2266 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner MT2266 from Microtune.
+
+config MEDIA_TUNER_MT2131
+ tristate "Microtune MT2131 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon baseband tuner MT2131 from Microtune.
+
+config MEDIA_TUNER_QT1010
+ tristate "Quantek QT1010 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner QT1010 from Quantek.
+
+config MEDIA_TUNER_XC2028
+ tristate "XCeive xc2028/xc3028 tuners"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to include support for the xc2028/xc3028 tuners.
+
+config MEDIA_TUNER_XC5000
+ tristate "Xceive XC5000 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner XC5000 from Xceive.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+config MEDIA_TUNER_XC4000
+ tristate "Xceive XC4000 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner XC4000 from Xceive.
+ This device is only used inside a SiP called together with a
+ demodulator for now.
+
+config MEDIA_TUNER_MXL5005S
+ tristate "MaxLinear MSL5005S silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner MXL5005S from MaxLinear.
+
+config MEDIA_TUNER_MXL5007T
+ tristate "MaxLinear MxL5007T silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner MxL5007T from MaxLinear.
+
+config MEDIA_TUNER_MC44S803
+ tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Freescale MC44S803 based tuners
+
+config MEDIA_TUNER_MAX2165
+ tristate "Maxim MAX2165 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ A driver for the silicon tuner MAX2165 from Maxim.
+
+config MEDIA_TUNER_TDA18218
+ tristate "NXP TDA18218 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ NXP TDA18218 silicon tuner driver.
+
+config MEDIA_TUNER_FC0011
+ tristate "Fitipower FC0011 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Fitipower FC0011 silicon tuner driver.
+
+config MEDIA_TUNER_FC0012
+ tristate "Fitipower FC0012 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Fitipower FC0012 silicon tuner driver.
+
+config MEDIA_TUNER_FC0013
+ tristate "Fitipower FC0013 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Fitipower FC0013 silicon tuner driver.
+
+config MEDIA_TUNER_TDA18212
+ tristate "NXP TDA18212 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ NXP TDA18212 silicon tuner driver.
+
+config MEDIA_TUNER_E4000
+ tristate "Elonics E4000 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Elonics E4000 silicon tuner driver.
+
+config MEDIA_TUNER_FC2580
+ tristate "FCI FC2580 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ FCI FC2580 silicon tuner driver.
+
+config MEDIA_TUNER_TUA9001
+ tristate "Infineon TUA 9001 silicon tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Infineon TUA 9001 silicon tuner driver.
+endmenu
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
new file mode 100644
index 000000000000..5e569b1083cf
--- /dev/null
+++ b/drivers/media/tuners/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for common V4L/DVB tuners
+#
+
+tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
+
+obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o
+obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o
+# tuner-types will be merged into tuner-simple, in the future
+obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o
+obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o
+obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o
+obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o
+obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o
+obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
+obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
+obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
+obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o
+obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o
+obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
+obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
+obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
+obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
+obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
+obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
+obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
+obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
+obj-$(CONFIG_MEDIA_TUNER_E4000) += e4000.o
+obj-$(CONFIG_MEDIA_TUNER_FC2580) += fc2580.o
+obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
+obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
+obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
+obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
new file mode 100644
index 000000000000..1b33ed368abe
--- /dev/null
+++ b/drivers/media/tuners/e4000.c
@@ -0,0 +1,409 @@
+/*
+ * Elonics E4000 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "e4000_priv.h"
+
+/* write multiple registers */
+static int e4000_wr_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int e4000_rd_regs(struct e4000_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg->i2c_addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int e4000_wr_reg(struct e4000_priv *priv, u8 reg, u8 val)
+{
+ return e4000_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int e4000_rd_reg(struct e4000_priv *priv, u8 reg, u8 *val)
+{
+ return e4000_rd_regs(priv, reg, val, 1);
+}
+
+static int e4000_init(struct dvb_frontend *fe)
+{
+ struct e4000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* dummy I2C to ensure I2C wakes up */
+ ret = e4000_wr_reg(priv, 0x02, 0x40);
+
+ /* reset */
+ ret = e4000_wr_reg(priv, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /* disable output clock */
+ ret = e4000_wr_reg(priv, 0x06, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x7a, 0x96);
+ if (ret < 0)
+ goto err;
+
+ /* configure gains */
+ ret = e4000_wr_regs(priv, 0x7e, "\x01\xfe", 2);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x82, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x24, 0x05);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_regs(priv, 0x87, "\x20\x01", 2);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_regs(priv, 0x9f, "\x7f\x07", 2);
+ if (ret < 0)
+ goto err;
+
+ /*
+ * TODO: Implement DC offset control correctly.
+ * DC offsets has quite much effect for received signal quality in case
+ * of direct conversion tuners (Zero-IF). Surely we will now lose few
+ * decimals or even decibels from SNR...
+ */
+ /* DC offset control */
+ ret = e4000_wr_reg(priv, 0x2d, 0x0c);
+ if (ret < 0)
+ goto err;
+
+ /* gain control */
+ ret = e4000_wr_reg(priv, 0x1a, 0x17);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x1f, 0x1a);
+ if (ret < 0)
+ goto err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int e4000_sleep(struct dvb_frontend *fe)
+{
+ struct e4000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = e4000_wr_reg(priv, 0x00, 0x00);
+ if (ret < 0)
+ goto err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int e4000_set_params(struct dvb_frontend *fe)
+{
+ struct e4000_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, sigma_delta;
+ unsigned int f_VCO;
+ u8 buf[5];
+
+ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
+ "bandwidth_hz=%d\n", __func__,
+ c->delivery_system, c->frequency, c->bandwidth_hz);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* gain control manual */
+ ret = e4000_wr_reg(priv, 0x1a, 0x00);
+ if (ret < 0)
+ goto err;
+
+ /* PLL */
+ for (i = 0; i < ARRAY_SIZE(e4000_pll_lut); i++) {
+ if (c->frequency <= e4000_pll_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(e4000_pll_lut))
+ goto err;
+
+ /*
+ * Note: Currently f_VCO overflows when c->frequency is 1 073 741 824 Hz
+ * or more.
+ */
+ f_VCO = c->frequency * e4000_pll_lut[i].mul;
+ sigma_delta = 0x10000UL * (f_VCO % priv->cfg->clock) / priv->cfg->clock;
+ buf[0] = f_VCO / priv->cfg->clock;
+ buf[1] = (sigma_delta >> 0) & 0xff;
+ buf[2] = (sigma_delta >> 8) & 0xff;
+ buf[3] = 0x00;
+ buf[4] = e4000_pll_lut[i].div;
+
+ dev_dbg(&priv->i2c->dev, "%s: f_VCO=%u pll div=%d sigma_delta=%04x\n",
+ __func__, f_VCO, buf[0], sigma_delta);
+
+ ret = e4000_wr_regs(priv, 0x09, buf, 5);
+ if (ret < 0)
+ goto err;
+
+ /* LNA filter (RF filter) */
+ for (i = 0; i < ARRAY_SIZE(e400_lna_filter_lut); i++) {
+ if (c->frequency <= e400_lna_filter_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(e400_lna_filter_lut))
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x10, e400_lna_filter_lut[i].val);
+ if (ret < 0)
+ goto err;
+
+ /* IF filters */
+ for (i = 0; i < ARRAY_SIZE(e4000_if_filter_lut); i++) {
+ if (c->bandwidth_hz <= e4000_if_filter_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(e4000_if_filter_lut))
+ goto err;
+
+ buf[0] = e4000_if_filter_lut[i].reg11_val;
+ buf[1] = e4000_if_filter_lut[i].reg12_val;
+
+ ret = e4000_wr_regs(priv, 0x11, buf, 2);
+ if (ret < 0)
+ goto err;
+
+ /* frequency band */
+ for (i = 0; i < ARRAY_SIZE(e4000_band_lut); i++) {
+ if (c->frequency <= e4000_band_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(e4000_band_lut))
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x07, e4000_band_lut[i].reg07_val);
+ if (ret < 0)
+ goto err;
+
+ ret = e4000_wr_reg(priv, 0x78, e4000_band_lut[i].reg78_val);
+ if (ret < 0)
+ goto err;
+
+ /* gain control auto */
+ ret = e4000_wr_reg(priv, 0x1a, 0x17);
+ if (ret < 0)
+ goto err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int e4000_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct e4000_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ *frequency = 0; /* Zero-IF */
+
+ return 0;
+}
+
+static int e4000_release(struct dvb_frontend *fe)
+{
+ struct e4000_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ kfree(fe->tuner_priv);
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops e4000_tuner_ops = {
+ .info = {
+ .name = "Elonics E4000",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ },
+
+ .release = e4000_release,
+
+ .init = e4000_init,
+ .sleep = e4000_sleep,
+ .set_params = e4000_set_params,
+
+ .get_if_frequency = e4000_get_if_frequency,
+};
+
+struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct e4000_config *cfg)
+{
+ struct e4000_priv *priv;
+ int ret;
+ u8 chip_id;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ priv = kzalloc(sizeof(struct e4000_priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ goto err;
+ }
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ /* check if the tuner is there */
+ ret = e4000_rd_reg(priv, 0x02, &chip_id);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+
+ if (chip_id != 0x40)
+ goto err;
+
+ /* put sleep as chip seems to be in normal mode by default */
+ ret = e4000_wr_reg(priv, 0x00, 0x00);
+ if (ret < 0)
+ goto err;
+
+ dev_info(&priv->i2c->dev,
+ "%s: Elonics E4000 successfully identified\n",
+ KBUILD_MODNAME);
+
+ fe->tuner_priv = priv;
+ memcpy(&fe->ops.tuner_ops, &e4000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return fe;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(e4000_attach);
+
+MODULE_DESCRIPTION("Elonics E4000 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/e4000.h b/drivers/media/tuners/e4000.h
new file mode 100644
index 000000000000..71b1935eb3d2
--- /dev/null
+++ b/drivers/media/tuners/e4000.h
@@ -0,0 +1,52 @@
+/*
+ * Elonics E4000 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef E4000_H
+#define E4000_H
+
+#include "dvb_frontend.h"
+
+struct e4000_config {
+ /*
+ * I2C address
+ * 0x64, 0x65, 0x66, 0x67
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock
+ */
+ u32 clock;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_E4000) || \
+ (defined(CONFIG_MEDIA_TUNER_E4000_MODULE) && defined(MODULE))
+extern struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct e4000_config *cfg);
+#else
+static inline struct dvb_frontend *e4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct e4000_config *cfg)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/tuners/e4000_priv.h b/drivers/media/tuners/e4000_priv.h
new file mode 100644
index 000000000000..a3855053e78f
--- /dev/null
+++ b/drivers/media/tuners/e4000_priv.h
@@ -0,0 +1,147 @@
+/*
+ * Elonics E4000 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef E4000_PRIV_H
+#define E4000_PRIV_H
+
+#include "e4000.h"
+
+struct e4000_priv {
+ const struct e4000_config *cfg;
+ struct i2c_adapter *i2c;
+};
+
+struct e4000_pll {
+ u32 freq;
+ u8 div;
+ u8 mul;
+};
+
+static const struct e4000_pll e4000_pll_lut[] = {
+/* VCO min VCO max */
+ { 72400000, 0x0f, 48 }, /* .......... 3475200000 */
+ { 81200000, 0x0e, 40 }, /* 2896000000 3248000000 */
+ { 108300000, 0x0d, 32 }, /* 2598400000 3465600000 */
+ { 162500000, 0x0c, 24 }, /* 2599200000 3900000000 */
+ { 216600000, 0x0b, 16 }, /* 2600000000 3465600000 */
+ { 325000000, 0x0a, 12 }, /* 2599200000 3900000000 */
+ { 350000000, 0x09, 8 }, /* 2600000000 2800000000 */
+ { 432000000, 0x03, 8 }, /* 2800000000 3456000000 */
+ { 667000000, 0x02, 6 }, /* 2592000000 4002000000 */
+ { 1200000000, 0x01, 4 }, /* 2668000000 4800000000 */
+ { 0xffffffff, 0x00, 2 }, /* 2400000000 .......... */
+};
+
+struct e4000_lna_filter {
+ u32 freq;
+ u8 val;
+};
+
+static const struct e4000_lna_filter e400_lna_filter_lut[] = {
+ { 370000000, 0 },
+ { 392500000, 1 },
+ { 415000000, 2 },
+ { 437500000, 3 },
+ { 462500000, 4 },
+ { 490000000, 5 },
+ { 522500000, 6 },
+ { 557500000, 7 },
+ { 595000000, 8 },
+ { 642500000, 9 },
+ { 695000000, 10 },
+ { 740000000, 11 },
+ { 800000000, 12 },
+ { 865000000, 13 },
+ { 930000000, 14 },
+ { 1000000000, 15 },
+ { 1310000000, 0 },
+ { 1340000000, 1 },
+ { 1385000000, 2 },
+ { 1427500000, 3 },
+ { 1452500000, 4 },
+ { 1475000000, 5 },
+ { 1510000000, 6 },
+ { 1545000000, 7 },
+ { 1575000000, 8 },
+ { 1615000000, 9 },
+ { 1650000000, 10 },
+ { 1670000000, 11 },
+ { 1690000000, 12 },
+ { 1710000000, 13 },
+ { 1735000000, 14 },
+ { 0xffffffff, 15 },
+};
+
+struct e4000_band {
+ u32 freq;
+ u8 reg07_val;
+ u8 reg78_val;
+};
+
+static const struct e4000_band e4000_band_lut[] = {
+ { 140000000, 0x01, 0x03 },
+ { 350000000, 0x03, 0x03 },
+ { 1000000000, 0x05, 0x03 },
+ { 0xffffffff, 0x07, 0x00 },
+};
+
+struct e4000_if_filter {
+ u32 freq;
+ u8 reg11_val;
+ u8 reg12_val;
+};
+
+static const struct e4000_if_filter e4000_if_filter_lut[] = {
+ { 4300000, 0xfd, 0x1f },
+ { 4400000, 0xfd, 0x1e },
+ { 4480000, 0xfc, 0x1d },
+ { 4560000, 0xfc, 0x1c },
+ { 4600000, 0xfc, 0x1b },
+ { 4800000, 0xfc, 0x1a },
+ { 4900000, 0xfc, 0x19 },
+ { 5000000, 0xfc, 0x18 },
+ { 5100000, 0xfc, 0x17 },
+ { 5200000, 0xfc, 0x16 },
+ { 5400000, 0xfc, 0x15 },
+ { 5500000, 0xfc, 0x14 },
+ { 5600000, 0xfc, 0x13 },
+ { 5800000, 0xfb, 0x12 },
+ { 5900000, 0xfb, 0x11 },
+ { 6000000, 0xfb, 0x10 },
+ { 6200000, 0xfb, 0x0f },
+ { 6400000, 0xfa, 0x0e },
+ { 6600000, 0xfa, 0x0d },
+ { 6800000, 0xf9, 0x0c },
+ { 7200000, 0xf9, 0x0b },
+ { 7400000, 0xf9, 0x0a },
+ { 7600000, 0xf8, 0x09 },
+ { 7800000, 0xf8, 0x08 },
+ { 8200000, 0xf8, 0x07 },
+ { 8600000, 0xf7, 0x06 },
+ { 8800000, 0xf7, 0x05 },
+ { 9200000, 0xf7, 0x04 },
+ { 9600000, 0xf6, 0x03 },
+ { 10000000, 0xf6, 0x02 },
+ { 10600000, 0xf5, 0x01 },
+ { 11000000, 0xf5, 0x00 },
+ { 0xffffffff, 0x00, 0x20 },
+};
+
+#endif
diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/tuners/fc0011.c
index e4882546c283..e4882546c283 100644
--- a/drivers/media/common/tuners/fc0011.c
+++ b/drivers/media/tuners/fc0011.c
diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/tuners/fc0011.h
index 0ee581f122d2..0ee581f122d2 100644
--- a/drivers/media/common/tuners/fc0011.h
+++ b/drivers/media/tuners/fc0011.h
diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/tuners/fc0012-priv.h
index 4577c917e616..4577c917e616 100644
--- a/drivers/media/common/tuners/fc0012-priv.h
+++ b/drivers/media/tuners/fc0012-priv.h
diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/tuners/fc0012.c
index 308135abd54c..308135abd54c 100644
--- a/drivers/media/common/tuners/fc0012.c
+++ b/drivers/media/tuners/fc0012.c
diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/tuners/fc0012.h
index 4dbd5efe8845..4dbd5efe8845 100644
--- a/drivers/media/common/tuners/fc0012.h
+++ b/drivers/media/tuners/fc0012.h
diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/tuners/fc0013-priv.h
index bfd49dedea22..bfd49dedea22 100644
--- a/drivers/media/common/tuners/fc0013-priv.h
+++ b/drivers/media/tuners/fc0013-priv.h
diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/tuners/fc0013.c
index bd8f0f1e8f3b..bd8f0f1e8f3b 100644
--- a/drivers/media/common/tuners/fc0013.c
+++ b/drivers/media/tuners/fc0013.c
diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/tuners/fc0013.h
index 594efd64aeec..594efd64aeec 100644
--- a/drivers/media/common/tuners/fc0013.h
+++ b/drivers/media/tuners/fc0013.h
diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/tuners/fc001x-common.h
index 718818156934..718818156934 100644
--- a/drivers/media/common/tuners/fc001x-common.h
+++ b/drivers/media/tuners/fc001x-common.h
diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
new file mode 100644
index 000000000000..aff39ae457a0
--- /dev/null
+++ b/drivers/media/tuners/fc2580.c
@@ -0,0 +1,529 @@
+/*
+ * FCI FC2580 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "fc2580_priv.h"
+
+/*
+ * TODO:
+ * I2C write and read works only for one single register. Multiple registers
+ * could not be accessed using normal register address auto-increment.
+ * There could be (very likely) register to change that behavior....
+ *
+ * Due to that limitation functions:
+ * fc2580_wr_regs()
+ * fc2580_rd_regs()
+ * could not be used for accessing more than one register at once.
+ *
+ * TODO:
+ * Currently it blind writes bunch of static registers from the
+ * fc2580_freq_regs_lut[] when fc2580_set_params() is called. Add some
+ * logic to reduce unneeded register writes.
+ * There is also don't-care registers, initialized with value 0xff, and those
+ * are also written to the chip currently (yes, not wise).
+ */
+
+/* write multiple registers */
+static int fc2580_wr_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int fc2580_rd_regs(struct fc2580_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg->i2c_addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int fc2580_wr_reg(struct fc2580_priv *priv, u8 reg, u8 val)
+{
+ return fc2580_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int fc2580_rd_reg(struct fc2580_priv *priv, u8 reg, u8 *val)
+{
+ return fc2580_rd_regs(priv, reg, val, 1);
+}
+
+static int fc2580_set_params(struct dvb_frontend *fe)
+{
+ struct fc2580_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret = 0, i;
+ unsigned int r_val, n_val, k_val, k_val_reg, f_ref;
+ u8 tmp_val, r18_val;
+ u64 f_vco;
+
+ /*
+ * Fractional-N synthesizer/PLL.
+ * Most likely all those PLL calculations are not correct. I am not
+ * sure, but it looks like it is divider based Fractional-N synthesizer.
+ * There is divider for reference clock too?
+ * Anyhow, synthesizer calculation results seems to be quite correct.
+ */
+
+ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
+ "bandwidth_hz=%d\n", __func__,
+ c->delivery_system, c->frequency, c->bandwidth_hz);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ /* PLL */
+ for (i = 0; i < ARRAY_SIZE(fc2580_pll_lut); i++) {
+ if (c->frequency <= fc2580_pll_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fc2580_pll_lut))
+ goto err;
+
+ f_vco = c->frequency;
+ f_vco *= fc2580_pll_lut[i].div;
+
+ if (f_vco >= 2600000000UL)
+ tmp_val = 0x0e | fc2580_pll_lut[i].band;
+ else
+ tmp_val = 0x06 | fc2580_pll_lut[i].band;
+
+ ret = fc2580_wr_reg(priv, 0x02, tmp_val);
+ if (ret < 0)
+ goto err;
+
+ if (f_vco >= 2UL * 76 * priv->cfg->clock) {
+ r_val = 1;
+ r18_val = 0x00;
+ } else if (f_vco >= 1UL * 76 * priv->cfg->clock) {
+ r_val = 2;
+ r18_val = 0x10;
+ } else {
+ r_val = 4;
+ r18_val = 0x20;
+ }
+
+ f_ref = 2UL * priv->cfg->clock / r_val;
+ n_val = div_u64_rem(f_vco, f_ref, &k_val);
+ k_val_reg = 1UL * k_val * (1 << 20) / f_ref;
+
+ ret = fc2580_wr_reg(priv, 0x18, r18_val | ((k_val_reg >> 16) & 0xff));
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x1a, (k_val_reg >> 8) & 0xff);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x1b, (k_val_reg >> 0) & 0xff);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x1c, n_val);
+ if (ret < 0)
+ goto err;
+
+ if (priv->cfg->clock >= 28000000) {
+ ret = fc2580_wr_reg(priv, 0x4b, 0x22);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (fc2580_pll_lut[i].band == 0x00) {
+ if (c->frequency <= 794000000)
+ tmp_val = 0x9f;
+ else
+ tmp_val = 0x8f;
+
+ ret = fc2580_wr_reg(priv, 0x2d, tmp_val);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* registers */
+ for (i = 0; i < ARRAY_SIZE(fc2580_freq_regs_lut); i++) {
+ if (c->frequency <= fc2580_freq_regs_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fc2580_freq_regs_lut))
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x25, fc2580_freq_regs_lut[i].r25_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x27, fc2580_freq_regs_lut[i].r27_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x28, fc2580_freq_regs_lut[i].r28_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x29, fc2580_freq_regs_lut[i].r29_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x2b, fc2580_freq_regs_lut[i].r2b_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x2c, fc2580_freq_regs_lut[i].r2c_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x2d, fc2580_freq_regs_lut[i].r2d_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x30, fc2580_freq_regs_lut[i].r30_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x44, fc2580_freq_regs_lut[i].r44_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x50, fc2580_freq_regs_lut[i].r50_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x53, fc2580_freq_regs_lut[i].r53_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x5f, fc2580_freq_regs_lut[i].r5f_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x61, fc2580_freq_regs_lut[i].r61_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x62, fc2580_freq_regs_lut[i].r62_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x63, fc2580_freq_regs_lut[i].r63_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x67, fc2580_freq_regs_lut[i].r67_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x68, fc2580_freq_regs_lut[i].r68_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x69, fc2580_freq_regs_lut[i].r69_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6a, fc2580_freq_regs_lut[i].r6a_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6b, fc2580_freq_regs_lut[i].r6b_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6c, fc2580_freq_regs_lut[i].r6c_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6d, fc2580_freq_regs_lut[i].r6d_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6e, fc2580_freq_regs_lut[i].r6e_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x6f, fc2580_freq_regs_lut[i].r6f_val);
+ if (ret < 0)
+ goto err;
+
+ /* IF filters */
+ for (i = 0; i < ARRAY_SIZE(fc2580_if_filter_lut); i++) {
+ if (c->bandwidth_hz <= fc2580_if_filter_lut[i].freq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(fc2580_if_filter_lut))
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x36, fc2580_if_filter_lut[i].r36_val);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x37, 1UL * priv->cfg->clock * \
+ fc2580_if_filter_lut[i].mul / 1000000000);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x39, fc2580_if_filter_lut[i].r39_val);
+ if (ret < 0)
+ goto err;
+
+ /* calibration? */
+ ret = fc2580_wr_reg(priv, 0x2e, 0x09);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 5; i++) {
+ ret = fc2580_rd_reg(priv, 0x2f, &tmp_val);
+ if (ret < 0)
+ goto err;
+
+ /* done when [7:6] are set */
+ if ((tmp_val & 0xc0) == 0xc0)
+ break;
+
+ ret = fc2580_wr_reg(priv, 0x2e, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = fc2580_wr_reg(priv, 0x2e, 0x09);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(5000, 25000);
+ }
+
+ dev_dbg(&priv->i2c->dev, "%s: loop=%i\n", __func__, i);
+
+ ret = fc2580_wr_reg(priv, 0x2e, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int fc2580_init(struct dvb_frontend *fe)
+{
+ struct fc2580_priv *priv = fe->tuner_priv;
+ int ret, i;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ for (i = 0; i < ARRAY_SIZE(fc2580_init_reg_vals); i++) {
+ ret = fc2580_wr_reg(priv, fc2580_init_reg_vals[i].reg,
+ fc2580_init_reg_vals[i].val);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int fc2580_sleep(struct dvb_frontend *fe)
+{
+ struct fc2580_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ ret = fc2580_wr_reg(priv, 0x02, 0x0a);
+ if (ret < 0)
+ goto err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int fc2580_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct fc2580_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ *frequency = 0; /* Zero-IF */
+
+ return 0;
+}
+
+static int fc2580_release(struct dvb_frontend *fe)
+{
+ struct fc2580_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ kfree(fe->tuner_priv);
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops fc2580_tuner_ops = {
+ .info = {
+ .name = "FCI FC2580",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ },
+
+ .release = fc2580_release,
+
+ .init = fc2580_init,
+ .sleep = fc2580_sleep,
+ .set_params = fc2580_set_params,
+
+ .get_if_frequency = fc2580_get_if_frequency,
+};
+
+struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct fc2580_config *cfg)
+{
+ struct fc2580_priv *priv;
+ int ret;
+ u8 chip_id;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ priv = kzalloc(sizeof(struct fc2580_priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ dev_err(&i2c->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ goto err;
+ }
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ /* check if the tuner is there */
+ ret = fc2580_rd_reg(priv, 0x01, &chip_id);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&priv->i2c->dev, "%s: chip_id=%02x\n", __func__, chip_id);
+
+ switch (chip_id) {
+ case 0x56:
+ case 0x5a:
+ break;
+ default:
+ goto err;
+ }
+
+ dev_info(&priv->i2c->dev,
+ "%s: FCI FC2580 successfully identified\n",
+ KBUILD_MODNAME);
+
+ fe->tuner_priv = priv;
+ memcpy(&fe->ops.tuner_ops, &fc2580_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return fe;
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(fc2580_attach);
+
+MODULE_DESCRIPTION("FCI FC2580 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/fc2580.h b/drivers/media/tuners/fc2580.h
new file mode 100644
index 000000000000..222601e5d23b
--- /dev/null
+++ b/drivers/media/tuners/fc2580.h
@@ -0,0 +1,52 @@
+/*
+ * FCI FC2580 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef FC2580_H
+#define FC2580_H
+
+#include "dvb_frontend.h"
+
+struct fc2580_config {
+ /*
+ * I2C address
+ * 0x56, ...
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock
+ */
+ u32 clock;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_FC2580) || \
+ (defined(CONFIG_MEDIA_TUNER_FC2580_MODULE) && defined(MODULE))
+extern struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct fc2580_config *cfg);
+#else
+static inline struct dvb_frontend *fc2580_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, const struct fc2580_config *cfg)
+{
+ pr_warn("%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/tuners/fc2580_priv.h b/drivers/media/tuners/fc2580_priv.h
new file mode 100644
index 000000000000..be38a9e637e0
--- /dev/null
+++ b/drivers/media/tuners/fc2580_priv.h
@@ -0,0 +1,134 @@
+/*
+ * FCI FC2580 silicon tuner driver
+ *
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef FC2580_PRIV_H
+#define FC2580_PRIV_H
+
+#include "fc2580.h"
+
+struct fc2580_reg_val {
+ u8 reg;
+ u8 val;
+};
+
+static const struct fc2580_reg_val fc2580_init_reg_vals[] = {
+ {0x00, 0x00},
+ {0x12, 0x86},
+ {0x14, 0x5c},
+ {0x16, 0x3c},
+ {0x1f, 0xd2},
+ {0x09, 0xd7},
+ {0x0b, 0xd5},
+ {0x0c, 0x32},
+ {0x0e, 0x43},
+ {0x21, 0x0a},
+ {0x22, 0x82},
+ {0x45, 0x10},
+ {0x4c, 0x00},
+ {0x3f, 0x88},
+ {0x02, 0x0e},
+ {0x58, 0x14},
+};
+
+struct fc2580_pll {
+ u32 freq;
+ u8 div;
+ u8 band;
+};
+
+static const struct fc2580_pll fc2580_pll_lut[] = {
+ /* VCO min VCO max */
+ { 400000000, 12, 0x80}, /* .......... 4800000000 */
+ {1000000000, 4, 0x00}, /* 1600000000 4000000000 */
+ {0xffffffff, 2, 0x40}, /* 2000000000 .......... */
+};
+
+struct fc2580_if_filter {
+ u32 freq;
+ u16 mul;
+ u8 r36_val;
+ u8 r39_val;
+};
+
+static const struct fc2580_if_filter fc2580_if_filter_lut[] = {
+ { 6000000, 4400, 0x18, 0x00},
+ { 7000000, 3910, 0x18, 0x80},
+ { 8000000, 3300, 0x18, 0x80},
+ {0xffffffff, 3300, 0x18, 0x80},
+};
+
+struct fc2580_freq_regs {
+ u32 freq;
+ u8 r25_val;
+ u8 r27_val;
+ u8 r28_val;
+ u8 r29_val;
+ u8 r2b_val;
+ u8 r2c_val;
+ u8 r2d_val;
+ u8 r30_val;
+ u8 r44_val;
+ u8 r50_val;
+ u8 r53_val;
+ u8 r5f_val;
+ u8 r61_val;
+ u8 r62_val;
+ u8 r63_val;
+ u8 r67_val;
+ u8 r68_val;
+ u8 r69_val;
+ u8 r6a_val;
+ u8 r6b_val;
+ u8 r6c_val;
+ u8 r6d_val;
+ u8 r6e_val;
+ u8 r6f_val;
+};
+
+/* XXX: 0xff is used for don't-care! */
+static const struct fc2580_freq_regs fc2580_freq_regs_lut[] = {
+ { 400000000,
+ 0xff, 0x77, 0x33, 0x40, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c,
+ 0x50, 0x0f, 0x07, 0x00, 0x15, 0x03, 0x05, 0x10, 0x12, 0x08,
+ 0x0a, 0x78, 0x32, 0x54},
+ { 538000000,
+ 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c,
+ 0x50, 0x13, 0x07, 0x06, 0x15, 0x06, 0x08, 0x10, 0x12, 0x0b,
+ 0x0c, 0x78, 0x32, 0x14},
+ { 794000000,
+ 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c,
+ 0x50, 0x15, 0x03, 0x03, 0x15, 0x03, 0x05, 0x0c, 0x0e, 0x0b,
+ 0x0c, 0x78, 0x32, 0x14},
+ {1000000000,
+ 0xf0, 0x77, 0x53, 0x60, 0xff, 0xff, 0xff, 0x09, 0xff, 0x8c,
+ 0x50, 0x15, 0x07, 0x06, 0x15, 0x07, 0x09, 0x10, 0x12, 0x0b,
+ 0x0c, 0x78, 0x32, 0x14},
+ {0xffffffff,
+ 0xff, 0xff, 0xff, 0xff, 0x70, 0x37, 0xe7, 0x09, 0x20, 0x8c,
+ 0x50, 0x0f, 0x0f, 0x00, 0x13, 0x00, 0x02, 0x0c, 0x0e, 0x08,
+ 0x0a, 0xa0, 0x50, 0x14},
+};
+
+struct fc2580_priv {
+ const struct fc2580_config *cfg;
+ struct i2c_adapter *i2c;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/tuners/max2165.c
index ba84936aafd6..ba84936aafd6 100644
--- a/drivers/media/common/tuners/max2165.c
+++ b/drivers/media/tuners/max2165.c
diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/tuners/max2165.h
index c063c36a93d3..c063c36a93d3 100644
--- a/drivers/media/common/tuners/max2165.h
+++ b/drivers/media/tuners/max2165.h
diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/tuners/max2165_priv.h
index 91bbe021a08d..91bbe021a08d 100644
--- a/drivers/media/common/tuners/max2165_priv.h
+++ b/drivers/media/tuners/max2165_priv.h
diff --git a/drivers/media/tuners/mc44s803.c b/drivers/media/tuners/mc44s803.c
new file mode 100644
index 000000000000..f1b764074661
--- /dev/null
+++ b/drivers/media/tuners/mc44s803.c
@@ -0,0 +1,379 @@
+/*
+ * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
+ *
+ * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+
+#include "mc44s803.h"
+#include "mc44s803_priv.h"
+
+#define mc_printk(level, format, arg...) \
+ printk(level "mc44s803: " format , ## arg)
+
+/* Writes a single register */
+static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
+{
+ u8 buf[3];
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
+ };
+
+ buf[0] = (val & 0xff0000) >> 16;
+ buf[1] = (val & 0xff00) >> 8;
+ buf[2] = (val & 0xff);
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ mc_printk(KERN_WARNING, "I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* Reads a single register */
+static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
+{
+ u32 wval;
+ u8 buf[3];
+ int ret;
+ struct i2c_msg msg[] = {
+ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
+ .buf = buf, .len = 3 },
+ };
+
+ wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
+ MC44S803_REG_SM(reg, MC44S803_D);
+
+ ret = mc44s803_writereg(priv, wval);
+ if (ret)
+ return ret;
+
+ if (i2c_transfer(priv->i2c, msg, 1) != 1) {
+ mc_printk(KERN_WARNING, "I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
+static int mc44s803_release(struct dvb_frontend *fe)
+{
+ struct mc44s803_priv *priv = fe->tuner_priv;
+
+ fe->tuner_priv = NULL;
+ kfree(priv);
+
+ return 0;
+}
+
+static int mc44s803_init(struct dvb_frontend *fe)
+{
+ struct mc44s803_priv *priv = fe->tuner_priv;
+ u32 val;
+ int err;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+/* Reset chip */
+ val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_RS);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+/* Power Up and Start Osc */
+
+ val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
+ MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
+ MC44S803_REG_SM(1, MC44S803_OSCSEL);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
+ MC44S803_REG_SM(0x200, MC44S803_POWER);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ msleep(10);
+
+ val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
+ MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
+ MC44S803_REG_SM(1, MC44S803_OSCSEL);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ msleep(20);
+
+/* Setup Mixer */
+
+ val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
+ MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+/* Setup Cirquit Adjust */
+
+ val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_G1) |
+ MC44S803_REG_SM(1, MC44S803_G3) |
+ MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
+ MC44S803_REG_SM(1, MC44S803_G6) |
+ MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
+ MC44S803_REG_SM(0x3, MC44S803_LP) |
+ MC44S803_REG_SM(1, MC44S803_CLRF) |
+ MC44S803_REG_SM(1, MC44S803_CLIF);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_G1) |
+ MC44S803_REG_SM(1, MC44S803_G3) |
+ MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
+ MC44S803_REG_SM(1, MC44S803_G6) |
+ MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
+ MC44S803_REG_SM(0x3, MC44S803_LP);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+/* Setup Digtune */
+
+ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
+ MC44S803_REG_SM(3, MC44S803_XOD);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+/* Setup AGC */
+
+ val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_AT1) |
+ MC44S803_REG_SM(1, MC44S803_AT2) |
+ MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
+ MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
+ MC44S803_REG_SM(1, MC44S803_LNA0);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ return 0;
+
+exit:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ mc_printk(KERN_WARNING, "I/O Error\n");
+ return err;
+}
+
+static int mc44s803_set_params(struct dvb_frontend *fe)
+{
+ struct mc44s803_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 r1, r2, n1, n2, lo1, lo2, freq, val;
+ int err;
+
+ priv->frequency = c->frequency;
+
+ r1 = MC44S803_OSC / 1000000;
+ r2 = MC44S803_OSC / 100000;
+
+ n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000;
+ freq = MC44S803_OSC / r1 * n1;
+ lo1 = ((60 * n1) + (r1 / 2)) / r1;
+ freq = freq - c->frequency;
+
+ n2 = (freq - MC44S803_IF2 + 50000) / 100000;
+ lo2 = ((60 * n2) + (r2 / 2)) / r2;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
+ MC44S803_REG_SM(r1-1, MC44S803_R1) |
+ MC44S803_REG_SM(r2-1, MC44S803_R2) |
+ MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
+ MC44S803_REG_SM(n1-2, MC44S803_LO1);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
+ MC44S803_REG_SM(n2-2, MC44S803_LO2);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
+ MC44S803_REG_SM(1, MC44S803_DA) |
+ MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
+ MC44S803_REG_SM(1, MC44S803_AT);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
+ MC44S803_REG_SM(2, MC44S803_DA) |
+ MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
+ MC44S803_REG_SM(1, MC44S803_AT);
+
+ err = mc44s803_writereg(priv, val);
+ if (err)
+ goto exit;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+
+exit:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ mc_printk(KERN_WARNING, "I/O Error\n");
+ return err;
+}
+
+static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mc44s803_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int mc44s803_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ *frequency = MC44S803_IF2; /* 36.125 MHz */
+ return 0;
+}
+
+static const struct dvb_tuner_ops mc44s803_tuner_ops = {
+ .info = {
+ .name = "Freescale MC44S803",
+ .frequency_min = 48000000,
+ .frequency_max = 1000000000,
+ .frequency_step = 100000,
+ },
+
+ .release = mc44s803_release,
+ .init = mc44s803_init,
+ .set_params = mc44s803_set_params,
+ .get_frequency = mc44s803_get_frequency,
+ .get_if_frequency = mc44s803_get_if_frequency,
+};
+
+/* This functions tries to identify a MC44S803 tuner by reading the ID
+ register. This is hasty. */
+struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct mc44s803_config *cfg)
+{
+ struct mc44s803_priv *priv;
+ u32 reg;
+ u8 id;
+ int ret;
+
+ reg = 0;
+
+ priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ priv->fe = fe;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
+ if (ret)
+ goto error;
+
+ id = MC44S803_REG_MS(reg, MC44S803_ID);
+
+ if (id != 0x14) {
+ mc_printk(KERN_ERR, "unsupported ID "
+ "(%x should be 0x14)\n", id);
+ goto error;
+ }
+
+ mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
+ memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return fe;
+
+error:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(mc44s803_attach);
+
+MODULE_AUTHOR("Jochen Friedrich");
+MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mc44s803.h b/drivers/media/tuners/mc44s803.h
index 34f3892d3f6d..34f3892d3f6d 100644
--- a/drivers/media/common/tuners/mc44s803.h
+++ b/drivers/media/tuners/mc44s803.h
diff --git a/drivers/media/common/tuners/mc44s803_priv.h b/drivers/media/tuners/mc44s803_priv.h
index 14a92780906d..14a92780906d 100644
--- a/drivers/media/common/tuners/mc44s803_priv.h
+++ b/drivers/media/tuners/mc44s803_priv.h
diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/tuners/mt2060.c
index 13381de58a84..13381de58a84 100644
--- a/drivers/media/common/tuners/mt2060.c
+++ b/drivers/media/tuners/mt2060.c
diff --git a/drivers/media/common/tuners/mt2060.h b/drivers/media/tuners/mt2060.h
index cb60caffb6b6..cb60caffb6b6 100644
--- a/drivers/media/common/tuners/mt2060.h
+++ b/drivers/media/tuners/mt2060.h
diff --git a/drivers/media/common/tuners/mt2060_priv.h b/drivers/media/tuners/mt2060_priv.h
index 2b60de6c707d..2b60de6c707d 100644
--- a/drivers/media/common/tuners/mt2060_priv.h
+++ b/drivers/media/tuners/mt2060_priv.h
diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/tuners/mt2063.c
index 0ed9091ff48e..0ed9091ff48e 100644
--- a/drivers/media/common/tuners/mt2063.c
+++ b/drivers/media/tuners/mt2063.c
diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/tuners/mt2063.h
index 3f5cfd93713f..3f5cfd93713f 100644
--- a/drivers/media/common/tuners/mt2063.h
+++ b/drivers/media/tuners/mt2063.h
diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c
index 0e74e97e0d1a..0e74e97e0d1a 100644
--- a/drivers/media/common/tuners/mt20xx.c
+++ b/drivers/media/tuners/mt20xx.c
diff --git a/drivers/media/common/tuners/mt20xx.h b/drivers/media/tuners/mt20xx.h
index 259553a24903..259553a24903 100644
--- a/drivers/media/common/tuners/mt20xx.h
+++ b/drivers/media/tuners/mt20xx.h
diff --git a/drivers/media/common/tuners/mt2131.c b/drivers/media/tuners/mt2131.c
index f83b0c1ea6c8..f83b0c1ea6c8 100644
--- a/drivers/media/common/tuners/mt2131.c
+++ b/drivers/media/tuners/mt2131.c
diff --git a/drivers/media/common/tuners/mt2131.h b/drivers/media/tuners/mt2131.h
index 6632de640df0..6632de640df0 100644
--- a/drivers/media/common/tuners/mt2131.h
+++ b/drivers/media/tuners/mt2131.h
diff --git a/drivers/media/common/tuners/mt2131_priv.h b/drivers/media/tuners/mt2131_priv.h
index 62aeedf5c550..62aeedf5c550 100644
--- a/drivers/media/common/tuners/mt2131_priv.h
+++ b/drivers/media/tuners/mt2131_priv.h
diff --git a/drivers/media/common/tuners/mt2266.c b/drivers/media/tuners/mt2266.c
index bca4d75e42d4..bca4d75e42d4 100644
--- a/drivers/media/common/tuners/mt2266.c
+++ b/drivers/media/tuners/mt2266.c
diff --git a/drivers/media/common/tuners/mt2266.h b/drivers/media/tuners/mt2266.h
index 4d083882d044..4d083882d044 100644
--- a/drivers/media/common/tuners/mt2266.h
+++ b/drivers/media/tuners/mt2266.h
diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c
new file mode 100644
index 000000000000..b473b76cb278
--- /dev/null
+++ b/drivers/media/tuners/mxl5005s.c
@@ -0,0 +1,4120 @@
+/*
+ MaxLinear MXL5005S VSB/QAM/DVBT tuner driver
+
+ Copyright (C) 2008 MaxLinear
+ Copyright (C) 2006 Steven Toth <stoth@linuxtv.org>
+ Functions:
+ mxl5005s_reset()
+ mxl5005s_writereg()
+ mxl5005s_writeregs()
+ mxl5005s_init()
+ mxl5005s_reconfigure()
+ mxl5005s_AssignTunerMode()
+ mxl5005s_set_params()
+ mxl5005s_get_frequency()
+ mxl5005s_get_bandwidth()
+ mxl5005s_release()
+ mxl5005s_attach()
+
+ Copyright (C) 2008 Realtek
+ Copyright (C) 2008 Jan Hoogenraad
+ Functions:
+ mxl5005s_SetRfFreqHz()
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/*
+ History of this driver (Steven Toth):
+ I was given a public release of a linux driver that included
+ support for the MaxLinear MXL5005S silicon tuner. Analysis of
+ the tuner driver showed clearly three things.
+
+ 1. The tuner driver didn't support the LinuxTV tuner API
+ so the code Realtek added had to be removed.
+
+ 2. A significant amount of the driver is reference driver code
+ from MaxLinear, I felt it was important to identify and
+ preserve this.
+
+ 3. New code has to be added to interface correctly with the
+ LinuxTV API, as a regular kernel module.
+
+ Other than the reference driver enum's, I've clearly marked
+ sections of the code and retained the copyright of the
+ respective owners.
+*/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "dvb_frontend.h"
+#include "mxl5005s.h"
+
+static int debug;
+
+#define dprintk(level, arg...) do { \
+ if (level <= debug) \
+ printk(arg); \
+ } while (0)
+
+#define TUNER_REGS_NUM 104
+#define INITCTRL_NUM 40
+
+#ifdef _MXL_PRODUCTION
+#define CHCTRL_NUM 39
+#else
+#define CHCTRL_NUM 36
+#endif
+
+#define MXLCTRL_NUM 189
+#define MASTER_CONTROL_ADDR 9
+
+/* Enumeration of Master Control Register State */
+enum master_control_state {
+ MC_LOAD_START = 1,
+ MC_POWER_DOWN,
+ MC_SYNTH_RESET,
+ MC_SEQ_OFF
+};
+
+/* Enumeration of MXL5005 Tuner Modulation Type */
+enum {
+ MXL_DEFAULT_MODULATION = 0,
+ MXL_DVBT,
+ MXL_ATSC,
+ MXL_QAM,
+ MXL_ANALOG_CABLE,
+ MXL_ANALOG_OTA
+};
+
+/* MXL5005 Tuner Register Struct */
+struct TunerReg {
+ u16 Reg_Num; /* Tuner Register Address */
+ u16 Reg_Val; /* Current sw programmed value waiting to be written */
+};
+
+enum {
+ /* Initialization Control Names */
+ DN_IQTN_AMP_CUT = 1, /* 1 */
+ BB_MODE, /* 2 */
+ BB_BUF, /* 3 */
+ BB_BUF_OA, /* 4 */
+ BB_ALPF_BANDSELECT, /* 5 */
+ BB_IQSWAP, /* 6 */
+ BB_DLPF_BANDSEL, /* 7 */
+ RFSYN_CHP_GAIN, /* 8 */
+ RFSYN_EN_CHP_HIGAIN, /* 9 */
+ AGC_IF, /* 10 */
+ AGC_RF, /* 11 */
+ IF_DIVVAL, /* 12 */
+ IF_VCO_BIAS, /* 13 */
+ CHCAL_INT_MOD_IF, /* 14 */
+ CHCAL_FRAC_MOD_IF, /* 15 */
+ DRV_RES_SEL, /* 16 */
+ I_DRIVER, /* 17 */
+ EN_AAF, /* 18 */
+ EN_3P, /* 19 */
+ EN_AUX_3P, /* 20 */
+ SEL_AAF_BAND, /* 21 */
+ SEQ_ENCLK16_CLK_OUT, /* 22 */
+ SEQ_SEL4_16B, /* 23 */
+ XTAL_CAPSELECT, /* 24 */
+ IF_SEL_DBL, /* 25 */
+ RFSYN_R_DIV, /* 26 */
+ SEQ_EXTSYNTHCALIF, /* 27 */
+ SEQ_EXTDCCAL, /* 28 */
+ AGC_EN_RSSI, /* 29 */
+ RFA_ENCLKRFAGC, /* 30 */
+ RFA_RSSI_REFH, /* 31 */
+ RFA_RSSI_REF, /* 32 */
+ RFA_RSSI_REFL, /* 33 */
+ RFA_FLR, /* 34 */
+ RFA_CEIL, /* 35 */
+ SEQ_EXTIQFSMPULSE, /* 36 */
+ OVERRIDE_1, /* 37 */
+ BB_INITSTATE_DLPF_TUNE, /* 38 */
+ TG_R_DIV, /* 39 */
+ EN_CHP_LIN_B, /* 40 */
+
+ /* Channel Change Control Names */
+ DN_POLY = 51, /* 51 */
+ DN_RFGAIN, /* 52 */
+ DN_CAP_RFLPF, /* 53 */
+ DN_EN_VHFUHFBAR, /* 54 */
+ DN_GAIN_ADJUST, /* 55 */
+ DN_IQTNBUF_AMP, /* 56 */
+ DN_IQTNGNBFBIAS_BST, /* 57 */
+ RFSYN_EN_OUTMUX, /* 58 */
+ RFSYN_SEL_VCO_OUT, /* 59 */
+ RFSYN_SEL_VCO_HI, /* 60 */
+ RFSYN_SEL_DIVM, /* 61 */
+ RFSYN_RF_DIV_BIAS, /* 62 */
+ DN_SEL_FREQ, /* 63 */
+ RFSYN_VCO_BIAS, /* 64 */
+ CHCAL_INT_MOD_RF, /* 65 */
+ CHCAL_FRAC_MOD_RF, /* 66 */
+ RFSYN_LPF_R, /* 67 */
+ CHCAL_EN_INT_RF, /* 68 */
+ TG_LO_DIVVAL, /* 69 */
+ TG_LO_SELVAL, /* 70 */
+ TG_DIV_VAL, /* 71 */
+ TG_VCO_BIAS, /* 72 */
+ SEQ_EXTPOWERUP, /* 73 */
+ OVERRIDE_2, /* 74 */
+ OVERRIDE_3, /* 75 */
+ OVERRIDE_4, /* 76 */
+ SEQ_FSM_PULSE, /* 77 */
+ GPIO_4B, /* 78 */
+ GPIO_3B, /* 79 */
+ GPIO_4, /* 80 */
+ GPIO_3, /* 81 */
+ GPIO_1B, /* 82 */
+ DAC_A_ENABLE, /* 83 */
+ DAC_B_ENABLE, /* 84 */
+ DAC_DIN_A, /* 85 */
+ DAC_DIN_B, /* 86 */
+#ifdef _MXL_PRODUCTION
+ RFSYN_EN_DIV, /* 87 */
+ RFSYN_DIVM, /* 88 */
+ DN_BYPASS_AGC_I2C /* 89 */
+#endif
+};
+
+/*
+ * The following context is source code provided by MaxLinear.
+ * MaxLinear source code - Common_MXL.h (?)
+ */
+
+/* Constants */
+#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104
+#define MXL5005S_LATCH_BYTE 0xfe
+
+/* Register address, MSB, and LSB */
+#define MXL5005S_BB_IQSWAP_ADDR 59
+#define MXL5005S_BB_IQSWAP_MSB 0
+#define MXL5005S_BB_IQSWAP_LSB 0
+
+#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53
+#define MXL5005S_BB_DLPF_BANDSEL_MSB 4
+#define MXL5005S_BB_DLPF_BANDSEL_LSB 3
+
+/* Standard modes */
+enum {
+ MXL5005S_STANDARD_DVBT,
+ MXL5005S_STANDARD_ATSC,
+};
+#define MXL5005S_STANDARD_MODE_NUM 2
+
+/* Bandwidth modes */
+enum {
+ MXL5005S_BANDWIDTH_6MHZ = 6000000,
+ MXL5005S_BANDWIDTH_7MHZ = 7000000,
+ MXL5005S_BANDWIDTH_8MHZ = 8000000,
+};
+#define MXL5005S_BANDWIDTH_MODE_NUM 3
+
+/* MXL5005 Tuner Control Struct */
+struct TunerControl {
+ u16 Ctrl_Num; /* Control Number */
+ u16 size; /* Number of bits to represent Value */
+ u16 addr[25]; /* Array of Tuner Register Address for each bit pos */
+ u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */
+ u16 val[25]; /* Binary representation of Value */
+};
+
+/* MXL5005 Tuner Struct */
+struct mxl5005s_state {
+ u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */
+ u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */
+ u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */
+ u32 IF_OUT; /* Desired IF Out Frequency */
+ u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */
+ u32 RF_IN; /* RF Input Frequency */
+ u32 Fxtal; /* XTAL Frequency */
+ u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */
+ u16 TOP; /* Value: take over point */
+ u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */
+ u8 DIV_OUT; /* 4MHz or 16MHz */
+ u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */
+ u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */
+
+ /* Modulation Type; */
+ /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */
+ u8 Mod_Type;
+
+ /* Tracking Filter Type */
+ /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */
+ u8 TF_Type;
+
+ /* Calculated Settings */
+ u32 RF_LO; /* Synth RF LO Frequency */
+ u32 IF_LO; /* Synth IF LO Frequency */
+ u32 TG_LO; /* Synth TG_LO Frequency */
+
+ /* Pointers to ControlName Arrays */
+ u16 Init_Ctrl_Num; /* Number of INIT Control Names */
+ struct TunerControl
+ Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */
+
+ u16 CH_Ctrl_Num; /* Number of CH Control Names */
+ struct TunerControl
+ CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */
+
+ u16 MXL_Ctrl_Num; /* Number of MXL Control Names */
+ struct TunerControl
+ MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */
+
+ /* Pointer to Tuner Register Array */
+ u16 TunerRegs_Num; /* Number of Tuner Registers */
+ struct TunerReg
+ TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */
+
+ /* Linux driver framework specific */
+ struct mxl5005s_config *config;
+ struct dvb_frontend *frontend;
+ struct i2c_adapter *i2c;
+
+ /* Cache values */
+ u32 current_mode;
+
+};
+
+static u16 MXL_GetMasterControl(u8 *MasterReg, int state);
+static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value);
+static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value);
+static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit,
+ u8 bitVal);
+static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum,
+ u8 *RegVal, int *count);
+static u32 MXL_Ceiling(u32 value, u32 resolution);
+static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal);
+static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
+ u32 value, u16 controlGroup);
+static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val);
+static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
+ u8 *RegVal, int *count);
+static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq);
+static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe);
+static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe);
+static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
+ u8 *RegVal, int *count);
+static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable,
+ u8 *datatable, u8 len);
+static u16 MXL_IFSynthInit(struct dvb_frontend *fe);
+static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type,
+ u32 bandwidth);
+static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type,
+ u32 bandwidth);
+
+/* ----------------------------------------------------------------
+ * Begin: Custom code salvaged from the Realtek driver.
+ * Copyright (C) 2008 Realtek
+ * Copyright (C) 2008 Jan Hoogenraad
+ * This code is placed under the terms of the GNU General Public License
+ *
+ * Released by Realtek under GPLv2.
+ * Thanks to Realtek for a lot of support we received !
+ *
+ * Revision: 080314 - original version
+ */
+
+static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
+ unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
+ int TableLen;
+
+ u32 IfDivval = 0;
+ unsigned char MasterControlByte;
+
+ dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz);
+
+ /* Set MxL5005S tuner RF frequency according to example code. */
+
+ /* Tuner RF frequency setting stage 0 */
+ MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
+ AddrTable[0] = MASTER_CONTROL_ADDR;
+ ByteTable[0] |= state->config->AgcMasterByte;
+
+ mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
+
+ /* Tuner RF frequency setting stage 1 */
+ MXL_TuneRF(fe, RfFreqHz);
+
+ MXL_ControlRead(fe, IF_DIVVAL, &IfDivval);
+
+ MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0);
+ MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1);
+ MXL_ControlWrite(fe, IF_DIVVAL, 8);
+ MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen);
+
+ MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START);
+ AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
+ ByteTable[TableLen] = MasterControlByte |
+ state->config->AgcMasterByte;
+ TableLen += 1;
+
+ mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+
+ /* Wait 30 ms. */
+ msleep(150);
+
+ /* Tuner RF frequency setting stage 2 */
+ MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1);
+ MXL_ControlWrite(fe, IF_DIVVAL, IfDivval);
+ MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen);
+
+ MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START);
+ AddrTable[TableLen] = MASTER_CONTROL_ADDR ;
+ ByteTable[TableLen] = MasterControlByte |
+ state->config->AgcMasterByte ;
+ TableLen += 1;
+
+ mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+
+ msleep(100);
+
+ return 0;
+}
+/* End: Custom code taken from the Realtek driver */
+
+/* ----------------------------------------------------------------
+ * Begin: Reference driver code found in the Realtek driver.
+ * Copyright (C) 2008 MaxLinear
+ */
+static u16 MXL5005_RegisterInit(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ state->TunerRegs_Num = TUNER_REGS_NUM ;
+
+ state->TunerRegs[0].Reg_Num = 9 ;
+ state->TunerRegs[0].Reg_Val = 0x40 ;
+
+ state->TunerRegs[1].Reg_Num = 11 ;
+ state->TunerRegs[1].Reg_Val = 0x19 ;
+
+ state->TunerRegs[2].Reg_Num = 12 ;
+ state->TunerRegs[2].Reg_Val = 0x60 ;
+
+ state->TunerRegs[3].Reg_Num = 13 ;
+ state->TunerRegs[3].Reg_Val = 0x00 ;
+
+ state->TunerRegs[4].Reg_Num = 14 ;
+ state->TunerRegs[4].Reg_Val = 0x00 ;
+
+ state->TunerRegs[5].Reg_Num = 15 ;
+ state->TunerRegs[5].Reg_Val = 0xC0 ;
+
+ state->TunerRegs[6].Reg_Num = 16 ;
+ state->TunerRegs[6].Reg_Val = 0x00 ;
+
+ state->TunerRegs[7].Reg_Num = 17 ;
+ state->TunerRegs[7].Reg_Val = 0x00 ;
+
+ state->TunerRegs[8].Reg_Num = 18 ;
+ state->TunerRegs[8].Reg_Val = 0x00 ;
+
+ state->TunerRegs[9].Reg_Num = 19 ;
+ state->TunerRegs[9].Reg_Val = 0x34 ;
+
+ state->TunerRegs[10].Reg_Num = 21 ;
+ state->TunerRegs[10].Reg_Val = 0x00 ;
+
+ state->TunerRegs[11].Reg_Num = 22 ;
+ state->TunerRegs[11].Reg_Val = 0x6B ;
+
+ state->TunerRegs[12].Reg_Num = 23 ;
+ state->TunerRegs[12].Reg_Val = 0x35 ;
+
+ state->TunerRegs[13].Reg_Num = 24 ;
+ state->TunerRegs[13].Reg_Val = 0x70 ;
+
+ state->TunerRegs[14].Reg_Num = 25 ;
+ state->TunerRegs[14].Reg_Val = 0x3E ;
+
+ state->TunerRegs[15].Reg_Num = 26 ;
+ state->TunerRegs[15].Reg_Val = 0x82 ;
+
+ state->TunerRegs[16].Reg_Num = 31 ;
+ state->TunerRegs[16].Reg_Val = 0x00 ;
+
+ state->TunerRegs[17].Reg_Num = 32 ;
+ state->TunerRegs[17].Reg_Val = 0x40 ;
+
+ state->TunerRegs[18].Reg_Num = 33 ;
+ state->TunerRegs[18].Reg_Val = 0x53 ;
+
+ state->TunerRegs[19].Reg_Num = 34 ;
+ state->TunerRegs[19].Reg_Val = 0x81 ;
+
+ state->TunerRegs[20].Reg_Num = 35 ;
+ state->TunerRegs[20].Reg_Val = 0xC9 ;
+
+ state->TunerRegs[21].Reg_Num = 36 ;
+ state->TunerRegs[21].Reg_Val = 0x01 ;
+
+ state->TunerRegs[22].Reg_Num = 37 ;
+ state->TunerRegs[22].Reg_Val = 0x00 ;
+
+ state->TunerRegs[23].Reg_Num = 41 ;
+ state->TunerRegs[23].Reg_Val = 0x00 ;
+
+ state->TunerRegs[24].Reg_Num = 42 ;
+ state->TunerRegs[24].Reg_Val = 0xF8 ;
+
+ state->TunerRegs[25].Reg_Num = 43 ;
+ state->TunerRegs[25].Reg_Val = 0x43 ;
+
+ state->TunerRegs[26].Reg_Num = 44 ;
+ state->TunerRegs[26].Reg_Val = 0x20 ;
+
+ state->TunerRegs[27].Reg_Num = 45 ;
+ state->TunerRegs[27].Reg_Val = 0x80 ;
+
+ state->TunerRegs[28].Reg_Num = 46 ;
+ state->TunerRegs[28].Reg_Val = 0x88 ;
+
+ state->TunerRegs[29].Reg_Num = 47 ;
+ state->TunerRegs[29].Reg_Val = 0x86 ;
+
+ state->TunerRegs[30].Reg_Num = 48 ;
+ state->TunerRegs[30].Reg_Val = 0x00 ;
+
+ state->TunerRegs[31].Reg_Num = 49 ;
+ state->TunerRegs[31].Reg_Val = 0x00 ;
+
+ state->TunerRegs[32].Reg_Num = 53 ;
+ state->TunerRegs[32].Reg_Val = 0x94 ;
+
+ state->TunerRegs[33].Reg_Num = 54 ;
+ state->TunerRegs[33].Reg_Val = 0xFA ;
+
+ state->TunerRegs[34].Reg_Num = 55 ;
+ state->TunerRegs[34].Reg_Val = 0x92 ;
+
+ state->TunerRegs[35].Reg_Num = 56 ;
+ state->TunerRegs[35].Reg_Val = 0x80 ;
+
+ state->TunerRegs[36].Reg_Num = 57 ;
+ state->TunerRegs[36].Reg_Val = 0x41 ;
+
+ state->TunerRegs[37].Reg_Num = 58 ;
+ state->TunerRegs[37].Reg_Val = 0xDB ;
+
+ state->TunerRegs[38].Reg_Num = 59 ;
+ state->TunerRegs[38].Reg_Val = 0x00 ;
+
+ state->TunerRegs[39].Reg_Num = 60 ;
+ state->TunerRegs[39].Reg_Val = 0x00 ;
+
+ state->TunerRegs[40].Reg_Num = 61 ;
+ state->TunerRegs[40].Reg_Val = 0x00 ;
+
+ state->TunerRegs[41].Reg_Num = 62 ;
+ state->TunerRegs[41].Reg_Val = 0x00 ;
+
+ state->TunerRegs[42].Reg_Num = 65 ;
+ state->TunerRegs[42].Reg_Val = 0xF8 ;
+
+ state->TunerRegs[43].Reg_Num = 66 ;
+ state->TunerRegs[43].Reg_Val = 0xE4 ;
+
+ state->TunerRegs[44].Reg_Num = 67 ;
+ state->TunerRegs[44].Reg_Val = 0x90 ;
+
+ state->TunerRegs[45].Reg_Num = 68 ;
+ state->TunerRegs[45].Reg_Val = 0xC0 ;
+
+ state->TunerRegs[46].Reg_Num = 69 ;
+ state->TunerRegs[46].Reg_Val = 0x01 ;
+
+ state->TunerRegs[47].Reg_Num = 70 ;
+ state->TunerRegs[47].Reg_Val = 0x50 ;
+
+ state->TunerRegs[48].Reg_Num = 71 ;
+ state->TunerRegs[48].Reg_Val = 0x06 ;
+
+ state->TunerRegs[49].Reg_Num = 72 ;
+ state->TunerRegs[49].Reg_Val = 0x00 ;
+
+ state->TunerRegs[50].Reg_Num = 73 ;
+ state->TunerRegs[50].Reg_Val = 0x20 ;
+
+ state->TunerRegs[51].Reg_Num = 76 ;
+ state->TunerRegs[51].Reg_Val = 0xBB ;
+
+ state->TunerRegs[52].Reg_Num = 77 ;
+ state->TunerRegs[52].Reg_Val = 0x13 ;
+
+ state->TunerRegs[53].Reg_Num = 81 ;
+ state->TunerRegs[53].Reg_Val = 0x04 ;
+
+ state->TunerRegs[54].Reg_Num = 82 ;
+ state->TunerRegs[54].Reg_Val = 0x75 ;
+
+ state->TunerRegs[55].Reg_Num = 83 ;
+ state->TunerRegs[55].Reg_Val = 0x00 ;
+
+ state->TunerRegs[56].Reg_Num = 84 ;
+ state->TunerRegs[56].Reg_Val = 0x00 ;
+
+ state->TunerRegs[57].Reg_Num = 85 ;
+ state->TunerRegs[57].Reg_Val = 0x00 ;
+
+ state->TunerRegs[58].Reg_Num = 91 ;
+ state->TunerRegs[58].Reg_Val = 0x70 ;
+
+ state->TunerRegs[59].Reg_Num = 92 ;
+ state->TunerRegs[59].Reg_Val = 0x00 ;
+
+ state->TunerRegs[60].Reg_Num = 93 ;
+ state->TunerRegs[60].Reg_Val = 0x00 ;
+
+ state->TunerRegs[61].Reg_Num = 94 ;
+ state->TunerRegs[61].Reg_Val = 0x00 ;
+
+ state->TunerRegs[62].Reg_Num = 95 ;
+ state->TunerRegs[62].Reg_Val = 0x0C ;
+
+ state->TunerRegs[63].Reg_Num = 96 ;
+ state->TunerRegs[63].Reg_Val = 0x00 ;
+
+ state->TunerRegs[64].Reg_Num = 97 ;
+ state->TunerRegs[64].Reg_Val = 0x00 ;
+
+ state->TunerRegs[65].Reg_Num = 98 ;
+ state->TunerRegs[65].Reg_Val = 0xE2 ;
+
+ state->TunerRegs[66].Reg_Num = 99 ;
+ state->TunerRegs[66].Reg_Val = 0x00 ;
+
+ state->TunerRegs[67].Reg_Num = 100 ;
+ state->TunerRegs[67].Reg_Val = 0x00 ;
+
+ state->TunerRegs[68].Reg_Num = 101 ;
+ state->TunerRegs[68].Reg_Val = 0x12 ;
+
+ state->TunerRegs[69].Reg_Num = 102 ;
+ state->TunerRegs[69].Reg_Val = 0x80 ;
+
+ state->TunerRegs[70].Reg_Num = 103 ;
+ state->TunerRegs[70].Reg_Val = 0x32 ;
+
+ state->TunerRegs[71].Reg_Num = 104 ;
+ state->TunerRegs[71].Reg_Val = 0xB4 ;
+
+ state->TunerRegs[72].Reg_Num = 105 ;
+ state->TunerRegs[72].Reg_Val = 0x60 ;
+
+ state->TunerRegs[73].Reg_Num = 106 ;
+ state->TunerRegs[73].Reg_Val = 0x83 ;
+
+ state->TunerRegs[74].Reg_Num = 107 ;
+ state->TunerRegs[74].Reg_Val = 0x84 ;
+
+ state->TunerRegs[75].Reg_Num = 108 ;
+ state->TunerRegs[75].Reg_Val = 0x9C ;
+
+ state->TunerRegs[76].Reg_Num = 109 ;
+ state->TunerRegs[76].Reg_Val = 0x02 ;
+
+ state->TunerRegs[77].Reg_Num = 110 ;
+ state->TunerRegs[77].Reg_Val = 0x81 ;
+
+ state->TunerRegs[78].Reg_Num = 111 ;
+ state->TunerRegs[78].Reg_Val = 0xC0 ;
+
+ state->TunerRegs[79].Reg_Num = 112 ;
+ state->TunerRegs[79].Reg_Val = 0x10 ;
+
+ state->TunerRegs[80].Reg_Num = 131 ;
+ state->TunerRegs[80].Reg_Val = 0x8A ;
+
+ state->TunerRegs[81].Reg_Num = 132 ;
+ state->TunerRegs[81].Reg_Val = 0x10 ;
+
+ state->TunerRegs[82].Reg_Num = 133 ;
+ state->TunerRegs[82].Reg_Val = 0x24 ;
+
+ state->TunerRegs[83].Reg_Num = 134 ;
+ state->TunerRegs[83].Reg_Val = 0x00 ;
+
+ state->TunerRegs[84].Reg_Num = 135 ;
+ state->TunerRegs[84].Reg_Val = 0x00 ;
+
+ state->TunerRegs[85].Reg_Num = 136 ;
+ state->TunerRegs[85].Reg_Val = 0x7E ;
+
+ state->TunerRegs[86].Reg_Num = 137 ;
+ state->TunerRegs[86].Reg_Val = 0x40 ;
+
+ state->TunerRegs[87].Reg_Num = 138 ;
+ state->TunerRegs[87].Reg_Val = 0x38 ;
+
+ state->TunerRegs[88].Reg_Num = 146 ;
+ state->TunerRegs[88].Reg_Val = 0xF6 ;
+
+ state->TunerRegs[89].Reg_Num = 147 ;
+ state->TunerRegs[89].Reg_Val = 0x1A ;
+
+ state->TunerRegs[90].Reg_Num = 148 ;
+ state->TunerRegs[90].Reg_Val = 0x62 ;
+
+ state->TunerRegs[91].Reg_Num = 149 ;
+ state->TunerRegs[91].Reg_Val = 0x33 ;
+
+ state->TunerRegs[92].Reg_Num = 150 ;
+ state->TunerRegs[92].Reg_Val = 0x80 ;
+
+ state->TunerRegs[93].Reg_Num = 156 ;
+ state->TunerRegs[93].Reg_Val = 0x56 ;
+
+ state->TunerRegs[94].Reg_Num = 157 ;
+ state->TunerRegs[94].Reg_Val = 0x17 ;
+
+ state->TunerRegs[95].Reg_Num = 158 ;
+ state->TunerRegs[95].Reg_Val = 0xA9 ;
+
+ state->TunerRegs[96].Reg_Num = 159 ;
+ state->TunerRegs[96].Reg_Val = 0x00 ;
+
+ state->TunerRegs[97].Reg_Num = 160 ;
+ state->TunerRegs[97].Reg_Val = 0x00 ;
+
+ state->TunerRegs[98].Reg_Num = 161 ;
+ state->TunerRegs[98].Reg_Val = 0x00 ;
+
+ state->TunerRegs[99].Reg_Num = 162 ;
+ state->TunerRegs[99].Reg_Val = 0x40 ;
+
+ state->TunerRegs[100].Reg_Num = 166 ;
+ state->TunerRegs[100].Reg_Val = 0xAE ;
+
+ state->TunerRegs[101].Reg_Num = 167 ;
+ state->TunerRegs[101].Reg_Val = 0x1B ;
+
+ state->TunerRegs[102].Reg_Num = 168 ;
+ state->TunerRegs[102].Reg_Val = 0xF2 ;
+
+ state->TunerRegs[103].Reg_Num = 195 ;
+ state->TunerRegs[103].Reg_Val = 0x00 ;
+
+ return 0 ;
+}
+
+static u16 MXL5005_ControlInit(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ state->Init_Ctrl_Num = INITCTRL_NUM;
+
+ state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ;
+ state->Init_Ctrl[0].size = 1 ;
+ state->Init_Ctrl[0].addr[0] = 73;
+ state->Init_Ctrl[0].bit[0] = 7;
+ state->Init_Ctrl[0].val[0] = 0;
+
+ state->Init_Ctrl[1].Ctrl_Num = BB_MODE ;
+ state->Init_Ctrl[1].size = 1 ;
+ state->Init_Ctrl[1].addr[0] = 53;
+ state->Init_Ctrl[1].bit[0] = 2;
+ state->Init_Ctrl[1].val[0] = 1;
+
+ state->Init_Ctrl[2].Ctrl_Num = BB_BUF ;
+ state->Init_Ctrl[2].size = 2 ;
+ state->Init_Ctrl[2].addr[0] = 53;
+ state->Init_Ctrl[2].bit[0] = 1;
+ state->Init_Ctrl[2].val[0] = 0;
+ state->Init_Ctrl[2].addr[1] = 57;
+ state->Init_Ctrl[2].bit[1] = 0;
+ state->Init_Ctrl[2].val[1] = 1;
+
+ state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ;
+ state->Init_Ctrl[3].size = 1 ;
+ state->Init_Ctrl[3].addr[0] = 53;
+ state->Init_Ctrl[3].bit[0] = 0;
+ state->Init_Ctrl[3].val[0] = 0;
+
+ state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ;
+ state->Init_Ctrl[4].size = 3 ;
+ state->Init_Ctrl[4].addr[0] = 53;
+ state->Init_Ctrl[4].bit[0] = 5;
+ state->Init_Ctrl[4].val[0] = 0;
+ state->Init_Ctrl[4].addr[1] = 53;
+ state->Init_Ctrl[4].bit[1] = 6;
+ state->Init_Ctrl[4].val[1] = 0;
+ state->Init_Ctrl[4].addr[2] = 53;
+ state->Init_Ctrl[4].bit[2] = 7;
+ state->Init_Ctrl[4].val[2] = 1;
+
+ state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ;
+ state->Init_Ctrl[5].size = 1 ;
+ state->Init_Ctrl[5].addr[0] = 59;
+ state->Init_Ctrl[5].bit[0] = 0;
+ state->Init_Ctrl[5].val[0] = 0;
+
+ state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ;
+ state->Init_Ctrl[6].size = 2 ;
+ state->Init_Ctrl[6].addr[0] = 53;
+ state->Init_Ctrl[6].bit[0] = 3;
+ state->Init_Ctrl[6].val[0] = 0;
+ state->Init_Ctrl[6].addr[1] = 53;
+ state->Init_Ctrl[6].bit[1] = 4;
+ state->Init_Ctrl[6].val[1] = 1;
+
+ state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ;
+ state->Init_Ctrl[7].size = 4 ;
+ state->Init_Ctrl[7].addr[0] = 22;
+ state->Init_Ctrl[7].bit[0] = 4;
+ state->Init_Ctrl[7].val[0] = 0;
+ state->Init_Ctrl[7].addr[1] = 22;
+ state->Init_Ctrl[7].bit[1] = 5;
+ state->Init_Ctrl[7].val[1] = 1;
+ state->Init_Ctrl[7].addr[2] = 22;
+ state->Init_Ctrl[7].bit[2] = 6;
+ state->Init_Ctrl[7].val[2] = 1;
+ state->Init_Ctrl[7].addr[3] = 22;
+ state->Init_Ctrl[7].bit[3] = 7;
+ state->Init_Ctrl[7].val[3] = 0;
+
+ state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ;
+ state->Init_Ctrl[8].size = 1 ;
+ state->Init_Ctrl[8].addr[0] = 22;
+ state->Init_Ctrl[8].bit[0] = 2;
+ state->Init_Ctrl[8].val[0] = 0;
+
+ state->Init_Ctrl[9].Ctrl_Num = AGC_IF ;
+ state->Init_Ctrl[9].size = 4 ;
+ state->Init_Ctrl[9].addr[0] = 76;
+ state->Init_Ctrl[9].bit[0] = 0;
+ state->Init_Ctrl[9].val[0] = 1;
+ state->Init_Ctrl[9].addr[1] = 76;
+ state->Init_Ctrl[9].bit[1] = 1;
+ state->Init_Ctrl[9].val[1] = 1;
+ state->Init_Ctrl[9].addr[2] = 76;
+ state->Init_Ctrl[9].bit[2] = 2;
+ state->Init_Ctrl[9].val[2] = 0;
+ state->Init_Ctrl[9].addr[3] = 76;
+ state->Init_Ctrl[9].bit[3] = 3;
+ state->Init_Ctrl[9].val[3] = 1;
+
+ state->Init_Ctrl[10].Ctrl_Num = AGC_RF ;
+ state->Init_Ctrl[10].size = 4 ;
+ state->Init_Ctrl[10].addr[0] = 76;
+ state->Init_Ctrl[10].bit[0] = 4;
+ state->Init_Ctrl[10].val[0] = 1;
+ state->Init_Ctrl[10].addr[1] = 76;
+ state->Init_Ctrl[10].bit[1] = 5;
+ state->Init_Ctrl[10].val[1] = 1;
+ state->Init_Ctrl[10].addr[2] = 76;
+ state->Init_Ctrl[10].bit[2] = 6;
+ state->Init_Ctrl[10].val[2] = 0;
+ state->Init_Ctrl[10].addr[3] = 76;
+ state->Init_Ctrl[10].bit[3] = 7;
+ state->Init_Ctrl[10].val[3] = 1;
+
+ state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ;
+ state->Init_Ctrl[11].size = 5 ;
+ state->Init_Ctrl[11].addr[0] = 43;
+ state->Init_Ctrl[11].bit[0] = 3;
+ state->Init_Ctrl[11].val[0] = 0;
+ state->Init_Ctrl[11].addr[1] = 43;
+ state->Init_Ctrl[11].bit[1] = 4;
+ state->Init_Ctrl[11].val[1] = 0;
+ state->Init_Ctrl[11].addr[2] = 43;
+ state->Init_Ctrl[11].bit[2] = 5;
+ state->Init_Ctrl[11].val[2] = 0;
+ state->Init_Ctrl[11].addr[3] = 43;
+ state->Init_Ctrl[11].bit[3] = 6;
+ state->Init_Ctrl[11].val[3] = 1;
+ state->Init_Ctrl[11].addr[4] = 43;
+ state->Init_Ctrl[11].bit[4] = 7;
+ state->Init_Ctrl[11].val[4] = 0;
+
+ state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ;
+ state->Init_Ctrl[12].size = 6 ;
+ state->Init_Ctrl[12].addr[0] = 44;
+ state->Init_Ctrl[12].bit[0] = 2;
+ state->Init_Ctrl[12].val[0] = 0;
+ state->Init_Ctrl[12].addr[1] = 44;
+ state->Init_Ctrl[12].bit[1] = 3;
+ state->Init_Ctrl[12].val[1] = 0;
+ state->Init_Ctrl[12].addr[2] = 44;
+ state->Init_Ctrl[12].bit[2] = 4;
+ state->Init_Ctrl[12].val[2] = 0;
+ state->Init_Ctrl[12].addr[3] = 44;
+ state->Init_Ctrl[12].bit[3] = 5;
+ state->Init_Ctrl[12].val[3] = 1;
+ state->Init_Ctrl[12].addr[4] = 44;
+ state->Init_Ctrl[12].bit[4] = 6;
+ state->Init_Ctrl[12].val[4] = 0;
+ state->Init_Ctrl[12].addr[5] = 44;
+ state->Init_Ctrl[12].bit[5] = 7;
+ state->Init_Ctrl[12].val[5] = 0;
+
+ state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ;
+ state->Init_Ctrl[13].size = 7 ;
+ state->Init_Ctrl[13].addr[0] = 11;
+ state->Init_Ctrl[13].bit[0] = 0;
+ state->Init_Ctrl[13].val[0] = 1;
+ state->Init_Ctrl[13].addr[1] = 11;
+ state->Init_Ctrl[13].bit[1] = 1;
+ state->Init_Ctrl[13].val[1] = 0;
+ state->Init_Ctrl[13].addr[2] = 11;
+ state->Init_Ctrl[13].bit[2] = 2;
+ state->Init_Ctrl[13].val[2] = 0;
+ state->Init_Ctrl[13].addr[3] = 11;
+ state->Init_Ctrl[13].bit[3] = 3;
+ state->Init_Ctrl[13].val[3] = 1;
+ state->Init_Ctrl[13].addr[4] = 11;
+ state->Init_Ctrl[13].bit[4] = 4;
+ state->Init_Ctrl[13].val[4] = 1;
+ state->Init_Ctrl[13].addr[5] = 11;
+ state->Init_Ctrl[13].bit[5] = 5;
+ state->Init_Ctrl[13].val[5] = 0;
+ state->Init_Ctrl[13].addr[6] = 11;
+ state->Init_Ctrl[13].bit[6] = 6;
+ state->Init_Ctrl[13].val[6] = 0;
+
+ state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ;
+ state->Init_Ctrl[14].size = 16 ;
+ state->Init_Ctrl[14].addr[0] = 13;
+ state->Init_Ctrl[14].bit[0] = 0;
+ state->Init_Ctrl[14].val[0] = 0;
+ state->Init_Ctrl[14].addr[1] = 13;
+ state->Init_Ctrl[14].bit[1] = 1;
+ state->Init_Ctrl[14].val[1] = 0;
+ state->Init_Ctrl[14].addr[2] = 13;
+ state->Init_Ctrl[14].bit[2] = 2;
+ state->Init_Ctrl[14].val[2] = 0;
+ state->Init_Ctrl[14].addr[3] = 13;
+ state->Init_Ctrl[14].bit[3] = 3;
+ state->Init_Ctrl[14].val[3] = 0;
+ state->Init_Ctrl[14].addr[4] = 13;
+ state->Init_Ctrl[14].bit[4] = 4;
+ state->Init_Ctrl[14].val[4] = 0;
+ state->Init_Ctrl[14].addr[5] = 13;
+ state->Init_Ctrl[14].bit[5] = 5;
+ state->Init_Ctrl[14].val[5] = 0;
+ state->Init_Ctrl[14].addr[6] = 13;
+ state->Init_Ctrl[14].bit[6] = 6;
+ state->Init_Ctrl[14].val[6] = 0;
+ state->Init_Ctrl[14].addr[7] = 13;
+ state->Init_Ctrl[14].bit[7] = 7;
+ state->Init_Ctrl[14].val[7] = 0;
+ state->Init_Ctrl[14].addr[8] = 12;
+ state->Init_Ctrl[14].bit[8] = 0;
+ state->Init_Ctrl[14].val[8] = 0;
+ state->Init_Ctrl[14].addr[9] = 12;
+ state->Init_Ctrl[14].bit[9] = 1;
+ state->Init_Ctrl[14].val[9] = 0;
+ state->Init_Ctrl[14].addr[10] = 12;
+ state->Init_Ctrl[14].bit[10] = 2;
+ state->Init_Ctrl[14].val[10] = 0;
+ state->Init_Ctrl[14].addr[11] = 12;
+ state->Init_Ctrl[14].bit[11] = 3;
+ state->Init_Ctrl[14].val[11] = 0;
+ state->Init_Ctrl[14].addr[12] = 12;
+ state->Init_Ctrl[14].bit[12] = 4;
+ state->Init_Ctrl[14].val[12] = 0;
+ state->Init_Ctrl[14].addr[13] = 12;
+ state->Init_Ctrl[14].bit[13] = 5;
+ state->Init_Ctrl[14].val[13] = 1;
+ state->Init_Ctrl[14].addr[14] = 12;
+ state->Init_Ctrl[14].bit[14] = 6;
+ state->Init_Ctrl[14].val[14] = 1;
+ state->Init_Ctrl[14].addr[15] = 12;
+ state->Init_Ctrl[14].bit[15] = 7;
+ state->Init_Ctrl[14].val[15] = 0;
+
+ state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ;
+ state->Init_Ctrl[15].size = 3 ;
+ state->Init_Ctrl[15].addr[0] = 147;
+ state->Init_Ctrl[15].bit[0] = 2;
+ state->Init_Ctrl[15].val[0] = 0;
+ state->Init_Ctrl[15].addr[1] = 147;
+ state->Init_Ctrl[15].bit[1] = 3;
+ state->Init_Ctrl[15].val[1] = 1;
+ state->Init_Ctrl[15].addr[2] = 147;
+ state->Init_Ctrl[15].bit[2] = 4;
+ state->Init_Ctrl[15].val[2] = 1;
+
+ state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ;
+ state->Init_Ctrl[16].size = 2 ;
+ state->Init_Ctrl[16].addr[0] = 147;
+ state->Init_Ctrl[16].bit[0] = 0;
+ state->Init_Ctrl[16].val[0] = 0;
+ state->Init_Ctrl[16].addr[1] = 147;
+ state->Init_Ctrl[16].bit[1] = 1;
+ state->Init_Ctrl[16].val[1] = 1;
+
+ state->Init_Ctrl[17].Ctrl_Num = EN_AAF ;
+ state->Init_Ctrl[17].size = 1 ;
+ state->Init_Ctrl[17].addr[0] = 147;
+ state->Init_Ctrl[17].bit[0] = 7;
+ state->Init_Ctrl[17].val[0] = 0;
+
+ state->Init_Ctrl[18].Ctrl_Num = EN_3P ;
+ state->Init_Ctrl[18].size = 1 ;
+ state->Init_Ctrl[18].addr[0] = 147;
+ state->Init_Ctrl[18].bit[0] = 6;
+ state->Init_Ctrl[18].val[0] = 0;
+
+ state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ;
+ state->Init_Ctrl[19].size = 1 ;
+ state->Init_Ctrl[19].addr[0] = 156;
+ state->Init_Ctrl[19].bit[0] = 0;
+ state->Init_Ctrl[19].val[0] = 0;
+
+ state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ;
+ state->Init_Ctrl[20].size = 1 ;
+ state->Init_Ctrl[20].addr[0] = 147;
+ state->Init_Ctrl[20].bit[0] = 5;
+ state->Init_Ctrl[20].val[0] = 0;
+
+ state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ;
+ state->Init_Ctrl[21].size = 1 ;
+ state->Init_Ctrl[21].addr[0] = 137;
+ state->Init_Ctrl[21].bit[0] = 4;
+ state->Init_Ctrl[21].val[0] = 0;
+
+ state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ;
+ state->Init_Ctrl[22].size = 1 ;
+ state->Init_Ctrl[22].addr[0] = 137;
+ state->Init_Ctrl[22].bit[0] = 7;
+ state->Init_Ctrl[22].val[0] = 0;
+
+ state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ;
+ state->Init_Ctrl[23].size = 1 ;
+ state->Init_Ctrl[23].addr[0] = 91;
+ state->Init_Ctrl[23].bit[0] = 5;
+ state->Init_Ctrl[23].val[0] = 1;
+
+ state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ;
+ state->Init_Ctrl[24].size = 1 ;
+ state->Init_Ctrl[24].addr[0] = 43;
+ state->Init_Ctrl[24].bit[0] = 0;
+ state->Init_Ctrl[24].val[0] = 1;
+
+ state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ;
+ state->Init_Ctrl[25].size = 2 ;
+ state->Init_Ctrl[25].addr[0] = 22;
+ state->Init_Ctrl[25].bit[0] = 0;
+ state->Init_Ctrl[25].val[0] = 1;
+ state->Init_Ctrl[25].addr[1] = 22;
+ state->Init_Ctrl[25].bit[1] = 1;
+ state->Init_Ctrl[25].val[1] = 1;
+
+ state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ;
+ state->Init_Ctrl[26].size = 1 ;
+ state->Init_Ctrl[26].addr[0] = 134;
+ state->Init_Ctrl[26].bit[0] = 2;
+ state->Init_Ctrl[26].val[0] = 0;
+
+ state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ;
+ state->Init_Ctrl[27].size = 1 ;
+ state->Init_Ctrl[27].addr[0] = 137;
+ state->Init_Ctrl[27].bit[0] = 3;
+ state->Init_Ctrl[27].val[0] = 0;
+
+ state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ;
+ state->Init_Ctrl[28].size = 1 ;
+ state->Init_Ctrl[28].addr[0] = 77;
+ state->Init_Ctrl[28].bit[0] = 7;
+ state->Init_Ctrl[28].val[0] = 0;
+
+ state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ;
+ state->Init_Ctrl[29].size = 1 ;
+ state->Init_Ctrl[29].addr[0] = 166;
+ state->Init_Ctrl[29].bit[0] = 7;
+ state->Init_Ctrl[29].val[0] = 1;
+
+ state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ;
+ state->Init_Ctrl[30].size = 3 ;
+ state->Init_Ctrl[30].addr[0] = 166;
+ state->Init_Ctrl[30].bit[0] = 0;
+ state->Init_Ctrl[30].val[0] = 0;
+ state->Init_Ctrl[30].addr[1] = 166;
+ state->Init_Ctrl[30].bit[1] = 1;
+ state->Init_Ctrl[30].val[1] = 1;
+ state->Init_Ctrl[30].addr[2] = 166;
+ state->Init_Ctrl[30].bit[2] = 2;
+ state->Init_Ctrl[30].val[2] = 1;
+
+ state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ;
+ state->Init_Ctrl[31].size = 3 ;
+ state->Init_Ctrl[31].addr[0] = 166;
+ state->Init_Ctrl[31].bit[0] = 3;
+ state->Init_Ctrl[31].val[0] = 1;
+ state->Init_Ctrl[31].addr[1] = 166;
+ state->Init_Ctrl[31].bit[1] = 4;
+ state->Init_Ctrl[31].val[1] = 0;
+ state->Init_Ctrl[31].addr[2] = 166;
+ state->Init_Ctrl[31].bit[2] = 5;
+ state->Init_Ctrl[31].val[2] = 1;
+
+ state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ;
+ state->Init_Ctrl[32].size = 3 ;
+ state->Init_Ctrl[32].addr[0] = 167;
+ state->Init_Ctrl[32].bit[0] = 0;
+ state->Init_Ctrl[32].val[0] = 1;
+ state->Init_Ctrl[32].addr[1] = 167;
+ state->Init_Ctrl[32].bit[1] = 1;
+ state->Init_Ctrl[32].val[1] = 1;
+ state->Init_Ctrl[32].addr[2] = 167;
+ state->Init_Ctrl[32].bit[2] = 2;
+ state->Init_Ctrl[32].val[2] = 0;
+
+ state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ;
+ state->Init_Ctrl[33].size = 4 ;
+ state->Init_Ctrl[33].addr[0] = 168;
+ state->Init_Ctrl[33].bit[0] = 0;
+ state->Init_Ctrl[33].val[0] = 0;
+ state->Init_Ctrl[33].addr[1] = 168;
+ state->Init_Ctrl[33].bit[1] = 1;
+ state->Init_Ctrl[33].val[1] = 1;
+ state->Init_Ctrl[33].addr[2] = 168;
+ state->Init_Ctrl[33].bit[2] = 2;
+ state->Init_Ctrl[33].val[2] = 0;
+ state->Init_Ctrl[33].addr[3] = 168;
+ state->Init_Ctrl[33].bit[3] = 3;
+ state->Init_Ctrl[33].val[3] = 0;
+
+ state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ;
+ state->Init_Ctrl[34].size = 4 ;
+ state->Init_Ctrl[34].addr[0] = 168;
+ state->Init_Ctrl[34].bit[0] = 4;
+ state->Init_Ctrl[34].val[0] = 1;
+ state->Init_Ctrl[34].addr[1] = 168;
+ state->Init_Ctrl[34].bit[1] = 5;
+ state->Init_Ctrl[34].val[1] = 1;
+ state->Init_Ctrl[34].addr[2] = 168;
+ state->Init_Ctrl[34].bit[2] = 6;
+ state->Init_Ctrl[34].val[2] = 1;
+ state->Init_Ctrl[34].addr[3] = 168;
+ state->Init_Ctrl[34].bit[3] = 7;
+ state->Init_Ctrl[34].val[3] = 1;
+
+ state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ;
+ state->Init_Ctrl[35].size = 1 ;
+ state->Init_Ctrl[35].addr[0] = 135;
+ state->Init_Ctrl[35].bit[0] = 0;
+ state->Init_Ctrl[35].val[0] = 0;
+
+ state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ;
+ state->Init_Ctrl[36].size = 1 ;
+ state->Init_Ctrl[36].addr[0] = 56;
+ state->Init_Ctrl[36].bit[0] = 3;
+ state->Init_Ctrl[36].val[0] = 0;
+
+ state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ;
+ state->Init_Ctrl[37].size = 7 ;
+ state->Init_Ctrl[37].addr[0] = 59;
+ state->Init_Ctrl[37].bit[0] = 1;
+ state->Init_Ctrl[37].val[0] = 0;
+ state->Init_Ctrl[37].addr[1] = 59;
+ state->Init_Ctrl[37].bit[1] = 2;
+ state->Init_Ctrl[37].val[1] = 0;
+ state->Init_Ctrl[37].addr[2] = 59;
+ state->Init_Ctrl[37].bit[2] = 3;
+ state->Init_Ctrl[37].val[2] = 0;
+ state->Init_Ctrl[37].addr[3] = 59;
+ state->Init_Ctrl[37].bit[3] = 4;
+ state->Init_Ctrl[37].val[3] = 0;
+ state->Init_Ctrl[37].addr[4] = 59;
+ state->Init_Ctrl[37].bit[4] = 5;
+ state->Init_Ctrl[37].val[4] = 0;
+ state->Init_Ctrl[37].addr[5] = 59;
+ state->Init_Ctrl[37].bit[5] = 6;
+ state->Init_Ctrl[37].val[5] = 0;
+ state->Init_Ctrl[37].addr[6] = 59;
+ state->Init_Ctrl[37].bit[6] = 7;
+ state->Init_Ctrl[37].val[6] = 0;
+
+ state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ;
+ state->Init_Ctrl[38].size = 6 ;
+ state->Init_Ctrl[38].addr[0] = 32;
+ state->Init_Ctrl[38].bit[0] = 2;
+ state->Init_Ctrl[38].val[0] = 0;
+ state->Init_Ctrl[38].addr[1] = 32;
+ state->Init_Ctrl[38].bit[1] = 3;
+ state->Init_Ctrl[38].val[1] = 0;
+ state->Init_Ctrl[38].addr[2] = 32;
+ state->Init_Ctrl[38].bit[2] = 4;
+ state->Init_Ctrl[38].val[2] = 0;
+ state->Init_Ctrl[38].addr[3] = 32;
+ state->Init_Ctrl[38].bit[3] = 5;
+ state->Init_Ctrl[38].val[3] = 0;
+ state->Init_Ctrl[38].addr[4] = 32;
+ state->Init_Ctrl[38].bit[4] = 6;
+ state->Init_Ctrl[38].val[4] = 1;
+ state->Init_Ctrl[38].addr[5] = 32;
+ state->Init_Ctrl[38].bit[5] = 7;
+ state->Init_Ctrl[38].val[5] = 0;
+
+ state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ;
+ state->Init_Ctrl[39].size = 1 ;
+ state->Init_Ctrl[39].addr[0] = 25;
+ state->Init_Ctrl[39].bit[0] = 3;
+ state->Init_Ctrl[39].val[0] = 1;
+
+
+ state->CH_Ctrl_Num = CHCTRL_NUM ;
+
+ state->CH_Ctrl[0].Ctrl_Num = DN_POLY ;
+ state->CH_Ctrl[0].size = 2 ;
+ state->CH_Ctrl[0].addr[0] = 68;
+ state->CH_Ctrl[0].bit[0] = 6;
+ state->CH_Ctrl[0].val[0] = 1;
+ state->CH_Ctrl[0].addr[1] = 68;
+ state->CH_Ctrl[0].bit[1] = 7;
+ state->CH_Ctrl[0].val[1] = 1;
+
+ state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ;
+ state->CH_Ctrl[1].size = 2 ;
+ state->CH_Ctrl[1].addr[0] = 70;
+ state->CH_Ctrl[1].bit[0] = 6;
+ state->CH_Ctrl[1].val[0] = 1;
+ state->CH_Ctrl[1].addr[1] = 70;
+ state->CH_Ctrl[1].bit[1] = 7;
+ state->CH_Ctrl[1].val[1] = 0;
+
+ state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ;
+ state->CH_Ctrl[2].size = 9 ;
+ state->CH_Ctrl[2].addr[0] = 69;
+ state->CH_Ctrl[2].bit[0] = 5;
+ state->CH_Ctrl[2].val[0] = 0;
+ state->CH_Ctrl[2].addr[1] = 69;
+ state->CH_Ctrl[2].bit[1] = 6;
+ state->CH_Ctrl[2].val[1] = 0;
+ state->CH_Ctrl[2].addr[2] = 69;
+ state->CH_Ctrl[2].bit[2] = 7;
+ state->CH_Ctrl[2].val[2] = 0;
+ state->CH_Ctrl[2].addr[3] = 68;
+ state->CH_Ctrl[2].bit[3] = 0;
+ state->CH_Ctrl[2].val[3] = 0;
+ state->CH_Ctrl[2].addr[4] = 68;
+ state->CH_Ctrl[2].bit[4] = 1;
+ state->CH_Ctrl[2].val[4] = 0;
+ state->CH_Ctrl[2].addr[5] = 68;
+ state->CH_Ctrl[2].bit[5] = 2;
+ state->CH_Ctrl[2].val[5] = 0;
+ state->CH_Ctrl[2].addr[6] = 68;
+ state->CH_Ctrl[2].bit[6] = 3;
+ state->CH_Ctrl[2].val[6] = 0;
+ state->CH_Ctrl[2].addr[7] = 68;
+ state->CH_Ctrl[2].bit[7] = 4;
+ state->CH_Ctrl[2].val[7] = 0;
+ state->CH_Ctrl[2].addr[8] = 68;
+ state->CH_Ctrl[2].bit[8] = 5;
+ state->CH_Ctrl[2].val[8] = 0;
+
+ state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ;
+ state->CH_Ctrl[3].size = 1 ;
+ state->CH_Ctrl[3].addr[0] = 70;
+ state->CH_Ctrl[3].bit[0] = 5;
+ state->CH_Ctrl[3].val[0] = 0;
+
+ state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ;
+ state->CH_Ctrl[4].size = 3 ;
+ state->CH_Ctrl[4].addr[0] = 73;
+ state->CH_Ctrl[4].bit[0] = 4;
+ state->CH_Ctrl[4].val[0] = 0;
+ state->CH_Ctrl[4].addr[1] = 73;
+ state->CH_Ctrl[4].bit[1] = 5;
+ state->CH_Ctrl[4].val[1] = 1;
+ state->CH_Ctrl[4].addr[2] = 73;
+ state->CH_Ctrl[4].bit[2] = 6;
+ state->CH_Ctrl[4].val[2] = 0;
+
+ state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ;
+ state->CH_Ctrl[5].size = 4 ;
+ state->CH_Ctrl[5].addr[0] = 70;
+ state->CH_Ctrl[5].bit[0] = 0;
+ state->CH_Ctrl[5].val[0] = 0;
+ state->CH_Ctrl[5].addr[1] = 70;
+ state->CH_Ctrl[5].bit[1] = 1;
+ state->CH_Ctrl[5].val[1] = 0;
+ state->CH_Ctrl[5].addr[2] = 70;
+ state->CH_Ctrl[5].bit[2] = 2;
+ state->CH_Ctrl[5].val[2] = 0;
+ state->CH_Ctrl[5].addr[3] = 70;
+ state->CH_Ctrl[5].bit[3] = 3;
+ state->CH_Ctrl[5].val[3] = 0;
+
+ state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ;
+ state->CH_Ctrl[6].size = 1 ;
+ state->CH_Ctrl[6].addr[0] = 70;
+ state->CH_Ctrl[6].bit[0] = 4;
+ state->CH_Ctrl[6].val[0] = 1;
+
+ state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ;
+ state->CH_Ctrl[7].size = 1 ;
+ state->CH_Ctrl[7].addr[0] = 111;
+ state->CH_Ctrl[7].bit[0] = 4;
+ state->CH_Ctrl[7].val[0] = 0;
+
+ state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ;
+ state->CH_Ctrl[8].size = 1 ;
+ state->CH_Ctrl[8].addr[0] = 111;
+ state->CH_Ctrl[8].bit[0] = 7;
+ state->CH_Ctrl[8].val[0] = 1;
+
+ state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ;
+ state->CH_Ctrl[9].size = 1 ;
+ state->CH_Ctrl[9].addr[0] = 111;
+ state->CH_Ctrl[9].bit[0] = 6;
+ state->CH_Ctrl[9].val[0] = 1;
+
+ state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ;
+ state->CH_Ctrl[10].size = 1 ;
+ state->CH_Ctrl[10].addr[0] = 111;
+ state->CH_Ctrl[10].bit[0] = 5;
+ state->CH_Ctrl[10].val[0] = 0;
+
+ state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ;
+ state->CH_Ctrl[11].size = 2 ;
+ state->CH_Ctrl[11].addr[0] = 110;
+ state->CH_Ctrl[11].bit[0] = 0;
+ state->CH_Ctrl[11].val[0] = 1;
+ state->CH_Ctrl[11].addr[1] = 110;
+ state->CH_Ctrl[11].bit[1] = 1;
+ state->CH_Ctrl[11].val[1] = 0;
+
+ state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ;
+ state->CH_Ctrl[12].size = 3 ;
+ state->CH_Ctrl[12].addr[0] = 69;
+ state->CH_Ctrl[12].bit[0] = 2;
+ state->CH_Ctrl[12].val[0] = 0;
+ state->CH_Ctrl[12].addr[1] = 69;
+ state->CH_Ctrl[12].bit[1] = 3;
+ state->CH_Ctrl[12].val[1] = 0;
+ state->CH_Ctrl[12].addr[2] = 69;
+ state->CH_Ctrl[12].bit[2] = 4;
+ state->CH_Ctrl[12].val[2] = 0;
+
+ state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ;
+ state->CH_Ctrl[13].size = 6 ;
+ state->CH_Ctrl[13].addr[0] = 110;
+ state->CH_Ctrl[13].bit[0] = 2;
+ state->CH_Ctrl[13].val[0] = 0;
+ state->CH_Ctrl[13].addr[1] = 110;
+ state->CH_Ctrl[13].bit[1] = 3;
+ state->CH_Ctrl[13].val[1] = 0;
+ state->CH_Ctrl[13].addr[2] = 110;
+ state->CH_Ctrl[13].bit[2] = 4;
+ state->CH_Ctrl[13].val[2] = 0;
+ state->CH_Ctrl[13].addr[3] = 110;
+ state->CH_Ctrl[13].bit[3] = 5;
+ state->CH_Ctrl[13].val[3] = 0;
+ state->CH_Ctrl[13].addr[4] = 110;
+ state->CH_Ctrl[13].bit[4] = 6;
+ state->CH_Ctrl[13].val[4] = 0;
+ state->CH_Ctrl[13].addr[5] = 110;
+ state->CH_Ctrl[13].bit[5] = 7;
+ state->CH_Ctrl[13].val[5] = 1;
+
+ state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ;
+ state->CH_Ctrl[14].size = 7 ;
+ state->CH_Ctrl[14].addr[0] = 14;
+ state->CH_Ctrl[14].bit[0] = 0;
+ state->CH_Ctrl[14].val[0] = 0;
+ state->CH_Ctrl[14].addr[1] = 14;
+ state->CH_Ctrl[14].bit[1] = 1;
+ state->CH_Ctrl[14].val[1] = 0;
+ state->CH_Ctrl[14].addr[2] = 14;
+ state->CH_Ctrl[14].bit[2] = 2;
+ state->CH_Ctrl[14].val[2] = 0;
+ state->CH_Ctrl[14].addr[3] = 14;
+ state->CH_Ctrl[14].bit[3] = 3;
+ state->CH_Ctrl[14].val[3] = 0;
+ state->CH_Ctrl[14].addr[4] = 14;
+ state->CH_Ctrl[14].bit[4] = 4;
+ state->CH_Ctrl[14].val[4] = 0;
+ state->CH_Ctrl[14].addr[5] = 14;
+ state->CH_Ctrl[14].bit[5] = 5;
+ state->CH_Ctrl[14].val[5] = 0;
+ state->CH_Ctrl[14].addr[6] = 14;
+ state->CH_Ctrl[14].bit[6] = 6;
+ state->CH_Ctrl[14].val[6] = 0;
+
+ state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ;
+ state->CH_Ctrl[15].size = 18 ;
+ state->CH_Ctrl[15].addr[0] = 17;
+ state->CH_Ctrl[15].bit[0] = 6;
+ state->CH_Ctrl[15].val[0] = 0;
+ state->CH_Ctrl[15].addr[1] = 17;
+ state->CH_Ctrl[15].bit[1] = 7;
+ state->CH_Ctrl[15].val[1] = 0;
+ state->CH_Ctrl[15].addr[2] = 16;
+ state->CH_Ctrl[15].bit[2] = 0;
+ state->CH_Ctrl[15].val[2] = 0;
+ state->CH_Ctrl[15].addr[3] = 16;
+ state->CH_Ctrl[15].bit[3] = 1;
+ state->CH_Ctrl[15].val[3] = 0;
+ state->CH_Ctrl[15].addr[4] = 16;
+ state->CH_Ctrl[15].bit[4] = 2;
+ state->CH_Ctrl[15].val[4] = 0;
+ state->CH_Ctrl[15].addr[5] = 16;
+ state->CH_Ctrl[15].bit[5] = 3;
+ state->CH_Ctrl[15].val[5] = 0;
+ state->CH_Ctrl[15].addr[6] = 16;
+ state->CH_Ctrl[15].bit[6] = 4;
+ state->CH_Ctrl[15].val[6] = 0;
+ state->CH_Ctrl[15].addr[7] = 16;
+ state->CH_Ctrl[15].bit[7] = 5;
+ state->CH_Ctrl[15].val[7] = 0;
+ state->CH_Ctrl[15].addr[8] = 16;
+ state->CH_Ctrl[15].bit[8] = 6;
+ state->CH_Ctrl[15].val[8] = 0;
+ state->CH_Ctrl[15].addr[9] = 16;
+ state->CH_Ctrl[15].bit[9] = 7;
+ state->CH_Ctrl[15].val[9] = 0;
+ state->CH_Ctrl[15].addr[10] = 15;
+ state->CH_Ctrl[15].bit[10] = 0;
+ state->CH_Ctrl[15].val[10] = 0;
+ state->CH_Ctrl[15].addr[11] = 15;
+ state->CH_Ctrl[15].bit[11] = 1;
+ state->CH_Ctrl[15].val[11] = 0;
+ state->CH_Ctrl[15].addr[12] = 15;
+ state->CH_Ctrl[15].bit[12] = 2;
+ state->CH_Ctrl[15].val[12] = 0;
+ state->CH_Ctrl[15].addr[13] = 15;
+ state->CH_Ctrl[15].bit[13] = 3;
+ state->CH_Ctrl[15].val[13] = 0;
+ state->CH_Ctrl[15].addr[14] = 15;
+ state->CH_Ctrl[15].bit[14] = 4;
+ state->CH_Ctrl[15].val[14] = 0;
+ state->CH_Ctrl[15].addr[15] = 15;
+ state->CH_Ctrl[15].bit[15] = 5;
+ state->CH_Ctrl[15].val[15] = 0;
+ state->CH_Ctrl[15].addr[16] = 15;
+ state->CH_Ctrl[15].bit[16] = 6;
+ state->CH_Ctrl[15].val[16] = 1;
+ state->CH_Ctrl[15].addr[17] = 15;
+ state->CH_Ctrl[15].bit[17] = 7;
+ state->CH_Ctrl[15].val[17] = 1;
+
+ state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ;
+ state->CH_Ctrl[16].size = 5 ;
+ state->CH_Ctrl[16].addr[0] = 112;
+ state->CH_Ctrl[16].bit[0] = 0;
+ state->CH_Ctrl[16].val[0] = 0;
+ state->CH_Ctrl[16].addr[1] = 112;
+ state->CH_Ctrl[16].bit[1] = 1;
+ state->CH_Ctrl[16].val[1] = 0;
+ state->CH_Ctrl[16].addr[2] = 112;
+ state->CH_Ctrl[16].bit[2] = 2;
+ state->CH_Ctrl[16].val[2] = 0;
+ state->CH_Ctrl[16].addr[3] = 112;
+ state->CH_Ctrl[16].bit[3] = 3;
+ state->CH_Ctrl[16].val[3] = 0;
+ state->CH_Ctrl[16].addr[4] = 112;
+ state->CH_Ctrl[16].bit[4] = 4;
+ state->CH_Ctrl[16].val[4] = 1;
+
+ state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ;
+ state->CH_Ctrl[17].size = 1 ;
+ state->CH_Ctrl[17].addr[0] = 14;
+ state->CH_Ctrl[17].bit[0] = 7;
+ state->CH_Ctrl[17].val[0] = 0;
+
+ state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ;
+ state->CH_Ctrl[18].size = 4 ;
+ state->CH_Ctrl[18].addr[0] = 107;
+ state->CH_Ctrl[18].bit[0] = 3;
+ state->CH_Ctrl[18].val[0] = 0;
+ state->CH_Ctrl[18].addr[1] = 107;
+ state->CH_Ctrl[18].bit[1] = 4;
+ state->CH_Ctrl[18].val[1] = 0;
+ state->CH_Ctrl[18].addr[2] = 107;
+ state->CH_Ctrl[18].bit[2] = 5;
+ state->CH_Ctrl[18].val[2] = 0;
+ state->CH_Ctrl[18].addr[3] = 107;
+ state->CH_Ctrl[18].bit[3] = 6;
+ state->CH_Ctrl[18].val[3] = 0;
+
+ state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ;
+ state->CH_Ctrl[19].size = 3 ;
+ state->CH_Ctrl[19].addr[0] = 107;
+ state->CH_Ctrl[19].bit[0] = 7;
+ state->CH_Ctrl[19].val[0] = 1;
+ state->CH_Ctrl[19].addr[1] = 106;
+ state->CH_Ctrl[19].bit[1] = 0;
+ state->CH_Ctrl[19].val[1] = 1;
+ state->CH_Ctrl[19].addr[2] = 106;
+ state->CH_Ctrl[19].bit[2] = 1;
+ state->CH_Ctrl[19].val[2] = 1;
+
+ state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ;
+ state->CH_Ctrl[20].size = 11 ;
+ state->CH_Ctrl[20].addr[0] = 109;
+ state->CH_Ctrl[20].bit[0] = 2;
+ state->CH_Ctrl[20].val[0] = 0;
+ state->CH_Ctrl[20].addr[1] = 109;
+ state->CH_Ctrl[20].bit[1] = 3;
+ state->CH_Ctrl[20].val[1] = 0;
+ state->CH_Ctrl[20].addr[2] = 109;
+ state->CH_Ctrl[20].bit[2] = 4;
+ state->CH_Ctrl[20].val[2] = 0;
+ state->CH_Ctrl[20].addr[3] = 109;
+ state->CH_Ctrl[20].bit[3] = 5;
+ state->CH_Ctrl[20].val[3] = 0;
+ state->CH_Ctrl[20].addr[4] = 109;
+ state->CH_Ctrl[20].bit[4] = 6;
+ state->CH_Ctrl[20].val[4] = 0;
+ state->CH_Ctrl[20].addr[5] = 109;
+ state->CH_Ctrl[20].bit[5] = 7;
+ state->CH_Ctrl[20].val[5] = 0;
+ state->CH_Ctrl[20].addr[6] = 108;
+ state->CH_Ctrl[20].bit[6] = 0;
+ state->CH_Ctrl[20].val[6] = 0;
+ state->CH_Ctrl[20].addr[7] = 108;
+ state->CH_Ctrl[20].bit[7] = 1;
+ state->CH_Ctrl[20].val[7] = 0;
+ state->CH_Ctrl[20].addr[8] = 108;
+ state->CH_Ctrl[20].bit[8] = 2;
+ state->CH_Ctrl[20].val[8] = 1;
+ state->CH_Ctrl[20].addr[9] = 108;
+ state->CH_Ctrl[20].bit[9] = 3;
+ state->CH_Ctrl[20].val[9] = 1;
+ state->CH_Ctrl[20].addr[10] = 108;
+ state->CH_Ctrl[20].bit[10] = 4;
+ state->CH_Ctrl[20].val[10] = 1;
+
+ state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ;
+ state->CH_Ctrl[21].size = 6 ;
+ state->CH_Ctrl[21].addr[0] = 106;
+ state->CH_Ctrl[21].bit[0] = 2;
+ state->CH_Ctrl[21].val[0] = 0;
+ state->CH_Ctrl[21].addr[1] = 106;
+ state->CH_Ctrl[21].bit[1] = 3;
+ state->CH_Ctrl[21].val[1] = 0;
+ state->CH_Ctrl[21].addr[2] = 106;
+ state->CH_Ctrl[21].bit[2] = 4;
+ state->CH_Ctrl[21].val[2] = 0;
+ state->CH_Ctrl[21].addr[3] = 106;
+ state->CH_Ctrl[21].bit[3] = 5;
+ state->CH_Ctrl[21].val[3] = 0;
+ state->CH_Ctrl[21].addr[4] = 106;
+ state->CH_Ctrl[21].bit[4] = 6;
+ state->CH_Ctrl[21].val[4] = 0;
+ state->CH_Ctrl[21].addr[5] = 106;
+ state->CH_Ctrl[21].bit[5] = 7;
+ state->CH_Ctrl[21].val[5] = 1;
+
+ state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ;
+ state->CH_Ctrl[22].size = 1 ;
+ state->CH_Ctrl[22].addr[0] = 138;
+ state->CH_Ctrl[22].bit[0] = 4;
+ state->CH_Ctrl[22].val[0] = 1;
+
+ state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ;
+ state->CH_Ctrl[23].size = 1 ;
+ state->CH_Ctrl[23].addr[0] = 17;
+ state->CH_Ctrl[23].bit[0] = 5;
+ state->CH_Ctrl[23].val[0] = 0;
+
+ state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ;
+ state->CH_Ctrl[24].size = 1 ;
+ state->CH_Ctrl[24].addr[0] = 111;
+ state->CH_Ctrl[24].bit[0] = 3;
+ state->CH_Ctrl[24].val[0] = 0;
+
+ state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ;
+ state->CH_Ctrl[25].size = 1 ;
+ state->CH_Ctrl[25].addr[0] = 112;
+ state->CH_Ctrl[25].bit[0] = 7;
+ state->CH_Ctrl[25].val[0] = 0;
+
+ state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ;
+ state->CH_Ctrl[26].size = 1 ;
+ state->CH_Ctrl[26].addr[0] = 136;
+ state->CH_Ctrl[26].bit[0] = 7;
+ state->CH_Ctrl[26].val[0] = 0;
+
+ state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ;
+ state->CH_Ctrl[27].size = 1 ;
+ state->CH_Ctrl[27].addr[0] = 149;
+ state->CH_Ctrl[27].bit[0] = 7;
+ state->CH_Ctrl[27].val[0] = 0;
+
+ state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ;
+ state->CH_Ctrl[28].size = 1 ;
+ state->CH_Ctrl[28].addr[0] = 149;
+ state->CH_Ctrl[28].bit[0] = 6;
+ state->CH_Ctrl[28].val[0] = 0;
+
+ state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ;
+ state->CH_Ctrl[29].size = 1 ;
+ state->CH_Ctrl[29].addr[0] = 149;
+ state->CH_Ctrl[29].bit[0] = 5;
+ state->CH_Ctrl[29].val[0] = 1;
+
+ state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ;
+ state->CH_Ctrl[30].size = 1 ;
+ state->CH_Ctrl[30].addr[0] = 149;
+ state->CH_Ctrl[30].bit[0] = 4;
+ state->CH_Ctrl[30].val[0] = 1;
+
+ state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ;
+ state->CH_Ctrl[31].size = 1 ;
+ state->CH_Ctrl[31].addr[0] = 149;
+ state->CH_Ctrl[31].bit[0] = 3;
+ state->CH_Ctrl[31].val[0] = 0;
+
+ state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ;
+ state->CH_Ctrl[32].size = 1 ;
+ state->CH_Ctrl[32].addr[0] = 93;
+ state->CH_Ctrl[32].bit[0] = 1;
+ state->CH_Ctrl[32].val[0] = 0;
+
+ state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ;
+ state->CH_Ctrl[33].size = 1 ;
+ state->CH_Ctrl[33].addr[0] = 93;
+ state->CH_Ctrl[33].bit[0] = 0;
+ state->CH_Ctrl[33].val[0] = 0;
+
+ state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ;
+ state->CH_Ctrl[34].size = 6 ;
+ state->CH_Ctrl[34].addr[0] = 92;
+ state->CH_Ctrl[34].bit[0] = 2;
+ state->CH_Ctrl[34].val[0] = 0;
+ state->CH_Ctrl[34].addr[1] = 92;
+ state->CH_Ctrl[34].bit[1] = 3;
+ state->CH_Ctrl[34].val[1] = 0;
+ state->CH_Ctrl[34].addr[2] = 92;
+ state->CH_Ctrl[34].bit[2] = 4;
+ state->CH_Ctrl[34].val[2] = 0;
+ state->CH_Ctrl[34].addr[3] = 92;
+ state->CH_Ctrl[34].bit[3] = 5;
+ state->CH_Ctrl[34].val[3] = 0;
+ state->CH_Ctrl[34].addr[4] = 92;
+ state->CH_Ctrl[34].bit[4] = 6;
+ state->CH_Ctrl[34].val[4] = 0;
+ state->CH_Ctrl[34].addr[5] = 92;
+ state->CH_Ctrl[34].bit[5] = 7;
+ state->CH_Ctrl[34].val[5] = 0;
+
+ state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ;
+ state->CH_Ctrl[35].size = 6 ;
+ state->CH_Ctrl[35].addr[0] = 93;
+ state->CH_Ctrl[35].bit[0] = 2;
+ state->CH_Ctrl[35].val[0] = 0;
+ state->CH_Ctrl[35].addr[1] = 93;
+ state->CH_Ctrl[35].bit[1] = 3;
+ state->CH_Ctrl[35].val[1] = 0;
+ state->CH_Ctrl[35].addr[2] = 93;
+ state->CH_Ctrl[35].bit[2] = 4;
+ state->CH_Ctrl[35].val[2] = 0;
+ state->CH_Ctrl[35].addr[3] = 93;
+ state->CH_Ctrl[35].bit[3] = 5;
+ state->CH_Ctrl[35].val[3] = 0;
+ state->CH_Ctrl[35].addr[4] = 93;
+ state->CH_Ctrl[35].bit[4] = 6;
+ state->CH_Ctrl[35].val[4] = 0;
+ state->CH_Ctrl[35].addr[5] = 93;
+ state->CH_Ctrl[35].bit[5] = 7;
+ state->CH_Ctrl[35].val[5] = 0;
+
+#ifdef _MXL_PRODUCTION
+ state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ;
+ state->CH_Ctrl[36].size = 1 ;
+ state->CH_Ctrl[36].addr[0] = 109;
+ state->CH_Ctrl[36].bit[0] = 1;
+ state->CH_Ctrl[36].val[0] = 1;
+
+ state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ;
+ state->CH_Ctrl[37].size = 2 ;
+ state->CH_Ctrl[37].addr[0] = 112;
+ state->CH_Ctrl[37].bit[0] = 5;
+ state->CH_Ctrl[37].val[0] = 0;
+ state->CH_Ctrl[37].addr[1] = 112;
+ state->CH_Ctrl[37].bit[1] = 6;
+ state->CH_Ctrl[37].val[1] = 0;
+
+ state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ;
+ state->CH_Ctrl[38].size = 1 ;
+ state->CH_Ctrl[38].addr[0] = 65;
+ state->CH_Ctrl[38].bit[0] = 1;
+ state->CH_Ctrl[38].val[0] = 0;
+#endif
+
+ return 0 ;
+}
+
+static void InitTunerControls(struct dvb_frontend *fe)
+{
+ MXL5005_RegisterInit(fe);
+ MXL5005_ControlInit(fe);
+#ifdef _MXL_INTERNAL
+ MXL5005_MXLControlInit(fe);
+#endif
+}
+
+static u16 MXL5005_TunerConfig(struct dvb_frontend *fe,
+ u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */
+ u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */
+ u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */
+ u32 IF_out, /* Desired IF Out Frequency */
+ u32 Fxtal, /* XTAL Frequency */
+ u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */
+ u16 TOP, /* 0: Dual AGC; Value: take over point */
+ u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */
+ u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */
+ u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */
+ u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */
+ u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */
+
+ /* Modulation Type; */
+ /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */
+ u8 Mod_Type,
+
+ /* Tracking Filter */
+ /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */
+ u8 TF_Type
+ )
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0;
+
+ state->Mode = Mode;
+ state->IF_Mode = IF_mode;
+ state->Chan_Bandwidth = Bandwidth;
+ state->IF_OUT = IF_out;
+ state->Fxtal = Fxtal;
+ state->AGC_Mode = AGC_Mode;
+ state->TOP = TOP;
+ state->IF_OUT_LOAD = IF_OUT_LOAD;
+ state->CLOCK_OUT = CLOCK_OUT;
+ state->DIV_OUT = DIV_OUT;
+ state->CAPSELECT = CAPSELECT;
+ state->EN_RSSI = EN_RSSI;
+ state->Mod_Type = Mod_Type;
+ state->TF_Type = TF_Type;
+
+ /* Initialize all the controls and registers */
+ InitTunerControls(fe);
+
+ /* Synthesizer LO frequency calculation */
+ MXL_SynthIFLO_Calc(fe);
+
+ return status;
+}
+
+static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ if (state->Mode == 1) /* Digital Mode */
+ state->IF_LO = state->IF_OUT;
+ else /* Analog Mode */ {
+ if (state->IF_Mode == 0) /* Analog Zero IF mode */
+ state->IF_LO = state->IF_OUT + 400000;
+ else /* Analog Low IF mode */
+ state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2;
+ }
+}
+
+static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+
+ if (state->Mode == 1) /* Digital Mode */ {
+ /* remove 20.48MHz setting for 2.6.10 */
+ state->RF_LO = state->RF_IN;
+ /* change for 2.6.6 */
+ state->TG_LO = state->RF_IN - 750000;
+ } else /* Analog Mode */ {
+ if (state->IF_Mode == 0) /* Analog Zero IF mode */ {
+ state->RF_LO = state->RF_IN - 400000;
+ state->TG_LO = state->RF_IN - 1750000;
+ } else /* Analog Low IF mode */ {
+ state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2;
+ state->TG_LO = state->RF_IN -
+ state->Chan_Bandwidth + 500000;
+ }
+ }
+}
+
+static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe)
+{
+ u16 status = 0;
+
+ status += MXL_ControlWrite(fe, OVERRIDE_1, 1);
+ status += MXL_ControlWrite(fe, OVERRIDE_2, 1);
+ status += MXL_ControlWrite(fe, OVERRIDE_3, 1);
+ status += MXL_ControlWrite(fe, OVERRIDE_4, 1);
+
+ return status;
+}
+
+static u16 MXL_BlockInit(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0;
+
+ status += MXL_OverwriteICDefault(fe);
+
+ /* Downconverter Control Dig Ana */
+ status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0);
+
+ /* Filter Control Dig Ana */
+ status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1);
+ status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2);
+ status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0);
+ status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1);
+ status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0);
+
+ /* Initialize Low-Pass Filter */
+ if (state->Mode) { /* Digital Mode */
+ switch (state->Chan_Bandwidth) {
+ case 8000000:
+ status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0);
+ break;
+ case 7000000:
+ status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2);
+ break;
+ case 6000000:
+ status += MXL_ControlWrite(fe,
+ BB_DLPF_BANDSEL, 3);
+ break;
+ }
+ } else { /* Analog Mode */
+ switch (state->Chan_Bandwidth) {
+ case 8000000: /* Low Zero */
+ status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
+ (state->IF_Mode ? 0 : 3));
+ break;
+ case 7000000:
+ status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
+ (state->IF_Mode ? 1 : 4));
+ break;
+ case 6000000:
+ status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT,
+ (state->IF_Mode ? 2 : 5));
+ break;
+ }
+ }
+
+ /* Charge Pump Control Dig Ana */
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8);
+ status += MXL_ControlWrite(fe,
+ RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1);
+ status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0);
+
+ /* AGC TOP Control */
+ if (state->AGC_Mode == 0) /* Dual AGC */ {
+ status += MXL_ControlWrite(fe, AGC_IF, 15);
+ status += MXL_ControlWrite(fe, AGC_RF, 15);
+ } else /* Single AGC Mode Dig Ana */
+ status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12);
+
+ if (state->TOP == 55) /* TOP == 5.5 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x0);
+
+ if (state->TOP == 72) /* TOP == 7.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x1);
+
+ if (state->TOP == 92) /* TOP == 9.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x2);
+
+ if (state->TOP == 110) /* TOP == 11.0 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x3);
+
+ if (state->TOP == 129) /* TOP == 12.9 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x4);
+
+ if (state->TOP == 147) /* TOP == 14.7 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x5);
+
+ if (state->TOP == 168) /* TOP == 16.8 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x6);
+
+ if (state->TOP == 194) /* TOP == 19.4 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x7);
+
+ if (state->TOP == 212) /* TOP == 21.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0x9);
+
+ if (state->TOP == 232) /* TOP == 23.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xA);
+
+ if (state->TOP == 252) /* TOP == 25.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xB);
+
+ if (state->TOP == 271) /* TOP == 27.1 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xC);
+
+ if (state->TOP == 292) /* TOP == 29.2 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xD);
+
+ if (state->TOP == 317) /* TOP == 31.7 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xE);
+
+ if (state->TOP == 349) /* TOP == 34.9 */
+ status += MXL_ControlWrite(fe, AGC_IF, 0xF);
+
+ /* IF Synthesizer Control */
+ status += MXL_IFSynthInit(fe);
+
+ /* IF UpConverter Control */
+ if (state->IF_OUT_LOAD == 200) {
+ status += MXL_ControlWrite(fe, DRV_RES_SEL, 6);
+ status += MXL_ControlWrite(fe, I_DRIVER, 2);
+ }
+ if (state->IF_OUT_LOAD == 300) {
+ status += MXL_ControlWrite(fe, DRV_RES_SEL, 4);
+ status += MXL_ControlWrite(fe, I_DRIVER, 1);
+ }
+
+ /* Anti-Alias Filtering Control
+ * initialise Anti-Aliasing Filter
+ */
+ if (state->Mode) { /* Digital Mode */
+ if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) {
+ status += MXL_ControlWrite(fe, EN_AAF, 1);
+ status += MXL_ControlWrite(fe, EN_3P, 1);
+ status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
+ status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
+ }
+ if ((state->IF_OUT == 36125000UL) ||
+ (state->IF_OUT == 36150000UL)) {
+ status += MXL_ControlWrite(fe, EN_AAF, 1);
+ status += MXL_ControlWrite(fe, EN_3P, 1);
+ status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
+ status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1);
+ }
+ if (state->IF_OUT > 36150000UL) {
+ status += MXL_ControlWrite(fe, EN_AAF, 0);
+ status += MXL_ControlWrite(fe, EN_3P, 1);
+ status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
+ status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1);
+ }
+ } else { /* Analog Mode */
+ if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) {
+ status += MXL_ControlWrite(fe, EN_AAF, 1);
+ status += MXL_ControlWrite(fe, EN_3P, 1);
+ status += MXL_ControlWrite(fe, EN_AUX_3P, 1);
+ status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
+ }
+ if (state->IF_OUT > 5000000UL) {
+ status += MXL_ControlWrite(fe, EN_AAF, 0);
+ status += MXL_ControlWrite(fe, EN_3P, 0);
+ status += MXL_ControlWrite(fe, EN_AUX_3P, 0);
+ status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0);
+ }
+ }
+
+ /* Demod Clock Out */
+ if (state->CLOCK_OUT)
+ status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1);
+ else
+ status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0);
+
+ if (state->DIV_OUT == 1)
+ status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1);
+ if (state->DIV_OUT == 0)
+ status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0);
+
+ /* Crystal Control */
+ if (state->CAPSELECT)
+ status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1);
+ else
+ status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0);
+
+ if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL)
+ status += MXL_ControlWrite(fe, IF_SEL_DBL, 1);
+ if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL)
+ status += MXL_ControlWrite(fe, IF_SEL_DBL, 0);
+
+ if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL)
+ status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3);
+ if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL)
+ status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0);
+
+ /* Misc Controls */
+ if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */
+ status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0);
+ else
+ status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1);
+
+ /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */
+
+ /* Set TG_R_DIV */
+ status += MXL_ControlWrite(fe, TG_R_DIV,
+ MXL_Ceiling(state->Fxtal, 1000000));
+
+ /* Apply Default value to BB_INITSTATE_DLPF_TUNE */
+
+ /* RSSI Control */
+ if (state->EN_RSSI) {
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
+
+ /* TOP point */
+ status += MXL_ControlWrite(fe, RFA_FLR, 0);
+ status += MXL_ControlWrite(fe, RFA_CEIL, 12);
+ }
+
+ /* Modulation type bit settings
+ * Override the control values preset
+ */
+ if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ {
+ state->AGC_Mode = 1; /* Single AGC Mode */
+
+ /* Enable RSSI */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
+
+ /* TOP point */
+ status += MXL_ControlWrite(fe, RFA_FLR, 2);
+ status += MXL_ControlWrite(fe, RFA_CEIL, 13);
+ if (state->IF_OUT <= 6280000UL) /* Low IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
+ else /* High IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+
+ }
+ if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ {
+ state->AGC_Mode = 1; /* Single AGC Mode */
+
+ /* Enable RSSI */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1);
+
+ /* TOP point */
+ status += MXL_ControlWrite(fe, RFA_FLR, 2);
+ status += MXL_ControlWrite(fe, RFA_CEIL, 13);
+ status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1);
+ /* Low Zero */
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5);
+
+ if (state->IF_OUT <= 6280000UL) /* Low IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
+ else /* High IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+ }
+ if (state->Mod_Type == MXL_QAM) /* QAM Mode */ {
+ state->Mode = MXL_DIGITAL_MODE;
+
+ /* state->AGC_Mode = 1; */ /* Single AGC Mode */
+
+ /* Disable RSSI */ /* change here for v2.6.5 */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
+ /* change here for v2.6.5 */
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
+
+ if (state->IF_OUT <= 6280000UL) /* Low IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 0);
+ else /* High IF */
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
+
+ }
+ if (state->Mod_Type == MXL_ANALOG_CABLE) {
+ /* Analog Cable Mode */
+ /* state->Mode = MXL_DIGITAL_MODE; */
+
+ state->AGC_Mode = 1; /* Single AGC Mode */
+
+ /* Disable RSSI */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+ /* change for 2.6.3 */
+ status += MXL_ControlWrite(fe, AGC_IF, 1);
+ status += MXL_ControlWrite(fe, AGC_RF, 15);
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+ }
+
+ if (state->Mod_Type == MXL_ANALOG_OTA) {
+ /* Analog OTA Terrestrial mode add for 2.6.7 */
+ /* state->Mode = MXL_ANALOG_MODE; */
+
+ /* Enable RSSI */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
+ status += MXL_ControlWrite(fe, BB_IQSWAP, 1);
+ }
+
+ /* RSSI disable */
+ if (state->EN_RSSI == 0) {
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+ }
+
+ return status;
+}
+
+static u16 MXL_IFSynthInit(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0 ;
+ u32 Fref = 0 ;
+ u32 Kdbl, intModVal ;
+ u32 fracModVal ;
+ Kdbl = 2 ;
+
+ if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL)
+ Kdbl = 2 ;
+ if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL)
+ Kdbl = 1 ;
+
+ /* IF Synthesizer Control */
+ if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ {
+ if (state->IF_LO == 41000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 328000000UL ;
+ }
+ if (state->IF_LO == 47000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 376000000UL ;
+ }
+ if (state->IF_LO == 54000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 324000000UL ;
+ }
+ if (state->IF_LO == 60000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 39250000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 314000000UL ;
+ }
+ if (state->IF_LO == 39650000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 317200000UL ;
+ }
+ if (state->IF_LO == 40150000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 321200000UL ;
+ }
+ if (state->IF_LO == 40650000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 325200000UL ;
+ }
+ }
+
+ if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) {
+ if (state->IF_LO == 57000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 342000000UL ;
+ }
+ if (state->IF_LO == 44000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 352000000UL ;
+ }
+ if (state->IF_LO == 43750000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 350000000UL ;
+ }
+ if (state->IF_LO == 36650000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 366500000UL ;
+ }
+ if (state->IF_LO == 36150000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 361500000UL ;
+ }
+ if (state->IF_LO == 36000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 35250000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 352500000UL ;
+ }
+ if (state->IF_LO == 34750000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 347500000UL ;
+ }
+ if (state->IF_LO == 6280000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 376800000UL ;
+ }
+ if (state->IF_LO == 5000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 4500000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 4570000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 365600000UL ;
+ }
+ if (state->IF_LO == 4000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 57400000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 344400000UL ;
+ }
+ if (state->IF_LO == 44400000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 355200000UL ;
+ }
+ if (state->IF_LO == 44150000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 353200000UL ;
+ }
+ if (state->IF_LO == 37050000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 370500000UL ;
+ }
+ if (state->IF_LO == 36550000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 365500000UL ;
+ }
+ if (state->IF_LO == 36125000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 361250000UL ;
+ }
+ if (state->IF_LO == 6000000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 360000000UL ;
+ }
+ if (state->IF_LO == 5400000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 324000000UL ;
+ }
+ if (state->IF_LO == 5380000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C);
+ Fref = 322800000UL ;
+ }
+ if (state->IF_LO == 5200000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 374400000UL ;
+ }
+ if (state->IF_LO == 4900000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 352800000UL ;
+ }
+ if (state->IF_LO == 4400000UL) {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 352000000UL ;
+ }
+ if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ {
+ status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05);
+ status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08);
+ Fref = 365670000UL ;
+ }
+ }
+ /* CHCAL_INT_MOD_IF */
+ /* CHCAL_FRAC_MOD_IF */
+ intModVal = Fref / (state->Fxtal * Kdbl/2);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal);
+
+ fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) *
+ intModVal);
+
+ fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000);
+ status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal);
+
+ return status ;
+}
+
+static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0;
+ u32 divider_val, E3, E4, E5, E5A;
+ u32 Fmax, Fmin, FmaxBin, FminBin;
+ u32 Kdbl_RF = 2;
+ u32 tg_divval;
+ u32 tg_lo;
+
+ u32 Fref_TG;
+ u32 Fvco;
+
+ state->RF_IN = RF_Freq;
+
+ MXL_SynthRFTGLO_Calc(fe);
+
+ if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL)
+ Kdbl_RF = 2;
+ if (state->Fxtal > 22000000 && state->Fxtal <= 32000000)
+ Kdbl_RF = 1;
+
+ /* Downconverter Controls
+ * Look-Up Table Implementation for:
+ * DN_POLY
+ * DN_RFGAIN
+ * DN_CAP_RFLPF
+ * DN_EN_VHFUHFBAR
+ * DN_GAIN_ADJUST
+ * Change the boundary reference from RF_IN to RF_LO
+ */
+ if (state->RF_LO < 40000000UL)
+ return -1;
+
+ if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 2);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1);
+ }
+ if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1);
+ }
+ if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2);
+ }
+ if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2);
+ }
+ if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 3);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
+ }
+ if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 1);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
+ }
+ if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) {
+ status += MXL_ControlWrite(fe, DN_POLY, 3);
+ status += MXL_ControlWrite(fe, DN_RFGAIN, 2);
+ status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0);
+ status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0);
+ status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3);
+ }
+ if (state->RF_LO > 900000000UL)
+ return -1;
+
+ /* DN_IQTNBUF_AMP */
+ /* DN_IQTNGNBFBIAS_BST */
+ if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0);
+ }
+ if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1);
+ }
+ if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) {
+ status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10);
+ status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1);
+ }
+
+ /*
+ * Set RF Synth and LO Path Control
+ *
+ * Look-Up table implementation for:
+ * RFSYN_EN_OUTMUX
+ * RFSYN_SEL_VCO_OUT
+ * RFSYN_SEL_VCO_HI
+ * RFSYN_SEL_DIVM
+ * RFSYN_RF_DIV_BIAS
+ * DN_SEL_FREQ
+ *
+ * Set divider_val, Fmax, Fmix to use in Equations
+ */
+ FminBin = 28000000UL ;
+ FmaxBin = 42500000UL ;
+ if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
+ divider_val = 64 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 42500000UL ;
+ FmaxBin = 56000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
+ divider_val = 64 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 56000000UL ;
+ FmaxBin = 85000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
+ divider_val = 32 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 85000000UL ;
+ FmaxBin = 112000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1);
+ divider_val = 32 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 112000000UL ;
+ FmaxBin = 170000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2);
+ divider_val = 16 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 170000000UL ;
+ FmaxBin = 225000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2);
+ divider_val = 16 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 225000000UL ;
+ FmaxBin = 300000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4);
+ divider_val = 8 ;
+ Fmax = 340000000UL ;
+ Fmin = FminBin ;
+ }
+ FminBin = 300000000UL ;
+ FmaxBin = 340000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ divider_val = 8 ;
+ Fmax = FmaxBin ;
+ Fmin = 225000000UL ;
+ }
+ FminBin = 340000000UL ;
+ FmaxBin = 450000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ divider_val = 8 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 450000000UL ;
+ FmaxBin = 680000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ divider_val = 4 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 680000000UL ;
+ FmaxBin = 900000000UL ;
+ if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ divider_val = 4 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+
+ /* CHCAL_INT_MOD_RF
+ * CHCAL_FRAC_MOD_RF
+ * RFSYN_LPF_R
+ * CHCAL_EN_INT_RF
+ */
+ /* Equation E3 RFSYN_VCO_BIAS */
+ E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ;
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3);
+
+ /* Equation E4 CHCAL_INT_MOD_RF */
+ E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000);
+ MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4);
+
+ /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */
+ E5 = ((2<<17)*(state->RF_LO/10000*divider_val -
+ (E4*(2*state->Fxtal*Kdbl_RF)/10000))) /
+ (2*state->Fxtal*Kdbl_RF/10000);
+
+ status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5);
+
+ /* Equation E5A RFSYN_LPF_R */
+ E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ;
+ status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A);
+
+ /* Euqation E5B CHCAL_EN_INIT_RF */
+ status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0));
+ /*if (E5 == 0)
+ * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1);
+ *else
+ * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5);
+ */
+
+ /*
+ * Set TG Synth
+ *
+ * Look-Up table implementation for:
+ * TG_LO_DIVVAL
+ * TG_LO_SELVAL
+ *
+ * Set divider_val, Fmax, Fmix to use in Equations
+ */
+ if (state->TG_LO < 33000000UL)
+ return -1;
+
+ FminBin = 33000000UL ;
+ FmaxBin = 50000000UL ;
+ if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0);
+ divider_val = 36 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 50000000UL ;
+ FmaxBin = 67000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0);
+ divider_val = 24 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 67000000UL ;
+ FmaxBin = 100000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
+ divider_val = 18 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 100000000UL ;
+ FmaxBin = 150000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
+ divider_val = 12 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 150000000UL ;
+ FmaxBin = 200000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2);
+ divider_val = 8 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 200000000UL ;
+ FmaxBin = 300000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3);
+ divider_val = 6 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 300000000UL ;
+ FmaxBin = 400000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3);
+ divider_val = 4 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 400000000UL ;
+ FmaxBin = 600000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7);
+ divider_val = 3 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+ FminBin = 600000000UL ;
+ FmaxBin = 900000000UL ;
+ if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) {
+ status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0);
+ status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7);
+ divider_val = 2 ;
+ Fmax = FmaxBin ;
+ Fmin = FminBin ;
+ }
+
+ /* TG_DIV_VAL */
+ tg_divval = (state->TG_LO*divider_val/100000) *
+ (MXL_Ceiling(state->Fxtal, 1000000) * 100) /
+ (state->Fxtal/1000);
+
+ status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval);
+
+ if (state->TG_LO > 600000000UL)
+ status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1);
+
+ Fmax = 1800000000UL ;
+ Fmin = 1200000000UL ;
+
+ /* prevent overflow of 32 bit unsigned integer, use
+ * following equation. Edit for v2.6.4
+ */
+ /* Fref_TF = Fref_TG * 1000 */
+ Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000);
+
+ /* Fvco = Fvco/10 */
+ Fvco = (state->TG_LO/10000) * divider_val * Fref_TG;
+
+ tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8;
+
+ /* below equation is same as above but much harder to debug.
+ *
+ * static u32 MXL_GetXtalInt(u32 Xtal_Freq)
+ * {
+ * if ((Xtal_Freq % 1000000) == 0)
+ * return (Xtal_Freq / 10000);
+ * else
+ * return (((Xtal_Freq / 1000000) + 1)*100);
+ * }
+ *
+ * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal);
+ * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) -
+ * ((state->TG_LO/10000)*divider_val *
+ * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 *
+ * Xtal_Int/100) + 8;
+ */
+
+ status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo);
+
+ /* add for 2.6.5 Special setting for QAM */
+ if (state->Mod_Type == MXL_QAM) {
+ if (state->config->qam_gain != 0)
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN,
+ state->config->qam_gain);
+ else if (state->RF_IN < 680000000)
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
+ else
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2);
+ }
+
+ /* Off Chip Tracking Filter Control */
+ if (state->TF_Type == MXL_TF_OFF) {
+ /* Tracking Filter Off State; turn off all the banks */
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */
+ status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */
+ status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */
+ }
+
+ if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ }
+ if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ }
+ if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ }
+ if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 0);
+ }
+ if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 29);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 0);
+ }
+ if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 0);
+ }
+ if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 16);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ }
+ if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 7);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ }
+ if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_C_H) {
+
+ /* Tracking Filter type C-H for Hauppauge only */
+ status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ }
+ if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ }
+ if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ }
+ if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ }
+ if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ }
+ if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ }
+ if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ }
+ if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ }
+ if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */
+
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_D_L) {
+
+ /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */
+ status += MXL_ControlWrite(fe, DAC_DIN_A, 0);
+
+ /* if UHF and terrestrial => Turn off Tracking Filter */
+ if (state->RF_IN >= 471000000 &&
+ (state->RF_IN - 471000000)%6000000 != 0) {
+ /* Turn off all the banks */
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_ControlWrite(fe, AGC_IF, 10);
+ } else {
+ /* if VHF or cable => Turn on Tracking Filter */
+ if (state->RF_IN >= 43000000 &&
+ state->RF_IN < 140000000) {
+
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 140000000 &&
+ state->RF_IN < 240000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 240000000 &&
+ state->RF_IN < 340000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 340000000 &&
+ state->RF_IN < 430000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 430000000 &&
+ state->RF_IN < 470000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 470000000 &&
+ state->RF_IN < 570000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 570000000 &&
+ state->RF_IN < 620000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 620000000 &&
+ state->RF_IN < 760000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 760000000 &&
+ state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ {
+
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_F) {
+
+ /* Tracking Filter type F */
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_E_2) {
+
+ /* Tracking Filter type E_2 */
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_G) {
+
+ /* Tracking Filter type G add for v2.6.8 */
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) {
+
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+
+ if (state->TF_Type == MXL_TF_E_NA) {
+
+ /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */
+ status += MXL_ControlWrite(fe, DAC_DIN_B, 0);
+
+ /* if UHF and terrestrial=> Turn off Tracking Filter */
+ if (state->RF_IN >= 471000000 &&
+ (state->RF_IN - 471000000)%6000000 != 0) {
+
+ /* Turn off all the banks */
+ status += MXL_SetGPIO(fe, 3, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+
+ /* 2.6.12 Turn on RSSI */
+ status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1);
+ status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1);
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1);
+ status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1);
+
+ /* RSSI reference point */
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3);
+ status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2);
+
+ /* following parameter is from analog OTA mode,
+ * can be change to seek better performance */
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3);
+ } else {
+ /* if VHF or Cable => Turn on Tracking Filter */
+
+ /* 2.6.12 Turn off RSSI */
+ status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0);
+
+ /* change back from above condition */
+ status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5);
+
+
+ if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) {
+
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 0);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 0);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 0);
+ }
+ if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) {
+ status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1);
+ status += MXL_SetGPIO(fe, 4, 1);
+ status += MXL_SetGPIO(fe, 1, 1);
+ status += MXL_SetGPIO(fe, 3, 1);
+ }
+ }
+ }
+ return status ;
+}
+
+static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val)
+{
+ u16 status = 0;
+
+ if (GPIO_Num == 1)
+ status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1);
+
+ /* GPIO2 is not available */
+
+ if (GPIO_Num == 3) {
+ if (GPIO_Val == 1) {
+ status += MXL_ControlWrite(fe, GPIO_3, 0);
+ status += MXL_ControlWrite(fe, GPIO_3B, 0);
+ }
+ if (GPIO_Val == 0) {
+ status += MXL_ControlWrite(fe, GPIO_3, 1);
+ status += MXL_ControlWrite(fe, GPIO_3B, 1);
+ }
+ if (GPIO_Val == 3) { /* tri-state */
+ status += MXL_ControlWrite(fe, GPIO_3, 0);
+ status += MXL_ControlWrite(fe, GPIO_3B, 1);
+ }
+ }
+ if (GPIO_Num == 4) {
+ if (GPIO_Val == 1) {
+ status += MXL_ControlWrite(fe, GPIO_4, 0);
+ status += MXL_ControlWrite(fe, GPIO_4B, 0);
+ }
+ if (GPIO_Val == 0) {
+ status += MXL_ControlWrite(fe, GPIO_4, 1);
+ status += MXL_ControlWrite(fe, GPIO_4B, 1);
+ }
+ if (GPIO_Val == 3) { /* tri-state */
+ status += MXL_ControlWrite(fe, GPIO_4, 0);
+ status += MXL_ControlWrite(fe, GPIO_4B, 1);
+ }
+ }
+
+ return status;
+}
+
+static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value)
+{
+ u16 status = 0;
+
+ /* Will write ALL Matching Control Name */
+ /* Write Matching INIT Control */
+ status += MXL_ControlWrite_Group(fe, ControlNum, value, 1);
+ /* Write Matching CH Control */
+ status += MXL_ControlWrite_Group(fe, ControlNum, value, 2);
+#ifdef _MXL_INTERNAL
+ /* Write Matching MXL Control */
+ status += MXL_ControlWrite_Group(fe, ControlNum, value, 3);
+#endif
+ return status;
+}
+
+static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum,
+ u32 value, u16 controlGroup)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 i, j, k;
+ u32 highLimit;
+ u32 ctrlVal;
+
+ if (controlGroup == 1) /* Initial Control */ {
+
+ for (i = 0; i < state->Init_Ctrl_Num; i++) {
+
+ if (controlNum == state->Init_Ctrl[i].Ctrl_Num) {
+
+ highLimit = 1 << state->Init_Ctrl[i].size;
+ if (value < highLimit) {
+ for (j = 0; j < state->Init_Ctrl[i].size; j++) {
+ state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
+ MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]),
+ (u8)(state->Init_Ctrl[i].bit[j]),
+ (u8)((value>>j) & 0x01));
+ }
+ ctrlVal = 0;
+ for (k = 0; k < state->Init_Ctrl[i].size; k++)
+ ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k);
+ } else
+ return -1;
+ }
+ }
+ }
+ if (controlGroup == 2) /* Chan change Control */ {
+
+ for (i = 0; i < state->CH_Ctrl_Num; i++) {
+
+ if (controlNum == state->CH_Ctrl[i].Ctrl_Num) {
+
+ highLimit = 1 << state->CH_Ctrl[i].size;
+ if (value < highLimit) {
+ for (j = 0; j < state->CH_Ctrl[i].size; j++) {
+ state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
+ MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]),
+ (u8)(state->CH_Ctrl[i].bit[j]),
+ (u8)((value>>j) & 0x01));
+ }
+ ctrlVal = 0;
+ for (k = 0; k < state->CH_Ctrl[i].size; k++)
+ ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k);
+ } else
+ return -1;
+ }
+ }
+ }
+#ifdef _MXL_INTERNAL
+ if (controlGroup == 3) /* Maxlinear Control */ {
+
+ for (i = 0; i < state->MXL_Ctrl_Num; i++) {
+
+ if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) {
+
+ highLimit = (1 << state->MXL_Ctrl[i].size);
+ if (value < highLimit) {
+ for (j = 0; j < state->MXL_Ctrl[i].size; j++) {
+ state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01);
+ MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]),
+ (u8)(state->MXL_Ctrl[i].bit[j]),
+ (u8)((value>>j) & 0x01));
+ }
+ ctrlVal = 0;
+ for (k = 0; k < state->MXL_Ctrl[i].size; k++)
+ ctrlVal += state->
+ MXL_Ctrl[i].val[k] *
+ (1 << k);
+ } else
+ return -1;
+ }
+ }
+ }
+#endif
+ return 0 ; /* successful return */
+}
+
+static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ int i ;
+
+ for (i = 0; i < 104; i++) {
+ if (RegNum == state->TunerRegs[i].Reg_Num) {
+ *RegVal = (u8)(state->TunerRegs[i].Reg_Val);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u32 ctrlVal ;
+ u16 i, k ;
+
+ for (i = 0; i < state->Init_Ctrl_Num ; i++) {
+
+ if (controlNum == state->Init_Ctrl[i].Ctrl_Num) {
+
+ ctrlVal = 0;
+ for (k = 0; k < state->Init_Ctrl[i].size; k++)
+ ctrlVal += state->Init_Ctrl[i].val[k] * (1<<k);
+ *value = ctrlVal;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < state->CH_Ctrl_Num ; i++) {
+
+ if (controlNum == state->CH_Ctrl[i].Ctrl_Num) {
+
+ ctrlVal = 0;
+ for (k = 0; k < state->CH_Ctrl[i].size; k++)
+ ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k);
+ *value = ctrlVal;
+ return 0;
+
+ }
+ }
+
+#ifdef _MXL_INTERNAL
+ for (i = 0; i < state->MXL_Ctrl_Num ; i++) {
+
+ if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) {
+
+ ctrlVal = 0;
+ for (k = 0; k < state->MXL_Ctrl[i].size; k++)
+ ctrlVal += state->MXL_Ctrl[i].val[k] * (1<<k);
+ *value = ctrlVal;
+ return 0;
+
+ }
+ }
+#endif
+ return 1;
+}
+
+static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit,
+ u8 bitVal)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ int i ;
+
+ const u8 AND_MAP[8] = {
+ 0xFE, 0xFD, 0xFB, 0xF7,
+ 0xEF, 0xDF, 0xBF, 0x7F } ;
+
+ const u8 OR_MAP[8] = {
+ 0x01, 0x02, 0x04, 0x08,
+ 0x10, 0x20, 0x40, 0x80 } ;
+
+ for (i = 0; i < state->TunerRegs_Num; i++) {
+ if (state->TunerRegs[i].Reg_Num == address) {
+ if (bitVal)
+ state->TunerRegs[i].Reg_Val |= OR_MAP[bit];
+ else
+ state->TunerRegs[i].Reg_Val &= AND_MAP[bit];
+ break ;
+ }
+ }
+}
+
+static u32 MXL_Ceiling(u32 value, u32 resolution)
+{
+ return value / resolution + (value % resolution > 0 ? 1 : 0);
+}
+
+/* Retrieve the Initialzation Registers */
+static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum,
+ u8 *RegVal, int *count)
+{
+ u16 status = 0;
+ int i ;
+
+ u8 RegAddr[] = {
+ 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73,
+ 76, 77, 91, 134, 135, 137, 147,
+ 156, 166, 167, 168, 25 };
+
+ *count = ARRAY_SIZE(RegAddr);
+
+ status += MXL_BlockInit(fe);
+
+ for (i = 0 ; i < *count; i++) {
+ RegNum[i] = RegAddr[i];
+ status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
+ }
+
+ return status;
+}
+
+static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal,
+ int *count)
+{
+ u16 status = 0;
+ int i ;
+
+/* add 77, 166, 167, 168 register for 2.6.12 */
+#ifdef _MXL_PRODUCTION
+ u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106,
+ 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ;
+#else
+ u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106,
+ 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ;
+ /*
+ u8 RegAddr[171];
+ for (i = 0; i <= 170; i++)
+ RegAddr[i] = i;
+ */
+#endif
+
+ *count = ARRAY_SIZE(RegAddr);
+
+ for (i = 0 ; i < *count; i++) {
+ RegNum[i] = RegAddr[i];
+ status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
+ }
+
+ return status;
+}
+
+static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum,
+ u8 *RegVal, int *count)
+{
+ u16 status = 0;
+ int i;
+
+ u8 RegAddr[] = {43, 136};
+
+ *count = ARRAY_SIZE(RegAddr);
+
+ for (i = 0; i < *count; i++) {
+ RegNum[i] = RegAddr[i];
+ status += MXL_RegRead(fe, RegNum[i], &RegVal[i]);
+ }
+
+ return status;
+}
+
+static u16 MXL_GetMasterControl(u8 *MasterReg, int state)
+{
+ if (state == 1) /* Load_Start */
+ *MasterReg = 0xF3;
+ if (state == 2) /* Power_Down */
+ *MasterReg = 0x41;
+ if (state == 3) /* Synth_Reset */
+ *MasterReg = 0xB1;
+ if (state == 4) /* Seq_Off */
+ *MasterReg = 0xF1;
+
+ return 0;
+}
+
+#ifdef _MXL_PRODUCTION
+static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0 ;
+
+ if (VCO_Range == 1) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ if (state->Mode == 0 && state->IF_Mode == 1) {
+ /* Analog Low IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 180224);
+ }
+ if (state->Mode == 0 && state->IF_Mode == 0) {
+ /* Analog Zero IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 222822);
+ }
+ if (state->Mode == 1) /* Digital Mode */ {
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 229376);
+ }
+ }
+
+ if (VCO_Range == 2) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41);
+ if (state->Mode == 0 && state->IF_Mode == 1) {
+ /* Analog Low IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 206438);
+ }
+ if (state->Mode == 0 && state->IF_Mode == 0) {
+ /* Analog Zero IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 206438);
+ }
+ if (state->Mode == 1) /* Digital Mode */ {
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 16384);
+ }
+ }
+
+ if (VCO_Range == 3) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
+ if (state->Mode == 0 && state->IF_Mode == 1) {
+ /* Analog Low IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 173670);
+ }
+ if (state->Mode == 0 && state->IF_Mode == 0) {
+ /* Analog Zero IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 173670);
+ }
+ if (state->Mode == 1) /* Digital Mode */ {
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 245760);
+ }
+ }
+
+ if (VCO_Range == 4) {
+ status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1);
+ status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0);
+ status += MXL_ControlWrite(fe, RFSYN_DIVM, 1);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1);
+ status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1);
+ status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0);
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
+ if (state->Mode == 0 && state->IF_Mode == 1) {
+ /* Analog Low IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 206438);
+ }
+ if (state->Mode == 0 && state->IF_Mode == 0) {
+ /* Analog Zero IF Mode */
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 206438);
+ }
+ if (state->Mode == 1) /* Digital Mode */ {
+ status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0);
+ status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40);
+ status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27);
+ status += MXL_ControlWrite(fe,
+ CHCAL_FRAC_MOD_RF, 212992);
+ }
+ }
+
+ return status;
+}
+
+static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u16 status = 0;
+
+ if (Hystersis == 1)
+ status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1);
+
+ return status;
+}
+#endif
+/* End: Reference driver code found in the Realtek driver that
+ * is copyright MaxLinear */
+
+/* ----------------------------------------------------------------
+ * Begin: Everything after here is new code to adapt the
+ * proprietary Realtek driver into a Linux API tuner.
+ * Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+ */
+static int mxl5005s_reset(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ int ret = 0;
+
+ u8 buf[2] = { 0xff, 0x00 };
+ struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+ .buf = buf, .len = 2 };
+
+ dprintk(2, "%s()\n", __func__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mxl5005s I2C reset failed\n");
+ ret = -EREMOTEIO;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return ret;
+}
+
+/* Write a single byte to a single reg, latch the value if required by
+ * following the transaction with the latch byte.
+ */
+static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE };
+ struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0,
+ .buf = buf, .len = 3 };
+
+ if (latch == 0)
+ msg.len = 2;
+
+ dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr);
+
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mxl5005s I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable,
+ u8 *datatable, u8 len)
+{
+ int ret = 0, i;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ for (i = 0 ; i < len-1; i++) {
+ ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0);
+ if (ret < 0)
+ break;
+ }
+
+ ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return ret;
+}
+
+static int mxl5005s_init(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+
+ dprintk(1, "%s()\n", __func__);
+ state->current_mode = MXL_QAM;
+ return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ);
+}
+
+static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type,
+ u32 bandwidth)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+
+ u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
+ u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX];
+ int TableLen;
+
+ dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth);
+
+ mxl5005s_reset(fe);
+
+ /* Tuner initialization stage 0 */
+ MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET);
+ AddrTable[0] = MASTER_CONTROL_ADDR;
+ ByteTable[0] |= state->config->AgcMasterByte;
+
+ mxl5005s_writeregs(fe, AddrTable, ByteTable, 1);
+
+ mxl5005s_AssignTunerMode(fe, mod_type, bandwidth);
+
+ /* Tuner initialization stage 1 */
+ MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen);
+
+ mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen);
+
+ return 0;
+}
+
+static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type,
+ u32 bandwidth)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ struct mxl5005s_config *c = state->config;
+
+ InitTunerControls(fe);
+
+ /* Set MxL5005S parameters. */
+ MXL5005_TunerConfig(
+ fe,
+ c->mod_mode,
+ c->if_mode,
+ bandwidth,
+ c->if_freq,
+ c->xtal_freq,
+ c->agc_mode,
+ c->top,
+ c->output_load,
+ c->clock_out,
+ c->div_out,
+ c->cap_select,
+ c->rssi_enable,
+ mod_type,
+ c->tracking_filter);
+
+ return 0;
+}
+
+static int mxl5005s_set_params(struct dvb_frontend *fe)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ u32 bw = c->bandwidth_hz;
+ u32 req_mode, req_bw = 0;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ switch (delsys) {
+ case SYS_ATSC:
+ req_mode = MXL_ATSC;
+ req_bw = MXL5005S_BANDWIDTH_6MHZ;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ req_mode = MXL_QAM;
+ req_bw = MXL5005S_BANDWIDTH_6MHZ;
+ break;
+ default: /* Assume DVB-T */
+ req_mode = MXL_DVBT;
+ switch (bw) {
+ case 6000000:
+ req_bw = MXL5005S_BANDWIDTH_6MHZ;
+ break;
+ case 7000000:
+ req_bw = MXL5005S_BANDWIDTH_7MHZ;
+ break;
+ case 8000000:
+ case 0:
+ req_bw = MXL5005S_BANDWIDTH_8MHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ /* Change tuner for new modulation type if reqd */
+ if (req_mode != state->current_mode ||
+ req_bw != state->Chan_Bandwidth) {
+ state->current_mode = req_mode;
+ ret = mxl5005s_reconfigure(fe, req_mode, req_bw);
+
+ } else
+ ret = 0;
+
+ if (ret == 0) {
+ dprintk(1, "%s() freq=%d\n", __func__, c->frequency);
+ ret = mxl5005s_SetRfFreqHz(fe, c->frequency);
+ }
+
+ return ret;
+}
+
+static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *frequency = state->RF_IN;
+
+ return 0;
+}
+
+static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *bandwidth = state->Chan_Bandwidth;
+
+ return 0;
+}
+
+static int mxl5005s_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mxl5005s_state *state = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *frequency = state->IF_OUT;
+
+ return 0;
+}
+
+static int mxl5005s_release(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops mxl5005s_tuner_ops = {
+ .info = {
+ .name = "MaxLinear MXL5005S",
+ .frequency_min = 48000000,
+ .frequency_max = 860000000,
+ .frequency_step = 50000,
+ },
+
+ .release = mxl5005s_release,
+ .init = mxl5005s_init,
+
+ .set_params = mxl5005s_set_params,
+ .get_frequency = mxl5005s_get_frequency,
+ .get_bandwidth = mxl5005s_get_bandwidth,
+ .get_if_frequency = mxl5005s_get_if_frequency,
+};
+
+struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct mxl5005s_config *config)
+{
+ struct mxl5005s_state *state = NULL;
+ dprintk(1, "%s()\n", __func__);
+
+ state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->frontend = fe;
+ state->config = config;
+ state->i2c = i2c;
+
+ printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n",
+ config->i2c_address);
+
+ memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+ return fe;
+}
+EXPORT_SYMBOL(mxl5005s_attach);
+
+MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver");
+MODULE_AUTHOR("Steven Toth");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/tuners/mxl5005s.h
index fc8a1ffc53b4..fc8a1ffc53b4 100644
--- a/drivers/media/common/tuners/mxl5005s.h
+++ b/drivers/media/tuners/mxl5005s.h
diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c
index 69e453ef0a1a..69e453ef0a1a 100644
--- a/drivers/media/common/tuners/mxl5007t.c
+++ b/drivers/media/tuners/mxl5007t.c
diff --git a/drivers/media/common/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h
index aa3eea0b5262..aa3eea0b5262 100644
--- a/drivers/media/common/tuners/mxl5007t.h
+++ b/drivers/media/tuners/mxl5007t.h
diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c
new file mode 100644
index 000000000000..bc419f8a9671
--- /dev/null
+++ b/drivers/media/tuners/qt1010.c
@@ -0,0 +1,456 @@
+/*
+ * Driver for Quantek QT1010 silicon tuner
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ * Aapo Tahkola <aet@rasterburn.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "qt1010.h"
+#include "qt1010_priv.h"
+
+/* read single register */
+static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed reg=%02x\n",
+ KBUILD_MODNAME, reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+/* write single register */
+static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = { .addr = priv->cfg->i2c_address,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed reg=%02x\n",
+ KBUILD_MODNAME, reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int qt1010_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct qt1010_priv *priv;
+ int err;
+ u32 freq, div, mod1, mod2;
+ u8 i, tmpval, reg05;
+ qt1010_i2c_oper_t rd[48] = {
+ { QT1010_WR, 0x01, 0x80 },
+ { QT1010_WR, 0x02, 0x3f },
+ { QT1010_WR, 0x05, 0xff }, /* 02 c write */
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x07, 0xff }, /* 04 c write */
+ { QT1010_WR, 0x08, 0x08 },
+ { QT1010_WR, 0x09, 0xff }, /* 06 c write */
+ { QT1010_WR, 0x0a, 0xff }, /* 07 c write */
+ { QT1010_WR, 0x0b, 0xff }, /* 08 c write */
+ { QT1010_WR, 0x0c, 0xe1 },
+ { QT1010_WR, 0x1a, 0xff }, /* 10 c write */
+ { QT1010_WR, 0x1b, 0x00 },
+ { QT1010_WR, 0x1c, 0x89 },
+ { QT1010_WR, 0x11, 0xff }, /* 13 c write */
+ { QT1010_WR, 0x12, 0xff }, /* 14 c write */
+ { QT1010_WR, 0x22, 0xff }, /* 15 c write */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xd0 },
+ { QT1010_RD, 0x22, 0xff }, /* 16 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_RD, 0x05, 0xff }, /* 20 c read */
+ { QT1010_RD, 0x22, 0xff }, /* 21 c read */
+ { QT1010_WR, 0x23, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xe0 },
+ { QT1010_RD, 0x23, 0xff }, /* 25 c read */
+ { QT1010_RD, 0x23, 0xff }, /* 26 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x24, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xf0 },
+ { QT1010_RD, 0x24, 0xff }, /* 31 c read */
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x14, 0x7f },
+ { QT1010_WR, 0x15, 0x7f },
+ { QT1010_WR, 0x05, 0xff }, /* 35 c write */
+ { QT1010_WR, 0x06, 0x00 },
+ { QT1010_WR, 0x15, 0x1f },
+ { QT1010_WR, 0x16, 0xff },
+ { QT1010_WR, 0x18, 0xff },
+ { QT1010_WR, 0x1f, 0xff }, /* 40 c write */
+ { QT1010_WR, 0x20, 0xff }, /* 41 c write */
+ { QT1010_WR, 0x21, 0x53 },
+ { QT1010_WR, 0x25, 0xff }, /* 43 c write */
+ { QT1010_WR, 0x26, 0x15 },
+ { QT1010_WR, 0x00, 0xff }, /* 45 c write */
+ { QT1010_WR, 0x02, 0x00 },
+ { QT1010_WR, 0x01, 0x00 }
+ };
+
+#define FREQ1 32000000 /* 32 MHz */
+#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */
+
+ priv = fe->tuner_priv;
+ freq = c->frequency;
+ div = (freq + QT1010_OFFSET) / QT1010_STEP;
+ freq = (div * QT1010_STEP) - QT1010_OFFSET;
+ mod1 = (freq + QT1010_OFFSET) % FREQ1;
+ mod2 = (freq + QT1010_OFFSET) % FREQ2;
+ priv->frequency = freq;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ /* reg 05 base value */
+ if (freq < 290000000) reg05 = 0x14; /* 290 MHz */
+ else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */
+ else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */
+ else reg05 = 0x74;
+
+ /* 0x5 */
+ rd[2].val = reg05;
+
+ /* 07 - set frequency: 32 MHz scale */
+ rd[4].val = (freq + QT1010_OFFSET) / FREQ1;
+
+ /* 09 - changes every 8/24 MHz */
+ if (mod1 < 8000000) rd[6].val = 0x1d;
+ else rd[6].val = 0x1c;
+
+ /* 0a - set frequency: 4 MHz scale (max 28 MHz) */
+ if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */
+ else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */
+ else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */
+ else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */
+ else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */
+ else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */
+ else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */
+ else rd[7].val = 0x0a; /* +28 MHz */
+
+ /* 0b - changes every 2/2 MHz */
+ if (mod2 < 2000000) rd[8].val = 0x45;
+ else rd[8].val = 0x44;
+
+ /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/
+ tmpval = 0x78; /* byte, overflows intentionally */
+ rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08);
+
+ /* 11 */
+ rd[13].val = 0xfd; /* TODO: correct value calculation */
+
+ /* 12 */
+ rd[14].val = 0x91; /* TODO: correct value calculation */
+
+ /* 22 */
+ if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */
+ else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */
+ else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */
+ else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */
+ else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */
+ else rd[15].val = 0xd0;
+
+ /* 05 */
+ rd[35].val = (reg05 & 0xf0);
+
+ /* 1f */
+ if (mod1 < 8000000) tmpval = 0x00;
+ else if (mod1 < 12000000) tmpval = 0x01;
+ else if (mod1 < 16000000) tmpval = 0x02;
+ else if (mod1 < 24000000) tmpval = 0x03;
+ else if (mod1 < 28000000) tmpval = 0x04;
+ else tmpval = 0x05;
+ rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval);
+
+ /* 20 */
+ if (mod1 < 8000000) tmpval = 0x00;
+ else if (mod1 < 12000000) tmpval = 0x01;
+ else if (mod1 < 20000000) tmpval = 0x02;
+ else if (mod1 < 24000000) tmpval = 0x03;
+ else if (mod1 < 28000000) tmpval = 0x04;
+ else tmpval = 0x05;
+ rd[41].val = (priv->reg20_init_val + 0x0d + tmpval);
+
+ /* 25 */
+ rd[43].val = priv->reg25_init_val;
+
+ /* 00 */
+ rd[45].val = 0x92; /* TODO: correct value calculation */
+
+ dev_dbg(&priv->i2c->dev,
+ "%s: freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \
+ "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \
+ "20:%02x 25:%02x 00:%02x\n", __func__, \
+ freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, \
+ rd[8].val, rd[10].val, rd[13].val, rd[14].val, \
+ rd[15].val, rd[35].val, rd[40].val, rd[41].val, \
+ rd[43].val, rd[45].val);
+
+ for (i = 0; i < ARRAY_SIZE(rd); i++) {
+ if (rd[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, rd[i].reg, rd[i].val);
+ } else { /* read is required to proper locking */
+ err = qt1010_readreg(priv, rd[i].reg, &tmpval);
+ }
+ if (err) return err;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ return 0;
+}
+
+static int qt1010_init_meas1(struct qt1010_priv *priv,
+ u8 oper, u8 reg, u8 reg_init_val, u8 *retval)
+{
+ u8 i, val1, val2;
+ int err;
+
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, reg, reg_init_val },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, oper },
+ { QT1010_RD, reg, 0xff }
+ };
+
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ if (i2c_data[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ } else {
+ err = qt1010_readreg(priv, i2c_data[i].reg, &val2);
+ }
+ if (err) return err;
+ }
+
+ do {
+ val1 = val2;
+ err = qt1010_readreg(priv, reg, &val2);
+ if (err) return err;
+ dev_dbg(&priv->i2c->dev, "%s: compare reg:%02x %02x %02x\n",
+ __func__, reg, val1, val2);
+ } while (val1 != val2);
+ *retval = val1;
+
+ return qt1010_writereg(priv, 0x1e, 0x00);
+}
+
+static int qt1010_init_meas2(struct qt1010_priv *priv,
+ u8 reg_init_val, u8 *retval)
+{
+ u8 i, val;
+ int err;
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, 0x07, reg_init_val },
+ { QT1010_WR, 0x22, 0xd0 },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x1e, 0xd0 },
+ { QT1010_RD, 0x22, 0xff },
+ { QT1010_WR, 0x1e, 0x00 },
+ { QT1010_WR, 0x22, 0xff }
+ };
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ if (i2c_data[i].oper == QT1010_WR) {
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ } else {
+ err = qt1010_readreg(priv, i2c_data[i].reg, &val);
+ }
+ if (err) return err;
+ }
+ *retval = val;
+ return 0;
+}
+
+static int qt1010_init(struct dvb_frontend *fe)
+{
+ struct qt1010_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int err = 0;
+ u8 i, tmpval, *valptr = NULL;
+
+ qt1010_i2c_oper_t i2c_data[] = {
+ { QT1010_WR, 0x01, 0x80 },
+ { QT1010_WR, 0x0d, 0x84 },
+ { QT1010_WR, 0x0e, 0xb7 },
+ { QT1010_WR, 0x2a, 0x23 },
+ { QT1010_WR, 0x2c, 0xdc },
+ { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */
+ { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */
+ { QT1010_WR, 0x2b, 0x70 },
+ { QT1010_WR, 0x2a, 0x23 },
+ { QT1010_M1, 0x26, 0x08 },
+ { QT1010_M1, 0x82, 0xff },
+ { QT1010_WR, 0x05, 0x14 },
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x07, 0x28 },
+ { QT1010_WR, 0x08, 0x0b },
+ { QT1010_WR, 0x11, 0xfd },
+ { QT1010_M1, 0x22, 0x0d },
+ { QT1010_M1, 0xd0, 0xff },
+ { QT1010_WR, 0x06, 0x40 },
+ { QT1010_WR, 0x16, 0xf0 },
+ { QT1010_WR, 0x02, 0x38 },
+ { QT1010_WR, 0x03, 0x18 },
+ { QT1010_WR, 0x20, 0xe0 },
+ { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */
+ { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */
+ { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */
+ { QT1010_WR, 0x03, 0x19 },
+ { QT1010_WR, 0x02, 0x3f },
+ { QT1010_WR, 0x21, 0x53 },
+ { QT1010_RD, 0x21, 0xff },
+ { QT1010_WR, 0x11, 0xfd },
+ { QT1010_WR, 0x05, 0x34 },
+ { QT1010_WR, 0x06, 0x44 },
+ { QT1010_WR, 0x08, 0x08 }
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+ for (i = 0; i < ARRAY_SIZE(i2c_data); i++) {
+ switch (i2c_data[i].oper) {
+ case QT1010_WR:
+ err = qt1010_writereg(priv, i2c_data[i].reg,
+ i2c_data[i].val);
+ break;
+ case QT1010_RD:
+ if (i2c_data[i].val == 0x20)
+ valptr = &priv->reg20_init_val;
+ else
+ valptr = &tmpval;
+ err = qt1010_readreg(priv, i2c_data[i].reg, valptr);
+ break;
+ case QT1010_M1:
+ if (i2c_data[i].val == 0x25)
+ valptr = &priv->reg25_init_val;
+ else if (i2c_data[i].val == 0x1f)
+ valptr = &priv->reg1f_init_val;
+ else
+ valptr = &tmpval;
+ err = qt1010_init_meas1(priv, i2c_data[i+1].reg,
+ i2c_data[i].reg,
+ i2c_data[i].val, valptr);
+ i++;
+ break;
+ }
+ if (err) return err;
+ }
+
+ for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */
+ if ((err = qt1010_init_meas2(priv, i, &tmpval)))
+ return err;
+
+ if (!c->frequency)
+ c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */
+ /* MSI Megasky 580 GL861 533000000 */
+ return qt1010_set_params(fe);
+}
+
+static int qt1010_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct qt1010_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ *frequency = 36125000;
+ return 0;
+}
+
+static const struct dvb_tuner_ops qt1010_tuner_ops = {
+ .info = {
+ .name = "Quantek QT1010",
+ .frequency_min = QT1010_MIN_FREQ,
+ .frequency_max = QT1010_MAX_FREQ,
+ .frequency_step = QT1010_STEP,
+ },
+
+ .release = qt1010_release,
+ .init = qt1010_init,
+ /* TODO: implement sleep */
+
+ .set_params = qt1010_set_params,
+ .get_frequency = qt1010_get_frequency,
+ .get_if_frequency = qt1010_get_if_frequency,
+};
+
+struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct qt1010_config *cfg)
+{
+ struct qt1010_priv *priv = NULL;
+ u8 id;
+
+ priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
+
+
+ /* Try to detect tuner chip. Probably this is not correct register. */
+ if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) {
+ kfree(priv);
+ return NULL;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
+
+ dev_info(&priv->i2c->dev,
+ "%s: Quantek QT1010 successfully identified\n",
+ KBUILD_MODNAME);
+
+ memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(qt1010_attach);
+
+MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/qt1010.h b/drivers/media/tuners/qt1010.h
index 807fb7b6146b..807fb7b6146b 100644
--- a/drivers/media/common/tuners/qt1010.h
+++ b/drivers/media/tuners/qt1010.h
diff --git a/drivers/media/common/tuners/qt1010_priv.h b/drivers/media/tuners/qt1010_priv.h
index 2c42d3f01636..2c42d3f01636 100644
--- a/drivers/media/common/tuners/qt1010_priv.h
+++ b/drivers/media/tuners/qt1010_priv.h
diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c
new file mode 100644
index 000000000000..5d9f02842501
--- /dev/null
+++ b/drivers/media/tuners/tda18212.c
@@ -0,0 +1,319 @@
+/*
+ * NXP TDA18212HN silicon tuner driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tda18212.h"
+
+struct tda18212_priv {
+ struct tda18212_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 if_frequency;
+};
+
+/* write multiple registers */
+static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len+1];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_address,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple registers */
+static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[len];
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, buf, len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)
+{
+ return tda18212_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)
+{
+ return tda18212_rd_regs(priv, reg, val, 1);
+}
+
+#if 0 /* keep, useful when developing driver */
+static void tda18212_dump_regs(struct tda18212_priv *priv)
+{
+ int i;
+ u8 buf[256];
+
+ #define TDA18212_RD_LEN 32
+ for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)
+ tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,
+ sizeof(buf), true);
+
+ return;
+}
+#endif
+
+static int tda18212_set_params(struct dvb_frontend *fe)
+{
+ struct tda18212_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u32 if_khz;
+ u8 buf[9];
+ #define DVBT_6 0
+ #define DVBT_7 1
+ #define DVBT_8 2
+ #define DVBT2_6 3
+ #define DVBT2_7 4
+ #define DVBT2_8 5
+ #define DVBC_6 6
+ #define DVBC_8 7
+ static const u8 bw_params[][3] = {
+ /* reg: 0f 13 23 */
+ [DVBT_6] = { 0xb3, 0x20, 0x03 },
+ [DVBT_7] = { 0xb3, 0x31, 0x01 },
+ [DVBT_8] = { 0xb3, 0x22, 0x01 },
+ [DVBT2_6] = { 0xbc, 0x20, 0x03 },
+ [DVBT2_7] = { 0xbc, 0x72, 0x03 },
+ [DVBT2_8] = { 0xbc, 0x22, 0x01 },
+ [DVBC_6] = { 0x92, 0x50, 0x03 },
+ [DVBC_8] = { 0x92, 0x53, 0x03 },
+ };
+
+ dev_dbg(&priv->i2c->dev,
+ "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
+ __func__, c->delivery_system, c->frequency,
+ c->bandwidth_hz);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ if_khz = priv->cfg->if_dvbt_6;
+ i = DVBT_6;
+ break;
+ case 7000000:
+ if_khz = priv->cfg->if_dvbt_7;
+ i = DVBT_7;
+ break;
+ case 8000000:
+ if_khz = priv->cfg->if_dvbt_8;
+ i = DVBT_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ case SYS_DVBT2:
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ if_khz = priv->cfg->if_dvbt2_6;
+ i = DVBT2_6;
+ break;
+ case 7000000:
+ if_khz = priv->cfg->if_dvbt2_7;
+ i = DVBT2_7;
+ break;
+ case 8000000:
+ if_khz = priv->cfg->if_dvbt2_8;
+ i = DVBT2_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error;
+ }
+ break;
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if_khz = priv->cfg->if_dvbc;
+ i = DVBC_8;
+ break;
+ default:
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);
+ if (ret)
+ goto error;
+
+ ret = tda18212_wr_reg(priv, 0x06, 0x00);
+ if (ret)
+ goto error;
+
+ ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);
+ if (ret)
+ goto error;
+
+ buf[0] = 0x02;
+ buf[1] = bw_params[i][1];
+ buf[2] = 0x03; /* default value */
+ buf[3] = DIV_ROUND_CLOSEST(if_khz, 50);
+ buf[4] = ((c->frequency / 1000) >> 16) & 0xff;
+ buf[5] = ((c->frequency / 1000) >> 8) & 0xff;
+ buf[6] = ((c->frequency / 1000) >> 0) & 0xff;
+ buf[7] = 0xc1;
+ buf[8] = 0x01;
+ ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ /* actual IF rounded as it is on register */
+ priv->if_frequency = buf[3] * 50 * 1000;
+
+exit:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ return ret;
+
+error:
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+ goto exit;
+}
+
+static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18212_priv *priv = fe->tuner_priv;
+
+ *frequency = priv->if_frequency;
+
+ return 0;
+}
+
+static int tda18212_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops tda18212_tuner_ops = {
+ .info = {
+ .name = "NXP TDA18212",
+
+ .frequency_min = 48000000,
+ .frequency_max = 864000000,
+ .frequency_step = 1000,
+ },
+
+ .release = tda18212_release,
+
+ .set_params = tda18212_set_params,
+ .get_if_frequency = tda18212_get_if_frequency,
+};
+
+struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tda18212_config *cfg)
+{
+ struct tda18212_priv *priv = NULL;
+ int ret;
+ u8 uninitialized_var(val);
+
+ priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ fe->tuner_priv = priv;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* check if the tuner is there */
+ ret = tda18212_rd_reg(priv, 0x00, &val);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret,
+ val);
+ if (ret || val != 0xc7) {
+ kfree(priv);
+ return NULL;
+ }
+
+ dev_info(&priv->i2c->dev,
+ "%s: NXP TDA18212HN successfully identified\n",
+ KBUILD_MODNAME);
+
+ memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ return fe;
+}
+EXPORT_SYMBOL(tda18212_attach);
+
+MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18212.h b/drivers/media/tuners/tda18212.h
index 9bd5da4aabb7..9bd5da4aabb7 100644
--- a/drivers/media/common/tuners/tda18212.h
+++ b/drivers/media/tuners/tda18212.h
diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c
new file mode 100644
index 000000000000..18198537be9f
--- /dev/null
+++ b/drivers/media/tuners/tda18218.c
@@ -0,0 +1,340 @@
+/*
+ * NXP TDA18218HN silicon tuner driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "tda18218_priv.h"
+
+/* write multiple registers */
+static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
+{
+ int ret = 0, len2, remaining;
+ u8 buf[1 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_address,
+ .flags = 0,
+ .buf = buf,
+ }
+ };
+
+ for (remaining = len; remaining > 0;
+ remaining -= (priv->cfg->i2c_wr_max - 1)) {
+ len2 = remaining;
+ if (len2 > (priv->cfg->i2c_wr_max - 1))
+ len2 = (priv->cfg->i2c_wr_max - 1);
+
+ msg[0].len = 1 + len2;
+ buf[0] = reg + len - remaining;
+ memcpy(&buf[1], &val[len - remaining], len2);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret != 1)
+ break;
+ }
+
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read multiple registers */
+static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)
+{
+ int ret;
+ u8 buf[reg+len]; /* we must start read always from reg 0x00 */
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg->i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = "\x00",
+ }, {
+ .addr = priv->cfg->i2c_address,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ memcpy(val, &buf[reg], len);
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \
+ "len=%d\n", KBUILD_MODNAME, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* write single register */
+static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)
+{
+ return tda18218_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register */
+
+static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)
+{
+ return tda18218_rd_regs(priv, reg, val, 1);
+}
+
+static int tda18218_set_params(struct dvb_frontend *fe)
+{
+ struct tda18218_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 bw = c->bandwidth_hz;
+ int ret;
+ u8 buf[3], i, BP_Filter, LP_Fc;
+ u32 LO_Frac;
+ /* TODO: find out correct AGC algorithm */
+ u8 agc[][2] = {
+ { R20_AGC11, 0x60 },
+ { R23_AGC21, 0x02 },
+ { R20_AGC11, 0xa0 },
+ { R23_AGC21, 0x09 },
+ { R20_AGC11, 0xe0 },
+ { R23_AGC21, 0x0c },
+ { R20_AGC11, 0x40 },
+ { R23_AGC21, 0x01 },
+ { R20_AGC11, 0x80 },
+ { R23_AGC21, 0x08 },
+ { R20_AGC11, 0xc0 },
+ { R23_AGC21, 0x0b },
+ { R24_AGC22, 0x1c },
+ { R24_AGC22, 0x0c },
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* low-pass filter cut-off frequency */
+ if (bw <= 6000000) {
+ LP_Fc = 0;
+ priv->if_frequency = 3000000;
+ } else if (bw <= 7000000) {
+ LP_Fc = 1;
+ priv->if_frequency = 3500000;
+ } else {
+ LP_Fc = 2;
+ priv->if_frequency = 4000000;
+ }
+
+ LO_Frac = c->frequency + priv->if_frequency;
+
+ /* band-pass filter */
+ if (LO_Frac < 188000000)
+ BP_Filter = 3;
+ else if (LO_Frac < 253000000)
+ BP_Filter = 4;
+ else if (LO_Frac < 343000000)
+ BP_Filter = 5;
+ else
+ BP_Filter = 6;
+
+ buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */
+ buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */
+ buf[2] = priv->regs[R1C_AGC2B];
+ ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);
+ if (ret)
+ goto error;
+
+ buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */
+ buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */
+ buf[2] = (LO_Frac / 1000) << 4 |
+ (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */
+ ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);
+ if (ret)
+ goto error;
+
+ buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */
+ ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
+ if (ret)
+ goto error;
+
+ buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */
+ ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);
+ if (ret)
+ goto error;
+
+ /* trigger AGC */
+ for (i = 0; i < ARRAY_SIZE(agc); i++) {
+ ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);
+ if (ret)
+ goto error;
+ }
+
+error:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (ret)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18218_priv *priv = fe->tuner_priv;
+ *frequency = priv->if_frequency;
+ dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d\n", __func__, *frequency);
+ return 0;
+}
+
+static int tda18218_sleep(struct dvb_frontend *fe)
+{
+ struct tda18218_priv *priv = fe->tuner_priv;
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* standby */
+ ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (ret)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tda18218_init(struct dvb_frontend *fe)
+{
+ struct tda18218_priv *priv = fe->tuner_priv;
+ int ret;
+
+ /* TODO: calibrations */
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (ret)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tda18218_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops tda18218_tuner_ops = {
+ .info = {
+ .name = "NXP TDA18218",
+
+ .frequency_min = 174000000,
+ .frequency_max = 864000000,
+ .frequency_step = 1000,
+ },
+
+ .release = tda18218_release,
+ .init = tda18218_init,
+ .sleep = tda18218_sleep,
+
+ .set_params = tda18218_set_params,
+
+ .get_if_frequency = tda18218_get_if_frequency,
+};
+
+struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tda18218_config *cfg)
+{
+ struct tda18218_priv *priv = NULL;
+ u8 uninitialized_var(val);
+ int ret;
+ /* chip default registers values */
+ static u8 def_regs[] = {
+ 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,
+ 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,
+ 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,
+ 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,
+ 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,
+ 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6
+ };
+
+ priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ fe->tuner_priv = priv;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* check if the tuner is there */
+ ret = tda18218_rd_reg(priv, R00_ID, &val);
+ dev_dbg(&priv->i2c->dev, "%s: ret=%d chip id=%02x\n", __func__, ret,
+ val);
+ if (ret || val != def_regs[R00_ID]) {
+ kfree(priv);
+ return NULL;
+ }
+
+ dev_info(&priv->i2c->dev,
+ "%s: NXP TDA18218HN successfully identified\n",
+ KBUILD_MODNAME);
+
+ memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+ memcpy(priv->regs, def_regs, sizeof(def_regs));
+
+ /* loop-through enabled chip default register values */
+ if (priv->cfg->loop_through) {
+ priv->regs[R17_PD1] = 0xb0;
+ priv->regs[R18_PD2] = 0x59;
+ }
+
+ /* standby */
+ ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));
+ if (ret)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ return fe;
+}
+EXPORT_SYMBOL(tda18218_attach);
+
+MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tda18218.h b/drivers/media/tuners/tda18218.h
index b4180d180029..b4180d180029 100644
--- a/drivers/media/common/tuners/tda18218.h
+++ b/drivers/media/tuners/tda18218.h
diff --git a/drivers/media/tuners/tda18218_priv.h b/drivers/media/tuners/tda18218_priv.h
new file mode 100644
index 000000000000..285b77366c8d
--- /dev/null
+++ b/drivers/media/tuners/tda18218_priv.h
@@ -0,0 +1,97 @@
+/*
+ * NXP TDA18218HN silicon tuner driver
+ *
+ * Copyright (C) 2010 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 TDA18218_PRIV_H
+#define TDA18218_PRIV_H
+
+#include "tda18218.h"
+
+#define R00_ID 0x00 /* ID byte */
+#define R01_R1 0x01 /* Read byte 1 */
+#define R02_R2 0x02 /* Read byte 2 */
+#define R03_R3 0x03 /* Read byte 3 */
+#define R04_R4 0x04 /* Read byte 4 */
+#define R05_R5 0x05 /* Read byte 5 */
+#define R06_R6 0x06 /* Read byte 6 */
+#define R07_MD1 0x07 /* Main divider byte 1 */
+#define R08_PSM1 0x08 /* PSM byte 1 */
+#define R09_MD2 0x09 /* Main divider byte 2 */
+#define R0A_MD3 0x0a /* Main divider byte 1 */
+#define R0B_MD4 0x0b /* Main divider byte 4 */
+#define R0C_MD5 0x0c /* Main divider byte 5 */
+#define R0D_MD6 0x0d /* Main divider byte 6 */
+#define R0E_MD7 0x0e /* Main divider byte 7 */
+#define R0F_MD8 0x0f /* Main divider byte 8 */
+#define R10_CD1 0x10 /* Call divider byte 1 */
+#define R11_CD2 0x11 /* Call divider byte 2 */
+#define R12_CD3 0x12 /* Call divider byte 3 */
+#define R13_CD4 0x13 /* Call divider byte 4 */
+#define R14_CD5 0x14 /* Call divider byte 5 */
+#define R15_CD6 0x15 /* Call divider byte 6 */
+#define R16_CD7 0x16 /* Call divider byte 7 */
+#define R17_PD1 0x17 /* Power-down byte 1 */
+#define R18_PD2 0x18 /* Power-down byte 2 */
+#define R19_XTOUT 0x19 /* XTOUT byte */
+#define R1A_IF1 0x1a /* IF byte 1 */
+#define R1B_IF2 0x1b /* IF byte 2 */
+#define R1C_AGC2B 0x1c /* AGC2b byte */
+#define R1D_PSM2 0x1d /* PSM byte 2 */
+#define R1E_PSM3 0x1e /* PSM byte 3 */
+#define R1F_PSM4 0x1f /* PSM byte 4 */
+#define R20_AGC11 0x20 /* AGC1 byte 1 */
+#define R21_AGC12 0x21 /* AGC1 byte 2 */
+#define R22_AGC13 0x22 /* AGC1 byte 3 */
+#define R23_AGC21 0x23 /* AGC2 byte 1 */
+#define R24_AGC22 0x24 /* AGC2 byte 2 */
+#define R25_AAGC 0x25 /* Analog AGC byte */
+#define R26_RC 0x26 /* RC byte */
+#define R27_RSSI 0x27 /* RSSI byte */
+#define R28_IRCAL1 0x28 /* IR CAL byte 1 */
+#define R29_IRCAL2 0x29 /* IR CAL byte 2 */
+#define R2A_IRCAL3 0x2a /* IR CAL byte 3 */
+#define R2B_IRCAL4 0x2b /* IR CAL byte 4 */
+#define R2C_RFCAL1 0x2c /* RF CAL byte 1 */
+#define R2D_RFCAL2 0x2d /* RF CAL byte 2 */
+#define R2E_RFCAL3 0x2e /* RF CAL byte 3 */
+#define R2F_RFCAL4 0x2f /* RF CAL byte 4 */
+#define R30_RFCAL5 0x30 /* RF CAL byte 5 */
+#define R31_RFCAL6 0x31 /* RF CAL byte 6 */
+#define R32_RFCAL7 0x32 /* RF CAL byte 7 */
+#define R33_RFCAL8 0x33 /* RF CAL byte 8 */
+#define R34_RFCAL9 0x34 /* RF CAL byte 9 */
+#define R35_RFCAL10 0x35 /* RF CAL byte 10 */
+#define R36_RFCALRAM1 0x36 /* RF CAL RAM byte 1 */
+#define R37_RFCALRAM2 0x37 /* RF CAL RAM byte 2 */
+#define R38_MARGIN 0x38 /* Margin byte */
+#define R39_FMAX1 0x39 /* Fmax byte 1 */
+#define R3A_FMAX2 0x3a /* Fmax byte 2 */
+
+#define TDA18218_NUM_REGS 59
+
+struct tda18218_priv {
+ struct tda18218_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 if_frequency;
+
+ u8 regs[TDA18218_NUM_REGS];
+};
+
+#endif
diff --git a/drivers/media/tuners/tda18271-common.c b/drivers/media/tuners/tda18271-common.c
new file mode 100644
index 000000000000..221171eeb0c3
--- /dev/null
+++ b/drivers/media/tuners/tda18271-common.c
@@ -0,0 +1,703 @@
+/*
+ tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "tda18271-priv.h"
+
+static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ enum tda18271_i2c_gate gate;
+ int ret = 0;
+
+ switch (priv->gate) {
+ case TDA18271_GATE_DIGITAL:
+ case TDA18271_GATE_ANALOG:
+ gate = priv->gate;
+ break;
+ case TDA18271_GATE_AUTO:
+ default:
+ switch (priv->mode) {
+ case TDA18271_DIGITAL:
+ gate = TDA18271_GATE_DIGITAL;
+ break;
+ case TDA18271_ANALOG:
+ default:
+ gate = TDA18271_GATE_ANALOG;
+ break;
+ }
+ }
+
+ switch (gate) {
+ case TDA18271_GATE_ANALOG:
+ if (fe->ops.analog_ops.i2c_gate_ctrl)
+ ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable);
+ break;
+ case TDA18271_GATE_DIGITAL:
+ if (fe->ops.i2c_gate_ctrl)
+ ret = fe->ops.i2c_gate_ctrl(fe, enable);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+};
+
+/*---------------------------------------------------------------------*/
+
+static void tda18271_dump_regs(struct dvb_frontend *fe, int extended)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_reg("=== TDA18271 REG DUMP ===\n");
+ tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]);
+ tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]);
+ tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]);
+ tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]);
+ tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]);
+ tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]);
+ tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]);
+ tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]);
+ tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]);
+ tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]);
+ tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]);
+ tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]);
+ tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]);
+ tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]);
+ tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]);
+ tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]);
+
+ /* only dump extended regs if DBG_ADV is set */
+ if (!(tda18271_debug & DBG_ADV))
+ return;
+
+ /* W indicates write-only registers.
+ * Register dump for write-only registers shows last value written. */
+
+ tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]);
+ tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]);
+ tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]);
+ tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]);
+ tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]);
+ tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]);
+ tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]);
+ tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]);
+ tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]);
+ tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]);
+ tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]);
+ tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]);
+ tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]);
+ tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]);
+ tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]);
+ tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]);
+ tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]);
+ tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]);
+ tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]);
+ tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]);
+ tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]);
+ tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]);
+ tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]);
+}
+
+int tda18271_read_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf = 0x00;
+ int ret;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_props.addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
+ .buf = regs, .len = 16 }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 0);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_read_extended(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char regdump[TDA18271_NUM_REGS];
+ unsigned char buf = 0x00;
+ int ret, i;
+ struct i2c_msg msg[] = {
+ { .addr = priv->i2c_props.addr, .flags = 0,
+ .buf = &buf, .len = 1 },
+ { .addr = priv->i2c_props.addr, .flags = I2C_M_RD,
+ .buf = regdump, .len = TDA18271_NUM_REGS }
+ };
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+
+ /* read all registers */
+ ret = i2c_transfer(priv->i2c_props.adap, msg, 2);
+
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ tda_err("ERROR: i2c_transfer returned: %d\n", ret);
+
+ for (i = 0; i < TDA18271_NUM_REGS; i++) {
+ /* don't update write-only registers */
+ if ((i != R_EB9) &&
+ (i != R_EB16) &&
+ (i != R_EB17) &&
+ (i != R_EB19) &&
+ (i != R_EB20))
+ regs[i] = regdump[i];
+ }
+
+ if (tda18271_debug & DBG_REG)
+ tda18271_dump_regs(fe, 1);
+
+ return (ret == 2 ? 0 : ret);
+}
+
+int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ unsigned char buf[TDA18271_NUM_REGS + 1];
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0,
+ .buf = buf };
+ int i, ret = 1, max;
+
+ BUG_ON((len == 0) || (idx + len > sizeof(buf)));
+
+
+ switch (priv->small_i2c) {
+ case TDA18271_03_BYTE_CHUNK_INIT:
+ max = 3;
+ break;
+ case TDA18271_08_BYTE_CHUNK_INIT:
+ max = 8;
+ break;
+ case TDA18271_16_BYTE_CHUNK_INIT:
+ max = 16;
+ break;
+ case TDA18271_39_BYTE_CHUNK_INIT:
+ default:
+ max = 39;
+ }
+
+ tda18271_i2c_gate_ctrl(fe, 1);
+ while (len) {
+ if (max > len)
+ max = len;
+
+ buf[0] = idx;
+ for (i = 1; i <= max; i++)
+ buf[i] = regs[idx - 1 + i];
+
+ msg.len = max + 1;
+
+ /* write registers */
+ ret = i2c_transfer(priv->i2c_props.adap, &msg, 1);
+ if (ret != 1)
+ break;
+
+ idx += max;
+ len -= max;
+ }
+ tda18271_i2c_gate_ctrl(fe, 0);
+
+ if (ret != 1)
+ tda_err("ERROR: idx = 0x%x, len = %d, "
+ "i2c_transfer returned: %d\n", idx, max, ret);
+
+ return (ret == 1 ? 0 : ret);
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_charge_pump_source(struct dvb_frontend *fe,
+ enum tda18271_pll pll, int force)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4;
+
+ regs[r_cp] &= ~0x20;
+ regs[r_cp] |= ((force & 1) << 5);
+
+ return tda18271_write_regs(fe, r_cp, 1);
+}
+
+int tda18271_init_regs(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ tda_dbg("initializing registers for device @ %d-%04x\n",
+ i2c_adapter_id(priv->i2c_props.adap),
+ priv->i2c_props.addr);
+
+ /* initialize registers */
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_ID] = 0x83;
+ break;
+ case TDA18271HDC2:
+ regs[R_ID] = 0x84;
+ break;
+ }
+
+ regs[R_TM] = 0x08;
+ regs[R_PL] = 0x80;
+ regs[R_EP1] = 0xc6;
+ regs[R_EP2] = 0xdf;
+ regs[R_EP3] = 0x16;
+ regs[R_EP4] = 0x60;
+ regs[R_EP5] = 0x80;
+ regs[R_CPD] = 0x80;
+ regs[R_CD1] = 0x00;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0x00;
+ regs[R_MD1] = 0x00;
+ regs[R_MD2] = 0x00;
+ regs[R_MD3] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB1] = 0xff;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB1] = 0xfc;
+ break;
+ }
+
+ regs[R_EB2] = 0x01;
+ regs[R_EB3] = 0x84;
+ regs[R_EB4] = 0x41;
+ regs[R_EB5] = 0x01;
+ regs[R_EB6] = 0x84;
+ regs[R_EB7] = 0x40;
+ regs[R_EB8] = 0x07;
+ regs[R_EB9] = 0x00;
+ regs[R_EB10] = 0x00;
+ regs[R_EB11] = 0x96;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB12] = 0x0f;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB12] = 0x33;
+ break;
+ }
+
+ regs[R_EB13] = 0xc1;
+ regs[R_EB14] = 0x00;
+ regs[R_EB15] = 0x8f;
+ regs[R_EB16] = 0x00;
+ regs[R_EB17] = 0x00;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB18] = 0x00;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB18] = 0x8c;
+ break;
+ }
+
+ regs[R_EB19] = 0x00;
+ regs[R_EB20] = 0x20;
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ regs[R_EB21] = 0x33;
+ break;
+ case TDA18271HDC2:
+ regs[R_EB21] = 0xb3;
+ break;
+ }
+
+ regs[R_EB22] = 0x48;
+ regs[R_EB23] = 0xb0;
+
+ tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS);
+
+ /* setup agc1 gain */
+ regs[R_EB17] = 0x00;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x03;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x43;
+ tda18271_write_regs(fe, R_EB17, 1);
+ regs[R_EB17] = 0x4c;
+ tda18271_write_regs(fe, R_EB17, 1);
+
+ /* setup agc2 gain */
+ if ((priv->id) == TDA18271HDC1) {
+ regs[R_EB20] = 0xa0;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xa7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xe7;
+ tda18271_write_regs(fe, R_EB20, 1);
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ }
+
+ /* image rejection calibration */
+
+ /* low-band */
+ regs[R_EP3] = 0x1f;
+ regs[R_EP4] = 0x66;
+ regs[R_EP5] = 0x81;
+ regs[R_CPD] = 0xcc;
+ regs[R_CD1] = 0x6c;
+ regs[R_CD2] = 0x00;
+ regs[R_CD3] = 0x00;
+ regs[R_MPD] = 0xcd;
+ regs[R_MD1] = 0x77;
+ regs[R_MD2] = 0x08;
+ regs[R_MD3] = 0x00;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+
+ if ((priv->id) == TDA18271HDC2) {
+ /* main pll cp source on */
+ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
+ msleep(1);
+
+ /* main pll cp source off */
+ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
+ }
+
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted low measurement */
+
+ regs[R_EP5] = 0x85;
+ regs[R_CPD] = 0xcb;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0x70;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image low optimization completion */
+
+ /* mid-band */
+ regs[R_EP5] = 0x82;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0xa9;
+ regs[R_MD1] = 0x73;
+ regs[R_MD2] = 0x1a;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted mid measurement */
+
+ regs[R_EP5] = 0x86;
+ regs[R_CPD] = 0xa8;
+ regs[R_CD1] = 0x66;
+ regs[R_CD2] = 0xa0;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image mid optimization completion */
+
+ /* high-band */
+ regs[R_EP5] = 0x83;
+ regs[R_CPD] = 0x98;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x00;
+ regs[R_MPD] = 0x99;
+ regs[R_MD1] = 0x71;
+ regs[R_MD2] = 0xcd;
+
+ tda18271_write_regs(fe, R_EP3, 11);
+ msleep(5); /* pll locking */
+
+ /* launch detector */
+ tda18271_write_regs(fe, R_EP1, 1);
+ msleep(5); /* wanted high measurement */
+
+ regs[R_EP5] = 0x87;
+ regs[R_CD1] = 0x65;
+ regs[R_CD2] = 0x50;
+
+ tda18271_write_regs(fe, R_EP3, 7);
+ msleep(5); /* pll locking */
+
+ /* launch optimization algorithm */
+ tda18271_write_regs(fe, R_EP2, 1);
+ msleep(30); /* image high optimization completion */
+
+ /* return to normal mode */
+ regs[R_EP4] = 0x64;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* synchronize */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+/*
+ * Standby modes, EP3 [7:5]
+ *
+ * | SM || SM_LT || SM_XT || mode description
+ * |=====\\=======\\=======\\===================================
+ * | 0 || 0 || 0 || normal mode
+ * |-----||-------||-------||-----------------------------------
+ * | || || || standby mode w/ slave tuner output
+ * | 1 || 0 || 0 || & loop thru & xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 0 || standby mode w/ xtal oscillator on
+ * |-----||-------||-------||-----------------------------------
+ * | 1 || 1 || 1 || power off
+ *
+ */
+
+int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ int sm, int sm_lt, int sm_xt)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ if (tda18271_debug & DBG_ADV)
+ tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt);
+
+ regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */
+ regs[R_EP3] |= (sm ? (1 << 7) : 0) |
+ (sm_lt ? (1 << 6) : 0) |
+ (sm_xt ? (1 << 5) : 0);
+
+ return tda18271_write_regs(fe, R_EP3, 1);
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets main post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_MPD] = (0x7f & pd);
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_MD1] = 0x7f & (div >> 16);
+ regs[R_MD2] = 0xff & (div >> 8);
+ regs[R_MD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq)
+{
+ /* sets cal post divider & divider bytes, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 d, pd;
+ u32 div;
+
+ int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_CPD] = pd;
+
+ div = ((d * (freq / 1000)) << 7) / 125;
+
+ regs[R_CD1] = 0x7f & (div >> 16);
+ regs[R_CD2] = 0xff & (div >> 8);
+ regs[R_CD3] = 0xff & div;
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets bp filter bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EP1] &= ~0x07; /* clear bp filter bits */
+ regs[R_EP1] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets K & M bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EB13] &= ~0x7c; /* clear k & m bits */
+ regs[R_EB13] |= (0x7c & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf band bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EP2] &= ~0xe0; /* clear rf band bits */
+ regs[R_EP2] |= (0xe0 & (val << 5));
+fail:
+ return ret;
+}
+
+int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets gain taper bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EP2] &= ~0x1f; /* clear gain taper bits */
+ regs[R_EP2] |= (0x1f & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets IR Meas bits, but does not write them */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EP5] &= ~0x07;
+ regs[R_EP5] |= (0x07 & val);
+fail:
+ return ret;
+}
+
+int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq)
+{
+ /* sets rf cal byte (RFC_Cprog), but does not write it */
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u8 val;
+
+ int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val);
+ /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range
+ * for frequencies above 61.1 MHz. In these cases, the internal RF
+ * tracking filters calibration mechanism is used.
+ *
+ * There is no need to warn the user about this.
+ */
+ if (ret < 0)
+ goto fail;
+
+ regs[R_EB14] = val;
+fail:
+ return ret;
+}
+
+int _tda_printk(struct tda18271_priv *state, const char *level,
+ const char *func, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+ int rtn;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (state)
+ rtn = printk("%s%s: [%d-%04x|%c] %pV",
+ level, func, i2c_adapter_id(state->i2c_props.adap),
+ state->i2c_props.addr,
+ (state->role == TDA18271_MASTER) ? 'M' : 'S',
+ &vaf);
+ else
+ rtn = printk("%s%s: %pV", level, func, &vaf);
+
+ va_end(args);
+
+ return rtn;
+}
diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c
new file mode 100644
index 000000000000..72c26fd77922
--- /dev/null
+++ b/drivers/media/tuners/tda18271-fe.c
@@ -0,0 +1,1362 @@
+/*
+ tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include "tda18271-priv.h"
+
+int tda18271_debug;
+module_param_named(debug, tda18271_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level "
+ "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))");
+
+static int tda18271_cal_on_startup = -1;
+module_param_named(cal, tda18271_cal_on_startup, int, 0644);
+MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup");
+
+static DEFINE_MUTEX(tda18271_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
+/*---------------------------------------------------------------------*/
+
+static int tda18271_toggle_output(struct dvb_frontend *fe, int standby)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0,
+ priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0,
+ priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0);
+
+ if (tda_fail(ret))
+ goto fail;
+
+ tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n",
+ standby ? "standby" : "active",
+ priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on",
+ priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on");
+fail:
+ return ret;
+}
+
+/*---------------------------------------------------------------------*/
+
+static inline int charge_pump_source(struct dvb_frontend *fe, int force)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ return tda18271_charge_pump_source(fe,
+ (priv->role == TDA18271_SLAVE) ?
+ TDA18271_CAL_PLL :
+ TDA18271_MAIN_PLL, force);
+}
+
+static inline void tda18271_set_if_notch(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ regs[R_MPD] &= ~0x80; /* IF notch = 0 */
+ break;
+ case TDA18271_DIGITAL:
+ regs[R_MPD] |= 0x80; /* IF notch = 1 */
+ break;
+ }
+}
+
+static int tda18271_channel_configuration(struct dvb_frontend *fe,
+ struct tda18271_std_map_item *map,
+ u32 freq, u32 bw)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+ u32 N;
+
+ /* update TV broadcast parameters */
+
+ /* set standard */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= (map->agc_mode << 3) | map->std;
+
+ if (priv->id == TDA18271HDC2) {
+ /* set rfagc to high speed mode */
+ regs[R_EP3] &= ~0x04;
+ }
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+ regs[R_EP4] |= (map->if_lvl << 2);
+
+ /* update FM_RFn */
+ regs[R_EP4] &= ~0x80;
+ regs[R_EP4] |= map->fm_rfn << 7;
+
+ /* update rf top / if top */
+ regs[R_EB22] = 0x00;
+ regs[R_EB22] |= map->rfagc_top;
+ ret = tda18271_write_regs(fe, R_EB22, 1);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* --------------------------------------------------------------- */
+
+ /* disable Power Level Indicator */
+ regs[R_EP1] |= 0x40;
+
+ /* make sure thermometer is off */
+ regs[R_TM] &= ~0x10;
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_ir_measure(fe, &freq);
+
+ tda18271_calc_bp_filter(fe, &freq);
+
+ tda18271_calc_rf_band(fe, &freq);
+
+ tda18271_calc_gain_taper(fe, &freq);
+
+ /* --------------------------------------------------------------- */
+
+ /* dual tuner and agc1 extra configuration */
+
+ switch (priv->role) {
+ case TDA18271_MASTER:
+ regs[R_EB1] |= 0x04; /* main vco */
+ break;
+ case TDA18271_SLAVE:
+ regs[R_EB1] &= ~0x04; /* cal vco */
+ break;
+ }
+
+ /* agc1 always active */
+ regs[R_EB1] &= ~0x02;
+
+ /* agc1 has priority on agc2 */
+ regs[R_EB1] &= ~0x01;
+
+ ret = tda18271_write_regs(fe, R_EB1, 1);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* --------------------------------------------------------------- */
+
+ N = map->if_freq * 1000 + freq;
+
+ switch (priv->role) {
+ case TDA18271_MASTER:
+ tda18271_calc_main_pll(fe, N);
+ tda18271_set_if_notch(fe);
+ tda18271_write_regs(fe, R_MPD, 4);
+ break;
+ case TDA18271_SLAVE:
+ tda18271_calc_cal_pll(fe, N);
+ tda18271_write_regs(fe, R_CPD, 4);
+
+ regs[R_MPD] = regs[R_CPD] & 0x7f;
+ tda18271_set_if_notch(fe);
+ tda18271_write_regs(fe, R_MPD, 1);
+ break;
+ }
+
+ ret = tda18271_write_regs(fe, R_TM, 7);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* force charge pump source */
+ charge_pump_source(fe, 1);
+
+ msleep(1);
+
+ /* return pll to normal operation */
+ charge_pump_source(fe, 0);
+
+ msleep(20);
+
+ if (priv->id == TDA18271HDC2) {
+ /* set rfagc to normal speed mode */
+ if (map->fm_rfn)
+ regs[R_EP3] &= ~0x04;
+ else
+ regs[R_EP3] |= 0x04;
+ ret = tda18271_write_regs(fe, R_EP3, 1);
+ }
+fail:
+ return ret;
+}
+
+static int tda18271_read_thermometer(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int tm;
+
+ /* switch thermometer on */
+ regs[R_TM] |= 0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+
+ if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) ||
+ (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) {
+
+ if ((regs[R_TM] & 0x20) == 0x20)
+ regs[R_TM] &= ~0x20;
+ else
+ regs[R_TM] |= 0x20;
+
+ tda18271_write_regs(fe, R_TM, 1);
+
+ msleep(10); /* temperature sensing */
+
+ /* read thermometer info */
+ tda18271_read_regs(fe);
+ }
+
+ tm = tda18271_lookup_thermometer(fe);
+
+ /* switch thermometer off */
+ regs[R_TM] &= ~0x10;
+ tda18271_write_regs(fe, R_TM, 1);
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ return tm;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe,
+ u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ unsigned char *regs = priv->tda18271_regs;
+ int i, ret;
+ u8 tm_current, dc_over_dt, rf_tab;
+ s32 rfcal_comp, approx;
+
+ /* power up */
+ ret = tda18271_set_standby_mode(fe, 0, 0, 0);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* read die current temperature */
+ tm_current = tda18271_read_thermometer(fe);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_rf_cal(fe, &freq);
+ rf_tab = regs[R_EB14];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+ if (tda_fail(i))
+ return i;
+
+ if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) {
+ approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) +
+ map[i].rf_b1 + rf_tab;
+ } else {
+ approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) +
+ map[i].rf_b2 + rf_tab;
+ }
+
+ if (approx < 0)
+ approx = 0;
+ if (approx > 255)
+ approx = 255;
+
+ tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt);
+
+ /* calculate temperature compensation */
+ rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000;
+
+ regs[R_EB14] = (unsigned char)(approx + rfcal_comp);
+ ret = tda18271_write_regs(fe, R_EB14, 1);
+fail:
+ return ret;
+}
+
+static int tda18271_por(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+
+ /* power up detector 1 */
+ regs[R_EB12] &= ~0x20;
+ ret = tda18271_write_regs(fe, R_EB12, 1);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EB18] &= ~0x80; /* turn agc1 loop on */
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ ret = tda18271_write_regs(fe, R_EB18, 1);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */
+
+ /* POR mode */
+ ret = tda18271_set_standby_mode(fe, 1, 0, 0);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* disable 1.5 MHz low pass filter */
+ regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */
+ regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */
+ ret = tda18271_write_regs(fe, R_EB21, 3);
+fail:
+ return ret;
+}
+
+static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ u32 N;
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* switch off agc1 */
+ regs[R_EP3] |= 0x40; /* sm_lt = 1 */
+
+ regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ /* frequency dependent parameters */
+
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_km(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP1, 3);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* main pll charge pump source */
+ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1);
+
+ /* cal pll charge pump source */
+ tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1);
+
+ /* force dcdc converter to 0 V */
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* disable plls lock */
+ regs[R_EB20] &= ~0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set CAL mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+ tda18271_write_regs(fe, R_EP4, 2);
+
+ /* --------------------------------------------------------------- */
+
+ /* set the internal calibration signal */
+ N = freq;
+
+ tda18271_calc_cal_pll(fe, N);
+ tda18271_write_regs(fe, R_CPD, 4);
+
+ /* downconvert internal calibration */
+ N += 1000000;
+
+ tda18271_calc_main_pll(fe, N);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* --------------------------------------------------------------- */
+
+ /* normal operation for the main pll */
+ tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0);
+
+ /* normal operation for the cal pll */
+ tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0);
+
+ msleep(10); /* plls locking */
+
+ /* launch the rf tracking filters calibration */
+ regs[R_EB20] |= 0x20;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ msleep(60); /* calibration */
+
+ /* --------------------------------------------------------------- */
+
+ /* set CAL mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* switch on agc1 */
+ regs[R_EP3] &= ~0x40; /* sm_lt = 0 */
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ tda18271_write_regs(fe, R_EB18, 1);
+
+ tda18271_write_regs(fe, R_EP3, 2);
+
+ /* synchronization */
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* get calibration result */
+ tda18271_read_extended(fe);
+
+ return regs[R_EB14];
+}
+
+static int tda18271_powerscan(struct dvb_frontend *fe,
+ u32 *freq_in, u32 *freq_out)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int sgn, bcal, count, wait, ret;
+ u8 cid_target;
+ u16 count_limit;
+ u32 freq;
+
+ freq = *freq_in;
+
+ tda18271_calc_rf_band(fe, &freq);
+ tda18271_calc_rf_cal(fe, &freq);
+ tda18271_calc_gain_taper(fe, &freq);
+ tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ /* downconvert frequency */
+ freq += 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ msleep(5); /* pll locking */
+
+ /* detection mode */
+ regs[R_EP4] &= ~0x03;
+ regs[R_EP4] |= 0x01;
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ ret = tda18271_read_extended(fe);
+ if (tda_fail(ret))
+ return ret;
+
+ /* algorithm initialization */
+ sgn = 1;
+ *freq_out = *freq_in;
+ bcal = 0;
+ count = 0;
+ wait = false;
+
+ while ((regs[R_EB10] & 0x3f) < cid_target) {
+ /* downconvert updated freq to 1 MHz */
+ freq = *freq_in + (sgn * count) + 1000000;
+
+ tda18271_calc_main_pll(fe, freq);
+ tda18271_write_regs(fe, R_MPD, 4);
+
+ if (wait) {
+ msleep(5); /* pll locking */
+ wait = false;
+ } else
+ udelay(100); /* pll locking */
+
+ /* launch power detection measurement */
+ tda18271_write_regs(fe, R_EP2, 1);
+
+ /* read power detection info, stored in EB10 */
+ ret = tda18271_read_extended(fe);
+ if (tda_fail(ret))
+ return ret;
+
+ count += 200;
+
+ if (count <= count_limit)
+ continue;
+
+ if (sgn <= 0)
+ break;
+
+ sgn = -1 * sgn;
+ count = 200;
+ wait = true;
+ }
+
+ if ((regs[R_EB10] & 0x3f) >= cid_target) {
+ bcal = 1;
+ *freq_out = freq - 1000000;
+ } else
+ bcal = 0;
+
+ tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n",
+ bcal, *freq_in, *freq_out, freq);
+
+ return bcal;
+}
+
+static int tda18271_powerscan_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+
+ /* set standard to digital */
+ regs[R_EP3] &= ~0x1f; /* clear std bits */
+ regs[R_EP3] |= 0x12;
+
+ /* set cal mode to normal */
+ regs[R_EP4] &= ~0x03;
+
+ /* update IF output level */
+ regs[R_EP4] &= ~0x1c; /* clear if level bits */
+
+ ret = tda18271_write_regs(fe, R_EP3, 2);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */
+ ret = tda18271_write_regs(fe, R_EB18, 1);
+ if (tda_fail(ret))
+ goto fail;
+
+ regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */
+
+ /* 1.5 MHz low pass filter */
+ regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */
+ regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */
+
+ ret = tda18271_write_regs(fe, R_EB21, 3);
+fail:
+ return ret;
+}
+
+static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state;
+ unsigned char *regs = priv->tda18271_regs;
+ int bcal, rf, i;
+ s32 divisor, dividend;
+#define RF1 0
+#define RF2 1
+#define RF3 2
+ u32 rf_default[3];
+ u32 rf_freq[3];
+ s32 prog_cal[3];
+ s32 prog_tab[3];
+
+ i = tda18271_lookup_rf_band(fe, &freq, NULL);
+
+ if (tda_fail(i))
+ return i;
+
+ rf_default[RF1] = 1000 * map[i].rf1_def;
+ rf_default[RF2] = 1000 * map[i].rf2_def;
+ rf_default[RF3] = 1000 * map[i].rf3_def;
+
+ for (rf = RF1; rf <= RF3; rf++) {
+ if (0 == rf_default[rf])
+ return 0;
+ tda_cal("freq = %d, rf = %d\n", freq, rf);
+
+ /* look for optimized calibration frequency */
+ bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]);
+ if (tda_fail(bcal))
+ return bcal;
+
+ tda18271_calc_rf_cal(fe, &rf_freq[rf]);
+ prog_tab[rf] = (s32)regs[R_EB14];
+
+ if (1 == bcal)
+ prog_cal[rf] =
+ (s32)tda18271_calibrate_rf(fe, rf_freq[rf]);
+ else
+ prog_cal[rf] = prog_tab[rf];
+
+ switch (rf) {
+ case RF1:
+ map[i].rf_a1 = 0;
+ map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]);
+ map[i].rf1 = rf_freq[RF1] / 1000;
+ break;
+ case RF2:
+ dividend = (prog_cal[RF2] - prog_tab[RF2] -
+ prog_cal[RF1] + prog_tab[RF1]);
+ divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000;
+ map[i].rf_a1 = (dividend / divisor);
+ map[i].rf2 = rf_freq[RF2] / 1000;
+ break;
+ case RF3:
+ dividend = (prog_cal[RF3] - prog_tab[RF3] -
+ prog_cal[RF2] + prog_tab[RF2]);
+ divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000;
+ map[i].rf_a2 = (dividend / divisor);
+ map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]);
+ map[i].rf3 = rf_freq[RF3] / 1000;
+ break;
+ default:
+ BUG();
+ }
+ }
+
+ return 0;
+}
+
+static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned int i;
+ int ret;
+
+ tda_info("tda18271: performing RF tracking filter calibration\n");
+
+ /* wait for die temperature stabilization */
+ msleep(200);
+
+ ret = tda18271_powerscan_init(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* rf band calibration */
+ for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) {
+ ret =
+ tda18271_rf_tracking_filters_init(fe, 1000 *
+ priv->rf_cal_state[i].rfmax);
+ if (tda_fail(ret))
+ goto fail;
+ }
+
+ priv->tm_rfcal = tda18271_read_thermometer(fe);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271c2_rf_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+
+ /* test RF_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x10) == 0)
+ priv->cal_initialized = false;
+
+ if (priv->cal_initialized)
+ return 0;
+
+ ret = tda18271_calc_rf_filter_curve(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ ret = tda18271_por(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ tda_info("tda18271: RF tracking filter calibration complete\n");
+
+ priv->cal_initialized = true;
+ goto end;
+fail:
+ tda_info("tda18271: RF tracking filter calibration failed!\n");
+end:
+ return ret;
+}
+
+static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe,
+ u32 freq, u32 bw)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+ u32 N = 0;
+
+ /* calculate bp filter */
+ tda18271_calc_bp_filter(fe, &freq);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x60;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x60;
+ tda18271_write_regs(fe, R_EB7, 1);
+
+ regs[R_EB14] = 0x00;
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ regs[R_EB20] = 0xcc;
+ tda18271_write_regs(fe, R_EB20, 1);
+
+ /* set cal mode to RF tracking filter calibration */
+ regs[R_EP4] |= 0x03;
+
+ /* calculate cal pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 1250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2;
+ break;
+ }
+
+ tda18271_calc_cal_pll(fe, N);
+
+ /* calculate main pll */
+
+ switch (priv->mode) {
+ case TDA18271_ANALOG:
+ N = freq - 250000;
+ break;
+ case TDA18271_DIGITAL:
+ N = freq + bw / 2 + 1000000;
+ break;
+ }
+
+ tda18271_calc_main_pll(fe, N);
+
+ ret = tda18271_write_regs(fe, R_EP3, 11);
+ if (tda_fail(ret))
+ return ret;
+
+ msleep(5); /* RF tracking filter calibration initialization */
+
+ /* search for K,M,CO for RF calibration */
+ tda18271_calc_km(fe, &freq);
+ tda18271_write_regs(fe, R_EB13, 1);
+
+ /* search for rf band */
+ tda18271_calc_rf_band(fe, &freq);
+
+ /* search for gain taper */
+ tda18271_calc_gain_taper(fe, &freq);
+
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+ tda18271_write_regs(fe, R_EP2, 1);
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ regs[R_EB4] &= 0x07;
+ regs[R_EB4] |= 0x40;
+ tda18271_write_regs(fe, R_EB4, 1);
+
+ regs[R_EB7] = 0x40;
+ tda18271_write_regs(fe, R_EB7, 1);
+ msleep(10); /* pll locking */
+
+ regs[R_EB20] = 0xec;
+ tda18271_write_regs(fe, R_EB20, 1);
+ msleep(60); /* RF tracking filter calibration completion */
+
+ regs[R_EP4] &= ~0x03; /* set cal mode to normal */
+ tda18271_write_regs(fe, R_EP4, 1);
+
+ tda18271_write_regs(fe, R_EP1, 1);
+
+ /* RF tracking filter correction for VHF_Low band */
+ if (0 == tda18271_calc_rf_cal(fe, &freq))
+ tda18271_write_regs(fe, R_EB14, 1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_ir_cal_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ int ret;
+
+ ret = tda18271_read_regs(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* test IR_CAL_OK to see if we need init */
+ if ((regs[R_EP1] & 0x08) == 0)
+ ret = tda18271_init_regs(fe);
+fail:
+ return ret;
+}
+
+static int tda18271_init(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret;
+
+ mutex_lock(&priv->lock);
+
+ /* full power up */
+ ret = tda18271_set_standby_mode(fe, 0, 0, 0);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* initialization */
+ ret = tda18271_ir_cal_init(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ if (priv->id == TDA18271HDC2)
+ tda18271c2_rf_cal_init(fe);
+fail:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int tda18271_sleep(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret;
+
+ mutex_lock(&priv->lock);
+
+ /* enter standby mode, with required output features enabled */
+ ret = tda18271_toggle_output(fe, 1);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_agc(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ switch (priv->config) {
+ case 0:
+ /* no external agc configuration required */
+ if (tda18271_debug & DBG_ADV)
+ tda_dbg("no agc configuration provided\n");
+ break;
+ case 3:
+ /* switch with GPIO of saa713x */
+ tda_dbg("invoking callback\n");
+ if (fe->callback)
+ ret = fe->callback(priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE,
+ priv->mode);
+ break;
+ case 1:
+ case 2:
+ default:
+ /* n/a - currently not supported */
+ tda_err("unsupported configuration: %d\n", priv->config);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int tda18271_tune(struct dvb_frontend *fe,
+ struct tda18271_std_map_item *map, u32 freq, u32 bw)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ int ret;
+
+ tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",
+ freq, map->if_freq, bw, map->agc_mode, map->std);
+
+ ret = tda18271_agc(fe);
+ if (tda_fail(ret))
+ tda_warn("failed to configure agc\n");
+
+ ret = tda18271_init(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ mutex_lock(&priv->lock);
+
+ switch (priv->id) {
+ case TDA18271HDC1:
+ tda18271c1_rf_tracking_filter_calibration(fe, freq, bw);
+ break;
+ case TDA18271HDC2:
+ tda18271c2_rf_tracking_filters_correction(fe, freq);
+ break;
+ }
+ ret = tda18271_channel_configuration(fe, map, freq, bw);
+
+ mutex_unlock(&priv->lock);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int tda18271_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ u32 bw = c->bandwidth_hz;
+ u32 freq = c->frequency;
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ struct tda18271_std_map_item *map;
+ int ret;
+
+ priv->mode = TDA18271_DIGITAL;
+
+ switch (delsys) {
+ case SYS_ATSC:
+ map = &std_map->atsc_6;
+ bw = 6000000;
+ break;
+ case SYS_ISDBT:
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ if (bw <= 6000000) {
+ map = &std_map->dvbt_6;
+ } else if (bw <= 7000000) {
+ map = &std_map->dvbt_7;
+ } else {
+ map = &std_map->dvbt_8;
+ }
+ break;
+ case SYS_DVBC_ANNEX_B:
+ bw = 6000000;
+ /* falltrough */
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ if (bw <= 6000000) {
+ map = &std_map->qam_6;
+ } else if (bw <= 7000000) {
+ map = &std_map->qam_7;
+ } else {
+ map = &std_map->qam_8;
+ }
+ break;
+ default:
+ tda_warn("modulation type not supported!\n");
+ return -EINVAL;
+ }
+
+ /* When tuning digital, the analog demod must be tri-stated */
+ if (fe->ops.analog_ops.standby)
+ fe->ops.analog_ops.standby(fe);
+
+ ret = tda18271_tune(fe, map, freq, bw);
+
+ if (tda_fail(ret))
+ goto fail;
+
+ priv->if_freq = map->if_freq;
+ priv->frequency = freq;
+ priv->bandwidth = bw;
+fail:
+ return ret;
+}
+
+static int tda18271_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std_map = &priv->std;
+ struct tda18271_std_map_item *map;
+ char *mode;
+ int ret;
+ u32 freq = params->frequency * 125 *
+ ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2;
+
+ priv->mode = TDA18271_ANALOG;
+
+ if (params->mode == V4L2_TUNER_RADIO) {
+ map = &std_map->fm_radio;
+ mode = "fm";
+ } else if (params->std & V4L2_STD_MN) {
+ map = &std_map->atv_mn;
+ mode = "MN";
+ } else if (params->std & V4L2_STD_B) {
+ map = &std_map->atv_b;
+ mode = "B";
+ } else if (params->std & V4L2_STD_GH) {
+ map = &std_map->atv_gh;
+ mode = "GH";
+ } else if (params->std & V4L2_STD_PAL_I) {
+ map = &std_map->atv_i;
+ mode = "I";
+ } else if (params->std & V4L2_STD_DK) {
+ map = &std_map->atv_dk;
+ mode = "DK";
+ } else if (params->std & V4L2_STD_SECAM_L) {
+ map = &std_map->atv_l;
+ mode = "L";
+ } else if (params->std & V4L2_STD_SECAM_LC) {
+ map = &std_map->atv_lc;
+ mode = "L'";
+ } else {
+ map = &std_map->atv_i;
+ mode = "xx";
+ }
+
+ tda_dbg("setting tda18271 to system %s\n", mode);
+
+ ret = tda18271_tune(fe, map, freq, 0);
+
+ if (tda_fail(ret))
+ goto fail;
+
+ priv->if_freq = map->if_freq;
+ priv->frequency = freq;
+ priv->bandwidth = 0;
+fail:
+ return ret;
+}
+
+static int tda18271_release(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&tda18271_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ *frequency = (u32)priv->if_freq * 1000;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+#define tda18271_update_std(std_cfg, name) do { \
+ if (map->std_cfg.if_freq + \
+ map->std_cfg.agc_mode + map->std_cfg.std + \
+ map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \
+ tda_dbg("Using custom std config for %s\n", name); \
+ memcpy(&std->std_cfg, &map->std_cfg, \
+ sizeof(struct tda18271_std_map_item)); \
+ } } while (0)
+
+#define tda18271_dump_std_item(std_cfg, name) do { \
+ tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \
+ "if_lvl = %d, rfagc_top = 0x%02x\n", \
+ name, std->std_cfg.if_freq, \
+ std->std_cfg.agc_mode, std->std_cfg.std, \
+ std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \
+ } while (0)
+
+static int tda18271_dump_std_map(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ tda_dbg("========== STANDARD MAP SETTINGS ==========\n");
+ tda18271_dump_std_item(fm_radio, " fm ");
+ tda18271_dump_std_item(atv_b, "atv b ");
+ tda18271_dump_std_item(atv_dk, "atv dk");
+ tda18271_dump_std_item(atv_gh, "atv gh");
+ tda18271_dump_std_item(atv_i, "atv i ");
+ tda18271_dump_std_item(atv_l, "atv l ");
+ tda18271_dump_std_item(atv_lc, "atv l'");
+ tda18271_dump_std_item(atv_mn, "atv mn");
+ tda18271_dump_std_item(atsc_6, "atsc 6");
+ tda18271_dump_std_item(dvbt_6, "dvbt 6");
+ tda18271_dump_std_item(dvbt_7, "dvbt 7");
+ tda18271_dump_std_item(dvbt_8, "dvbt 8");
+ tda18271_dump_std_item(qam_6, "qam 6 ");
+ tda18271_dump_std_item(qam_8, "qam 8 ");
+
+ return 0;
+}
+
+static int tda18271_update_std_map(struct dvb_frontend *fe,
+ struct tda18271_std_map *map)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ struct tda18271_std_map *std = &priv->std;
+
+ if (!map)
+ return -EINVAL;
+
+ tda18271_update_std(fm_radio, "fm");
+ tda18271_update_std(atv_b, "atv b");
+ tda18271_update_std(atv_dk, "atv dk");
+ tda18271_update_std(atv_gh, "atv gh");
+ tda18271_update_std(atv_i, "atv i");
+ tda18271_update_std(atv_l, "atv l");
+ tda18271_update_std(atv_lc, "atv l'");
+ tda18271_update_std(atv_mn, "atv mn");
+ tda18271_update_std(atsc_6, "atsc 6");
+ tda18271_update_std(dvbt_6, "dvbt 6");
+ tda18271_update_std(dvbt_7, "dvbt 7");
+ tda18271_update_std(dvbt_8, "dvbt 8");
+ tda18271_update_std(qam_6, "qam 6");
+ tda18271_update_std(qam_8, "qam 8");
+
+ return 0;
+}
+
+static int tda18271_get_id(struct dvb_frontend *fe)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+ unsigned char *regs = priv->tda18271_regs;
+ char *name;
+ int ret;
+
+ mutex_lock(&priv->lock);
+ ret = tda18271_read_regs(fe);
+ mutex_unlock(&priv->lock);
+
+ if (ret) {
+ tda_info("Error reading device ID @ %d-%04x, bailing out.\n",
+ i2c_adapter_id(priv->i2c_props.adap),
+ priv->i2c_props.addr);
+ return -EIO;
+ }
+
+ switch (regs[R_ID] & 0x7f) {
+ case 3:
+ name = "TDA18271HD/C1";
+ priv->id = TDA18271HDC1;
+ break;
+ case 4:
+ name = "TDA18271HD/C2";
+ priv->id = TDA18271HDC2;
+ break;
+ default:
+ tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n",
+ regs[R_ID], i2c_adapter_id(priv->i2c_props.adap),
+ priv->i2c_props.addr);
+ return -EINVAL;
+ }
+
+ tda_info("%s detected @ %d-%04x\n", name,
+ i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr);
+
+ return 0;
+}
+
+static int tda18271_setup_configuration(struct dvb_frontend *fe,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = fe->tuner_priv;
+
+ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+ priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
+ priv->config = (cfg) ? cfg->config : 0;
+ priv->small_i2c = (cfg) ?
+ cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT;
+ priv->output_opt = (cfg) ?
+ cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON;
+
+ return 0;
+}
+
+static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg)
+{
+ /* tda18271_cal_on_startup == -1 when cal module option is unset */
+ return ((tda18271_cal_on_startup == -1) ?
+ /* honor configuration setting */
+ ((cfg) && (cfg->rf_cal_on_startup)) :
+ /* module option overrides configuration setting */
+ (tda18271_cal_on_startup)) ? 1 : 0;
+}
+
+static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg;
+
+ tda18271_setup_configuration(fe, cfg);
+
+ if (tda18271_need_cal_on_startup(cfg))
+ tda18271_init(fe);
+
+ /* override default std map with values in config struct */
+ if ((cfg) && (cfg->std_map))
+ tda18271_update_std_map(fe, cfg->std_map);
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops tda18271_tuner_ops = {
+ .info = {
+ .name = "NXP TDA18271HD",
+ .frequency_min = 45000000,
+ .frequency_max = 864000000,
+ .frequency_step = 62500
+ },
+ .init = tda18271_init,
+ .sleep = tda18271_sleep,
+ .set_params = tda18271_set_params,
+ .set_analog_params = tda18271_set_analog_params,
+ .release = tda18271_release,
+ .set_config = tda18271_set_config,
+ .get_frequency = tda18271_get_frequency,
+ .get_bandwidth = tda18271_get_bandwidth,
+ .get_if_frequency = tda18271_get_if_frequency,
+};
+
+struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ struct tda18271_priv *priv = NULL;
+ int instance, ret;
+
+ mutex_lock(&tda18271_list_mutex);
+
+ instance = hybrid_tuner_request_state(struct tda18271_priv, priv,
+ hybrid_tuner_instance_list,
+ i2c, addr, "tda18271");
+ switch (instance) {
+ case 0:
+ goto fail;
+ case 1:
+ /* new tuner instance */
+ fe->tuner_priv = priv;
+
+ tda18271_setup_configuration(fe, cfg);
+
+ priv->cal_initialized = false;
+ mutex_init(&priv->lock);
+
+ ret = tda18271_get_id(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ ret = tda18271_assign_map_layout(fe);
+ if (tda_fail(ret))
+ goto fail;
+
+ /* if delay_cal is set, delay IR & RF calibration until init()
+ * module option 'cal' overrides this delay */
+ if ((cfg->delay_cal) && (!tda18271_need_cal_on_startup(cfg)))
+ break;
+
+ mutex_lock(&priv->lock);
+ tda18271_init_regs(fe);
+
+ if ((tda18271_need_cal_on_startup(cfg)) &&
+ (priv->id == TDA18271HDC2))
+ tda18271c2_rf_cal_init(fe);
+
+ /* enter standby mode, with required output features enabled */
+ ret = tda18271_toggle_output(fe, 1);
+ tda_fail(ret);
+
+ mutex_unlock(&priv->lock);
+ break;
+ default:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+
+ /* allow dvb driver to override configuration settings */
+ if (cfg) {
+ if (cfg->gate != TDA18271_GATE_ANALOG)
+ priv->gate = cfg->gate;
+ if (cfg->role)
+ priv->role = cfg->role;
+ if (cfg->config)
+ priv->config = cfg->config;
+ if (cfg->small_i2c)
+ priv->small_i2c = cfg->small_i2c;
+ if (cfg->output_opt)
+ priv->output_opt = cfg->output_opt;
+ if (cfg->std_map)
+ tda18271_update_std_map(fe, cfg->std_map);
+ }
+ if (tda18271_need_cal_on_startup(cfg))
+ tda18271_init(fe);
+ break;
+ }
+
+ /* override default std map with values in config struct */
+ if ((cfg) && (cfg->std_map))
+ tda18271_update_std_map(fe, cfg->std_map);
+
+ mutex_unlock(&tda18271_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (tda18271_debug & (DBG_MAP | DBG_ADV))
+ tda18271_dump_std_map(fe);
+
+ return fe;
+fail:
+ mutex_unlock(&tda18271_list_mutex);
+
+ tda18271_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(tda18271_attach);
+MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.4");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c
index fb881c667c94..fb881c667c94 100644
--- a/drivers/media/common/tuners/tda18271-maps.c
+++ b/drivers/media/tuners/tda18271-maps.c
diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h
index 454c152ccaa0..454c152ccaa0 100644
--- a/drivers/media/common/tuners/tda18271-priv.h
+++ b/drivers/media/tuners/tda18271-priv.h
diff --git a/drivers/media/tuners/tda18271.h b/drivers/media/tuners/tda18271.h
new file mode 100644
index 000000000000..89b6c6d93fec
--- /dev/null
+++ b/drivers/media/tuners/tda18271.h
@@ -0,0 +1,139 @@
+/*
+ tda18271.h - header for the Philips / NXP TDA18271 silicon tuner
+
+ Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __TDA18271_H__
+#define __TDA18271_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+struct tda18271_std_map_item {
+ u16 if_freq;
+
+ /* EP3[4:3] */
+ unsigned int agc_mode:2;
+ /* EP3[2:0] */
+ unsigned int std:3;
+ /* EP4[7] */
+ unsigned int fm_rfn:1;
+ /* EP4[4:2] */
+ unsigned int if_lvl:3;
+ /* EB22[6:0] */
+ unsigned int rfagc_top:7;
+};
+
+struct tda18271_std_map {
+ struct tda18271_std_map_item fm_radio;
+ struct tda18271_std_map_item atv_b;
+ struct tda18271_std_map_item atv_dk;
+ struct tda18271_std_map_item atv_gh;
+ struct tda18271_std_map_item atv_i;
+ struct tda18271_std_map_item atv_l;
+ struct tda18271_std_map_item atv_lc;
+ struct tda18271_std_map_item atv_mn;
+ struct tda18271_std_map_item atsc_6;
+ struct tda18271_std_map_item dvbt_6;
+ struct tda18271_std_map_item dvbt_7;
+ struct tda18271_std_map_item dvbt_8;
+ struct tda18271_std_map_item qam_6;
+ struct tda18271_std_map_item qam_7;
+ struct tda18271_std_map_item qam_8;
+};
+
+enum tda18271_role {
+ TDA18271_MASTER = 0,
+ TDA18271_SLAVE,
+};
+
+enum tda18271_i2c_gate {
+ TDA18271_GATE_AUTO = 0,
+ TDA18271_GATE_ANALOG,
+ TDA18271_GATE_DIGITAL,
+};
+
+enum tda18271_output_options {
+ /* slave tuner output & loop thru & xtal oscillator always on */
+ TDA18271_OUTPUT_LT_XT_ON = 0,
+
+ /* slave tuner output loop thru off */
+ TDA18271_OUTPUT_LT_OFF = 1,
+
+ /* xtal oscillator off */
+ TDA18271_OUTPUT_XT_OFF = 2,
+};
+
+enum tda18271_small_i2c {
+ TDA18271_39_BYTE_CHUNK_INIT = 0,
+ TDA18271_16_BYTE_CHUNK_INIT = 16,
+ TDA18271_08_BYTE_CHUNK_INIT = 8,
+ TDA18271_03_BYTE_CHUNK_INIT = 3,
+};
+
+struct tda18271_config {
+ /* override default if freq / std settings (optional) */
+ struct tda18271_std_map *std_map;
+
+ /* master / slave tuner: master uses main pll, slave uses cal pll */
+ enum tda18271_role role;
+
+ /* use i2c gate provided by analog or digital demod */
+ enum tda18271_i2c_gate gate;
+
+ /* output options that can be disabled */
+ enum tda18271_output_options output_opt;
+
+ /* some i2c providers can't write all 39 registers at once */
+ enum tda18271_small_i2c small_i2c;
+
+ /* force rf tracking filter calibration on startup */
+ unsigned int rf_cal_on_startup:1;
+
+ /* prevent any register access during attach(),
+ * delaying both IR & RF calibration until init()
+ * module option 'cal' overrides this delay */
+ unsigned int delay_cal:1;
+
+ /* interface to saa713x / tda829x */
+ unsigned int config;
+};
+
+#define TDA18271_CALLBACK_CMD_AGC_ENABLE 0
+
+enum tda18271_mode {
+ TDA18271_ANALOG = 0,
+ TDA18271_DIGITAL,
+};
+
+#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg);
+#else
+static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe,
+ u8 addr,
+ struct i2c_adapter *i2c,
+ struct tda18271_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* __TDA18271_H__ */
diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/tuners/tda827x.c
index a0d176267470..a0d176267470 100644
--- a/drivers/media/common/tuners/tda827x.c
+++ b/drivers/media/tuners/tda827x.c
diff --git a/drivers/media/common/tuners/tda827x.h b/drivers/media/tuners/tda827x.h
index 7d72ce0a0c2d..7d72ce0a0c2d 100644
--- a/drivers/media/common/tuners/tda827x.h
+++ b/drivers/media/tuners/tda827x.h
diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/tuners/tda8290.c
index 8c4852114eeb..8c4852114eeb 100644
--- a/drivers/media/common/tuners/tda8290.c
+++ b/drivers/media/tuners/tda8290.c
diff --git a/drivers/media/common/tuners/tda8290.h b/drivers/media/tuners/tda8290.h
index 7e288b26fcc3..7e288b26fcc3 100644
--- a/drivers/media/common/tuners/tda8290.h
+++ b/drivers/media/tuners/tda8290.h
diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/tuners/tda9887.c
index cdb645d57438..cdb645d57438 100644
--- a/drivers/media/common/tuners/tda9887.c
+++ b/drivers/media/tuners/tda9887.c
diff --git a/drivers/media/common/tuners/tda9887.h b/drivers/media/tuners/tda9887.h
index acc419e8c4fc..acc419e8c4fc 100644
--- a/drivers/media/common/tuners/tda9887.h
+++ b/drivers/media/tuners/tda9887.h
diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/tuners/tea5761.c
index bf78cb9fc52c..bf78cb9fc52c 100644
--- a/drivers/media/common/tuners/tea5761.c
+++ b/drivers/media/tuners/tea5761.c
diff --git a/drivers/media/common/tuners/tea5761.h b/drivers/media/tuners/tea5761.h
index 2e2ff82c95a4..2e2ff82c95a4 100644
--- a/drivers/media/common/tuners/tea5761.h
+++ b/drivers/media/tuners/tea5761.h
diff --git a/drivers/media/common/tuners/tea5767.c b/drivers/media/tuners/tea5767.c
index 36e85d81acb2..36e85d81acb2 100644
--- a/drivers/media/common/tuners/tea5767.c
+++ b/drivers/media/tuners/tea5767.c
diff --git a/drivers/media/common/tuners/tea5767.h b/drivers/media/tuners/tea5767.h
index d30ab1b483de..d30ab1b483de 100644
--- a/drivers/media/common/tuners/tea5767.h
+++ b/drivers/media/tuners/tea5767.h
diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c
new file mode 100644
index 000000000000..389668474070
--- /dev/null
+++ b/drivers/media/tuners/tua9001.c
@@ -0,0 +1,294 @@
+/*
+ * Infineon TUA 9001 silicon tuner driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tua9001.h"
+#include "tua9001_priv.h"
+
+/* write register */
+static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
+{
+ int ret;
+ u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n",
+ KBUILD_MODNAME, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+static int tua9001_release(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->callback)
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_CEN, 0);
+
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+
+ return ret;
+}
+
+static int tua9001_init(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ int ret = 0;
+ u8 i;
+ struct reg_val data[] = {
+ { 0x1e, 0x6512 },
+ { 0x25, 0xb888 },
+ { 0x39, 0x5460 },
+ { 0x3b, 0x00c0 },
+ { 0x3a, 0xf000 },
+ { 0x08, 0x0000 },
+ { 0x32, 0x0030 },
+ { 0x41, 0x703a },
+ { 0x40, 0x1c78 },
+ { 0x2c, 0x1c00 },
+ { 0x36, 0xc013 },
+ { 0x37, 0x6f18 },
+ { 0x27, 0x0008 },
+ { 0x2a, 0x0001 },
+ { 0x34, 0x0a40 },
+ };
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RESETN, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
+ if (ret < 0)
+ goto err_i2c_gate_ctrl;
+ }
+
+err_i2c_gate_ctrl:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
+err:
+ if (ret < 0)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tua9001_sleep(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ if (fe->callback)
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RESETN, 1);
+
+ if (ret < 0)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tua9001_set_params(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u16 val;
+ u32 frequency;
+ struct reg_val data[2];
+
+ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \
+ "bandwidth_hz=%d\n", __func__,
+ c->delivery_system, c->frequency, c->bandwidth_hz);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ switch (c->bandwidth_hz) {
+ case 8000000:
+ val = 0x0000;
+ break;
+ case 7000000:
+ val = 0x1000;
+ break;
+ case 6000000:
+ val = 0x2000;
+ break;
+ case 5000000:
+ val = 0x3000;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ data[0].reg = 0x04;
+ data[0].val = val;
+
+ frequency = (c->frequency - 150000000);
+ frequency /= 100;
+ frequency *= 48;
+ frequency /= 10000;
+
+ data[1].reg = 0x1f;
+ data[1].val = frequency;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RXEN, 0);
+ if (ret < 0)
+ goto err_i2c_gate_ctrl;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
+ if (ret < 0)
+ goto err_i2c_gate_ctrl;
+ }
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RXEN, 1);
+ if (ret < 0)
+ goto err_i2c_gate_ctrl;
+ }
+
+err_i2c_gate_ctrl:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
+err:
+ if (ret < 0)
+ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+
+ dev_dbg(&priv->i2c->dev, "%s:\n", __func__);
+
+ *frequency = 0; /* Zero-IF */
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops tua9001_tuner_ops = {
+ .info = {
+ .name = "Infineon TUA 9001",
+
+ .frequency_min = 170000000,
+ .frequency_max = 862000000,
+ .frequency_step = 0,
+ },
+
+ .release = tua9001_release,
+
+ .init = tua9001_init,
+ .sleep = tua9001_sleep,
+ .set_params = tua9001_set_params,
+
+ .get_if_frequency = tua9001_get_if_frequency,
+};
+
+struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg)
+{
+ struct tua9001_priv *priv = NULL;
+ int ret;
+
+ priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_CEN, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RXEN, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ TUA9001_CMD_RESETN, 1);
+ if (ret < 0)
+ goto err;
+ }
+
+ dev_info(&priv->i2c->dev,
+ "%s: Infineon TUA 9001 successfully attached\n",
+ KBUILD_MODNAME);
+
+ memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ return fe;
+err:
+ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(tua9001_attach);
+
+MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/tua9001.h b/drivers/media/tuners/tua9001.h
new file mode 100644
index 000000000000..cf5b815feff9
--- /dev/null
+++ b/drivers/media/tuners/tua9001.h
@@ -0,0 +1,66 @@
+/*
+ * Infineon TUA 9001 silicon tuner driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TUA9001_H
+#define TUA9001_H
+
+#include "dvb_frontend.h"
+
+struct tua9001_config {
+ /*
+ * I2C address
+ */
+ u8 i2c_addr;
+};
+
+/*
+ * TUA9001 I/O PINs:
+ *
+ * CEN - chip enable
+ * 0 = chip disabled (chip off)
+ * 1 = chip enabled (chip on)
+ *
+ * RESETN - chip reset
+ * 0 = reset disabled (chip reset off)
+ * 1 = reset enabled (chip reset on)
+ *
+ * RXEN - RX enable
+ * 0 = RX disabled (chip idle)
+ * 1 = RX enabled (chip tuned)
+ */
+
+#define TUA9001_CMD_CEN 0
+#define TUA9001_CMD_RESETN 1
+#define TUA9001_CMD_RXEN 2
+
+#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \
+ (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg);
+#else
+static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/tuners/tua9001_priv.h
index 73cc1ce0575c..73cc1ce0575c 100644
--- a/drivers/media/common/tuners/tua9001_priv.h
+++ b/drivers/media/tuners/tua9001_priv.h
diff --git a/drivers/media/common/tuners/tuner-i2c.h b/drivers/media/tuners/tuner-i2c.h
index 18f005634c67..18f005634c67 100644
--- a/drivers/media/common/tuners/tuner-i2c.h
+++ b/drivers/media/tuners/tuner-i2c.h
diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c
index 39e7e583c8c0..39e7e583c8c0 100644
--- a/drivers/media/common/tuners/tuner-simple.c
+++ b/drivers/media/tuners/tuner-simple.c
diff --git a/drivers/media/common/tuners/tuner-simple.h b/drivers/media/tuners/tuner-simple.h
index 381fa5d35a9b..381fa5d35a9b 100644
--- a/drivers/media/common/tuners/tuner-simple.h
+++ b/drivers/media/tuners/tuner-simple.h
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/tuners/tuner-types.c
index 2da4440c16ee..2da4440c16ee 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/tuners/tuner-types.c
diff --git a/drivers/media/common/tuners/tuner-xc2028-types.h b/drivers/media/tuners/tuner-xc2028-types.h
index 74dc46a71f64..74dc46a71f64 100644
--- a/drivers/media/common/tuners/tuner-xc2028-types.h
+++ b/drivers/media/tuners/tuner-xc2028-types.h
diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c
new file mode 100644
index 000000000000..7bcb6b0ff1df
--- /dev/null
+++ b/drivers/media/tuners/tuner-xc2028.c
@@ -0,0 +1,1509 @@
+/* tuner-xc2028
+ *
+ * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org)
+ *
+ * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com)
+ * - frontend interface
+ *
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <asm/div64.h>
+#include <linux/firmware.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <media/tuner.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include "tuner-i2c.h"
+#include "tuner-xc2028.h"
+#include "tuner-xc2028-types.h"
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+/* Registers (Write-only) */
+#define XREG_INIT 0x00
+#define XREG_RF_FREQ 0x02
+#define XREG_POWER_DOWN 0x08
+
+/* Registers (Read-only) */
+#define XREG_FREQ_ERROR 0x01
+#define XREG_LOCK 0x02
+#define XREG_VERSION 0x04
+#define XREG_PRODUCT_ID 0x08
+#define XREG_HSYNC_FREQ 0x10
+#define XREG_FRAME_LINES 0x20
+#define XREG_SNR 0x40
+
+#define XREG_ADC_ENV 0x0100
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable verbose debug messages");
+
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
+ "1 keep device energized and with tuner ready all the times.\n"
+ " Faster, but consumes more power and keeps the device hotter\n");
+
+static char audio_std[8];
+module_param_string(audio_std, audio_std, sizeof(audio_std), 0);
+MODULE_PARM_DESC(audio_std,
+ "Audio standard. XC3028 audio decoder explicitly "
+ "needs to know what audio\n"
+ "standard is needed for some video standards with audio A2 or NICAM.\n"
+ "The valid values are:\n"
+ "A2\n"
+ "A2/A\n"
+ "A2/B\n"
+ "NICAM\n"
+ "NICAM/A\n"
+ "NICAM/B\n");
+
+static char firmware_name[30];
+module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
+MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
+ "default firmware name\n");
+
+static LIST_HEAD(hybrid_tuner_instance_list);
+static DEFINE_MUTEX(xc2028_list_mutex);
+
+/* struct for storing firmware table */
+struct firmware_description {
+ unsigned int type;
+ v4l2_std_id id;
+ __u16 int_freq;
+ unsigned char *ptr;
+ unsigned int size;
+};
+
+struct firmware_properties {
+ unsigned int type;
+ v4l2_std_id id;
+ v4l2_std_id std_req;
+ __u16 int_freq;
+ unsigned int scode_table;
+ int scode_nr;
+};
+
+enum xc2028_state {
+ XC2028_NO_FIRMWARE = 0,
+ XC2028_WAITING_FIRMWARE,
+ XC2028_ACTIVE,
+ XC2028_SLEEP,
+ XC2028_NODEV,
+};
+
+struct xc2028_data {
+ struct list_head hybrid_tuner_instance_list;
+ struct tuner_i2c_props i2c_props;
+ __u32 frequency;
+
+ enum xc2028_state state;
+ const char *fname;
+
+ struct firmware_description *firm;
+ int firm_size;
+ __u16 firm_version;
+
+ __u16 hwmodel;
+ __u16 hwvers;
+
+ struct xc2028_ctrl ctrl;
+
+ struct firmware_properties cur_fw;
+
+ struct mutex lock;
+};
+
+#define i2c_send(priv, buf, size) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \
+ if (size != _rc) \
+ tuner_info("i2c output error: rc = %d (should be %d)\n",\
+ _rc, (int)size); \
+ if (priv->ctrl.msleep) \
+ msleep(priv->ctrl.msleep); \
+ _rc; \
+})
+
+#define i2c_rcv(priv, buf, size) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \
+ if (size != _rc) \
+ tuner_err("i2c input error: rc = %d (should be %d)\n", \
+ _rc, (int)size); \
+ _rc; \
+})
+
+#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \
+ int _rc; \
+ _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \
+ ibuf, isize); \
+ if (isize != _rc) \
+ tuner_err("i2c input error: rc = %d (should be %d)\n", \
+ _rc, (int)isize); \
+ if (priv->ctrl.msleep) \
+ msleep(priv->ctrl.msleep); \
+ _rc; \
+})
+
+#define send_seq(priv, data...) ({ \
+ static u8 _val[] = data; \
+ int _rc; \
+ if (sizeof(_val) != \
+ (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \
+ _val, sizeof(_val)))) { \
+ tuner_err("Error on line %d: %d\n", __LINE__, _rc); \
+ } else if (priv->ctrl.msleep) \
+ msleep(priv->ctrl.msleep); \
+ _rc; \
+})
+
+static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val)
+{
+ unsigned char buf[2];
+ unsigned char ibuf[2];
+
+ tuner_dbg("%s %04x called\n", __func__, reg);
+
+ buf[0] = reg >> 8;
+ buf[1] = (unsigned char) reg;
+
+ if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2)
+ return -EIO;
+
+ *val = (ibuf[1]) | (ibuf[0] << 8);
+ return 0;
+}
+
+#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
+static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+ if (type & BASE)
+ printk("BASE ");
+ if (type & INIT1)
+ printk("INIT1 ");
+ if (type & F8MHZ)
+ printk("F8MHZ ");
+ if (type & MTS)
+ printk("MTS ");
+ if (type & D2620)
+ printk("D2620 ");
+ if (type & D2633)
+ printk("D2633 ");
+ if (type & DTV6)
+ printk("DTV6 ");
+ if (type & QAM)
+ printk("QAM ");
+ if (type & DTV7)
+ printk("DTV7 ");
+ if (type & DTV78)
+ printk("DTV78 ");
+ if (type & DTV8)
+ printk("DTV8 ");
+ if (type & FM)
+ printk("FM ");
+ if (type & INPUT1)
+ printk("INPUT1 ");
+ if (type & LCD)
+ printk("LCD ");
+ if (type & NOGD)
+ printk("NOGD ");
+ if (type & MONO)
+ printk("MONO ");
+ if (type & ATSC)
+ printk("ATSC ");
+ if (type & IF)
+ printk("IF ");
+ if (type & LG60)
+ printk("LG60 ");
+ if (type & ATI638)
+ printk("ATI638 ");
+ if (type & OREN538)
+ printk("OREN538 ");
+ if (type & OREN36)
+ printk("OREN36 ");
+ if (type & TOYOTA388)
+ printk("TOYOTA388 ");
+ if (type & TOYOTA794)
+ printk("TOYOTA794 ");
+ if (type & DIBCOM52)
+ printk("DIBCOM52 ");
+ if (type & ZARLINK456)
+ printk("ZARLINK456 ");
+ if (type & CHINA)
+ printk("CHINA ");
+ if (type & F6MHZ)
+ printk("F6MHZ ");
+ if (type & INPUT2)
+ printk("INPUT2 ");
+ if (type & SCODE)
+ printk("SCODE ");
+ if (type & HAS_IF)
+ printk("HAS_IF_%d ", int_freq);
+}
+
+static v4l2_std_id parse_audio_std_option(void)
+{
+ if (strcasecmp(audio_std, "A2") == 0)
+ return V4L2_STD_A2;
+ if (strcasecmp(audio_std, "A2/A") == 0)
+ return V4L2_STD_A2_A;
+ if (strcasecmp(audio_std, "A2/B") == 0)
+ return V4L2_STD_A2_B;
+ if (strcasecmp(audio_std, "NICAM") == 0)
+ return V4L2_STD_NICAM;
+ if (strcasecmp(audio_std, "NICAM/A") == 0)
+ return V4L2_STD_NICAM_A;
+ if (strcasecmp(audio_std, "NICAM/B") == 0)
+ return V4L2_STD_NICAM_B;
+
+ return 0;
+}
+
+static int check_device_status(struct xc2028_data *priv)
+{
+ switch (priv->state) {
+ case XC2028_NO_FIRMWARE:
+ case XC2028_WAITING_FIRMWARE:
+ return -EAGAIN;
+ case XC2028_ACTIVE:
+ case XC2028_SLEEP:
+ return 0;
+ case XC2028_NODEV:
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void free_firmware(struct xc2028_data *priv)
+{
+ int i;
+ tuner_dbg("%s called\n", __func__);
+
+ if (!priv->firm)
+ return;
+
+ for (i = 0; i < priv->firm_size; i++)
+ kfree(priv->firm[i].ptr);
+
+ kfree(priv->firm);
+
+ priv->firm = NULL;
+ priv->firm_size = 0;
+ priv->state = XC2028_NO_FIRMWARE;
+
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+}
+
+static int load_all_firmwares(struct dvb_frontend *fe,
+ const struct firmware *fw)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ const unsigned char *p, *endp;
+ int rc = 0;
+ int n, n_array;
+ char name[33];
+
+ tuner_dbg("%s called\n", __func__);
+
+ p = fw->data;
+ endp = p + fw->size;
+
+ if (fw->size < sizeof(name) - 1 + 2 + 2) {
+ tuner_err("Error: firmware file %s has invalid size!\n",
+ priv->fname);
+ goto corrupt;
+ }
+
+ memcpy(name, p, sizeof(name) - 1);
+ name[sizeof(name) - 1] = 0;
+ p += sizeof(name) - 1;
+
+ priv->firm_version = get_unaligned_le16(p);
+ p += 2;
+
+ n_array = get_unaligned_le16(p);
+ p += 2;
+
+ tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n",
+ n_array, priv->fname, name,
+ priv->firm_version >> 8, priv->firm_version & 0xff);
+
+ priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
+ if (priv->firm == NULL) {
+ tuner_err("Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ priv->firm_size = n_array;
+
+ n = -1;
+ while (p < endp) {
+ __u32 type, size;
+ v4l2_std_id id;
+ __u16 int_freq = 0;
+
+ n++;
+ if (n >= n_array) {
+ tuner_err("More firmware images in file than "
+ "were expected!\n");
+ goto corrupt;
+ }
+
+ /* Checks if there's enough bytes to read */
+ if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
+ goto header;
+
+ type = get_unaligned_le32(p);
+ p += sizeof(type);
+
+ id = get_unaligned_le64(p);
+ p += sizeof(id);
+
+ if (type & HAS_IF) {
+ int_freq = get_unaligned_le16(p);
+ p += sizeof(int_freq);
+ if (endp - p < sizeof(size))
+ goto header;
+ }
+
+ size = get_unaligned_le32(p);
+ p += sizeof(size);
+
+ if (!size || size > endp - p) {
+ tuner_err("Firmware type ");
+ dump_firm_type(type);
+ printk("(%x), id %llx is corrupted "
+ "(size=%d, expected %d)\n",
+ type, (unsigned long long)id,
+ (unsigned)(endp - p), size);
+ goto corrupt;
+ }
+
+ priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
+ if (priv->firm[n].ptr == NULL) {
+ tuner_err("Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto err;
+ }
+ tuner_dbg("Reading firmware type ");
+ if (debug) {
+ dump_firm_type_and_int_freq(type, int_freq);
+ printk("(%x), id %llx, size=%d.\n",
+ type, (unsigned long long)id, size);
+ }
+
+ memcpy(priv->firm[n].ptr, p, size);
+ priv->firm[n].type = type;
+ priv->firm[n].id = id;
+ priv->firm[n].size = size;
+ priv->firm[n].int_freq = int_freq;
+
+ p += size;
+ }
+
+ if (n + 1 != priv->firm_size) {
+ tuner_err("Firmware file is incomplete!\n");
+ goto corrupt;
+ }
+
+ goto done;
+
+header:
+ tuner_err("Firmware header is incomplete!\n");
+corrupt:
+ rc = -EINVAL;
+ tuner_err("Error: firmware file is corrupted!\n");
+
+err:
+ tuner_info("Releasing partially loaded firmware file.\n");
+ free_firmware(priv);
+
+done:
+ if (rc == 0)
+ tuner_dbg("Firmware files loaded.\n");
+ else
+ priv->state = XC2028_NODEV;
+
+ return rc;
+}
+
+static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int i, best_i = -1, best_nr_matches = 0;
+ unsigned int type_mask = 0;
+
+ tuner_dbg("%s called, want type=", __func__);
+ if (debug) {
+ dump_firm_type(type);
+ printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+
+ if (!priv->firm) {
+ tuner_err("Error! firmware not loaded\n");
+ return -EINVAL;
+ }
+
+ if (((type & ~SCODE) == 0) && (*id == 0))
+ *id = V4L2_STD_PAL;
+
+ if (type & BASE)
+ type_mask = BASE_TYPES;
+ else if (type & SCODE) {
+ type &= SCODE_TYPES;
+ type_mask = SCODE_TYPES & ~HAS_IF;
+ } else if (type & DTV_TYPES)
+ type_mask = DTV_TYPES;
+ else if (type & STD_SPECIFIC_TYPES)
+ type_mask = STD_SPECIFIC_TYPES;
+
+ type &= type_mask;
+
+ if (!(type & SCODE))
+ type_mask = ~0;
+
+ /* Seek for exact match */
+ for (i = 0; i < priv->firm_size; i++) {
+ if ((type == (priv->firm[i].type & type_mask)) &&
+ (*id == priv->firm[i].id))
+ goto found;
+ }
+
+ /* Seek for generic video standard match */
+ for (i = 0; i < priv->firm_size; i++) {
+ v4l2_std_id match_mask;
+ int nr_matches;
+
+ if (type != (priv->firm[i].type & type_mask))
+ continue;
+
+ match_mask = *id & priv->firm[i].id;
+ if (!match_mask)
+ continue;
+
+ if ((*id & match_mask) == *id)
+ goto found; /* Supports all the requested standards */
+
+ nr_matches = hweight64(match_mask);
+ if (nr_matches > best_nr_matches) {
+ best_nr_matches = nr_matches;
+ best_i = i;
+ }
+ }
+
+ if (best_nr_matches > 0) {
+ tuner_dbg("Selecting best matching firmware (%d bits) for "
+ "type=", best_nr_matches);
+ dump_firm_type(type);
+ printk("(%x), id %016llx:\n", type, (unsigned long long)*id);
+ i = best_i;
+ goto found;
+ }
+
+ /*FIXME: Would make sense to seek for type "hint" match ? */
+
+ i = -ENOENT;
+ goto ret;
+
+found:
+ *id = priv->firm[i].id;
+
+ret:
+ tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found");
+ if (debug) {
+ dump_firm_type(type);
+ printk("(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+ return i;
+}
+
+static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ /* analog side (tuner-core) uses i2c_adap->algo_data.
+ * digital side is not guaranteed to have algo_data defined.
+ *
+ * digital side will always have fe->dvb defined.
+ * analog side (tuner-core) doesn't (yet) define fe->dvb.
+ */
+
+ return (!fe->callback) ? -EINVAL :
+ fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv : priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER, cmd, arg);
+}
+
+static int load_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p, *endp, buf[priv->ctrl.max_len];
+
+ tuner_dbg("%s called\n", __func__);
+
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+
+ tuner_info("Loading firmware for type=");
+ dump_firm_type(priv->firm[pos].type);
+ printk("(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+
+ p = priv->firm[pos].ptr;
+ endp = p + priv->firm[pos].size;
+
+ while (p < endp) {
+ __u16 size;
+
+ /* Checks if there's enough bytes to read */
+ if (p + sizeof(size) > endp) {
+ tuner_err("Firmware chunk size is wrong\n");
+ return -EINVAL;
+ }
+
+ size = le16_to_cpu(*(__u16 *) p);
+ p += sizeof(size);
+
+ if (size == 0xffff)
+ return 0;
+
+ if (!size) {
+ /* Special callback command received */
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
+ if (rc < 0) {
+ tuner_err("Error at RESET code %d\n",
+ (*p) & 0x7f);
+ return -EINVAL;
+ }
+ continue;
+ }
+ if (size >= 0xff00) {
+ switch (size) {
+ case 0xff00:
+ rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0);
+ if (rc < 0) {
+ tuner_err("Error at RESET code %d\n",
+ (*p) & 0x7f);
+ return -EINVAL;
+ }
+ break;
+ default:
+ tuner_info("Invalid RESET code %d\n",
+ size & 0x7f);
+ return -EINVAL;
+
+ }
+ continue;
+ }
+
+ /* Checks for a sleep command */
+ if (size & 0x8000) {
+ msleep(size & 0x7fff);
+ continue;
+ }
+
+ if ((size + p > endp)) {
+ tuner_err("missing bytes: need %d, have %d\n",
+ size, (int)(endp - p));
+ return -EINVAL;
+ }
+
+ buf[0] = *p;
+ p++;
+ size--;
+
+ /* Sends message chunks */
+ while (size > 0) {
+ int len = (size < priv->ctrl.max_len - 1) ?
+ size : priv->ctrl.max_len - 1;
+
+ memcpy(buf + 1, p, len);
+
+ rc = i2c_send(priv, buf, len + 1);
+ if (rc < 0) {
+ tuner_err("%d returned from send\n", rc);
+ return -EINVAL;
+ }
+
+ p += len;
+ size -= len;
+ }
+
+ /* silently fail if the frontend doesn't support I2C flush */
+ rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0);
+ if ((rc < 0) && (rc != -EINVAL)) {
+ tuner_err("error executing flush: %d\n", rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id, __u16 int_freq, int scode)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+
+ tuner_dbg("%s called\n", __func__);
+
+ if (!int_freq) {
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+ } else {
+ for (pos = 0; pos < priv->firm_size; pos++) {
+ if ((priv->firm[pos].int_freq == int_freq) &&
+ (priv->firm[pos].type & HAS_IF))
+ break;
+ }
+ if (pos == priv->firm_size)
+ return -ENOENT;
+ }
+
+ p = priv->firm[pos].ptr;
+
+ if (priv->firm[pos].type & HAS_IF) {
+ if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+ return -EINVAL;
+ p += 12 * scode;
+ } else {
+ /* 16 SCODE entries per file; each SCODE entry is 12 bytes and
+ * has a 2-byte size header in the firmware format. */
+ if (priv->firm[pos].size != 14 * 16 || scode >= 16 ||
+ le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12)
+ return -EINVAL;
+ p += 14 * scode + 2;
+ }
+
+ tuner_info("Loading SCODE for type=");
+ dump_firm_type_and_int_freq(priv->firm[pos].type,
+ priv->firm[pos].int_freq);
+ printk("(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00});
+ if (rc < 0)
+ return -EIO;
+
+ rc = i2c_send(priv, p, 12);
+ if (rc < 0)
+ return -EIO;
+
+ rc = send_seq(priv, {0x00, 0x8c});
+ if (rc < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id std, __u16 int_freq)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ struct firmware_properties new_fw;
+ int rc, retry_count = 0;
+ u16 version, hwmodel;
+ v4l2_std_id std0;
+
+ tuner_dbg("%s called\n", __func__);
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ if (priv->ctrl.mts && !(type & FM))
+ type |= MTS;
+
+retry:
+ new_fw.type = type;
+ new_fw.id = std;
+ new_fw.std_req = std;
+ new_fw.scode_table = SCODE | priv->ctrl.scode_table;
+ new_fw.scode_nr = 0;
+ new_fw.int_freq = int_freq;
+
+ tuner_dbg("checking firmware, user requested type=");
+ if (debug) {
+ dump_firm_type(new_fw.type);
+ printk("(%x), id %016llx, ", new_fw.type,
+ (unsigned long long)new_fw.std_req);
+ if (!int_freq) {
+ printk("scode_tbl ");
+ dump_firm_type(priv->ctrl.scode_table);
+ printk("(%x), ", priv->ctrl.scode_table);
+ } else
+ printk("int_freq %d, ", new_fw.int_freq);
+ printk("scode_nr %d\n", new_fw.scode_nr);
+ }
+
+ /*
+ * No need to reload base firmware if it matches and if the tuner
+ * is not at sleep mode
+ */
+ if ((priv->state == XC2028_ACTIVE) &&
+ (((BASE | new_fw.type) & BASE_TYPES) ==
+ (priv->cur_fw.type & BASE_TYPES))) {
+ tuner_dbg("BASE firmware not changed.\n");
+ goto skip_base;
+ }
+
+ /* Updating BASE - forget about all currently loaded firmware */
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+ /* Reset is needed before loading firmware */
+ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0);
+ if (rc < 0)
+ goto fail;
+
+ /* BASE firmwares are all std0 */
+ std0 = 0;
+ rc = load_firmware(fe, BASE | new_fw.type, &std0);
+ if (rc < 0) {
+ tuner_err("Error %d while loading base firmware\n",
+ rc);
+ goto fail;
+ }
+
+ /* Load INIT1, if needed */
+ tuner_dbg("Load init1 firmware, if exists\n");
+
+ rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ,
+ &std0);
+ if (rc < 0 && rc != -ENOENT) {
+ tuner_err("Error %d while loading init1 firmware\n",
+ rc);
+ goto fail;
+ }
+
+skip_base:
+ /*
+ * No need to reload standard specific firmware if base firmware
+ * was not reloaded and requested video standards have not changed.
+ */
+ if (priv->cur_fw.type == (BASE | new_fw.type) &&
+ priv->cur_fw.std_req == std) {
+ tuner_dbg("Std-specific firmware already loaded.\n");
+ goto skip_std_specific;
+ }
+
+ /* Reloading std-specific firmware forces a SCODE update */
+ priv->cur_fw.scode_table = 0;
+
+ rc = load_firmware(fe, new_fw.type, &new_fw.id);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id);
+
+ if (rc < 0)
+ goto fail;
+
+skip_std_specific:
+ if (priv->cur_fw.scode_table == new_fw.scode_table &&
+ priv->cur_fw.scode_nr == new_fw.scode_nr) {
+ tuner_dbg("SCODE firmware already loaded.\n");
+ goto check_device;
+ }
+
+ if (new_fw.type & FM)
+ goto check_device;
+
+ /* Load SCODE firmware, if exists */
+ tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr);
+
+ rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+ new_fw.int_freq, new_fw.scode_nr);
+
+check_device:
+ if (xc2028_get_reg(priv, 0x0004, &version) < 0 ||
+ xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) {
+ tuner_err("Unable to read tuner registers.\n");
+ goto fail;
+ }
+
+ tuner_dbg("Device is Xceive %d version %d.%d, "
+ "firmware version %d.%d\n",
+ hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8,
+ (version & 0xf0) >> 4, version & 0xf);
+
+
+ if (priv->ctrl.read_not_reliable)
+ goto read_not_reliable;
+
+ /* Check firmware version against what we downloaded. */
+ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) {
+ if (!priv->ctrl.read_not_reliable) {
+ tuner_err("Incorrect readback of firmware version.\n");
+ goto fail;
+ } else {
+ tuner_err("Returned an incorrect version. However, "
+ "read is not reliable enough. Ignoring it.\n");
+ hwmodel = 3028;
+ }
+ }
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = version & 0xff00;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != (version & 0xff00)) {
+ tuner_err("Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
+read_not_reliable:
+ memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+ /*
+ * By setting BASE in cur_fw.type only after successfully loading all
+ * firmwares, we can:
+ * 1. Identify that BASE firmware with type=0 has been loaded;
+ * 2. Tell whether BASE firmware was just changed the next time through.
+ */
+ priv->cur_fw.type |= BASE;
+ priv->state = XC2028_ACTIVE;
+
+ return 0;
+
+fail:
+ priv->state = XC2028_SLEEP;
+
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (retry_count < 8) {
+ msleep(50);
+ retry_count++;
+ tuner_dbg("Retrying firmware load\n");
+ goto retry;
+ }
+
+ if (rc == -ENOENT)
+ rc = -EINVAL;
+ return rc;
+}
+
+static int xc2028_signal(struct dvb_frontend *fe, u16 *strength)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ u16 frq_lock, signal = 0;
+ int rc, i;
+
+ tuner_dbg("%s called\n", __func__);
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&priv->lock);
+
+ /* Sync Lock Indicator */
+ for (i = 0; i < 3; i++) {
+ rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+ if (rc < 0)
+ goto ret;
+
+ if (frq_lock)
+ break;
+ msleep(6);
+ }
+
+ /* Frequency didn't lock */
+ if (frq_lock == 2)
+ goto ret;
+
+ /* Get SNR of the video signal */
+ rc = xc2028_get_reg(priv, XREG_SNR, &signal);
+ if (rc < 0)
+ goto ret;
+
+ /* Signal level is 3 bits only */
+
+ signal = ((1 << 12) - 1) | ((signal & 0x07) << 12);
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ *strength = signal;
+
+ tuner_dbg("signal strength is %d\n", signal);
+
+ return rc;
+}
+
+static int xc2028_get_afc(struct dvb_frontend *fe, s32 *afc)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int i, rc;
+ u16 frq_lock = 0;
+ s16 afc_reg = 0;
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&priv->lock);
+
+ /* Sync Lock Indicator */
+ for (i = 0; i < 3; i++) {
+ rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock);
+ if (rc < 0)
+ goto ret;
+
+ if (frq_lock)
+ break;
+ msleep(6);
+ }
+
+ /* Frequency didn't lock */
+ if (frq_lock == 2)
+ goto ret;
+
+ /* Get AFC */
+ rc = xc2028_get_reg(priv, XREG_FREQ_ERROR, &afc_reg);
+ if (rc < 0)
+ goto ret;
+
+ *afc = afc_reg * 15625; /* Hz */
+
+ tuner_dbg("AFC is %d Hz\n", *afc);
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+#define DIV 15625
+
+static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */,
+ enum v4l2_tuner_type new_type,
+ unsigned int type,
+ v4l2_std_id std,
+ u16 int_freq)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc = -EINVAL;
+ unsigned char buf[4];
+ u32 div, offset = 0;
+
+ tuner_dbg("%s called\n", __func__);
+
+ mutex_lock(&priv->lock);
+
+ tuner_dbg("should set frequency %d kHz\n", freq / 1000);
+
+ if (check_firmware(fe, type, std, int_freq) < 0)
+ goto ret;
+
+ /* On some cases xc2028 can disable video output, if
+ * very weak signals are received. By sending a soft
+ * reset, this is re-enabled. So, it is better to always
+ * send a soft reset before changing channels, to be sure
+ * that xc2028 will be in a safe state.
+ * Maybe this might also be needed for DTV.
+ */
+ switch (new_type) {
+ case V4L2_TUNER_ANALOG_TV:
+ rc = send_seq(priv, {0x00, 0x00});
+
+ /* Analog mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_RADIO:
+ /* Radio mode requires offset = 0 */
+ break;
+ case V4L2_TUNER_DIGITAL_TV:
+ /*
+ * Digital modes require an offset to adjust to the
+ * proper frequency. The offset depends on what
+ * firmware version is used.
+ */
+
+ /*
+ * Adjust to the center frequency. This is calculated by the
+ * formula: offset = 1.25MHz - BW/2
+ * For DTV 7/8, the firmware uses BW = 8000, so it needs a
+ * further adjustment to get the frequency center on VHF
+ */
+
+ /*
+ * The firmware DTV78 used to work fine in UHF band (8 MHz
+ * bandwidth) but not at all in VHF band (7 MHz bandwidth).
+ * The real problem was connected to the formula used to
+ * calculate the center frequency offset in VHF band.
+ * In fact, removing the 500KHz adjustment fixed the problem.
+ * This is coherent to what was implemented for the DTV7
+ * firmware.
+ * In the end, now the center frequency is the same for all 3
+ * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel
+ * bandwidth.
+ */
+
+ if (priv->cur_fw.type & DTV6)
+ offset = 1750000;
+ else /* DTV7 or DTV8 or DTV78 */
+ offset = 2750000;
+
+ /*
+ * xc3028 additional "magic"
+ * Depending on the firmware version, it needs some adjustments
+ * to properly centralize the frequency. This seems to be
+ * needed to compensate the SCODE table adjustments made by
+ * newer firmwares
+ */
+
+ /*
+ * The proper adjustment would be to do it at s-code table.
+ * However, this didn't work, as reported by
+ * Robert Lowery <rglowery@exemail.com.au>
+ */
+
+#if 0
+ /*
+ * Still need tests for XC3028L (firmware 3.2 or upper)
+ * So, for now, let's just comment the per-firmware
+ * version of this change. Reports with xc3028l working
+ * with and without the lines bellow are welcome
+ */
+
+ if (priv->firm_version < 0x0302) {
+ if (priv->cur_fw.type & DTV7)
+ offset += 500000;
+ } else {
+ if (priv->cur_fw.type & DTV7)
+ offset -= 300000;
+ else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */
+ offset += 200000;
+ }
+#endif
+ }
+
+ div = (freq - offset + DIV / 2) / DIV;
+
+ /* CMD= Set frequency */
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00});
+ if (rc < 0)
+ goto ret;
+
+ /* Return code shouldn't be checked.
+ The reset CLK is needed only with tm6000.
+ Driver should work fine even if this fails.
+ */
+ if (priv->ctrl.msleep)
+ msleep(priv->ctrl.msleep);
+ do_tuner_callback(fe, XC2028_RESET_CLK, 1);
+
+ msleep(10);
+
+ buf[0] = 0xff & (div >> 24);
+ buf[1] = 0xff & (div >> 16);
+ buf[2] = 0xff & (div >> 8);
+ buf[3] = 0xff & (div);
+
+ rc = i2c_send(priv, buf, sizeof(buf));
+ if (rc < 0)
+ goto ret;
+ msleep(100);
+
+ priv->frequency = freq;
+
+ tuner_dbg("divisor= %*ph (freq=%d.%03d)\n", 4, buf,
+ freq / 1000000, (freq % 1000000) / 1000);
+
+ rc = 0;
+
+ret:
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+static int xc2028_set_analog_freq(struct dvb_frontend *fe,
+ struct analog_parameters *p)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ unsigned int type=0;
+
+ tuner_dbg("%s called\n", __func__);
+
+ if (p->mode == V4L2_TUNER_RADIO) {
+ type |= FM;
+ if (priv->ctrl.input1)
+ type |= INPUT1;
+ return generic_set_freq(fe, (625l * p->frequency) / 10,
+ V4L2_TUNER_RADIO, type, 0, 0);
+ }
+
+ /* if std is not defined, choose one */
+ if (!p->std)
+ p->std = V4L2_STD_MN;
+
+ /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */
+ if (!(p->std & V4L2_STD_MN))
+ type |= F8MHZ;
+
+ /* Add audio hack to std mask */
+ p->std |= parse_audio_std_option();
+
+ return generic_set_freq(fe, 62500l * p->frequency,
+ V4L2_TUNER_ANALOG_TV, type, p->std, 0);
+}
+
+static int xc2028_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ u32 bw = c->bandwidth_hz;
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
+ unsigned int type = 0;
+ u16 demod = 0;
+
+ tuner_dbg("%s called\n", __func__);
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ switch (delsys) {
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ /*
+ * The only countries with 6MHz seem to be Taiwan/Uruguay.
+ * Both seem to require QAM firmware for OFDM decoding
+ * Tested in Taiwan by Terry Wu <terrywu2009@gmail.com>
+ */
+ if (bw <= 6000000)
+ type |= QAM;
+
+ switch (priv->ctrl.type) {
+ case XC2028_D2633:
+ type |= D2633;
+ break;
+ case XC2028_D2620:
+ type |= D2620;
+ break;
+ case XC2028_AUTO:
+ default:
+ /* Zarlink seems to need D2633 */
+ if (priv->ctrl.demod == XC3028_FE_ZARLINK456)
+ type |= D2633;
+ else
+ type |= D2620;
+ }
+ break;
+ case SYS_ATSC:
+ /* The only ATSC firmware (at least on v2.7) is D2633 */
+ type |= ATSC | D2633;
+ break;
+ /* DVB-S and pure QAM (FE_QAM) are not supported */
+ default:
+ return -EINVAL;
+ }
+
+ if (bw <= 6000000) {
+ type |= DTV6;
+ priv->ctrl.vhfbw7 = 0;
+ priv->ctrl.uhfbw8 = 0;
+ } else if (bw <= 7000000) {
+ if (c->frequency < 470000000)
+ priv->ctrl.vhfbw7 = 1;
+ else
+ priv->ctrl.uhfbw8 = 0;
+ type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7;
+ type |= F8MHZ;
+ } else {
+ if (c->frequency < 470000000)
+ priv->ctrl.vhfbw7 = 0;
+ else
+ priv->ctrl.uhfbw8 = 1;
+ type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8;
+ type |= F8MHZ;
+ }
+
+ /* All S-code tables need a 200kHz shift */
+ if (priv->ctrl.demod) {
+ demod = priv->ctrl.demod;
+
+ /*
+ * Newer firmwares require a 200 kHz offset only for ATSC
+ */
+ if (type == ATSC || priv->firm_version < 0x0302)
+ demod += 200;
+ /*
+ * The DTV7 S-code table needs a 700 kHz shift.
+ *
+ * DTV7 is only used in Australia. Germany or Italy may also
+ * use this firmware after initialization, but a tune to a UHF
+ * channel should then cause DTV78 to be used.
+ *
+ * Unfortunately, on real-field tests, the s-code offset
+ * didn't work as expected, as reported by
+ * Robert Lowery <rglowery@exemail.com.au>
+ */
+ }
+
+ return generic_set_freq(fe, c->frequency,
+ V4L2_TUNER_DIGITAL_TV, type, 0, demod);
+}
+
+static int xc2028_sleep(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ /* Avoid firmware reload on slow devices or if PM disabled */
+ if (no_poweroff || priv->ctrl.disable_power_mgmt)
+ return 0;
+
+ tuner_dbg("Putting xc2028/3028 into poweroff mode.\n");
+ if (debug > 1) {
+ tuner_dbg("Printing sleep stack trace:\n");
+ dump_stack();
+ }
+
+ mutex_lock(&priv->lock);
+
+ if (priv->firm_version < 0x0202)
+ rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00});
+ else
+ rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00});
+
+ priv->state = XC2028_SLEEP;
+
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+static int xc2028_dvb_release(struct dvb_frontend *fe)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+
+ tuner_dbg("%s called\n", __func__);
+
+ mutex_lock(&xc2028_list_mutex);
+
+ /* only perform final cleanup if this is the last instance */
+ if (hybrid_tuner_report_instance_count(priv) == 1) {
+ free_firmware(priv);
+ kfree(priv->ctrl.fname);
+ priv->ctrl.fname = NULL;
+ }
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&xc2028_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
+
+ tuner_dbg("%s called\n", __func__);
+
+ rc = check_device_status(priv);
+ if (rc < 0)
+ return rc;
+
+ *frequency = priv->frequency;
+
+ return 0;
+}
+
+static void load_firmware_cb(const struct firmware *fw,
+ void *context)
+{
+ struct dvb_frontend *fe = context;
+ struct xc2028_data *priv = fe->tuner_priv;
+ int rc;
+
+ tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
+ if (!fw) {
+ tuner_err("Could not load firmware %s.\n", priv->fname);
+ priv->state = XC2028_NODEV;
+ return;
+ }
+
+ rc = load_all_firmwares(fe, fw);
+
+ release_firmware(fw);
+
+ if (rc < 0)
+ return;
+ priv->state = XC2028_SLEEP;
+}
+
+static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
+{
+ struct xc2028_data *priv = fe->tuner_priv;
+ struct xc2028_ctrl *p = priv_cfg;
+ int rc = 0;
+
+ tuner_dbg("%s called\n", __func__);
+
+ mutex_lock(&priv->lock);
+
+ /*
+ * Copy the config data.
+ * For the firmware name, keep a local copy of the string,
+ * in order to avoid troubles during device release.
+ */
+ if (priv->ctrl.fname)
+ kfree(priv->ctrl.fname);
+ memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
+ if (p->fname) {
+ priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
+ if (priv->ctrl.fname == NULL)
+ rc = -ENOMEM;
+ }
+
+ /*
+ * If firmware name changed, frees firmware. As free_firmware will
+ * reset the status to NO_FIRMWARE, this forces a new request_firmware
+ */
+ if (!firmware_name[0] && p->fname &&
+ priv->fname && strcmp(p->fname, priv->fname))
+ free_firmware(priv);
+
+ if (priv->ctrl.max_len < 9)
+ priv->ctrl.max_len = 13;
+
+ if (priv->state == XC2028_NO_FIRMWARE) {
+ if (!firmware_name[0])
+ priv->fname = priv->ctrl.fname;
+ else
+ priv->fname = firmware_name;
+
+ rc = request_firmware_nowait(THIS_MODULE, 1,
+ priv->fname,
+ priv->i2c_props.adap->dev.parent,
+ GFP_KERNEL,
+ fe, load_firmware_cb);
+ if (rc < 0) {
+ tuner_err("Failed to request firmware %s\n",
+ priv->fname);
+ priv->state = XC2028_NODEV;
+ } else
+ priv->state = XC2028_WAITING_FIRMWARE;
+ }
+ mutex_unlock(&priv->lock);
+
+ return rc;
+}
+
+static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = {
+ .info = {
+ .name = "Xceive XC3028",
+ .frequency_min = 42000000,
+ .frequency_max = 864000000,
+ .frequency_step = 50000,
+ },
+
+ .set_config = xc2028_set_config,
+ .set_analog_params = xc2028_set_analog_freq,
+ .release = xc2028_dvb_release,
+ .get_frequency = xc2028_get_frequency,
+ .get_rf_strength = xc2028_signal,
+ .get_afc = xc2028_get_afc,
+ .set_params = xc2028_set_params,
+ .sleep = xc2028_sleep,
+};
+
+struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
+ struct xc2028_config *cfg)
+{
+ struct xc2028_data *priv;
+ int instance;
+
+ if (debug)
+ printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n");
+
+ if (NULL == cfg)
+ return NULL;
+
+ if (!fe) {
+ printk(KERN_ERR "xc2028: No frontend!\n");
+ return NULL;
+ }
+
+ mutex_lock(&xc2028_list_mutex);
+
+ instance = hybrid_tuner_request_state(struct xc2028_data, priv,
+ hybrid_tuner_instance_list,
+ cfg->i2c_adap, cfg->i2c_addr,
+ "xc2028");
+ switch (instance) {
+ case 0:
+ /* memory allocation failure */
+ goto fail;
+ break;
+ case 1:
+ /* new tuner instance */
+ priv->ctrl.max_len = 13;
+
+ mutex_init(&priv->lock);
+
+ fe->tuner_priv = priv;
+ break;
+ case 2:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+ break;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops,
+ sizeof(xc2028_dvb_tuner_ops));
+
+ tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner");
+
+ if (cfg->ctrl)
+ xc2028_set_config(fe, cfg->ctrl);
+
+ mutex_unlock(&xc2028_list_mutex);
+
+ return fe;
+fail:
+ mutex_unlock(&xc2028_list_mutex);
+
+ xc2028_dvb_release(fe);
+ return NULL;
+}
+
+EXPORT_SYMBOL(xc2028_attach);
+
+MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");
+MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
+MODULE_FIRMWARE(XC3028L_DEFAULT_FIRMWARE);
diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/tuners/tuner-xc2028.h
index 9ebfb2d0ff14..9ebfb2d0ff14 100644
--- a/drivers/media/common/tuners/tuner-xc2028.h
+++ b/drivers/media/tuners/tuner-xc2028.h
diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c
new file mode 100644
index 000000000000..4937712278f6
--- /dev/null
+++ b/drivers/media/tuners/xc4000.c
@@ -0,0 +1,1757 @@
+/*
+ * Driver for Xceive XC4000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Xceive Corporation
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ * Copyright (c) 2009 Davide Ferri <d.ferri@zero11.it>
+ * Copyright (c) 2010 Istvan Varga <istvan_v@mailbox.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <asm/unaligned.h>
+
+#include "dvb_frontend.h"
+
+#include "xc4000.h"
+#include "tuner-i2c.h"
+#include "tuner-xc2028-types.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off)).");
+
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, "
+ "0 (default): use device-specific default mode).");
+
+static int audio_std;
+module_param(audio_std, int, 0644);
+MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly "
+ "needs to know what audio standard is needed for some video standards "
+ "with audio A2 or NICAM. The valid settings are a sum of:\n"
+ " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n"
+ " 2: use A2 instead of NICAM or BTSC\n"
+ " 4: use SECAM/K3 instead of K1\n"
+ " 8: use PAL-D/K audio for SECAM-D/K\n"
+ "16: use FM radio input 1 instead of input 2\n"
+ "32: use mono audio (the lower three bits are ignored)");
+
+static char firmware_name[30];
+module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0);
+MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the "
+ "default firmware name.");
+
+static DEFINE_MUTEX(xc4000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "xc4000", ## arg)
+
+/* struct for storing firmware table */
+struct firmware_description {
+ unsigned int type;
+ v4l2_std_id id;
+ __u16 int_freq;
+ unsigned char *ptr;
+ unsigned int size;
+};
+
+struct firmware_properties {
+ unsigned int type;
+ v4l2_std_id id;
+ v4l2_std_id std_req;
+ __u16 int_freq;
+ unsigned int scode_table;
+ int scode_nr;
+};
+
+struct xc4000_priv {
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+ struct firmware_description *firm;
+ int firm_size;
+ u32 if_khz;
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+ u8 default_pm;
+ u8 dvb_amplitude;
+ u8 set_smoothedcvbs;
+ u8 ignore_i2c_write_errors;
+ __u16 firm_version;
+ struct firmware_properties cur_fw;
+ __u16 hwmodel;
+ __u16 hwvers;
+ struct mutex lock;
+};
+
+#define XC4000_AUDIO_STD_B 1
+#define XC4000_AUDIO_STD_A2 2
+#define XC4000_AUDIO_STD_K3 4
+#define XC4000_AUDIO_STD_L 8
+#define XC4000_AUDIO_STD_INPUT1 16
+#define XC4000_AUDIO_STD_MONO 32
+
+#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw"
+
+/* Misc Defines */
+#define MAX_TV_STANDARD 24
+#define XC_MAX_I2C_WRITE_LENGTH 64
+#define XC_POWERED_DOWN 0x80000000U
+
+/* Signal Types */
+#define XC_RF_MODE_AIR 0
+#define XC_RF_MODE_CABLE 1
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
+#define XC_PRODUCT_ID_XC4000 0x0FA0
+#define XC_PRODUCT_ID_XC4100 0x1004
+
+/* Registers (Write-only) */
+#define XREG_INIT 0x00
+#define XREG_VIDEO_MODE 0x01
+#define XREG_AUDIO_MODE 0x02
+#define XREG_RF_FREQ 0x03
+#define XREG_D_CODE 0x04
+#define XREG_DIRECTSITTING_MODE 0x05
+#define XREG_SEEK_MODE 0x06
+#define XREG_POWER_DOWN 0x08
+#define XREG_SIGNALSOURCE 0x0A
+#define XREG_SMOOTHEDCVBS 0x0E
+#define XREG_AMPLITUDE 0x10
+
+/* Registers (Read-only) */
+#define XREG_ADC_ENV 0x00
+#define XREG_QUALITY 0x01
+#define XREG_FRAME_LINES 0x02
+#define XREG_HSYNC_FREQ 0x03
+#define XREG_LOCK 0x04
+#define XREG_FREQ_ERROR 0x05
+#define XREG_SNR 0x06
+#define XREG_VERSION 0x07
+#define XREG_PRODUCT_ID 0x08
+#define XREG_SIGNAL_LEVEL 0x0A
+#define XREG_NOISE_LEVEL 0x0B
+
+/*
+ Basic firmware description. This will remain with
+ the driver for documentation purposes.
+
+ This represents an I2C firmware file encoded as a
+ string of unsigned char. Format is as follows:
+
+ char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
+ char[1 ]=len0_LSB -> length of first write transaction
+ char[2 ]=data0 -> first byte to be sent
+ char[3 ]=data1
+ char[4 ]=data2
+ char[ ]=...
+ char[M ]=dataN -> last byte to be sent
+ char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
+ char[M+2]=len1_LSB -> length of second write transaction
+ char[M+3]=data0
+ char[M+4]=data1
+ ...
+ etc.
+
+ The [len] value should be interpreted as follows:
+
+ len= len_MSB _ len_LSB
+ len=1111_1111_1111_1111 : End of I2C_SEQUENCE
+ len=0000_0000_0000_0000 : Reset command: Do hardware reset
+ len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
+ len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
+
+ For the RESET and WAIT commands, the two following bytes will contain
+ immediately the length of the following transaction.
+*/
+
+struct XC_TV_STANDARD {
+ const char *Name;
+ u16 audio_mode;
+ u16 video_mode;
+ u16 int_freq;
+};
+
+/* Tuner standards */
+#define XC4000_MN_NTSC_PAL_BTSC 0
+#define XC4000_MN_NTSC_PAL_A2 1
+#define XC4000_MN_NTSC_PAL_EIAJ 2
+#define XC4000_MN_NTSC_PAL_Mono 3
+#define XC4000_BG_PAL_A2 4
+#define XC4000_BG_PAL_NICAM 5
+#define XC4000_BG_PAL_MONO 6
+#define XC4000_I_PAL_NICAM 7
+#define XC4000_I_PAL_NICAM_MONO 8
+#define XC4000_DK_PAL_A2 9
+#define XC4000_DK_PAL_NICAM 10
+#define XC4000_DK_PAL_MONO 11
+#define XC4000_DK_SECAM_A2DK1 12
+#define XC4000_DK_SECAM_A2LDK3 13
+#define XC4000_DK_SECAM_A2MONO 14
+#define XC4000_DK_SECAM_NICAM 15
+#define XC4000_L_SECAM_NICAM 16
+#define XC4000_LC_SECAM_NICAM 17
+#define XC4000_DTV6 18
+#define XC4000_DTV8 19
+#define XC4000_DTV7_8 20
+#define XC4000_DTV7 21
+#define XC4000_FM_Radio_INPUT2 22
+#define XC4000_FM_Radio_INPUT1 23
+
+static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = {
+ {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500},
+ {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600},
+ {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500},
+ {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500},
+ {"B/G-PAL-A2", 0x0000, 0x8159, 5640},
+ {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740},
+ {"B/G-PAL-MONO", 0x0078, 0x8159, 5500},
+ {"I-PAL-NICAM", 0x0080, 0x8049, 6240},
+ {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000},
+ {"D/K-PAL-A2", 0x0000, 0x8049, 6380},
+ {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200},
+ {"D/K-PAL-MONO", 0x0078, 0x8049, 6500},
+ {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340},
+ {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000},
+ {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500},
+ {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200},
+ {"L-SECAM-NICAM", 0x8080, 0x0009, 6200},
+ {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200},
+ {"DTV6", 0x00C0, 0x8002, 0},
+ {"DTV8", 0x00C0, 0x800B, 0},
+ {"DTV7/8", 0x00C0, 0x801B, 0},
+ {"DTV7", 0x00C0, 0x8007, 0},
+ {"FM Radio-INPUT2", 0x0008, 0x9800, 10700},
+ {"FM Radio-INPUT1", 0x0008, 0x9000, 10700}
+};
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val);
+static int xc4000_tuner_reset(struct dvb_frontend *fe);
+static void xc_debug_dump(struct xc4000_priv *priv);
+
+static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len)
+{
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = buf, .len = len };
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
+ if (priv->ignore_i2c_write_errors == 0) {
+ printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n",
+ len);
+ if (len == 4) {
+ printk(KERN_ERR "bytes %*ph\n", 4, buf);
+ }
+ return -EREMOTEIO;
+ }
+ }
+ return 0;
+}
+
+static int xc4000_tuner_reset(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (fe->callback) {
+ ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv :
+ priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ XC4000_TUNER_RESET, 0);
+ if (ret) {
+ printk(KERN_ERR "xc4000: reset failed\n");
+ return -EREMOTEIO;
+ }
+ } else {
+ printk(KERN_ERR "xc4000: no tuner reset callback function, "
+ "fatal\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData)
+{
+ u8 buf[4];
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ buf[2] = (i2cData >> 8) & 0xFF;
+ buf[3] = i2cData & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 4);
+
+ return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ int i, nbytes_to_send, result;
+ unsigned int len, pos, index;
+ u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+ index = 0;
+ while ((i2c_sequence[index] != 0xFF) ||
+ (i2c_sequence[index + 1] != 0xFF)) {
+ len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
+ if (len == 0x0000) {
+ /* RESET command */
+ /* NOTE: this is ignored, as the reset callback was */
+ /* already called by check_firmware() */
+ index += 2;
+ } else if (len & 0x8000) {
+ /* WAIT command */
+ msleep(len & 0x7FFF);
+ index += 2;
+ } else {
+ /* Send i2c data whilst ensuring individual transactions
+ * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+ */
+ index += 2;
+ buf[0] = i2c_sequence[index];
+ buf[1] = i2c_sequence[index + 1];
+ pos = 2;
+ while (pos < len) {
+ if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
+ nbytes_to_send =
+ XC_MAX_I2C_WRITE_LENGTH;
+ else
+ nbytes_to_send = (len - pos + 2);
+ for (i = 2; i < nbytes_to_send; i++) {
+ buf[i] = i2c_sequence[index + pos +
+ i - 2];
+ }
+ result = xc_send_i2c_data(priv, buf,
+ nbytes_to_send);
+
+ if (result != 0)
+ return result;
+
+ pos += nbytes_to_send - 2;
+ }
+ index += len;
+ }
+ }
+ return 0;
+}
+
+static int xc_set_tv_standard(struct xc4000_priv *priv,
+ u16 video_mode, u16 audio_mode)
+{
+ int ret;
+ dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode);
+ dprintk(1, "%s() Standard = %s\n",
+ __func__,
+ xc4000_standard[priv->video_standard].Name);
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+
+ ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode);
+ if (ret == 0)
+ ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode);
+
+ priv->ignore_i2c_write_errors = 0;
+
+ return ret;
+}
+
+static int xc_set_signal_source(struct xc4000_priv *priv, u16 rf_mode)
+{
+ dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
+ rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+ if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
+ rf_mode = XC_RF_MODE_CABLE;
+ printk(KERN_ERR
+ "%s(), Invalid mode, defaulting to CABLE",
+ __func__);
+ }
+ return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops;
+
+static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz)
+{
+ u16 freq_code;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ if ((freq_hz > xc4000_tuner_ops.info.frequency_max) ||
+ (freq_hz < xc4000_tuner_ops.info.frequency_min))
+ return -EINVAL;
+
+ freq_code = (u16)(freq_hz / 15625);
+
+ /* WAS: Starting in firmware version 1.1.44, Xceive recommends using the
+ FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
+ only be used for fast scanning for channel lock) */
+ /* WAS: XREG_FINERFREQ */
+ return xc_write_reg(priv, XREG_RF_FREQ, freq_code);
+}
+
+static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope)
+{
+ return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz)
+{
+ int result;
+ u16 regData;
+ u32 tmp;
+
+ result = xc4000_readreg(priv, XREG_FREQ_ERROR, &regData);
+ if (result != 0)
+ return result;
+
+ tmp = (u32)regData & 0xFFFFU;
+ tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp);
+ (*freq_error_hz) = tmp * 15625;
+ return result;
+}
+
+static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status)
+{
+ return xc4000_readreg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc4000_priv *priv,
+ u8 *hw_majorversion, u8 *hw_minorversion,
+ u8 *fw_majorversion, u8 *fw_minorversion)
+{
+ u16 data;
+ int result;
+
+ result = xc4000_readreg(priv, XREG_VERSION, &data);
+ if (result != 0)
+ return result;
+
+ (*hw_majorversion) = (data >> 12) & 0x0F;
+ (*hw_minorversion) = (data >> 8) & 0x0F;
+ (*fw_majorversion) = (data >> 4) & 0x0F;
+ (*fw_minorversion) = data & 0x0F;
+
+ return 0;
+}
+
+static int xc_get_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz)
+{
+ u16 regData;
+ int result;
+
+ result = xc4000_readreg(priv, XREG_HSYNC_FREQ, &regData);
+ if (result != 0)
+ return result;
+
+ (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+ return result;
+}
+
+static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines)
+{
+ return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc4000_priv *priv, u16 *quality)
+{
+ return xc4000_readreg(priv, XREG_QUALITY, quality);
+}
+
+static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal)
+{
+ return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal);
+}
+
+static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise)
+{
+ return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise);
+}
+
+static u16 xc_wait_for_lock(struct xc4000_priv *priv)
+{
+ u16 lock_state = 0;
+ int watchdog_count = 40;
+
+ while ((lock_state == 0) && (watchdog_count > 0)) {
+ xc_get_lock_status(priv, &lock_state);
+ if (lock_state != 1) {
+ msleep(5);
+ watchdog_count--;
+ }
+ }
+ return lock_state;
+}
+
+static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz)
+{
+ int found = 1;
+ int result;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+ result = xc_set_rf_frequency(priv, freq_hz);
+ priv->ignore_i2c_write_errors = 0;
+
+ if (result != 0)
+ return 0;
+
+ /* wait for lock only in analog TV mode */
+ if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) {
+ if (xc_wait_for_lock(priv) != 1)
+ found = 0;
+ }
+
+ /* Wait for stats to stabilize.
+ * Frame Lines needs two frame times after initial lock
+ * before it is valid.
+ */
+ msleep(debug ? 100 : 10);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return found;
+}
+
+static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ u8 bval[2] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = &buf[0], .len = 2 },
+ { .addr = priv->i2c_props.addr,
+ .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+ };
+
+ if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
+ printk(KERN_ERR "xc4000: I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (bval[0] << 8) | bval[1];
+ return 0;
+}
+
+#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0)
+static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq)
+{
+ if (type & BASE)
+ printk(KERN_CONT "BASE ");
+ if (type & INIT1)
+ printk(KERN_CONT "INIT1 ");
+ if (type & F8MHZ)
+ printk(KERN_CONT "F8MHZ ");
+ if (type & MTS)
+ printk(KERN_CONT "MTS ");
+ if (type & D2620)
+ printk(KERN_CONT "D2620 ");
+ if (type & D2633)
+ printk(KERN_CONT "D2633 ");
+ if (type & DTV6)
+ printk(KERN_CONT "DTV6 ");
+ if (type & QAM)
+ printk(KERN_CONT "QAM ");
+ if (type & DTV7)
+ printk(KERN_CONT "DTV7 ");
+ if (type & DTV78)
+ printk(KERN_CONT "DTV78 ");
+ if (type & DTV8)
+ printk(KERN_CONT "DTV8 ");
+ if (type & FM)
+ printk(KERN_CONT "FM ");
+ if (type & INPUT1)
+ printk(KERN_CONT "INPUT1 ");
+ if (type & LCD)
+ printk(KERN_CONT "LCD ");
+ if (type & NOGD)
+ printk(KERN_CONT "NOGD ");
+ if (type & MONO)
+ printk(KERN_CONT "MONO ");
+ if (type & ATSC)
+ printk(KERN_CONT "ATSC ");
+ if (type & IF)
+ printk(KERN_CONT "IF ");
+ if (type & LG60)
+ printk(KERN_CONT "LG60 ");
+ if (type & ATI638)
+ printk(KERN_CONT "ATI638 ");
+ if (type & OREN538)
+ printk(KERN_CONT "OREN538 ");
+ if (type & OREN36)
+ printk(KERN_CONT "OREN36 ");
+ if (type & TOYOTA388)
+ printk(KERN_CONT "TOYOTA388 ");
+ if (type & TOYOTA794)
+ printk(KERN_CONT "TOYOTA794 ");
+ if (type & DIBCOM52)
+ printk(KERN_CONT "DIBCOM52 ");
+ if (type & ZARLINK456)
+ printk(KERN_CONT "ZARLINK456 ");
+ if (type & CHINA)
+ printk(KERN_CONT "CHINA ");
+ if (type & F6MHZ)
+ printk(KERN_CONT "F6MHZ ");
+ if (type & INPUT2)
+ printk(KERN_CONT "INPUT2 ");
+ if (type & SCODE)
+ printk(KERN_CONT "SCODE ");
+ if (type & HAS_IF)
+ printk(KERN_CONT "HAS_IF_%d ", int_freq);
+}
+
+static int seek_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int i, best_i = -1;
+ unsigned int best_nr_diffs = 255U;
+
+ if (!priv->firm) {
+ printk(KERN_ERR "Error! firmware not loaded\n");
+ return -EINVAL;
+ }
+
+ if (((type & ~SCODE) == 0) && (*id == 0))
+ *id = V4L2_STD_PAL;
+
+ /* Seek for generic video standard match */
+ for (i = 0; i < priv->firm_size; i++) {
+ v4l2_std_id id_diff_mask =
+ (priv->firm[i].id ^ (*id)) & (*id);
+ unsigned int type_diff_mask =
+ (priv->firm[i].type ^ type)
+ & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE);
+ unsigned int nr_diffs;
+
+ if (type_diff_mask
+ & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE))
+ continue;
+
+ nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask);
+ if (!nr_diffs) /* Supports all the requested standards */
+ goto found;
+
+ if (nr_diffs < best_nr_diffs) {
+ best_nr_diffs = nr_diffs;
+ best_i = i;
+ }
+ }
+
+ /* FIXME: Would make sense to seek for type "hint" match ? */
+ if (best_i < 0) {
+ i = -ENOENT;
+ goto ret;
+ }
+
+ if (best_nr_diffs > 0U) {
+ printk(KERN_WARNING
+ "Selecting best matching firmware (%u bits differ) for "
+ "type=(%x), id %016llx:\n",
+ best_nr_diffs, type, (unsigned long long)*id);
+ i = best_i;
+ }
+
+found:
+ *id = priv->firm[i].id;
+
+ret:
+ if (debug) {
+ printk(KERN_DEBUG "%s firmware for type=",
+ (i < 0) ? "Can't find" : "Found");
+ dump_firm_type(type);
+ printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id);
+ }
+ return i;
+}
+
+static int load_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+
+ p = priv->firm[pos].ptr;
+
+ /* Don't complain when the request fails because of i2c stretching */
+ priv->ignore_i2c_write_errors = 1;
+
+ rc = xc_load_i2c_sequence(fe, p);
+
+ priv->ignore_i2c_write_errors = 0;
+
+ return rc;
+}
+
+static int xc4000_fwupload(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ const struct firmware *fw = NULL;
+ const unsigned char *p, *endp;
+ int rc = 0;
+ int n, n_array;
+ char name[33];
+ const char *fname;
+
+ if (firmware_name[0] != '\0')
+ fname = firmware_name;
+ else
+ fname = XC4000_DEFAULT_FIRMWARE;
+
+ dprintk(1, "Reading firmware %s\n", fname);
+ rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent);
+ if (rc < 0) {
+ if (rc == -ENOENT)
+ printk(KERN_ERR "Error: firmware %s not found.\n", fname);
+ else
+ printk(KERN_ERR "Error %d while requesting firmware %s\n",
+ rc, fname);
+
+ return rc;
+ }
+ p = fw->data;
+ endp = p + fw->size;
+
+ if (fw->size < sizeof(name) - 1 + 2 + 2) {
+ printk(KERN_ERR "Error: firmware file %s has invalid size!\n",
+ fname);
+ goto corrupt;
+ }
+
+ memcpy(name, p, sizeof(name) - 1);
+ name[sizeof(name) - 1] = '\0';
+ p += sizeof(name) - 1;
+
+ priv->firm_version = get_unaligned_le16(p);
+ p += 2;
+
+ n_array = get_unaligned_le16(p);
+ p += 2;
+
+ dprintk(1, "Loading %d firmware images from %s, type: %s, ver %d.%d\n",
+ n_array, fname, name,
+ priv->firm_version >> 8, priv->firm_version & 0xff);
+
+ priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL);
+ if (priv->firm == NULL) {
+ printk(KERN_ERR "Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+ priv->firm_size = n_array;
+
+ n = -1;
+ while (p < endp) {
+ __u32 type, size;
+ v4l2_std_id id;
+ __u16 int_freq = 0;
+
+ n++;
+ if (n >= n_array) {
+ printk(KERN_ERR "More firmware images in file than "
+ "were expected!\n");
+ goto corrupt;
+ }
+
+ /* Checks if there's enough bytes to read */
+ if (endp - p < sizeof(type) + sizeof(id) + sizeof(size))
+ goto header;
+
+ type = get_unaligned_le32(p);
+ p += sizeof(type);
+
+ id = get_unaligned_le64(p);
+ p += sizeof(id);
+
+ if (type & HAS_IF) {
+ int_freq = get_unaligned_le16(p);
+ p += sizeof(int_freq);
+ if (endp - p < sizeof(size))
+ goto header;
+ }
+
+ size = get_unaligned_le32(p);
+ p += sizeof(size);
+
+ if (!size || size > endp - p) {
+ printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n",
+ type, (unsigned long long)id,
+ (unsigned)(endp - p), size);
+ goto corrupt;
+ }
+
+ priv->firm[n].ptr = kzalloc(size, GFP_KERNEL);
+ if (priv->firm[n].ptr == NULL) {
+ printk(KERN_ERR "Not enough memory to load firmware file.\n");
+ rc = -ENOMEM;
+ goto done;
+ }
+
+ if (debug) {
+ printk(KERN_DEBUG "Reading firmware type ");
+ dump_firm_type_and_int_freq(type, int_freq);
+ printk(KERN_DEBUG "(%x), id %llx, size=%d.\n",
+ type, (unsigned long long)id, size);
+ }
+
+ memcpy(priv->firm[n].ptr, p, size);
+ priv->firm[n].type = type;
+ priv->firm[n].id = id;
+ priv->firm[n].size = size;
+ priv->firm[n].int_freq = int_freq;
+
+ p += size;
+ }
+
+ if (n + 1 != priv->firm_size) {
+ printk(KERN_ERR "Firmware file is incomplete!\n");
+ goto corrupt;
+ }
+
+ goto done;
+
+header:
+ printk(KERN_ERR "Firmware header is incomplete!\n");
+corrupt:
+ rc = -EINVAL;
+ printk(KERN_ERR "Error: firmware file is corrupted!\n");
+
+done:
+ release_firmware(fw);
+ if (rc == 0)
+ dprintk(1, "Firmware files loaded.\n");
+
+ return rc;
+}
+
+static int load_scode(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id *id, __u16 int_freq, int scode)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int pos, rc;
+ unsigned char *p;
+ u8 scode_buf[13];
+ u8 indirect_mode[5];
+
+ dprintk(1, "%s called int_freq=%d\n", __func__, int_freq);
+
+ if (!int_freq) {
+ pos = seek_firmware(fe, type, id);
+ if (pos < 0)
+ return pos;
+ } else {
+ for (pos = 0; pos < priv->firm_size; pos++) {
+ if ((priv->firm[pos].int_freq == int_freq) &&
+ (priv->firm[pos].type & HAS_IF))
+ break;
+ }
+ if (pos == priv->firm_size)
+ return -ENOENT;
+ }
+
+ p = priv->firm[pos].ptr;
+
+ if (priv->firm[pos].size != 12 * 16 || scode >= 16)
+ return -EINVAL;
+ p += 12 * scode;
+
+ if (debug) {
+ tuner_info("Loading SCODE for type=");
+ dump_firm_type_and_int_freq(priv->firm[pos].type,
+ priv->firm[pos].int_freq);
+ printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type,
+ (unsigned long long)*id);
+ }
+
+ scode_buf[0] = 0x00;
+ memcpy(&scode_buf[1], p, 12);
+
+ /* Enter direct-mode */
+ rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0);
+ if (rc < 0) {
+ printk(KERN_ERR "failed to put device into direct mode!\n");
+ return -EIO;
+ }
+
+ rc = xc_send_i2c_data(priv, scode_buf, 13);
+ if (rc != 0) {
+ /* Even if the send failed, make sure we set back to indirect
+ mode */
+ printk(KERN_ERR "Failed to set scode %d\n", rc);
+ }
+
+ /* Switch back to indirect-mode */
+ memset(indirect_mode, 0, sizeof(indirect_mode));
+ indirect_mode[4] = 0x88;
+ xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode));
+ msleep(10);
+
+ return 0;
+}
+
+static int check_firmware(struct dvb_frontend *fe, unsigned int type,
+ v4l2_std_id std, __u16 int_freq)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ struct firmware_properties new_fw;
+ int rc = 0, is_retry = 0;
+ u16 hwmodel;
+ v4l2_std_id std0;
+ u8 hw_major, hw_minor, fw_major, fw_minor;
+
+ dprintk(1, "%s called\n", __func__);
+
+ if (!priv->firm) {
+ rc = xc4000_fwupload(fe);
+ if (rc < 0)
+ return rc;
+ }
+
+retry:
+ new_fw.type = type;
+ new_fw.id = std;
+ new_fw.std_req = std;
+ new_fw.scode_table = SCODE;
+ new_fw.scode_nr = 0;
+ new_fw.int_freq = int_freq;
+
+ dprintk(1, "checking firmware, user requested type=");
+ if (debug) {
+ dump_firm_type(new_fw.type);
+ printk(KERN_CONT "(%x), id %016llx, ", new_fw.type,
+ (unsigned long long)new_fw.std_req);
+ if (!int_freq)
+ printk(KERN_CONT "scode_tbl ");
+ else
+ printk(KERN_CONT "int_freq %d, ", new_fw.int_freq);
+ printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr);
+ }
+
+ /* No need to reload base firmware if it matches */
+ if (priv->cur_fw.type & BASE) {
+ dprintk(1, "BASE firmware not changed.\n");
+ goto skip_base;
+ }
+
+ /* Updating BASE - forget about all currently loaded firmware */
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+
+ /* Reset is needed before loading firmware */
+ rc = xc4000_tuner_reset(fe);
+ if (rc < 0)
+ goto fail;
+
+ /* BASE firmwares are all std0 */
+ std0 = 0;
+ rc = load_firmware(fe, BASE, &std0);
+ if (rc < 0) {
+ printk(KERN_ERR "Error %d while loading base firmware\n", rc);
+ goto fail;
+ }
+
+ /* Load INIT1, if needed */
+ dprintk(1, "Load init1 firmware, if exists\n");
+
+ rc = load_firmware(fe, BASE | INIT1, &std0);
+ if (rc == -ENOENT)
+ rc = load_firmware(fe, BASE | INIT1, &std0);
+ if (rc < 0 && rc != -ENOENT) {
+ tuner_err("Error %d while loading init1 firmware\n",
+ rc);
+ goto fail;
+ }
+
+skip_base:
+ /*
+ * No need to reload standard specific firmware if base firmware
+ * was not reloaded and requested video standards have not changed.
+ */
+ if (priv->cur_fw.type == (BASE | new_fw.type) &&
+ priv->cur_fw.std_req == std) {
+ dprintk(1, "Std-specific firmware already loaded.\n");
+ goto skip_std_specific;
+ }
+
+ /* Reloading std-specific firmware forces a SCODE update */
+ priv->cur_fw.scode_table = 0;
+
+ /* Load the standard firmware */
+ rc = load_firmware(fe, new_fw.type, &new_fw.id);
+
+ if (rc < 0)
+ goto fail;
+
+skip_std_specific:
+ if (priv->cur_fw.scode_table == new_fw.scode_table &&
+ priv->cur_fw.scode_nr == new_fw.scode_nr) {
+ dprintk(1, "SCODE firmware already loaded.\n");
+ goto check_device;
+ }
+
+ /* Load SCODE firmware, if exists */
+ rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id,
+ new_fw.int_freq, new_fw.scode_nr);
+ if (rc != 0)
+ dprintk(1, "load scode failed %d\n", rc);
+
+check_device:
+ rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel);
+
+ if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major,
+ &fw_minor) != 0) {
+ printk(KERN_ERR "Unable to read tuner registers.\n");
+ goto fail;
+ }
+
+ dprintk(1, "Device is Xceive %d version %d.%d, "
+ "firmware version %d.%d\n",
+ hwmodel, hw_major, hw_minor, fw_major, fw_minor);
+
+ /* Check firmware version against what we downloaded. */
+ if (priv->firm_version != ((fw_major << 8) | fw_minor)) {
+ printk(KERN_WARNING
+ "Incorrect readback of firmware version %d.%d.\n",
+ fw_major, fw_minor);
+ goto fail;
+ }
+
+ /* Check that the tuner hardware model remains consistent over time. */
+ if (priv->hwmodel == 0 &&
+ (hwmodel == XC_PRODUCT_ID_XC4000 ||
+ hwmodel == XC_PRODUCT_ID_XC4100)) {
+ priv->hwmodel = hwmodel;
+ priv->hwvers = (hw_major << 8) | hw_minor;
+ } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel ||
+ priv->hwvers != ((hw_major << 8) | hw_minor)) {
+ printk(KERN_WARNING
+ "Read invalid device hardware information - tuner "
+ "hung?\n");
+ goto fail;
+ }
+
+ memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw));
+
+ /*
+ * By setting BASE in cur_fw.type only after successfully loading all
+ * firmwares, we can:
+ * 1. Identify that BASE firmware with type=0 has been loaded;
+ * 2. Tell whether BASE firmware was just changed the next time through.
+ */
+ priv->cur_fw.type |= BASE;
+
+ return 0;
+
+fail:
+ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
+ if (!is_retry) {
+ msleep(50);
+ is_retry = 1;
+ dprintk(1, "Retrying firmware load\n");
+ goto retry;
+ }
+
+ if (rc == -ENOENT)
+ rc = -EINVAL;
+ return rc;
+}
+
+static void xc_debug_dump(struct xc4000_priv *priv)
+{
+ u16 adc_envelope;
+ u32 freq_error_hz = 0;
+ u16 lock_status;
+ u32 hsync_freq_hz = 0;
+ u16 frame_lines;
+ u16 quality;
+ u16 signal = 0;
+ u16 noise = 0;
+ u8 hw_majorversion = 0, hw_minorversion = 0;
+ u8 fw_majorversion = 0, fw_minorversion = 0;
+
+ xc_get_adc_envelope(priv, &adc_envelope);
+ dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+ xc_get_frequency_error(priv, &freq_error_hz);
+ dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+ xc_get_lock_status(priv, &lock_status);
+ dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+ lock_status);
+
+ xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+ &fw_majorversion, &fw_minorversion);
+ dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n",
+ hw_majorversion, hw_minorversion,
+ fw_majorversion, fw_minorversion);
+
+ if (priv->video_standard < XC4000_DTV6) {
+ xc_get_hsync_freq(priv, &hsync_freq_hz);
+ dprintk(1, "*** Horizontal sync frequency = %d Hz\n",
+ hsync_freq_hz);
+
+ xc_get_frame_lines(priv, &frame_lines);
+ dprintk(1, "*** Frame lines = %d\n", frame_lines);
+ }
+
+ xc_get_quality(priv, &quality);
+ dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality);
+
+ xc_get_signal_level(priv, &signal);
+ dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal);
+
+ xc_get_noise_level(priv, &noise);
+ dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise);
+}
+
+static int xc4000_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ u32 bw = c->bandwidth_hz;
+ struct xc4000_priv *priv = fe->tuner_priv;
+ unsigned int type;
+ int ret = -EREMOTEIO;
+
+ dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency);
+
+ mutex_lock(&priv->lock);
+
+ switch (delsys) {
+ case SYS_ATSC:
+ dprintk(1, "%s() VSB modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = c->frequency - 1750000;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = c->frequency - 1750000;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ dprintk(1, "%s() OFDM\n", __func__);
+ if (bw == 0) {
+ if (c->frequency < 400000000) {
+ priv->freq_hz = c->frequency - 2250000;
+ } else {
+ priv->freq_hz = c->frequency - 2750000;
+ }
+ priv->video_standard = XC4000_DTV7_8;
+ type = DTV78;
+ } else if (bw <= 6000000) {
+ priv->video_standard = XC4000_DTV6;
+ priv->freq_hz = c->frequency - 1750000;
+ type = DTV6;
+ } else if (bw <= 7000000) {
+ priv->video_standard = XC4000_DTV7;
+ priv->freq_hz = c->frequency - 2250000;
+ type = DTV7;
+ } else {
+ priv->video_standard = XC4000_DTV8;
+ priv->freq_hz = c->frequency - 2750000;
+ type = DTV8;
+ }
+ priv->rf_mode = XC_RF_MODE_AIR;
+ break;
+ default:
+ printk(KERN_ERR "xc4000 delivery system not supported!\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ dprintk(1, "%s() frequency=%d (compensated)\n",
+ __func__, priv->freq_hz);
+
+ /* Make sure the correct firmware type is loaded */
+ if (check_firmware(fe, type, 0, priv->if_khz) != 0)
+ goto fail;
+
+ priv->bandwidth = c->bandwidth_hz;
+
+ ret = xc_set_signal_source(priv, priv->rf_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n",
+ priv->rf_mode);
+ goto fail;
+ } else {
+ u16 video_mode, audio_mode;
+ video_mode = xc4000_standard[priv->video_standard].video_mode;
+ audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+ if (type == DTV6 && priv->firm_version != 0x0102)
+ video_mode |= 0x0001;
+ ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+ /* DJH - do not return when it fails... */
+ /* goto fail; */
+ }
+ }
+
+ if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+ ret = 0;
+ if (priv->dvb_amplitude != 0) {
+ if (xc_write_reg(priv, XREG_AMPLITUDE,
+ (priv->firm_version != 0x0102 ||
+ priv->dvb_amplitude != 134 ?
+ priv->dvb_amplitude : 132)) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (priv->set_smoothedcvbs != 0) {
+ if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: setting registers failed\n");
+ /* goto fail; */
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ ret = 0;
+
+fail:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ unsigned int type = 0;
+ int ret = -EREMOTEIO;
+
+ if (params->mode == V4L2_TUNER_RADIO) {
+ dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n",
+ __func__, params->frequency);
+
+ mutex_lock(&priv->lock);
+
+ params->std = 0;
+ priv->freq_hz = params->frequency * 125L / 2;
+
+ if (audio_std & XC4000_AUDIO_STD_INPUT1) {
+ priv->video_standard = XC4000_FM_Radio_INPUT1;
+ type = FM | INPUT1;
+ } else {
+ priv->video_standard = XC4000_FM_Radio_INPUT2;
+ type = FM | INPUT2;
+ }
+
+ goto tune_channel;
+ }
+
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __func__, params->frequency);
+
+ mutex_lock(&priv->lock);
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+
+ params->std &= V4L2_STD_ALL;
+ /* if std is not defined, choose one */
+ if (!params->std)
+ params->std = V4L2_STD_PAL_BG;
+
+ if (audio_std & XC4000_AUDIO_STD_MONO)
+ type = MONO;
+
+ if (params->std & V4L2_STD_MN) {
+ params->std = V4L2_STD_MN;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_MN_NTSC_PAL_Mono;
+ } else if (audio_std & XC4000_AUDIO_STD_A2) {
+ params->std |= V4L2_STD_A2;
+ priv->video_standard = XC4000_MN_NTSC_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_BTSC;
+ priv->video_standard = XC4000_MN_NTSC_PAL_BTSC;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_BG) {
+ params->std = V4L2_STD_PAL_BG;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_BG_PAL_MONO;
+ } else if (!(audio_std & XC4000_AUDIO_STD_A2)) {
+ if (!(audio_std & XC4000_AUDIO_STD_B)) {
+ params->std |= V4L2_STD_NICAM_A;
+ priv->video_standard = XC4000_BG_PAL_NICAM;
+ } else {
+ params->std |= V4L2_STD_NICAM_B;
+ priv->video_standard = XC4000_BG_PAL_NICAM;
+ }
+ } else {
+ if (!(audio_std & XC4000_AUDIO_STD_B)) {
+ params->std |= V4L2_STD_A2_A;
+ priv->video_standard = XC4000_BG_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_A2_B;
+ priv->video_standard = XC4000_BG_PAL_A2;
+ }
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_I) {
+ /* default to NICAM audio standard */
+ params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM;
+ if (audio_std & XC4000_AUDIO_STD_MONO)
+ priv->video_standard = XC4000_I_PAL_NICAM_MONO;
+ else
+ priv->video_standard = XC4000_I_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_DK) {
+ params->std = V4L2_STD_PAL_DK;
+ if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_DK_PAL_MONO;
+ } else if (audio_std & XC4000_AUDIO_STD_A2) {
+ params->std |= V4L2_STD_A2;
+ priv->video_standard = XC4000_DK_PAL_A2;
+ } else {
+ params->std |= V4L2_STD_NICAM;
+ priv->video_standard = XC4000_DK_PAL_NICAM;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_DK) {
+ /* default to A2 audio standard */
+ params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2;
+ if (audio_std & XC4000_AUDIO_STD_L) {
+ type = 0;
+ priv->video_standard = XC4000_DK_SECAM_NICAM;
+ } else if (audio_std & XC4000_AUDIO_STD_MONO) {
+ priv->video_standard = XC4000_DK_SECAM_A2MONO;
+ } else if (audio_std & XC4000_AUDIO_STD_K3) {
+ params->std |= V4L2_STD_SECAM_K3;
+ priv->video_standard = XC4000_DK_SECAM_A2LDK3;
+ } else {
+ priv->video_standard = XC4000_DK_SECAM_A2DK1;
+ }
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_L) {
+ /* default to NICAM audio standard */
+ type = 0;
+ params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM;
+ priv->video_standard = XC4000_L_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_LC) {
+ /* default to NICAM audio standard */
+ type = 0;
+ params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM;
+ priv->video_standard = XC4000_LC_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+tune_channel:
+ /* FIXME: it could be air. */
+ priv->rf_mode = XC_RF_MODE_CABLE;
+
+ if (check_firmware(fe, type, params->std,
+ xc4000_standard[priv->video_standard].int_freq) != 0)
+ goto fail;
+
+ ret = xc_set_signal_source(priv, priv->rf_mode);
+ if (ret != 0) {
+ printk(KERN_ERR
+ "xc4000: xc_set_signal_source(%d) failed\n",
+ priv->rf_mode);
+ goto fail;
+ } else {
+ u16 video_mode, audio_mode;
+ video_mode = xc4000_standard[priv->video_standard].video_mode;
+ audio_mode = xc4000_standard[priv->video_standard].audio_mode;
+ if (priv->video_standard < XC4000_BG_PAL_A2) {
+ if (type & NOGD)
+ video_mode &= 0xFF7F;
+ } else if (priv->video_standard < XC4000_I_PAL_NICAM) {
+ if (priv->firm_version == 0x0102)
+ video_mode &= 0xFEFF;
+ if (audio_std & XC4000_AUDIO_STD_B)
+ video_mode |= 0x0080;
+ }
+ ret = xc_set_tv_standard(priv, video_mode, audio_mode);
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n");
+ goto fail;
+ }
+ }
+
+ if (xc_write_reg(priv, XREG_D_CODE, 0) == 0)
+ ret = 0;
+ if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0)
+ ret = -EREMOTEIO;
+ if (priv->set_smoothedcvbs != 0) {
+ if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0)
+ ret = -EREMOTEIO;
+ }
+ if (ret != 0) {
+ printk(KERN_ERR "xc4000: setting registers failed\n");
+ goto fail;
+ }
+
+ xc_tune_channel(priv, priv->freq_hz);
+
+ ret = 0;
+
+fail:
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ u16 value = 0;
+ int rc;
+
+ mutex_lock(&priv->lock);
+ rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value);
+ mutex_unlock(&priv->lock);
+
+ if (rc < 0)
+ goto ret;
+
+ /* Informations from real testing of DVB-T and radio part,
+ coeficient for one dB is 0xff.
+ */
+ tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value);
+
+ /* all known digital modes */
+ if ((priv->video_standard == XC4000_DTV6) ||
+ (priv->video_standard == XC4000_DTV7) ||
+ (priv->video_standard == XC4000_DTV7_8) ||
+ (priv->video_standard == XC4000_DTV8))
+ goto digital;
+
+ /* Analog mode has NOISE LEVEL important, signal
+ depends only on gain of antenna and amplifiers,
+ but it doesn't tell anything about real quality
+ of reception.
+ */
+ mutex_lock(&priv->lock);
+ rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value);
+ mutex_unlock(&priv->lock);
+
+ tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value);
+
+ /* highest noise level: 32dB */
+ if (value >= 0x2000) {
+ value = 0;
+ } else {
+ value = ~value << 3;
+ }
+
+ goto ret;
+
+ /* Digital mode has SIGNAL LEVEL important and real
+ noise level is stored in demodulator registers.
+ */
+digital:
+ /* best signal: -50dB */
+ if (value <= 0x3200) {
+ value = 0xffff;
+ /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */
+ } else if (value >= 0x713A) {
+ value = 0;
+ } else {
+ value = ~(value - 0x3200) << 2;
+ }
+
+ret:
+ *strength = value;
+
+ return rc;
+}
+
+static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ *freq = priv->freq_hz;
+
+ if (debug) {
+ mutex_lock(&priv->lock);
+ if ((priv->cur_fw.type
+ & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) {
+ u16 snr = 0;
+ if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) {
+ mutex_unlock(&priv->lock);
+ dprintk(1, "%s() freq = %u, SNR = %d\n",
+ __func__, *freq, snr);
+ return 0;
+ }
+ }
+ mutex_unlock(&priv->lock);
+ }
+
+ dprintk(1, "%s()\n", __func__);
+
+ return 0;
+}
+
+static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int xc4000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ mutex_lock(&priv->lock);
+
+ if (priv->cur_fw.type & BASE)
+ xc_get_lock_status(priv, &lock_status);
+
+ *status = (lock_status == 1 ?
+ TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0);
+ if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8))
+ *status &= (~TUNER_STATUS_STEREO);
+
+ mutex_unlock(&priv->lock);
+
+ dprintk(2, "%s() lock_status = %d\n", __func__, lock_status);
+
+ return 0;
+}
+
+static int xc4000_sleep(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&priv->lock);
+
+ /* Avoid firmware reload on slow devices */
+ if ((no_poweroff == 2 ||
+ (no_poweroff == 0 && priv->default_pm != 0)) &&
+ (priv->cur_fw.type & BASE) != 0) {
+ /* force reset and firmware reload */
+ priv->cur_fw.type = XC_POWERED_DOWN;
+
+ if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) {
+ printk(KERN_ERR
+ "xc4000: %s() unable to shutdown tuner\n",
+ __func__);
+ ret = -EREMOTEIO;
+ }
+ msleep(20);
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int xc4000_init(struct dvb_frontend *fe)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ return 0;
+}
+
+static int xc4000_release(struct dvb_frontend *fe)
+{
+ struct xc4000_priv *priv = fe->tuner_priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&xc4000_list_mutex);
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&xc4000_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops xc4000_tuner_ops = {
+ .info = {
+ .name = "Xceive XC4000",
+ .frequency_min = 1000000,
+ .frequency_max = 1023000000,
+ .frequency_step = 50000,
+ },
+
+ .release = xc4000_release,
+ .init = xc4000_init,
+ .sleep = xc4000_sleep,
+
+ .set_params = xc4000_set_params,
+ .set_analog_params = xc4000_set_analog_params,
+ .get_frequency = xc4000_get_frequency,
+ .get_rf_strength = xc4000_get_signal,
+ .get_bandwidth = xc4000_get_bandwidth,
+ .get_status = xc4000_get_status
+};
+
+struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ struct xc4000_config *cfg)
+{
+ struct xc4000_priv *priv = NULL;
+ int instance;
+ u16 id = 0;
+
+ dprintk(1, "%s(%d-%04x)\n", __func__,
+ i2c ? i2c_adapter_id(i2c) : -1,
+ cfg ? cfg->i2c_address : -1);
+
+ mutex_lock(&xc4000_list_mutex);
+
+ instance = hybrid_tuner_request_state(struct xc4000_priv, priv,
+ hybrid_tuner_instance_list,
+ i2c, cfg->i2c_address, "xc4000");
+ switch (instance) {
+ case 0:
+ goto fail;
+ break;
+ case 1:
+ /* new tuner instance */
+ priv->bandwidth = 6000000;
+ /* set default configuration */
+ priv->if_khz = 4560;
+ priv->default_pm = 0;
+ priv->dvb_amplitude = 134;
+ priv->set_smoothedcvbs = 1;
+ mutex_init(&priv->lock);
+ fe->tuner_priv = priv;
+ break;
+ default:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+ break;
+ }
+
+ if (cfg->if_khz != 0) {
+ /* copy configuration if provided by the caller */
+ priv->if_khz = cfg->if_khz;
+ priv->default_pm = cfg->default_pm;
+ priv->dvb_amplitude = cfg->dvb_amplitude;
+ priv->set_smoothedcvbs = cfg->set_smoothedcvbs;
+ }
+
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+
+ if (instance == 1) {
+ if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0)
+ goto fail;
+ } else {
+ id = ((priv->cur_fw.type & BASE) != 0 ?
+ priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED);
+ }
+
+ switch (id) {
+ case XC_PRODUCT_ID_XC4000:
+ case XC_PRODUCT_ID_XC4100:
+ printk(KERN_INFO
+ "xc4000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc4000: Firmware has been loaded previously\n");
+ break;
+ case XC_PRODUCT_ID_FW_NOT_LOADED:
+ printk(KERN_INFO
+ "xc4000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc4000: Firmware has not been loaded previously\n");
+ break;
+ default:
+ printk(KERN_ERR
+ "xc4000: Device not found at addr 0x%02x (0x%x)\n",
+ cfg->i2c_address, id);
+ goto fail;
+ }
+
+ mutex_unlock(&xc4000_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ if (instance == 1) {
+ int ret;
+ mutex_lock(&priv->lock);
+ ret = xc4000_fwupload(fe);
+ mutex_unlock(&priv->lock);
+ if (ret != 0)
+ goto fail2;
+ }
+
+ return fe;
+fail:
+ mutex_unlock(&xc4000_list_mutex);
+fail2:
+ xc4000_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL(xc4000_attach);
+
+MODULE_AUTHOR("Steven Toth, Davide Ferri");
+MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/xc4000.h b/drivers/media/tuners/xc4000.h
index e6a44d151cbd..e6a44d151cbd 100644
--- a/drivers/media/common/tuners/xc4000.h
+++ b/drivers/media/tuners/xc4000.h
diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c
new file mode 100644
index 000000000000..dc93cf338f36
--- /dev/null
+++ b/drivers/media/tuners/xc5000.c
@@ -0,0 +1,1366 @@
+/*
+ * Driver for Xceive XC5000 "QAM/8VSB single chip tuner"
+ *
+ * Copyright (c) 2007 Xceive Corporation
+ * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
+ * Copyright (c) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "xc5000.h"
+#include "tuner-i2c.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+static int no_poweroff;
+module_param(no_poweroff, int, 0644);
+MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n"
+ "\t\t1 keep device energized and with tuner ready all the times.\n"
+ "\t\tFaster, but consumes more power and keeps the device hotter");
+
+static DEFINE_MUTEX(xc5000_list_mutex);
+static LIST_HEAD(hybrid_tuner_instance_list);
+
+#define dprintk(level, fmt, arg...) if (debug >= level) \
+ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
+
+struct xc5000_priv {
+ struct tuner_i2c_props i2c_props;
+ struct list_head hybrid_tuner_instance_list;
+
+ u32 if_khz;
+ u16 xtal_khz;
+ u32 freq_hz;
+ u32 bandwidth;
+ u8 video_standard;
+ u8 rf_mode;
+ u8 radio_input;
+
+ int chip_id;
+ u16 pll_register_no;
+ u8 init_status_supported;
+ u8 fw_checksum_supported;
+};
+
+/* Misc Defines */
+#define MAX_TV_STANDARD 24
+#define XC_MAX_I2C_WRITE_LENGTH 64
+
+/* Signal Types */
+#define XC_RF_MODE_AIR 0
+#define XC_RF_MODE_CABLE 1
+
+/* Result codes */
+#define XC_RESULT_SUCCESS 0
+#define XC_RESULT_RESET_FAILURE 1
+#define XC_RESULT_I2C_WRITE_FAILURE 2
+#define XC_RESULT_I2C_READ_FAILURE 3
+#define XC_RESULT_OUT_OF_RANGE 5
+
+/* Product id */
+#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000
+#define XC_PRODUCT_ID_FW_LOADED 0x1388
+
+/* Registers */
+#define XREG_INIT 0x00
+#define XREG_VIDEO_MODE 0x01
+#define XREG_AUDIO_MODE 0x02
+#define XREG_RF_FREQ 0x03
+#define XREG_D_CODE 0x04
+#define XREG_IF_OUT 0x05
+#define XREG_SEEK_MODE 0x07
+#define XREG_POWER_DOWN 0x0A /* 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
+#define XREG_FINERFREQ 0x10
+#define XREG_DDIMODE 0x11
+
+#define XREG_ADC_ENV 0x00
+#define XREG_QUALITY 0x01
+#define XREG_FRAME_LINES 0x02
+#define XREG_HSYNC_FREQ 0x03
+#define XREG_LOCK 0x04
+#define XREG_FREQ_ERROR 0x05
+#define XREG_SNR 0x06
+#define XREG_VERSION 0x07
+#define XREG_PRODUCT_ID 0x08
+#define XREG_BUSY 0x09
+#define XREG_BUILD 0x0D
+#define XREG_TOTALGAIN 0x0F
+#define XREG_FW_CHECKSUM 0x12
+#define XREG_INIT_STATUS 0x13
+
+/*
+ Basic firmware description. This will remain with
+ the driver for documentation purposes.
+
+ This represents an I2C firmware file encoded as a
+ string of unsigned char. Format is as follows:
+
+ char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB
+ char[1 ]=len0_LSB -> length of first write transaction
+ char[2 ]=data0 -> first byte to be sent
+ char[3 ]=data1
+ char[4 ]=data2
+ char[ ]=...
+ char[M ]=dataN -> last byte to be sent
+ char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB
+ char[M+2]=len1_LSB -> length of second write transaction
+ char[M+3]=data0
+ char[M+4]=data1
+ ...
+ etc.
+
+ The [len] value should be interpreted as follows:
+
+ len= len_MSB _ len_LSB
+ len=1111_1111_1111_1111 : End of I2C_SEQUENCE
+ len=0000_0000_0000_0000 : Reset command: Do hardware reset
+ len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767)
+ len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms
+
+ For the RESET and WAIT commands, the two following bytes will contain
+ immediately the length of the following transaction.
+
+*/
+struct XC_TV_STANDARD {
+ char *Name;
+ u16 AudioMode;
+ u16 VideoMode;
+};
+
+/* Tuner standards */
+#define MN_NTSC_PAL_BTSC 0
+#define MN_NTSC_PAL_A2 1
+#define MN_NTSC_PAL_EIAJ 2
+#define MN_NTSC_PAL_Mono 3
+#define BG_PAL_A2 4
+#define BG_PAL_NICAM 5
+#define BG_PAL_MONO 6
+#define I_PAL_NICAM 7
+#define I_PAL_NICAM_MONO 8
+#define DK_PAL_A2 9
+#define DK_PAL_NICAM 10
+#define DK_PAL_MONO 11
+#define DK_SECAM_A2DK1 12
+#define DK_SECAM_A2LDK3 13
+#define DK_SECAM_A2MONO 14
+#define L_SECAM_NICAM 15
+#define LC_SECAM_NICAM 16
+#define DTV6 17
+#define DTV8 18
+#define DTV7_8 19
+#define DTV7 20
+#define FM_Radio_INPUT2 21
+#define FM_Radio_INPUT1 22
+#define FM_Radio_INPUT1_MONO 23
+
+static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
+ {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020},
+ {"M/N-NTSC/PAL-A2", 0x0600, 0x8020},
+ {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020},
+ {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020},
+ {"B/G-PAL-A2", 0x0A00, 0x8049},
+ {"B/G-PAL-NICAM", 0x0C04, 0x8049},
+ {"B/G-PAL-MONO", 0x0878, 0x8059},
+ {"I-PAL-NICAM", 0x1080, 0x8009},
+ {"I-PAL-NICAM-MONO", 0x0E78, 0x8009},
+ {"D/K-PAL-A2", 0x1600, 0x8009},
+ {"D/K-PAL-NICAM", 0x0E80, 0x8009},
+ {"D/K-PAL-MONO", 0x1478, 0x8009},
+ {"D/K-SECAM-A2 DK1", 0x1200, 0x8009},
+ {"D/K-SECAM-A2 L/DK3", 0x0E00, 0x8009},
+ {"D/K-SECAM-A2 MONO", 0x1478, 0x8009},
+ {"L-SECAM-NICAM", 0x8E82, 0x0009},
+ {"L'-SECAM-NICAM", 0x8E82, 0x4009},
+ {"DTV6", 0x00C0, 0x8002},
+ {"DTV8", 0x00C0, 0x800B},
+ {"DTV7/8", 0x00C0, 0x801B},
+ {"DTV7", 0x00C0, 0x8007},
+ {"FM Radio-INPUT2", 0x9802, 0x9002},
+ {"FM Radio-INPUT1", 0x0208, 0x9002},
+ {"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
+};
+
+
+struct xc5000_fw_cfg {
+ char *name;
+ u16 size;
+ u16 pll_reg;
+ u8 init_status_supported;
+ u8 fw_checksum_supported;
+};
+
+#define XC5000A_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
+static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
+ .name = XC5000A_FIRMWARE,
+ .size = 12401,
+ .pll_reg = 0x806c,
+};
+
+#define XC5000C_FIRMWARE "dvb-fe-xc5000c-4.1.30.7.fw"
+static const struct xc5000_fw_cfg xc5000c_41_024_5 = {
+ .name = XC5000C_FIRMWARE,
+ .size = 16497,
+ .pll_reg = 0x13,
+ .init_status_supported = 1,
+ .fw_checksum_supported = 1,
+};
+
+static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
+{
+ switch (chip_id) {
+ default:
+ case XC5000A:
+ return &xc5000a_1_6_114;
+ case XC5000C:
+ return &xc5000c_41_024_5;
+ }
+}
+
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force);
+static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
+static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
+static int xc5000_TunerReset(struct dvb_frontend *fe);
+
+static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len);
+ return XC_RESULT_I2C_WRITE_FAILURE;
+ }
+ return XC_RESULT_SUCCESS;
+}
+
+#if 0
+/* This routine is never used because the only time we read data from the
+ i2c bus is when we read registers, and we want that to be an atomic i2c
+ transaction in case we are on a multi-master bus */
+static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len)
+{
+ struct i2c_msg msg = { .addr = priv->i2c_props.addr,
+ .flags = I2C_M_RD, .buf = buf, .len = len };
+
+ if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) {
+ printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n", len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+#endif
+
+static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val)
+{
+ u8 buf[2] = { reg >> 8, reg & 0xff };
+ u8 bval[2] = { 0, 0 };
+ struct i2c_msg msg[2] = {
+ { .addr = priv->i2c_props.addr,
+ .flags = 0, .buf = &buf[0], .len = 2 },
+ { .addr = priv->i2c_props.addr,
+ .flags = I2C_M_RD, .buf = &bval[0], .len = 2 },
+ };
+
+ if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) {
+ printk(KERN_WARNING "xc5000: I2C read failed\n");
+ return -EREMOTEIO;
+ }
+
+ *val = (bval[0] << 8) | bval[1];
+ return XC_RESULT_SUCCESS;
+}
+
+static void xc_wait(int wait_ms)
+{
+ msleep(wait_ms);
+}
+
+static int xc5000_TunerReset(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (fe->callback) {
+ ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ?
+ fe->dvb->priv :
+ priv->i2c_props.adap->algo_data,
+ DVB_FRONTEND_COMPONENT_TUNER,
+ XC5000_TUNER_RESET, 0);
+ if (ret) {
+ printk(KERN_ERR "xc5000: reset failed\n");
+ return XC_RESULT_RESET_FAILURE;
+ }
+ } else {
+ printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n");
+ return XC_RESULT_RESET_FAILURE;
+ }
+ return XC_RESULT_SUCCESS;
+}
+
+static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData)
+{
+ u8 buf[4];
+ int WatchDogTimer = 100;
+ int result;
+
+ buf[0] = (regAddr >> 8) & 0xFF;
+ buf[1] = regAddr & 0xFF;
+ buf[2] = (i2cData >> 8) & 0xFF;
+ buf[3] = i2cData & 0xFF;
+ result = xc_send_i2c_data(priv, buf, 4);
+ if (result == XC_RESULT_SUCCESS) {
+ /* wait for busy flag to clear */
+ while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) {
+ result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf);
+ if (result == XC_RESULT_SUCCESS) {
+ if ((buf[0] == 0) && (buf[1] == 0)) {
+ /* busy flag cleared */
+ break;
+ } else {
+ xc_wait(5); /* wait 5 ms */
+ WatchDogTimer--;
+ }
+ }
+ }
+ }
+ if (WatchDogTimer <= 0)
+ result = XC_RESULT_I2C_WRITE_FAILURE;
+
+ return result;
+}
+
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
+ int i, nbytes_to_send, result;
+ unsigned int len, pos, index;
+ u8 buf[XC_MAX_I2C_WRITE_LENGTH];
+
+ index = 0;
+ while ((i2c_sequence[index] != 0xFF) ||
+ (i2c_sequence[index + 1] != 0xFF)) {
+ len = i2c_sequence[index] * 256 + i2c_sequence[index+1];
+ if (len == 0x0000) {
+ /* RESET command */
+ result = xc5000_TunerReset(fe);
+ index += 2;
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+ } else if (len & 0x8000) {
+ /* WAIT command */
+ xc_wait(len & 0x7FFF);
+ index += 2;
+ } else {
+ /* Send i2c data whilst ensuring individual transactions
+ * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes.
+ */
+ index += 2;
+ buf[0] = i2c_sequence[index];
+ buf[1] = i2c_sequence[index + 1];
+ pos = 2;
+ while (pos < len) {
+ if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2)
+ nbytes_to_send =
+ XC_MAX_I2C_WRITE_LENGTH;
+ else
+ nbytes_to_send = (len - pos + 2);
+ for (i = 2; i < nbytes_to_send; i++) {
+ buf[i] = i2c_sequence[index + pos +
+ i - 2];
+ }
+ result = xc_send_i2c_data(priv, buf,
+ nbytes_to_send);
+
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ pos += nbytes_to_send - 2;
+ }
+ index += len;
+ }
+ }
+ return XC_RESULT_SUCCESS;
+}
+
+static int xc_initialize(struct xc5000_priv *priv)
+{
+ dprintk(1, "%s()\n", __func__);
+ return xc_write_reg(priv, XREG_INIT, 0);
+}
+
+static int xc_SetTVStandard(struct xc5000_priv *priv,
+ u16 VideoMode, u16 AudioMode)
+{
+ int ret;
+ dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode);
+ dprintk(1, "%s() Standard = %s\n",
+ __func__,
+ XC5000_Standard[priv->video_standard].Name);
+
+ ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode);
+ if (ret == XC_RESULT_SUCCESS)
+ ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode);
+
+ return ret;
+}
+
+static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode)
+{
+ dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode,
+ rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE");
+
+ if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) {
+ rf_mode = XC_RF_MODE_CABLE;
+ printk(KERN_ERR
+ "%s(), Invalid mode, defaulting to CABLE",
+ __func__);
+ }
+ return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode);
+}
+
+static const struct dvb_tuner_ops xc5000_tuner_ops;
+
+static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz)
+{
+ u16 freq_code;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ if ((freq_hz > xc5000_tuner_ops.info.frequency_max) ||
+ (freq_hz < xc5000_tuner_ops.info.frequency_min))
+ return XC_RESULT_OUT_OF_RANGE;
+
+ freq_code = (u16)(freq_hz / 15625);
+
+ /* Starting in firmware version 1.1.44, Xceive recommends using the
+ FINERFREQ for all normal tuning (the doc indicates reg 0x03 should
+ only be used for fast scanning for channel lock) */
+ return xc_write_reg(priv, XREG_FINERFREQ, freq_code);
+}
+
+
+static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz)
+{
+ u32 freq_code = (freq_khz * 1024)/1000;
+ dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n",
+ __func__, freq_khz, freq_code);
+
+ return xc_write_reg(priv, XREG_IF_OUT, freq_code);
+}
+
+
+static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope)
+{
+ return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope);
+}
+
+static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz)
+{
+ int result;
+ u16 regData;
+ u32 tmp;
+
+ result = xc5000_readreg(priv, XREG_FREQ_ERROR, &regData);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ tmp = (u32)regData;
+ (*freq_error_hz) = (tmp * 15625) / 1000;
+ return result;
+}
+
+static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status)
+{
+ return xc5000_readreg(priv, XREG_LOCK, lock_status);
+}
+
+static int xc_get_version(struct xc5000_priv *priv,
+ u8 *hw_majorversion, u8 *hw_minorversion,
+ u8 *fw_majorversion, u8 *fw_minorversion)
+{
+ u16 data;
+ int result;
+
+ result = xc5000_readreg(priv, XREG_VERSION, &data);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ (*hw_majorversion) = (data >> 12) & 0x0F;
+ (*hw_minorversion) = (data >> 8) & 0x0F;
+ (*fw_majorversion) = (data >> 4) & 0x0F;
+ (*fw_minorversion) = data & 0x0F;
+
+ return 0;
+}
+
+static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev)
+{
+ return xc5000_readreg(priv, XREG_BUILD, buildrev);
+}
+
+static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz)
+{
+ u16 regData;
+ int result;
+
+ result = xc5000_readreg(priv, XREG_HSYNC_FREQ, &regData);
+ if (result != XC_RESULT_SUCCESS)
+ return result;
+
+ (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100;
+ return result;
+}
+
+static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines)
+{
+ return xc5000_readreg(priv, XREG_FRAME_LINES, frame_lines);
+}
+
+static int xc_get_quality(struct xc5000_priv *priv, u16 *quality)
+{
+ return xc5000_readreg(priv, XREG_QUALITY, quality);
+}
+
+static int xc_get_analogsnr(struct xc5000_priv *priv, u16 *snr)
+{
+ return xc5000_readreg(priv, XREG_SNR, snr);
+}
+
+static int xc_get_totalgain(struct xc5000_priv *priv, u16 *totalgain)
+{
+ return xc5000_readreg(priv, XREG_TOTALGAIN, totalgain);
+}
+
+static u16 WaitForLock(struct xc5000_priv *priv)
+{
+ u16 lockState = 0;
+ int watchDogCount = 40;
+
+ while ((lockState == 0) && (watchDogCount > 0)) {
+ xc_get_lock_status(priv, &lockState);
+ if (lockState != 1) {
+ xc_wait(5);
+ watchDogCount--;
+ }
+ }
+ return lockState;
+}
+
+#define XC_TUNE_ANALOG 0
+#define XC_TUNE_DIGITAL 1
+static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode)
+{
+ int found = 0;
+
+ dprintk(1, "%s(%u)\n", __func__, freq_hz);
+
+ if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS)
+ return 0;
+
+ if (mode == XC_TUNE_ANALOG) {
+ if (WaitForLock(priv) == 1)
+ found = 1;
+ }
+
+ return found;
+}
+
+static int xc_set_xtal(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = XC_RESULT_SUCCESS;
+
+ switch (priv->chip_id) {
+ default:
+ case XC5000A:
+ /* 32.000 MHz xtal is default */
+ break;
+ case XC5000C:
+ switch (priv->xtal_khz) {
+ default:
+ case 32000:
+ /* 32.000 MHz xtal is default */
+ break;
+ case 31875:
+ /* 31.875 MHz xtal configuration */
+ ret = xc_write_reg(priv, 0x000f, 0x8081);
+ break;
+ }
+ break;
+ }
+ return ret;
+}
+
+static int xc5000_fwupload(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ const struct firmware *fw;
+ int ret;
+ const struct xc5000_fw_cfg *desired_fw =
+ xc5000_assign_firmware(priv->chip_id);
+ priv->pll_register_no = desired_fw->pll_reg;
+ priv->init_status_supported = desired_fw->init_status_supported;
+ priv->fw_checksum_supported = desired_fw->fw_checksum_supported;
+
+ /* request the firmware, this will block and timeout */
+ printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
+ desired_fw->name);
+
+ ret = request_firmware(&fw, desired_fw->name,
+ priv->i2c_props.adap->dev.parent);
+ if (ret) {
+ printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ goto out;
+ } else {
+ printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n",
+ fw->size);
+ ret = XC_RESULT_SUCCESS;
+ }
+
+ if (fw->size != desired_fw->size) {
+ printk(KERN_ERR "xc5000: firmware incorrect size\n");
+ ret = XC_RESULT_RESET_FAILURE;
+ } else {
+ printk(KERN_INFO "xc5000: firmware uploading...\n");
+ ret = xc_load_i2c_sequence(fe, fw->data);
+ if (XC_RESULT_SUCCESS == ret)
+ ret = xc_set_xtal(fe);
+ if (XC_RESULT_SUCCESS == ret)
+ printk(KERN_INFO "xc5000: firmware upload complete...\n");
+ else
+ printk(KERN_ERR "xc5000: firmware upload failed...\n");
+ }
+
+out:
+ release_firmware(fw);
+ return ret;
+}
+
+static void xc_debug_dump(struct xc5000_priv *priv)
+{
+ u16 adc_envelope;
+ u32 freq_error_hz = 0;
+ u16 lock_status;
+ u32 hsync_freq_hz = 0;
+ u16 frame_lines;
+ u16 quality;
+ u16 snr;
+ u16 totalgain;
+ u8 hw_majorversion = 0, hw_minorversion = 0;
+ u8 fw_majorversion = 0, fw_minorversion = 0;
+ u16 fw_buildversion = 0;
+ u16 regval;
+
+ /* Wait for stats to stabilize.
+ * Frame Lines needs two frame times after initial lock
+ * before it is valid.
+ */
+ xc_wait(100);
+
+ xc_get_ADC_Envelope(priv, &adc_envelope);
+ dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope);
+
+ xc_get_frequency_error(priv, &freq_error_hz);
+ dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz);
+
+ xc_get_lock_status(priv, &lock_status);
+ dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n",
+ lock_status);
+
+ xc_get_version(priv, &hw_majorversion, &hw_minorversion,
+ &fw_majorversion, &fw_minorversion);
+ xc_get_buildversion(priv, &fw_buildversion);
+ dprintk(1, "*** HW: V%d.%d, FW: V %d.%d.%d\n",
+ hw_majorversion, hw_minorversion,
+ fw_majorversion, fw_minorversion, fw_buildversion);
+
+ xc_get_hsync_freq(priv, &hsync_freq_hz);
+ dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz);
+
+ xc_get_frame_lines(priv, &frame_lines);
+ dprintk(1, "*** Frame lines = %d\n", frame_lines);
+
+ xc_get_quality(priv, &quality);
+ dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality & 0x07);
+
+ xc_get_analogsnr(priv, &snr);
+ dprintk(1, "*** Unweighted analog SNR = %d dB\n", snr & 0x3f);
+
+ xc_get_totalgain(priv, &totalgain);
+ dprintk(1, "*** Total gain = %d.%d dB\n", totalgain / 256,
+ (totalgain % 256) * 100 / 256);
+
+ if (priv->pll_register_no) {
+ xc5000_readreg(priv, priv->pll_register_no, &regval);
+ dprintk(1, "*** PLL lock status = 0x%04x\n", regval);
+ }
+}
+
+static int xc5000_set_params(struct dvb_frontend *fe)
+{
+ int ret, b;
+ struct xc5000_priv *priv = fe->tuner_priv;
+ u32 bw = fe->dtv_property_cache.bandwidth_hz;
+ u32 freq = fe->dtv_property_cache.frequency;
+ u32 delsys = fe->dtv_property_cache.delivery_system;
+
+ if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+ dprintk(1, "Unable to load firmware and init tuner\n");
+ return -EINVAL;
+ }
+
+ dprintk(1, "%s() frequency=%d (Hz)\n", __func__, freq);
+
+ switch (delsys) {
+ case SYS_ATSC:
+ dprintk(1, "%s() VSB modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = freq - 1750000;
+ priv->video_standard = DTV6;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = freq - 1750000;
+ priv->video_standard = DTV6;
+ break;
+ case SYS_ISDBT:
+ /* All ISDB-T are currently for 6 MHz bw */
+ if (!bw)
+ bw = 6000000;
+ /* fall to OFDM handling */
+ case SYS_DMBTH:
+ case SYS_DVBT:
+ case SYS_DVBT2:
+ dprintk(1, "%s() OFDM\n", __func__);
+ switch (bw) {
+ case 6000000:
+ priv->video_standard = DTV6;
+ priv->freq_hz = freq - 1750000;
+ break;
+ case 7000000:
+ priv->video_standard = DTV7;
+ priv->freq_hz = freq - 2250000;
+ break;
+ case 8000000:
+ priv->video_standard = DTV8;
+ priv->freq_hz = freq - 2750000;
+ break;
+ default:
+ printk(KERN_ERR "xc5000 bandwidth not set!\n");
+ return -EINVAL;
+ }
+ priv->rf_mode = XC_RF_MODE_AIR;
+ case SYS_DVBC_ANNEX_A:
+ case SYS_DVBC_ANNEX_C:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ if (bw <= 6000000) {
+ priv->video_standard = DTV6;
+ priv->freq_hz = freq - 1750000;
+ b = 6;
+ } else if (bw <= 7000000) {
+ priv->video_standard = DTV7;
+ priv->freq_hz = freq - 2250000;
+ b = 7;
+ } else {
+ priv->video_standard = DTV7_8;
+ priv->freq_hz = freq - 2750000;
+ b = 8;
+ }
+ dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__,
+ b, bw);
+ break;
+ default:
+ printk(KERN_ERR "xc5000: delivery system is not supported!\n");
+ return -EINVAL;
+ }
+
+ dprintk(1, "%s() frequency=%d (compensated to %d)\n",
+ __func__, freq, priv->freq_hz);
+
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ ret = xc_set_IF_frequency(priv, priv->if_khz);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n",
+ priv->if_khz);
+ return -EIO;
+ }
+
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a);
+
+ xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ priv->bandwidth = bw;
+
+ return 0;
+}
+
+static int xc5000_is_firmware_loaded(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret;
+ u16 id;
+
+ ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id);
+ if (ret == XC_RESULT_SUCCESS) {
+ if (id == XC_PRODUCT_ID_FW_NOT_LOADED)
+ ret = XC_RESULT_RESET_FAILURE;
+ else
+ ret = XC_RESULT_SUCCESS;
+ }
+
+ dprintk(1, "%s() returns %s id = 0x%x\n", __func__,
+ ret == XC_RESULT_SUCCESS ? "True" : "False", id);
+ return ret;
+}
+
+static int xc5000_set_tv_freq(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ u16 pll_lock_status;
+ int ret;
+
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __func__, params->frequency);
+
+ /* Fix me: it could be air. */
+ priv->rf_mode = params->mode;
+ if (params->mode > XC_RF_MODE_CABLE)
+ priv->rf_mode = XC_RF_MODE_CABLE;
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+
+ /* FIX ME: Some video standards may have several possible audio
+ standards. We simply default to one of them here.
+ */
+ if (params->std & V4L2_STD_MN) {
+ /* default to BTSC audio standard */
+ priv->video_standard = MN_NTSC_PAL_BTSC;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_BG) {
+ /* default to NICAM audio standard */
+ priv->video_standard = BG_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_I) {
+ /* default to NICAM audio standard */
+ priv->video_standard = I_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_PAL_DK) {
+ /* default to NICAM audio standard */
+ priv->video_standard = DK_PAL_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_DK) {
+ /* default to A2 DK1 audio standard */
+ priv->video_standard = DK_SECAM_A2DK1;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_L) {
+ priv->video_standard = L_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+ if (params->std & V4L2_STD_SECAM_LC) {
+ priv->video_standard = LC_SECAM_NICAM;
+ goto tune_channel;
+ }
+
+tune_channel:
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetTVStandard(priv,
+ XC5000_Standard[priv->video_standard].VideoMode,
+ XC5000_Standard[priv->video_standard].AudioMode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09);
+
+ xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG);
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ if (priv->pll_register_no != 0) {
+ msleep(20);
+ xc5000_readreg(priv, priv->pll_register_no, &pll_lock_status);
+ if (pll_lock_status > 63) {
+ /* PLL is unlocked, force reload of the firmware */
+ dprintk(1, "xc5000: PLL not locked (0x%x). Reloading...\n",
+ pll_lock_status);
+ if (xc_load_fw_and_init_tuner(fe, 1) != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: Unable to reload fw\n");
+ return -EREMOTEIO;
+ }
+ goto tune_channel;
+ }
+ }
+
+ return 0;
+}
+
+static int xc5000_set_radio_freq(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+ u8 radio_input;
+
+ dprintk(1, "%s() frequency=%d (in units of khz)\n",
+ __func__, params->frequency);
+
+ if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) {
+ dprintk(1, "%s() radio input not configured\n", __func__);
+ return -EINVAL;
+ }
+
+ if (priv->radio_input == XC5000_RADIO_FM1)
+ 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);
+ return -EINVAL;
+ }
+
+ priv->freq_hz = params->frequency * 125 / 2;
+
+ priv->rf_mode = XC_RF_MODE_AIR;
+
+ ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode,
+ XC5000_Standard[radio_input].AudioMode);
+
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n");
+ return -EREMOTEIO;
+ }
+
+ ret = xc_SetSignalSource(priv, priv->rf_mode);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: xc_SetSignalSource(%d) failed\n",
+ priv->rf_mode);
+ return -EREMOTEIO;
+ }
+
+ 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;
+}
+
+static int xc5000_set_analog_params(struct dvb_frontend *fe,
+ struct analog_parameters *params)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = -EINVAL;
+
+ if (priv->i2c_props.adap == NULL)
+ return -EINVAL;
+
+ if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+ dprintk(1, "Unable to load firmware and init tuner\n");
+ return -EINVAL;
+ }
+
+ switch (params->mode) {
+ case V4L2_TUNER_RADIO:
+ ret = xc5000_set_radio_freq(fe, params);
+ break;
+ case V4L2_TUNER_ANALOG_TV:
+ case V4L2_TUNER_DIGITAL_TV:
+ ret = xc5000_set_tv_freq(fe, params);
+ break;
+ }
+
+ return ret;
+}
+
+
+static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+ *freq = priv->freq_hz;
+ return 0;
+}
+
+static int xc5000_get_if_frequency(struct dvb_frontend *fe, u32 *freq)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+ *freq = priv->if_khz * 1000;
+ return 0;
+}
+
+static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ *bw = priv->bandwidth;
+ return 0;
+}
+
+static int xc5000_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ u16 lock_status = 0;
+
+ xc_get_lock_status(priv, &lock_status);
+
+ dprintk(1, "%s() lock_status = 0x%08x\n", __func__, lock_status);
+
+ *status = lock_status;
+
+ return 0;
+}
+
+static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe, int force)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ int ret = XC_RESULT_SUCCESS;
+ u16 pll_lock_status;
+ u16 fw_ck;
+
+ if (force || xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) {
+
+fw_retry:
+
+ ret = xc5000_fwupload(fe);
+ if (ret != XC_RESULT_SUCCESS)
+ return ret;
+
+ msleep(20);
+
+ if (priv->fw_checksum_supported) {
+ if (xc5000_readreg(priv, XREG_FW_CHECKSUM, &fw_ck)
+ != XC_RESULT_SUCCESS) {
+ dprintk(1, "%s() FW checksum reading failed.\n",
+ __func__);
+ goto fw_retry;
+ }
+
+ if (fw_ck == 0) {
+ dprintk(1, "%s() FW checksum failed = 0x%04x\n",
+ __func__, fw_ck);
+ goto fw_retry;
+ }
+ }
+
+ /* Start the tuner self-calibration process */
+ ret |= xc_initialize(priv);
+
+ if (ret != XC_RESULT_SUCCESS)
+ goto fw_retry;
+
+ /* Wait for calibration to complete.
+ * We could continue but XC5000 will clock stretch subsequent
+ * I2C transactions until calibration is complete. This way we
+ * don't have to rely on clock stretching working.
+ */
+ xc_wait(100);
+
+ if (priv->init_status_supported) {
+ if (xc5000_readreg(priv, XREG_INIT_STATUS, &fw_ck) != XC_RESULT_SUCCESS) {
+ dprintk(1, "%s() FW failed reading init status.\n",
+ __func__);
+ goto fw_retry;
+ }
+
+ if (fw_ck == 0) {
+ dprintk(1, "%s() FW init status failed = 0x%04x\n", __func__, fw_ck);
+ goto fw_retry;
+ }
+ }
+
+ if (priv->pll_register_no) {
+ xc5000_readreg(priv, priv->pll_register_no,
+ &pll_lock_status);
+ if (pll_lock_status > 63) {
+ /* PLL is unlocked, force reload of the firmware */
+ printk(KERN_ERR "xc5000: PLL not running after fwload.\n");
+ goto fw_retry;
+ }
+ }
+
+ /* Default to "CABLE" mode */
+ ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE);
+ }
+
+ return ret;
+}
+
+static int xc5000_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Avoid firmware reload on slow devices */
+ if (no_poweroff)
+ return 0;
+
+ /* According to Xceive technical support, the "powerdown" register
+ was removed in newer versions of the firmware. The "supported"
+ way to sleep the tuner is to pull the reset pin low for 10ms */
+ ret = xc5000_TunerReset(fe);
+ if (ret != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR
+ "xc5000: %s() unable to shutdown tuner\n",
+ __func__);
+ return -EREMOTEIO;
+ } else
+ return XC_RESULT_SUCCESS;
+}
+
+static int xc5000_init(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+ dprintk(1, "%s()\n", __func__);
+
+ if (xc_load_fw_and_init_tuner(fe, 0) != XC_RESULT_SUCCESS) {
+ printk(KERN_ERR "xc5000: Unable to initialise tuner\n");
+ return -EREMOTEIO;
+ }
+
+ if (debug)
+ xc_debug_dump(priv);
+
+ return 0;
+}
+
+static int xc5000_release(struct dvb_frontend *fe)
+{
+ struct xc5000_priv *priv = fe->tuner_priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ mutex_lock(&xc5000_list_mutex);
+
+ if (priv)
+ hybrid_tuner_release_state(priv);
+
+ mutex_unlock(&xc5000_list_mutex);
+
+ fe->tuner_priv = NULL;
+
+ 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",
+ .frequency_min = 1000000,
+ .frequency_max = 1023000000,
+ .frequency_step = 50000,
+ },
+
+ .release = xc5000_release,
+ .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,
+ .get_if_frequency = xc5000_get_if_frequency,
+ .get_bandwidth = xc5000_get_bandwidth,
+ .get_status = xc5000_get_status
+};
+
+struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct xc5000_config *cfg)
+{
+ struct xc5000_priv *priv = NULL;
+ int instance;
+ u16 id = 0;
+
+ dprintk(1, "%s(%d-%04x)\n", __func__,
+ i2c ? i2c_adapter_id(i2c) : -1,
+ cfg ? cfg->i2c_address : -1);
+
+ mutex_lock(&xc5000_list_mutex);
+
+ instance = hybrid_tuner_request_state(struct xc5000_priv, priv,
+ hybrid_tuner_instance_list,
+ i2c, cfg->i2c_address, "xc5000");
+ switch (instance) {
+ case 0:
+ goto fail;
+ break;
+ case 1:
+ /* new tuner instance */
+ priv->bandwidth = 6000000;
+ fe->tuner_priv = priv;
+ break;
+ default:
+ /* existing tuner instance */
+ fe->tuner_priv = priv;
+ break;
+ }
+
+ if (priv->if_khz == 0) {
+ /* If the IF hasn't been set yet, use the value provided by
+ the caller (occurs in hybrid devices where the analog
+ call to xc5000_attach occurs before the digital side) */
+ priv->if_khz = cfg->if_khz;
+ }
+
+ if (priv->xtal_khz == 0)
+ priv->xtal_khz = cfg->xtal_khz;
+
+ if (priv->radio_input == 0)
+ priv->radio_input = cfg->radio_input;
+
+ /* don't override chip id if it's already been set
+ unless explicitly specified */
+ if ((priv->chip_id == 0) || (cfg->chip_id))
+ /* use default chip id if none specified, set to 0 so
+ it can be overridden if this is a hybrid driver */
+ priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
+
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+ if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS)
+ goto fail;
+
+ switch (id) {
+ case XC_PRODUCT_ID_FW_LOADED:
+ printk(KERN_INFO
+ "xc5000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc5000: Firmware has been loaded previously\n");
+ break;
+ case XC_PRODUCT_ID_FW_NOT_LOADED:
+ printk(KERN_INFO
+ "xc5000: Successfully identified at address 0x%02x\n",
+ cfg->i2c_address);
+ printk(KERN_INFO
+ "xc5000: Firmware has not been loaded previously\n");
+ break;
+ default:
+ printk(KERN_ERR
+ "xc5000: Device not found at addr 0x%02x (0x%x)\n",
+ cfg->i2c_address, id);
+ goto fail;
+ }
+
+ mutex_unlock(&xc5000_list_mutex);
+
+ memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ return fe;
+fail:
+ mutex_unlock(&xc5000_list_mutex);
+
+ xc5000_release(fe);
+ return NULL;
+}
+EXPORT_SYMBOL(xc5000_attach);
+
+MODULE_AUTHOR("Steven Toth");
+MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(XC5000A_FIRMWARE);
+MODULE_FIRMWARE(XC5000C_FIRMWARE);
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/tuners/xc5000.h
index b1a547494625..b1a547494625 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/tuners/xc5000.h
diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig
new file mode 100644
index 000000000000..6746994d03fe
--- /dev/null
+++ b/drivers/media/usb/Kconfig
@@ -0,0 +1,54 @@
+menuconfig MEDIA_USB_SUPPORT
+ bool "Media USB Adapters"
+ depends on USB && MEDIA_SUPPORT
+ help
+ Enable media drivers for USB bus.
+ If you have such devices, say Y.
+
+if MEDIA_USB_SUPPORT
+
+if MEDIA_CAMERA_SUPPORT
+ comment "Webcam devices"
+source "drivers/media/usb/uvc/Kconfig"
+source "drivers/media/usb/gspca/Kconfig"
+source "drivers/media/usb/pwc/Kconfig"
+source "drivers/media/usb/cpia2/Kconfig"
+source "drivers/media/usb/zr364xx/Kconfig"
+source "drivers/media/usb/stkwebcam/Kconfig"
+source "drivers/media/usb/s2255/Kconfig"
+source "drivers/media/usb/sn9c102/Kconfig"
+endif
+
+if MEDIA_ANALOG_TV_SUPPORT
+ comment "Analog TV USB devices"
+source "drivers/media/usb/au0828/Kconfig"
+source "drivers/media/usb/pvrusb2/Kconfig"
+source "drivers/media/usb/hdpvr/Kconfig"
+source "drivers/media/usb/tlg2300/Kconfig"
+source "drivers/media/usb/usbvision/Kconfig"
+source "drivers/media/usb/stk1160/Kconfig"
+endif
+
+if (MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
+ comment "Analog/digital TV USB devices"
+source "drivers/media/usb/cx231xx/Kconfig"
+source "drivers/media/usb/tm6000/Kconfig"
+endif
+
+
+if I2C && MEDIA_DIGITAL_TV_SUPPORT
+ comment "Digital TV USB devices"
+source "drivers/media/usb/dvb-usb/Kconfig"
+source "drivers/media/usb/dvb-usb-v2/Kconfig"
+source "drivers/media/usb/ttusb-budget/Kconfig"
+source "drivers/media/usb/ttusb-dec/Kconfig"
+source "drivers/media/usb/siano/Kconfig"
+source "drivers/media/usb/b2c2/Kconfig"
+endif
+
+if (MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT)
+ comment "Webcam, TV (analog/digital) USB devices"
+source "drivers/media/usb/em28xx/Kconfig"
+endif
+
+endif #MEDIA_USB_SUPPORT
diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile
new file mode 100644
index 000000000000..7f51d7e5f739
--- /dev/null
+++ b/drivers/media/usb/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for the USB media device drivers
+#
+
+# DVB USB-only drivers
+obj-y += ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/
+obj-y += zr364xx/ stkwebcam/ s2255/
+
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
+obj-$(CONFIG_USB_GSPCA) += gspca/
+obj-$(CONFIG_USB_PWC) += pwc/
+obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
+obj-$(CONFIG_USB_SN9C102) += sn9c102/
+obj-$(CONFIG_VIDEO_AU0828) += au0828/
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
+obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
+obj-$(CONFIG_VIDEO_STK1160) += stk1160/
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+obj-$(CONFIG_VIDEO_TM6000) += tm6000/
+obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig
new file mode 100644
index 000000000000..1766c0ce93be
--- /dev/null
+++ b/drivers/media/usb/au0828/Kconfig
@@ -0,0 +1,17 @@
+
+config VIDEO_AU0828
+ tristate "Auvitek AU0828 support"
+ depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
+ select I2C_ALGOBIT
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_VMALLOC
+ select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ This is a video4linux driver for Auvitek's USB device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called au0828
diff --git a/drivers/media/usb/au0828/Makefile b/drivers/media/usb/au0828/Makefile
new file mode 100644
index 000000000000..98cc20cc0ffb
--- /dev/null
+++ b/drivers/media/usb/au0828/Makefile
@@ -0,0 +1,9 @@
+au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o
+
+obj-$(CONFIG_VIDEO_AU0828) += au0828.o
+
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c
new file mode 100644
index 000000000000..448361c6a13e
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-cards.c
@@ -0,0 +1,339 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "au0828.h"
+#include "au0828-cards.h"
+#include "au8522.h"
+#include "media/tuner.h"
+#include "media/v4l2-common.h"
+
+void hvr950q_cs5340_audio(void *priv, int enable)
+{
+ /* Because the HVR-950q shares an i2s bus between the cs5340 and the
+ au8522, we need to hold cs5340 in reset when using the au8522 */
+ struct au0828_dev *dev = priv;
+ if (enable == 1)
+ au0828_set(dev, REG_000, 0x10);
+ else
+ au0828_clear(dev, REG_000, 0x10);
+}
+
+struct au0828_board au0828_boards[] = {
+ [AU0828_BOARD_UNKNOWN] = {
+ .name = "Unknown board",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR850] = {
+ .name = "Hauppauge HVR850",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ .i2c_clk_divider = AU0828_I2C_CLK_20KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_TELEVISION,
+ .vmux = AU8522_COMPOSITE_CH4_SIF,
+ .amux = AU8522_AUDIO_SIF,
+ },
+ {
+ .type = AU0828_VMUX_COMPOSITE,
+ .vmux = AU8522_COMPOSITE_CH1,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
+ .name = "Hauppauge HVR950Q",
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x61,
+ /* The au0828 hardware i2c implementation does not properly
+ support the xc5000's i2c clock stretching. So we need to
+ lower the clock frequency enough where the 15us clock
+ stretch fits inside of a normal clock cycle, or else the
+ au0828 fails to set the STOP bit. A 30 KHz clock puts the
+ clock pulse width at 18us */
+ .i2c_clk_divider = AU0828_I2C_CLK_20KHZ,
+ .input = {
+ {
+ .type = AU0828_VMUX_TELEVISION,
+ .vmux = AU8522_COMPOSITE_CH4_SIF,
+ .amux = AU8522_AUDIO_SIF,
+ },
+ {
+ .type = AU0828_VMUX_COMPOSITE,
+ .vmux = AU8522_COMPOSITE_CH1,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ {
+ .type = AU0828_VMUX_SVIDEO,
+ .vmux = AU8522_SVIDEO_CH13,
+ .amux = AU8522_AUDIO_NONE,
+ .audio_setup = hvr950q_cs5340_audio,
+ },
+ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
+ .name = "Hauppauge HVR950Q rev xxF8",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+ [AU0828_BOARD_DVICO_FUSIONHDTV7] = {
+ .name = "DViCO FusionHDTV USB",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+ [AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
+ .name = "Hauppauge Woodbury",
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
+ },
+};
+
+/* Tuner callback function for au0828 boards. Currently only needed
+ * for HVR1500Q, which has an xc5000 tuner.
+ */
+int au0828_tuner_callback(void *priv, int component, int command, int arg)
+{
+ struct au0828_dev *dev = priv;
+
+ dprintk(1, "%s()\n", __func__);
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ if (command == 0) {
+ /* Tuner Reset Command from xc5000 */
+ /* Drive the tuner into reset and out */
+ au0828_clear(dev, REG_001, 2);
+ mdelay(10);
+ au0828_set(dev, REG_001, 2);
+ mdelay(10);
+ return 0;
+ } else {
+ printk(KERN_ERR
+ "%s(): Unknown command.\n", __func__);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ return 0; /* Should never be here */
+}
+
+static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
+{
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
+ dev->board.tuner_type = tv.tuner_type;
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
+ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
+ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
+ case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
+ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
+ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n", __func__, tv.model);
+ break;
+ }
+
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
+ __func__, tv.model);
+}
+
+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;
+
+ dprintk(1, "%s()\n", __func__);
+
+ memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board));
+
+ if (dev->i2c_rc == 0) {
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
+ }
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ if (dev->i2c_rc == 0)
+ hauppauge_eeprom(dev, eeprom+0xa0);
+ break;
+ }
+
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
+ /* Load the analog demodulator driver (note this would need to
+ be abstracted out if we ever need to support a different
+ demod) */
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "au8522", 0x8e >> 1, NULL);
+ if (sd == NULL)
+ printk(KERN_ERR "analog subdev registration failed\n");
+ }
+
+ /* Setup tuners */
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ /* Load the tuner module, which does the attach */
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->board.tuner_addr, NULL);
+ if (sd == NULL)
+ printk(KERN_ERR "tuner subdev registration fail\n");
+
+ tun_setup.mode_mask = mode_mask;
+ tun_setup.type = dev->board.tuner_type;
+ tun_setup.addr = dev->board.tuner_addr;
+ tun_setup.tuner_callback = au0828_tuner_callback;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
+ &tun_setup);
+ }
+}
+
+/*
+ * The bridge has between 8 and 12 gpios.
+ * Regs 1 and 0 deal with output enables.
+ * Regs 3 and 2 deal with direction.
+ */
+void au0828_gpio_setup(struct au0828_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ /* GPIO's
+ * 4 - CS5340
+ * 5 - AU8522 Demodulator
+ * 6 - eeprom W/P
+ * 7 - power supply
+ * 9 - XC5000 Tuner
+ */
+
+ /* Into reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+ au0828_write(dev, REG_001, 0x0);
+ au0828_write(dev, REG_000, 0x0);
+ msleep(100);
+
+ /* Out of reset (leave the cs5340 in reset until needed) */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_001, 0x02);
+ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+ au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
+
+ msleep(250);
+ break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ /* GPIO's
+ * 6 - ?
+ * 8 - AU8522 Demodulator
+ * 9 - XC5000 Tuner
+ */
+
+ /* Into reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0xa0);
+ au0828_write(dev, REG_001, 0x0);
+ au0828_write(dev, REG_000, 0x0);
+ msleep(100);
+
+ /* Out of reset */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_002, 0xa0);
+ au0828_write(dev, REG_001, 0x02);
+ au0828_write(dev, REG_000, 0xa0);
+ msleep(250);
+ break;
+ }
+}
+
+/* table of devices that work with this driver */
+struct usb_device_id au0828_usb_id_table[] = {
+ { USB_DEVICE(0x2040, 0x7200),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7240),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 },
+ { USB_DEVICE(0x0fe9, 0xd620),
+ .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 },
+ { USB_DEVICE(0x2040, 0x7210),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7217),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721b),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721e),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x721f),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7280),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x0fd9, 0x0008),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7201),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x2040, 0x7211),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x2040, 0x7281),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x05e1, 0x0480),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
+ { USB_DEVICE(0x2040, 0x8200),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
+ { USB_DEVICE(0x2040, 0x7260),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { USB_DEVICE(0x2040, 0x7213),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, au0828_usb_id_table);
diff --git a/drivers/media/video/au0828/au0828-cards.h b/drivers/media/usb/au0828/au0828-cards.h
index 48a1882c2b6b..48a1882c2b6b 100644
--- a/drivers/media/video/au0828/au0828-cards.h
+++ b/drivers/media/usb/au0828/au0828-cards.h
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
new file mode 100644
index 000000000000..745a80a798c8
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -0,0 +1,282 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/mutex.h>
+
+#include "au0828.h"
+
+/*
+ * 1 = General debug messages
+ * 2 = USB handling
+ * 4 = I2C related
+ * 8 = Bridge related
+ */
+int au0828_debug;
+module_param_named(debug, au0828_debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static unsigned int disable_usb_speed_check;
+module_param(disable_usb_speed_check, int, 0444);
+MODULE_PARM_DESC(disable_usb_speed_check,
+ "override min bandwidth requirement of 480M bps");
+
+#define _AU0828_BULKPIPE 0x03
+#define _BULKPIPESIZE 0xffff
+
+static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index);
+static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size);
+
+/* USB Direction */
+#define CMD_REQUEST_IN 0x00
+#define CMD_REQUEST_OUT 0x01
+
+u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
+{
+ u8 result = 0;
+
+ recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1);
+ dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result);
+
+ return result;
+}
+
+u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
+{
+ dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+ return send_control_msg(dev, CMD_REQUEST_OUT, val, reg);
+}
+
+static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index)
+{
+ int status = -ENODEV;
+
+ if (dev->usbdev) {
+
+ /* cp must be memory that has been allocated by kmalloc */
+ status = usb_control_msg(dev->usbdev,
+ usb_sndctrlpipe(dev->usbdev, 0),
+ request,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ value, index, NULL, 0, 1000);
+
+ status = min(status, 0);
+
+ if (status < 0) {
+ printk(KERN_ERR "%s() Failed sending control message, error %d.\n",
+ __func__, status);
+ }
+
+ }
+
+ return status;
+}
+
+static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u16 index, unsigned char *cp, u16 size)
+{
+ int status = -ENODEV;
+ mutex_lock(&dev->mutex);
+ if (dev->usbdev) {
+ status = usb_control_msg(dev->usbdev,
+ usb_rcvctrlpipe(dev->usbdev, 0),
+ request,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ dev->ctrlmsg, size, 1000);
+
+ status = min(status, 0);
+
+ if (status < 0) {
+ printk(KERN_ERR "%s() Failed receiving control message, error %d.\n",
+ __func__, status);
+ }
+
+ /* the host controller requires heap allocated memory, which
+ is why we didn't just pass "cp" into usb_control_msg */
+ memcpy(cp, dev->ctrlmsg, size);
+ }
+ mutex_unlock(&dev->mutex);
+ return status;
+}
+
+static void au0828_usb_disconnect(struct usb_interface *interface)
+{
+ struct au0828_dev *dev = usb_get_intfdata(interface);
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Digital TV */
+ au0828_dvb_unregister(dev);
+
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
+ au0828_analog_unregister(dev);
+
+ /* I2C */
+ au0828_i2c_unregister(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ usb_set_intfdata(interface, NULL);
+
+ mutex_lock(&dev->mutex);
+ dev->usbdev = NULL;
+ mutex_unlock(&dev->mutex);
+
+ kfree(dev);
+
+}
+
+static int au0828_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int ifnum, retval;
+ struct au0828_dev *dev;
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+ ifnum = interface->altsetting->desc.bInterfaceNumber;
+
+ if (ifnum != 0)
+ return -ENODEV;
+
+ dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
+ le16_to_cpu(usbdev->descriptor.idVendor),
+ le16_to_cpu(usbdev->descriptor.idProduct),
+ ifnum);
+
+ /*
+ * Make sure we have 480 Mbps of bandwidth, otherwise things like
+ * video stream wouldn't likely work, since 12 Mbps is generally
+ * not enough even for most Digital TV streams.
+ */
+ if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+ printk(KERN_ERR "au0828: Device initialization failed.\n");
+ printk(KERN_ERR "au0828: Device must be connected to a "
+ "high-speed USB 2.0 port.\n");
+ return -ENODEV;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ printk(KERN_ERR "%s() Unable to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ mutex_init(&dev->mutex);
+ mutex_init(&dev->dvb.lock);
+ dev->usbdev = usbdev;
+ dev->boardnr = id->driver_info;
+
+ /* Create the v4l2_device */
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval) {
+ printk(KERN_ERR "%s() v4l2_device_register failed\n",
+ __func__);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ return -EIO;
+ }
+
+ /* Power Up the bridge */
+ au0828_write(dev, REG_600, 1 << 4);
+
+ /* Bring up the GPIO's and supporting devices */
+ au0828_gpio_setup(dev);
+
+ /* I2C */
+ au0828_i2c_register(dev);
+
+ /* Setup */
+ au0828_card_setup(dev);
+
+ /* Analog TV */
+ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
+ au0828_analog_register(dev, interface);
+
+ /* Digital TV */
+ au0828_dvb_register(dev);
+
+ /* Store the pointer to the au0828_dev so it can be accessed in
+ au0828_usb_disconnect */
+ usb_set_intfdata(interface, dev);
+
+ printk(KERN_INFO "Registered device AU0828 [%s]\n",
+ dev->board.name == NULL ? "Unset" : dev->board.name);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static struct usb_driver au0828_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = au0828_usb_probe,
+ .disconnect = au0828_usb_disconnect,
+ .id_table = au0828_usb_id_table,
+};
+
+static int __init au0828_init(void)
+{
+ int ret;
+
+ if (au0828_debug & 1)
+ printk(KERN_INFO "%s() Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 2)
+ printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 4)
+ printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__);
+
+ if (au0828_debug & 8)
+ printk(KERN_INFO "%s() Bridge Debugging is enabled\n",
+ __func__);
+
+ printk(KERN_INFO "au0828 driver loaded\n");
+
+ ret = usb_register(&au0828_usb_driver);
+ if (ret)
+ printk(KERN_ERR "usb_register failed, error = %d\n", ret);
+
+ return ret;
+}
+
+static void __exit au0828_exit(void)
+{
+ usb_deregister(&au0828_usb_driver);
+}
+
+module_init(au0828_init);
+module_exit(au0828_exit);
+
+MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
+MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.2");
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
new file mode 100644
index 000000000000..b328f6550d0b
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -0,0 +1,500 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <media/v4l2-common.h>
+#include <media/tuner.h>
+
+#include "au0828.h"
+#include "au8522.h"
+#include "xc5000.h"
+#include "mxl5007t.h"
+#include "tda18271.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define _AU0828_BULKPIPE 0x83
+#define _BULKPIPESIZE 0xe522
+
+static u8 hauppauge_hvr950q_led_states[] = {
+ 0x00, /* off */
+ 0x02, /* yellow */
+ 0x04, /* green */
+};
+
+static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
+ .gpio_output = 0x00e0,
+ .gpio_output_enable = 0x6006,
+ .gpio_output_disable = 0x0660,
+
+ .gpio_leds = 0x00e2,
+ .led_states = hauppauge_hvr950q_led_states,
+ .num_led_states = sizeof(hauppauge_hvr950q_led_states),
+
+ .vsb8_strong = 20 /* dB */ * 10,
+ .qam64_strong = 25 /* dB */ * 10,
+ .qam256_strong = 32 /* dB */ * 10,
+};
+
+static struct au8522_config hauppauge_hvr950q_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_6MHZ,
+ .vsb_if = AU8522_IF_6MHZ,
+ .led_cfg = &hauppauge_hvr950q_led_cfg,
+};
+
+static struct au8522_config fusionhdtv7usb_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_6MHZ,
+ .vsb_if = AU8522_IF_6MHZ,
+};
+
+static struct au8522_config hauppauge_woodbury_config = {
+ .demod_address = 0x8e >> 1,
+ .status_mode = AU8522_DEMODLOCKING,
+ .qam_if = AU8522_IF_4MHZ,
+ .vsb_if = AU8522_IF_3_25MHZ,
+};
+
+static struct xc5000_config hauppauge_xc5000a_config = {
+ .i2c_address = 0x61,
+ .if_khz = 6000,
+ .chip_id = XC5000A,
+};
+
+static struct xc5000_config hauppauge_xc5000c_config = {
+ .i2c_address = 0x61,
+ .if_khz = 6000,
+ .chip_id = XC5000C,
+};
+
+static struct mxl5007t_config mxl5007t_hvr950q_config = {
+ .xtal_freq_hz = MxL_XTAL_24_MHZ,
+ .if_freq_hz = MxL_IF_6_MHZ,
+};
+
+static struct tda18271_config hauppauge_woodbury_tunerconfig = {
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static void au0828_restart_dvb_streaming(struct work_struct *work);
+
+/*-------------------------------------------------------------------*/
+static void urb_completion(struct urb *purb)
+{
+ struct au0828_dev *dev = purb->context;
+ int ptype = usb_pipetype(purb->pipe);
+ unsigned char *ptr;
+
+ dprintk(2, "%s()\n", __func__);
+
+ if (!dev)
+ return;
+
+ if (dev->urb_streaming == 0)
+ return;
+
+ if (ptype != PIPE_BULK) {
+ printk(KERN_ERR "%s() Unsupported URB type %d\n",
+ __func__, ptype);
+ return;
+ }
+
+ /* See if the stream is corrupted (to work around a hardware
+ bug where the stream gets misaligned */
+ ptr = purb->transfer_buffer;
+ if (purb->actual_length > 0 && ptr[0] != 0x47) {
+ dprintk(1, "Need to restart streaming %02x len=%d!\n",
+ ptr[0], purb->actual_length);
+ schedule_work(&dev->restart_streaming);
+ return;
+ }
+
+ /* Feed the transport payload into the kernel demux */
+ dvb_dmx_swfilter_packets(&dev->dvb.demux,
+ purb->transfer_buffer, purb->actual_length / 188);
+
+ /* Clean the buffer before we requeue */
+ memset(purb->transfer_buffer, 0, URB_BUFSIZE);
+
+ /* Requeue URB */
+ usb_submit_urb(purb, GFP_ATOMIC);
+}
+
+static int stop_urb_transfer(struct au0828_dev *dev)
+{
+ int i;
+
+ dprintk(2, "%s()\n", __func__);
+
+ dev->urb_streaming = 0;
+ for (i = 0; i < URB_COUNT; i++) {
+ usb_kill_urb(dev->urbs[i]);
+ kfree(dev->urbs[i]->transfer_buffer);
+ usb_free_urb(dev->urbs[i]);
+ }
+
+ return 0;
+}
+
+static int start_urb_transfer(struct au0828_dev *dev)
+{
+ struct urb *purb;
+ int i, ret = -ENOMEM;
+
+ dprintk(2, "%s()\n", __func__);
+
+ if (dev->urb_streaming) {
+ dprintk(2, "%s: bulk xfer already running!\n", __func__);
+ return 0;
+ }
+
+ for (i = 0; i < URB_COUNT; i++) {
+
+ dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urbs[i])
+ goto err;
+
+ purb = dev->urbs[i];
+
+ purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL);
+ if (!purb->transfer_buffer) {
+ usb_free_urb(purb);
+ dev->urbs[i] = NULL;
+ goto err;
+ }
+
+ purb->status = -EINPROGRESS;
+ usb_fill_bulk_urb(purb,
+ dev->usbdev,
+ usb_rcvbulkpipe(dev->usbdev,
+ _AU0828_BULKPIPE),
+ purb->transfer_buffer,
+ URB_BUFSIZE,
+ urb_completion,
+ dev);
+
+ }
+
+ for (i = 0; i < URB_COUNT; i++) {
+ ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
+ if (ret != 0) {
+ stop_urb_transfer(dev);
+ printk(KERN_ERR "%s: failed urb submission, "
+ "err = %d\n", __func__, ret);
+ return ret;
+ }
+ }
+
+ dev->urb_streaming = 1;
+ ret = 0;
+
+err:
+ return ret;
+}
+
+static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (dvb->feeding++ == 0) {
+ /* Start transport */
+ au0828_write(dev, 0x608, 0x90);
+ au0828_write(dev, 0x609, 0x72);
+ au0828_write(dev, 0x60a, 0x71);
+ au0828_write(dev, 0x60b, 0x01);
+ ret = start_urb_transfer(dev);
+ }
+ mutex_unlock(&dvb->lock);
+ }
+
+ return ret;
+}
+
+static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret = 0;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (dvb) {
+ mutex_lock(&dvb->lock);
+ if (--dvb->feeding == 0) {
+ /* Stop transport */
+ ret = stop_urb_transfer(dev);
+ au0828_write(dev, 0x60b, 0x00);
+ }
+ mutex_unlock(&dvb->lock);
+ }
+
+ return ret;
+}
+
+static void au0828_restart_dvb_streaming(struct work_struct *work)
+{
+ struct au0828_dev *dev = container_of(work, struct au0828_dev,
+ restart_streaming);
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret;
+
+ if (dev->urb_streaming == 0)
+ return;
+
+ dprintk(1, "Restarting streaming...!\n");
+
+ mutex_lock(&dvb->lock);
+
+ /* Stop transport */
+ ret = stop_urb_transfer(dev);
+ au0828_write(dev, 0x608, 0x00);
+ au0828_write(dev, 0x609, 0x00);
+ au0828_write(dev, 0x60a, 0x00);
+ au0828_write(dev, 0x60b, 0x00);
+
+ /* Start transport */
+ au0828_write(dev, 0x608, 0x90);
+ au0828_write(dev, 0x609, 0x72);
+ au0828_write(dev, 0x60a, 0x71);
+ au0828_write(dev, 0x60b, 0x01);
+ ret = start_urb_transfer(dev);
+
+ mutex_unlock(&dvb->lock);
+}
+
+static int dvb_register(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+ int result;
+
+ dprintk(1, "%s()\n", __func__);
+
+ INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
+ &dev->usbdev->dev, adapter_nr);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_adapter failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_adapter;
+ }
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_register_frontend failed "
+ "(errno = %d)\n", DRIVER_NAME, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dev;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = au0828_dvb_start_feed;
+ dvb->demux.stop_feed = au0828_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_ERR "%s: add_frontend failed "
+ "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
+ DRIVER_NAME, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+void au0828_dvb_unregister(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+
+ dprintk(1, "%s()\n", __func__);
+
+ if (dvb->frontend == NULL)
+ return;
+
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ dvb_unregister_frontend(dvb->frontend);
+ dvb_frontend_detach(dvb->frontend);
+ dvb_unregister_adapter(&dvb->adapter);
+}
+
+/* All the DVB attach calls go here, this function get's modified
+ * for each new card. No other function in this file needs
+ * to change.
+ */
+int au0828_dvb_register(struct au0828_dev *dev)
+{
+ struct au0828_dvb *dvb = &dev->dvb;
+ int ret;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* init frontend */
+ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_hvr950q_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ switch (dev->board.tuner_type) {
+ default:
+ case TUNER_XC5000:
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_xc5000a_config);
+ break;
+ case TUNER_XC5000C:
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_xc5000c_config);
+ break;
+ }
+ break;
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_hvr950q_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ dvb_attach(mxl5007t_attach, dvb->frontend,
+ &dev->i2c_adap, 0x60,
+ &mxl5007t_hvr950q_config);
+ break;
+ case AU0828_BOARD_HAUPPAUGE_WOODBURY:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &hauppauge_woodbury_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL)
+ dvb_attach(tda18271_attach, dvb->frontend,
+ 0x60, &dev->i2c_adap,
+ &hauppauge_woodbury_tunerconfig);
+ break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+ dvb->frontend = dvb_attach(au8522_attach,
+ &fusionhdtv7usb_config,
+ &dev->i2c_adap);
+ if (dvb->frontend != NULL) {
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_xc5000a_config);
+ }
+ break;
+ default:
+ printk(KERN_WARNING "The frontend of your DVB/ATSC card "
+ "isn't supported yet\n");
+ break;
+ }
+ if (NULL == dvb->frontend) {
+ printk(KERN_ERR "%s() Frontend initialization failed\n",
+ __func__);
+ return -1;
+ }
+ /* define general-purpose callback pointer */
+ dvb->frontend->callback = au0828_tuner_callback;
+
+ /* register everything */
+ ret = dvb_register(dev);
+ if (ret < 0) {
+ if (dvb->frontend->ops.release)
+ dvb->frontend->ops.release(dvb->frontend);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/usb/au0828/au0828-i2c.c b/drivers/media/usb/au0828/au0828-i2c.c
new file mode 100644
index 000000000000..4ded17fe1957
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-i2c.c
@@ -0,0 +1,401 @@
+/*
+ * Driver for the Auvitek AU0828 USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "au0828.h"
+#include "media/tuner.h"
+#include <media/v4l2-common.h>
+
+static int i2c_scan;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
+
+#define I2C_WAIT_DELAY 25
+#define I2C_WAIT_RETRY 1000
+
+static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
+}
+
+static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
+}
+
+static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_slave_did_read_ack(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
+}
+
+static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_read_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
+}
+
+static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (i2c_is_write_done(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ return au0828_read(dev, AU0828_I2C_STATUS_201) &
+ AU0828_I2C_STATUS_BUSY ? 1 : 0;
+}
+
+static int i2c_wait_done(struct i2c_adapter *i2c_adap)
+{
+ int count;
+
+ for (count = 0; count < I2C_WAIT_RETRY; count++) {
+ if (!i2c_is_busy(i2c_adap))
+ break;
+ udelay(I2C_WAIT_DELAY);
+ }
+
+ if (I2C_WAIT_RETRY == count)
+ return 0;
+
+ return 1;
+}
+
+/* FIXME: Implement join handling correctly */
+static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined_rlen)
+{
+ int i, strobe = 0;
+ struct au0828_dev *dev = i2c_adap->algo_data;
+
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
+
+ /* Set the I2C clock */
+ if (((dev->board.tuner_type == TUNER_XC5000) ||
+ (dev->board.tuner_type == TUNER_XC5000C)) &&
+ (dev->board.tuner_addr == msg->addr) &&
+ (msg->len == 64)) {
+ /* Hack to speed up firmware load. The xc5000 lets us do up
+ to 400 KHz when in firmware download mode */
+ au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
+ AU0828_I2C_CLK_250KHZ);
+ } else {
+ /* Use the i2c clock speed in the board configuration */
+ au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
+ dev->board.i2c_clk_divider);
+ }
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
+
+ dprintk(4, "SEND: %02x\n", msg->addr);
+
+ /* Deal with i2c_scan */
+ if (msg->len == 0) {
+ /* The analog tuner detection code makes use of the SMBUS_QUICK
+ message (which involves a zero length i2c write). To avoid
+ checking the status register when we didn't strobe out any
+ actual bytes to the bus, just do a read check. This is
+ consistent with how I saw i2c device checking done in the
+ USB trace of the Windows driver */
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ if (i2c_wait_read_ack(i2c_adap))
+ return -EIO;
+
+ return 0;
+ }
+
+ for (i = 0; i < msg->len;) {
+
+ dprintk(4, " %02x\n", msg->buf[i]);
+
+ au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
+
+ strobe++;
+ i++;
+
+ if ((strobe >= 4) || (i >= msg->len)) {
+
+ /* Strobe the byte into the bus */
+ if (i < msg->len)
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_WRITE |
+ AU0828_I2C_TRIGGER_HOLD);
+ else
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_WRITE);
+
+ /* Reset strobe trigger */
+ strobe = 0;
+
+ if (!i2c_wait_write_done(i2c_adap))
+ return -EIO;
+
+ }
+
+ }
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ dprintk(4, "\n");
+
+ return msg->len;
+}
+
+/* FIXME: Implement join handling correctly */
+static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+ const struct i2c_msg *msg, int joined)
+{
+ struct au0828_dev *dev = i2c_adap->algo_data;
+ int i;
+
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
+
+ /* Set the I2C clock */
+ au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
+ dev->board.i2c_clk_divider);
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
+
+ dprintk(4, " RECV:\n");
+
+ /* Deal with i2c_scan */
+ if (msg->len == 0) {
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (i2c_wait_read_ack(i2c_adap))
+ return -EIO;
+ return 0;
+ }
+
+ for (i = 0; i < msg->len;) {
+
+ i++;
+
+ if (i < msg->len)
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ |
+ AU0828_I2C_TRIGGER_HOLD);
+ else
+ au0828_write(dev, AU0828_I2C_TRIGGER_200,
+ AU0828_I2C_TRIGGER_READ);
+
+ if (!i2c_wait_read_done(i2c_adap))
+ return -EIO;
+
+ msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
+ 0xff;
+
+ dprintk(4, " %02x\n", msg->buf[i-1]);
+ }
+ if (!i2c_wait_done(i2c_adap))
+ return -EIO;
+
+ dprintk(4, "\n");
+
+ return msg->len;
+}
+
+static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ int i, retval = 0;
+
+ dprintk(4, "%s(num = %d)\n", __func__, num);
+
+ for (i = 0; i < num; i++) {
+ dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
+ __func__, num, msgs[i].addr, msgs[i].len);
+ if (msgs[i].flags & I2C_M_RD) {
+ /* read */
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
+ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+ /* write then read from same address */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i],
+ msgs[i + 1].len);
+ if (retval < 0)
+ goto err;
+ i++;
+ retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
+ } else {
+ /* write */
+ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
+ }
+ if (retval < 0)
+ goto err;
+ }
+ return num;
+
+err:
+ return retval;
+}
+
+static u32 au0828_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au0828_i2c_algo_template = {
+ .master_xfer = i2c_xfer,
+ .functionality = au0828_functionality,
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct i2c_adapter au0828_i2c_adap_template = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .algo = &au0828_i2c_algo_template,
+};
+
+static struct i2c_client au0828_i2c_client_template = {
+ .name = "au0828 internal",
+};
+
+static char *i2c_devs[128] = {
+ [0x8e >> 1] = "au8522",
+ [0xa0 >> 1] = "eeprom",
+ [0xc2 >> 1] = "tuner/xc5000",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i, rc;
+
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c, &buf, 0);
+ if (rc < 0)
+ continue;
+ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
+/* init + register i2c adapter */
+int au0828_i2c_register(struct au0828_dev *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ memcpy(&dev->i2c_adap, &au0828_i2c_adap_template,
+ sizeof(dev->i2c_adap));
+ memcpy(&dev->i2c_algo, &au0828_i2c_algo_template,
+ sizeof(dev->i2c_algo));
+ memcpy(&dev->i2c_client, &au0828_i2c_client_template,
+ sizeof(dev->i2c_client));
+
+ dev->i2c_adap.dev.parent = &dev->usbdev->dev;
+
+ strlcpy(dev->i2c_adap.name, DRIVER_NAME,
+ sizeof(dev->i2c_adap.name));
+
+ dev->i2c_adap.algo = &dev->i2c_algo;
+ dev->i2c_adap.algo_data = dev;
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ if (0 == dev->i2c_rc) {
+ printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME);
+ if (i2c_scan)
+ do_i2c_scan(DRIVER_NAME, &dev->i2c_client);
+ } else
+ printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME);
+
+ return dev->i2c_rc;
+}
+
+int au0828_i2c_unregister(struct au0828_dev *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
+
diff --git a/drivers/media/usb/au0828/au0828-reg.h b/drivers/media/usb/au0828/au0828-reg.h
new file mode 100644
index 000000000000..2140f4cfb645
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-reg.h
@@ -0,0 +1,66 @@
+/*
+ * Driver for the Auvitek USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* We'll start to rename these registers once we have a better
+ * understanding of their meaning.
+ */
+#define REG_000 0x000
+#define REG_001 0x001
+#define REG_002 0x002
+#define REG_003 0x003
+
+#define AU0828_SENSORCTRL_100 0x100
+#define AU0828_SENSORCTRL_VBI_103 0x103
+
+/* I2C registers */
+#define AU0828_I2C_TRIGGER_200 0x200
+#define AU0828_I2C_STATUS_201 0x201
+#define AU0828_I2C_CLK_DIVIDER_202 0x202
+#define AU0828_I2C_DEST_ADDR_203 0x203
+#define AU0828_I2C_WRITE_FIFO_205 0x205
+#define AU0828_I2C_READ_FIFO_209 0x209
+#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
+
+/* Audio registers */
+#define AU0828_AUDIOCTRL_50C 0x50C
+
+#define REG_600 0x600
+
+/*********************************************************************/
+/* Here are constants for values associated with the above registers */
+
+/* I2C Trigger (Reg 0x200) */
+#define AU0828_I2C_TRIGGER_WRITE 0x01
+#define AU0828_I2C_TRIGGER_READ 0x20
+#define AU0828_I2C_TRIGGER_HOLD 0x40
+
+/* I2C Status (Reg 0x201) */
+#define AU0828_I2C_STATUS_READ_DONE 0x01
+#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
+#define AU0828_I2C_STATUS_WRITE_DONE 0x04
+#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
+#define AU0828_I2C_STATUS_BUSY 0x10
+
+/* I2C Clock Divider (Reg 0x202) */
+#define AU0828_I2C_CLK_250KHZ 0x07
+#define AU0828_I2C_CLK_100KHZ 0x14
+#define AU0828_I2C_CLK_30KHZ 0x40
+#define AU0828_I2C_CLK_20KHZ 0x60
diff --git a/drivers/media/video/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c
index 63f593070ee8..63f593070ee8 100644
--- a/drivers/media/video/au0828/au0828-vbi.c
+++ b/drivers/media/usb/au0828/au0828-vbi.c
diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c
new file mode 100644
index 000000000000..870585570571
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828-video.c
@@ -0,0 +1,2034 @@
+/*
+ * Auvitek AU0828 USB Bridge (Analog video support)
+ *
+ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
+ * Copyright (C) 2005-2008 Auvitek International, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * As published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ */
+
+/* Developer Notes:
+ *
+ * VBI support is not yet working
+ * The hardware scaler supported is unimplemented
+ * AC97 audio support is unimplemented (only i2s audio mode)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/suspend.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/tuner.h>
+#include "au0828.h"
+#include "au0828-reg.h"
+
+static DEFINE_MUTEX(au0828_sysfs_lock);
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define au0828_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "au0828 %s :"fmt, \
+ __func__ , ##arg); \
+ } \
+ } while (0)
+
+static inline void print_err_status(struct au0828_dev *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ au0828_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ au0828_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static int check_dev(struct au0828_dev *dev)
+{
+ if (dev->dev_state & DEV_DISCONNECTED) {
+ printk(KERN_INFO "v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+
+ if (dev->dev_state & DEV_MISCONFIGURED) {
+ printk(KERN_INFO "v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void au0828_irq_callback(struct urb *urb)
+{
+ struct au0828_dmaqueue *dma_q = urb->context;
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
+ unsigned long flags = 0;
+ int i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ au0828_isocdbg("au0828_irq_callback called: status kill\n");
+ return;
+ default: /* unknown error */
+ au0828_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock_irqsave(&dev->slock, flags);
+ dev->isoc_ctl.isoc_copy(dev, urb);
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ au0828_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void au0828_uninit_isoc(struct au0828_dev *dev)
+{
+ struct urb *urb;
+ int i;
+
+ au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
+
+ dev->isoc_ctl.nfields = -1;
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->usbdev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->isoc_ctl.urb[i] = NULL;
+ }
+ dev->isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->isoc_ctl.urb);
+ kfree(dev->isoc_ctl.transfer_buffer);
+
+ dev->isoc_ctl.urb = NULL;
+ dev->isoc_ctl.transfer_buffer = NULL;
+ dev->isoc_ctl.num_bufs = 0;
+}
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
+ int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
+{
+ struct au0828_dmaqueue *dma_q = &dev->vidq;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int j, k;
+ int rc;
+
+ au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
+
+ /* De-allocates all pending stuff */
+ au0828_uninit_isoc(dev);
+
+ dev->isoc_ctl.isoc_copy = isoc_copy;
+ dev->isoc_ctl.num_bufs = num_bufs;
+
+ dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ au0828_isocdbg("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ au0828_isocdbg("cannot allocate memory for usb transfer\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.max_pkt_size = max_pkt_size;
+ dev->isoc_ctl.buf = NULL;
+
+ sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i);
+ au0828_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ printk("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ au0828_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ pipe = usb_rcvisocpipe(dev->usbdev,
+ dev->isoc_in_endpointaddr),
+
+ usb_fill_int_urb(urb, dev->usbdev, pipe,
+ dev->isoc_ctl.transfer_buffer[i], sb_size,
+ au0828_irq_callback, dma_q, 1);
+
+ urb->number_of_packets = max_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ dev->isoc_ctl.max_pkt_size;
+ k += dev->isoc_ctl.max_pkt_size;
+ }
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+ if (rc) {
+ au0828_isocdbg("submit of urb %i failed (error=%i)\n",
+ i, rc);
+ au0828_uninit_isoc(dev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the buffer header type and properly handles
+ */
+static void au0828_copy_video(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *fieldstart, *startwrite, *startread;
+ int linesdone, currlinedone, offset, lencopy, remain;
+ int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
+
+ if (len == 0)
+ return;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ remain = len;
+
+ /* Interlaces frame */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+
+ linesdone = dma_q->pos / bytesperline;
+ currlinedone = dma_q->pos % bytesperline;
+ offset = linesdone * bytesperline * 2 + currlinedone;
+ startwrite = fieldstart + offset;
+ lencopy = bytesperline - currlinedone;
+ lencopy = lencopy > remain ? remain : lencopy;
+
+ if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
+ au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ remain = (char *)outp + buf->vb.size - (char *)startwrite;
+ lencopy = remain;
+ }
+ if (lencopy <= 0)
+ return;
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+
+ while (remain > 0) {
+ startwrite += lencopy + bytesperline;
+ startread += lencopy;
+ if (bytesperline > remain)
+ lencopy = remain;
+ else
+ lencopy = bytesperline;
+
+ if ((char *)startwrite + lencopy > (char *)outp +
+ buf->vb.size) {
+ au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ lencopy = remain = (char *)outp + buf->vb.size -
+ (char *)startwrite;
+ }
+ if (lencopy <= 0)
+ break;
+
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+ }
+
+ if (offset > 1440) {
+ /* We have enough data to check for greenscreen */
+ if (outp[0] < 0x60 && outp[1440] < 0x60)
+ dev->greenscreen_detected = 1;
+ }
+
+ dma_q->pos += len;
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer **buf)
+{
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
+
+ if (list_empty(&dma_q->active)) {
+ au0828_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+ dev->isoc_ctl.buf = *buf;
+
+ return;
+}
+
+static void au0828_copy_vbi(struct au0828_dev *dev,
+ struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ unsigned char *startwrite, *startread;
+ int bytesperline;
+ int i, j = 0;
+
+ if (dev == NULL) {
+ au0828_isocdbg("dev is null\n");
+ return;
+ }
+
+ if (dma_q == NULL) {
+ au0828_isocdbg("dma_q is null\n");
+ return;
+ }
+ if (buf == NULL)
+ return;
+ if (p == NULL) {
+ au0828_isocdbg("p is null\n");
+ return;
+ }
+ if (outp == NULL) {
+ au0828_isocdbg("outp is null\n");
+ return;
+ }
+
+ bytesperline = dev->vbi_width;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ startwrite = outp + (dma_q->pos / 2);
+
+ /* Make sure the bottom field populates the second half of the frame */
+ if (buf->top_field == 0)
+ startwrite += bytesperline * dev->vbi_height;
+
+ for (i = 0; i < len; i += 2)
+ startwrite[j++] = startread[i+1];
+
+ dma_q->pos += len;
+}
+
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q,
+ struct au0828_buffer **buf)
+{
+ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ au0828_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vbi_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0x00, (*buf)->vb.size);
+
+ dev->isoc_ctl.vbi_buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
+{
+ struct au0828_buffer *buf;
+ struct au0828_buffer *vbi_buf;
+ struct au0828_dmaqueue *dma_q = urb->context;
+ struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq;
+ unsigned char *outp = NULL;
+ unsigned char *vbioutp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+ unsigned char fbyte;
+ unsigned int vbi_field_size;
+ unsigned int remain, lencopy;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->dev_state & DEV_DISCONNECTED) ||
+ (dev->dev_state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ vbi_buf = dev->isoc_ctl.vbi_buf;
+ if (vbi_buf != NULL)
+ vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ if (urb->iso_frame_desc[i].actual_length <= 0)
+ continue;
+
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ au0828_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ fbyte = p[0];
+ len = urb->iso_frame_desc[i].actual_length - 4;
+ p += 4;
+
+ if (fbyte & 0x80) {
+ len -= 4;
+ p += 4;
+ au0828_isocdbg("Video frame %s\n",
+ (fbyte & 0x40) ? "odd" : "even");
+ if (fbyte & 0x40) {
+ /* VBI */
+ if (vbi_buf != NULL)
+ vbi_buffer_filled(dev,
+ vbi_dma_q,
+ vbi_buf);
+ vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+ if (vbi_buf == NULL)
+ vbioutp = NULL;
+ else
+ vbioutp = videobuf_to_vmalloc(
+ &vbi_buf->vb);
+
+ /* Video */
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ /* As long as isoc traffic is arriving, keep
+ resetting the timer */
+ if (dev->vid_timeout_running)
+ mod_timer(&dev->vid_timeout,
+ jiffies + (HZ / 10));
+ if (dev->vbi_timeout_running)
+ mod_timer(&dev->vbi_timeout,
+ jiffies + (HZ / 10));
+ }
+
+ if (buf != NULL) {
+ if (fbyte & 0x40)
+ buf->top_field = 1;
+ else
+ buf->top_field = 0;
+ }
+
+ if (vbi_buf != NULL) {
+ if (fbyte & 0x40)
+ vbi_buf->top_field = 1;
+ else
+ vbi_buf->top_field = 0;
+ }
+
+ dev->vbi_read = 0;
+ vbi_dma_q->pos = 0;
+ dma_q->pos = 0;
+ }
+
+ vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
+ if (dev->vbi_read < vbi_field_size) {
+ remain = vbi_field_size - dev->vbi_read;
+ if (len < remain)
+ lencopy = len;
+ else
+ lencopy = remain;
+
+ if (vbi_buf != NULL)
+ au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, len);
+
+ len -= lencopy;
+ p += lencopy;
+ dev->vbi_read += lencopy;
+ }
+
+ if (dev->vbi_read >= vbi_field_size && buf != NULL)
+ au0828_copy_video(dev, dma_q, buf, p, outp, len);
+ }
+ return rc;
+}
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
+
+ if (0 == *count)
+ *count = AU0828_DEF_BUF;
+
+ if (*count < AU0828_MIN_BUF)
+ *count = AU0828_MIN_BUF;
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.buf == buf)
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
+ struct au0828_dev *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0) {
+ printk(KERN_INFO "videobuf_iolock failed\n");
+ goto fail;
+ }
+ }
+
+ if (!dev->isoc_ctl.num_bufs)
+ urb_init = 1;
+
+ if (urb_init) {
+ rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
+ AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
+ au0828_isoc_copy);
+ if (rc < 0) {
+ printk(KERN_INFO "au0828_init_isoc failed\n");
+ goto fail;
+ }
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb,
+ struct au0828_buffer,
+ vb);
+ struct au0828_fh *fh = vq->priv_data;
+ struct au0828_dev *dev = fh->dev;
+ struct au0828_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct au0828_buffer *buf = container_of(vb,
+ struct au0828_buffer,
+ vb);
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops au0828_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------
+ V4L2 interface
+ ------------------------------------------------------------------*/
+
+static int au0828_i2s_init(struct au0828_dev *dev)
+{
+ /* Enable i2s mode */
+ au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01);
+ return 0;
+}
+
+/*
+ * Auvitek au0828 analog stream enable
+ * Please set interface0 to AS5 before enable the stream
+ */
+int au0828_analog_stream_enable(struct au0828_dev *d)
+{
+ dprintk(1, "au0828_analog_stream_enable called\n");
+ au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
+ au0828_writereg(d, 0x106, 0x00);
+ /* set x position */
+ au0828_writereg(d, 0x110, 0x00);
+ au0828_writereg(d, 0x111, 0x00);
+ au0828_writereg(d, 0x114, 0xa0);
+ au0828_writereg(d, 0x115, 0x05);
+ /* set y position */
+ au0828_writereg(d, 0x112, 0x00);
+ au0828_writereg(d, 0x113, 0x00);
+ au0828_writereg(d, 0x116, 0xf2);
+ au0828_writereg(d, 0x117, 0x00);
+ au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
+
+ return 0;
+}
+
+int au0828_analog_stream_disable(struct au0828_dev *d)
+{
+ dprintk(1, "au0828_analog_stream_disable called\n");
+ au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
+ return 0;
+}
+
+void au0828_analog_stream_reset(struct au0828_dev *dev)
+{
+ dprintk(1, "au0828_analog_stream_reset called\n");
+ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
+ mdelay(30);
+ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3);
+}
+
+/*
+ * Some operations needs to stop current streaming
+ */
+static int au0828_stream_interrupt(struct au0828_dev *dev)
+{
+ int ret = 0;
+
+ dev->stream_state = STREAM_INTERRUPT;
+ if (dev->dev_state == DEV_DISCONNECTED)
+ return -ENODEV;
+ else if (ret) {
+ dev->dev_state = DEV_MISCONFIGURED;
+ dprintk(1, "%s device is misconfigured!\n", __func__);
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * au0828_release_resources
+ * unregister v4l2 devices
+ */
+void au0828_analog_unregister(struct au0828_dev *dev)
+{
+ dprintk(1, "au0828_release_resources called\n");
+ mutex_lock(&au0828_sysfs_lock);
+
+ if (dev->vdev)
+ video_unregister_device(dev->vdev);
+ if (dev->vbi_dev)
+ video_unregister_device(dev->vbi_dev);
+
+ mutex_unlock(&au0828_sysfs_lock);
+}
+
+
+/* Usage lock check functions */
+static int res_get(struct au0828_fh *fh, unsigned int bit)
+{
+ struct au0828_dev *dev = fh->dev;
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+
+ return 1;
+}
+
+static int res_check(struct au0828_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct au0828_dev *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct au0828_fh *fh, unsigned int bits)
+{
+ struct au0828_dev *dev = fh->dev;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+}
+
+static int get_ressource(struct au0828_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return AU0828_RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return AU0828_RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+/* This function ensures that video frames continue to be delivered even if
+ the ITU-656 input isn't receiving any data (thereby preventing applications
+ such as tvtime from hanging) */
+void au0828_vid_buffer_timeout(unsigned long data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) data;
+ struct au0828_dmaqueue *dma_q = &dev->vidq;
+ struct au0828_buffer *buf;
+ unsigned char *vid_data;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ buf = dev->isoc_ctl.buf;
+ if (buf != NULL) {
+ vid_data = videobuf_to_vmalloc(&buf->vb);
+ memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
+ buffer_filled(dev, dma_q, buf);
+ }
+ get_next_buf(dma_q, &buf);
+
+ if (dev->vid_timeout_running == 1)
+ mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+void au0828_vbi_buffer_timeout(unsigned long data)
+{
+ struct au0828_dev *dev = (struct au0828_dev *) data;
+ struct au0828_dmaqueue *dma_q = &dev->vbiq;
+ struct au0828_buffer *buf;
+ unsigned char *vbi_data;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&dev->slock, flags);
+
+ buf = dev->isoc_ctl.vbi_buf;
+ if (buf != NULL) {
+ vbi_data = videobuf_to_vmalloc(&buf->vb);
+ memset(vbi_data, 0x00, buf->vb.size);
+ vbi_buffer_filled(dev, dma_q, buf);
+ }
+ vbi_get_next_buf(dma_q, &buf);
+
+ if (dev->vbi_timeout_running == 1)
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+
+static int au0828_v4l2_open(struct file *filp)
+{
+ int ret = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct au0828_dev *dev = video_drvdata(filp);
+ struct au0828_fh *fh;
+ int type;
+
+ 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;
+ default:
+ return -EINVAL;
+ }
+
+ fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
+ if (NULL == fh) {
+ dprintk(1, "Failed allocate au0828_fh struct!\n");
+ return -ENOMEM;
+ }
+
+ fh->type = type;
+ fh->dev = dev;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ /* set au0828 interface0 to AS5 here again */
+ ret = usb_set_interface(dev->usbdev, 0, 5);
+ if (ret < 0) {
+ printk(KERN_INFO "Au0828 can't set alternate to 5!\n");
+ return -EBUSY;
+ }
+ dev->width = NTSC_STD_W;
+ dev->height = NTSC_STD_H;
+ dev->frame_size = dev->width * dev->height * 2;
+ dev->field_size = dev->width * dev->height;
+ dev->bytesperline = dev->width * 2;
+
+ au0828_analog_stream_enable(dev);
+ au0828_analog_stream_reset(dev);
+
+ /* If we were doing ac97 instead of i2s, it would go here...*/
+ au0828_i2s_init(dev);
+
+ dev->stream_state = STREAM_OFF;
+ dev->dev_state |= DEV_INITIALIZED;
+ }
+
+ dev->users++;
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct au0828_buffer), fh,
+ &dev->lock);
+
+ /* VBI Setup */
+ dev->vbi_width = 720;
+ dev->vbi_height = 1;
+ videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct au0828_buffer), fh,
+ &dev->lock);
+ return ret;
+}
+
+static int au0828_v4l2_close(struct file *filp)
+{
+ int ret;
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+
+ if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+ /* Cancel timeout thread in case they didn't call streamoff */
+ dev->vid_timeout_running = 0;
+ del_timer_sync(&dev->vid_timeout);
+
+ videobuf_stop(&fh->vb_vidq);
+ res_free(fh, AU0828_RESOURCE_VIDEO);
+ }
+
+ if (res_check(fh, AU0828_RESOURCE_VBI)) {
+ /* Cancel timeout thread in case they didn't call streamoff */
+ dev->vbi_timeout_running = 0;
+ del_timer_sync(&dev->vbi_timeout);
+
+ videobuf_stop(&fh->vb_vbiq);
+ res_free(fh, AU0828_RESOURCE_VBI);
+ }
+
+ if (dev->users == 1) {
+ if (dev->dev_state & DEV_DISCONNECTED) {
+ au0828_analog_unregister(dev);
+ kfree(dev);
+ return 0;
+ }
+
+ au0828_analog_stream_disable(dev);
+
+ au0828_uninit_isoc(dev);
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ /* When close the device, set the usb intf0 into alt0 to free
+ USB bandwidth */
+ ret = usb_set_interface(dev->usbdev, 0, 0);
+ if (ret < 0)
+ printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
+ }
+
+ videobuf_mmap_free(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vbiq);
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+}
+
+static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_locked(dev, AU0828_RESOURCE_VIDEO))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VBI))
+ return -EBUSY;
+
+ if (dev->vbi_timeout_running == 0) {
+ /* Handle case where caller tries to read without
+ calling streamon first */
+ dev->vbi_timeout_running = 1;
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ }
+
+ return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
+ return 0;
+}
+
+static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VIDEO))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, AU0828_RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+ } else {
+ return POLLERR;
+ }
+}
+
+static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct au0828_fh *fh = filp->private_data;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
+
+ return rc;
+}
+
+static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
+ struct v4l2_format *format)
+{
+ int ret;
+ int width = format->fmt.pix.width;
+ int height = format->fmt.pix.height;
+
+ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* If they are demanding a format other than the one we support,
+ bail out (tvtime asks for UYVY and then retries with YUYV) */
+ if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
+ return -EINVAL;
+
+ /* format->fmt.pix.width only support 720 and height 480 */
+ if (width != 720)
+ width = 720;
+ if (height != 480)
+ height = 480;
+
+ format->fmt.pix.width = width;
+ format->fmt.pix.height = height;
+ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ format->fmt.pix.bytesperline = width * 2;
+ format->fmt.pix.sizeimage = width * height * 2;
+ format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ format->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ if (cmd == VIDIOC_TRY_FMT)
+ return 0;
+
+ /* maybe set new image format, driver current only support 720*480 */
+ dev->width = width;
+ dev->height = height;
+ dev->frame_size = width * height * 2;
+ dev->field_size = width * height;
+ dev->bytesperline = width * 2;
+
+ if (dev->stream_state == STREAM_ON) {
+ dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n");
+ ret = au0828_stream_interrupt(dev);
+ if (ret != 0) {
+ dprintk(1, "error interrupting video stream!\n");
+ return ret;
+ }
+ }
+
+ /* set au0828 interface0 to AS5 here again */
+ ret = usb_set_interface(dev->usbdev, 0, 5);
+ if (ret < 0) {
+ printk(KERN_INFO "Au0828 can't set alt setting to 5!\n");
+ return -EBUSY;
+ }
+
+ au0828_analog_stream_enable(dev);
+
+ return 0;
+}
+
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
+ if (qc->type)
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ strlcpy(cap->driver, "au0828", sizeof(cap->driver));
+ strlcpy(cap->card, dev->board.name, sizeof(cap->card));
+ strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+
+ /*set the device capabilities */
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(f->description, "Packed YUV2");
+
+ f->flags = 0;
+ f->pixelformat = V4L2_PIX_FMT_UYVY;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
+ f->fmt.pix.bytesperline = dev->bytesperline;
+ f->fmt.pix.sizeimage = dev->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ printk(KERN_INFO "%s queue busy\n", __func__);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ rc = au0828_set_format(dev, VIDIOC_S_FMT, f);
+out:
+ return rc;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1);
+
+ /* FIXME: when we support something other than NTSC, we are going to
+ have to make the au0828 bridge adjust the size of its capture
+ buffer, which is currently hardcoded at 720x480 */
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm);
+ dev->std_set_in_tuner_core = 1;
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0);
+
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ unsigned int tmp;
+
+ static const char *inames[] = {
+ [AU0828_VMUX_UNDEFINED] = "Undefined",
+ [AU0828_VMUX_COMPOSITE] = "Composite",
+ [AU0828_VMUX_SVIDEO] = "S-Video",
+ [AU0828_VMUX_CABLE] = "Cable TV",
+ [AU0828_VMUX_TELEVISION] = "Television",
+ [AU0828_VMUX_DVB] = "DVB",
+ [AU0828_VMUX_DEBUG] = "tv debug"
+ };
+
+ tmp = input->index;
+
+ if (tmp >= AU0828_MAX_INPUT)
+ return -EINVAL;
+ if (AUVI_INPUT(tmp).type == 0)
+ return -EINVAL;
+
+ input->index = tmp;
+ strcpy(input->name, inames[AUVI_INPUT(tmp).type]);
+ if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) ||
+ (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE))
+ input->type |= V4L2_INPUT_TYPE_TUNER;
+ else
+ input->type |= V4L2_INPUT_TYPE_CAMERA;
+
+ input->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ *i = dev->ctrl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int i;
+
+ dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
+ index);
+ if (index >= AU0828_MAX_INPUT)
+ return -EINVAL;
+ if (AUVI_INPUT(index).type == 0)
+ return -EINVAL;
+ dev->ctrl_input = index;
+
+ switch (AUVI_INPUT(index).type) {
+ case AU0828_VMUX_SVIDEO:
+ dev->input_type = AU0828_VMUX_SVIDEO;
+ break;
+ case AU0828_VMUX_COMPOSITE:
+ dev->input_type = AU0828_VMUX_COMPOSITE;
+ break;
+ case AU0828_VMUX_TELEVISION:
+ dev->input_type = AU0828_VMUX_TELEVISION;
+ break;
+ default:
+ dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n",
+ AUVI_INPUT(index).type);
+ break;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ AUVI_INPUT(index).vmux, 0, 0);
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ int enable = 0;
+ if (AUVI_INPUT(i).audio_setup == NULL)
+ continue;
+
+ if (i == index)
+ enable = 1;
+ else
+ enable = 0;
+ if (enable) {
+ (AUVI_INPUT(i).audio_setup)(dev, enable);
+ } else {
+ /* Make sure we leave it turned on if some
+ other input is routed to this callback */
+ if ((AUVI_INPUT(i).audio_setup) !=
+ ((AUVI_INPUT(index).audio_setup))) {
+ (AUVI_INPUT(i).audio_setup)(dev, enable);
+ }
+ }
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ AUVI_INPUT(index).amux, 0, 0);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ unsigned int index = a->index;
+
+ if (a->index > 1)
+ return -EINVAL;
+
+ index = dev->ctrl_ainput;
+ if (index == 0)
+ strcpy(a->name, "Television");
+ else
+ strcpy(a->name, "Line in");
+
+ a->capability = V4L2_AUDCAP_STEREO;
+ a->index = index;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ if (a->index != dev->ctrl_ainput)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
+ return 0;
+
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (t->index != 0)
+ return -EINVAL;
+
+ strcpy(t->name, "Auvitek tuner");
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (t->index != 0)
+ return -EINVAL;
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0);
+
+ dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
+ t->afc);
+
+ return 0;
+
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ freq->frequency = dev->ctrl_freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (freq->tuner != 0)
+ return -EINVAL;
+ if (freq->type != V4L2_TUNER_ANALOG_TV)
+ return -EINVAL;
+
+ dev->ctrl_freq = freq->frequency;
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 1);
+
+ if (dev->std_set_in_tuner_core == 0) {
+ /* If we've never sent the standard in tuner core, do so now. We
+ don't do this at device probe because we don't want to incur
+ the cost of a firmware load */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
+ dev->vdev->tvnorms);
+ dev->std_set_in_tuner_core = 1;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
+
+ if (dev->dvb.frontend && dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl)
+ dev->dvb.frontend->ops.analog_ops.i2c_gate_ctrl(dev->dvb.frontend, 0);
+
+ au0828_analog_stream_reset(dev);
+
+ return 0;
+}
+
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+ format->fmt.vbi.start[0] = 21;
+ format->fmt.vbi.start[1] = 284;
+
+ return 0;
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ if (v4l2_chip_match_host(&chip->match)) {
+ chip->ident = V4L2_IDENT_AU0828;
+ return 0;
+ }
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
+ if (chip->ident == V4L2_IDENT_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+
+ cc->defrect = cc->bounds;
+
+ cc->pixelaspect.numerator = 54;
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc = -EINVAL;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (unlikely(type != fh->type))
+ return -EINVAL;
+
+ dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (unlikely(!res_get(fh, get_ressource(fh))))
+ return -EBUSY;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ au0828_analog_stream_enable(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ rc = videobuf_streamon(&fh->vb_vidq);
+ dev->vid_timeout_running = 1;
+ mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ rc = videobuf_streamon(&fh->vb_vbiq);
+ dev->vbi_timeout_running = 1;
+ mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
+ }
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+ int i;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev->vid_timeout_running = 0;
+ del_timer_sync(&dev->vid_timeout);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+ rc = au0828_stream_interrupt(dev);
+ if (rc != 0)
+ return rc;
+
+ for (i = 0; i < AU0828_MAX_INPUT; i++) {
+ if (AUVI_INPUT(i).audio_setup == NULL)
+ continue;
+ (AUVI_INPUT(i).audio_setup)(dev, 0);
+ }
+
+ if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh, AU0828_RESOURCE_VIDEO);
+ }
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ dev->vbi_timeout_running = 0;
+ del_timer_sync(&dev->vbi_timeout);
+
+ if (res_check(fh, AU0828_RESOURCE_VBI)) {
+ videobuf_streamoff(&fh->vb_vbiq);
+ res_free(fh, AU0828_RESOURCE_VBI);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ reg->val = au0828_read(dev, reg->reg);
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+ return au0828_writereg(dev, reg->reg, reg->val);
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_reqbufs(&fh->vb_vidq, rb);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_reqbufs(&fh->vb_vbiq, rb);
+
+ return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_querybuf(&fh->vb_vidq, b);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_querybuf(&fh->vb_vbiq, b);
+
+ return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_qbuf(&fh->vb_vidq, b);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_qbuf(&fh->vb_vbiq, b);
+
+ return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct au0828_fh *fh = priv;
+ struct au0828_dev *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* Workaround for a bug in the au0828 hardware design that sometimes
+ results in the colorspace being inverted */
+ if (dev->greenscreen_detected == 1) {
+ dprintk(1, "Detected green frame. Resetting stream...\n");
+ au0828_analog_stream_reset(dev);
+ dev->greenscreen_detected = 0;
+ }
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK);
+
+ return rc;
+}
+
+static struct v4l2_file_operations au0828_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = au0828_v4l2_open,
+ .release = au0828_v4l2_close,
+ .read = au0828_v4l2_read,
+ .poll = au0828_v4l2_poll,
+ .mmap = au0828_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+};
+
+static const struct video_device au0828_video_template = {
+ .fops = &au0828_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = V4L2_STD_NTSC_M,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+/**************************************************************************/
+
+int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface)
+{
+ int retval = -ENOMEM;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, ret;
+
+ dprintk(1, "au0828_analog_register called!\n");
+
+ /* set au0828 usb interface0 to as5 */
+ retval = usb_set_interface(dev->usbdev,
+ interface->cur_altsetting->desc.bInterfaceNumber, 5);
+ if (retval != 0) {
+ printk(KERN_INFO "Failure setting usb interface0 to as5\n");
+ return retval;
+ }
+
+ /* Figure out which endpoint has the isoc interface */
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) &&
+ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_ISOC)) {
+
+ /* we find our isoc in endpoint */
+ u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize);
+ dev->max_pkt_size = (tmp & 0x07ff) *
+ (((tmp & 0x1800) >> 11) + 1);
+ dev->isoc_in_endpointaddr = endpoint->bEndpointAddress;
+ }
+ }
+ if (!(dev->isoc_in_endpointaddr)) {
+ printk(KERN_INFO "Could not locate isoc endpoint\n");
+ kfree(dev);
+ return -ENODEV;
+ }
+
+ init_waitqueue_head(&dev->open);
+ spin_lock_init(&dev->slock);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+
+ dev->vid_timeout.function = au0828_vid_buffer_timeout;
+ dev->vid_timeout.data = (unsigned long) dev;
+ init_timer(&dev->vid_timeout);
+
+ dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
+ dev->vbi_timeout.data = (unsigned long) dev;
+ init_timer(&dev->vbi_timeout);
+
+ dev->width = NTSC_STD_W;
+ dev->height = NTSC_STD_H;
+ dev->field_size = dev->width * dev->height;
+ dev->frame_size = dev->field_size << 1;
+ dev->bytesperline = dev->width << 1;
+ dev->ctrl_ainput = 0;
+
+ /* allocate and fill v4l2 video struct */
+ dev->vdev = video_device_alloc();
+ if (NULL == dev->vdev) {
+ dprintk(1, "Can't allocate video_device.\n");
+ return -ENOMEM;
+ }
+
+ /* allocate the VBI struct */
+ dev->vbi_dev = video_device_alloc();
+ if (NULL == dev->vbi_dev) {
+ dprintk(1, "Can't allocate vbi_device.\n");
+ ret = -ENOMEM;
+ goto err_vdev;
+ }
+
+ /* Fill the video capture device struct */
+ *dev->vdev = au0828_video_template;
+ dev->vdev->parent = &dev->usbdev->dev;
+ dev->vdev->lock = &dev->lock;
+ strcpy(dev->vdev->name, "au0828a video");
+
+ /* Setup the VBI device */
+ *dev->vbi_dev = au0828_video_template;
+ dev->vbi_dev->parent = &dev->usbdev->dev;
+ dev->vbi_dev->lock = &dev->lock;
+ strcpy(dev->vbi_dev->name, "au0828a vbi");
+
+ /* Register the v4l2 device */
+ video_set_drvdata(dev->vdev, dev);
+ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (retval != 0) {
+ dprintk(1, "unable to register video device (error = %d).\n",
+ retval);
+ ret = -ENODEV;
+ goto err_vbi_dev;
+ }
+
+ /* Register the vbi device */
+ video_set_drvdata(dev->vbi_dev, dev);
+ retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
+ if (retval != 0) {
+ dprintk(1, "unable to register vbi device (error = %d).\n",
+ retval);
+ ret = -ENODEV;
+ goto err_vbi_dev;
+ }
+
+ dprintk(1, "%s completed!\n", __func__);
+
+ return 0;
+
+err_vbi_dev:
+ video_device_release(dev->vbi_dev);
+err_vdev:
+ video_device_release(dev->vdev);
+ return ret;
+}
+
diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h
new file mode 100644
index 000000000000..66a56ef7bbe4
--- /dev/null
+++ b/drivers/media/usb/au0828/au0828.h
@@ -0,0 +1,304 @@
+/*
+ * Driver for the Auvitek AU0828 USB bridge
+ *
+ * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <media/tveeprom.h>
+
+/* Analog */
+#include <linux/videodev2.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-device.h>
+
+/* DVB */
+#include "demux.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+#include "dvbdev.h"
+
+#include "au0828-reg.h"
+#include "au0828-cards.h"
+
+#define DRIVER_NAME "au0828"
+#define URB_COUNT 16
+#define URB_BUFSIZE (0xe522)
+
+/* Analog constants */
+#define NTSC_STD_W 720
+#define NTSC_STD_H 480
+
+#define AU0828_INTERLACED_DEFAULT 1
+#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
+
+/* Defination for AU0828 USB transfer */
+#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
+#define AU0828_ISO_PACKETS_PER_URB 128
+
+#define AU0828_MIN_BUF 4
+#define AU0828_DEF_BUF 8
+
+#define AU0828_MAX_INPUT 4
+
+/* au0828 resource types (used for res_get/res_lock etc */
+#define AU0828_RESOURCE_VIDEO 0x01
+#define AU0828_RESOURCE_VBI 0x02
+
+enum au0828_itype {
+ AU0828_VMUX_UNDEFINED = 0,
+ AU0828_VMUX_COMPOSITE,
+ AU0828_VMUX_SVIDEO,
+ AU0828_VMUX_CABLE,
+ AU0828_VMUX_TELEVISION,
+ AU0828_VMUX_DVB,
+ AU0828_VMUX_DEBUG
+};
+
+struct au0828_input {
+ enum au0828_itype type;
+ unsigned int vmux;
+ unsigned int amux;
+ void (*audio_setup) (void *priv, int enable);
+};
+
+struct au0828_board {
+ char *name;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned char i2c_clk_divider;
+ struct au0828_input input[AU0828_MAX_INPUT];
+
+};
+
+struct au0828_dvb {
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct dvb_frontend *frontend;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+ int feeding;
+};
+
+enum au0828_stream_state {
+ STREAM_OFF,
+ STREAM_INTERRUPT,
+ STREAM_ON
+};
+
+#define AUVI_INPUT(nr) (dev->board.input[nr])
+
+/* device state */
+enum au0828_dev_state {
+ DEV_INITIALIZED = 0x01,
+ DEV_DISCONNECTED = 0x02,
+ DEV_MISCONFIGURED = 0x04
+};
+
+struct au0828_fh {
+ struct au0828_dev *dev;
+ unsigned int resources;
+
+ struct videobuf_queue vb_vidq;
+ struct videobuf_queue vb_vbiq;
+ enum v4l2_buf_type type;
+};
+
+struct au0828_usb_isoc_ctl {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+
+ /* Last buffer command and region */
+ u8 cmd;
+ int pos, size, pktsize;
+
+ /* Last field: ODD or EVEN? */
+ int field;
+
+ /* Stores incomplete commands */
+ u32 tmp_buf;
+ int tmp_buf_len;
+
+ /* Stores already requested buffers */
+ struct au0828_buffer *buf;
+ struct au0828_buffer *vbi_buf;
+
+ /* Stores the number of received fields */
+ int nfields;
+
+ /* isoc urb callback */
+ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
+
+};
+
+/* buffer for one video frame */
+struct au0828_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+
+ struct list_head frame;
+ int top_field;
+ int receiving;
+};
+
+struct au0828_dmaqueue {
+ struct list_head active;
+ struct list_head queued;
+
+ wait_queue_head_t wq;
+
+ /* Counters to control buffer fill */
+ int pos;
+};
+
+struct au0828_dev {
+ struct mutex mutex;
+ struct usb_device *usbdev;
+ int boardnr;
+ struct au0828_board board;
+ u8 ctrlmsg[64];
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* Digital */
+ struct au0828_dvb dvb;
+ struct work_struct restart_streaming;
+
+ /* Analog */
+ struct v4l2_device v4l2_dev;
+ int users;
+ unsigned int resources; /* resources in use */
+ struct video_device *vdev;
+ struct video_device *vbi_dev;
+ struct timer_list vid_timeout;
+ int vid_timeout_running;
+ struct timer_list vbi_timeout;
+ int vbi_timeout_running;
+ int width;
+ int height;
+ int vbi_width;
+ int vbi_height;
+ u32 vbi_read;
+ u32 field_size;
+ u32 frame_size;
+ u32 bytesperline;
+ int type;
+ u8 ctrl_ainput;
+ __u8 isoc_in_endpointaddr;
+ u8 isoc_init_ok;
+ int greenscreen_detected;
+ unsigned int frame_count;
+ int ctrl_freq;
+ int input_type;
+ int std_set_in_tuner_core;
+ unsigned int ctrl_input;
+ enum au0828_dev_state dev_state;
+ enum au0828_stream_state stream_state;
+ wait_queue_head_t open;
+
+ struct mutex lock;
+
+ /* Isoc control struct */
+ struct au0828_dmaqueue vidq;
+ struct au0828_dmaqueue vbiq;
+ struct au0828_usb_isoc_ctl isoc_ctl;
+ spinlock_t slock;
+
+ /* usb transfer */
+ int alt; /* alternate */
+ int max_pkt_size; /* max packet size of isoc transaction */
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
+ char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
+ transfer */
+
+ /* USB / URB Related */
+ int urb_streaming;
+ struct urb *urbs[URB_COUNT];
+};
+
+/* ----------------------------------------------------------- */
+#define au0828_read(dev, reg) au0828_readreg(dev, reg)
+#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
+#define au0828_andor(dev, reg, mask, value) \
+ au0828_writereg(dev, reg, \
+ (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask)))
+
+#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit))
+#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0)
+
+/* ----------------------------------------------------------- */
+/* au0828-core.c */
+extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
+extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
+extern int au0828_debug;
+
+/* ----------------------------------------------------------- */
+/* au0828-cards.c */
+extern struct au0828_board au0828_boards[];
+extern struct usb_device_id au0828_usb_id_table[];
+extern void au0828_gpio_setup(struct au0828_dev *dev);
+extern int au0828_tuner_callback(void *priv, int component,
+ int command, int arg);
+extern void au0828_card_setup(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-i2c.c */
+extern int au0828_i2c_register(struct au0828_dev *dev);
+extern int au0828_i2c_unregister(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-video.c */
+int au0828_analog_register(struct au0828_dev *dev,
+ struct usb_interface *interface);
+int au0828_analog_stream_disable(struct au0828_dev *d);
+void au0828_analog_unregister(struct au0828_dev *dev);
+
+/* ----------------------------------------------------------- */
+/* au0828-dvb.c */
+extern int au0828_dvb_register(struct au0828_dev *dev);
+extern void au0828_dvb_unregister(struct au0828_dev *dev);
+
+/* au0828-vbi.c */
+extern struct videobuf_queue_ops au0828_vbi_qops;
+
+#define dprintk(level, fmt, arg...)\
+ do { if (au0828_debug & level)\
+ printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\
+ } while (0)
diff --git a/drivers/media/usb/b2c2/Kconfig b/drivers/media/usb/b2c2/Kconfig
new file mode 100644
index 000000000000..17d35833980c
--- /dev/null
+++ b/drivers/media/usb/b2c2/Kconfig
@@ -0,0 +1,15 @@
+config DVB_B2C2_FLEXCOP_USB
+ tristate "Technisat/B2C2 Air/Sky/Cable2PC USB"
+ depends on DVB_CORE && I2C
+ help
+ Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2,
+
+ Say Y if you own such a device and want to use it.
+
+config DVB_B2C2_FLEXCOP_USB_DEBUG
+ bool "Enable debug for the B2C2 FlexCop drivers"
+ depends on DVB_B2C2_FLEXCOP_USB
+ select DVB_B2C2_FLEXCOP_DEBUG
+ help
+ Say Y if you want to enable the module option to control debug messages
+ of all B2C2 FlexCop drivers.
diff --git a/drivers/media/usb/b2c2/Makefile b/drivers/media/usb/b2c2/Makefile
new file mode 100644
index 000000000000..2778c19a45eb
--- /dev/null
+++ b/drivers/media/usb/b2c2/Makefile
@@ -0,0 +1,5 @@
+b2c2-flexcop-usb-objs := flexcop-usb.o
+obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
+
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/common/b2c2/
diff --git a/drivers/media/usb/b2c2/flexcop-usb.c b/drivers/media/usb/b2c2/flexcop-usb.c
new file mode 100644
index 000000000000..8b6275f85908
--- /dev/null
+++ b/drivers/media/usb/b2c2/flexcop-usb.c
@@ -0,0 +1,587 @@
+/*
+ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-usb.c - covers the USB part
+ * see flexcop.c for copyright information
+ */
+#define FC_LOG_PREFIX "flexcop_usb"
+#include "flexcop-usb.h"
+#include "flexcop-common.h"
+
+/* Version information */
+#define DRIVER_VERSION "0.1"
+#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver"
+#define DRIVER_AUTHOR "Patrick Boettcher <patrick.boettcher@desy.de>"
+
+/* debug */
+#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+#define dprintk(level,args...) \
+ do { if ((debug & level)) printk(args); } while (0)
+
+#define debug_dump(b, l, method) do {\
+ int i; \
+ for (i = 0; i < l; i++) \
+ method("%02x ", b[i]); \
+ method("\n"); \
+} while (0)
+
+#define DEBSTATUS ""
+#else
+#define dprintk(level, args...)
+#define debug_dump(b, l, method)
+#define DEBSTATUS " (debugging is not enabled)"
+#endif
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,"
+ "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
+#undef DEBSTATUS
+
+#define deb_info(args...) dprintk(0x01, args)
+#define deb_ts(args...) dprintk(0x02, args)
+#define deb_ctrl(args...) dprintk(0x04, args)
+#define deb_i2c(args...) dprintk(0x08, args)
+#define deb_v8(args...) dprintk(0x10, args)
+
+/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
+ * in the IBI address, to make the V8 code simpler.
+ * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
+ * in general: 0000 0HHH 000L LL00
+ * IBI ADDRESS FORMAT: RHHH BLLL
+ *
+ * where R is the read(1)/write(0) bit, B is the busy bit
+ * and HHH and LLL are the two sets of three bits from the PCI address.
+ */
+#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
+ (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
+#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
+ (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
+
+/*
+ * DKT 020228
+ * - forget about this VENDOR_BUFFER_SIZE, read and write register
+ * deal with DWORD or 4 bytes, that should be should from now on
+ * - from now on, we don't support anything older than firm 1.00
+ * I eliminated the write register as a 2 trip of writing hi word and lo word
+ * and force this to write only 4 bytes at a time.
+ * NOTE: this should work with all the firmware from 1.00 and newer
+ */
+static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read)
+{
+ struct flexcop_usb *fc_usb = fc->bus_specific;
+ u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
+ u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
+ u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
+ (read ? 0x80 : 0);
+
+ int len = usb_control_msg(fc_usb->udev,
+ read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
+ request,
+ request_type, /* 0xc0 read or 0x40 write */
+ wAddress,
+ 0,
+ val,
+ sizeof(u32),
+ B2C2_WAIT_FOR_OPERATION_RDW * HZ);
+
+ if (len != sizeof(u32)) {
+ err("error while %s dword from %d (%d).", read ? "reading" :
+ "writing", wAddress, wRegOffsPCI);
+ return -EIO;
+ }
+ return 0;
+}
+/*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
+ flexcop_usb_request_t req, u8 page, u16 wAddress,
+ u8 *pbBuffer, u32 buflen)
+{
+ u8 request_type = USB_TYPE_VENDOR;
+ u16 wIndex;
+ int nWaitTime, pipe, len;
+ wIndex = page << 8;
+
+ switch (req) {
+ case B2C2_USB_READ_V8_MEM:
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+ request_type |= USB_DIR_IN;
+ pipe = B2C2_USB_CTRL_PIPE_IN;
+ break;
+ case B2C2_USB_WRITE_V8_MEM:
+ wIndex |= pbBuffer[0];
+ request_type |= USB_DIR_OUT;
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+ case B2C2_USB_FLASH_BLOCK:
+ request_type |= USB_DIR_OUT;
+ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+ default:
+ deb_info("unsupported request for v8_mem_req %x.\n", req);
+ return -EINVAL;
+ }
+ deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
+ wAddress, wIndex, buflen);
+
+ len = usb_control_msg(fc_usb->udev, pipe,
+ req,
+ request_type,
+ wAddress,
+ wIndex,
+ pbBuffer,
+ buflen,
+ nWaitTime * HZ);
+
+ debug_dump(pbBuffer, len, deb_v8);
+ return len == buflen ? 0 : -EIO;
+}
+
+#define bytes_left_to_read_on_page(paddr,buflen) \
+ ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
+ ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
+
+static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
+ flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
+ u32 addr, int extended, u8 *buf, u32 len)
+{
+ int i,ret = 0;
+ u16 wMax;
+ u32 pagechunk = 0;
+
+ switch(req) {
+ case B2C2_USB_READ_V8_MEM:
+ wMax = USB_MEM_READ_MAX;
+ break;
+ case B2C2_USB_WRITE_V8_MEM:
+ wMax = USB_MEM_WRITE_MAX;
+ break;
+ case B2C2_USB_FLASH_BLOCK:
+ wMax = USB_FLASH_MAX;
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ for (i = 0; i < len;) {
+ pagechunk =
+ wMax < bytes_left_to_read_on_page(addr, len) ?
+ wMax :
+ bytes_left_to_read_on_page(addr, len);
+ deb_info("%x\n",
+ (addr & V8_MEMORY_PAGE_MASK) |
+ (V8_MEMORY_EXTENDED*extended));
+
+ ret = flexcop_usb_v8_memory_req(fc_usb, req,
+ page_start + (addr / V8_MEMORY_PAGE_SIZE),
+ (addr & V8_MEMORY_PAGE_MASK) |
+ (V8_MEMORY_EXTENDED*extended),
+ &buf[i], pagechunk);
+
+ if (ret < 0)
+ return ret;
+ addr += pagechunk;
+ len -= pagechunk;
+ }
+ return 0;
+}
+
+static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
+{
+ return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
+ V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
+ fc->dvb_adapter.proposed_mac, 6);
+}
+
+#if 0
+static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
+ flexcop_usb_utility_function_t func, u8 extra, u16 wIndex,
+ u16 buflen, u8 *pvBuffer)
+{
+ u16 wValue;
+ u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
+ int nWaitTime = 2,
+ pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;
+ wValue = (func << 8) | extra;
+
+ len = usb_control_msg(fc_usb->udev,pipe,
+ B2C2_USB_UTILITY,
+ request_type,
+ wValue,
+ wIndex,
+ pvBuffer,
+ buflen,
+ nWaitTime * HZ);
+ return len == buflen ? 0 : -EIO;
+}
+#endif
+
+/* usb i2c stuff */
+static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
+ flexcop_usb_request_t req, flexcop_usb_i2c_function_t func,
+ u8 chipaddr, u8 addr, u8 *buf, u8 buflen)
+{
+ struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
+ u16 wValue, wIndex;
+ int nWaitTime,pipe,len;
+ u8 request_type = USB_TYPE_VENDOR;
+
+ switch (func) {
+ case USB_FUNC_I2C_WRITE:
+ case USB_FUNC_I2C_MULTIWRITE:
+ case USB_FUNC_I2C_REPEATWRITE:
+ /* DKT 020208 - add this to support special case of DiSEqC */
+ case USB_FUNC_I2C_CHECKWRITE:
+ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ nWaitTime = 2;
+ request_type |= USB_DIR_OUT;
+ break;
+ case USB_FUNC_I2C_READ:
+ case USB_FUNC_I2C_REPEATREAD:
+ pipe = B2C2_USB_CTRL_PIPE_IN;
+ nWaitTime = 2;
+ request_type |= USB_DIR_IN;
+ break;
+ default:
+ deb_info("unsupported function for i2c_req %x\n", func);
+ return -EINVAL;
+ }
+ wValue = (func << 8) | (i2c->port << 4);
+ wIndex = (chipaddr << 8 ) | addr;
+
+ deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
+ func, request_type, req,
+ wValue & 0xff, wValue >> 8,
+ wIndex & 0xff, wIndex >> 8);
+
+ len = usb_control_msg(fc_usb->udev,pipe,
+ req,
+ request_type,
+ wValue,
+ wIndex,
+ buf,
+ buflen,
+ nWaitTime * HZ);
+ return len == buflen ? 0 : -EREMOTEIO;
+}
+
+/* actual bus specific access functions,
+ make sure prototype are/will be equal to pci */
+static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
+ flexcop_ibi_register reg)
+{
+ flexcop_ibi_value val;
+ val.raw = 0;
+ flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
+ return val;
+}
+
+static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
+ flexcop_ibi_register reg, flexcop_ibi_value val)
+{
+ return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
+}
+
+static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
+ flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+{
+ if (op == FC_READ)
+ return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
+ USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
+ else
+ return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
+ USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
+}
+
+static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
+ u8 *buffer, int buffer_length)
+{
+ u8 *b;
+ int l;
+
+ deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
+ fc_usb->tmp_buffer_length, buffer_length);
+
+ if (fc_usb->tmp_buffer_length > 0) {
+ memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
+ buffer_length);
+ fc_usb->tmp_buffer_length += buffer_length;
+ b = fc_usb->tmp_buffer;
+ l = fc_usb->tmp_buffer_length;
+ } else {
+ b=buffer;
+ l=buffer_length;
+ }
+
+ while (l >= 190) {
+ if (*b == 0xff) {
+ switch (*(b+1) & 0x03) {
+ case 0x01: /* media packet */
+ if (*(b+2) == 0x47)
+ flexcop_pass_dmx_packets(
+ fc_usb->fc_dev, b+2, 1);
+ else
+ deb_ts("not ts packet %*ph\n", 4, b+2);
+ b += 190;
+ l -= 190;
+ break;
+ default:
+ deb_ts("wrong packet type\n");
+ l = 0;
+ break;
+ }
+ } else {
+ deb_ts("wrong header\n");
+ l = 0;
+ }
+ }
+
+ if (l>0)
+ memcpy(fc_usb->tmp_buffer, b, l);
+ fc_usb->tmp_buffer_length = l;
+}
+
+static void flexcop_usb_urb_complete(struct urb *urb)
+{
+ struct flexcop_usb *fc_usb = urb->context;
+ int i;
+
+ if (urb->actual_length > 0)
+ deb_ts("urb completed, bufsize: %d actlen; %d\n",
+ urb->transfer_buffer_length, urb->actual_length);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ err("iso frame descriptor %d has an error: %d\n", i,
+ urb->iso_frame_desc[i].status);
+ } else
+ if (urb->iso_frame_desc[i].actual_length > 0) {
+ deb_ts("passed %d bytes to the demux\n",
+ urb->iso_frame_desc[i].actual_length);
+
+ flexcop_usb_process_frame(fc_usb,
+ urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ usb_submit_urb(urb,GFP_ATOMIC);
+}
+
+static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff)
+{
+ /* submit/kill iso packets */
+ return 0;
+}
+
+static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
+{
+ int i;
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+ if (fc_usb->iso_urb[i] != NULL) {
+ deb_ts("unlinking/killing urb no. %d\n",i);
+ usb_kill_urb(fc_usb->iso_urb[i]);
+ usb_free_urb(fc_usb->iso_urb[i]);
+ }
+
+ if (fc_usb->iso_buffer != NULL)
+ pci_free_consistent(NULL,
+ fc_usb->buffer_size, fc_usb->iso_buffer,
+ fc_usb->dma_addr);
+}
+
+static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
+{
+ u16 frame_size = le16_to_cpu(
+ fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
+ int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
+ frame_size, i, j, ret;
+ int buffer_offset = 0;
+
+ deb_ts("creating %d iso-urbs with %d frames "
+ "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,
+ B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
+
+ fc_usb->iso_buffer = pci_alloc_consistent(NULL,
+ bufsize, &fc_usb->dma_addr);
+ if (fc_usb->iso_buffer == NULL)
+ return -ENOMEM;
+
+ memset(fc_usb->iso_buffer, 0, bufsize);
+ fc_usb->buffer_size = bufsize;
+
+ /* creating iso urbs */
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+ fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
+ GFP_ATOMIC);
+ if (fc_usb->iso_urb[i] == NULL) {
+ ret = -ENOMEM;
+ goto urb_error;
+ }
+ }
+
+ /* initialising and submitting iso urbs */
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+ int frame_offset = 0;
+ struct urb *urb = fc_usb->iso_urb[i];
+ deb_ts("initializing and submitting urb no. %d "
+ "(buf_offset: %d).\n", i, buffer_offset);
+
+ urb->dev = fc_usb->udev;
+ urb->context = fc_usb;
+ urb->complete = flexcop_usb_urb_complete;
+ urb->pipe = B2C2_USB_DATA_PIPE;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->interval = 1;
+ urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO;
+ urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO;
+ urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset;
+
+ buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+ for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+ deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
+ i, j, frame_offset);
+ urb->iso_frame_desc[j].offset = frame_offset;
+ urb->iso_frame_desc[j].length = frame_size;
+ frame_offset += frame_size;
+ }
+
+ if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
+ err("submitting urb %d failed with %d.", i, ret);
+ goto urb_error;
+ }
+ deb_ts("submitted urb no. %d.\n",i);
+ }
+
+ /* SRAM */
+ flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
+ FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
+ FC_SRAM_DEST_TARGET_WAN_USB);
+ flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
+ flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
+ return 0;
+
+urb_error:
+ flexcop_usb_transfer_exit(fc_usb);
+ return ret;
+}
+
+static int flexcop_usb_init(struct flexcop_usb *fc_usb)
+{
+ /* use the alternate setting with the larges buffer */
+ usb_set_interface(fc_usb->udev,0,1);
+ switch (fc_usb->udev->speed) {
+ case USB_SPEED_LOW:
+ err("cannot handle USB speed because it is too slow.");
+ return -ENODEV;
+ break;
+ case USB_SPEED_FULL:
+ info("running at FULL speed.");
+ break;
+ case USB_SPEED_HIGH:
+ info("running at HIGH speed.");
+ break;
+ case USB_SPEED_UNKNOWN: /* fall through */
+ default:
+ err("cannot handle USB speed because it is unknown.");
+ return -ENODEV;
+ }
+ usb_set_intfdata(fc_usb->uintf, fc_usb);
+ return 0;
+}
+
+static void flexcop_usb_exit(struct flexcop_usb *fc_usb)
+{
+ usb_set_intfdata(fc_usb->uintf, NULL);
+}
+
+static int flexcop_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct flexcop_usb *fc_usb = NULL;
+ struct flexcop_device *fc = NULL;
+ int ret;
+
+ if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) {
+ err("out of memory\n");
+ return -ENOMEM;
+ }
+
+ /* general flexcop init */
+ fc_usb = fc->bus_specific;
+ fc_usb->fc_dev = fc;
+
+ fc->read_ibi_reg = flexcop_usb_read_ibi_reg;
+ fc->write_ibi_reg = flexcop_usb_write_ibi_reg;
+ fc->i2c_request = flexcop_usb_i2c_request;
+ fc->get_mac_addr = flexcop_usb_get_mac_addr;
+
+ fc->stream_control = flexcop_usb_stream_control;
+
+ fc->pid_filtering = 1;
+ fc->bus_type = FC_USB;
+
+ fc->dev = &udev->dev;
+ fc->owner = THIS_MODULE;
+
+ /* bus specific part */
+ fc_usb->udev = udev;
+ fc_usb->uintf = intf;
+ if ((ret = flexcop_usb_init(fc_usb)) != 0)
+ goto err_kfree;
+
+ /* init flexcop */
+ if ((ret = flexcop_device_initialize(fc)) != 0)
+ goto err_usb_exit;
+
+ /* xfer init */
+ if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
+ goto err_fc_exit;
+
+ info("%s successfully initialized and connected.", DRIVER_NAME);
+ return 0;
+
+err_fc_exit:
+ flexcop_device_exit(fc);
+err_usb_exit:
+ flexcop_usb_exit(fc_usb);
+err_kfree:
+ flexcop_device_kfree(fc);
+ return ret;
+}
+
+static void flexcop_usb_disconnect(struct usb_interface *intf)
+{
+ struct flexcop_usb *fc_usb = usb_get_intfdata(intf);
+ flexcop_usb_transfer_exit(fc_usb);
+ flexcop_device_exit(fc_usb->fc_dev);
+ flexcop_usb_exit(fc_usb);
+ flexcop_device_kfree(fc_usb->fc_dev);
+ info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
+}
+
+static struct usb_device_id flexcop_usb_table [] = {
+ { USB_DEVICE(0x0af7, 0x0101) },
+ { }
+};
+MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver flexcop_usb_driver = {
+ .name = "b2c2_flexcop_usb",
+ .probe = flexcop_usb_probe,
+ .disconnect = flexcop_usb_disconnect,
+ .id_table = flexcop_usb_table,
+};
+
+module_usb_driver(flexcop_usb_driver);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/b2c2/flexcop-usb.h b/drivers/media/usb/b2c2/flexcop-usb.h
index 92529a9c4475..92529a9c4475 100644
--- a/drivers/media/dvb/b2c2/flexcop-usb.h
+++ b/drivers/media/usb/b2c2/flexcop-usb.h
diff --git a/drivers/media/video/cpia2/Kconfig b/drivers/media/usb/cpia2/Kconfig
index 66e9283f5993..66e9283f5993 100644
--- a/drivers/media/video/cpia2/Kconfig
+++ b/drivers/media/usb/cpia2/Kconfig
diff --git a/drivers/media/video/cpia2/Makefile b/drivers/media/usb/cpia2/Makefile
index 828cf1b1df86..828cf1b1df86 100644
--- a/drivers/media/video/cpia2/Makefile
+++ b/drivers/media/usb/cpia2/Makefile
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/usb/cpia2/cpia2.h
index cdef677d57ec..cdef677d57ec 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/usb/cpia2/cpia2.h
diff --git a/drivers/media/usb/cpia2/cpia2_core.c b/drivers/media/usb/cpia2/cpia2_core.c
new file mode 100644
index 000000000000..187012ce444b
--- /dev/null
+++ b/drivers/media/usb/cpia2/cpia2_core.c
@@ -0,0 +1,2417 @@
+/****************************************************************************
+ *
+ * Filename: cpia2_core.c
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ * Contact: steve.miller@st.com
+ *
+ * Description:
+ * This is a USB driver for CPia2 based video cameras.
+ * The infrastructure of this driver is based on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ *
+ * Stripped of 2.4 stuff ready for main kernel submit by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ ****************************************************************************/
+
+#include "cpia2.h"
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+
+#define FIRMWARE "cpia2/stv0672_vp4.bin"
+MODULE_FIRMWARE(FIRMWARE);
+
+/* #define _CPIA2_DEBUG_ */
+
+#ifdef _CPIA2_DEBUG_
+
+static const char *block_name[] = {
+ "System",
+ "VC",
+ "VP",
+ "IDATA"
+};
+#endif
+
+static unsigned int debugs_on; /* default 0 - DEBUG_REG */
+
+
+/******************************************************************************
+ *
+ * Forward Declarations
+ *
+ *****************************************************************************/
+static int apply_vp_patch(struct camera_data *cam);
+static int set_default_user_mode(struct camera_data *cam);
+static int set_vw_size(struct camera_data *cam, int size);
+static int configure_sensor(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int config_sensor_410(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int config_sensor_500(struct camera_data *cam,
+ int reqwidth, int reqheight);
+static int set_all_properties(struct camera_data *cam);
+static void wake_system(struct camera_data *cam);
+static void set_lowlight_boost(struct camera_data *cam);
+static void reset_camera_struct(struct camera_data *cam);
+static int cpia2_set_high_power(struct camera_data *cam);
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
+ kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+ ret = __pa(kva);
+ return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ /* Round it off to PAGE_SIZE */
+ 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 ((long)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;
+
+ size = PAGE_ALIGN(size);
+
+ adr = (unsigned long) mem;
+ while ((long)size > 0) {
+ ClearPageReserved(vmalloc_to_page((void *)adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+/******************************************************************************
+ *
+ * cpia2_do_command
+ *
+ * Send an arbitrary command to the camera. For commands that read from
+ * the camera, copy the buffers into the proper param structures.
+ *****************************************************************************/
+int cpia2_do_command(struct camera_data *cam,
+ u32 command, u8 direction, u8 param)
+{
+ int retval = 0;
+ struct cpia2_command cmd;
+ unsigned int device = cam->params.pnp_id.device_type;
+
+ cmd.command = command;
+ cmd.reg_count = 2; /* default */
+ cmd.direction = direction;
+
+ /***
+ * Set up the command.
+ ***/
+ switch (command) {
+ case CPIA2_CMD_GET_VERSION:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.start = CPIA2_SYSTEM_DEVICE_HI;
+ break;
+ case CPIA2_CMD_GET_PNP_ID:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 8;
+ cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI;
+ break;
+ case CPIA2_CMD_GET_ASIC_TYPE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.start = CPIA2_VC_ASIC_ID;
+ break;
+ case CPIA2_CMD_GET_SENSOR:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.start = CPIA2_VP_SENSOR_FLAGS;
+ break;
+ case CPIA2_CMD_GET_VP_DEVICE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.start = CPIA2_VP_DEVICEH;
+ break;
+ case CPIA2_CMD_SET_VP_BRIGHTNESS:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_BRIGHTNESS:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_EXPOSURE_TARGET;
+ else
+ cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
+ break;
+ case CPIA2_CMD_SET_CONTRAST:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_CONTRAST:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_YRANGE;
+ break;
+ case CPIA2_CMD_SET_VP_SATURATION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_SATURATION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP_SATURATION;
+ else
+ cmd.start = CPIA2_VP5_MCUVSATURATION;
+ break;
+ case CPIA2_CMD_SET_VP_GPIO_DATA:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_GPIO_DATA:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_GPIO_DATA;
+ break;
+ case CPIA2_CMD_SET_VP_GPIO_DIRECTION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_GPIO_DIRECTION;
+ break;
+ case CPIA2_CMD_SET_VC_MP_GPIO_DATA:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_MP_DATA;
+ break;
+ case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_MP_DIR;
+ break;
+ case CPIA2_CMD_ENABLE_PACKET_CTRL:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL;
+ cmd.reg_count = 1;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_FLICKER_MODES:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_FLICKER_MODES:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_FLICKER_MODES;
+ break;
+ case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 2;
+ cmd.start = 0;
+ cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
+ cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB |
+ CPIA2_VC_ST_CTRL_EOF_DETECT |
+ CPIA2_VC_ST_CTRL_FIFO_ENABLE;
+ break;
+ case CPIA2_CMD_SET_HI_POWER:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 2;
+ cmd.buffer.registers[0].index =
+ CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.registers[1].index =
+ CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
+ cmd.buffer.registers[1].value =
+ CPIA2_SYSTEM_CONTROL_HIGH_POWER;
+ break;
+ case CPIA2_CMD_SET_LOW_POWER:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.block_data[0] = 0;
+ break;
+ case CPIA2_CMD_CLEAR_V2W_ERR:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
+ break;
+ case CPIA2_CMD_SET_USER_MODE: /* Then fall through */
+ cmd.buffer.block_data[0] = param;
+ case CPIA2_CMD_GET_USER_MODE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_USER_MODE;
+ else
+ cmd.start = CPIA2_VP5_USER_MODE;
+ break;
+ case CPIA2_CMD_FRAMERATE_REQ:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_FRAMERATE_REQUEST;
+ else
+ cmd.start = CPIA2_VP5_FRAMERATE_REQUEST;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_WAKEUP:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_WAKEUP:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_WAKEUP;
+ break;
+ case CPIA2_CMD_SET_PW_CONTROL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_PW_CONTROL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_PW_CTRL;
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_STATE:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_SYSTEMSTATE;
+ break;
+ case CPIA2_CMD_SET_SYSTEM_CTRL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_SYSTEM_CTRL:
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
+ break;
+ case CPIA2_CMD_SET_VP_SYSTEM_CTRL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_SYSTEMCTRL;
+ break;
+ case CPIA2_CMD_SET_VP_EXP_MODES:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VP_EXP_MODES:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_EXPOSURE_MODES;
+ break;
+ case CPIA2_CMD_SET_DEVICE_CONFIG:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_DEVICE_CONFIG:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_DEVICE_CONFIG;
+ break;
+ case CPIA2_CMD_SET_SERIAL_ADDR:
+ cmd.buffer.block_data[0] = param;
+ cmd.req_mode =
+ CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR;
+ break;
+ case CPIA2_CMD_SET_SENSOR_CR1:
+ cmd.buffer.block_data[0] = param;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_SENSOR_CR1;
+ break;
+ case CPIA2_CMD_SET_VC_CONTROL:
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_VC_CONTROL:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VC_VC_CTRL;
+ break;
+ case CPIA2_CMD_SET_TARGET_KB:
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 1;
+ cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB;
+ cmd.buffer.registers[0].value = param;
+ break;
+ case CPIA2_CMD_SET_DEF_JPEG_OPT:
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.reg_count = 4;
+ cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT;
+ cmd.buffer.registers[0].value =
+ CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE;
+ cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE;
+ cmd.buffer.registers[1].value = 20;
+ cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD;
+ cmd.buffer.registers[2].value = 2;
+ cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT;
+ cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
+ break;
+ case CPIA2_CMD_REHASH_VP4:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP_REHASH_VALUES;
+ cmd.buffer.block_data[0] = param;
+ break;
+ case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as
+ this register can also affect
+ flicker modes */
+ cmd.buffer.block_data[0] = param; /* Then fall through */
+ case CPIA2_CMD_GET_USER_EFFECTS:
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 1;
+ if (device == DEVICE_STV_672)
+ cmd.start = CPIA2_VP4_USER_EFFECTS;
+ else
+ cmd.start = CPIA2_VP5_USER_EFFECTS;
+ break;
+ default:
+ LOG("DoCommand received invalid command\n");
+ return -EINVAL;
+ }
+
+ retval = cpia2_send_command(cam, &cmd);
+ if (retval) {
+ return retval;
+ }
+
+ /***
+ * Now copy any results from a read into the appropriate param struct.
+ ***/
+ switch (command) {
+ case CPIA2_CMD_GET_VERSION:
+ cam->params.version.firmware_revision_hi =
+ cmd.buffer.block_data[0];
+ cam->params.version.firmware_revision_lo =
+ cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_PNP_ID:
+ cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) |
+ cmd.buffer.block_data[1];
+ cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) |
+ cmd.buffer.block_data[3];
+ cam->params.pnp_id.device_revision =
+ (cmd.buffer.block_data[4] << 8) |
+ cmd.buffer.block_data[5];
+ if (cam->params.pnp_id.vendor == 0x553) {
+ if (cam->params.pnp_id.product == 0x100) {
+ cam->params.pnp_id.device_type = DEVICE_STV_672;
+ } else if (cam->params.pnp_id.product == 0x140 ||
+ cam->params.pnp_id.product == 0x151) {
+ cam->params.pnp_id.device_type = DEVICE_STV_676;
+ }
+ }
+ break;
+ case CPIA2_CMD_GET_ASIC_TYPE:
+ cam->params.version.asic_id = cmd.buffer.block_data[0];
+ cam->params.version.asic_rev = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_SENSOR:
+ cam->params.version.sensor_flags = cmd.buffer.block_data[0];
+ cam->params.version.sensor_rev = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_VP_DEVICE:
+ cam->params.version.vp_device_hi = cmd.buffer.block_data[0];
+ cam->params.version.vp_device_lo = cmd.buffer.block_data[1];
+ break;
+ case CPIA2_CMD_GET_VP_GPIO_DATA:
+ cam->params.vp_params.gpio_data = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
+ cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
+ cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
+ cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_FLICKER_MODES:
+ cam->params.flicker_control.cam_register =
+ cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_WAKEUP:
+ cam->params.vc_params.wakeup = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_PW_CONTROL:
+ cam->params.vc_params.pw_control = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_SYSTEM_CTRL:
+ cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_STATE:
+ cam->params.vp_params.system_state = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
+ cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VP_EXP_MODES:
+ cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_DEVICE_CONFIG:
+ cam->params.vp_params.device_config = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_VC_CONTROL:
+ cam->params.vc_params.vc_control = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_USER_MODE:
+ cam->params.vp_params.video_mode = cmd.buffer.block_data[0];
+ break;
+ case CPIA2_CMD_GET_USER_EFFECTS:
+ cam->params.vp_params.user_effects = cmd.buffer.block_data[0];
+ break;
+ default:
+ break;
+ }
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_send_command
+ *
+ *****************************************************************************/
+
+#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read")
+#define BINDEX(cmd) (cmd->req_mode & 0x03)
+
+int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd)
+{
+ u8 count;
+ u8 start;
+ u8 *buffer;
+ int retval;
+
+ switch (cmd->req_mode & 0x0c) {
+ case CAMERAACCESS_TYPE_RANDOM:
+ count = cmd->reg_count * sizeof(struct cpia2_register);
+ start = 0;
+ buffer = (u8 *) & cmd->buffer;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Random: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_BLOCK:
+ count = cmd->reg_count;
+ start = cmd->start;
+ buffer = cmd->buffer.block_data;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Block: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_MASK:
+ count = cmd->reg_count * sizeof(struct cpia2_reg_mask);
+ start = 0;
+ buffer = (u8 *) & cmd->buffer;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Mask: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */
+ count = cmd->reg_count;
+ start = cmd->start;
+ buffer = cmd->buffer.block_data;
+ if (debugs_on & DEBUG_REG)
+ DBG("%s Repeat: Register block %s\n", DIR(cmd),
+ block_name[BINDEX(cmd)]);
+ break;
+ default:
+ LOG("%s: invalid request mode\n",__func__);
+ return -EINVAL;
+ }
+
+ retval = cpia2_usb_transfer_cmd(cam,
+ buffer,
+ cmd->req_mode,
+ start, count, cmd->direction);
+#ifdef _CPIA2_DEBUG_
+ if (debugs_on & DEBUG_REG) {
+ int i;
+ for (i = 0; i < cmd->reg_count; i++) {
+ if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK)
+ KINFO("%s Block: [0x%02X] = 0x%02X\n",
+ DIR(cmd), start + i, buffer[i]);
+ if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM)
+ KINFO("%s Random: [0x%02X] = 0x%02X\n",
+ DIR(cmd), cmd->buffer.registers[i].index,
+ cmd->buffer.registers[i].value);
+ }
+ }
+#endif
+
+ return retval;
+};
+
+/*************
+ * Functions to implement camera functionality
+ *************/
+/******************************************************************************
+ *
+ * cpia2_get_version_info
+ *
+ *****************************************************************************/
+static void cpia2_get_version_info(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0);
+}
+
+/******************************************************************************
+ *
+ * cpia2_reset_camera
+ *
+ * Called at least during the open process, sets up initial params.
+ *****************************************************************************/
+int cpia2_reset_camera(struct camera_data *cam)
+{
+ u8 tmp_reg;
+ int retval = 0;
+ int target_kb;
+ int i;
+ struct cpia2_command cmd;
+
+ /***
+ * VC setup
+ ***/
+ retval = configure_sensor(cam,
+ cam->params.roi.width,
+ cam->params.roi.height);
+ if (retval < 0) {
+ ERR("Couldn't configure sensor, error=%d\n", retval);
+ return retval;
+ }
+
+ /* Clear FIFO and route/enable stream block */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+ cmd.reg_count = 2;
+ cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
+ cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
+ cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
+ CPIA2_VC_ST_CTRL_DST_USB |
+ CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE;
+
+ cpia2_send_command(cam, &cmd);
+
+ cpia2_set_high_power(cam);
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ /* Enable button notification */
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
+ cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL;
+ cmd.buffer.registers[0].value =
+ CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX;
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ }
+
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ retval = apply_vp_patch(cam);
+
+ /* wait for vp to go to sleep */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ /***
+ * If this is a 676, apply VP5 fixes before we start streaming
+ ***/
+ if (cam->params.pnp_id.device_type == DEVICE_STV_676) {
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+
+ /* The following writes improve the picture */
+ cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL;
+ cmd.buffer.registers[0].value = 0; /* reduce from the default
+ * rec 601 pedestal of 16 */
+ cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE;
+ cmd.buffer.registers[1].value = 0x92; /* increase from 100% to
+ * (256/256 - 31) to fill
+ * available range */
+ cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING;
+ cmd.buffer.registers[2].value = 0xFF; /* Increase from the
+ * default rec 601 ceiling
+ * of 240 */
+ cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION;
+ cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec
+ * 601 100% level (128)
+ * to 145-192 */
+ cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP;
+ cmd.buffer.registers[4].value = 0x80; /* Inhibit the
+ * anti-flicker */
+
+ /* The following 4 writes are a fix to allow QVGA to work at 30 fps */
+ cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H;
+ cmd.buffer.registers[5].value = 0x01;
+ cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L;
+ cmd.buffer.registers[6].value = 0xE3;
+ cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA;
+ cmd.buffer.registers[7].value = 0x02;
+ cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA;
+ cmd.buffer.registers[8].value = 0xFC;
+
+ cmd.direction = TRANSFER_WRITE;
+ cmd.reg_count = 9;
+
+ cpia2_send_command(cam, &cmd);
+ }
+
+ /* Activate all settings and start the data stream */
+ /* Set user mode */
+ set_default_user_mode(cam);
+
+ /* Give VP time to wake up */
+ schedule_timeout_interruptible(msecs_to_jiffies(100));
+
+ set_all_properties(cam);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
+ DBG("After SetAllProperties(cam), user mode is 0x%0X\n",
+ cam->params.vp_params.video_mode);
+
+ /***
+ * Set audio regulator off. This and the code to set the compresison
+ * state are too complex to form a CPIA2_CMD_, and seem to be somewhat
+ * intertwined. This stuff came straight from the windows driver.
+ ***/
+ /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
+ tmp_reg = cam->params.vp_params.system_ctrl;
+ cmd.buffer.registers[0].value = tmp_reg &
+ (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF));
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
+ cmd.buffer.registers[1].value = cam->params.vp_params.device_config |
+ CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE;
+ cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL;
+ cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG;
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+ cmd.reg_count = 2;
+ cmd.direction = TRANSFER_WRITE;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+
+ /* Set the correct I2C address in the CPiA-2 system register */
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR);
+
+ /* Now have sensor access - set bit to turn the audio regulator off */
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SENSOR_CR1,
+ TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR);
+
+ /* Set the correct I2C address in the CPiA-2 system register */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88
+ else
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_SERIAL_ADDR,
+ TRANSFER_WRITE,
+ CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a
+
+ /* increase signal drive strength */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_676)
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_VP_EXP_MODES,
+ TRANSFER_WRITE,
+ CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP);
+
+ /* Start autoexposure */
+ cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
+ cmd.buffer.registers[0].value = cam->params.vp_params.device_config &
+ (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
+ cmd.buffer.registers[1].value =
+ cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL;
+
+ cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG;
+ cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL;
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
+ cmd.reg_count = 2;
+ cmd.direction = TRANSFER_WRITE;
+
+ cpia2_send_command(cam, &cmd);
+
+ /* Set compression state */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0);
+ if (cam->params.compression.inhibit_htables) {
+ tmp_reg = cam->params.vc_params.vc_control |
+ CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
+ } else {
+ tmp_reg = cam->params.vc_params.vc_control &
+ ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
+ }
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ /* Set target size (kb) on vc
+ This is a heuristic based on the quality parameter and the raw
+ framesize in kB divided by 16 (the compression factor when the
+ quality is 100%) */
+ target_kb = (cam->width * cam->height * 2 / 16384) *
+ cam->params.vc_params.quality / 100;
+ if (target_kb < 1)
+ target_kb = 1;
+ cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB,
+ TRANSFER_WRITE, target_kb);
+
+ /* Wiggle VC Reset */
+ /***
+ * First read and wait a bit.
+ ***/
+ for (i = 0; i < 50; i++) {
+ cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL,
+ TRANSFER_READ, 0);
+ }
+
+ tmp_reg = cam->params.vc_params.pw_control;
+ tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N;
+
+ cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N;
+ cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
+
+ cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0);
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
+ DBG("After VC RESET, user mode is 0x%0X\n",
+ cam->params.vp_params.video_mode);
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_high_power
+ *
+ *****************************************************************************/
+static int cpia2_set_high_power(struct camera_data *cam)
+{
+ int i;
+ for (i = 0; i <= 50; i++) {
+ /* Read system status */
+ cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0);
+
+ /* If there is an error, clear it */
+ if(cam->params.camera_state.system_ctrl &
+ CPIA2_SYSTEM_CONTROL_V2W_ERR)
+ cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR,
+ TRANSFER_WRITE, 0);
+
+ /* Try to set high power mode */
+ cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL,
+ TRANSFER_WRITE, 1);
+
+ /* Try to read something in VP to check if everything is awake */
+ cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE,
+ TRANSFER_READ, 0);
+ if (cam->params.vp_params.system_state &
+ CPIA2_VP_SYSTEMSTATE_HK_ALIVE) {
+ break;
+ } else if (i == 50) {
+ cam->params.camera_state.power_mode = LO_POWER_MODE;
+ ERR("Camera did not wake up\n");
+ return -EIO;
+ }
+ }
+
+ DBG("System now in high power state\n");
+ cam->params.camera_state.power_mode = HI_POWER_MODE;
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_low_power
+ *
+ *****************************************************************************/
+int cpia2_set_low_power(struct camera_data *cam)
+{
+ cam->params.camera_state.power_mode = LO_POWER_MODE;
+ cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * apply_vp_patch
+ *
+ *****************************************************************************/
+static int cpia2_send_onebyte_command(struct camera_data *cam,
+ struct cpia2_command *cmd,
+ u8 start, u8 datum)
+{
+ cmd->buffer.block_data[0] = datum;
+ cmd->start = start;
+ cmd->reg_count = 1;
+ return cpia2_send_command(cam, cmd);
+}
+
+static int apply_vp_patch(struct camera_data *cam)
+{
+ const struct firmware *fw;
+ const char fw_name[] = FIRMWARE;
+ int i, ret;
+ struct cpia2_command cmd;
+
+ ret = request_firmware(&fw, fw_name, &cam->dev->dev);
+ if (ret) {
+ printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n",
+ fw_name);
+ return ret;
+ }
+
+ cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP;
+ cmd.direction = TRANSFER_WRITE;
+
+ /* First send the start address... */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
+
+ /* ... followed by the data payload */
+ for (i = 2; i < fw->size; i += 64) {
+ cmd.start = 0x0C; /* Data */
+ cmd.reg_count = min_t(int, 64, fw->size - i);
+ memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count);
+ cpia2_send_command(cam, &cmd);
+ }
+
+ /* Next send the start address... */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
+
+ /* ... followed by the 'goto' command */
+ cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1);
+
+ release_firmware(fw);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * set_default_user_mode
+ *
+ *****************************************************************************/
+static int set_default_user_mode(struct camera_data *cam)
+{
+ unsigned char user_mode;
+ unsigned char frame_rate;
+ int width = cam->params.roi.width;
+ int height = cam->params.roi.height;
+
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ if ((width > STV_IMAGE_QCIF_COLS)
+ || (height > STV_IMAGE_QCIF_ROWS)) {
+ user_mode = CPIA2_VP_USER_MODE_CIF;
+ } else {
+ user_mode = CPIA2_VP_USER_MODE_QCIFDS;
+ }
+ frame_rate = CPIA2_VP_FRAMERATE_30;
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ if ((width > STV_IMAGE_CIF_COLS)
+ || (height > STV_IMAGE_CIF_ROWS)) {
+ user_mode = CPIA2_VP_USER_MODE_VGA;
+ } else {
+ user_mode = CPIA2_VP_USER_MODE_QVGADS;
+ }
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ frame_rate = CPIA2_VP_FRAMERATE_15;
+ else
+ frame_rate = CPIA2_VP_FRAMERATE_30;
+ break;
+ default:
+ LOG("%s: Invalid sensor flag value 0x%0X\n",__func__,
+ cam->params.version.sensor_flags);
+ return -EINVAL;
+ }
+
+ DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n",
+ cam->params.version.sensor_flags, user_mode, frame_rate);
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE,
+ user_mode);
+ if(cam->params.vp_params.frame_rate > 0 &&
+ frame_rate > cam->params.vp_params.frame_rate)
+ frame_rate = cam->params.vp_params.frame_rate;
+
+ cpia2_set_fps(cam, frame_rate);
+
+// if (cam->params.pnp_id.device_type == DEVICE_STV_676)
+// cpia2_do_command(cam,
+// CPIA2_CMD_SET_VP_SYSTEM_CTRL,
+// TRANSFER_WRITE,
+// CPIA2_VP_SYSTEMCTRL_HK_CONTROL |
+// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_match_video_size
+ *
+ * return the best match, where 'best' is as always
+ * the largest that is not bigger than what is requested.
+ *****************************************************************************/
+int cpia2_match_video_size(int width, int height)
+{
+ if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS)
+ return VIDEOSIZE_VGA;
+
+ if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS)
+ return VIDEOSIZE_CIF;
+
+ if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS)
+ return VIDEOSIZE_QVGA;
+
+ if (width >= 288 && height >= 216)
+ return VIDEOSIZE_288_216;
+
+ if (width >= 256 && height >= 192)
+ return VIDEOSIZE_256_192;
+
+ if (width >= 224 && height >= 168)
+ return VIDEOSIZE_224_168;
+
+ if (width >= 192 && height >= 144)
+ return VIDEOSIZE_192_144;
+
+ if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS)
+ return VIDEOSIZE_QCIF;
+
+ return -1;
+}
+
+/******************************************************************************
+ *
+ * SetVideoSize
+ *
+ *****************************************************************************/
+static int set_vw_size(struct camera_data *cam, int size)
+{
+ int retval = 0;
+
+ cam->params.vp_params.video_size = size;
+
+ switch (size) {
+ case VIDEOSIZE_VGA:
+ DBG("Setting size to VGA\n");
+ cam->params.roi.width = STV_IMAGE_VGA_COLS;
+ cam->params.roi.height = STV_IMAGE_VGA_ROWS;
+ cam->width = STV_IMAGE_VGA_COLS;
+ cam->height = STV_IMAGE_VGA_ROWS;
+ break;
+ case VIDEOSIZE_CIF:
+ DBG("Setting size to CIF\n");
+ cam->params.roi.width = STV_IMAGE_CIF_COLS;
+ cam->params.roi.height = STV_IMAGE_CIF_ROWS;
+ cam->width = STV_IMAGE_CIF_COLS;
+ cam->height = STV_IMAGE_CIF_ROWS;
+ break;
+ case VIDEOSIZE_QVGA:
+ DBG("Setting size to QVGA\n");
+ cam->params.roi.width = STV_IMAGE_QVGA_COLS;
+ cam->params.roi.height = STV_IMAGE_QVGA_ROWS;
+ cam->width = STV_IMAGE_QVGA_COLS;
+ cam->height = STV_IMAGE_QVGA_ROWS;
+ break;
+ case VIDEOSIZE_288_216:
+ cam->params.roi.width = 288;
+ cam->params.roi.height = 216;
+ cam->width = 288;
+ cam->height = 216;
+ break;
+ case VIDEOSIZE_256_192:
+ cam->width = 256;
+ cam->height = 192;
+ cam->params.roi.width = 256;
+ cam->params.roi.height = 192;
+ break;
+ case VIDEOSIZE_224_168:
+ cam->width = 224;
+ cam->height = 168;
+ cam->params.roi.width = 224;
+ cam->params.roi.height = 168;
+ break;
+ case VIDEOSIZE_192_144:
+ cam->width = 192;
+ cam->height = 144;
+ cam->params.roi.width = 192;
+ cam->params.roi.height = 144;
+ break;
+ case VIDEOSIZE_QCIF:
+ DBG("Setting size to QCIF\n");
+ cam->params.roi.width = STV_IMAGE_QCIF_COLS;
+ cam->params.roi.height = STV_IMAGE_QCIF_ROWS;
+ cam->width = STV_IMAGE_QCIF_COLS;
+ cam->height = STV_IMAGE_QCIF_ROWS;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * configure_sensor
+ *
+ *****************************************************************************/
+static int configure_sensor(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ int retval;
+
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ retval = config_sensor_410(cam, req_width, req_height);
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ retval = config_sensor_500(cam, req_width, req_height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * config_sensor_410
+ *
+ *****************************************************************************/
+static int config_sensor_410(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ struct cpia2_command cmd;
+ int i = 0;
+ int image_size;
+ int image_type;
+ int width = req_width;
+ int height = req_height;
+
+ /***
+ * Make sure size doesn't exceed CIF.
+ ***/
+ if (width > STV_IMAGE_CIF_COLS)
+ width = STV_IMAGE_CIF_COLS;
+ if (height > STV_IMAGE_CIF_ROWS)
+ height = STV_IMAGE_CIF_ROWS;
+
+ image_size = cpia2_match_video_size(width, height);
+
+ DBG("Config 410: width = %d, height = %d\n", width, height);
+ DBG("Image size returned is %d\n", image_size);
+ if (image_size >= 0) {
+ set_vw_size(cam, image_size);
+ width = cam->params.roi.width;
+ height = cam->params.roi.height;
+
+ DBG("After set_vw_size(), width = %d, height = %d\n",
+ width, height);
+ if (width <= 176 && height <= 144) {
+ DBG("image type = VIDEOSIZE_QCIF\n");
+ image_type = VIDEOSIZE_QCIF;
+ }
+ else if (width <= 320 && height <= 240) {
+ DBG("image type = VIDEOSIZE_QVGA\n");
+ image_type = VIDEOSIZE_QVGA;
+ }
+ else {
+ DBG("image type = VIDEOSIZE_CIF\n");
+ image_type = VIDEOSIZE_CIF;
+ }
+ } else {
+ ERR("ConfigSensor410 failed\n");
+ return -EINVAL;
+ }
+
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+
+ /* VC Format */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
+ if (image_type == VIDEOSIZE_CIF) {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_FORMAT_UFIRST |
+ CPIA2_VC_VC_FORMAT_SHORTLINE);
+ } else {
+ cmd.buffer.registers[i++].value =
+ (u8) CPIA2_VC_VC_FORMAT_UFIRST;
+ }
+
+ /* VC Clocks */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
+ if (image_type == VIDEOSIZE_QCIF) {
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.buffer.registers[i++].value=
+ (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_672_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ DBG("VC_Clocks (0xc4) should be B\n");
+ }
+ else {
+ cmd.buffer.registers[i++].value=
+ (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ }
+ } else {
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_CLOCKS_LOGDIV0);
+ }
+ else {
+ cmd.buffer.registers[i++].value =
+ (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
+ CPIA2_VC_VC_676_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV0);
+ }
+ }
+ DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value);
+
+ /* Input reqWidth from VC */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (STV_IMAGE_QCIF_COLS / 4);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (STV_IMAGE_CIF_COLS / 4);
+
+ /* Timings */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 208;
+ else
+ cmd.buffer.registers[i++].value = (u8) 160;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 160;
+ else
+ cmd.buffer.registers[i++].value = (u8) 64;
+
+ /* Output Image Size */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
+ cmd.buffer.registers[i++].value = cam->params.roi.width / 4;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
+ cmd.buffer.registers[i++].value = cam->params.roi.height / 4;
+
+ /* Cropping */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
+ else
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
+
+ /* Scaling registers (defaults) */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
+
+ cmd.reg_count = i;
+
+ cpia2_send_command(cam, &cmd);
+
+ return i;
+}
+
+
+/******************************************************************************
+ *
+ * config_sensor_500(cam)
+ *
+ *****************************************************************************/
+static int config_sensor_500(struct camera_data *cam,
+ int req_width, int req_height)
+{
+ struct cpia2_command cmd;
+ int i = 0;
+ int image_size = VIDEOSIZE_CIF;
+ int image_type = VIDEOSIZE_VGA;
+ int width = req_width;
+ int height = req_height;
+ unsigned int device = cam->params.pnp_id.device_type;
+
+ image_size = cpia2_match_video_size(width, height);
+
+ if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS)
+ image_type = VIDEOSIZE_VGA;
+ else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS)
+ image_type = VIDEOSIZE_CIF;
+ else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS)
+ image_type = VIDEOSIZE_QVGA;
+ else
+ image_type = VIDEOSIZE_QCIF;
+
+ if (image_size >= 0) {
+ set_vw_size(cam, image_size);
+ width = cam->params.roi.width;
+ height = cam->params.roi.height;
+ } else {
+ ERR("ConfigSensor500 failed\n");
+ return -EINVAL;
+ }
+
+ DBG("image_size = %d, width = %d, height = %d, type = %d\n",
+ image_size, width, height, image_type);
+
+ cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
+ cmd.direction = TRANSFER_WRITE;
+ i = 0;
+
+ /* VC Format */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
+ cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING;
+ i++;
+
+ /* VC Clocks */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
+ if (device == DEVICE_STV_672) {
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1;
+ else
+ cmd.buffer.registers[i].value =
+ (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV3);
+ } else {
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0;
+ else
+ cmd.buffer.registers[i].value =
+ (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING |
+ CPIA2_VC_VC_CLOCKS_LOGDIV2);
+ }
+ i++;
+
+ DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value);
+
+ /* Input width from VP */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i].value =
+ (u8) (STV_IMAGE_VGA_COLS / 4);
+ else
+ cmd.buffer.registers[i].value =
+ (u8) (STV_IMAGE_QVGA_COLS / 4);
+ i++;
+ DBG("Input width = %d\n", cmd.buffer.registers[i-1].value);
+
+ /* Timings */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 2;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 250;
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value = (u8) 125;
+ else
+ cmd.buffer.registers[i++].value = (u8) 160;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 2;
+ else
+ cmd.buffer.registers[i++].value = (u8) 1;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value = (u8) 12;
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value = (u8) 64;
+ else
+ cmd.buffer.registers[i++].value = (u8) 6;
+
+ /* Output Image Size */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4;
+ else
+ cmd.buffer.registers[i++].value = width / 4;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
+ if (image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4;
+ else
+ cmd.buffer.registers[i++].value = height / 4;
+
+ /* Cropping */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2);
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2);
+ else if (image_type == VIDEOSIZE_CIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
+ else /*if (image_type == VIDEOSIZE_QCIF)*/
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
+ if (image_type == VIDEOSIZE_VGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2);
+ else if (image_type == VIDEOSIZE_QVGA)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2);
+ else if (image_type == VIDEOSIZE_CIF)
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
+ else /*if (image_type == VIDEOSIZE_QCIF)*/
+ cmd.buffer.registers[i++].value =
+ (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
+
+ /* Scaling registers (defaults) */
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 36;
+ else
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 32;
+ else
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 26;
+ else
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 21;
+ else
+ cmd.buffer.registers[i++].value = (u8) 31;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
+ cmd.buffer.registers[i++].value = (u8) 0;
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */
+ else
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
+
+ cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
+ if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
+ cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */
+ else
+ cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
+
+ cmd.reg_count = i;
+
+ cpia2_send_command(cam, &cmd);
+
+ return i;
+}
+
+
+/******************************************************************************
+ *
+ * setallproperties
+ *
+ * This sets all user changeable properties to the values in cam->params.
+ *****************************************************************************/
+static int set_all_properties(struct camera_data *cam)
+{
+ /**
+ * Don't set target_kb here, it will be set later.
+ * framerate and user_mode were already set (set_default_user_mode).
+ **/
+
+ cpia2_usb_change_streaming_alternate(cam,
+ cam->params.camera_state.stream_mode);
+
+ cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ TRANSFER_WRITE, cam->params.vp_params.gpio_direction);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE,
+ cam->params.vp_params.gpio_data);
+
+ v4l2_ctrl_handler_setup(&cam->hdl);
+
+ wake_system(cam);
+
+ set_lowlight_boost(cam);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_save_camera_state
+ *
+ *****************************************************************************/
+void cpia2_save_camera_state(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ,
+ 0);
+ cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0);
+ /* Don't get framerate or target_kb. Trust the values we already have */
+}
+
+
+/******************************************************************************
+ *
+ * cpia2_set_flicker_mode
+ *
+ *****************************************************************************/
+int cpia2_set_flicker_mode(struct camera_data *cam, int mode)
+{
+ unsigned char cam_reg;
+ int err = 0;
+
+ if(cam->params.pnp_id.device_type != DEVICE_STV_672)
+ return -EINVAL;
+
+ /* Set the appropriate bits in FLICKER_MODES, preserving the rest */
+ if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES,
+ TRANSFER_READ, 0)))
+ return err;
+ cam_reg = cam->params.flicker_control.cam_register;
+
+ switch(mode) {
+ case NEVER_FLICKER:
+ cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ case FLICKER_60:
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ case FLICKER_50:
+ cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
+ cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES,
+ TRANSFER_WRITE, cam_reg)))
+ return err;
+
+ /* Set the appropriate bits in EXP_MODES, preserving the rest */
+ if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES,
+ TRANSFER_READ, 0)))
+ return err;
+ cam_reg = cam->params.vp_params.exposure_modes;
+
+ if (mode == NEVER_FLICKER) {
+ cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
+ } else {
+ cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
+ }
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES,
+ TRANSFER_WRITE, cam_reg)))
+ return err;
+
+ if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4,
+ TRANSFER_WRITE, 1)))
+ return err;
+
+ switch(mode) {
+ case NEVER_FLICKER:
+ case FLICKER_60:
+ case FLICKER_50:
+ cam->params.flicker_control.flicker_mode_req = mode;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_property_flip
+ *
+ *****************************************************************************/
+void cpia2_set_property_flip(struct camera_data *cam, int prop_val)
+{
+ unsigned char cam_reg;
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cam_reg = cam->params.vp_params.user_effects;
+
+ if (prop_val)
+ {
+ cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP;
+ }
+ else
+ {
+ cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP;
+ }
+ cam->params.vp_params.user_effects = cam_reg;
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam_reg);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_property_mirror
+ *
+ *****************************************************************************/
+void cpia2_set_property_mirror(struct camera_data *cam, int prop_val)
+{
+ unsigned char cam_reg;
+
+ cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
+ cam_reg = cam->params.vp_params.user_effects;
+
+ if (prop_val)
+ {
+ cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR;
+ }
+ else
+ {
+ cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR;
+ }
+ cam->params.vp_params.user_effects = cam_reg;
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam_reg);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_gpio
+ *
+ *****************************************************************************/
+int cpia2_set_gpio(struct camera_data *cam, unsigned char setting)
+{
+ int ret;
+
+ /* Set the microport direction (register 0x90, should be defined
+ * already) to 1 (user output), and set the microport data (0x91) to
+ * the value in the ioctl argument.
+ */
+
+ ret = cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ CPIA2_VC_MP_DIR_OUTPUT,
+ 255);
+ if (ret < 0)
+ return ret;
+ cam->params.vp_params.gpio_direction = 255;
+
+ ret = cpia2_do_command(cam,
+ CPIA2_CMD_SET_VC_MP_GPIO_DATA,
+ CPIA2_VC_MP_DIR_OUTPUT,
+ setting);
+ if (ret < 0)
+ return ret;
+ cam->params.vp_params.gpio_data = setting;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_fps
+ *
+ *****************************************************************************/
+int cpia2_set_fps(struct camera_data *cam, int framerate)
+{
+ int retval;
+
+ switch(framerate) {
+ case CPIA2_VP_FRAMERATE_30:
+ case CPIA2_VP_FRAMERATE_25:
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags ==
+ CPIA2_VP_SENSOR_FLAGS_500) {
+ return -EINVAL;
+ }
+ /* Fall through */
+ case CPIA2_VP_FRAMERATE_15:
+ case CPIA2_VP_FRAMERATE_12_5:
+ case CPIA2_VP_FRAMERATE_7_5:
+ case CPIA2_VP_FRAMERATE_6_25:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ framerate == CPIA2_VP_FRAMERATE_15)
+ framerate = 0; /* Work around bug in VP4 */
+
+ retval = cpia2_do_command(cam,
+ CPIA2_CMD_FRAMERATE_REQ,
+ TRANSFER_WRITE,
+ framerate);
+
+ if(retval == 0)
+ cam->params.vp_params.frame_rate = framerate;
+
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_brightness
+ *
+ *****************************************************************************/
+void cpia2_set_brightness(struct camera_data *cam, unsigned char value)
+{
+ /***
+ * Don't let the register be set to zero - bug in VP4 - flash of full
+ * brightness
+ ***/
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0)
+ value++;
+ DBG("Setting brightness to %d (0x%0x)\n", value, value);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_contrast
+ *
+ *****************************************************************************/
+void cpia2_set_contrast(struct camera_data *cam, unsigned char value)
+{
+ DBG("Setting contrast to %d (0x%0x)\n", value, value);
+ cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_saturation
+ *
+ *****************************************************************************/
+void cpia2_set_saturation(struct camera_data *cam, unsigned char value)
+{
+ DBG("Setting saturation to %d (0x%0x)\n", value, value);
+ cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value);
+}
+
+/******************************************************************************
+ *
+ * wake_system
+ *
+ *****************************************************************************/
+static void wake_system(struct camera_data *cam)
+{
+ cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0);
+}
+
+/******************************************************************************
+ *
+ * set_lowlight_boost
+ *
+ * Valid for STV500 sensor only
+ *****************************************************************************/
+static void set_lowlight_boost(struct camera_data *cam)
+{
+ struct cpia2_command cmd;
+
+ if (cam->params.pnp_id.device_type != DEVICE_STV_672 ||
+ cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500)
+ return;
+
+ cmd.direction = TRANSFER_WRITE;
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 3;
+ cmd.start = CPIA2_VP_RAM_ADDR_H;
+
+ cmd.buffer.block_data[0] = 0; /* High byte of address to write to */
+ cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */
+ cmd.buffer.block_data[2] = 0; /* High byte of data to write */
+
+ cpia2_send_command(cam, &cmd);
+
+ if (cam->params.vp_params.lowlight_boost) {
+ cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */
+ } else {
+ cmd.buffer.block_data[0] = 0x06;
+ }
+ cmd.start = CPIA2_VP_RAM_DATA;
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+
+ /* Rehash the VP4 values */
+ cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1);
+}
+
+/******************************************************************************
+ *
+ * cpia2_set_format
+ *
+ * Assumes that new size is already set in param struct.
+ *****************************************************************************/
+void cpia2_set_format(struct camera_data *cam)
+{
+ cam->flush = true;
+
+ cpia2_usb_stream_pause(cam);
+
+ /* reset camera to new size */
+ cpia2_set_low_power(cam);
+ cpia2_reset_camera(cam);
+ cam->flush = false;
+
+ cpia2_dbg_dump_registers(cam);
+
+ cpia2_usb_stream_resume(cam);
+}
+
+/******************************************************************************
+ *
+ * cpia2_dbg_dump_registers
+ *
+ *****************************************************************************/
+void cpia2_dbg_dump_registers(struct camera_data *cam)
+{
+#ifdef _CPIA2_DEBUG_
+ struct cpia2_command cmd;
+
+ if (!(debugs_on & DEBUG_DUMP_REGS))
+ return;
+
+ cmd.direction = TRANSFER_READ;
+
+ /* Start with bank 0 (SYSTEM) */
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
+ cmd.reg_count = 3;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "System Device Hi = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "System Device Lo = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "System_system control = 0x%X\n",
+ cmd.buffer.block_data[2]);
+
+ /* Bank 1 (VC) */
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
+ cmd.reg_count = 4;
+ cmd.start = 0x80;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "ASIC_ID = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "ASIC_REV = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "PW_CONTRL = 0x%X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "WAKEUP = 0x%X\n",
+ cmd.buffer.block_data[3]);
+
+ cmd.start = 0xA0; /* ST_CTRL */
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "Stream ctrl = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xA4; /* Stream status */
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "Stream status = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xA8; /* USB status */
+ cmd.reg_count = 3;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "USB_CTRL = 0x%X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "USB_STRM = 0x%X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "USB_STATUS = 0x%X\n",
+ cmd.buffer.block_data[2]);
+
+ cmd.start = 0xAF; /* USB settings */
+ cmd.reg_count = 1;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "USB settings = 0x%X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.start = 0xC0; /* VC stuff */
+ cmd.reg_count = 26;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VC Control = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VC Format = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VC Clocks = 0x%0X\n",
+ cmd.buffer.block_data[4]);
+ printk(KERN_DEBUG "VC IHSize = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+ printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n",
+ cmd.buffer.block_data[9]);
+ printk(KERN_DEBUG "VC OHSize = 0x%0X\n",
+ cmd.buffer.block_data[10]);
+ printk(KERN_DEBUG "VC OVSize = 0x%0X\n",
+ cmd.buffer.block_data[11]);
+ printk(KERN_DEBUG "VC HCrop = 0x%0X\n",
+ cmd.buffer.block_data[12]);
+ printk(KERN_DEBUG "VC VCrop = 0x%0X\n",
+ cmd.buffer.block_data[13]);
+ printk(KERN_DEBUG "VC HPhase = 0x%0X\n",
+ cmd.buffer.block_data[14]);
+ printk(KERN_DEBUG "VC VPhase = 0x%0X\n",
+ cmd.buffer.block_data[15]);
+ printk(KERN_DEBUG "VC HIspan = 0x%0X\n",
+ cmd.buffer.block_data[16]);
+ printk(KERN_DEBUG "VC VIspan = 0x%0X\n",
+ cmd.buffer.block_data[17]);
+ printk(KERN_DEBUG "VC HiCrop = 0x%0X\n",
+ cmd.buffer.block_data[18]);
+ printk(KERN_DEBUG "VC ViCrop = 0x%0X\n",
+ cmd.buffer.block_data[19]);
+ printk(KERN_DEBUG "VC HiFract = 0x%0X\n",
+ cmd.buffer.block_data[20]);
+ printk(KERN_DEBUG "VC ViFract = 0x%0X\n",
+ cmd.buffer.block_data[21]);
+ printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n",
+ cmd.buffer.block_data[22]);
+ printk(KERN_DEBUG "VC Creep Per = 0x%0X\n",
+ cmd.buffer.block_data[23]);
+ printk(KERN_DEBUG "VC User Sq. = 0x%0X\n",
+ cmd.buffer.block_data[24]);
+ printk(KERN_DEBUG "VC Target KB = 0x%0X\n",
+ cmd.buffer.block_data[25]);
+
+ /*** VP ***/
+ cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
+ cmd.reg_count = 14;
+ cmd.start = 0;
+ cpia2_send_command(cam, &cmd);
+
+ printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Sys State = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP Dev Config = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+ printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n",
+ cmd.buffer.block_data[9]);
+ printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n",
+ cmd.buffer.block_data[10]);
+ printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n",
+ cmd.buffer.block_data[11]);
+ printk(KERN_DEBUG "VP RAM Data = 0x%0X\n",
+ cmd.buffer.block_data[12]);
+ printk(KERN_DEBUG "Do Call = 0x%0X\n",
+ cmd.buffer.block_data[13]);
+
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ cmd.reg_count = 9;
+ cmd.start = 0x0E;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
+ cmd.buffer.block_data[4]);
+ printk(KERN_DEBUG "VP White Bal = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP WB thresh = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+ printk(KERN_DEBUG "VP Exp Target = 0x%0X\n",
+ cmd.buffer.block_data[8]);
+
+ cmd.reg_count = 1;
+ cmd.start = 0x1B;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ } else {
+ cmd.reg_count = 8 ;
+ cmd.start = 0x0E;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
+ cmd.buffer.block_data[5]);
+ printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
+ cmd.buffer.block_data[6]);
+ printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
+ cmd.buffer.block_data[7]);
+
+ cmd.reg_count = 1;
+ cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n",
+ cmd.buffer.block_data[0]);
+
+ cmd.reg_count = 4;
+ cmd.start = 0x3A;
+ cpia2_send_command(cam, &cmd);
+ printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n",
+ cmd.buffer.block_data[0]);
+ printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n",
+ cmd.buffer.block_data[1]);
+ printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n",
+ cmd.buffer.block_data[2]);
+ printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n",
+ cmd.buffer.block_data[3]);
+ }
+#endif
+}
+
+/******************************************************************************
+ *
+ * reset_camera_struct
+ *
+ * Sets all values to the defaults
+ *****************************************************************************/
+static void reset_camera_struct(struct camera_data *cam)
+{
+ /***
+ * The following parameter values are the defaults from the register map.
+ ***/
+ cam->params.vp_params.lowlight_boost = 0;
+
+ /* FlickerModes */
+ cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER;
+
+ /* jpeg params */
+ cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
+ cam->params.compression.creep_period = 2;
+ cam->params.compression.user_squeeze = 20;
+ cam->params.compression.inhibit_htables = false;
+
+ /* gpio params */
+ cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */
+ cam->params.vp_params.gpio_data = 0;
+
+ /* Target kb params */
+ cam->params.vc_params.quality = 100;
+
+ /***
+ * Set Sensor FPS as fast as possible.
+ ***/
+ if(cam->params.pnp_id.device_type == DEVICE_STV_672) {
+ if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15;
+ else
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
+ } else {
+ cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
+ }
+
+ /***
+ * Set default video mode as large as possible :
+ * for vga sensor set to vga, for cif sensor set to CIF.
+ ***/
+ if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) {
+ cam->sensor_type = CPIA2_SENSOR_500;
+ cam->video_size = VIDEOSIZE_VGA;
+ cam->params.roi.width = STV_IMAGE_VGA_COLS;
+ cam->params.roi.height = STV_IMAGE_VGA_ROWS;
+ } else {
+ cam->sensor_type = CPIA2_SENSOR_410;
+ cam->video_size = VIDEOSIZE_CIF;
+ cam->params.roi.width = STV_IMAGE_CIF_COLS;
+ cam->params.roi.height = STV_IMAGE_CIF_ROWS;
+ }
+
+ cam->width = cam->params.roi.width;
+ cam->height = cam->params.roi.height;
+}
+
+/******************************************************************************
+ *
+ * cpia2_init_camera_struct
+ *
+ * Initializes camera struct, does not call reset to fill in defaults.
+ *****************************************************************************/
+struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf)
+{
+ struct camera_data *cam;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+
+ if (!cam) {
+ ERR("couldn't kmalloc cpia2 struct\n");
+ return NULL;
+ }
+
+ cam->v4l2_dev.release = cpia2_camera_release;
+ if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) {
+ v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n");
+ kfree(cam);
+ return NULL;
+ }
+
+ mutex_init(&cam->v4l2_lock);
+ init_waitqueue_head(&cam->wq_stream);
+
+ return cam;
+}
+
+/******************************************************************************
+ *
+ * cpia2_init_camera
+ *
+ * Initializes camera.
+ *****************************************************************************/
+int cpia2_init_camera(struct camera_data *cam)
+{
+ DBG("Start\n");
+
+ cam->mmapped = false;
+
+ /* Get sensor and asic types before reset. */
+ cpia2_set_high_power(cam);
+ cpia2_get_version_info(cam);
+ if (cam->params.version.asic_id != CPIA2_ASIC_672) {
+ ERR("Device IO error (asicID has incorrect value of 0x%X\n",
+ cam->params.version.asic_id);
+ return -ENODEV;
+ }
+
+ /* Set GPIO direction and data to a safe state. */
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
+ TRANSFER_WRITE, 0);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA,
+ TRANSFER_WRITE, 0);
+
+ /* resetting struct requires version info for sensor and asic types */
+ reset_camera_struct(cam);
+
+ cpia2_set_low_power(cam);
+
+ DBG("End\n");
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_allocate_buffers
+ *
+ *****************************************************************************/
+int cpia2_allocate_buffers(struct camera_data *cam)
+{
+ int i;
+
+ if(!cam->buffers) {
+ u32 size = cam->num_frames*sizeof(struct framebuf);
+ cam->buffers = kmalloc(size, GFP_KERNEL);
+ if(!cam->buffers) {
+ ERR("couldn't kmalloc frame buffer structures\n");
+ return -ENOMEM;
+ }
+ }
+
+ if(!cam->frame_buffer) {
+ cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames);
+ if (!cam->frame_buffer) {
+ ERR("couldn't vmalloc frame buffer data area\n");
+ kfree(cam->buffers);
+ cam->buffers = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ for(i=0; i<cam->num_frames-1; ++i) {
+ cam->buffers[i].next = &cam->buffers[i+1];
+ cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
+ cam->buffers[i].status = FRAME_EMPTY;
+ cam->buffers[i].length = 0;
+ cam->buffers[i].max_length = 0;
+ cam->buffers[i].num = i;
+ }
+ cam->buffers[i].next = cam->buffers;
+ cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
+ cam->buffers[i].status = FRAME_EMPTY;
+ cam->buffers[i].length = 0;
+ cam->buffers[i].max_length = 0;
+ cam->buffers[i].num = i;
+ cam->curbuff = cam->buffers;
+ cam->workbuff = cam->curbuff->next;
+ DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff,
+ cam->workbuff);
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_free_buffers
+ *
+ *****************************************************************************/
+void cpia2_free_buffers(struct camera_data *cam)
+{
+ if(cam->buffers) {
+ kfree(cam->buffers);
+ cam->buffers = NULL;
+ }
+ if(cam->frame_buffer) {
+ rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames);
+ cam->frame_buffer = NULL;
+ }
+}
+
+/******************************************************************************
+ *
+ * cpia2_read
+ *
+ *****************************************************************************/
+long cpia2_read(struct camera_data *cam,
+ char __user *buf, unsigned long count, int noblock)
+{
+ struct framebuf *frame;
+
+ if (!count)
+ return 0;
+
+ if (!buf) {
+ ERR("%s: buffer NULL\n",__func__);
+ return -EINVAL;
+ }
+
+ if (!cam) {
+ ERR("%s: Internal error, camera_data NULL!\n",__func__);
+ return -EINVAL;
+ }
+
+ if (!cam->streaming) {
+ /* Start streaming */
+ cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ }
+
+ /* Copy cam->curbuff in case it changes while we're processing */
+ frame = cam->curbuff;
+ if (noblock && frame->status != FRAME_READY) {
+ return -EAGAIN;
+ }
+
+ if (frame->status != FRAME_READY) {
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !video_is_registered(&cam->vdev) ||
+ (frame = cam->curbuff)->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if (!video_is_registered(&cam->vdev))
+ return 0;
+ }
+
+ /* copy data to user space */
+ if (frame->length > count)
+ return -EFAULT;
+ if (copy_to_user(buf, frame->data, frame->length))
+ return -EFAULT;
+
+ count = frame->length;
+
+ frame->status = FRAME_EMPTY;
+
+ return count;
+}
+
+/******************************************************************************
+ *
+ * cpia2_poll
+ *
+ *****************************************************************************/
+unsigned int cpia2_poll(struct camera_data *cam, struct file *filp,
+ poll_table *wait)
+{
+ unsigned int status = v4l2_ctrl_poll(filp, wait);
+
+ if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) &&
+ !cam->streaming) {
+ /* Start streaming */
+ cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ }
+
+ poll_wait(filp, &cam->wq_stream, wait);
+
+ if (cam->curbuff->status == FRAME_READY)
+ status |= POLLIN | POLLRDNORM;
+
+ return status;
+}
+
+/******************************************************************************
+ *
+ * cpia2_remap_buffer
+ *
+ *****************************************************************************/
+int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma)
+{
+ const char *adr = (const char *)vma->vm_start;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long start = (unsigned long) adr;
+ unsigned long page, pos;
+
+ DBG("mmap offset:%ld size:%ld\n", start_offset, size);
+
+ if (!video_is_registered(&cam->vdev))
+ return -ENODEV;
+
+ if (size > cam->frame_size*cam->num_frames ||
+ (start_offset % cam->frame_size) != 0 ||
+ (start_offset+size > cam->frame_size*cam->num_frames))
+ return -EINVAL;
+
+ pos = ((unsigned long) (cam->frame_buffer)) + start_offset;
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ cam->mmapped = true;
+ return 0;
+}
diff --git a/drivers/media/video/cpia2/cpia2_registers.h b/drivers/media/usb/cpia2/cpia2_registers.h
index 3bbec514a967..3bbec514a967 100644
--- a/drivers/media/video/cpia2/cpia2_registers.h
+++ b/drivers/media/usb/cpia2/cpia2_registers.h
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/usb/cpia2/cpia2_usb.c
index 95b5d6e7cdc4..95b5d6e7cdc4 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/usb/cpia2/cpia2_usb.c
diff --git a/drivers/media/usb/cpia2/cpia2_v4l.c b/drivers/media/usb/cpia2/cpia2_v4l.c
new file mode 100644
index 000000000000..aeb9d2275725
--- /dev/null
+++ b/drivers/media/usb/cpia2/cpia2_v4l.c
@@ -0,0 +1,1266 @@
+/****************************************************************************
+ *
+ * Filename: cpia2_v4l.c
+ *
+ * Copyright 2001, STMicrolectronics, Inc.
+ * Contact: steve.miller@st.com
+ * Copyright 2001,2005, Scott J. Bertin <scottbertin@yahoo.com>
+ *
+ * Description:
+ * This is a USB driver for CPia2 based video cameras.
+ * The infrastructure of this driver is based on the cpia usb driver by
+ * Jochen Scharrlach and Johannes Erdfeldt.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ *
+ * Stripped of 2.4 stuff ready for main kernel submit by
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>
+ ****************************************************************************/
+
+#define CPIA_VERSION "3.0.1"
+
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/videodev2.h>
+#include <linux/stringify.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+
+#include "cpia2.h"
+
+static int video_nr = -1;
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
+
+static int buffer_size = 68 * 1024;
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)");
+
+static int num_buffers = 3;
+module_param(num_buffers, int, 0);
+MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-"
+ __stringify(VIDEO_MAX_FRAME) ", default 3)");
+
+static int alternate = DEFAULT_ALT;
+module_param(alternate, int, 0);
+MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-"
+ __stringify(USBIF_ISO_6) ", default "
+ __stringify(DEFAULT_ALT) ")");
+
+static int flicker_mode;
+module_param(flicker_mode, int, 0);
+MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or "
+ __stringify(60) ", default 0)");
+
+MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
+MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
+MODULE_SUPPORTED_DEVICE("video");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CPIA_VERSION);
+
+#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
+#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000)
+
+/******************************************************************************
+ *
+ * cpia2_open
+ *
+ *****************************************************************************/
+static int cpia2_open(struct file *file)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int retval;
+
+ if (mutex_lock_interruptible(&cam->v4l2_lock))
+ return -ERESTARTSYS;
+ retval = v4l2_fh_open(file);
+ if (retval)
+ goto open_unlock;
+
+ if (v4l2_fh_is_singular_file(file)) {
+ if (cpia2_allocate_buffers(cam)) {
+ v4l2_fh_release(file);
+ retval = -ENOMEM;
+ goto open_unlock;
+ }
+
+ /* reset the camera */
+ if (cpia2_reset_camera(cam) < 0) {
+ v4l2_fh_release(file);
+ retval = -EIO;
+ goto open_unlock;
+ }
+
+ cam->APP_len = 0;
+ cam->COM_len = 0;
+ }
+
+ cpia2_dbg_dump_registers(cam);
+open_unlock:
+ mutex_unlock(&cam->v4l2_lock);
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * cpia2_close
+ *
+ *****************************************************************************/
+static int cpia2_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct camera_data *cam = video_get_drvdata(dev);
+
+ mutex_lock(&cam->v4l2_lock);
+ if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) {
+ cpia2_usb_stream_stop(cam);
+
+ /* save camera state for later open */
+ cpia2_save_camera_state(cam);
+
+ cpia2_set_low_power(cam);
+ cpia2_free_buffers(cam);
+ }
+
+ if (cam->stream_fh == file->private_data) {
+ cam->stream_fh = NULL;
+ cam->mmapped = 0;
+ }
+ mutex_unlock(&cam->v4l2_lock);
+ return v4l2_fh_release(file);
+}
+
+/******************************************************************************
+ *
+ * cpia2_v4l_read
+ *
+ *****************************************************************************/
+static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
+ loff_t *off)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int noblock = file->f_flags&O_NONBLOCK;
+ ssize_t ret;
+
+ if(!cam)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&cam->v4l2_lock))
+ return -ERESTARTSYS;
+ ret = cpia2_read(cam, buf, count, noblock);
+ mutex_unlock(&cam->v4l2_lock);
+ return ret;
+}
+
+
+/******************************************************************************
+ *
+ * cpia2_v4l_poll
+ *
+ *****************************************************************************/
+static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct camera_data *cam = video_drvdata(filp);
+ unsigned int res;
+
+ mutex_lock(&cam->v4l2_lock);
+ res = cpia2_poll(cam, filp, wait);
+ mutex_unlock(&cam->v4l2_lock);
+ return res;
+}
+
+
+static int sync(struct camera_data *cam, int frame_nr)
+{
+ struct framebuf *frame = &cam->buffers[frame_nr];
+
+ while (1) {
+ if (frame->status == FRAME_READY)
+ return 0;
+
+ if (!cam->streaming) {
+ frame->status = FRAME_READY;
+ frame->length = 0;
+ return 0;
+ }
+
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !cam->streaming ||
+ frame->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if (!video_is_registered(&cam->vdev))
+ return -ENOTTY;
+ }
+}
+
+/******************************************************************************
+ *
+ * ioctl_querycap
+ *
+ * V4L2 device capabilities
+ *
+ *****************************************************************************/
+
+static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ strcpy(vc->driver, "cpia2");
+
+ if (cam->params.pnp_id.product == 0x151)
+ strcpy(vc->card, "QX5 Microscope");
+ else
+ strcpy(vc->card, "CPiA2 Camera");
+ switch (cam->params.pnp_id.device_type) {
+ case DEVICE_STV_672:
+ strcat(vc->card, " (672/");
+ break;
+ case DEVICE_STV_676:
+ strcat(vc->card, " (676/");
+ break;
+ default:
+ strcat(vc->card, " (XXX/");
+ break;
+ }
+ switch (cam->params.version.sensor_flags) {
+ case CPIA2_VP_SENSOR_FLAGS_404:
+ strcat(vc->card, "404)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_407:
+ strcat(vc->card, "407)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_409:
+ strcat(vc->card, "409)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_410:
+ strcat(vc->card, "410)");
+ break;
+ case CPIA2_VP_SENSOR_FLAGS_500:
+ strcat(vc->card, "500)");
+ break;
+ default:
+ strcat(vc->card, "XXX)");
+ break;
+ }
+
+ if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
+ memset(vc->bus_info,0, sizeof(vc->bus_info));
+
+ vc->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ vc->capabilities = vc->device_caps |
+ V4L2_CAP_DEVICE_CAPS;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_input
+ *
+ * V4L2 input get/set/enumerate
+ *
+ *****************************************************************************/
+
+static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *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
+ *
+ * V4L2 format enumerate
+ *
+ *****************************************************************************/
+
+static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (index < 0 || index > 1)
+ return -EINVAL;
+
+ memset(f, 0, sizeof(*f));
+ f->index = index;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ switch(index) {
+ case 0:
+ strcpy(f->description, "MJPEG");
+ f->pixelformat = V4L2_PIX_FMT_MJPEG;
+ break;
+ case 1:
+ strcpy(f->description, "JPEG");
+ f->pixelformat = V4L2_PIX_FMT_JPEG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_try_fmt
+ *
+ * V4L2 format try
+ *
+ *****************************************************************************/
+
+static int cpia2_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = cam->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ f->fmt.pix.priv = 0;
+
+ switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) {
+ case VIDEOSIZE_VGA:
+ f->fmt.pix.width = 640;
+ f->fmt.pix.height = 480;
+ break;
+ case VIDEOSIZE_CIF:
+ f->fmt.pix.width = 352;
+ f->fmt.pix.height = 288;
+ break;
+ case VIDEOSIZE_QVGA:
+ f->fmt.pix.width = 320;
+ f->fmt.pix.height = 240;
+ break;
+ case VIDEOSIZE_288_216:
+ f->fmt.pix.width = 288;
+ f->fmt.pix.height = 216;
+ break;
+ case VIDEOSIZE_256_192:
+ f->fmt.pix.width = 256;
+ f->fmt.pix.height = 192;
+ break;
+ case VIDEOSIZE_224_168:
+ f->fmt.pix.width = 224;
+ f->fmt.pix.height = 168;
+ break;
+ case VIDEOSIZE_192_144:
+ f->fmt.pix.width = 192;
+ f->fmt.pix.height = 144;
+ break;
+ case VIDEOSIZE_QCIF:
+ default:
+ f->fmt.pix.width = 176;
+ f->fmt.pix.height = 144;
+ break;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_set_fmt
+ *
+ * V4L2 format set
+ *
+ *****************************************************************************/
+
+static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int err, frame;
+
+ err = cpia2_try_fmt_vid_cap(file, _fh, f);
+ if(err != 0)
+ return err;
+
+ cam->pixelformat = f->fmt.pix.pixelformat;
+
+ /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle
+ * the missing Huffman table properly. */
+ cam->params.compression.inhibit_htables = 0;
+ /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/
+
+ /* we set the video window to something smaller or equal to what
+ * is requested by the user???
+ */
+ DBG("Requested width = %d, height = %d\n",
+ f->fmt.pix.width, f->fmt.pix.height);
+ if (f->fmt.pix.width != cam->width ||
+ f->fmt.pix.height != cam->height) {
+ cam->width = f->fmt.pix.width;
+ cam->height = f->fmt.pix.height;
+ cam->params.roi.width = f->fmt.pix.width;
+ cam->params.roi.height = f->fmt.pix.height;
+ cpia2_set_format(cam);
+ }
+
+ for (frame = 0; frame < cam->num_frames; ++frame) {
+ if (cam->buffers[frame].status == FRAME_READING)
+ if ((err = sync(cam, frame)) < 0)
+ return err;
+
+ cam->buffers[frame].status = FRAME_EMPTY;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_get_fmt
+ *
+ * V4L2 format get
+ *
+ *****************************************************************************/
+
+static int cpia2_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ f->fmt.pix.width = cam->width;
+ f->fmt.pix.height = cam->height;
+ f->fmt.pix.pixelformat = cam->pixelformat;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = cam->frame_size;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
+ f->fmt.pix.priv = 0;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_cropcap
+ *
+ * V4L2 query cropping capabilities
+ * NOTE: cropping is currently disabled
+ *
+ *****************************************************************************/
+
+static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ c->bounds.left = 0;
+ c->bounds.top = 0;
+ c->bounds.width = cam->width;
+ c->bounds.height = cam->height;
+ c->defrect.left = 0;
+ c->defrect.top = 0;
+ c->defrect.width = cam->width;
+ c->defrect.height = cam->height;
+ c->pixelaspect.numerator = 1;
+ c->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+struct framerate_info {
+ int value;
+ struct v4l2_fract period;
+};
+
+static const struct framerate_info framerate_controls[] = {
+ { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } },
+ { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } },
+ { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } },
+ { CPIA2_VP_FRAMERATE_15, { 1, 15 } },
+ { CPIA2_VP_FRAMERATE_25, { 1, 25 } },
+ { CPIA2_VP_FRAMERATE_30, { 1, 30 } },
+};
+
+static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct v4l2_captureparm *cap = &p->parm.capture;
+ int i;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cap->capability = V4L2_CAP_TIMEPERFRAME;
+ cap->readbuffers = cam->num_frames;
+ for (i = 0; i < ARRAY_SIZE(framerate_controls); i++)
+ if (cam->params.vp_params.frame_rate == framerate_controls[i].value) {
+ cap->timeperframe = framerate_controls[i].period;
+ break;
+ }
+ return 0;
+}
+
+static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
+{
+ struct camera_data *cam = video_drvdata(file);
+ struct v4l2_captureparm *cap = &p->parm.capture;
+ struct v4l2_fract tpf = cap->timeperframe;
+ int max = ARRAY_SIZE(framerate_controls) - 1;
+ int ret;
+ int i;
+
+ ret = cpia2_g_parm(file, fh, p);
+ if (ret || !tpf.denominator || !tpf.numerator)
+ return ret;
+
+ /* Maximum 15 fps for this model */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ max -= 2;
+ for (i = 0; i <= max; i++) {
+ struct v4l2_fract f1 = tpf;
+ struct v4l2_fract f2 = framerate_controls[i].period;
+
+ f1.numerator *= f2.denominator;
+ f2.numerator *= f1.denominator;
+ if (f1.numerator >= f2.numerator)
+ break;
+ }
+ if (i > max)
+ i = max;
+ cap->timeperframe = framerate_controls[i].period;
+ return cpia2_set_fps(cam, framerate_controls[i].value);
+}
+
+static const struct {
+ u32 width;
+ u32 height;
+} cpia2_framesizes[] = {
+ { 640, 480 },
+ { 352, 288 },
+ { 320, 240 },
+ { 288, 216 },
+ { 256, 192 },
+ { 224, 168 },
+ { 192, 144 },
+ { 176, 144 },
+};
+
+static int cpia2_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+
+ if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG &&
+ fsize->pixel_format != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+ if (fsize->index >= ARRAY_SIZE(cpia2_framesizes))
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = cpia2_framesizes[fsize->index].width;
+ fsize->discrete.height = cpia2_framesizes[fsize->index].height;
+
+ return 0;
+}
+
+static int cpia2_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int max = ARRAY_SIZE(framerate_controls) - 1;
+ int i;
+
+ if (fival->pixel_format != V4L2_PIX_FMT_MJPEG &&
+ fival->pixel_format != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+
+ /* Maximum 15 fps for this model */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ max -= 2;
+ if (fival->index > max)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++)
+ if (fival->width == cpia2_framesizes[i].width &&
+ fival->height == cpia2_framesizes[i].height)
+ break;
+ if (i == ARRAY_SIZE(cpia2_framesizes))
+ return -EINVAL;
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = framerate_controls[fival->index].period;
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_s_ctrl
+ *
+ * V4L2 set the value of a control variable
+ *
+ *****************************************************************************/
+
+static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct camera_data *cam =
+ container_of(ctrl->handler, struct camera_data, hdl);
+ static const int flicker_table[] = {
+ NEVER_FLICKER,
+ FLICKER_50,
+ FLICKER_60,
+ };
+
+ DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cpia2_set_brightness(cam, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ cpia2_set_contrast(cam, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ cpia2_set_saturation(cam, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ cpia2_set_property_mirror(cam, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ cpia2_set_property_flip(cam, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]);
+ case V4L2_CID_ILLUMINATORS_1:
+ return cpia2_set_gpio(cam, (cam->top_light->val << 6) |
+ (cam->bottom_light->val << 7));
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
+ cam->params.compression.inhibit_htables =
+ !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ cam->params.vc_params.quality = ctrl->val;
+ break;
+ case CPIA2_CID_USB_ALT:
+ cam->params.camera_state.stream_mode = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_g_jpegcomp
+ *
+ * V4L2 get the JPEG compression parameters
+ *
+ *****************************************************************************/
+
+static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ memset(parms, 0, sizeof(*parms));
+
+ parms->quality = 80; // TODO: Can this be made meaningful?
+
+ parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI;
+ if(!cam->params.compression.inhibit_htables) {
+ parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT;
+ }
+
+ parms->APPn = cam->APPn;
+ parms->APP_len = cam->APP_len;
+ if(cam->APP_len > 0) {
+ memcpy(parms->APP_data, cam->APP_data, cam->APP_len);
+ parms->jpeg_markers |= V4L2_JPEG_MARKER_APP;
+ }
+
+ parms->COM_len = cam->COM_len;
+ if(cam->COM_len > 0) {
+ memcpy(parms->COM_data, cam->COM_data, cam->COM_len);
+ parms->jpeg_markers |= JPEG_MARKER_COM;
+ }
+
+ DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n",
+ parms->APP_len, parms->COM_len);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_s_jpegcomp
+ *
+ * V4L2 set the JPEG compression parameters
+ * NOTE: quality and some jpeg_markers are ignored.
+ *
+ *****************************************************************************/
+
+static int cpia2_s_jpegcomp(struct file *file, void *fh,
+ const struct v4l2_jpegcompression *parms)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n",
+ parms->APP_len, parms->COM_len);
+
+ cam->params.compression.inhibit_htables =
+ !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT);
+
+ if(parms->APP_len != 0) {
+ if(parms->APP_len > 0 &&
+ parms->APP_len <= sizeof(cam->APP_data) &&
+ parms->APPn >= 0 && parms->APPn <= 15) {
+ cam->APPn = parms->APPn;
+ cam->APP_len = parms->APP_len;
+ memcpy(cam->APP_data, parms->APP_data, parms->APP_len);
+ } else {
+ LOG("Bad APPn Params n=%d len=%d\n",
+ parms->APPn, parms->APP_len);
+ return -EINVAL;
+ }
+ } else {
+ cam->APP_len = 0;
+ }
+
+ if(parms->COM_len != 0) {
+ if(parms->COM_len > 0 &&
+ parms->COM_len <= sizeof(cam->COM_data)) {
+ cam->COM_len = parms->COM_len;
+ memcpy(cam->COM_data, parms->COM_data, parms->COM_len);
+ } else {
+ LOG("Bad COM_len=%d\n", parms->COM_len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_reqbufs
+ *
+ * V4L2 Initiate memory mapping.
+ * NOTE: The user's request is ignored. For now the buffers are fixed.
+ *
+ *****************************************************************************/
+
+static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames);
+ req->count = cam->num_frames;
+ memset(&req->reserved, 0, sizeof(req->reserved));
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_querybuf
+ *
+ * V4L2 Query memory buffer status.
+ *
+ *****************************************************************************/
+
+static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->index > cam->num_frames)
+ return -EINVAL;
+
+ buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
+ buf->length = cam->frame_size;
+
+ buf->memory = V4L2_MEMORY_MMAP;
+
+ if(cam->mmapped)
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+ else
+ buf->flags = 0;
+
+ switch (cam->buffers[buf->index].status) {
+ case FRAME_EMPTY:
+ case FRAME_ERROR:
+ case FRAME_READING:
+ buf->bytesused = 0;
+ buf->flags = V4L2_BUF_FLAG_QUEUED;
+ break;
+ case FRAME_READY:
+ buf->bytesused = cam->buffers[buf->index].length;
+ buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->sequence = cam->buffers[buf->index].seq;
+ buf->flags = V4L2_BUF_FLAG_DONE;
+ break;
+ }
+
+ DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n",
+ buf->index, buf->m.offset, buf->flags, buf->sequence,
+ buf->bytesused);
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * ioctl_qbuf
+ *
+ * V4L2 User is freeing buffer
+ *
+ *****************************************************************************/
+
+static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->memory != V4L2_MEMORY_MMAP ||
+ buf->index > cam->num_frames)
+ return -EINVAL;
+
+ DBG("QBUF #%d\n", buf->index);
+
+ if(cam->buffers[buf->index].status == FRAME_READY)
+ cam->buffers[buf->index].status = FRAME_EMPTY;
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * find_earliest_filled_buffer
+ *
+ * Helper for ioctl_dqbuf. Find the next ready buffer.
+ *
+ *****************************************************************************/
+
+static int find_earliest_filled_buffer(struct camera_data *cam)
+{
+ int i;
+ int found = -1;
+ for (i=0; i<cam->num_frames; i++) {
+ if(cam->buffers[i].status == FRAME_READY) {
+ if(found < 0) {
+ found = i;
+ } else {
+ /* find which buffer is earlier */
+ struct timeval *tv1, *tv2;
+ tv1 = &cam->buffers[i].timestamp;
+ tv2 = &cam->buffers[found].timestamp;
+ if(tv1->tv_sec < tv2->tv_sec ||
+ (tv1->tv_sec == tv2->tv_sec &&
+ tv1->tv_usec < tv2->tv_usec))
+ found = i;
+ }
+ }
+ }
+ return found;
+}
+
+/******************************************************************************
+ *
+ * ioctl_dqbuf
+ *
+ * V4L2 User is asking for a filled buffer.
+ *
+ *****************************************************************************/
+
+static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int frame;
+
+ if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ frame = find_earliest_filled_buffer(cam);
+
+ if(frame < 0 && file->f_flags&O_NONBLOCK)
+ return -EAGAIN;
+
+ if(frame < 0) {
+ /* Wait for a frame to become available */
+ struct framebuf *cb=cam->curbuff;
+ mutex_unlock(&cam->v4l2_lock);
+ wait_event_interruptible(cam->wq_stream,
+ !video_is_registered(&cam->vdev) ||
+ (cb=cam->curbuff)->status == FRAME_READY);
+ mutex_lock(&cam->v4l2_lock);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ if (!video_is_registered(&cam->vdev))
+ return -ENOTTY;
+ frame = cb->num;
+ }
+
+
+ buf->index = frame;
+ buf->bytesused = cam->buffers[buf->index].length;
+ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+ buf->field = V4L2_FIELD_NONE;
+ buf->timestamp = cam->buffers[buf->index].timestamp;
+ buf->sequence = cam->buffers[buf->index].seq;
+ buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
+ buf->length = cam->frame_size;
+ buf->reserved2 = 0;
+ buf->reserved = 0;
+ memset(&buf->timecode, 0, sizeof(buf->timecode));
+
+ DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index,
+ cam->buffers[buf->index].status, buf->sequence, buf->bytesused);
+
+ return 0;
+}
+
+static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int ret = -EINVAL;
+
+ DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (!cam->streaming) {
+ ret = cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ if (!ret)
+ v4l2_ctrl_grab(cam->usb_alt, true);
+ }
+ return ret;
+}
+
+static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int ret = -EINVAL;
+
+ DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
+ if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (cam->streaming) {
+ ret = cpia2_usb_stream_stop(cam);
+ if (!ret)
+ v4l2_ctrl_grab(cam->usb_alt, false);
+ }
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * cpia2_mmap
+ *
+ *****************************************************************************/
+static int cpia2_mmap(struct file *file, struct vm_area_struct *area)
+{
+ struct camera_data *cam = video_drvdata(file);
+ int retval;
+
+ if (mutex_lock_interruptible(&cam->v4l2_lock))
+ return -ERESTARTSYS;
+ retval = cpia2_remap_buffer(cam, area);
+
+ if(!retval)
+ cam->stream_fh = file->private_data;
+ mutex_unlock(&cam->v4l2_lock);
+ return retval;
+}
+
+/******************************************************************************
+ *
+ * reset_camera_struct_v4l
+ *
+ * Sets all values to the defaults
+ *****************************************************************************/
+static void reset_camera_struct_v4l(struct camera_data *cam)
+{
+ cam->width = cam->params.roi.width;
+ cam->height = cam->params.roi.height;
+
+ cam->frame_size = buffer_size;
+ cam->num_frames = num_buffers;
+
+ /* Flicker modes */
+ cam->params.flicker_control.flicker_mode_req = flicker_mode;
+
+ /* stream modes */
+ cam->params.camera_state.stream_mode = alternate;
+
+ cam->pixelformat = V4L2_PIX_FMT_JPEG;
+}
+
+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_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_s_parm = cpia2_s_parm,
+ .vidioc_g_parm = cpia2_g_parm,
+ .vidioc_enum_framesizes = cpia2_enum_framesizes,
+ .vidioc_enum_frameintervals = cpia2_enum_frameintervals,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/***
+ * The v4l video device structure initialized for this device
+ ***/
+static const struct v4l2_file_operations cpia2_fops = {
+ .owner = THIS_MODULE,
+ .open = cpia2_open,
+ .release = cpia2_close,
+ .read = cpia2_v4l_read,
+ .poll = cpia2_v4l_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = cpia2_mmap,
+};
+
+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_empty,
+};
+
+void cpia2_camera_release(struct v4l2_device *v4l2_dev)
+{
+ struct camera_data *cam =
+ container_of(v4l2_dev, struct camera_data, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&cam->hdl);
+ v4l2_device_unregister(&cam->v4l2_dev);
+ kfree(cam);
+}
+
+static const struct v4l2_ctrl_ops cpia2_ctrl_ops = {
+ .s_ctrl = cpia2_s_ctrl,
+};
+
+/******************************************************************************
+ *
+ * cpia2_register_camera
+ *
+ *****************************************************************************/
+int cpia2_register_camera(struct camera_data *cam)
+{
+ struct v4l2_ctrl_handler *hdl = &cam->hdl;
+ struct v4l2_ctrl_config cpia2_usb_alt = {
+ .ops = &cpia2_ctrl_ops,
+ .id = CPIA2_CID_USB_ALT,
+ .name = "USB Alternate",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = USBIF_ISO_1,
+ .max = USBIF_ISO_6,
+ .step = 1,
+ };
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 12);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_BRIGHTNESS,
+ cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0,
+ 255, 1, DEFAULT_BRIGHTNESS);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_JPEG_ACTIVE_MARKER, 0,
+ V4L2_JPEG_ACTIVE_MARKER_DHT, 0,
+ V4L2_JPEG_ACTIVE_MARKER_DHT);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 1,
+ 100, 1, 100);
+ cpia2_usb_alt.def = alternate;
+ cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL);
+ /* VP5 Only */
+ if (cam->params.pnp_id.device_type != DEVICE_STV_672)
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ /* Flicker control only valid for 672 */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+ /* Light control only valid for the QX5 Microscope */
+ if (cam->params.pnp_id.product == 0x151) {
+ cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
+ cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &cam->top_light);
+ }
+
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ cam->vdev = cpia2_template;
+ video_set_drvdata(&cam->vdev, cam);
+ cam->vdev.lock = &cam->v4l2_lock;
+ cam->vdev.ctrl_handler = hdl;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
+
+ reset_camera_struct_v4l(cam);
+
+ /* register v4l device */
+ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ ERR("video_register_device failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * cpia2_unregister_camera
+ *
+ *****************************************************************************/
+void cpia2_unregister_camera(struct camera_data *cam)
+{
+ video_unregister_device(&cam->vdev);
+}
+
+/******************************************************************************
+ *
+ * check_parameters
+ *
+ * Make sure that all user-supplied parameters are sensible
+ *****************************************************************************/
+static void __init check_parameters(void)
+{
+ if(buffer_size < PAGE_SIZE) {
+ buffer_size = PAGE_SIZE;
+ LOG("buffer_size too small, setting to %d\n", buffer_size);
+ } else if(buffer_size > 1024*1024) {
+ /* arbitrary upper limiit */
+ buffer_size = 1024*1024;
+ LOG("buffer_size ridiculously large, setting to %d\n",
+ buffer_size);
+ } else {
+ buffer_size += PAGE_SIZE-1;
+ buffer_size &= ~(PAGE_SIZE-1);
+ }
+
+ if(num_buffers < 1) {
+ num_buffers = 1;
+ LOG("num_buffers too small, setting to %d\n", num_buffers);
+ } else if(num_buffers > VIDEO_MAX_FRAME) {
+ num_buffers = VIDEO_MAX_FRAME;
+ LOG("num_buffers too large, setting to %d\n", num_buffers);
+ }
+
+ if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) {
+ alternate = DEFAULT_ALT;
+ LOG("alternate specified is invalid, using %d\n", alternate);
+ }
+
+ if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) {
+ flicker_mode = 0;
+ LOG("Flicker mode specified is invalid, using %d\n",
+ flicker_mode);
+ }
+
+ DBG("Using %d buffers, each %d bytes, alternate=%d\n",
+ num_buffers, buffer_size, alternate);
+}
+
+/************ Module Stuff ***************/
+
+
+/******************************************************************************
+ *
+ * cpia2_init/module_init
+ *
+ *****************************************************************************/
+static int __init cpia2_init(void)
+{
+ LOG("%s v%s\n",
+ ABOUT, CPIA_VERSION);
+ check_parameters();
+ cpia2_usb_init();
+ return 0;
+}
+
+
+/******************************************************************************
+ *
+ * cpia2_exit/module_exit
+ *
+ *****************************************************************************/
+static void __exit cpia2_exit(void)
+{
+ cpia2_usb_cleanup();
+ schedule_timeout(2 * HZ);
+}
+
+module_init(cpia2_init);
+module_exit(cpia2_exit);
diff --git a/drivers/media/usb/cx231xx/Kconfig b/drivers/media/usb/cx231xx/Kconfig
new file mode 100644
index 000000000000..86feeeaf61c2
--- /dev/null
+++ b/drivers/media/usb/cx231xx/Kconfig
@@ -0,0 +1,51 @@
+config VIDEO_CX231XX
+ tristate "Conexant cx231xx USB video capture support"
+ depends on VIDEO_DEV && I2C
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ depends on RC_CORE
+ select VIDEOBUF_VMALLOC
+ select VIDEO_CX25840
+ select VIDEO_CX2341X
+
+ ---help---
+ This is a video4linux driver for Conexant 231xx USB based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx231xx
+
+config VIDEO_CX231XX_RC
+ bool "Conexant cx231xx Remote Controller additional support"
+ depends on RC_CORE
+ depends on VIDEO_CX231XX
+ default y
+ ---help---
+ cx231xx hardware has a builtin RX/TX support. However, a few
+ designs opted to not use it, but, instead, some other hardware.
+ This module enables the usage of those other hardware, like the
+ ones used with ISDB-T boards.
+
+ On most cases, all you need for IR is mceusb module.
+
+config VIDEO_CX231XX_ALSA
+ tristate "Conexant Cx231xx ALSA audio module"
+ depends on VIDEO_CX231XX && SND
+ select SND_PCM
+
+ ---help---
+ This is an ALSA driver for Cx231xx USB based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx231xx-alsa
+
+config VIDEO_CX231XX_DVB
+ tristate "DVB/ATSC Support for Cx231xx based TV cards"
+ depends on VIDEO_CX231XX && DVB_CORE
+ select VIDEOBUF_DVB
+ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
+
+ ---help---
+ This adds support for DVB cards based on the
+ Conexant cx231xx chips.
diff --git a/drivers/media/usb/cx231xx/Makefile b/drivers/media/usb/cx231xx/Makefile
new file mode 100644
index 000000000000..52cf76935e69
--- /dev/null
+++ b/drivers/media/usb/cx231xx/Makefile
@@ -0,0 +1,15 @@
+cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o
+cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
+cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o
+
+cx231xx-alsa-objs := cx231xx-audio.o
+
+obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
+obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
+obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/usb/dvb-usb
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
new file mode 100644
index 000000000000..b024e5197a75
--- /dev/null
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -0,0 +1,2197 @@
+/*
+ *
+ * Support for a cx23417 mpeg encoder via cx231xx host port.
+ *
+ * (c) 2004 Jelle Foks <jelle@foks.us>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
+ * (c) 2008 Steven Toth <stoth@linuxtv.org>
+ * - CX23885/7/8 support
+ *
+ * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/),
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cx2341x.h>
+#include <linux/usb.h>
+
+#include "cx231xx.h"
+/*#include "cx23885-ioctl.h"*/
+
+#define CX231xx_FIRM_IMAGE_SIZE 376836
+#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
+
+/* for polaris ITVC */
+#define ITVC_WRITE_DIR 0x03FDFC00
+#define ITVC_READ_DIR 0x0001FC00
+
+#define MCI_MEMORY_DATA_BYTE0 0x00
+#define MCI_MEMORY_DATA_BYTE1 0x08
+#define MCI_MEMORY_DATA_BYTE2 0x10
+#define MCI_MEMORY_DATA_BYTE3 0x18
+
+#define MCI_MEMORY_ADDRESS_BYTE2 0x20
+#define MCI_MEMORY_ADDRESS_BYTE1 0x28
+#define MCI_MEMORY_ADDRESS_BYTE0 0x30
+
+#define MCI_REGISTER_DATA_BYTE0 0x40
+#define MCI_REGISTER_DATA_BYTE1 0x48
+#define MCI_REGISTER_DATA_BYTE2 0x50
+#define MCI_REGISTER_DATA_BYTE3 0x58
+
+#define MCI_REGISTER_ADDRESS_BYTE0 0x60
+#define MCI_REGISTER_ADDRESS_BYTE1 0x68
+
+#define MCI_REGISTER_MODE 0x70
+
+/* Read and write modes for polaris ITVC */
+#define MCI_MODE_REGISTER_READ 0x000
+#define MCI_MODE_REGISTER_WRITE 0x100
+#define MCI_MODE_MEMORY_READ 0x000
+#define MCI_MODE_MEMORY_WRITE 0x4000
+
+static unsigned int mpegbufs = 8;
+module_param(mpegbufs, int, 0644);
+MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
+static unsigned int mpeglines = 128;
+module_param(mpeglines, int, 0644);
+MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
+static unsigned int mpeglinesize = 512;
+module_param(mpeglinesize, int, 0644);
+MODULE_PARM_DESC(mpeglinesize,
+ "number of bytes in each line of an MPEG buffer, range 512-1024");
+
+static unsigned int v4l_debug = 1;
+module_param(v4l_debug, int, 0644);
+MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
+struct cx231xx_dmaqueue *dma_qq;
+#define dprintk(level, fmt, arg...)\
+ do { if (v4l_debug >= level) \
+ printk(KERN_INFO "%s: " fmt, \
+ (dev) ? dev->name : "cx231xx[?]", ## arg); \
+ } while (0)
+
+static struct cx231xx_tvnorm cx231xx_tvnorms[] = {
+ {
+ .name = "NTSC-M",
+ .id = V4L2_STD_NTSC_M,
+ }, {
+ .name = "NTSC-JP",
+ .id = V4L2_STD_NTSC_M_JP,
+ }, {
+ .name = "PAL-BG",
+ .id = V4L2_STD_PAL_BG,
+ }, {
+ .name = "PAL-DK",
+ .id = V4L2_STD_PAL_DK,
+ }, {
+ .name = "PAL-I",
+ .id = V4L2_STD_PAL_I,
+ }, {
+ .name = "PAL-M",
+ .id = V4L2_STD_PAL_M,
+ }, {
+ .name = "PAL-N",
+ .id = V4L2_STD_PAL_N,
+ }, {
+ .name = "PAL-Nc",
+ .id = V4L2_STD_PAL_Nc,
+ }, {
+ .name = "PAL-60",
+ .id = V4L2_STD_PAL_60,
+ }, {
+ .name = "SECAM-L",
+ .id = V4L2_STD_SECAM_L,
+ }, {
+ .name = "SECAM-DK",
+ .id = V4L2_STD_SECAM_DK,
+ }
+};
+
+/* ------------------------------------------------------------------ */
+enum cx231xx_capture_type {
+ CX231xx_MPEG_CAPTURE,
+ CX231xx_RAW_CAPTURE,
+ CX231xx_RAW_PASSTHRU_CAPTURE
+};
+enum cx231xx_capture_bits {
+ CX231xx_RAW_BITS_NONE = 0x00,
+ CX231xx_RAW_BITS_YUV_CAPTURE = 0x01,
+ CX231xx_RAW_BITS_PCM_CAPTURE = 0x02,
+ CX231xx_RAW_BITS_VBI_CAPTURE = 0x04,
+ CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
+ CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10
+};
+enum cx231xx_capture_end {
+ CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */
+ CX231xx_END_NOW, /* stop immediately, no irq */
+};
+enum cx231xx_framerate {
+ CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */
+ CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */
+};
+enum cx231xx_stream_port {
+ CX231xx_OUTPUT_PORT_MEMORY,
+ CX231xx_OUTPUT_PORT_STREAMING,
+ CX231xx_OUTPUT_PORT_SERIAL
+};
+enum cx231xx_data_xfer_status {
+ CX231xx_MORE_BUFFERS_FOLLOW,
+ CX231xx_LAST_BUFFER,
+};
+enum cx231xx_picture_mask {
+ CX231xx_PICTURE_MASK_NONE,
+ CX231xx_PICTURE_MASK_I_FRAMES,
+ CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3,
+ CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7,
+};
+enum cx231xx_vbi_mode_bits {
+ CX231xx_VBI_BITS_SLICED,
+ CX231xx_VBI_BITS_RAW,
+};
+enum cx231xx_vbi_insertion_bits {
+ CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
+ CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
+ CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
+};
+enum cx231xx_dma_unit {
+ CX231xx_DMA_BYTES,
+ CX231xx_DMA_FRAMES,
+};
+enum cx231xx_dma_transfer_status_bits {
+ CX231xx_DMA_TRANSFER_BITS_DONE = 0x01,
+ CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04,
+ CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
+};
+enum cx231xx_pause {
+ CX231xx_PAUSE_ENCODING,
+ CX231xx_RESUME_ENCODING,
+};
+enum cx231xx_copyright {
+ CX231xx_COPYRIGHT_OFF,
+ CX231xx_COPYRIGHT_ON,
+};
+enum cx231xx_notification_type {
+ CX231xx_NOTIFICATION_REFRESH,
+};
+enum cx231xx_notification_status {
+ CX231xx_NOTIFICATION_OFF,
+ CX231xx_NOTIFICATION_ON,
+};
+enum cx231xx_notification_mailbox {
+ CX231xx_NOTIFICATION_NO_MAILBOX = -1,
+};
+enum cx231xx_field1_lines {
+ CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */
+ CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */
+ CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */
+};
+enum cx231xx_field2_lines {
+ CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */
+ CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */
+ CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */
+};
+enum cx231xx_custom_data_type {
+ CX231xx_CUSTOM_EXTENSION_USR_DATA,
+ CX231xx_CUSTOM_PRIVATE_PACKET,
+};
+enum cx231xx_mute {
+ CX231xx_UNMUTE,
+ CX231xx_MUTE,
+};
+enum cx231xx_mute_video_mask {
+ CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00,
+ CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000,
+ CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000,
+};
+enum cx231xx_mute_video_shift {
+ CX231xx_MUTE_VIDEO_V_SHIFT = 8,
+ CX231xx_MUTE_VIDEO_U_SHIFT = 16,
+ CX231xx_MUTE_VIDEO_Y_SHIFT = 24,
+};
+
+/* defines below are from ivtv-driver.h */
+#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
+
+/* Firmware API commands */
+#define IVTV_API_STD_TIMEOUT 500
+
+/* Registers */
+/* IVTV_REG_OFFSET */
+#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
+#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
+#define IVTV_REG_SPU (0x9050)
+#define IVTV_REG_HW_BLOCKS (0x9054)
+#define IVTV_REG_VPU (0x9058)
+#define IVTV_REG_APU (0xA064)
+
+/*
+ * Bit definitions for MC417_RWD and MC417_OEN registers
+ *
+ * bits 31-16
+ *+-----------+
+ *| Reserved |
+ *|+-----------+
+ *| bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *| bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
+ *|+-------+-------+-------+-------+-------+-------+-------+-------+
+ */
+#define MC417_MIWR 0x8000
+#define MC417_MIRD 0x4000
+#define MC417_MICS 0x2000
+#define MC417_MIRDY 0x1000
+#define MC417_MIADDR 0x0F00
+#define MC417_MIDATA 0x00FF
+
+
+/* Bit definitions for MC417_CTL register ****
+ *bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
+ *+--------+-------------+--------+--------------+------------+
+ *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
+ *+--------+-------------+--------+--------------+------------+
+ */
+#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
+#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
+#define MC417_UART_GPIO_EN 0x00000001
+
+/* Values for speed control */
+#define MC417_SPD_CTL_SLOW 0x1
+#define MC417_SPD_CTL_MEDIUM 0x0
+#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
+
+/* Values for GPIO select */
+#define MC417_GPIO_SEL_GPIO3 0x3
+#define MC417_GPIO_SEL_GPIO2 0x2
+#define MC417_GPIO_SEL_GPIO1 0x1
+#define MC417_GPIO_SEL_GPIO0 0x0
+
+
+#define CX23417_GPIO_MASK 0xFC0003FF
+static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value)
+{
+ int status = 0;
+ u32 _gpio_direction = 0;
+
+ _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+ _gpio_direction = _gpio_direction|gpio_direction;
+ status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+ (u8 *)&value, 4, 0, 0);
+ return status;
+}
+static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue)
+{
+ int status = 0;
+ u32 _gpio_direction = 0;
+
+ _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
+ _gpio_direction = _gpio_direction|gpio_direction;
+
+ status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
+ (u8 *)pValue, 4, 0, 1);
+ return status;
+}
+
+static int waitForMciComplete(struct cx231xx *dev)
+{
+ u32 gpio;
+ u32 gpio_driection = 0;
+ u8 count = 0;
+ getITVCReg(dev, gpio_driection, &gpio);
+
+ while (!(gpio&0x020000)) {
+ msleep(10);
+
+ getITVCReg(dev, gpio_driection, &gpio);
+
+ if (count++ > 100) {
+ dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value)
+{
+ u32 temp;
+ int status = 0;
+
+ temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8);
+ temp = temp<<10;
+ status = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (status < 0)
+ return status;
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 1;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 0;*/
+ temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1;*/
+ temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*Write that the mode is write.*/
+ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE;
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ return waitForMciComplete(dev);
+}
+
+static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value)
+{
+ /*write address byte 0;*/
+ u32 temp;
+ u32 return_value = 0;
+ int ret = 0;
+
+ temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1;*/
+ temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write that the mode is read;*/
+ temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ;
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*wait for the MIRDY line to be asserted ,
+ signalling that the read is done;*/
+ ret = waitForMciComplete(dev);
+
+ /*switch the DATA- GPIO to input mode;*/
+
+ /*Read data byte 0;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) >> 18);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /* Read data byte 1;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+
+ return_value |= ((temp & 0x03FC0000) >> 10);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /*Read data byte 2;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) >> 2);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ /*Read data byte 3;*/
+ temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp & 0x03FC0000) << 6);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
+
+ *value = return_value;
+
+
+ return ret;
+}
+
+static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value)
+{
+ /*write data byte 0;*/
+
+ u32 temp;
+ int ret = 0;
+
+ temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8);
+ temp = temp << 10;
+ ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (ret < 0)
+ return ret;
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 1;*/
+ temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00);
+ temp = temp << 10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp | ((0x05) << 10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 1;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /* write address byte 0;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*wait for MIRDY line;*/
+ waitForMciComplete(dev);
+
+ return 0;
+}
+
+static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value)
+{
+ u32 temp = 0;
+ u32 return_value = 0;
+ int ret = 0;
+
+ /*write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ if (ret < 0)
+ return ret;
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 1*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*write address byte 0*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8);
+ temp = temp<<10;
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+ temp = temp|((0x05)<<10);
+ setITVCReg(dev, ITVC_WRITE_DIR, temp);
+
+ /*Wait for MIRDY line*/
+ ret = waitForMciComplete(dev);
+
+
+ /*Read data byte 3;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)<<6);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /*Read data byte 2;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>2);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /* Read data byte 1;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>10);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ /*Read data byte 0;*/
+ temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10;
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10);
+ setITVCReg(dev, ITVC_READ_DIR, temp);
+ getITVCReg(dev, ITVC_READ_DIR, &temp);
+ return_value |= ((temp&0x03FC0000)>>18);
+ setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
+
+ *value = return_value;
+ return ret;
+}
+
+/* ------------------------------------------------------------------ */
+
+/* MPEG encoder API */
+static char *cmd_to_str(int cmd)
+{
+ switch (cmd) {
+ case CX2341X_ENC_PING_FW:
+ return "PING_FW";
+ case CX2341X_ENC_START_CAPTURE:
+ return "START_CAPTURE";
+ case CX2341X_ENC_STOP_CAPTURE:
+ return "STOP_CAPTURE";
+ case CX2341X_ENC_SET_AUDIO_ID:
+ return "SET_AUDIO_ID";
+ case CX2341X_ENC_SET_VIDEO_ID:
+ return "SET_VIDEO_ID";
+ case CX2341X_ENC_SET_PCR_ID:
+ return "SET_PCR_PID";
+ case CX2341X_ENC_SET_FRAME_RATE:
+ return "SET_FRAME_RATE";
+ case CX2341X_ENC_SET_FRAME_SIZE:
+ return "SET_FRAME_SIZE";
+ case CX2341X_ENC_SET_BIT_RATE:
+ return "SET_BIT_RATE";
+ case CX2341X_ENC_SET_GOP_PROPERTIES:
+ return "SET_GOP_PROPERTIES";
+ case CX2341X_ENC_SET_ASPECT_RATIO:
+ return "SET_ASPECT_RATIO";
+ case CX2341X_ENC_SET_DNR_FILTER_MODE:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_DNR_FILTER_PROPS:
+ return "SET_DNR_FILTER_PROPS";
+ case CX2341X_ENC_SET_CORING_LEVELS:
+ return "SET_CORING_LEVELS";
+ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
+ return "SET_SPATIAL_FILTER_TYPE";
+ case CX2341X_ENC_SET_VBI_LINE:
+ return "SET_VBI_LINE";
+ case CX2341X_ENC_SET_STREAM_TYPE:
+ return "SET_STREAM_TYPE";
+ case CX2341X_ENC_SET_OUTPUT_PORT:
+ return "SET_OUTPUT_PORT";
+ case CX2341X_ENC_SET_AUDIO_PROPERTIES:
+ return "SET_AUDIO_PROPERTIES";
+ case CX2341X_ENC_HALT_FW:
+ return "HALT_FW";
+ case CX2341X_ENC_GET_VERSION:
+ return "GET_VERSION";
+ case CX2341X_ENC_SET_GOP_CLOSURE:
+ return "SET_GOP_CLOSURE";
+ case CX2341X_ENC_GET_SEQ_END:
+ return "GET_SEQ_END";
+ case CX2341X_ENC_SET_PGM_INDEX_INFO:
+ return "SET_PGM_INDEX_INFO";
+ case CX2341X_ENC_SET_VBI_CONFIG:
+ return "SET_VBI_CONFIG";
+ case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
+ return "SET_DMA_BLOCK_SIZE";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
+ return "GET_PREV_DMA_INFO_MB_10";
+ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
+ return "GET_PREV_DMA_INFO_MB_9";
+ case CX2341X_ENC_SCHED_DMA_TO_HOST:
+ return "SCHED_DMA_TO_HOST";
+ case CX2341X_ENC_INITIALIZE_INPUT:
+ return "INITIALIZE_INPUT";
+ case CX2341X_ENC_SET_FRAME_DROP_RATE:
+ return "SET_FRAME_DROP_RATE";
+ case CX2341X_ENC_PAUSE_ENCODER:
+ return "PAUSE_ENCODER";
+ case CX2341X_ENC_REFRESH_INPUT:
+ return "REFRESH_INPUT";
+ case CX2341X_ENC_SET_COPYRIGHT:
+ return "SET_COPYRIGHT";
+ case CX2341X_ENC_SET_EVENT_NOTIFICATION:
+ return "SET_EVENT_NOTIFICATION";
+ case CX2341X_ENC_SET_NUM_VSYNC_LINES:
+ return "SET_NUM_VSYNC_LINES";
+ case CX2341X_ENC_SET_PLACEHOLDER:
+ return "SET_PLACEHOLDER";
+ case CX2341X_ENC_MUTE_VIDEO:
+ return "MUTE_VIDEO";
+ case CX2341X_ENC_MUTE_AUDIO:
+ return "MUTE_AUDIO";
+ case CX2341X_ENC_MISC:
+ return "MISC";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int cx231xx_mbox_func(void *priv,
+ u32 command,
+ int in,
+ int out,
+ u32 data[CX2341X_MBOX_MAX_DATA])
+{
+ struct cx231xx *dev = priv;
+ unsigned long timeout;
+ u32 value, flag, retval = 0;
+ int i;
+
+ dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
+ cmd_to_str(command));
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
+ if (value != 0x12345678) {
+ dprintk(3,
+ "Firmware and/or mailbox pointer not initialized "
+ "or corrupted, signature = 0x%x, cmd = %s\n", value,
+ cmd_to_str(command));
+ return -1;
+ }
+
+ /* This read looks at 32 bits, but flag is only 8 bits.
+ * Seems we also bail if CMD or TIMEOUT bytes are set???
+ */
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (flag) {
+ dprintk(3, "ERROR: Mailbox appears to be in use "
+ "(%x), cmd = %s\n", flag, cmd_to_str(command));
+ return -1;
+ }
+
+ flag |= 1; /* tell 'em we're working on it */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ /* command code */
+ mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
+ mc417_memory_write(dev, dev->cx23417_mailbox + 3,
+ IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < in; i++) {
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
+ dprintk(3, "API Input %d = %d\n", i, data[i]);
+ }
+ for (; i < CX2341X_MBOX_MAX_DATA; i++)
+ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
+ if (0 != (flag & 4))
+ break;
+ if (time_after(jiffies, timeout)) {
+ dprintk(3, "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < out; i++) {
+ mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
+ dprintk(3, "API Output %d = %d\n", i, data[i]);
+ }
+
+ mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
+ dprintk(3, "API result = %d\n", retval);
+
+ flag = 0;
+ mc417_memory_write(dev, dev->cx23417_mailbox, flag);
+
+ return retval;
+}
+
+/* We don't need to call the API often, so using just one
+ * mailbox will probably suffice
+ */
+static int cx231xx_api_cmd(struct cx231xx *dev,
+ u32 command,
+ u32 inputcnt,
+ u32 outputcnt,
+ ...)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+ va_list vargs;
+ int i, err;
+
+ dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
+
+ va_start(vargs, outputcnt);
+ for (i = 0; i < inputcnt; i++)
+ data[i] = va_arg(vargs, int);
+
+ err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data);
+ for (i = 0; i < outputcnt; i++) {
+ int *vptr = va_arg(vargs, int *);
+ *vptr = data[i];
+ }
+ va_end(vargs);
+
+ return err;
+}
+
+static int cx231xx_find_mailbox(struct cx231xx *dev)
+{
+ u32 signature[4] = {
+ 0x12345678, 0x34567812, 0x56781234, 0x78123456
+ };
+ int signaturecnt = 0;
+ u32 value;
+ int i;
+ int ret = 0;
+
+ dprintk(2, "%s()\n", __func__);
+
+ for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/
+ ret = mc417_memory_read(dev, i, &value);
+ if (ret < 0)
+ return ret;
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
+ return i+1;
+ }
+ }
+ dprintk(3, "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value,
+ u32 *p_fw_image)
+{
+
+ u32 temp = 0;
+ int i = 0;
+
+ temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 1;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 2;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /*write data byte 3;*/
+ temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 2;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
+ ((address & 0x003F0000)>>8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 1;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ /* write address byte 0;*/
+ temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
+ temp = temp<<10;
+ *p_fw_image = temp;
+ p_fw_image++;
+ temp = temp|((0x05)<<10);
+ *p_fw_image = temp;
+ p_fw_image++;
+
+ for (i = 0; i < 6; i++) {
+ *p_fw_image = 0xFFFFFFFF;
+ p_fw_image++;
+ }
+}
+
+
+static int cx231xx_load_firmware(struct cx231xx *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 gpio_output = 0;
+ /*u32 checksum = 0;*/
+ /*u32 *dataptr;*/
+ u32 transfer_size = 0;
+ u32 fw_data = 0;
+ u32 address = 0;
+ /*u32 current_fw[800];*/
+ u32 *p_current_fw, *p_fw;
+ u32 *p_fw_data;
+ int frame = 0;
+ u16 _buffer_size = 4096;
+ u8 *p_buffer;
+
+ p_current_fw = vmalloc(1884180 * 4);
+ p_fw = p_current_fw;
+ if (p_current_fw == NULL) {
+ dprintk(2, "FAIL!!!\n");
+ return -1;
+ }
+
+ p_buffer = vmalloc(4096);
+ if (p_buffer == NULL) {
+ dprintk(2, "FAIL!!!\n");
+ return -1;
+ }
+
+ dprintk(2, "%s()\n", __func__);
+
+ /* Save GPIO settings before reset of APU */
+ retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
+ retval |= mc417_memory_read(dev, 0x900C, &value);
+
+ retval = mc417_register_write(dev,
+ IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ retval |= mc417_register_write(dev,
+ IVTV_REG_APU, 0);
+
+ if (retval != 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return -1;
+ }
+
+ retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME,
+ &dev->udev->dev);
+
+ if (retval != 0) {
+ printk(KERN_ERR
+ "ERROR: Hotplug firmware request failed (%s).\n",
+ CX231xx_FIRM_IMAGE_NAME);
+ printk(KERN_ERR "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) {
+ printk(KERN_ERR "ERROR: Firmware size mismatch "
+ "(have %zd, expected %d)\n",
+ firmware->size, CX231xx_FIRM_IMAGE_SIZE);
+ release_firmware(firmware);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ printk(KERN_ERR
+ "ERROR: Firmware magic mismatch, wrong file?\n");
+ release_firmware(firmware);
+ return -1;
+ }
+
+ initGPIO(dev);
+
+ /* transfer to the chip */
+ dprintk(2, "Loading firmware to GPIO...\n");
+ p_fw_data = (u32 *)firmware->data;
+ dprintk(2, "firmware->size=%zd\n", firmware->size);
+ for (transfer_size = 0; transfer_size < firmware->size;
+ transfer_size += 4) {
+ fw_data = *p_fw_data;
+
+ mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw);
+ address = address + 1;
+ p_current_fw += 20;
+ p_fw_data += 1;
+ }
+
+ /*download the firmware by ep5-out*/
+
+ for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size);
+ frame++) {
+ for (i = 0; i < _buffer_size; i++) {
+ *(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16);
+ i++;
+ *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24);
+ }
+ cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size);
+ }
+
+ p_current_fw = p_fw;
+ vfree(p_current_fw);
+ p_current_fw = NULL;
+ uninitGPIO(dev);
+ release_firmware(firmware);
+ dprintk(1, "Firmware upload successful.\n");
+
+ retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
+ IVTV_CMD_HW_BLOCKS_RST);
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return retval;
+ }
+ /* F/W power up disturbs the GPIOs, restore state */
+ retval |= mc417_register_write(dev, 0x9020, gpio_output);
+ retval |= mc417_register_write(dev, 0x900C, value);
+
+ retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
+ retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ if (retval < 0) {
+ printk(KERN_ERR "%s: Error with mc417_register_write\n",
+ __func__);
+ return retval;
+ }
+ return 0;
+}
+
+static void cx231xx_417_check_encoder(struct cx231xx *dev)
+{
+ u32 status, seq;
+
+ status = 0;
+ seq = 0;
+ cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
+ dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
+}
+
+static void cx231xx_codec_settings(struct cx231xx *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+
+ /* assign frame size */
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
+ dev->ts1.height, dev->ts1.width);
+
+ dev->mpeg_params.width = dev->ts1.width;
+ dev->mpeg_params.height = dev->ts1.height;
+
+ cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params);
+
+ cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
+ cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
+}
+
+static int cx231xx_initialize_codec(struct cx231xx *dev)
+{
+ int version;
+ int retval;
+ u32 i;
+ u32 val = 0;
+
+ dprintk(1, "%s()\n", __func__);
+ cx231xx_disable656(dev);
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(2, "%s() PING OK\n", __func__);
+ retval = cx231xx_load_firmware(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() f/w load failed\n", __func__);
+ return retval;
+ }
+ retval = cx231xx_find_mailbox(dev);
+ if (retval < 0) {
+ printk(KERN_ERR "%s() mailbox < 0, error\n",
+ __func__);
+ return -1;
+ }
+ dev->cx23417_mailbox = retval;
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "ERROR: cx23417 firmware ping failed!\n");
+ return -1;
+ }
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
+ &version);
+ if (retval < 0) {
+ printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
+ "version failed!\n");
+ return -1;
+ }
+ dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
+ msleep(200);
+ }
+
+ for (i = 0; i < 1; i++) {
+ retval = mc417_register_read(dev, 0x20f8, &val);
+ dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n",
+ val);
+ if (retval < 0)
+ return retval;
+ }
+
+ cx231xx_enable656(dev);
+ /* stop mpeg capture */
+ cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE,
+ 3, 0, 1, 3, 4);
+
+ cx231xx_codec_settings(dev);
+ msleep(60);
+
+/* cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
+ CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115);
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
+ CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0);
+*/
+
+#if 0
+ /* TODO */
+ u32 data[7];
+
+ /* Setup to capture VBI */
+ data[0] = 0x0001BD00;
+ data[1] = 1; /* frames per interrupt */
+ data[2] = 4; /* total bufs */
+ data[3] = 0x91559155; /* start codes */
+ data[4] = 0x206080C0; /* stop codes */
+ data[5] = 6; /* lines */
+ data[6] = 64; /* BPL */
+
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
+ data[2], data[3], data[4], data[5], data[6]);
+
+ for (i = 2; i <= 24; i++) {
+ int valid;
+
+ valid = ((i >= 19) && (i <= 21));
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
+ valid, 0 , 0, 0);
+ cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
+ i | 0x80000000, valid, 0, 0, 0);
+ }
+#endif
+/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE);
+ msleep(60);
+*/
+ /* initialize the video input */
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
+ if (retval < 0)
+ return retval;
+ msleep(60);
+
+ /* Enable VIP style pixel invalidation so we work with scaled mode */
+ mc417_memory_write(dev, 2120, 0x00000080);
+
+ /* start capturing to the host interface */
+ retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
+ CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE);
+ if (retval < 0)
+ return retval;
+ msleep(10);
+
+ for (i = 0; i < 1; i++) {
+ mc417_register_read(dev, 0x20f8, &val);
+ dprintk(3, "***VIM Capture Lines =%d ***\n", val);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(struct videobuf_queue *q,
+ unsigned int *count, unsigned int *size)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+
+ fh->dev->ts1.ts_packet_size = mpeglinesize;
+ fh->dev->ts1.ts_packet_count = mpeglines;
+
+ *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+ *count = mpegbufs;
+
+ return 0;
+}
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned long flags = 0;
+
+ if (in_interrupt())
+ BUG();
+
+ spin_lock_irqsave(&dev->video_mode.slock, flags);
+ if (dev->USE_ISO) {
+ if (dev->video_mode.isoc_ctl.buf == buf)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ } else {
+ if (dev->video_mode.bulk_ctl.buf == buf)
+ dev->video_mode.bulk_ctl.buf = NULL;
+ }
+ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
+ videobuf_waiton(vq, &buf->vb, 0, 0);
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ void *vbuf;
+ struct cx231xx_buffer *buf;
+ u32 tail_data = 0;
+ char *p_data;
+
+ if (dma_q->mpeg_buffer_done == 0) {
+ if (list_empty(&dma_q->active))
+ return;
+
+ buf = list_entry(dma_q->active.next,
+ struct cx231xx_buffer, vb.queue);
+ dev->video_mode.isoc_ctl.buf = buf;
+ dma_q->mpeg_buffer_done = 1;
+ }
+ /* Fill buffer */
+ buf = dev->video_mode.isoc_ctl.buf;
+ vbuf = videobuf_to_vmalloc(&buf->vb);
+
+ if ((dma_q->mpeg_buffer_completed+len) <
+ mpeglines*mpeglinesize) {
+ if (dma_q->add_ps_package_head ==
+ CX231XX_NEED_ADD_PS_PACKAGE_HEAD) {
+ memcpy(vbuf+dma_q->mpeg_buffer_completed,
+ dma_q->ps_head, 3);
+ dma_q->mpeg_buffer_completed =
+ dma_q->mpeg_buffer_completed + 3;
+ dma_q->add_ps_package_head =
+ CX231XX_NONEED_PS_PACKAGE_HEAD;
+ }
+ memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len);
+ dma_q->mpeg_buffer_completed =
+ dma_q->mpeg_buffer_completed + len;
+ } else {
+ dma_q->mpeg_buffer_done = 0;
+
+ tail_data =
+ mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed;
+ memcpy(vbuf+dma_q->mpeg_buffer_completed,
+ data, tail_data);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ dma_q->mpeg_buffer_completed = 0;
+
+ if (len - tail_data > 0) {
+ p_data = data + tail_data;
+ dma_q->left_data_count = len - tail_data;
+ memcpy(dma_q->p_left_data,
+ p_data, len - tail_data);
+ }
+
+ }
+
+ return;
+}
+
+static void buffer_filled(char *data, int len, struct urb *urb,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ void *vbuf;
+ struct cx231xx_buffer *buf;
+
+ if (list_empty(&dma_q->active))
+ return;
+
+
+ buf = list_entry(dma_q->active.next,
+ struct cx231xx_buffer, vb.queue);
+
+
+ /* Fill buffer */
+ vbuf = videobuf_to_vmalloc(&buf->vb);
+ memcpy(vbuf, data, len);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+
+ return;
+}
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *p_buffer;
+ u32 buffer_size = 0;
+ u32 i = 0;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (dma_q->left_data_count > 0) {
+ buffer_copy(dev, dma_q->p_left_data,
+ dma_q->left_data_count, urb, dma_q);
+ dma_q->mpeg_buffer_completed = dma_q->left_data_count;
+ dma_q->left_data_count = 0;
+ }
+
+ p_buffer = urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+ buffer_size = urb->iso_frame_desc[i].actual_length;
+
+ if (buffer_size > 0)
+ buffer_copy(dev, p_buffer, buffer_size, urb, dma_q);
+ }
+
+ return 0;
+}
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+
+ /*char *outp;*/
+ /*struct cx231xx_buffer *buf;*/
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ unsigned char *p_buffer, *buffer;
+ u32 buffer_size = 0;
+
+ p_buffer = urb->transfer_buffer;
+ buffer_size = urb->actual_length;
+
+ buffer = kmalloc(buffer_size, GFP_ATOMIC);
+
+ memcpy(buffer, dma_q->ps_head, 3);
+ memcpy(buffer+3, p_buffer, buffer_size-3);
+ memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3);
+
+ p_buffer = buffer;
+ buffer_filled(p_buffer, buffer_size, urb, dma_q);
+
+ kfree(buffer);
+ return 0;
+}
+
+static int bb_buf_prepare(struct videobuf_queue *q,
+ struct videobuf_buffer *vb, enum v4l2_field field)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+ int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
+
+ dma_qq = &dev->video_mode.vidq;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+ buf->vb.width = fh->dev->ts1.ts_packet_size;
+ buf->vb.height = fh->dev->ts1.ts_packet_count;
+ buf->vb.size = size;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (dev->USE_ISO) {
+ if (!dev->video_mode.isoc_ctl.num_bufs)
+ urb_init = 1;
+ } else {
+ if (!dev->video_mode.bulk_ctl.num_bufs)
+ urb_init = 1;
+ }
+ /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);*/
+ dev->mode_tv = 1;
+
+ if (urb_init) {
+ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ rc = cx231xx_unmute_audio(dev);
+ if (dev->USE_ISO) {
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+ rc = cx231xx_init_isoc(dev, mpeglines,
+ mpegbufs,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ } else {
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+ rc = cx231xx_init_bulk(dev, mpeglines,
+ mpegbufs,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ }
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(q, buf);
+ return rc;
+}
+
+static void bb_buf_queue(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_fh *fh = q->priv_data;
+
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void bb_buf_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ /*struct cx231xx_fh *fh = q->priv_data;*/
+ /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/
+
+ free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx231xx_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_buf_release,
+};
+
+/* ------------------------------------------------------------------ */
+
+static const u32 *ctrl_classes[] = {
+ cx2341x_mpeg_ctrls,
+ NULL
+};
+
+static int cx231xx_queryctrl(struct cx231xx *dev,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (qctrl->id == 0)
+ return -EINVAL;
+
+ /* MPEG V4L2 controls */
+ if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
+}
+
+static int cx231xx_querymenu(struct cx231xx *dev,
+ struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ cx231xx_queryctrl(dev, &qctrl);
+ return v4l2_ctrl_query_menu(qmenu, &qctrl,
+ cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
+}
+
+static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ *norm = dev->encodernorm.id;
+ return 0;
+}
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++)
+ if (*id & cx231xx_tvnorms[i].id)
+ break;
+ if (i == ARRAY_SIZE(cx231xx_tvnorms))
+ return -EINVAL;
+ dev->encodernorm = cx231xx_tvnorms[i];
+
+ if (dev->encodernorm.id & 0xb000) {
+ dprintk(3, "encodernorm set to NTSC\n");
+ dev->norm = V4L2_STD_NTSC;
+ dev->ts1.height = 480;
+ dev->mpeg_params.is_50hz = 0;
+ } else {
+ dprintk(3, "encodernorm set to PAL\n");
+ dev->norm = V4L2_STD_PAL_B;
+ dev->ts1.height = 576;
+ dev->mpeg_params.is_50hz = 1;
+ }
+ call_all(dev, core, s_std, dev->norm);
+ /* do mode control overrides */
+ cx231xx_do_mode_ctrl_overrides(dev);
+
+ dprintk(3, "exit vidioc_s_std() i=0x%x\n", i);
+ return 0;
+}
+static int vidioc_g_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ struct v4l2_audio *vin = a;
+
+ int ret = -EINVAL;
+ if (vin->index > 0)
+ return ret;
+ strncpy(vin->name, "VideoGrabber Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+return 0;
+}
+static int vidioc_enumaudio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ struct v4l2_audio *vin = a;
+
+ int ret = -EINVAL;
+
+ if (vin->index > 0)
+ return ret;
+ strncpy(vin->name, "VideoGrabber Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+
+
+return 0;
+}
+static const char *iname[] = {
+ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
+ [CX231XX_VMUX_SVIDEO] = "S-Video",
+ [CX231XX_VMUX_TELEVISION] = "Television",
+ [CX231XX_VMUX_CABLE] = "Cable TV",
+ [CX231XX_VMUX_DVB] = "DVB",
+ [CX231XX_VMUX_DEBUG] = "for debug only",
+};
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_input *input;
+ int n;
+ dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index);
+
+ if (i->index >= 4)
+ return -EINVAL;
+
+
+ input = &cx231xx_boards[dev->model].input[i->index];
+
+ if (input->type == 0)
+ return -EINVAL;
+
+ /* FIXME
+ * strcpy(i->name, input->name); */
+
+ n = i->index;
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if (input->type == CX231XX_VMUX_TELEVISION ||
+ input->type == CX231XX_VMUX_CABLE)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(3, "enter vidioc_s_input() i=%d\n", i);
+
+ mutex_lock(&dev->lock);
+
+ video_mux(dev, i);
+
+ mutex_unlock(&dev->lock);
+
+ if (i >= 4)
+ return -EINVAL;
+ dev->input = i;
+ dprintk(3, "exit vidioc_s_input()\n");
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+
+
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_s_ctrl()\n");
+ /* Update the A/V core */
+ call_all(dev, core, s_ctrl, ctl);
+ dprintk(3, "exit vidioc_s_ctrl()\n");
+ return 0;
+}
+static struct v4l2_capability pvr_capability = {
+ .driver = "cx231xx",
+ .card = "VideoGrabber",
+ .bus_info = "usb",
+ .version = 1,
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
+};
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+
+
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_g_fmt_vid_cap()\n");
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.width = dev->ts1.width;
+ f->fmt.pix.height = dev->ts1.height;
+ f->fmt.pix.field = fh->vidq.field;
+ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->vidq.field);
+ dprintk(3, "exit vidioc_g_fmt_vid_cap()\n");
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_try_fmt_vid_cap()\n");
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->vidq.field);
+ dprintk(3, "exit vidioc_try_fmt_vid_cap()\n");
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_reqbufs(&fh->vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_querybuf(&fh->vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_qbuf(&fh->vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_streamon()\n");
+ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
+ cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ if (dev->USE_ISO)
+ cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ else {
+ cx231xx_init_bulk(dev, 320,
+ 5,
+ dev->ts1_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ }
+ dprintk(3, "exit vidioc_streamon()\n");
+ return videobuf_streamon(&fh->vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx231xx_fh *fh = file->private_data;
+
+ return videobuf_streamoff(&fh->vidq);
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_g_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+ dprintk(3, "exit vidioc_g_ext_ctrls()\n");
+ return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+ dprintk(3, "enter vidioc_s_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ if (err == 0) {
+ err = cx2341x_update(dev, cx231xx_mbox_func,
+ &dev->mpeg_params, &p);
+ dev->mpeg_params = p;
+ }
+
+ return err;
+
+
+return 0;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct cx2341x_mpeg_params p;
+ int err;
+ dprintk(3, "enter vidioc_try_ext_ctrls()\n");
+ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+ p = dev->mpeg_params;
+ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
+ dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err);
+ return err;
+}
+
+static int vidioc_log_status(struct file *file, void *priv)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", dev->name);
+ dprintk(3,
+ "%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+ call_all(dev, core, log_status);
+ cx2341x_log_status(&dev->mpeg_params, name);
+ dprintk(3,
+ "%s/2: ============= END LOG STATUS =============\n",
+ dev->name);
+ return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_querymenu()\n");
+ dprintk(3, "exit vidioc_querymenu()\n");
+ return cx231xx_querymenu(dev, a);
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ dprintk(3, "enter vidioc_queryctrl()\n");
+ dprintk(3, "exit vidioc_queryctrl()\n");
+ return cx231xx_queryctrl(dev, c);
+}
+
+static int mpeg_open(struct file *file)
+{
+ int minor = video_devdata(file)->minor;
+ struct cx231xx *h, *dev = NULL;
+ /*struct list_head *list;*/
+ struct cx231xx_fh *fh;
+ /*u32 value = 0;*/
+
+ dprintk(2, "%s()\n", __func__);
+
+ list_for_each_entry(h, &cx231xx_devlist, devlist) {
+ if (h->v4l_device->minor == minor)
+ dev = h;
+ }
+
+ if (dev == NULL)
+ return -ENODEV;
+
+ mutex_lock(&dev->lock);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh) {
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+
+
+ videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops,
+ NULL, &dev->video_mode.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer), fh, NULL);
+/*
+ videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops,
+ &dev->udev->dev, &dev->ts1.slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer),
+ fh, NULL);
+*/
+
+
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
+ cx231xx_set_gpio_value(dev, 2, 0);
+
+ cx231xx_initialize_codec(dev);
+
+ mutex_unlock(&dev->lock);
+ cx231xx_start_TS1(dev);
+
+ return 0;
+}
+
+static int mpeg_release(struct file *file)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(3, "mpeg_release()! dev=0x%p\n", dev);
+
+ if (!dev) {
+ dprintk(3, "abort!!!\n");
+ return 0;
+ }
+
+ mutex_lock(&dev->lock);
+
+ cx231xx_stop_TS1(dev);
+
+ /* do this before setting alternate! */
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+ cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
+ CX231xx_END_NOW, CX231xx_MPEG_CAPTURE,
+ CX231xx_RAW_BITS_NONE);
+
+ /* FIXME: Review this crap */
+ /* Shut device down on last close */
+ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
+ if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
+ /* stop mpeg capture */
+
+ msleep(500);
+ cx231xx_417_check_encoder(dev);
+
+ }
+ }
+
+ if (fh->vidq.streaming)
+ videobuf_streamoff(&fh->vidq);
+ if (fh->vidq.reading)
+ videobuf_read_stop(&fh->vidq);
+
+ videobuf_mmap_free(&fh->vidq);
+ file->private_data = NULL;
+ kfree(fh);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static ssize_t mpeg_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+
+ /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
+ /* Start mpeg encoder on first read. */
+ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
+ if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
+ if (cx231xx_initialize_codec(dev) < 0)
+ return -EINVAL;
+ }
+ }
+
+ return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+static unsigned int mpeg_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ /*struct cx231xx *dev = fh->dev;*/
+
+ /*dprintk(2, "%s\n", __func__);*/
+
+ return videobuf_poll_stream(file, &fh->vidq, wait);
+}
+
+static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx231xx_fh *fh = file->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ dprintk(2, "%s()\n", __func__);
+
+ return videobuf_mmap_mapper(&fh->vidq, vma);
+}
+
+static struct v4l2_file_operations mpeg_fops = {
+ .owner = THIS_MODULE,
+ .open = mpeg_open,
+ .release = mpeg_release,
+ .read = mpeg_read,
+ .poll = mpeg_poll,
+ .mmap = mpeg_mmap,
+ .ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_log_status = vidioc_log_status,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_queryctrl = vidioc_queryctrl,
+/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+/* .vidioc_g_register = cx231xx_g_register,*/
+/* .vidioc_s_register = cx231xx_s_register,*/
+#endif
+};
+
+static struct video_device cx231xx_mpeg_template = {
+ .name = "cx231xx",
+ .fops = &mpeg_fops,
+ .ioctl_ops = &mpeg_ioctl_ops,
+ .minor = -1,
+ .tvnorms = CX231xx_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+void cx231xx_417_unregister(struct cx231xx *dev)
+{
+ dprintk(1, "%s()\n", __func__);
+ dprintk(3, "%s()\n", __func__);
+
+ if (dev->v4l_device) {
+ if (-1 != dev->v4l_device->minor)
+ video_unregister_device(dev->v4l_device);
+ else
+ video_device_release(dev->v4l_device);
+ dev->v4l_device = NULL;
+ }
+}
+
+static struct video_device *cx231xx_video_dev_alloc(
+ struct cx231xx *dev,
+ struct usb_device *usbdev,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+
+ dprintk(1, "%s()\n", __func__);
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
+ type, cx231xx_boards[dev->model].name);
+
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+
+ return vfd;
+
+}
+
+int cx231xx_417_register(struct cx231xx *dev)
+{
+ /* FIXME: Port1 hardcoded here */
+ int err = -ENODEV;
+ struct cx231xx_tsport *tsport = &dev->ts1;
+
+ dprintk(1, "%s()\n", __func__);
+
+ /* Set default TV standard */
+ dev->encodernorm = cx231xx_tvnorms[0];
+
+ if (dev->encodernorm.id & V4L2_STD_525_60)
+ tsport->height = 480;
+ else
+ tsport->height = 576;
+
+ tsport->width = 720;
+ cx2341x_fill_defaults(&dev->mpeg_params);
+ dev->norm = V4L2_STD_NTSC;
+
+ dev->mpeg_params.port = CX2341X_PORT_SERIAL;
+
+ /* Allocate and initialize V4L video device */
+ dev->v4l_device = cx231xx_video_dev_alloc(dev,
+ dev->udev, &cx231xx_mpeg_template, "mpeg");
+ err = video_register_device(dev->v4l_device,
+ VFL_TYPE_GRABBER, -1);
+ if (err < 0) {
+ dprintk(3, "%s: can't register mpeg device\n", dev->name);
+ return err;
+ }
+
+ dprintk(3, "%s: registered device video%d [mpeg]\n",
+ dev->name, dev->v4l_device->num);
+
+ return 0;
+}
+
+MODULE_FIRMWARE(CX231xx_FIRM_IMAGE_NAME);
diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index b4c99c7270cf..b4c99c7270cf 100644
--- a/drivers/media/video/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/usb/cx231xx/cx231xx-avcore.c
index 447148eff958..447148eff958 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/usb/cx231xx/cx231xx-avcore.c
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 02d4d36735d3..02d4d36735d3 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/usb/cx231xx/cx231xx-conf-reg.h
index 25593f212abf..25593f212abf 100644
--- a/drivers/media/video/cx231xx/cx231xx-conf-reg.h
+++ b/drivers/media/usb/cx231xx/cx231xx-conf-reg.h
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 05358d486135..05358d486135 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
diff --git a/drivers/media/video/cx231xx/cx231xx-dif.h b/drivers/media/usb/cx231xx/cx231xx-dif.h
index 2b63c2f6d3b0..2b63c2f6d3b0 100644
--- a/drivers/media/video/cx231xx/cx231xx-dif.h
+++ b/drivers/media/usb/cx231xx/cx231xx-dif.h
diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 7c4e360ba9bc..7c4e360ba9bc 100644
--- a/drivers/media/video/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/usb/cx231xx/cx231xx-i2c.c
index 781feed406f7..781feed406f7 100644
--- a/drivers/media/video/cx231xx/cx231xx-i2c.c
+++ b/drivers/media/usb/cx231xx/cx231xx-i2c.c
diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/usb/cx231xx/cx231xx-input.c
index 96176e9db5a2..96176e9db5a2 100644
--- a/drivers/media/video/cx231xx/cx231xx-input.c
+++ b/drivers/media/usb/cx231xx/cx231xx-input.c
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
index 7473c33e823e..7473c33e823e 100644
--- a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
+++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.c
diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
index f5e46e89f3ab..f5e46e89f3ab 100644
--- a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
+++ b/drivers/media/usb/cx231xx/cx231xx-pcb-cfg.h
diff --git a/drivers/media/video/cx231xx/cx231xx-reg.h b/drivers/media/usb/cx231xx/cx231xx-reg.h
index 750c5d37d569..750c5d37d569 100644
--- a/drivers/media/video/cx231xx/cx231xx-reg.h
+++ b/drivers/media/usb/cx231xx/cx231xx-reg.h
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/usb/cx231xx/cx231xx-vbi.c
index ac7db52f404f..ac7db52f404f 100644
--- a/drivers/media/video/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.c
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/usb/cx231xx/cx231xx-vbi.h
index 16c7d20a22a4..16c7d20a22a4 100644
--- a/drivers/media/video/cx231xx/cx231xx-vbi.h
+++ b/drivers/media/usb/cx231xx/cx231xx-vbi.h
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
new file mode 100644
index 000000000000..fedf7852a355
--- /dev/null
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -0,0 +1,2670 @@
+/*
+ cx231xx-video.c - driver for Conexant Cx23100/101/102
+ USB video capture devices
+
+ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
+ Based on em28xx driver
+ Based on cx23885 driver
+ Based on cx88 driver
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/msp3400.h>
+#include <media/tuner.h>
+
+#include "dvb_frontend.h"
+
+#include "cx231xx.h"
+#include "cx231xx-vbi.h"
+
+#define CX231XX_VERSION "0.0.2"
+
+#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
+#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
+
+#define cx231xx_videodbg(fmt, arg...) do {\
+ if (video_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define cx231xx_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); \
+ } \
+ } while (0)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CX231XX_VERSION);
+
+static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(card, int, NULL, 0444);
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+
+MODULE_PARM_DESC(card, "card type");
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+/* supported video standards */
+static struct cx231xx_fmt format[] = {
+ {
+ .name = "16bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = 0,
+ },
+};
+
+/* supported controls */
+/* Common to all boards */
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx231xx_ctrl cx231xx_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* strictly, this only describes only U saturation.
+ * V saturation is handled specially through code.
+ */
+ .v = {
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls);
+
+static const u32 cx231xx_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+#if 0
+ V4L2_CID_AUDIO_BALANCE,
+#endif
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ cx231xx_user_ctrls,
+ NULL
+};
+
+/* ------------------------------------------------------------------
+ Video buffer and parser functions
+ ------------------------------------------------------------------*/
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ else
+ dev->video_mode.bulk_ctl.buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void print_err_status(struct cx231xx *dev, int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
+ struct cx231xx_buffer **buf)
+{
+ struct cx231xx_video_mode *vmode =
+ container_of(dma_q, struct cx231xx_video_mode, vidq);
+ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
+
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ cx231xx_isocdbg("No active queue to serve\n");
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ else
+ dev->video_mode.bulk_ctl.buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0, (*buf)->vb.size);
+
+ if (dev->USE_ISO)
+ dev->video_mode.isoc_ctl.buf = *buf;
+ else
+ dev->video_mode.bulk_ctl.buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ int i, rc = 1;
+ unsigned char *p_buffer;
+ u32 bytes_parsed = 0, buffer_size = 0;
+ u8 sav_eav = 0;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->video_mode.max_pkt_size) {
+ cx231xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ /* get buffer pointer and length */
+ p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ buffer_size = urb->iso_frame_desc[i].actual_length;
+ bytes_parsed = 0;
+
+ if (dma_q->is_partial_line) {
+ /* Handle the case of a partial line */
+ sav_eav = dma_q->last_sav;
+ } else {
+ /* Check for a SAV/EAV overlapping
+ the buffer boundary */
+ sav_eav =
+ cx231xx_find_boundary_SAV_EAV(p_buffer,
+ dma_q->partial_buf,
+ &bytes_parsed);
+ }
+
+ sav_eav &= 0xF0;
+ /* Get the first line if we have some portion of an SAV/EAV from
+ the last buffer or a partial line */
+ if (sav_eav) {
+ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
+ sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed);/* buf size */
+ }
+
+ /* Now parse data that is completely in this buffer */
+ /* dma_q->is_partial_line = 0; */
+
+ while (bytes_parsed < buffer_size) {
+ u32 bytes_used = 0;
+
+ sav_eav = cx231xx_find_next_SAV_EAV(
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed, /* buf size */
+ &bytes_used);/* bytes used to get SAV/EAV */
+
+ bytes_parsed += bytes_used;
+
+ sav_eav &= 0xF0;
+ if (sav_eav && (bytes_parsed < buffer_size)) {
+ bytes_parsed += cx231xx_get_video_line(dev,
+ dma_q, sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed,/* p_buffer */
+ buffer_size - bytes_parsed);/*buf size*/
+ }
+ }
+
+ /* Save the last four bytes of the buffer so we can check the
+ buffer boundary condition next time */
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+ bytes_parsed = 0;
+
+ }
+ return rc;
+}
+
+static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
+{
+ struct cx231xx_dmaqueue *dma_q = urb->context;
+ int rc = 1;
+ unsigned char *p_buffer;
+ u32 bytes_parsed = 0, buffer_size = 0;
+ u8 sav_eav = 0;
+
+ if (!dev)
+ return 0;
+
+ if (dev->state & DEV_DISCONNECTED)
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ if (1) {
+
+ /* get buffer pointer and length */
+ p_buffer = urb->transfer_buffer;
+ buffer_size = urb->actual_length;
+ bytes_parsed = 0;
+
+ if (dma_q->is_partial_line) {
+ /* Handle the case of a partial line */
+ sav_eav = dma_q->last_sav;
+ } else {
+ /* Check for a SAV/EAV overlapping
+ the buffer boundary */
+ sav_eav =
+ cx231xx_find_boundary_SAV_EAV(p_buffer,
+ dma_q->partial_buf,
+ &bytes_parsed);
+ }
+
+ sav_eav &= 0xF0;
+ /* Get the first line if we have some portion of an SAV/EAV from
+ the last buffer or a partial line */
+ if (sav_eav) {
+ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
+ sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed);/* buf size */
+ }
+
+ /* Now parse data that is completely in this buffer */
+ /* dma_q->is_partial_line = 0; */
+
+ while (bytes_parsed < buffer_size) {
+ u32 bytes_used = 0;
+
+ sav_eav = cx231xx_find_next_SAV_EAV(
+ p_buffer + bytes_parsed, /* p_buffer */
+ buffer_size - bytes_parsed, /* buf size */
+ &bytes_used);/* bytes used to get SAV/EAV */
+
+ bytes_parsed += bytes_used;
+
+ sav_eav &= 0xF0;
+ if (sav_eav && (bytes_parsed < buffer_size)) {
+ bytes_parsed += cx231xx_get_video_line(dev,
+ dma_q, sav_eav, /* SAV/EAV */
+ p_buffer + bytes_parsed,/* p_buffer */
+ buffer_size - bytes_parsed);/*buf size*/
+ }
+ }
+
+ /* Save the last four bytes of the buffer so we can check the
+ buffer boundary condition next time */
+ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
+ bytes_parsed = 0;
+
+ }
+ return rc;
+}
+
+
+u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
+ u32 *p_bytes_used)
+{
+ u32 bytes_used;
+ u8 boundary_bytes[8];
+ u8 sav_eav = 0;
+
+ *p_bytes_used = 0;
+
+ /* Create an array of the last 4 bytes of the last buffer and the first
+ 4 bytes of the current buffer. */
+
+ memcpy(boundary_bytes, partial_buf, 4);
+ memcpy(boundary_bytes + 4, p_buffer, 4);
+
+ /* Check for the SAV/EAV in the boundary buffer */
+ sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
+ &bytes_used);
+
+ if (sav_eav) {
+ /* found a boundary SAV/EAV. Updates the bytes used to reflect
+ only those used in the new buffer */
+ *p_bytes_used = bytes_used - 4;
+ }
+
+ return sav_eav;
+}
+
+u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
+{
+ u32 i;
+ u8 sav_eav = 0;
+
+ /*
+ * Don't search if the buffer size is less than 4. It causes a page
+ * fault since buffer_size - 4 evaluates to a large number in that
+ * case.
+ */
+ if (buffer_size < 4) {
+ *p_bytes_used = buffer_size;
+ return 0;
+ }
+
+ for (i = 0; i < (buffer_size - 3); i++) {
+
+ if ((p_buffer[i] == 0xFF) &&
+ (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
+
+ *p_bytes_used = i + 4;
+ sav_eav = p_buffer[i + 3];
+ return sav_eav;
+ }
+ }
+
+ *p_bytes_used = buffer_size;
+ return 0;
+}
+
+u32 cx231xx_get_video_line(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
+ u8 *p_buffer, u32 buffer_size)
+{
+ u32 bytes_copied = 0;
+ int current_field = -1;
+
+ switch (sav_eav) {
+ case SAV_ACTIVE_VIDEO_FIELD1:
+ /* looking for skipped line which occurred in PAL 720x480 mode.
+ In this case, there will be no active data contained
+ between the SAV and EAV */
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
+ return bytes_copied;
+ current_field = 1;
+ break;
+
+ case SAV_ACTIVE_VIDEO_FIELD2:
+ /* looking for skipped line which occurred in PAL 720x480 mode.
+ In this case, there will be no active data contained between
+ the SAV and EAV */
+ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
+ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
+ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
+ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
+ (p_buffer[3] == EAV_VBLANK_FIELD2)))
+ return bytes_copied;
+ current_field = 2;
+ break;
+ }
+
+ dma_q->last_sav = sav_eav;
+
+ bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
+ buffer_size, current_field);
+
+ return bytes_copied;
+}
+
+u32 cx231xx_copy_video_line(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q, u8 *p_line,
+ u32 length, int field_number)
+{
+ u32 bytes_to_copy;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width * 2;
+
+ if (dma_q->current_field != field_number)
+ cx231xx_reset_video_buffer(dev, dma_q);
+
+ /* get the buffer pointer */
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ /* Remember the field number for next time */
+ dma_q->current_field = field_number;
+
+ bytes_to_copy = dma_q->bytes_left_in_line;
+ if (bytes_to_copy > length)
+ bytes_to_copy = length;
+
+ if (dma_q->lines_completed >= dma_q->lines_per_field) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
+ 0 : 1;
+ return 0;
+ }
+
+ dma_q->is_partial_line = 1;
+
+ /* If we don't have a buffer, just return the number of bytes we would
+ have copied if we had a buffer. */
+ if (!buf) {
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
+ ? 0 : 1;
+ return bytes_to_copy;
+ }
+
+ /* copy the data to video buffer */
+ cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
+
+ dma_q->pos += bytes_to_copy;
+ dma_q->bytes_left_in_line -= bytes_to_copy;
+
+ if (dma_q->bytes_left_in_line == 0) {
+ dma_q->bytes_left_in_line = _line_size;
+ dma_q->lines_completed++;
+ dma_q->is_partial_line = 0;
+
+ if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
+ buffer_filled(dev, dma_q, buf);
+
+ dma_q->pos = 0;
+ buf = NULL;
+ dma_q->lines_completed = 0;
+ }
+ }
+
+ return bytes_to_copy;
+}
+
+void cx231xx_reset_video_buffer(struct cx231xx *dev,
+ struct cx231xx_dmaqueue *dma_q)
+{
+ struct cx231xx_buffer *buf;
+
+ /* handle the switch from field 1 to field 2 */
+ if (dma_q->current_field == 1) {
+ if (dma_q->lines_completed >= dma_q->lines_per_field)
+ dma_q->field1_done = 1;
+ else
+ dma_q->field1_done = 0;
+ }
+
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ if (buf == NULL) {
+ /* first try to get the buffer */
+ get_next_buf(dma_q, &buf);
+
+ dma_q->pos = 0;
+ dma_q->field1_done = 0;
+ dma_q->current_field = -1;
+ }
+
+ /* reset the counters */
+ dma_q->bytes_left_in_line = dev->width << 1;
+ dma_q->lines_completed = 0;
+}
+
+int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
+ u8 *p_buffer, u32 bytes_to_copy)
+{
+ u8 *p_out_buffer = NULL;
+ u32 current_line_bytes_copied = 0;
+ struct cx231xx_buffer *buf;
+ u32 _line_size = dev->width << 1;
+ void *startwrite;
+ int offset, lencopy;
+
+ if (dev->USE_ISO)
+ buf = dev->video_mode.isoc_ctl.buf;
+ else
+ buf = dev->video_mode.bulk_ctl.buf;
+
+ if (buf == NULL)
+ return -1;
+
+ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
+
+ current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
+
+ /* Offset field 2 one line from the top of the buffer */
+ offset = (dma_q->current_field == 1) ? 0 : _line_size;
+
+ /* Offset for field 2 */
+ startwrite = p_out_buffer + offset;
+
+ /* lines already completed in the current field */
+ startwrite += (dma_q->lines_completed * _line_size * 2);
+
+ /* bytes already completed in the current line */
+ startwrite += current_line_bytes_copied;
+
+ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
+ bytes_to_copy : dma_q->bytes_left_in_line;
+
+ if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
+ return 0;
+
+ /* The below copies the UYVY data straight into video buffer */
+ cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
+
+ return 0;
+}
+
+void cx231xx_swab(u16 *from, u16 *to, u16 len)
+{
+ u16 i;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len / 2; i++)
+ to[i] = (from[i] << 8) | (from[i] >> 8);
+}
+
+u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
+{
+ u8 buffer_complete = 0;
+
+ /* Dual field stream */
+ buffer_complete = ((dma_q->current_field == 2) &&
+ (dma_q->lines_completed >= dma_q->lines_per_field) &&
+ dma_q->field1_done);
+
+ return buffer_complete;
+}
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
+ if (0 == *count)
+ *count = CX231XX_DEF_BUF;
+
+ if (*count < CX231XX_MIN_BUF)
+ *count = CX231XX_MIN_BUF;
+
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ unsigned long flags = 0;
+
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->video_mode.slock, flags);
+ if (dev->USE_ISO) {
+ if (dev->video_mode.isoc_ctl.buf == buf)
+ dev->video_mode.isoc_ctl.buf = NULL;
+ } else {
+ if (dev->video_mode.bulk_ctl.buf == buf)
+ dev->video_mode.bulk_ctl.buf = NULL;
+ }
+ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ /* The only currently supported format is 16 bits/pixel */
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ + 7) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (dev->USE_ISO) {
+ if (!dev->video_mode.isoc_ctl.num_bufs)
+ urb_init = 1;
+ } else {
+ if (!dev->video_mode.bulk_ctl.num_bufs)
+ urb_init = 1;
+ }
+ /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
+ urb_init, dev->video_mode.max_pkt_size);*/
+ if (urb_init) {
+ dev->mode_tv = 0;
+ if (dev->USE_ISO)
+ rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_isoc_copy);
+ else
+ rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
+ CX231XX_NUM_BUFS,
+ dev->video_mode.max_pkt_size,
+ cx231xx_bulk_copy);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = fh->dev;
+ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct cx231xx_buffer *buf =
+ container_of(vb, struct cx231xx_buffer, vb);
+ struct cx231xx_fh *fh = vq->priv_data;
+ struct cx231xx *dev = (struct cx231xx *)fh->dev;
+
+ cx231xx_isocdbg("cx231xx: called buffer_release\n");
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops cx231xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************* v4l2 interface **************************************/
+
+void video_mux(struct cx231xx *dev, int index)
+{
+ dev->video_input = index;
+ dev->ctl_ainput = INPUT(index)->amux;
+
+ cx231xx_set_video_input_mux(dev, index);
+
+ cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
+
+ cx231xx_set_audio_input(dev, dev->ctl_ainput);
+
+ cx231xx_info("video_mux : %d\n", index);
+
+ /* do mode control overrides if required */
+ cx231xx_do_mode_ctrl_overrides(dev);
+}
+
+/* Usage lock check functions */
+static int res_get(struct cx231xx_fh *fh)
+{
+ struct cx231xx *dev = fh->dev;
+ int rc = 0;
+
+ /* This instance already has stream_on */
+ if (fh->stream_on)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (dev->stream_on)
+ return -EBUSY;
+ dev->stream_on = 1;
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (dev->vbi_stream_on)
+ return -EBUSY;
+ dev->vbi_stream_on = 1;
+ } else
+ return -EINVAL;
+
+ fh->stream_on = 1;
+
+ return rc;
+}
+
+static int res_check(struct cx231xx_fh *fh)
+{
+ return fh->stream_on;
+}
+
+static void res_free(struct cx231xx_fh *fh)
+{
+ struct cx231xx *dev = fh->dev;
+
+ fh->stream_on = 0;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ dev->stream_on = 0;
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ dev->vbi_stream_on = 0;
+}
+
+static int check_dev(struct cx231xx *dev)
+{
+ if (dev->state & DEV_DISCONNECTED) {
+ cx231xx_errdev("v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ unsigned int width = f->fmt.pix.width;
+ unsigned int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ struct cx231xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+ struct cx231xx_fmt *fmt;
+ struct v4l2_mbus_framefmt mbus_fmt;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt)
+ return -EINVAL;
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ cx231xx_errdev("%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ if (dev->stream_on && !fh->stream_on) {
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
+ return -EBUSY;
+ }
+
+ /* set new image size */
+ dev->width = f->fmt.pix.width;
+ dev->height = f->fmt.pix.height;
+ dev->format = fmt;
+
+ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
+
+ return rc;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ *id = dev->norm;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
+
+ dev->norm = *norm;
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_vid_cap(file, priv, &f);
+
+ call_all(dev, core, s_std, dev->norm);
+
+ /* We need to reset basic properties in the decoder related to
+ resolution (since a standard change effects things like the number
+ of lines in VACT, etc) */
+ v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED);
+ call_all(dev, video, s_mbus_fmt, &mbus_fmt);
+ v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+
+ /* do mode control overrides */
+ cx231xx_do_mode_ctrl_overrides(dev);
+
+ return 0;
+}
+
+static const char *iname[] = {
+ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
+ [CX231XX_VMUX_SVIDEO] = "S-Video",
+ [CX231XX_VMUX_TELEVISION] = "Television",
+ [CX231XX_VMUX_CABLE] = "Cable TV",
+ [CX231XX_VMUX_DVB] = "DVB",
+ [CX231XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ u32 gen_stat;
+ unsigned int ret, n;
+
+ n = i->index;
+ if (n >= MAX_CX231XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX231XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ /* If they are asking about the active input, read signal status */
+ if (n == dev->video_input) {
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ GEN_STAT, 2, &gen_stat, 4);
+ if (ret > 0) {
+ if ((gen_stat & FLD_VPRES) == 0x00)
+ i->status |= V4L2_IN_ST_NO_SIGNAL;
+ if ((gen_stat & FLD_HLOCK) == 0x00)
+ i->status |= V4L2_IN_ST_NO_H_LOCK;
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ *i = dev->video_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ dev->mode_tv = 0;
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_CX231XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ video_mux(dev, i);
+
+ if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
+ INPUT(i)->type == CX231XX_VMUX_CABLE) {
+ /* There's a tuner, so reset the standard and put it on the
+ last known frequency (since it was probably powered down
+ until now */
+ call_all(dev, core, s_std, dev->norm);
+ }
+
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ switch (a->index) {
+ case CX231XX_AMUX_VIDEO:
+ strcpy(a->name, "Television");
+ break;
+ case CX231XX_AMUX_LINE_IN:
+ strcpy(a->name, "Line In");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->index = dev->ctl_ainput;
+ a->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int status = 0;
+
+ /* Doesn't allow manual routing */
+ if (a->index != dev->ctl_ainput)
+ return -EINVAL;
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ status = cx231xx_set_audio_input(dev, dev->ctl_ainput);
+
+ return status;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int id = qc->id;
+ int i;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
+ if (unlikely(qc->id == 0))
+ return -EINVAL;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < CX231XX_CTLS; i++)
+ if (cx231xx_ctls[i].v.id == qc->id)
+ break;
+
+ if (i == CX231XX_CTLS) {
+ *qc = no_ctl;
+ return 0;
+ }
+ *qc = cx231xx_ctls[i].v;
+
+ call_all(dev, 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)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, core, g_ctrl, ctrl);
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, core, s_ctrl, ctrl);
+ return rc;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->signal = 0xffff; /* LOCKED */
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+#if 0
+ call_all(dev, tuner, s_tuner, t);
+#endif
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+
+ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+ u32 if_frequency = 5400000;
+
+ cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
+ f->frequency, f->type);
+ /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ /* set pre channel change settings in DIF first */
+ rc = cx231xx_tuner_pre_channel_change(dev);
+
+ dev->ctl_freq = f->frequency;
+ call_all(dev, tuner, s_frequency, f);
+
+ /* set post channel change settings in DIF first */
+ rc = cx231xx_tuner_post_channel_change(dev);
+
+ if (dev->tuner_type == TUNER_NXP_TDA18271) {
+ if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
+ if_frequency = 5400000; /*5.4MHz */
+ else if (dev->norm & V4L2_STD_B)
+ if_frequency = 6000000; /*6.0MHz */
+ else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
+ if_frequency = 6900000; /*6.9MHz */
+ else if (dev->norm & V4L2_STD_GH)
+ if_frequency = 7100000; /*7.1MHz */
+ else if (dev->norm & V4L2_STD_PAL_I)
+ if_frequency = 7250000; /*7.25MHz */
+ else if (dev->norm & V4L2_STD_SECAM_L)
+ if_frequency = 6900000; /*6.9MHz */
+ else if (dev->norm & V4L2_STD_SECAM_LC)
+ if_frequency = 1250000; /*1.25MHz */
+
+ cx231xx_info("if_frequency is set to %d\n", if_frequency);
+ cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
+
+ update_HH_register_after_set_DIF(dev);
+ }
+
+ cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
+
+ return rc;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+
+/*
+ -R, --list-registers=type=<host/i2cdrv/i2caddr>,
+ chip=<chip>[,min=<addr>,max=<addr>]
+ dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
+ -r, --set-register=type=<host/i2cdrv/i2caddr>,
+ chip=<chip>,reg=<addr>,val=<val>
+ set the register [VIDIOC_DBG_S_REGISTER]
+
+ if type == host, then <chip> is the hosts chip ID (default 0)
+ if type == i2cdrv (default), then <chip> is the I2C driver name or ID
+ if type == i2caddr, then <chip> is the 7-bit I2C address
+*/
+
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int ret = 0;
+ u8 value[4] = { 0, 0, 0, 0 };
+ u32 data = 0;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ switch (reg->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ (u16)reg->reg, value, 4);
+ reg->val = value[0] | value[1] << 8 |
+ value[2] << 16 | value[3] << 24;
+ break;
+ case 1: /* AFE - read byte */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 14: /* AFE - read dword */
+ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 2: /* Video Block - read byte */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 24: /* Video Block - read dword */
+ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 3: /* I2S block - read byte */
+ ret = cx231xx_read_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ &data, 1);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 34: /* I2S Block - read dword */
+ ret =
+ cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1, &data, 4);
+ reg->val = le32_to_cpu(data);
+ break;
+ }
+ return ret < 0 ? ret : 0;
+
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ call_all(dev, core, g_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/
+ switch (reg->match.addr) {
+ case 0: /* Cx231xx - internal registers */
+ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
+ (u16)reg->reg, value, 4);
+ reg->val = value[0] | value[1] << 8 |
+ value[2] << 16 | value[3] << 24;
+
+ break;
+ case 0x600:/* AFE - read byte */
+ ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+
+ case 0x880:/* Video Block - read byte */
+ if (reg->reg < 0x0b) {
+ ret = cx231xx_read_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ } else {
+ ret = cx231xx_read_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ &data, 4 , 0);
+ reg->val = le32_to_cpu(data);
+ }
+ break;
+ case 0x980:
+ ret = cx231xx_read_i2c_master(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0x400:
+ ret =
+ cx231xx_read_i2c_master(dev, 0x40,
+ (u16)reg->reg, 1,
+ &data, 1 , 0);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0xc01:
+ ret =
+ cx231xx_read_i2c_master(dev, 0xc0,
+ (u16)reg->reg, 2,
+ &data, 38, 1);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 0x022:
+ ret =
+ cx231xx_read_i2c_master(dev, 0x02,
+ (u16)reg->reg, 1,
+ &data, 1, 2);
+ reg->val = le32_to_cpu(data & 0xff);
+ break;
+ case 0x322:
+ ret = cx231xx_read_i2c_master(dev,
+ 0x32,
+ (u16)reg->reg, 1,
+ &data, 4 , 2);
+ reg->val = le32_to_cpu(data);
+ break;
+ case 0x342:
+ ret = cx231xx_read_i2c_master(dev,
+ 0x34,
+ (u16)reg->reg, 1,
+ &data, 4 , 2);
+ reg->val = le32_to_cpu(data);
+ break;
+
+ default:
+ cx231xx_info("no match device address!!\n");
+ break;
+ }
+ return ret < 0 ? ret : 0;
+ /*return -EINVAL;*/
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ call_all(dev, core, g_register, reg);
+
+ return ret;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int ret = 0;
+ __le64 buf;
+ u32 value;
+ u8 data[4] = { 0, 0, 0, 0 };
+
+ buf = cpu_to_le64(reg->val);
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ {
+ value = (u32) buf & 0xffffffff;
+
+ switch (reg->match.addr) {
+ case 0: /* cx231xx internal registers */
+ data[0] = (u8) value;
+ data[1] = (u8) (value >> 8);
+ data[2] = (u8) (value >> 16);
+ data[3] = (u8) (value >> 24);
+ ret = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ (u16)reg->reg, data,
+ 4);
+ break;
+ case 1: /* AFE - read byte */
+ ret = cx231xx_write_i2c_data(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1);
+ break;
+ case 14: /* AFE - read dword */
+ ret = cx231xx_write_i2c_data(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4);
+ break;
+ case 2: /* Video Block - read byte */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1);
+ break;
+ case 24: /* Video Block - read dword */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4);
+ break;
+ case 3: /* I2S block - read byte */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 1);
+ break;
+ case 34: /* I2S block - read dword */
+ ret =
+ cx231xx_write_i2c_data(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 4);
+ break;
+ }
+ }
+ return ret < 0 ? ret : 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ {
+ value = (u32) buf & 0xffffffff;
+
+ switch (reg->match.addr) {
+ case 0:/*cx231xx internal registers*/
+ data[0] = (u8) value;
+ data[1] = (u8) (value >> 8);
+ data[2] = (u8) (value >> 16);
+ data[3] = (u8) (value >> 24);
+ ret = cx231xx_write_ctrl_reg(dev,
+ VRT_SET_REGISTER,
+ (u16)reg->reg, data,
+ 4);
+ break;
+ case 0x600:/* AFE - read byte */
+ ret = cx231xx_write_i2c_master(dev,
+ AFE_DEVICE_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1 , 0);
+ break;
+
+ case 0x880:/* Video Block - read byte */
+ if (reg->reg < 0x0b)
+ cx231xx_write_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 1, 0);
+ else
+ cx231xx_write_i2c_master(dev,
+ VID_BLK_I2C_ADDRESS,
+ (u16)reg->reg, 2,
+ value, 4, 0);
+ break;
+ case 0x980:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ I2S_BLK_DEVICE_ADDRESS,
+ (u16)reg->reg, 1,
+ value, 1, 0);
+ break;
+ case 0x400:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x40,
+ (u16)reg->reg, 1,
+ value, 1, 0);
+ break;
+ case 0xc01:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0xc0,
+ (u16)reg->reg, 1,
+ value, 1, 1);
+ break;
+
+ case 0x022:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x02,
+ (u16)reg->reg, 1,
+ value, 1, 2);
+ case 0x322:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x32,
+ (u16)reg->reg, 1,
+ value, 4, 2);
+ break;
+
+ case 0x342:
+ ret =
+ cx231xx_write_i2c_master(dev,
+ 0x34,
+ (u16)reg->reg, 1,
+ value, 4, 2);
+ break;
+ default:
+ cx231xx_info("no match device address, "
+ "the value is %x\n", reg->match.addr);
+ break;
+
+ }
+
+ }
+ default:
+ break;
+ }
+
+ call_all(dev, core, s_register, reg);
+
+ return ret;
+}
+#endif
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (likely(rc >= 0))
+ rc = videobuf_streamon(&fh->vb_vidq);
+
+ call_all(dev, video, s_stream, 1);
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ cx25840_call(dev, video, s_stream, 0);
+
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_VBI_CAPTURE |
+#if 0
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+#endif
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ f->fmt.sliced.service_set = 0;
+
+ call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES;
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+
+ return 0;
+
+}
+
+static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+
+ if (dev->vbi_stream_on && !fh->stream_on) {
+ cx231xx_errdev("%s device in use by another fh\n", __func__);
+ return -EBUSY;
+ }
+
+ f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ f->fmt.vbi.sampling_rate = 6750000 * 4;
+ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
+ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ f->fmt.vbi.offset = 0;
+ f->fmt.vbi.flags = 0;
+ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
+ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_LINES : NTSC_VBI_LINES;
+ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
+ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
+ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
+
+ return 0;
+
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_querybuf(&fh->vb_vidq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_qbuf(&fh->vb_vidq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct cx231xx_fh *fh = priv;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+}
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
+ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
+{
+ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ int i;
+
+ if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ for (i = 0; i < CX231XX_CTLS; i++) {
+ if (cx231xx_ctls[i].v.id == c->id)
+ break;
+ }
+ if (i == CX231XX_CTLS)
+ return -EINVAL;
+ *c = cx231xx_ctls[i].v;
+ } else
+ *c = no_ctl;
+ return 0;
+}
+
+/*
+ * cx231xx_v4l2_open()
+ * inits the device and starts isoc transfer
+ */
+static int cx231xx_v4l2_open(struct file *filp)
+{
+ int errCode = 0, radio = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct cx231xx *dev = video_drvdata(filp);
+ struct cx231xx_fh *fh;
+ enum v4l2_buf_type fh_type = 0;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ cx231xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
+
+#if 0
+ errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+ if (errCode < 0) {
+ cx231xx_errdev
+ ("Device locked on digital mode. Can't open analog\n");
+ return -EBUSY;
+ }
+#endif
+
+ fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
+ if (!fh) {
+ cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
+ return -ENOMEM;
+ }
+ if (mutex_lock_interruptible(&dev->lock)) {
+ kfree(fh);
+ return -ERESTARTSYS;
+ }
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = fh_type;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+
+ /* Power up in Analog TV mode */
+ if (dev->board.external_av)
+ cx231xx_set_power_mode(dev,
+ POLARIS_AVMODE_ENXTERNAL_AV);
+ else
+ cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
+
+#if 0
+ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
+#endif
+
+ /* set video alternate setting */
+ cx231xx_set_video_alternate(dev);
+
+ /* Needed, since GPIO might have disabled power of
+ some i2c device */
+ cx231xx_config_i2c(dev);
+
+ /* device needs to be initialized before isoc transfer */
+ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
+
+ }
+ if (fh->radio) {
+ cx231xx_videodbg("video_open: setting radio device\n");
+
+ /* cx231xx_start_radio(dev); */
+
+ call_all(dev, tuner, s_radio);
+ }
+
+ dev->users++;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
+ NULL, &dev->video_mode.slock,
+ fh->type, V4L2_FIELD_INTERLACED,
+ sizeof(struct cx231xx_buffer),
+ fh, &dev->lock);
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ /* Set the required alternate setting VBI interface works in
+ Bulk mode only */
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
+ NULL, &dev->vbi_mode.slock,
+ fh->type, V4L2_FIELD_SEQ_TB,
+ sizeof(struct cx231xx_buffer),
+ fh, &dev->lock);
+ }
+ mutex_unlock(&dev->lock);
+
+ return errCode;
+}
+
+/*
+ * cx231xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void cx231xx_release_analog_resources(struct cx231xx *dev)
+{
+
+ /*FIXME: I2C IR should be disconnected */
+
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ cx231xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+
+ if (dev->board.has_417)
+ cx231xx_417_unregister(dev);
+
+ if (video_is_registered(dev->vdev))
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
+}
+
+/*
+ * cx231xx_close()
+ * stops streaming and deallocates all resources allocated by the v4l2
+ * calls and ioctls
+ */
+static int cx231xx_close(struct file *filp)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+
+ cx231xx_videodbg("users=%d\n", dev->users);
+
+ cx231xx_videodbg("users=%d\n", dev->users);
+ if (res_check(fh))
+ res_free(fh);
+
+ /*
+ * 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);
+
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ if (atomic_read(&dev->devlist_count) > 0) {
+ cx231xx_release_resources(dev);
+ fh->dev = NULL;
+ return 0;
+ }
+ return 0;
+ }
+
+ /* do this before setting alternate! */
+ cx231xx_uninit_vbi_isoc(dev);
+
+ /* set alternate 0 */
+ if (!dev->vbi_or_sliced_cc_mode)
+ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
+ else
+ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
+
+ kfree(fh);
+ dev->users--;
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+ }
+
+ dev->users--;
+ if (!dev->users) {
+ videobuf_stop(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vidq);
+
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ cx231xx_release_resources(dev);
+ fh->dev = NULL;
+ return 0;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ call_all(dev, core, s_power, 0);
+
+ /* do this before setting alternate! */
+ if (dev->USE_ISO)
+ cx231xx_uninit_isoc(dev);
+ else
+ cx231xx_uninit_bulk(dev);
+ cx231xx_set_mode(dev, CX231XX_SUSPEND);
+
+ /* set alternate 0 */
+ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
+ }
+ kfree(fh);
+ wake_up_interruptible_nr(&dev->open, 1);
+ return 0;
+}
+
+static int cx231xx_v4l2_close(struct file *filp)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = cx231xx_close(filp);
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+/*
+ * cx231xx_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t
+cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return rc;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&dev->lock);
+ return rc;
+ }
+ return 0;
+}
+
+/*
+ * cx231xx_v4l2_poll()
+ * will allocate buffers when called for the first time
+ */
+static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return POLLERR;
+
+ if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
+ (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) {
+ unsigned int res;
+
+ mutex_lock(&dev->lock);
+ res = videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ mutex_unlock(&dev->lock);
+ return res;
+ }
+ return POLLERR;
+}
+
+/*
+ * cx231xx_v4l2_mmap()
+ */
+static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct cx231xx_fh *fh = filp->private_data;
+ struct cx231xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return rc;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ mutex_unlock(&dev->lock);
+
+ cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end -
+ (unsigned long)vma->vm_start, rc);
+
+ return rc;
+}
+
+static const struct v4l2_file_operations cx231xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = cx231xx_v4l2_open,
+ .release = cx231xx_v4l2_close,
+ .read = cx231xx_v4l2_read,
+ .poll = cx231xx_v4l2_poll,
+ .mmap = cx231xx_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device cx231xx_vbi_template;
+
+static const struct video_device cx231xx_video_template = {
+ .fops = &cx231xx_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = cx231xx_v4l2_open,
+ .release = cx231xx_v4l2_close,
+ .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_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device cx231xx_radio_template = {
+ .name = "cx231xx-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/******************************** usb interface ******************************/
+
+static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
+ const struct video_device
+ *template, const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int cx231xx_register_analog_devices(struct cx231xx *dev)
+{
+ int ret;
+
+ cx231xx_info("%s: v4l2 driver version %s\n",
+ dev->name, CX231XX_VERSION);
+
+ /* set default norm */
+ /*dev->norm = cx231xx_video_template.current_norm; */
+ dev->width = norm_maxw(dev);
+ dev->height = norm_maxh(dev);
+ dev->interlaced = 0;
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+
+ /* Set the initial input */
+ video_mux(dev, dev->video_input);
+
+ /* Audio defaults */
+ dev->mute = 1;
+ dev->volume = 0x1f;
+
+ /* enable vbi capturing */
+ /* write code here... */
+
+ /* allocate and fill video video_device struct */
+ dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
+ if (!dev->vdev) {
+ cx231xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+
+ /* register v4l2 video video_device */
+ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (ret) {
+ cx231xx_errdev("unable to register video device (error=%i).\n",
+ ret);
+ return ret;
+ }
+
+ cx231xx_info("%s/0: registered device %s [v4l2]\n",
+ dev->name, video_device_node_name(dev->vdev));
+
+ /* Initialize VBI template */
+ memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
+ sizeof(cx231xx_vbi_template));
+ strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
+
+ /* Allocate and fill vbi video_device struct */
+ dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
+
+ /* register v4l2 vbi video_device */
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]);
+ if (ret < 0) {
+ cx231xx_errdev("unable to register vbi device\n");
+ return ret;
+ }
+
+ cx231xx_info("%s/0: registered device %s\n",
+ dev->name, video_device_node_name(dev->vbi_dev));
+
+ if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
+ dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ cx231xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (ret < 0) {
+ cx231xx_errdev("can't register radio device\n");
+ return ret;
+ }
+ cx231xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
+ }
+
+ cx231xx_info("V4L2 device registered as %s and %s\n",
+ video_device_node_name(dev->vdev),
+ video_device_node_name(dev->vbi_dev));
+
+ return 0;
+}
diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index a89d020de948..a89d020de948 100644
--- a/drivers/media/video/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
new file mode 100644
index 000000000000..834bfecbed73
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -0,0 +1,149 @@
+config DVB_USB_V2
+ tristate "Support for various USB DVB devices v2"
+ depends on DVB_CORE && USB && I2C && RC_CORE
+ help
+ By enabling this you will be able to choose the various supported
+ USB1.1 and USB2.0 DVB devices.
+
+ Almost every USB device needs a firmware, please look into
+ <file:Documentation/dvb/README.dvb-usb>.
+
+ For a complete list of supported USB devices see the LinuxTV DVB Wiki:
+ <http://www.linuxtv.org/wiki/index.php/DVB_USB>
+
+ Say Y if you own a USB DVB device.
+
+config DVB_USB_CYPRESS_FIRMWARE
+ tristate "Cypress firmware helper routines"
+ depends on DVB_USB_V2
+
+config DVB_USB_AF9015
+ tristate "Afatech AF9015 DVB-T USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_AF9013
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MC44S803 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
+
+config DVB_USB_AF9035
+ tristate "Afatech AF9035 DVB-T USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_AF9033
+ select MEDIA_TUNER_TUA9001 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_FC0011 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18218 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Afatech AF9035 based DVB USB receiver.
+
+config DVB_USB_ANYSEE
+ tristate "Anysee DVB-T/C USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Anysee E30, Anysee E30 Plus or
+ Anysee E30 C Plus DVB USB2.0 receiver.
+
+config DVB_USB_AU6610
+ tristate "Alcor Micro AU6610 USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
+
+config DVB_USB_AZ6007
+ tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_USB_CYPRESS_FIRMWARE
+ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the AZ6007 receivers like Terratec H7.
+
+config DVB_USB_CE6230
+ tristate "Intel CE6230 DVB-T USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_ZL10353
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver
+
+config DVB_USB_EC168
+ tristate "E3C EC168 DVB-T USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_EC100
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
+
+config DVB_USB_GL861
+ tristate "Genesys Logic GL861 USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
+ receiver with USB ID 0db0:5581.
+
+config DVB_USB_IT913X
+ tristate "ITE IT913X DVB-T USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_IT913X_FE
+ help
+ Say Y here to support the ITE IT913X DVB-T USB2.0
+
+config DVB_USB_LME2510
+ tristate "LME DM04/QQBOX DVB-S USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_IX2505V if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_M88RS2000 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the LME DM04/QQBOX DVB-S USB2.0
+
+config DVB_USB_MXL111SF
+ tristate "MxL111SF DTV USB2.0 support"
+ depends on DVB_USB_V2
+ select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LG2160 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TVEEPROM
+ help
+ Say Y here to support the MxL111SF USB2.0 DTV receiver.
+
+config DVB_USB_RTL28XXU
+ tristate "Realtek RTL28xxU DVB USB support"
+ depends on DVB_USB_V2 && EXPERIMENTAL
+ select DVB_RTL2830
+ select DVB_RTL2832
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_FC0012 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_FC0013 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_E4000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_FC2580 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Realtek RTL28xxU DVB USB receiver.
+
diff --git a/drivers/media/usb/dvb-usb-v2/Makefile b/drivers/media/usb/dvb-usb-v2/Makefile
new file mode 100644
index 000000000000..b76f58e6c64f
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/Makefile
@@ -0,0 +1,49 @@
+dvb_usb_v2-objs := dvb_usb_core.o dvb_usb_urb.o usb_urb.o
+obj-$(CONFIG_DVB_USB_V2) += dvb_usb_v2.o
+
+dvb_usb_cypress_firmware-objs := cypress_firmware.o
+obj-$(CONFIG_DVB_USB_CYPRESS_FIRMWARE) += dvb_usb_cypress_firmware.o
+
+dvb-usb-af9015-objs := af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
+dvb-usb-af9035-objs := af9035.o
+obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o
+
+dvb-usb-anysee-objs := anysee.o
+obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
+
+dvb-usb-au6610-objs := au6610.o
+obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o
+
+dvb-usb-az6007-objs := az6007.o
+obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
+
+dvb-usb-ce6230-objs := ce6230.o
+obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
+
+dvb-usb-ec168-objs := ec168.o
+obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+
+dvb-usb-it913x-objs := it913x.o
+obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o
+
+dvb-usb-lmedm04-objs := lmedm04.o
+obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o
+
+dvb-usb-gl861-objs := gl861.o
+obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o
+
+dvb-usb-mxl111sf-objs += mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o
+dvb-usb-mxl111sf-objs += mxl111sf-gpio.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
+obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
+
+dvb-usb-rtl28xxu-objs := rtl28xxu.o
+obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+ccflags-y += -I$(srctree)/drivers/media/tuners
+
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.c b/drivers/media/usb/dvb-usb-v2/af9015.c
new file mode 100644
index 000000000000..824f1911ee21
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/af9015.c
@@ -0,0 +1,1454 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "af9015.h"
+
+static int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+#define BUF_LEN 63
+#define REQ_HDR_LEN 8 /* send header size */
+#define ACK_HDR_LEN 2 /* rece header size */
+ struct af9015_state *state = d_to_priv(d);
+ int ret, wlen, rlen;
+ u8 buf[BUF_LEN];
+ u8 write = 1;
+
+ buf[0] = req->cmd;
+ buf[1] = state->seq++;
+ buf[2] = req->i2c_addr;
+ buf[3] = req->addr >> 8;
+ buf[4] = req->addr & 0xff;
+ buf[5] = req->mbox;
+ buf[6] = req->addr_len;
+ buf[7] = req->data_len;
+
+ switch (req->cmd) {
+ case GET_CONFIG:
+ case READ_MEMORY:
+ case RECONNECT_USB:
+ write = 0;
+ break;
+ case READ_I2C:
+ write = 0;
+ buf[2] |= 0x01; /* set I2C direction */
+ case WRITE_I2C:
+ buf[0] = READ_WRITE_I2C;
+ break;
+ case WRITE_MEMORY:
+ if (((req->addr & 0xff00) == 0xff00) ||
+ ((req->addr & 0xff00) == 0xae00))
+ buf[0] = WRITE_VIRTUAL_MEMORY;
+ case WRITE_VIRTUAL_MEMORY:
+ case COPY_FIRMWARE:
+ case DOWNLOAD_FIRMWARE:
+ case BOOT:
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: unknown command=%d\n",
+ KBUILD_MODNAME, req->cmd);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* buffer overflow check */
+ if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) ||
+ (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) {
+ dev_err(&d->udev->dev, "%s: too much data; cmd=%d len=%d\n",
+ KBUILD_MODNAME, req->cmd, req->data_len);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* write receives seq + status = 2 bytes
+ read receives seq + status + data = 2 + N bytes */
+ wlen = REQ_HDR_LEN;
+ rlen = ACK_HDR_LEN;
+ if (write) {
+ wlen += req->data_len;
+ memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len);
+ } else {
+ rlen += req->data_len;
+ }
+
+ /* no ack for these packets */
+ if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+ rlen = 0;
+
+ ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen);
+ if (ret)
+ goto error;
+
+ /* check status */
+ if (rlen && buf[1]) {
+ dev_err(&d->udev->dev, "%s: command failed=%d\n",
+ KBUILD_MODNAME, buf[1]);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (!write)
+ memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len);
+error:
+ return ret;
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+ u8 len)
+{
+ struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+ val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len)
+{
+ struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+ val};
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+ return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+ return af9015_read_regs(d, addr, val, 1);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 val)
+{
+ struct af9015_state *state = d_to_priv(d);
+ struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+ if (addr == state->af9013_config[0].i2c_addr ||
+ addr == state->af9013_config[1].i2c_addr)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+ u8 *val)
+{
+ struct af9015_state *state = d_to_priv(d);
+ struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+ if (addr == state->af9013_config[0].i2c_addr ||
+ addr == state->af9013_config[1].i2c_addr)
+ req.addr_len = 3;
+
+ return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+ int ret;
+ u8 val, mask = 0x01;
+
+ ret = af9015_read_reg(d, addr, &val);
+ if (ret)
+ return ret;
+
+ mask <<= bit;
+ if (op) {
+ /* set bit */
+ val |= mask;
+ } else {
+ /* clear bit */
+ mask ^= 0xff;
+ val &= mask;
+ }
+
+ return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+ return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct af9015_state *state = d_to_priv(d);
+ int ret = 0, i = 0;
+ u16 addr;
+ u8 uninitialized_var(mbox), addr_len;
+ struct req_t req;
+
+/*
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________ ____________ . ____________
+.| uC | | demod | . | tuner |
+.|------------| |------------| . |------------|
+.| AF9015 | | AF9013/5 | . | MXL5003 |
+.| |--+----I2C-------|-----/ -----|-.-----I2C-------| |
+.| | | | addr 0x38 | . | addr 0xc6 |
+.|____________| | |____________| . |____________|
+.................|..............................
+ | ____________ ____________
+ | | demod | | tuner |
+ | |------------| |------------|
+ | | AF9013 | | MXL5003 |
+ +----I2C-------|-----/ -----|-------I2C-------| |
+ | addr 0x3a | | addr 0xc6 |
+ |____________| |____________|
+*/
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (msg[i].addr == state->af9013_config[0].i2c_addr ||
+ msg[i].addr == state->af9013_config[1].i2c_addr) {
+ addr = msg[i].buf[0] << 8;
+ addr += msg[i].buf[1];
+ mbox = msg[i].buf[2];
+ addr_len = 3;
+ } else {
+ addr = msg[i].buf[0];
+ addr_len = 1;
+ /* mbox is don't care in that case */
+ }
+
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].len > 3 || msg[i+1].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+ if (msg[i].addr == state->af9013_config[0].i2c_addr)
+ req.cmd = READ_MEMORY;
+ else
+ req.cmd = READ_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i+1].len;
+ req.data = &msg[i+1].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 2;
+ } else if (msg[i].flags & I2C_M_RD) {
+ if (msg[i].len > 61) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+ if (msg[i].addr == state->af9013_config[0].i2c_addr) {
+ ret = -EINVAL;
+ goto error;
+ }
+ req.cmd = READ_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i].len;
+ req.data = &msg[i].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 1;
+ } else {
+ if (msg[i].len > 21) {
+ ret = -EOPNOTSUPP;
+ goto error;
+ }
+ if (msg[i].addr == state->af9013_config[0].i2c_addr)
+ req.cmd = WRITE_MEMORY;
+ else
+ req.cmd = WRITE_I2C;
+ req.i2c_addr = msg[i].addr;
+ req.addr = addr;
+ req.mbox = mbox;
+ req.addr_len = addr_len;
+ req.data_len = msg[i].len-addr_len;
+ req.data = &msg[i].buf[addr_len];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 1;
+ }
+ if (ret)
+ goto error;
+
+ }
+ ret = i;
+
+error:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+ .master_xfer = af9015_i2c_xfer,
+ .functionality = af9015_i2c_func,
+};
+
+static int af9015_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ int ret;
+ u8 reply;
+ struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ return ret;
+
+ dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply);
+
+ if (reply == 0x02)
+ ret = WARM;
+ else
+ ret = COLD;
+
+ return ret;
+}
+
+static int af9015_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int i, len, remaining, ret;
+ struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+ u16 checksum = 0;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* calc checksum */
+ for (i = 0; i < fw->size; i++)
+ checksum += fw->data[i];
+
+ state->firmware_size = fw->size;
+ state->firmware_checksum = checksum;
+
+ #define FW_ADDR 0x5100 /* firmware start address */
+ #define LEN_MAX 55 /* max packet size */
+ for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
+ len = remaining;
+ if (len > LEN_MAX)
+ len = LEN_MAX;
+
+ req.data_len = len;
+ req.data = (u8 *) &fw->data[fw->size - remaining];
+ req.addr = FW_ADDR + fw->size - remaining;
+
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret) {
+ dev_err(&d->udev->dev,
+ "%s: firmware download failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto error;
+ }
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = BOOT;
+ req.data_len = 0;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret) {
+ dev_err(&d->udev->dev, "%s: firmware boot failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/* hash (and dump) eeprom */
+static int af9015_eeprom_hash(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret, i;
+ static const unsigned int AF9015_EEPROM_SIZE = 256;
+ u8 buf[AF9015_EEPROM_SIZE];
+ struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, NULL};
+
+ /* read eeprom */
+ for (i = 0; i < AF9015_EEPROM_SIZE; i++) {
+ req.addr = i;
+ req.data = &buf[i];
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* calculate checksum */
+ for (i = 0; i < AF9015_EEPROM_SIZE / sizeof(u32); i++) {
+ state->eeprom_sum *= GOLDEN_RATIO_PRIME_32;
+ state->eeprom_sum += le32_to_cpu(((u32 *)buf)[i]);
+ }
+
+ for (i = 0; i < AF9015_EEPROM_SIZE; i += 16)
+ dev_dbg(&d->udev->dev, "%s: %*ph\n", __func__, 16, buf + i);
+
+ dev_dbg(&d->udev->dev, "%s: eeprom sum=%.8x\n",
+ __func__, state->eeprom_sum);
+ return 0;
+err:
+ dev_err(&d->udev->dev, "%s: eeprom failed=%d\n", KBUILD_MODNAME, ret);
+ return ret;
+}
+
+static int af9015_read_config(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ u8 val, i, offset = 0;
+ struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* IR remote controller */
+ req.addr = AF9015_EEPROM_IR_MODE;
+ /* first message will timeout often due to possible hw bug */
+ for (i = 0; i < 4; i++) {
+ ret = af9015_ctrl_msg(d, &req);
+ if (!ret)
+ break;
+ }
+ if (ret)
+ goto error;
+
+ ret = af9015_eeprom_hash(d);
+ if (ret)
+ goto error;
+
+ state->ir_mode = val;
+ dev_dbg(&d->udev->dev, "%s: IR mode=%d\n", __func__, val);
+
+ /* TS mode - one or two receivers */
+ req.addr = AF9015_EEPROM_TS_MODE;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ state->dual_mode = val;
+ dev_dbg(&d->udev->dev, "%s: TS mode=%d\n", __func__, state->dual_mode);
+
+ /* disable 2nd adapter because we don't have PID-filters */
+ if (d->udev->speed == USB_SPEED_FULL)
+ state->dual_mode = 0;
+
+ if (state->dual_mode) {
+ /* read 2nd demodulator I2C address */
+ req.addr = AF9015_EEPROM_DEMOD2_I2C;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ state->af9013_config[1].i2c_addr = val;
+ }
+
+ for (i = 0; i < state->dual_mode + 1; i++) {
+ if (i == 1)
+ offset = AF9015_EEPROM_OFFSET;
+ /* xtal */
+ req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case 0:
+ state->af9013_config[i].clock = 28800000;
+ break;
+ case 1:
+ state->af9013_config[i].clock = 20480000;
+ break;
+ case 2:
+ state->af9013_config[i].clock = 28000000;
+ break;
+ case 3:
+ state->af9013_config[i].clock = 25000000;
+ break;
+ };
+ dev_dbg(&d->udev->dev, "%s: [%d] xtal=%d set clock=%d\n",
+ __func__, i, val,
+ state->af9013_config[i].clock);
+
+ /* IF frequency */
+ req.addr = AF9015_EEPROM_IF1H + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ state->af9013_config[i].if_frequency = val << 8;
+
+ req.addr = AF9015_EEPROM_IF1L + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ state->af9013_config[i].if_frequency += val;
+ state->af9013_config[i].if_frequency *= 1000;
+ dev_dbg(&d->udev->dev, "%s: [%d] IF frequency=%d\n", __func__,
+ i, state->af9013_config[i].if_frequency);
+
+ /* MT2060 IF1 */
+ req.addr = AF9015_EEPROM_MT2060_IF1H + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+ state->mt2060_if1[i] = val << 8;
+ req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+ state->mt2060_if1[i] += val;
+ dev_dbg(&d->udev->dev, "%s: [%d] MT2060 IF1=%d\n", __func__, i,
+ state->mt2060_if1[i]);
+
+ /* tuner */
+ req.addr = AF9015_EEPROM_TUNER_ID1 + offset;
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+ switch (val) {
+ case AF9013_TUNER_ENV77H11D5:
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_UNKNOWN:
+ case AF9013_TUNER_MT2060_2:
+ case AF9013_TUNER_TDA18271:
+ case AF9013_TUNER_QT1010A:
+ case AF9013_TUNER_TDA18218:
+ state->af9013_config[i].spec_inv = 1;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ case AF9013_TUNER_MXL5007T:
+ state->af9013_config[i].spec_inv = 0;
+ break;
+ case AF9013_TUNER_MC44S803:
+ state->af9013_config[i].gpio[1] = AF9013_GPIO_LO;
+ state->af9013_config[i].spec_inv = 1;
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: tuner id=%d not " \
+ "supported, please report!\n",
+ KBUILD_MODNAME, val);
+ return -ENODEV;
+ };
+
+ state->af9013_config[i].tuner = val;
+ dev_dbg(&d->udev->dev, "%s: [%d] tuner id=%d\n",
+ __func__, i, val);
+ }
+
+error:
+ if (ret)
+ dev_err(&d->udev->dev, "%s: eeprom read failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM
+ content :-( Override some wrong values here. Ditto for the
+ AVerTV Red HD+ (A850T) device. */
+ if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_AVERMEDIA &&
+ ((le16_to_cpu(d->udev->descriptor.idProduct) ==
+ USB_PID_AVERMEDIA_A850) ||
+ (le16_to_cpu(d->udev->descriptor.idProduct) ==
+ USB_PID_AVERMEDIA_A850T))) {
+ dev_dbg(&d->udev->dev,
+ "%s: AverMedia A850: overriding config\n",
+ __func__);
+ /* disable dual mode */
+ state->dual_mode = 0;
+
+ /* set correct IF */
+ state->af9013_config[0].if_frequency = 4570000;
+ }
+
+ return ret;
+}
+
+static int af9015_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
+ struct usb_data_stream_properties *stream)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, fe_to_adap(fe)->id);
+
+ if (d->udev->speed == USB_SPEED_FULL)
+ stream->u.bulk.buffersize = TS_USB11_FRAME_SIZE;
+
+ return 0;
+}
+
+static int af9015_get_adapter_count(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ return state->dual_mode + 1;
+}
+
+/* override demod callbacks for resource locking */
+static int af9015_af9013_set_frontend(struct dvb_frontend *fe)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->set_frontend[fe_to_adap(fe)->id](fe);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+/* override demod callbacks for resource locking */
+static int af9015_af9013_read_status(struct dvb_frontend *fe,
+ fe_status_t *status)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->read_status[fe_to_adap(fe)->id](fe, status);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+/* override demod callbacks for resource locking */
+static int af9015_af9013_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->init[fe_to_adap(fe)->id](fe);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+/* override demod callbacks for resource locking */
+static int af9015_af9013_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->sleep[fe_to_adap(fe)->id](fe);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->tuner_init[fe_to_adap(fe)->id](fe);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct af9015_state *state = fe_to_priv(fe);
+
+ if (mutex_lock_interruptible(&state->fe_mutex))
+ return -EAGAIN;
+
+ ret = state->tuner_sleep[fe_to_adap(fe)->id](fe);
+
+ mutex_unlock(&state->fe_mutex);
+
+ return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ u8 fw_params[4];
+ u8 val, i;
+ struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+ fw_params };
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ fw_params[0] = state->firmware_size >> 8;
+ fw_params[1] = state->firmware_size & 0xff;
+ fw_params[2] = state->firmware_checksum >> 8;
+ fw_params[3] = state->firmware_checksum & 0xff;
+
+ /* wait 2nd demodulator ready */
+ msleep(100);
+
+ ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr,
+ 0x98be, &val);
+ if (ret)
+ goto error;
+ else
+ dev_dbg(&d->udev->dev, "%s: firmware status=%02x\n",
+ __func__, val);
+
+ if (val == 0x0c) /* fw is running, no need for download */
+ goto exit;
+
+ /* set I2C master clock to fast (to speed up firmware copy) */
+ ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+ if (ret)
+ goto error;
+
+ msleep(50);
+
+ /* copy firmware */
+ ret = af9015_ctrl_msg(d, &req);
+ if (ret)
+ dev_err(&d->udev->dev, "%s: firmware copy cmd failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ dev_dbg(&d->udev->dev, "%s: firmware copy done\n", __func__);
+
+ /* set I2C master clock back to normal */
+ ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+ if (ret)
+ goto error;
+
+ /* request boot firmware */
+ ret = af9015_write_reg_i2c(d, state->af9013_config[1].i2c_addr,
+ 0xe205, 1);
+ dev_dbg(&d->udev->dev, "%s: firmware boot cmd status=%d\n",
+ __func__, ret);
+ if (ret)
+ goto error;
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ /* check firmware status */
+ ret = af9015_read_reg_i2c(d, state->af9013_config[1].i2c_addr,
+ 0x98be, &val);
+ dev_dbg(&d->udev->dev, "%s: firmware status cmd status=%d " \
+ "firmware status=%02x\n", __func__, ret, val);
+ if (ret)
+ goto error;
+
+ if (val == 0x0c || val == 0x04) /* success or fail */
+ break;
+ }
+
+ if (val == 0x04) {
+ dev_err(&d->udev->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -ETIMEDOUT;
+ } else if (val != 0x0c) {
+ dev_err(&d->udev->dev, "%s: firmware boot timeout\n",
+ KBUILD_MODNAME);
+ ret = -ETIMEDOUT;
+ }
+
+error:
+exit:
+ return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct af9015_state *state = adap_to_priv(adap);
+
+ if (adap->id == 0) {
+ state->af9013_config[0].ts_mode = AF9013_TS_USB;
+ memcpy(state->af9013_config[0].api_version, "\x0\x1\x9\x0", 4);
+ state->af9013_config[0].gpio[0] = AF9013_GPIO_HI;
+ state->af9013_config[0].gpio[3] = AF9013_GPIO_TUNER_ON;
+ } else if (adap->id == 1) {
+ state->af9013_config[1].ts_mode = AF9013_TS_SERIAL;
+ memcpy(state->af9013_config[1].api_version, "\x0\x1\x9\x0", 4);
+ state->af9013_config[1].gpio[0] = AF9013_GPIO_TUNER_ON;
+ state->af9013_config[1].gpio[1] = AF9013_GPIO_LO;
+
+ /* copy firmware to 2nd demodulator */
+ if (state->dual_mode) {
+ ret = af9015_copy_firmware(adap_to_d(adap));
+ if (ret) {
+ dev_err(&adap_to_d(adap)->udev->dev,
+ "%s: firmware copy to 2nd " \
+ "frontend failed, will " \
+ "disable it\n", KBUILD_MODNAME);
+ state->dual_mode = 0;
+ return -ENODEV;
+ }
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ /* attach demodulator */
+ adap->fe[0] = dvb_attach(af9013_attach,
+ &state->af9013_config[adap->id], &adap_to_d(adap)->i2c_adap);
+
+ /*
+ * AF9015 firmware does not like if it gets interrupted by I2C adapter
+ * request on some critical phases. During normal operation I2C adapter
+ * is used only 2nd demodulator and tuner on dual tuner devices.
+ * Override demodulator callbacks and use mutex for limit access to
+ * those "critical" paths to keep AF9015 happy.
+ */
+ if (adap->fe[0]) {
+ state->set_frontend[adap->id] =
+ adap->fe[0]->ops.set_frontend;
+ adap->fe[0]->ops.set_frontend =
+ af9015_af9013_set_frontend;
+
+ state->read_status[adap->id] =
+ adap->fe[0]->ops.read_status;
+ adap->fe[0]->ops.read_status =
+ af9015_af9013_read_status;
+
+ state->init[adap->id] = adap->fe[0]->ops.init;
+ adap->fe[0]->ops.init = af9015_af9013_init;
+
+ state->sleep[adap->id] = adap->fe[0]->ops.sleep;
+ adap->fe[0]->ops.sleep = af9015_af9013_sleep;
+ }
+
+ return adap->fe[0] == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+ .i2c_address = 0xc0,
+ .clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+ .i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+ .gate = TDA18271_GATE_DIGITAL,
+ .small_i2c = TDA18271_16_BYTE_CHUNK_INIT,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_DEFAULT,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_OFF,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static struct mc44s803_config af9015_mc44s803_config = {
+ .i2c_address = 0xc0,
+ .dig_out = 1,
+};
+
+static struct tda18218_config af9015_tda18218_config = {
+ .i2c_address = 0xc0,
+ .i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */
+};
+
+static struct mxl5007t_config af9015_mxl5007t_config = {
+ .xtal_freq_hz = MxL_XTAL_24_MHZ,
+ .if_freq_hz = MxL_IF_4_57_MHZ,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ switch (state->af9013_config[adap->id].tuner) {
+ case AF9013_TUNER_MT2060:
+ case AF9013_TUNER_MT2060_2:
+ ret = dvb_attach(mt2060_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap, &af9015_mt2060_config,
+ state->mt2060_if1[adap->id])
+ == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_QT1010A:
+ ret = dvb_attach(qt1010_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_qt1010_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_TDA18271:
+ ret = dvb_attach(tda18271_attach, adap->fe[0], 0xc0,
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_tda18271_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_TDA18218:
+ ret = dvb_attach(tda18218_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_tda18218_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5003D:
+ ret = dvb_attach(mxl5005s_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5005D:
+ case AF9013_TUNER_MXL5005R:
+ ret = dvb_attach(mxl5005s_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_ENV77H11D5:
+ ret = dvb_attach(dvb_pll_attach, adap->fe[0], 0xc0,
+ &adap_to_d(adap)->i2c_adap,
+ DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MC44S803:
+ ret = dvb_attach(mc44s803_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &af9015_mc44s803_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MXL5007T:
+ ret = dvb_attach(mxl5007t_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+ dev_err(&d->udev->dev, "%s: unknown tuner id=%d\n",
+ KBUILD_MODNAME,
+ state->af9013_config[adap->id].tuner);
+ ret = -ENODEV;
+ }
+
+ if (adap->fe[0]->ops.tuner_ops.init) {
+ state->tuner_init[adap->id] =
+ adap->fe[0]->ops.tuner_ops.init;
+ adap->fe[0]->ops.tuner_ops.init = af9015_tuner_init;
+ }
+
+ if (adap->fe[0]->ops.tuner_ops.sleep) {
+ state->tuner_sleep[adap->id] =
+ adap->fe[0]->ops.tuner_ops.sleep;
+ adap->fe[0]->ops.tuner_ops.sleep = af9015_tuner_sleep;
+ }
+
+ return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ if (onoff)
+ ret = af9015_set_reg_bit(d, 0xd503, 0);
+ else
+ ret = af9015_clear_reg_bit(d, 0xd503, 0);
+
+ return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+ int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+ u8 idx;
+ dev_dbg(&d->udev->dev, "%s: index=%d pid=%04x onoff=%d\n",
+ __func__, index, pid, onoff);
+
+ ret = af9015_write_reg(d, 0xd505, (pid & 0xff));
+ if (ret)
+ goto error;
+
+ ret = af9015_write_reg(d, 0xd506, (pid >> 8));
+ if (ret)
+ goto error;
+
+ idx = ((index & 0x1f) | (1 << 5));
+ ret = af9015_write_reg(d, 0xd504, idx);
+
+error:
+ return ret;
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ u16 frame_size;
+ u8 packet_size;
+ dev_dbg(&d->udev->dev, "%s: USB speed=%d\n", __func__, d->udev->speed);
+
+ if (d->udev->speed == USB_SPEED_FULL) {
+ frame_size = TS_USB11_FRAME_SIZE/4;
+ packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+ } else {
+ frame_size = TS_USB20_FRAME_SIZE/4;
+ packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+ }
+
+ ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+ if (ret)
+ goto error;
+ ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+ if (ret)
+ goto error;
+ if (state->dual_mode) {
+ ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+ if (ret)
+ goto error;
+ }
+ ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+ if (ret)
+ goto error;
+ if (state->dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+ if (ret)
+ goto error;
+ }
+ /* EP4 xfer length */
+ ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+ if (ret)
+ goto error;
+ /* EP5 xfer length */
+ ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+ if (ret)
+ goto error;
+ ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+ if (ret)
+ goto error;
+ if (state->dual_mode) {
+ ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+ if (ret)
+ goto error;
+ }
+
+ /* enable / disable mp2if2 */
+ if (state->dual_mode)
+ ret = af9015_set_reg_bit(d, 0xd50b, 0);
+ else
+ ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+
+error:
+ if (ret)
+ dev_err(&d->udev->dev, "%s: endpoint init failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ return ret;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ mutex_init(&state->fe_mutex);
+
+ /* init RC canary */
+ ret = af9015_write_reg(d, 0x98e9, 0xff);
+ if (ret)
+ goto error;
+
+ ret = af9015_init_endpoint(d);
+ if (ret)
+ goto error;
+
+error:
+ return ret;
+}
+
+struct af9015_rc_setup {
+ unsigned int id;
+ char *rc_codes;
+};
+
+static char *af9015_rc_setup_match(unsigned int id,
+ const struct af9015_rc_setup *table)
+{
+ for (; table->rc_codes; table++)
+ if (table->id == id)
+ return table->rc_codes;
+ return NULL;
+}
+
+static const struct af9015_rc_setup af9015_rc_setup_modparam[] = {
+ { AF9015_REMOTE_A_LINK_DTU_M, RC_MAP_ALINK_DTU_M },
+ { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, RC_MAP_MSI_DIGIVOX_II },
+ { AF9015_REMOTE_MYGICTV_U718, RC_MAP_TOTAL_MEDIA_IN_HAND },
+ { AF9015_REMOTE_DIGITTRADE_DVB_T, RC_MAP_DIGITTRADE },
+ { AF9015_REMOTE_AVERMEDIA_KS, RC_MAP_AVERMEDIA_RM_KS },
+ { }
+};
+
+static const struct af9015_rc_setup af9015_rc_setup_hashes[] = {
+ { 0xb8feb708, RC_MAP_MSI_DIGIVOX_II },
+ { 0xa3703d00, RC_MAP_ALINK_DTU_M },
+ { 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */
+ { 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */
+ { }
+};
+
+static int af9015_rc_query(struct dvb_usb_device *d)
+{
+ struct af9015_state *state = d_to_priv(d);
+ int ret;
+ u8 buf[17];
+
+ /* read registers needed to detect remote controller code */
+ ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf));
+ if (ret)
+ goto error;
+
+ /* If any of these are non-zero, assume invalid data */
+ if (buf[1] || buf[2] || buf[3]) {
+ dev_dbg(&d->udev->dev, "%s: invalid data\n", __func__);
+ return ret;
+ }
+
+ /* Check for repeat of previous code */
+ if ((state->rc_repeat != buf[6] || buf[0]) &&
+ !memcmp(&buf[12], state->rc_last, 4)) {
+ dev_dbg(&d->udev->dev, "%s: key repeated\n", __func__);
+ rc_keydown(d->rc_dev, state->rc_keycode, 0);
+ state->rc_repeat = buf[6];
+ return ret;
+ }
+
+ /* Only process key if canary killed */
+ if (buf[16] != 0xff && buf[0] != 0x01) {
+ dev_dbg(&d->udev->dev, "%s: key pressed %*ph\n",
+ __func__, 4, buf + 12);
+
+ /* Reset the canary */
+ ret = af9015_write_reg(d, 0x98e9, 0xff);
+ if (ret)
+ goto error;
+
+ /* Remember this key */
+ memcpy(state->rc_last, &buf[12], 4);
+ if (buf[14] == (u8) ~buf[15]) {
+ if (buf[12] == (u8) ~buf[13]) {
+ /* NEC */
+ state->rc_keycode = buf[12] << 8 | buf[14];
+ } else {
+ /* NEC extended*/
+ state->rc_keycode = buf[12] << 16 |
+ buf[13] << 8 | buf[14];
+ }
+ } else {
+ /* 32 bit NEC */
+ state->rc_keycode = buf[12] << 24 | buf[13] << 16 |
+ buf[14] << 8 | buf[15];
+ }
+ rc_keydown(d->rc_dev, state->rc_keycode, 0);
+ } else {
+ dev_dbg(&d->udev->dev, "%s: no key press\n", __func__);
+ /* Invalidate last keypress */
+ /* Not really needed, but helps with debug */
+ state->rc_last[2] = state->rc_last[3];
+ }
+
+ state->rc_repeat = buf[6];
+ state->rc_failed = false;
+
+error:
+ if (ret) {
+ dev_warn(&d->udev->dev, "%s: rc query failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ /* allow random errors as dvb-usb will stop polling on error */
+ if (!state->rc_failed)
+ ret = 0;
+
+ state->rc_failed = true;
+ }
+
+ return ret;
+}
+
+static int af9015_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+ struct af9015_state *state = d_to_priv(d);
+ u16 vid = le16_to_cpu(d->udev->descriptor.idVendor);
+
+ if (state->ir_mode == AF9015_IR_MODE_DISABLED)
+ return 0;
+
+ /* try to load remote based module param */
+ if (!rc->map_name)
+ rc->map_name = af9015_rc_setup_match(dvb_usb_af9015_remote,
+ af9015_rc_setup_modparam);
+
+ /* try to load remote based eeprom hash */
+ if (!rc->map_name)
+ rc->map_name = af9015_rc_setup_match(state->eeprom_sum,
+ af9015_rc_setup_hashes);
+
+ /* try to load remote based USB iManufacturer string */
+ if (!rc->map_name && vid == USB_VID_AFATECH) {
+ /* Check USB manufacturer and product strings and try
+ to determine correct remote in case of chip vendor
+ reference IDs are used.
+ DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */
+ char manufacturer[10];
+ memset(manufacturer, 0, sizeof(manufacturer));
+ usb_string(d->udev, d->udev->descriptor.iManufacturer,
+ manufacturer, sizeof(manufacturer));
+ if (!strcmp("MSI", manufacturer)) {
+ /* iManufacturer 1 MSI
+ iProduct 2 MSI K-VOX */
+ rc->map_name = af9015_rc_setup_match(
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ af9015_rc_setup_modparam);
+ }
+ }
+
+ /* load empty to enable rc */
+ if (!rc->map_name)
+ rc->map_name = RC_MAP_EMPTY;
+
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = af9015_rc_query;
+ rc->interval = 500;
+
+ return 0;
+}
+
+/* interface 0 is used by DVB-T receiver and
+ interface 1 is for remote controller (HID) */
+static struct dvb_usb_device_properties af9015_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct af9015_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .identify_state = af9015_identify_state,
+ .firmware = AF9015_FIRMWARE,
+ .download_firmware = af9015_download_firmware,
+
+ .i2c_algo = &af9015_i2c_algo,
+ .read_config = af9015_read_config,
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .init = af9015_init,
+ .get_rc_config = af9015_get_rc_config,
+ .get_stream_config = af9015_get_stream_config,
+
+ .get_adapter_count = af9015_get_adapter_count,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .stream = DVB_USB_STREAM_BULK(0x84, 8, TS_USB20_FRAME_SIZE),
+ }, {
+ .stream = DVB_USB_STREAM_BULK(0x85, 8, TS_USB20_FRAME_SIZE),
+ },
+ },
+};
+
+static const struct usb_device_id af9015_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015,
+ &af9015_props, "Afatech AF9015 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016,
+ &af9015_props, "Afatech AF9015 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD,
+ &af9015_props, "Leadtek WinFast DTV Dongle Gold", RC_MAP_LEADTEK_Y04G0051) },
+ { DVB_USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E,
+ &af9015_props, "Pinnacle PCTV 71e", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U,
+ &af9015_props, "KWorld PlusTV Dual DVB-T Stick (DVB-T 399U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN,
+ &af9015_props, "DigitalNow TinyTwin", RC_MAP_AZUREWAVE_AD_TU700) },
+ { DVB_USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700,
+ &af9015_props, "TwinHan AzureWave AD-TU700(704J)", RC_MAP_AZUREWAVE_AD_TU700) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2,
+ &af9015_props, "TerraTec Cinergy T USB XE", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T,
+ &af9015_props, "KWorld PlusTV Dual DVB-T PCI (DVB-T PC160-2T)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X,
+ &af9015_props, "AVerMedia AVerTV DVB-T Volar X", RC_MAP_AVERMEDIA_M135A) },
+ { DVB_USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380,
+ &af9015_props, "Xtensions XD-380", NULL) },
+ { DVB_USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO,
+ &af9015_props, "MSI DIGIVOX Duo", RC_MAP_MSI_DIGIVOX_III) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2,
+ &af9015_props, "Fujitsu-Siemens Slim Mobile USB DVB-T", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2,
+ &af9015_props, "Telestar Starstick 2", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309,
+ &af9015_props, "AVerMedia A309", NULL) },
+ { DVB_USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III,
+ &af9015_props, "MSI Digi VOX mini III", RC_MAP_MSI_DIGIVOX_III) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U,
+ &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2,
+ &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3,
+ &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT,
+ &af9015_props, "TrekStor DVB-T USB Stick", RC_MAP_TREKSTOR) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850,
+ &af9015_props, "AverMedia AVerTV Volar Black HD (A850)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805,
+ &af9015_props, "AverMedia AVerTV Volar GPS 805 (A805)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU,
+ &af9015_props, "Conceptronic USB2.0 DVB-T CTVDIGRCU V3.0", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810,
+ &af9015_props, "KWorld Digial MC-810", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03,
+ &af9015_props, "Genius TVGo DVB-T03", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2,
+ &af9015_props, "KWorld PlusTV Dual DVB-T Stick (DVB-T 399U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T,
+ &af9015_props, "KWorld PlusTV DVB-T PCI Pro Card (DVB-T PC160-T)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20,
+ &af9015_props, "Sveon STV20 Tuner USB DVB-T HDTV", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2,
+ &af9015_props, "DigitalNow TinyTwin v2", RC_MAP_DIGITALNOW_TINYTWIN) },
+ { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS,
+ &af9015_props, "Leadtek WinFast DTV2000DS", RC_MAP_LEADTEK_Y04G0051) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T,
+ &af9015_props, "KWorld USB DVB-T Stick Mobile (UB383-T)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4,
+ &af9015_props, "KWorld USB DVB-T TV Stick II (VS-DVB-T 395U)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M,
+ &af9015_props, "AverMedia AVerTV Volar M (A815Mac)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC,
+ &af9015_props, "TerraTec Cinergy T Stick RC", RC_MAP_TERRATEC_SLIM_2) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC,
+ &af9015_props, "TerraTec Cinergy T Stick Dual RC", RC_MAP_TERRATEC_SLIM) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T,
+ &af9015_props, "AverMedia AVerTV Red HD+ (A850T)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3,
+ &af9015_props, "DigitalNow TinyTwin v3", RC_MAP_DIGITALNOW_TINYTWIN) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22,
+ &af9015_props, "Sveon STV22 Dual USB DVB-T Tuner HDTV", RC_MAP_MSI_DIGIVOX_III) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, af9015_id_table);
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = af9015_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(af9015_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9015 driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(AF9015_FIRMWARE);
diff --git a/drivers/media/usb/dvb-usb-v2/af9015.h b/drivers/media/usb/dvb-usb-v2/af9015.h
new file mode 100644
index 000000000000..533637dedd23
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/af9015.h
@@ -0,0 +1,151 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 AF9015_H
+#define AF9015_H
+
+#include <linux/hash.h>
+#include "dvb_usb.h"
+#include "af9013.h"
+#include "dvb-pll.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#include "mc44s803.h"
+#include "tda18218.h"
+#include "mxl5007t.h"
+
+#define AF9015_FIRMWARE "dvb-usb-af9015.fw"
+
+/* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0.
+ We use smaller - about 1/4 from the original, 5 and 87. */
+#define TS_PACKET_SIZE 188
+
+#define TS_USB20_PACKET_COUNT 87
+#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT 5
+#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE 512
+#define TS_USB11_MAX_PACKET_SIZE 64
+
+#define AF9015_I2C_EEPROM 0xa0
+#define AF9015_I2C_DEMOD 0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE 0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE 0x31
+#define AF9015_EEPROM_DEMOD2_I2C 0x32
+
+#define AF9015_EEPROM_SAW_BW1 0x35
+#define AF9015_EEPROM_XTAL_TYPE1 0x36
+#define AF9015_EEPROM_SPEC_INV1 0x37
+#define AF9015_EEPROM_IF1L 0x38
+#define AF9015_EEPROM_IF1H 0x39
+#define AF9015_EEPROM_MT2060_IF1L 0x3a
+#define AF9015_EEPROM_MT2060_IF1H 0x3b
+#define AF9015_EEPROM_TUNER_ID1 0x3c
+
+#define AF9015_EEPROM_SAW_BW2 0x45
+#define AF9015_EEPROM_XTAL_TYPE2 0x46
+#define AF9015_EEPROM_SPEC_INV2 0x47
+#define AF9015_EEPROM_IF2L 0x48
+#define AF9015_EEPROM_IF2H 0x49
+#define AF9015_EEPROM_MT2060_IF2L 0x4a
+#define AF9015_EEPROM_MT2060_IF2H 0x4b
+#define AF9015_EEPROM_TUNER_ID2 0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+struct req_t {
+ u8 cmd; /* [0] */
+ /* seq */ /* [1] */
+ u8 i2c_addr; /* [2] */
+ u16 addr; /* [3|4] */
+ u8 mbox; /* [5] */
+ u8 addr_len; /* [6] */
+ u8 data_len; /* [7] */
+ u8 *data;
+};
+
+enum af9015_cmd {
+ GET_CONFIG = 0x10,
+ DOWNLOAD_FIRMWARE = 0x11,
+ BOOT = 0x13,
+ READ_MEMORY = 0x20,
+ WRITE_MEMORY = 0x21,
+ READ_WRITE_I2C = 0x22,
+ COPY_FIRMWARE = 0x23,
+ RECONNECT_USB = 0x5a,
+ WRITE_VIRTUAL_MEMORY = 0x26,
+ GET_IR_CODE = 0x27,
+ READ_I2C,
+ WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+ AF9015_IR_MODE_DISABLED = 0,
+ AF9015_IR_MODE_HID,
+ AF9015_IR_MODE_RLC,
+ AF9015_IR_MODE_RC6,
+ AF9015_IR_MODE_POLLING, /* just guess */
+};
+
+struct af9015_state {
+ u8 ir_mode;
+ u8 rc_repeat;
+ u32 rc_keycode;
+ u8 rc_last[4];
+ bool rc_failed;
+ u8 dual_mode;
+ u8 seq; /* packet sequence number */
+ u16 mt2060_if1[2];
+ u16 firmware_size;
+ u16 firmware_checksum;
+ u32 eeprom_sum;
+ struct af9013_config af9013_config[2];
+
+ /* for demod callback override */
+ int (*set_frontend[2]) (struct dvb_frontend *fe);
+ int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
+ int (*init[2]) (struct dvb_frontend *fe);
+ int (*sleep[2]) (struct dvb_frontend *fe);
+ int (*tuner_init[2]) (struct dvb_frontend *fe);
+ int (*tuner_sleep[2]) (struct dvb_frontend *fe);
+ struct mutex fe_mutex;
+};
+
+enum af9015_remote {
+ AF9015_REMOTE_NONE = 0,
+/* 1 */ AF9015_REMOTE_A_LINK_DTU_M,
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ AF9015_REMOTE_MYGICTV_U718,
+ AF9015_REMOTE_DIGITTRADE_DVB_T,
+/* 5 */ AF9015_REMOTE_AVERMEDIA_KS,
+};
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c
new file mode 100644
index 000000000000..aabd3fc03ea7
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/af9035.c
@@ -0,0 +1,1158 @@
+/*
+ * Afatech AF9035 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "af9035.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static u16 af9035_checksum(const u8 *buf, size_t len)
+{
+ size_t i;
+ u16 checksum = 0;
+
+ for (i = 1; i < len; i++) {
+ if (i % 2)
+ checksum += buf[i] << 8;
+ else
+ checksum += buf[i];
+ }
+ checksum = ~checksum;
+
+ return checksum;
+}
+
+static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
+{
+#define BUF_LEN 64
+#define REQ_HDR_LEN 4 /* send header size */
+#define ACK_HDR_LEN 3 /* rece header size */
+#define CHECKSUM_LEN 2
+#define USB_TIMEOUT 2000
+ struct state *state = d_to_priv(d);
+ int ret, wlen, rlen;
+ u8 buf[BUF_LEN];
+ u16 checksum, tmp_checksum;
+
+ /* buffer overflow check */
+ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) ||
+ req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) {
+ dev_err(&d->udev->dev, "%s: too much data wlen=%d rlen=%d\n",
+ __func__, req->wlen, req->rlen);
+ return -EINVAL;
+ }
+
+ buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1;
+ buf[1] = req->mbox;
+ buf[2] = req->cmd;
+ buf[3] = state->seq++;
+ memcpy(&buf[REQ_HDR_LEN], req->wbuf, req->wlen);
+
+ wlen = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN;
+ rlen = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN;
+
+ /* calc and add checksum */
+ checksum = af9035_checksum(buf, buf[0] - 1);
+ buf[buf[0] - 1] = (checksum >> 8);
+ buf[buf[0] - 0] = (checksum & 0xff);
+
+ /* no ack for these packets */
+ if (req->cmd == CMD_FW_DL)
+ rlen = 0;
+
+ ret = dvb_usbv2_generic_rw(d, buf, wlen, buf, rlen);
+ if (ret)
+ goto err;
+
+ /* no ack for those packets */
+ if (req->cmd == CMD_FW_DL)
+ goto exit;
+
+ /* verify checksum */
+ checksum = af9035_checksum(buf, rlen - 2);
+ tmp_checksum = (buf[rlen - 2] << 8) | buf[rlen - 1];
+ if (tmp_checksum != checksum) {
+ dev_err(&d->udev->dev, "%s: command=%02x checksum mismatch " \
+ "(%04x != %04x)\n", KBUILD_MODNAME, req->cmd,
+ tmp_checksum, checksum);
+ ret = -EIO;
+ goto err;
+ }
+
+ /* check status */
+ if (buf[2]) {
+ dev_dbg(&d->udev->dev, "%s: command=%02x failed fw error=%d\n",
+ __func__, req->cmd, buf[2]);
+ ret = -EIO;
+ goto err;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (req->rlen)
+ memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen);
+
+exit:
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+/* write multiple registers */
+static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
+{
+ u8 wbuf[6 + len];
+ u8 mbox = (reg >> 16) & 0xff;
+ struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
+
+ wbuf[0] = len;
+ wbuf[1] = 2;
+ wbuf[2] = 0;
+ wbuf[3] = 0;
+ wbuf[4] = (reg >> 8) & 0xff;
+ wbuf[5] = (reg >> 0) & 0xff;
+ memcpy(&wbuf[6], val, len);
+
+ return af9035_ctrl_msg(d, &req);
+}
+
+/* read multiple registers */
+static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
+{
+ u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff };
+ u8 mbox = (reg >> 16) & 0xff;
+ struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val };
+
+ return af9035_ctrl_msg(d, &req);
+}
+
+/* write single register */
+static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val)
+{
+ return af9035_wr_regs(d, reg, &val, 1);
+}
+
+/* read single register */
+static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val)
+{
+ return af9035_rd_regs(d, reg, val, 1);
+}
+
+/* write single register with mask */
+static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = af9035_rd_regs(d, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return af9035_wr_regs(d, reg, &val, 1);
+}
+
+static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct state *state = d_to_priv(d);
+ int ret;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ /*
+ * I2C sub header is 5 bytes long. Meaning of those bytes are:
+ * 0: data len
+ * 1: I2C addr << 1
+ * 2: reg addr len
+ * byte 3 and 4 can be used as reg addr
+ * 3: reg addr MSB
+ * used when reg addr len is set to 2
+ * 4: reg addr LSB
+ * used when reg addr len is set to 1 or 2
+ *
+ * For the simplify we do not use register addr at all.
+ * NOTE: As a firmware knows tuner type there is very small possibility
+ * there could be some tuner I2C hacks done by firmware and this may
+ * lead problems if firmware expects those bytes are used.
+ */
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 40 || msg[1].len > 40) {
+ /* TODO: correct limits > 40 */
+ ret = -EOPNOTSUPP;
+ } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
+ /* integrated demod */
+ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
+ msg[0].buf[2];
+ ret = af9035_rd_regs(d, reg, &msg[1].buf[0],
+ msg[1].len);
+ } else {
+ /* I2C */
+ u8 buf[5 + msg[0].len];
+ struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
+ buf, msg[1].len, msg[1].buf };
+ buf[0] = msg[1].len;
+ buf[1] = msg[0].addr << 1;
+ buf[2] = 0x00; /* reg addr len */
+ buf[3] = 0x00; /* reg addr MSB */
+ buf[4] = 0x00; /* reg addr LSB */
+ memcpy(&buf[5], msg[0].buf, msg[0].len);
+ ret = af9035_ctrl_msg(d, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 40) {
+ /* TODO: correct limits > 40 */
+ ret = -EOPNOTSUPP;
+ } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
+ /* integrated demod */
+ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
+ msg[0].buf[2];
+ ret = af9035_wr_regs(d, reg, &msg[0].buf[3],
+ msg[0].len - 3);
+ } else {
+ /* I2C */
+ u8 buf[5 + msg[0].len];
+ struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf,
+ 0, NULL };
+ buf[0] = msg[0].len;
+ buf[1] = msg[0].addr << 1;
+ buf[2] = 0x00; /* reg addr len */
+ buf[3] = 0x00; /* reg addr MSB */
+ buf[4] = 0x00; /* reg addr LSB */
+ memcpy(&buf[5], msg[0].buf, msg[0].len);
+ ret = af9035_ctrl_msg(d, &req);
+ }
+ } else {
+ /*
+ * We support only two kind of I2C transactions:
+ * 1) 1 x read + 1 x write
+ * 2) 1 x write
+ */
+ ret = -EOPNOTSUPP;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (ret < 0)
+ return ret;
+ else
+ return num;
+}
+
+static u32 af9035_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9035_i2c_algo = {
+ .master_xfer = af9035_i2c_master_xfer,
+ .functionality = af9035_i2c_functionality,
+};
+
+static int af9035_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ int ret;
+ u8 wbuf[1] = { 1 };
+ u8 rbuf[4];
+ struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf,
+ sizeof(rbuf), rbuf };
+
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&d->udev->dev, "%s: reply=%*ph\n", __func__, 4, rbuf);
+ if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])
+ ret = WARM;
+ else
+ ret = COLD;
+
+ return ret;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ int ret, i, j, len;
+ u8 wbuf[1];
+ u8 rbuf[4];
+ struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL };
+ struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+ u8 hdr_core;
+ u16 hdr_addr, hdr_data_len, hdr_checksum;
+ #define MAX_DATA 58
+ #define HDR_SIZE 7
+
+ /*
+ * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info!
+ *
+ * byte 0: MCS 51 core
+ * There are two inside the AF9035 (1=Link and 2=OFDM) with separate
+ * address spaces
+ * byte 1-2: Big endian destination address
+ * byte 3-4: Big endian number of data bytes following the header
+ * byte 5-6: Big endian header checksum, apparently ignored by the chip
+ * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256)
+ */
+
+ for (i = fw->size; i > HDR_SIZE;) {
+ hdr_core = fw->data[fw->size - i + 0];
+ hdr_addr = fw->data[fw->size - i + 1] << 8;
+ hdr_addr |= fw->data[fw->size - i + 2] << 0;
+ hdr_data_len = fw->data[fw->size - i + 3] << 8;
+ hdr_data_len |= fw->data[fw->size - i + 4] << 0;
+ hdr_checksum = fw->data[fw->size - i + 5] << 8;
+ hdr_checksum |= fw->data[fw->size - i + 6] << 0;
+
+ dev_dbg(&d->udev->dev, "%s: core=%d addr=%04x data_len=%d " \
+ "checksum=%04x\n", __func__, hdr_core, hdr_addr,
+ hdr_data_len, hdr_checksum);
+
+ if (((hdr_core != 1) && (hdr_core != 2)) ||
+ (hdr_data_len > i)) {
+ dev_dbg(&d->udev->dev, "%s: bad firmware\n", __func__);
+ break;
+ }
+
+ /* download begin packet */
+ req.cmd = CMD_FW_DL_BEGIN;
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ /* download firmware packet(s) */
+ for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) {
+ len = j;
+ if (len > MAX_DATA)
+ len = MAX_DATA;
+ req_fw_dl.wlen = len;
+ req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i +
+ HDR_SIZE + hdr_data_len - j];
+ ret = af9035_ctrl_msg(d, &req_fw_dl);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* download end packet */
+ req.cmd = CMD_FW_DL_END;
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ i -= hdr_data_len + HDR_SIZE;
+
+ dev_dbg(&d->udev->dev, "%s: data uploaded=%zu\n",
+ __func__, fw->size - i);
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = CMD_FW_BOOT;
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ /* ensure firmware starts */
+ wbuf[0] = 1;
+ ret = af9035_ctrl_msg(d, &req_fw_ver);
+ if (ret < 0)
+ goto err;
+
+ if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
+ dev_err(&d->udev->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d",
+ KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_download_firmware_it9135(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ int ret, i, i_prev;
+ u8 wbuf[1];
+ u8 rbuf[4];
+ struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+ #define HDR_SIZE 7
+
+ /*
+ * There seems to be following firmware header. Meaning of bytes 0-3
+ * is unknown.
+ *
+ * 0: 3
+ * 1: 0, 1
+ * 2: 0
+ * 3: 1, 2, 3
+ * 4: addr MSB
+ * 5: addr LSB
+ * 6: count of data bytes ?
+ */
+
+ for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) {
+ if (i == fw->size ||
+ (fw->data[i + 0] == 0x03 &&
+ (fw->data[i + 1] == 0x00 ||
+ fw->data[i + 1] == 0x01) &&
+ fw->data[i + 2] == 0x00)) {
+ req_fw_dl.wlen = i - i_prev;
+ req_fw_dl.wbuf = (u8 *) &fw->data[i_prev];
+ i_prev = i;
+ ret = af9035_ctrl_msg(d, &req_fw_dl);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&d->udev->dev, "%s: data uploaded=%d\n",
+ __func__, i);
+ }
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = CMD_FW_BOOT;
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ /* ensure firmware starts */
+ wbuf[0] = 1;
+ ret = af9035_ctrl_msg(d, &req_fw_ver);
+ if (ret < 0)
+ goto err;
+
+ if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
+ dev_err(&d->udev->dev, "%s: firmware did not run\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ dev_info(&d->udev->dev, "%s: firmware version=%d.%d.%d.%d",
+ KBUILD_MODNAME, rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_read_config(struct dvb_usb_device *d)
+{
+ struct state *state = d_to_priv(d);
+ int ret, i, eeprom_shift = 0;
+ u8 tmp;
+ u16 tmp16;
+
+ /* check if there is dual tuners */
+ ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp);
+ if (ret < 0)
+ goto err;
+
+ state->dual_mode = tmp;
+ dev_dbg(&d->udev->dev, "%s: dual mode=%d\n",
+ __func__, state->dual_mode);
+
+ for (i = 0; i < state->dual_mode + 1; i++) {
+ /* tuner */
+ ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ state->af9033_config[i].tuner = tmp;
+ dev_dbg(&d->udev->dev, "%s: [%d]tuner=%02x\n",
+ __func__, i, tmp);
+
+ switch (tmp) {
+ case AF9033_TUNER_TUA9001:
+ case AF9033_TUNER_FC0011:
+ case AF9033_TUNER_MXL5007T:
+ case AF9033_TUNER_TDA18218:
+ case AF9033_TUNER_FC2580:
+ state->af9033_config[i].spec_inv = 1;
+ break;
+ default:
+ dev_warn(&d->udev->dev, "%s: tuner id=%02x not " \
+ "supported, please report!",
+ KBUILD_MODNAME, tmp);
+ };
+
+ /* tuner IF frequency */
+ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp16 = tmp;
+
+ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp16 |= tmp << 8;
+
+ dev_dbg(&d->udev->dev, "%s: [%d]IF=%d\n", __func__, i, tmp16);
+
+ eeprom_shift = 0x10; /* shift for the 2nd tuner params */
+ }
+
+ /* get demod clock */
+ ret = af9035_rd_reg(d, 0x00d800, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp = (tmp >> 0) & 0x0f;
+
+ for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++)
+ state->af9033_config[i].clock = clock_lut[tmp];
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_read_config_it9135(struct dvb_usb_device *d)
+{
+ struct state *state = d_to_priv(d);
+ int ret, i;
+ u8 tmp;
+
+ state->dual_mode = false;
+
+ /* get demod clock */
+ ret = af9035_rd_reg(d, 0x00d800, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp = (tmp >> 0) & 0x0f;
+
+ for (i = 0; i < ARRAY_SIZE(state->af9033_config); i++)
+ state->af9033_config[i].clock = clock_lut_it9135[tmp];
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_tua9001_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+
+ /*
+ * CEN always enabled by hardware wiring
+ * RESETN GPIOT3
+ * RXEN GPIOT2
+ */
+
+ switch (cmd) {
+ case TUA9001_CMD_RESETN:
+ if (arg)
+ val = 0x00;
+ else
+ val = 0x01;
+
+ ret = af9035_wr_reg_mask(d, 0x00d8e7, val, 0x01);
+ if (ret < 0)
+ goto err;
+ break;
+ case TUA9001_CMD_RXEN:
+ if (arg)
+ val = 0x01;
+ else
+ val = 0x00;
+
+ ret = af9035_wr_reg_mask(d, 0x00d8eb, val, 0x01);
+ if (ret < 0)
+ goto err;
+ break;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+
+static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case FC0011_FE_CALLBACK_POWER:
+ /* Tuner enable */
+ ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ /* LED */
+ ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 50000);
+ break;
+ case FC0011_FE_CALLBACK_RESET:
+ ret = af9035_wr_reg(d, 0xd8e9, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(d, 0xd8e8, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(d, 0xd8e7, 1);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 20000);
+
+ ret = af9035_wr_reg(d, 0xd8e7, 0);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 20000);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+ struct state *state = d_to_priv(d);
+
+ switch (state->af9033_config[0].tuner) {
+ case AF9033_TUNER_FC0011:
+ return af9035_fc0011_tuner_callback(d, cmd, arg);
+ case AF9033_TUNER_TUA9001:
+ return af9035_tua9001_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int af9035_frontend_callback(void *adapter_priv, int component,
+ int cmd, int arg)
+{
+ struct i2c_adapter *adap = adapter_priv;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+ dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n",
+ __func__, component, cmd, arg);
+
+ switch (component) {
+ case DVB_FRONTEND_COMPONENT_TUNER:
+ return af9035_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+
+ if (!state->af9033_config[adap->id].tuner) {
+ /* unsupported tuner */
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (adap->id == 0) {
+ state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB;
+ state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL;
+
+ ret = af9035_wr_reg(d, 0x00417f,
+ state->af9033_config[1].i2c_addr);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(d, 0x00d81a,
+ state->dual_mode);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* attach demodulator */
+ adap->fe[0] = dvb_attach(af9033_attach,
+ &state->af9033_config[adap->id], &d->i2c_adap);
+ if (adap->fe[0] == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* disable I2C-gate */
+ adap->fe[0]->ops.i2c_gate_ctrl = NULL;
+ adap->fe[0]->callback = af9035_frontend_callback;
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static struct tua9001_config af9035_tua9001_config = {
+ .i2c_addr = 0x60,
+};
+
+static const struct fc0011_config af9035_fc0011_config = {
+ .i2c_address = 0x60,
+};
+
+static struct mxl5007t_config af9035_mxl5007t_config = {
+ .xtal_freq_hz = MxL_XTAL_24_MHZ,
+ .if_freq_hz = MxL_IF_4_57_MHZ,
+ .invert_if = 0,
+ .loop_thru_enable = 0,
+ .clk_out_enable = 0,
+ .clk_out_amp = MxL_CLKOUT_AMP_0_94V,
+};
+
+static struct tda18218_config af9035_tda18218_config = {
+ .i2c_address = 0x60,
+ .i2c_wr_max = 21,
+};
+
+static const struct fc2580_config af9035_fc2580_config = {
+ .i2c_addr = 0x56,
+ .clock = 16384000,
+};
+
+static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+ struct dvb_frontend *fe;
+
+ switch (state->af9033_config[adap->id].tuner) {
+ case AF9033_TUNER_TUA9001:
+ /* AF9035 gpiot3 = TUA9001 RESETN
+ AF9035 gpiot2 = TUA9001 RXEN */
+
+ /* configure gpiot2 and gpiot2 as output */
+ ret = af9035_wr_reg_mask(d, 0x00d8ec, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0x00d8ed, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0x00d8e8, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0x00d8e9, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /* attach tuner */
+ fe = dvb_attach(tua9001_attach, adap->fe[0],
+ &d->i2c_adap, &af9035_tua9001_config);
+ break;
+ case AF9033_TUNER_FC0011:
+ fe = dvb_attach(fc0011_attach, adap->fe[0],
+ &d->i2c_adap, &af9035_fc0011_config);
+ break;
+ case AF9033_TUNER_MXL5007T:
+ ret = af9035_wr_reg(d, 0x00d8e0, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8e1, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8df, 0);
+ if (ret < 0)
+ goto err;
+
+ msleep(30);
+
+ ret = af9035_wr_reg(d, 0x00d8df, 1);
+ if (ret < 0)
+ goto err;
+
+ msleep(300);
+
+ ret = af9035_wr_reg(d, 0x00d8c0, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8c1, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8bf, 0);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8b4, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8b5, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(d, 0x00d8b3, 1);
+ if (ret < 0)
+ goto err;
+
+ /* attach tuner */
+ fe = dvb_attach(mxl5007t_attach, adap->fe[0],
+ &d->i2c_adap, 0x60, &af9035_mxl5007t_config);
+ break;
+ case AF9033_TUNER_TDA18218:
+ /* attach tuner */
+ fe = dvb_attach(tda18218_attach, adap->fe[0],
+ &d->i2c_adap, &af9035_tda18218_config);
+ break;
+ case AF9033_TUNER_FC2580:
+ /* Tuner enable using gpiot2_o, gpiot2_en and gpiot2_on */
+ ret = af9035_wr_reg_mask(d, 0xd8eb, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ec, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ed, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 50000);
+ /* attach tuner */
+ fe = dvb_attach(fc2580_attach, adap->fe[0],
+ &d->i2c_adap, &af9035_fc2580_config);
+ break;
+ default:
+ fe = NULL;
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_init(struct dvb_usb_device *d)
+{
+ struct state *state = d_to_priv(d);
+ int ret, i;
+ u16 frame_size = 87 * 188 / 4;
+ u8 packet_size = 512 / 4;
+ struct reg_val_mask tab[] = {
+ { 0x80f99d, 0x01, 0x01 },
+ { 0x80f9a4, 0x01, 0x01 },
+ { 0x00dd11, 0x00, 0x20 },
+ { 0x00dd11, 0x00, 0x40 },
+ { 0x00dd13, 0x00, 0x20 },
+ { 0x00dd13, 0x00, 0x40 },
+ { 0x00dd11, 0x20, 0x20 },
+ { 0x00dd88, (frame_size >> 0) & 0xff, 0xff},
+ { 0x00dd89, (frame_size >> 8) & 0xff, 0xff},
+ { 0x00dd0c, packet_size, 0xff},
+ { 0x00dd11, state->dual_mode << 6, 0x40 },
+ { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff},
+ { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff},
+ { 0x00dd0d, packet_size, 0xff },
+ { 0x80f9a3, 0x00, 0x01 },
+ { 0x80f9cd, 0x00, 0x01 },
+ { 0x80f99d, 0x00, 0x01 },
+ { 0x80f9a4, 0x00, 0x01 },
+ };
+
+ dev_dbg(&d->udev->dev, "%s: USB speed=%d frame_size=%04x " \
+ "packet_size=%02x\n", __func__,
+ d->udev->speed, frame_size, packet_size);
+
+ /* init endpoints */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_rc_query(struct dvb_usb_device *d)
+{
+ unsigned int key;
+ unsigned char b[4];
+ int ret;
+ struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b };
+
+ ret = af9035_ctrl_msg(d, &req);
+ if (ret < 0)
+ goto err;
+
+ if ((b[2] + b[3]) == 0xff) {
+ if ((b[0] + b[1]) == 0xff) {
+ /* NEC */
+ key = b[0] << 8 | b[2];
+ } else {
+ /* ext. NEC */
+ key = b[0] << 16 | b[1] << 8 | b[2];
+ }
+ } else {
+ key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
+ }
+
+ rc_keydown(d->rc_dev, key, 0);
+
+err:
+ /* ignore errors */
+ return 0;
+}
+
+static int af9035_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+ int ret;
+ u8 tmp;
+
+ ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&d->udev->dev, "%s: ir_mode=%02x\n", __func__, tmp);
+
+ /* don't activate rc if in HID mode or if not available */
+ if (tmp == 5) {
+ ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp);
+ if (ret < 0)
+ goto err;
+
+ dev_dbg(&d->udev->dev, "%s: ir_type=%02x\n", __func__, tmp);
+
+ switch (tmp) {
+ case 0: /* NEC */
+ default:
+ rc->allowed_protos = RC_TYPE_NEC;
+ break;
+ case 1: /* RC6 */
+ rc->allowed_protos = RC_TYPE_RC6;
+ break;
+ }
+
+ rc->query = af9035_rc_query;
+ rc->interval = 500;
+
+ /* load empty to enable rc */
+ if (!rc->map_name)
+ rc->map_name = RC_MAP_EMPTY;
+ }
+
+ return 0;
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+/* interface 0 is used by DVB-T receiver and
+ interface 1 is for remote controller (HID) */
+static const struct dvb_usb_device_properties af9035_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .identify_state = af9035_identify_state,
+ .firmware = AF9035_FIRMWARE_AF9035,
+ .download_firmware = af9035_download_firmware,
+
+ .i2c_algo = &af9035_i2c_algo,
+ .read_config = af9035_read_config,
+ .frontend_attach = af9035_frontend_attach,
+ .tuner_attach = af9035_tuner_attach,
+ .init = af9035_init,
+ .get_rc_config = af9035_get_rc_config,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188),
+ }, {
+ .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188),
+ },
+ },
+};
+
+static const struct dvb_usb_device_properties it9135_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .identify_state = af9035_identify_state,
+ .firmware = AF9035_FIRMWARE_IT9135,
+ .download_firmware = af9035_download_firmware_it9135,
+
+ .i2c_algo = &af9035_i2c_algo,
+ .read_config = af9035_read_config_it9135,
+ .frontend_attach = af9035_frontend_attach,
+ .tuner_attach = af9035_tuner_attach,
+ .init = af9035_init,
+ .get_rc_config = af9035_get_rc_config,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x84, 6, 87 * 188),
+ }, {
+ .stream = DVB_USB_STREAM_BULK(0x85, 6, 87 * 188),
+ },
+ },
+};
+
+static const struct usb_device_id af9035_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035,
+ &af9035_props, "Afatech AF9035 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000,
+ &af9035_props, "Afatech AF9035 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001,
+ &af9035_props, "Afatech AF9035 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002,
+ &af9035_props, "Afatech AF9035 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003,
+ &af9035_props, "Afatech AF9035 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK,
+ &af9035_props, "TerraTec Cinergy T Stick", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835,
+ &af9035_props, "AVerMedia AVerTV Volar HD/PRO (A835)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835,
+ &af9035_props, "AVerMedia AVerTV Volar HD/PRO (A835)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867,
+ &af9035_props, "AVerMedia HD Volar (A867)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867,
+ &af9035_props, "AVerMedia HD Volar (A867)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR,
+ &af9035_props, "AVerMedia Twinstar (A825)", NULL) },
+ { DVB_USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100MINI_PLUS,
+ &af9035_props, "Asus U3100Mini Plus", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, af9035_id_table);
+
+static struct usb_driver af9035_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = af9035_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(af9035_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9035 driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(AF9035_FIRMWARE_AF9035);
+MODULE_FIRMWARE(AF9035_FIRMWARE_IT9135);
diff --git a/drivers/media/usb/dvb-usb-v2/af9035.h b/drivers/media/usb/dvb-usb-v2/af9035.h
new file mode 100644
index 000000000000..75ef1ec13fbf
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/af9035.h
@@ -0,0 +1,115 @@
+/*
+ * Afatech AF9035 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9035_H
+#define AF9035_H
+
+#include "dvb_usb.h"
+#include "af9033.h"
+#include "tua9001.h"
+#include "fc0011.h"
+#include "mxl5007t.h"
+#include "tda18218.h"
+#include "fc2580.h"
+
+struct reg_val {
+ u32 reg;
+ u8 val;
+};
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct usb_req {
+ u8 cmd;
+ u8 mbox;
+ u8 wlen;
+ u8 *wbuf;
+ u8 rlen;
+ u8 *rbuf;
+};
+
+struct state {
+ u8 seq; /* packet sequence number */
+ bool dual_mode;
+
+ struct af9033_config af9033_config[2];
+};
+
+u32 clock_lut[] = {
+ 20480000, /* FPGA */
+ 16384000, /* 16.38 MHz */
+ 20480000, /* 20.48 MHz */
+ 36000000, /* 36.00 MHz */
+ 30000000, /* 30.00 MHz */
+ 26000000, /* 26.00 MHz */
+ 28000000, /* 28.00 MHz */
+ 32000000, /* 32.00 MHz */
+ 34000000, /* 34.00 MHz */
+ 24000000, /* 24.00 MHz */
+ 22000000, /* 22.00 MHz */
+ 12000000, /* 12.00 MHz */
+};
+
+u32 clock_lut_it9135[] = {
+ 12000000, /* 12.00 MHz */
+ 20480000, /* 20.48 MHz */
+ 36000000, /* 36.00 MHz */
+ 30000000, /* 30.00 MHz */
+ 26000000, /* 26.00 MHz */
+ 28000000, /* 28.00 MHz */
+ 32000000, /* 32.00 MHz */
+ 34000000, /* 34.00 MHz */
+ 24000000, /* 24.00 MHz */
+ 22000000, /* 22.00 MHz */
+};
+
+#define AF9035_FIRMWARE_AF9035 "dvb-usb-af9035-02.fw"
+#define AF9035_FIRMWARE_IT9135 "dvb-usb-it9135-01.fw"
+
+/* EEPROM locations */
+#define EEPROM_IR_MODE 0x430d
+#define EEPROM_DUAL_MODE 0x4326
+#define EEPROM_IR_TYPE 0x4329
+#define EEPROM_1_IFFREQ_L 0x432d
+#define EEPROM_1_IFFREQ_H 0x432e
+#define EEPROM_1_TUNER_ID 0x4331
+#define EEPROM_2_IFFREQ_L 0x433d
+#define EEPROM_2_IFFREQ_H 0x433e
+#define EEPROM_2_TUNER_ID 0x4341
+
+/* USB commands */
+#define CMD_MEM_RD 0x00
+#define CMD_MEM_WR 0x01
+#define CMD_I2C_RD 0x02
+#define CMD_I2C_WR 0x03
+#define CMD_IR_GET 0x18
+#define CMD_FW_DL 0x21
+#define CMD_FW_QUERYINFO 0x22
+#define CMD_FW_BOOT 0x23
+#define CMD_FW_DL_BEGIN 0x24
+#define CMD_FW_DL_END 0x25
+#define CMD_FW_SCATTER_WR 0x29
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.c b/drivers/media/usb/dvb-usb-v2/anysee.c
new file mode 100644
index 000000000000..ec540140c810
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/anysee.c
@@ -0,0 +1,1327 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
+ * module registers serial interface that can be used to communicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#include "anysee.h"
+#include "dvb-pll.h"
+#include "tda1002x.h"
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "zl10353.h"
+#include "tda18212.h"
+#include "cx24116.h"
+#include "stv0900.h"
+#include "stv6110.h"
+#include "isl6423.h"
+#include "cxd2820r.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+static DEFINE_MUTEX(anysee_usb_mutex);
+
+static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
+ u8 *rbuf, u8 rlen)
+{
+ struct anysee_state *state = d_to_priv(d);
+ int act_len, ret, i;
+ u8 buf[64];
+
+ memcpy(&buf[0], sbuf, slen);
+ buf[60] = state->seq++;
+
+ mutex_lock(&anysee_usb_mutex);
+
+ dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, slen, buf);
+
+ /* We need receive one message more after dvb_usb_generic_rw due
+ to weird transaction flow, which is 1 x send + 2 x receive. */
+ ret = dvb_usbv2_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf));
+ if (ret)
+ goto error_unlock;
+
+ /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32
+ * (EPIPE, Broken pipe). Function supports currently msleep() as a
+ * parameter but I would not like to use it, since according to
+ * Documentation/timers/timers-howto.txt it should not be used such
+ * short, under < 20ms, sleeps. Repeating failed message would be
+ * better choice as not to add unwanted delays...
+ * Fixing that correctly is one of those or both;
+ * 1) use repeat if possible
+ * 2) add suitable delay
+ */
+
+ /* get answer, retry few times if error returned */
+ for (i = 0; i < 3; i++) {
+ /* receive 2nd answer */
+ ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
+ d->props->generic_bulk_ctrl_endpoint), buf, sizeof(buf),
+ &act_len, 2000);
+
+ if (ret) {
+ dev_dbg(&d->udev->dev, "%s: recv bulk message " \
+ "failed=%d\n", __func__, ret);
+ } else {
+ dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__,
+ rlen, buf);
+
+ if (buf[63] != 0x4f)
+ dev_dbg(&d->udev->dev, "%s: cmd failed\n",
+ __func__);
+
+ break;
+ }
+ }
+
+ if (ret) {
+ /* all retries failed, it is fatal */
+ dev_err(&d->udev->dev, "%s: recv bulk message failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto error_unlock;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (rbuf && rlen)
+ memcpy(rbuf, buf, rlen);
+
+error_unlock:
+ mutex_unlock(&anysee_usb_mutex);
+
+ return ret;
+}
+
+static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01};
+ int ret;
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1);
+ dev_dbg(&d->udev->dev, "%s: reg=%04x val=%02x\n", __func__, reg, *val);
+ return ret;
+}
+
+static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val};
+ dev_dbg(&d->udev->dev, "%s: reg=%04x val=%02x\n", __func__, reg, val);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+/* write single register with mask */
+static int anysee_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = anysee_read_reg(d, reg, &tmp);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return anysee_write_reg(d, reg, val);
+}
+
+/* read single register with mask */
+static int anysee_rd_reg_mask(struct dvb_usb_device *d, u16 reg, u8 *val,
+ u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = anysee_read_reg(d, reg, &tmp);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id)
+{
+ u8 buf[] = {CMD_GET_HW_INFO};
+ return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3);
+}
+
+static int anysee_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00};
+ dev_dbg(&fe_to_d(fe)->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+ return anysee_ctrl_msg(fe_to_d(fe), buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval)
+{
+ u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval};
+ dev_dbg(&d->udev->dev, "%s: state=%d interval=%d\n", __func__,
+ mode, interval);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+ u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff};
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+/* I2C */
+static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0, inc, i = 0;
+ u8 buf[52]; /* 4 + 48 (I2C WR USB command header + I2C WR max) */
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].len > 2 || msg[i+1].len > 60) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ buf[0] = CMD_I2C_READ;
+ buf[1] = (msg[i].addr << 1) | 0x01;
+ buf[2] = msg[i].buf[0];
+ buf[3] = msg[i].buf[1];
+ buf[4] = msg[i].len-1;
+ buf[5] = msg[i+1].len;
+ ret = anysee_ctrl_msg(d, buf, 6, msg[i+1].buf,
+ msg[i+1].len);
+ inc = 2;
+ } else {
+ if (msg[i].len > 48) {
+ ret = -EOPNOTSUPP;
+ break;
+ }
+ buf[0] = CMD_I2C_WRITE;
+ buf[1] = (msg[i].addr << 1);
+ buf[2] = msg[i].len;
+ buf[3] = 0x01;
+ memcpy(&buf[4], msg[i].buf, msg[i].len);
+ ret = anysee_ctrl_msg(d, buf, 4 + msg[i].len, NULL, 0);
+ inc = 1;
+ }
+ if (ret)
+ break;
+
+ i += inc;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret ? ret : i;
+}
+
+static u32 anysee_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm anysee_i2c_algo = {
+ .master_xfer = anysee_master_xfer,
+ .functionality = anysee_i2c_func,
+};
+
+static int anysee_mt352_demod_init(struct dvb_frontend *fe)
+{
+ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 };
+ static u8 reset[] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 };
+ static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+/* Callbacks for DVB USB */
+static struct tda10023_config anysee_tda10023_config = {
+ .demod_address = (0x1a >> 1),
+ .invert = 0,
+ .xtal = 16000000,
+ .pll_m = 11,
+ .pll_p = 3,
+ .pll_n = 1,
+ .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C,
+ .deltaf = 0xfeeb,
+};
+
+static struct mt352_config anysee_mt352_config = {
+ .demod_address = (0x1e >> 1),
+ .demod_init = anysee_mt352_demod_init,
+};
+
+static struct zl10353_config anysee_zl10353_config = {
+ .demod_address = (0x1e >> 1),
+ .parallel_ts = 1,
+};
+
+static struct zl10353_config anysee_zl10353_tda18212_config2 = {
+ .demod_address = (0x1e >> 1),
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+ .no_tuner = 1,
+ .if2 = 41500,
+};
+
+static struct zl10353_config anysee_zl10353_tda18212_config = {
+ .demod_address = (0x18 >> 1),
+ .parallel_ts = 1,
+ .disable_i2c_gate_ctrl = 1,
+ .no_tuner = 1,
+ .if2 = 41500,
+};
+
+static struct tda10023_config anysee_tda10023_tda18212_config = {
+ .demod_address = (0x1a >> 1),
+ .xtal = 16000000,
+ .pll_m = 12,
+ .pll_p = 3,
+ .pll_n = 1,
+ .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_B,
+ .deltaf = 0xba02,
+};
+
+static struct tda18212_config anysee_tda18212_config = {
+ .i2c_address = (0xc0 >> 1),
+ .if_dvbt_6 = 4150,
+ .if_dvbt_7 = 4150,
+ .if_dvbt_8 = 4150,
+ .if_dvbc = 5000,
+};
+
+static struct tda18212_config anysee_tda18212_config2 = {
+ .i2c_address = 0x60 /* (0xc0 >> 1) */,
+ .if_dvbt_6 = 3550,
+ .if_dvbt_7 = 3700,
+ .if_dvbt_8 = 4150,
+ .if_dvbt2_6 = 3250,
+ .if_dvbt2_7 = 4000,
+ .if_dvbt2_8 = 4000,
+ .if_dvbc = 5000,
+};
+
+static struct cx24116_config anysee_cx24116_config = {
+ .demod_address = (0xaa >> 1),
+ .mpg_clk_pos_pol = 0x00,
+ .i2c_wr_max = 48,
+};
+
+static struct stv0900_config anysee_stv0900_config = {
+ .demod_address = (0xd0 >> 1),
+ .demod_mode = 0,
+ .xtal = 8000000,
+ .clkmode = 3,
+ .diseqc_mode = 2,
+ .tun1_maddress = 0,
+ .tun1_adc = 1, /* 1 Vpp */
+ .path1_mode = 3,
+};
+
+static struct stv6110_config anysee_stv6110_config = {
+ .i2c_address = (0xc0 >> 1),
+ .mclk = 16000000,
+ .clk_div = 1,
+};
+
+static struct isl6423_config anysee_isl6423_config = {
+ .current_max = SEC_CURRENT_800m,
+ .curlim = SEC_CURRENT_LIM_OFF,
+ .mod_extern = 1,
+ .addr = (0x10 >> 1),
+};
+
+static struct cxd2820r_config anysee_cxd2820r_config = {
+ .i2c_address = 0x6d, /* (0xda >> 1) */
+ .ts_mode = 0x38,
+};
+
+/*
+ * New USB device strings: Mfr=1, Product=2, SerialNumber=0
+ * Manufacturer: AMT.CO.KR
+ *
+ * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=????????
+ * PCB: ?
+ * parts: DNOS404ZH102A(MT352, DTT7579(?))
+ *
+ * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)"
+ * PCB: PCB 507T (rev1.61)
+ * parts: DNOS404ZH103A(ZL10353, DTT7579(?))
+ * OEA=0a OEB=00 OEC=00 OED=ff OEE=00
+ * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00
+ *
+ * E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee"
+ * PCB: 507CD (rev1.1)
+ * parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01
+ * IOD[0] ZL10353 1=enabled
+ * IOA[7] TS 0=enabled
+ * tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not)
+ *
+ * E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)"
+ * PCB: 507DC (rev0.2)
+ * parts: TDA10023, DTOS403IH102B TM, CST56I01
+ * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe
+ * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01
+ * IOD[0] TDA10023 1=enabled
+ *
+ * E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)"
+ * PCB: 507SI (rev2.1)
+ * parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe
+ * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01
+ * IOD[0] CX24116 1=enabled
+ *
+ * E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
+ * PCB: 507FA (rev0.4)
+ * parts: TDA10023, DTOS403IH102B TM, TDA8024
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] tuner 1=enabled
+ *
+ * E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)"
+ * PCB: 507FA (rev1.1)
+ * parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024
+ * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff
+ * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0
+ * DVB-C:
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] tuner 1=enabled
+ * DVB-T:
+ * IOD[0] ZL10353 1=enabled
+ * IOE[0] tuner 0=enabled
+ * tuner is behind ZL10353 I2C-gate
+ *
+ * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)"
+ * PCB: 508TC (rev0.6)
+ * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[4] TDA18212 1=enabled
+ * DVB-C:
+ * IOD[6] ZL10353 0=disabled
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] IF 1=enabled
+ * DVB-T:
+ * IOD[5] TDA10023 0=disabled
+ * IOD[6] ZL10353 1=enabled
+ * IOE[0] IF 0=enabled
+ *
+ * E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)"
+ * PCB: 508S2 (rev0.7)
+ * parts: DNBU10512IST(STV0903, STV6110), ISL6423
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[5] STV0903 1=enabled
+ *
+ * E7 T2C VID=1c73 PID=861f HW=20 FW=0.1 AMTCI=0.5 "anysee-E7T2C(LP)"
+ * PCB: 508T2C (rev0.3)
+ * parts: DNOQ44QCH106A(CXD2820R, TDA18212), TDA8024
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[5] CXD2820R 1=enabled
+ *
+ * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)"
+ * PCB: 508PTC (rev0.5)
+ * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212)
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[4] TDA18212 1=enabled
+ * DVB-C:
+ * IOD[6] ZL10353 0=disabled
+ * IOD[5] TDA10023 1=enabled
+ * IOE[0] IF 1=enabled
+ * DVB-T:
+ * IOD[5] TDA10023 0=disabled
+ * IOD[6] ZL10353 1=enabled
+ * IOE[0] IF 0=enabled
+ *
+ * E7 PS2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)"
+ * PCB: 508PS2 (rev0.4)
+ * parts: DNBU10512IST(STV0903, STV6110), ISL6423
+ * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff
+ * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4
+ * IOA[7] TS 1=enabled
+ * IOE[5] STV0903 1=enabled
+ */
+
+static int anysee_read_config(struct dvb_usb_device *d)
+{
+ struct anysee_state *state = d_to_priv(d);
+ int ret;
+ u8 hw_info[3];
+
+ /*
+ * Check which hardware we have.
+ * We must do this call two times to get reliable values (hw/fw bug).
+ */
+ ret = anysee_get_hw_info(d, hw_info);
+ if (ret)
+ goto error;
+
+ ret = anysee_get_hw_info(d, hw_info);
+ if (ret)
+ goto error;
+
+ /*
+ * Meaning of these info bytes are guessed.
+ */
+ dev_info(&d->udev->dev, "%s: firmware version %d.%d hardware id %d\n",
+ KBUILD_MODNAME, hw_info[1], hw_info[2], hw_info[0]);
+
+ state->hw = hw_info[0];
+error:
+ return ret;
+}
+
+/* external I2C gate used for DNOD44CDH086A(TDA18212) tuner module */
+static int anysee_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ /* enable / disable tuner access on IOE[4] */
+ return anysee_wr_reg_mask(fe_to_d(fe), REG_IOE, (enable << 4), 0x10);
+}
+
+static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct anysee_state *state = fe_to_priv(fe);
+ struct dvb_usb_device *d = fe_to_d(fe);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ /* no frontend sleep control */
+ if (onoff == 0)
+ return 0;
+
+ switch (state->hw) {
+ case ANYSEE_HW_507FA: /* 15 */
+ /* E30 Combo Plus */
+ /* E30 C Plus */
+
+ if (fe->id == 0) {
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T tuner on IOE[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 0), 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
+ /* E7 TC */
+ /* E7 PTC */
+
+ if (fe->id == 0) {
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 6), 0x40);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+ } else {
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 6), 0x40);
+ if (ret)
+ goto error;
+
+ /* enable IF route on IOE[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 0), 0x01);
+ if (ret)
+ goto error;
+ }
+
+ break;
+ default:
+ ret = 0;
+ }
+
+error:
+ return ret;
+}
+
+static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct anysee_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+ u8 tmp;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = anysee_tda18212_config.i2c_address,
+ .flags = 0,
+ .len = 1,
+ .buf = "\x00",
+ }, {
+ .addr = anysee_tda18212_config.i2c_address,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &tmp,
+ }
+ };
+
+ switch (state->hw) {
+ case ANYSEE_HW_507T: /* 2 */
+ /* E30 */
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(mt352_attach, &anysee_mt352_config,
+ &d->i2c_adap);
+ if (adap->fe[0])
+ break;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ &d->i2c_adap);
+
+ break;
+ case ANYSEE_HW_507CD: /* 6 */
+ /* E30 Plus */
+
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* enable transport stream on IOA[7] */
+ ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ &d->i2c_adap);
+
+ break;
+ case ANYSEE_HW_507DC: /* 10 */
+ /* E30 C Plus */
+
+ /* enable DVB-C demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(tda10023_attach,
+ &anysee_tda10023_config, &d->i2c_adap, 0x48);
+
+ break;
+ case ANYSEE_HW_507SI: /* 11 */
+ /* E30 S2 Plus */
+
+ /* enable DVB-S/S2 demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(cx24116_attach, &anysee_cx24116_config,
+ &d->i2c_adap);
+
+ break;
+ case ANYSEE_HW_507FA: /* 15 */
+ /* E30 Combo Plus */
+ /* E30 C Plus */
+
+ /* enable tuner on IOE[4] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 4), 0x10);
+ if (ret)
+ goto error;
+
+ /* probe TDA18212 */
+ tmp = 0;
+ ret = i2c_transfer(&d->i2c_adap, msg, 2);
+ if (ret == 2 && tmp == 0xc7)
+ dev_dbg(&d->udev->dev, "%s: TDA18212 found\n",
+ __func__);
+ else
+ tmp = 0;
+
+ /* disable tuner on IOE[4] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (0 << 4), 0x10);
+ if (ret)
+ goto error;
+
+ /* disable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ if (tmp == 0xc7) {
+ /* TDA18212 config */
+ adap->fe[0] = dvb_attach(tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &d->i2c_adap, 0x48);
+
+ /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
+ if (adap->fe[0])
+ adap->fe[0]->ops.i2c_gate_ctrl =
+ anysee_i2c_gate_ctrl;
+ } else {
+ /* PLL config */
+ adap->fe[0] = dvb_attach(tda10023_attach,
+ &anysee_tda10023_config,
+ &d->i2c_adap, 0x48);
+ }
+
+ /* break out if first frontend attaching fails */
+ if (!adap->fe[0])
+ break;
+
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[0] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 0), 0x01);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ if (tmp == 0xc7) {
+ /* TDA18212 config */
+ adap->fe[1] = dvb_attach(zl10353_attach,
+ &anysee_zl10353_tda18212_config2,
+ &d->i2c_adap);
+
+ /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
+ if (adap->fe[1])
+ adap->fe[1]->ops.i2c_gate_ctrl =
+ anysee_i2c_gate_ctrl;
+ } else {
+ /* PLL config */
+ adap->fe[1] = dvb_attach(zl10353_attach,
+ &anysee_zl10353_config,
+ &d->i2c_adap);
+ }
+
+ break;
+ case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
+ /* E7 TC */
+ /* E7 PTC */
+
+ /* disable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 6), 0x40);
+ if (ret)
+ goto error;
+
+ /* enable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(tda10023_attach,
+ &anysee_tda10023_tda18212_config,
+ &d->i2c_adap, 0x48);
+
+ /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
+ if (adap->fe[0])
+ adap->fe[0]->ops.i2c_gate_ctrl = anysee_i2c_gate_ctrl;
+
+ /* break out if first frontend attaching fails */
+ if (!adap->fe[0])
+ break;
+
+ /* disable DVB-C demod on IOD[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* enable DVB-T demod on IOD[6] */
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 6), 0x40);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[1] = dvb_attach(zl10353_attach,
+ &anysee_zl10353_tda18212_config,
+ &d->i2c_adap);
+
+ /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */
+ if (adap->fe[1])
+ adap->fe[1]->ops.i2c_gate_ctrl = anysee_i2c_gate_ctrl;
+
+ state->has_ci = true;
+
+ break;
+ case ANYSEE_HW_508S2: /* 19 */
+ case ANYSEE_HW_508PS2: /* 22 */
+ /* E7 S2 */
+ /* E7 PS2 */
+
+ /* enable DVB-S/S2 demod on IOE[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(stv0900_attach,
+ &anysee_stv0900_config, &d->i2c_adap, 0);
+
+ state->has_ci = true;
+
+ break;
+ case ANYSEE_HW_508T2C: /* 20 */
+ /* E7 T2C */
+
+ /* enable DVB-T/T2/C demod on IOE[5] */
+ ret = anysee_wr_reg_mask(d, REG_IOE, (1 << 5), 0x20);
+ if (ret)
+ goto error;
+
+ /* attach demod */
+ adap->fe[0] = dvb_attach(cxd2820r_attach,
+ &anysee_cxd2820r_config, &d->i2c_adap, NULL);
+
+ state->has_ci = true;
+
+ break;
+ }
+
+ if (!adap->fe[0]) {
+ /* we have no frontend :-( */
+ ret = -ENODEV;
+ dev_err(&d->udev->dev, "%s: Unsupported Anysee version. " \
+ "Please report the " \
+ "<linux-media@vger.kernel.org>.\n",
+ KBUILD_MODNAME);
+ }
+error:
+ return ret;
+}
+
+static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct anysee_state *state = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct dvb_frontend *fe;
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ switch (state->hw) {
+ case ANYSEE_HW_507T: /* 2 */
+ /* E30 */
+
+ /* attach tuner */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc2 >> 1), NULL,
+ DVB_PLL_THOMSON_DTT7579);
+
+ break;
+ case ANYSEE_HW_507CD: /* 6 */
+ /* E30 Plus */
+
+ /* attach tuner */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc2 >> 1),
+ &d->i2c_adap, DVB_PLL_THOMSON_DTT7579);
+
+ break;
+ case ANYSEE_HW_507DC: /* 10 */
+ /* E30 C Plus */
+
+ /* attach tuner */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1),
+ &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+
+ break;
+ case ANYSEE_HW_507SI: /* 11 */
+ /* E30 S2 Plus */
+
+ /* attach LNB controller */
+ fe = dvb_attach(isl6423_attach, adap->fe[0], &d->i2c_adap,
+ &anysee_isl6423_config);
+
+ break;
+ case ANYSEE_HW_507FA: /* 15 */
+ /* E30 Combo Plus */
+ /* E30 C Plus */
+
+ /* Try first attach TDA18212 silicon tuner on IOE[4], if that
+ * fails attach old simple PLL. */
+
+ /* attach tuner */
+ fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
+ &anysee_tda18212_config);
+
+ if (fe && adap->fe[1]) {
+ /* attach tuner for 2nd FE */
+ fe = dvb_attach(tda18212_attach, adap->fe[1],
+ &d->i2c_adap, &anysee_tda18212_config);
+ break;
+ } else if (fe) {
+ break;
+ }
+
+ /* attach tuner */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0], (0xc0 >> 1),
+ &d->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+
+ if (fe && adap->fe[1]) {
+ /* attach tuner for 2nd FE */
+ fe = dvb_attach(dvb_pll_attach, adap->fe[0],
+ (0xc0 >> 1), &d->i2c_adap,
+ DVB_PLL_SAMSUNG_DTOS403IH102A);
+ }
+
+ break;
+ case ANYSEE_HW_508TC: /* 18 */
+ case ANYSEE_HW_508PTC: /* 21 */
+ /* E7 TC */
+ /* E7 PTC */
+
+ /* attach tuner */
+ fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
+ &anysee_tda18212_config);
+
+ if (fe) {
+ /* attach tuner for 2nd FE */
+ fe = dvb_attach(tda18212_attach, adap->fe[1],
+ &d->i2c_adap, &anysee_tda18212_config);
+ }
+
+ break;
+ case ANYSEE_HW_508S2: /* 19 */
+ case ANYSEE_HW_508PS2: /* 22 */
+ /* E7 S2 */
+ /* E7 PS2 */
+
+ /* attach tuner */
+ fe = dvb_attach(stv6110_attach, adap->fe[0],
+ &anysee_stv6110_config, &d->i2c_adap);
+
+ if (fe) {
+ /* attach LNB controller */
+ fe = dvb_attach(isl6423_attach, adap->fe[0],
+ &d->i2c_adap, &anysee_isl6423_config);
+ }
+
+ break;
+
+ case ANYSEE_HW_508T2C: /* 20 */
+ /* E7 T2C */
+
+ /* attach tuner */
+ fe = dvb_attach(tda18212_attach, adap->fe[0], &d->i2c_adap,
+ &anysee_tda18212_config2);
+
+ break;
+ default:
+ fe = NULL;
+ }
+
+ if (fe)
+ ret = 0;
+ else
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int anysee_rc_query(struct dvb_usb_device *d)
+{
+ u8 buf[] = {CMD_GET_IR_CODE};
+ u8 ircode[2];
+ int ret;
+
+ /* Remote controller is basic NEC using address byte 0x08.
+ Anysee device RC query returns only two bytes, status and code,
+ address byte is dropped. Also it does not return any value for
+ NEC RCs having address byte other than 0x08. Due to that, we
+ cannot use that device as standard NEC receiver.
+ It could be possible make hack which reads whole code directly
+ from device memory... */
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), ircode, sizeof(ircode));
+ if (ret)
+ return ret;
+
+ if (ircode[0]) {
+ dev_dbg(&d->udev->dev, "%s: key pressed %02x\n", __func__,
+ ircode[1]);
+ rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0);
+ }
+
+ return 0;
+}
+
+static int anysee_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = anysee_rc_query;
+ rc->interval = 250; /* windows driver uses 500ms */
+
+ return 0;
+}
+
+static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot,
+ int addr)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+ u8 buf[] = {CMD_CI, 0x02, 0x40 | addr >> 8, addr & 0xff, 0x00, 1};
+ u8 val;
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static int anysee_ci_write_attribute_mem(struct dvb_ca_en50221 *ci, int slot,
+ int addr, u8 val)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+ u8 buf[] = {CMD_CI, 0x03, 0x40 | addr >> 8, addr & 0xff, 0x00, 1, val};
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int anysee_ci_read_cam_control(struct dvb_ca_en50221 *ci, int slot,
+ u8 addr)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+ u8 buf[] = {CMD_CI, 0x04, 0x40, addr, 0x00, 1};
+ u8 val;
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1);
+ if (ret)
+ return ret;
+
+ return val;
+}
+
+static int anysee_ci_write_cam_control(struct dvb_ca_en50221 *ci, int slot,
+ u8 addr, u8 val)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+ u8 buf[] = {CMD_CI, 0x05, 0x40, addr, 0x00, 1, val};
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int anysee_ci_slot_reset(struct dvb_ca_en50221 *ci, int slot)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+ struct anysee_state *state = d_to_priv(d);
+
+ state->ci_cam_ready = jiffies + msecs_to_jiffies(1000);
+
+ ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80);
+ if (ret)
+ return ret;
+
+ msleep(300);
+
+ ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int anysee_ci_slot_shutdown(struct dvb_ca_en50221 *ci, int slot)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+
+ ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80);
+ if (ret)
+ return ret;
+
+ msleep(30);
+
+ ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int anysee_ci_slot_ts_enable(struct dvb_ca_en50221 *ci, int slot)
+{
+ struct dvb_usb_device *d = ci->data;
+ int ret;
+
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 1), 0x02);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int anysee_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot,
+ int open)
+{
+ struct dvb_usb_device *d = ci->data;
+ struct anysee_state *state = d_to_priv(d);
+ int ret;
+ u8 tmp;
+
+ ret = anysee_rd_reg_mask(d, REG_IOC, &tmp, 0x40);
+ if (ret)
+ return ret;
+
+ if (tmp == 0) {
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT;
+ if (time_after(jiffies, state->ci_cam_ready))
+ ret |= DVB_CA_EN50221_POLL_CAM_READY;
+ }
+
+ return ret;
+}
+
+static int anysee_ci_init(struct dvb_usb_device *d)
+{
+ struct anysee_state *state = d_to_priv(d);
+ int ret;
+
+ state->ci.owner = THIS_MODULE;
+ state->ci.read_attribute_mem = anysee_ci_read_attribute_mem;
+ state->ci.write_attribute_mem = anysee_ci_write_attribute_mem;
+ state->ci.read_cam_control = anysee_ci_read_cam_control;
+ state->ci.write_cam_control = anysee_ci_write_cam_control;
+ state->ci.slot_reset = anysee_ci_slot_reset;
+ state->ci.slot_shutdown = anysee_ci_slot_shutdown;
+ state->ci.slot_ts_enable = anysee_ci_slot_ts_enable;
+ state->ci.poll_slot_status = anysee_ci_poll_slot_status;
+ state->ci.data = d;
+
+ ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80);
+ if (ret)
+ return ret;
+
+ ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07);
+ if (ret)
+ return ret;
+
+ ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07);
+ if (ret)
+ return ret;
+
+ ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1);
+ if (ret)
+ return ret;
+
+ state->ci_attached = true;
+
+ return 0;
+}
+
+static void anysee_ci_release(struct dvb_usb_device *d)
+{
+ struct anysee_state *state = d_to_priv(d);
+
+ /* detach CI */
+ if (state->ci_attached)
+ dvb_ca_en50221_release(&state->ci);
+
+ return;
+}
+
+static int anysee_init(struct dvb_usb_device *d)
+{
+ struct anysee_state *state = d_to_priv(d);
+ int ret;
+
+ /* There is one interface with two alternate settings.
+ Alternate setting 0 is for bulk transfer.
+ Alternate setting 1 is for isochronous transfer.
+ We use bulk transfer (alternate setting 0). */
+ ret = usb_set_interface(d->udev, 0, 0);
+ if (ret)
+ return ret;
+
+ /* LED light */
+ ret = anysee_led_ctrl(d, 0x01, 0x03);
+ if (ret)
+ return ret;
+
+ /* enable IR */
+ ret = anysee_ir_ctrl(d, 1);
+ if (ret)
+ return ret;
+
+ /* attach CI */
+ if (state->has_ci) {
+ ret = anysee_ci_init(d);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void anysee_exit(struct dvb_usb_device *d)
+{
+ return anysee_ci_release(d);
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties anysee_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct anysee_state),
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &anysee_i2c_algo,
+ .read_config = anysee_read_config,
+ .frontend_attach = anysee_frontend_attach,
+ .tuner_attach = anysee_tuner_attach,
+ .init = anysee_init,
+ .get_rc_config = anysee_get_rc_config,
+ .frontend_ctrl = anysee_frontend_ctrl,
+ .streaming_ctrl = anysee_streaming_ctrl,
+ .exit = anysee_exit,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 8, 16 * 512),
+ }
+ }
+};
+
+static const struct usb_device_id anysee_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE,
+ &anysee_props, "Anysee", RC_MAP_ANYSEE) },
+ { DVB_USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE,
+ &anysee_props, "Anysee", RC_MAP_ANYSEE) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, anysee_id_table);
+
+static struct usb_driver anysee_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = anysee_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(anysee_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/anysee.h b/drivers/media/usb/dvb-usb-v2/anysee.h
new file mode 100644
index 000000000000..c1a4273f14ff
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/anysee.h
@@ -0,0 +1,326 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This
+ * module registers serial interface that can be used to communicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#ifndef _DVB_USB_ANYSEE_H_
+#define _DVB_USB_ANYSEE_H_
+
+#define DVB_USB_LOG_PREFIX "anysee"
+#include "dvb_usb.h"
+#include "dvb_ca_en50221.h"
+
+enum cmd {
+ CMD_I2C_READ = 0x33,
+ CMD_I2C_WRITE = 0x31,
+ CMD_REG_READ = 0xb0,
+ CMD_REG_WRITE = 0xb1,
+ CMD_STREAMING_CTRL = 0x12,
+ CMD_LED_AND_IR_CTRL = 0x16,
+ CMD_GET_IR_CODE = 0x41,
+ CMD_GET_HW_INFO = 0x19,
+ CMD_SMARTCARD = 0x34,
+ CMD_CI = 0x37,
+};
+
+struct anysee_state {
+ u8 hw; /* PCB ID */
+ u8 seq;
+ u8 fe_id:1; /* frondend ID */
+ u8 has_ci:1;
+ u8 ci_attached:1;
+ struct dvb_ca_en50221 ci;
+ unsigned long ci_cam_ready; /* jiffies */
+};
+
+#define ANYSEE_HW_507T 2 /* E30 */
+#define ANYSEE_HW_507CD 6 /* E30 Plus */
+#define ANYSEE_HW_507DC 10 /* E30 C Plus */
+#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */
+#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */
+#define ANYSEE_HW_508TC 18 /* E7 TC */
+#define ANYSEE_HW_508S2 19 /* E7 S2 */
+#define ANYSEE_HW_508T2C 20 /* E7 T2C */
+#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */
+#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */
+
+#define REG_IOA 0x80 /* Port A (bit addressable) */
+#define REG_IOB 0x90 /* Port B (bit addressable) */
+#define REG_IOC 0xa0 /* Port C (bit addressable) */
+#define REG_IOD 0xb0 /* Port D (bit addressable) */
+#define REG_IOE 0xb1 /* Port E (NOT bit addressable) */
+#define REG_OEA 0xb2 /* Port A Output Enable */
+#define REG_OEB 0xb3 /* Port B Output Enable */
+#define REG_OEC 0xb4 /* Port C Output Enable */
+#define REG_OED 0xb5 /* Port D Output Enable */
+#define REG_OEE 0xb6 /* Port E Output Enable */
+
+#endif
+
+/***************************************************************************
+ * USB API description (reverse engineered)
+ ***************************************************************************
+
+Transaction flow:
+=================
+BULK[00001] >>> REQUEST PACKET 64 bytes
+BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY)
+BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY)
+
+General reply packet(s) are always used if not own reply defined.
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY)
+============================================================================
+| 00 | reply data (if any) from previous transaction
+| | Just same reply packet as returned during previous transaction.
+| | Needed only if reply is missed in previous transaction.
+| | Just skip normally.
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY)
+============================================================================
+| 00 | reply data (if any)
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C WRITE REQUEST PACKET
+============================================================================
+| 00 | 0x31 I2C write command
+----------------------------------------------------------------------------
+| 01 | i2c address
+----------------------------------------------------------------------------
+| 02 | data length
+| | 0x02 (for typical I2C reg / val pair)
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04- | data
+----------------------------------------------------------------------------
+| -59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C READ REQUEST PACKET
+============================================================================
+| 00 | 0x33 I2C read command
+----------------------------------------------------------------------------
+| 01 | i2c address + 1
+----------------------------------------------------------------------------
+| 02 | register
+----------------------------------------------------------------------------
+| 03 | 0x00
+----------------------------------------------------------------------------
+| 04 | 0x00
+----------------------------------------------------------------------------
+| 05 | data length
+----------------------------------------------------------------------------
+| 06-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET
+============================================================================
+| 00 | 0xb1 register write command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04 | value
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET
+============================================================================
+| 00 | 0xb0 register read command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | LED CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+| 01 | 0x01 (LED)
+----------------------------------------------------------------------------
+| 03 | 0x00 blink
+| | 0x01 lights continuously
+----------------------------------------------------------------------------
+| 04 | blink interval
+| | 0x00 fastest (looks like LED lights continuously)
+| | 0xff slowest
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | IR CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+| 01 | 0x02 (IR)
+----------------------------------------------------------------------------
+| 03 | 0x00 IR disabled
+| | 0x01 IR enabled
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | STREAMING CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x12 streaming control command
+----------------------------------------------------------------------------
+| 01 | 0x00 streaming disabled
+| | 0x01 streaming enabled
+----------------------------------------------------------------------------
+| 02 | 0x00
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x41 remote control command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REPLY PACKET
+============================================================================
+| 00 | 0x00 code not received
+| | 0x01 code received
+----------------------------------------------------------------------------
+| 01 | remote control code
+----------------------------------------------------------------------------
+| 02-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REQUEST PACKET
+============================================================================
+| 00 | 0x19 get hardware info command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REPLY PACKET
+============================================================================
+| 00 | hardware id
+----------------------------------------------------------------------------
+| 01-02 | firmware version
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | SMART CARD READER PACKET
+============================================================================
+| 00 | 0x34 smart card reader command
+----------------------------------------------------------------------------
+| xx |
+----------------------------------------------------------------------------
+| xx-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+*/
diff --git a/drivers/media/usb/dvb-usb-v2/au6610.c b/drivers/media/usb/dvb-usb-v2/au6610.c
new file mode 100644
index 000000000000..ae6a671b7fd5
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/au6610.c
@@ -0,0 +1,213 @@
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "au6610.h"
+#include "zl10353.h"
+#include "qt1010.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+ int ret;
+ u16 index;
+ u8 *usb_buf;
+
+ /*
+ * allocate enough for all known requests,
+ * read returns 5 and write 6 bytes
+ */
+ usb_buf = kmalloc(6, GFP_KERNEL);
+ if (!usb_buf)
+ return -ENOMEM;
+
+ switch (wlen) {
+ case 1:
+ index = wbuf[0] << 8;
+ break;
+ case 2:
+ index = wbuf[0] << 8;
+ index += wbuf[1];
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
+ KBUILD_MODNAME, wlen);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
+ USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
+ usb_buf, 6, AU6610_USB_TIMEOUT);
+
+ dvb_usb_dbg_usb_control_msg(d->udev, operation,
+ (USB_TYPE_VENDOR|USB_DIR_IN), addr << 1, index,
+ usb_buf, 6);
+
+ if (ret < 0)
+ goto error;
+
+ switch (operation) {
+ case AU6610_REQ_I2C_READ:
+ case AU6610_REQ_USB_READ:
+ /* requested value is always 5th byte in buffer */
+ rbuf[0] = usb_buf[4];
+ }
+error:
+ kfree(usb_buf);
+ return ret;
+}
+
+static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+ u8 request;
+ u8 wo = (rbuf == NULL || rlen == 0); /* write-only */
+
+ if (wo) {
+ request = AU6610_REQ_I2C_WRITE;
+ } else { /* rw */
+ request = AU6610_REQ_I2C_READ;
+ }
+
+ return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen);
+}
+
+
+/* I2C */
+static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int i;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, msg[i+1].buf,
+ msg[i+1].len) < 0)
+ break;
+ i++;
+ } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, NULL, 0) < 0)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+
+static u32 au6610_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au6610_i2c_algo = {
+ .master_xfer = au6610_i2c_xfer,
+ .functionality = au6610_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct zl10353_config au6610_zl10353_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+
+static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ adap->fe[0] = dvb_attach(zl10353_attach, &au6610_zl10353_config,
+ &adap_to_d(adap)->i2c_adap);
+ if (adap->fe[0] == NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct qt1010_config au6610_qt1010_config = {
+ .i2c_address = 0x62
+};
+
+static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ return dvb_attach(qt1010_attach, adap->fe[0],
+ &adap_to_d(adap)->i2c_adap,
+ &au6610_qt1010_config) == NULL ? -ENODEV : 0;
+}
+
+static int au6610_init(struct dvb_usb_device *d)
+{
+ /* TODO: this functionality belongs likely to the streaming control */
+ /* bInterfaceNumber 0, bAlternateSetting 5 */
+ return usb_set_interface(d->udev, 0, 5);
+}
+
+static struct dvb_usb_device_properties au6610_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+
+ .i2c_algo = &au6610_i2c_algo,
+ .frontend_attach = au6610_zl10353_frontend_attach,
+ .tuner_attach = au6610_qt1010_tuner_attach,
+ .init = au6610_init,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(0x82, 5, 40, 942, 1),
+ },
+ },
+};
+
+static const struct usb_device_id au6610_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110,
+ &au6610_props, "Sigmatek DVB-110", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, au6610_id_table);
+
+static struct usb_driver au6610_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = au6610_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(au6610_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/au6610.h b/drivers/media/usb/dvb-usb-v2/au6610.h
new file mode 100644
index 000000000000..ea337bfc00b1
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/au6610.h
@@ -0,0 +1,32 @@
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 AU6610_H
+#define AU6610_H
+#include "dvb_usb.h"
+
+#define AU6610_REQ_I2C_WRITE 0x14
+#define AU6610_REQ_I2C_READ 0x13
+#define AU6610_REQ_USB_WRITE 0x16
+#define AU6610_REQ_USB_READ 0x15
+
+#define AU6610_USB_TIMEOUT 1000
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c
new file mode 100644
index 000000000000..54f1221d930d
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/az6007.c
@@ -0,0 +1,919 @@
+/*
+ * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
+ *
+ * Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
+ *
+ * This driver was made publicly available by Terratec, at:
+ * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
+ * The original driver's license is GPL, as declared with MODULE_LICENSE()
+ *
+ * Copyright (c) 2010-2012 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Driver modified by in order to work with upstream drxk driver, and
+ * tons of bugs got fixed, and converted to use dvb-usb-v2.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "drxk.h"
+#include "mt2063.h"
+#include "dvb_ca_en50221.h"
+#include "dvb_usb.h"
+#include "cypress_firmware.h"
+
+#define AZ6007_FIRMWARE "dvb-usb-terratec-h7-az6007.fw"
+
+static int az6007_xfer_debug;
+module_param_named(xfer_debug, az6007_xfer_debug, int, 0644);
+MODULE_PARM_DESC(xfer_debug, "Enable xfer debug");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
+
+#define FX2_OED 0xb5
+#define AZ6007_READ_DATA 0xb7
+#define AZ6007_I2C_RD 0xb9
+#define AZ6007_POWER 0xbc
+#define AZ6007_I2C_WR 0xbd
+#define FX2_SCON1 0xc0
+#define AZ6007_TS_THROUGH 0xc7
+#define AZ6007_READ_IR 0xb4
+
+struct az6007_device_state {
+ struct mutex mutex;
+ struct mutex ca_mutex;
+ struct dvb_ca_en50221 ca;
+ unsigned warm:1;
+ int (*gate_ctrl) (struct dvb_frontend *, int);
+ unsigned char data[4096];
+};
+
+static struct drxk_config terratec_h7_drxk = {
+ .adr = 0x29,
+ .parallel_ts = true,
+ .dynamic_clk = true,
+ .single_master = true,
+ .enable_merr_cfg = true,
+ .no_i2c_bridge = false,
+ .chunk_size = 64,
+ .mpeg_out_clk_strength = 0x02,
+ .qam_demod_parameter_count = 2,
+ .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct az6007_device_state *st = fe_to_priv(fe);
+ struct dvb_usb_adapter *adap = fe->sec_priv;
+ int status = 0;
+
+ pr_debug("%s: %s\n", __func__, enable ? "enable" : "disable");
+
+ if (!adap || !st)
+ return -EINVAL;
+
+ if (enable)
+ status = st->gate_ctrl(fe, 1);
+ else
+ status = st->gate_ctrl(fe, 0);
+
+ return status;
+}
+
+static struct mt2063_config az6007_mt2063_config = {
+ .tuner_address = 0x60,
+ .refclock = 36125000,
+};
+
+static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, b, blen, 5000);
+ if (ret < 0) {
+ pr_warn("usb read operation failed. (%d)\n", ret);
+ return -EIO;
+ }
+
+ if (az6007_xfer_debug) {
+ printk(KERN_DEBUG "az6007: IN req: %02x, value: %04x, index: %04x\n",
+ req, value, index);
+ print_hex_dump_bytes("az6007: payload: ",
+ DUMP_PREFIX_NONE, b, blen);
+ }
+
+ return ret;
+}
+
+static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_read(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ if (az6007_xfer_debug) {
+ printk(KERN_DEBUG "az6007: OUT req: %02x, value: %04x, index: %04x\n",
+ req, value, index);
+ print_hex_dump_bytes("az6007: payload: ",
+ DUMP_PREFIX_NONE, b, blen);
+ }
+
+ if (blen > 64) {
+ pr_err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
+ blen);
+ return -EOPNOTSUPP;
+ }
+
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ value, index, b, blen, 5000);
+ if (ret != blen) {
+ pr_err("usb write operation failed. (%d)\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_write(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int az6007_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+
+ pr_debug("%s: %s\n", __func__, onoff ? "enable" : "disable");
+
+ return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
+}
+
+/* remote control stuff (does not work with my box) */
+static int az6007_rc_query(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *st = d_to_priv(d);
+ unsigned code = 0;
+
+ az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
+
+ if (st->data[1] == 0x44)
+ return 0;
+
+ if ((st->data[1] ^ st->data[2]) == 0xff)
+ code = st->data[1];
+ else
+ code = st->data[1] << 8 | st->data[2];
+
+ if ((st->data[3] ^ st->data[4]) == 0xff)
+ code = code << 8 | st->data[3];
+ else
+ code = code << 16 | st->data[3] << 8 | st->data[4];
+
+ rc_keydown(d->rc_dev, code, st->data[5]);
+
+ return 0;
+}
+
+static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC1;
+ value = address;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ pr_warn("usb in operation failed. (%d)\n", ret);
+ ret = -EINVAL;
+ } else {
+ ret = b[0];
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ pr_debug("%s(), slot %d\n", __func__, slot);
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC2;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0)
+ pr_warn("usb out operation failed. (%d)\n", ret);
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC3;
+ value = address;
+ index = 0;
+ blen = 2;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ pr_warn("usb in operation failed. (%d)\n", ret);
+ ret = -EINVAL;
+ } else {
+ if (b[0] == 0)
+ pr_warn("Read CI IO error\n");
+
+ ret = b[1];
+ pr_debug("read cam data = %x from 0x%x\n", b[1], value);
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC4;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0) {
+ pr_warn("usb out operation failed. (%d)\n", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ req = 0xC8;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ pr_warn("usb in operation failed. (%d)\n", ret);
+ ret = -EIO;
+ } else{
+ ret = b[0];
+ }
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret, i;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC6;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ pr_warn("usb out operation failed. (%d)\n", ret);
+ goto failed;
+ }
+
+ msleep(500);
+ req = 0xC6;
+ value = 0;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ pr_warn("usb out operation failed. (%d)\n", ret);
+ goto failed;
+ }
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ if (CI_CamReady(ca, slot)) {
+ pr_debug("CAM Ready\n");
+ break;
+ }
+ }
+ msleep(5000);
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return 0;
+}
+
+static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ pr_debug("%s()\n", __func__);
+ mutex_lock(&state->ca_mutex);
+ req = 0xC7;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ pr_warn("usb out operation failed. (%d)\n", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = d_to_priv(d);
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC5;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ pr_warn("usb in operation failed. (%d)\n", ret);
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ if (!ret && b[0] == 1) {
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+
+static void az6007_ci_uninit(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *state;
+
+ pr_debug("%s()\n", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = d_to_priv(d);
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+
+static int az6007_ci_init(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct az6007_device_state *state = adap_to_priv(adap);
+ int ret;
+
+ pr_debug("%s()\n", __func__);
+
+ mutex_init(&state->ca_mutex);
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
+ state->ca.read_cam_control = az6007_ci_read_cam_control;
+ state->ca.write_cam_control = az6007_ci_write_cam_control;
+ state->ca.slot_reset = az6007_ci_slot_reset;
+ state->ca.slot_shutdown = az6007_ci_slot_shutdown;
+ state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
+ state->ca.poll_slot_status = az6007_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&adap->dvb_adap,
+ &state->ca,
+ 0, /* flags */
+ 1);/* n_slots */
+ if (ret != 0) {
+ pr_err("Cannot initialize CI: Error %d.\n", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ pr_debug("CI initialized.\n");
+
+ return 0;
+}
+
+static int az6007_read_mac_addr(struct dvb_usb_adapter *adap, u8 mac[6])
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct az6007_device_state *st = adap_to_priv(adap);
+ int ret;
+
+ ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
+ memcpy(mac, st->data, 6);
+
+ if (ret > 0)
+ pr_debug("%s: mac is %pM\n", __func__, mac);
+
+ return ret;
+}
+
+static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct az6007_device_state *st = adap_to_priv(adap);
+ struct dvb_usb_device *d = adap_to_d(adap);
+
+ pr_debug("attaching demod drxk\n");
+
+ adap->fe[0] = dvb_attach(drxk_attach, &terratec_h7_drxk,
+ &d->i2c_adap);
+ if (!adap->fe[0])
+ return -EINVAL;
+
+ adap->fe[0]->sec_priv = adap;
+ st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl;
+ adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ az6007_ci_init(adap);
+
+ return 0;
+}
+
+static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+
+ pr_debug("attaching tuner mt2063\n");
+
+ /* Attach mt2063 to DVB-C frontend */
+ if (adap->fe[0]->ops.i2c_gate_ctrl)
+ adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 1);
+ if (!dvb_attach(mt2063_attach, adap->fe[0],
+ &az6007_mt2063_config,
+ &d->i2c_adap))
+ return -EINVAL;
+
+ if (adap->fe[0]->ops.i2c_gate_ctrl)
+ adap->fe[0]->ops.i2c_gate_ctrl(adap->fe[0], 0);
+
+ return 0;
+}
+
+static int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ struct az6007_device_state *state = d_to_priv(d);
+ int ret;
+
+ pr_debug("%s()\n", __func__);
+
+ if (!state->warm) {
+ mutex_init(&state->mutex);
+
+ ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(60);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(100);
+ ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(20);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ msleep(400);
+ ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+ ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(430);
+ ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ state->warm = true;
+
+ return 0;
+ }
+
+ if (!onoff)
+ return 0;
+
+ az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
+
+ return 0;
+}
+
+/* I2C */
+static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct az6007_device_state *st = d_to_priv(d);
+ int i, j, len;
+ int ret = 0;
+ u16 index;
+ u16 value;
+ int length;
+ u8 req, addr;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ addr = msgs[i].addr << 1;
+ if (((i + 1) < num)
+ && (msgs[i].len == 1)
+ && ((msgs[i].flags & I2C_M_RD) != I2C_M_RD)
+ && (msgs[i + 1].flags & I2C_M_RD)
+ && (msgs[i].addr == msgs[i + 1].addr)) {
+ /*
+ * A write + read xfer for the same address, where
+ * the first xfer has just 1 byte length.
+ * Need to join both into one operation
+ */
+ if (az6007_xfer_debug)
+ printk(KERN_DEBUG "az6007: I2C W/R addr=0x%x len=%d/%d\n",
+ addr, msgs[i].len, msgs[i + 1].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = 6 + msgs[i + 1].len;
+ len = msgs[i + 1].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ if (ret >= len) {
+ for (j = 0; j < len; j++)
+ msgs[i + 1].buf[j] = st->data[j + 5];
+ } else
+ ret = -EIO;
+ i++;
+ } else if (!(msgs[i].flags & I2C_M_RD)) {
+ /* write bytes */
+ if (az6007_xfer_debug)
+ printk(KERN_DEBUG "az6007: I2C W addr=0x%x len=%d\n",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_WR;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = msgs[i].len - 1;
+ len = msgs[i].len - 1;
+ for (j = 0; j < len; j++)
+ st->data[j] = msgs[i].buf[j + 1];
+ ret = __az6007_write(d->udev, req, value, index,
+ st->data, length);
+ } else {
+ /* read bytes */
+ if (az6007_xfer_debug)
+ printk(KERN_DEBUG "az6007: I2C R addr=0x%x len=%d\n",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr;
+ length = msgs[i].len + 6;
+ len = msgs[i].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ for (j = 0; j < len; j++)
+ msgs[i].buf[j] = st->data[j + 5];
+ }
+ if (ret < 0)
+ goto err;
+ }
+err:
+ mutex_unlock(&st->mutex);
+
+ if (ret < 0) {
+ pr_info("%s ERROR: %i\n", __func__, ret);
+ return ret;
+ }
+ return num;
+}
+
+static u32 az6007_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm az6007_i2c_algo = {
+ .master_xfer = az6007_i2c_xfer,
+ .functionality = az6007_i2c_func,
+};
+
+static int az6007_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ int ret;
+ u8 *mac;
+
+ pr_debug("Identifying az6007 state\n");
+
+ mac = kmalloc(6, GFP_ATOMIC);
+ if (!mac)
+ return -ENOMEM;
+
+ /* Try to read the mac address */
+ ret = __az6007_read(d->udev, AZ6007_READ_DATA, 6, 0, mac, 6);
+ if (ret == 6)
+ ret = WARM;
+ else
+ ret = COLD;
+
+ kfree(mac);
+
+ if (ret == COLD) {
+ __az6007_write(d->udev, 0x09, 1, 0, NULL, 0);
+ __az6007_write(d->udev, 0x00, 0, 0, NULL, 0);
+ __az6007_write(d->udev, 0x00, 0, 0, NULL, 0);
+ }
+
+ pr_debug("Device is on %s state\n",
+ ret == WARM ? "warm" : "cold");
+ return ret;
+}
+
+static void az6007_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ az6007_ci_uninit(d);
+ dvb_usbv2_disconnect(intf);
+}
+
+static int az6007_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+ pr_debug("Getting az6007 Remote Control properties\n");
+
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = az6007_rc_query;
+ rc->interval = 400;
+
+ return 0;
+}
+
+static int az6007_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ pr_debug("Loading az6007 firmware\n");
+
+ return usbv2_cypress_load_firmware(d->udev, fw, CYPRESS_FX2);
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties az6007_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .firmware = AZ6007_FIRMWARE,
+
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct az6007_device_state),
+ .i2c_algo = &az6007_i2c_algo,
+ .tuner_attach = az6007_tuner_attach,
+ .frontend_attach = az6007_frontend_attach,
+ .streaming_ctrl = az6007_streaming_ctrl,
+ .get_rc_config = az6007_get_rc_config,
+ .read_mac_address = az6007_read_mac_addr,
+ .download_firmware = az6007_download_firmware,
+ .identify_state = az6007_identify_state,
+ .power_ctrl = az6007_power_ctrl,
+ .num_adapters = 1,
+ .adapter = {
+ { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), }
+ }
+};
+
+static struct usb_device_id az6007_usb_table[] = {
+ {DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007,
+ &az6007_props, "Azurewave 6007", RC_MAP_EMPTY)},
+ {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7,
+ &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)},
+ {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2,
+ &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)},
+ {0},
+};
+
+MODULE_DEVICE_TABLE(usb, az6007_usb_table);
+
+static int az6007_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ az6007_ci_uninit(d);
+ return dvb_usbv2_suspend(intf, msg);
+}
+
+static int az6007_resume(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ struct dvb_usb_adapter *adap = &d->adapter[0];
+
+ az6007_ci_init(adap);
+ return dvb_usbv2_resume(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver az6007_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = az6007_usb_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = az6007_usb_disconnect,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+ /*
+ * FIXME: need to implement reset_resume, likely with
+ * dvb-usb-v2 core support
+ */
+ .suspend = az6007_suspend,
+ .resume = az6007_resume,
+};
+
+module_usb_driver(az6007_usb_driver);
+
+MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(AZ6007_FIRMWARE);
diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.c b/drivers/media/usb/dvb-usb-v2/ce6230.c
new file mode 100644
index 000000000000..f67b14bc32e3
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/ce6230.c
@@ -0,0 +1,292 @@
+/*
+ * Intel CE6230 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "ce6230.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 request;
+ u8 requesttype;
+ u16 value;
+ u16 index;
+ u8 *buf;
+
+ request = req->cmd;
+ value = req->value;
+ index = req->index;
+
+ switch (req->cmd) {
+ case I2C_READ:
+ case DEMOD_READ:
+ case REG_READ:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ break;
+ case I2C_WRITE:
+ case DEMOD_WRITE:
+ case REG_WRITE:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
+ KBUILD_MODNAME, req->cmd);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ buf = kmalloc(req->data_len, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
+ /* write */
+ memcpy(buf, req->data, req->data_len);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ msleep(1); /* avoid I2C errors */
+
+ ret = usb_control_msg(d->udev, pipe, request, requesttype, value, index,
+ buf, req->data_len, CE6230_USB_TIMEOUT);
+
+ dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, value, index,
+ buf, req->data_len);
+
+ if (ret < 0)
+ dev_err(&d->udev->dev, "%s: usb_control_msg() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ else
+ ret = 0;
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->data_len);
+
+ kfree(buf);
+error:
+ return ret;
+}
+
+/* I2C */
+static struct zl10353_config ce6230_zl10353_config;
+
+static int ce6230_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret = 0, i = 0;
+ struct usb_req req;
+
+ if (num > 2)
+ return -EOPNOTSUPP;
+
+ memset(&req, 0, sizeof(req));
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].addr ==
+ ce6230_zl10353_config.demod_address) {
+ req.cmd = DEMOD_READ;
+ req.value = msg[i].addr >> 1;
+ req.index = msg[i].buf[0];
+ req.data_len = msg[i+1].len;
+ req.data = &msg[i+1].buf[0];
+ ret = ce6230_ctrl_msg(d, &req);
+ } else {
+ dev_err(&d->udev->dev, "%s: I2C read not " \
+ "implemented\n",
+ KBUILD_MODNAME);
+ ret = -EOPNOTSUPP;
+ }
+ i += 2;
+ } else {
+ if (msg[i].addr ==
+ ce6230_zl10353_config.demod_address) {
+ req.cmd = DEMOD_WRITE;
+ req.value = msg[i].addr >> 1;
+ req.index = msg[i].buf[0];
+ req.data_len = msg[i].len-1;
+ req.data = &msg[i].buf[1];
+ ret = ce6230_ctrl_msg(d, &req);
+ } else {
+ req.cmd = I2C_WRITE;
+ req.value = 0x2000 + (msg[i].addr >> 1);
+ req.index = 0x0000;
+ req.data_len = msg[i].len;
+ req.data = &msg[i].buf[0];
+ ret = ce6230_ctrl_msg(d, &req);
+ }
+ i += 1;
+ }
+ if (ret)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return ret ? ret : i;
+}
+
+static u32 ce6230_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ce6230_i2c_algorithm = {
+ .master_xfer = ce6230_i2c_master_xfer,
+ .functionality = ce6230_i2c_functionality,
+};
+
+/* Callbacks for DVB USB */
+static struct zl10353_config ce6230_zl10353_config = {
+ .demod_address = 0x1e,
+ .adc_clock = 450000,
+ .if2 = 45700,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .clock_ctl_1 = 0x34,
+ .pll_0 = 0x0e,
+};
+
+static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ adap->fe[0] = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
+ &d->i2c_adap);
+ if (adap->fe[0] == NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct mxl5005s_config ce6230_mxl5003s_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_DEFAULT,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ ret = dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
+ &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
+ return ret;
+}
+
+static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ /* InterfaceNumber 1 / AlternateSetting 0 idle
+ InterfaceNumber 1 / AlternateSetting 1 streaming */
+ ret = usb_set_interface(d->udev, 1, onoff);
+ if (ret)
+ dev_err(&d->udev->dev, "%s: usb_set_interface() failed=%d\n",
+ KBUILD_MODNAME, ret);
+
+ return ret;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ce6230_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .bInterfaceNumber = 1,
+
+ .i2c_algo = &ce6230_i2c_algorithm,
+ .power_ctrl = ce6230_power_ctrl,
+ .frontend_attach = ce6230_zl10353_frontend_attach,
+ .tuner_attach = ce6230_mxl5003s_tuner_attach,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = (16 * 512),
+ }
+ }
+ },
+ }
+ },
+};
+
+static const struct usb_device_id ce6230_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500,
+ &ce6230_props, "Intel CE9500 reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310,
+ &ce6230_props, "AVerMedia A310 USB 2.0 DVB-T tuner", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ce6230_id_table);
+
+static struct usb_driver ce6230_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ce6230_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(ce6230_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Intel CE6230 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/ce6230.h b/drivers/media/usb/dvb-usb-v2/ce6230.h
new file mode 100644
index 000000000000..299e57e3390b
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/ce6230.h
@@ -0,0 +1,50 @@
+/*
+ * Intel CE6230 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 CE6230_H
+#define CE6230_H
+
+#include "dvb_usb.h"
+#include "zl10353.h"
+#include "mxl5005s.h"
+
+#define CE6230_USB_TIMEOUT 1000
+
+struct usb_req {
+ u8 cmd; /* [1] */
+ u16 value; /* [2|3] */
+ u16 index; /* [4|5] */
+ u16 data_len; /* [6|7] */
+ u8 *data;
+};
+
+enum ce6230_cmd {
+ CONFIG_READ = 0xd0, /* rd 0 (unclear) */
+ UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */
+ I2C_READ = 0xd9, /* rd 9 (unclear) */
+ I2C_WRITE = 0xca, /* wr a */
+ DEMOD_READ = 0xdb, /* rd b */
+ DEMOD_WRITE = 0xcc, /* wr c */
+ REG_READ = 0xde, /* rd e */
+ REG_WRITE = 0xcf, /* wr f */
+};
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.c b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c
new file mode 100644
index 000000000000..211df549f26a
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.c
@@ -0,0 +1,134 @@
+/* cypress_firmware.c is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#include "dvb_usb.h"
+#include "cypress_firmware.h"
+
+struct usb_cypress_controller {
+ u8 id;
+ const char *name; /* name of the usb controller */
+ u16 cs_reg; /* needs to be restarted,
+ * when the firmware has been downloaded */
+};
+
+static const struct usb_cypress_controller cypress[] = {
+ { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cs_reg = 0x7f92 },
+ { .id = CYPRESS_FX2, .name = "Cypress FX2", .cs_reg = 0xe600 },
+};
+
+/*
+ * load a firmware packet to the device
+ */
+static int usb_cypress_writemem(struct usb_device *udev, u16 addr, u8 *data,
+ u8 len)
+{
+ dvb_usb_dbg_usb_control_msg(udev,
+ 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len);
+
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0xa0, USB_TYPE_VENDOR, addr, 0x00, data, len, 5000);
+}
+
+int usbv2_cypress_load_firmware(struct usb_device *udev,
+ const struct firmware *fw, int type)
+{
+ struct hexline *hx;
+ int ret, pos = 0;
+
+ hx = kmalloc(sizeof(struct hexline), GFP_KERNEL);
+ if (!hx) {
+ dev_err(&udev->dev, "%s: kmalloc() failed\n", KBUILD_MODNAME);
+ return -ENOMEM;
+ }
+
+ /* stop the CPU */
+ hx->data[0] = 1;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU stop failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ /* write firmware to memory */
+ for (;;) {
+ ret = dvb_usbv2_get_hexline(fw, hx, &pos);
+ if (ret < 0)
+ goto err_kfree;
+ else if (ret == 0)
+ break;
+
+ ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
+ if (ret < 0) {
+ goto err_kfree;
+ } else if (ret != hx->len) {
+ dev_err(&udev->dev, "%s: error while transferring " \
+ "firmware (transferred size=%d, " \
+ "block size=%d)\n",
+ KBUILD_MODNAME, ret, hx->len);
+ ret = -EIO;
+ goto err_kfree;
+ }
+ }
+
+ /* start the CPU */
+ hx->data[0] = 0;
+ ret = usb_cypress_writemem(udev, cypress[type].cs_reg, hx->data, 1);
+ if (ret != 1) {
+ dev_err(&udev->dev, "%s: CPU start failed=%d\n",
+ KBUILD_MODNAME, ret);
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ ret = 0;
+err_kfree:
+ kfree(hx);
+ return ret;
+}
+EXPORT_SYMBOL(usbv2_cypress_load_firmware);
+
+int dvb_usbv2_get_hexline(const struct firmware *fw, struct hexline *hx,
+ int *pos)
+{
+ u8 *b = (u8 *) &fw->data[*pos];
+ int data_offs = 4;
+
+ if (*pos >= fw->size)
+ return 0;
+
+ memset(hx, 0, sizeof(struct hexline));
+ hx->len = b[0];
+
+ if ((*pos + hx->len + 4) >= fw->size)
+ return -EINVAL;
+
+ hx->addr = b[1] | (b[2] << 8);
+ hx->type = b[3];
+
+ if (hx->type == 0x04) {
+ /* b[4] and b[5] are the Extended linear address record data
+ * field */
+ hx->addr |= (b[4] << 24) | (b[5] << 16);
+ }
+
+ memcpy(hx->data, &b[data_offs], hx->len);
+ hx->chk = b[hx->len + data_offs];
+ *pos += hx->len + 5;
+
+ return *pos;
+}
+EXPORT_SYMBOL(dvb_usbv2_get_hexline);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Cypress firmware download");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/cypress_firmware.h b/drivers/media/usb/dvb-usb-v2/cypress_firmware.h
new file mode 100644
index 000000000000..80085fd4132c
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/cypress_firmware.h
@@ -0,0 +1,31 @@
+/* cypress_firmware.h is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for downloading the firmware to Cypress FX 1
+ * and 2 based devices.
+ *
+ */
+
+#ifndef CYPRESS_FIRMWARE_H
+#define CYPRESS_FIRMWARE_H
+
+#define CYPRESS_AN2135 0
+#define CYPRESS_AN2235 1
+#define CYPRESS_FX2 2
+
+/* commonly used firmware download types and function */
+struct hexline {
+ u8 len;
+ u32 addr;
+ u8 type;
+ u8 data[255];
+ u8 chk;
+};
+extern int usbv2_cypress_load_firmware(struct usb_device *,
+ const struct firmware *, int);
+extern int dvb_usbv2_get_hexline(const struct firmware *,
+ struct hexline *, int *);
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
new file mode 100644
index 000000000000..bae16a1189d6
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -0,0 +1,403 @@
+/*
+ * DVB USB framework
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DVB_USB_H
+#define DVB_USB_H
+
+#include <linux/usb/input.h>
+#include <linux/firmware.h>
+#include <media/rc-core.h>
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+#include "dvb-usb-ids.h"
+
+/*
+ * device file: /dev/dvb/adapter[0-1]/frontend[0-2]
+ *
+ * |-- device
+ * | |-- adapter0
+ * | | |-- frontend0
+ * | | |-- frontend1
+ * | | `-- frontend2
+ * | `-- adapter1
+ * | |-- frontend0
+ * | |-- frontend1
+ * | `-- frontend2
+ *
+ *
+ * Commonly used variable names:
+ * d = pointer to device (struct dvb_usb_device *)
+ * adap = pointer to adapter (struct dvb_usb_adapter *)
+ * fe = pointer to frontend (struct dvb_frontend *)
+ *
+ * Use macros defined in that file to resolve needed pointers.
+ */
+
+/* helper macros for every DVB USB driver use */
+#define adap_to_d(adap) (container_of(adap, struct dvb_usb_device, \
+ adapter[adap->id]))
+#define adap_to_priv(adap) (adap_to_d(adap)->priv)
+#define fe_to_adap(fe) ((struct dvb_usb_adapter *) ((fe)->dvb->priv))
+#define fe_to_d(fe) (adap_to_d(fe_to_adap(fe)))
+#define fe_to_priv(fe) (fe_to_d(fe)->priv)
+#define d_to_priv(d) (d->priv)
+
+#define dvb_usb_dbg_usb_control_msg(udev, r, t, v, i, b, l) { \
+ char *direction; \
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+ direction = ">>>"; \
+ else \
+ direction = "<<<"; \
+ dev_dbg(&udev->dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x " \
+ "%s %*ph\n", __func__, t, r, v & 0xff, v >> 8, \
+ i & 0xff, i >> 8, l & 0xff, l >> 8, direction, l, b); \
+}
+
+#define DVB_USB_STREAM_BULK(endpoint_, count_, size_) { \
+ .type = USB_BULK, \
+ .count = count_, \
+ .endpoint = endpoint_, \
+ .u = { \
+ .bulk = { \
+ .buffersize = size_, \
+ } \
+ } \
+}
+
+#define DVB_USB_STREAM_ISOC(endpoint_, count_, frames_, size_, interval_) { \
+ .type = USB_ISOC, \
+ .count = count_, \
+ .endpoint = endpoint_, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = frames_, \
+ .framesize = size_,\
+ .interval = interval_, \
+ } \
+ } \
+}
+
+#define DVB_USB_DEVICE(vend, prod, props_, name_, rc) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
+ .idVendor = (vend), \
+ .idProduct = (prod), \
+ .driver_info = (kernel_ulong_t) &((const struct dvb_usb_driver_info) { \
+ .props = (props_), \
+ .name = (name_), \
+ .rc_map = (rc), \
+ })
+
+struct dvb_usb_device;
+struct dvb_usb_adapter;
+
+/**
+ * structure for carrying all needed data from the device driver to the general
+ * dvb usb routines
+ * @name: device name
+ * @rc_map: name of rc codes table
+ * @props: structure containing all device properties
+ */
+struct dvb_usb_driver_info {
+ const char *name;
+ const char *rc_map;
+ const struct dvb_usb_device_properties *props;
+};
+
+/**
+ * structure for remote controller configuration
+ * @map_name: name of rc codes table
+ * @allowed_protos: protocol(s) supported by the driver
+ * @change_protocol: callback to change protocol
+ * @query: called to query an event from the device
+ * @interval: time in ms between two queries
+ * @driver_type: used to point if a device supports raw mode
+ * @bulk_mode: device supports bulk mode for rc (disable polling mode)
+ */
+struct dvb_usb_rc {
+ const char *map_name;
+ u64 allowed_protos;
+ int (*change_protocol)(struct rc_dev *dev, u64 rc_type);
+ int (*query) (struct dvb_usb_device *d);
+ unsigned int interval;
+ const enum rc_driver_type driver_type;
+ bool bulk_mode;
+};
+
+/**
+ * usb streaming configration for adapter
+ * @type: urb type
+ * @count: count of used urbs
+ * @endpoint: stream usb endpoint number
+ */
+struct usb_data_stream_properties {
+#define USB_BULK 1
+#define USB_ISOC 2
+ u8 type;
+ u8 count;
+ u8 endpoint;
+
+ union {
+ struct {
+ unsigned int buffersize; /* per URB */
+ } bulk;
+ struct {
+ int framesperurb;
+ int framesize;
+ int interval;
+ } isoc;
+ } u;
+};
+
+/**
+ * properties of dvb usb device adapter
+ * @caps: adapter capabilities
+ * @pid_filter_count: pid count of adapter pid-filter
+ * @pid_filter_ctrl: called to enable/disable pid-filter
+ * @pid_filter: called to set/unset pid for filtering
+ * @stream: adapter usb stream configuration
+ */
+#define MAX_NO_OF_FE_PER_ADAP 3
+struct dvb_usb_adapter_properties {
+#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
+#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
+#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
+ u8 caps;
+
+ u8 pid_filter_count;
+ int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int);
+ int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int);
+
+ struct usb_data_stream_properties stream;
+};
+
+/**
+ * struct dvb_usb_device_properties - properties of a dvb-usb-device
+ * @driver_name: name of the owning driver module
+ * @owner: owner of the dvb_adapter
+ * @adapter_nr: values from the DVB_DEFINE_MOD_OPT_ADAPTER_NR() macro
+ * @bInterfaceNumber: usb interface number driver binds
+ * @size_of_priv: bytes allocated for the driver private data
+ * @generic_bulk_ctrl_endpoint: bulk control endpoint number for sent
+ * @generic_bulk_ctrl_endpoint_response: bulk control endpoint number for
+ * receive
+ * @generic_bulk_ctrl_delay: delay between bulk control sent and receive message
+ * @identify_state: called to determine the firmware state (cold or warm) and
+ * return possible firmware file name to be loaded
+ * @firmware: name of the firmware file to be loaded
+ * @download_firmware: called to download the firmware
+ * @i2c_algo: i2c_algorithm if the device has i2c-adapter
+ * @num_adapters: dvb usb device adapter count
+ * @get_adapter_count: called to resolve adapter count
+ * @adapter: array of all adapter properties of device
+ * @power_ctrl: called to enable/disable power of the device
+ * @read_config: called to resolve device configuration
+ * @read_mac_address: called to resolve adapter mac-address
+ * @frontend_attach: called to attach the possible frontends
+ * @tuner_attach: called to attach the possible tuners
+ * @frontend_ctrl: called to power on/off active frontend
+ * @streaming_ctrl: called to start/stop the usb streaming of adapter
+ * @init: called after adapters are created in order to finalize device
+ * configuration
+ * @exit: called when driver is unloaded
+ * @get_rc_config: called to resolve used remote controller configuration
+ * @get_stream_config: called to resolve input and output stream configuration
+ * of the adapter just before streaming is started. input stream is transport
+ * stream from the demodulator and output stream is usb stream to host.
+ */
+#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
+struct dvb_usb_device_properties {
+ const char *driver_name;
+ struct module *owner;
+ short *adapter_nr;
+
+ u8 bInterfaceNumber;
+ unsigned int size_of_priv;
+ u8 generic_bulk_ctrl_endpoint;
+ u8 generic_bulk_ctrl_endpoint_response;
+ unsigned int generic_bulk_ctrl_delay;
+
+#define WARM 0
+#define COLD 1
+ int (*identify_state) (struct dvb_usb_device *, const char **);
+ const char *firmware;
+#define RECONNECTS_USB 1
+ int (*download_firmware) (struct dvb_usb_device *,
+ const struct firmware *);
+
+ struct i2c_algorithm *i2c_algo;
+
+ unsigned int num_adapters;
+ int (*get_adapter_count) (struct dvb_usb_device *);
+ struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
+ int (*power_ctrl) (struct dvb_usb_device *, int);
+ int (*read_config) (struct dvb_usb_device *d);
+ int (*read_mac_address) (struct dvb_usb_adapter *, u8 []);
+ int (*frontend_attach) (struct dvb_usb_adapter *);
+ int (*tuner_attach) (struct dvb_usb_adapter *);
+ int (*frontend_ctrl) (struct dvb_frontend *, int);
+ int (*streaming_ctrl) (struct dvb_frontend *, int);
+ int (*init) (struct dvb_usb_device *);
+ void (*exit) (struct dvb_usb_device *);
+ int (*get_rc_config) (struct dvb_usb_device *, struct dvb_usb_rc *);
+#define DVB_USB_FE_TS_TYPE_188 0
+#define DVB_USB_FE_TS_TYPE_204 1
+#define DVB_USB_FE_TS_TYPE_RAW 2
+ int (*get_stream_config) (struct dvb_frontend *, u8 *,
+ struct usb_data_stream_properties *);
+};
+
+/**
+ * generic object of an usb stream
+ * @buf_num: number of buffer allocated
+ * @buf_size: size of each buffer in buf_list
+ * @buf_list: array containing all allocate buffers for streaming
+ * @dma_addr: list of dma_addr_t for each buffer in buf_list
+ *
+ * @urbs_initialized: number of URBs initialized
+ * @urbs_submitted: number of URBs submitted
+ */
+#define MAX_NO_URBS_FOR_DATA_STREAM 10
+struct usb_data_stream {
+ struct usb_device *udev;
+ struct usb_data_stream_properties props;
+
+#define USB_STATE_INIT 0x00
+#define USB_STATE_URB_BUF 0x01
+ u8 state;
+
+ void (*complete) (struct usb_data_stream *, u8 *, size_t);
+
+ struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM];
+ int buf_num;
+ unsigned long buf_size;
+ u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM];
+ dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM];
+
+ int urbs_initialized;
+ int urbs_submitted;
+
+ void *user_priv;
+};
+
+/**
+ * dvb adapter object on dvb usb device
+ * @props: pointer to adapter properties
+ * @stream: adapter the usb data stream
+ * @id: index of this adapter (starting with 0)
+ * @ts_type: transport stream, input stream, type
+ * @suspend_resume_active: set when there is ongoing suspend / resume
+ * @pid_filtering: is hardware pid_filtering used or not
+ * @feed_count: current feed count
+ * @max_feed_count: maimum feed count device can handle
+ * @dvb_adap: adapter dvb_adapter
+ * @dmxdev: adapter dmxdev
+ * @demux: adapter software demuxer
+ * @dvb_net: adapter dvb_net interfaces
+ * @sync_mutex: mutex used to sync control and streaming of the adapter
+ * @fe: adapter frontends
+ * @fe_init: rerouted frontend-init function
+ * @fe_sleep: rerouted frontend-sleep function
+ */
+struct dvb_usb_adapter {
+ const struct dvb_usb_adapter_properties *props;
+ struct usb_data_stream stream;
+ u8 id;
+ u8 ts_type;
+ bool suspend_resume_active;
+ bool pid_filtering;
+ u8 feed_count;
+ u8 max_feed_count;
+ s8 active_fe;
+
+ /* dvb */
+ struct dvb_adapter dvb_adap;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvb_net;
+ struct mutex sync_mutex;
+
+ struct dvb_frontend *fe[MAX_NO_OF_FE_PER_ADAP];
+ int (*fe_init[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *);
+ int (*fe_sleep[MAX_NO_OF_FE_PER_ADAP]) (struct dvb_frontend *);
+};
+
+/**
+ * dvb usb device object
+ * @props: device properties
+ * @name: device name
+ * @rc_map: name of rc codes table
+ * @udev: pointer to the device's struct usb_device
+ * @intf: pointer to the device's usb interface
+ * @rc: remote controller configuration
+ * @probe_work: work to defer .probe()
+ * @powered: indicated whether the device is power or not
+ * @usb_mutex: mutex for usb control messages
+ * @i2c_mutex: mutex for i2c-transfers
+ * @i2c_adap: device's i2c-adapter
+ * @rc_dev: rc device for the remote control
+ * @rc_query_work: work for polling remote
+ * @priv: private data of the actual driver (allocate by dvb usb, size defined
+ * in size_of_priv of dvb_usb_properties).
+ */
+struct dvb_usb_device {
+ const struct dvb_usb_device_properties *props;
+ const char *name;
+ const char *rc_map;
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct dvb_usb_rc rc;
+ struct work_struct probe_work;
+ pid_t work_pid;
+ int powered;
+
+ /* locking */
+ struct mutex usb_mutex;
+
+ /* i2c */
+ struct mutex i2c_mutex;
+ struct i2c_adapter i2c_adap;
+
+ struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
+
+ /* remote control */
+ struct rc_dev *rc_dev;
+ char rc_phys[64];
+ struct delayed_work rc_query_work;
+
+ void *priv;
+};
+
+extern int dvb_usbv2_probe(struct usb_interface *,
+ const struct usb_device_id *);
+extern void dvb_usbv2_disconnect(struct usb_interface *);
+extern int dvb_usbv2_suspend(struct usb_interface *, pm_message_t);
+extern int dvb_usbv2_resume(struct usb_interface *);
+extern int dvb_usbv2_reset_resume(struct usb_interface *);
+
+/* the generic read/write method for device control */
+extern int dvb_usbv2_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16);
+extern int dvb_usbv2_generic_write(struct dvb_usb_device *, u8 *, u16);
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h b/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h
new file mode 100644
index 000000000000..45f07090d431
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_common.h
@@ -0,0 +1,35 @@
+/*
+ * DVB USB framework
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef DVB_USB_COMMON_H
+#define DVB_USB_COMMON_H
+
+#include "dvb_usb.h"
+
+/* commonly used methods */
+extern int usb_urb_initv2(struct usb_data_stream *stream,
+ const struct usb_data_stream_properties *props);
+extern int usb_urb_exitv2(struct usb_data_stream *stream);
+extern int usb_urb_submitv2(struct usb_data_stream *stream,
+ struct usb_data_stream_properties *props);
+extern int usb_urb_killv2(struct usb_data_stream *stream);
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
new file mode 100644
index 000000000000..9859d2a2449b
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -0,0 +1,1049 @@
+/*
+ * DVB USB framework
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dvb_usb_common.h"
+
+int dvb_usbv2_disable_rc_polling;
+module_param_named(disable_rc_polling, dvb_usbv2_disable_rc_polling, int, 0644);
+MODULE_PARM_DESC(disable_rc_polling,
+ "disable remote control polling (default: 0)");
+static int dvb_usb_force_pid_filter_usage;
+module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage,
+ int, 0444);
+MODULE_PARM_DESC(force_pid_filter_usage, "force all DVB USB devices to use a " \
+ "PID filter, if any (default: 0)");
+
+static int dvb_usbv2_download_firmware(struct dvb_usb_device *d, const char *name)
+{
+ int ret;
+ const struct firmware *fw;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ if (!d->props->download_firmware) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = request_firmware(&fw, name, &d->udev->dev);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: Did not find the firmware file "\
+ "'%s'. Please see linux/Documentation/dvb/ " \
+ "for more details on firmware-problems. " \
+ "Status %d\n", KBUILD_MODNAME, name, ret);
+ goto err;
+ }
+
+ dev_info(&d->udev->dev, "%s: downloading firmware from file '%s'\n",
+ KBUILD_MODNAME, name);
+
+ ret = d->props->download_firmware(d, fw);
+ release_firmware(fw);
+ if (ret < 0)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usbv2_i2c_init(struct dvb_usb_device *d)
+{
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ if (!d->props->i2c_algo)
+ return 0;
+
+ strlcpy(d->i2c_adap.name, d->name, sizeof(d->i2c_adap.name));
+ d->i2c_adap.algo = d->props->i2c_algo;
+ d->i2c_adap.dev.parent = &d->udev->dev;
+ i2c_set_adapdata(&d->i2c_adap, d);
+
+ ret = i2c_add_adapter(&d->i2c_adap);
+ if (ret < 0) {
+ d->i2c_adap.algo = NULL;
+ dev_err(&d->udev->dev, "%s: i2c_add_adapter() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usbv2_i2c_exit(struct dvb_usb_device *d)
+{
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ if (d->i2c_adap.algo)
+ i2c_del_adapter(&d->i2c_adap);
+
+ return 0;
+}
+
+static void dvb_usb_read_remote_control(struct work_struct *work)
+{
+ struct dvb_usb_device *d = container_of(work,
+ struct dvb_usb_device, rc_query_work.work);
+ int ret;
+
+ /*
+ * When the parameter has been set to 1 via sysfs while the
+ * driver was running, or when bulk mode is enabled after IR init.
+ */
+ if (dvb_usbv2_disable_rc_polling || d->rc.bulk_mode)
+ return;
+
+ ret = d->rc.query(d);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: rc.query() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ return; /* stop polling */
+ }
+
+ schedule_delayed_work(&d->rc_query_work,
+ msecs_to_jiffies(d->rc.interval));
+}
+
+static int dvb_usbv2_remote_init(struct dvb_usb_device *d)
+{
+ int ret;
+ struct rc_dev *dev;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ if (dvb_usbv2_disable_rc_polling || !d->props->get_rc_config)
+ return 0;
+
+ d->rc.map_name = d->rc_map;
+ ret = d->props->get_rc_config(d, &d->rc);
+ if (ret < 0)
+ goto err;
+
+ /* disable rc when there is no keymap defined */
+ if (!d->rc.map_name)
+ return 0;
+
+ dev = rc_allocate_device();
+ if (!dev) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev->dev.parent = &d->udev->dev;
+ dev->input_name = d->name;
+ usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
+ strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
+ dev->input_phys = d->rc_phys;
+ usb_to_input_id(d->udev, &dev->input_id);
+ /* TODO: likely RC-core should took const char * */
+ dev->driver_name = (char *) d->props->driver_name;
+ dev->map_name = d->rc.map_name;
+ dev->driver_type = d->rc.driver_type;
+ dev->allowed_protos = d->rc.allowed_protos;
+ dev->change_protocol = d->rc.change_protocol;
+ dev->priv = d;
+
+ ret = rc_register_device(dev);
+ if (ret < 0) {
+ rc_free_device(dev);
+ goto err;
+ }
+
+ d->rc_dev = dev;
+
+ /* start polling if needed */
+ if (d->rc.query && !d->rc.bulk_mode) {
+ /* initialize a work queue for handling polling */
+ INIT_DELAYED_WORK(&d->rc_query_work,
+ dvb_usb_read_remote_control);
+ dev_info(&d->udev->dev, "%s: schedule remote query interval " \
+ "to %d msecs\n", KBUILD_MODNAME,
+ d->rc.interval);
+ schedule_delayed_work(&d->rc_query_work,
+ msecs_to_jiffies(d->rc.interval));
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usbv2_remote_exit(struct dvb_usb_device *d)
+{
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ if (d->rc_dev) {
+ cancel_delayed_work_sync(&d->rc_query_work);
+ rc_unregister_device(d->rc_dev);
+ d->rc_dev = NULL;
+ }
+
+ return 0;
+}
+
+static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buf,
+ size_t len)
+{
+ struct dvb_usb_adapter *adap = stream->user_priv;
+ dvb_dmx_swfilter(&adap->demux, buf, len);
+}
+
+static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buf,
+ size_t len)
+{
+ struct dvb_usb_adapter *adap = stream->user_priv;
+ dvb_dmx_swfilter_204(&adap->demux, buf, len);
+}
+
+static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, u8 *buf,
+ size_t len)
+{
+ struct dvb_usb_adapter *adap = stream->user_priv;
+ dvb_dmx_swfilter_raw(&adap->demux, buf, len);
+}
+
+int dvb_usbv2_adapter_stream_init(struct dvb_usb_adapter *adap)
+{
+ dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
+ adap->id);
+
+ adap->stream.udev = adap_to_d(adap)->udev;
+ adap->stream.user_priv = adap;
+ adap->stream.complete = dvb_usb_data_complete;
+
+ return usb_urb_initv2(&adap->stream, &adap->props->stream);
+}
+
+int dvb_usbv2_adapter_stream_exit(struct dvb_usb_adapter *adap)
+{
+ dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
+ adap->id);
+
+ return usb_urb_exitv2(&adap->stream);
+}
+
+static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed,
+ int count)
+{
+ struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s: adap=%d active_fe=%d feed_type=%d " \
+ "setting pid [%s]: %04x (%04d) at index %d '%s'\n",
+ __func__, adap->id, adap->active_fe, dvbdmxfeed->type,
+ adap->pid_filtering ? "yes" : "no", dvbdmxfeed->pid,
+ dvbdmxfeed->pid, dvbdmxfeed->index,
+ (count == 1) ? "on" : "off");
+
+ if (adap->active_fe == -1)
+ return -EINVAL;
+
+ adap->feed_count += count;
+
+ /* stop feeding if it is last pid */
+ if (adap->feed_count == 0) {
+ dev_dbg(&d->udev->dev, "%s: stop feeding\n", __func__);
+
+ if (d->props->streaming_ctrl) {
+ ret = d->props->streaming_ctrl(
+ adap->fe[adap->active_fe], 0);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: streaming_ctrl() " \
+ "failed=%d\n", KBUILD_MODNAME,
+ ret);
+ usb_urb_killv2(&adap->stream);
+ goto err_mutex_unlock;
+ }
+ }
+ usb_urb_killv2(&adap->stream);
+ mutex_unlock(&adap->sync_mutex);
+ }
+
+ /* activate the pid on the device pid filter */
+ if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->pid_filtering &&
+ adap->props->pid_filter)
+ ret = adap->props->pid_filter(adap, dvbdmxfeed->index,
+ dvbdmxfeed->pid, (count == 1) ? 1 : 0);
+ if (ret < 0)
+ dev_err(&d->udev->dev, "%s: pid_filter() " \
+ "failed=%d\n", KBUILD_MODNAME,
+ ret);
+
+ /* start feeding if it is first pid */
+ if (adap->feed_count == 1 && count == 1) {
+ struct usb_data_stream_properties stream_props;
+ mutex_lock(&adap->sync_mutex);
+ dev_dbg(&d->udev->dev, "%s: start feeding\n", __func__);
+
+ /* resolve input and output streaming paramters */
+ if (d->props->get_stream_config) {
+ memcpy(&stream_props, &adap->props->stream,
+ sizeof(struct usb_data_stream_properties));
+ ret = d->props->get_stream_config(
+ adap->fe[adap->active_fe],
+ &adap->ts_type, &stream_props);
+ if (ret < 0)
+ goto err_mutex_unlock;
+ } else {
+ stream_props = adap->props->stream;
+ }
+
+ switch (adap->ts_type) {
+ case DVB_USB_FE_TS_TYPE_204:
+ adap->stream.complete = dvb_usb_data_complete_204;
+ break;
+ case DVB_USB_FE_TS_TYPE_RAW:
+ adap->stream.complete = dvb_usb_data_complete_raw;
+ break;
+ case DVB_USB_FE_TS_TYPE_188:
+ default:
+ adap->stream.complete = dvb_usb_data_complete;
+ break;
+ }
+
+ usb_urb_submitv2(&adap->stream, &stream_props);
+
+ if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->props->caps &
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
+ adap->props->pid_filter_ctrl) {
+ ret = adap->props->pid_filter_ctrl(adap,
+ adap->pid_filtering);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: " \
+ "pid_filter_ctrl() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err_mutex_unlock;
+ }
+ }
+
+ if (d->props->streaming_ctrl) {
+ ret = d->props->streaming_ctrl(
+ adap->fe[adap->active_fe], 1);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: streaming_ctrl() " \
+ "failed=%d\n", KBUILD_MODNAME,
+ ret);
+ goto err_mutex_unlock;
+ }
+ }
+ }
+
+ return 0;
+err_mutex_unlock:
+ mutex_unlock(&adap->sync_mutex);
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ return dvb_usb_ctrl_feed(dvbdmxfeed, 1);
+}
+
+static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ return dvb_usb_ctrl_feed(dvbdmxfeed, -1);
+}
+
+int dvb_usbv2_adapter_dvb_init(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id);
+
+ ret = dvb_register_adapter(&adap->dvb_adap, d->name, d->props->owner,
+ &d->udev->dev, d->props->adapter_nr);
+ if (ret < 0) {
+ dev_dbg(&d->udev->dev, "%s: dvb_register_adapter() failed=%d\n",
+ __func__, ret);
+ goto err_dvb_register_adapter;
+ }
+
+ adap->dvb_adap.priv = adap;
+
+ if (d->props->read_mac_address) {
+ ret = d->props->read_mac_address(adap,
+ adap->dvb_adap.proposed_mac);
+ if (ret < 0)
+ goto err_dvb_dmx_init;
+
+ dev_info(&d->udev->dev, "%s: MAC address: %pM\n",
+ KBUILD_MODNAME, adap->dvb_adap.proposed_mac);
+ }
+
+ adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+ adap->demux.priv = adap;
+ adap->demux.filternum = 0;
+ adap->demux.filternum = adap->max_feed_count;
+ adap->demux.feednum = adap->demux.filternum;
+ adap->demux.start_feed = dvb_usb_start_feed;
+ adap->demux.stop_feed = dvb_usb_stop_feed;
+ adap->demux.write_to_decoder = NULL;
+ ret = dvb_dmx_init(&adap->demux);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: dvb_dmx_init() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err_dvb_dmx_init;
+ }
+
+ adap->dmxdev.filternum = adap->demux.filternum;
+ adap->dmxdev.demux = &adap->demux.dmx;
+ adap->dmxdev.capabilities = 0;
+ ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: dvb_dmxdev_init() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err_dvb_dmxdev_init;
+ }
+
+ ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: dvb_net_init() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto err_dvb_net_init;
+ }
+
+ mutex_init(&adap->sync_mutex);
+
+ return 0;
+err_dvb_net_init:
+ dvb_dmxdev_release(&adap->dmxdev);
+err_dvb_dmxdev_init:
+ dvb_dmx_release(&adap->demux);
+err_dvb_dmx_init:
+ dvb_unregister_adapter(&adap->dvb_adap);
+err_dvb_register_adapter:
+ adap->dvb_adap.priv = NULL;
+ return ret;
+}
+
+int dvb_usbv2_adapter_dvb_exit(struct dvb_usb_adapter *adap)
+{
+ dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
+ adap->id);
+
+ if (adap->dvb_adap.priv) {
+ dvb_net_release(&adap->dvb_net);
+ adap->demux.dmx.close(&adap->demux.dmx);
+ dvb_dmxdev_release(&adap->dmxdev);
+ dvb_dmx_release(&adap->demux);
+ dvb_unregister_adapter(&adap->dvb_adap);
+ }
+
+ return 0;
+}
+
+int dvb_usbv2_device_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+
+ if (onoff)
+ d->powered++;
+ else
+ d->powered--;
+
+ if (d->powered == 0 || (onoff && d->powered == 1)) {
+ /* when switching from 1 to 0 or from 0 to 1 */
+ dev_dbg(&d->udev->dev, "%s: power=%d\n", __func__, onoff);
+ if (d->props->power_ctrl) {
+ ret = d->props->power_ctrl(d, onoff);
+ if (ret < 0)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usb_fe_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id,
+ fe->id);
+
+ if (!adap->suspend_resume_active) {
+ adap->active_fe = fe->id;
+ mutex_lock(&adap->sync_mutex);
+ }
+
+ ret = dvb_usbv2_device_power_ctrl(d, 1);
+ if (ret < 0)
+ goto err;
+
+ if (d->props->frontend_ctrl) {
+ ret = d->props->frontend_ctrl(fe, 1);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (adap->fe_init[fe->id]) {
+ ret = adap->fe_init[fe->id](fe);
+ if (ret < 0)
+ goto err;
+ }
+err:
+ if (!adap->suspend_resume_active)
+ mutex_unlock(&adap->sync_mutex);
+
+ dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s: adap=%d fe=%d\n", __func__, adap->id,
+ fe->id);
+
+ if (!adap->suspend_resume_active)
+ mutex_lock(&adap->sync_mutex);
+
+ if (adap->fe_sleep[fe->id]) {
+ ret = adap->fe_sleep[fe->id](fe);
+ if (ret < 0)
+ goto err;
+ }
+
+ if (d->props->frontend_ctrl) {
+ ret = d->props->frontend_ctrl(fe, 0);
+ if (ret < 0)
+ goto err;
+ }
+
+ ret = dvb_usbv2_device_power_ctrl(d, 0);
+ if (ret < 0)
+ goto err;
+err:
+ if (!adap->suspend_resume_active) {
+ adap->active_fe = -1;
+ mutex_unlock(&adap->sync_mutex);
+ }
+
+ dev_dbg(&d->udev->dev, "%s: ret=%d\n", __func__, ret);
+ return ret;
+}
+
+int dvb_usbv2_adapter_frontend_init(struct dvb_usb_adapter *adap)
+{
+ int ret, i, count_registered = 0;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id);
+
+ memset(adap->fe, 0, sizeof(adap->fe));
+ adap->active_fe = -1;
+
+ if (d->props->frontend_attach) {
+ ret = d->props->frontend_attach(adap);
+ if (ret < 0) {
+ dev_dbg(&d->udev->dev, "%s: frontend_attach() " \
+ "failed=%d\n", __func__, ret);
+ goto err_dvb_frontend_detach;
+ }
+ } else {
+ dev_dbg(&d->udev->dev, "%s: frontend_attach() do not exists\n",
+ __func__);
+ ret = 0;
+ goto err;
+ }
+
+ for (i = 0; i < MAX_NO_OF_FE_PER_ADAP && adap->fe[i]; i++) {
+ adap->fe[i]->id = i;
+ /* re-assign sleep and wakeup functions */
+ adap->fe_init[i] = adap->fe[i]->ops.init;
+ adap->fe[i]->ops.init = dvb_usb_fe_init;
+ adap->fe_sleep[i] = adap->fe[i]->ops.sleep;
+ adap->fe[i]->ops.sleep = dvb_usb_fe_sleep;
+
+ ret = dvb_register_frontend(&adap->dvb_adap, adap->fe[i]);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: frontend%d registration " \
+ "failed\n", KBUILD_MODNAME, i);
+ goto err_dvb_unregister_frontend;
+ }
+
+ count_registered++;
+ }
+
+ if (d->props->tuner_attach) {
+ ret = d->props->tuner_attach(adap);
+ if (ret < 0) {
+ dev_dbg(&d->udev->dev, "%s: tuner_attach() failed=%d\n",
+ __func__, ret);
+ goto err_dvb_unregister_frontend;
+ }
+ }
+
+ return 0;
+
+err_dvb_unregister_frontend:
+ for (i = count_registered - 1; i >= 0; i--)
+ dvb_unregister_frontend(adap->fe[i]);
+
+err_dvb_frontend_detach:
+ for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) {
+ if (adap->fe[i]) {
+ dvb_frontend_detach(adap->fe[i]);
+ adap->fe[i] = NULL;
+ }
+ }
+
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+int dvb_usbv2_adapter_frontend_exit(struct dvb_usb_adapter *adap)
+{
+ int i;
+ dev_dbg(&adap_to_d(adap)->udev->dev, "%s: adap=%d\n", __func__,
+ adap->id);
+
+ for (i = MAX_NO_OF_FE_PER_ADAP - 1; i >= 0; i--) {
+ if (adap->fe[i]) {
+ dvb_unregister_frontend(adap->fe[i]);
+ dvb_frontend_detach(adap->fe[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int dvb_usbv2_adapter_init(struct dvb_usb_device *d)
+{
+ struct dvb_usb_adapter *adap;
+ int ret, i, adapter_count;
+
+ /* resolve adapter count */
+ adapter_count = d->props->num_adapters;
+ if (d->props->get_adapter_count) {
+ ret = d->props->get_adapter_count(d);
+ if (ret < 0)
+ goto err;
+
+ adapter_count = ret;
+ }
+
+ for (i = 0; i < adapter_count; i++) {
+ adap = &d->adapter[i];
+ adap->id = i;
+ adap->props = &d->props->adapter[i];
+
+ /* speed - when running at FULL speed we need a HW PID filter */
+ if (d->udev->speed == USB_SPEED_FULL &&
+ !(adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) {
+ dev_err(&d->udev->dev, "%s: this USB2.0 device " \
+ "cannot be run on a USB1.1 port (it " \
+ "lacks a hardware PID filter)\n",
+ KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ } else if ((d->udev->speed == USB_SPEED_FULL &&
+ adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) ||
+ (adap->props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) {
+ dev_info(&d->udev->dev, "%s: will use the device's " \
+ "hardware PID filter " \
+ "(table count: %d)\n", KBUILD_MODNAME,
+ adap->props->pid_filter_count);
+ adap->pid_filtering = 1;
+ adap->max_feed_count = adap->props->pid_filter_count;
+ } else {
+ dev_info(&d->udev->dev, "%s: will pass the complete " \
+ "MPEG2 transport stream to the " \
+ "software demuxer\n", KBUILD_MODNAME);
+ adap->pid_filtering = 0;
+ adap->max_feed_count = 255;
+ }
+
+ if (!adap->pid_filtering && dvb_usb_force_pid_filter_usage &&
+ adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER) {
+ dev_info(&d->udev->dev, "%s: PID filter enabled by " \
+ "module option\n", KBUILD_MODNAME);
+ adap->pid_filtering = 1;
+ adap->max_feed_count = adap->props->pid_filter_count;
+ }
+
+ ret = dvb_usbv2_adapter_stream_init(adap);
+ if (ret)
+ goto err;
+
+ ret = dvb_usbv2_adapter_dvb_init(adap);
+ if (ret)
+ goto err;
+
+ ret = dvb_usbv2_adapter_frontend_init(adap);
+ if (ret)
+ goto err;
+
+ /* use exclusive FE lock if there is multiple shared FEs */
+ if (adap->fe[1])
+ adap->dvb_adap.mfe_shared = 1;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int dvb_usbv2_adapter_exit(struct dvb_usb_device *d)
+{
+ int i;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) {
+ if (d->adapter[i].props) {
+ dvb_usbv2_adapter_frontend_exit(&d->adapter[i]);
+ dvb_usbv2_adapter_dvb_exit(&d->adapter[i]);
+ dvb_usbv2_adapter_stream_exit(&d->adapter[i]);
+ }
+ }
+
+ return 0;
+}
+
+/* general initialization functions */
+static int dvb_usbv2_exit(struct dvb_usb_device *d)
+{
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ dvb_usbv2_remote_exit(d);
+ dvb_usbv2_adapter_exit(d);
+ dvb_usbv2_i2c_exit(d);
+ kfree(d->priv);
+ kfree(d);
+
+ return 0;
+}
+
+static int dvb_usbv2_init(struct dvb_usb_device *d)
+{
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ dvb_usbv2_device_power_ctrl(d, 1);
+
+ if (d->props->read_config) {
+ ret = d->props->read_config(d);
+ if (ret < 0)
+ goto err;
+ }
+
+ ret = dvb_usbv2_i2c_init(d);
+ if (ret < 0)
+ goto err;
+
+ ret = dvb_usbv2_adapter_init(d);
+ if (ret < 0)
+ goto err;
+
+ if (d->props->init) {
+ ret = d->props->init(d);
+ if (ret < 0)
+ goto err;
+ }
+
+ ret = dvb_usbv2_remote_init(d);
+ if (ret < 0)
+ goto err;
+
+ dvb_usbv2_device_power_ctrl(d, 0);
+
+ return 0;
+err:
+ dvb_usbv2_device_power_ctrl(d, 0);
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * udev, which is used for the firmware downloading, requires we cannot
+ * block during module_init(). module_init() calls USB probe() which
+ * is this routine. Due to that we delay actual operation using workqueue
+ * and return always success here.
+ */
+static void dvb_usbv2_init_work(struct work_struct *work)
+{
+ int ret;
+ struct dvb_usb_device *d =
+ container_of(work, struct dvb_usb_device, probe_work);
+
+ d->work_pid = current->pid;
+ dev_dbg(&d->udev->dev, "%s: work_pid=%d\n", __func__, d->work_pid);
+
+ if (d->props->size_of_priv) {
+ d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL);
+ if (!d->priv) {
+ dev_err(&d->udev->dev, "%s: kzalloc() failed\n",
+ KBUILD_MODNAME);
+ ret = -ENOMEM;
+ goto err_usb_driver_release_interface;
+ }
+ }
+
+ if (d->props->identify_state) {
+ const char *name = NULL;
+ ret = d->props->identify_state(d, &name);
+ if (ret == 0) {
+ ;
+ } else if (ret == COLD) {
+ dev_info(&d->udev->dev, "%s: found a '%s' in cold " \
+ "state\n", KBUILD_MODNAME, d->name);
+
+ if (!name)
+ name = d->props->firmware;
+
+ ret = dvb_usbv2_download_firmware(d, name);
+ if (ret == 0) {
+ /* device is warm, continue initialization */
+ ;
+ } else if (ret == RECONNECTS_USB) {
+ /*
+ * USB core will call disconnect() and then
+ * probe() as device reconnects itself from the
+ * USB bus. disconnect() will release all driver
+ * resources and probe() is called for 'new'
+ * device. As 'new' device is warm we should
+ * never go here again.
+ */
+ return;
+ } else {
+ /*
+ * Unexpected error. We must unregister driver
+ * manually from the device, because device is
+ * already register by returning from probe()
+ * with success. usb_driver_release_interface()
+ * finally calls disconnect() in order to free
+ * resources.
+ */
+ goto err_usb_driver_release_interface;
+ }
+ } else {
+ goto err_usb_driver_release_interface;
+ }
+ }
+
+ dev_info(&d->udev->dev, "%s: found a '%s' in warm state\n",
+ KBUILD_MODNAME, d->name);
+
+ ret = dvb_usbv2_init(d);
+ if (ret < 0)
+ goto err_usb_driver_release_interface;
+
+ dev_info(&d->udev->dev, "%s: '%s' successfully initialized and " \
+ "connected\n", KBUILD_MODNAME, d->name);
+
+ return;
+err_usb_driver_release_interface:
+ dev_info(&d->udev->dev, "%s: '%s' error while loading driver (%d)\n",
+ KBUILD_MODNAME, d->name, ret);
+ usb_driver_release_interface(to_usb_driver(d->intf->dev.driver),
+ d->intf);
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return;
+}
+
+int dvb_usbv2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret;
+ struct dvb_usb_device *d;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct dvb_usb_driver_info *driver_info =
+ (struct dvb_usb_driver_info *) id->driver_info;
+
+ dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (!id->driver_info) {
+ dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL);
+ if (!d) {
+ dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ d->name = driver_info->name;
+ d->rc_map = driver_info->rc_map;
+ d->udev = udev;
+ d->intf = intf;
+ d->props = driver_info->props;
+
+ if (d->intf->cur_altsetting->desc.bInterfaceNumber !=
+ d->props->bInterfaceNumber) {
+ ret = -ENODEV;
+ goto err_kfree;
+ }
+
+ mutex_init(&d->usb_mutex);
+ mutex_init(&d->i2c_mutex);
+ INIT_WORK(&d->probe_work, dvb_usbv2_init_work);
+ usb_set_intfdata(intf, d);
+ ret = schedule_work(&d->probe_work);
+ if (ret < 0) {
+ dev_err(&d->udev->dev, "%s: schedule_work() failed\n",
+ KBUILD_MODNAME);
+ goto err_kfree;
+ }
+
+ return 0;
+err_kfree:
+ kfree(d);
+err:
+ dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL(dvb_usbv2_probe);
+
+void dvb_usbv2_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ const char *name = d->name;
+ struct device dev = d->udev->dev;
+ dev_dbg(&d->udev->dev, "%s: pid=%d work_pid=%d\n", __func__,
+ current->pid, d->work_pid);
+
+ /* ensure initialization work is finished until release resources */
+ if (d->work_pid != current->pid)
+ cancel_work_sync(&d->probe_work);
+
+ if (d->props->exit)
+ d->props->exit(d);
+
+ dvb_usbv2_exit(d);
+
+ dev_info(&dev, "%s: '%s' successfully deinitialized and disconnected\n",
+ KBUILD_MODNAME, name);
+}
+EXPORT_SYMBOL(dvb_usbv2_disconnect);
+
+int dvb_usbv2_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ int ret = 0, i, active_fe;
+ struct dvb_frontend *fe;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* stop remote controller poll */
+ if (d->rc.query && !d->rc.bulk_mode)
+ cancel_delayed_work_sync(&d->rc_query_work);
+
+ for (i = MAX_NO_OF_ADAPTER_PER_DEVICE - 1; i >= 0; i--) {
+ active_fe = d->adapter[i].active_fe;
+ if (d->adapter[i].dvb_adap.priv && active_fe != -1) {
+ fe = d->adapter[i].fe[active_fe];
+ d->adapter[i].suspend_resume_active = true;
+
+ if (d->props->streaming_ctrl)
+ d->props->streaming_ctrl(fe, 0);
+
+ /* stop usb streaming */
+ usb_urb_killv2(&d->adapter[i].stream);
+
+ ret = dvb_frontend_suspend(fe);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_usbv2_suspend);
+
+static int dvb_usbv2_resume_common(struct dvb_usb_device *d)
+{
+ int ret = 0, i, active_fe;
+ struct dvb_frontend *fe;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ for (i = 0; i < MAX_NO_OF_ADAPTER_PER_DEVICE; i++) {
+ active_fe = d->adapter[i].active_fe;
+ if (d->adapter[i].dvb_adap.priv && active_fe != -1) {
+ fe = d->adapter[i].fe[active_fe];
+
+ ret = dvb_frontend_resume(fe);
+
+ /* resume usb streaming */
+ usb_urb_submitv2(&d->adapter[i].stream, NULL);
+
+ if (d->props->streaming_ctrl)
+ d->props->streaming_ctrl(fe, 1);
+
+ d->adapter[i].suspend_resume_active = false;
+ }
+ }
+
+ /* start remote controller poll */
+ if (d->rc.query && !d->rc.bulk_mode)
+ schedule_delayed_work(&d->rc_query_work,
+ msecs_to_jiffies(d->rc.interval));
+
+ return ret;
+}
+
+int dvb_usbv2_resume(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ return dvb_usbv2_resume_common(d);
+}
+EXPORT_SYMBOL(dvb_usbv2_resume);
+
+int dvb_usbv2_reset_resume(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ int ret;
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ dvb_usbv2_device_power_ctrl(d, 1);
+
+ if (d->props->init)
+ d->props->init(d);
+
+ ret = dvb_usbv2_resume_common(d);
+
+ dvb_usbv2_device_power_ctrl(d, 0);
+
+ return ret;
+}
+EXPORT_SYMBOL(dvb_usbv2_reset_resume);
+
+MODULE_VERSION("2.0");
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("DVB USB common");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c
new file mode 100644
index 000000000000..0431beed0ef4
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c
@@ -0,0 +1,77 @@
+/*
+ * DVB USB framework
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher <patrick.boettcher@desy.de>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dvb_usb_common.h"
+
+int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf,
+ u16 rlen)
+{
+ int ret, actual_length;
+
+ if (!d || !wbuf || !wlen || !d->props->generic_bulk_ctrl_endpoint ||
+ !d->props->generic_bulk_ctrl_endpoint_response) {
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, -EINVAL);
+ return -EINVAL;
+ }
+
+ ret = mutex_lock_interruptible(&d->usb_mutex);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, wlen, wbuf);
+
+ ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev,
+ d->props->generic_bulk_ctrl_endpoint), wbuf, wlen,
+ &actual_length, 2000);
+ if (ret < 0)
+ dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n",
+ KBUILD_MODNAME, ret);
+ else
+ ret = actual_length != wlen ? -EIO : 0;
+
+ /* an answer is expected, and no error before */
+ if (!ret && rbuf && rlen) {
+ if (d->props->generic_bulk_ctrl_delay)
+ usleep_range(d->props->generic_bulk_ctrl_delay,
+ d->props->generic_bulk_ctrl_delay
+ + 20000);
+
+ ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
+ d->props->generic_bulk_ctrl_endpoint_response),
+ rbuf, rlen, &actual_length, 2000);
+ if (ret)
+ dev_err(&d->udev->dev, "%s: 2nd usb_bulk_msg() " \
+ "failed=%d\n", KBUILD_MODNAME, ret);
+
+ dev_dbg(&d->udev->dev, "%s: <<< %*ph\n", __func__,
+ actual_length, rbuf);
+ }
+
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(dvb_usbv2_generic_rw);
+
+int dvb_usbv2_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len)
+{
+ return dvb_usbv2_generic_rw(d, buf, len, NULL, 0);
+}
+EXPORT_SYMBOL(dvb_usbv2_generic_write);
diff --git a/drivers/media/usb/dvb-usb-v2/ec168.c b/drivers/media/usb/dvb-usb-v2/ec168.c
new file mode 100644
index 000000000000..5c68f3918bc8
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/ec168.c
@@ -0,0 +1,385 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 "ec168.h"
+#include "ec100.h"
+#include "mxl5005s.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 request, requesttype;
+ u8 *buf;
+
+ switch (req->cmd) {
+ case DOWNLOAD_FIRMWARE:
+ case GPIO:
+ case WRITE_I2C:
+ case STREAMING_CTRL:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = req->cmd;
+ break;
+ case READ_I2C:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = req->cmd;
+ break;
+ case GET_CONFIG:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = CONFIG;
+ break;
+ case SET_CONFIG:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = CONFIG;
+ break;
+ case WRITE_DEMOD:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ request = DEMOD_RW;
+ break;
+ case READ_DEMOD:
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ request = DEMOD_RW;
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: unknown command=%02x\n",
+ KBUILD_MODNAME, req->cmd);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ buf = kmalloc(req->size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ msleep(1); /* avoid I2C errors */
+
+ ret = usb_control_msg(d->udev, pipe, request, requesttype, req->value,
+ req->index, buf, req->size, EC168_USB_TIMEOUT);
+
+ dvb_usb_dbg_usb_control_msg(d->udev, request, requesttype, req->value,
+ req->index, buf, req->size);
+
+ if (ret < 0)
+ goto err_dealloc;
+ else
+ ret = 0;
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ kfree(buf);
+ return ret;
+
+err_dealloc:
+ kfree(buf);
+error:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+/* I2C */
+static struct ec100_config ec168_ec100_config;
+
+static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct ec168_req req;
+ int i = 0;
+ int ret;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ if (msg[i].addr == ec168_ec100_config.demod_address) {
+ req.cmd = READ_DEMOD;
+ req.value = 0;
+ req.index = 0xff00 + msg[i].buf[0]; /* reg */
+ req.size = msg[i+1].len; /* bytes to read */
+ req.data = &msg[i+1].buf[0];
+ ret = ec168_ctrl_msg(d, &req);
+ i += 2;
+ } else {
+ dev_err(&d->udev->dev, "%s: I2C read not " \
+ "implemented\n",
+ KBUILD_MODNAME);
+ ret = -EOPNOTSUPP;
+ i += 2;
+ }
+ } else {
+ if (msg[i].addr == ec168_ec100_config.demod_address) {
+ req.cmd = WRITE_DEMOD;
+ req.value = msg[i].buf[1]; /* val */
+ req.index = 0xff00 + msg[i].buf[0]; /* reg */
+ req.size = 0;
+ req.data = NULL;
+ ret = ec168_ctrl_msg(d, &req);
+ i += 1;
+ } else {
+ req.cmd = WRITE_I2C;
+ req.value = msg[i].buf[0]; /* val */
+ req.index = 0x0100 + msg[i].addr; /* I2C addr */
+ req.size = msg[i].len-1;
+ req.data = &msg[i].buf[1];
+ ret = ec168_ctrl_msg(d, &req);
+ i += 1;
+ }
+ }
+ if (ret)
+ goto error;
+
+ }
+ ret = i;
+
+error:
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 ec168_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ec168_i2c_algo = {
+ .master_xfer = ec168_i2c_xfer,
+ .functionality = ec168_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static int ec168_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ int ret;
+ u8 reply;
+ struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply};
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ ret = ec168_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ dev_dbg(&d->udev->dev, "%s: reply=%02x\n", __func__, reply);
+
+ if (reply == 0x01)
+ ret = WARM;
+ else
+ ret = COLD;
+
+ return ret;
+error:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int ec168_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ int ret, len, remaining;
+ struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL};
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ #define LEN_MAX 2048 /* max packet size */
+ for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) {
+ len = remaining;
+ if (len > LEN_MAX)
+ len = LEN_MAX;
+
+ req.size = len;
+ req.data = (u8 *) &fw->data[fw->size - remaining];
+ req.index = fw->size - remaining;
+
+ ret = ec168_ctrl_msg(d, &req);
+ if (ret) {
+ dev_err(&d->udev->dev,
+ "%s: firmware download failed=%d\n",
+ KBUILD_MODNAME, ret);
+ goto error;
+ }
+ }
+
+ req.size = 0;
+
+ /* set "warm"? */
+ req.cmd = SET_CONFIG;
+ req.value = 0;
+ req.index = 0x0001;
+ ret = ec168_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ /* really needed - no idea what does */
+ req.cmd = GPIO;
+ req.value = 0;
+ req.index = 0x0206;
+ ret = ec168_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ /* activate tuner I2C? */
+ req.cmd = WRITE_I2C;
+ req.value = 0;
+ req.index = 0x00c6;
+ ret = ec168_ctrl_msg(d, &req);
+ if (ret)
+ goto error;
+
+ return ret;
+error:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct ec100_config ec168_ec100_config = {
+ .demod_address = 0xff, /* not real address, demod is integrated */
+};
+
+static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ adap->fe[0] = dvb_attach(ec100_attach, &ec168_ec100_config,
+ &d->i2c_adap);
+ if (adap->fe[0] == NULL)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct mxl5005s_config ec168_mxl5003s_config = {
+ .i2c_address = 0xc6,
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_OFF,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ return dvb_attach(mxl5005s_attach, adap->fe[0], &d->i2c_adap,
+ &ec168_mxl5003s_config) == NULL ? -ENODEV : 0;
+}
+
+static int ec168_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL};
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ if (onoff)
+ req.index = 0x0102;
+ return ec168_ctrl_msg(d, &req);
+}
+
+/* DVB USB Driver stuff */
+/* bInterfaceNumber 0 is HID
+ * bInterfaceNumber 1 is DVB-T */
+static struct dvb_usb_device_properties ec168_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .bInterfaceNumber = 1,
+
+ .identify_state = ec168_identify_state,
+ .firmware = EC168_FIRMWARE,
+ .download_firmware = ec168_download_firmware,
+
+ .i2c_algo = &ec168_i2c_algo,
+ .frontend_attach = ec168_ec100_frontend_attach,
+ .tuner_attach = ec168_mxl5003s_tuner_attach,
+ .streaming_ctrl = ec168_streaming_ctrl,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x82, 6, 32 * 512),
+ }
+ },
+};
+
+static const struct dvb_usb_driver_info ec168_driver_info = {
+ .name = "E3C EC168 reference design",
+ .props = &ec168_props,
+};
+
+static const struct usb_device_id ec168_id[] = {
+ { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168),
+ .driver_info = (kernel_ulong_t) &ec168_driver_info },
+ { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2),
+ .driver_info = (kernel_ulong_t) &ec168_driver_info },
+ { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3),
+ .driver_info = (kernel_ulong_t) &ec168_driver_info },
+ { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4),
+ .driver_info = (kernel_ulong_t) &ec168_driver_info },
+ { USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5),
+ .driver_info = (kernel_ulong_t) &ec168_driver_info },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, ec168_id);
+
+static struct usb_driver ec168_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ec168_id,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(ec168_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("E3C EC168 driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(EC168_FIRMWARE);
diff --git a/drivers/media/usb/dvb-usb-v2/ec168.h b/drivers/media/usb/dvb-usb-v2/ec168.h
new file mode 100644
index 000000000000..615a6569421f
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/ec168.h
@@ -0,0 +1,53 @@
+/*
+ * E3C EC168 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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 EC168_H
+#define EC168_H
+
+#include "dvb_usb.h"
+
+#define EC168_USB_TIMEOUT 1000
+#define EC168_FIRMWARE "dvb-usb-ec168.fw"
+
+struct ec168_req {
+ u8 cmd; /* [1] */
+ u16 value; /* [2|3] */
+ u16 index; /* [4|5] */
+ u16 size; /* [6|7] */
+ u8 *data;
+};
+
+enum ec168_cmd {
+ DOWNLOAD_FIRMWARE = 0x00,
+ CONFIG = 0x01,
+ DEMOD_RW = 0x03,
+ GPIO = 0x04,
+ STREAMING_CTRL = 0x10,
+ READ_I2C = 0x20,
+ WRITE_I2C = 0x21,
+ HID_DOWNLOAD = 0x30,
+ GET_CONFIG,
+ SET_CONFIG,
+ READ_DEMOD,
+ WRITE_DEMOD,
+};
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/gl861.c b/drivers/media/usb/dvb-usb-v2/gl861.c
new file mode 100644
index 000000000000..b1b09c547861
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/gl861.c
@@ -0,0 +1,177 @@
+/* DVB USB compliant linux driver for GL861 USB2.0 devices.
+ *
+ * This program is free software; 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 "gl861.h"
+
+#include "zl10353.h"
+#include "qt1010.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen)
+{
+ u16 index;
+ u16 value = addr << (8 + 1);
+ int wo = (rbuf == NULL || rlen == 0); /* write-only */
+ u8 req, type;
+
+ if (wo) {
+ req = GL861_REQ_I2C_WRITE;
+ type = GL861_WRITE;
+ } else { /* rw */
+ req = GL861_REQ_I2C_READ;
+ type = GL861_READ;
+ }
+
+ switch (wlen) {
+ case 1:
+ index = wbuf[0];
+ break;
+ case 2:
+ index = wbuf[0];
+ value = value + wbuf[1];
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: wlen=%d, aborting\n",
+ KBUILD_MODNAME, wlen);
+ return -EINVAL;
+ }
+
+ msleep(1); /* avoid I2C errors */
+
+ return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
+ value, index, rbuf, rlen, 2000);
+}
+
+/* I2C */
+static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int i;
+
+ if (num > 2)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
+ break;
+ i++;
+ } else
+ if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
+ msg[i].len, NULL, 0) < 0)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 gl861_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm gl861_i2c_algo = {
+ .master_xfer = gl861_i2c_xfer,
+ .functionality = gl861_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+static struct zl10353_config gl861_zl10353_config = {
+ .demod_address = 0x0f,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+
+static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
+{
+
+ adap->fe[0] = dvb_attach(zl10353_attach, &gl861_zl10353_config,
+ &adap_to_d(adap)->i2c_adap);
+ if (adap->fe[0] == NULL)
+ return -EIO;
+
+ return 0;
+}
+
+static struct qt1010_config gl861_qt1010_config = {
+ .i2c_address = 0x62
+};
+
+static int gl861_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ return dvb_attach(qt1010_attach,
+ adap->fe[0], &adap_to_d(adap)->i2c_adap,
+ &gl861_qt1010_config) == NULL ? -ENODEV : 0;
+}
+
+static int gl861_init(struct dvb_usb_device *d)
+{
+ /*
+ * There is 2 interfaces. Interface 0 is for TV and interface 1 is
+ * for HID remote controller. Interface 0 has 2 alternate settings.
+ * For some reason we need to set interface explicitly, defaulted
+ * as alternate setting 1?
+ */
+ return usb_set_interface(d->udev, 0, 0);
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties gl861_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+
+ .i2c_algo = &gl861_i2c_algo,
+ .frontend_attach = gl861_frontend_attach,
+ .tuner_attach = gl861_tuner_attach,
+ .init = gl861_init,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x81, 7, 512),
+ }
+ }
+};
+
+static const struct usb_device_id gl861_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801,
+ &gl861_props, "MSI Mega Sky 55801 DVB-T USB2.0", NULL) },
+ { DVB_USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU,
+ &gl861_props, "A-LINK DTU DVB-T USB2.0", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, gl861_id_table);
+
+static struct usb_driver gl861_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = gl861_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(gl861_usb_driver);
+
+MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
+MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/gl861.h b/drivers/media/usb/dvb-usb-v2/gl861.h
new file mode 100644
index 000000000000..b0b80d87bb7e
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/gl861.h
@@ -0,0 +1,12 @@
+#ifndef _DVB_USB_GL861_H_
+#define _DVB_USB_GL861_H_
+
+#include "dvb_usb.h"
+
+#define GL861_WRITE 0x40
+#define GL861_READ 0xc0
+
+#define GL861_REQ_I2C_WRITE 0x01
+#define GL861_REQ_I2C_READ 0x02
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/it913x.c b/drivers/media/usb/dvb-usb-v2/it913x.c
new file mode 100644
index 000000000000..695f9106bc54
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/it913x.c
@@ -0,0 +1,799 @@
+/*
+ * DVB USB compliant linux driver for ITE IT9135 and IT9137
+ *
+ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com)
+ * IT9135 (C) ITE Tech Inc.
+ * IT9137 (C) ITE Tech Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ * see Documentation/dvb/it9137.txt for firmware information
+ *
+ */
+#define DVB_USB_LOG_PREFIX "it913x"
+
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#include "dvb_usb.h"
+#include "it913x-fe.h"
+
+/* debug */
+static int dvb_usb_it913x_debug;
+#define it_debug(var, level, args...) \
+ do { if ((var & level)) pr_debug(DVB_USB_LOG_PREFIX": " args); \
+} while (0)
+#define deb_info(level, args...) it_debug(dvb_usb_it913x_debug, level, args)
+#define info(args...) pr_info(DVB_USB_LOG_PREFIX": " args)
+
+module_param_named(debug, dvb_usb_it913x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+static int dvb_usb_it913x_firmware;
+module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644);
+MODULE_PARM_DESC(firmware, "set firmware 0=auto"\
+ "1=IT9137 2=IT9135 V1 3=IT9135 V2");
+#define FW_IT9137 "dvb-usb-it9137-01.fw"
+#define FW_IT9135_V1 "dvb-usb-it9135-01.fw"
+#define FW_IT9135_V2 "dvb-usb-it9135-02.fw"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct it913x_state {
+ struct ite_config it913x_config;
+ u8 pid_filter_onoff;
+ bool proprietary_ir;
+ int cmd_counter;
+};
+
+static u16 check_sum(u8 *p, u8 len)
+{
+ u16 sum = 0;
+ u8 i = 1;
+ while (i < len)
+ sum += (i++ & 1) ? (*p++) << 8 : *p++;
+ return ~sum;
+}
+
+static int it913x_io(struct dvb_usb_device *d, u8 mode, u8 pro,
+ u8 cmd, u32 reg, u8 addr, u8 *data, u8 len)
+{
+ struct it913x_state *st = d->priv;
+ int ret = 0, i, buf_size = 1;
+ u8 *buff;
+ u8 rlen;
+ u16 chk_sum;
+
+ buff = kzalloc(256, GFP_KERNEL);
+ if (!buff) {
+ info("USB Buffer Failed");
+ return -ENOMEM;
+ }
+
+ buff[buf_size++] = pro;
+ buff[buf_size++] = cmd;
+ buff[buf_size++] = st->cmd_counter;
+
+ switch (mode) {
+ case READ_LONG:
+ case WRITE_LONG:
+ buff[buf_size++] = len;
+ buff[buf_size++] = 2;
+ buff[buf_size++] = (reg >> 24);
+ buff[buf_size++] = (reg >> 16) & 0xff;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_SHORT:
+ buff[buf_size++] = addr;
+ break;
+ case WRITE_SHORT:
+ buff[buf_size++] = len;
+ buff[buf_size++] = addr;
+ buff[buf_size++] = (reg >> 8) & 0xff;
+ buff[buf_size++] = reg & 0xff;
+ break;
+ case READ_DATA:
+ case WRITE_DATA:
+ break;
+ case WRITE_CMD:
+ mode = 7;
+ break;
+ default:
+ kfree(buff);
+ return -EINVAL;
+ }
+
+ if (mode & 1) {
+ for (i = 0; i < len ; i++)
+ buff[buf_size++] = data[i];
+ }
+ chk_sum = check_sum(&buff[1], buf_size);
+
+ buff[buf_size++] = chk_sum >> 8;
+ buff[0] = buf_size;
+ buff[buf_size++] = (chk_sum & 0xff);
+
+ ret = dvb_usbv2_generic_rw(d, buff, buf_size, buff, (mode & 1) ?
+ 5 : len + 5);
+ if (ret < 0)
+ goto error;
+
+ rlen = (mode & 0x1) ? 0x1 : len;
+
+ if (mode & 1)
+ ret = buff[2];
+ else
+ memcpy(data, &buff[3], rlen);
+
+ st->cmd_counter++;
+
+error: kfree(buff);
+
+ return ret;
+}
+
+static int it913x_wr_reg(struct dvb_usb_device *d, u8 pro, u32 reg , u8 data)
+{
+ int ret;
+ u8 b[1];
+ b[0] = data;
+ ret = it913x_io(d, WRITE_LONG, pro,
+ CMD_DEMOD_WRITE, reg, 0, b, sizeof(b));
+
+ return ret;
+}
+
+static int it913x_read_reg(struct dvb_usb_device *d, u32 reg)
+{
+ int ret;
+ u8 data[1];
+
+ ret = it913x_io(d, READ_LONG, DEV_0,
+ CMD_DEMOD_READ, reg, 0, &data[0], sizeof(data));
+
+ return (ret < 0) ? ret : data[0];
+}
+
+static int it913x_query(struct dvb_usb_device *d, u8 pro)
+{
+ struct it913x_state *st = d->priv;
+ int ret, i;
+ u8 data[4];
+ u8 ver;
+
+ for (i = 0; i < 5; i++) {
+ ret = it913x_io(d, READ_LONG, pro, CMD_DEMOD_READ,
+ 0x1222, 0, &data[0], 3);
+ ver = data[0];
+ if (ver > 0 && ver < 3)
+ break;
+ msleep(100);
+ }
+
+ if (ver < 1 || ver > 2) {
+ info("Failed to identify chip version applying 1");
+ st->it913x_config.chip_ver = 0x1;
+ st->it913x_config.chip_type = 0x9135;
+ return 0;
+ }
+
+ st->it913x_config.chip_ver = ver;
+ st->it913x_config.chip_type = (u16)(data[2] << 8) + data[1];
+
+ info("Chip Version=%02x Chip Type=%04x", st->it913x_config.chip_ver,
+ st->it913x_config.chip_type);
+
+ ret = it913x_io(d, READ_SHORT, pro,
+ CMD_QUERYINFO, 0, 0x1, &data[0], 4);
+
+ st->it913x_config.firmware = (data[0] << 24) | (data[1] << 16) |
+ (data[2] << 8) | data[3];
+
+ return ret;
+}
+
+static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct it913x_state *st = adap_to_priv(adap);
+ int ret;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ mutex_lock(&d->i2c_mutex);
+
+ deb_info(1, "PID_C (%02x)", onoff);
+
+ ret = it913x_wr_reg(d, pro, PID_EN, st->pid_filter_onoff);
+
+ mutex_unlock(&d->i2c_mutex);
+ return ret;
+}
+
+static int it913x_pid_filter(struct dvb_usb_adapter *adap,
+ int index, u16 pid, int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct it913x_state *st = adap_to_priv(adap);
+ int ret;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ mutex_lock(&d->i2c_mutex);
+
+ deb_info(1, "PID_F (%02x)", onoff);
+
+ ret = it913x_wr_reg(d, pro, PID_LSB, (u8)(pid & 0xff));
+
+ ret |= it913x_wr_reg(d, pro, PID_MSB, (u8)(pid >> 8));
+
+ ret |= it913x_wr_reg(d, pro, PID_INX_EN, (u8)onoff);
+
+ ret |= it913x_wr_reg(d, pro, PID_INX, (u8)(index & 0x1f));
+
+ if (d->udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
+ ret |= it913x_wr_reg(d , pro, PID_EN, !onoff);
+ st->pid_filter_onoff = !onoff;
+ } else
+ st->pid_filter_onoff =
+ adap->pid_filtering;
+
+ mutex_unlock(&d->i2c_mutex);
+ return 0;
+}
+
+
+static int it913x_return_status(struct dvb_usb_device *d)
+{
+ struct it913x_state *st = d->priv;
+ int ret = it913x_query(d, DEV_0);
+ if (st->it913x_config.firmware > 0)
+ info("Firmware Version %d", st->it913x_config.firmware);
+
+ return ret;
+}
+
+static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ static u8 data[256];
+ int ret;
+ u32 reg;
+ u8 pro;
+
+ mutex_lock(&d->i2c_mutex);
+
+ deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
+
+ pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0;
+ pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0;
+ memcpy(data, msg[0].buf, msg[0].len);
+ reg = (data[0] << 24) + (data[1] << 16) +
+ (data[2] << 8) + data[3];
+ if (num == 2) {
+ ret = it913x_io(d, READ_LONG, pro,
+ CMD_DEMOD_READ, reg, 0, data, msg[1].len);
+ memcpy(msg[1].buf, data, msg[1].len);
+ } else
+ ret = it913x_io(d, WRITE_LONG, pro, CMD_DEMOD_WRITE,
+ reg, 0, &data[4], msg[0].len - 4);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static u32 it913x_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm it913x_i2c_algo = {
+ .master_xfer = it913x_i2c_xfer,
+ .functionality = it913x_i2c_func,
+};
+
+/* Callbacks for DVB USB */
+#define IT913X_POLL 250
+static int it913x_rc_query(struct dvb_usb_device *d)
+{
+ u8 ibuf[4];
+ int ret;
+ u32 key;
+ /* Avoid conflict with frontends*/
+ mutex_lock(&d->i2c_mutex);
+
+ ret = it913x_io(d, READ_LONG, PRO_LINK, CMD_IR_GET,
+ 0, 0, &ibuf[0], sizeof(ibuf));
+
+ if ((ibuf[2] + ibuf[3]) == 0xff) {
+ key = ibuf[2];
+ key += ibuf[0] << 16;
+ key += ibuf[1] << 8;
+ deb_info(1, "NEC Extended Key =%08x", key);
+ if (d->rc_dev != NULL)
+ rc_keydown(d->rc_dev, key, 0);
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+/* Firmware sets raw */
+static const char fw_it9135_v1[] = FW_IT9135_V1;
+static const char fw_it9135_v2[] = FW_IT9135_V2;
+static const char fw_it9137[] = FW_IT9137;
+
+static void ite_get_firmware_name(struct dvb_usb_device *d,
+ const char **name)
+{
+ struct it913x_state *st = d->priv;
+ int sw;
+ /* auto switch */
+ if (le16_to_cpu(d->udev->descriptor.idVendor) == USB_VID_KWORLD_2)
+ sw = IT9137_FW;
+ else if (st->it913x_config.chip_ver == 1)
+ sw = IT9135_V1_FW;
+ else
+ sw = IT9135_V2_FW;
+
+ /* force switch */
+ if (dvb_usb_it913x_firmware != IT9135_AUTO)
+ sw = dvb_usb_it913x_firmware;
+
+ switch (sw) {
+ case IT9135_V1_FW:
+ st->it913x_config.firmware_ver = 1;
+ st->it913x_config.adc_x2 = 1;
+ st->it913x_config.read_slevel = false;
+ *name = fw_it9135_v1;
+ break;
+ case IT9135_V2_FW:
+ st->it913x_config.firmware_ver = 1;
+ st->it913x_config.adc_x2 = 1;
+ st->it913x_config.read_slevel = false;
+ *name = fw_it9135_v2;
+ switch (st->it913x_config.tuner_id_0) {
+ case IT9135_61:
+ case IT9135_62:
+ break;
+ default:
+ info("Unknown tuner ID applying default 0x60");
+ case IT9135_60:
+ st->it913x_config.tuner_id_0 = IT9135_60;
+ }
+ break;
+ case IT9137_FW:
+ default:
+ st->it913x_config.firmware_ver = 0;
+ st->it913x_config.adc_x2 = 0;
+ st->it913x_config.read_slevel = true;
+ *name = fw_it9137;
+ }
+
+ return;
+}
+
+#define TS_MPEG_PKT_SIZE 188
+#define EP_LOW 21
+#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
+#define EP_HIGH 348
+#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
+
+static int it913x_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
+ struct usb_data_stream_properties *stream)
+{
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ if (adap->pid_filtering)
+ stream->u.bulk.buffersize = TS_BUFFER_SIZE_PID;
+ else
+ stream->u.bulk.buffersize = TS_BUFFER_SIZE_MAX;
+
+ return 0;
+}
+
+static int it913x_select_config(struct dvb_usb_device *d)
+{
+ struct it913x_state *st = d->priv;
+ int ret, reg;
+
+ ret = it913x_return_status(d);
+ if (ret < 0)
+ return ret;
+
+ if (st->it913x_config.chip_ver == 0x02
+ && st->it913x_config.chip_type == 0x9135)
+ reg = it913x_read_reg(d, 0x461d);
+ else
+ reg = it913x_read_reg(d, 0x461b);
+
+ if (reg < 0)
+ return reg;
+
+ if (reg == 0) {
+ st->it913x_config.dual_mode = 0;
+ st->it913x_config.tuner_id_0 = IT9135_38;
+ st->proprietary_ir = true;
+ } else {
+ /* TS mode */
+ reg = it913x_read_reg(d, 0x49c5);
+ if (reg < 0)
+ return reg;
+ st->it913x_config.dual_mode = reg;
+
+ /* IR mode type */
+ reg = it913x_read_reg(d, 0x49ac);
+ if (reg < 0)
+ return reg;
+ if (reg == 5) {
+ info("Remote propriety (raw) mode");
+ st->proprietary_ir = true;
+ } else if (reg == 1) {
+ info("Remote HID mode NOT SUPPORTED");
+ st->proprietary_ir = false;
+ }
+
+ /* Tuner_id */
+ reg = it913x_read_reg(d, 0x49d0);
+ if (reg < 0)
+ return reg;
+ st->it913x_config.tuner_id_0 = reg;
+ }
+
+ info("Dual mode=%x Tuner Type=%x", st->it913x_config.dual_mode,
+ st->it913x_config.tuner_id_0);
+
+ return ret;
+}
+
+static int it913x_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct it913x_state *st = fe_to_priv(fe);
+ int ret = 0;
+ u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
+
+ deb_info(1, "STM (%02x)", onoff);
+
+ if (!onoff) {
+ mutex_lock(&d->i2c_mutex);
+
+ ret = it913x_wr_reg(d, pro, PID_RST, 0x1);
+
+ mutex_unlock(&d->i2c_mutex);
+ st->pid_filter_onoff =
+ adap->pid_filtering;
+
+ }
+
+ return ret;
+}
+
+static int it913x_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ struct it913x_state *st = d->priv;
+ int ret;
+ u8 reg;
+
+ /* Read and select config */
+ ret = it913x_select_config(d);
+ if (ret < 0)
+ return ret;
+
+ ite_get_firmware_name(d, name);
+
+ if (st->it913x_config.firmware > 0)
+ return WARM;
+
+ if (st->it913x_config.dual_mode) {
+ st->it913x_config.tuner_id_1 = it913x_read_reg(d, 0x49e0);
+ ret = it913x_wr_reg(d, DEV_0, GPIOH1_EN, 0x1);
+ ret |= it913x_wr_reg(d, DEV_0, GPIOH1_ON, 0x1);
+ ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1);
+ msleep(50);
+ ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x0);
+ msleep(50);
+ reg = it913x_read_reg(d, GPIOH1_O);
+ if (reg == 0) {
+ ret |= it913x_wr_reg(d, DEV_0, GPIOH1_O, 0x1);
+ ret |= it913x_return_status(d);
+ if (ret != 0)
+ ret = it913x_wr_reg(d, DEV_0,
+ GPIOH1_O, 0x0);
+ }
+ }
+
+ reg = it913x_read_reg(d, IO_MUX_POWER_CLK);
+
+ if (st->it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, CHIP2_I2C_ADDR);
+ if (st->it913x_config.firmware_ver == 1)
+ ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x1);
+ else
+ ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x1);
+ } else {
+ ret |= it913x_wr_reg(d, DEV_0, 0x4bfb, 0x0);
+ if (st->it913x_config.firmware_ver == 1)
+ ret |= it913x_wr_reg(d, DEV_0, 0xcfff, 0x0);
+ else
+ ret |= it913x_wr_reg(d, DEV_0, CLK_O_EN, 0x0);
+ }
+
+ ret |= it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100);
+
+ return (ret < 0) ? ret : COLD;
+}
+
+static int it913x_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ struct it913x_state *st = d->priv;
+ int ret = 0, i = 0, pos = 0;
+ u8 packet_size, min_pkt;
+ u8 *fw_data;
+
+ ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_100);
+
+ info("FRM Starting Firmware Download");
+
+ /* Multi firmware loader */
+ /* This uses scatter write firmware headers */
+ /* The firmware must start with 03 XX 00 */
+ /* and be the extact firmware length */
+
+ if (st->it913x_config.chip_ver == 2)
+ min_pkt = 0x11;
+ else
+ min_pkt = 0x19;
+
+ while (i <= fw->size) {
+ if (((fw->data[i] == 0x3) && (fw->data[i + 2] == 0x0))
+ || (i == fw->size)) {
+ packet_size = i - pos;
+ if ((packet_size > min_pkt) || (i == fw->size)) {
+ fw_data = (u8 *)(fw->data + pos);
+ pos += packet_size;
+ if (packet_size > 0) {
+ ret = it913x_io(d, WRITE_DATA,
+ DEV_0, CMD_SCATTER_WRITE, 0,
+ 0, fw_data, packet_size);
+ if (ret < 0)
+ break;
+ }
+ udelay(1000);
+ }
+ }
+ i++;
+ }
+
+ if (ret < 0)
+ info("FRM Firmware Download Failed (%d)" , ret);
+ else
+ info("FRM Firmware Download Completed - Resetting Device");
+
+ msleep(30);
+
+ ret = it913x_io(d, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0);
+ if (ret < 0)
+ info("FRM Device not responding to reboot");
+
+ ret = it913x_return_status(d);
+ if (st->it913x_config.firmware == 0) {
+ info("FRM Failed to reboot device");
+ return -ENODEV;
+ }
+
+ msleep(30);
+
+ ret = it913x_wr_reg(d, DEV_0, I2C_CLK, I2C_CLK_400);
+
+ msleep(30);
+
+ /* Tuner function */
+ if (st->it913x_config.dual_mode)
+ ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0xa0);
+ else
+ ret |= it913x_wr_reg(d, DEV_0_DMOD , 0xec4c, 0x68);
+
+ if ((st->it913x_config.chip_ver == 1) &&
+ (st->it913x_config.chip_type == 0x9135)) {
+ ret |= it913x_wr_reg(d, DEV_0, PADODPU, 0x0);
+ ret |= it913x_wr_reg(d, DEV_0, AGC_O_D, 0x0);
+ if (st->it913x_config.dual_mode) {
+ ret |= it913x_wr_reg(d, DEV_1, PADODPU, 0x0);
+ ret |= it913x_wr_reg(d, DEV_1, AGC_O_D, 0x0);
+ }
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int it913x_name(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ const char *desc = d->name;
+ char *fe_name[] = {"_1", "_2", "_3", "_4"};
+ char *name = adap->fe[0]->ops.info.name;
+
+ strlcpy(name, desc, 128);
+ strlcat(name, fe_name[adap->id], 128);
+
+ return 0;
+}
+
+static int it913x_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct it913x_state *st = d->priv;
+ int ret = 0;
+ u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5);
+ u16 ep_size = adap->stream.buf_size / 4;
+ u8 pkt_size = 0x80;
+
+ if (d->udev->speed != USB_SPEED_HIGH)
+ pkt_size = 0x10;
+
+ st->it913x_config.adf = it913x_read_reg(d, IO_MUX_POWER_CLK);
+
+ adap->fe[0] = dvb_attach(it913x_fe_attach,
+ &d->i2c_adap, adap_addr, &st->it913x_config);
+
+ if (adap->id == 0 && adap->fe[0]) {
+ it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x1);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x1);
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x0f);
+ it913x_wr_reg(d, DEV_0, EP0_TX_NAK, 0x1b);
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x2f);
+ it913x_wr_reg(d, DEV_0, EP4_TX_LEN_LSB,
+ ep_size & 0xff);
+ it913x_wr_reg(d, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8);
+ ret = it913x_wr_reg(d, DEV_0, EP4_MAX_PKT, pkt_size);
+ } else if (adap->id == 1 && adap->fe[0]) {
+ it913x_wr_reg(d, DEV_0, EP0_TX_EN, 0x6f);
+ it913x_wr_reg(d, DEV_0, EP5_TX_LEN_LSB,
+ ep_size & 0xff);
+ it913x_wr_reg(d, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8);
+ it913x_wr_reg(d, DEV_0, EP5_MAX_PKT, pkt_size);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_EN, 0x1);
+ it913x_wr_reg(d, DEV_1_DMOD, MP2IF_SERIAL, 0x1);
+ it913x_wr_reg(d, DEV_1, TOP_HOSTB_SER_MODE, 0x1);
+ it913x_wr_reg(d, DEV_0_DMOD, TSIS_ENABLE, 0x1);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2_SW_RST, 0x0);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_SW_RST, 0x0);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0);
+ it913x_wr_reg(d, DEV_0_DMOD, MP2IF_STOP_EN, 0x1);
+ it913x_wr_reg(d, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0);
+ ret = it913x_wr_reg(d, DEV_1_DMOD, MP2IF_STOP_EN, 0x0);
+ } else
+ return -ENODEV;
+
+ ret |= it913x_name(adap);
+
+ return ret;
+}
+
+/* DVB USB Driver */
+static int it913x_get_rc_config(struct dvb_usb_device *d, struct dvb_usb_rc *rc)
+{
+ struct it913x_state *st = d->priv;
+
+ if (st->proprietary_ir == false) {
+ rc->map_name = NULL;
+ return 0;
+ }
+
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = it913x_rc_query;
+ rc->interval = 250;
+
+ return 0;
+}
+
+static int it913x_get_adapter_count(struct dvb_usb_device *d)
+{
+ struct it913x_state *st = d->priv;
+ if (st->it913x_config.dual_mode)
+ return 2;
+ return 1;
+}
+
+static struct dvb_usb_device_properties it913x_properties = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .bInterfaceNumber = 0,
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct it913x_state),
+
+ .identify_state = it913x_identify_state,
+ .i2c_algo = &it913x_i2c_algo,
+
+ .download_firmware = it913x_download_firmware,
+
+ .frontend_attach = it913x_frontend_attach,
+ .get_rc_config = it913x_get_rc_config,
+ .get_stream_config = it913x_get_stream_config,
+ .get_adapter_count = it913x_get_adapter_count,
+ .streaming_ctrl = it913x_streaming_ctrl,
+
+
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .stream =
+ DVB_USB_STREAM_BULK(0x84, 10, TS_BUFFER_SIZE_MAX),
+ },
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 32,
+ .pid_filter = it913x_pid_filter,
+ .pid_filter_ctrl = it913x_pid_filter_ctrl,
+ .stream =
+ DVB_USB_STREAM_BULK(0x85, 10, TS_BUFFER_SIZE_MAX),
+ }
+ }
+};
+
+static const struct usb_device_id it913x_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09,
+ &it913x_properties, "Kworld UB499-2T T09(IT9137)",
+ RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135,
+ &it913x_properties, "ITE 9135 Generic",
+ RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137,
+ &it913x_properties, "Sveon STV22 Dual DVB-T HDTV(IT9137)",
+ RC_MAP_IT913X_V1) },
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005,
+ &it913x_properties, "ITE 9135(9005) Generic",
+ RC_MAP_IT913X_V2) },
+ { DVB_USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006,
+ &it913x_properties, "ITE 9135(9006) Generic",
+ RC_MAP_IT913X_V1) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, it913x_id_table);
+
+static struct usb_driver it913x_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .id_table = it913x_id_table,
+};
+
+module_usb_driver(it913x_driver);
+
+MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
+MODULE_DESCRIPTION("it913x USB 2 Driver");
+MODULE_VERSION("1.32");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FW_IT9135_V1);
+MODULE_FIRMWARE(FW_IT9135_V2);
+MODULE_FIRMWARE(FW_IT9137);
+
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
new file mode 100644
index 000000000000..c41d9d9ec7b5
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -0,0 +1,1369 @@
+/* DVB USB compliant linux driver for
+ *
+ * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395
+ * LME2510C + LG TDQY-P001F
+ * LME2510C + BS2F7HZ0194
+ * LME2510 + LG TDQY-P001F
+ * LME2510 + BS2F7HZ0194
+ *
+ * MVB7395 (LME2510C+SHARP:BS2F7HZ7395)
+ * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V)
+ *
+ * MV001F (LME2510+LGTDQY-P001F)
+ * LG TDQY - P001F =(TDA8263 + TDA10086H)
+ *
+ * MVB0001F (LME2510C+LGTDQT-P001F)
+ *
+ * MV0194 (LME2510+SHARP:BS2F7HZ0194)
+ * SHARP:BS2F7HZ0194 = (STV0299+IX2410)
+ *
+ * MVB0194 (LME2510C+SHARP0194)
+ *
+ * LME2510C + M88RS2000
+ *
+ * For firmware see Documentation/dvb/lmedm04.txt
+ *
+ * I2C addresses:
+ * 0xd0 - STV0288 - Demodulator
+ * 0xc0 - Sharp IX2505V - Tuner
+ * --
+ * 0x1c - TDA10086 - Demodulator
+ * 0xc0 - TDA8263 - Tuner
+ * --
+ * 0xd0 - STV0299 - Demodulator
+ * 0xc0 - IX2410 - Tuner
+ *
+ *
+ * VID = 3344 PID LME2510=1122 LME2510C=1120
+ *
+ * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com)
+ * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ *
+ * Known Issues :
+ * LME2510: Non Intel USB chipsets fail to maintain High Speed on
+ * Boot or Hot Plug.
+ *
+ * QQbox suffers from noise on LNB voltage.
+ *
+ * LME2510: SHARP:BS2F7HZ0194(MV0194) cannot cold reset and share system
+ * with other tuners. After a cold reset streaming will not start.
+ *
+ * M88RS2000 suffers from loss of lock.
+ */
+#define DVB_USB_LOG_PREFIX "LME2510(C)"
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+#include "dvb_usb.h"
+#include "lmedm04.h"
+#include "tda826x.h"
+#include "tda10086.h"
+#include "stv0288.h"
+#include "ix2505v.h"
+#include "stv0299.h"
+#include "dvb-pll.h"
+#include "z0194a.h"
+#include "m88rs2000.h"
+
+
+#define LME2510_C_S7395 "dvb-usb-lme2510c-s7395.fw";
+#define LME2510_C_LG "dvb-usb-lme2510c-lg.fw";
+#define LME2510_C_S0194 "dvb-usb-lme2510c-s0194.fw";
+#define LME2510_C_RS2000 "dvb-usb-lme2510c-rs2000.fw";
+#define LME2510_LG "dvb-usb-lme2510-lg.fw";
+#define LME2510_S0194 "dvb-usb-lme2510-s0194.fw";
+
+/* debug */
+static int dvb_usb_lme2510_debug;
+#define lme_debug(var, level, args...) do { \
+ if ((var >= level)) \
+ pr_debug(DVB_USB_LOG_PREFIX": " args); \
+} while (0)
+#define deb_info(level, args...) lme_debug(dvb_usb_lme2510_debug, level, args)
+#define debug_data_snipet(level, name, p) \
+ deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \
+ *p, *(p+1), *(p+2), *(p+3), *(p+4), \
+ *(p+5), *(p+6), *(p+7));
+#define info(args...) pr_info(DVB_USB_LOG_PREFIX": "args)
+
+module_param_named(debug, dvb_usb_lme2510_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+static int dvb_usb_lme2510_firmware;
+module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644);
+MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG");
+
+static int pid_filter;
+module_param_named(pid, pid_filter, int, 0644);
+MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on");
+
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define TUNER_DEFAULT 0x0
+#define TUNER_LG 0x1
+#define TUNER_S7395 0x2
+#define TUNER_S0194 0x3
+#define TUNER_RS2000 0x4
+
+struct lme2510_state {
+ u8 id;
+ u8 tuner_config;
+ u8 signal_lock;
+ u8 signal_level;
+ u8 signal_sn;
+ u8 time_key;
+ u8 last_key;
+ u8 key_timeout;
+ u8 i2c_talk_onoff;
+ u8 i2c_gate;
+ u8 i2c_tuner_gate_w;
+ u8 i2c_tuner_gate_r;
+ u8 i2c_tuner_addr;
+ u8 stream_on;
+ u8 pid_size;
+ u8 pid_off;
+ void *buffer;
+ struct urb *lme_urb;
+ void *usb_buffer;
+ int (*fe_set_voltage)(struct dvb_frontend *, fe_sec_voltage_t);
+ u8 dvb_usb_lme2510_firmware;
+};
+
+static int lme2510_bulk_write(struct usb_device *dev,
+ u8 *snd, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
+ snd, len , &actual_l, 100);
+ return ret;
+}
+
+static int lme2510_bulk_read(struct usb_device *dev,
+ u8 *rev, int len, u8 pipe)
+{
+ int ret, actual_l;
+
+ ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
+ rev, len , &actual_l, 200);
+ return ret;
+}
+
+static int lme2510_usb_talk(struct dvb_usb_device *d,
+ u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ struct lme2510_state *st = d->priv;
+ u8 *buff;
+ int ret = 0;
+
+ if (st->usb_buffer == NULL) {
+ st->usb_buffer = kmalloc(64, GFP_KERNEL);
+ if (st->usb_buffer == NULL) {
+ info("MEM Error no memory");
+ return -ENOMEM;
+ }
+ }
+ buff = st->usb_buffer;
+
+ ret = mutex_lock_interruptible(&d->usb_mutex);
+
+ if (ret < 0)
+ return -EAGAIN;
+
+ /* the read/write capped at 64 */
+ memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
+
+ ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
+
+ ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
+ rlen : 64 , 0x01);
+
+ if (rlen > 0)
+ memcpy(rbuf, buff, rlen);
+
+ mutex_unlock(&d->usb_mutex);
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int lme2510_stream_restart(struct dvb_usb_device *d)
+{
+ struct lme2510_state *st = d->priv;
+ u8 all_pids[] = LME_ALL_PIDS;
+ u8 stream_on[] = LME_ST_ON_W;
+ int ret;
+ u8 rbuff[1];
+ if (st->pid_off)
+ ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
+ rbuff, sizeof(rbuff));
+ /*Restart Stream Command*/
+ ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
+ rbuff, sizeof(rbuff));
+ return ret;
+}
+
+static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out)
+{
+ struct lme2510_state *st = d->priv;
+ static u8 pid_buff[] = LME_ZERO_PID;
+ static u8 rbuf[1];
+ u8 pid_no = index * 2;
+ u8 pid_len = pid_no + 2;
+ int ret = 0;
+ deb_info(1, "PID Setting Pid %04x", pid_out);
+
+ if (st->pid_size == 0)
+ ret |= lme2510_stream_restart(d);
+
+ pid_buff[2] = pid_no;
+ pid_buff[3] = (u8)pid_out & 0xff;
+ pid_buff[4] = pid_no + 1;
+ pid_buff[5] = (u8)(pid_out >> 8);
+
+ if (pid_len > st->pid_size)
+ st->pid_size = pid_len;
+ pid_buff[7] = 0x80 + st->pid_size;
+
+ ret |= lme2510_usb_talk(d, pid_buff ,
+ sizeof(pid_buff) , rbuf, sizeof(rbuf));
+
+ if (st->stream_on)
+ ret |= lme2510_stream_restart(d);
+
+ return ret;
+}
+
+static void lme2510_int_response(struct urb *lme_urb)
+{
+ struct dvb_usb_adapter *adap = lme_urb->context;
+ struct lme2510_state *st = adap_to_priv(adap);
+ static u8 *ibuf, *rbuf;
+ int i = 0, offset;
+ u32 key;
+
+ switch (lme_urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ info("Error %x", lme_urb->status);
+ break;
+ }
+
+ rbuf = (u8 *) lme_urb->transfer_buffer;
+
+ offset = ((lme_urb->actual_length/8) > 4)
+ ? 4 : (lme_urb->actual_length/8) ;
+
+ for (i = 0; i < offset; ++i) {
+ ibuf = (u8 *)&rbuf[i*8];
+ deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x",
+ offset, i, ibuf[0], ibuf[1]);
+
+ switch (ibuf[0]) {
+ case 0xaa:
+ debug_data_snipet(1, "INT Remote data snipet", ibuf);
+ if ((ibuf[4] + ibuf[5]) == 0xff) {
+ key = ibuf[5];
+ key += (ibuf[3] > 0)
+ ? (ibuf[3] ^ 0xff) << 8 : 0;
+ key += (ibuf[2] ^ 0xff) << 16;
+ deb_info(1, "INT Key =%08x", key);
+ if (adap_to_d(adap)->rc_dev != NULL)
+ rc_keydown(adap_to_d(adap)->rc_dev,
+ key, 0);
+ }
+ break;
+ case 0xbb:
+ switch (st->tuner_config) {
+ case TUNER_LG:
+ if (ibuf[2] > 0)
+ st->signal_lock = ibuf[2];
+ st->signal_level = ibuf[4];
+ st->signal_sn = ibuf[3];
+ st->time_key = ibuf[7];
+ break;
+ case TUNER_S7395:
+ case TUNER_S0194:
+ /* Tweak for earlier firmware*/
+ if (ibuf[1] == 0x03) {
+ if (ibuf[2] > 1)
+ st->signal_lock = ibuf[2];
+ st->signal_level = ibuf[3];
+ st->signal_sn = ibuf[4];
+ } else {
+ st->signal_level = ibuf[4];
+ st->signal_sn = ibuf[5];
+ st->signal_lock =
+ (st->signal_lock & 0xf7) +
+ ((ibuf[2] & 0x01) << 0x03);
+ }
+ break;
+ case TUNER_RS2000:
+ if (ibuf[1] == 0x3 && ibuf[6] == 0xff)
+ st->signal_lock = 0xff;
+ else
+ st->signal_lock = 0x00;
+ st->signal_level = ibuf[5];
+ st->signal_sn = ibuf[4];
+ st->time_key = ibuf[7];
+ default:
+ break;
+ }
+ debug_data_snipet(5, "INT Remote data snipet in", ibuf);
+ break;
+ case 0xcc:
+ debug_data_snipet(1, "INT Control data snipet", ibuf);
+ break;
+ default:
+ debug_data_snipet(1, "INT Unknown data snipet", ibuf);
+ break;
+ }
+ }
+ usb_submit_urb(lme_urb, GFP_ATOMIC);
+}
+
+static int lme2510_int_read(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *lme_int = adap_to_priv(adap);
+
+ lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ if (lme_int->lme_urb == NULL)
+ return -ENOMEM;
+
+ lme_int->buffer = usb_alloc_coherent(d->udev, 128, GFP_ATOMIC,
+ &lme_int->lme_urb->transfer_dma);
+
+ if (lme_int->buffer == NULL)
+ return -ENOMEM;
+
+ usb_fill_int_urb(lme_int->lme_urb,
+ d->udev,
+ usb_rcvintpipe(d->udev, 0xa),
+ lme_int->buffer,
+ 128,
+ lme2510_int_response,
+ adap,
+ 8);
+
+ lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC);
+ info("INT Interrupt Service Started");
+
+ return 0;
+}
+
+static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = adap_to_priv(adap);
+ static u8 clear_pid_reg[] = LME_ALL_PIDS;
+ static u8 rbuf[1];
+ int ret = 0;
+
+ deb_info(1, "PID Clearing Filter");
+
+ mutex_lock(&d->i2c_mutex);
+
+ if (!onoff) {
+ ret |= lme2510_usb_talk(d, clear_pid_reg,
+ sizeof(clear_pid_reg), rbuf, sizeof(rbuf));
+ st->pid_off = true;
+ } else
+ st->pid_off = false;
+
+ st->pid_size = 0;
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return 0;
+}
+
+static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+ int onoff)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ int ret = 0;
+
+ deb_info(3, "%s PID=%04x Index=%04x onoff=%02x", __func__,
+ pid, index, onoff);
+
+ if (onoff) {
+ mutex_lock(&d->i2c_mutex);
+ ret |= lme2510_enable_pid(d, index, pid);
+ mutex_unlock(&d->i2c_mutex);
+ }
+
+
+ return ret;
+}
+
+
+static int lme2510_return_status(struct dvb_usb_device *d)
+{
+ int ret = 0;
+ u8 *data;
+
+ data = kzalloc(10, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret |= usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
+ 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200);
+ info("Firmware Status: %x (%x)", ret , data[2]);
+
+ ret = (ret < 0) ? -ENODEV : data[2];
+ kfree(data);
+ return ret;
+}
+
+static int lme2510_msg(struct dvb_usb_device *d,
+ u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ int ret = 0;
+ struct lme2510_state *st = d->priv;
+
+ if (st->i2c_talk_onoff == 1) {
+
+ ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+
+ switch (st->tuner_config) {
+ case TUNER_LG:
+ if (wbuf[2] == 0x1c) {
+ if (wbuf[3] == 0x0e) {
+ st->signal_lock = rbuf[1];
+ if ((st->stream_on & 1) &&
+ (st->signal_lock & 0x10)) {
+ lme2510_stream_restart(d);
+ st->i2c_talk_onoff = 0;
+ }
+ msleep(80);
+ }
+ }
+ break;
+ case TUNER_S7395:
+ if (wbuf[2] == 0xd0) {
+ if (wbuf[3] == 0x24) {
+ st->signal_lock = rbuf[1];
+ if ((st->stream_on & 1) &&
+ (st->signal_lock & 0x8)) {
+ lme2510_stream_restart(d);
+ st->i2c_talk_onoff = 0;
+ }
+ }
+ }
+ break;
+ case TUNER_S0194:
+ if (wbuf[2] == 0xd0) {
+ if (wbuf[3] == 0x1b) {
+ st->signal_lock = rbuf[1];
+ if ((st->stream_on & 1) &&
+ (st->signal_lock & 0x8)) {
+ lme2510_stream_restart(d);
+ st->i2c_talk_onoff = 0;
+ }
+ }
+ }
+ break;
+ case TUNER_RS2000:
+ default:
+ break;
+ }
+ } else {
+ /* TODO rewrite this section */
+ switch (st->tuner_config) {
+ case TUNER_LG:
+ switch (wbuf[3]) {
+ case 0x0e:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_lock;
+ break;
+ case 0x43:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_level;
+ break;
+ case 0x1c:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_sn;
+ break;
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0x00;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
+ break;
+ case TUNER_S7395:
+ switch (wbuf[3]) {
+ case 0x10:
+ rbuf[0] = 0x55;
+ rbuf[1] = (st->signal_level & 0x80)
+ ? 0 : (st->signal_level * 2);
+ break;
+ case 0x2d:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_sn;
+ break;
+ case 0x24:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_lock;
+ break;
+ case 0x2e:
+ case 0x26:
+ case 0x27:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0x00;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
+ break;
+ case TUNER_S0194:
+ switch (wbuf[3]) {
+ case 0x18:
+ rbuf[0] = 0x55;
+ rbuf[1] = (st->signal_level & 0x80)
+ ? 0 : (st->signal_level * 2);
+ break;
+ case 0x24:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_sn;
+ break;
+ case 0x1b:
+ rbuf[0] = 0x55;
+ rbuf[1] = st->signal_lock;
+ break;
+ case 0x19:
+ case 0x25:
+ case 0x1e:
+ case 0x1d:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0x00;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
+ break;
+ case TUNER_RS2000:
+ switch (wbuf[3]) {
+ case 0x8c:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0xff;
+ if (st->last_key == st->time_key) {
+ st->key_timeout++;
+ if (st->key_timeout > 5)
+ rbuf[1] = 0;
+ } else
+ st->key_timeout = 0;
+ st->last_key = st->time_key;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
+ default:
+ break;
+ }
+
+ deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)",
+ wbuf[3], rbuf[1]);
+
+ }
+
+ return ret;
+}
+
+
+static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct lme2510_state *st = d->priv;
+ static u8 obuf[64], ibuf[64];
+ int i, read, read_o;
+ u16 len;
+ u8 gate = st->i2c_gate;
+
+ mutex_lock(&d->i2c_mutex);
+
+ if (gate == 0)
+ gate = 5;
+
+ for (i = 0; i < num; i++) {
+ read_o = 1 & (msg[i].flags & I2C_M_RD);
+ read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ read |= read_o;
+ gate = (msg[i].addr == st->i2c_tuner_addr)
+ ? (read) ? st->i2c_tuner_gate_r
+ : st->i2c_tuner_gate_w
+ : st->i2c_gate;
+ obuf[0] = gate | (read << 7);
+
+ if (gate == 5)
+ obuf[1] = (read) ? 2 : msg[i].len + 1;
+ else
+ obuf[1] = msg[i].len + read + 1;
+
+ obuf[2] = msg[i].addr;
+ if (read) {
+ if (read_o)
+ len = 3;
+ else {
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
+ obuf[msg[i].len+3] = msg[i+1].len;
+ len = msg[i].len+4;
+ }
+ } else {
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
+ len = msg[i].len+3;
+ }
+
+ if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
+ deb_info(1, "i2c transfer failed.");
+ mutex_unlock(&d->i2c_mutex);
+ return -EAGAIN;
+ }
+
+ if (read) {
+ if (read_o)
+ memcpy(msg[i].buf, &ibuf[1], msg[i].len);
+ else {
+ memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len);
+ i++;
+ }
+ }
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 lme2510_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm lme2510_i2c_algo = {
+ .master_xfer = lme2510_i2c_xfer,
+ .functionality = lme2510_i2c_func,
+};
+
+static int lme2510_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = adap_to_priv(adap);
+ static u8 clear_reg_3[] = LME_ALL_PIDS;
+ static u8 rbuf[1];
+ int ret = 0, rlen = sizeof(rbuf);
+
+ deb_info(1, "STM (%02x)", onoff);
+
+ /* Streaming is started by FE_HAS_LOCK */
+ if (onoff == 1)
+ st->stream_on = 1;
+ else {
+ deb_info(1, "STM Steam Off");
+ /* mutex is here only to avoid collision with I2C */
+ mutex_lock(&d->i2c_mutex);
+
+ ret = lme2510_usb_talk(d, clear_reg_3,
+ sizeof(clear_reg_3), rbuf, rlen);
+ st->stream_on = 0;
+ st->i2c_talk_onoff = 1;
+
+ mutex_unlock(&d->i2c_mutex);
+ }
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static u8 check_sum(u8 *p, u8 len)
+{
+ u8 sum = 0;
+ while (len--)
+ sum += *p++;
+ return sum;
+}
+
+static int lme2510_download_firmware(struct dvb_usb_device *d,
+ const struct firmware *fw)
+{
+ int ret = 0;
+ u8 *data;
+ u16 j, wlen, len_in, start, end;
+ u8 packet_size, dlen, i;
+ u8 *fw_data;
+
+ packet_size = 0x31;
+ len_in = 1;
+
+ data = kzalloc(128, GFP_KERNEL);
+ if (!data) {
+ info("FRM Could not start Firmware Download"\
+ "(Buffer allocation failed)");
+ return -ENOMEM;
+ }
+
+ info("FRM Starting Firmware Download");
+
+ for (i = 1; i < 3; i++) {
+ start = (i == 1) ? 0 : 512;
+ end = (i == 1) ? 512 : fw->size;
+ for (j = start; j < end; j += (packet_size+1)) {
+ fw_data = (u8 *)(fw->data + j);
+ if ((end - j) > packet_size) {
+ data[0] = i;
+ dlen = packet_size;
+ } else {
+ data[0] = i | 0x80;
+ dlen = (u8)(end - j)-1;
+ }
+ data[1] = dlen;
+ memcpy(&data[2], fw_data, dlen+1);
+ wlen = (u8) dlen + 4;
+ data[wlen-1] = check_sum(fw_data, dlen+1);
+ deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3],
+ data[dlen+2], data[dlen+3]);
+ lme2510_usb_talk(d, data, wlen, data, len_in);
+ ret |= (data[0] == 0x88) ? 0 : -1;
+ }
+ }
+
+ data[0] = 0x8a;
+ len_in = 1;
+ msleep(2000);
+ lme2510_usb_talk(d, data, len_in, data, len_in);
+ msleep(400);
+
+ if (ret < 0)
+ info("FRM Firmware Download Failed (%04x)" , ret);
+ else
+ info("FRM Firmware Download Completed - Resetting Device");
+
+ kfree(data);
+ return RECONNECTS_USB;
+}
+
+static void lme_coldreset(struct dvb_usb_device *d)
+{
+ u8 data[1] = {0};
+ data[0] = 0x0a;
+ info("FRM Firmware Cold Reset");
+
+ lme2510_usb_talk(d, data, sizeof(data), data, sizeof(data));
+
+ return;
+}
+
+static const char fw_c_s7395[] = LME2510_C_S7395;
+static const char fw_c_lg[] = LME2510_C_LG;
+static const char fw_c_s0194[] = LME2510_C_S0194;
+static const char fw_c_rs2000[] = LME2510_C_RS2000;
+static const char fw_lg[] = LME2510_LG;
+static const char fw_s0194[] = LME2510_S0194;
+
+const char *lme_firmware_switch(struct dvb_usb_device *d, int cold)
+{
+ struct lme2510_state *st = d->priv;
+ struct usb_device *udev = d->udev;
+ const struct firmware *fw = NULL;
+ const char *fw_lme;
+ int ret = 0;
+
+ cold = (cold > 0) ? (cold & 1) : 0;
+
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case 0x1122:
+ switch (st->dvb_usb_lme2510_firmware) {
+ default:
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
+ case TUNER_S0194:
+ fw_lme = fw_s0194;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ if (ret == 0) {
+ cold = 0;
+ break;
+ }
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
+ case TUNER_LG:
+ fw_lme = fw_lg;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ if (ret == 0)
+ break;
+ st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
+ break;
+ }
+ break;
+ case 0x1120:
+ switch (st->dvb_usb_lme2510_firmware) {
+ default:
+ st->dvb_usb_lme2510_firmware = TUNER_S7395;
+ case TUNER_S7395:
+ fw_lme = fw_c_s7395;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ if (ret == 0) {
+ cold = 0;
+ break;
+ }
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
+ case TUNER_LG:
+ fw_lme = fw_c_lg;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ if (ret == 0)
+ break;
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
+ case TUNER_S0194:
+ fw_lme = fw_c_s0194;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ if (ret == 0)
+ break;
+ st->dvb_usb_lme2510_firmware = TUNER_DEFAULT;
+ cold = 0;
+ break;
+ }
+ break;
+ case 0x22f0:
+ fw_lme = fw_c_rs2000;
+ st->dvb_usb_lme2510_firmware = TUNER_RS2000;
+ break;
+ default:
+ fw_lme = fw_c_s7395;
+ }
+
+ release_firmware(fw);
+
+ if (cold) {
+ dvb_usb_lme2510_firmware = st->dvb_usb_lme2510_firmware;
+ info("FRM Changing to %s firmware", fw_lme);
+ lme_coldreset(d);
+ return NULL;
+ }
+
+ return fw_lme;
+}
+
+static int lme2510_kill_urb(struct usb_data_stream *stream)
+{
+ int i;
+
+ for (i = 0; i < stream->urbs_submitted; i++) {
+ deb_info(3, "killing URB no. %d.", i);
+ /* stop the URB */
+ usb_kill_urb(stream->urb_list[i]);
+ }
+ stream->urbs_submitted = 0;
+
+ return 0;
+}
+
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x1c,
+ .invert = 0,
+ .diseqc_tone = 1,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct stv0288_config lme_config = {
+ .demod_address = 0xd0,
+ .min_delay_ms = 15,
+ .inittab = s7395_inittab,
+};
+
+static struct ix2505v_config lme_tuner = {
+ .tuner_address = 0xc0,
+ .min_delay_ms = 100,
+ .tuner_gain = 0x0,
+ .tuner_chargepump = 0x3,
+};
+
+static struct stv0299_config sharp_z0194_config = {
+ .demod_address = 0xd0,
+ .inittab = sharp_z0194a_inittab,
+ .mclk = 88000000UL,
+ .invert = 0,
+ .skip_reinit = 0,
+ .lock_output = STV0299_LOCKOUTPUT_1,
+ .volt13_op0_op1 = STV0299_VOLT13_OP1,
+ .min_delay_ms = 100,
+ .set_symbol_rate = sharp_z0194a_set_symbol_rate,
+};
+
+static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
+ int caller)
+{
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = d->priv;
+
+ mutex_lock(&d->i2c_mutex);
+ if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
+ st->i2c_talk_onoff = 0;
+ lme2510_stream_restart(d);
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return 0;
+}
+
+static struct m88rs2000_config m88rs2000_config = {
+ .demod_addr = 0xd0,
+ .tuner_addr = 0xc0,
+ .set_ts_params = dm04_rs2000_set_ts_param,
+};
+
+static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct lme2510_state *st = fe_to_priv(fe);
+ static u8 voltage_low[] = LME_VOLTAGE_L;
+ static u8 voltage_high[] = LME_VOLTAGE_H;
+ static u8 rbuf[1];
+ int ret = 0, len = 3, rlen = 1;
+
+ mutex_lock(&d->i2c_mutex);
+
+ switch (voltage) {
+ case SEC_VOLTAGE_18:
+ ret |= lme2510_usb_talk(d,
+ voltage_high, len, rbuf, rlen);
+ break;
+
+ case SEC_VOLTAGE_OFF:
+ case SEC_VOLTAGE_13:
+ default:
+ ret |= lme2510_usb_talk(d,
+ voltage_low, len, rbuf, rlen);
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (st->tuner_config == TUNER_RS2000)
+ if (st->fe_set_voltage)
+ st->fe_set_voltage(fe, voltage);
+
+
+ return (ret < 0) ? -ENODEV : 0;
+}
+
+static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct lme2510_state *st = fe_to_priv(fe);
+
+ *strength = (u16)((u32)st->signal_level * 0xffff / 0xff);
+
+ return 0;
+}
+
+static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lme2510_state *st = fe_to_priv(fe);
+
+ *snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f);
+
+ return 0;
+}
+
+static int dm04_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+
+ return 0;
+}
+
+static int dm04_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+
+ return 0;
+}
+
+static int lme_name(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = adap_to_priv(adap);
+ const char *desc = d->name;
+ char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
+ " SHARP:BS2F7HZ0194", " RS2000"};
+ char *name = adap->fe[0]->ops.info.name;
+
+ strlcpy(name, desc, 128);
+ strlcat(name, fe_name[st->tuner_config], 128);
+
+ return 0;
+}
+
+static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = d->priv;
+ int ret = 0;
+
+ st->i2c_talk_onoff = 1;
+ switch (le16_to_cpu(d->udev->descriptor.idProduct)) {
+ case 0x1122:
+ case 0x1120:
+ st->i2c_gate = 4;
+ adap->fe[0] = dvb_attach(tda10086_attach,
+ &tda10086_config, &d->i2c_adap);
+ if (adap->fe[0]) {
+ info("TUN Found Frontend TDA10086");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 4;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_LG;
+ if (st->dvb_usb_lme2510_firmware != TUNER_LG) {
+ st->dvb_usb_lme2510_firmware = TUNER_LG;
+ ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV;
+ }
+ break;
+ }
+
+ st->i2c_gate = 4;
+ adap->fe[0] = dvb_attach(stv0299_attach,
+ &sharp_z0194_config, &d->i2c_adap);
+ if (adap->fe[0]) {
+ info("FE Found Stv0299");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S0194;
+ if (st->dvb_usb_lme2510_firmware != TUNER_S0194) {
+ st->dvb_usb_lme2510_firmware = TUNER_S0194;
+ ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV;
+ }
+ break;
+ }
+
+ st->i2c_gate = 5;
+ adap->fe[0] = dvb_attach(stv0288_attach, &lme_config,
+ &d->i2c_adap);
+
+ if (adap->fe[0]) {
+ info("FE Found Stv0288");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S7395;
+ if (st->dvb_usb_lme2510_firmware != TUNER_S7395) {
+ st->dvb_usb_lme2510_firmware = TUNER_S7395;
+ ret = lme_firmware_switch(d, 1) ? 0 : -ENODEV;
+ }
+ break;
+ }
+ case 0x22f0:
+ st->i2c_gate = 5;
+ adap->fe[0] = dvb_attach(m88rs2000_attach,
+ &m88rs2000_config, &d->i2c_adap);
+
+ if (adap->fe[0]) {
+ info("FE Found M88RS2000");
+ st->i2c_tuner_gate_w = 5;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_RS2000;
+ st->fe_set_voltage =
+ adap->fe[0]->ops.set_voltage;
+
+ adap->fe[0]->ops.read_signal_strength =
+ dm04_rs2000_read_signal_strength;
+ adap->fe[0]->ops.read_snr =
+ dm04_rs2000_read_snr;
+ adap->fe[0]->ops.read_ber =
+ dm04_read_ber;
+ adap->fe[0]->ops.read_ucblocks =
+ dm04_read_ucblocks;
+ }
+ break;
+ }
+
+ if (adap->fe[0] == NULL) {
+ info("DM04/QQBOX Not Powered up or not Supported");
+ return -ENODEV;
+ }
+
+ if (ret) {
+ if (adap->fe[0]) {
+ dvb_frontend_detach(adap->fe[0]);
+ adap->fe[0] = NULL;
+ }
+ d->rc_map = NULL;
+ return -ENODEV;
+ }
+
+ adap->fe[0]->ops.set_voltage = dm04_lme2510_set_voltage;
+ ret = lme_name(adap);
+ return ret;
+}
+
+static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct lme2510_state *st = adap_to_priv(adap);
+ char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"};
+ int ret = 0;
+
+ switch (st->tuner_config) {
+ case TUNER_LG:
+ if (dvb_attach(tda826x_attach, adap->fe[0], 0xc0,
+ &d->i2c_adap, 1))
+ ret = st->tuner_config;
+ break;
+ case TUNER_S7395:
+ if (dvb_attach(ix2505v_attach , adap->fe[0], &lme_tuner,
+ &d->i2c_adap))
+ ret = st->tuner_config;
+ break;
+ case TUNER_S0194:
+ if (dvb_attach(dvb_pll_attach , adap->fe[0], 0xc0,
+ &d->i2c_adap, DVB_PLL_OPERA1))
+ ret = st->tuner_config;
+ break;
+ case TUNER_RS2000:
+ ret = st->tuner_config;
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ info("TUN Found %s tuner", tun_msg[ret]);
+ else {
+ info("TUN No tuner found --- resetting device");
+ lme_coldreset(d);
+ return -ENODEV;
+ }
+
+ /* Start the Interrupt*/
+ ret = lme2510_int_read(adap);
+ if (ret < 0) {
+ info("INT Unable to start Interrupt Service");
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+static int lme2510_powerup(struct dvb_usb_device *d, int onoff)
+{
+ struct lme2510_state *st = d->priv;
+ static u8 lnb_on[] = LNB_ON;
+ static u8 lnb_off[] = LNB_OFF;
+ static u8 rbuf[1];
+ int ret = 0, len = 3, rlen = 1;
+
+ mutex_lock(&d->i2c_mutex);
+
+ if (onoff)
+ ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen);
+ else
+ ret = lme2510_usb_talk(d, lnb_off, len, rbuf, rlen);
+
+ st->i2c_talk_onoff = 1;
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret;
+}
+
+static int lme2510_get_adapter_count(struct dvb_usb_device *d)
+{
+ return 1;
+}
+
+static int lme2510_identify_state(struct dvb_usb_device *d, const char **name)
+{
+ struct lme2510_state *st = d->priv;
+
+ usb_reset_configuration(d->udev);
+
+ usb_set_interface(d->udev,
+ d->intf->cur_altsetting->desc.bInterfaceNumber, 1);
+
+ st->dvb_usb_lme2510_firmware = dvb_usb_lme2510_firmware;
+
+ if (lme2510_return_status(d) == 0x44) {
+ *name = lme_firmware_switch(d, 0);
+ return COLD;
+ }
+
+ return 0;
+}
+
+static int lme2510_get_stream_config(struct dvb_frontend *fe, u8 *ts_type,
+ struct usb_data_stream_properties *stream)
+{
+ struct dvb_usb_adapter *adap = fe_to_adap(fe);
+ struct dvb_usb_device *d = adap_to_d(adap);
+
+ if (adap == NULL)
+ return 0;
+ /* Turn PID filter on the fly by module option */
+ if (pid_filter == 2) {
+ adap->pid_filtering = 1;
+ adap->max_feed_count = 15;
+ }
+
+ if (!(le16_to_cpu(d->udev->descriptor.idProduct)
+ == 0x1122))
+ stream->endpoint = 0x8;
+
+ return 0;
+}
+
+static int lme2510_get_rc_config(struct dvb_usb_device *d,
+ struct dvb_usb_rc *rc)
+{
+ rc->allowed_protos = RC_TYPE_NEC;
+ return 0;
+}
+
+static void *lme2510_exit_int(struct dvb_usb_device *d)
+{
+ struct lme2510_state *st = d->priv;
+ struct dvb_usb_adapter *adap = &d->adapter[0];
+ void *buffer = NULL;
+
+ if (adap != NULL) {
+ lme2510_kill_urb(&adap->stream);
+ }
+
+ if (st->usb_buffer != NULL) {
+ st->i2c_talk_onoff = 1;
+ st->signal_lock = 0;
+ st->signal_level = 0;
+ st->signal_sn = 0;
+ buffer = st->usb_buffer;
+ }
+
+ if (st->lme_urb != NULL) {
+ usb_kill_urb(st->lme_urb);
+ usb_free_coherent(d->udev, 128, st->buffer,
+ st->lme_urb->transfer_dma);
+ info("Interrupt Service Stopped");
+ }
+
+ return buffer;
+}
+
+static void lme2510_exit(struct dvb_usb_device *d)
+{
+ void *usb_buffer;
+
+ if (d != NULL) {
+ usb_buffer = lme2510_exit_int(d);
+ if (usb_buffer != NULL)
+ kfree(usb_buffer);
+ }
+}
+
+static struct dvb_usb_device_properties lme2510_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .bInterfaceNumber = 0,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct lme2510_state),
+
+ .download_firmware = lme2510_download_firmware,
+
+ .power_ctrl = lme2510_powerup,
+ .identify_state = lme2510_identify_state,
+ .i2c_algo = &lme2510_i2c_algo,
+
+ .frontend_attach = dm04_lme2510_frontend_attach,
+ .tuner_attach = dm04_lme2510_tuner,
+ .get_stream_config = lme2510_get_stream_config,
+ .get_adapter_count = lme2510_get_adapter_count,
+ .streaming_ctrl = lme2510_streaming_ctrl,
+
+ .get_rc_config = lme2510_get_rc_config,
+
+ .exit = lme2510_exit,
+ .adapter = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER|
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+ .pid_filter_count = 15,
+ .pid_filter = lme2510_pid_filter,
+ .pid_filter_ctrl = lme2510_pid_filter_ctrl,
+ .stream =
+ DVB_USB_STREAM_BULK(0x86, 10, 4096),
+ },
+ {
+ }
+ },
+};
+
+static const struct usb_device_id lme2510_id_table[] = {
+ { DVB_USB_DEVICE(0x3344, 0x1122, &lme2510_props,
+ "DM04_LME2510_DVB-S", RC_MAP_LME2510) },
+ { DVB_USB_DEVICE(0x3344, 0x1120, &lme2510_props,
+ "DM04_LME2510C_DVB-S", RC_MAP_LME2510) },
+ { DVB_USB_DEVICE(0x3344, 0x22f0, &lme2510_props,
+ "DM04_LME2510C_DVB-S RS2000", RC_MAP_LME2510) },
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, lme2510_id_table);
+
+static struct usb_driver lme2510_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .id_table = lme2510_id_table,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(lme2510_driver);
+
+MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
+MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
+MODULE_VERSION("2.06");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(LME2510_C_S7395);
+MODULE_FIRMWARE(LME2510_C_LG);
+MODULE_FIRMWARE(LME2510_C_S0194);
+MODULE_FIRMWARE(LME2510_C_RS2000);
+MODULE_FIRMWARE(LME2510_LG);
+MODULE_FIRMWARE(LME2510_S0194);
+
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/usb/dvb-usb-v2/lmedm04.h
index e9c207205c2f..e9c207205c2f 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.h
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.h
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
index d83df4bb72d3..d83df4bb72d3 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
index 432706ae5274..432706ae5274 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c
index e4121cb8f5ef..e4121cb8f5ef 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h
index 0220f54299a5..0220f54299a5 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
index 34434557ef65..34434557ef65 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h
index a57a45ffb9e4..a57a45ffb9e4 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c
index b741b3a7a325..b741b3a7a325 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h
index f0756071d347..f0756071d347 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h
index 17831b0fb9db..17831b0fb9db 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
new file mode 100644
index 000000000000..ef4c65fcbb73
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c
@@ -0,0 +1,527 @@
+/*
+ * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "mxl111sf-tuner.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-reg.h"
+
+/* debug */
+static int mxl111sf_tuner_debug;
+module_param_named(debug, mxl111sf_tuner_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define mxl_dbg(fmt, arg...) \
+ if (mxl111sf_tuner_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define err pr_err
+
+/* ------------------------------------------------------------------------ */
+
+struct mxl111sf_tuner_state {
+ struct mxl111sf_state *mxl_state;
+
+ struct mxl111sf_tuner_config *cfg;
+
+ enum mxl_if_freq if_freq;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 *data)
+{
+ return (state->cfg->read_reg) ?
+ state->cfg->read_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state,
+ u8 addr, u8 data)
+{
+ return (state->cfg->write_reg) ?
+ state->cfg->write_reg(state->mxl_state, addr, data) :
+ -EINVAL;
+}
+
+static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ return (state->cfg->program_regs) ?
+ state->cfg->program_regs(state->mxl_state, ctrl_reg_info) :
+ -EINVAL;
+}
+
+static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return (state->cfg->top_master_ctrl) ?
+ state->cfg->top_master_ctrl(state->mxl_state, onoff) :
+ -EINVAL;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = {
+ {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3,
+ DIG_MODEINDEX, _A, _CSF, */
+ {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */
+ {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */
+ {0, 0, 0}
+};
+
+/* ------------------------------------------------------------------------ */
+
+static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq,
+ u8 bw)
+{
+ u8 filt_bw;
+
+ /* set channel bandwidth */
+ switch (bw) {
+ case 0: /* ATSC */
+ filt_bw = 25;
+ break;
+ case 1: /* QAM */
+ filt_bw = 69;
+ break;
+ case 6:
+ filt_bw = 21;
+ break;
+ case 7:
+ filt_bw = 42;
+ break;
+ case 8:
+ filt_bw = 63;
+ break;
+ default:
+ err("%s: invalid bandwidth setting!", __func__);
+ return NULL;
+ }
+
+ /* calculate RF channel */
+ freq /= 1000000;
+
+ freq *= 64;
+#if 0
+ /* do round */
+ freq += 0.5;
+#endif
+ /* set bandwidth */
+ mxl_phy_tune_rf[0].data = filt_bw;
+
+ /* set RF */
+ mxl_phy_tune_rf[1].data = (freq & 0xff);
+ mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff;
+
+ /* start tune */
+ return mxl_phy_tune_rf;
+}
+
+static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state)
+{
+ int ret;
+ u8 ctrl;
+#if 0
+ u16 iffcw;
+ u32 if_freq;
+#endif
+ mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)",
+ state->cfg->invert_spectrum, state->cfg->if_freq);
+
+ /* set IF polarity */
+ ctrl = state->cfg->invert_spectrum;
+
+ ctrl |= state->cfg->if_freq;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ if_freq /= 1000000;
+
+ /* do round */
+ if_freq += 0.5;
+
+ if (MXL_IF_LO == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (108 * 4096));
+ } else if (MXL_IF_HI == state->cfg->if_freq) {
+ ctrl = 0x08;
+ iffcw = (u16)(if_freq / (216 * 4096));
+ } else {
+ ctrl = 0;
+ iffcw = 0;
+ }
+
+ ctrl |= (iffcw >> 8);
+#endif
+ ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ctrl &= 0xf0;
+ ctrl |= 0x90;
+
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+#if 0
+ ctrl = iffcw & 0x00ff;
+#endif
+ ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl);
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->if_freq = state->cfg->if_freq;
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ static struct mxl111sf_reg_ctrl_info *reg_ctrl_array;
+ int ret;
+ u8 mxl_mode;
+
+ mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw);
+
+ /* stop tune */
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* check device mode */
+ ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* Fill out registers for channel tune */
+ reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw);
+ if (!reg_ctrl_array)
+ return -EINVAL;
+
+ ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) {
+ /* IF tuner mode only */
+ mxl1x1sf_tuner_top_master_ctrl(state, 0);
+ mxl1x1sf_tuner_top_master_ctrl(state, 1);
+ mxl1x1sf_tuner_set_if_output_freq(state);
+ }
+
+ ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ if (state->cfg->ant_hunt)
+ state->cfg->ant_hunt(fe);
+fail:
+ return ret;
+}
+
+static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state,
+ int *rf_synth_lock,
+ int *ref_synth_lock)
+{
+ int ret;
+ u8 data;
+
+ *rf_synth_lock = 0;
+ *ref_synth_lock = 0;
+
+ ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0;
+ *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0;
+fail:
+ return ret;
+}
+
+#if 0
+static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state,
+ int onoff)
+{
+ return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG,
+ onoff ? 1 : 0);
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+ u8 bw;
+
+ mxl_dbg("()");
+
+ switch (delsys) {
+ case SYS_ATSC:
+ case SYS_ATSCMH:
+ bw = 0; /* ATSC */
+ break;
+ case SYS_DVBC_ANNEX_B:
+ bw = 1; /* US CABLE */
+ break;
+ case SYS_DVBT:
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bw = 6;
+ break;
+ case 7000000:
+ bw = 7;
+ break;
+ case 8000000:
+ bw = 8;
+ break;
+ default:
+ err("%s: bandwidth not set!", __func__);
+ return -EINVAL;
+ }
+ break;
+ default:
+ err("%s: modulation type not supported!", __func__);
+ return -EINVAL;
+ }
+ ret = mxl1x1sf_tune_rf(fe, c->frequency, bw);
+ if (mxl_fail(ret))
+ goto fail;
+
+ state->frequency = c->frequency;
+ state->bandwidth = c->bandwidth_hz;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+static int mxl111sf_tuner_init(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* wake from standby handled by usb driver */
+
+ return ret;
+}
+
+static int mxl111sf_tuner_sleep(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int ret;
+
+ /* enter standby mode handled by usb driver */
+
+ return ret;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ int rf_locked, ref_locked, ret;
+
+ *status = 0;
+
+ ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked);
+ if (mxl_fail(ret))
+ goto fail;
+ mxl_info("%s%s", rf_locked ? "rf locked " : "",
+ ref_locked ? "ref locked" : "");
+
+ if ((rf_locked) || (ref_locked))
+ *status |= TUNER_STATUS_LOCKED;
+fail:
+ return ret;
+}
+
+static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ u8 val1, val2;
+ int ret;
+
+ *strength = 0;
+
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2);
+ if (mxl_fail(ret))
+ goto fail;
+
+ *strength = val1 | ((val2 & 0x07) << 8);
+fail:
+ ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *frequency = state->frequency;
+ return 0;
+}
+
+static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ *bandwidth = state->bandwidth;
+ return 0;
+}
+
+static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe,
+ u32 *frequency)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+
+ *frequency = 0;
+
+ switch (state->if_freq) {
+ case MXL_IF_4_0: /* 4.0 MHz */
+ *frequency = 4000000;
+ break;
+ case MXL_IF_4_5: /* 4.5 MHz */
+ *frequency = 4500000;
+ break;
+ case MXL_IF_4_57: /* 4.57 MHz */
+ *frequency = 4570000;
+ break;
+ case MXL_IF_5_0: /* 5.0 MHz */
+ *frequency = 5000000;
+ break;
+ case MXL_IF_5_38: /* 5.38 MHz */
+ *frequency = 5380000;
+ break;
+ case MXL_IF_6_0: /* 6.0 MHz */
+ *frequency = 6000000;
+ break;
+ case MXL_IF_6_28: /* 6.28 MHz */
+ *frequency = 6280000;
+ break;
+ case MXL_IF_7_2: /* 7.2 MHz */
+ *frequency = 7200000;
+ break;
+ case MXL_IF_35_25: /* 35.25 MHz */
+ *frequency = 35250000;
+ break;
+ case MXL_IF_36: /* 36 MHz */
+ *frequency = 36000000;
+ break;
+ case MXL_IF_36_15: /* 36.15 MHz */
+ *frequency = 36150000;
+ break;
+ case MXL_IF_44: /* 44 MHz */
+ *frequency = 44000000;
+ break;
+ }
+ return 0;
+}
+
+static int mxl111sf_tuner_release(struct dvb_frontend *fe)
+{
+ struct mxl111sf_tuner_state *state = fe->tuner_priv;
+ mxl_dbg("()");
+ kfree(state);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = {
+ .info = {
+ .name = "MaxLinear MxL111SF",
+#if 0
+ .frequency_min = ,
+ .frequency_max = ,
+ .frequency_step = ,
+#endif
+ },
+#if 0
+ .init = mxl111sf_tuner_init,
+ .sleep = mxl111sf_tuner_sleep,
+#endif
+ .set_params = mxl111sf_tuner_set_params,
+ .get_status = mxl111sf_tuner_get_status,
+ .get_rf_strength = mxl111sf_get_rf_strength,
+ .get_frequency = mxl111sf_tuner_get_frequency,
+ .get_bandwidth = mxl111sf_tuner_get_bandwidth,
+ .get_if_frequency = mxl111sf_tuner_get_if_frequency,
+ .release = mxl111sf_tuner_release,
+};
+
+struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe,
+ struct mxl111sf_state *mxl_state,
+ struct mxl111sf_tuner_config *cfg)
+{
+ struct mxl111sf_tuner_state *state = NULL;
+
+ mxl_dbg("()");
+
+ state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL);
+ if (state == NULL)
+ return NULL;
+
+ state->mxl_state = mxl_state;
+ state->cfg = cfg;
+
+ memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = state;
+ return fe;
+}
+EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach);
+
+MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.1");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
index ff333960b184..ff333960b184 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
new file mode 100644
index 000000000000..efdcb15358f1
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c
@@ -0,0 +1,1431 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/i2c.h>
+
+#include "mxl111sf.h"
+#include "mxl111sf-reg.h"
+#include "mxl111sf-phy.h"
+#include "mxl111sf-i2c.h"
+#include "mxl111sf-gpio.h"
+
+#include "mxl111sf-demod.h"
+#include "mxl111sf-tuner.h"
+
+#include "lgdt3305.h"
+#include "lg2160.h"
+
+int dvb_usb_mxl111sf_debug;
+module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level "
+ "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able)).");
+
+int dvb_usb_mxl111sf_isoc;
+module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
+MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
+
+int dvb_usb_mxl111sf_spi;
+module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644);
+MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi).");
+
+#define ANT_PATH_AUTO 0
+#define ANT_PATH_EXTERNAL 1
+#define ANT_PATH_INTERNAL 2
+
+int dvb_usb_mxl111sf_rfswitch =
+#if 0
+ ANT_PATH_AUTO;
+#else
+ ANT_PATH_EXTERNAL;
+#endif
+
+module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644);
+MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define deb_info pr_debug
+#define deb_reg pr_debug
+#define deb_adv pr_debug
+#define err pr_err
+#define info pr_info
+
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ int wo = (rbuf == NULL || rlen == 0); /* write-only */
+ int ret;
+ u8 sndbuf[1+wlen];
+
+ deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen);
+
+ memset(sndbuf, 0, 1+wlen);
+
+ sndbuf[0] = cmd;
+ memcpy(&sndbuf[1], wbuf, wlen);
+
+ ret = (wo) ? dvb_usbv2_generic_write(d, sndbuf, 1+wlen) :
+ dvb_usbv2_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#define MXL_CMD_REG_READ 0xaa
+#define MXL_CMD_REG_WRITE 0x55
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data)
+{
+ u8 buf[2];
+ int ret;
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2);
+ if (mxl_fail(ret)) {
+ mxl_debug("error reading reg: 0x%02x", addr);
+ goto fail;
+ }
+
+ if (buf[0] == addr)
+ *data = buf[1];
+ else {
+ err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x",
+ addr, buf[0], buf[1]);
+ ret = -EINVAL;
+ }
+
+ deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data);
+fail:
+ return ret;
+}
+
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data)
+{
+ u8 buf[] = { addr, data };
+ int ret;
+
+ deb_reg("W: (0x%02x, 0x%02x)\n", addr, data);
+
+ ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0);
+ if (mxl_fail(ret))
+ err("error writing reg: 0x%02x, val: 0x%02x", addr, data);
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data)
+{
+ int ret;
+ u8 val;
+
+ if (mask != 0xff) {
+ ret = mxl111sf_read_reg(state, addr, &val);
+#if 1
+ /* dont know why this usually errors out on the first try */
+ if (mxl_fail(ret))
+ err("error writing addr: 0x%02x, mask: 0x%02x, "
+ "data: 0x%02x, retrying...", addr, mask, data);
+
+ ret = mxl111sf_read_reg(state, addr, &val);
+#endif
+ if (mxl_fail(ret))
+ goto fail;
+ }
+ val &= ~mask;
+ val |= data;
+
+ ret = mxl111sf_write_reg(state, addr, val);
+ mxl_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info)
+{
+ int i, ret = 0;
+
+ for (i = 0; ctrl_reg_info[i].addr |
+ ctrl_reg_info[i].mask |
+ ctrl_reg_info[i].data; i++) {
+
+ ret = mxl111sf_write_reg_mask(state,
+ ctrl_reg_info[i].addr,
+ ctrl_reg_info[i].mask,
+ ctrl_reg_info[i].data);
+ if (mxl_fail(ret)) {
+ err("failed on reg #%d (0x%02x)", i,
+ ctrl_reg_info[i].addr);
+ break;
+ }
+ }
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state)
+{
+ int ret;
+ u8 id, ver;
+ char *mxl_chip, *mxl_rev;
+
+ if ((state->chip_id) && (state->chip_ver))
+ return 0;
+
+ ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_id = id;
+
+ ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver);
+ if (mxl_fail(ret))
+ goto fail;
+ state->chip_ver = ver;
+
+ switch (id) {
+ case 0x61:
+ mxl_chip = "MxL101SF";
+ break;
+ case 0x63:
+ mxl_chip = "MxL111SF";
+ break;
+ default:
+ mxl_chip = "UNKNOWN MxL1X1";
+ break;
+ }
+ switch (ver) {
+ case 0x36:
+ state->chip_rev = MXL111SF_V6;
+ mxl_rev = "v6";
+ break;
+ case 0x08:
+ state->chip_rev = MXL111SF_V8_100;
+ mxl_rev = "v8_100";
+ break;
+ case 0x18:
+ state->chip_rev = MXL111SF_V8_200;
+ mxl_rev = "v8_200";
+ break;
+ default:
+ state->chip_rev = 0;
+ mxl_rev = "UNKNOWN REVISION";
+ break;
+ }
+ info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver);
+fail:
+ return ret;
+}
+
+#define get_chip_info(state) \
+({ \
+ int ___ret; \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) { \
+ mxl_debug("failed to get chip info" \
+ " on first probe attempt"); \
+ ___ret = mxl1x1sf_get_chip_info(state); \
+ if (mxl_fail(___ret)) \
+ err("failed to get chip info during probe"); \
+ else \
+ mxl_debug("probe needed a retry " \
+ "in order to succeed."); \
+ } \
+ ___ret; \
+})
+
+/* ------------------------------------------------------------------------ */
+#if 0
+static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ /* power control depends on which adapter is being woken:
+ * save this for init, instead, via mxl111sf_adap_fe_init */
+ return 0;
+}
+#endif
+
+static int mxl111sf_adap_fe_init(struct dvb_frontend *fe)
+{
+ struct dvb_usb_device *d = fe_to_d(fe);
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id];
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ mutex_lock(&state->fe_lock);
+
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ err = mxl1x1sf_soft_reset(state);
+ mxl_fail(err);
+ err = mxl111sf_init_tuner_demod(state);
+ mxl_fail(err);
+ err = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+
+ mxl_fail(err);
+ mxl111sf_enable_usb_output(state);
+ mxl_fail(err);
+ mxl1x1sf_top_master_ctrl(state, 1);
+ mxl_fail(err);
+
+ if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) &&
+ (state->chip_rev > MXL111SF_V6)) {
+ mxl111sf_config_pin_mux_modes(state,
+ PIN_MUX_TS_SPI_IN_MODE_1);
+ mxl_fail(err);
+ }
+ err = mxl111sf_init_port_expander(state);
+ if (!mxl_fail(err)) {
+ state->gpio_mode = adap_state->gpio_mode;
+ err = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ mxl_fail(err);
+#if 0
+ err = fe->ops.init(fe);
+#endif
+ msleep(100); /* add short delay after enabling
+ * the demod before touching it */
+ }
+
+ return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0;
+fail:
+ return -ENODEV;
+}
+
+static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe)
+{
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id];
+ int err;
+
+ /* exit if we didnt initialize the driver yet */
+ if (!state->chip_id) {
+ mxl_debug("driver not yet initialized, exit.");
+ goto fail;
+ }
+
+ deb_info("%s()\n", __func__);
+
+ err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0;
+
+ mutex_unlock(&state->fe_lock);
+
+ return err;
+fail:
+ return -ENODEV;
+}
+
+
+static int mxl111sf_ep6_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe->id];
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ ret = mxl111sf_config_mpeg_in(state, 1, 1,
+ adap_state->ep6_clockphase,
+ 0, 0);
+ mxl_fail(ret);
+#if 0
+ } else {
+ ret = mxl111sf_disable_656_port(state);
+ mxl_fail(ret);
+#endif
+ }
+
+ return ret;
+}
+
+static int mxl111sf_ep5_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+
+ ret = mxl111sf_init_i2s_port(state, 200);
+ mxl_fail(ret);
+ ret = mxl111sf_config_i2s(state, 0, 15);
+ mxl_fail(ret);
+ } else {
+ ret = mxl111sf_disable_i2s_port(state);
+ mxl_fail(ret);
+ }
+ if (state->chip_rev > MXL111SF_V6)
+ ret = mxl111sf_config_spi(state, onoff);
+ mxl_fail(ret);
+
+ return ret;
+}
+
+static int mxl111sf_ep4_streaming_ctrl(struct dvb_frontend *fe, int onoff)
+{
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+ }
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static struct lgdt3305_config hauppauge_lgdt3305_config = {
+ .i2c_addr = 0xb2 >> 1,
+ .mpeg_mode = LGDT3305_MPEG_SERIAL,
+ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .qam_if_khz = 6000,
+ .vsb_if_khz = 6000,
+};
+
+static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct mxl111sf_state *state = d_to_priv(d);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_ATSC;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe[fe_id] = dvb_attach(lgdt3305_attach,
+ &hauppauge_lgdt3305_config,
+ &d->i2c_adap);
+ if (adap->fe[fe_id]) {
+ state->num_frontends++;
+ adap_state->fe_init = adap->fe[fe_id]->ops.init;
+ adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep;
+ adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct lg2160_config hauppauge_lg2160_config = {
+ .lg_chip = LG2160,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+};
+
+static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct mxl111sf_state *state = d_to_priv(d);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe[fe_id] = dvb_attach(lg2160_attach,
+ &hauppauge_lg2160_config,
+ &d->i2c_adap);
+ if (adap->fe[fe_id]) {
+ state->num_frontends++;
+ adap_state->fe_init = adap->fe[fe_id]->ops.init;
+ adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep;
+ adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct lg2160_config hauppauge_lg2161_1019_config = {
+ .lg_chip = LG2161_1019,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 2, /* LG2161_OIF_SPI_MAS */
+};
+
+static struct lg2160_config hauppauge_lg2161_1040_config = {
+ .lg_chip = LG2161_1040,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 4, /* LG2161_OIF_SPI_MAS */
+};
+
+static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct mxl111sf_state *state = d_to_priv(d);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe[fe_id] = dvb_attach(lg2160_attach,
+ (MXL111SF_V8_200 == state->chip_rev) ?
+ &hauppauge_lg2161_1040_config :
+ &hauppauge_lg2161_1019_config,
+ &d->i2c_adap);
+ if (adap->fe[fe_id]) {
+ state->num_frontends++;
+ adap_state->fe_init = adap->fe[fe_id]->ops.init;
+ adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep;
+ adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct lg2160_config hauppauge_lg2161_1019_ep6_config = {
+ .lg_chip = LG2161_1019,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 1, /* LG2161_OIF_SERIAL_TS */
+};
+
+static struct lg2160_config hauppauge_lg2161_1040_ep6_config = {
+ .lg_chip = LG2161_1040,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 7, /* LG2161_OIF_SERIAL_TS */
+};
+
+static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap, u8 fe_id)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct mxl111sf_state *state = d_to_priv(d);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 0;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe[fe_id] = dvb_attach(lg2160_attach,
+ (MXL111SF_V8_200 == state->chip_rev) ?
+ &hauppauge_lg2161_1040_ep6_config :
+ &hauppauge_lg2161_1019_ep6_config,
+ &d->i2c_adap);
+ if (adap->fe[fe_id]) {
+ state->num_frontends++;
+ adap_state->fe_init = adap->fe[fe_id]->ops.init;
+ adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep;
+ adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct mxl111sf_demod_config mxl_demod_config = {
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+};
+
+static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap, u8 fe_id)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct mxl111sf_state *state = d_to_priv(d);
+ struct mxl111sf_adap_state *adap_state = &state->adap_state[fe_id];
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(d->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_DVBT;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_SOC_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ /* dont care if this fails */
+ mxl111sf_init_port_expander(state);
+
+ adap->fe[fe_id] = dvb_attach(mxl111sf_demod_attach, state,
+ &mxl_demod_config);
+ if (adap->fe[fe_id]) {
+ state->num_frontends++;
+ adap_state->fe_init = adap->fe[fe_id]->ops.init;
+ adap->fe[fe_id]->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe[fe_id]->ops.sleep;
+ adap->fe[fe_id]->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state,
+ int antpath)
+{
+ return mxl111sf_idac_config(state, 1, 1,
+ (antpath == ANT_PATH_INTERNAL) ?
+ 0x3f : 0x00, 0);
+}
+
+#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \
+ err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \
+ __func__, __LINE__, \
+ (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \
+ pwr0, pwr1, pwr2, pwr3)
+
+#define ANT_HUNT_SLEEP 90
+#define ANT_EXT_TWEAK 0
+
+static int mxl111sf_ant_hunt(struct dvb_frontend *fe)
+{
+ struct mxl111sf_state *state = fe_to_priv(fe);
+ int antctrl = dvb_usb_mxl111sf_rfswitch;
+
+ u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2;
+
+ /* FIXME: must force EXTERNAL for QAM - done elsewhere */
+ mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ?
+ ANT_PATH_EXTERNAL : antctrl);
+
+ if (antctrl == ANT_PATH_AUTO) {
+#if 0
+ msleep(ANT_HUNT_SLEEP);
+#endif
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1);
+
+ mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL);
+ msleep(ANT_HUNT_SLEEP);
+ fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2);
+
+ if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) {
+ /* return with EXTERNAL enabled */
+ mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL);
+ DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ } else {
+ /* return with INTERNAL enabled */
+ DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA,
+ rxPwr0, rxPwr1, rxPwr2);
+ }
+ }
+ return 0;
+}
+
+static struct mxl111sf_tuner_config mxl_tuner_config = {
+ .if_freq = MXL_IF_6_0, /* applies to external IF output, only */
+ .invert_spectrum = 0,
+ .read_reg = mxl111sf_read_reg,
+ .write_reg = mxl111sf_write_reg,
+ .program_regs = mxl111sf_ctrl_program_regs,
+ .top_master_ctrl = mxl1x1sf_top_master_ctrl,
+ .ant_hunt = mxl111sf_ant_hunt,
+};
+
+static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap)
+{
+ struct mxl111sf_state *state = adap_to_priv(adap);
+ int i;
+
+ deb_adv("%s()\n", __func__);
+
+ for (i = 0; i < state->num_frontends; i++) {
+ if (dvb_attach(mxl111sf_tuner_attach, adap->fe[i], state,
+ &mxl_tuner_config) == NULL)
+ return -EIO;
+ adap->fe[i]->ops.read_signal_strength = adap->fe[i]->ops.tuner_ops.get_rf_strength;
+ }
+
+ return 0;
+}
+
+static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+struct i2c_algorithm mxl111sf_i2c_algo = {
+ .master_xfer = mxl111sf_i2c_xfer,
+ .functionality = mxl111sf_i2c_func,
+#ifdef NEED_ALGO_CONTROL
+ .algo_control = dummy_algo_control,
+#endif
+};
+
+static int mxl111sf_init(struct dvb_usb_device *d)
+{
+ struct mxl111sf_state *state = d_to_priv(d);
+ int ret;
+ static u8 eeprom[256];
+ struct i2c_client c;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ err("failed to get chip info during probe");
+
+ mutex_init(&state->fe_lock);
+
+ if (state->chip_rev > MXL111SF_V6)
+ mxl111sf_config_pin_mux_modes(state, PIN_MUX_TS_SPI_IN_MODE_1);
+
+ c.adapter = &d->i2c_adap;
+ c.addr = 0xa0 >> 1;
+
+ ret = tveeprom_read(&c, eeprom, sizeof(eeprom));
+ if (mxl_fail(ret))
+ return 0;
+ tveeprom_hauppauge_analog(&c, &state->tv, (0x84 == eeprom[0xa0]) ?
+ eeprom + 0xa0 : eeprom + 0x80);
+#if 0
+ switch (state->tv.model) {
+ case 117001:
+ case 126001:
+ case 138001:
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+ "unknown hauppauge model #%d\n",
+ __func__, state->tv.model);
+ }
+#endif
+ return 0;
+}
+
+static int mxl111sf_frontend_attach_dvbt(struct dvb_usb_adapter *adap)
+{
+ return mxl111sf_attach_demod(adap, 0);
+}
+
+static int mxl111sf_frontend_attach_atsc(struct dvb_usb_adapter *adap)
+{
+ return mxl111sf_lgdt3305_frontend_attach(adap, 0);
+}
+
+static int mxl111sf_frontend_attach_mh(struct dvb_usb_adapter *adap)
+{
+ return mxl111sf_lg2160_frontend_attach(adap, 0);
+}
+
+static int mxl111sf_frontend_attach_atsc_mh(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = mxl111sf_lgdt3305_frontend_attach(adap, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mxl111sf_attach_demod(adap, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mxl111sf_lg2160_frontend_attach(adap, 2);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = mxl111sf_lgdt3305_frontend_attach(adap, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mxl111sf_attach_demod(adap, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = mxl111sf_lg2161_ep6_frontend_attach(adap, 2);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int mxl111sf_frontend_attach_mercury_mh(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ deb_info("%s\n", __func__);
+
+ ret = mxl111sf_attach_demod(adap, 0);
+ if (ret < 0)
+ return ret;
+
+ if (dvb_usb_mxl111sf_spi)
+ ret = mxl111sf_lg2161_frontend_attach(adap, 1);
+ else
+ ret = mxl111sf_lg2161_ep6_frontend_attach(adap, 1);
+
+ return ret;
+}
+
+static void mxl111sf_stream_config_bulk(struct usb_data_stream_properties *stream, u8 endpoint)
+{
+ deb_info("%s: endpoint=%d size=8192\n", __func__, endpoint);
+ stream->type = USB_BULK;
+ stream->count = 5;
+ stream->endpoint = endpoint;
+ stream->u.bulk.buffersize = 8192;
+}
+
+static void mxl111sf_stream_config_isoc(struct usb_data_stream_properties *stream,
+ u8 endpoint, int framesperurb, int framesize)
+{
+ deb_info("%s: endpoint=%d size=%d\n", __func__, endpoint,
+ framesperurb * framesize);
+ stream->type = USB_ISOC;
+ stream->count = 5;
+ stream->endpoint = endpoint;
+ stream->u.isoc.framesperurb = framesperurb;
+ stream->u.isoc.framesize = framesize;
+ stream->u.isoc.interval = 1;
+}
+
+/* DVB USB Driver stuff */
+
+/* dvbt mxl111sf
+ * bulk EP4/BULK/5/8192
+ * isoc EP4/ISOC/5/96/564
+ */
+static int mxl111sf_get_stream_config_dvbt(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 4, 96, 564);
+ else
+ mxl111sf_stream_config_bulk(stream, 4);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_dvbt = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_dvbt,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_ep4_streaming_ctrl,
+ .get_stream_config = mxl111sf_get_stream_config_dvbt,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+/* atsc lgdt3305
+ * bulk EP6/BULK/5/8192
+ * isoc EP6/ISOC/5/24/3072
+ */
+static int mxl111sf_get_stream_config_atsc(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 6, 24, 3072);
+ else
+ mxl111sf_stream_config_bulk(stream, 6);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_atsc = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_atsc,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl,
+ .get_stream_config = mxl111sf_get_stream_config_atsc,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+/* mh lg2160
+ * bulk EP5/BULK/5/8192/RAW
+ * isoc EP5/ISOC/5/96/200/RAW
+ */
+static int mxl111sf_get_stream_config_mh(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 5, 96, 200);
+ else
+ mxl111sf_stream_config_bulk(stream, 5);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_mh = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_mh,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_ep5_streaming_ctrl,
+ .get_stream_config = mxl111sf_get_stream_config_mh,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+/* atsc mh lgdt3305 mxl111sf lg2160
+ * bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW
+ * isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW
+ */
+static int mxl111sf_get_stream_config_atsc_mh(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ if (fe->id == 0) {
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 6, 24, 3072);
+ else
+ mxl111sf_stream_config_bulk(stream, 6);
+ } else if (fe->id == 1) {
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 4, 96, 564);
+ else
+ mxl111sf_stream_config_bulk(stream, 4);
+ } else if (fe->id == 2) {
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 5, 96, 200);
+ else
+ mxl111sf_stream_config_bulk(stream, 5);
+ }
+ return 0;
+}
+
+static int mxl111sf_streaming_ctrl_atsc_mh(struct dvb_frontend *fe, int onoff)
+{
+ deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ if (fe->id == 0)
+ return mxl111sf_ep6_streaming_ctrl(fe, onoff);
+ else if (fe->id == 1)
+ return mxl111sf_ep4_streaming_ctrl(fe, onoff);
+ else if (fe->id == 2)
+ return mxl111sf_ep5_streaming_ctrl(fe, onoff);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_atsc_mh = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_atsc_mh,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_streaming_ctrl_atsc_mh,
+ .get_stream_config = mxl111sf_get_stream_config_atsc_mh,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+/* mercury lgdt3305 mxl111sf lg2161
+ * tp bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP6/BULK/5/8192/RAW
+ * tp isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW
+ * spi bulk EP6/BULK/5/8192 EP4/BULK/5/8192 EP5/BULK/5/8192/RAW
+ * spi isoc EP6/ISOC/5/24/3072 EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW
+ */
+static int mxl111sf_get_stream_config_mercury(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ if (fe->id == 0) {
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 6, 24, 3072);
+ else
+ mxl111sf_stream_config_bulk(stream, 6);
+ } else if (fe->id == 1) {
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 4, 96, 564);
+ else
+ mxl111sf_stream_config_bulk(stream, 4);
+ } else if (fe->id == 2 && dvb_usb_mxl111sf_spi) {
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 5, 96, 200);
+ else
+ mxl111sf_stream_config_bulk(stream, 5);
+ } else if (fe->id == 2 && !dvb_usb_mxl111sf_spi) {
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 6, 24, 3072);
+ else
+ mxl111sf_stream_config_bulk(stream, 6);
+ }
+ return 0;
+}
+
+static int mxl111sf_streaming_ctrl_mercury(struct dvb_frontend *fe, int onoff)
+{
+ deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ if (fe->id == 0)
+ return mxl111sf_ep6_streaming_ctrl(fe, onoff);
+ else if (fe->id == 1)
+ return mxl111sf_ep4_streaming_ctrl(fe, onoff);
+ else if (fe->id == 2 && dvb_usb_mxl111sf_spi)
+ return mxl111sf_ep5_streaming_ctrl(fe, onoff);
+ else if (fe->id == 2 && !dvb_usb_mxl111sf_spi)
+ return mxl111sf_ep6_streaming_ctrl(fe, onoff);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_mercury = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_mercury,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_streaming_ctrl_mercury,
+ .get_stream_config = mxl111sf_get_stream_config_mercury,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+/* mercury mh mxl111sf lg2161
+ * tp bulk EP4/BULK/5/8192 EP6/BULK/5/8192/RAW
+ * tp isoc EP4/ISOC/5/96/564 EP6/ISOC/5/24/3072/RAW
+ * spi bulk EP4/BULK/5/8192 EP5/BULK/5/8192/RAW
+ * spi isoc EP4/ISOC/5/96/564 EP5/ISOC/5/96/200/RAW
+ */
+static int mxl111sf_get_stream_config_mercury_mh(struct dvb_frontend *fe,
+ u8 *ts_type, struct usb_data_stream_properties *stream)
+{
+ deb_info("%s: fe=%d\n", __func__, fe->id);
+
+ if (fe->id == 0) {
+ *ts_type = DVB_USB_FE_TS_TYPE_188;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 4, 96, 564);
+ else
+ mxl111sf_stream_config_bulk(stream, 4);
+ } else if (fe->id == 1 && dvb_usb_mxl111sf_spi) {
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 5, 96, 200);
+ else
+ mxl111sf_stream_config_bulk(stream, 5);
+ } else if (fe->id == 1 && !dvb_usb_mxl111sf_spi) {
+ *ts_type = DVB_USB_FE_TS_TYPE_RAW;
+ if (dvb_usb_mxl111sf_isoc)
+ mxl111sf_stream_config_isoc(stream, 6, 24, 3072);
+ else
+ mxl111sf_stream_config_bulk(stream, 6);
+ }
+ return 0;
+}
+
+static int mxl111sf_streaming_ctrl_mercury_mh(struct dvb_frontend *fe, int onoff)
+{
+ deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff);
+
+ if (fe->id == 0)
+ return mxl111sf_ep4_streaming_ctrl(fe, onoff);
+ else if (fe->id == 1 && dvb_usb_mxl111sf_spi)
+ return mxl111sf_ep5_streaming_ctrl(fe, onoff);
+ else if (fe->id == 1 && !dvb_usb_mxl111sf_spi)
+ return mxl111sf_ep6_streaming_ctrl(fe, onoff);
+ return 0;
+}
+
+static struct dvb_usb_device_properties mxl111sf_props_mercury_mh = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct mxl111sf_state),
+
+ .generic_bulk_ctrl_endpoint = 0x02,
+ .generic_bulk_ctrl_endpoint_response = 0x81,
+
+ .i2c_algo = &mxl111sf_i2c_algo,
+ .frontend_attach = mxl111sf_frontend_attach_mercury_mh,
+ .tuner_attach = mxl111sf_attach_tuner,
+ .init = mxl111sf_init,
+ .streaming_ctrl = mxl111sf_streaming_ctrl_mercury_mh,
+ .get_stream_config = mxl111sf_get_stream_config_mercury_mh,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_ISOC(6, 5, 24, 3072, 1),
+ }
+ }
+};
+
+static const struct usb_device_id mxl111sf_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602, &mxl111sf_props_mh, "HCW 126xxx", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a, &mxl111sf_props_mh, "HCW 126xxx", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c, &mxl111sf_props_dvbt, "Hauppauge 126xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b, &mxl111sf_props_atsc_mh, "Hauppauge 126xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701, &mxl111sf_props_atsc, "Hauppauge 126xxx ATSC", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702, &mxl111sf_props_mh, "HCW 117xxx", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764, &mxl111sf_props_dvbt, "Hauppauge 117xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4, &mxl111sf_props_dvbt, "Hauppauge 138xxx DVBT", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff, &mxl111sf_props_mercury, "Hauppauge Mercury", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a, &mxl111sf_props_mercury_mh, "Hauppauge 126xxx", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b, &mxl111sf_props_mercury, "Hauppauge WinTV-Aero-M", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { DVB_USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767, &mxl111sf_props_atsc_mh, "Hauppauge 117xxx ATSC+", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, mxl111sf_id_table);
+
+static struct usb_driver mxl111sf_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = mxl111sf_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(mxl111sf_usb_driver);
+
+MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>");
+MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
new file mode 100644
index 000000000000..9816de86e48c
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+
+#ifndef _DVB_USB_MXL111SF_H_
+#define _DVB_USB_MXL111SF_H_
+
+#ifdef DVB_USB_LOG_PREFIX
+#undef DVB_USB_LOG_PREFIX
+#endif
+#define DVB_USB_LOG_PREFIX "mxl111sf"
+#include "dvb_usb.h"
+#include <media/tveeprom.h>
+
+#define MXL_EP1_REG_READ 1
+#define MXL_EP2_REG_WRITE 2
+#define MXL_EP3_INTERRUPT 3
+#define MXL_EP4_MPEG2 4
+#define MXL_EP5_I2S 5
+#define MXL_EP6_656 6
+#define MXL_EP6_MPEG2 6
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+enum mxl111sf_current_mode {
+ mxl_mode_dvbt = MXL_EP4_MPEG2,
+ mxl_mode_mh = MXL_EP5_I2S,
+ mxl_mode_atsc = MXL_EP6_MPEG2,
+};
+#endif
+
+enum mxl111sf_gpio_port_expander {
+ mxl111sf_gpio_hw,
+ mxl111sf_PCA9534,
+};
+
+struct mxl111sf_adap_state {
+ int alt_mode;
+ int gpio_mode;
+ int device_mode;
+ int ep6_clockphase;
+ int (*fe_init)(struct dvb_frontend *);
+ int (*fe_sleep)(struct dvb_frontend *);
+};
+
+struct mxl111sf_state {
+ struct dvb_usb_device *d;
+
+ enum mxl111sf_gpio_port_expander gpio_port_expander;
+ u8 port_expander_addr;
+
+ u8 chip_id;
+ u8 chip_ver;
+#define MXL111SF_V6 1
+#define MXL111SF_V8_100 2
+#define MXL111SF_V8_200 3
+ u8 chip_rev;
+
+#ifdef USING_ENUM_mxl111sf_current_mode
+ enum mxl111sf_current_mode current_mode;
+#endif
+
+#define MXL_TUNER_MODE 0
+#define MXL_SOC_MODE 1
+#define MXL_DEV_MODE_MASK 0x01
+#if 1
+ int device_mode;
+#endif
+ /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t),
+ EP5 BULK transfer (atsc-mh),
+ EP6 BULK transfer (atsc/qam),
+ use usb alt setting 2 for EP4 BULK transfer (dvb-t),
+ EP5 ISOC transfer (atsc-mh),
+ EP6 ISOC transfer (atsc/qam),
+ */
+ int alt_mode;
+ int gpio_mode;
+ struct tveeprom tv;
+
+ struct mutex fe_lock;
+ u8 num_frontends;
+ struct mxl111sf_adap_state adap_state[3];
+};
+
+int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data);
+int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data);
+
+struct mxl111sf_reg_ctrl_info {
+ u8 addr;
+ u8 mask;
+ u8 data;
+};
+
+int mxl111sf_write_reg_mask(struct mxl111sf_state *state,
+ u8 addr, u8 mask, u8 data);
+int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state,
+ struct mxl111sf_reg_ctrl_info *ctrl_reg_info);
+
+/* needed for hardware i2c functions in mxl111sf-i2c.c:
+ * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */
+int mxl111sf_ctrl_msg(struct dvb_usb_device *d,
+ u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen);
+
+#define mxl_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt "\n", __func__, ##arg)
+
+#define mxl_info(fmt, arg...) \
+ mxl_printk(KERN_INFO, fmt, ##arg)
+
+extern int dvb_usb_mxl111sf_debug;
+#define mxl_debug(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define MXL_I2C_DBG 0x04
+#define MXL_ADV_DBG 0x10
+#define mxl_debug_adv(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c(fmt, arg...) \
+ if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+#define mxl_i2c_adv(fmt, arg...) \
+ if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \
+ (MXL_I2C_DBG | MXL_ADV_DBG)) \
+ mxl_printk(KERN_DEBUG, fmt, ##arg)
+
+/* The following allows the mxl_fail() macro defined below to work
+ * in externel modules, such as mxl111sf-tuner.ko, even though
+ * dvb_usb_mxl111sf_debug is not defined within those modules */
+#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__))
+#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG
+#else
+#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug
+#endif
+
+#define mxl_fail(ret) \
+({ \
+ int __ret; \
+ __ret = (ret < 0); \
+ if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \
+ mxl_printk(KERN_ERR, "error %d on line %d", \
+ ret, __LINE__); \
+ __ret; \
+})
+
+#endif /* _DVB_USB_MXL111SF_H_ */
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
new file mode 100644
index 000000000000..adabba8d28bc
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c
@@ -0,0 +1,1370 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Thomas Mair <thomas.mair86@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl28xxu.h"
+
+#include "rtl2830.h"
+#include "rtl2832.h"
+
+#include "qt1010.h"
+#include "mt2060.h"
+#include "mxl5005s.h"
+#include "fc0012.h"
+#include "fc0013.h"
+#include "e4000.h"
+#include "fc2580.h"
+#include "tua9001.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 requesttype;
+ u8 *buf;
+
+ buf = kmalloc(req->size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (req->index & CMD_WR_FLAG) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
+ req->index, buf, req->size, 1000);
+
+ dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value,
+ req->index, buf, req->size);
+
+ if (ret > 0)
+ ret = 0;
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ kfree(buf);
+
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_WR;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_WR;
+ else
+ req.index = CMD_IR_WR;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_RD;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_RD;
+ else
+ req.index = CMD_IR_RD;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ return rtl28xx_wr_regs(d, reg, &val, 1);
+}
+
+static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ return rtl2831_rd_regs(d, reg, val, 1);
+}
+
+static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl28xx_rd_reg(d, reg, &tmp);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl28xx_wr_reg(d, reg, val);
+}
+
+/* I2C */
+static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ int ret;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct rtl28xxu_priv *priv = d->priv;
+ struct rtl28xxu_req req;
+
+ /*
+ * It is not known which are real I2C bus xfer limits, but testing
+ * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
+ * TODO: find out RTL2832U lens
+ */
+
+ /*
+ * I2C adapter logic looks rather complicated due to fact it handles
+ * three different access methods. Those methods are;
+ * 1) integrated demod access
+ * 2) old I2C access
+ * 3) new I2C access
+ *
+ * Used method is selected in order 1, 2, 3. Method 3 can handle all
+ * requests but there is two reasons why not use it always;
+ * 1) It is most expensive, usually two USB messages are needed
+ * 2) At least RTL2831U does not support it
+ *
+ * Method 3 is needed in case of I2C write+read (typical register read)
+ * where write is more than one byte.
+ */
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 24 || msg[1].len > 24) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_DEMOD_RD | priv->page;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else if (msg[0].len < 2) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_RD;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ if (ret)
+ goto err_mutex_unlock;
+
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_RD;
+ req.size = msg[1].len;
+ req.data = msg[1].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 22) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ if (msg[0].buf[0] == 0x00) {
+ /* save demod page for later demod access */
+ priv->page = msg[0].buf[1];
+ ret = 0;
+ } else {
+ req.value = (msg[0].buf[0] << 8) |
+ (msg[0].addr << 1);
+ req.index = CMD_DEMOD_WR | priv->page;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (msg[0].len < 23) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_WR;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+err_mutex_unlock:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret ? ret : num;
+}
+
+static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm rtl28xxu_i2c_algo = {
+ .master_xfer = rtl28xxu_i2c_xfer,
+ .functionality = rtl28xxu_i2c_func,
+};
+
+static int rtl2831u_read_config(struct dvb_usb_device *d)
+{
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ int ret;
+ u8 buf[1];
+ /* open RTL2831U/RTL2830 I2C gate */
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x08"};
+ /* tuner probes */
+ struct rtl28xxu_req req_mt2060 = {0x00c0, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_qt1010 = {0x0fc4, CMD_I2C_RD, 1, buf};
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /*
+ * RTL2831U GPIOs
+ * =========================================================
+ * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
+ * GPIO2 | LED | 0 off | 1 on |
+ * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
+ */
+
+ /* GPIO direction */
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* demod needs some time to wake up */
+ msleep(20);
+
+ priv->tuner_name = "NONE";
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(d, &req_gate_open);
+ if (ret)
+ goto err;
+
+ /* check QT1010 ID(?) register; reg=0f val=2c */
+ ret = rtl28xxu_ctrl_msg(d, &req_qt1010);
+ if (ret == 0 && buf[0] == 0x2c) {
+ priv->tuner = TUNER_RTL2830_QT1010;
+ priv->tuner_name = "QT1010";
+ goto found;
+ }
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(d, &req_gate_open);
+ if (ret)
+ goto err;
+
+ /* check MT2060 ID register; reg=00 val=63 */
+ ret = rtl28xxu_ctrl_msg(d, &req_mt2060);
+ if (ret == 0 && buf[0] == 0x63) {
+ priv->tuner = TUNER_RTL2830_MT2060;
+ priv->tuner_name = "MT2060";
+ goto found;
+ }
+
+ /* assume MXL5005S */
+ priv->tuner = TUNER_RTL2830_MXL5005S;
+ priv->tuner_name = "MXL5005S";
+ goto found;
+
+found:
+ dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_read_config(struct dvb_usb_device *d)
+{
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ int ret;
+ u8 buf[2];
+ /* open RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
+ /* close RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+ /* tuner probes */
+ struct rtl28xxu_req req_fc0012 = {0x00c6, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_fc0013 = {0x00c6, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mt2266 = {0x00c0, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_mt2063 = {0x00c0, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_max3543 = {0x00c0, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_tua9001 = {0x7ec0, CMD_I2C_RD, 2, buf};
+ struct rtl28xxu_req req_mxl5007t = {0xd9c0, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_e4000 = {0x02c8, CMD_I2C_RD, 1, buf};
+ struct rtl28xxu_req req_tda18272 = {0x00c0, CMD_I2C_RD, 2, buf};
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* enable GPIO3 and GPIO6 as output */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(d, &req_gate_open);
+ if (ret)
+ goto err;
+
+ priv->tuner_name = "NONE";
+
+ /* check FC0012 ID register; reg=00 val=a1 */
+ ret = rtl28xxu_ctrl_msg(d, &req_fc0012);
+ if (ret == 0 && buf[0] == 0xa1) {
+ priv->tuner = TUNER_RTL2832_FC0012;
+ priv->tuner_name = "FC0012";
+ goto found;
+ }
+
+ /* check FC0013 ID register; reg=00 val=a3 */
+ ret = rtl28xxu_ctrl_msg(d, &req_fc0013);
+ if (ret == 0 && buf[0] == 0xa3) {
+ priv->tuner = TUNER_RTL2832_FC0013;
+ priv->tuner_name = "FC0013";
+ goto found;
+ }
+
+ /* check MT2266 ID register; reg=00 val=85 */
+ ret = rtl28xxu_ctrl_msg(d, &req_mt2266);
+ if (ret == 0 && buf[0] == 0x85) {
+ priv->tuner = TUNER_RTL2832_MT2266;
+ priv->tuner_name = "MT2266";
+ goto found;
+ }
+
+ /* check FC2580 ID register; reg=01 val=56 */
+ ret = rtl28xxu_ctrl_msg(d, &req_fc2580);
+ if (ret == 0 && buf[0] == 0x56) {
+ priv->tuner = TUNER_RTL2832_FC2580;
+ priv->tuner_name = "FC2580";
+ goto found;
+ }
+
+ /* check MT2063 ID register; reg=00 val=9e || 9c */
+ ret = rtl28xxu_ctrl_msg(d, &req_mt2063);
+ if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) {
+ priv->tuner = TUNER_RTL2832_MT2063;
+ priv->tuner_name = "MT2063";
+ goto found;
+ }
+
+ /* check MAX3543 ID register; reg=00 val=38 */
+ ret = rtl28xxu_ctrl_msg(d, &req_max3543);
+ if (ret == 0 && buf[0] == 0x38) {
+ priv->tuner = TUNER_RTL2832_MAX3543;
+ priv->tuner_name = "MAX3543";
+ goto found;
+ }
+
+ /* check TUA9001 ID register; reg=7e val=2328 */
+ ret = rtl28xxu_ctrl_msg(d, &req_tua9001);
+ if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) {
+ priv->tuner = TUNER_RTL2832_TUA9001;
+ priv->tuner_name = "TUA9001";
+ goto found;
+ }
+
+ /* check MXL5007R ID register; reg=d9 val=14 */
+ ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t);
+ if (ret == 0 && buf[0] == 0x14) {
+ priv->tuner = TUNER_RTL2832_MXL5007T;
+ priv->tuner_name = "MXL5007T";
+ goto found;
+ }
+
+ /* check E4000 ID register; reg=02 val=40 */
+ ret = rtl28xxu_ctrl_msg(d, &req_e4000);
+ if (ret == 0 && buf[0] == 0x40) {
+ priv->tuner = TUNER_RTL2832_E4000;
+ priv->tuner_name = "E4000";
+ goto found;
+ }
+
+ /* check TDA18272 ID register; reg=00 val=c760 */
+ ret = rtl28xxu_ctrl_msg(d, &req_tda18272);
+ if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) {
+ priv->tuner = TUNER_RTL2832_TDA18272;
+ priv->tuner_name = "TDA18272";
+ goto found;
+ }
+
+found:
+ dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name);
+
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(d, &req_gate_close);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 0,
+ .vtop = 0x3f,
+ .krf = 0x04,
+ .agc_targ_val = 0x3e,
+};
+
+static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ struct rtl2830_config *rtl2830_config;
+ int ret;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2830_QT1010:
+ rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
+ break;
+ case TUNER_RTL2830_MT2060:
+ rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
+ break;
+ case TUNER_RTL2830_MXL5005S:
+ rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: unknown tuner=%s\n",
+ KBUILD_MODNAME, priv->tuner_name);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* attach demodulator */
+ adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, &d->i2c_adap);
+ if (!adap->fe[0]) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .if_dvbt = 0,
+ .tuner = TUNER_RTL2832_FC0012
+};
+
+static struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .if_dvbt = 0,
+ .tuner = TUNER_RTL2832_FC0013
+};
+
+static struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .tuner = TUNER_RTL2832_TUA9001,
+};
+
+static struct rtl2832_config rtl28xxu_rtl2832_e4000_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .tuner = TUNER_RTL2832_E4000,
+};
+
+static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+
+ switch (cmd) {
+ case FC_FE_CALLBACK_VHF_ENABLE:
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ if (arg)
+ val &= 0xbf; /* set GPIO6 low */
+ else
+ val |= 0x40; /* set GPIO6 high */
+
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg);
+
+ /*
+ * CEN always enabled by hardware wiring
+ * RESETN GPIO4
+ * RXEN GPIO1
+ */
+
+ switch (cmd) {
+ case TUA9001_CMD_RESETN:
+ if (arg)
+ val = (1 << 4);
+ else
+ val = (0 << 4);
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10);
+ if (ret)
+ goto err;
+ break;
+ case TUA9001_CMD_RXEN:
+ if (arg)
+ val = (1 << 1);
+ else
+ val = (0 << 1);
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02);
+ if (ret)
+ goto err;
+ break;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+ struct rtl28xxu_priv *priv = d->priv;
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC0012:
+ return rtl2832u_fc0012_tuner_callback(d, cmd, arg);
+ case TUNER_RTL2832_TUA9001:
+ return rtl2832u_tua9001_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rtl2832u_frontend_callback(void *adapter_priv, int component,
+ int cmd, int arg)
+{
+ struct i2c_adapter *adap = adapter_priv;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+ dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n",
+ __func__, component, cmd, arg);
+
+ switch (component) {
+ case DVB_FRONTEND_COMPONENT_TUNER:
+ return rtl2832u_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ struct rtl2832_config *rtl2832_config;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC0012:
+ rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+ break;
+ case TUNER_RTL2832_FC0013:
+ rtl2832_config = &rtl28xxu_rtl2832_fc0013_config;
+ break;
+ case TUNER_RTL2832_FC2580:
+ /* FIXME: do not abuse fc0012 settings */
+ rtl2832_config = &rtl28xxu_rtl2832_fc0012_config;
+ break;
+ case TUNER_RTL2832_TUA9001:
+ rtl2832_config = &rtl28xxu_rtl2832_tua9001_config;
+ break;
+ case TUNER_RTL2832_E4000:
+ rtl2832_config = &rtl28xxu_rtl2832_e4000_config;
+ break;
+ default:
+ dev_err(&d->udev->dev, "%s: unknown tuner=%s\n",
+ KBUILD_MODNAME, priv->tuner_name);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* attach demodulator */
+ adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, &d->i2c_adap);
+ if (!adap->fe[0]) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* set fe callback */
+ adap->fe[0]->callback = rtl2832u_frontend_callback;
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct qt1010_config rtl28xxu_qt1010_config = {
+ .i2c_address = 0x62, /* 0xc4 */
+};
+
+static struct mt2060_config rtl28xxu_mt2060_config = {
+ .i2c_address = 0x60, /* 0xc0 */
+ .clock_out = 0,
+};
+
+static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
+ .i2c_address = 0x63, /* 0xc6 */
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ struct i2c_adapter *rtl2830_tuner_i2c;
+ struct dvb_frontend *fe;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
+ rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe[0]);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2830_QT1010:
+ fe = dvb_attach(qt1010_attach, adap->fe[0],
+ rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
+ break;
+ case TUNER_RTL2830_MT2060:
+ fe = dvb_attach(mt2060_attach, adap->fe[0],
+ rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
+ 1220);
+ break;
+ case TUNER_RTL2830_MXL5005S:
+ fe = dvb_attach(mxl5005s_attach, adap->fe[0],
+ rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
+ break;
+ default:
+ fe = NULL;
+ dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
+ priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static const struct e4000_config rtl2832u_e4000_config = {
+ .i2c_addr = 0x64,
+ .clock = 28800000,
+};
+
+static const struct fc2580_config rtl2832u_fc2580_config = {
+ .i2c_addr = 0x56,
+ .clock = 16384000,
+};
+
+static struct tua9001_config rtl2832u_tua9001_config = {
+ .i2c_addr = 0x60,
+};
+
+static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct dvb_usb_device *d = adap_to_d(adap);
+ struct rtl28xxu_priv *priv = d_to_priv(d);
+ struct dvb_frontend *fe;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC0012:
+ fe = dvb_attach(fc0012_attach, adap->fe[0],
+ &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+ /* since fc0012 includs reading the signal strength delegate
+ * that to the tuner driver */
+ adap->fe[0]->ops.read_signal_strength =
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
+ return 0;
+ break;
+ case TUNER_RTL2832_FC0013:
+ fe = dvb_attach(fc0013_attach, adap->fe[0],
+ &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ);
+
+ /* fc0013 also supports signal strength reading */
+ adap->fe[0]->ops.read_signal_strength =
+ adap->fe[0]->ops.tuner_ops.get_rf_strength;
+ return 0;
+ case TUNER_RTL2832_E4000:
+ fe = dvb_attach(e4000_attach, adap->fe[0], &d->i2c_adap,
+ &rtl2832u_e4000_config);
+ break;
+ case TUNER_RTL2832_FC2580:
+ fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap,
+ &rtl2832u_fc2580_config);
+ break;
+ case TUNER_RTL2832_TUA9001:
+ /* enable GPIO1 and GPIO4 as output */
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12);
+ if (ret)
+ goto err;
+
+ fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap,
+ &rtl2832u_tua9001_config);
+ break;
+ default:
+ fe = NULL;
+ dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME,
+ priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_init(struct dvb_usb_device *d)
+{
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s:\n", __func__);
+
+ /* init USB endpoints */
+ ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val);
+ if (ret)
+ goto err;
+
+ /* enable DMA and Full Packet Mode*/
+ val |= 0x09;
+ ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val);
+ if (ret)
+ goto err;
+
+ /* set EPA maximum packet size to 0x0200 */
+ ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ /* change EPA FIFO length */
+ ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 gpio, sys0, epa_ctl[2];
+
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ /* demod adc */
+ ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, read GPIOs */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ dev_dbg(&d->udev->dev, "%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__,
+ sys0, gpio);
+
+ if (onoff) {
+ gpio |= 0x01; /* GPIO0 = 1 */
+ gpio &= (~0x10); /* GPIO4 = 0 */
+ gpio |= 0x04; /* GPIO2 = 1, LED on */
+ sys0 = sys0 & 0x0f;
+ sys0 |= 0xe0;
+ epa_ctl[0] = 0x00; /* clear stall */
+ epa_ctl[1] = 0x00; /* clear reset */
+ } else {
+ gpio &= (~0x01); /* GPIO0 = 0 */
+ gpio |= 0x10; /* GPIO4 = 1 */
+ gpio &= (~0x04); /* GPIO2 = 1, LED off */
+ sys0 = sys0 & (~0xc0);
+ epa_ctl[0] = 0x10; /* set stall */
+ epa_ctl[1] = 0x02; /* set reset */
+ }
+
+ dev_dbg(&d->udev->dev, "%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__,
+ sys0, gpio);
+
+ /* demod adc */
+ ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, write GPIOs */
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ /* streaming EP: stall & reset */
+ ret = rtl28xx_wr_regs(d, USB_EPA_CTL, epa_ctl, 2);
+ if (ret)
+ goto err;
+
+ if (onoff)
+ usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81));
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 val;
+
+ dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff);
+
+ if (onoff) {
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x08;
+ val &= 0xef;
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+
+ /* demod_ctl_1 */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ if (ret)
+ goto err;
+
+ val &= 0xef;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+ if (ret)
+ goto err;
+
+ /* demod control */
+ /* PLL enable */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ /* bit 7 to 1 */
+ val |= 0x80;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x20;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ mdelay(5);
+
+ /*enable ADC_Q and ADC_I */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x48;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ /* streaming EP: clear stall & reset */
+ ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x81));
+ if (ret)
+ goto err;
+ } else {
+ /* demod_ctl_1 */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL1, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x0c;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL1, val);
+ if (ret)
+ goto err;
+
+ /* set output values */
+ ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val);
+ if (ret)
+ goto err;
+
+ val |= 0x10;
+
+ ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val);
+ if (ret)
+ goto err;
+
+ /* demod control */
+ ret = rtl28xx_rd_reg(d, SYS_DEMOD_CTL, &val);
+ if (ret)
+ goto err;
+
+ val &= 0x37;
+
+ ret = rtl28xx_wr_reg(d, SYS_DEMOD_CTL, val);
+ if (ret)
+ goto err;
+
+ /* streaming EP: set stall & reset */
+ ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+
+static int rtl2831u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[5];
+ u32 rc_code;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { 0x3033, 0x80 },
+ { 0x3020, 0x43 },
+ { 0x3021, 0x16 },
+ { 0x3022, 0x16 },
+ { 0x3023, 0x5a },
+ { 0x3024, 0x2d },
+ { 0x3025, 0x16 },
+ { 0x3026, 0x01 },
+ { 0x3028, 0xb0 },
+ { 0x3029, 0x04 },
+ { 0x302c, 0x88 },
+ { 0x302e, 0x13 },
+ { 0x3030, 0xdf },
+ { 0x3031, 0x05 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
+ if (ret)
+ goto err;
+
+ if (buf[4] & 0x01) {
+ if (buf[2] == (u8) ~buf[3]) {
+ if (buf[0] == (u8) ~buf[1]) {
+ /* NEC standard (16 bit) */
+ rc_code = buf[0] << 8 | buf[2];
+ } else {
+ /* NEC extended (24 bit) */
+ rc_code = buf[0] << 16 |
+ buf[1] << 8 | buf[2];
+ }
+ } else {
+ /* NEC full (32 bit) */
+ rc_code = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+ }
+
+ rc_keydown(d->rc_dev, rc_code, 0);
+
+ ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+
+ /* repeated intentionally to avoid extra keypress */
+ ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831u_get_rc_config(struct dvb_usb_device *d,
+ struct dvb_usb_rc *rc)
+{
+ rc->map_name = RC_MAP_EMPTY;
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = rtl2831u_rc_query;
+ rc->interval = 400;
+
+ return 0;
+}
+
+static int rtl2832u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[128];
+ int len;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { IR_RX_CTRL, 0x20 },
+ { IR_RX_BUF_CTRL, 0x80 },
+ { IR_RX_IF, 0xff },
+ { IR_RX_IE, 0xff },
+ { IR_MAX_DURATION0, 0xd0 },
+ { IR_MAX_DURATION1, 0x07 },
+ { IR_IDLE_LEN0, 0xc0 },
+ { IR_IDLE_LEN1, 0x00 },
+ { IR_GLITCH_LEN, 0x03 },
+ { IR_RX_CLK, 0x09 },
+ { IR_RX_CFG, 0x1c },
+ { IR_MAX_H_TOL_LEN, 0x1e },
+ { IR_MAX_L_TOL_LEN, 0x1e },
+ { IR_RX_CTRL, 0x80 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (buf[0] != 0x83)
+ goto exit;
+
+ ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]);
+ if (ret)
+ goto err;
+
+ len = buf[0];
+ ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+
+ /* TODO: pass raw IR to Kernel IR decoder */
+
+ ret = rtl28xx_wr_reg(d, IR_RX_IF, 0x03);
+ ret = rtl28xx_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+ ret = rtl28xx_wr_reg(d, IR_RX_CTRL, 0x80);
+
+exit:
+ return ret;
+err:
+ dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_get_rc_config(struct dvb_usb_device *d,
+ struct dvb_usb_rc *rc)
+{
+ rc->map_name = RC_MAP_EMPTY;
+ rc->allowed_protos = RC_TYPE_NEC;
+ rc->query = rtl2832u_rc_query;
+ rc->interval = 400;
+
+ return 0;
+}
+
+static const struct dvb_usb_device_properties rtl2831u_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .power_ctrl = rtl2831u_power_ctrl,
+ .i2c_algo = &rtl28xxu_i2c_algo,
+ .read_config = rtl2831u_read_config,
+ .frontend_attach = rtl2831u_frontend_attach,
+ .tuner_attach = rtl2831u_tuner_attach,
+ .init = rtl28xxu_init,
+ .get_rc_config = rtl2831u_get_rc_config,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512),
+ },
+ },
+};
+
+static const struct dvb_usb_device_properties rtl2832u_props = {
+ .driver_name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ .adapter_nr = adapter_nr,
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .power_ctrl = rtl2832u_power_ctrl,
+ .i2c_algo = &rtl28xxu_i2c_algo,
+ .read_config = rtl2832u_read_config,
+ .frontend_attach = rtl2832u_frontend_attach,
+ .tuner_attach = rtl2832u_tuner_attach,
+ .init = rtl28xxu_init,
+ .get_rc_config = rtl2832u_get_rc_config,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512),
+ },
+ },
+};
+
+static const struct usb_device_id rtl28xxu_id_table[] = {
+ { DVB_USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U,
+ &rtl2831u_props, "Realtek RTL2831U reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT,
+ &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) },
+ { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2,
+ &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) },
+
+ { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2832,
+ &rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838,
+ &rtl2832u_props, "Realtek RTL2832U reference design", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1,
+ &rtl2832u_props, "Terratec Cinergy T Stick Black", NULL) },
+ { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT,
+ &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK,
+ &rtl2832u_props, "NOXON DAB/DAB+ USB dongle", NULL) },
+ { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0,
+ &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) },
+ { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101,
+ &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) },
+ { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680,
+ &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) },
+ { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3,
+ &rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table);
+
+static struct usb_driver rtl28xxu_usb_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtl28xxu_id_table,
+ .probe = dvb_usbv2_probe,
+ .disconnect = dvb_usbv2_disconnect,
+ .suspend = dvb_usbv2_suspend,
+ .resume = dvb_usbv2_resume,
+ .reset_resume = dvb_usbv2_reset_resume,
+ .no_dynamic_id = 1,
+ .soft_unbind = 1,
+};
+
+module_usb_driver(rtl28xxu_usb_driver);
+
+MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_AUTHOR("Thomas Mair <thomas.mair86@googlemail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
new file mode 100644
index 000000000000..2f3af2d3b6ce
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h
@@ -0,0 +1,245 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL28XXU_H
+#define RTL28XXU_H
+
+#include "dvb_usb.h"
+
+/*
+ * USB commands
+ * (usb_control_msg() index parameter)
+ */
+
+#define DEMOD 0x0000
+#define USB 0x0100
+#define SYS 0x0200
+#define I2C 0x0300
+#define I2C_DA 0x0600
+
+#define CMD_WR_FLAG 0x0010
+#define CMD_DEMOD_RD 0x0000
+#define CMD_DEMOD_WR 0x0010
+#define CMD_USB_RD 0x0100
+#define CMD_USB_WR 0x0110
+#define CMD_SYS_RD 0x0200
+#define CMD_IR_RD 0x0201
+#define CMD_IR_WR 0x0211
+#define CMD_SYS_WR 0x0210
+#define CMD_I2C_RD 0x0300
+#define CMD_I2C_WR 0x0310
+#define CMD_I2C_DA_RD 0x0600
+#define CMD_I2C_DA_WR 0x0610
+
+
+struct rtl28xxu_priv {
+ u8 chip_id;
+ u8 tuner;
+ char *tuner_name;
+ u8 page; /* integrated demod active register page */
+ bool rc_active;
+};
+
+enum rtl28xxu_chip_id {
+ CHIP_ID_NONE,
+ CHIP_ID_RTL2831U,
+ CHIP_ID_RTL2832U,
+};
+
+/* XXX: Hack. This must be keep sync with rtl2832 demod driver. */
+enum rtl28xxu_tuner {
+ TUNER_NONE,
+
+ TUNER_RTL2830_QT1010 = 0x10,
+ TUNER_RTL2830_MT2060,
+ TUNER_RTL2830_MXL5005S,
+
+ TUNER_RTL2832_MT2266 = 0x20,
+ TUNER_RTL2832_FC2580,
+ TUNER_RTL2832_MT2063,
+ TUNER_RTL2832_MAX3543,
+ TUNER_RTL2832_TUA9001,
+ TUNER_RTL2832_MXL5007T,
+ TUNER_RTL2832_FC0012,
+ TUNER_RTL2832_E4000,
+ TUNER_RTL2832_TDA18272,
+ TUNER_RTL2832_FC0013,
+};
+
+struct rtl28xxu_req {
+ u16 value;
+ u16 index;
+ u16 size;
+ u8 *data;
+};
+
+struct rtl28xxu_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+/*
+ * memory map
+ *
+ * 0x0000 DEMOD : demodulator
+ * 0x2000 USB : SIE, USB endpoint, debug, DMA
+ * 0x3000 SYS : system
+ * 0xfc00 RC : remote controller (not RTL2831U)
+ */
+
+/*
+ * USB registers
+ */
+/* SIE Control Registers */
+#define USB_SYSCTL 0x2000 /* USB system control */
+#define USB_SYSCTL_0 0x2000 /* USB system control */
+#define USB_SYSCTL_1 0x2001 /* USB system control */
+#define USB_SYSCTL_2 0x2002 /* USB system control */
+#define USB_SYSCTL_3 0x2003 /* USB system control */
+#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
+#define USB_IRQEN 0x200C /* SIE interrupt enable */
+#define USB_CTRL 0x2010 /* USB control */
+#define USB_STAT 0x2014 /* USB status */
+#define USB_DEVADDR 0x2018 /* USB device address */
+#define USB_TEST 0x201C /* USB test mode */
+#define USB_FRAME_NUMBER 0x2020 /* frame number */
+#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
+#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
+#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
+/* Endpoint Registers */
+#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
+#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
+#define USB_EP0_CFG 0x2104 /* EP 0 configure */
+#define USB_EP0_CTL 0x2108 /* EP 0 control */
+#define USB_EP0_STAT 0x210C /* EP 0 status */
+#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
+#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
+#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
+#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
+#define USB_EPA_CFG 0x2144 /* EP A configure */
+#define USB_EPA_CFG_0 0x2144 /* EP A configure */
+#define USB_EPA_CFG_1 0x2145 /* EP A configure */
+#define USB_EPA_CFG_2 0x2146 /* EP A configure */
+#define USB_EPA_CFG_3 0x2147 /* EP A configure */
+#define USB_EPA_CTL 0x2148 /* EP A control */
+#define USB_EPA_CTL_0 0x2148 /* EP A control */
+#define USB_EPA_CTL_1 0x2149 /* EP A control */
+#define USB_EPA_CTL_2 0x214A /* EP A control */
+#define USB_EPA_CTL_3 0x214B /* EP A control */
+#define USB_EPA_STAT 0x214C /* EP A status */
+#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
+#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
+#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
+#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
+#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
+#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
+/* Debug Registers */
+#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
+#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
+#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
+#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
+#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
+#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
+#define USB_UTMI_TST 0x2F80 /* UTMI test */
+#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
+#define USB_TSTCTL 0x2F88 /* test control */
+#define USB_TSTCTL2 0x2F8C /* test control 2 */
+#define USB_PID_FORCE 0x2F90 /* force PID */
+#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
+#define USB_RXERR_CNT 0x2F98 /* RX error counter */
+#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
+#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
+#define USB_CNTTEST 0x2FA4 /* counter test */
+#define USB_PHYTST 0x2FC0 /* USB PHY test */
+#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
+#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
+
+/*
+ * SYS registers
+ */
+/* demod control registers */
+#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
+#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
+/* GPIO registers */
+#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
+#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
+#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
+#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
+#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
+#define SYS_SYSINTE 0x3005 /* system interrupt enable */
+#define SYS_SYSINTS 0x3006 /* system interrupt status */
+#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
+#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
+#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
+#define SYS_DEMOD_CTL1 0x300B
+
+/* IrDA registers */
+#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
+#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
+#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
+#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
+#define SYS_IRRC_CR 0x3030 /* IR control */
+#define SYS_IRRC_RP 0x3034 /* IR read port */
+#define SYS_IRRC_SR 0x3038 /* IR status */
+/* I2C master registers */
+#define SYS_I2CCR 0x3040 /* I2C clock */
+#define SYS_I2CMCR 0x3044 /* I2C master control */
+#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
+#define SYS_I2CMSR 0x304C /* I2C master status */
+#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
+
+/*
+ * IR registers
+ */
+#define IR_RX_BUF 0xFC00
+#define IR_RX_IE 0xFD00
+#define IR_RX_IF 0xFD01
+#define IR_RX_CTRL 0xFD02
+#define IR_RX_CFG 0xFD03
+#define IR_MAX_DURATION0 0xFD04
+#define IR_MAX_DURATION1 0xFD05
+#define IR_IDLE_LEN0 0xFD06
+#define IR_IDLE_LEN1 0xFD07
+#define IR_GLITCH_LEN 0xFD08
+#define IR_RX_BUF_CTRL 0xFD09
+#define IR_RX_BUF_DATA 0xFD0A
+#define IR_RX_BC 0xFD0B
+#define IR_RX_CLK 0xFD0C
+#define IR_RX_C_COUNT_L 0xFD0D
+#define IR_RX_C_COUNT_H 0xFD0E
+#define IR_SUSPEND_CTRL 0xFD10
+#define IR_ERR_TOL_CTRL 0xFD11
+#define IR_UNIT_LEN 0xFD12
+#define IR_ERR_TOL_LEN 0xFD13
+#define IR_MAX_H_TOL_LEN 0xFD14
+#define IR_MAX_L_TOL_LEN 0xFD15
+#define IR_MASK_CTRL 0xFD16
+#define IR_MASK_DATA 0xFD17
+#define IR_RES_MASK_ADDR 0xFD18
+#define IR_RES_MASK_T_LEN 0xFD19
+
+#endif
diff --git a/drivers/media/usb/dvb-usb-v2/usb_urb.c b/drivers/media/usb/dvb-usb-v2/usb_urb.c
new file mode 100644
index 000000000000..5989b6590377
--- /dev/null
+++ b/drivers/media/usb/dvb-usb-v2/usb_urb.c
@@ -0,0 +1,358 @@
+/* usb-urb.c is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file keeps functions for initializing and handling the
+ * BULK and ISOC USB data transfers in a generic way.
+ * Can be used for DVB-only and also, that's the plan, for
+ * Hybrid USB devices (analog and DVB).
+ */
+#include "dvb_usb_common.h"
+
+/* URB stuff for streaming */
+
+int usb_urb_reconfig(struct usb_data_stream *stream,
+ struct usb_data_stream_properties *props);
+
+static void usb_urb_complete(struct urb *urb)
+{
+ struct usb_data_stream *stream = urb->context;
+ int ptype = usb_pipetype(urb->pipe);
+ int i;
+ u8 *b;
+
+ dev_dbg_ratelimited(&stream->udev->dev, "%s: %s urb completed " \
+ "status=%d length=%d/%d pack_num=%d errors=%d\n",
+ __func__, ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk",
+ urb->status, urb->actual_length,
+ urb->transfer_buffer_length,
+ urb->number_of_packets, urb->error_count);
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dev_dbg_ratelimited(&stream->udev->dev,
+ "%s: urb completition failed=%d\n",
+ __func__, urb->status);
+ break;
+ }
+
+ b = (u8 *) urb->transfer_buffer;
+ switch (ptype) {
+ case PIPE_ISOCHRONOUS:
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status != 0)
+ dev_dbg(&stream->udev->dev, "%s: iso frame " \
+ "descriptor has an error=%d\n",
+ __func__,
+ urb->iso_frame_desc[i].status);
+ else if (urb->iso_frame_desc[i].actual_length > 0)
+ stream->complete(stream,
+ b + urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ break;
+ case PIPE_BULK:
+ if (urb->actual_length > 0)
+ stream->complete(stream, b, urb->actual_length);
+ break;
+ default:
+ dev_err(&stream->udev->dev, "%s: unknown endpoint type in " \
+ "completition handler\n", KBUILD_MODNAME);
+ return;
+ }
+ usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+int usb_urb_killv2(struct usb_data_stream *stream)
+{
+ int i;
+ for (i = 0; i < stream->urbs_submitted; i++) {
+ dev_dbg(&stream->udev->dev, "%s: kill urb=%d\n", __func__, i);
+ /* stop the URB */
+ usb_kill_urb(stream->urb_list[i]);
+ }
+ stream->urbs_submitted = 0;
+ return 0;
+}
+
+int usb_urb_submitv2(struct usb_data_stream *stream,
+ struct usb_data_stream_properties *props)
+{
+ int i, ret;
+
+ if (props) {
+ ret = usb_urb_reconfig(stream, props);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < stream->urbs_initialized; i++) {
+ dev_dbg(&stream->udev->dev, "%s: submit urb=%d\n", __func__, i);
+ ret = usb_submit_urb(stream->urb_list[i], GFP_ATOMIC);
+ if (ret) {
+ dev_err(&stream->udev->dev, "%s: could not submit " \
+ "urb no. %d - get them all back\n",
+ KBUILD_MODNAME, i);
+ usb_urb_killv2(stream);
+ return ret;
+ }
+ stream->urbs_submitted++;
+ }
+ return 0;
+}
+
+int usb_urb_free_urbs(struct usb_data_stream *stream)
+{
+ int i;
+
+ usb_urb_killv2(stream);
+
+ for (i = stream->urbs_initialized - 1; i >= 0; i--) {
+ if (stream->urb_list[i]) {
+ dev_dbg(&stream->udev->dev, "%s: free urb=%d\n",
+ __func__, i);
+ /* free the URBs */
+ usb_free_urb(stream->urb_list[i]);
+ }
+ }
+ stream->urbs_initialized = 0;
+
+ return 0;
+}
+
+static int usb_urb_alloc_bulk_urbs(struct usb_data_stream *stream)
+{
+ int i, j;
+
+ /* allocate the URBs */
+ for (i = 0; i < stream->props.count; i++) {
+ dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+ stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!stream->urb_list[i]) {
+ dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
+ for (j = 0; j < i; j++)
+ usb_free_urb(stream->urb_list[j]);
+ return -ENOMEM;
+ }
+ usb_fill_bulk_urb(stream->urb_list[i],
+ stream->udev,
+ usb_rcvbulkpipe(stream->udev,
+ stream->props.endpoint),
+ stream->buf_list[i],
+ stream->props.u.bulk.buffersize,
+ usb_urb_complete, stream);
+
+ stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ stream->urb_list[i]->transfer_dma = stream->dma_addr[i];
+ stream->urbs_initialized++;
+ }
+ return 0;
+}
+
+static int usb_urb_alloc_isoc_urbs(struct usb_data_stream *stream)
+{
+ int i, j;
+
+ /* allocate the URBs */
+ for (i = 0; i < stream->props.count; i++) {
+ struct urb *urb;
+ int frame_offset = 0;
+ dev_dbg(&stream->udev->dev, "%s: alloc urb=%d\n", __func__, i);
+ stream->urb_list[i] = usb_alloc_urb(
+ stream->props.u.isoc.framesperurb, GFP_ATOMIC);
+ if (!stream->urb_list[i]) {
+ dev_dbg(&stream->udev->dev, "%s: failed\n", __func__);
+ for (j = 0; j < i; j++)
+ usb_free_urb(stream->urb_list[j]);
+ return -ENOMEM;
+ }
+
+ urb = stream->urb_list[i];
+
+ urb->dev = stream->udev;
+ urb->context = stream;
+ urb->complete = usb_urb_complete;
+ urb->pipe = usb_rcvisocpipe(stream->udev,
+ stream->props.endpoint);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = stream->props.u.isoc.interval;
+ urb->number_of_packets = stream->props.u.isoc.framesperurb;
+ urb->transfer_buffer_length = stream->props.u.isoc.framesize *
+ stream->props.u.isoc.framesperurb;
+ urb->transfer_buffer = stream->buf_list[i];
+ urb->transfer_dma = stream->dma_addr[i];
+
+ for (j = 0; j < stream->props.u.isoc.framesperurb; j++) {
+ urb->iso_frame_desc[j].offset = frame_offset;
+ urb->iso_frame_desc[j].length =
+ stream->props.u.isoc.framesize;
+ frame_offset += stream->props.u.isoc.framesize;
+ }
+
+ stream->urbs_initialized++;
+ }
+ return 0;
+}
+
+int usb_free_stream_buffers(struct usb_data_stream *stream)
+{
+ if (stream->state & USB_STATE_URB_BUF) {
+ while (stream->buf_num) {
+ stream->buf_num--;
+ dev_dbg(&stream->udev->dev, "%s: free buf=%d\n",
+ __func__, stream->buf_num);
+ usb_free_coherent(stream->udev, stream->buf_size,
+ stream->buf_list[stream->buf_num],
+ stream->dma_addr[stream->buf_num]);
+ }
+ }
+
+ stream->state &= ~USB_STATE_URB_BUF;
+
+ return 0;
+}
+
+int usb_alloc_stream_buffers(struct usb_data_stream *stream, int num,
+ unsigned long size)
+{
+ stream->buf_num = 0;
+ stream->buf_size = size;
+
+ dev_dbg(&stream->udev->dev, "%s: all in all I will use %lu bytes for " \
+ "streaming\n", __func__, num * size);
+
+ for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) {
+ stream->buf_list[stream->buf_num] = usb_alloc_coherent(
+ stream->udev, size, GFP_ATOMIC,
+ &stream->dma_addr[stream->buf_num]);
+ if (!stream->buf_list[stream->buf_num]) {
+ dev_dbg(&stream->udev->dev, "%s: alloc buf=%d failed\n",
+ __func__, stream->buf_num);
+ usb_free_stream_buffers(stream);
+ return -ENOMEM;
+ }
+
+ dev_dbg(&stream->udev->dev, "%s: alloc buf=%d %p (dma %llu)\n",
+ __func__, stream->buf_num,
+ stream->buf_list[stream->buf_num],
+ (long long)stream->dma_addr[stream->buf_num]);
+ memset(stream->buf_list[stream->buf_num], 0, size);
+ stream->state |= USB_STATE_URB_BUF;
+ }
+
+ return 0;
+}
+
+int usb_urb_reconfig(struct usb_data_stream *stream,
+ struct usb_data_stream_properties *props)
+{
+ int buf_size;
+
+ if (!props)
+ return 0;
+
+ /* check allocated buffers are large enough for the request */
+ if (props->type == USB_BULK) {
+ buf_size = stream->props.u.bulk.buffersize;
+ } else if (props->type == USB_ISOC) {
+ buf_size = props->u.isoc.framesize * props->u.isoc.framesperurb;
+ } else {
+ dev_err(&stream->udev->dev, "%s: invalid endpoint type=%d\n",
+ KBUILD_MODNAME, props->type);
+ return -EINVAL;
+ }
+
+ if (stream->buf_num < props->count || stream->buf_size < buf_size) {
+ dev_err(&stream->udev->dev, "%s: cannot reconfigure as " \
+ "allocated buffers are too small\n",
+ KBUILD_MODNAME);
+ return -EINVAL;
+ }
+
+ /* check if all fields are same */
+ if (stream->props.type == props->type &&
+ stream->props.count == props->count &&
+ stream->props.endpoint == props->endpoint) {
+ if (props->type == USB_BULK &&
+ props->u.bulk.buffersize ==
+ stream->props.u.bulk.buffersize)
+ return 0;
+ else if (props->type == USB_ISOC &&
+ props->u.isoc.framesperurb ==
+ stream->props.u.isoc.framesperurb &&
+ props->u.isoc.framesize ==
+ stream->props.u.isoc.framesize &&
+ props->u.isoc.interval ==
+ stream->props.u.isoc.interval)
+ return 0;
+ }
+
+ dev_dbg(&stream->udev->dev, "%s: re-alloc urbs\n", __func__);
+
+ usb_urb_free_urbs(stream);
+ memcpy(&stream->props, props, sizeof(*props));
+ if (props->type == USB_BULK)
+ return usb_urb_alloc_bulk_urbs(stream);
+ else if (props->type == USB_ISOC)
+ return usb_urb_alloc_isoc_urbs(stream);
+
+ return 0;
+}
+
+int usb_urb_initv2(struct usb_data_stream *stream,
+ const struct usb_data_stream_properties *props)
+{
+ int ret;
+
+ if (!stream || !props)
+ return -EINVAL;
+
+ memcpy(&stream->props, props, sizeof(*props));
+
+ if (!stream->complete) {
+ dev_err(&stream->udev->dev, "%s: there is no data callback - " \
+ "this doesn't make sense\n", KBUILD_MODNAME);
+ return -EINVAL;
+ }
+
+ switch (stream->props.type) {
+ case USB_BULK:
+ ret = usb_alloc_stream_buffers(stream, stream->props.count,
+ stream->props.u.bulk.buffersize);
+ if (ret < 0)
+ return ret;
+
+ return usb_urb_alloc_bulk_urbs(stream);
+ case USB_ISOC:
+ ret = usb_alloc_stream_buffers(stream, stream->props.count,
+ stream->props.u.isoc.framesize *
+ stream->props.u.isoc.framesperurb);
+ if (ret < 0)
+ return ret;
+
+ return usb_urb_alloc_isoc_urbs(stream);
+ default:
+ dev_err(&stream->udev->dev, "%s: unknown urb-type for data " \
+ "transfer\n", KBUILD_MODNAME);
+ return -EINVAL;
+ }
+}
+
+int usb_urb_exitv2(struct usb_data_stream *stream)
+{
+ usb_urb_free_urbs(stream);
+ usb_free_stream_buffers(stream);
+
+ return 0;
+}
diff --git a/drivers/media/usb/dvb-usb/Kconfig b/drivers/media/usb/dvb-usb/Kconfig
new file mode 100644
index 000000000000..fa0b2931d305
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/Kconfig
@@ -0,0 +1,313 @@
+config DVB_USB
+ tristate "Support for various USB DVB devices"
+ depends on DVB_CORE && USB && I2C && RC_CORE
+ help
+ By enabling this you will be able to choose the various supported
+ USB1.1 and USB2.0 DVB devices.
+
+ Almost every USB device needs a firmware, please look into
+ <file:Documentation/dvb/README.dvb-usb>.
+
+ For a complete list of supported USB devices see the LinuxTV DVB Wiki:
+ <http://www.linuxtv.org/wiki/index.php/DVB_USB>
+
+ Say Y if you own a USB DVB device.
+
+config DVB_USB_DEBUG
+ bool "Enable extended debug support for all DVB-USB devices"
+ depends on DVB_USB
+ help
+ Say Y if you want to enable debugging. See modinfo dvb-usb (and the
+ appropriate drivers) for debug levels.
+
+config DVB_USB_A800
+ tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
+ depends on DVB_USB
+ select DVB_DIB3000MC
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
+
+config DVB_USB_DIBUSB_MB
+ tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)"
+ depends on DVB_USB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB3000MB
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
+ DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
+
+ For an up-to-date list of devices supported by this driver, have a look
+ on the Linux-DVB Wiki at www.linuxtv.org.
+
+ Say Y if you own such a device and want to use it. You should build it as
+ a module.
+
+config DVB_USB_DIBUSB_MB_FAULTY
+ bool "Support faulty USB IDs"
+ depends on DVB_USB_DIBUSB_MB
+ help
+ Support for faulty USB IDs due to an invalid EEPROM on some Artec devices.
+
+config DVB_USB_DIBUSB_MC
+ tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
+ depends on DVB_USB
+ select DVB_DIB3000MC
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for USB2.0 DVB-T receivers based on reference designs made by
+ DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
+
+ For an up-to-date list of devices supported by this driver, have a look
+ on the Linux-DVB Wiki at www.linuxtv.org.
+
+ Say Y if you own such a device and want to use it. You should build it as
+ a module.
+
+config DVB_USB_DIB0700
+ tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
+ depends on DVB_USB
+ select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB7000M if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB8000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB3000MC if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_DIB0090 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2266 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC4000 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
+ USB bridge is also present in devices having the DiB7700 DVB-T-USB
+ silicon. This chip can be found in devices offered by Hauppauge,
+ Avermedia and other big and small companies.
+
+ For an up-to-date list of devices supported by this driver, have a look
+ on the LinuxTV Wiki at www.linuxtv.org.
+
+ Say Y if you own such a device and want to use it. You should build it as
+ a module.
+
+config DVB_USB_UMT_010
+ tristate "HanfTek UMT-010 DVB-T USB2.0 support"
+ depends on DVB_USB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB3000MC
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
+
+config DVB_USB_CXUSB
+ tristate "Conexant USB2.0 hybrid reference design support"
+ depends on DVB_USB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ATBM8830 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LGS8GXX if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MAX2165 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Conexant USB2.0 hybrid reference design.
+ Currently, only DVB and ATSC modes are supported, analog mode
+ shall be added in the future. Devices that require this module:
+
+ Medion MD95700 hybrid USB2.0 device.
+ DViCO FusionHDTV (Bluebird) USB2.0 devices
+
+config DVB_USB_M920X
+ tristate "Uli m920x DVB-T USB2.0 support"
+ depends on DVB_USB
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
+ Currently, only devices with a product id of
+ "DTV USB MINI" (in cold state) are supported.
+ Firmware required.
+
+config DVB_USB_DIGITV
+ tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
+ depends on DVB_USB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver.
+
+config DVB_USB_VP7045
+ tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support"
+ depends on DVB_USB
+ help
+ Say Y here to support the
+
+ TwinhanDTV Alpha (stick) (VP-7045),
+ TwinhanDTV MagicBox II (VP-7046),
+ DigitalNow TinyUSB 2 DVB-t,
+ DigitalRise USB 2.0 Ter (Beetle) and
+ TYPHOON DVB-T USB DRIVE
+
+ DVB-T USB2.0 receivers.
+
+config DVB_USB_VP702X
+ tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support"
+ depends on DVB_USB
+ help
+ Say Y here to support the
+
+ TwinhanDTV StarBox,
+ DigitalRise USB Starbox and
+ TYPHOON DVB-S USB 2.0 BOX
+
+ DVB-S USB2.0 receivers.
+
+config DVB_USB_GP8PSK
+ tristate "GENPIX 8PSK->USB module support"
+ depends on DVB_USB
+ help
+ Say Y here to support the
+ GENPIX 8psk module
+
+ DVB-S USB2.0 receivers.
+
+config DVB_USB_NOVA_T_USB2
+ tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
+ depends on DVB_USB
+ select DVB_DIB3000MC
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
+
+config DVB_USB_TTUSB2
+ tristate "Pinnacle 400e DVB-S USB2.0 support"
+ depends on DVB_USB
+ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The
+ firmware protocol used by this module is similar to the one used by the
+ old ttusb-driver - that's why the module is called dvb-usb-ttusb2.
+
+config DVB_USB_DTT200U
+ tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)"
+ depends on DVB_USB
+ help
+ Say Y here to support the WideView/Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver.
+
+ The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan).
+
+ The WT-220U and its clones are pen-sized.
+
+config DVB_USB_OPERA1
+ tristate "Opera1 DVB-S USB2.0 receiver"
+ depends on DVB_USB
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Opera DVB-S USB2.0 receiver.
+
+config DVB_USB_AF9005
+ tristate "Afatech AF9005 DVB-T USB1.1 support"
+ depends on DVB_USB
+ select MEDIA_TUNER_MT2060 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
+ and the TerraTec Cinergy T USB XE (Rev.1)
+
+config DVB_USB_AF9005_REMOTE
+ tristate "Afatech AF9005 default remote control support"
+ depends on DVB_USB_AF9005
+ help
+ Say Y here to support the default remote control decoding for the
+ Afatech AF9005 based receiver.
+
+config DVB_USB_PCTV452E
+ tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600"
+ depends on DVB_USB
+ select TTPCI_EEPROM
+ select DVB_LNBP22 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for external USB adapter designed by Pinnacle,
+ shipped under the brand name 'PCTV HDTV Pro USB'.
+ Also supports TT Connect S2-3600/3650 cards.
+ Say Y if you own such a device and want to use it.
+
+config DVB_USB_DW2102
+ tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
+ depends on DVB_USB
+ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0
+ receivers.
+
+config DVB_USB_CINERGY_T2
+ tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
+ depends on DVB_USB
+ help
+ Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers
+
+ Say Y if you own such a device and want to use it.
+
+config DVB_USB_DTV5100
+ tristate "AME DTV-5100 USB2.0 DVB-T support"
+ depends on DVB_USB
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QT1010 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_FRIIO
+ tristate "Friio ISDB-T USB2.0 Receiver support"
+ depends on DVB_USB
+ help
+ Say Y here to support the Japanese DTV receiver Friio.
+
+config DVB_USB_AZ6027
+ tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
+ depends on DVB_USB
+ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the AZ6027 device
+
+config DVB_USB_TECHNISAT_USB2
+ tristate "Technisat DVB-S/S2 USB2.0 support"
+ depends on DVB_USB
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Say Y here to support the Technisat USB2 DVB-S/S2 device
diff --git a/drivers/media/usb/dvb-usb/Makefile b/drivers/media/usb/dvb-usb/Makefile
new file mode 100644
index 000000000000..acdd1efd4e74
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/Makefile
@@ -0,0 +1,83 @@
+dvb-usb-objs += dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o
+dvb-usb-objs += dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o
+obj-$(CONFIG_DVB_USB) += dvb-usb.o
+
+dvb-usb-vp7045-objs := vp7045.o vp7045-fe.o
+obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o
+
+dvb-usb-vp702x-objs := vp702x.o vp702x-fe.o
+obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o
+
+dvb-usb-gp8psk-objs := gp8psk.o gp8psk-fe.o
+obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o
+
+dvb-usb-dtt200u-objs := dtt200u.o dtt200u-fe.o
+obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o
+
+dvb-usb-dibusb-common-objs := dibusb-common.o
+
+dvb-usb-a800-objs := a800.o
+obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o
+
+dvb-usb-dibusb-mb-objs := dibusb-mb.o
+obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o
+
+dvb-usb-dibusb-mc-objs := dibusb-mc.o
+obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o
+
+dvb-usb-nova-t-usb2-objs := nova-t-usb2.o
+obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o
+
+dvb-usb-umt-010-objs := umt-010.o
+obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o
+
+dvb-usb-m920x-objs := m920x.o
+obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o
+
+dvb-usb-digitv-objs := digitv.o
+obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o
+
+dvb-usb-cxusb-objs := cxusb.o
+obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o
+
+dvb-usb-ttusb2-objs := ttusb2.o
+obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o
+
+dvb-usb-dib0700-objs := dib0700_core.o dib0700_devices.o
+obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
+
+dvb-usb-opera-objs := opera1.o
+obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
+
+dvb-usb-af9005-objs := af9005.o af9005-fe.o
+obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
+
+dvb-usb-af9005-remote-objs := af9005-remote.o
+obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
+
+dvb-usb-pctv452e-objs := pctv452e.o
+obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o
+
+dvb-usb-dw2102-objs := dw2102.o
+obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
+
+dvb-usb-dtv5100-objs := dtv5100.o
+obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
+
+dvb-usb-cinergyT2-objs := cinergyT2-core.o cinergyT2-fe.o
+obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
+
+dvb-usb-friio-objs := friio.o friio-fe.o
+obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
+
+dvb-usb-az6027-objs := az6027.o
+obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
+
+dvb-usb-technisat-usb2-objs := technisat-usb2.o
+obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends/
+# due to tuner-xc3028
+ccflags-y += -I$(srctree)/drivers/media/tuners
+ccflags-y += -I$(srctree)/drivers/media/pci/ttpci
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/usb/dvb-usb/a800.c
index 8d7fef84afd8..8d7fef84afd8 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/usb/dvb-usb/a800.c
diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/usb/dvb-usb/af9005-fe.c
index 740f3f496f12..740f3f496f12 100644
--- a/drivers/media/dvb/dvb-usb/af9005-fe.c
+++ b/drivers/media/usb/dvb-usb/af9005-fe.c
diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/usb/dvb-usb/af9005-remote.c
index 7e3961d0db6b..7e3961d0db6b 100644
--- a/drivers/media/dvb/dvb-usb/af9005-remote.c
+++ b/drivers/media/usb/dvb-usb/af9005-remote.c
diff --git a/drivers/media/dvb/dvb-usb/af9005-script.h b/drivers/media/usb/dvb-usb/af9005-script.h
index 4d69045426dd..4d69045426dd 100644
--- a/drivers/media/dvb/dvb-usb/af9005-script.h
+++ b/drivers/media/usb/dvb-usb/af9005-script.h
diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/usb/dvb-usb/af9005.c
index af176b6ce738..af176b6ce738 100644
--- a/drivers/media/dvb/dvb-usb/af9005.c
+++ b/drivers/media/usb/dvb-usb/af9005.c
diff --git a/drivers/media/dvb/dvb-usb/af9005.h b/drivers/media/usb/dvb-usb/af9005.h
index 6a2bf3de8456..6a2bf3de8456 100644
--- a/drivers/media/dvb/dvb-usb/af9005.h
+++ b/drivers/media/usb/dvb-usb/af9005.h
diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/usb/dvb-usb/az6027.c
index 5e45ae605427..5e45ae605427 100644
--- a/drivers/media/dvb/dvb-usb/az6027.c
+++ b/drivers/media/usb/dvb-usb/az6027.c
diff --git a/drivers/media/dvb/dvb-usb/az6027.h b/drivers/media/usb/dvb-usb/az6027.h
index f3afe17f3f3d..f3afe17f3f3d 100644
--- a/drivers/media/dvb/dvb-usb/az6027.h
+++ b/drivers/media/usb/dvb-usb/az6027.h
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/usb/dvb-usb/cinergyT2-core.c
index 0a98548ecd17..0a98548ecd17 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c
+++ b/drivers/media/usb/dvb-usb/cinergyT2-core.c
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/usb/dvb-usb/cinergyT2-fe.c
index 1efc028a76c9..1efc028a76c9 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c
+++ b/drivers/media/usb/dvb-usb/cinergyT2-fe.c
diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/usb/dvb-usb/cinergyT2.h
index 84efe03771eb..84efe03771eb 100644
--- a/drivers/media/dvb/dvb-usb/cinergyT2.h
+++ b/drivers/media/usb/dvb-usb/cinergyT2.h
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c
index 3940bb0f9ef6..3940bb0f9ef6 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/usb/dvb-usb/cxusb.c
diff --git a/drivers/media/dvb/dvb-usb/cxusb.h b/drivers/media/usb/dvb-usb/cxusb.h
index 1a51eafd31b9..1a51eafd31b9 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.h
+++ b/drivers/media/usb/dvb-usb/cxusb.h
diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/usb/dvb-usb/dib0700.h
index 7de125c0b36f..7de125c0b36f 100644
--- a/drivers/media/dvb/dvb-usb/dib0700.h
+++ b/drivers/media/usb/dvb-usb/dib0700.h
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
new file mode 100644
index 000000000000..ef87229de6af
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -0,0 +1,845 @@
+/* Linux driver for devices based on the DiBcom DiB0700 USB bridge
+ *
+ * This program is free software; 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.
+ *
+ * Copyright (C) 2005-6 DiBcom, SA
+ */
+#include "dib0700.h"
+
+/* debug */
+int dvb_usb_dib0700_debug;
+module_param_named(debug,dvb_usb_dib0700_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS);
+
+static int nb_packet_buffer_size = 21;
+module_param(nb_packet_buffer_size, int, 0644);
+MODULE_PARM_DESC(nb_packet_buffer_size,
+ "Set the dib0700 driver data buffer size. This parameter "
+ "corresponds to the number of TS packets. The actual size of "
+ "the data buffer corresponds to this parameter "
+ "multiplied by 188 (default: 21)");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+
+int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
+ u32 *romversion, u32 *ramversion, u32 *fwtype)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ st->buf, 16, USB_CTRL_GET_TIMEOUT);
+ if (hwversion != NULL)
+ *hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) |
+ (st->buf[2] << 8) | st->buf[3];
+ if (romversion != NULL)
+ *romversion = (st->buf[4] << 24) | (st->buf[5] << 16) |
+ (st->buf[6] << 8) | st->buf[7];
+ if (ramversion != NULL)
+ *ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) |
+ (st->buf[10] << 8) | st->buf[11];
+ if (fwtype != NULL)
+ *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) |
+ (st->buf[14] << 8) | st->buf[15];
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+/* expecting rx buffer: request data[0] data[1] ... data[2] */
+static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen)
+{
+ int status;
+
+ deb_data(">>> ");
+ debug_dump(tx, txlen, deb_data);
+
+ status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0),
+ tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (status != txlen)
+ deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen);
+
+ return status < 0 ? status : 0;
+}
+
+/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */
+int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen)
+{
+ u16 index, value;
+ int status;
+
+ if (txlen < 2) {
+ err("tx buffer length is smaller than 2. Makes no sense.");
+ return -EINVAL;
+ }
+ if (txlen > 4) {
+ err("tx buffer length is larger than 4. Not supported.");
+ return -EINVAL;
+ }
+
+ deb_data(">>> ");
+ debug_dump(tx,txlen,deb_data);
+
+ value = ((txlen - 2) << 8) | tx[1];
+ index = 0;
+ if (txlen > 2)
+ index |= (tx[2] << 8);
+ if (txlen > 3)
+ index |= tx[3];
+
+ status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0],
+ USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (status < 0)
+ deb_info("ep 0 read error (status = %d)\n",status);
+
+ deb_data("<<< ");
+ debug_dump(rx, rxlen, deb_data);
+
+ return status; /* length in case of success */
+}
+
+int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_GPIO;
+ st->buf[1] = gpio;
+ st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (st->fw_version >= 0x10201) {
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_USB_XFER_LEN;
+ st->buf[1] = (nb_ts_packets >> 8) & 0xff;
+ st->buf[2] = nb_ts_packets & 0xff;
+
+ deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+ mutex_unlock(&d->usb_mutex);
+ } else {
+ deb_info("this firmware does not allow to change the USB xfer len\n");
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/*
+ * I2C master xfer function (supported in 1.20 firmware)
+ */
+static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ /* The new i2c firmware messages are more reliable and in particular
+ properly support i2c read calls not preceded by a write */
+
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+ uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */
+ uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */
+ uint8_t en_start = 0;
+ uint8_t en_stop = 0;
+ int result, i;
+
+ /* 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 -EINTR;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0) {
+ /* First message in the transaction */
+ en_start = 1;
+ } else if (!(msg[i].flags & I2C_M_NOSTART)) {
+ /* Device supports repeated-start */
+ en_start = 1;
+ } else {
+ /* Not the first packet and device doesn't support
+ repeated start */
+ en_start = 0;
+ }
+ if (i == (num - 1)) {
+ /* Last message in the transaction */
+ en_stop = 1;
+ }
+
+ if (msg[i].flags & I2C_M_RD) {
+ /* Read request */
+ u16 index, value;
+ uint8_t i2c_dest;
+
+ i2c_dest = (msg[i].addr << 1);
+ value = ((en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F)) << 8 | i2c_dest;
+ /* I2C ctrl + FE bus; */
+ index = ((gen_mode << 6) & 0xC0) |
+ ((bus_mode << 4) & 0x30);
+
+ result = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_READ,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, msg[i].buf,
+ msg[i].len,
+ USB_CTRL_GET_TIMEOUT);
+ if (result < 0) {
+ deb_info("i2c read error (status = %d)\n", result);
+ break;
+ }
+
+ deb_data("<<< ");
+ debug_dump(msg[i].buf, msg[i].len, deb_data);
+
+ } else {
+ /* Write request */
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ mutex_unlock(&d->i2c_mutex);
+ return -EINTR;
+ }
+ st->buf[0] = REQUEST_NEW_I2C_WRITE;
+ st->buf[1] = msg[i].addr << 1;
+ st->buf[2] = (en_start << 7) | (en_stop << 6) |
+ (msg[i].len & 0x3F);
+ /* I2C ctrl + FE bus; */
+ st->buf[3] = ((gen_mode << 6) & 0xC0) |
+ ((bus_mode << 4) & 0x30);
+ /* The Actual i2c payload */
+ memcpy(&st->buf[4], msg[i].buf, msg[i].len);
+
+ deb_data(">>> ");
+ debug_dump(st->buf, msg[i].len + 4, deb_data);
+
+ result = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_WRITE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ 0, 0, st->buf, msg[i].len + 4,
+ USB_CTRL_GET_TIMEOUT);
+ mutex_unlock(&d->usb_mutex);
+ if (result < 0) {
+ deb_info("i2c write error (status = %d)\n", result);
+ break;
+ }
+ }
+ }
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+/*
+ * I2C master xfer function (pre-1.20 firmware)
+ */
+static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+ int i,len;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EINTR;
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ mutex_unlock(&d->i2c_mutex);
+ return -EINTR;
+ }
+
+ for (i = 0; i < num; i++) {
+ /* fill in the address */
+ st->buf[1] = msg[i].addr << 1;
+ /* fill the buffer */
+ memcpy(&st->buf[2], msg[i].buf, msg[i].len);
+
+ /* write/read request */
+ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
+ st->buf[0] = REQUEST_I2C_READ;
+ st->buf[1] |= 1;
+
+ /* special thing in the current firmware: when length is zero the read-failed */
+ len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2,
+ msg[i+1].buf, msg[i+1].len);
+ if (len <= 0) {
+ deb_info("I2C read failed on address 0x%02x\n",
+ msg[i].addr);
+ break;
+ }
+
+ msg[i+1].len = len;
+
+ i++;
+ } else {
+ st->buf[0] = REQUEST_I2C_WRITE;
+ if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0)
+ break;
+ }
+ }
+ mutex_unlock(&d->usb_mutex);
+ mutex_unlock(&d->i2c_mutex);
+
+ return i;
+}
+
+static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct dib0700_state *st = d->priv;
+
+ if (st->fw_use_new_i2c_api == 1) {
+ /* User running at least fw 1.20 */
+ return dib0700_i2c_xfer_new(adap, msg, num);
+ } else {
+ /* Use legacy calls */
+ return dib0700_i2c_xfer_legacy(adap, msg, num);
+ }
+}
+
+static u32 dib0700_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+struct i2c_algorithm dib0700_i2c_algo = {
+ .master_xfer = dib0700_i2c_xfer,
+ .functionality = dib0700_i2c_func,
+};
+
+int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc, int *cold)
+{
+ s16 ret;
+ u8 *b;
+
+ b = kmalloc(16, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT);
+
+ deb_info("FW GET_VERSION length: %d\n",ret);
+
+ *cold = ret <= 0;
+ deb_info("cold: %d\n", *cold);
+
+ kfree(b);
+ return 0;
+}
+
+static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
+ u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv,
+ u16 pll_loopdiv, u16 free_div, u16 dsuScaler)
+{
+ struct dib0700_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_CLOCK;
+ st->buf[1] = (en_pll << 7) | (pll_src << 6) |
+ (pll_range << 5) | (clock_gpio3 << 4);
+ st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */
+ st->buf[3] = pll_prediv & 0xff; /* LSB */
+ st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */
+ st->buf[5] = pll_loopdiv & 0xff; /* LSB */
+ st->buf[6] = (free_div >> 8) & 0xff; /* MSB */
+ st->buf[7] = free_div & 0xff; /* LSB */
+ st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */
+ st->buf[9] = dsuScaler & 0xff; /* LSB */
+
+ ret = dib0700_ctrl_wr(d, st->buf, 10);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
+}
+
+int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
+{
+ struct dib0700_state *st = d->priv;
+ u16 divider;
+ int ret;
+
+ if (scl_kHz == 0)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_I2C_PARAM;
+ divider = (u16) (30000 / scl_kHz);
+ st->buf[1] = 0;
+ st->buf[2] = (u8) (divider >> 8);
+ st->buf[3] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz);
+ st->buf[4] = (u8) (divider >> 8);
+ st->buf[5] = (u8) (divider & 0xff);
+ divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */
+ st->buf[6] = (u8) (divider >> 8);
+ st->buf[7] = (u8) (divider & 0xff);
+
+ deb_info("setting I2C speed: %04x %04x %04x (%d kHz).",
+ (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) |
+ st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz);
+
+ ret = dib0700_ctrl_wr(d, st->buf, 8);
+ mutex_unlock(&d->usb_mutex);
+
+ return ret;
+}
+
+
+int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3)
+{
+ switch (clk_MHz) {
+ case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break;
+ default: return -EINVAL;
+ }
+ return 0;
+}
+
+static int dib0700_jumpram(struct usb_device *udev, u32 address)
+{
+ int ret = 0, actlen;
+ u8 *buf;
+
+ buf = kmalloc(8, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ buf[0] = REQUEST_JUMPRAM;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = (address >> 24) & 0xff;
+ buf[5] = (address >> 16) & 0xff;
+ buf[6] = (address >> 8) & 0xff;
+ buf[7] = address & 0xff;
+
+ if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) {
+ deb_fw("jumpram to 0x%x failed\n",address);
+ goto out;
+ }
+ if (actlen != 8) {
+ deb_fw("jumpram to 0x%x failed\n",address);
+ ret = -EIO;
+ goto out;
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw)
+{
+ struct hexline hx;
+ int pos = 0, ret, act_len, i, adap_num;
+ u8 *buf;
+ u32 fw_version;
+
+ buf = kmalloc(260, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) {
+ deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n",
+ hx.addr, hx.len, hx.chk);
+
+ buf[0] = hx.len;
+ buf[1] = (hx.addr >> 8) & 0xff;
+ buf[2] = hx.addr & 0xff;
+ buf[3] = hx.type;
+ memcpy(&buf[4],hx.data,hx.len);
+ buf[4+hx.len] = hx.chk;
+
+ ret = usb_bulk_msg(udev,
+ usb_sndbulkpipe(udev, 0x01),
+ buf,
+ hx.len + 5,
+ &act_len,
+ 1000);
+
+ if (ret < 0) {
+ err("firmware download failed at %d with %d",pos,ret);
+ goto out;
+ }
+ }
+
+ if (ret == 0) {
+ /* start the firmware */
+ if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) {
+ info("firmware started successfully.");
+ msleep(500);
+ }
+ } else
+ ret = -EIO;
+
+ /* the number of ts packet has to be at least 1 */
+ if (nb_packet_buffer_size < 1)
+ nb_packet_buffer_size = 1;
+
+ /* get the fimware version */
+ usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_GET_VERSION,
+ USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
+ buf, 16, USB_CTRL_GET_TIMEOUT);
+ fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
+
+ /* set the buffer size - DVB-USB is allocating URB buffers
+ * only after the firwmare download was successful */
+ for (i = 0; i < dib0700_device_count; i++) {
+ for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
+ adap_num++) {
+ if (fw_version >= 0x10201) {
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size;
+ } else {
+ /* for fw version older than 1.20.1,
+ * the buffersize has to be n times 512 */
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512;
+ if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512)
+ dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512;
+ }
+ }
+ }
+out:
+ kfree(buf);
+ return ret;
+}
+
+int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dib0700_state *st = adap->dev->priv;
+ int ret;
+
+ if ((onoff != 0) && (st->fw_version >= 0x10201)) {
+ /* for firmware later than 1.20.1,
+ * the USB xfer length can be set */
+ ret = dib0700_set_usb_xfer_len(adap->dev,
+ st->nb_packet_buffer_size);
+ if (ret < 0) {
+ deb_info("can not set the USB xfer len\n");
+ return ret;
+ }
+ }
+
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_ENABLE_VIDEO;
+ /* this bit gives a kind of command,
+ * rather than enabling something or not */
+ st->buf[1] = (onoff << 4) | 0x00;
+
+ if (st->disable_streaming_master_mode == 1)
+ st->buf[2] = 0x00;
+ else
+ st->buf[2] = 0x01 << 4; /* Master mode */
+
+ st->buf[3] = 0x00;
+
+ deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id);
+
+ st->channel_state &= ~0x3;
+ if ((adap->fe_adap[0].stream.props.endpoint != 2)
+ && (adap->fe_adap[0].stream.props.endpoint != 3)) {
+ deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].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->fe_adap[0].stream.props.endpoint-2);
+ else
+ st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint);
+ }
+
+ st->buf[2] |= st->channel_state;
+
+ deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]);
+
+ ret = dib0700_ctrl_wr(adap->dev, st->buf, 4);
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
+{
+ struct dvb_usb_device *d = rc->priv;
+ struct dib0700_state *st = d->priv;
+ int new_proto, ret;
+
+ if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
+ err("could not acquire lock");
+ return -EINTR;
+ }
+
+ st->buf[0] = REQUEST_SET_RC;
+ st->buf[1] = 0;
+ st->buf[2] = 0;
+
+ /* Set the IR mode */
+ if (rc_type == RC_TYPE_RC5)
+ new_proto = 1;
+ else if (rc_type == RC_TYPE_NEC)
+ new_proto = 0;
+ else if (rc_type == RC_TYPE_RC6) {
+ if (st->fw_version < 0x10200) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ new_proto = 2;
+ } else {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ st->buf[1] = new_proto;
+
+ ret = dib0700_ctrl_wr(d, st->buf, 3);
+ if (ret < 0) {
+ err("ir protocol setup failed");
+ goto out;
+ }
+
+ d->props.rc.core.protocol = rc_type;
+
+out:
+ mutex_unlock(&d->usb_mutex);
+ return ret;
+}
+
+/* Number of keypresses to ignore before start repeating */
+#define RC_REPEAT_DELAY_V1_20 10
+
+/* This is the structure of the RC response packet starting in firmware 1.20 */
+struct dib0700_rc_response {
+ u8 report_id;
+ u8 data_state;
+ union {
+ u16 system16;
+ struct {
+ u8 not_system;
+ u8 system;
+ };
+ };
+ u8 data;
+ u8 not_data;
+};
+#define RC_MSG_SIZE_V1_20 6
+
+static void dib0700_rc_urb_completion(struct urb *purb)
+{
+ struct dvb_usb_device *d = purb->context;
+ struct dib0700_rc_response *poll_reply;
+ u32 uninitialized_var(keycode);
+ u8 toggle;
+
+ deb_info("%s()\n", __func__);
+ if (d->rc_dev == NULL) {
+ /* This will occur if disable_rc_polling=1 */
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ return;
+ }
+
+ poll_reply = purb->transfer_buffer;
+
+ if (purb->status < 0) {
+ deb_info("discontinuing polling\n");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ return;
+ }
+
+ if (purb->actual_length != RC_MSG_SIZE_V1_20) {
+ deb_info("malformed rc msg size=%d\n", purb->actual_length);
+ goto resubmit;
+ }
+
+ deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n",
+ poll_reply->report_id, poll_reply->data_state,
+ poll_reply->system, poll_reply->not_system,
+ poll_reply->data, poll_reply->not_data,
+ purb->actual_length);
+
+ switch (d->props.rc.core.protocol) {
+ case RC_TYPE_NEC:
+ toggle = 0;
+
+ /* NEC protocol sends repeat code as 0 0 0 FF */
+ if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00)
+ && (poll_reply->not_data == 0xff)) {
+ poll_reply->data_state = 2;
+ break;
+ }
+
+ if ((poll_reply->system ^ poll_reply->not_system) != 0xff) {
+ deb_data("NEC extended protocol\n");
+ /* NEC extended code - 24 bits */
+ keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data;
+ } else {
+ deb_data("NEC normal protocol\n");
+ /* normal NEC code - 16 bits */
+ keycode = poll_reply->system << 8 | poll_reply->data;
+ }
+
+ break;
+ default:
+ deb_data("RC5 protocol\n");
+ /* RC5 Protocol */
+ toggle = poll_reply->report_id;
+ keycode = poll_reply->system << 8 | poll_reply->data;
+
+ break;
+ }
+
+ if ((poll_reply->data + poll_reply->not_data) != 0xff) {
+ /* Key failed integrity check */
+ err("key failed integrity check: %04x %02x %02x",
+ poll_reply->system,
+ poll_reply->data, poll_reply->not_data);
+ goto resubmit;
+ }
+
+ rc_keydown(d->rc_dev, keycode, toggle);
+
+resubmit:
+ /* Clean the buffer before we requeue */
+ memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20);
+
+ /* Requeue URB */
+ usb_submit_urb(purb, GFP_ATOMIC);
+}
+
+int dib0700_rc_setup(struct dvb_usb_device *d)
+{
+ struct dib0700_state *st = d->priv;
+ struct urb *purb;
+ int ret;
+
+ /* Poll-based. Don't initialize bulk mode */
+ if (st->fw_version < 0x10200)
+ return 0;
+
+ /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */
+ purb = usb_alloc_urb(0, GFP_KERNEL);
+ if (purb == NULL) {
+ err("rc usb alloc urb failed");
+ return -ENOMEM;
+ }
+
+ purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL);
+ if (purb->transfer_buffer == NULL) {
+ err("rc kzalloc failed");
+ usb_free_urb(purb);
+ return -ENOMEM;
+ }
+
+ purb->status = -EINPROGRESS;
+ usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1),
+ purb->transfer_buffer, RC_MSG_SIZE_V1_20,
+ dib0700_rc_urb_completion, d);
+
+ ret = usb_submit_urb(purb, GFP_ATOMIC);
+ if (ret) {
+ err("rc submit urb failed");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ }
+
+ return ret;
+}
+
+static int dib0700_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int i;
+ struct dvb_usb_device *dev;
+
+ for (i = 0; i < dib0700_device_count; i++)
+ if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE,
+ &dev, adapter_nr) == 0) {
+ struct dib0700_state *st = dev->priv;
+ u32 hwversion, romversion, fw_version, fwtype;
+
+ dib0700_get_version(dev, &hwversion, &romversion,
+ &fw_version, &fwtype);
+
+ deb_info("Firmware version: %x, %d, 0x%x, %d\n",
+ hwversion, romversion, fw_version, fwtype);
+
+ st->fw_version = fw_version;
+ st->nb_packet_buffer_size = (u32)nb_packet_buffer_size;
+
+ /* Disable polling mode on newer firmwares */
+ if (st->fw_version >= 0x10200)
+ dev->props.rc.core.bulk_mode = true;
+ else
+ dev->props.rc.core.bulk_mode = false;
+
+ dib0700_rc_setup(dev);
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static struct usb_driver dib0700_driver = {
+ .name = "dvb_usb_dib0700",
+ .probe = dib0700_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = dib0700_usb_id_table,
+};
+
+module_usb_driver(dib0700_driver);
+
+MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw");
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index 510001da6e83..510001da6e83 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
diff --git a/drivers/media/dvb/dvb-usb/dib07x0.h b/drivers/media/usb/dvb-usb/dib07x0.h
index 7e62c1018520..7e62c1018520 100644
--- a/drivers/media/dvb/dvb-usb/dib07x0.h
+++ b/drivers/media/usb/dvb-usb/dib07x0.h
diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/usb/dvb-usb/dibusb-common.c
index a76bbb29ca36..a76bbb29ca36 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-common.c
+++ b/drivers/media/usb/dvb-usb/dibusb-common.c
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/usb/dvb-usb/dibusb-mb.c
index a4ac37e0e98b..a4ac37e0e98b 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mb.c
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/usb/dvb-usb/dibusb-mc.c
index 9d1a59d09c52..9d1a59d09c52 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/usb/dvb-usb/dibusb-mc.c
diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/usb/dvb-usb/dibusb.h
index e47c321b3ffc..e47c321b3ffc 100644
--- a/drivers/media/dvb/dvb-usb/dibusb.h
+++ b/drivers/media/usb/dvb-usb/dibusb.h
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/usb/dvb-usb/digitv.c
index ff34419a4c88..ff34419a4c88 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/usb/dvb-usb/digitv.c
diff --git a/drivers/media/dvb/dvb-usb/digitv.h b/drivers/media/usb/dvb-usb/digitv.h
index 908c09f4966b..908c09f4966b 100644
--- a/drivers/media/dvb/dvb-usb/digitv.h
+++ b/drivers/media/usb/dvb-usb/digitv.h
diff --git a/drivers/media/dvb/dvb-usb/dtt200u-fe.c b/drivers/media/usb/dvb-usb/dtt200u-fe.c
index 3d81daa49172..3d81daa49172 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u-fe.c
+++ b/drivers/media/usb/dvb-usb/dtt200u-fe.c
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/usb/dvb-usb/dtt200u.c
index 66f205c112b2..66f205c112b2 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/usb/dvb-usb/dtt200u.c
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.h b/drivers/media/usb/dvb-usb/dtt200u.h
index 005b0a7df358..005b0a7df358 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.h
+++ b/drivers/media/usb/dvb-usb/dtt200u.h
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/usb/dvb-usb/dtv5100.c
index 3d11df41cac0..3d11df41cac0 100644
--- a/drivers/media/dvb/dvb-usb/dtv5100.c
+++ b/drivers/media/usb/dvb-usb/dtv5100.c
diff --git a/drivers/media/dvb/dvb-usb/dtv5100.h b/drivers/media/usb/dvb-usb/dtv5100.h
index 93e96e04a82a..93e96e04a82a 100644
--- a/drivers/media/dvb/dvb-usb/dtv5100.h
+++ b/drivers/media/usb/dvb-usb/dtv5100.h
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-common.h b/drivers/media/usb/dvb-usb/dvb-usb-common.h
index 6b7b2a89242e..6b7b2a89242e 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-common.h
+++ b/drivers/media/usb/dvb-usb/dvb-usb-common.h
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-dvb.c b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
new file mode 100644
index 000000000000..719413b15f20
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/dvb-usb-dvb.c
@@ -0,0 +1,288 @@
+/* dvb-usb-dvb.c is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * This file contains functions for initializing and handling the
+ * linux-dvb API.
+ */
+#include "dvb-usb-common.h"
+
+/* does the complete input transfer handling */
+static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv;
+ int newfeedcount, ret;
+
+ if (adap == NULL)
+ return -ENODEV;
+
+ if ((adap->active_fe < 0) ||
+ (adap->active_fe >= adap->num_frontends_initialized)) {
+ return -EINVAL;
+ }
+
+ newfeedcount = adap->feedcount + (onoff ? 1 : -1);
+
+ /* stop feed before setting a new pid if there will be no pid anymore */
+ if (newfeedcount == 0) {
+ deb_ts("stop feeding\n");
+ usb_urb_kill(&adap->fe_adap[adap->active_fe].stream);
+
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0);
+ if (ret < 0) {
+ err("error while stopping stream.");
+ return ret;
+ }
+ }
+ }
+
+ adap->feedcount = newfeedcount;
+
+ /* activate the pid on the device specific pid_filter */
+ deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n",
+ adap->fe_adap[adap->active_fe].pid_filtering ?
+ "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid,
+ dvbdmxfeed->index, onoff ? "on" : "off");
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->fe_adap[adap->active_fe].pid_filtering &&
+ adap->props.fe[adap->active_fe].pid_filter != NULL)
+ adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
+
+ /* start the feed if this was the first feed and there is still a feed
+ * for reception.
+ */
+ if (adap->feedcount == onoff && adap->feedcount > 0) {
+ deb_ts("submitting all URBs\n");
+ usb_urb_submit(&adap->fe_adap[adap->active_fe].stream);
+
+ deb_ts("controlling pid parser\n");
+ if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER &&
+ adap->props.fe[adap->active_fe].caps &
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF &&
+ adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap,
+ adap->fe_adap[adap->active_fe].pid_filtering);
+ if (ret < 0) {
+ err("could not handle pid_parser");
+ return ret;
+ }
+ }
+ deb_ts("start feeding\n");
+ if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) {
+ ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1);
+ if (ret < 0) {
+ err("error while enabling fifo.");
+ return ret;
+ }
+ }
+
+ }
+ return 0;
+}
+
+static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type);
+ return dvb_usb_ctrl_feed(dvbdmxfeed,1);
+}
+
+static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type);
+ return dvb_usb_ctrl_feed(dvbdmxfeed,0);
+}
+
+int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums)
+{
+ int i;
+ int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name,
+ adap->dev->owner, &adap->dev->udev->dev,
+ adapter_nums);
+
+ if (ret < 0) {
+ deb_info("dvb_register_adapter failed: error %d", ret);
+ goto err;
+ }
+ adap->dvb_adap.priv = adap;
+
+ if (adap->dev->props.read_mac_address) {
+ if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0)
+ info("MAC address: %pM",adap->dvb_adap.proposed_mac);
+ else
+ err("MAC address reading failed.");
+ }
+
+
+ adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+ adap->demux.priv = adap;
+
+ adap->demux.filternum = 0;
+ for (i = 0; i < adap->props.num_frontends; i++) {
+ if (adap->demux.filternum < adap->fe_adap[i].max_feed_count)
+ adap->demux.filternum = adap->fe_adap[i].max_feed_count;
+ }
+ adap->demux.feednum = adap->demux.filternum;
+ adap->demux.start_feed = dvb_usb_start_feed;
+ adap->demux.stop_feed = dvb_usb_stop_feed;
+ adap->demux.write_to_decoder = NULL;
+ if ((ret = dvb_dmx_init(&adap->demux)) < 0) {
+ err("dvb_dmx_init failed: error %d",ret);
+ goto err_dmx;
+ }
+
+ adap->dmxdev.filternum = adap->demux.filternum;
+ adap->dmxdev.demux = &adap->demux.dmx;
+ adap->dmxdev.capabilities = 0;
+ if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) {
+ err("dvb_dmxdev_init failed: error %d",ret);
+ goto err_dmx_dev;
+ }
+
+ if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net,
+ &adap->demux.dmx)) < 0) {
+ err("dvb_net_init failed: error %d",ret);
+ goto err_net_init;
+ }
+
+ adap->state |= DVB_USB_ADAP_STATE_DVB;
+ return 0;
+
+err_net_init:
+ dvb_dmxdev_release(&adap->dmxdev);
+err_dmx_dev:
+ dvb_dmx_release(&adap->demux);
+err_dmx:
+ dvb_unregister_adapter(&adap->dvb_adap);
+err:
+ return ret;
+}
+
+int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap)
+{
+ if (adap->state & DVB_USB_ADAP_STATE_DVB) {
+ deb_info("unregistering DVB part\n");
+ dvb_net_release(&adap->dvb_net);
+ adap->demux.dmx.close(&adap->demux.dmx);
+ dvb_dmxdev_release(&adap->dmxdev);
+ dvb_dmx_release(&adap->demux);
+ dvb_unregister_adapter(&adap->dvb_adap);
+ adap->state &= ~DVB_USB_ADAP_STATE_DVB;
+ }
+ return 0;
+}
+
+static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ int ret = (adap->props.frontend_ctrl) ?
+ adap->props.frontend_ctrl(fe, onoff) : 0;
+
+ if (ret < 0) {
+ err("frontend_ctrl request failed");
+ return ret;
+ }
+ if (onoff)
+ adap->active_fe = fe->id;
+
+ return 0;
+}
+
+static int dvb_usb_fe_wakeup(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ dvb_usb_device_power_ctrl(adap->dev, 1);
+
+ dvb_usb_set_active_fe(fe, 1);
+
+ if (adap->fe_adap[fe->id].fe_init)
+ adap->fe_adap[fe->id].fe_init(fe);
+
+ return 0;
+}
+
+static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ if (adap->fe_adap[fe->id].fe_sleep)
+ adap->fe_adap[fe->id].fe_sleep(fe);
+
+ dvb_usb_set_active_fe(fe, 0);
+
+ return dvb_usb_device_power_ctrl(adap->dev, 0);
+}
+
+int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap)
+{
+ int ret, i;
+
+ /* register all given adapter frontends */
+ for (i = 0; i < adap->props.num_frontends; i++) {
+
+ if (adap->props.fe[i].frontend_attach == NULL) {
+ err("strange: '%s' #%d,%d "
+ "doesn't want to attach a frontend.",
+ adap->dev->desc->name, adap->id, i);
+
+ return 0;
+ }
+
+ ret = adap->props.fe[i].frontend_attach(adap);
+ if (ret || adap->fe_adap[i].fe == NULL) {
+ /* only print error when there is no FE at all */
+ if (i == 0)
+ err("no frontend was attached by '%s'",
+ adap->dev->desc->name);
+
+ return 0;
+ }
+
+ adap->fe_adap[i].fe->id = i;
+
+ /* re-assign sleep and wakeup functions */
+ adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init;
+ adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup;
+ adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep;
+ adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep;
+
+ if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) {
+ err("Frontend %d registration failed.", i);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ adap->fe_adap[i].fe = NULL;
+ /* In error case, do not try register more FEs,
+ * still leaving already registered FEs alive. */
+ if (i == 0)
+ return -ENODEV;
+ else
+ return 0;
+ }
+
+ /* only attach the tuner if the demod is there */
+ if (adap->props.fe[i].tuner_attach != NULL)
+ adap->props.fe[i].tuner_attach(adap);
+
+ adap->num_frontends_initialized++;
+ }
+
+ return 0;
+}
+
+int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap)
+{
+ int i = adap->num_frontends_initialized - 1;
+
+ /* unregister all given adapter frontends */
+ for (; i >= 0; i--) {
+ if (adap->fe_adap[i].fe != NULL) {
+ dvb_unregister_frontend(adap->fe_adap[i].fe);
+ dvb_frontend_detach(adap->fe_adap[i].fe);
+ }
+ }
+ adap->num_frontends_initialized = 0;
+
+ return 0;
+}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
index 733a7ff7b207..733a7ff7b207 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c
index 88e4a62abc44..88e4a62abc44 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-i2c.c
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/usb/dvb-usb/dvb-usb-init.c
index 169196ec2d4e..169196ec2d4e 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-init.c
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
index 41bacff24960..41bacff24960 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/usb/dvb-usb/dvb-usb-urb.c
index 5c8f651344fc..5c8f651344fc 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-urb.c
diff --git a/drivers/media/usb/dvb-usb/dvb-usb.h b/drivers/media/usb/dvb-usb/dvb-usb.h
new file mode 100644
index 000000000000..aab0f99bc892
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/dvb-usb.h
@@ -0,0 +1,483 @@
+/* dvb-usb.h is part of the DVB USB library.
+ *
+ * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.de)
+ * see dvb-usb-init.c for copyright information.
+ *
+ * the headerfile, all dvb-usb-drivers have to include.
+ *
+ * TODO: clean-up the structures for unused fields and update the comments
+ */
+#ifndef __DVB_USB_H__
+#define __DVB_USB_H__
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/mutex.h>
+#include <media/rc-core.h>
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+
+#include "dvb-pll.h"
+
+#include "dvb-usb-ids.h"
+
+/* debug */
+#ifdef CONFIG_DVB_USB_DEBUG
+#define dprintk(var,level,args...) \
+ do { if ((var & level)) { printk(args); } } while (0)
+
+#define debug_dump(b,l,func) {\
+ int loop_; \
+ for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \
+ func("\n");\
+}
+#define DVB_USB_DEBUG_STATUS
+#else
+#define dprintk(args...)
+#define debug_dump(b,l,func)
+
+#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)"
+
+#endif
+
+/* generic log methods - taken from usb.h */
+#ifndef DVB_USB_LOG_PREFIX
+ #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)"
+#endif
+
+#undef err
+#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
+#undef info
+#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
+#undef warn
+#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg)
+
+/**
+ * struct dvb_usb_device_description - name and its according USB IDs
+ * @name: real name of the box, regardless which DVB USB device class is in use
+ * @cold_ids: array of struct usb_device_id which describe the device in
+ * pre-firmware state
+ * @warm_ids: array of struct usb_device_id which describe the device in
+ * post-firmware state
+ *
+ * Each DVB USB device class can have one or more actual devices, this struct
+ * assigns a name to it.
+ */
+struct dvb_usb_device_description {
+ const char *name;
+
+#define DVB_USB_ID_MAX_NUM 15
+ struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM];
+ struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM];
+};
+
+static inline u8 rc5_custom(struct rc_map_table *key)
+{
+ return (key->scancode >> 8) & 0xff;
+}
+
+static inline u8 rc5_data(struct rc_map_table *key)
+{
+ return key->scancode & 0xff;
+}
+
+static inline u16 rc5_scan(struct rc_map_table *key)
+{
+ return key->scancode & 0xffff;
+}
+
+struct dvb_usb_device;
+struct dvb_usb_adapter;
+struct usb_data_stream;
+
+/**
+ * Properties of USB streaming - TODO this structure should be somewhere else
+ * describes the kind of USB transfer used for data-streaming.
+ * (BULK or ISOC)
+ */
+struct usb_data_stream_properties {
+#define USB_BULK 1
+#define USB_ISOC 2
+ int type;
+ int count;
+ int endpoint;
+
+ union {
+ struct {
+ int buffersize; /* per URB */
+ } bulk;
+ struct {
+ int framesperurb;
+ int framesize;
+ int interval;
+ } isoc;
+ } u;
+};
+
+/**
+ * struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter.
+ * A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device.
+ * @caps: capabilities of the DVB USB device.
+ * @pid_filter_count: number of PID filter position in the optional hardware
+ * PID-filter.
+ * @num_frontends: number of frontends of the DVB USB adapter.
+ * @frontend_ctrl: called to power on/off active frontend.
+ * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the
+ * device (not URB submitting/killing).
+ * @pid_filter_ctrl: called to en/disable the PID filter, if any.
+ * @pid_filter: called to set/unset a PID for filtering.
+ * @frontend_attach: called to attach the possible frontends (fill fe-field
+ * of struct dvb_usb_device).
+ * @tuner_attach: called to attach the correct tuner and to fill pll_addr,
+ * pll_desc and pll_init_buf of struct dvb_usb_device).
+ * @stream: configuration of the USB streaming
+ */
+struct dvb_usb_adapter_fe_properties {
+#define DVB_USB_ADAP_HAS_PID_FILTER 0x01
+#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
+#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
+#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08
+#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10
+ int caps;
+ int pid_filter_count;
+
+ int (*streaming_ctrl) (struct dvb_usb_adapter *, int);
+ int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int);
+ int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int);
+
+ int (*frontend_attach) (struct dvb_usb_adapter *);
+ int (*tuner_attach) (struct dvb_usb_adapter *);
+
+ struct usb_data_stream_properties stream;
+
+ int size_of_priv;
+};
+
+#define MAX_NO_OF_FE_PER_ADAP 3
+struct dvb_usb_adapter_properties {
+ int size_of_priv;
+
+ int (*frontend_ctrl) (struct dvb_frontend *, int);
+
+ int num_frontends;
+ struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP];
+};
+
+/**
+ * struct dvb_rc_legacy - old properties of remote controller
+ * @rc_map_table: a hard-wired array of struct rc_map_table (NULL to disable
+ * remote control handling).
+ * @rc_map_size: number of items in @rc_map_table.
+ * @rc_query: called to query an event event.
+ * @rc_interval: time in ms between two queries.
+ */
+struct dvb_rc_legacy {
+/* remote control properties */
+#define REMOTE_NO_KEY_PRESSED 0x00
+#define REMOTE_KEY_PRESSED 0x01
+#define REMOTE_KEY_REPEAT 0x02
+ struct rc_map_table *rc_map_table;
+ int rc_map_size;
+ int (*rc_query) (struct dvb_usb_device *, u32 *, int *);
+ int rc_interval;
+};
+
+/**
+ * struct dvb_rc properties of remote controller, using rc-core
+ * @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.
+ * @bulk_mode: device supports bulk mode for RC (disable polling mode)
+ */
+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);
+ int rc_interval;
+ bool bulk_mode; /* uses bulk mode */
+};
+
+/**
+ * enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one
+ * based on rc-core
+ * This is initialized/used only inside dvb-usb-remote.c.
+ * It shouldn't be set by the drivers.
+ */
+enum dvb_usb_mode {
+ DVB_RC_LEGACY,
+ DVB_RC_CORE,
+};
+
+/**
+ * struct dvb_usb_device_properties - properties of a dvb-usb-device
+ * @usb_ctrl: which USB device-side controller is in use. Needed for firmware
+ * download.
+ * @firmware: name of the firmware file.
+ * @download_firmware: called to download the firmware when the usb_ctrl is
+ * DEVICE_SPECIFIC.
+ * @no_reconnect: device doesn't do a reconnect after downloading the firmware,
+ * so do the warm initialization right after it
+ *
+ * @size_of_priv: how many bytes shall be allocated for the private field
+ * of struct dvb_usb_device.
+ *
+ * @power_ctrl: called to enable/disable power of the device.
+ * @read_mac_address: called to read the MAC address of the device.
+ * @identify_state: called to determine the state (cold or warm), when it
+ * is not distinguishable by the USB IDs.
+ *
+ * @rc: remote controller properties
+ *
+ * @i2c_algo: i2c_algorithm if the device has I2CoverUSB.
+ *
+ * @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic
+ * endpoint which received control messages with bulk transfers. When this
+ * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write-
+ * helper functions.
+ *
+ * @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate
+ * endpoint for responses to control messages sent with bulk transfers via
+ * the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used
+ * instead of the generic_bulk_ctrl_endpoint when reading usb responses in
+ * the dvb_usb_generic_rw helper function.
+ *
+ * @num_device_descs: number of struct dvb_usb_device_description in @devices
+ * @devices: array of struct dvb_usb_device_description compatibles with these
+ * properties.
+ */
+#define MAX_NO_OF_ADAPTER_PER_DEVICE 2
+struct dvb_usb_device_properties {
+
+#define DVB_USB_IS_AN_I2C_ADAPTER 0x01
+ int caps;
+
+#define DEVICE_SPECIFIC 0
+#define CYPRESS_AN2135 1
+#define CYPRESS_AN2235 2
+#define CYPRESS_FX2 3
+ int usb_ctrl;
+ int (*download_firmware) (struct usb_device *, const struct firmware *);
+ const char *firmware;
+ int no_reconnect;
+
+ int size_of_priv;
+
+ int num_adapters;
+ struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
+
+ int (*power_ctrl) (struct dvb_usb_device *, int);
+ int (*read_mac_address) (struct dvb_usb_device *, u8 []);
+ int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *,
+ struct dvb_usb_device_description **, int *);
+
+ struct {
+ enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */
+ struct dvb_rc_legacy legacy;
+ struct dvb_rc core;
+ } rc;
+
+ struct i2c_algorithm *i2c_algo;
+
+ int generic_bulk_ctrl_endpoint;
+ int generic_bulk_ctrl_endpoint_response;
+
+ int num_device_descs;
+ struct dvb_usb_device_description devices[12];
+};
+
+/**
+ * struct usb_data_stream - generic object of an USB stream
+ * @buf_num: number of buffer allocated.
+ * @buf_size: size of each buffer in buf_list.
+ * @buf_list: array containing all allocate buffers for streaming.
+ * @dma_addr: list of dma_addr_t for each buffer in buf_list.
+ *
+ * @urbs_initialized: number of URBs initialized.
+ * @urbs_submitted: number of URBs submitted.
+ */
+#define MAX_NO_URBS_FOR_DATA_STREAM 10
+struct usb_data_stream {
+ struct usb_device *udev;
+ struct usb_data_stream_properties props;
+
+#define USB_STATE_INIT 0x00
+#define USB_STATE_URB_BUF 0x01
+ int state;
+
+ void (*complete) (struct usb_data_stream *, u8 *, size_t);
+
+ struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM];
+ int buf_num;
+ unsigned long buf_size;
+ u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM];
+ dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM];
+
+ int urbs_initialized;
+ int urbs_submitted;
+
+ void *user_priv;
+};
+
+/**
+ * struct dvb_usb_adapter - a DVB adapter on a USB device
+ * @id: index of this adapter (starting with 0).
+ *
+ * @feedcount: number of reqested feeds (used for streaming-activation)
+ * @pid_filtering: is hardware pid_filtering used or not.
+ *
+ * @pll_addr: I2C address of the tuner for programming
+ * @pll_init: array containing the initialization buffer
+ * @pll_desc: pointer to the appropriate struct dvb_pll_desc
+ * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board
+ *
+ * @dvb_adap: device's dvb_adapter.
+ * @dmxdev: device's dmxdev.
+ * @demux: device's software demuxer.
+ * @dvb_net: device's dvb_net interfaces.
+ * @dvb_frontend: device's frontend.
+ * @max_feed_count: how many feeds can be handled simultaneously by this
+ * device
+ *
+ * @fe_init: rerouted frontend-init (wakeup) function.
+ * @fe_sleep: rerouted frontend-sleep function.
+ *
+ * @stream: the usb data stream.
+ */
+struct dvb_usb_fe_adapter {
+ struct dvb_frontend *fe;
+
+ int (*fe_init) (struct dvb_frontend *);
+ int (*fe_sleep) (struct dvb_frontend *);
+
+ struct usb_data_stream stream;
+
+ int pid_filtering;
+ int max_feed_count;
+
+ void *priv;
+};
+
+struct dvb_usb_adapter {
+ struct dvb_usb_device *dev;
+ struct dvb_usb_adapter_properties props;
+
+#define DVB_USB_ADAP_STATE_INIT 0x000
+#define DVB_USB_ADAP_STATE_DVB 0x001
+ int state;
+
+ u8 id;
+
+ int feedcount;
+
+ /* dvb */
+ struct dvb_adapter dvb_adap;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvb_net;
+
+ struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP];
+ int active_fe;
+ int num_frontends_initialized;
+
+ void *priv;
+};
+
+/**
+ * struct dvb_usb_device - object of a DVB USB device
+ * @props: copy of the struct dvb_usb_properties this device belongs to.
+ * @desc: pointer to the device's struct dvb_usb_device_description.
+ * @state: initialization and runtime state of the device.
+ *
+ * @powered: indicated whether the device is power or not.
+ * Powered is in/decremented for each call to modify the state.
+ * @udev: pointer to the device's struct usb_device.
+ *
+ * @usb_mutex: semaphore of USB control messages (reading needs two messages)
+ * @i2c_mutex: semaphore for i2c-transfers
+ *
+ * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB
+ *
+ * @rc_dev: rc device for the remote control (rc-core mode)
+ * @input_dev: input device for the remote control (legacy mode)
+ * @rc_query_work: struct work_struct frequent rc queries
+ * @last_event: last triggered event
+ * @last_state: last state (no, pressed, repeat)
+ * @owner: owner of the dvb_adapter
+ * @priv: private data of the actual driver (allocate by dvb-usb, size defined
+ * in size_of_priv of dvb_usb_properties).
+ */
+struct dvb_usb_device {
+ struct dvb_usb_device_properties props;
+ struct dvb_usb_device_description *desc;
+
+ struct usb_device *udev;
+
+#define DVB_USB_STATE_INIT 0x000
+#define DVB_USB_STATE_I2C 0x001
+#define DVB_USB_STATE_DVB 0x002
+#define DVB_USB_STATE_REMOTE 0x004
+ int state;
+
+ int powered;
+
+ /* locking */
+ struct mutex usb_mutex;
+
+ /* i2c */
+ struct mutex i2c_mutex;
+ struct i2c_adapter i2c_adap;
+
+ int num_adapters_initialized;
+ struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE];
+
+ /* remote control */
+ struct rc_dev *rc_dev;
+ struct input_dev *input_dev;
+ char rc_phys[64];
+ struct delayed_work rc_query_work;
+ u32 last_event;
+ int last_state;
+
+ struct module *owner;
+
+ void *priv;
+};
+
+extern int dvb_usb_device_init(struct usb_interface *,
+ struct dvb_usb_device_properties *,
+ struct module *, struct dvb_usb_device **,
+ short *adapter_nums);
+extern void dvb_usb_device_exit(struct usb_interface *);
+
+/* the generic read/write method for device control */
+extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int);
+extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16);
+
+/* commonly used remote control parsing */
+extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *);
+
+/* commonly used firmware download types and function */
+struct hexline {
+ u8 len;
+ u32 addr;
+ u8 type;
+ u8 data[255];
+ u8 chk;
+};
+extern int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type);
+extern int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, int *pos);
+
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/usb/dvb-usb/dw2102.c
index 9382895b1b88..9382895b1b88 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/usb/dvb-usb/dw2102.c
diff --git a/drivers/media/dvb/dvb-usb/dw2102.h b/drivers/media/usb/dvb-usb/dw2102.h
index 5cd0b0eb6ce1..5cd0b0eb6ce1 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.h
+++ b/drivers/media/usb/dvb-usb/dw2102.h
diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/usb/dvb-usb/friio-fe.c
index 90a70c66a96e..90a70c66a96e 100644
--- a/drivers/media/dvb/dvb-usb/friio-fe.c
+++ b/drivers/media/usb/dvb-usb/friio-fe.c
diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/usb/dvb-usb/friio.c
index 474a17e4db0c..474a17e4db0c 100644
--- a/drivers/media/dvb/dvb-usb/friio.c
+++ b/drivers/media/usb/dvb-usb/friio.c
diff --git a/drivers/media/dvb/dvb-usb/friio.h b/drivers/media/usb/dvb-usb/friio.h
index 0f461ca10cb9..0f461ca10cb9 100644
--- a/drivers/media/dvb/dvb-usb/friio.h
+++ b/drivers/media/usb/dvb-usb/friio.h
diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/usb/dvb-usb/gp8psk-fe.c
index 67957dd99ede..67957dd99ede 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c
+++ b/drivers/media/usb/dvb-usb/gp8psk-fe.c
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/usb/dvb-usb/gp8psk.c
index 5d0384dd45b5..5d0384dd45b5 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/usb/dvb-usb/gp8psk.c
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/usb/dvb-usb/gp8psk.h
index ed32b9da4843..ed32b9da4843 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.h
+++ b/drivers/media/usb/dvb-usb/gp8psk.h
diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c
index 288af29a8bb7..288af29a8bb7 100644
--- a/drivers/media/dvb/dvb-usb/m920x.c
+++ b/drivers/media/usb/dvb-usb/m920x.c
diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/usb/dvb-usb/m920x.h
index 3c061518ffc1..3c061518ffc1 100644
--- a/drivers/media/dvb/dvb-usb/m920x.h
+++ b/drivers/media/usb/dvb-usb/m920x.h
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/usb/dvb-usb/nova-t-usb2.c
index 6c55384e2fca..6c55384e2fca 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/usb/dvb-usb/nova-t-usb2.c
diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/usb/dvb-usb/opera1.c
index c8a95042dfbc..c8a95042dfbc 100644
--- a/drivers/media/dvb/dvb-usb/opera1.c
+++ b/drivers/media/usb/dvb-usb/opera1.c
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
new file mode 100644
index 000000000000..02e878577c3d
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -0,0 +1,1063 @@
+/*
+ * PCTV 452e DVB driver
+ *
+ * Copyright (c) 2006-2008 Dominik Kuhlen <dkuhlen@gmx.net>
+ *
+ * TT connect S2-3650-CI Common Interface support, MAC readout
+ * Copyright (C) 2008 Michael H. Schimek <mschimek@gmx.at>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+/* dvb usb framework */
+#define DVB_USB_LOG_PREFIX "pctv452e"
+#include "dvb-usb.h"
+
+/* Demodulator */
+#include "stb0899_drv.h"
+#include "stb0899_reg.h"
+#include "stb0899_cfg.h"
+/* Tuner */
+#include "stb6100.h"
+#include "stb6100_cfg.h"
+/* FE Power */
+#include "lnbp22.h"
+
+#include "dvb_ca_en50221.h"
+#include "ttpci-eeprom.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define ISOC_INTERFACE_ALTERNATIVE 3
+
+#define SYNC_BYTE_OUT 0xaa
+#define SYNC_BYTE_IN 0x55
+
+/* guessed: (copied from ttusb-budget) */
+#define PCTV_CMD_RESET 0x15
+/* command to poll IR receiver */
+#define PCTV_CMD_IR 0x1b
+/* command to send I2C */
+#define PCTV_CMD_I2C 0x31
+
+#define I2C_ADDR_STB0899 (0xd0 >> 1)
+#define I2C_ADDR_STB6100 (0xc0 >> 1)
+#define I2C_ADDR_LNBP22 (0x10 >> 1)
+#define I2C_ADDR_24C16 (0xa0 >> 1)
+#define I2C_ADDR_24C64 (0xa2 >> 1)
+
+
+/* pctv452e sends us this amount of data for each issued usb-command */
+#define PCTV_ANSWER_LEN 64
+/* Wait up to 1000ms for device */
+#define PCTV_TIMEOUT 1000
+
+
+#define PCTV_LED_GPIO STB0899_GPIO01
+#define PCTV_LED_GREEN 0x82
+#define PCTV_LED_ORANGE 0x02
+
+#define ci_dbg(format, arg...) \
+do { \
+ if (0) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": " format "\n" , ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
+
+static struct stb0899_postproc pctv45e_postproc[] = {
+ { PCTV_LED_GPIO, STB0899_GPIOPULLUP },
+ { 0, 0 }
+};
+
+/*
+ * stores all private variables for communication with the PCTV452e DVB-S2
+ */
+struct pctv452e_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+
+ u8 c; /* transaction counter, wraps around... */
+ u8 initialized; /* set to 1 if 0x15 has been sent */
+ u16 last_rc_key;
+};
+
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data,
+ unsigned int write_len, unsigned int read_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ unsigned int rlen;
+ int ret;
+
+ BUG_ON(NULL == data && 0 != (write_len | read_len));
+ BUG_ON(write_len > 64 - 4);
+ BUG_ON(read_len > 64 - 4);
+
+ id = state->c++;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = cmd;
+ buf[3] = write_len;
+
+ memcpy(buf + 4, data, write_len);
+
+ rlen = (read_len > 0) ? 64 : 0;
+ ret = dvb_usb_generic_rw(d, buf, 4 + write_len,
+ buf, rlen, /* delay_ms */ 0);
+ if (0 != ret)
+ goto failed;
+
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ memcpy(data, buf + 4, read_len);
+
+ return 0;
+
+failed:
+ err("CI error %d; %02X %02X %02X -> %*ph.",
+ ret, SYNC_BYTE_OUT, id, cmd, 3, buf);
+
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca,
+ u8 cmd, u8 *data, unsigned int write_len,
+ unsigned int read_len)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address)
+{
+ u8 buf[3];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%s %04x -> %d 0x%02x",
+ __func__, address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%s %d 0x%04x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("%s 0x%02x -> %d 0x%02x",
+ __func__, address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%s %d 0x%02x 0x%02x",
+ __func__, slot, address, value);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca,
+ int slot,
+ int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d %d", __func__, slot, enable);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ enable = !!enable;
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, /* enable */ 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%s %d", __func__, slot);
+
+ if (0 != slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (0 != ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca,
+ int slot,
+ int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (0 != slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (0 != ret)
+ return ret;
+
+ if (1 == buf[0])
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+
+ return 0;
+
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state;
+
+ ci_dbg("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct pctv452e_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ /* Error ignored. */
+ tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0);
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ int ret;
+
+ ci_dbg("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (0 != ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
+#define CMD_BUFFER_SIZE 0x28
+static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
+ const u8 *snd_buf, u8 snd_len,
+ u8 *rcv_buf, u8 rcv_len)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 buf[64];
+ u8 id;
+ int ret;
+
+ id = state->c++;
+
+ ret = -EINVAL;
+ if (snd_len > 64 - 7 || rcv_len > 64 - 7)
+ goto failed;
+
+ buf[0] = SYNC_BYTE_OUT;
+ buf[1] = id;
+ buf[2] = PCTV_CMD_I2C;
+ buf[3] = snd_len + 3;
+ buf[4] = addr << 1;
+ buf[5] = snd_len;
+ buf[6] = rcv_len;
+
+ memcpy(buf + 7, snd_buf, snd_len);
+
+ ret = dvb_usb_generic_rw(d, buf, 7 + snd_len,
+ buf, /* rcv_len */ 64,
+ /* delay_ms */ 0);
+ if (ret < 0)
+ goto failed;
+
+ /* TT USB protocol error. */
+ ret = -EIO;
+ if (SYNC_BYTE_IN != buf[0] || id != buf[1])
+ goto failed;
+
+ /* I2C device didn't respond as expected. */
+ ret = -EREMOTEIO;
+ if (buf[5] < snd_len || buf[6] < rcv_len)
+ goto failed;
+
+ memcpy(rcv_buf, buf + 7, rcv_len);
+
+ return rcv_len;
+
+failed:
+ err("I2C error %d; %02X %02X %02X %02X %02X -> "
+ "%02X %02X %02X %02X %02X.",
+ ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len,
+ buf[0], buf[1], buf[4], buf[5], buf[6]);
+
+ return ret;
+}
+
+static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adapter);
+ int i;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf;
+ int ret;
+
+ if (msg[i].flags & I2C_M_RD) {
+ addr = msg[i].addr;
+ snd_buf = NULL;
+ snd_len = 0;
+ rcv_buf = msg[i].buf;
+ rcv_len = msg[i].len;
+ } else {
+ addr = msg[i].addr;
+ snd_buf = msg[i].buf;
+ snd_len = msg[i].len;
+ rcv_buf = NULL;
+ rcv_len = 0;
+ }
+
+ ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf,
+ rcv_len);
+ if (ret < rcv_len)
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 pctv452e_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 };
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret;
+
+ info("%s: %d\n", __func__, i);
+
+ if (!i)
+ return 0;
+
+ if (state->initialized)
+ return 0;
+
+ /* hmm where shoud this should go? */
+ ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE);
+ if (ret != 0)
+ info("%s: Warning set interface returned: %d\n",
+ __func__, ret);
+
+ /* this is a one-time initialization, dont know where to put */
+ b0[1] = state->c++;
+ /* reset board */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ b0[1] = state->c++;
+ b0[4] = 1;
+ /* reset board (again?) */
+ ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0);
+ if (ret)
+ return ret;
+
+ state->initialized = 1;
+
+ return 0;
+}
+
+static int pctv452e_rc_query(struct dvb_usb_device *d)
+{
+ struct pctv452e_state *state = (struct pctv452e_state *)d->priv;
+ u8 b[CMD_BUFFER_SIZE];
+ u8 rx[PCTV_ANSWER_LEN];
+ int ret, i;
+ u8 id = state->c++;
+
+ /* prepare command header */
+ b[0] = SYNC_BYTE_OUT;
+ b[1] = id;
+ b[2] = PCTV_CMD_IR;
+ b[3] = 0;
+
+ /* send ir request */
+ ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0);
+ if (ret != 0)
+ return ret;
+
+ if (debug > 3) {
+ info("%s: read: %2d: %*ph: ", __func__, ret, 3, rx);
+ for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++)
+ info(" %02x", rx[i+3]);
+
+ info("\n");
+ }
+
+ if ((rx[3] == 9) && (rx[12] & 0x01)) {
+ /* got a "press" event */
+ state->last_rc_key = (rx[7] << 8) | rx[6];
+ if (debug > 2)
+ info("%s: cmd=0x%02x sys=0x%02x\n",
+ __func__, rx[6], rx[7]);
+
+ rc_keydown(d->rc_dev, state->last_rc_key, 0);
+ } else if (state->last_rc_key) {
+ rc_keyup(d->rc_dev);
+ state->last_rc_key = 0;
+ }
+
+ return 0;
+}
+
+static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ const u8 mem_addr[] = { 0x1f, 0xcc };
+ u8 encoded_mac[20];
+ int ret;
+
+ ret = -EAGAIN;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ goto failed;
+
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16,
+ mem_addr + 1, /* snd_len */ 1,
+ encoded_mac, /* rcv_len */ 20);
+ if (-EREMOTEIO == ret)
+ /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a
+ byte write if /WC is low. */
+ ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64,
+ mem_addr, 2,
+ encoded_mac, 20);
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (20 != ret)
+ goto failed;
+
+ ret = ttpci_eeprom_decode_mac(mac, encoded_mac);
+ if (0 != ret)
+ goto failed;
+
+ return 0;
+
+failed:
+ memset(mac, 0, 6);
+
+ return ret;
+}
+
+static const struct stb0899_s1_reg pctv452e_init_dev[] = {
+ { STB0899_DISCNTRL1, 0x26 },
+ { STB0899_DISCNTRL2, 0x80 },
+ { STB0899_DISRX_ST0, 0x04 },
+ { STB0899_DISRX_ST1, 0x20 },
+ { STB0899_DISPARITY, 0x00 },
+ { STB0899_DISFIFO, 0x00 },
+ { STB0899_DISF22, 0x99 },
+ { STB0899_DISF22RX, 0x85 }, /* 0xa8 */
+ { STB0899_ACRPRESC, 0x11 },
+ { STB0899_ACRDIV1, 0x0a },
+ { STB0899_ACRDIV2, 0x05 },
+ { STB0899_DACR1 , 0x00 },
+ { STB0899_DACR2 , 0x00 },
+ { STB0899_OUTCFG, 0x00 },
+ { STB0899_MODECFG, 0x00 }, /* Inversion */
+ { STB0899_IRQMSK_3, 0xf3 },
+ { STB0899_IRQMSK_2, 0xfc },
+ { STB0899_IRQMSK_1, 0xff },
+ { STB0899_IRQMSK_0, 0xff },
+ { STB0899_I2CCFG, 0x88 },
+ { STB0899_I2CRPT, 0x58 },
+ { STB0899_GPIO00CFG, 0x82 },
+ { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */
+ { STB0899_GPIO02CFG, 0x82 },
+ { STB0899_GPIO03CFG, 0x82 },
+ { STB0899_GPIO04CFG, 0x82 },
+ { STB0899_GPIO05CFG, 0x82 },
+ { STB0899_GPIO06CFG, 0x82 },
+ { STB0899_GPIO07CFG, 0x82 },
+ { STB0899_GPIO08CFG, 0x82 },
+ { STB0899_GPIO09CFG, 0x82 },
+ { STB0899_GPIO10CFG, 0x82 },
+ { STB0899_GPIO11CFG, 0x82 },
+ { STB0899_GPIO12CFG, 0x82 },
+ { STB0899_GPIO13CFG, 0x82 },
+ { STB0899_GPIO14CFG, 0x82 },
+ { STB0899_GPIO15CFG, 0x82 },
+ { STB0899_GPIO16CFG, 0x82 },
+ { STB0899_GPIO17CFG, 0x82 },
+ { STB0899_GPIO18CFG, 0x82 },
+ { STB0899_GPIO19CFG, 0x82 },
+ { STB0899_GPIO20CFG, 0x82 },
+ { STB0899_SDATCFG, 0xb8 },
+ { STB0899_SCLTCFG, 0xba },
+ { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */
+ { STB0899_GPIO22, 0x82 },
+ { STB0899_GPIO21, 0x91 },
+ { STB0899_DIRCLKCFG, 0x82 },
+ { STB0899_CLKOUT27CFG, 0x7e },
+ { STB0899_STDBYCFG, 0x82 },
+ { STB0899_CS0CFG, 0x82 },
+ { STB0899_CS1CFG, 0x82 },
+ { STB0899_DISEQCOCFG, 0x20 },
+ { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */
+ { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */
+ { STB0899_FILTCTRL, 0x00 },
+ { STB0899_SYSCTRL, 0x00 },
+ { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */
+ { STB0899_STOPCLK2, 0x00 },
+ { STB0899_INTBUFCTRL, 0x0a },
+ { STB0899_AGC2I1, 0x00 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_AGCIQIN, 0x00 },
+ { STB0899_TSTRES, 0x40 }, /* rjkm */
+ { 0xffff, 0xff },
+};
+
+static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = {
+ { STB0899_DEMOD, 0x00 },
+ { STB0899_RCOMPC, 0xc9 },
+ { STB0899_AGC1CN, 0x01 },
+ { STB0899_AGC1REF, 0x10 },
+ { STB0899_RTC, 0x23 },
+ { STB0899_TMGCFG, 0x4e },
+ { STB0899_AGC2REF, 0x34 },
+ { STB0899_TLSR, 0x84 },
+ { STB0899_CFD, 0xf7 },
+ { STB0899_ACLC, 0x87 },
+ { STB0899_BCLC, 0x94 },
+ { STB0899_EQON, 0x41 },
+ { STB0899_LDT, 0xf1 },
+ { STB0899_LDT2, 0xe3 },
+ { STB0899_EQUALREF, 0xb4 },
+ { STB0899_TMGRAMP, 0x10 },
+ { STB0899_TMGTHD, 0x30 },
+ { STB0899_IDCCOMP, 0xfd },
+ { STB0899_QDCCOMP, 0xff },
+ { STB0899_POWERI, 0x0c },
+ { STB0899_POWERQ, 0x0f },
+ { STB0899_RCOMP, 0x6c },
+ { STB0899_AGCIQIN, 0x80 },
+ { STB0899_AGC2I1, 0x06 },
+ { STB0899_AGC2I2, 0x00 },
+ { STB0899_TLIR, 0x30 },
+ { STB0899_RTF, 0x7f },
+ { STB0899_DSTATUS, 0x00 },
+ { STB0899_LDI, 0xbc },
+ { STB0899_CFRM, 0xea },
+ { STB0899_CFRL, 0x31 },
+ { STB0899_NIRM, 0x2b },
+ { STB0899_NIRL, 0x80 },
+ { STB0899_ISYMB, 0x1d },
+ { STB0899_QSYMB, 0xa6 },
+ { STB0899_SFRH, 0x2f },
+ { STB0899_SFRM, 0x68 },
+ { STB0899_SFRL, 0x40 },
+ { STB0899_SFRUPH, 0x2f },
+ { STB0899_SFRUPM, 0x68 },
+ { STB0899_SFRUPL, 0x40 },
+ { STB0899_EQUAI1, 0x02 },
+ { STB0899_EQUAQ1, 0xff },
+ { STB0899_EQUAI2, 0x04 },
+ { STB0899_EQUAQ2, 0x05 },
+ { STB0899_EQUAI3, 0x02 },
+ { STB0899_EQUAQ3, 0xfd },
+ { STB0899_EQUAI4, 0x03 },
+ { STB0899_EQUAQ4, 0x07 },
+ { STB0899_EQUAI5, 0x08 },
+ { STB0899_EQUAQ5, 0xf5 },
+ { STB0899_DSTATUS2, 0x00 },
+ { STB0899_VSTATUS, 0x00 },
+ { STB0899_VERROR, 0x86 },
+ { STB0899_IQSWAP, 0x2a },
+ { STB0899_ECNT1M, 0x00 },
+ { STB0899_ECNT1L, 0x00 },
+ { STB0899_ECNT2M, 0x00 },
+ { STB0899_ECNT2L, 0x00 },
+ { STB0899_ECNT3M, 0x0a },
+ { STB0899_ECNT3L, 0xad },
+ { STB0899_FECAUTO1, 0x06 },
+ { STB0899_FECM, 0x01 },
+ { STB0899_VTH12, 0xb0 },
+ { STB0899_VTH23, 0x7a },
+ { STB0899_VTH34, 0x58 },
+ { STB0899_VTH56, 0x38 },
+ { STB0899_VTH67, 0x34 },
+ { STB0899_VTH78, 0x24 },
+ { STB0899_PRVIT, 0xff },
+ { STB0899_VITSYNC, 0x19 },
+ { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
+ { STB0899_TSULC, 0x42 },
+ { STB0899_RSLLC, 0x41 },
+ { STB0899_TSLPL, 0x12 },
+ { STB0899_TSCFGH, 0x0c },
+ { STB0899_TSCFGM, 0x00 },
+ { STB0899_TSCFGL, 0x00 },
+ { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */
+ { STB0899_RSSYNCDEL, 0x00 },
+ { STB0899_TSINHDELH, 0x02 },
+ { STB0899_TSINHDELM, 0x00 },
+ { STB0899_TSINHDELL, 0x00 },
+ { STB0899_TSLLSTKM, 0x1b },
+ { STB0899_TSLLSTKL, 0xb3 },
+ { STB0899_TSULSTKM, 0x00 },
+ { STB0899_TSULSTKL, 0x00 },
+ { STB0899_PCKLENUL, 0xbc },
+ { STB0899_PCKLENLL, 0xcc },
+ { STB0899_RSPCKLEN, 0xbd },
+ { STB0899_TSSTATUS, 0x90 },
+ { STB0899_ERRCTRL1, 0xb6 },
+ { STB0899_ERRCTRL2, 0x95 },
+ { STB0899_ERRCTRL3, 0x8d },
+ { STB0899_DMONMSK1, 0x27 },
+ { STB0899_DMONMSK0, 0x03 },
+ { STB0899_DEMAPVIT, 0x5c },
+ { STB0899_PLPARM, 0x19 },
+ { STB0899_PDELCTRL, 0x48 },
+ { STB0899_PDELCTRL2, 0x00 },
+ { STB0899_BBHCTRL1, 0x00 },
+ { STB0899_BBHCTRL2, 0x00 },
+ { STB0899_HYSTTHRESH, 0x77 },
+ { STB0899_MATCSTM, 0x00 },
+ { STB0899_MATCSTL, 0x00 },
+ { STB0899_UPLCSTM, 0x00 },
+ { STB0899_UPLCSTL, 0x00 },
+ { STB0899_DFLCSTM, 0x00 },
+ { STB0899_DFLCSTL, 0x00 },
+ { STB0899_SYNCCST, 0x00 },
+ { STB0899_SYNCDCSTM, 0x00 },
+ { STB0899_SYNCDCSTL, 0x00 },
+ { STB0899_ISI_ENTRY, 0x00 },
+ { STB0899_ISI_BIT_EN, 0x00 },
+ { STB0899_MATSTRM, 0xf0 },
+ { STB0899_MATSTRL, 0x02 },
+ { STB0899_UPLSTRM, 0x45 },
+ { STB0899_UPLSTRL, 0x60 },
+ { STB0899_DFLSTRM, 0xe3 },
+ { STB0899_DFLSTRL, 0x00 },
+ { STB0899_SYNCSTR, 0x47 },
+ { STB0899_SYNCDSTRM, 0x05 },
+ { STB0899_SYNCDSTRL, 0x18 },
+ { STB0899_CFGPDELSTATUS1, 0x19 },
+ { STB0899_CFGPDELSTATUS2, 0x2b },
+ { STB0899_BBFERRORM, 0x00 },
+ { STB0899_BBFERRORL, 0x01 },
+ { STB0899_UPKTERRORM, 0x00 },
+ { STB0899_UPKTERRORL, 0x00 },
+ { 0xffff, 0xff },
+};
+
+static struct stb0899_config stb0899_config = {
+ .init_dev = pctv452e_init_dev,
+ .init_s2_demod = stb0899_s2_init_2,
+ .init_s1_demod = pctv452e_init_s1_demod,
+ .init_s2_fec = stb0899_s2_init_4,
+ .init_tst = stb0899_s1_init_5,
+
+ .demod_address = I2C_ADDR_STB0899, /* I2C Address */
+ .block_sync_mode = STB0899_SYNC_FORCED, /* ? */
+
+ .xtal_freq = 27000000, /* Assume Hz ? */
+ .inversion = IQ_SWAP_ON, /* ? */
+
+ .lo_clk = 76500000,
+ .hi_clk = 99000000,
+
+ .ts_output_mode = 0, /* Use parallel mode */
+ .clock_polarity = 0,
+ .data_clk_parity = 0,
+ .fec_mode = 0,
+
+ .esno_ave = STB0899_DVBS2_ESNO_AVE,
+ .esno_quant = STB0899_DVBS2_ESNO_QUANT,
+ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
+ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
+ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
+ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
+ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
+ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
+ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
+
+ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
+ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
+ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
+ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
+
+ .tuner_get_frequency = stb6100_get_frequency,
+ .tuner_set_frequency = stb6100_set_frequency,
+ .tuner_set_bandwidth = stb6100_set_bandwidth,
+ .tuner_get_bandwidth = stb6100_get_bandwidth,
+ .tuner_set_rfsiggain = NULL,
+
+ /* helper for switching LED green/orange */
+ .postproc = pctv45e_postproc
+};
+
+static struct stb6100_config stb6100_config = {
+ .tuner_address = I2C_ADDR_STB6100,
+ .refclock = 27000000
+};
+
+
+static struct i2c_algorithm pctv452e_i2c_algo = {
+ .master_xfer = pctv452e_i2c_xfer,
+ .functionality = pctv452e_i2c_func
+};
+
+static int pctv452e_frontend_attach(struct dvb_usb_adapter *a)
+{
+ struct usb_device_id *id;
+
+ a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config,
+ &a->dev->i2c_adap);
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe,
+ &a->dev->i2c_adap)) == 0)
+ err("Cannot attach lnbp22\n");
+
+ id = a->dev->desc->warm_ids[0];
+ if (USB_VID_TECHNOTREND == id->idVendor
+ && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct)
+ /* Error ignored. */
+ tt3650_ci_init(a);
+
+ return 0;
+}
+
+static int pctv452e_tuner_attach(struct dvb_usb_adapter *a)
+{
+ if (!a->fe_adap[0].fe)
+ return -ENODEV;
+ if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config,
+ &a->dev->i2c_adap) == 0) {
+ err("%s failed\n", __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct usb_device_id pctv452e_usb_table[] = {
+ {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)},
+ {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)},
+ {USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, pctv452e_usb_table);
+
+static struct dvb_usb_device_properties pctv452e_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_DIB0700_RC5_TABLE,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 4,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */
+
+ .num_device_descs = 1,
+ .devices = {
+ { .name = "PCTV HDTV USB",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[0], NULL }
+ },
+ { 0 },
+ }
+};
+
+static struct dvb_usb_device_properties tt_connect_s2_3600_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct pctv452e_state),
+
+ .power_ctrl = pctv452e_power_ctrl,
+ .read_mac_address = pctv452e_read_mac_address,
+
+ .rc.core = {
+ .rc_codes = RC_MAP_TT_1500,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_query = pctv452e_rc_query,
+ .rc_interval = 100,
+ },
+
+ .num_adapters = 1,
+ .adapter = {{
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = pctv452e_frontend_attach,
+ .tuner_attach = pctv452e_tuner_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1
+ }
+ }
+ },
+
+ } },
+ } },
+
+ .i2c_algo = &pctv452e_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "Technotrend TT Connect S2-3600",
+ .cold_ids = { NULL, NULL }, /* this is a warm only device */
+ .warm_ids = { &pctv452e_usb_table[1], NULL }
+ },
+ { .name = "Technotrend TT Connect S2-3650-CI",
+ .cold_ids = { NULL, NULL },
+ .warm_ids = { &pctv452e_usb_table[2], NULL }
+ },
+ { 0 },
+ }
+};
+
+static void pctv452e_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int pctv452e_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ if (0 == dvb_usb_device_init(intf, &pctv452e_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties,
+ THIS_MODULE, NULL, adapter_nr))
+ return 0;
+
+ return -ENODEV;
+}
+
+static struct usb_driver pctv452e_usb_driver = {
+ .name = "pctv452e",
+ .probe = pctv452e_usb_probe,
+ .disconnect = pctv452e_usb_disconnect,
+ .id_table = pctv452e_usb_table,
+};
+
+module_usb_driver(pctv452e_usb_driver);
+
+MODULE_AUTHOR("Dominik Kuhlen <dkuhlen@gmx.net>");
+MODULE_AUTHOR("Andre Weidemann <Andre.Weidemann@web.de>");
+MODULE_AUTHOR("Michael H. Schimek <mschimek@gmx.at>");
+MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/usb/dvb-usb/technisat-usb2.c
index acefaa89cc53..acefaa89cc53 100644
--- a/drivers/media/dvb/dvb-usb/technisat-usb2.c
+++ b/drivers/media/usb/dvb-usb/technisat-usb2.c
diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c
new file mode 100644
index 000000000000..6a50cdea3bce
--- /dev/null
+++ b/drivers/media/usb/dvb-usb/ttusb2.c
@@ -0,0 +1,820 @@
+/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones
+ * (e.g. Pinnacle 400e DVB-S USB2.0).
+ *
+ * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes.
+ *
+ * TDA8263 + TDA10086
+ *
+ * I2C addresses:
+ * 0x08 - LNBP21PD - LNB power supply
+ * 0x0e - TDA10086 - Demodulator
+ * 0x50 - FX2 eeprom
+ * 0x60 - TDA8263 - Tuner
+ * 0x78 ???
+ *
+ * Copyright (c) 2002 Holger Waechtler <holger@convergence.de>
+ * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net>
+ * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#define DVB_USB_LOG_PREFIX "ttusb2"
+#include "dvb-usb.h"
+
+#include "ttusb2.h"
+
+#include "tda826x.h"
+#include "tda10086.h"
+#include "tda1002x.h"
+#include "tda10048.h"
+#include "tda827x.h"
+#include "lnbp21.h"
+/* CA */
+#include "dvb_ca_en50221.h"
+
+/* debug */
+static int dvb_usb_ttusb2_debug;
+#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args)
+module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS);
+static int dvb_usb_ttusb2_debug_ci;
+module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644);
+MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS);
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define ci_dbg(format, arg...) \
+do { \
+ if (dvb_usb_ttusb2_debug_ci) \
+ printk(KERN_DEBUG DVB_USB_LOG_PREFIX \
+ ": %s " format "\n" , __func__, ## arg); \
+} while (0)
+
+enum {
+ TT3650_CMD_CI_TEST = 0x40,
+ TT3650_CMD_CI_RD_CTRL,
+ TT3650_CMD_CI_WR_CTRL,
+ TT3650_CMD_CI_RD_ATTR,
+ TT3650_CMD_CI_WR_ATTR,
+ TT3650_CMD_CI_RESET,
+ TT3650_CMD_CI_SET_VIDEO_PORT
+};
+
+struct ttusb2_state {
+ struct dvb_ca_en50221 ca;
+ struct mutex ca_mutex;
+ u8 id;
+ u16 last_rc_key;
+};
+
+static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd,
+ u8 *wbuf, int wlen, u8 *rbuf, int rlen)
+{
+ struct ttusb2_state *st = d->priv;
+ u8 *s, *r = NULL;
+ int ret = 0;
+
+ s = kzalloc(wlen+4, GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ r = kzalloc(64, GFP_KERNEL);
+ if (!r) {
+ kfree(s);
+ return -ENOMEM;
+ }
+
+ s[0] = 0xaa;
+ s[1] = ++st->id;
+ s[2] = cmd;
+ s[3] = wlen;
+ memcpy(&s[4],wbuf,wlen);
+
+ ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0);
+
+ if (ret != 0 ||
+ r[0] != 0x55 ||
+ r[1] != s[1] ||
+ r[2] != cmd ||
+ (rlen > 0 && r[3] != rlen)) {
+ warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]);
+ kfree(s);
+ kfree(r);
+ return -EIO;
+ }
+
+ if (rlen > 0)
+ memcpy(rbuf, &r[4], rlen);
+
+ kfree(s);
+ kfree(r);
+
+ return 0;
+}
+
+/* ci */
+static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ int ret;
+ u8 rx[60];/* (64 -4) */
+ ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len);
+ if (!ret)
+ memcpy(data, rx, read_len);
+ return ret;
+}
+
+static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ mutex_lock(&state->ca_mutex);
+ ret = tt3650_ci_msg(d, cmd, data, write_len, read_len);
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
+{
+ u8 buf[3];
+ int ret = 0;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3);
+
+ ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[2];
+}
+
+static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
+{
+ u8 buf[3];
+
+ ci_dbg("%d 0x%04x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = (address >> 8) & 0x0F;
+ buf[1] = address;
+ buf[2] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3);
+}
+
+static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
+{
+ u8 buf[2];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address & 3;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2);
+
+ ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]);
+
+ if (ret < 0)
+ return ret;
+
+ return buf[1];
+}
+
+static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
+{
+ u8 buf[2];
+
+ ci_dbg("%d 0x%02x 0x%02x", slot, address, value);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = address;
+ buf[1] = value;
+
+ return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2);
+}
+
+static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable)
+{
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d %d", slot, enable);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = enable;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+ if (ret < 0)
+ return ret;
+
+ if (enable != buf[0]) {
+ err("CI not %sabled.", enable ? "en" : "dis");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 0);
+}
+
+static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ return tt3650_ci_set_video_port(ca, slot, 1);
+}
+
+static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = ca->data;
+ struct ttusb2_state *state = d->priv;
+ u8 buf[1];
+ int ret;
+
+ ci_dbg("%d", slot);
+
+ if (slot)
+ return -EINVAL;
+
+ buf[0] = 0;
+
+ mutex_lock(&state->ca_mutex);
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 1;
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1);
+ if (ret)
+ goto failed;
+
+ msleep(500);
+
+ buf[0] = 0; /* FTA */
+
+ ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1);
+
+ msleep(1100);
+
+ failed:
+ mutex_unlock(&state->ca_mutex);
+
+ return ret;
+}
+
+static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ u8 buf[1];
+ int ret;
+
+ if (slot)
+ return -EINVAL;
+
+ ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1);
+ if (ret)
+ return ret;
+
+ if (1 == buf[0]) {
+ return DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+ return 0;
+}
+
+static void tt3650_ci_uninit(struct dvb_usb_device *d)
+{
+ struct ttusb2_state *state;
+
+ ci_dbg("");
+
+ if (NULL == d)
+ return;
+
+ state = d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+static int tt3650_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct ttusb2_state *state = d->priv;
+ int ret;
+
+ ci_dbg("");
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem;
+ state->ca.read_cam_control = tt3650_ci_read_cam_control;
+ state->ca.write_cam_control = tt3650_ci_write_cam_control;
+ state->ca.slot_reset = tt3650_ci_slot_reset;
+ state->ca.slot_shutdown = tt3650_ci_slot_shutdown;
+ state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable;
+ state->ca.poll_slot_status = tt3650_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ /* flags */ 0,
+ /* n_slots */ 1);
+ if (ret) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ info("CI initialized.");
+
+ return 0;
+}
+
+static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ static u8 obuf[60], ibuf[60];
+ int i, write_read, read;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num > 2)
+ warn("more than 2 i2c messages at a time is not handled yet. TODO.");
+
+ for (i = 0; i < num; i++) {
+ write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD);
+ read = msg[i].flags & I2C_M_RD;
+
+ obuf[0] = (msg[i].addr << 1) | (write_read | read);
+ if (read)
+ obuf[1] = 0;
+ else
+ obuf[1] = msg[i].len;
+
+ /* read request */
+ if (write_read)
+ obuf[2] = msg[i+1].len;
+ else if (read)
+ obuf[2] = msg[i].len;
+ else
+ obuf[2] = 0;
+
+ memcpy(&obuf[3], msg[i].buf, msg[i].len);
+
+ if (ttusb2_msg(d, CMD_I2C_XFER, obuf, obuf[1]+3, ibuf, obuf[2] + 3) < 0) {
+ err("i2c transfer failed.");
+ break;
+ }
+
+ if (write_read) {
+ memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len);
+ i++;
+ } else if (read)
+ memcpy(msg[i].buf, &ibuf[3], msg[i].len);
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return i;
+}
+
+static u32 ttusb2_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm ttusb2_i2c_algo = {
+ .master_xfer = ttusb2_i2c_xfer,
+ .functionality = ttusb2_i2c_func,
+};
+
+/* command to poll IR receiver (copied from pctv452e.c) */
+#define CMD_GET_IR_CODE 0x1b
+
+/* IR */
+static int tt3650_rc_query(struct dvb_usb_device *d)
+{
+ int ret;
+ u8 rx[9]; /* A CMD_GET_IR_CODE reply is 9 bytes long */
+ struct ttusb2_state *st = d->priv;
+ ret = ttusb2_msg(d, CMD_GET_IR_CODE, NULL, 0, rx, sizeof(rx));
+ if (ret != 0)
+ return ret;
+
+ if (rx[8] & 0x01) {
+ /* got a "press" event */
+ st->last_rc_key = (rx[3] << 8) | rx[2];
+ deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]);
+ rc_keydown(d->rc_dev, st->last_rc_key, rx[1]);
+ } else if (st->last_rc_key) {
+ rc_keyup(d->rc_dev);
+ st->last_rc_key = 0;
+ }
+
+ return 0;
+}
+
+
+/* Callbacks for DVB USB */
+static int ttusb2_identify_state (struct usb_device *udev, struct
+ dvb_usb_device_properties *props, struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0;
+ return 0;
+}
+
+static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ u8 b = onoff;
+ ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0);
+ return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0);
+}
+
+
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x0e,
+ .invert = 0,
+ .diseqc_tone = 1,
+ .xtal_freq = TDA10086_XTAL_16M,
+};
+
+static struct tda10023_config tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 0,
+ .xtal = 16000000,
+ .pll_m = 11,
+ .pll_p = 3,
+ .pll_n = 1,
+ .deltaf = 0xa511,
+};
+
+static struct tda10048_config tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_4000,
+ .dtv7_if_freq_khz = TDA10048_IF_4500,
+ .dtv8_if_freq_khz = TDA10048_IF_5000,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .no_firmware = 1,
+ .set_pll = true ,
+ .pll_m = 5,
+ .pll_n = 3,
+ .pll_p = 0,
+};
+
+static struct tda827x_config tda827x_config = {
+ .config = 0,
+};
+
+static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap)
+{
+ if (usb_set_interface(adap->dev->udev,0,3) < 0)
+ err("set interface to alts=3 failed");
+
+ if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) {
+ deb_info("TDA10086 attach failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+
+ return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable);
+}
+
+static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap)
+{
+ if (usb_set_interface(adap->dev->udev, 0, 3) < 0)
+ err("set interface to alts=3 failed");
+
+ if (adap->fe_adap[0].fe == NULL) {
+ /* FE 0 DVB-C */
+ adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
+ &tda10023_config, &adap->dev->i2c_adap, 0x48);
+
+ if (adap->fe_adap[0].fe == NULL) {
+ deb_info("TDA10023 attach failed\n");
+ return -ENODEV;
+ }
+ tt3650_ci_init(adap);
+ } else {
+ adap->fe_adap[1].fe = dvb_attach(tda10048_attach,
+ &tda10048_config, &adap->dev->i2c_adap);
+
+ if (adap->fe_adap[1].fe == NULL) {
+ deb_info("TDA10048 attach failed\n");
+ return -ENODEV;
+ }
+
+ /* tuner is behind TDA10023 I2C-gate */
+ adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl;
+
+ }
+
+ return 0;
+}
+
+static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_frontend *fe;
+
+ /* MFE: select correct FE to attach tuner since that's called twice */
+ if (adap->fe_adap[1].fe == NULL)
+ fe = adap->fe_adap[0].fe;
+ else
+ fe = adap->fe_adap[1].fe;
+
+ /* attach tuner */
+ if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) {
+ printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap)
+{
+ if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) {
+ deb_info("TDA8263 attach failed\n");
+ return -ENODEV;
+ }
+
+ if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) {
+ deb_info("LNBP21 attach failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties ttusb2_properties;
+static struct dvb_usb_device_properties ttusb2_properties_s2400;
+static struct dvb_usb_device_properties ttusb2_properties_ct3650;
+
+static void ttusb2_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+
+ tt3650_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int ttusb2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ if (0 == dvb_usb_device_init(intf, &ttusb2_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &ttusb2_properties_s2400,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &ttusb2_properties_ct3650,
+ THIS_MODULE, NULL, adapter_nr))
+ return 0;
+ return -ENODEV;
+}
+
+static struct usb_device_id ttusb2_table [] = {
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) },
+ { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) },
+ { USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_S2400) },
+ { USB_DEVICE(USB_VID_TECHNOTREND,
+ USB_PID_TECHNOTREND_CONNECT_CT3650) },
+ {} /* Terminating entry */
+};
+MODULE_DEVICE_TABLE (usb, ttusb2_table);
+
+static struct dvb_usb_device_properties ttusb2_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-pctv-400e-01.fw",
+
+ .size_of_priv = sizeof(struct ttusb2_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .streaming_ctrl = NULL, // ttusb2_streaming_ctrl,
+
+ .frontend_attach = ttusb2_frontend_tda10086_attach,
+ .tuner_attach = ttusb2_tuner_tda826x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }},
+ }
+ },
+
+ .power_ctrl = ttusb2_power_ctrl,
+ .identify_state = ttusb2_identify_state,
+
+ .i2c_algo = &ttusb2_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .num_device_descs = 2,
+ .devices = {
+ { "Pinnacle 400e DVB-S USB2.0",
+ { &ttusb2_table[0], NULL },
+ { NULL },
+ },
+ { "Pinnacle 450e DVB-S USB2.0",
+ { &ttusb2_table[1], NULL },
+ { NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties ttusb2_properties_s2400 = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-tt-s2400-01.fw",
+
+ .size_of_priv = sizeof(struct ttusb2_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .streaming_ctrl = NULL,
+
+ .frontend_attach = ttusb2_frontend_tda10086_attach,
+ .tuner_attach = ttusb2_tuner_tda826x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }},
+ }
+ },
+
+ .power_ctrl = ttusb2_power_ctrl,
+ .identify_state = ttusb2_identify_state,
+
+ .i2c_algo = &ttusb2_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "Technotrend TT-connect S-2400",
+ { &ttusb2_table[2], NULL },
+ { NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties ttusb2_properties_ct3650 = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = CYPRESS_FX2,
+
+ .size_of_priv = sizeof(struct ttusb2_state),
+
+ .rc.core = {
+ .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */
+ .rc_codes = RC_MAP_TT_1500,
+ .rc_query = tt3650_rc_query,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ },
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 2,
+ .fe = {{
+ .streaming_ctrl = NULL,
+
+ .frontend_attach = ttusb2_frontend_tda10023_attach,
+ .tuner_attach = ttusb2_tuner_tda827x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }, {
+ .streaming_ctrl = NULL,
+
+ .frontend_attach = ttusb2_frontend_tda10023_attach,
+ .tuner_attach = ttusb2_tuner_tda827x_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_ISOC,
+ .count = 5,
+ .endpoint = 0x02,
+ .u = {
+ .isoc = {
+ .framesperurb = 4,
+ .framesize = 940,
+ .interval = 1,
+ }
+ }
+ }
+ }},
+ },
+ },
+
+ .power_ctrl = ttusb2_power_ctrl,
+ .identify_state = ttusb2_identify_state,
+
+ .i2c_algo = &ttusb2_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ .num_device_descs = 1,
+ .devices = {
+ { "Technotrend TT-connect CT-3650",
+ .warm_ids = { &ttusb2_table[3], NULL },
+ },
+ }
+};
+
+static struct usb_driver ttusb2_driver = {
+ .name = "dvb_usb_ttusb2",
+ .probe = ttusb2_probe,
+ .disconnect = ttusb2_usb_disconnect,
+ .id_table = ttusb2_table,
+};
+
+module_usb_driver(ttusb2_driver);
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/usb/dvb-usb/ttusb2.h
index 52a63af40896..52a63af40896 100644
--- a/drivers/media/dvb/dvb-usb/ttusb2.h
+++ b/drivers/media/usb/dvb-usb/ttusb2.h
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/usb/dvb-usb/umt-010.c
index 9b042292e788..9b042292e788 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/usb/dvb-usb/umt-010.c
diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/usb/dvb-usb/usb-urb.c
index d62ee0f5a165..d62ee0f5a165 100644
--- a/drivers/media/dvb/dvb-usb/usb-urb.c
+++ b/drivers/media/usb/dvb-usb/usb-urb.c
diff --git a/drivers/media/dvb/dvb-usb/vp702x-fe.c b/drivers/media/usb/dvb-usb/vp702x-fe.c
index 5eab468dd904..5eab468dd904 100644
--- a/drivers/media/dvb/dvb-usb/vp702x-fe.c
+++ b/drivers/media/usb/dvb-usb/vp702x-fe.c
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/usb/dvb-usb/vp702x.c
index 07c673a6e764..07c673a6e764 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.c
+++ b/drivers/media/usb/dvb-usb/vp702x.c
diff --git a/drivers/media/dvb/dvb-usb/vp702x.h b/drivers/media/usb/dvb-usb/vp702x.h
index 20b90055e7ac..20b90055e7ac 100644
--- a/drivers/media/dvb/dvb-usb/vp702x.h
+++ b/drivers/media/usb/dvb-usb/vp702x.h
diff --git a/drivers/media/dvb/dvb-usb/vp7045-fe.c b/drivers/media/usb/dvb-usb/vp7045-fe.c
index b8825b18c003..b8825b18c003 100644
--- a/drivers/media/dvb/dvb-usb/vp7045-fe.c
+++ b/drivers/media/usb/dvb-usb/vp7045-fe.c
diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/usb/dvb-usb/vp7045.c
index d750724132ee..d750724132ee 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.c
+++ b/drivers/media/usb/dvb-usb/vp7045.c
diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/usb/dvb-usb/vp7045.h
index cf5ec46f8bb1..cf5ec46f8bb1 100644
--- a/drivers/media/dvb/dvb-usb/vp7045.h
+++ b/drivers/media/usb/dvb-usb/vp7045.h
diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig
new file mode 100644
index 000000000000..7a5bd61bd3bb
--- /dev/null
+++ b/drivers/media/usb/em28xx/Kconfig
@@ -0,0 +1,58 @@
+config VIDEO_EM28XX
+ tristate "Empia EM28xx USB video capture support"
+ depends on VIDEO_DEV && I2C
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEOBUF_VMALLOC
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEO_MT9V011 if MEDIA_SUBDRV_AUTOSELECT
+
+ ---help---
+ This is a video4linux driver for Empia 28xx based TV cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx
+
+config VIDEO_EM28XX_ALSA
+ depends on VIDEO_EM28XX && SND
+ select SND_PCM
+ tristate "Empia EM28xx ALSA audio module"
+ ---help---
+ This is an ALSA driver for some Empia 28xx based TV cards.
+
+ This is not required for em2800/em2820/em2821 boards. However,
+ newer em28xx devices uses Vendor Class for audio, instead of
+ implementing the USB Audio Class. For those chips, this module
+ will enable digital audio.
+
+ To compile this driver as a module, choose M here: the
+ module will be called em28xx-alsa
+
+config VIDEO_EM28XX_DVB
+ tristate "DVB/ATSC Support for em28xx based TV cards"
+ depends on VIDEO_EM28XX && DVB_CORE
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S921 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRXD if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_CXD2820R if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT
+ select VIDEOBUF_DVB
+ ---help---
+ This adds support for DVB cards based on the
+ Empiatech em28xx chips.
+
+config VIDEO_EM28XX_RC
+ tristate "EM28XX Remote Controller support"
+ depends on RC_CORE
+ depends on VIDEO_EM28XX
+ depends on !(RC_CORE=m && VIDEO_EM28XX=y)
+ default VIDEO_EM28XX
+ ---help---
+ Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/usb/em28xx/Makefile b/drivers/media/usb/em28xx/Makefile
new file mode 100644
index 000000000000..634fb920dd39
--- /dev/null
+++ b/drivers/media/usb/em28xx/Makefile
@@ -0,0 +1,15 @@
+em28xx-y += em28xx-video.o em28xx-i2c.o em28xx-cards.o
+em28xx-y += em28xx-core.o em28xx-vbi.o
+
+em28xx-alsa-objs := em28xx-audio.o
+em28xx-rc-objs := em28xx-input.o
+
+obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
+obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
+obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
+obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c
new file mode 100644
index 000000000000..2fdb66ee44ab
--- /dev/null
+++ b/drivers/media/usb/em28xx/em28xx-audio.c
@@ -0,0 +1,751 @@
+/*
+ * Empiatech em28x1 audio extension
+ *
+ * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
+ *
+ * Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * - Port to work with the in-kernel driver
+ * - Cleanups, fixes, alsa-controls, etc.
+ *
+ * This driver is based on my previous au600 usb pstn audio driver
+ * and inherits all the copyrights
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/ac97_codec.h>
+#include <media/v4l2-common.h>
+#include "em28xx.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+#define dprintk(fmt, arg...) do { \
+ if (debug) \
+ printk(KERN_INFO "em28xx-audio %s: " fmt, \
+ __func__, ##arg); \
+ } while (0)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+static int em28xx_deinit_isoc_audio(struct em28xx *dev)
+{
+ int i;
+
+ dprintk("Stopping isoc\n");
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ if (!irqs_disabled())
+ usb_kill_urb(dev->adev.urb[i]);
+ else
+ usb_unlink_urb(dev->adev.urb[i]);
+
+ usb_free_urb(dev->adev.urb[i]);
+ dev->adev.urb[i] = NULL;
+
+ kfree(dev->adev.transfer_buffer[i]);
+ dev->adev.transfer_buffer[i] = NULL;
+ }
+
+ return 0;
+}
+
+static void em28xx_audio_isocirq(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+ unsigned int oldptr;
+ int period_elapsed = 0;
+ int status;
+ unsigned char *cp;
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ dprintk("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ if (atomic_read(&dev->stream_started) == 0)
+ return;
+
+ if (dev->adev.capture_pcm_substream) {
+ substream = dev->adev.capture_pcm_substream;
+ runtime = substream->runtime;
+ stride = runtime->frame_bits >> 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int length =
+ urb->iso_frame_desc[i].actual_length / stride;
+ cp = (unsigned char *)urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset;
+
+ if (!length)
+ continue;
+
+ oldptr = dev->adev.hwptr_done_capture;
+ if (oldptr + length >= runtime->buffer_size) {
+ unsigned int cnt =
+ runtime->buffer_size - oldptr;
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ cnt * stride);
+ memcpy(runtime->dma_area, cp + cnt * stride,
+ length * stride - cnt * stride);
+ } else {
+ memcpy(runtime->dma_area + oldptr * stride, cp,
+ length * stride);
+ }
+
+ snd_pcm_stream_lock(substream);
+
+ dev->adev.hwptr_done_capture += length;
+ if (dev->adev.hwptr_done_capture >=
+ runtime->buffer_size)
+ dev->adev.hwptr_done_capture -=
+ runtime->buffer_size;
+
+ dev->adev.capture_transfer_done += length;
+ if (dev->adev.capture_transfer_done >=
+ runtime->period_size) {
+ dev->adev.capture_transfer_done -=
+ runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+ }
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ urb->status = 0;
+
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status < 0) {
+ em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
+ status);
+ }
+ return;
+}
+
+static int em28xx_init_audio_isoc(struct em28xx *dev)
+{
+ int i, errCode;
+ const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+
+ dprintk("Starting isoc transfers\n");
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ struct urb *urb;
+ int j, k;
+
+ dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
+ if (!dev->adev.transfer_buffer[i])
+ return -ENOMEM;
+
+ memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
+ urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
+ if (!urb) {
+ em28xx_errdev("usb_alloc_urb failed!\n");
+ for (j = 0; j < i; j++) {
+ usb_free_urb(dev->adev.urb[j]);
+ kfree(dev->adev.transfer_buffer[j]);
+ }
+ return -ENOMEM;
+ }
+
+ urb->dev = dev->udev;
+ urb->context = dev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = dev->adev.transfer_buffer[i];
+ urb->interval = 1;
+ urb->complete = em28xx_audio_isocirq;
+ urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
+ urb->transfer_buffer_length = sb_size;
+
+ for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
+ j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ EM28XX_AUDIO_MAX_PACKET_SIZE;
+ }
+ dev->adev.urb[i] = urb;
+ }
+
+ for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
+ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
+ if (errCode) {
+ em28xx_errdev("submit of audio urb failed\n");
+ em28xx_deinit_isoc_audio(dev);
+ atomic_set(&dev->stream_started, 0);
+ return errCode;
+ }
+
+ }
+
+ return 0;
+}
+
+static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ size_t size)
+{
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+
+ runtime->dma_bytes = size;
+
+ return 0;
+}
+
+static struct snd_pcm_hardware snd_em28xx_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
+ .period_bytes_min = 64, /* 12544/2, */
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98, /* 12544, */
+};
+
+static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int ret = 0;
+
+ dprintk("opening device and trying to acquire exclusive lock\n");
+
+ if (!dev) {
+ em28xx_err("BUG: em28xx can't find device struct."
+ " Can't proceed with open\n");
+ return -ENODEV;
+ }
+
+ runtime->hw = snd_em28xx_hw_capture;
+ if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
+ if (dev->audio_ifnum)
+ dev->alt = 1;
+ else
+ dev->alt = 7;
+
+ dprintk("changing alternate number on interface %d to %d\n",
+ dev->audio_ifnum, dev->alt);
+ usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
+
+ /* Sets volume, mute, etc */
+ dev->mute = 0;
+ mutex_lock(&dev->lock);
+ ret = em28xx_audio_analog_set(dev);
+ if (ret < 0)
+ goto err;
+
+ dev->adev.users++;
+ mutex_unlock(&dev->lock);
+ }
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev.capture_pcm_substream = substream;
+
+ return 0;
+err:
+ mutex_unlock(&dev->lock);
+
+ em28xx_err("Error while configuring em28xx mixer\n");
+ return ret;
+}
+
+static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("closing device\n");
+
+ dev->mute = 1;
+ mutex_lock(&dev->lock);
+ dev->adev.users--;
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+
+ em28xx_audio_analog_set(dev);
+ if (substream->runtime->dma_area) {
+ dprintk("freeing\n");
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ }
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int ret;
+
+ dprintk("Setting capture parameters\n");
+
+ ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0)
+ return ret;
+#if 0
+ /* TODO: set up em28xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ unsigned int channels, rate, format;
+
+ format = params_format(hw_params);
+ rate = params_rate(hw_params);
+ channels = params_channels(hw_params);
+#endif
+
+ return 0;
+}
+
+static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dprintk("Stop capture, if needed\n");
+
+ if (atomic_read(&dev->stream_started) > 0) {
+ atomic_set(&dev->stream_started, 0);
+ schedule_work(&dev->wq_trigger);
+ }
+
+ return 0;
+}
+
+static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+
+ dev->adev.hwptr_done_capture = 0;
+ dev->adev.capture_transfer_done = 0;
+
+ return 0;
+}
+
+static void audio_trigger(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
+
+ if (atomic_read(&dev->stream_started)) {
+ dprintk("starting capture");
+ em28xx_init_audio_isoc(dev);
+ } else {
+ dprintk("stopping capture");
+ em28xx_deinit_isoc_audio(dev);
+ }
+}
+
+static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+{
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
+ int retval = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&dev->stream_started, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&dev->stream_started, 0);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ schedule_work(&dev->wq_trigger);
+ return retval;
+}
+
+static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+{
+ unsigned long flags;
+ struct em28xx *dev;
+ snd_pcm_uframes_t hwptr_done;
+
+ dev = snd_pcm_substream_chip(substream);
+ spin_lock_irqsave(&dev->adev.slock, flags);
+ hwptr_done = dev->adev.hwptr_done_capture;
+ spin_unlock_irqrestore(&dev->adev.slock, flags);
+
+ return hwptr_done;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * AC97 volume control support
+ */
+static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *info)
+{
+ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ info->count = 2;
+ info->value.integer.min = 0;
+ info->value.integer.max = 0x1f;
+
+ return 0;
+}
+
+static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
+ (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ val |= rc & 0x8000; /* Preserve the mute flag */
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ value->value.integer.value[0] = 0x1f - (val & 0x1f);
+ value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
+
+ return 0;
+}
+
+static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ u16 val = value->value.integer.value[0];
+ int rc;
+
+ mutex_lock(&dev->lock);
+ rc = em28xx_read_ac97(dev, kcontrol->private_value);
+ if (rc < 0)
+ goto err;
+
+ if (val)
+ rc &= 0x1f1f;
+ else
+ rc |= 0x8000;
+
+ rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
+ if (rc < 0)
+ goto err;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+err:
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *value)
+{
+ struct em28xx *dev = snd_kcontrol_chip(kcontrol);
+ int val;
+
+ mutex_lock(&dev->lock);
+ val = em28xx_read_ac97(dev, kcontrol->private_value);
+ mutex_unlock(&dev->lock);
+ if (val < 0)
+ return val;
+
+ if (val & 0x8000)
+ value->value.integer.value[0] = 0;
+ else
+ value->value.integer.value[0] = 1;
+
+ dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
+ (val & 0x8000) ? "muted " : "",
+ 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
+ val, (int)kcontrol->private_value);
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
+
+static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
+ char *name, int id)
+{
+ int err;
+ char ctl_name[44];
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new tmp;
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Mute Control */
+ sprintf(ctl_name, "%s Switch", name);
+ tmp.get = em28xx_vol_get_mute;
+ tmp.put = em28xx_vol_put_mute;
+ tmp.info = snd_ctl_boolean_mono_info;
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ memset (&tmp, 0, sizeof(tmp));
+ tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ tmp.private_value = id,
+ tmp.name = ctl_name,
+
+ /* Add Volume Control */
+ sprintf(ctl_name, "%s Volume", name);
+ tmp.get = em28xx_vol_get;
+ tmp.put = em28xx_vol_put;
+ tmp.info = em28xx_vol_info;
+ tmp.tlv.p = em28xx_db_scale,
+ kctl = snd_ctl_new1(&tmp, dev);
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ return err;
+ dprintk("Added control %s for ac97 volume control 0x%04x\n",
+ ctl_name, id);
+
+ return 0;
+}
+
+/*
+ * register/unregister code and data
+ */
+static struct snd_pcm_ops snd_em28xx_pcm_capture = {
+ .open = snd_em28xx_capture_open,
+ .close = snd_em28xx_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_em28xx_hw_capture_params,
+ .hw_free = snd_em28xx_hw_capture_free,
+ .prepare = snd_em28xx_prepare,
+ .trigger = snd_em28xx_capture_trigger,
+ .pointer = snd_em28xx_capture_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+static int em28xx_audio_init(struct em28xx *dev)
+{
+ struct em28xx_audio *adev = &dev->adev;
+ struct snd_pcm *pcm;
+ struct snd_card *card;
+ static int devnr;
+ int err;
+
+ if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
+ "Rechberger\n");
+ printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
+
+ err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
+ &card);
+ if (err < 0)
+ return err;
+
+ spin_lock_init(&adev->slock);
+ err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
+ pcm->info_flags = 0;
+ pcm->private_data = dev;
+ strcpy(pcm->name, "Empia 28xx Capture");
+
+ snd_card_set_dev(card, &dev->udev->dev);
+ strcpy(card->driver, "Em28xx-Audio");
+ strcpy(card->shortname, "Em28xx Audio");
+ strcpy(card->longname, "Empia Em28xx Audio");
+
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
+ em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
+ em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
+ em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
+ em28xx_cvol_new(card, dev, "CD", AC97_CD);
+ em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
+ em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
+
+ em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
+ em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
+ em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
+ em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
+ em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
+ }
+
+ err = snd_card_register(card);
+ if (err < 0) {
+ snd_card_free(card);
+ return err;
+ }
+ adev->sndcard = card;
+ adev->udev = dev->udev;
+
+ return 0;
+}
+
+static int em28xx_audio_fini(struct em28xx *dev)
+{
+ if (dev == NULL)
+ return 0;
+
+ if (dev->has_alsa_audio != 1) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module or
+ doesn't have analog audio support at all) */
+ return 0;
+ }
+
+ if (dev->adev.sndcard) {
+ snd_card_free(dev->adev.sndcard);
+ dev->adev.sndcard = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops audio_ops = {
+ .id = EM28XX_AUDIO,
+ .name = "Em28xx Audio Extension",
+ .init = em28xx_audio_init,
+ .fini = em28xx_audio_fini,
+};
+
+static int __init em28xx_alsa_register(void)
+{
+ return em28xx_register_extension(&audio_ops);
+}
+
+static void __exit em28xx_alsa_unregister(void)
+{
+ em28xx_unregister_extension(&audio_ops);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_DESCRIPTION("Em28xx Audio driver");
+
+module_init(em28xx_alsa_register);
+module_exit(em28xx_alsa_unregister);
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
new file mode 100644
index 000000000000..ab98d0845861
--- /dev/null
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -0,0 +1,3415 @@
+/*
+ em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/usb.h>
+#include <media/tuner.h>
+#include <media/msp3400.h>
+#include <media/saa7115.h>
+#include <media/tvp5150.h>
+#include <media/tvaudio.h>
+#include <media/mt9v011.h>
+#include <media/i2c-addr.h>
+#include <media/tveeprom.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-chip-ident.h>
+
+#include "em28xx.h"
+
+#define DRIVER_NAME "em28xx"
+
+static int tuner = -1;
+module_param(tuner, int, 0444);
+MODULE_PARM_DESC(tuner, "tuner type");
+
+static unsigned int disable_ir;
+module_param(disable_ir, int, 0444);
+MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
+
+static unsigned int disable_usb_speed_check;
+module_param(disable_usb_speed_check, int, 0444);
+MODULE_PARM_DESC(disable_usb_speed_check,
+ "override min bandwidth requirement of 480M bps");
+
+static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+module_param_array(card, int, NULL, 0444);
+MODULE_PARM_DESC(card, "card type");
+
+/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
+static unsigned long em28xx_devused;
+
+struct em28xx_hash_table {
+ unsigned long hash;
+ unsigned int model;
+ unsigned int tuner;
+};
+
+static void em28xx_pre_card_setup(struct em28xx *dev);
+
+/*
+ * Reset sequences for analog/digital modes
+ */
+
+/* Reset for the most [analog] boards */
+static struct em28xx_reg_seq default_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq default_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 analog */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
+ {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
+ {0x05, 0xff, 0x10, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0x0f, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Board Hauppauge WinTV HVR 900 (R2) digital */
+static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
+ {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x0c, 0x0f, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
+ {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
+
+/* Board - EM2870 Kworld 355u
+ Analog - No input analog */
+
+/* Board - EM2882 Kworld 315U digital */
+static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM2880_R04_GPO, 0x04, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x7e, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq kworld_330u_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x00, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq kworld_330u_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Evga inDtube
+ GPIO0 - Enable digital power (s5h1409) - low to enable
+ GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
+ GPIO4 - xc3028 reset
+ GOP3 - s5h1409 reset
+ */
+static struct em28xx_reg_seq evga_indtube_analog[] = {
+ {EM28XX_R08_GPIO, 0x79, 0xff, 60},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq evga_indtube_digital[] = {
+ {EM28XX_R08_GPIO, 0x7a, 0xff, 1},
+ {EM2880_R04_GPO, 0x04, 0xff, 10},
+ {EM2880_R04_GPO, 0x0c, 0xff, 1},
+ { -1, -1, -1, -1},
+};
+
+/*
+ * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
+ * EM_GPIO_0 - currently unknown
+ * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
+ * EM_GPIO_2 - currently unknown
+ * EM_GPIO_3 - currently unknown
+ * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
+ * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
+ * EM_GPIO_6 - currently unknown
+ * EM_GPIO_7 - currently unknown
+ */
+static struct em28xx_reg_seq kworld_a340_digital[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Pinnacle Hybrid Pro eb1a:2881 */
+static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+ {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
+ {EM2880_R04_GPO, 0x0c, 0xff, 1},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
+ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x00, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* eb1a:2868 Reddo DVB-C USB TV Box
+ GPIO4 - CU1216L NIM
+ Other GPIOs seems to be don't care. */
+static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xde, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x7f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x6f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {-1, -1, -1, -1},
+};
+
+/* Callback for the most boards */
+static struct em28xx_reg_seq default_tuner_gpio[] = {
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
+ {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Mute/unmute */
+static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
+ {EM28XX_R08_GPIO, 5, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
+ {EM28XX_R08_GPIO, 4, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq compro_mute_gpio[] = {
+ {EM28XX_R08_GPIO, 6, 7, 10},
+ { -1, -1, -1, -1},
+};
+
+/* Terratec AV350 */
+static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0x7f, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x01, 0xf7, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq vc211a_enable[] = {
+ {EM28XX_R08_GPIO, 0xff, 0x07, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0f, 10},
+ {EM28XX_R08_GPIO, 0xff, 0x0b, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq dikom_dk300_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x08, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+
+/* Reset for the most [digital] boards */
+static struct em28xx_reg_seq leadership_digital[] = {
+ {EM2874_R80_GPIO, 0x70, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq leadership_reset[] = {
+ {EM2874_R80_GPIO, 0xf0, 0xff, 10},
+ {EM2874_R80_GPIO, 0xb0, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf0, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+/* 2013:024f PCTV nanoStick T2 290e
+ * GPIO_6 - demod reset
+ * GPIO_7 - LED
+ */
+static struct em28xx_reg_seq pctv_290e[] = {
+ {EM2874_R80_GPIO, 0x00, 0xff, 80},
+ {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
+ {-1, -1, -1, -1},
+};
+
+#if 0
+static struct em28xx_reg_seq terratec_h5_gpio[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq terratec_h5_digital[] = {
+ {EM2874_R80_GPIO, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+#endif
+
+/* 2013:024f PCTV DVB-S2 Stick 460e
+ * GPIO_0 - POWER_ON
+ * GPIO_1 - BOOST
+ * GPIO_2 - VUV_LNB (red LED)
+ * GPIO_3 - EXT_12V
+ * GPIO_4 - INT_DEM (DEMOD GPIO_0)
+ * GPIO_5 - INT_LNB
+ * GPIO_6 - RESET_DEM
+ * GPIO_7 - LED (green LED)
+ */
+static struct em28xx_reg_seq pctv_460e[] = {
+ {EM2874_R80_GPIO, 0x01, 0xff, 50},
+ {0x0d, 0xff, 0xff, 50},
+ {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
+ {0x0d, 0x42, 0xff, 50},
+ {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
+ { -1, -1, -1, -1},
+};
+
+#if 0
+static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
+ {EM2874_R80_GPIO, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */
+ {EM2874_R80_GPIO, 0x6f, 0xff, 10},
+ {EM2874_R80_GPIO, 0x4f, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq hauppauge_930c_digital[] = {
+ {EM2874_R80_GPIO, 0xf6, 0xff, 10},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 10},
+ { -1, -1, -1, -1},
+};
+#endif
+
+/* 1b80:e425 MaxMedia UB425-TC
+ * GPIO_6 - demod reset, 0=active
+ * GPIO_7 - LED, 0=active
+ */
+static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+ {EM2874_R80_GPIO, 0x83, 0xff, 100},
+ {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
+ {-1, -1, -1, -1},
+};
+
+/* 2304:0242 PCTV QuatroStick (510e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_510e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ { -1, -1, -1, -1},
+};
+
+/* 2013:0251 PCTV QuatroStick nano (520e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_520e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+ { -1, -1, -1, -1},
+};
+
+/*
+ * Board definitions
+ */
+struct em28xx_board em28xx_boards[] = {
+ [EM2750_BOARD_UNKNOWN] = {
+ .name = "EM2710/EM2750/EM2751 webcam grabber",
+ .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
+ } },
+ },
+ [EM2800_BOARD_UNKNOWN] = {
+ .name = "Unknown EM2800 video grabber",
+ .is_em2800 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_UNKNOWN] = {
+ .name = "Unknown EM2750/28xx video grabber",
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1, /* To enable sensor probe */
+ },
+ [EM2750_BOARD_DLCW_130] = {
+ /* Beijing Huaqi Information Digital Technology Co., Ltd */
+ .name = "Huaqi DLCW-130",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2820_BOARD_KWORLD_PVRTV2800RF] = {
+ .name = "Kworld PVR TV 2800 RF",
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_GADMEI_TVR200] = {
+ .name = "Gadmei TVR200",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_TERRATEC_CINERGY_250] = {
+ .name = "Terratec Cinergy 250 USB",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .has_ir_i2c = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_USB_2] = {
+ .name = "Pinnacle PCTV USB 2",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .has_ir_i2c = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
+ .name = "Hauppauge WinTV USB 2",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
+ .has_ir_i2c = 1,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = MSP_INPUT_DEFAULT,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
+ } },
+ },
+ [EM2820_BOARD_DLINK_USB_TV] = {
+ .name = "D-Link DUB-T210 TV Tuner",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
+ .name = "Hercules Smart TV USB 2.0",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
+ .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_GADMEI_UTV310] = {
+ .name = "Gadmei UTV310",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
+ .name = "Leadtek Winfast USB II Deluxe",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .has_ir_i2c = 1,
+ .tvaudio_addr = 0x58,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT2_ACTIVE |
+ TDA9887_QSS,
+ .decoder = EM28XX_SAA711X,
+ .adecoder = EM28XX_TVAUDIO,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = EM28XX_AMUX_AUX,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE5,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ .radio = {
+ .type = EM28XX_RADIO,
+ .amux = EM28XX_AMUX_AUX,
+ }
+ },
+ [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
+ .name = "Videology 20K14XUSB USB2.0",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2820_BOARD_SILVERCREST_WEBCAM] = {
+ .name = "Silvercrest Webcam 1.3mpix",
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
+ } },
+ },
+ [EM2821_BOARD_SUPERCOMP_USB_2] = {
+ .name = "Supercomp USB 2.0 TV",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2821_BOARD_USBGEAR_VD204] = {
+ .name = "Usbgear VD204v9",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_NETGMBH_CAM] = {
+ /* Beijing Huaqi Information Digital Technology Co., Ltd */
+ .name = "NetGMBH Cam",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .is_webcam = 1,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2860_BOARD_TYPHOON_DVD_MAKER] = {
+ .name = "Typhoon DVD Maker",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_GADMEI_UTV330] = {
+ .name = "Gadmei UTV330",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_GADMEI_UTV330PLUS] = {
+ .name = "Gadmei UTV330+",
+ .tuner_type = TUNER_TNF_5335MF,
+ .tda9887_conf = TDA9887_PRESENT,
+ .ir_codes = RC_MAP_GADMEI_RM008Z,
+ .decoder = EM28XX_SAA711X,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Cinergy A Hybrid XS",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2861_BOARD_KWORLD_PVRTV_300U] = {
+ .name = "KWorld PVRTV 300U",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
+ .name = "Yakumo MovieMixer",
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = {
+ .name = "EM2860/TVP5150 Reference Design",
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2861_BOARD_PLEXTOR_PX_TV100U] = {
+ .name = "Plextor ConvertX PX-TV100U",
+ .tuner_type = TUNER_TNF_5335MF,
+ .xclk = EM28XX_XCLK_I2S_MSB_TIMING |
+ EM28XX_XCLK_FREQUENCY_12MHZ,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ } },
+ },
+
+ /* Those boards with em2870 are DVB Only*/
+
+ [EM2870_BOARD_TERRATEC_XS] = {
+ .name = "Terratec Cinergy T XS",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ [EM2870_BOARD_TERRATEC_XS_MT2060] = {
+ .name = "Terratec Cinergy T XS (MT2060)",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ },
+ [EM2870_BOARD_KWORLD_350U] = {
+ .name = "Kworld 350 U DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ [EM2870_BOARD_KWORLD_355U] = {
+ .name = "Kworld 355 U DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ },
+ [EM2870_BOARD_PINNACLE_PCTV_DVB] = {
+ .name = "Pinnacle PCTV DVB-T",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ /* djh - I have serious doubts this is right... */
+ .xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_10MHZ,
+ },
+ [EM2870_BOARD_COMPRO_VIDEOMATE] = {
+ .name = "Compro, VideoMate U3",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_ABSENT, /* MT2060 */
+ },
+
+ [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
+ .name = "Terratec Hybrid XS Secam",
+ .has_msp34xx = 1,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = terratec_cinergy_USB_XS_FR_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = terratec_cinergy_USB_XS_FR_analog,
+ } },
+ },
+ [EM2884_BOARD_TERRATEC_H5] = {
+ .name = "Terratec Cinergy H5",
+ .has_dvb = 1,
+#if 0
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x41,
+ .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
+ .tuner_gpio = terratec_h5_gpio,
+#else
+ .tuner_type = TUNER_ABSENT,
+#endif
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
+ .name = "Hauppauge WinTV HVR 930C",
+ .has_dvb = 1,
+#if 0 /* FIXME: Add analog support */
+ .tuner_type = TUNER_XC5000,
+ .tuner_addr = 0x41,
+ .dvb_gpio = hauppauge_930c_digital,
+ .tuner_gpio = hauppauge_930c_gpio,
+#else
+ .tuner_type = TUNER_ABSENT,
+#endif
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2884_BOARD_CINERGY_HTC_STICK] = {
+ .name = "Terratec Cinergy HTC Stick",
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+ .tuner_type = TUNER_ABSENT,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
+ .name = "Hauppauge WinTV HVR 900",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+ .name = "Hauppauge WinTV HVR 900 (R2)",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
+ .name = "Hauppauge WinTV HVR 850",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
+ .name = "Hauppauge WinTV HVR 950",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_HAUPPAUGE,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+ .name = "Pinnacle PCTV HD Pro Stick",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
+ .name = "AMD ATI TV Wonder HD 600",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Hybrid XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ /* maybe there's a reason behind it why Terratec sells the Hybrid XS
+ as Prodigy XS with a different PID, let's keep it separated for now
+ maybe we'll need it lateron */
+ [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
+ .name = "Terratec Prodigy XS",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2820_BOARD_MSI_VOX_USB_2] = {
+ .name = "MSI VOX USB 2.0",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT |
+ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .max_range_640_480 = 1,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE4,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_TERRATEC_CINERGY_200] = {
+ .name = "Terratec Cinergy 200 USB",
+ .is_em2800 = 1,
+ .has_ir_i2c = 1,
+ .tuner_type = TUNER_LG_TALN,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_GRABBEEX_USB2800] = {
+ .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
+ .is_em2800 = 1,
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_VC211A] = {
+ .name = "Actionmaster/LinXcel/Digitus VC211A",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_ABSENT, /* Capture-only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = vc211a_enable,
+ } },
+ },
+ [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
+ .name = "Leadtek Winfast USB II",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_KWORLD_USB2800] = {
+ .name = "Kworld USB2800",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_DVC_90] = {
+ .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker "
+ "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U",
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2800_BOARD_VGEAR_POCKETTV] = {
+ .name = "V-Gear PocketTV",
+ .is_em2800 = 1,
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
+ .name = "Pixelview PlayTV Box 4 USB 2.0",
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+ .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
+ .has_snapshot_button = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .aout = EM28XX_AOUT_MONO | /* I2S */
+ EM28XX_AOUT_MASTER, /* Line out pin */
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
+ .name = "EM2860/SAA711X Reference Design",
+ .has_snapshot_button = 1,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ } },
+ },
+
+ [EM2874_BOARD_LEADERSHIP_ISDBT] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ,
+ .xclk = EM28XX_XCLK_FREQUENCY_10MHZ,
+ .name = "EM2874 Leadership ISDBT",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = leadership_reset,
+ .dvb_gpio = leadership_digital,
+ .has_dvb = 1,
+ },
+
+ [EM2880_BOARD_MSI_DIGIVOX_AD] = {
+ .name = "MSI DigiVox A/D",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ } },
+ },
+ [EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
+ .name = "MSI DigiVox A/D II",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2880_msi_digivox_ad_analog,
+ } },
+ },
+ [EM2880_BOARD_KWORLD_DVB_305U] = {
+ .name = "KWorld DVB-T 305U",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2880_BOARD_KWORLD_DVB_310U] = {
+ .name = "KWorld DVB-T 310U",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, { /* S-video has not been tested yet */
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2882_BOARD_KWORLD_ATSC_315U] = {
+ .name = "KWorld ATSC 315U HDTV TV Box",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_THOMSON_DTT761X,
+ .tuner_gpio = em2882_kworld_315u_tuner_gpio,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_SAA711X,
+ .has_dvb = 1,
+ .dvb_gpio = em2882_kworld_315u_digital,
+ .ir_codes = RC_MAP_KWORLD_315U,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
+ /* Analog mode - still not ready */
+ /*.input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = SAA7115_COMPOSITE2,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = em2882_kworld_315u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2882_kworld_315u_analog1,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = em2882_kworld_315u_analog1,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ } }, */
+ },
+ [EM2880_BOARD_EMPIRE_DUAL_TV] = {
+ .name = "Empire dual TV",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .has_dvb = 1,
+ .dvb_gpio = default_digital,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2881_BOARD_DNT_DA2_HYBRID] = {
+ .name = "DNT DA2 Hybrid",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
+ .name = "Pinnacle Hybrid Pro",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = pinnacle_hybrid_pro_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
+ } },
+ },
+ [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = {
+ .name = "Pinnacle Hybrid Pro (330e)",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2882_BOARD_KWORLD_VS_DVBT] = {
+ .name = "Kworld VS-DVB-T 323UR",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = kworld_330u_digital,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .ir_codes = RC_MAP_KWORLD_315U,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
+ .name = "Terratec Cinnergy Hybrid T USB XS (em2882)",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
+ .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = hauppauge_wintv_hvr_900_analog,
+ } },
+ },
+ [EM2882_BOARD_DIKOM_DK300] = {
+ .name = "Dikom DK300",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = dikom_dk300_digital,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = default_analog,
+ } },
+ },
+ [EM2883_BOARD_KWORLD_HYBRID_330U] = {
+ .name = "Kworld PlusTV HD Hybrid 330",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = kworld_330u_digital,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_EEPROM_ON_BOARD |
+ EM28XX_I2C_EEPROM_KEY_VALID,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = kworld_330u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = kworld_330u_analog,
+ .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = kworld_330u_analog,
+ } },
+ },
+ [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
+ .name = "Compro VideoMate ForYou/Stereo",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tvaudio_addr = 0xb0,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .adecoder = EM28XX_TVAUDIO,
+ .mute_gpio = compro_mute_gpio,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = compro_unmute_tv_gpio,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = compro_unmute_svid_gpio,
+ } },
+ },
+ [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
+ .name = "Kaiomy TVnPC U2",
+ .vchannels = 3,
+ .tuner_type = TUNER_XC2028,
+ .tuner_addr = 0x61,
+ .mts_firmware = 1,
+ .decoder = EM28XX_TVP5150,
+ .tuner_gpio = default_tuner_gpio,
+ .ir_codes = RC_MAP_KAIOMY,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ .radio = {
+ .type = EM28XX_RADIO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }
+ },
+ [EM2860_BOARD_EASYCAP] = {
+ .name = "Easy Cap Capture DC-60",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2820_BOARD_IODATA_GVMVP_SZ] = {
+ .name = "IO-DATA GV-MVP/SZ",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tuner_gpio = default_tuner_gpio,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, { /* Composite has not been tested yet */
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_VIDEO,
+ }, { /* S-video has not been tested yet */
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_GRABBY] = {
+ .name = "Terratec Grabby",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ [EM2860_BOARD_TERRATEC_AV350] = {
+ .name = "Terratec AV350",
+ .vchannels = 2,
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_TVP5150,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+ .mute_gpio = terratec_av350_mute_gpio,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AUDIO_SRC_LINE,
+ .gpio = terratec_av350_unmute_gpio,
+
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AUDIO_SRC_LINE,
+ .gpio = terratec_av350_unmute_gpio,
+ } },
+ },
+
+ [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
+ .name = "Elgato Video Capture",
+ .decoder = EM28XX_SAA711X,
+ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+
+ [EM2882_BOARD_EVGA_INDTUBE] = {
+ .name = "Evga inDtube",
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .decoder = EM28XX_TVP5150,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = evga_indtube_digital,
+ .ir_codes = RC_MAP_EVGA_INDTUBE,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = evga_indtube_analog,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = evga_indtube_analog,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ .gpio = evga_indtube_analog,
+ } },
+ },
+ /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
+ Infineon TUA6034) */
+ [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
+ .name = "Reddo DVB-C USB TV Box",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = reddo_dvb_c_usb_box,
+ .has_dvb = 1,
+ },
+ /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
+ * initially as the KWorld PlusTV 340U, then as the UB435-Q.
+ * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
+ [EM2870_BOARD_KWORLD_A340] = {
+ .name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
+ .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
+ .has_dvb = 1,
+ .dvb_gpio = kworld_a340_digital,
+ .tuner_gpio = default_tuner_gpio,
+ },
+ /* 2013:024f PCTV nanoStick T2 290e.
+ * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
+ [EM28174_BOARD_PCTV_290E] = {
+ .name = "PCTV nanoStick T2 290e",
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_290e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /* 2013:024f PCTV DVB-S2 Stick 460e
+ * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
+ [EM28174_BOARD_PCTV_460E] = {
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
+ .name = "PCTV DVB-S2 Stick (460e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_460e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ },
+ /* eb1a:5006 Honestech VIDBOX NW03
+ * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
+ [EM2860_BOARD_HT_VIDBOX_NW03] = {
+ .name = "Honestech Vidbox NW03",
+ .tuner_type = TUNER_ABSENT,
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
+ /* 1b80:e425 MaxMedia UB425-TC
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+ [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
+ .name = "MaxMedia UB425-TC",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = maxmedia_ub425_tc,
+ .has_dvb = 1,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2304:0242 PCTV QuatroStick (510e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_510E] = {
+ .name = "PCTV QuatroStick (510e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_510e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2013:0251 PCTV QuatroStick nano (520e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_520E] = {
+ .name = "PCTV QuatroStick nano (520e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_520e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+};
+const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+
+/* table of devices that work with this driver */
+struct usb_device_id em28xx_id_table[] = {
+ { USB_DEVICE(0xeb1a, 0x2750),
+ .driver_info = EM2750_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2751),
+ .driver_info = EM2750_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2800),
+ .driver_info = EM2800_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2710),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2820),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2821),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2860),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2861),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2862),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2863),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2870),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2881),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2883),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2868),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2875),
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0xe300),
+ .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
+ { USB_DEVICE(0xeb1a, 0xe303),
+ .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
+ { USB_DEVICE(0xeb1a, 0xe305),
+ .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
+ { USB_DEVICE(0xeb1a, 0xe310),
+ .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
+ { USB_DEVICE(0xeb1a, 0xa313),
+ .driver_info = EM2882_BOARD_KWORLD_ATSC_315U },
+ { USB_DEVICE(0xeb1a, 0xa316),
+ .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U },
+ { USB_DEVICE(0xeb1a, 0xe320),
+ .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II },
+ { USB_DEVICE(0xeb1a, 0xe323),
+ .driver_info = EM2882_BOARD_KWORLD_VS_DVBT },
+ { USB_DEVICE(0xeb1a, 0xe350),
+ .driver_info = EM2870_BOARD_KWORLD_350U },
+ { USB_DEVICE(0xeb1a, 0xe355),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0xeb1a, 0x2801),
+ .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
+ { USB_DEVICE(0xeb1a, 0xe357),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0xeb1a, 0xe359),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
+ { USB_DEVICE(0x1b80, 0xe302),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
+ { USB_DEVICE(0x1b80, 0xe304),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
+ { USB_DEVICE(0x0ccd, 0x0036),
+ .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+ { USB_DEVICE(0x0ccd, 0x004c),
+ .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR },
+ { USB_DEVICE(0x0ccd, 0x004f),
+ .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x005e),
+ .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0042),
+ .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
+ { USB_DEVICE(0x0ccd, 0x0043),
+ .driver_info = EM2870_BOARD_TERRATEC_XS },
+ { USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */
+ .driver_info = EM2884_BOARD_TERRATEC_H5 },
+ { USB_DEVICE(0x0ccd, 0x0084),
+ .driver_info = EM2860_BOARD_TERRATEC_AV350 },
+ { USB_DEVICE(0x0ccd, 0x0096),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x10AF),
+ .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
+ { USB_DEVICE(0x0ccd, 0x00b2),
+ .driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
+ { USB_DEVICE(0x0fd9, 0x0033),
+ .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
+ { USB_DEVICE(0x185b, 0x2870),
+ .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
+ { USB_DEVICE(0x185b, 0x2041),
+ .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU },
+ { USB_DEVICE(0x2040, 0x4200),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x4201),
+ .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
+ { USB_DEVICE(0x2040, 0x6500),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ { USB_DEVICE(0x2040, 0x6502),
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
+ { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
+ { USB_DEVICE(0x2040, 0x651f),
+ .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
+ { USB_DEVICE(0x0438, 0xb002),
+ .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
+ { USB_DEVICE(0x2001, 0xf112),
+ .driver_info = EM2820_BOARD_DLINK_USB_TV },
+ { USB_DEVICE(0x2304, 0x0207),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x0208),
+ .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
+ { USB_DEVICE(0x2304, 0x021a),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x0226),
+ .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
+ { USB_DEVICE(0x2304, 0x0227),
+ .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
+ { USB_DEVICE(0x0413, 0x6023),
+ .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
+ { USB_DEVICE(0x093b, 0xa003),
+ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x093b, 0xa005),
+ .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
+ { USB_DEVICE(0x04bb, 0x0515),
+ .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+ { USB_DEVICE(0xeb1a, 0x50a6),
+ .driver_info = EM2860_BOARD_GADMEI_UTV330 },
+ { USB_DEVICE(0x1b80, 0xa340),
+ .driver_info = EM2870_BOARD_KWORLD_A340 },
+ { USB_DEVICE(0x2013, 0x024f),
+ .driver_info = EM28174_BOARD_PCTV_290E },
+ { USB_DEVICE(0x2013, 0x024c),
+ .driver_info = EM28174_BOARD_PCTV_460E },
+ { USB_DEVICE(0x2040, 0x1605),
+ .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
+ { USB_DEVICE(0xeb1a, 0x5006),
+ .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
+ { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
+ .driver_info = EM2860_BOARD_EASYCAP },
+ { USB_DEVICE(0x1b80, 0xe425),
+ .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
+ { USB_DEVICE(0x2304, 0x0242),
+ .driver_info = EM2884_BOARD_PCTV_510E },
+ { USB_DEVICE(0x2013, 0x0251),
+ .driver_info = EM2884_BOARD_PCTV_520E },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+
+/*
+ * EEPROM hash table for devices with generic USB IDs
+ */
+static struct em28xx_hash_table em28xx_eeprom_hash[] = {
+ /* P/N: SA 60002070465 Tuner: TVF7533-MF */
+ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
+ {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
+ {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+ {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
+ {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
+ {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
+ {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
+};
+
+/* I2C devicelist hash table for devices with generic USB IDs */
+static struct em28xx_hash_table em28xx_i2c_hash[] = {
+ {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
+ {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
+ {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
+ {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
+ {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
+ {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
+ {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
+};
+
+/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
+static unsigned short saa711x_addrs[] = {
+ 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
+ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
+ I2C_CLIENT_END };
+
+static unsigned short tvp5150_addrs[] = {
+ 0xb8 >> 1,
+ 0xba >> 1,
+ I2C_CLIENT_END
+};
+
+static unsigned short msp3400_addrs[] = {
+ 0x80 >> 1,
+ 0x88 >> 1,
+ I2C_CLIENT_END
+};
+
+int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+{
+ int rc = 0;
+ struct em28xx *dev = ptr;
+
+ if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
+ return 0;
+
+ if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
+ return 0;
+
+ rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
+
+static inline void em28xx_set_model(struct em28xx *dev)
+{
+ memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+
+ /* Those are the default values for the majority of boards
+ Use those values if not specified otherwise at boards entry
+ */
+ if (!dev->board.xclk)
+ dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
+ EM28XX_XCLK_FREQUENCY_12MHZ;
+
+ if (!dev->board.i2c_speed)
+ dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_100_KHZ;
+}
+
+
+/* FIXME: Should be replaced by a proper mt9m111 driver */
+static int em28xx_initialize_mt9m111(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, }, /* reset and use defaults */
+ { 0x0d, 0x00, 0x00, },
+ { 0x0a, 0x00, 0x21, },
+ { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+
+/* FIXME: Should be replaced by a proper mt9m001 driver */
+static int em28xx_initialize_mt9m001(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, },
+ { 0x0d, 0x00, 0x00, },
+ { 0x04, 0x05, 0x00, }, /* hres = 1280 */
+ { 0x03, 0x04, 0x00, }, /* vres = 1024 */
+ { 0x20, 0x11, 0x00, },
+ { 0x06, 0x00, 0x10, },
+ { 0x2b, 0x00, 0x24, },
+ { 0x2e, 0x00, 0x24, },
+ { 0x35, 0x00, 0x24, },
+ { 0x2d, 0x00, 0x20, },
+ { 0x2c, 0x00, 0x20, },
+ { 0x09, 0x0a, 0xd4, },
+ { 0x35, 0x00, 0x57, },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+/* HINT method: webcam I2C chips
+ *
+ * This method works for webcams with Micron sensors
+ */
+static int em28xx_hint_sensor(struct em28xx *dev)
+{
+ int rc;
+ char *sensor_name;
+ unsigned char cmd;
+ __be16 version_be;
+ u16 version;
+
+ /* Micron sensor detection */
+ dev->i2c_client.addr = 0xba >> 1;
+ cmd = 0;
+ i2c_master_send(&dev->i2c_client, &cmd, 1);
+ rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
+ if (rc != 2)
+ return -EINVAL;
+
+ version = be16_to_cpu(version_be);
+ switch (version) {
+ case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
+ case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
+ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9v011";
+ dev->em28xx_sensor = EM28XX_MT9V011;
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 480;
+ /*
+ * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+ * the Silvercrest cam I have here for testing - for higher
+ * resolutions, a high clock cause horizontal artifacts, so we
+ * need to use a lower xclk frequency.
+ * Yet, it would be possible to adjust xclk depending on the
+ * desired resolution, since this affects directly the
+ * frame rate.
+ */
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+ dev->sensor_xtal = 4300000;
+
+ /* probably means GRGB 16 bit bayer */
+ dev->vinmode = 0x0d;
+ dev->vinctl = 0x00;
+
+ break;
+
+ case 0x143a: /* MT9M111 as found in the ECS G200 */
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m111";
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+ dev->em28xx_sensor = EM28XX_MT9M111;
+ em28xx_initialize_mt9m111(dev);
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 512;
+
+ dev->vinmode = 0x0a;
+ dev->vinctl = 0x00;
+
+ break;
+
+ case 0x8431:
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m001";
+ dev->em28xx_sensor = EM28XX_MT9M001;
+ em28xx_initialize_mt9m001(dev);
+ dev->sensor_xres = 1280;
+ dev->sensor_yres = 1024;
+
+ /* probably means BGGR 16 bit bayer */
+ dev->vinmode = 0x0c;
+ dev->vinctl = 0x00;
+
+ break;
+ default:
+ printk("Unknown Micron Sensor 0x%04x\n", version);
+ return -EINVAL;
+ }
+
+ /* Setup webcam defaults */
+ em28xx_pre_card_setup(dev);
+
+ em28xx_errdev("Sensor is %s, using model %s entry.\n",
+ sensor_name, em28xx_boards[dev->model].name);
+
+ return 0;
+}
+
+/* Since em28xx_pre_card_setup() requires a proper dev->model,
+ * this won't work for boards with generic PCI IDs
+ */
+static void em28xx_pre_card_setup(struct em28xx *dev)
+{
+ /* Set the initial XCLK and I2C clock values based on the board
+ definition */
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
+ if (!dev->board.is_em2800)
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ msleep(50);
+
+ /* request some modules */
+ switch (dev->model) {
+ case EM2861_BOARD_PLEXTOR_PX_TV100U:
+ /* Sets the msp34xx I2S speed */
+ dev->i2s_speed = 2048000;
+ break;
+ case EM2861_BOARD_KWORLD_PVRTV_300U:
+ case EM2880_BOARD_KWORLD_DVB_305U:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
+ msleep(10);
+ break;
+ case EM2870_BOARD_COMPRO_VIDEOMATE:
+ /* TODO: someone can do some cleanup here...
+ not everything's needed */
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
+ mdelay(70);
+ break;
+ case EM2870_BOARD_TERRATEC_XS_MT2060:
+ /* this device needs some gpio writes to get the DVB-T
+ demod work */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ break;
+ case EM2870_BOARD_PINNACLE_PCTV_DVB:
+ /* this device needs some gpio writes to get the
+ DVB-T demod work */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
+ mdelay(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ mdelay(70);
+ break;
+ case EM2820_BOARD_GADMEI_UTV310:
+ case EM2820_BOARD_MSI_VOX_USB_2:
+ /* enables audio for that devices */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ break;
+
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
+ msleep(10);
+ em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
+ msleep(10);
+ break;
+
+ case EM2860_BOARD_KAIOMY_TVNPC_U2:
+ em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
+ em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
+ em28xx_write_regs(dev, 0x0d, "\x42", 1);
+ em28xx_write_regs(dev, 0x08, "\xfd", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\xff", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\x7f", 1);
+ msleep(10);
+ em28xx_write_regs(dev, 0x08, "\x6b", 1);
+
+ break;
+ case EM2860_BOARD_EASYCAP:
+ em28xx_write_regs(dev, 0x08, "\xf8", 1);
+ break;
+
+ case EM2820_BOARD_IODATA_GVMVP_SZ:
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ msleep(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
+ msleep(70);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ msleep(70);
+ break;
+ }
+
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+
+ /* Unlock device */
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+}
+
+static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
+{
+ memset(ctl, 0, sizeof(*ctl));
+
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ ctl->max_len = 64;
+ ctl->mts = em28xx_boards[dev->model].mts_firmware;
+
+ switch (dev->model) {
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+ ctl->demod = XC3028_FE_DEFAULT;
+ break;
+ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+ ctl->demod = XC3028_FE_DEFAULT;
+ ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+ break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ /* FIXME: Better to specify the needed IF */
+ ctl->demod = XC3028_FE_DEFAULT;
+ break;
+ case EM2883_BOARD_KWORLD_HYBRID_330U:
+ case EM2882_BOARD_DIKOM_DK300:
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ ctl->demod = XC3028_FE_CHINA;
+ ctl->fname = XC2028_DEFAULT_FIRMWARE;
+ break;
+ case EM2882_BOARD_EVGA_INDTUBE:
+ ctl->demod = XC3028_FE_CHINA;
+ ctl->fname = XC3028L_DEFAULT_FIRMWARE;
+ break;
+ default:
+ ctl->demod = XC3028_FE_OREN538;
+ }
+}
+
+static void em28xx_tuner_setup(struct em28xx *dev)
+{
+ struct tuner_setup tun_setup;
+ struct v4l2_frequency f;
+
+ if (dev->tuner_type == TUNER_ABSENT)
+ return;
+
+ memset(&tun_setup, 0, sizeof(tun_setup));
+
+ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+ tun_setup.tuner_callback = em28xx_tuner_callback;
+
+ if (dev->board.radio.type) {
+ tun_setup.type = dev->board.radio.type;
+ tun_setup.addr = dev->board.radio_addr;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+ }
+
+ if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (dev->tda9887_conf) {
+ struct v4l2_priv_tun_config tda9887_cfg;
+
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (dev->tuner_type == TUNER_XC2028) {
+ struct v4l2_priv_tun_config xc2028_cfg;
+ struct xc2028_ctrl ctl;
+
+ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
+ memset(&ctl, 0, sizeof(ctl));
+
+ em28xx_setup_xc3028(dev, &ctl);
+
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
+ }
+
+ /* configure tuner */
+ f.tuner = 0;
+ f.type = V4L2_TUNER_ANALOG_TV;
+ f.frequency = 9076; /* just a magic number */
+ dev->ctl_freq = f.frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+}
+
+static int em28xx_hint_board(struct em28xx *dev)
+{
+ int i;
+
+ /* HINT method: EEPROM
+ *
+ * This method works only for boards with eeprom.
+ * Uses a hash of all eeprom bytes. The hash should be
+ * unique for a vendor/tuner pair.
+ * There are a high chance that tuners for different
+ * video standards produce different hashes.
+ */
+ for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
+ if (dev->hash == em28xx_eeprom_hash[i].hash) {
+ dev->model = em28xx_eeprom_hash[i].model;
+ dev->tuner_type = em28xx_eeprom_hash[i].tuner;
+
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on eeprom hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ /* HINT method: I2C attached devices
+ *
+ * This method works for all boards.
+ * Uses a hash of i2c scanned devices.
+ * Devices with the same i2c attached chips will
+ * be considered equal.
+ * This method is less precise than the eeprom one.
+ */
+
+ /* user did not request i2c scanning => do it now */
+ if (!dev->i2c_hash)
+ em28xx_do_i2c_scan(dev);
+
+ for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
+ if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
+ dev->model = em28xx_i2c_hash[i].model;
+ dev->tuner_type = em28xx_i2c_hash[i].tuner;
+ em28xx_errdev("Your board has no unique USB ID.\n");
+ em28xx_errdev("A hint were successfully done, "
+ "based on i2c devicelist hash.\n");
+ em28xx_errdev("This method is not 100%% failproof.\n");
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+ return 0;
+ }
+ }
+
+ em28xx_errdev("Your board has no unique USB ID and thus need a "
+ "hint to be detected.\n");
+ em28xx_errdev("You may try to use card=<n> insmod option to "
+ "workaround that.\n");
+ em28xx_errdev("Please send an email with this log to:\n");
+ em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
+ em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
+
+ em28xx_errdev("Here is a list of valid choices for the card=<n>"
+ " insmod option:\n");
+ for (i = 0; i < em28xx_bcount; i++) {
+ em28xx_errdev(" card=%d -> %s\n",
+ i, em28xx_boards[i].name);
+ }
+ return -1;
+}
+
+static void em28xx_card_setup(struct em28xx *dev)
+{
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam) {
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
+ else
+ dev->progressive = 1;
+ }
+
+ if (!dev->board.is_webcam) {
+ switch (dev->model) {
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ /*
+ * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the K-WORLD
+ * and if it is found then we decide that we do not have
+ * a DIGIVOX and reset the device to the K-WORLD instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ if (em28xx_hint_board(dev) < 0)
+ em28xx_errdev("Board not discovered\n");
+ else {
+ em28xx_set_model(dev);
+ em28xx_pre_card_setup(dev);
+ }
+ break;
+ default:
+ em28xx_set_model(dev);
+ }
+ }
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
+
+ dev->tuner_type = em28xx_boards[dev->model].tuner_type;
+ if (em28xx_boards[dev->model].tuner_addr)
+ dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
+
+ if (em28xx_boards[dev->model].tda9887_conf)
+ dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
+
+ /* request some modules */
+ switch (dev->model) {
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ {
+ struct tveeprom tv;
+#if defined(CONFIG_MODULES) && defined(MODULE)
+ request_module("tveeprom");
+#endif
+ /* Call first TVeeprom */
+
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
+
+ dev->tuner_type = tv.tuner_type;
+
+ if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
+ dev->i2s_speed = 2048000;
+ dev->board.has_msp34xx = 1;
+ }
+ break;
+ }
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ em28xx_write_reg(dev, 0x0d, 0x42);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ msleep(10);
+ break;
+ case EM2820_BOARD_KWORLD_PVRTV2800RF:
+ /* GPIO enables sound on KWORLD PVR TV 2800RF */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
+ break;
+ case EM2820_BOARD_UNKNOWN:
+ case EM2800_BOARD_UNKNOWN:
+ /*
+ * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the K-WORLD
+ * and if it is found then we decide that we do not have
+ * a DIGIVOX and reset the device to the K-WORLD instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2880_BOARD_MSI_DIGIVOX_AD:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
+
+ /* In cases where we had to use a board hint, the call to
+ em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ so make the call now so the analog GPIOs are set properly
+ before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ break;
+
+/*
+ * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
+ *
+ * This occurs because they share identical USB vendor and
+ * product IDs.
+ *
+ * What we do here is look up the EEPROM hash of the Dikom
+ * and if it is found then we decide that we do not have
+ * a Kworld and reset the device to the Dikom instead.
+ *
+ * This solution is only valid if they do not share eeprom
+ * hash identities which has not been determined as yet.
+ */
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ if (!em28xx_hint_board(dev))
+ em28xx_set_model(dev);
+
+ /* In cases where we had to use a board hint, the call to
+ em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
+ so make the call now so the analog GPIOs are set properly
+ before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ break;
+ }
+
+ if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
+ em28xx_errdev("\n\n");
+ em28xx_errdev("The support for this board weren't "
+ "valid yet.\n");
+ em28xx_errdev("Please send a report of having this working\n");
+ em28xx_errdev("not to V4L mailing list (and/or to other "
+ "addresses)\n\n");
+ }
+
+ /* Allow override tuner type by a module parameter */
+ if (tuner >= 0)
+ dev->tuner_type = tuner;
+
+ /* request some modules */
+ if (dev->board.has_msp34xx)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "msp3400", 0, msp3400_addrs);
+
+ if (dev->board.decoder == EM28XX_SAA711X)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa7115_auto", 0, saa711x_addrs);
+
+ if (dev->board.decoder == EM28XX_TVP5150)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvp5150", 0, tvp5150_addrs);
+
+ if (dev->em28xx_sensor == EM28XX_MT9V011) {
+ struct mt9v011_platform_data pdata;
+ struct i2c_board_info mt9v011_info = {
+ .type = "mt9v011",
+ .addr = 0xba >> 1,
+ .platform_data = &pdata,
+ };
+
+ pdata.xtal = dev->sensor_xtal;
+ v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
+ &mt9v011_info, NULL);
+ }
+
+
+ if (dev->board.adecoder == EM28XX_TVAUDIO)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvaudio", dev->board.tvaudio_addr, NULL);
+
+ if (dev->board.tuner_type != TUNER_ABSENT) {
+ int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
+
+ if (dev->board.radio.type)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->board.radio_addr, NULL);
+
+ if (has_demod)
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ if (dev->tuner_addr == 0) {
+ enum v4l2_i2c_tuner_type type =
+ has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "tuner",
+ 0, v4l2_i2c_tuner_addrs(type));
+
+ if (sd)
+ dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
+ } else {
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tuner", dev->tuner_addr, NULL);
+ }
+ }
+
+ em28xx_tuner_setup(dev);
+}
+
+
+static void request_module_async(struct work_struct *work)
+{
+ struct em28xx *dev = container_of(work,
+ struct em28xx, request_module_wk);
+
+ /*
+ * The em28xx extensions can be modules or builtin. If the
+ * modules are already loaded or are built in, those extensions
+ * can be initialised right now. Otherwise, the module init
+ * code will do it.
+ */
+ em28xx_init_extension(dev);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+ if (dev->has_audio_class)
+ request_module("snd-usb-audio");
+ else if (dev->has_alsa_audio)
+ request_module("em28xx-alsa");
+
+ if (dev->board.has_dvb)
+ request_module("em28xx-dvb");
+ if (dev->board.ir_codes && !disable_ir)
+ request_module("em28xx-rc");
+#endif /* CONFIG_MODULES */
+}
+
+static void request_modules(struct em28xx *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct em28xx *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+
+/*
+ * em28xx_release_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconnected or at module unload
+*/
+void em28xx_release_resources(struct em28xx *dev)
+{
+ /*FIXME: I2C IR should be disconnected */
+
+ em28xx_release_analog_resources(dev);
+
+ em28xx_i2c_unregister(dev);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ usb_put_dev(dev->udev);
+
+ /* Mark device as unused */
+ clear_bit(dev->devno, &em28xx_devused);
+};
+
+/*
+ * em28xx_init_dev()
+ * allocates and inits the device structs, registers i2c bus and v4l device
+ */
+static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
+ struct usb_interface *interface,
+ int minor)
+{
+ int retval;
+
+ dev->udev = udev;
+ mutex_init(&dev->ctrl_urb_lock);
+ spin_lock_init(&dev->slock);
+
+ dev->em28xx_write_regs = em28xx_write_regs;
+ dev->em28xx_read_reg = em28xx_read_reg;
+ dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
+ dev->em28xx_write_regs_req = em28xx_write_regs_req;
+ dev->em28xx_read_reg_req = em28xx_read_reg_req;
+ dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+
+ em28xx_set_model(dev);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
+
+ dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
+ retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+ if (retval > 0) {
+ dev->chip_id = retval;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2800:
+ em28xx_info("chip ID is em2800\n");
+ break;
+ case CHIP_ID_EM2710:
+ em28xx_info("chip ID is em2710\n");
+ break;
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820 (or em2710)\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM28174:
+ em28xx_info("chip ID is em28174\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2883:
+ em28xx_info("chip ID is em2882/em2883\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2884:
+ em28xx_info("chip ID is em2884\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ default:
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+ }
+ }
+
+ if (dev->is_audio_only) {
+ retval = em28xx_audio_setup(dev);
+ if (retval)
+ return -ENODEV;
+ em28xx_init_extension(dev);
+
+ return 0;
+ }
+
+ /* Prepopulate cached GPO register content */
+ retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (retval >= 0)
+ dev->reg_gpo = retval;
+
+ em28xx_pre_card_setup(dev);
+
+ if (!dev->board.is_em2800) {
+ /* Resets I2C speed */
+ retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg failed!"
+ " retval [%d]\n",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
+ if (retval < 0) {
+ em28xx_errdev("Call to v4l2_device_register() failed!\n");
+ return retval;
+ }
+
+ /* register i2c bus */
+ retval = em28xx_i2c_register(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
+ __func__, retval);
+ goto unregister_dev;
+ }
+
+ /*
+ * Default format, used for tvp5150 or saa711x output formats
+ */
+ dev->vinmode = 0x10;
+ dev->vinctl = EM28XX_VINCTRL_INTERLACED |
+ EM28XX_VINCTRL_CCIR656_ENABLE;
+
+ /* Do board specific init and eeprom reading */
+ em28xx_card_setup(dev);
+
+ /* Configure audio */
+ retval = em28xx_audio_setup(dev);
+ if (retval < 0) {
+ em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
+ __func__, retval);
+ goto fail;
+ }
+
+ /* wake i2c devices */
+ em28xx_wake_i2c(dev);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vbiq.active);
+
+ if (dev->board.has_msp34xx) {
+ /* Send a reset to other chips via gpio */
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(1) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
+ }
+ msleep(3);
+
+ retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
+ if (retval < 0) {
+ em28xx_errdev("%s: em28xx_write_reg - "
+ "msp34xx(2) failed! error [%d]\n",
+ __func__, retval);
+ goto fail;
+ }
+ msleep(3);
+ }
+
+ retval = em28xx_register_analog_devices(dev);
+ if (retval < 0) {
+ goto fail;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ return 0;
+
+fail:
+ em28xx_i2c_unregister(dev);
+
+unregister_dev:
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ return retval;
+}
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/*
+ * em28xx_usb_probe()
+ * checks for supported devices
+ */
+static int em28xx_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev;
+ struct em28xx *dev = NULL;
+ int retval;
+ bool has_audio = false, has_video = false, has_dvb = false;
+ int i, nr;
+ const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
+ char *speed;
+
+ udev = usb_get_dev(interface_to_usbdev(interface));
+
+ /* Check to see next free device and mark as used */
+ do {
+ nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
+ if (nr >= EM28XX_MAXBOARDS) {
+ /* No free device slots */
+ printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
+ EM28XX_MAXBOARDS);
+ retval = -ENOMEM;
+ goto err_no_slot;
+ }
+ } while (test_and_set_bit(nr, &em28xx_devused));
+
+ /* Don't register audio interfaces */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
+ "interface %i, class %i\n",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting[0].desc.bInterfaceClass);
+
+ retval = -ENODEV;
+ goto err;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ em28xx_err(DRIVER_NAME ": out of memory!\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ /* compute alternate max packet sizes */
+ dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) *
+ interface->num_altsetting, GFP_KERNEL);
+ if (dev->alt_max_pkt_size == NULL) {
+ em28xx_errdev("out of memory!\n");
+ kfree(dev);
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ /* Get endpoints */
+ for (i = 0; i < interface->num_altsetting; i++) {
+ int ep;
+
+ for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
+ const struct usb_endpoint_descriptor *e;
+ int sizedescr, size;
+
+ e = &interface->altsetting[i].endpoint[ep].desc;
+
+ sizedescr = le16_to_cpu(e->wMaxPacketSize);
+ size = sizedescr & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(sizedescr);
+
+ if (usb_endpoint_xfer_isoc(e) &&
+ usb_endpoint_dir_in(e)) {
+ switch (e->bEndpointAddress) {
+ case EM28XX_EP_AUDIO:
+ has_audio = true;
+ break;
+ case EM28XX_EP_ANALOG:
+ has_video = true;
+ dev->alt_max_pkt_size[i] = size;
+ break;
+ case EM28XX_EP_DIGITAL:
+ has_dvb = true;
+ if (size > dev->dvb_max_pkt_size) {
+ dev->dvb_max_pkt_size = size;
+ dev->dvb_alt = i;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (!(has_audio || has_video || has_dvb)) {
+ retval = -ENODEV;
+ goto err_free;
+ }
+
+ 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";
+ }
+
+ printk(KERN_INFO DRIVER_NAME
+ ": New device %s %s @ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ interface->altsetting->desc.bInterfaceNumber);
+
+ if (has_audio)
+ printk(KERN_INFO DRIVER_NAME
+ ": Audio Vendor Class interface %i found\n",
+ ifnum);
+ if (has_video)
+ printk(KERN_INFO DRIVER_NAME
+ ": Video interface %i found\n",
+ ifnum);
+ if (has_dvb)
+ printk(KERN_INFO DRIVER_NAME
+ ": DVB interface %i found\n",
+ ifnum);
+
+ /*
+ * Make sure we have 480 Mbps of bandwidth, otherwise things like
+ * video stream wouldn't likely work, since 12 Mbps is generally
+ * not enough even for most Digital TV streams.
+ */
+ if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
+ printk(DRIVER_NAME ": Device initialization failed.\n");
+ printk(DRIVER_NAME ": Device must be connected to a high-speed"
+ " USB 2.0 port.\n");
+ retval = -ENODEV;
+ goto err_free;
+ }
+
+ snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
+ dev->devno = nr;
+ dev->model = id->driver_info;
+ dev->alt = -1;
+ dev->is_audio_only = has_audio && !(has_video || has_dvb);
+ dev->has_alsa_audio = has_audio;
+ dev->audio_ifnum = ifnum;
+
+ /* Checks if audio is provided by some interface */
+ for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
+ struct usb_interface *uif = udev->config->interface[i];
+ if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
+ dev->has_audio_class = 1;
+ break;
+ }
+ }
+
+ dev->num_alt = interface->num_altsetting;
+
+ if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
+ dev->model = card[nr];
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, dev);
+
+ /* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
+ retval = em28xx_init_dev(dev, udev, interface, nr);
+ if (retval) {
+ goto unlock_and_free;
+ }
+
+ if (has_dvb) {
+ /* pre-allocate DVB isoc transfer buffers */
+ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS,
+ EM28XX_DVB_NUM_BUFS,
+ dev->dvb_max_pkt_size);
+ if (retval) {
+ goto unlock_and_free;
+ }
+ }
+
+ request_modules(dev);
+
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+unlock_and_free:
+ mutex_unlock(&dev->lock);
+
+err_free:
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+
+err:
+ clear_bit(nr, &em28xx_devused);
+
+err_no_slot:
+ usb_put_dev(udev);
+ return retval;
+}
+
+/*
+ * em28xx_usb_disconnect()
+ * called when the device gets disconnected
+ * video device will be unregistered on v4l2_close in case it is still open
+ */
+static void em28xx_usb_disconnect(struct usb_interface *interface)
+{
+ struct em28xx *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (!dev)
+ return;
+
+ if (dev->is_audio_only) {
+ mutex_lock(&dev->lock);
+ em28xx_close_extension(dev);
+ mutex_unlock(&dev->lock);
+ return;
+ }
+
+ em28xx_info("disconnecting %s\n", dev->vdev->name);
+
+ flush_request_modules(dev);
+
+ /* wait until all current v4l2 io is finished then deallocate
+ resources */
+ mutex_lock(&dev->lock);
+
+ v4l2_device_disconnect(&dev->v4l2_dev);
+
+ if (dev->users) {
+ em28xx_warn
+ ("device %s is open! Deregistration and memory "
+ "deallocation are deferred on close.\n",
+ video_device_node_name(dev->vdev));
+
+ dev->state |= DEV_MISCONFIGURED;
+ em28xx_uninit_isoc(dev, dev->mode);
+ dev->state |= DEV_DISCONNECTED;
+ } else {
+ dev->state |= DEV_DISCONNECTED;
+ em28xx_release_resources(dev);
+ }
+
+ /* free DVB isoc buffers */
+ em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
+
+ mutex_unlock(&dev->lock);
+
+ em28xx_close_extension(dev);
+
+ if (!dev->users) {
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+ }
+}
+
+static struct usb_driver em28xx_usb_driver = {
+ .name = "em28xx",
+ .probe = em28xx_usb_probe,
+ .disconnect = em28xx_usb_disconnect,
+ .id_table = em28xx_id_table,
+};
+
+module_usb_driver(em28xx_usb_driver);
diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c
new file mode 100644
index 000000000000..bed07a6c33f8
--- /dev/null
+++ b/drivers/media/usb/em28xx/em28xx-core.c
@@ -0,0 +1,1260 @@
+/*
+ em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/vmalloc.h>
+#include <sound/ac97_codec.h>
+#include <media/v4l2-common.h>
+
+#include "em28xx.h"
+
+/* #define ENABLE_DEBUG_ISOC_FRAMES */
+
+static unsigned int core_debug;
+module_param(core_debug, int, 0644);
+MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
+
+#define em28xx_coredbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int reg_debug;
+module_param(reg_debug, int, 0644);
+MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
+
+#define em28xx_regdbg(fmt, arg...) do {\
+ if (reg_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static int alt;
+module_param(alt, int, 0644);
+MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
+
+static unsigned int disable_vbi;
+module_param(disable_vbi, int, 0644);
+MODULE_PARM_DESC(disable_vbi, "disable vbi support");
+
+/* FIXME */
+#define em28xx_isocdbg(fmt, arg...) do {\
+ if (core_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+/*
+ * em28xx_read_reg_req()
+ * reads data from the usb device specifying bRequest
+ */
+int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
+ char *buf, int len)
+{
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if (len > URB_MAX_CTRL_SIZE)
+ return -EINVAL;
+
+ if (reg_debug) {
+ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+ }
+
+ mutex_lock(&dev->ctrl_urb_lock);
+ ret = usb_control_msg(dev->udev, pipe, req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0000, reg, dev->urb_buf, len, HZ);
+ if (ret < 0) {
+ if (reg_debug)
+ printk(" failed!\n");
+ mutex_unlock(&dev->ctrl_urb_lock);
+ return ret;
+ }
+
+ if (len)
+ memcpy(buf, dev->urb_buf, len);
+
+ mutex_unlock(&dev->ctrl_urb_lock);
+
+ if (reg_debug) {
+ int byte;
+
+ printk("<<<");
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
+ printk("\n");
+ }
+
+ return ret;
+}
+
+/*
+ * em28xx_read_reg_req()
+ * reads data from the usb device specifying bRequest
+ */
+int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
+{
+ int ret;
+ u8 val;
+
+ ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+int em28xx_read_reg(struct em28xx *dev, u16 reg)
+{
+ return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg);
+}
+EXPORT_SYMBOL_GPL(em28xx_read_reg);
+
+/*
+ * em28xx_write_regs_req()
+ * sends data to the usb device, specifying bRequest
+ */
+int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
+ int len)
+{
+ int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
+
+ if (dev->state & DEV_DISCONNECTED)
+ return -ENODEV;
+
+ if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
+ return -EINVAL;
+
+ if (reg_debug) {
+ int byte;
+
+ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ req, 0, 0,
+ reg & 0xff, reg >> 8,
+ len & 0xff, len >> 8);
+
+ for (byte = 0; byte < len; byte++)
+ printk(" %02x", (unsigned char)buf[byte]);
+ printk("\n");
+ }
+
+ mutex_lock(&dev->ctrl_urb_lock);
+ memcpy(dev->urb_buf, buf, len);
+ ret = usb_control_msg(dev->udev, pipe, req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x0000, reg, dev->urb_buf, len, HZ);
+ mutex_unlock(&dev->ctrl_urb_lock);
+
+ if (dev->wait_after_write)
+ msleep(dev->wait_after_write);
+
+ return ret;
+}
+
+int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
+{
+ int rc;
+
+ rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
+
+ /* Stores GPO/GPIO values at the cache, if changed
+ Only write values should be stored, since input on a GPIO
+ register will return the input bits.
+ Not sure what happens on reading GPO register.
+ */
+ if (rc >= 0) {
+ if (reg == dev->reg_gpo_num)
+ dev->reg_gpo = buf[0];
+ else if (reg == dev->reg_gpio_num)
+ dev->reg_gpio = buf[0];
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_write_regs);
+
+/* Write a single register */
+int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
+{
+ return em28xx_write_regs(dev, reg, &val, 1);
+}
+EXPORT_SYMBOL_GPL(em28xx_write_reg);
+
+/*
+ * em28xx_write_reg_bits()
+ * sets only some bits (specified by bitmask) of a register, by first reading
+ * the actual value
+ */
+int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
+ u8 bitmask)
+{
+ int oldval;
+ u8 newval;
+
+ /* Uses cache for gpo/gpio registers */
+ if (reg == dev->reg_gpo_num)
+ oldval = dev->reg_gpo;
+ else if (reg == dev->reg_gpio_num)
+ oldval = dev->reg_gpio;
+ else
+ oldval = em28xx_read_reg(dev, reg);
+
+ if (oldval < 0)
+ return oldval;
+
+ newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
+
+ return em28xx_write_regs(dev, reg, &newval, 1);
+}
+EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
+
+/*
+ * em28xx_is_ac97_ready()
+ * Checks if ac97 is ready
+ */
+static int em28xx_is_ac97_ready(struct em28xx *dev)
+{
+ int ret, i;
+
+ /* Wait up to 50 ms for AC97 command to complete */
+ for (i = 0; i < 10; i++, msleep(5)) {
+ ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & 0x01))
+ return 0;
+ }
+
+ em28xx_warn("AC97 command still being executed: not handled properly!\n");
+ return -EBUSY;
+}
+
+/*
+ * em28xx_read_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_read_ac97(struct em28xx *dev, u8 reg)
+{
+ int ret;
+ u8 addr = (reg & 0x7f) | 0x80;
+ u16 val;
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
+ (u8 *)&val, sizeof(val));
+
+ if (ret < 0)
+ return ret;
+ return le16_to_cpu(val);
+}
+EXPORT_SYMBOL_GPL(em28xx_read_ac97);
+
+/*
+ * em28xx_write_ac97()
+ * write a 16 bit value to the specified AC97 address (LSB first!)
+ */
+int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
+{
+ int ret;
+ u8 addr = reg & 0x7f;
+ __le16 value;
+
+ value = cpu_to_le16(val);
+
+ ret = em28xx_is_ac97_ready(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_write_ac97);
+
+struct em28xx_vol_itable {
+ enum em28xx_amux mux;
+ u8 reg;
+};
+
+static struct em28xx_vol_itable inputs[] = {
+ { EM28XX_AMUX_VIDEO, AC97_VIDEO },
+ { EM28XX_AMUX_LINE_IN, AC97_LINE },
+ { EM28XX_AMUX_PHONE, AC97_PHONE },
+ { EM28XX_AMUX_MIC, AC97_MIC },
+ { EM28XX_AMUX_CD, AC97_CD },
+ { EM28XX_AMUX_AUX, AC97_AUX },
+ { EM28XX_AMUX_PCM_OUT, AC97_PCM },
+};
+
+static int set_ac97_input(struct em28xx *dev)
+{
+ int ret, i;
+ enum em28xx_amux amux = dev->ctl_ainput;
+
+ /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
+ em28xx should point to LINE IN, while AC97 should use VIDEO
+ */
+ if (amux == EM28XX_AMUX_VIDEO2)
+ amux = EM28XX_AMUX_VIDEO;
+
+ /* Mute all entres but the one that were selected */
+ for (i = 0; i < ARRAY_SIZE(inputs); i++) {
+ if (amux == inputs[i].mux)
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
+ else
+ ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
+
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ inputs[i].reg);
+ }
+ return 0;
+}
+
+static int em28xx_set_audio_source(struct em28xx *dev)
+{
+ int ret;
+ u8 input;
+
+ if (dev->board.is_em2800) {
+ if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
+ input = EM2800_AUDIO_SRC_TUNER;
+ else
+ input = EM2800_AUDIO_SRC_LINE;
+
+ ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (dev->board.has_msp34xx)
+ input = EM28XX_AUDIO_SRC_TUNER;
+ else {
+ switch (dev->ctl_ainput) {
+ case EM28XX_AMUX_VIDEO:
+ input = EM28XX_AUDIO_SRC_TUNER;
+ break;
+ default:
+ input = EM28XX_AUDIO_SRC_LINE;
+ break;
+ }
+ }
+
+ if (dev->board.mute_gpio && dev->mute)
+ em28xx_gpio_set(dev, dev->board.mute_gpio);
+ else
+ em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
+ if (ret < 0)
+ return ret;
+ msleep(5);
+
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ break;
+ default:
+ ret = set_ac97_input(dev);
+ }
+
+ return ret;
+}
+
+struct em28xx_vol_otable {
+ enum em28xx_aout mux;
+ u8 reg;
+};
+
+static const struct em28xx_vol_otable outputs[] = {
+ { EM28XX_AOUT_MASTER, AC97_MASTER },
+ { EM28XX_AOUT_LINE, AC97_HEADPHONE },
+ { EM28XX_AOUT_MONO, AC97_MASTER_MONO },
+ { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER },
+ { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER },
+};
+
+int em28xx_audio_analog_set(struct em28xx *dev)
+{
+ int ret, i;
+ u8 xclk;
+
+ if (!dev->audio_mode.has_audio)
+ return 0;
+
+ /* It is assumed that all devices use master volume for output.
+ It would be possible to use also line output.
+ */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ /* Mute all outputs */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+ }
+
+ xclk = dev->board.xclk & 0x7f;
+ if (!dev->mute)
+ xclk |= EM28XX_XCLK_AUDIO_UNMUTE;
+
+ ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
+ if (ret < 0)
+ return ret;
+ msleep(10);
+
+ /* Selects the proper audio input */
+ ret = em28xx_set_audio_source(dev);
+
+ /* Sets volume */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ int vol;
+
+ em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200);
+ em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031);
+ em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80);
+
+ /* LSB: left channel - both channels with the same level */
+ vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
+
+ /* Mute device, if needed */
+ if (dev->mute)
+ vol |= 0x8000;
+
+ /* Sets volume */
+ for (i = 0; i < ARRAY_SIZE(outputs); i++) {
+ if (dev->ctl_aoutput & outputs[i].mux)
+ ret = em28xx_write_ac97(dev, outputs[i].reg,
+ vol);
+ if (ret < 0)
+ em28xx_warn("couldn't setup AC97 register %d\n",
+ outputs[i].reg);
+ }
+
+ if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
+ int sel = ac97_return_record_select(dev->ctl_aoutput);
+
+ /* Use the same input for both left and right
+ channels */
+ sel |= (sel << 8);
+
+ em28xx_write_ac97(dev, AC97_REC_SEL, sel);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
+
+int em28xx_audio_setup(struct em28xx *dev)
+{
+ int vid1, vid2, feat, cfg;
+ u32 vid;
+
+ if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
+ || dev->chip_id == CHIP_ID_EM28174) {
+ /* Digital only device - don't load any alsa module */
+ dev->audio_mode.has_audio = false;
+ dev->has_audio_class = false;
+ dev->has_alsa_audio = false;
+ return 0;
+ }
+
+ dev->audio_mode.has_audio = true;
+
+ /* See how this device is configured */
+ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
+ em28xx_info("Config register raw data: 0x%02x\n", cfg);
+ if (cfg < 0) {
+ /* Register read error? */
+ cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
+ /* The device doesn't have vendor audio at all */
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
+ return 0;
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
+ em28xx_info("I2S Audio (3 sample rates)\n");
+ dev->audio_mode.i2s_3rates = 1;
+ } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
+ EM28XX_CHIPCFG_I2S_5_SAMPRATES) {
+ em28xx_info("I2S Audio (5 sample rates)\n");
+ dev->audio_mode.i2s_5rates = 1;
+ }
+
+ if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
+ /* Skip the code that does AC97 vendor detection */
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ goto init_audio;
+ }
+
+ dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
+
+ vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
+ if (vid1 < 0) {
+ /*
+ * Device likely doesn't support AC97
+ * Note: (some) em2800 devices without eeprom reports 0x91 on
+ * CHIPCFG register, even not having an AC97 chip
+ */
+ em28xx_warn("AC97 chip type couldn't be determined\n");
+ dev->audio_mode.ac97 = EM28XX_NO_AC97;
+ dev->has_alsa_audio = false;
+ dev->audio_mode.has_audio = false;
+ goto init_audio;
+ }
+
+ vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
+ if (vid2 < 0)
+ goto init_audio;
+
+ vid = vid1 << 16 | vid2;
+
+ dev->audio_mode.ac97_vendor_id = vid;
+ em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
+
+ feat = em28xx_read_ac97(dev, AC97_RESET);
+ if (feat < 0)
+ goto init_audio;
+
+ dev->audio_mode.ac97_feat = feat;
+ em28xx_warn("AC97 features = 0x%04x\n", feat);
+
+ /* Try to identify what audio processor we have */
+ if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90))
+ dev->audio_mode.ac97 = EM28XX_AC97_EM202;
+ else if ((vid >> 8) == 0x838476)
+ dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
+
+init_audio:
+ /* Reports detected AC97 processor */
+ switch (dev->audio_mode.ac97) {
+ case EM28XX_NO_AC97:
+ em28xx_info("No AC97 audio processor\n");
+ break;
+ case EM28XX_AC97_EM202:
+ em28xx_info("Empia 202 AC97 audio processor detected\n");
+ break;
+ case EM28XX_AC97_SIGMATEL:
+ em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
+ dev->audio_mode.ac97_vendor_id & 0xff);
+ break;
+ case EM28XX_AC97_OTHER:
+ em28xx_warn("Unknown AC97 audio processor detected!\n");
+ break;
+ default:
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+EXPORT_SYMBOL_GPL(em28xx_audio_setup);
+
+int em28xx_colorlevels_set_default(struct em28xx *dev)
+{
+ em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */
+ em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */
+ em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */
+ em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00);
+
+ em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
+ em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
+ em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
+ em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
+ return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
+}
+
+int em28xx_capture_start(struct em28xx *dev, int start)
+{
+ int rc;
+
+ if (dev->chip_id == CHIP_ID_EM2874 ||
+ dev->chip_id == CHIP_ID_EM2884 ||
+ dev->chip_id == CHIP_ID_EM28174) {
+ /* The Transport Stream Enable Register moved in em2874 */
+ if (!start) {
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ 0x00,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+ /* Enable Transport Stream */
+ rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE,
+ EM2874_TS1_CAPTURE_ENABLE);
+ return rc;
+ }
+
+
+ /* FIXME: which is the best order? */
+ /* video registers are sampled by VREF */
+ rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
+ start ? 0x10 : 0x00, 0x10);
+ if (rc < 0)
+ return rc;
+
+ if (!start) {
+ /* disable video capture */
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
+ return rc;
+ }
+
+ if (dev->board.is_webcam)
+ rc = em28xx_write_reg(dev, 0x13, 0x0c);
+
+ /* enable video capture */
+ rc = em28xx_write_reg(dev, 0x48, 0x00);
+
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+
+ msleep(6);
+
+ return rc;
+}
+
+int em28xx_vbi_supported(struct em28xx *dev)
+{
+ /* Modprobe option to manually disable */
+ if (disable_vbi == 1)
+ return 0;
+
+ if (dev->chip_id == CHIP_ID_EM2860 ||
+ dev->chip_id == CHIP_ID_EM2883)
+ return 1;
+
+ /* Version of em28xx that does not support VBI */
+ return 0;
+}
+
+int em28xx_set_outfmt(struct em28xx *dev)
+{
+ int ret;
+ u8 vinctrl;
+
+ ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
+ dev->format->reg | 0x20, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
+ if (ret < 0)
+ return ret;
+
+ vinctrl = dev->vinctl;
+ if (em28xx_vbi_supported(dev) == 1) {
+ vinctrl |= EM28XX_VINCTRL_VBI_RAW;
+ em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
+ em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
+ em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
+ }
+ }
+
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
+}
+
+static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
+ u8 ymin, u8 ymax)
+{
+ em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n",
+ xmin, ymin, xmax, ymax);
+
+ em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1);
+ em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1);
+ em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1);
+ return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1);
+}
+
+static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
+ u16 width, u16 height)
+{
+ u8 cwidth = width;
+ u8 cheight = height;
+ u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01);
+
+ em28xx_coredbg("em28xx Area Set: (%d,%d)\n",
+ (width | (overflow & 2) << 7),
+ (height | (overflow & 1) << 8));
+
+ em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1);
+ em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1);
+ em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1);
+ em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1);
+ return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1);
+}
+
+static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
+{
+ u8 mode;
+ /* the em2800 scaler only supports scaling down to 50% */
+
+ if (dev->board.is_em2800) {
+ mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
+ } else {
+ u8 buf[2];
+
+ buf[0] = h;
+ buf[1] = h >> 8;
+ em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
+
+ buf[0] = v;
+ buf[1] = v >> 8;
+ em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
+ /* it seems that both H and V scalers must be active
+ to work correctly */
+ mode = (h || v) ? 0x30 : 0x00;
+ }
+ return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
+}
+
+/* FIXME: this only function read values from dev */
+int em28xx_resolution_set(struct em28xx *dev)
+{
+ int width, height;
+ width = norm_maxw(dev);
+ height = norm_maxh(dev);
+
+ /* Properly setup VBI */
+ dev->vbi_width = 720;
+ if (dev->norm & V4L2_STD_525_60)
+ dev->vbi_height = 12;
+ else
+ dev->vbi_height = 18;
+
+ em28xx_set_outfmt(dev);
+
+ em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
+
+ /* If we don't set the start position to 2 in VBI mode, we end up
+ with line 20/21 being YUYV encoded instead of being in 8-bit
+ greyscale. The core of the issue is that line 21 (and line 23 for
+ PAL WSS) are inside of active video region, and as a result they
+ get the pixelformatting associated with that area. So by cropping
+ it out, we end up with the same format as the rest of the VBI
+ region */
+ if (em28xx_vbi_supported(dev) == 1)
+ em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2);
+ else
+ em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
+
+ return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
+}
+
+int em28xx_set_alternate(struct em28xx *dev)
+{
+ int errCode, prev_alt = dev->alt;
+ int i;
+ unsigned int min_pkt_size = dev->width * 2 + 4;
+
+ /*
+ * alt = 0 is used only for control messages, so, only values
+ * greater than 0 can be used for streaming.
+ */
+ if (alt && alt < dev->num_alt) {
+ em28xx_coredbg("alternate forced to %d\n", dev->alt);
+ dev->alt = alt;
+ goto set_alt;
+ }
+
+ /* When image size is bigger than a certain value,
+ the frame size should be increased, otherwise, only
+ green screen will be received.
+ */
+ if (dev->width * 2 * dev->height > 720 * 240 * 2)
+ min_pkt_size *= 2;
+
+ for (i = 0; i < dev->num_alt; i++) {
+ /* stop when the selected alt setting offers enough bandwidth */
+ if (dev->alt_max_pkt_size[i] >= min_pkt_size) {
+ dev->alt = i;
+ break;
+ /* otherwise make sure that we end up with the maximum bandwidth
+ because the min_pkt_size equation might be wrong...
+ */
+ } else if (dev->alt_max_pkt_size[i] >
+ dev->alt_max_pkt_size[dev->alt])
+ dev->alt = i;
+ }
+
+set_alt:
+ if (dev->alt != prev_alt) {
+ em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
+ min_pkt_size, dev->alt);
+ dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
+ em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
+ dev->alt, dev->max_pkt_size);
+ errCode = usb_set_interface(dev->udev, 0, dev->alt);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
+ dev->alt, errCode);
+ return errCode;
+ }
+ }
+ return 0;
+}
+
+int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
+{
+ int rc = 0;
+
+ if (!gpio)
+ return rc;
+
+ if (dev->mode != EM28XX_SUSPEND) {
+ em28xx_write_reg(dev, 0x48, 0x00);
+ if (dev->mode == EM28XX_ANALOG_MODE)
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+ else
+ em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+ msleep(6);
+ }
+
+ /* Send GPIO reset sequences specified at board entry */
+ while (gpio->sleep >= 0) {
+ if (gpio->reg >= 0) {
+ rc = em28xx_write_reg_bits(dev,
+ gpio->reg,
+ gpio->val,
+ gpio->mask);
+ if (rc < 0)
+ return rc;
+ }
+ if (gpio->sleep > 0)
+ msleep(gpio->sleep);
+
+ gpio++;
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(em28xx_gpio_set);
+
+int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
+{
+ if (dev->mode == set_mode)
+ return 0;
+
+ if (set_mode == EM28XX_SUSPEND) {
+ dev->mode = set_mode;
+
+ /* FIXME: add suspend support for ac97 */
+
+ return em28xx_gpio_set(dev, dev->board.suspend_gpio);
+ }
+
+ dev->mode = set_mode;
+
+ if (dev->mode == EM28XX_DIGITAL_MODE)
+ return em28xx_gpio_set(dev, dev->board.dvb_gpio);
+ else
+ return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
+}
+EXPORT_SYMBOL_GPL(em28xx_set_mode);
+
+/* ------------------------------------------------------------------
+ URB control
+ ------------------------------------------------------------------*/
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void em28xx_irq_callback(struct urb *urb)
+{
+ struct em28xx *dev = urb->context;
+ int i;
+
+ switch (urb->status) {
+ case 0: /* success */
+ case -ETIMEDOUT: /* NAK */
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default: /* error */
+ em28xx_isocdbg("urb completition error %d.\n", urb->status);
+ break;
+ }
+
+ /* Copy data from URB */
+ spin_lock(&dev->slock);
+ dev->isoc_ctl.isoc_copy(dev, urb);
+ spin_unlock(&dev->slock);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ urb->status = 0;
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status) {
+ em28xx_isocdbg("urb resubmit failed (error=%i)\n",
+ urb->status);
+ }
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
+{
+ struct urb *urb;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+
+ em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = isoc_bufs->urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+
+ if (isoc_bufs->transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ isoc_bufs->transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ isoc_bufs->urb[i] = NULL;
+ }
+ isoc_bufs->transfer_buffer[i] = NULL;
+ }
+
+ kfree(isoc_bufs->urb);
+ kfree(isoc_bufs->transfer_buffer);
+
+ isoc_bufs->urb = NULL;
+ isoc_bufs->transfer_buffer = NULL;
+ isoc_bufs->num_bufs = 0;
+
+ em28xx_capture_start(dev, 0);
+}
+EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
+
+/*
+ * Stop URBs
+ */
+void em28xx_stop_urbs(struct em28xx *dev)
+{
+ int i;
+ struct urb *urb;
+ struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs;
+
+ em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n");
+
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = isoc_bufs->urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+ }
+ }
+
+ em28xx_capture_start(dev, 0);
+}
+EXPORT_SYMBOL_GPL(em28xx_stop_urbs);
+
+/*
+ * Allocate URBs
+ */
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size)
+{
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+ int sb_size, pipe;
+ struct urb *urb;
+ int j, k;
+
+ em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+
+ /* De-allocates all pending stuff */
+ em28xx_uninit_isoc(dev, mode);
+
+ isoc_bufs->num_bufs = num_bufs;
+
+ isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!isoc_bufs->urb) {
+ em28xx_errdev("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!isoc_bufs->transfer_buffer) {
+ em28xx_errdev("cannot allocate memory for usb transfer\n");
+ kfree(isoc_bufs->urb);
+ return -ENOMEM;
+ }
+
+ isoc_bufs->max_pkt_size = max_pkt_size;
+ isoc_bufs->num_packets = max_packets;
+ dev->isoc_ctl.vid_buf = NULL;
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size;
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL);
+ if (!urb) {
+ em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
+ em28xx_uninit_isoc(dev, mode);
+ return -ENOMEM;
+ }
+ isoc_bufs->urb[i] = urb;
+
+ isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!isoc_bufs->transfer_buffer[i]) {
+ em28xx_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ em28xx_uninit_isoc(dev, mode);
+ return -ENOMEM;
+ }
+ memset(isoc_bufs->transfer_buffer[i], 0, sb_size);
+
+ /* FIXME: this is a hack - should be
+ 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
+ should also be using 'desc.bInterval'
+ */
+ pipe = usb_rcvisocpipe(dev->udev,
+ mode == EM28XX_ANALOG_MODE ?
+ EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL);
+
+ usb_fill_int_urb(urb, dev->udev, pipe,
+ isoc_bufs->transfer_buffer[i], sb_size,
+ em28xx_irq_callback, dev, 1);
+
+ urb->number_of_packets = isoc_bufs->num_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < isoc_bufs->num_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ isoc_bufs->max_pkt_size;
+ k += isoc_bufs->max_pkt_size;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_alloc_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
+{
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+ int rc;
+ int alloc;
+
+ em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode);
+
+ dev->isoc_ctl.isoc_copy = isoc_copy;
+
+ if (mode == EM28XX_DIGITAL_MODE) {
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ /* no need to free/alloc isoc buffers in digital mode */
+ alloc = 0;
+ } else {
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+ alloc = 1;
+ }
+
+ if (alloc) {
+ rc = em28xx_alloc_isoc(dev, mode, max_packets,
+ num_bufs, max_pkt_size);
+ if (rc)
+ return rc;
+ }
+
+ init_waitqueue_head(&dma_q->wq);
+ init_waitqueue_head(&vbi_dma_q->wq);
+
+ em28xx_capture_start(dev, 1);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC);
+ if (rc) {
+ em28xx_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ em28xx_uninit_isoc(dev, mode);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_init_isoc);
+
+/*
+ * em28xx_wake_i2c()
+ * configure i2c attached devices
+ */
+void em28xx_wake_i2c(struct em28xx *dev)
+{
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ INPUT(dev->ctl_input)->vmux, 0, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+}
+
+/*
+ * Device control list
+ */
+
+static LIST_HEAD(em28xx_devlist);
+static DEFINE_MUTEX(em28xx_devlist_mutex);
+
+/*
+ * Extension interface
+ */
+
+static LIST_HEAD(em28xx_extension_devlist);
+
+int em28xx_register_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&ops->next, &em28xx_extension_devlist);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ ops->init(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
+ return 0;
+}
+EXPORT_SYMBOL(em28xx_register_extension);
+
+void em28xx_unregister_extension(struct em28xx_ops *ops)
+{
+ struct em28xx *dev = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ ops->fini(dev);
+ }
+ list_del(&ops->next);
+ mutex_unlock(&em28xx_devlist_mutex);
+ printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
+}
+EXPORT_SYMBOL(em28xx_unregister_extension);
+
+void em28xx_init_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_add_tail(&dev->devlist, &em28xx_devlist);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->init)
+ ops->init(dev);
+ }
+ mutex_unlock(&em28xx_devlist_mutex);
+}
+
+void em28xx_close_extension(struct em28xx *dev)
+{
+ const struct em28xx_ops *ops = NULL;
+
+ mutex_lock(&em28xx_devlist_mutex);
+ list_for_each_entry(ops, &em28xx_extension_devlist, next) {
+ if (ops->fini)
+ ops->fini(dev);
+ }
+ list_del(&dev->devlist);
+ mutex_unlock(&em28xx_devlist_mutex);
+}
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c
new file mode 100644
index 000000000000..913e5227897a
--- /dev/null
+++ b/drivers/media/usb/em28xx/em28xx-dvb.c
@@ -0,0 +1,1245 @@
+/*
+ DVB device driver for em28xx
+
+ (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
+
+ (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
+ - Fixes for the driver to properly work with HVR-950
+ - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
+ - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600
+
+ (c) 2008 Aidan Thornton <makosoft@googlemail.com>
+
+ Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by:
+ (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "em28xx.h"
+#include <media/v4l2-common.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/tuner.h>
+#include "tuner-simple.h"
+#include <linux/gpio.h>
+
+#include "lgdt330x.h"
+#include "lgdt3305.h"
+#include "zl10353.h"
+#include "s5h1409.h"
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1002x.h"
+#include "tda18271.h"
+#include "s921.h"
+#include "drxd.h"
+#include "cxd2820r.h"
+#include "tda18271c2dd.h"
+#include "drxk.h"
+#include "tda10071.h"
+#include "a8293.h"
+#include "qt1010.h"
+
+MODULE_DESCRIPTION("driver for em28xx based DVB cards");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+#define dprintk(level, fmt, arg...) do { \
+if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
+} while (0)
+
+struct em28xx_dvb {
+ struct dvb_frontend *fe[2];
+
+ /* feed count management */
+ struct mutex lock;
+ int nfeeds;
+
+ /* general boilerplate stuff */
+ struct dvb_adapter adapter;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net net;
+
+ /* Due to DRX-K - probably need changes */
+ int (*gate_ctrl)(struct dvb_frontend *, int);
+ struct semaphore pll_mutex;
+ bool dont_attach_fe1;
+ int lna_gpio;
+};
+
+
+static inline void print_err_status(struct em28xx *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(1, "URB status %d [%s].\n", status, errmsg);
+ } else {
+ dprintk(1, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
+{
+ int i;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+
+ return 0;
+}
+
+static int em28xx_start_streaming(struct em28xx_dvb *dvb)
+{
+ int rc;
+ struct em28xx *dev = dvb->adapter.priv;
+ int max_dvb_packet_size;
+
+ usb_set_interface(dev->udev, 0, dev->dvb_alt);
+ rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ if (rc < 0)
+ return rc;
+
+ max_dvb_packet_size = dev->dvb_max_pkt_size;
+ if (max_dvb_packet_size < 0)
+ return max_dvb_packet_size;
+ dprintk(1, "Using %d buffers each with %d x %d bytes\n",
+ EM28XX_DVB_NUM_BUFS,
+ EM28XX_DVB_MAX_PACKETS,
+ max_dvb_packet_size);
+
+ return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS,
+ max_dvb_packet_size, em28xx_dvb_isoc_copy);
+}
+
+static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
+{
+ struct em28xx *dev = dvb->adapter.priv;
+
+ em28xx_stop_urbs(dev);
+
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+
+ return 0;
+}
+
+static int em28xx_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct em28xx_dvb *dvb = demux->priv;
+ int rc, ret;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+ rc = dvb->nfeeds;
+
+ if (dvb->nfeeds == 1) {
+ ret = em28xx_start_streaming(dvb);
+ if (ret < 0)
+ rc = ret;
+ }
+
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int em28xx_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct em28xx_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+
+ if (0 == dvb->nfeeds)
+ err = em28xx_stop_streaming(dvb);
+
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+
+
+/* ------------------------------------------------------------------ */
+static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct em28xx *dev = fe->dvb->priv;
+
+ if (acquire)
+ return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ else
+ return em28xx_set_mode(dev, EM28XX_SUSPEND);
+}
+
+/* ------------------------------------------------------------------ */
+
+static struct lgdt330x_config em2880_lgdt3303_dev = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+};
+
+static struct lgdt3305_config em2870_lgdt3304_dev = {
+ .i2c_addr = 0x0e,
+ .demod_chip = LGDT3304,
+ .spectral_inversion = 1,
+ .deny_i2c_rptr = 1,
+ .mpeg_mode = LGDT3305_MPEG_PARALLEL,
+ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
+ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
+ .vsb_if_khz = 3250,
+ .qam_if_khz = 4000,
+};
+
+static struct s921_config sharp_isdbt = {
+ .demod_address = 0x30 >> 1
+};
+
+static struct zl10353_config em28xx_zl10353_with_xc3028 = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .parallel_ts = 1,
+ .if2 = 45600,
+};
+
+static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+};
+
+static struct tda18271_std_map kworld_a340_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config kworld_a340_config = {
+ .std_map = &kworld_a340_std_map,
+};
+
+static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+ .parallel_ts = 1,
+ .if2 = 45600,
+};
+
+static struct drxd_config em28xx_drxd = {
+ .demod_address = 0x70,
+ .demod_revision = 0xa2,
+ .pll_type = DRXD_PLL_NONE,
+ .clock = 12000,
+ .insert_rs_byte = 1,
+ .IF = 42800000,
+ .disable_i2c_gate_ctrl = 1,
+};
+
+static struct drxk_config terratec_h5_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
+ .qam_demod_parameter_count = 2,
+ .load_firmware_sync = true,
+};
+
+static struct drxk_config hauppauge_930c_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw",
+ .chunk_size = 56,
+ .qam_demod_parameter_count = 2,
+ .load_firmware_sync = true,
+};
+
+struct drxk_config terratec_htc_stick_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw",
+ .chunk_size = 54,
+ .qam_demod_parameter_count = 2,
+ /* Required for the antenna_gpio to disable LNA. */
+ .antenna_dvbt = true,
+ /* The windows driver uses the same. This will disable LNA. */
+ .antenna_gpio = 0x6,
+ .load_firmware_sync = true,
+};
+
+static struct drxk_config maxmedia_ub425_tc_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+ .load_firmware_sync = true,
+};
+
+static struct drxk_config pctv_520e_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .microcode_name = "dvb-demod-drxk-pctv.fw",
+ .qam_demod_parameter_count = 2,
+ .chunk_size = 58,
+ .antenna_dvbt = true, /* disable LNA */
+ .antenna_gpio = (1 << 2), /* disable LNA */
+ .load_firmware_sync = true,
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct em28xx_dvb *dvb = fe->sec_priv;
+ int status;
+
+ if (!dvb)
+ return -EINVAL;
+
+ if (enable) {
+ down(&dvb->pll_mutex);
+ status = dvb->gate_ctrl(fe, 1);
+ } else {
+ status = dvb->gate_ctrl(fe, 0);
+ up(&dvb->pll_mutex);
+ }
+ return status;
+}
+
+static void hauppauge_hvr930c_init(struct em28xx *dev)
+{
+ int i;
+
+ struct em28xx_reg_seq hauppauge_hvr930c_init[] = {
+ {EM2874_R80_GPIO, 0xff, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xfb, 0xff, 0x32},
+ {EM2874_R80_GPIO, 0xff, 0xff, 0xb8},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq hauppauge_hvr930c_end[] = {
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO, 0xaf, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x76},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x40},
+
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
+ {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
+ {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
+
+ { -1, -1, -1, -1},
+ };
+
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_gpio_set(dev, hauppauge_hvr930c_init);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+ em28xx_gpio_set(dev, hauppauge_hvr930c_end);
+
+ msleep(100);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(30);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+ msleep(10);
+
+}
+
+static void terratec_h5_init(struct em28xx *dev)
+{
+ int i;
+ struct em28xx_reg_seq terratec_h5_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf2, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_h5_end[] = {
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xa6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ {{ 0x04, 0x00 }, 2},
+ {{ 0x00, 0x04 }, 2},
+ {{ 0x00, 0x04, 0x00, 0x0a }, 4},
+ {{ 0x04, 0x14 }, 2},
+ {{ 0x04, 0x14, 0x00, 0x00 }, 4},
+ };
+
+ em28xx_gpio_set(dev, terratec_h5_init);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+ em28xx_gpio_set(dev, terratec_h5_end);
+};
+
+static void terratec_htc_stick_init(struct em28xx *dev)
+{
+ int i;
+
+ /*
+ * GPIO configuration:
+ * 0xff: unknown (does not affect DVB-T).
+ * 0xf6: DRX-K (demodulator).
+ * 0xe6: unknown (does not affect DVB-T).
+ * 0xb6: unknown (does not affect DVB-T).
+ */
+ struct em28xx_reg_seq terratec_htc_stick_init[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xe6, 0xff, 50},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 100},
+ { -1, -1, -1, -1},
+ };
+ struct em28xx_reg_seq terratec_htc_stick_end[] = {
+ {EM2874_R80_GPIO, 0xb6, 0xff, 100},
+ {EM2874_R80_GPIO, 0xf6, 0xff, 50},
+ { -1, -1, -1, -1},
+ };
+
+ /* Init the analog decoder? */
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ };
+
+ em28xx_gpio_set(dev, terratec_htc_stick_init);
+
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
+ msleep(10);
+ em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
+ msleep(10);
+
+ dev->i2c_client.addr = 0x82 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+
+ em28xx_gpio_set(dev, terratec_htc_stick_end);
+};
+
+static void pctv_520e_init(struct em28xx *dev)
+{
+ /*
+ * Init AVF4910B analog decoder. Looks like I2C traffic to
+ * digital demodulator and tuner are routed via AVF4910B.
+ */
+ int i;
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ };
+
+ dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+};
+
+static int em28xx_pctv_290e_set_lna(struct dvb_frontend *fe, int val)
+{
+ struct em28xx *dev = fe->dvb->priv;
+#ifdef CONFIG_GPIOLIB
+ struct em28xx_dvb *dvb = dev->dvb;
+ int ret;
+ unsigned long flags;
+
+ if (val)
+ flags = GPIOF_OUT_INIT_LOW;
+ else
+ flags = GPIOF_OUT_INIT_HIGH;
+
+ ret = gpio_request_one(dvb->lna_gpio, flags, NULL);
+ if (ret)
+ em28xx_errdev("gpio request failed %d\n", ret);
+ else
+ gpio_free(dvb->lna_gpio);
+
+ return ret;
+#else
+ dev_warn(&dev->udev->dev, "%s: LNA control is disabled\n",
+ KBUILD_MODNAME);
+ return 0;
+#endif
+}
+
+static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
+{
+ /* Values extracted from a USB trace of the Terratec Windows driver */
+ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
+ static u8 reset[] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 };
+ static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+ static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 };
+ static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 };
+ static u8 tuner_go[] = { TUNER_GO, 0x01};
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg));
+ mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg));
+ mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg));
+ mt352_write(fe, tuner_go, sizeof(tuner_go));
+ return 0;
+}
+
+static struct mt352_config terratec_xs_mt352_cfg = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+ .demod_init = em28xx_mt352_terratec_xs_init,
+};
+
+static struct tda10023_config em28xx_tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
+static struct cxd2820r_config em28xx_cxd2820r_config = {
+ .i2c_address = (0xd8 >> 1),
+ .ts_mode = CXD2820R_TS_SERIAL,
+};
+
+static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .gate = TDA18271_GATE_DIGITAL,
+};
+
+static const struct tda10071_config em28xx_tda10071_config = {
+ .i2c_address = 0x55, /* (0xaa >> 1) */
+ .i2c_wr_max = 64,
+ .ts_mode = TDA10071_TS_SERIAL,
+ .spec_inv = 0,
+ .xtal = 40444000, /* 40.444 MHz */
+ .pll_multiplier = 20,
+};
+
+static const struct a8293_config em28xx_a8293_config = {
+ .i2c_addr = 0x08, /* (0x10 >> 1) */
+};
+
+static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = {
+ .demod_address = (0x1e >> 1),
+ .disable_i2c_gate_ctrl = 1,
+ .no_tuner = 1,
+ .parallel_ts = 1,
+};
+static struct qt1010_config em28xx_qt1010_config = {
+ .i2c_address = 0x62
+
+};
+
+/* ------------------------------------------------------------------ */
+
+static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
+{
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.i2c_adap = &dev->i2c_adap;
+ cfg.i2c_addr = addr;
+
+ if (!dev->dvb->fe[0]) {
+ em28xx_errdev("/2: dvb frontend not attached. "
+ "Can't attach xc3028\n");
+ return -EINVAL;
+ }
+
+ fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg);
+ if (!fe) {
+ em28xx_errdev("/2: xc3028 attach failed\n");
+ dvb_frontend_detach(dev->dvb->fe[0]);
+ dev->dvb->fe[0] = NULL;
+ return -EINVAL;
+ }
+
+ em28xx_info("%s/2: xc3028 attached\n", dev->name);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
+ struct em28xx *dev, struct device *device)
+{
+ int result;
+
+ mutex_init(&dvb->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
+ adapter_nr);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_adapter;
+ }
+
+ /* Ensure all frontends negotiate bus access */
+ dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
+ if (dvb->fe[1])
+ dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
+
+ dvb->adapter.priv = dev;
+
+ /* register frontend */
+ result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend0;
+ }
+
+ /* register 2nd frontend */
+ if (dvb->fe[1]) {
+ result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_frontend1;
+ }
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = em28xx_start_feed;
+ dvb->demux.stop_feed = em28xx_stop_feed;
+
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+ dev->name, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ if (dvb->fe[1])
+ dvb_unregister_frontend(dvb->fe[1]);
+ dvb_unregister_frontend(dvb->fe[0]);
+fail_frontend1:
+ if (dvb->fe[1])
+ dvb_frontend_detach(dvb->fe[1]);
+fail_frontend0:
+ dvb_frontend_detach(dvb->fe[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+fail_adapter:
+ return result;
+}
+
+static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
+{
+ dvb_net_release(&dvb->net);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ dvb_dmxdev_release(&dvb->dmxdev);
+ dvb_dmx_release(&dvb->demux);
+ if (dvb->fe[1])
+ dvb_unregister_frontend(dvb->fe[1]);
+ dvb_unregister_frontend(dvb->fe[0]);
+ if (dvb->fe[1] && !dvb->dont_attach_fe1)
+ dvb_frontend_detach(dvb->fe[1]);
+ dvb_frontend_detach(dvb->fe[0]);
+ dvb_unregister_adapter(&dvb->adapter);
+}
+
+static int em28xx_dvb_init(struct em28xx *dev)
+{
+ int result = 0, mfe_shared = 0;
+ struct em28xx_dvb *dvb;
+
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n");
+ return 0;
+ }
+
+ dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
+
+ if (dvb == NULL) {
+ em28xx_info("em28xx_dvb: memory allocation failed\n");
+ return -ENOMEM;
+ }
+ dev->dvb = dvb;
+ dvb->fe[0] = dvb->fe[1] = NULL;
+
+ mutex_lock(&dev->lock);
+ em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
+ /* init frontend */
+ switch (dev->model) {
+ case EM2874_BOARD_LEADERSHIP_ISDBT:
+ dvb->fe[0] = dvb_attach(s921_attach,
+ &sharp_isdbt, &dev->i2c_adap);
+
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ break;
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
+ case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
+ dvb->fe[0] = dvb_attach(lgdt330x_attach,
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_KWORLD_DVB_310U:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_with_xc3028,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2882_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
+ case EM2882_BOARD_DIKOM_DK300:
+ case EM2882_BOARD_KWORLD_VS_DVBT:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap);
+ if (dvb->fe[0] == NULL) {
+ /* This board could have either a zl10353 or a mt352.
+ If the chip id isn't for zl10353, try mt352 */
+ dvb->fe[0] = dvb_attach(mt352_attach,
+ &terratec_xs_mt352_cfg,
+ &dev->i2c_adap);
+ }
+
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2870_BOARD_KWORLD_355U:
+ dvb->fe[0] = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_no_i2c_gate_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL)
+ dvb_attach(qt1010_attach, dvb->fe[0],
+ &dev->i2c_adap, &em28xx_qt1010_config);
+ break;
+ case EM2883_BOARD_KWORLD_HYBRID_330U:
+ case EM2882_BOARD_EVGA_INDTUBE:
+ dvb->fe[0] = dvb_attach(s5h1409_attach,
+ &em28xx_s5h1409_with_xc3028,
+ &dev->i2c_adap);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2882_BOARD_KWORLD_ATSC_315U:
+ dvb->fe[0] = dvb_attach(lgdt330x_attach,
+ &em2880_lgdt3303_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL) {
+ if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
+ dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
+ &dev->i2c_adap, &dev->udev->dev);
+ if (em28xx_attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
+ /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
+ dvb->fe[0] = dvb_attach(tda10023_attach,
+ &em28xx_tda10023_config,
+ &dev->i2c_adap, 0x48);
+ if (dvb->fe[0]) {
+ if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2870_BOARD_KWORLD_A340:
+ dvb->fe[0] = dvb_attach(lgdt3305_attach,
+ &em2870_lgdt3304_dev,
+ &dev->i2c_adap);
+ if (dvb->fe[0] != NULL)
+ dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap, &kworld_a340_config);
+ break;
+ case EM28174_BOARD_PCTV_290E:
+ /* set default GPIO0 for LNA, used if GPIOLIB is undefined */
+ dvb->lna_gpio = CXD2820R_GPIO_E | CXD2820R_GPIO_O |
+ CXD2820R_GPIO_L;
+ dvb->fe[0] = dvb_attach(cxd2820r_attach,
+ &em28xx_cxd2820r_config,
+ &dev->i2c_adap,
+ &dvb->lna_gpio);
+ if (dvb->fe[0]) {
+ /* FE 0 attach tuner */
+ if (!dvb_attach(tda18271_attach,
+ dvb->fe[0],
+ 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+
+#ifdef CONFIG_GPIOLIB
+ /* enable LNA for DVB-T, DVB-T2 and DVB-C */
+ result = gpio_request_one(dvb->lna_gpio,
+ GPIOF_OUT_INIT_LOW, NULL);
+ if (result)
+ em28xx_errdev("gpio request failed %d\n",
+ result);
+ else
+ gpio_free(dvb->lna_gpio);
+
+ result = 0; /* continue even set LNA fails */
+#endif
+ dvb->fe[0]->ops.set_lna = em28xx_pctv_290e_set_lna;
+ }
+
+ break;
+ case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
+ {
+ struct xc5000_config cfg;
+ hauppauge_hvr930c_init(dev);
+
+ dvb->fe[0] = dvb_attach(drxk_attach,
+ &hauppauge_930c_drxk, &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* FIXME: do we need a pll semaphore? */
+ dvb->fe[0]->sec_priv = dvb;
+ sema_init(&dvb->pll_mutex, 1);
+ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ /* Attach xc5000 */
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.i2c_address = 0x61;
+ cfg.if_khz = 4000;
+
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+ if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap,
+ &cfg)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+ break;
+ }
+ case EM2884_BOARD_TERRATEC_H5:
+ terratec_h5_init(dev);
+
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* FIXME: do we need a pll semaphore? */
+ dvb->fe[0]->sec_priv = dvb;
+ sema_init(&dvb->pll_mutex, 1);
+ dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
+ dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ /* Attach tda18271 to DVB-C frontend */
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (dvb->fe[0]->ops.i2c_gate_ctrl)
+ dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
+
+ break;
+ case EM28174_BOARD_PCTV_460E:
+ /* attach demod */
+ dvb->fe[0] = dvb_attach(tda10071_attach,
+ &em28xx_tda10071_config, &dev->i2c_adap);
+
+ /* attach SEC */
+ if (dvb->fe[0])
+ dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
+ &em28xx_a8293_config);
+ break;
+ case EM2874_BOARD_MAXMEDIA_UB425_TC:
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* disable I2C-gate */
+ dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* attach tuner */
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x60)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ /* TODO: we need drx-3913k firmware in order to support DVB-T */
+ em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
+ "driver version\n");
+
+ break;
+ case EM2884_BOARD_PCTV_510E:
+ case EM2884_BOARD_PCTV_520E:
+ pctv_520e_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* attach tuner */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
+ case EM2884_BOARD_CINERGY_HTC_STICK:
+ terratec_htc_stick_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
+ &dev->i2c_adap);
+ if (!dvb->fe[0]) {
+ result = -EINVAL;
+ goto out_free;
+ }
+
+ /* Attach the demodulator. */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+ default:
+ em28xx_errdev("/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n");
+ break;
+ }
+ if (NULL == dvb->fe[0]) {
+ em28xx_errdev("/2: frontend initialization failed\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+ /* define general-purpose callback pointer */
+ dvb->fe[0]->callback = em28xx_tuner_callback;
+ if (dvb->fe[1])
+ dvb->fe[1]->callback = em28xx_tuner_callback;
+
+ /* register everything */
+ result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
+
+ if (result < 0)
+ goto out_free;
+
+ /* MFE lock */
+ dvb->adapter.mfe_shared = mfe_shared;
+
+ em28xx_info("Successfully loaded em28xx-dvb\n");
+ret:
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+ mutex_unlock(&dev->lock);
+ return result;
+
+out_free:
+ kfree(dvb);
+ dev->dvb = NULL;
+ goto ret;
+}
+
+static inline void prevent_sleep(struct dvb_frontend_ops *ops)
+{
+ ops->set_voltage = NULL;
+ ops->sleep = NULL;
+ ops->tuner_ops.sleep = NULL;
+}
+
+static int em28xx_dvb_fini(struct em28xx *dev)
+{
+ if (!dev->board.has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
+ if (dev->dvb) {
+ struct em28xx_dvb *dvb = dev->dvb;
+
+ if (dev->state & DEV_DISCONNECTED) {
+ /* We cannot tell the device to sleep
+ * once it has been unplugged. */
+ if (dvb->fe[0])
+ prevent_sleep(&dvb->fe[0]->ops);
+ if (dvb->fe[1])
+ prevent_sleep(&dvb->fe[1]->ops);
+ }
+
+ em28xx_unregister_dvb(dvb);
+ kfree(dvb);
+ dev->dvb = NULL;
+ }
+
+ return 0;
+}
+
+static struct em28xx_ops dvb_ops = {
+ .id = EM28XX_DVB,
+ .name = "Em28xx dvb Extension",
+ .init = em28xx_dvb_init,
+ .fini = em28xx_dvb_fini,
+};
+
+static int __init em28xx_dvb_register(void)
+{
+ return em28xx_register_extension(&dvb_ops);
+}
+
+static void __exit em28xx_dvb_unregister(void)
+{
+ em28xx_unregister_extension(&dvb_ops);
+}
+
+module_init(em28xx_dvb_register);
+module_exit(em28xx_dvb_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 1683bd9d51ee..1683bd9d51ee 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c
index 97d36b4f19db..97d36b4f19db 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/usb/em28xx/em28xx-input.c
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h
index 6ff368297f6e..6ff368297f6e 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/usb/em28xx/em28xx-reg.h
diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/usb/em28xx/em28xx-vbi.c
index 2b4c9cba2d67..2b4c9cba2d67 100644
--- a/drivers/media/video/em28xx/em28xx-vbi.c
+++ b/drivers/media/usb/em28xx/em28xx-vbi.c
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
new file mode 100644
index 000000000000..1e553d357380
--- /dev/null
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -0,0 +1,2624 @@
+/*
+ em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
+ video capture devices
+
+ Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
+ Markus Rechberger <mrechberger@gmail.com>
+ Mauro Carvalho Chehab <mchehab@infradead.org>
+ Sascha Sommer <saschasommer@freenet.de>
+
+ Some parts based on SN9C10x PC Camera Controllers GPL driver made
+ by Luca Risolia <luca.risolia@studio.unibo.it>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "em28xx.h"
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/msp3400.h>
+#include <media/tuner.h>
+
+#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
+ "Markus Rechberger <mrechberger@gmail.com>, " \
+ "Mauro Carvalho Chehab <mchehab@infradead.org>, " \
+ "Sascha Sommer <saschasommer@freenet.de>"
+
+#define DRIVER_DESC "Empia em28xx based USB video device driver"
+
+#define EM28XX_VERSION "0.1.3"
+
+#define em28xx_videodbg(fmt, arg...) do {\
+ if (video_debug) \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); } while (0)
+
+static unsigned int isoc_debug;
+module_param(isoc_debug, int, 0644);
+MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
+
+#define em28xx_isocdbg(fmt, arg...) \
+do {\
+ if (isoc_debug) { \
+ printk(KERN_INFO "%s %s :"fmt, \
+ dev->name, __func__ , ##arg); \
+ } \
+ } while (0)
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EM28XX_VERSION);
+
+static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
+
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+/* supported video standards */
+static struct em28xx_fmt format[] = {
+ {
+ .name = "16 bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ }, {
+ .name = "16 bpp RGB 565, LE",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_RGB_16_656,
+ }, {
+ .name = "8 bpp Bayer BGBG..GRGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_BGBG,
+ }, {
+ .name = "8 bpp Bayer GRGR..BGBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GRGR,
+ }, {
+ .name = "8 bpp Bayer GBGB..RGRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GBGB,
+ }, {
+ .name = "12 bpp YUV411",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .depth = 12,
+ .reg = EM28XX_OUTFMT_YUV411,
+ },
+};
+
+/* supported controls */
+/* Common to all boards */
+static struct v4l2_queryctrl ac97_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0x0,
+ .maximum = 0x1f,
+ .step = 0x1,
+ .default_value = 0x1f,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+ .flags = 0,
+ }
+};
+
+/* ------------------------------------------------------------------
+ DMA and thread functions
+ ------------------------------------------------------------------*/
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vid_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vbi_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the buffer header type and properly handles
+ */
+static void em28xx_copy_video(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *fieldstart, *startwrite, *startread;
+ int linesdone, currlinedone, offset, lencopy, remain;
+ int bytesperline = dev->width << 1;
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+ remain = len;
+
+ if (dev->progressive)
+ fieldstart = outp;
+ else {
+ /* Interlaces two half frames */
+ if (buf->top_field)
+ fieldstart = outp;
+ else
+ fieldstart = outp + bytesperline;
+ }
+
+ linesdone = dma_q->pos / bytesperline;
+ currlinedone = dma_q->pos % bytesperline;
+
+ if (dev->progressive)
+ offset = linesdone * bytesperline + currlinedone;
+ else
+ offset = linesdone * bytesperline * 2 + currlinedone;
+
+ startwrite = fieldstart + offset;
+ lencopy = bytesperline - currlinedone;
+ lencopy = lencopy > remain ? remain : lencopy;
+
+ if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ remain = (char *)outp + buf->vb.size - (char *)startwrite;
+ lencopy = remain;
+ }
+ if (lencopy <= 0)
+ return;
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+
+ while (remain > 0) {
+ startwrite += lencopy + bytesperline;
+ startread += lencopy;
+ if (bytesperline > remain)
+ lencopy = remain;
+ else
+ lencopy = bytesperline;
+
+ if ((char *)startwrite + lencopy > (char *)outp +
+ buf->vb.size) {
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end"
+ "(2)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+ lencopy = remain = (char *)outp + buf->vb.size -
+ (char *)startwrite;
+ }
+ if (lencopy <= 0)
+ break;
+
+ memcpy(startwrite, startread, lencopy);
+
+ remain -= lencopy;
+ }
+
+ dma_q->pos += len;
+}
+
+static void em28xx_copy_vbi(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *startwrite, *startread;
+ int offset;
+ int bytesperline;
+
+ if (dev == NULL) {
+ em28xx_isocdbg("dev is null\n");
+ return;
+ }
+ bytesperline = dev->vbi_width;
+
+ if (dma_q == NULL) {
+ em28xx_isocdbg("dma_q is null\n");
+ return;
+ }
+ if (buf == NULL) {
+ return;
+ }
+ if (p == NULL) {
+ em28xx_isocdbg("p is null\n");
+ return;
+ }
+ if (outp == NULL) {
+ em28xx_isocdbg("outp is null\n");
+ return;
+ }
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ startread = p;
+
+ startwrite = outp + dma_q->pos;
+ offset = dma_q->pos;
+
+ /* Make sure the bottom field populates the second half of the frame */
+ if (buf->top_field == 0) {
+ startwrite += bytesperline * dev->vbi_height;
+ offset += bytesperline * dev->vbi_height;
+ }
+
+ memcpy(startwrite, startread, len);
+ dma_q->pos += len;
+}
+
+static inline void print_err_status(struct em28xx *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ em28xx_isocdbg("URB status %d [%s].\n", status, errmsg);
+ } else {
+ em28xx_isocdbg("URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer **buf)
+{
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ em28xx_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vid_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0, (*buf)->vb.size);
+
+ dev->isoc_ctl.vid_buf = *buf;
+
+ return;
+}
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer **buf)
+{
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ em28xx_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vbi_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+ /* Cleans up buffer - Useful for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0x00, (*buf)->vb.size);
+
+ dev->isoc_ctl.vbi_buf = *buf;
+
+ return;
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
+{
+ struct em28xx_buffer *buf;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ unsigned char *outp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.vid_buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length - 4;
+
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ em28xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* FIXME: incomplete buffer checks where removed to make
+ logic simpler. Impacts of those changes should be evaluated
+ */
+ if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) {
+ em28xx_isocdbg("VBI HEADER!!!\n");
+ /* FIXME: Should add vbi copy */
+ continue;
+ }
+ if (p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+ len, (p[2] & 1) ? "odd" : "even");
+
+ if (dev->progressive || !(p[2] & 1)) {
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+ }
+
+ if (buf != NULL) {
+ if (p[2] & 1)
+ buf->top_field = 0;
+ else
+ buf->top_field = 1;
+ }
+
+ dma_q->pos = 0;
+ }
+ if (buf != NULL) {
+ if (p[0] != 0x88 && p[0] != 0x22) {
+ em28xx_isocdbg("frame is not complete\n");
+ len += 4;
+ } else {
+ p += 4;
+ }
+ em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+ }
+ }
+ return rc;
+}
+
+/* Version of isoc handler that takes into account a mixture of video and
+ VBI data */
+static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+{
+ struct em28xx_buffer *buf, *vbi_buf;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ unsigned char *outp = NULL;
+ unsigned char *vbioutp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+ int vbi_size;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.vid_buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ vbi_buf = dev->isoc_ctl.vbi_buf;
+ if (vbi_buf != NULL)
+ vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length;
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ em28xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* capture type 0 = vbi start
+ capture type 1 = video start
+ capture type 2 = video in progress */
+ if (p[0] == 0x33 && p[1] == 0x95) {
+ dev->capture_type = 0;
+ dev->vbi_read = 0;
+ em28xx_isocdbg("VBI START HEADER!!!\n");
+ dev->cur_field = p[2];
+ p += 4;
+ len -= 4;
+ } else if (p[0] == 0x88 && p[1] == 0x88 &&
+ p[2] == 0x88 && p[3] == 0x88) {
+ /* continuation */
+ p += 4;
+ len -= 4;
+ } else if (p[0] == 0x22 && p[1] == 0x5a) {
+ /* start video */
+ p += 4;
+ len -= 4;
+ }
+
+ vbi_size = dev->vbi_width * dev->vbi_height;
+
+ if (dev->capture_type == 0) {
+ if (dev->vbi_read >= vbi_size) {
+ /* We've already read all the VBI data, so
+ treat the rest as video */
+ em28xx_isocdbg("dev->vbi_read > vbi_size\n");
+ } else if ((dev->vbi_read + len) < vbi_size) {
+ /* This entire frame is VBI data */
+ if (dev->vbi_read == 0 &&
+ (!(dev->cur_field & 1))) {
+ /* Brand new frame */
+ if (vbi_buf != NULL)
+ vbi_buffer_filled(dev,
+ vbi_dma_q,
+ vbi_buf);
+ vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+ if (vbi_buf == NULL)
+ vbioutp = NULL;
+ else
+ vbioutp = videobuf_to_vmalloc(
+ &vbi_buf->vb);
+ }
+
+ if (dev->vbi_read == 0) {
+ vbi_dma_q->pos = 0;
+ if (vbi_buf != NULL) {
+ if (dev->cur_field & 1)
+ vbi_buf->top_field = 0;
+ else
+ vbi_buf->top_field = 1;
+ }
+ }
+
+ dev->vbi_read += len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, len);
+ } else {
+ /* Some of this frame is VBI data and some is
+ video data */
+ int vbi_data_len = vbi_size - dev->vbi_read;
+ dev->vbi_read += vbi_data_len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, vbi_data_len);
+ dev->capture_type = 1;
+ p += vbi_data_len;
+ len -= vbi_data_len;
+ }
+ }
+
+ if (dev->capture_type == 1) {
+ dev->capture_type = 2;
+ if (dev->progressive || !(dev->cur_field & 1)) {
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+ }
+ if (buf != NULL) {
+ if (dev->cur_field & 1)
+ buf->top_field = 0;
+ else
+ buf->top_field = 1;
+ }
+
+ dma_q->pos = 0;
+ }
+
+ if (buf != NULL && dev->capture_type == 2) {
+ if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 &&
+ p[2] == 0x88 && p[3] == 0x88) {
+ p += 4;
+ len -= 4;
+ }
+ if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, len=%i, %s\n",
+ p[2], len, (p[2] & 1) ?
+ "odd" : "even");
+ p += 4;
+ len -= 4;
+ }
+
+ if (len > 0)
+ em28xx_copy_video(dev, dma_q, buf, p, outp,
+ len);
+ }
+ }
+ return rc;
+}
+
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_frequency f;
+
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
+ >> 3;
+
+ if (0 == *count)
+ *count = EM28XX_DEF_BUF;
+
+ if (*count < EM28XX_MIN_BUF)
+ *count = EM28XX_MIN_BUF;
+
+ /* Ask tuner to go to analog or radio mode */
+ memset(&f, 0, sizeof(f));
+ f.frequency = dev->ctl_freq;
+ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
+
+ return 0;
+}
+
+/* This is called *without* dev->slock held; please keep it that way */
+static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.vid_buf == buf)
+ dev->isoc_ctl.vid_buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ struct em28xx *dev = fh->dev;
+ int rc = 0, urb_init = 0;
+
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ + 7) >> 3;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = dev->width;
+ buf->vb.height = dev->height;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ if (!dev->isoc_ctl.analog_bufs.num_bufs)
+ urb_init = 1;
+
+ if (urb_init) {
+ if (em28xx_vbi_supported(dev) == 1)
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy_vbi);
+ else
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = (struct em28xx *)fh->dev;
+
+ em28xx_isocdbg("em28xx: called buffer_release\n");
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops em28xx_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/********************* v4l2 interface **************************************/
+
+static void video_mux(struct em28xx *dev, int index)
+{
+ dev->ctl_input = index;
+ dev->ctl_ainput = INPUT(index)->amux;
+ dev->ctl_aoutput = INPUT(index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ INPUT(index)->vmux, 0, 0);
+
+ if (dev->board.has_msp34xx) {
+ if (dev->i2s_speed) {
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio,
+ s_i2s_clock_freq, dev->i2s_speed);
+ }
+ /* Note: this is msp3400 specific */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
+ }
+
+ if (dev->board.adecoder != EM28XX_NOADECODER) {
+ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
+ dev->ctl_ainput, dev->ctl_aoutput, 0);
+ }
+
+ em28xx_audio_analog_set(dev);
+}
+
+/* Usage lock check functions */
+static int res_get(struct em28xx_fh *fh, unsigned int bit)
+{
+ struct em28xx *dev = fh->dev;
+
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ em28xx_videodbg("res: get %d\n", bit);
+ return 1;
+}
+
+static int res_check(struct em28xx_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
+}
+
+static int res_locked(struct em28xx *dev, unsigned int bit)
+{
+ return dev->resources & bit;
+}
+
+static void res_free(struct em28xx_fh *fh, unsigned int bits)
+{
+ struct em28xx *dev = fh->dev;
+
+ BUG_ON((fh->resources & bits) != bits);
+
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ em28xx_videodbg("res: put %d\n", bits);
+}
+
+static int get_ressource(struct em28xx_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return EM28XX_RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return EM28XX_RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+/*
+ * ac97_queryctrl()
+ * return the ac97 supported controls
+ */
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ /* Control is not ac97 related */
+ return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
+ */
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = dev->mute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value = dev->volume;
+ return 0;
+ default:
+ /* Control is not ac97 related */
+ return 1;
+ }
+}
+
+/*
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
+ */
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+ if (ctrl->id == ac97_qctrl[i].id)
+ goto handle;
+
+ /* Announce that hasn't handle it */
+ return 1;
+
+handle:
+ if (ctrl->value < ac97_qctrl[i].minimum ||
+ ctrl->value > ac97_qctrl[i].maximum)
+ return -ERANGE;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ dev->mute = ctrl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ break;
+ }
+
+ return em28xx_audio_analog_set(dev);
+}
+
+static int check_dev(struct em28xx *dev)
+{
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_errdev("v4l2 ioctl: device not present\n");
+ return -ENODEV;
+ }
+
+ if (dev->state & DEV_MISCONFIGURED) {
+ em28xx_errdev("v4l2 ioctl: device is misconfigured; "
+ "close and open it again\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static void get_scale(struct em28xx *dev,
+ unsigned int width, unsigned int height,
+ unsigned int *hscale, unsigned int *vscale)
+{
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
+ if (*hscale >= 0x4000)
+ *hscale = 0x3fff;
+
+ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
+ if (*vscale >= 0x4000)
+ *vscale = 0x3fff;
+}
+
+/* ------------------------------------------------------------------
+ IOCTL vidioc handling
+ ------------------------------------------------------------------*/
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.pixelformat = dev->format->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+ return 0;
+}
+
+static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return &format[i];
+
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int width = f->fmt.pix.width;
+ unsigned int height = f->fmt.pix.height;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+ unsigned int hscale, vscale;
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ /* the em2800 can only scale down to 50% */
+ height = height > (3 * maxh / 4) ? maxh : maxh / 2;
+ width = width > (3 * maxw / 4) ? maxw : maxw / 2;
+ /* MaxPacketSize for em2800 is too small to capture at full resolution
+ * use half of maxw as the scaler can only scale to 50% */
+ if (width == maxw && height == maxh)
+ width /= 2;
+ } else {
+ /* width must even because of the YUYV format
+ height must be even because of interlacing */
+ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
+ 1, 0);
+ }
+
+ get_scale(dev, width, height, &hscale, &vscale);
+
+ width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
+ height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
+
+ f->fmt.pix.width = width;
+ f->fmt.pix.height = height;
+ f->fmt.pix.pixelformat = fmt->fourcc;
+ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (dev->progressive)
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ else
+ f->fmt.pix.field = dev->interlaced ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+
+ return 0;
+}
+
+static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
+ unsigned width, unsigned height)
+{
+ struct em28xx_fmt *fmt;
+
+ fmt = format_by_fourcc(fourcc);
+ if (!fmt)
+ return -EINVAL;
+
+ dev->format = fmt;
+ dev->width = width;
+ dev->height = height;
+
+ /* set new image size */
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_set_alternate(dev);
+ em28xx_resolution_set(dev);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ em28xx_errdev("%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ return em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ *norm = dev->norm;
+
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct v4l2_format f;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ dev->norm = *norm;
+
+ /* Adjusts width/height, if needed */
+ f.fmt.pix.width = dev->width;
+ f.fmt.pix.height = dev->height;
+ vidioc_try_fmt_vid_cap(file, priv, &f);
+
+ /* set new image size */
+ dev->width = f.fmt.pix.width;
+ dev->height = f.fmt.pix.height;
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_resolution_set(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = 0;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (dev->board.is_webcam)
+ rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
+ video, g_parm, p);
+ else
+ v4l2_video_std_frame_period(dev->norm,
+ &p->parm.capture.timeperframe);
+
+ return rc;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *p)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->board.is_webcam)
+ return -EINVAL;
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
+}
+
+static const char *iname[] = {
+ [EM28XX_VMUX_COMPOSITE1] = "Composite1",
+ [EM28XX_VMUX_COMPOSITE2] = "Composite2",
+ [EM28XX_VMUX_COMPOSITE3] = "Composite3",
+ [EM28XX_VMUX_COMPOSITE4] = "Composite4",
+ [EM28XX_VMUX_SVIDEO] = "S-Video",
+ [EM28XX_VMUX_TELEVISION] = "Television",
+ [EM28XX_VMUX_CABLE] = "Cable TV",
+ [EM28XX_VMUX_DVB] = "DVB",
+ [EM28XX_VMUX_DEBUG] = "for debug only",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[INPUT(n)->type]);
+
+ if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
+ (EM28XX_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ i->std = dev->vdev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ *i = dev->ctl_input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (i >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(i)->type)
+ return -EINVAL;
+
+ video_mux(dev, i);
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
+ switch (a->index) {
+ case EM28XX_AMUX_VIDEO:
+ strcpy(a->name, "Television");
+ break;
+ case EM28XX_AMUX_LINE_IN:
+ strcpy(a->name, "Line In");
+ break;
+ case EM28XX_AMUX_VIDEO2:
+ strcpy(a->name, "Television alt");
+ break;
+ case EM28XX_AMUX_PHONE:
+ strcpy(a->name, "Phone");
+ break;
+ case EM28XX_AMUX_MIC:
+ strcpy(a->name, "Mic");
+ break;
+ case EM28XX_AMUX_CD:
+ strcpy(a->name, "CD");
+ break;
+ case EM28XX_AMUX_AUX:
+ strcpy(a->name, "Aux");
+ break;
+ case EM28XX_AMUX_PCM_OUT:
+ strcpy(a->name, "PCM");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ a->index = dev->ctl_ainput;
+ a->capability = V4L2_AUDCAP_STEREO;
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+
+ if (!dev->audio_mode.has_audio)
+ return -EINVAL;
+
+ if (a->index >= MAX_EM28XX_INPUT)
+ return -EINVAL;
+ if (0 == INPUT(a->index)->type)
+ return -EINVAL;
+
+ dev->ctl_ainput = INPUT(a->index)->amux;
+ dev->ctl_aoutput = INPUT(a->index)->aout;
+
+ if (!dev->ctl_aoutput)
+ dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int id = qc->id;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ memset(qc, 0, sizeof(*qc));
+
+ qc->id = id;
+
+ /* enumerate AC97 controls */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ rc = ac97_queryctrl(qc);
+ if (!rc)
+ return 0;
+ }
+
+ /* enumerate V4L2 device controls */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
+
+ if (qc->type)
+ return 0;
+ else
+ 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)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+ rc = 0;
+
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_get_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* 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;
+ }
+
+ return rc;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_set_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
+ if (rc == 1) {
+ 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
+ * and to adjust audio volume. However, the value ranges
+ * should be checked by the corresponding V4L subdriver.
+ */
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ dev->mute = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ }
+ }
+ return (rc < 0) ? rc : 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Tuner");
+ t->type = V4L2_TUNER_ANALOG_TV;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->ctl_freq;
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (0 != f->tuner)
+ return -EINVAL;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ dev->ctl_freq = f->frequency;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int em28xx_reg_len(int reg)
+{
+ switch (reg) {
+ case EM28XX_R40_AC97LSB:
+ case EM28XX_R30_HSCALELOW:
+ case EM28XX_R32_VSCALELOW:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
+
+ return 0;
+}
+
+
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int ret;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ ret = em28xx_read_ac97(dev, reg->reg);
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ reg->size = 1;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ reg->size = em28xx_reg_len(reg->reg);
+ if (reg->size == 1) {
+ ret = em28xx_read_reg(dev, reg->reg);
+
+ if (ret < 0)
+ return ret;
+
+ reg->val = ret;
+ } else {
+ __le16 val = 0;
+ ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
+ reg->reg, (char *)&val, 2);
+ if (ret < 0)
+ return ret;
+
+ reg->val = le16_to_cpu(val);
+ }
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ __le16 buf;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ return em28xx_write_ac97(dev, reg->reg, reg->val);
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ buf = cpu_to_le16(reg->val);
+
+ return em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+}
+#endif
+
+
+static int vidioc_cropcap(struct file *file, void *priv,
+ struct v4l2_cropcap *cc)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cc->bounds.left = 0;
+ cc->bounds.top = 0;
+ cc->bounds.width = dev->width;
+ cc->bounds.height = dev->height;
+ cc->defrect = cc->bounds;
+ cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
+ cc->pixelaspect.denominator = 59;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc = -EINVAL;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (unlikely(type != fh->type))
+ return -EINVAL;
+
+ em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (unlikely(!res_get(fh, get_ressource(fh))))
+ return -EBUSY;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vidq);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vbiq);
+
+ return rc;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+ return -EINVAL;
+ if (type != fh->type)
+ return -EINVAL;
+
+ em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ }
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+ videobuf_streamoff(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
+ }
+
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities =
+ V4L2_CAP_SLICED_VBI_CAPTURE |
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+
+ if (dev->vbi_dev)
+ cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
+
+ if (dev->audio_mode.has_audio)
+ cap->capabilities |= V4L2_CAP_AUDIO;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+
+ return 0;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_fmt *fmt;
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
+
+ fmt = format_by_fourcc(fsize->pixel_format);
+ if (!fmt) {
+ em28xx_videodbg("Fourcc format (%08x) invalid.\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ if (dev->board.is_em2800) {
+ if (fsize->index > 1)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = maxw / (1 + fsize->index);
+ fsize->discrete.height = maxh / (1 + fsize->index);
+ return 0;
+ }
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ /* Report a continuous range */
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = 48;
+ fsize->stepwise.min_height = 32;
+ fsize->stepwise.max_width = maxw;
+ fsize->stepwise.max_height = maxh;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ return 0;
+}
+
+/* Sliced VBI ioctls */
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ f->fmt.sliced.service_set = 0;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ rc = -EINVAL;
+
+ return rc;
+}
+
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
+
+ if (f->fmt.sliced.service_set == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+
+ format->fmt.vbi.samples_per_line = dev->vbi_width;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
+ format->fmt.vbi.count[0] = dev->vbi_height;
+ format->fmt.vbi.count[1] = dev->vbi_height;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ if (dev->norm & V4L2_STD_525_60) {
+ /* NTSC */
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ /* PAL */
+ format->fmt.vbi.start[0] = 6;
+ format->fmt.vbi.start[1] = 318;
+ }
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+ else
+ return videobuf_reqbufs(&fh->vb_vbiq, rb);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->vb_vidq, b);
+ else {
+ /* FIXME: I'm not sure yet whether this is a bug in zvbi or
+ the videobuf framework, but we probably shouldn't be
+ returning a buffer larger than that which was asked for.
+ At a minimum, it causes a crash in zvbi since it does
+ a memcpy based on the source buffer length */
+ int result = videobuf_querybuf(&fh->vb_vbiq, b);
+ b->length = dev->vbi_width * dev->vbi_height * 2;
+
+ return result;
+ }
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->vb_vidq, b);
+ else
+ return videobuf_qbuf(&fh->vb_vbiq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
+ O_NONBLOCK);
+ else
+ return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
+ O_NONBLOCK);
+}
+
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+ const struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ if (qc->id < V4L2_CID_BASE ||
+ qc->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * em28xx_v4l2_open()
+ * inits the device and starts isoc transfer
+ */
+static int em28xx_v4l2_open(struct file *filp)
+{
+ int errCode = 0, radio = 0;
+ struct video_device *vdev = video_devdata(filp);
+ struct em28xx *dev = video_drvdata(filp);
+ enum v4l2_buf_type fh_type = 0;
+ struct em28xx_fh *fh;
+ enum v4l2_field field;
+
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
+ fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case VFL_TYPE_VBI:
+ fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ break;
+ case VFL_TYPE_RADIO:
+ radio = 1;
+ break;
+ }
+
+ em28xx_videodbg("open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type],
+ dev->users);
+
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
+ if (!fh) {
+ em28xx_errdev("em28xx-video.c: Out of memory?!\n");
+ mutex_unlock(&dev->lock);
+ return -ENOMEM;
+ }
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = fh_type;
+ filp->private_data = fh;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
+ em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
+ em28xx_set_alternate(dev);
+ em28xx_resolution_set(dev);
+
+ /* Needed, since GPIO might have disabled power of
+ some i2c device
+ */
+ em28xx_wake_i2c(dev);
+
+ }
+ if (fh->radio) {
+ em28xx_videodbg("video_open: setting radio device\n");
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+ }
+
+ dev->users++;
+
+ if (dev->progressive)
+ field = V4L2_FIELD_NONE;
+ else
+ field = V4L2_FIELD_INTERLACED;
+
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
+ sizeof(struct em28xx_buffer), fh, &dev->lock);
+
+ videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct em28xx_buffer), fh, &dev->lock);
+ mutex_unlock(&dev->lock);
+
+ return errCode;
+}
+
+/*
+ * em28xx_realease_resources()
+ * unregisters the v4l2,i2c and usb devices
+ * called when the device gets disconected or at module unload
+*/
+void em28xx_release_analog_resources(struct em28xx *dev)
+{
+
+ /*FIXME: I2C IR should be disconnected */
+
+ if (dev->radio_dev) {
+ if (video_is_registered(dev->radio_dev))
+ video_unregister_device(dev->radio_dev);
+ else
+ video_device_release(dev->radio_dev);
+ dev->radio_dev = NULL;
+ }
+ if (dev->vbi_dev) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vbi_dev));
+ if (video_is_registered(dev->vbi_dev))
+ video_unregister_device(dev->vbi_dev);
+ else
+ video_device_release(dev->vbi_dev);
+ dev->vbi_dev = NULL;
+ }
+ if (dev->vdev) {
+ em28xx_info("V4L2 device %s deregistered\n",
+ video_device_node_name(dev->vdev));
+ if (video_is_registered(dev->vdev))
+ video_unregister_device(dev->vdev);
+ else
+ video_device_release(dev->vdev);
+ dev->vdev = NULL;
+ }
+}
+
+/*
+ * em28xx_v4l2_close()
+ * stops streaming and deallocates all resources allocated by the v4l2
+ * calls and ioctls
+ */
+static int em28xx_v4l2_close(struct file *filp)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int errCode;
+
+ em28xx_videodbg("users=%d\n", dev->users);
+
+ mutex_lock(&dev->lock);
+ if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+ videobuf_stop(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ }
+
+ if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+ videobuf_stop(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
+
+ if (dev->users == 1) {
+ /* the device is already disconnect,
+ free the remaining resources */
+ if (dev->state & DEV_DISCONNECTED) {
+ em28xx_release_resources(dev);
+ kfree(dev->alt_max_pkt_size);
+ mutex_unlock(&dev->lock);
+ kfree(dev);
+ kfree(fh);
+ return 0;
+ }
+
+ /* Save some power by putting tuner to sleep */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
+
+ /* do this before setting alternate! */
+ em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE);
+ em28xx_set_mode(dev, EM28XX_SUSPEND);
+
+ /* set alternate 0 */
+ dev->alt = 0;
+ em28xx_videodbg("setting alternate 0\n");
+ errCode = usb_set_interface(dev->udev, 0, 0);
+ if (errCode < 0) {
+ em28xx_errdev("cannot change alternate number to "
+ "0 (error=%i)\n", errCode);
+ }
+ }
+
+ videobuf_mmap_free(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vbiq);
+ kfree(fh);
+ dev->users--;
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+/*
+ * em28xx_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t
+em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *pos)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ /* FIXME: read() is not prepared to allow changing the video
+ resolution while streaming. Seems a bug at em28xx_set_fmt
+ */
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
+ rc = -EBUSY;
+ else
+ rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ rc = -EBUSY;
+ else
+ rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+/*
+ * em28xx_poll()
+ * will allocate buffers when called for the first time
+ */
+static unsigned int em28xx_poll(struct file *filp, poll_table *wait)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+ } else {
+ return POLLERR;
+ }
+}
+
+static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ unsigned int res;
+
+ mutex_lock(&dev->lock);
+ res = em28xx_poll(filp, wait);
+ mutex_unlock(&dev->lock);
+ return res;
+}
+
+/*
+ * em28xx_v4l2_mmap()
+ */
+static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
+ mutex_unlock(&dev->lock);
+
+ em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
+ rc);
+
+ return rc;
+}
+
+static const struct v4l2_file_operations em28xx_v4l_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .read = em28xx_v4l2_read,
+ .poll = em28xx_v4l2_poll,
+ .mmap = em28xx_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_cropcap = vidioc_cropcap,
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+#endif
+};
+
+static const struct video_device em28xx_video_template = {
+ .fops = &em28xx_v4l_fops,
+ .release = video_device_release,
+ .ioctl_ops = &video_ioctl_ops,
+
+ .tvnorms = V4L2_STD_ALL,
+ .current_norm = V4L2_STD_PAL,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = em28xx_v4l2_open,
+ .release = em28xx_v4l2_close,
+ .unlocked_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_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device em28xx_radio_template = {
+ .name = "em28xx-radio",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/******************************** usb interface ******************************/
+
+
+
+static struct video_device *em28xx_vdev_init(struct em28xx *dev,
+ const struct video_device *template,
+ const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s",
+ dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int em28xx_register_analog_devices(struct em28xx *dev)
+{
+ u8 val;
+ int ret;
+ unsigned int maxw;
+
+ printk(KERN_INFO "%s: v4l2 driver version %s\n",
+ dev->name, EM28XX_VERSION);
+
+ /* set default norm */
+ dev->norm = em28xx_video_template.current_norm;
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+ dev->interlaced = EM28XX_INTERLACED_DEFAULT;
+
+ /* Analog specific initialization */
+ dev->format = &format[0];
+
+ maxw = norm_maxw(dev);
+ /* MaxPacketSize for em2800 is too small to capture at full resolution
+ * use half of maxw as the scaler can only scale to 50% */
+ if (dev->board.is_em2800)
+ maxw /= 2;
+
+ em28xx_set_video_format(dev, format[0].fourcc,
+ maxw, norm_maxh(dev));
+
+ video_mux(dev, 0);
+
+ /* Audio defaults */
+ dev->mute = 1;
+ dev->volume = 0x1f;
+
+/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
+ em28xx_write_reg(dev, EM28XX_R0F_XCLK,
+ (EM28XX_XCLK_AUDIO_UNMUTE | val));
+
+ em28xx_set_outfmt(dev);
+ em28xx_colorlevels_set_default(dev);
+ em28xx_compression_disable(dev);
+
+ /* allocate and fill video video_device struct */
+ dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
+ if (!dev->vdev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+
+ /* register v4l2 video video_device */
+ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
+ video_nr[dev->devno]);
+ if (ret) {
+ em28xx_errdev("unable to register video device (error=%i).\n",
+ ret);
+ return ret;
+ }
+
+ /* Allocate and fill vbi video_device struct */
+ if (em28xx_vbi_supported(dev) == 1) {
+ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+ "vbi");
+
+ /* register v4l2 vbi video_device */
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]);
+ if (ret < 0) {
+ em28xx_errdev("unable to register vbi device\n");
+ return ret;
+ }
+ }
+
+ if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+ dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
+ "radio");
+ if (!dev->radio_dev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+ }
+ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->devno]);
+ if (ret < 0) {
+ em28xx_errdev("can't register radio device\n");
+ return ret;
+ }
+ em28xx_info("Registered radio device as %s\n",
+ video_device_node_name(dev->radio_dev));
+ }
+
+ em28xx_info("V4L2 video device registered as %s\n",
+ video_device_node_name(dev->vdev));
+
+ if (dev->vbi_dev)
+ em28xx_info("V4L2 VBI device registered as %s\n",
+ video_device_node_name(dev->vbi_dev));
+
+ return 0;
+}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 8757523e6863..8757523e6863 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
new file mode 100644
index 000000000000..6345f9331e7f
--- /dev/null
+++ b/drivers/media/usb/gspca/Kconfig
@@ -0,0 +1,425 @@
+menuconfig USB_GSPCA
+ tristate "GSPCA based webcams"
+ depends on VIDEO_V4L2
+ default m
+ ---help---
+ Say Y here if you want to enable selecting webcams based
+ on the GSPCA framework.
+
+ See <file:Documentation/video4linux/gspca.txt> for more info.
+
+ This driver uses the Video For Linux API. You must say Y or M to
+ "Video For Linux" to use this driver.
+
+ To compile this driver as modules, choose M here: the
+ module will be called gspca_main.
+
+
+if USB_GSPCA && VIDEO_V4L2
+
+source "drivers/media/usb/gspca/m5602/Kconfig"
+source "drivers/media/usb/gspca/stv06xx/Kconfig"
+source "drivers/media/usb/gspca/gl860/Kconfig"
+
+config USB_GSPCA_BENQ
+ tristate "Benq USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the Benq DC E300 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_benq.
+
+config USB_GSPCA_CONEX
+ tristate "Conexant Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Conexant chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_conex.
+
+config USB_GSPCA_CPIA1
+ tristate "cpia CPiA (version 1) Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for USB cameras based on the cpia
+ CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
+ applications to understand the videoformat generated by this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_cpia1.
+
+config USB_GSPCA_ETOMS
+ tristate "Etoms USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Etoms chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_etoms.
+
+config USB_GSPCA_FINEPIX
+ tristate "Fujifilm FinePix USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the FinePix chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_finepix.
+
+config USB_GSPCA_JEILINJ
+ tristate "Jeilin JPEG USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on this Jeilin chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_jeilinj.
+
+config USB_GSPCA_JL2005BCD
+ tristate "JL2005B/C/D USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based the
+ JL2005B, JL2005C, or JL2005D chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_jl2005bcd.
+
+config USB_GSPCA_KINECT
+ tristate "Kinect sensor device USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for the Microsoft Kinect sensor device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_kinect.
+
+config USB_GSPCA_KONICA
+ tristate "Konica USB Camera V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Konica chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_konica.
+
+config USB_GSPCA_MARS
+ tristate "Mars USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Mars chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mars.
+
+config USB_GSPCA_MR97310A
+ tristate "Mars-Semi MR97310A USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the MR97310A chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mr97310a.
+
+config USB_GSPCA_NW80X
+ tristate "Divio based (NW80x) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the NW80x chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_nw80x.
+
+config USB_GSPCA_OV519
+ tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on one of these:
+ OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov519.
+
+config USB_GSPCA_OV534
+ tristate "OV534 OV772x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV772x (e.g. Sony Playstation EYE)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534.
+
+config USB_GSPCA_OV534_9
+ tristate "OV534 OV965x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the OV534 chip
+ and sensor OV965x (e.g. Hercules Dualpix)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534_9.
+
+config USB_GSPCA_PAC207
+ tristate "Pixart PAC207 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC207 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac207.
+
+config USB_GSPCA_PAC7302
+ tristate "Pixart PAC7302 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7302 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7302.
+
+config USB_GSPCA_PAC7311
+ tristate "Pixart PAC7311 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the PAC7311 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7311.
+
+config USB_GSPCA_SE401
+ tristate "SE401 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ Endpoints (formerly known as AOX) se401 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_se401.
+
+config USB_GSPCA_SN9C2028
+ tristate "SONIX Dual-Mode USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want streaming support for Sonix SN9C2028 cameras.
+ These are supported as stillcams in libgphoto2/camlibs/sonix.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c2028.
+
+config USB_GSPCA_SN9C20X
+ tristate "SN9C20X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ sn9c20x chips (SN9C201 and SN9C202).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sn9c20x.
+
+config USB_GSPCA_SONIXB
+ tristate "SONIX Bayer USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sonix
+ chips with Bayer format (SN9C101, SN9C102 and SN9C103).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixb.
+
+config USB_GSPCA_SONIXJ
+ tristate "SONIX JPEG USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sonix
+ chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sonixj
+
+config USB_GSPCA_SPCA500
+ tristate "SPCA500 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA500 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca500.
+
+config USB_GSPCA_SPCA501
+ tristate "SPCA501 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA501 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca501.
+
+config USB_GSPCA_SPCA505
+ tristate "SPCA505 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA505 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca505.
+
+config USB_GSPCA_SPCA506
+ tristate "SPCA506 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA506 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca506.
+
+config USB_GSPCA_SPCA508
+ tristate "SPCA508 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA508 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca508.
+
+config USB_GSPCA_SPCA561
+ tristate "SPCA561 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA561 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca561.
+
+config USB_GSPCA_SPCA1528
+ tristate "SPCA1528 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SPCA1528 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca1528.
+
+config USB_GSPCA_SQ905
+ tristate "SQ Technologies SQ905 based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ905 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq905.
+
+config USB_GSPCA_SQ905C
+ tristate "SQ Technologies SQ905C based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ905C chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq905c.
+
+config USB_GSPCA_SQ930X
+ tristate "SQ Technologies SQ930X based USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the SQ930X chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sq930x.
+
+config USB_GSPCA_STK014
+ tristate "Syntek DV4000 (STK014) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STK014 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk014.
+
+config USB_GSPCA_STV0680
+ tristate "STV0680 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STV0680 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stv0680.
+
+config USB_GSPCA_SUNPLUS
+ tristate "SUNPLUS USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the Sunplus
+ SPCA504(abc) SPCA533 SPCA536 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_sunplus.
+
+config USB_GSPCA_T613
+ tristate "T613 (JPEG Compliance) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the T613 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_t613.
+
+config USB_GSPCA_TOPRO
+ tristate "TOPRO USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the
+ TP6800 and TP6810 Topro chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_topro.
+
+config USB_GSPCA_TV8532
+ tristate "TV8532 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the TV8531 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_tv8532.
+
+config USB_GSPCA_VC032X
+ tristate "VC032X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the VC032X chip.
+
+ 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
+ help
+ Say Y here if you want support for Xirlink C-It bases cameras.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_xirlink_cit.
+
+config USB_GSPCA_ZC3XX
+ tristate "ZC3XX USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the ZC3XX chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_zc3xx.
+
+endif
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/usb/gspca/Makefile
index c901da0bd657..c901da0bd657 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/usb/gspca/Makefile
diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/usb/gspca/autogain_functions.c
index 67db674bb044..67db674bb044 100644
--- a/drivers/media/video/gspca/autogain_functions.c
+++ b/drivers/media/usb/gspca/autogain_functions.c
diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/usb/gspca/autogain_functions.h
index d625eafe63eb..d625eafe63eb 100644
--- a/drivers/media/video/gspca/autogain_functions.h
+++ b/drivers/media/usb/gspca/autogain_functions.h
diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/usb/gspca/benq.c
index 352f32190e68..352f32190e68 100644
--- a/drivers/media/video/gspca/benq.c
+++ b/drivers/media/usb/gspca/benq.c
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/usb/gspca/conex.c
index c9052f20435e..c9052f20435e 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/usb/gspca/conex.c
diff --git a/drivers/media/usb/gspca/cpia1.c b/drivers/media/usb/gspca/cpia1.c
new file mode 100644
index 000000000000..b3ba47d4d6a2
--- /dev/null
+++ b/drivers/media/usb/gspca/cpia1.c
@@ -0,0 +1,1905 @@
+/*
+ * cpia CPiA (1) gspca driver
+ *
+ * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the in kernel v4l1 cpia driver which is :
+ *
+ * (C) Copyright 1999-2000 Peter Pregler
+ * (C) Copyright 1999-2000 Scott J. Bertin
+ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
+ * (C) Copyright 2000 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "cpia1"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Vision CPiA");
+MODULE_LICENSE("GPL");
+
+/* constant value's */
+#define MAGIC_0 0x19
+#define MAGIC_1 0x68
+#define DATA_IN 0xc0
+#define DATA_OUT 0x40
+#define VIDEOSIZE_QCIF 0 /* 176x144 */
+#define VIDEOSIZE_CIF 1 /* 352x288 */
+#define SUBSAMPLE_420 0
+#define SUBSAMPLE_422 1
+#define YUVORDER_YUYV 0
+#define YUVORDER_UYVY 1
+#define NOT_COMPRESSED 0
+#define COMPRESSED 1
+#define NO_DECIMATION 0
+#define DECIMATION_ENAB 1
+#define EOI 0xff /* End Of Image */
+#define EOL 0xfd /* End Of Line */
+#define FRAME_HEADER_SIZE 64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE 0
+#define CPIA_GRAB_CONTINEOUS 1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE 0
+#define CPIA_COMPRESSION_AUTO 1
+#define CPIA_COMPRESSION_MANUAL 2
+#define CPIA_COMPRESSION_TARGET_QUALITY 0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE 0
+#define GRABSTATE 1
+#define STREAMSTATE 2
+#define FATALERROR 3
+#define CMDERROR 4
+#define DEBUGFLAGS 5
+#define VPSTATUS 6
+#define ERRORCODE 7
+
+/* SystemState */
+#define UNINITIALISED_STATE 0
+#define PASS_THROUGH_STATE 1
+#define LO_POWER_STATE 2
+#define HI_POWER_STATE 3
+#define WARM_BOOT_STATE 4
+
+/* GrabState */
+#define GRAB_IDLE 0
+#define GRAB_ACTIVE 1
+#define GRAB_DONE 2
+
+/* StreamState */
+#define STREAM_NOT_READY 0
+#define STREAM_READY 1
+#define STREAM_OPEN 2
+#define STREAM_PAUSED 3
+#define STREAM_FINISHED 4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG 1
+#define SYSTEM_FLAG 2
+#define INT_CTRL_FLAG 4
+#define PROCESS_FLAG 8
+#define COM_FLAG 16
+#define VP_CTRL_FLAG 32
+#define CAPTURE_FLAG 64
+#define DEBUG_FLAG 128
+
+/* VPStatus */
+#define VP_STATE_OK 0x00
+
+#define VP_STATE_FAILED_VIDEOINIT 0x01
+#define VP_STATE_FAILED_AECACBINIT 0x02
+#define VP_STATE_AEC_MAX 0x04
+#define VP_STATE_ACB_BMAX 0x08
+
+#define VP_STATE_ACB_RMIN 0x10
+#define VP_STATE_ACB_GMIN 0x20
+#define VP_STATE_ACB_RMAX 0x40
+#define VP_STATE_ACB_GMAX 0x80
+
+/* default (minimum) compensation values */
+#define COMP_RED 220
+#define COMP_GREEN1 214
+#define COMP_GREEN2 COMP_GREEN1
+#define COMP_BLUE 230
+
+/* exposure status */
+#define EXPOSURE_VERY_LIGHT 0
+#define EXPOSURE_LIGHT 1
+#define EXPOSURE_NORMAL 2
+#define EXPOSURE_DARK 3
+#define EXPOSURE_VERY_DARK 4
+
+#define CPIA_MODULE_CPIA (0 << 5)
+#define CPIA_MODULE_SYSTEM (1 << 5)
+#define CPIA_MODULE_VP_CTRL (5 << 5)
+#define CPIA_MODULE_CAPTURE (6 << 5)
+#define CPIA_MODULE_DEBUG (7 << 5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
+
+#define ROUND_UP_EXP_FOR_FLICKER 15
+
+/* Constants for automatic frame rate adjustment */
+#define MAX_EXP 302
+#define MAX_EXP_102 255
+#define LOW_EXP 140
+#define VERY_LOW_EXP 70
+#define TC 94
+#define EXP_ACC_DARK 50
+#define EXP_ACC_LIGHT 90
+#define HIGH_COMP_102 160
+#define MAX_COMP 239
+#define DARK_TIME 3
+#define LIGHT_TIME 3
+
+#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
+ sd->params.version.firmwareRevision == (y))
+
+#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
+#define BRIGHTNESS_DEF 50
+#define CONTRAST_DEF 48
+#define SATURATION_DEF 50
+#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
+#define ILLUMINATORS_1_DEF 0
+#define ILLUMINATORS_2_DEF 0
+#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+struct cam_params {
+ struct {
+ u8 firmwareVersion;
+ u8 firmwareRevision;
+ u8 vcVersion;
+ u8 vcRevision;
+ } version;
+ struct {
+ u16 vendor;
+ u16 product;
+ u16 deviceRevision;
+ } pnpID;
+ struct {
+ u8 vpVersion;
+ u8 vpRevision;
+ u16 cameraHeadID;
+ } vpVersion;
+ struct {
+ u8 systemState;
+ u8 grabState;
+ u8 streamState;
+ u8 fatalError;
+ u8 cmdError;
+ u8 debugFlags;
+ u8 vpStatus;
+ u8 errorCode;
+ } status;
+ struct {
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ } colourParams;
+ struct {
+ u8 gainMode;
+ u8 expMode;
+ u8 compMode;
+ u8 centreWeight;
+ u8 gain;
+ u8 fineExp;
+ u8 coarseExpLo;
+ u8 coarseExpHi;
+ u8 redComp;
+ u8 green1Comp;
+ u8 green2Comp;
+ u8 blueComp;
+ } exposure;
+ struct {
+ u8 balanceMode;
+ u8 redGain;
+ u8 greenGain;
+ u8 blueGain;
+ } colourBalance;
+ struct {
+ u8 divisor;
+ u8 baserate;
+ } sensorFps;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } apcor;
+ struct {
+ u8 disabled;
+ u8 flickerMode;
+ u8 coarseJump;
+ u8 allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct { /* Intel QX3 specific data */
+ u8 qx3_detected; /* a QX3 is present */
+ u8 toplight; /* top light lit , R/W */
+ u8 bottomlight; /* bottom light lit, R/W */
+ u8 button; /* snapshot button pressed (R/O) */
+ u8 cradled; /* microscope is in cradle (R/O) */
+ } qx3;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct cam_params params; /* camera settings */
+
+ atomic_t cam_exposure;
+ atomic_t fps;
+ int exposure_count;
+ u8 exposure_status;
+ struct v4l2_ctrl *freq;
+ u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
+ u8 first_frame;
+};
+
+static const struct v4l2_pix_format mode[] = {
+ {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ /* The sizeimage is trial and error, as with low framerates
+ the camera will pad out usb frames, making the image
+ data larger then strictly necessary */
+ .bytesperline = 160,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 172,
+ .sizeimage = 65536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 262144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+
+static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
+{
+ u8 requesttype;
+ unsigned int pipe;
+ int ret, databytes = command[6] | (command[7] << 8);
+ /* Sometimes we see spurious EPIPE errors */
+ int retries = 3;
+
+ if (command[0] == DATA_IN) {
+ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else if (command[0] == DATA_OUT) {
+ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
+ requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ } else {
+ PDEBUG(D_ERR, "Unexpected first byte of command: %x",
+ command[0]);
+ return -EINVAL;
+ }
+
+retry:
+ ret = usb_control_msg(gspca_dev->dev, pipe,
+ command[1],
+ requesttype,
+ command[2] | (command[3] << 8),
+ command[4] | (command[5] << 8),
+ gspca_dev->usb_buf, databytes, 1000);
+
+ if (ret < 0)
+ pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
+
+ if (ret == -EPIPE && retries > 0) {
+ retries--;
+ goto retry;
+ }
+
+ return (ret < 0) ? ret : 0;
+}
+
+/* send an arbitrary command to the camera */
+static int do_command(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, datasize;
+ u8 cmd[8];
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ datasize = 8;
+ break;
+ case CPIA_COMMAND_ReadMCPorts:
+ case CPIA_COMMAND_ReadVCRegs:
+ datasize = 4;
+ break;
+ default:
+ datasize = 0;
+ break;
+ }
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret)
+ return ret;
+
+ switch (command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
+ sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
+ sd->params.version.vcVersion = gspca_dev->usb_buf[2];
+ sd->params.version.vcRevision = gspca_dev->usb_buf[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ sd->params.pnpID.vendor =
+ gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
+ sd->params.pnpID.product =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ sd->params.pnpID.deviceRevision =
+ gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ sd->params.status.systemState = gspca_dev->usb_buf[0];
+ sd->params.status.grabState = gspca_dev->usb_buf[1];
+ sd->params.status.streamState = gspca_dev->usb_buf[2];
+ sd->params.status.fatalError = gspca_dev->usb_buf[3];
+ sd->params.status.cmdError = gspca_dev->usb_buf[4];
+ sd->params.status.debugFlags = gspca_dev->usb_buf[5];
+ sd->params.status.vpStatus = gspca_dev->usb_buf[6];
+ sd->params.status.errorCode = gspca_dev->usb_buf[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
+ sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
+ sd->params.vpVersion.cameraHeadID =
+ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
+ sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
+ sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
+ sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
+ sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
+ break;
+ case CPIA_COMMAND_GetExposure:
+ sd->params.exposure.gain = gspca_dev->usb_buf[0];
+ sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
+ sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
+ sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
+ sd->params.exposure.redComp = gspca_dev->usb_buf[4];
+ sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
+ sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
+ sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
+ break;
+
+ case CPIA_COMMAND_ReadMCPorts:
+ /* test button press */
+ a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
+ if (a != sd->params.qx3.button) {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
+ input_sync(gspca_dev->input_dev);
+#endif
+ sd->params.qx3.button = a;
+ }
+ if (sd->params.qx3.button) {
+ /* button pressed - unlock the latch */
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xdf, 0xdf, 0);
+ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
+ 3, 0xff, 0xff, 0);
+ }
+
+ /* test whether microscope is cradled */
+ sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
+ break;
+ }
+
+ return 0;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ u8 cmd[8];
+
+ cmd[0] = command >> 8;
+ cmd[1] = command & 0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ gspca_dev->usb_buf[0] = e;
+ gspca_dev->usb_buf[1] = f;
+ gspca_dev->usb_buf[2] = g;
+ gspca_dev->usb_buf[3] = h;
+ gspca_dev->usb_buf[4] = i;
+ gspca_dev->usb_buf[5] = j;
+ gspca_dev->usb_buf[6] = k;
+ gspca_dev->usb_buf[7] = l;
+
+ return cpia_usb_transferCmd(gspca_dev, cmd);
+}
+
+/* find_over_exposure
+ * Finds a suitable value of OverExposure for use with SetFlickerCtrl
+ * Some calculation is required because this value changes with the brightness
+ * set with SetColourParameters
+ *
+ * Parameters: Brightness - last brightness value set with SetColourParameters
+ *
+ * Returns: OverExposure value to use with SetFlickerCtrl
+ */
+#define FLICKER_MAX_EXPOSURE 250
+#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
+#define FLICKER_BRIGHTNESS_CONSTANT 59
+static int find_over_exposure(int brightness)
+{
+ int MaxAllowableOverExposure, OverExposure;
+
+ MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
+ FLICKER_BRIGHTNESS_CONSTANT;
+
+ if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
+ OverExposure = MaxAllowableOverExposure;
+ else
+ OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
+
+ return OverExposure;
+}
+#undef FLICKER_MAX_EXPOSURE
+#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
+#undef FLICKER_BRIGHTNESS_CONSTANT
+
+/* initialise cam_data structure */
+static void reset_camera_params(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam_params *params = &sd->params;
+
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ params->colourParams.brightness = BRIGHTNESS_DEF;
+ params->colourParams.contrast = CONTRAST_DEF;
+ params->colourParams.saturation = SATURATION_DEF;
+ params->exposure.gainMode = 4;
+ params->exposure.expMode = 2; /* AEC */
+ params->exposure.compMode = 1;
+ params->exposure.centreWeight = 1;
+ params->exposure.gain = 0;
+ params->exposure.fineExp = 0;
+ params->exposure.coarseExpLo = 185;
+ params->exposure.coarseExpHi = 0;
+ params->exposure.redComp = COMP_RED;
+ params->exposure.green1Comp = COMP_GREEN1;
+ params->exposure.green2Comp = COMP_GREEN2;
+ params->exposure.blueComp = COMP_BLUE;
+ params->colourBalance.balanceMode = 2; /* ACB */
+ params->colourBalance.redGain = 32;
+ params->colourBalance.greenGain = 6;
+ params->colourBalance.blueGain = 92;
+ params->apcor.gain1 = 0x18;
+ params->apcor.gain2 = 0x16;
+ params->apcor.gain4 = 0x24;
+ params->apcor.gain8 = 0x34;
+ params->vlOffset.gain1 = 20;
+ params->vlOffset.gain2 = 24;
+ params->vlOffset.gain4 = 26;
+ params->vlOffset.gain8 = 26;
+ params->compressionParams.hysteresis = 3;
+ params->compressionParams.threshMax = 11;
+ params->compressionParams.smallStep = 1;
+ params->compressionParams.largeStep = 3;
+ params->compressionParams.decimationHysteresis = 2;
+ params->compressionParams.frDiffStepThresh = 5;
+ params->compressionParams.qDiffStepThresh = 3;
+ params->compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ params->sensorFps.divisor = 1;
+ params->sensorFps.baserate = 1;
+
+ params->flickerControl.flickerMode = 0;
+ params->flickerControl.disabled = 1;
+ params->flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [params->sensorFps.baserate]
+ [params->sensorFps.divisor];
+ params->flickerControl.allowableOverExposure =
+ find_over_exposure(params->colourParams.brightness);
+
+ params->yuvThreshold.yThreshold = 6; /* From windows driver */
+ params->yuvThreshold.uvThreshold = 6; /* From windows driver */
+
+ params->format.subSample = SUBSAMPLE_420;
+ params->format.yuvOrder = YUVORDER_YUYV;
+
+ params->compression.mode = CPIA_COMPRESSION_AUTO;
+ params->compression.decimation = NO_DECIMATION;
+
+ params->compressionTarget.frTargeting = COMP_TARGET_DEF;
+ params->compressionTarget.targetFR = 15; /* From windows driver */
+ params->compressionTarget.targetQ = 5; /* From windows driver */
+
+ params->qx3.qx3_detected = 0;
+ params->qx3.toplight = 0;
+ params->qx3.bottomlight = 0;
+ params->qx3.button = 0;
+ params->qx3.cradled = 0;
+}
+
+static void printstatus(struct cam_params *params)
+{
+ PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
+ params->status.systemState, params->status.grabState,
+ params->status.streamState, params->status.fatalError,
+ params->status.cmdError, params->status.debugFlags,
+ params->status.vpStatus, params->status.errorCode);
+}
+
+static int goto_low_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != LO_POWER_STATE) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR,
+ "unexpected state after lo power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ }
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in LOW power state");
+ return 0;
+}
+
+static int goto_high_power(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ msleep_interruptible(40); /* windows driver does it too */
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.systemState != HI_POWER_STATE) {
+ PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -EIO;
+ }
+
+ PDEBUG(D_CONF, "camera now in HIGH power state");
+ return 0;
+}
+
+static int get_version_information(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* GetCPIAVersion */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* GetPnPID */
+ return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+static int save_camera_state(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+}
+
+static int command_setformat(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
+ sd->params.format.videoSize,
+ sd->params.format.subSample,
+ sd->params.format.yuvOrder, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetROI,
+ sd->params.roi.colStart, sd->params.roi.colEnd,
+ sd->params.roi.rowStart, sd->params.roi.rowEnd);
+}
+
+static int command_setcolourparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
+ sd->params.colourParams.brightness,
+ sd->params.colourParams.contrast,
+ sd->params.colourParams.saturation, 0);
+}
+
+static int command_setapcor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
+ sd->params.apcor.gain1,
+ sd->params.apcor.gain2,
+ sd->params.apcor.gain4,
+ sd->params.apcor.gain8);
+}
+
+static int command_setvloffset(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
+ sd->params.vlOffset.gain1,
+ sd->params.vlOffset.gain2,
+ sd->params.vlOffset.gain4,
+ sd->params.vlOffset.gain8);
+}
+
+static int command_setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ sd->params.exposure.gainMode,
+ 1,
+ sd->params.exposure.compMode,
+ sd->params.exposure.centreWeight,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ sd->params.exposure.redComp,
+ sd->params.exposure.green1Comp,
+ sd->params.exposure.green2Comp,
+ sd->params.exposure.blueComp);
+ if (ret)
+ return ret;
+
+ if (sd->params.exposure.expMode != 1) {
+ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
+ 0,
+ sd->params.exposure.expMode,
+ 0, 0,
+ sd->params.exposure.gain,
+ sd->params.exposure.fineExp,
+ sd->params.exposure.coarseExpLo,
+ sd->params.exposure.coarseExpHi,
+ 0, 0, 0, 0);
+ }
+
+ return ret;
+}
+
+static int command_setcolourbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->params.colourBalance.balanceMode == 1) {
+ int ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 1,
+ sd->params.colourBalance.redGain,
+ sd->params.colourBalance.greenGain,
+ sd->params.colourBalance.blueGain);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 2) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ }
+ if (sd->params.colourBalance.balanceMode == 3) {
+ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+
+ return -EINVAL;
+}
+
+static int command_setcompressiontarget(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
+ sd->params.compressionTarget.frTargeting,
+ sd->params.compressionTarget.targetFR,
+ sd->params.compressionTarget.targetQ, 0);
+}
+
+static int command_setyuvtresh(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
+ sd->params.yuvThreshold.yThreshold,
+ sd->params.yuvThreshold.uvThreshold, 0, 0);
+}
+
+static int command_setcompressionparams(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command_extended(gspca_dev,
+ CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ sd->params.compressionParams.hysteresis,
+ sd->params.compressionParams.threshMax,
+ sd->params.compressionParams.smallStep,
+ sd->params.compressionParams.largeStep,
+ sd->params.compressionParams.decimationHysteresis,
+ sd->params.compressionParams.frDiffStepThresh,
+ sd->params.compressionParams.qDiffStepThresh,
+ sd->params.compressionParams.decimationThreshMod);
+}
+
+static int command_setcompression(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ sd->params.compression.mode,
+ sd->params.compression.decimation, 0, 0);
+}
+
+static int command_setsensorfps(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
+ sd->params.sensorFps.divisor,
+ sd->params.sensorFps.baserate, 0, 0);
+}
+
+static int command_setflickerctrl(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
+ sd->params.flickerControl.flickerMode,
+ sd->params.flickerControl.coarseJump,
+ sd->params.flickerControl.allowableOverExposure,
+ 0);
+}
+
+static int command_setecptiming(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
+ sd->params.ecpTiming, 0, 0, 0);
+}
+
+static int command_pause(struct gspca_dev *gspca_dev)
+{
+ return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+}
+
+static int command_resume(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
+ 0, sd->params.streamStartLine, 0, 0);
+}
+
+static int command_setlights(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, p1, p2;
+
+ p1 = (sd->params.qx3.bottomlight == 0) << 1;
+ p2 = (sd->params.qx3.toplight == 0) << 3;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
+ 0x90, 0x8f, 0x50, 0);
+ if (ret)
+ return ret;
+
+ return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
+ p1 | p2 | 0xe0, 0);
+}
+
+static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
+{
+ /* Everything in here is from the Windows driver */
+/* define for compgain calculation */
+#if 0
+#define COMPGAIN(base, curexp, newexp) \
+ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
+ (float)(u8)(basecomp - 128))
+#else
+ /* equivalent functions without floating point math */
+#define COMPGAIN(base, curexp, newexp) \
+ (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
+#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
+ (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
+#endif
+
+ struct sd *sd = (struct sd *) gspca_dev;
+ int currentexp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi * 256;
+ int ret, startexp;
+
+ if (on) {
+ int cj = sd->params.flickerControl.coarseJump;
+ sd->params.flickerControl.flickerMode = 1;
+ sd->params.flickerControl.disabled = 0;
+ if (sd->params.exposure.expMode != 2) {
+ sd->params.exposure.expMode = 2;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ currentexp = currentexp << sd->params.exposure.gain;
+ sd->params.exposure.gain = 0;
+ /* round down current exposure to nearest value */
+ startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
+ if (startexp < 1)
+ startexp = 1;
+ startexp = (startexp * cj) - 1;
+ if (FIRMWARE_VERSION(1, 2))
+ while (startexp > MAX_EXP_102)
+ startexp -= cj;
+ else
+ while (startexp > MAX_EXP)
+ startexp -= cj;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ if (currentexp > startexp) {
+ if (currentexp > (2 * startexp))
+ currentexp = 2 * startexp;
+ sd->params.exposure.redComp =
+ COMPGAIN(COMP_RED, currentexp, startexp);
+ sd->params.exposure.green1Comp =
+ COMPGAIN(COMP_GREEN1, currentexp, startexp);
+ sd->params.exposure.green2Comp =
+ COMPGAIN(COMP_GREEN2, currentexp, startexp);
+ sd->params.exposure.blueComp =
+ COMPGAIN(COMP_BLUE, currentexp, startexp);
+ } else {
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ }
+ if (FIRMWARE_VERSION(1, 2))
+ sd->params.exposure.compMode = 0;
+ else
+ sd->params.exposure.compMode = 1;
+
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x18;
+ sd->params.apcor.gain4 = 0x16;
+ sd->params.apcor.gain8 = 0x14;
+ } else {
+ sd->params.flickerControl.flickerMode = 0;
+ sd->params.flickerControl.disabled = 1;
+ /* Average equivalent coarse for each comp channel */
+ startexp = EXP_FROM_COMP(COMP_RED,
+ sd->params.exposure.redComp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN1,
+ sd->params.exposure.green1Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_GREEN2,
+ sd->params.exposure.green2Comp, currentexp);
+ startexp += EXP_FROM_COMP(COMP_BLUE,
+ sd->params.exposure.blueComp, currentexp);
+ startexp = startexp >> 2;
+ while (startexp > MAX_EXP && sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ startexp = startexp >> 1;
+ ++sd->params.exposure.gain;
+ }
+ if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
+ startexp = MAX_EXP_102;
+ if (startexp > MAX_EXP)
+ startexp = MAX_EXP;
+ sd->params.exposure.coarseExpLo = startexp & 0xff;
+ sd->params.exposure.coarseExpHi = startexp >> 8;
+ sd->params.exposure.redComp = COMP_RED;
+ sd->params.exposure.green1Comp = COMP_GREEN1;
+ sd->params.exposure.green2Comp = COMP_GREEN2;
+ sd->params.exposure.blueComp = COMP_BLUE;
+ sd->params.exposure.compMode = 1;
+ sd->params.apcor.gain1 = 0x18;
+ sd->params.apcor.gain2 = 0x16;
+ sd->params.apcor.gain4 = 0x24;
+ sd->params.apcor.gain8 = 0x34;
+ }
+ sd->params.vlOffset.gain1 = 20;
+ sd->params.vlOffset.gain2 = 24;
+ sd->params.vlOffset.gain4 = 26;
+ sd->params.vlOffset.gain8 = 26;
+
+ if (apply) {
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+#undef EXP_FROM_COMP
+#undef COMPGAIN
+}
+
+/* monitor the exposure and adjust the sensor frame rate if needed */
+static void monitor_exposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp_acc, bcomp, cmd[8];
+ int ret, light_exp, dark_exp, very_dark_exp;
+ int old_exposure, new_exposure, framerate;
+ int setfps = 0, setexp = 0, setflicker = 0;
+
+ /* get necessary stats and register settings from camera */
+ /* do_command can't handle this, so do it ourselves */
+ cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
+ cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
+ cmd[2] = 30;
+ cmd[3] = 4;
+ cmd[4] = 9;
+ cmd[5] = 8;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ ret = cpia_usb_transferCmd(gspca_dev, cmd);
+ if (ret) {
+ pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
+ return;
+ }
+ exp_acc = gspca_dev->usb_buf[0];
+ bcomp = gspca_dev->usb_buf[1];
+
+ light_exp = sd->params.colourParams.brightness +
+ TC - 50 + EXP_ACC_LIGHT;
+ if (light_exp > 255)
+ light_exp = 255;
+ dark_exp = sd->params.colourParams.brightness +
+ TC - 50 - EXP_ACC_DARK;
+ if (dark_exp < 0)
+ dark_exp = 0;
+ very_dark_exp = dark_exp / 2;
+
+ old_exposure = sd->params.exposure.coarseExpHi * 256 +
+ sd->params.exposure.coarseExpLo;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
+ HIGH_COMP_102;
+ bcomp += 128; /* decode */
+ if (bcomp >= max_comp && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ } else {
+ /* Flicker control off */
+ if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
+ /* dark */
+ if (exp_acc < very_dark_exp) {
+ /* very dark */
+ if (sd->exposure_status == EXPOSURE_VERY_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_DARK;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just dark */
+ if (sd->exposure_status == EXPOSURE_DARK)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_DARK;
+ sd->exposure_count = 1;
+ }
+ }
+ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
+ /* light */
+ if (old_exposure <= VERY_LOW_EXP) {
+ /* very light */
+ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status =
+ EXPOSURE_VERY_LIGHT;
+ sd->exposure_count = 1;
+ }
+ } else {
+ /* just light */
+ if (sd->exposure_status == EXPOSURE_LIGHT)
+ ++sd->exposure_count;
+ else {
+ sd->exposure_status = EXPOSURE_LIGHT;
+ sd->exposure_count = 1;
+ }
+ }
+ } else {
+ /* not dark or light */
+ sd->exposure_status = EXPOSURE_NORMAL;
+ }
+ }
+
+ framerate = atomic_read(&sd->fps);
+ if (framerate > 30 || framerate < 1)
+ framerate = 1;
+
+ if (!sd->params.flickerControl.disabled) {
+ /* Flicker control on */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 2) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < old_exposure / 2)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
+ MAX_EXP;
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+ setflicker = 1;
+
+ new_exposure = sd->params.flickerControl.coarseJump-1;
+ while (new_exposure < 2 * old_exposure &&
+ new_exposure +
+ sd->params.flickerControl.coarseJump < max_exp)
+ new_exposure +=
+ sd->params.flickerControl.coarseJump;
+ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
+ sd->params.exposure.coarseExpHi = new_exposure >> 8;
+ setexp = 1;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ } else {
+ /* Flicker control off */
+ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
+ sd->exposure_status == EXPOSURE_DARK) &&
+ sd->exposure_count >= DARK_TIME * framerate &&
+ sd->params.sensorFps.divisor < 2) {
+
+ /* dark for too long */
+ ++sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain > 0) {
+ --sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
+
+ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
+ sd->exposure_status == EXPOSURE_LIGHT) &&
+ sd->exposure_count >= LIGHT_TIME * framerate &&
+ sd->params.sensorFps.divisor > 0) {
+
+ /* light for too long */
+ --sd->params.sensorFps.divisor;
+ setfps = 1;
+
+ if (sd->params.exposure.gain <
+ sd->params.exposure.gainMode - 1) {
+ ++sd->params.exposure.gain;
+ setexp = 1;
+ }
+ sd->exposure_status = EXPOSURE_NORMAL;
+ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
+ }
+ }
+
+ if (setexp)
+ command_setexposure(gspca_dev);
+
+ if (setfps)
+ command_setsensorfps(gspca_dev);
+
+ if (setflicker)
+ command_setflickerctrl(gspca_dev);
+}
+
+/*-----------------------------------------------------------------*/
+/* if flicker is switched off, this function switches it back on.It checks,
+ however, that conditions are suitable before restarting it.
+ This should only be called for firmware version 1.2.
+
+ It also adjust the colour balance when an exposure step is detected - as
+ long as flicker is running
+*/
+static void restart_flicker(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int cam_exposure, old_exp;
+
+ if (!FIRMWARE_VERSION(1, 2))
+ return;
+
+ cam_exposure = atomic_read(&sd->cam_exposure);
+
+ if (sd->params.flickerControl.flickerMode == 0 ||
+ cam_exposure == 0)
+ return;
+
+ old_exp = sd->params.exposure.coarseExpLo +
+ sd->params.exposure.coarseExpHi*256;
+ /*
+ see how far away camera exposure is from a valid
+ flicker exposure value
+ */
+ cam_exposure %= sd->params.flickerControl.coarseJump;
+ if (!sd->params.flickerControl.disabled &&
+ cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
+ /* Flicker control auto-disabled */
+ sd->params.flickerControl.disabled = 1;
+ }
+
+ if (sd->params.flickerControl.disabled &&
+ old_exp > sd->params.flickerControl.coarseJump +
+ ROUND_UP_EXP_FOR_FLICKER) {
+ /* exposure is now high enough to switch
+ flicker control back on */
+ set_flicker(gspca_dev, 1, 1);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+ reset_camera_params(gspca_dev);
+
+ PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
+ id->idVendor, id->idProduct);
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = mode;
+ cam->nmodes = ARRAY_SIZE(mode);
+
+ goto_low_power(gspca_dev);
+ /* Check the firmware version. */
+ sd->params.version.firmwareVersion = 0;
+ get_version_information(gspca_dev);
+ if (sd->params.version.firmwareVersion != 1) {
+ PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
+ sd->params.version.firmwareVersion);
+ return -ENODEV;
+ }
+
+ /* A bug in firmware 1-02 limits gainMode to 2 */
+ if (sd->params.version.firmwareRevision <= 2 &&
+ sd->params.exposure.gainMode > 2) {
+ sd->params.exposure.gainMode = 2;
+ }
+
+ /* set QX3 detected flag */
+ sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
+ sd->params.pnpID.product == 0x0001);
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int priv, ret;
+
+ /* Start the camera in low power mode */
+ if (goto_low_power(gspca_dev)) {
+ if (sd->params.status.systemState != WARM_BOOT_STATE) {
+ PDEBUG(D_ERR, "unexpected systemstate: %02x",
+ sd->params.status.systemState);
+ printstatus(&sd->params);
+ return -ENODEV;
+ }
+
+ /* FIXME: this is just dirty trial and error */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
+ 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = goto_low_power(gspca_dev);
+ if (ret)
+ return ret;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version. */
+ sd->params.version.firmwareVersion = 0;
+ get_version_information(gspca_dev);
+
+ /* The fatal error checking should be done after
+ * the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
+ STREAMSTATE, 0, STREAM_NOT_READY, 0);
+ if (ret)
+ return ret;
+
+ /* GotoHiPower */
+ ret = goto_high_power(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Check the camera status */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ if (sd->params.status.fatalError) {
+ PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
+ sd->params.status.fatalError,
+ sd->params.status.vpStatus);
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+ if (ret)
+ return ret;
+
+ /* Determine video mode settings */
+ sd->params.streamStartLine = 120;
+
+ priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (priv & 0x01) { /* crop */
+ sd->params.roi.colStart = 2;
+ sd->params.roi.rowStart = 6;
+ } else {
+ sd->params.roi.colStart = 0;
+ sd->params.roi.rowStart = 0;
+ }
+
+ if (priv & 0x02) { /* quarter */
+ sd->params.format.videoSize = VIDEOSIZE_QCIF;
+ sd->params.roi.colStart /= 2;
+ sd->params.roi.rowStart /= 2;
+ sd->params.streamStartLine /= 2;
+ } else
+ sd->params.format.videoSize = VIDEOSIZE_CIF;
+
+ sd->params.roi.colEnd = sd->params.roi.colStart +
+ (gspca_dev->width >> 3);
+ sd->params.roi.rowEnd = sd->params.roi.rowStart +
+ (gspca_dev->height >> 2);
+
+ /* And now set the camera to a known state */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_CONTINEOUS, 0, 0, 0);
+ if (ret)
+ return ret;
+ /* We start with compression disabled, as we need one uncompressed
+ frame to handle later compressed frames */
+ ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+ if (ret)
+ return ret;
+ ret = command_setcompressiontarget(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setformat(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setyuvtresh(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setecptiming(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcompressionparams(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setexposure(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setcolourbalance(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setsensorfps(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setapcor(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setflickerctrl(gspca_dev);
+ if (ret)
+ return ret;
+ ret = command_setvloffset(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Start stream */
+ ret = command_resume(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Wait 6 frames before turning compression on for the sensor to get
+ all settings and AEC/ACB to settle */
+ sd->first_frame = 6;
+ sd->exposure_status = EXPOSURE_NORMAL;
+ sd->exposure_count = 0;
+ atomic_set(&sd->cam_exposure, 0);
+ atomic_set(&sd->fps, 0);
+
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ command_pause(gspca_dev);
+
+ /* save camera state for later open (developers guide ch 3.5.3) */
+ save_camera_state(gspca_dev);
+
+ /* GotoLoPower */
+ goto_low_power(gspca_dev);
+
+ /* Update the camera status */
+ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->params.qx3.button) {
+ /* The camera latch will hold the pressed state until we reset
+ the latch, so we do not reset sd->params.qx3.button now, to
+ avoid a false keypress being reported the next sd_start */
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+#endif
+}
+
+/* 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;
+ int ret;
+
+ /* Start / Stop the camera to make sure we are talking to
+ a supported camera, and to get some information from it
+ to print. */
+ ret = sd_start(gspca_dev);
+ if (ret)
+ return ret;
+
+ /* Ensure the QX3 illuminators' states are restored upon resume,
+ or disable the illuminator controls, if this isn't a QX3 */
+ if (sd->params.qx3.qx3_detected)
+ command_setlights(gspca_dev);
+
+ sd_stopN(gspca_dev);
+
+ PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)",
+ sd->params.version.firmwareVersion,
+ sd->params.version.firmwareRevision,
+ sd->params.version.vcVersion,
+ sd->params.version.vcRevision);
+ PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x",
+ sd->params.pnpID.vendor, sd->params.pnpID.product,
+ sd->params.pnpID.deviceRevision);
+ PDEBUG(D_PROBE, "VP-Version: %d.%d %04x",
+ sd->params.vpVersion.vpVersion,
+ sd->params.vpVersion.vpRevision,
+ sd->params.vpVersion.cameraHeadID);
+
+ return 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Check for SOF */
+ if (len >= 64 &&
+ data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
+ data[16] == sd->params.format.videoSize &&
+ data[17] == sd->params.format.subSample &&
+ data[18] == sd->params.format.yuvOrder &&
+ data[24] == sd->params.roi.colStart &&
+ data[25] == sd->params.roi.colEnd &&
+ data[26] == sd->params.roi.rowStart &&
+ data[27] == sd->params.roi.rowEnd) {
+ u8 *image;
+
+ atomic_set(&sd->cam_exposure, data[39] * 2);
+ atomic_set(&sd->fps, data[41]);
+
+ /* Check for proper EOF for last frame */
+ image = gspca_dev->image;
+ if (image != NULL &&
+ gspca_dev->image_len > 4 &&
+ image[gspca_dev->image_len - 4] == 0xff &&
+ image[gspca_dev->image_len - 3] == 0xff &&
+ image[gspca_dev->image_len - 2] == 0xff &&
+ image[gspca_dev->image_len - 1] == 0xff)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Set the normal compression settings once we have captured a
+ few uncompressed frames (and AEC has hopefully settled) */
+ if (sd->first_frame) {
+ sd->first_frame--;
+ if (sd->first_frame == 0)
+ command_setcompression(gspca_dev);
+ }
+
+ /* Switch flicker control back on if it got turned off */
+ restart_flicker(gspca_dev);
+
+ /* If AEC is enabled, monitor the exposure and
+ adjust the sensor frame rate if needed */
+ if (sd->params.exposure.expMode == 2)
+ monitor_exposure(gspca_dev);
+
+ /* Update our knowledge of the camera state */
+ do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+ do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ sd->params.colourParams.brightness = ctrl->val;
+ sd->params.flickerControl.allowableOverExposure =
+ find_over_exposure(sd->params.colourParams.brightness);
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+ if (!gspca_dev->usb_err)
+ gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
+ break;
+ case V4L2_CID_CONTRAST:
+ sd->params.colourParams.contrast = ctrl->val;
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+ break;
+ case V4L2_CID_SATURATION:
+ sd->params.colourParams.saturation = ctrl->val;
+ gspca_dev->usb_err = command_setcolourparams(gspca_dev);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+ sd->params.flickerControl.coarseJump =
+ flicker_jumps[sd->mainsFreq]
+ [sd->params.sensorFps.baserate]
+ [sd->params.sensorFps.divisor];
+
+ gspca_dev->usb_err = set_flicker(gspca_dev,
+ ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
+ gspca_dev->streaming);
+ break;
+ case V4L2_CID_ILLUMINATORS_1:
+ sd->params.qx3.bottomlight = ctrl->val;
+ gspca_dev->usb_err = command_setlights(gspca_dev);
+ break;
+ case V4L2_CID_ILLUMINATORS_2:
+ sd->params.qx3.toplight = ctrl->val;
+ gspca_dev->usb_err = command_setlights(gspca_dev);
+ break;
+ case CPIA1_CID_COMP_TARGET:
+ sd->params.compressionTarget.frTargeting = ctrl->val;
+ gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const char * const comp_target_menu[] = {
+ "Quality",
+ "Framerate",
+ NULL
+ };
+ static const struct v4l2_ctrl_config comp_target = {
+ .ops = &sd_ctrl_ops,
+ .id = CPIA1_CID_COMP_TARGET,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Compression Target",
+ .qmenu = comp_target_menu,
+ .max = 1,
+ .def = COMP_TARGET_DEF,
+ };
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 7);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ FREQ_DEF);
+ if (sd->params.qx3.qx3_detected) {
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
+ ILLUMINATORS_1_DEF);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
+ ILLUMINATORS_2_DEF);
+ }
+ v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .dq_callback = sd_dq_callback,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0553, 0x0002)},
+ {USB_DEVICE(0x0813, 0x0001)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/usb/gspca/etoms.c
index 38f68e11c3a2..38f68e11c3a2 100644
--- a/drivers/media/video/gspca/etoms.c
+++ b/drivers/media/usb/gspca/etoms.c
diff --git a/drivers/media/usb/gspca/finepix.c b/drivers/media/usb/gspca/finepix.c
new file mode 100644
index 000000000000..52bdb569760b
--- /dev/null
+++ b/drivers/media/usb/gspca/finepix.c
@@ -0,0 +1,310 @@
+/*
+ * Fujifilm Finepix subdriver
+ *
+ * Copyright (C) 2008 Frank Zago
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "finepix"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Frank Zago <frank@zago.net>");
+MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeout, in ms */
+#define FPIX_TIMEOUT 250
+
+/* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+ * too. */
+#define FPIX_MAX_TRANSFER 0x2000
+
+/* Structure to hold all of our device specific stuff */
+struct usb_fpix {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+/* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+ * will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
+ * 30ms is bad while 35ms is perfect. */
+#define NEXT_FRAME_DELAY 35
+
+/* These cameras only support 320x200. */
+static const struct v4l2_pix_format fpix_mode[1] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* send a command to the webcam */
+static int command(struct gspca_dev *gspca_dev,
+ int order) /* 0: reset, 1: frame request */
+{
+ static u8 order_values[2][12] = {
+ {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */
+ {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */
+ };
+
+ memcpy(gspca_dev->usb_buf, order_values[order], 12);
+ return usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+ 12, FPIX_TIMEOUT);
+}
+
+/*
+ * 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 gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void dostream(struct work_struct *work)
+{
+ struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ struct urb *urb = gspca_dev->urb[0];
+ u8 *data = urb->transfer_buffer;
+ int ret = 0;
+ int len;
+
+ PDEBUG(D_STREAM, "dostream started");
+
+ /* loop reading a frame */
+again:
+ while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+
+ /* request a frame */
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = command(gspca_dev, 1);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ break;
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ break;
+
+ /* the frame comes in parts */
+ for (;;) {
+ ret = usb_bulk_msg(gspca_dev->dev,
+ urb->pipe,
+ data,
+ FPIX_MAX_TRANSFER,
+ &len, FPIX_TIMEOUT);
+ if (ret < 0) {
+ /* Most of the time we get a timeout
+ * error. Just restart. */
+ goto again;
+ }
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ goto out;
+#endif
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto out;
+ if (len < FPIX_MAX_TRANSFER ||
+ (data[len - 2] == 0xff &&
+ data[len - 1] == 0xd9)) {
+
+ /* If the result is less than what was asked
+ * for, then it's the end of the
+ * frame. Sometimes the jpeg is not complete,
+ * but there's nothing we can do. We also end
+ * here if the the jpeg ends right at the end
+ * of the frame. */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ break;
+ }
+
+ /* got a partial image */
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+ data, len);
+ }
+
+ /* We must wait before trying reading the next
+ * frame. If we don't, or if the delay is too short,
+ * the camera will disconnect. */
+ msleep(NEXT_FRAME_DELAY);
+ }
+
+out:
+ PDEBUG(D_STREAM, "dostream stopped");
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ cam->cam_mode = fpix_mode;
+ cam->nmodes = 1;
+ cam->bulk = 1;
+ cam->bulk_size = FPIX_MAX_TRANSFER;
+
+ INIT_WORK(&dev->work_struct, dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* start the camera */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ int ret, len;
+
+ /* Init the device */
+ ret = command(gspca_dev, 0);
+ if (ret < 0) {
+ pr_err("init failed %d\n", ret);
+ return ret;
+ }
+
+ /* Read the result of the command. Ignore the result, for it
+ * varies with the device. */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe,
+ gspca_dev->urb[0]->transfer_buffer,
+ FPIX_MAX_TRANSFER, &len,
+ FPIX_TIMEOUT);
+ if (ret < 0) {
+ pr_err("usb_bulk_msg failed %d\n", ret);
+ return ret;
+ }
+
+ /* Request a frame, but don't read it */
+ ret = command(gspca_dev, 1);
+ if (ret < 0) {
+ pr_err("frame request failed %d\n", ret);
+ return ret;
+ }
+
+ /* Again, reset bulk in endpoint */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->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 usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(dev->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ dev->work_thread = NULL;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04cb, 0x0104)},
+ {USB_DEVICE(0x04cb, 0x0109)},
+ {USB_DEVICE(0x04cb, 0x010b)},
+ {USB_DEVICE(0x04cb, 0x010f)},
+ {USB_DEVICE(0x04cb, 0x0111)},
+ {USB_DEVICE(0x04cb, 0x0113)},
+ {USB_DEVICE(0x04cb, 0x0115)},
+ {USB_DEVICE(0x04cb, 0x0117)},
+ {USB_DEVICE(0x04cb, 0x0119)},
+ {USB_DEVICE(0x04cb, 0x011b)},
+ {USB_DEVICE(0x04cb, 0x011d)},
+ {USB_DEVICE(0x04cb, 0x0121)},
+ {USB_DEVICE(0x04cb, 0x0123)},
+ {USB_DEVICE(0x04cb, 0x0125)},
+ {USB_DEVICE(0x04cb, 0x0127)},
+ {USB_DEVICE(0x04cb, 0x0129)},
+ {USB_DEVICE(0x04cb, 0x012b)},
+ {USB_DEVICE(0x04cb, 0x012d)},
+ {USB_DEVICE(0x04cb, 0x012f)},
+ {USB_DEVICE(0x04cb, 0x0131)},
+ {USB_DEVICE(0x04cb, 0x013b)},
+ {USB_DEVICE(0x04cb, 0x013d)},
+ {USB_DEVICE(0x04cb, 0x013f)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .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 usb_fpix),
+ 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/gl860/Kconfig b/drivers/media/usb/gspca/gl860/Kconfig
index 22772f53ec7b..22772f53ec7b 100644
--- a/drivers/media/video/gspca/gl860/Kconfig
+++ b/drivers/media/usb/gspca/gl860/Kconfig
diff --git a/drivers/media/usb/gspca/gl860/Makefile b/drivers/media/usb/gspca/gl860/Makefile
new file mode 100644
index 000000000000..cf6397415aad
--- /dev/null
+++ b/drivers/media/usb/gspca/gl860/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_GL860) += gspca_gl860.o
+
+gspca_gl860-objs := gl860.o \
+ gl860-mi1320.o \
+ gl860-ov2640.o \
+ gl860-ov9655.o \
+ gl860-mi2020.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
+
diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/usb/gspca/gl860/gl860-mi1320.c
index b57160e04866..b57160e04866 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi1320.c
+++ b/drivers/media/usb/gspca/gl860/gl860-mi1320.c
diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/usb/gspca/gl860/gl860-mi2020.c
index 2edda6b7d653..2edda6b7d653 100644
--- a/drivers/media/video/gspca/gl860/gl860-mi2020.c
+++ b/drivers/media/usb/gspca/gl860/gl860-mi2020.c
diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/usb/gspca/gl860/gl860-ov2640.c
index 768cac5cd72b..768cac5cd72b 100644
--- a/drivers/media/video/gspca/gl860/gl860-ov2640.c
+++ b/drivers/media/usb/gspca/gl860/gl860-ov2640.c
diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/usb/gspca/gl860/gl860-ov9655.c
index 5ae9619d72a5..5ae9619d72a5 100644
--- a/drivers/media/video/gspca/gl860/gl860-ov9655.c
+++ b/drivers/media/usb/gspca/gl860/gl860-ov9655.c
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/usb/gspca/gl860/gl860.c
index ced3b71f14e5..ced3b71f14e5 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/usb/gspca/gl860/gl860.c
diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/usb/gspca/gl860/gl860.h
index 0330a0293b9c..0330a0293b9c 100644
--- a/drivers/media/video/gspca/gl860/gl860.h
+++ b/drivers/media/usb/gspca/gl860/gl860.h
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
new file mode 100644
index 000000000000..a2b934146ebf
--- /dev/null
+++ b/drivers/media/usb/gspca/gspca.c
@@ -0,0 +1,2460 @@
+/*
+ * Main USB camera driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
+ *
+ * Camera button input handling by Márton Németh
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define GSPCA_VERSION "2.14.0"
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/io.h>
+#include <asm/page.h>
+#include <linux/uaccess.h>
+#include <linux/ktime.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include "gspca.h"
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#include <linux/input.h>
+#include <linux/usb/input.h>
+#endif
+
+/* global values */
+#define DEF_NURBS 3 /* default number of URBs */
+#if DEF_NURBS > MAX_NURBS
+#error "DEF_NURBS too big"
+#endif
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(GSPCA_VERSION);
+
+#ifdef GSPCA_DEBUG
+int gspca_debug = D_ERR | D_PROBE;
+EXPORT_SYMBOL(gspca_debug);
+
+static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
+{
+ if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
+ PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d",
+ txt,
+ pixfmt & 0xff,
+ (pixfmt >> 8) & 0xff,
+ (pixfmt >> 16) & 0xff,
+ pixfmt >> 24,
+ w, h);
+ } else {
+ PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d",
+ txt,
+ pixfmt,
+ w, h);
+ }
+}
+#else
+#define PDEBUG_MODE(txt, pixfmt, w, h)
+#endif
+
+/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
+#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
+#define GSPCA_MEMORY_READ 7
+
+#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
+
+/*
+ * VMA operations.
+ */
+static void gspca_vm_open(struct vm_area_struct *vma)
+{
+ struct gspca_frame *frame = vma->vm_private_data;
+
+ frame->vma_use_count++;
+ frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;
+}
+
+static void gspca_vm_close(struct vm_area_struct *vma)
+{
+ struct gspca_frame *frame = vma->vm_private_data;
+
+ if (--frame->vma_use_count <= 0)
+ frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
+}
+
+static const struct vm_operations_struct gspca_vm_ops = {
+ .open = gspca_vm_open,
+ .close = gspca_vm_close,
+};
+
+/*
+ * Input and interrupt endpoint handling functions
+ */
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static void int_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ int ret;
+
+ ret = urb->status;
+ switch (ret) {
+ case 0:
+ if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
+ urb->transfer_buffer, urb->actual_length) < 0) {
+ PDEBUG(D_ERR, "Unknown packet received");
+ }
+ break;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ENODEV:
+ case -ESHUTDOWN:
+ /* Stop is requested either by software or hardware is gone,
+ * keep the ret value non-zero and don't resubmit later.
+ */
+ break;
+
+ default:
+ PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status);
+ urb->status = 0;
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ pr_err("Resubmit URB failed with error %i\n", ret);
+ }
+}
+
+static int gspca_input_connect(struct gspca_dev *dev)
+{
+ struct input_dev *input_dev;
+ int err = 0;
+
+ dev->input_dev = NULL;
+ if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) {
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ input_dev->name = dev->sd_desc->name;
+ input_dev->phys = dev->phys;
+
+ usb_to_input_id(dev->dev, &input_dev->id);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+ input_dev->dev.parent = &dev->dev->dev;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ pr_err("Input device registration failed with error %i\n",
+ err);
+ input_dev->dev.parent = NULL;
+ input_free_device(input_dev);
+ } else {
+ dev->input_dev = input_dev;
+ }
+ }
+
+ return err;
+}
+
+static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
+ struct usb_endpoint_descriptor *ep)
+{
+ unsigned int buffer_len;
+ int interval;
+ struct urb *urb;
+ struct usb_device *dev;
+ void *buffer = NULL;
+ int ret = -EINVAL;
+
+ buffer_len = le16_to_cpu(ep->wMaxPacketSize);
+ interval = ep->bInterval;
+ PDEBUG(D_CONF, "found int in endpoint: 0x%x, "
+ "buffer_len=%u, interval=%u",
+ ep->bEndpointAddress, buffer_len, interval);
+
+ dev = gspca_dev->dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ buffer = usb_alloc_coherent(dev, buffer_len,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto error_buffer;
+ }
+ usb_fill_int_urb(urb, dev,
+ usb_rcvintpipe(dev, ep->bEndpointAddress),
+ buffer, buffer_len,
+ int_irq, (void *)gspca_dev, interval);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "submit int URB failed with error %i", ret);
+ goto error_submit;
+ }
+ gspca_dev->int_urb = urb;
+ return ret;
+
+error_submit:
+ usb_free_coherent(dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+error_buffer:
+ usb_free_urb(urb);
+error:
+ return ret;
+}
+
+static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *intf_desc;
+ struct usb_endpoint_descriptor *ep;
+ int i;
+
+ if (gspca_dev->sd_desc->int_pkt_scan) {
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ intf_desc = intf->cur_altsetting;
+ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+ ep = &intf_desc->endpoint[i].desc;
+ if (usb_endpoint_dir_in(ep) &&
+ usb_endpoint_xfer_int(ep)) {
+
+ alloc_and_submit_int_urb(gspca_dev, ep);
+ break;
+ }
+ }
+ }
+}
+
+static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+
+ urb = gspca_dev->int_urb;
+ if (urb) {
+ gspca_dev->int_urb = NULL;
+ usb_kill_urb(urb);
+ usb_free_coherent(gspca_dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+}
+#else
+static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev)
+{
+}
+
+static inline int gspca_input_connect(struct gspca_dev *dev)
+{
+ return 0;
+}
+#endif
+
+/*
+ * fill a video frame from an URB and resubmit
+ */
+static void fill_frame(struct gspca_dev *gspca_dev,
+ struct urb *urb)
+{
+ u8 *data; /* address of data in the iso message */
+ int i, len, st;
+ cam_pkt_op pkt_scan;
+
+ if (urb->status != 0) {
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
+ }
+ pkt_scan = gspca_dev->sd_desc->pkt_scan;
+ for (i = 0; i < urb->number_of_packets; i++) {
+ len = urb->iso_frame_desc[i].actual_length;
+
+ /* check the packet status and length */
+ st = urb->iso_frame_desc[i].status;
+ if (st) {
+ pr_err("ISOC data error: [%d] len=%d, status=%d\n",
+ i, len, st);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ continue;
+ }
+ if (len == 0) {
+ if (gspca_dev->empty_packet == 0)
+ gspca_dev->empty_packet = 1;
+ continue;
+ }
+
+ /* let the packet be analyzed by the subdriver */
+ PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
+ i, urb->iso_frame_desc[i].offset, len);
+ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+ pkt_scan(gspca_dev, data, len);
+ }
+
+resubmit:
+ /* resubmit the URB */
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb() ret %d\n", st);
+}
+
+/*
+ * ISOC message interrupt from the USB device
+ *
+ * Analyse each packet and call the subdriver for copy to the frame buffer.
+ */
+static void isoc_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+
+ PDEBUG(D_PACK, "isoc irq");
+ if (!gspca_dev->streaming)
+ return;
+ fill_frame(gspca_dev, urb);
+}
+
+/*
+ * bulk message interrupt from the USB device
+ */
+static void bulk_irq(struct urb *urb)
+{
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ int st;
+
+ PDEBUG(D_PACK, "bulk irq");
+ if (!gspca_dev->streaming)
+ return;
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ESHUTDOWN:
+ return; /* disconnection */
+ default:
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ return;
+#endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+ urb->status = 0;
+ goto resubmit;
+ }
+
+ PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+ gspca_dev->sd_desc->pkt_scan(gspca_dev,
+ urb->transfer_buffer,
+ urb->actual_length);
+
+resubmit:
+ /* resubmit the URB */
+ if (gspca_dev->cam.bulk_nurbs != 0) {
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+ pr_err("usb_submit_urb() ret %d\n", st);
+ }
+}
+
+/*
+ * add data to the current frame
+ *
+ * This function is called by the subdrivers at interrupt level.
+ *
+ * To build a frame, these ones must add
+ * - one FIRST_PACKET
+ * - 0 or many INTER_PACKETs
+ * - one LAST_PACKET
+ * DISCARD_PACKET invalidates the whole frame.
+ */
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len)
+{
+ struct gspca_frame *frame;
+ int i, j;
+
+ PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
+
+ if (packet_type == FIRST_PACKET) {
+ i = atomic_read(&gspca_dev->fr_i);
+
+ /* if there are no queued buffer, discard the whole frame */
+ if (i == atomic_read(&gspca_dev->fr_q)) {
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_dev->sequence++;
+ return;
+ }
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+ frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get());
+ frame->v4l2_buf.sequence = gspca_dev->sequence++;
+ gspca_dev->image = frame->data;
+ gspca_dev->image_len = 0;
+ } else {
+ switch (gspca_dev->last_packet_type) {
+ case DISCARD_PACKET:
+ if (packet_type == LAST_PACKET) {
+ gspca_dev->last_packet_type = packet_type;
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ }
+ return;
+ case LAST_PACKET:
+ return;
+ }
+ }
+
+ /* append the packet to the frame buffer */
+ if (len > 0) {
+ if (gspca_dev->image_len + len > gspca_dev->frsz) {
+ PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d",
+ gspca_dev->image_len + len,
+ gspca_dev->frsz);
+ packet_type = DISCARD_PACKET;
+ } else {
+/* !! image is NULL only when last pkt is LAST or DISCARD
+ if (gspca_dev->image == NULL) {
+ pr_err("gspca_frame_add() image == NULL\n");
+ return;
+ }
+ */
+ memcpy(gspca_dev->image + gspca_dev->image_len,
+ data, len);
+ gspca_dev->image_len += len;
+ }
+ }
+ gspca_dev->last_packet_type = packet_type;
+
+ /* if last packet, invalidate packet concatenation until
+ * next first packet, wake up the application and advance
+ * in the queue */
+ if (packet_type == LAST_PACKET) {
+ i = atomic_read(&gspca_dev->fr_i);
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+ frame->v4l2_buf.bytesused = gspca_dev->image_len;
+ frame->v4l2_buf.flags = (frame->v4l2_buf.flags
+ | V4L2_BUF_FLAG_DONE)
+ & ~V4L2_BUF_FLAG_QUEUED;
+ i = (i + 1) % GSPCA_MAX_FRAMES;
+ atomic_set(&gspca_dev->fr_i, i);
+ wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
+ PDEBUG(D_FRAM, "frame complete len:%d",
+ frame->v4l2_buf.bytesused);
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ }
+}
+EXPORT_SYMBOL(gspca_frame_add);
+
+static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory, unsigned int count)
+{
+ struct gspca_frame *frame;
+ unsigned int frsz;
+ int i;
+
+ i = gspca_dev->curr_mode;
+ frsz = gspca_dev->cam.cam_mode[i].sizeimage;
+ PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
+ frsz = PAGE_ALIGN(frsz);
+ if (count >= GSPCA_MAX_FRAMES)
+ count = GSPCA_MAX_FRAMES - 1;
+ gspca_dev->frbuf = vmalloc_32(frsz * count);
+ if (!gspca_dev->frbuf) {
+ pr_err("frame alloc failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->capt_file = file;
+ gspca_dev->memory = memory;
+ gspca_dev->frsz = frsz;
+ gspca_dev->nframes = count;
+ for (i = 0; i < count; i++) {
+ frame = &gspca_dev->frame[i];
+ frame->v4l2_buf.index = i;
+ frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ frame->v4l2_buf.flags = 0;
+ frame->v4l2_buf.field = V4L2_FIELD_NONE;
+ frame->v4l2_buf.length = frsz;
+ frame->v4l2_buf.memory = memory;
+ frame->v4l2_buf.sequence = 0;
+ frame->data = gspca_dev->frbuf + i * frsz;
+ frame->v4l2_buf.m.offset = i * frsz;
+ }
+ atomic_set(&gspca_dev->fr_q, 0);
+ atomic_set(&gspca_dev->fr_i, 0);
+ gspca_dev->fr_o = 0;
+ return 0;
+}
+
+static void frame_free(struct gspca_dev *gspca_dev)
+{
+ int i;
+
+ PDEBUG(D_STREAM, "frame free");
+ if (gspca_dev->frbuf != NULL) {
+ vfree(gspca_dev->frbuf);
+ gspca_dev->frbuf = NULL;
+ for (i = 0; i < gspca_dev->nframes; i++)
+ gspca_dev->frame[i].data = NULL;
+ }
+ gspca_dev->nframes = 0;
+ gspca_dev->frsz = 0;
+ gspca_dev->capt_file = NULL;
+ gspca_dev->memory = GSPCA_MEMORY_NO;
+}
+
+static void destroy_urbs(struct gspca_dev *gspca_dev)
+{
+ struct urb *urb;
+ unsigned int i;
+
+ PDEBUG(D_STREAM, "kill transfer");
+ for (i = 0; i < MAX_NURBS; i++) {
+ urb = gspca_dev->urb[i];
+ if (urb == NULL)
+ break;
+
+ gspca_dev->urb[i] = NULL;
+ usb_kill_urb(urb);
+ if (urb->transfer_buffer != NULL)
+ usb_free_coherent(gspca_dev->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+}
+
+static int gspca_set_alt0(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ if (gspca_dev->alt == 0)
+ return 0;
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
+ if (ret < 0)
+ pr_err("set alt 0 err %d\n", ret);
+ return ret;
+}
+
+/* Note: both the queue and the usb locks should be held when calling this */
+static void gspca_stream_off(struct gspca_dev *gspca_dev)
+{
+ gspca_dev->streaming = 0;
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ PDEBUG(D_STREAM, "stream off OK");
+}
+
+/*
+ * look for an input transfer endpoint in an alternate setting
+ */
+static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
+ int xfer)
+{
+ struct usb_host_endpoint *ep;
+ int i, attr;
+
+ for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+ ep = &alt->endpoint[i];
+ attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ if (attr == xfer
+ && ep->desc.wMaxPacketSize != 0
+ && usb_endpoint_dir_in(&ep->desc))
+ return ep;
+ }
+ return NULL;
+}
+
+/* compute the minimum bandwidth for the current transfer */
+static u32 which_bandwidth(struct gspca_dev *gspca_dev)
+{
+ u32 bandwidth;
+ int i;
+
+ /* get the (max) image size */
+ i = gspca_dev->curr_mode;
+ bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
+
+ /* if the image is compressed, estimate its mean size */
+ if (!gspca_dev->cam.needs_full_bandwidth &&
+ bandwidth < gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ bandwidth = bandwidth * 3 / 8; /* 0.375 */
+
+ /* estimate the frame rate */
+ if (gspca_dev->sd_desc->get_streamparm) {
+ struct v4l2_streamparm parm;
+
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
+ bandwidth *= parm.parm.capture.timeperframe.denominator;
+ bandwidth /= parm.parm.capture.timeperframe.numerator;
+ } else {
+
+ /* don't hope more than 15 fps with USB 1.1 and
+ * image resolution >= 640x480 */
+ if (gspca_dev->width >= 640
+ && gspca_dev->dev->speed == USB_SPEED_FULL)
+ bandwidth *= 15; /* 15 fps */
+ else
+ bandwidth *= 30; /* 30 fps */
+ }
+
+ PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
+ return bandwidth;
+}
+
+/* endpoint table */
+#define MAX_ALT 16
+struct ep_tb_s {
+ u32 alt;
+ u32 bandwidth;
+};
+
+/*
+ * build the table of the endpoints
+ * and compute the minimum bandwidth for the image transfer
+ */
+static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
+ struct usb_interface *intf,
+ struct ep_tb_s *ep_tb)
+{
+ struct usb_host_endpoint *ep;
+ int i, j, nbalt, psize, found;
+ u32 bandwidth, last_bw;
+
+ nbalt = intf->num_altsetting;
+ if (nbalt > MAX_ALT)
+ nbalt = MAX_ALT; /* fixme: should warn */
+
+ /* build the endpoint table */
+ i = 0;
+ last_bw = 0;
+ for (;;) {
+ ep_tb->bandwidth = 2000 * 2000 * 120;
+ found = 0;
+ for (j = 0; j < nbalt; j++) {
+ ep = alt_xfer(&intf->altsetting[j],
+ USB_ENDPOINT_XFER_ISOC);
+ if (ep == NULL)
+ continue;
+ if (ep->desc.bInterval == 0) {
+ pr_err("alt %d iso endp with 0 interval\n", j);
+ continue;
+ }
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ bandwidth = psize * 1000;
+ if (gspca_dev->dev->speed == USB_SPEED_HIGH
+ || gspca_dev->dev->speed == USB_SPEED_SUPER)
+ bandwidth *= 8;
+ bandwidth /= 1 << (ep->desc.bInterval - 1);
+ if (bandwidth <= last_bw)
+ continue;
+ if (bandwidth < ep_tb->bandwidth) {
+ ep_tb->bandwidth = bandwidth;
+ ep_tb->alt = j;
+ found = 1;
+ }
+ }
+ if (!found)
+ break;
+ PDEBUG(D_STREAM, "alt %d bandwidth %d",
+ ep_tb->alt, ep_tb->bandwidth);
+ last_bw = ep_tb->bandwidth;
+ i++;
+ ep_tb++;
+ }
+
+ /*
+ * If the camera:
+ * has a usb audio class interface (a built in usb mic); and
+ * is a usb 1 full speed device; and
+ * uses the max full speed iso bandwidth; and
+ * and has more than 1 alt setting
+ * then skip the highest alt setting to spare bandwidth for the mic
+ */
+ if (gspca_dev->audio &&
+ gspca_dev->dev->speed == USB_SPEED_FULL &&
+ last_bw >= 1000000 &&
+ i > 1) {
+ PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt");
+ i--;
+ ep_tb--;
+ }
+
+ /* get the requested bandwidth and start at the highest atlsetting */
+ bandwidth = which_bandwidth(gspca_dev);
+ ep_tb--;
+ while (i > 1) {
+ ep_tb--;
+ if (ep_tb->bandwidth < bandwidth)
+ break;
+ i--;
+ }
+ return i;
+}
+
+/*
+ * create the URBs for image transfer
+ */
+static int create_urbs(struct gspca_dev *gspca_dev,
+ struct usb_host_endpoint *ep)
+{
+ struct urb *urb;
+ int n, nurbs, i, psize, npkt, bsize;
+
+ /* calculate the packet size and the number of packets */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+
+ if (!gspca_dev->cam.bulk) { /* isoc */
+
+ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+ if (gspca_dev->pkt_size == 0)
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ else
+ psize = gspca_dev->pkt_size;
+ npkt = gspca_dev->cam.npkt;
+ if (npkt == 0)
+ npkt = 32; /* default value */
+ bsize = psize * npkt;
+ PDEBUG(D_STREAM,
+ "isoc %d pkts size %d = bsize:%d",
+ npkt, psize, bsize);
+ nurbs = DEF_NURBS;
+ } else { /* bulk */
+ npkt = 0;
+ bsize = gspca_dev->cam.bulk_size;
+ if (bsize == 0)
+ bsize = psize;
+ PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
+ if (gspca_dev->cam.bulk_nurbs != 0)
+ nurbs = gspca_dev->cam.bulk_nurbs;
+ else
+ nurbs = 1;
+ }
+
+ for (n = 0; n < nurbs; n++) {
+ urb = usb_alloc_urb(npkt, GFP_KERNEL);
+ if (!urb) {
+ pr_err("usb_alloc_urb failed\n");
+ return -ENOMEM;
+ }
+ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
+ bsize,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ if (urb->transfer_buffer == NULL) {
+ pr_err("usb_alloc_coherent failed\n");
+ return -ENOMEM;
+ }
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = bsize;
+ if (npkt != 0) { /* ISOC */
+ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_ISO_ASAP
+ | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1 << (ep->desc.bInterval - 1);
+ urb->complete = isoc_irq;
+ urb->number_of_packets = npkt;
+ for (i = 0; i < npkt; i++) {
+ urb->iso_frame_desc[i].length = psize;
+ urb->iso_frame_desc[i].offset = psize * i;
+ }
+ } else { /* bulk */
+ urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
+ ep->desc.bEndpointAddress);
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->complete = bulk_irq;
+ }
+ }
+ return 0;
+}
+
+/*
+ * start the USB transfer
+ */
+static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ struct usb_host_endpoint *ep;
+ struct urb *urb;
+ struct ep_tb_s ep_tb[MAX_ALT];
+ int n, ret, xfer, alt, alt_idx;
+
+ /* reset the streaming variables */
+ gspca_dev->image = NULL;
+ gspca_dev->image_len = 0;
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_dev->sequence = 0;
+
+ gspca_dev->usb_err = 0;
+
+ /* do the specific subdriver stuff before endpoint selection */
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0;
+ if (gspca_dev->sd_desc->isoc_init) {
+ ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
+ if (ret < 0)
+ return ret;
+ }
+ xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+ : USB_ENDPOINT_XFER_ISOC;
+
+ /* if bulk or the subdriver forced an altsetting, get the endpoint */
+ if (gspca_dev->alt != 0) {
+ gspca_dev->alt--; /* (previous version compatibility) */
+ ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
+ if (ep == NULL) {
+ pr_err("bad altsetting %d\n", gspca_dev->alt);
+ return -EIO;
+ }
+ ep_tb[0].alt = gspca_dev->alt;
+ alt_idx = 1;
+ } else {
+
+ /* else, compute the minimum bandwidth
+ * and build the endpoint table */
+ alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ return -EIO;
+ }
+ }
+
+ /* set the highest alternate setting and
+ * loop until urb submit succeeds */
+ gspca_input_destroy_urb(gspca_dev);
+
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ alt = -1;
+ for (;;) {
+ if (alt != gspca_dev->alt) {
+ alt = gspca_dev->alt;
+ if (intf->num_altsetting > 1) {
+ ret = usb_set_interface(gspca_dev->dev,
+ gspca_dev->iface,
+ alt);
+ if (ret < 0) {
+ if (ret == -ENOSPC)
+ goto retry; /*fixme: ugly*/
+ pr_err("set alt %d err %d\n", alt, ret);
+ goto out;
+ }
+ }
+ }
+ if (!gspca_dev->cam.no_urb_create) {
+ PDEBUG(D_STREAM, "init transfer alt %d", alt);
+ ret = create_urbs(gspca_dev,
+ alt_xfer(&intf->altsetting[alt], xfer));
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
+ }
+
+ /* clear the bulk endpoint */
+ if (gspca_dev->cam.bulk)
+ usb_clear_halt(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe);
+
+ /* start the cam */
+ ret = gspca_dev->sd_desc->start(gspca_dev);
+ if (ret < 0) {
+ destroy_urbs(gspca_dev);
+ goto out;
+ }
+ gspca_dev->streaming = 1;
+ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+
+ /* some bulk transfers are started by the subdriver */
+ if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
+ break;
+
+ /* submit the URBs */
+ for (n = 0; n < MAX_NURBS; n++) {
+ urb = gspca_dev->urb[n];
+ if (urb == NULL)
+ break;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0)
+ break;
+ }
+ if (ret >= 0)
+ break; /* transfer is started */
+
+ /* something when wrong
+ * stop the webcam and free the transfer resources */
+ gspca_stream_off(gspca_dev);
+ if (ret != -ENOSPC) {
+ pr_err("usb_submit_urb alt %d err %d\n",
+ gspca_dev->alt, ret);
+ goto out;
+ }
+
+ /* the bandwidth is not wide enough
+ * negotiate or try a lower alternate setting */
+retry:
+ PDEBUG(D_ERR|D_STREAM,
+ "alt %d - bandwidth not wide enough - trying again",
+ alt);
+ msleep(20); /* wait for kill complete */
+ if (gspca_dev->sd_desc->isoc_nego) {
+ ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
+ if (ret < 0)
+ goto out;
+ } else {
+ if (alt_idx <= 0) {
+ pr_err("no transfer endpoint found\n");
+ ret = -EIO;
+ goto out;
+ }
+ gspca_dev->alt = ep_tb[--alt_idx].alt;
+ }
+ }
+out:
+ gspca_input_create_urb(gspca_dev);
+ return ret;
+}
+
+static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
+{
+ struct gspca_ctrl *ctrl;
+ int i;
+
+ i = gspca_dev->cam.nmodes - 1; /* take the highest mode */
+ gspca_dev->curr_mode = i;
+ gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
+ gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
+ gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
+
+ /* set the current control values to their default values
+ * which may have changed in sd_init() */
+ /* does nothing if ctrl_handler == NULL */
+ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+ ctrl = gspca_dev->cam.ctrls;
+ if (ctrl != NULL) {
+ for (i = 0;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrl++)
+ ctrl->val = ctrl->def;
+ }
+}
+
+static int wxh_to_mode(struct gspca_dev *gspca_dev,
+ int width, int height)
+{
+ int i;
+
+ for (i = gspca_dev->cam.nmodes; --i > 0; ) {
+ if (width >= gspca_dev->cam.cam_mode[i].width
+ && height >= gspca_dev->cam.cam_mode[i].height)
+ break;
+ }
+ return i;
+}
+
+/*
+ * search a mode with the right pixel format
+ */
+static int gspca_get_mode(struct gspca_dev *gspca_dev,
+ int mode,
+ int pixfmt)
+{
+ int modeU, modeD;
+
+ modeU = modeD = mode;
+ while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) {
+ if (--modeD >= 0) {
+ if (gspca_dev->cam.cam_mode[modeD].pixelformat
+ == pixfmt)
+ return modeD;
+ }
+ if (++modeU < gspca_dev->cam.nmodes) {
+ if (gspca_dev->cam.cam_mode[modeU].pixelformat
+ == pixfmt)
+ return modeU;
+ }
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ return gspca_dev->sd_desc->get_register(gspca_dev, reg);
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ return gspca_dev->sd_desc->set_register(gspca_dev, reg);
+}
+#endif
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmtdesc)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int i, j, index;
+ __u32 fmt_tb[8];
+
+ /* give an index to each format */
+ index = 0;
+ j = 0;
+ for (i = gspca_dev->cam.nmodes; --i >= 0; ) {
+ fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat;
+ j = 0;
+ for (;;) {
+ if (fmt_tb[j] == fmt_tb[index])
+ break;
+ j++;
+ }
+ if (j == index) {
+ if (fmtdesc->index == index)
+ break; /* new format */
+ index++;
+ if (index >= ARRAY_SIZE(fmt_tb))
+ return -EINVAL;
+ }
+ }
+ if (i < 0)
+ return -EINVAL; /* no more format */
+
+ fmtdesc->pixelformat = fmt_tb[index];
+ if (gspca_dev->cam.cam_mode[i].sizeimage <
+ gspca_dev->cam.cam_mode[i].width *
+ gspca_dev->cam.cam_mode[i].height)
+ fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+ fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
+ fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
+ fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
+ fmtdesc->description[3] = fmtdesc->pixelformat >> 24;
+ fmtdesc->description[4] = '\0';
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int mode;
+
+ mode = gspca_dev->curr_mode;
+ fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+ /* some drivers use priv internally, zero it before giving it to
+ userspace */
+ fmt->fmt.pix.priv = 0;
+ return 0;
+}
+
+static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
+ struct v4l2_format *fmt)
+{
+ int w, h, mode, mode2;
+
+ w = fmt->fmt.pix.width;
+ h = fmt->fmt.pix.height;
+
+#ifdef GSPCA_DEBUG
+ if (gspca_debug & D_CONF)
+ PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);
+#endif
+ /* search the closest mode for width and height */
+ mode = wxh_to_mode(gspca_dev, w, h);
+
+ /* OK if right palette */
+ if (gspca_dev->cam.cam_mode[mode].pixelformat
+ != fmt->fmt.pix.pixelformat) {
+
+ /* else, search the closest mode with the same pixel format */
+ mode2 = gspca_get_mode(gspca_dev, mode,
+ fmt->fmt.pix.pixelformat);
+ if (mode2 >= 0)
+ mode = mode2;
+/* else
+ ; * no chance, return this mode */
+ }
+ fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
+ /* some drivers use priv internally, zero it before giving it to
+ userspace */
+ fmt->fmt.pix.priv = 0;
+ return mode; /* used when s_fmt */
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file,
+ void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int ret;
+
+ ret = try_fmt_vid_cap(gspca_dev, fmt);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ ret = try_fmt_vid_cap(gspca_dev, fmt);
+ if (ret < 0)
+ goto out;
+
+ if (gspca_dev->nframes != 0
+ && fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (ret == gspca_dev->curr_mode) {
+ ret = 0;
+ goto out; /* same mode */
+ }
+
+ if (gspca_dev->streaming) {
+ ret = -EBUSY;
+ goto out;
+ }
+ gspca_dev->width = fmt->fmt.pix.width;
+ gspca_dev->height = fmt->fmt.pix.height;
+ gspca_dev->pixfmt = fmt->fmt.pix.pixelformat;
+ gspca_dev->curr_mode = ret;
+
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int i;
+ __u32 index = 0;
+
+ for (i = 0; i < gspca_dev->cam.nmodes; i++) {
+ if (fsize->pixel_format !=
+ gspca_dev->cam.cam_mode[i].pixelformat)
+ continue;
+
+ if (fsize->index == index) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width =
+ gspca_dev->cam.cam_mode[i].width;
+ fsize->discrete.height =
+ gspca_dev->cam.cam_mode[i].height;
+ return 0;
+ }
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_enum_frameintervals(struct file *filp, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
+ int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
+ __u32 i;
+
+ if (gspca_dev->cam.mode_framerates == NULL ||
+ gspca_dev->cam.mode_framerates[mode].nrates == 0)
+ return -EINVAL;
+
+ if (fival->pixel_format !=
+ gspca_dev->cam.cam_mode[mode].pixelformat)
+ return -EINVAL;
+
+ for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
+ if (fival->index == i) {
+ fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator =
+ gspca_dev->cam.mode_framerates[mode].rates[i];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void gspca_release(struct v4l2_device *v4l2_device)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(v4l2_device, struct gspca_dev, v4l2_dev);
+
+ v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
+ v4l2_device_unregister(&gspca_dev->v4l2_dev);
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+}
+
+static int dev_open(struct file *file)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ PDEBUG(D_STREAM, "[%s] open", current->comm);
+
+ /* protect the subdriver against rmmod */
+ if (!try_module_get(gspca_dev->module))
+ return -ENODEV;
+
+#ifdef GSPCA_DEBUG
+ /* activate the v4l2 debug */
+ if (gspca_debug & D_V4L2)
+ gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG;
+ else
+ gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
+ | V4L2_DEBUG_IOCTL_ARG);
+#endif
+ return v4l2_fh_open(file);
+}
+
+static int dev_close(struct file *file)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ PDEBUG(D_STREAM, "[%s] close", current->comm);
+
+ /* Needed for gspca_stream_off, always lock before queue_lock! */
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock)) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ return -ERESTARTSYS;
+ }
+
+ /* if the file did the capture, free the streaming resources */
+ if (gspca_dev->capt_file == file) {
+ if (gspca_dev->streaming)
+ gspca_stream_off(gspca_dev);
+ frame_free(gspca_dev);
+ }
+ module_put(gspca_dev->module);
+ mutex_unlock(&gspca_dev->queue_lock);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ PDEBUG(D_STREAM, "close done");
+
+ return v4l2_fh_release(file);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
+ sizeof cap->driver);
+ if (gspca_dev->dev->product != NULL) {
+ strlcpy((char *) cap->card, gspca_dev->dev->product,
+ sizeof cap->card);
+ } else {
+ snprintf((char *) cap->card, sizeof cap->card,
+ "USB Camera (%04x:%04x)",
+ le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
+ le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
+ }
+ usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
+ sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int get_ctrl(struct gspca_dev *gspca_dev,
+ int id)
+{
+ const struct ctrl *ctrls;
+ int i;
+
+ for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrls++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (id == ctrls->qctrl.id)
+ return i;
+ }
+ return -1;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *q_ctrl)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ const struct ctrl *ctrls;
+ struct gspca_ctrl *gspca_ctrl;
+ int i, idx;
+ u32 id;
+
+ id = q_ctrl->id;
+ if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ id &= V4L2_CTRL_ID_MASK;
+ id++;
+ idx = -1;
+ for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
+ if (gspca_dev->ctrl_dis & (1 << i))
+ continue;
+ if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
+ continue;
+ if (idx >= 0
+ && gspca_dev->sd_desc->ctrls[i].qctrl.id
+ > gspca_dev->sd_desc->ctrls[idx].qctrl.id)
+ continue;
+ idx = i;
+ }
+ } else {
+ idx = get_ctrl(gspca_dev, id);
+ }
+ if (idx < 0)
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+ memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl);
+ if (gspca_dev->cam.ctrls != NULL) {
+ gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+ q_ctrl->default_value = gspca_ctrl->def;
+ q_ctrl->minimum = gspca_ctrl->min;
+ q_ctrl->maximum = gspca_ctrl->max;
+ }
+ if (gspca_dev->ctrl_inac & (1 << idx))
+ q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ const struct ctrl *ctrls;
+ struct gspca_ctrl *gspca_ctrl;
+ int idx;
+
+ idx = get_ctrl(gspca_dev, ctrl->id);
+ if (idx < 0)
+ return -EINVAL;
+ if (gspca_dev->ctrl_inac & (1 << idx))
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+ if (gspca_dev->cam.ctrls != NULL) {
+ gspca_ctrl = &gspca_dev->cam.ctrls[idx];
+ if (ctrl->value < gspca_ctrl->min
+ || ctrl->value > gspca_ctrl->max)
+ return -ERANGE;
+ } else {
+ gspca_ctrl = NULL;
+ if (ctrl->value < ctrls->qctrl.minimum
+ || ctrl->value > ctrls->qctrl.maximum)
+ return -ERANGE;
+ }
+ PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+ gspca_dev->usb_err = 0;
+ if (ctrls->set != NULL)
+ return ctrls->set(gspca_dev, ctrl->value);
+ if (gspca_ctrl != NULL) {
+ gspca_ctrl->val = ctrl->value;
+ if (ctrls->set_control != NULL
+ && gspca_dev->streaming)
+ ctrls->set_control(gspca_dev);
+ }
+ return gspca_dev->usb_err;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ const struct ctrl *ctrls;
+ int idx;
+
+ idx = get_ctrl(gspca_dev, ctrl->id);
+ if (idx < 0)
+ return -EINVAL;
+ ctrls = &gspca_dev->sd_desc->ctrls[idx];
+
+ gspca_dev->usb_err = 0;
+ if (ctrls->get != NULL)
+ return ctrls->get(gspca_dev, &ctrl->value);
+ if (gspca_dev->cam.ctrls != NULL)
+ ctrl->value = gspca_dev->cam.ctrls[idx].val;
+ return 0;
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ if (!gspca_dev->sd_desc->querymenu)
+ return -ENOTTY;
+ return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ if (input->index != 0)
+ return -EINVAL;
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->status = gspca_dev->cam.input_flags;
+ strlcpy(input->name, gspca_dev->sd_desc->name,
+ sizeof input->name);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return (0);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *rb)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int i, ret = 0, streaming;
+
+ i = rb->memory; /* (avoid compilation warning) */
+ switch (i) {
+ case GSPCA_MEMORY_READ: /* (internal call) */
+ case V4L2_MEMORY_MMAP:
+ case V4L2_MEMORY_USERPTR:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ if (gspca_dev->memory != GSPCA_MEMORY_NO
+ && gspca_dev->memory != GSPCA_MEMORY_READ
+ && gspca_dev->memory != rb->memory) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* only one file may do the capture */
+ if (gspca_dev->capt_file != NULL
+ && gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* if allocated, the buffers must not be mapped */
+ for (i = 0; i < gspca_dev->nframes; i++) {
+ if (gspca_dev->frame[i].vma_use_count) {
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ /* stop streaming */
+ streaming = gspca_dev->streaming;
+ if (streaming) {
+ gspca_stream_off(gspca_dev);
+
+ /* Don't restart the stream when switching from read
+ * to mmap mode */
+ if (gspca_dev->memory == GSPCA_MEMORY_READ)
+ streaming = 0;
+ }
+
+ /* free the previous allocated buffers, if any */
+ if (gspca_dev->nframes != 0)
+ frame_free(gspca_dev);
+ if (rb->count == 0) /* unrequest */
+ goto out;
+ ret = frame_alloc(gspca_dev, file, rb->memory, rb->count);
+ if (ret == 0) {
+ rb->count = gspca_dev->nframes;
+ if (streaming)
+ ret = gspca_init_transfer(gspca_dev);
+ }
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count);
+ return ret;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ struct gspca_frame *frame;
+
+ if (v4l2_buf->index < 0
+ || v4l2_buf->index >= gspca_dev->nframes)
+ return -EINVAL;
+
+ frame = &gspca_dev->frame[v4l2_buf->index];
+ memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int ret;
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ /* check the capture file */
+ if (gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!gspca_dev->streaming) {
+ ret = gspca_init_transfer(gspca_dev);
+ if (ret < 0)
+ goto out;
+ }
+#ifdef GSPCA_DEBUG
+ if (gspca_debug & D_STREAM) {
+ PDEBUG_MODE("stream on OK",
+ gspca_dev->pixfmt,
+ gspca_dev->width,
+ gspca_dev->height);
+ }
+#endif
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ int i, ret;
+
+ if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ if (!gspca_dev->streaming) {
+ ret = 0;
+ goto out;
+ }
+
+ /* check the capture file */
+ if (gspca_dev->capt_file != file) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* stop streaming */
+ gspca_stream_off(gspca_dev);
+ /* In case another thread is waiting in dqbuf */
+ wake_up_interruptible(&gspca_dev->wq);
+
+ /* empty the transfer queues */
+ for (i = 0; i < gspca_dev->nframes; i++)
+ gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
+ atomic_set(&gspca_dev->fr_q, 0);
+ atomic_set(&gspca_dev->fr_i, 0);
+ gspca_dev->fr_o = 0;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jpegcomp)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ const struct v4l2_jpegcompression *jpegcomp)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+
+ gspca_dev->usb_err = 0;
+ return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+}
+
+static int vidioc_g_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
+
+ parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+ if (gspca_dev->sd_desc->get_streamparm) {
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
+ }
+ return 0;
+}
+
+static int vidioc_s_parm(struct file *filp, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
+ int n;
+
+ n = parm->parm.capture.readbuffers;
+ if (n == 0 || n >= GSPCA_MAX_FRAMES)
+ parm->parm.capture.readbuffers = gspca_dev->nbufread;
+ else
+ gspca_dev->nbufread = n;
+
+ if (gspca_dev->sd_desc->set_streamparm) {
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
+ }
+
+ return 0;
+}
+
+static int dev_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ struct gspca_frame *frame;
+ struct page *page;
+ unsigned long addr, start, size;
+ int i, ret;
+
+ start = vma->vm_start;
+ size = vma->vm_end - vma->vm_start;
+ PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size);
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ if (gspca_dev->capt_file != file) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame = NULL;
+ for (i = 0; i < gspca_dev->nframes; ++i) {
+ if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) {
+ PDEBUG(D_STREAM, "mmap bad memory type");
+ break;
+ }
+ if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT)
+ == vma->vm_pgoff) {
+ frame = &gspca_dev->frame[i];
+ break;
+ }
+ }
+ if (frame == NULL) {
+ PDEBUG(D_STREAM, "mmap no frame buffer found");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (size != frame->v4l2_buf.length) {
+ PDEBUG(D_STREAM, "mmap bad size");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * - VM_IO marks the area as being a mmaped region for I/O to a
+ * device. It also prevents the region from being core dumped.
+ */
+ vma->vm_flags |= VM_IO;
+
+ addr = (unsigned long) frame->data;
+ while (size > 0) {
+ page = vmalloc_to_page((void *) addr);
+ ret = vm_insert_page(vma, start, page);
+ if (ret < 0)
+ goto out;
+ start += PAGE_SIZE;
+ addr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ vma->vm_ops = &gspca_vm_ops;
+ vma->vm_private_data = frame;
+ gspca_vm_open(vma);
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory)
+{
+ if (!gspca_dev->present)
+ return -ENODEV;
+ if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
+ !gspca_dev->streaming)
+ return -EINVAL;
+
+ /* check if a frame is ready */
+ return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
+}
+
+static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
+ enum v4l2_memory memory)
+{
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ ret = frame_ready_nolock(gspca_dev, file, memory);
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+/*
+ * dequeue a video buffer
+ *
+ * If nonblock_ing is false, block until a buffer is available.
+ */
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ struct gspca_frame *frame;
+ int i, j, ret;
+
+ PDEBUG(D_FRAM, "dqbuf");
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ for (;;) {
+ ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory);
+ if (ret < 0)
+ goto out;
+ if (ret > 0)
+ break;
+
+ mutex_unlock(&gspca_dev->queue_lock);
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ /* wait till a frame is ready */
+ ret = wait_event_interruptible_timeout(gspca_dev->wq,
+ frame_ready(gspca_dev, file, v4l2_buf->memory),
+ msecs_to_jiffies(3000));
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EIO;
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ }
+
+ i = gspca_dev->fr_o;
+ j = gspca_dev->fr_queue[i];
+ frame = &gspca_dev->frame[j];
+
+ gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
+
+ frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
+ memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
+ PDEBUG(D_FRAM, "dqbuf %d", j);
+ ret = 0;
+
+ if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
+ if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
+ frame->data,
+ frame->v4l2_buf.bytesused)) {
+ PDEBUG(D_ERR|D_STREAM,
+ "dqbuf cp to user failed");
+ ret = -EFAULT;
+ }
+ }
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+
+ if (ret == 0 && gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+
+ return ret;
+}
+
+/*
+ * queue a video buffer
+ *
+ * Attempting to queue a buffer that has already been
+ * queued will return -EINVAL.
+ */
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *v4l2_buf)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ struct gspca_frame *frame;
+ int i, index, ret;
+
+ PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+
+ index = v4l2_buf->index;
+ if ((unsigned) index >= gspca_dev->nframes) {
+ PDEBUG(D_FRAM,
+ "qbuf idx %d >= %d", index, gspca_dev->nframes);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (v4l2_buf->memory != gspca_dev->memory) {
+ PDEBUG(D_FRAM, "qbuf bad memory type");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame = &gspca_dev->frame[index];
+ if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
+ PDEBUG(D_FRAM, "qbuf bad state");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
+
+ if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
+ frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
+ frame->v4l2_buf.length = v4l2_buf->length;
+ }
+
+ /* put the buffer in the 'queued' queue */
+ i = atomic_read(&gspca_dev->fr_q);
+ gspca_dev->fr_queue[i] = index;
+ atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES);
+
+ v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE;
+ ret = 0;
+out:
+ mutex_unlock(&gspca_dev->queue_lock);
+ return ret;
+}
+
+/*
+ * allocate the resources for read()
+ */
+static int read_alloc(struct gspca_dev *gspca_dev,
+ struct file *file)
+{
+ struct v4l2_buffer v4l2_buf;
+ int i, ret;
+
+ PDEBUG(D_STREAM, "read alloc");
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+
+ if (gspca_dev->nframes == 0) {
+ struct v4l2_requestbuffers rb;
+
+ memset(&rb, 0, sizeof rb);
+ rb.count = gspca_dev->nbufread;
+ rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rb.memory = GSPCA_MEMORY_READ;
+ ret = vidioc_reqbufs(file, gspca_dev, &rb);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read reqbuf err %d", ret);
+ goto out;
+ }
+ memset(&v4l2_buf, 0, sizeof v4l2_buf);
+ v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buf.memory = GSPCA_MEMORY_READ;
+ for (i = 0; i < gspca_dev->nbufread; i++) {
+ v4l2_buf.index = i;
+ ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read qbuf err: %d", ret);
+ goto out;
+ }
+ }
+ }
+
+ /* start streaming */
+ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (ret != 0)
+ PDEBUG(D_STREAM, "read streamon err %d", ret);
+out:
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+}
+
+static unsigned int dev_poll(struct file *file, poll_table *wait)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ unsigned long req_events = poll_requested_events(wait);
+ int ret = 0;
+
+ PDEBUG(D_FRAM, "poll");
+
+ if (req_events & POLLPRI)
+ ret |= v4l2_ctrl_poll(file, wait);
+
+ if (req_events & (POLLIN | POLLRDNORM)) {
+ /* if reqbufs is not done, the user would use read() */
+ if (gspca_dev->memory == GSPCA_MEMORY_NO) {
+ if (read_alloc(gspca_dev, file) != 0) {
+ ret |= POLLERR;
+ goto out;
+ }
+ }
+
+ poll_wait(file, &gspca_dev->wq, wait);
+
+ /* check if an image has been received */
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) {
+ ret |= POLLERR;
+ goto out;
+ }
+ if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
+ ret |= POLLIN | POLLRDNORM;
+ mutex_unlock(&gspca_dev->queue_lock);
+ }
+
+out:
+ if (!gspca_dev->present)
+ ret |= POLLHUP;
+
+ return ret;
+}
+
+static ssize_t dev_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ struct gspca_frame *frame;
+ struct v4l2_buffer v4l2_buf;
+ struct timeval timestamp;
+ int n, ret, ret2;
+
+ PDEBUG(D_FRAM, "read (%zd)", count);
+ if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */
+ ret = read_alloc(gspca_dev, file);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* get a frame */
+ timestamp = ktime_to_timeval(ktime_get());
+ timestamp.tv_sec--;
+ n = 2;
+ for (;;) {
+ memset(&v4l2_buf, 0, sizeof v4l2_buf);
+ v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buf.memory = GSPCA_MEMORY_READ;
+ ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read dqbuf err %d", ret);
+ return ret;
+ }
+
+ /* if the process slept for more than 1 second,
+ * get a newer frame */
+ frame = &gspca_dev->frame[v4l2_buf.index];
+ if (--n < 0)
+ break; /* avoid infinite loop */
+ if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec)
+ break;
+ ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret != 0) {
+ PDEBUG(D_STREAM, "read qbuf err %d", ret);
+ return ret;
+ }
+ }
+
+ /* copy the frame */
+ if (count > frame->v4l2_buf.bytesused)
+ count = frame->v4l2_buf.bytesused;
+ ret = copy_to_user(data, frame->data, count);
+ if (ret != 0) {
+ PDEBUG(D_ERR|D_STREAM,
+ "read cp to user lack %d / %zd", ret, count);
+ ret = -EFAULT;
+ goto out;
+ }
+ ret = count;
+out:
+ /* in each case, requeue the buffer */
+ ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
+ if (ret2 != 0)
+ return ret2;
+ return ret;
+}
+
+static struct v4l2_file_operations dev_fops = {
+ .owner = THIS_MODULE,
+ .open = dev_open,
+ .release = dev_close,
+ .read = dev_read,
+ .mmap = dev_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = dev_poll,
+};
+
+static const struct v4l2_ioctl_ops dev_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct video_device gspca_template = {
+ .name = "gspca main driver",
+ .fops = &dev_fops,
+ .ioctl_ops = &dev_ioctl_ops,
+ .release = video_device_release_empty, /* We use v4l2_dev.release */
+};
+
+/* initialize the controls */
+static void ctrls_init(struct gspca_dev *gspca_dev)
+{
+ struct gspca_ctrl *ctrl;
+ int i;
+
+ for (i = 0, ctrl = gspca_dev->cam.ctrls;
+ i < gspca_dev->sd_desc->nctrls;
+ i++, ctrl++) {
+ ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value;
+ ctrl->val = ctrl->def;
+ ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum;
+ ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum;
+ }
+}
+
+/*
+ * probe and create a new gspca device
+ *
+ * This function must be called by the sub-driver when it is
+ * called for probing a new device.
+ */
+int gspca_dev_probe2(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module)
+{
+ struct gspca_dev *gspca_dev;
+ struct usb_device *dev = interface_to_usbdev(intf);
+ int ret;
+
+ pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
+ sd_desc->name, id->idVendor, id->idProduct);
+
+ /* create the device */
+ if (dev_size < sizeof *gspca_dev)
+ dev_size = sizeof *gspca_dev;
+ gspca_dev = kzalloc(dev_size, GFP_KERNEL);
+ if (!gspca_dev) {
+ pr_err("couldn't kzalloc gspca struct\n");
+ return -ENOMEM;
+ }
+ gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
+ if (!gspca_dev->usb_buf) {
+ pr_err("out of memory\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ gspca_dev->dev = dev;
+ gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* check if any audio device */
+ if (dev->actconfig->desc.bNumInterfaces != 1) {
+ int i;
+ struct usb_interface *intf2;
+
+ for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+ intf2 = dev->actconfig->interface[i];
+ if (intf2 != NULL
+ && intf2->altsetting != NULL
+ && intf2->altsetting->desc.bInterfaceClass ==
+ USB_CLASS_AUDIO) {
+ gspca_dev->audio = 1;
+ break;
+ }
+ }
+ }
+
+ gspca_dev->v4l2_dev.release = gspca_release;
+ ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
+ if (ret)
+ goto out;
+ gspca_dev->sd_desc = sd_desc;
+ gspca_dev->nbufread = 2;
+ gspca_dev->empty_packet = -1; /* don't check the empty packets */
+ gspca_dev->vdev = gspca_template;
+ gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
+ video_set_drvdata(&gspca_dev->vdev, gspca_dev);
+ set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags);
+ gspca_dev->module = module;
+ gspca_dev->present = 1;
+
+ mutex_init(&gspca_dev->usb_lock);
+ gspca_dev->vdev.lock = &gspca_dev->usb_lock;
+ mutex_init(&gspca_dev->queue_lock);
+ init_waitqueue_head(&gspca_dev->wq);
+
+ /* configure the subdriver and initialize the USB device */
+ ret = sd_desc->config(gspca_dev, id);
+ if (ret < 0)
+ goto out;
+ if (gspca_dev->cam.ctrls != NULL)
+ ctrls_init(gspca_dev);
+ ret = sd_desc->init(gspca_dev);
+ if (ret < 0)
+ goto out;
+ if (sd_desc->init_controls)
+ ret = sd_desc->init_controls(gspca_dev);
+ if (ret < 0)
+ goto out;
+ gspca_set_default_mode(gspca_dev);
+
+ ret = gspca_input_connect(gspca_dev);
+ if (ret)
+ goto out;
+
+ /*
+ * Don't take usb_lock for these ioctls. This improves latency if
+ * usb_lock is taken for a long time, e.g. when changing a control
+ * value, and a new frame is ready to be dequeued.
+ */
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
+ if (!gspca_dev->sd_desc->get_chip_ident)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ if (!gspca_dev->sd_desc->get_chip_ident ||
+ !gspca_dev->sd_desc->get_register)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
+ if (!gspca_dev->sd_desc->get_chip_ident ||
+ !gspca_dev->sd_desc->set_register)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
+#endif
+ if (!gspca_dev->sd_desc->get_jcomp)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
+ if (!gspca_dev->sd_desc->set_jcomp)
+ v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
+
+ /* init video stuff */
+ ret = video_register_device(&gspca_dev->vdev,
+ VFL_TYPE_GRABBER,
+ -1);
+ if (ret < 0) {
+ pr_err("video_register_device err %d\n", ret);
+ goto out;
+ }
+
+ usb_set_intfdata(intf, gspca_dev);
+ PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
+
+ gspca_input_create_urb(gspca_dev);
+
+ return 0;
+out:
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ if (gspca_dev->input_dev)
+ input_unregister_device(gspca_dev->input_dev);
+#endif
+ v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+ return ret;
+}
+EXPORT_SYMBOL(gspca_dev_probe2);
+
+/* same function as the previous one, but check the interface */
+int gspca_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+
+ /* we don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1) {
+ pr_err("%04x:%04x too many config\n",
+ id->idVendor, id->idProduct);
+ return -ENODEV;
+ }
+
+ /* the USB video interface must be the first one */
+ if (dev->actconfig->desc.bNumInterfaces != 1
+ && intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ return gspca_dev_probe2(intf, id, sd_desc, dev_size, module);
+}
+EXPORT_SYMBOL(gspca_dev_probe);
+
+/*
+ * USB disconnection
+ *
+ * This function must be called by the sub-driver
+ * when the device disconnects, after the specific resources are freed.
+ */
+void gspca_disconnect(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *input_dev;
+#endif
+
+ PDEBUG(D_PROBE, "%s disconnect",
+ video_device_node_name(&gspca_dev->vdev));
+
+ mutex_lock(&gspca_dev->usb_lock);
+
+ gspca_dev->present = 0;
+ destroy_urbs(gspca_dev);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ gspca_input_destroy_urb(gspca_dev);
+ input_dev = gspca_dev->input_dev;
+ if (input_dev) {
+ gspca_dev->input_dev = NULL;
+ input_unregister_device(input_dev);
+ }
+#endif
+ /* Free subdriver's streaming resources / stop sd workqueue(s) */
+ if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ gspca_dev->streaming = 0;
+ gspca_dev->dev = NULL;
+ wake_up_interruptible(&gspca_dev->wq);
+
+ v4l2_device_disconnect(&gspca_dev->v4l2_dev);
+ video_unregister_device(&gspca_dev->vdev);
+
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ /* (this will call gspca_release() immediately or on last close) */
+ v4l2_device_put(&gspca_dev->v4l2_dev);
+}
+EXPORT_SYMBOL(gspca_disconnect);
+
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+
+ gspca_input_destroy_urb(gspca_dev);
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->frozen = 1; /* avoid urb error messages */
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(gspca_suspend);
+
+int gspca_resume(struct usb_interface *intf)
+{
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ int streaming, ret = 0;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->frozen = 0;
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->init(gspca_dev);
+ /*
+ * Most subdrivers send all ctrl values on sd_start and thus
+ * only write to the device registers on s_ctrl when streaming ->
+ * Clear streaming to avoid setting all ctrls twice.
+ */
+ streaming = gspca_dev->streaming;
+ gspca_dev->streaming = 0;
+ if (streaming)
+ ret = gspca_init_transfer(gspca_dev);
+ else
+ gspca_input_create_urb(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(gspca_resume);
+#endif
+
+/* -- module insert / remove -- */
+static int __init gspca_init(void)
+{
+ pr_info("v" GSPCA_VERSION " registered\n");
+ return 0;
+}
+static void __exit gspca_exit(void)
+{
+}
+
+module_init(gspca_init);
+module_exit(gspca_exit);
+
+#ifdef GSPCA_DEBUG
+module_param_named(debug, gspca_debug, int, 0644);
+MODULE_PARM_DESC(debug,
+ "Debug (bit) 0x01:error 0x02:probe 0x04:config"
+ " 0x08:stream 0x10:frame 0x20:packet"
+ " 0x0100: v4l2");
+#endif
diff --git a/drivers/media/usb/gspca/gspca.h b/drivers/media/usb/gspca/gspca.h
new file mode 100644
index 000000000000..e3eab82cd4e5
--- /dev/null
+++ b/drivers/media/usb/gspca/gspca.h
@@ -0,0 +1,261 @@
+#ifndef GSPCAV2_H
+#define GSPCAV2_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <linux/mutex.h>
+
+/* compilation option */
+/*#define GSPCA_DEBUG 1*/
+
+#ifdef GSPCA_DEBUG
+/* GSPCA our debug messages */
+extern int gspca_debug;
+#define PDEBUG(level, fmt, ...) \
+do { \
+ if (gspca_debug & (level)) \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define D_ERR 0x01
+#define D_PROBE 0x02
+#define D_CONF 0x04
+#define D_STREAM 0x08
+#define D_FRAM 0x10
+#define D_PACK 0x20
+#define D_USBI 0x00
+#define D_USBO 0x00
+#define D_V4L2 0x0100
+#else
+#define PDEBUG(level, fmt, ...)
+#endif
+
+#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
+/* image transfers */
+#define MAX_NURBS 4 /* max number of URBs */
+
+
+/* used to list framerates supported by a camera mode (resolution) */
+struct framerates {
+ const u8 *rates;
+ int nrates;
+};
+
+/* control definition */
+struct gspca_ctrl {
+ s16 val; /* current value */
+ s16 def; /* default value */
+ s16 min, max; /* minimum and maximum values */
+};
+
+/* device information - set at probe time */
+struct cam {
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
+ const struct framerates *mode_framerates; /* must have size nmodes,
+ * just like cam_mode */
+ struct gspca_ctrl *ctrls; /* control table - size nctrls */
+ /* may be NULL */
+ u32 bulk_size; /* buffer size when image transfer by bulk */
+ u32 input_flags; /* value for ENUM_INPUT status flags */
+ u8 nmodes; /* size of cam_mode */
+ u8 no_urb_create; /* don't create transfer URBs */
+ u8 bulk_nurbs; /* number of URBs in bulk mode
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
+ u8 bulk; /* image transfer by 0:isoc / 1:bulk */
+ u8 npkt; /* number of packets in an ISOC message
+ * 0 is the default value: 32 packets */
+ u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc.
+ * code that the cam fills all image buffers to
+ * the max, even when using compression. */
+};
+
+struct gspca_dev;
+struct gspca_frame;
+
+/* subdriver operations */
+typedef int (*cam_op) (struct gspca_dev *);
+typedef void (*cam_v_op) (struct gspca_dev *);
+typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
+typedef int (*cam_get_jpg_op) (struct gspca_dev *,
+ struct v4l2_jpegcompression *);
+typedef int (*cam_set_jpg_op) (struct gspca_dev *,
+ const struct v4l2_jpegcompression *);
+typedef int (*cam_reg_op) (struct gspca_dev *,
+ struct v4l2_dbg_register *);
+typedef int (*cam_ident_op) (struct gspca_dev *,
+ struct v4l2_dbg_chip_ident *);
+typedef void (*cam_streamparm_op) (struct gspca_dev *,
+ struct v4l2_streamparm *);
+typedef int (*cam_qmnu_op) (struct gspca_dev *,
+ struct v4l2_querymenu *);
+typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len);
+typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len);
+
+struct ctrl {
+ struct v4l2_queryctrl qctrl;
+ int (*set)(struct gspca_dev *, __s32);
+ int (*get)(struct gspca_dev *, __s32 *);
+ cam_v_op set_control;
+};
+
+/* subdriver description */
+struct sd_desc {
+/* information */
+ const char *name; /* sub-driver name */
+/* controls */
+ const struct ctrl *ctrls; /* static control definition */
+ int nctrls;
+/* mandatory operations */
+ cam_cf_op config; /* called on probe */
+ cam_op init; /* called on probe and resume */
+ cam_op init_controls; /* called on probe */
+ cam_op start; /* called on stream on after URBs creation */
+ cam_pkt_op pkt_scan;
+/* optional operations */
+ cam_op isoc_init; /* called on stream on before getting the EP */
+ cam_op isoc_nego; /* called when URB submit failed with NOSPC */
+ cam_v_op stopN; /* called on stream off - main alt */
+ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */
+ cam_v_op dq_callback; /* called when a frame has been dequeued */
+ cam_get_jpg_op get_jcomp;
+ cam_set_jpg_op set_jcomp;
+ cam_qmnu_op querymenu;
+ cam_streamparm_op get_streamparm;
+ cam_streamparm_op set_streamparm;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ cam_reg_op set_register;
+ cam_reg_op get_register;
+#endif
+ cam_ident_op get_chip_ident;
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ cam_int_pkt_op int_pkt_scan;
+ /* other_input makes the gspca core create gspca_dev->input even when
+ int_pkt_scan is NULL, for cams with non interrupt driven buttons */
+ u8 other_input;
+#endif
+};
+
+/* packet types when moving from iso buf to frame buf */
+enum gspca_packet_type {
+ DISCARD_PACKET,
+ FIRST_PACKET,
+ INTER_PACKET,
+ LAST_PACKET
+};
+
+struct gspca_frame {
+ __u8 *data; /* frame buffer */
+ int vma_use_count;
+ struct v4l2_buffer v4l2_buf;
+};
+
+struct gspca_dev {
+ struct video_device vdev; /* !! must be the first item */
+ struct module *module; /* subdriver handling the device */
+ struct v4l2_device v4l2_dev;
+ struct usb_device *dev;
+ struct file *capt_file; /* file doing video capture */
+ /* protected by queue_lock */
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct input_dev *input_dev;
+ char phys[64]; /* physical device path */
+#endif
+
+ struct cam cam; /* device information */
+ const struct sd_desc *sd_desc; /* subdriver description */
+ unsigned ctrl_dis; /* disabled controls (bit map) */
+ unsigned ctrl_inac; /* inactive controls (bit map) */
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* autogain and exposure or gain control cluster, these are global as
+ the autogain/exposure functions in autogain_functions.c use them */
+ struct {
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ int exp_too_low_cnt, exp_too_high_cnt;
+ };
+
+#define USB_BUF_SZ 64
+ __u8 *usb_buf; /* buffer for USB exchanges */
+ struct urb *urb[MAX_NURBS];
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ struct urb *int_urb;
+#endif
+
+ __u8 *frbuf; /* buffer for nframes */
+ struct gspca_frame frame[GSPCA_MAX_FRAMES];
+ u8 *image; /* image beeing filled */
+ __u32 frsz; /* frame size */
+ u32 image_len; /* current length of image */
+ atomic_t fr_q; /* next frame to queue */
+ atomic_t fr_i; /* frame being filled */
+ signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
+ char nframes; /* number of frames */
+ u8 fr_o; /* next frame to dequeue */
+ __u8 last_packet_type;
+ __s8 empty_packet; /* if (-1) don't check empty packets */
+ __u8 streaming; /* protected by both mutexes (*) */
+
+ __u8 curr_mode; /* current camera mode */
+ __u32 pixfmt; /* current mode parameters */
+ __u16 width;
+ __u16 height;
+ __u32 sequence; /* frame sequence number */
+
+ wait_queue_head_t wq; /* wait queue */
+ struct mutex usb_lock; /* usb exchange protection */
+ struct mutex queue_lock; /* ISOC queue protection */
+ int usb_err; /* USB error - protected by usb_lock */
+ u16 pkt_size; /* ISOC packet size */
+#ifdef CONFIG_PM
+ char frozen; /* suspend - resume */
+#endif
+ char present; /* device connected */
+ char nbufread; /* number of buffers for read() */
+ char memory; /* memory type (V4L2_MEMORY_xxx) */
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
+ u8 audio; /* presence of audio device */
+
+ /* (*) These variables are proteced by both usb_lock and queue_lock,
+ that is any code setting them is holding *both*, which means that
+ any code getting them needs to hold at least one of them */
+};
+
+int gspca_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module);
+int gspca_dev_probe2(struct usb_interface *intf,
+ const struct usb_device_id *id,
+ const struct sd_desc *sd_desc,
+ int dev_size,
+ struct module *module);
+void gspca_disconnect(struct usb_interface *intf);
+void gspca_frame_add(struct gspca_dev *gspca_dev,
+ enum gspca_packet_type packet_type,
+ const u8 *data,
+ int len);
+#ifdef CONFIG_PM
+int gspca_suspend(struct usb_interface *intf, pm_message_t message);
+int gspca_resume(struct usb_interface *intf);
+#endif
+int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum,
+ int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
+int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
+ int avg_lum, int desired_avg_lum, int deadzone);
+
+#endif /* GSPCAV2_H */
diff --git a/drivers/media/usb/gspca/jeilinj.c b/drivers/media/usb/gspca/jeilinj.c
new file mode 100644
index 000000000000..b897aa86f315
--- /dev/null
+++ b/drivers/media/usb/gspca/jeilinj.c
@@ -0,0 +1,548 @@
+/*
+ * Jeilinj subdriver
+ *
+ * Supports some Jeilin dual-mode cameras which use bulk transport and
+ * download raw JPEG data.
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * Sportscam DV15 support and control settings are
+ * Copyright (C) 2011 Patrice Chotard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "jeilinj"
+
+#include <linux/slab.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_CMD_DELAY 160
+#define JEILINJ_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JEILINJ_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 0x10
+#define FRAME_START 0xFFFFFFFF
+
+enum {
+ SAKAR_57379,
+ SPORTSCAM_DV15,
+};
+
+#define CAMQUALITY_MIN 0 /* highest cam quality */
+#define CAMQUALITY_MAX 97 /* lowest cam quality */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ int blocks_left;
+ const struct v4l2_pix_format *cap_mode;
+ struct v4l2_ctrl *freq;
+ struct v4l2_ctrl *jpegqual;
+ /* Driver stuff */
+ u8 type;
+ u8 quality; /* image quality */
+#define QUALITY_MIN 35
+#define QUALITY_MAX 85
+#define QUALITY_DEF 85
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+struct jlj_command {
+ unsigned char instruction[2];
+ unsigned char ack_wanted;
+ unsigned char delay;
+};
+
+/* AFAICT these cameras will only do 320x240. */
+static struct v4l2_pix_format jlj_mode[] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0}
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk transfer.
+ */
+
+/* All commands are two bytes only */
+static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+ int retval;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, command, 2);
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, 2, NULL, 500);
+ if (retval < 0) {
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ gspca_dev->usb_err = retval;
+ }
+}
+
+/* Responses are one byte only */
+static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
+{
+ int retval;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+ gspca_dev->usb_buf, 1, NULL, 500);
+ response = gspca_dev->usb_buf[0];
+ if (retval < 0) {
+ pr_err("read command [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ gspca_dev->usb_err = retval;
+ }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 freq_commands[][2] = {
+ {0x71, 0x80},
+ {0x70, 0x07}
+ };
+
+ freq_commands[0][1] |= val >> 1;
+
+ jlj_write2(gspca_dev, freq_commands[0]);
+ jlj_write2(gspca_dev, freq_commands[1]);
+}
+
+static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 quality_commands[][2] = {
+ {0x71, 0x1E},
+ {0x70, 0x06}
+ };
+ u8 camquality;
+
+ /* adapt camera quality from jpeg quality */
+ camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
+ / (QUALITY_MAX - QUALITY_MIN);
+ quality_commands[0][1] += camquality;
+
+ jlj_write2(gspca_dev, quality_commands[0]);
+ jlj_write2(gspca_dev, quality_commands[1]);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 autogain_commands[][2] = {
+ {0x94, 0x02},
+ {0xcf, 0x00}
+ };
+
+ autogain_commands[1][1] = val << 4;
+
+ jlj_write2(gspca_dev, autogain_commands[0]);
+ jlj_write2(gspca_dev, autogain_commands[1]);
+}
+
+static void setred(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 setred_commands[][2] = {
+ {0x94, 0x02},
+ {0xe6, 0x00}
+ };
+
+ setred_commands[1][1] = val;
+
+ jlj_write2(gspca_dev, setred_commands[0]);
+ jlj_write2(gspca_dev, setred_commands[1]);
+}
+
+static void setgreen(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 setgreen_commands[][2] = {
+ {0x94, 0x02},
+ {0xe7, 0x00}
+ };
+
+ setgreen_commands[1][1] = val;
+
+ jlj_write2(gspca_dev, setgreen_commands[0]);
+ jlj_write2(gspca_dev, setgreen_commands[1]);
+}
+
+static void setblue(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 setblue_commands[][2] = {
+ {0x94, 0x02},
+ {0xe9, 0x00}
+ };
+
+ setblue_commands[1][1] = val;
+
+ jlj_write2(gspca_dev, setblue_commands[0]);
+ jlj_write2(gspca_dev, setblue_commands[1]);
+}
+
+static int jlj_start(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int start_commands_size;
+ u8 response = 0xff;
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct jlj_command start_commands[] = {
+ {{0x71, 0x81}, 0, 0},
+ {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+ {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
+ {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+ {{0x71, 0x00}, 0, 0}, /* start streaming ??*/
+ {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
+ {{0x95, 0x70}, 1, 0},
+#define SPORTSCAM_DV15_CMD_SIZE 9
+ {{0x94, 0x02}, 0, 0},
+ {{0xde, 0x24}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xdd, 0xf0}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe3, 0x2c}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe4, 0x00}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe5, 0x00}, 0, 0},
+ {{0x94, 0x02}, 0, 0},
+ {{0xe6, 0x2c}, 0, 0},
+ {{0x94, 0x03}, 0, 0},
+ {{0xaa, 0x00}, 0, 0}
+ };
+
+ sd->blocks_left = 0;
+ /* Under Windows, USB spy shows that only the 9 first start
+ * commands are used for SPORTSCAM_DV15 webcam
+ */
+ if (sd->type == SPORTSCAM_DV15)
+ start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
+ else
+ start_commands_size = ARRAY_SIZE(start_commands);
+
+ for (i = 0; i < start_commands_size; i++) {
+ jlj_write2(gspca_dev, start_commands[i].instruction);
+ if (start_commands[i].delay)
+ msleep(start_commands[i].delay);
+ if (start_commands[i].ack_wanted)
+ jlj_read1(gspca_dev, response);
+ }
+ setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+ msleep(2);
+ setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
+ if (gspca_dev->usb_err < 0)
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return gspca_dev->usb_err;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int packet_type;
+ u32 header_marker;
+
+ PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
+ len, JEILINJ_MAX_TRANSFER);
+ if (len != JEILINJ_MAX_TRANSFER) {
+ PDEBUG(D_PACK, "bad length");
+ goto discard;
+ }
+ /* check if it's start of frame */
+ header_marker = ((u32 *)data)[0];
+ if (header_marker == FRAME_START) {
+ sd->blocks_left = data[0x0a] - 1;
+ PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
+ /* Start a new frame, and add the JPEG header, first thing */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ /* Toss line 0 of data block 0, keep the rest. */
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + FRAME_HEADER_LEN,
+ JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+ } else if (sd->blocks_left > 0) {
+ PDEBUG(D_STREAM, "%d blocks remaining for frame",
+ sd->blocks_left);
+ sd->blocks_left -= 1;
+ if (sd->blocks_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ data, JEILINJ_MAX_TRANSFER);
+ } else
+ goto discard;
+ return;
+discard:
+ /* Discard data until a new frame starts. */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+}
+
+/* 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 *dev = (struct sd *) gspca_dev;
+
+ dev->type = id->driver_info;
+ dev->quality = QUALITY_DEF;
+
+ cam->cam_mode = jlj_mode;
+ cam->nmodes = ARRAY_SIZE(jlj_mode);
+ cam->bulk = 1;
+ cam->bulk_nurbs = 1;
+ cam->bulk_size = JEILINJ_MAX_TRANSFER;
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ int i;
+ u8 *buf;
+ static u8 stop_commands[][2] = {
+ {0x71, 0x00},
+ {0x70, 0x09},
+ {0x71, 0x80},
+ {0x70, 0x05}
+ };
+
+ for (;;) {
+ /* get the image remaining blocks */
+ usb_bulk_msg(gspca_dev->dev,
+ gspca_dev->urb[0]->pipe,
+ gspca_dev->urb[0]->transfer_buffer,
+ JEILINJ_MAX_TRANSFER, NULL,
+ JEILINJ_DATA_TIMEOUT);
+
+ /* search for 0xff 0xd9 (EOF for JPEG) */
+ i = 0;
+ buf = gspca_dev->urb[0]->transfer_buffer;
+ while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
+ ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
+ i++;
+
+ if (i != (JEILINJ_MAX_TRANSFER - 1))
+ /* last remaining block found */
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
+ jlj_write2(gspca_dev, stop_commands[i]);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return gspca_dev->usb_err;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* create the JPEG header */
+ jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(dev->jpeg_hdr, dev->quality);
+ PDEBUG(D_STREAM, "Start streaming at %dx%d",
+ gspca_dev->height, gspca_dev->width);
+ jlj_start(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
+ {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setred(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgreen(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setblue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+ setcamquality(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const struct v4l2_ctrl_config custom_autogain = {
+ .ops = &sd_ctrl_ops,
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain (and Exposure)",
+ .max = 3,
+ .step = 1,
+ .def = 0,
+ };
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 6);
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
+ v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 3, 1, 2);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ const struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sakar_57379 = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* sub-driver description */
+static const struct sd_desc sd_desc_sportscam_dv15 = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct sd_desc *sd_desc[2] = {
+ &sd_desc_sakar_57379,
+ &sd_desc_sportscam_dv15
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ sd_desc[id->driver_info],
+ 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c
new file mode 100644
index 000000000000..62ba80d9b998
--- /dev/null
+++ b/drivers/media/usb/gspca/jl2005bcd.c
@@ -0,0 +1,555 @@
+/*
+ * Jeilin JL2005B/C/D library
+ *
+ * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 "jl2005bcd"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JL2005C_CMD_TIMEOUT 500
+#define JL2005C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JL2005C_MAX_TRANSFER 0x200
+#define FRAME_HEADER_LEN 16
+
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ unsigned char firmware_id[6];
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+ u8 frame_brightness;
+ int block_size; /* block size of camera */
+ int vga; /* 1 if vga cam, 0 if cif cam */
+};
+
+
+/* Camera has two resolution settings. What they are depends on model. */
+static const struct v4l2_pix_format cif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk data transfer.
+ */
+
+/* All commands are two bytes only */
+static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+ int retval;
+
+ memcpy(gspca_dev->usb_buf, command, 2);
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, 2, NULL, 500);
+ if (retval < 0)
+ pr_err("command write [%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+/* Response to a command is one byte in usb_buf[0], only if requested. */
+static int jl2005c_read1(struct gspca_dev *gspca_dev)
+{
+ int retval;
+
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+ gspca_dev->usb_buf, 1, NULL, 500);
+ if (retval < 0)
+ pr_err("read command [0x%02x] error %d\n",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+/* Response appears in gspca_dev->usb_buf[0] */
+static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
+{
+ int retval;
+
+ static u8 instruction[2] = {0x95, 0x00};
+ /* put register to read in byte 1 */
+ instruction[1] = reg;
+ /* Send the read request */
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+ retval = jl2005c_read1(gspca_dev);
+
+ return retval;
+}
+
+static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval;
+ int frame_brightness = 0;
+
+ static u8 instruction[2] = {0x7f, 0x01};
+
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+
+ i = 0;
+ while (i < 20 && !frame_brightness) {
+ /* If we tried 20 times, give up. */
+ retval = jl2005c_read_reg(gspca_dev, 0x7e);
+ if (retval < 0)
+ return retval;
+ frame_brightness = gspca_dev->usb_buf[0];
+ retval = jl2005c_read_reg(gspca_dev, 0x7d);
+ if (retval < 0)
+ return retval;
+ i++;
+ }
+ PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
+ return retval;
+}
+
+static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
+ unsigned char value)
+{
+ int retval;
+ u8 instruction[2];
+
+ instruction[0] = reg;
+ instruction[1] = value;
+
+ retval = jl2005c_write2(gspca_dev, instruction);
+ if (retval < 0)
+ return retval;
+
+ return retval;
+}
+
+static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ int i = 0;
+ int retval = -1;
+ unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
+
+ PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
+ /* Read the first ID byte once for warmup */
+ retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
+ PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
+ if (retval < 0)
+ return retval;
+ /* Now actually get the ID string */
+ for (i = 0; i < 6; i++) {
+ retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
+ if (retval < 0)
+ return retval;
+ sd->firmware_id[i] = gspca_dev->usb_buf[0];
+ }
+ PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
+ sd->firmware_id[0],
+ sd->firmware_id[1],
+ sd->firmware_id[2],
+ sd->firmware_id[3],
+ sd->firmware_id[4],
+ sd->firmware_id[5]);
+ return 0;
+}
+
+static int jl2005c_stream_start_vga_lg
+ (struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x05, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x18},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x52},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x06, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x1a},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x52},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x05, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x30},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x42},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ static u8 instruction[][2] = {
+ {0x06, 0x00},
+ {0x7c, 0x00},
+ {0x7d, 0x32},
+ {0x02, 0x00},
+ {0x01, 0x00},
+ {0x04, 0x42},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(instruction); i++) {
+ msleep(60);
+ retval = jl2005c_write2(gspca_dev, instruction[i]);
+ if (retval < 0)
+ return retval;
+ }
+ msleep(60);
+ return retval;
+}
+
+
+static int jl2005c_stop(struct gspca_dev *gspca_dev)
+{
+ int retval;
+
+ retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
+ return retval;
+}
+
+/*
+ * 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 gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void jl2005c_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left = 0; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int header_read = 0;
+ unsigned char header_sig[2] = {0x4a, 0x4c};
+ int act_len;
+ int packet_type;
+ int ret;
+ u8 *buffer;
+
+ buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ /* Check if this is a new frame. If so, start the frame first */
+ if (!header_read) {
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = jl2005c_start_new_frame(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ goto quit_stream;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, JL2005C_MAX_TRANSFER, &act_len,
+ JL2005C_DATA_TIMEOUT);
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for header",
+ act_len, JL2005C_MAX_TRANSFER);
+ if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
+ goto quit_stream;
+ /* Check whether we actually got the first blodk */
+ if (memcmp(header_sig, buffer, 2) != 0) {
+ pr_err("First block is not the first block\n");
+ goto quit_stream;
+ }
+ /* total size to fetch is byte 7, times blocksize
+ * of which we already got act_len */
+ bytes_left = buffer[0x07] * dev->block_size - act_len;
+ PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
+ /* We keep the header. It has other information, too.*/
+ packet_type = FIRST_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, act_len);
+ header_read = 1;
+ }
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > JL2005C_MAX_TRANSFER ?
+ JL2005C_MAX_TRANSFER : bytes_left;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, data_len, &act_len,
+ JL2005C_DATA_TIMEOUT);
+ if (ret < 0 || act_len < data_len)
+ goto quit_stream;
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ if (bytes_left == 0) {
+ packet_type = LAST_PACKET;
+ header_read = 0;
+ } else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, data_len);
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ jl2005c_stop(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ kfree(buffer);
+}
+
+
+
+
+/* This function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ cam = &gspca_dev->cam;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 64;
+ cam->bulk = 1;
+ /* For the rest, the camera needs to be detected */
+ jl2005c_get_firmware_id(gspca_dev);
+ /* Here are some known firmware IDs
+ * First some JL2005B cameras
+ * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam
+ * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B
+ * JL2005C cameras
+ * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512
+ * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly
+ * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz
+ *
+ * Based upon this scanty evidence, we can detect a CIF camera by
+ * testing byte 0 for 0x4x.
+ */
+ if ((sd->firmware_id[0] & 0xf0) == 0x40) {
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ sd->block_size = 0x80;
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ sd->block_size = 0x200;
+ }
+
+ INIT_WORK(&sd->work_struct, jl2005c_dostream);
+
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+
+ struct sd *sd = (struct sd *) gspca_dev;
+ sd->cap_mode = gspca_dev->cam.cam_mode;
+
+ switch (gspca_dev->width) {
+ case 640:
+ PDEBUG(D_STREAM, "Start streaming at vga resolution");
+ jl2005c_stream_start_vga_lg(gspca_dev);
+ break;
+ case 320:
+ PDEBUG(D_STREAM, "Start streaming at qvga resolution");
+ jl2005c_stream_start_vga_small(gspca_dev);
+ break;
+ case 352:
+ PDEBUG(D_STREAM, "Start streaming at cif resolution");
+ jl2005c_stream_start_cif_lg(gspca_dev);
+ break;
+ case 176:
+ PDEBUG(D_STREAM, "Start streaming at qcif resolution");
+ jl2005c_stream_start_cif_small(gspca_dev);
+ break;
+ default:
+ pr_err("Unknown resolution specified\n");
+ return -1;
+ }
+
+ /* 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 sq905c_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0979, 0x0227)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+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/jpeg.h b/drivers/media/usb/gspca/jpeg.h
index ab54910418b4..ab54910418b4 100644
--- a/drivers/media/video/gspca/jpeg.h
+++ b/drivers/media/usb/gspca/jpeg.h
diff --git a/drivers/media/video/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c
index 40ad6687ee5d..40ad6687ee5d 100644
--- a/drivers/media/video/gspca/kinect.c
+++ b/drivers/media/usb/gspca/kinect.c
diff --git a/drivers/media/video/gspca/konica.c b/drivers/media/usb/gspca/konica.c
index bbf91e07e38b..bbf91e07e38b 100644
--- a/drivers/media/video/gspca/konica.c
+++ b/drivers/media/usb/gspca/konica.c
diff --git a/drivers/media/video/gspca/m5602/Kconfig b/drivers/media/usb/gspca/m5602/Kconfig
index 5a69016ed75f..5a69016ed75f 100644
--- a/drivers/media/video/gspca/m5602/Kconfig
+++ b/drivers/media/usb/gspca/m5602/Kconfig
diff --git a/drivers/media/usb/gspca/m5602/Makefile b/drivers/media/usb/gspca/m5602/Makefile
new file mode 100644
index 000000000000..8e1fb5a1d2a1
--- /dev/null
+++ b/drivers/media/usb/gspca/m5602/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_M5602) += gspca_m5602.o
+
+gspca_m5602-objs := m5602_core.o \
+ m5602_ov9650.o \
+ m5602_ov7660.o \
+ m5602_mt9m111.o \
+ m5602_po1030.o \
+ m5602_s5k83a.o \
+ m5602_s5k4aa.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/usb/gspca/m5602/m5602_bridge.h
index 51af3ee3ab85..51af3ee3ab85 100644
--- a/drivers/media/video/gspca/m5602/m5602_bridge.h
+++ b/drivers/media/usb/gspca/m5602/m5602_bridge.h
diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/usb/gspca/m5602/m5602_core.c
index ed22638978ce..ed22638978ce 100644
--- a/drivers/media/video/gspca/m5602/m5602_core.c
+++ b/drivers/media/usb/gspca/m5602/m5602_core.c
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
index 6268aa24ec5d..6268aa24ec5d 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.c
diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h
index 8c672b5c8c6a..8c672b5c8c6a 100644
--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h
+++ b/drivers/media/usb/gspca/m5602/m5602_mt9m111.h
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/usb/gspca/m5602/m5602_ov7660.c
index 9a14835c128f..9a14835c128f 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
+++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.c
diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/usb/gspca/m5602/m5602_ov7660.h
index 2b6a13b508f7..2b6a13b508f7 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov7660.h
+++ b/drivers/media/usb/gspca/m5602/m5602_ov7660.h
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/usb/gspca/m5602/m5602_ov9650.c
index 2114a8b90ec9..2114a8b90ec9 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
+++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.c
diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/usb/gspca/m5602/m5602_ov9650.h
index f7aa5bf68983..f7aa5bf68983 100644
--- a/drivers/media/video/gspca/m5602/m5602_ov9650.h
+++ b/drivers/media/usb/gspca/m5602/m5602_ov9650.h
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/usb/gspca/m5602/m5602_po1030.c
index b8771698cbcb..b8771698cbcb 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
+++ b/drivers/media/usb/gspca/m5602/m5602_po1030.c
diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/usb/gspca/m5602/m5602_po1030.h
index 81a2bcb88fe3..81a2bcb88fe3 100644
--- a/drivers/media/video/gspca/m5602/m5602_po1030.h
+++ b/drivers/media/usb/gspca/m5602/m5602_po1030.h
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
index cc8ec3f7e8dc..cc8ec3f7e8dc 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.c
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h
index 8e0035e731c7..8e0035e731c7 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k4aa.h
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
index 1de743a02b02..1de743a02b02 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.c
diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h
index 79952247b534..79952247b534 100644
--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h
+++ b/drivers/media/usb/gspca/m5602/m5602_s5k83a.h
diff --git a/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/usb/gspca/m5602/m5602_sensor.h
index edff4f1f586f..edff4f1f586f 100644
--- a/drivers/media/video/gspca/m5602/m5602_sensor.h
+++ b/drivers/media/usb/gspca/m5602/m5602_sensor.h
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/usb/gspca/mars.c
index ff2c5abf115b..ff2c5abf115b 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/usb/gspca/mars.c
diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/usb/gspca/mr97310a.c
index 8f4714df5990..8f4714df5990 100644
--- a/drivers/media/video/gspca/mr97310a.c
+++ b/drivers/media/usb/gspca/mr97310a.c
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/usb/gspca/nw80x.c
index 44c9964b1b3e..44c9964b1b3e 100644
--- a/drivers/media/video/gspca/nw80x.c
+++ b/drivers/media/usb/gspca/nw80x.c
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
new file mode 100644
index 000000000000..9aa09f845ce4
--- /dev/null
+++ b/drivers/media/usb/gspca/ov519.c
@@ -0,0 +1,4991 @@
+/**
+ * OV519 driver
+ *
+ * Copyright (C) 2008-2011 Jean-François Moine <moinejf@free.fr>
+ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2006 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
+ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
+ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "ov519"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+/* The jpeg_hdr is used by w996Xcf only */
+/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
+#define CONEX_CAM
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("OV519 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* global parameters */
+static int frame_rate;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 10;
+
+/* ov519 device descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct v4l2_ctrl *jpegqual;
+ struct v4l2_ctrl *freq;
+ struct { /* h/vflip control cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct { /* autobrightness/brightness control cluster */
+ struct v4l2_ctrl *autobright;
+ struct v4l2_ctrl *brightness;
+ };
+
+ u8 packet_nr;
+
+ char bridge;
+#define BRIDGE_OV511 0
+#define BRIDGE_OV511PLUS 1
+#define BRIDGE_OV518 2
+#define BRIDGE_OV518PLUS 3
+#define BRIDGE_OV519 4 /* = ov530 */
+#define BRIDGE_OVFX2 5
+#define BRIDGE_W9968CF 6
+#define BRIDGE_MASK 7
+
+ char invert_led;
+#define BRIDGE_INVERT_LED 8
+
+ char snapshot_pressed;
+ char snapshot_needs_reset;
+
+ /* Determined by sensor type */
+ u8 sif;
+
+#define QUALITY_MIN 50
+#define QUALITY_MAX 70
+#define QUALITY_DEF 50
+
+ u8 stopped; /* Streaming is temporarily paused */
+ u8 first_frame;
+
+ u8 frame_rate; /* current Framerate */
+ u8 clockdiv; /* clockdiv override */
+
+ s8 sensor; /* Type of image sensor chip (SEN_*) */
+
+ u8 sensor_addr;
+ u16 sensor_width;
+ u16 sensor_height;
+ s16 sensor_reg_cache[256];
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+ SEN_OV2610,
+ SEN_OV2610AE,
+ SEN_OV3610,
+ SEN_OV6620,
+ SEN_OV6630,
+ SEN_OV66308AF,
+ SEN_OV7610,
+ SEN_OV7620,
+ SEN_OV7620AE,
+ SEN_OV7640,
+ SEN_OV7648,
+ SEN_OV7660,
+ SEN_OV7670,
+ SEN_OV76BE,
+ SEN_OV8610,
+ SEN_OV9600,
+};
+
+/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
+ the ov sensors which is already present here. When we have the time we
+ really should move the sensor drivers to v4l2 sub drivers. */
+#include "w996Xcf.c"
+
+/* table of the disabled controls */
+struct ctrl_valid {
+ unsigned int has_brightness:1;
+ unsigned int has_contrast:1;
+ unsigned int has_exposure:1;
+ unsigned int has_autogain:1;
+ unsigned int has_sat:1;
+ unsigned int has_hvflip:1;
+ unsigned int has_autobright:1;
+ unsigned int has_freq:1;
+};
+
+static const struct ctrl_valid valid_controls[] = {
+ [SEN_OV2610] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
+ },
+ [SEN_OV2610AE] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
+ },
+ [SEN_OV3610] = {
+ /* No controls */
+ },
+ [SEN_OV6620] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV6630] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV66308AF] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7610] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7620] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7620AE] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7640] = {
+ .has_brightness = 1,
+ .has_sat = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7648] = {
+ .has_brightness = 1,
+ .has_sat = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7660] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_hvflip = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV7670] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_hvflip = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV76BE] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ .has_freq = 1,
+ },
+ [SEN_OV8610] = {
+ .has_brightness = 1,
+ .has_contrast = 1,
+ .has_sat = 1,
+ .has_autobright = 1,
+ },
+ [SEN_OV9600] = {
+ .has_exposure = 1,
+ .has_autogain = 1,
+ },
+};
+
+static const struct v4l2_pix_format ov519_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov519_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* Note some of the sizeimage values for the ov511 / ov518 may seem
+ larger then necessary, however they need to be this big as the ov511 /
+ ov518 always fills the entire isoc frame, using 0 padding bytes when
+ it doesn't have any data. So with low framerates the amount of data
+ transferred can become quite large (libv4l will remove all the 0 padding
+ in userspace). */
+static const struct v4l2_pix_format ov518_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov518_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format ov511_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ov511_sif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 70000,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format ovfx2_vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_cif_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 3},
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .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,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1600,
+ .sizeimage = 1600 * 1200,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 2048,
+ .sizeimage = 2048 * 1536,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/* Registers common to OV511 / OV518 */
+#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
+#define R51x_SYS_RESET 0x50
+ /* Reset type flags */
+ #define OV511_RESET_OMNICE 0x08
+#define R51x_SYS_INIT 0x53
+#define R51x_SYS_SNAP 0x52
+#define R51x_SYS_CUST_ID 0x5f
+#define R51x_COMP_LUT_BEGIN 0x80
+
+/* OV511 Camera interface register numbers */
+#define R511_CAM_DELAY 0x10
+#define R511_CAM_EDGE 0x11
+#define R511_CAM_PXCNT 0x12
+#define R511_CAM_LNCNT 0x13
+#define R511_CAM_PXDIV 0x14
+#define R511_CAM_LNDIV 0x15
+#define R511_CAM_UV_EN 0x16
+#define R511_CAM_LINE_MODE 0x17
+#define R511_CAM_OPTS 0x18
+
+#define R511_SNAP_FRAME 0x19
+#define R511_SNAP_PXCNT 0x1a
+#define R511_SNAP_LNCNT 0x1b
+#define R511_SNAP_PXDIV 0x1c
+#define R511_SNAP_LNDIV 0x1d
+#define R511_SNAP_UV_EN 0x1e
+#define R511_SNAP_OPTS 0x1f
+
+#define R511_DRAM_FLOW_CTL 0x20
+#define R511_FIFO_OPTS 0x31
+#define R511_I2C_CTL 0x40
+#define R511_SYS_LED_CTL 0x55 /* OV511+ only */
+#define R511_COMP_EN 0x78
+#define R511_COMP_LUT_EN 0x79
+
+/* OV518 Camera interface register numbers */
+#define R518_GPIO_OUT 0x56 /* OV518(+) only */
+#define R518_GPIO_CTL 0x57 /* OV518(+) only */
+
+/* OV519 Camera interface register numbers */
+#define OV519_R10_H_SIZE 0x10
+#define OV519_R11_V_SIZE 0x11
+#define OV519_R12_X_OFFSETL 0x12
+#define OV519_R13_X_OFFSETH 0x13
+#define OV519_R14_Y_OFFSETL 0x14
+#define OV519_R15_Y_OFFSETH 0x15
+#define OV519_R16_DIVIDER 0x16
+#define OV519_R20_DFR 0x20
+#define OV519_R25_FORMAT 0x25
+
+/* OV519 System Controller register numbers */
+#define OV519_R51_RESET1 0x51
+#define OV519_R54_EN_CLK1 0x54
+#define OV519_R57_SNAPSHOT 0x57
+
+#define OV519_GPIO_DATA_OUT0 0x71
+#define OV519_GPIO_IO_CTRL0 0x72
+
+/*#define OV511_ENDPOINT_ADDRESS 1 * Isoc endpoint number */
+
+/*
+ * The FX2 chip does not give us a zero length read at end of frame.
+ * It does, however, give a short read at the end of a frame, if
+ * necessary, rather than run two frames together.
+ *
+ * By choosing the right bulk transfer size, we are guaranteed to always
+ * get a short read for the last read of each frame. Frame sizes are
+ * always a composite number (width * height, or a multiple) so if we
+ * choose a prime number, we are guaranteed that the last read of a
+ * frame will be short.
+ *
+ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
+ * otherwise EOVERFLOW "babbling" errors occur. I have not been able
+ * to figure out why. [PMiller]
+ *
+ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
+ *
+ * It isn't enough to know the number of bytes per frame, in case we
+ * have data dropouts or buffer overruns (even though the FX2 double
+ * buffers, there are some pretty strict real time constraints for
+ * isochronous transfer for larger frame sizes).
+ */
+/*jfm: this value does not work for 800x600 - see isoc_init */
+#define OVFX2_BULK_SIZE (13 * 4096)
+
+/* I2C registers */
+#define R51x_I2C_W_SID 0x41
+#define R51x_I2C_SADDR_3 0x42
+#define R51x_I2C_SADDR_2 0x43
+#define R51x_I2C_R_SID 0x44
+#define R51x_I2C_DATA 0x45
+#define R518_I2C_CTL 0x47 /* OV518(+) only */
+#define OVFX2_I2C_ADDR 0x00
+
+/* I2C ADDRESSES */
+#define OV7xx0_SID 0x42
+#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */
+#define OV8xx0_SID 0xa0
+#define OV6xx0_SID 0xc0
+
+/* OV7610 registers */
+#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
+#define OV7610_REG_BLUE 0x01 /* blue channel balance */
+#define OV7610_REG_RED 0x02 /* red channel balance */
+#define OV7610_REG_SAT 0x03 /* saturation */
+#define OV8610_REG_HUE 0x04 /* 04 reserved */
+#define OV7610_REG_CNT 0x05 /* Y contrast */
+#define OV7610_REG_BRT 0x06 /* Y brightness */
+#define OV7610_REG_COM_C 0x14 /* misc common regs */
+#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */
+#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */
+#define OV7610_REG_COM_I 0x29 /* misc settings */
+
+/* OV7660 and OV7670 registers */
+#define OV7670_R00_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
+#define OV7670_R01_BLUE 0x01 /* blue gain */
+#define OV7670_R02_RED 0x02 /* red gain */
+#define OV7670_R03_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
+#define OV7670_R04_COM1 0x04 /* Control 1 */
+/*#define OV7670_R07_AECHH 0x07 * AEC MS 5 bits */
+#define OV7670_R0C_COM3 0x0c /* Control 3 */
+#define OV7670_R0D_COM4 0x0d /* Control 4 */
+#define OV7670_R0E_COM5 0x0e /* All "reserved" */
+#define OV7670_R0F_COM6 0x0f /* Control 6 */
+#define OV7670_R10_AECH 0x10 /* More bits of AEC value */
+#define OV7670_R11_CLKRC 0x11 /* Clock control */
+#define OV7670_R12_COM7 0x12 /* Control 7 */
+#define OV7670_COM7_FMT_VGA 0x00
+/*#define OV7670_COM7_YUV 0x00 * YUV */
+#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */
+#define OV7670_COM7_FMT_MASK 0x38
+#define OV7670_COM7_RESET 0x80 /* Register reset */
+#define OV7670_R13_COM8 0x13 /* Control 8 */
+#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */
+#define OV7670_COM8_AWB 0x02 /* White balance enable */
+#define OV7670_COM8_AGC 0x04 /* Auto gain enable */
+#define OV7670_COM8_BFILT 0x20 /* Band filter enable */
+#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */
+#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
+#define OV7670_R14_COM9 0x14 /* Control 9 - gain ceiling */
+#define OV7670_R15_COM10 0x15 /* Control 10 */
+#define OV7670_R17_HSTART 0x17 /* Horiz start high bits */
+#define OV7670_R18_HSTOP 0x18 /* Horiz stop high bits */
+#define OV7670_R19_VSTART 0x19 /* Vert start high bits */
+#define OV7670_R1A_VSTOP 0x1a /* Vert stop high bits */
+#define OV7670_R1E_MVFP 0x1e /* Mirror / vflip */
+#define OV7670_MVFP_VFLIP 0x10 /* vertical flip */
+#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */
+#define OV7670_R24_AEW 0x24 /* AGC upper limit */
+#define OV7670_R25_AEB 0x25 /* AGC lower limit */
+#define OV7670_R26_VPT 0x26 /* AGC/AEC fast mode op region */
+#define OV7670_R32_HREF 0x32 /* HREF pieces */
+#define OV7670_R3A_TSLB 0x3a /* lots of stuff */
+#define OV7670_R3B_COM11 0x3b /* Control 11 */
+#define OV7670_COM11_EXP 0x02
+#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
+#define OV7670_R3C_COM12 0x3c /* Control 12 */
+#define OV7670_R3D_COM13 0x3d /* Control 13 */
+#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */
+#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */
+#define OV7670_R3E_COM14 0x3e /* Control 14 */
+#define OV7670_R3F_EDGE 0x3f /* Edge enhancement factor */
+#define OV7670_R40_COM15 0x40 /* Control 15 */
+/*#define OV7670_COM15_R00FF 0xc0 * 00 to FF */
+#define OV7670_R41_COM16 0x41 /* Control 16 */
+#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */
+/* end of ov7660 common registers */
+#define OV7670_R55_BRIGHT 0x55 /* Brightness */
+#define OV7670_R56_CONTRAS 0x56 /* Contrast control */
+#define OV7670_R69_GFIX 0x69 /* Fix gain control */
+/*#define OV7670_R8C_RGB444 0x8c * RGB 444 control */
+#define OV7670_R9F_HAECC1 0x9f /* Hist AEC/AGC control 1 */
+#define OV7670_RA0_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
+#define OV7670_RA5_BD50MAX 0xa5 /* 50hz banding step limit */
+#define OV7670_RA6_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
+#define OV7670_RA7_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
+#define OV7670_RA8_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
+#define OV7670_RA9_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
+#define OV7670_RAA_HAECC7 0xaa /* Hist AEC/AGC control 7 */
+#define OV7670_RAB_BD60MAX 0xab /* 60hz banding step limit */
+
+struct ov_regvals {
+ u8 reg;
+ u8 val;
+};
+struct ov_i2c_regvals {
+ u8 reg;
+ u8 val;
+};
+
+/* Settings for OV2610 camera chip */
+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
+ * (0x12) to change the sensor mode, registers related to the
+ * sensor’s cropping window will be reset back to their default
+ * values."
+ *
+ * "wait 4096 external clock ... to make sure the sensor is
+ * stable and ready to access registers" i.e. 160us at 24MHz
+ */
+ { 0x12, 0x80 }, /* COMH reset */
+ { 0x12, 0x00 }, /* QXGA, master */
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * [7] internal frequency doublers: on
+ * [6] video port mode: master
+ * [5:0] clock divider: 1
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 13 COMI "Common Control I"
+ * = 192 (0xC0) 11000000
+ * COMI[7] "AEC speed selection"
+ * = 1 (0x01) 1....... "Faster AEC correction"
+ * COMI[6] "AEC speed step selection"
+ * = 1 (0x01) .1...... "Big steps, fast"
+ * COMI[5] "Banding filter on off"
+ * = 0 (0x00) ..0..... "Off"
+ * COMI[4] "Banding filter option"
+ * = 0 (0x00) ...0.... "Main clock is 48 MHz and
+ * the PLL is ON"
+ * COMI[3] "Reserved"
+ * = 0 (0x00) ....0...
+ * COMI[2] "AGC auto manual control selection"
+ * = 0 (0x00) .....0.. "Manual"
+ * COMI[1] "AWB auto manual control selection"
+ * = 0 (0x00) ......0. "Manual"
+ * COMI[0] "Exposure control"
+ * = 0 (0x00) .......0 "Manual"
+ */
+ { 0x13, 0xc0 },
+
+ /*
+ * 09 COMC "Common Control C"
+ * = 8 (0x08) 00001000
+ * COMC[7:5] "Reserved"
+ * = 0 (0x00) 000.....
+ * COMC[4] "Sleep Mode Enable"
+ * = 0 (0x00) ...0.... "Normal mode"
+ * COMC[3:2] "Sensor sampling reset timing selection"
+ * = 2 (0x02) ....10.. "Longer reset time"
+ * COMC[1:0] "Output drive current select"
+ * = 0 (0x00) ......00 "Weakest"
+ */
+ { 0x09, 0x08 },
+
+ /*
+ * 0C COMD "Common Control D"
+ * = 8 (0x08) 00001000
+ * COMD[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * COMD[6] "Swap MSB and LSB at the output port"
+ * = 0 (0x00) .0...... "False"
+ * COMD[5:3] "Reserved"
+ * = 1 (0x01) ..001...
+ * COMD[2] "Output Average On Off"
+ * = 0 (0x00) .....0.. "Output Normal"
+ * COMD[1] "Sensor precharge voltage selection"
+ * = 0 (0x00) ......0. "Selects internal
+ * reference precharge
+ * voltage"
+ * COMD[0] "Snapshot option"
+ * = 0 (0x00) .......0 "Enable live video output
+ * after snapshot sequence"
+ */
+ { 0x0c, 0x08 },
+
+ /*
+ * 0D COME "Common Control E"
+ * = 161 (0xA1) 10100001
+ * COME[7] "Output average option"
+ * = 1 (0x01) 1....... "Output average of 4 pixels"
+ * COME[6] "Anti-blooming control"
+ * = 0 (0x00) .0...... "Off"
+ * COME[5:3] "Reserved"
+ * = 4 (0x04) ..100...
+ * COME[2] "Clock output power down pin status"
+ * = 0 (0x00) .....0.. "Tri-state data output pin
+ * on power down"
+ * COME[1] "Data output pin status selection at power down"
+ * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK,
+ * HREF, and CHSYNC pins on
+ * power down"
+ * COME[0] "Auto zero circuit select"
+ * = 1 (0x01) .......1 "On"
+ */
+ { 0x0d, 0xa1 },
+
+ /*
+ * 0E COMF "Common Control F"
+ * = 112 (0x70) 01110000
+ * COMF[7] "System clock selection"
+ * = 0 (0x00) 0....... "Use 24 MHz system clock"
+ * COMF[6:4] "Reserved"
+ * = 7 (0x07) .111....
+ * COMF[3] "Manual auto negative offset canceling selection"
+ * = 0 (0x00) ....0... "Auto detect negative
+ * offset and cancel it"
+ * COMF[2:0] "Reserved"
+ * = 0 (0x00) .....000
+ */
+ { 0x0e, 0x70 },
+
+ /*
+ * 0F COMG "Common Control G"
+ * = 66 (0x42) 01000010
+ * COMG[7] "Optical black output selection"
+ * = 0 (0x00) 0....... "Disable"
+ * COMG[6] "Black level calibrate selection"
+ * = 1 (0x01) .1...... "Use optical black pixels
+ * to calibrate"
+ * COMG[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMG[3] "Channel offset adjustment"
+ * = 0 (0x00) ....0... "Disable offset adjustment"
+ * COMG[2] "ADC black level calibration option"
+ * = 0 (0x00) .....0.. "Use B/G line and G/R
+ * line to calibrate each
+ * channel's black level"
+ * COMG[1] "Reserved"
+ * = 1 (0x01) ......1.
+ * COMG[0] "ADC black level calibration enable"
+ * = 0 (0x00) .......0 "Disable"
+ */
+ { 0x0f, 0x42 },
+
+ /*
+ * 14 COMJ "Common Control J"
+ * = 198 (0xC6) 11000110
+ * COMJ[7:6] "AGC gain ceiling"
+ * = 3 (0x03) 11...... "8x"
+ * COMJ[5:4] "Reserved"
+ * = 0 (0x00) ..00....
+ * COMJ[3] "Auto banding filter"
+ * = 0 (0x00) ....0... "Banding filter is always
+ * on off depending on
+ * COMI[5] setting"
+ * COMJ[2] "VSYNC drop option"
+ * = 1 (0x01) .....1.. "SYNC is dropped if frame
+ * data is dropped"
+ * COMJ[1] "Frame data drop"
+ * = 1 (0x01) ......1. "Drop frame data if
+ * exposure is not within
+ * tolerance. In AEC mode,
+ * data is normally dropped
+ * when data is out of
+ * range."
+ * COMJ[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x14, 0xc6 },
+
+ /*
+ * 15 COMK "Common Control K"
+ * = 2 (0x02) 00000010
+ * COMK[7] "CHSYNC pin output swap"
+ * = 0 (0x00) 0....... "CHSYNC"
+ * COMK[6] "HREF pin output swap"
+ * = 0 (0x00) .0...... "HREF"
+ * COMK[5] "PCLK output selection"
+ * = 0 (0x00) ..0..... "PCLK always output"
+ * COMK[4] "PCLK edge selection"
+ * = 0 (0x00) ...0.... "Data valid on falling edge"
+ * COMK[3] "HREF output polarity"
+ * = 0 (0x00) ....0... "positive"
+ * COMK[2] "Reserved"
+ * = 0 (0x00) .....0..
+ * COMK[1] "VSYNC polarity"
+ * = 1 (0x01) ......1. "negative"
+ * COMK[0] "HSYNC polarity"
+ * = 0 (0x00) .......0 "positive"
+ */
+ { 0x15, 0x02 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 36 VCHG "Sensor Precharge Voltage Control"
+ * = 0 (0x00) 00000000
+ * VCHG[7] "Reserved"
+ * = 0 (0x00) 0.......
+ * VCHG[6:4] "Sensor precharge voltage control"
+ * = 0 (0x00) .000....
+ * VCHG[3:0] "Sensor array common reference"
+ * = 0 (0x00) ....0000
+ */
+ { 0x36, 0x00 },
+
+ /*
+ * 37 ADC "ADC Reference Control"
+ * = 4 (0x04) 00000100
+ * ADC[7:4] "Reserved"
+ * = 0 (0x00) 0000....
+ * ADC[3] "ADC input signal range"
+ * = 0 (0x00) ....0... "Input signal 1.0x"
+ * ADC[2:0] "ADC range control"
+ * = 4 (0x04) .....100
+ */
+ { 0x37, 0x04 },
+
+ /*
+ * 38 ACOM "Analog Common Ground"
+ * = 82 (0x52) 01010010
+ * ACOM[7] "Analog gain control"
+ * = 0 (0x00) 0....... "Gain 1x"
+ * ACOM[6] "Analog black level calibration"
+ * = 1 (0x01) .1...... "On"
+ * ACOM[5:0] "Reserved"
+ * = 18 (0x12) ..010010
+ */
+ { 0x38, 0x52 },
+
+ /*
+ * 3A FREFA "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFA[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3a, 0x00 },
+
+ /*
+ * 3C FVOPT "Internal Reference Adjustment"
+ * = 31 (0x1F) 00011111
+ * FVOPT[7:0] "Range"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x3c, 0x1f },
+
+ /*
+ * 44 Undocumented = 0 (0x00) 00000000
+ * 44[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x44, 0x00 },
+
+ /*
+ * 40 Undocumented = 0 (0x00) 00000000
+ * 40[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x40, 0x00 },
+
+ /*
+ * 41 Undocumented = 0 (0x00) 00000000
+ * 41[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x41, 0x00 },
+
+ /*
+ * 42 Undocumented = 0 (0x00) 00000000
+ * 42[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x42, 0x00 },
+
+ /*
+ * 43 Undocumented = 0 (0x00) 00000000
+ * 43[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x43, 0x00 },
+
+ /*
+ * 45 Undocumented = 128 (0x80) 10000000
+ * 45[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x45, 0x80 },
+
+ /*
+ * 48 Undocumented = 192 (0xC0) 11000000
+ * 48[7:0] "It's a secret"
+ * = 192 (0xC0) 11000000
+ */
+ { 0x48, 0xc0 },
+
+ /*
+ * 49 Undocumented = 25 (0x19) 00011001
+ * 49[7:0] "It's a secret"
+ * = 25 (0x19) 00011001
+ */
+ { 0x49, 0x19 },
+
+ /*
+ * 4B Undocumented = 128 (0x80) 10000000
+ * 4B[7:0] "It's a secret"
+ * = 128 (0x80) 10000000
+ */
+ { 0x4b, 0x80 },
+
+ /*
+ * 4D Undocumented = 196 (0xC4) 11000100
+ * 4D[7:0] "It's a secret"
+ * = 196 (0xC4) 11000100
+ */
+ { 0x4d, 0xc4 },
+
+ /*
+ * 35 VREF "Reference Voltage Control"
+ * = 76 (0x4c) 01001100
+ * VREF[7:5] "Column high reference control"
+ * = 2 (0x02) 010..... "higher voltage"
+ * VREF[4:2] "Column low reference control"
+ * = 3 (0x03) ...011.. "Highest voltage"
+ * VREF[1:0] "Reserved"
+ * = 0 (0x00) ......00
+ */
+ { 0x35, 0x4c },
+
+ /*
+ * 3D Undocumented = 0 (0x00) 00000000
+ * 3D[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3d, 0x00 },
+
+ /*
+ * 3E Undocumented = 0 (0x00) 00000000
+ * 3E[7:0] "It's a secret"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3e, 0x00 },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 24 (0x18) 00011000
+ * FREFB[7:0] "Range"
+ * = 24 (0x18) 00011000
+ */
+ { 0x3b, 0x18 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 25 (0x19) 00011001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 1 (0x01) ...1.... "double current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x19 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 90 (0x5A) 01011010
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 10 (0x0A) ....1010
+ */
+ { 0x34, 0x5a },
+
+ /*
+ * 3B FREFB "Internal Reference Adjustment"
+ * = 0 (0x00) 00000000
+ * FREFB[7:0] "Range"
+ * = 0 (0x00) 00000000
+ */
+ { 0x3b, 0x00 },
+
+ /*
+ * 33 CHLF "Current Control"
+ * = 9 (0x09) 00001001
+ * CHLF[7:6] "Sensor current control"
+ * = 0 (0x00) 00......
+ * CHLF[5] "Sensor current range control"
+ * = 0 (0x00) ..0..... "normal range"
+ * CHLF[4] "Sensor current"
+ * = 0 (0x00) ...0.... "normal current"
+ * CHLF[3] "Sensor buffer current control"
+ * = 1 (0x01) ....1... "half current"
+ * CHLF[2] "Column buffer current control"
+ * = 0 (0x00) .....0.. "normal current"
+ * CHLF[1] "Analog DSP current control"
+ * = 0 (0x00) ......0. "normal current"
+ * CHLF[1] "ADC current control"
+ * = 0 (0x00) ......0. "normal current"
+ */
+ { 0x33, 0x09 },
+
+ /*
+ * 34 VBLM "Blooming Control"
+ * = 80 (0x50) 01010000
+ * VBLM[7] "Hard soft reset switch"
+ * = 0 (0x00) 0....... "Hard reset"
+ * VBLM[6:4] "Blooming voltage selection"
+ * = 5 (0x05) .101....
+ * VBLM[3:0] "Sensor current control"
+ * = 0 (0x00) ....0000
+ */
+ { 0x34, 0x50 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1f },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5f },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4a },
+
+ /*
+ * 11 CLKRC "Clock Rate Control"
+ * = 128 (0x80) 10000000
+ * CLKRC[7] "Internal frequency doublers on off seclection"
+ * = 1 (0x01) 1....... "On"
+ * CLKRC[6] "Digital video master slave selection"
+ * = 0 (0x00) .0...... "Master mode, sensor
+ * provides PCLK"
+ * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
+ * = 0 (0x00) ..000000
+ */
+ { 0x11, 0x80 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 0 (0x00) 00000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 0 (0x00) .000.... "QXGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x00 },
+
+ /*
+ * 12 COMH "Common Control H"
+ * = 64 (0x40) 01000000
+ * COMH[7] "SRST"
+ * = 0 (0x00) 0....... "No-op"
+ * COMH[6:4] "Resolution selection"
+ * = 4 (0x04) .100.... "XGA"
+ * COMH[3] "Master slave selection"
+ * = 0 (0x00) ....0... "Master mode"
+ * COMH[2] "Internal B/R channel option"
+ * = 0 (0x00) .....0.. "B/R use same channel"
+ * COMH[1] "Color bar test pattern"
+ * = 0 (0x00) ......0. "Off"
+ * COMH[0] "Reserved"
+ * = 0 (0x00) .......0
+ */
+ { 0x12, 0x40 },
+
+ /*
+ * 17 HREFST "Horizontal window start"
+ * = 31 (0x1F) 00011111
+ * HREFST[7:0] "Horizontal window start, 8 MSBs"
+ * = 31 (0x1F) 00011111
+ */
+ { 0x17, 0x1f },
+
+ /*
+ * 18 HREFEND "Horizontal window end"
+ * = 95 (0x5F) 01011111
+ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
+ * = 95 (0x5F) 01011111
+ */
+ { 0x18, 0x5f },
+
+ /*
+ * 19 VSTRT "Vertical window start"
+ * = 0 (0x00) 00000000
+ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
+ * = 0 (0x00) 00000000
+ */
+ { 0x19, 0x00 },
+
+ /*
+ * 1A VEND "Vertical window end"
+ * = 96 (0x60) 01100000
+ * VEND[7:0] "Vertical Window End, 8 MSBs"
+ * = 96 (0x60) 01100000
+ */
+ { 0x1a, 0x60 },
+
+ /*
+ * 32 COMM "Common Control M"
+ * = 18 (0x12) 00010010
+ * COMM[7:6] "Pixel clock divide option"
+ * = 0 (0x00) 00...... "/1"
+ * COMM[5:3] "Horizontal window end position, 3 LSBs"
+ * = 2 (0x02) ..010...
+ * COMM[2:0] "Horizontal window start position, 3 LSBs"
+ * = 2 (0x02) .....010
+ */
+ { 0x32, 0x12 },
+
+ /*
+ * 03 COMA "Common Control A"
+ * = 74 (0x4A) 01001010
+ * COMA[7:4] "AWB Update Threshold"
+ * = 4 (0x04) 0100....
+ * COMA[3:2] "Vertical window end line control 2 LSBs"
+ * = 2 (0x02) ....10..
+ * COMA[1:0] "Vertical window start line control 2 LSBs"
+ * = 2 (0x02) ......10
+ */
+ { 0x03, 0x4a },
+
+ /*
+ * 02 RED "Red Gain Control"
+ * = 175 (0xAF) 10101111
+ * RED[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * RED[6:0] "Value"
+ * = 47 (0x2F) .0101111
+ */
+ { 0x02, 0xaf },
+
+ /*
+ * 2D ADDVSL "VSYNC Pulse Width"
+ * = 210 (0xD2) 11010010
+ * ADDVSL[7:0] "VSYNC pulse width, LSB"
+ * = 210 (0xD2) 11010010
+ */
+ { 0x2d, 0xd2 },
+
+ /*
+ * 00 GAIN = 24 (0x18) 00011000
+ * GAIN[7:6] "Reserved"
+ * = 0 (0x00) 00......
+ * GAIN[5] "Double"
+ * = 0 (0x00) ..0..... "False"
+ * GAIN[4] "Double"
+ * = 1 (0x01) ...1.... "True"
+ * GAIN[3:0] "Range"
+ * = 8 (0x08) ....1000
+ */
+ { 0x00, 0x18 },
+
+ /*
+ * 01 BLUE "Blue Gain Control"
+ * = 240 (0xF0) 11110000
+ * BLUE[7] "Action"
+ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
+ * BLUE[6:0] "Value"
+ * = 112 (0x70) .1110000
+ */
+ { 0x01, 0xf0 },
+
+ /*
+ * 10 AEC "Automatic Exposure Control"
+ * = 10 (0x0A) 00001010
+ * AEC[7:0] "Automatic Exposure Control, 8 MSBs"
+ * = 10 (0x0A) 00001010
+ */
+ { 0x10, 0x0a },
+
+ { 0xe1, 0x67 },
+ { 0xe3, 0x03 },
+ { 0xe4, 0x26 },
+ { 0xe5, 0x3e },
+ { 0xf8, 0x01 },
+ { 0xff, 0x01 },
+};
+
+static const struct ov_i2c_regvals norm_6x20[] = {
+ { 0x12, 0x80 }, /* reset */
+ { 0x11, 0x01 },
+ { 0x03, 0x60 },
+ { 0x05, 0x7f }, /* For when autoadjust is off */
+ { 0x07, 0xa8 },
+ /* The ratio of 0x0c and 0x0d controls the white point */
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+ { 0x0f, 0x15 }, /* COMS */
+ { 0x10, 0x75 }, /* AEC Exposure time */
+ { 0x12, 0x24 }, /* Enable AGC */
+ { 0x14, 0x04 },
+ /* 0x16: 0x06 helps frame stability with moving objects */
+ { 0x16, 0x06 },
+/* { 0x20, 0x30 }, * Aperture correction enable */
+ { 0x26, 0xb2 }, /* BLC enable */
+ /* 0x28: 0x05 Selects RGB format if RGB on */
+ { 0x28, 0x05 },
+ { 0x2a, 0x04 }, /* Disable framerate adjust */
+/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */
+ { 0x2d, 0x85 },
+ { 0x33, 0xa0 }, /* Color Processing Parameter */
+ { 0x34, 0xd2 }, /* Max A/D range */
+ { 0x38, 0x8b },
+ { 0x39, 0x40 },
+
+ { 0x3c, 0x39 }, /* Enable AEC mode changing */
+ { 0x3c, 0x3c }, /* Change AEC mode */
+ { 0x3c, 0x24 }, /* Disable AEC mode changing */
+
+ { 0x3d, 0x80 },
+ /* These next two registers (0x4a, 0x4b) are undocumented.
+ * They control the color balance */
+ { 0x4a, 0x80 },
+ { 0x4b, 0x80 },
+ { 0x4d, 0xd2 }, /* This reduces noise a bit */
+ { 0x4e, 0xc1 },
+ { 0x4f, 0x04 },
+/* Do 50-53 have any effect? */
+/* Toggle 0x12[2] off and on here? */
+};
+
+static const struct ov_i2c_regvals norm_6x30[] = {
+ { 0x12, 0x80 }, /* Reset */
+ { 0x00, 0x1f }, /* Gain */
+ { 0x01, 0x99 }, /* Blue gain */
+ { 0x02, 0x7c }, /* Red gain */
+ { 0x03, 0xc0 }, /* Saturation */
+ { 0x05, 0x0a }, /* Contrast */
+ { 0x06, 0x95 }, /* Brightness */
+ { 0x07, 0x2d }, /* Sharpness */
+ { 0x0c, 0x20 },
+ { 0x0d, 0x20 },
+ { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */
+ { 0x0f, 0x05 },
+ { 0x10, 0x9a },
+ { 0x11, 0x00 }, /* Pixel clock = fastest */
+ { 0x12, 0x24 }, /* Enable AGC and AWB */
+ { 0x13, 0x21 },
+ { 0x14, 0x80 },
+ { 0x15, 0x01 },
+ { 0x16, 0x03 },
+ { 0x17, 0x38 },
+ { 0x18, 0xea },
+ { 0x19, 0x04 },
+ { 0x1a, 0x93 },
+ { 0x1b, 0x00 },
+ { 0x1e, 0xc4 },
+ { 0x1f, 0x04 },
+ { 0x20, 0x20 },
+ { 0x21, 0x10 },
+ { 0x22, 0x88 },
+ { 0x23, 0xc0 }, /* Crystal circuit power level */
+ { 0x25, 0x9a }, /* Increase AEC black ratio */
+ { 0x26, 0xb2 }, /* BLC enable */
+ { 0x27, 0xa2 },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x84 }, /* 60 Hz power */
+ { 0x2b, 0xa8 }, /* 60 Hz power */
+ { 0x2c, 0xa0 },
+ { 0x2d, 0x95 }, /* Enable auto-brightness */
+ { 0x2e, 0x88 },
+ { 0x33, 0x26 },
+ { 0x34, 0x03 },
+ { 0x36, 0x8f },
+ { 0x37, 0x80 },
+ { 0x38, 0x83 },
+ { 0x39, 0x80 },
+ { 0x3a, 0x0f },
+ { 0x3b, 0x3c },
+ { 0x3c, 0x1a },
+ { 0x3d, 0x80 },
+ { 0x3e, 0x80 },
+ { 0x3f, 0x0e },
+ { 0x40, 0x00 }, /* White bal */
+ { 0x41, 0x00 }, /* White bal */
+ { 0x42, 0x80 },
+ { 0x43, 0x3f }, /* White bal */
+ { 0x44, 0x80 },
+ { 0x45, 0x20 },
+ { 0x46, 0x20 },
+ { 0x47, 0x80 },
+ { 0x48, 0x7f },
+ { 0x49, 0x00 },
+ { 0x4a, 0x00 },
+ { 0x4b, 0x80 },
+ { 0x4c, 0xd0 },
+ { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+ { 0x4e, 0x40 },
+ { 0x4f, 0x07 }, /* UV avg., col. killer: max */
+ { 0x50, 0xff },
+ { 0x54, 0x23 }, /* Max AGC gain: 18dB */
+ { 0x55, 0xff },
+ { 0x56, 0x12 },
+ { 0x57, 0x81 },
+ { 0x58, 0x75 },
+ { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
+ { 0x5a, 0x2c },
+ { 0x5b, 0x0f }, /* AWB chrominance levels */
+ { 0x5c, 0x10 },
+ { 0x3d, 0x80 },
+ { 0x27, 0xa6 },
+ { 0x12, 0x20 }, /* Toggle AWB */
+ { 0x12, 0x24 },
+};
+
+/* Lawrence Glaister <lg@jfm.bc.ca> reports:
+ *
+ * Register 0x0f in the 7610 has the following effects:
+ *
+ * 0x85 (AEC method 1): Best overall, good contrast range
+ * 0x45 (AEC method 2): Very overexposed
+ * 0xa5 (spec sheet default): Ok, but the black level is
+ * shifted resulting in loss of contrast
+ * 0x05 (old driver setting): very overexposed, too much
+ * contrast
+ */
+static const struct ov_i2c_regvals norm_7610[] = {
+ { 0x10, 0xff },
+ { 0x16, 0x06 },
+ { 0x28, 0x24 },
+ { 0x2b, 0xac },
+ { 0x12, 0x00 },
+ { 0x38, 0x81 },
+ { 0x28, 0x24 }, /* 0c */
+ { 0x0f, 0x85 }, /* lg's setting */
+ { 0x15, 0x01 },
+ { 0x20, 0x1c },
+ { 0x23, 0x2a },
+ { 0x24, 0x10 },
+ { 0x25, 0x8a },
+ { 0x26, 0xa2 },
+ { 0x27, 0xc2 },
+ { 0x2a, 0x04 },
+ { 0x2c, 0xfe },
+ { 0x2d, 0x93 },
+ { 0x30, 0x71 },
+ { 0x31, 0x60 },
+ { 0x32, 0x26 },
+ { 0x33, 0x20 },
+ { 0x34, 0x48 },
+ { 0x12, 0x24 },
+ { 0x11, 0x01 },
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+};
+
+static const struct ov_i2c_regvals norm_7620[] = {
+ { 0x12, 0x80 }, /* reset */
+ { 0x00, 0x00 }, /* gain */
+ { 0x01, 0x80 }, /* blue gain */
+ { 0x02, 0x80 }, /* red gain */
+ { 0x03, 0xc0 }, /* OV7670_R03_VREF */
+ { 0x06, 0x60 },
+ { 0x07, 0x00 },
+ { 0x0c, 0x24 },
+ { 0x0c, 0x24 },
+ { 0x0d, 0x24 },
+ { 0x11, 0x01 },
+ { 0x12, 0x24 },
+ { 0x13, 0x01 },
+ { 0x14, 0x84 },
+ { 0x15, 0x01 },
+ { 0x16, 0x03 },
+ { 0x17, 0x2f },
+ { 0x18, 0xcf },
+ { 0x19, 0x06 },
+ { 0x1a, 0xf5 },
+ { 0x1b, 0x00 },
+ { 0x20, 0x18 },
+ { 0x21, 0x80 },
+ { 0x22, 0x80 },
+ { 0x23, 0x00 },
+ { 0x26, 0xa2 },
+ { 0x27, 0xea },
+ { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */
+ { 0x29, 0x00 },
+ { 0x2a, 0x10 },
+ { 0x2b, 0x00 },
+ { 0x2c, 0x88 },
+ { 0x2d, 0x91 },
+ { 0x2e, 0x80 },
+ { 0x2f, 0x44 },
+ { 0x60, 0x27 },
+ { 0x61, 0x02 },
+ { 0x62, 0x5f },
+ { 0x63, 0xd5 },
+ { 0x64, 0x57 },
+ { 0x65, 0x83 },
+ { 0x66, 0x55 },
+ { 0x67, 0x92 },
+ { 0x68, 0xcf },
+ { 0x69, 0x76 },
+ { 0x6a, 0x22 },
+ { 0x6b, 0x00 },
+ { 0x6c, 0x02 },
+ { 0x6d, 0x44 },
+ { 0x6e, 0x80 },
+ { 0x6f, 0x1d },
+ { 0x70, 0x8b },
+ { 0x71, 0x00 },
+ { 0x72, 0x14 },
+ { 0x73, 0x54 },
+ { 0x74, 0x00 },
+ { 0x75, 0x8e },
+ { 0x76, 0x00 },
+ { 0x77, 0xff },
+ { 0x78, 0x80 },
+ { 0x79, 0x80 },
+ { 0x7a, 0x80 },
+ { 0x7b, 0xe2 },
+ { 0x7c, 0x00 },
+};
+
+/* 7640 and 7648. The defaults should be OK for most registers. */
+static const struct ov_i2c_regvals norm_7640[] = {
+ { 0x12, 0x80 },
+ { 0x12, 0x14 },
+};
+
+static const struct ov_regvals init_519_ov7660[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */
+ { 0x54, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x20, 0x0c }, /* 0x0d does U <-> V swap */
+ { 0x21, 0x38 },
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+};
+static const struct ov_i2c_regvals norm_7660[] = {
+ {OV7670_R12_COM7, OV7670_COM7_RESET},
+ {OV7670_R11_CLKRC, 0x81},
+ {0x92, 0x00}, /* DM_LNL */
+ {0x93, 0x00}, /* DM_LNH */
+ {0x9d, 0x4c}, /* BD50ST */
+ {0x9e, 0x3f}, /* BD60ST */
+ {OV7670_R3B_COM11, 0x02},
+ {OV7670_R13_COM8, 0xf5},
+ {OV7670_R10_AECH, 0x00},
+ {OV7670_R00_GAIN, 0x00},
+ {OV7670_R01_BLUE, 0x7c},
+ {OV7670_R02_RED, 0x9d},
+ {OV7670_R12_COM7, 0x00},
+ {OV7670_R04_COM1, 00},
+ {OV7670_R18_HSTOP, 0x01},
+ {OV7670_R17_HSTART, 0x13},
+ {OV7670_R32_HREF, 0x92},
+ {OV7670_R19_VSTART, 0x02},
+ {OV7670_R1A_VSTOP, 0x7a},
+ {OV7670_R03_VREF, 0x00},
+ {OV7670_R0E_COM5, 0x04},
+ {OV7670_R0F_COM6, 0x62},
+ {OV7670_R15_COM10, 0x00},
+ {0x16, 0x02}, /* RSVD */
+ {0x1b, 0x00}, /* PSHFT */
+ {OV7670_R1E_MVFP, 0x01},
+ {0x29, 0x3c}, /* RSVD */
+ {0x33, 0x00}, /* CHLF */
+ {0x34, 0x07}, /* ARBLM */
+ {0x35, 0x84}, /* RSVD */
+ {0x36, 0x00}, /* RSVD */
+ {0x37, 0x04}, /* ADC */
+ {0x39, 0x43}, /* OFON */
+ {OV7670_R3A_TSLB, 0x00},
+ {OV7670_R3C_COM12, 0x6c},
+ {OV7670_R3D_COM13, 0x98},
+ {OV7670_R3F_EDGE, 0x23},
+ {OV7670_R40_COM15, 0xc1},
+ {OV7670_R41_COM16, 0x22},
+ {0x6b, 0x0a}, /* DBLV */
+ {0xa1, 0x08}, /* RSVD */
+ {0x69, 0x80}, /* HV */
+ {0x43, 0xf0}, /* RSVD.. */
+ {0x44, 0x10},
+ {0x45, 0x78},
+ {0x46, 0xa8},
+ {0x47, 0x60},
+ {0x48, 0x80},
+ {0x59, 0xba},
+ {0x5a, 0x9a},
+ {0x5b, 0x22},
+ {0x5c, 0xb9},
+ {0x5d, 0x9b},
+ {0x5e, 0x10},
+ {0x5f, 0xe0},
+ {0x60, 0x85},
+ {0x61, 0x60},
+ {0x9f, 0x9d}, /* RSVD */
+ {0xa0, 0xa0}, /* DSPC2 */
+ {0x4f, 0x60}, /* matrix */
+ {0x50, 0x64},
+ {0x51, 0x04},
+ {0x52, 0x18},
+ {0x53, 0x3c},
+ {0x54, 0x54},
+ {0x55, 0x40},
+ {0x56, 0x40},
+ {0x57, 0x40},
+ {0x58, 0x0d}, /* matrix sign */
+ {0x8b, 0xcc}, /* RSVD */
+ {0x8c, 0xcc},
+ {0x8d, 0xcf},
+ {0x6c, 0x40}, /* gamma curve */
+ {0x6d, 0xe0},
+ {0x6e, 0xa0},
+ {0x6f, 0x80},
+ {0x70, 0x70},
+ {0x71, 0x80},
+ {0x72, 0x60},
+ {0x73, 0x60},
+ {0x74, 0x50},
+ {0x75, 0x40},
+ {0x76, 0x38},
+ {0x77, 0x3c},
+ {0x78, 0x32},
+ {0x79, 0x1a},
+ {0x7a, 0x28},
+ {0x7b, 0x24},
+ {0x7c, 0x04}, /* gamma curve */
+ {0x7d, 0x12},
+ {0x7e, 0x26},
+ {0x7f, 0x46},
+ {0x80, 0x54},
+ {0x81, 0x64},
+ {0x82, 0x70},
+ {0x83, 0x7c},
+ {0x84, 0x86},
+ {0x85, 0x8e},
+ {0x86, 0x9c},
+ {0x87, 0xab},
+ {0x88, 0xc4},
+ {0x89, 0xd1},
+ {0x8a, 0xe5},
+ {OV7670_R14_COM9, 0x1e},
+ {OV7670_R24_AEW, 0x80},
+ {OV7670_R25_AEB, 0x72},
+ {OV7670_R26_VPT, 0xb3},
+ {0x62, 0x80}, /* LCC1 */
+ {0x63, 0x80}, /* LCC2 */
+ {0x64, 0x06}, /* LCC3 */
+ {0x65, 0x00}, /* LCC4 */
+ {0x66, 0x01}, /* LCC5 */
+ {0x94, 0x0e}, /* RSVD.. */
+ {0x95, 0x14},
+ {OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | 0x10
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AWB
+ | OV7670_COM8_AEC},
+ {0xa1, 0xc8}
+};
+static const struct ov_i2c_regvals norm_9600[] = {
+ {0x12, 0x80},
+ {0x0c, 0x28},
+ {0x11, 0x80},
+ {0x13, 0xb5},
+ {0x14, 0x3e},
+ {0x1b, 0x04},
+ {0x24, 0xb0},
+ {0x25, 0x90},
+ {0x26, 0x94},
+ {0x35, 0x90},
+ {0x37, 0x07},
+ {0x38, 0x08},
+ {0x01, 0x8e},
+ {0x02, 0x85}
+};
+
+/* 7670. Defaults taken from OmniVision provided data,
+* as provided by Jonathan Corbet of OLPC */
+static const struct ov_i2c_regvals norm_7670[] = {
+ { OV7670_R12_COM7, OV7670_COM7_RESET },
+ { OV7670_R3A_TSLB, 0x04 }, /* OV */
+ { OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
+ { OV7670_R11_CLKRC, 0x01 },
+/*
+ * Set the hardware window. These values from OV don't entirely
+ * make sense - hstop is less than hstart. But they work...
+ */
+ { OV7670_R17_HSTART, 0x13 },
+ { OV7670_R18_HSTOP, 0x01 },
+ { OV7670_R32_HREF, 0xb6 },
+ { OV7670_R19_VSTART, 0x02 },
+ { OV7670_R1A_VSTOP, 0x7a },
+ { OV7670_R03_VREF, 0x0a },
+
+ { OV7670_R0C_COM3, 0x00 },
+ { OV7670_R3E_COM14, 0x00 },
+/* Mystery scaling numbers */
+ { 0x70, 0x3a },
+ { 0x71, 0x35 },
+ { 0x72, 0x11 },
+ { 0x73, 0xf0 },
+ { 0xa2, 0x02 },
+/* { OV7670_R15_COM10, 0x0 }, */
+
+/* Gamma curve values */
+ { 0x7a, 0x20 },
+ { 0x7b, 0x10 },
+ { 0x7c, 0x1e },
+ { 0x7d, 0x35 },
+ { 0x7e, 0x5a },
+ { 0x7f, 0x69 },
+ { 0x80, 0x76 },
+ { 0x81, 0x80 },
+ { 0x82, 0x88 },
+ { 0x83, 0x8f },
+ { 0x84, 0x96 },
+ { 0x85, 0xa3 },
+ { 0x86, 0xaf },
+ { 0x87, 0xc4 },
+ { 0x88, 0xd7 },
+ { 0x89, 0xe8 },
+
+/* AGC and AEC parameters. Note we start by disabling those features,
+ then turn them only after tweaking the values. */
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT },
+ { OV7670_R00_GAIN, 0x00 },
+ { OV7670_R10_AECH, 0x00 },
+ { OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */
+ { OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
+ { OV7670_RA5_BD50MAX, 0x05 },
+ { OV7670_RAB_BD60MAX, 0x07 },
+ { OV7670_R24_AEW, 0x95 },
+ { OV7670_R25_AEB, 0x33 },
+ { OV7670_R26_VPT, 0xe3 },
+ { OV7670_R9F_HAECC1, 0x78 },
+ { OV7670_RA0_HAECC2, 0x68 },
+ { 0xa1, 0x03 }, /* magic */
+ { OV7670_RA6_HAECC3, 0xd8 },
+ { OV7670_RA7_HAECC4, 0xd8 },
+ { OV7670_RA8_HAECC5, 0xf0 },
+ { OV7670_RA9_HAECC6, 0x90 },
+ { OV7670_RAA_HAECC7, 0x94 },
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AEC },
+
+/* Almost all of these are magic "reserved" values. */
+ { OV7670_R0E_COM5, 0x61 },
+ { OV7670_R0F_COM6, 0x4b },
+ { 0x16, 0x02 },
+ { OV7670_R1E_MVFP, 0x07 },
+ { 0x21, 0x02 },
+ { 0x22, 0x91 },
+ { 0x29, 0x07 },
+ { 0x33, 0x0b },
+ { 0x35, 0x0b },
+ { 0x37, 0x1d },
+ { 0x38, 0x71 },
+ { 0x39, 0x2a },
+ { OV7670_R3C_COM12, 0x78 },
+ { 0x4d, 0x40 },
+ { 0x4e, 0x20 },
+ { OV7670_R69_GFIX, 0x00 },
+ { 0x6b, 0x4a },
+ { 0x74, 0x10 },
+ { 0x8d, 0x4f },
+ { 0x8e, 0x00 },
+ { 0x8f, 0x00 },
+ { 0x90, 0x00 },
+ { 0x91, 0x00 },
+ { 0x96, 0x00 },
+ { 0x9a, 0x00 },
+ { 0xb0, 0x84 },
+ { 0xb1, 0x0c },
+ { 0xb2, 0x0e },
+ { 0xb3, 0x82 },
+ { 0xb8, 0x0a },
+
+/* More reserved magic, some of which tweaks white balance */
+ { 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 }, /* "9e for advance AWB" */
+ { 0x6a, 0x40 },
+ { OV7670_R01_BLUE, 0x40 },
+ { OV7670_R02_RED, 0x60 },
+ { OV7670_R13_COM8, OV7670_COM8_FASTAEC
+ | OV7670_COM8_AECSTEP
+ | OV7670_COM8_BFILT
+ | OV7670_COM8_AGC
+ | OV7670_COM8_AEC
+ | OV7670_COM8_AWB },
+
+/* Matrix coefficients */
+ { 0x4f, 0x80 },
+ { 0x50, 0x80 },
+ { 0x51, 0x00 },
+ { 0x52, 0x22 },
+ { 0x53, 0x5e },
+ { 0x54, 0x80 },
+ { 0x58, 0x9e },
+
+ { OV7670_R41_COM16, OV7670_COM16_AWBGAIN },
+ { OV7670_R3F_EDGE, 0x00 },
+ { 0x75, 0x05 },
+ { 0x76, 0xe1 },
+ { 0x4c, 0x00 },
+ { 0x77, 0x01 },
+ { OV7670_R3D_COM13, OV7670_COM13_GAMMA
+ | OV7670_COM13_UVSAT
+ | 2}, /* was 3 */
+ { 0x4b, 0x09 },
+ { 0xc9, 0x60 },
+ { OV7670_R41_COM16, 0x38 },
+ { 0x56, 0x40 },
+
+ { 0x34, 0x11 },
+ { OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
+ { 0xa4, 0x88 },
+ { 0x96, 0x00 },
+ { 0x97, 0x30 },
+ { 0x98, 0x20 },
+ { 0x99, 0x30 },
+ { 0x9a, 0x84 },
+ { 0x9b, 0x29 },
+ { 0x9c, 0x03 },
+ { 0x9d, 0x4c },
+ { 0x9e, 0x3f },
+ { 0x78, 0x04 },
+
+/* Extra-weird stuff. Some sort of multiplexor register */
+ { 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, 0x40 },
+ { 0x79, 0x05 },
+ { 0xc8, 0x30 },
+ { 0x79, 0x26 },
+};
+
+static const struct ov_i2c_regvals norm_8610[] = {
+ { 0x12, 0x80 },
+ { 0x00, 0x00 },
+ { 0x01, 0x80 },
+ { 0x02, 0x80 },
+ { 0x03, 0xc0 },
+ { 0x04, 0x30 },
+ { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
+ { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
+ { 0x0a, 0x86 },
+ { 0x0b, 0xb0 },
+ { 0x0c, 0x20 },
+ { 0x0d, 0x20 },
+ { 0x11, 0x01 },
+ { 0x12, 0x25 },
+ { 0x13, 0x01 },
+ { 0x14, 0x04 },
+ { 0x15, 0x01 }, /* Lin and Win think different about UV order */
+ { 0x16, 0x03 },
+ { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
+ { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
+ { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
+ { 0x1a, 0xf5 },
+ { 0x1b, 0x00 },
+ { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
+ { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
+ { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
+ { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
+ { 0x26, 0xa2 },
+ { 0x27, 0xea },
+ { 0x28, 0x00 },
+ { 0x29, 0x00 },
+ { 0x2a, 0x80 },
+ { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
+ { 0x2c, 0xac },
+ { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
+ { 0x2e, 0x80 },
+ { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
+ { 0x4c, 0x00 },
+ { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
+ { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
+ { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
+ { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
+ { 0x63, 0xff },
+ { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
+ * maybe thats wrong */
+ { 0x65, 0x00 },
+ { 0x66, 0x55 },
+ { 0x67, 0xb0 },
+ { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
+ { 0x69, 0x02 },
+ { 0x6a, 0x22 },
+ { 0x6b, 0x00 },
+ { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
+ * deleting bit7 colors the first images red */
+ { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
+ { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
+ { 0x6f, 0x01 },
+ { 0x70, 0x8b },
+ { 0x71, 0x00 },
+ { 0x72, 0x14 },
+ { 0x73, 0x54 },
+ { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
+ { 0x75, 0x0e },
+ { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
+ { 0x77, 0xff },
+ { 0x78, 0x80 },
+ { 0x79, 0x80 },
+ { 0x7a, 0x80 },
+ { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
+ { 0x7c, 0x00 },
+ { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
+ { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
+ { 0x7f, 0xfb },
+ { 0x80, 0x28 },
+ { 0x81, 0x00 },
+ { 0x82, 0x23 },
+ { 0x83, 0x0b },
+ { 0x84, 0x00 },
+ { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
+ { 0x86, 0xc9 },
+ { 0x87, 0x00 },
+ { 0x88, 0x00 },
+ { 0x89, 0x01 },
+ { 0x12, 0x20 },
+ { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
+};
+
+static unsigned char ov7670_abs_to_sm(unsigned char v)
+{
+ if (v > 127)
+ return v & 0x7f;
+ return (128 - v) | 0x80;
+}
+
+/* Write a OV519 register */
+static void reg_w(struct sd *sd, u16 index, u16 value)
+{
+ int ret, req = 0;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 2;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0a;
+ /* fall through */
+ case BRIDGE_W9968CF:
+ PDEBUG(D_USBO, "SET %02x %04x %04x",
+ req, value, index);
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ goto leave;
+ default:
+ req = 1;
+ }
+
+ PDEBUG(D_USBO, "SET %02x 0000 %04x %02x",
+ req, index, value);
+ sd->gspca_dev.usb_buf[0] = value;
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, 1, 500);
+leave:
+ if (ret < 0) {
+ pr_err("reg_w %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ return;
+ }
+}
+
+/* Read from a OV519 register, note not valid for the w9968cf!! */
+/* returns: negative is error, pos or zero is data */
+static int reg_r(struct sd *sd, u16 index)
+{
+ int ret;
+ int req;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ req = 3;
+ break;
+ case BRIDGE_OVFX2:
+ req = 0x0b;
+ break;
+ default:
+ req = 1;
+ }
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, sd->gspca_dev.usb_buf, 1, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
+ req, index, ret);
+ } else {
+ pr_err("reg_r %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+/* Read 8 values from a OV519 register */
+static int reg_r8(struct sd *sd,
+ u16 index)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 1, /* REQ_IO */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, sd->gspca_dev.usb_buf, 8, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ } else {
+ pr_err("reg_r8 %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static void reg_w_mask(struct sd *sd,
+ u16 index,
+ u8 value,
+ u8 mask)
+{
+ int ret;
+ u8 oldval;
+
+ if (mask != 0xff) {
+ value &= mask; /* Enforce mask on value */
+ ret = reg_r(sd, index);
+ if (ret < 0)
+ return;
+
+ oldval = ret & ~mask; /* Clear the masked bits */
+ value |= oldval; /* Set the desired bits */
+ }
+ reg_w(sd, index, value);
+}
+
+/*
+ * Writes multiple (n) byte value to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce).
+ */
+static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 1 /* REG_IO */,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, n, 500);
+ if (ret < 0) {
+ pr_err("reg_w32 %02x failed %d\n", index, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+}
+
+static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ int rc, retries;
+
+ PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value);
+
+ /* Three byte write cycle */
+ for (retries = 6; ; ) {
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+ /* Write "value" to I2C data port of OV511 */
+ reg_w(sd, R51x_I2C_DATA, value);
+
+ /* Initiate 3-byte write cycle */
+ reg_w(sd, R511_I2C_CTL, 0x01);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+ if (--retries < 0) {
+ PDEBUG(D_USBO, "i2c write retries exhausted");
+ return;
+ }
+ }
+}
+
+static int ov511_i2c_r(struct sd *sd, u8 reg)
+{
+ int rc, value, retries;
+
+ /* Two byte write cycle */
+ for (retries = 6; ; ) {
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+ /* Initiate 2-byte write cycle */
+ reg_w(sd, R511_I2C_CTL, 0x03);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(sd, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ PDEBUG(D_USBI, "i2c write retries exhausted");
+ return -1;
+ }
+ }
+
+ /* Two byte read cycle */
+ for (retries = 6; ; ) {
+ /* Initiate 2-byte read cycle */
+ reg_w(sd, R511_I2C_CTL, 0x05);
+
+ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
+
+ /* I2C abort */
+ reg_w(sd, R511_I2C_CTL, 0x10);
+
+ if (--retries < 0) {
+ PDEBUG(D_USBI, "i2c read retries exhausted");
+ return -1;
+ }
+ }
+
+ value = reg_r(sd, R51x_I2C_DATA);
+
+ PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value);
+
+ /* This is needed to make i2c_w() work */
+ reg_w(sd, R511_I2C_CTL, 0x05);
+
+ return value;
+}
+
+/*
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_w(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static void ov518_i2c_w(struct sd *sd,
+ u8 reg,
+ u8 value)
+{
+ PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value);
+
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_3, reg);
+
+ /* Write "value" to I2C data port of OV511 */
+ reg_w(sd, R51x_I2C_DATA, value);
+
+ /* Initiate 3-byte write cycle */
+ reg_w(sd, R518_I2C_CTL, 0x01);
+
+ /* wait for write complete */
+ msleep(4);
+ reg_r8(sd, R518_I2C_CTL);
+}
+
+/*
+ * returns: negative is error, pos or zero is data
+ *
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from i2c_r(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int ov518_i2c_r(struct sd *sd, u8 reg)
+{
+ int value;
+
+ /* Select camera register */
+ reg_w(sd, R51x_I2C_SADDR_2, reg);
+
+ /* Initiate 2-byte write cycle */
+ reg_w(sd, R518_I2C_CTL, 0x03);
+ reg_r8(sd, R518_I2C_CTL);
+
+ /* Initiate 2-byte read cycle */
+ reg_w(sd, R518_I2C_CTL, 0x05);
+ reg_r8(sd, R518_I2C_CTL);
+
+ value = reg_r(sd, R51x_I2C_DATA);
+ PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
+ return value;
+}
+
+static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ (u16) value, (u16) reg, NULL, 0, 500);
+
+ if (ret < 0) {
+ pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value);
+}
+
+static int ovfx2_i2c_r(struct sd *sd, u8 reg)
+{
+ int ret;
+
+ if (sd->gspca_dev.usb_err < 0)
+ return -1;
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+ 0x03,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500);
+
+ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+ PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
+ } else {
+ pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret);
+ sd->gspca_dev.usb_err = ret;
+ }
+
+ return ret;
+}
+
+static void i2c_w(struct sd *sd, u8 reg, u8 value)
+{
+ if (sd->sensor_reg_cache[reg] == value)
+ return;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+ ov518_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_i2c_w(sd, reg, value);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_i2c_w(sd, reg, value);
+ break;
+ }
+
+ if (sd->gspca_dev.usb_err >= 0) {
+ /* Up on sensor reset empty the register cache */
+ if (reg == 0x12 && (value & 0x80))
+ memset(sd->sensor_reg_cache, -1,
+ sizeof(sd->sensor_reg_cache));
+ else
+ sd->sensor_reg_cache[reg] = value;
+ }
+}
+
+static int i2c_r(struct sd *sd, u8 reg)
+{
+ int ret = -1;
+
+ if (sd->sensor_reg_cache[reg] != -1)
+ return sd->sensor_reg_cache[reg];
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ret = ov511_i2c_r(sd, reg);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+ ret = ov518_i2c_r(sd, reg);
+ break;
+ case BRIDGE_OVFX2:
+ ret = ovfx2_i2c_r(sd, reg);
+ break;
+ case BRIDGE_W9968CF:
+ ret = w9968cf_i2c_r(sd, reg);
+ break;
+ }
+
+ if (ret >= 0)
+ sd->sensor_reg_cache[reg] = ret;
+
+ return ret;
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless
+ * of their respective state in "value".
+ */
+static void i2c_w_mask(struct sd *sd,
+ u8 reg,
+ u8 value,
+ u8 mask)
+{
+ int rc;
+ u8 oldval;
+
+ value &= mask; /* Enforce mask on value */
+ rc = i2c_r(sd, reg);
+ if (rc < 0)
+ return;
+ oldval = rc & ~mask; /* Clear the masked bits */
+ value |= oldval; /* Set the desired bits */
+ i2c_w(sd, reg, value);
+}
+
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline void ov51x_stop(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "stopping");
+ sd->stopped = 1;
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_RESET, 0x3d);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, OV519_R51_RESET1, 0x0f);
+ reg_w(sd, OV519_R51_RESET1, 0x00);
+ reg_w(sd, 0x22, 0x00); /* FRAR */
+ break;
+ case BRIDGE_OVFX2:
+ reg_w_mask(sd, 0x0f, 0x00, 0x02);
+ break;
+ case BRIDGE_W9968CF:
+ reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
+ break;
+ }
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline void ov51x_restart(struct sd *sd)
+{
+ PDEBUG(D_STREAM, "restarting");
+ if (!sd->stopped)
+ return;
+ sd->stopped = 0;
+
+ /* Reinitialize the stream */
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w(sd, 0x2f, 0x80);
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, OV519_R51_RESET1, 0x0f);
+ reg_w(sd, OV519_R51_RESET1, 0x00);
+ reg_w(sd, 0x22, 0x1d); /* FRAR */
+ break;
+ case BRIDGE_OVFX2:
+ reg_w_mask(sd, 0x0f, 0x02, 0x02);
+ break;
+ case BRIDGE_W9968CF:
+ reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
+ break;
+ }
+}
+
+static void ov51x_set_slave_ids(struct sd *sd, u8 slave);
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 on failure.
+ */
+static int init_ov_sensor(struct sd *sd, u8 slave)
+{
+ int i;
+
+ ov51x_set_slave_ids(sd, slave);
+
+ /* Reset the sensor */
+ i2c_w(sd, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ for (i = 0; i < i2c_detect_tries; i++) {
+ if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
+ i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
+ PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
+ return 0;
+ }
+
+ /* Reset the sensor */
+ i2c_w(sd, 0x12, 0x80);
+
+ /* Wait for it to initialize */
+ msleep(150);
+
+ /* Dummy read to sync I2C */
+ if (i2c_r(sd, 0x00) < 0)
+ return -1;
+ }
+ return -1;
+}
+
+/* Set the read and write slave IDs. The "slave" argument is the write slave,
+ * and the read slave will be set to (slave + 1).
+ * This should not be called from outside the i2c I/O functions.
+ * Sets I2C read and write slave IDs. Returns <0 for error
+ */
+static void ov51x_set_slave_ids(struct sd *sd,
+ u8 slave)
+{
+ switch (sd->bridge) {
+ case BRIDGE_OVFX2:
+ reg_w(sd, OVFX2_I2C_ADDR, slave);
+ return;
+ case BRIDGE_W9968CF:
+ sd->sensor_addr = slave;
+ return;
+ }
+
+ reg_w(sd, R51x_I2C_W_SID, slave);
+ reg_w(sd, R51x_I2C_R_SID, slave + 1);
+}
+
+static void write_regvals(struct sd *sd,
+ const struct ov_regvals *regvals,
+ int n)
+{
+ while (--n >= 0) {
+ reg_w(sd, regvals->reg, regvals->val);
+ regvals++;
+ }
+}
+
+static void write_i2c_regvals(struct sd *sd,
+ const struct ov_i2c_regvals *regvals,
+ int n)
+{
+ while (--n >= 0) {
+ i2c_w(sd, regvals->reg, regvals->val);
+ regvals++;
+ }
+}
+
+/****************************************************************************
+ *
+ * OV511 and sensor configuration
+ *
+ ***************************************************************************/
+
+/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
+static void ov_hires_configure(struct sd *sd)
+{
+ int high, low;
+
+ if (sd->bridge != BRIDGE_OVFX2) {
+ pr_err("error hires sensors only supported with ovfx2\n");
+ return;
+ }
+
+ PDEBUG(D_PROBE, "starting ov hires configuration");
+
+ /* Detect sensor (sub)type */
+ high = i2c_r(sd, 0x0a);
+ low = i2c_r(sd, 0x0b);
+ /* info("%x, %x", high, low); */
+ switch (high) {
+ case 0x96:
+ switch (low) {
+ case 0x40:
+ PDEBUG(D_PROBE, "Sensor is a OV2610");
+ sd->sensor = SEN_OV2610;
+ return;
+ case 0x41:
+ PDEBUG(D_PROBE, "Sensor is a OV2610AE");
+ sd->sensor = SEN_OV2610AE;
+ return;
+ case 0xb1:
+ PDEBUG(D_PROBE, "Sensor is a OV9600");
+ sd->sensor = SEN_OV9600;
+ return;
+ }
+ break;
+ case 0x36:
+ if ((low & 0x0f) == 0x00) {
+ PDEBUG(D_PROBE, "Sensor is a OV3610");
+ sd->sensor = SEN_OV3610;
+ return;
+ }
+ break;
+ }
+ pr_err("Error unknown sensor type: %02x%02x\n", high, low);
+}
+
+/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
+ * the same register settings as the OV8610, since they are very similar.
+ */
+static void ov8xx0_configure(struct sd *sd)
+{
+ int rc;
+
+ PDEBUG(D_PROBE, "starting ov8xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+ if (rc < 0) {
+ PDEBUG(D_ERR, "Error detecting sensor type");
+ return;
+ }
+ if ((rc & 3) == 1)
+ sd->sensor = SEN_OV8610;
+ else
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
+}
+
+/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static void ov7xx0_configure(struct sd *sd)
+{
+ int rc, high, low;
+
+ PDEBUG(D_PROBE, "starting OV7xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+
+ /* add OV7670 here
+ * it appears to be wrongly detected as a 7610 by default */
+ if (rc < 0) {
+ pr_err("Error detecting sensor type\n");
+ return;
+ }
+ if ((rc & 3) == 3) {
+ /* quick hack to make OV7670s work */
+ high = i2c_r(sd, 0x0a);
+ low = i2c_r(sd, 0x0b);
+ /* info("%x, %x", high, low); */
+ if (high == 0x76 && (low & 0xf0) == 0x70) {
+ PDEBUG(D_PROBE, "Sensor is an OV76%02x", low);
+ sd->sensor = SEN_OV7670;
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV7610");
+ sd->sensor = SEN_OV7610;
+ }
+ } else if ((rc & 3) == 1) {
+ /* I don't know what's different about the 76BE yet. */
+ if (i2c_r(sd, 0x15) & 1) {
+ PDEBUG(D_PROBE, "Sensor is an OV7620AE");
+ sd->sensor = SEN_OV7620AE;
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV76BE");
+ sd->sensor = SEN_OV76BE;
+ }
+ } else if ((rc & 3) == 0) {
+ /* try to read product id registers */
+ high = i2c_r(sd, 0x0a);
+ if (high < 0) {
+ pr_err("Error detecting camera chip PID\n");
+ return;
+ }
+ low = i2c_r(sd, 0x0b);
+ if (low < 0) {
+ pr_err("Error detecting camera chip VER\n");
+ return;
+ }
+ if (high == 0x76) {
+ switch (low) {
+ case 0x30:
+ pr_err("Sensor is an OV7630/OV7635\n");
+ pr_err("7630 is not supported by this driver\n");
+ return;
+ case 0x40:
+ PDEBUG(D_PROBE, "Sensor is an OV7645");
+ sd->sensor = SEN_OV7640; /* FIXME */
+ break;
+ case 0x45:
+ PDEBUG(D_PROBE, "Sensor is an OV7645B");
+ sd->sensor = SEN_OV7640; /* FIXME */
+ break;
+ case 0x48:
+ PDEBUG(D_PROBE, "Sensor is an OV7648");
+ sd->sensor = SEN_OV7648;
+ break;
+ case 0x60:
+ PDEBUG(D_PROBE, "Sensor is a OV7660");
+ sd->sensor = SEN_OV7660;
+ break;
+ default:
+ pr_err("Unknown sensor: 0x76%02x\n", low);
+ return;
+ }
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV7620");
+ sd->sensor = SEN_OV7620;
+ }
+ } else {
+ pr_err("Unknown image sensor version: %d\n", rc & 3);
+ }
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static void ov6xx0_configure(struct sd *sd)
+{
+ int rc;
+ PDEBUG(D_PROBE, "starting OV6xx0 configuration");
+
+ /* Detect sensor (sub)type */
+ rc = i2c_r(sd, OV7610_REG_COM_I);
+ if (rc < 0) {
+ pr_err("Error detecting sensor type\n");
+ return;
+ }
+
+ /* Ugh. The first two bits are the version bits, but
+ * the entire register value must be used. I guess OVT
+ * underestimated how many variants they would make. */
+ switch (rc) {
+ case 0x00:
+ sd->sensor = SEN_OV6630;
+ pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
+ break;
+ case 0x01:
+ sd->sensor = SEN_OV6620;
+ PDEBUG(D_PROBE, "Sensor is an OV6620");
+ break;
+ case 0x02:
+ sd->sensor = SEN_OV6630;
+ PDEBUG(D_PROBE, "Sensor is an OV66308AE");
+ break;
+ case 0x03:
+ sd->sensor = SEN_OV66308AF;
+ PDEBUG(D_PROBE, "Sensor is an OV66308AF");
+ break;
+ case 0x90:
+ sd->sensor = SEN_OV6630;
+ pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
+ break;
+ default:
+ pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc);
+ return;
+ }
+
+ /* Set sensor-specific vars */
+ sd->sif = 1;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
+static void ov51x_led_control(struct sd *sd, int on)
+{
+ if (sd->invert_led)
+ on = !on;
+
+ switch (sd->bridge) {
+ /* OV511 has no LED control */
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R511_SYS_LED_CTL, on);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02);
+ break;
+ case BRIDGE_OV519:
+ reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1);
+ break;
+ }
+}
+
+static void sd_reset_snapshot(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->snapshot_needs_reset)
+ return;
+
+ /* Note it is important that we clear sd->snapshot_needs_reset,
+ before actually clearing the snapshot state in the bridge
+ otherwise we might race with the pkt_scan interrupt handler */
+ sd->snapshot_needs_reset = 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02);
+ reg_w(sd, R51x_SYS_SNAP, 0x00);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */
+ reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */
+ break;
+ case BRIDGE_OV519:
+ reg_w(sd, R51x_SYS_RESET, 0x40);
+ reg_w(sd, R51x_SYS_RESET, 0x00);
+ break;
+ }
+}
+
+static void ov51x_upload_quan_tables(struct sd *sd)
+{
+ const unsigned char yQuanTable511[] = {
+ 0, 1, 1, 2, 2, 3, 3, 4,
+ 1, 1, 1, 2, 2, 3, 4, 4,
+ 1, 1, 2, 2, 3, 4, 4, 4,
+ 2, 2, 2, 3, 4, 4, 4, 4,
+ 2, 2, 3, 4, 4, 5, 5, 5,
+ 3, 3, 4, 4, 5, 5, 5, 5,
+ 3, 4, 4, 4, 5, 5, 5, 5,
+ 4, 4, 4, 4, 5, 5, 5, 5
+ };
+
+ const unsigned char uvQuanTable511[] = {
+ 0, 2, 2, 3, 4, 4, 4, 4,
+ 2, 2, 2, 4, 4, 4, 4, 4,
+ 2, 2, 3, 4, 4, 4, 4, 4,
+ 3, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4
+ };
+
+ /* OV518 quantization tables are 8x4 (instead of 8x8) */
+ const unsigned char yQuanTable518[] = {
+ 5, 4, 5, 6, 6, 7, 7, 7,
+ 5, 5, 5, 5, 6, 7, 7, 7,
+ 6, 6, 6, 6, 7, 7, 7, 8,
+ 7, 7, 6, 7, 7, 7, 8, 8
+ };
+ const unsigned char uvQuanTable518[] = {
+ 6, 6, 6, 7, 7, 7, 7, 7,
+ 6, 6, 6, 7, 7, 7, 7, 7,
+ 6, 6, 6, 7, 7, 7, 7, 8,
+ 7, 7, 7, 7, 7, 7, 8, 8
+ };
+
+ const unsigned char *pYTable, *pUVTable;
+ unsigned char val0, val1;
+ int i, size, reg = R51x_COMP_LUT_BEGIN;
+
+ PDEBUG(D_PROBE, "Uploading quantization tables");
+
+ if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) {
+ pYTable = yQuanTable511;
+ pUVTable = uvQuanTable511;
+ size = 32;
+ } else {
+ pYTable = yQuanTable518;
+ pUVTable = uvQuanTable518;
+ size = 16;
+ }
+
+ for (i = 0; i < size; i++) {
+ val0 = *pYTable++;
+ val1 = *pYTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ reg_w(sd, reg, val0);
+
+ val0 = *pUVTable++;
+ val1 = *pUVTable++;
+ val0 &= 0x0f;
+ val1 &= 0x0f;
+ val0 |= val1 << 4;
+ reg_w(sd, reg + size, val0);
+
+ reg++;
+ }
+}
+
+/* This initializes the OV511/OV511+ and the sensor */
+static void ov511_configure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* For 511 and 511+ */
+ const struct ov_regvals init_511[] = {
+ { R51x_SYS_RESET, 0x7f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x7f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x3f },
+ { R51x_SYS_INIT, 0x01 },
+ { R51x_SYS_RESET, 0x3d },
+ };
+
+ const struct ov_regvals norm_511[] = {
+ { R511_DRAM_FLOW_CTL, 0x01 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R51x_SYS_SNAP, 0x02 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R511_FIFO_OPTS, 0x1f },
+ { R511_COMP_EN, 0x00 },
+ { R511_COMP_LUT_EN, 0x03 },
+ };
+
+ const struct ov_regvals norm_511_p[] = {
+ { R511_DRAM_FLOW_CTL, 0xff },
+ { R51x_SYS_SNAP, 0x00 },
+ { R51x_SYS_SNAP, 0x02 },
+ { R51x_SYS_SNAP, 0x00 },
+ { R511_FIFO_OPTS, 0xff },
+ { R511_COMP_EN, 0x00 },
+ { R511_COMP_LUT_EN, 0x03 },
+ };
+
+ const struct ov_regvals compress_511[] = {
+ { 0x70, 0x1f },
+ { 0x71, 0x05 },
+ { 0x72, 0x06 },
+ { 0x73, 0x06 },
+ { 0x74, 0x14 },
+ { 0x75, 0x03 },
+ { 0x76, 0x04 },
+ { 0x77, 0x04 },
+ };
+
+ PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID));
+
+ write_regvals(sd, init_511, ARRAY_SIZE(init_511));
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ write_regvals(sd, norm_511, ARRAY_SIZE(norm_511));
+ break;
+ case BRIDGE_OV511PLUS:
+ write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p));
+ break;
+ }
+
+ /* Init compression */
+ write_regvals(sd, compress_511, ARRAY_SIZE(compress_511));
+
+ ov51x_upload_quan_tables(sd);
+}
+
+/* This initializes the OV518/OV518+ and the sensor */
+static void ov518_configure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* For 518 and 518+ */
+ const struct ov_regvals init_518[] = {
+ { R51x_SYS_RESET, 0x40 },
+ { R51x_SYS_INIT, 0xe1 },
+ { R51x_SYS_RESET, 0x3e },
+ { R51x_SYS_INIT, 0xe1 },
+ { R51x_SYS_RESET, 0x00 },
+ { R51x_SYS_INIT, 0xe1 },
+ { 0x46, 0x00 },
+ { 0x5d, 0x03 },
+ };
+
+ const struct ov_regvals norm_518[] = {
+ { R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { 0x31, 0x0f },
+ { 0x5d, 0x03 },
+ { 0x24, 0x9f },
+ { 0x25, 0x90 },
+ { 0x20, 0x00 },
+ { 0x51, 0x04 },
+ { 0x71, 0x19 },
+ { 0x2f, 0x80 },
+ };
+
+ const struct ov_regvals norm_518_p[] = {
+ { R51x_SYS_SNAP, 0x02 }, /* Reset */
+ { R51x_SYS_SNAP, 0x01 }, /* Enable */
+ { 0x31, 0x0f },
+ { 0x5d, 0x03 },
+ { 0x24, 0x9f },
+ { 0x25, 0x90 },
+ { 0x20, 0x60 },
+ { 0x51, 0x02 },
+ { 0x71, 0x19 },
+ { 0x40, 0xff },
+ { 0x41, 0x42 },
+ { 0x46, 0x00 },
+ { 0x33, 0x04 },
+ { 0x21, 0x19 },
+ { 0x3f, 0x10 },
+ { 0x2f, 0x80 },
+ };
+
+ /* First 5 bits of custom ID reg are a revision ID on OV518 */
+ PDEBUG(D_PROBE, "Device revision %d",
+ 0x1f & reg_r(sd, R51x_SYS_CUST_ID));
+
+ write_regvals(sd, init_518, ARRAY_SIZE(init_518));
+
+ /* Set LED GPIO pin to output mode */
+ reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02);
+
+ switch (sd->bridge) {
+ case BRIDGE_OV518:
+ write_regvals(sd, norm_518, ARRAY_SIZE(norm_518));
+ break;
+ case BRIDGE_OV518PLUS:
+ write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p));
+ break;
+ }
+
+ ov51x_upload_quan_tables(sd);
+
+ reg_w(sd, 0x2f, 0x80);
+}
+
+static void ov519_configure(struct sd *sd)
+{
+ static const struct ov_regvals init_519[] = {
+ { 0x5a, 0x6d }, /* EnableSystem */
+ { 0x53, 0x9b }, /* don't enable the microcontroller */
+ { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */
+ { 0x5d, 0x03 },
+ { 0x49, 0x01 },
+ { 0x48, 0x00 },
+ /* Set LED pin to output mode. Bit 4 must be cleared or sensor
+ * detection will fail. This deserves further investigation. */
+ { OV519_GPIO_IO_CTRL0, 0xee },
+ { OV519_R51_RESET1, 0x0f },
+ { OV519_R51_RESET1, 0x00 },
+ { 0x22, 0x00 },
+ /* windows reads 0x55 at this point*/
+ };
+
+ write_regvals(sd, init_519, ARRAY_SIZE(init_519));
+}
+
+static void ovfx2_configure(struct sd *sd)
+{
+ static const struct ov_regvals init_fx2[] = {
+ { 0x00, 0x60 },
+ { 0x02, 0x01 },
+ { 0x0f, 0x1d },
+ { 0xe9, 0x82 },
+ { 0xea, 0xc7 },
+ { 0xeb, 0x10 },
+ { 0xec, 0xf6 },
+ };
+
+ sd->stopped = 1;
+
+ write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
+}
+
+/* set the mode */
+/* This function works for ov7660 only */
+static void ov519_set_mode(struct sd *sd)
+{
+ static const struct ov_regvals bridge_ov7660[2][10] = {
+ {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00},
+ {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+ {0x25, 0x01}, {0x26, 0x00}},
+ {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00},
+ {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
+ {0x25, 0x03}, {0x26, 0x00}}
+ };
+ static const struct ov_i2c_regvals sensor_ov7660[2][3] = {
+ {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}},
+ {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}}
+ };
+ static const struct ov_i2c_regvals sensor_ov7660_2[] = {
+ {OV7670_R17_HSTART, 0x13},
+ {OV7670_R18_HSTOP, 0x01},
+ {OV7670_R32_HREF, 0x92},
+ {OV7670_R19_VSTART, 0x02},
+ {OV7670_R1A_VSTOP, 0x7a},
+ {OV7670_R03_VREF, 0x00},
+/* {0x33, 0x00}, */
+/* {0x34, 0x07}, */
+/* {0x36, 0x00}, */
+/* {0x6b, 0x0a}, */
+ };
+
+ write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode],
+ ARRAY_SIZE(bridge_ov7660[0]));
+ write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode],
+ ARRAY_SIZE(sensor_ov7660[0]));
+ write_i2c_regvals(sd, sensor_ov7660_2,
+ ARRAY_SIZE(sensor_ov7660_2));
+}
+
+/* set the frame rate */
+/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */
+static void ov519_set_fr(struct sd *sd)
+{
+ int fr;
+ u8 clock;
+ /* frame rate table with indices:
+ * - mode = 0: 320x240, 1: 640x480
+ * - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5
+ * - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock)
+ */
+ static const u8 fr_tb[2][6][3] = {
+ {{0x04, 0xff, 0x00},
+ {0x04, 0x1f, 0x00},
+ {0x04, 0x1b, 0x00},
+ {0x04, 0x15, 0x00},
+ {0x04, 0x09, 0x00},
+ {0x04, 0x01, 0x00}},
+ {{0x0c, 0xff, 0x00},
+ {0x0c, 0x1f, 0x00},
+ {0x0c, 0x1b, 0x00},
+ {0x04, 0xff, 0x01},
+ {0x04, 0x1f, 0x01},
+ {0x04, 0x1b, 0x01}},
+ };
+
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+ if (sd->frame_rate >= 30)
+ fr = 0;
+ else if (sd->frame_rate >= 25)
+ fr = 1;
+ else if (sd->frame_rate >= 20)
+ fr = 2;
+ else if (sd->frame_rate >= 15)
+ fr = 3;
+ else if (sd->frame_rate >= 10)
+ fr = 4;
+ else
+ fr = 5;
+ reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]);
+ reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]);
+ clock = fr_tb[sd->gspca_dev.curr_mode][fr][2];
+ if (sd->sensor == SEN_OV7660)
+ clock |= 0x80; /* enable double clock */
+ ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ sd->bridge = id->driver_info & BRIDGE_MASK;
+ sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ cam->cam_mode = ov511_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov511_vga_mode);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ cam->cam_mode = ov518_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov518_vga_mode);
+ break;
+ case BRIDGE_OV519:
+ cam->cam_mode = ov519_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+ break;
+ case BRIDGE_OVFX2:
+ cam->cam_mode = ov519_vga_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
+ cam->bulk_size = OVFX2_BULK_SIZE;
+ cam->bulk_nurbs = MAX_NURBS;
+ cam->bulk = 1;
+ break;
+ case BRIDGE_W9968CF:
+ cam->cam_mode = w9968cf_vga_mode;
+ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
+ break;
+ }
+
+ sd->frame_rate = 15;
+
+ 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;
+ struct cam *cam = &gspca_dev->cam;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_configure(gspca_dev);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_configure(gspca_dev);
+ break;
+ case BRIDGE_OV519:
+ ov519_configure(sd);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_configure(sd);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_configure(sd);
+ break;
+ }
+
+ /* The OV519 must be more aggressive about sensor detection since
+ * I2C write will never fail if the sensor is not present. We have
+ * to try to initialize the sensor to detect its presence */
+ sd->sensor = -1;
+
+ /* Test for 76xx */
+ if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
+ ov7xx0_configure(sd);
+
+ /* Test for 6xx0 */
+ } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
+ ov6xx0_configure(sd);
+
+ /* Test for 8xx0 */
+ } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
+ ov8xx0_configure(sd);
+
+ /* Test for 3xxx / 2xxx */
+ } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
+ ov_hires_configure(sd);
+ } else {
+ pr_err("Can't determine sensor slave IDs\n");
+ goto error;
+ }
+
+ if (sd->sensor < 0)
+ goto error;
+
+ ov51x_led_control(sd, 0); /* turn LED off */
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ if (sd->sif) {
+ cam->cam_mode = ov511_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov511_sif_mode);
+ }
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ if (sd->sif) {
+ cam->cam_mode = ov518_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov518_sif_mode);
+ }
+ break;
+ case BRIDGE_OV519:
+ if (sd->sif) {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
+ case BRIDGE_OVFX2:
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ case SEN_OV2610AE:
+ cam->cam_mode = ovfx2_ov2610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
+ break;
+ case SEN_OV3610:
+ cam->cam_mode = ovfx2_ov3610_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
+ break;
+ case SEN_OV9600:
+ cam->cam_mode = ovfx2_ov9600_mode;
+ cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
+ break;
+ default:
+ if (sd->sif) {
+ cam->cam_mode = ov519_sif_mode;
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
+ }
+ break;
+ case BRIDGE_W9968CF:
+ if (sd->sif)
+ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1;
+
+ /* w9968cf needs initialisation once the sensor is known */
+ w9968cf_init(sd);
+ break;
+ }
+
+ /* initialize the sensor */
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610));
+
+ /* 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));
+
+ /* Enable autogain, autoexpo, awb, bandfilter */
+ i2c_w_mask(sd, 0x13, 0x27, 0x27);
+ break;
+ case SEN_OV6620:
+ write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20));
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
+ break;
+ default:
+/* case SEN_OV7610: */
+/* case SEN_OV76BE: */
+ write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610));
+ i2c_w_mask(sd, 0x0e, 0x00, 0x40);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620));
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640));
+ break;
+ case SEN_OV7660:
+ i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET);
+ msleep(14);
+ reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+ write_regvals(sd, init_519_ov7660,
+ ARRAY_SIZE(init_519_ov7660));
+ write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660));
+ sd->gspca_dev.curr_mode = 1; /* 640x480 */
+ ov519_set_mode(sd);
+ ov519_set_fr(sd);
+ sd_reset_snapshot(gspca_dev);
+ ov51x_restart(sd);
+ ov51x_stop(sd); /* not in win traces */
+ ov51x_led_control(sd, 0);
+ break;
+ case SEN_OV7670:
+ write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
+ break;
+ case SEN_OV8610:
+ write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
+ break;
+ case SEN_OV9600:
+ write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
+
+ /* enable autoexpo */
+/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */
+ break;
+ }
+ return gspca_dev->usb_err;
+error:
+ PDEBUG(D_ERR, "OV519 Config failed");
+ 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 != 800)
+ 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)
+ */
+static void ov511_mode_init_regs(struct sd *sd)
+{
+ int hsegs, vsegs, packet_size, fps, needed;
+ int interlaced = 0;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ sd->gspca_dev.usb_err = -EIO;
+ return;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5);
+
+ reg_w(sd, R511_CAM_UV_EN, 0x01);
+ reg_w(sd, R511_SNAP_UV_EN, 0x01);
+ reg_w(sd, R511_SNAP_OPTS, 0x03);
+
+ /* Here I'm assuming that snapshot size == image size.
+ * I hope that's always true. --claudio
+ */
+ hsegs = (sd->gspca_dev.width >> 3) - 1;
+ vsegs = (sd->gspca_dev.height >> 3) - 1;
+
+ reg_w(sd, R511_CAM_PXCNT, hsegs);
+ reg_w(sd, R511_CAM_LNCNT, vsegs);
+ reg_w(sd, R511_CAM_PXDIV, 0x00);
+ reg_w(sd, R511_CAM_LNDIV, 0x00);
+
+ /* YUV420, low pass filter on */
+ reg_w(sd, R511_CAM_OPTS, 0x03);
+
+ /* Snapshot additions */
+ reg_w(sd, R511_SNAP_PXCNT, hsegs);
+ reg_w(sd, R511_SNAP_LNCNT, vsegs);
+ reg_w(sd, R511_SNAP_PXDIV, 0x00);
+ reg_w(sd, R511_SNAP_LNDIV, 0x00);
+
+ /******** Set the framerate ********/
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+
+ switch (sd->sensor) {
+ case SEN_OV6620:
+ /* No framerate control, doesn't like higher rates yet */
+ sd->clockdiv = 3;
+ break;
+
+ /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
+ for more sensors we need to do this for them too */
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ case SEN_OV7640:
+ case SEN_OV7648:
+ case SEN_OV76BE:
+ if (sd->gspca_dev.width == 320)
+ interlaced = 1;
+ /* Fall through */
+ case SEN_OV6630:
+ case SEN_OV7610:
+ case SEN_OV7670:
+ switch (sd->frame_rate) {
+ case 30:
+ case 25:
+ /* Not enough bandwidth to do 640x480 @ 30 fps */
+ if (sd->gspca_dev.width != 640) {
+ sd->clockdiv = 0;
+ break;
+ }
+ /* Fall through for 640x480 case */
+ default:
+/* case 20: */
+/* case 15: */
+ sd->clockdiv = 1;
+ break;
+ case 10:
+ sd->clockdiv = 2;
+ break;
+ case 5:
+ sd->clockdiv = 5;
+ break;
+ }
+ if (interlaced) {
+ sd->clockdiv = (sd->clockdiv + 1) * 2 - 1;
+ /* Higher then 10 does not work */
+ if (sd->clockdiv > 10)
+ sd->clockdiv = 10;
+ }
+ break;
+
+ case SEN_OV8610:
+ /* No framerate control ?? */
+ sd->clockdiv = 0;
+ break;
+ }
+
+ /* Check if we have enough bandwidth to disable compression */
+ fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1;
+ needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2;
+ /* 1000 isoc packets/sec */
+ if (needed > 1000 * packet_size) {
+ /* Enable Y and UV quantization and compression */
+ reg_w(sd, R511_COMP_EN, 0x07);
+ reg_w(sd, R511_COMP_LUT_EN, 0x03);
+ } else {
+ reg_w(sd, R511_COMP_EN, 0x06);
+ reg_w(sd, R511_COMP_LUT_EN, 0x00);
+ }
+
+ reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE);
+ reg_w(sd, R51x_SYS_RESET, 0);
+}
+
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov518_mode_init_regs(struct sd *sd)
+{
+ int hsegs, vsegs, packet_size;
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+ alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ sd->gspca_dev.usb_err = -EIO;
+ return;
+ }
+
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2);
+
+ /******** Set the mode ********/
+ reg_w(sd, 0x2b, 0);
+ reg_w(sd, 0x2c, 0);
+ reg_w(sd, 0x2d, 0);
+ reg_w(sd, 0x2e, 0);
+ reg_w(sd, 0x3b, 0);
+ reg_w(sd, 0x3c, 0);
+ reg_w(sd, 0x3d, 0);
+ reg_w(sd, 0x3e, 0);
+
+ if (sd->bridge == BRIDGE_OV518) {
+ /* Set 8-bit (YVYU) input format */
+ reg_w_mask(sd, 0x20, 0x08, 0x08);
+
+ /* Set 12-bit (4:2:0) output format */
+ reg_w_mask(sd, 0x28, 0x80, 0xf0);
+ reg_w_mask(sd, 0x38, 0x80, 0xf0);
+ } else {
+ reg_w(sd, 0x28, 0x80);
+ reg_w(sd, 0x38, 0x80);
+ }
+
+ hsegs = sd->gspca_dev.width / 16;
+ vsegs = sd->gspca_dev.height / 4;
+
+ reg_w(sd, 0x29, hsegs);
+ reg_w(sd, 0x2a, vsegs);
+
+ reg_w(sd, 0x39, hsegs);
+ reg_w(sd, 0x3a, vsegs);
+
+ /* Windows driver does this here; who knows why */
+ reg_w(sd, 0x2f, 0x80);
+
+ /******** Set the framerate ********/
+ sd->clockdiv = 1;
+
+ /* Mode independent, but framerate dependent, regs */
+ /* 0x51: Clock divider; Only works on some cams which use 2 crystals */
+ reg_w(sd, 0x51, 0x04);
+ reg_w(sd, 0x22, 0x18);
+ reg_w(sd, 0x23, 0xff);
+
+ if (sd->bridge == BRIDGE_OV518PLUS) {
+ switch (sd->sensor) {
+ case SEN_OV7620AE:
+ if (sd->gspca_dev.width == 320) {
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+ } else {
+ reg_w(sd, 0x20, 0x60);
+ reg_w(sd, 0x21, 0x1f);
+ }
+ break;
+ case SEN_OV7620:
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+ break;
+ default:
+ reg_w(sd, 0x21, 0x19);
+ }
+ } else
+ reg_w(sd, 0x71, 0x17); /* Compression-related? */
+
+ /* FIXME: Sensor-specific */
+ /* Bit 5 is what matters here. Of course, it is "reserved" */
+ i2c_w(sd, 0x54, 0x23);
+
+ reg_w(sd, 0x2f, 0x80);
+
+ if (sd->bridge == BRIDGE_OV518PLUS) {
+ reg_w(sd, 0x24, 0x94);
+ reg_w(sd, 0x25, 0x90);
+ ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */
+ ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */
+ ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */
+ ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */
+ ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */
+ ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */
+ ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */
+ ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */
+ } else {
+ reg_w(sd, 0x24, 0x9f);
+ reg_w(sd, 0x25, 0x90);
+ ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
+ ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */
+ ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */
+ ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */
+ ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */
+ ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */
+ ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */
+ ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */
+ ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */
+ }
+
+ reg_w(sd, 0x2f, 0x80);
+}
+
+/* Sets up the OV519 with the given image parameters
+ *
+ * OV519 needs a completely different approach, until we can figure out what
+ * the individual registers do.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static void ov519_mode_init_regs(struct sd *sd)
+{
+ static const struct ov_regvals mode_init_519_ov7670[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+ { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x20, 0x0c },
+ { 0x21, 0x38 },
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+ { 0x59, 0x04 }, /* new from windrv 090403 */
+ { 0xff, 0x00 }, /* undocumented */
+ /* windows reads 0x55 at this point, why? */
+ };
+
+ static const struct ov_regvals mode_init_519[] = {
+ { 0x5d, 0x03 }, /* Turn off suspend mode */
+ { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
+ { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
+ { 0xa2, 0x20 }, /* a2-a5 are undocumented */
+ { 0xa3, 0x18 },
+ { 0xa4, 0x04 },
+ { 0xa5, 0x28 },
+ { 0x37, 0x00 }, /* SetUsbInit */
+ { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
+ /* Enable both fields, YUV Input, disable defect comp (why?) */
+ { 0x22, 0x1d },
+ { 0x17, 0x50 }, /* undocumented */
+ { 0x37, 0x00 }, /* undocumented */
+ { 0x40, 0xff }, /* I2C timeout counter */
+ { 0x46, 0x00 }, /* I2C clock prescaler */
+ { 0x59, 0x04 }, /* new from windrv 090403 */
+ { 0xff, 0x00 }, /* undocumented */
+ /* windows reads 0x55 at this point, why? */
+ };
+
+ /******** Set the mode ********/
+ switch (sd->sensor) {
+ default:
+ write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519));
+ if (sd->sensor == SEN_OV7640 ||
+ sd->sensor == SEN_OV7648) {
+ /* Select 8-bit input mode */
+ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
+ }
+ break;
+ case SEN_OV7660:
+ return; /* done by ov519_set_mode/fr() */
+ case SEN_OV7670:
+ write_regvals(sd, mode_init_519_ov7670,
+ ARRAY_SIZE(mode_init_519_ov7670));
+ break;
+ }
+
+ reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4);
+ reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3);
+ if (sd->sensor == SEN_OV7670 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
+ else if (sd->sensor == SEN_OV7648 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
+ else
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+ reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
+ reg_w(sd, OV519_R14_Y_OFFSETL, 0x00);
+ reg_w(sd, OV519_R15_Y_OFFSETH, 0x00);
+ reg_w(sd, OV519_R16_DIVIDER, 0x00);
+ reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */
+ reg_w(sd, 0x26, 0x00); /* Undocumented */
+
+ /******** Set the framerate ********/
+ if (frame_rate > 0)
+ sd->frame_rate = frame_rate;
+
+/* FIXME: These are only valid at the max resolution. */
+ sd->clockdiv = 0;
+ switch (sd->sensor) {
+ case SEN_OV7640:
+ case SEN_OV7648:
+ switch (sd->frame_rate) {
+ default:
+/* case 30: */
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 25:
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0x1f);
+ break;
+ case 20:
+ reg_w(sd, 0xa4, 0x0c);
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ case 15:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0xff);
+ sd->clockdiv = 1;
+ break;
+ case 10:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0x1f);
+ sd->clockdiv = 1;
+ break;
+ case 5:
+ reg_w(sd, 0xa4, 0x04);
+ reg_w(sd, 0x23, 0x1b);
+ sd->clockdiv = 1;
+ break;
+ }
+ break;
+ case SEN_OV8610:
+ switch (sd->frame_rate) {
+ default: /* 15 fps */
+/* case 15: */
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 10:
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0x1f);
+ break;
+ case 5:
+ reg_w(sd, 0xa4, 0x06);
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ }
+ break;
+ case SEN_OV7670: /* guesses, based on 7640 */
+ PDEBUG(D_STREAM, "Setting framerate to %d fps",
+ (sd->frame_rate == 0) ? 15 : sd->frame_rate);
+ reg_w(sd, 0xa4, 0x10);
+ switch (sd->frame_rate) {
+ case 30:
+ reg_w(sd, 0x23, 0xff);
+ break;
+ case 20:
+ reg_w(sd, 0x23, 0x1b);
+ break;
+ default:
+/* case 15: */
+ reg_w(sd, 0x23, 0xff);
+ sd->clockdiv = 1;
+ break;
+ }
+ break;
+ }
+}
+
+static void mode_init_ov_sensor_regs(struct sd *sd)
+{
+ struct gspca_dev *gspca_dev;
+ int qvga, xstart, xend, ystart, yend;
+ u8 v;
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+
+ /******** Mode (VGA/QVGA) and sensor specific regs ********/
+ switch (sd->sensor) {
+ case SEN_OV2610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ 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);
+ ystart = (776 - gspca_dev->height) / 2;
+ } else {
+ xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
+ ystart = (1544 - gspca_dev->height) / 2;
+ }
+ xend = xstart + gspca_dev->width;
+ yend = ystart + gspca_dev->height;
+ /* Writing to the COMH register resets the other windowing regs
+ to their default values, so we must do this first. */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
+ i2c_w_mask(sd, 0x32,
+ (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
+ 0x3f);
+ i2c_w_mask(sd, 0x03,
+ (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
+ 0x0f);
+ i2c_w(sd, 0x17, xstart >> 4);
+ i2c_w(sd, 0x18, xend >> 4);
+ i2c_w(sd, 0x19, ystart >> 3);
+ i2c_w(sd, 0x1a, yend >> 3);
+ return;
+ case SEN_OV8610:
+ /* For OV8610 qvga means qsvga */
+ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
+ i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
+ break;
+ case SEN_OV7610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ case SEN_OV76BE:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
+ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ if (sd->sensor == SEN_OV76BE)
+ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+ /* Setting this undocumented bit in qvga mode removes a very
+ annoying vertical shaking of the image */
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ /* Unknown */
+ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
+ /* Allow higher automatic gain (to allow higher framerates) */
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
+ break;
+ case SEN_OV7670:
+ /* set COM7_FMT_VGA or COM7_FMT_QVGA
+ * do we need to set anything else?
+ * HSTART etc are set in set_ov_sensor_window itself */
+ i2c_w_mask(sd, OV7670_R12_COM7,
+ qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
+ OV7670_COM7_FMT_MASK);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB,
+ OV7670_COM8_AWB);
+ if (qvga) { /* QVGA from ov7670.c by
+ * Jonathan Corbet */
+ xstart = 164;
+ xend = 28;
+ ystart = 14;
+ yend = 494;
+ } else { /* VGA */
+ xstart = 158;
+ xend = 14;
+ ystart = 10;
+ yend = 490;
+ }
+ /* OV7670 hardware window registers are split across
+ * multiple locations */
+ i2c_w(sd, OV7670_R17_HSTART, xstart >> 3);
+ i2c_w(sd, OV7670_R18_HSTOP, xend >> 3);
+ v = i2c_r(sd, OV7670_R32_HREF);
+ v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_R32_HREF, v);
+
+ i2c_w(sd, OV7670_R19_VSTART, ystart >> 2);
+ i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2);
+ v = i2c_r(sd, OV7670_R03_VREF);
+ v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
+ msleep(10); /* need to sleep between read and write to
+ * same reg! */
+ i2c_w(sd, OV7670_R03_VREF, v);
+ break;
+ case SEN_OV6620:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV9600: {
+ const struct ov_i2c_regvals *vals;
+ static const struct ov_i2c_regvals sxga_15[] = {
+ {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals sxga_7_5[] = {
+ {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
+ };
+ static const struct ov_i2c_regvals vga_30[] = {
+ {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
+ };
+ static const struct ov_i2c_regvals vga_15[] = {
+ {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
+ };
+
+ /* frame rates:
+ * 15fps / 7.5 fps for 1280x1024
+ * 30fps / 15fps for 640x480
+ */
+ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
+ if (qvga)
+ vals = sd->frame_rate < 30 ? vga_15 : vga_30;
+ else
+ vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
+ write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
+ return;
+ }
+ default:
+ return;
+ }
+
+ /******** Clock programming ********/
+ i2c_w(sd, 0x11, sd->clockdiv);
+}
+
+/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->gspca_dev.streaming)
+ reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */
+ i2c_w_mask(sd, OV7670_R1E_MVFP,
+ OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
+ OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
+ if (sd->gspca_dev.streaming)
+ reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */
+}
+
+static void set_ov_sensor_window(struct sd *sd)
+{
+ struct gspca_dev *gspca_dev;
+ int qvga, crop;
+ int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
+
+ /* 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:
+ case SEN_OV9600:
+ mode_init_ov_sensor_regs(sd);
+ return;
+ case SEN_OV7660:
+ ov519_set_mode(sd);
+ ov519_set_fr(sd);
+ return;
+ }
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
+ crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2;
+
+ /* The different sensor ICs handle setting up of window differently.
+ * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ hwsbase = 0x1e;
+ hwebase = 0x1e;
+ vwsbase = 0x02;
+ vwebase = 0x02;
+ break;
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ hwsbase = 0x38;
+ hwebase = 0x3a;
+ vwsbase = 0x05;
+ vwebase = 0x06;
+ if (sd->sensor == SEN_OV66308AF && qvga)
+ /* HDG: this fixes U and V getting swapped */
+ hwsbase++;
+ if (crop) {
+ hwsbase += 8;
+ hwebase += 8;
+ vwsbase += 11;
+ vwebase += 11;
+ }
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
+ hwebase = 0x2f;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ hwsbase = 0x1a;
+ hwebase = 0x1a;
+ vwsbase = vwebase = 0x03;
+ break;
+ default:
+ return;
+ }
+
+ switch (sd->sensor) {
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ if (qvga) { /* QCIF */
+ hwscale = 0;
+ vwscale = 0;
+ } else { /* CIF */
+ hwscale = 1;
+ vwscale = 1; /* The datasheet says 0;
+ * it's wrong */
+ }
+ break;
+ case SEN_OV8610:
+ if (qvga) { /* QSVGA */
+ hwscale = 1;
+ vwscale = 1;
+ } else { /* SVGA */
+ hwscale = 2;
+ vwscale = 2;
+ }
+ break;
+ default: /* SEN_OV7xx0 */
+ if (qvga) { /* QVGA */
+ hwscale = 1;
+ vwscale = 0;
+ } else { /* VGA */
+ hwscale = 2;
+ vwscale = 1;
+ }
+ }
+
+ mode_init_ov_sensor_regs(sd);
+
+ i2c_w(sd, 0x17, hwsbase);
+ i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
+ i2c_w(sd, 0x19, vwsbase);
+ i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Default for most bridges, allow bridge_mode_init_regs to override */
+ sd->sensor_width = sd->gspca_dev.width;
+ sd->sensor_height = sd->gspca_dev.height;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_mode_init_regs(sd);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_mode_init_regs(sd);
+ break;
+ case BRIDGE_OV519:
+ ov519_mode_init_regs(sd);
+ break;
+ /* case BRIDGE_OVFX2: nothing to do */
+ case BRIDGE_W9968CF:
+ w9968cf_mode_init_regs(sd);
+ break;
+ }
+
+ set_ov_sensor_window(sd);
+
+ /* Force clear snapshot state in case the snapshot button was
+ pressed while we weren't streaming */
+ sd->snapshot_needs_reset = 1;
+ sd_reset_snapshot(gspca_dev);
+
+ sd->first_frame = 3;
+
+ ov51x_restart(sd);
+ ov51x_led_control(sd, 1);
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ ov51x_stop(sd);
+ ov51x_led_control(sd, 0);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!sd->gspca_dev.present)
+ return;
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_stop0(sd);
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->snapshot_pressed) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->snapshot_pressed = 0;
+ }
+#endif
+ if (sd->bridge == BRIDGE_OV519)
+ reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
+}
+
+static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->snapshot_pressed != state) {
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
+ input_sync(gspca_dev->input_dev);
+#endif
+ if (state)
+ sd->snapshot_needs_reset = 1;
+
+ sd->snapshot_pressed = state;
+ } else {
+ /* On the ov511 / ov519 we need to reset the button state
+ multiple times, as resetting does not work as long as the
+ button stays pressed */
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ case BRIDGE_OV519:
+ if (state)
+ sd->snapshot_needs_reset = 1;
+ break;
+ }
+ }
+}
+
+static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *in, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
+ * byte non-zero. The EOF packet has image width/height in the
+ * 10th and 11th bytes. The 9th byte is given as follows:
+ *
+ * bit 7: EOF
+ * 6: compression enabled
+ * 5: 422/420/400 modes
+ * 4: 422/420/400 modes
+ * 3: 1
+ * 2: snapshot button on
+ * 1: snapshot frame
+ * 0: even/odd field
+ */
+ if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) &&
+ (in[8] & 0x08)) {
+ ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
+ if (in[8] & 0x80) {
+ /* Frame end */
+ if ((in[9] + 1) * 8 != gspca_dev->width ||
+ (in[10] + 1) * 8 != gspca_dev->height) {
+ PDEBUG(D_ERR, "Invalid frame size, got: %dx%d,"
+ " requested: %dx%d\n",
+ (in[9] + 1) * 8, (in[10] + 1) * 8,
+ gspca_dev->width, gspca_dev->height);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ /* Add 11 byte footer to frame, might be useful */
+ gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
+ return;
+ } else {
+ /* Frame start */
+ gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
+ sd->packet_nr = 0;
+ }
+ }
+
+ /* Ignore the packet number */
+ len--;
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
+}
+
+static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* A false positive here is likely, until OVT gives me
+ * the definitive SOF/EOF format */
+ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
+ ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ sd->packet_nr = 0;
+ }
+
+ if (gspca_dev->last_packet_type == DISCARD_PACKET)
+ return;
+
+ /* Does this device use packet numbers ? */
+ if (len & 7) {
+ len--;
+ if (sd->packet_nr == data[len])
+ sd->packet_nr++;
+ /* The last few packets of the frame (which are all 0's
+ except that they may contain part of the footer), are
+ numbered 0 */
+ else if (sd->packet_nr == 0 || data[len]) {
+ PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)",
+ (int)data[len], (int)sd->packet_nr);
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ /* Header of ov519 is 16 bytes:
+ * Byte Value Description
+ * 0 0xff magic
+ * 1 0xff magic
+ * 2 0xff magic
+ * 3 0xXX 0x50 = SOF, 0x51 = EOF
+ * 9 0xXX 0x01 initial frame without data,
+ * 0x00 standard frame with image
+ * 14 Lo in EOF: length of image data / 8
+ * 15 Hi
+ */
+
+ if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) {
+ switch (data[3]) {
+ case 0x50: /* start of frame */
+ /* Don't check the button state here, as the state
+ usually (always ?) changes at EOF and checking it
+ here leads to unnecessary snapshot state resets. */
+#define HDRSZ 16
+ data += HDRSZ;
+ len -= HDRSZ;
+#undef HDRSZ
+ if (data[0] == 0xff || data[1] == 0xd8)
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ case 0x51: /* end of frame */
+ ov51x_handle_button(gspca_dev, data[11] & 1);
+ if (data[9] != 0)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ return;
+ }
+ }
+
+ /* intermediate packet */
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+
+ /* A short read signals EOF */
+ if (len < gspca_dev->cam.bulk_size) {
+ /* If the frame is short, and it is one of the first ones
+ the sensor and bridge are still syncing, so drop it. */
+ if (sd->first_frame) {
+ sd->first_frame--;
+ if (gspca_dev->image_len <
+ sd->gspca_dev.width * sd->gspca_dev.height)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ }
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+ ov511_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ ov518_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV519:
+ ov519_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OVFX2:
+ ovfx2_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_W9968CF:
+ w9968cf_pkt_scan(gspca_dev, data, len);
+ break;
+ }
+}
+
+/* -- management routines -- */
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct ov_i2c_regvals brit_7660[][7] = {
+ {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
+ {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
+ {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1},
+ {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}},
+ {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2},
+ {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}},
+ {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3},
+ {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}},
+ {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3},
+ {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}},
+ {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3},
+ {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}},
+ {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4},
+ {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
+ };
+
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_BRT, val);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ i2c_w(sd, OV7610_REG_BRT, val);
+ break;
+ case SEN_OV7660:
+ write_i2c_regvals(sd, brit_7660[val],
+ ARRAY_SIZE(brit_7660[0]));
+ break;
+ case SEN_OV7670:
+/*win trace
+ * i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */
+ i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val));
+ break;
+ }
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct ov_i2c_regvals contrast_7660[][31] = {
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
+ {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
+ {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24},
+ {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65},
+ {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83},
+ {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f},
+ {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94},
+ {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30},
+ {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24},
+ {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62},
+ {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81},
+ {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1},
+ {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84},
+ {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40},
+ {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24},
+ {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d},
+ {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81},
+ {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e},
+ {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}},
+ {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70},
+ {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48},
+ {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34},
+ {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22},
+ {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58},
+ {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80},
+ {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9},
+ {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}},
+ {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80},
+ {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60},
+ {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38},
+ {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e},
+ {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46},
+ {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c},
+ {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4},
+ {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}},
+ {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80},
+ {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30},
+ {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50},
+ {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08},
+ {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a},
+ {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b},
+ {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3},
+ {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}},
+ {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60},
+ {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8},
+ {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c},
+ {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04},
+ {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22},
+ {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b},
+ {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde},
+ {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
+ };
+
+ switch (sd->sensor) {
+ case SEN_OV7610:
+ case SEN_OV6620:
+ i2c_w(sd, OV7610_REG_CNT, val);
+ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f);
+ break;
+ case SEN_OV8610: {
+ static const u8 ctab[] = {
+ 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ i2c_w(sd, 0x64, ctab[val >> 5]);
+ break;
+ }
+ case SEN_OV7620:
+ case SEN_OV7620AE: {
+ static const u8 ctab[] = {
+ 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+ 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+ };
+
+ /* Use Y gamma control instead. Bit 0 enables it. */
+ i2c_w(sd, 0x64, ctab[val >> 4]);
+ break;
+ }
+ case SEN_OV7660:
+ write_i2c_regvals(sd, contrast_7660[val],
+ ARRAY_SIZE(contrast_7660[0]));
+ break;
+ case SEN_OV7670:
+ /* check that this isn't just the same as ov7610 */
+ i2c_w(sd, OV7670_R56_CONTRAS, val >> 1);
+ break;
+ }
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w(sd, 0x10, val);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct ov_i2c_regvals colors_7660[][6] = {
+ {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
+ {0x53, 0x19}, {0x54, 0x23}},
+ {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11},
+ {0x53, 0x2c}, {0x54, 0x3e}},
+ {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19},
+ {0x53, 0x40}, {0x54, 0x59}},
+ {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20},
+ {0x53, 0x53}, {0x54, 0x73}},
+ {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28},
+ {0x53, 0x66}, {0x54, 0x8e}},
+ };
+
+ switch (sd->sensor) {
+ case SEN_OV8610:
+ case SEN_OV7610:
+ case SEN_OV76BE:
+ case SEN_OV6620:
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7620:
+ case SEN_OV7620AE:
+ /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
+ if (rc < 0)
+ goto out; */
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7640:
+ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
+ break;
+ case SEN_OV7660:
+ write_i2c_regvals(sd, colors_7660[val],
+ ARRAY_SIZE(colors_7660[0]));
+ break;
+ case SEN_OV7670:
+ /* supported later once I work out how to do it
+ * transparently fail now! */
+ /* set REG_COM13 values for UV sat auto mode */
+ break;
+ }
+}
+
+static void setautobright(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
+}
+
+static void setfreq_i(struct sd *sd, s32 val)
+{
+ if (sd->sensor == SEN_OV7660
+ || sd->sensor == SEN_OV7670) {
+ switch (val) {
+ case 0: /* Banding filter disabled */
+ i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
+ break;
+ case 1: /* 50 hz */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18);
+ break;
+ case 2: /* 60 hz */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18);
+ break;
+ case 3: /* Auto hz - ov7670 only */
+ i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
+ OV7670_COM8_BFILT);
+ i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO,
+ 0x18);
+ break;
+ }
+ } else {
+ switch (val) {
+ case 0: /* Banding filter disabled */
+ i2c_w_mask(sd, 0x2d, 0x00, 0x04);
+ i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+ break;
+ case 1: /* 50 hz (filter on and framerate adj) */
+ i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+ i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+ /* 20 fps -> 16.667 fps */
+ if (sd->sensor == SEN_OV6620 ||
+ sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF)
+ i2c_w(sd, 0x2b, 0x5e);
+ else
+ i2c_w(sd, 0x2b, 0xac);
+ break;
+ case 2: /* 60 hz (filter on, ...) */
+ i2c_w_mask(sd, 0x2d, 0x04, 0x04);
+ if (sd->sensor == SEN_OV6620 ||
+ sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF) {
+ /* 20 fps -> 15 fps */
+ i2c_w_mask(sd, 0x2a, 0x80, 0x80);
+ i2c_w(sd, 0x2b, 0xa8);
+ } else {
+ /* no framerate adj. */
+ i2c_w_mask(sd, 0x2a, 0x00, 0x80);
+ }
+ break;
+ }
+ }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ setfreq_i(sd, val);
+
+ /* Ugly but necessary */
+ if (sd->bridge == BRIDGE_W9968CF)
+ w9968cf_set_crop_window(sd);
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -ENOTTY;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
+ V4L2_JPEG_MARKER_DRI;
+ return 0;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ const struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge != BRIDGE_W9968CF)
+ return -ENOTTY;
+
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ return 0;
+}
+
+static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->exposure->val = i2c_r(sd, 0x10);
+ break;
+ }
+ return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOBRIGHTNESS:
+ if (ctrl->is_new)
+ setautobright(gspca_dev, ctrl->val);
+ if (!ctrl->val && sd->brightness->is_new)
+ setbrightness(gspca_dev, sd->brightness->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->is_new)
+ setautogain(gspca_dev, ctrl->val);
+ if (!ctrl->val && gspca_dev->exposure->is_new)
+ setexposure(gspca_dev, gspca_dev->exposure->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ return -EBUSY; /* Should never happen, as we grab the ctrl */
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .g_volatile_ctrl = sd_g_volatile_ctrl,
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 10);
+ if (valid_controls[sd->sensor].has_brightness)
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0,
+ sd->sensor == SEN_OV7660 ? 6 : 255, 1,
+ sd->sensor == SEN_OV7660 ? 3 : 127);
+ if (valid_controls[sd->sensor].has_contrast) {
+ if (sd->sensor == SEN_OV7660)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 6, 1, 3);
+ else
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1,
+ (sd->sensor == SEN_OV6630 ||
+ sd->sensor == SEN_OV66308AF) ? 200 : 127);
+ }
+ if (valid_controls[sd->sensor].has_sat)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0,
+ sd->sensor == SEN_OV7660 ? 4 : 255, 1,
+ sd->sensor == SEN_OV7660 ? 2 : 127);
+ if (valid_controls[sd->sensor].has_exposure)
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 255, 1, 127);
+ if (valid_controls[sd->sensor].has_hvflip) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
+ if (valid_controls[sd->sensor].has_autobright)
+ sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
+ if (valid_controls[sd->sensor].has_autogain)
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (valid_controls[sd->sensor].has_freq) {
+ if (sd->sensor == SEN_OV7670)
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
+ else
+ sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+ }
+ if (sd->bridge == BRIDGE_W9968CF)
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
+ if (sd->autobright)
+ v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
+ if (sd->hflip)
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_reset_snapshot,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x041e, 0x4052),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x045e, 0x028c),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
+ {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
+ {USB_DEVICE(0x05a9, 0x0519),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x0530),
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
+ {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(frame_rate, int, 0644);
+MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)");
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index bb09d7884b89..bb09d7884b89 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/usb/gspca/ov534_9.c
index c4cd028fe0b4..c4cd028fe0b4 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/usb/gspca/ov534_9.c
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/usb/gspca/pac207.c
index d236d1791f78..d236d1791f78 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/usb/gspca/pac207.c
diff --git a/drivers/media/usb/gspca/pac7302.c b/drivers/media/usb/gspca/pac7302.c
new file mode 100644
index 000000000000..2d5c6d8343a0
--- /dev/null
+++ b/drivers/media/usb/gspca/pac7302.c
@@ -0,0 +1,957 @@
+/*
+ * Pixart PAC7302 driver
+ *
+ * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ *
+ * Separated from Pixart PAC7311 library by Márton Németh
+ * Camera button input handling by Márton Németh <nm127@freemail.hu>
+ * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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
+ */
+
+/*
+ * Some documentation about various registers as determined by trial and error.
+ *
+ * Register page 0:
+ *
+ * Address Description
+ * 0x02 Red balance control
+ * 0x03 Green balance control
+ * 0x04 Blue balance control
+ * Valus are inverted (0=max, 255=min).
+ * The Windows driver uses a quadratic approach to map
+ * the settable values (0-200) on register values:
+ * min=0x80, default=0x40, max=0x20
+ * 0x0f-0x20 Colors, saturation and exposure control
+ * 0xa2-0xab Brightness, contrast and gamma control
+ * 0xb6 Sharpness control (bits 0-4)
+ *
+ * Register page 1:
+ *
+ * Address Description
+ * 0x78 Global control, bit 6 controls the LED (inverted)
+ * 0x80 Compression balance, 2 interesting settings:
+ * 0x0f Default
+ * 0x50 Values >= this switch the camera to a lower compression,
+ * using the same table for both luminance and chrominance.
+ * This gives a sharper picture. Only usable when running
+ * at < 15 fps! Note currently the driver does not use this
+ * as the quality gain is small and the generated JPG-s are
+ * only understood by v4l-utils >= 0.8.9
+ *
+ * Register page 3:
+ *
+ * Address Description
+ * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
+ * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
+ * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
+ * 63 -> ~27 fps, the 2 msb's must always be 1 !!
+ * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
+ * 1 -> ~30 fps, 2 -> ~20 fps
+ * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
+ * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
+ * 0x10 Gain 0-31
+ * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an
+ * amplification value of 1 rather then 0 at its lowest setting
+ * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ * 0x80 Another framerate control, best left at 1, moving it from 1 to
+ * 2 causes the framerate to become 3/4th of what it was, and
+ * also seems to cause pixel averaging, resulting in an effective
+ * resolution of 320x240 and thus a much blockier image
+ *
+ * The registers are accessed in the following functions:
+ *
+ * Page | Register | Function
+ * -----+------------+---------------------------------------------------
+ * 0 | 0x0f..0x20 | setcolors()
+ * 0 | 0xa2..0xab | setbrightcont()
+ * 0 | 0xb6 | setsharpness()
+ * 0 | 0xc5 | setredbalance()
+ * 0 | 0xc6 | setwhitebalance()
+ * 0 | 0xc7 | setbluebalance()
+ * 0 | 0xdc | setbrightcont(), setcolors()
+ * 3 | 0x02 | setexposure()
+ * 3 | 0x10, 0x12 | setgain()
+ * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
+ * 3 | 0x21 | sethvflip()
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include <media/v4l2-chip-ident.h>
+#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+#define PAC7302_GAIN_DEFAULT 15
+#define PAC7302_GAIN_KNEE 42
+#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
+#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_DESCRIPTION("Pixart PAC7302");
+MODULE_LICENSE("GPL");
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct { /* brightness / contrast cluster */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ };
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *white_balance;
+ struct v4l2_ctrl *red_balance;
+ struct v4l2_ctrl *blue_balance;
+ struct { /* flip cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct v4l2_ctrl *sharpness;
+ u8 flags;
+#define FL_HFLIP 0x01 /* mirrored by default */
+#define FL_VFLIP 0x02 /* vertical flipped by default */
+
+ u8 sof_read;
+ s8 autogain_ignore_frames;
+
+ atomic_t avg_lum;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+};
+
+#define LOAD_PAGE3 255
+#define END_OF_SEQUENCE 0
+
+static const u8 init_7302[] = {
+/* index,value */
+ 0xff, 0x01, /* page 1 */
+ 0x78, 0x00, /* deactivate */
+ 0xff, 0x01,
+ 0x78, 0x40, /* led off */
+};
+static const u8 start_7302[] = {
+/* index, len, [value]* */
+ 0xff, 1, 0x00, /* page 0 */
+ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+ 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+ 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+ 0x26, 2, 0xaa, 0xaa,
+ 0x2e, 1, 0x31,
+ 0x38, 1, 0x01,
+ 0x3a, 3, 0x14, 0xff, 0x5a,
+ 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+ 0x00, 0x54, 0x11,
+ 0x55, 1, 0x00,
+ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
+ 0x6b, 1, 0x00,
+ 0x6e, 3, 0x08, 0x06, 0x00,
+ 0x72, 3, 0x00, 0xff, 0x00,
+ 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+ 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+ 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+ 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+ 0xd2, 0xeb,
+ 0xaf, 1, 0x02,
+ 0xb5, 2, 0x08, 0x08,
+ 0xb8, 2, 0x08, 0x88,
+ 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
+ 0xcc, 1, 0x00,
+ 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+ 0xc1, 0xd7, 0xec,
+ 0xdc, 1, 0x01,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x12, 3, 0x02, 0x00, 0x01,
+ 0x3e, 2, 0x00, 0x00,
+ 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
+ 0x7c, 1, 0x00,
+ 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+ 0x02, 0x00,
+ 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
+ 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+ 0xd8, 1, 0x01,
+ 0xdb, 2, 0x00, 0x01,
+ 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+ 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
+ 0xeb, 1, 0x00,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x22, 1, 0x00,
+ 0xff, 1, 0x03, /* page 3 */
+ 0, LOAD_PAGE3, /* load the page 3 */
+ 0x11, 1, 0x01,
+ 0xff, 1, 0x02, /* page 2 */
+ 0x13, 1, 0x00,
+ 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
+ 0x27, 2, 0x14, 0x0c,
+ 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
+ 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+ 0x6e, 1, 0x08,
+ 0xff, 1, 0x01, /* page 1 */
+ 0x78, 1, 0x00,
+ 0, END_OF_SEQUENCE /* end of sequence */
+};
+
+#define SKIP 0xaa
+/* page 3 - the value SKIP says skip the index - see reg_w_page() */
+static const u8 page3_7302[] = {
+ 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
+ 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+ 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+ 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+ 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+ 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+ SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+ 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+ 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+ 0x00
+};
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ u8 index,
+ const u8 *buffer, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_buf failed i: %02x error %d\n",
+ index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u8 index,
+ u8 value)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ gspca_dev->usb_buf[0] = value;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w() failed i: %02x v: %02x error %d\n",
+ index, value, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_seq(struct gspca_dev *gspca_dev,
+ const u8 *seq, int len)
+{
+ while (--len >= 0) {
+ reg_w(gspca_dev, seq[0], seq[1]);
+ seq += 2;
+ }
+}
+
+/* load the beginning of a page */
+static void reg_w_page(struct gspca_dev *gspca_dev,
+ const u8 *page, int len)
+{
+ int index;
+ int ret = 0;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (index = 0; index < len; index++) {
+ if (page[index] == SKIP) /* skip this index */
+ continue;
+ gspca_dev->usb_buf[0] = page[index];
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
+ index, page[index], ret);
+ gspca_dev->usb_err = ret;
+ break;
+ }
+ }
+}
+
+/* output a variable sequence */
+static void reg_w_var(struct gspca_dev *gspca_dev,
+ const u8 *seq,
+ const u8 *page3, unsigned int page3_len)
+{
+ int index, len;
+
+ for (;;) {
+ index = *seq++;
+ len = *seq++;
+ switch (len) {
+ case END_OF_SEQUENCE:
+ return;
+ case LOAD_PAGE3:
+ reg_w_page(gspca_dev, page3, page3_len);
+ break;
+ default:
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_STREAM,
+ "Incorrect variable sequence");
+ return;
+ }
+#endif
+ while (len > 0) {
+ if (len < 8) {
+ reg_w_buf(gspca_dev,
+ index, seq, len);
+ seq += len;
+ break;
+ }
+ reg_w_buf(gspca_dev, index, seq, 8);
+ seq += 8;
+ index += 8;
+ len -= 8;
+ }
+ }
+ }
+ /* not reached */
+}
+
+/* this function is called at probe time for pac7302 */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+
+ cam->cam_mode = vga_mode; /* only 640x480 */
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ sd->flags = id->driver_info;
+ return 0;
+}
+
+static void setbrightcont(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ static const u8 max[10] =
+ {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+ 0xd4, 0xec};
+ static const u8 delta[10] =
+ {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+ 0x11, 0x0b};
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 10; i++) {
+ v = max[i];
+ v += (sd->brightness->val - sd->brightness->maximum)
+ * 150 / sd->brightness->maximum; /* 200 ? */
+ v -= delta[i] * sd->contrast->val / sd->contrast->maximum;
+ if (v < 0)
+ v = 0;
+ else if (v > 0xff)
+ v = 0xff;
+ reg_w(gspca_dev, 0xa2 + i, v);
+ }
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+ static const int a[9] =
+ {217, -212, 0, -101, 170, -67, -38, -315, 355};
+ static const int b[9] =
+ {19, 106, 0, 19, 106, 1, 19, 106, 1};
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x11, 0x01);
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ for (i = 0; i < 9; i++) {
+ v = a[i] * sd->saturation->val / sd->saturation->maximum;
+ v += b[i];
+ reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+ reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+ }
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setwhitebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc6, sd->white_balance->val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setredbalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc5, sd->red_balance->val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setbluebalance(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ u8 reg10, reg12;
+
+ if (gspca_dev->gain->val < 32) {
+ reg10 = gspca_dev->gain->val;
+ reg12 = 0;
+ } else {
+ reg10 = 31;
+ reg12 = gspca_dev->gain->val - 31;
+ }
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x10, reg10);
+ reg_w(gspca_dev, 0x12, reg12);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ u8 clockdiv;
+ u16 exposure;
+
+ /*
+ * Register 2 of frame 3 contains the clock divider configuring the
+ * no fps according to the formula: 90 / reg. sd->exposure is the
+ * desired exposure time in 0.5 ms.
+ */
+ clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
+
+ /*
+ * Note clockdiv = 3 also works, but when running at 30 fps, depending
+ * on the scene being recorded, the camera switches to another
+ * quantization table for certain JPEG blocks, and we don't know how
+ * to decompress these blocks. So we cap the framerate at 15 fps.
+ */
+ if (clockdiv < 6)
+ clockdiv = 6;
+ else if (clockdiv > 63)
+ clockdiv = 63;
+
+ /*
+ * Register 2 MUST be a multiple of 3, except when between 6 and 12?
+ * Always round up, otherwise we cannot get the desired frametime
+ * using the partial frame time exposure control.
+ */
+ if (clockdiv < 6 || clockdiv > 12)
+ clockdiv = ((clockdiv + 2) / 3) * 3;
+
+ /*
+ * frame exposure time in ms = 1000 * clockdiv / 90 ->
+ * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
+ */
+ exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
+ /* 0 = use full frametime, 448 = no exposure, reverse it */
+ exposure = 448 - exposure;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ reg_w(gspca_dev, 0x02, clockdiv);
+ reg_w(gspca_dev, 0x0e, exposure & 0xff);
+ reg_w(gspca_dev, 0x0f, exposure >> 8);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data, hflip, vflip;
+
+ hflip = sd->hflip->val;
+ if (sd->flags & FL_HFLIP)
+ hflip = !hflip;
+ vflip = sd->vflip->val;
+ if (sd->flags & FL_VFLIP)
+ vflip = !vflip;
+
+ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+ data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
+ reg_w(gspca_dev, 0x21, data);
+
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, 0xb6, sd->sharpness->val);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+}
+
+/* this function is called at probe and resume time for pac7302 */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
+ return gspca_dev->usb_err;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT;
+ gspca_dev->gain->val = PAC7302_GAIN_DEFAULT;
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightcont(gspca_dev);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev);
+ break;
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
+ setwhitebalance(gspca_dev);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setredbalance(gspca_dev);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setbluebalance(gspca_dev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+ setexposure(gspca_dev);
+ if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+ setgain(gspca_dev);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 12);
+
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+
+ sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+ 0, 255, 1, 55);
+ sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
+ sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 3, 1, 1);
+
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 1023, 1,
+ PAC7302_EXPOSURE_DEFAULT);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 62, 1,
+ PAC7302_GAIN_DEFAULT);
+
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 15, 1, 8);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ v4l2_ctrl_cluster(2, &sd->brightness);
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w_var(gspca_dev, start_7302,
+ page3_7302, sizeof(page3_7302));
+
+ sd->sof_read = 0;
+ sd->autogain_ignore_frames = 0;
+ atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
+
+ /* start stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x01);
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+
+ /* stop stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x00);
+}
+
+/* called on streamoff with alt 0 and on disconnect for pac7302 */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ if (!gspca_dev->present)
+ return;
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x78, 0x40);
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum = atomic_read(&sd->avg_lum);
+ int desired_lum;
+ const int deadzone = 30;
+
+ if (sd->autogain_ignore_frames < 0)
+ return;
+
+ if (sd->autogain_ignore_frames > 0) {
+ sd->autogain_ignore_frames--;
+ } else {
+ desired_lum = 270 + sd->brightness->val;
+
+ if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
+ deadzone, PAC7302_GAIN_KNEE,
+ PAC7302_EXPOSURE_KNEE))
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+}
+
+/* JPEG header */
+static const u8 jpeg_header[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08, /* Precision: 8 */
+ 0x02, 0x80, /* height = 640 (image rotated) */
+ 0x01, 0xe0, /* width = 480 */
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
+};
+
+/* this function is run at interrupt level */
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 *image;
+ u8 *sof;
+
+ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n, lum_offset, footer_length;
+
+ /*
+ * 6 bytes after the FF D9 EOF marker a number of lumination
+ * bytes are send corresponding to different parts of the
+ * image, the 14th and 15th byte after the EOF seem to
+ * correspond to the center of the image.
+ */
+ lum_offset = 61 + sizeof pac_sof_marker;
+ footer_length = 74;
+
+ /* Finish decoding current frame */
+ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+ if (n < 0) {
+ gspca_dev->image_len += n;
+ n = 0;
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
+ }
+
+ image = gspca_dev->image;
+ if (image != NULL
+ && image[gspca_dev->image_len - 2] == 0xff
+ && image[gspca_dev->image_len - 1] == 0xd9)
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ n = sof - data;
+ len -= n;
+ data = sof;
+
+ /* Get average lumination */
+ if (gspca_dev->last_packet_type == LAST_PACKET &&
+ n >= lum_offset)
+ atomic_set(&sd->avg_lum, data[-lum_offset] +
+ data[-lum_offset + 1]);
+
+ /* Start the new frame with the jpeg header */
+ /* The PAC7302 has the image rotated 90 degrees */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ jpeg_header, sizeof jpeg_header);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ u8 index;
+ u8 value;
+
+ /*
+ * reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+ * long on the USB bus)
+ */
+ if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
+ reg->match.addr == 0 &&
+ (reg->reg < 0x000000ff) &&
+ (reg->val <= 0x000000ff)
+ ) {
+ /* Currently writing to page 0 is only supported. */
+ /* reg_w() only supports 8bit index */
+ index = reg->reg;
+ value = reg->val;
+
+ /*
+ * Note that there shall be no access to other page
+ * by any other function between the page switch and
+ * the actual register write.
+ */
+ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+ reg_w(gspca_dev, index, value);
+
+ reg_w(gspca_dev, 0xdc, 0x01);
+ }
+ return gspca_dev->usb_err;
+}
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int ret = -EINVAL;
+
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
+ chip->match.addr == 0) {
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_UNKNOWN;
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ int ret = -EINVAL;
+ u8 data0, data1;
+
+ if (len == 2) {
+ data0 = data[0];
+ data1 = data[1];
+ if ((data0 == 0x00 && data1 == 0x11) ||
+ (data0 == 0x22 && data1 == 0x33) ||
+ (data0 == 0x44 && data1 == 0x55) ||
+ (data0 == 0x66 && data1 == 0x77) ||
+ (data0 == 0x88 && data1 == 0x99) ||
+ (data0 == 0xaa && data1 == 0xbb) ||
+ (data0 == 0xcc && data1 == 0xdd) ||
+ (data0 == 0xee && data1 == 0xff)) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description for pac7302 */
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_chip_ident = sd_chip_ident,
+#endif
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06f8, 0x3009)},
+ {USB_DEVICE(0x06f8, 0x301b)},
+ {USB_DEVICE(0x093a, 0x2620)},
+ {USB_DEVICE(0x093a, 0x2621)},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2625)},
+ {USB_DEVICE(0x093a, 0x2626)},
+ {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x2628)},
+ {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
+ {USB_DEVICE(0x093a, 0x262a)},
+ {USB_DEVICE(0x093a, 0x262c)},
+ {USB_DEVICE(0x145f, 0x013c)},
+ {USB_DEVICE(0x1ae7, 0x2001)}, /* SpeedLink Snappy Mic SL-6825-SBK */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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 = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/usb/gspca/pac7311.c
index ba3558d3f017..ba3558d3f017 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/usb/gspca/pac7311.c
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/usb/gspca/pac_common.h
index 8462a7c1a338..8462a7c1a338 100644
--- a/drivers/media/video/gspca/pac_common.h
+++ b/drivers/media/usb/gspca/pac_common.h
diff --git a/drivers/media/video/gspca/se401.c b/drivers/media/usb/gspca/se401.c
index a33cb78a839c..a33cb78a839c 100644
--- a/drivers/media/video/gspca/se401.c
+++ b/drivers/media/usb/gspca/se401.c
diff --git a/drivers/media/video/gspca/se401.h b/drivers/media/usb/gspca/se401.h
index 96d8ebf3cf59..96d8ebf3cf59 100644
--- a/drivers/media/video/gspca/se401.h
+++ b/drivers/media/usb/gspca/se401.h
diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/usb/gspca/sn9c2028.c
index 03fa3fd940b4..03fa3fd940b4 100644
--- a/drivers/media/video/gspca/sn9c2028.c
+++ b/drivers/media/usb/gspca/sn9c2028.c
diff --git a/drivers/media/video/gspca/sn9c2028.h b/drivers/media/usb/gspca/sn9c2028.h
index 8fd1d3e05665..8fd1d3e05665 100644
--- a/drivers/media/video/gspca/sn9c2028.h
+++ b/drivers/media/usb/gspca/sn9c2028.h
diff --git a/drivers/media/usb/gspca/sn9c20x.c b/drivers/media/usb/gspca/sn9c20x.c
new file mode 100644
index 000000000000..41f769fe340c
--- /dev/null
+++ b/drivers/media/usb/gspca/sn9c20x.c
@@ -0,0 +1,2430 @@
+/*
+ * Sonix sn9c201 sn9c202 library
+ *
+ * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
+ * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+
+#include "gspca.h"
+#include "jpeg.h"
+
+#include <media/v4l2-chip-ident.h>
+#include <linux/dmi.h>
+
+MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
+ "microdia project <microdia@googlegroups.com>");
+MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/*
+ * Pixel format private data
+ */
+#define SCALE_MASK 0x0f
+#define SCALE_160x120 0
+#define SCALE_320x240 1
+#define SCALE_640x480 2
+#define SCALE_1280x1024 3
+#define MODE_RAW 0x10
+#define MODE_JPEG 0x20
+#define MODE_SXGA 0x80
+
+#define SENSOR_OV9650 0
+#define SENSOR_OV9655 1
+#define SENSOR_SOI968 2
+#define SENSOR_OV7660 3
+#define SENSOR_OV7670 4
+#define SENSOR_MT9V011 5
+#define SENSOR_MT9V111 6
+#define SENSOR_MT9V112 7
+#define SENSOR_MT9M001 8
+#define SENSOR_MT9M111 9
+#define SENSOR_MT9M112 10
+#define SENSOR_HV7131R 11
+#define SENSOR_MT9VPRB 12
+
+/* camera flags */
+#define HAS_NO_BUTTON 0x1
+#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
+#define FLIP_DETECT 0x4
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev;
+
+ struct { /* color control cluster */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ };
+ struct { /* blue/red balance control cluster */
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
+ struct { /* h/vflip control cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct v4l2_ctrl *gamma;
+ struct { /* autogain and exposure or gain control cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *jpegqual;
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ s8 nchg;
+ u8 fmt; /* (used for JPEG QTAB update */
+
+#define MIN_AVG_LUM 80
+#define MAX_AVG_LUM 130
+ atomic_t avg_lum;
+ u8 old_step;
+ u8 older_step;
+ u8 exposure_step;
+
+ u8 i2c_addr;
+ u8 i2c_intf;
+ u8 sensor;
+ u8 hstart;
+ u8 vstart;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+
+ u8 flags;
+};
+
+static void qual_upd(struct work_struct *work);
+
+struct i2c_reg_u8 {
+ u8 reg;
+ u8 val;
+};
+
+struct i2c_reg_u16 {
+ u8 reg;
+ u16 val;
+};
+
+static const struct dmi_system_id flip_dmi_table[] = {
+ {
+ .ident = "MSI MS-1034",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "0341")
+ }
+ },
+ {
+ .ident = "MSI MS-1632",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1632")
+ }
+ },
+ {
+ .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"),
+ DMI_MATCH(DMI_BOARD_NAME, "MS-1635X")
+ }
+ },
+ {
+ .ident = "ASUSTeK W7J",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_BOARD_NAME, "W7J ")
+ }
+ },
+ {}
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_160x120 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_320x240 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_640x480 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480},
+};
+
+static const struct v4l2_pix_format sxga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_160x120 | MODE_JPEG},
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 240 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_320x240 | MODE_JPEG},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 480 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = SCALE_640x480 | MODE_JPEG},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 960 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const struct v4l2_pix_format mono_mode[] = {
+ {160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_160x120 | MODE_RAW},
+ {320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 ,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_320x240 | MODE_RAW},
+ {640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_640x480 | MODE_RAW},
+ {1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
+};
+
+static const s16 hsv_red_x[] = {
+ 41, 44, 46, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 68, 70, 72,
+ 74, 76, 78, 80, 81, 83, 85, 87,
+ 88, 90, 92, 93, 95, 97, 98, 100,
+ 101, 102, 104, 105, 107, 108, 109, 110,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 123, 124, 125, 125,
+ 126, 127, 127, 128, 128, 129, 129, 129,
+ 130, 130, 130, 130, 131, 131, 131, 131,
+ 131, 131, 131, 131, 130, 130, 130, 130,
+ 129, 129, 129, 128, 128, 127, 127, 126,
+ 125, 125, 124, 123, 122, 122, 121, 120,
+ 119, 118, 117, 116, 115, 114, 112, 111,
+ 110, 109, 107, 106, 105, 103, 102, 101,
+ 99, 98, 96, 94, 93, 91, 90, 88,
+ 86, 84, 83, 81, 79, 77, 75, 74,
+ 72, 70, 68, 66, 64, 62, 60, 58,
+ 56, 54, 52, 49, 47, 45, 43, 41,
+ 39, 36, 34, 32, 30, 28, 25, 23,
+ 21, 19, 16, 14, 12, 9, 7, 5,
+ 3, 0, -1, -3, -6, -8, -10, -12,
+ -15, -17, -19, -22, -24, -26, -28, -30,
+ -33, -35, -37, -39, -41, -44, -46, -48,
+ -50, -52, -54, -56, -58, -60, -62, -64,
+ -66, -68, -70, -72, -74, -76, -78, -80,
+ -81, -83, -85, -87, -88, -90, -92, -93,
+ -95, -97, -98, -100, -101, -102, -104, -105,
+ -107, -108, -109, -110, -112, -113, -114, -115,
+ -116, -117, -118, -119, -120, -121, -122, -123,
+ -123, -124, -125, -125, -126, -127, -127, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -127, -127, -126, -125, -125, -124, -123,
+ -122, -122, -121, -120, -119, -118, -117, -116,
+ -115, -114, -112, -111, -110, -109, -107, -106,
+ -105, -103, -102, -101, -99, -98, -96, -94,
+ -93, -91, -90, -88, -86, -84, -83, -81,
+ -79, -77, -75, -74, -72, -70, -68, -66,
+ -64, -62, -60, -58, -56, -54, -52, -49,
+ -47, -45, -43, -41, -39, -36, -34, -32,
+ -30, -28, -25, -23, -21, -19, -16, -14,
+ -12, -9, -7, -5, -3, 0, 1, 3,
+ 6, 8, 10, 12, 15, 17, 19, 22,
+ 24, 26, 28, 30, 33, 35, 37, 39, 41
+};
+
+static const s16 hsv_red_y[] = {
+ 82, 80, 78, 76, 74, 73, 71, 69,
+ 67, 65, 63, 61, 58, 56, 54, 52,
+ 50, 48, 46, 44, 41, 39, 37, 35,
+ 32, 30, 28, 26, 23, 21, 19, 16,
+ 14, 12, 10, 7, 5, 3, 0, -1,
+ -3, -6, -8, -10, -13, -15, -17, -19,
+ -22, -24, -26, -29, -31, -33, -35, -38,
+ -40, -42, -44, -46, -48, -51, -53, -55,
+ -57, -59, -61, -63, -65, -67, -69, -71,
+ -73, -75, -77, -79, -81, -82, -84, -86,
+ -88, -89, -91, -93, -94, -96, -98, -99,
+ -101, -102, -104, -105, -106, -108, -109, -110,
+ -112, -113, -114, -115, -116, -117, -119, -120,
+ -120, -121, -122, -123, -124, -125, -126, -126,
+ -127, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128,
+ -127, -127, -126, -125, -125, -124, -123, -122,
+ -121, -120, -119, -118, -117, -116, -115, -114,
+ -113, -111, -110, -109, -107, -106, -105, -103,
+ -102, -100, -99, -97, -96, -94, -92, -91,
+ -89, -87, -85, -84, -82, -80, -78, -76,
+ -74, -73, -71, -69, -67, -65, -63, -61,
+ -58, -56, -54, -52, -50, -48, -46, -44,
+ -41, -39, -37, -35, -32, -30, -28, -26,
+ -23, -21, -19, -16, -14, -12, -10, -7,
+ -5, -3, 0, 1, 3, 6, 8, 10,
+ 13, 15, 17, 19, 22, 24, 26, 29,
+ 31, 33, 35, 38, 40, 42, 44, 46,
+ 48, 51, 53, 55, 57, 59, 61, 63,
+ 65, 67, 69, 71, 73, 75, 77, 79,
+ 81, 82, 84, 86, 88, 89, 91, 93,
+ 94, 96, 98, 99, 101, 102, 104, 105,
+ 106, 108, 109, 110, 112, 113, 114, 115,
+ 116, 117, 119, 120, 120, 121, 122, 123,
+ 124, 125, 126, 126, 127, 128, 128, 129,
+ 129, 130, 130, 131, 131, 131, 131, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 131, 131, 131, 130, 130,
+ 130, 129, 129, 128, 127, 127, 126, 125,
+ 125, 124, 123, 122, 121, 120, 119, 118,
+ 117, 116, 115, 114, 113, 111, 110, 109,
+ 107, 106, 105, 103, 102, 100, 99, 97,
+ 96, 94, 92, 91, 89, 87, 85, 84, 82
+};
+
+static const s16 hsv_green_x[] = {
+ -124, -124, -125, -125, -125, -125, -125, -125,
+ -125, -126, -126, -125, -125, -125, -125, -125,
+ -125, -124, -124, -124, -123, -123, -122, -122,
+ -121, -121, -120, -120, -119, -118, -117, -117,
+ -116, -115, -114, -113, -112, -111, -110, -109,
+ -108, -107, -105, -104, -103, -102, -100, -99,
+ -98, -96, -95, -93, -92, -91, -89, -87,
+ -86, -84, -83, -81, -79, -77, -76, -74,
+ -72, -70, -69, -67, -65, -63, -61, -59,
+ -57, -55, -53, -51, -49, -47, -45, -43,
+ -41, -39, -37, -35, -33, -30, -28, -26,
+ -24, -22, -20, -18, -15, -13, -11, -9,
+ -7, -4, -2, 0, 1, 3, 6, 8,
+ 10, 12, 14, 17, 19, 21, 23, 25,
+ 27, 29, 32, 34, 36, 38, 40, 42,
+ 44, 46, 48, 50, 52, 54, 56, 58,
+ 60, 62, 64, 66, 68, 70, 71, 73,
+ 75, 77, 78, 80, 82, 83, 85, 87,
+ 88, 90, 91, 93, 94, 96, 97, 98,
+ 100, 101, 102, 104, 105, 106, 107, 108,
+ 109, 111, 112, 113, 113, 114, 115, 116,
+ 117, 118, 118, 119, 120, 120, 121, 122,
+ 122, 123, 123, 124, 124, 124, 125, 125,
+ 125, 125, 125, 125, 125, 126, 126, 125,
+ 125, 125, 125, 125, 125, 124, 124, 124,
+ 123, 123, 122, 122, 121, 121, 120, 120,
+ 119, 118, 117, 117, 116, 115, 114, 113,
+ 112, 111, 110, 109, 108, 107, 105, 104,
+ 103, 102, 100, 99, 98, 96, 95, 93,
+ 92, 91, 89, 87, 86, 84, 83, 81,
+ 79, 77, 76, 74, 72, 70, 69, 67,
+ 65, 63, 61, 59, 57, 55, 53, 51,
+ 49, 47, 45, 43, 41, 39, 37, 35,
+ 33, 30, 28, 26, 24, 22, 20, 18,
+ 15, 13, 11, 9, 7, 4, 2, 0,
+ -1, -3, -6, -8, -10, -12, -14, -17,
+ -19, -21, -23, -25, -27, -29, -32, -34,
+ -36, -38, -40, -42, -44, -46, -48, -50,
+ -52, -54, -56, -58, -60, -62, -64, -66,
+ -68, -70, -71, -73, -75, -77, -78, -80,
+ -82, -83, -85, -87, -88, -90, -91, -93,
+ -94, -96, -97, -98, -100, -101, -102, -104,
+ -105, -106, -107, -108, -109, -111, -112, -113,
+ -113, -114, -115, -116, -117, -118, -118, -119,
+ -120, -120, -121, -122, -122, -123, -123, -124, -124
+};
+
+static const s16 hsv_green_y[] = {
+ -100, -99, -98, -97, -95, -94, -93, -91,
+ -90, -89, -87, -86, -84, -83, -81, -80,
+ -78, -76, -75, -73, -71, -70, -68, -66,
+ -64, -63, -61, -59, -57, -55, -53, -51,
+ -49, -48, -46, -44, -42, -40, -38, -36,
+ -34, -32, -30, -27, -25, -23, -21, -19,
+ -17, -15, -13, -11, -9, -7, -4, -2,
+ 0, 1, 3, 5, 7, 9, 11, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 48, 50, 52, 54, 56, 58, 59, 61,
+ 63, 65, 67, 68, 70, 72, 74, 75,
+ 77, 78, 80, 82, 83, 85, 86, 88,
+ 89, 90, 92, 93, 95, 96, 97, 98,
+ 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 112, 113, 114,
+ 115, 115, 116, 116, 117, 117, 118, 118,
+ 119, 119, 119, 120, 120, 120, 120, 120,
+ 121, 121, 121, 121, 121, 121, 120, 120,
+ 120, 120, 120, 119, 119, 119, 118, 118,
+ 117, 117, 116, 116, 115, 114, 114, 113,
+ 112, 111, 111, 110, 109, 108, 107, 106,
+ 105, 104, 103, 102, 100, 99, 98, 97,
+ 95, 94, 93, 91, 90, 89, 87, 86,
+ 84, 83, 81, 80, 78, 76, 75, 73,
+ 71, 70, 68, 66, 64, 63, 61, 59,
+ 57, 55, 53, 51, 49, 48, 46, 44,
+ 42, 40, 38, 36, 34, 32, 30, 27,
+ 25, 23, 21, 19, 17, 15, 13, 11,
+ 9, 7, 4, 2, 0, -1, -3, -5,
+ -7, -9, -11, -14, -16, -18, -20, -22,
+ -24, -26, -28, -30, -32, -34, -36, -38,
+ -40, -42, -44, -46, -48, -50, -52, -54,
+ -56, -58, -59, -61, -63, -65, -67, -68,
+ -70, -72, -74, -75, -77, -78, -80, -82,
+ -83, -85, -86, -88, -89, -90, -92, -93,
+ -95, -96, -97, -98, -100, -101, -102, -103,
+ -104, -105, -106, -107, -108, -109, -110, -111,
+ -112, -112, -113, -114, -115, -115, -116, -116,
+ -117, -117, -118, -118, -119, -119, -119, -120,
+ -120, -120, -120, -120, -121, -121, -121, -121,
+ -121, -121, -120, -120, -120, -120, -120, -119,
+ -119, -119, -118, -118, -117, -117, -116, -116,
+ -115, -114, -114, -113, -112, -111, -111, -110,
+ -109, -108, -107, -106, -105, -104, -103, -102, -100
+};
+
+static const s16 hsv_blue_x[] = {
+ 112, 113, 114, 114, 115, 116, 117, 117,
+ 118, 118, 119, 119, 120, 120, 120, 121,
+ 121, 121, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 121, 121,
+ 121, 120, 120, 120, 119, 119, 118, 118,
+ 117, 116, 116, 115, 114, 113, 113, 112,
+ 111, 110, 109, 108, 107, 106, 105, 104,
+ 103, 102, 100, 99, 98, 97, 95, 94,
+ 93, 91, 90, 88, 87, 85, 84, 82,
+ 80, 79, 77, 76, 74, 72, 70, 69,
+ 67, 65, 63, 61, 60, 58, 56, 54,
+ 52, 50, 48, 46, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 19, 17, 15, 13, 11, 9, 7, 5,
+ 2, 0, -1, -3, -5, -7, -9, -12,
+ -14, -16, -18, -20, -22, -24, -26, -28,
+ -31, -33, -35, -37, -39, -41, -43, -45,
+ -47, -49, -51, -53, -54, -56, -58, -60,
+ -62, -64, -66, -67, -69, -71, -73, -74,
+ -76, -78, -79, -81, -83, -84, -86, -87,
+ -89, -90, -92, -93, -94, -96, -97, -98,
+ -99, -101, -102, -103, -104, -105, -106, -107,
+ -108, -109, -110, -111, -112, -113, -114, -114,
+ -115, -116, -117, -117, -118, -118, -119, -119,
+ -120, -120, -120, -121, -121, -121, -122, -122,
+ -122, -122, -122, -122, -122, -122, -122, -122,
+ -122, -122, -121, -121, -121, -120, -120, -120,
+ -119, -119, -118, -118, -117, -116, -116, -115,
+ -114, -113, -113, -112, -111, -110, -109, -108,
+ -107, -106, -105, -104, -103, -102, -100, -99,
+ -98, -97, -95, -94, -93, -91, -90, -88,
+ -87, -85, -84, -82, -80, -79, -77, -76,
+ -74, -72, -70, -69, -67, -65, -63, -61,
+ -60, -58, -56, -54, -52, -50, -48, -46,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -19, -17, -15, -13,
+ -11, -9, -7, -5, -2, 0, 1, 3,
+ 5, 7, 9, 12, 14, 16, 18, 20,
+ 22, 24, 26, 28, 31, 33, 35, 37,
+ 39, 41, 43, 45, 47, 49, 51, 53,
+ 54, 56, 58, 60, 62, 64, 66, 67,
+ 69, 71, 73, 74, 76, 78, 79, 81,
+ 83, 84, 86, 87, 89, 90, 92, 93,
+ 94, 96, 97, 98, 99, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111, 112
+};
+
+static const s16 hsv_blue_y[] = {
+ -11, -13, -15, -17, -19, -21, -23, -25,
+ -27, -29, -31, -33, -35, -37, -39, -41,
+ -43, -45, -46, -48, -50, -52, -54, -55,
+ -57, -59, -61, -62, -64, -66, -67, -69,
+ -71, -72, -74, -75, -77, -78, -80, -81,
+ -83, -84, -86, -87, -88, -90, -91, -92,
+ -93, -95, -96, -97, -98, -99, -100, -101,
+ -102, -103, -104, -105, -106, -106, -107, -108,
+ -109, -109, -110, -111, -111, -112, -112, -113,
+ -113, -114, -114, -114, -115, -115, -115, -115,
+ -116, -116, -116, -116, -116, -116, -116, -116,
+ -116, -115, -115, -115, -115, -114, -114, -114,
+ -113, -113, -112, -112, -111, -111, -110, -110,
+ -109, -108, -108, -107, -106, -105, -104, -103,
+ -102, -101, -100, -99, -98, -97, -96, -95,
+ -94, -93, -91, -90, -89, -88, -86, -85,
+ -84, -82, -81, -79, -78, -76, -75, -73,
+ -71, -70, -68, -67, -65, -63, -62, -60,
+ -58, -56, -55, -53, -51, -49, -47, -45,
+ -44, -42, -40, -38, -36, -34, -32, -30,
+ -28, -26, -24, -22, -20, -18, -16, -14,
+ -12, -10, -8, -6, -4, -2, 0, 1,
+ 3, 5, 7, 9, 11, 13, 15, 17,
+ 19, 21, 23, 25, 27, 29, 31, 33,
+ 35, 37, 39, 41, 43, 45, 46, 48,
+ 50, 52, 54, 55, 57, 59, 61, 62,
+ 64, 66, 67, 69, 71, 72, 74, 75,
+ 77, 78, 80, 81, 83, 84, 86, 87,
+ 88, 90, 91, 92, 93, 95, 96, 97,
+ 98, 99, 100, 101, 102, 103, 104, 105,
+ 106, 106, 107, 108, 109, 109, 110, 111,
+ 111, 112, 112, 113, 113, 114, 114, 114,
+ 115, 115, 115, 115, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 115, 115, 115,
+ 115, 114, 114, 114, 113, 113, 112, 112,
+ 111, 111, 110, 110, 109, 108, 108, 107,
+ 106, 105, 104, 103, 102, 101, 100, 99,
+ 98, 97, 96, 95, 94, 93, 91, 90,
+ 89, 88, 86, 85, 84, 82, 81, 79,
+ 78, 76, 75, 73, 71, 70, 68, 67,
+ 65, 63, 62, 60, 58, 56, 55, 53,
+ 51, 49, 47, 45, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22,
+ 20, 18, 16, 14, 12, 10, 8, 6,
+ 4, 2, 0, -1, -3, -5, -7, -9, -11
+};
+
+static const u16 i2c_ident[] = {
+ V4L2_IDENT_OV9650,
+ V4L2_IDENT_OV9655,
+ V4L2_IDENT_SOI968,
+ V4L2_IDENT_OV7660,
+ V4L2_IDENT_OV7670,
+ V4L2_IDENT_MT9V011,
+ V4L2_IDENT_MT9V111,
+ V4L2_IDENT_MT9V112,
+ V4L2_IDENT_MT9M001C12ST,
+ V4L2_IDENT_MT9M111,
+ V4L2_IDENT_MT9M112,
+ V4L2_IDENT_HV7131R,
+[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN,
+};
+
+static const u16 bridge_init[][2] = {
+ {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
+ {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
+ {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10},
+ {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00},
+ {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50},
+ {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50},
+ {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04},
+ {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05},
+ {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00},
+ {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d},
+ {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04},
+ {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8},
+ {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32},
+ {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd},
+ {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01},
+ {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f},
+ {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f},
+ {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01},
+ {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80},
+ {0x1007, 0x00}
+};
+
+/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
+static const u8 ov_gain[] = {
+ 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
+ 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
+ 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
+ 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */,
+ 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */,
+ 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */,
+ 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */,
+ 0x70 /* 8x */
+};
+
+/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
+static const u16 micron1_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0020, 0x0028, 0x0030, 0x0038,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x00a0, 0x00a4, 0x00a8, 0x00ac,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x00b0, 0x00b4, 0x00b8, 0x00bc,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x00c0, 0x00c4, 0x00c8, 0x00cc,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x00d0, 0x00d4, 0x00d8, 0x00dc,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x00e0, 0x00e4, 0x00e8, 0x00ec,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x00f0, 0x00f4, 0x00f8, 0x00fc,
+ /* 8x */
+ 0x01c0
+};
+
+/* mt9m001 sensor uses a different gain formula then other micron sensors */
+/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
+static const u16 micron2_gain[] = {
+ /* 1x 1.25x 1.5x 1.75x */
+ 0x0008, 0x000a, 0x000c, 0x000e,
+ /* 2x 2.25x 2.5x 2.75x */
+ 0x0010, 0x0012, 0x0014, 0x0016,
+ /* 3x 3.25x 3.5x 3.75x */
+ 0x0018, 0x001a, 0x001c, 0x001e,
+ /* 4x 4.25x 4.5x 4.75x */
+ 0x0020, 0x0051, 0x0052, 0x0053,
+ /* 5x 5.25x 5.5x 5.75x */
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ /* 6x 6.25x 6.5x 6.75x */
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ /* 7x 7.25x 7.5x 7.75x */
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 8x */
+ 0x0060
+};
+
+/* Gain = .5 + bit[7:0] / 16 */
+static const u8 hv7131r_gain[] = {
+ 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
+ 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
+ 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
+ 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */,
+ 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */,
+ 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */,
+ 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */,
+ 0x78 /* 8x */
+};
+
+static const struct i2c_reg_u8 soi968_init[] = {
+ {0x0c, 0x00}, {0x0f, 0x1f},
+ {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
+ {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
+ {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
+ {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20},
+ {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e},
+ {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13},
+ {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79},
+ {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40},
+ {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32},
+ {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
+};
+
+static const 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, 0x00},
+ {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50},
+};
+
+static const struct i2c_reg_u8 ov7670_init[] = {
+ {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
+ {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
+ {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
+ {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
+ {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07},
+ {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75},
+ {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8},
+ {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5},
+ {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27},
+ {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b},
+ {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a},
+ {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00},
+ {0x74, 0x19}, {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, 0x20},
+ {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c},
+ {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66},
+ {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11},
+ {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40},
+ {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02},
+ {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a},
+ {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08},
+ {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04},
+ {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30},
+ {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88},
+ {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30},
+ {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99},
+ {0x9e, 0x7f}, {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, 0x40}, {0x79, 0x05}, {0xc8, 0x30},
+ {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06},
+ {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a},
+ {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a},
+ {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84},
+ {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d},
+ {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d},
+ {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00},
+ {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60},
+ {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d},
+ {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e},
+ {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56},
+ {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03},
+ {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47},
+ {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74},
+ {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2},
+ {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00},
+ {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a},
+ {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00},
+ {0x93, 0x00},
+};
+
+static const struct i2c_reg_u8 ov9650_init[] = {
+ {0x00, 0x00}, {0x01, 0x78},
+ {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
+ {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
+ {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
+ {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c},
+ {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2},
+ {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07},
+ {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00},
+ {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04},
+ {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68},
+ {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80},
+ {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00},
+ {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30},
+ {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf},
+ {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00},
+ {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01},
+ {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19},
+ {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1},
+ {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80},
+ {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00},
+ {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20},
+ {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf},
+ {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88},
+ {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00},
+ {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8},
+ {0xaa, 0x92}, {0xab, 0x0a},
+};
+
+static const struct i2c_reg_u8 ov9655_init[] = {
+ {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
+ {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
+ {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
+ {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
+ {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19},
+ {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80},
+ {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c},
+ {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc},
+ {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05},
+ {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e},
+ {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68},
+ {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92},
+ {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80},
+ {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00},
+ {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44},
+ {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01},
+ {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0},
+ {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00},
+ {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61},
+ {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a},
+ {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01},
+ {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a},
+ {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c},
+ {0x04, 0x03}, {0x00, 0x13},
+};
+
+static const struct i2c_reg_u16 mt9v112_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
+ {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
+ {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
+ {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a},
+ {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58},
+ {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001},
+ {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020},
+ {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020},
+ {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2},
+ {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020},
+ {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
+ {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae},
+ {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
+};
+
+static const struct i2c_reg_u16 mt9v111_init[] = {
+ {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0},
+ {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e},
+ {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016},
+ {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004},
+ {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008},
+ {0x0e, 0x0008}, {0x20, 0x0000}
+};
+
+static const struct i2c_reg_u16 mt9v011_init[] = {
+ {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
+ {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1},
+ {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006},
+ {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000},
+ {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000},
+ {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000},
+ {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000},
+ {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000},
+ {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000},
+ {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000},
+ {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000},
+ {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000},
+ {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024},
+ {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000},
+ {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100},
+ {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1},
+ {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000},
+ {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000},
+ {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101},
+ {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003},
+ {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
+ {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000},
+ {0x06, 0x0029}, {0x05, 0x0009},
+};
+
+static const struct i2c_reg_u16 mt9m001_init[] = {
+ {0x0d, 0x0001},
+ {0x0d, 0x0000},
+ {0x04, 0x0500}, /* hres = 1280 */
+ {0x03, 0x0400}, /* vres = 1024 */
+ {0x20, 0x1100},
+ {0x06, 0x0010},
+ {0x2b, 0x0024},
+ {0x2e, 0x0024},
+ {0x35, 0x0024},
+ {0x2d, 0x0020},
+ {0x2c, 0x0020},
+ {0x09, 0x0ad4},
+ {0x35, 0x0057},
+};
+
+static const struct i2c_reg_u16 mt9m111_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+ {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+ {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+ {0xf0, 0x0000},
+};
+
+static const struct i2c_reg_u16 mt9m112_init[] = {
+ {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
+ {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
+ {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
+ {0xf0, 0x0000},
+};
+
+static const struct i2c_reg_u8 hv7131r_init[] = {
+ {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
+ {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
+ {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
+ {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07},
+ {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62},
+ {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10},
+ {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00},
+ {0x23, 0x09}, {0x01, 0x08},
+};
+
+static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ pr_err("Read register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
+ const u8 *buffer, int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ memcpy(gspca_dev->usb_buf, buffer, length);
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ reg,
+ 0x00,
+ gspca_dev->usb_buf,
+ length,
+ 500);
+ if (unlikely(result < 0 || result != length)) {
+ pr_err("Write register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
+ }
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
+{
+ reg_w(gspca_dev, reg, &value, 1);
+}
+
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
+{
+ int i;
+
+ reg_w(gspca_dev, 0x10c0, buffer, 8);
+ for (i = 0; i < 5; i++) {
+ reg_r(gspca_dev, 0x10c0, 1);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (gspca_dev->usb_buf[0] & 0x04) {
+ if (gspca_dev->usb_buf[0] & 0x08) {
+ pr_err("i2c_w error\n");
+ gspca_dev->usb_err = -EIO;
+ }
+ return;
+ }
+ msleep(10);
+ }
+ pr_err("i2c_w reg %02x no response\n", buffer[2]);
+/* gspca_dev->usb_err = -EIO; fixme: may occur */
+}
+
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = sd->i2c_intf | (2 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = val;
+ row[4] = 0x00;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w1_buf(struct gspca_dev *gspca_dev,
+ const struct i2c_reg_u8 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w1(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
+}
+
+static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ /*
+ * from the point of view of the bridge, the length
+ * includes the address
+ */
+ row[0] = sd->i2c_intf | (3 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = val >> 8;
+ row[4] = val;
+ row[5] = 0x00;
+ row[6] = 0x00;
+ row[7] = 0x10;
+
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w2_buf(struct gspca_dev *gspca_dev,
+ const struct i2c_reg_u16 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w2(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
+}
+
+static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = sd->i2c_intf | (1 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ i2c_w(gspca_dev, row);
+ row[0] = sd->i2c_intf | (1 << 4) | 0x02;
+ row[2] = 0;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = gspca_dev->usb_buf[4];
+}
+
+static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+
+ row[0] = sd->i2c_intf | (1 << 4);
+ row[1] = sd->i2c_addr;
+ row[2] = reg;
+ row[3] = 0;
+ row[4] = 0;
+ row[5] = 0;
+ row[6] = 0;
+ row[7] = 0x10;
+ i2c_w(gspca_dev, row);
+ row[0] = sd->i2c_intf | (2 << 4) | 0x02;
+ row[2] = 0;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
+ *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+}
+
+static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
+{
+ u16 id;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_r2(gspca_dev, 0x1c, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ if (id != 0x7fa2) {
+ pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
+ gspca_dev->usb_err = -ENODEV;
+ return;
+ }
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9650 sensor initialization failed\n");
+ sd->hstart = 1;
+ sd->vstart = 7;
+}
+
+static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9655 sensor initialization failed\n");
+
+ sd->hstart = 1;
+ sd->vstart = 2;
+}
+
+static void soi968_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("SOI968 sensor initialization failed\n");
+
+ sd->hstart = 60;
+ sd->vstart = 11;
+}
+
+static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7660 sensor initialization failed\n");
+ sd->hstart = 3;
+ sd->vstart = 3;
+}
+
+static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7670 sensor initialization failed\n");
+
+ sd->hstart = 0;
+ sd->vstart = 1;
+}
+
+static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 value;
+
+ sd->i2c_addr = 0x5d;
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x8243) {
+ i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V011 sensor initialization failed\n");
+ return;
+ }
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V011;
+ pr_info("MT9V011 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x5c;
+ i2c_w2(gspca_dev, 0x01, 0x0004);
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x823a) {
+ i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V111 sensor initialization failed\n");
+ return;
+ }
+ sd->hstart = 2;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V111;
+ pr_info("MT9V111 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x5d;
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ if (gspca_dev->usb_err < 0) {
+ gspca_dev->usb_err = 0;
+ sd->i2c_addr = 0x48;
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ }
+ i2c_r2(gspca_dev, 0x00, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x1229) {
+ i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V112 sensor initialization failed\n");
+ return;
+ }
+ sd->hstart = 6;
+ sd->vstart = 2;
+ sd->sensor = SENSOR_MT9V112;
+ pr_info("MT9V112 sensor detected\n");
+ return;
+ }
+
+ gspca_dev->usb_err = -ENODEV;
+}
+
+static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M112 sensor initialization failed\n");
+
+ sd->hstart = 0;
+ sd->vstart = 2;
+}
+
+static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M111 sensor initialization failed\n");
+
+ sd->hstart = 0;
+ sd->vstart = 2;
+}
+
+static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 id;
+
+ i2c_r2(gspca_dev, 0x00, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
+ switch (id) {
+ case 0x8411:
+ case 0x8421:
+ pr_info("MT9M001 color sensor detected\n");
+ break;
+ case 0x8431:
+ pr_info("MT9M001 mono sensor detected\n");
+ break;
+ default:
+ pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
+ gspca_dev->usb_err = -ENODEV;
+ return;
+ }
+
+ i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M001 sensor initialization failed\n");
+
+ sd->hstart = 1;
+ sd->vstart = 1;
+}
+
+static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("HV7131R Sensor initialization failed\n");
+
+ sd->hstart = 0;
+ sd->vstart = 1;
+}
+
+static void set_cmatrix(struct gspca_dev *gspca_dev,
+ s32 brightness, s32 contrast, s32 satur, s32 hue)
+{
+ s32 hue_coord, hue_index = 180 + hue;
+ u8 cmatrix[21];
+
+ memset(cmatrix, 0, sizeof cmatrix);
+ cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
+ cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
+ cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
+ cmatrix[18] = brightness - 0x80;
+
+ hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
+ cmatrix[6] = hue_coord;
+ cmatrix[7] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
+ cmatrix[8] = hue_coord;
+ cmatrix[9] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
+ cmatrix[10] = hue_coord;
+ cmatrix[11] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
+ cmatrix[12] = hue_coord;
+ cmatrix[13] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
+ cmatrix[14] = hue_coord;
+ cmatrix[15] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
+ cmatrix[16] = hue_coord;
+ cmatrix[17] = (hue_coord >> 8) & 0x0f;
+
+ reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+}
+
+static void set_gamma(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 gamma[17];
+ u8 gval = val * 0xb8 / 0x100;
+
+ gamma[0] = 0x0a;
+ gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
+ gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8);
+ gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8);
+ gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8);
+ gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8);
+ gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8);
+ gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8);
+ gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8);
+ gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8);
+ gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8);
+ gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8);
+ gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8);
+ gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8);
+ gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8);
+ gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
+ gamma[16] = 0xf5;
+
+ reg_w(gspca_dev, 0x1190, gamma, 17);
+}
+
+static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red)
+{
+ reg_w1(gspca_dev, 0x118c, red);
+ reg_w1(gspca_dev, 0x118f, blue);
+}
+
+static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
+{
+ u8 value, tslb;
+ u16 value2;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
+ hflip = !hflip;
+ vflip = !vflip;
+ }
+
+ 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;
+ tslb = 0x01;
+ if (hflip)
+ value |= 0x20;
+ if (vflip) {
+ value |= 0x10;
+ tslb = 0x49;
+ }
+ i2c_w1(gspca_dev, 0x1e, value);
+ i2c_w1(gspca_dev, 0x3a, tslb);
+ break;
+ case SENSOR_MT9V111:
+ case SENSOR_MT9V011:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0xc0a0;
+ if (hflip)
+ value2 |= 0x8080;
+ if (vflip)
+ value2 |= 0x4020;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ case SENSOR_MT9V112:
+ i2c_r2(gspca_dev, 0x20, &value2);
+ value2 &= ~0x0003;
+ if (hflip)
+ value2 |= 0x0002;
+ if (vflip)
+ value2 |= 0x0001;
+ i2c_w2(gspca_dev, 0x20, value2);
+ break;
+ case SENSOR_HV7131R:
+ i2c_r1(gspca_dev, 0x01, &value);
+ value &= ~0x03;
+ if (vflip)
+ value |= 0x01;
+ if (hflip)
+ value |= 0x02;
+ i2c_w1(gspca_dev, 0x01, value);
+ break;
+ }
+}
+
+static void set_exposure(struct gspca_dev *gspca_dev, s32 expo)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 exp[8] = {sd->i2c_intf, sd->i2c_addr,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ int expo2;
+
+ if (gspca_dev->streaming)
+ exp[7] = 0x1e;
+
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ if (expo > 547)
+ expo2 = 547;
+ else
+ expo2 = expo;
+ exp[0] |= (2 << 4);
+ exp[2] = 0x10; /* AECH */
+ exp[3] = expo2 >> 2;
+ exp[7] = 0x10;
+ i2c_w(gspca_dev, exp);
+ exp[2] = 0x04; /* COM1 */
+ exp[3] = expo2 & 0x0003;
+ exp[7] = 0x10;
+ i2c_w(gspca_dev, exp);
+ expo -= expo2;
+ exp[7] = 0x1e;
+ exp[0] |= (3 << 4);
+ exp[2] = 0x2d; /* ADVFL & ADVFH */
+ exp[3] = expo;
+ exp[4] = expo >> 8;
+ break;
+ case SENSOR_MT9M001:
+ case SENSOR_MT9V112:
+ case SENSOR_MT9V011:
+ exp[0] |= (3 << 4);
+ exp[2] = 0x09;
+ exp[3] = expo >> 8;
+ exp[4] = expo;
+ break;
+ case SENSOR_HV7131R:
+ exp[0] |= (4 << 4);
+ exp[2] = 0x25;
+ exp[3] = expo >> 5;
+ exp[4] = expo << 3;
+ exp[5] = 0;
+ break;
+ default:
+ return;
+ }
+ i2c_w(gspca_dev, exp);
+}
+
+static void set_gain(struct gspca_dev *gspca_dev, s32 g)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 gain[8] = {sd->i2c_intf, sd->i2c_addr,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+ if (gspca_dev->streaming)
+ gain[7] = 0x15; /* or 1d ? */
+
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_SOI968:
+ case SENSOR_OV9655:
+ case SENSOR_OV9650:
+ gain[0] |= (2 << 4);
+ gain[3] = ov_gain[g];
+ break;
+ case SENSOR_MT9V011:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x35;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
+ break;
+ case SENSOR_MT9V112:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
+ break;
+ case SENSOR_MT9M001:
+ gain[0] |= (3 << 4);
+ gain[2] = 0x2f;
+ gain[3] = micron2_gain[g] >> 8;
+ gain[4] = micron2_gain[g];
+ break;
+ case SENSOR_HV7131R:
+ gain[0] |= (2 << 4);
+ gain[2] = 0x30;
+ gain[3] = hv7131r_gain[g];
+ break;
+ default:
+ return;
+ }
+ i2c_w(gspca_dev, gain);
+}
+
+static void set_quality(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_set_qual(sd->jpeg_hdr, val);
+ reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
+ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
+ sd->fmt ^= 0x0c; /* invert QTAB use + write */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ reg_r(gspca_dev, reg->reg, 1);
+ reg->val = gspca_dev->usb_buf[0];
+ return gspca_dev->usb_err;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M112) {
+ i2c_r2(gspca_dev, reg->reg, (u16 *) &reg->val);
+ } else {
+ i2c_r1(gspca_dev, reg->reg, (u8 *) &reg->val);
+ }
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_register *reg)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (reg->match.addr != 0)
+ return -EINVAL;
+ if (reg->reg < 0x1000 || reg->reg > 0x11ff)
+ return -EINVAL;
+ reg_w1(gspca_dev, reg->reg, reg->val);
+ return gspca_dev->usb_err;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (reg->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ if (sd->sensor >= SENSOR_MT9V011 &&
+ sd->sensor <= SENSOR_MT9M112) {
+ i2c_w2(gspca_dev, reg->reg, reg->val);
+ } else {
+ i2c_w1(gspca_dev, reg->reg, reg->val);
+ }
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+#endif
+
+static int sd_chip_ident(struct gspca_dev *gspca_dev,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ if (chip->match.addr != 0)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = V4L2_IDENT_SN9C20X;
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ if (chip->match.addr != sd->i2c_addr)
+ return -EINVAL;
+ chip->revision = 0;
+ chip->ident = i2c_ident[sd->sensor];
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->needs_full_bandwidth = 1;
+
+ sd->sensor = id->driver_info >> 8;
+ sd->i2c_addr = id->driver_info;
+ sd->flags = id->driver_info >> 16;
+ sd->i2c_intf = 0x80; /* i2c 100 Kb/s */
+
+ switch (sd->sensor) {
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ case SENSOR_OV9650:
+ case SENSOR_SOI968:
+ cam->cam_mode = sxga_mode;
+ cam->nmodes = ARRAY_SIZE(sxga_mode);
+ break;
+ case SENSOR_MT9M001:
+ cam->cam_mode = mono_mode;
+ cam->nmodes = ARRAY_SIZE(mono_mode);
+ break;
+ case SENSOR_HV7131R:
+ sd->i2c_intf = 0x81; /* i2c 400 Kb/s */
+ /* fall thru */
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ }
+
+ sd->old_step = 0;
+ sd->older_step = 0;
+ sd->exposure_step = 16;
+
+ INIT_WORK(&sd->work, qual_upd);
+
+ return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ /* color control cluster */
+ case V4L2_CID_BRIGHTNESS:
+ set_cmatrix(gspca_dev, sd->brightness->val,
+ sd->contrast->val, sd->saturation->val, sd->hue->val);
+ break;
+ case V4L2_CID_GAMMA:
+ set_gamma(gspca_dev, ctrl->val);
+ break;
+ /* blue/red balance cluster */
+ case V4L2_CID_BLUE_BALANCE:
+ set_redblue(gspca_dev, sd->blue->val, sd->red->val);
+ break;
+ /* h/vflip cluster */
+ case V4L2_CID_HFLIP:
+ set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+ break;
+ /* standalone exposure control */
+ case V4L2_CID_EXPOSURE:
+ set_exposure(gspca_dev, ctrl->val);
+ break;
+ /* standalone gain control */
+ case V4L2_CID_GAIN:
+ set_gain(gspca_dev, ctrl->val);
+ break;
+ /* autogain + exposure or gain control cluster */
+ case V4L2_CID_AUTOGAIN:
+ if (sd->sensor == SENSOR_SOI968)
+ set_gain(gspca_dev, sd->gain->val);
+ else
+ set_exposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ set_quality(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 13);
+
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, -180, 180, 1, 0);
+
+ sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 255, 1, 0x10);
+
+ sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28);
+ sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28);
+
+ if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 &&
+ sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 &&
+ sd->sensor != SENSOR_MT9VPRB) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
+
+ if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB &&
+ sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 &&
+ sd->sensor != SENSOR_MT9V111)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33);
+
+ if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 &&
+ sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) {
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 28, 1, 0);
+ sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ }
+
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ v4l2_ctrl_cluster(4, &sd->brightness);
+ v4l2_ctrl_cluster(2, &sd->blue);
+ if (sd->hflip)
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ if (sd->autogain) {
+ if (sd->sensor == SENSOR_SOI968)
+ /* this sensor doesn't have the exposure control and
+ autogain is clustered with gain instead. This works
+ because sd->exposure == NULL. */
+ v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false);
+ else
+ /* Otherwise autogain is clustered with exposure. */
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
+ }
+ return 0;
+}
+
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u8 value;
+ u8 i2c_init[9] =
+ {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
+
+ for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
+ value = bridge_init[i][1];
+ reg_w(gspca_dev, bridge_init[i][0], &value, 1);
+ if (gspca_dev->usb_err < 0) {
+ pr_err("Device initialization failed\n");
+ return gspca_dev->usb_err;
+ }
+ }
+
+ if (sd->flags & LED_REVERSE)
+ reg_w1(gspca_dev, 0x1006, 0x00);
+ else
+ reg_w1(gspca_dev, 0x1006, 0x20);
+
+ reg_w(gspca_dev, 0x10c0, i2c_init, 9);
+ if (gspca_dev->usb_err < 0) {
+ pr_err("Device initialization failed\n");
+ return gspca_dev->usb_err;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_OV9650:
+ ov9650_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV9650 sensor detected\n");
+ break;
+ case SENSOR_OV9655:
+ ov9655_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV9655 sensor detected\n");
+ break;
+ case SENSOR_SOI968:
+ soi968_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("SOI968 sensor detected\n");
+ break;
+ case SENSOR_OV7660:
+ ov7660_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV7660 sensor detected\n");
+ break;
+ case SENSOR_OV7670:
+ ov7670_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("OV7670 sensor detected\n");
+ break;
+ case SENSOR_MT9VPRB:
+ mt9v_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9VPRB sensor detected\n");
+ break;
+ case SENSOR_MT9M111:
+ mt9m111_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9M111 sensor detected\n");
+ break;
+ case SENSOR_MT9M112:
+ mt9m112_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9M112 sensor detected\n");
+ break;
+ case SENSOR_MT9M001:
+ mt9m001_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ break;
+ case SENSOR_HV7131R:
+ hv7131r_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("HV7131R sensor detected\n");
+ break;
+ default:
+ pr_err("Unsupported sensor\n");
+ gspca_dev->usb_err = -ENODEV;
+ }
+ return gspca_dev->usb_err;
+}
+
+static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 value;
+
+ switch (sd->sensor) {
+ case SENSOR_SOI968:
+ if (mode & MODE_SXGA) {
+ i2c_w1(gspca_dev, 0x17, 0x1d);
+ i2c_w1(gspca_dev, 0x18, 0xbd);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x81);
+ i2c_w1(gspca_dev, 0x12, 0x00);
+ sd->hstart = 140;
+ sd->vstart = 19;
+ } else {
+ i2c_w1(gspca_dev, 0x17, 0x13);
+ i2c_w1(gspca_dev, 0x18, 0x63);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x79);
+ i2c_w1(gspca_dev, 0x12, 0x40);
+ sd->hstart = 60;
+ sd->vstart = 11;
+ }
+ break;
+ case SENSOR_OV9650:
+ if (mode & MODE_SXGA) {
+ i2c_w1(gspca_dev, 0x17, 0x1b);
+ i2c_w1(gspca_dev, 0x18, 0xbc);
+ i2c_w1(gspca_dev, 0x19, 0x01);
+ i2c_w1(gspca_dev, 0x1a, 0x82);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, value & 0x07);
+ } else {
+ i2c_w1(gspca_dev, 0x17, 0x24);
+ i2c_w1(gspca_dev, 0x18, 0xc5);
+ i2c_w1(gspca_dev, 0x19, 0x00);
+ i2c_w1(gspca_dev, 0x1a, 0x3c);
+ i2c_r1(gspca_dev, 0x12, &value);
+ i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40);
+ }
+ break;
+ case SENSOR_MT9M112:
+ case SENSOR_MT9M111:
+ if (mode & MODE_SXGA) {
+ i2c_w2(gspca_dev, 0xf0, 0x0002);
+ i2c_w2(gspca_dev, 0xc8, 0x970b);
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ } else {
+ i2c_w2(gspca_dev, 0xf0, 0x0002);
+ i2c_w2(gspca_dev, 0xc8, 0x8000);
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ }
+ break;
+ }
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_interface *intf;
+ u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv;
+
+ /*
+ * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth
+ * than our regular bandwidth calculations reserve, so we force the
+ * use of a specific altsetting when using the SN9C20X_I420 fmt.
+ */
+ if (!(flags & (MODE_RAW | MODE_JPEG))) {
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+
+ if (intf->num_altsetting != 9) {
+ pr_warn("sn9c20x camera with unknown number of alt "
+ "settings (%d), please report!\n",
+ intf->num_altsetting);
+ gspca_dev->alt = intf->num_altsetting;
+ return 0;
+ }
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ gspca_dev->alt = 2;
+ break;
+ case 320: /* 320x240 */
+ gspca_dev->alt = 6;
+ break;
+ default: /* >= 640x480 */
+ gspca_dev->alt = 9;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#define HW_WIN(mode, hstart, vstart) \
+((const u8 []){hstart, 0, vstart, 0, \
+(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
+(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
+
+#define CLR_WIN(width, height) \
+((const u8 [])\
+{0, width >> 2, 0, height >> 1,\
+((width >> 10) & 0x01) | ((height >> 8) & 0x6)})
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ int width = gspca_dev->width;
+ int height = gspca_dev->height;
+ u8 fmt, scale = 0;
+
+ jpeg_define(sd->jpeg_hdr, height, width,
+ 0x21);
+ jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
+
+ if (mode & MODE_RAW)
+ fmt = 0x2d;
+ else if (mode & MODE_JPEG)
+ fmt = 0x24;
+ else
+ fmt = 0x2f; /* YUV 420 */
+ sd->fmt = fmt;
+
+ switch (mode & SCALE_MASK) {
+ case SCALE_1280x1024:
+ scale = 0xc0;
+ pr_info("Set 1280x1024\n");
+ break;
+ case SCALE_640x480:
+ scale = 0x80;
+ pr_info("Set 640x480\n");
+ break;
+ case SCALE_320x240:
+ scale = 0x90;
+ pr_info("Set 320x240\n");
+ break;
+ case SCALE_160x120:
+ scale = 0xa0;
+ pr_info("Set 160x120\n");
+ break;
+ }
+
+ configure_sensor_output(gspca_dev, mode);
+ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5);
+ reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6);
+ reg_w1(gspca_dev, 0x1189, scale);
+ reg_w1(gspca_dev, 0x10e0, fmt);
+
+ set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness),
+ v4l2_ctrl_g_ctrl(sd->contrast),
+ v4l2_ctrl_g_ctrl(sd->saturation),
+ v4l2_ctrl_g_ctrl(sd->hue));
+ set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ if (sd->gain)
+ set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+ if (sd->exposure)
+ set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+ if (sd->hflip)
+ set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+ v4l2_ctrl_g_ctrl(sd->vflip));
+
+ reg_w1(gspca_dev, 0x1007, 0x20);
+ reg_w1(gspca_dev, 0x1061, 0x03);
+
+ /* if JPEG, prepare the compression quality update */
+ if (mode & MODE_JPEG) {
+ sd->pktsz = sd->npkt = 0;
+ sd->nchg = 0;
+ sd->work_thread =
+ create_singlethread_workqueue(KBUILD_MODNAME);
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ reg_w1(gspca_dev, 0x1007, 0x00);
+ reg_w1(gspca_dev, 0x1061, 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_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure);
+ s32 max = sd->exposure->maximum - sd->exposure_step;
+ s32 min = sd->exposure->minimum + sd->exposure_step;
+ s16 new_exp;
+
+ /*
+ * some hardcoded values are present
+ * like those for maximal/minimal exposure
+ * and exposure steps
+ */
+ if (avg_lum < MIN_AVG_LUM) {
+ if (cur_exp > max)
+ return;
+
+ new_exp = cur_exp + sd->exposure_step;
+ if (new_exp > max)
+ new_exp = max;
+ if (new_exp < min)
+ new_exp = min;
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
+
+ sd->older_step = sd->old_step;
+ sd->old_step = 1;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+ if (avg_lum > MAX_AVG_LUM) {
+ if (cur_exp < min)
+ return;
+ new_exp = cur_exp - sd->exposure_step;
+ if (new_exp > max)
+ new_exp = max;
+ if (new_exp < min)
+ new_exp = min;
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
+ sd->older_step = sd->old_step;
+ sd->old_step = 0;
+
+ if (sd->old_step ^ sd->older_step)
+ sd->exposure_step /= 2;
+ else
+ sd->exposure_step += 2;
+ }
+}
+
+static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
+
+ if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum)
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1);
+ if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum)
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1);
+}
+
+static void sd_dqcallback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum;
+
+ if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
+ return;
+
+ avg_lum = atomic_read(&sd->avg_lum);
+ if (sd->sensor == SENSOR_SOI968)
+ do_autogain(gspca_dev, avg_lum);
+ else
+ do_autoexposure(gspca_dev, avg_lum);
+}
+
+/* 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;
+ s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual);
+
+ /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", qual);
+ gspca_dev->usb_err = 0;
+ set_quality(gspca_dev, qual);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet */
+ int len) /* interrupt packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ return 0;
+ }
+ return -EINVAL;
+}
+#endif
+
+/* check the JPEG compression */
+static void transfer_check(struct gspca_dev *gspca_dev,
+ u8 *data)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int new_qual, r;
+
+ new_qual = 0;
+
+ /* if USB error, discard the frame and decrease the quality */
+ if (data[6] & 0x08) { /* USB FIFO full */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -5;
+ } else {
+
+ /* else, compute the filling rate and a new JPEG quality */
+ 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) {
+ /* Note: we are in interrupt context, so we can't
+ use v4l2_ctrl_g/s_ctrl here. Access the value
+ directly instead. */
+ s32 curqual = sd->jpegqual->cur.val;
+ sd->nchg = 0;
+ new_qual += curqual;
+ if (new_qual < sd->jpegqual->minimum)
+ new_qual = sd->jpegqual->minimum;
+ else if (new_qual > sd->jpegqual->maximum)
+ new_qual = sd->jpegqual->maximum;
+ if (new_qual != curqual) {
+ sd->jpegqual->cur.val = new_qual;
+ queue_work(sd->work_thread, &sd->work);
+ }
+ }
+ } else {
+ sd->nchg = 0;
+ }
+ sd->pktsz = sd->npkt = 0;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum, is_jpeg;
+ static const u8 frame_header[] =
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+
+ is_jpeg = (sd->fmt & 0x03) == 0;
+ if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
+ avg_lum = ((data[35] >> 2) & 3) |
+ (data[20] << 2) |
+ (data[19] << 10);
+ avg_lum += ((data[35] >> 4) & 3) |
+ (data[22] << 2) |
+ (data[21] << 10);
+ avg_lum += ((data[35] >> 6) & 3) |
+ (data[24] << 2) |
+ (data[23] << 10);
+ avg_lum += (data[36] & 3) |
+ (data[26] << 2) |
+ (data[25] << 10);
+ avg_lum += ((data[36] >> 2) & 3) |
+ (data[28] << 2) |
+ (data[27] << 10);
+ avg_lum += ((data[36] >> 4) & 3) |
+ (data[30] << 2) |
+ (data[29] << 10);
+ avg_lum += ((data[36] >> 6) & 3) |
+ (data[32] << 2) |
+ (data[31] << 10);
+ avg_lum += ((data[44] >> 4) & 3) |
+ (data[34] << 2) |
+ (data[33] << 10);
+ avg_lum >>= 9;
+ atomic_set(&sd->avg_lum, avg_lum);
+
+ if (is_jpeg)
+ transfer_check(gspca_dev, data);
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ len -= 64;
+ if (len == 0)
+ return;
+ data += 64;
+ }
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+ if (is_jpeg) {
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ }
+ } else {
+ /* if JPEG, count the packets and their size */
+ if (is_jpeg) {
+ sd->npkt++;
+ sd->pktsz += len;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+ .dq_callback = sd_dqcallback,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .set_register = sd_dbg_s_register,
+ .get_register = sd_dbg_g_register,
+#endif
+ .get_chip_ident = sd_chip_ident,
+};
+
+#define SN9C20X(sensor, i2c_addr, flags) \
+ .driver_info = ((flags & 0xff) << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (i2c_addr)
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)},
+ {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30,
+ (FLIP_DETECT | HAS_NO_BUTTON))},
+ {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
+ {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, 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)},
+ {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
+ {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
+ {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
+ {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
+ {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)},
+ {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)},
+ {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)},
+ {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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 = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index fd1f8d2d3b0b..fd1f8d2d3b0b 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
diff --git a/drivers/media/usb/gspca/sonixj.c b/drivers/media/usb/gspca/sonixj.c
new file mode 100644
index 000000000000..5a86047b846f
--- /dev/null
+++ b/drivers/media/usb/gspca/sonixj.c
@@ -0,0 +1,3208 @@
+/*
+ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
+ *
+ * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.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; 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sonixj"
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* controls */
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ COLORS,
+ BLUE,
+ RED,
+ GAMMA,
+ EXPOSURE,
+ AUTOGAIN,
+ GAIN,
+ HFLIP,
+ VFLIP,
+ SHARPNESS,
+ ILLUM,
+ FREQ,
+ NCTRLS /* number of controls */
+};
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ atomic_t avg_lum;
+ u32 exposure;
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ s8 nchg;
+ s8 short_mark;
+
+ u8 quality; /* image quality */
+#define QUALITY_MIN 25
+#define QUALITY_MAX 90
+#define QUALITY_DEF 70
+
+ u8 reg01;
+ u8 reg17;
+ u8 reg18;
+ u8 flags;
+
+ s8 ag_cnt;
+#define AG_CNT_START 13
+
+ u8 bridge;
+#define BRIDGE_SN9C102P 0
+#define BRIDGE_SN9C105 1
+#define BRIDGE_SN9C110 2
+#define BRIDGE_SN9C120 3
+ u8 sensor; /* Type of image sensor chip */
+ u8 i2c_addr;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum sensors {
+ SENSOR_ADCM1700,
+ SENSOR_GC0307,
+ SENSOR_HV7131R,
+ SENSOR_MI0360,
+ SENSOR_MI0360B,
+ SENSOR_MO4000,
+ SENSOR_MT9V111,
+ SENSOR_OM6802,
+ SENSOR_OV7630,
+ SENSOR_OV7648,
+ SENSOR_OV7660,
+ SENSOR_PO1030,
+ SENSOR_PO2030N,
+ SENSOR_SOI768,
+ 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 */
+
+/* sn9c1xx definitions */
+/* register 0x01 */
+#define S_PWR_DN 0x01 /* sensor power down */
+#define S_PDN_INV 0x02 /* inverse pin S_PWR_DN */
+#define V_TX_EN 0x04 /* video transfer enable */
+#define LED 0x08 /* output to pin LED */
+#define SCL_SEL_OD 0x20 /* open-drain mode */
+#define SYS_SEL_48M 0x40 /* system clock 0: 24MHz, 1: 48MHz */
+/* register 0x17 */
+#define MCK_SIZE_MASK 0x1f /* sensor master clock */
+#define SEN_CLK_EN 0x20 /* enable sensor clock */
+#define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */
+
+/* V4L2 controls supported by the driver */
+static void setbrightness(struct gspca_dev *gspca_dev);
+static void setcontrast(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setredblue(struct gspca_dev *gspca_dev);
+static void setgamma(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setgain(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
+static void setsharpness(struct gspca_dev *gspca_dev);
+static void setillum(struct gspca_dev *gspca_dev);
+static void setfreq(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x80,
+ },
+ .set_control = setbrightness
+ },
+[CONTRAST] = {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+#define CONTRAST_MAX 127
+ .maximum = CONTRAST_MAX,
+ .step = 1,
+ .default_value = 20,
+ },
+ .set_control = setcontrast
+ },
+[COLORS] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 40,
+ .step = 1,
+#define COLORS_DEF 25
+ .default_value = COLORS_DEF,
+ },
+ .set_control = setcolors
+ },
+[BLUE] = {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+ .default_value = 32,
+ },
+ .set_control = setredblue
+ },
+[RED] = {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+ .default_value = 32,
+ },
+ .set_control = setredblue
+ },
+[GAMMA] = {
+ {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+ .minimum = 0,
+ .maximum = 40,
+ .step = 1,
+#define GAMMA_DEF 20
+ .default_value = GAMMA_DEF,
+ },
+ .set_control = setgamma
+ },
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 500,
+ .maximum = 1500,
+ .step = 1,
+ .default_value = 1024
+ },
+ .set_control = setexposure
+ },
+[AUTOGAIN] = {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Auto Gain",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1
+ },
+ .set = sd_setautogain,
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 4,
+ .maximum = 49,
+ .step = 1,
+ .default_value = 15
+ },
+ .set_control = setgain
+ },
+[HFLIP] = {
+ {
+ .id = V4L2_CID_HFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mirror",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[VFLIP] = {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethvflip
+ },
+[SHARPNESS] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 90,
+ },
+ .set_control = setsharpness
+ },
+[ILLUM] = {
+ {
+ .id = V4L2_CID_ILLUMINATORS_1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Illuminator / infrared",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = setillum
+ },
+/* ov7630/ov7648/ov7660 only */
+[FREQ] = {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .name = "Light frequency filter",
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+ .default_value = 1,
+ },
+ .set_control = setfreq
+ },
+};
+
+/* table of the disabled controls */
+static const __u32 ctrl_dis[] = {
+[SENSOR_ADCM1700] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_GC0307] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_HV7131R] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MI0360] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MI0360B] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MO4000] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_MT9V111] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_OM6802] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_OV7630] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
+
+[SENSOR_OV7648] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
+
+[SENSOR_OV7660] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP),
+
+[SENSOR_PO1030] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_PO2030N] = (1 << FREQ),
+
+[SENSOR_SOI768] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+
+[SENSOR_SP80708] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
+ (1 << VFLIP) |
+ (1 << FREQ),
+};
+
+static const struct v4l2_pix_format cif_mode[] = {
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */
+ .sizeimage = 640 * 480 * 3 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const u8 sn_adcm1700[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_gc0307[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_hv7131[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
+/* reg18 reg19 reg1a reg1b */
+ 0x0a, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mi0360[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 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 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mi0360b[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mo4000[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x08, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_mt9v111[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x06, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_om6802[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40,
+/* reg18 reg19 reg1a reg1b */
+ 0x05, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7630[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
+/* reg18 reg19 reg1a reg1b */
+ 0x0b, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7648[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x0b, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_ov7660[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_po1030[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_po2030n[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_soi768[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+static const u8 sn_sp80708[0x1c] = {
+/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
+/* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
+/* reg18 reg19 reg1a reg1b */
+ 0x07, 0x00, 0x00, 0x00
+};
+
+/* sequence specific to the sensors - !! index = SENSOR_xxx */
+static const u8 *sn_tb[] = {
+[SENSOR_ADCM1700] = sn_adcm1700,
+[SENSOR_GC0307] = sn_gc0307,
+[SENSOR_HV7131R] = sn_hv7131,
+[SENSOR_MI0360] = sn_mi0360,
+[SENSOR_MI0360B] = sn_mi0360b,
+[SENSOR_MO4000] = sn_mo4000,
+[SENSOR_MT9V111] = sn_mt9v111,
+[SENSOR_OM6802] = sn_om6802,
+[SENSOR_OV7630] = sn_ov7630,
+[SENSOR_OV7648] = sn_ov7648,
+[SENSOR_OV7660] = sn_ov7660,
+[SENSOR_PO1030] = sn_po1030,
+[SENSOR_PO2030N] = sn_po2030n,
+[SENSOR_SOI768] = sn_soi768,
+[SENSOR_SP80708] = sn_sp80708,
+};
+
+/* default gamma table */
+static const u8 gamma_def[17] = {
+ 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+ 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+};
+/* gamma for sensor ADCM1700 */
+static const u8 gamma_spec_0[17] = {
+ 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
+ 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensors HV7131R and MT9V111 */
+static const u8 gamma_spec_1[17] = {
+ 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
+ 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5
+};
+/* gamma for sensor GC0307 */
+static const u8 gamma_spec_2[17] = {
+ 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab,
+ 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb
+};
+/* gamma for sensor SP80708 */
+static const u8 gamma_spec_3[17] = {
+ 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab,
+ 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6
+};
+
+/* color matrix and offsets */
+static const u8 reg84[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
+ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
+};
+
+#define DELAY 0xdd
+
+static const u8 adcm1700_sensor_init[][8] = {
+ {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 adcm1700_sensor_param1[][8] = {
+ {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */
+ {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
+ {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */
+
+ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 gc0307_sensor_init[][8] = {
+ {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
+ {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 gc0307_sensor_param1[][8] = {
+ {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10},
+ {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10},
+ {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10},
+ {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10},
+/*param3*/
+ {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 hv7131r_sensor_init[][8] = {
+ {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
+ {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10},
+/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+ {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10},
+/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10},
+ {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10},
+ {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10},
+ {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
+ {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* set sensor clock */
+ {}
+};
+static const u8 mi0360_sensor_init[][8] = {
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+ {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+ {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+ {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+ {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
+ {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+ {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10},
+
+ {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
+ {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+ {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */
+/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
+/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
+ {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+};
+static const u8 mi0360b_sensor_init[][8] = {
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+ {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
+ {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+ {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
+ {0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+ {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
+ {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+ {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+ {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
+ {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+ {0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10},
+ {}
+};
+static const u8 mi0360b_sensor_param1[][8] = {
+ {0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10},
+ {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+
+ {0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10},
+ {0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10},
+ {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+};
+static const u8 mo4000_sensor_init[][8] = {
+ {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 mt9v111_sensor_init[][8] = {
+ {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {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, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
+ {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
+ {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 */
+ {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */
+ {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */
+ {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */
+ {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */
+ {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 mt9v111_sensor_param1[][8] = {
+ {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 */
+ {}
+};
+static const u8 om6802_init0[2][8] = {
+/*fixme: variable*/
+ {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
+};
+static const u8 om6802_sensor_init[][8] = {
+ {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
+ /* factory mode */
+ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
+ /* output raw RGB */
+ {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
+ /* auto-exposure speed (0) / white balance mode (auto RGB) */
+/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
+ * set color mode */
+/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
+ * max AGC value in AE */
+/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset AGC */
+/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset brightness */
+/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * preset contrast */
+/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
+ * preset gamma */
+ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
+ /* luminance mode (0x4f -> AutoExpo on) */
+ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
+ /* preset shutter */
+/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * auto frame rate */
+/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 om6802_sensor_param1[][8] = {
+ {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7630_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/* win: i2c_r from 00 to 80 */
+ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
+ {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10},
+/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30)
+ 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */
+ {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10},
+ {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10},
+ {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10},
+ {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10},
+ {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10},
+ {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10},
+ {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10},
+ {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10},
+ {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10},
+ {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10},
+ {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7630_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+/*fixme: + 0x12, 0x04*/
+/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10},
+/* */
+/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
+/* */
+ {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10},
+/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
+ {}
+};
+
+static const u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+ {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+ {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
+ {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
+ {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
+ {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
+ {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
+ {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
+ {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
+ {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
+ {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
+ {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
+ {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
+ {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
+ {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
+
+ {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
+/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
+ {}
+};
+static const u8 ov7648_sensor_param1[][8] = {
+/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
+ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */
+/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */
+/*...*/
+ {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
+/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */
+
+ {}
+};
+
+static const u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
+ /* Outformat = rawRGB */
+ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
+ {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10},
+ /* GAIN BLUE RED VREF */
+ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
+ /* COM 1 BAVE GEAVE AECHH */
+ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
+ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
+ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
+ /* AECH CLKRC COM7 COM8 */
+ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
+ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
+ /* HSTART HSTOP VSTRT VSTOP */
+ {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */
+ {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */
+ {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10},
+ /* BOS GBOS GROS ROS (BGGR offset) */
+/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, */
+ {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10},
+ /* AEW AEB VPT BBIAS */
+ {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10},
+ /* GbBIAS RSVD EXHCH EXHCL */
+ {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10},
+ /* RBIAS ADVFL ASDVFH YAVE */
+ {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10},
+ /* HSYST HSYEN HREF */
+ {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10},
+ /* ADC ACOM OFON TSLB */
+ {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10},
+ /* COM11 COM12 COM13 COM14 */
+ {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10},
+ /* EDGE COM15 COM16 COM17 */
+ {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */
+ {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */
+ {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */
+ {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */
+ {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */
+ {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10},
+ /* LCC1 LCC2 LCC3 LCC4 */
+ {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */
+ {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, /* MANU */
+ {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ /* band gap reference [0:3] DBLV */
+ {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */
+ {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
+ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
+/* not in all ms-win traces*/
+ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 ov7660_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
+ /* bits[3..0]reserved */
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ /* VREF vertical frame ctrl */
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* AECH 0x20 */
+ {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */
+ {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
+/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
+/****** (some exchanges in the win trace) ******/
+/*fixme:param2*/
+ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
+ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
+ {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */
+/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */
+/****** (some exchanges in the win trace) ******/
+/******!! startsensor KO if changed !!****/
+/*fixme: param3*/
+ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 po1030_sensor_init[][8] = {
+/* the sensor registers are described in m5602/m5602_po1030.h */
+ {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
+ {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
+ {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
+ {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
+ {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
+ {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
+ {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
+ {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
+ {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
+ {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
+ {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
+ {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
+ {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
+ {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
+ {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
+ {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
+ {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
+
+ {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
+ {}
+};
+static const u8 po1030_sensor_param1[][8] = {
+/* from ms-win traces - these values change with auto gain/expo/wb.. */
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
+/* mean values */
+ {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
+ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
+ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
+
+ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
+ {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
+/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
+ {}
+};
+
+static const u8 po2030n_sensor_init[][8] = {
+ {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+ {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
+ {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10},
+ {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10},
+ {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10},
+ {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10},
+ {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10},
+ {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10},
+ {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10},
+ {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10},
+ {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10},
+ {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10},
+ {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10},
+ {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10},
+ {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10},
+ {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 po2030n_sensor_param1[][8] = {
+ {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
+ {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */
+/*param2*/
+ {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 soi768_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 soi768_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10},
+/* */
+/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
+/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10},
+/* the next sequence should be used for auto gain */
+ {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10},
+ /* global gain ? : 07 - change with 0x15 at the end */
+ {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
+ {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
+ /* exposure ? : 0200 - change with 0x1e at the end */
+ {}
+};
+
+static const u8 sp80708_sensor_init[][8] = {
+ {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
+ {}
+};
+static const u8 sp80708_sensor_param1[][8] = {
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10},
+ {}
+};
+
+static const u8 (*sensor_init[])[8] = {
+[SENSOR_ADCM1700] = adcm1700_sensor_init,
+[SENSOR_GC0307] = gc0307_sensor_init,
+[SENSOR_HV7131R] = hv7131r_sensor_init,
+[SENSOR_MI0360] = mi0360_sensor_init,
+[SENSOR_MI0360B] = mi0360b_sensor_init,
+[SENSOR_MO4000] = mo4000_sensor_init,
+[SENSOR_MT9V111] = mt9v111_sensor_init,
+[SENSOR_OM6802] = om6802_sensor_init,
+[SENSOR_OV7630] = ov7630_sensor_init,
+[SENSOR_OV7648] = ov7648_sensor_init,
+[SENSOR_OV7660] = ov7660_sensor_init,
+[SENSOR_PO1030] = po1030_sensor_init,
+[SENSOR_PO2030N] = po2030n_sensor_init,
+[SENSOR_SOI768] = soi768_sensor_init,
+[SENSOR_SP80708] = sp80708_sensor_init,
+};
+
+/* read <len> bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 value, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_r: buffer overflow\n");
+ return;
+ }
+#endif
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w1(struct gspca_dev *gspca_dev,
+ u16 value,
+ u8 data)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
+ gspca_dev->usb_buf[0] = data;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value,
+ 0,
+ gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w1 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 value,
+ const u8 *buffer,
+ int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
+ value, buffer[0], buffer[1]);
+#ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ pr_err("reg_w: buffer overflow\n");
+ return;
+ }
+#endif
+ memcpy(gspca_dev->usb_buf, buffer, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ value, 0,
+ gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* I2C write 1 byte */
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OM6802:
+ case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
+ gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
+ break;
+ default: /* i2c command = a1 (400 kHz) */
+ gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
+ break;
+ }
+ gspca_dev->usb_buf[1] = sd->i2c_addr;
+ gspca_dev->usb_buf[2] = reg;
+ gspca_dev->usb_buf[3] = val;
+ gspca_dev->usb_buf[4] = 0;
+ gspca_dev->usb_buf[5] = 0;
+ gspca_dev->usb_buf[6] = 0;
+ gspca_dev->usb_buf[7] = 0x10;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x08, /* value = i2c */
+ 0,
+ gspca_dev->usb_buf, 8,
+ 500);
+ if (ret < 0) {
+ pr_err("i2c_w1 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* I2C write 8 bytes */
+static void i2c_w8(struct gspca_dev *gspca_dev,
+ const u8 *buffer)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..",
+ buffer[2], buffer[3]);
+ memcpy(gspca_dev->usb_buf, buffer, 8);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x08,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x08, 0, /* value, index */
+ gspca_dev->usb_buf, 8,
+ 500);
+ msleep(2);
+ if (ret < 0) {
+ pr_err("i2c_w8 err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
+static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 mode[8];
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OM6802:
+ case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
+ mode[0] = 0x80 | 0x10;
+ break;
+ default: /* i2c command = 91 (400 kHz) */
+ mode[0] = 0x81 | 0x10;
+ break;
+ }
+ mode[1] = sd->i2c_addr;
+ mode[2] = reg;
+ mode[3] = 0;
+ mode[4] = 0;
+ mode[5] = 0;
+ mode[6] = 0;
+ mode[7] = 0x10;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
+ mode[2] = 0;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+}
+
+static void i2c_w_seq(struct gspca_dev *gspca_dev,
+ const u8 (*data)[8])
+{
+ while ((*data)[0] != 0) {
+ if ((*data)[0] != DELAY)
+ i2c_w8(gspca_dev, *data);
+ else
+ msleep((*data)[1]);
+ data++;
+ }
+}
+
+/* check the ID of the hv7131 sensor */
+/* this sequence is needed because it activates the sensor */
+static void hv7131r_probe(struct gspca_dev *gspca_dev)
+{
+ i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
+ msleep(10);
+ i2c_r(gspca_dev, 0, 5); /* read sensor id */
+ if (gspca_dev->usb_buf[0] == 0x02 /* chip ID (02 is R) */
+ && gspca_dev->usb_buf[1] == 0x09
+ && gspca_dev->usb_buf[2] == 0x01) {
+ PDEBUG(D_PROBE, "Sensor HV7131R found");
+ return;
+ }
+ pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+}
+
+static void mi0360_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, j;
+ u16 val = 0;
+ static const u8 probe_tb[][4][8] = {
+ { /* mi0360 */
+ {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+ {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}
+ },
+ { /* mt9v111 */
+ {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10},
+ {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {}
+ },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(probe_tb); i++) {
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ for (j = 0; j < 3; j++)
+ i2c_w8(gspca_dev, probe_tb[i][j]);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ if (probe_tb[i][3][0] != 0)
+ i2c_w8(gspca_dev, probe_tb[i][3]);
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (val != 0xffff)
+ break;
+ }
+ if (gspca_dev->usb_err < 0)
+ return;
+ switch (val) {
+ case 0x8221:
+ PDEBUG(D_PROBE, "Sensor mi0360b");
+ sd->sensor = SENSOR_MI0360B;
+ break;
+ case 0x823a:
+ PDEBUG(D_PROBE, "Sensor mt9v111");
+ sd->sensor = SENSOR_MT9V111;
+ break;
+ case 0x8243:
+ PDEBUG(D_PROBE, "Sensor mi0360");
+ break;
+ default:
+ PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val);
+ break;
+ }
+}
+
+static void ov7630_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check ov76xx */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x0a, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x7628) { /* soi768 */
+ sd->sensor = SENSOR_SOI768;
+/*fixme: only valid for 0c45:613e?*/
+ gspca_dev->cam.input_flags =
+ V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
+ PDEBUG(D_PROBE, "Sensor soi768");
+ return;
+ }
+ PDEBUG(D_PROBE, "Sensor ov%04x", val);
+}
+
+static void ov7648_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check ov76xx */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x0a, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if ((val & 0xff00) == 0x7600) { /* ov76xx */
+ PDEBUG(D_PROBE, "Sensor ov%04x", val);
+ return;
+ }
+
+ /* check po1030 */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ sd->i2c_addr = 0x6e;
+ i2c_r(gspca_dev, 0x00, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x1030) { /* po1030 */
+ PDEBUG(D_PROBE, "Sensor po1030");
+ sd->sensor = SENSOR_PO1030;
+ return;
+ }
+ pr_err("Unknown sensor %04x\n", val);
+}
+
+/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
+static void po2030n_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 val;
+
+ /* check gc0307 */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x08);
+ reg_w1(gspca_dev, 0x02, 0x22);
+ sd->i2c_addr = 0x21;
+ i2c_r(gspca_dev, 0x00, 1);
+ val = gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29); /* reset */
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (val == 0x99) { /* gc0307 (?) */
+ PDEBUG(D_PROBE, "Sensor gc0307");
+ sd->sensor = SENSOR_GC0307;
+ return;
+ }
+
+ /* check po2030n */
+ reg_w1(gspca_dev, 0x17, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x0a);
+ sd->i2c_addr = 0x6e;
+ i2c_r(gspca_dev, 0x00, 2);
+ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
+ reg_w1(gspca_dev, 0x01, 0x29);
+ reg_w1(gspca_dev, 0x17, 0x42);
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (val == 0x2030) {
+ PDEBUG(D_PROBE, "Sensor po2030n");
+/* sd->sensor = SENSOR_PO2030N; */
+ } else {
+ pr_err("Unknown sensor ID %04x\n", val);
+ }
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->bridge = id->driver_info >> 16;
+ sd->sensor = id->driver_info >> 8;
+ sd->flags = id->driver_info;
+
+ cam = &gspca_dev->cam;
+ if (sd->sensor == SENSOR_ADCM1700) {
+ cam->cam_mode = cif_mode;
+ cam->nmodes = ARRAY_SIZE(cif_mode);
+ } else {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ }
+ cam->npkt = 24; /* 24 packets per ISOC message */
+ cam->ctrls = sd->ctrls;
+
+ sd->ag_cnt = -1;
+ sd->quality = QUALITY_DEF;
+
+ INIT_WORK(&sd->work, qual_upd);
+
+ 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;
+ const u8 *sn9c1xx;
+ u8 regGpio[] = { 0x29, 0x70 }; /* no audio */
+ u8 regF1;
+
+ /* setup a selector by bridge */
+ reg_w1(gspca_dev, 0xf1, 0x01);
+ reg_r(gspca_dev, 0x00, 1);
+ reg_w1(gspca_dev, 0xf1, 0x00);
+ reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */
+ regF1 = gspca_dev->usb_buf[0];
+ 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:
+ if (regF1 != 0x11)
+ return -ENODEV;
+ break;
+ default:
+/* case BRIDGE_SN9C110: */
+/* case BRIDGE_SN9C120: */
+ if (regF1 != 0x12)
+ return -ENODEV;
+ }
+
+ switch (sd->sensor) {
+ case SENSOR_MI0360:
+ mi0360_probe(gspca_dev);
+ break;
+ case SENSOR_OV7630:
+ ov7630_probe(gspca_dev);
+ break;
+ case SENSOR_OV7648:
+ ov7648_probe(gspca_dev);
+ break;
+ case SENSOR_PO2030N:
+ po2030n_probe(gspca_dev);
+ break;
+ }
+
+ switch (sd->bridge) {
+ case BRIDGE_SN9C102P:
+ reg_w1(gspca_dev, 0x02, regGpio[1]);
+ break;
+ default:
+ reg_w(gspca_dev, 0x01, regGpio, 2);
+ break;
+ }
+
+ if (sd->sensor == SENSOR_OM6802)
+ sd->ctrls[SHARPNESS].def = 0x10;
+
+ /* Note we do not disable the sensor clock here (power saving mode),
+ as that also disables the button on the cam. */
+ reg_w1(gspca_dev, 0xf1, 0x00);
+
+ /* set the i2c address */
+ sn9c1xx = sn_tb[sd->sensor];
+ sd->i2c_addr = sn9c1xx[9];
+
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+ if (!(sd->flags & F_ILLUM))
+ gspca_dev->ctrl_dis |= (1 << ILLUM);
+
+ return gspca_dev->usb_err;
+}
+
+static u32 expo_adjust(struct gspca_dev *gspca_dev,
+ u32 expo)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_GC0307: {
+ int a, b;
+
+ /* expo = 0..255 -> a = 19..43 */
+ a = 19 + expo * 25 / 256;
+ i2c_w1(gspca_dev, 0x68, a);
+ a -= 12;
+ b = a * a * 4; /* heuristic */
+ i2c_w1(gspca_dev, 0x03, b >> 8);
+ i2c_w1(gspca_dev, 0x04, b);
+ break;
+ }
+ case SENSOR_HV7131R: {
+ u8 Expodoit[] =
+ { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
+
+ Expodoit[3] = expo >> 16;
+ Expodoit[4] = expo >> 8;
+ Expodoit[5] = expo;
+ i2c_w8(gspca_dev, Expodoit);
+ break;
+ }
+ case SENSOR_MI0360:
+ case SENSOR_MI0360B: {
+ u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
+ { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
+ static const u8 doit[] = /* update sensor */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+ static const u8 sensorgo[] = /* sensor on */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x0635)
+ expo = 0x0635;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMi[3] = expo >> 8;
+ expoMi[4] = expo;
+ i2c_w8(gspca_dev, expoMi);
+ i2c_w8(gspca_dev, doit);
+ i2c_w8(gspca_dev, sensorgo);
+ break;
+ }
+ case SENSOR_MO4000: {
+ u8 expoMof[] =
+ { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ u8 expoMo10[] =
+ { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 gainMo[] =
+ { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+ if (expo > 0x1fff)
+ expo = 0x1fff;
+ else if (expo < 0x0001)
+ expo = 0x0001;
+ expoMof[3] = (expo & 0x03fc) >> 2;
+ i2c_w8(gspca_dev, expoMof);
+ expoMo10[3] = ((expo & 0x1c00) >> 10)
+ | ((expo & 0x0003) << 4);
+ i2c_w8(gspca_dev, expoMo10);
+ i2c_w8(gspca_dev, gainMo);
+ PDEBUG(D_FRAM, "set exposure %d",
+ ((expoMo10[3] & 0x07) << 10)
+ | (expoMof[3] << 2)
+ | ((expoMo10[3] & 0x30) >> 4));
+ break;
+ }
+ case SENSOR_MT9V111: {
+ u8 expo_c1[] =
+ { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
+
+ 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);
+ break;
+ }
+ case SENSOR_OM6802: {
+ u8 gainOm[] =
+ { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ /* preset AGC - works when AutoExpo = off */
+
+ if (expo > 0x03ff)
+ expo = 0x03ff;
+ if (expo < 0x0001)
+ expo = 0x0001;
+ gainOm[3] = expo >> 2;
+ i2c_w8(gspca_dev, gainOm);
+ reg_w1(gspca_dev, 0x96, expo >> 5);
+ PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
+ break;
+ }
+ }
+ return expo;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int expo;
+ int brightness;
+ u8 k2;
+
+ brightness = sd->ctrls[BRIGHTNESS].val;
+ k2 = (brightness - 0x80) >> 2;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ if (k2 > 0x1f)
+ k2 = 0; /* only positive Y offset */
+ break;
+ case SENSOR_HV7131R:
+ expo = brightness << 12;
+ if (expo > 0x002dc6c0)
+ expo = 0x002dc6c0;
+ else if (expo < 0x02a0)
+ expo = 0x02a0;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_MI0360:
+ case SENSOR_MO4000:
+ expo = brightness << 4;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_MI0360B:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ break;
+ case SENSOR_GC0307:
+ expo = brightness;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* don't set the Y offset */
+ case SENSOR_MT9V111:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* don't set the Y offset */
+ case SENSOR_OM6802:
+ expo = brightness << 2;
+ sd->exposure = expo_adjust(gspca_dev, expo);
+ return; /* Y offset already set */
+ }
+
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 k2;
+ u8 contrast[6];
+
+ k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
+ + 37; /* 37..73 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+ contrast[1] = 0;
+ contrast[2] = k2; /* green */
+ contrast[3] = 0;
+ contrast[4] = k2 / 5; /* blue */
+ contrast[5] = 0;
+ reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v, colors;
+ const s16 *uv;
+ u8 reg8a[12]; /* U & V gains */
+ static const s16 uv_com[6] = { /* same as reg84 in signed decimal */
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
+ static const s16 uv_mi0360b[6] = {
+ -20, -38, 64, /* UR UG UB */
+ 60, -51, -9 /* VR VG VB */
+ };
+
+ colors = sd->ctrls[COLORS].val;
+ if (sd->sensor == SENSOR_MI0360B)
+ uv = uv_mi0360b;
+ else
+ uv = uv_com;
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * colors / COLORS_DEF;
+ reg8a[i * 2] = v;
+ reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
+ }
+ reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rg1b[] = /* red green1 blue (no g2) */
+ {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+ /* 0x40 = normal value = gain x 1 */
+ rg1b[3] = sd->ctrls[RED].val * 2;
+ rg1b[5] = sd->ctrls[BLUE].val * 2;
+ i2c_w8(gspca_dev, rg1b);
+ return;
+ }
+ reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val);
+/* reg_w1(gspca_dev, 0x07, 32); */
+ reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, val;
+ u8 gamma[17];
+ const u8 *gamma_base;
+ static const u8 delta[17] = {
+ 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a,
+ 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00
+ };
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ gamma_base = gamma_spec_0;
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360B:
+ case SENSOR_MT9V111:
+ gamma_base = gamma_spec_1;
+ break;
+ case SENSOR_GC0307:
+ gamma_base = gamma_spec_2;
+ break;
+ case SENSOR_SP80708:
+ gamma_base = gamma_spec_3;
+ break;
+ default:
+ gamma_base = gamma_def;
+ break;
+ }
+
+ val = sd->ctrls[GAMMA].val;
+ for (i = 0; i < sizeof gamma; i++)
+ gamma[i] = gamma_base[i]
+ + delta[i] * (val - GAMMA_DEF) / 32;
+ reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rexpo[] = /* 1a: expo H, 1b: expo M */
+ {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+ rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
+ i2c_w8(gspca_dev, rexpo);
+ msleep(6);
+ rexpo[2] = 0x1b;
+ rexpo[3] = sd->ctrls[EXPOSURE].val;
+ i2c_w8(gspca_dev, rexpo);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
+ return;
+ switch (sd->sensor) {
+ case SENSOR_OV7630:
+ case SENSOR_OV7648: {
+ u8 comb;
+
+ if (sd->sensor == SENSOR_OV7630)
+ comb = 0xc0;
+ else
+ comb = 0xa0;
+ if (sd->ctrls[AUTOGAIN].val)
+ comb |= 0x03;
+ i2c_w1(&sd->gspca_dev, 0x13, comb);
+ return;
+ }
+ }
+ if (sd->ctrls[AUTOGAIN].val)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+}
+
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rgain[] = /* 15: gain */
+ {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
+
+ rgain[3] = sd->ctrls[GAIN].val;
+ i2c_w8(gspca_dev, rgain);
+ }
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 comn;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ comn = 0x18; /* clkdiv = 1, ablcen = 1 */
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x01;
+ i2c_w1(gspca_dev, 0x01, comn); /* sctra */
+ break;
+ case SENSOR_OV7630:
+ comn = 0x02;
+ if (!sd->ctrls[VFLIP].val)
+ comn |= 0x80;
+ i2c_w1(gspca_dev, 0x75, comn);
+ break;
+ case SENSOR_OV7648:
+ comn = 0x06;
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x80;
+ i2c_w1(gspca_dev, 0x75, comn);
+ break;
+ case SENSOR_PO2030N:
+ /* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2)
+ * (reset value: 0x0A)
+ * bit7: HM: Horizontal Mirror: 0: disable, 1: enable
+ * bit6: VM: Vertical Mirror: 0: disable, 1: enable
+ * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical
+ * bit4: FT: Single Frame Transfer: 0: disable, 1: enable
+ * bit3-0: X
+ */
+ comn = 0x0a;
+ if (sd->ctrls[HFLIP].val)
+ comn |= 0x80;
+ if (sd->ctrls[VFLIP].val)
+ comn |= 0x40;
+ i2c_w1(&sd->gspca_dev, 0x1e, comn);
+ break;
+ }
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val);
+}
+
+static void setillum(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << ILLUM))
+ return;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ reg_w1(gspca_dev, 0x02, /* gpio */
+ sd->ctrls[ILLUM].val ? 0x64 : 0x60);
+ break;
+ case SENSOR_MT9V111:
+ reg_w1(gspca_dev, 0x02,
+ sd->ctrls[ILLUM].val ? 0x77 : 0x74);
+/* should have been: */
+/* 0x55 : 0x54); * 370i */
+/* 0x66 : 0x64); * Clip */
+ break;
+ }
+}
+
+static void setfreq(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << FREQ))
+ return;
+ if (sd->sensor == SENSOR_OV7660) {
+ u8 com8;
+
+ com8 = 0xdf; /* auto gain/wb/expo */
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ i2c_w1(gspca_dev, 0x13, com8 | 0x20);
+ break;
+ case 1: /* 50 hz */
+ i2c_w1(gspca_dev, 0x13, com8);
+ i2c_w1(gspca_dev, 0x3b, 0x0a);
+ break;
+ case 2: /* 60 hz */
+ i2c_w1(gspca_dev, 0x13, com8);
+ i2c_w1(gspca_dev, 0x3b, 0x02);
+ break;
+ }
+ } else {
+ u8 reg2a = 0, reg2b = 0, reg2d = 0;
+
+ /* Get reg2a / reg2d base values */
+ switch (sd->sensor) {
+ case SENSOR_OV7630:
+ reg2a = 0x08;
+ reg2d = 0x01;
+ break;
+ case SENSOR_OV7648:
+ reg2a = 0x11;
+ reg2d = 0x81;
+ break;
+ }
+
+ switch (sd->ctrls[FREQ].val) {
+ case 0: /* Banding filter disabled */
+ break;
+ case 1: /* 50 hz (filter on and framerate adj) */
+ reg2a |= 0x80;
+ reg2b = 0xac;
+ reg2d |= 0x04;
+ break;
+ case 2: /* 60 hz (filter on, no framerate adj) */
+ reg2a |= 0x80;
+ reg2d |= 0x04;
+ break;
+ }
+ i2c_w1(gspca_dev, 0x2a, reg2a);
+ i2c_w1(gspca_dev, 0x2b, reg2b);
+ i2c_w1(gspca_dev, 0x2d, reg2d);
+ }
+}
+
+static void setjpegqual(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+#if USB_BUF_SZ < 64
+#error "No room enough in usb_buf for quantization table"
+#endif
+ 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,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0100, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+ 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,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ 0x0140, 0,
+ gspca_dev->usb_buf, 64,
+ 500);
+
+ sd->reg18 ^= 0x40;
+ 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;
+
+ /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->quality);
+ gspca_dev->usb_err = 0;
+ setjpegqual(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u8 reg01, reg17;
+ u8 reg0102[2];
+ const u8 *sn9c1xx;
+ const u8 (*init)[8];
+ const u8 *reg9a;
+ int mode;
+ static const u8 reg9a_def[] =
+ {0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
+ static const u8 reg9a_spec[] =
+ {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+ static const u8 regd4[] = {0x60, 0x00, 0x00};
+ static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+ static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+ static const u8 CA_adcm1700[] =
+ { 0x14, 0xec, 0x0a, 0xf6 };
+ static const u8 CA_po2030n[] =
+ { 0x1e, 0xe2, 0x14, 0xec };
+ static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
+ static const u8 CE_gc0307[] =
+ { 0x32, 0xce, 0x2d, 0xd3 };
+ static const u8 CE_ov76xx[] =
+ { 0x32, 0xdd, 0x32, 0xdd };
+ static const u8 CE_po2030n[] =
+ { 0x14, 0xe7, 0x1e, 0xdd };
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+
+ /* initialize the bridge */
+ sn9c1xx = sn_tb[sd->sensor];
+
+ /* sensor clock already enabled in sd_init */
+ /* reg_w1(gspca_dev, 0xf1, 0x00); */
+ reg01 = sn9c1xx[1];
+ if (sd->flags & F_PDN_INV)
+ reg01 ^= S_PDN_INV; /* power down inverted */
+ reg_w1(gspca_dev, 0x01, reg01);
+
+ /* configure gpio */
+ reg0102[0] = reg01;
+ reg0102[1] = sn9c1xx[2];
+ if (gspca_dev->audio)
+ reg0102[1] |= 0x04; /* keep the audio connection */
+ reg_w(gspca_dev, 0x01, reg0102, 2);
+ reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+ reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ case SENSOR_OV7660:
+ case SENSOR_PO1030:
+ case SENSOR_PO2030N:
+ case SENSOR_SOI768:
+ case SENSOR_SP80708:
+ reg9a = reg9a_spec;
+ break;
+ default:
+ reg9a = reg9a_def;
+ break;
+ }
+ reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+ reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
+
+ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+ reg17 = sn9c1xx[0x17];
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ msleep(50); /*fixme: is it useful? */
+ break;
+ case SENSOR_OM6802:
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x73);
+ reg17 |= SEN_CLK_EN;
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, 0x22);
+ msleep(100);
+ reg01 = SCL_SEL_OD | S_PDN_INV;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x04; /* clock / 4 */
+ break;
+ }
+ reg01 |= SYS_SEL_48M;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg17 |= SEN_CLK_EN;
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg01 &= ~S_PWR_DN; /* sensor power on */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 &= ~SCL_SEL_OD; /* remove open-drain mode */
+ reg_w1(gspca_dev, 0x01, reg01);
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ hv7131r_probe(gspca_dev); /*fixme: is it useful? */
+ break;
+ case SENSOR_OM6802:
+ msleep(10);
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, om6802_init0[0]);
+ i2c_w8(gspca_dev, om6802_init0[1]);
+ msleep(15);
+ reg_w1(gspca_dev, 0x02, 0x71);
+ msleep(150);
+ break;
+ case SENSOR_SP80708:
+ msleep(100);
+ reg_w1(gspca_dev, 0x02, 0x62);
+ break;
+ }
+
+ /* initialize the sensor */
+ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
+
+ reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
+ reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
+ reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
+ reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */
+ reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */
+ } else {
+ reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */
+ reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */
+ }
+ reg_w1(gspca_dev, 0xc6, 0x00);
+ reg_w1(gspca_dev, 0xc7, 0x00);
+ if (sd->sensor == SENSOR_ADCM1700) {
+ reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */
+ reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */
+ } else {
+ reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */
+ reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */
+ }
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ switch (sd->sensor) {
+ case SENSOR_OM6802:
+/* case SENSOR_OV7648: * fixme: sometimes */
+ break;
+ default:
+ reg17 |= DEF_EN;
+ break;
+ }
+ reg_w1(gspca_dev, 0x17, reg17);
+
+ reg_w1(gspca_dev, 0x05, 0x00); /* red */
+ reg_w1(gspca_dev, 0x07, 0x00); /* green */
+ reg_w1(gspca_dev, 0x06, 0x00); /* blue */
+ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+
+ setgamma(gspca_dev);
+
+/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OV7660:
+ case SENSOR_SP80708:
+ reg_w1(gspca_dev, 0x9a, 0x05);
+ break;
+ case SENSOR_GC0307:
+ case SENSOR_MT9V111:
+ case SENSOR_MI0360B:
+ reg_w1(gspca_dev, 0x9a, 0x07);
+ break;
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ break;
+ case SENSOR_PO2030N:
+ case SENSOR_SOI768:
+ reg_w1(gspca_dev, 0x9a, 0x06);
+ break;
+ default:
+ reg_w1(gspca_dev, 0x9a, 0x08);
+ break;
+ }
+ setsharpness(gspca_dev);
+
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ reg_w1(gspca_dev, 0x05, 0x20); /* red */
+ reg_w1(gspca_dev, 0x07, 0x20); /* green */
+ reg_w1(gspca_dev, 0x06, 0x20); /* blue */
+
+ init = NULL;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ reg01 |= SYS_SEL_48M | V_TX_EN;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x02; /* clock / 2 */
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ init = adcm1700_sensor_param1;
+ break;
+ case SENSOR_GC0307:
+ init = gc0307_sensor_param1;
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360:
+ if (!mode)
+ reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ break;
+ case SENSOR_MI0360B:
+ init = mi0360b_sensor_param1;
+ break;
+ case SENSOR_MO4000:
+ if (mode) { /* if 320x240 */
+ reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ }
+ break;
+ case SENSOR_MT9V111:
+ init = mt9v111_sensor_param1;
+ break;
+ case SENSOR_OM6802:
+ init = om6802_sensor_param1;
+ if (!mode) { /* if 640x480 */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x04; /* clock / 4 */
+ } else {
+ reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x02; /* clock / 2 */
+ }
+ break;
+ case SENSOR_OV7630:
+ init = ov7630_sensor_param1;
+ break;
+ case SENSOR_OV7648:
+ init = ov7648_sensor_param1;
+ reg17 &= ~MCK_SIZE_MASK;
+ reg17 |= 0x01; /* clock / 1 */
+ break;
+ case SENSOR_OV7660:
+ init = ov7660_sensor_param1;
+ break;
+ case SENSOR_PO1030:
+ init = po1030_sensor_param1;
+ break;
+ case SENSOR_PO2030N:
+ init = po2030n_sensor_param1;
+ break;
+ case SENSOR_SOI768:
+ init = soi768_sensor_param1;
+ break;
+ case SENSOR_SP80708:
+ init = sp80708_sensor_param1;
+ break;
+ }
+
+ /* more sensor initialization - param1 */
+ if (init != NULL) {
+ i2c_w_seq(gspca_dev, init);
+/* init = NULL; */
+ }
+
+ reg_w(gspca_dev, 0xc0, C0, 6);
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_GC0307:
+ case SENSOR_SOI768:
+ reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
+ break;
+ case SENSOR_PO2030N:
+ reg_w(gspca_dev, 0xca, CA_po2030n, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xca, CA, 4);
+ break;
+ }
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ case SENSOR_OV7660:
+ case SENSOR_SOI768:
+ reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
+ break;
+ case SENSOR_GC0307:
+ reg_w(gspca_dev, 0xce, CE_gc0307, 4);
+ break;
+ case SENSOR_PO2030N:
+ reg_w(gspca_dev, 0xce, CE_po2030n, 4);
+ break;
+ default:
+ reg_w(gspca_dev, 0xce, CE, 4);
+ /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
+ break;
+ }
+
+ /* here change size mode 0 -> VGA; 1 -> CIF */
+ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
+ reg_w1(gspca_dev, 0x18, sd->reg18);
+ setjpegqual(gspca_dev);
+
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg01);
+ sd->reg01 = reg01;
+ sd->reg17 = reg17;
+
+ sethvflip(gspca_dev);
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+ setcolors(gspca_dev);
+ setautogain(gspca_dev);
+ if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
+ setexposure(gspca_dev);
+ setgain(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;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 stophv7131[] =
+ { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopmi0360[] =
+ { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopov7648[] =
+ { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 stopsoi768[] =
+ { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 };
+ u8 reg01;
+ u8 reg17;
+
+ reg01 = sd->reg01;
+ reg17 = sd->reg17 & ~SEN_CLK_EN;
+ switch (sd->sensor) {
+ case SENSOR_ADCM1700:
+ case SENSOR_GC0307:
+ case SENSOR_PO2030N:
+ case SENSOR_SP80708:
+ reg01 |= LED;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 &= ~(LED | V_TX_EN);
+ reg_w1(gspca_dev, 0x01, reg01);
+/* reg_w1(gspca_dev, 0x02, 0x??); * LED off ? */
+ break;
+ case SENSOR_HV7131R:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, stophv7131);
+ break;
+ case SENSOR_MI0360:
+ case SENSOR_MI0360B:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+/* reg_w1(gspca_dev, 0x02, 0x40); * LED off ? */
+ i2c_w8(gspca_dev, stopmi0360);
+ break;
+ case SENSOR_MT9V111:
+ case SENSOR_OM6802:
+ case SENSOR_PO1030:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ break;
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ i2c_w8(gspca_dev, stopov7648);
+ break;
+ case SENSOR_OV7660:
+ reg01 &= ~V_TX_EN;
+ reg_w1(gspca_dev, 0x01, reg01);
+ break;
+ case SENSOR_SOI768:
+ i2c_w8(gspca_dev, stopsoi768);
+ break;
+ }
+
+ reg01 |= SCL_SEL_OD;
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 |= S_PWR_DN; /* sensor power down */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg01 &= ~SYS_SEL_48M; /* clock 24MHz */
+ reg_w1(gspca_dev, 0x01, reg01);
+ reg01 |= LED;
+ reg_w1(gspca_dev, 0x01, reg01);
+ /* Don't disable sensor clock as that disables the button on the cam */
+ /* 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;
+ }
+}
+
+#define WANT_REGULAR_AUTOGAIN
+#include "autogain_functions.h"
+
+static void do_autogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int delta;
+ int expotimes;
+ u8 luma_mean = 130;
+ u8 luma_delta = 20;
+
+ /* Thanks S., without your advice, autobright should not work :) */
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt >= 0)
+ return;
+ sd->ag_cnt = AG_CNT_START;
+
+ delta = atomic_read(&sd->avg_lum);
+ PDEBUG(D_FRAM, "mean lum %d", delta);
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
+ 15, 1024);
+ return;
+ }
+
+ if (delta < luma_mean - luma_delta ||
+ delta > luma_mean + luma_delta) {
+ switch (sd->sensor) {
+ case SENSOR_GC0307:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ break;
+ case SENSOR_HV7131R:
+ expotimes = sd->exposure >> 8;
+ expotimes += (luma_mean - delta) >> 4;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) (expotimes << 8));
+ break;
+ case SENSOR_OM6802:
+ case SENSOR_MT9V111:
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 2;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ setredblue(gspca_dev);
+ break;
+ default:
+/* case SENSOR_MO4000: */
+/* case SENSOR_MI0360: */
+/* case SENSOR_MI0360B: */
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+ expotimes = 0;
+ sd->exposure = expo_adjust(gspca_dev,
+ (unsigned int) expotimes);
+ setredblue(gspca_dev);
+ break;
+ }
+ }
+}
+
+/* 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,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ 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').
+ */
+
+ /* count the packets and their size */
+ sd->npkt++;
+ sd->pktsz += len;
+
+/*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;
+ }
+
+ /* 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);
+ 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;
+ }
+ }
+
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
+
+ /* compute the filling rate and a new JPEG quality */
+ if (new_qual == 0) {
+ int r;
+
+ 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 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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val)
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+ else
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu)
+{
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ switch (menu->index) {
+ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
+ strcpy((char *) menu->name, "NoFliker");
+ return 0;
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrupt packet length */
+{
+ int ret = -EINVAL;
+
+ if (len == 1 && data[0] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ ret = 0;
+ }
+
+ return ret;
+}
+#endif
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = NCTRLS,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
+ .querymenu = sd_querymenu,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+/* -- module initialisation -- */
+#define BS(bridge, sensor) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+ | (SENSOR_ ## sensor << 8)
+#define BSF(bridge, sensor, flags) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+ | (SENSOR_ ## sensor << 8) \
+ | (flags)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0458, 0x7025), BSF(SN9C120, MI0360B, F_PDN_INV)},
+ {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
+ {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)},
+ {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)},
+ {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
+ {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
+/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
+ {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
+/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
+ {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
+/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
+ {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)},
+ /* or MT9V111 */
+/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
+/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
+/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
+ {USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)},
+ {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
+/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
+/* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */
+ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
+ {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
+ {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/
+/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
+ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/
+ {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/
+/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
+/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
+/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
+ {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/
+/*bw600.inf:*/
+ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/
+ {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)},
+ {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
+ {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
+/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
+ {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
+ /* or MT9V111 / MI0360B */
+/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
+ {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
+ {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
+ {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
+ {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
+ {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
+ {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/
+ /* or GC0305 / GC0307 */
+ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
+ {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)},
+/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca1528.c b/drivers/media/usb/gspca/spca1528.c
index 14d635277d71..14d635277d71 100644
--- a/drivers/media/video/gspca/spca1528.c
+++ b/drivers/media/usb/gspca/spca1528.c
diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/usb/gspca/spca500.c
index 25cb68d0556d..25cb68d0556d 100644
--- a/drivers/media/video/gspca/spca500.c
+++ b/drivers/media/usb/gspca/spca500.c
diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/usb/gspca/spca501.c
index 3b7f777785b4..3b7f777785b4 100644
--- a/drivers/media/video/gspca/spca501.c
+++ b/drivers/media/usb/gspca/spca501.c
diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/usb/gspca/spca505.c
index bc7d67c3cb04..bc7d67c3cb04 100644
--- a/drivers/media/video/gspca/spca505.c
+++ b/drivers/media/usb/gspca/spca505.c
diff --git a/drivers/media/usb/gspca/spca506.c b/drivers/media/usb/gspca/spca506.c
new file mode 100644
index 000000000000..bab01c86c315
--- /dev/null
+++ b/drivers/media/usb/gspca/spca506.c
@@ -0,0 +1,612 @@
+/*
+ * SPCA506 chip based cameras function
+ * M Xhaard 15/04/2004 based on different work Mark Taylor and others
+ * and my own snoopy file on a pv-321c donate by a german compagny
+ * "Firma Frank Gmbh" from Saarbruecken
+ *
+ * V4L2 by Jean-Francois Moine <http://moinejf.free.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; 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 "spca506"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
+MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ char norme;
+ char channel;
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 5},
+ {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 4},
+ {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+
+#define SPCA50X_OFFSET_DATA 10
+
+#define SAA7113_bright 0x0a /* defaults 0x80 */
+#define SAA7113_contrast 0x0b /* defaults 0x47 */
+#define SAA7113_saturation 0x0c /* defaults 0x40 */
+#define SAA7113_hue 0x0d /* defaults 0x00 */
+#define SAA7113_I2C_BASE_WRITE 0x4a
+
+/* read 'len' bytes to gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev,
+ __u16 req,
+ __u16 index,
+ __u16 length)
+{
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, length,
+ 500);
+}
+
+static void reg_w(struct usb_device *dev,
+ __u16 req,
+ __u16 value,
+ __u16 index)
+{
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ NULL, 0, 500);
+}
+
+static void spca506_Initi2c(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
+}
+
+static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
+ __u16 reg)
+{
+ int retry = 60;
+
+ reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
+ reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
+ while (retry--) {
+ reg_r(gspca_dev, 0x07, 0x0003, 2);
+ if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
+ break;
+ }
+}
+
+static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
+ __u16 norme,
+ __u16 channel)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+/* fixme: check if channel == 0..3 and 6..9 (8 values) */
+ __u8 setbit0 = 0x00;
+ __u8 setbit1 = 0x00;
+ __u8 videomask = 0x00;
+
+ PDEBUG(D_STREAM, "** Open Set Norme **");
+ spca506_Initi2c(gspca_dev);
+ /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
+ /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
+ /* and exclude SAA7113 reserved channel set default 0 otherwise */
+ if (norme & V4L2_STD_NTSC)
+ setbit0 = 0x01;
+ if (channel == 4 || channel == 5 || channel > 9)
+ channel = 0;
+ if (channel < 4)
+ setbit1 = 0x02;
+ videomask = (0x48 | setbit0 | setbit1);
+ reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
+ spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
+
+ if (norme & V4L2_STD_NTSC)
+ spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
+ /* Chrominance Control NTSC N */
+ else if (norme & V4L2_STD_SECAM)
+ spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
+ /* Chrominance Control SECAM */
+ else
+ spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
+ /* Chrominance Control PAL BGHIV */
+
+ sd->norme = norme;
+ sd->channel = channel;
+ PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
+ PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
+}
+
+static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
+ __u16 *norme, __u16 *channel)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* Read the register is not so good value change so
+ we use your own copy in spca50x struct */
+ *norme = sd->norme;
+ *channel = sd->channel;
+ PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
+}
+
+static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
+ __u16 xmult, __u16 ymult)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ PDEBUG(D_STREAM, "** SetSize **");
+ reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
+ /* Soft snap 0x40 Hard 0x41 */
+ reg_w(dev, 0x04, 0x41, 0x0001);
+ reg_w(dev, 0x04, 0x00, 0x0002);
+ /* reserved */
+ reg_w(dev, 0x04, 0x00, 0x0003);
+
+ /* reserved */
+ reg_w(dev, 0x04, 0x00, 0x0004);
+ /* reserved */
+ reg_w(dev, 0x04, 0x01, 0x0005);
+ /* reserced */
+ reg_w(dev, 0x04, xmult, 0x0006);
+ /* reserved */
+ reg_w(dev, 0x04, ymult, 0x0007);
+ /* compression 1 */
+ reg_w(dev, 0x04, 0x00, 0x0008);
+ /* T=64 -> 2 */
+ reg_w(dev, 0x04, 0x00, 0x0009);
+ /* threshold2D */
+ reg_w(dev, 0x04, 0x21, 0x000a);
+ /* quantization */
+ reg_w(dev, 0x04, 0x00, 0x000b);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x1c, 0x0001);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ /* Init on PAL and composite input0 */
+ spca506_SetNormeInput(gspca_dev, 0, 0);
+ reg_w(dev, 0x03, 0x1c, 0x0001);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ reg_w(dev, 0x05, 0x00, 0x0000);
+ reg_w(dev, 0x05, 0xef, 0x0001);
+ reg_w(dev, 0x05, 0x00, 0x00c1);
+ reg_w(dev, 0x05, 0x00, 0x00c2);
+ reg_w(dev, 0x06, 0x18, 0x0002);
+ reg_w(dev, 0x06, 0xf5, 0x0011);
+ reg_w(dev, 0x06, 0x02, 0x0012);
+ reg_w(dev, 0x06, 0xfb, 0x0013);
+ reg_w(dev, 0x06, 0x00, 0x0014);
+ reg_w(dev, 0x06, 0xa4, 0x0051);
+ reg_w(dev, 0x06, 0x40, 0x0052);
+ reg_w(dev, 0x06, 0x71, 0x0053);
+ reg_w(dev, 0x06, 0x40, 0x0054);
+ /************************************************/
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x60, 0x0000);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+ /* for a better reading mx :) */
+ /*sdca506_WriteI2c(value,register) */
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, 0x08, 0x01);
+ spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
+ /* input composite video */
+ spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+ spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+ spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+ spca506_WriteI2c(gspca_dev, 0x98, 0x08);
+ spca506_WriteI2c(gspca_dev, 0x03, 0x09);
+ spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+ spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
+ spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+ spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */
+ spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+ spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
+ spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x13);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x14);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x15);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x16);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x17);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+ spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+ spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+ spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+ spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+ spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+ spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+ spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+ PDEBUG(D_STREAM, "** Close Init *");
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ __u16 norme;
+ __u16 channel;
+
+ /**************************************/
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0xFF, 0x0003);
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x60, 0x0000);
+ reg_w(dev, 0x03, 0x18, 0x0001);
+
+ /*sdca506_WriteI2c(value,register) */
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */
+/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
+ spca506_WriteI2c(gspca_dev, 0x33, 0x03);
+ /* Analog Input Control 2 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x04);
+ /* Analog Input Control 3 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x05);
+ /* Analog Input Control 4 */
+ spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
+ /* Horizontal Sync Start 0xe9-0x0d */
+ spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
+ /* Horizontal Sync Stop 0x0d-0xf0 */
+
+ spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */
+/* Defaults value */
+ spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */
+ spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
+ /* Luminance Brightness */
+ spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */
+ spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
+ /* Chrominance Saturation */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
+ /* Chrominance Hue Control */
+ spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
+ /* Chrominance Gain Control */
+ /**************************************/
+ spca506_WriteI2c(gspca_dev, 0x00, 0x10);
+ /* Format/Delay Control */
+ spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */
+ spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */
+ spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */
+ spca506_WriteI2c(gspca_dev, 0x00, 0x18);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x19);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
+ spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
+ spca506_WriteI2c(gspca_dev, 0x02, 0x40);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x41);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x42);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x43);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x44);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x45);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x46);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x47);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x48);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x49);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x50);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x51);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x52);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x53);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x54);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x55);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x56);
+ spca506_WriteI2c(gspca_dev, 0xff, 0x57);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x58);
+ spca506_WriteI2c(gspca_dev, 0x54, 0x59);
+ spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
+ spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
+ spca506_WriteI2c(gspca_dev, 0x00, 0x60);
+ spca506_WriteI2c(gspca_dev, 0x05, 0x61);
+ spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
+ /**************************************/
+ reg_w(dev, 0x05, 0x00, 0x0003);
+ reg_w(dev, 0x05, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x10, 0x0001);
+ reg_w(dev, 0x03, 0x78, 0x0000);
+ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ case 0:
+ spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
+ break;
+ case 1:
+ spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
+ break;
+ case 2:
+ spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
+ break;
+ case 4:
+ spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
+ break;
+ default:
+/* case 5: */
+ spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
+ break;
+ }
+
+ /* compress setting and size */
+ /* set i2c luma */
+ reg_w(dev, 0x02, 0x01, 0x0000);
+ reg_w(dev, 0x03, 0x12, 0x0000);
+ reg_r(gspca_dev, 0x04, 0x0001, 2);
+ PDEBUG(D_STREAM, "webcam started");
+ spca506_GetNormeInput(gspca_dev, &norme, &channel);
+ spca506_SetNormeInput(gspca_dev, norme, channel);
+ return 0;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ reg_w(dev, 0x02, 0x00, 0x0000);
+ reg_w(dev, 0x03, 0x00, 0x0004);
+ reg_w(dev, 0x03, 0x00, 0x0003);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ switch (data[0]) {
+ case 0: /* start of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
+{
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
+{
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
+{
+ spca506_Initi2c(gspca_dev);
+ spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ sethue(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 0, 255, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06e1, 0xa190)},
+/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
+ {USB_DEVICE(0x0733, 0x0430)}, */
+ {USB_DEVICE(0x0734, 0x043b)},
+ {USB_DEVICE(0x99fa, 0x8988)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int __devinit 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/usb/gspca/spca508.c
index 1286b4170b88..1286b4170b88 100644
--- a/drivers/media/video/gspca/spca508.c
+++ b/drivers/media/usb/gspca/spca508.c
diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/usb/gspca/spca561.c
index cfe71dd6747d..cfe71dd6747d 100644
--- a/drivers/media/video/gspca/spca561.c
+++ b/drivers/media/usb/gspca/spca561.c
diff --git a/drivers/media/usb/gspca/sq905.c b/drivers/media/usb/gspca/sq905.c
new file mode 100644
index 000000000000..1d99f10a3e19
--- /dev/null
+++ b/drivers/media/usb/gspca/sq905.c
@@ -0,0 +1,439 @@
+/*
+ * SQ905 subdriver
+ *
+ * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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
+ */
+
+/*
+ * History and Acknowledgments
+ *
+ * The original Linux driver for SQ905 based cameras was written by
+ * Marcell Lengyel and furter developed by many other contributors
+ * and is available from http://sourceforge.net/projects/sqcam/
+ *
+ * This driver takes advantage of the reverse engineering work done for
+ * that driver and for libgphoto2 but shares no code with them.
+ *
+ * This driver has used as a base the finepix driver and other gspca
+ * based drivers and may still contain code fragments taken from those
+ * drivers.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, "
+ "Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905_CMD_TIMEOUT 500
+#define SQ905_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905_MAX_TRANSFER 0x8000
+#define FRAME_HEADER_LEN 64
+
+/* The known modes, or registers. These go in the "value" slot. */
+
+/* 00 is "none" obviously */
+
+#define SQ905_BULK_READ 0x03 /* precedes any bulk read */
+#define SQ905_COMMAND 0x06 /* precedes the command codes below */
+#define SQ905_PING 0x07 /* when reading an "idling" command */
+#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
+
+/* Any non-zero value in the bottom 2 bits of the 2nd byte of
+ * the ID appears to indicate the camera can do 640*480. If the
+ * LSB of that byte is set the image is just upside down, otherwise
+ * it is rotated 180 degrees. */
+#define SQ905_HIRES_MASK 0x00000300
+#define SQ905_ORIENTATION_MASK 0x00000100
+
+/* Some command codes. These go in the "index" slot. */
+
+#define SQ905_ID 0xf0 /* asks for model string */
+#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
+#define SQ905_DATA 0x30 /* accesses photo data, not used here */
+#define SQ905_CLEAR 0xa0 /* clear everything */
+#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
+#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
+#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
+/* note that the capture command also controls the output dimensions */
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ /*
+ * Driver stuff
+ */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+static struct v4l2_pix_format sq905_mode[] = {
+ { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/*
+ * Send a command to the camera.
+ */
+static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
+{
+ int ret;
+
+ gspca_dev->usb_buf[0] = '\0';
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_PING, 0, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Acknowledge the end of a frame - see warning on sq905_command.
+ */
+static int sq905_ack_frame(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ gspca_dev->usb_buf[0] = '\0';
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
+ SQ905_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * request and read a block of data - see warning on sq905_command.
+ */
+static int
+sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
+{
+ int ret;
+ int act_len;
+
+ gspca_dev->usb_buf[0] = '\0';
+ if (need_lock)
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_BULK_READ, size, gspca_dev->usb_buf,
+ 1, SQ905_CMD_TIMEOUT);
+ if (need_lock)
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ data, size, &act_len, SQ905_DATA_TIMEOUT);
+
+ /* successful, it returns 0, otherwise negative */
+ if (ret < 0 || act_len != size) {
+ pr_err("bulk read fail (%d) len %d/%d\n", 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 gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void sq905_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int header_read; /* true if we have already read the frame header. */
+ int packet_type;
+ int frame_sz;
+ int ret;
+ u8 *data;
+ u8 *buffer;
+
+ buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
+ + FRAME_HEADER_LEN;
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ /* request some data and then read it until we have
+ * a complete frame. */
+ bytes_left = frame_sz;
+ header_read = 0;
+
+ /* Note we do not check for gspca_dev->streaming here, as
+ we must finish reading an entire frame, otherwise the
+ next time we stream we start reading in the middle of a
+ frame. */
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905_MAX_TRANSFER ?
+ SQ905_MAX_TRANSFER : bytes_left;
+ ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
+ if (ret < 0)
+ goto quit_stream;
+ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ data = buffer;
+ if (!header_read) {
+ packet_type = FIRST_PACKET;
+ /* The first 64 bytes of each frame are
+ * a header full of FF 00 bytes */
+ data += FRAME_HEADER_LEN;
+ data_len -= FRAME_HEADER_LEN;
+ header_read = 1;
+ } else if (bytes_left == 0) {
+ packet_type = LAST_PACKET;
+ } else {
+ packet_type = INTER_PACKET;
+ }
+ gspca_frame_add(gspca_dev, packet_type,
+ data, data_len);
+ /* If entire frame fits in one packet we still
+ need to add a LAST_PACKET */
+ if (packet_type == FIRST_PACKET &&
+ bytes_left == 0)
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+ }
+ if (gspca_dev->present) {
+ /* acknowledge the frame */
+ mutex_lock(&gspca_dev->usb_lock);
+ ret = sq905_ack_frame(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0)
+ goto quit_stream;
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ sq905_command(gspca_dev, SQ905_CLEAR);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ 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 *dev = (struct sd *) gspca_dev;
+
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk = 1;
+ cam->bulk_size = 64;
+
+ INIT_WORK(&dev->work_struct, sq905_dostream);
+
+ 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 sq905_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ u32 ident;
+ int ret;
+
+ /* connect to the camera and read
+ * the model ID and process that and put it away.
+ */
+ ret = sq905_command(gspca_dev, SQ905_CLEAR);
+ if (ret < 0)
+ return ret;
+ ret = sq905_command(gspca_dev, SQ905_ID);
+ if (ret < 0)
+ return ret;
+ ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
+ if (ret < 0)
+ return ret;
+ /* usb_buf is allocated with kmalloc so is aligned.
+ * Camera model number is the right way round if we assume this
+ * reverse engineered ID is supposed to be big endian. */
+ ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
+ ret = sq905_command(gspca_dev, SQ905_CLEAR);
+ if (ret < 0)
+ return ret;
+ PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
+ gspca_dev->cam.cam_mode = sq905_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
+ if (!(ident & SQ905_HIRES_MASK))
+ gspca_dev->cam.nmodes--;
+
+ if (ident & SQ905_ORIENTATION_MASK)
+ gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
+ else
+ gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
+ V4L2_IN_ST_HFLIP;
+ return 0;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ /* "Open the shutter" and set size, to start capture */
+ switch (gspca_dev->curr_mode) {
+ default:
+/* case 2: */
+ PDEBUG(D_STREAM, "Start streaming at high resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
+ break;
+ case 1:
+ PDEBUG(D_STREAM, "Start streaming at medium resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
+ break;
+ case 0:
+ PDEBUG(D_STREAM, "Start streaming at low resolution");
+ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
+ }
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return ret;
+ }
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x2770, 0x9120)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sq905c.c b/drivers/media/usb/gspca/sq905c.c
new file mode 100644
index 000000000000..410cdcbb55d4
--- /dev/null
+++ b/drivers/media/usb/gspca/sq905c.c
@@ -0,0 +1,342 @@
+/*
+ * SQ905C subdriver
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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
+ */
+
+/*
+ *
+ * This driver uses work done in
+ * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
+ *
+ * This driver has also used as a base the sq905c driver
+ * and may contain code fragments from it.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq905c"
+
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define SQ905C_CMD_TIMEOUT 500
+#define SQ905C_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define SQ905C_MAX_TRANSFER 0x8000
+
+#define FRAME_HEADER_LEN 0x50
+
+/* Commands. These go in the "value" slot. */
+#define SQ905C_CLEAR 0xa0 /* clear everything */
+#define SQ905C_GET_ID 0x14f4 /* Read version number */
+#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
+#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
+#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
+
+/* For capture, this must go in the "index" slot. */
+#define SQ905C_CAPTURE_INDEX 0x110f
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+/*
+ * Most of these cameras will do 640x480 and 320x240. 160x120 works
+ * in theory but gives very poor output. Therefore, not supported.
+ * The 0x2770:0x9050 cameras have max resolution of 320x240.
+ */
+static struct v4l2_pix_format sq905c_mode[] = {
+ { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0}
+};
+
+/* Send a command to the camera. */
+static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ command, index, NULL, 0,
+ SQ905C_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
+ int size)
+{
+ int ret;
+
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ command, index, gspca_dev->usb_buf, size,
+ SQ905C_CMD_TIMEOUT);
+ if (ret < 0) {
+ pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
+ return ret;
+ }
+
+ 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 gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the camera doesn't provide any controls.
+ */
+static void sq905c_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int act_len;
+ int packet_type;
+ int ret;
+ u8 *buffer;
+
+ buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto quit_stream;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ /* Request the header, which tells the size to download */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ buffer, FRAME_HEADER_LEN, &act_len,
+ SQ905C_DATA_TIMEOUT);
+ PDEBUG(D_STREAM,
+ "Got %d bytes out of %d for header",
+ act_len, FRAME_HEADER_LEN);
+ if (ret < 0 || act_len < FRAME_HEADER_LEN)
+ goto quit_stream;
+ /* size is read from 4 bytes starting 0x40, little endian */
+ bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
+ |(buffer[0x43]<<24);
+ PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
+ /* We keep the header. It has other information, too. */
+ packet_type = FIRST_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, FRAME_HEADER_LEN);
+ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905C_MAX_TRANSFER ?
+ SQ905C_MAX_TRANSFER : bytes_left;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ buffer, data_len, &act_len,
+ SQ905C_DATA_TIMEOUT);
+ if (ret < 0 || act_len < data_len)
+ goto quit_stream;
+ PDEBUG(D_STREAM,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+ if (bytes_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ gspca_frame_add(gspca_dev, packet_type,
+ buffer, data_len);
+ }
+ }
+quit_stream:
+ if (gspca_dev->present) {
+ mutex_lock(&gspca_dev->usb_lock);
+ sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ 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 *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ PDEBUG(D_PROBE,
+ "SQ9050 camera detected"
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+ ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Get version command failed");
+ return ret;
+ }
+
+ ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Reading version command failed");
+ return ret;
+ }
+ /* Note we leave out the usb id and the manufacturing date */
+ PDEBUG(D_PROBE,
+ "SQ9050 ID string: %02x - %*ph",
+ gspca_dev->usb_buf[3], 6, gspca_dev->usb_buf + 14);
+
+ cam->cam_mode = sq905c_mode;
+ cam->nmodes = 2;
+ if (gspca_dev->usb_buf[15] == 0)
+ cam->nmodes = 1;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 32;
+ cam->bulk = 1;
+ INIT_WORK(&dev->work_struct, sq905c_dostream);
+ 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 sq905c_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ int ret;
+
+ /* connect to the camera and reset it. */
+ ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+ return ret;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ dev->cap_mode = gspca_dev->cam.cam_mode;
+ /* "Open the shutter" and set size, to start capture */
+ switch (gspca_dev->width) {
+ case 640:
+ PDEBUG(D_STREAM, "Start streaming at high resolution");
+ dev->cap_mode++;
+ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
+ SQ905C_CAPTURE_INDEX);
+ break;
+ default: /* 320 */
+ PDEBUG(D_STREAM, "Start streaming at medium resolution");
+ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
+ SQ905C_CAPTURE_INDEX);
+ }
+
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return ret;
+ }
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* Table of supported USB devices */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x2770, 0x905c)},
+ {USB_DEVICE(0x2770, 0x9050)},
+ {USB_DEVICE(0x2770, 0x9051)},
+ {USB_DEVICE(0x2770, 0x9052)},
+ {USB_DEVICE(0x2770, 0x913d)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/sq930x.c b/drivers/media/usb/gspca/sq930x.c
new file mode 100644
index 000000000000..7e8748b31e85
--- /dev/null
+++ b/drivers/media/usb/gspca/sq930x.c
@@ -0,0 +1,1164 @@
+/*
+ * SQ930x subdriver
+ *
+ * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
+ * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "sq930x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
+ "Gerard Klaver <gerard at gkall dot hobby dot nl\n"
+ "Sam Revitch <samr7@cs.washington.edu>");
+MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct { /* exposure/gain control cluster */
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ };
+
+ u8 do_ctrl;
+ u8 gpio[2];
+ u8 sensor;
+ u8 type;
+#define Generic 0
+#define Creative_live_motion 1
+};
+enum sensors {
+ SENSOR_ICX098BQ,
+ SENSOR_LZ24BP,
+ SENSOR_MI0360,
+ SENSOR_MT9V111, /* = MI360SOC */
+ SENSOR_OV7660,
+ SENSOR_OV9630,
+};
+
+static struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+ {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+};
+
+/* sq930x registers */
+#define SQ930_CTRL_UCBUS_IO 0x0001
+#define SQ930_CTRL_I2C_IO 0x0002
+#define SQ930_CTRL_GPIO 0x0005
+#define SQ930_CTRL_CAP_START 0x0010
+#define SQ930_CTRL_CAP_STOP 0x0011
+#define SQ930_CTRL_SET_EXPOSURE 0x001d
+#define SQ930_CTRL_RESET 0x001e
+#define SQ930_CTRL_GET_DEV_INFO 0x001f
+
+/* gpio 1 (8..15) */
+#define SQ930_GPIO_DFL_I2C_SDA 0x0001
+#define SQ930_GPIO_DFL_I2C_SCL 0x0002
+#define SQ930_GPIO_RSTBAR 0x0004
+#define SQ930_GPIO_EXTRA1 0x0040
+#define SQ930_GPIO_EXTRA2 0x0080
+/* gpio 3 (24..31) */
+#define SQ930_GPIO_POWER 0x0200
+#define SQ930_GPIO_DFL_LED 0x1000
+
+struct ucbus_write_cmd {
+ u16 bw_addr;
+ u8 bw_data;
+};
+struct i2c_write_cmd {
+ u8 reg;
+ u16 val;
+};
+
+static const struct ucbus_write_cmd icx098bq_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
+ {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
+ {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
+ {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
+ {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
+ {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
+ {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
+ {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
+ {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+ {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+ {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
+ {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+ {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
+ {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+ {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+ {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
+ {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
+ {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
+ {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
+ {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+ {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
+ {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
+ {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
+ {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
+ {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+ {0xf800, 0x03}
+};
+static const struct ucbus_write_cmd icx098bq_start_1[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xc0},
+ {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xc0},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd icx098bq_start_2[] = {
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
+ {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
+ {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
+ {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
+ {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
+ {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
+ {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
+ {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
+ {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
+ {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
+ {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
+ {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
+ {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
+ {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
+ {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
+ {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
+ {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
+ {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
+ {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
+ {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
+ {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
+ {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
+ {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
+ {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
+ {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
+ {0xf800, 0x03}
+};
+static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xb3},
+ {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xb3},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
+ {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+ {0xf5f4, 0xc0},
+ {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
+ {0xf5f4, 0xc0},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+
+static const struct ucbus_write_cmd lz24bp_start_2[] = {
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
+ {0xf807, 0x7f}, {0xf800, 0x03},
+ {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
+ {0xf807, 0x7f}, {0xf800, 0x03}
+};
+
+static const struct ucbus_write_cmd mi0360_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
+ {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
+};
+static const struct i2c_write_cmd mi0360_init_23[] = {
+ {0x30, 0x0040}, /* reserved - def 0x0005 */
+ {0x31, 0x0000}, /* reserved - def 0x002a */
+ {0x34, 0x0100}, /* reserved - def 0x0100 */
+ {0x3d, 0x068f}, /* reserved - def 0x068f */
+};
+static const struct i2c_write_cmd mi0360_init_24[] = {
+ {0x03, 0x01e5}, /* window height */
+ {0x04, 0x0285}, /* window width */
+};
+static const struct i2c_write_cmd mi0360_init_25[] = {
+ {0x35, 0x0020}, /* global gain */
+ {0x2b, 0x0020}, /* green1 gain */
+ {0x2c, 0x002a}, /* blue gain */
+ {0x2d, 0x0028}, /* red gain */
+ {0x2e, 0x0020}, /* green2 gain */
+};
+static const struct ucbus_write_cmd mi0360_start_1[] = {
+ {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xa6},
+ {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xa6},
+ {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
+ {0xf5f9, 0x00}
+};
+static const struct i2c_write_cmd mi0360_start_2[] = {
+ {0x62, 0x041d}, /* reserved - def 0x0418 */
+};
+static const struct i2c_write_cmd mi0360_start_3[] = {
+ {0x05, 0x007b}, /* horiz blanking */
+};
+static const struct i2c_write_cmd mi0360_start_4[] = {
+ {0x05, 0x03f5}, /* horiz blanking */
+};
+
+static const struct i2c_write_cmd mt9v111_init_0[] = {
+ {0x01, 0x0001}, /* select IFP/SOC registers */
+ {0x06, 0x300c}, /* operating mode control */
+ {0x08, 0xcc00}, /* output format control (RGB) */
+ {0x01, 0x0004}, /* select sensor core registers */
+};
+static const struct i2c_write_cmd mt9v111_init_1[] = {
+ {0x03, 0x01e5}, /* window height */
+ {0x04, 0x0285}, /* window width */
+};
+static const struct i2c_write_cmd mt9v111_init_2[] = {
+ {0x30, 0x7800},
+ {0x31, 0x0000},
+ {0x07, 0x3002}, /* output control */
+ {0x35, 0x0020}, /* global gain */
+ {0x2b, 0x0020}, /* green1 gain */
+ {0x2c, 0x0020}, /* blue gain */
+ {0x2d, 0x0020}, /* red gain */
+ {0x2e, 0x0020}, /* green2 gain */
+};
+static const struct ucbus_write_cmd mt9v111_start_1[] = {
+ {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xaa},
+ {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
+ {0xf5f4, 0xaa},
+ {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
+ {0xf5f9, 0x0a}
+};
+static const struct i2c_write_cmd mt9v111_init_3[] = {
+ {0x62, 0x0405},
+};
+static const struct i2c_write_cmd mt9v111_init_4[] = {
+/* {0x05, 0x00ce}, */
+ {0x05, 0x005d}, /* horizontal blanking */
+};
+
+static const struct ucbus_write_cmd ov7660_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
+ {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
+};
+
+static const struct ucbus_write_cmd ov9630_start_0[] = {
+ {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
+ {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
+};
+
+/* start parameters indexed by [sensor][mode] */
+static const struct cap_s {
+ u8 cc_sizeid;
+ u8 cc_bytes[32];
+} capconfig[4][2] = {
+ [SENSOR_ICX098BQ] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_LZ24BP] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_MI0360] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+ [SENSOR_MT9V111] = {
+ {2, /* Bayer 320x240 */
+ {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ {4, /* Bayer 640x480 */
+ {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
+ 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
+ },
+};
+
+struct sensor_s {
+ const char *name;
+ u8 i2c_addr;
+ u8 i2c_dum;
+ u8 gpio[5];
+ u8 cmd_len;
+ const struct ucbus_write_cmd *cmd;
+};
+
+static const struct sensor_s sensor_tb[] = {
+ [SENSOR_ICX098BQ] = {
+ "icx098bp",
+ 0x00, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 8, icx098bq_start_0
+ },
+ [SENSOR_LZ24BP] = {
+ "lz24bp",
+ 0x00, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 8, lz24bp_start_0
+ },
+ [SENSOR_MI0360] = {
+ "mi0360",
+ 0x5d, 0x80,
+ {SQ930_GPIO_RSTBAR,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ 0
+ },
+ 7, mi0360_start_0
+ },
+ [SENSOR_MT9V111] = {
+ "mt9v111",
+ 0x5c, 0x7f,
+ {SQ930_GPIO_RSTBAR,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ 0
+ },
+ 7, mi0360_start_0
+ },
+ [SENSOR_OV7660] = {
+ "ov7660",
+ 0x21, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 7, ov7660_start_0
+ },
+ [SENSOR_OV9630] = {
+ "ov9630",
+ 0x30, 0x00,
+ {0,
+ SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
+ SQ930_GPIO_DFL_I2C_SDA,
+ 0,
+ SQ930_GPIO_RSTBAR
+ },
+ 7, ov9630_start_0
+ },
+};
+
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 value, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0x0c,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r %04x failed %d\n", value, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ msleep(30);
+ if (ret < 0) {
+ pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
+ const u8 *data, int len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x",
+ value, index, *data, data[len - 1]);
+ memcpy(gspca_dev->usb_buf, data, len);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, gspca_dev->usb_buf, len,
+ 1000);
+ msleep(30);
+ if (ret < 0) {
+ pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void i2c_write(struct sd *sd,
+ const struct i2c_write_cmd *cmd,
+ int ncmds)
+{
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ const struct sensor_s *sensor;
+ u16 val, idx;
+ u8 *buf;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+ sensor = &sensor_tb[sd->sensor];
+
+ val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
+ idx = (cmd->val & 0xff00) | cmd->reg;
+
+ buf = gspca_dev->usb_buf;
+ *buf++ = sensor->i2c_dum;
+ *buf++ = cmd->val;
+
+ while (--ncmds > 0) {
+ cmd++;
+ *buf++ = cmd->reg;
+ *buf++ = cmd->val >> 8;
+ *buf++ = sensor->i2c_dum;
+ *buf++ = cmd->val;
+ }
+
+ PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x",
+ val, idx, gspca_dev->usb_buf[0], buf[-1]);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, idx,
+ gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+ 500);
+ if (ret < 0) {
+ pr_err("i2c_write failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void ucbus_write(struct gspca_dev *gspca_dev,
+ const struct ucbus_write_cmd *cmd,
+ int ncmds,
+ int batchsize)
+{
+ u8 *buf;
+ u16 val, idx;
+ int len, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+
+#ifdef GSPCA_DEBUG
+ if ((batchsize - 1) * 3 > USB_BUF_SZ) {
+ pr_err("Bug: usb_buf overflow\n");
+ gspca_dev->usb_err = -ENOMEM;
+ return;
+ }
+#endif
+
+ for (;;) {
+ len = ncmds;
+ if (len > batchsize)
+ len = batchsize;
+ ncmds -= len;
+
+ val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
+ idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
+
+ buf = gspca_dev->usb_buf;
+ while (--len > 0) {
+ cmd++;
+ *buf++ = cmd->bw_addr;
+ *buf++ = cmd->bw_addr >> 8;
+ *buf++ = cmd->bw_data;
+ }
+ if (buf != gspca_dev->usb_buf)
+ PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x",
+ val, idx,
+ gspca_dev->usb_buf[0], buf[-1]);
+ else
+ PDEBUG(D_USBO, "ucbus v: %04x i: %04x",
+ val, idx);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0x0c, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, idx,
+ gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
+ 500);
+ if (ret < 0) {
+ pr_err("ucbus_write failed %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ msleep(30);
+ if (ncmds <= 0)
+ break;
+ cmd++;
+ }
+}
+
+static void gpio_set(struct sd *sd, u16 val, u16 mask)
+{
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ if (mask & 0x00ff) {
+ sd->gpio[0] &= ~mask;
+ sd->gpio[0] |= val;
+ reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
+ ~sd->gpio[0] << 8);
+ }
+ mask >>= 8;
+ val >>= 8;
+ if (mask) {
+ sd->gpio[1] &= ~mask;
+ sd->gpio[1] |= val;
+ reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
+ ~sd->gpio[1] << 8);
+ }
+}
+
+static void gpio_init(struct sd *sd,
+ const u8 *gpio)
+{
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio++, 0x000f);
+ gpio_set(sd, *gpio, 0x000f);
+}
+
+static void bridge_init(struct sd *sd)
+{
+ static const struct ucbus_write_cmd clkfreq_cmd = {
+ 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */
+ };
+
+ ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
+
+ gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
+}
+
+static void cmos_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ const struct sensor_s *sensor;
+ static const u8 probe_order[] = {
+/* SENSOR_LZ24BP, (tested as ccd) */
+ SENSOR_OV9630,
+ SENSOR_MI0360,
+ SENSOR_OV7660,
+ SENSOR_MT9V111,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
+ sensor = &sensor_tb[probe_order[i]];
+ ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
+ gpio_init(sd, sensor->gpio);
+ msleep(100);
+ reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
+ msleep(100);
+ if (gspca_dev->usb_buf[0] != 0)
+ break;
+ }
+ if (i >= ARRAY_SIZE(probe_order)) {
+ pr_err("Unknown sensor\n");
+ gspca_dev->usb_err = -EINVAL;
+ return;
+ }
+ sd->sensor = probe_order[i];
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV9630:
+ pr_err("Sensor %s not yet treated\n",
+ sensor_tb[sd->sensor].name);
+ gspca_dev->usb_err = -EINVAL;
+ break;
+ }
+}
+
+static void mt9v111_init(struct gspca_dev *gspca_dev)
+{
+ int i, nwait;
+ static const u8 cmd_001b[] = {
+ 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+ };
+ static const u8 cmd_011b[][7] = {
+ {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
+ {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
+ {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
+ {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
+ };
+
+ reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
+ for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
+ reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
+ ARRAY_SIZE(cmd_011b[0]));
+ msleep(400);
+ nwait = 20;
+ for (;;) {
+ reg_r(gspca_dev, 0x031b, 1);
+ if (gspca_dev->usb_buf[0] == 0
+ || gspca_dev->usb_err != 0)
+ break;
+ if (--nwait < 0) {
+ PDEBUG(D_PROBE, "mt9v111_init timeout");
+ gspca_dev->usb_err = -ETIME;
+ return;
+ }
+ msleep(50);
+ }
+ }
+}
+
+static void global_init(struct sd *sd, int first_time)
+{
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ:
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ icx098bq_start_0,
+ 8, 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ case SENSOR_LZ24BP:
+ if (sd->type != Creative_live_motion)
+ gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
+ else
+ gpio_set(sd, 0, 0x00ff);
+ msleep(50);
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ lz24bp_start_0,
+ 8, 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ case SENSOR_MI0360:
+ if (first_time)
+ ucbus_write(&sd->gspca_dev,
+ mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
+ break;
+ default:
+/* case SENSOR_MT9V111: */
+ if (first_time)
+ mt9v111_init(&sd->gspca_dev);
+ else
+ gpio_init(sd, sensor_tb[sd->sensor].gpio);
+ break;
+ }
+}
+
+static void lz24bp_ppl(struct sd *sd, u16 ppl)
+{
+ struct ucbus_write_cmd cmds[2] = {
+ {0xf810, ppl >> 8},
+ {0xf811, ppl}
+ };
+
+ ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, integclks, intstartclk, frameclks, min_frclk;
+ const struct sensor_s *sensor;
+ u16 cmd;
+ u8 buf[15];
+
+ integclks = expo;
+ i = 0;
+ cmd = SQ930_CTRL_SET_EXPOSURE;
+
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ: /* ccd */
+ case SENSOR_LZ24BP:
+ min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
+ if (integclks >= min_frclk) {
+ intstartclk = 0;
+ frameclks = integclks;
+ } else {
+ intstartclk = min_frclk - integclks;
+ frameclks = min_frclk;
+ }
+ buf[i++] = intstartclk >> 8;
+ buf[i++] = intstartclk;
+ buf[i++] = frameclks >> 8;
+ buf[i++] = frameclks;
+ buf[i++] = gain;
+ break;
+ default: /* cmos */
+/* case SENSOR_MI0360: */
+/* case SENSOR_MT9V111: */
+ cmd |= 0x0100;
+ sensor = &sensor_tb[sd->sensor];
+ buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */
+ buf[i++] = 0x08; /* 2 * ni2c */
+ buf[i++] = 0x09; /* reg = shutter width */
+ buf[i++] = integclks >> 8; /* val H */
+ buf[i++] = sensor->i2c_dum;
+ buf[i++] = integclks; /* val L */
+ buf[i++] = 0x35; /* reg = global gain */
+ buf[i++] = 0x00; /* val H */
+ buf[i++] = sensor->i2c_dum;
+ buf[i++] = 0x80 + gain / 2; /* val L */
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x00;
+ buf[i++] = 0x83;
+ break;
+ }
+ reg_wb(gspca_dev, cmd, 0, buf, i);
+}
+
+/* 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 sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ sd->sensor = id->driver_info >> 8;
+ sd->type = id->driver_info;
+
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+ cam->bulk = 1;
+
+ 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;
+
+ sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */
+
+/*fixme: is this needed for icx098bp and mi0360?
+ if (sd->sensor != SENSOR_LZ24BP)
+ reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
+ */
+
+ reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+
+/* it returns:
+ * 03 00 12 93 0b f6 c9 00 live! ultra
+ * 03 00 07 93 0b f6 ca 00 live! ultra for notebook
+ * 03 00 12 93 0b fe c8 00 Trust WB-3500T
+ * 02 00 06 93 0b fe c8 00 Joy-IT 318S
+ * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq
+ * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam
+ *
+ * byte
+ * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
+ * 1: 00
+ * 2: 06 / 07 / 12 = mode webcam? firmware??
+ * 3: 93 chip = 930b (930b or 930c)
+ * 4: 0b
+ * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
+ * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
+ * 7: 00
+ */
+ PDEBUG(D_PROBE, "info: %*ph", 8, gspca_dev->usb_buf);
+
+ bridge_init(sd);
+
+ if (sd->sensor == SENSOR_MI0360) {
+
+ /* no sensor probe for icam tracer */
+ if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */
+ sd->sensor = SENSOR_ICX098BQ;
+ else
+ cmos_probe(gspca_dev);
+ }
+ if (gspca_dev->usb_err >= 0) {
+ PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
+ global_init(sd, 1);
+ }
+ return gspca_dev->usb_err;
+}
+
+/* send the start/stop commands to the webcam */
+static void send_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const struct cap_s *cap;
+ int mode;
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ cap = &capconfig[sd->sensor][mode];
+ reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
+ 0x0a00 | cap->cc_sizeid,
+ cap->cc_bytes, 32);
+}
+
+static void send_stop(struct gspca_dev *gspca_dev)
+{
+ reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
+}
+
+/* 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;
+
+ gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
+ sd->do_ctrl = 0;
+ gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
+ return 0;
+}
+
+/* start the capture */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+
+ bridge_init(sd);
+ global_init(sd, 0);
+ msleep(100);
+
+ switch (sd->sensor) {
+ case SENSOR_ICX098BQ:
+ ucbus_write(gspca_dev, icx098bq_start_0,
+ ARRAY_SIZE(icx098bq_start_0),
+ 8);
+ ucbus_write(gspca_dev, icx098bq_start_1,
+ ARRAY_SIZE(icx098bq_start_1),
+ 5);
+ ucbus_write(gspca_dev, icx098bq_start_2,
+ ARRAY_SIZE(icx098bq_start_2),
+ 6);
+ msleep(50);
+
+ /* 1st start */
+ send_start(gspca_dev);
+ gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+ msleep(70);
+ reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
+ gpio_set(sd, 0x7f, 0x00ff);
+
+ /* 2nd start */
+ send_start(gspca_dev);
+ gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
+ goto out;
+ case SENSOR_LZ24BP:
+ ucbus_write(gspca_dev, lz24bp_start_0,
+ ARRAY_SIZE(lz24bp_start_0),
+ 8);
+ if (sd->type != Creative_live_motion)
+ ucbus_write(gspca_dev, lz24bp_start_1_gen,
+ ARRAY_SIZE(lz24bp_start_1_gen),
+ 5);
+ else
+ ucbus_write(gspca_dev, lz24bp_start_1_clm,
+ ARRAY_SIZE(lz24bp_start_1_clm),
+ 5);
+ ucbus_write(gspca_dev, lz24bp_start_2,
+ ARRAY_SIZE(lz24bp_start_2),
+ 6);
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
+ msleep(10);
+ break;
+ case SENSOR_MI0360:
+ ucbus_write(gspca_dev, mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ i2c_write(sd, mi0360_init_23,
+ ARRAY_SIZE(mi0360_init_23));
+ i2c_write(sd, mi0360_init_24,
+ ARRAY_SIZE(mi0360_init_24));
+ i2c_write(sd, mi0360_init_25,
+ ARRAY_SIZE(mi0360_init_25));
+ ucbus_write(gspca_dev, mi0360_start_1,
+ ARRAY_SIZE(mi0360_start_1),
+ 5);
+ i2c_write(sd, mi0360_start_2,
+ ARRAY_SIZE(mi0360_start_2));
+ i2c_write(sd, mi0360_start_3,
+ ARRAY_SIZE(mi0360_start_3));
+
+ /* 1st start */
+ send_start(gspca_dev);
+ msleep(60);
+ send_stop(gspca_dev);
+
+ i2c_write(sd,
+ mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
+ break;
+ default:
+/* case SENSOR_MT9V111: */
+ ucbus_write(gspca_dev, mi0360_start_0,
+ ARRAY_SIZE(mi0360_start_0),
+ 8);
+ i2c_write(sd, mt9v111_init_0,
+ ARRAY_SIZE(mt9v111_init_0));
+ i2c_write(sd, mt9v111_init_1,
+ ARRAY_SIZE(mt9v111_init_1));
+ i2c_write(sd, mt9v111_init_2,
+ ARRAY_SIZE(mt9v111_init_2));
+ ucbus_write(gspca_dev, mt9v111_start_1,
+ ARRAY_SIZE(mt9v111_start_1),
+ 5);
+ i2c_write(sd, mt9v111_init_3,
+ ARRAY_SIZE(mt9v111_init_3));
+ i2c_write(sd, mt9v111_init_4,
+ ARRAY_SIZE(mt9v111_init_4));
+ break;
+ }
+
+ send_start(gspca_dev);
+out:
+ msleep(1000);
+
+ if (sd->sensor == SENSOR_MT9V111)
+ gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
+
+ sd->do_ctrl = 1; /* set the exposure */
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_MT9V111)
+ gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
+ send_stop(gspca_dev);
+}
+
+/* function called when the application gets a new frame */
+/* It sets the exposure if required and restart the bulk transfer. */
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
+
+ if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
+ return;
+ sd->do_ctrl = 0;
+
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
+ v4l2_ctrl_g_ctrl(sd->gain));
+
+ gspca_dev->cam.bulk_nurbs = 1;
+ ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
+ if (ret < 0)
+ pr_err("sd_dq_callback() err %d\n", ret);
+
+ /* wait a little time, otherwise the webcam crashes */
+ msleep(100);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->do_ctrl)
+ gspca_dev->cam.bulk_nurbs = 0;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val, sd->gain->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 1, 255, 1, 0x8d);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(2, &sd->exposure);
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+};
+
+/* Table of supported USB devices */
+#define ST(sensor, type) \
+ .driver_info = (SENSOR_ ## sensor << 8) \
+ | (type)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
+ {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
+ {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
+ {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
+ {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
+ {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/usb/gspca/stk014.c
index 8c0982607f25..8c0982607f25 100644
--- a/drivers/media/video/gspca/stk014.c
+++ b/drivers/media/usb/gspca/stk014.c
diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/usb/gspca/stv0680.c
index 67605272aaa8..67605272aaa8 100644
--- a/drivers/media/video/gspca/stv0680.c
+++ b/drivers/media/usb/gspca/stv0680.c
diff --git a/drivers/media/video/gspca/stv06xx/Kconfig b/drivers/media/usb/gspca/stv06xx/Kconfig
index 634ad38d9fb8..634ad38d9fb8 100644
--- a/drivers/media/video/gspca/stv06xx/Kconfig
+++ b/drivers/media/usb/gspca/stv06xx/Kconfig
diff --git a/drivers/media/usb/gspca/stv06xx/Makefile b/drivers/media/usb/gspca/stv06xx/Makefile
new file mode 100644
index 000000000000..3a4b2f899049
--- /dev/null
+++ b/drivers/media/usb/gspca/stv06xx/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
+
+gspca_stv06xx-objs := stv06xx.o \
+ stv06xx_vv6410.o \
+ stv06xx_hdcs.o \
+ stv06xx_pb0100.o \
+ stv06xx_st6422.o
+
+ccflags-y += -I$(srctree)/drivers/media/usb/gspca
+
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c
index 999ec7764449..999ec7764449 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/usb/gspca/stv06xx/stv06xx.h
index 34957a4ec150..34957a4ec150 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx.h
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c
index 06fa54c5efb2..06fa54c5efb2 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.c
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h
index 1ba9158d0102..1ba9158d0102 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_hdcs.h
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c
index cdfc3d05ab6b..cdfc3d05ab6b 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.c
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h
index 5071e5353fd3..5071e5353fd3 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_pb0100.h
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h
index 3a498c2495c6..3a498c2495c6 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_sensor.h
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c
index 8a57990dfe0f..8a57990dfe0f 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.c
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h
index 8f20fbf30f33..8f20fbf30f33 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_st6422.h
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
index 748e1421d6d8..748e1421d6d8 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.c
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h
index 53e67b40ca05..53e67b40ca05 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+++ b/drivers/media/usb/gspca/stv06xx/stv06xx_vv6410.h
diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/usb/gspca/sunplus.c
index 9ccfcb1c6479..9ccfcb1c6479 100644
--- a/drivers/media/video/gspca/sunplus.c
+++ b/drivers/media/usb/gspca/sunplus.c
diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/usb/gspca/t613.c
index 8bc6c3ceec2c..8bc6c3ceec2c 100644
--- a/drivers/media/video/gspca/t613.c
+++ b/drivers/media/usb/gspca/t613.c
diff --git a/drivers/media/usb/gspca/topro.c b/drivers/media/usb/gspca/topro.c
new file mode 100644
index 000000000000..4cb511ccc5f6
--- /dev/null
+++ b/drivers/media/usb/gspca/topro.c
@@ -0,0 +1,4969 @@
+/*
+ * Topro TP6800/6810 webcam driver.
+ *
+ * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
+ * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
+ * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "gspca.h"
+
+MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Anders Blomdell <anders.blomdell@control.lth.se>");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+/* JPEG header */
+static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+
+/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+0,
+#define JPEG_QT0_OFFSET 7
+ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+1,
+#define JPEG_QT1_OFFSET 72
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+ /* Define Huffman table (thanks to Thomas Kaiser) */
+ 0xff, 0xc4, 0x01, 0x5e,
+ 0x00, 0x00, 0x02, 0x03,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+ 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
+ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
+ 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
+ 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
+ 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
+ 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
+ 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
+ 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
+ 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
+ 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
+ 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
+ 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
+ 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
+ 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+ 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
+ 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
+ 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
+ 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
+ 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
+ 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
+ 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
+ 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
+ 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
+ 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
+ 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+#define JPEG_HEIGHT_OFFSET 493
+ 0x01, 0xe0, /* height */
+ 0x02, 0x80, /* width */
+ 0x03, /* component number */
+ 0x01,
+ 0x21, /* samples Y = jpeg 422 */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+#define JPEG_HDR_SZ 521
+};
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct v4l2_ctrl *jpegqual;
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *gamma;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+
+ u8 framerate;
+ u8 quality; /* webcam current JPEG quality (0..16) */
+ s8 ag_cnt; /* autogain / start counter for tp6810 */
+#define AG_CNT_START 13 /* check gain every N frames */
+
+ u8 bridge;
+ u8 sensor;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+
+enum bridges {
+ BRIDGE_TP6800,
+ BRIDGE_TP6810,
+};
+
+enum sensors {
+ SENSOR_CX0342,
+ SENSOR_SOI763A, /* ~= ov7630 / ov7648 */
+ NSENSORS
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 4 / 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}
+};
+
+/*
+ * JPEG quality
+ * index: webcam compression
+ * value: JPEG quality in %
+ */
+static const u8 jpeg_q[17] = {
+ 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
+};
+
+#define BULK_OUT_SIZE 0x20
+#if BULK_OUT_SIZE > USB_BUF_SZ
+#error "USB buffer too small"
+#endif
+
+static const u8 rates[] = {30, 20, 15, 10, 7, 5};
+static const struct framerates framerates[] = {
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ },
+ {
+ .rates = rates,
+ .nrates = ARRAY_SIZE(rates)
+ }
+};
+static const u8 rates_6810[] = {30, 15, 10, 7, 5};
+static const struct framerates framerates_6810[] = {
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ },
+ {
+ .rates = rates_6810,
+ .nrates = ARRAY_SIZE(rates_6810)
+ }
+};
+
+/*
+ * webcam quality in %
+ * the last value is the ultra fine quality
+ */
+
+/* TP6800 register offsets */
+#define TP6800_R10_SIF_TYPE 0x10
+#define TP6800_R11_SIF_CONTROL 0x11
+#define TP6800_R12_SIF_ADDR_S 0x12
+#define TP6800_R13_SIF_TX_DATA 0x13
+#define TP6800_R14_SIF_RX_DATA 0x14
+#define TP6800_R15_GPIO_PU 0x15
+#define TP6800_R16_GPIO_PD 0x16
+#define TP6800_R17_GPIO_IO 0x17
+#define TP6800_R18_GPIO_DATA 0x18
+#define TP6800_R19_SIF_ADDR_S2 0x19
+#define TP6800_R1A_SIF_TX_DATA2 0x1a
+#define TP6800_R1B_SIF_RX_DATA2 0x1b
+#define TP6800_R21_ENDP_1_CTL 0x21
+#define TP6800_R2F_TIMING_CFG 0x2f
+#define TP6800_R30_SENSOR_CFG 0x30
+#define TP6800_R31_PIXEL_START 0x31
+#define TP6800_R32_PIXEL_END_L 0x32
+#define TP6800_R33_PIXEL_END_H 0x33
+#define TP6800_R34_LINE_START 0x34
+#define TP6800_R35_LINE_END_L 0x35
+#define TP6800_R36_LINE_END_H 0x36
+#define TP6800_R37_FRONT_DARK_ST 0x37
+#define TP6800_R38_FRONT_DARK_END 0x38
+#define TP6800_R39_REAR_DARK_ST_L 0x39
+#define TP6800_R3A_REAR_DARK_ST_H 0x3a
+#define TP6800_R3B_REAR_DARK_END_L 0x3b
+#define TP6800_R3C_REAR_DARK_END_H 0x3c
+#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d
+#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e
+#define TP6800_R3F_FRAME_RATE 0x3f
+#define TP6800_R50 0x50
+#define TP6800_R51 0x51
+#define TP6800_R52 0x52
+#define TP6800_R53 0x53
+#define TP6800_R54_DARK_CFG 0x54
+#define TP6800_R55_GAMMA_R 0x55
+#define TP6800_R56_GAMMA_G 0x56
+#define TP6800_R57_GAMMA_B 0x57
+#define TP6800_R5C_EDGE_THRLD 0x5c
+#define TP6800_R5D_DEMOSAIC_CFG 0x5d
+#define TP6800_R78_FORMAT 0x78
+#define TP6800_R79_QUALITY 0x79
+#define TP6800_R7A_BLK_THRLD 0x7a
+
+/* CX0342 register offsets */
+
+#define CX0342_SENSOR_ID 0x00
+#define CX0342_VERSION_NO 0x01
+#define CX0342_ORG_X_L 0x02
+#define CX0342_ORG_X_H 0x03
+#define CX0342_ORG_Y_L 0x04
+#define CX0342_ORG_Y_H 0x05
+#define CX0342_STOP_X_L 0x06
+#define CX0342_STOP_X_H 0x07
+#define CX0342_STOP_Y_L 0x08
+#define CX0342_STOP_Y_H 0x09
+#define CX0342_FRAME_WIDTH_L 0x0a
+#define CX0342_FRAME_WIDTH_H 0x0b
+#define CX0342_FRAME_HEIGH_L 0x0c
+#define CX0342_FRAME_HEIGH_H 0x0d
+#define CX0342_EXPO_LINE_L 0x10
+#define CX0342_EXPO_LINE_H 0x11
+#define CX0342_EXPO_CLK_L 0x12
+#define CX0342_EXPO_CLK_H 0x13
+#define CX0342_RAW_GRGAIN_L 0x14
+#define CX0342_RAW_GRGAIN_H 0x15
+#define CX0342_RAW_GBGAIN_L 0x16
+#define CX0342_RAW_GBGAIN_H 0x17
+#define CX0342_RAW_RGAIN_L 0x18
+#define CX0342_RAW_RGAIN_H 0x19
+#define CX0342_RAW_BGAIN_L 0x1a
+#define CX0342_RAW_BGAIN_H 0x1b
+#define CX0342_GLOBAL_GAIN 0x1c
+#define CX0342_SYS_CTRL_0 0x20
+#define CX0342_SYS_CTRL_1 0x21
+#define CX0342_SYS_CTRL_2 0x22
+#define CX0342_BYPASS_MODE 0x23
+#define CX0342_SYS_CTRL_3 0x24
+#define CX0342_TIMING_EN 0x25
+#define CX0342_OUTPUT_CTRL 0x26
+#define CX0342_AUTO_ADC_CALIB 0x27
+#define CX0342_SYS_CTRL_4 0x28
+#define CX0342_ADCGN 0x30
+#define CX0342_SLPCR 0x31
+#define CX0342_SLPFN_LO 0x32
+#define CX0342_ADC_CTL 0x33
+#define CX0342_LVRST_BLBIAS 0x34
+#define CX0342_VTHSEL 0x35
+#define CX0342_RAMP_RIV 0x36
+#define CX0342_LDOSEL 0x37
+#define CX0342_CLOCK_GEN 0x40
+#define CX0342_SOFT_RESET 0x41
+#define CX0342_PLL 0x42
+#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43
+#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44
+#define CX0342_DR_ENH_PULSE_POS_L 0x45
+#define CX0342_DR_ENH_PULSE_POS_H 0x46
+#define CX0342_DR_ENH_PULSE_WIDTH 0x47
+#define CX0342_AS_CURRENT_CNT_L 0x48
+#define CX0342_AS_CURRENT_CNT_H 0x49
+#define CX0342_AS_PREVIOUS_CNT_L 0x4a
+#define CX0342_AS_PREVIOUS_CNT_H 0x4b
+#define CX0342_SPV_VALUE_L 0x4c
+#define CX0342_SPV_VALUE_H 0x4d
+#define CX0342_GPXLTHD_L 0x50
+#define CX0342_GPXLTHD_H 0x51
+#define CX0342_RBPXLTHD_L 0x52
+#define CX0342_RBPXLTHD_H 0x53
+#define CX0342_PLANETHD_L 0x54
+#define CX0342_PLANETHD_H 0x55
+#define CX0342_ROWDARK_TH 0x56
+#define CX0342_ROWDARK_TOL 0x57
+#define CX0342_RB_GAP_L 0x58
+#define CX0342_RB_GAP_H 0x59
+#define CX0342_G_GAP_L 0x5a
+#define CX0342_G_GAP_H 0x5b
+#define CX0342_AUTO_ROW_DARK 0x60
+#define CX0342_MANUAL_DARK_VALUE 0x61
+#define CX0342_GB_DARK_OFFSET 0x62
+#define CX0342_GR_DARK_OFFSET 0x63
+#define CX0342_RED_DARK_OFFSET 0x64
+#define CX0342_BLUE_DARK_OFFSET 0x65
+#define CX0342_DATA_SCALING_MULTI 0x66
+#define CX0342_AUTOD_Q_FRAME 0x67
+#define CX0342_AUTOD_ALLOW_VARI 0x68
+#define CX0342_AUTO_DARK_VALUE_L 0x69
+#define CX0342_AUTO_DARK_VALUE_H 0x6a
+#define CX0342_IO_CTRL_0 0x70
+#define CX0342_IO_CTRL_1 0x71
+#define CX0342_IO_CTRL_2 0x72
+#define CX0342_IDLE_CTRL 0x73
+#define CX0342_TEST_MODE 0x74
+#define CX0342_FRAME_FIX_DATA_TEST 0x75
+#define CX0342_FRAME_CNT_TEST 0x76
+#define CX0342_RST_OVERFLOW_L 0x80
+#define CX0342_RST_OVERFLOW_H 0x81
+#define CX0342_RST_UNDERFLOW_L 0x82
+#define CX0342_RST_UNDERFLOW_H 0x83
+#define CX0342_DATA_OVERFLOW_L 0x84
+#define CX0342_DATA_OVERFLOW_H 0x85
+#define CX0342_DATA_UNDERFLOW_L 0x86
+#define CX0342_DATA_UNDERFLOW_H 0x87
+#define CX0342_CHANNEL_0_0_L_irst 0x90
+#define CX0342_CHANNEL_0_0_H_irst 0x91
+#define CX0342_CHANNEL_0_1_L_irst 0x92
+#define CX0342_CHANNEL_0_1_H_irst 0x93
+#define CX0342_CHANNEL_0_2_L_irst 0x94
+#define CX0342_CHANNEL_0_2_H_irst 0x95
+#define CX0342_CHANNEL_0_3_L_irst 0x96
+#define CX0342_CHANNEL_0_3_H_irst 0x97
+#define CX0342_CHANNEL_0_4_L_irst 0x98
+#define CX0342_CHANNEL_0_4_H_irst 0x99
+#define CX0342_CHANNEL_0_5_L_irst 0x9a
+#define CX0342_CHANNEL_0_5_H_irst 0x9b
+#define CX0342_CHANNEL_0_6_L_irst 0x9c
+#define CX0342_CHANNEL_0_6_H_irst 0x9d
+#define CX0342_CHANNEL_0_7_L_irst 0x9e
+#define CX0342_CHANNEL_0_7_H_irst 0x9f
+#define CX0342_CHANNEL_1_0_L_itx 0xa0
+#define CX0342_CHANNEL_1_0_H_itx 0xa1
+#define CX0342_CHANNEL_1_1_L_itx 0xa2
+#define CX0342_CHANNEL_1_1_H_itx 0xa3
+#define CX0342_CHANNEL_1_2_L_itx 0xa4
+#define CX0342_CHANNEL_1_2_H_itx 0xa5
+#define CX0342_CHANNEL_1_3_L_itx 0xa6
+#define CX0342_CHANNEL_1_3_H_itx 0xa7
+#define CX0342_CHANNEL_1_4_L_itx 0xa8
+#define CX0342_CHANNEL_1_4_H_itx 0xa9
+#define CX0342_CHANNEL_1_5_L_itx 0xaa
+#define CX0342_CHANNEL_1_5_H_itx 0xab
+#define CX0342_CHANNEL_1_6_L_itx 0xac
+#define CX0342_CHANNEL_1_6_H_itx 0xad
+#define CX0342_CHANNEL_1_7_L_itx 0xae
+#define CX0342_CHANNEL_1_7_H_itx 0xaf
+#define CX0342_CHANNEL_2_0_L_iwl 0xb0
+#define CX0342_CHANNEL_2_0_H_iwl 0xb1
+#define CX0342_CHANNEL_2_1_L_iwl 0xb2
+#define CX0342_CHANNEL_2_1_H_iwl 0xb3
+#define CX0342_CHANNEL_2_2_L_iwl 0xb4
+#define CX0342_CHANNEL_2_2_H_iwl 0xb5
+#define CX0342_CHANNEL_2_3_L_iwl 0xb6
+#define CX0342_CHANNEL_2_3_H_iwl 0xb7
+#define CX0342_CHANNEL_2_4_L_iwl 0xb8
+#define CX0342_CHANNEL_2_4_H_iwl 0xb9
+#define CX0342_CHANNEL_2_5_L_iwl 0xba
+#define CX0342_CHANNEL_2_5_H_iwl 0xbb
+#define CX0342_CHANNEL_2_6_L_iwl 0xbc
+#define CX0342_CHANNEL_2_6_H_iwl 0xbd
+#define CX0342_CHANNEL_2_7_L_iwl 0xbe
+#define CX0342_CHANNEL_2_7_H_iwl 0xbf
+#define CX0342_CHANNEL_3_0_L_ensp 0xc0
+#define CX0342_CHANNEL_3_0_H_ensp 0xc1
+#define CX0342_CHANNEL_3_1_L_ensp 0xc2
+#define CX0342_CHANNEL_3_1_H_ensp 0xc3
+#define CX0342_CHANNEL_3_2_L_ensp 0xc4
+#define CX0342_CHANNEL_3_2_H_ensp 0xc5
+#define CX0342_CHANNEL_3_3_L_ensp 0xc6
+#define CX0342_CHANNEL_3_3_H_ensp 0xc7
+#define CX0342_CHANNEL_3_4_L_ensp 0xc8
+#define CX0342_CHANNEL_3_4_H_ensp 0xc9
+#define CX0342_CHANNEL_3_5_L_ensp 0xca
+#define CX0342_CHANNEL_3_5_H_ensp 0xcb
+#define CX0342_CHANNEL_3_6_L_ensp 0xcc
+#define CX0342_CHANNEL_3_6_H_ensp 0xcd
+#define CX0342_CHANNEL_3_7_L_ensp 0xce
+#define CX0342_CHANNEL_3_7_H_ensp 0xcf
+#define CX0342_CHANNEL_4_0_L_sela 0xd0
+#define CX0342_CHANNEL_4_0_H_sela 0xd1
+#define CX0342_CHANNEL_4_1_L_sela 0xd2
+#define CX0342_CHANNEL_4_1_H_sela 0xd3
+#define CX0342_CHANNEL_5_0_L_intla 0xe0
+#define CX0342_CHANNEL_5_0_H_intla 0xe1
+#define CX0342_CHANNEL_5_1_L_intla 0xe2
+#define CX0342_CHANNEL_5_1_H_intla 0xe3
+#define CX0342_CHANNEL_5_2_L_intla 0xe4
+#define CX0342_CHANNEL_5_2_H_intla 0xe5
+#define CX0342_CHANNEL_5_3_L_intla 0xe6
+#define CX0342_CHANNEL_5_3_H_intla 0xe7
+#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
+#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
+#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2
+#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3
+#define CX0342_SENSOR_HEIGHT_L 0xfb
+#define CX0342_SENSOR_HEIGHT_H 0xfc
+#define CX0342_SENSOR_WIDTH_L 0xfd
+#define CX0342_SENSOR_WIDTH_H 0xfe
+#define CX0342_VSYNC_HSYNC_READ 0xff
+
+struct cmd {
+ u8 reg;
+ u8 val;
+};
+
+static const u8 DQT[17][130] = {
+ /* Define quantization table (thanks to Thomas Kaiser) */
+ { /* Quality 0 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
+ 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ },
+ { /* Quality 1 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x01,
+ 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
+ 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ },
+ { /* Quality 2 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x01,
+ 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
+ 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
+ },
+ { /* Quality 3 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
+ 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
+ 0x01,
+ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+ },
+ { /* Quality 4 */
+ 0x00,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
+ 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x01,
+ 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
+ 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
+ },
+ { /* Quality 5 */
+ 0x00,
+ 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x01,
+ 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
+ 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
+ },
+ { /* Quality 6 */
+ 0x00,
+ 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
+ 0x01,
+ 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
+ 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
+ },
+ { /* Quality 7 */
+ 0x00,
+ 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
+ 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
+ 0x01,
+ 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
+ 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ },
+ { /* Quality 8 */
+ 0x00,
+ 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
+ 0x01,
+ 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
+ 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ },
+ { /* Quality 9 */
+ 0x00,
+ 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
+ 0x01,
+ 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
+ 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 10 */
+ 0x00,
+ 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+ 0x01,
+ 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
+ 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 11 */
+ 0x00,
+ 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
+ 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
+ 0x01,
+ 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
+ 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 12 */
+ 0x00,
+ 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
+ 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
+ 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
+ 0x01,
+ 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
+ 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 13 */
+ 0x00,
+ 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
+ 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
+ 0x01,
+ 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
+ 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 14 */
+ 0x00,
+ 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
+ 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
+ 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+ 0x01,
+ 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
+ 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 15 */
+ 0x00,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
+ 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x01,
+ 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
+ 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ },
+ { /* Quality 16-31 */
+ 0x00,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x01,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ }
+};
+
+static const struct cmd tp6810_cx_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x37},
+ {TP6800_R30_SENSOR_CFG, 0x10},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {0x4b, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x83},
+ {TP6800_R79_QUALITY, 0x05},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {0x7c, 0x04},
+ {0x25, 0x14},
+ {0x26, 0x0f},
+ {0x7b, 0x10},
+};
+
+static const struct cmd tp6810_ov_init_common[] = {
+ {0x1c, 0x00},
+ {TP6800_R10_SIF_TYPE, 0x00},
+ {0x4e, 0x00},
+ {0x4f, 0x00},
+ {TP6800_R50, 0xff},
+ {TP6800_R51, 0x03},
+ {0x00, 0x07},
+ {TP6800_R52, 0x40},
+ {TP6800_R53, 0x40},
+ {TP6800_R54_DARK_CFG, 0x40},
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x86},
+ {0x25, 0x18},
+ {0x26, 0x0f},
+ {0x7b, 0x90},
+};
+
+static const struct cmd tp6810_bridge_start[] = {
+ {0x59, 0x88},
+ {0x5a, 0x0f},
+ {0x5b, 0x4e},
+ {TP6800_R5C_EDGE_THRLD, 0x63},
+ {TP6800_R5D_DEMOSAIC_CFG, 0x00},
+ {0x03, 0x7f},
+ {0x04, 0x80},
+ {0x06, 0x00},
+ {0x00, 0x00},
+};
+
+static const struct cmd tp6810_late_start[] = {
+ {0x7d, 0x01},
+ {0xb0, 0x04},
+ {0xb1, 0x04},
+ {0xb2, 0x04},
+ {0xb3, 0x04},
+ {0xb4, 0x04},
+ {0xb5, 0x04},
+ {0xb6, 0x08},
+ {0xb7, 0x08},
+ {0xb8, 0x04},
+ {0xb9, 0x04},
+ {0xba, 0x04},
+ {0xbb, 0x04},
+ {0xbc, 0x04},
+ {0xbd, 0x08},
+ {0xbe, 0x08},
+ {0xbf, 0x08},
+ {0xc0, 0x04},
+ {0xc1, 0x04},
+ {0xc2, 0x08},
+ {0xc3, 0x08},
+ {0xc4, 0x08},
+ {0xc5, 0x08},
+ {0xc6, 0x08},
+ {0xc7, 0x13},
+ {0xc8, 0x04},
+ {0xc9, 0x08},
+ {0xca, 0x08},
+ {0xcb, 0x08},
+ {0xcc, 0x08},
+ {0xcd, 0x08},
+ {0xce, 0x13},
+ {0xcf, 0x13},
+ {0xd0, 0x08},
+ {0xd1, 0x08},
+ {0xd2, 0x08},
+ {0xd3, 0x08},
+ {0xd4, 0x08},
+ {0xd5, 0x13},
+ {0xd6, 0x13},
+ {0xd7, 0x13},
+ {0xd8, 0x08},
+ {0xd9, 0x08},
+ {0xda, 0x08},
+ {0xdb, 0x08},
+ {0xdc, 0x13},
+ {0xdd, 0x13},
+ {0xde, 0x13},
+ {0xdf, 0x13},
+ {0xe0, 0x08},
+ {0xe1, 0x08},
+ {0xe2, 0x08},
+ {0xe3, 0x13},
+ {0xe4, 0x13},
+ {0xe5, 0x13},
+ {0xe6, 0x13},
+ {0xe7, 0x13},
+ {0xe8, 0x08},
+ {0xe9, 0x08},
+ {0xea, 0x13},
+ {0xeb, 0x13},
+ {0xec, 0x13},
+ {0xed, 0x13},
+ {0xee, 0x13},
+ {0xef, 0x13},
+ {0x7d, 0x02},
+
+ /* later after isoc start */
+ {0x7d, 0x08},
+ {0x7d, 0x00},
+};
+
+static const struct cmd cx0342_timing_seq[] = {
+ {CX0342_CHANNEL_0_1_L_irst, 0x20},
+ {CX0342_CHANNEL_0_2_L_irst, 0x24},
+ {CX0342_CHANNEL_0_2_H_irst, 0x00},
+ {CX0342_CHANNEL_0_3_L_irst, 0x2f},
+ {CX0342_CHANNEL_0_3_H_irst, 0x00},
+ {CX0342_CHANNEL_1_0_L_itx, 0x02},
+ {CX0342_CHANNEL_1_0_H_itx, 0x00},
+ {CX0342_CHANNEL_1_1_L_itx, 0x20},
+ {CX0342_CHANNEL_1_1_H_itx, 0x00},
+ {CX0342_CHANNEL_1_2_L_itx, 0xe4},
+ {CX0342_CHANNEL_1_2_H_itx, 0x00},
+ {CX0342_CHANNEL_1_3_L_itx, 0xee},
+ {CX0342_CHANNEL_1_3_H_itx, 0x00},
+ {CX0342_CHANNEL_2_0_L_iwl, 0x30},
+ {CX0342_CHANNEL_2_0_H_iwl, 0x00},
+ {CX0342_CHANNEL_3_0_L_ensp, 0x34},
+ {CX0342_CHANNEL_3_1_L_ensp, 0xe2},
+ {CX0342_CHANNEL_3_1_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_2_L_ensp, 0xf6},
+ {CX0342_CHANNEL_3_2_H_ensp, 0x00},
+ {CX0342_CHANNEL_3_3_L_ensp, 0xf4},
+ {CX0342_CHANNEL_3_3_H_ensp, 0x02},
+ {CX0342_CHANNEL_4_0_L_sela, 0x26},
+ {CX0342_CHANNEL_4_0_H_sela, 0x00},
+ {CX0342_CHANNEL_4_1_L_sela, 0xe2},
+ {CX0342_CHANNEL_4_1_H_sela, 0x00},
+ {CX0342_CHANNEL_5_0_L_intla, 0x26},
+ {CX0342_CHANNEL_5_1_L_intla, 0x29},
+ {CX0342_CHANNEL_5_2_L_intla, 0xf0},
+ {CX0342_CHANNEL_5_2_H_intla, 0x00},
+ {CX0342_CHANNEL_5_3_L_intla, 0xf3},
+ {CX0342_CHANNEL_5_3_H_intla, 0x00},
+ {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
+ {CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
+ {CX0342_TIMING_EN, 0x01},
+};
+
+/* define the JPEG header */
+static void jpeg_define(u8 *jpeg_hdr,
+ int height,
+ int width)
+{
+ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
+ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
+}
+
+/* set the JPEG quality for sensor soi763a */
+static void jpeg_set_qual(u8 *jpeg_hdr,
+ int quality)
+{
+ int i, sc;
+
+ if (quality < 50)
+ sc = 5000 / quality;
+ else
+ sc = 200 - quality * 2;
+ for (i = 0; i < 64; i++) {
+ jpeg_hdr[JPEG_QT0_OFFSET + i] =
+ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
+ jpeg_hdr[JPEG_QT1_OFFSET + i] =
+ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
+ }
+}
+
+static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x0e,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+/* the returned value is in gspca_dev->usb_buf */
+static void reg_r(struct gspca_dev *gspca_dev, u8 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x0d,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1, 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ reg_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return 0;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1; /* error */
+}
+
+static void i2c_w_buf(struct gspca_dev *gspca_dev,
+ const struct cmd *p, int l)
+{
+ do {
+ i2c_w(gspca_dev, p->reg, p->val);
+ p++;
+ } while (--l > 0);
+}
+
+static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int v;
+
+ reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
+ msleep(5);
+ reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
+ v = gspca_dev->usb_buf[0];
+ if (sd->bridge == BRIDGE_TP6800)
+ return v;
+ if (len > 1) {
+ reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
+ v |= (gspca_dev->usb_buf[0] << 8);
+ }
+ reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
+ if (gspca_dev->usb_buf[0] == 0)
+ return v;
+ reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
+ return -1;
+}
+
+static void bulk_w(struct gspca_dev *gspca_dev,
+ u8 tag,
+ const u8 *data,
+ int length)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int count, actual_count, ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ for (;;) {
+ count = length > BULK_OUT_SIZE - 1
+ ? BULK_OUT_SIZE - 1 : length;
+ gspca_dev->usb_buf[0] = tag;
+ memcpy(&gspca_dev->usb_buf[1], data, count);
+ ret = usb_bulk_msg(dev,
+ usb_sndbulkpipe(dev, 3),
+ gspca_dev->usb_buf, count + 1,
+ &actual_count, 500);
+ if (ret < 0) {
+ pr_err("bulk write error %d tag=%02x\n",
+ ret, tag);
+ gspca_dev->usb_err = ret;
+ return;
+ }
+ length -= count;
+ if (length <= 0)
+ break;
+ data += count;
+ }
+}
+
+static int probe_6810(struct gspca_dev *gspca_dev)
+{
+ u8 gpio;
+ int ret;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ gpio = gspca_dev->usb_buf[0];
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return SENSOR_SOI763A;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */
+ if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
+ return -2;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -3;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return -4;
+
+ ret = i2c_r(gspca_dev, 0x01, 1);
+ if (ret > 0)
+ return -5;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x00, 2);
+ if (ret > 0)
+ return -6;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */
+ ret = i2c_r(gspca_dev, 0x36, 2);
+ if (ret > 0)
+ return -7;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */
+ reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
+ if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
+ return -8;
+
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
+ reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */
+ ret = i2c_r(gspca_dev, 0x00, 1);
+ if (ret > 0)
+ return SENSOR_CX0342;
+ return -9;
+}
+
+static void cx0342_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x48},
+ {TP6800_R11_SIF_CONTROL, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x28},
+ {CX0342_RBPXLTHD_L, 0x28},
+ {CX0342_PLANETHD_L, 0x50},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_TIMING_EN, 0x01},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+}
+
+static void soi763a_6810_init(struct gspca_dev *gspca_dev)
+{
+ static const struct cmd reg_init_1[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xe1},
+ {0x25, 0x02},
+ {TP6800_R21_ENDP_1_CTL, 0x00},
+ {TP6800_R3F_FRAME_RATE, 0x80},
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R18_GPIO_DATA, 0xc1},
+ };
+ static const struct cmd reg_init_2[] = {
+ {TP6800_R78_FORMAT, 0x54},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x00, 0x00},
+ {0x01, 0x80},
+ {0x02, 0x80},
+ {0x03, 0x90},
+ {0x04, 0x20},
+ {0x05, 0x20},
+ {0x06, 0x80},
+ {0x07, 0x00},
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x76}, /* 7630 = soi673a */
+ {0x0b, 0x30},
+ {0x0c, 0x20},
+ {0x0d, 0x20},
+ {0x0e, 0xff},
+ {0x0f, 0xff},
+ {0x10, 0x41},
+ {0x15, 0x14},
+ {0x11, 0x40},
+ {0x12, 0x48},
+ {0x13, 0x80},
+ {0x14, 0x80},
+ {0x16, 0x03},
+ {0x28, 0xb0},
+ {0x71, 0x20},
+ {0x75, 0x8e},
+ {0x17, 0x1b},
+ {0x18, 0xbd},
+ {0x19, 0x05},
+ {0x1a, 0xf6},
+ {0x1b, 0x04},
+ {0x1c, 0x7f}, /* omnivision */
+ {0x1d, 0xa2},
+ {0x1e, 0x00},
+ {0x1f, 0x00},
+ {0x20, 0x45},
+ {0x21, 0x80},
+ {0x22, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0xa0},
+ {0x27, 0x9a},
+ {0x29, 0x30},
+ {0x2a, 0x80},
+ {0x2b, 0x00},
+ {0x2c, 0xac},
+ {0x2d, 0x05},
+ {0x2e, 0x80},
+ {0x2f, 0x3c},
+ {0x30, 0x22},
+ {0x31, 0x00},
+ {0x32, 0x86},
+ {0x33, 0x08},
+ {0x34, 0xff},
+ {0x35, 0xff},
+ {0x36, 0xff},
+ {0x37, 0xff},
+ {0x38, 0xff},
+ {0x39, 0xff},
+ {0x3a, 0xfe},
+ {0x3b, 0xfe},
+ {0x3c, 0xfe},
+ {0x3d, 0xfe},
+ {0x3e, 0xfe},
+ {0x3f, 0x71},
+ {0x40, 0xff},
+ {0x41, 0xff},
+ {0x42, 0xff},
+ {0x43, 0xff},
+ {0x44, 0xff},
+ {0x45, 0xff},
+ {0x46, 0xff},
+ {0x47, 0xff},
+ {0x48, 0xff},
+ {0x49, 0xff},
+ {0x4a, 0xfe},
+ {0x4b, 0xff},
+ {0x4c, 0x00},
+ {0x4d, 0x00},
+ {0x4e, 0xff},
+ {0x4f, 0xff},
+ {0x50, 0xff},
+ {0x51, 0xff},
+ {0x52, 0xff},
+ {0x53, 0xff},
+ {0x54, 0xff},
+ {0x55, 0xff},
+ {0x56, 0xff},
+ {0x57, 0xff},
+ {0x58, 0xff},
+ {0x59, 0xff},
+ {0x5a, 0xff},
+ {0x5b, 0xfe},
+ {0x5c, 0xff},
+ {0x5d, 0x8f},
+ {0x5e, 0xff},
+ {0x5f, 0x8f},
+ {0x60, 0xa2},
+ {0x61, 0x4a},
+ {0x62, 0xf3},
+ {0x63, 0x75},
+ {0x64, 0xf0},
+ {0x65, 0x00},
+ {0x66, 0x55},
+ {0x67, 0x92},
+ {0x68, 0xa0},
+ {0x69, 0x4a},
+ {0x6a, 0x22},
+ {0x6b, 0x00},
+ {0x6c, 0x33},
+ {0x6d, 0x44},
+ {0x6e, 0x22},
+ {0x6f, 0x84},
+ {0x70, 0x0b},
+ {0x72, 0x10},
+ {0x73, 0x50},
+ {0x74, 0x21},
+ {0x76, 0x00},
+ {0x77, 0xa5},
+ {0x78, 0x80},
+ {0x79, 0x80},
+ {0x7a, 0x80},
+ {0x7b, 0xe2},
+ {0x7c, 0x00},
+ {0x7d, 0xf7},
+ {0x7e, 0x00},
+ {0x7f, 0x00},
+ };
+
+ reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+}
+
+/* set the gain and exposure */
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
+ s32 blue, s32 red)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ expo = (expo << 2) - 1;
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
+ gain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
+ gain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
+ if (sd->sensor == SENSOR_CX0342) {
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+ blue >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
+ if (sd->bridge == BRIDGE_TP6800)
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+ red >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
+ }
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
+ sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
+ return;
+ }
+
+ /* soi763a */
+ i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
+ expo);
+/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
+ i2c_w(gspca_dev, 0x00, /* gain */
+ gain);
+}
+
+/* set the JPEG quantization tables */
+static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* update the jpeg quantization tables */
+ PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
+ sd->quality = q;
+ if (q > 16)
+ q = 16;
+ if (sd->sensor == SENSOR_SOI763A)
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
+ else
+ memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
+ DQT[q], sizeof DQT[0]);
+}
+
+/* set the JPEG compression quality factor */
+static void setquality(struct gspca_dev *gspca_dev, s32 q)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (q != 16)
+ q = 15 - q;
+
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
+ reg_w(gspca_dev, TP6800_R79_QUALITY, q);
+
+ /* auto quality */
+ if (q == 15 && sd->bridge == BRIDGE_TP6810) {
+ msleep(4);
+ reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
+ }
+}
+
+static const u8 color_null[18] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+static const u8 color_gain[NSENSORS][18] = {
+[SENSOR_CX0342] =
+ {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */
+ 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+[SENSOR_SOI763A] =
+ {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */
+ 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */
+ 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
+};
+
+static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+#define NGAMMA 6
+ static const u8 gamma_tb[NGAMMA][3][1024] = {
+ { /* gamma 0 - from tp6800 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
+ 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
+ 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
+ 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
+ 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
+ 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
+ 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
+ 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
+ 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
+ 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
+ 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
+ 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
+ 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
+ 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
+ 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 1 - from tp6810 + soi763a */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
+ 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
+ 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
+ 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
+ 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
+ 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
+ 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
+ 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
+ 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
+ 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
+ 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
+ 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
+ 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
+ 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
+ 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
+ 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
+ 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+ 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
+ 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
+ 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
+ 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
+ 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
+ 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
+ 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
+ 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
+ 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
+ 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
+ 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
+ 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
+ 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
+ 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
+ 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
+ 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
+ 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
+ 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
+ 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
+ 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
+ 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
+ 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
+ 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
+ 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
+ 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
+ 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
+ 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
+ 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
+ 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
+ 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
+ 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 2 */
+ {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
+ 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
+ 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
+ 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
+ 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
+ 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
+ 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
+ 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
+ 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
+ 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
+ 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
+ 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
+ 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
+ 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
+ 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
+ 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
+ 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
+ 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
+ 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
+ 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
+ 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
+ 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
+ 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
+ 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
+ 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
+ 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
+ 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
+ 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
+ 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
+ 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
+ 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
+ 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
+ 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
+ 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
+ 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
+ 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
+ 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
+ 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
+ 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
+ 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
+ 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
+ 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
+ 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
+ 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
+ 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
+ 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 3 - from tp6810 + cx0342 */
+ {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
+ 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
+ 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
+ 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
+ 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
+ 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
+ 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
+ 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
+ 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
+ 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
+ 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
+ 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
+ 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
+ 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
+ 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
+ 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
+ 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
+ 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
+ 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
+ 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
+ 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
+ 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
+ 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
+ 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
+ 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
+ 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
+ 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
+ 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
+ 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
+ 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
+ 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
+ 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
+ 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
+ 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
+ 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
+ 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
+ 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
+ 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
+ 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
+ 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
+ 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
+ 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
+ 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
+ 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
+ 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
+ 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
+ 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
+ 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
+ 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
+ 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
+ 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+ },
+ { /* gamma 4 - from tp6800 + soi763a */
+ {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
+ 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
+ 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
+ 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
+ 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
+ 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
+ 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
+ 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
+ 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
+ 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
+ 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
+ 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
+ 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
+ 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
+ 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
+ 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
+ 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
+ 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
+ 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
+ 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
+ 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
+ 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
+ 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
+ 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
+ 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
+ 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
+ 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
+ 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
+ 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
+ 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
+ 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
+ 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
+ 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
+ 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
+ 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
+ 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
+ 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
+ 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
+ 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
+ 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
+ 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
+ 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
+ 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
+ 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
+ {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
+ 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
+ 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
+ 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
+ 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
+ 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
+ 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
+ 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
+ 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
+ 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
+ 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
+ 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
+ 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
+ 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
+ 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
+ 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
+ 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
+ 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
+ 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
+ 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
+ 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
+ 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
+ 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
+ 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
+ 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
+ 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
+ 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
+ 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
+ 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
+ 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
+ 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
+ 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
+ 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
+ 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
+ },
+ { /* gamma 5 */
+ {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
+ 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
+ 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
+ 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
+ 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
+ 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
+ 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
+ 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
+ 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
+ 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
+ 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
+ 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+ 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
+ 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
+ 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
+ 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
+ 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
+ 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
+ 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
+ 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
+ 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
+ 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
+ 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
+ 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
+ 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
+ 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
+ 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
+ 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
+ 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
+ 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
+ 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
+ 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
+ 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
+ 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
+ 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
+ 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
+ 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
+ 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
+ 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
+ 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
+ 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
+ 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
+ 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
+ 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
+ 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
+ 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
+ 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
+ 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
+ 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
+ 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
+ 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
+ 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
+ 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
+ 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
+ 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
+ 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
+ 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
+ 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
+ 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
+ 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
+ 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
+ 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
+ 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
+ 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
+ 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
+ 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
+ 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
+ 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
+ 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
+ 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
+ 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
+ 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
+ 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
+ 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
+ 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
+ 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
+ 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
+ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
+ 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
+ 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
+ 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
+ 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
+ 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
+ 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
+ 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
+ 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
+ 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
+ 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
+ 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
+ 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
+ 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
+ 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
+ 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
+ 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
+ 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
+ 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+ 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
+ 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
+ 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
+ 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
+ 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
+ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
+ 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
+ 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
+ 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
+ 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
+ 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
+ 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
+ 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
+ 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
+ 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
+ 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
+ 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
+ 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
+ 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
+ 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
+ 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
+ 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
+ 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
+ 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
+ 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
+ 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
+ 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
+ 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
+ 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
+ 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
+ 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
+ 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
+ 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
+ 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
+ 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
+ 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
+ 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
+ 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
+ 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
+ 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
+ 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
+ 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
+ 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
+ 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
+ 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
+ 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
+ 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
+ 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
+ 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
+ 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
+ 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
+ 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
+ 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
+ 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
+ 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
+ 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
+ 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
+ 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
+ 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
+ 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
+ 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
+ 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
+ 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
+ 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
+ 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
+ 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
+ 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
+ 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
+ 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
+ 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
+ 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
+ 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ },
+ };
+
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (sd->bridge == BRIDGE_TP6810)
+ reg_w(gspca_dev, 0x02, 0x28);
+/* msleep(50); */
+ bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
+ bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
+ bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
+ if (sd->bridge == BRIDGE_TP6810) {
+ int i;
+
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R55_GAMMA_R,
+ gamma_tb[gamma][0][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R56_GAMMA_G,
+ gamma_tb[gamma][1][i]);
+ reg_w(gspca_dev, 0x02, 0x2b);
+ reg_w(gspca_dev, 0x02, 0x28);
+ for (i = 0; i < 6; i++)
+ reg_w(gspca_dev, TP6800_R57_GAMMA_B,
+ gamma_tb[gamma][2][i]);
+ reg_w(gspca_dev, 0x02, 0x28);
+ }
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+/* msleep(50); */
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ val |= 0x08; /* grid compensation enable */
+ if (gspca_dev->width == 640)
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ else
+ val |= 0x04; /* scaling down enable */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
+ } else {
+ val = (val << 5) | 0x08;
+ reg_w(gspca_dev, 0x59, val);
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->ag_cnt = val ? AG_CNT_START : -1;
+}
+
+/* set the resolution for sensor cx0342 */
+static void set_resolution(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+ if (gspca_dev->width == 320) {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
+ } else {
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
+ msleep(100);
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+ msleep(100);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ }
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+}
+
+/* convert the frame rate to a tp68x0 value */
+static int get_fr_idx(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ if (sd->bridge == BRIDGE_TP6800) {
+ for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
+ if (sd->framerate >= rates[i])
+ break;
+ }
+ i = 6 - i; /* 1 = 5fps .. 6 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 6 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 0x05; /* 15 fps */
+ } else {
+ for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
+ if (sd->framerate >= rates_6810[i])
+ break;
+ }
+ i = 7 - i; /* 3 = 5fps .. 7 = 30fps */
+
+ /* 640x480 * 30 fps does not work */
+ if (i == 7 /* if 30 fps */
+ && gspca_dev->width == 640)
+ i = 6; /* 15 fps */
+ i |= 0x80; /* clock * 1 */
+ }
+ return i;
+}
+
+static void setframerate(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 fr_idx;
+
+ fr_idx = get_fr_idx(gspca_dev);
+
+ if (sd->bridge == BRIDGE_TP6810) {
+ reg_r(gspca_dev, 0x7b);
+ reg_w(gspca_dev, 0x7b,
+ sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
+ if (val >= 128)
+ fr_idx = 0xf0; /* lower frame rate */
+ }
+
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
+
+ if (sd->sensor == SENSOR_CX0342)
+ i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
+}
+
+static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
+{
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+static int sd_setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 val = gspca_dev->gain->val;
+
+ if (sd->sensor == SENSOR_CX0342) {
+ s32 old = gspca_dev->gain->cur.val ?
+ gspca_dev->gain->cur.val : 1;
+
+ sd->blue->val = sd->blue->val * val / old;
+ if (sd->blue->val > 4095)
+ sd->blue->val = 4095;
+ sd->red->val = sd->red->val * val / old;
+ if (sd->red->val > 4095)
+ sd->red->val = 4095;
+ }
+ if (gspca_dev->streaming) {
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, gspca_dev->exposure->val,
+ gspca_dev->gain->val,
+ sd->blue->val, sd->red->val);
+ else
+ setexposure(gspca_dev, gspca_dev->exposure->val,
+ gspca_dev->gain->val, 0, 0);
+ }
+ return gspca_dev->usb_err;
+}
+
+static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
+{
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
+ i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info;
+
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
+ framerates : framerates_6810;
+
+ sd->framerate = 30; /* default: 30 fps */
+ 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;
+ static const struct cmd tp6800_preinit[] = {
+ {TP6800_R10_SIF_TYPE, 0x01}, /* sif */
+ {TP6800_R11_SIF_CONTROL, 0x01},
+ {TP6800_R15_GPIO_PU, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x9f},
+ {TP6800_R17_GPIO_IO, 0x80},
+ {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */
+ };
+ static const struct cmd tp6810_preinit[] = {
+ {TP6800_R2F_TIMING_CFG, 0x2f},
+ {TP6800_R15_GPIO_PU, 0x6f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w_buf(gspca_dev, tp6800_preinit,
+ ARRAY_SIZE(tp6800_preinit));
+ else
+ reg_w_buf(gspca_dev, tp6810_preinit,
+ ARRAY_SIZE(tp6810_preinit));
+ msleep(15);
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
+/* values:
+ * 0x80: snapshot button
+ * 0x40: LED
+ * 0x20: (bridge / sensor) reset for tp6810 ?
+ * 0x07: sensor type ?
+ */
+
+ /* guess the sensor type */
+ if (force_sensor >= 0) {
+ sd->sensor = force_sensor;
+ } else {
+ if (sd->bridge == BRIDGE_TP6800) {
+/*fixme: not sure this is working*/
+ switch (gspca_dev->usb_buf[0] & 0x07) {
+ case 0:
+ sd->sensor = SENSOR_SOI763A;
+ break;
+ case 1:
+ sd->sensor = SENSOR_CX0342;
+ break;
+ }
+ } else {
+ int sensor;
+
+ sensor = probe_6810(gspca_dev);
+ if (sensor < 0) {
+ pr_warn("Unknown sensor %d - forced to soi763a\n",
+ -sensor);
+ sensor = SENSOR_SOI763A;
+ }
+ sd->sensor = sensor;
+ }
+ }
+ if (sd->sensor == SENSOR_SOI763A) {
+ pr_info("Sensor soi763a\n");
+ if (sd->bridge == BRIDGE_TP6810) {
+ soi763a_6810_init(gspca_dev);
+ }
+ } else {
+ pr_info("Sensor cx0342\n");
+ if (sd->bridge == BRIDGE_TP6810) {
+ cx0342_6810_init(gspca_dev);
+ }
+ }
+
+ set_dqt(gspca_dev, 0);
+ return 0;
+}
+
+/* This function is called before choosing the alt setting */
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd cx_sensor_init[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x37},
+ {CX0342_EXPO_LINE_H, 0x01},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd cx_bridge_init[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd ov_sensor_init[] = {
+ {0x10, 0x75}, /* exposure */
+ {0x76, 0x03},
+ {0x00, 0x00}, /* gain */
+ };
+ static const struct cmd ov_bridge_init[] = {
+ {0x7b, 0x90},
+ {TP6800_R3F_FRAME_RATE, 0x87},
+ };
+
+ if (sd->bridge == BRIDGE_TP6800)
+ return 0;
+ if (sd->sensor == SENSOR_CX0342) {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, cx_sensor_init,
+ ARRAY_SIZE(cx_sensor_init));
+ reg_w_buf(gspca_dev, cx_bridge_init,
+ ARRAY_SIZE(cx_bridge_init));
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ } else {
+ reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
+ i2c_w_buf(gspca_dev, ov_sensor_init,
+ ARRAY_SIZE(ov_sensor_init));
+ reg_r(gspca_dev, 0x7b);
+ reg_w_buf(gspca_dev, ov_bridge_init,
+ ARRAY_SIZE(ov_bridge_init));
+ }
+ reg_w(gspca_dev, TP6800_R78_FORMAT,
+ gspca_dev->curr_mode ? 0x00 : 0x01);
+ return gspca_dev->usb_err;
+}
+
+static void set_led(struct gspca_dev *gspca_dev, int on)
+{
+ u8 data;
+
+ reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
+ data = gspca_dev->usb_buf[0];
+ if (on)
+ data &= ~0x40;
+ else
+ data |= 0x40;
+ reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
+}
+
+static void cx0342_6800_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd reg_init[] = {
+ /* fixme: is this useful? */
+ {TP6800_R17_GPIO_IO, 0x9f},
+ {TP6800_R16_GPIO_PD, 0x40},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+ {TP6800_R2F_TIMING_CFG, 0x17},
+ {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R31_PIXEL_START, 0x0b},
+ {TP6800_R32_PIXEL_END_L, 0x8a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x00},
+ {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */
+ };
+ static const struct cmd sensor_init[] = {
+ {CX0342_OUTPUT_CTRL, 0x07},
+ {CX0342_BYPASS_MODE, 0x58},
+ {CX0342_GPXLTHD_L, 0x16},
+ {CX0342_RBPXLTHD_L, 0x16},
+ {CX0342_PLANETHD_L, 0xc0},
+ {CX0342_PLANETHD_H, 0x03},
+ {CX0342_RB_GAP_L, 0xff},
+ {CX0342_RB_GAP_H, 0x07},
+ {CX0342_G_GAP_L, 0xff},
+ {CX0342_G_GAP_H, 0x07},
+ {CX0342_RST_OVERFLOW_L, 0x5c},
+ {CX0342_RST_OVERFLOW_H, 0x01},
+ {CX0342_DATA_OVERFLOW_L, 0xfc},
+ {CX0342_DATA_OVERFLOW_H, 0x03},
+ {CX0342_DATA_UNDERFLOW_L, 0x00},
+ {CX0342_DATA_UNDERFLOW_H, 0x00},
+ {CX0342_SYS_CTRL_0, 0x40},
+ {CX0342_GLOBAL_GAIN, 0x01},
+ {CX0342_CLOCK_GEN, 0x00},
+ {CX0342_SYS_CTRL_0, 0x02},
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+ i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+ i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
+ i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ set_led(gspca_dev, 1);
+ set_resolution(gspca_dev);
+}
+
+static void cx0342_6810_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd sensor_init_2[] = {
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_2[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x08},
+ {TP6800_R32_PIXEL_END_L, 0x87},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0e},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd sensor_init_3[] = {
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ {CX0342_EXPO_LINE_L, 0x6f},
+ {CX0342_EXPO_LINE_H, 0x02},
+ {CX0342_RAW_GRGAIN_L, 0x00},
+ {CX0342_RAW_GBGAIN_L, 0x00},
+ {CX0342_RAW_RGAIN_L, 0x00},
+ {CX0342_RAW_BGAIN_L, 0x00},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd bridge_init_5[] = {
+ {0x4d, 0x00},
+ {0x4c, 0xff},
+ {0x4e, 0xff},
+ {0x4f, 0x00},
+ };
+ static const struct cmd sensor_init_4[] = {
+ {CX0342_EXPO_LINE_L, 0xd3},
+ {CX0342_EXPO_LINE_H, 0x01},
+/*fixme: gains, but 00..80 only*/
+ {CX0342_RAW_GRGAIN_L, 0x40},
+ {CX0342_RAW_GBGAIN_L, 0x40},
+ {CX0342_RAW_RGAIN_L, 0x40},
+ {CX0342_RAW_BGAIN_L, 0x40},
+ {CX0342_SYS_CTRL_0, 0x81},
+ };
+ static const struct cmd sensor_init_5[] = {
+ {CX0342_IDLE_CTRL, 0x05},
+ {CX0342_ADCGN, 0x00},
+ {CX0342_ADC_CTL, 0x00},
+ {CX0342_LVRST_BLBIAS, 0x01},
+ {CX0342_VTHSEL, 0x0b},
+ {CX0342_RAMP_RIV, 0x0b},
+ {CX0342_LDOSEL, 0x07},
+ {CX0342_SPV_VALUE_L, 0x40},
+ {CX0342_SPV_VALUE_H, 0x02},
+ {CX0342_AUTO_ADC_CALIB, 0x81},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_cx_init_common,
+ ARRAY_SIZE(tp6810_cx_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
+ ARRAY_SIZE(color_gain[0]));
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
+ i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
+ reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
+ i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
+
+ set_led(gspca_dev, 1);
+/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
+}
+
+static void soi763a_6800_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd reg_init[] = {
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
+
+ {TP6800_R50, 0x00},
+ {TP6800_R51, 0x00},
+ {TP6800_R52, 0xff},
+ {TP6800_R53, 0x03},
+ {TP6800_R54_DARK_CFG, 0x07},
+ {TP6800_R5C_EDGE_THRLD, 0x40},
+
+ {TP6800_R79_QUALITY, 0x03},
+ {TP6800_R7A_BLK_THRLD, 0x40},
+
+ {TP6800_R2F_TIMING_CFG, 0x46},
+ {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */
+ {TP6800_R37_FRONT_DARK_ST, 0x00},
+ {TP6800_R38_FRONT_DARK_END, 0x00},
+ {TP6800_R39_REAR_DARK_ST_L, 0x00},
+ {TP6800_R3A_REAR_DARK_ST_H, 0x00},
+ {TP6800_R3B_REAR_DARK_END_L, 0x00},
+ {TP6800_R3C_REAR_DARK_END_H, 0x00},
+ {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
+ {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
+ {TP6800_R21_ENDP_1_CTL, 0x03},
+
+ {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */
+ {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
+
+ {TP6800_R31_PIXEL_START, 0x1b},
+ {TP6800_R32_PIXEL_END_L, 0x9a},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x0f},
+ {TP6800_R35_LINE_END_L, 0xf4},
+ {TP6800_R36_LINE_END_H, 0x01},
+ {TP6800_R78_FORMAT, 0x01}, /* qvga */
+ {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */
+ {TP6800_R1A_SIF_TX_DATA2, 0x00},
+ };
+ static const struct cmd sensor_init[] = {
+ {0x12, 0x48}, /* mirror - RGB */
+ {0x13, 0xa0}, /* clock - no AGC nor AEC */
+ {0x03, 0xa4}, /* saturation */
+ {0x04, 0x30}, /* hue */
+ {0x05, 0x88}, /* contrast */
+ {0x06, 0x60}, /* brightness */
+ {0x10, 0x41}, /* AEC */
+ {0x11, 0x40}, /* clock rate */
+ {0x13, 0xa0},
+ {0x14, 0x00}, /* 640x480 */
+ {0x15, 0x14},
+ {0x1f, 0x41},
+ {0x20, 0x80},
+ {0x23, 0xee},
+ {0x24, 0x50},
+ {0x25, 0x7a},
+ {0x26, 0x00},
+ {0x27, 0xe2},
+ {0x28, 0xb0},
+ {0x2a, 0x00},
+ {0x2b, 0x00},
+ {0x2d, 0x81},
+ {0x2f, 0x9d},
+ {0x60, 0x80},
+ {0x61, 0x00},
+ {0x62, 0x88},
+ {0x63, 0x11},
+ {0x64, 0x89},
+ {0x65, 0x00},
+ {0x67, 0x94},
+ {0x68, 0x7a},
+ {0x69, 0x0f},
+ {0x6c, 0x80},
+ {0x6d, 0x80},
+ {0x6e, 0x80},
+ {0x6f, 0xff},
+ {0x71, 0x20},
+ {0x74, 0x20},
+ {0x75, 0x86},
+ {0x77, 0xb5},
+ {0x17, 0x18}, /* H href start */
+ {0x18, 0xbf}, /* H href end */
+ {0x19, 0x03}, /* V start */
+ {0x1a, 0xf8}, /* V end */
+ {0x01, 0x80}, /* blue gain */
+ {0x02, 0x80}, /* red gain */
+ };
+
+ reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
+
+ i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(10);
+
+ i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
+
+ reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
+ reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
+
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+
+ set_led(gspca_dev, 1);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+}
+
+static void soi763a_6810_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const struct cmd bridge_init_2[] = {
+ {TP6800_R7A_BLK_THRLD, 0x00},
+ {TP6800_R79_QUALITY, 0x04},
+ {TP6800_R79_QUALITY, 0x01},
+ };
+ static const struct cmd bridge_init_3[] = {
+ {TP6800_R31_PIXEL_START, 0x20},
+ {TP6800_R32_PIXEL_END_L, 0x9f},
+ {TP6800_R33_PIXEL_END_H, 0x02},
+ {TP6800_R34_LINE_START, 0x13},
+ {TP6800_R35_LINE_END_L, 0xf8},
+ {TP6800_R36_LINE_END_H, 0x01},
+ };
+ static const struct cmd bridge_init_6[] = {
+ {0x08, 0xff},
+ {0x09, 0xff},
+ {0x0a, 0x5f},
+ {0x0b, 0x80},
+ };
+
+ reg_w(gspca_dev, 0x22, gspca_dev->alt);
+ bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
+ reg_w(gspca_dev, 0x59, 0x40);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
+ reg_w_buf(gspca_dev, tp6810_ov_init_common,
+ ARRAY_SIZE(tp6810_ov_init_common));
+ reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4a, 0x7f);
+ reg_w(gspca_dev, 0x07, 0x05);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
+ } else {
+ reg_w(gspca_dev, 0x4a, 0xff);
+ reg_w(gspca_dev, 0x07, 0x85);
+ reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
+ }
+ setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ reg_w_buf(gspca_dev, tp6810_bridge_start,
+ ARRAY_SIZE(tp6810_bridge_start));
+
+ if (gspca_dev->curr_mode) {
+ reg_w(gspca_dev, 0x4f, 0x00);
+ reg_w(gspca_dev, 0x4e, 0x7c);
+ }
+
+ reg_w(gspca_dev, 0x00, 0x00);
+
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+ bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
+ ARRAY_SIZE(color_gain[0]));
+ set_led(gspca_dev, 1);
+ reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
+ set_dqt(gspca_dev, sd->quality);
+ if (sd->bridge == BRIDGE_TP6800) {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6800_start(gspca_dev);
+ else
+ soi763a_6800_start(gspca_dev);
+ } else {
+ if (sd->sensor == SENSOR_CX0342)
+ cx0342_6810_start(gspca_dev);
+ else
+ soi763a_6810_start(gspca_dev);
+ reg_w_buf(gspca_dev, tp6810_late_start,
+ ARRAY_SIZE(tp6810_late_start));
+ reg_w(gspca_dev, 0x80, 0x03);
+ reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
+
+ if (sd->sensor == SENSOR_CX0342)
+ setexposure(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain),
+ v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ else
+ setexposure(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+ v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+ if (sd->sensor == SENSOR_SOI763A)
+ setquality(gspca_dev,
+ v4l2_ctrl_g_ctrl(sd->jpegqual));
+ if (sd->bridge == BRIDGE_TP6810)
+ setautogain(gspca_dev,
+ v4l2_ctrl_g_ctrl(gspca_dev->autogain));
+ }
+
+ setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->bridge == BRIDGE_TP6800)
+ reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
+ set_led(gspca_dev, 0);
+ reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* the start of frame contains:
+ * ff d8
+ * ff fe
+ * width / 16
+ * height / 8
+ * quality
+ */
+ if (sd->bridge == BRIDGE_TP6810) {
+ if (*data != 0x5a) {
+/*fixme: don't discard the whole frame..*/
+ if (*data == 0xaa || *data == 0x00)
+ return;
+ if (*data > 0xc0) {
+ PDEBUG(D_FRAM, "bad frame");
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+ data++;
+ len--;
+ if (*data == 0xff && data[1] == 0xd8) {
+/*fixme: there may be information in the 4 high bits*/
+ if ((data[6] & 0x0f) != sd->quality)
+ set_dqt(gspca_dev, data[6] & 0x0f);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 7, len - 7);
+ } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len);
+ } else {
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ }
+ return;
+ }
+
+ switch (*data) {
+ case 0x55:
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+
+ if (len < 8
+ || data[1] != 0xff || data[2] != 0xd8
+ || data[3] != 0xff || data[4] != 0xfe) {
+
+ /* Have only seen this with corrupt frames */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ if (data[7] != sd->quality)
+ set_dqt(gspca_dev, data[7]);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 8, len - 8);
+ break;
+ case 0xaa:
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ case 0xcc:
+ if (data[1] != 0xff || data[2] != 0xd8)
+ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data + 1, len - 1);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ break;
+ }
+}
+
+static void sd_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, alen;
+ int luma, expo;
+
+ if (sd->ag_cnt < 0)
+ return;
+ if (--sd->ag_cnt > 5)
+ return;
+ switch (sd->ag_cnt) {
+/* case 5: */
+ default:
+ reg_w(gspca_dev, 0x7d, 0x00);
+ break;
+ case 4:
+ reg_w(gspca_dev, 0x27, 0xb0);
+ break;
+ case 3:
+ reg_w(gspca_dev, 0x0c, 0x01);
+ break;
+ case 2:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ /* values not used (unknown) */
+ break;
+ case 1:
+ reg_w(gspca_dev, 0x27, 0xd0);
+ break;
+ case 0:
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x02),
+ gspca_dev->usb_buf,
+ 32,
+ &alen,
+ 500);
+ if (ret < 0) {
+ pr_err("bulk err %d\n", ret);
+ break;
+ }
+ luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
+ (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
+ (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
+ (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
+ (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
+ (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
+ (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
+ (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
+ / 8;
+ if (gspca_dev->width == 640)
+ luma /= 4;
+ reg_w(gspca_dev, 0x7d, 0x00);
+
+ expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+ ret = gspca_expo_autogain(gspca_dev, luma,
+ 60, /* desired luma */
+ 6, /* dead zone */
+ 2, /* gain knee */
+ 70); /* expo knee */
+ sd->ag_cnt = AG_CNT_START;
+ if (sd->bridge == BRIDGE_TP6810) {
+ int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+ if ((expo >= 128 && new_expo < 128)
+ || (expo < 128 && new_expo >= 128))
+ setframerate(gspca_dev, new_expo);
+ }
+ break;
+ }
+}
+
+/* get stream parameters (framerate) */
+static void sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80) {
+ if (sd->bridge == BRIDGE_TP6800)
+ fr = rates[6 - (i & 0x07)];
+ else
+ fr = rates_6810[7 - (i & 0x07)];
+ } else {
+ fr = rates[6 - i];
+ }
+ tpf->denominator = fr;
+}
+
+/* set stream parameters (framerate) */
+static void sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ int fr, i;
+
+ sd->framerate = tpf->denominator / tpf->numerator;
+ if (gspca_dev->streaming)
+ setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+
+ /* Return the actual framerate */
+ i = get_fr_idx(gspca_dev);
+ if (i & 0x80)
+ fr = rates_6810[7 - (i & 0x07)];
+ else
+ fr = rates[6 - i];
+ tpf->numerator = 1;
+ tpf->denominator = fr;
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ const struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor != SENSOR_SOI763A)
+ return -ENOTTY;
+ v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ return 0;
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor != SENSOR_SOI763A)
+ return -ENOTTY;
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAMMA:
+ setgamma(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ setbgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_RED_BALANCE:
+ setrgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ sd_setgain(gspca_dev);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (ctrl->val)
+ break;
+ sd_setgain(gspca_dev);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
+ if (sd->sensor == SENSOR_CX0342) {
+ sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
+ sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
+ }
+ if (sd->sensor == SENSOR_SOI763A)
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 15, 1, 3);
+ else
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 4095, 1, 256);
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 3, 1, 2);
+ sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
+ (sd->sensor == SENSOR_SOI763A &&
+ sd->bridge == BRIDGE_TP6800) ? 0 : 1);
+ if (sd->bridge == BRIDGE_TP6810)
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (sd->sensor == SENSOR_SOI763A)
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (gspca_dev->autogain)
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ else
+ v4l2_ctrl_cluster(2, &gspca_dev->exposure);
+ return 0;
+}
+
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_isoc_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = sd_dq_callback,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
+ {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+static int sd_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/usb/gspca/tv8532.c
index 8591324a53e1..8591324a53e1 100644
--- a/drivers/media/video/gspca/tv8532.c
+++ b/drivers/media/usb/gspca/tv8532.c
diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c
new file mode 100644
index 000000000000..e50079503d96
--- /dev/null
+++ b/drivers/media/usb/gspca/vc032x.c
@@ -0,0 +1,3850 @@
+/*
+ * Z-star vc0321 library
+ *
+ * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
+ * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl
+ * Copyright (C) 2006 Michel Xhaard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "vc032x"
+
+#include "gspca.h"
+
+MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
+MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct { /* hvflip cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+
+ u8 image_offset;
+
+ u8 bridge;
+ u8 sensor;
+ u8 flags;
+#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */
+#define FL_HFLIP 0x02 /* mirrored by default */
+#define FL_VFLIP 0x04 /* vertical flipped by default */
+};
+enum bridges {
+ BRIDGE_VC0321,
+ BRIDGE_VC0323,
+};
+enum sensors {
+ SENSOR_HV7131R,
+ SENSOR_MI0360,
+ SENSOR_MI1310_SOC,
+ SENSOR_MI1320,
+ SENSOR_MI1320_SOC,
+ SENSOR_OV7660,
+ SENSOR_OV7670,
+ SENSOR_PO1200,
+ SENSOR_PO3130NC,
+ SENSOR_POxxxx,
+ NSENSORS
+};
+
+
+static const struct v4l2_pix_format vc0321_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format vc0323_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+ {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 960 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+};
+static const struct v4l2_pix_format bi_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 1},
+ {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024 * 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .priv = 0},
+};
+static const struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 1 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR 0x20
+#define OV7660_MVFP_VFLIP 0x10
+
+static const u8 mi0360_matrix[9] = {
+ 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
+};
+
+static const u8 mi0360_initVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0x35, 0x00, 0x60, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi0360_initQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0x35, 0x00, 0xef, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 mi1310_socinitVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb},
+ {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb},
+ {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb},
+ {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb},
+ {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb},
+ {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb},
+ {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2e, 0x0c, 0x55, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb},
+ {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc1, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb},
+ {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb},
+ {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb},
+ {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb},
+ {0x64, 0x7b, 0x5b, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc0, 0xbb},
+ {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc},
+ {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x25, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb},
+ {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb},
+ {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb},
+ {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb},
+ {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb},
+ {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb},
+ {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb},
+ {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb},
+ {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb},
+ {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb},
+ {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb},
+ {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb},
+ {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb},
+ {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb},
+ {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb},
+ {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb},
+ {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb},
+ {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xf4, 0x8e, 0xbb},
+ {0x00, 0x00, 0x50, 0xdd},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x24, 0x50, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x34, 0x0c, 0x50, 0xbb},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {},
+};
+static const u8 mi1310_socinitQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+ {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb},
+ {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb},
+ {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {},
+};
+static const u8 mi1310_soc_InitSXGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x0d, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc},
+ {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0x70, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x05, 0x00, 0x07, 0xbb},
+ {0x34, 0x00, 0x00, 0xbb},
+ {0x35, 0xff, 0x00, 0xbb},
+ {0xdc, 0x07, 0x02, 0xbb},
+ {0xdd, 0x3c, 0x18, 0xbb},
+ {0xde, 0x92, 0x6d, 0xbb},
+ {0xdf, 0xcd, 0xb1, 0xbb},
+ {0xe0, 0xff, 0xe7, 0xbb},
+ {0x06, 0xf0, 0x0d, 0xbb},
+ {0x06, 0x70, 0x0e, 0xbb},
+ {0x4c, 0x00, 0x01, 0xbb},
+ {0x4d, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2e, 0x0c, 0x60, 0xbb},
+ {0x21, 0xb6, 0x6e, 0xbb},
+ {0x37, 0x01, 0x40, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x07, 0x00, 0x84, 0xbb},
+ {0x08, 0x02, 0x4a, 0xbb},
+ {0x05, 0x01, 0x10, 0xbb},
+ {0x06, 0x00, 0x39, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x58, 0x02, 0x67, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x5a, 0x02, 0x67, 0xbb},
+ {0x59, 0x02, 0x00, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x39, 0x06, 0x18, 0xbb},
+ {0x3a, 0x06, 0x18, 0xbb},
+ {0x3b, 0x06, 0x18, 0xbb},
+ {0x3c, 0x06, 0x18, 0xbb},
+ {0x64, 0x7b, 0x5b, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x05, 0xcc},
+ {0xb6, 0x02, 0x00, 0xcc},
+ {0xb6, 0x05, 0x03, 0xcc},
+ {0xb6, 0x04, 0xc0, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x29, 0xcc},
+ {0xb6, 0x18, 0x09, 0xcc},
+ {0xb6, 0x17, 0x60, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x00, 0x80, 0xdd},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x06, 0xf0, 0x8e, 0xbb},
+ {0x00, 0x00, 0x80, 0xdd},
+ {0x06, 0x70, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x5e, 0x6a, 0x53, 0xbb},
+ {0x5f, 0x40, 0x2c, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x58, 0x00, 0x00, 0xbb},
+ {0x53, 0x09, 0x03, 0xbb},
+ {0x54, 0x31, 0x18, 0xbb},
+ {0x55, 0x8b, 0x5f, 0xbb},
+ {0x56, 0xc0, 0xa9, 0xbb},
+ {0x57, 0xe0, 0xd2, 0xbb},
+ {0xe1, 0x00, 0x00, 0xbb},
+ {0xdc, 0x09, 0x03, 0xbb},
+ {0xdd, 0x31, 0x18, 0xbb},
+ {0xde, 0x8b, 0x5f, 0xbb},
+ {0xdf, 0xc0, 0xa9, 0xbb},
+ {0xe0, 0xe0, 0xd2, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xf0, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x2f, 0xde, 0x20, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x24, 0x50, 0x20, 0xbb},
+ {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc},
+ {0xbc, 0x10, 0xc0, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x34, 0x0c, 0x50, 0xbb},
+ {0xbc, 0x11, 0x03, 0xcc},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x80, 0x00, 0x03, 0xbb},
+ {0x81, 0xc7, 0x14, 0xbb},
+ {0x82, 0xeb, 0xe8, 0xbb},
+ {0x83, 0xfe, 0xf4, 0xbb},
+ {0x84, 0xcd, 0x10, 0xbb},
+ {0x85, 0xf3, 0xee, 0xbb},
+ {0x86, 0xff, 0xf1, 0xbb},
+ {0x87, 0xcd, 0x10, 0xbb},
+ {0x88, 0xf3, 0xee, 0xbb},
+ {0x89, 0x01, 0xf1, 0xbb},
+ {0x8a, 0xe5, 0x17, 0xbb},
+ {0x8b, 0xe8, 0xe2, 0xbb},
+ {0x8c, 0xf7, 0xed, 0xbb},
+ {0x8d, 0x00, 0xff, 0xbb},
+ {0x8e, 0xec, 0x10, 0xbb},
+ {0x8f, 0xf0, 0xed, 0xbb},
+ {0x90, 0xf9, 0xf2, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xe9, 0x0d, 0xbb},
+ {0x93, 0xf4, 0xf2, 0xbb},
+ {0x94, 0xfb, 0xf5, 0xbb},
+ {0x95, 0x00, 0xff, 0xbb},
+ {0xb6, 0x0f, 0x08, 0xbb},
+ {0xb7, 0x3d, 0x16, 0xbb},
+ {0xb8, 0x0c, 0x04, 0xbb},
+ {0xb9, 0x1c, 0x07, 0xbb},
+ {0xba, 0x0a, 0x03, 0xbb},
+ {0xbb, 0x1b, 0x09, 0xbb},
+ {0xbc, 0x17, 0x0d, 0xbb},
+ {0xbd, 0x23, 0x1d, 0xbb},
+ {0xbe, 0x00, 0x28, 0xbb},
+ {0xbf, 0x11, 0x09, 0xbb},
+ {0xc0, 0x16, 0x15, 0xbb},
+ {0xc1, 0x00, 0x1b, 0xbb},
+ {0xc2, 0x0e, 0x07, 0xbb},
+ {0xc3, 0x14, 0x10, 0xbb},
+ {0xc4, 0x00, 0x17, 0xbb},
+ {0x06, 0x74, 0x8e, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
+ {}
+};
+
+static const u8 mi1320_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 mi1320_matrix[9] = {
+ 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52
+};
+static const u8 mi1320_initVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd},
+ {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb},
+ {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+ {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb},
+ {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb},
+ {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb},
+ {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb},
+ {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb},
+ {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb},
+ {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb},
+ {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb},
+ {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb},
+ {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb},
+ {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb},
+ {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb},
+ {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb},
+ {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb},
+ {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb},
+ {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb},
+ {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb},
+ {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb},
+ {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb},
+ {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb},
+ {0x08, 0x00, 0x27, 0xbb},
+ {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */
+ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb},
+ {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb},
+ {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb},
+ {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
+ {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
+ {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd},
+ {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
+ {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
+ {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc},
+ {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc},
+ {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+static const u8 mi1320_initQVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+ {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb},
+ {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+ {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb},
+ {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb},
+ {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb},
+ {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb},
+ {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb},
+ {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb},
+ {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb},
+ {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb},
+ {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb},
+ {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb},
+ {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
+ {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb},
+ {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb},
+ {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb},
+ {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb},
+ {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb},
+ {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb},
+ {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb},
+ {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb},
+ {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb},
+ {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
+ {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb},
+ {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb},
+ {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
+ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+
+static const u8 mi1320_soc_InitVGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x07, 0x00, 0xe0, 0xbb},
+ {0x08, 0x00, 0x0b, 0xbb},
+ {0x21, 0x00, 0x0c, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi1320_soc_InitQVGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb8, 0x00, 0x00, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xc8, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0x07, 0x00, 0xe0, 0xbb},
+ {0x08, 0x00, 0x0b, 0xbb},
+ {0x21, 0x00, 0x0c, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const u8 mi1320_soc_InitSXGA[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xc8, 0xcc},
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x04, 0xcc},
+ {0xb3, 0x23, 0x00, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc},
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xc8, 0x9f, 0x0b, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x5b, 0x00, 0x01, 0xbb},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x78, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x01, 0x42, 0xbb},
+ {0x08, 0x00, 0x11, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0xca, 0xbb},
+ {0x3a, 0x06, 0x80, 0xbb},
+ {0x3b, 0x01, 0x52, 0xbb},
+ {0x3c, 0x05, 0x40, 0xbb},
+ {0x57, 0x01, 0x9c, 0xbb},
+ {0x58, 0x01, 0xee, 0xbb},
+ {0x59, 0x00, 0xf0, 0xbb},
+ {0x5a, 0x01, 0x20, 0xbb},
+ {0x5c, 0x1d, 0x17, 0xbb},
+ {0x5d, 0x22, 0x1c, 0xbb},
+ {0x64, 0x1e, 0x1c, 0xbb},
+ {0x5b, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x22, 0xa0, 0x78, 0xbb},
+ {0x23, 0xa0, 0x78, 0xbb},
+ {0x24, 0x7f, 0x00, 0xbb},
+ {0x28, 0xea, 0x02, 0xbb},
+ {0x29, 0x86, 0x7a, 0xbb},
+ {0x5e, 0x52, 0x4c, 0xbb},
+ {0x5f, 0x20, 0x24, 0xbb},
+ {0x60, 0x00, 0x02, 0xbb},
+ {0x02, 0x00, 0xee, 0xbb},
+ {0x03, 0x39, 0x23, 0xbb},
+ {0x04, 0x07, 0x24, 0xbb},
+ {0x09, 0x00, 0xc0, 0xbb},
+ {0x0a, 0x00, 0x79, 0xbb},
+ {0x0b, 0x00, 0x04, 0xbb},
+ {0x0c, 0x00, 0x5c, 0xbb},
+ {0x0d, 0x00, 0xd9, 0xbb},
+ {0x0e, 0x00, 0x53, 0xbb},
+ {0x0f, 0x00, 0x21, 0xbb},
+ {0x10, 0x00, 0xa4, 0xbb},
+ {0x11, 0x00, 0xe5, 0xbb},
+ {0x15, 0x00, 0x00, 0xbb},
+ {0x16, 0x00, 0x00, 0xbb},
+ {0x17, 0x00, 0x00, 0xbb},
+ {0x18, 0x00, 0x00, 0xbb},
+ {0x19, 0x00, 0x00, 0xbb},
+ {0x1a, 0x00, 0x00, 0xbb},
+ {0x1b, 0x00, 0x00, 0xbb},
+ {0x1c, 0x00, 0x00, 0xbb},
+ {0x1d, 0x00, 0x00, 0xbb},
+ {0x1e, 0x00, 0x00, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+ {0x06, 0xe0, 0x0e, 0xbb},
+ {0x06, 0x60, 0x0e, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+ {0x05, 0x01, 0x13, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb},
+ {0x07, 0x00, 0x85, 0xbb},
+ {0x08, 0x00, 0x27, 0xbb},
+ {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
+ {0x21, 0x80, 0x00, 0xbb},
+ {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb},
+ {0x39, 0x03, 0x0d, 0xbb},
+ {0x3a, 0x06, 0x1b, 0xbb},
+ {0x3b, 0x00, 0x95, 0xbb},
+ {0x3c, 0x04, 0xdb, 0xbb},
+ {0x57, 0x02, 0x00, 0xbb},
+ {0x58, 0x02, 0x66, 0xbb},
+ {0x59, 0x00, 0xff, 0xbb},
+ {0x5a, 0x01, 0x33, 0xbb},
+ {0x5c, 0x12, 0x0d, 0xbb},
+ {0x5d, 0x16, 0x11, 0xbb},
+ {0x64, 0x5e, 0x1c, 0xbb},
+ {}
+};
+static const u8 po3130_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 po3130_matrix[9] = {
+ 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+
+static const u8 po3130_initVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
+ {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
+ {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
+ {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
+ {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
+ {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
+ {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
+ {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
+ {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
+ {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
+ {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
+ {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
+ {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
+ {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
+ {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0x4c, 0x07, 0xaa},
+ {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
+ {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
+/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */
+ {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
+ {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+static const u8 po3130_rundata[][4] = {
+ {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa},
+ {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa},
+ {0x00, 0x44, 0x40, 0xaa},
+/* {0x00, 0xd5, 0x7c, 0xaa}, */
+ {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa},
+ {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa},
+ {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa},
+ {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa},
+ {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa},
+ {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {}
+};
+
+static const u8 po3130_initQVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc},
+ {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc},
+ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
+ {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
+ {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
+ {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
+ {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
+ {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
+ {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
+ {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
+ {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
+ {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
+ {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
+ {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
+ {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
+ {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
+ {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
+ {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
+ {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa},
+ {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
+ {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
+ {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
+ {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
+ {}
+};
+
+static const u8 hv7131r_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 hv7131r_matrix[9] = {
+ 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
+};
+static const u8 hv7131r_initVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0xb3, 0x03, 0x0b, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x02, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */
+ {0xb3, 0x00, 0x27, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc},
+ {0xb8, 0x00, 0x23, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x58, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0x00, 0x01, 0x0c, 0xaa},
+ {0x00, 0x14, 0x01, 0xaa},
+ {0x00, 0x15, 0xe6, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x17, 0x86, 0xaa},
+ {0x00, 0x23, 0x00, 0xaa},
+ {0x00, 0x25, 0x03, 0xaa},
+ {0x00, 0x26, 0xa9, 0xaa},
+ {0x00, 0x27, 0x80, 0xaa},
+ {0x00, 0x30, 0x18, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 hv7131r_initQVGA_data[][4] = {
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0xb3, 0x03, 0x0b, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x02, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0x91, 0xcc},
+ {0xb3, 0x00, 0x27, 0xcc},
+ {0xbc, 0x00, 0xd3, 0xcc},
+ {0xb8, 0x00, 0x23, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x58, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0x00, 0x01, 0x0c, 0xaa},
+ {0x00, 0x14, 0x01, 0xaa},
+ {0x00, 0x15, 0xe6, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x17, 0x86, 0xaa},
+ {0x00, 0x23, 0x00, 0xaa},
+ {0x00, 0x25, 0x03, 0xaa},
+ {0x00, 0x26, 0xa9, 0xaa},
+ {0x00, 0x27, 0x80, 0xaa},
+ {0x00, 0x30, 0x18, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x01, 0xcc},
+ {0xb6, 0x02, 0x40, 0xcc},
+ {0xb6, 0x05, 0x00, 0xcc},
+ {0xb6, 0x04, 0xf0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x00, 0xcc},
+ {0xb6, 0x17, 0x96, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
+static const u8 ov7660_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 ov7660_matrix[9] = {
+ 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62
+};
+static const u8 ov7660_initVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd},
+ {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
+ {0xb3, 0x1f, 0x02, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x00, 0x26, 0xcc},
+ {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
+ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
+ {0x00, 0x36, 0x00, 0xaa},
+ {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
+ {0x00, 0x39, 0x43, 0xaa},
+ {0x00, 0x8d, 0xcf, 0xaa},
+ {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
+ {0x00, 0x0f, 0x62, 0xaa},
+ {0x00, 0x35, 0x84, 0xaa},
+ {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
+ {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+ {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+ {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa},
+ {0x00, 0x02, 0x80, 0xaa},
+ {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+
+ {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
+
+ {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc},
+ {}
+};
+static const u8 ov7660_initQVGA_data[][4] = {
+ {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
+ {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
+ {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
+ {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
+ {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+ {0xb8, 0x01, 0x7d, 0xcc},
+/* sizer */
+ {0xbc, 0x00, 0xd3, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
+ {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
+ {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
+ {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
+ {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
+ {0x00, 0x36, 0x00, 0xaa},
+ {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
+ {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa},
+ {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
+ {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa},
+ {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
+ {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
+ {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
+ {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
+ {0x00, 0x01, 0x80, 0xaa},
+ {0x00, 0x02, 0x80, 0xaa},
+/* sizer filters */
+ {0xbc, 0x02, 0x08, 0xcc},
+ {0xbc, 0x03, 0x70, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xbc, 0x04, 0x08, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x3c, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x04, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+/* */
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+/* */
+ {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+/* */
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc}, /* ff */
+ {0x00, 0x29, 0x3c, 0xaa},
+ {0xb3, 0x01, 0x45, 0xcc}, /* 45 */
+ {}
+};
+
+static const u8 ov7660_50HZ[][4] = {
+ {0x00, 0x3b, 0x08, 0xaa},
+ {0x00, 0x9d, 0x40, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {}
+};
+
+static const u8 ov7660_60HZ[][4] = {
+ {0x00, 0x3b, 0x00, 0xaa},
+ {0x00, 0x9e, 0x40, 0xaa},
+ {0x00, 0x13, 0xa7, 0xaa},
+ {}
+};
+
+static const u8 ov7660_NoFliker[][4] = {
+ {0x00, 0x13, 0x87, 0xaa},
+ {}
+};
+
+static const u8 ov7670_InitVGA[][4] = {
+ {0xb3, 0x01, 0x05, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x66, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x16, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xbc, 0x00, 0x41, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x6b, 0x0a, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x7a, 0x29, 0xaa},
+ {0x00, 0x7b, 0x0e, 0xaa},
+ {0x00, 0x7c, 0x1a, 0xaa},
+ {0x00, 0x7d, 0x31, 0xaa},
+ {0x00, 0x7e, 0x53, 0xaa},
+ {0x00, 0x7f, 0x60, 0xaa},
+ {0x00, 0x80, 0x6b, 0xaa},
+ {0x00, 0x81, 0x73, 0xaa},
+ {0x00, 0x82, 0x7b, 0xaa},
+ {0x00, 0x83, 0x82, 0xaa},
+ {0x00, 0x84, 0x89, 0xaa},
+ {0x00, 0x85, 0x96, 0xaa},
+ {0x00, 0x86, 0xa1, 0xaa},
+ {0x00, 0x87, 0xb7, 0xaa},
+ {0x00, 0x88, 0xcc, 0xaa},
+ {0x00, 0x89, 0xe1, 0xaa},
+ {0x00, 0x13, 0xe0, 0xaa},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x10, 0x00, 0xaa},
+ {0x00, 0x0d, 0x40, 0xaa},
+ {0x00, 0x14, 0x28, 0xaa},
+ {0x00, 0xa5, 0x05, 0xaa},
+ {0x00, 0xab, 0x07, 0xaa},
+ {0x00, 0x24, 0x95, 0xaa},
+ {0x00, 0x25, 0x33, 0xaa},
+ {0x00, 0x26, 0xe3, 0xaa},
+ {0x00, 0x9f, 0x88, 0xaa},
+ {0x00, 0xa0, 0x78, 0xaa},
+ {0x00, 0x55, 0x90, 0xaa},
+ {0x00, 0xa1, 0x03, 0xaa},
+ {0x00, 0xa6, 0xe0, 0xaa},
+ {0x00, 0xa7, 0xd8, 0xaa},
+ {0x00, 0xa8, 0xf0, 0xaa},
+ {0x00, 0xa9, 0x90, 0xaa},
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x13, 0xe5, 0xaa},
+ {0x00, 0x0e, 0x61, 0xaa},
+ {0x00, 0x0f, 0x4b, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x22, 0x91, 0xaa},
+ {0x00, 0x29, 0x07, 0xaa},
+ {0x00, 0x33, 0x0b, 0xaa},
+ {0x00, 0x35, 0x0b, 0xaa},
+ {0x00, 0x37, 0x1d, 0xaa},
+ {0x00, 0x38, 0x71, 0xaa},
+ {0x00, 0x39, 0x2a, 0xaa},
+ {0x00, 0x3c, 0x78, 0xaa},
+ {0x00, 0x4d, 0x40, 0xaa},
+ {0x00, 0x4e, 0x20, 0xaa},
+ {0x00, 0x74, 0x19, 0xaa},
+ {0x00, 0x8d, 0x4f, 0xaa},
+ {0x00, 0x8e, 0x00, 0xaa},
+ {0x00, 0x8f, 0x00, 0xaa},
+ {0x00, 0x90, 0x00, 0xaa},
+ {0x00, 0x91, 0x00, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x9a, 0x80, 0xaa},
+ {0x00, 0xb0, 0x84, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0xb2, 0x0e, 0xaa},
+ {0x00, 0xb3, 0x82, 0xaa},
+ {0x00, 0xb8, 0x0a, 0xaa},
+ {0x00, 0x43, 0x14, 0xaa},
+ {0x00, 0x44, 0xf0, 0xaa},
+ {0x00, 0x45, 0x45, 0xaa},
+ {0x00, 0x46, 0x63, 0xaa},
+ {0x00, 0x47, 0x2d, 0xaa},
+ {0x00, 0x48, 0x46, 0xaa},
+ {0x00, 0x59, 0x88, 0xaa},
+ {0x00, 0x5a, 0xa0, 0xaa},
+ {0x00, 0x5b, 0xc6, 0xaa},
+ {0x00, 0x5c, 0x7d, 0xaa},
+ {0x00, 0x5d, 0x5f, 0xaa},
+ {0x00, 0x5e, 0x19, 0xaa},
+ {0x00, 0x6c, 0x0a, 0xaa},
+ {0x00, 0x6d, 0x55, 0xaa},
+ {0x00, 0x6e, 0x11, 0xaa},
+ {0x00, 0x6f, 0x9e, 0xaa},
+ {0x00, 0x69, 0x00, 0xaa},
+ {0x00, 0x6a, 0x40, 0xaa},
+ {0x00, 0x01, 0x40, 0xaa},
+ {0x00, 0x02, 0x40, 0xaa},
+ {0x00, 0x13, 0xe7, 0xaa},
+ {0x00, 0x5f, 0xf0, 0xaa},
+ {0x00, 0x60, 0xf0, 0xaa},
+ {0x00, 0x61, 0xf0, 0xaa},
+ {0x00, 0x27, 0xa0, 0xaa},
+ {0x00, 0x28, 0x80, 0xaa},
+ {0x00, 0x2c, 0x90, 0xaa},
+ {0x00, 0x4f, 0x66, 0xaa},
+ {0x00, 0x50, 0x66, 0xaa},
+ {0x00, 0x51, 0x00, 0xaa},
+ {0x00, 0x52, 0x22, 0xaa},
+ {0x00, 0x53, 0x5e, 0xaa},
+ {0x00, 0x54, 0x80, 0xaa},
+ {0x00, 0x58, 0x9e, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x85, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x0a, 0xaa},
+ {0x00, 0x3d, 0x88, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x0b, 0xaa},
+ {0x00, 0x65, 0x00, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x56, 0x50, 0xaa},
+ {0x00, 0x34, 0x11, 0xaa},
+ {0x00, 0xa4, 0x88, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x97, 0x30, 0xaa},
+ {0x00, 0x98, 0x20, 0xaa},
+ {0x00, 0x99, 0x30, 0xaa},
+ {0x00, 0x9a, 0x84, 0xaa},
+ {0x00, 0x9b, 0x29, 0xaa},
+ {0x00, 0x9c, 0x03, 0xaa},
+ {0x00, 0x78, 0x04, 0xaa},
+ {0x00, 0x79, 0x01, 0xaa},
+ {0x00, 0xc8, 0xf0, 0xaa},
+ {0x00, 0x79, 0x0f, 0xaa},
+ {0x00, 0xc8, 0x00, 0xaa},
+ {0x00, 0x79, 0x10, 0xaa},
+ {0x00, 0xc8, 0x7e, 0xaa},
+ {0x00, 0x79, 0x0a, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x0b, 0xaa},
+ {0x00, 0xc8, 0x01, 0xaa},
+ {0x00, 0x79, 0x0c, 0xaa},
+ {0x00, 0xc8, 0x0f, 0xaa},
+ {0x00, 0x79, 0x0d, 0xaa},
+ {0x00, 0xc8, 0x20, 0xaa},
+ {0x00, 0x79, 0x09, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x02, 0xaa},
+ {0x00, 0xc8, 0xc0, 0xaa},
+ {0x00, 0x79, 0x03, 0xaa},
+ {0x00, 0xc8, 0x40, 0xaa},
+ {0x00, 0x79, 0x05, 0xaa},
+ {0x00, 0xc8, 0x30, 0xaa},
+ {0x00, 0x79, 0x26, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x17, 0x14, 0xaa},
+ {0x00, 0x18, 0x02, 0xaa},
+ {0x00, 0x32, 0x92, 0xaa},
+ {0x00, 0x19, 0x02, 0xaa},
+ {0x00, 0x1a, 0x7a, 0xaa},
+ {0x00, 0x03, 0x0a, 0xaa},
+ {0x00, 0x0c, 0x00, 0xaa},
+ {0x00, 0x3e, 0x00, 0xaa},
+ {0x00, 0x70, 0x3a, 0xaa},
+ {0x00, 0x71, 0x35, 0xaa},
+ {0x00, 0x72, 0x11, 0xaa},
+ {0x00, 0x73, 0xf0, 0xaa},
+ {0x00, 0xa2, 0x02, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x24, 0x80, 0xaa},
+ {0x00, 0x25, 0x74, 0xaa},
+ {0x00, 0x26, 0xd3, 0xaa},
+ {0x00, 0x0d, 0x00, 0xaa},
+ {0x00, 0x14, 0x18, 0xaa},
+ {0x00, 0x9d, 0x99, 0xaa},
+ {0x00, 0x9e, 0x7f, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x06, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x07, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x00, 0xaa},
+ {0x00, 0x3d, 0xc2, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0x00, 0x77, 0x05, 0xaa},
+ {},
+};
+
+static const u8 ov7670_InitQVGA[][4] = {
+ {0xb3, 0x01, 0x05, 0xcc},
+ {0x00, 0x00, 0x30, 0xdd},
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x66, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x16, 0x01, 0xcc},
+ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xb3, 0x03, 0x1f, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0x00, 0x12, 0x80, 0xaa},
+ {0x00, 0x00, 0x20, 0xdd},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x6b, 0x0a, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x7a, 0x29, 0xaa},
+ {0x00, 0x7b, 0x0e, 0xaa},
+ {0x00, 0x7c, 0x1a, 0xaa},
+ {0x00, 0x7d, 0x31, 0xaa},
+ {0x00, 0x7e, 0x53, 0xaa},
+ {0x00, 0x7f, 0x60, 0xaa},
+ {0x00, 0x80, 0x6b, 0xaa},
+ {0x00, 0x81, 0x73, 0xaa},
+ {0x00, 0x82, 0x7b, 0xaa},
+ {0x00, 0x83, 0x82, 0xaa},
+ {0x00, 0x84, 0x89, 0xaa},
+ {0x00, 0x85, 0x96, 0xaa},
+ {0x00, 0x86, 0xa1, 0xaa},
+ {0x00, 0x87, 0xb7, 0xaa},
+ {0x00, 0x88, 0xcc, 0xaa},
+ {0x00, 0x89, 0xe1, 0xaa},
+ {0x00, 0x13, 0xe0, 0xaa},
+ {0x00, 0x00, 0x00, 0xaa},
+ {0x00, 0x10, 0x00, 0xaa},
+ {0x00, 0x0d, 0x40, 0xaa},
+ {0x00, 0x14, 0x28, 0xaa},
+ {0x00, 0xa5, 0x05, 0xaa},
+ {0x00, 0xab, 0x07, 0xaa},
+ {0x00, 0x24, 0x95, 0xaa},
+ {0x00, 0x25, 0x33, 0xaa},
+ {0x00, 0x26, 0xe3, 0xaa},
+ {0x00, 0x9f, 0x88, 0xaa},
+ {0x00, 0xa0, 0x78, 0xaa},
+ {0x00, 0x55, 0x90, 0xaa},
+ {0x00, 0xa1, 0x03, 0xaa},
+ {0x00, 0xa6, 0xe0, 0xaa},
+ {0x00, 0xa7, 0xd8, 0xaa},
+ {0x00, 0xa8, 0xf0, 0xaa},
+ {0x00, 0xa9, 0x90, 0xaa},
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x13, 0xe5, 0xaa},
+ {0x00, 0x0e, 0x61, 0xaa},
+ {0x00, 0x0f, 0x4b, 0xaa},
+ {0x00, 0x16, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x22, 0x91, 0xaa},
+ {0x00, 0x29, 0x07, 0xaa},
+ {0x00, 0x33, 0x0b, 0xaa},
+ {0x00, 0x35, 0x0b, 0xaa},
+ {0x00, 0x37, 0x1d, 0xaa},
+ {0x00, 0x38, 0x71, 0xaa},
+ {0x00, 0x39, 0x2a, 0xaa},
+ {0x00, 0x3c, 0x78, 0xaa},
+ {0x00, 0x4d, 0x40, 0xaa},
+ {0x00, 0x4e, 0x20, 0xaa},
+ {0x00, 0x74, 0x19, 0xaa},
+ {0x00, 0x8d, 0x4f, 0xaa},
+ {0x00, 0x8e, 0x00, 0xaa},
+ {0x00, 0x8f, 0x00, 0xaa},
+ {0x00, 0x90, 0x00, 0xaa},
+ {0x00, 0x91, 0x00, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x9a, 0x80, 0xaa},
+ {0x00, 0xb0, 0x84, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0xb2, 0x0e, 0xaa},
+ {0x00, 0xb3, 0x82, 0xaa},
+ {0x00, 0xb8, 0x0a, 0xaa},
+ {0x00, 0x43, 0x14, 0xaa},
+ {0x00, 0x44, 0xf0, 0xaa},
+ {0x00, 0x45, 0x45, 0xaa},
+ {0x00, 0x46, 0x63, 0xaa},
+ {0x00, 0x47, 0x2d, 0xaa},
+ {0x00, 0x48, 0x46, 0xaa},
+ {0x00, 0x59, 0x88, 0xaa},
+ {0x00, 0x5a, 0xa0, 0xaa},
+ {0x00, 0x5b, 0xc6, 0xaa},
+ {0x00, 0x5c, 0x7d, 0xaa},
+ {0x00, 0x5d, 0x5f, 0xaa},
+ {0x00, 0x5e, 0x19, 0xaa},
+ {0x00, 0x6c, 0x0a, 0xaa},
+ {0x00, 0x6d, 0x55, 0xaa},
+ {0x00, 0x6e, 0x11, 0xaa},
+ {0x00, 0x6f, 0x9e, 0xaa},
+ {0x00, 0x69, 0x00, 0xaa},
+ {0x00, 0x6a, 0x40, 0xaa},
+ {0x00, 0x01, 0x40, 0xaa},
+ {0x00, 0x02, 0x40, 0xaa},
+ {0x00, 0x13, 0xe7, 0xaa},
+ {0x00, 0x5f, 0xf0, 0xaa},
+ {0x00, 0x60, 0xf0, 0xaa},
+ {0x00, 0x61, 0xf0, 0xaa},
+ {0x00, 0x27, 0xa0, 0xaa},
+ {0x00, 0x28, 0x80, 0xaa},
+ {0x00, 0x2c, 0x90, 0xaa},
+ {0x00, 0x4f, 0x66, 0xaa},
+ {0x00, 0x50, 0x66, 0xaa},
+ {0x00, 0x51, 0x00, 0xaa},
+ {0x00, 0x52, 0x22, 0xaa},
+ {0x00, 0x53, 0x5e, 0xaa},
+ {0x00, 0x54, 0x80, 0xaa},
+ {0x00, 0x58, 0x9e, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x85, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x0a, 0xaa},
+ {0x00, 0x3d, 0x88, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x30, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x0b, 0xaa},
+ {0x00, 0x65, 0x00, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x56, 0x50, 0xaa},
+ {0x00, 0x34, 0x11, 0xaa},
+ {0x00, 0xa4, 0x88, 0xaa},
+ {0x00, 0x96, 0x00, 0xaa},
+ {0x00, 0x97, 0x30, 0xaa},
+ {0x00, 0x98, 0x20, 0xaa},
+ {0x00, 0x99, 0x30, 0xaa},
+ {0x00, 0x9a, 0x84, 0xaa},
+ {0x00, 0x9b, 0x29, 0xaa},
+ {0x00, 0x9c, 0x03, 0xaa},
+ {0x00, 0x78, 0x04, 0xaa},
+ {0x00, 0x79, 0x01, 0xaa},
+ {0x00, 0xc8, 0xf0, 0xaa},
+ {0x00, 0x79, 0x0f, 0xaa},
+ {0x00, 0xc8, 0x00, 0xaa},
+ {0x00, 0x79, 0x10, 0xaa},
+ {0x00, 0xc8, 0x7e, 0xaa},
+ {0x00, 0x79, 0x0a, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x0b, 0xaa},
+ {0x00, 0xc8, 0x01, 0xaa},
+ {0x00, 0x79, 0x0c, 0xaa},
+ {0x00, 0xc8, 0x0f, 0xaa},
+ {0x00, 0x79, 0x0d, 0xaa},
+ {0x00, 0xc8, 0x20, 0xaa},
+ {0x00, 0x79, 0x09, 0xaa},
+ {0x00, 0xc8, 0x80, 0xaa},
+ {0x00, 0x79, 0x02, 0xaa},
+ {0x00, 0xc8, 0xc0, 0xaa},
+ {0x00, 0x79, 0x03, 0xaa},
+ {0x00, 0xc8, 0x40, 0xaa},
+ {0x00, 0x79, 0x05, 0xaa},
+ {0x00, 0xc8, 0x30, 0xaa},
+ {0x00, 0x79, 0x26, 0xaa},
+ {0x00, 0x11, 0x40, 0xaa},
+ {0x00, 0x3a, 0x04, 0xaa},
+ {0x00, 0x12, 0x00, 0xaa},
+ {0x00, 0x40, 0xc0, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x17, 0x14, 0xaa},
+ {0x00, 0x18, 0x02, 0xaa},
+ {0x00, 0x32, 0x92, 0xaa},
+ {0x00, 0x19, 0x02, 0xaa},
+ {0x00, 0x1a, 0x7a, 0xaa},
+ {0x00, 0x03, 0x0a, 0xaa},
+ {0x00, 0x0c, 0x00, 0xaa},
+ {0x00, 0x3e, 0x00, 0xaa},
+ {0x00, 0x70, 0x3a, 0xaa},
+ {0x00, 0x71, 0x35, 0xaa},
+ {0x00, 0x72, 0x11, 0xaa},
+ {0x00, 0x73, 0xf0, 0xaa},
+ {0x00, 0xa2, 0x02, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb1, 0x0c, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x24, 0x80, 0xaa},
+ {0x00, 0x25, 0x74, 0xaa},
+ {0x00, 0x26, 0xd3, 0xaa},
+ {0x00, 0x0d, 0x00, 0xaa},
+ {0x00, 0x14, 0x18, 0xaa},
+ {0x00, 0x9d, 0x99, 0xaa},
+ {0x00, 0x9e, 0x7f, 0xaa},
+ {0x00, 0x64, 0x08, 0xaa},
+ {0x00, 0x94, 0x07, 0xaa},
+ {0x00, 0x95, 0x06, 0xaa},
+ {0x00, 0x66, 0x05, 0xaa},
+ {0x00, 0x41, 0x08, 0xaa},
+ {0x00, 0x3f, 0x00, 0xaa},
+ {0x00, 0x75, 0x07, 0xaa},
+ {0x00, 0x76, 0xe1, 0xaa},
+ {0x00, 0x4c, 0x00, 0xaa},
+ {0x00, 0x77, 0x00, 0xaa},
+ {0x00, 0x3d, 0xc2, 0xaa},
+ {0x00, 0x4b, 0x09, 0xaa},
+ {0x00, 0xc9, 0x60, 0xaa},
+ {0x00, 0x41, 0x38, 0xaa},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+ {0xbc, 0x05, 0x00, 0xcc},
+ {0xbc, 0x06, 0x00, 0xcc},
+ {0xbc, 0x08, 0x30, 0xcc},
+ {0xbc, 0x09, 0x40, 0xcc},
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0xbf, 0xc0, 0x26, 0xcc},
+ {0xbf, 0xc1, 0x02, 0xcc},
+ {0xbf, 0xcc, 0x04, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x01, 0x45, 0xcc},
+ {0x00, 0x77, 0x05, 0xaa},
+ {},
+};
+
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const u8 po1200_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+};
+static const u8 po1200_matrix[9] = {
+ 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
+ {0xb0, 0x03, 0x19, 0xcc},
+/* {0x00, 0x00, 0x33, 0xdd}, */
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb0, 0x02, 0x02, 0xcc},
+ {0xb3, 0x5d, 0x00, 0xcc},
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x02, 0xb2, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x02, 0xcc},
+ {0xb3, 0x23, 0x58, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x03, 0xcc},
+ {0xb3, 0x17, 0x1f, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x25, 0x02, 0xaa},
+ {0x00, 0x3c, 0x4f, 0xaa},
+ {0x00, 0x3f, 0xe0, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x45, 0x34, 0xaa},
+ {0x00, 0x55, 0xfe, 0xaa},
+ {0x00, 0x59, 0xd3, 0xaa},
+ {0x00, 0x5e, 0x04, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+ {0x00, 0x62, 0x02, 0xaa},
+ {0x00, 0xa7, 0x31, 0xaa},
+ {0x00, 0xa9, 0x66, 0xaa},
+ {0x00, 0xb0, 0x00, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb3, 0x11, 0xaa},
+ {0x00, 0xb6, 0x26, 0xaa},
+ {0x00, 0xb7, 0x20, 0xaa},
+ {0x00, 0xba, 0x04, 0xaa},
+ {0x00, 0x88, 0x42, 0xaa},
+ {0x00, 0x89, 0x9a, 0xaa},
+ {0x00, 0x8a, 0x88, 0xaa},
+ {0x00, 0x8b, 0x8e, 0xaa},
+ {0x00, 0x8c, 0x3e, 0xaa},
+ {0x00, 0x8d, 0x90, 0xaa},
+ {0x00, 0x8e, 0x87, 0xaa},
+ {0x00, 0x8f, 0x96, 0xaa},
+ {0x00, 0x90, 0x3d, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x10, 0xaa},
+ {0x00, 0x66, 0x20, 0xaa},
+ {0x00, 0x67, 0x2b, 0xaa},
+ {0x00, 0x68, 0x36, 0xaa},
+ {0x00, 0x69, 0x49, 0xaa},
+ {0x00, 0x6a, 0x5a, 0xaa},
+ {0x00, 0x6b, 0x7f, 0xaa},
+ {0x00, 0x6c, 0x9b, 0xaa},
+ {0x00, 0x6d, 0xba, 0xaa},
+ {0x00, 0x6e, 0xd4, 0xaa},
+ {0x00, 0x6f, 0xea, 0xaa},
+ {0x00, 0x70, 0x00, 0xaa},
+ {0x00, 0x71, 0x10, 0xaa},
+ {0x00, 0x72, 0x20, 0xaa},
+ {0x00, 0x73, 0x2b, 0xaa},
+ {0x00, 0x74, 0x36, 0xaa},
+ {0x00, 0x75, 0x49, 0xaa},
+ {0x00, 0x76, 0x5a, 0xaa},
+ {0x00, 0x77, 0x7f, 0xaa},
+ {0x00, 0x78, 0x9b, 0xaa},
+ {0x00, 0x79, 0xba, 0xaa},
+ {0x00, 0x7a, 0xd4, 0xaa},
+ {0x00, 0x7b, 0xea, 0xaa},
+ {0x00, 0x7c, 0x00, 0xaa},
+ {0x00, 0x7d, 0x10, 0xaa},
+ {0x00, 0x7e, 0x20, 0xaa},
+ {0x00, 0x7f, 0x2b, 0xaa},
+ {0x00, 0x80, 0x36, 0xaa},
+ {0x00, 0x81, 0x49, 0xaa},
+ {0x00, 0x82, 0x5a, 0xaa},
+ {0x00, 0x83, 0x7f, 0xaa},
+ {0x00, 0x84, 0x9b, 0xaa},
+ {0x00, 0x85, 0xba, 0xaa},
+ {0x00, 0x86, 0xd4, 0xaa},
+ {0x00, 0x87, 0xea, 0xaa},
+ {0x00, 0x57, 0x2a, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x04, 0x10, 0xaa},
+ {0x00, 0x05, 0x10, 0xaa},
+ {0x00, 0x06, 0x10, 0xaa},
+ {0x00, 0x07, 0x10, 0xaa},
+ {0x00, 0x08, 0x13, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x10, 0xaa},
+ {0x00, 0x0c, 0x20, 0xaa},
+ {0x00, 0x0d, 0x18, 0xaa},
+ {0x00, 0x22, 0x01, 0xaa},
+ {0x00, 0x23, 0x60, 0xaa},
+ {0x00, 0x25, 0x08, 0xaa},
+ {0x00, 0x26, 0x82, 0xaa},
+ {0x00, 0x2e, 0x0f, 0xaa},
+ {0x00, 0x2f, 0x1e, 0xaa},
+ {0x00, 0x30, 0x2d, 0xaa},
+ {0x00, 0x31, 0x3c, 0xaa},
+ {0x00, 0x32, 0x4b, 0xaa},
+ {0x00, 0x33, 0x5a, 0xaa},
+ {0x00, 0x34, 0x69, 0xaa},
+ {0x00, 0x35, 0x78, 0xaa},
+ {0x00, 0x36, 0x87, 0xaa},
+ {0x00, 0x37, 0x96, 0xaa},
+ {0x00, 0x38, 0xa5, 0xaa},
+ {0x00, 0x39, 0xb4, 0xaa},
+ {0x00, 0x3a, 0xc3, 0xaa},
+ {0x00, 0x3b, 0xd2, 0xaa},
+ {0x00, 0x3c, 0xe1, 0xaa},
+ {0x00, 0x3e, 0xff, 0xaa},
+ {0x00, 0x3f, 0xff, 0xaa},
+ {0x00, 0x40, 0xff, 0xaa},
+ {0x00, 0x41, 0xff, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x43, 0xff, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x20, 0xc4, 0xaa},
+ {0x00, 0x13, 0x03, 0xaa},
+ {0x00, 0x3c, 0x50, 0xaa},
+ {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
+ {0x00, 0x51, 0x5b, 0xaa},
+ {0x00, 0x52, 0x91, 0xaa},
+ {0x00, 0x53, 0x4c, 0xaa},
+ {0x00, 0x54, 0x50, 0xaa},
+ {0x00, 0x56, 0x02, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x03, 0xcc},
+ {0xb6, 0x02, 0x20, 0xcc},
+ {0xb6, 0x05, 0x02, 0xcc},
+ {0xb6, 0x04, 0x58, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x21, 0xcc},
+ {0xb6, 0x18, 0x03, 0xcc},
+ {0xb6, 0x17, 0xa9, 0xcc},
+ {0xb6, 0x16, 0x80, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb8, 0x06, 0x20, 0xcc},
+ {0xb8, 0x07, 0x03, 0xcc},
+ {0xb8, 0x08, 0x58, 0xcc},
+ {0xb8, 0x09, 0x02, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xd9, 0x0f, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x10, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x11, 0xaa},
+ {0x00, 0xda, 0x00, 0xaa},
+ {0x00, 0xd9, 0x12, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xd9, 0x13, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xe8, 0x11, 0xaa},
+ {0x00, 0xe9, 0x12, 0xaa},
+ {0x00, 0xea, 0x5c, 0xaa},
+ {0x00, 0xeb, 0xff, 0xaa},
+ {0x00, 0xd8, 0x80, 0xaa},
+ {0x00, 0xe6, 0x02, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe3, 0x05, 0xaa},
+ {0x00, 0xe0, 0x40, 0xaa},
+ {0x00, 0xde, 0x03, 0xaa},
+ {0x00, 0xdf, 0x03, 0xaa},
+ {0x00, 0xdb, 0x02, 0xaa},
+ {0x00, 0xdc, 0x00, 0xaa},
+ {0x00, 0xdd, 0x03, 0xaa},
+ {0x00, 0xe1, 0x08, 0xaa},
+ {0x00, 0xe2, 0x01, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe4, 0x40, 0xaa},
+ {0x00, 0xa8, 0x8f, 0xaa},
+ {0x00, 0xb4, 0x16, 0xaa},
+ {0xb0, 0x02, 0x06, 0xcc},
+ {0xb0, 0x18, 0x06, 0xcc},
+ {0xb0, 0x19, 0x06, 0xcc},
+ {0xb3, 0x5d, 0x18, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0x00, 0xb4, 0x0e, 0xaa},
+ {0x00, 0xb5, 0x49, 0xaa},
+ {0x00, 0xb6, 0x1c, 0xaa},
+ {0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1a, 0x09, 0xaa},
+ {0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+ {0xb8, 0x27, 0x00, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x91, 0x35, 0xaa},
+ {0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x4d, 0x20, 0xaa},
+ {0xb8, 0x22, 0x40, 0xcc},
+ {0xb8, 0x23, 0x40, 0xcc},
+ {0xb8, 0x24, 0x40, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x16, 0x40, 0xaa},
+ {0x00, 0x17, 0x40, 0xaa},
+ {0x00, 0x18, 0x40, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xb4, 0x1c, 0xaa},
+ {0x00, 0xb5, 0x92, 0xaa},
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+ {}
+};
+
+static const u8 poxxxx_init_common[][4] = {
+ {0xb3, 0x00, 0x04, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb0, 0x03, 0x09, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */
+ {0xb3, 0x23, 0x00, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */
+ {0xb3, 0x17, 0xff, 0xcc},
+ {0xb3, 0x2c, 0x03, 0xcc},
+ {0xb3, 0x2d, 0x56, 0xcc},
+ {0xb3, 0x2e, 0x02, 0xcc},
+ {0xb3, 0x2f, 0x0a, 0xcc},
+ {0xb3, 0x40, 0x00, 0xcc},
+ {0xb3, 0x41, 0x34, 0xcc},
+ {0xb3, 0x42, 0x01, 0xcc},
+ {0xb3, 0x43, 0xe0, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb3, 0x4d, 0x00, 0xcc},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0x12, 0x08, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x31, 0x1f, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0x36, 0xc0, 0xaa},
+ {0x00, 0x37, 0xc8, 0xaa},
+ {0x00, 0x3b, 0x36, 0xaa},
+ {0x00, 0x4b, 0xfe, 0xaa},
+ {0x00, 0x4d, 0x2e, 0xaa},
+ {0x00, 0x51, 0x1c, 0xaa},
+ {0x00, 0x52, 0x01, 0xaa},
+ {0x00, 0x55, 0x0a, 0xaa},
+ {0x00, 0x56, 0x0a, 0xaa},
+ {0x00, 0x57, 0x07, 0xaa},
+ {0x00, 0x58, 0x07, 0xaa},
+ {0x00, 0x59, 0x04, 0xaa},
+ {0x00, 0x70, 0x68, 0xaa},
+ {0x00, 0x71, 0x04, 0xaa},
+ {0x00, 0x72, 0x10, 0xaa},
+ {0x00, 0x80, 0x71, 0xaa},
+ {0x00, 0x81, 0x08, 0xaa},
+ {0x00, 0x82, 0x00, 0xaa},
+ {0x00, 0x83, 0x55, 0xaa},
+ {0x00, 0x84, 0x06, 0xaa},
+ {0x00, 0x85, 0x06, 0xaa},
+ {0x00, 0x8b, 0x25, 0xaa},
+ {0x00, 0x8c, 0x00, 0xaa},
+ {0x00, 0x8d, 0x86, 0xaa},
+ {0x00, 0x8e, 0x82, 0xaa},
+ {0x00, 0x8f, 0x2d, 0xaa},
+ {0x00, 0x90, 0x8b, 0xaa},
+ {0x00, 0x91, 0x81, 0xaa},
+ {0x00, 0x92, 0x81, 0xaa},
+ {0x00, 0x93, 0x23, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {0x00, 0xb0, 0x68, 0xaa},
+ {0x00, 0xbc, 0x04, 0xaa},
+ {0x00, 0xbe, 0x3b, 0xaa},
+ {0x00, 0x4e, 0x40, 0xaa},
+ {0x00, 0x06, 0x04, 0xaa},
+ {0x00, 0x07, 0x03, 0xaa},
+ {0x00, 0xcd, 0x18, 0xaa},
+ {0x00, 0x28, 0x03, 0xaa},
+ {0x00, 0x29, 0xef, 0xaa},
+/* reinit on alt 2 (qvga) or alt7 (vga) */
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb8, 0x00, 0x01, 0xcc},
+
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa},
+ {0x00, 0x00, 0x40, 0xdd},
+ {0x00, 0x1d, 0x05, 0xaa},
+ {}
+};
+static const u8 poxxxx_gamma[][4] = {
+ {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+
+ {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */
+ {0x00, 0x73, 0x00, 0xaa},
+ {0x00, 0x74, 0x0a, 0xaa},
+ {0x00, 0x75, 0x16, 0xaa},
+ {0x00, 0x76, 0x25, 0xaa},
+ {0x00, 0x77, 0x34, 0xaa},
+ {0x00, 0x78, 0x49, 0xaa},
+ {0x00, 0x79, 0x5a, 0xaa},
+ {0x00, 0x7a, 0x7f, 0xaa},
+ {0x00, 0x7b, 0x9b, 0xaa},
+ {0x00, 0x7c, 0xba, 0xaa},
+ {0x00, 0x7d, 0xd4, 0xaa},
+ {0x00, 0x7e, 0xea, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_start_3[][4] = {
+ {0x00, 0xb8, 0x28, 0xaa},
+ {0x00, 0xb9, 0x1e, 0xaa},
+ {0x00, 0xb6, 0x14, 0xaa},
+ {0x00, 0xb7, 0x0f, 0xaa},
+ {0x00, 0x5c, 0x10, 0xaa},
+ {0x00, 0x5d, 0x18, 0xaa},
+ {0x00, 0x5e, 0x24, 0xaa},
+ {0x00, 0x5f, 0x24, 0xaa},
+ {0x00, 0x86, 0x1a, 0xaa},
+ {0x00, 0x60, 0x00, 0xaa},
+ {0x00, 0x61, 0x1b, 0xaa},
+ {0x00, 0x62, 0x30, 0xaa},
+ {0x00, 0x63, 0x40, 0xaa},
+ {0x00, 0x87, 0x1a, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x08, 0xaa},
+ {0x00, 0x66, 0x10, 0xaa},
+ {0x00, 0x67, 0x20, 0xaa},
+ {0x00, 0x88, 0x10, 0xaa},
+ {0x00, 0x68, 0x00, 0xaa},
+ {0x00, 0x69, 0x08, 0xaa},
+ {0x00, 0x6a, 0x0f, 0xaa},
+ {0x00, 0x6b, 0x0f, 0xaa},
+ {0x00, 0x89, 0x07, 0xaa},
+ {0x00, 0xd5, 0x4c, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x2a, 0xaa},
+ {0x00, 0x0e, 0x03, 0xaa},
+ {0x00, 0x0f, 0xea, 0xaa},
+ {0x00, 0xa2, 0x00, 0xaa},
+ {0x00, 0xa3, 0x2a, 0xaa},
+ {0x00, 0xa4, 0x03, 0xaa},
+ {0x00, 0xa5, 0xea, 0xaa},
+ {}
+};
+static const u8 poxxxx_initVGA[][4] = {
+ {0x00, 0x20, 0x11, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_initQVGA[][4] = {
+ {0x00, 0x20, 0x33, 0xaa},
+ {0x00, 0x33, 0x38, 0xaa},
+ {0x00, 0xbb, 0x0d, 0xaa},
+ {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */
+ {0xb3, 0x23, 0xf0, 0xcc},
+ {0xb3, 0x16, 0x01, 0xcc},
+ {0xb3, 0x17, 0x3f, 0xcc},
+ {0xb3, 0x02, 0xb0, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x00, 0xcc},
+ {0x00, 0x04, 0x06, 0xaa},
+ {0x00, 0x05, 0x3f, 0xaa},
+ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
+ {}
+};
+static const u8 poxxxx_init_end_1[][4] = {
+ {0x00, 0x47, 0x25, 0xaa},
+ {0x00, 0x48, 0x80, 0xaa},
+ {0x00, 0x49, 0x1f, 0xaa},
+ {0x00, 0x4a, 0x40, 0xaa},
+ {0x00, 0x44, 0x40, 0xaa},
+ {0x00, 0xab, 0x4a, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb2, 0x04, 0xaa},
+ {0x00, 0xb3, 0x08, 0xaa},
+ {0x00, 0xb4, 0x0b, 0xaa},
+ {0x00, 0xb5, 0x0d, 0xaa},
+ {}
+};
+static const u8 poxxxx_init_end_2[][4] = {
+ {0x00, 0x1d, 0x85, 0xaa},
+ {0x00, 0x1e, 0x06, 0xaa},
+ {0x00, 0x1d, 0x05, 0xaa},
+ {}
+};
+
+struct sensor_info {
+ s8 sensorId;
+ u8 I2cAdd;
+ u8 IdAdd;
+ u16 VpId;
+ u8 m1;
+ u8 m2;
+ u8 op;
+};
+
+/* probe values */
+static const struct sensor_info vc0321_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 PO3030K */
+ {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
+};
+static const struct sensor_info vc0323_probe_data[] = {
+/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
+ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 3 MI1310 */
+ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 4 MI360 - tested in vc032x_probe_sensor */
+/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
+/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
+/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
+/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
+/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
+/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+/* 12 OV9650 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
+/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+/* 14 MI360_SOC - ??? */
+/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
+/* 16 ?? */
+ {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
+/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
+/*fixme: not in the ms-win probe - may be found before? */
+ {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+};
+
+/* read 'len' bytes in gspca_dev->usb_buf */
+static void reg_r_i(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 index,
+ u16 len)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_r(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 index,
+ u16 len)
+{
+ reg_r_i(gspca_dev, req, index, len);
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (len == 1)
+ PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index,
+ gspca_dev->usb_buf[0]);
+ else
+ PDEBUG(D_USBI, "GET %02x 0001 %04x %*ph",
+ req, index, 3, gspca_dev->usb_buf);
+#endif
+}
+
+static void reg_w_i(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 value,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+static void reg_w(struct gspca_dev *gspca_dev,
+ u16 req,
+ u16 value,
+ u16 index)
+{
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
+#endif
+ reg_w_i(gspca_dev, req, value, index);
+}
+
+static u16 read_sensor_register(struct gspca_dev *gspca_dev,
+ u16 address)
+{
+ u8 ldata, mdata, hdata;
+ int retry = 50;
+
+ reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+ if (!(gspca_dev->usb_buf[0] & 0x02)) {
+ pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
+ return 0;
+ }
+ reg_w(gspca_dev, 0xa0, address, 0xb33a);
+ reg_w(gspca_dev, 0xa0, 0x02, 0xb339);
+
+ do {
+ reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+ if (gspca_dev->usb_buf[0] == 0x00)
+ break;
+ msleep(40);
+ } while (--retry >= 0);
+
+ reg_r(gspca_dev, 0xa1, 0xb33e, 1);
+ ldata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33d, 1);
+ mdata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33c, 1);
+ hdata = gspca_dev->usb_buf[0];
+ if (hdata != 0 && mdata != 0 && ldata != 0)
+ PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
+ hdata, mdata, ldata);
+ reg_r(gspca_dev, 0xa1, 0xb334, 1);
+ if (gspca_dev->usb_buf[0] == 0x02)
+ return (hdata << 8) + mdata;
+ return hdata;
+}
+
+static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, n;
+ u16 value;
+ const struct sensor_info *ptsensor_info;
+
+/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/
+ if (sd->flags & FL_SAMSUNG) {
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+ reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+ /* select the back sensor */
+ }
+
+ reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+ PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
+ sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
+ if (sd->bridge == BRIDGE_VC0321) {
+ ptsensor_info = vc0321_probe_data;
+ n = ARRAY_SIZE(vc0321_probe_data);
+ } else {
+ ptsensor_info = vc0323_probe_data;
+ n = ARRAY_SIZE(vc0323_probe_data);
+ }
+ for (i = 0; i < n; i++) {
+ reg_w(gspca_dev, 0xa0, 0x02, 0xb334);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb308);
+ reg_w(gspca_dev, 0xa0, 0x0c, 0xb309);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
+ reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301);
+ value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd);
+ if (value == 0 && ptsensor_info->IdAdd == 0x82)
+ value = read_sensor_register(gspca_dev, 0x83);
+ if (value != 0) {
+ PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)",
+ value, i);
+ if (value == ptsensor_info->VpId)
+ return ptsensor_info->sensorId;
+
+ switch (value) {
+ case 0x3130:
+ return SENSOR_PO3130NC;
+ case 0x7673:
+ return SENSOR_OV7670;
+ case 0x8243:
+ return SENSOR_MI0360;
+ }
+ }
+ ptsensor_info++;
+ }
+ return -1;
+}
+
+static void i2c_write(struct gspca_dev *gspca_dev,
+ u8 reg, const u8 *val,
+ u8 size) /* 1 or 2 */
+{
+ int retry;
+
+#ifdef GSPCA_DEBUG
+ if (gspca_dev->usb_err < 0)
+ return;
+ if (size == 1)
+ PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val);
+ else
+ PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]);
+#endif
+ reg_r_i(gspca_dev, 0xa1, 0xb33f, 1);
+/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/
+ reg_w_i(gspca_dev, 0xa0, size, 0xb334);
+ reg_w_i(gspca_dev, 0xa0, reg, 0xb33a);
+ reg_w_i(gspca_dev, 0xa0, val[0], 0xb336);
+ if (size > 1)
+ reg_w_i(gspca_dev, 0xa0, val[1], 0xb337);
+ reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339);
+ retry = 4;
+ do {
+ reg_r_i(gspca_dev, 0xa1, 0xb33b, 1);
+ if (gspca_dev->usb_buf[0] == 0)
+ break;
+ msleep(20);
+ } while (--retry > 0);
+ if (retry <= 0)
+ pr_err("i2c_write timeout\n");
+}
+
+static void put_tab_to_reg(struct gspca_dev *gspca_dev,
+ const u8 *tab, u8 tabsize, u16 addr)
+{
+ int j;
+ u16 ad = addr;
+
+ for (j = 0; j < tabsize; j++)
+ reg_w(gspca_dev, 0xa0, tab[j], ad++);
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+ const u8 data[][4])
+{
+ int i = 0;
+
+ for (;;) {
+ switch (data[i][3]) {
+ default:
+ return;
+ case 0xcc: /* normal write */
+ reg_w(gspca_dev, 0xa0, data[i][2],
+ (data[i][0]) << 8 | data[i][1]);
+ break;
+ case 0xaa: /* i2c op */
+ i2c_write(gspca_dev, data[i][1], &data[i][2], 1);
+ break;
+ case 0xbb: /* i2c op */
+ i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
+ break;
+ case 0xdd:
+ msleep(data[i][1] * 256 + data[i][2] + 10);
+ break;
+ }
+ i++;
+ }
+ /*not reached*/
+}
+
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->flags = id->driver_info & 0xff;
+
+ if (id->idVendor == 0x046d &&
+ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
+ sd->sensor = SENSOR_POxxxx; /* no probe */
+
+ 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;
+ struct cam *cam;
+ int sensor;
+ /* number of packets per ISOC message */
+ static u8 npkt[NSENSORS] = {
+ [SENSOR_HV7131R] = 64,
+ [SENSOR_MI0360] = 32,
+ [SENSOR_MI1310_SOC] = 32,
+ [SENSOR_MI1320] = 64,
+ [SENSOR_MI1320_SOC] = 128,
+ [SENSOR_OV7660] = 32,
+ [SENSOR_OV7670] = 64,
+ [SENSOR_PO1200] = 128,
+ [SENSOR_PO3130NC] = 128,
+ [SENSOR_POxxxx] = 128,
+ };
+
+ if (sd->sensor != SENSOR_POxxxx)
+ sensor = vc032x_probe_sensor(gspca_dev);
+ else
+ sensor = sd->sensor;
+
+ switch (sensor) {
+ case -1:
+ pr_err("Unknown sensor...\n");
+ return -EINVAL;
+ case SENSOR_HV7131R:
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ break;
+ case SENSOR_MI0360:
+ PDEBUG(D_PROBE, "Find Sensor MI0360");
+ sd->bridge = BRIDGE_VC0323;
+ break;
+ case SENSOR_MI1310_SOC:
+ PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
+ break;
+ case SENSOR_MI1320:
+ PDEBUG(D_PROBE, "Find Sensor MI1320");
+ break;
+ case SENSOR_MI1320_SOC:
+ PDEBUG(D_PROBE, "Find Sensor MI1320_SOC");
+ break;
+ case SENSOR_OV7660:
+ PDEBUG(D_PROBE, "Find Sensor OV7660");
+ break;
+ case SENSOR_OV7670:
+ PDEBUG(D_PROBE, "Find Sensor OV7670");
+ break;
+ case SENSOR_PO1200:
+ PDEBUG(D_PROBE, "Find Sensor PO1200");
+ break;
+ case SENSOR_PO3130NC:
+ PDEBUG(D_PROBE, "Find Sensor PO3130NC");
+ break;
+ case SENSOR_POxxxx:
+ PDEBUG(D_PROBE, "Sensor POxxxx");
+ break;
+ }
+ sd->sensor = sensor;
+
+ cam = &gspca_dev->cam;
+ if (sd->bridge == BRIDGE_VC0321) {
+ cam->cam_mode = vc0321_mode;
+ cam->nmodes = ARRAY_SIZE(vc0321_mode);
+ } else {
+ switch (sensor) {
+ case SENSOR_PO1200:
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
+ break;
+ case SENSOR_MI1310_SOC:
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode);
+ break;
+ case SENSOR_MI1320_SOC:
+ cam->cam_mode = bi_mode;
+ cam->nmodes = ARRAY_SIZE(bi_mode);
+ break;
+ case SENSOR_OV7670:
+ cam->cam_mode = bi_mode;
+ cam->nmodes = ARRAY_SIZE(bi_mode) - 1;
+ break;
+ default:
+ cam->cam_mode = vc0323_mode;
+ cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1;
+ break;
+ }
+ }
+ cam->npkt = npkt[sd->sensor];
+
+ if (sd->sensor == SENSOR_OV7670)
+ sd->flags |= FL_HFLIP | FL_VFLIP;
+
+ if (sd->bridge == BRIDGE_VC0321) {
+ reg_r(gspca_dev, 0x8a, 0, 3);
+ reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
+ reg_r(gspca_dev, 0x8b, 0, 3);
+ reg_w(gspca_dev, 0x88, 0x00, 0x0202);
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_r(gspca_dev, 0xa1, 0xb300, 1);
+ if (gspca_dev->usb_buf[0] != 0) {
+ reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+ }
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+ }
+ }
+ return gspca_dev->usb_err;
+}
+
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 data;
+
+ data = val;
+ if (data >= 0x80)
+ data &= 0x7f;
+ else
+ data = 0xff ^ data;
+ i2c_write(gspca_dev, 0x98, &data, 1);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev, u8 val)
+{
+ i2c_write(gspca_dev, 0x99, &val, 1);
+}
+
+static void setcolors(struct gspca_dev *gspca_dev, u8 val)
+{
+ u8 data;
+
+ data = val - (val >> 3) - 1;
+ i2c_write(gspca_dev, 0x94, &data, 1);
+ i2c_write(gspca_dev, 0x95, &val, 1);
+}
+
+static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data[2];
+
+ if (sd->flags & FL_HFLIP)
+ hflip = !hflip;
+ if (sd->flags & FL_VFLIP)
+ vflip = !vflip;
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
+ case SENSOR_MI1320:
+ case SENSOR_MI1320_SOC:
+ data[0] = data[1] = 0; /* select page 0 */
+ i2c_write(gspca_dev, 0xf0, data, 2);
+ data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01;
+ data[1] = 0x02 * hflip
+ | 0x01 * vflip;
+ i2c_write(gspca_dev, 0x20, data, 2);
+ break;
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07;
+ data[0] |= OV7660_MVFP_MIRROR * hflip
+ | OV7660_MVFP_VFLIP * vflip;
+ i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1);
+ break;
+ case SENSOR_PO1200:
+ data[0] = 0;
+ i2c_write(gspca_dev, 0x03, data, 1);
+ data[0] = 0x80 * hflip
+ | 0x40 * vflip
+ | 0x06;
+ i2c_write(gspca_dev, 0x1e, data, 1);
+ break;
+ }
+}
+
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ static const u8 (*ov7660_freq_tb[3])[4] =
+ {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ};
+
+ if (sd->sensor != SENSOR_OV7660)
+ return;
+ usb_exchange(gspca_dev, ov7660_freq_tb[val]);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ if (val < 0)
+ data = 0x6a;
+ else
+ data = 0xb5 + val * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+ break;
+ case SENSOR_POxxxx:
+ if (val < 0)
+ data = 0x7e; /* def = max */
+ else
+ data = 0x60 + val * 0x0f;
+ i2c_write(gspca_dev, 0x59, &data, 1);
+ break;
+ }
+}
+static void setgain(struct gspca_dev *gspca_dev, u8 val)
+{
+ i2c_write(gspca_dev, 0x15, &val, 1);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ u8 data;
+
+ data = val >> 8;
+ i2c_write(gspca_dev, 0x1a, &data, 1);
+ data = val;
+ i2c_write(gspca_dev, 0x1b, &data, 1);
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+ static const u8 data[2] = {0x28, 0x3c};
+
+ i2c_write(gspca_dev, 0xd1, &data[val], 1);
+}
+
+static void setgamma(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do */
+ usb_exchange(gspca_dev, poxxxx_gamma);
+}
+
+static void setbacklight(struct gspca_dev *gspca_dev, s32 val)
+{
+ u16 v;
+ u8 data;
+
+ data = (val << 4) | 0x0f;
+ i2c_write(gspca_dev, 0xaa, &data, 1);
+ v = 613 + 12 * val;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc4, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc5, &data, 1);
+ v = 1093 - 12 * val;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc6, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc7, &data, 1);
+ v = 342 + 9 * val;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xc8, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xc9, &data, 1);
+ v = 702 - 9 * val;
+ data = v >> 8;
+ i2c_write(gspca_dev, 0xca, &data, 1);
+ data = v;
+ i2c_write(gspca_dev, 0xcb, &data, 1);
+}
+
+static void setwb(struct gspca_dev *gspca_dev)
+{
+/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
+ static const u8 data[2] = {0x00, 0x00};
+
+ i2c_write(gspca_dev, 0x16, &data[0], 1);
+ i2c_write(gspca_dev, 0x18, &data[1], 1);
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 (*init)[4];
+ const u8 *GammaT = NULL;
+ const u8 *MatrixT = NULL;
+ int mode;
+ static const u8 (*mi1320_soc_init[])[4] = {
+ mi1320_soc_InitSXGA,
+ mi1320_soc_InitVGA,
+ mi1320_soc_InitQVGA,
+ };
+
+/*fixme: back sensor only*/
+ if (sd->flags & FL_SAMSUNG) {
+ reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
+ reg_w(gspca_dev, 0xa9, 0x8348, 0x000e);
+ reg_w(gspca_dev, 0xa9, 0x0000, 0x001a);
+ }
+
+ /* Assume start use the good resolution from gspca_dev->mode */
+ if (sd->bridge == BRIDGE_VC0321) {
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfec);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfed);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfee);
+ reg_w(gspca_dev, 0xa0, 0xff, 0xbfef);
+ sd->image_offset = 46;
+ } else {
+ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat
+ == V4L2_PIX_FMT_JPEG)
+ sd->image_offset = 0;
+ else
+ sd->image_offset = 32;
+ }
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ GammaT = hv7131r_gamma;
+ MatrixT = hv7131r_matrix;
+ if (mode)
+ init = hv7131r_initQVGA_data; /* 320x240 */
+ else
+ init = hv7131r_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_OV7660:
+ GammaT = ov7660_gamma;
+ MatrixT = ov7660_matrix;
+ if (mode)
+ init = ov7660_initQVGA_data; /* 320x240 */
+ else
+ init = ov7660_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_MI0360:
+ GammaT = mi1320_gamma;
+ MatrixT = mi0360_matrix;
+ if (mode)
+ init = mi0360_initQVGA_JPG; /* 320x240 */
+ else
+ init = mi0360_initVGA_JPG; /* 640x480 */
+ break;
+ case SENSOR_MI1310_SOC:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ switch (mode) {
+ case 1:
+ init = mi1310_socinitQVGA_JPG; /* 320x240 */
+ break;
+ case 0:
+ init = mi1310_socinitVGA_JPG; /* 640x480 */
+ break;
+ default:
+ init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */
+ break;
+ }
+ break;
+ case SENSOR_MI1320:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ if (mode)
+ init = mi1320_initQVGA_data; /* 320x240 */
+ else
+ init = mi1320_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_MI1320_SOC:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+ init = mi1320_soc_init[mode];
+ break;
+ case SENSOR_OV7670:
+ init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA;
+ break;
+ case SENSOR_PO3130NC:
+ GammaT = po3130_gamma;
+ MatrixT = po3130_matrix;
+ if (mode)
+ init = po3130_initQVGA_data; /* 320x240 */
+ else
+ init = po3130_initVGA_data; /* 640x480 */
+ usb_exchange(gspca_dev, init);
+ init = po3130_rundata;
+ break;
+ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ init = po1200_initVGA_data;
+ break;
+ default:
+/* case SENSOR_POxxxx: */
+ usb_exchange(gspca_dev, poxxxx_init_common);
+ setgamma(gspca_dev);
+ usb_exchange(gspca_dev, poxxxx_init_start_3);
+ if (mode)
+ init = poxxxx_initQVGA;
+ else
+ init = poxxxx_initVGA;
+ usb_exchange(gspca_dev, init);
+ reg_r(gspca_dev, 0x8c, 0x0000, 3);
+ reg_w(gspca_dev, 0xa0,
+ gspca_dev->usb_buf[2] & 1 ? 0 : 1,
+ 0xb35c);
+ msleep(300);
+/*fixme: i2c read 04 and 05*/
+ init = poxxxx_init_end_1;
+ break;
+ }
+ usb_exchange(gspca_dev, init);
+ if (GammaT && MatrixT) {
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
+ put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
+
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ case SENSOR_HV7131R:
+ reg_w(gspca_dev, 0x89, 0x0400, 0x1415);
+ break;
+ case SENSOR_MI1310_SOC:
+ reg_w(gspca_dev, 0x89, 0x058c, 0x0000);
+ break;
+ }
+ msleep(100);
+ }
+ switch (sd->sensor) {
+ case SENSOR_OV7670:
+ reg_w(gspca_dev, 0x87, 0xffff, 0xffff);
+ reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1);
+ reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
+ break;
+ case SENSOR_POxxxx:
+ usb_exchange(gspca_dev, poxxxx_init_end_2);
+ setwb(gspca_dev);
+ msleep(80); /* led on */
+ reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
+ reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+ break;
+ case SENSOR_POxxxx:
+ return;
+ default:
+ if (!(sd->flags & FL_SAMSUNG))
+ reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+ break;
+ }
+ reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
+ reg_w(gspca_dev, 0xa0, 0x09, 0xb003);
+}
+
+/* called on streamoff with alt 0 and on disconnect */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!gspca_dev->present)
+ return;
+/*fixme: is this useful?*/
+ if (sd->sensor == SENSOR_MI1310_SOC)
+ reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
+ else if (!(sd->flags & FL_SAMSUNG))
+ reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
+
+ if (sd->sensor == SENSOR_POxxxx) {
+ reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
+ reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
+ }
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso pkt length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ PDEBUG(D_PACK,
+ "vc032x header packet found len %d", len);
+ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += sd->image_offset;
+ len -= sd->image_offset;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+ if (sd->bridge == BRIDGE_VC0321) {
+ int size, l;
+
+ l = gspca_dev->image_len;
+ size = gspca_dev->frsz;
+ if (len > size - l)
+ len = size - l;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ setgain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ setexposure(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ setbacklight(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ bool has_brightness = false;
+ bool has_contrast = false;
+ bool has_sat = false;
+ bool has_hvflip = false;
+ bool has_freq = false;
+ bool has_backlight = false;
+ bool has_exposure = false;
+ bool has_autogain = false;
+ bool has_gain = false;
+ bool has_sharpness = false;
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ case SENSOR_MI0360:
+ case SENSOR_PO3130NC:
+ break;
+ case SENSOR_MI1310_SOC:
+ case SENSOR_MI1320:
+ case SENSOR_MI1320_SOC:
+ case SENSOR_OV7660:
+ has_hvflip = true;
+ break;
+ case SENSOR_OV7670:
+ has_hvflip = has_freq = true;
+ break;
+ case SENSOR_PO1200:
+ has_hvflip = has_sharpness = true;
+ break;
+ case SENSOR_POxxxx:
+ has_brightness = has_contrast = has_sat = has_backlight =
+ has_exposure = has_autogain = has_gain =
+ has_sharpness = true;
+ break;
+ }
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 8);
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ if (has_sat)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 1, 127, 1, 63);
+ if (has_hvflip) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
+ if (has_sharpness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, -1, 2, 1, -1);
+ if (has_freq)
+ v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
+ if (has_autogain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (has_gain)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 78, 1, 0);
+ if (has_exposure)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 4095, 1, 450);
+ if (has_backlight)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ if (sd->hflip)
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .init_controls = sd_init_controls,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+};
+
+/* -- module initialisation -- */
+#define BF(bridge, flags) \
+ .driver_info = (BRIDGE_ ## bridge << 8) \
+ | (flags)
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)},
+ {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)},
+ {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)},
+ {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)},
+ {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)},
+ {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)},
+ {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)},
+ {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)},
+ {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)},
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
new file mode 100644
index 000000000000..d6890bc37198
--- /dev/null
+++ b/drivers/media/usb/gspca/vicam.c
@@ -0,0 +1,364 @@
+/*
+ * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#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"
+
+#define VICAM_FIRMWARE "vicam/firmware.fw"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(VICAM_FIRMWARE);
+
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+};
+
+/* 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 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)
+ pr_err("control msg req %02X error %d\n", 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
+ */
+static int vicam_read_frame(struct gspca_dev *gspca_dev, u8 *data, int size)
+{
+ int ret, unscaled_height, act_len = 0;
+ u8 *req_data = gspca_dev->usb_buf;
+ s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+ s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+
+ memset(req_data, 0, 16);
+ req_data[0] = gain;
+ 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 (expo < 256) {
+ /* Frame rate maxed out, use partial frame expo time */
+ req_data[4] = 255 - expo;
+ 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] = expo & 0xFF;
+ req_data[7] = expo >> 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) {
+ pr_err("bulk read fail (%d) len %d/%d\n",
+ 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 gspca_dev->usb_buf we take the usb_lock when
+ * performing USB operations using it. In practice we don't really need this
+ * as the cameras controls are only written from the workqueue.
+ */
+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) {
+ pr_err("Couldn't allocate USB buffer\n");
+ goto exit;
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ 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);
+
+ 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,
+ &gspca_dev->dev->dev);
+ if (ret) {
+ pr_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);
+
+ if (gspca_dev->present)
+ vicam_set_camera_power(gspca_dev, 0);
+}
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_EXPOSURE, 0, 2047, 1, 256);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_GAIN, 0, 255, 1, 200);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 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,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c
index 9e3a909e0a00..9e3a909e0a00 100644
--- a/drivers/media/video/gspca/w996Xcf.c
+++ b/drivers/media/usb/gspca/w996Xcf.c
diff --git a/drivers/media/usb/gspca/xirlink_cit.c b/drivers/media/usb/gspca/xirlink_cit.c
new file mode 100644
index 000000000000..d4b23c9bf90c
--- /dev/null
+++ b/drivers/media/usb/gspca/xirlink_cit.c
@@ -0,0 +1,3143 @@
+/*
+ * USB IBM C-It Video Camera driver
+ *
+ * Supports Xirlink C-It Video Camera, IBM PC Camera,
+ * IBM NetCamera and Veo Stingray.
+ *
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This driver is based on earlier work of:
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ * (C) Copyright 1999 Randy Dunlap
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "xirlink-cit"
+
+#include <linux/input.h>
+#include "gspca.h"
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Xirlink C-IT");
+MODULE_LICENSE("GPL");
+
+/* FIXME we should autodetect this */
+static int ibm_netcam_pro;
+module_param(ibm_netcam_pro, int, 0);
+MODULE_PARM_DESC(ibm_netcam_pro,
+ "Use IBM Netcamera Pro init sequences for Model 3 cams");
+
+/* FIXME this should be handled through the V4L2 input selection API */
+static int rca_input;
+module_param(rca_input, int, 0644);
+MODULE_PARM_DESC(rca_input,
+ "Use rca input instead of ccd sensor on Model 3 cams");
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ struct v4l2_ctrl *lighting;
+ u8 model;
+#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
+#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
+#define CIT_MODEL2 2 /* ibmcam driver */
+#define CIT_MODEL3 3
+#define CIT_MODEL4 4
+#define CIT_IBM_NETCAM_PRO 5
+ u8 input_index;
+ u8 button_state;
+ u8 stop_on_control_change;
+ u8 sof_read;
+ u8 sof_len;
+};
+
+static void sd_stop0(struct gspca_dev *gspca_dev);
+
+static const struct v4l2_pix_format cif_yuv_mode[] = {
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format vga_yuv_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model0_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+static const struct v4l2_pix_format model2_mode[] = {
+ {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 + 4,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/*
+ * 01.01.08 - Added for RCA video in support -LO
+ * This struct is used to init the Model3 cam to use the RCA video in port
+ * instead of the CCD sensor.
+ */
+static const u16 rca_initdata[][3] = {
+ {0, 0x0000, 0x010c},
+ {0, 0x0006, 0x012c},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {1, 0x0000, 0x0116},
+ {0, 0x0064, 0x0116},
+ {1, 0x0000, 0x0115},
+ {0, 0x0003, 0x0115},
+ {0, 0x0008, 0x0123},
+ {0, 0x0000, 0x0117},
+ {0, 0x0000, 0x0112},
+ {0, 0x0080, 0x0100},
+ {0, 0x0000, 0x0100},
+ {1, 0x0000, 0x0116},
+ {0, 0x0060, 0x0116},
+ {0, 0x0002, 0x0112},
+ {0, 0x0000, 0x0123},
+ {0, 0x0001, 0x0117},
+ {0, 0x0040, 0x0108},
+ {0, 0x0019, 0x012c},
+ {0, 0x0040, 0x0116},
+ {0, 0x000a, 0x0115},
+ {0, 0x000b, 0x0115},
+ {0, 0x0078, 0x012d},
+ {0, 0x0046, 0x012f},
+ {0, 0xd141, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfea8, 0x0124},
+ {0, 0x0064, 0x0116},
+ {0, 0x0000, 0x0115},
+ {0, 0x0001, 0x0115},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00aa, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f2, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f8, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00fc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f9, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x003c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xffff, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0027, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0019, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0021, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0006, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0045, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002a, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x000e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002b, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00f4, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002c, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0004, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002d, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002e, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x002f, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0003, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0014, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0053, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0000, 0x0101},
+ {0, 0x00a0, 0x0103},
+ {0, 0x0078, 0x0105},
+ {0, 0x0000, 0x010a},
+ {0, 0x0024, 0x010b},
+ {0, 0x0028, 0x0119},
+ {0, 0x0088, 0x011b},
+ {0, 0x0002, 0x011d},
+ {0, 0x0003, 0x011e},
+ {0, 0x0000, 0x0129},
+ {0, 0x00fc, 0x012b},
+ {0, 0x0008, 0x0102},
+ {0, 0x0000, 0x0104},
+ {0, 0x0008, 0x011a},
+ {0, 0x0028, 0x011c},
+ {0, 0x0021, 0x012a},
+ {0, 0x0000, 0x0118},
+ {0, 0x0000, 0x0132},
+ {0, 0x0000, 0x0109},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0031, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x00dc, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0032, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0020, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0001, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0040, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0037, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0030, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0xfff9, 0x0124},
+ {0, 0x0086, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0038, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0008, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0x0000, 0x0127},
+ {0, 0xfff8, 0x0124},
+ {0, 0xfffd, 0x0124},
+ {0, 0xfffa, 0x0124},
+ {0, 0x0003, 0x0111},
+};
+
+/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we
+ do the same for now (testing needed to see if this is really necessary) */
+static const int cit_model1_ntries = 5;
+static const int cit_model1_ntries2 = 2;
+
+static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ int err;
+
+ err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ value, index, NULL, 0, 1000);
+ if (err < 0)
+ pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
+ index, value, err);
+
+ return 0;
+}
+
+static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
+{
+ struct usb_device *udev = gspca_dev->dev;
+ __u8 *buf = gspca_dev->usb_buf;
+ int res;
+
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
+ 0x00, index, buf, 8, 1000);
+ if (res < 0) {
+ pr_err("Failed to read a register (index 0x%04X, error %d)\n",
+ index, res);
+ return res;
+ }
+
+ if (verbose)
+ PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]);
+
+ return 0;
+}
+
+/*
+ * cit_send_FF_04_02()
+ *
+ * This procedure sends magic 3-command prefix to the camera.
+ * The purpose of this prefix is not known.
+ *
+ * History:
+ * 1/2/00 Created.
+ */
+static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x00FF, 0x0127);
+ cit_write_reg(gspca_dev, 0x0004, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_00_04_06(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x0004, 0x0124);
+ cit_write_reg(gspca_dev, 0x0006, 0x0124);
+}
+
+static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+}
+
+static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_send_x_00(gspca_dev, x);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+}
+
+static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+}
+
+static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x)
+{
+ cit_write_reg(gspca_dev, x, 0x0127);
+ cit_write_reg(gspca_dev, 0x0000, 0x0124);
+ cit_write_reg(gspca_dev, 0x0005, 0x0124);
+ cit_write_reg(gspca_dev, 0x0002, 0x0124);
+ cit_write_reg(gspca_dev, 0x0008, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0124);
+}
+
+static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+ cit_send_x_01_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05(gspca_dev, fkey);
+ cit_send_x_00_05_02_08_01(gspca_dev, val);
+ cit_send_x_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05_02_01(gspca_dev, fkey);
+ cit_send_x_00_05(gspca_dev, 0x0089);
+ cit_send_x_00(gspca_dev, fkey);
+ cit_send_00_04_06(gspca_dev);
+ cit_read_reg(gspca_dev, 0x0126, 0);
+ cit_send_FF_04_02(gspca_dev);
+}
+
+static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
+{
+ cit_send_x_01_00_05(gspca_dev, 0x0088);
+ cit_send_x_00_05(gspca_dev, fkey);
+ cit_send_x_00_05_02(gspca_dev, val);
+}
+
+static void cit_model2_Packet2(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x00ff, 0x012d);
+ cit_write_reg(gspca_dev, 0xfea3, 0x0124);
+}
+
+static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x012e);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xc719, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+
+ cit_model2_Packet2(gspca_dev);
+}
+
+/*
+ * cit_model3_Packet1()
+ *
+ * 00_0078_012d
+ * 00_0097_012f
+ * 00_d141_0124
+ * 00_0096_0127
+ * 00_fea8_0124
+*/
+static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, v1, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, v2, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+}
+
+static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val)
+{
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0026, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, val, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0038, 0x012d);
+ cit_write_reg(gspca_dev, 0x0004, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+ sd->model = id->driver_info;
+ if (sd->model == CIT_MODEL3 && ibm_netcam_pro)
+ sd->model = CIT_IBM_NETCAM_PRO;
+
+ cam = &gspca_dev->cam;
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cam->cam_mode = model0_mode;
+ cam->nmodes = ARRAY_SIZE(model0_mode);
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL1:
+ cam->cam_mode = cif_yuv_mode;
+ cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL2:
+ cam->cam_mode = model2_mode + 1; /* no 160x120 */
+ cam->nmodes = 3;
+ break;
+ case CIT_MODEL3:
+ cam->cam_mode = vga_yuv_mode;
+ cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
+ sd->stop_on_control_change = 1;
+ sd->sof_len = 4;
+ break;
+ case CIT_MODEL4:
+ cam->cam_mode = model2_mode;
+ cam->nmodes = ARRAY_SIZE(model2_mode);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cam->cam_mode = vga_yuv_mode;
+ cam->nmodes = 2; /* no 640 x 480 */
+ cam->input_flags = V4L2_IN_ST_VFLIP;
+ sd->stop_on_control_change = 1;
+ sd->sof_len = 4;
+ break;
+ }
+
+ return 0;
+}
+
+static int cit_init_model0(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+ cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */
+ cit_write_reg(gspca_dev, 0x0000, 0x0400);
+ cit_write_reg(gspca_dev, 0x0001, 0x0400);
+ cit_write_reg(gspca_dev, 0x0000, 0x0420);
+ cit_write_reg(gspca_dev, 0x0001, 0x0420);
+ cit_write_reg(gspca_dev, 0x000d, 0x0409);
+ cit_write_reg(gspca_dev, 0x0002, 0x040a);
+ cit_write_reg(gspca_dev, 0x0018, 0x0405);
+ cit_write_reg(gspca_dev, 0x0008, 0x0435);
+ cit_write_reg(gspca_dev, 0x0026, 0x040b);
+ cit_write_reg(gspca_dev, 0x0007, 0x0437);
+ cit_write_reg(gspca_dev, 0x0015, 0x042f);
+ cit_write_reg(gspca_dev, 0x002b, 0x0439);
+ cit_write_reg(gspca_dev, 0x0026, 0x043a);
+ cit_write_reg(gspca_dev, 0x0008, 0x0438);
+ cit_write_reg(gspca_dev, 0x001e, 0x042b);
+ cit_write_reg(gspca_dev, 0x0041, 0x042c);
+
+ return 0;
+}
+
+static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+ cit_read_reg(gspca_dev, 0x128, 1);
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x0000, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0115);
+ cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0079, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_read_reg(gspca_dev, 0x0126, 1);
+
+ cit_model3_Packet1(gspca_dev, 0x0000, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0000, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x000b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x000c, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x000d, 0x003a);
+ cit_model3_Packet1(gspca_dev, 0x000e, 0x0060);
+ cit_model3_Packet1(gspca_dev, 0x000f, 0x0060);
+ cit_model3_Packet1(gspca_dev, 0x0010, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0011, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0012, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0013, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0014, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb);
+ cit_model3_Packet1(gspca_dev, 0x0016, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0017, 0x0037);
+ cit_model3_Packet1(gspca_dev, 0x0018, 0x0036);
+ cit_model3_Packet1(gspca_dev, 0x001e, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1);
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0034);
+ cit_model3_Packet1(gspca_dev, 0x0022, 0x0034);
+ cit_model3_Packet1(gspca_dev, 0x0025, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0028, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x0029, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x002b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x002c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x0032, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x0033, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0037, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x003a, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x003c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x000c);
+ cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb);
+ cit_model3_Packet1(gspca_dev, 0x0042, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0043, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0045, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0048, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff);
+ cit_model3_Packet1(gspca_dev, 0x004f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0050, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0055, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0056, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0057, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0058, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0059, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);
+ cit_model3_Packet1(gspca_dev, 0x005d, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x005e, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x005f, 0x0050);
+ cit_model3_Packet1(gspca_dev, 0x0060, 0x0044);
+ cit_model3_Packet1(gspca_dev, 0x0061, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x006a, 0x007e);
+ cit_model3_Packet1(gspca_dev, 0x006f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0072, 0x001b);
+ cit_model3_Packet1(gspca_dev, 0x0073, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0074, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0075, 0x001b);
+ cit_model3_Packet1(gspca_dev, 0x0076, 0x002a);
+ cit_model3_Packet1(gspca_dev, 0x0077, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x0078, 0x0050);
+ cit_model3_Packet1(gspca_dev, 0x007b, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007c, 0x0011);
+ cit_model3_Packet1(gspca_dev, 0x007d, 0x0024);
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x0043);
+ cit_model3_Packet1(gspca_dev, 0x007f, 0x005a);
+ cit_model3_Packet1(gspca_dev, 0x0084, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0085, 0x0033);
+ cit_model3_Packet1(gspca_dev, 0x0086, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0087, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x0088, 0x0070);
+ cit_model3_Packet1(gspca_dev, 0x008b, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x008f, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0090, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x0091, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0092, 0x005a);
+ cit_model3_Packet1(gspca_dev, 0x0093, 0x0082);
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046);
+ cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8);
+ cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf);
+ cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8);
+ cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000);
+
+ cit_model3_Packet1(gspca_dev, 0x00be, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x0053, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0082, 0x000e);
+ cit_model3_Packet1(gspca_dev, 0x0083, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x0034, 0x003c);
+ cit_model3_Packet1(gspca_dev, 0x006e, 0x0055);
+ cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0063, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0066, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0067, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x006b, 0x0010);
+ cit_model3_Packet1(gspca_dev, 0x005a, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x005b, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0023, 0x0006);
+ cit_model3_Packet1(gspca_dev, 0x0026, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0036, 0x0069);
+ cit_model3_Packet1(gspca_dev, 0x0038, 0x0064);
+ cit_model3_Packet1(gspca_dev, 0x003d, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x003e, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014);
+ cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001);
+
+ 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;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cit_init_model0(gspca_dev);
+ sd_stop0(gspca_dev);
+ break;
+ case CIT_MODEL1:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ break; /* All is done in sd_start */
+ case CIT_IBM_NETCAM_PRO:
+ cit_init_ibm_netcam_pro(gspca_dev);
+ sd_stop0(gspca_dev);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_IBM_NETCAM_PRO:
+ /* No (known) brightness control for these */
+ break;
+ case CIT_MODEL1:
+ /* Model 1: Brightness range 0 - 63 */
+ cit_Packet_Format1(gspca_dev, 0x0031, val);
+ cit_Packet_Format1(gspca_dev, 0x0032, val);
+ cit_Packet_Format1(gspca_dev, 0x0033, val);
+ break;
+ case CIT_MODEL2:
+ /* Model 2: Brightness range 0x60 - 0xee */
+ /* Scale 0 - 63 to 0x60 - 0xee */
+ i = 0x60 + val * 2254 / 1000;
+ cit_model2_Packet1(gspca_dev, 0x001a, i);
+ break;
+ case CIT_MODEL3:
+ /* Model 3: Brightness range 'i' in [0x0C..0x3F] */
+ i = val;
+ if (i < 0x0c)
+ i = 0x0c;
+ cit_model3_Packet1(gspca_dev, 0x0036, i);
+ break;
+ case CIT_MODEL4:
+ /* Model 4: Brightness range 'i' in [0x04..0xb4] */
+ /* Scale 0 - 63 to 0x04 - 0xb4 */
+ i = 0x04 + val * 2794 / 1000;
+ cit_model4_BrightnessPacket(gspca_dev, i);
+ break;
+ }
+
+ return 0;
+}
+
+static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0: {
+ int i;
+ /* gain 0-15, 0-20 -> 0-15 */
+ i = val * 1000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0422);
+ /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
+ i = val * 2000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0423);
+ /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */
+ i = val * 4000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0424);
+ /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
+ i = val * 8000 / 1333;
+ cit_write_reg(gspca_dev, i, 0x0425);
+ break;
+ }
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ /* These models do not have this control. */
+ break;
+ case CIT_MODEL1:
+ {
+ /* Scale 0 - 20 to 15 - 0 */
+ int i, new_contrast = (20 - val) * 1000 / 1333;
+ for (i = 0; i < cit_model1_ntries; i++) {
+ cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
+ cit_send_FF_04_02(gspca_dev);
+ }
+ break;
+ }
+ case CIT_MODEL3:
+ { /* Preset hardware values */
+ static const struct {
+ unsigned short cv1;
+ unsigned short cv2;
+ unsigned short cv3;
+ } cv[7] = {
+ { 0x05, 0x05, 0x0f }, /* Minimum */
+ { 0x04, 0x04, 0x16 },
+ { 0x02, 0x03, 0x16 },
+ { 0x02, 0x08, 0x16 },
+ { 0x01, 0x0c, 0x16 },
+ { 0x01, 0x0e, 0x16 },
+ { 0x01, 0x10, 0x16 } /* Maximum */
+ };
+ int i = val / 3;
+ cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
+ cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
+ cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
+ break;
+ }
+ case CIT_IBM_NETCAM_PRO:
+ cit_model3_Packet1(gspca_dev, 0x005b, val + 1);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ case CIT_IBM_NETCAM_PRO:
+ /* No hue control for these models */
+ break;
+ case CIT_MODEL2:
+ cit_model2_Packet1(gspca_dev, 0x0024, val);
+ /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
+ break;
+ case CIT_MODEL3: {
+ /* Model 3: Brightness range 'i' in [0x05..0x37] */
+ /* TESTME according to the ibmcam driver this does not work */
+ if (0) {
+ /* Scale 0 - 127 to 0x05 - 0x37 */
+ int i = 0x05 + val * 1000 / 2540;
+ cit_model3_Packet1(gspca_dev, 0x007e, i);
+ }
+ break;
+ }
+ case CIT_MODEL4:
+ /* HDG: taken from ibmcam, setting the color gains does not
+ * really belong here.
+ *
+ * I am not sure r/g/b_gain variables exactly control gain
+ * of those channels. Most likely they subtly change some
+ * very internal image processing settings in the camera.
+ * In any case, here is what they do, and feel free to tweak:
+ *
+ * r_gain: seriously affects red gain
+ * g_gain: seriously affects green gain
+ * b_gain: seriously affects blue gain
+ * hue: changes average color from violet (0) to red (0xFF)
+ */
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 160, 0x0127); /* Green gain */
+ cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */
+ cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, val, 0x012d); /* Hue */
+ cit_write_reg(gspca_dev, 0xf545, 0x0124);
+ break;
+ }
+ return 0;
+}
+
+static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ /* These models do not have this control */
+ break;
+ case CIT_MODEL1: {
+ int i;
+ const unsigned short sa[] = {
+ 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
+ break;
+ }
+ case CIT_MODEL3:
+ { /*
+ * "Use a table of magic numbers.
+ * This setting doesn't really change much.
+ * But that's how Windows does it."
+ */
+ static const struct {
+ unsigned short sv1;
+ unsigned short sv2;
+ unsigned short sv3;
+ unsigned short sv4;
+ } sv[7] = {
+ { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */
+ { 0x01, 0x04, 0x05, 0x14 },
+ { 0x02, 0x04, 0x05, 0x14 },
+ { 0x03, 0x04, 0x05, 0x14 },
+ { 0x03, 0x05, 0x05, 0x14 },
+ { 0x03, 0x06, 0x05, 0x14 },
+ { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */
+ };
+ cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
+ cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
+ cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
+ cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * cit_set_lighting()
+ *
+ * Camera model 1:
+ * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
+ *
+ * Camera model 2:
+ * We have 16 levels of lighting, 0 for bright light and up to 15 for
+ * low light. But values above 5 or so are useless because camera is
+ * not really capable to produce anything worth viewing at such light.
+ * This setting may be altered only in certain camera state.
+ *
+ * Low lighting forces slower FPS.
+ *
+ * History:
+ * 1/5/00 Created.
+ * 2/20/00 Added support for Model 2 cameras.
+ */
+static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ case CIT_MODEL1: {
+ int i;
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x0027, val);
+ break;
+ }
+ }
+}
+
+static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ if (val)
+ cit_write_reg(gspca_dev, 0x0020, 0x0115);
+ else
+ cit_write_reg(gspca_dev, 0x0040, 0x0115);
+ break;
+ case CIT_MODEL1:
+ case CIT_MODEL2:
+ case CIT_MODEL3:
+ case CIT_MODEL4:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ }
+}
+
+static int cit_restart_stream(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ cit_write_reg(gspca_dev, 0x0001, 0x0114);
+ /* Fall through */
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+ break;
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ cit_write_reg(gspca_dev, 0x0001, 0x0114);
+ cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
+ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+ /* Clear button events from while we were not streaming */
+ cit_write_reg(gspca_dev, 0x0001, 0x0113);
+ break;
+ }
+
+ sd->sof_read = 0;
+
+ return 0;
+}
+
+static int cit_get_packet_size(struct gspca_dev *gspca_dev)
+{
+ struct usb_host_interface *alt;
+ struct usb_interface *intf;
+
+ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
+ alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
+ if (!alt) {
+ pr_err("Couldn't get altsetting\n");
+ return -EIO;
+ }
+
+ return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+}
+
+/* Calculate the clockdiv giving us max fps given the available bandwidth */
+static int cit_get_clock_div(struct gspca_dev *gspca_dev)
+{
+ int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
+ int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
+ int packet_size;
+
+ packet_size = cit_get_packet_size(gspca_dev);
+ if (packet_size < 0)
+ return packet_size;
+
+ while (clock_div > 3 &&
+ 1000 * packet_size >
+ gspca_dev->width * gspca_dev->height *
+ fps[clock_div - 1] * 3 / 2)
+ clock_div--;
+
+ PDEBUG(D_PROBE,
+ "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
+ packet_size, gspca_dev->width, gspca_dev->height, clock_div,
+ fps[clock_div]);
+
+ return clock_div;
+}
+
+static int cit_start_model0(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
+ cit_write_reg(gspca_dev, 0x0003, 0x0438);
+ cit_write_reg(gspca_dev, 0x001e, 0x042b);
+ cit_write_reg(gspca_dev, 0x0041, 0x042c);
+ cit_write_reg(gspca_dev, 0x0008, 0x0436);
+ cit_write_reg(gspca_dev, 0x0024, 0x0403);
+ cit_write_reg(gspca_dev, 0x002c, 0x0404);
+ cit_write_reg(gspca_dev, 0x0002, 0x0426);
+ cit_write_reg(gspca_dev, 0x0014, 0x0427);
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0004, 0x010b);
+ cit_write_reg(gspca_dev, 0x0001, 0x010a);
+ cit_write_reg(gspca_dev, 0x0010, 0x0102);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0078, 0x0105);
+ break;
+
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0006, 0x010b);
+ cit_write_reg(gspca_dev, 0x0000, 0x010a);
+ cit_write_reg(gspca_dev, 0x0005, 0x0102);
+ cit_write_reg(gspca_dev, 0x00b0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0090, 0x0105);
+ break;
+
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0008, 0x010b);
+ cit_write_reg(gspca_dev, 0x0004, 0x010a);
+ cit_write_reg(gspca_dev, 0x0005, 0x0102);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103);
+ cit_write_reg(gspca_dev, 0x0010, 0x0104);
+ cit_write_reg(gspca_dev, 0x0078, 0x0105);
+ break;
+ }
+
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+ return 0;
+}
+
+static int cit_start_model1(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_read_reg(gspca_dev, 0x0128, 1);
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_write_reg(gspca_dev, 0x01, 0x0108);
+
+ cit_write_reg(gspca_dev, 0x03, 0x0112);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x06, 0x0115);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x44, 0x0116);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x40, 0x0116);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x0e, 0x0115);
+ cit_write_reg(gspca_dev, 0x19, 0x012c);
+
+ cit_Packet_Format1(gspca_dev, 0x00, 0x1e);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x0d);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x09);
+ cit_Packet_Format1(gspca_dev, 0x3b, 0x00);
+ cit_Packet_Format1(gspca_dev, 0x28, 0x22);
+ cit_Packet_Format1(gspca_dev, 0x27, 0x00);
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x2c, 0x00);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x30, 0x14);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x01, 0xe1);
+ cit_PacketFormat2(gspca_dev, 0x02, 0xcd);
+ cit_PacketFormat2(gspca_dev, 0x03, 0xcd);
+ cit_PacketFormat2(gspca_dev, 0x04, 0xfa);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x0a, 0x37);
+ cit_PacketFormat2(gspca_dev, 0x0b, 0xb8);
+ cit_PacketFormat2(gspca_dev, 0x0c, 0xf3);
+ cit_PacketFormat2(gspca_dev, 0x0d, 0xe3);
+ cit_PacketFormat2(gspca_dev, 0x0e, 0x0d);
+ cit_PacketFormat2(gspca_dev, 0x0f, 0xf2);
+ cit_PacketFormat2(gspca_dev, 0x10, 0xd5);
+ cit_PacketFormat2(gspca_dev, 0x11, 0xba);
+ cit_PacketFormat2(gspca_dev, 0x12, 0x53);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ cit_PacketFormat2(gspca_dev, 0x39, 0x02);
+ cit_PacketFormat2(gspca_dev, 0x16, 0x00);
+ cit_PacketFormat2(gspca_dev, 0x17, 0x28);
+ cit_PacketFormat2(gspca_dev, 0x18, 0x7d);
+ cit_PacketFormat2(gspca_dev, 0x19, 0xbe);
+ cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
+ cit_PacketFormat2(gspca_dev, 0x39, 0x00);
+
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x00, 0x18);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x13, 0x18);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x14, 0x06);
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0) {
+ /* This is default brightness */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x31, 0x37);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x32, 0x46);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x33, 0x55);
+ }
+
+ cit_Packet_Format1(gspca_dev, 0x2e, 0x04);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x2d, 0x04);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x29, 0x80);
+ cit_Packet_Format1(gspca_dev, 0x2c, 0x01);
+ cit_Packet_Format1(gspca_dev, 0x30, 0x17);
+ cit_Packet_Format1(gspca_dev, 0x39, 0x08);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x34, 0x00);
+
+ cit_write_reg(gspca_dev, 0x00, 0x0101);
+ cit_write_reg(gspca_dev, 0x00, 0x010a);
+
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_write_reg(gspca_dev, 0x80, 0x0103);
+ cit_write_reg(gspca_dev, 0x60, 0x0105);
+ cit_write_reg(gspca_dev, 0x0c, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x0b, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x00, 0x0129);
+ break;
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0xb0, 0x0103);
+ cit_write_reg(gspca_dev, 0x8f, 0x0105);
+ cit_write_reg(gspca_dev, 0x06, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x0d, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x03, 0x0129);
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0xb0, 0x0103);
+ cit_write_reg(gspca_dev, 0x90, 0x0105);
+ cit_write_reg(gspca_dev, 0x02, 0x010b);
+ cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x05, 0x011d);
+ cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x00, 0x0129);
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0xff, 0x012b);
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0) {
+ /* This is another brightness - don't know why */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x31, 0xc3);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x32, 0xd2);
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x33, 0xe1);
+
+ /* Default contrast */
+ for (i = 0; i < cit_model1_ntries; i++)
+ cit_Packet_Format1(gspca_dev, 0x14, 0x0a);
+
+ /* Default sharpness */
+ for (i = 0; i < cit_model1_ntries2; i++)
+ cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
+
+ /* Default lighting conditions */
+ cit_Packet_Format1(gspca_dev, 0x0027,
+ v4l2_ctrl_g_ctrl(sd->lighting));
+ }
+
+ /* Assorted init */
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x36, 0x0102);
+ cit_write_reg(gspca_dev, 0x1a, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2b, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ case 176: /* 176x144 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x04, 0x0102);
+ cit_write_reg(gspca_dev, 0x02, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2b, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ case 352: /* 352x288 */
+ cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
+ cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x08, 0x0102);
+ cit_write_reg(gspca_dev, 0x01, 0x0104);
+ cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
+ cit_write_reg(gspca_dev, 0x2f, 0x011c);
+ cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+ return 0;
+}
+
+static int cit_start_model2(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int clock_div = 0;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+ cit_write_reg(gspca_dev, 0x0008, 0x012b);
+ cit_write_reg(gspca_dev, 0x0000, 0x0108);
+ cit_write_reg(gspca_dev, 0x0001, 0x0133);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a); /* Unique to this mode */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119); /* Unique to this mode */
+ /* TESTME HDG: this does not seem right
+ (it is 2 for all other resolutions) */
+ sd->sof_len = 10;
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0028, 0x0103); /* Unique to this mode */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ /* case VIDEOSIZE_352x240: */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
+ cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
+ sd->sof_len = 2;
+ break;
+ }
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
+
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0050, 0x0111);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ break;
+ case 320: /* 320x240 */
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x0040, 0x0111);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ break;
+ }
+ cit_write_reg(gspca_dev, 0x009b, 0x010f);
+ cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+
+ /*
+ * Hardware settings, may affect CMOS sensor; not user controls!
+ * -------------------------------------------------------------
+ * 0x0004: no effect
+ * 0x0006: hardware effect
+ * 0x0008: no effect
+ * 0x000a: stops video stream, probably important h/w setting
+ * 0x000c: changes color in hardware manner (not user setting)
+ * 0x0012: changes number of colors (does not affect speed)
+ * 0x002a: no effect
+ * 0x002c: hardware setting (related to scan lines)
+ * 0x002e: stops video stream, probably important h/w setting
+ */
+ cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
+ cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
+ cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
+ cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
+ cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
+ cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);
+
+ /*
+ * Function 0x0030 pops up all over the place. Apparently
+ * it is a hardware control register, with every bit assigned to
+ * do something.
+ */
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);
+
+ /*
+ * Magic control of CMOS sensor. Only lower values like
+ * 0-3 work, and picture shifts left or right. Don't change.
+ */
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+ clock_div = 6;
+ break;
+ case 320: /* 320x240 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
+ clock_div = 8;
+ break;
+ /* case VIDEOSIZE_352x240: */
+ /* This mode doesn't work as Windows programs it; changed to work */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
+ clock_div = 10;
+ break;
+ case 352: /* 352x288 */
+ cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
+ cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
+ cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
+ clock_div = 16;
+ break;
+ }
+
+ /* TESTME These are handled through controls
+ KEEP until someone can test leaving this out is ok */
+ if (0)
+ cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);
+
+ /*
+ * We have our own frame rate setting varying from 0 (slowest) to 6
+ * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
+ # where 0 is also the slowest setting. However for all practical
+ # reasons high settings make no sense because USB is not fast enough
+ # to support high FPS. Be aware that the picture datastream will be
+ # severely disrupted if you ask for frame rate faster than allowed
+ # for the video size - see below:
+ *
+ * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
+ * -----------------------------------------------------------------
+ * 176x144: [6..31]
+ * 320x240: [8..31]
+ * 352x240: [10..31]
+ * 352x288: [16..31] I have to raise lower threshold for stability...
+ *
+ * As usual, slower FPS provides better sensitivity.
+ */
+ cit_model2_Packet1(gspca_dev, 0x001c, clock_div);
+
+ /*
+ * This setting does not visibly affect pictures; left it here
+ * because it was present in Windows USB data stream. This function
+ * does not allow arbitrary values and apparently is a bit mask, to
+ * be activated only at appropriate time. Don't change it randomly!
+ */
+ switch (gspca_dev->width) {
+ case 176: /* 176x144 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
+ break;
+ case 320: /* 320x240 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
+ break;
+ /* case VIDEOSIZE_352x240: */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
+ break;
+ case 352: /* 352x288 */
+ cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
+ break;
+ }
+
+ cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
+ /* model2 cannot change the backlight compensation while streaming */
+ v4l2_ctrl_grab(sd->lighting, true);
+
+ /* color balance rg2 */
+ cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
+ /* saturation */
+ cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
+ /* color balance yb */
+ cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);
+
+ /* Hardware control command */
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+ return 0;
+}
+
+static int cit_start_model3(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int i, clock_div = 0;
+
+ /* HDG not in ibmcam driver, added to see if it helps with
+ auto-detecting between model3 and ibm netcamera pro */
+ cit_read_reg(gspca_dev, 0x128, 1);
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0112);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ cit_write_reg(gspca_dev, 0x0002, 0x0115);
+ cit_write_reg(gspca_dev, 0x0003, 0x0115);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x000b, 0x0115);
+
+ /* TESTME HDG not in ibmcam driver, added to see if it helps with
+ auto-detecting between model3 and ibm netcamera pro */
+ if (0) {
+ cit_write_reg(gspca_dev, 0x0078, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0079, 0x012d);
+ cit_write_reg(gspca_dev, 0x00ff, 0x0130);
+ cit_write_reg(gspca_dev, 0xcd41, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_read_reg(gspca_dev, 0x0126, 1);
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x000a, 0x0040);
+ cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6);
+ cit_model3_Packet1(gspca_dev, 0x000c, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x000d, 0x0020);
+ cit_model3_Packet1(gspca_dev, 0x000e, 0x0033);
+ cit_model3_Packet1(gspca_dev, 0x000f, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x0010, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0011, 0x0070);
+ cit_model3_Packet1(gspca_dev, 0x0012, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x0013, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0014, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0015, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0016, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0017, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0018, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3);
+ cit_model3_Packet1(gspca_dev, 0x0020, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0028, 0x0010);
+ cit_model3_Packet1(gspca_dev, 0x0029, 0x0054);
+ cit_model3_Packet1(gspca_dev, 0x002a, 0x0013);
+ cit_model3_Packet1(gspca_dev, 0x002b, 0x0007);
+ cit_model3_Packet1(gspca_dev, 0x002d, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x002e, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0031, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0032, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0033, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0034, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0035, 0x0038);
+ cit_model3_Packet1(gspca_dev, 0x003a, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x003c, 0x001e);
+ cit_model3_Packet1(gspca_dev, 0x003f, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0041, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x003f);
+ cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0050, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x0052, 0x001a);
+ cit_model3_Packet1(gspca_dev, 0x0053, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x005a, 0x006b);
+ cit_model3_Packet1(gspca_dev, 0x005d, 0x001e);
+ cit_model3_Packet1(gspca_dev, 0x005e, 0x0030);
+ cit_model3_Packet1(gspca_dev, 0x005f, 0x0041);
+ cit_model3_Packet1(gspca_dev, 0x0064, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0065, 0x0015);
+ cit_model3_Packet1(gspca_dev, 0x0068, 0x000f);
+ cit_model3_Packet1(gspca_dev, 0x0079, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007a, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x007c, 0x003f);
+ cit_model3_Packet1(gspca_dev, 0x0082, 0x000f);
+ cit_model3_Packet1(gspca_dev, 0x0085, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0099, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x009b, 0x0023);
+ cit_model3_Packet1(gspca_dev, 0x009c, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x009d, 0x0096);
+ cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
+ cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
+
+ switch (gspca_dev->width) {
+ case 160:
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x00a9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0016, 0x011b);
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0018, 0x0102);
+ cit_write_reg(gspca_dev, 0x0004, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x0028, 0x011c);
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_write_reg(gspca_dev, 0x0000, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ clock_div = 3;
+ break;
+ case 320:
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */
+ cit_write_reg(gspca_dev, 0x0000, 0x011e);
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ /* 4 commands from 160x120 skipped */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0006, 0x011b);
+ cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0010, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x003f, 0x011c);
+ cit_write_reg(gspca_dev, 0x001c, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ clock_div = 5;
+ break;
+ case 640:
+ cit_write_reg(gspca_dev, 0x00f0, 0x0105);
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */
+ cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */
+ cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+ cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
+ cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, 0x0040, 0x0101);
+ cit_write_reg(gspca_dev, 0x0040, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
+ clock_div = 7;
+ break;
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); /* Hue */
+ cit_model3_Packet1(gspca_dev, 0x0036, 0x0011); /* Brightness */
+ cit_model3_Packet1(gspca_dev, 0x0060, 0x0002); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0061, 0x0004); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0063, 0x0014); /* Sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0067, 0x0001); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x005b, 0x000c); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); /* Contrast */
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x002c, 0x0003); /* Was 1, broke 640x480 */
+ cit_model3_Packet1(gspca_dev, 0x002f, 0x002a);
+ cit_model3_Packet1(gspca_dev, 0x0030, 0x0029);
+ cit_model3_Packet1(gspca_dev, 0x0037, 0x0002);
+ cit_model3_Packet1(gspca_dev, 0x0038, 0x0059);
+ cit_model3_Packet1(gspca_dev, 0x003d, 0x002e);
+ cit_model3_Packet1(gspca_dev, 0x003e, 0x0028);
+ cit_model3_Packet1(gspca_dev, 0x0078, 0x0005);
+ cit_model3_Packet1(gspca_dev, 0x007b, 0x0011);
+ cit_model3_Packet1(gspca_dev, 0x007d, 0x004b);
+ cit_model3_Packet1(gspca_dev, 0x007f, 0x0022);
+ cit_model3_Packet1(gspca_dev, 0x0080, 0x000c);
+ cit_model3_Packet1(gspca_dev, 0x0081, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd);
+ cit_model3_Packet1(gspca_dev, 0x0086, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x0087, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);
+ cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
+ cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
+
+ /* FIXME we should probably use cit_get_clock_div() here (in
+ combination with isoc negotiation using the programmable isoc size)
+ like with the IBM netcam pro). */
+ cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
+
+ switch (gspca_dev->width) {
+ case 160:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x000a);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+ break;
+ case 320:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000b);
+ break;
+ case 640:
+ cit_model3_Packet1(gspca_dev, 0x001f, 0x0002); /* !Same */
+ cit_model3_Packet1(gspca_dev, 0x0039, 0x003e); /* !Same */
+ cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
+ cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
+ break;
+ }
+
+/* if (sd->input_index) { */
+ if (rca_input) {
+ for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+ if (rca_initdata[i][0])
+ cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+ else
+ cit_write_reg(gspca_dev, rca_initdata[i][1],
+ rca_initdata[i][2]);
+ }
+ }
+
+ return 0;
+}
+
+static int cit_start_model4(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00bc, 0x012c);
+ cit_write_reg(gspca_dev, 0x0080, 0x012b);
+ cit_write_reg(gspca_dev, 0x0000, 0x0108);
+ cit_write_reg(gspca_dev, 0x0001, 0x0133);
+ cit_write_reg(gspca_dev, 0x009b, 0x010f);
+ cit_write_reg(gspca_dev, 0x00bb, 0x010f);
+ cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
+ cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);
+
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0004, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00fb, 0x012e);
+ cit_write_reg(gspca_dev, 0x0000, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x000c, 0x0127);
+ cit_write_reg(gspca_dev, 0x0009, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0012, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0008, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x002a, 0x012d);
+ cit_write_reg(gspca_dev, 0x0000, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
+
+ switch (gspca_dev->width) {
+ case 128: /* 128x96 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000a, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0031, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x012d);
+ cit_write_reg(gspca_dev, 0x0078, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000b, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0025, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0048, 0x0127);
+ cit_write_reg(gspca_dev, 0x0035, 0x012e);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0048, 0x012d);
+ cit_write_reg(gspca_dev, 0x0090, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 176: /* 176x144 */
+ cit_write_reg(gspca_dev, 0x0038, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x00b9, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x002c, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0024, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0007, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0001, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005e, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0049, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00c7, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0028, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x002a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x012d);
+ cit_write_reg(gspca_dev, 0x006d, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0001, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ /* TESTME HDG: this does not seem right
+ (it is 2 for all other resolutions) */
+ sd->sof_len = 10;
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00d0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x0028, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x001e, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x000a, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005a, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0043, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00eb, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0031, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0017, 0x012d);
+ cit_write_reg(gspca_dev, 0x0078, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ case 352: /* 352x288 */
+ cit_write_reg(gspca_dev, 0x0070, 0x0119);
+ cit_write_reg(gspca_dev, 0x00c0, 0x0111);
+ cit_write_reg(gspca_dev, 0x0039, 0x010a);
+ cit_write_reg(gspca_dev, 0x0001, 0x0102);
+ cit_write_reg(gspca_dev, 0x002c, 0x0103);
+ cit_write_reg(gspca_dev, 0x0000, 0x0104);
+ cit_write_reg(gspca_dev, 0x0024, 0x0105);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0016, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0006, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0014, 0x012d);
+ cit_write_reg(gspca_dev, 0x0002, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012e);
+ cit_write_reg(gspca_dev, 0x001a, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
+ cit_write_reg(gspca_dev, 0x005e, 0x012d);
+ cit_write_reg(gspca_dev, 0x9545, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0127);
+ cit_write_reg(gspca_dev, 0x0018, 0x012e);
+ cit_write_reg(gspca_dev, 0x0049, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012f);
+ cit_write_reg(gspca_dev, 0xd055, 0x0124);
+ cit_write_reg(gspca_dev, 0x001c, 0x0127);
+ cit_write_reg(gspca_dev, 0x00cf, 0x012e);
+ cit_write_reg(gspca_dev, 0xaa28, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x0032, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0x00aa, 0x0130);
+ cit_write_reg(gspca_dev, 0x82a8, 0x0124);
+ cit_write_reg(gspca_dev, 0x0036, 0x012d);
+ cit_write_reg(gspca_dev, 0x0008, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0xfffa, 0x0124);
+ cit_write_reg(gspca_dev, 0x00aa, 0x012d);
+ cit_write_reg(gspca_dev, 0x001e, 0x012f);
+ cit_write_reg(gspca_dev, 0xd141, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x0127);
+ cit_write_reg(gspca_dev, 0x0013, 0x012e);
+ cit_write_reg(gspca_dev, 0x0025, 0x0130);
+ cit_write_reg(gspca_dev, 0x8a28, 0x0124);
+ cit_write_reg(gspca_dev, 0x0010, 0x012d);
+ cit_write_reg(gspca_dev, 0x0048, 0x012f);
+ cit_write_reg(gspca_dev, 0xd145, 0x0124);
+ cit_write_reg(gspca_dev, 0x0000, 0x0127);
+ cit_write_reg(gspca_dev, 0xfea8, 0x0124);
+ sd->sof_len = 2;
+ break;
+ }
+
+ cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);
+
+ return 0;
+}
+
+static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
+{
+ const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+ int i, clock_div;
+
+ clock_div = cit_get_clock_div(gspca_dev);
+ if (clock_div < 0)
+ return clock_div;
+
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0100);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ /* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0123);
+ cit_write_reg(gspca_dev, 0x0001, 0x0117);
+ cit_write_reg(gspca_dev, 0x0040, 0x0108);
+ cit_write_reg(gspca_dev, 0x0019, 0x012c);
+ cit_write_reg(gspca_dev, 0x0060, 0x0116);
+ /* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */
+
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
+
+ cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */
+ cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
+ cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
+ cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
+ cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
+ cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
+
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ cit_write_reg(gspca_dev, 0x0024, 0x010b);
+ cit_write_reg(gspca_dev, 0x0089, 0x0119);
+ cit_write_reg(gspca_dev, 0x000a, 0x011b);
+ cit_write_reg(gspca_dev, 0x0003, 0x011e);
+ cit_write_reg(gspca_dev, 0x0007, 0x0104);
+ cit_write_reg(gspca_dev, 0x0009, 0x011a);
+ cit_write_reg(gspca_dev, 0x008b, 0x011c);
+ cit_write_reg(gspca_dev, 0x0008, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ break;
+ case 320: /* 320x240 */
+ cit_write_reg(gspca_dev, 0x0028, 0x010b);
+ cit_write_reg(gspca_dev, 0x00d9, 0x0119);
+ cit_write_reg(gspca_dev, 0x0006, 0x011b);
+ cit_write_reg(gspca_dev, 0x0000, 0x011e);
+ cit_write_reg(gspca_dev, 0x000e, 0x0104);
+ cit_write_reg(gspca_dev, 0x0004, 0x011a);
+ cit_write_reg(gspca_dev, 0x003f, 0x011c);
+ cit_write_reg(gspca_dev, 0x000c, 0x0118);
+ cit_write_reg(gspca_dev, 0x0000, 0x0132);
+ break;
+ }
+
+ cit_model3_Packet1(gspca_dev, 0x0019, 0x0031);
+ cit_model3_Packet1(gspca_dev, 0x001a, 0x0003);
+ cit_model3_Packet1(gspca_dev, 0x001b, 0x0038);
+ cit_model3_Packet1(gspca_dev, 0x001c, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0024, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0027, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x002a, 0x0004);
+ cit_model3_Packet1(gspca_dev, 0x0035, 0x000b);
+ cit_model3_Packet1(gspca_dev, 0x003f, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x0044, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x0054, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001);
+ cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000);
+ cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0);
+
+ cit_write_reg(gspca_dev, compression, 0x0109);
+ cit_write_reg(gspca_dev, clock_div, 0x0111);
+
+/* if (sd->input_index) { */
+ if (rca_input) {
+ for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
+ if (rca_initdata[i][0])
+ cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
+ else
+ cit_write_reg(gspca_dev, rca_initdata[i][1],
+ rca_initdata[i][2]);
+ }
+ }
+
+ return 0;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int packet_size;
+
+ packet_size = cit_get_packet_size(gspca_dev);
+ if (packet_size < 0)
+ return packet_size;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ cit_start_model0(gspca_dev);
+ break;
+ case CIT_MODEL1:
+ cit_start_model1(gspca_dev);
+ break;
+ case CIT_MODEL2:
+ cit_start_model2(gspca_dev);
+ break;
+ case CIT_MODEL3:
+ cit_start_model3(gspca_dev);
+ break;
+ case CIT_MODEL4:
+ cit_start_model4(gspca_dev);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cit_start_ibm_netcam_pro(gspca_dev);
+ break;
+ }
+
+ /* Program max isoc packet size */
+ cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
+ cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
+
+ cit_restart_stream(gspca_dev);
+
+ return 0;
+}
+
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+ struct usb_host_interface *alt;
+ int max_packet_size;
+
+ switch (gspca_dev->width) {
+ case 160:
+ max_packet_size = 450;
+ break;
+ case 176:
+ max_packet_size = 600;
+ break;
+ default:
+ max_packet_size = 1022;
+ break;
+ }
+
+ /* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+ return 0;
+}
+
+static int sd_isoc_nego(struct gspca_dev *gspca_dev)
+{
+ int ret, packet_size, min_packet_size;
+ struct usb_host_interface *alt;
+
+ switch (gspca_dev->width) {
+ case 160:
+ min_packet_size = 200;
+ break;
+ case 176:
+ min_packet_size = 266;
+ break;
+ default:
+ min_packet_size = 400;
+ break;
+ }
+
+ alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
+ packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+ if (packet_size <= min_packet_size)
+ return -EIO;
+
+ packet_size -= 100;
+ if (packet_size < min_packet_size)
+ packet_size = min_packet_size;
+ alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+ if (ret < 0)
+ pr_err("set alt 1 err %d\n", ret);
+
+ return ret;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ cit_write_reg(gspca_dev, 0x0000, 0x010c);
+}
+
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (!gspca_dev->present)
+ return;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ /* HDG windows does this, but it causes the cams autogain to
+ restart from a gain of 0, which does not look good when
+ changing resolutions. */
+ /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+ cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */
+ break;
+ case CIT_MODEL1:
+ cit_send_FF_04_02(gspca_dev);
+ cit_read_reg(gspca_dev, 0x0100, 0);
+ cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
+ break;
+ case CIT_MODEL2:
+ v4l2_ctrl_grab(sd->lighting, false);
+ /* Fall through! */
+ case CIT_MODEL4:
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
+
+ cit_write_reg(gspca_dev, 0x0080, 0x0100); /* LED Off */
+ cit_write_reg(gspca_dev, 0x0020, 0x0111);
+ cit_write_reg(gspca_dev, 0x00a0, 0x0111);
+
+ cit_model2_Packet1(gspca_dev, 0x0030, 0x0002);
+
+ cit_write_reg(gspca_dev, 0x0020, 0x0111);
+ cit_write_reg(gspca_dev, 0x0000, 0x0112);
+ break;
+ case CIT_MODEL3:
+ cit_write_reg(gspca_dev, 0x0006, 0x012c);
+ cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
+ cit_read_reg(gspca_dev, 0x0116, 0);
+ cit_write_reg(gspca_dev, 0x0064, 0x0116);
+ cit_read_reg(gspca_dev, 0x0115, 0);
+ cit_write_reg(gspca_dev, 0x0003, 0x0115);
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0000, 0x0112);
+ cit_write_reg(gspca_dev, 0x0080, 0x0100);
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff);
+ cit_write_reg(gspca_dev, 0x0006, 0x012c);
+ cit_write_reg(gspca_dev, 0x0000, 0x0116);
+ /* HDG windows does this, but I cannot get the camera
+ to restart with this without redoing the entire init
+ sequence which makes switching modes really slow */
+ /* cit_write_reg(gspca_dev, 0x0006, 0x0115); */
+ cit_write_reg(gspca_dev, 0x0008, 0x0123);
+ cit_write_reg(gspca_dev, 0x0000, 0x0117);
+ cit_write_reg(gspca_dev, 0x0003, 0x0133);
+ cit_write_reg(gspca_dev, 0x0000, 0x0111);
+ /* HDG windows does this, but I get a green picture when
+ restarting the stream after this */
+ /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
+ cit_write_reg(gspca_dev, 0x00c0, 0x0100);
+ break;
+ }
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ /* If the last button state is pressed, release it now! */
+ if (sd->button_state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = 0;
+ }
+#endif
+}
+
+static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 byte3 = 0, byte4 = 0;
+ int i;
+
+ switch (sd->model) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ switch (gspca_dev->width) {
+ case 160: /* 160x120 */
+ byte3 = 0x02;
+ byte4 = 0x0a;
+ break;
+ case 176: /* 176x144 */
+ byte3 = 0x02;
+ byte4 = 0x0e;
+ break;
+ case 320: /* 320x240 */
+ byte3 = 0x02;
+ byte4 = 0x08;
+ break;
+ case 352: /* 352x288 */
+ byte3 = 0x02;
+ byte4 = 0x00;
+ break;
+ case 640:
+ byte3 = 0x03;
+ byte4 = 0x08;
+ break;
+ }
+
+ /* These have a different byte3 */
+ if (sd->model <= CIT_MODEL1)
+ byte3 = 0x00;
+
+ for (i = 0; i < len; i++) {
+ /* For this model the SOF always starts at offset 0
+ so no need to search the entire frame */
+ if (sd->model == CIT_MODEL0 && sd->sof_read != i)
+ break;
+
+ switch (sd->sof_read) {
+ case 0:
+ if (data[i] == 0x00)
+ sd->sof_read++;
+ break;
+ case 1:
+ if (data[i] == 0xff)
+ sd->sof_read++;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ case 2:
+ if (data[i] == byte3)
+ sd->sof_read++;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ case 3:
+ if (data[i] == byte4) {
+ sd->sof_read = 0;
+ return data + i + (sd->sof_len - 3);
+ }
+ if (byte3 == 0x00 && data[i] == 0xff)
+ sd->sof_read = 2;
+ else if (data[i] == 0x00)
+ sd->sof_read = 1;
+ else
+ sd->sof_read = 0;
+ break;
+ }
+ }
+ break;
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ /* TESTME we need to find a longer sof signature to avoid
+ false positives */
+ for (i = 0; i < len; i++) {
+ switch (sd->sof_read) {
+ case 0:
+ if (data[i] == 0x00)
+ sd->sof_read++;
+ break;
+ case 1:
+ sd->sof_read = 0;
+ if (data[i] == 0xff) {
+ if (i >= 4)
+ PDEBUG(D_FRAM,
+ "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n",
+ i - 1,
+ data[i - 4],
+ data[i - 3],
+ data[i],
+ data[i + 1],
+ data[i + 2]);
+ else
+ PDEBUG(D_FRAM,
+ "header found at offset: %d: 00 %02x %02x %02x\n",
+ i - 1,
+ data[i],
+ data[i + 1],
+ data[i + 2]);
+ return data + i + (sd->sof_len - 1);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+ sof = cit_find_sof(gspca_dev, data, len);
+ if (sof) {
+ int n;
+
+ /* finish decoding current frame */
+ n = sof - data;
+ if (n > sd->sof_len)
+ n -= sd->sof_len;
+ else
+ n = 0;
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
+ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ len -= sof - data;
+ data = sof;
+ }
+
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static void cit_check_button(struct gspca_dev *gspca_dev)
+{
+ int new_button_state;
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (sd->model) {
+ case CIT_MODEL3:
+ case CIT_IBM_NETCAM_PRO:
+ break;
+ default: /* TEST ME unknown if this works on other models too */
+ return;
+ }
+
+ /* Read the button state */
+ cit_read_reg(gspca_dev, 0x0113, 0);
+ new_button_state = !gspca_dev->usb_buf[0];
+
+ /* Tell the cam we've seen the button press, notice that this
+ is a nop (iow the cam keeps reporting pressed) until the
+ button is actually released. */
+ if (new_button_state)
+ cit_write_reg(gspca_dev, 0x01, 0x0113);
+
+ if (sd->button_state != new_button_state) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA,
+ new_button_state);
+ input_sync(gspca_dev->input_dev);
+ sd->button_state = new_button_state;
+ }
+}
+#endif
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ if (sd->stop_on_control_change)
+ sd_stopN(gspca_dev);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ cit_set_brightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ cit_set_contrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HUE:
+ cit_set_hue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ cit_set_hflip(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ cit_set_sharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BACKLIGHT_COMPENSATION:
+ cit_set_lighting(gspca_dev, ctrl->val);
+ break;
+ }
+ if (sd->stop_on_control_change)
+ cit_restart_stream(gspca_dev);
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ bool has_brightness;
+ bool has_contrast;
+ bool has_hue;
+ bool has_sharpness;
+ bool has_lighting;
+ bool has_hflip;
+
+ has_brightness = has_contrast = has_hue =
+ has_sharpness = has_hflip = has_lighting = false;
+ switch (sd->model) {
+ case CIT_MODEL0:
+ has_contrast = has_hflip = true;
+ break;
+ case CIT_MODEL1:
+ has_brightness = has_contrast =
+ has_sharpness = has_lighting = true;
+ break;
+ case CIT_MODEL2:
+ has_brightness = has_hue = has_lighting = true;
+ break;
+ case CIT_MODEL3:
+ has_brightness = has_contrast = has_sharpness = true;
+ break;
+ case CIT_MODEL4:
+ has_brightness = has_hue = true;
+ break;
+ case CIT_IBM_NETCAM_PRO:
+ has_brightness = has_hue =
+ has_sharpness = has_hflip = has_lighting = true;
+ break;
+ }
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 5);
+ if (has_brightness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 63, 1, 32);
+ if (has_contrast)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 20, 1, 10);
+ if (has_hue)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, 0, 127, 1, 63);
+ if (has_sharpness)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 6, 1, 3);
+ if (has_lighting)
+ sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1);
+ if (has_hflip)
+ v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .dq_callback = cit_check_button,
+ .other_input = 1,
+#endif
+};
+
+static const struct sd_desc sd_desc_isoc_nego = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .isoc_init = sd_isoc_init,
+ .isoc_nego = sd_isoc_nego,
+ .stopN = sd_stopN,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .dq_callback = cit_check_button,
+ .other_input = 1,
+#endif
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+ { USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 },
+ { USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+ { USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
+ { USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ const struct sd_desc *desc = &sd_desc;
+
+ switch (id->driver_info) {
+ case CIT_MODEL0:
+ case CIT_MODEL1:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
+ return -ENODEV;
+ break;
+ case CIT_MODEL2:
+ case CIT_MODEL4:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+ break;
+ case CIT_MODEL3:
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+ /* FIXME this likely applies to all model3 cams and probably
+ to other models too. */
+ if (ibm_netcam_pro)
+ desc = &sd_desc_isoc_nego;
+ break;
+ }
+
+ return gspca_dev_probe2(intf, id, 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,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/usb/gspca/zc3xx-reg.h
index a1bd94e8ce52..a1bd94e8ce52 100644
--- a/drivers/media/video/gspca/zc3xx-reg.h
+++ b/drivers/media/usb/gspca/zc3xx-reg.h
diff --git a/drivers/media/usb/gspca/zc3xx.c b/drivers/media/usb/gspca/zc3xx.c
new file mode 100644
index 000000000000..77c57755e7b4
--- /dev/null
+++ b/drivers/media/usb/gspca/zc3xx.c
@@ -0,0 +1,7021 @@
+/*
+ * Z-Star/Vimicro zc301/zc302p/vc30x driver
+ *
+ * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/input.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Serge A. Suchkov <Serge.A.S@tochka.ru>");
+MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+static int force_sensor = -1;
+
+#define REG08_DEF 3 /* default JPEG compression (75%) */
+#include "zc3xx-reg.h"
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ struct { /* gamma/brightness/contrast control cluster */
+ struct v4l2_ctrl *gamma;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ };
+ struct { /* autogain/exposure control cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_ctrl *plfreq;
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *jpegqual;
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u8 reg08; /* webcam compression quality */
+
+ u8 bridge;
+ u8 sensor; /* Type of image sensor chip */
+ u16 chip_revision;
+
+ u8 jpeg_hdr[JPEG_HDR_SZ];
+};
+enum bridges {
+ BRIDGE_ZC301,
+ BRIDGE_ZC303,
+};
+enum sensors {
+ SENSOR_ADCM2700,
+ SENSOR_CS2102,
+ SENSOR_CS2102K,
+ SENSOR_GC0303,
+ SENSOR_GC0305,
+ SENSOR_HDCS2020,
+ SENSOR_HV7131B,
+ SENSOR_HV7131R,
+ SENSOR_ICM105A,
+ SENSOR_MC501CB,
+ SENSOR_MT9V111_1, /* (mi360soc) zc301 */
+ SENSOR_MT9V111_3, /* (mi360soc) zc303 */
+ SENSOR_OV7620, /* OV7648 - same values */
+ SENSOR_OV7630C,
+ SENSOR_PAS106,
+ SENSOR_PAS202B,
+ SENSOR_PB0330,
+ SENSOR_PO2030,
+ SENSOR_TAS5130C,
+ SENSOR_MAX
+};
+
+static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format broken_vga_mode[] = {
+ {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 232 * 4 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 472 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 1},
+ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
+};
+
+/*
+ * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest
+ * quality setting is not usable as USB 1 does not have enough bandwidth.
+ */
+static u8 jpeg_qual[] = {50, 75, 87, /* 94 */};
+
+/* usb exchanges */
+struct usb_action {
+ u8 req;
+ u8 val;
+ u16 idx;
+};
+
+static const struct usb_action adcm2700_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
+ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
+ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
+ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
+ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
+ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
+ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
+ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
+ {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */
+ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
+ {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */
+ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
+/*mswin+*/
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xaa, 0xfe, 0x0002},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xaa, 0xb4, 0xcd37},
+ {0xaa, 0xa4, 0x0004},
+ {0xaa, 0xa8, 0x0007},
+ {0xaa, 0xac, 0x0004},
+/*mswin-*/
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
+ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action adcm2700_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
+ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
+ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
+ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
+ {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */
+ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
+ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
+ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
+ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
+ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
+ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
+ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
+ {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */
+ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
+ {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */
+ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
+ /*******/
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
+ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
+ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action adcm2700_50HZ[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */
+ {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */
+ {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */
+ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
+ {}
+};
+static const struct usb_action adcm2700_60HZ[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
+ {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */
+ {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */
+ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
+ {}
+};
+static const struct usb_action adcm2700_NoFliker[] = {
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
+ {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */
+ {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+};
+static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+ {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+ {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+ {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x02, 0x0008},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x12, 0x0089},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x00e9},
+ {0xaa, 0x20, 0x0000},
+ {0xaa, 0x22, 0x0000},
+ {0xaa, 0x0b, 0x0004},
+ {0xaa, 0x30, 0x0030},
+ {0xaa, 0x31, 0x0030},
+ {0xaa, 0x32, 0x0030},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x10, 0x01ae},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+ {}
+};
+
+static const struct usb_action cs2102_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
+ {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
+ {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
+ {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x02, 0x0008},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x11, 0x0001},
+ {0xaa, 0x12, 0x0087},
+ {0xaa, 0x13, 0x0001},
+ {0xaa, 0x14, 0x00e7},
+ {0xaa, 0x20, 0x0000},
+ {0xaa, 0x22, 0x0000},
+ {0xaa, 0x0b, 0x0004},
+ {0xaa, 0x30, 0x0030},
+ {0xaa, 0x31, 0x0030},
+ {0xaa, 0x32, 0x0030},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+ {}
+};
+static const struct usb_action cs2102_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0090},
+ {0xaa, 0x21, 0x00dd},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x00c8},
+ {0xaa, 0x21, 0x0068},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x0055},
+ {0xaa, 0x25, 0x00cc},
+ {0xaa, 0x21, 0x003f},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00aa},
+ {0xaa, 0x25, 0x00e6},
+ {0xaa, 0x21, 0x003f},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0001},
+ {0xaa, 0x24, 0x005f},
+ {0xaa, 0x25, 0x0000},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action cs2102_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00af},
+ {0xaa, 0x25, 0x0080},
+ {0xaa, 0x21, 0x0001},
+ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+/* CS2102_KOCOM */
+static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x01, 0x01b1},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {}
+};
+
+static const struct usb_action cs2102K_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+/*fixme: next sequence = i2c exchanges*/
+ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x01, 0x01b1},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+/*fixme:what does the next sequence?*/
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {}
+};
+
+static const struct usb_action gc0305_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+ {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */
+ {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
+ {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+ {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
+ {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
+ {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
+ {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
+ {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
+ {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
+ {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
+ {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
+ {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */
+ {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
+ {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+ {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+ {}
+};
+static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
+ {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+ {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
+ {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
+ {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
+ {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
+ {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
+ {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
+ {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
+ {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
+ {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
+ {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
+ {}
+};
+static const struct usb_action gc0305_50HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */
+ {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ /* win: 01,92,10 */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */
+ /* win: 01,97,ec */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc *
+ * if 640x480 */
+ {}
+};
+static const struct usb_action gc0305_60HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
+ {}
+};
+
+static const struct usb_action gc0305_NoFliker[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
+ {}
+};
+
+static const struct usb_action hdcs2020_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x1c, 0x0000},
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0006},
+ {0xaa, 0x0c, 0x007b},
+ {0xaa, 0x0d, 0x00a7},
+ {0xaa, 0x03, 0x00fb},
+ {0xaa, 0x05, 0x0000},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x09, 0x0008},
+
+ {0xaa, 0x0f, 0x0018}, /* set sensor gain */
+ {0xaa, 0x10, 0x0018},
+ {0xaa, 0x11, 0x0018},
+ {0xaa, 0x12, 0x0018},
+
+ {0xaa, 0x15, 0x004e},
+ {0xaa, 0x1c, 0x0004},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+
+ {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xed, ZC3XX_R10B_RGB01},
+ {0xa0, 0xed, ZC3XX_R10C_RGB02},
+ {0xa0, 0xed, ZC3XX_R10D_RGB10},
+ {0xa0, 0x66, ZC3XX_R10E_RGB11},
+ {0xa0, 0xed, ZC3XX_R10F_RGB12},
+ {0xa0, 0xed, ZC3XX_R110_RGB20},
+ {0xa0, 0xed, ZC3XX_R111_RGB21},
+ {0xa0, 0x66, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x13, 0x0031},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x0e, 0x0004},
+ {0xaa, 0x19, 0x00cd},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action hdcs2020_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x1c, 0x0000},
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0006},
+ {0xaa, 0x0c, 0x007a},
+ {0xaa, 0x0d, 0x00a7},
+ {0xaa, 0x03, 0x00fb},
+ {0xaa, 0x05, 0x0000},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x09, 0x0008},
+ {0xaa, 0x0f, 0x0018}, /* original setting */
+ {0xaa, 0x10, 0x0018},
+ {0xaa, 0x11, 0x0018},
+ {0xaa, 0x12, 0x0018},
+ {0xaa, 0x15, 0x004e},
+ {0xaa, 0x1c, 0x0004},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+ {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xed, ZC3XX_R10B_RGB01},
+ {0xa0, 0xed, ZC3XX_R10C_RGB02},
+ {0xa0, 0xed, ZC3XX_R10D_RGB10},
+ {0xa0, 0x66, ZC3XX_R10E_RGB11},
+ {0xa0, 0xed, ZC3XX_R10F_RGB12},
+ {0xa0, 0xed, ZC3XX_R110_RGB20},
+ {0xa0, 0xed, ZC3XX_R111_RGB21},
+ {0xa0, 0x66, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ /**** set exposure ***/
+ {0xaa, 0x13, 0x0031},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x0e, 0x0004},
+ {0xaa, 0x19, 0x00cd},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action hdcs2020_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */
+ {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+ {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */
+ {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */
+ {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */
+ {}
+};
+static const struct usb_action hdcs2020_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+ {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+ {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+ {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */
+ {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */
+ {}
+};
+static const struct usb_action hdcs2020_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */
+ {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
+ {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
+ {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
+ {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */
+ {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */
+ {}
+};
+
+static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x30, 0x002d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */
+ {0xaa, 0x31, 0x0038},
+ {0xaa, 0x32, 0x0038},
+ {0xaa, 0x33, 0x0038},
+ {0xaa, 0x5b, 0x0001},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */
+ {}
+};
+
+static const struct usb_action hv7131b_Initial[] = { /* 640x480*/
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xaa, 0x30, 0x002d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x11, 0x0001},
+ {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e6},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x31, 0x0038},
+ {0xaa, 0x32, 0x0038},
+ {0xaa, 0x33, 0x0038},
+ {0xaa, 0x5b, 0x0001},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x02, ZC3XX_R188_MINGAIN},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */
+ {}
+};
+static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
+ {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */
+ {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */
+ {}
+};
+static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
+ {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
+ {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+ {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */
+ {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
+ {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
+ {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+ {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */
+ {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */
+ {}
+};
+static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
+ {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
+ {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
+ {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
+ {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
+ {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */
+ {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
+ {}
+};
+static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
+ {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
+ {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
+ {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
+ {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
+ {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
+ {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
+ {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
+ {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
+ {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
+ {}
+};
+
+/* from lPEPI264v.inf (hv7131b!) */
+static const struct usb_action hv7131r_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x000c},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e8},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0088},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {}
+};
+static const struct usb_action hv7131r_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x000c},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x13, 0x0000},
+ {0xaa, 0x14, 0x0001},
+ {0xaa, 0x15, 0x00e6},
+ {0xaa, 0x16, 0x0002},
+ {0xaa, 0x17, 0x0086},
+ {0xaa, 0x30, 0x000b},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0xc0, 0x019b},
+ {0xa0, 0xa0, 0x019c},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {}
+};
+static const struct usb_action hv7131r_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action hv7131r_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+static const struct usb_action icm105a_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+ {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xaa, 0x01, 0x0010},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0001},
+ {0xaa, 0x04, 0x0011},
+ {0xaa, 0x05, 0x00a0},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0002},
+ {0xaa, 0x04, 0x0013},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0003},
+ {0xaa, 0x04, 0x0015},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0004},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0005},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0006},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0026},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0007},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0022},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0008},
+ {0xaa, 0x04, 0x0021},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0009},
+ {0xaa, 0x04, 0x0023},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000a},
+ {0xaa, 0x04, 0x0025},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000b},
+ {0xaa, 0x04, 0x00ec},
+ {0xaa, 0x05, 0x002e},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000c},
+ {0xaa, 0x04, 0x00fa},
+ {0xaa, 0x05, 0x002a},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x07, 0x000d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x94, 0x0002},
+ {0xaa, 0x90, 0x0000},
+ {0xaa, 0x91, 0x001f},
+ {0xaa, 0x10, 0x0064},
+ {0xaa, 0x9b, 0x00f0},
+ {0xaa, 0x9c, 0x0002},
+ {0xaa, 0x14, 0x001a},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0xa8, 0x00c0},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x52, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf7, ZC3XX_R110_RGB20},
+ {0xa0, 0xf7, ZC3XX_R111_RGB21},
+ {0xa0, 0x52, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x0d, 0x0003},
+ {0xaa, 0x0c, 0x008c},
+ {0xaa, 0x0e, 0x0095},
+ {0xaa, 0x0f, 0x0002},
+ {0xaa, 0x1c, 0x0094},
+ {0xaa, 0x1d, 0x0002},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xec, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+
+static const struct usb_action icm105a_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xaa, 0x01, 0x0010},
+ {0xaa, 0x03, 0x0000},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0001},
+ {0xaa, 0x04, 0x0011},
+ {0xaa, 0x05, 0x00a0},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0002},
+ {0xaa, 0x04, 0x0013},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0001},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0003},
+ {0xaa, 0x04, 0x0015},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0004},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0005},
+ {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE},
+ {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+ {0xa1, 0x01, 0x0091},
+ {0xaa, 0x05, 0x0020},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0006},
+ {0xaa, 0x04, 0x0017},
+ {0xaa, 0x05, 0x0026},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0007},
+ {0xaa, 0x04, 0x0019},
+ {0xaa, 0x05, 0x0022},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0008},
+ {0xaa, 0x04, 0x0021},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x0009},
+ {0xaa, 0x04, 0x0023},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x000d},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000a},
+ {0xaa, 0x04, 0x0025},
+ {0xaa, 0x05, 0x00aa},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000b},
+ {0xaa, 0x04, 0x00ec},
+ {0xaa, 0x05, 0x002e},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x03, 0x000c},
+ {0xaa, 0x04, 0x00fa},
+ {0xaa, 0x05, 0x002a},
+ {0xaa, 0x06, 0x0005},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x07, 0x000d},
+ {0xaa, 0x01, 0x0005},
+ {0xaa, 0x94, 0x0002},
+ {0xaa, 0x90, 0x0000},
+ {0xaa, 0x91, 0x0010},
+ {0xaa, 0x10, 0x0064},
+ {0xaa, 0x9b, 0x00f0},
+ {0xaa, 0x9c, 0x0002},
+ {0xaa, 0x14, 0x001a},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xaa, 0xa8, 0x0080},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+ {0xa1, 0x01, 0x0002},
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {0xa1, 0x01, 0x0008},
+
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+
+ {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf7, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf7, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x52, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf7, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf7, ZC3XX_R110_RGB20},
+ {0xa0, 0xf7, ZC3XX_R111_RGB21},
+ {0xa0, 0x52, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x0d, 0x0003},
+ {0xaa, 0x0c, 0x0020},
+ {0xaa, 0x0e, 0x000e},
+ {0xaa, 0x0f, 0x0002},
+ {0xaa, 0x1c, 0x000d},
+ {0xaa, 0x1d, 0x0002},
+ {0xaa, 0x20, 0x0080},
+ {0xaa, 0x22, 0x0080},
+ {0xaa, 0x24, 0x0080},
+ {0xaa, 0x26, 0x0080},
+ {0xaa, 0x00, 0x0084},
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+ {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action icm105a_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
+ {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+ {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
+ {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */
+ {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */
+ {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+static const struct usb_action icm105a_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
+ {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */
+ {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+static const struct usb_action icm105a_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+};
+static const struct usb_action icm105a_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+ {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */
+ {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
+ {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
+ {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
+ {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
+ {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
+ {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
+ {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
+ {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
+ {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
+ {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+};
+
+static const struct usb_action mc501cb_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+ {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+ {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+ {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+ {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+ {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+ {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */
+ {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+ {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+ {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+ {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+ {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+ {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+ {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+ {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+ {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+ {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+ {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+ {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+ {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+ {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+ {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+ {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+ {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
+ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+ {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+ {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+ {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+ {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+ {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+ {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+ {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+ {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+ {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+ {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+ {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+ {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+ {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+ {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+ {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+ {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+ {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */
+ {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */
+ {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+ {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */
+ {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+ {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+ {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+ {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+ {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+ {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
+ {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
+ {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
+ {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
+ {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
+ {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+ {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
+ {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
+ {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
+ {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */
+ {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
+ {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
+ {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
+ {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
+ {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
+ {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
+ {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
+ {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
+ {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
+ {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
+ {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
+ {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
+ {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
+ {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
+ {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
+ {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
+ {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
+ {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
+ {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
+ {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
+ {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
+ {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
+ {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
+ {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
+ {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
+ {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
+ {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
+ {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
+ {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
+ {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
+ {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
+ {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
+ {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
+ {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
+ {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
+ {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
+ {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
+ {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
+ {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
+ {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
+ {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */
+ {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
+ {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */
+ {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
+ {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
+ {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */
+ {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
+ {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
+ {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
+ {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
+ {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
+ {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
+ {}
+};
+
+static const struct usb_action mc501cb_50HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
+ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
+ {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */
+ {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */
+ {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_50HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
+ {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */
+ {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */
+ {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */
+ {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */
+ {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_60HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_60HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_NoFliker[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
+ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
+ {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
+ {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
+ {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
+ {}
+};
+
+static const struct usb_action mc501cb_NoFlikerScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
+ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
+ {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
+ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
+ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
+ {}
+};
+
+/* from zs211.inf */
+static const struct usb_action ov7620_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
+ {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
+ {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+ {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+ {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+ {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */
+ {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+ {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+ {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+ {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+ {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+ {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+ {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+ {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+ {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+ {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+ {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+};
+static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ /* mx change? */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
+ {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
+ {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */
+ /* OV7648 00,9c,d8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
+ {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
+ {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
+ {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
+ {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
+ {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
+ {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
+ {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
+ {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */
+ {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
+ {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
+ {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
+ {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
+ {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
+ {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
+ {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
+ {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
+ {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
+ {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+};
+static const struct usb_action ov7620_50HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+ {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */
+ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
+/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
+ * if mode0 (640x480) */
+ {}
+};
+static const struct usb_action ov7620_60HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
+ {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
+ {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+ {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */
+ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
+/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
+ * if mode0 (640x480) */
+/* ?? in gspca v1, it was
+ {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
+ {0xa1, 0x01, 0x0037}, */
+ {}
+};
+static const struct usb_action ov7620_NoFliker[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
+ {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */
+ {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
+ {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
+/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc
+ * if mode1 (320x240) */
+/* ?? was
+ {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
+ {0xa1, 0x01, 0x0037}, */
+ {}
+};
+
+static const struct usb_action ov7630c_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x12, 0x0080},
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x12, 0x0069},
+ {0xaa, 0x04, 0x0020},
+ {0xaa, 0x06, 0x0050},
+ {0xaa, 0x13, 0x0083},
+ {0xaa, 0x14, 0x0000},
+ {0xaa, 0x15, 0x0024},
+ {0xaa, 0x17, 0x0018},
+ {0xaa, 0x18, 0x00ba},
+ {0xaa, 0x19, 0x0002},
+ {0xaa, 0x1a, 0x00f6},
+ {0xaa, 0x1b, 0x0002},
+ {0xaa, 0x20, 0x00c2},
+ {0xaa, 0x24, 0x0060},
+ {0xaa, 0x25, 0x0040},
+ {0xaa, 0x26, 0x0030},
+ {0xaa, 0x27, 0x00ea},
+ {0xaa, 0x28, 0x00a0},
+ {0xaa, 0x21, 0x0000},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0096},
+ {0xaa, 0x2d, 0x0094},
+ {0xaa, 0x2f, 0x003d},
+ {0xaa, 0x30, 0x0024},
+ {0xaa, 0x60, 0x0000},
+ {0xaa, 0x61, 0x0040},
+ {0xaa, 0x68, 0x007c},
+ {0xaa, 0x6f, 0x0015},
+ {0xaa, 0x75, 0x0088},
+ {0xaa, 0x77, 0x00b5},
+ {0xaa, 0x01, 0x0060},
+ {0xaa, 0x02, 0x0060},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x46, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R113_RGB03},
+/* 0x10, */
+ {0xa1, 0x01, 0x0002},
+ {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+ {0xa0, 0x50, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf8, ZC3XX_R110_RGB20},
+ {0xa0, 0xf8, ZC3XX_R111_RGB21},
+ {0xa0, 0x50, ZC3XX_R112_RGB22},
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/
+ {0xa0, 0x0c, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x1f, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x3a, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x53, ZC3XX_R124_GAMMA04},
+ {0xa0, 0x6d, ZC3XX_R125_GAMMA05},
+ {0xa0, 0x85, ZC3XX_R126_GAMMA06},
+ {0xa0, 0x9c, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xb0, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xc2, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xde, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x05, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x0f, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x16, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x19, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x19, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x17, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x15, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x12, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x10, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x09, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x08, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x06, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x03, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+ {0xa0, 0x50, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf8, ZC3XX_R110_RGB20},
+ {0xa0, 0xf8, ZC3XX_R111_RGB21},
+ {0xa0, 0x50, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x10, 0x001b},
+ {0xaa, 0x76, 0x0002},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0000},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x13, 0x0083}, /* 40 */
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action ov7630c_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+
+ {0xaa, 0x12, 0x0080},
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
+ {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
+ {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
+ {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xaa, 0x12, 0x0069}, /* i2c */
+ {0xaa, 0x04, 0x0020},
+ {0xaa, 0x06, 0x0050},
+ {0xaa, 0x13, 0x00c3},
+ {0xaa, 0x14, 0x0000},
+ {0xaa, 0x15, 0x0024},
+ {0xaa, 0x19, 0x0003},
+ {0xaa, 0x1a, 0x00f6},
+ {0xaa, 0x1b, 0x0002},
+ {0xaa, 0x20, 0x00c2},
+ {0xaa, 0x24, 0x0060},
+ {0xaa, 0x25, 0x0040},
+ {0xaa, 0x26, 0x0030},
+ {0xaa, 0x27, 0x00ea},
+ {0xaa, 0x28, 0x00a0},
+ {0xaa, 0x21, 0x0000},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0096},
+ {0xaa, 0x2d, 0x0084},
+ {0xaa, 0x2f, 0x003d},
+ {0xaa, 0x30, 0x0024},
+ {0xaa, 0x60, 0x0000},
+ {0xaa, 0x61, 0x0040},
+ {0xaa, 0x68, 0x007c},
+ {0xaa, 0x6f, 0x0015},
+ {0xaa, 0x75, 0x0088},
+ {0xaa, 0x77, 0x00b5},
+ {0xaa, 0x01, 0x0060},
+ {0xaa, 0x02, 0x0060},
+ {0xaa, 0x17, 0x0018},
+ {0xaa, 0x18, 0x00ba},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x46, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R113_RGB03},
+
+ {0xa1, 0x01, 0x0002},
+ {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+ {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+ {0xa0, 0x00, ZC3XX_R110_RGB20},
+ {0xa0, 0xf6, ZC3XX_R111_RGB21},
+ {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0008},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+ {0xa1, 0x01, 0x01c8},
+ {0xa1, 0x01, 0x01c9},
+ {0xa1, 0x01, 0x01ca},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+ {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
+ {0xa0, 0x3a, ZC3XX_R121_GAMMA01},
+ {0xa0, 0x5b, ZC3XX_R122_GAMMA02},
+ {0xa0, 0x7c, ZC3XX_R123_GAMMA03},
+ {0xa0, 0x94, ZC3XX_R124_GAMMA04},
+ {0xa0, 0xa9, ZC3XX_R125_GAMMA05},
+ {0xa0, 0xbb, ZC3XX_R126_GAMMA06},
+ {0xa0, 0xca, ZC3XX_R127_GAMMA07},
+ {0xa0, 0xd7, ZC3XX_R128_GAMMA08},
+ {0xa0, 0xe1, ZC3XX_R129_GAMMA09},
+ {0xa0, 0xea, ZC3XX_R12A_GAMMA0A},
+ {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B},
+ {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C},
+ {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D},
+ {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+ {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+ {0xa0, 0x20, ZC3XX_R130_GAMMA10},
+ {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+ {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+ {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+ {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+ {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+ {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+ {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+ {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+ {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+ {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+ {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+ {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+ {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+ {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+ {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+ {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xfe, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf7, ZC3XX_R10D_RGB10},
+ {0xa0, 0x4d, ZC3XX_R10E_RGB11},
+ {0xa0, 0xfc, ZC3XX_R10F_RGB12},
+ {0xa0, 0x00, ZC3XX_R110_RGB20},
+ {0xa0, 0xf6, ZC3XX_R111_RGB21},
+ {0xa0, 0x4a, ZC3XX_R112_RGB22},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x10, 0x000d},
+ {0xaa, 0x76, 0x0002},
+ {0xaa, 0x2a, 0x0081},
+ {0xaa, 0x2b, 0x0000},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xaa, 0x13, 0x00c3},
+
+ {0xa1, 0x01, 0x0180},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pas106b_Initial_com[] = {
+/* Sream and Sensor specific */
+ {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */
+/* System */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
+/* Picture size */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */
+ {0xa0, 0x03, 0x003a},
+ {0xa0, 0x0c, 0x003b},
+ {0xa0, 0x04, 0x0038},
+ {}
+};
+
+static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+ {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+ {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
+/* Init the sensor */
+ {0xaa, 0x02, 0x0004},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x09, 0x0005},
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0002},
+ {0xaa, 0x0c, 0x0005},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x14, 0x0081},
+/* Other registers */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+ {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+ {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+ {0xa0, 0x03, ZC3XX_R181_WINXSTART},
+ {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+ {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+ {0xa0, 0x03, ZC3XX_R184_WINYSTART},
+ {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+ {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Auto exposure and white balance */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+ {0xaa, 0x07, 0x00b1},
+ {0xaa, 0x05, 0x0003},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x03, 0x003b},
+/* Gains */
+ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+};
+
+static const struct usb_action pas106b_Initial[] = { /* 352x288 */
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+/* Sream and Sensor specific */
+ {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
+/* Picture size */
+ {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW},
+/* System */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+/* Sream and Sensor specific */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+/* Sensor Interface */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
+/* Window inside sensor array */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
+/* Init the sensor */
+ {0xaa, 0x02, 0x0004},
+ {0xaa, 0x08, 0x0000},
+ {0xaa, 0x09, 0x0005},
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0002},
+ {0xaa, 0x0c, 0x0005},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x14, 0x0081},
+/* Other registers */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+/* Frame retreiving */
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+/* Gains */
+ {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
+/* Unknown */
+ {0xa0, 0x00, 0x01ad},
+/* Sharpness */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+/* Other registers */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+/* Auto exposure and white balance */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+/*Dead pixels */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+/* EEPROM */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+/* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+ {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
+
+ {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+ {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+ {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+ {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+ {0xa0, 0x58, ZC3XX_R10E_RGB11},
+ {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+ {0xa0, 0xf4, ZC3XX_R110_RGB20},
+ {0xa0, 0xf4, ZC3XX_R111_RGB21},
+ {0xa0, 0x58, ZC3XX_R112_RGB22},
+/* Auto correction */
+ {0xa0, 0x03, ZC3XX_R181_WINXSTART},
+ {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
+ {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
+ {0xa0, 0x03, ZC3XX_R184_WINYSTART},
+ {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
+ {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+
+/* Auto exposure and white balance */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
+
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
+
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+/* sensor on */
+ {0xaa, 0x07, 0x00b1},
+ {0xaa, 0x05, 0x0003},
+ {0xaa, 0x04, 0x0001},
+ {0xaa, 0x03, 0x003b},
+/* Gains */
+ {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+/* Auto correction */
+ {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+/* Gains */
+ {0xa0, 0x40, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+
+ {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */
+ {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */
+ {}
+};
+static const struct usb_action pas106b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
+ {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */
+ {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */
+ {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */
+ {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */
+ {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+ {}
+};
+static const struct usb_action pas106b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
+ {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */
+ {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */
+ {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
+ {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */
+ {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
+ {}
+};
+static const struct usb_action pas106b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
+ {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */
+ {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
+ {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
+ {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {}
+};
+
+/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
+static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
+ {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
+ {}
+};
+static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x001b},
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+ {0xaa, 0x04, 0x0008},
+ {0xaa, 0x05, 0x001b},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x003d},
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+ {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x0008},
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x0008},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0006},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0006},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0004},
+ {0xaa, 0x21, 0x000c},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0010},
+ {0xaa, 0x05, 0x000c},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+
+/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
+static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x507f},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x5100},
+ {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/
+ {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
+ {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_1_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0562},
+ {0xbb, 0x01, 0x09aa},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0934},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x09, 0x016e},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0983},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x01, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0534},
+ {0xbb, 0x02, 0x0960},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
+static const struct usb_action mt9v111_3_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */
+ {0xaa, 0x06, 0x0000}, /* operating mode control */
+ {0xaa, 0x08, 0x0483}, /* output format control */
+ /* H red first, V red or blue first,
+ * raw Bayer, auto flicker */
+ {0xaa, 0x01, 0x0004}, /* select sensor core registers */
+ {0xaa, 0x08, 0x0006}, /* row start */
+ {0xaa, 0x02, 0x0011}, /* column start */
+ {0xaa, 0x03, 0x01e5}, /* window height - 1 */
+ {0xaa, 0x04, 0x0285}, /* window width - 1 */
+ {0xaa, 0x07, 0x3002}, /* output control */
+ {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */
+ {0xaa, 0x35, 0x007f}, /* global gain */
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f}, /* green1 gain */
+ {0xaa, 0x2c, 0x007f}, /* blue gain */
+ {0xaa, 0x2d, 0x007f}, /* red gain */
+ {0xaa, 0x2e, 0x007f}, /* green2 gain */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x35, 0x007f},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x007f},
+ {0xaa, 0x2c, 0x007f},
+ {0xaa, 0x2d, 0x007f},
+ {0xaa, 0x2e, 0x007f},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009}, /* horizontal blanking */
+ {0xaa, 0x09, 0x01ce}, /* shutter width */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x01ce},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AE60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0009},
+ {0xaa, 0x09, 0x0083},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x05, 0x0034},
+ {0xaa, 0x09, 0x0260},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+};
+
+static const struct usb_action pb0330_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
+ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+ {0xaa, 0x2f, 0xf7b0},
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x34, 0x0100},
+ {0xaa, 0x35, 0x0060},
+ {0xaa, 0x3d, 0x068f},
+ {0xaa, 0x40, 0x01e0},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x09, 0x01ad},
+ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+};
+static const struct usb_action pb0330_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x055c},
+ {0xbb, 0x01, 0x09aa},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0566},
+ {0xbb, 0x02, 0x09b2},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0974},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x02, 0x096c},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0509},
+ {0xbb, 0x02, 0x0940},
+ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+static const struct usb_action pb0330_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xbb, 0x00, 0x0535},
+ {0xbb, 0x01, 0x0980},
+ {0xbb, 0x00, 0x1001},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+};
+
+/* from oem9.inf */
+static const struct usb_action po2030_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+ {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+ {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+ {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+ {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+ {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+ {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+ {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */
+ {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+ {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */
+ {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */
+ {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+ {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+ {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+ {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+ {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+ {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+ {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+ {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+ {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+ {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+ {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+ {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+ {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+ {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+ {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+ {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+ {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+ {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+ {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+ {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+ {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+ {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+ {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+ {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+ {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+ {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+ {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+ {}
+};
+
+/* from oem9.inf */
+static const struct usb_action po2030_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
+ {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
+ {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
+ {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
+ {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
+ {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
+ {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
+ {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */
+ {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
+ {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */
+ {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */
+ {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
+ {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
+ {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
+ {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
+ {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
+ {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
+ {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
+ {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
+ {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
+ {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
+ {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
+ {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
+ {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
+ {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
+ {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
+ {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
+ {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
+ {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
+ {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
+ {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
+ {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
+ {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
+ {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
+ {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
+ {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
+ {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
+ {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
+ {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
+ {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
+ {}
+};
+
+static const struct usb_action po2030_50HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+ {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+ {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
+ {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
+ {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
+ {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
+ {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_60HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+ {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
+ {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
+ {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
+ {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+ {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
+ {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
+ /* win: 01,8d,80 */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
+ {}
+};
+
+static const struct usb_action po2030_NoFliker[] = {
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
+ {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
+ {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
+ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
+ {}
+};
+
+static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+
+ {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+};
+static const struct usb_action tas5130c_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
+ {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+};
+static const struct usb_action tas5130c_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
+ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
+ {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+static const struct usb_action tas5130c_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+
+static const struct usb_action tas5130c_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
+ {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
+ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
+ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+};
+
+/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */
+static const struct usb_action gc0303_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc,
+ * 6<->8 */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc,
+ * 6<->8 */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xaa, 0x01, 0x0000},
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
+ {0xaa, 0x1b, 0x0000},
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
+ {0xaa, 0x0a, 0x0002},
+ {0xaa, 0x0b, 0x0000},
+ {0xaa, 0x0c, 0x0002},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0002},
+ {0xaa, 0x0f, 0x0000},
+ {0xaa, 0x10, 0x0002},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
+ {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */
+ {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
+ {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
+ {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
+ {0xaa, 0x1b, 0x0000},
+ {}
+};
+
+static const struct usb_action gc0303_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc,
+ * 8<->6 */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc,
+ * 8<->6 */
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
+ {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
+ {0xaa, 0x01, 0x0000},
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
+ {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
+ {0xaa, 0x1b, 0x0000},
+ {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
+ {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
+ {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
+ {0xaa, 0x0a, 0x0001},
+ {0xaa, 0x0b, 0x0000},
+ {0xaa, 0x0c, 0x0001},
+ {0xaa, 0x0d, 0x0000},
+ {0xaa, 0x0e, 0x0001},
+ {0xaa, 0x0f, 0x0000},
+ {0xaa, 0x10, 0x0001},
+ {0xaa, 0x11, 0x0000},
+ {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
+ {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
+ {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
+ {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
+ {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
+ {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
+ {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
+ {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
+ {0xaa, 0x1b, 0x0000},
+ {}
+};
+static const struct usb_action gc0303_50HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
+ {0xaa, 0x84, 0x0063},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
+ {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_50HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */
+ {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
+ {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_60HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000},
+ {0xaa, 0x84, 0x003b},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */
+ {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_60HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000},
+ {0xaa, 0x84, 0x0076},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */
+ {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */
+ {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */
+ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
+ {0xa0, 0x80, ZC3XX_R18D_YTARGET},
+ {}
+};
+
+static const struct usb_action gc0303_NoFliker[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {}
+};
+
+static const struct usb_action gc0303_NoFlikerScale[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+ {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
+ {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
+ {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
+ {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
+ {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
+ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
+ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
+ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
+ {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
+ {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
+ {}
+};
+
+static u8 reg_r(struct gspca_dev *gspca_dev,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0xa1,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x01, /* value */
+ index, gspca_dev->usb_buf, 1,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_r err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
+ return gspca_dev->usb_buf[0];
+}
+
+static void reg_w(struct gspca_dev *gspca_dev,
+ u8 value,
+ u16 index)
+{
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0xa0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0,
+ 500);
+ if (ret < 0) {
+ pr_err("reg_w_i err %d\n", ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static u16 i2c_read(struct gspca_dev *gspca_dev,
+ u8 reg)
+{
+ u8 retbyte;
+ u16 retval;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w(gspca_dev, reg, 0x0092);
+ reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */
+ msleep(20);
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ pr_err("i2c_r status error %02x\n", retbyte);
+ retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */
+ retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */
+ return retval;
+}
+
+static u8 i2c_write(struct gspca_dev *gspca_dev,
+ u8 reg,
+ u8 valL,
+ u8 valH)
+{
+ u8 retbyte;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ reg_w(gspca_dev, reg, 0x92);
+ reg_w(gspca_dev, valL, 0x93);
+ reg_w(gspca_dev, valH, 0x94);
+ reg_w(gspca_dev, 0x01, 0x90); /* <- write command */
+ msleep(1);
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
+ if (retbyte != 0x00)
+ pr_err("i2c_w status error %02x\n", retbyte);
+ return retbyte;
+}
+
+static void usb_exchange(struct gspca_dev *gspca_dev,
+ const struct usb_action *action)
+{
+ while (action->req) {
+ switch (action->req) {
+ case 0xa0: /* write register */
+ reg_w(gspca_dev, action->val, action->idx);
+ break;
+ case 0xa1: /* read status */
+ reg_r(gspca_dev, action->idx);
+ break;
+ case 0xaa:
+ i2c_write(gspca_dev,
+ action->val, /* reg */
+ action->idx & 0xff, /* valL */
+ action->idx >> 8); /* valH */
+ break;
+ case 0xbb:
+ i2c_write(gspca_dev,
+ action->idx >> 8, /* reg */
+ action->idx & 0xff, /* valL */
+ action->val); /* valH */
+ break;
+ default:
+/* case 0xdd: * delay */
+ msleep(action->idx);
+ break;
+ }
+ action++;
+ msleep(1);
+ }
+}
+
+static void setmatrix(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ const u8 *matrix;
+ static const u8 adcm2700_matrix[9] =
+/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
+/*ms-win*/
+ {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
+ static const u8 gc0305_matrix[9] =
+ {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
+ static const u8 ov7620_matrix[9] =
+ {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+ static const u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
+ static const u8 po2030_matrix[9] =
+ {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+ static const u8 tas5130c_matrix[9] =
+ {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
+ static const u8 gc0303_matrix[9] =
+ {0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c};
+ static const u8 *matrix_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = adcm2700_matrix,
+ [SENSOR_CS2102] = ov7620_matrix,
+ [SENSOR_CS2102K] = NULL,
+ [SENSOR_GC0303] = gc0303_matrix,
+ [SENSOR_GC0305] = gc0305_matrix,
+ [SENSOR_HDCS2020] = NULL,
+ [SENSOR_HV7131B] = NULL,
+ [SENSOR_HV7131R] = po2030_matrix,
+ [SENSOR_ICM105A] = po2030_matrix,
+ [SENSOR_MC501CB] = NULL,
+ [SENSOR_MT9V111_1] = gc0305_matrix,
+ [SENSOR_MT9V111_3] = gc0305_matrix,
+ [SENSOR_OV7620] = ov7620_matrix,
+ [SENSOR_OV7630C] = NULL,
+ [SENSOR_PAS106] = NULL,
+ [SENSOR_PAS202B] = pas202b_matrix,
+ [SENSOR_PB0330] = gc0305_matrix,
+ [SENSOR_PO2030] = po2030_matrix,
+ [SENSOR_TAS5130C] = tas5130c_matrix,
+ };
+
+ matrix = matrix_tb[sd->sensor];
+ if (matrix == NULL)
+ return; /* matrix already loaded */
+ for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
+ reg_w(gspca_dev, matrix[i], 0x010a + i);
+}
+
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
+{
+ static const u8 sharpness_tb[][2] = {
+ {0x02, 0x03},
+ {0x04, 0x07},
+ {0x08, 0x0f},
+ {0x10, 0x1e}
+ };
+
+ reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6);
+ reg_r(gspca_dev, 0x01c8);
+ reg_r(gspca_dev, 0x01c9);
+ reg_r(gspca_dev, 0x01ca);
+ reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb);
+}
+
+static void setcontrast(struct gspca_dev *gspca_dev,
+ s32 gamma, s32 brightness, s32 contrast)
+{
+ const u8 *Tgamma;
+ int g, i, adj, gp1, gp2;
+ u8 gr[16];
+ static const u8 delta_b[16] = /* delta for brightness */
+ {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
+ 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18};
+ static const u8 delta_c[16] = /* delta for contrast */
+ {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06,
+ 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02};
+ static const u8 gamma_tb[6][16] = {
+ {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
+ 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
+ {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
+ 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
+ {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
+ 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
+ {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
+ {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
+ 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
+ {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
+ 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
+ };
+
+ Tgamma = gamma_tb[gamma - 1];
+
+ contrast -= 128; /* -128 / 127 */
+ brightness -= 128; /* -128 / 92 */
+ adj = 0;
+ gp1 = gp2 = 0;
+ for (i = 0; i < 16; i++) {
+ g = Tgamma[i] + delta_b[i] * brightness / 256
+ - delta_c[i] * contrast / 256 - adj / 2;
+ if (g > 0xff)
+ g = 0xff;
+ else if (g < 0)
+ g = 0;
+ reg_w(gspca_dev, g, 0x0120 + i); /* gamma */
+ if (contrast > 0)
+ adj--;
+ else if (contrast < 0)
+ adj++;
+ if (i > 1)
+ gr[i - 1] = (g - gp2) / 2;
+ else if (i != 0)
+ gr[0] = gp1 == 0 ? 0 : (g - gp1);
+ gp2 = gp1;
+ gp1 = g;
+ }
+ gr[15] = (0xff - gp2) / 2;
+ for (i = 0; i < 16; i++)
+ reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */
+}
+
+static s32 getexposure(struct gspca_dev *gspca_dev)
+{
+ return (i2c_read(gspca_dev, 0x25) << 9)
+ | (i2c_read(gspca_dev, 0x26) << 1)
+ | (i2c_read(gspca_dev, 0x27) >> 7);
+}
+
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+ i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
+ i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
+ i2c_write(gspca_dev, 0x27, val << 7, 0x00);
+}
+
+static void setquality(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]);
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ * 50Hz, for European and Asian lighting (default)
+ * 60Hz, for American lighting
+ * 0 = No Fliker (for outdoore usage)
+ */
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, mode;
+ const struct usb_action *zc3_freq;
+ static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
+ [SENSOR_ADCM2700] =
+ {adcm2700_NoFliker, adcm2700_NoFliker,
+ adcm2700_50HZ, adcm2700_50HZ,
+ adcm2700_60HZ, adcm2700_60HZ},
+ [SENSOR_CS2102] =
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ cs2102_50HZ, cs2102_50HZScale,
+ cs2102_60HZ, cs2102_60HZScale},
+ [SENSOR_CS2102K] =
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ NULL, NULL, /* currently disabled */
+ NULL, NULL},
+ [SENSOR_GC0303] =
+ {gc0303_NoFliker, gc0303_NoFlikerScale,
+ gc0303_50HZ, gc0303_50HZScale,
+ gc0303_60HZ, gc0303_60HZScale},
+ [SENSOR_GC0305] =
+ {gc0305_NoFliker, gc0305_NoFliker,
+ gc0305_50HZ, gc0305_50HZ,
+ gc0305_60HZ, gc0305_60HZ},
+ [SENSOR_HDCS2020] =
+ {hdcs2020_NoFliker, hdcs2020_NoFliker,
+ hdcs2020_50HZ, hdcs2020_50HZ,
+ hdcs2020_60HZ, hdcs2020_60HZ},
+ [SENSOR_HV7131B] =
+ {hv7131b_NoFliker, hv7131b_NoFlikerScale,
+ hv7131b_50HZ, hv7131b_50HZScale,
+ hv7131b_60HZ, hv7131b_60HZScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_NoFliker, hv7131r_NoFlikerScale,
+ hv7131r_50HZ, hv7131r_50HZScale,
+ hv7131r_60HZ, hv7131r_60HZScale},
+ [SENSOR_ICM105A] =
+ {icm105a_NoFliker, icm105a_NoFlikerScale,
+ icm105a_50HZ, icm105a_50HZScale,
+ icm105a_60HZ, icm105a_60HZScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_NoFliker, mc501cb_NoFlikerScale,
+ mc501cb_50HZ, mc501cb_50HZScale,
+ mc501cb_60HZ, mc501cb_60HZScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
+ mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
+ mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
+ mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
+ mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
+ [SENSOR_OV7620] =
+ {ov7620_NoFliker, ov7620_NoFliker,
+ ov7620_50HZ, ov7620_50HZ,
+ ov7620_60HZ, ov7620_60HZ},
+ [SENSOR_OV7630C] =
+ {NULL, NULL,
+ NULL, NULL,
+ NULL, NULL},
+ [SENSOR_PAS106] =
+ {pas106b_NoFliker, pas106b_NoFliker,
+ pas106b_50HZ, pas106b_50HZ,
+ pas106b_60HZ, pas106b_60HZ},
+ [SENSOR_PAS202B] =
+ {pas202b_NoFliker, pas202b_NoFlikerScale,
+ pas202b_50HZ, pas202b_50HZScale,
+ pas202b_60HZ, pas202b_60HZScale},
+ [SENSOR_PB0330] =
+ {pb0330_NoFliker, pb0330_NoFlikerScale,
+ pb0330_50HZ, pb0330_50HZScale,
+ pb0330_60HZ, pb0330_60HZScale},
+ [SENSOR_PO2030] =
+ {po2030_NoFliker, po2030_NoFliker,
+ po2030_50HZ, po2030_50HZ,
+ po2030_60HZ, po2030_60HZ},
+ [SENSOR_TAS5130C] =
+ {tas5130c_NoFliker, tas5130c_NoFlikerScale,
+ tas5130c_50HZ, tas5130c_50HZScale,
+ tas5130c_60HZ, tas5130c_60HZScale},
+ };
+
+ i = val * 2;
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (mode)
+ i++; /* 320x240 */
+ zc3_freq = freq_tb[sd->sensor][i];
+ if (zc3_freq == NULL)
+ return;
+ usb_exchange(gspca_dev, zc3_freq);
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ if (mode /* if 320x240 */
+ && val == 1) /* and 50Hz */
+ reg_w(gspca_dev, 0x85, 0x018d);
+ /* win: 0x80, 0x018d */
+ break;
+ case SENSOR_OV7620:
+ if (!mode) { /* if 640x480 */
+ if (val != 0) /* and filter */
+ reg_w(gspca_dev, 0x40, 0x0002);
+ else
+ reg_w(gspca_dev, 0x44, 0x0002);
+ }
+ break;
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x00, 0x01a7);
+ break;
+ }
+}
+
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
+{
+ reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
+}
+
+/*
+ * Update the transfer parameters.
+ * This function is executed from a work queue.
+ */
+static void transfer_update(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ int change, good;
+ u8 reg07, reg11;
+
+ /* reg07 gets set to 0 by sd_start before starting us */
+ reg07 = 0;
+
+ good = 0;
+ for (;;) {
+ msleep(100);
+
+ /* To protect gspca_dev->usb_buf and gspca_dev->usb_err */
+ mutex_lock(&gspca_dev->usb_lock);
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ goto err;
+#endif
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto err;
+
+ /* Bit 0 of register 11 indicates FIFO overflow */
+ gspca_dev->usb_err = 0;
+ reg11 = reg_r(gspca_dev, 0x0011);
+ if (gspca_dev->usb_err)
+ goto err;
+
+ change = reg11 & 0x01;
+ if (change) { /* overflow */
+ good = 0;
+
+ if (reg07 == 0) /* Bit Rate Control not enabled? */
+ reg07 = 0x32; /* Allow 98 bytes / unit */
+ else if (reg07 > 2)
+ reg07 -= 2; /* Decrease allowed bytes / unit */
+ else
+ change = 0;
+ } else { /* no overflow */
+ good++;
+ if (good >= 10) {
+ good = 0;
+ if (reg07) { /* BRC enabled? */
+ change = 1;
+ if (reg07 < 0x32)
+ reg07 += 2;
+ else
+ reg07 = 0;
+ }
+ }
+ }
+ if (change) {
+ gspca_dev->usb_err = 0;
+ reg_w(gspca_dev, reg07, 0x0007);
+ if (gspca_dev->usb_err)
+ goto err;
+ }
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ return;
+err:
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
+static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
+{
+ reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */
+ switch (sensor) {
+ case SENSOR_PAS106:
+ reg_w(gspca_dev, 0x03, 0x003a);
+ reg_w(gspca_dev, 0x0c, 0x003b);
+ reg_w(gspca_dev, 0x08, 0x0038);
+ break;
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_MT9V111_1:
+ case SENSOR_MT9V111_3:
+ case SENSOR_PB0330:
+ case SENSOR_PO2030:
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ if (sensor == SENSOR_PAS202B)
+ reg_w(gspca_dev, 0x0b, 0x0038);
+ break;
+ }
+}
+
+/* start probe 2 wires */
+static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
+{
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, sensor, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+/* msleep(2); */
+}
+
+static int sif_probe(struct gspca_dev *gspca_dev)
+{
+ u16 checkword;
+
+ start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ msleep(150);
+ checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
+ | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
+ PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
+ if (checkword == 0x0007) {
+ send_unknown(gspca_dev, SENSOR_PAS106);
+ return 0x0f; /* PAS106 */
+ }
+ return -1;
+}
+
+static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+{
+ u16 retword;
+
+ start_2wr_probe(gspca_dev, 0x00); /* HV7131B */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x00; /* HV7131B */
+
+ start_2wr_probe(gspca_dev, 0x04); /* CS2102 */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x04; /* CS2102 */
+
+ start_2wr_probe(gspca_dev, 0x06); /* OmniVision */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x11);
+ if (retword != 0) {
+ /* (should have returned 0xaa) --> Omnivision? */
+ /* reg_r 0x10 -> 0x06 --> */
+ goto ov_check;
+ }
+
+ start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */
+ i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
+ i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x15);
+ if (retword != 0)
+ return 0x08; /* HDCS2020 */
+
+ start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */
+ i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
+ retword = i2c_read(gspca_dev, 0x07);
+ if (retword != 0)
+ return 0x0a; /* PB0330 */
+ retword = i2c_read(gspca_dev, 0x03);
+ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+ retword = i2c_read(gspca_dev, 0x04);
+ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+
+ start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */
+ i2c_write(gspca_dev, 0x01, 0x11, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x0c; /* ICM105A */
+
+ start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */
+ reg_w(gspca_dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+ msleep(50);
+ retword = i2c_read(gspca_dev, 0x03);
+ if (retword != 0) {
+ send_unknown(gspca_dev, SENSOR_PAS202B);
+ return 0x0e; /* PAS202BCB */
+ }
+
+ start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+ return 0x02; /* TAS5130C */
+ov_check:
+ reg_r(gspca_dev, 0x0010); /* ?? */
+ reg_r(gspca_dev, 0x0010);
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ msleep(500);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
+ retword = i2c_read(gspca_dev, 0x0a) << 8;
+ retword |= i2c_read(gspca_dev, 0x0b);
+ PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
+ switch (retword) {
+ case 0x7631: /* OV7630C */
+ reg_w(gspca_dev, 0x06, 0x0010);
+ break;
+ case 0x7620: /* OV7620 */
+ case 0x7648: /* OV7648 */
+ break;
+ default:
+ return -1; /* not OmniVision */
+ }
+ return retword;
+}
+
+struct sensor_by_chipset_revision {
+ u16 revision;
+ u8 internal_sensor_id;
+};
+static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+ {0xc000, 0x12}, /* TAS5130C */
+ {0xc001, 0x13}, /* MT9V111 */
+ {0xe001, 0x13},
+ {0x8001, 0x13},
+ {0x8000, 0x14}, /* CS2102K */
+ {0x8400, 0x15}, /* MT9V111 */
+ {0xe400, 0x15},
+};
+
+static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ u16 retword;
+
+/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+ reg_w(gspca_dev, 0x02, 0x0010);
+ reg_r(gspca_dev, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x91, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ retword = i2c_read(gspca_dev, 0x14);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+ retword = i2c_read(gspca_dev, 0x15);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+ retword = i2c_read(gspca_dev, 0x16);
+ if (retword != 0)
+ return 0x11; /* HV7131R */
+
+ reg_w(gspca_dev, 0x02, 0x0010);
+ retword = reg_r(gspca_dev, 0x000b) << 8;
+ retword |= reg_r(gspca_dev, 0x000a);
+ PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
+ reg_r(gspca_dev, 0x0010);
+ if ((retword & 0xff00) == 0x6400)
+ return 0x02; /* TAS5130C */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+ if (chipset_revision_sensor[i].revision == retword) {
+ sd->chip_revision = retword;
+ send_unknown(gspca_dev, SENSOR_PB0330);
+ return chipset_revision_sensor[i].internal_sensor_id;
+ }
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xdd, 0x008b);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a");
+ return 0x0a; /* PB0330 */
+ }
+
+ /* probe gc0303 / gc0305 */
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x98, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ msleep(2);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
+ if (retword == 0x0011) /* gc0303 */
+ return 0x0303;
+ if (retword == 0x0029) /* gc0305 */
+ send_unknown(gspca_dev, SENSOR_GC0305);
+ return retword;
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xa1, 0x008b);
+ reg_w(gspca_dev, 0x08, 0x008d);
+ reg_w(gspca_dev, 0x06, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
+ && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
+ send_unknown(gspca_dev, SENSOR_OV7620);
+ return 0x06; /* OmniVision confirm ? */
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x00, 0x0002);
+ reg_w(gspca_dev, 0x01, 0x0010);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0xee, 0x008b);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
+ retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
+ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
+ if (retword == 0x2030) {
+#ifdef GSPCA_DEBUG
+ u8 retbyte;
+
+ retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
+ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
+#endif
+ send_unknown(gspca_dev, SENSOR_PO2030);
+ return retword;
+ }
+
+ reg_w(gspca_dev, 0x01, 0x0000);
+ reg_w(gspca_dev, 0x0a, 0x0010);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ reg_w(gspca_dev, 0x01, 0x0001);
+ reg_w(gspca_dev, 0x03, 0x0012);
+ reg_w(gspca_dev, 0x01, 0x0012);
+ reg_w(gspca_dev, 0x05, 0x0012);
+ reg_w(gspca_dev, 0xd3, 0x008b);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
+ return 0x16; /* adcm2700 (6100/6200) */
+ }
+ return -1;
+}
+
+static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int sensor;
+
+ switch (sd->sensor) {
+ case SENSOR_MC501CB:
+ return -1; /* don't probe */
+ case SENSOR_GC0303:
+ /* may probe but with no write in reg 0x0010 */
+ return -1; /* don't probe */
+ case SENSOR_PAS106:
+ sensor = sif_probe(gspca_dev);
+ if (sensor >= 0)
+ return sensor;
+ break;
+ }
+ sensor = vga_2wr_probe(gspca_dev);
+ if (sensor >= 0)
+ return sensor;
+ return vga_3wr_probe(gspca_dev);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (id->idProduct == 0x301b)
+ sd->bridge = BRIDGE_ZC301;
+ else
+ sd->bridge = BRIDGE_ZC303;
+
+ /* define some sensors from the vendor/product */
+ sd->sensor = id->driver_info;
+
+ sd->reg08 = REG08_DEF;
+
+ INIT_WORK(&sd->work, transfer_update);
+
+ return 0;
+}
+
+static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val && sd->exposure && gspca_dev->streaming)
+ sd->exposure->val = getexposure(gspca_dev);
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+ int i, qual;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) {
+ qual = sd->reg08 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
+ if (ctrl->val <= jpeg_qual[i])
+ break;
+ }
+ if (i > 0 && i == qual && ctrl->val < jpeg_qual[i])
+ i--;
+
+ /* With high quality settings we need max bandwidth */
+ if (i >= 2 && gspca_dev->streaming &&
+ !gspca_dev->cam.needs_full_bandwidth)
+ return -EBUSY;
+
+ sd->reg08 = (i << 1) | 1;
+ ctrl->val = jpeg_qual[i];
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ /* gamma/brightness/contrast cluster */
+ case V4L2_CID_GAMMA:
+ setcontrast(gspca_dev, sd->gamma->val,
+ sd->brightness->val, sd->contrast->val);
+ break;
+ /* autogain/exposure cluster */
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && !ctrl->val && sd->exposure)
+ setexposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ setquality(gspca_dev);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops zcxx_ctrl_ops = {
+ .g_volatile_ctrl = zcxx_g_volatile_ctrl,
+ .s_ctrl = zcxx_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+ static const u8 gamma[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 4,
+ [SENSOR_CS2102] = 4,
+ [SENSOR_CS2102K] = 5,
+ [SENSOR_GC0303] = 3,
+ [SENSOR_GC0305] = 4,
+ [SENSOR_HDCS2020] = 4,
+ [SENSOR_HV7131B] = 4,
+ [SENSOR_HV7131R] = 4,
+ [SENSOR_ICM105A] = 4,
+ [SENSOR_MC501CB] = 4,
+ [SENSOR_MT9V111_1] = 4,
+ [SENSOR_MT9V111_3] = 4,
+ [SENSOR_OV7620] = 3,
+ [SENSOR_OV7630C] = 4,
+ [SENSOR_PAS106] = 4,
+ [SENSOR_PAS202B] = 4,
+ [SENSOR_PB0330] = 4,
+ [SENSOR_PO2030] = 4,
+ [SENSOR_TAS5130C] = 3,
+ };
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 8);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]);
+ if (sd->sensor == SENSOR_HV7131R)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
+ sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (sd->sensor != SENSOR_OV7630C)
+ sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 3, 1,
+ sd->sensor == SENSOR_PO2030 ? 0 : 2);
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1,
+ jpeg_qual[REG08_DEF >> 1]);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(3, &sd->gamma);
+ if (sd->sensor == SENSOR_HV7131R)
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+ 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;
+ struct cam *cam;
+ int sensor;
+ static const u8 mode_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 2,
+ [SENSOR_CS2102] = 1,
+ [SENSOR_CS2102K] = 1,
+ [SENSOR_GC0303] = 1,
+ [SENSOR_GC0305] = 1,
+ [SENSOR_HDCS2020] = 1,
+ [SENSOR_HV7131B] = 1,
+ [SENSOR_HV7131R] = 1,
+ [SENSOR_ICM105A] = 1,
+ [SENSOR_MC501CB] = 2,
+ [SENSOR_MT9V111_1] = 1,
+ [SENSOR_MT9V111_3] = 1,
+ [SENSOR_OV7620] = 2,
+ [SENSOR_OV7630C] = 1,
+ [SENSOR_PAS106] = 0,
+ [SENSOR_PAS202B] = 1,
+ [SENSOR_PB0330] = 1,
+ [SENSOR_PO2030] = 1,
+ [SENSOR_TAS5130C] = 1,
+ };
+
+ sensor = zcxx_probeSensor(gspca_dev);
+ if (sensor >= 0)
+ PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
+ if ((unsigned) force_sensor < SENSOR_MAX) {
+ sd->sensor = force_sensor;
+ PDEBUG(D_PROBE, "sensor forced to %d", force_sensor);
+ } else {
+ switch (sensor) {
+ case -1:
+ switch (sd->sensor) {
+ case SENSOR_MC501CB:
+ PDEBUG(D_PROBE, "Sensor MC501CB");
+ break;
+ case SENSOR_GC0303:
+ PDEBUG(D_PROBE, "Sensor GC0303");
+ break;
+ default:
+ pr_warn("Unknown sensor - set to TAS5130C\n");
+ sd->sensor = SENSOR_TAS5130C;
+ }
+ break;
+ case 0:
+ /* check the sensor type */
+ sensor = i2c_read(gspca_dev, 0x00);
+ PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
+ switch (sensor) {
+ case 0: /* hv7131b */
+ case 1: /* hv7131e */
+ PDEBUG(D_PROBE, "Find Sensor HV7131B");
+ sd->sensor = SENSOR_HV7131B;
+ break;
+ default:
+/* case 2: * hv7131r */
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
+ break;
+ }
+ break;
+ case 0x02:
+ PDEBUG(D_PROBE, "Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
+ break;
+ case 0x04:
+ PDEBUG(D_PROBE, "Find Sensor CS2102");
+ sd->sensor = SENSOR_CS2102;
+ break;
+ case 0x08:
+ PDEBUG(D_PROBE, "Find Sensor HDCS2020");
+ sd->sensor = SENSOR_HDCS2020;
+ break;
+ case 0x0a:
+ PDEBUG(D_PROBE,
+ "Find Sensor PB0330. Chip revision %x",
+ sd->chip_revision);
+ sd->sensor = SENSOR_PB0330;
+ break;
+ case 0x0c:
+ PDEBUG(D_PROBE, "Find Sensor ICM105A");
+ sd->sensor = SENSOR_ICM105A;
+ break;
+ case 0x0e:
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
+ break;
+ case 0x0f:
+ PDEBUG(D_PROBE, "Find Sensor PAS106");
+ sd->sensor = SENSOR_PAS106;
+ break;
+ case 0x10:
+ case 0x12:
+ PDEBUG(D_PROBE, "Find Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130C;
+ break;
+ case 0x11:
+ PDEBUG(D_PROBE, "Find Sensor HV7131R");
+ sd->sensor = SENSOR_HV7131R;
+ break;
+ case 0x13:
+ case 0x15:
+ PDEBUG(D_PROBE,
+ "Sensor MT9V111. Chip revision %04x",
+ sd->chip_revision);
+ sd->sensor = sd->bridge == BRIDGE_ZC301
+ ? SENSOR_MT9V111_1
+ : SENSOR_MT9V111_3;
+ break;
+ case 0x14:
+ PDEBUG(D_PROBE,
+ "Find Sensor CS2102K?. Chip revision %x",
+ sd->chip_revision);
+ sd->sensor = SENSOR_CS2102K;
+ break;
+ case 0x16:
+ PDEBUG(D_PROBE, "Find Sensor ADCM2700");
+ sd->sensor = SENSOR_ADCM2700;
+ break;
+ case 0x29:
+ PDEBUG(D_PROBE, "Find Sensor GC0305");
+ sd->sensor = SENSOR_GC0305;
+ break;
+ case 0x0303:
+ PDEBUG(D_PROBE, "Sensor GC0303");
+ sd->sensor = SENSOR_GC0303;
+ break;
+ case 0x2030:
+ PDEBUG(D_PROBE, "Find Sensor PO2030");
+ sd->sensor = SENSOR_PO2030;
+ break;
+ case 0x7620:
+ PDEBUG(D_PROBE, "Find Sensor OV7620");
+ sd->sensor = SENSOR_OV7620;
+ break;
+ case 0x7631:
+ PDEBUG(D_PROBE, "Find Sensor OV7630C");
+ sd->sensor = SENSOR_OV7630C;
+ break;
+ case 0x7648:
+ PDEBUG(D_PROBE, "Find Sensor OV7648");
+ sd->sensor = SENSOR_OV7620; /* same sensor (?) */
+ break;
+ default:
+ pr_err("Unknown sensor %04x\n", sensor);
+ return -EINVAL;
+ }
+ }
+ if (sensor < 0x20) {
+ if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
+ reg_w(gspca_dev, 0x02, 0x0010);
+ reg_r(gspca_dev, 0x0010);
+ }
+
+ cam = &gspca_dev->cam;
+ switch (mode_tb[sd->sensor]) {
+ case 0:
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ break;
+ case 1:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+ break;
+ default:
+/* case 2: */
+ cam->cam_mode = broken_vga_mode;
+ cam->nmodes = ARRAY_SIZE(broken_vga_mode);
+ break;
+ }
+
+ /* switch off the led */
+ reg_w(gspca_dev, 0x01, 0x0000);
+ return gspca_dev->usb_err;
+}
+
+static int sd_pre_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0;
+ return 0;
+}
+
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+ static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+ [SENSOR_ADCM2700] =
+ {adcm2700_Initial, adcm2700_InitialScale},
+ [SENSOR_CS2102] =
+ {cs2102_Initial, cs2102_InitialScale},
+ [SENSOR_CS2102K] =
+ {cs2102K_Initial, cs2102K_InitialScale},
+ [SENSOR_GC0303] =
+ {gc0303_Initial, gc0303_InitialScale},
+ [SENSOR_GC0305] =
+ {gc0305_Initial, gc0305_InitialScale},
+ [SENSOR_HDCS2020] =
+ {hdcs2020_Initial, hdcs2020_InitialScale},
+ [SENSOR_HV7131B] =
+ {hv7131b_Initial, hv7131b_InitialScale},
+ [SENSOR_HV7131R] =
+ {hv7131r_Initial, hv7131r_InitialScale},
+ [SENSOR_ICM105A] =
+ {icm105a_Initial, icm105a_InitialScale},
+ [SENSOR_MC501CB] =
+ {mc501cb_Initial, mc501cb_InitialScale},
+ [SENSOR_MT9V111_1] =
+ {mt9v111_1_Initial, mt9v111_1_InitialScale},
+ [SENSOR_MT9V111_3] =
+ {mt9v111_3_Initial, mt9v111_3_InitialScale},
+ [SENSOR_OV7620] =
+ {ov7620_Initial, ov7620_InitialScale},
+ [SENSOR_OV7630C] =
+ {ov7630c_Initial, ov7630c_InitialScale},
+ [SENSOR_PAS106] =
+ {pas106b_Initial, pas106b_InitialScale},
+ [SENSOR_PAS202B] =
+ {pas202b_Initial, pas202b_InitialScale},
+ [SENSOR_PB0330] =
+ {pb0330_Initial, pb0330_InitialScale},
+ [SENSOR_PO2030] =
+ {po2030_Initial, po2030_InitialScale},
+ [SENSOR_TAS5130C] =
+ {tas5130c_Initial, tas5130c_InitialScale},
+ };
+
+ /* create the JPEG header */
+ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+
+ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ zcxx_probeSensor(gspca_dev);
+ break;
+ case SENSOR_PAS106:
+ usb_exchange(gspca_dev, pas106b_Initial_com);
+ break;
+ }
+ usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_PO2030:
+ case SENSOR_TAS5130C:
+ case SENSOR_GC0303:
+/* msleep(100); * ?? */
+ reg_r(gspca_dev, 0x0002); /* --> 0x40 */
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ if (sd->sensor == SENSOR_TAS5130C)
+ break;
+ reg_w(gspca_dev, 0x0d, 0x003a);
+ reg_w(gspca_dev, 0x02, 0x003b);
+ reg_w(gspca_dev, 0x00, 0x0038);
+ break;
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ reg_w(gspca_dev, 0x03, 0x003b);
+ reg_w(gspca_dev, 0x0c, 0x003a);
+ reg_w(gspca_dev, 0x0b, 0x0039);
+ if (sd->sensor == SENSOR_HV7131R)
+ reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN);
+ break;
+ }
+
+ setmatrix(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_OV7620:
+ reg_r(gspca_dev, 0x0008);
+ reg_w(gspca_dev, 0x00, 0x0008);
+ break;
+ case SENSOR_PAS202B:
+ case SENSOR_GC0305:
+ case SENSOR_HV7131R:
+ case SENSOR_TAS5130C:
+ reg_r(gspca_dev, 0x0008);
+ /* fall thru */
+ case SENSOR_PO2030:
+ reg_w(gspca_dev, 0x03, 0x0008);
+ break;
+ }
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+
+ /* set the gamma tables when not set */
+ switch (sd->sensor) {
+ case SENSOR_CS2102K: /* gamma set in xxx_Initial */
+ case SENSOR_HDCS2020:
+ case SENSOR_OV7630C:
+ break;
+ default:
+ setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma),
+ v4l2_ctrl_g_ctrl(sd->brightness),
+ v4l2_ctrl_g_ctrl(sd->contrast));
+ break;
+ }
+ setmatrix(gspca_dev); /* one more time? */
+ switch (sd->sensor) {
+ case SENSOR_OV7620:
+ case SENSOR_PAS202B:
+ reg_r(gspca_dev, 0x0180); /* from win */
+ reg_w(gspca_dev, 0x00, 0x0180);
+ break;
+ }
+ setquality(gspca_dev);
+ /* Start with BRC disabled, transfer_update will enable it if needed */
+ reg_w(gspca_dev, 0x00, 0x0007);
+ if (sd->plfreq)
+ setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ reg_w(gspca_dev, 0x02, 0x0180);
+ /* ms-win + */
+ reg_w(gspca_dev, 0x40, 0x0117);
+ break;
+ case SENSOR_HV7131R:
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+ reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
+ break;
+ case SENSOR_GC0305:
+ case SENSOR_TAS5130C:
+ reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ /* fall thru */
+ case SENSOR_PAS202B:
+ case SENSOR_PO2030:
+/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
+ reg_r(gspca_dev, 0x0180);
+ break;
+ case SENSOR_OV7620:
+ reg_w(gspca_dev, 0x09, 0x01ad);
+ reg_w(gspca_dev, 0x15, 0x01ae);
+ i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
+ i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
+ /*fixme: returned value to send? */
+ reg_w(gspca_dev, 0x40, 0x0117);
+ reg_r(gspca_dev, 0x0180);
+ break;
+ }
+
+ setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
+
+ /* Start the transfer parameters update thread */
+ sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME);
+ queue_work(sd->work_thread, &sd->work);
+
+ 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 *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;
+ }
+ if (!gspca_dev->present)
+ return;
+ send_unknown(gspca_dev, sd->sensor);
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data,
+ int len)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* check the JPEG end of frame */
+ if (len >= 3
+ && data[len - 3] == 0xff && data[len - 2] == 0xd9) {
+/*fixme: what does the last byte mean?*/
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, len - 1);
+ return;
+ }
+
+ /* check the JPEG start of a frame */
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ /* put the JPEG header in the new frame */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* remove the webcam's header:
+ * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
+ * - 'ss ss' is the frame sequence number (BE)
+ * - 'ww ww' and 'hh hh' are the window dimensions (BE)
+ * - 'pp pp' is the packet sequence number (BE)
+ */
+ data += 18;
+ len -= 18;
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+}
+
+static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ const struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ return v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+}
+
+static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ struct v4l2_jpegcompression *jcomp)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ memset(jcomp, 0, sizeof *jcomp);
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
+ | V4L2_JPEG_MARKER_DQT;
+ return 0;
+}
+
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* interrupt packet data */
+ int len) /* interrput packet length */
+{
+ if (len == 8 && data[4] == 1) {
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct sd_desc sd_desc = {
+ .name = KBUILD_MODNAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_pre_start,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .get_jcomp = sd_get_jcomp,
+ .set_jcomp = sd_set_jcomp,
+#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+ .int_pkt_scan = sd_int_pkt_scan,
+#endif
+};
+
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x03f0, 0x1b07)},
+ {USB_DEVICE(0x041e, 0x041e)},
+ {USB_DEVICE(0x041e, 0x4017)},
+ {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x401e)},
+ {USB_DEVICE(0x041e, 0x401f)},
+ {USB_DEVICE(0x041e, 0x4022)},
+ {USB_DEVICE(0x041e, 0x4029)},
+ {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x041e, 0x4036)},
+ {USB_DEVICE(0x041e, 0x403a)},
+ {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303},
+ {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303},
+ {USB_DEVICE(0x0458, 0x7007)},
+ {USB_DEVICE(0x0458, 0x700c)},
+ {USB_DEVICE(0x0458, 0x700f)},
+ {USB_DEVICE(0x0461, 0x0a00)},
+ {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
+ {USB_DEVICE(0x046d, 0x08a0)},
+ {USB_DEVICE(0x046d, 0x08a1)},
+ {USB_DEVICE(0x046d, 0x08a2)},
+ {USB_DEVICE(0x046d, 0x08a3)},
+ {USB_DEVICE(0x046d, 0x08a6)},
+ {USB_DEVICE(0x046d, 0x08a7)},
+ {USB_DEVICE(0x046d, 0x08a9)},
+ {USB_DEVICE(0x046d, 0x08aa)},
+ {USB_DEVICE(0x046d, 0x08ac)},
+ {USB_DEVICE(0x046d, 0x08ad)},
+ {USB_DEVICE(0x046d, 0x08ae)},
+ {USB_DEVICE(0x046d, 0x08af)},
+ {USB_DEVICE(0x046d, 0x08b9)},
+ {USB_DEVICE(0x046d, 0x08d7)},
+ {USB_DEVICE(0x046d, 0x08d8)},
+ {USB_DEVICE(0x046d, 0x08d9)},
+ {USB_DEVICE(0x046d, 0x08da)},
+ {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB},
+ {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x055f, 0xc005)},
+ {USB_DEVICE(0x055f, 0xd003)},
+ {USB_DEVICE(0x055f, 0xd004)},
+ {USB_DEVICE(0x0698, 0x2003)},
+ {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x301b)},
+ {USB_DEVICE(0x0ac8, 0x303b)},
+ {USB_DEVICE(0x0ac8, 0x305b)},
+ {USB_DEVICE(0x0ac8, 0x307b)},
+ {USB_DEVICE(0x10fd, 0x0128)},
+ {USB_DEVICE(0x10fd, 0x804d)},
+ {USB_DEVICE(0x10fd, 0x8050)},
+ {} /* end of entry */
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- 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);
+}
+
+/* USB driver */
+static struct usb_driver sd_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
+
+module_param(force_sensor, int, 0644);
+MODULE_PARM_DESC(force_sensor,
+ "Force sensor. Only for experts!!!");
diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig
index de247f3c7d05..de247f3c7d05 100644
--- a/drivers/media/video/hdpvr/Kconfig
+++ b/drivers/media/usb/hdpvr/Kconfig
diff --git a/drivers/media/usb/hdpvr/Makefile b/drivers/media/usb/hdpvr/Makefile
new file mode 100644
index 000000000000..9b8d1463c526
--- /dev/null
+++ b/drivers/media/usb/hdpvr/Makefile
@@ -0,0 +1,7 @@
+hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
+
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
+
+ccflags-y += -Idrivers/media/i2c
+
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c
index ae8f229d1141..ae8f229d1141 100644
--- a/drivers/media/video/hdpvr/hdpvr-control.c
+++ b/drivers/media/usb/hdpvr/hdpvr-control.c
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index 304f43ef59eb..304f43ef59eb 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 82e819fa91c0..82e819fa91c0 100644
--- a/drivers/media/video/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
new file mode 100644
index 000000000000..da6b77912222
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -0,0 +1,1289 @@
+/*
+ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "hdpvr.h"
+
+#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
+
+#define print_buffer_status() { \
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
+ "%s:%d buffer stat: %d free, %d proc\n", \
+ __func__, __LINE__, \
+ list_size(&dev->free_buff_list), \
+ list_size(&dev->rec_buff_list)); }
+
+struct hdpvr_fh {
+ struct hdpvr_device *dev;
+};
+
+static uint list_size(struct list_head *list)
+{
+ struct list_head *tmp;
+ uint count = 0;
+
+ list_for_each(tmp, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*=========================================================================*/
+/* urb callback */
+static void hdpvr_read_bulk_callback(struct urb *urb)
+{
+ struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
+ struct hdpvr_device *dev = buf->dev;
+
+ /* marking buffer as received and wake waiting */
+ buf->status = BUFSTAT_READY;
+ wake_up_interruptible(&dev->wait_data);
+}
+
+/*=========================================================================*/
+/* bufffer bits */
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_cancel_queue(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+
+ list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
+ usb_kill_urb(buf->urb);
+ buf->status = BUFSTAT_AVAILABLE;
+ }
+
+ list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
+
+ return 0;
+}
+
+static int hdpvr_free_queue(struct list_head *q)
+{
+ struct list_head *tmp;
+ struct list_head *p;
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+
+ for (p = q->next; p != q;) {
+ buf = list_entry(p, struct hdpvr_buffer, buff_list);
+
+ urb = buf->urb;
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ tmp = p->next;
+ list_del(p);
+ kfree(buf);
+ p = tmp;
+ }
+
+ return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_free_buffers(struct hdpvr_device *dev)
+{
+ hdpvr_cancel_queue(dev);
+
+ hdpvr_free_queue(&dev->free_buff_list);
+ hdpvr_free_queue(&dev->rec_buff_list);
+
+ return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
+{
+ uint i;
+ int retval = -ENOMEM;
+ u8 *mem;
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "allocating %u buffers\n", count);
+
+ for (i = 0; i < count; i++) {
+
+ buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
+ if (!buf) {
+ v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
+ goto exit;
+ }
+ buf->dev = dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
+ goto exit_urb;
+ }
+ buf->urb = urb;
+
+ mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!mem) {
+ v4l2_err(&dev->v4l2_dev,
+ "cannot allocate usb transfer buffer\n");
+ goto exit_urb_buffer;
+ }
+
+ usb_fill_bulk_urb(buf->urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
+ mem, dev->bulk_in_size,
+ hdpvr_read_bulk_callback, buf);
+
+ buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ buf->status = BUFSTAT_AVAILABLE;
+ list_add_tail(&buf->buff_list, &dev->free_buff_list);
+ }
+ return 0;
+exit_urb_buffer:
+ usb_free_urb(urb);
+exit_urb:
+ kfree(buf);
+exit:
+ hdpvr_free_buffers(dev);
+ return retval;
+}
+
+static int hdpvr_submit_buffers(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+ struct urb *urb;
+ int ret = 0, err_count = 0;
+
+ mutex_lock(&dev->io_mutex);
+
+ while (dev->status == STATUS_STREAMING &&
+ !list_empty(&dev->free_buff_list)) {
+
+ buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
+ buff_list);
+ if (buf->status != BUFSTAT_AVAILABLE) {
+ v4l2_err(&dev->v4l2_dev,
+ "buffer not marked as available\n");
+ ret = -EFAULT;
+ goto err;
+ }
+
+ urb = buf->urb;
+ urb->status = 0;
+ urb->actual_length = 0;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "usb_submit_urb in %s returned %d\n",
+ __func__, ret);
+ if (++err_count > 2)
+ break;
+ continue;
+ }
+ buf->status = BUFSTAT_INPROGRESS;
+ list_move_tail(&buf->buff_list, &dev->rec_buff_list);
+ }
+err:
+ print_buffer_status();
+ mutex_unlock(&dev->io_mutex);
+ return ret;
+}
+
+static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
+{
+ struct hdpvr_buffer *buf;
+
+ mutex_lock(&dev->io_mutex);
+
+ if (list_empty(&dev->rec_buff_list)) {
+ mutex_unlock(&dev->io_mutex);
+ return NULL;
+ }
+
+ buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
+ buff_list);
+ mutex_unlock(&dev->io_mutex);
+
+ return buf;
+}
+
+static void hdpvr_transmit_buffers(struct work_struct *work)
+{
+ struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
+ worker);
+
+ while (dev->status == STATUS_STREAMING) {
+
+ if (hdpvr_submit_buffers(dev)) {
+ v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
+ goto error;
+ }
+ if (wait_event_interruptible(dev->wait_buffer,
+ !list_empty(&dev->free_buff_list) ||
+ dev->status != STATUS_STREAMING))
+ goto error;
+ }
+
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "transmit worker exited\n");
+ return;
+error:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "transmit buffers errored\n");
+ dev->status = STATUS_ERROR;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_start_streaming(struct hdpvr_device *dev)
+{
+ int ret;
+ struct hdpvr_video_info *vidinf;
+
+ if (dev->status == STATUS_STREAMING)
+ return 0;
+ else if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ vidinf = get_video_info(dev);
+
+ if (vidinf) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "video signal: %dx%d@%dhz\n", vidinf->width,
+ vidinf->height, vidinf->fps);
+ kfree(vidinf);
+
+ /* start streaming 2 request */
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "encoder start control request returned %d\n", ret);
+
+ hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
+
+ dev->status = STATUS_STREAMING;
+
+ INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
+ queue_work(dev->workqueue, &dev->worker);
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "streaming started\n");
+
+ return 0;
+ }
+ msleep(250);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "no video signal at input %d\n", dev->options.video_input);
+ return -EAGAIN;
+}
+
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_stop_streaming(struct hdpvr_device *dev)
+{
+ int actual_length;
+ uint c = 0;
+ u8 *buf;
+
+ if (dev->status == STATUS_IDLE)
+ return 0;
+ else if (dev->status != STATUS_STREAMING)
+ return -EAGAIN;
+
+ buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
+ if (!buf)
+ v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
+ "for emptying the internal device buffer. "
+ "Next capture start will be slow\n");
+
+ dev->status = STATUS_SHUTTING_DOWN;
+ hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
+ mutex_unlock(&dev->io_mutex);
+
+ wake_up_interruptible(&dev->wait_buffer);
+ msleep(50);
+
+ flush_workqueue(dev->workqueue);
+
+ mutex_lock(&dev->io_mutex);
+ /* kill the still outstanding urbs */
+ hdpvr_cancel_queue(dev);
+
+ /* emptying the device buffer beforeshutting it down */
+ while (buf && ++c < 500 &&
+ !usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
+ buf, dev->bulk_in_size, &actual_length,
+ BULK_URB_TIMEOUT)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "%2d: got %d bytes\n", c, actual_length);
+ }
+ kfree(buf);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "used %d urbs to empty device buffers\n", c-1);
+ msleep(10);
+
+ dev->status = STATUS_IDLE;
+
+ return 0;
+}
+
+
+/*=======================================================================*/
+/*
+ * video 4 linux 2 file operations
+ */
+
+static int hdpvr_open(struct file *file)
+{
+ struct hdpvr_device *dev;
+ struct hdpvr_fh *fh;
+ int retval = -ENOMEM;
+
+ dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
+ if (!dev) {
+ pr_err("open failing with with ENODEV\n");
+ retval = -ENODEV;
+ goto err;
+ }
+
+ fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
+ if (!fh) {
+ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
+ goto err;
+ }
+ /* lock the device to allow correctly handling errors
+ * in resumption */
+ mutex_lock(&dev->io_mutex);
+ dev->open_count++;
+ mutex_unlock(&dev->io_mutex);
+
+ fh->dev = dev;
+
+ /* save our object in the file's private structure */
+ file->private_data = fh;
+
+ retval = 0;
+err:
+ return retval;
+}
+
+static int hdpvr_release(struct file *file)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->io_mutex);
+ if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
+ hdpvr_stop_streaming(dev);
+
+ mutex_unlock(&dev->io_mutex);
+
+ return 0;
+}
+
+/*
+ * hdpvr_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
+ loff_t *pos)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ struct hdpvr_buffer *buf = NULL;
+ struct urb *urb;
+ unsigned int ret = 0;
+ int rem, cnt;
+
+ if (*pos)
+ return -ESPIPE;
+
+ if (!dev)
+ return -ENODEV;
+
+ mutex_lock(&dev->io_mutex);
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ ret = -EIO;
+ msleep(200);
+ dev->status = STATUS_IDLE;
+ mutex_unlock(&dev->io_mutex);
+ goto err;
+ }
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ /* wait for the first buffer */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ if (wait_event_interruptible(dev->wait_data,
+ hdpvr_get_next_buffer(dev)))
+ return -ERESTARTSYS;
+ }
+
+ buf = hdpvr_get_next_buffer(dev);
+
+ while (count > 0 && buf) {
+
+ if (buf->status != BUFSTAT_READY &&
+ dev->status != STATUS_DISCONNECTED) {
+ /* return nonblocking */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ if (wait_event_interruptible(dev->wait_data,
+ buf->status == BUFSTAT_READY)) {
+ ret = -ERESTARTSYS;
+ goto err;
+ }
+ }
+
+ if (buf->status != BUFSTAT_READY)
+ break;
+
+ /* set remaining bytes to copy */
+ urb = buf->urb;
+ rem = urb->actual_length - buf->pos;
+ cnt = rem > count ? count : rem;
+
+ if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
+ cnt)) {
+ v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
+ if (!ret)
+ ret = -EFAULT;
+ goto err;
+ }
+
+ buf->pos += cnt;
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+
+ /* finished, take next buffer */
+ if (buf->pos == urb->actual_length) {
+ mutex_lock(&dev->io_mutex);
+ buf->pos = 0;
+ buf->status = BUFSTAT_AVAILABLE;
+
+ list_move_tail(&buf->buff_list, &dev->free_buff_list);
+
+ print_buffer_status();
+
+ mutex_unlock(&dev->io_mutex);
+
+ wake_up_interruptible(&dev->wait_buffer);
+
+ buf = hdpvr_get_next_buffer(dev);
+ }
+ }
+err:
+ if (!ret && !buf)
+ ret = -EAGAIN;
+ return ret;
+}
+
+static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
+{
+ struct hdpvr_buffer *buf = NULL;
+ struct hdpvr_fh *fh = filp->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ unsigned int mask = 0;
+
+ mutex_lock(&dev->io_mutex);
+
+ if (!video_is_registered(dev->video_dev)) {
+ mutex_unlock(&dev->io_mutex);
+ return -EIO;
+ }
+
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ dev->status = STATUS_IDLE;
+ }
+
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ buf = hdpvr_get_next_buffer(dev);
+ /* only wait if no data is available */
+ if (!buf || buf->status != BUFSTAT_READY) {
+ poll_wait(filp, &dev->wait_data, wait);
+ buf = hdpvr_get_next_buffer(dev);
+ }
+ if (buf && buf->status == BUFSTAT_READY)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+
+static const struct v4l2_file_operations hdpvr_fops = {
+ .owner = THIS_MODULE,
+ .open = hdpvr_open,
+ .release = hdpvr_release,
+ .read = hdpvr_read,
+ .poll = hdpvr_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/*=======================================================================*/
+/*
+ * V4L2 ioctl handling
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "hdpvr");
+ strcpy(cap->card, "Hauppauge HD PVR");
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *private_data,
+ v4l2_std_id *std)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ u8 std_type = 1;
+
+ if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
+ std_type = 0;
+
+ return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
+}
+
+static const char *iname[] = {
+ [HDPVR_COMPONENT] = "Component",
+ [HDPVR_SVIDEO] = "S-Video",
+ [HDPVR_COMPOSITE] = "Composite",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strncpy(i->name, iname[n], sizeof(i->name) - 1);
+ i->name[sizeof(i->name) - 1] = '\0';
+
+ i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
+
+ i->std = dev->video_dev->tvnorms;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *private_data,
+ unsigned int index)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ if (index >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
+ if (!retval)
+ dev->options.video_input = index;
+
+ return retval;
+}
+
+static int vidioc_g_input(struct file *file, void *private_data,
+ unsigned int *index)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ *index = dev->options.video_input;
+ return 0;
+}
+
+
+static const char *audio_iname[] = {
+ [HDPVR_RCA_FRONT] = "RCA front",
+ [HDPVR_RCA_BACK] = "RCA back",
+ [HDPVR_SPDIF] = "SPDIF",
+};
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ unsigned int n;
+
+ n = audio->index;
+ if (n >= HDPVR_AUDIO_INPUTS)
+ return -EINVAL;
+
+ audio->capability = V4L2_AUDCAP_STEREO;
+
+ strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
+ audio->name[sizeof(audio->name) - 1] = '\0';
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *private_data,
+ const struct v4l2_audio *audio)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ if (audio->index >= HDPVR_AUDIO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
+ if (!retval)
+ dev->options.audio_input = audio->index;
+
+ return retval;
+}
+
+static int vidioc_g_audio(struct file *file, void *private_data,
+ struct v4l2_audio *audio)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ audio->index = dev->options.audio_input;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
+ audio->name[sizeof(audio->name) - 1] = '\0';
+ return 0;
+}
+
+static const s32 supported_v4l2_ctrls[] = {
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_SHARPNESS,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+};
+
+static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
+ int ac3, int fw_ver)
+{
+ int err;
+
+ if (fw_ver > 0x15) {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ } else {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ case V4L2_CID_SHARPNESS:
+ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+ }
+ }
+
+ switch (qc->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
+ ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
+ : V4L2_MPEG_AUDIO_ENCODING_AAC,
+ 1, V4L2_MPEG_AUDIO_ENCODING_AAC);
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+
+/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
+/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return v4l2_ctrl_query_fill(
+ qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
+ 6500000);
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
+ 9000000);
+ if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
+ qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vidioc_queryctrl(struct file *file, void *private_data,
+ struct v4l2_queryctrl *qc)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, next;
+ u32 id = qc->id;
+
+ memset(qc, 0, sizeof(*qc));
+
+ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
+ qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+ for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
+ if (next) {
+ if (qc->id < supported_v4l2_ctrls[i])
+ qc->id = supported_v4l2_ctrls[i];
+ else
+ continue;
+ }
+
+ if (qc->id == supported_v4l2_ctrls[i])
+ return fill_queryctrl(&dev->options, qc,
+ dev->flags & HDPVR_FLAG_AC3_CAP,
+ dev->fw_ver);
+
+ if (qc->id < supported_v4l2_ctrls[i])
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *private_data,
+ struct v4l2_control *ctrl)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dev->options.brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dev->options.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = dev->options.saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dev->options.hue;
+ break;
+ case V4L2_CID_SHARPNESS:
+ ctrl->value = dev->options.sharpness;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *private_data,
+ struct v4l2_control *ctrl)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int retval;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
+ if (!retval)
+ dev->options.brightness = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
+ if (!retval)
+ dev->options.contrast = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
+ if (!retval)
+ dev->options.saturation = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
+ if (!retval)
+ dev->options.hue = ctrl->value;
+ break;
+ case V4L2_CID_SHARPNESS:
+ retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
+ if (!retval)
+ dev->options.sharpness = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+
+static int hdpvr_get_ctrl(struct hdpvr_options *opt,
+ struct v4l2_ext_control *ctrl)
+{
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ ctrl->value = opt->audio_codec;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
+ ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+ : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ ctrl->value = opt->bitrate * 100000;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ ctrl->value = opt->peak_bitrate * 100000;
+ break;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_get_ctrl(&dev->options, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+
+static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
+ (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
+ ret = 0;
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* if (ctrl->value == 0 || ctrl->value == 128) */
+/* ret = 0; */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
+ ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ ret = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ {
+ uint bitrate = ctrl->value / 100000;
+ if (bitrate >= 10 && bitrate <= 135)
+ ret = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ {
+ uint peak_bitrate = ctrl->value / 100000;
+ if (peak_bitrate >= 10 && peak_bitrate <= 202)
+ ret = 0;
+ break;
+ }
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+ ret = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_try_ctrl(ctrl,
+ dev->flags & HDPVR_FLAG_AC3_CAP);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+ }
+
+ return -EINVAL;
+}
+
+
+static int hdpvr_set_ctrl(struct hdpvr_device *dev,
+ struct v4l2_ext_control *ctrl)
+{
+ struct hdpvr_options *opt = &dev->options;
+ int ret = 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+ opt->audio_codec = ctrl->value;
+ ret = hdpvr_set_audio(dev, opt->audio_input,
+ opt->audio_codec);
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ break;
+/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
+/* opt->gop_mode |= 0x2; */
+/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/* opt->gop_mode); */
+/* } */
+/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
+/* opt->gop_mode &= ~0x2; */
+/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/* opt->gop_mode); */
+/* } */
+/* break; */
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
+ opt->bitrate_mode != HDPVR_CONSTANT) {
+ opt->bitrate_mode = HDPVR_CONSTANT;
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ opt->bitrate_mode);
+ }
+ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ opt->bitrate_mode == HDPVR_CONSTANT) {
+ opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ opt->bitrate_mode);
+ }
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE: {
+ uint bitrate = ctrl->value / 100000;
+
+ opt->bitrate = bitrate;
+ if (bitrate >= opt->peak_bitrate)
+ opt->peak_bitrate = bitrate+1;
+
+ hdpvr_set_bitrate(dev);
+ break;
+ }
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
+ uint peak_bitrate = ctrl->value / 100000;
+
+ if (opt->bitrate_mode == HDPVR_CONSTANT)
+ break;
+
+ if (opt->bitrate < peak_bitrate) {
+ opt->peak_bitrate = peak_bitrate;
+ hdpvr_set_bitrate(dev);
+ } else
+ ret = -EINVAL;
+ break;
+ }
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ break;
+ default:
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int i, err = 0;
+
+ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ err = hdpvr_try_ctrl(ctrl,
+ dev->flags & HDPVR_FLAG_AC3_CAP);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ err = hdpvr_set_ctrl(dev, ctrl);
+ if (err) {
+ ctrls->error_idx = i;
+ break;
+ }
+ }
+ return err;
+
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
+ struct v4l2_fmtdesc *f)
+{
+
+ if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
+ struct v4l2_format *f)
+{
+ struct hdpvr_fh *fh = file->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ struct hdpvr_video_info *vid_info;
+
+ if (!dev)
+ return -ENODEV;
+
+ vid_info = get_video_info(dev);
+ if (!vid_info)
+ return -EFAULT;
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.width = vid_info->width;
+ f->fmt.pix.height = vid_info->height;
+ f->fmt.pix.sizeimage = dev->bulk_in_size;
+ f->fmt.pix.colorspace = 0;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.field = V4L2_FIELD_ANY;
+
+ kfree(vid_info);
+ return 0;
+}
+
+static int vidioc_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ struct hdpvr_fh *fh = filp->private_data;
+ struct hdpvr_device *dev = fh->dev;
+ int res;
+
+ mutex_lock(&dev->io_mutex);
+
+ memset(&a->raw, 0, sizeof(a->raw));
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ a->flags = 0;
+ res = hdpvr_start_streaming(dev);
+ break;
+ case V4L2_ENC_CMD_STOP:
+ res = hdpvr_stop_streaming(dev);
+ break;
+ default:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "Unsupported encoder cmd %d\n", a->cmd);
+ res = -EINVAL;
+ }
+ mutex_unlock(&dev->io_mutex);
+ return res;
+}
+
+static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ case V4L2_ENC_CMD_STOP:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_encoder_cmd = vidioc_encoder_cmd,
+ .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
+};
+
+static void hdpvr_device_release(struct video_device *vdev)
+{
+ struct hdpvr_device *dev = video_get_drvdata(vdev);
+
+ hdpvr_delete(dev);
+ mutex_lock(&dev->io_mutex);
+ destroy_workqueue(dev->workqueue);
+ mutex_unlock(&dev->io_mutex);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ /* deregister I2C adapter */
+#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE)
+ mutex_lock(&dev->i2c_mutex);
+ i2c_del_adapter(&dev->i2c_adapter);
+ mutex_unlock(&dev->i2c_mutex);
+#endif /* CONFIG_I2C */
+
+ kfree(dev->usbc_buf);
+ kfree(dev);
+}
+
+static const struct video_device hdpvr_video_template = {
+/* .type = VFL_TYPE_GRABBER, */
+/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
+ .fops = &hdpvr_fops,
+ .release = hdpvr_device_release,
+ .ioctl_ops = &hdpvr_ioctl_ops,
+ .tvnorms =
+ V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B |
+ V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
+ V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
+ V4L2_STD_PAL_60,
+ .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
+ V4L2_STD_PAL_60,
+};
+
+int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
+ int devnum)
+{
+ /* setup and register video device */
+ dev->video_dev = video_device_alloc();
+ if (!dev->video_dev) {
+ v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
+ goto error;
+ }
+
+ *(dev->video_dev) = hdpvr_video_template;
+ strcpy(dev->video_dev->name, "Hauppauge HD PVR");
+ dev->video_dev->parent = parent;
+ video_set_drvdata(dev->video_dev, dev);
+
+ if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
+ v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ return -ENOMEM;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
index fea3c6926997..fea3c6926997 100644
--- a/drivers/media/video/hdpvr/hdpvr.h
+++ b/drivers/media/usb/hdpvr/hdpvr.h
diff --git a/drivers/media/usb/pvrusb2/Kconfig b/drivers/media/usb/pvrusb2/Kconfig
new file mode 100644
index 000000000000..32b11c15bb1a
--- /dev/null
+++ b/drivers/media/usb/pvrusb2/Kconfig
@@ -0,0 +1,65 @@
+config VIDEO_PVRUSB2
+ tristate "Hauppauge WinTV-PVR USB2 support"
+ depends on VIDEO_V4L2 && I2C
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_SAA711X
+ select VIDEO_CX25840
+ select VIDEO_MSP3400
+ select VIDEO_WM8775
+ select VIDEO_CS53L32A
+ ---help---
+ This is a video4linux driver for Conexant 23416 based
+ usb2 personal video recorder devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pvrusb2
+
+config VIDEO_PVRUSB2_SYSFS
+ bool "pvrusb2 sysfs support (EXPERIMENTAL)"
+ default y
+ depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL
+ ---help---
+ This option enables the operation of a sysfs based
+ interface for query and control of the pvrusb2 driver.
+
+ This is not generally needed for v4l applications,
+ although certain applications are optimized to take
+ advantage of this feature.
+
+ If you are in doubt, say Y.
+
+ Note: This feature is experimental and subject to change.
+
+config VIDEO_PVRUSB2_DVB
+ bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
+ default y
+ depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
+ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+
+ This option enables a DVB interface for the pvrusb2 driver.
+ If your device does not support digital television, this
+ feature will have no affect on the driver's operation.
+
+ If you are in doubt, say Y.
+
+config VIDEO_PVRUSB2_DEBUGIFC
+ bool "pvrusb2 debug interface"
+ depends on VIDEO_PVRUSB2_SYSFS
+ ---help---
+ This option enables the inclusion of a debug interface
+ in the pvrusb2 driver, hosted through sysfs.
+
+ You do not need to select this option unless you plan
+ on debugging the driver or performing a manual firmware
+ extraction.
+
+ If you are in doubt, say N.
diff --git a/drivers/media/usb/pvrusb2/Makefile b/drivers/media/usb/pvrusb2/Makefile
new file mode 100644
index 000000000000..ad705547bdce
--- /dev/null
+++ b/drivers/media/usb/pvrusb2/Makefile
@@ -0,0 +1,22 @@
+obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
+obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
+obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
+
+pvrusb2-objs := pvrusb2-i2c-core.o \
+ pvrusb2-audio.o \
+ pvrusb2-encoder.o pvrusb2-video-v4l.o \
+ pvrusb2-eeprom.o \
+ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
+ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
+ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
+ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
+ pvrusb2-cs53l32a.o \
+ $(obj-pvrusb2-dvb-y) \
+ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
+
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
index cc06d5e4adcc..cc06d5e4adcc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/usb/pvrusb2/pvrusb2-audio.h
index e3e63d750891..e3e63d750891 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-audio.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c
index 7c19ff72e6b3..7c19ff72e6b3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/usb/pvrusb2/pvrusb2-context.h
index d657e53bbfa3..d657e53bbfa3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-context.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
index 88320900dbd4..88320900dbd4 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
index 53ba548b72a7..53ba548b72a7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
index 7d5a7139a45a..7d5a7139a45a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
index 794ff90121c7..794ff90121c7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
index c514d0b9ffdc..c514d0b9ffdc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
index e35c2322a08c..e35c2322a08c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/usb/pvrusb2/pvrusb2-debug.h
index be79249f8628..be79249f8628 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debug.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
index 4279ebb811a1..4279ebb811a1 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
index 2f8d46761cd0..2f8d46761cd0 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
new file mode 100644
index 000000000000..adc501d3c287
--- /dev/null
+++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c
@@ -0,0 +1,576 @@
+/*
+ *
+ *
+ * Copyright (C) 2007 Mike Isely <isely@pobox.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+
+This source file should encompass ALL per-device type information for the
+driver. To define a new device, add elements to the pvr2_device_table and
+pvr2_device_desc structures.
+
+*/
+
+#include "pvrusb2-devattr.h"
+#include <linux/usb.h>
+#include <linux/module.h>
+/* This is needed in order to pull in tuner type ids... */
+#include <linux/i2c.h>
+#include <media/tuner.h>
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+#include "pvrusb2-hdw-internal.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "s5h1411.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "tda8290.h"
+#include "tuner-simple.h"
+#endif
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 29xxx */
+
+static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_MSP3400 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+#define PVR2_FIRMWARE_29xxx "v4l-pvrusb2-29xxx-01.fw"
+static const char *pvr2_fw1_names_29xxx[] = {
+ PVR2_FIRMWARE_29xxx,
+};
+
+static const struct pvr2_device_desc pvr2_device_29xxx = {
+ .description = "WinTV PVR USB2 Model 29xxx",
+ .shortname = "29xxx",
+ .client_table.lst = pvr2_cli_29xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_29XXX,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 24xxx */
+
+static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_WM8775 },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+#define PVR2_FIRMWARE_24xxx "v4l-pvrusb2-24xxx-01.fw"
+static const char *pvr2_fw1_names_24xxx[] = {
+ PVR2_FIRMWARE_24xxx,
+};
+
+static const struct pvr2_device_desc pvr2_device_24xxx = {
+ .description = "WinTV PVR USB2 Model 24xxx",
+ .shortname = "24xxx",
+ .client_table.lst = pvr2_cli_24xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_wm8775 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_24XXX,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD2 */
+
+static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+ { .module_id = PVR2_CLIENT_ID_DEMOD },
+};
+
+static const struct pvr2_device_desc pvr2_device_gotview_2 = {
+ .description = "Gotview USB 2.0 DVD 2",
+ .shortname = "gv2",
+ .client_table.lst = pvr2_cli_gotview_2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD Deluxe */
+
+/* (same module list as gotview_2) */
+
+static const struct pvr2_device_desc pvr2_device_gotview_2d = {
+ .description = "Gotview USB 2.0 DVD Deluxe",
+ .shortname = "gv2d",
+ .client_table.lst = pvr2_cli_gotview_2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Terratec Grabster AV400 */
+
+static const struct pvr2_device_client_desc pvr2_cli_av400[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+};
+
+static const struct pvr2_device_desc pvr2_device_av400 = {
+ .description = "Terratec Grabster AV400",
+ .shortname = "av400",
+ .flag_is_experimental = 1,
+ .client_table.lst = pvr2_cli_av400,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400),
+ .flag_has_cx25840 = !0,
+ .flag_has_analogtuner = 0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* OnAir Creator */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3303_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .clock_polarity_flip = 1,
+};
+
+static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+ .frontend_attach = pvr2_lgdt3303_attach,
+ .tuner_attach = pvr2_lgh06xf_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_CS53L32A },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_creator = {
+ .description = "OnAir Creator Hybrid USB tuner",
+ .shortname = "oac",
+ .client_table.lst = pvr2_cli_onair_creator,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator),
+ .default_tuner_type = TUNER_LG_TDVS_H06XF,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_creator_fe_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* OnAir USB 2.0 */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3302_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+};
+
+static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_PHILIPS_FCV1236D);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+ .frontend_attach = pvr2_lgdt3302_attach,
+ .tuner_attach = pvr2_fcv1236d_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = {
+ { .module_id = PVR2_CLIENT_ID_SAA7115 },
+ { .module_id = PVR2_CLIENT_ID_CS53L32A },
+ { .module_id = PVR2_CLIENT_ID_TUNER },
+};
+
+static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
+ .description = "OnAir USB2 Hybrid USB tuner",
+ .shortname = "oa2",
+ .client_table.lst = pvr2_cli_onair_usb2,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2),
+ .default_tuner_type = TUNER_PHILIPS_FCV1236D,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_usb2_fe_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 73xxx */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct tda10048_config hauppauge_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_50,
+ .inversion = TDA10048_INVERSION_ON,
+ .dtv6_if_freq_khz = TDA10048_IF_3300,
+ .dtv7_if_freq_khz = TDA10048_IF_3800,
+ .dtv8_if_freq_khz = TDA10048_IF_4300,
+ .clk_freq_khz = TDA10048_CLK_16000,
+ .disable_gate_access = 1,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_dvb_config = {
+ .std_map = &hauppauge_tda18271_dvbt_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_dvb_config);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+ .frontend_attach = pvr2_tda10048_attach,
+ .tuner_attach = pvr2_73xxx_tda18271_8295_attach,
+};
+#endif
+
+static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = {
+ { .module_id = PVR2_CLIENT_ID_CX25840 },
+ { .module_id = PVR2_CLIENT_ID_TUNER,
+ .i2c_address_list = "\x42"},
+};
+
+#define PVR2_FIRMWARE_73xxx "v4l-pvrusb2-73xxx-01.fw"
+static const char *pvr2_fw1_names_73xxx[] = {
+ PVR2_FIRMWARE_73xxx,
+};
+
+static const struct pvr2_device_desc pvr2_device_73xxx = {
+ .description = "WinTV HVR-1900 Model 73xxx",
+ .shortname = "73xxx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_73xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_73xxx_dvb_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* Hauppauge PVR-USB2 Model 75xxx */
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct s5h1409_config pvr2_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+};
+
+static struct s5h1411_config pvr2_s5h1411_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .vsb_if = S5H1411_IF_44000,
+ .qam_if = S5H1411_IF_4000,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+};
+
+static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_config);
+
+ return 0;
+}
+
+static const struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1409_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+};
+
+static const struct pvr2_dvb_props pvr2_751xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1411_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+};
+#endif
+
+#define PVR2_FIRMWARE_75xxx "v4l-pvrusb2-73xxx-01.fw"
+static const char *pvr2_fw1_names_75xxx[] = {
+ PVR2_FIRMWARE_75xxx,
+};
+
+static const struct pvr2_device_desc pvr2_device_750xx = {
+ .description = "WinTV HVR-1950 Model 750xx",
+ .shortname = "750xx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_750xx_dvb_props,
+#endif
+};
+
+static const struct pvr2_device_desc pvr2_device_751xx = {
+ .description = "WinTV HVR-1950 Model 751xx",
+ .shortname = "751xx",
+ .client_table.lst = pvr2_cli_73xxx,
+ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_fx2_16kb = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+ .ir_scheme = PVR2_IR_SCHEME_ZILOG,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_751xx_dvb_props,
+#endif
+};
+
+
+
+/*------------------------------------------------------------------------*/
+
+struct usb_device_id pvr2_device_table[] = {
+ { USB_DEVICE(0x2040, 0x2900),
+ .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
+ { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */
+ .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
+ { USB_DEVICE(0x2040, 0x2400),
+ .driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
+ { USB_DEVICE(0x1164, 0x0622),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
+ { USB_DEVICE(0x1164, 0x0602),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
+ { USB_DEVICE(0x11ba, 0x1003),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
+ { USB_DEVICE(0x11ba, 0x1001),
+ .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
+ { USB_DEVICE(0x2040, 0x7300),
+ .driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
+ { USB_DEVICE(0x2040, 0x7500),
+ .driver_info = (kernel_ulong_t)&pvr2_device_750xx},
+ { USB_DEVICE(0x2040, 0x7501),
+ .driver_info = (kernel_ulong_t)&pvr2_device_751xx},
+ { USB_DEVICE(0x0ccd, 0x0039),
+ .driver_info = (kernel_ulong_t)&pvr2_device_av400},
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx);
+MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx);
+MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx);
+MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx);
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h
index 273c8d4b3853..273c8d4b3853 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c
index 8c95793433e7..8c95793433e7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h
index 884ff916a352..884ff916a352 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-dvb.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
index 9515f3a68f8f..9515f3a68f8f 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
index cca3216f94cc..cca3216f94cc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c
index e046fdaec5ae..e046fdaec5ae 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h
index 232fefbcd1ac..232fefbcd1ac 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
index 614755ea2ea3..614755ea2ea3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
index 036952f2a3cb..036952f2a3cb 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index fb828ba1dbbe..fb828ba1dbbe 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
index 8060fc666eeb..8060fc666eeb 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
index 885ce11f222d..885ce11f222d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
index 6a75769200bd..6a75769200bd 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c
index 20b6ae0bb40d..20b6ae0bb40d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/usb/pvrusb2/pvrusb2-io.h
index afb7e87c0394..afb7e87c0394 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-io.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
index bba6115c9ae8..bba6115c9ae8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h
index 100e0780e1aa..100e0780e1aa 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/usb/pvrusb2/pvrusb2-main.c
index c1d9bb61cd77..c1d9bb61cd77 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-main.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c
index 453627b07833..453627b07833 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h
index a35c53d0b320..a35c53d0b320 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
index 6ef1335b2858..6ef1335b2858 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
index 6d875bfe7991..6d875bfe7991 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/usb/pvrusb2/pvrusb2-util.h
index 92b75544ee2e..92b75544ee2e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-util.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-util.h
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
new file mode 100644
index 000000000000..db249cad3cd9
--- /dev/null
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c
@@ -0,0 +1,1413 @@
+/*
+ *
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.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; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "pvrusb2-context.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#include "pvrusb2-ioread.h"
+#include <linux/videodev2.h>
+#include <linux/module.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+struct pvr2_v4l2_dev;
+struct pvr2_v4l2_fh;
+struct pvr2_v4l2;
+
+struct pvr2_v4l2_dev {
+ struct video_device devbase; /* MUST be first! */
+ struct pvr2_v4l2 *v4lp;
+ struct pvr2_context_stream *stream;
+ /* Information about this device: */
+ enum pvr2_config config; /* Expected stream format */
+ int v4l_type; /* V4L defined type for this device node */
+ enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
+};
+
+struct pvr2_v4l2_fh {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_dev *pdi;
+ enum v4l2_priority prio;
+ struct pvr2_ioread *rhp;
+ struct file *file;
+ struct pvr2_v4l2 *vhead;
+ struct pvr2_v4l2_fh *vnext;
+ struct pvr2_v4l2_fh *vprev;
+ wait_queue_head_t wait_data;
+ int fw_mode_flag;
+ /* Map contiguous ordinal value to input id */
+ unsigned char *input_map;
+ unsigned int input_cnt;
+};
+
+struct pvr2_v4l2 {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_fh *vfirst;
+ struct pvr2_v4l2_fh *vlast;
+
+ struct v4l2_prio_state prio;
+
+ /* streams - Note that these must be separately, individually,
+ * allocated pointers. This is because the v4l core is going to
+ * manage their deletion - separately, individually... */
+ struct pvr2_v4l2_dev *dev_video;
+ struct pvr2_v4l2_dev *dev_radio;
+};
+
+static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
+static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
+static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(vbi_nr, int, NULL, 0444);
+MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
+
+static struct v4l2_capability pvr_capability ={
+ .driver = "pvrusb2",
+ .card = "Hauppauge WinTV pvr-usb2",
+ .bus_info = "usb",
+ .version = LINUX_VERSION_CODE,
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE),
+};
+
+static struct v4l2_fmtdesc pvr_fmtdesc [] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = V4L2_FMT_FLAG_COMPRESSED,
+ .description = "MPEG1/2",
+ // This should really be V4L2_PIX_FMT_MPEG, but xawtv
+ // breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ }
+};
+
+#define PVR_FORMAT_PIX 0
+#define PVR_FORMAT_VBI 1
+
+static struct v4l2_format pvr_format [] = {
+ [PVR_FORMAT_PIX] = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt = {
+ .pix = {
+ .width = 720,
+ .height = 576,
+ // This should really be V4L2_PIX_FMT_MPEG,
+ // but xawtv breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 0, // doesn't make sense
+ // here
+ //FIXME : Don't know what to put here...
+ .sizeimage = (32*1024),
+ .colorspace = 0, // doesn't make sense here
+ .priv = 0
+ }
+ }
+ },
+ [PVR_FORMAT_VBI] = {
+ .type = V4L2_BUF_TYPE_VBI_CAPTURE,
+ .fmt = {
+ .vbi = {
+ .sampling_rate = 27000000,
+ .offset = 248,
+ .samples_per_line = 1443,
+ .sample_format = V4L2_PIX_FMT_GREY,
+ .start = { 0, 0 },
+ .count = { 0, 0 },
+ .flags = 0,
+ }
+ }
+ }
+};
+
+
+
+/*
+ * This is part of Video 4 Linux API. These procedures handle ioctl() calls.
+ */
+static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
+ sizeof(cap->bus_info));
+ strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
+ return 0;
+}
+
+static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+
+ *p = v4l2_prio_max(&vp->prio);
+ return 0;
+}
+
+static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+
+ return v4l2_prio_change(&vp->prio, &fh->prio, prio);
+}
+
+static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val);
+ *std = val;
+ return ret;
+}
+
+int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std);
+}
+
+static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val);
+ *std = val;
+ return ret;
+}
+
+static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input tmp;
+ unsigned int cnt;
+ int val;
+
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.index = vi->index;
+ if (vi->index >= fh->input_cnt)
+ return -EINVAL;
+ val = fh->input_map[vi->index];
+ switch (val) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_DTV:
+ case PVR2_CVAL_INPUT_RADIO:
+ tmp.type = V4L2_INPUT_TYPE_TUNER;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ tmp.type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cnt = 0;
+ pvr2_ctrl_get_valname(cptr, val,
+ tmp.name, sizeof(tmp.name) - 1, &cnt);
+ tmp.name[cnt] = 0;
+
+ /* Don't bother with audioset, since this driver currently
+ always switches the audio whenever the video is
+ switched. */
+
+ /* Handling std is a tougher problem. It doesn't make
+ sense in cases where a device might be multi-standard.
+ We could just copy out the current value for the
+ standard, but it can change over time. For now just
+ leave it zero. */
+ *vi = tmp;
+ return 0;
+}
+
+static int pvr2_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int val;
+ int ret;
+
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+ val = 0;
+ ret = pvr2_ctrl_get_value(cptr, &val);
+ *i = 0;
+ for (idx = 0; idx < fh->input_cnt; idx++) {
+ if (fh->input_map[idx] == val) {
+ *i = idx;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (inp >= fh->input_cnt)
+ return -EINVAL;
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
+ fh->input_map[inp]);
+}
+
+static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
+{
+ /* pkt: FIXME: We are returning one "fake" input here
+ which could very well be called "whatever_we_like".
+ This is for apps that want to see an audio input
+ just to feel comfortable, as well as to test if
+ it can do stereo or sth. There is actually no guarantee
+ that the actual audio input cannot change behind the app's
+ back, but most applications should not mind that either.
+
+ Hopefully, mplayer people will work with us on this (this
+ whole mess is to support mplayer pvr://), or Hans will come
+ up with a more standard way to say "we have inputs but we
+ don 't want you to change them independent of video" which
+ will sort this mess.
+ */
+
+ if (vin->index > 0)
+ return -EINVAL;
+ strncpy(vin->name, "PVRUSB2 Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
+{
+ /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
+ vin->index = 0;
+ strncpy(vin->name, "PVRUSB2 Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
+
+static int pvr2_s_audio(struct file *file, void *priv, const struct v4l2_audio *vout)
+{
+ if (vout->index)
+ return -EINVAL;
+ return 0;
+}
+
+static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (vt->index != 0)
+ return -EINVAL; /* Only answer for the 1st tuner */
+
+ pvr2_hdw_execute_tuner_poll(hdw);
+ return pvr2_hdw_get_tuner_status(hdw, vt);
+}
+
+static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
+ vt->audmode);
+}
+
+int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned long fv;
+ struct v4l2_tuner vt;
+ int cur_input;
+ struct pvr2_ctrl *ctrlp;
+ int ret;
+
+ ret = pvr2_hdw_get_tuner_status(hdw, &vt);
+ if (ret != 0)
+ return ret;
+ ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+ ret = pvr2_ctrl_get_value(ctrlp, &cur_input);
+ if (ret != 0)
+ return ret;
+ if (vf->type == V4L2_TUNER_RADIO) {
+ if (cur_input != PVR2_CVAL_INPUT_RADIO)
+ pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO);
+ } else {
+ if (cur_input == PVR2_CVAL_INPUT_RADIO)
+ pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV);
+ }
+ fv = vf->frequency;
+ if (vt.capability & V4L2_TUNER_CAP_LOW)
+ fv = (fv * 125) / 2;
+ else
+ fv = fv * 62500;
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
+}
+
+static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int cur_input;
+ struct v4l2_tuner vt;
+ int ret;
+
+ ret = pvr2_hdw_get_tuner_status(hdw, &vt);
+ if (ret != 0)
+ return ret;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY),
+ &val);
+ if (ret != 0)
+ return ret;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
+ &cur_input);
+ if (cur_input == PVR2_CVAL_INPUT_RADIO)
+ vf->type = V4L2_TUNER_RADIO;
+ else
+ vf->type = V4L2_TUNER_ANALOG_TV;
+ if (vt.capability & V4L2_TUNER_CAP_LOW)
+ val = (val * 2) / 125;
+ else
+ val /= 62500;
+ vf->frequency = val;
+ return 0;
+}
+
+static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd)
+{
+ /* Only one format is supported : mpeg.*/
+ if (fd->index != 0)
+ return -EINVAL;
+
+ memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
+ return 0;
+}
+
+static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val;
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format));
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES),
+ &val);
+ vf->fmt.pix.width = val;
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES),
+ &val);
+ vf->fmt.pix.height = val;
+ return 0;
+}
+
+static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int lmin, lmax, ldef;
+ struct pvr2_ctrl *hcp, *vcp;
+ int h = vf->fmt.pix.height;
+ int w = vf->fmt.pix.width;
+
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
+
+ lmin = pvr2_ctrl_get_min(hcp);
+ lmax = pvr2_ctrl_get_max(hcp);
+ pvr2_ctrl_get_def(hcp, &ldef);
+ if (w == -1)
+ w = ldef;
+ else if (w < lmin)
+ w = lmin;
+ else if (w > lmax)
+ w = lmax;
+ lmin = pvr2_ctrl_get_min(vcp);
+ lmax = pvr2_ctrl_get_max(vcp);
+ pvr2_ctrl_get_def(vcp, &ldef);
+ if (h == -1)
+ h = ldef;
+ else if (h < lmin)
+ h = lmin;
+ else if (h > lmax)
+ h = lmax;
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ vf->fmt.pix.width = w;
+ vf->fmt.pix.height = h;
+ return 0;
+}
+
+static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *hcp, *vcp;
+ int ret = pvr2_try_fmt_vid_cap(file, fh, vf);
+
+ if (ret)
+ return ret;
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
+ pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
+ pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
+ return 0;
+}
+
+static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_v4l2_dev *pdi = fh->pdi;
+ int ret;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ return -EPERM;
+ }
+ ret = pvr2_hdw_set_stream_type(hdw, pdi->config);
+ if (ret < 0)
+ return ret;
+ return pvr2_hdw_set_streaming(hdw, !0);
+}
+
+static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ return -EPERM;
+ }
+ return pvr2_hdw_set_streaming(hdw, 0);
+}
+
+static int pvr2_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *cptr;
+ int val;
+
+ if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ cptr = pvr2_hdw_get_ctrl_nextv4l(
+ hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
+ if (cptr)
+ vc->id = pvr2_ctrl_get_v4lid(cptr);
+ } else {
+ cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id);
+ }
+ if (!cptr) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x not implemented here",
+ vc->id);
+ return -EINVAL;
+ }
+
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x mapping name=%s (%s)",
+ vc->id, pvr2_ctrl_get_name(cptr),
+ pvr2_ctrl_get_desc(cptr));
+ strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name));
+ vc->flags = pvr2_ctrl_get_v4lflags(cptr);
+ pvr2_ctrl_get_def(cptr, &val);
+ vc->default_value = val;
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ vc->type = V4L2_CTRL_TYPE_MENU;
+ vc->minimum = 0;
+ vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_bool:
+ vc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ vc->minimum = 0;
+ vc->maximum = 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_int:
+ vc->type = V4L2_CTRL_TYPE_INTEGER;
+ vc->minimum = pvr2_ctrl_get_min(cptr);
+ vc->maximum = pvr2_ctrl_get_max(cptr);
+ vc->step = 1;
+ break;
+ default:
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x name=%s not mappable",
+ vc->id, pvr2_ctrl_get_name(cptr));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned int cnt = 0;
+ int ret;
+
+ ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id),
+ vm->index,
+ vm->name, sizeof(vm->name) - 1,
+ &cnt);
+ vm->name[cnt] = 0;
+ return ret;
+}
+
+static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
+ &val);
+ vc->value = val;
+ return ret;
+}
+
+static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
+ vc->value);
+}
+
+static int pvr2_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int val;
+ int ret;
+
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val);
+ if (ret) {
+ ctls->error_idx = idx;
+ return ret;
+ }
+ /* Ensure that if read as a 64 bit value, the user
+ will still get a hopefully sane value */
+ ctrl->value64 = 0;
+ ctrl->value = val;
+ }
+ return 0;
+}
+
+static int pvr2_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int ret;
+
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id),
+ ctrl->value);
+ if (ret) {
+ ctls->error_idx = idx;
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ struct pvr2_ctrl *pctl;
+ unsigned int idx;
+
+ /* For the moment just validate that the requested control
+ actually exists. */
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
+ if (!pctl) {
+ ctls->error_idx = idx;
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = pvr2_hdw_get_cropcap(hdw, cap);
+ cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+ return ret;
+}
+
+static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.left = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.top = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.width = val;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.height = val;
+ return 0;
+}
+
+static int pvr2_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
+ crop->c.left);
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
+ crop->c.top);
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
+ crop->c.width);
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
+ crop->c.height);
+ if (ret != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int pvr2_log_status(struct file *file, void *priv)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ pvr2_hdw_trigger_module_log(hdw);
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ u64 val;
+ int ret;
+
+ ret = pvr2_hdw_register_access(
+ hdw, &req->match, req->reg,
+ 0, &val);
+ req->val = val;
+ return ret;
+}
+
+static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ u64 val;
+ int ret;
+
+ val = req->val;
+ ret = pvr2_hdw_register_access(
+ hdw, &req->match, req->reg,
+ 1, &val);
+ return ret;
+}
+#endif
+
+static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
+ .vidioc_querycap = pvr2_querycap,
+ .vidioc_g_priority = pvr2_g_priority,
+ .vidioc_s_priority = pvr2_s_priority,
+ .vidioc_s_audio = pvr2_s_audio,
+ .vidioc_g_audio = pvr2_g_audio,
+ .vidioc_enumaudio = pvr2_enumaudio,
+ .vidioc_enum_input = pvr2_enum_input,
+ .vidioc_cropcap = pvr2_cropcap,
+ .vidioc_s_crop = pvr2_s_crop,
+ .vidioc_g_crop = pvr2_g_crop,
+ .vidioc_g_input = pvr2_g_input,
+ .vidioc_s_input = pvr2_s_input,
+ .vidioc_g_frequency = pvr2_g_frequency,
+ .vidioc_s_frequency = pvr2_s_frequency,
+ .vidioc_s_tuner = pvr2_s_tuner,
+ .vidioc_g_tuner = pvr2_g_tuner,
+ .vidioc_g_std = pvr2_g_std,
+ .vidioc_s_std = pvr2_s_std,
+ .vidioc_querystd = pvr2_querystd,
+ .vidioc_log_status = pvr2_log_status,
+ .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap,
+ .vidioc_streamon = pvr2_streamon,
+ .vidioc_streamoff = pvr2_streamoff,
+ .vidioc_queryctrl = pvr2_queryctrl,
+ .vidioc_querymenu = pvr2_querymenu,
+ .vidioc_g_ctrl = pvr2_g_ctrl,
+ .vidioc_s_ctrl = pvr2_s_ctrl,
+ .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = pvr2_g_register,
+ .vidioc_s_register = pvr2_s_register,
+#endif
+};
+
+static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
+{
+ struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
+ enum pvr2_config cfg = dip->config;
+ char msg[80];
+ unsigned int mcnt;
+
+ /* Construct the unregistration message *before* we actually
+ perform the unregistration step. By doing it this way we don't
+ have to worry about potentially touching deleted resources. */
+ mcnt = scnprintf(msg, sizeof(msg) - 1,
+ "pvrusb2: unregistered device %s [%s]",
+ video_device_node_name(&dip->devbase),
+ pvr2_config_get_name(cfg));
+ msg[mcnt] = 0;
+
+ pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
+
+ /* Paranoia */
+ dip->v4lp = NULL;
+ dip->stream = NULL;
+
+ /* Actual deallocation happens later when all internal references
+ are gone. */
+ video_unregister_device(&dip->devbase);
+
+ printk(KERN_INFO "%s\n", msg);
+
+}
+
+
+static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
+{
+ if (!dip) return;
+ if (!dip->devbase.parent) return;
+ dip->devbase.parent = NULL;
+ device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
+}
+
+
+static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
+{
+ if (vp->dev_video) {
+ pvr2_v4l2_dev_destroy(vp->dev_video);
+ vp->dev_video = NULL;
+ }
+ if (vp->dev_radio) {
+ pvr2_v4l2_dev_destroy(vp->dev_radio);
+ vp->dev_radio = NULL;
+ }
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
+ pvr2_channel_done(&vp->channel);
+ kfree(vp);
+}
+
+
+static void pvr2_video_device_release(struct video_device *vdev)
+{
+ struct pvr2_v4l2_dev *dev;
+ dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
+ kfree(dev);
+}
+
+
+static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_v4l2 *vp;
+ vp = container_of(chp,struct pvr2_v4l2,channel);
+ if (!vp->channel.mc_head->disconnect_flag) return;
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
+ pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
+ if (vp->vfirst) return;
+ pvr2_v4l2_destroy_no_lock(vp);
+}
+
+
+static long pvr2_v4l2_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ long ret = -EINVAL;
+
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
+ v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ioctl failed - bad or no context");
+ return -EFAULT;
+ }
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ ret = v4l2_prio_check(&vp->prio, fh->prio);
+ if (ret)
+ return ret;
+ }
+
+ ret = video_ioctl2(file, cmd, arg);
+
+ pvr2_hdw_commit_ctl(hdw);
+
+ if (ret < 0) {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
+ } else {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld"
+ " command was:", ret);
+ v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw),
+ cmd);
+ }
+ }
+ } else {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
+ ret, ret);
+ }
+ return ret;
+
+}
+
+
+static int pvr2_v4l2_release(struct file *file)
+{
+ struct pvr2_v4l2_fh *fhp = file->private_data;
+ struct pvr2_v4l2 *vp = fhp->vhead;
+ struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
+
+ if (fhp->rhp) {
+ struct pvr2_stream *sp;
+ pvr2_hdw_set_streaming(hdw,0);
+ sp = pvr2_ioread_get_stream(fhp->rhp);
+ if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
+ pvr2_ioread_destroy(fhp->rhp);
+ fhp->rhp = NULL;
+ }
+
+ v4l2_prio_close(&vp->prio, fhp->prio);
+ file->private_data = NULL;
+
+ if (fhp->vnext) {
+ fhp->vnext->vprev = fhp->vprev;
+ } else {
+ vp->vlast = fhp->vprev;
+ }
+ if (fhp->vprev) {
+ fhp->vprev->vnext = fhp->vnext;
+ } else {
+ vp->vfirst = fhp->vnext;
+ }
+ fhp->vnext = NULL;
+ fhp->vprev = NULL;
+ fhp->vhead = NULL;
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p",fhp);
+ if (fhp->input_map) {
+ kfree(fhp->input_map);
+ fhp->input_map = NULL;
+ }
+ kfree(fhp);
+ if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+ pvr2_v4l2_destroy_no_lock(vp);
+ }
+ return 0;
+}
+
+
+static int pvr2_v4l2_open(struct file *file)
+{
+ struct pvr2_v4l2_dev *dip; /* Our own context pointer */
+ struct pvr2_v4l2_fh *fhp;
+ struct pvr2_v4l2 *vp;
+ struct pvr2_hdw *hdw;
+ unsigned int input_mask = 0;
+ unsigned int input_cnt,idx;
+ int ret = 0;
+
+ dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
+
+ vp = dip->v4lp;
+ hdw = vp->channel.hdw;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
+ "pvr2_v4l2_open: hardware not ready");
+ return -EIO;
+ }
+
+ fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
+ if (!fhp) {
+ return -ENOMEM;
+ }
+
+ init_waitqueue_head(&fhp->wait_data);
+ fhp->pdi = dip;
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+ pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+
+ if (dip->v4l_type == VFL_TYPE_RADIO) {
+ /* Opening device as a radio, legal input selection subset
+ is just the radio. */
+ input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
+ } else {
+ /* Opening the main V4L device, legal input selection
+ subset includes all analog inputs. */
+ input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
+ (1 << PVR2_CVAL_INPUT_TV) |
+ (1 << PVR2_CVAL_INPUT_COMPOSITE) |
+ (1 << PVR2_CVAL_INPUT_SVIDEO));
+ }
+ ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
+ if (ret) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input mask error)",
+ fhp);
+
+ kfree(fhp);
+ return ret;
+ }
+
+ input_mask &= pvr2_hdw_get_input_available(hdw);
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (input_mask & (1 << idx)) input_cnt++;
+ }
+ fhp->input_cnt = input_cnt;
+ fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
+ if (!fhp->input_map) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input map failure)",
+ fhp);
+ kfree(fhp);
+ return -ENOMEM;
+ }
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (!(input_mask & (1 << idx))) continue;
+ fhp->input_map[input_cnt++] = idx;
+ }
+
+ fhp->vnext = NULL;
+ fhp->vprev = vp->vlast;
+ if (vp->vlast) {
+ vp->vlast->vnext = fhp;
+ } else {
+ vp->vfirst = fhp;
+ }
+ vp->vlast = fhp;
+ fhp->vhead = vp;
+
+ fhp->file = file;
+ file->private_data = fhp;
+ v4l2_prio_open(&vp->prio, &fhp->prio);
+
+ fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
+
+ return 0;
+}
+
+
+static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
+{
+ wake_up(&fhp->wait_data);
+}
+
+static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
+{
+ int ret;
+ struct pvr2_stream *sp;
+ struct pvr2_hdw *hdw;
+ if (fh->rhp) return 0;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means that we're
+ not currently allowed to stream from this node. */
+ return -EPERM;
+ }
+
+ /* First read() attempt. Try to claim the stream and start
+ it... */
+ if ((ret = pvr2_channel_claim_stream(&fh->channel,
+ fh->pdi->stream)) != 0) {
+ /* Someone else must already have it */
+ return ret;
+ }
+
+ fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
+ if (!fh->rhp) {
+ pvr2_channel_claim_stream(&fh->channel,NULL);
+ return -ENOMEM;
+ }
+
+ hdw = fh->channel.mc_head->hdw;
+ sp = fh->pdi->stream->stream;
+ pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
+ pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
+ if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
+ return pvr2_ioread_set_enabled(fh->rhp,!0);
+}
+
+
+static ssize_t pvr2_v4l2_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ char *tbuf;
+ int c1,c2;
+ int tcnt = 0;
+ unsigned int offs = *ppos;
+
+ tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
+ if (!tbuf) return -ENOMEM;
+
+ while (count) {
+ c1 = count;
+ if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
+ c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
+ if (c2 < 0) {
+ tcnt = c2;
+ break;
+ }
+ if (!c2) break;
+ if (copy_to_user(buff,tbuf,c2)) {
+ tcnt = -EFAULT;
+ break;
+ }
+ offs += c2;
+ tcnt += c2;
+ buff += c2;
+ count -= c2;
+ *ppos += c2;
+ }
+ kfree(tbuf);
+ return tcnt;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ for (;;) {
+ ret = pvr2_ioread_read(fh->rhp,buff,count);
+ if (ret >= 0) break;
+ if (ret != -EAGAIN) break;
+ if (file->f_flags & O_NONBLOCK) break;
+ /* Doing blocking I/O. Wait here. */
+ ret = wait_event_interruptible(
+ fh->wait_data,
+ pvr2_ioread_avail(fh->rhp) >= 0);
+ if (ret < 0) break;
+ }
+
+ return ret;
+}
+
+
+static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) return POLLERR;
+ }
+
+ poll_wait(file,&fh->wait_data,wait);
+
+ if (pvr2_ioread_avail(fh->rhp) >= 0) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+
+static const struct v4l2_file_operations vdev_fops = {
+ .owner = THIS_MODULE,
+ .open = pvr2_v4l2_open,
+ .release = pvr2_v4l2_release,
+ .read = pvr2_v4l2_read,
+ .ioctl = pvr2_v4l2_ioctl,
+ .poll = pvr2_v4l2_poll,
+};
+
+
+static struct video_device vdev_template = {
+ .fops = &vdev_fops,
+};
+
+
+static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ struct pvr2_v4l2 *vp,
+ int v4l_type)
+{
+ struct usb_device *usbdev;
+ int mindevnum;
+ int unit_number;
+ struct pvr2_hdw *hdw;
+ int *nr_ptr = NULL;
+ dip->v4lp = vp;
+
+ hdw = vp->channel.mc_head->hdw;
+ usbdev = pvr2_hdw_get_dev(hdw);
+ dip->v4l_type = v4l_type;
+ switch (v4l_type) {
+ case VFL_TYPE_GRABBER:
+ dip->stream = &vp->channel.mc_head->video_stream;
+ dip->config = pvr2_config_mpeg;
+ dip->minor_type = pvr2_v4l_type_video;
+ nr_ptr = video_nr;
+ if (!dip->stream) {
+ pr_err(KBUILD_MODNAME
+ ": Failed to set up pvrusb2 v4l video dev"
+ " due to missing stream instance\n");
+ return;
+ }
+ break;
+ case VFL_TYPE_VBI:
+ dip->config = pvr2_config_vbi;
+ dip->minor_type = pvr2_v4l_type_vbi;
+ nr_ptr = vbi_nr;
+ break;
+ case VFL_TYPE_RADIO:
+ dip->stream = &vp->channel.mc_head->video_stream;
+ dip->config = pvr2_config_mpeg;
+ dip->minor_type = pvr2_v4l_type_radio;
+ nr_ptr = radio_nr;
+ break;
+ default:
+ /* Bail out (this should be impossible) */
+ pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
+ " due to unrecognized config\n");
+ return;
+ }
+
+ memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template));
+ dip->devbase.release = pvr2_video_device_release;
+ dip->devbase.ioctl_ops = &pvr2_ioctl_ops;
+ {
+ int val;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,
+ PVR2_CID_STDAVAIL), &val);
+ dip->devbase.tvnorms = (v4l2_std_id)val;
+ }
+
+ mindevnum = -1;
+ unit_number = pvr2_hdw_get_unit_number(hdw);
+ if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
+ mindevnum = nr_ptr[unit_number];
+ }
+ dip->devbase.parent = &usbdev->dev;
+ if ((video_register_device(&dip->devbase,
+ dip->v4l_type, mindevnum) < 0) &&
+ (video_register_device(&dip->devbase,
+ dip->v4l_type, -1) < 0)) {
+ pr_err(KBUILD_MODNAME
+ ": Failed to register pvrusb2 v4l device\n");
+ }
+
+ printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
+ video_device_node_name(&dip->devbase),
+ pvr2_config_get_name(dip->config));
+
+ pvr2_hdw_v4l_store_minor_number(hdw,
+ dip->minor_type,dip->devbase.minor);
+}
+
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
+{
+ struct pvr2_v4l2 *vp;
+
+ vp = kzalloc(sizeof(*vp),GFP_KERNEL);
+ if (!vp) return vp;
+ pvr2_channel_init(&vp->channel,mnp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
+
+ vp->channel.check_func = pvr2_v4l2_internal_check;
+
+ /* register streams */
+ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
+ if (!vp->dev_video) goto fail;
+ pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
+ if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
+ (1 << PVR2_CVAL_INPUT_RADIO)) {
+ vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
+ if (!vp->dev_radio) goto fail;
+ pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+ }
+
+ return vp;
+ fail:
+ pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
+ pvr2_v4l2_destroy_no_lock(vp);
+ return NULL;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
index 34c011a7b107..34c011a7b107 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
index 2e205c99eb96..2e205c99eb96 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
index 3b0bd5db602b..3b0bd5db602b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
index 3ac8d751a5c0..3ac8d751a5c0 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
index 0577bc7246fb..0577bc7246fb 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h
diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/usb/pvrusb2/pvrusb2.h
index 240de9b35661..240de9b35661 100644
--- a/drivers/media/video/pvrusb2/pvrusb2.h
+++ b/drivers/media/usb/pvrusb2/pvrusb2.h
diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/usb/pwc/Kconfig
index d63d0a850035..d63d0a850035 100644
--- a/drivers/media/video/pwc/Kconfig
+++ b/drivers/media/usb/pwc/Kconfig
diff --git a/drivers/media/usb/pwc/Makefile b/drivers/media/usb/pwc/Makefile
new file mode 100644
index 000000000000..d7fdbcb9edd3
--- /dev/null
+++ b/drivers/media/usb/pwc/Makefile
@@ -0,0 +1,4 @@
+pwc-objs += pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o
+pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o
+
+obj-$(CONFIG_USB_PWC) += pwc.o
diff --git a/drivers/media/video/pwc/philips.txt b/drivers/media/usb/pwc/philips.txt
index d38dd791511e..d38dd791511e 100644
--- a/drivers/media/video/pwc/philips.txt
+++ b/drivers/media/usb/pwc/philips.txt
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/usb/pwc/pwc-ctrl.c
index 1f506fde97d0..1f506fde97d0 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/usb/pwc/pwc-ctrl.c
diff --git a/drivers/media/video/pwc/pwc-dec1.c b/drivers/media/usb/pwc/pwc-dec1.c
index e899036aadf4..e899036aadf4 100644
--- a/drivers/media/video/pwc/pwc-dec1.c
+++ b/drivers/media/usb/pwc/pwc-dec1.c
diff --git a/drivers/media/video/pwc/pwc-dec1.h b/drivers/media/usb/pwc/pwc-dec1.h
index c565ef8f52fb..c565ef8f52fb 100644
--- a/drivers/media/video/pwc/pwc-dec1.h
+++ b/drivers/media/usb/pwc/pwc-dec1.h
diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/usb/pwc/pwc-dec23.c
index 3792fedff951..3792fedff951 100644
--- a/drivers/media/video/pwc/pwc-dec23.c
+++ b/drivers/media/usb/pwc/pwc-dec23.c
diff --git a/drivers/media/video/pwc/pwc-dec23.h b/drivers/media/usb/pwc/pwc-dec23.h
index c655b1c1e6a9..c655b1c1e6a9 100644
--- a/drivers/media/video/pwc/pwc-dec23.h
+++ b/drivers/media/usb/pwc/pwc-dec23.h
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
new file mode 100644
index 000000000000..42e36bac4d72
--- /dev/null
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -0,0 +1,1164 @@
+/* Linux driver for Philips webcam
+ USB and Video4Linux interface part.
+ (C) 1999-2004 Nemosoft Unv.
+ (C) 2004-2006 Luc Saillard (luc@saillard.org)
+ (C) 2011 Hans de Goede <hdegoede@redhat.com>
+
+ NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+ driver and thus may have bugs that are not present in the original version.
+ Please send bug reports and support requests to <luc@saillard.org>.
+ The decompression routines have been implemented by reverse-engineering the
+ Nemosoft binary pwcx module. Caveat emptor.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+*/
+
+/*
+ This code forms the interface between the USB layers and the Philips
+ specific stuff. Some adanved stuff of the driver falls under an
+ NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
+ is thus not distributed in source form. The binary pwcx.o module
+ contains the code that falls under the NDA.
+
+ In case you're wondering: 'pwc' stands for "Philips WebCam", but
+ I really didn't want to type 'philips_web_cam' every time (I'm lazy as
+ any Linux kernel hacker, but I don't like uncomprehensible abbreviations
+ without explanation).
+
+ Oh yes, convention: to disctinguish between all the various pointers to
+ device-structures, I use these names for the pointer variables:
+ udev: struct usb_device *
+ vdev: struct video_device (member of pwc_dev)
+ pdev: struct pwc_devive *
+*/
+
+/* Contributors:
+ - Alvarado: adding whitebalance code
+ - Alistar Moire: QuickCam 3000 Pro device/product ID
+ - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+ - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+ - Jk Fang: Sotec Afina Eye ID
+ - Xavier Roche: QuickCam Pro 4000 ID
+ - Jens Knudsen: QuickCam Zoom ID
+ - J. Debert: QuickCam for Notebooks ID
+ - Pham Thanh Nam: webcam snapshot button as an event input device
+*/
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+#include <linux/usb/input.h>
+#endif
+#include <linux/vmalloc.h>
+#include <asm/io.h>
+#include <linux/kernel.h> /* simple_strtol() */
+
+#include "pwc.h"
+#include "pwc-kiara.h"
+#include "pwc-timon.h"
+#include "pwc-dec23.h"
+#include "pwc-dec1.h"
+
+/* Function prototypes and driver templates */
+
+/* hotplug device table support */
+static const struct usb_device_id pwc_device_table [] = {
+ { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
+ { USB_DEVICE(0x0471, 0x0303) },
+ { USB_DEVICE(0x0471, 0x0304) },
+ { USB_DEVICE(0x0471, 0x0307) },
+ { USB_DEVICE(0x0471, 0x0308) },
+ { USB_DEVICE(0x0471, 0x030C) },
+ { USB_DEVICE(0x0471, 0x0310) },
+ { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
+ { USB_DEVICE(0x0471, 0x0312) },
+ { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
+ { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
+ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
+ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
+ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
+ { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
+ { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
+ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
+ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
+ { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
+ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
+ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
+ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
+ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
+ { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
+ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
+ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
+ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
+ { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
+ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
+ { USB_DEVICE(0x0d81, 0x1900) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, pwc_device_table);
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
+static void usb_pwc_disconnect(struct usb_interface *intf);
+static void pwc_isoc_cleanup(struct pwc_device *pdev);
+
+static struct usb_driver pwc_driver = {
+ .name = "Philips webcam", /* name */
+ .id_table = pwc_device_table,
+ .probe = usb_pwc_probe, /* probe() */
+ .disconnect = usb_pwc_disconnect, /* disconnect() */
+};
+
+#define MAX_DEV_HINTS 20
+#define MAX_ISOC_ERRORS 20
+
+#ifdef CONFIG_USB_PWC_DEBUG
+ int pwc_trace = PWC_DEBUG_LEVEL;
+#endif
+static int power_save = -1;
+static int leds[2] = { 100, 0 };
+
+/***/
+
+static const struct v4l2_file_operations pwc_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+static struct video_device pwc_template = {
+ .name = "Philips Webcam", /* Filled in later */
+ .release = video_device_release_empty,
+ .fops = &pwc_fops,
+ .ioctl_ops = &pwc_ioctl_ops,
+};
+
+/***************************************************************************/
+/* Private functions */
+
+struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
+{
+ unsigned long flags = 0;
+ struct pwc_frame_buf *buf = NULL;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ if (list_empty(&pdev->queued_bufs))
+ goto leave;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
+ list_del(&buf->list);
+leave:
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+ return buf;
+}
+
+static void pwc_snapshot_button(struct pwc_device *pdev, int down)
+{
+ if (down) {
+ PWC_TRACE("Snapshot button pressed.\n");
+ } else {
+ PWC_TRACE("Snapshot button released.\n");
+ }
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ if (pdev->button_dev) {
+ input_report_key(pdev->button_dev, KEY_CAMERA, down);
+ input_sync(pdev->button_dev);
+ }
+#endif
+}
+
+static void pwc_frame_complete(struct pwc_device *pdev)
+{
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
+
+ /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
+ frames on the USB wire after an exposure change. This conditition is
+ however detected in the cam and a bit is set in the header.
+ */
+ if (pdev->type == 730) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+
+ if (ptr[1] == 1 && ptr[0] & 0x10) {
+ PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
+ pdev->drop_frames += 2;
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+ if (ptr[0] & 0x02)
+ PWC_TRACE("Image is mirrored.\n");
+ else
+ PWC_TRACE("Image is normal.\n");
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
+ after a short frame; this condition is filtered out specifically. A 4 byte
+ frame doesn't make sense anyway.
+ So we get either this sequence:
+ drop_bit set -> 4 byte frame -> short frame -> good frame
+ Or this one:
+ drop_bit set -> short frame -> good frame
+ So we drop either 3 or 2 frames in all!
+ */
+ if (fbuf->filled == 4)
+ pdev->drop_frames++;
+ } else if (pdev->type == 740 || pdev->type == 720) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ }
+
+ /* In case we were instructed to drop the frame, do so silently. */
+ if (pdev->drop_frames > 0) {
+ pdev->drop_frames--;
+ } else {
+ /* Check for underflow first */
+ if (fbuf->filled < pdev->frame_total_size) {
+ PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
+ " discarded.\n", fbuf->filled);
+ } else {
+ fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
+ vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
+ pdev->fill_buf = NULL;
+ pdev->vsync = 0;
+ }
+ } /* !drop_frames */
+ pdev->vframe_count++;
+}
+
+/* This gets called for the Isochronous pipe (video). This is done in
+ * interrupt time, so it has to be fast, not crash, and not stall. Neat.
+ */
+static void pwc_isoc_handler(struct urb *urb)
+{
+ struct pwc_device *pdev = (struct pwc_device *)urb->context;
+ int i, fst, flen;
+ unsigned char *iso_buf = NULL;
+
+ if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
+ PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
+ return;
+ }
+
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+
+ if (urb->status != 0) {
+ const char *errmsg;
+
+ errmsg = "Unknown";
+ switch(urb->status) {
+ case -ENOSR: errmsg = "Buffer error (overrun)"; break;
+ case -EPIPE: errmsg = "Stalled (device not responding)"; break;
+ case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
+ case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
+ case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
+ case -ETIME: errmsg = "Device does not respond"; break;
+ }
+ PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
+ urb->status, errmsg);
+ /* Give up after a number of contiguous errors */
+ if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
+ {
+ PWC_ERROR("Too many ISOC errors, bailing out.\n");
+ if (pdev->fill_buf) {
+ vb2_buffer_done(&pdev->fill_buf->vb,
+ VB2_BUF_STATE_ERROR);
+ pdev->fill_buf = NULL;
+ }
+ }
+ pdev->vsync = 0; /* Drop the current frame */
+ goto handler_end;
+ }
+
+ /* Reset ISOC error counter. We did get here, after all. */
+ pdev->visoc_errors = 0;
+
+ /* vsync: 0 = don't copy data
+ 1 = sync-hunt
+ 2 = synched
+ */
+ /* Compact data */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ fst = urb->iso_frame_desc[i].status;
+ flen = urb->iso_frame_desc[i].actual_length;
+ iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (fst != 0) {
+ PWC_ERROR("Iso frame %d has error %d\n", i, fst);
+ continue;
+ }
+ if (flen > 0 && pdev->vsync) {
+ struct pwc_frame_buf *fbuf = pdev->fill_buf;
+
+ if (pdev->vsync == 1) {
+ do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
+ pdev->vsync = 2;
+ }
+
+ if (flen + fbuf->filled > pdev->frame_total_size) {
+ PWC_ERROR("Frame overflow (%d > %d)\n",
+ flen + fbuf->filled,
+ pdev->frame_total_size);
+ pdev->vsync = 0; /* Let's wait for an EOF */
+ } else {
+ memcpy(fbuf->data + fbuf->filled, iso_buf,
+ flen);
+ fbuf->filled += flen;
+ }
+ }
+ if (flen < pdev->vlast_packet_size) {
+ /* Shorter packet... end of frame */
+ if (pdev->vsync == 2)
+ pwc_frame_complete(pdev);
+ if (pdev->fill_buf == NULL)
+ pdev->fill_buf = pwc_get_next_fill_buf(pdev);
+ if (pdev->fill_buf) {
+ pdev->fill_buf->filled = 0;
+ pdev->vsync = 1;
+ }
+ }
+ pdev->vlast_packet_size = flen;
+ }
+
+handler_end:
+ i = usb_submit_urb(urb, GFP_ATOMIC);
+ if (i != 0)
+ PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
+}
+
+/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
+static int pwc_isoc_init(struct pwc_device *pdev)
+{
+ struct usb_device *udev;
+ struct urb *urb;
+ int i, j, ret;
+ struct usb_interface *intf;
+ struct usb_host_interface *idesc = NULL;
+ int compression = 0; /* 0..3 = uncompressed..high */
+
+ pdev->vsync = 0;
+ pdev->vlast_packet_size = 0;
+ pdev->fill_buf = NULL;
+ pdev->vframe_count = 0;
+ pdev->visoc_errors = 0;
+ udev = pdev->udev;
+
+retry:
+ /* We first try with low compression and then retry with a higher
+ compression setting if there is not enough bandwidth. */
+ ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
+ pdev->vframes, &compression, 1);
+
+ /* Get the current alternate interface, adjust packet size */
+ intf = usb_ifnum_to_if(udev, 0);
+ if (intf)
+ idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
+ if (!idesc)
+ return -EIO;
+
+ /* Search video endpoint */
+ pdev->vmax_packet_size = -1;
+ for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
+ if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
+ pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
+ break;
+ }
+ }
+
+ if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
+ PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
+ return -ENFILE; /* Odd error, that should be noticeable */
+ }
+
+ /* Set alternate interface */
+ PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
+ ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
+ if (ret == -ENOSPC && compression < 3) {
+ compression++;
+ goto retry;
+ }
+ if (ret < 0)
+ return ret;
+
+ /* Allocate and init Isochronuous urbs */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
+ if (urb == NULL) {
+ PWC_ERROR("Failed to allocate urb %d\n", i);
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
+ }
+ pdev->urbs[i] = urb;
+ PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
+
+ urb->interval = 1; // devik
+ urb->dev = udev;
+ urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = usb_alloc_coherent(udev,
+ ISO_BUFFER_SIZE,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+ if (urb->transfer_buffer == NULL) {
+ PWC_ERROR("Failed to allocate urb buffer %d\n", i);
+ pwc_isoc_cleanup(pdev);
+ return -ENOMEM;
+ }
+ urb->transfer_buffer_length = ISO_BUFFER_SIZE;
+ urb->complete = pwc_isoc_handler;
+ urb->context = pdev;
+ urb->start_frame = 0;
+ urb->number_of_packets = ISO_FRAMES_PER_DESC;
+ for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
+ urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
+ }
+ }
+
+ /* link */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
+ if (ret == -ENOSPC && compression < 3) {
+ compression++;
+ pwc_isoc_cleanup(pdev);
+ goto retry;
+ }
+ if (ret) {
+ PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
+ pwc_isoc_cleanup(pdev);
+ return ret;
+ }
+ PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
+ }
+
+ /* All is done... */
+ PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
+ return 0;
+}
+
+static void pwc_iso_stop(struct pwc_device *pdev)
+{
+ int i;
+
+ /* Unlinking ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (pdev->urbs[i]) {
+ PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
+ usb_kill_urb(pdev->urbs[i]);
+ }
+ }
+}
+
+static void pwc_iso_free(struct pwc_device *pdev)
+{
+ int i;
+
+ /* Freeing ISOC buffers one by one */
+ for (i = 0; i < MAX_ISO_BUFS; i++) {
+ if (pdev->urbs[i]) {
+ PWC_DEBUG_MEMORY("Freeing URB\n");
+ if (pdev->urbs[i]->transfer_buffer) {
+ usb_free_coherent(pdev->udev,
+ pdev->urbs[i]->transfer_buffer_length,
+ pdev->urbs[i]->transfer_buffer,
+ pdev->urbs[i]->transfer_dma);
+ }
+ usb_free_urb(pdev->urbs[i]);
+ pdev->urbs[i] = NULL;
+ }
+ }
+}
+
+/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
+static void pwc_isoc_cleanup(struct pwc_device *pdev)
+{
+ PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
+
+ pwc_iso_stop(pdev);
+ pwc_iso_free(pdev);
+ usb_set_interface(pdev->udev, 0, 0);
+
+ PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
+}
+
+/* Must be called with vb_queue_lock hold */
+static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
+{
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ while (!list_empty(&pdev->queued_bufs)) {
+ struct pwc_frame_buf *buf;
+
+ buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
+ list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+#ifdef CONFIG_USB_PWC_DEBUG
+static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
+{
+ switch(sensor_type) {
+ case 0x00:
+ return "Hyundai CMOS sensor";
+ case 0x20:
+ return "Sony CCD sensor + TDA8787";
+ case 0x2E:
+ return "Sony CCD sensor + Exas 98L59";
+ case 0x2F:
+ return "Sony CCD sensor + ADI 9804";
+ case 0x30:
+ return "Sharp CCD sensor + TDA8787";
+ case 0x3E:
+ return "Sharp CCD sensor + Exas 98L59";
+ case 0x3F:
+ return "Sharp CCD sensor + ADI 9804";
+ case 0x40:
+ return "UPA 1021 sensor";
+ case 0x100:
+ return "VGA sensor";
+ case 0x101:
+ return "PAL MR sensor";
+ default:
+ return "unknown type of sensor";
+ }
+}
+#endif
+
+/***************************************************************************/
+/* Video4Linux functions */
+
+static void pwc_video_release(struct v4l2_device *v)
+{
+ struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+ v4l2_device_unregister(&pdev->v4l2_dev);
+ kfree(pdev->ctrl_buf);
+ kfree(pdev);
+}
+
+/***************************************************************************/
+/* Videobuf2 operations */
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ int size;
+
+ if (*nbuffers < MIN_FRAMES)
+ *nbuffers = MIN_FRAMES;
+ else if (*nbuffers > MAX_FRAMES)
+ *nbuffers = MAX_FRAMES;
+
+ *nplanes = 1;
+
+ size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
+ sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
+ pwc_image_sizes[size][1] * 3 / 2);
+
+ return 0;
+}
+
+static int buffer_init(struct vb2_buffer *vb)
+{
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ /* need vmalloc since frame buffer > 128K */
+ buf->data = vzalloc(PWC_FRAME_SIZE);
+ if (buf->data == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+
+ /* Don't allow queing new buffers after device disconnection */
+ if (!pdev->udev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int buffer_finish(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ /*
+ * Application has called dqbuf and is getting back a buffer we've
+ * filled, take the pwc data we've stored in buf->data and decompress
+ * it into a usable format, storing the result in the vb2_buffer
+ */
+ return pwc_decompress(pdev, buf);
+}
+
+static void buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+
+ vfree(buf->data);
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
+ unsigned long flags = 0;
+
+ /* Check the device has not disconnected between prep and queuing */
+ if (!pdev->udev) {
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ list_add_tail(&buf->list, &pdev->queued_bufs);
+ spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ int r;
+
+ if (!pdev->udev)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ return -ERESTARTSYS;
+ /* Turn on camera and set LEDS on */
+ pwc_camera_power(pdev, 1);
+ pwc_set_leds(pdev, leds[0], leds[1]);
+
+ r = pwc_isoc_init(pdev);
+ if (r) {
+ /* If we failed turn camera and LEDS back off */
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+ /* And cleanup any queued bufs!! */
+ pwc_cleanup_queued_bufs(pdev);
+ }
+ mutex_unlock(&pdev->v4l2_lock);
+
+ return r;
+}
+
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+
+ if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ return -ERESTARTSYS;
+ if (pdev->udev) {
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+ pwc_isoc_cleanup(pdev);
+ }
+
+ pwc_cleanup_queued_bufs(pdev);
+ mutex_unlock(&pdev->v4l2_lock);
+
+ return 0;
+}
+
+static struct vb2_ops pwc_vb_queue_ops = {
+ .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 = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+/***************************************************************************/
+/* USB functions */
+
+/* This function gets called when a new device is plugged in or the usb core
+ * is loaded.
+ */
+
+static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct pwc_device *pdev = NULL;
+ int vendor_id, product_id, type_id;
+ int rc;
+ int features = 0;
+ int compression = 0;
+ int my_power_save = power_save;
+ char serial_number[30], *name;
+
+ vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+ product_id = le16_to_cpu(udev->descriptor.idProduct);
+
+ /* Check if we can handle this device */
+ PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
+ vendor_id, product_id,
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* the interfaces are probed one by one. We are only interested in the
+ video interface (0) now.
+ Interface 1 is the Audio Control, and interface 2 Audio itself.
+ */
+ if (intf->altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
+
+ if (vendor_id == 0x0471) {
+ switch (product_id) {
+ case 0x0302:
+ PWC_INFO("Philips PCA645VC USB webcam detected.\n");
+ name = "Philips 645 webcam";
+ type_id = 645;
+ break;
+ case 0x0303:
+ PWC_INFO("Philips PCA646VC USB webcam detected.\n");
+ name = "Philips 646 webcam";
+ type_id = 646;
+ break;
+ case 0x0304:
+ PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 646;
+ break;
+ case 0x0307:
+ PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
+ name = "Philips 675 webcam";
+ type_id = 675;
+ break;
+ case 0x0308:
+ PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
+ name = "Philips 680 webcam";
+ type_id = 680;
+ break;
+ case 0x030C:
+ PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
+ name = "Philips 690 webcam";
+ type_id = 690;
+ break;
+ case 0x0310:
+ PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 730 webcam";
+ type_id = 730;
+ break;
+ case 0x0311:
+ PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
+ name = "Philips 740 webcam";
+ type_id = 740;
+ break;
+ case 0x0312:
+ PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
+ name = "Philips 750 webcam";
+ type_id = 750;
+ break;
+ case 0x0313:
+ PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
+ name = "Philips 720K/40 webcam";
+ type_id = 720;
+ break;
+ case 0x0329:
+ PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
+ name = "Philips SPC 900NC webcam";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x069A) {
+ switch(product_id) {
+ case 0x0001:
+ PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
+ name = "Askey VC010 webcam";
+ type_id = 645;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x046d) {
+ switch(product_id) {
+ case 0x08b0:
+ PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 3000";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b1:
+ PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Notebook Pro";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b2:
+ PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
+ name = "Logitech QuickCam Pro 4000";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x08b3:
+ PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08B4:
+ PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
+ name = "Logitech QuickCam Zoom";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x08b5:
+ PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
+ name = "Logitech QuickCam Orbit";
+ type_id = 740; /* CCD sensor */
+ if (my_power_save == -1)
+ my_power_save = 1;
+ features |= FEATURE_MOTOR_PANTILT;
+ break;
+ case 0x08b6:
+ PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
+ name = "Cisco VT Camera";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b7:
+ PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
+ name = "Logitech ViewPort AV 100";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x08b8: /* Where this released? */
+ PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
+ name = "Logitech QuickCam (res.)";
+ type_id = 730; /* Assuming CMOS */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x055d) {
+ /* I don't know the difference between the C10 and the C30;
+ I suppose the difference is the sensor, but both cameras
+ work equally well with a type_id of 675
+ */
+ switch(product_id) {
+ case 0x9000:
+ PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
+ name = "Samsung MPC-C10";
+ type_id = 675;
+ break;
+ case 0x9001:
+ PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 675;
+ break;
+ case 0x9002:
+ PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
+ name = "Samsung MPC-C30";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x041e) {
+ switch(product_id) {
+ case 0x400c:
+ PWC_INFO("Creative Labs Webcam 5 detected.\n");
+ name = "Creative Labs Webcam 5";
+ type_id = 730;
+ if (my_power_save == -1)
+ my_power_save = 1;
+ break;
+ case 0x4011:
+ PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
+ name = "Creative Labs Webcam Pro Ex";
+ type_id = 740;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x04cc) {
+ switch(product_id) {
+ case 0x8116:
+ PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
+ name = "Sotec Afina Eye";
+ type_id = 730;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else if (vendor_id == 0x06be) {
+ switch(product_id) {
+ case 0x8116:
+ /* This is essentially the same cam as the Sotec Afina Eye */
+ PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
+ name = "AME Co. Afina Eye";
+ type_id = 750;
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+
+ }
+ else if (vendor_id == 0x0d81) {
+ switch(product_id) {
+ case 0x1900:
+ PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
+ name = "Visionite VCS-UC300";
+ type_id = 740; /* CCD sensor */
+ break;
+ case 0x1910:
+ PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
+ name = "Visionite VCS-UM100";
+ type_id = 730; /* CMOS sensor */
+ break;
+ default:
+ return -ENODEV;
+ break;
+ }
+ }
+ else
+ return -ENODEV; /* Not any of the know types; but the list keeps growing. */
+
+ if (my_power_save == -1)
+ my_power_save = 0;
+
+ memset(serial_number, 0, 30);
+ usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
+ PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
+
+ if (udev->descriptor.bNumConfigurations > 1)
+ PWC_WARNING("Warning: more than 1 configuration available.\n");
+
+ /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
+ pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
+ if (pdev == NULL) {
+ PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
+ return -ENOMEM;
+ }
+ pdev->type = type_id;
+ pdev->features = features;
+ pwc_construct(pdev); /* set min/max sizes correct */
+
+ mutex_init(&pdev->v4l2_lock);
+ mutex_init(&pdev->vb_queue_lock);
+ spin_lock_init(&pdev->queued_bufs_lock);
+ INIT_LIST_HEAD(&pdev->queued_bufs);
+
+ pdev->udev = udev;
+ pdev->power_save = my_power_save;
+
+ /* Init videobuf2 queue structure */
+ pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
+ pdev->vb_queue.drv_priv = pdev;
+ pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
+ pdev->vb_queue.ops = &pwc_vb_queue_ops;
+ pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
+ vb2_queue_init(&pdev->vb_queue);
+
+ /* Init video_device structure */
+ memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
+ strcpy(pdev->vdev.name, name);
+ pdev->vdev.queue = &pdev->vb_queue;
+ pdev->vdev.queue->lock = &pdev->vb_queue_lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
+ video_set_drvdata(&pdev->vdev, pdev);
+
+ pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
+ PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
+
+ /* Allocate USB command buffers */
+ pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL);
+ if (!pdev->ctrl_buf) {
+ PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
+ rc = -ENOMEM;
+ goto err_free_mem;
+ }
+
+#ifdef CONFIG_USB_PWC_DEBUG
+ /* Query sensor type */
+ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
+ PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
+ pdev->vdev.name,
+ pwc_sensor_type_to_string(rc), rc);
+ }
+#endif
+
+ /* Set the leds off */
+ pwc_set_leds(pdev, 0, 0);
+
+ /* Setup intial videomode */
+ rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
+ V4L2_PIX_FMT_YUV420, 30, &compression, 1);
+ if (rc)
+ goto err_free_mem;
+
+ /* Register controls (and read default values from camera */
+ rc = pwc_init_controls(pdev);
+ if (rc) {
+ PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
+ goto err_free_mem;
+ }
+
+ /* And powerdown the camera until streaming starts */
+ pwc_camera_power(pdev, 0);
+
+ /* Register the v4l2_device structure */
+ pdev->v4l2_dev.release = pwc_video_release;
+ rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
+ if (rc) {
+ PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
+ goto err_free_controls;
+ }
+
+ pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
+ pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
+ pdev->vdev.lock = &pdev->v4l2_lock;
+
+ rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
+ if (rc < 0) {
+ PWC_ERROR("Failed to register as video device (%d).\n", rc);
+ goto err_unregister_v4l2_dev;
+ }
+ PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ /* register webcam snapshot button input device */
+ pdev->button_dev = input_allocate_device();
+ if (!pdev->button_dev) {
+ PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
+ rc = -ENOMEM;
+ goto err_video_unreg;
+ }
+
+ usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
+ strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
+
+ pdev->button_dev->name = "PWC snapshot button";
+ pdev->button_dev->phys = pdev->button_phys;
+ usb_to_input_id(pdev->udev, &pdev->button_dev->id);
+ pdev->button_dev->dev.parent = &pdev->udev->dev;
+ pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
+ pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
+
+ rc = input_register_device(pdev->button_dev);
+ if (rc) {
+ input_free_device(pdev->button_dev);
+ pdev->button_dev = NULL;
+ goto err_video_unreg;
+ }
+#endif
+
+ return 0;
+
+err_video_unreg:
+ video_unregister_device(&pdev->vdev);
+err_unregister_v4l2_dev:
+ v4l2_device_unregister(&pdev->v4l2_dev);
+err_free_controls:
+ v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+err_free_mem:
+ kfree(pdev->ctrl_buf);
+ kfree(pdev);
+ return rc;
+}
+
+/* The user yanked out the cable... */
+static void usb_pwc_disconnect(struct usb_interface *intf)
+{
+ struct v4l2_device *v = usb_get_intfdata(intf);
+ struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
+
+ mutex_lock(&pdev->vb_queue_lock);
+ mutex_lock(&pdev->v4l2_lock);
+ /* No need to keep the urbs around after disconnection */
+ if (pdev->vb_queue.streaming)
+ pwc_isoc_cleanup(pdev);
+ pdev->udev = NULL;
+ pwc_cleanup_queued_bufs(pdev);
+
+ v4l2_device_disconnect(&pdev->v4l2_dev);
+ video_unregister_device(&pdev->vdev);
+ mutex_unlock(&pdev->v4l2_lock);
+ mutex_unlock(&pdev->vb_queue_lock);
+
+#ifdef CONFIG_USB_PWC_INPUT_EVDEV
+ if (pdev->button_dev)
+ input_unregister_device(pdev->button_dev);
+#endif
+
+ v4l2_device_put(&pdev->v4l2_dev);
+}
+
+
+/*
+ * Initialization code & module stuff
+ */
+
+static unsigned int leds_nargs;
+
+#ifdef CONFIG_USB_PWC_DEBUG
+module_param_named(trace, pwc_trace, int, 0644);
+#endif
+module_param(power_save, int, 0644);
+module_param_array(leds, int, &leds_nargs, 0444);
+
+#ifdef CONFIG_USB_PWC_DEBUG
+MODULE_PARM_DESC(trace, "For debugging purposes");
+#endif
+MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
+MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
+
+MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
+MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("pwcx");
+MODULE_VERSION( PWC_VERSION );
+
+module_usb_driver(pwc_driver);
diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/usb/pwc/pwc-kiara.c
index e5f4fd817125..e5f4fd817125 100644
--- a/drivers/media/video/pwc/pwc-kiara.c
+++ b/drivers/media/usb/pwc/pwc-kiara.c
diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/usb/pwc/pwc-kiara.h
index 8e02b7ac2139..8e02b7ac2139 100644
--- a/drivers/media/video/pwc/pwc-kiara.h
+++ b/drivers/media/usb/pwc/pwc-kiara.h
diff --git a/drivers/media/video/pwc/pwc-misc.c b/drivers/media/usb/pwc/pwc-misc.c
index 9be5adffa874..9be5adffa874 100644
--- a/drivers/media/video/pwc/pwc-misc.c
+++ b/drivers/media/usb/pwc/pwc-misc.c
diff --git a/drivers/media/video/pwc/pwc-nala.h b/drivers/media/usb/pwc/pwc-nala.h
index 168c73ef75d8..168c73ef75d8 100644
--- a/drivers/media/video/pwc/pwc-nala.h
+++ b/drivers/media/usb/pwc/pwc-nala.h
diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/usb/pwc/pwc-timon.c
index c56c174b161c..c56c174b161c 100644
--- a/drivers/media/video/pwc/pwc-timon.c
+++ b/drivers/media/usb/pwc/pwc-timon.c
diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/usb/pwc/pwc-timon.h
index 270c5b9010f6..270c5b9010f6 100644
--- a/drivers/media/video/pwc/pwc-timon.h
+++ b/drivers/media/usb/pwc/pwc-timon.h
diff --git a/drivers/media/video/pwc/pwc-uncompress.c b/drivers/media/usb/pwc/pwc-uncompress.c
index b65903fbcf0d..b65903fbcf0d 100644
--- a/drivers/media/video/pwc/pwc-uncompress.c
+++ b/drivers/media/usb/pwc/pwc-uncompress.c
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/usb/pwc/pwc-v4l.c
index 545e9bbdeede..545e9bbdeede 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/usb/pwc/pwc-v4l.c
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/usb/pwc/pwc.h
index 7a6a0d39c2c6..7a6a0d39c2c6 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/usb/pwc/pwc.h
diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig
new file mode 100644
index 000000000000..7e8ee1f864ab
--- /dev/null
+++ b/drivers/media/usb/s2255/Kconfig
@@ -0,0 +1,9 @@
+config USB_S2255
+ tristate "USB Sensoray 2255 video capture device"
+ depends on VIDEO_V4L2
+ select VIDEOBUF_VMALLOC
+ default n
+ help
+ Say Y here if you want support for the Sensoray 2255 USB device.
+ This driver can be compiled as a module, called s2255drv.
+
diff --git a/drivers/media/usb/s2255/Makefile b/drivers/media/usb/s2255/Makefile
new file mode 100644
index 000000000000..197d0bb2adfd
--- /dev/null
+++ b/drivers/media/usb/s2255/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_USB_S2255) += s2255drv.o
+
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
new file mode 100644
index 000000000000..2191f6ddf9e7
--- /dev/null
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -0,0 +1,2690 @@
+/*
+ * s2255drv.c - a driver for the Sensoray 2255 USB video capture device
+ *
+ * Copyright (C) 2007-2010 by Sensoray Company Inc.
+ * Dean Anderson
+ *
+ * Some video buffer code based on vivi driver:
+ *
+ * Sensoray 2255 device supports 4 simultaneous channels.
+ * The channels are not "crossbar" inputs, they are physically
+ * attached to separate video decoders.
+ *
+ * Because of USB2.0 bandwidth limitations. There is only a
+ * certain amount of data which may be transferred at one time.
+ *
+ * Example maximum bandwidth utilization:
+ *
+ * -full size, color mode YUYV or YUV422P: 2 channels at once
+ * -full or half size Grey scale: all 4 channels at once
+ * -half size, color mode YUYV or YUV422P: all 4 channels at once
+ * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
+ * at once.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/mm.h>
+#include <media/videobuf-vmalloc.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#define S2255_VERSION "1.22.1"
+#define FIRMWARE_FILE_NAME "f2255usb.bin"
+
+/* default JPEG quality */
+#define S2255_DEF_JPEG_QUAL 50
+/* vendor request in */
+#define S2255_VR_IN 0
+/* vendor request out */
+#define S2255_VR_OUT 1
+/* firmware query */
+#define S2255_VR_FW 0x30
+/* USB endpoint number for configuring the device */
+#define S2255_CONFIG_EP 2
+/* maximum time for DSP to start responding after last FW word loaded(ms) */
+#define S2255_DSP_BOOTTIME 800
+/* maximum time to wait for firmware to load (ms) */
+#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME)
+#define S2255_DEF_BUFS 16
+#define S2255_SETMODE_TIMEOUT 500
+#define S2255_VIDSTATUS_TIMEOUT 350
+#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL)
+#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL)
+#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01)
+#define S2255_RESPONSE_FW cpu_to_le32(0x10)
+#define S2255_RESPONSE_STATUS cpu_to_le32(0x20)
+#define S2255_USB_XFER_SIZE (16 * 1024)
+#define MAX_CHANNELS 4
+#define SYS_FRAMES 4
+/* maximum size is PAL full size plus room for the marker header(s) */
+#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096)
+#define DEF_USB_BLOCK S2255_USB_XFER_SIZE
+#define LINE_SZ_4CIFS_NTSC 640
+#define LINE_SZ_2CIFS_NTSC 640
+#define LINE_SZ_1CIFS_NTSC 320
+#define LINE_SZ_4CIFS_PAL 704
+#define LINE_SZ_2CIFS_PAL 704
+#define LINE_SZ_1CIFS_PAL 352
+#define NUM_LINES_4CIFS_NTSC 240
+#define NUM_LINES_2CIFS_NTSC 240
+#define NUM_LINES_1CIFS_NTSC 240
+#define NUM_LINES_4CIFS_PAL 288
+#define NUM_LINES_2CIFS_PAL 288
+#define NUM_LINES_1CIFS_PAL 288
+#define LINE_SZ_DEF 640
+#define NUM_LINES_DEF 240
+
+
+/* predefined settings */
+#define FORMAT_NTSC 1
+#define FORMAT_PAL 2
+
+#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */
+#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */
+#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */
+/* SCALE_4CIFSI is the 2 fields interpolated into one */
+#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */
+
+#define COLOR_YUVPL 1 /* YUV planar */
+#define COLOR_YUVPK 2 /* YUV packed */
+#define COLOR_Y8 4 /* monochrome */
+#define COLOR_JPG 5 /* JPEG */
+
+#define MASK_COLOR 0x000000ff
+#define MASK_JPG_QUALITY 0x0000ff00
+#define MASK_INPUT_TYPE 0x000f0000
+/* frame decimation. */
+#define FDEC_1 1 /* capture every frame. default */
+#define FDEC_2 2 /* capture every 2nd frame */
+#define FDEC_3 3 /* capture every 3rd frame */
+#define FDEC_5 5 /* capture every 5th frame */
+
+/*-------------------------------------------------------
+ * Default mode parameters.
+ *-------------------------------------------------------*/
+#define DEF_SCALE SCALE_4CIFS
+#define DEF_COLOR COLOR_YUVPL
+#define DEF_FDEC FDEC_1
+#define DEF_BRIGHT 0
+#define DEF_CONTRAST 0x5c
+#define DEF_SATURATION 0x80
+#define DEF_HUE 0
+
+/* usb config commands */
+#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de)
+#define CMD_2255 0xc2255000
+#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10))
+#define CMD_START cpu_to_le32((CMD_2255 | 0x20))
+#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30))
+#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40))
+
+struct s2255_mode {
+ u32 format; /* input video format (NTSC, PAL) */
+ u32 scale; /* output video scale */
+ u32 color; /* output video color format */
+ u32 fdec; /* frame decimation */
+ u32 bright; /* brightness */
+ u32 contrast; /* contrast */
+ u32 saturation; /* saturation */
+ u32 hue; /* hue (NTSC only)*/
+ u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/
+ u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */
+ u32 restart; /* if DSP requires restart */
+};
+
+
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
+
+/* frame structure */
+struct s2255_framei {
+ unsigned long size;
+ unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
+ void *lpvbits; /* image data */
+ unsigned long cur_size; /* current data copied to it */
+};
+
+/* image buffer structure */
+struct s2255_bufferi {
+ unsigned long dwFrames; /* number of frames in buffer */
+ struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */
+};
+
+#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \
+ DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
+ DEF_HUE, 0, DEF_USB_BLOCK, 0}
+
+struct s2255_dmaqueue {
+ struct list_head active;
+ struct s2255_dev *dev;
+};
+
+/* for firmware loading, fw_state */
+#define S2255_FW_NOTLOADED 0
+#define S2255_FW_LOADED_DSPWAIT 1
+#define S2255_FW_SUCCESS 2
+#define S2255_FW_FAILED 3
+#define S2255_FW_DISCONNECTING 4
+#define S2255_FW_MARKER cpu_to_le32(0x22552f2f)
+/* 2255 read states */
+#define S2255_READ_IDLE 0
+#define S2255_READ_FRAME 1
+struct s2255_fw {
+ int fw_loaded;
+ int fw_size;
+ struct urb *fw_urb;
+ atomic_t fw_state;
+ void *pfw_data;
+ wait_queue_head_t wait_fw;
+ const struct firmware *fw;
+};
+
+struct s2255_pipeinfo {
+ u32 max_transfer_size;
+ u32 cur_transfer_size;
+ u8 *transfer_buffer;
+ u32 state;
+ void *stream_urb;
+ void *dev; /* back pointer to s2255_dev struct*/
+ u32 err_count;
+ u32 idx;
+};
+
+struct s2255_fmt; /*forward declaration */
+struct s2255_dev;
+
+struct s2255_channel {
+ struct video_device vdev;
+ int resources;
+ struct s2255_dmaqueue vidq;
+ struct s2255_bufferi buffer;
+ struct s2255_mode mode;
+ /* jpeg compression */
+ struct v4l2_jpegcompression jc;
+ /* capture parameters (for high quality mode full size) */
+ struct v4l2_captureparm cap_parm;
+ int cur_frame;
+ int last_frame;
+
+ int b_acquire;
+ /* allocated image size */
+ unsigned long req_image_size;
+ /* received packet size */
+ unsigned long pkt_size;
+ int bad_payload;
+ unsigned long frame_count;
+ /* if JPEG image */
+ int jpg_size;
+ /* if channel configured to default state */
+ int configured;
+ wait_queue_head_t wait_setmode;
+ int setmode_ready;
+ /* video status items */
+ int vidstatus;
+ wait_queue_head_t wait_vidstatus;
+ int vidstatus_ready;
+ unsigned int width;
+ unsigned int height;
+ const struct s2255_fmt *fmt;
+ int idx; /* channel number on device, 0-3 */
+};
+
+
+struct s2255_dev {
+ struct s2255_channel channel[MAX_CHANNELS];
+ struct v4l2_device v4l2_dev;
+ atomic_t num_channels;
+ int frames;
+ struct mutex lock; /* channels[].vdev.lock */
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ u8 read_endpoint;
+ struct timer_list timer;
+ struct s2255_fw *fw_data;
+ struct s2255_pipeinfo pipe;
+ u32 cc; /* current channel */
+ int frame_ready;
+ int chn_ready;
+ spinlock_t slock;
+ /* dsp firmware version (f2255usb.bin) */
+ int dsp_fw_ver;
+ u16 pid; /* product id */
+};
+
+static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
+}
+
+struct s2255_fmt {
+ char *name;
+ u32 fourcc;
+ int depth;
+};
+
+/* buffer for one video frame */
+struct s2255_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct videobuf_buffer vb;
+ const struct s2255_fmt *fmt;
+};
+
+struct s2255_fh {
+ struct s2255_dev *dev;
+ struct videobuf_queue vb_vidq;
+ enum v4l2_buf_type type;
+ struct s2255_channel *channel;
+ int resources;
+};
+
+/* current cypress EEPROM firmware version */
+#define S2255_CUR_USB_FWVER ((3 << 8) | 12)
+/* current DSP FW version */
+#define S2255_CUR_DSP_FWVER 10104
+/* Need DSP version 5+ for video status feature */
+#define S2255_MIN_DSP_STATUS 5
+#define S2255_MIN_DSP_COLORFILTER 8
+#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC)
+
+/* private V4L2 controls */
+
+/*
+ * The following chart displays how COLORFILTER should be set
+ * =========================================================
+ * = fourcc = COLORFILTER =
+ * = ===============================
+ * = = 0 = 1 =
+ * =========================================================
+ * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome=
+ * = = s-video or = composite =
+ * = = B/W camera = input =
+ * =========================================================
+ * = other = color, svideo = color, =
+ * = = = composite =
+ * =========================================================
+ *
+ * Notes:
+ * channels 0-3 on 2255 are composite
+ * channels 0-1 on 2257 are composite, 2-3 are s-video
+ * If COLORFILTER is 0 with a composite color camera connected,
+ * the output will appear monochrome but hatching
+ * will occur.
+ * COLORFILTER is different from "color killer" and "color effects"
+ * for reasons above.
+ */
+#define S2255_V4L2_YC_ON 1
+#define S2255_V4L2_YC_OFF 0
+#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0)
+
+/* frame prefix size (sent once every frame) */
+#define PREFIX_SIZE 512
+
+/* Channels on box are in reverse order */
+static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
+
+static int debug;
+static int *s2255_debug = &debug;
+
+static int s2255_start_readpipe(struct s2255_dev *dev);
+static void s2255_stop_readpipe(struct s2255_dev *dev);
+static int s2255_start_acquire(struct s2255_channel *channel);
+static int s2255_stop_acquire(struct s2255_channel *channel);
+static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf,
+ int jpgsize);
+static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode);
+static int s2255_board_shutdown(struct s2255_dev *dev);
+static void s2255_fwload_start(struct s2255_dev *dev, int reset);
+static void s2255_destroy(struct s2255_dev *dev);
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
+ u16 index, u16 value, void *buf,
+ s32 buf_len, int bOut);
+
+/* dev_err macro with driver name */
+#define S2255_DRIVER_NAME "s2255"
+#define s2255_dev_err(dev, fmt, arg...) \
+ dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
+
+#define dprintk(level, fmt, arg...) \
+ do { \
+ if (*s2255_debug >= (level)) { \
+ printk(KERN_DEBUG S2255_DRIVER_NAME \
+ ": " fmt, ##arg); \
+ } \
+ } while (0)
+
+static struct usb_driver s2255_driver;
+
+/* Declare static vars that will be used as parameters */
+static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
+
+/* start video number */
+static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+
+/* Enable jpeg capture. */
+static int jpeg_enable = 1;
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
+module_param(video_nr, int, 0644);
+MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
+module_param(jpeg_enable, int, 0644);
+MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
+
+/* USB device table */
+#define USB_SENSORAY_VID 0x1943
+static struct usb_device_id s2255_table[] = {
+ {USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
+ {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, s2255_table);
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(400)
+
+/* image formats. */
+/* JPEG formats must be defined last to support jpeg_enable parameter */
+static const struct s2255_fmt formats[] = {
+ {
+ .name = "4:2:2, planar, YUV422P",
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .depth = 16
+
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16
+
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16
+ }, {
+ .name = "8bpp GREY",
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .depth = 8
+ }, {
+ .name = "JPG",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .depth = 24
+ }, {
+ .name = "MJPG",
+ .fourcc = V4L2_PIX_FMT_MJPEG,
+ .depth = 24
+ }
+};
+
+static int norm_maxw(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
+}
+
+static int norm_maxh(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
+}
+
+static int norm_minw(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
+}
+
+static int norm_minh(struct video_device *vdev)
+{
+ return (vdev->current_norm & V4L2_STD_NTSC) ?
+ (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
+}
+
+
+/*
+ * TODO: fixme: move YUV reordering to hardware
+ * converts 2255 planar format to yuyv or uyvy
+ */
+static void planar422p_to_yuv_packed(const unsigned char *in,
+ unsigned char *out,
+ int width, int height,
+ int fmt)
+{
+ unsigned char *pY;
+ unsigned char *pCb;
+ unsigned char *pCr;
+ unsigned long size = height * width;
+ unsigned int i;
+ pY = (unsigned char *)in;
+ pCr = (unsigned char *)in + height * width;
+ pCb = (unsigned char *)in + height * width + (height * width / 2);
+ for (i = 0; i < size * 2; i += 4) {
+ out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
+ out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
+ out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
+ out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
+ }
+ return;
+}
+
+static void s2255_reset_dsppower(struct s2255_dev *dev)
+{
+ s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
+ msleep(10);
+ s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
+ msleep(600);
+ s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
+ return;
+}
+
+/* kickstarts the firmware loading. from probe
+ */
+static void s2255_timer(unsigned long user_data)
+{
+ struct s2255_fw *data = (struct s2255_fw *)user_data;
+ dprintk(100, "%s\n", __func__);
+ if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+ printk(KERN_ERR "s2255: can't submit urb\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+}
+
+
+/* this loads the firmware asynchronously.
+ Originally this was done synchroously in probe.
+ But it is better to load it asynchronously here than block
+ inside the probe function. Blocking inside probe affects boot time.
+ FW loading is triggered by the timer in the probe function
+*/
+static void s2255_fwchunk_complete(struct urb *urb)
+{
+ struct s2255_fw *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+ dprintk(100, "%s: udev %p urb %p", __func__, udev, urb);
+ if (urb->status) {
+ dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+ if (data->fw_urb == NULL) {
+ s2255_dev_err(&udev->dev, "disconnected\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+#define CHUNK_SIZE 512
+ /* all USB transfers must be done with continuous kernel memory.
+ can't allocate more than 128k in current linux kernel, so
+ upload the firmware in chunks
+ */
+ if (data->fw_loaded < data->fw_size) {
+ len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
+ data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
+
+ if (len < CHUNK_SIZE)
+ memset(data->pfw_data, 0, CHUNK_SIZE);
+
+ dprintk(100, "completed len %d, loaded %d \n", len,
+ data->fw_loaded);
+
+ memcpy(data->pfw_data,
+ (char *) data->fw->data + data->fw_loaded, len);
+
+ usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
+ data->pfw_data, CHUNK_SIZE,
+ s2255_fwchunk_complete, data);
+ if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
+ dev_err(&udev->dev, "failed submit URB\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+ data->fw_loaded += len;
+ } else {
+ atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
+ dprintk(100, "%s: firmware upload complete\n", __func__);
+ }
+ return;
+
+}
+
+static int s2255_got_frame(struct s2255_channel *channel, int jpgsize)
+{
+ struct s2255_dmaqueue *dma_q = &channel->vidq;
+ struct s2255_buffer *buf;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ unsigned long flags = 0;
+ int rc = 0;
+ spin_lock_irqsave(&dev->slock, flags);
+ if (list_empty(&dma_q->active)) {
+ dprintk(1, "No active queue to serve\n");
+ rc = -1;
+ goto unlock;
+ }
+ buf = list_entry(dma_q->active.next,
+ struct s2255_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ do_gettimeofday(&buf->vb.ts);
+ s2255_fillbuff(channel, buf, jpgsize);
+ wake_up(&buf->vb.done);
+ dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
+unlock:
+ spin_unlock_irqrestore(&dev->slock, flags);
+ return rc;
+}
+
+static const struct s2255_fmt *format_by_fourcc(int fourcc)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (-1 == formats[i].fourcc)
+ continue;
+ if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
+ continue;
+ if (formats[i].fourcc == fourcc)
+ return formats + i;
+ }
+ return NULL;
+}
+
+/* video buffer vmalloc implementation based partly on VIVI driver which is
+ * Copyright (c) 2006 by
+ * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
+ * Ted Walther <ted--a.t--enumera.com>
+ * John Sokol <sokol--a.t--videotechnology.com>
+ * http://v4l.videotechnology.com/
+ *
+ */
+static void s2255_fillbuff(struct s2255_channel *channel,
+ struct s2255_buffer *buf, int jpgsize)
+{
+ int pos = 0;
+ struct timeval ts;
+ const char *tmpbuf;
+ char *vbuf = videobuf_to_vmalloc(&buf->vb);
+ unsigned long last_frame;
+
+ if (!vbuf)
+ return;
+ last_frame = channel->last_frame;
+ if (last_frame != -1) {
+ tmpbuf =
+ (const char *)channel->buffer.frame[last_frame].lpvbits;
+ switch (buf->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
+ vbuf, buf->vb.width,
+ buf->vb.height,
+ buf->fmt->fourcc);
+ break;
+ case V4L2_PIX_FMT_GREY:
+ memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_MJPEG:
+ buf->vb.size = jpgsize;
+ memcpy(vbuf, tmpbuf, buf->vb.size);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ memcpy(vbuf, tmpbuf,
+ buf->vb.width * buf->vb.height * 2);
+ break;
+ default:
+ printk(KERN_DEBUG "s2255: unknown format?\n");
+ }
+ channel->last_frame = -1;
+ } else {
+ printk(KERN_ERR "s2255: =======no frame\n");
+ return;
+
+ }
+ dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
+ (unsigned long)vbuf, pos);
+ /* tell v4l buffer was filled */
+
+ buf->vb.field_count = channel->frame_count * 2;
+ do_gettimeofday(&ts);
+ buf->vb.ts = ts;
+ buf->vb.state = VIDEOBUF_DONE;
+}
+
+
+/* ------------------------------------------------------------------
+ Videobuf operations
+ ------------------------------------------------------------------*/
+
+static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ *size = channel->width * channel->height * (channel->fmt->depth >> 3);
+
+ if (0 == *count)
+ *count = S2255_DEF_BUFS;
+
+ if (*size * *count > vid_limit * 1024 * 1024)
+ *count = (vid_limit * 1024 * 1024) / *size;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
+{
+ dprintk(4, "%s\n", __func__);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ int rc;
+ int w = channel->width;
+ int h = channel->height;
+ dprintk(4, "%s, field=%d\n", __func__, field);
+ if (channel->fmt == NULL)
+ return -EINVAL;
+
+ if ((w < norm_minw(&channel->vdev)) ||
+ (w > norm_maxw(&channel->vdev)) ||
+ (h < norm_minh(&channel->vdev)) ||
+ (h > norm_maxh(&channel->vdev))) {
+ dprintk(4, "invalid buffer prepare\n");
+ return -EINVAL;
+ }
+ buf->vb.size = w * h * (channel->fmt->depth >> 3);
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
+ dprintk(4, "invalid buffer prepare\n");
+ return -EINVAL;
+ }
+
+ buf->fmt = channel->fmt;
+ buf->vb.width = w;
+ buf->vb.height = h;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ struct s2255_fh *fh = vq->priv_data;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dmaqueue *vidq = &channel->vidq;
+ dprintk(1, "%s\n", __func__);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+{
+ struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
+ struct s2255_fh *fh = vq->priv_data;
+ dprintk(4, "%s %d\n", __func__, fh->channel->idx);
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops s2255_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+
+static int res_get(struct s2255_fh *fh)
+{
+ struct s2255_channel *channel = fh->channel;
+ /* is it free? */
+ if (channel->resources)
+ return 0; /* no, someone else uses it */
+ /* it's free, grab it */
+ channel->resources = 1;
+ fh->resources = 1;
+ dprintk(1, "s2255: res: get\n");
+ return 1;
+}
+
+static int res_locked(struct s2255_fh *fh)
+{
+ return fh->channel->resources;
+}
+
+static int res_check(struct s2255_fh *fh)
+{
+ return fh->resources;
+}
+
+
+static void res_free(struct s2255_fh *fh)
+{
+ struct s2255_channel *channel = fh->channel;
+ channel->resources = 0;
+ fh->resources = 0;
+ dprintk(1, "res: put\n");
+}
+
+static int vidioc_querymenu(struct file *file, void *priv,
+ struct v4l2_querymenu *qmenu)
+{
+ static const char *colorfilter[] = {
+ "Off",
+ "On",
+ NULL
+ };
+ if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) {
+ int i;
+ const char **menu_items = colorfilter;
+ for (i = 0; i < qmenu->index && menu_items[i]; i++)
+ ; /* do nothing (from v4l2-common.c) */
+ if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qmenu->name, menu_items[qmenu->index],
+ sizeof(qmenu->name));
+ return 0;
+ }
+ return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev = fh->dev;
+ strlcpy(cap->driver, "s2255", sizeof(cap->driver));
+ strlcpy(cap->card, "s2255", sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ int index = f->index;
+
+ if (index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+ if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
+ return -EINVAL;
+ dprintk(4, "name %s\n", formats[index].name);
+ strlcpy(f->description, formats[index].name, sizeof(f->description));
+ f->pixelformat = formats[index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+
+ f->fmt.pix.width = channel->width;
+ f->fmt.pix.height = channel->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = channel->fmt->fourcc;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3);
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ const struct s2255_fmt *fmt;
+ enum v4l2_field field;
+ int b_any_field = 0;
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ int is_ntsc;
+ is_ntsc =
+ (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ b_any_field = 1;
+
+ dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n",
+ __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
+ if (is_ntsc) {
+ /* NTSC */
+ if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
+ f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
+ if (b_any_field) {
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (!((field == V4L2_FIELD_INTERLACED) ||
+ (field == V4L2_FIELD_SEQ_TB) ||
+ (field == V4L2_FIELD_INTERLACED_TB))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ } else {
+ f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
+ if (b_any_field) {
+ field = V4L2_FIELD_TOP;
+ } else if (!((field == V4L2_FIELD_TOP) ||
+ (field == V4L2_FIELD_BOTTOM))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+
+ }
+ if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
+ else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
+ else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
+ f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+ else
+ f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
+ } else {
+ /* PAL */
+ if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
+ f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
+ if (b_any_field) {
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (!((field == V4L2_FIELD_INTERLACED) ||
+ (field == V4L2_FIELD_SEQ_TB) ||
+ (field == V4L2_FIELD_INTERLACED_TB))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ } else {
+ f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
+ if (b_any_field) {
+ field = V4L2_FIELD_TOP;
+ } else if (!((field == V4L2_FIELD_TOP) ||
+ (field == V4L2_FIELD_BOTTOM))) {
+ dprintk(1, "unsupported field setting\n");
+ return -EINVAL;
+ }
+ }
+ if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
+ field = V4L2_FIELD_SEQ_TB;
+ } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
+ f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ } else {
+ f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
+ field = V4L2_FIELD_TOP;
+ }
+ }
+ f->fmt.pix.field = field;
+ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ dprintk(50, "%s: set width %d height %d field %d\n", __func__,
+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ const struct s2255_fmt *fmt;
+ struct videobuf_queue *q = &fh->vb_vidq;
+ struct s2255_mode mode;
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, fh, f);
+
+ if (ret < 0)
+ return ret;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ mutex_lock(&q->vb_lock);
+
+ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+ dprintk(1, "queue busy\n");
+ ret = -EBUSY;
+ goto out_s_fmt;
+ }
+
+ if (res_locked(fh)) {
+ dprintk(1, "%s: channel busy\n", __func__);
+ ret = -EBUSY;
+ goto out_s_fmt;
+ }
+ mode = channel->mode;
+ channel->fmt = fmt;
+ channel->width = f->fmt.pix.width;
+ channel->height = f->fmt.pix.height;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+ if (channel->width > norm_minw(&channel->vdev)) {
+ if (channel->height > norm_minh(&channel->vdev)) {
+ if (channel->cap_parm.capturemode &
+ V4L2_MODE_HIGHQUALITY)
+ mode.scale = SCALE_4CIFSI;
+ else
+ mode.scale = SCALE_4CIFS;
+ } else
+ mode.scale = SCALE_2CIFS;
+
+ } else {
+ mode.scale = SCALE_1CIFS;
+ }
+ /* color mode */
+ switch (channel->fmt->fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_Y8;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_MJPEG:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_JPG;
+ mode.color |= (channel->jc.quality << 8);
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_YUVPL;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ default:
+ mode.color &= ~MASK_COLOR;
+ mode.color |= COLOR_YUVPK;
+ break;
+ }
+ if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR))
+ mode.restart = 1;
+ else if (mode.scale != channel->mode.scale)
+ mode.restart = 1;
+ else if (mode.format != channel->mode.format)
+ mode.restart = 1;
+ channel->mode = mode;
+ (void) s2255_set_mode(channel, &mode);
+ ret = 0;
+out_s_fmt:
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_reqbufs(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_querybuf(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_qbuf(&fh->vb_vidq, p);
+ return rc;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ int rc;
+ struct s2255_fh *fh = priv;
+ rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
+ return rc;
+}
+
+/* write to the configuration pipe, synchronously */
+static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
+ int size)
+{
+ int pipe;
+ int done;
+ long retval = -1;
+ if (udev) {
+ pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
+ retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
+ }
+ return retval;
+}
+
+static u32 get_transfer_size(struct s2255_mode *mode)
+{
+ int linesPerFrame = LINE_SZ_DEF;
+ int pixelsPerLine = NUM_LINES_DEF;
+ u32 outImageSize;
+ u32 usbInSize;
+ unsigned int mask_mult;
+
+ if (mode == NULL)
+ return 0;
+
+ if (mode->format == FORMAT_NTSC) {
+ switch (mode->scale) {
+ case SCALE_4CIFS:
+ case SCALE_4CIFSI:
+ linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
+ pixelsPerLine = LINE_SZ_4CIFS_NTSC;
+ break;
+ case SCALE_2CIFS:
+ linesPerFrame = NUM_LINES_2CIFS_NTSC;
+ pixelsPerLine = LINE_SZ_2CIFS_NTSC;
+ break;
+ case SCALE_1CIFS:
+ linesPerFrame = NUM_LINES_1CIFS_NTSC;
+ pixelsPerLine = LINE_SZ_1CIFS_NTSC;
+ break;
+ default:
+ break;
+ }
+ } else if (mode->format == FORMAT_PAL) {
+ switch (mode->scale) {
+ case SCALE_4CIFS:
+ case SCALE_4CIFSI:
+ linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
+ pixelsPerLine = LINE_SZ_4CIFS_PAL;
+ break;
+ case SCALE_2CIFS:
+ linesPerFrame = NUM_LINES_2CIFS_PAL;
+ pixelsPerLine = LINE_SZ_2CIFS_PAL;
+ break;
+ case SCALE_1CIFS:
+ linesPerFrame = NUM_LINES_1CIFS_PAL;
+ pixelsPerLine = LINE_SZ_1CIFS_PAL;
+ break;
+ default:
+ break;
+ }
+ }
+ outImageSize = linesPerFrame * pixelsPerLine;
+ if ((mode->color & MASK_COLOR) != COLOR_Y8) {
+ /* 2 bytes/pixel if not monochrome */
+ outImageSize *= 2;
+ }
+
+ /* total bytes to send including prefix and 4K padding;
+ must be a multiple of USB_READ_SIZE */
+ usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */
+ mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
+ /* if size not a multiple of USB_READ_SIZE */
+ if (usbInSize & ~mask_mult)
+ usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
+ return usbInSize;
+}
+
+static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
+{
+ struct device *dev = &sdev->udev->dev;
+ dev_info(dev, "------------------------------------------------\n");
+ dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
+ dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
+ dev_info(dev, "bright: 0x%x\n", mode->bright);
+ dev_info(dev, "------------------------------------------------\n");
+}
+
+/*
+ * set mode is the function which controls the DSP.
+ * the restart parameter in struct s2255_mode should be set whenever
+ * the image size could change via color format, video system or image
+ * size.
+ * When the restart parameter is set, we sleep for ONE frame to allow the
+ * DSP time to get the new frame
+ */
+static int s2255_set_mode(struct s2255_channel *channel,
+ struct s2255_mode *mode)
+{
+ int res;
+ __le32 *buffer;
+ unsigned long chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ dprintk(3, "%s channel: %d\n", __func__, channel->idx);
+ /* if JPEG, set the quality */
+ if ((mode->color & MASK_COLOR) == COLOR_JPG) {
+ mode->color &= ~MASK_COLOR;
+ mode->color |= COLOR_JPG;
+ mode->color &= ~MASK_JPG_QUALITY;
+ mode->color |= (channel->jc.quality << 8);
+ }
+ /* save the mode */
+ channel->mode = *mode;
+ channel->req_image_size = get_transfer_size(mode);
+ dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size);
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* set the mode */
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_SET_MODE;
+ memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode));
+ channel->setmode_ready = 0;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (debug)
+ s2255_print_cfg(dev, mode);
+ kfree(buffer);
+ /* wait at least 3 frames before continuing */
+ if (mode->restart) {
+ wait_event_timeout(channel->wait_setmode,
+ (channel->setmode_ready != 0),
+ msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
+ if (channel->setmode_ready != 1) {
+ printk(KERN_DEBUG "s2255: no set mode response\n");
+ res = -EFAULT;
+ }
+ }
+ /* clear the restart flag */
+ channel->mode.restart = 0;
+ dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res);
+ return res;
+}
+
+static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus)
+{
+ int res;
+ __le32 *buffer;
+ u32 chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ dprintk(4, "%s chan %d\n", __func__, channel->idx);
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* form the get vid status command */
+ buffer[0] = IN_DATA_TOKEN;
+ buffer[1] = (__le32) cpu_to_le32(chn_rev);
+ buffer[2] = CMD_STATUS;
+ *pstatus = 0;
+ channel->vidstatus_ready = 0;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ kfree(buffer);
+ wait_event_timeout(channel->wait_vidstatus,
+ (channel->vidstatus_ready != 0),
+ msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
+ if (channel->vidstatus_ready != 1) {
+ printk(KERN_DEBUG "s2255: no vidstatus response\n");
+ res = -EFAULT;
+ }
+ *pstatus = channel->vidstatus;
+ dprintk(4, "%s, vid status %d\n", __func__, *pstatus);
+ return res;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ int res;
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ int j;
+ dprintk(4, "%s\n", __func__);
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ dev_err(&dev->udev->dev, "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (i != fh->type) {
+ dev_err(&dev->udev->dev, "invalid fh type1\n");
+ return -EINVAL;
+ }
+
+ if (!res_get(fh)) {
+ s2255_dev_err(&dev->udev->dev, "stream busy\n");
+ return -EBUSY;
+ }
+ channel->last_frame = -1;
+ channel->bad_payload = 0;
+ channel->cur_frame = 0;
+ channel->frame_count = 0;
+ for (j = 0; j < SYS_FRAMES; j++) {
+ channel->buffer.frame[j].ulState = S2255_READ_IDLE;
+ channel->buffer.frame[j].cur_size = 0;
+ }
+ res = videobuf_streamon(&fh->vb_vidq);
+ if (res == 0) {
+ s2255_start_acquire(channel);
+ channel->b_acquire = 1;
+ } else
+ res_free(fh);
+
+ return res;
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct s2255_fh *fh = priv;
+ dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx);
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ printk(KERN_ERR "invalid fh type0\n");
+ return -EINVAL;
+ }
+ if (i != fh->type) {
+ printk(KERN_ERR "invalid type i\n");
+ return -EINVAL;
+ }
+ s2255_stop_acquire(fh->channel);
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_mode mode;
+ struct videobuf_queue *q = &fh->vb_vidq;
+ int ret = 0;
+ mutex_lock(&q->vb_lock);
+ if (videobuf_queue_is_busy(q)) {
+ dprintk(1, "queue busy\n");
+ ret = -EBUSY;
+ goto out_s_std;
+ }
+ if (res_locked(fh)) {
+ dprintk(1, "can't change standard after started\n");
+ ret = -EBUSY;
+ goto out_s_std;
+ }
+ mode = fh->channel->mode;
+ if (*i & V4L2_STD_NTSC) {
+ dprintk(4, "%s NTSC\n", __func__);
+ /* if changing format, reset frame decimation/intervals */
+ if (mode.format != FORMAT_NTSC) {
+ mode.restart = 1;
+ mode.format = FORMAT_NTSC;
+ mode.fdec = FDEC_1;
+ }
+ } else if (*i & V4L2_STD_PAL) {
+ dprintk(4, "%s PAL\n", __func__);
+ if (mode.format != FORMAT_PAL) {
+ mode.restart = 1;
+ mode.format = FORMAT_PAL;
+ mode.fdec = FDEC_1;
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ if (mode.restart)
+ s2255_set_mode(fh->channel, &mode);
+out_s_std:
+ mutex_unlock(&q->vb_lock);
+ return ret;
+}
+
+/* Sensoray 2255 is a multiple channel capture device.
+ It does not have a "crossbar" of inputs.
+ We use one V4L device per channel. The user must
+ be aware that certain combinations are not allowed.
+ For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
+ at once in color(you can do full fps on 4 channels with greyscale.
+*/
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ u32 status = 0;
+ if (inp->index != 0)
+ return -EINVAL;
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = S2255_NORMS;
+ inp->status = 0;
+ if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
+ int rc;
+ rc = s2255_cmd_status(fh->channel, &status);
+ dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status);
+ if (rc == 0)
+ inp->status = (status & 0x01) ? 0
+ : V4L2_IN_ST_NO_SIGNAL;
+ }
+ switch (dev->pid) {
+ case 0x2255:
+ default:
+ strlcpy(inp->name, "Composite", sizeof(inp->name));
+ break;
+ case 0x2257:
+ strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video",
+ sizeof(inp->name));
+ break;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+ return 0;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dev *dev = fh->dev;
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT);
+ break;
+ case V4L2_CID_CONTRAST:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST);
+ break;
+ case V4L2_CID_SATURATION:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION);
+ break;
+ case V4L2_CID_HUE:
+ v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE);
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ strlcpy(qc->name, "Color Filter", sizeof(qc->name));
+ qc->type = V4L2_CTRL_TYPE_MENU;
+ qc->minimum = 0;
+ qc->maximum = 1;
+ qc->step = 1;
+ qc->default_value = 1;
+ qc->flags = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dprintk(4, "%s, id %d\n", __func__, qc->id);
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_dev *dev = fh->dev;
+ struct s2255_channel *channel = fh->channel;
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = channel->mode.bright;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = channel->mode.contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = channel->mode.saturation;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = channel->mode.hue;
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16);
+ break;
+ default:
+ return -EINVAL;
+ }
+ dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ struct s2255_mode mode;
+ mode = channel->mode;
+ dprintk(4, "%s\n", __func__);
+ /* update the mode to the corresponding value */
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ mode.bright = ctrl->value;
+ break;
+ case V4L2_CID_CONTRAST:
+ mode.contrast = ctrl->value;
+ break;
+ case V4L2_CID_HUE:
+ mode.hue = ctrl->value;
+ break;
+ case V4L2_CID_SATURATION:
+ mode.saturation = ctrl->value;
+ break;
+ case V4L2_CID_PRIVATE_COLORFILTER:
+ if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ return -EINVAL;
+ if ((dev->pid == 0x2257) && (channel->idx > 1))
+ return -EINVAL;
+ mode.color &= ~MASK_INPUT_TYPE;
+ mode.color |= ((ctrl->value ? 0 : 1) << 16);
+ break;
+ default:
+ return -EINVAL;
+ }
+ mode.restart = 0;
+ /* set mode here. Note: stream does not need restarted.
+ some V4L programs restart stream unnecessarily
+ after a s_crtl.
+ */
+ s2255_set_mode(fh->channel, &mode);
+ return 0;
+}
+
+static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ *jc = channel->jc;
+ dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ return 0;
+}
+
+static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ const struct v4l2_jpegcompression *jc)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ if (jc->quality < 0 || jc->quality > 100)
+ return -EINVAL;
+ channel->jc.quality = jc->quality;
+ dprintk(2, "%s: quality %d\n", __func__, jc->quality);
+ return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct s2255_fh *fh = priv;
+ __u32 def_num, def_dem;
+ struct s2255_channel *channel = fh->channel;
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ memset(sp, 0, sizeof(struct v4l2_streamparm));
+ sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ sp->parm.capture.capturemode = channel->cap_parm.capturemode;
+ def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000;
+ def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000;
+ sp->parm.capture.timeperframe.denominator = def_dem;
+ switch (channel->mode.fdec) {
+ default:
+ case FDEC_1:
+ sp->parm.capture.timeperframe.numerator = def_num;
+ break;
+ case FDEC_2:
+ sp->parm.capture.timeperframe.numerator = def_num * 2;
+ break;
+ case FDEC_3:
+ sp->parm.capture.timeperframe.numerator = def_num * 3;
+ break;
+ case FDEC_5:
+ sp->parm.capture.timeperframe.numerator = def_num * 5;
+ break;
+ }
+ dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__,
+ sp->parm.capture.capturemode,
+ sp->parm.capture.timeperframe.numerator,
+ sp->parm.capture.timeperframe.denominator);
+ return 0;
+}
+
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *sp)
+{
+ struct s2255_fh *fh = priv;
+ struct s2255_channel *channel = fh->channel;
+ struct s2255_mode mode;
+ int fdec = FDEC_1;
+ __u32 def_num, def_dem;
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ mode = channel->mode;
+ /* high quality capture mode requires a stream restart */
+ if (channel->cap_parm.capturemode
+ != sp->parm.capture.capturemode && res_locked(fh))
+ return -EBUSY;
+ def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
+ def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
+ if (def_dem != sp->parm.capture.timeperframe.denominator)
+ sp->parm.capture.timeperframe.numerator = def_num;
+ else if (sp->parm.capture.timeperframe.numerator <= def_num)
+ sp->parm.capture.timeperframe.numerator = def_num;
+ else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
+ sp->parm.capture.timeperframe.numerator = def_num * 2;
+ fdec = FDEC_2;
+ } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
+ sp->parm.capture.timeperframe.numerator = def_num * 3;
+ fdec = FDEC_3;
+ } else {
+ sp->parm.capture.timeperframe.numerator = def_num * 5;
+ fdec = FDEC_5;
+ }
+ mode.fdec = fdec;
+ sp->parm.capture.timeperframe.denominator = def_dem;
+ s2255_set_mode(channel, &mode);
+ dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
+ __func__,
+ sp->parm.capture.capturemode,
+ sp->parm.capture.timeperframe.numerator,
+ sp->parm.capture.timeperframe.denominator, fdec);
+ return 0;
+}
+
+static int vidioc_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fe)
+{
+ int is_ntsc = 0;
+#define NUM_FRAME_ENUMS 4
+ int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
+ if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS)
+ return -EINVAL;
+ switch (fe->width) {
+ case 640:
+ if (fe->height != 240 && fe->height != 480)
+ return -EINVAL;
+ is_ntsc = 1;
+ break;
+ case 320:
+ if (fe->height != 240)
+ return -EINVAL;
+ is_ntsc = 1;
+ break;
+ case 704:
+ if (fe->height != 288 && fe->height != 576)
+ return -EINVAL;
+ break;
+ case 352:
+ if (fe->height != 288)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fe->discrete.denominator = is_ntsc ? 30000 : 25000;
+ fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
+ dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator,
+ fe->discrete.denominator);
+ return 0;
+}
+
+static int __s2255_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct s2255_channel *channel = video_drvdata(file);
+ struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
+ struct s2255_fh *fh;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int state;
+ dprintk(1, "s2255: open called (dev=%s)\n",
+ video_device_node_name(vdev));
+ state = atomic_read(&dev->fw_data->fw_state);
+ switch (state) {
+ case S2255_FW_DISCONNECTING:
+ return -ENODEV;
+ case S2255_FW_FAILED:
+ s2255_dev_err(&dev->udev->dev,
+ "firmware load failed. retrying.\n");
+ s2255_fwload_start(dev, 1);
+ wait_event_timeout(dev->fw_data->wait_fw,
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
+ msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+ /* state may have changed, re-read */
+ state = atomic_read(&dev->fw_data->fw_state);
+ break;
+ case S2255_FW_NOTLOADED:
+ case S2255_FW_LOADED_DSPWAIT:
+ /* give S2255_LOAD_TIMEOUT time for firmware to load in case
+ driver loaded and then device immediately opened */
+ printk(KERN_INFO "%s waiting for firmware load\n", __func__);
+ wait_event_timeout(dev->fw_data->wait_fw,
+ ((atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_SUCCESS) ||
+ (atomic_read(&dev->fw_data->fw_state)
+ == S2255_FW_DISCONNECTING)),
+ msecs_to_jiffies(S2255_LOAD_TIMEOUT));
+ /* state may have changed, re-read */
+ state = atomic_read(&dev->fw_data->fw_state);
+ break;
+ case S2255_FW_SUCCESS:
+ default:
+ break;
+ }
+ /* state may have changed in above switch statement */
+ switch (state) {
+ case S2255_FW_SUCCESS:
+ break;
+ case S2255_FW_FAILED:
+ printk(KERN_INFO "2255 firmware load failed.\n");
+ return -ENODEV;
+ case S2255_FW_DISCONNECTING:
+ printk(KERN_INFO "%s: disconnecting\n", __func__);
+ return -ENODEV;
+ case S2255_FW_LOADED_DSPWAIT:
+ case S2255_FW_NOTLOADED:
+ printk(KERN_INFO "%s: firmware not loaded yet"
+ "please try again later\n",
+ __func__);
+ /*
+ * Timeout on firmware load means device unusable.
+ * Set firmware failure state.
+ * On next s2255_open the firmware will be reloaded.
+ */
+ atomic_set(&dev->fw_data->fw_state,
+ S2255_FW_FAILED);
+ return -EAGAIN;
+ default:
+ printk(KERN_INFO "%s: unknown state\n", __func__);
+ return -EFAULT;
+ }
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fh->channel = channel;
+ if (!channel->configured) {
+ /* configure channel to default state */
+ channel->fmt = &formats[0];
+ s2255_set_mode(channel, &channel->mode);
+ channel->configured = 1;
+ }
+ dprintk(1, "%s: dev=%s type=%s\n", __func__,
+ video_device_node_name(vdev), v4l2_type_names[type]);
+ dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__,
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&channel->vidq);
+ dprintk(4, "%s: list_empty active=%d\n", __func__,
+ list_empty(&channel->vidq.active));
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct s2255_buffer),
+ fh, vdev->lock);
+ return 0;
+}
+
+static int s2255_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ int ret;
+
+ if (mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ ret = __s2255_open(file);
+ mutex_unlock(vdev->lock);
+ return ret;
+}
+
+static unsigned int s2255_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev = fh->dev;
+ int rc;
+ dprintk(100, "%s\n", __func__);
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+ mutex_lock(&dev->lock);
+ rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
+ mutex_unlock(&dev->lock);
+ return rc;
+}
+
+static void s2255_destroy(struct s2255_dev *dev)
+{
+ /* board shutdown stops the read pipe if it is running */
+ s2255_board_shutdown(dev);
+ /* make sure firmware still not trying to load */
+ del_timer(&dev->timer); /* only started in .probe and .open */
+ if (dev->fw_data->fw_urb) {
+ usb_kill_urb(dev->fw_data->fw_urb);
+ usb_free_urb(dev->fw_data->fw_urb);
+ dev->fw_data->fw_urb = NULL;
+ }
+ release_firmware(dev->fw_data->fw);
+ kfree(dev->fw_data->pfw_data);
+ kfree(dev->fw_data);
+ /* reset the DSP so firmware can be reloaded next time */
+ s2255_reset_dsppower(dev);
+ mutex_destroy(&dev->lock);
+ usb_put_dev(dev->udev);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ dprintk(1, "%s", __func__);
+ kfree(dev);
+}
+
+static int s2255_release(struct file *file)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev = fh->dev;
+ struct video_device *vdev = video_devdata(file);
+ struct s2255_channel *channel = fh->channel;
+ if (!dev)
+ return -ENODEV;
+ mutex_lock(&dev->lock);
+ /* turn off stream */
+ if (res_check(fh)) {
+ if (channel->b_acquire)
+ s2255_stop_acquire(fh->channel);
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh);
+ }
+ videobuf_mmap_free(&fh->vb_vidq);
+ mutex_unlock(&dev->lock);
+ dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev));
+ kfree(fh);
+ return 0;
+}
+
+static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
+{
+ struct s2255_fh *fh = file->private_data;
+ struct s2255_dev *dev;
+ int ret;
+
+ if (!fh)
+ return -ENODEV;
+ dev = fh->dev;
+ dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ mutex_unlock(&dev->lock);
+ dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
+ (unsigned long)vma->vm_start,
+ (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
+ return ret;
+}
+
+static const struct v4l2_file_operations s2255_fops_v4l = {
+ .owner = THIS_MODULE,
+ .open = s2255_open,
+ .release = s2255_release,
+ .poll = s2255_poll,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .mmap = s2255_mmap_v4l,
+};
+
+static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
+ .vidioc_querymenu = vidioc_querymenu,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
+ .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+};
+
+static void s2255_video_device_release(struct video_device *vdev)
+{
+ struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
+ dprintk(4, "%s, chnls: %d \n", __func__,
+ atomic_read(&dev->num_channels));
+ if (atomic_dec_and_test(&dev->num_channels))
+ s2255_destroy(dev);
+ return;
+}
+
+static struct video_device template = {
+ .name = "s2255v",
+ .fops = &s2255_fops_v4l,
+ .ioctl_ops = &s2255_ioctl_ops,
+ .release = s2255_video_device_release,
+ .tvnorms = S2255_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static int s2255_probe_v4l(struct s2255_dev *dev)
+{
+ int ret;
+ int i;
+ int cur_nr = video_nr;
+ struct s2255_channel *channel;
+ ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
+ if (ret)
+ return ret;
+ /* initialize all video 4 linux */
+ /* register 4 video devices */
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ channel = &dev->channel[i];
+ INIT_LIST_HEAD(&channel->vidq.active);
+ channel->vidq.dev = dev;
+ /* register 4 video devices */
+ channel->vdev = template;
+ channel->vdev.lock = &dev->lock;
+ channel->vdev.v4l2_dev = &dev->v4l2_dev;
+ video_set_drvdata(&channel->vdev, channel);
+ if (video_nr == -1)
+ ret = video_register_device(&channel->vdev,
+ VFL_TYPE_GRABBER,
+ video_nr);
+ else
+ ret = video_register_device(&channel->vdev,
+ VFL_TYPE_GRABBER,
+ cur_nr + i);
+
+ if (ret) {
+ dev_err(&dev->udev->dev,
+ "failed to register video device!\n");
+ break;
+ }
+ atomic_inc(&dev->num_channels);
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&channel->vdev));
+
+ }
+ printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
+ S2255_VERSION);
+ /* if no channels registered, return error and probe will fail*/
+ if (atomic_read(&dev->num_channels) == 0) {
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
+ printk(KERN_WARNING "s2255: Not all channels available.\n");
+ return 0;
+}
+
+/* this function moves the usb stream read pipe data
+ * into the system buffers.
+ * returns 0 on success, EAGAIN if more data to process( call this
+ * function again).
+ *
+ * Received frame structure:
+ * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME)
+ * bytes 4-7: channel: 0-3
+ * bytes 8-11: payload size: size of the frame
+ * bytes 12-payloadsize+12: frame data
+ */
+static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
+{
+ char *pdest;
+ u32 offset = 0;
+ int bframe = 0;
+ char *psrc;
+ unsigned long copy_size;
+ unsigned long size;
+ s32 idx = -1;
+ struct s2255_framei *frm;
+ unsigned char *pdata;
+ struct s2255_channel *channel;
+ dprintk(100, "buffer to user\n");
+ channel = &dev->channel[dev->cc];
+ idx = channel->cur_frame;
+ frm = &channel->buffer.frame[idx];
+ if (frm->ulState == S2255_READ_IDLE) {
+ int jj;
+ unsigned int cc;
+ __le32 *pdword; /*data from dsp is little endian */
+ int payload;
+ /* search for marker codes */
+ pdata = (unsigned char *)pipe_info->transfer_buffer;
+ pdword = (__le32 *)pdata;
+ for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
+ switch (*pdword) {
+ case S2255_MARKER_FRAME:
+ dprintk(4, "found frame marker at offset:"
+ " %d [%x %x]\n", jj, pdata[0],
+ pdata[1]);
+ offset = jj + PREFIX_SIZE;
+ bframe = 1;
+ cc = le32_to_cpu(pdword[1]);
+ if (cc >= MAX_CHANNELS) {
+ printk(KERN_ERR
+ "bad channel\n");
+ return -EINVAL;
+ }
+ /* reverse it */
+ dev->cc = G_chnmap[cc];
+ channel = &dev->channel[dev->cc];
+ payload = le32_to_cpu(pdword[3]);
+ if (payload > channel->req_image_size) {
+ channel->bad_payload++;
+ /* discard the bad frame */
+ return -EINVAL;
+ }
+ channel->pkt_size = payload;
+ channel->jpg_size = le32_to_cpu(pdword[4]);
+ break;
+ case S2255_MARKER_RESPONSE:
+
+ pdata += DEF_USB_BLOCK;
+ jj += DEF_USB_BLOCK;
+ if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
+ break;
+ cc = G_chnmap[le32_to_cpu(pdword[1])];
+ if (cc >= MAX_CHANNELS)
+ break;
+ channel = &dev->channel[cc];
+ switch (pdword[2]) {
+ case S2255_RESPONSE_SETMODE:
+ /* check if channel valid */
+ /* set mode ready */
+ channel->setmode_ready = 1;
+ wake_up(&channel->wait_setmode);
+ dprintk(5, "setmode ready %d\n", cc);
+ break;
+ case S2255_RESPONSE_FW:
+ dev->chn_ready |= (1 << cc);
+ if ((dev->chn_ready & 0x0f) != 0x0f)
+ break;
+ /* all channels ready */
+ printk(KERN_INFO "s2255: fw loaded\n");
+ atomic_set(&dev->fw_data->fw_state,
+ S2255_FW_SUCCESS);
+ wake_up(&dev->fw_data->wait_fw);
+ break;
+ case S2255_RESPONSE_STATUS:
+ channel->vidstatus = le32_to_cpu(pdword[3]);
+ channel->vidstatus_ready = 1;
+ wake_up(&channel->wait_vidstatus);
+ dprintk(5, "got vidstatus %x chan %d\n",
+ le32_to_cpu(pdword[3]), cc);
+ break;
+ default:
+ printk(KERN_INFO "s2255 unknown resp\n");
+ }
+ default:
+ pdata++;
+ break;
+ }
+ if (bframe)
+ break;
+ } /* for */
+ if (!bframe)
+ return -EINVAL;
+ }
+ channel = &dev->channel[dev->cc];
+ idx = channel->cur_frame;
+ frm = &channel->buffer.frame[idx];
+ /* search done. now find out if should be acquiring on this channel */
+ if (!channel->b_acquire) {
+ /* we found a frame, but this channel is turned off */
+ frm->ulState = S2255_READ_IDLE;
+ return -EINVAL;
+ }
+
+ if (frm->ulState == S2255_READ_IDLE) {
+ frm->ulState = S2255_READ_FRAME;
+ frm->cur_size = 0;
+ }
+
+ /* skip the marker 512 bytes (and offset if out of sync) */
+ psrc = (u8 *)pipe_info->transfer_buffer + offset;
+
+
+ if (frm->lpvbits == NULL) {
+ dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
+ frm, dev, dev->cc, idx);
+ return -ENOMEM;
+ }
+
+ pdest = frm->lpvbits + frm->cur_size;
+
+ copy_size = (pipe_info->cur_transfer_size - offset);
+
+ size = channel->pkt_size - PREFIX_SIZE;
+
+ /* sanity check on pdest */
+ if ((copy_size + frm->cur_size) < channel->req_image_size)
+ memcpy(pdest, psrc, copy_size);
+
+ frm->cur_size += copy_size;
+ dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
+
+ if (frm->cur_size >= size) {
+ dprintk(2, "****************[%d]Buffer[%d]full*************\n",
+ dev->cc, idx);
+ channel->last_frame = channel->cur_frame;
+ channel->cur_frame++;
+ /* end of system frame ring buffer, start at zero */
+ if ((channel->cur_frame == SYS_FRAMES) ||
+ (channel->cur_frame == channel->buffer.dwFrames))
+ channel->cur_frame = 0;
+ /* frame ready */
+ if (channel->b_acquire)
+ s2255_got_frame(channel, channel->jpg_size);
+ channel->frame_count++;
+ frm->ulState = S2255_READ_IDLE;
+ frm->cur_size = 0;
+
+ }
+ /* done successfully */
+ return 0;
+}
+
+static void s2255_read_video_callback(struct s2255_dev *dev,
+ struct s2255_pipeinfo *pipe_info)
+{
+ int res;
+ dprintk(50, "callback read video \n");
+
+ if (dev->cc >= MAX_CHANNELS) {
+ dev->cc = 0;
+ dev_err(&dev->udev->dev, "invalid channel\n");
+ return;
+ }
+ /* otherwise copy to the system buffers */
+ res = save_frame(dev, pipe_info);
+ if (res != 0)
+ dprintk(4, "s2255: read callback failed\n");
+
+ dprintk(50, "callback read video done\n");
+ return;
+}
+
+static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
+ u16 Index, u16 Value, void *TransferBuffer,
+ s32 TransferBufferLength, int bOut)
+{
+ int r;
+ if (!bOut) {
+ r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ Request,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE |
+ USB_DIR_IN,
+ Value, Index, TransferBuffer,
+ TransferBufferLength, HZ * 5);
+ } else {
+ r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ Value, Index, TransferBuffer,
+ TransferBufferLength, HZ * 5);
+ }
+ return r;
+}
+
+/*
+ * retrieve FX2 firmware version. future use.
+ * @param dev pointer to device extension
+ * @return -1 for fail, else returns firmware version as an int(16 bits)
+ */
+static int s2255_get_fx2fw(struct s2255_dev *dev)
+{
+ int fw;
+ int ret;
+ unsigned char transBuffer[64];
+ ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
+ S2255_VR_IN);
+ if (ret < 0)
+ dprintk(2, "get fw error: %x\n", ret);
+ fw = transBuffer[0] + (transBuffer[1] << 8);
+ dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
+ return fw;
+}
+
+/*
+ * Create the system ring buffer to copy frames into from the
+ * usb read pipe.
+ */
+static int s2255_create_sys_buffers(struct s2255_channel *channel)
+{
+ unsigned long i;
+ unsigned long reqsize;
+ dprintk(1, "create sys buffers\n");
+ channel->buffer.dwFrames = SYS_FRAMES;
+ /* always allocate maximum size(PAL) for system buffers */
+ reqsize = SYS_FRAMES_MAXSIZE;
+
+ if (reqsize > SYS_FRAMES_MAXSIZE)
+ reqsize = SYS_FRAMES_MAXSIZE;
+
+ for (i = 0; i < SYS_FRAMES; i++) {
+ /* allocate the frames */
+ channel->buffer.frame[i].lpvbits = vmalloc(reqsize);
+ dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n",
+ &channel->buffer.frame[i], channel->idx, i,
+ channel->buffer.frame[i].lpvbits);
+ channel->buffer.frame[i].size = reqsize;
+ if (channel->buffer.frame[i].lpvbits == NULL) {
+ printk(KERN_INFO "out of memory. using less frames\n");
+ channel->buffer.dwFrames = i;
+ break;
+ }
+ }
+
+ /* make sure internal states are set */
+ for (i = 0; i < SYS_FRAMES; i++) {
+ channel->buffer.frame[i].ulState = 0;
+ channel->buffer.frame[i].cur_size = 0;
+ }
+
+ channel->cur_frame = 0;
+ channel->last_frame = -1;
+ return 0;
+}
+
+static int s2255_release_sys_buffers(struct s2255_channel *channel)
+{
+ unsigned long i;
+ dprintk(1, "release sys buffers\n");
+ for (i = 0; i < SYS_FRAMES; i++) {
+ if (channel->buffer.frame[i].lpvbits) {
+ dprintk(1, "vfree %p\n",
+ channel->buffer.frame[i].lpvbits);
+ vfree(channel->buffer.frame[i].lpvbits);
+ }
+ channel->buffer.frame[i].lpvbits = NULL;
+ }
+ return 0;
+}
+
+static int s2255_board_init(struct s2255_dev *dev)
+{
+ struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
+ int fw_ver;
+ int j;
+ struct s2255_pipeinfo *pipe = &dev->pipe;
+ dprintk(4, "board init: %p", dev);
+ memset(pipe, 0, sizeof(*pipe));
+ pipe->dev = dev;
+ pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
+ pipe->max_transfer_size = S2255_USB_XFER_SIZE;
+
+ pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
+ GFP_KERNEL);
+ if (pipe->transfer_buffer == NULL) {
+ dprintk(1, "out of memory!\n");
+ return -ENOMEM;
+ }
+ /* query the firmware */
+ fw_ver = s2255_get_fx2fw(dev);
+
+ printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
+ (fw_ver >> 8) & 0xff,
+ fw_ver & 0xff);
+
+ if (fw_ver < S2255_CUR_USB_FWVER)
+ printk(KERN_INFO "s2255: newer USB firmware available\n");
+
+ for (j = 0; j < MAX_CHANNELS; j++) {
+ struct s2255_channel *channel = &dev->channel[j];
+ channel->b_acquire = 0;
+ channel->mode = mode_def;
+ if (dev->pid == 0x2257 && j > 1)
+ channel->mode.color |= (1 << 16);
+ channel->jc.quality = S2255_DEF_JPEG_QUAL;
+ channel->width = LINE_SZ_4CIFS_NTSC;
+ channel->height = NUM_LINES_4CIFS_NTSC * 2;
+ channel->fmt = &formats[0];
+ channel->mode.restart = 1;
+ channel->req_image_size = get_transfer_size(&mode_def);
+ channel->frame_count = 0;
+ /* create the system buffers */
+ s2255_create_sys_buffers(channel);
+ }
+ /* start read pipe */
+ s2255_start_readpipe(dev);
+ dprintk(1, "%s: success\n", __func__);
+ return 0;
+}
+
+static int s2255_board_shutdown(struct s2255_dev *dev)
+{
+ u32 i;
+ dprintk(1, "%s: dev: %p", __func__, dev);
+
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ if (dev->channel[i].b_acquire)
+ s2255_stop_acquire(&dev->channel[i]);
+ }
+ s2255_stop_readpipe(dev);
+ for (i = 0; i < MAX_CHANNELS; i++)
+ s2255_release_sys_buffers(&dev->channel[i]);
+ /* release transfer buffer */
+ kfree(dev->pipe.transfer_buffer);
+ return 0;
+}
+
+static void read_pipe_completion(struct urb *purb)
+{
+ struct s2255_pipeinfo *pipe_info;
+ struct s2255_dev *dev;
+ int status;
+ int pipe;
+ pipe_info = purb->context;
+ dprintk(100, "%s: urb:%p, status %d\n", __func__, purb,
+ purb->status);
+ if (pipe_info == NULL) {
+ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+
+ dev = pipe_info->dev;
+ if (dev == NULL) {
+ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+ status = purb->status;
+ /* if shutting down, do not resubmit, exit immediately */
+ if (status == -ESHUTDOWN) {
+ dprintk(2, "%s: err shutdown\n", __func__);
+ pipe_info->err_count++;
+ return;
+ }
+
+ if (pipe_info->state == 0) {
+ dprintk(2, "%s: exiting USB pipe", __func__);
+ return;
+ }
+
+ if (status == 0)
+ s2255_read_video_callback(dev, pipe_info);
+ else {
+ pipe_info->err_count++;
+ dprintk(1, "%s: failed URB %d\n", __func__, status);
+ }
+
+ pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+ /* reuse urb */
+ usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->cur_transfer_size,
+ read_pipe_completion, pipe_info);
+
+ if (pipe_info->state != 0) {
+ if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) {
+ dev_err(&dev->udev->dev, "error submitting urb\n");
+ }
+ } else {
+ dprintk(2, "%s :complete state 0\n", __func__);
+ }
+ return;
+}
+
+static int s2255_start_readpipe(struct s2255_dev *dev)
+{
+ int pipe;
+ int retval;
+ struct s2255_pipeinfo *pipe_info = &dev->pipe;
+ pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
+ dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint);
+ pipe_info->state = 1;
+ pipe_info->err_count = 0;
+ pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pipe_info->stream_urb) {
+ dev_err(&dev->udev->dev,
+ "ReadStream: Unable to alloc URB\n");
+ return -ENOMEM;
+ }
+ /* transfer buffer allocated in board_init */
+ usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
+ pipe,
+ pipe_info->transfer_buffer,
+ pipe_info->cur_transfer_size,
+ read_pipe_completion, pipe_info);
+ retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
+ if (retval) {
+ printk(KERN_ERR "s2255: start read pipe failed\n");
+ return retval;
+ }
+ return 0;
+}
+
+/* starts acquisition process */
+static int s2255_start_acquire(struct s2255_channel *channel)
+{
+ unsigned char *buffer;
+ int res;
+ unsigned long chn_rev;
+ int j;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+
+ channel->last_frame = -1;
+ channel->bad_payload = 0;
+ channel->cur_frame = 0;
+ for (j = 0; j < SYS_FRAMES; j++) {
+ channel->buffer.frame[j].ulState = 0;
+ channel->buffer.frame[j].cur_size = 0;
+ }
+
+ /* send the start command */
+ *(__le32 *) buffer = IN_DATA_TOKEN;
+ *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
+ *((__le32 *) buffer + 2) = CMD_START;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (res != 0)
+ dev_err(&dev->udev->dev, "CMD_START error\n");
+
+ dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res);
+ kfree(buffer);
+ return 0;
+}
+
+static int s2255_stop_acquire(struct s2255_channel *channel)
+{
+ unsigned char *buffer;
+ int res;
+ unsigned long chn_rev;
+ struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
+ chn_rev = G_chnmap[channel->idx];
+ buffer = kzalloc(512, GFP_KERNEL);
+ if (buffer == NULL) {
+ dev_err(&dev->udev->dev, "out of mem\n");
+ return -ENOMEM;
+ }
+ /* send the stop command */
+ *(__le32 *) buffer = IN_DATA_TOKEN;
+ *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
+ *((__le32 *) buffer + 2) = CMD_STOP;
+ res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
+ if (res != 0)
+ dev_err(&dev->udev->dev, "CMD_STOP error\n");
+ kfree(buffer);
+ channel->b_acquire = 0;
+ dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res);
+ return res;
+}
+
+static void s2255_stop_readpipe(struct s2255_dev *dev)
+{
+ struct s2255_pipeinfo *pipe = &dev->pipe;
+
+ pipe->state = 0;
+ if (pipe->stream_urb) {
+ /* cancel urb */
+ usb_kill_urb(pipe->stream_urb);
+ usb_free_urb(pipe->stream_urb);
+ pipe->stream_urb = NULL;
+ }
+ dprintk(4, "%s", __func__);
+ return;
+}
+
+static void s2255_fwload_start(struct s2255_dev *dev, int reset)
+{
+ if (reset)
+ s2255_reset_dsppower(dev);
+ dev->fw_data->fw_size = dev->fw_data->fw->size;
+ atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
+ memcpy(dev->fw_data->pfw_data,
+ dev->fw_data->fw->data, CHUNK_SIZE);
+ dev->fw_data->fw_loaded = CHUNK_SIZE;
+ usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
+ usb_sndbulkpipe(dev->udev, 2),
+ dev->fw_data->pfw_data,
+ CHUNK_SIZE, s2255_fwchunk_complete,
+ dev->fw_data);
+ mod_timer(&dev->timer, jiffies + HZ);
+}
+
+/* standard usb probe function */
+static int s2255_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct s2255_dev *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ int retval = -ENOMEM;
+ __le32 *pdata;
+ int fw_size;
+ dprintk(2, "%s\n", __func__);
+ /* allocate memory for our device state and initialize it to zero */
+ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
+ if (dev == NULL) {
+ s2255_dev_err(&interface->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ atomic_set(&dev->num_channels, 0);
+ dev->pid = id->idProduct;
+ dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
+ if (!dev->fw_data)
+ goto errorFWDATA1;
+ mutex_init(&dev->lock);
+ /* grab usb_device and save it */
+ dev->udev = usb_get_dev(interface_to_usbdev(interface));
+ if (dev->udev == NULL) {
+ dev_err(&interface->dev, "null usb device\n");
+ retval = -ENODEV;
+ goto errorUDEV;
+ }
+ dprintk(1, "dev: %p, udev %p interface %p\n", dev,
+ dev->udev, interface);
+ dev->interface = interface;
+ /* set up the endpoint information */
+ iface_desc = interface->cur_altsetting;
+ dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
+ /* we found the bulk in endpoint */
+ dev->read_endpoint = endpoint->bEndpointAddress;
+ }
+ }
+
+ if (!dev->read_endpoint) {
+ dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
+ goto errorEP;
+ }
+ init_timer(&dev->timer);
+ dev->timer.function = s2255_timer;
+ dev->timer.data = (unsigned long)dev->fw_data;
+ init_waitqueue_head(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ struct s2255_channel *channel = &dev->channel[i];
+ dev->channel[i].idx = i;
+ init_waitqueue_head(&channel->wait_setmode);
+ init_waitqueue_head(&channel->wait_vidstatus);
+ }
+
+ dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->fw_data->fw_urb) {
+ dev_err(&interface->dev, "out of memory!\n");
+ goto errorFWURB;
+ }
+
+ dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!dev->fw_data->pfw_data) {
+ dev_err(&interface->dev, "out of memory!\n");
+ goto errorFWDATA2;
+ }
+ /* load the first chunk */
+ if (request_firmware(&dev->fw_data->fw,
+ FIRMWARE_FILE_NAME, &dev->udev->dev)) {
+ printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
+ goto errorREQFW;
+ }
+ /* check the firmware is valid */
+ fw_size = dev->fw_data->fw->size;
+ pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
+
+ if (*pdata != S2255_FW_MARKER) {
+ printk(KERN_INFO "Firmware invalid.\n");
+ retval = -ENODEV;
+ goto errorFWMARKER;
+ } else {
+ /* make sure firmware is the latest */
+ __le32 *pRel;
+ pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
+ printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
+ dev->dsp_fw_ver = le32_to_cpu(*pRel);
+ if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
+ printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
+ if (dev->pid == 0x2257 &&
+ dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
+ printk(KERN_WARNING "s2255: 2257 requires firmware %d"
+ " or above.\n", S2255_MIN_DSP_COLORFILTER);
+ }
+ usb_reset_device(dev->udev);
+ /* load 2255 board specific */
+ retval = s2255_board_init(dev);
+ if (retval)
+ goto errorBOARDINIT;
+ spin_lock_init(&dev->slock);
+ s2255_fwload_start(dev, 0);
+ /* loads v4l specific */
+ retval = s2255_probe_v4l(dev);
+ if (retval)
+ goto errorBOARDINIT;
+ dev_info(&interface->dev, "Sensoray 2255 detected\n");
+ return 0;
+errorBOARDINIT:
+ s2255_board_shutdown(dev);
+errorFWMARKER:
+ release_firmware(dev->fw_data->fw);
+errorREQFW:
+ kfree(dev->fw_data->pfw_data);
+errorFWDATA2:
+ usb_free_urb(dev->fw_data->fw_urb);
+errorFWURB:
+ del_timer(&dev->timer);
+errorEP:
+ usb_put_dev(dev->udev);
+errorUDEV:
+ kfree(dev->fw_data);
+ mutex_destroy(&dev->lock);
+errorFWDATA1:
+ kfree(dev);
+ printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval);
+ return retval;
+}
+
+/* disconnect routine. when board is removed physically or with rmmod */
+static void s2255_disconnect(struct usb_interface *interface)
+{
+ struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
+ int i;
+ int channels = atomic_read(&dev->num_channels);
+ mutex_lock(&dev->lock);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ mutex_unlock(&dev->lock);
+ /*see comments in the uvc_driver.c usb disconnect function */
+ atomic_inc(&dev->num_channels);
+ /* unregister each video device. */
+ for (i = 0; i < channels; i++)
+ video_unregister_device(&dev->channel[i].vdev);
+ /* wake up any of our timers */
+ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
+ wake_up(&dev->fw_data->wait_fw);
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ dev->channel[i].setmode_ready = 1;
+ wake_up(&dev->channel[i].wait_setmode);
+ dev->channel[i].vidstatus_ready = 1;
+ wake_up(&dev->channel[i].wait_vidstatus);
+ }
+ if (atomic_dec_and_test(&dev->num_channels))
+ s2255_destroy(dev);
+ dev_info(&interface->dev, "%s\n", __func__);
+}
+
+static struct usb_driver s2255_driver = {
+ .name = S2255_DRIVER_NAME,
+ .probe = s2255_probe,
+ .disconnect = s2255_disconnect,
+ .id_table = s2255_table,
+};
+
+module_usb_driver(s2255_driver);
+
+MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
+MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(S2255_VERSION);
+MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig
new file mode 100644
index 000000000000..3c76e62d820d
--- /dev/null
+++ b/drivers/media/usb/siano/Kconfig
@@ -0,0 +1,10 @@
+#
+# Siano Mobile Silicon Digital TV device configuration
+#
+
+config SMS_USB_DRV
+ tristate "Siano SMS1xxx based MDTV receiver"
+ depends on DVB_CORE && RC_CORE && HAS_DMA
+ ---help---
+ Choose if you would like to have Siano's support for USB interface
+
diff --git a/drivers/media/usb/siano/Makefile b/drivers/media/usb/siano/Makefile
new file mode 100644
index 000000000000..758b6a090c59
--- /dev/null
+++ b/drivers/media/usb/siano/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
+
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/common/siano
+ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
+
diff --git a/drivers/media/usb/siano/smsusb.c b/drivers/media/usb/siano/smsusb.c
new file mode 100644
index 000000000000..aac622200e99
--- /dev/null
+++ b/drivers/media/usb/siano/smsusb.c
@@ -0,0 +1,568 @@
+/****************************************************************
+
+Siano Mobile Silicon, Inc.
+MDTV receiver kernel modules.
+Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+****************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "smscoreapi.h"
+#include "sms-cards.h"
+#include "smsendian.h"
+
+static int sms_dbg;
+module_param_named(debug, sms_dbg, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
+
+#define USB1_BUFFER_SIZE 0x1000
+#define USB2_BUFFER_SIZE 0x4000
+
+#define MAX_BUFFERS 50
+#define MAX_URBS 10
+
+struct smsusb_device_t;
+
+struct smsusb_urb_t {
+ struct smscore_buffer_t *cb;
+ struct smsusb_device_t *dev;
+
+ struct urb urb;
+};
+
+struct smsusb_device_t {
+ struct usb_device *udev;
+ struct smscore_device_t *coredev;
+
+ struct smsusb_urb_t surbs[MAX_URBS];
+
+ int response_alignment;
+ int buffer_size;
+};
+
+static int smsusb_submit_urb(struct smsusb_device_t *dev,
+ struct smsusb_urb_t *surb);
+
+static void smsusb_onresponse(struct urb *urb)
+{
+ struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
+ struct smsusb_device_t *dev = surb->dev;
+
+ if (urb->status == -ESHUTDOWN) {
+ sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",
+ urb->status, urb->actual_length);
+ return;
+ }
+
+ if ((urb->actual_length > 0) && (urb->status == 0)) {
+ struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p;
+
+ smsendian_handle_message_header(phdr);
+ if (urb->actual_length >= phdr->msgLength) {
+ surb->cb->size = phdr->msgLength;
+
+ if (dev->response_alignment &&
+ (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {
+
+ surb->cb->offset =
+ dev->response_alignment +
+ ((phdr->msgFlags >> 8) & 3);
+
+ /* sanity check */
+ if (((int) phdr->msgLength +
+ surb->cb->offset) > urb->actual_length) {
+ sms_err("invalid response "
+ "msglen %d offset %d "
+ "size %d",
+ phdr->msgLength,
+ surb->cb->offset,
+ urb->actual_length);
+ goto exit_and_resubmit;
+ }
+
+ /* move buffer pointer and
+ * copy header to its new location */
+ memcpy((char *) phdr + surb->cb->offset,
+ phdr, sizeof(struct SmsMsgHdr_ST));
+ } else
+ surb->cb->offset = 0;
+
+ smscore_onresponse(dev->coredev, surb->cb);
+ surb->cb = NULL;
+ } else {
+ sms_err("invalid response "
+ "msglen %d actual %d",
+ phdr->msgLength, urb->actual_length);
+ }
+ } else
+ sms_err("error, urb status %d, %d bytes",
+ urb->status, urb->actual_length);
+
+
+exit_and_resubmit:
+ smsusb_submit_urb(dev, surb);
+}
+
+static int smsusb_submit_urb(struct smsusb_device_t *dev,
+ struct smsusb_urb_t *surb)
+{
+ if (!surb->cb) {
+ surb->cb = smscore_getbuffer(dev->coredev);
+ if (!surb->cb) {
+ sms_err("smscore_getbuffer(...) returned NULL");
+ return -ENOMEM;
+ }
+ }
+
+ usb_fill_bulk_urb(
+ &surb->urb,
+ dev->udev,
+ usb_rcvbulkpipe(dev->udev, 0x81),
+ surb->cb->p,
+ dev->buffer_size,
+ smsusb_onresponse,
+ surb
+ );
+ surb->urb.transfer_dma = surb->cb->phys;
+ surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ return usb_submit_urb(&surb->urb, GFP_ATOMIC);
+}
+
+static void smsusb_stop_streaming(struct smsusb_device_t *dev)
+{
+ int i;
+
+ for (i = 0; i < MAX_URBS; i++) {
+ usb_kill_urb(&dev->surbs[i].urb);
+
+ if (dev->surbs[i].cb) {
+ smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
+ dev->surbs[i].cb = NULL;
+ }
+ }
+}
+
+static int smsusb_start_streaming(struct smsusb_device_t *dev)
+{
+ int i, rc;
+
+ for (i = 0; i < MAX_URBS; i++) {
+ rc = smsusb_submit_urb(dev, &dev->surbs[i]);
+ if (rc < 0) {
+ sms_err("smsusb_submit_urb(...) failed");
+ smsusb_stop_streaming(dev);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int smsusb_sendrequest(void *context, void *buffer, size_t size)
+{
+ struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
+ int dummy;
+
+ smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer);
+ return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
+ buffer, size, &dummy, 1000);
+}
+
+static char *smsusb1_fw_lkup[] = {
+ "dvbt_stellar_usb.inp",
+ "dvbh_stellar_usb.inp",
+ "tdmb_stellar_usb.inp",
+ "none",
+ "dvbt_bda_stellar_usb.inp",
+};
+
+static inline char *sms_get_fw_name(int mode, int board_id)
+{
+ char **fw = sms_get_board(board_id)->fw;
+ return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];
+}
+
+static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
+{
+ const struct firmware *fw;
+ u8 *fw_buffer;
+ int rc, dummy;
+ char *fw_filename;
+
+ if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
+ sms_err("invalid firmware id specified %d", id);
+ return -EINVAL;
+ }
+
+ fw_filename = sms_get_fw_name(id, board_id);
+
+ rc = request_firmware(&fw, fw_filename, &udev->dev);
+ if (rc < 0) {
+ sms_warn("failed to open \"%s\" mode %d, "
+ "trying again with default firmware", fw_filename, id);
+
+ fw_filename = smsusb1_fw_lkup[id];
+ rc = request_firmware(&fw, fw_filename, &udev->dev);
+ if (rc < 0) {
+ sms_warn("failed to open \"%s\" mode %d",
+ fw_filename, id);
+
+ return rc;
+ }
+ }
+
+ fw_buffer = kmalloc(fw->size, GFP_KERNEL);
+ if (fw_buffer) {
+ memcpy(fw_buffer, fw->data, fw->size);
+
+ rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
+ fw_buffer, fw->size, &dummy, 1000);
+
+ sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
+
+ kfree(fw_buffer);
+ } else {
+ sms_err("failed to allocate firmware buffer");
+ rc = -ENOMEM;
+ }
+ sms_info("read FW %s, size=%zd", fw_filename, fw->size);
+
+ release_firmware(fw);
+
+ return rc;
+}
+
+static void smsusb1_detectmode(void *context, int *mode)
+{
+ char *product_string =
+ ((struct smsusb_device_t *) context)->udev->product;
+
+ *mode = DEVICE_MODE_NONE;
+
+ if (!product_string) {
+ product_string = "none";
+ sms_err("product string not found");
+ } else if (strstr(product_string, "DVBH"))
+ *mode = 1;
+ else if (strstr(product_string, "BDA"))
+ *mode = 4;
+ else if (strstr(product_string, "DVBT"))
+ *mode = 0;
+ else if (strstr(product_string, "TDMB"))
+ *mode = 2;
+
+ sms_info("%d \"%s\"", *mode, product_string);
+}
+
+static int smsusb1_setmode(void *context, int mode)
+{
+ struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
+ sizeof(struct SmsMsgHdr_ST), 0 };
+
+ if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
+ sms_err("invalid firmware id specified %d", mode);
+ return -EINVAL;
+ }
+
+ return smsusb_sendrequest(context, &Msg, sizeof(Msg));
+}
+
+static void smsusb_term_device(struct usb_interface *intf)
+{
+ struct smsusb_device_t *dev = usb_get_intfdata(intf);
+
+ if (dev) {
+ smsusb_stop_streaming(dev);
+
+ /* unregister from smscore */
+ if (dev->coredev)
+ smscore_unregister_device(dev->coredev);
+
+ sms_info("device %p destroyed", dev);
+ kfree(dev);
+ }
+
+ usb_set_intfdata(intf, NULL);
+}
+
+static int smsusb_init_device(struct usb_interface *intf, int board_id)
+{
+ struct smsdevice_params_t params;
+ struct smsusb_device_t *dev;
+ int i, rc;
+
+ /* create device object */
+ dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
+ if (!dev) {
+ sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
+ return -ENOMEM;
+ }
+
+ memset(&params, 0, sizeof(params));
+ usb_set_intfdata(intf, dev);
+ dev->udev = interface_to_usbdev(intf);
+
+ params.device_type = sms_get_board(board_id)->type;
+
+ switch (params.device_type) {
+ case SMS_STELLAR:
+ dev->buffer_size = USB1_BUFFER_SIZE;
+
+ params.setmode_handler = smsusb1_setmode;
+ params.detectmode_handler = smsusb1_detectmode;
+ break;
+ default:
+ sms_err("Unspecified sms device type!");
+ /* fall-thru */
+ case SMS_NOVA_A0:
+ case SMS_NOVA_B0:
+ case SMS_VEGA:
+ dev->buffer_size = USB2_BUFFER_SIZE;
+ dev->response_alignment =
+ le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) -
+ sizeof(struct SmsMsgHdr_ST);
+
+ params.flags |= SMS_DEVICE_FAMILY2;
+ break;
+ }
+
+ params.device = &dev->udev->dev;
+ params.buffer_size = dev->buffer_size;
+ params.num_buffers = MAX_BUFFERS;
+ params.sendrequest_handler = smsusb_sendrequest;
+ params.context = dev;
+ usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));
+
+ /* register in smscore */
+ rc = smscore_register_device(&params, &dev->coredev);
+ if (rc < 0) {
+ sms_err("smscore_register_device(...) failed, rc %d", rc);
+ smsusb_term_device(intf);
+ return rc;
+ }
+
+ smscore_set_board_id(dev->coredev, board_id);
+
+ /* initialize urbs */
+ for (i = 0; i < MAX_URBS; i++) {
+ dev->surbs[i].dev = dev;
+ usb_init_urb(&dev->surbs[i].urb);
+ }
+
+ sms_info("smsusb_start_streaming(...).");
+ rc = smsusb_start_streaming(dev);
+ if (rc < 0) {
+ sms_err("smsusb_start_streaming(...) failed");
+ smsusb_term_device(intf);
+ return rc;
+ }
+
+ rc = smscore_start_device(dev->coredev);
+ if (rc < 0) {
+ sms_err("smscore_start_device(...) failed");
+ smsusb_term_device(intf);
+ return rc;
+ }
+
+ sms_info("device %p created", dev);
+
+ return rc;
+}
+
+static int __devinit smsusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ char devpath[32];
+ int i, rc;
+
+ rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+ rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+ if (intf->num_altsetting > 0) {
+ rc = usb_set_interface(
+ udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
+ if (rc < 0) {
+ sms_err("usb_set_interface failed, rc %d", rc);
+ return rc;
+ }
+ }
+
+ sms_info("smsusb_probe %d",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
+ sms_info("endpoint %d %02x %02x %d", i,
+ intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
+ intf->cur_altsetting->endpoint[i].desc.bmAttributes,
+ intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+ if ((udev->actconfig->desc.bNumInterfaces == 2) &&
+ (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
+ sms_err("rom interface 0 is not used");
+ return -ENODEV;
+ }
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
+ snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
+ udev->bus->busnum, udev->devpath);
+ sms_info("stellar device was found.");
+ return smsusb1_load_firmware(
+ udev, smscore_registry_getmode(devpath),
+ id->driver_info);
+ }
+
+ rc = smsusb_init_device(intf, id->driver_info);
+ sms_info("rc %d", rc);
+ sms_board_load_modules(id->driver_info);
+ return rc;
+}
+
+static void smsusb_disconnect(struct usb_interface *intf)
+{
+ smsusb_term_device(intf);
+}
+
+static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
+{
+ struct smsusb_device_t *dev = usb_get_intfdata(intf);
+ printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);
+ smsusb_stop_streaming(dev);
+ return 0;
+}
+
+static int smsusb_resume(struct usb_interface *intf)
+{
+ int rc, i;
+ struct smsusb_device_t *dev = usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ printk(KERN_INFO "%s: Entering.\n", __func__);
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
+ printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
+ intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
+ intf->cur_altsetting->endpoint[i].desc.bmAttributes,
+ intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
+
+ if (intf->num_altsetting > 0) {
+ rc = usb_set_interface(udev,
+ intf->cur_altsetting->desc.
+ bInterfaceNumber, 0);
+ if (rc < 0) {
+ printk(KERN_INFO "%s usb_set_interface failed, "
+ "rc %d\n", __func__, rc);
+ return rc;
+ }
+ }
+
+ smsusb_start_streaming(dev);
+ return 0;
+}
+
+static const struct usb_device_id smsusb_id_table[] = {
+ { USB_DEVICE(0x187f, 0x0010),
+ .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+ { USB_DEVICE(0x187f, 0x0100),
+ .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+ { USB_DEVICE(0x187f, 0x0200),
+ .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
+ { USB_DEVICE(0x187f, 0x0201),
+ .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
+ { USB_DEVICE(0x187f, 0x0300),
+ .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
+ { USB_DEVICE(0x2040, 0x1700),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
+ { USB_DEVICE(0x2040, 0x1800),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
+ { USB_DEVICE(0x2040, 0x1801),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
+ { USB_DEVICE(0x2040, 0x2000),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2009),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
+ { USB_DEVICE(0x2040, 0x200a),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2010),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2011),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x2019),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+ { USB_DEVICE(0x2040, 0x5500),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5510),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5520),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5530),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5580),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0x5590),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x187f, 0x0202),
+ .driver_info = SMS1XXX_BOARD_SIANO_NICE },
+ { USB_DEVICE(0x187f, 0x0301),
+ .driver_info = SMS1XXX_BOARD_SIANO_VENICE },
+ { USB_DEVICE(0x2040, 0xb900),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xb910),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xb980),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xb990),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc000),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc010),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc080),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc090),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc0a0),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xf5a0),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { } /* Terminating entry */
+ };
+
+MODULE_DEVICE_TABLE(usb, smsusb_id_table);
+
+static struct usb_driver smsusb_driver = {
+ .name = "smsusb",
+ .probe = smsusb_probe,
+ .disconnect = smsusb_disconnect,
+ .id_table = smsusb_id_table,
+
+ .suspend = smsusb_suspend,
+ .resume = smsusb_resume,
+};
+
+module_usb_driver(smsusb_driver);
+
+MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle");
+MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/usb/sn9c102/Kconfig
index 6ebaf2940d06..6ebaf2940d06 100644
--- a/drivers/media/video/sn9c102/Kconfig
+++ b/drivers/media/usb/sn9c102/Kconfig
diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/usb/sn9c102/Makefile
index 7ecd5a90c7c9..7ecd5a90c7c9 100644
--- a/drivers/media/video/sn9c102/Makefile
+++ b/drivers/media/usb/sn9c102/Makefile
diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/usb/sn9c102/sn9c102.h
index 2bc153e869be..2bc153e869be 100644
--- a/drivers/media/video/sn9c102/sn9c102.h
+++ b/drivers/media/usb/sn9c102/sn9c102.h
diff --git a/drivers/media/video/sn9c102/sn9c102_config.h b/drivers/media/usb/sn9c102/sn9c102_config.h
index 0f4e0378b071..0f4e0378b071 100644
--- a/drivers/media/video/sn9c102/sn9c102_config.h
+++ b/drivers/media/usb/sn9c102/sn9c102_config.h
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/usb/sn9c102/sn9c102_core.c
index 19ea780b16ff..19ea780b16ff 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/usb/sn9c102/sn9c102_core.c
diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/usb/sn9c102/sn9c102_devtable.h
index b3d2cc729657..b3d2cc729657 100644
--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
+++ b/drivers/media/usb/sn9c102/sn9c102_devtable.h
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c
index 2dce5c908c8e..2dce5c908c8e 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c
+++ b/drivers/media/usb/sn9c102/sn9c102_hv7131d.c
diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c
index 4295887ff609..4295887ff609 100644
--- a/drivers/media/video/sn9c102/sn9c102_hv7131r.c
+++ b/drivers/media/usb/sn9c102/sn9c102_hv7131r.c
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/usb/sn9c102/sn9c102_mi0343.c
index 1f5b09bec89c..1f5b09bec89c 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0343.c
+++ b/drivers/media/usb/sn9c102/sn9c102_mi0343.c
diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/usb/sn9c102/sn9c102_mi0360.c
index d973fc1973d9..d973fc1973d9 100644
--- a/drivers/media/video/sn9c102/sn9c102_mi0360.c
+++ b/drivers/media/usb/sn9c102/sn9c102_mi0360.c
diff --git a/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c
index 95986eb492e4..95986eb492e4 100644
--- a/drivers/media/video/sn9c102/sn9c102_mt9v111.c
+++ b/drivers/media/usb/sn9c102/sn9c102_mt9v111.c
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/usb/sn9c102/sn9c102_ov7630.c
index 803712c29f02..803712c29f02 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7630.c
+++ b/drivers/media/usb/sn9c102/sn9c102_ov7630.c
diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/usb/sn9c102/sn9c102_ov7660.c
index 7977795d342b..7977795d342b 100644
--- a/drivers/media/video/sn9c102/sn9c102_ov7660.c
+++ b/drivers/media/usb/sn9c102/sn9c102_ov7660.c
diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/usb/sn9c102/sn9c102_pas106b.c
index 81cd969c1b7b..81cd969c1b7b 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas106b.c
+++ b/drivers/media/usb/sn9c102/sn9c102_pas106b.c
diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c
index 2e86fdc86989..2e86fdc86989 100644
--- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c
+++ b/drivers/media/usb/sn9c102/sn9c102_pas202bcb.c
diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/usb/sn9c102/sn9c102_sensor.h
index 3679970dba2c..3679970dba2c 100644
--- a/drivers/media/video/sn9c102/sn9c102_sensor.h
+++ b/drivers/media/usb/sn9c102/sn9c102_sensor.h
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c
index 04cdfdde8564..04cdfdde8564 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c
+++ b/drivers/media/usb/sn9c102/sn9c102_tas5110c1b.c
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c
index 9372e6f9fcff..9372e6f9fcff 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5110d.c
+++ b/drivers/media/usb/sn9c102/sn9c102_tas5110d.c
diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c
index a30bbc4389f5..a30bbc4389f5 100644
--- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c
+++ b/drivers/media/usb/sn9c102/sn9c102_tas5130d1b.c
diff --git a/drivers/media/usb/stk1160/Kconfig b/drivers/media/usb/stk1160/Kconfig
new file mode 100644
index 000000000000..1c3a1ec00237
--- /dev/null
+++ b/drivers/media/usb/stk1160/Kconfig
@@ -0,0 +1,20 @@
+config VIDEO_STK1160
+ tristate "STK1160 USB video capture support"
+ depends on VIDEO_DEV && I2C
+ select VIDEOBUF2_VMALLOC
+ select VIDEO_SAA711X
+
+ ---help---
+ This is a video4linux driver for STK1160 based video capture devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stk1160
+
+config VIDEO_STK1160_AC97
+ bool "STK1160 AC97 codec support"
+ depends on VIDEO_STK1160 && SND
+ select SND_AC97_CODEC
+
+ ---help---
+ Enables AC97 codec support for stk1160 driver.
+.
diff --git a/drivers/media/usb/stk1160/Makefile b/drivers/media/usb/stk1160/Makefile
new file mode 100644
index 000000000000..dfe3e90ff392
--- /dev/null
+++ b/drivers/media/usb/stk1160/Makefile
@@ -0,0 +1,11 @@
+obj-stk1160-ac97-$(CONFIG_VIDEO_STK1160_AC97) := stk1160-ac97.o
+
+stk1160-y := stk1160-core.o \
+ stk1160-v4l.o \
+ stk1160-video.o \
+ stk1160-i2c.o \
+ $(obj-stk1160-ac97-y)
+
+obj-$(CONFIG_VIDEO_STK1160) += stk1160.o
+
+ccflags-y += -Idrivers/media/i2c
diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c
new file mode 100644
index 000000000000..c8583c262c3d
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-ac97.c
@@ -0,0 +1,152 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+
+#include "stk1160.h"
+#include "stk1160-reg.h"
+
+static struct snd_ac97 *stk1160_ac97;
+
+static void stk1160_write_ac97(struct snd_ac97 *ac97, u16 reg, u16 value)
+{
+ struct stk1160 *dev = ac97->private_data;
+
+ /* Set codec register address */
+ stk1160_write_reg(dev, STK1160_AC97_ADDR, reg);
+
+ /* Set codec command */
+ stk1160_write_reg(dev, STK1160_AC97_CMD, value & 0xff);
+ stk1160_write_reg(dev, STK1160_AC97_CMD + 1, (value & 0xff00) >> 8);
+
+ /*
+ * Set command write bit to initiate write operation.
+ * The bit will be cleared when transfer is done.
+ */
+ stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8c);
+}
+
+static u16 stk1160_read_ac97(struct snd_ac97 *ac97, u16 reg)
+{
+ struct stk1160 *dev = ac97->private_data;
+ u8 vall = 0;
+ u8 valh = 0;
+
+ /* Set codec register address */
+ stk1160_write_reg(dev, STK1160_AC97_ADDR, reg);
+
+ /*
+ * Set command read bit to initiate read operation.
+ * The bit will be cleared when transfer is done.
+ */
+ stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x8b);
+
+ /* Retrieve register value */
+ stk1160_read_reg(dev, STK1160_AC97_CMD, &vall);
+ stk1160_read_reg(dev, STK1160_AC97_CMD + 1, &valh);
+
+ return (valh << 8) | vall;
+}
+
+static void stk1160_reset_ac97(struct snd_ac97 *ac97)
+{
+ struct stk1160 *dev = ac97->private_data;
+ /* Two-step reset AC97 interface and hardware codec */
+ stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x94);
+ stk1160_write_reg(dev, STK1160_AC97CTL_0, 0x88);
+
+ /* Set 16-bit audio data and choose L&R channel*/
+ stk1160_write_reg(dev, STK1160_AC97CTL_1 + 2, 0x01);
+}
+
+static struct snd_ac97_bus_ops stk1160_ac97_ops = {
+ .read = stk1160_read_ac97,
+ .write = stk1160_write_ac97,
+ .reset = stk1160_reset_ac97,
+};
+
+int stk1160_ac97_register(struct stk1160 *dev)
+{
+ struct snd_card *card = NULL;
+ struct snd_ac97_bus *ac97_bus;
+ struct snd_ac97_template ac97_template;
+ int rc;
+
+ /*
+ * Just want a card to access ac96 controls,
+ * the actual capture interface will be handled by snd-usb-audio
+ */
+ rc = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+ THIS_MODULE, 0, &card);
+ if (rc < 0)
+ return rc;
+
+ snd_card_set_dev(card, dev->dev);
+
+ /* TODO: I'm not sure where should I get these names :-( */
+ snprintf(card->shortname, sizeof(card->shortname),
+ "stk1160-mixer");
+ snprintf(card->longname, sizeof(card->longname),
+ "stk1160 ac97 codec mixer control");
+ strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver));
+
+ rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus);
+ if (rc)
+ goto err;
+
+ /* We must set private_data before calling snd_ac97_mixer */
+ memset(&ac97_template, 0, sizeof(ac97_template));
+ ac97_template.private_data = dev;
+ ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
+ rc = snd_ac97_mixer(ac97_bus, &ac97_template, &stk1160_ac97);
+ if (rc)
+ goto err;
+
+ dev->snd_card = card;
+ rc = snd_card_register(card);
+ if (rc)
+ goto err;
+
+ return 0;
+
+err:
+ dev->snd_card = NULL;
+ snd_card_free(card);
+ return rc;
+}
+
+int stk1160_ac97_unregister(struct stk1160 *dev)
+{
+ struct snd_card *card = dev->snd_card;
+
+ /*
+ * We need to check usb_device,
+ * because ac97 release attempts to communicate with codec
+ */
+ if (card && dev->udev)
+ snd_card_free(card);
+
+ return 0;
+}
diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c
new file mode 100644
index 000000000000..b62740846061
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-core.c
@@ -0,0 +1,430 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * TODO:
+ *
+ * 1. (Try to) detect if we must register ac97 mixer
+ * 2. Support stream at lower speed: lower frame rate or lower frame size.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <media/saa7115.h>
+
+#include "stk1160.h"
+#include "stk1160-reg.h"
+
+static unsigned int input;
+module_param(input, int, 0644);
+MODULE_PARM_DESC(input, "Set default input");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ezequiel Garcia");
+MODULE_DESCRIPTION("STK1160 driver");
+
+/* Devices supported by this driver */
+static struct usb_device_id stk1160_id_table[] = {
+ { USB_DEVICE(0x05e1, 0x0408) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, stk1160_id_table);
+
+/* saa7113 I2C address */
+static unsigned short saa7113_addrs[] = {
+ 0x4a >> 1,
+ I2C_CLIENT_END
+};
+
+/*
+ * Read/Write stk registers
+ */
+int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value)
+{
+ int ret;
+ int pipe = usb_rcvctrlpipe(dev->udev, 0);
+
+ *value = 0;
+ ret = usb_control_msg(dev->udev, pipe, 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00, reg, value, sizeof(u8), HZ);
+ if (ret < 0) {
+ stk1160_err("read failed on reg 0x%x (%d)\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value)
+{
+ int ret;
+ int pipe = usb_sndctrlpipe(dev->udev, 0);
+
+ ret = usb_control_msg(dev->udev, pipe, 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, reg, NULL, 0, HZ);
+ if (ret < 0) {
+ stk1160_err("write failed on reg 0x%x (%d)\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void stk1160_select_input(struct stk1160 *dev)
+{
+ static const u8 gctrl[] = {
+ 0x98, 0x90, 0x88, 0x80
+ };
+
+ if (dev->ctl_input < ARRAY_SIZE(gctrl))
+ stk1160_write_reg(dev, STK1160_GCTRL, gctrl[dev->ctl_input]);
+}
+
+/* TODO: We should break this into pieces */
+static void stk1160_reg_reset(struct stk1160 *dev)
+{
+ int i;
+
+ static const struct regval ctl[] = {
+ {STK1160_GCTRL+2, 0x0078},
+
+ {STK1160_RMCTL+1, 0x0000},
+ {STK1160_RMCTL+3, 0x0002},
+
+ {STK1160_PLLSO, 0x0010},
+ {STK1160_PLLSO+1, 0x0000},
+ {STK1160_PLLSO+2, 0x0014},
+ {STK1160_PLLSO+3, 0x000E},
+
+ {STK1160_PLLFD, 0x0046},
+
+ /* Timing generator setup */
+ {STK1160_TIGEN, 0x0012},
+ {STK1160_TICTL, 0x002D},
+ {STK1160_TICTL+1, 0x0001},
+ {STK1160_TICTL+2, 0x0000},
+ {STK1160_TICTL+3, 0x0000},
+ {STK1160_TIGEN, 0x0080},
+
+ {0xffff, 0xffff}
+ };
+
+ for (i = 0; ctl[i].reg != 0xffff; i++)
+ stk1160_write_reg(dev, ctl[i].reg, ctl[i].val);
+}
+
+static void stk1160_release(struct v4l2_device *v4l2_dev)
+{
+ struct stk1160 *dev = container_of(v4l2_dev, struct stk1160, v4l2_dev);
+
+ stk1160_info("releasing all resources\n");
+
+ stk1160_i2c_unregister(dev);
+
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ v4l2_device_unregister(&dev->v4l2_dev);
+ kfree(dev->alt_max_pkt_size);
+ kfree(dev);
+}
+
+/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
+#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
+
+/*
+ * Scan usb interface and populate max_pkt_size array
+ * with information on each alternate setting.
+ * The array should be allocated by the caller.
+ */
+static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev,
+ unsigned int *max_pkt_size)
+{
+ int i, e, sizedescr, size, ifnum;
+ const struct usb_endpoint_descriptor *desc;
+
+ bool has_video = false, has_audio = false;
+ const char *speed;
+
+ ifnum = intf->altsetting[0].desc.bInterfaceNumber;
+
+ /* Get endpoints */
+ for (i = 0; i < intf->num_altsetting; i++) {
+
+ for (e = 0; e < intf->altsetting[i].desc.bNumEndpoints; e++) {
+
+ /* This isn't clear enough, at least to me */
+ desc = &intf->altsetting[i].endpoint[e].desc;
+ sizedescr = le16_to_cpu(desc->wMaxPacketSize);
+ size = sizedescr & 0x7ff;
+
+ if (udev->speed == USB_SPEED_HIGH)
+ size = size * hb_mult(sizedescr);
+
+ if (usb_endpoint_xfer_isoc(desc) &&
+ usb_endpoint_dir_in(desc)) {
+ switch (desc->bEndpointAddress) {
+ case STK1160_EP_AUDIO:
+ has_audio = true;
+ break;
+ case STK1160_EP_VIDEO:
+ has_video = true;
+ max_pkt_size[i] = size;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Is this even possible? */
+ if (!(has_audio || has_video)) {
+ dev_err(&udev->dev, "no audio or video endpoints found\n");
+ return -ENODEV;
+ }
+
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ speed = "1.5";
+ break;
+ case USB_SPEED_FULL:
+ speed = "12";
+ break;
+ case USB_SPEED_HIGH:
+ speed = "480";
+ break;
+ default:
+ speed = "unknown";
+ }
+
+ dev_info(&udev->dev, "New device %s %s @ %s Mbps (%04x:%04x, interface %d, class %d)\n",
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
+ speed,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct),
+ ifnum,
+ intf->altsetting->desc.bInterfaceNumber);
+
+ /* This should never happen, since we rejected audio interfaces */
+ if (has_audio)
+ dev_warn(&udev->dev, "audio interface %d found.\n\
+ This is not implemented by this driver,\
+ you should use snd-usb-audio instead\n", ifnum);
+
+ if (has_video)
+ dev_info(&udev->dev, "video interface %d found\n",
+ ifnum);
+
+ /*
+ * Make sure we have 480 Mbps of bandwidth, otherwise things like
+ * video stream wouldn't likely work, since 12 Mbps is generally
+ * not enough even for most streams.
+ */
+ if (udev->speed != USB_SPEED_HIGH)
+ dev_warn(&udev->dev, "must be connected to a high-speed USB 2.0 port\n\
+ You may not be able to stream video smoothly\n");
+
+ return 0;
+}
+
+static int stk1160_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int rc = 0;
+
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ struct usb_device *udev;
+ struct stk1160 *dev;
+
+ udev = interface_to_usbdev(interface);
+
+ /*
+ * Since usb audio class is supported by snd-usb-audio,
+ * we reject audio interface.
+ */
+ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO)
+ return -ENODEV;
+
+ /* Alloc an array for all possible max_pkt_size */
+ alt_max_pkt_size = kmalloc(sizeof(alt_max_pkt_size[0]) *
+ interface->num_altsetting, GFP_KERNEL);
+ if (alt_max_pkt_size == NULL)
+ return -ENOMEM;
+
+ /*
+ * Scan usb posibilities and populate alt_max_pkt_size array.
+ * Also, check if device speed is fast enough.
+ */
+ rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size);
+ if (rc < 0) {
+ kfree(alt_max_pkt_size);
+ return rc;
+ }
+
+ dev = kzalloc(sizeof(struct stk1160), GFP_KERNEL);
+ if (dev == NULL) {
+ kfree(alt_max_pkt_size);
+ return -ENOMEM;
+ }
+
+ dev->alt_max_pkt_size = alt_max_pkt_size;
+ dev->udev = udev;
+ dev->num_alt = interface->num_altsetting;
+ dev->ctl_input = input;
+
+ /* We save struct device for debug purposes only */
+ dev->dev = &interface->dev;
+
+ usb_set_intfdata(interface, dev);
+
+ /* initialize videobuf2 stuff */
+ rc = stk1160_vb2_setup(dev);
+ if (rc < 0)
+ goto free_err;
+
+ /*
+ * There is no need to take any locks here in probe
+ * because we register the device node as the *last* thing.
+ */
+ spin_lock_init(&dev->buf_lock);
+ mutex_init(&dev->v4l_lock);
+ mutex_init(&dev->vb_queue_lock);
+
+ rc = v4l2_ctrl_handler_init(&dev->ctrl_handler, 0);
+ if (rc) {
+ stk1160_err("v4l2_ctrl_handler_init failed (%d)\n", rc);
+ goto free_err;
+ }
+
+ /*
+ * We obtain a v4l2_dev but defer
+ * registration of video device node as the last thing.
+ * There is no need to set the name if we give a device struct
+ */
+ dev->v4l2_dev.release = stk1160_release;
+ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler;
+ rc = v4l2_device_register(dev->dev, &dev->v4l2_dev);
+ if (rc) {
+ stk1160_err("v4l2_device_register failed (%d)\n", rc);
+ goto free_ctrl;
+ }
+
+ rc = stk1160_i2c_register(dev);
+ if (rc < 0)
+ goto unreg_v4l2;
+
+ /*
+ * To the best of my knowledge stk1160 boards only have
+ * saa7113, but it doesn't hurt to support them all.
+ */
+ dev->sd_saa7115 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa7115_auto", 0, saa7113_addrs);
+
+ stk1160_info("driver ver %s successfully loaded\n",
+ STK1160_VERSION);
+
+ /* i2c reset saa711x */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
+ 0, 0, 0);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+
+ /* reset stk1160 to default values */
+ stk1160_reg_reset(dev);
+
+ /* select default input */
+ stk1160_select_input(dev);
+
+ stk1160_ac97_register(dev);
+
+ rc = stk1160_video_register(dev);
+ if (rc < 0)
+ goto unreg_i2c;
+
+ return 0;
+
+unreg_i2c:
+ stk1160_i2c_unregister(dev);
+unreg_v4l2:
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_ctrl:
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+free_err:
+ kfree(alt_max_pkt_size);
+ kfree(dev);
+
+ return rc;
+}
+
+static void stk1160_disconnect(struct usb_interface *interface)
+{
+ struct stk1160 *dev;
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ /*
+ * Wait until all current v4l2 operation are finished
+ * then deallocate resources
+ */
+ mutex_lock(&dev->vb_queue_lock);
+ mutex_lock(&dev->v4l_lock);
+
+ /* Here is the only place where isoc get released */
+ stk1160_uninit_isoc(dev);
+
+ /* ac97 unregister needs to be done before usb_device is cleared */
+ stk1160_ac97_unregister(dev);
+
+ stk1160_clear_queue(dev);
+
+ video_unregister_device(&dev->vdev);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+
+ /* This way current users can detect device is gone */
+ dev->udev = NULL;
+
+ mutex_unlock(&dev->v4l_lock);
+ mutex_unlock(&dev->vb_queue_lock);
+
+ /*
+ * This calls stk1160_release if it's the last reference.
+ * therwise, release is posponed until there are no users left.
+ */
+ v4l2_device_put(&dev->v4l2_dev);
+}
+
+static struct usb_driver stk1160_usb_driver = {
+ .name = "stk1160",
+ .id_table = stk1160_id_table,
+ .probe = stk1160_probe,
+ .disconnect = stk1160_disconnect,
+};
+
+module_usb_driver(stk1160_usb_driver);
diff --git a/drivers/media/usb/stk1160/stk1160-i2c.c b/drivers/media/usb/stk1160/stk1160-i2c.c
new file mode 100644
index 000000000000..176ac937306b
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-i2c.c
@@ -0,0 +1,294 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+
+#include "stk1160.h"
+#include "stk1160-reg.h"
+
+static unsigned int i2c_debug;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
+
+#define dprintk_i2c(fmt, args...) \
+do { \
+ if (i2c_debug) \
+ printk(KERN_DEBUG fmt, ##args); \
+} while (0)
+
+static int stk1160_i2c_busy_wait(struct stk1160 *dev, u8 wait_bit_mask)
+{
+ unsigned long end;
+ u8 flag;
+
+ /* Wait until read/write finish bit is set */
+ end = jiffies + msecs_to_jiffies(STK1160_I2C_TIMEOUT);
+ while (time_is_after_jiffies(end)) {
+
+ stk1160_read_reg(dev, STK1160_SICTL+1, &flag);
+ /* read/write done? */
+ if (flag & wait_bit_mask)
+ goto done;
+
+ usleep_range(10 * USEC_PER_MSEC, 20 * USEC_PER_MSEC);
+ }
+
+ return -ETIMEDOUT;
+
+done:
+ return 0;
+}
+
+static int stk1160_i2c_write_reg(struct stk1160 *dev, u8 addr,
+ u8 reg, u8 value)
+{
+ int rc;
+
+ /* Set serial device address */
+ rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
+ if (rc < 0)
+ return rc;
+
+ /* Set i2c device register sub-address */
+ rc = stk1160_write_reg(dev, STK1160_SBUSW_WA, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Set i2c device register value */
+ rc = stk1160_write_reg(dev, STK1160_SBUSW_WD, value);
+ if (rc < 0)
+ return rc;
+
+ /* Start write now */
+ rc = stk1160_write_reg(dev, STK1160_SICTL, 0x01);
+ if (rc < 0)
+ return rc;
+
+ rc = stk1160_i2c_busy_wait(dev, 0x04);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int stk1160_i2c_read_reg(struct stk1160 *dev, u8 addr,
+ u8 reg, u8 *value)
+{
+ int rc;
+
+ /* Set serial device address */
+ rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
+ if (rc < 0)
+ return rc;
+
+ /* Set i2c device register sub-address */
+ rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, reg);
+ if (rc < 0)
+ return rc;
+
+ /* Start read now */
+ rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20);
+ if (rc < 0)
+ return rc;
+
+ rc = stk1160_i2c_busy_wait(dev, 0x01);
+ if (rc < 0)
+ return rc;
+
+ stk1160_read_reg(dev, STK1160_SBUSR_RD, value);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/*
+ * stk1160_i2c_check_for_device()
+ * check if there is a i2c_device at the supplied address
+ */
+static int stk1160_i2c_check_for_device(struct stk1160 *dev,
+ unsigned char addr)
+{
+ int rc;
+
+ /* Set serial device address */
+ rc = stk1160_write_reg(dev, STK1160_SICTL_SDA, addr);
+ if (rc < 0)
+ return rc;
+
+ /* Set device sub-address, we'll chip version reg */
+ rc = stk1160_write_reg(dev, STK1160_SBUSR_RA, 0x00);
+ if (rc < 0)
+ return rc;
+
+ /* Start read now */
+ rc = stk1160_write_reg(dev, STK1160_SICTL, 0x20);
+ if (rc < 0)
+ return rc;
+
+ rc = stk1160_i2c_busy_wait(dev, 0x01);
+ if (rc < 0)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ * stk1160_i2c_xfer()
+ * the main i2c transfer function
+ */
+static int stk1160_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[], int num)
+{
+ struct stk1160 *dev = i2c_adap->algo_data;
+ int addr, rc, i;
+
+ for (i = 0; i < num; i++) {
+ addr = msgs[i].addr << 1;
+ dprintk_i2c("%s: addr=%x", __func__, addr);
+
+ if (!msgs[i].len) {
+ /* no len: check only for device presence */
+ rc = stk1160_i2c_check_for_device(dev, addr);
+ if (rc < 0) {
+ dprintk_i2c(" no device\n");
+ return rc;
+ }
+
+ } else if (msgs[i].flags & I2C_M_RD) {
+ /* read request without preceding register selection */
+ dprintk_i2c(" subaddr not selected");
+ rc = -EOPNOTSUPP;
+ goto err;
+
+ } else if (i + 1 < num && msgs[i].len <= 2 &&
+ (msgs[i + 1].flags & I2C_M_RD) &&
+ msgs[i].addr == msgs[i + 1].addr) {
+
+ if (msgs[i].len != 1 || msgs[i + 1].len != 1) {
+ dprintk_i2c(" len not supported");
+ rc = -EOPNOTSUPP;
+ goto err;
+ }
+
+ dprintk_i2c(" subaddr=%x", msgs[i].buf[0]);
+
+ rc = stk1160_i2c_read_reg(dev, addr, msgs[i].buf[0],
+ msgs[i + 1].buf);
+
+ dprintk_i2c(" read=%x", *msgs[i + 1].buf);
+
+ /* consumed two msgs, so we skip one of them */
+ i++;
+
+ } else {
+ if (msgs[i].len != 2) {
+ dprintk_i2c(" len not supported");
+ rc = -EOPNOTSUPP;
+ goto err;
+ }
+
+ dprintk_i2c(" subaddr=%x write=%x",
+ msgs[i].buf[0], msgs[i].buf[1]);
+
+ rc = stk1160_i2c_write_reg(dev, addr, msgs[i].buf[0],
+ msgs[i].buf[1]);
+ }
+
+ if (rc < 0)
+ goto err;
+ dprintk_i2c(" OK\n");
+ }
+
+ return num;
+err:
+ dprintk_i2c(" ERROR: %d\n", rc);
+ return num;
+}
+
+/*
+ * functionality(), what da heck is this?
+ */
+static u32 functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm algo = {
+ .master_xfer = stk1160_i2c_xfer,
+ .functionality = functionality,
+};
+
+static struct i2c_adapter adap_template = {
+ .owner = THIS_MODULE,
+ .name = "stk1160",
+ .algo = &algo,
+};
+
+static struct i2c_client client_template = {
+ .name = "stk1160 internal",
+};
+
+/*
+ * stk1160_i2c_register()
+ * register i2c bus
+ */
+int stk1160_i2c_register(struct stk1160 *dev)
+{
+ int rc;
+
+ dev->i2c_adap = adap_template;
+ dev->i2c_adap.dev.parent = dev->dev;
+ strcpy(dev->i2c_adap.name, "stk1160");
+ dev->i2c_adap.algo_data = dev;
+
+ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+
+ rc = i2c_add_adapter(&dev->i2c_adap);
+ if (rc < 0) {
+ stk1160_err("cannot add i2c adapter (%d)\n", rc);
+ return rc;
+ }
+
+ dev->i2c_client = client_template;
+ dev->i2c_client.adapter = &dev->i2c_adap;
+
+ /* Set i2c clock divider device address */
+ stk1160_write_reg(dev, STK1160_SICTL_CD, 0x0f);
+
+ /* ??? */
+ stk1160_write_reg(dev, STK1160_ASIC + 3, 0x00);
+
+ return 0;
+}
+
+/*
+ * stk1160_i2c_unregister()
+ * unregister i2c_bus
+ */
+int stk1160_i2c_unregister(struct stk1160 *dev)
+{
+ i2c_del_adapter(&dev->i2c_adap);
+ return 0;
+}
diff --git a/drivers/media/usb/stk1160/stk1160-reg.h b/drivers/media/usb/stk1160/stk1160-reg.h
new file mode 100644
index 000000000000..3e49da6e7edd
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-reg.h
@@ -0,0 +1,93 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/* GPIO Control */
+#define STK1160_GCTRL 0x000
+
+/* Remote Wakup Control */
+#define STK1160_RMCTL 0x00c
+
+/*
+ * Decoder Control Register:
+ * This byte controls capture start/stop
+ * with bit #7 (0x?? OR 0x80 to activate).
+ */
+#define STK1160_DCTRL 0x100
+
+/* Capture Frame Start Position */
+#define STK116_CFSPO 0x110
+#define STK116_CFSPO_STX_L 0x110
+#define STK116_CFSPO_STX_H 0x111
+#define STK116_CFSPO_STY_L 0x112
+#define STK116_CFSPO_STY_H 0x113
+
+/* Capture Frame End Position */
+#define STK116_CFEPO 0x114
+#define STK116_CFEPO_ENX_L 0x114
+#define STK116_CFEPO_ENX_H 0x115
+#define STK116_CFEPO_ENY_L 0x116
+#define STK116_CFEPO_ENY_H 0x117
+
+/* Serial Interface Control */
+#define STK1160_SICTL 0x200
+#define STK1160_SICTL_CD 0x202
+#define STK1160_SICTL_SDA 0x203
+
+/* Serial Bus Write */
+#define STK1160_SBUSW 0x204
+#define STK1160_SBUSW_WA 0x204
+#define STK1160_SBUSW_WD 0x205
+
+/* Serial Bus Read */
+#define STK1160_SBUSR 0x208
+#define STK1160_SBUSR_RA 0x208
+#define STK1160_SBUSR_RD 0x209
+
+/* Alternate Serial Inteface Control */
+#define STK1160_ASIC 0x2fc
+
+/* PLL Select Options */
+#define STK1160_PLLSO 0x018
+
+/* PLL Frequency Divider */
+#define STK1160_PLLFD 0x01c
+
+/* Timing Generator */
+#define STK1160_TIGEN 0x300
+
+/* Timing Control Parameter */
+#define STK1160_TICTL 0x350
+
+/* AC97 Audio Control */
+#define STK1160_AC97CTL_0 0x500
+#define STK1160_AC97CTL_1 0x504
+
+/* Use [0:6] bits of register 0x504 to set codec command address */
+#define STK1160_AC97_ADDR 0x504
+/* Use [16:31] bits of register 0x500 to set codec command data */
+#define STK1160_AC97_CMD 0x502
+
+/* Audio I2S Interface */
+#define STK1160_I2SCTL 0x50c
+
+/* EEPROM Interface */
+#define STK1160_EEPROM_SZ 0x5f0
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
new file mode 100644
index 000000000000..fe6e857969ca
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -0,0 +1,739 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <media/saa7115.h>
+
+#include "stk1160.h"
+#include "stk1160-reg.h"
+
+static unsigned int vidioc_debug;
+module_param(vidioc_debug, int, 0644);
+MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]");
+
+static bool keep_buffers;
+module_param(keep_buffers, bool, 0644);
+MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming");
+
+/* supported video standards */
+static struct stk1160_fmt format[] = {
+ {
+ .name = "16 bpp YUY2, 4:2:2, packed",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ }
+};
+
+static void stk1160_set_std(struct stk1160 *dev)
+{
+ int i;
+
+ static struct regval std525[] = {
+
+ /* 720x480 */
+
+ /* Frame start */
+ {STK116_CFSPO_STX_L, 0x0000},
+ {STK116_CFSPO_STX_H, 0x0000},
+ {STK116_CFSPO_STY_L, 0x0003},
+ {STK116_CFSPO_STY_H, 0x0000},
+
+ /* Frame end */
+ {STK116_CFEPO_ENX_L, 0x05a0},
+ {STK116_CFEPO_ENX_H, 0x0005},
+ {STK116_CFEPO_ENY_L, 0x00f3},
+ {STK116_CFEPO_ENY_H, 0x0000},
+
+ {0xffff, 0xffff}
+ };
+
+ static struct regval std625[] = {
+
+ /* 720x576 */
+
+ /* TODO: Each line of frame has some junk at the end */
+ /* Frame start */
+ {STK116_CFSPO, 0x0000},
+ {STK116_CFSPO+1, 0x0000},
+ {STK116_CFSPO+2, 0x0001},
+ {STK116_CFSPO+3, 0x0000},
+
+ /* Frame end */
+ {STK116_CFEPO, 0x05a0},
+ {STK116_CFEPO+1, 0x0005},
+ {STK116_CFEPO+2, 0x0121},
+ {STK116_CFEPO+3, 0x0001},
+
+ {0xffff, 0xffff}
+ };
+
+ if (dev->norm & V4L2_STD_525_60) {
+ stk1160_dbg("registers to NTSC like standard\n");
+ for (i = 0; std525[i].reg != 0xffff; i++)
+ stk1160_write_reg(dev, std525[i].reg, std525[i].val);
+ } else {
+ stk1160_dbg("registers to PAL like standard\n");
+ for (i = 0; std625[i].reg != 0xffff; i++)
+ stk1160_write_reg(dev, std625[i].reg, std625[i].val);
+ }
+
+}
+
+/*
+ * Set a new alternate setting.
+ * Returns true is dev->max_pkt_size has changed, false otherwise.
+ */
+static bool stk1160_set_alternate(struct stk1160 *dev)
+{
+ int i, prev_alt = dev->alt;
+ unsigned int min_pkt_size;
+ bool new_pkt_size;
+
+ /*
+ * If we don't set right alternate,
+ * then we will get a green screen with junk.
+ */
+ min_pkt_size = STK1160_MIN_PKT_SIZE;
+
+ for (i = 0; i < dev->num_alt; i++) {
+ /* stop when the selected alt setting offers enough bandwidth */
+ if (dev->alt_max_pkt_size[i] >= min_pkt_size) {
+ dev->alt = i;
+ break;
+ /*
+ * otherwise make sure that we end up with the maximum bandwidth
+ * because the min_pkt_size equation might be wrong...
+ */
+ } else if (dev->alt_max_pkt_size[i] >
+ dev->alt_max_pkt_size[dev->alt])
+ dev->alt = i;
+ }
+
+ stk1160_info("setting alternate %d\n", dev->alt);
+
+ if (dev->alt != prev_alt) {
+ stk1160_dbg("minimum isoc packet size: %u (alt=%d)\n",
+ min_pkt_size, dev->alt);
+ stk1160_dbg("setting alt %d with wMaxPacketSize=%u\n",
+ dev->alt, dev->alt_max_pkt_size[dev->alt]);
+ usb_set_interface(dev->udev, 0, dev->alt);
+ }
+
+ new_pkt_size = dev->max_pkt_size != dev->alt_max_pkt_size[dev->alt];
+ dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
+
+ return new_pkt_size;
+}
+
+static int stk1160_start_streaming(struct stk1160 *dev)
+{
+ bool new_pkt_size;
+ int rc = 0;
+ int i;
+
+ /* Check device presence */
+ if (!dev->udev)
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&dev->v4l_lock))
+ return -ERESTARTSYS;
+ /*
+ * For some reason it is mandatory to set alternate *first*
+ * and only *then* initialize isoc urbs.
+ * Someone please explain me why ;)
+ */
+ new_pkt_size = stk1160_set_alternate(dev);
+
+ /*
+ * We (re)allocate isoc urbs if:
+ * there is no allocated isoc urbs, OR
+ * a new dev->max_pkt_size is detected
+ */
+ if (!dev->isoc_ctl.num_bufs || new_pkt_size) {
+ rc = stk1160_alloc_isoc(dev);
+ if (rc < 0)
+ goto out_stop_hw;
+ }
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_KERNEL);
+ if (rc) {
+ stk1160_err("cannot submit urb[%d] (%d)\n", i, rc);
+ goto out_uninit;
+ }
+ }
+
+ /* Start saa711x */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
+
+ /* Start stk1160 */
+ stk1160_write_reg(dev, STK1160_DCTRL, 0xb3);
+ stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00);
+
+ stk1160_dbg("streaming started\n");
+
+ mutex_unlock(&dev->v4l_lock);
+
+ return 0;
+
+out_uninit:
+ stk1160_uninit_isoc(dev);
+out_stop_hw:
+ usb_set_interface(dev->udev, 0, 0);
+ stk1160_clear_queue(dev);
+
+ mutex_unlock(&dev->v4l_lock);
+
+ return rc;
+}
+
+/* Must be called with v4l_lock hold */
+static void stk1160_stop_hw(struct stk1160 *dev)
+{
+ /* If the device is not physically present, there is nothing to do */
+ if (!dev->udev)
+ return;
+
+ /* set alternate 0 */
+ dev->alt = 0;
+ stk1160_info("setting alternate %d\n", dev->alt);
+ usb_set_interface(dev->udev, 0, 0);
+
+ /* Stop stk1160 */
+ stk1160_write_reg(dev, STK1160_DCTRL, 0x00);
+ stk1160_write_reg(dev, STK1160_DCTRL+3, 0x00);
+
+ /* Stop saa711x */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
+}
+
+static int stk1160_stop_streaming(struct stk1160 *dev)
+{
+ if (mutex_lock_interruptible(&dev->v4l_lock))
+ return -ERESTARTSYS;
+
+ stk1160_cancel_isoc(dev);
+
+ /*
+ * It is possible to keep buffers around using a module parameter.
+ * This is intended to avoid memory fragmentation.
+ */
+ if (!keep_buffers)
+ stk1160_free_isoc(dev);
+
+ stk1160_stop_hw(dev);
+
+ stk1160_clear_queue(dev);
+
+ stk1160_dbg("streaming stopped\n");
+
+ mutex_unlock(&dev->v4l_lock);
+
+ return 0;
+}
+
+static struct v4l2_file_operations stk1160_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+/*
+ * vidioc ioctls
+ */
+static int vidioc_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ strcpy(cap->driver, "stk1160");
+ strcpy(cap->card, "stk1160");
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps =
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ /*
+ * User can't choose size at his own will,
+ * so we just return him the current size chosen
+ * at standard selection.
+ * TODO: Implement frame scaling?
+ */
+
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ f->fmt.pix.bytesperline = dev->width * 2;
+ f->fmt.pix.sizeimage = dev->height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct stk1160 *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
+ /* We don't support any format changes */
+
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct stk1160 *dev = video_drvdata(file);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
+ return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ *norm = dev->norm;
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct stk1160 *dev = video_drvdata(file);
+ struct vb2_queue *q = &dev->vb_vidq;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ /* Check device presence */
+ if (!dev->udev)
+ return -ENODEV;
+
+ /* We need to set this now, before we call stk1160_set_std */
+ dev->norm = *norm;
+
+ /* This is taken from saa7115 video decoder */
+ if (dev->norm & V4L2_STD_525_60) {
+ dev->width = 720;
+ dev->height = 480;
+ } else if (dev->norm & V4L2_STD_625_50) {
+ dev->width = 720;
+ dev->height = 576;
+ } else {
+ stk1160_err("invalid standard\n");
+ return -EINVAL;
+ }
+
+ stk1160_set_std(dev);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
+ dev->norm);
+
+ return 0;
+}
+
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ if (i->index > STK1160_MAX_INPUT)
+ return -EINVAL;
+
+ sprintf(i->name, "Composite%d", i->index);
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = dev->vdev.tvnorms;
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct stk1160 *dev = video_drvdata(file);
+ *i = dev->ctl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ if (vb2_is_busy(&dev->vb_vidq))
+ return -EBUSY;
+
+ if (i > STK1160_MAX_INPUT)
+ return -EINVAL;
+
+ dev->ctl_input = i;
+
+ stk1160_select_input(dev);
+
+ return 0;
+}
+
+static int vidioc_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ switch (chip->match.type) {
+ case V4L2_CHIP_MATCH_HOST:
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct stk1160 *dev = video_drvdata(file);
+ int rc;
+ u8 val;
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ /* TODO: Support me please :-( */
+ return -EINVAL;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ rc = stk1160_read_reg(dev, reg->reg, &val);
+ reg->val = val;
+ reg->size = 1;
+
+ return rc;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct stk1160 *dev = video_drvdata(file);
+
+ switch (reg->match.type) {
+ case V4L2_CHIP_MATCH_AC97:
+ return -EINVAL;
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
+ default:
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ }
+
+ /* Match host */
+ return stk1160_write_reg(dev, reg->reg, cpu_to_le16(reg->val));
+}
+#endif
+
+static const struct v4l2_ioctl_ops stk1160_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+
+ /* vb2 takes care of these */
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+/********************************************************************/
+
+/*
+ * Videobuf2 operations
+ */
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *v4l_fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct stk1160 *dev = vb2_get_drv_priv(vq);
+ unsigned long size;
+
+ size = dev->width * dev->height * 2;
+
+ /*
+ * Here we can change the number of buffers being requested.
+ * So, we set a minimum and a maximum like this:
+ */
+ *nbuffers = clamp_t(unsigned int, *nbuffers,
+ STK1160_MIN_VIDEO_BUFFERS, STK1160_MAX_VIDEO_BUFFERS);
+
+ /* This means a packed colorformat */
+ *nplanes = 1;
+
+ sizes[0] = size;
+
+ stk1160_info("%s: buffer count %d, each %ld bytes\n",
+ __func__, *nbuffers, size);
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ unsigned long flags;
+ struct stk1160 *dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct stk1160_buffer *buf =
+ container_of(vb, struct stk1160_buffer, vb);
+
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ if (!dev->udev) {
+ /*
+ * If the device is disconnected return the buffer to userspace
+ * directly. The next QBUF call will fail with -ENODEV.
+ */
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ } else {
+
+ buf->mem = vb2_plane_vaddr(vb, 0);
+ buf->length = vb2_plane_size(vb, 0);
+ buf->bytesused = 0;
+ buf->pos = 0;
+
+ /*
+ * If buffer length is less from expected then we return
+ * the buffer to userspace directly.
+ */
+ if (buf->length < dev->width * dev->height * 2)
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ else
+ list_add_tail(&buf->list, &dev->avail_bufs);
+
+ }
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+}
+
+static int start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct stk1160 *dev = vb2_get_drv_priv(vq);
+ return stk1160_start_streaming(dev);
+}
+
+/* abort streaming and wait for last buffer */
+static int stop_streaming(struct vb2_queue *vq)
+{
+ struct stk1160 *dev = vb2_get_drv_priv(vq);
+ return stk1160_stop_streaming(dev);
+}
+
+static struct vb2_ops stk1160_video_qops = {
+ .queue_setup = queue_setup,
+ .buf_queue = buffer_queue,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static struct video_device v4l_template = {
+ .name = "stk1160",
+ .tvnorms = V4L2_STD_525_60 | V4L2_STD_625_50,
+ .fops = &stk1160_fops,
+ .ioctl_ops = &stk1160_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+/********************************************************************/
+
+/* Must be called with both v4l_lock and vb_queue_lock hold */
+void stk1160_clear_queue(struct stk1160 *dev)
+{
+ struct stk1160_buffer *buf;
+ unsigned long flags;
+
+ /* Release all active buffers */
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ while (!list_empty(&dev->avail_bufs)) {
+ buf = list_first_entry(&dev->avail_bufs,
+ struct stk1160_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ stk1160_info("buffer [%p/%d] aborted\n",
+ buf, buf->vb.v4l2_buf.index);
+ }
+ /* It's important to clear current buffer */
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+}
+
+int stk1160_vb2_setup(struct stk1160 *dev)
+{
+ int rc;
+ struct vb2_queue *q;
+
+ q = &dev->vb_vidq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = dev;
+ q->buf_struct_size = sizeof(struct stk1160_buffer);
+ q->ops = &stk1160_video_qops;
+ q->mem_ops = &vb2_vmalloc_memops;
+
+ rc = vb2_queue_init(q);
+ if (rc < 0)
+ return rc;
+
+ /* initialize video dma queue */
+ INIT_LIST_HEAD(&dev->avail_bufs);
+
+ return 0;
+}
+
+int stk1160_video_register(struct stk1160 *dev)
+{
+ int rc;
+
+ /* Initialize video_device with a template structure */
+ dev->vdev = v4l_template;
+ dev->vdev.debug = vidioc_debug;
+ dev->vdev.queue = &dev->vb_vidq;
+
+ /*
+ * Provide mutexes for v4l2 core and for videobuf2 queue.
+ * It will be used to protect *only* v4l2 ioctls.
+ */
+ dev->vdev.lock = &dev->v4l_lock;
+ dev->vdev.queue->lock = &dev->vb_queue_lock;
+
+ /* This will be used to set video_device parent */
+ dev->vdev.v4l2_dev = &dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
+
+ /* NTSC is default */
+ dev->norm = V4L2_STD_NTSC_M;
+ dev->width = 720;
+ dev->height = 480;
+
+ /* set default format */
+ dev->fmt = &format[0];
+ stk1160_set_std(dev);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std,
+ dev->norm);
+
+ video_set_drvdata(&dev->vdev, dev);
+ rc = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1);
+ if (rc < 0) {
+ stk1160_err("video_register_device failed (%d)\n", rc);
+ return rc;
+ }
+
+ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&dev->vdev));
+
+ return 0;
+}
diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c
new file mode 100644
index 000000000000..8bdfb0275313
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160-video.c
@@ -0,0 +1,522 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+
+#include "stk1160.h"
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+static inline void print_err_status(struct stk1160 *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+
+ if (packet < 0)
+ printk_ratelimited(KERN_WARNING "URB status %d [%s].\n",
+ status, errmsg);
+ else
+ printk_ratelimited(KERN_INFO "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+}
+
+static inline
+struct stk1160_buffer *stk1160_next_buffer(struct stk1160 *dev)
+{
+ struct stk1160_buffer *buf = NULL;
+ unsigned long flags = 0;
+
+ /* Current buffer must be NULL when this functions gets called */
+ BUG_ON(dev->isoc_ctl.buf);
+
+ spin_lock_irqsave(&dev->buf_lock, flags);
+ if (!list_empty(&dev->avail_bufs)) {
+ buf = list_first_entry(&dev->avail_bufs,
+ struct stk1160_buffer, list);
+ list_del(&buf->list);
+ }
+ spin_unlock_irqrestore(&dev->buf_lock, flags);
+
+ return buf;
+}
+
+static inline
+void stk1160_buffer_done(struct stk1160 *dev)
+{
+ struct stk1160_buffer *buf = dev->isoc_ctl.buf;
+
+ dev->field_count++;
+
+ buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
+ buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ buf->vb.v4l2_buf.bytesused = buf->bytesused;
+ do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
+
+ vb2_set_plane_payload(&buf->vb, 0, buf->bytesused);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+
+ dev->isoc_ctl.buf = NULL;
+}
+
+static inline
+void stk1160_copy_video(struct stk1160 *dev, u8 *src, int len)
+{
+ int linesdone, lineoff, lencopy;
+ int bytesperline = dev->width * 2;
+ struct stk1160_buffer *buf = dev->isoc_ctl.buf;
+ u8 *dst = buf->mem;
+ int remain;
+
+ /*
+ * TODO: These stk1160_dbg are very spammy!
+ * We should 1) check why we are getting them
+ * and 2) add ratelimit.
+ *
+ * UPDATE: One of the reasons (the only one?) for getting these
+ * is incorrect standard (mismatch between expected and configured).
+ * So perhaps, we could add a counter for errors. When the counter
+ * reaches some value, we simply stop streaming.
+ */
+
+ len -= 4;
+ src += 4;
+
+ remain = len;
+
+ linesdone = buf->pos / bytesperline;
+ lineoff = buf->pos % bytesperline; /* offset in current line */
+
+ if (!buf->odd)
+ dst += bytesperline;
+
+ /* Multiply linesdone by two, to take account of the other field */
+ dst += linesdone * bytesperline * 2 + lineoff;
+
+ /* Copy the remaining of current line */
+ if (remain < (bytesperline - lineoff))
+ lencopy = remain;
+ else
+ lencopy = bytesperline - lineoff;
+
+ /*
+ * Check if we have enough space left in the buffer.
+ * In that case, we force loop exit after copy.
+ */
+ if (lencopy > buf->bytesused - buf->length) {
+ lencopy = buf->bytesused - buf->length;
+ remain = lencopy;
+ }
+
+ /* Check if the copy is done */
+ if (lencopy == 0 || remain == 0)
+ return;
+
+ /* Let the bug hunt begin! sanity checks! */
+ if (lencopy < 0) {
+ stk1160_dbg("copy skipped: negative lencopy\n");
+ return;
+ }
+
+ if ((unsigned long)dst + lencopy >
+ (unsigned long)buf->mem + buf->length) {
+ printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n");
+ return;
+ }
+
+ memcpy(dst, src, lencopy);
+
+ buf->bytesused += lencopy;
+ buf->pos += lencopy;
+ remain -= lencopy;
+
+ /* Copy current field line by line, interlacing with the other field */
+ while (remain > 0) {
+
+ dst += lencopy + bytesperline;
+ src += lencopy;
+
+ /* Copy one line at a time */
+ if (remain < bytesperline)
+ lencopy = remain;
+ else
+ lencopy = bytesperline;
+
+ /*
+ * Check if we have enough space left in the buffer.
+ * In that case, we force loop exit after copy.
+ */
+ if (lencopy > buf->bytesused - buf->length) {
+ lencopy = buf->bytesused - buf->length;
+ remain = lencopy;
+ }
+
+ /* Check if the copy is done */
+ if (lencopy == 0 || remain == 0)
+ return;
+
+ if (lencopy < 0) {
+ printk_ratelimited(KERN_WARNING "stk1160: negative lencopy detected\n");
+ return;
+ }
+
+ if ((unsigned long)dst + lencopy >
+ (unsigned long)buf->mem + buf->length) {
+ printk_ratelimited(KERN_WARNING "stk1160: buffer overflow detected\n");
+ return;
+ }
+
+ memcpy(dst, src, lencopy);
+ remain -= lencopy;
+
+ buf->bytesused += lencopy;
+ buf->pos += lencopy;
+ }
+}
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static void stk1160_process_isoc(struct stk1160 *dev, struct urb *urb)
+{
+ int i, len, status;
+ u8 *p;
+
+ if (!dev) {
+ stk1160_warn("%s called with null device\n", __func__);
+ return;
+ }
+
+ if (urb->status < 0) {
+ /* Print status and drop current packet (or field?) */
+ print_err_status(dev, -1, urb->status);
+ return;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ status = urb->iso_frame_desc[i].status;
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ continue;
+ }
+
+ /* Get packet actual length and pointer to data */
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ len = urb->iso_frame_desc[i].actual_length;
+
+ /* Empty packet */
+ if (len <= 4)
+ continue;
+
+ /*
+ * An 8-byte packet sequence means end of field.
+ * So if we don't have any packet, we start receiving one now
+ * and if we do have a packet, then we are done with it.
+ *
+ * These end of field packets are always 0xc0 or 0x80,
+ * but not always 8-byte long so we don't check packet length.
+ */
+ if (p[0] == 0xc0) {
+
+ /*
+ * If first byte is 0xc0 then we received
+ * second field, and frame has ended.
+ */
+ if (dev->isoc_ctl.buf != NULL)
+ stk1160_buffer_done(dev);
+
+ dev->isoc_ctl.buf = stk1160_next_buffer(dev);
+ if (dev->isoc_ctl.buf == NULL)
+ return;
+ }
+
+ /*
+ * If we don't have a buffer here, then it means we
+ * haven't found the start mark sequence.
+ */
+ if (dev->isoc_ctl.buf == NULL)
+ continue;
+
+ if (p[0] == 0xc0 || p[0] == 0x80) {
+
+ /* We set next packet parity and
+ * continue to get next one
+ */
+ dev->isoc_ctl.buf->odd = *p & 0x40;
+ dev->isoc_ctl.buf->pos = 0;
+ continue;
+ }
+
+ stk1160_copy_video(dev, p, len);
+ }
+}
+
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void stk1160_isoc_irq(struct urb *urb)
+{
+ int i, rc;
+ struct stk1160 *dev = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET: /* kill */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* TODO: check uvc driver: he frees the queue here */
+ return;
+ default:
+ stk1160_err("urb error! status %d\n", urb->status);
+ return;
+ }
+
+ stk1160_process_isoc(dev, urb);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (rc)
+ stk1160_err("urb re-submit failed (%d)\n", rc);
+}
+
+/*
+ * Cancel urbs
+ * This function can't be called in atomic context
+ */
+void stk1160_cancel_isoc(struct stk1160 *dev)
+{
+ int i, num_bufs = dev->isoc_ctl.num_bufs;
+
+ /*
+ * This check is not necessary, but we add it
+ * to avoid a spurious debug message
+ */
+ if (!num_bufs)
+ return;
+
+ stk1160_dbg("killing %d urbs...\n", num_bufs);
+
+ for (i = 0; i < num_bufs; i++) {
+
+ /*
+ * To kill urbs we can't be in atomic context.
+ * We don't care for NULL pointer since
+ * usb_kill_urb allows it.
+ */
+ usb_kill_urb(dev->isoc_ctl.urb[i]);
+ }
+
+ stk1160_dbg("all urbs killed\n");
+}
+
+/*
+ * Releases urb and transfer buffers
+ * Obviusly, associated urb must be killed before releasing it.
+ */
+void stk1160_free_isoc(struct stk1160 *dev)
+{
+ struct urb *urb;
+ int i, num_bufs = dev->isoc_ctl.num_bufs;
+
+ stk1160_dbg("freeing %d urb buffers...\n", num_bufs);
+
+ for (i = 0; i < num_bufs; i++) {
+
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+#else
+ kfree(dev->isoc_ctl.transfer_buffer[i]);
+#endif
+ }
+ usb_free_urb(urb);
+ dev->isoc_ctl.urb[i] = NULL;
+ }
+ dev->isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->isoc_ctl.urb);
+ kfree(dev->isoc_ctl.transfer_buffer);
+
+ dev->isoc_ctl.urb = NULL;
+ dev->isoc_ctl.transfer_buffer = NULL;
+ dev->isoc_ctl.num_bufs = 0;
+
+ stk1160_dbg("all urb buffers freed\n");
+}
+
+/*
+ * Helper for cancelling and freeing urbs
+ * This function can't be called in atomic context
+ */
+void stk1160_uninit_isoc(struct stk1160 *dev)
+{
+ stk1160_cancel_isoc(dev);
+ stk1160_free_isoc(dev);
+}
+
+/*
+ * Allocate URBs
+ */
+int stk1160_alloc_isoc(struct stk1160 *dev)
+{
+ struct urb *urb;
+ int i, j, k, sb_size, max_packets, num_bufs;
+
+ /*
+ * It may be necessary to release isoc here,
+ * since isoc are only released on disconnection.
+ * (see new_pkt_size flag)
+ */
+ if (dev->isoc_ctl.num_bufs)
+ stk1160_uninit_isoc(dev);
+
+ stk1160_dbg("allocating urbs...\n");
+
+ num_bufs = STK1160_NUM_BUFS;
+ max_packets = STK1160_NUM_PACKETS;
+ sb_size = max_packets * dev->max_pkt_size;
+
+ dev->isoc_ctl.buf = NULL;
+ dev->isoc_ctl.max_pkt_size = dev->max_pkt_size;
+ dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ stk1160_err("out of memory for urb array\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ stk1160_err("out of memory for usb transfers\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < num_bufs; i++) {
+
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ stk1160_err("cannot alloc urb[%d]\n", i);
+ goto free_i_bufs;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+
+#ifndef CONFIG_DMA_NONCOHERENT
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+#else
+ dev->isoc_ctl.transfer_buffer[i] = kmalloc(sb_size, GFP_KERNEL);
+#endif
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ stk1160_err("cannot alloc %d bytes for tx[%d] buffer\n",
+ sb_size, i);
+ goto free_i_bufs;
+ }
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ /*
+ * FIXME: Where can I get the endpoint?
+ */
+ urb->dev = dev->udev;
+ urb->pipe = usb_rcvisocpipe(dev->udev, STK1160_EP_VIDEO);
+ urb->transfer_buffer = dev->isoc_ctl.transfer_buffer[i];
+ urb->transfer_buffer_length = sb_size;
+ urb->complete = stk1160_isoc_irq;
+ urb->context = dev;
+ urb->interval = 1;
+ urb->start_frame = 0;
+ urb->number_of_packets = max_packets;
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
+
+ k = 0;
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length =
+ dev->isoc_ctl.max_pkt_size;
+ k += dev->isoc_ctl.max_pkt_size;
+ }
+ }
+
+ stk1160_dbg("urbs allocated\n");
+
+ /* At last we can say we have some buffers */
+ dev->isoc_ctl.num_bufs = num_bufs;
+
+ return 0;
+
+free_i_bufs:
+ /* Save the allocated buffers so far, so we can properly free them */
+ dev->isoc_ctl.num_bufs = i+1;
+ stk1160_free_isoc(dev);
+ return -ENOMEM;
+}
+
diff --git a/drivers/media/usb/stk1160/stk1160.h b/drivers/media/usb/stk1160/stk1160.h
new file mode 100644
index 000000000000..3feba0033f98
--- /dev/null
+++ b/drivers/media/usb/stk1160/stk1160.h
@@ -0,0 +1,208 @@
+/*
+ * STK1160 driver
+ *
+ * Copyright (C) 2012 Ezequiel Garcia
+ * <elezegarcia--a.t--gmail.com>
+ *
+ * Based on Easycap driver by R.M. Thomas
+ * Copyright (C) 2010 R.M. Thomas
+ * <rmthomas--a.t--sciolus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/ac97_codec.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+#define STK1160_VERSION "0.9.5"
+#define STK1160_VERSION_NUM 0x000905
+
+/* TODO: Decide on number of packets for each buffer */
+#define STK1160_NUM_PACKETS 64
+
+/* Number of buffers for isoc transfers */
+#define STK1160_NUM_BUFS 16 /* TODO */
+
+/* TODO: This endpoint address should be retrieved */
+#define STK1160_EP_VIDEO 0x82
+#define STK1160_EP_AUDIO 0x81
+
+/* Max and min video buffers */
+#define STK1160_MIN_VIDEO_BUFFERS 8
+#define STK1160_MAX_VIDEO_BUFFERS 32
+
+#define STK1160_MIN_PKT_SIZE 3072
+
+#define STK1160_MAX_INPUT 3
+
+#define STK1160_I2C_TIMEOUT 100
+
+/* TODO: Print helpers
+ * I could use dev_xxx, pr_xxx, v4l2_xxx or printk.
+ * However, there isn't a solid consensus on which
+ * new drivers should use.
+ *
+ */
+#define DEBUG
+#ifdef DEBUG
+#define stk1160_dbg(fmt, args...) \
+ printk(KERN_DEBUG "stk1160: " fmt, ## args)
+#else
+#define stk1160_dbg(fmt, args...)
+#endif
+
+#define stk1160_info(fmt, args...) \
+ pr_info("stk1160: " fmt, ## args)
+
+#define stk1160_warn(fmt, args...) \
+ pr_warn("stk1160: " fmt, ## args)
+
+#define stk1160_err(fmt, args...) \
+ pr_err("stk1160: " fmt, ## args)
+
+/* Buffer for one video frame */
+struct stk1160_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ struct list_head list;
+
+ void *mem;
+ unsigned int length; /* buffer length */
+ unsigned int bytesused; /* bytes written */
+ int odd; /* current oddity */
+
+ /*
+ * Since we interlace two fields per frame,
+ * this is different from bytesused.
+ */
+ unsigned int pos; /* current pos inside buffer */
+};
+
+struct stk1160_isoc_ctl {
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+
+ /* number of allocated urbs */
+ int num_bufs;
+
+ /* urb for isoc transfers */
+ struct urb **urb;
+
+ /* transfer buffers for isoc transfer */
+ char **transfer_buffer;
+
+ /* current buffer */
+ struct stk1160_buffer *buf;
+};
+
+struct stk1160_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+};
+
+struct stk1160 {
+ struct v4l2_device v4l2_dev;
+ struct video_device vdev;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ struct device *dev;
+ struct usb_device *udev;
+
+ /* saa7115 subdev */
+ struct v4l2_subdev *sd_saa7115;
+
+ /* isoc control struct */
+ struct list_head avail_bufs;
+
+ /* video capture */
+ struct vb2_queue vb_vidq;
+
+ /* max packet size of isoc transaction */
+ int max_pkt_size;
+ /* array of wMaxPacketSize */
+ unsigned int *alt_max_pkt_size;
+ /* alternate */
+ int alt;
+ /* Number of alternative settings */
+ int num_alt;
+
+ struct stk1160_isoc_ctl isoc_ctl;
+ char urb_buf[255]; /* urb control msg buffer */
+
+ /* frame properties */
+ int width; /* current frame width */
+ int height; /* current frame height */
+ unsigned int ctl_input; /* selected input */
+ v4l2_std_id norm; /* current norm */
+ struct stk1160_fmt *fmt; /* selected format */
+
+ unsigned int field_count; /* not sure ??? */
+ enum v4l2_field field; /* also not sure :/ */
+
+ /* i2c i/o */
+ struct i2c_adapter i2c_adap;
+ struct i2c_client i2c_client;
+
+ struct mutex v4l_lock;
+ struct mutex vb_queue_lock;
+ spinlock_t buf_lock;
+
+ struct file *fh_owner; /* filehandle ownership */
+
+ /* EXPERIMENTAL */
+ struct snd_card *snd_card;
+};
+
+struct regval {
+ u16 reg;
+ u16 val;
+};
+
+/* Provided by stk1160-v4l.c */
+int stk1160_vb2_setup(struct stk1160 *dev);
+int stk1160_video_register(struct stk1160 *dev);
+void stk1160_video_unregister(struct stk1160 *dev);
+void stk1160_clear_queue(struct stk1160 *dev);
+
+/* Provided by stk1160-video.c */
+int stk1160_alloc_isoc(struct stk1160 *dev);
+void stk1160_free_isoc(struct stk1160 *dev);
+void stk1160_cancel_isoc(struct stk1160 *dev);
+void stk1160_uninit_isoc(struct stk1160 *dev);
+
+/* Provided by stk1160-i2c.c */
+int stk1160_i2c_register(struct stk1160 *dev);
+int stk1160_i2c_unregister(struct stk1160 *dev);
+
+/* Provided by stk1160-core.c */
+int stk1160_read_reg(struct stk1160 *dev, u16 reg, u8 *value);
+int stk1160_write_reg(struct stk1160 *dev, u16 reg, u16 value);
+int stk1160_write_regs_req(struct stk1160 *dev, u8 req, u16 reg,
+ char *buf, int len);
+int stk1160_read_reg_req_len(struct stk1160 *dev, u8 req, u16 reg,
+ char *buf, int len);
+void stk1160_select_input(struct stk1160 *dev);
+
+/* Provided by stk1160-ac97.c */
+#ifdef CONFIG_VIDEO_STK1160_AC97
+int stk1160_ac97_register(struct stk1160 *dev);
+int stk1160_ac97_unregister(struct stk1160 *dev);
+#else
+static inline int stk1160_ac97_register(struct stk1160 *dev) { return 0; }
+static inline int stk1160_ac97_unregister(struct stk1160 *dev) { return 0; }
+#endif
+
diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig
new file mode 100644
index 000000000000..a6a00aa4fce6
--- /dev/null
+++ b/drivers/media/usb/stkwebcam/Kconfig
@@ -0,0 +1,13 @@
+config USB_STKWEBCAM
+ tristate "USB Syntek DC1125 Camera support"
+ depends on VIDEO_V4L2
+ ---help---
+ Say Y here if you want to use this type of camera.
+ Supported devices are typically found in some Asus laptops,
+ with USB id 174f:a311 and 05e1:0501. Other Syntek cameras
+ may be supported by the stk11xx driver, from which this is
+ derived, see <http://sourceforge.net/projects/syntekdriver/>
+
+ To compile this driver as a module, choose M here: the
+ module will be called stkwebcam.
+
diff --git a/drivers/media/usb/stkwebcam/Makefile b/drivers/media/usb/stkwebcam/Makefile
new file mode 100644
index 000000000000..20ef8a4b990c
--- /dev/null
+++ b/drivers/media/usb/stkwebcam/Makefile
@@ -0,0 +1,4 @@
+stkwebcam-objs := stk-webcam.o stk-sensor.o
+
+obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
+
diff --git a/drivers/media/video/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c
index e546b014d7ad..e546b014d7ad 100644
--- a/drivers/media/video/stk-sensor.c
+++ b/drivers/media/usb/stkwebcam/stk-sensor.c
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c
index 86a0fc56c330..86a0fc56c330 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/usb/stkwebcam/stk-webcam.c
diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h
index 9f6736637571..9f6736637571 100644
--- a/drivers/media/video/stk-webcam.h
+++ b/drivers/media/usb/stkwebcam/stk-webcam.h
diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/usb/tlg2300/Kconfig
index 645d915267e6..645d915267e6 100644
--- a/drivers/media/video/tlg2300/Kconfig
+++ b/drivers/media/usb/tlg2300/Kconfig
diff --git a/drivers/media/usb/tlg2300/Makefile b/drivers/media/usb/tlg2300/Makefile
new file mode 100644
index 000000000000..137f8e38cdec
--- /dev/null
+++ b/drivers/media/usb/tlg2300/Makefile
@@ -0,0 +1,9 @@
+poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
+
+obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+
diff --git a/drivers/media/usb/tlg2300/pd-alsa.c b/drivers/media/usb/tlg2300/pd-alsa.c
new file mode 100644
index 000000000000..3f3e141f70fb
--- /dev/null
+++ b/drivers/media/usb/tlg2300/pd-alsa.c
@@ -0,0 +1,336 @@
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/init.h>
+#include <linux/sound.h>
+#include <linux/spinlock.h>
+#include <linux/soundcard.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-common.h>
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static void complete_handler_audio(struct urb *urb);
+#define AUDIO_EP (0x83)
+#define AUDIO_BUF_SIZE (512)
+#define PERIOD_SIZE (1024 * 8)
+#define PERIOD_MIN (4)
+#define PERIOD_MAX PERIOD_MIN
+
+static struct snd_pcm_hardware snd_pd_hw_capture = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
+ .period_bytes_min = PERIOD_SIZE,
+ .period_bytes_max = PERIOD_SIZE,
+ .periods_min = PERIOD_MIN,
+ .periods_max = PERIOD_MAX,
+ /*
+ .buffer_bytes_max = 62720 * 8,
+ .period_bytes_min = 64,
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98
+ */
+};
+
+static int snd_pd_capture_open(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ if (!p)
+ return -ENODEV;
+ pa->users++;
+ pa->card_close = 0;
+ pa->capture_pcm_substream = substream;
+ runtime->private_data = p;
+
+ runtime->hw = snd_pd_hw_capture;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ usb_autopm_get_interface(p->interface);
+ kref_get(&p->kref);
+ return 0;
+}
+
+static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ pa->users--;
+ pa->card_close = 1;
+ usb_autopm_put_interface(p->interface);
+ kref_put(&p->kref, poseidon_delete);
+ return 0;
+}
+
+static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ unsigned int size;
+
+ size = params_buffer_bytes(hw_params);
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+ vfree(runtime->dma_area);
+ }
+ runtime->dma_area = vmalloc(size);
+ if (!runtime->dma_area)
+ return -ENOMEM;
+ else
+ runtime->dma_bytes = size;
+ return 0;
+}
+
+static int audio_buf_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ int i;
+
+ for (i = 0; i < AUDIO_BUFS; i++)
+ if (pa->urb_array[i])
+ usb_kill_urb(pa->urb_array[i]);
+ free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
+ logpm();
+ return 0;
+}
+
+static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+
+ logpm();
+ audio_buf_free(p);
+ return 0;
+}
+
+static int snd_pd_prepare(struct snd_pcm_substream *substream)
+{
+ return 0;
+}
+
+#define AUDIO_TRAILER_SIZE (16)
+static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
+
+ int stride = runtime->frame_bits >> 3;
+ int len = urb->actual_length / stride;
+ unsigned char *cp = urb->transfer_buffer;
+ unsigned int oldptr = pa->rcv_position;
+
+ if (urb->actual_length == AUDIO_BUF_SIZE - 4)
+ len -= (AUDIO_TRAILER_SIZE / stride);
+
+ /* do the copy */
+ if (oldptr + len >= runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - oldptr;
+
+ memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
+ memcpy(runtime->dma_area, (cp + cnt * stride),
+ (len * stride - cnt * stride));
+ } else
+ memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+
+ /* update the statas */
+ snd_pcm_stream_lock(pa->capture_pcm_substream);
+ pa->rcv_position += len;
+ if (pa->rcv_position >= runtime->buffer_size)
+ pa->rcv_position -= runtime->buffer_size;
+
+ pa->copied_position += (len);
+ if (pa->copied_position >= runtime->period_size) {
+ pa->copied_position -= runtime->period_size;
+ *period_elapsed = 1;
+ }
+ snd_pcm_stream_unlock(pa->capture_pcm_substream);
+}
+
+static void complete_handler_audio(struct urb *urb)
+{
+ struct poseidon_audio *pa = urb->context;
+ struct snd_pcm_substream *substream = pa->capture_pcm_substream;
+ int period_elapsed = 0;
+ int ret;
+
+ if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
+ return;
+
+ if (urb->status != 0) {
+ /*if (urb->status == -ESHUTDOWN)*/
+ return;
+ }
+
+ if (substream) {
+ if (urb->actual_length) {
+ handle_audio_data(urb, &period_elapsed);
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+ }
+ }
+
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret < 0)
+ log("audio urb failed (errcod = %i)", ret);
+ return;
+}
+
+static int fire_audio_urb(struct poseidon *p)
+{
+ int i, ret = 0;
+ struct poseidon_audio *pa = &p->audio;
+
+ alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
+ p->udev, AUDIO_EP,
+ AUDIO_BUF_SIZE, GFP_ATOMIC,
+ complete_handler_audio, pa);
+
+ for (i = 0; i < AUDIO_BUFS; i++) {
+ ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("urb err : %d", ret);
+ }
+ log();
+ return ret;
+}
+
+static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+
+ if (debug_mode)
+ log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ if (pa->capture_stream == STREAM_ON)
+ return 0;
+
+ pa->rcv_position = pa->copied_position = 0;
+ pa->capture_stream = STREAM_ON;
+
+ if (in_hibernation(p))
+ return 0;
+ fire_audio_urb(p);
+ return 0;
+
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ pa->capture_stream = STREAM_SUSPEND;
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ pa->capture_stream = STREAM_OFF;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static snd_pcm_uframes_t
+snd_pd_capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct poseidon *p = snd_pcm_substream_chip(substream);
+ struct poseidon_audio *pa = &p->audio;
+ return pa->rcv_position;
+}
+
+static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+ return vmalloc_to_page(pageptr);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = snd_pd_capture_open,
+ .close = snd_pd_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_pd_hw_capture_params,
+ .hw_free = snd_pd_hw_capture_free,
+ .prepare = snd_pd_prepare,
+ .trigger = snd_pd_capture_trigger,
+ .pointer = snd_pd_capture_pointer,
+ .page = snd_pcm_pd_get_page,
+};
+
+#ifdef CONFIG_PM
+int pm_alsa_suspend(struct poseidon *p)
+{
+ logpm(p);
+ audio_buf_free(p);
+ return 0;
+}
+
+int pm_alsa_resume(struct poseidon *p)
+{
+ logpm(p);
+ fire_audio_urb(p);
+ return 0;
+}
+#endif
+
+int poseidon_audio_init(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+ struct snd_card *card;
+ struct snd_pcm *pcm;
+ int ret;
+
+ ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
+ if (ret != 0)
+ return ret;
+
+ ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
+ if (ret < 0) {
+ snd_card_free(card);
+ return ret;
+ }
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+ pcm->info_flags = 0;
+ pcm->private_data = p;
+ strcpy(pcm->name, "poseidon audio capture");
+
+ strcpy(card->driver, "ALSA driver");
+ strcpy(card->shortname, "poseidon Audio");
+ strcpy(card->longname, "poseidon ALSA Audio");
+
+ if (snd_card_register(card)) {
+ snd_card_free(card);
+ return -ENOMEM;
+ }
+ pa->card = card;
+ return 0;
+}
+
+int poseidon_audio_free(struct poseidon *p)
+{
+ struct poseidon_audio *pa = &p->audio;
+
+ if (pa->card)
+ snd_card_free(pa->card);
+ return 0;
+}
diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/usb/tlg2300/pd-common.h
index 5dd73b7857d1..5dd73b7857d1 100644
--- a/drivers/media/video/tlg2300/pd-common.h
+++ b/drivers/media/usb/tlg2300/pd-common.h
diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/usb/tlg2300/pd-dvb.c
index 30fcb117e898..30fcb117e898 100644
--- a/drivers/media/video/tlg2300/pd-dvb.c
+++ b/drivers/media/usb/tlg2300/pd-dvb.c
diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c
index 7b1f6ebd0e2c..7b1f6ebd0e2c 100644
--- a/drivers/media/video/tlg2300/pd-main.c
+++ b/drivers/media/usb/tlg2300/pd-main.c
diff --git a/drivers/media/usb/tlg2300/pd-radio.c b/drivers/media/usb/tlg2300/pd-radio.c
new file mode 100644
index 000000000000..25eeb166aa0b
--- /dev/null
+++ b/drivers/media/usb/tlg2300/pd-radio.c
@@ -0,0 +1,421 @@
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <media/v4l2-dev.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/sched.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+static int set_frequency(struct poseidon *p, __u32 frequency);
+static int poseidon_fm_close(struct file *filp);
+static int poseidon_fm_open(struct file *filp);
+
+#define TUNER_FREQ_MIN_FM 76000000
+#define TUNER_FREQ_MAX_FM 108000000
+
+#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
+static int preemphasis[MAX_PREEMPHASIS] = {
+ TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
+ TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
+ TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
+};
+
+static int poseidon_check_mode_radio(struct poseidon *p)
+{
+ int ret;
+ u32 status;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+ ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
+ if (ret < 0)
+ goto out;
+
+ ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
+ if (ret != 0)
+ goto out;
+
+ ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+ ret |= send_set_req(p, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
+ ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &status);
+ ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
+out:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_fm_suspend(struct poseidon *p)
+{
+ logpm(p);
+ pm_alsa_suspend(p);
+ usb_set_interface(p->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int pm_fm_resume(struct poseidon *p)
+{
+ logpm(p);
+ poseidon_check_mode_radio(p);
+ set_frequency(p, p->radio_data.fm_freq);
+ pm_alsa_resume(p);
+ return 0;
+}
+#endif
+
+static int poseidon_fm_open(struct file *filp)
+{
+ struct video_device *vfd = video_devdata(filp);
+ struct poseidon *p = video_get_drvdata(vfd);
+ int ret = 0;
+
+ if (!p)
+ return -1;
+
+ mutex_lock(&p->lock);
+ if (p->state & POSEIDON_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (p->state && !(p->state & POSEIDON_STATE_FM)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ usb_autopm_get_interface(p->interface);
+ if (0 == p->state) {
+ /* default pre-emphasis */
+ if (p->radio_data.pre_emphasis == 0)
+ p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
+ set_debug_mode(vfd, debug_mode);
+
+ ret = poseidon_check_mode_radio(p);
+ if (ret < 0) {
+ usb_autopm_put_interface(p->interface);
+ goto out;
+ }
+ p->state |= POSEIDON_STATE_FM;
+ }
+ p->radio_data.users++;
+ kref_get(&p->kref);
+ filp->private_data = p;
+out:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+static int poseidon_fm_close(struct file *filp)
+{
+ struct poseidon *p = filp->private_data;
+ struct radio_data *fm = &p->radio_data;
+ uint32_t status;
+
+ mutex_lock(&p->lock);
+ fm->users--;
+ if (0 == fm->users)
+ p->state &= ~POSEIDON_STATE_FM;
+
+ if (fm->is_radio_streaming && filp == p->file_for_stream) {
+ fm->is_radio_streaming = 0;
+ send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
+ }
+ usb_autopm_put_interface(p->interface);
+ mutex_unlock(&p->lock);
+
+ kref_put(&p->kref, poseidon_delete);
+ filp->private_data = NULL;
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct poseidon *p = file->private_data;
+
+ strlcpy(v->driver, "tele-radio", sizeof(v->driver));
+ strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
+ usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+}
+
+static const struct v4l2_file_operations poseidon_fm_fops = {
+ .owner = THIS_MODULE,
+ .open = poseidon_fm_open,
+ .release = poseidon_fm_close,
+ .ioctl = video_ioctl2,
+};
+
+static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct tuner_fm_sig_stat_s fm_stat = {};
+ int ret, status, count = 5;
+ struct poseidon *p = file->private_data;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ vt->type = V4L2_TUNER_RADIO;
+ vt->capability = V4L2_TUNER_CAP_STEREO;
+ vt->rangelow = TUNER_FREQ_MIN_FM / 62500;
+ vt->rangehigh = TUNER_FREQ_MAX_FM / 62500;
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ vt->audmode = V4L2_TUNER_MODE_STEREO;
+ vt->signal = 0;
+ vt->afc = 0;
+
+ mutex_lock(&p->lock);
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+
+ while (fm_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
+ &fm_stat, &status, sizeof(fm_stat));
+ }
+ mutex_unlock(&p->lock);
+
+ if (ret || status) {
+ vt->signal = 0;
+ } else if ((fm_stat.sig_present || fm_stat.sig_locked)
+ && fm_stat.sig_strength == 0) {
+ vt->signal = 0xffff;
+ } else
+ vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
+
+ return 0;
+}
+
+static int fm_get_freq(struct file *file, void *priv,
+ struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ argp->frequency = p->radio_data.fm_freq;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *p, __u32 frequency)
+{
+ __u32 freq ;
+ int ret, status;
+
+ mutex_lock(&p->lock);
+
+ ret = send_set_req(p, TUNER_AUD_ANA_STD,
+ p->radio_data.pre_emphasis, &status);
+
+ freq = (frequency * 125) * 500 / 1000;/* kHZ */
+ if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
+ if (ret < 0)
+ goto error ;
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ if (!p->radio_data.is_radio_streaming) {
+ ret = send_set_req(p, TAKE_REQUEST, 0, &status);
+ ret = send_set_req(p, PLAY_SERVICE,
+ TLG_TUNE_PLAY_SVC_START, &status);
+ p->radio_data.is_radio_streaming = 1;
+ }
+ p->radio_data.fm_freq = frequency;
+error:
+ mutex_unlock(&p->lock);
+ return ret;
+}
+
+static int fm_set_freq(struct file *file, void *priv,
+ struct v4l2_frequency *argp)
+{
+ struct poseidon *p = file->private_data;
+
+ p->file_for_stream = file;
+#ifdef CONFIG_PM
+ p->pm_suspend = pm_fm_suspend;
+ p->pm_resume = pm_fm_resume;
+#endif
+ return set_frequency(p, argp->frequency);
+}
+
+static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *arg)
+{
+ return 0;
+}
+
+static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ struct poseidon *p = file->private_data;
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (i < MAX_PREEMPHASIS)
+ ctrl->value = p->radio_data.pre_emphasis;
+ }
+ return 0;
+}
+
+static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh,
+ struct v4l2_ext_controls *ctrls)
+{
+ int i;
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
+ return -EINVAL;
+
+ for (i = 0; i < ctrls->count; i++) {
+ struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
+ continue;
+
+ if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) {
+ struct poseidon *p = file->private_data;
+ int pre_emphasis = preemphasis[ctrl->value];
+ u32 status;
+
+ send_set_req(p, TUNER_AUD_ANA_STD,
+ pre_emphasis, &status);
+ p->radio_data.pre_emphasis = pre_emphasis;
+ }
+ }
+ return 0;
+}
+
+static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ return 0;
+}
+
+static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL))
+ return -EINVAL;
+
+ ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
+ if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) {
+ /* return the next supported control */
+ ctrl->id = V4L2_CID_TUNE_PREEMPHASIS;
+ v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED,
+ V4L2_PREEMPHASIS_75_uS, 1,
+ V4L2_PREEMPHASIS_50_uS);
+ ctrl->flags = V4L2_CTRL_FLAG_UPDATE;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int tlg_fm_vidioc_querymenu(struct file *file, void *fh,
+ struct v4l2_querymenu *qmenu)
+{
+ return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ return vt->index > 0 ? -EINVAL : 0;
+}
+static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *va)
+{
+ return (va->index != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->mode = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int vidioc_s_input(struct file *filp, void *priv, u32 i)
+{
+ return (i != 0) ? -EINVAL : 0;
+}
+
+static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
+{
+ return (*i != 0) ? -EINVAL : 0;
+}
+
+static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = tlg_fm_vidioc_queryctrl,
+ .vidioc_querymenu = tlg_fm_vidioc_querymenu,
+ .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl,
+ .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl,
+ .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl,
+ .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
+ .vidioc_g_frequency = fm_get_freq,
+ .vidioc_s_frequency = fm_set_freq,
+};
+
+static struct video_device poseidon_fm_template = {
+ .name = "Telegent-Radio",
+ .fops = &poseidon_fm_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .ioctl_ops = &poseidon_fm_ioctl_ops,
+};
+
+int poseidon_fm_init(struct poseidon *p)
+{
+ struct video_device *fm_dev;
+
+ fm_dev = vdev_init(p, &poseidon_fm_template);
+ if (fm_dev == NULL)
+ return -1;
+
+ if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
+ video_device_release(fm_dev);
+ return -1;
+ }
+ p->radio_data.fm_dev = fm_dev;
+ return 0;
+}
+
+int poseidon_fm_exit(struct poseidon *p)
+{
+ destroy_video_device(&p->radio_data.fm_dev);
+ return 0;
+}
diff --git a/drivers/media/usb/tlg2300/pd-video.c b/drivers/media/usb/tlg2300/pd-video.c
new file mode 100644
index 000000000000..1f448ac7a496
--- /dev/null
+++ b/drivers/media/usb/tlg2300/pd-video.c
@@ -0,0 +1,1668 @@
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <linux/usb.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+
+#include "pd-common.h"
+#include "vendorcmds.h"
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd);
+static int pm_video_resume(struct poseidon *pd);
+#endif
+static void iso_bubble_handler(struct work_struct *w);
+
+static int usb_transfer_mode;
+module_param(usb_transfer_mode, int, 0644);
+MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
+
+static const struct poseidon_format poseidon_formats[] = {
+ { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
+ { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
+};
+
+static const struct poseidon_tvnorm poseidon_tvnorms[] = {
+ { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D },
+ { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B },
+ { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G },
+ { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H },
+ { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I },
+ { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M },
+ { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
+ { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
+ { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
+ { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
+ { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
+ { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
+ { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
+ { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
+ { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
+ { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
+ { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
+};
+static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
+
+struct pd_audio_mode {
+ u32 tlg_audio_mode;
+ u32 v4l2_audio_sub;
+ u32 v4l2_audio_mode;
+};
+
+static const struct pd_audio_mode pd_audio_modes[] = {
+ { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
+ V4L2_TUNER_MODE_MONO },
+ { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
+ V4L2_TUNER_MODE_STEREO },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
+ V4L2_TUNER_MODE_LANG2 },
+ { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
+ V4L2_TUNER_MODE_LANG1_LANG2 }
+};
+static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
+
+struct pd_input {
+ char *name;
+ uint32_t tlg_src;
+};
+
+static const struct pd_input pd_inputs[] = {
+ { "TV Antenna", TLG_SIG_SRC_ANTENNA },
+ { "TV Cable", TLG_SIG_SRC_CABLE },
+ { "TV SVideo", TLG_SIG_SRC_SVIDEO },
+ { "TV Composite", TLG_SIG_SRC_COMPOSITE }
+};
+static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
+
+struct poseidon_control {
+ struct v4l2_queryctrl v4l2_ctrl;
+ enum cmd_custom_param_id vc_id;
+};
+
+static struct poseidon_control controls[] = {
+ {
+ { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
+ "brightness", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_BRIGHTNESS_CTRL
+ }, {
+ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
+ "contrast", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_CONTRAST_CTRL,
+ }, {
+ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
+ "hue", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_HUE_CTRL,
+ }, {
+ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
+ "saturation", 0, 10000, 1, 100, 0, },
+ CUST_PARM_ID_SATURATION_CTRL,
+ },
+};
+
+struct video_std_to_audio_std {
+ v4l2_std_id video_std;
+ int audio_std;
+};
+
+static const struct video_std_to_audio_std video_to_audio_map[] = {
+ /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
+ 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
+ { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
+ V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
+
+ /* country : { 1, 52, 54, 55, 886 } */
+ {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
+
+ /* country : { 81 } */
+ { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
+
+ /* other country : TLG_TUNE_ASTD_A2 */
+};
+static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
+
+static int get_audio_std(v4l2_std_id v4l2_std)
+{
+ int i = 0;
+
+ for (; i < map_size; i++) {
+ if (v4l2_std & video_to_audio_map[i].video_std)
+ return video_to_audio_map[i].audio_std;
+ }
+ return TLG_TUNE_ASTD_A2;
+}
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct front_face *front = fh;
+ struct poseidon *p = front->pd;
+
+ logs(front);
+
+ strcpy(cap->driver, "tele-video");
+ strcpy(cap->card, "Telegent Poseidon");
+ usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
+ V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
+ return 0;
+}
+
+/*====================================================================*/
+static void init_copy(struct video_data *video, bool index)
+{
+ struct front_face *front = video->front;
+
+ video->field_count = index;
+ video->lines_copied = 0;
+ video->prev_left = 0 ;
+ video->dst = (char *)videobuf_to_vmalloc(front->curr_frame)
+ + index * video->lines_size;
+ video->vbi->copied = 0; /* set it here */
+}
+
+static bool get_frame(struct front_face *front, int *need_init)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb)
+ return true;
+
+ spin_lock(&front->queue_lock);
+ if (!list_empty(&front->active)) {
+ vb = list_entry(front->active.next,
+ struct videobuf_buffer, queue);
+ if (need_init)
+ *need_init = 1;
+ front->curr_frame = vb;
+ list_del_init(&vb->queue);
+ }
+ spin_unlock(&front->queue_lock);
+
+ return !!vb;
+}
+
+/* check if the video's buffer is ready */
+static bool get_video_frame(struct front_face *front, struct video_data *video)
+{
+ int need_init = 0;
+ bool ret = true;
+
+ ret = get_frame(front, &need_init);
+ if (ret && need_init)
+ init_copy(video, 0);
+ return ret;
+}
+
+static void submit_frame(struct front_face *front)
+{
+ struct videobuf_buffer *vb = front->curr_frame;
+
+ if (vb == NULL)
+ return;
+
+ front->curr_frame = NULL;
+ vb->state = VIDEOBUF_DONE;
+ vb->field_count++;
+ do_gettimeofday(&vb->ts);
+
+ wake_up(&vb->done);
+}
+
+/*
+ * A frame is composed of two fields. If we receive all the two fields,
+ * call the submit_frame() to submit the whole frame to applications.
+ */
+static void end_field(struct video_data *video)
+{
+ /* logs(video->front); */
+ if (1 == video->field_count)
+ submit_frame(video->front);
+ else
+ init_copy(video, 1);
+}
+
+static void copy_video_data(struct video_data *video, char *src,
+ unsigned int count)
+{
+#define copy_data(len) \
+ do { \
+ if (++video->lines_copied > video->lines_per_field) \
+ goto overflow; \
+ memcpy(video->dst, src, len);\
+ video->dst += len + video->lines_size; \
+ src += len; \
+ count -= len; \
+ } while (0)
+
+ while (count && count >= video->lines_size) {
+ if (video->prev_left) {
+ copy_data(video->prev_left);
+ video->prev_left = 0;
+ continue;
+ }
+ copy_data(video->lines_size);
+ }
+ if (count && count < video->lines_size) {
+ memcpy(video->dst, src, count);
+
+ video->prev_left = video->lines_size - count;
+ video->dst += count;
+ }
+ return;
+
+overflow:
+ end_field(video);
+}
+
+static void check_trailer(struct video_data *video, char *src, int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ int offset; /* trailer's offset */
+ char *buf;
+
+ offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
+ - (vbi->copied + video->lines_size * video->lines_copied);
+ if (video->prev_left)
+ offset -= (video->lines_size - video->prev_left);
+
+ if (offset > count || offset <= 0)
+ goto short_package;
+
+ buf = src + offset;
+
+ /* trailer : (VFHS) + U32 + U32 + field_num */
+ if (!strncmp(buf, "VFHS", 4)) {
+ int field_num = *((u32 *)(buf + 12));
+
+ if ((field_num & 1) ^ video->field_count) {
+ init_copy(video, video->field_count);
+ return;
+ }
+ copy_video_data(video, src, offset);
+ }
+short_package:
+ end_field(video);
+}
+
+/* ========== Check this more carefully! =========== */
+static inline void copy_vbi_data(struct vbi_data *vbi,
+ char *src, unsigned int count)
+{
+ struct front_face *front = vbi->front;
+
+ if (front && get_frame(front, NULL)) {
+ char *buf = videobuf_to_vmalloc(front->curr_frame);
+
+ if (vbi->video->field_count)
+ buf += (vbi->vbi_size / 2);
+ memcpy(buf + vbi->copied, src, count);
+ }
+ vbi->copied += count;
+}
+
+/*
+ * Copy the normal data (VBI or VIDEO) without the trailer.
+ * VBI is not interlaced, while VIDEO is interlaced.
+ */
+static inline void copy_vbi_video_data(struct video_data *video,
+ char *src, unsigned int count)
+{
+ struct vbi_data *vbi = video->vbi;
+ unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
+
+ if (vbi_delta >= count) {
+ copy_vbi_data(vbi, src, count);
+ } else {
+ if (vbi_delta) {
+ copy_vbi_data(vbi, src, vbi_delta);
+
+ /* we receive the two fields of the VBI*/
+ if (vbi->front && video->field_count)
+ submit_frame(vbi->front);
+ }
+ copy_video_data(video, src + vbi_delta, count - vbi_delta);
+ }
+}
+
+static void urb_complete_bulk(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ char *src = (char *)urb->transfer_buffer;
+ int count = urb->actual_length;
+ int ret = 0;
+
+ if (!video->is_streaming || urb->status) {
+ if (urb->status == -EPROTO)
+ goto resend_it;
+ return;
+ }
+ if (!get_video_frame(front, video))
+ goto resend_it;
+
+ if (count == urb->transfer_buffer_length)
+ copy_vbi_video_data(video, src, count);
+ else
+ check_trailer(video, src, count);
+
+resend_it:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log(" submit failed: error %d", ret);
+}
+
+/************************* for ISO *********************/
+#define GET_SUCCESS (0)
+#define GET_TRAILER (1)
+#define GET_TOO_MUCH_BUBBLE (2)
+#define GET_NONE (3)
+static int get_chunk(int start, struct urb *urb,
+ int *head, int *tail, int *bubble_err)
+{
+ struct usb_iso_packet_descriptor *pkt = NULL;
+ int ret = GET_SUCCESS;
+
+ for (*head = *tail = -1; start < urb->number_of_packets; start++) {
+ pkt = &urb->iso_frame_desc[start];
+
+ /* handle the bubble of the Hub */
+ if (-EOVERFLOW == pkt->status) {
+ if (++*bubble_err > urb->number_of_packets / 3)
+ return GET_TOO_MUCH_BUBBLE;
+ continue;
+ }
+
+ /* This is the gap */
+ if (pkt->status || pkt->actual_length <= 0
+ || pkt->actual_length > ISO_PKT_SIZE) {
+ if (*head != -1)
+ break;
+ continue;
+ }
+
+ /* a good isochronous packet */
+ if (pkt->actual_length == ISO_PKT_SIZE) {
+ if (*head == -1)
+ *head = start;
+ *tail = start;
+ continue;
+ }
+
+ /* trailer is here */
+ if (pkt->actual_length < ISO_PKT_SIZE) {
+ if (*head == -1) {
+ *head = start;
+ *tail = start;
+ return GET_TRAILER;
+ }
+ break;
+ }
+ }
+
+ if (*head == -1 && *tail == -1)
+ ret = GET_NONE;
+ return ret;
+}
+
+/*
+ * |__|------|___|-----|_______|
+ * ^ ^
+ * | |
+ * gap gap
+ */
+static void urb_complete_iso(struct urb *urb)
+{
+ struct front_face *front = urb->context;
+ struct video_data *video = &front->pd->video_data;
+ int bubble_err = 0, head = 0, tail = 0;
+ char *src = (char *)urb->transfer_buffer;
+ int ret = 0;
+
+ if (!video->is_streaming)
+ return;
+
+ do {
+ if (!get_video_frame(front, video))
+ goto out;
+
+ switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
+ case GET_SUCCESS:
+ copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
+ (tail - head + 1) * ISO_PKT_SIZE);
+ break;
+ case GET_TRAILER:
+ check_trailer(video, src + (head * ISO_PKT_SIZE),
+ ISO_PKT_SIZE);
+ break;
+ case GET_NONE:
+ goto out;
+ case GET_TOO_MUCH_BUBBLE:
+ log("\t We got too much bubble");
+ schedule_work(&video->bubble_work);
+ return;
+ }
+ } while (head = tail + 1, head < urb->number_of_packets);
+
+out:
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret)
+ log("usb_submit_urb err : %d", ret);
+}
+/*============================= [ end ] =====================*/
+
+static int prepare_iso_urb(struct video_data *video)
+{
+ struct usb_device *udev = video->pd->udev;
+ int i;
+
+ if (video->urb_array[0])
+ return 0;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ struct urb *urb;
+ void *mem;
+ int j;
+
+ urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
+ if (urb == NULL)
+ goto out;
+
+ video->urb_array[i] = urb;
+ mem = usb_alloc_coherent(udev,
+ ISO_PKT_SIZE * PK_PER_URB,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ urb->complete = urb_complete_iso; /* handler */
+ urb->dev = udev;
+ urb->context = video->front;
+ urb->pipe = usb_rcvisocpipe(udev,
+ video->endpoint_addr);
+ urb->interval = 1;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->number_of_packets = PK_PER_URB;
+ urb->transfer_buffer = mem;
+ urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
+
+ for (j = 0; j < PK_PER_URB; j++) {
+ urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
+ urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
+ }
+ }
+ return 0;
+out:
+ for (; i > 0; i--)
+ ;
+ return -ENOMEM;
+}
+
+/* return the succeeded number of the allocation */
+int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
+ struct usb_device *udev, u8 ep_addr,
+ int buf_size, gfp_t gfp_flags,
+ usb_complete_t complete_fn, void *context)
+{
+ int i = 0;
+
+ for (; i < num; i++) {
+ void *mem;
+ struct urb *urb = usb_alloc_urb(0, gfp_flags);
+ if (urb == NULL)
+ return i;
+
+ mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
+ &urb->transfer_dma);
+ if (mem == NULL) {
+ usb_free_urb(urb);
+ return i;
+ }
+
+ usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
+ mem, buf_size, complete_fn, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb_array[i] = urb;
+ }
+ return i;
+}
+
+void free_all_urb_generic(struct urb **urb_array, int num)
+{
+ int i;
+ struct urb *urb;
+
+ for (i = 0; i < num; i++) {
+ urb = urb_array[i];
+ if (urb) {
+ usb_free_coherent(urb->dev,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer,
+ urb->transfer_dma);
+ usb_free_urb(urb);
+ urb_array[i] = NULL;
+ }
+ }
+}
+
+static int prepare_bulk_urb(struct video_data *video)
+{
+ if (video->urb_array[0])
+ return 0;
+
+ alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
+ video->pd->udev, video->endpoint_addr,
+ 0x2000, GFP_KERNEL,
+ urb_complete_bulk, video->front);
+ return 0;
+}
+
+/* free the URBs */
+static void free_all_urb(struct video_data *video)
+{
+ free_all_urb_generic(video->urb_array, SBUF_NUM);
+}
+
+static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ videobuf_vmalloc_free(vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct front_face *front = q->priv_data;
+ vb->state = VIDEOBUF_QUEUED;
+ list_add_tail(&vb->queue, &front->active);
+}
+
+static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct front_face *front = q->priv_data;
+ int rc;
+
+ switch (front->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ struct v4l2_pix_format *pix;
+
+ pix = &front->pd->video_data.context.pix;
+ vb->size = pix->sizeimage; /* real frame size */
+ vb->width = pix->width;
+ vb->height = pix->height;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ vb->size = front->pd->vbi_data.vbi_size;
+ rc = videobuf_iolock(q, vb, NULL);
+ if (rc < 0)
+ return rc;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ vb->field = field;
+ vb->state = VIDEOBUF_PREPARED;
+ return 0;
+}
+
+static int fire_all_urb(struct video_data *video)
+{
+ int i, ret;
+
+ video->is_streaming = 1;
+
+ for (i = 0; i < SBUF_NUM; i++) {
+ ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
+ if (ret)
+ log("(%d) failed: error %d", i, ret);
+ }
+ return ret;
+}
+
+static int start_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ s32 cmd_status;
+
+ send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
+
+ if (pd->cur_transfer_mode) {
+ prepare_iso_urb(video);
+ INIT_WORK(&video->bubble_work, iso_bubble_handler);
+ } else {
+ /* The bulk mode does not need a bubble handler */
+ prepare_bulk_urb(video);
+ }
+ fire_all_urb(video);
+ return 0;
+}
+
+static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct front_face *front = q->priv_data;
+ struct poseidon *pd = front->pd;
+
+ switch (front->type) {
+ default:
+ return -EINVAL;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ struct video_data *video = &pd->video_data;
+ struct v4l2_pix_format *pix = &video->context.pix;
+
+ *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
+ if (*count < 4)
+ *count = 4;
+ if (1) {
+ /* same in different altersetting */
+ video->endpoint_addr = 0x82;
+ video->vbi = &pd->vbi_data;
+ video->vbi->video = video;
+ video->pd = pd;
+ video->lines_per_field = pix->height / 2;
+ video->lines_size = pix->width * 2;
+ video->front = front;
+ }
+ return start_video_stream(pd);
+ }
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ *size = PAGE_ALIGN(vbi->vbi_size);
+ log("size : %d", *size);
+ if (*count == 0)
+ *count = 4;
+ }
+ break;
+ }
+ return 0;
+}
+
+static struct videobuf_queue_ops pd_video_qops = {
+ .buf_setup = pd_buf_setup,
+ .buf_prepare = pd_buf_prepare,
+ .buf_queue = pd_buf_queue,
+ .buf_release = pd_buf_release,
+};
+
+static int vidioc_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (ARRAY_SIZE(poseidon_formats) <= f->index)
+ return -EINVAL;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = 0;
+ f->pixelformat = poseidon_formats[f->index].fourcc;
+ strcpy(f->description, poseidon_formats[f->index].name);
+ return 0;
+}
+
+static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ f->fmt.pix = pd->video_data.context.pix;
+ return 0;
+}
+
+static int vidioc_try_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ return 0;
+}
+
+/*
+ * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
+ * Mplayer calls them in the reverse order.
+ */
+static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
+{
+ struct video_data *video = &pd->video_data;
+ struct running_context *context = &video->context;
+ struct v4l2_pix_format *pix_def = &context->pix;
+ s32 ret = 0, cmd_status = 0, vid_resol;
+
+ /* set the pixel format to firmware */
+ if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
+ vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
+ } else {
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+ vid_resol = TLG_TUNER_VID_FORMAT_YUV;
+ }
+ ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ vid_resol, &cmd_status);
+
+ /* set the resolution to firmware */
+ vid_resol = TLG_TUNE_VID_RES_720;
+ switch (pix->width) {
+ case 704:
+ vid_resol = TLG_TUNE_VID_RES_704;
+ break;
+ default:
+ pix->width = 720;
+ case 720:
+ break;
+ }
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ vid_resol, &cmd_status);
+ if (ret || cmd_status)
+ return -EBUSY;
+
+ pix_def->pixelformat = pix->pixelformat; /* save it */
+ pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576;
+
+ /* Compare with the default setting */
+ if ((pix_def->width != pix->width)
+ || (pix_def->height != pix->height)) {
+ pix_def->width = pix->width;
+ pix_def->height = pix->height;
+ pix_def->bytesperline = pix->width * 2;
+ pix_def->sizeimage = pix->width * pix->height * 2;
+ }
+ *pix = *pix_def;
+
+ return 0;
+}
+
+static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+ /* stop VBI here */
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ if (pd->file_for_stream == NULL)
+ pd->file_for_stream = file;
+ else if (file != pd->file_for_stream) {
+ mutex_unlock(&pd->lock);
+ return -EINVAL;
+ }
+
+ pd_vidioc_s_fmt(pd, &f->fmt.pix);
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+static int vidioc_g_fmt_vbi(struct file *file, void *fh,
+ struct v4l2_format *v4l2_f)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
+
+ vbi_fmt->samples_per_line = 720 * 2;
+ vbi_fmt->sampling_rate = 6750000 * 4;
+ vbi_fmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbi_fmt->offset = 64 * 4; /*FIXME: why offset */
+ if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
+ vbi_fmt->start[0] = 10;
+ vbi_fmt->start[1] = 264;
+ vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
+ vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
+ } else {
+ vbi_fmt->start[0] = 6;
+ vbi_fmt->start[1] = 314;
+ vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
+ vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
+ }
+ vbi_fmt->flags = V4L2_VBI_UNSYNC;
+ logs(front);
+ return 0;
+}
+
+static int set_std(struct poseidon *pd, v4l2_std_id *norm)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ struct running_context *context;
+ struct v4l2_pix_format *pix;
+ s32 i, ret = 0, cmd_status, param;
+ int height;
+
+ for (i = 0; i < POSEIDON_TVNORMS; i++) {
+ if (*norm & poseidon_tvnorms[i].v4l2_id) {
+ param = poseidon_tvnorms[i].tlg_tvnorm;
+ log("name : %s", poseidon_tvnorms[i].name);
+ goto found;
+ }
+ }
+ return -EINVAL;
+found:
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
+ if (ret || cmd_status)
+ goto out;
+
+ /* Set vbi size and check the height of the frame */
+ context = &video->context;
+ context->tvnormid = poseidon_tvnorms[i].v4l2_id;
+ if (context->tvnormid & V4L2_STD_525_60) {
+ vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
+ height = 480;
+ } else {
+ vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
+ height = 576;
+ }
+
+ pix = &context->pix;
+ if (pix->height != height) {
+ pix->height = height;
+ pix->sizeimage = pix->width * pix->height * 2;
+ }
+
+out:
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct front_face *front = fh;
+ logs(front);
+ return set_std(front->pd, norm);
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
+{
+ struct front_face *front = fh;
+
+ if (in->index < 0 || in->index >= POSEIDON_INPUTS)
+ return -EINVAL;
+ strcpy(in->name, pd_inputs[in->index].name);
+ in->type = V4L2_INPUT_TYPE_TUNER;
+
+ /*
+ * the audio input index mixed with this video input,
+ * Poseidon only have one audio/video, set to "0"
+ */
+ in->audioset = 0;
+ in->tuner = 0;
+ in->std = V4L2_STD_ALL;
+ in->status = 0;
+ logs(front);
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ logs(front);
+ *i = context->sig_index;
+ return 0;
+}
+
+/* We can support several inputs */
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret, cmd_status;
+
+ if (i < 0 || i >= POSEIDON_INPUTS)
+ return -EINVAL;
+ ret = send_set_req(pd, SGNL_SRC_SEL,
+ pd_inputs[i].tlg_src, &cmd_status);
+ if (ret)
+ return ret;
+
+ pd->video_data.context.sig_index = i;
+ return 0;
+}
+
+static struct poseidon_control *check_control_id(__u32 id)
+{
+ struct poseidon_control *control = &controls[0];
+ int array_size = ARRAY_SIZE(controls);
+
+ for (; control < &controls[array_size]; control++)
+ if (control->v4l2_ctrl.id == id)
+ return control;
+ return NULL;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct poseidon_control *control = NULL;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ *a = control->v4l2_ctrl;
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct poseidon_control *control = NULL;
+ struct tuner_custom_parameter_s tuner_param;
+ s32 ret = 0, cmd_status;
+
+ control = check_control_id(ctrl->id);
+ if (!control)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id,
+ &tuner_param, &cmd_status, sizeof(tuner_param));
+ mutex_unlock(&pd->lock);
+
+ if (ret || cmd_status)
+ return -1;
+
+ ctrl->value = tuner_param.param_value;
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
+{
+ struct tuner_custom_parameter_s param = {0};
+ struct poseidon_control *control = NULL;
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ s32 ret = 0, cmd_status, params;
+
+ control = check_control_id(a->id);
+ if (!control)
+ return -EINVAL;
+
+ param.param_value = a->value;
+ param.param_id = control->vc_id;
+ params = *(s32 *)&param; /* temp code */
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+ mutex_unlock(&pd->lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ return ret;
+}
+
+/* Audio ioctls */
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (0 != a->index)
+ return -EINVAL;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ /*Poseidon have no AVL function.*/
+ a->mode = 0;
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ a->index = 0;
+ a->capability = V4L2_AUDCAP_STEREO;
+ strcpy(a->name, "USB audio in");
+ a->mode = 0;
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a)
+{
+ return (0 == a->index) ? 0 : -EINVAL;
+}
+
+/* Tuner ioctls */
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct tuner_atv_sig_stat_s atv_stat;
+ s32 count = 5, ret, cmd_status;
+ int index;
+
+ if (0 != tuner->index)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+
+ while (atv_stat.sig_lock_busy && count-- && !ret) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
+ &atv_stat, &cmd_status, sizeof(atv_stat));
+ }
+ mutex_unlock(&pd->lock);
+
+ if (debug_mode)
+ log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
+
+ if (ret || cmd_status)
+ tuner->signal = 0;
+ else if (atv_stat.sig_present && !atv_stat.sig_strength)
+ tuner->signal = 0xFFFF;
+ else
+ tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
+
+ strcpy(tuner->name, "Telegent Systems");
+ tuner->type = V4L2_TUNER_ANALOG_TV;
+ tuner->rangelow = TUNER_FREQ_MIN / 62500;
+ tuner->rangehigh = TUNER_FREQ_MAX / 62500;
+ tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ index = pd->video_data.context.audio_idx;
+ tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
+ tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
+ tuner->afc = 0;
+ logs(front);
+ return 0;
+}
+
+static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
+{
+ s32 ret = 0, cmd_status, param, audiomode;
+
+ mutex_lock(&pd->lock);
+ param = pd_audio_modes[index].tlg_audio_mode;
+ ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
+ &cmd_status);
+ if (!ret)
+ pd->video_data.context.audio_idx = index;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ int index;
+
+ if (0 != a->index)
+ return -EINVAL;
+ logs(front);
+ for (index = 0; index < POSEIDON_AUDIOMODS; index++)
+ if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
+ return pd_vidioc_s_tuner(pd, index);
+ return -EINVAL;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+ struct running_context *context = &pd->video_data.context;
+
+ if (0 != freq->tuner)
+ return -EINVAL;
+ freq->frequency = context->freq;
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ return 0;
+}
+
+static int set_frequency(struct poseidon *pd, __u32 frequency)
+{
+ s32 ret = 0, param, cmd_status;
+ struct running_context *context = &pd->video_data.context;
+
+ param = frequency * 62500 / 1000;
+ if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000)
+ return -EINVAL;
+
+ mutex_lock(&pd->lock);
+ ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
+ ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
+
+ msleep(250); /* wait for a while until the hardware is ready. */
+ context->freq = frequency;
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh,
+ struct v4l2_frequency *freq)
+{
+ struct front_face *front = fh;
+ struct poseidon *pd = front->pd;
+
+ logs(front);
+#ifdef CONFIG_PM
+ pd->pm_suspend = pm_video_suspend;
+ pd->pm_resume = pm_video_resume;
+#endif
+ return set_frequency(pd, freq->frequency);
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_reqbufs(&front->q, b);
+}
+
+static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ logs(front);
+ return videobuf_querybuf(&front->q, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_qbuf(&front->q, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
+}
+
+/* Just stop the URBs, do not free the URBs */
+static int usb_transfer_stop(struct video_data *video)
+{
+ if (video->is_streaming) {
+ int i;
+ s32 cmd_status;
+ struct poseidon *pd = video->pd;
+
+ video->is_streaming = 0;
+ for (i = 0; i < SBUF_NUM; ++i) {
+ if (video->urb_array[i])
+ usb_kill_urb(video->urb_array[i]);
+ }
+
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+ }
+ return 0;
+}
+
+int stop_all_video_stream(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ mutex_lock(&pd->lock);
+ if (video->is_streaming) {
+ struct front_face *front = video->front;
+
+ /* stop the URBs */
+ usb_transfer_stop(video);
+ free_all_urb(video);
+
+ /* stop the host side of VIDEO */
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ /* stop the host side of VBI */
+ front = vbi->front;
+ if (front) {
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+ }
+ }
+ mutex_unlock(&pd->lock);
+ return 0;
+}
+
+/*
+ * The bubbles can seriously damage the video's quality,
+ * though it occurs in very rare situation.
+ */
+static void iso_bubble_handler(struct work_struct *w)
+{
+ struct video_data *video;
+ struct poseidon *pd;
+
+ video = container_of(w, struct video_data, bubble_work);
+ pd = video->pd;
+
+ mutex_lock(&pd->lock);
+ usb_transfer_stop(video);
+ msleep(500);
+ start_video_stream(pd);
+ mutex_unlock(&pd->lock);
+}
+
+
+static int vidioc_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = fh;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamon(&front->q);
+}
+
+static int vidioc_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct front_face *front = file->private_data;
+
+ logs(front);
+ if (unlikely(type != front->type))
+ return -EINVAL;
+ return videobuf_streamoff(&front->q);
+}
+
+/* Set the firmware's default values : need altersetting */
+static int pd_video_checkmode(struct poseidon *pd)
+{
+ s32 ret = 0, cmd_status, audiomode;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/2);
+
+ /* choose the altersetting */
+ ret = usb_set_interface(pd->udev, 0,
+ (pd->cur_transfer_mode ?
+ ISO_3K_BULK_ALTERNATE_IFACE :
+ BULK_ALTERNATE_IFACE));
+ if (ret < 0)
+ goto error;
+
+ /* set default parameters for PAL-D , with the VBI enabled*/
+ ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
+ ret |= send_set_req(pd, SGNL_SRC_SEL,
+ TLG_SIG_SRC_ANTENNA, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STD_SEL,
+ TLG_TUNE_VSTD_PAL_D, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
+ TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
+ ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
+ TLG_TUNE_VID_RES_720, &cmd_status);
+ ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
+ ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
+
+ /* set the audio */
+ audiomode = get_audio_std(pd->video_data.context.tvnormid);
+ ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
+ ret |= send_set_req(pd, TUNER_AUD_MODE,
+ TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
+ ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
+ ATV_AUDIO_RATE_48K, &cmd_status);
+error:
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int pm_video_suspend(struct poseidon *pd)
+{
+ /* stop audio */
+ pm_alsa_suspend(pd);
+
+ /* stop and free all the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* reset the interface */
+ usb_set_interface(pd->udev, 0, 0);
+ msleep(300);
+ return 0;
+}
+
+static int restore_v4l2_context(struct poseidon *pd,
+ struct running_context *context)
+{
+ struct front_face *front = pd->video_data.front;
+
+ pd_video_checkmode(pd);
+
+ set_std(pd, &context->tvnormid);
+ vidioc_s_input(NULL, front, context->sig_index);
+ pd_vidioc_s_tuner(pd, context->audio_idx);
+ pd_vidioc_s_fmt(pd, &context->pix);
+ set_frequency(pd, context->freq);
+ return 0;
+}
+
+static int pm_video_resume(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+
+ /* resume the video */
+ /* [1] restore the origin V4L2 parameters */
+ restore_v4l2_context(pd, &video->context);
+
+ /* [2] initiate video copy variables */
+ if (video->front->curr_frame)
+ init_copy(video, 0);
+
+ /* [3] fire urbs */
+ start_video_stream(pd);
+
+ /* resume the audio */
+ pm_alsa_resume(pd);
+ return 0;
+}
+#endif
+
+void set_debug_mode(struct video_device *vfd, int debug_mode)
+{
+ vfd->debug = 0;
+ if (debug_mode & 0x1)
+ vfd->debug = V4L2_DEBUG_IOCTL;
+ if (debug_mode & 0x2)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+}
+
+static void init_video_context(struct running_context *context)
+{
+ context->sig_index = 0;
+ context->audio_idx = 1; /* stereo */
+ context->tvnormid = V4L2_STD_PAL_D;
+ context->pix = (struct v4l2_pix_format) {
+ .width = 720,
+ .height = 576,
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 576 * 2,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .priv = 0
+ };
+}
+
+static int pd_video_open(struct file *file)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct poseidon *pd = video_get_drvdata(vfd);
+ struct front_face *front = NULL;
+ int ret = -ENOMEM;
+
+ mutex_lock(&pd->lock);
+ usb_autopm_get_interface(pd->interface);
+
+ if (vfd->vfl_type == VFL_TYPE_GRABBER
+ && !(pd->state & POSEIDON_STATE_ANALOG)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */
+ init_video_context(&pd->video_data.context);
+
+ ret = pd_video_checkmode(pd);
+ if (ret < 0) {
+ kfree(front);
+ ret = -1;
+ goto out;
+ }
+
+ pd->state |= POSEIDON_STATE_ANALOG;
+ front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ pd->video_data.users++;
+ set_debug_mode(vfd, debug_mode);
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,/* video is interlacd */
+ sizeof(struct videobuf_buffer),/*it's enough*/
+ front, NULL);
+ } else if (vfd->vfl_type == VFL_TYPE_VBI
+ && !(pd->state & POSEIDON_STATE_VBI)) {
+ front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
+ if (!front)
+ goto out;
+
+ pd->state |= POSEIDON_STATE_VBI;
+ front->type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ pd->vbi_data.front = front;
+ pd->vbi_data.users++;
+
+ videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
+ NULL, &front->queue_lock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_NONE, /* vbi is NONE mode */
+ sizeof(struct videobuf_buffer),
+ front, NULL);
+ } else {
+ /* maybe add FM support here */
+ log("other ");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ front->pd = pd;
+ front->curr_frame = NULL;
+ INIT_LIST_HEAD(&front->active);
+ spin_lock_init(&front->queue_lock);
+
+ file->private_data = front;
+ kref_get(&pd->kref);
+
+ mutex_unlock(&pd->lock);
+ return 0;
+out:
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+ return ret;
+}
+
+static int pd_video_release(struct file *file)
+{
+ struct front_face *front = file->private_data;
+ struct poseidon *pd = front->pd;
+ s32 cmd_status = 0;
+
+ logs(front);
+ mutex_lock(&pd->lock);
+
+ if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_ANALOG;
+
+ /* stop the device, and free the URBs */
+ usb_transfer_stop(&pd->video_data);
+ free_all_urb(&pd->video_data);
+
+ /* stop the firmware */
+ send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
+ &cmd_status);
+
+ pd->file_for_stream = NULL;
+ pd->video_data.users--;
+ } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ pd->state &= ~POSEIDON_STATE_VBI;
+ pd->vbi_data.front = NULL;
+ pd->vbi_data.users--;
+ }
+ videobuf_stop(&front->q);
+ videobuf_mmap_free(&front->q);
+
+ usb_autopm_put_interface(pd->interface);
+ mutex_unlock(&pd->lock);
+
+ kfree(front);
+ file->private_data = NULL;
+ kref_put(&pd->kref, poseidon_delete);
+ return 0;
+}
+
+static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_mmap_mapper(&front->q, vma);
+}
+
+static unsigned int pd_video_poll(struct file *file, poll_table *table)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_poll_stream(file, &front->q, table);
+}
+
+static ssize_t pd_video_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct front_face *front = file->private_data;
+ return videobuf_read_stream(&front->q, buffer, count, ppos,
+ 0, file->f_flags & O_NONBLOCK);
+}
+
+/* This struct works for both VIDEO and VBI */
+static const struct v4l2_file_operations pd_video_fops = {
+ .owner = THIS_MODULE,
+ .open = pd_video_open,
+ .release = pd_video_release,
+ .read = pd_video_read,
+ .poll = pd_video_poll,
+ .mmap = pd_video_mmap,
+ .ioctl = video_ioctl2, /* maybe changed in future */
+};
+
+static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ /* Video format */
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
+
+ /* Input */
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_enum_input = vidioc_enum_input,
+
+ /* Audio ioctls */
+ .vidioc_enumaudio = vidioc_enumaudio,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+
+ /* Tuner ioctls */
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+
+ /* Buffer handlers */
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ /* Stream on/off */
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+
+ /* Control handling */
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+};
+
+static struct video_device pd_video_template = {
+ .name = "Telegent-Video",
+ .fops = &pd_video_fops,
+ .minor = -1,
+ .release = video_device_release,
+ .tvnorms = V4L2_STD_ALL,
+ .ioctl_ops = &pd_video_ioctl_ops,
+};
+
+struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (vfd == NULL)
+ return NULL;
+ *vfd = *tmp;
+ vfd->minor = -1;
+ vfd->v4l2_dev = &pd->v4l2_dev;
+ /*vfd->parent = &(pd->udev->dev); */
+ vfd->release = video_device_release;
+ video_set_drvdata(vfd, pd);
+ return vfd;
+}
+
+void destroy_video_device(struct video_device **v_dev)
+{
+ struct video_device *dev = *v_dev;
+
+ if (dev == NULL)
+ return;
+
+ if (video_is_registered(dev))
+ video_unregister_device(dev);
+ else
+ video_device_release(dev);
+ *v_dev = NULL;
+}
+
+void pd_video_exit(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+
+ destroy_video_device(&video->v_dev);
+ destroy_video_device(&vbi->v_dev);
+ log();
+}
+
+int pd_video_init(struct poseidon *pd)
+{
+ struct video_data *video = &pd->video_data;
+ struct vbi_data *vbi = &pd->vbi_data;
+ int ret = -ENOMEM;
+
+ video->v_dev = vdev_init(pd, &pd_video_template);
+ if (video->v_dev == NULL)
+ goto out;
+
+ ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1);
+ if (ret != 0)
+ goto out;
+
+ /* VBI uses the same template as video */
+ vbi->v_dev = vdev_init(pd, &pd_video_template);
+ if (vbi->v_dev == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1);
+ if (ret != 0)
+ goto out;
+ log("register VIDEO/VBI devices");
+ return 0;
+out:
+ log("VIDEO/VBI devices register failed, : %d", ret);
+ pd_video_exit(pd);
+ return ret;
+}
+
diff --git a/drivers/media/video/tlg2300/vendorcmds.h b/drivers/media/usb/tlg2300/vendorcmds.h
index ba6f4ae3b2c2..ba6f4ae3b2c2 100644
--- a/drivers/media/video/tlg2300/vendorcmds.h
+++ b/drivers/media/usb/tlg2300/vendorcmds.h
diff --git a/drivers/media/video/tm6000/Kconfig b/drivers/media/usb/tm6000/Kconfig
index a43b77abd931..a43b77abd931 100644
--- a/drivers/media/video/tm6000/Kconfig
+++ b/drivers/media/usb/tm6000/Kconfig
diff --git a/drivers/media/usb/tm6000/Makefile b/drivers/media/usb/tm6000/Makefile
new file mode 100644
index 000000000000..f2644933b8d1
--- /dev/null
+++ b/drivers/media/usb/tm6000/Makefile
@@ -0,0 +1,15 @@
+tm6000-y := tm6000-cards.o \
+ tm6000-core.o \
+ tm6000-i2c.o \
+ tm6000-video.o \
+ tm6000-stds.o \
+ tm6000-input.o
+
+obj-$(CONFIG_VIDEO_TM6000) += tm6000.o
+obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o
+obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
+ccflags-y += -Idrivers/media/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
diff --git a/drivers/media/usb/tm6000/tm6000-alsa.c b/drivers/media/usb/tm6000/tm6000-alsa.c
new file mode 100644
index 000000000000..813c1ec53608
--- /dev/null
+++ b/drivers/media/usb/tm6000/tm6000-alsa.c
@@ -0,0 +1,529 @@
+/*
+ *
+ * Support for audio capture for tm5600/6000/6010
+ * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on cx88-alsa.c
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+#undef dprintk
+
+#define dprintk(level, fmt, arg...) do { \
+ if (debug >= level) \
+ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
+ } while (0)
+
+/****************************************************************************
+ Module global static vars
+ ****************************************************************************/
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
+
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
+
+
+/****************************************************************************
+ Module macros
+ ****************************************************************************/
+
+MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
+ "{{Trident,tm6000},"
+ "{{Trident,tm6010}");
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "enable debug messages");
+
+/****************************************************************************
+ Module specific funtions
+ ****************************************************************************/
+
+/*
+ * BOARD Specific: Sets audio DMA
+ */
+
+static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
+{
+ struct tm6000_core *core = chip->core;
+
+ dprintk(1, "Starting audio DMA\n");
+
+ /* Enables audio */
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
+
+ tm6000_set_audio_bitrate(core, 48000);
+
+ return 0;
+}
+
+/*
+ * BOARD Specific: Resets audio DMA
+ */
+static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
+{
+ struct tm6000_core *core = chip->core;
+
+ dprintk(1, "Stopping audio DMA\n");
+
+ /* Disables audio */
+ tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
+
+ return 0;
+}
+
+static void dsp_buffer_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ dprintk(2, "Freeing buffer\n");
+
+ vfree(substream->runtime->dma_area);
+ substream->runtime->dma_area = NULL;
+ substream->runtime->dma_bytes = 0;
+}
+
+static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ dprintk(2, "Allocating buffer\n");
+
+ if (substream->runtime->dma_area) {
+ if (substream->runtime->dma_bytes > size)
+ return 0;
+
+ dsp_buffer_free(substream);
+ }
+
+ substream->runtime->dma_area = vmalloc(size);
+ if (!substream->runtime->dma_area)
+ return -ENOMEM;
+
+ substream->runtime->dma_bytes = size;
+
+ return 0;
+}
+
+
+/****************************************************************************
+ ALSA PCM Interface
+ ****************************************************************************/
+
+/*
+ * Digital hardware definition
+ */
+#define DEFAULT_FIFO_SIZE 4096
+
+static struct snd_pcm_hardware snd_tm6000_digital_hw = {
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .period_bytes_min = 64,
+ .period_bytes_max = 12544,
+ .periods_min = 2,
+ .periods_max = 98,
+ .buffer_bytes_max = 62720 * 8,
+};
+
+/*
+ * audio pcm capture open callback
+ */
+static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ err = snd_pcm_hw_constraint_pow2(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (err < 0)
+ goto _error;
+
+ chip->substream = substream;
+
+ runtime->hw = snd_tm6000_digital_hw;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+ return 0;
+_error:
+ dprintk(1, "Error opening PCM!\n");
+ return err;
+}
+
+/*
+ * audio close callback
+ */
+static int snd_tm6000_close(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+
+ if (atomic_read(&core->stream_started) > 0) {
+ atomic_set(&core->stream_started, 0);
+ schedule_work(&core->wq_trigger);
+ }
+
+ return 0;
+}
+
+static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
+{
+ struct snd_tm6000_card *chip = core->adev;
+ struct snd_pcm_substream *substream = chip->substream;
+ struct snd_pcm_runtime *runtime;
+ int period_elapsed = 0;
+ unsigned int stride, buf_pos;
+ int length;
+
+ if (atomic_read(&core->stream_started) == 0)
+ return 0;
+
+ if (!size || !substream) {
+ dprintk(1, "substream was NULL\n");
+ return -EINVAL;
+ }
+
+ runtime = substream->runtime;
+ if (!runtime || !runtime->dma_area) {
+ dprintk(1, "runtime was NULL\n");
+ return -EINVAL;
+ }
+
+ buf_pos = chip->buf_pos;
+ stride = runtime->frame_bits >> 3;
+
+ if (stride == 0) {
+ dprintk(1, "stride is zero\n");
+ return -EINVAL;
+ }
+
+ length = size / stride;
+ if (length == 0) {
+ dprintk(1, "%s: length was zero\n", __func__);
+ return -EINVAL;
+ }
+
+ dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
+ runtime->dma_area, buf_pos,
+ (unsigned int)runtime->buffer_size, stride);
+
+ if (buf_pos + length >= runtime->buffer_size) {
+ unsigned int cnt = runtime->buffer_size - buf_pos;
+ memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
+ memcpy(runtime->dma_area, buf + cnt * stride,
+ length * stride - cnt * stride);
+ } else
+ memcpy(runtime->dma_area + buf_pos * stride, buf,
+ length * stride);
+
+ snd_pcm_stream_lock(substream);
+
+ chip->buf_pos += length;
+ if (chip->buf_pos >= runtime->buffer_size)
+ chip->buf_pos -= runtime->buffer_size;
+
+ chip->period_pos += length;
+ if (chip->period_pos >= runtime->period_size) {
+ chip->period_pos -= runtime->period_size;
+ period_elapsed = 1;
+ }
+
+ snd_pcm_stream_unlock(substream);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(substream);
+
+ return 0;
+}
+
+/*
+ * hw_params callback
+ */
+static int snd_tm6000_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ int size, rc;
+
+ size = params_period_bytes(hw_params) * params_periods(hw_params);
+
+ rc = dsp_buffer_alloc(substream, size);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+/*
+ * hw free callback
+ */
+static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+
+ if (atomic_read(&core->stream_started) > 0) {
+ atomic_set(&core->stream_started, 0);
+ schedule_work(&core->wq_trigger);
+ }
+
+ dsp_buffer_free(substream);
+ return 0;
+}
+
+/*
+ * prepare callback
+ */
+static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ chip->buf_pos = 0;
+ chip->period_pos = 0;
+
+ return 0;
+}
+
+
+/*
+ * trigger callback
+ */
+static void audio_trigger(struct work_struct *work)
+{
+ struct tm6000_core *core = container_of(work, struct tm6000_core,
+ wq_trigger);
+ struct snd_tm6000_card *chip = core->adev;
+
+ if (atomic_read(&core->stream_started)) {
+ dprintk(1, "starting capture");
+ _tm6000_start_audio_dma(chip);
+ } else {
+ dprintk(1, "stopping capture");
+ _tm6000_stop_audio_dma(chip);
+ }
+}
+
+static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+ struct tm6000_core *core = chip->core;
+ int err = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
+ case SNDRV_PCM_TRIGGER_START:
+ atomic_set(&core->stream_started, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
+ case SNDRV_PCM_TRIGGER_STOP:
+ atomic_set(&core->stream_started, 0);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ schedule_work(&core->wq_trigger);
+
+ return err;
+}
+/*
+ * pointer callback
+ */
+static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
+
+ return chip->buf_pos;
+}
+
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
+/*
+ * operators
+ */
+static struct snd_pcm_ops snd_tm6000_pcm_ops = {
+ .open = snd_tm6000_pcm_open,
+ .close = snd_tm6000_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_tm6000_hw_params,
+ .hw_free = snd_tm6000_hw_free,
+ .prepare = snd_tm6000_prepare,
+ .trigger = snd_tm6000_card_trigger,
+ .pointer = snd_tm6000_pointer,
+ .page = snd_pcm_get_vmalloc_page,
+};
+
+/*
+ * create a PCM device
+ */
+
+/* FIXME: Control interface - How to control volume/mute? */
+
+/****************************************************************************
+ Basic Flow for Sound Devices
+ ****************************************************************************/
+
+/*
+ * Alsa Constructor - Component probe
+ */
+static int tm6000_audio_init(struct tm6000_core *dev)
+{
+ struct snd_card *card;
+ struct snd_tm6000_card *chip;
+ int rc;
+ static int devnr;
+ char component[14];
+ struct snd_pcm *pcm;
+
+ if (!dev)
+ return 0;
+
+ if (devnr >= SNDRV_CARDS)
+ return -ENODEV;
+
+ if (!enable[devnr])
+ return -ENOENT;
+
+ rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card);
+ if (rc < 0) {
+ snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
+ return rc;
+ }
+ strcpy(card->driver, "tm6000-alsa");
+ strcpy(card->shortname, "TM5600/60x0");
+ sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
+ dev->udev->bus->busnum, dev->udev->devnum);
+
+ sprintf(component, "USB%04x:%04x",
+ le16_to_cpu(dev->udev->descriptor.idVendor),
+ le16_to_cpu(dev->udev->descriptor.idProduct));
+ snd_component_add(card, component);
+ snd_card_set_dev(card, &dev->udev->dev);
+
+ chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
+ if (!chip) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ chip->core = dev;
+ chip->card = card;
+ dev->adev = chip;
+ spin_lock_init(&chip->reg_lock);
+
+ rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
+ if (rc < 0)
+ goto error_chip;
+
+ pcm->info_flags = 0;
+ pcm->private_data = chip;
+ strcpy(pcm->name, "Trident TM5600/60x0");
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
+
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+ rc = snd_card_register(card);
+ if (rc < 0)
+ goto error_chip;
+
+ dprintk(1, "Registered audio driver for %s\n", card->longname);
+
+ return 0;
+
+error_chip:
+ kfree(chip);
+ dev->adev = NULL;
+error:
+ snd_card_free(card);
+ return rc;
+}
+
+static int tm6000_audio_fini(struct tm6000_core *dev)
+{
+ struct snd_tm6000_card *chip;
+
+ if (!dev)
+ return 0;
+ chip = dev->adev;
+
+ if (!chip)
+ return 0;
+
+ if (!chip->card)
+ return 0;
+
+ snd_card_free(chip->card);
+ chip->card = NULL;
+ kfree(chip);
+ dev->adev = NULL;
+
+ return 0;
+}
+
+static struct tm6000_ops audio_ops = {
+ .type = TM6000_AUDIO,
+ .name = "TM6000 Audio Extension",
+ .init = tm6000_audio_init,
+ .fini = tm6000_audio_fini,
+ .fillbuf = tm6000_fillbuf,
+};
+
+static int __init tm6000_alsa_register(void)
+{
+ return tm6000_register_extension(&audio_ops);
+}
+
+static void __exit tm6000_alsa_unregister(void)
+{
+ tm6000_unregister_extension(&audio_ops);
+}
+
+module_init(tm6000_alsa_register);
+module_exit(tm6000_alsa_unregister);
diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/usb/tm6000/tm6000-cards.c
index 034659b13174..034659b13174 100644
--- a/drivers/media/video/tm6000/tm6000-cards.c
+++ b/drivers/media/usb/tm6000/tm6000-cards.c
diff --git a/drivers/media/video/tm6000/tm6000-core.c b/drivers/media/usb/tm6000/tm6000-core.c
index 22cc0116deb6..22cc0116deb6 100644
--- a/drivers/media/video/tm6000/tm6000-core.c
+++ b/drivers/media/usb/tm6000/tm6000-core.c
diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/usb/tm6000/tm6000-dvb.c
index e1f3f66e1e63..e1f3f66e1e63 100644
--- a/drivers/media/video/tm6000/tm6000-dvb.c
+++ b/drivers/media/usb/tm6000/tm6000-dvb.c
diff --git a/drivers/media/video/tm6000/tm6000-i2c.c b/drivers/media/usb/tm6000/tm6000-i2c.c
index c7e23e3dd75e..c7e23e3dd75e 100644
--- a/drivers/media/video/tm6000/tm6000-i2c.c
+++ b/drivers/media/usb/tm6000/tm6000-i2c.c
diff --git a/drivers/media/usb/tm6000/tm6000-input.c b/drivers/media/usb/tm6000/tm6000-input.c
new file mode 100644
index 000000000000..dffbd4bd47b1
--- /dev/null
+++ b/drivers/media/usb/tm6000/tm6000-input.c
@@ -0,0 +1,499 @@
+/*
+ * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include <media/rc-core.h>
+
+#include "tm6000.h"
+#include "tm6000-regs.h"
+
+static unsigned int ir_debug;
+module_param(ir_debug, int, 0644);
+MODULE_PARM_DESC(ir_debug, "debug message level");
+
+static unsigned int enable_ir = 1;
+module_param(enable_ir, int, 0644);
+MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
+
+static unsigned int ir_clock_mhz = 12;
+module_param(ir_clock_mhz, int, 0644);
+MODULE_PARM_DESC(enable_ir, "ir clock, in MHz");
+
+#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */
+#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */
+
+#undef dprintk
+
+#define dprintk(level, fmt, arg...) do {\
+ if (ir_debug >= level) \
+ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
+ } while (0)
+
+struct tm6000_ir_poll_result {
+ u16 rc_data;
+};
+
+struct tm6000_IR {
+ struct tm6000_core *dev;
+ struct rc_dev *rc;
+ char name[32];
+ char phys[32];
+
+ /* poll expernal decoder */
+ int polling;
+ struct delayed_work work;
+ u8 wait:1;
+ u8 pwled:2;
+ u8 submit_urb:1;
+ u16 key_addr;
+ struct urb *int_urb;
+
+ /* IR device properties */
+ u64 rc_type;
+};
+
+void tm6000_ir_wait(struct tm6000_core *dev, u8 state)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!dev->ir)
+ return;
+
+ dprintk(2, "%s: %i\n",__func__, ir->wait);
+
+ if (state)
+ ir->wait = 1;
+ else
+ ir->wait = 0;
+}
+
+static int tm6000_ir_config(struct tm6000_IR *ir)
+{
+ struct tm6000_core *dev = ir->dev;
+ u32 pulse = 0, leader = 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ /*
+ * The IR decoder supports RC-5 or NEC, with a configurable timing.
+ * The timing configuration there is not that accurate, as it uses
+ * approximate values. The NEC spec mentions a 562.5 unit period,
+ * and RC-5 uses a 888.8 period.
+ * Currently, driver assumes a clock provided by a 12 MHz XTAL, but
+ * a modprobe parameter can adjust it.
+ * Adjustments are required for other timings.
+ * It seems that the 900ms timing for NEC is used to detect a RC-5
+ * IR, in order to discard such decoding
+ */
+
+ switch (ir->rc_type) {
+ case RC_TYPE_NEC:
+ leader = 900; /* ms */
+ pulse = 700; /* ms - the actual value would be 562 */
+ break;
+ default:
+ case RC_TYPE_RC5:
+ leader = 900; /* ms - from the NEC decoding */
+ pulse = 1780; /* ms - The actual value would be 1776 */
+ break;
+ }
+
+ pulse = ir_clock_mhz * pulse;
+ leader = ir_clock_mhz * leader;
+ if (ir->rc_type == RC_TYPE_NEC)
+ leader = leader | 0x8000;
+
+ dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n",
+ __func__,
+ (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5",
+ ir_clock_mhz, leader, pulse);
+
+ /* Remote WAKEUP = enable, normal mode, from IR decoder output */
+ tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
+
+ /* Enable IR reception on non-busrt mode */
+ tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f);
+
+ /* IR_WKUP_SEL = Low byte in decoded IR data */
+ tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff);
+ /* IR_WKU_ADD code */
+ tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse);
+
+ if (!ir->polling)
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
+ else
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1);
+ msleep(10);
+
+ /* Shows that IR is working via the LED */
+ tm6000_flash_led(dev, 0);
+ msleep(100);
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+
+ return 0;
+}
+
+static void tm6000_ir_urb_received(struct urb *urb)
+{
+ struct tm6000_core *dev = urb->context;
+ struct tm6000_IR *ir = dev->ir;
+ struct tm6000_ir_poll_result poll_result;
+ char *buf;
+
+ dprintk(2, "%s\n",__func__);
+ if (urb->status < 0 || urb->actual_length <= 0) {
+ printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n",
+ urb->status, urb->actual_length);
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ buf = urb->transfer_buffer;
+
+ if (ir_debug)
+ print_hex_dump(KERN_DEBUG, "tm6000: IR data: ",
+ DUMP_PREFIX_OFFSET,16, 1,
+ buf, urb->actual_length, false);
+
+ poll_result.rc_data = buf[0];
+ if (urb->actual_length > 1)
+ poll_result.rc_data |= buf[1] << 8;
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
+
+ usb_submit_urb(urb, GFP_ATOMIC);
+ /*
+ * Flash the led. We can't do it here, as it is running on IRQ context.
+ * So, use the scheduler to do it, in a few ms.
+ */
+ ir->pwled = 2;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(10));
+}
+
+static void tm6000_ir_handle_key(struct work_struct *work)
+{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
+ struct tm6000_core *dev = ir->dev;
+ struct tm6000_ir_poll_result poll_result;
+ int rc;
+ u8 buf[2];
+
+ if (ir->wait)
+ return;
+
+ dprintk(3, "%s\n",__func__);
+
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ REQ_02_GET_IR_CODE, 0, 0, buf, 2);
+ if (rc < 0)
+ return;
+
+ if (rc > 1)
+ poll_result.rc_data = buf[0] | buf[1] << 8;
+ else
+ poll_result.rc_data = buf[0];
+
+ /* Check if something was read */
+ if ((poll_result.rc_data & 0xff) == 0xff) {
+ if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+ }
+ return;
+ }
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
+ /* Re-schedule polling */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+}
+
+static void tm6000_ir_int_work(struct work_struct *work)
+{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
+ struct tm6000_core *dev = ir->dev;
+ int rc;
+
+ dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb,
+ ir->pwled);
+
+ if (ir->submit_urb) {
+ dprintk(3, "Resubmit urb\n");
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
+
+ rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC);
+ if (rc < 0) {
+ printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n",
+ rc);
+ /* Retry in 100 ms */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ ir->submit_urb = 0;
+ }
+
+ /* Led is enabled only if USB submit doesn't fail */
+ if (ir->pwled == 2) {
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY));
+ } else if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+ }
+}
+
+static int tm6000_ir_start(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ dprintk(2, "%s\n",__func__);
+
+ schedule_delayed_work(&ir->work, 0);
+
+ return 0;
+}
+
+static void tm6000_ir_stop(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ dprintk(2, "%s\n",__func__);
+
+ cancel_delayed_work_sync(&ir->work);
+}
+
+static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ if (!ir)
+ return 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
+ ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
+
+ ir->rc_type = rc_type;
+
+ tm6000_ir_config(ir);
+ /* TODO */
+ return 0;
+}
+
+static int __tm6000_ir_int_start(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+ struct tm6000_core *dev;
+ int pipe, size;
+ int err = -ENOMEM;
+
+ if (!ir)
+ return -ENODEV;
+ dev = ir->dev;
+
+ dprintk(2, "%s\n",__func__);
+
+ ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!ir->int_urb)
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(dev->udev,
+ dev->int_in.endp->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+
+ size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+ dprintk(1, "IR max size: %d\n", size);
+
+ ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC);
+ if (ir->int_urb->transfer_buffer == NULL) {
+ usb_free_urb(ir->int_urb);
+ return err;
+ }
+ dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval);
+
+ usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
+ ir->int_urb->transfer_buffer, size,
+ tm6000_ir_urb_received, dev,
+ dev->int_in.endp->desc.bInterval);
+
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+
+ return 0;
+}
+
+static void __tm6000_ir_int_stop(struct rc_dev *rc)
+{
+ struct tm6000_IR *ir = rc->priv;
+
+ if (!ir || !ir->int_urb)
+ return;
+
+ dprintk(2, "%s\n",__func__);
+
+ usb_kill_urb(ir->int_urb);
+ kfree(ir->int_urb->transfer_buffer);
+ usb_free_urb(ir->int_urb);
+ ir->int_urb = NULL;
+}
+
+int tm6000_ir_int_start(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir)
+ return 0;
+
+ return __tm6000_ir_int_start(ir->rc);
+}
+
+void tm6000_ir_int_stop(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir || !ir->rc)
+ return;
+
+ __tm6000_ir_int_stop(ir->rc);
+}
+
+int tm6000_ir_init(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir;
+ struct rc_dev *rc;
+ int err = -ENOMEM;
+
+ if (!enable_ir)
+ return -ENODEV;
+
+ if (!dev->caps.has_remote)
+ return 0;
+
+ if (!dev->ir_codes)
+ return 0;
+
+ ir = kzalloc(sizeof(*ir), GFP_ATOMIC);
+ rc = rc_allocate_device();
+ if (!ir || !rc)
+ goto out;
+
+ dprintk(2, "%s\n", __func__);
+
+ /* record handles to ourself */
+ ir->dev = dev;
+ dev->ir = ir;
+ ir->rc = rc;
+
+ /* input setup */
+ rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ /* Neded, in order to support NEC remotes with 24 or 32 bits */
+ rc->scanmask = 0xffff;
+ rc->priv = ir;
+ rc->change_protocol = tm6000_ir_change_protocol;
+ if (dev->int_in.endp) {
+ rc->open = __tm6000_ir_int_start;
+ rc->close = __tm6000_ir_int_stop;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work);
+ } else {
+ rc->open = tm6000_ir_start;
+ rc->close = tm6000_ir_stop;
+ ir->polling = 50;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key);
+ }
+ rc->driver_type = RC_DRIVER_SCANCODE;
+
+ snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)",
+ dev->name);
+
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+ tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
+
+ rc->input_name = ir->name;
+ rc->input_phys = ir->phys;
+ rc->input_id.bustype = BUS_USB;
+ rc->input_id.version = 1;
+ rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+ rc->map_name = dev->ir_codes;
+ rc->driver_name = "tm6000";
+ rc->dev.parent = &dev->udev->dev;
+
+ /* ir register */
+ err = rc_register_device(rc);
+ if (err)
+ goto out;
+
+ return 0;
+
+out:
+ dev->ir = NULL;
+ rc_free_device(rc);
+ kfree(ir);
+ return err;
+}
+
+int tm6000_ir_fini(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ /* skip detach on non attached board */
+
+ if (!ir)
+ return 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ if (!ir->polling)
+ __tm6000_ir_int_stop(ir->rc);
+
+ tm6000_ir_stop(ir->rc);
+
+ /* Turn off the led */
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
+ rc_unregister_device(ir->rc);
+
+ kfree(ir);
+ dev->ir = NULL;
+
+ return 0;
+}
diff --git a/drivers/media/video/tm6000/tm6000-regs.h b/drivers/media/usb/tm6000/tm6000-regs.h
index a38c251ed57b..a38c251ed57b 100644
--- a/drivers/media/video/tm6000/tm6000-regs.h
+++ b/drivers/media/usb/tm6000/tm6000-regs.h
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/usb/tm6000/tm6000-stds.c
index 5e28d6a2412f..5e28d6a2412f 100644
--- a/drivers/media/video/tm6000/tm6000-stds.c
+++ b/drivers/media/usb/tm6000/tm6000-stds.c
diff --git a/drivers/media/video/tm6000/tm6000-usb-isoc.h b/drivers/media/usb/tm6000/tm6000-usb-isoc.h
index 99d15a55aa03..99d15a55aa03 100644
--- a/drivers/media/video/tm6000/tm6000-usb-isoc.h
+++ b/drivers/media/usb/tm6000/tm6000-usb-isoc.h
diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c
new file mode 100644
index 000000000000..4342cd4f5c8a
--- /dev/null
+++ b/drivers/media/usb/tm6000/tm6000-video.c
@@ -0,0 +1,1852 @@
+/*
+ * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
+ *
+ * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
+ * - Fixed module load/unload
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tuner.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/highmem.h>
+#include <linux/freezer.h>
+
+#include "tm6000-regs.h"
+#include "tm6000.h"
+
+#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
+
+/* Limits minimum and default number of buffers */
+#define TM6000_MIN_BUF 4
+#define TM6000_DEF_BUF 8
+
+#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */
+
+/* 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[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 54,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 119,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 112,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -128,
+ .maximum = 127,
+ .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[] = {
+ {
+ .name = "4:2:2, packed, YVY2",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+ }, {
+ .name = "A/V + VBI mux packet",
+ .fourcc = V4L2_PIX_FMT_TM6000,
+ .depth = 16,
+ }
+};
+
+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
+ * ------------------------------------------------------------------
+ */
+
+#define norm_maxw(a) 720
+#define norm_maxh(a) 576
+
+#define norm_minw(a) norm_maxw(a)
+#define norm_minh(a) norm_maxh(a)
+
+/*
+ * video-buf generic routine to get the next available buffer
+ */
+static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
+ struct tm6000_buffer **buf)
+{
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+
+ if (list_empty(&dma_q->active)) {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
+ *buf = NULL;
+ return;
+ }
+
+ *buf = list_entry(dma_q->active.next,
+ struct tm6000_buffer, vb.queue);
+}
+
+/*
+ * Announces that a buffer were filled and request the next
+ */
+static inline void buffer_filled(struct tm6000_core *dev,
+ struct tm6000_dmaqueue *dma_q,
+ struct tm6000_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+/*
+ * Identify the tm5600/6000 buffer header type and properly handles
+ */
+static int copy_streams(u8 *data, unsigned long len,
+ struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ u8 *ptr = data, *endp = data+len;
+ unsigned long header = 0;
+ int rc = 0;
+ unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
+ struct tm6000_buffer *vbuf = NULL;
+ char *voutp = NULL;
+ unsigned int linewidth;
+
+ 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) {
+ /* Header */
+ if (dev->isoc_ctl.tmp_buf_len > 0) {
+ /* from last urb or packet */
+ header = dev->isoc_ctl.tmp_buf;
+ if (4 - dev->isoc_ctl.tmp_buf_len > 0) {
+ memcpy((u8 *)&header +
+ dev->isoc_ctl.tmp_buf_len,
+ ptr,
+ 4 - dev->isoc_ctl.tmp_buf_len);
+ ptr += 4 - dev->isoc_ctl.tmp_buf_len;
+ }
+ dev->isoc_ctl.tmp_buf_len = 0;
+ } else {
+ if (ptr + 3 >= endp) {
+ /* have incomplete header */
+ dev->isoc_ctl.tmp_buf_len = endp - ptr;
+ memcpy(&dev->isoc_ctl.tmp_buf, ptr,
+ dev->isoc_ctl.tmp_buf_len);
+ return rc;
+ }
+ /* Seek for sync */
+ for (; ptr < endp - 3; ptr++) {
+ if (*(ptr + 3) == 0x47)
+ break;
+ }
+ /* Get message header */
+ header = *(unsigned long *)ptr;
+ ptr += 4;
+ }
+
+ /* split the header fields */
+ size = ((header & 0x7e) << 1);
+ if (size > 0)
+ size -= 4;
+ block = (header >> 7) & 0xf;
+ field = (header >> 11) & 0x1;
+ line = (header >> 12) & 0x1ff;
+ cmd = (header >> 21) & 0x7;
+ /* Validates haeder fields */
+ if (size > TM6000_URB_MSG_LEN)
+ size = TM6000_URB_MSG_LEN;
+ pktsize = TM6000_URB_MSG_LEN;
+ /*
+ * calculate position in buffer and change the buffer
+ */
+ switch (cmd) {
+ case TM6000_URB_MSG_VIDEO:
+ 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,
+ "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;
+ }
+ break;
+ case TM6000_URB_MSG_VBI:
+ break;
+ case TM6000_URB_MSG_AUDIO:
+ case TM6000_URB_MSG_PTS:
+ size = pktsize; /* Size is always 180 bytes */
+ break;
+ }
+ } else {
+ /* Continue the last copy */
+ cmd = dev->isoc_ctl.cmd;
+ size = dev->isoc_ctl.size;
+ pos = dev->isoc_ctl.pos;
+ pktsize = dev->isoc_ctl.pktsize;
+ field = dev->isoc_ctl.field;
+ }
+ cpysize = (endp - ptr > size) ? size : endp - ptr;
+ if (cpysize) {
+ /* copy data in different buffers */
+ switch (cmd) {
+ case TM6000_URB_MSG_VIDEO:
+ /* Fills video buffer */
+ if (vbuf)
+ memcpy(&voutp[pos], ptr, cpysize);
+ break;
+ case TM6000_URB_MSG_AUDIO: {
+ int i;
+ for (i = 0; i < cpysize; i += 2)
+ swab16s((u16 *)(ptr + i));
+
+ tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize);
+ break;
+ }
+ case TM6000_URB_MSG_VBI:
+ /* Need some code to copy vbi buffer */
+ break;
+ case TM6000_URB_MSG_PTS: {
+ /* Need some code to copy pts */
+ u32 pts;
+ pts = *(u32 *)ptr;
+ dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x",
+ field, pts);
+ break;
+ }
+ }
+ }
+ if (ptr + pktsize > endp) {
+ /*
+ * End of URB packet, but cmd processing is not
+ * complete. Preserve the state for a next packet
+ */
+ dev->isoc_ctl.pos = pos + cpysize;
+ dev->isoc_ctl.size = size - cpysize;
+ dev->isoc_ctl.cmd = cmd;
+ dev->isoc_ctl.field = field;
+ dev->isoc_ctl.pktsize = pktsize - (endp - ptr);
+ ptr += endp - ptr;
+ } else {
+ dev->isoc_ctl.cmd = 0;
+ ptr += pktsize;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Identify the tm5600/6000 buffer header type and properly handles
+ */
+static int copy_multiplexed(u8 *ptr, unsigned long len,
+ struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ unsigned int pos = dev->isoc_ctl.pos, cpysize;
+ int rc = 1;
+ struct tm6000_buffer *buf;
+ char *outp = NULL;
+
+ get_next_buf(dma_q, &buf);
+ if (buf)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ if (!outp)
+ return 0;
+
+ while (len > 0) {
+ cpysize = min(len, buf->vb.size-pos);
+ memcpy(&outp[pos], ptr, cpysize);
+ pos += cpysize;
+ ptr += cpysize;
+ len -= cpysize;
+ if (pos >= buf->vb.size) {
+ pos = 0;
+ /* Announces that a new buffer were filled */
+ buffer_filled(dev, dma_q, buf);
+ dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
+ get_next_buf(dma_q, &buf);
+ if (!buf)
+ break;
+ outp = videobuf_to_vmalloc(&(buf->vb));
+ if (!outp)
+ return rc;
+ pos = 0;
+ }
+ }
+
+ dev->isoc_ctl.pos = pos;
+ return rc;
+}
+
+static inline void print_err_status(struct tm6000_core *dev,
+ int packet, int status)
+{
+ char *errmsg = "Unknown";
+
+ switch (status) {
+ case -ENOENT:
+ errmsg = "unlinked synchronuously";
+ break;
+ case -ECONNRESET:
+ errmsg = "unlinked asynchronuously";
+ break;
+ case -ENOSR:
+ errmsg = "Buffer error (overrun)";
+ break;
+ case -EPIPE:
+ errmsg = "Stalled (device not responding)";
+ break;
+ case -EOVERFLOW:
+ errmsg = "Babble (bad cable?)";
+ break;
+ case -EPROTO:
+ errmsg = "Bit-stuff error (bad cable?)";
+ break;
+ case -EILSEQ:
+ errmsg = "CRC/Timeout (could be anything)";
+ break;
+ case -ETIME:
+ errmsg = "Device does not respond";
+ break;
+ }
+ if (packet < 0) {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",
+ status, errmsg);
+ } else {
+ dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n",
+ packet, status, errmsg);
+ }
+}
+
+
+/*
+ * Controls the isoc copy of each urb packet
+ */
+static inline int tm6000_isoc_copy(struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ int i, len = 0, rc = 1, status;
+ char *p;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ return 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length;
+
+ if (len > 0) {
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ if (!urb->iso_frame_desc[i].status) {
+ if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) {
+ rc = copy_multiplexed(p, len, urb);
+ if (rc <= 0)
+ return rc;
+ } else {
+ copy_streams(p, len, urb);
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/* ------------------------------------------------------------------
+ * URB control
+ * ------------------------------------------------------------------
+ */
+
+/*
+ * IRQ callback, called by URB callback
+ */
+static void tm6000_irq_callback(struct urb *urb)
+{
+ struct tm6000_dmaqueue *dma_q = urb->context;
+ struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
+ int i;
+
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ tm6000_err("urb completion error %d.\n", urb->status);
+ break;
+ }
+
+ spin_lock(&dev->slock);
+ tm6000_isoc_copy(urb);
+ spin_unlock(&dev->slock);
+
+ /* Reset urb buffers */
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+
+ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (urb->status)
+ tm6000_err("urb resubmit failed (error=%i)\n",
+ urb->status);
+}
+
+/*
+ * Stop and Deallocate URBs
+ */
+static void tm6000_uninit_isoc(struct tm6000_core *dev)
+{
+ struct urb *urb;
+ int i;
+
+ dev->isoc_ctl.buf = NULL;
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = dev->isoc_ctl.urb[i];
+ if (urb) {
+ usb_kill_urb(urb);
+ usb_unlink_urb(urb);
+ if (dev->isoc_ctl.transfer_buffer[i]) {
+ usb_free_coherent(dev->udev,
+ urb->transfer_buffer_length,
+ dev->isoc_ctl.transfer_buffer[i],
+ urb->transfer_dma);
+ }
+ usb_free_urb(urb);
+ dev->isoc_ctl.urb[i] = NULL;
+ }
+ dev->isoc_ctl.transfer_buffer[i] = NULL;
+ }
+
+ kfree(dev->isoc_ctl.urb);
+ kfree(dev->isoc_ctl.transfer_buffer);
+
+ dev->isoc_ctl.urb = NULL;
+ dev->isoc_ctl.transfer_buffer = NULL;
+ dev->isoc_ctl.num_bufs = 0;
+}
+
+/*
+ * Allocate URBs and start IRQ
+ */
+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;
+ struct urb *urb;
+
+ /* De-allocates all pending stuff */
+ tm6000_uninit_isoc(dev);
+ /* Stop interrupt USB pipe */
+ tm6000_ir_int_stop(dev);
+
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber,
+ dev->isoc_in.bAlternateSetting);
+
+ /* Start interrupt USB pipe */
+ tm6000_ir_int_start(dev);
+
+ pipe = usb_rcvisocpipe(dev->udev,
+ dev->isoc_in.endp->desc.bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK);
+
+ size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+
+ if (size > dev->isoc_in.maxsize)
+ size = dev->isoc_in.maxsize;
+
+ dev->isoc_ctl.max_pkt_size = size;
+
+ max_packets = TM6000_MAX_ISO_PACKETS;
+ sb_size = max_packets * size;
+
+ dev->isoc_ctl.num_bufs = num_bufs;
+
+ dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!dev->isoc_ctl.urb) {
+ tm6000_err("cannot alloc memory for usb buffers\n");
+ return -ENOMEM;
+ }
+
+ dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!dev->isoc_ctl.transfer_buffer) {
+ tm6000_err("cannot allocate memory for usbtransfer\n");
+ kfree(dev->isoc_ctl.urb);
+ return -ENOMEM;
+ }
+
+ dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets"
+ " (%d bytes) of %d bytes each to handle %u size\n",
+ max_packets, num_bufs, sb_size,
+ dev->isoc_in.maxsize, size);
+
+ /* allocate urbs and transfer buffers */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ if (!urb) {
+ tm6000_err("cannot alloc isoc_ctl.urb %i\n", i);
+ tm6000_uninit_isoc(dev);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ dev->isoc_ctl.urb[i] = urb;
+
+ dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ sb_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!dev->isoc_ctl.transfer_buffer[i]) {
+ tm6000_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+ in_interrupt() ? " while in int" : "");
+ tm6000_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+
+ usb_fill_bulk_urb(urb, dev->udev, pipe,
+ dev->isoc_ctl.transfer_buffer[i], sb_size,
+ tm6000_irq_callback, dma_q);
+ urb->interval = dev->isoc_in.endp->desc.bInterval;
+ urb->number_of_packets = max_packets;
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ for (j = 0; j < max_packets; j++) {
+ urb->iso_frame_desc[j].offset = size * j;
+ urb->iso_frame_desc[j].length = size;
+ }
+ }
+
+ return 0;
+}
+
+static int tm6000_start_thread(struct tm6000_core *dev)
+{
+ struct tm6000_dmaqueue *dma_q = &dev->vidq;
+ int i;
+
+ dma_q->frame = 0;
+ dma_q->ini_jiffies = jiffies;
+
+ init_waitqueue_head(&dma_q->wq);
+
+ /* submit urbs and enables IRQ */
+ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
+ int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+ if (rc) {
+ tm6000_err("submit of urb %i failed (error=%i)\n", i,
+ rc);
+ tm6000_uninit_isoc(dev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------
+ * Videobuf operations
+ * ------------------------------------------------------------------
+ */
+
+static int
+buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+
+ *size = fh->fmt->depth * fh->width * fh->height >> 3;
+ if (0 == *count)
+ *count = TM6000_DEF_BUF;
+
+ if (*count < TM6000_MIN_BUF)
+ *count = TM6000_MIN_BUF;
+
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+
+ return 0;
+}
+
+static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_core *dev = fh->dev;
+ unsigned long flags;
+
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.buf == buf)
+ dev->isoc_ctl.buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+ struct tm6000_core *dev = fh->dev;
+ int rc = 0;
+
+ BUG_ON(NULL == fh->fmt);
+
+
+ /* FIXME: It assumes depth=2 */
+ /* The only currently supported format is 16 bits/pixel */
+ buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(vq, &buf->vb, NULL);
+ if (rc != 0)
+ goto fail;
+ }
+
+ if (!dev->isoc_ctl.num_bufs) {
+ rc = tm6000_prepare_isoc(dev);
+ if (rc < 0)
+ goto fail;
+
+ rc = tm6000_start_thread(dev);
+ if (rc < 0)
+ goto fail;
+
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(vq, buf);
+ return rc;
+}
+
+static void
+buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+ struct tm6000_fh *fh = vq->priv_data;
+ struct tm6000_core *dev = fh->dev;
+ struct tm6000_dmaqueue *vidq = &dev->vidq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vidq->active);
+}
+
+static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
+
+ free_buffer(vq, buf);
+}
+
+static struct videobuf_queue_ops tm6000_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+/* ------------------------------------------------------------------
+ * IOCTL handling
+ * ------------------------------------------------------------------
+ */
+
+static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh && dev->is_res_read)
+ return true;
+
+ return false;
+}
+
+static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh)
+ return true;
+
+ return false;
+}
+
+static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh,
+ bool is_res_read)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources == fh && dev->is_res_read == is_res_read)
+ return true;
+
+ /* is it free? */
+ if (dev->resources)
+ return false;
+
+ /* grab it */
+ dev->resources = fh;
+ dev->is_res_read = is_res_read;
+ dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
+ return true;
+}
+
+static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh)
+{
+ /* Is the current fh handling it? if so, that's OK */
+ if (dev->resources != fh)
+ return;
+
+ dev->resources = NULL;
+ dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n");
+}
+
+/* ------------------------------------------------------------------
+ * IOCTL vidioc handling
+ * ------------------------------------------------------------------
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
+
+ strlcpy(cap->driver, "tm6000", sizeof(cap->driver));
+ strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+
+ if (dev->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(format)))
+ return -EINVAL;
+
+ strlcpy(f->description, format[f->index].name, sizeof(f->description));
+ f->pixelformat = format[f->index].fourcc;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vb_vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(format); i++)
+ if (format[i].fourcc == fourcc)
+ return format+i;
+ return NULL;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
+ struct tm6000_fmt *fmt;
+ enum v4l2_field field;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt) {
+ dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)"
+ " invalid.\n", f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ field = f->fmt.pix.field;
+
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_SEQ_TB;
+ else if (V4L2_FIELD_INTERLACED != field) {
+ dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n");
+ return -EINVAL;
+ }
+
+ tm6000_get_std_res(dev);
+
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+
+ f->fmt.pix.width &= ~0x01;
+
+ f->fmt.pix.field = field;
+
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+/*FIXME: This seems to be generic enough to be at videodev2 */
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int ret = vidioc_try_fmt_vid_cap(file, fh, f);
+ if (ret < 0)
+ return ret;
+
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vb_vidq.field = f->fmt.pix.field;
+ fh->type = f->type;
+
+ dev->fourcc = f->fmt.pix.pixelformat;
+
+ tm6000_set_fourcc_format(dev);
+
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_reqbufs(&fh->vb_vidq, p);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_querybuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_qbuf(&fh->vb_vidq, p);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct tm6000_fh *fh = priv;
+
+ return videobuf_dqbuf(&fh->vb_vidq, p,
+ file->f_flags & O_NONBLOCK);
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ if (!res_get(dev, fh, false))
+ return -EBUSY;
+ return videobuf_streamon(&fh->vb_vidq);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (i != fh->type)
+ return -EINVAL;
+
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(dev, fh);
+
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ int rc = 0;
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ dev->norm = *norm;
+ rc = tm6000_init_analog_mode(dev);
+
+ fh->width = dev->width;
+ fh->height = dev->height;
+
+ if (rc < 0)
+ return rc;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
+
+ return 0;
+}
+
+static const char *iname[] = {
+ [TM6000_INPUT_TV] = "Television",
+ [TM6000_INPUT_COMPOSITE1] = "Composite 1",
+ [TM6000_INPUT_COMPOSITE2] = "Composite 2",
+ [TM6000_INPUT_SVIDEO] = "S-Video",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ unsigned int n;
+
+ n = i->index;
+ if (n >= 3)
+ return -EINVAL;
+
+ if (!dev->vinput[n].type)
+ return -EINVAL;
+
+ i->index = n;
+
+ if (dev->vinput[n].type == TM6000_INPUT_TV)
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ else
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strcpy(i->name, iname[dev->vinput[n].type]);
+
+ i->std = TM6000_STD;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ *i = dev->input;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int rc = 0;
+
+ if (i >= 3)
+ return -EINVAL;
+ if (!dev->vinput[i].type)
+ return -EINVAL;
+
+ dev->input = i;
+
+ rc = vidioc_s_std(file, priv, &dev->vfd->current_norm);
+
+ return rc;
+}
+
+/* --- controls ---------------------------------------------- */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
+ if (qc->id && qc->id == tm6000_qctrl[i].id) {
+ memcpy(qc, &(tm6000_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ int val;
+
+ /* FIXME: Probably, those won't work! Maybe we need shadow regs */
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0);
+ return 0;
+ case V4L2_CID_SATURATION:
+ val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0);
+ return 0;
+ 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;
+ }
+
+ if (val < 0)
+ return val;
+
+ ctrl->value = val;
+
+ return 0;
+}
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+ u8 val = ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
+ return 0;
+ case V4L2_CID_BRIGHTNESS:
+ tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
+ return 0;
+ case V4L2_CID_SATURATION:
+ tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
+ return 0;
+ 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;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ t->audmode = dev->amode;
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ dev->amode = t->audmode;
+ dprintk(dev, 3, "audio mode: %x\n", t->audmode);
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ 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);
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ 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);
+
+ 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 |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ 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;
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
+
+ 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)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (i->index != 0)
+ return -EINVAL;
+
+ if (!dev->rinput.type)
+ 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)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (dev->input != 5)
+ return -EINVAL;
+
+ *i = dev->input - 5;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+{
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *priv,
+ const struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *filp, void *priv, unsigned int i)
+{
+ struct tm6000_fh *fh = priv;
+ struct tm6000_core *dev = fh->dev;
+
+ if (i)
+ return -EINVAL;
+
+ if (!dev->rinput.type)
+ return -EINVAL;
+
+ dev->input = i + 5;
+
+ 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
+ ------------------------------------------------------------------*/
+
+static int __tm6000_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct tm6000_core *dev = video_drvdata(file);
+ struct tm6000_fh *fh;
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int i, rc;
+ int radio = 0;
+
+ 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++;
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n",
+ video_device_node_name(vdev), v4l2_type_names[type],
+ dev->users);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh) {
+ dev->users--;
+ return -ENOMEM;
+ }
+
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ dev->radio = radio;
+ fh->type = type;
+ dev->fourcc = format[0].fourcc;
+
+ fh->fmt = format_by_fourcc(dev->fourcc);
+
+ tm6000_get_std_res(dev);
+
+ fh->width = dev->width;
+ fh->height = dev->height;
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, "
+ "dev->vidq=0x%08lx\n",
+ (unsigned long)fh, (unsigned long)dev,
+ (unsigned long)&dev->vidq);
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
+ "queued=%d\n", list_empty(&dev->vidq.queued));
+ dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
+ "active=%d\n", list_empty(&dev->vidq.active));
+
+ /* initialize hardware on analog mode */
+ rc = tm6000_init_analog_mode(dev);
+ if (rc < 0)
+ return rc;
+
+ if (dev->mode != TM6000_MODE_ANALOG) {
+ /* Put all controls at a sane state */
+ for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
+ qctl_regs[i] = tm6000_qctrl[i].default_value;
+
+ dev->mode = TM6000_MODE_ANALOG;
+ }
+
+ if (!fh->radio) {
+ videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
+ NULL, &dev->slock,
+ fh->type,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct tm6000_buffer), fh, &dev->lock);
+ } else {
+ dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
+ dev->input = 5;
+ tm6000_set_audio_rinput(dev);
+ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
+ tm6000_prepare_isoc(dev);
+ tm6000_start_thread(dev);
+ }
+
+ return 0;
+}
+
+static int tm6000_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res;
+
+ mutex_lock(vdev->lock);
+ res = __tm6000_open(file);
+ mutex_unlock(vdev->lock);
+ return res;
+}
+
+static ssize_t
+tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ int res;
+
+ if (!res_get(fh->dev, fh, true))
+ return -EBUSY;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ res = videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0,
+ file->f_flags & O_NONBLOCK);
+ mutex_unlock(&dev->lock);
+ return res;
+ }
+ return 0;
+}
+
+static unsigned int
+__tm6000_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_buffer *buf;
+
+ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ return POLLERR;
+
+ if (!!is_res_streaming(fh->dev, fh))
+ return POLLERR;
+
+ if (!is_res_read(fh->dev, fh)) {
+ /* streaming capture */
+ if (list_empty(&fh->vb_vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ return videobuf_poll_stream(file, &fh->vb_vidq, wait);
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static unsigned int tm6000_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+ unsigned int res;
+
+ mutex_lock(&dev->lock);
+ res = __tm6000_poll(file, wait);
+ mutex_unlock(&dev->lock);
+ return res;
+}
+
+static int tm6000_release(struct file *file)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+ struct video_device *vdev = video_devdata(file);
+
+ dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n",
+ video_device_node_name(vdev), dev->users);
+
+ mutex_lock(&dev->lock);
+ dev->users--;
+
+ res_free(dev, fh);
+
+ if (!dev->users) {
+ tm6000_uninit_isoc(dev);
+
+ /* Stop interrupt USB pipe */
+ tm6000_ir_int_stop(dev);
+
+ usb_reset_configuration(dev->udev);
+
+ if (dev->int_in.endp)
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 2);
+ else
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 0);
+
+ /* Start interrupt USB pipe */
+ tm6000_ir_int_start(dev);
+
+ if (!fh->radio)
+ videobuf_mmap_free(&fh->vb_vidq);
+ }
+
+ kfree(fh);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+static int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
+{
+ struct tm6000_fh *fh = file->private_data;
+ struct tm6000_core *dev = fh->dev;
+ int res;
+
+ if (mutex_lock_interruptible(&dev->lock))
+ return -ERESTARTSYS;
+ res = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ mutex_unlock(&dev->lock);
+ return res;
+}
+
+static struct v4l2_file_operations tm6000_fops = {
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
+ .read = tm6000_read,
+ .poll = tm6000_poll,
+ .mmap = tm6000_mmap,
+};
+
+static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+};
+
+static struct video_device tm6000_template = {
+ .name = "tm6000",
+ .fops = &tm6000_fops,
+ .ioctl_ops = &video_ioctl_ops,
+ .release = video_device_release,
+ .tvnorms = TM6000_STD,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = tm6000_open,
+ .release = tm6000_release,
+ .unlocked_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,
+};
+
+static struct video_device tm6000_radio_template = {
+ .name = "tm6000",
+ .fops = &radio_fops,
+ .ioctl_ops = &radio_ioctl_ops,
+};
+
+/* -----------------------------------------------------------------
+ * Initialization and module stuff
+ * ------------------------------------------------------------------
+ */
+
+static struct video_device *vdev_init(struct tm6000_core *dev,
+ const struct video_device
+ *template, const char *type_name)
+{
+ struct video_device *vfd;
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+
+ *vfd = *template;
+ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = tm6000_debug;
+ vfd->lock = &dev->lock;
+
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
+
+ video_set_drvdata(vfd, dev);
+ return vfd;
+}
+
+int tm6000_v4l2_register(struct tm6000_core *dev)
+{
+ int ret = -1;
+
+ dev->vfd = vdev_init(dev, &tm6000_template, "video");
+
+ if (!dev->vfd) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ return -ENOMEM;
+ }
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+
+ ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr);
+
+ if (ret < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ return ret;
+ }
+
+ printk(KERN_INFO "%s: registered device %s\n",
+ dev->name, video_device_node_name(dev->vfd));
+
+ if (dev->caps.has_radio) {
+ 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;
+}
+
+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;
+}
+
+int tm6000_v4l2_exit(void)
+{
+ return 0;
+}
+
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr, "Allow changing video device number");
+
+module_param_named(debug, tm6000_debug, int, 0444);
+MODULE_PARM_DESC(debug, "activates debug info");
+
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/usb/tm6000/tm6000.h
index 6df418658c9c..6df418658c9c 100644
--- a/drivers/media/video/tm6000/tm6000.h
+++ b/drivers/media/usb/tm6000/tm6000.h
diff --git a/drivers/media/usb/ttusb-budget/Kconfig b/drivers/media/usb/ttusb-budget/Kconfig
new file mode 100644
index 000000000000..97bad7da689c
--- /dev/null
+++ b/drivers/media/usb/ttusb-budget/Kconfig
@@ -0,0 +1,18 @@
+config DVB_TTUSB_BUDGET
+ tristate "Technotrend/Hauppauge Nova-USB devices"
+ depends on DVB_CORE && USB && I2C && PCI
+ select DVB_CX22700 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for external USB adapters designed by Technotrend and
+ produced by Hauppauge, shipped under the brand name 'Nova-USB'.
+
+ These devices don't have a MPEG decoder built in, so you need
+ an external software decoder to watch TV.
+
+ Say Y if you own such a device and want to use it.
diff --git a/drivers/media/usb/ttusb-budget/Makefile b/drivers/media/usb/ttusb-budget/Makefile
new file mode 100644
index 000000000000..f47bbf62dcde
--- /dev/null
+++ b/drivers/media/usb/ttusb-budget/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o
+
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
index 5b682cc4c814..5b682cc4c814 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/usb/ttusb-budget/dvb-ttusb-budget.c
diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/usb/ttusb-dec/Kconfig
index 290254ab06db..290254ab06db 100644
--- a/drivers/media/dvb/ttusb-dec/Kconfig
+++ b/drivers/media/usb/ttusb-dec/Kconfig
diff --git a/drivers/media/usb/ttusb-dec/Makefile b/drivers/media/usb/ttusb-dec/Makefile
new file mode 100644
index 000000000000..5352740d2353
--- /dev/null
+++ b/drivers/media/usb/ttusb-dec/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o
+
+ccflags-y += -Idrivers/media/dvb-core/
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/usb/ttusb-dec/ttusb_dec.c
index 504c81230339..504c81230339 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/usb/ttusb-dec/ttusb_dec.c
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
index 5c45c9d0712d..5c45c9d0712d 100644
--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c
+++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.c
diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h b/drivers/media/usb/ttusb-dec/ttusbdecfe.h
index 15ccc3d1a20e..15ccc3d1a20e 100644
--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h
+++ b/drivers/media/usb/ttusb-dec/ttusbdecfe.h
diff --git a/drivers/media/usb/usbvision/Kconfig b/drivers/media/usb/usbvision/Kconfig
new file mode 100644
index 000000000000..6b6afc5d8f7e
--- /dev/null
+++ b/drivers/media/usb/usbvision/Kconfig
@@ -0,0 +1,12 @@
+config VIDEO_USBVISION
+ tristate "USB video devices based on Nogatech NT1003/1004/1005"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_TUNER
+ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT
+ ---help---
+ There are more than 50 different USB video devices based on
+ NT1003/1004/1005 USB Bridges. This driver enables using those
+ devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called usbvision.
diff --git a/drivers/media/usb/usbvision/Makefile b/drivers/media/usb/usbvision/Makefile
new file mode 100644
index 000000000000..9b3a5581df42
--- /dev/null
+++ b/drivers/media/usb/usbvision/Makefile
@@ -0,0 +1,6 @@
+usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o
+
+obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/usb/usbvision/usbvision-cards.c
index 3103d0d020e8..3103d0d020e8 100644
--- a/drivers/media/video/usbvision/usbvision-cards.c
+++ b/drivers/media/usb/usbvision/usbvision-cards.c
diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/usb/usbvision/usbvision-cards.h
index a51cc1185cce..a51cc1185cce 100644
--- a/drivers/media/video/usbvision/usbvision-cards.h
+++ b/drivers/media/usb/usbvision/usbvision-cards.h
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c
index c9b2042f8bdf..c9b2042f8bdf 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/usb/usbvision/usbvision-core.c
diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c
index 89fec029e924..89fec029e924 100644
--- a/drivers/media/video/usbvision/usbvision-i2c.c
+++ b/drivers/media/usb/usbvision/usbvision-i2c.c
diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c
new file mode 100644
index 000000000000..f67018ed3795
--- /dev/null
+++ b/drivers/media/usb/usbvision/usbvision-video.c
@@ -0,0 +1,1720 @@
+/*
+ * USB USBVISION Video device driver 0.9.10
+ *
+ *
+ *
+ * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
+ *
+ * This module is part of usbvision driver project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You 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.
+ *
+ * Let's call the version 0.... until compression decoding is completely
+ * implemented.
+ *
+ * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
+ * It was based on USB CPiA driver written by Peter Pregler,
+ * Scott J. Bertin and Johannes Erdfelt
+ * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
+ * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
+ * Updates to driver completed by Dwaine P. Garden
+ *
+ *
+ * TODO:
+ * - use submit_urb for all setup packets
+ * - Fix memory settings for nt1004. It is 4 times as big as the
+ * nt1003 memory.
+ * - Add audio on endpoint 3 for nt1004 chip.
+ * Seems impossible, needs a codec interface. Which one?
+ * - Clean up the driver.
+ * - optimization for performance.
+ * - Add Videotext capability (VBI). Working on it.....
+ * - Check audio for other devices
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+
+#include <media/saa7115.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/tuner.h>
+
+#include <linux/workqueue.h>
+
+#include "usbvision.h"
+#include "usbvision-cards.h"
+
+#define DRIVER_AUTHOR \
+ "Joerg Heckenbach <joerg@heckenbach-aw.de>, " \
+ "Dwaine Garden <DwaineGarden@rogers.com>"
+#define DRIVER_NAME "usbvision"
+#define DRIVER_ALIAS "USBVision"
+#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
+#define DRIVER_LICENSE "GPL"
+#define USBVISION_VERSION_STRING "0.9.11"
+
+#define ENABLE_HEXDUMP 0 /* Enable if you need it */
+
+
+#ifdef USBVISION_DEBUG
+ #define PDEBUG(level, fmt, args...) { \
+ if (video_debug & (level)) \
+ printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
+ __func__, __LINE__ , ## args); \
+ }
+#else
+ #define PDEBUG(level, fmt, args...) do {} while (0)
+#endif
+
+#define DBG_IO (1 << 1)
+#define DBG_PROBE (1 << 2)
+#define DBG_MMAP (1 << 3)
+
+/* String operations */
+#define rmspace(str) while (*str == ' ') str++;
+#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++;
+
+
+/* sequential number of usbvision device */
+static int usbvision_nr;
+
+static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
+ { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" },
+ { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" },
+ { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" },
+ { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" },
+ { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" },
+ { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, /* 1.5 ! */
+ { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
+};
+
+/* Function prototypes */
+static void usbvision_release(struct usb_usbvision *usbvision);
+
+/* Default initialization of device driver parameters */
+/* Set the default format for ISOC endpoint */
+static int isoc_mode = ISOC_MODE_COMPRESS;
+/* Set the default Debug Mode of the device driver */
+static int video_debug;
+/* Set the default device to power on at startup */
+static int power_on_at_open = 1;
+/* Sequential Number of Video Device */
+static int video_nr = -1;
+/* Sequential Number of Radio Device */
+static int radio_nr = -1;
+
+/* Grab parameters for the device driver */
+
+/* Showing parameters under SYSFS */
+module_param(isoc_mode, int, 0444);
+module_param(video_debug, int, 0444);
+module_param(power_on_at_open, int, 0444);
+module_param(video_nr, int, 0444);
+module_param(radio_nr, int, 0444);
+
+MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)");
+MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)");
+MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)");
+MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)");
+MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
+
+
+/* Misc stuff */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+MODULE_VERSION(USBVISION_VERSION_STRING);
+MODULE_ALIAS(DRIVER_ALIAS);
+
+
+/*****************************************************************************/
+/* SYSFS Code - Copied from the stv680.c usb module. */
+/* Device information is located at /sys/class/video4linux/video0 */
+/* Device parameters information is located at /sys/module/usbvision */
+/* Device USB Information is located at */
+/* /sys/bus/usb/drivers/USBVision Video Grabber */
+/*****************************************************************************/
+
+#define YES_NO(x) ((x) ? "Yes" : "No")
+
+static inline struct usb_usbvision *cd_to_usbvision(struct device *cd)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ return video_get_drvdata(vdev);
+}
+
+static ssize_t show_version(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", USBVISION_VERSION_STRING);
+}
+static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
+
+static ssize_t show_model(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ usbvision_device_data[usbvision->dev_model].model_string);
+}
+static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
+
+static ssize_t show_hue(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+
+static ssize_t show_contrast(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+
+static ssize_t show_brightness(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+
+static ssize_t show_saturation(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ struct v4l2_control ctrl;
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = 0;
+ if (usbvision->user)
+ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+}
+static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+
+static ssize_t show_streaming(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ YES_NO(usbvision->streaming == stream_on ? 1 : 0));
+}
+static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);
+
+static ssize_t show_compression(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%s\n",
+ YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS));
+}
+static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);
+
+static ssize_t show_device_bridge(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev =
+ container_of(cd, struct video_device, dev);
+ struct usb_usbvision *usbvision = video_get_drvdata(vdev);
+ return sprintf(buf, "%d\n", usbvision->bridge_type);
+}
+static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);
+
+static void usbvision_create_sysfs(struct video_device *vdev)
+{
+ int res;
+
+ if (!vdev)
+ return;
+ do {
+ res = device_create_file(&vdev->dev, &dev_attr_version);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_model);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_hue);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_contrast);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_brightness);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_saturation);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_streaming);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_compression);
+ if (res < 0)
+ break;
+ res = device_create_file(&vdev->dev, &dev_attr_bridge);
+ if (res >= 0)
+ return;
+ } while (0);
+
+ dev_err(&vdev->dev, "%s error: %d\n", __func__, res);
+}
+
+static void usbvision_remove_sysfs(struct video_device *vdev)
+{
+ if (vdev) {
+ device_remove_file(&vdev->dev, &dev_attr_version);
+ device_remove_file(&vdev->dev, &dev_attr_model);
+ device_remove_file(&vdev->dev, &dev_attr_hue);
+ device_remove_file(&vdev->dev, &dev_attr_contrast);
+ device_remove_file(&vdev->dev, &dev_attr_brightness);
+ device_remove_file(&vdev->dev, &dev_attr_saturation);
+ device_remove_file(&vdev->dev, &dev_attr_streaming);
+ device_remove_file(&vdev->dev, &dev_attr_compression);
+ device_remove_file(&vdev->dev, &dev_attr_bridge);
+ }
+}
+
+/*
+ * usbvision_open()
+ *
+ * This is part of Video 4 Linux API. The driver can be opened by one
+ * client only (checks internal counter 'usbvision->user'). The procedure
+ * then allocates buffers needed for video processing.
+ *
+ */
+static int usbvision_v4l2_open(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "open");
+
+ if (mutex_lock_interruptible(&usbvision->v4l2_lock))
+ return -ERESTARTSYS;
+ usbvision_reset_power_off_timer(usbvision);
+
+ if (usbvision->user)
+ err_code = -EBUSY;
+ else {
+ /* Allocate memory for the scratch ring buffer */
+ err_code = usbvision_scratch_alloc(usbvision);
+ if (isoc_mode == ISOC_MODE_COMPRESS) {
+ /* Allocate intermediate decompression buffers
+ only if needed */
+ err_code = usbvision_decompress_alloc(usbvision);
+ }
+ if (err_code) {
+ /* Deallocate all buffers if trouble */
+ usbvision_scratch_free(usbvision);
+ usbvision_decompress_free(usbvision);
+ }
+ }
+
+ /* If so far no errors then we shall start the camera */
+ if (!err_code) {
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+
+ /* Send init sequence only once, it's large! */
+ if (!usbvision->initialized) {
+ int setup_ok = 0;
+ setup_ok = usbvision_setup(usbvision, isoc_mode);
+ if (setup_ok)
+ usbvision->initialized = 1;
+ else
+ err_code = -EBUSY;
+ }
+
+ if (!err_code) {
+ usbvision_begin_streaming(usbvision);
+ err_code = usbvision_init_isoc(usbvision);
+ /* device must be initialized before isoc transfer */
+ usbvision_muxsel(usbvision, 0);
+ usbvision->user++;
+ } else {
+ if (power_on_at_open) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+ }
+
+ /* prepare queues */
+ usbvision_empty_framequeues(usbvision);
+ mutex_unlock(&usbvision->v4l2_lock);
+
+ PDEBUG(DBG_IO, "success");
+ return err_code;
+}
+
+/*
+ * usbvision_v4l2_close()
+ *
+ * This is part of Video 4 Linux API. The procedure
+ * stops streaming and deallocates all buffers that were earlier
+ * allocated in usbvision_v4l2_open().
+ *
+ */
+static int usbvision_v4l2_close(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ PDEBUG(DBG_IO, "close");
+
+ mutex_lock(&usbvision->v4l2_lock);
+ usbvision_audio_off(usbvision);
+ usbvision_restart_isoc(usbvision);
+ usbvision_stop_isoc(usbvision);
+
+ usbvision_decompress_free(usbvision);
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ usbvision_scratch_free(usbvision);
+
+ usbvision->user--;
+
+ if (power_on_at_open) {
+ /* power off in a little while
+ to avoid off/on every close/open short sequences */
+ usbvision_set_power_off_timer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ if (usbvision->remove_pending) {
+ printk(KERN_INFO "%s: Final disconnect\n", __func__);
+ usbvision_release(usbvision);
+ }
+ mutex_unlock(&usbvision->v4l2_lock);
+
+ PDEBUG(DBG_IO, "success");
+ return 0;
+}
+
+
+/*
+ * usbvision_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* NT100x has a 8-bit register space */
+ err_code = usbvision_read_reg(usbvision, reg->reg&0xff);
+ if (err_code < 0) {
+ dev_err(&usbvision->vdev->dev,
+ "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n",
+ __func__, err_code);
+ return err_code;
+ }
+ reg->val = err_code;
+ reg->size = 1;
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code;
+
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+ /* NT100x has a 8-bit register space */
+ err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val);
+ if (err_code < 0) {
+ dev_err(&usbvision->vdev->dev,
+ "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n",
+ __func__, err_code);
+ return err_code;
+ }
+ return 0;
+}
+#endif
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *vc)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
+ strlcpy(vc->card,
+ usbvision_device_data[usbvision->dev_model].model_string,
+ sizeof(vc->card));
+ usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
+ vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
+ return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *vi)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int chan;
+
+ if (vi->index >= usbvision->video_inputs)
+ return -EINVAL;
+ if (usbvision->have_tuner)
+ chan = vi->index;
+ else
+ chan = vi->index + 1; /* skip Television string*/
+
+ /* Determine the requested input characteristics
+ specific for each usbvision card model */
+ switch (chan) {
+ case 0:
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4) {
+ strcpy(vi->name, "White Video Input");
+ } else {
+ strcpy(vi->name, "Television");
+ vi->type = V4L2_INPUT_TYPE_TUNER;
+ vi->audioset = 1;
+ vi->tuner = chan;
+ vi->std = USBVISION_NORMS;
+ }
+ break;
+ case 1:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
+ strcpy(vi->name, "Green Video Input");
+ else
+ strcpy(vi->name, "Composite Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 2:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
+ strcpy(vi->name, "Yellow Video Input");
+ else
+ strcpy(vi->name, "S-Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ case 3:
+ vi->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(vi->name, "Red Video Input");
+ vi->std = V4L2_STD_PAL;
+ break;
+ }
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *input)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ *input = usbvision->ctl_input;
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (input >= usbvision->video_inputs)
+ return -EINVAL;
+
+ usbvision_muxsel(usbvision, input);
+ usbvision_set_input(usbvision);
+ usbvision_set_output(usbvision,
+ usbvision->curwidth,
+ usbvision->curheight);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ usbvision->tvnorm_id = *id;
+
+ call_all(usbvision, core, s_std, usbvision->tvnorm_id);
+ /* propagate the change to the decoder */
+ usbvision_muxsel(usbvision, usbvision->ctl_input);
+
+ return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */
+ return -EINVAL;
+ if (usbvision->radio) {
+ strcpy(vt->name, "Radio");
+ vt->type = V4L2_TUNER_RADIO;
+ } else {
+ strcpy(vt->name, "Television");
+ }
+ /* Let clients fill in the remainder of this struct */
+ call_all(usbvision, tuner, g_tuner, vt);
+
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *vt)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ /* Only no or one tuner for now */
+ if (!usbvision->have_tuner || vt->index)
+ return -EINVAL;
+ /* let clients handle this */
+ call_all(usbvision, tuner, s_tuner, vt);
+
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ freq->tuner = 0; /* Only one tuner */
+ if (usbvision->radio)
+ freq->type = V4L2_TUNER_RADIO;
+ else
+ freq->type = V4L2_TUNER_ANALOG_TV;
+ freq->frequency = usbvision->freq;
+
+ return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *freq)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ /* Only no or one tuner for now */
+ if (!usbvision->have_tuner || freq->tuner)
+ return -EINVAL;
+
+ usbvision->freq = freq->frequency;
+ call_all(usbvision, tuner, s_frequency, freq);
+
+ return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (usbvision->radio)
+ strcpy(a->name, "Radio");
+ else
+ strcpy(a->name, "TV");
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh,
+ const struct v4l2_audio *a)
+{
+ if (a->index)
+ return -EINVAL;
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, queryctrl, ctrl);
+
+ if (!ctrl->type)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, g_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ call_all(usbvision, core, s_ctrl, ctrl);
+ return 0;
+}
+
+static int vidioc_reqbufs(struct file *file,
+ void *priv, struct v4l2_requestbuffers *vr)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+
+ RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES);
+
+ /* Check input validity:
+ the user must do a VIDEO CAPTURE and MMAP method. */
+ if (vr->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if (usbvision->streaming == stream_on) {
+ ret = usbvision_stream_interrupt(usbvision);
+ if (ret)
+ return ret;
+ }
+
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ vr->count = usbvision_frames_alloc(usbvision, vr->count);
+
+ usbvision->cur_frame = NULL;
+
+ return 0;
+}
+
+static int vidioc_querybuf(struct file *file,
+ void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ struct usbvision_frame *frame;
+
+ /* FIXME : must control
+ that buffers are mapped (VIDIOC_REQBUFS has been called) */
+ if (vb->index >= usbvision->num_frames)
+ return -EINVAL;
+ /* Updating the corresponding frame state */
+ vb->flags = 0;
+ frame = &usbvision->frame[vb->index];
+ if (frame->grabstate >= frame_state_ready)
+ vb->flags |= V4L2_BUF_FLAG_QUEUED;
+ if (frame->grabstate >= frame_state_done)
+ vb->flags |= V4L2_BUF_FLAG_DONE;
+ if (frame->grabstate == frame_state_unused)
+ vb->flags |= V4L2_BUF_FLAG_MAPPED;
+ vb->memory = V4L2_MEMORY_MMAP;
+
+ vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size);
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->field = V4L2_FIELD_NONE;
+ vb->length = usbvision->curwidth *
+ usbvision->curheight *
+ usbvision->palette.bytes_per_pixel;
+ vb->timestamp = usbvision->frame[vb->index].timestamp;
+ vb->sequence = usbvision->frame[vb->index].sequence;
+ return 0;
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ struct usbvision_frame *frame;
+ unsigned long lock_flags;
+
+ /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */
+ if (vb->index >= usbvision->num_frames)
+ return -EINVAL;
+
+ frame = &usbvision->frame[vb->index];
+
+ if (frame->grabstate != frame_state_unused)
+ return -EAGAIN;
+
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = frame_state_ready;
+ frame->scanstate = scan_state_scanning;
+ frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */
+
+ vb->flags &= ~V4L2_BUF_FLAG_DONE;
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ return 0;
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+ struct usbvision_frame *f;
+ unsigned long lock_flags;
+
+ if (list_empty(&(usbvision->outqueue))) {
+ if (usbvision->streaming == stream_idle)
+ return -EINVAL;
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ f = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ f->grabstate = frame_state_unused;
+
+ vb->memory = V4L2_MEMORY_MMAP;
+ vb->flags = V4L2_BUF_FLAG_MAPPED |
+ V4L2_BUF_FLAG_QUEUED |
+ V4L2_BUF_FLAG_DONE;
+ vb->index = f->index;
+ vb->sequence = f->sequence;
+ vb->timestamp = f->timestamp;
+ vb->field = V4L2_FIELD_NONE;
+ vb->bytesused = f->scanlength;
+
+ return 0;
+}
+
+static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ usbvision->streaming = stream_on;
+ call_all(usbvision, video, s_stream, 1);
+
+ return 0;
+}
+
+static int vidioc_streamoff(struct file *file,
+ void *priv, enum v4l2_buf_type type)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (usbvision->streaming == stream_on) {
+ usbvision_stream_interrupt(usbvision);
+ /* Stop all video streamings */
+ call_all(usbvision, video, s_stream, 0);
+ }
+ usbvision_empty_framequeues(usbvision);
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *vfd)
+{
+ if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1)
+ return -EINVAL;
+ strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc);
+ vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ vf->fmt.pix.width = usbvision->curwidth;
+ vf->fmt.pix.height = usbvision->curheight;
+ vf->fmt.pix.pixelformat = usbvision->palette.format;
+ vf->fmt.pix.bytesperline =
+ usbvision->curwidth * usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight;
+ vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int format_idx;
+
+ /* Find requested format in available ones */
+ for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) {
+ if (vf->fmt.pix.pixelformat ==
+ usbvision_v4l2_format[format_idx].format) {
+ usbvision->palette = usbvision_v4l2_format[format_idx];
+ break;
+ }
+ }
+ /* robustness */
+ if (format_idx == USBVISION_SUPPORTED_PALETTES)
+ return -EINVAL;
+ RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
+ RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
+
+ vf->fmt.pix.bytesperline = vf->fmt.pix.width*
+ usbvision->palette.bytes_per_pixel;
+ vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *vf)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, vf);
+ if (ret)
+ return ret;
+
+ /* stop io in case it is already in progress */
+ if (usbvision->streaming == stream_on) {
+ ret = usbvision_stream_interrupt(usbvision);
+ if (ret)
+ return ret;
+ }
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+
+ usbvision->cur_frame = NULL;
+
+ /* by now we are committed to the new data... */
+ usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
+
+ return 0;
+}
+
+static ssize_t usbvision_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int noblock = file->f_flags & O_NONBLOCK;
+ unsigned long lock_flags;
+ int ret, i;
+ struct usbvision_frame *frame;
+
+ PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__,
+ (unsigned long)count, noblock);
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
+ return -EFAULT;
+
+ /* This entry point is compatible with the mmap routines
+ so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF
+ to get frames or call read on the device. */
+ if (!usbvision->num_frames) {
+ /* First, allocate some frames to work with
+ if this has not been done with VIDIOC_REQBUF */
+ usbvision_frames_free(usbvision);
+ usbvision_empty_framequeues(usbvision);
+ usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES);
+ }
+
+ if (usbvision->streaming != stream_on) {
+ /* no stream is running, make it running ! */
+ usbvision->streaming = stream_on;
+ call_all(usbvision, video, s_stream, 1);
+ }
+
+ /* Then, enqueue as many frames as possible
+ (like a user of VIDIOC_QBUF would do) */
+ for (i = 0; i < usbvision->num_frames; i++) {
+ frame = &usbvision->frame[i];
+ if (frame->grabstate == frame_state_unused) {
+ /* Mark it as ready and enqueue frame */
+ frame->grabstate = frame_state_ready;
+ frame->scanstate = scan_state_scanning;
+ /* Accumulated in usbvision_parse_data() */
+ frame->scanlength = 0;
+
+ /* set v4l2_format index */
+ frame->v4l2_format = usbvision->palette;
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ list_add_tail(&frame->frame, &usbvision->inqueue);
+ spin_unlock_irqrestore(&usbvision->queue_lock,
+ lock_flags);
+ }
+ }
+
+ /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */
+ if (list_empty(&(usbvision->outqueue))) {
+ if (noblock)
+ return -EAGAIN;
+
+ ret = wait_event_interruptible
+ (usbvision->wait_frame,
+ !list_empty(&(usbvision->outqueue)));
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
+ frame = list_entry(usbvision->outqueue.next,
+ struct usbvision_frame, frame);
+ list_del(usbvision->outqueue.next);
+ spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
+
+ /* An error returns an empty frame */
+ if (frame->grabstate == frame_state_error) {
+ frame->bytes_read = 0;
+ return 0;
+ }
+
+ PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld",
+ __func__,
+ frame->index, frame->bytes_read, frame->scanlength);
+
+ /* copy bytes to user space; we allow for partials reads */
+ if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
+ count = frame->scanlength - frame->bytes_read;
+
+ if (copy_to_user(buf, frame->data + frame->bytes_read, count))
+ return -EFAULT;
+
+ frame->bytes_read += count;
+ PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld",
+ __func__,
+ (unsigned long)count, frame->bytes_read);
+
+ /* For now, forget the frame if it has not been read in one shot. */
+/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */
+ frame->bytes_read = 0;
+
+ /* Mark it as available to be used again. */
+ frame->grabstate = frame_state_unused;
+/* } */
+
+ return count;
+}
+
+static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int res;
+
+ if (mutex_lock_interruptible(&usbvision->v4l2_lock))
+ return -ERESTARTSYS;
+ res = usbvision_read(file, buf, count, ppos);
+ mutex_unlock(&usbvision->v4l2_lock);
+ return res;
+}
+
+static int usbvision_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start,
+ start = vma->vm_start;
+ void *pos;
+ u32 i;
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+ PDEBUG(DBG_MMAP, "mmap");
+
+ if (!USBVISION_IS_OPERATIONAL(usbvision))
+ return -EFAULT;
+
+ if (!(vma->vm_flags & VM_WRITE) ||
+ size != PAGE_ALIGN(usbvision->max_frame_size)) {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < usbvision->num_frames; i++) {
+ if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) ==
+ vma->vm_pgoff)
+ break;
+ }
+ if (i == usbvision->num_frames) {
+ PDEBUG(DBG_MMAP,
+ "mmap: user supplied mapping address is out of range");
+ return -EINVAL;
+ }
+
+ /* VM_IO is eventually going to replace PageReserved altogether */
+ vma->vm_flags |= VM_IO;
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+
+ pos = usbvision->frame[i].data;
+ while (size > 0) {
+ if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
+ PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int res;
+
+ if (mutex_lock_interruptible(&usbvision->v4l2_lock))
+ return -ERESTARTSYS;
+ res = usbvision_mmap(file, vma);
+ mutex_unlock(&usbvision->v4l2_lock);
+ return res;
+}
+
+/*
+ * Here comes the stuff for radio on usbvision based devices
+ *
+ */
+static int usbvision_radio_open(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "%s:", __func__);
+
+ if (mutex_lock_interruptible(&usbvision->v4l2_lock))
+ return -ERESTARTSYS;
+ if (usbvision->user) {
+ dev_err(&usbvision->rdev->dev,
+ "%s: Someone tried to open an already opened USBVision Radio!\n",
+ __func__);
+ err_code = -EBUSY;
+ } else {
+ if (power_on_at_open) {
+ usbvision_reset_power_off_timer(usbvision);
+ if (usbvision->power == 0) {
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+ }
+
+ /* Alternate interface 1 is is the biggest frame size */
+ err_code = usbvision_set_alternate(usbvision);
+ if (err_code < 0) {
+ usbvision->last_error = err_code;
+ err_code = -EBUSY;
+ goto out;
+ }
+
+ /* If so far no errors then we shall start the radio */
+ usbvision->radio = 1;
+ call_all(usbvision, tuner, s_radio);
+ usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
+ usbvision->user++;
+ }
+
+ if (err_code) {
+ if (power_on_at_open) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ usbvision->initialized = 0;
+ }
+ }
+out:
+ mutex_unlock(&usbvision->v4l2_lock);
+ return err_code;
+}
+
+
+static int usbvision_radio_close(struct file *file)
+{
+ struct usb_usbvision *usbvision = video_drvdata(file);
+ int err_code = 0;
+
+ PDEBUG(DBG_IO, "");
+
+ mutex_lock(&usbvision->v4l2_lock);
+ /* Set packet size to 0 */
+ usbvision->iface_alt = 0;
+ err_code = usb_set_interface(usbvision->dev, usbvision->iface,
+ usbvision->iface_alt);
+
+ usbvision_audio_off(usbvision);
+ usbvision->radio = 0;
+ usbvision->user--;
+
+ if (power_on_at_open) {
+ usbvision_set_power_off_timer(usbvision);
+ usbvision->initialized = 0;
+ }
+
+ if (usbvision->remove_pending) {
+ printk(KERN_INFO "%s: Final disconnect\n", __func__);
+ usbvision_release(usbvision);
+ }
+
+ mutex_unlock(&usbvision->v4l2_lock);
+ PDEBUG(DBG_IO, "success");
+ return err_code;
+}
+
+/* Video registration stuff */
+
+/* Video template */
+static const struct v4l2_file_operations usbvision_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_v4l2_open,
+ .release = usbvision_v4l2_close,
+ .read = usbvision_v4l2_read,
+ .mmap = usbvision_v4l2_mmap,
+ .unlocked_ioctl = video_ioctl2,
+/* .poll = video_poll, */
+};
+
+static const struct v4l2_ioctl_ops usbvision_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+};
+
+static struct video_device usbvision_video_template = {
+ .fops = &usbvision_fops,
+ .ioctl_ops = &usbvision_ioctl_ops,
+ .name = "usbvision-video",
+ .release = video_device_release,
+ .tvnorms = USBVISION_NORMS,
+ .current_norm = V4L2_STD_PAL
+};
+
+
+/* Radio template */
+static const struct v4l2_file_operations usbvision_radio_fops = {
+ .owner = THIS_MODULE,
+ .open = usbvision_radio_open,
+ .release = usbvision_radio_close,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
+
+static struct video_device usbvision_radio_template = {
+ .fops = &usbvision_radio_fops,
+ .name = "usbvision-radio",
+ .release = video_device_release,
+ .ioctl_ops = &usbvision_radio_ioctl_ops,
+
+ .tvnorms = USBVISION_NORMS,
+ .current_norm = V4L2_STD_PAL
+};
+
+
+static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+ struct video_device *vdev_template,
+ char *name)
+{
+ struct usb_device *usb_dev = usbvision->dev;
+ struct video_device *vdev;
+
+ if (usb_dev == NULL) {
+ dev_err(&usbvision->dev->dev,
+ "%s: usbvision->dev is not set\n", __func__);
+ return NULL;
+ }
+
+ vdev = video_device_alloc();
+ if (NULL == vdev)
+ return NULL;
+ *vdev = *vdev_template;
+ vdev->lock = &usbvision->v4l2_lock;
+ vdev->v4l2_dev = &usbvision->v4l2_dev;
+ snprintf(vdev->name, sizeof(vdev->name), "%s", name);
+ video_set_drvdata(vdev, usbvision);
+ return vdev;
+}
+
+/* unregister video4linux devices */
+static void usbvision_unregister_video(struct usb_usbvision *usbvision)
+{
+ /* Radio Device: */
+ if (usbvision->rdev) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->rdev));
+ if (video_is_registered(usbvision->rdev))
+ video_unregister_device(usbvision->rdev);
+ else
+ video_device_release(usbvision->rdev);
+ usbvision->rdev = NULL;
+ }
+
+ /* Video Device: */
+ if (usbvision->vdev) {
+ PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
+ video_device_node_name(usbvision->vdev));
+ if (video_is_registered(usbvision->vdev))
+ video_unregister_device(usbvision->vdev);
+ else
+ video_device_release(usbvision->vdev);
+ usbvision->vdev = NULL;
+ }
+}
+
+/* register video4linux devices */
+static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
+{
+ /* Video Device: */
+ usbvision->vdev = usbvision_vdev_init(usbvision,
+ &usbvision_video_template,
+ "USBVision Video");
+ if (usbvision->vdev == NULL)
+ goto err_exit;
+ if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
+ goto err_exit;
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->vdev));
+
+ /* Radio Device: */
+ if (usbvision_device_data[usbvision->dev_model].radio) {
+ /* usbvision has radio */
+ usbvision->rdev = usbvision_vdev_init(usbvision,
+ &usbvision_radio_template,
+ "USBVision Radio");
+ if (usbvision->rdev == NULL)
+ goto err_exit;
+ if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0)
+ goto err_exit;
+ printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n",
+ usbvision->nr, video_device_node_name(usbvision->rdev));
+ }
+ /* all done */
+ return 0;
+
+ err_exit:
+ dev_err(&usbvision->dev->dev,
+ "USBVision[%d]: video_register_device() failed\n",
+ usbvision->nr);
+ usbvision_unregister_video(usbvision);
+ return -1;
+}
+
+/*
+ * usbvision_alloc()
+ *
+ * This code allocates the struct usb_usbvision.
+ * It is filled with default values.
+ *
+ * Returns NULL on error, a pointer to usb_usbvision else.
+ *
+ */
+static struct usb_usbvision *usbvision_alloc(struct usb_device *dev,
+ struct usb_interface *intf)
+{
+ struct usb_usbvision *usbvision;
+
+ usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL);
+ if (usbvision == NULL)
+ return NULL;
+
+ usbvision->dev = dev;
+ if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev))
+ goto err_free;
+
+ mutex_init(&usbvision->v4l2_lock);
+
+ /* prepare control urb for control messages during interrupts */
+ usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (usbvision->ctrl_urb == NULL)
+ goto err_unreg;
+ init_waitqueue_head(&usbvision->ctrl_urb_wq);
+
+ usbvision_init_power_off_timer(usbvision);
+
+ return usbvision;
+
+err_unreg:
+ v4l2_device_unregister(&usbvision->v4l2_dev);
+err_free:
+ kfree(usbvision);
+ return NULL;
+}
+
+/*
+ * usbvision_release()
+ *
+ * This code does final release of struct usb_usbvision. This happens
+ * after the device is disconnected -and- all clients closed their files.
+ *
+ */
+static void usbvision_release(struct usb_usbvision *usbvision)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usbvision_reset_power_off_timer(usbvision);
+
+ usbvision->initialized = 0;
+
+ usbvision_remove_sysfs(usbvision->vdev);
+ usbvision_unregister_video(usbvision);
+
+ usb_free_urb(usbvision->ctrl_urb);
+
+ v4l2_device_unregister(&usbvision->v4l2_dev);
+ kfree(usbvision);
+
+ PDEBUG(DBG_PROBE, "success");
+}
+
+
+/*********************** usb interface **********************************/
+
+static void usbvision_configure_video(struct usb_usbvision *usbvision)
+{
+ int model;
+
+ if (usbvision == NULL)
+ return;
+
+ model = usbvision->dev_model;
+ usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */
+
+ if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) {
+ usbvision->vin_reg2_preset =
+ usbvision_device_data[usbvision->dev_model].vin_reg2;
+ } else {
+ usbvision->vin_reg2_preset = 0;
+ }
+
+ usbvision->tvnorm_id = usbvision_device_data[model].video_norm;
+
+ usbvision->video_inputs = usbvision_device_data[model].video_channels;
+ usbvision->ctl_input = 0;
+
+ /* This should be here to make i2c clients to be able to register */
+ /* first switch off audio */
+ if (usbvision_device_data[model].audio_channels > 0)
+ usbvision_audio_off(usbvision);
+ if (!power_on_at_open) {
+ /* and then power up the noisy tuner */
+ usbvision_power_on(usbvision);
+ usbvision_i2c_register(usbvision);
+ }
+}
+
+/*
+ * usbvision_probe()
+ *
+ * This procedure queries device descriptor and accepts the interface
+ * if it looks like USBVISION video device
+ *
+ */
+static int __devinit usbvision_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf));
+ struct usb_interface *uif;
+ __u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
+ const struct usb_host_interface *interface;
+ struct usb_usbvision *usbvision = NULL;
+ const struct usb_endpoint_descriptor *endpoint;
+ int model, i;
+
+ PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct, ifnum);
+
+ model = devid->driver_info;
+ if (model < 0 || model >= usbvision_device_data_size) {
+ PDEBUG(DBG_PROBE, "model out of bounds %d", model);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "%s: %s found\n", __func__,
+ usbvision_device_data[model].model_string);
+
+ if (usbvision_device_data[model].interface >= 0)
+ interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0];
+ else
+ interface = &dev->actconfig->interface[ifnum]->altsetting[0];
+ endpoint = &interface->endpoint[1].desc;
+ if (!usb_endpoint_xfer_isoc(endpoint)) {
+ dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
+ __func__, ifnum);
+ dev_err(&intf->dev, "%s: Endpoint attributes %d",
+ __func__, endpoint->bmAttributes);
+ return -ENODEV;
+ }
+ if (usb_endpoint_dir_out(endpoint)) {
+ dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n",
+ __func__, ifnum);
+ return -ENODEV;
+ }
+
+ usbvision = usbvision_alloc(dev, intf);
+ if (usbvision == NULL) {
+ dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__);
+ return -ENOMEM;
+ }
+
+ if (dev->descriptor.bNumConfigurations > 1)
+ usbvision->bridge_type = BRIDGE_NT1004;
+ else if (model == DAZZLE_DVC_90_REV_1_SECAM)
+ usbvision->bridge_type = BRIDGE_NT1005;
+ else
+ usbvision->bridge_type = BRIDGE_NT1003;
+ PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type);
+
+ /* compute alternate max packet sizes */
+ uif = dev->actconfig->interface[0];
+
+ usbvision->num_alt = uif->num_altsetting;
+ PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt);
+ usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
+ if (usbvision->alt_max_pkt_size == NULL) {
+ dev_err(&intf->dev, "usbvision: out of memory!\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < usbvision->num_alt; i++) {
+ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
+ wMaxPacketSize);
+ usbvision->alt_max_pkt_size[i] =
+ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
+ PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i,
+ usbvision->alt_max_pkt_size[i]);
+ }
+
+
+ usbvision->nr = usbvision_nr++;
+
+ usbvision->have_tuner = usbvision_device_data[model].tuner;
+ if (usbvision->have_tuner)
+ usbvision->tuner_type = usbvision_device_data[model].tuner_type;
+
+ usbvision->dev_model = model;
+ usbvision->remove_pending = 0;
+ usbvision->iface = ifnum;
+ usbvision->iface_alt = 0;
+ usbvision->video_endp = endpoint->bEndpointAddress;
+ usbvision->isoc_packet_size = 0;
+ usbvision->usb_bandwidth = 0;
+ usbvision->user = 0;
+ usbvision->streaming = stream_off;
+ usbvision_configure_video(usbvision);
+ usbvision_register_video(usbvision);
+
+ usbvision_create_sysfs(usbvision->vdev);
+
+ PDEBUG(DBG_PROBE, "success");
+ return 0;
+}
+
+
+/*
+ * usbvision_disconnect()
+ *
+ * This procedure stops all driver activity, deallocates interface-private
+ * structure (pointed by 'ptr') and after that driver should be removable
+ * with no ill consequences.
+ *
+ */
+static void __devexit usbvision_disconnect(struct usb_interface *intf)
+{
+ struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf));
+
+ PDEBUG(DBG_PROBE, "");
+
+ if (usbvision == NULL) {
+ pr_err("%s: usb_get_intfdata() failed\n", __func__);
+ return;
+ }
+
+ mutex_lock(&usbvision->v4l2_lock);
+
+ /* At this time we ask to cancel outstanding URBs */
+ usbvision_stop_isoc(usbvision);
+
+ v4l2_device_disconnect(&usbvision->v4l2_dev);
+
+ if (usbvision->power) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+ }
+ usbvision->remove_pending = 1; /* Now all ISO data will be ignored */
+
+ usb_put_dev(usbvision->dev);
+ usbvision->dev = NULL; /* USB device is no more */
+
+ mutex_unlock(&usbvision->v4l2_lock);
+
+ if (usbvision->user) {
+ printk(KERN_INFO "%s: In use, disconnect pending\n",
+ __func__);
+ wake_up_interruptible(&usbvision->wait_frame);
+ wake_up_interruptible(&usbvision->wait_stream);
+ } else {
+ usbvision_release(usbvision);
+ }
+
+ PDEBUG(DBG_PROBE, "success");
+}
+
+static struct usb_driver usbvision_driver = {
+ .name = "usbvision",
+ .id_table = usbvision_table,
+ .probe = usbvision_probe,
+ .disconnect = __devexit_p(usbvision_disconnect),
+};
+
+/*
+ * usbvision_init()
+ *
+ * This code is run to initialize the driver.
+ *
+ */
+static int __init usbvision_init(void)
+{
+ int err_code;
+
+ PDEBUG(DBG_PROBE, "");
+
+ PDEBUG(DBG_IO, "IO debugging is enabled [video]");
+ PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]");
+ PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]");
+
+ /* disable planar mode support unless compression enabled */
+ if (isoc_mode != ISOC_MODE_COMPRESS) {
+ /* FIXME : not the right way to set supported flag */
+ usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */
+ usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */
+ }
+
+ err_code = usb_register(&usbvision_driver);
+
+ if (err_code == 0) {
+ printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n");
+ PDEBUG(DBG_PROBE, "success");
+ }
+ return err_code;
+}
+
+static void __exit usbvision_exit(void)
+{
+ PDEBUG(DBG_PROBE, "");
+
+ usb_deregister(&usbvision_driver);
+ PDEBUG(DBG_PROBE, "success");
+}
+
+module_init(usbvision_init);
+module_exit(usbvision_exit);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h
index 43cf61fe4943..43cf61fe4943 100644
--- a/drivers/media/video/usbvision/usbvision.h
+++ b/drivers/media/usb/usbvision/usbvision.h
diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/usb/uvc/Kconfig
index 541c9f1e4c6a..541c9f1e4c6a 100644
--- a/drivers/media/video/uvc/Kconfig
+++ b/drivers/media/usb/uvc/Kconfig
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/usb/uvc/Makefile
index c26d12fdb8f4..c26d12fdb8f4 100644
--- a/drivers/media/video/uvc/Makefile
+++ b/drivers/media/usb/uvc/Makefile
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index f7061a5ef1d2..f7061a5ef1d2 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
diff --git a/drivers/media/video/uvc/uvc_debugfs.c b/drivers/media/usb/uvc/uvc_debugfs.c
index 14561a5abb79..14561a5abb79 100644
--- a/drivers/media/video/uvc/uvc_debugfs.c
+++ b/drivers/media/usb/uvc/uvc_debugfs.c
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
new file mode 100644
index 000000000000..5967081747ce
--- /dev/null
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -0,0 +1,2462 @@
+/*
+ * uvc_driver.c -- USB Video Class driver
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.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.
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/version.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+#define DRIVER_AUTHOR "Laurent Pinchart " \
+ "<laurent.pinchart@ideasonboard.com>"
+#define DRIVER_DESC "USB Video Class driver"
+
+unsigned int uvc_clock_param = CLOCK_MONOTONIC;
+unsigned int uvc_no_drop_param;
+static unsigned int uvc_quirks_param = -1;
+unsigned int uvc_trace_param;
+unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+
+/* ------------------------------------------------------------------------
+ * Video formats
+ */
+
+static struct uvc_format_desc uvc_fmts[] = {
+ {
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .name = "YUV 4:2:2 (YUYV)",
+ .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
+ .fcc = V4L2_PIX_FMT_YUYV,
+ },
+ {
+ .name = "YUV 4:2:0 (NV12)",
+ .guid = UVC_GUID_FORMAT_NV12,
+ .fcc = V4L2_PIX_FMT_NV12,
+ },
+ {
+ .name = "MJPEG",
+ .guid = UVC_GUID_FORMAT_MJPEG,
+ .fcc = V4L2_PIX_FMT_MJPEG,
+ },
+ {
+ .name = "YVU 4:2:0 (YV12)",
+ .guid = UVC_GUID_FORMAT_YV12,
+ .fcc = V4L2_PIX_FMT_YVU420,
+ },
+ {
+ .name = "YUV 4:2:0 (I420)",
+ .guid = UVC_GUID_FORMAT_I420,
+ .fcc = V4L2_PIX_FMT_YUV420,
+ },
+ {
+ .name = "YUV 4:2:0 (M420)",
+ .guid = UVC_GUID_FORMAT_M420,
+ .fcc = V4L2_PIX_FMT_M420,
+ },
+ {
+ .name = "YUV 4:2:2 (UYVY)",
+ .guid = UVC_GUID_FORMAT_UYVY,
+ .fcc = V4L2_PIX_FMT_UYVY,
+ },
+ {
+ .name = "Greyscale 8-bit (Y800)",
+ .guid = UVC_GUID_FORMAT_Y800,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .name = "Greyscale 8-bit (Y8 )",
+ .guid = UVC_GUID_FORMAT_Y8,
+ .fcc = V4L2_PIX_FMT_GREY,
+ },
+ {
+ .name = "Greyscale 10-bit (Y10 )",
+ .guid = UVC_GUID_FORMAT_Y10,
+ .fcc = V4L2_PIX_FMT_Y10,
+ },
+ {
+ .name = "Greyscale 12-bit (Y12 )",
+ .guid = UVC_GUID_FORMAT_Y12,
+ .fcc = V4L2_PIX_FMT_Y12,
+ },
+ {
+ .name = "Greyscale 16-bit (Y16 )",
+ .guid = UVC_GUID_FORMAT_Y16,
+ .fcc = V4L2_PIX_FMT_Y16,
+ },
+ {
+ .name = "RGB Bayer",
+ .guid = UVC_GUID_FORMAT_BY8,
+ .fcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .name = "RGB565",
+ .guid = UVC_GUID_FORMAT_RGBP,
+ .fcc = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .name = "H.264",
+ .guid = UVC_GUID_FORMAT_H264,
+ .fcc = V4L2_PIX_FMT_H264,
+ },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
+ __u8 epaddr)
+{
+ struct usb_host_endpoint *ep;
+ unsigned int i;
+
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ ep = &alts->endpoint[i];
+ if (ep->desc.bEndpointAddress == epaddr)
+ return ep;
+ }
+
+ return NULL;
+}
+
+static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
+{
+ unsigned int len = ARRAY_SIZE(uvc_fmts);
+ unsigned int i;
+
+ for (i = 0; i < len; ++i) {
+ if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+ return &uvc_fmts[i];
+ }
+
+ return NULL;
+}
+
+static __u32 uvc_colorspace(const __u8 primaries)
+{
+ static const __u8 colorprimaries[] = {
+ 0,
+ V4L2_COLORSPACE_SRGB,
+ V4L2_COLORSPACE_470_SYSTEM_M,
+ V4L2_COLORSPACE_470_SYSTEM_BG,
+ V4L2_COLORSPACE_SMPTE170M,
+ V4L2_COLORSPACE_SMPTE240M,
+ };
+
+ if (primaries < ARRAY_SIZE(colorprimaries))
+ return colorprimaries[primaries];
+
+ return 0;
+}
+
+/* Simplify a fraction using a simple continued fraction decomposition. The
+ * idea here is to convert fractions such as 333333/10000000 to 1/30 using
+ * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
+ * arbitrary parameters to remove non-significative terms from the simple
+ * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
+ * respectively seems to give nice results.
+ */
+void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold)
+{
+ uint32_t *an;
+ uint32_t x, y, r;
+ unsigned int i, n;
+
+ an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
+ if (an == NULL)
+ return;
+
+ /* Convert the fraction to a simple continued fraction. See
+ * http://mathforum.org/dr.math/faq/faq.fractions.html
+ * Stop if the current term is bigger than or equal to the given
+ * threshold.
+ */
+ x = *numerator;
+ y = *denominator;
+
+ for (n = 0; n < n_terms && y != 0; ++n) {
+ an[n] = x / y;
+ if (an[n] >= threshold) {
+ if (n < 2)
+ n++;
+ break;
+ }
+
+ r = x - an[n] * y;
+ x = y;
+ y = r;
+ }
+
+ /* Expand the simple continued fraction back to an integer fraction. */
+ x = 0;
+ y = 1;
+
+ for (i = n; i > 0; --i) {
+ r = y;
+ y = an[i-1] * y + x;
+ x = r;
+ }
+
+ *numerator = y;
+ *denominator = x;
+ kfree(an);
+}
+
+/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
+ * to compute numerator / denominator * 10000000 using 32 bit fixed point
+ * arithmetic only.
+ */
+uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+{
+ uint32_t multiplier;
+
+ /* Saturate the result if the operation would overflow. */
+ if (denominator == 0 ||
+ numerator/denominator >= ((uint32_t)-1)/10000000)
+ return (uint32_t)-1;
+
+ /* Divide both the denominator and the multiplier by two until
+ * numerator * multiplier doesn't overflow. If anyone knows a better
+ * algorithm please let me know.
+ */
+ multiplier = 10000000;
+ while (numerator > ((uint32_t)-1)/multiplier) {
+ multiplier /= 2;
+ denominator /= 2;
+ }
+
+ return denominator ? numerator * multiplier / denominator : 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_entity *entity;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (entity->id == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
+ int id, struct uvc_entity *entity)
+{
+ unsigned int i;
+
+ if (entity == NULL)
+ entity = list_entry(&dev->entities, struct uvc_entity, list);
+
+ list_for_each_entry_continue(entity, &dev->entities, list) {
+ for (i = 0; i < entity->bNrInPins; ++i)
+ if (entity->baSourceID[i] == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
+{
+ struct uvc_streaming *stream;
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->header.bTerminalLink == id)
+ return stream;
+ }
+
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------
+ * Descriptors parsing
+ */
+
+static int uvc_parse_format(struct uvc_device *dev,
+ struct uvc_streaming *streaming, struct uvc_format *format,
+ __u32 **intervals, unsigned char *buffer, int buflen)
+{
+ struct usb_interface *intf = streaming->intf;
+ struct usb_host_interface *alts = intf->cur_altsetting;
+ struct uvc_format_desc *fmtdesc;
+ struct uvc_frame *frame;
+ const unsigned char *start = buffer;
+ unsigned int interval;
+ unsigned int i, n;
+ __u8 ftype;
+
+ format->type = buffer[2];
+ format->index = buffer[3];
+
+ switch (buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
+ if (buflen < n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Find the format descriptor from its GUID. */
+ fmtdesc = uvc_format_by_guid(&buffer[5]);
+
+ if (fmtdesc != NULL) {
+ strlcpy(format->name, fmtdesc->name,
+ sizeof format->name);
+ format->fcc = fmtdesc->fcc;
+ } else {
+ uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
+ &buffer[5]);
+ snprintf(format->name, sizeof(format->name), "%pUl\n",
+ &buffer[5]);
+ format->fcc = 0;
+ }
+
+ format->bpp = buffer[21];
+ if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
+ ftype = UVC_VS_FRAME_UNCOMPRESSED;
+ } else {
+ ftype = UVC_VS_FRAME_FRAME_BASED;
+ if (buffer[27])
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ }
+ break;
+
+ case UVC_VS_FORMAT_MJPEG:
+ if (buflen < 11) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ strlcpy(format->name, "MJPEG", sizeof format->name);
+ format->fcc = V4L2_PIX_FMT_MJPEG;
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ format->bpp = 0;
+ ftype = UVC_VS_FRAME_MJPEG;
+ break;
+
+ case UVC_VS_FORMAT_DV:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ switch (buffer[8] & 0x7f) {
+ case 0:
+ strlcpy(format->name, "SD-DV", sizeof format->name);
+ break;
+ case 1:
+ strlcpy(format->name, "SDL-DV", sizeof format->name);
+ break;
+ case 2:
+ strlcpy(format->name, "HD-DV", sizeof format->name);
+ break;
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d: unknown DV format %u\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[8]);
+ return -EINVAL;
+ }
+
+ strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
+ sizeof format->name);
+
+ format->fcc = V4L2_PIX_FMT_DV;
+ format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
+ format->bpp = 0;
+ ftype = 0;
+
+ /* Create a dummy frame descriptor. */
+ frame = &format->frame[0];
+ memset(&format->frame[0], 0, sizeof format->frame[0]);
+ frame->bFrameIntervalType = 1;
+ frame->dwDefaultFrameInterval = 1;
+ frame->dwFrameInterval = *intervals;
+ *(*intervals)++ = 1;
+ format->nframes = 1;
+ break;
+
+ case UVC_VS_FORMAT_MPEG2TS:
+ case UVC_VS_FORMAT_STREAM_BASED:
+ /* Not supported yet. */
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d unsupported format %u\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber,
+ buffer[2]);
+ return -EINVAL;
+ }
+
+ uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
+ * based formats have frame descriptors.
+ */
+ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == ftype) {
+ frame = &format->frame[format->nframes];
+ if (ftype != UVC_VS_FRAME_FRAME_BASED)
+ n = buflen > 25 ? buffer[25] : 0;
+ else
+ n = buflen > 21 ? buffer[21] : 0;
+
+ n = n ? n : 3;
+
+ if (buflen < 26 + 4*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FRAME error\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ frame->bFrameIndex = buffer[3];
+ frame->bmCapabilities = buffer[4];
+ frame->wWidth = get_unaligned_le16(&buffer[5]);
+ frame->wHeight = get_unaligned_le16(&buffer[7]);
+ frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
+ frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
+ if (ftype != UVC_VS_FRAME_FRAME_BASED) {
+ frame->dwMaxVideoFrameBufferSize =
+ get_unaligned_le32(&buffer[17]);
+ frame->dwDefaultFrameInterval =
+ get_unaligned_le32(&buffer[21]);
+ frame->bFrameIntervalType = buffer[25];
+ } else {
+ frame->dwMaxVideoFrameBufferSize = 0;
+ frame->dwDefaultFrameInterval =
+ get_unaligned_le32(&buffer[17]);
+ frame->bFrameIntervalType = buffer[21];
+ }
+ frame->dwFrameInterval = *intervals;
+
+ /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
+ * completely. Observed behaviours range from setting the
+ * value to 1.1x the actual frame size to hardwiring the
+ * 16 low bits to 0. This results in a higher than necessary
+ * memory usage as well as a wrong image size information. For
+ * uncompressed formats this can be fixed by computing the
+ * value from the frame size.
+ */
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
+ frame->dwMaxVideoFrameBufferSize = format->bpp
+ * frame->wWidth * frame->wHeight / 8;
+
+ /* Some bogus devices report dwMinFrameInterval equal to
+ * dwMaxFrameInterval and have dwFrameIntervalStep set to
+ * zero. Setting all null intervals to 1 fixes the problem and
+ * some other divisions by zero that could happen.
+ */
+ for (i = 0; i < n; ++i) {
+ interval = get_unaligned_le32(&buffer[26+4*i]);
+ *(*intervals)++ = interval ? interval : 1;
+ }
+
+ /* Make sure that the default frame interval stays between
+ * the boundaries.
+ */
+ n -= frame->bFrameIntervalType ? 1 : 2;
+ frame->dwDefaultFrameInterval =
+ min(frame->dwFrameInterval[n],
+ max(frame->dwFrameInterval[0],
+ frame->dwDefaultFrameInterval));
+
+ if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
+ frame->bFrameIntervalType = 1;
+ frame->dwFrameInterval[0] =
+ frame->dwDefaultFrameInterval;
+ }
+
+ uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+ frame->wWidth, frame->wHeight,
+ 10000000/frame->dwDefaultFrameInterval,
+ (100000000/frame->dwDefaultFrameInterval)%10);
+
+ format->nframes++;
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
+ buffer[2] == UVC_VS_COLORFORMAT) {
+ if (buflen < 6) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d COLORFORMAT error\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ format->colorspace = uvc_colorspace(buffer[3]);
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ return buffer - start;
+}
+
+static int uvc_parse_streaming(struct uvc_device *dev,
+ struct usb_interface *intf)
+{
+ struct uvc_streaming *streaming = NULL;
+ struct uvc_format *format;
+ struct uvc_frame *frame;
+ struct usb_host_interface *alts = &intf->altsetting[0];
+ unsigned char *_buffer, *buffer = alts->extra;
+ int _buflen, buflen = alts->extralen;
+ unsigned int nformats = 0, nframes = 0, nintervals = 0;
+ unsigned int size, i, n, p;
+ __u32 *interval;
+ __u16 psize;
+ int ret = -EINVAL;
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass
+ != UVC_SC_VIDEOSTREAMING) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
+ "video streaming interface\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
+ "claimed\n", dev->udev->devnum,
+ intf->altsetting[0].desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
+ if (streaming == NULL) {
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ return -EINVAL;
+ }
+
+ mutex_init(&streaming->mutex);
+ streaming->dev = dev;
+ streaming->intf = usb_get_intf(intf);
+ streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* The Pico iMage webcam has its class-specific interface descriptors
+ * after the endpoint descriptors.
+ */
+ if (buflen == 0) {
+ for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+ struct usb_host_endpoint *ep = &alts->endpoint[i];
+
+ if (ep->extralen == 0)
+ continue;
+
+ if (ep->extralen > 2 &&
+ ep->extra[1] == USB_DT_CS_INTERFACE) {
+ uvc_trace(UVC_TRACE_DESCR, "trying extra data "
+ "from endpoint %u.\n", i);
+ buffer = alts->endpoint[i].extra;
+ buflen = alts->endpoint[i].extralen;
+ break;
+ }
+ }
+ }
+
+ /* Skip the standard interface descriptors. */
+ while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen <= 2) {
+ uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
+ "interface descriptors found.\n");
+ goto error;
+ }
+
+ /* Parse the header descriptor. */
+ switch (buffer[2]) {
+ case UVC_VS_OUTPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ size = 9;
+ break;
+
+ case UVC_VS_INPUT_HEADER:
+ streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ size = 13;
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d HEADER descriptor not found.\n", dev->udev->devnum,
+ alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ p = buflen >= 4 ? buffer[3] : 0;
+ n = buflen >= size ? buffer[size-1] : 0;
+
+ if (buflen < size + p*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d HEADER descriptor is invalid.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ streaming->header.bNumFormats = p;
+ streaming->header.bEndpointAddress = buffer[6];
+ if (buffer[2] == UVC_VS_INPUT_HEADER) {
+ streaming->header.bmInfo = buffer[7];
+ streaming->header.bTerminalLink = buffer[8];
+ streaming->header.bStillCaptureMethod = buffer[9];
+ streaming->header.bTriggerSupport = buffer[10];
+ streaming->header.bTriggerUsage = buffer[11];
+ } else {
+ streaming->header.bTerminalLink = buffer[7];
+ }
+ streaming->header.bControlSize = n;
+
+ streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
+ GFP_KERNEL);
+ if (streaming->header.bmaControls == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+
+ _buffer = buffer;
+ _buflen = buflen;
+
+ /* Count the format and frame descriptors. */
+ while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
+ switch (_buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_MJPEG:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ nformats++;
+ break;
+
+ case UVC_VS_FORMAT_DV:
+ /* DV format has no frame descriptor. We will create a
+ * dummy frame descriptor with a dummy frame interval.
+ */
+ nformats++;
+ nframes++;
+ nintervals++;
+ break;
+
+ case UVC_VS_FORMAT_MPEG2TS:
+ case UVC_VS_FORMAT_STREAM_BASED:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+ "interface %d FORMAT %u is not supported.\n",
+ dev->udev->devnum,
+ alts->desc.bInterfaceNumber, _buffer[2]);
+ break;
+
+ case UVC_VS_FRAME_UNCOMPRESSED:
+ case UVC_VS_FRAME_MJPEG:
+ nframes++;
+ if (_buflen > 25)
+ nintervals += _buffer[25] ? _buffer[25] : 3;
+ break;
+
+ case UVC_VS_FRAME_FRAME_BASED:
+ nframes++;
+ if (_buflen > 21)
+ nintervals += _buffer[21] ? _buffer[21] : 3;
+ break;
+ }
+
+ _buflen -= _buffer[0];
+ _buffer += _buffer[0];
+ }
+
+ if (nformats == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has no supported formats defined.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber);
+ goto error;
+ }
+
+ size = nformats * sizeof *format + nframes * sizeof *frame
+ + nintervals * sizeof *interval;
+ format = kzalloc(size, GFP_KERNEL);
+ if (format == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ frame = (struct uvc_frame *)&format[nformats];
+ interval = (__u32 *)&frame[nframes];
+
+ streaming->format = format;
+ streaming->nformats = nformats;
+
+ /* Parse the format descriptors. */
+ while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
+ switch (buffer[2]) {
+ case UVC_VS_FORMAT_UNCOMPRESSED:
+ case UVC_VS_FORMAT_MJPEG:
+ case UVC_VS_FORMAT_DV:
+ case UVC_VS_FORMAT_FRAME_BASED:
+ format->frame = frame;
+ ret = uvc_parse_format(dev, streaming, format,
+ &interval, buffer, buflen);
+ if (ret < 0)
+ goto error;
+
+ frame += format->nframes;
+ format++;
+
+ buflen -= ret;
+ buffer += ret;
+ continue;
+
+ default:
+ break;
+ }
+
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ if (buflen)
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+ "%d has %u bytes of trailing descriptor garbage.\n",
+ dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
+
+ /* Parse the alternate settings to find the maximum bandwidth. */
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ struct usb_host_endpoint *ep;
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ streaming->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ if (psize > streaming->maxpsize)
+ streaming->maxpsize = psize;
+ }
+
+ list_add_tail(&streaming->list, &dev->streams);
+ return 0;
+
+error:
+ usb_driver_release_interface(&uvc_driver.driver, intf);
+ usb_put_intf(intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ return ret;
+}
+
+static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
+ unsigned int num_pads, unsigned int extra_size)
+{
+ struct uvc_entity *entity;
+ unsigned int num_inputs;
+ unsigned int size;
+ unsigned int i;
+
+ extra_size = ALIGN(extra_size, sizeof(*entity->pads));
+ num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
+ size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
+ + num_inputs;
+ entity = kzalloc(size, GFP_KERNEL);
+ if (entity == NULL)
+ return NULL;
+
+ entity->id = id;
+ entity->type = type;
+
+ entity->num_links = 0;
+ entity->num_pads = num_pads;
+ entity->pads = ((void *)(entity + 1)) + extra_size;
+
+ for (i = 0; i < num_inputs; ++i)
+ entity->pads[i].flags = MEDIA_PAD_FL_SINK;
+ if (!UVC_ENTITY_IS_OTERM(entity))
+ entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
+
+ entity->bNrInPins = num_inputs;
+ entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
+
+ return entity;
+}
+
+/* Parse vendor-specific extensions. */
+static int uvc_parse_vendor_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ struct uvc_entity *unit;
+ unsigned int n, p;
+ int handled = 0;
+
+ switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
+ case 0x046d: /* Logitech */
+ if (buffer[1] != 0x41 || buffer[2] != 0x01)
+ break;
+
+ /* Logitech implements several vendor specific functions
+ * through vendor specific extension units (LXU).
+ *
+ * The LXU descriptors are similar to XU descriptors
+ * (see "USB Device Video Class for Video Devices", section
+ * 3.7.2.6 "Extension Unit Descriptor") with the following
+ * differences:
+ *
+ * ----------------------------------------------------------
+ * 0 bLength 1 Number
+ * Size of this descriptor, in bytes: 24+p+n*2
+ * ----------------------------------------------------------
+ * 23+p+n bmControlsType N Bitmap
+ * Individual bits in the set are defined:
+ * 0: Absolute
+ * 1: Relative
+ *
+ * This bitset is mapped exactly the same as bmControls.
+ * ----------------------------------------------------------
+ * 23+p+n*2 bReserved 1 Boolean
+ * ----------------------------------------------------------
+ * 24+p+n*2 iExtension 1 Index
+ * Index of a string descriptor that describes this
+ * extension unit.
+ * ----------------------------------------------------------
+ */
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 25 + p ? buffer[22+p] : 0;
+
+ if (buflen < 25 + p + 2*n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ break;
+ }
+
+ unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
+ p + 1, 2*n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ memcpy(unit->baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
+ unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
+ + n;
+ memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
+
+ if (buffer[24+p+2*n] != 0)
+ usb_string(udev, buffer[24+p+2*n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ handled = 1;
+ break;
+ }
+
+ return handled;
+}
+
+static int uvc_parse_standard_control(struct uvc_device *dev,
+ const unsigned char *buffer, int buflen)
+{
+ struct usb_device *udev = dev->udev;
+ struct uvc_entity *unit, *term;
+ struct usb_interface *intf;
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned int i, n, p, len;
+ __u16 type;
+
+ switch (buffer[2]) {
+ case UVC_VC_HEADER:
+ n = buflen >= 12 ? buffer[11] : 0;
+
+ if (buflen < 12 || buflen < 12 + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d HEADER error\n", udev->devnum,
+ alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ dev->uvc_version = get_unaligned_le16(&buffer[3]);
+ dev->clock_frequency = get_unaligned_le32(&buffer[7]);
+
+ /* Parse all USB Video Streaming interfaces. */
+ for (i = 0; i < n; ++i) {
+ intf = usb_ifnum_to_if(udev, buffer[12+i]);
+ if (intf == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d "
+ "interface %d doesn't exists\n",
+ udev->devnum, i);
+ continue;
+ }
+
+ uvc_parse_streaming(dev, intf);
+ }
+ break;
+
+ case UVC_VC_INPUT_TERMINAL:
+ if (buflen < 8) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = get_unaligned_le16(&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber,
+ buffer[3], type);
+ return 0;
+ }
+
+ n = 0;
+ p = 0;
+ len = 8;
+
+ if (type == UVC_ITT_CAMERA) {
+ n = buflen >= 15 ? buffer[14] : 0;
+ len = 15;
+
+ } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
+ n = buflen >= 9 ? buffer[8] : 0;
+ p = buflen >= 10 + n ? buffer[9+n] : 0;
+ len = 10;
+ }
+
+ if (buflen < len + n + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d INPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
+ 1, n + p);
+ if (term == NULL)
+ return -ENOMEM;
+
+ if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
+ term->camera.bControlSize = n;
+ term->camera.bmControls = (__u8 *)term + sizeof *term;
+ term->camera.wObjectiveFocalLengthMin =
+ get_unaligned_le16(&buffer[8]);
+ term->camera.wObjectiveFocalLengthMax =
+ get_unaligned_le16(&buffer[10]);
+ term->camera.wOcularFocalLength =
+ get_unaligned_le16(&buffer[12]);
+ memcpy(term->camera.bmControls, &buffer[15], n);
+ } else if (UVC_ENTITY_TYPE(term) ==
+ UVC_ITT_MEDIA_TRANSPORT_INPUT) {
+ term->media.bControlSize = n;
+ term->media.bmControls = (__u8 *)term + sizeof *term;
+ term->media.bTransportModeSize = p;
+ term->media.bmTransportModes = (__u8 *)term
+ + sizeof *term + n;
+ memcpy(term->media.bmControls, &buffer[9], n);
+ memcpy(term->media.bmTransportModes, &buffer[10+n], p);
+ }
+
+ if (buffer[7] != 0)
+ usb_string(udev, buffer[7], term->name,
+ sizeof term->name);
+ else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
+ sprintf(term->name, "Camera %u", buffer[3]);
+ else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
+ sprintf(term->name, "Media %u", buffer[3]);
+ else
+ sprintf(term->name, "Input %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case UVC_VC_OUTPUT_TERMINAL:
+ if (buflen < 9) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ /* Make sure the terminal type MSB is not null, otherwise it
+ * could be confused with a unit.
+ */
+ type = get_unaligned_le16(&buffer[4]);
+ if ((type & 0xff00) == 0) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d OUTPUT_TERMINAL %d has invalid "
+ "type 0x%04x, skipping\n", udev->devnum,
+ alts->desc.bInterfaceNumber, buffer[3], type);
+ return 0;
+ }
+
+ term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
+ 1, 0);
+ if (term == NULL)
+ return -ENOMEM;
+
+ memcpy(term->baSourceID, &buffer[7], 1);
+
+ if (buffer[8] != 0)
+ usb_string(udev, buffer[8], term->name,
+ sizeof term->name);
+ else
+ sprintf(term->name, "Output %u", buffer[3]);
+
+ list_add_tail(&term->list, &dev->entities);
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ p = buflen >= 5 ? buffer[4] : 0;
+
+ if (buflen < 5 || buflen < 6 + p) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d SELECTOR_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->baSourceID, &buffer[5], p);
+
+ if (buffer[5+p] != 0)
+ usb_string(udev, buffer[5+p], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Selector %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case UVC_VC_PROCESSING_UNIT:
+ n = buflen >= 8 ? buffer[7] : 0;
+ p = dev->uvc_version >= 0x0110 ? 10 : 9;
+
+ if (buflen < p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d PROCESSING_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->baSourceID, &buffer[4], 1);
+ unit->processing.wMaxMultiplier =
+ get_unaligned_le16(&buffer[5]);
+ unit->processing.bControlSize = buffer[7];
+ unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->processing.bmControls, &buffer[8], n);
+ if (dev->uvc_version >= 0x0110)
+ unit->processing.bmVideoStandards = buffer[9+n];
+
+ if (buffer[8+n] != 0)
+ usb_string(udev, buffer[8+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Processing %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ case UVC_VC_EXTENSION_UNIT:
+ p = buflen >= 22 ? buffer[21] : 0;
+ n = buflen >= 24 + p ? buffer[22+p] : 0;
+
+ if (buflen < 24 + p + n) {
+ uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+ "interface %d EXTENSION_UNIT error\n",
+ udev->devnum, alts->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
+ if (unit == NULL)
+ return -ENOMEM;
+
+ memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
+ unit->extension.bNumControls = buffer[20];
+ memcpy(unit->baSourceID, &buffer[22], p);
+ unit->extension.bControlSize = buffer[22+p];
+ unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
+ memcpy(unit->extension.bmControls, &buffer[23+p], n);
+
+ if (buffer[23+p+n] != 0)
+ usb_string(udev, buffer[23+p+n], unit->name,
+ sizeof unit->name);
+ else
+ sprintf(unit->name, "Extension %u", buffer[3]);
+
+ list_add_tail(&unit->list, &dev->entities);
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
+ "descriptor (%u)\n", buffer[2]);
+ break;
+ }
+
+ return 0;
+}
+
+static int uvc_parse_control(struct uvc_device *dev)
+{
+ struct usb_host_interface *alts = dev->intf->cur_altsetting;
+ unsigned char *buffer = alts->extra;
+ int buflen = alts->extralen;
+ int ret;
+
+ /* Parse the default alternate setting only, as the UVC specification
+ * defines a single alternate setting, the default alternate setting
+ * zero.
+ */
+
+ while (buflen > 2) {
+ if (uvc_parse_vendor_control(dev, buffer, buflen) ||
+ buffer[1] != USB_DT_CS_INTERFACE)
+ goto next_descriptor;
+
+ if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
+ return ret;
+
+next_descriptor:
+ buflen -= buffer[0];
+ buffer += buffer[0];
+ }
+
+ /* Check if the optional status endpoint is present. Built-in iSight
+ * webcams have an interrupt endpoint but spit proprietary data that
+ * don't conform to the UVC status endpoint messages. Don't try to
+ * handle the interrupt endpoint for those cameras.
+ */
+ if (alts->desc.bNumEndpoints == 1 &&
+ !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
+ struct usb_host_endpoint *ep = &alts->endpoint[0];
+ struct usb_endpoint_descriptor *desc = &ep->desc;
+
+ if (usb_endpoint_is_int_in(desc) &&
+ le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
+ desc->bInterval != 0) {
+ uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
+ "(addr %02x).\n", desc->bEndpointAddress);
+ dev->int_ep = ep;
+ }
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * UVC device scan
+ */
+
+/*
+ * Scan the UVC descriptors to locate a chain starting at an Output Terminal
+ * and containing the following units:
+ *
+ * - one or more Output Terminals (USB Streaming or Display)
+ * - zero or one Processing Unit
+ * - zero, one or more single-input Selector Units
+ * - zero or one multiple-input Selector Units, provided all inputs are
+ * connected to input terminals
+ * - zero, one or mode single-input Extension Units
+ * - one or more Input Terminals (Camera, External or USB Streaming)
+ *
+ * The terminal and units must match on of the following structures:
+ *
+ * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
+ * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
+ * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
+ *
+ * +---------+ +---------+ -> OTT_*(0)
+ * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
+ * +---------+ +---------+ -> OTT_*(n)
+ *
+ * The Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported. Single-input Selector Units are ignored.
+ */
+static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
+ struct uvc_entity *entity)
+{
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_VC_EXTENSION_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- XU %d", entity->id);
+
+ if (entity->bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
+ "than 1 input pin.\n", entity->id);
+ return -1;
+ }
+
+ break;
+
+ case UVC_VC_PROCESSING_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- PU %d", entity->id);
+
+ if (chain->processing != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple "
+ "Processing Units in chain.\n");
+ return -1;
+ }
+
+ chain->processing = entity;
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- SU %d", entity->id);
+
+ /* Single-input selector units are ignored. */
+ if (entity->bNrInPins == 1)
+ break;
+
+ if (chain->selector != NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
+ "Units in chain.\n");
+ return -1;
+ }
+
+ chain->selector = entity;
+ break;
+
+ case UVC_ITT_VENDOR_SPECIFIC:
+ case UVC_ITT_CAMERA:
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+
+ break;
+
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" OT %d", entity->id);
+
+ break;
+
+ case UVC_TT_STREAMING:
+ if (UVC_ENTITY_IS_ITERM(entity)) {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT %d\n", entity->id);
+ } else {
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" OT %d", entity->id);
+ }
+
+ break;
+
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
+ "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+ return -1;
+ }
+
+ list_add_tail(&entity->chain, &chain->entities);
+ return 0;
+}
+
+static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
+ struct uvc_entity *entity, struct uvc_entity *prev)
+{
+ struct uvc_entity *forward;
+ int found;
+
+ /* Forward scan */
+ forward = NULL;
+ found = 0;
+
+ while (1) {
+ forward = uvc_entity_by_reference(chain->dev, entity->id,
+ forward);
+ if (forward == NULL)
+ break;
+ if (forward == prev)
+ continue;
+
+ switch (UVC_ENTITY_TYPE(forward)) {
+ case UVC_VC_EXTENSION_UNIT:
+ if (forward->bNrInPins != 1) {
+ uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
+ "has more than 1 input pin.\n",
+ entity->id);
+ return -EINVAL;
+ }
+
+ list_add_tail(&forward->chain, &chain->entities);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
+
+ printk(" XU %d", forward->id);
+ found = 1;
+ }
+ break;
+
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ if (UVC_ENTITY_IS_ITERM(forward)) {
+ uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+ "terminal %u.\n", forward->id);
+ return -EINVAL;
+ }
+
+ list_add_tail(&forward->chain, &chain->entities);
+ if (uvc_trace_param & UVC_TRACE_PROBE) {
+ if (!found)
+ printk(" (->");
+
+ printk(" OT %d", forward->id);
+ found = 1;
+ }
+ break;
+ }
+ }
+ if (found)
+ printk(")");
+
+ return 0;
+}
+
+static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
+ struct uvc_entity **_entity)
+{
+ struct uvc_entity *entity = *_entity;
+ struct uvc_entity *term;
+ int id = -EINVAL, i;
+
+ switch (UVC_ENTITY_TYPE(entity)) {
+ case UVC_VC_EXTENSION_UNIT:
+ case UVC_VC_PROCESSING_UNIT:
+ id = entity->baSourceID[0];
+ break;
+
+ case UVC_VC_SELECTOR_UNIT:
+ /* Single-input selector units are ignored. */
+ if (entity->bNrInPins == 1) {
+ id = entity->baSourceID[0];
+ break;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" <- IT");
+
+ chain->selector = entity;
+ for (i = 0; i < entity->bNrInPins; ++i) {
+ id = entity->baSourceID[i];
+ term = uvc_entity_by_id(chain->dev, id);
+ if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
+ uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
+ "input %d isn't connected to an "
+ "input terminal\n", entity->id, i);
+ return -1;
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk(" %d", term->id);
+
+ list_add_tail(&term->chain, &chain->entities);
+ uvc_scan_chain_forward(chain, term, entity);
+ }
+
+ if (uvc_trace_param & UVC_TRACE_PROBE)
+ printk("\n");
+
+ id = 0;
+ break;
+
+ case UVC_ITT_VENDOR_SPECIFIC:
+ case UVC_ITT_CAMERA:
+ case UVC_ITT_MEDIA_TRANSPORT_INPUT:
+ case UVC_OTT_VENDOR_SPECIFIC:
+ case UVC_OTT_DISPLAY:
+ case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
+ case UVC_TT_STREAMING:
+ id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
+ break;
+ }
+
+ if (id <= 0) {
+ *_entity = NULL;
+ return id;
+ }
+
+ entity = uvc_entity_by_id(chain->dev, id);
+ if (entity == NULL) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "unknown entity %d.\n", id);
+ return -EINVAL;
+ }
+
+ *_entity = entity;
+ return 0;
+}
+
+static int uvc_scan_chain(struct uvc_video_chain *chain,
+ struct uvc_entity *term)
+{
+ struct uvc_entity *entity, *prev;
+
+ uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
+
+ entity = term;
+ prev = NULL;
+
+ while (entity != NULL) {
+ /* Entity must not be part of an existing chain */
+ if (entity->chain.next || entity->chain.prev) {
+ uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+ "entity %d already in chain.\n", entity->id);
+ return -EINVAL;
+ }
+
+ /* Process entity */
+ if (uvc_scan_chain_entity(chain, entity) < 0)
+ return -EINVAL;
+
+ /* Forward scan */
+ if (uvc_scan_chain_forward(chain, entity, prev) < 0)
+ return -EINVAL;
+
+ /* Backward scan */
+ prev = entity;
+ if (uvc_scan_chain_backward(chain, &entity) < 0)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
+ char *buffer)
+{
+ struct uvc_entity *term;
+ unsigned int nterms = 0;
+ char *p = buffer;
+
+ list_for_each_entry(term, terms, chain) {
+ if (!UVC_ENTITY_IS_TERM(term) ||
+ UVC_TERM_DIRECTION(term) != dir)
+ continue;
+
+ if (nterms)
+ p += sprintf(p, ",");
+ if (++nterms >= 4) {
+ p += sprintf(p, "...");
+ break;
+ }
+ p += sprintf(p, "%u", term->id);
+ }
+
+ return p - buffer;
+}
+
+static const char *uvc_print_chain(struct uvc_video_chain *chain)
+{
+ static char buffer[43];
+ char *p = buffer;
+
+ p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
+ p += sprintf(p, " -> ");
+ uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
+
+ return buffer;
+}
+
+/*
+ * Scan the device for video chains and register video devices.
+ *
+ * Chains are scanned starting at their output terminals and walked backwards.
+ */
+static int uvc_scan_device(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ struct uvc_entity *term;
+
+ list_for_each_entry(term, &dev->entities, list) {
+ if (!UVC_ENTITY_IS_OTERM(term))
+ continue;
+
+ /* If the terminal is already included in a chain, skip it.
+ * This can happen for chains that have multiple output
+ * terminals, where all output terminals beside the first one
+ * will be inserted in the chain in forward scans.
+ */
+ if (term->chain.next || term->chain.prev)
+ continue;
+
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&chain->entities);
+ mutex_init(&chain->ctrl_mutex);
+ chain->dev = dev;
+
+ if (uvc_scan_chain(chain, term) < 0) {
+ kfree(chain);
+ continue;
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
+ uvc_print_chain(chain));
+
+ list_add_tail(&chain->list, &dev->chains);
+ }
+
+ if (list_empty(&dev->chains)) {
+ uvc_printk(KERN_INFO, "No valid video chain found.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device registration and unregistration
+ */
+
+/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+static void uvc_delete(struct uvc_device *dev)
+{
+ struct list_head *p, *n;
+
+ usb_put_intf(dev->intf);
+ usb_put_dev(dev->udev);
+
+ uvc_status_cleanup(dev);
+ uvc_ctrl_cleanup_device(dev);
+
+ if (dev->vdev.dev)
+ v4l2_device_unregister(&dev->vdev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ if (media_devnode_is_registered(&dev->mdev.devnode))
+ media_device_unregister(&dev->mdev);
+#endif
+
+ list_for_each_safe(p, n, &dev->chains) {
+ struct uvc_video_chain *chain;
+ chain = list_entry(p, struct uvc_video_chain, list);
+ kfree(chain);
+ }
+
+ list_for_each_safe(p, n, &dev->entities) {
+ struct uvc_entity *entity;
+ entity = list_entry(p, struct uvc_entity, list);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ uvc_mc_cleanup_entity(entity);
+#endif
+ if (entity->vdev) {
+ video_device_release(entity->vdev);
+ entity->vdev = NULL;
+ }
+ kfree(entity);
+ }
+
+ list_for_each_safe(p, n, &dev->streams) {
+ struct uvc_streaming *streaming;
+ streaming = list_entry(p, struct uvc_streaming, list);
+ usb_driver_release_interface(&uvc_driver.driver,
+ streaming->intf);
+ usb_put_intf(streaming->intf);
+ kfree(streaming->format);
+ kfree(streaming->header.bmaControls);
+ kfree(streaming);
+ }
+
+ kfree(dev);
+}
+
+static void uvc_release(struct video_device *vdev)
+{
+ struct uvc_streaming *stream = video_get_drvdata(vdev);
+ struct uvc_device *dev = stream->dev;
+
+ /* Decrement the registered streams count and delete the device when it
+ * reaches zero.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
+}
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+ struct uvc_streaming *stream;
+
+ /* Unregistering all video devices might result in uvc_delete() being
+ * called from inside the loop if there's no open file handle. To avoid
+ * that, increment the stream count before iterating over the streams
+ * and decrement it when done.
+ */
+ atomic_inc(&dev->nstreams);
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->vdev == NULL)
+ continue;
+
+ video_unregister_device(stream->vdev);
+ stream->vdev = NULL;
+
+ uvc_debugfs_cleanup_stream(stream);
+ }
+
+ /* Decrement the stream count and call uvc_delete explicitly if there
+ * are no stream left.
+ */
+ if (atomic_dec_and_test(&dev->nstreams))
+ uvc_delete(dev);
+}
+
+static int uvc_register_video(struct uvc_device *dev,
+ struct uvc_streaming *stream)
+{
+ struct video_device *vdev;
+ int ret;
+
+ /* Initialize the streaming interface with default streaming
+ * parameters.
+ */
+ ret = uvc_video_init(stream);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to initialize the device "
+ "(%d).\n", ret);
+ return ret;
+ }
+
+ uvc_debugfs_init_stream(stream);
+
+ /* Register the device with V4L. */
+ vdev = video_device_alloc();
+ if (vdev == NULL) {
+ uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
+ ret);
+ return -ENOMEM;
+ }
+
+ /* We already hold a reference to dev->udev. The video device will be
+ * unregistered before the reference is released, so we don't need to
+ * get another one.
+ */
+ vdev->v4l2_dev = &dev->vdev;
+ vdev->fops = &uvc_fops;
+ vdev->release = uvc_release;
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ vdev->vfl_dir = VFL_DIR_TX;
+ strlcpy(vdev->name, dev->name, sizeof vdev->name);
+
+ /* Set the driver data before calling video_register_device, otherwise
+ * uvc_v4l2_open might race us.
+ */
+ stream->vdev = vdev;
+ video_set_drvdata(vdev, stream);
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
+ ret);
+ stream->vdev = NULL;
+ video_device_release(vdev);
+ return ret;
+ }
+
+ atomic_inc(&dev->nstreams);
+ return 0;
+}
+
+/*
+ * Register all video devices in all chains.
+ */
+static int uvc_register_terms(struct uvc_device *dev,
+ struct uvc_video_chain *chain)
+{
+ struct uvc_streaming *stream;
+ struct uvc_entity *term;
+ int ret;
+
+ list_for_each_entry(term, &chain->entities, chain) {
+ if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
+ continue;
+
+ stream = uvc_stream_by_id(dev, term->id);
+ if (stream == NULL) {
+ uvc_printk(KERN_INFO, "No streaming interface found "
+ "for terminal %u.", term->id);
+ continue;
+ }
+
+ stream->chain = chain;
+ ret = uvc_register_video(dev, stream);
+ if (ret < 0)
+ return ret;
+
+ term->vdev = stream->vdev;
+ }
+
+ return 0;
+}
+
+static int uvc_register_chains(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ int ret;
+
+ list_for_each_entry(chain, &dev->chains, list) {
+ ret = uvc_register_terms(dev, chain);
+ if (ret < 0)
+ return ret;
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+ ret = uvc_mc_register_entities(chain);
+ if (ret < 0) {
+ uvc_printk(KERN_INFO, "Failed to register entites "
+ "(%d).\n", ret);
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe, disconnect, suspend and resume
+ */
+
+static int uvc_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct uvc_device *dev;
+ int ret;
+
+ if (id->idVendor && id->idProduct)
+ uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
+ "(%04x:%04x)\n", udev->devpath, id->idVendor,
+ id->idProduct);
+ else
+ uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
+ udev->devpath);
+
+ /* Allocate memory for the device and initialize it. */
+ if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dev->entities);
+ INIT_LIST_HEAD(&dev->chains);
+ INIT_LIST_HEAD(&dev->streams);
+ atomic_set(&dev->nstreams, 0);
+ atomic_set(&dev->users, 0);
+ atomic_set(&dev->nmappings, 0);
+
+ dev->udev = usb_get_dev(udev);
+ dev->intf = usb_get_intf(intf);
+ dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+ dev->quirks = (uvc_quirks_param == -1)
+ ? id->driver_info : uvc_quirks_param;
+
+ if (udev->product != NULL)
+ strlcpy(dev->name, udev->product, sizeof dev->name);
+ else
+ snprintf(dev->name, sizeof dev->name,
+ "UVC Camera (%04x:%04x)",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /* Parse the Video Class control descriptor. */
+ if (uvc_parse_control(dev) < 0) {
+ uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
+ "descriptors.\n");
+ goto error;
+ }
+
+ uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
+ dev->uvc_version >> 8, dev->uvc_version & 0xff,
+ udev->product ? udev->product : "<unnamed>",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ if (dev->quirks != id->driver_info) {
+ uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
+ "parameter for testing purpose.\n", dev->quirks);
+ uvc_printk(KERN_INFO, "Please report required quirks to the "
+ "linux-uvc-devel mailing list.\n");
+ }
+
+ /* Register the media and V4L2 devices. */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ dev->mdev.dev = &intf->dev;
+ strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
+ if (udev->serial)
+ strlcpy(dev->mdev.serial, udev->serial,
+ sizeof(dev->mdev.serial));
+ strcpy(dev->mdev.bus_info, udev->devpath);
+ dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
+ dev->mdev.driver_version = LINUX_VERSION_CODE;
+ if (media_device_register(&dev->mdev) < 0)
+ goto error;
+
+ dev->vdev.mdev = &dev->mdev;
+#endif
+ if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
+ goto error;
+
+ /* Initialize controls. */
+ if (uvc_ctrl_init_device(dev) < 0)
+ goto error;
+
+ /* Scan the device for video chains. */
+ if (uvc_scan_device(dev) < 0)
+ goto error;
+
+ /* Register video device nodes. */
+ if (uvc_register_chains(dev) < 0)
+ goto error;
+
+ /* Save our data pointer in the interface data. */
+ usb_set_intfdata(intf, dev);
+
+ /* Initialize the interrupt URB. */
+ if ((ret = uvc_status_init(dev)) < 0) {
+ uvc_printk(KERN_INFO, "Unable to initialize the status "
+ "endpoint (%d), status interrupt will not be "
+ "supported.\n", ret);
+ }
+
+ uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+ usb_enable_autosuspend(udev);
+ return 0;
+
+error:
+ uvc_unregister_video(dev);
+ return -ENODEV;
+}
+
+static void uvc_disconnect(struct usb_interface *intf)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+
+ /* Set the USB interface data to NULL. This can be done outside the
+ * lock, as there's no other reader.
+ */
+ usb_set_intfdata(intf, NULL);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOSTREAMING)
+ return;
+
+ dev->state |= UVC_DEV_DISCONNECTED;
+
+ uvc_unregister_video(dev);
+}
+
+static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* Controls are cached on the fly so they don't need to be saved. */
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOCONTROL)
+ return uvc_status_suspend(dev);
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_suspend(stream);
+ }
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
+}
+
+static int __uvc_resume(struct usb_interface *intf, int reset)
+{
+ struct uvc_device *dev = usb_get_intfdata(intf);
+ struct uvc_streaming *stream;
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceSubClass ==
+ UVC_SC_VIDEOCONTROL) {
+ if (reset) {
+ int ret = uvc_ctrl_resume_device(dev);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return uvc_status_resume(dev);
+ }
+
+ list_for_each_entry(stream, &dev->streams, list) {
+ if (stream->intf == intf)
+ return uvc_video_resume(stream, reset);
+ }
+
+ uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
+ "mismatch.\n");
+ return -EINVAL;
+}
+
+static int uvc_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 0);
+}
+
+static int uvc_reset_resume(struct usb_interface *intf)
+{
+ return __uvc_resume(intf, 1);
+}
+
+/* ------------------------------------------------------------------------
+ * Module parameters
+ */
+
+static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
+{
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ return sprintf(buffer, "CLOCK_MONOTONIC");
+ else
+ return sprintf(buffer, "CLOCK_REALTIME");
+}
+
+static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
+{
+ if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
+ val += strlen("clock_");
+
+ if (strcasecmp(val, "monotonic") == 0)
+ uvc_clock_param = CLOCK_MONOTONIC;
+ else if (strcasecmp(val, "realtime") == 0)
+ uvc_clock_param = CLOCK_REALTIME;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
+ &uvc_clock_param, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
+module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(quirks, "Forced device quirks");
+module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
+
+/* ------------------------------------------------------------------------
+ * Driver initialization and cleanup
+ */
+
+/*
+ * The Logitech cameras listed below have their interface class set to
+ * VENDOR_SPEC because they don't announce themselves as UVC devices, even
+ * though they are compliant.
+ */
+static struct usb_device_id uvc_ids[] = {
+ /* LogiLink Wireless Webcam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0416,
+ .idProduct = 0xa91a,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Genius eFace 2025 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0458,
+ .idProduct = 0x706e,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam NX-6000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x00f8,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Microsoft Lifecam VX-7000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x045e,
+ .idProduct = 0x0723,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Logitech Quickcam Fusion */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c1,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Orbit MP */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro for Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c3,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam Pro 5000 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c5,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Dell Notebook */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c6,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Logitech Quickcam OEM Cisco VT Camera II */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x046d,
+ .idProduct = 0x08c7,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Chicony CNF7129 (Asus EEE 100HE) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x04f2,
+ .idProduct = 0xb071,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE },
+ /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x058f,
+ .idProduct = 0x3820,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Dell XPS m1530 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x2640,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
+ /* Apple Built-In iSight */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05ac,
+ .idProduct = 0x8501,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_BUILTIN_ISIGHT },
+ /* Foxlink ("HP Webcam" on HP Mini 5103) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05c8,
+ .idProduct = 0x0403,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* Genesys Logic USB 2.0 PC Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05e3,
+ .idProduct = 0x0505,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Hercules Classic Silver */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x06f8,
+ .idProduct = 0x300c,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro Vega */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x332d,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x3410,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* ViMicro Venus - Minoru3D */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0ac8,
+ .idProduct = 0x3420,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* Ophir Optronics - SPCAM 620U */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0bd3,
+ .idProduct = 0x0555,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* MT6227 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x0e8d,
+ .idProduct = 0x0004,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_PROBE_DEF },
+ /* IMC Networks (Medion Akoya) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x13d3,
+ .idProduct = 0x5103,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* JMicron USB2.0 XGA WebCam */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x152d,
+ .idProduct = 0x0310,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Syntek (HP Spartan) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5212,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Samsung Q310) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x5931,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Packard Bell EasyNote MX52 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a12,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Asus F9SG) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a31,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (Asus U3S) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a33,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Syntek (JAOtech Smart Terminal) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+ .idProduct = 0x8a34,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Miricle 307K */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17dc,
+ .idProduct = 0x0202,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Lenovo Thinkpad SL400/SL500 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x17ef,
+ .idProduct = 0x480b,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Aveo Technology USB 2.0 Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1871,
+ .idProduct = 0x0306,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_PROBE_EXTRAFIELDS },
+ /* Ecamm Pico iMage */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18cd,
+ .idProduct = 0xcafe,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
+ /* Manta MM-353 Plako */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3188,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* FSC WebCam V30S */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3288,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Arkmicro unbranded */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x18ec,
+ .idProduct = 0x3290,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
+ /* The Imaging Source USB CCD cameras */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x199e,
+ .idProduct = 0x8102,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
+ /* Bodelin ProScopeHR */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_DEV_HI
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19ab,
+ .idProduct = 0x1000,
+ .bcdDevice_hi = 0x0126,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STATUS_INTERVAL },
+ /* MSI StarCam 370i */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1b3b,
+ .idProduct = 0x2951,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* SiGma Micro USB Web Camera */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x1c4f,
+ .idProduct = 0x3000,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_MINMAX
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT },
+ /* Generic USB Video Class */
+ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, uvc_ids);
+
+struct uvc_driver uvc_driver = {
+ .driver = {
+ .name = "uvcvideo",
+ .probe = uvc_probe,
+ .disconnect = uvc_disconnect,
+ .suspend = uvc_suspend,
+ .resume = uvc_resume,
+ .reset_resume = uvc_reset_resume,
+ .id_table = uvc_ids,
+ .supports_autosuspend = 1,
+ },
+};
+
+static int __init uvc_init(void)
+{
+ int ret;
+
+ uvc_debugfs_init();
+
+ ret = usb_register(&uvc_driver.driver);
+ if (ret < 0) {
+ uvc_debugfs_cleanup();
+ return ret;
+ }
+
+ printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
+ return 0;
+}
+
+static void __exit uvc_cleanup(void)
+{
+ usb_deregister(&uvc_driver.driver);
+ uvc_debugfs_cleanup();
+}
+
+module_init(uvc_init);
+module_exit(uvc_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
diff --git a/drivers/media/video/uvc/uvc_entity.c b/drivers/media/usb/uvc/uvc_entity.c
index 29e239911d0e..29e239911d0e 100644
--- a/drivers/media/video/uvc/uvc_entity.c
+++ b/drivers/media/usb/uvc/uvc_entity.c
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/usb/uvc/uvc_isight.c
index 8510e7259e76..8510e7259e76 100644
--- a/drivers/media/video/uvc/uvc_isight.c
+++ b/drivers/media/usb/uvc/uvc_isight.c
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 5577381b5bf0..5577381b5bf0 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index b7492775e6ae..b7492775e6ae 100644
--- a/drivers/media/video/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index f00db3060e0e..f00db3060e0e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
new file mode 100644
index 000000000000..1c15b4227bdb
--- /dev/null
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -0,0 +1,1879 @@
+/*
+ * uvc_video.c -- USB Video Class driver - Video handling
+ *
+ * Copyright (C) 2005-2010
+ * Laurent Pinchart (laurent.pinchart@ideasonboard.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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/atomic.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size,
+ int timeout)
+{
+ __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ unsigned int pipe;
+
+ pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
+ : usb_sndctrlpipe(dev->udev, 0);
+ type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
+
+ return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+ unit << 8 | intfnum, data, size, timeout);
+}
+
+static const char *uvc_query_name(__u8 query)
+{
+ switch (query) {
+ case UVC_SET_CUR:
+ return "SET_CUR";
+ case UVC_GET_CUR:
+ return "GET_CUR";
+ case UVC_GET_MIN:
+ return "GET_MIN";
+ case UVC_GET_MAX:
+ return "GET_MAX";
+ case UVC_GET_RES:
+ return "GET_RES";
+ case UVC_GET_LEN:
+ return "GET_LEN";
+ case UVC_GET_INFO:
+ return "GET_INFO";
+ case UVC_GET_DEF:
+ return "GET_DEF";
+ default:
+ return "<invalid>";
+ }
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+ int ret;
+
+ ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+ UVC_CTRL_CONTROL_TIMEOUT);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on "
+ "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,
+ unit, ret, size);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl)
+{
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ unsigned int i;
+
+ for (i = 0; i < stream->nformats; ++i) {
+ if (stream->format[i].index == ctrl->bFormatIndex) {
+ format = &stream->format[i];
+ break;
+ }
+ }
+
+ if (format == NULL)
+ return;
+
+ for (i = 0; i < format->nframes; ++i) {
+ if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) {
+ frame = &format->frame[i];
+ break;
+ }
+ }
+
+ if (frame == NULL)
+ return;
+
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ (ctrl->dwMaxVideoFrameSize == 0 &&
+ stream->dev->uvc_version < 0x0110))
+ ctrl->dwMaxVideoFrameSize =
+ frame->dwMaxVideoFrameBufferSize;
+
+ if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
+ stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
+ stream->intf->num_altsetting > 1) {
+ u32 interval;
+ u32 bandwidth;
+
+ interval = (ctrl->dwFrameInterval > 100000)
+ ? ctrl->dwFrameInterval
+ : frame->dwFrameInterval[0];
+
+ /* Compute a bandwidth estimation by multiplying the frame
+ * size by the number of video frames per second, divide the
+ * result by the number of USB frames (or micro-frames for
+ * high-speed devices) per second and add the UVC header size
+ * (assumed to be 12 bytes long).
+ */
+ bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
+ bandwidth *= 10000000 / interval + 1;
+ bandwidth /= 1000;
+ if (stream->dev->udev->speed == USB_SPEED_HIGH)
+ bandwidth /= 8;
+ bandwidth += 12;
+
+ /* The bandwidth estimate is too low for many cameras. Don't use
+ * maximum packet sizes lower than 1024 bytes to try and work
+ * around the problem. According to measurements done on two
+ * different camera models, the value is high enough to get most
+ * resolutions working while not preventing two simultaneous
+ * VGA streams at 15 fps.
+ */
+ bandwidth = max_t(u32, bandwidth, 1024);
+
+ ctrl->dwMaxPayloadTransferSize = bandwidth;
+ }
+}
+
+static int uvc_get_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl, int probe, __u8 query)
+{
+ __u8 *data;
+ __u16 size;
+ int ret;
+
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
+ if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) &&
+ query == UVC_GET_DEF)
+ return -EIO;
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
+ probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
+ size, uvc_timeout_param);
+
+ if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
+ /* Some cameras, mostly based on Bison Electronics chipsets,
+ * answer a GET_MIN or GET_MAX request with the wCompQuality
+ * field only.
+ */
+ uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
+ "compliance - GET_MIN/MAX(PROBE) incorrectly "
+ "supported. Enabling workaround.\n");
+ memset(ctrl, 0, sizeof *ctrl);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
+ ret = 0;
+ goto out;
+ } else if (query == UVC_GET_DEF && probe == 1 && ret != size) {
+ /* Many cameras don't support the GET_DEF request on their
+ * video probe control. Warn once and return, the caller will
+ * fall back to GET_CUR.
+ */
+ uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non "
+ "compliance - GET_DEF(PROBE) not supported. "
+ "Enabling workaround.\n");
+ ret = -EIO;
+ goto out;
+ } else if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
+ "%d (exp. %u).\n", query, probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ goto out;
+ }
+
+ ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
+ ctrl->bFormatIndex = data[2];
+ ctrl->bFrameIndex = data[3];
+ ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
+ ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
+ ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
+ ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
+ ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
+ ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
+ ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
+ ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
+
+ if (size == 34) {
+ ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
+ ctrl->bmFramingInfo = data[30];
+ ctrl->bPreferedVersion = data[31];
+ ctrl->bMinVersion = data[32];
+ ctrl->bMaxVersion = data[33];
+ } else {
+ ctrl->dwClockFrequency = stream->dev->clock_frequency;
+ ctrl->bmFramingInfo = 0;
+ ctrl->bPreferedVersion = 0;
+ ctrl->bMinVersion = 0;
+ ctrl->bMaxVersion = 0;
+ }
+
+ /* Some broken devices return null or wrong dwMaxVideoFrameSize and
+ * dwMaxPayloadTransferSize fields. Try to get the value from the
+ * format and frame descriptors.
+ */
+ uvc_fixup_video_ctrl(stream, ctrl);
+ ret = 0;
+
+out:
+ kfree(data);
+ return ret;
+}
+
+static int uvc_set_video_ctrl(struct uvc_streaming *stream,
+ struct uvc_streaming_control *ctrl, int probe)
+{
+ __u8 *data;
+ __u16 size;
+ int ret;
+
+ size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
+ data = kzalloc(size, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
+ data[2] = ctrl->bFormatIndex;
+ data[3] = ctrl->bFrameIndex;
+ *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
+ *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
+ *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
+ *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
+ *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
+ *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
+ put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
+ put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
+
+ if (size == 34) {
+ put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
+ data[30] = ctrl->bmFramingInfo;
+ data[31] = ctrl->bPreferedVersion;
+ data[32] = ctrl->bMinVersion;
+ data[33] = ctrl->bMaxVersion;
+ }
+
+ ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
+ probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
+ size, uvc_timeout_param);
+ if (ret != size) {
+ uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
+ "%d (exp. %u).\n", probe ? "probe" : "commit",
+ ret, size);
+ ret = -EIO;
+ }
+
+ kfree(data);
+ return ret;
+}
+
+int uvc_probe_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe)
+{
+ struct uvc_streaming_control probe_min, probe_max;
+ __u16 bandwidth;
+ unsigned int i;
+ int ret;
+
+ /* Perform probing. The device should adjust the requested values
+ * according to its capabilities. However, some devices, namely the
+ * first generation UVC Logitech webcams, don't implement the Video
+ * Probe control properly, and just return the needed bandwidth. For
+ * that reason, if the needed bandwidth exceeds the maximum available
+ * bandwidth, try to lower the quality.
+ */
+ ret = uvc_set_video_ctrl(stream, probe, 1);
+ if (ret < 0)
+ goto done;
+
+ /* Get the minimum and maximum values for compression settings. */
+ if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+ ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN);
+ if (ret < 0)
+ goto done;
+ ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX);
+ if (ret < 0)
+ goto done;
+
+ probe->wCompQuality = probe_max.wCompQuality;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ret = uvc_set_video_ctrl(stream, probe, 1);
+ if (ret < 0)
+ goto done;
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
+ if (ret < 0)
+ goto done;
+
+ if (stream->intf->num_altsetting == 1)
+ break;
+
+ bandwidth = probe->dwMaxPayloadTransferSize;
+ if (bandwidth <= stream->maxpsize)
+ break;
+
+ if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
+ ret = -ENOSPC;
+ goto done;
+ }
+
+ /* TODO: negotiate compression parameters */
+ probe->wKeyFrameRate = probe_min.wKeyFrameRate;
+ probe->wPFrameRate = probe_min.wPFrameRate;
+ probe->wCompQuality = probe_max.wCompQuality;
+ probe->wCompWindowSize = probe_min.wCompWindowSize;
+ }
+
+done:
+ return ret;
+}
+
+static int uvc_commit_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe)
+{
+ return uvc_set_video_ctrl(stream, probe, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Clocks and timestamps
+ */
+
+static void
+uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
+ const __u8 *data, int len)
+{
+ struct uvc_clock_sample *sample;
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ unsigned long flags;
+ struct timespec ts;
+ u16 host_sof;
+ u16 dev_sof;
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size)
+ return;
+
+ /* Extract the timestamps:
+ *
+ * - store the frame PTS in the buffer structure
+ * - if the SCR field is present, retrieve the host SOF counter and
+ * kernel timestamps and store them with the SCR STC and SOF fields
+ * in the ring buffer
+ */
+ if (has_pts && buf != NULL)
+ buf->pts = get_unaligned_le32(&data[2]);
+
+ if (!has_scr)
+ return;
+
+ /* To limit the amount of data, drop SCRs with an SOF identical to the
+ * previous one.
+ */
+ dev_sof = get_unaligned_le16(&data[header_size - 2]);
+ if (dev_sof == stream->clock.last_sof)
+ return;
+
+ stream->clock.last_sof = dev_sof;
+
+ host_sof = usb_get_current_frame_number(stream->dev->udev);
+ ktime_get_ts(&ts);
+
+ /* The UVC specification allows device implementations that can't obtain
+ * the USB frame number to keep their own frame counters as long as they
+ * match the size and frequency of the frame number associated with USB
+ * SOF tokens. The SOF values sent by such devices differ from the USB
+ * SOF tokens by a fixed offset that needs to be estimated and accounted
+ * for to make timestamp recovery as accurate as possible.
+ *
+ * The offset is estimated the first time a device SOF value is received
+ * as the difference between the host and device SOF values. As the two
+ * SOF values can differ slightly due to transmission delays, consider
+ * that the offset is null if the difference is not higher than 10 ms
+ * (negative differences can not happen and are thus considered as an
+ * offset). The video commit control wDelay field should be used to
+ * compute a dynamic threshold instead of using a fixed 10 ms value, but
+ * devices don't report reliable wDelay values.
+ *
+ * See uvc_video_clock_host_sof() for an explanation regarding why only
+ * the 8 LSBs of the delta are kept.
+ */
+ if (stream->clock.sof_offset == (u16)-1) {
+ u16 delta_sof = (host_sof - dev_sof) & 255;
+ if (delta_sof >= 10)
+ stream->clock.sof_offset = delta_sof;
+ else
+ stream->clock.sof_offset = 0;
+ }
+
+ dev_sof = (dev_sof + stream->clock.sof_offset) & 2047;
+
+ spin_lock_irqsave(&stream->clock.lock, flags);
+
+ sample = &stream->clock.samples[stream->clock.head];
+ sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
+ sample->dev_sof = dev_sof;
+ sample->host_sof = host_sof;
+ sample->host_ts = ts;
+
+ /* Update the sliding window head and count. */
+ stream->clock.head = (stream->clock.head + 1) % stream->clock.size;
+
+ if (stream->clock.count < stream->clock.size)
+ stream->clock.count++;
+
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
+static void uvc_video_clock_reset(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ clock->head = 0;
+ clock->count = 0;
+ clock->last_sof = -1;
+ clock->sof_offset = -1;
+}
+
+static int uvc_video_clock_init(struct uvc_streaming *stream)
+{
+ struct uvc_clock *clock = &stream->clock;
+
+ spin_lock_init(&clock->lock);
+ clock->size = 32;
+
+ clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
+ GFP_KERNEL);
+ if (clock->samples == NULL)
+ return -ENOMEM;
+
+ uvc_video_clock_reset(stream);
+
+ return 0;
+}
+
+static void uvc_video_clock_cleanup(struct uvc_streaming *stream)
+{
+ kfree(stream->clock.samples);
+ stream->clock.samples = NULL;
+}
+
+/*
+ * uvc_video_clock_host_sof - Return the host SOF value for a clock sample
+ *
+ * Host SOF counters reported by usb_get_current_frame_number() usually don't
+ * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame
+ * schedule window. They can be limited to 8, 9 or 10 bits depending on the host
+ * controller and its configuration.
+ *
+ * We thus need to recover the SOF value corresponding to the host frame number.
+ * As the device and host frame numbers are sampled in a short interval, the
+ * difference between their values should be equal to a small delta plus an
+ * integer multiple of 256 caused by the host frame number limited precision.
+ *
+ * To obtain the recovered host SOF value, compute the small delta by masking
+ * the high bits of the host frame counter and device SOF difference and add it
+ * to the device SOF value.
+ */
+static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample)
+{
+ /* The delta value can be negative. */
+ s8 delta_sof;
+
+ delta_sof = (sample->host_sof - sample->dev_sof) & 255;
+
+ return (sample->dev_sof + delta_sof) & 2047;
+}
+
+/*
+ * uvc_video_clock_update - Update the buffer timestamp
+ *
+ * This function converts the buffer PTS timestamp to the host clock domain by
+ * going through the USB SOF clock domain and stores the result in the V4L2
+ * buffer timestamp field.
+ *
+ * The relationship between the device clock and the host clock isn't known.
+ * However, the device and the host share the common USB SOF clock which can be
+ * used to recover that relationship.
+ *
+ * The relationship between the device clock and the USB SOF clock is considered
+ * to be linear over the clock samples sliding window and is given by
+ *
+ * SOF = m * PTS + p
+ *
+ * Several methods to compute the slope (m) and intercept (p) can be used. As
+ * the clock drift should be small compared to the sliding window size, we
+ * assume that the line that goes through the points at both ends of the window
+ * is a good approximation. Naming those points P1 and P2, we get
+ *
+ * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS
+ * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1)
+ *
+ * or
+ *
+ * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1)
+ *
+ * to avoid loosing precision in the division. Similarly, the host timestamp is
+ * computed with
+ *
+ * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2)
+ *
+ * SOF values are coded on 11 bits by USB. We extend their precision with 16
+ * decimal bits, leading to a 11.16 coding.
+ *
+ * TODO: To avoid surprises with device clock values, PTS/STC timestamps should
+ * be normalized using the nominal device clock frequency reported through the
+ * UVC descriptors.
+ *
+ * Both the PTS/STC and SOF counters roll over, after a fixed but device
+ * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the
+ * sliding window size is smaller than the rollover period, differences computed
+ * on unsigned integers will produce the correct result. However, the p term in
+ * the linear relations will be miscomputed.
+ *
+ * To fix the issue, we subtract a constant from the PTS and STC values to bring
+ * PTS to half the 32 bit STC range. The sliding window STC values then fit into
+ * the 32 bit range without any rollover.
+ *
+ * Similarly, we add 2048 to the device SOF values to make sure that the SOF
+ * computed by (1) will never be smaller than 0. This offset is then compensated
+ * by adding 2048 to the SOF values used in (2). However, this doesn't prevent
+ * rollovers between (1) and (2): the SOF value computed by (1) can be slightly
+ * lower than 4096, and the host SOF counters can have rolled over to 2048. This
+ * case is handled by subtracting 2048 from the SOF value if it exceeds the host
+ * SOF value at the end of the sliding window.
+ *
+ * Finally we subtract a constant from the host timestamps to bring the first
+ * timestamp of the sliding window to 1s.
+ */
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf)
+{
+ struct uvc_clock *clock = &stream->clock;
+ struct uvc_clock_sample *first;
+ struct uvc_clock_sample *last;
+ unsigned long flags;
+ struct timespec ts;
+ u32 delta_stc;
+ u32 y1, y2;
+ u32 x1, x2;
+ u32 mean;
+ u32 sof;
+ u32 div;
+ u32 rem;
+ u64 y;
+
+ spin_lock_irqsave(&clock->lock, flags);
+
+ if (clock->count < clock->size)
+ goto done;
+
+ first = &clock->samples[clock->head];
+ last = &clock->samples[(clock->head - 1) % clock->size];
+
+ /* First step, PTS to SOF conversion. */
+ delta_stc = buf->pts - (1UL << 31);
+ x1 = first->dev_stc - delta_stc;
+ x2 = last->dev_stc - delta_stc;
+ if (x1 == x2)
+ goto done;
+
+ y1 = (first->dev_sof + 2048) << 16;
+ y2 = (last->dev_sof + 2048) << 16;
+ if (y2 < y1)
+ y2 += 2048 << 16;
+
+ y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ sof = y;
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
+ "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
+ stream->dev->name, buf->pts,
+ y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ x1, x2, y1, y2, clock->sof_offset);
+
+ /* Second step, SOF to host clock conversion. */
+ x1 = (uvc_video_clock_host_sof(first) + 2048) << 16;
+ x2 = (uvc_video_clock_host_sof(last) + 2048) << 16;
+ if (x2 < x1)
+ x2 += 2048 << 16;
+ if (x1 == x2)
+ goto done;
+
+ ts = timespec_sub(last->host_ts, first->host_ts);
+ y1 = NSEC_PER_SEC;
+ y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec;
+
+ /* Interpolated and host SOF timestamps can wrap around at slightly
+ * different times. Handle this by adding or removing 2048 to or from
+ * the computed SOF value to keep it close to the SOF samples mean
+ * value.
+ */
+ mean = (x1 + x2) / 2;
+ if (mean - (1024 << 16) > sof)
+ sof += 2048 << 16;
+ else if (sof > mean + (1024 << 16))
+ sof -= 2048 << 16;
+
+ y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2
+ - (u64)y2 * (u64)x1;
+ y = div_u64(y, x2 - x1);
+
+ div = div_u64_rem(y, NSEC_PER_SEC, &rem);
+ ts.tv_sec = first->host_ts.tv_sec - 1 + div;
+ ts.tv_nsec = first->host_ts.tv_nsec + rem;
+ if (ts.tv_nsec >= NSEC_PER_SEC) {
+ ts.tv_sec++;
+ ts.tv_nsec -= NSEC_PER_SEC;
+ }
+
+ uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu "
+ "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
+ stream->dev->name,
+ sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
+ y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
+ v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec,
+ x1, first->host_sof, first->dev_sof,
+ x2, last->host_sof, last->dev_sof, y1, y2);
+
+ /* Update the V4L2 buffer. */
+ v4l2_buf->timestamp.tv_sec = ts.tv_sec;
+ v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+
+done:
+ spin_unlock_irqrestore(&stream->clock.lock, flags);
+}
+
+/* ------------------------------------------------------------------------
+ * Stream statistics
+ */
+
+static void uvc_video_stats_decode(struct uvc_streaming *stream,
+ const __u8 *data, int len)
+{
+ unsigned int header_size;
+ bool has_pts = false;
+ bool has_scr = false;
+ u16 uninitialized_var(scr_sof);
+ u32 uninitialized_var(scr_stc);
+ u32 uninitialized_var(pts);
+
+ if (stream->stats.stream.nb_frames == 0 &&
+ stream->stats.frame.nb_packets == 0)
+ ktime_get_ts(&stream->stats.stream.start_ts);
+
+ switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
+ case UVC_STREAM_PTS | UVC_STREAM_SCR:
+ header_size = 12;
+ has_pts = true;
+ has_scr = true;
+ break;
+ case UVC_STREAM_PTS:
+ header_size = 6;
+ has_pts = true;
+ break;
+ case UVC_STREAM_SCR:
+ header_size = 8;
+ has_scr = true;
+ break;
+ default:
+ header_size = 2;
+ break;
+ }
+
+ /* Check for invalid headers. */
+ if (len < header_size || data[0] < header_size) {
+ stream->stats.frame.nb_invalid++;
+ return;
+ }
+
+ /* Extract the timestamps. */
+ if (has_pts)
+ pts = get_unaligned_le32(&data[2]);
+
+ if (has_scr) {
+ scr_stc = get_unaligned_le32(&data[header_size - 6]);
+ scr_sof = get_unaligned_le16(&data[header_size - 2]);
+ }
+
+ /* Is PTS constant through the whole frame ? */
+ if (has_pts && stream->stats.frame.nb_pts) {
+ if (stream->stats.frame.pts != pts) {
+ stream->stats.frame.nb_pts_diffs++;
+ stream->stats.frame.last_pts_diff =
+ stream->stats.frame.nb_packets;
+ }
+ }
+
+ if (has_pts) {
+ stream->stats.frame.nb_pts++;
+ stream->stats.frame.pts = pts;
+ }
+
+ /* Do all frames have a PTS in their first non-empty packet, or before
+ * their first empty packet ?
+ */
+ if (stream->stats.frame.size == 0) {
+ if (len > header_size)
+ stream->stats.frame.has_initial_pts = has_pts;
+ if (len == header_size && has_pts)
+ stream->stats.frame.has_early_pts = true;
+ }
+
+ /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
+ if (has_scr && stream->stats.frame.nb_scr) {
+ if (stream->stats.frame.scr_stc != scr_stc)
+ stream->stats.frame.nb_scr_diffs++;
+ }
+
+ if (has_scr) {
+ /* Expand the SOF counter to 32 bits and store its value. */
+ if (stream->stats.stream.nb_frames > 0 ||
+ stream->stats.frame.nb_scr > 0)
+ stream->stats.stream.scr_sof_count +=
+ (scr_sof - stream->stats.stream.scr_sof) % 2048;
+ stream->stats.stream.scr_sof = scr_sof;
+
+ stream->stats.frame.nb_scr++;
+ stream->stats.frame.scr_stc = scr_stc;
+ stream->stats.frame.scr_sof = scr_sof;
+
+ if (scr_sof < stream->stats.stream.min_sof)
+ stream->stats.stream.min_sof = scr_sof;
+ if (scr_sof > stream->stats.stream.max_sof)
+ stream->stats.stream.max_sof = scr_sof;
+ }
+
+ /* Record the first non-empty packet number. */
+ if (stream->stats.frame.size == 0 && len > header_size)
+ stream->stats.frame.first_data = stream->stats.frame.nb_packets;
+
+ /* Update the frame size. */
+ stream->stats.frame.size += len - header_size;
+
+ /* Update the packets counters. */
+ stream->stats.frame.nb_packets++;
+ if (len > header_size)
+ stream->stats.frame.nb_empty++;
+
+ if (data[1] & UVC_STREAM_ERR)
+ stream->stats.frame.nb_errors++;
+}
+
+static void uvc_video_stats_update(struct uvc_streaming *stream)
+{
+ struct uvc_stats_frame *frame = &stream->stats.frame;
+
+ uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
+ "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
+ "last pts/stc/sof %u/%u/%u\n",
+ stream->sequence, frame->first_data,
+ frame->nb_packets - frame->nb_empty, frame->nb_packets,
+ frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
+ frame->has_early_pts ? "" : "!",
+ frame->has_initial_pts ? "" : "!",
+ frame->nb_scr_diffs, frame->nb_scr,
+ frame->pts, frame->scr_stc, frame->scr_sof);
+
+ stream->stats.stream.nb_frames++;
+ stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
+ stream->stats.stream.nb_empty += stream->stats.frame.nb_empty;
+ stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
+ stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
+
+ if (frame->has_early_pts)
+ stream->stats.stream.nb_pts_early++;
+ if (frame->has_initial_pts)
+ stream->stats.stream.nb_pts_initial++;
+ if (frame->last_pts_diff <= frame->first_data)
+ stream->stats.stream.nb_pts_constant++;
+ if (frame->nb_scr >= frame->nb_packets - frame->nb_empty)
+ stream->stats.stream.nb_scr_count_ok++;
+ if (frame->nb_scr_diffs + 1 == frame->nb_scr)
+ stream->stats.stream.nb_scr_diffs_ok++;
+
+ memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
+}
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size)
+{
+ unsigned int scr_sof_freq;
+ unsigned int duration;
+ struct timespec ts;
+ size_t count = 0;
+
+ ts.tv_sec = stream->stats.stream.stop_ts.tv_sec
+ - stream->stats.stream.start_ts.tv_sec;
+ ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec
+ - stream->stats.stream.start_ts.tv_nsec;
+ if (ts.tv_nsec < 0) {
+ ts.tv_sec--;
+ ts.tv_nsec += 1000000000;
+ }
+
+ /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF
+ * frequency this will not overflow before more than 1h.
+ */
+ duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+ if (duration != 0)
+ scr_sof_freq = stream->stats.stream.scr_sof_count * 1000
+ / duration;
+ else
+ scr_sof_freq = 0;
+
+ count += scnprintf(buf + count, size - count,
+ "frames: %u\npackets: %u\nempty: %u\n"
+ "errors: %u\ninvalid: %u\n",
+ stream->stats.stream.nb_frames,
+ stream->stats.stream.nb_packets,
+ stream->stats.stream.nb_empty,
+ stream->stats.stream.nb_errors,
+ stream->stats.stream.nb_invalid);
+ count += scnprintf(buf + count, size - count,
+ "pts: %u early, %u initial, %u ok\n",
+ stream->stats.stream.nb_pts_early,
+ stream->stats.stream.nb_pts_initial,
+ stream->stats.stream.nb_pts_constant);
+ count += scnprintf(buf + count, size - count,
+ "scr: %u count ok, %u diff ok\n",
+ stream->stats.stream.nb_scr_count_ok,
+ stream->stats.stream.nb_scr_diffs_ok);
+ count += scnprintf(buf + count, size - count,
+ "sof: %u <= sof <= %u, freq %u.%03u kHz\n",
+ stream->stats.stream.min_sof,
+ stream->stats.stream.max_sof,
+ scr_sof_freq / 1000, scr_sof_freq % 1000);
+
+ return count;
+}
+
+static void uvc_video_stats_start(struct uvc_streaming *stream)
+{
+ memset(&stream->stats, 0, sizeof(stream->stats));
+ stream->stats.stream.min_sof = 2048;
+}
+
+static void uvc_video_stats_stop(struct uvc_streaming *stream)
+{
+ ktime_get_ts(&stream->stats.stream.stop_ts);
+}
+
+/* ------------------------------------------------------------------------
+ * Video codecs
+ */
+
+/* Video payload decoding is handled by uvc_video_decode_start(),
+ * uvc_video_decode_data() and uvc_video_decode_end().
+ *
+ * uvc_video_decode_start is called with URB data at the start of a bulk or
+ * isochronous payload. It processes header data and returns the header size
+ * in bytes if successful. If an error occurs, it returns a negative error
+ * code. The following error codes have special meanings.
+ *
+ * - EAGAIN informs the caller that the current video buffer should be marked
+ * as done, and that the function should be called again with the same data
+ * and a new video buffer. This is used when end of frame conditions can be
+ * reliably detected at the beginning of the next frame only.
+ *
+ * If an error other than -EAGAIN is returned, the caller will drop the current
+ * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be
+ * made until the next payload. -ENODATA can be used to drop the current
+ * payload if no other error code is appropriate.
+ *
+ * uvc_video_decode_data is called for every URB with URB data. It copies the
+ * data to the video buffer.
+ *
+ * uvc_video_decode_end is called with header data at the end of a bulk or
+ * isochronous payload. It performs any additional header data processing and
+ * returns 0 or a negative error code if an error occurred. As header data have
+ * already been processed by uvc_video_decode_start, this functions isn't
+ * required to perform sanity checks a second time.
+ *
+ * For isochronous transfers where a payload is always transferred in a single
+ * URB, the three functions will be called in a row.
+ *
+ * To let the decoder process header data and update its internal state even
+ * when no video buffer is available, uvc_video_decode_start must be prepared
+ * to be called with a NULL buf parameter. uvc_video_decode_data and
+ * uvc_video_decode_end will never be called with a NULL buffer.
+ */
+static int uvc_video_decode_start(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ __u8 fid;
+
+ /* Sanity checks:
+ * - packet must be at least 2 bytes long
+ * - bHeaderLength value must be at least 2 bytes (see above)
+ * - bHeaderLength value can't be larger than the packet size.
+ */
+ if (len < 2 || data[0] < 2 || data[0] > len) {
+ stream->stats.frame.nb_invalid++;
+ return -EINVAL;
+ }
+
+ fid = data[1] & UVC_STREAM_FID;
+
+ /* Increase the sequence number regardless of any buffer states, so
+ * that discontinuous sequence numbers always indicate lost frames.
+ */
+ if (stream->last_fid != fid) {
+ stream->sequence++;
+ if (stream->sequence)
+ uvc_video_stats_update(stream);
+ }
+
+ uvc_video_clock_decode(stream, buf, data, len);
+ uvc_video_stats_decode(stream, data, len);
+
+ /* Store the payload FID bit and return immediately when the buffer is
+ * NULL.
+ */
+ if (buf == NULL) {
+ stream->last_fid = fid;
+ return -ENODATA;
+ }
+
+ /* Mark the buffer as bad if the error bit is set. */
+ if (data[1] & UVC_STREAM_ERR) {
+ uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
+ "set).\n");
+ buf->error = 1;
+ }
+
+ /* Synchronize to the input stream by waiting for the FID bit to be
+ * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
+ * stream->last_fid is initialized to -1, so the first isochronous
+ * frame will always be in sync.
+ *
+ * If the device doesn't toggle the FID bit, invert stream->last_fid
+ * when the EOF bit is set to force synchronisation on the next packet.
+ */
+ if (buf->state != UVC_BUF_STATE_ACTIVE) {
+ struct timespec ts;
+
+ if (fid == stream->last_fid) {
+ uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
+ "sync).\n");
+ if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
+ (data[1] & UVC_STREAM_EOF))
+ stream->last_fid ^= UVC_STREAM_FID;
+ return -ENODATA;
+ }
+
+ if (uvc_clock_param == CLOCK_MONOTONIC)
+ ktime_get_ts(&ts);
+ else
+ ktime_get_real_ts(&ts);
+
+ buf->buf.v4l2_buf.sequence = stream->sequence;
+ buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
+ buf->buf.v4l2_buf.timestamp.tv_usec =
+ ts.tv_nsec / NSEC_PER_USEC;
+
+ /* TODO: Handle PTS and SCR. */
+ buf->state = UVC_BUF_STATE_ACTIVE;
+ }
+
+ /* Mark the buffer as done if we're at the beginning of a new frame.
+ * End of frame detection is better implemented by checking the EOF
+ * bit (FID bit toggling is delayed by one frame compared to the EOF
+ * bit), but some devices don't set the bit at end of frame (and the
+ * last payload can be lost anyway). We thus must check if the FID has
+ * been toggled.
+ *
+ * stream->last_fid is initialized to -1, so the first isochronous
+ * frame will never trigger an end of frame detection.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer. This also
+ * avoids detecting end of frame conditions at FID toggling if the
+ * previous payload had the EOF bit set.
+ */
+ if (fid != stream->last_fid && buf->bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
+ "toggled).\n");
+ buf->state = UVC_BUF_STATE_READY;
+ return -EAGAIN;
+ }
+
+ stream->last_fid = fid;
+
+ return data[0];
+}
+
+static void uvc_video_decode_data(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ unsigned int maxlen, nbytes;
+ void *mem;
+
+ if (len <= 0)
+ return;
+
+ /* Copy the video data to the buffer. */
+ maxlen = buf->length - buf->bytesused;
+ mem = buf->mem + buf->bytesused;
+ nbytes = min((unsigned int)len, maxlen);
+ memcpy(mem, data, nbytes);
+ buf->bytesused += nbytes;
+
+ /* Complete the current frame if the buffer size was exceeded. */
+ if (len > maxlen) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+ buf->state = UVC_BUF_STATE_READY;
+ }
+}
+
+static void uvc_video_decode_end(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, const __u8 *data, int len)
+{
+ /* Mark the buffer as done if the EOF marker is set. */
+ if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
+ uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+ if (data[0] == len)
+ uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
+ buf->state = UVC_BUF_STATE_READY;
+ if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
+ stream->last_fid ^= UVC_STREAM_FID;
+ }
+}
+
+/* Video payload encoding is handled by uvc_video_encode_header() and
+ * uvc_video_encode_data(). Only bulk transfers are currently supported.
+ *
+ * uvc_video_encode_header is called at the start of a payload. It adds header
+ * data to the transfer buffer and returns the header size. As the only known
+ * UVC output device transfers a whole frame in a single payload, the EOF bit
+ * is always set in the header.
+ *
+ * uvc_video_encode_data is called for every URB and copies the data from the
+ * video buffer to the transfer buffer.
+ */
+static int uvc_video_encode_header(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ data[0] = 2; /* Header length */
+ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
+ | (stream->last_fid & UVC_STREAM_FID);
+ return 2;
+}
+
+static int uvc_video_encode_data(struct uvc_streaming *stream,
+ struct uvc_buffer *buf, __u8 *data, int len)
+{
+ struct uvc_video_queue *queue = &stream->queue;
+ unsigned int nbytes;
+ void *mem;
+
+ /* Copy video data to the URB buffer. */
+ mem = buf->mem + queue->buf_used;
+ nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
+ nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size,
+ nbytes);
+ memcpy(data, mem, nbytes);
+
+ queue->buf_used += nbytes;
+
+ return nbytes;
+}
+
+/* ------------------------------------------------------------------------
+ * URB handling
+ */
+
+/*
+ * Completion handler for video URBs.
+ */
+static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int ret, i;
+
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->iso_frame_desc[i].status < 0) {
+ uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+ "lost (%d).\n", urb->iso_frame_desc[i].status);
+ /* Mark the buffer as faulty. */
+ if (buf != NULL)
+ buf->error = 1;
+ continue;
+ }
+
+ /* Decode the payload header. */
+ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+ do {
+ ret = uvc_video_decode_start(stream, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ } while (ret == -EAGAIN);
+
+ if (ret < 0)
+ continue;
+
+ /* Decode the payload data. */
+ uvc_video_decode_data(stream, buf, mem + ret,
+ urb->iso_frame_desc[i].actual_length - ret);
+
+ /* Process the header again. */
+ uvc_video_decode_end(stream, buf, mem,
+ urb->iso_frame_desc[i].actual_length);
+
+ if (buf->state == UVC_BUF_STATE_READY) {
+ if (buf->length != buf->bytesused &&
+ !(stream->cur_format->flags &
+ UVC_FMT_FLAG_COMPRESSED))
+ buf->error = 1;
+
+ buf = uvc_queue_next_buffer(&stream->queue, buf);
+ }
+ }
+}
+
+static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem;
+ int len, ret;
+
+ /*
+ * Ignore ZLPs if they're not part of a frame, otherwise process them
+ * to trigger the end of payload detection.
+ */
+ if (urb->actual_length == 0 && stream->bulk.header_size == 0)
+ return;
+
+ mem = urb->transfer_buffer;
+ len = urb->actual_length;
+ stream->bulk.payload_size += len;
+
+ /* If the URB is the first of its payload, decode and save the
+ * header.
+ */
+ if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
+ do {
+ ret = uvc_video_decode_start(stream, buf, mem, len);
+ if (ret == -EAGAIN)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ } while (ret == -EAGAIN);
+
+ /* If an error occurred skip the rest of the payload. */
+ if (ret < 0 || buf == NULL) {
+ stream->bulk.skip_payload = 1;
+ } else {
+ memcpy(stream->bulk.header, mem, ret);
+ stream->bulk.header_size = ret;
+
+ mem += ret;
+ len -= ret;
+ }
+ }
+
+ /* The buffer queue might have been cancelled while a bulk transfer
+ * was in progress, so we can reach here with buf equal to NULL. Make
+ * sure buf is never dereferenced if NULL.
+ */
+
+ /* Process video data. */
+ if (!stream->bulk.skip_payload && buf != NULL)
+ uvc_video_decode_data(stream, buf, mem, len);
+
+ /* Detect the payload end by a URB smaller than the maximum size (or
+ * a payload size equal to the maximum) and process the header again.
+ */
+ if (urb->actual_length < urb->transfer_buffer_length ||
+ stream->bulk.payload_size >= stream->bulk.max_payload_size) {
+ if (!stream->bulk.skip_payload && buf != NULL) {
+ uvc_video_decode_end(stream, buf, stream->bulk.header,
+ stream->bulk.payload_size);
+ if (buf->state == UVC_BUF_STATE_READY)
+ buf = uvc_queue_next_buffer(&stream->queue,
+ buf);
+ }
+
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
+ }
+}
+
+static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf)
+{
+ u8 *mem = urb->transfer_buffer;
+ int len = stream->urb_size, ret;
+
+ if (buf == NULL) {
+ urb->transfer_buffer_length = 0;
+ return;
+ }
+
+ /* If the URB is the first of its payload, add the header. */
+ if (stream->bulk.header_size == 0) {
+ ret = uvc_video_encode_header(stream, buf, mem, len);
+ stream->bulk.header_size = ret;
+ stream->bulk.payload_size += ret;
+ mem += ret;
+ len -= ret;
+ }
+
+ /* Process video data. */
+ ret = uvc_video_encode_data(stream, buf, mem, len);
+
+ stream->bulk.payload_size += ret;
+ len -= ret;
+
+ if (buf->bytesused == stream->queue.buf_used ||
+ stream->bulk.payload_size == stream->bulk.max_payload_size) {
+ if (buf->bytesused == stream->queue.buf_used) {
+ stream->queue.buf_used = 0;
+ buf->state = UVC_BUF_STATE_READY;
+ buf->buf.v4l2_buf.sequence = ++stream->sequence;
+ uvc_queue_next_buffer(&stream->queue, buf);
+ stream->last_fid ^= UVC_STREAM_FID;
+ }
+
+ stream->bulk.header_size = 0;
+ stream->bulk.payload_size = 0;
+ }
+
+ urb->transfer_buffer_length = stream->urb_size - len;
+}
+
+static void uvc_video_complete(struct urb *urb)
+{
+ struct uvc_streaming *stream = urb->context;
+ struct uvc_video_queue *queue = &stream->queue;
+ struct uvc_buffer *buf = NULL;
+ unsigned long flags;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ break;
+
+ default:
+ uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
+ "completion handler.\n", urb->status);
+
+ case -ENOENT: /* usb_kill_urb() called. */
+ if (stream->frozen)
+ return;
+
+ case -ECONNRESET: /* usb_unlink_urb() called. */
+ case -ESHUTDOWN: /* The endpoint is being disabled. */
+ uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
+ return;
+ }
+
+ spin_lock_irqsave(&queue->irqlock, flags);
+ if (!list_empty(&queue->irqqueue))
+ buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+ queue);
+ spin_unlock_irqrestore(&queue->irqlock, flags);
+
+ stream->decode(urb, stream, buf);
+
+ if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+ ret);
+ }
+}
+
+/*
+ * Free transfer buffers.
+ */
+static void uvc_free_urb_buffers(struct uvc_streaming *stream)
+{
+ unsigned int i;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ if (stream->urb_buffer[i]) {
+#ifndef CONFIG_DMA_NONCOHERENT
+ usb_free_coherent(stream->dev->udev, stream->urb_size,
+ stream->urb_buffer[i], stream->urb_dma[i]);
+#else
+ kfree(stream->urb_buffer[i]);
+#endif
+ stream->urb_buffer[i] = NULL;
+ }
+ }
+
+ stream->urb_size = 0;
+}
+
+/*
+ * Allocate transfer buffers. This function can be called with buffers
+ * already allocated when resuming from suspend, in which case it will
+ * return without touching the buffers.
+ *
+ * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the
+ * system is too low on memory try successively smaller numbers of packets
+ * until allocation succeeds.
+ *
+ * Return the number of allocated packets on success or 0 when out of memory.
+ */
+static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
+ unsigned int size, unsigned int psize, gfp_t gfp_flags)
+{
+ unsigned int npackets;
+ unsigned int i;
+
+ /* Buffers are already allocated, bail out. */
+ if (stream->urb_size)
+ return stream->urb_size / psize;
+
+ /* Compute the number of packets. Bulk endpoints might transfer UVC
+ * payloads across multiple URBs.
+ */
+ npackets = DIV_ROUND_UP(size, psize);
+ if (npackets > UVC_MAX_PACKETS)
+ npackets = UVC_MAX_PACKETS;
+
+ /* Retry allocations until one succeed. */
+ for (; npackets > 1; npackets /= 2) {
+ for (i = 0; i < UVC_URBS; ++i) {
+ stream->urb_size = psize * npackets;
+#ifndef CONFIG_DMA_NONCOHERENT
+ stream->urb_buffer[i] = usb_alloc_coherent(
+ stream->dev->udev, stream->urb_size,
+ gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
+#else
+ stream->urb_buffer[i] =
+ kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
+#endif
+ if (!stream->urb_buffer[i]) {
+ uvc_free_urb_buffers(stream);
+ break;
+ }
+ }
+
+ if (i == UVC_URBS) {
+ uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
+ "of %ux%u bytes each.\n", UVC_URBS, npackets,
+ psize);
+ return npackets;
+ }
+ }
+
+ uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
+ "per packet).\n", psize);
+ return 0;
+}
+
+/*
+ * Uninitialize isochronous/bulk URBs and free transfer buffers.
+ */
+static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
+{
+ struct urb *urb;
+ unsigned int i;
+
+ uvc_video_stats_stop(stream);
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = stream->urb[i];
+ if (urb == NULL)
+ continue;
+
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ stream->urb[i] = NULL;
+ }
+
+ if (free_buffers)
+ uvc_free_urb_buffers(stream);
+}
+
+/*
+ * Compute the maximum number of bytes per interval for an endpoint.
+ */
+static unsigned int uvc_endpoint_max_bpi(struct usb_device *dev,
+ struct usb_host_endpoint *ep)
+{
+ u16 psize;
+
+ switch (dev->speed) {
+ case USB_SPEED_SUPER:
+ return ep->ss_ep_comp.wBytesPerInterval;
+ case USB_SPEED_HIGH:
+ psize = usb_endpoint_maxp(&ep->desc);
+ return (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+ default:
+ psize = usb_endpoint_maxp(&ep->desc);
+ return psize & 0x07ff;
+ }
+}
+
+/*
+ * Initialize isochronous URBs and allocate transfer buffers. The packet size
+ * is given by the endpoint.
+ */
+static int uvc_init_video_isoc(struct uvc_streaming *stream,
+ struct usb_host_endpoint *ep, gfp_t gfp_flags)
+{
+ struct urb *urb;
+ unsigned int npackets, i, j;
+ u16 psize;
+ u32 size;
+
+ psize = uvc_endpoint_max_bpi(stream->dev->udev, ep);
+ size = stream->ctrl.dwMaxVideoFrameSize;
+
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
+ if (npackets == 0)
+ return -ENOMEM;
+
+ size = npackets * psize;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(npackets, gfp_flags);
+ if (urb == NULL) {
+ uvc_uninit_video(stream, 1);
+ return -ENOMEM;
+ }
+
+ urb->dev = stream->dev->udev;
+ urb->context = stream;
+ urb->pipe = usb_rcvisocpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#else
+ urb->transfer_flags = URB_ISO_ASAP;
+#endif
+ urb->interval = ep->desc.bInterval;
+ urb->transfer_buffer = stream->urb_buffer[i];
+ urb->complete = uvc_video_complete;
+ urb->number_of_packets = npackets;
+ urb->transfer_buffer_length = size;
+
+ for (j = 0; j < npackets; ++j) {
+ urb->iso_frame_desc[j].offset = j * psize;
+ urb->iso_frame_desc[j].length = psize;
+ }
+
+ stream->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize bulk URBs and allocate transfer buffers. The packet size is
+ * given by the endpoint.
+ */
+static int uvc_init_video_bulk(struct uvc_streaming *stream,
+ struct usb_host_endpoint *ep, gfp_t gfp_flags)
+{
+ struct urb *urb;
+ unsigned int npackets, pipe, i;
+ u16 psize;
+ u32 size;
+
+ psize = usb_endpoint_maxp(&ep->desc) & 0x7ff;
+ size = stream->ctrl.dwMaxPayloadTransferSize;
+ stream->bulk.max_payload_size = size;
+
+ npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
+ if (npackets == 0)
+ return -ENOMEM;
+
+ size = npackets * psize;
+
+ if (usb_endpoint_dir_in(&ep->desc))
+ pipe = usb_rcvbulkpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+ else
+ pipe = usb_sndbulkpipe(stream->dev->udev,
+ ep->desc.bEndpointAddress);
+
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ size = 0;
+
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(0, gfp_flags);
+ if (urb == NULL) {
+ uvc_uninit_video(stream, 1);
+ return -ENOMEM;
+ }
+
+ usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
+ stream->urb_buffer[i], size, uvc_video_complete,
+ stream);
+#ifndef CONFIG_DMA_NONCOHERENT
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_dma = stream->urb_dma[i];
+#endif
+
+ stream->urb[i] = urb;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize isochronous/bulk URBs and allocate transfer buffers.
+ */
+static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
+{
+ struct usb_interface *intf = stream->intf;
+ struct usb_host_endpoint *ep;
+ unsigned int i;
+ int ret;
+
+ stream->sequence = -1;
+ stream->last_fid = -1;
+ stream->bulk.header_size = 0;
+ stream->bulk.skip_payload = 0;
+ stream->bulk.payload_size = 0;
+
+ uvc_video_stats_start(stream);
+
+ if (intf->num_altsetting > 1) {
+ struct usb_host_endpoint *best_ep = NULL;
+ unsigned int best_psize = UINT_MAX;
+ unsigned int bandwidth;
+ unsigned int uninitialized_var(altsetting);
+ int intfnum = stream->intfnum;
+
+ /* Isochronous endpoint, select the alternate setting. */
+ bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
+
+ if (bandwidth == 0) {
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
+ "bandwidth, defaulting to lowest.\n");
+ bandwidth = 1;
+ } else {
+ uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
+ "B/frame bandwidth.\n", bandwidth);
+ }
+
+ for (i = 0; i < intf->num_altsetting; ++i) {
+ struct usb_host_interface *alts;
+ unsigned int psize;
+
+ alts = &intf->altsetting[i];
+ ep = uvc_find_endpoint(alts,
+ stream->header.bEndpointAddress);
+ if (ep == NULL)
+ continue;
+
+ /* Check if the bandwidth is high enough. */
+ psize = uvc_endpoint_max_bpi(stream->dev->udev, ep);
+ if (psize >= bandwidth && psize <= best_psize) {
+ altsetting = alts->desc.bAlternateSetting;
+ best_psize = psize;
+ best_ep = ep;
+ }
+ }
+
+ if (best_ep == NULL) {
+ uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
+ "for requested bandwidth.\n");
+ return -EIO;
+ }
+
+ uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
+ "(%u B/frame bandwidth).\n", altsetting, best_psize);
+
+ ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
+ } else {
+ /* Bulk endpoint, proceed to URB initialization. */
+ ep = uvc_find_endpoint(&intf->altsetting[0],
+ stream->header.bEndpointAddress);
+ if (ep == NULL)
+ return -EIO;
+
+ ret = uvc_init_video_bulk(stream, ep, gfp_flags);
+ }
+
+ if (ret < 0)
+ return ret;
+
+ /* Submit the URBs. */
+ for (i = 0; i < UVC_URBS; ++i) {
+ ret = usb_submit_urb(stream->urb[i], gfp_flags);
+ if (ret < 0) {
+ uvc_printk(KERN_ERR, "Failed to submit URB %u "
+ "(%d).\n", i, ret);
+ uvc_uninit_video(stream, 1);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Stop streaming without disabling the video queue.
+ *
+ * To let userspace applications resume without trouble, we must not touch the
+ * video buffers in any way. We mark the device as frozen to make sure the URB
+ * completion handler won't try to cancel the queue when we kill the URBs.
+ */
+int uvc_video_suspend(struct uvc_streaming *stream)
+{
+ if (!uvc_queue_streaming(&stream->queue))
+ return 0;
+
+ stream->frozen = 1;
+ uvc_uninit_video(stream, 0);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ return 0;
+}
+
+/*
+ * Reconfigure the video interface and restart streaming if it was enabled
+ * before suspend.
+ *
+ * If an error occurs, disable the video queue. This will wake all pending
+ * buffers, making sure userspace applications are notified of the problem
+ * instead of waiting forever.
+ */
+int uvc_video_resume(struct uvc_streaming *stream, int reset)
+{
+ int ret;
+
+ /* If the bus has been reset on resume, set the alternate setting to 0.
+ * This should be the default value, but some devices crash or otherwise
+ * misbehave if they don't receive a SET_INTERFACE request before any
+ * other video control request.
+ */
+ if (reset)
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+
+ stream->frozen = 0;
+
+ uvc_video_clock_reset(stream);
+
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0) {
+ uvc_queue_enable(&stream->queue, 0);
+ return ret;
+ }
+
+ if (!uvc_queue_streaming(&stream->queue))
+ return 0;
+
+ ret = uvc_init_video(stream, GFP_NOIO);
+ if (ret < 0)
+ uvc_queue_enable(&stream->queue, 0);
+
+ return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device
+ */
+
+/*
+ * Initialize the UVC video device by switching to alternate setting 0 and
+ * retrieve the default format.
+ *
+ * Some cameras (namely the Fuji Finepix) set the format and frame
+ * indexes to zero. The UVC standard doesn't clearly make this a spec
+ * violation, so try to silently fix the values if possible.
+ *
+ * This function is called before registering the device with V4L.
+ */
+int uvc_video_init(struct uvc_streaming *stream)
+{
+ struct uvc_streaming_control *probe = &stream->ctrl;
+ struct uvc_format *format = NULL;
+ struct uvc_frame *frame = NULL;
+ unsigned int i;
+ int ret;
+
+ if (stream->nformats == 0) {
+ uvc_printk(KERN_INFO, "No supported video formats found.\n");
+ return -EINVAL;
+ }
+
+ atomic_set(&stream->active, 0);
+
+ /* Initialize the video buffers queue. */
+ uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
+
+ /* Alternate setting 0 should be the default, yet the XBox Live Vision
+ * Cam (and possibly other devices) crash or otherwise misbehave if
+ * they don't receive a SET_INTERFACE request before any other video
+ * control request.
+ */
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+
+ /* Set the streaming probe control with default streaming parameters
+ * retrieved from the device. Webcams that don't suport GET_DEF
+ * requests on the probe control will just keep their current streaming
+ * parameters.
+ */
+ if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
+ uvc_set_video_ctrl(stream, probe, 1);
+
+ /* Initialize the streaming parameters with the probe control current
+ * value. This makes sure SET_CUR requests on the streaming commit
+ * control will always use values retrieved from a successful GET_CUR
+ * request on the probe control, as required by the UVC specification.
+ */
+ ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
+ if (ret < 0)
+ return ret;
+
+ /* Check if the default format descriptor exists. Use the first
+ * available format otherwise.
+ */
+ for (i = stream->nformats; i > 0; --i) {
+ format = &stream->format[i-1];
+ if (format->index == probe->bFormatIndex)
+ break;
+ }
+
+ if (format->nframes == 0) {
+ uvc_printk(KERN_INFO, "No frame descriptor found for the "
+ "default format.\n");
+ return -EINVAL;
+ }
+
+ /* Zero bFrameIndex might be correct. Stream-based formats (including
+ * MPEG-2 TS and DV) do not support frames but have a dummy frame
+ * descriptor with bFrameIndex set to zero. If the default frame
+ * descriptor is not found, use the first available frame.
+ */
+ for (i = format->nframes; i > 0; --i) {
+ frame = &format->frame[i-1];
+ if (frame->bFrameIndex == probe->bFrameIndex)
+ break;
+ }
+
+ probe->bFormatIndex = format->index;
+ probe->bFrameIndex = frame->bFrameIndex;
+
+ stream->cur_format = format;
+ stream->cur_frame = frame;
+
+ /* Select the video decoding function */
+ if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+ stream->decode = uvc_video_decode_isight;
+ else if (stream->intf->num_altsetting > 1)
+ stream->decode = uvc_video_decode_isoc;
+ else
+ stream->decode = uvc_video_decode_bulk;
+ } else {
+ if (stream->intf->num_altsetting == 1)
+ stream->decode = uvc_video_encode_bulk;
+ else {
+ uvc_printk(KERN_INFO, "Isochronous endpoints are not "
+ "supported for video output devices.\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+int uvc_video_enable(struct uvc_streaming *stream, int enable)
+{
+ int ret;
+
+ if (!enable) {
+ uvc_uninit_video(stream, 1);
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+ uvc_queue_enable(&stream->queue, 0);
+ uvc_video_clock_cleanup(stream);
+ return 0;
+ }
+
+ ret = uvc_video_clock_init(stream);
+ if (ret < 0)
+ return ret;
+
+ ret = uvc_queue_enable(&stream->queue, 1);
+ if (ret < 0)
+ goto error_queue;
+
+ /* Commit the streaming parameters. */
+ ret = uvc_commit_video(stream, &stream->ctrl);
+ if (ret < 0)
+ goto error_commit;
+
+ ret = uvc_init_video(stream, GFP_KERNEL);
+ if (ret < 0)
+ goto error_video;
+
+ return 0;
+
+error_video:
+ usb_set_interface(stream->dev->udev, stream->intfnum, 0);
+error_commit:
+ uvc_queue_enable(&stream->queue, 0);
+error_queue:
+ uvc_video_clock_cleanup(stream);
+
+ return ret;
+}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
new file mode 100644
index 000000000000..3764040475bb
--- /dev/null
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -0,0 +1,718 @@
+#ifndef _USB_VIDEO_H_
+#define _USB_VIDEO_H_
+
+#ifndef __KERNEL__
+#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
+#endif /* __KERNEL__ */
+
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/usb.h>
+#include <linux/usb/video.h>
+#include <linux/uvcvideo.h>
+#include <linux/videodev2.h>
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/videobuf2-core.h>
+
+/* --------------------------------------------------------------------------
+ * UVC constants
+ */
+
+#define UVC_TERM_INPUT 0x0000
+#define UVC_TERM_OUTPUT 0x8000
+#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000)
+
+#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
+#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
+#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
+#define UVC_ENTITY_IS_ITERM(entity) \
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_INPUT)
+#define UVC_ENTITY_IS_OTERM(entity) \
+ (UVC_ENTITY_IS_TERM(entity) && \
+ ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
+
+
+/* ------------------------------------------------------------------------
+ * GUIDs
+ */
+#define UVC_GUID_UVC_CAMERA \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
+#define UVC_GUID_UVC_OUTPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}
+#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
+#define UVC_GUID_UVC_PROCESSING \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}
+#define UVC_GUID_UVC_SELECTOR \
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
+
+#define UVC_GUID_FORMAT_MJPEG \
+ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2 \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YUY2_ISIGHT \
+ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_NV12 \
+ { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_YV12 \
+ { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_I420 \
+ { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_UYVY \
+ { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y800 \
+ { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y8 \
+ { 'Y', '8', ' ', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y10 \
+ { 'Y', '1', '0', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y12 \
+ { 'Y', '1', '2', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_Y16 \
+ { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_BY8 \
+ { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_RGBP \
+ { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+#define UVC_GUID_FORMAT_M420 \
+ { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+#define UVC_GUID_FORMAT_H264 \
+ { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
+ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
+
+/* ------------------------------------------------------------------------
+ * Driver specific constants.
+ */
+
+#define DRIVER_VERSION "1.1.1"
+
+/* Number of isochronous URBs. */
+#define UVC_URBS 5
+/* Maximum number of packets per URB. */
+#define UVC_MAX_PACKETS 32
+/* Maximum number of video buffers. */
+#define UVC_MAX_VIDEO_BUFFERS 32
+/* Maximum status buffer size in bytes of interrupt URB. */
+#define UVC_MAX_STATUS_SIZE 16
+
+#define UVC_CTRL_CONTROL_TIMEOUT 300
+#define UVC_CTRL_STREAMING_TIMEOUT 5000
+
+/* Maximum allowed number of control mappings per device */
+#define UVC_MAX_CONTROL_MAPPINGS 1024
+#define UVC_MAX_CONTROL_MENU_ENTRIES 32
+
+/* Devices quirks */
+#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
+#define UVC_QUIRK_PROBE_MINMAX 0x00000002
+#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004
+#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
+#define UVC_QUIRK_STREAM_NO_FID 0x00000010
+#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080
+#define UVC_QUIRK_PROBE_DEF 0x00000100
+#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
+
+/* Format flags */
+#define UVC_FMT_FLAG_COMPRESSED 0x00000001
+#define UVC_FMT_FLAG_STREAM 0x00000002
+
+/* ------------------------------------------------------------------------
+ * Structures.
+ */
+
+struct uvc_device;
+
+/* TODO: Put the most frequently accessed fields at the beginning of
+ * structures to maximize cache efficiency.
+ */
+struct uvc_control_info {
+ struct list_head mappings;
+
+ __u8 entity[16];
+ __u8 index; /* Bit index in bmControls */
+ __u8 selector;
+
+ __u16 size;
+ __u32 flags;
+};
+
+struct uvc_control_mapping {
+ struct list_head list;
+ struct list_head ev_subs;
+
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ enum v4l2_ctrl_type v4l2_type;
+ __u32 data_type;
+
+ struct uvc_menu_info *menu_info;
+ __u32 menu_count;
+
+ __u32 master_id;
+ __s32 master_manual;
+ __u32 slave_ids[2];
+
+ __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
+ const __u8 *data);
+ void (*set) (struct uvc_control_mapping *mapping, __s32 value,
+ __u8 *data);
+};
+
+struct uvc_control {
+ struct uvc_entity *entity;
+ struct uvc_control_info info;
+
+ __u8 index; /* Used to match the uvc_control entry with a
+ uvc_control_info. */
+ __u8 dirty:1,
+ loaded:1,
+ modified:1,
+ cached:1,
+ initialized:1;
+
+ __u8 *uvc_data;
+};
+
+struct uvc_format_desc {
+ char *name;
+ __u8 guid[16];
+ __u32 fcc;
+};
+
+/* The term 'entity' refers to both UVC units and UVC terminals.
+ *
+ * The type field is either the terminal type (wTerminalType in the terminal
+ * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor).
+ * As the bDescriptorSubtype field is one byte long, the type value will
+ * always have a null MSB for units. All terminal types defined by the UVC
+ * specification have a non-null MSB, so it is safe to use the MSB to
+ * differentiate between units and terminals as long as the descriptor parsing
+ * code makes sure terminal types have a non-null MSB.
+ *
+ * For terminals, the type's most significant bit stores the terminal
+ * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should
+ * always be accessed with the UVC_ENTITY_* macros and never directly.
+ */
+
+struct uvc_entity {
+ struct list_head list; /* Entity as part of a UVC device. */
+ struct list_head chain; /* Entity as part of a video device
+ * chain. */
+ __u8 id;
+ __u16 type;
+ char name[64];
+
+ /* Media controller-related fields. */
+ struct video_device *vdev;
+ struct v4l2_subdev subdev;
+ unsigned int num_pads;
+ unsigned int num_links;
+ struct media_pad *pads;
+
+ union {
+ struct {
+ __u16 wObjectiveFocalLengthMin;
+ __u16 wObjectiveFocalLengthMax;
+ __u16 wOcularFocalLength;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ } camera;
+
+ struct {
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bTransportModeSize;
+ __u8 *bmTransportModes;
+ } media;
+
+ struct {
+ } output;
+
+ struct {
+ __u16 wMaxMultiplier;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 bmVideoStandards;
+ } processing;
+
+ struct {
+ } selector;
+
+ struct {
+ __u8 guidExtensionCode[16];
+ __u8 bNumControls;
+ __u8 bControlSize;
+ __u8 *bmControls;
+ __u8 *bmControlsType;
+ } extension;
+ };
+
+ __u8 bNrInPins;
+ __u8 *baSourceID;
+
+ unsigned int ncontrols;
+ struct uvc_control *controls;
+};
+
+struct uvc_frame {
+ __u8 bFrameIndex;
+ __u8 bmCapabilities;
+ __u16 wWidth;
+ __u16 wHeight;
+ __u32 dwMinBitRate;
+ __u32 dwMaxBitRate;
+ __u32 dwMaxVideoFrameBufferSize;
+ __u8 bFrameIntervalType;
+ __u32 dwDefaultFrameInterval;
+ __u32 *dwFrameInterval;
+};
+
+struct uvc_format {
+ __u8 type;
+ __u8 index;
+ __u8 bpp;
+ __u8 colorspace;
+ __u32 fcc;
+ __u32 flags;
+
+ char name[32];
+
+ unsigned int nframes;
+ struct uvc_frame *frame;
+};
+
+struct uvc_streaming_header {
+ __u8 bNumFormats;
+ __u8 bEndpointAddress;
+ __u8 bTerminalLink;
+ __u8 bControlSize;
+ __u8 *bmaControls;
+ /* The following fields are used by input headers only. */
+ __u8 bmInfo;
+ __u8 bStillCaptureMethod;
+ __u8 bTriggerSupport;
+ __u8 bTriggerUsage;
+};
+
+enum uvc_buffer_state {
+ UVC_BUF_STATE_IDLE = 0,
+ UVC_BUF_STATE_QUEUED = 1,
+ UVC_BUF_STATE_ACTIVE = 2,
+ UVC_BUF_STATE_READY = 3,
+ UVC_BUF_STATE_DONE = 4,
+ UVC_BUF_STATE_ERROR = 5,
+};
+
+struct uvc_buffer {
+ struct vb2_buffer buf;
+ struct list_head queue;
+
+ enum uvc_buffer_state state;
+ unsigned int error;
+
+ void *mem;
+ unsigned int length;
+ unsigned int bytesused;
+
+ u32 pts;
+};
+
+#define UVC_QUEUE_DISCONNECTED (1 << 0)
+#define UVC_QUEUE_DROP_CORRUPTED (1 << 1)
+
+struct uvc_video_queue {
+ struct vb2_queue queue;
+ struct mutex mutex; /* Protects queue */
+
+ unsigned int flags;
+ unsigned int buf_used;
+
+ spinlock_t irqlock; /* Protects irqqueue */
+ struct list_head irqqueue;
+};
+
+struct uvc_video_chain {
+ struct uvc_device *dev;
+ struct list_head list;
+
+ struct list_head entities; /* All entities */
+ struct uvc_entity *processing; /* Processing unit */
+ struct uvc_entity *selector; /* Selector unit */
+
+ struct mutex ctrl_mutex; /* Protects ctrl.info */
+};
+
+struct uvc_stats_frame {
+ unsigned int size; /* Number of bytes captured */
+ unsigned int first_data; /* Index of the first non-empty packet */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts; /* Number of packets with a PTS timestamp */
+ unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */
+ unsigned int last_pts_diff; /* Index of the last PTS difference */
+ bool has_initial_pts; /* Whether the first non-empty packet has a PTS */
+ bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */
+ u32 pts; /* PTS of the last packet */
+
+ unsigned int nb_scr; /* Number of packets with a SCR timestamp */
+ unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */
+ u16 scr_sof; /* SCR.SOF of the last packet */
+ u32 scr_stc; /* SCR.STC of the last packet */
+};
+
+struct uvc_stats_stream {
+ struct timespec start_ts; /* Stream start timestamp */
+ struct timespec stop_ts; /* Stream stop timestamp */
+
+ unsigned int nb_frames; /* Number of frames */
+
+ unsigned int nb_packets; /* Number of packets */
+ unsigned int nb_empty; /* Number of empty packets */
+ unsigned int nb_invalid; /* Number of packets with an invalid header */
+ unsigned int nb_errors; /* Number of packets with the error bit set */
+
+ unsigned int nb_pts_constant; /* Number of frames with constant PTS */
+ unsigned int nb_pts_early; /* Number of frames with early PTS */
+ unsigned int nb_pts_initial; /* Number of frames with initial PTS */
+
+ unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */
+ unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */
+ unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */
+ unsigned int scr_sof; /* STC.SOF of the last packet */
+ unsigned int min_sof; /* Minimum STC.SOF value */
+ unsigned int max_sof; /* Maximum STC.SOF value */
+};
+
+struct uvc_streaming {
+ struct list_head list;
+ struct uvc_device *dev;
+ struct video_device *vdev;
+ struct uvc_video_chain *chain;
+ atomic_t active;
+
+ struct usb_interface *intf;
+ int intfnum;
+ __u16 maxpsize;
+
+ struct uvc_streaming_header header;
+ enum v4l2_buf_type type;
+
+ unsigned int nformats;
+ struct uvc_format *format;
+
+ struct uvc_streaming_control ctrl;
+ struct uvc_format *cur_format;
+ struct uvc_frame *cur_frame;
+ /* Protect access to ctrl, cur_format, cur_frame and hardware video
+ * probe control.
+ */
+ struct mutex mutex;
+
+ /* Buffers queue. */
+ unsigned int frozen : 1;
+ struct uvc_video_queue queue;
+ void (*decode) (struct urb *urb, struct uvc_streaming *video,
+ struct uvc_buffer *buf);
+
+ /* Context data used by the bulk completion handler. */
+ struct {
+ __u8 header[256];
+ unsigned int header_size;
+ int skip_payload;
+ __u32 payload_size;
+ __u32 max_payload_size;
+ } bulk;
+
+ struct urb *urb[UVC_URBS];
+ char *urb_buffer[UVC_URBS];
+ dma_addr_t urb_dma[UVC_URBS];
+ unsigned int urb_size;
+
+ __u32 sequence;
+ __u8 last_fid;
+
+ /* debugfs */
+ struct dentry *debugfs_dir;
+ struct {
+ struct uvc_stats_frame frame;
+ struct uvc_stats_stream stream;
+ } stats;
+
+ /* Timestamps support. */
+ struct uvc_clock {
+ struct uvc_clock_sample {
+ u32 dev_stc;
+ u16 dev_sof;
+ struct timespec host_ts;
+ u16 host_sof;
+ } *samples;
+
+ unsigned int head;
+ unsigned int count;
+ unsigned int size;
+
+ u16 last_sof;
+ u16 sof_offset;
+
+ spinlock_t lock;
+ } clock;
+};
+
+enum uvc_device_state {
+ UVC_DEV_DISCONNECTED = 1,
+};
+
+struct uvc_device {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ unsigned long warnings;
+ __u32 quirks;
+ int intfnum;
+ char name[32];
+
+ enum uvc_device_state state;
+ atomic_t users;
+ atomic_t nmappings;
+
+ /* Video control interface */
+#ifdef CONFIG_MEDIA_CONTROLLER
+ struct media_device mdev;
+#endif
+ struct v4l2_device vdev;
+ __u16 uvc_version;
+ __u32 clock_frequency;
+
+ struct list_head entities;
+ struct list_head chains;
+
+ /* Video Streaming interfaces */
+ struct list_head streams;
+ atomic_t nstreams;
+
+ /* Status Interrupt Endpoint */
+ struct usb_host_endpoint *int_ep;
+ struct urb *int_urb;
+ __u8 *status;
+ struct input_dev *input;
+ char input_phys[64];
+};
+
+enum uvc_handle_state {
+ UVC_HANDLE_PASSIVE = 0,
+ UVC_HANDLE_ACTIVE = 1,
+};
+
+struct uvc_fh {
+ struct v4l2_fh vfh;
+ struct uvc_video_chain *chain;
+ struct uvc_streaming *stream;
+ enum uvc_handle_state state;
+};
+
+struct uvc_driver {
+ struct usb_driver driver;
+};
+
+/* ------------------------------------------------------------------------
+ * Debugging, printing and logging
+ */
+
+#define UVC_TRACE_PROBE (1 << 0)
+#define UVC_TRACE_DESCR (1 << 1)
+#define UVC_TRACE_CONTROL (1 << 2)
+#define UVC_TRACE_FORMAT (1 << 3)
+#define UVC_TRACE_CAPTURE (1 << 4)
+#define UVC_TRACE_CALLS (1 << 5)
+#define UVC_TRACE_IOCTL (1 << 6)
+#define UVC_TRACE_FRAME (1 << 7)
+#define UVC_TRACE_SUSPEND (1 << 8)
+#define UVC_TRACE_STATUS (1 << 9)
+#define UVC_TRACE_VIDEO (1 << 10)
+#define UVC_TRACE_STATS (1 << 11)
+#define UVC_TRACE_CLOCK (1 << 12)
+
+#define UVC_WARN_MINMAX 0
+#define UVC_WARN_PROBE_DEF 1
+#define UVC_WARN_XU_GET_RES 2
+
+extern unsigned int uvc_clock_param;
+extern unsigned int uvc_no_drop_param;
+extern unsigned int uvc_trace_param;
+extern unsigned int uvc_timeout_param;
+
+#define uvc_trace(flag, msg...) \
+ do { \
+ if (uvc_trace_param & flag) \
+ printk(KERN_DEBUG "uvcvideo: " msg); \
+ } while (0)
+
+#define uvc_warn_once(dev, warn, msg...) \
+ do { \
+ if (!test_and_set_bit(warn, &dev->warnings)) \
+ printk(KERN_INFO "uvcvideo: " msg); \
+ } while (0)
+
+#define uvc_printk(level, msg...) \
+ printk(level "uvcvideo: " msg)
+
+/* --------------------------------------------------------------------------
+ * Internal functions.
+ */
+
+/* Core driver */
+extern struct uvc_driver uvc_driver;
+
+extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
+
+/* Video buffers queue management. */
+extern void uvc_queue_init(struct uvc_video_queue *queue,
+ enum v4l2_buf_type type, int drop_corrupted);
+extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
+ struct v4l2_requestbuffers *rb);
+extern void uvc_free_buffers(struct uvc_video_queue *queue);
+extern int uvc_query_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_queue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf);
+extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+ struct v4l2_buffer *v4l2_buf, int nonblocking);
+extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
+extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
+extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+ struct uvc_buffer *buf);
+extern int uvc_queue_mmap(struct uvc_video_queue *queue,
+ struct vm_area_struct *vma);
+extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
+ struct file *file, poll_table *wait);
+#ifndef CONFIG_MMU
+extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff);
+#endif
+extern int uvc_queue_allocated(struct uvc_video_queue *queue);
+static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
+{
+ return vb2_is_streaming(&queue->queue);
+}
+
+/* V4L2 interface */
+extern const struct v4l2_file_operations uvc_fops;
+
+/* Media controller */
+extern int uvc_mc_register_entities(struct uvc_video_chain *chain);
+extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
+
+/* Video */
+extern int uvc_video_init(struct uvc_streaming *stream);
+extern int uvc_video_suspend(struct uvc_streaming *stream);
+extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
+extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
+extern int uvc_probe_video(struct uvc_streaming *stream,
+ struct uvc_streaming_control *probe);
+extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ __u8 intfnum, __u8 cs, void *data, __u16 size);
+void uvc_video_clock_update(struct uvc_streaming *stream,
+ struct v4l2_buffer *v4l2_buf,
+ struct uvc_buffer *buf);
+
+/* Status */
+extern int uvc_status_init(struct uvc_device *dev);
+extern void uvc_status_cleanup(struct uvc_device *dev);
+extern int uvc_status_start(struct uvc_device *dev);
+extern void uvc_status_stop(struct uvc_device *dev);
+extern int uvc_status_suspend(struct uvc_device *dev);
+extern int uvc_status_resume(struct uvc_device *dev);
+
+/* Controls */
+extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
+
+extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct v4l2_queryctrl *v4l2_ctrl);
+extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
+ struct v4l2_querymenu *query_menu);
+
+extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
+ const struct uvc_control_mapping *mapping);
+extern int uvc_ctrl_init_device(struct uvc_device *dev);
+extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
+extern int uvc_ctrl_resume_device(struct uvc_device *dev);
+
+extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
+extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count);
+static inline int uvc_ctrl_commit(struct uvc_fh *handle,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count)
+{
+ return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count);
+}
+static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
+{
+ return __uvc_ctrl_commit(handle, 1, NULL, 0);
+}
+
+extern int uvc_ctrl_get(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl);
+extern int uvc_ctrl_set(struct uvc_video_chain *chain,
+ struct v4l2_ext_control *xctrl);
+
+extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
+ struct uvc_xu_control_query *xqry);
+
+/* Utility functions */
+extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+ unsigned int n_terms, unsigned int threshold);
+extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
+ uint32_t denominator);
+extern struct usb_host_endpoint *uvc_find_endpoint(
+ struct usb_host_interface *alts, __u8 epaddr);
+
+/* Quirks support */
+void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
+ struct uvc_buffer *buf);
+
+/* debugfs and statistics */
+int uvc_debugfs_init(void);
+void uvc_debugfs_cleanup(void);
+int uvc_debugfs_init_stream(struct uvc_streaming *stream);
+void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
+
+size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
+ size_t size);
+
+#endif
diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig
new file mode 100644
index 000000000000..0f585662881d
--- /dev/null
+++ b/drivers/media/usb/zr364xx/Kconfig
@@ -0,0 +1,14 @@
+config USB_ZR364XX
+ tristate "USB ZR364XX Camera support"
+ depends on VIDEO_V4L2
+ select VIDEOBUF_GEN
+ select VIDEOBUF_VMALLOC
+ ---help---
+ Say Y here if you want to connect this type of camera to your
+ computer's USB port.
+ See <file:Documentation/video4linux/zr364xx.txt> for more info
+ and list of supported cameras.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zr364xx.
+
diff --git a/drivers/media/usb/zr364xx/Makefile b/drivers/media/usb/zr364xx/Makefile
new file mode 100644
index 000000000000..a5777883a1f8
--- /dev/null
+++ b/drivers/media/usb/zr364xx/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
+
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c
index 9afab35878b4..9afab35878b4 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/usb/zr364xx/zr364xx.c
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
new file mode 100644
index 000000000000..0c54e19d9944
--- /dev/null
+++ b/drivers/media/v4l2-core/Kconfig
@@ -0,0 +1,81 @@
+#
+# Generic video config states
+#
+
+# Enable the V4L2 core and API
+config VIDEO_V4L2
+ tristate
+ depends on (I2C || I2C=n) && VIDEO_DEV
+ default (I2C || I2C=n) && VIDEO_DEV
+
+config VIDEO_ADV_DEBUG
+ bool "Enable advanced debug functionality on V4L2 drivers"
+ default n
+ ---help---
+ Say Y here to enable advanced debugging functionality on some
+ V4L devices.
+ In doubt, say N.
+
+config VIDEO_FIXED_MINOR_RANGES
+ bool "Enable old-style fixed minor ranges on drivers/video devices"
+ default n
+ ---help---
+ Say Y here to enable the old-style fixed-range minor assignments.
+ Only useful if you rely on the old behavior and use mknod instead of udev.
+
+ When in doubt, say N.
+
+# Used by drivers that need tuner.ko
+config VIDEO_TUNER
+ tristate
+ depends on MEDIA_TUNER
+
+# Used by drivers that need v4l2-mem2mem.ko
+config V4L2_MEM2MEM_DEV
+ tristate
+ depends on VIDEOBUF2_CORE
+
+# Used by drivers that need Videobuf modules
+config VIDEOBUF_GEN
+ tristate
+
+config VIDEOBUF_DMA_SG
+ tristate
+ depends on HAS_DMA
+ select VIDEOBUF_GEN
+
+config VIDEOBUF_VMALLOC
+ tristate
+ select VIDEOBUF_GEN
+
+config VIDEOBUF_DMA_CONTIG
+ tristate
+ depends on HAS_DMA
+ select VIDEOBUF_GEN
+
+config VIDEOBUF_DVB
+ tristate
+ select VIDEOBUF_GEN
+
+# Used by drivers that need Videobuf2 modules
+config VIDEOBUF2_CORE
+ tristate
+
+config VIDEOBUF2_MEMOPS
+ tristate
+
+config VIDEOBUF2_DMA_CONTIG
+ tristate
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+
+config VIDEOBUF2_VMALLOC
+ tristate
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+
+config VIDEOBUF2_DMA_SG
+ tristate
+ #depends on HAS_DMA
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
new file mode 100644
index 000000000000..c2d61d4f03d1
--- /dev/null
+++ b/drivers/media/v4l2-core/Makefile
@@ -0,0 +1,35 @@
+#
+# Makefile for the V4L2 core
+#
+
+tuner-objs := tuner-core.o
+
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
+ v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+ifeq ($(CONFIG_COMPAT),y)
+ videodev-objs += v4l2-compat-ioctl32.o
+endif
+
+obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
+obj-$(CONFIG_VIDEO_V4L2) += v4l2-common.o
+
+obj-$(CONFIG_VIDEO_TUNER) += tuner.o
+
+obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
+
+obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
+obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
+obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
+obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o
+obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.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
+
+ccflags-y += -I$(srctree)/drivers/media/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
+ccflags-y += -I$(srctree)/drivers/media/tuners
+
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index b5a819af2b8c..b5a819af2b8c 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
new file mode 100644
index 000000000000..f995dd31151d
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -0,0 +1,981 @@
+/*
+ * Video for Linux Two
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This file replaces the videodev.c file that comes with the
+ * regular kernel distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Bill Dirks <bill@thedirks.org>
+ * based on code by Alan Cox, <alan@cymru.net>
+ *
+ */
+
+/*
+ * Video capture interface for Linux
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk>
+ *
+ * Fixes:
+ */
+
+/*
+ * Video4linux 1/2 integration by Justin Schoeman
+ * <justin@suntiger.ee.up.ac.za>
+ * 2.4 PROCFS support ported from 2.4 kernels by
+ * Iñaki García Etxebarria <garetxe@euskalnet.net>
+ * Makefile fix by "W. Michael Petullo" <mike@flyn.org>
+ * 2.4 devfs support ported from 2.4 kernels by
+ * Dan Merillat <dan@merillat.org>
+ * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/div64.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-chip-ident.h>
+
+#include <linux/videodev2.h>
+
+MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr");
+MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers");
+MODULE_LICENSE("GPL");
+
+/*
+ *
+ * V 4 L 2 D R I V E R H E L P E R A P I
+ *
+ */
+
+/*
+ * Video Standard Operations (contributed by Michael Schimek)
+ */
+
+/* Helper functions for control handling */
+
+/* Check for correctness of the ctrl's value based on the data from
+ struct v4l2_queryctrl and the available menu items. Note that
+ menu_items may be NULL, in that case it is ignored. */
+int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+ if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ return -EBUSY;
+ if (qctrl->type == V4L2_CTRL_TYPE_STRING)
+ return 0;
+ if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
+ qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return 0;
+ if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
+ return -ERANGE;
+ if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
+ if (menu_items[ctrl->value] == NULL ||
+ menu_items[ctrl->value][0] == '\0')
+ return -EINVAL;
+ }
+ if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+ (ctrl->value & ~qctrl->maximum))
+ return -ERANGE;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_check);
+
+/* Fill in a struct v4l2_queryctrl */
+int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
+{
+ const char *name;
+
+ v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
+ &min, &max, &step, &def, &qctrl->flags);
+
+ if (name == NULL)
+ return -EINVAL;
+
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_fill);
+
+/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
+ the menu. The qctrl pointer may be NULL, in which case it is ignored.
+ If menu_items is NULL, then the menu items are retrieved using
+ v4l2_ctrl_get_menu. */
+int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
+ const char * const *menu_items)
+{
+ int i;
+
+ qmenu->reserved = 0;
+ if (menu_items == NULL)
+ menu_items = v4l2_ctrl_get_menu(qmenu->id);
+ if (menu_items == NULL ||
+ (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
+ return -EINVAL;
+ for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
+ if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu);
+
+/* Fill in a struct v4l2_querymenu based on the specified array of valid
+ menu items (terminated by V4L2_CTRL_MENU_IDS_END).
+ Use this if there are 'holes' in the list of valid menu items. */
+int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
+{
+ const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
+
+ qmenu->reserved = 0;
+ if (menu_items == NULL || ids == NULL)
+ return -EINVAL;
+ while (*ids != V4L2_CTRL_MENU_IDS_END) {
+ if (*ids++ == qmenu->index) {
+ strlcpy(qmenu->name, menu_items[qmenu->index],
+ sizeof(qmenu->name));
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
+
+/* ctrl_classes points to an array of u32 pointers, the last element is
+ a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
+ Each array must be sorted low to high and belong to the same control
+ class. The array of u32 pointers must also be sorted, from low class IDs
+ to high class IDs.
+
+ This function returns the first ID that follows after the given ID.
+ When no more controls are available 0 is returned. */
+u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
+{
+ u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
+ const u32 *pctrl;
+
+ if (ctrl_classes == NULL)
+ return 0;
+
+ /* if no query is desired, then check if the ID is part of ctrl_classes */
+ if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
+ /* find class */
+ while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
+ ctrl_classes++;
+ if (*ctrl_classes == NULL)
+ return 0;
+ pctrl = *ctrl_classes;
+ /* find control ID */
+ while (*pctrl && *pctrl != id) pctrl++;
+ return *pctrl ? id : 0;
+ }
+ id &= V4L2_CTRL_ID_MASK;
+ id++; /* select next control */
+ /* find first class that matches (or is greater than) the class of
+ the ID */
+ while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
+ ctrl_classes++;
+ /* no more classes */
+ if (*ctrl_classes == NULL)
+ return 0;
+ pctrl = *ctrl_classes;
+ /* find first ctrl within the class that is >= ID */
+ while (*pctrl && *pctrl < id) pctrl++;
+ if (*pctrl)
+ return *pctrl;
+ /* we are at the end of the controls of the current class. */
+ /* continue with next class if available */
+ ctrl_classes++;
+ if (*ctrl_classes == NULL)
+ return 0;
+ return **ctrl_classes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_next);
+
+int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
+{
+ switch (match->type) {
+ case V4L2_CHIP_MATCH_HOST:
+ return match->addr == 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(v4l2_chip_match_host);
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
+{
+ int len;
+
+ if (c == NULL || match == NULL)
+ return 0;
+
+ switch (match->type) {
+ case V4L2_CHIP_MATCH_I2C_DRIVER:
+ if (c->driver == NULL || c->driver->driver.name == NULL)
+ return 0;
+ len = strlen(c->driver->driver.name);
+ /* legacy drivers have a ' suffix, don't try to match that */
+ if (len && c->driver->driver.name[len - 1] == '\'')
+ len--;
+ return len && !strncmp(c->driver->driver.name, match->name, len);
+ case V4L2_CHIP_MATCH_I2C_ADDR:
+ return c->addr == match->addr;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
+
+int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
+ u32 ident, u32 revision)
+{
+ if (!v4l2_chip_match_i2c_client(c, &chip->match))
+ return 0;
+ if (chip->ident == V4L2_IDENT_NONE) {
+ chip->ident = ident;
+ chip->revision = revision;
+ }
+ else {
+ chip->ident = V4L2_IDENT_AMBIGUOUS;
+ chip->revision = 0;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
+
+/* ----------------------------------------------------------------- */
+
+/* I2C Helper functions */
+
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = client->driver->driver.owner;
+ /* i2c_client and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, client);
+ i2c_set_clientdata(client, sd);
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ client->driver->driver.name, i2c_adapter_id(client->adapter),
+ client->addr);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
+
+
+
+/* Load an i2c sub-device. */
+struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
+ struct i2c_adapter *adapter, struct i2c_board_info *info,
+ const unsigned short *probe_addrs)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client;
+
+ BUG_ON(!v4l2_dev);
+
+ request_module(I2C_MODULE_PREFIX "%s", info->type);
+
+ /* Create the i2c client */
+ if (info->addr == 0 && probe_addrs)
+ client = i2c_new_probed_device(adapter, info, probe_addrs,
+ NULL);
+ else
+ client = i2c_new_device(adapter, info);
+
+ /* Note: by loading the module first we are certain that c->driver
+ will be set if the driver was found. If the module was not loaded
+ first, then the i2c core tries to delay-load the module for us,
+ and then c->driver is still NULL until the module is finally
+ loaded. This delay-load mechanism doesn't work if other drivers
+ want to use the i2c device, so explicitly loading the module
+ is the best alternative. */
+ if (client == NULL || client->driver == NULL)
+ goto error;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ goto error;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(v4l2_dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+
+error:
+ /* If we have a client but no subdev, then something went wrong and
+ we must unregister the client. */
+ if (client && sd == NULL)
+ i2c_unregister_device(client);
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
+
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
+ struct i2c_adapter *adapter, const char *client_type,
+ u8 addr, const unsigned short *probe_addrs)
+{
+ struct i2c_board_info info;
+
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+ strlcpy(info.type, client_type, sizeof(info.type));
+ info.addr = addr;
+
+ return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Return i2c client address of v4l2_subdev. */
+unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return client ? client->addr : I2C_CLIENT_END;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
+
+/* Return a list of I2C tuner addresses to probe. Use only if the tuner
+ addresses are unknown. */
+const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
+{
+ static const unsigned short radio_addrs[] = {
+#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
+ 0x10,
+#endif
+ 0x60,
+ I2C_CLIENT_END
+ };
+ static const unsigned short demod_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b,
+ I2C_CLIENT_END
+ };
+ static const unsigned short tv_addrs[] = {
+ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
+ 0x60, 0x61, 0x62, 0x63, 0x64,
+ I2C_CLIENT_END
+ };
+
+ switch (type) {
+ case ADDRS_RADIO:
+ return radio_addrs;
+ case ADDRS_DEMOD:
+ return demod_addrs;
+ case ADDRS_TV:
+ return tv_addrs;
+ case ADDRS_TV_WITH_DEMOD:
+ return tv_addrs + 4;
+ }
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
+
+#endif /* defined(CONFIG_I2C) */
+
+#if defined(CONFIG_SPI)
+
+/* Load an spi sub-device. */
+
+void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
+ /* the owner is the same as the spi_device's driver owner */
+ sd->owner = spi->dev.driver->owner;
+ /* spi_device and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, spi);
+ spi_set_drvdata(spi, sd);
+ /* initialize name */
+ strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
+
+struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
+ struct spi_master *master, struct spi_board_info *info)
+{
+ struct v4l2_subdev *sd = NULL;
+ struct spi_device *spi = NULL;
+
+ BUG_ON(!v4l2_dev);
+
+ if (info->modalias[0])
+ request_module(info->modalias);
+
+ spi = spi_new_device(master, info);
+
+ if (spi == NULL || spi->dev.driver == NULL)
+ goto error;
+
+ if (!try_module_get(spi->dev.driver->owner))
+ goto error;
+
+ sd = spi_get_drvdata(spi);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(v4l2_dev, sd))
+ sd = NULL;
+
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(spi->dev.driver->owner);
+
+error:
+ /* If we have a client but no subdev, then something went wrong and
+ we must unregister the client. */
+ if (spi && sd == NULL)
+ spi_unregister_device(spi);
+
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
+
+#endif /* defined(CONFIG_SPI) */
+
+/* Clamp x to be between min and max, aligned to a multiple of 2^align. min
+ * and max don't have to be aligned, but there must be at least one valid
+ * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples
+ * of 16 between 17 and 31. */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+ unsigned int max, unsigned int align)
+{
+ /* Bits that must be zero to be aligned */
+ unsigned int mask = ~((1 << align) - 1);
+
+ /* Round to nearest aligned value */
+ if (align)
+ x = (x + (1 << (align - 1))) & mask;
+
+ /* Clamp to aligned value of min and max */
+ if (x < min)
+ x = (min + ~mask) & mask;
+ else if (x > max)
+ x = max & mask;
+
+ return x;
+}
+
+/* Bound an image to have a width between wmin and wmax, and height between
+ * hmin and hmax, inclusive. Additionally, the width will be a multiple of
+ * 2^walign, the height will be a multiple of 2^halign, and the overall size
+ * (width*height) will be a multiple of 2^salign. The image may be shrunk
+ * or enlarged to fit the alignment constraints.
+ *
+ * The width or height maximum must not be smaller than the corresponding
+ * minimum. The alignments must not be so high there are no possible image
+ * sizes within the allowed bounds. wmin and hmin must be at least 1
+ * (don't use 0). If you don't care about a certain alignment, specify 0,
+ * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If
+ * you only want to adjust downward, specify a maximum that's the same as
+ * the initial value.
+ */
+void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
+ unsigned int walign,
+ u32 *h, unsigned int hmin, unsigned int hmax,
+ unsigned int halign, unsigned int salign)
+{
+ *w = clamp_align(*w, wmin, wmax, walign);
+ *h = clamp_align(*h, hmin, hmax, halign);
+
+ /* Usually we don't need to align the size and are done now. */
+ if (!salign)
+ return;
+
+ /* How much alignment do we have? */
+ walign = __ffs(*w);
+ halign = __ffs(*h);
+ /* Enough to satisfy the image alignment? */
+ if (walign + halign < salign) {
+ /* Max walign where there is still a valid width */
+ unsigned int wmaxa = __fls(wmax ^ (wmin - 1));
+ /* Max halign where there is still a valid height */
+ unsigned int hmaxa = __fls(hmax ^ (hmin - 1));
+
+ /* up the smaller alignment until we have enough */
+ do {
+ if (halign >= hmaxa ||
+ (walign <= halign && walign < wmaxa)) {
+ *w = clamp_align(*w, wmin, wmax, walign + 1);
+ walign = __ffs(*w);
+ } else {
+ *h = clamp_align(*h, hmin, hmax, halign + 1);
+ halign = __ffs(*h);
+ }
+ } while (halign + walign < salign);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l_bound_align_image);
+
+/**
+ * v4l_fill_dv_preset_info - fill description of a digital video preset
+ * @preset - preset value
+ * @info - pointer to struct v4l2_dv_enum_preset
+ *
+ * drivers can use this helper function to fill description of dv preset
+ * in info.
+ */
+int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
+{
+ static const struct v4l2_dv_preset_info {
+ u16 width;
+ u16 height;
+ const char *name;
+ } dv_presets[] = {
+ { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */
+ { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */
+ { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */
+ { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */
+ { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */
+ { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */
+ { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */
+ { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */
+ { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */
+ { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */
+ { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */
+ { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */
+ { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */
+ { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */
+ { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */
+ { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */
+ { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */
+ { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */
+ { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */
+ };
+
+ if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
+ return -EINVAL;
+
+ info->preset = preset;
+ info->width = dv_presets[preset].width;
+ info->height = dv_presets[preset].height;
+ strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
+
+/**
+ * v4l_match_dv_timings - check if two timings match
+ * @t1 - compare this v4l2_dv_timings struct...
+ * @t2 - with this struct.
+ * @pclock_delta - the allowed pixelclock deviation.
+ *
+ * Compare t1 with t2 with a given margin of error for the pixelclock.
+ */
+bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1,
+ const struct v4l2_dv_timings *t2,
+ unsigned pclock_delta)
+{
+ if (t1->type != t2->type || t1->type != V4L2_DV_BT_656_1120)
+ return false;
+ if (t1->bt.width == t2->bt.width &&
+ t1->bt.height == t2->bt.height &&
+ t1->bt.interlaced == t2->bt.interlaced &&
+ t1->bt.polarities == t2->bt.polarities &&
+ t1->bt.pixelclock >= t2->bt.pixelclock - pclock_delta &&
+ t1->bt.pixelclock <= t2->bt.pixelclock + pclock_delta &&
+ t1->bt.hfrontporch == t2->bt.hfrontporch &&
+ t1->bt.vfrontporch == t2->bt.vfrontporch &&
+ t1->bt.vsync == t2->bt.vsync &&
+ t1->bt.vbackporch == t2->bt.vbackporch &&
+ (!t1->bt.interlaced ||
+ (t1->bt.il_vfrontporch == t2->bt.il_vfrontporch &&
+ t1->bt.il_vsync == t2->bt.il_vsync &&
+ t1->bt.il_vbackporch == t2->bt.il_vbackporch)))
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(v4l_match_dv_timings);
+
+/*
+ * CVT defines
+ * Based on Coordinated Video Timings Standard
+ * version 1.1 September 10, 2003
+ */
+
+#define CVT_PXL_CLK_GRAN 250000 /* pixel clock granularity */
+
+/* Normal blanking */
+#define CVT_MIN_V_BPORCH 7 /* lines */
+#define CVT_MIN_V_PORCH_RND 3 /* lines */
+#define CVT_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */
+
+/* Normal blanking for CVT uses GTF to calculate horizontal blanking */
+#define CVT_CELL_GRAN 8 /* character cell granularity */
+#define CVT_M 600 /* blanking formula gradient */
+#define CVT_C 40 /* blanking formula offset */
+#define CVT_K 128 /* blanking formula scaling factor */
+#define CVT_J 20 /* blanking formula scaling factor */
+#define CVT_C_PRIME (((CVT_C - CVT_J) * CVT_K / 256) + CVT_J)
+#define CVT_M_PRIME (CVT_K * CVT_M / 256)
+
+/* Reduced Blanking */
+#define CVT_RB_MIN_V_BPORCH 7 /* lines */
+#define CVT_RB_V_FPORCH 3 /* lines */
+#define CVT_RB_MIN_V_BLANK 460 /* us */
+#define CVT_RB_H_SYNC 32 /* pixels */
+#define CVT_RB_H_BPORCH 80 /* pixels */
+#define CVT_RB_H_BLANK 160 /* pixels */
+
+/** v4l2_detect_cvt - detect if the given timings follow the CVT standard
+ * @frame_height - the total height of the frame (including blanking) in lines.
+ * @hfreq - the horizontal frequency in Hz.
+ * @vsync - the height of the vertical sync in lines.
+ * @polarities - the horizontal and vertical polarities (same as struct
+ * v4l2_bt_timings polarities).
+ * @fmt - the resulting timings.
+ *
+ * This function will attempt to detect if the given values correspond to a
+ * valid CVT format. If so, then it will return true, and fmt will be filled
+ * in with the found CVT timings.
+ */
+bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync,
+ u32 polarities, struct v4l2_dv_timings *fmt)
+{
+ int v_fp, v_bp, h_fp, h_bp, hsync;
+ int frame_width, image_height, image_width;
+ bool reduced_blanking;
+ unsigned pix_clk;
+
+ if (vsync < 4 || vsync > 7)
+ return false;
+
+ if (polarities == V4L2_DV_VSYNC_POS_POL)
+ reduced_blanking = false;
+ else if (polarities == V4L2_DV_HSYNC_POS_POL)
+ reduced_blanking = true;
+ else
+ return false;
+
+ /* Vertical */
+ if (reduced_blanking) {
+ v_fp = CVT_RB_V_FPORCH;
+ v_bp = (CVT_RB_MIN_V_BLANK * hfreq + 999999) / 1000000;
+ v_bp -= vsync + v_fp;
+
+ if (v_bp < CVT_RB_MIN_V_BPORCH)
+ v_bp = CVT_RB_MIN_V_BPORCH;
+ } else {
+ v_fp = CVT_MIN_V_PORCH_RND;
+ v_bp = (CVT_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync;
+
+ if (v_bp < CVT_MIN_V_BPORCH)
+ v_bp = CVT_MIN_V_BPORCH;
+ }
+ image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1;
+
+ /* Aspect ratio based on vsync */
+ switch (vsync) {
+ case 4:
+ image_width = (image_height * 4) / 3;
+ break;
+ case 5:
+ image_width = (image_height * 16) / 9;
+ break;
+ case 6:
+ image_width = (image_height * 16) / 10;
+ break;
+ case 7:
+ /* special case */
+ if (image_height == 1024)
+ image_width = (image_height * 5) / 4;
+ else if (image_height == 768)
+ image_width = (image_height * 15) / 9;
+ else
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ image_width = image_width & ~7;
+
+ /* Horizontal */
+ if (reduced_blanking) {
+ pix_clk = (image_width + CVT_RB_H_BLANK) * hfreq;
+ pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN;
+
+ h_bp = CVT_RB_H_BPORCH;
+ hsync = CVT_RB_H_SYNC;
+ h_fp = CVT_RB_H_BLANK - h_bp - hsync;
+
+ frame_width = image_width + CVT_RB_H_BLANK;
+ } else {
+ int h_blank;
+ unsigned ideal_duty_cycle = CVT_C_PRIME - (CVT_M_PRIME * 1000) / hfreq;
+
+ h_blank = (image_width * ideal_duty_cycle + (100 - ideal_duty_cycle) / 2) /
+ (100 - ideal_duty_cycle);
+ h_blank = h_blank - h_blank % (2 * CVT_CELL_GRAN);
+
+ if (h_blank * 100 / image_width < 20) {
+ h_blank = image_width / 5;
+ h_blank = (h_blank + 0x7) & ~0x7;
+ }
+
+ pix_clk = (image_width + h_blank) * hfreq;
+ pix_clk = (pix_clk / CVT_PXL_CLK_GRAN) * CVT_PXL_CLK_GRAN;
+
+ h_bp = h_blank / 2;
+ frame_width = image_width + h_blank;
+
+ hsync = (frame_width * 8 + 50) / 100;
+ hsync = hsync - hsync % CVT_CELL_GRAN;
+ h_fp = h_blank - hsync - h_bp;
+ }
+
+ fmt->bt.polarities = polarities;
+ fmt->bt.width = image_width;
+ fmt->bt.height = image_height;
+ fmt->bt.hfrontporch = h_fp;
+ fmt->bt.vfrontporch = v_fp;
+ fmt->bt.hsync = hsync;
+ fmt->bt.vsync = vsync;
+ fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync;
+ fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync;
+ fmt->bt.pixelclock = pix_clk;
+ fmt->bt.standards = V4L2_DV_BT_STD_CVT;
+ if (reduced_blanking)
+ fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ return true;
+}
+EXPORT_SYMBOL_GPL(v4l2_detect_cvt);
+
+/*
+ * GTF defines
+ * Based on Generalized Timing Formula Standard
+ * Version 1.1 September 2, 1999
+ */
+
+#define GTF_PXL_CLK_GRAN 250000 /* pixel clock granularity */
+
+#define GTF_MIN_VSYNC_BP 550 /* min time of vsync + back porch (us) */
+#define GTF_V_FP 1 /* vertical front porch (lines) */
+#define GTF_CELL_GRAN 8 /* character cell granularity */
+
+/* Default */
+#define GTF_D_M 600 /* blanking formula gradient */
+#define GTF_D_C 40 /* blanking formula offset */
+#define GTF_D_K 128 /* blanking formula scaling factor */
+#define GTF_D_J 20 /* blanking formula scaling factor */
+#define GTF_D_C_PRIME ((((GTF_D_C - GTF_D_J) * GTF_D_K) / 256) + GTF_D_J)
+#define GTF_D_M_PRIME ((GTF_D_K * GTF_D_M) / 256)
+
+/* Secondary */
+#define GTF_S_M 3600 /* blanking formula gradient */
+#define GTF_S_C 40 /* blanking formula offset */
+#define GTF_S_K 128 /* blanking formula scaling factor */
+#define GTF_S_J 35 /* blanking formula scaling factor */
+#define GTF_S_C_PRIME ((((GTF_S_C - GTF_S_J) * GTF_S_K) / 256) + GTF_S_J)
+#define GTF_S_M_PRIME ((GTF_S_K * GTF_S_M) / 256)
+
+/** v4l2_detect_gtf - detect if the given timings follow the GTF standard
+ * @frame_height - the total height of the frame (including blanking) in lines.
+ * @hfreq - the horizontal frequency in Hz.
+ * @vsync - the height of the vertical sync in lines.
+ * @polarities - the horizontal and vertical polarities (same as struct
+ * v4l2_bt_timings polarities).
+ * @aspect - preferred aspect ratio. GTF has no method of determining the
+ * aspect ratio in order to derive the image width from the
+ * image height, so it has to be passed explicitly. Usually
+ * the native screen aspect ratio is used for this. If it
+ * is not filled in correctly, then 16:9 will be assumed.
+ * @fmt - the resulting timings.
+ *
+ * This function will attempt to detect if the given values correspond to a
+ * valid GTF format. If so, then it will return true, and fmt will be filled
+ * in with the found GTF timings.
+ */
+bool v4l2_detect_gtf(unsigned frame_height,
+ unsigned hfreq,
+ unsigned vsync,
+ u32 polarities,
+ struct v4l2_fract aspect,
+ struct v4l2_dv_timings *fmt)
+{
+ int pix_clk;
+ int v_fp, v_bp, h_fp, h_bp, hsync;
+ int frame_width, image_height, image_width;
+ bool default_gtf;
+ int h_blank;
+
+ if (vsync != 3)
+ return false;
+
+ if (polarities == V4L2_DV_VSYNC_POS_POL)
+ default_gtf = true;
+ else if (polarities == V4L2_DV_HSYNC_POS_POL)
+ default_gtf = false;
+ else
+ return false;
+
+ /* Vertical */
+ v_fp = GTF_V_FP;
+ v_bp = (GTF_MIN_VSYNC_BP * hfreq + 999999) / 1000000 - vsync;
+ image_height = (frame_height - v_fp - vsync - v_bp + 1) & ~0x1;
+
+ if (aspect.numerator == 0 || aspect.denominator == 0) {
+ aspect.numerator = 16;
+ aspect.denominator = 9;
+ }
+ image_width = ((image_height * aspect.numerator) / aspect.denominator);
+
+ /* Horizontal */
+ if (default_gtf)
+ h_blank = ((image_width * GTF_D_C_PRIME * hfreq) -
+ (image_width * GTF_D_M_PRIME * 1000) +
+ (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000) / 2) /
+ (hfreq * (100 - GTF_D_C_PRIME) + GTF_D_M_PRIME * 1000);
+ else
+ h_blank = ((image_width * GTF_S_C_PRIME * hfreq) -
+ (image_width * GTF_S_M_PRIME * 1000) +
+ (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000) / 2) /
+ (hfreq * (100 - GTF_S_C_PRIME) + GTF_S_M_PRIME * 1000);
+
+ h_blank = h_blank - h_blank % (2 * GTF_CELL_GRAN);
+ frame_width = image_width + h_blank;
+
+ pix_clk = (image_width + h_blank) * hfreq;
+ pix_clk = pix_clk / GTF_PXL_CLK_GRAN * GTF_PXL_CLK_GRAN;
+
+ hsync = (frame_width * 8 + 50) / 100;
+ hsync = hsync - hsync % GTF_CELL_GRAN;
+
+ h_fp = h_blank / 2 - hsync;
+ h_bp = h_blank / 2;
+
+ fmt->bt.polarities = polarities;
+ fmt->bt.width = image_width;
+ fmt->bt.height = image_height;
+ fmt->bt.hfrontporch = h_fp;
+ fmt->bt.vfrontporch = v_fp;
+ fmt->bt.hsync = hsync;
+ fmt->bt.vsync = vsync;
+ fmt->bt.hbackporch = frame_width - image_width - h_fp - hsync;
+ fmt->bt.vbackporch = frame_height - image_height - v_fp - vsync;
+ fmt->bt.pixelclock = pix_clk;
+ fmt->bt.standards = V4L2_DV_BT_STD_GTF;
+ if (!default_gtf)
+ fmt->bt.flags |= V4L2_DV_FL_REDUCED_BLANKING;
+ return true;
+}
+EXPORT_SYMBOL_GPL(v4l2_detect_gtf);
+
+/** v4l2_calc_aspect_ratio - calculate the aspect ratio based on bytes
+ * 0x15 and 0x16 from the EDID.
+ * @hor_landscape - byte 0x15 from the EDID.
+ * @vert_portrait - byte 0x16 from the EDID.
+ *
+ * Determines the aspect ratio from the EDID.
+ * See VESA Enhanced EDID standard, release A, rev 2, section 3.6.2:
+ * "Horizontal and Vertical Screen Size or Aspect Ratio"
+ */
+struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait)
+{
+ struct v4l2_fract aspect = { 16, 9 };
+ u32 tmp;
+ u8 ratio;
+
+ /* Nothing filled in, fallback to 16:9 */
+ if (!hor_landscape && !vert_portrait)
+ return aspect;
+ /* Both filled in, so they are interpreted as the screen size in cm */
+ if (hor_landscape && vert_portrait) {
+ aspect.numerator = hor_landscape;
+ aspect.denominator = vert_portrait;
+ return aspect;
+ }
+ /* Only one is filled in, so interpret them as a ratio:
+ (val + 99) / 100 */
+ ratio = hor_landscape | vert_portrait;
+ /* Change some rounded values into the exact aspect ratio */
+ if (ratio == 79) {
+ aspect.numerator = 16;
+ aspect.denominator = 9;
+ } else if (ratio == 34) {
+ aspect.numerator = 4;
+ aspect.numerator = 3;
+ } else if (ratio == 68) {
+ aspect.numerator = 15;
+ aspect.numerator = 9;
+ } else {
+ aspect.numerator = hor_landscape + 99;
+ aspect.denominator = 100;
+ }
+ if (hor_landscape)
+ return aspect;
+ /* The aspect ratio is for portrait, so swap numerator and denominator */
+ tmp = aspect.denominator;
+ aspect.denominator = aspect.numerator;
+ aspect.numerator = tmp;
+ return aspect;
+}
+EXPORT_SYMBOL_GPL(v4l2_calc_aspect_ratio);
+
+const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
+ const struct v4l2_discrete_probe *probe,
+ s32 width, s32 height)
+{
+ int i;
+ u32 error, min_error = UINT_MAX;
+ const struct v4l2_frmsize_discrete *size, *best = NULL;
+
+ if (!probe)
+ return best;
+
+ for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
+ error = abs(size->width - width) + abs(size->height - height);
+ if (error < min_error) {
+ min_error = error;
+ best = size;
+ }
+ if (!error)
+ break;
+ }
+
+ return best;
+}
+EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
new file mode 100644
index 000000000000..83ffb6436baf
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -0,0 +1,1094 @@
+/*
+ * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
+ * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
+ *
+ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
+ * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
+ * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
+ * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * These routines maintain argument size conversion between 32bit and 64bit
+ * ioctls.
+ */
+
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <linux/v4l2-subdev.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+
+static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret = -ENOIOCTLCMD;
+
+ if (file->f_op->unlocked_ioctl)
+ ret = file->f_op->unlocked_ioctl(file, cmd, arg);
+
+ return ret;
+}
+
+
+struct v4l2_clip32 {
+ struct v4l2_rect c;
+ compat_caddr_t next;
+};
+
+struct v4l2_window32 {
+ struct v4l2_rect w;
+ __u32 field; /* enum v4l2_field */
+ __u32 chromakey;
+ compat_caddr_t clips; /* actually struct v4l2_clip32 * */
+ __u32 clipcount;
+ compat_caddr_t bitmap;
+};
+
+static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
+ copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
+ get_user(kp->field, &up->field) ||
+ get_user(kp->chromakey, &up->chromakey) ||
+ get_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ if (kp->clipcount > 2048)
+ return -EINVAL;
+ if (kp->clipcount) {
+ struct v4l2_clip32 __user *uclips;
+ struct v4l2_clip __user *kclips;
+ int n = kp->clipcount;
+ compat_caddr_t p;
+
+ if (get_user(p, &up->clips))
+ return -EFAULT;
+ uclips = compat_ptr(p);
+ kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
+ kp->clips = kclips;
+ while (--n >= 0) {
+ if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
+ return -EFAULT;
+ if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+ return -EFAULT;
+ uclips += 1;
+ kclips += 1;
+ }
+ } else
+ kp->clips = NULL;
+ return 0;
+}
+
+static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+{
+ if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->chromakey, &up->chromakey) ||
+ put_user(kp->clipcount, &up->clipcount))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
+ return -EFAULT;
+ 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)))
+ return -EFAULT;
+ 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)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_format32 {
+ __u32 type; /* enum v4l2_buf_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;
+ __u8 raw_data[200]; /* user-defined */
+ } fmt;
+};
+
+/**
+ * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
+ * @index: on return, index of the first created buffer
+ * @count: entry: number of requested buffers,
+ * return: number of created buffers
+ * @memory: buffer memory type
+ * @format: frame format, for which buffers are requested
+ * @reserved: future extensions
+ */
+struct v4l2_create_buffers32 {
+ __u32 index;
+ __u32 count;
+ __u32 memory; /* enum v4l2_memory */
+ struct v4l2_format32 format;
+ __u32 reserved[8];
+};
+
+static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ switch (kp->type) {
+ 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);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
+ get_user(kp->type, &up->type))
+ return -EFAULT;
+ return __get_v4l2_format32(kp, up);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __get_v4l2_format32(&kp->format, &up->format);
+}
+
+static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ switch (kp->type) {
+ 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);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+ default:
+ printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
+ kp->type);
+ return -EINVAL;
+ }
+}
+
+static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
+ put_user(kp->type, &up->type))
+ return -EFAULT;
+ return __put_v4l2_format32(kp, up);
+}
+
+static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+ copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ return -EFAULT;
+ return __put_v4l2_format32(&kp->format, &up->format);
+}
+
+struct v4l2_standard32 {
+ __u32 index;
+ __u32 id[2]; /* __u64 would get the alignment wrong */
+ __u8 name[24];
+ struct v4l2_fract frameperiod; /* Frames, not fields */
+ __u32 framelines;
+ __u32 reserved[4];
+};
+
+static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ /* other fields are not set by the user, nor used by the driver */
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
+ get_user(kp->index, &up->index))
+ return -EFAULT;
+ return 0;
+}
+
+static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+ put_user(kp->index, &up->index) ||
+ copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
+ copy_to_user(up->name, kp->name, 24) ||
+ copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
+ put_user(kp->framelines, &up->framelines) ||
+ copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
+ return -EFAULT;
+ 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;
+ __u32 type; /* enum v4l2_buf_type */
+ __u32 bytesused;
+ __u32 flags;
+ __u32 field; /* enum v4l2_field */
+ struct compat_timeval timestamp;
+ struct v4l2_timecode timecode;
+ __u32 sequence;
+
+ /* memory location */
+ __u32 memory; /* enum v4l2_memory */
+ union {
+ __u32 offset;
+ compat_long_t userptr;
+ compat_caddr_t planes;
+ } m;
+ __u32 length;
+ __u32 reserved2;
+ __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) ||
+ get_user(kp->type, &up->type) ||
+ get_user(kp->flags, &up->flags) ||
+ get_user(kp->memory, &up->memory))
+ return -EFAULT;
+
+ 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;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
+ if (get_user(kp->length, &up->length))
+ return -EFAULT;
+
+ 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;
+ }
+
+ if (get_user(p, &up->m.planes))
+ return -EFAULT;
+
+ 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) ||
+ put_user(kp->flags, &up->flags) ||
+ put_user(kp->memory, &up->memory))
+ return -EFAULT;
+
+ if (put_user(kp->bytesused, &up->bytesused) ||
+ put_user(kp->field, &up->field) ||
+ put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
+ copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_user(kp->reserved2, &up->reserved2) ||
+ 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;
+}
+
+struct v4l2_framebuffer32 {
+ __u32 capability;
+ __u32 flags;
+ compat_caddr_t base;
+ struct v4l2_pix_format fmt;
+};
+
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
+ get_user(tmp, &up->base) ||
+ get_user(kp->capability, &up->capability) ||
+ get_user(kp->flags, &up->flags))
+ return -EFAULT;
+ kp->base = compat_ptr(tmp);
+ get_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+{
+ u32 tmp = (u32)((unsigned long)kp->base);
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+ put_user(tmp, &up->base) ||
+ put_user(kp->capability, &up->capability) ||
+ put_user(kp->flags, &up->flags))
+ return -EFAULT;
+ put_v4l2_pix_format(&kp->fmt, &up->fmt);
+ return 0;
+}
+
+struct v4l2_input32 {
+ __u32 index; /* Which input */
+ __u8 name[32]; /* Label */
+ __u32 type; /* Type of input */
+ __u32 audioset; /* Associated audios (bitfield) */
+ __u32 tuner; /* Associated tuner */
+ v4l2_std_id std;
+ __u32 status;
+ __u32 reserved[4];
+} __attribute__ ((packed));
+
+/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
+ Otherwise it is identical to the 32-bit version. */
+static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+{
+ if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_ext_controls32 {
+ __u32 ctrl_class;
+ __u32 count;
+ __u32 error_idx;
+ __u32 reserved[2];
+ compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
+};
+
+struct v4l2_ext_control32 {
+ __u32 id;
+ __u32 size;
+ __u32 reserved2[1];
+ union {
+ __s32 value;
+ __s64 value64;
+ compat_caddr_t string; /* actually char * */
+ };
+} __attribute__ ((packed));
+
+/* The following function really belong in v4l2-common, but that causes
+ a circular dependency between modules. We need to think about this, but
+ for now this will do. */
+
+/* Return non-zero if this control is a pointer type. Currently only
+ type STRING is a pointer type. */
+static inline int ctrl_is_pointer(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control32 __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols;
+ int n;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+ get_user(kp->ctrl_class, &up->ctrl_class) ||
+ get_user(kp->count, &up->count) ||
+ get_user(kp->error_idx, &up->error_idx) ||
+ copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ return -EFAULT;
+ n = kp->count;
+ if (n == 0) {
+ kp->controls = NULL;
+ return 0;
+ }
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ 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(*ucontrols)))
+ return -EFAULT;
+ if (ctrl_is_pointer(kcontrols->id)) {
+ void __user *s;
+
+ if (get_user(p, &ucontrols->string))
+ return -EFAULT;
+ s = compat_ptr(p);
+ if (put_user(s, &kcontrols->string))
+ return -EFAULT;
+ }
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+{
+ struct v4l2_ext_control32 __user *ucontrols;
+ struct v4l2_ext_control __user *kcontrols = kp->controls;
+ int n = kp->count;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+ put_user(kp->ctrl_class, &up->ctrl_class) ||
+ put_user(kp->count, &up->count) ||
+ put_user(kp->error_idx, &up->error_idx) ||
+ copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ return -EFAULT;
+ if (!kp->count)
+ return 0;
+
+ if (get_user(p, &up->controls))
+ return -EFAULT;
+ ucontrols = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, ucontrols,
+ n * sizeof(struct v4l2_ext_control32)))
+ return -EFAULT;
+
+ while (--n >= 0) {
+ unsigned size = sizeof(*ucontrols);
+
+ /* Do not modify the pointer when copying a pointer control.
+ The contents of the pointer was changed, not the pointer
+ itself. */
+ if (ctrl_is_pointer(kcontrols->id))
+ size -= sizeof(ucontrols->value64);
+ if (copy_in_user(ucontrols, kcontrols, size))
+ return -EFAULT;
+ ucontrols++;
+ kcontrols++;
+ }
+ return 0;
+}
+
+struct v4l2_event32 {
+ __u32 type;
+ union {
+ __u8 data[64];
+ } u;
+ __u32 pending;
+ __u32 sequence;
+ struct compat_timespec timestamp;
+ __u32 id;
+ __u32 reserved[8];
+};
+
+static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+{
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+ put_user(kp->type, &up->type) ||
+ copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
+ put_user(kp->pending, &up->pending) ||
+ put_user(kp->sequence, &up->sequence) ||
+ put_compat_timespec(&kp->timestamp, &up->timestamp) ||
+ put_user(kp->id, &up->id) ||
+ copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+ return -EFAULT;
+ return 0;
+}
+
+struct v4l2_subdev_edid32 {
+ __u32 pad;
+ __u32 start_block;
+ __u32 blocks;
+ __u32 reserved[5];
+ compat_caddr_t edid;
+};
+
+static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+{
+ u32 tmp;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_subdev_edid32)) ||
+ get_user(kp->pad, &up->pad) ||
+ get_user(kp->start_block, &up->start_block) ||
+ get_user(kp->blocks, &up->blocks) ||
+ get_user(tmp, &up->edid) ||
+ copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ return -EFAULT;
+ kp->edid = compat_ptr(tmp);
+ return 0;
+}
+
+static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+{
+ u32 tmp = (u32)((unsigned long)kp->edid);
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_subdev_edid32)) ||
+ put_user(kp->pad, &up->pad) ||
+ put_user(kp->start_block, &up->start_block) ||
+ put_user(kp->blocks, &up->blocks) ||
+ put_user(tmp, &up->edid) ||
+ copy_to_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ return -EFAULT;
+ return 0;
+}
+
+
+#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
+#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
+#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
+#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
+#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
+#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32)
+#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
+#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
+#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
+#define VIDIOC_SUBDEV_G_EDID32 _IOWR('V', 63, struct v4l2_subdev_edid32)
+#define VIDIOC_SUBDEV_S_EDID32 _IOWR('V', 64, struct v4l2_subdev_edid32)
+#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
+#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
+#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
+#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
+#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32)
+#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32)
+#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32)
+
+#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
+#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
+#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
+#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
+#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32)
+#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
+#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
+
+static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ union {
+ struct v4l2_format v2f;
+ struct v4l2_buffer v2b;
+ struct v4l2_framebuffer v2fb;
+ struct v4l2_input v2i;
+ struct v4l2_standard v2s;
+ struct v4l2_ext_controls v2ecs;
+ struct v4l2_event v2ev;
+ struct v4l2_create_buffers v2crt;
+ struct v4l2_subdev_edid v2edid;
+ unsigned long vx;
+ int vi;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ int compatible_arg = 1;
+ long err = 0;
+
+ /* First, convert the command. */
+ switch (cmd) {
+ case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
+ case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
+ case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
+ case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
+ case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
+ case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
+ case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
+ case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
+ case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
+ case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+ case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
+ case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
+ case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
+ case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
+ case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+ case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
+ case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
+ case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
+ case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
+ case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
+ case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
+ case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
+ case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
+ case VIDIOC_SUBDEV_G_EDID32: cmd = VIDIOC_SUBDEV_G_EDID; break;
+ case VIDIOC_SUBDEV_S_EDID32: cmd = VIDIOC_SUBDEV_S_EDID; break;
+ }
+
+ switch (cmd) {
+ case VIDIOC_OVERLAY:
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ err = get_user(karg.vi, (s32 __user *)up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_SUBDEV_G_EDID:
+ case VIDIOC_SUBDEV_S_EDID:
+ err = get_v4l2_subdev_edid32(&karg.v2edid, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = get_v4l2_format32(&karg.v2f, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_CREATE_BUFS:
+ err = get_v4l2_create32(&karg.v2crt, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_PREPARE_BUF:
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = get_v4l2_buffer32(&karg.v2b, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_S_FBUF:
+ err = get_v4l2_framebuffer32(&karg.v2fb, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_FBUF:
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = get_v4l2_standard32(&karg.v2s, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = get_v4l2_input32(&karg.v2i, up);
+ compatible_arg = 0;
+ break;
+
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+ compatible_arg = 0;
+ break;
+ case VIDIOC_DQEVENT:
+ compatible_arg = 0;
+ break;
+ }
+ if (err)
+ return err;
+
+ if (compatible_arg)
+ err = native_ioctl(file, cmd, (unsigned long)up);
+ else {
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = native_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+ }
+
+ /* Special case: even after an error we need to put the
+ results back for these ioctls since the error_idx will
+ contain information on which control failed. */
+ switch (cmd) {
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+ err = -EFAULT;
+ break;
+ }
+ if (err)
+ return err;
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_OUTPUT:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_G_OUTPUT:
+ err = put_user(((s32)karg.vi), (s32 __user *)up);
+ break;
+
+ case VIDIOC_G_FBUF:
+ err = put_v4l2_framebuffer32(&karg.v2fb, up);
+ break;
+
+ case VIDIOC_DQEVENT:
+ err = put_v4l2_event32(&karg.v2ev, up);
+ break;
+
+ case VIDIOC_SUBDEV_G_EDID:
+ case VIDIOC_SUBDEV_S_EDID:
+ err = put_v4l2_subdev_edid32(&karg.v2edid, up);
+ break;
+
+ case VIDIOC_G_FMT:
+ case VIDIOC_S_FMT:
+ case VIDIOC_TRY_FMT:
+ err = put_v4l2_format32(&karg.v2f, up);
+ break;
+
+ case VIDIOC_CREATE_BUFS:
+ err = put_v4l2_create32(&karg.v2crt, up);
+ break;
+
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ err = put_v4l2_buffer32(&karg.v2b, up);
+ break;
+
+ case VIDIOC_ENUMSTD:
+ err = put_v4l2_standard32(&karg.v2s, up);
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ err = put_v4l2_input32(&karg.v2i, up);
+ break;
+ }
+ return err;
+}
+
+long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ long ret = -ENOIOCTLCMD;
+
+ if (!file->f_op->unlocked_ioctl)
+ return ret;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ case VIDIOC_RESERVED:
+ case VIDIOC_ENUM_FMT:
+ case VIDIOC_G_FMT32:
+ case VIDIOC_S_FMT32:
+ case VIDIOC_REQBUFS:
+ case VIDIOC_QUERYBUF32:
+ case VIDIOC_G_FBUF32:
+ case VIDIOC_S_FBUF32:
+ case VIDIOC_OVERLAY32:
+ case VIDIOC_QBUF32:
+ case VIDIOC_DQBUF32:
+ case VIDIOC_STREAMON32:
+ case VIDIOC_STREAMOFF32:
+ case VIDIOC_G_PARM:
+ case VIDIOC_S_PARM:
+ case VIDIOC_G_STD:
+ case VIDIOC_S_STD:
+ case VIDIOC_ENUMSTD32:
+ case VIDIOC_ENUMINPUT32:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_G_TUNER:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_G_AUDIO:
+ case VIDIOC_S_AUDIO:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_G_INPUT32:
+ case VIDIOC_S_INPUT32:
+ case VIDIOC_G_OUTPUT32:
+ case VIDIOC_S_OUTPUT32:
+ case VIDIOC_ENUMOUTPUT:
+ case VIDIOC_G_AUDOUT:
+ case VIDIOC_S_AUDOUT:
+ case VIDIOC_G_MODULATOR:
+ case VIDIOC_S_MODULATOR:
+ case VIDIOC_S_FREQUENCY:
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ case VIDIOC_G_SELECTION:
+ case VIDIOC_S_SELECTION:
+ case VIDIOC_G_JPEGCOMP:
+ case VIDIOC_S_JPEGCOMP:
+ case VIDIOC_QUERYSTD:
+ case VIDIOC_TRY_FMT32:
+ case VIDIOC_ENUMAUDIO:
+ case VIDIOC_ENUMAUDOUT:
+ case VIDIOC_G_PRIORITY:
+ case VIDIOC_S_PRIORITY:
+ case VIDIOC_G_SLICED_VBI_CAP:
+ case VIDIOC_LOG_STATUS:
+ case VIDIOC_G_EXT_CTRLS32:
+ case VIDIOC_S_EXT_CTRLS32:
+ case VIDIOC_TRY_EXT_CTRLS32:
+ case VIDIOC_ENUM_FRAMESIZES:
+ case VIDIOC_ENUM_FRAMEINTERVALS:
+ case VIDIOC_G_ENC_INDEX:
+ case VIDIOC_ENCODER_CMD:
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_DECODER_CMD:
+ case VIDIOC_TRY_DECODER_CMD:
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_G_CHIP_IDENT:
+ case VIDIOC_S_HW_FREQ_SEEK:
+ case VIDIOC_ENUM_DV_PRESETS:
+ case VIDIOC_S_DV_PRESET:
+ case VIDIOC_G_DV_PRESET:
+ case VIDIOC_QUERY_DV_PRESET:
+ case VIDIOC_S_DV_TIMINGS:
+ case VIDIOC_G_DV_TIMINGS:
+ case VIDIOC_DQEVENT:
+ case VIDIOC_DQEVENT32:
+ case VIDIOC_SUBSCRIBE_EVENT:
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ case VIDIOC_CREATE_BUFS32:
+ case VIDIOC_PREPARE_BUF32:
+ case VIDIOC_ENUM_DV_TIMINGS:
+ case VIDIOC_QUERY_DV_TIMINGS:
+ case VIDIOC_DV_TIMINGS_CAP:
+ case VIDIOC_ENUM_FREQ_BANDS:
+ case VIDIOC_SUBDEV_G_EDID32:
+ case VIDIOC_SUBDEV_S_EDID32:
+ ret = do_video_ioctl(file, cmd, arg);
+ break;
+
+ default:
+ if (vdev->fops->compat_ioctl32)
+ ret = vdev->fops->compat_ioctl32(file, cmd, arg);
+
+ if (ret == -ENOIOCTLCMD)
+ printk(KERN_WARNING "compat_ioctl32: "
+ "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+ _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
+ cmd);
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c
new file mode 100644
index 000000000000..631cdc0e0bda
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-ctrls.c
@@ -0,0 +1,2742 @@
+/*
+ V4L2 controls framework implementation.
+
+ Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-dev.h>
+
+#define has_op(master, op) \
+ (master->ops && master->ops->op)
+#define call_op(master, op) \
+ (has_op(master, op) ? master->ops->op(master) : 0)
+
+/* Internal temporary helper struct, one for each v4l2_ext_control */
+struct v4l2_ctrl_helper {
+ /* Pointer to the control reference of the master control */
+ struct v4l2_ctrl_ref *mref;
+ /* The control corresponding to the v4l2_ext_control ID field. */
+ struct v4l2_ctrl *ctrl;
+ /* v4l2_ext_control index of the next control belonging to the
+ same cluster, or 0 if there isn't any. */
+ u32 next;
+};
+
+/* Small helper function to determine if the autocluster is set to manual
+ mode. */
+static bool is_cur_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->cur.val == master->manual_mode_value;
+}
+
+/* Same as above, but this checks the against the new value instead of the
+ current value. */
+static bool is_new_manual(const struct v4l2_ctrl *master)
+{
+ return master->is_auto && master->val == master->manual_mode_value;
+}
+
+/* Returns NULL or a character pointer array containing the menu for
+ the given control ID. The pointer array ends with a NULL pointer.
+ An empty string signifies a menu entry that is invalid. This allows
+ drivers to disable certain options if it is not supported. */
+const char * const *v4l2_ctrl_get_menu(u32 id)
+{
+ static const char * const mpeg_audio_sampling_freq[] = {
+ "44.1 kHz",
+ "48 kHz",
+ "32 kHz",
+ NULL
+ };
+ static const char * const mpeg_audio_encoding[] = {
+ "MPEG-1/2 Layer I",
+ "MPEG-1/2 Layer II",
+ "MPEG-1/2 Layer III",
+ "MPEG-2/4 AAC",
+ "AC-3",
+ NULL
+ };
+ static const char * const mpeg_audio_l1_bitrate[] = {
+ "32 kbps",
+ "64 kbps",
+ "96 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "288 kbps",
+ "320 kbps",
+ "352 kbps",
+ "384 kbps",
+ "416 kbps",
+ "448 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_l2_bitrate[] = {
+ "32 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_l3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_ac3_bitrate[] = {
+ "32 kbps",
+ "40 kbps",
+ "48 kbps",
+ "56 kbps",
+ "64 kbps",
+ "80 kbps",
+ "96 kbps",
+ "112 kbps",
+ "128 kbps",
+ "160 kbps",
+ "192 kbps",
+ "224 kbps",
+ "256 kbps",
+ "320 kbps",
+ "384 kbps",
+ "448 kbps",
+ "512 kbps",
+ "576 kbps",
+ "640 kbps",
+ NULL
+ };
+ static const char * const mpeg_audio_mode[] = {
+ "Stereo",
+ "Joint Stereo",
+ "Dual",
+ "Mono",
+ NULL
+ };
+ static const char * const mpeg_audio_mode_extension[] = {
+ "Bound 4",
+ "Bound 8",
+ "Bound 12",
+ "Bound 16",
+ NULL
+ };
+ static const char * const mpeg_audio_emphasis[] = {
+ "No Emphasis",
+ "50/15 us",
+ "CCITT J17",
+ NULL
+ };
+ static const char * const mpeg_audio_crc[] = {
+ "No CRC",
+ "16-bit CRC",
+ NULL
+ };
+ static const char * const mpeg_audio_dec_playback[] = {
+ "Auto",
+ "Stereo",
+ "Left",
+ "Right",
+ "Mono",
+ "Swapped Stereo",
+ NULL
+ };
+ static const char * const mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4 AVC",
+ NULL
+ };
+ static const char * const mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ "2.21x1",
+ NULL
+ };
+ static const char * const mpeg_video_bitrate_mode[] = {
+ "Variable Bitrate",
+ "Constant Bitrate",
+ NULL
+ };
+ static const char * const mpeg_stream_type[] = {
+ "MPEG-2 Program Stream",
+ "MPEG-2 Transport Stream",
+ "MPEG-1 System Stream",
+ "MPEG-2 DVD-compatible Stream",
+ "MPEG-1 VCD-compatible Stream",
+ "MPEG-2 SVCD-compatible Stream",
+ NULL
+ };
+ static const char * const mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private Packet, IVTV Format",
+ NULL
+ };
+ static const char * const camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ "Auto",
+ NULL
+ };
+ static const char * const camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
+ static const char * const camera_exposure_metering[] = {
+ "Average",
+ "Center Weighted",
+ "Spot",
+ NULL
+ };
+ static const char * const camera_auto_focus_range[] = {
+ "Auto",
+ "Normal",
+ "Macro",
+ "Infinity",
+ NULL
+ };
+ static const char * const colorfx[] = {
+ "None",
+ "Black & White",
+ "Sepia",
+ "Negative",
+ "Emboss",
+ "Sketch",
+ "Sky Blue",
+ "Grass Green",
+ "Skin Whiten",
+ "Vivid",
+ "Aqua",
+ "Art Freeze",
+ "Silhouette",
+ "Solarization",
+ "Antique",
+ "Set Cb/Cr",
+ NULL
+ };
+ static const char * const auto_n_preset_white_balance[] = {
+ "Manual",
+ "Auto",
+ "Incandescent",
+ "Fluorescent",
+ "Fluorescent H",
+ "Horizon",
+ "Daylight",
+ "Flash",
+ "Cloudy",
+ "Shade",
+ NULL,
+ };
+ static const char * const camera_iso_sensitivity_auto[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+ static const char * const scene_mode[] = {
+ "None",
+ "Backlight",
+ "Beach/Snow",
+ "Candle Light",
+ "Dusk/Dawn",
+ "Fall Colors",
+ "Fireworks",
+ "Landscape",
+ "Night",
+ "Party/Indoor",
+ "Portrait",
+ "Sports",
+ "Sunset",
+ "Text",
+ NULL
+ };
+ static const char * const tune_preemphasis[] = {
+ "No Preemphasis",
+ "50 Microseconds",
+ "75 Microseconds",
+ NULL,
+ };
+ static const char * const header_mode[] = {
+ "Separate Buffer",
+ "Joined With 1st Frame",
+ NULL,
+ };
+ static const char * const multi_slice[] = {
+ "Single",
+ "Max Macroblocks",
+ "Max Bytes",
+ NULL,
+ };
+ static const char * const entropy_mode[] = {
+ "CAVLC",
+ "CABAC",
+ NULL,
+ };
+ static const char * const mpeg_h264_level[] = {
+ "1",
+ "1b",
+ "1.1",
+ "1.2",
+ "1.3",
+ "2",
+ "2.1",
+ "2.2",
+ "3",
+ "3.1",
+ "3.2",
+ "4",
+ "4.1",
+ "4.2",
+ "5",
+ "5.1",
+ NULL,
+ };
+ static const char * const h264_loop_filter[] = {
+ "Enabled",
+ "Disabled",
+ "Disabled at Slice Boundary",
+ NULL,
+ };
+ static const char * const h264_profile[] = {
+ "Baseline",
+ "Constrained Baseline",
+ "Main",
+ "Extended",
+ "High",
+ "High 10",
+ "High 422",
+ "High 444 Predictive",
+ "High 10 Intra",
+ "High 422 Intra",
+ "High 444 Intra",
+ "CAVLC 444 Intra",
+ "Scalable Baseline",
+ "Scalable High",
+ "Scalable High Intra",
+ "Multiview High",
+ NULL,
+ };
+ static const char * const vui_sar_idc[] = {
+ "Unspecified",
+ "1:1",
+ "12:11",
+ "10:11",
+ "16:11",
+ "40:33",
+ "24:11",
+ "20:11",
+ "32:11",
+ "80:33",
+ "18:11",
+ "15:11",
+ "64:33",
+ "160:99",
+ "4:3",
+ "3:2",
+ "2:1",
+ "Extended SAR",
+ NULL,
+ };
+ static const char * const mpeg_mpeg4_level[] = {
+ "0",
+ "0b",
+ "1",
+ "2",
+ "3",
+ "3b",
+ "4",
+ "5",
+ NULL,
+ };
+ static const char * const mpeg4_profile[] = {
+ "Simple",
+ "Advanced Simple",
+ "Core",
+ "Simple Scalable",
+ "Advanced Coding Efficency",
+ NULL,
+ };
+
+ static const char * const flash_led_mode[] = {
+ "Off",
+ "Flash",
+ "Torch",
+ NULL,
+ };
+ static const char * const flash_strobe_source[] = {
+ "Software",
+ "External",
+ NULL,
+ };
+
+ static const char * const jpeg_chroma_subsampling[] = {
+ "4:4:4",
+ "4:2:2",
+ "4:2:0",
+ "4:1:1",
+ "4:1:0",
+ "Gray",
+ NULL,
+ };
+ static const char * const dv_tx_mode[] = {
+ "DVI-D",
+ "HDMI",
+ NULL,
+ };
+ static const char * const dv_rgb_range[] = {
+ "Automatic",
+ "RGB limited range (16-235)",
+ "RGB full range (0-255)",
+ NULL,
+ };
+
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ return mpeg_audio_sampling_freq;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ return mpeg_audio_encoding;
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ return mpeg_audio_l1_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ return mpeg_audio_l2_bitrate;
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return mpeg_audio_l3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ return mpeg_audio_ac3_bitrate;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ return mpeg_audio_mode;
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ return mpeg_audio_mode_extension;
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ return mpeg_audio_emphasis;
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ return mpeg_audio_crc;
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+ return mpeg_audio_dec_playback;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ return mpeg_video_bitrate_mode;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
+ case V4L2_CID_EXPOSURE_METERING:
+ return camera_exposure_metering;
+ case V4L2_CID_AUTO_FOCUS_RANGE:
+ return camera_auto_focus_range;
+ case V4L2_CID_COLORFX:
+ return colorfx;
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+ return auto_n_preset_white_balance;
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ return camera_iso_sensitivity_auto;
+ case V4L2_CID_SCENE_MODE:
+ return scene_mode;
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ return tune_preemphasis;
+ case V4L2_CID_FLASH_LED_MODE:
+ return flash_led_mode;
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ return flash_strobe_source;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ return header_mode;
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ return multi_slice;
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ return entropy_mode;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ return mpeg_h264_level;
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ return h264_loop_filter;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ return h264_profile;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ return vui_sar_idc;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ return mpeg_mpeg4_level;
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ return mpeg4_profile;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ return jpeg_chroma_subsampling;
+ case V4L2_CID_DV_TX_MODE:
+ return dv_tx_mode;
+ case V4L2_CID_DV_TX_RGB_RANGE:
+ case V4L2_CID_DV_RX_RGB_RANGE:
+ return dv_rgb_range;
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_menu);
+
+/* Return the control name. */
+const char *v4l2_ctrl_get_name(u32 id)
+{
+ switch (id) {
+ /* USER controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_USER_CLASS: return "User Controls";
+ case V4L2_CID_BRIGHTNESS: return "Brightness";
+ case V4L2_CID_CONTRAST: return "Contrast";
+ case V4L2_CID_SATURATION: return "Saturation";
+ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
+ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
+ case V4L2_CID_COLORFX: return "Color Effects";
+ case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
+ case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
+ case V4L2_CID_ROTATE: return "Rotate";
+ case V4L2_CID_BG_COLOR: return "Background Color";
+ case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
+ case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1";
+ case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2";
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers";
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers";
+ case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component";
+ case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr";
+
+ /* MPEG controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+ case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
+ case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
+ case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
+ case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
+ case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
+ case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
+ case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
+ case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
+ case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback";
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
+ case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
+ case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
+ case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
+ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
+ case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
+ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
+ case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs";
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode";
+ case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics";
+ case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entropy Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I-Frame Period";
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset";
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: return "H264 Loop Filter Mode";
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE: return "H264 Profile";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: return "Vertical Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: return "Horizontal Size of SAR";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable";
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile";
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "Maximum Bytes in a Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice";
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method";
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
+
+ /* CAMERA controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+ case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
+ case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
+ case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias";
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset";
+ case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range";
+ case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization";
+ case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity";
+ case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto";
+ case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode";
+ case V4L2_CID_SCENE_MODE: return "Scene Mode";
+ case V4L2_CID_3A_LOCK: return "3A Lock";
+ case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start";
+ case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop";
+ case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status";
+ case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range";
+
+ /* FM Radio Modulator control */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
+ case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
+ case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
+ case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
+ case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
+ case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
+ case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled";
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
+ case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
+ case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
+ case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
+ case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-Emphasis";
+ case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
+
+ /* Flash controls */
+ case V4L2_CID_FLASH_CLASS: return "Flash Controls";
+ case V4L2_CID_FLASH_LED_MODE: return "LED Mode";
+ case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source";
+ case V4L2_CID_FLASH_STROBE: return "Strobe";
+ case V4L2_CID_FLASH_STROBE_STOP: return "Stop Strobe";
+ case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe Status";
+ case V4L2_CID_FLASH_TIMEOUT: return "Strobe Timeout";
+ case V4L2_CID_FLASH_INTENSITY: return "Intensity, Flash Mode";
+ case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, Torch Mode";
+ case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator";
+ case V4L2_CID_FLASH_FAULT: return "Faults";
+ case V4L2_CID_FLASH_CHARGE: return "Charge";
+ case V4L2_CID_FLASH_READY: return "Ready to Strobe";
+
+ /* JPEG encoder controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls";
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling";
+ case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval";
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
+ case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
+
+ /* Image source controls */
+ case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls";
+ case V4L2_CID_VBLANK: return "Vertical Blanking";
+ case V4L2_CID_HBLANK: return "Horizontal Blanking";
+ case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain";
+
+ /* Image processing controls */
+ case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls";
+ case V4L2_CID_LINK_FREQ: return "Link Frequency";
+ case V4L2_CID_PIXEL_RATE: return "Pixel Rate";
+
+ /* DV controls */
+ case V4L2_CID_DV_CLASS: return "Digital Video Controls";
+ case V4L2_CID_DV_TX_HOTPLUG: return "Hotplug Present";
+ case V4L2_CID_DV_TX_RXSENSE: return "RxSense Present";
+ case V4L2_CID_DV_TX_EDID_PRESENT: return "EDID Present";
+ case V4L2_CID_DV_TX_MODE: return "Transmit Mode";
+ case V4L2_CID_DV_TX_RGB_RANGE: return "Tx RGB Quantization Range";
+ case V4L2_CID_DV_RX_POWER_PRESENT: return "Power Present";
+ case V4L2_CID_DV_RX_RGB_RANGE: return "Rx RGB Quantization Range";
+
+ default:
+ return NULL;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_get_name);
+
+void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
+ s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
+{
+ *name = v4l2_ctrl_get_name(id);
+ *flags = 0;
+
+ switch (id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
+ case V4L2_CID_CHROMA_AGC:
+ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_AUTOBRIGHTNESS:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
+ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ case V4L2_CID_AUDIO_LIMITER_ENABLED:
+ case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
+ case V4L2_CID_PILOT_TONE_ENABLED:
+ case V4L2_CID_ILLUMINATORS_1:
+ case V4L2_CID_ILLUMINATORS_2:
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_FLASH_CHARGE:
+ case V4L2_CID_FLASH_READY:
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ case V4L2_CID_WIDE_DYNAMIC_RANGE:
+ case V4L2_CID_IMAGE_STABILIZATION:
+ *type = V4L2_CTRL_TYPE_BOOLEAN;
+ *min = 0;
+ *max = *step = 1;
+ break;
+ case V4L2_CID_PAN_RESET:
+ case V4L2_CID_TILT_RESET:
+ case V4L2_CID_FLASH_STROBE:
+ case V4L2_CID_FLASH_STROBE_STOP:
+ case V4L2_CID_AUTO_FOCUS_START:
+ case V4L2_CID_AUTO_FOCUS_STOP:
+ *type = V4L2_CTRL_TYPE_BUTTON;
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+ case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_AUTO_FOCUS_RANGE:
+ case V4L2_CID_COLORFX:
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ case V4L2_CID_FLASH_LED_MODE:
+ case V4L2_CID_FLASH_STROBE_SOURCE:
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+ case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ case V4L2_CID_EXPOSURE_METERING:
+ case V4L2_CID_SCENE_MODE:
+ case V4L2_CID_DV_TX_MODE:
+ case V4L2_CID_DV_TX_RGB_RANGE:
+ case V4L2_CID_DV_RX_RGB_RANGE:
+ *type = V4L2_CTRL_TYPE_MENU;
+ break;
+ case V4L2_CID_LINK_FREQ:
+ *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+ break;
+ case V4L2_CID_RDS_TX_PS_NAME:
+ case V4L2_CID_RDS_TX_RADIO_TEXT:
+ *type = V4L2_CTRL_TYPE_STRING;
+ break;
+ case V4L2_CID_ISO_SENSITIVITY:
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+ break;
+ case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
+ case V4L2_CID_MPEG_CLASS:
+ case V4L2_CID_FM_TX_CLASS:
+ case V4L2_CID_FLASH_CLASS:
+ case V4L2_CID_JPEG_CLASS:
+ case V4L2_CID_IMAGE_SOURCE_CLASS:
+ case V4L2_CID_IMAGE_PROC_CLASS:
+ case V4L2_CID_DV_CLASS:
+ *type = V4L2_CTRL_TYPE_CTRL_CLASS;
+ /* You can neither read not write these */
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ case V4L2_CID_BG_COLOR:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *step = 1;
+ *min = 0;
+ /* Max is calculated as RGB888 that is 2^24 */
+ *max = 0xFFFFFF;
+ break;
+ case V4L2_CID_FLASH_FAULT:
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
+ case V4L2_CID_3A_LOCK:
+ case V4L2_CID_AUTO_FOCUS_STATUS:
+ case V4L2_CID_DV_TX_HOTPLUG:
+ case V4L2_CID_DV_TX_RXSENSE:
+ case V4L2_CID_DV_TX_EDID_PRESENT:
+ case V4L2_CID_DV_RX_POWER_PRESENT:
+ *type = V4L2_CTRL_TYPE_BITMASK;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ *flags |= V4L2_CTRL_FLAG_VOLATILE;
+ /* Fall through */
+ case V4L2_CID_PIXEL_RATE:
+ *type = V4L2_CTRL_TYPE_INTEGER64;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ *min = *max = *step = *def = 0;
+ break;
+ default:
+ *type = V4L2_CTRL_TYPE_INTEGER;
+ break;
+ }
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ *flags |= V4L2_CTRL_FLAG_UPDATE;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_BRIGHTNESS:
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
+ case V4L2_CID_RED_BALANCE:
+ case V4L2_CID_BLUE_BALANCE:
+ case V4L2_CID_GAMMA:
+ case V4L2_CID_SHARPNESS:
+ case V4L2_CID_CHROMA_GAIN:
+ case V4L2_CID_RDS_TX_DEVIATION:
+ case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
+ case V4L2_CID_AUDIO_LIMITER_DEVIATION:
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
+ case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
+ case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
+ case V4L2_CID_PILOT_TONE_DEVIATION:
+ case V4L2_CID_PILOT_TONE_FREQUENCY:
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
+ *flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
+ case V4L2_CID_PAN_RELATIVE:
+ case V4L2_CID_TILT_RELATIVE:
+ case V4L2_CID_FOCUS_RELATIVE:
+ case V4L2_CID_IRIS_RELATIVE:
+ case V4L2_CID_ZOOM_RELATIVE:
+ *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ break;
+ case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_AUTO_FOCUS_STATUS:
+ case V4L2_CID_FLASH_READY:
+ case V4L2_CID_DV_TX_HOTPLUG:
+ case V4L2_CID_DV_TX_RXSENSE:
+ case V4L2_CID_DV_TX_EDID_PRESENT:
+ case V4L2_CID_DV_RX_POWER_PRESENT:
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ break;
+ }
+}
+EXPORT_SYMBOL(v4l2_ctrl_fill);
+
+/* Helper function to determine whether the control type is compatible with
+ VIDIOC_G/S_CTRL. */
+static bool type_is_int(const struct v4l2_ctrl *ctrl)
+{
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ case V4L2_CTRL_TYPE_STRING:
+ /* Nope, these need v4l2_ext_control */
+ return false;
+ default:
+ return true;
+ }
+}
+
+static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ memset(ev->reserved, 0, sizeof(ev->reserved));
+ ev->type = V4L2_EVENT_CTRL;
+ ev->id = ctrl->id;
+ ev->u.ctrl.changes = changes;
+ ev->u.ctrl.type = ctrl->type;
+ ev->u.ctrl.flags = ctrl->flags;
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+ ev->u.ctrl.value64 = 0;
+ else
+ ev->u.ctrl.value64 = ctrl->cur.val64;
+ ev->u.ctrl.minimum = ctrl->minimum;
+ ev->u.ctrl.maximum = ctrl->maximum;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU
+ || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
+ ev->u.ctrl.step = 1;
+ else
+ ev->u.ctrl.step = ctrl->step;
+ ev->u.ctrl.default_value = ctrl->default_value;
+}
+
+static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
+{
+ struct v4l2_event ev;
+ struct v4l2_subscribed_event *sev;
+
+ if (list_empty(&ctrl->ev_subs))
+ return;
+ fill_event(&ev, ctrl, changes);
+
+ list_for_each_entry(sev, &ctrl->ev_subs, node)
+ if (sev->fh != fh ||
+ (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
+ v4l2_event_queue_fh(sev->fh, &ev);
+}
+
+/* Helper function: copy the current control value back to the caller */
+static int cur_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->cur.string);
+ if (c->size < len + 1) {
+ c->size = len + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->cur.string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->cur.val64;
+ break;
+ default:
+ c->value = ctrl->cur.val;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the caller-provider value as the new control value */
+static int user_to_new(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ int ret;
+ u32 size;
+
+ ctrl->is_new = 1;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = c->value64;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ size = c->size;
+ if (size == 0)
+ return -ERANGE;
+ if (size > ctrl->maximum + 1)
+ size = ctrl->maximum + 1;
+ ret = copy_from_user(ctrl->string, c->string, size);
+ if (!ret) {
+ char last = ctrl->string[size - 1];
+
+ ctrl->string[size - 1] = 0;
+ /* If the string was longer than ctrl->maximum,
+ then return an error. */
+ if (strlen(ctrl->string) == ctrl->maximum && last)
+ return -ERANGE;
+ }
+ return ret ? -EFAULT : 0;
+ default:
+ ctrl->val = c->value;
+ break;
+ }
+ return 0;
+}
+
+/* Helper function: copy the new control value back to the caller */
+static int new_to_user(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl)
+{
+ u32 len;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(ctrl->string);
+ if (c->size < len + 1) {
+ c->size = ctrl->maximum + 1;
+ return -ENOSPC;
+ }
+ return copy_to_user(c->string, ctrl->string,
+ len + 1) ? -EFAULT : 0;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ c->value64 = ctrl->val64;
+ break;
+ default:
+ c->value = ctrl->val;
+ break;
+ }
+ return 0;
+}
+
+/* Copy the new value to the current value. */
+static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+ bool update_inactive)
+{
+ bool changed = false;
+
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ changed = true;
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ changed = strcmp(ctrl->string, ctrl->cur.string);
+ strcpy(ctrl->cur.string, ctrl->string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ changed = ctrl->val64 != ctrl->cur.val64;
+ ctrl->cur.val64 = ctrl->val64;
+ break;
+ default:
+ changed = ctrl->val != ctrl->cur.val;
+ ctrl->cur.val = ctrl->val;
+ break;
+ }
+ if (update_inactive) {
+ /* Note: update_inactive can only be true for auto clusters. */
+ ctrl->flags &=
+ ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
+ if (!is_cur_manual(ctrl->cluster[0])) {
+ ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ctrl->cluster[0]->has_volatiles)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ }
+ fh = NULL;
+ }
+ if (changed || update_inactive) {
+ /* If a control was changed that was not one of the controls
+ modified by the application, then send the event to all. */
+ if (!ctrl->is_new)
+ fh = NULL;
+ send_event(fh, ctrl,
+ (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
+ (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
+ }
+}
+
+/* Copy the current value to the new value */
+static void cur_to_new(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl == NULL)
+ return;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ strcpy(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ ctrl->val64 = ctrl->cur.val64;
+ break;
+ default:
+ ctrl->val = ctrl->cur.val;
+ break;
+ }
+}
+
+/* Return non-zero if one or more of the controls in the cluster has a new
+ value that differs from the current value. */
+static int cluster_changed(struct v4l2_ctrl *master)
+{
+ int diff = 0;
+ int i;
+
+ for (i = 0; !diff && i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_BUTTON:
+ /* Button controls are always 'different' */
+ return 1;
+ case V4L2_CTRL_TYPE_STRING:
+ /* strings are always 0-terminated */
+ diff = strcmp(ctrl->string, ctrl->cur.string);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ diff = ctrl->val64 != ctrl->cur.val64;
+ break;
+ default:
+ diff = ctrl->val != ctrl->cur.val;
+ break;
+ }
+ }
+ return diff;
+}
+
+/* Validate a new control */
+static int validate_new(const struct v4l2_ctrl *ctrl,
+ struct v4l2_ext_control *c)
+{
+ size_t len;
+ u32 offset;
+ s32 val;
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ /* Round towards the closest legal value */
+ val = c->value + ctrl->step / 2;
+ val = clamp(val, ctrl->minimum, ctrl->maximum);
+ offset = val - ctrl->minimum;
+ offset = ctrl->step * (offset / ctrl->step);
+ c->value = ctrl->minimum + offset;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ c->value = !!c->value;
+ return 0;
+
+ case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ if (c->value < ctrl->minimum || c->value > ctrl->maximum)
+ return -ERANGE;
+ if (ctrl->menu_skip_mask & (1 << c->value))
+ return -EINVAL;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
+ ctrl->qmenu[c->value][0] == '\0')
+ return -EINVAL;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BITMASK:
+ c->value &= ctrl->maximum;
+ return 0;
+
+ case V4L2_CTRL_TYPE_BUTTON:
+ case V4L2_CTRL_TYPE_CTRL_CLASS:
+ c->value = 0;
+ return 0;
+
+ case V4L2_CTRL_TYPE_INTEGER64:
+ return 0;
+
+ case V4L2_CTRL_TYPE_STRING:
+ len = strlen(c->string);
+ if (len < ctrl->minimum)
+ return -ERANGE;
+ if ((len - ctrl->minimum) % ctrl->step)
+ return -ERANGE;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static inline u32 node2id(struct list_head *node)
+{
+ return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
+}
+
+/* Set the handler's error code if it wasn't set earlier already */
+static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
+{
+ if (hdl->error == 0)
+ hdl->error = err;
+ return err;
+}
+
+/* Initialize the handler */
+int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
+ unsigned nr_of_controls_hint)
+{
+ hdl->lock = &hdl->_lock;
+ mutex_init(hdl->lock);
+ INIT_LIST_HEAD(&hdl->ctrls);
+ INIT_LIST_HEAD(&hdl->ctrl_refs);
+ hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
+ hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
+ GFP_KERNEL);
+ hdl->error = hdl->buckets ? 0 : -ENOMEM;
+ return hdl->error;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_init);
+
+/* Free all controls and control refs */
+void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl_ref *ref, *next_ref;
+ struct v4l2_ctrl *ctrl, *next_ctrl;
+ struct v4l2_subscribed_event *sev, *next_sev;
+
+ if (hdl == NULL || hdl->buckets == NULL)
+ return;
+
+ mutex_lock(hdl->lock);
+ /* Free all nodes */
+ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
+ list_del(&ref->node);
+ kfree(ref);
+ }
+ /* Free all controls owned by the handler */
+ list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
+ list_del(&ctrl->node);
+ list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+ list_del(&sev->node);
+ kfree(ctrl);
+ }
+ kfree(hdl->buckets);
+ hdl->buckets = NULL;
+ hdl->cached = NULL;
+ hdl->error = 0;
+ mutex_unlock(hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_free);
+
+/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
+ be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
+ with applications that do not use the NEXT_CTRL flag.
+
+ We just find the n-th private user control. It's O(N), but that should not
+ be an issue in this particular case. */
+static struct v4l2_ctrl_ref *find_private_ref(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+
+ id -= V4L2_CID_PRIVATE_BASE;
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ /* Search for private user controls that are compatible with
+ VIDIOC_G/S_CTRL. */
+ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
+ V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
+ if (!type_is_int(ref->ctrl))
+ continue;
+ if (id == 0)
+ return ref;
+ id--;
+ }
+ }
+ return NULL;
+}
+
+/* Find a control with the given ID. */
+static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref;
+ int bucket;
+
+ id &= V4L2_CTRL_ID_MASK;
+
+ /* Old-style private controls need special handling */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return find_private_ref(hdl, id);
+ bucket = id % hdl->nr_of_buckets;
+
+ /* Simple optimization: cache the last control found */
+ if (hdl->cached && hdl->cached->ctrl->id == id)
+ return hdl->cached;
+
+ /* Not in cache, search the hash */
+ ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
+ while (ref && ref->ctrl->id != id)
+ ref = ref->next;
+
+ if (ref)
+ hdl->cached = ref; /* cache it! */
+ return ref;
+}
+
+/* Find a control with the given ID. Take the handler's lock first. */
+static struct v4l2_ctrl_ref *find_ref_lock(
+ struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = NULL;
+
+ if (hdl) {
+ mutex_lock(hdl->lock);
+ ref = find_ref(hdl, id);
+ mutex_unlock(hdl->lock);
+ }
+ return ref;
+}
+
+/* Find a control with the given ID. */
+struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
+{
+ struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
+
+ return ref ? ref->ctrl : NULL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_find);
+
+/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
+static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl_ref *new_ref;
+ u32 id = ctrl->id;
+ u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
+ int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
+
+ /* Automatically add the control class if it is not yet present. */
+ if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
+ if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
+ return hdl->error;
+
+ if (hdl->error)
+ return hdl->error;
+
+ new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
+ if (!new_ref)
+ return handler_set_err(hdl, -ENOMEM);
+ new_ref->ctrl = ctrl;
+ if (ctrl->handler == hdl) {
+ /* By default each control starts in a cluster of its own.
+ new_ref->ctrl is basically a cluster array with one
+ element, so that's perfect to use as the cluster pointer.
+ But only do this for the handler that owns the control. */
+ ctrl->cluster = &new_ref->ctrl;
+ ctrl->ncontrols = 1;
+ }
+
+ INIT_LIST_HEAD(&new_ref->node);
+
+ mutex_lock(hdl->lock);
+
+ /* Add immediately at the end of the list if the list is empty, or if
+ the last element in the list has a lower ID.
+ This ensures that when elements are added in ascending order the
+ insertion is an O(1) operation. */
+ if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
+ list_add_tail(&new_ref->node, &hdl->ctrl_refs);
+ goto insert_in_hash;
+ }
+
+ /* Find insert position in sorted list */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node) {
+ if (ref->ctrl->id < id)
+ continue;
+ /* Don't add duplicates */
+ if (ref->ctrl->id == id) {
+ kfree(new_ref);
+ goto unlock;
+ }
+ list_add(&new_ref->node, ref->node.prev);
+ break;
+ }
+
+insert_in_hash:
+ /* Insert the control node in the hash */
+ new_ref->next = hdl->buckets[bucket];
+ hdl->buckets[bucket] = new_ref;
+
+unlock:
+ mutex_unlock(hdl->lock);
+ return 0;
+}
+
+/* Add a new control */
+static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, const char *name, enum v4l2_ctrl_type type,
+ s32 min, s32 max, u32 step, s32 def,
+ u32 flags, const char * const *qmenu,
+ const s64 *qmenu_int, void *priv)
+{
+ struct v4l2_ctrl *ctrl;
+ unsigned sz_extra = 0;
+
+ if (hdl->error)
+ return NULL;
+
+ /* Sanity checks */
+ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
+ (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+ (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
+ (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+ (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) ||
+ (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if ((type == V4L2_CTRL_TYPE_INTEGER ||
+ type == V4L2_CTRL_TYPE_MENU ||
+ type == V4L2_CTRL_TYPE_INTEGER_MENU ||
+ type == V4L2_CTRL_TYPE_BOOLEAN) &&
+ (def < min || def > max)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+ if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+ handler_set_err(hdl, -ERANGE);
+ return NULL;
+ }
+
+ if (type == V4L2_CTRL_TYPE_BUTTON)
+ flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
+ else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ else if (type == V4L2_CTRL_TYPE_STRING)
+ sz_extra += 2 * (max + 1);
+
+ ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -ENOMEM);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&ctrl->node);
+ INIT_LIST_HEAD(&ctrl->ev_subs);
+ ctrl->handler = hdl;
+ ctrl->ops = ops;
+ ctrl->id = id;
+ ctrl->name = name;
+ ctrl->type = type;
+ ctrl->flags = flags;
+ ctrl->minimum = min;
+ ctrl->maximum = max;
+ ctrl->step = step;
+ if (type == V4L2_CTRL_TYPE_MENU)
+ ctrl->qmenu = qmenu;
+ else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
+ ctrl->qmenu_int = qmenu_int;
+ ctrl->priv = priv;
+ ctrl->cur.val = ctrl->val = ctrl->default_value = def;
+
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
+ ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
+ ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
+ if (ctrl->minimum)
+ memset(ctrl->cur.string, ' ', ctrl->minimum);
+ }
+ if (handler_new_ref(hdl, ctrl)) {
+ kfree(ctrl);
+ return NULL;
+ }
+ mutex_lock(hdl->lock);
+ list_add_tail(&ctrl->node, &hdl->ctrls);
+ mutex_unlock(hdl->lock);
+ return ctrl;
+}
+
+struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_config *cfg, void *priv)
+{
+ bool is_menu;
+ struct v4l2_ctrl *ctrl;
+ const char *name = cfg->name;
+ const char * const *qmenu = cfg->qmenu;
+ const s64 *qmenu_int = cfg->qmenu_int;
+ enum v4l2_ctrl_type type = cfg->type;
+ u32 flags = cfg->flags;
+ s32 min = cfg->min;
+ s32 max = cfg->max;
+ u32 step = cfg->step;
+ s32 def = cfg->def;
+
+ if (name == NULL)
+ v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
+ &def, &flags);
+
+ is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU ||
+ cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU);
+ if (is_menu)
+ WARN_ON(step);
+ else
+ WARN_ON(cfg->menu_skip_mask);
+ if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)
+ qmenu = v4l2_ctrl_get_menu(cfg->id);
+ else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU &&
+ qmenu_int == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+
+ ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
+ type, min, max,
+ is_menu ? cfg->menu_skip_mask : step,
+ def, flags, qmenu, qmenu_int, priv);
+ if (ctrl)
+ ctrl->is_private = cfg->is_private;
+ return ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_custom);
+
+/* Helper function for standard non-menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 min, s32 max, u32 step, s32 def)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type == V4L2_CTRL_TYPE_MENU
+ || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ min, max, step, def, flags, NULL, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std);
+
+/* Helper function for standard menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 mask, s32 def)
+{
+ const char * const *qmenu = v4l2_ctrl_get_menu(id);
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, mask, def, flags, qmenu, NULL, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+
+/* Helper function for standard integer menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 def, const s64 *qmenu_int)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, 0, def, flags, NULL, qmenu_int, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
+
+/* Add a control from another handler to this handler */
+struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl *ctrl)
+{
+ if (hdl == NULL || hdl->error)
+ return NULL;
+ if (ctrl == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ if (ctrl->handler == hdl)
+ return ctrl;
+ return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
+
+/* Add the controls from another handler to our own. */
+int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ctrl_handler *add,
+ bool (*filter)(const struct v4l2_ctrl *ctrl))
+{
+ struct v4l2_ctrl_ref *ref;
+ int ret = 0;
+
+ /* Do nothing if either handler is NULL or if they are the same */
+ if (!hdl || !add || hdl == add)
+ return 0;
+ if (hdl->error)
+ return hdl->error;
+ mutex_lock(add->lock);
+ list_for_each_entry(ref, &add->ctrl_refs, node) {
+ struct v4l2_ctrl *ctrl = ref->ctrl;
+
+ /* Skip handler-private controls. */
+ if (ctrl->is_private)
+ continue;
+ /* And control classes */
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ continue;
+ /* Filter any unwanted controls */
+ if (filter && !filter(ctrl))
+ continue;
+ ret = handler_new_ref(hdl, ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(add->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_handler);
+
+bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl)
+{
+ if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_FM_TX)
+ return true;
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+EXPORT_SYMBOL(v4l2_ctrl_radio_filter);
+
+/* Cluster controls */
+void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
+{
+ bool has_volatiles = false;
+ int i;
+
+ /* The first control is the master control and it must not be NULL */
+ BUG_ON(ncontrols == 0 || controls[0] == NULL);
+
+ for (i = 0; i < ncontrols; i++) {
+ if (controls[i]) {
+ controls[i]->cluster = controls;
+ controls[i]->ncontrols = ncontrols;
+ if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
+ has_volatiles = true;
+ }
+ }
+ controls[0]->has_volatiles = has_volatiles;
+}
+EXPORT_SYMBOL(v4l2_ctrl_cluster);
+
+void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
+ u8 manual_val, bool set_volatile)
+{
+ struct v4l2_ctrl *master = controls[0];
+ u32 flag = 0;
+ int i;
+
+ v4l2_ctrl_cluster(ncontrols, controls);
+ WARN_ON(ncontrols <= 1);
+ WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
+ WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
+ master->is_auto = true;
+ master->has_volatiles = set_volatile;
+ master->manual_mode_value = manual_val;
+ master->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ if (!is_cur_manual(master))
+ flag = V4L2_CTRL_FLAG_INACTIVE |
+ (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
+
+ for (i = 1; i < ncontrols; i++)
+ if (controls[i])
+ controls[i]->flags |= flag;
+}
+EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
+
+/* Activate/deactivate a control. */
+void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+{
+ /* invert since the actual flag is called 'inactive' */
+ bool inactive = !active;
+ bool old;
+
+ if (ctrl == NULL)
+ return;
+
+ if (inactive)
+ /* set V4L2_CTRL_FLAG_INACTIVE */
+ old = test_and_set_bit(4, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_INACTIVE */
+ old = test_and_clear_bit(4, &ctrl->flags);
+ if (old != inactive)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+}
+EXPORT_SYMBOL(v4l2_ctrl_activate);
+
+/* Grab/ungrab a control.
+ Typically used when streaming starts and you want to grab controls,
+ preventing the user from changing them.
+
+ Just call this and the framework will block any attempts to change
+ these controls. */
+void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+{
+ bool old;
+
+ if (ctrl == NULL)
+ return;
+
+ v4l2_ctrl_lock(ctrl);
+ if (grabbed)
+ /* set V4L2_CTRL_FLAG_GRABBED */
+ old = test_and_set_bit(1, &ctrl->flags);
+ else
+ /* clear V4L2_CTRL_FLAG_GRABBED */
+ old = test_and_clear_bit(1, &ctrl->flags);
+ if (old != grabbed)
+ send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
+ v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_grab);
+
+/* Log the control name and value */
+static void log_ctrl(const struct v4l2_ctrl *ctrl,
+ const char *prefix, const char *colon)
+{
+ if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
+ return;
+ if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
+ return;
+
+ printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
+
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ printk(KERN_CONT "%d", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
+ break;
+ case V4L2_CTRL_TYPE_BITMASK:
+ printk(KERN_CONT "0x%08x", ctrl->cur.val);
+ break;
+ case V4L2_CTRL_TYPE_INTEGER64:
+ printk(KERN_CONT "%lld", ctrl->cur.val64);
+ break;
+ case V4L2_CTRL_TYPE_STRING:
+ printk(KERN_CONT "%s", ctrl->cur.string);
+ break;
+ default:
+ printk(KERN_CONT "unknown type %d", ctrl->type);
+ break;
+ }
+ if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
+ V4L2_CTRL_FLAG_GRABBED |
+ V4L2_CTRL_FLAG_VOLATILE)) {
+ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
+ printk(KERN_CONT " inactive");
+ if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
+ printk(KERN_CONT " grabbed");
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
+ printk(KERN_CONT " volatile");
+ }
+ printk(KERN_CONT "\n");
+}
+
+/* Log all controls owned by the handler */
+void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
+ const char *prefix)
+{
+ struct v4l2_ctrl *ctrl;
+ const char *colon = "";
+ int len;
+
+ if (hdl == NULL)
+ return;
+ if (prefix == NULL)
+ prefix = "";
+ len = strlen(prefix);
+ if (len && prefix[len - 1] != ' ')
+ colon = ": ";
+ mutex_lock(hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
+ log_ctrl(ctrl, prefix, colon);
+ mutex_unlock(hdl->lock);
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
+
+/* Call s_ctrl for all controls owned by the handler */
+int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret = 0;
+
+ if (hdl == NULL)
+ return 0;
+ mutex_lock(hdl->lock);
+ list_for_each_entry(ctrl, &hdl->ctrls, node)
+ ctrl->done = false;
+
+ list_for_each_entry(ctrl, &hdl->ctrls, node) {
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int i;
+
+ /* Skip if this control was already handled by a cluster. */
+ /* Skip button controls and read-only controls. */
+ if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
+ (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
+ continue;
+
+ for (i = 0; i < master->ncontrols; i++) {
+ if (master->cluster[i]) {
+ cur_to_new(master->cluster[i]);
+ master->cluster[i]->is_new = 1;
+ master->cluster[i]->done = true;
+ }
+ }
+ ret = call_op(master, s_ctrl);
+ if (ret)
+ break;
+ }
+ mutex_unlock(hdl->lock);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
+
+/* Implement VIDIOC_QUERYCTRL */
+int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
+{
+ u32 id = qc->id & V4L2_CTRL_ID_MASK;
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ mutex_lock(hdl->lock);
+
+ /* Try to find it */
+ ref = find_ref(hdl, id);
+
+ if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
+ /* Find the next control with ID > qc->id */
+
+ /* Did we reach the end of the control list? */
+ if (id >= node2id(hdl->ctrl_refs.prev)) {
+ ref = NULL; /* Yes, so there is no next control */
+ } else if (ref) {
+ /* We found a control with the given ID, so just get
+ the next one in the list. */
+ ref = list_entry(ref->node.next, typeof(*ref), node);
+ } else {
+ /* No control with the given ID exists, so start
+ searching for the next largest ID. We know there
+ is one, otherwise the first 'if' above would have
+ been true. */
+ list_for_each_entry(ref, &hdl->ctrl_refs, node)
+ if (id < ref->ctrl->id)
+ break;
+ }
+ }
+ mutex_unlock(hdl->lock);
+ if (!ref)
+ return -EINVAL;
+
+ ctrl = ref->ctrl;
+ memset(qc, 0, sizeof(*qc));
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ qc->id = id;
+ else
+ qc->id = ctrl->id;
+ strlcpy(qc->name, ctrl->name, sizeof(qc->name));
+ qc->minimum = ctrl->minimum;
+ qc->maximum = ctrl->maximum;
+ qc->default_value = ctrl->default_value;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU
+ || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
+ qc->step = 1;
+ else
+ qc->step = ctrl->step;
+ qc->flags = ctrl->flags;
+ qc->type = ctrl->type;
+ return 0;
+}
+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);
+
+/* Implement VIDIOC_QUERYMENU */
+int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
+{
+ struct v4l2_ctrl *ctrl;
+ u32 i = qm->index;
+
+ ctrl = v4l2_ctrl_find(hdl, qm->id);
+ if (!ctrl)
+ return -EINVAL;
+
+ qm->reserved = 0;
+ /* Sanity checks */
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_MENU:
+ if (ctrl->qmenu == NULL)
+ return -EINVAL;
+ break;
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ if (ctrl->qmenu_int == NULL)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+
+ /* Use mask to see if this menu item should be skipped */
+ if (ctrl->menu_skip_mask & (1 << i))
+ return -EINVAL;
+ /* Empty menu items should also be skipped */
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ } else {
+ qm->value = ctrl->qmenu_int[i];
+ }
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_querymenu);
+
+int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
+{
+ return v4l2_querymenu(sd->ctrl_handler, qm);
+}
+EXPORT_SYMBOL(v4l2_subdev_querymenu);
+
+
+
+/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
+
+ It is not a fully atomic operation, just best-effort only. After all, if
+ multiple controls have to be set through multiple i2c writes (for example)
+ then some initial writes may succeed while others fail. Thus leaving the
+ system in an inconsistent state. The question is how much effort you are
+ willing to spend on trying to make something atomic that really isn't.
+
+ From the point of view of an application the main requirement is that
+ when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
+ error should be returned without actually affecting any controls.
+
+ If all the values are correct, then it is acceptable to just give up
+ in case of low-level errors.
+
+ It is important though that the application can tell when only a partial
+ configuration was done. The way we do that is through the error_idx field
+ of struct v4l2_ext_controls: if that is equal to the count field then no
+ controls were affected. Otherwise all controls before that index were
+ successful in performing their 'get' or 'set' operation, the control at
+ the given index failed, and you don't know what happened with the controls
+ after the failed one. Since if they were part of a control cluster they
+ could have been successfully processed (if a cluster member was encountered
+ at index < error_idx), they could have failed (if a cluster member was at
+ error_idx), or they may not have been processed yet (if the first cluster
+ member appeared after error_idx).
+
+ It is all fairly theoretical, though. In practice all you can do is to
+ bail out. If error_idx == count, then it is an application bug. If
+ error_idx < count then it is only an application bug if the error code was
+ EBUSY. That usually means that something started streaming just when you
+ tried to set the controls. In all other cases it is a driver/hardware
+ problem and all you can do is to retry or bail out.
+
+ Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
+ never modifies controls the error_idx is just set to whatever control
+ has an invalid value.
+ */
+
+/* Prepare for the extended g/s/try functions.
+ Find the controls in the control array and do some basic checks. */
+static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers)
+{
+ struct v4l2_ctrl_helper *h;
+ bool have_clusters = false;
+ u32 i;
+
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
+ struct v4l2_ext_control *c = &cs->controls[i];
+ struct v4l2_ctrl_ref *ref;
+ struct v4l2_ctrl *ctrl;
+ u32 id = c->id & V4L2_CTRL_ID_MASK;
+
+ cs->error_idx = i;
+
+ if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
+ return -EINVAL;
+
+ /* Old-style private controls are not allowed for
+ extended controls */
+ if (id >= V4L2_CID_PRIVATE_BASE)
+ return -EINVAL;
+ ref = find_ref_lock(hdl, id);
+ if (ref == NULL)
+ return -EINVAL;
+ ctrl = ref->ctrl;
+ if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
+ return -EINVAL;
+
+ if (ctrl->cluster[0]->ncontrols > 1)
+ have_clusters = true;
+ if (ctrl->cluster[0] != ctrl)
+ ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
+ /* Store the ref to the master control of the cluster */
+ h->mref = ref;
+ h->ctrl = ctrl;
+ /* Initially set next to 0, meaning that there is no other
+ control in this helper array belonging to the same
+ cluster */
+ h->next = 0;
+ }
+
+ /* We are done if there were no controls that belong to a multi-
+ control cluster. */
+ if (!have_clusters)
+ return 0;
+
+ /* The code below figures out in O(n) time which controls in the list
+ belong to the same cluster. */
+
+ /* This has to be done with the handler lock taken. */
+ mutex_lock(hdl->lock);
+
+ /* First zero the helper field in the master control references */
+ for (i = 0; i < cs->count; i++)
+ helpers[i].mref->helper = NULL;
+ for (i = 0, h = helpers; i < cs->count; i++, h++) {
+ struct v4l2_ctrl_ref *mref = h->mref;
+
+ /* If the mref->helper is set, then it points to an earlier
+ helper that belongs to the same cluster. */
+ if (mref->helper) {
+ /* Set the next field of mref->helper to the current
+ index: this means that that earlier helper now
+ points to the next helper in the same cluster. */
+ mref->helper->next = i;
+ /* mref should be set only for the first helper in the
+ cluster, clear the others. */
+ h->mref = NULL;
+ }
+ /* Point the mref helper to the current helper struct. */
+ mref->helper = h;
+ }
+ mutex_unlock(hdl->lock);
+ return 0;
+}
+
+/* Handles the corner case where cs->count == 0. It checks whether the
+ specified control class exists. If that class ID is 0, then it checks
+ whether there are any controls at all. */
+static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
+{
+ if (ctrl_class == 0)
+ return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
+ return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
+}
+
+
+
+/* Get extended controls. Allocates the helpers array if needed. */
+int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
+ int ret;
+ int i, j;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
+ if (helpers == NULL)
+ return -ENOMEM;
+ }
+
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ cs->error_idx = cs->count;
+
+ for (i = 0; !ret && i < cs->count; i++)
+ if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ ret = -EACCES;
+
+ for (i = 0; !ret && i < cs->count; i++) {
+ int (*ctrl_to_user)(struct v4l2_ext_control *c,
+ struct v4l2_ctrl *ctrl) = cur_to_user;
+ struct v4l2_ctrl *master;
+
+ if (helpers[i].mref == NULL)
+ continue;
+
+ master = helpers[i].mref->ctrl;
+ cs->error_idx = i;
+
+ v4l2_ctrl_lock(master);
+
+ /* g_volatile_ctrl will update the new control values */
+ if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
+ (master->has_volatiles && !is_cur_manual(master))) {
+ for (j = 0; j < master->ncontrols; j++)
+ cur_to_new(master->cluster[j]);
+ ret = call_op(master, g_volatile_ctrl);
+ ctrl_to_user = new_to_user;
+ }
+ /* If OK, then copy the current (for non-volatile controls)
+ or the new (for volatile controls) control values to the
+ caller */
+ if (!ret) {
+ u32 idx = i;
+
+ do {
+ ret = ctrl_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
+ v4l2_ctrl_unlock(master);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ext_ctrls);
+
+int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
+
+/* Helper function to get a single control */
+static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret = 0;
+ int i;
+
+ /* String controls are not supported. The new_to_user() and
+ * cur_to_user() calls below would need to be modified not to access
+ * userspace memory when called from get_ctrl().
+ */
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+ return -EINVAL;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
+ return -EACCES;
+
+ v4l2_ctrl_lock(master);
+ /* g_volatile_ctrl will update the current control values */
+ if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ ret = call_op(master, g_volatile_ctrl);
+ new_to_user(c, ctrl);
+ } else {
+ cur_to_user(c, ctrl);
+ }
+ v4l2_ctrl_unlock(master);
+ return ret;
+}
+
+int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+ struct v4l2_ext_control c;
+ int ret;
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+ ret = get_ctrl(ctrl, &c);
+ control->value = c.value;
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_g_ctrl);
+
+int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_g_ctrl(sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
+
+s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ext_control c;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ c.value = 0;
+ get_ctrl(ctrl, &c);
+ return c.value;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
+
+s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_ext_control c;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
+ c.value = 0;
+ get_ctrl(ctrl, &c);
+ return c.value;
+}
+EXPORT_SYMBOL(v4l2_ctrl_g_ctrl_int64);
+
+
+/* Core function that calls try/s_ctrl and ensures that the new value is
+ copied to the current value on a set.
+ Must be called with ctrl->handler->lock held. */
+static int try_or_set_cluster(struct v4l2_fh *fh,
+ struct v4l2_ctrl *master, bool set)
+{
+ bool update_flag;
+ int ret;
+ int i;
+
+ /* Go through the cluster and either validate the new value or
+ (if no new value was set), copy the current value to the new
+ value, ensuring a consistent view for the control ops when
+ called. */
+ for (i = 0; i < master->ncontrols; i++) {
+ struct v4l2_ctrl *ctrl = master->cluster[i];
+
+ if (ctrl == NULL)
+ continue;
+
+ if (!ctrl->is_new) {
+ cur_to_new(ctrl);
+ continue;
+ }
+ /* Check again: it may have changed since the
+ previous check in try_or_set_ext_ctrls(). */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ }
+
+ ret = call_op(master, try_ctrl);
+
+ /* Don't set if there is no change */
+ if (ret || !set || !cluster_changed(master))
+ return ret;
+ ret = call_op(master, s_ctrl);
+ if (ret)
+ return ret;
+
+ /* If OK, then make the new values permanent. */
+ update_flag = is_cur_manual(master) != is_new_manual(master);
+ for (i = 0; i < master->ncontrols; i++)
+ new_to_cur(fh, master->cluster[i], update_flag && i > 0);
+ return 0;
+}
+
+/* Validate controls. */
+static int validate_ctrls(struct v4l2_ext_controls *cs,
+ struct v4l2_ctrl_helper *helpers, bool set)
+{
+ unsigned i;
+ int ret = 0;
+
+ cs->error_idx = cs->count;
+ for (i = 0; i < cs->count; i++) {
+ struct v4l2_ctrl *ctrl = helpers[i].ctrl;
+
+ cs->error_idx = i;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+ /* This test is also done in try_set_control_cluster() which
+ is called in atomic context, so that has the final say,
+ but it makes sense to do an up-front check as well. Once
+ an error occurs in try_set_control_cluster() some other
+ controls may have been set already and we want to do a
+ best-effort to avoid that. */
+ if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
+ return -EBUSY;
+ ret = validate_new(ctrl, &cs->controls[i]);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* Obtain the current volatile values of an autocluster and mark them
+ as new. */
+static void update_from_auto_cluster(struct v4l2_ctrl *master)
+{
+ int i;
+
+ for (i = 0; i < master->ncontrols; i++)
+ cur_to_new(master->cluster[i]);
+ if (!call_op(master, g_volatile_ctrl))
+ for (i = 1; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 1;
+}
+
+/* Try or try-and-set controls */
+static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs,
+ bool set)
+{
+ struct v4l2_ctrl_helper helper[4];
+ struct v4l2_ctrl_helper *helpers = helper;
+ unsigned i, j;
+ int ret;
+
+ cs->error_idx = cs->count;
+ cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
+
+ if (hdl == NULL)
+ return -EINVAL;
+
+ if (cs->count == 0)
+ return class_check(hdl, cs->ctrl_class);
+
+ if (cs->count > ARRAY_SIZE(helper)) {
+ helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
+ if (!helpers)
+ return -ENOMEM;
+ }
+ ret = prepare_ext_ctrls(hdl, cs, helpers);
+ if (!ret)
+ ret = validate_ctrls(cs, helpers, set);
+ if (ret && set)
+ cs->error_idx = cs->count;
+ for (i = 0; !ret && i < cs->count; i++) {
+ struct v4l2_ctrl *master;
+ u32 idx = i;
+
+ if (helpers[i].mref == NULL)
+ continue;
+
+ cs->error_idx = i;
+ master = helpers[i].mref->ctrl;
+ v4l2_ctrl_lock(master);
+
+ /* Reset the 'is_new' flags of the cluster */
+ for (j = 0; j < master->ncontrols; j++)
+ if (master->cluster[j])
+ master->cluster[j]->is_new = 0;
+
+ /* For volatile autoclusters that are currently in auto mode
+ we need to discover if it will be set to manual mode.
+ If so, then we have to copy the current volatile values
+ first since those will become the new manual values (which
+ may be overwritten by explicit new values from this set
+ of controls). */
+ if (master->is_auto && master->has_volatiles &&
+ !is_cur_manual(master)) {
+ /* Pick an initial non-manual value */
+ s32 new_auto_val = master->manual_mode_value + 1;
+ u32 tmp_idx = idx;
+
+ do {
+ /* Check if the auto control is part of the
+ list, and remember the new value. */
+ if (helpers[tmp_idx].ctrl == master)
+ new_auto_val = cs->controls[tmp_idx].value;
+ tmp_idx = helpers[tmp_idx].next;
+ } while (tmp_idx);
+ /* If the new value == the manual value, then copy
+ the current volatile values. */
+ if (new_auto_val == master->manual_mode_value)
+ update_from_auto_cluster(master);
+ }
+
+ /* Copy the new caller-supplied control values.
+ user_to_new() sets 'is_new' to 1. */
+ do {
+ ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+
+ if (!ret)
+ ret = try_or_set_cluster(fh, master, set);
+
+ /* Copy the new values back to userspace. */
+ if (!ret) {
+ idx = i;
+ do {
+ ret = new_to_user(cs->controls + idx,
+ helpers[idx].ctrl);
+ idx = helpers[idx].next;
+ } while (!ret && idx);
+ }
+ v4l2_ctrl_unlock(master);
+ }
+
+ if (cs->count > ARRAY_SIZE(helper))
+ kfree(helpers);
+ return ret;
+}
+
+int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, hdl, cs, false);
+}
+EXPORT_SYMBOL(v4l2_try_ext_ctrls);
+
+int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(fh, hdl, cs, true);
+}
+EXPORT_SYMBOL(v4l2_s_ext_ctrls);
+
+int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
+}
+EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
+
+int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
+{
+ return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
+
+/* Helper function for VIDIOC_S_CTRL compatibility */
+static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
+ struct v4l2_ext_control *c)
+{
+ struct v4l2_ctrl *master = ctrl->cluster[0];
+ int ret;
+ int i;
+
+ /* String controls are not supported. The user_to_new() and
+ * cur_to_user() calls below would need to be modified not to access
+ * userspace memory when called from set_ctrl().
+ */
+ if (ctrl->type == V4L2_CTRL_TYPE_STRING)
+ return -EINVAL;
+
+ ret = validate_new(ctrl, c);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_lock(ctrl);
+
+ /* Reset the 'is_new' flags of the cluster */
+ for (i = 0; i < master->ncontrols; i++)
+ if (master->cluster[i])
+ master->cluster[i]->is_new = 0;
+
+ /* For autoclusters with volatiles that are switched from auto to
+ manual mode we have to update the current volatile values since
+ those will become the initial manual values after such a switch. */
+ if (master->is_auto && master->has_volatiles && ctrl == master &&
+ !is_cur_manual(master) && c->value == master->manual_mode_value)
+ update_from_auto_cluster(master);
+
+ user_to_new(c, ctrl);
+ ret = try_or_set_cluster(fh, master, true);
+ cur_to_user(c, ctrl);
+
+ v4l2_ctrl_unlock(ctrl);
+ return ret;
+}
+
+int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
+ struct v4l2_control *control)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
+ struct v4l2_ext_control c;
+ int ret;
+
+ if (ctrl == NULL || !type_is_int(ctrl))
+ return -EINVAL;
+
+ if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
+ return -EACCES;
+
+ c.value = control->value;
+ ret = set_ctrl(fh, ctrl, &c);
+ control->value = c.value;
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_s_ctrl);
+
+int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
+{
+ return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
+}
+EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
+
+int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
+{
+ struct v4l2_ext_control c;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(!type_is_int(ctrl));
+ c.value = val;
+ return set_ctrl(NULL, ctrl, &c);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val)
+{
+ struct v4l2_ext_control c;
+
+ /* It's a driver bug if this happens. */
+ WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64);
+ c.value64 = val;
+ return set_ctrl(NULL, ctrl, &c);
+}
+EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64);
+
+static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ v4l2_ctrl_lock(ctrl);
+ list_add_tail(&sev->node, &ctrl->ev_subs);
+ if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
+ (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+ struct v4l2_event ev;
+ u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+
+ if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
+ changes |= V4L2_EVENT_CTRL_CH_VALUE;
+ fill_event(&ev, ctrl, changes);
+ /* Mark the queue as active, allowing this initial
+ event to be accepted. */
+ sev->elems = elems;
+ v4l2_event_queue_fh(sev->fh, &ev);
+ }
+ v4l2_ctrl_unlock(ctrl);
+ return 0;
+}
+
+static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
+ v4l2_ctrl_lock(ctrl);
+ list_del(&sev->node);
+ v4l2_ctrl_unlock(ctrl);
+}
+
+void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
+{
+ u32 old_changes = old->u.ctrl.changes;
+
+ old->u.ctrl = new->u.ctrl;
+ old->u.ctrl.changes |= old_changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_replace);
+
+void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
+{
+ new->u.ctrl.changes |= old->u.ctrl.changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_merge);
+
+const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = {
+ .add = v4l2_ctrl_add_event,
+ .del = v4l2_ctrl_del_event,
+ .replace = v4l2_ctrl_replace,
+ .merge = v4l2_ctrl_merge,
+};
+EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops);
+
+int v4l2_ctrl_log_status(struct file *file, void *fh)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
+ v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
+ vfd->v4l2_dev->name);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_log_status);
+
+int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ if (sub->type == V4L2_EVENT_CTRL)
+ return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
+
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ return POLLPRI;
+ poll_wait(file, &fh->wait, wait);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
new file mode 100644
index 000000000000..a2df842e5100
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -0,0 +1,1029 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ * A generic video device interface for the LINUX operating system
+ * using a set of device structures/vectors for low level operations.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ *
+ * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
+ * - Added procfs support
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#define VIDEO_NUM_DEVICES 256
+#define VIDEO_NAME "video4linux"
+
+/*
+ * sysfs stuff
+ */
+
+static ssize_t show_index(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%i\n", vdev->index);
+}
+
+static ssize_t show_debug(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%i\n", vdev->debug);
+}
+
+static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct video_device *vdev = to_video_device(cd);
+ int res = 0;
+ u16 value;
+
+ res = kstrtou16(buf, 0, &value);
+ if (res)
+ return res;
+
+ vdev->debug = value;
+ return len;
+}
+
+static ssize_t show_name(struct device *cd,
+ struct device_attribute *attr, char *buf)
+{
+ struct video_device *vdev = to_video_device(cd);
+
+ return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
+}
+
+static struct device_attribute video_device_attrs[] = {
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(debug, 0644, show_debug, set_debug),
+ __ATTR(index, S_IRUGO, show_index, NULL),
+ __ATTR_NULL
+};
+
+/*
+ * Active devices
+ */
+static struct video_device *video_device[VIDEO_NUM_DEVICES];
+static DEFINE_MUTEX(videodev_lock);
+static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
+
+/* Device node utility functions */
+
+/* Note: these utility functions all assume that vfl_type is in the range
+ [0, VFL_TYPE_MAX-1]. */
+
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+ /* Any types not assigned to fixed minor ranges must be mapped to
+ one single bitmap for the purposes of finding a free node number
+ since all those unassigned types use the same minor range. */
+ int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
+
+ return devnode_nums[idx];
+}
+#else
+/* Return the bitmap corresponding to vfl_type. */
+static inline unsigned long *devnode_bits(int vfl_type)
+{
+ return devnode_nums[vfl_type];
+}
+#endif
+
+/* Mark device node number vdev->num as used */
+static inline void devnode_set(struct video_device *vdev)
+{
+ set_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Mark device node number vdev->num as unused */
+static inline void devnode_clear(struct video_device *vdev)
+{
+ clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
+}
+
+/* Try to find a free device node number in the range [from, to> */
+static inline int devnode_find(struct video_device *vdev, int from, int to)
+{
+ return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
+}
+
+struct video_device *video_device_alloc(void)
+{
+ return kzalloc(sizeof(struct video_device), GFP_KERNEL);
+}
+EXPORT_SYMBOL(video_device_alloc);
+
+void video_device_release(struct video_device *vdev)
+{
+ kfree(vdev);
+}
+EXPORT_SYMBOL(video_device_release);
+
+void video_device_release_empty(struct video_device *vdev)
+{
+ /* Do nothing */
+ /* Only valid when the video_device struct is a static. */
+}
+EXPORT_SYMBOL(video_device_release_empty);
+
+static inline void video_get(struct video_device *vdev)
+{
+ get_device(&vdev->dev);
+}
+
+static inline void video_put(struct video_device *vdev)
+{
+ put_device(&vdev->dev);
+}
+
+/* Called when the last user of the video device exits. */
+static void v4l2_device_release(struct device *cd)
+{
+ struct video_device *vdev = to_video_device(cd);
+ struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
+
+ mutex_lock(&videodev_lock);
+ if (WARN_ON(video_device[vdev->minor] != vdev)) {
+ /* should not happen */
+ mutex_unlock(&videodev_lock);
+ return;
+ }
+
+ /* Free up this device for reuse */
+ video_device[vdev->minor] = NULL;
+
+ /* Delete the cdev on this minor as well */
+ cdev_del(vdev->cdev);
+ /* Just in case some driver tries to access this from
+ the release() callback. */
+ vdev->cdev = NULL;
+
+ /* Mark device node number as free */
+ devnode_clear(vdev);
+
+ mutex_unlock(&videodev_lock);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (v4l2_dev && v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV)
+ media_device_unregister_entity(&vdev->entity);
+#endif
+
+ /* Do not call v4l2_device_put if there is no release callback set.
+ * Drivers that have no v4l2_device release callback might free the
+ * v4l2_dev instance in the video_device release callback below, so we
+ * must perform this check here.
+ *
+ * TODO: In the long run all drivers that use v4l2_device should use the
+ * v4l2_device release callback. This check will then be unnecessary.
+ */
+ if (v4l2_dev && v4l2_dev->release == NULL)
+ v4l2_dev = NULL;
+
+ /* Release video_device and perform other
+ cleanups as needed. */
+ vdev->release(vdev);
+
+ /* Decrease v4l2_device refcount */
+ if (v4l2_dev)
+ v4l2_device_put(v4l2_dev);
+}
+
+static struct class video_class = {
+ .name = VIDEO_NAME,
+ .dev_attrs = video_device_attrs,
+};
+
+struct video_device *video_devdata(struct file *file)
+{
+ return video_device[iminor(file->f_path.dentry->d_inode)];
+}
+EXPORT_SYMBOL(video_devdata);
+
+
+/* Priority handling */
+
+static inline bool prio_is_valid(enum v4l2_priority prio)
+{
+ return prio == V4L2_PRIORITY_BACKGROUND ||
+ prio == V4L2_PRIORITY_INTERACTIVE ||
+ prio == V4L2_PRIORITY_RECORD;
+}
+
+void v4l2_prio_init(struct v4l2_prio_state *global)
+{
+ memset(global, 0, sizeof(*global));
+}
+EXPORT_SYMBOL(v4l2_prio_init);
+
+int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
+ enum v4l2_priority new)
+{
+ if (!prio_is_valid(new))
+ return -EINVAL;
+ if (*local == new)
+ return 0;
+
+ atomic_inc(&global->prios[new]);
+ if (prio_is_valid(*local))
+ atomic_dec(&global->prios[*local]);
+ *local = new;
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_prio_change);
+
+void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
+{
+ v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT);
+}
+EXPORT_SYMBOL(v4l2_prio_open);
+
+void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+ if (prio_is_valid(local))
+ atomic_dec(&global->prios[local]);
+}
+EXPORT_SYMBOL(v4l2_prio_close);
+
+enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
+{
+ if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0)
+ return V4L2_PRIORITY_RECORD;
+ if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0)
+ return V4L2_PRIORITY_INTERACTIVE;
+ if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0)
+ return V4L2_PRIORITY_BACKGROUND;
+ return V4L2_PRIORITY_UNSET;
+}
+EXPORT_SYMBOL(v4l2_prio_max);
+
+int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
+{
+ return (local < v4l2_prio_max(global)) ? -EBUSY : 0;
+}
+EXPORT_SYMBOL(v4l2_prio_check);
+
+
+static ssize_t v4l2_read(struct file *filp, char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->read)
+ return -EINVAL;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->read(filp, buf, sz, off);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: read: %zd (%d)\n",
+ video_device_node_name(vdev), sz, ret);
+ return ret;
+}
+
+static ssize_t v4l2_write(struct file *filp, const char __user *buf,
+ size_t sz, loff_t *off)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->write)
+ return -EINVAL;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->write(filp, buf, sz, off);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: write: %zd (%d)\n",
+ video_device_node_name(vdev), sz, ret);
+ return ret;
+}
+
+static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
+{
+ struct video_device *vdev = video_devdata(filp);
+ unsigned int res = POLLERR | POLLHUP;
+
+ if (!vdev->fops->poll)
+ return DEFAULT_POLLMASK;
+ if (video_is_registered(vdev))
+ res = vdev->fops->poll(filp, poll);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: poll: %08x\n",
+ video_device_node_name(vdev), res);
+ return res;
+}
+
+static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (vdev->fops->unlocked_ioctl) {
+ struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
+
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ if (lock)
+ mutex_unlock(lock);
+ } else if (vdev->fops->ioctl) {
+ /* This code path is a replacement for the BKL. It is a major
+ * hack but it will have to do for those drivers that are not
+ * yet converted to use unlocked_ioctl.
+ *
+ * There are two options: if the driver implements struct
+ * v4l2_device, then the lock defined there is used to
+ * serialize the ioctls. Otherwise the v4l2 core lock defined
+ * below is used. This lock is really bad since it serializes
+ * completely independent devices.
+ *
+ * Both variants suffer from the same problem: if the driver
+ * sleeps, then it blocks all ioctls since the lock is still
+ * held. This is very common for VIDIOC_DQBUF since that
+ * normally waits for a frame to arrive. As a result any other
+ * ioctl calls will proceed very, very slowly since each call
+ * will have to wait for the VIDIOC_QBUF to finish. Things that
+ * should take 0.01s may now take 10-20 seconds.
+ *
+ * The workaround is to *not* take the lock for VIDIOC_DQBUF.
+ * This actually works OK for videobuf-based drivers, since
+ * videobuf will take its own internal lock.
+ */
+ static DEFINE_MUTEX(v4l2_ioctl_mutex);
+ struct mutex *m = vdev->v4l2_dev ?
+ &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
+
+ if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
+ return -ERESTARTSYS;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->ioctl(filp, cmd, arg);
+ if (cmd != VIDIOC_DQBUF)
+ mutex_unlock(m);
+ } else
+ ret = -ENOTTY;
+
+ return ret;
+}
+
+#ifdef CONFIG_MMU
+#define v4l2_get_unmapped_area NULL
+#else
+static unsigned long v4l2_get_unmapped_area(struct file *filp,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret;
+
+ if (!vdev->fops->get_unmapped_area)
+ return -ENOSYS;
+ if (!video_is_registered(vdev))
+ return -ENODEV;
+ ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
+ video_device_node_name(vdev), ret);
+ return ret;
+}
+#endif
+
+static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = -ENODEV;
+
+ if (!vdev->fops->mmap)
+ return -ENODEV;
+ if (video_is_registered(vdev))
+ ret = vdev->fops->mmap(filp, vm);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: mmap (%d)\n",
+ video_device_node_name(vdev), ret);
+ return ret;
+}
+
+/* Override for the open function */
+static int v4l2_open(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev;
+ int ret = 0;
+
+ /* Check if the video device is available */
+ mutex_lock(&videodev_lock);
+ vdev = video_devdata(filp);
+ /* return ENODEV if the video device has already been removed. */
+ if (vdev == NULL || !video_is_registered(vdev)) {
+ mutex_unlock(&videodev_lock);
+ return -ENODEV;
+ }
+ /* and increase the device refcount */
+ video_get(vdev);
+ mutex_unlock(&videodev_lock);
+ if (vdev->fops->open) {
+ if (video_is_registered(vdev))
+ ret = vdev->fops->open(filp);
+ else
+ ret = -ENODEV;
+ }
+
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: open (%d)\n",
+ video_device_node_name(vdev), ret);
+ /* decrease the refcount in case of an error */
+ if (ret)
+ video_put(vdev);
+ return ret;
+}
+
+/* Override for the release function */
+static int v4l2_release(struct inode *inode, struct file *filp)
+{
+ struct video_device *vdev = video_devdata(filp);
+ int ret = 0;
+
+ if (vdev->fops->release)
+ ret = vdev->fops->release(filp);
+ if (vdev->debug)
+ printk(KERN_DEBUG "%s: release\n",
+ video_device_node_name(vdev));
+
+ /* decrease the refcount unconditionally since the release()
+ return value is ignored. */
+ video_put(vdev);
+ return ret;
+}
+
+static const struct file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
+ .get_unmapped_area = v4l2_get_unmapped_area,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = v4l2_compat_ioctl32,
+#endif
+ .release = v4l2_release,
+ .poll = v4l2_poll,
+ .llseek = no_llseek,
+};
+
+/**
+ * get_index - assign stream index number based on parent device
+ * @vdev: video_device to assign index number to, vdev->parent should be assigned
+ *
+ * Note that when this is called the new device has not yet been registered
+ * in the video_device array, but it was able to obtain a minor number.
+ *
+ * This means that we can always obtain a free stream index number since
+ * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in
+ * use of the video_device array.
+ *
+ * Returns a free index number.
+ */
+static int get_index(struct video_device *vdev)
+{
+ /* This can be static since this function is called with the global
+ videodev_lock held. */
+ static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
+ int i;
+
+ /* Some drivers do not set the parent. In that case always return 0. */
+ if (vdev->parent == NULL)
+ return 0;
+
+ bitmap_zero(used, VIDEO_NUM_DEVICES);
+
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+ if (video_device[i] != NULL &&
+ video_device[i]->parent == vdev->parent) {
+ set_bit(video_device[i]->index, used);
+ }
+ }
+
+ return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
+}
+
+#define SET_VALID_IOCTL(ops, cmd, op) \
+ if (ops->op) \
+ set_bit(_IOC_NR(cmd), valid_ioctls)
+
+/* This determines which ioctls are actually implemented in the driver.
+ It's a one-time thing which simplifies video_ioctl2 as it can just do
+ a bit test.
+
+ Note that drivers can override this by setting bits to 1 in
+ vdev->valid_ioctls. If an ioctl is marked as 1 when this function is
+ called, then that ioctl will actually be marked as unimplemented.
+
+ It does that by first setting up the local valid_ioctls bitmap, and
+ at the end do a:
+
+ vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls)
+ */
+static void determine_valid_ioctls(struct video_device *vdev)
+{
+ DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
+ const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
+ bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER;
+ bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI;
+ bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO;
+ bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
+
+ bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
+
+ /* vfl_type and vfl_dir independent ioctls */
+
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
+ if (ops->vidioc_g_priority ||
+ test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+ set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+ if (ops->vidioc_s_priority ||
+ test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+ set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
+ SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ /* Note: the control handler can also be passed through the filehandle,
+ and that can't be tested here. If the bit for these control ioctls
+ is set, then the ioctl is valid. But if it is 0, then it can still
+ be valid if the filehandle passed the control handler. */
+ if (vdev->ctrl_handler || ops->vidioc_queryctrl)
+ set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_querymenu)
+ set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
+ SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register);
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register);
+#endif
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
+ /* yes, really vidioc_subscribe_event */
+ SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
+ SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+ if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
+ set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
+
+ if (is_vid) {
+ /* video specific ioctls */
+ if ((is_rx && (ops->vidioc_enum_fmt_vid_cap ||
+ ops->vidioc_enum_fmt_vid_cap_mplane ||
+ ops->vidioc_enum_fmt_vid_overlay)) ||
+ (is_tx && (ops->vidioc_enum_fmt_vid_out ||
+ ops->vidioc_enum_fmt_vid_out_mplane)))
+ set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+ if ((is_rx && (ops->vidioc_g_fmt_vid_cap ||
+ ops->vidioc_g_fmt_vid_cap_mplane ||
+ ops->vidioc_g_fmt_vid_overlay)) ||
+ (is_tx && (ops->vidioc_g_fmt_vid_out ||
+ ops->vidioc_g_fmt_vid_out_mplane ||
+ ops->vidioc_g_fmt_vid_out_overlay)))
+ set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ if ((is_rx && (ops->vidioc_s_fmt_vid_cap ||
+ ops->vidioc_s_fmt_vid_cap_mplane ||
+ ops->vidioc_s_fmt_vid_overlay)) ||
+ (is_tx && (ops->vidioc_s_fmt_vid_out ||
+ ops->vidioc_s_fmt_vid_out_mplane ||
+ ops->vidioc_s_fmt_vid_out_overlay)))
+ set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ if ((is_rx && (ops->vidioc_try_fmt_vid_cap ||
+ ops->vidioc_try_fmt_vid_cap_mplane ||
+ ops->vidioc_try_fmt_vid_overlay)) ||
+ (is_tx && (ops->vidioc_try_fmt_vid_out ||
+ ops->vidioc_try_fmt_vid_out_mplane ||
+ ops->vidioc_try_fmt_vid_out_overlay)))
+ set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp);
+ SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp);
+ SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index);
+ SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
+ } else if (is_vbi) {
+ /* vbi specific ioctls */
+ if ((is_rx && (ops->vidioc_g_fmt_vbi_cap ||
+ ops->vidioc_g_fmt_sliced_vbi_cap)) ||
+ (is_tx && (ops->vidioc_g_fmt_vbi_out ||
+ ops->vidioc_g_fmt_sliced_vbi_out)))
+ set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ if ((is_rx && (ops->vidioc_s_fmt_vbi_cap ||
+ ops->vidioc_s_fmt_sliced_vbi_cap)) ||
+ (is_tx && (ops->vidioc_s_fmt_vbi_out ||
+ ops->vidioc_s_fmt_sliced_vbi_out)))
+ set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ if ((is_rx && (ops->vidioc_try_fmt_vbi_cap ||
+ ops->vidioc_try_fmt_sliced_vbi_cap)) ||
+ (is_tx && (ops->vidioc_try_fmt_vbi_out ||
+ ops->vidioc_try_fmt_sliced_vbi_out)))
+ set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
+ }
+ if (!is_radio) {
+ /* ioctls valid for video or vbi */
+ if (ops->vidioc_s_std)
+ set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
+ if (ops->vidioc_g_std || vdev->current_norm)
+ set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
+ if (is_rx) {
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
+ SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
+ SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
+ }
+ if (is_tx) {
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+ SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+ SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
+ SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
+ SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
+ }
+ if (ops->vidioc_g_crop || ops->vidioc_g_selection)
+ set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+ if (ops->vidioc_s_crop || ops->vidioc_s_selection)
+ set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
+ SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+ if (ops->vidioc_cropcap || ops->vidioc_g_selection)
+ set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
+ if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
+ (ops->vidioc_g_std || vdev->current_norm)))
+ set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets);
+ SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
+ }
+ if (is_tx) {
+ /* transmitter only ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
+ SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
+ }
+ if (is_rx) {
+ /* receiver only ioctls */
+ SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
+ SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
+ SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
+ }
+
+ bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
+ BASE_VIDIOC_PRIVATE);
+}
+
+/**
+ * __video_register_device - register video4linux devices
+ * @vdev: video device structure we want to register
+ * @type: type of device to register
+ * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
+ * -1 == first free)
+ * @warn_if_nr_in_use: warn if the desired device node number
+ * was already in use and another number was chosen instead.
+ * @owner: module that owns the video device node
+ *
+ * The registration code assigns minor numbers and device node numbers
+ * based on the requested type and registers the new device node with
+ * the kernel.
+ *
+ * This function assumes that struct video_device was zeroed when it
+ * was allocated and does not contain any stale date.
+ *
+ * An error is returned if no free minor or device node number could be
+ * found, or if the registration of the device node failed.
+ *
+ * Zero is returned on success.
+ *
+ * Valid types are
+ *
+ * %VFL_TYPE_GRABBER - A frame grabber
+ *
+ * %VFL_TYPE_VBI - Vertical blank data (undecoded)
+ *
+ * %VFL_TYPE_RADIO - A radio card
+ *
+ * %VFL_TYPE_SUBDEV - A subdevice
+ */
+int __video_register_device(struct video_device *vdev, int type, int nr,
+ int warn_if_nr_in_use, struct module *owner)
+{
+ int i = 0;
+ int ret;
+ int minor_offset = 0;
+ int minor_cnt = VIDEO_NUM_DEVICES;
+ const char *name_base;
+
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+ vdev->minor = -1;
+
+ /* the release callback MUST be present */
+ if (WARN_ON(!vdev->release))
+ return -EINVAL;
+
+ /* v4l2_fh support */
+ spin_lock_init(&vdev->fh_lock);
+ INIT_LIST_HEAD(&vdev->fh_list);
+
+ /* Part 1: check device type */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ name_base = "video";
+ break;
+ case VFL_TYPE_VBI:
+ name_base = "vbi";
+ break;
+ case VFL_TYPE_RADIO:
+ name_base = "radio";
+ break;
+ case VFL_TYPE_SUBDEV:
+ name_base = "v4l-subdev";
+ break;
+ default:
+ printk(KERN_ERR "%s called with unknown type: %d\n",
+ __func__, type);
+ return -EINVAL;
+ }
+
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+ if (vdev->v4l2_dev) {
+ if (vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+ if (vdev->ctrl_handler == NULL)
+ vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
+ /* If the prio state pointer is NULL, then use the v4l2_device
+ prio state. */
+ if (vdev->prio == NULL)
+ vdev->prio = &vdev->v4l2_dev->prio;
+ }
+
+ /* Part 2: find a free minor, device node number and device index. */
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* Keep the ranges for the first four types for historical
+ * reasons.
+ * Newer devices (not yet in place) should use the range
+ * of 128-191 and just pick the first free minor there
+ * (new style). */
+ switch (type) {
+ case VFL_TYPE_GRABBER:
+ minor_offset = 0;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_RADIO:
+ minor_offset = 64;
+ minor_cnt = 64;
+ break;
+ case VFL_TYPE_VBI:
+ minor_offset = 224;
+ minor_cnt = 32;
+ break;
+ default:
+ minor_offset = 128;
+ minor_cnt = 64;
+ break;
+ }
+#endif
+
+ /* Pick a device node number */
+ mutex_lock(&videodev_lock);
+ nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
+ if (nr == minor_cnt)
+ nr = devnode_find(vdev, 0, minor_cnt);
+ if (nr == minor_cnt) {
+ printk(KERN_ERR "could not get a free device node number\n");
+ mutex_unlock(&videodev_lock);
+ return -ENFILE;
+ }
+#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
+ /* 1-on-1 mapping of device node number to minor number */
+ i = nr;
+#else
+ /* The device node number and minor numbers are independent, so
+ we just find the first free minor number. */
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++)
+ if (video_device[i] == NULL)
+ break;
+ if (i == VIDEO_NUM_DEVICES) {
+ mutex_unlock(&videodev_lock);
+ printk(KERN_ERR "could not get a free minor\n");
+ return -ENFILE;
+ }
+#endif
+ vdev->minor = i + minor_offset;
+ vdev->num = nr;
+ devnode_set(vdev);
+
+ /* Should not happen since we thought this minor was free */
+ WARN_ON(video_device[vdev->minor] != NULL);
+ vdev->index = get_index(vdev);
+ mutex_unlock(&videodev_lock);
+
+ if (vdev->ioctl_ops)
+ determine_valid_ioctls(vdev);
+
+ /* Part 3: Initialize the character device */
+ vdev->cdev = cdev_alloc();
+ if (vdev->cdev == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ vdev->cdev->ops = &v4l2_fops;
+ vdev->cdev->owner = owner;
+ ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: cdev_add failed\n", __func__);
+ kfree(vdev->cdev);
+ vdev->cdev = NULL;
+ goto cleanup;
+ }
+
+ /* Part 4: register the device with sysfs */
+ vdev->dev.class = &video_class;
+ vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
+ if (vdev->parent)
+ vdev->dev.parent = vdev->parent;
+ dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
+ ret = device_register(&vdev->dev);
+ if (ret < 0) {
+ printk(KERN_ERR "%s: device_register failed\n", __func__);
+ goto cleanup;
+ }
+ /* Register the release callback that will be called when the last
+ reference to the device goes away. */
+ vdev->dev.release = v4l2_device_release;
+
+ if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
+ printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
+ name_base, nr, video_device_node_name(vdev));
+
+ /* Increase v4l2_device refcount */
+ if (vdev->v4l2_dev)
+ v4l2_device_get(vdev->v4l2_dev);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* Part 5: Register the entity. */
+ if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
+ vdev->vfl_type != VFL_TYPE_SUBDEV) {
+ vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ vdev->entity.name = vdev->name;
+ vdev->entity.info.v4l.major = VIDEO_MAJOR;
+ vdev->entity.info.v4l.minor = vdev->minor;
+ ret = media_device_register_entity(vdev->v4l2_dev->mdev,
+ &vdev->entity);
+ if (ret < 0)
+ printk(KERN_WARNING
+ "%s: media_device_register_entity failed\n",
+ __func__);
+ }
+#endif
+ /* Part 6: Activate this minor. The char device can now be used. */
+ set_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_lock(&videodev_lock);
+ video_device[vdev->minor] = vdev;
+ mutex_unlock(&videodev_lock);
+
+ return 0;
+
+cleanup:
+ mutex_lock(&videodev_lock);
+ if (vdev->cdev)
+ cdev_del(vdev->cdev);
+ devnode_clear(vdev);
+ mutex_unlock(&videodev_lock);
+ /* Mark this video device as never having been registered. */
+ vdev->minor = -1;
+ return ret;
+}
+EXPORT_SYMBOL(__video_register_device);
+
+/**
+ * video_unregister_device - unregister a video4linux device
+ * @vdev: the device to unregister
+ *
+ * This unregisters the passed device. Future open calls will
+ * be met with errors.
+ */
+void video_unregister_device(struct video_device *vdev)
+{
+ /* Check if vdev was ever registered at all */
+ if (!vdev || !video_is_registered(vdev))
+ return;
+
+ mutex_lock(&videodev_lock);
+ /* This must be in a critical section to prevent a race with v4l2_open.
+ * Once this bit has been cleared video_get may never be called again.
+ */
+ clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
+ mutex_unlock(&videodev_lock);
+ device_unregister(&vdev->dev);
+}
+EXPORT_SYMBOL(video_unregister_device);
+
+/*
+ * Initialise video for linux
+ */
+static int __init videodev_init(void)
+{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+ int ret;
+
+ printk(KERN_INFO "Linux video capture interface: v2.00\n");
+ ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
+ if (ret < 0) {
+ printk(KERN_WARNING "videodev: unable to get major %d\n",
+ VIDEO_MAJOR);
+ return ret;
+ }
+
+ ret = class_register(&video_class);
+ if (ret < 0) {
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+ printk(KERN_WARNING "video_dev: class_register failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void __exit videodev_exit(void)
+{
+ dev_t dev = MKDEV(VIDEO_MAJOR, 0);
+
+ class_unregister(&video_class);
+ unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
+}
+
+subsys_initcall(videodev_init);
+module_exit(videodev_exit)
+
+MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
+MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/v4l2-core/v4l2-device.c b/drivers/media/v4l2-core/v4l2-device.c
new file mode 100644
index 000000000000..513969fa695d
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-device.c
@@ -0,0 +1,280 @@
+/*
+ V4L2 device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You 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/types.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#if defined(CONFIG_SPI)
+#include <linux/spi/spi.h>
+#endif
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+{
+ if (v4l2_dev == NULL)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&v4l2_dev->subdevs);
+ spin_lock_init(&v4l2_dev->lock);
+ mutex_init(&v4l2_dev->ioctl_lock);
+ v4l2_prio_init(&v4l2_dev->prio);
+ kref_init(&v4l2_dev->ref);
+ get_device(dev);
+ v4l2_dev->dev = dev;
+ if (dev == NULL) {
+ /* If dev == NULL, then name must be filled in by the caller */
+ WARN_ON(!v4l2_dev->name[0]);
+ return 0;
+ }
+
+ /* Set name to driver name + device name if it is empty. */
+ if (!v4l2_dev->name[0])
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+ dev->driver->name, dev_name(dev));
+ if (!dev_get_drvdata(dev))
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register);
+
+static void v4l2_device_release(struct kref *ref)
+{
+ struct v4l2_device *v4l2_dev =
+ container_of(ref, struct v4l2_device, ref);
+
+ if (v4l2_dev->release)
+ v4l2_dev->release(v4l2_dev);
+}
+
+int v4l2_device_put(struct v4l2_device *v4l2_dev)
+{
+ return kref_put(&v4l2_dev->ref, v4l2_device_release);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_put);
+
+int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
+ atomic_t *instance)
+{
+ int num = atomic_inc_return(instance) - 1;
+ int len = strlen(basename);
+
+ if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s-%d", basename, num);
+ else
+ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
+ "%s%d", basename, num);
+ return num;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_set_name);
+
+void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
+{
+ if (v4l2_dev->dev == NULL)
+ return;
+
+ if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
+ dev_set_drvdata(v4l2_dev->dev, NULL);
+ put_device(v4l2_dev->dev);
+ v4l2_dev->dev = NULL;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
+
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd, *next;
+
+ if (v4l2_dev == NULL)
+ return;
+ v4l2_device_disconnect(v4l2_dev);
+
+ /* Unregister subdevs */
+ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
+ v4l2_device_unregister_subdev(sd);
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+ if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) {
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* We need to unregister the i2c client explicitly.
+ We cannot rely on i2c_del_adapter to always
+ unregister clients for us, since if the i2c bus
+ is a platform bus, then it is never deleted. */
+ if (client)
+ i2c_unregister_device(client);
+ continue;
+ }
+#endif
+#if defined(CONFIG_SPI)
+ if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) {
+ struct spi_device *spi = v4l2_get_subdevdata(sd);
+
+ if (spi)
+ spi_unregister_device(spi);
+ continue;
+ }
+#endif
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister);
+
+int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_entity *entity = &sd->entity;
+#endif
+ int err;
+
+ /* Check for valid input */
+ if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+
+ /* Warn if we apparently re-register a subdev */
+ WARN_ON(sd->v4l2_dev != NULL);
+
+ if (!try_module_get(sd->owner))
+ return -ENODEV;
+
+ sd->v4l2_dev = v4l2_dev;
+ if (sd->internal_ops && sd->internal_ops->registered) {
+ err = sd->internal_ops->registered(sd);
+ if (err) {
+ module_put(sd->owner);
+ return err;
+ }
+ }
+
+ /* This just returns 0 if either of the two args is NULL */
+ err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler, NULL);
+ if (err) {
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ module_put(sd->owner);
+ return err;
+ }
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ /* Register the entity. */
+ if (v4l2_dev->mdev) {
+ err = media_device_register_entity(v4l2_dev->mdev, entity);
+ if (err < 0) {
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ module_put(sd->owner);
+ return err;
+ }
+ }
+#endif
+
+ spin_lock(&v4l2_dev->lock);
+ list_add_tail(&sd->list, &v4l2_dev->subdevs);
+ spin_unlock(&v4l2_dev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+
+static void v4l2_device_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vdev;
+ struct v4l2_subdev *sd;
+ int err;
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ continue;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ err = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = v4l2_device_release_subdev_node;
+ vdev->ctrl_handler = sd->ctrl_handler;
+ err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (err < 0) {
+ kfree(vdev);
+ goto clean_up;
+ }
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+#endif
+ sd->devnode = vdev;
+ }
+ return 0;
+
+clean_up:
+ list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
+ if (!sd->devnode)
+ break;
+ video_unregister_device(sd->devnode);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
+
+void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+{
+ struct v4l2_device *v4l2_dev;
+
+ /* return if it isn't registered */
+ if (sd == NULL || sd->v4l2_dev == NULL)
+ return;
+
+ v4l2_dev = sd->v4l2_dev;
+
+ spin_lock(&v4l2_dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&v4l2_dev->lock);
+
+ if (sd->internal_ops && sd->internal_ops->unregistered)
+ sd->internal_ops->unregistered(sd);
+ sd->v4l2_dev = NULL;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (v4l2_dev->mdev)
+ media_device_unregister_entity(&sd->entity);
+#endif
+ video_unregister_device(sd->devnode);
+ module_put(sd->owner);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c
new file mode 100644
index 000000000000..18a040b935a3
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-event.c
@@ -0,0 +1,313 @@
+/*
+ * v4l2-event.c
+ *
+ * V4L2 events.
+ *
+ * Copyright (C) 2009--2010 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
+{
+ idx += sev->first;
+ return idx >= sev->elems ? idx - sev->elems : idx;
+}
+
+static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
+{
+ struct v4l2_kevent *kev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+ if (list_empty(&fh->available)) {
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ return -ENOENT;
+ }
+
+ WARN_ON(fh->navailable == 0);
+
+ kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
+ list_del(&kev->list);
+ fh->navailable--;
+
+ kev->event.pending = fh->navailable;
+ *event = kev->event;
+ kev->sev->first = sev_pos(kev->sev, 1);
+ kev->sev->in_use--;
+
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+ return 0;
+}
+
+int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
+ int nonblocking)
+{
+ int ret;
+
+ if (nonblocking)
+ return __v4l2_event_dequeue(fh, event);
+
+ /* Release the vdev lock while waiting */
+ if (fh->vdev->lock)
+ mutex_unlock(fh->vdev->lock);
+
+ do {
+ ret = wait_event_interruptible(fh->wait,
+ fh->navailable != 0);
+ if (ret < 0)
+ break;
+
+ ret = __v4l2_event_dequeue(fh, event);
+ } while (ret == -ENOENT);
+
+ if (fh->vdev->lock)
+ mutex_lock(fh->vdev->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
+
+/* Caller must hold fh->vdev->fh_lock! */
+static struct v4l2_subscribed_event *v4l2_event_subscribed(
+ struct v4l2_fh *fh, u32 type, u32 id)
+{
+ struct v4l2_subscribed_event *sev;
+
+ assert_spin_locked(&fh->vdev->fh_lock);
+
+ list_for_each_entry(sev, &fh->subscribed, list)
+ if (sev->type == type && sev->id == id)
+ return sev;
+
+ return NULL;
+}
+
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+ const struct timespec *ts)
+{
+ struct v4l2_subscribed_event *sev;
+ struct v4l2_kevent *kev;
+ bool copy_payload = true;
+
+ /* Are we subscribed? */
+ sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+ if (sev == NULL)
+ return;
+
+ /*
+ * If the event has been added to the fh->subscribed list, but its
+ * add op has not completed yet elems will be 0, treat this as
+ * not being subscribed.
+ */
+ if (!sev->elems)
+ return;
+
+ /* Increase event sequence number on fh. */
+ fh->sequence++;
+
+ /* Do we have any free events? */
+ if (sev->in_use == sev->elems) {
+ /* no, remove the oldest one */
+ kev = sev->events + sev_pos(sev, 0);
+ list_del(&kev->list);
+ sev->in_use--;
+ sev->first = sev_pos(sev, 1);
+ fh->navailable--;
+ if (sev->elems == 1) {
+ if (sev->ops && sev->ops->replace) {
+ sev->ops->replace(&kev->event, ev);
+ copy_payload = false;
+ }
+ } else if (sev->ops && sev->ops->merge) {
+ struct v4l2_kevent *second_oldest =
+ sev->events + sev_pos(sev, 0);
+ sev->ops->merge(&kev->event, &second_oldest->event);
+ }
+ }
+
+ /* Take one and fill it. */
+ kev = sev->events + sev_pos(sev, sev->in_use);
+ kev->event.type = ev->type;
+ if (copy_payload)
+ kev->event.u = ev->u;
+ kev->event.id = ev->id;
+ kev->event.timestamp = *ts;
+ kev->event.sequence = fh->sequence;
+ sev->in_use++;
+ list_add_tail(&kev->list, &fh->available);
+
+ fh->navailable++;
+
+ wake_up_all(&fh->wait);
+}
+
+void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
+{
+ struct v4l2_fh *fh;
+ unsigned long flags;
+ struct timespec timestamp;
+
+ ktime_get_ts(&timestamp);
+
+ spin_lock_irqsave(&vdev->fh_lock, flags);
+
+ list_for_each_entry(fh, &vdev->fh_list, list)
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
+
+ spin_unlock_irqrestore(&vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue);
+
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+ unsigned long flags;
+ struct timespec timestamp;
+
+ ktime_get_ts(&timestamp);
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ __v4l2_event_queue_fh(fh, ev, &timestamp);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
+
+int v4l2_event_pending(struct v4l2_fh *fh)
+{
+ return fh->navailable;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_pending);
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub, unsigned elems,
+ const struct v4l2_subscribed_event_ops *ops)
+{
+ struct v4l2_subscribed_event *sev, *found_ev;
+ unsigned long flags;
+ unsigned i;
+
+ if (sub->type == V4L2_EVENT_ALL)
+ return -EINVAL;
+
+ if (elems < 1)
+ elems = 1;
+
+ sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
+ if (!sev)
+ return -ENOMEM;
+ for (i = 0; i < elems; i++)
+ sev->events[i].sev = sev;
+ sev->type = sub->type;
+ sev->id = sub->id;
+ sev->flags = sub->flags;
+ sev->fh = fh;
+ sev->ops = ops;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (!found_ev)
+ list_add(&sev->list, &fh->subscribed);
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+ if (found_ev) {
+ kfree(sev);
+ return 0; /* Already listening */
+ }
+
+ if (sev->ops && sev->ops->add) {
+ int ret = sev->ops->add(sev, elems);
+ if (ret) {
+ sev->ops = NULL;
+ v4l2_event_unsubscribe(fh, sub);
+ return ret;
+ }
+ }
+
+ /* Mark as ready for use */
+ sev->elems = elems;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
+
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+{
+ struct v4l2_event_subscription sub;
+ struct v4l2_subscribed_event *sev;
+ unsigned long flags;
+
+ do {
+ sev = NULL;
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+ if (!list_empty(&fh->subscribed)) {
+ sev = list_first_entry(&fh->subscribed,
+ struct v4l2_subscribed_event, list);
+ sub.type = sev->type;
+ sub.id = sev->id;
+ }
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+ if (sev)
+ v4l2_event_unsubscribe(fh, &sub);
+ } while (sev);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
+
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct v4l2_subscribed_event *sev;
+ unsigned long flags;
+ int i;
+
+ if (sub->type == V4L2_EVENT_ALL) {
+ v4l2_event_unsubscribe_all(fh);
+ return 0;
+ }
+
+ spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+
+ sev = v4l2_event_subscribed(fh, sub->type, sub->id);
+ if (sev != NULL) {
+ /* Remove any pending events for this subscription */
+ for (i = 0; i < sev->in_use; i++) {
+ list_del(&sev->events[sev_pos(sev, i)].list);
+ fh->navailable--;
+ }
+ list_del(&sev->list);
+ }
+
+ spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+ if (sev && sev->ops && sev->ops->del)
+ sev->ops->del(sev);
+
+ kfree(sev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c
index 9e3fc040ea20..9e3fc040ea20 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/v4l2-core/v4l2-fh.c
diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/v4l2-core/v4l2-int-device.c
index f4473494af7a..f4473494af7a 100644
--- a/drivers/media/video/v4l2-int-device.c
+++ b/drivers/media/v4l2-core/v4l2-int-device.c
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
new file mode 100644
index 000000000000..9d3e46c446ad
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -0,0 +1,2358 @@
+/*
+ * Video capture interface for Linux version 2
+ *
+ * A generic framework to process V4L2 ioctl commands.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
+ * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/videobuf2-core.h>
+
+/* Zero out the end of the struct pointed to by p. Everything after, but
+ * not including, the specified field is cleared. */
+#define CLEAR_AFTER_FIELD(p, field) \
+ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
+ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
+
+struct std_descr {
+ v4l2_std_id std;
+ const char *descr;
+};
+
+static const struct std_descr standards[] = {
+ { V4L2_STD_NTSC, "NTSC" },
+ { V4L2_STD_NTSC_M, "NTSC-M" },
+ { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" },
+ { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" },
+ { V4L2_STD_NTSC_443, "NTSC-443" },
+ { V4L2_STD_PAL, "PAL" },
+ { V4L2_STD_PAL_BG, "PAL-BG" },
+ { V4L2_STD_PAL_B, "PAL-B" },
+ { V4L2_STD_PAL_B1, "PAL-B1" },
+ { V4L2_STD_PAL_G, "PAL-G" },
+ { V4L2_STD_PAL_H, "PAL-H" },
+ { V4L2_STD_PAL_I, "PAL-I" },
+ { V4L2_STD_PAL_DK, "PAL-DK" },
+ { V4L2_STD_PAL_D, "PAL-D" },
+ { V4L2_STD_PAL_D1, "PAL-D1" },
+ { V4L2_STD_PAL_K, "PAL-K" },
+ { V4L2_STD_PAL_M, "PAL-M" },
+ { V4L2_STD_PAL_N, "PAL-N" },
+ { V4L2_STD_PAL_Nc, "PAL-Nc" },
+ { V4L2_STD_PAL_60, "PAL-60" },
+ { V4L2_STD_SECAM, "SECAM" },
+ { V4L2_STD_SECAM_B, "SECAM-B" },
+ { V4L2_STD_SECAM_G, "SECAM-G" },
+ { V4L2_STD_SECAM_H, "SECAM-H" },
+ { V4L2_STD_SECAM_DK, "SECAM-DK" },
+ { V4L2_STD_SECAM_D, "SECAM-D" },
+ { V4L2_STD_SECAM_K, "SECAM-K" },
+ { V4L2_STD_SECAM_K1, "SECAM-K1" },
+ { V4L2_STD_SECAM_L, "SECAM-L" },
+ { V4L2_STD_SECAM_LC, "SECAM-Lc" },
+ { 0, "Unknown" }
+};
+
+/* video4linux standard ID conversion to standard name
+ */
+const char *v4l2_norm_to_name(v4l2_std_id id)
+{
+ u32 myid = id;
+ int i;
+
+ /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
+ 64 bit comparations. So, on that architecture, with some gcc
+ variants, compilation fails. Currently, the max value is 30bit wide.
+ */
+ BUG_ON(myid != id);
+
+ for (i = 0; standards[i].std; i++)
+ if (myid == standards[i].std)
+ break;
+ return standards[i].descr;
+}
+EXPORT_SYMBOL(v4l2_norm_to_name);
+
+/* Returns frame period for the given standard */
+void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
+{
+ if (id & V4L2_STD_525_60) {
+ frameperiod->numerator = 1001;
+ frameperiod->denominator = 30000;
+ } else {
+ frameperiod->numerator = 1;
+ frameperiod->denominator = 25;
+ }
+}
+EXPORT_SYMBOL(v4l2_video_std_frame_period);
+
+/* Fill in the fields of a v4l2_standard structure according to the
+ 'id' and 'transmission' parameters. Returns negative on error. */
+int v4l2_video_std_construct(struct v4l2_standard *vs,
+ int id, const char *name)
+{
+ vs->id = id;
+ v4l2_video_std_frame_period(id, &vs->frameperiod);
+ vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
+ strlcpy(vs->name, name, sizeof(vs->name));
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_video_std_construct);
+
+/* ----------------------------------------------------------------- */
+/* some arrays for pretty-printing debug messages of enum types */
+
+const char *v4l2_field_names[] = {
+ [V4L2_FIELD_ANY] = "any",
+ [V4L2_FIELD_NONE] = "none",
+ [V4L2_FIELD_TOP] = "top",
+ [V4L2_FIELD_BOTTOM] = "bottom",
+ [V4L2_FIELD_INTERLACED] = "interlaced",
+ [V4L2_FIELD_SEQ_TB] = "seq-tb",
+ [V4L2_FIELD_SEQ_BT] = "seq-bt",
+ [V4L2_FIELD_ALTERNATE] = "alternate",
+ [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
+ [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
+};
+EXPORT_SYMBOL(v4l2_field_names);
+
+const char *v4l2_type_names[] = {
+ [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap",
+ [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay",
+ [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out",
+ [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
+ [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
+ [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);
+
+static const char *v4l2_memory_names[] = {
+ [V4L2_MEMORY_MMAP] = "mmap",
+ [V4L2_MEMORY_USERPTR] = "userptr",
+ [V4L2_MEMORY_OVERLAY] = "overlay",
+};
+
+#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
+ arr[a] : "unknown")
+
+/* ------------------------------------------------------------------ */
+/* debug help functions */
+
+static void v4l_print_querycap(const void *arg, bool write_only)
+{
+ const struct v4l2_capability *p = arg;
+
+ pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
+ "capabilities=0x%08x, device_caps=0x%08x\n",
+ p->driver, p->card, p->bus_info,
+ p->version, p->capabilities, p->device_caps);
+}
+
+static void v4l_print_enuminput(const void *arg, bool write_only)
+{
+ const struct v4l2_input *p = arg;
+
+ pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
+ "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
+ p->index, p->name, p->type, p->audioset, p->tuner,
+ (unsigned long long)p->std, p->status, p->capabilities);
+}
+
+static void v4l_print_enumoutput(const void *arg, bool write_only)
+{
+ const struct v4l2_output *p = arg;
+
+ pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
+ "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
+ p->index, p->name, p->type, p->audioset, p->modulator,
+ (unsigned long long)p->std, p->capabilities);
+}
+
+static void v4l_print_audio(const void *arg, bool write_only)
+{
+ const struct v4l2_audio *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+ p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_audioout(const void *arg, bool write_only)
+{
+ const struct v4l2_audioout *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u\n", p->index);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
+ p->index, p->name, p->capability, p->mode);
+}
+
+static void v4l_print_fmtdesc(const void *arg, bool write_only)
+{
+ const struct v4l2_fmtdesc *p = arg;
+
+ pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
+ p->index, prt_names(p->type, v4l2_type_names),
+ p->flags, (p->pixelformat & 0xff),
+ (p->pixelformat >> 8) & 0xff,
+ (p->pixelformat >> 16) & 0xff,
+ (p->pixelformat >> 24) & 0xff,
+ p->description);
+}
+
+static void v4l_print_format(const void *arg, bool write_only)
+{
+ const struct v4l2_format *p = arg;
+ const struct v4l2_pix_format *pix;
+ const struct v4l2_pix_format_mplane *mp;
+ const struct v4l2_vbi_format *vbi;
+ const struct v4l2_sliced_vbi_format *sliced;
+ const struct v4l2_window *win;
+ const struct v4l2_clip *clip;
+ unsigned i;
+
+ pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ pix = &p->fmt.pix;
+ pr_cont(", width=%u, height=%u, "
+ "pixelformat=%c%c%c%c, field=%s, "
+ "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+ pix->width, pix->height,
+ (pix->pixelformat & 0xff),
+ (pix->pixelformat >> 8) & 0xff,
+ (pix->pixelformat >> 16) & 0xff,
+ (pix->pixelformat >> 24) & 0xff,
+ prt_names(pix->field, v4l2_field_names),
+ pix->bytesperline, pix->sizeimage,
+ pix->colorspace);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ mp = &p->fmt.pix_mp;
+ pr_cont(", width=%u, height=%u, "
+ "format=%c%c%c%c, field=%s, "
+ "colorspace=%d, num_planes=%u\n",
+ mp->width, mp->height,
+ (mp->pixelformat & 0xff),
+ (mp->pixelformat >> 8) & 0xff,
+ (mp->pixelformat >> 16) & 0xff,
+ (mp->pixelformat >> 24) & 0xff,
+ prt_names(mp->field, v4l2_field_names),
+ mp->colorspace, mp->num_planes);
+ for (i = 0; i < mp->num_planes; i++)
+ printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
+ mp->plane_fmt[i].bytesperline,
+ mp->plane_fmt[i].sizeimage);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ win = &p->fmt.win;
+ pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
+ "chromakey=0x%08x, bitmap=%p, "
+ "global_alpha=0x%02x\n",
+ win->w.width, win->w.height,
+ win->w.left, win->w.top,
+ prt_names(win->field, v4l2_field_names),
+ win->chromakey, win->bitmap, win->global_alpha);
+ clip = win->clips;
+ for (i = 0; i < win->clipcount; i++) {
+ printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
+ i, clip->c.width, clip->c.height,
+ clip->c.left, clip->c.top);
+ clip = clip->next;
+ }
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ vbi = &p->fmt.vbi;
+ pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
+ "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
+ vbi->sampling_rate, vbi->offset,
+ vbi->samples_per_line,
+ (vbi->sample_format & 0xff),
+ (vbi->sample_format >> 8) & 0xff,
+ (vbi->sample_format >> 16) & 0xff,
+ (vbi->sample_format >> 24) & 0xff,
+ vbi->start[0], vbi->start[1],
+ vbi->count[0], vbi->count[1]);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ sliced = &p->fmt.sliced;
+ pr_cont(", service_set=0x%08x, io_size=%d\n",
+ sliced->service_set, sliced->io_size);
+ for (i = 0; i < 24; i++)
+ printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+ sliced->service_lines[0][i],
+ sliced->service_lines[1][i]);
+ break;
+ }
+}
+
+static void v4l_print_framebuffer(const void *arg, bool write_only)
+{
+ const struct v4l2_framebuffer *p = arg;
+
+ pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
+ "height=%u, pixelformat=%c%c%c%c, "
+ "bytesperline=%u sizeimage=%u, colorspace=%d\n",
+ p->capability, p->flags, p->base,
+ p->fmt.width, p->fmt.height,
+ (p->fmt.pixelformat & 0xff),
+ (p->fmt.pixelformat >> 8) & 0xff,
+ (p->fmt.pixelformat >> 16) & 0xff,
+ (p->fmt.pixelformat >> 24) & 0xff,
+ p->fmt.bytesperline, p->fmt.sizeimage,
+ p->fmt.colorspace);
+}
+
+static void v4l_print_buftype(const void *arg, bool write_only)
+{
+ pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
+}
+
+static void v4l_print_modulator(const void *arg, bool write_only)
+{
+ const struct v4l2_modulator *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
+ else
+ pr_cont("index=%u, name=%s, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
+ p->index, p->name, p->capability,
+ p->rangelow, p->rangehigh, p->txsubchans);
+}
+
+static void v4l_print_tuner(const void *arg, bool write_only)
+{
+ const struct v4l2_tuner *p = arg;
+
+ if (write_only)
+ pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
+ else
+ pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
+ "rxsubchans=0x%x, audmode=%u\n",
+ p->index, p->name, p->type,
+ p->capability, p->rangelow,
+ p->rangehigh, p->signal, p->afc,
+ p->rxsubchans, p->audmode);
+}
+
+static void v4l_print_frequency(const void *arg, bool write_only)
+{
+ const struct v4l2_frequency *p = arg;
+
+ pr_cont("tuner=%u, type=%u, frequency=%u\n",
+ p->tuner, p->type, p->frequency);
+}
+
+static void v4l_print_standard(const void *arg, bool write_only)
+{
+ const struct v4l2_standard *p = arg;
+
+ pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
+ "framelines=%u\n", p->index,
+ (unsigned long long)p->id, p->name,
+ p->frameperiod.numerator,
+ p->frameperiod.denominator,
+ p->framelines);
+}
+
+static void v4l_print_std(const void *arg, bool write_only)
+{
+ pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
+}
+
+static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
+{
+ const struct v4l2_hw_freq_seek *p = arg;
+
+ pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u, "
+ "rangelow=%u, rangehigh=%u\n",
+ p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing,
+ p->rangelow, p->rangehigh);
+}
+
+static void v4l_print_requestbuffers(const void *arg, bool write_only)
+{
+ const struct v4l2_requestbuffers *p = arg;
+
+ pr_cont("count=%d, type=%s, memory=%s\n",
+ p->count,
+ prt_names(p->type, v4l2_type_names),
+ prt_names(p->memory, v4l2_memory_names));
+}
+
+static void v4l_print_buffer(const void *arg, bool write_only)
+{
+ const struct v4l2_buffer *p = arg;
+ const struct v4l2_timecode *tc = &p->timecode;
+ const struct v4l2_plane *plane;
+ int i;
+
+ pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
+ "flags=0x%08x, field=%s, sequence=%d, memory=%s",
+ 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->flags, prt_names(p->field, v4l2_field_names),
+ p->sequence, prt_names(p->memory, v4l2_memory_names));
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
+ pr_cont("\n");
+ for (i = 0; i < p->length; ++i) {
+ plane = &p->m.planes[i];
+ printk(KERN_DEBUG
+ "plane %d: bytesused=%d, data_offset=0x%08x "
+ "offset/userptr=0x%lx, length=%d\n",
+ i, plane->bytesused, plane->data_offset,
+ plane->m.userptr, plane->length);
+ }
+ } else {
+ pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
+ p->bytesused, p->m.userptr, p->length);
+ }
+
+ printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
+ "flags=0x%08x, frames=%d, userbits=0x%08x\n",
+ tc->hours, tc->minutes, tc->seconds,
+ tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
+}
+
+static void v4l_print_create_buffers(const void *arg, bool write_only)
+{
+ const struct v4l2_create_buffers *p = arg;
+
+ pr_cont("index=%d, count=%d, memory=%s, ",
+ p->index, p->count,
+ prt_names(p->memory, v4l2_memory_names));
+ v4l_print_format(&p->format, write_only);
+}
+
+static void v4l_print_streamparm(const void *arg, bool write_only)
+{
+ const struct v4l2_streamparm *p = arg;
+
+ pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
+
+ if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ const struct v4l2_captureparm *c = &p->parm.capture;
+
+ pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
+ "extendedmode=%d, readbuffers=%d\n",
+ c->capability, c->capturemode,
+ c->timeperframe.numerator, c->timeperframe.denominator,
+ c->extendedmode, c->readbuffers);
+ } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ const struct v4l2_outputparm *c = &p->parm.output;
+
+ pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
+ "extendedmode=%d, writebuffers=%d\n",
+ c->capability, c->outputmode,
+ c->timeperframe.numerator, c->timeperframe.denominator,
+ c->extendedmode, c->writebuffers);
+ }
+}
+
+static void v4l_print_queryctrl(const void *arg, bool write_only)
+{
+ const struct v4l2_queryctrl *p = arg;
+
+ pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
+ "step=%d, default=%d, flags=0x%08x\n",
+ p->id, p->type, p->name,
+ p->minimum, p->maximum,
+ p->step, p->default_value, p->flags);
+}
+
+static void v4l_print_querymenu(const void *arg, bool write_only)
+{
+ const struct v4l2_querymenu *p = arg;
+
+ pr_cont("id=0x%x, index=%d\n", p->id, p->index);
+}
+
+static void v4l_print_control(const void *arg, bool write_only)
+{
+ const struct v4l2_control *p = arg;
+
+ pr_cont("id=0x%x, value=%d\n", p->id, p->value);
+}
+
+static void v4l_print_ext_controls(const void *arg, bool write_only)
+{
+ const struct v4l2_ext_controls *p = arg;
+ int i;
+
+ pr_cont("class=0x%x, count=%d, error_idx=%d",
+ p->ctrl_class, p->count, p->error_idx);
+ for (i = 0; i < p->count; i++) {
+ if (p->controls[i].size)
+ pr_cont(", id/val=0x%x/0x%x",
+ p->controls[i].id, p->controls[i].value);
+ else
+ pr_cont(", id/size=0x%x/%u",
+ p->controls[i].id, p->controls[i].size);
+ }
+ pr_cont("\n");
+}
+
+static void v4l_print_cropcap(const void *arg, bool write_only)
+{
+ const struct v4l2_cropcap *p = arg;
+
+ pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
+ "defrect wxh=%dx%d, x,y=%d,%d\n, "
+ "pixelaspect %d/%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->bounds.width, p->bounds.height,
+ p->bounds.left, p->bounds.top,
+ p->defrect.width, p->defrect.height,
+ p->defrect.left, p->defrect.top,
+ p->pixelaspect.numerator, p->pixelaspect.denominator);
+}
+
+static void v4l_print_crop(const void *arg, bool write_only)
+{
+ const struct v4l2_crop *p = arg;
+
+ pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->c.width, p->c.height,
+ p->c.left, p->c.top);
+}
+
+static void v4l_print_selection(const void *arg, bool write_only)
+{
+ const struct v4l2_selection *p = arg;
+
+ pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
+ prt_names(p->type, v4l2_type_names),
+ p->target, p->flags,
+ p->r.width, p->r.height, p->r.left, p->r.top);
+}
+
+static void v4l_print_jpegcompression(const void *arg, bool write_only)
+{
+ const struct v4l2_jpegcompression *p = arg;
+
+ pr_cont("quality=%d, APPn=%d, APP_len=%d, "
+ "COM_len=%d, jpeg_markers=0x%x\n",
+ p->quality, p->APPn, p->APP_len,
+ p->COM_len, p->jpeg_markers);
+}
+
+static void v4l_print_enc_idx(const void *arg, bool write_only)
+{
+ const struct v4l2_enc_idx *p = arg;
+
+ pr_cont("entries=%d, entries_cap=%d\n",
+ p->entries, p->entries_cap);
+}
+
+static void v4l_print_encoder_cmd(const void *arg, bool write_only)
+{
+ const struct v4l2_encoder_cmd *p = arg;
+
+ pr_cont("cmd=%d, flags=0x%x\n",
+ p->cmd, p->flags);
+}
+
+static void v4l_print_decoder_cmd(const void *arg, bool write_only)
+{
+ const struct v4l2_decoder_cmd *p = arg;
+
+ pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
+
+ if (p->cmd == V4L2_DEC_CMD_START)
+ pr_info("speed=%d, format=%u\n",
+ p->start.speed, p->start.format);
+ else if (p->cmd == V4L2_DEC_CMD_STOP)
+ pr_info("pts=%llu\n", p->stop.pts);
+}
+
+static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
+{
+ const struct v4l2_dbg_chip_ident *p = arg;
+
+ pr_cont("type=%u, ", p->match.type);
+ if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ pr_cont("name=%s, ", p->match.name);
+ else
+ pr_cont("addr=%u, ", p->match.addr);
+ pr_cont("chip_ident=%u, revision=0x%x\n",
+ p->ident, p->revision);
+}
+
+static void v4l_print_dbg_register(const void *arg, bool write_only)
+{
+ const struct v4l2_dbg_register *p = arg;
+
+ pr_cont("type=%u, ", p->match.type);
+ if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
+ pr_cont("name=%s, ", p->match.name);
+ else
+ pr_cont("addr=%u, ", p->match.addr);
+ pr_cont("reg=0x%llx, val=0x%llx\n",
+ p->reg, p->val);
+}
+
+static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_enum_preset *p = arg;
+
+ pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
+ p->index, p->preset, p->name, p->width, p->height);
+}
+
+static void v4l_print_dv_preset(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_preset *p = arg;
+
+ pr_cont("preset=%u\n", p->preset);
+}
+
+static void v4l_print_dv_timings(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_timings *p = arg;
+
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ pr_cont("type=bt-656/1120, interlaced=%u, "
+ "pixelclock=%llu, "
+ "width=%u, height=%u, polarities=0x%x, "
+ "hfrontporch=%u, hsync=%u, "
+ "hbackporch=%u, vfrontporch=%u, "
+ "vsync=%u, vbackporch=%u, "
+ "il_vfrontporch=%u, il_vsync=%u, "
+ "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height,
+ p->bt.polarities, p->bt.hfrontporch,
+ p->bt.hsync, p->bt.hbackporch,
+ p->bt.vfrontporch, p->bt.vsync,
+ p->bt.vbackporch, p->bt.il_vfrontporch,
+ p->bt.il_vsync, p->bt.il_vbackporch,
+ p->bt.standards, p->bt.flags);
+ break;
+ default:
+ pr_cont("type=%d\n", p->type);
+ break;
+ }
+}
+
+static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
+{
+ const struct v4l2_enum_dv_timings *p = arg;
+
+ pr_cont("index=%u, ", p->index);
+ v4l_print_dv_timings(&p->timings, write_only);
+}
+
+static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
+{
+ const struct v4l2_dv_timings_cap *p = arg;
+
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
+ "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
+ p->bt.min_width, p->bt.max_width,
+ p->bt.min_height, p->bt.max_height,
+ p->bt.min_pixelclock, p->bt.max_pixelclock,
+ p->bt.standards, p->bt.capabilities);
+ break;
+ default:
+ pr_cont("type=%u\n", p->type);
+ break;
+ }
+}
+
+static void v4l_print_frmsizeenum(const void *arg, bool write_only)
+{
+ const struct v4l2_frmsizeenum *p = arg;
+
+ pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
+ p->index,
+ (p->pixel_format & 0xff),
+ (p->pixel_format >> 8) & 0xff,
+ (p->pixel_format >> 16) & 0xff,
+ (p->pixel_format >> 24) & 0xff,
+ p->type);
+ switch (p->type) {
+ case V4L2_FRMSIZE_TYPE_DISCRETE:
+ pr_cont(" wxh=%ux%u\n",
+ p->discrete.width, p->discrete.height);
+ break;
+ case V4L2_FRMSIZE_TYPE_STEPWISE:
+ pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
+ p->stepwise.min_width, p->stepwise.min_height,
+ p->stepwise.step_width, p->stepwise.step_height,
+ p->stepwise.max_width, p->stepwise.max_height);
+ break;
+ case V4L2_FRMSIZE_TYPE_CONTINUOUS:
+ /* fall through */
+ default:
+ pr_cont("\n");
+ break;
+ }
+}
+
+static void v4l_print_frmivalenum(const void *arg, bool write_only)
+{
+ const struct v4l2_frmivalenum *p = arg;
+
+ pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
+ p->index,
+ (p->pixel_format & 0xff),
+ (p->pixel_format >> 8) & 0xff,
+ (p->pixel_format >> 16) & 0xff,
+ (p->pixel_format >> 24) & 0xff,
+ p->width, p->height, p->type);
+ switch (p->type) {
+ case V4L2_FRMIVAL_TYPE_DISCRETE:
+ pr_cont(" fps=%d/%d\n",
+ p->discrete.numerator,
+ p->discrete.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_STEPWISE:
+ pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
+ p->stepwise.min.numerator,
+ p->stepwise.min.denominator,
+ p->stepwise.max.numerator,
+ p->stepwise.max.denominator,
+ p->stepwise.step.numerator,
+ p->stepwise.step.denominator);
+ break;
+ case V4L2_FRMIVAL_TYPE_CONTINUOUS:
+ /* fall through */
+ default:
+ pr_cont("\n");
+ break;
+ }
+}
+
+static void v4l_print_event(const void *arg, bool write_only)
+{
+ const struct v4l2_event *p = arg;
+ const struct v4l2_event_ctrl *c;
+
+ pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
+ "timestamp=%lu.%9.9lu\n",
+ p->type, p->pending, p->sequence, p->id,
+ p->timestamp.tv_sec, p->timestamp.tv_nsec);
+ switch (p->type) {
+ case V4L2_EVENT_VSYNC:
+ printk(KERN_DEBUG "field=%s\n",
+ prt_names(p->u.vsync.field, v4l2_field_names));
+ break;
+ case V4L2_EVENT_CTRL:
+ c = &p->u.ctrl;
+ printk(KERN_DEBUG "changes=0x%x, type=%u, ",
+ c->changes, c->type);
+ if (c->type == V4L2_CTRL_TYPE_INTEGER64)
+ pr_cont("value64=%lld, ", c->value64);
+ else
+ pr_cont("value=%d, ", c->value);
+ pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
+ " default_value=%d\n",
+ c->flags, c->minimum, c->maximum,
+ c->step, c->default_value);
+ break;
+ case V4L2_EVENT_FRAME_SYNC:
+ pr_cont("frame_sequence=%u\n",
+ p->u.frame_sync.frame_sequence);
+ break;
+ }
+}
+
+static void v4l_print_event_subscription(const void *arg, bool write_only)
+{
+ const struct v4l2_event_subscription *p = arg;
+
+ pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
+ p->type, p->id, p->flags);
+}
+
+static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
+{
+ const struct v4l2_sliced_vbi_cap *p = arg;
+ int i;
+
+ pr_cont("type=%s, service_set=0x%08x\n",
+ prt_names(p->type, v4l2_type_names), p->service_set);
+ for (i = 0; i < 24; i++)
+ printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
+ p->service_lines[0][i],
+ p->service_lines[1][i]);
+}
+
+static void v4l_print_freq_band(const void *arg, bool write_only)
+{
+ const struct v4l2_frequency_band *p = arg;
+
+ pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
+ "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
+ p->tuner, p->type, p->index,
+ p->capability, p->rangelow,
+ p->rangehigh, p->modulation);
+}
+
+static void v4l_print_u32(const void *arg, bool write_only)
+{
+ pr_cont("value=%u\n", *(const u32 *)arg);
+}
+
+static void v4l_print_newline(const void *arg, bool write_only)
+{
+ pr_cont("\n");
+}
+
+static void v4l_print_default(const void *arg, bool write_only)
+{
+ pr_cont("driver-specific ioctl\n");
+}
+
+static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
+{
+ __u32 i;
+
+ /* zero the reserved fields */
+ c->reserved[0] = c->reserved[1] = 0;
+ for (i = 0; i < c->count; i++)
+ c->controls[i].reserved2[0] = 0;
+
+ /* V4L2_CID_PRIVATE_BASE cannot be used as control class
+ when using extended controls.
+ Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
+ is it allowed for backwards compatibility.
+ */
+ if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
+ return 0;
+ /* Check that all controls are from the same control class. */
+ for (i = 0; i < c->count; i++) {
+ if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
+ c->error_idx = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int check_fmt(struct file *file, enum v4l2_buf_type type)
+{
+ struct video_device *vfd = video_devdata(file);
+ const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
+ bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI;
+ bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
+
+ if (ops == NULL)
+ return -EINVAL;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (is_vid && is_rx &&
+ (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (is_vid && is_rx && ops->vidioc_g_fmt_vid_cap_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (is_vid && is_rx && ops->vidioc_g_fmt_vid_overlay)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (is_vid && is_tx &&
+ (ops->vidioc_g_fmt_vid_out || ops->vidioc_g_fmt_vid_out_mplane))
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_mplane)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (is_vid && is_tx && ops->vidioc_g_fmt_vid_out_overlay)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (is_vbi && is_rx && ops->vidioc_g_fmt_vbi_cap)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (is_vbi && is_tx && ops->vidioc_g_fmt_vbi_out)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (is_vbi && is_rx && ops->vidioc_g_fmt_sliced_vbi_cap)
+ return 0;
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (is_vbi && is_tx && ops->vidioc_g_fmt_sliced_vbi_out)
+ return 0;
+ break;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_capability *cap = (struct v4l2_capability *)arg;
+
+ cap->version = LINUX_VERSION_CODE;
+ return ops->vidioc_querycap(file, fh, cap);
+}
+
+static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd;
+ u32 *p = arg;
+
+ if (ops->vidioc_g_priority)
+ return ops->vidioc_g_priority(file, fh, arg);
+ vfd = video_devdata(file);
+ *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
+ return 0;
+}
+
+static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd;
+ struct v4l2_fh *vfh;
+ u32 *p = arg;
+
+ if (ops->vidioc_s_priority)
+ return ops->vidioc_s_priority(file, fh, *p);
+ vfd = video_devdata(file);
+ vfh = file->private_data;
+ return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
+}
+
+static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_input *p = arg;
+
+ /*
+ * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific input, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_IN_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_IN_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_IN_CAP_DV_TIMINGS;
+
+ return ops->vidioc_enum_input(file, fh, p);
+}
+
+static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_output *p = arg;
+
+ /*
+ * We set the flags for CAP_PRESETS, CAP_DV_TIMINGS &
+ * CAP_STD here based on ioctl handler provided by the
+ * driver. If the driver doesn't support these
+ * for a specific output, it must override these flags.
+ */
+ if (ops->vidioc_s_std)
+ p->capabilities |= V4L2_OUT_CAP_STD;
+ if (ops->vidioc_s_dv_preset)
+ p->capabilities |= V4L2_OUT_CAP_PRESETS;
+ if (ops->vidioc_s_dv_timings)
+ p->capabilities |= V4L2_OUT_CAP_DV_TIMINGS;
+
+ return ops->vidioc_enum_output(file, fh, p);
+}
+
+static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_fmtdesc *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap))
+ break;
+ return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap_mplane))
+ break;
+ return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_overlay))
+ break;
+ return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out))
+ break;
+ return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out_mplane))
+ break;
+ return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
+ }
+ return -EINVAL;
+}
+
+static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap))
+ break;
+ return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
+ break;
+ return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
+ break;
+ return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
+ break;
+ return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
+ break;
+ return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
+ break;
+ return ops->vidioc_g_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
+ break;
+ return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
+ break;
+ return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
+ break;
+ return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
+ break;
+ return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
+ }
+ return -EINVAL;
+}
+
+static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_s_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
+ }
+ return -EINVAL;
+}
+
+static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_format *p = arg;
+ struct video_device *vfd = video_devdata(file);
+ bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
+ bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
+ bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
+
+ switch (p->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix);
+ return ops->vidioc_try_fmt_vid_out(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.pix_mp);
+ return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.win);
+ return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.vbi);
+ return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
+ break;
+ CLEAR_AFTER_FIELD(p, fmt.sliced);
+ return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
+ }
+ return -EINVAL;
+}
+
+static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_tuner *p = arg;
+ int err;
+
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ err = ops->vidioc_g_tuner(file, fh, p);
+ if (!err)
+ p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+ return err;
+}
+
+static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_tuner *p = arg;
+
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ return ops->vidioc_s_tuner(file, fh, p);
+}
+
+static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_modulator *p = arg;
+ int err;
+
+ err = ops->vidioc_g_modulator(file, fh, p);
+ if (!err)
+ p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
+ return err;
+}
+
+static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency *p = arg;
+
+ p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ return ops->vidioc_g_frequency(file, fh, p);
+}
+
+static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency *p = arg;
+ enum v4l2_tuner_type type;
+
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (p->type != type)
+ return -EINVAL;
+ return ops->vidioc_s_frequency(file, fh, p);
+}
+
+static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_standard *p = arg;
+ v4l2_std_id id = vfd->tvnorms, curr_id = 0;
+ unsigned int index = p->index, i, j = 0;
+ const char *descr = "";
+
+ /* Return -ENODATA if the tvnorms for the current input
+ or output is 0, meaning that it doesn't support this API. */
+ if (id == 0)
+ return -ENODATA;
+
+ /* Return norm array in a canonical way */
+ for (i = 0; i <= index && id; i++) {
+ /* last std value in the standards array is 0, so this
+ while always ends there since (id & 0) == 0. */
+ while ((id & standards[j].std) != standards[j].std)
+ j++;
+ curr_id = standards[j].std;
+ descr = standards[j].descr;
+ j++;
+ if (curr_id == 0)
+ break;
+ if (curr_id != V4L2_STD_PAL &&
+ curr_id != V4L2_STD_SECAM &&
+ curr_id != V4L2_STD_NTSC)
+ id &= ~curr_id;
+ }
+ if (i <= index)
+ return -EINVAL;
+
+ v4l2_video_std_construct(p, curr_id, descr);
+ return 0;
+}
+
+static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *id = arg;
+
+ /* Calls the specific handler */
+ if (ops->vidioc_g_std)
+ return ops->vidioc_g_std(file, fh, arg);
+ if (vfd->current_norm) {
+ *id = vfd->current_norm;
+ return 0;
+ }
+ return -ENOTTY;
+}
+
+static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *id = arg, norm;
+ int ret;
+
+ norm = (*id) & vfd->tvnorms;
+ if (vfd->tvnorms && !norm) /* Check if std is supported */
+ return -EINVAL;
+
+ /* Calls the specific handler */
+ ret = ops->vidioc_s_std(file, fh, &norm);
+
+ /* Updates standard information */
+ if (ret >= 0)
+ vfd->current_norm = norm;
+ return ret;
+}
+
+static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ v4l2_std_id *p = arg;
+
+ /*
+ * If nothing detected, it should return all supported
+ * standard.
+ * Drivers just need to mask the std argument, in order
+ * to remove the standards that don't apply from the mask.
+ * This means that tuners, audio and video decoders can join
+ * their efforts to improve the standards detection.
+ */
+ *p = vfd->tvnorms;
+ return ops->vidioc_querystd(file, fh, arg);
+}
+
+static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_hw_freq_seek *p = arg;
+ enum v4l2_tuner_type type;
+
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ if (p->type != type)
+ return -EINVAL;
+ return ops->vidioc_s_hw_freq_seek(file, fh, p);
+}
+
+static int v4l_overlay(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_overlay(file, fh, *(unsigned int *)arg);
+}
+
+static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_requestbuffers *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ if (ret)
+ return ret;
+
+ CLEAR_AFTER_FIELD(p, memory);
+
+ return ops->vidioc_reqbufs(file, fh, p);
+}
+
+static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ return ret ? ret : ops->vidioc_querybuf(file, fh, p);
+}
+
+static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ return ret ? ret : ops->vidioc_qbuf(file, fh, p);
+}
+
+static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
+}
+
+static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_create_buffers *create = arg;
+ int ret = check_fmt(file, create->format.type);
+
+ return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
+}
+
+static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_buffer *b = arg;
+ int ret = check_fmt(file, b->type);
+
+ return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
+}
+
+static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_streamparm *p = arg;
+ v4l2_std_id std;
+ int ret = check_fmt(file, p->type);
+
+ if (ret)
+ return ret;
+ if (ops->vidioc_g_parm)
+ return ops->vidioc_g_parm(file, fh, p);
+ std = vfd->current_norm;
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+ p->parm.capture.readbuffers = 2;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
+ return ret;
+}
+
+static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_streamparm *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ return ret ? ret : ops->vidioc_s_parm(file, fh, p);
+}
+
+static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_queryctrl *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_queryctrl(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_queryctrl(vfd->ctrl_handler, p);
+ if (ops->vidioc_queryctrl)
+ return ops->vidioc_queryctrl(file, fh, p);
+ return -ENOTTY;
+}
+
+static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_querymenu *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_querymenu(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_querymenu(vfd->ctrl_handler, p);
+ if (ops->vidioc_querymenu)
+ return ops->vidioc_querymenu(file, fh, p);
+ return -ENOTTY;
+}
+
+static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_control *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_g_ctrl(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_g_ctrl(vfd->ctrl_handler, p);
+ if (ops->vidioc_g_ctrl)
+ return ops->vidioc_g_ctrl(file, fh, p);
+ if (ops->vidioc_g_ext_ctrls == NULL)
+ return -ENOTTY;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1)) {
+ int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
+
+ if (ret == 0)
+ p->value = ctrl.value;
+ return ret;
+ }
+ return -EINVAL;
+}
+
+static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_control *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
+ if (ops->vidioc_s_ctrl)
+ return ops->vidioc_s_ctrl(file, fh, p);
+ if (ops->vidioc_s_ext_ctrls == NULL)
+ return -ENOTTY;
+
+ ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ ctrl.id = p->id;
+ ctrl.value = p->value;
+ if (check_ext_ctrls(&ctrls, 1))
+ return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
+ return -EINVAL;
+}
+
+static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
+ if (ops->vidioc_g_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
+
+static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
+ if (ops->vidioc_s_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
+
+static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_ext_controls *p = arg;
+ struct v4l2_fh *vfh =
+ test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
+
+ p->error_idx = p->count;
+ if (vfh && vfh->ctrl_handler)
+ return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+ if (vfd->ctrl_handler)
+ return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
+ if (ops->vidioc_try_ext_ctrls == NULL)
+ return -ENOTTY;
+ return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
+ -EINVAL;
+}
+
+static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_crop *p = arg;
+ struct v4l2_selection s = {
+ .type = p->type,
+ };
+ int ret;
+
+ if (ops->vidioc_g_crop)
+ return ops->vidioc_g_crop(file, fh, p);
+ /* simulate capture crop using selection api */
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+
+ /* copying results to old structure on success */
+ if (!ret)
+ p->c = s.r;
+ return ret;
+}
+
+static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_crop *p = arg;
+ struct v4l2_selection s = {
+ .type = p->type,
+ .r = p->c,
+ };
+
+ if (ops->vidioc_s_crop)
+ return ops->vidioc_s_crop(file, fh, p);
+ /* simulate capture crop using selection api */
+
+ /* crop means compose for output devices */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
+ else
+ s.target = V4L2_SEL_TGT_CROP_ACTIVE;
+
+ return ops->vidioc_s_selection(file, fh, &s);
+}
+
+static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_cropcap *p = arg;
+ struct v4l2_selection s = { .type = p->type };
+ int ret;
+
+ if (ops->vidioc_cropcap)
+ return ops->vidioc_cropcap(file, fh, p);
+
+ /* obtaining bounds */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
+ else
+ s.target = V4L2_SEL_TGT_CROP_BOUNDS;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ return ret;
+ p->bounds = s.r;
+
+ /* obtaining defrect */
+ if (V4L2_TYPE_IS_OUTPUT(p->type))
+ s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
+ else
+ s.target = V4L2_SEL_TGT_CROP_DEFAULT;
+
+ ret = ops->vidioc_g_selection(file, fh, &s);
+ if (ret)
+ return ret;
+ p->defrect = s.r;
+
+ /* setting trivial pixelaspect */
+ p->pixelaspect.numerator = 1;
+ p->pixelaspect.denominator = 1;
+ return 0;
+}
+
+static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ int ret;
+
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================= START STATUS =================\n",
+ vfd->v4l2_dev->name);
+ ret = ops->vidioc_log_status(file, fh);
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================== END STATUS ==================\n",
+ vfd->v4l2_dev->name);
+ return ret;
+}
+
+static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ops->vidioc_g_register(file, fh, p);
+#else
+ return -ENOTTY;
+#endif
+}
+
+static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return ops->vidioc_s_register(file, fh, p);
+#else
+ return -ENOTTY;
+#endif
+}
+
+static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_dbg_chip_ident *p = arg;
+
+ p->ident = V4L2_IDENT_NONE;
+ p->revision = 0;
+ return ops->vidioc_g_chip_ident(file, fh, p);
+}
+
+static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
+}
+
+static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_subscribe_event(fh, arg);
+}
+
+static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ return ops->vidioc_unsubscribe_event(fh, arg);
+}
+
+static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct v4l2_sliced_vbi_cap *p = arg;
+ int ret = check_fmt(file, p->type);
+
+ if (ret)
+ return ret;
+
+ /* Clear up to type, everything after type is zeroed already */
+ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+
+ return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+}
+
+static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *arg)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_frequency_band *p = arg;
+ enum v4l2_tuner_type type;
+ int err;
+
+ type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
+ V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+
+ if (type != p->type)
+ return -EINVAL;
+ if (ops->vidioc_enum_freq_bands)
+ return ops->vidioc_enum_freq_bands(file, fh, p);
+ if (ops->vidioc_g_tuner) {
+ struct v4l2_tuner t = {
+ .index = p->tuner,
+ .type = type,
+ };
+
+ if (p->index)
+ return -EINVAL;
+ err = ops->vidioc_g_tuner(file, fh, &t);
+ if (err)
+ return err;
+ p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+ p->rangelow = t.rangelow;
+ p->rangehigh = t.rangehigh;
+ p->modulation = (type == V4L2_TUNER_RADIO) ?
+ V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+ return 0;
+ }
+ if (ops->vidioc_g_modulator) {
+ struct v4l2_modulator m = {
+ .index = p->tuner,
+ };
+
+ if (type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ if (p->index)
+ return -EINVAL;
+ err = ops->vidioc_g_modulator(file, fh, &m);
+ if (err)
+ return err;
+ p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
+ p->rangelow = m.rangelow;
+ p->rangehigh = m.rangehigh;
+ p->modulation = (type == V4L2_TUNER_RADIO) ?
+ V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
+ return 0;
+ }
+ return -ENOTTY;
+}
+
+struct v4l2_ioctl_info {
+ unsigned int ioctl;
+ u32 flags;
+ const char * const name;
+ union {
+ u32 offset;
+ int (*func)(const struct v4l2_ioctl_ops *ops,
+ struct file *file, void *fh, void *p);
+ } u;
+ void (*debug)(const void *arg, bool write_only);
+};
+
+/* This control needs a priority check */
+#define INFO_FL_PRIO (1 << 0)
+/* This control can be valid if the filehandle passes a control handler. */
+#define INFO_FL_CTRL (1 << 1)
+/* This is a standard ioctl, no need for special code */
+#define INFO_FL_STD (1 << 2)
+/* This is ioctl has its own function */
+#define INFO_FL_FUNC (1 << 3)
+/* Queuing ioctl */
+#define INFO_FL_QUEUE (1 << 4)
+/* Zero struct from after the field to the end */
+#define INFO_FL_CLEAR(v4l2_struct, field) \
+ ((offsetof(struct v4l2_struct, field) + \
+ sizeof(((struct v4l2_struct *)0)->field)) << 16)
+#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
+
+#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
+ [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags | INFO_FL_STD, \
+ .name = #_ioctl, \
+ .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
+ .debug = _debug, \
+ }
+
+#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
+ [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags | INFO_FL_FUNC, \
+ .name = #_ioctl, \
+ .u.func = _func, \
+ .debug = _debug, \
+ }
+
+static struct v4l2_ioctl_info v4l2_ioctls[] = {
+ IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
+ IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
+ IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
+ IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
+ IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
+ IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
+ IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
+ IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
+ IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
+ IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
+ IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
+ IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
+ IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
+ IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
+ IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
+ IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
+ IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
+ IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
+ IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
+ IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
+ IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
+ IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
+ IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
+ IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
+ IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
+ IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
+ IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
+ IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
+ IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
+ IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
+ IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
+ IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
+ IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
+ IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
+ IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
+ IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
+ IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
+ IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
+ IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
+ IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
+ IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
+ IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
+ IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
+ IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
+ IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
+};
+#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+bool v4l2_is_known_ioctl(unsigned int cmd)
+{
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+ return false;
+ return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+}
+
+struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
+{
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+ return vdev->lock;
+ if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
+ return NULL;
+ if (vdev->queue && vdev->queue->lock &&
+ (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
+ return vdev->queue->lock;
+ return vdev->lock;
+}
+
+/* Common ioctl debug function. This function can be used by
+ external ioctl messages as well as internal V4L ioctl */
+void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
+{
+ const char *dir, *type;
+
+ if (prefix)
+ printk(KERN_DEBUG "%s: ", prefix);
+
+ switch (_IOC_TYPE(cmd)) {
+ case 'd':
+ type = "v4l2_int";
+ break;
+ case 'V':
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
+ type = "v4l2";
+ break;
+ }
+ pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
+ return;
+ default:
+ type = "unknown";
+ break;
+ }
+
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE: dir = "--"; break;
+ case _IOC_READ: dir = "r-"; break;
+ case _IOC_WRITE: dir = "-w"; break;
+ case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
+ default: dir = "*ERR*"; break;
+ }
+ pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
+ type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
+}
+EXPORT_SYMBOL(v4l_printk_ioctl);
+
+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;
+ bool write_only = false;
+ struct v4l2_ioctl_info default_info;
+ const struct v4l2_ioctl_info *info;
+ void *fh = file->private_data;
+ struct v4l2_fh *vfh = NULL;
+ int use_fh_prio = 0;
+ int debug = vfd->debug;
+ long ret = -ENOTTY;
+
+ if (ops == NULL) {
+ pr_warn("%s: has no ioctl_ops.\n",
+ video_device_node_name(vfd));
+ return ret;
+ }
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ vfh = file->private_data;
+ use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ }
+
+ if (v4l2_is_known_ioctl(cmd)) {
+ info = &v4l2_ioctls[_IOC_NR(cmd)];
+
+ if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
+ !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+ goto done;
+
+ if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
+ ret = v4l2_prio_check(vfd->prio, vfh->prio);
+ if (ret)
+ goto done;
+ }
+ } else {
+ default_info.ioctl = cmd;
+ default_info.flags = 0;
+ default_info.debug = v4l_print_default;
+ info = &default_info;
+ }
+
+ write_only = _IOC_DIR(cmd) == _IOC_WRITE;
+ if (write_only && debug > V4L2_DEBUG_IOCTL) {
+ v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+ pr_cont(": ");
+ info->debug(arg, write_only);
+ }
+ if (info->flags & INFO_FL_STD) {
+ typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
+ const void *p = vfd->ioctl_ops;
+ const vidioc_op *vidioc = p + info->u.offset;
+
+ ret = (*vidioc)(file, fh, arg);
+ } else if (info->flags & INFO_FL_FUNC) {
+ ret = info->u.func(ops, file, fh, arg);
+ } else if (!ops->vidioc_default) {
+ ret = -ENOTTY;
+ } else {
+ ret = ops->vidioc_default(file, fh,
+ use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+ cmd, arg);
+ }
+
+done:
+ if (debug) {
+ if (write_only && debug > V4L2_DEBUG_IOCTL) {
+ if (ret < 0)
+ printk(KERN_DEBUG "%s: error %ld\n",
+ video_device_node_name(vfd), ret);
+ return ret;
+ }
+ v4l_printk_ioctl(video_device_node_name(vfd), cmd);
+ if (ret < 0)
+ pr_cont(": error %ld\n", ret);
+ else if (debug == V4L2_DEBUG_IOCTL)
+ pr_cont("\n");
+ else if (_IOC_DIR(cmd) == _IOC_NONE)
+ info->debug(arg, write_only);
+ else {
+ pr_cont(": ");
+ info->debug(arg, write_only);
+ }
+ }
+
+ return ret;
+}
+
+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_SUBDEV_G_EDID:
+ case VIDIOC_SUBDEV_S_EDID: {
+ struct v4l2_subdev_edid *edid = parg;
+
+ if (edid->blocks) {
+ *user_ptr = (void __user *)edid->edid;
+ *kernel_ptr = (void *)&edid->edid;
+ *array_size = edid->blocks * 128;
+ 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) {
+ if (ctrls->count > V4L2_CID_MAX_CTRLS) {
+ ret = -EINVAL;
+ break;
+ }
+ *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_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+ v4l2_kioctl func)
+{
+ char sbuf[128];
+ void *mbuf = NULL;
+ void *parg = (void *)arg;
+ long err = -EINVAL;
+ bool has_array_args;
+ size_t array_size = 0;
+ void __user *user_ptr = NULL;
+ void **kernel_ptr = NULL;
+
+ /* Copy arguments into temp kernel buffer */
+ if (_IOC_DIR(cmd) != _IOC_NONE) {
+ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ parg = sbuf;
+ } else {
+ /* too big to allocate from stack */
+ mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+ if (NULL == mbuf)
+ return -ENOMEM;
+ parg = mbuf;
+ }
+
+ err = -EFAULT;
+ if (_IOC_DIR(cmd) & _IOC_WRITE) {
+ unsigned int n = _IOC_SIZE(cmd);
+
+ /*
+ * In some cases, only a few fields are used as input,
+ * i.e. when the app sets "index" and then the driver
+ * fills in the rest of the structure for the thing
+ * with that index. We only need to copy up the first
+ * non-input field.
+ */
+ if (v4l2_is_known_ioctl(cmd)) {
+ u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
+ if (flags & INFO_FL_CLEAR_MASK)
+ n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+ }
+
+ if (copy_from_user(parg, (void __user *)arg, n))
+ goto out;
+
+ /* zero out anything we don't copy from userspace */
+ if (n < _IOC_SIZE(cmd))
+ memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
+ } else {
+ /* read-only ioctl */
+ memset(parg, 0, _IOC_SIZE(cmd));
+ }
+ }
+
+ err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
+ if (err < 0)
+ goto out;
+ has_array_args = err;
+
+ 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 = func(file, cmd, parg);
+ if (err == -ENOIOCTLCMD)
+ err = -ENOTTY;
+
+ if (has_array_args) {
+ *kernel_ptr = user_ptr;
+ if (copy_to_user(user_ptr, mbuf, array_size))
+ err = -EFAULT;
+ goto out_array_args;
+ }
+ /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
+ results that must be returned. */
+ if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
+ goto out;
+
+out_array_args:
+ /* Copy results into user buffer */
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_READ:
+ case (_IOC_WRITE | _IOC_READ):
+ if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+ err = -EFAULT;
+ break;
+ }
+
+out:
+ kfree(mbuf);
+ return err;
+}
+EXPORT_SYMBOL(video_usercopy);
+
+long video_ioctl2(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, __video_do_ioctl);
+}
+EXPORT_SYMBOL(video_ioctl2);
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
new file mode 100644
index 000000000000..3ac83583ad7a
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -0,0 +1,647 @@
+/*
+ * Memory-to-memory device framework for Video for Linux 2 and videobuf.
+ *
+ * Helper functions for devices that use videobuf buffers for both their
+ * source and destination.
+ *
+ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
+ * Pawel Osciak, <pawel@osciak.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <media/videobuf2-core.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
+MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define dprintk(fmt, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
+ } while (0)
+
+
+/* Instance is already queued on the job_queue */
+#define TRANS_QUEUED (1 << 0)
+/* Instance is currently running in hardware */
+#define TRANS_RUNNING (1 << 1)
+
+
+/* Offset base for buffers on the destination queue - used to distinguish
+ * between source and destination buffers when mmapping - they receive the same
+ * offsets but for different queues */
+#define DST_QUEUE_OFF_BASE (1 << 30)
+
+
+/**
+ * struct v4l2_m2m_dev - per-device context
+ * @curr_ctx: currently running instance
+ * @job_queue: instances queued to run
+ * @job_spinlock: protects job_queue
+ * @m2m_ops: driver callbacks
+ */
+struct v4l2_m2m_dev {
+ struct v4l2_m2m_ctx *curr_ctx;
+
+ struct list_head job_queue;
+ spinlock_t job_spinlock;
+
+ struct v4l2_m2m_ops *m2m_ops;
+};
+
+static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &m2m_ctx->out_q_ctx;
+ else
+ return &m2m_ctx->cap_q_ctx;
+}
+
+/**
+ * v4l2_m2m_get_vq() - return vb2_queue for the given type
+ */
+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;
+
+ q_ctx = get_queue_ctx(m2m_ctx, type);
+ if (!q_ctx)
+ return NULL;
+
+ return &q_ctx->q;
+}
+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_queue_ctx *q_ctx)
+{
+ struct v4l2_m2m_buffer *b = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
+
+ b = list_first_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list);
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return &b->vb;
+}
+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_queue_ctx *q_ctx)
+{
+ struct v4l2_m2m_buffer *b = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ if (list_empty(&q_ctx->rdy_queue)) {
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+ return NULL;
+ }
+ b = list_first_entry(&q_ctx->rdy_queue, struct v4l2_m2m_buffer, list);
+ list_del(&b->list);
+ q_ctx->num_rdy--;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+
+ return &b->vb;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
+
+/*
+ * Scheduling handlers
+ */
+
+/**
+ * v4l2_m2m_get_curr_priv() - return driver private data for the currently
+ * running instance or NULL if no instance is running
+ */
+void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
+{
+ unsigned long flags;
+ void *ret = NULL;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (m2m_dev->curr_ctx)
+ ret = m2m_dev->curr_ctx->priv;
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
+
+/**
+ * v4l2_m2m_try_run() - select next job to perform and run it if possible
+ *
+ * Get next transaction (if present) from the waiting jobs list and run it.
+ */
+static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (NULL != m2m_dev->curr_ctx) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Another instance is running, won't run now\n");
+ return;
+ }
+
+ if (list_empty(&m2m_dev->job_queue)) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("No job pending\n");
+ return;
+ }
+
+ m2m_dev->curr_ctx = list_first_entry(&m2m_dev->job_queue,
+ struct v4l2_m2m_ctx, queue);
+ m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+
+ m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
+}
+
+/**
+ * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to
+ * the pending job queue and add it if so.
+ * @m2m_ctx: m2m context assigned to the instance to be checked
+ *
+ * There are three basic requirements an instance has to meet to be able to run:
+ * 1) at least one source buffer has to be queued,
+ * 2) at least one destination buffer has to be queued,
+ * 3) streaming has to be on.
+ *
+ * There may also be additional, custom requirements. In such case the driver
+ * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
+ * return 1 if the instance is ready.
+ * An example of the above could be an instance that requires more than one
+ * src/dst buffer per transaction.
+ */
+static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+ unsigned long flags_job, flags;
+
+ m2m_dev = m2m_ctx->m2m_dev;
+ dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
+
+ if (!m2m_ctx->out_q_ctx.q.streaming
+ || !m2m_ctx->cap_q_ctx.q.streaming) {
+ dprintk("Streaming needs to be on for both queues\n");
+ return;
+ }
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
+ if (m2m_ctx->job_flags & TRANS_QUEUED) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("On job queue already\n");
+ return;
+ }
+
+ 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.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.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.rdy_spinlock, flags);
+
+ if (m2m_dev->m2m_ops->job_ready
+ && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+ dprintk("Driver not ready\n");
+ return;
+ }
+
+ list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
+ m2m_ctx->job_flags |= TRANS_QUEUED;
+
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
+
+ v4l2_m2m_try_run(m2m_dev);
+}
+
+/**
+ * v4l2_m2m_job_finish() - inform the framework that a job has been finished
+ * and have it clean up
+ *
+ * Called by a driver to yield back the device after it has finished with it.
+ * Should be called as soon as possible after reaching a state which allows
+ * other instances to take control of the device.
+ *
+ * This function has to be called only after device_run() callback has been
+ * called on the driver. To prevent recursion, it should not be called directly
+ * from the device_run() callback though.
+ */
+void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
+ struct v4l2_m2m_ctx *m2m_ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("Called by an instance not currently running\n");
+ return;
+ }
+
+ 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);
+
+ /* This instance might have more buffers ready, but since we do not
+ * allow more than one job on the job_queue per instance, each has
+ * to be scheduled separately after the previous one finishes. */
+ v4l2_m2m_try_schedule(m2m_ctx);
+ v4l2_m2m_try_run(m2m_dev);
+}
+EXPORT_SYMBOL(v4l2_m2m_job_finish);
+
+/**
+ * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
+ */
+int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
+ return vb2_reqbufs(vq, reqbufs);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
+
+/**
+ * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
+ *
+ * See v4l2_m2m_mmap() documentation for details.
+ */
+int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+ int ret = 0;
+ unsigned int i;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ 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;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
+
+/**
+ * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ ret = vb2_qbuf(vq, buf);
+ if (!ret)
+ v4l2_m2m_try_schedule(m2m_ctx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
+
+/**
+ * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
+ * the type
+ */
+int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
+ return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
+
+/**
+ * v4l2_m2m_streamon() - turn on streaming for a video queue
+ */
+int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, type);
+ ret = vb2_streamon(vq, type);
+ if (!ret)
+ v4l2_m2m_try_schedule(m2m_ctx);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
+
+/**
+ * v4l2_m2m_streamoff() - turn off streaming for a video queue
+ */
+int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ enum v4l2_buf_type type)
+{
+ struct vb2_queue *vq;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, type);
+ return vb2_streamoff(vq, type);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
+
+/**
+ * v4l2_m2m_poll() - poll replacement, for destination buffers only
+ *
+ * Call from the driver's poll() function. Will poll both queues. If a buffer
+ * is available to dequeue (with dqbuf) from the source queue, this will
+ * indicate that a non-blocking write can be performed, while read will be
+ * returned in case of the destination queue.
+ */
+unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
+ struct poll_table_struct *wait)
+{
+ struct video_device *vfd = video_devdata(file);
+ unsigned long req_events = poll_requested_events(wait);
+ struct vb2_queue *src_q, *dst_q;
+ struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
+ unsigned int rc = 0;
+ unsigned long flags;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ rc = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->wait, wait);
+ if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
+ return rc;
+ }
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
+
+ /*
+ * 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 (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:
+ return rc;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
+
+/**
+ * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
+ *
+ * Call from driver's mmap() function. Will handle mmap() for both queues
+ * seamlessly for videobuffer, which will receive normal per-queue offsets and
+ * proper videobuf queue pointers. The differentiation is made outside videobuf
+ * by adding a predefined offset to buffers from one of the queues and
+ * subtracting it before passing it back to videobuf. Only drivers (and
+ * thus applications) receive modified offsets.
+ */
+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 vb2_queue *vq;
+
+ if (offset < DST_QUEUE_OFF_BASE) {
+ vq = v4l2_m2m_get_src_vq(m2m_ctx);
+ } else {
+ vq = v4l2_m2m_get_dst_vq(m2m_ctx);
+ vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
+ }
+
+ return vb2_mmap(vq, vma);
+}
+EXPORT_SYMBOL(v4l2_m2m_mmap);
+
+/**
+ * v4l2_m2m_init() - initialize per-driver m2m data
+ *
+ * Usually called from driver's probe() function.
+ */
+struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+
+ if (!m2m_ops)
+ return ERR_PTR(-EINVAL);
+
+ BUG_ON(!m2m_ops->device_run);
+ BUG_ON(!m2m_ops->job_abort);
+
+ m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
+ if (!m2m_dev)
+ return ERR_PTR(-ENOMEM);
+
+ m2m_dev->curr_ctx = NULL;
+ m2m_dev->m2m_ops = m2m_ops;
+ INIT_LIST_HEAD(&m2m_dev->job_queue);
+ spin_lock_init(&m2m_dev->job_spinlock);
+
+ return m2m_dev;
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_init);
+
+/**
+ * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
+ *
+ * Usually called from driver's remove() function.
+ */
+void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
+{
+ kfree(m2m_dev);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_release);
+
+/**
+ * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
+ * @priv - driver's instance private data
+ * @m2m_dev - a previously initialized m2m_dev struct
+ * @vq_init - a callback for queue type-specific initialization function to be
+ * used for initializing videobuf_queues
+ *
+ * Usually called from driver's open() function.
+ */
+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;
+ int ret;
+
+ m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
+ if (!m2m_ctx)
+ return ERR_PTR(-ENOMEM);
+
+ m2m_ctx->priv = drv_priv;
+ m2m_ctx->m2m_dev = m2m_dev;
+ init_waitqueue_head(&m2m_ctx->finished);
+
+ 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);
+
+ 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);
+
+/**
+ * v4l2_m2m_ctx_release() - release m2m context
+ *
+ * Usually called from driver's release() function.
+ */
+void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
+{
+ struct v4l2_m2m_dev *m2m_dev;
+ unsigned long flags;
+
+ m2m_dev = m2m_ctx->m2m_dev;
+
+ spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
+ if (m2m_ctx->job_flags & TRANS_RUNNING) {
+ 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);
+ 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);
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ dprintk("m2m_ctx: %p had been on queue and was removed\n",
+ m2m_ctx);
+ } else {
+ /* Do nothing, was not on queue/running */
+ spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
+ }
+
+ vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
+ vb2_queue_release(&m2m_ctx->out_q_ctx.q);
+
+ kfree(m2m_ctx);
+}
+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.
+ */
+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, vb->vb2_queue->type);
+ if (!q_ctx)
+ return;
+
+ spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
+ list_add_tail(&b->list, &q_ctx->rdy_queue);
+ q_ctx->num_rdy++;
+ spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
+
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
new file mode 100644
index 000000000000..dced41c1d993
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -0,0 +1,476 @@
+/*
+ * V4L2 sub-device
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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/ioctl.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/export.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
+
+static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL);
+ if (fh->pad == NULL)
+ return -ENOMEM;
+#endif
+ return 0;
+}
+
+static void subdev_fh_free(struct v4l2_subdev_fh *fh)
+{
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ kfree(fh->pad);
+ fh->pad = NULL;
+#endif
+}
+
+static int subdev_open(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_subdev_fh *subdev_fh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ struct media_entity *entity = NULL;
+#endif
+ int ret;
+
+ subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
+ if (subdev_fh == NULL)
+ return -ENOMEM;
+
+ ret = subdev_fh_init(subdev_fh, sd);
+ if (ret) {
+ kfree(subdev_fh);
+ return ret;
+ }
+
+ v4l2_fh_init(&subdev_fh->vfh, vdev);
+ v4l2_fh_add(&subdev_fh->vfh);
+ file->private_data = &subdev_fh->vfh;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (sd->v4l2_dev->mdev) {
+ entity = media_entity_get(&sd->entity);
+ if (!entity) {
+ ret = -EBUSY;
+ goto err;
+ }
+ }
+#endif
+
+ if (sd->internal_ops && sd->internal_ops->open) {
+ ret = sd->internal_ops->open(sd, subdev_fh);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (entity)
+ media_entity_put(entity);
+#endif
+ v4l2_fh_del(&subdev_fh->vfh);
+ v4l2_fh_exit(&subdev_fh->vfh);
+ subdev_fh_free(subdev_fh);
+ kfree(subdev_fh);
+
+ return ret;
+}
+
+static int subdev_close(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+
+ if (sd->internal_ops && sd->internal_ops->close)
+ sd->internal_ops->close(sd, subdev_fh);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (sd->v4l2_dev->mdev)
+ media_entity_put(&sd->entity);
+#endif
+ v4l2_fh_del(vfh);
+ v4l2_fh_exit(vfh);
+ subdev_fh_free(subdev_fh);
+ kfree(subdev_fh);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *vfh = file->private_data;
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
+#endif
+
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ return v4l2_queryctrl(vfh->ctrl_handler, arg);
+
+ case VIDIOC_QUERYMENU:
+ return v4l2_querymenu(vfh->ctrl_handler, arg);
+
+ case VIDIOC_G_CTRL:
+ return v4l2_g_ctrl(vfh->ctrl_handler, arg);
+
+ case VIDIOC_S_CTRL:
+ return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
+
+ case VIDIOC_G_EXT_CTRLS:
+ return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
+
+ case VIDIOC_S_EXT_CTRLS:
+ return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
+
+ case VIDIOC_DQEVENT:
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return -ENOIOCTLCMD;
+
+ return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_SUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, g_register, p);
+ }
+ case VIDIOC_DBG_S_REGISTER:
+ {
+ struct v4l2_dbg_register *p = arg;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return v4l2_subdev_call(sd, core, s_register, p);
+ }
+#endif
+
+ case VIDIOC_LOG_STATUS: {
+ int ret;
+
+ pr_info("%s: ================= START STATUS =================\n",
+ sd->name);
+ ret = v4l2_subdev_call(sd, core, log_status);
+ pr_info("%s: ================== END STATUS ==================\n",
+ sd->name);
+ return ret;
+ }
+
+#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
+ case VIDIOC_SUBDEV_G_FMT: {
+ struct v4l2_subdev_format *format = arg;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+ format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (format->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
+ }
+
+ case VIDIOC_SUBDEV_S_FMT: {
+ struct v4l2_subdev_format *format = arg;
+
+ if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
+ format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (format->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
+ }
+
+ case VIDIOC_SUBDEV_G_CROP: {
+ struct v4l2_subdev_crop *crop = arg;
+ struct v4l2_subdev_selection sel;
+ int rval;
+
+ if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (crop->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = crop->which;
+ sel.pad = crop->pad;
+ sel.target = V4L2_SEL_TGT_CROP;
+
+ rval = v4l2_subdev_call(
+ sd, pad, get_selection, subdev_fh, &sel);
+
+ crop->rect = sel.r;
+
+ return rval;
+ }
+
+ case VIDIOC_SUBDEV_S_CROP: {
+ struct v4l2_subdev_crop *crop = arg;
+ struct v4l2_subdev_selection sel;
+ int rval;
+
+ if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
+ crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (crop->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = crop->which;
+ sel.pad = crop->pad;
+ sel.target = V4L2_SEL_TGT_CROP;
+ sel.r = crop->rect;
+
+ rval = v4l2_subdev_call(
+ sd, pad, set_selection, subdev_fh, &sel);
+
+ crop->rect = sel.r;
+
+ return rval;
+ }
+
+ case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
+ struct v4l2_subdev_mbus_code_enum *code = arg;
+
+ if (code->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
+ code);
+ }
+
+ case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
+ struct v4l2_subdev_frame_size_enum *fse = arg;
+
+ if (fse->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
+ fse);
+ }
+
+ case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
+ return v4l2_subdev_call(sd, video, g_frame_interval, arg);
+
+ case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
+ return v4l2_subdev_call(sd, video, s_frame_interval, arg);
+
+ case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
+ struct v4l2_subdev_frame_interval_enum *fie = arg;
+
+ if (fie->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
+ fie);
+ }
+
+ case VIDIOC_SUBDEV_G_SELECTION: {
+ struct v4l2_subdev_selection *sel = arg;
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+ sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (sel->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(
+ sd, pad, get_selection, subdev_fh, sel);
+ }
+
+ case VIDIOC_SUBDEV_S_SELECTION: {
+ struct v4l2_subdev_selection *sel = arg;
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+ sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (sel->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(
+ sd, pad, set_selection, subdev_fh, sel);
+ }
+
+ case VIDIOC_SUBDEV_G_EDID:
+ return v4l2_subdev_call(sd, pad, get_edid, arg);
+
+ case VIDIOC_SUBDEV_S_EDID:
+ return v4l2_subdev_call(sd, pad, set_edid, arg);
+#endif
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+
+ return 0;
+}
+
+static long subdev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, subdev_do_ioctl);
+}
+
+static unsigned int subdev_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+ struct v4l2_fh *fh = file->private_data;
+
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
+ return POLLERR;
+
+ poll_wait(file, &fh->wait, wait);
+
+ if (v4l2_event_pending(fh))
+ return POLLPRI;
+
+ return 0;
+}
+
+const struct v4l2_file_operations v4l2_subdev_fops = {
+ .owner = THIS_MODULE,
+ .open = subdev_open,
+ .unlocked_ioctl = subdev_ioctl,
+ .release = subdev_close,
+ .poll = subdev_poll,
+};
+
+#ifdef CONFIG_MEDIA_CONTROLLER
+int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ if (source_fmt->format.width != sink_fmt->format.width
+ || source_fmt->format.height != sink_fmt->format.height
+ || source_fmt->format.code != sink_fmt->format.code)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
+
+static int
+v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+ struct v4l2_subdev_format *fmt)
+{
+ switch (media_entity_type(pad->entity)) {
+ case MEDIA_ENT_T_V4L2_SUBDEV:
+ fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt->pad = pad->index;
+ return v4l2_subdev_call(media_entity_to_v4l2_subdev(
+ pad->entity),
+ pad, get_fmt, NULL, fmt);
+ default:
+ WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
+ media_entity_type(pad->entity), pad->entity->name);
+ /* Fall through */
+ case MEDIA_ENT_T_DEVNODE_V4L:
+ return -EINVAL;
+ }
+}
+
+int v4l2_subdev_link_validate(struct media_link *link)
+{
+ struct v4l2_subdev *sink;
+ struct v4l2_subdev_format sink_fmt, source_fmt;
+ int rval;
+
+ rval = v4l2_subdev_link_validate_get_format(
+ link->source, &source_fmt);
+ if (rval < 0)
+ return 0;
+
+ rval = v4l2_subdev_link_validate_get_format(
+ link->sink, &sink_fmt);
+ if (rval < 0)
+ return 0;
+
+ sink = media_entity_to_v4l2_subdev(link->sink->entity);
+
+ rval = v4l2_subdev_call(sink, pad, link_validate, link,
+ &source_fmt, &sink_fmt);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ return v4l2_subdev_link_validate_default(
+ sink, link, &source_fmt, &sink_fmt);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
+void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
+{
+ INIT_LIST_HEAD(&sd->list);
+ BUG_ON(!ops);
+ sd->ops = ops;
+ sd->v4l2_dev = NULL;
+ sd->flags = 0;
+ sd->name[0] = '\0';
+ sd->grp_id = 0;
+ sd->dev_priv = NULL;
+ sd->host_priv = NULL;
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.name = sd->name;
+ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+#endif
+}
+EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/v4l2-core/videobuf-core.c
index bf7a326b1cdc..bf7a326b1cdc 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/v4l2-core/videobuf-core.c
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/v4l2-core/videobuf-dma-contig.c
index 3a43ba0959bf..3a43ba0959bf 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf-dma-contig.c
diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c
index f300deafd268..f300deafd268 100644
--- a/drivers/media/video/videobuf-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf-dma-sg.c
diff --git a/drivers/media/v4l2-core/videobuf-dvb.c b/drivers/media/v4l2-core/videobuf-dvb.c
new file mode 100644
index 000000000000..b7efa4516d36
--- /dev/null
+++ b/drivers/media/v4l2-core/videobuf-dvb.c
@@ -0,0 +1,398 @@
+/*
+ *
+ * some helper function for simple DVB cards which simply DMA the
+ * complete transport stream and let the computer sort everything else
+ * (i.e. we are using the software demux, ...). Also uses the
+ * video-buf to manage DMA buffers.
+ *
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+
+#include <linux/freezer.h>
+
+#include <media/videobuf-core.h>
+#include <media/videobuf-dvb.h>
+
+/* ------------------------------------------------------------------ */
+
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages");
+
+#define dprintk(fmt, arg...) if (debug) \
+ printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int videobuf_dvb_thread(void *data)
+{
+ struct videobuf_dvb *dvb = data;
+ struct videobuf_buffer *buf;
+ unsigned long flags;
+ void *outp;
+
+ dprintk("dvb thread started\n");
+ set_freezable();
+ videobuf_read_start(&dvb->dvbq);
+
+ for (;;) {
+ /* fetch next buffer */
+ buf = list_entry(dvb->dvbq.stream.next,
+ struct videobuf_buffer, stream);
+ list_del(&buf->stream);
+ videobuf_waiton(&dvb->dvbq, buf, 0, 1);
+
+ /* no more feeds left or stop_feed() asked us to quit */
+ if (0 == dvb->nfeeds)
+ break;
+ if (kthread_should_stop())
+ break;
+ try_to_freeze();
+
+ /* feed buffer data to demux */
+ outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
+
+ if (buf->state == VIDEOBUF_DONE)
+ dvb_dmx_swfilter(&dvb->demux, outp,
+ buf->size);
+
+ /* requeue buffer */
+ list_add_tail(&buf->stream,&dvb->dvbq.stream);
+ spin_lock_irqsave(dvb->dvbq.irqlock,flags);
+ dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
+ spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
+ }
+
+ videobuf_read_stop(&dvb->dvbq);
+ dprintk("dvb thread stopped\n");
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return 0;
+}
+
+static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct videobuf_dvb *dvb = demux->priv;
+ int rc;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds++;
+ rc = dvb->nfeeds;
+
+ if (NULL != dvb->thread)
+ goto out;
+ dvb->thread = kthread_run(videobuf_dvb_thread,
+ dvb, "%s dvb", dvb->name);
+ if (IS_ERR(dvb->thread)) {
+ rc = PTR_ERR(dvb->thread);
+ dvb->thread = NULL;
+ }
+
+out:
+ mutex_unlock(&dvb->lock);
+ return rc;
+}
+
+static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct videobuf_dvb *dvb = demux->priv;
+ int err = 0;
+
+ mutex_lock(&dvb->lock);
+ dvb->nfeeds--;
+ if (0 == dvb->nfeeds && NULL != dvb->thread) {
+ err = kthread_stop(dvb->thread);
+ dvb->thread = NULL;
+ }
+ mutex_unlock(&dvb->lock);
+ return err;
+}
+
+static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ char *adapter_name,
+ short *adapter_nr,
+ int mfe_shared)
+{
+ int result;
+
+ mutex_init(&fe->lock);
+
+ /* register adapter */
+ result = dvb_register_adapter(&fe->adapter, adapter_name, module,
+ device, adapter_nr);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ adapter_name, result);
+ }
+ fe->adapter.priv = adapter_priv;
+ fe->adapter.mfe_shared = mfe_shared;
+
+ return result;
+}
+
+static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
+ struct videobuf_dvb *dvb)
+{
+ int result;
+
+ /* register frontend */
+ result = dvb_register_frontend(adapter, dvb->frontend);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_frontend;
+ }
+
+ /* register demux stuff */
+ dvb->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dvb->demux.priv = dvb;
+ dvb->demux.filternum = 256;
+ dvb->demux.feednum = 256;
+ dvb->demux.start_feed = videobuf_dvb_start_feed;
+ dvb->demux.stop_feed = videobuf_dvb_stop_feed;
+ result = dvb_dmx_init(&dvb->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmx;
+ }
+
+ dvb->dmxdev.filternum = 256;
+ dvb->dmxdev.demux = &dvb->demux.dmx;
+ dvb->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
+
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_dmxdev;
+ }
+
+ dvb->fe_hw.source = DMX_FRONTEND_0;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_hw;
+ }
+
+ dvb->fe_mem.source = DMX_MEMORY_FE;
+ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_mem;
+ }
+
+ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+
+ /* register network adapter */
+ result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
+ dvb->name, result);
+ goto fail_fe_conn;
+ }
+ return 0;
+
+fail_fe_conn:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
+fail_fe_mem:
+ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
+fail_fe_hw:
+ dvb_dmxdev_release(&dvb->dmxdev);
+fail_dmxdev:
+ dvb_dmx_release(&dvb->demux);
+fail_dmx:
+ dvb_unregister_frontend(dvb->frontend);
+fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
+ dvb->frontend = NULL;
+
+ return result;
+}
+
+/* ------------------------------------------------------------------ */
+/* Register a single adapter and one or more frontends */
+int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
+ struct module *module,
+ void *adapter_priv,
+ struct device *device,
+ short *adapter_nr,
+ int mfe_shared)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe;
+ int res;
+
+ fe = videobuf_dvb_get_frontend(f, 1);
+ if (!fe) {
+ printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
+ return -EINVAL;
+ }
+
+ /* Bring up the adapter */
+ res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
+ fe->dvb.name, adapter_nr, mfe_shared);
+ if (res < 0) {
+ printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
+ return res;
+ }
+
+ /* Attach all of the frontends to the adapter */
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
+ if (res < 0) {
+ printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
+ fe->dvb.name, res);
+ goto err;
+ }
+ }
+ mutex_unlock(&f->lock);
+ return 0;
+
+err:
+ mutex_unlock(&f->lock);
+ videobuf_dvb_unregister_bus(f);
+ return res;
+}
+EXPORT_SYMBOL(videobuf_dvb_register_bus);
+
+void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
+{
+ videobuf_dvb_dealloc_frontends(f);
+
+ dvb_unregister_adapter(&f->adapter);
+}
+EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
+
+struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
+ struct videobuf_dvb_frontends *f, int id)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe, *ret = NULL;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->id == id) {
+ ret = fe;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_get_frontend);
+
+int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
+ struct dvb_frontend *p)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe = NULL;
+ int ret = 0;
+
+ mutex_lock(&f->lock);
+
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->dvb.frontend == p) {
+ ret = fe->id;
+ break;
+ }
+ }
+
+ mutex_unlock(&f->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(videobuf_dvb_find_frontend);
+
+struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
+ struct videobuf_dvb_frontends *f, int id)
+{
+ struct videobuf_dvb_frontend *fe;
+
+ fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
+ if (fe == NULL)
+ goto fail_alloc;
+
+ fe->id = id;
+ mutex_init(&fe->dvb.lock);
+
+ mutex_lock(&f->lock);
+ list_add_tail(&fe->felist, &f->felist);
+ mutex_unlock(&f->lock);
+
+fail_alloc:
+ return fe;
+}
+EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
+
+void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
+{
+ struct list_head *list, *q;
+ struct videobuf_dvb_frontend *fe;
+
+ mutex_lock(&f->lock);
+ list_for_each_safe(list, q, &f->felist) {
+ fe = list_entry(list, struct videobuf_dvb_frontend, felist);
+ if (fe->dvb.net.dvbdev) {
+ dvb_net_release(&fe->dvb.net);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_mem);
+ fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
+ &fe->dvb.fe_hw);
+ dvb_dmxdev_release(&fe->dvb.dmxdev);
+ dvb_dmx_release(&fe->dvb.demux);
+ dvb_unregister_frontend(fe->dvb.frontend);
+ }
+ if (fe->dvb.frontend)
+ /* always allocated, may have been reset */
+ dvb_frontend_detach(fe->dvb.frontend);
+ list_del(list); /* remove list entry */
+ kfree(fe); /* free frontend allocation */
+ }
+ mutex_unlock(&f->lock);
+}
+EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);
diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/v4l2-core/videobuf-vmalloc.c
index df142580e44c..df142580e44c 100644
--- a/drivers/media/video/videobuf-vmalloc.c
+++ b/drivers/media/v4l2-core/videobuf-vmalloc.c
diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c
new file mode 100644
index 000000000000..e6a26b433e87
--- /dev/null
+++ b/drivers/media/v4l2-core/videobuf2-core.c
@@ -0,0 +1,2383 @@
+/*
+ * videobuf2-core.c - V4L2 driver helper framework
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.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/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.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, 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)
+
+#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
+ V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
+ V4L2_BUF_FLAG_PREPARED)
+
+/**
+ * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
+ */
+static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
+{
+ 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, alloc, q->alloc_ctx[plane],
+ q->plane_sizes[plane]);
+ if (IS_ERR_OR_NULL(mem_priv))
+ goto free;
+
+ /* Associate allocator private data with this plane */
+ vb->planes[plane].mem_priv = mem_priv;
+ vb->v4l2_planes[plane].length = q->plane_sizes[plane];
+ }
+
+ return 0;
+free:
+ /* Free already allocated memory if one of the allocations failed */
+ for (; plane > 0; --plane) {
+ call_memop(q, put, vb->planes[plane - 1].mem_priv);
+ vb->planes[plane - 1].mem_priv = NULL;
+ }
+
+ 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, 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) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].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 n)
+{
+ unsigned int buffer, plane;
+ struct vb2_buffer *vb;
+ unsigned long off;
+
+ if (q->num_buffers) {
+ struct v4l2_plane *p;
+ vb = q->bufs[q->num_buffers - 1];
+ p = &vb->v4l2_planes[vb->num_planes - 1];
+ off = PAGE_ALIGN(p->m.mem_offset + p->length);
+ } else {
+ off = 0;
+ }
+
+ for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
+ vb = q->bufs[buffer];
+ if (!vb)
+ continue;
+
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ vb->v4l2_planes[plane].length = q->plane_sizes[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 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 = q->num_buffers + 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);
+ 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[q->num_buffers + buffer] = vb;
+ }
+
+ __setup_offsets(q, buffer);
+
+ dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
+ buffer, 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 buffers)
+{
+ unsigned int buffer;
+ struct vb2_buffer *vb;
+
+ for (buffer = q->num_buffers - buffers; 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 buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been freed.
+ */
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
+{
+ unsigned int buffer;
+
+ /* Call driver-provided cleanup function for each buffer, if provided */
+ if (q->ops->buf_cleanup) {
+ for (buffer = q->num_buffers - buffers; 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, buffers);
+
+ /* Free videobuf buffers */
+ for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+ ++buffer) {
+ kfree(q->bufs[buffer]);
+ q->bufs[buffer] = NULL;
+ }
+
+ q->num_buffers -= buffers;
+ if (!q->num_buffers)
+ q->memory = 0;
+ INIT_LIST_HEAD(&q->queued_list);
+}
+
+/**
+ * __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, const 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;
+}
+
+/**
+ * __buffer_in_use() - return true if the buffer is in use and
+ * the queue cannot be freed (by the means of REQBUFS(0)) call
+ */
+static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
+{
+ unsigned int plane;
+ for (plane = 0; plane < vb->num_planes; ++plane) {
+ void *mem_priv = vb->planes[plane].mem_priv;
+ /*
+ * 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 (mem_priv && call_memop(q, num_users, mem_priv) > 1)
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __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;
+ for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+ if (__buffer_in_use(q, q->bufs[buffer]))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * __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;
+
+ /* Copy back data such as timestamp, flags, etc. */
+ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
+ b->reserved2 = vb->v4l2_buf.reserved2;
+ 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;
+ }
+
+ /*
+ * Clear any buffer state related flags.
+ */
+ b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
+
+ 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_PREPARED:
+ b->flags |= V4L2_BUF_FLAG_PREPARED;
+ break;
+ case VB2_BUF_STATE_DEQUEUED:
+ /* nothing */
+ break;
+ }
+
+ if (__buffer_in_use(q, vb))
+ b->flags |= V4L2_BUF_FLAG_MAPPED;
+
+ return 0;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * __verify_memory_type() - Check whether the memory type and buffer type
+ * passed to a buffer operation are compatible with the queue.
+ */
+static int __verify_memory_type(struct vb2_queue *q,
+ enum v4l2_memory memory, enum v4l2_buf_type type)
+{
+ if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
+ dprintk(1, "reqbufs: unsupported memory type\n");
+ return -EINVAL;
+ }
+
+ if (type != q->type) {
+ dprintk(1, "reqbufs: requested type is incorrect\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure all the required memory ops for given memory type
+ * are available.
+ */
+ if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
+ dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
+ dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Place the busy tests at the end: -EBUSY can be ignored when
+ * create_bufs is called with count == 0, but count == 0 should still
+ * do the memory and type validation.
+ */
+ if (q->fileio) {
+ dprintk(1, "reqbufs: file io in progress\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/**
+ * __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.
+ */
+static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ unsigned int num_buffers, allocated_buffers, num_planes = 0;
+ int ret;
+
+ if (q->streaming) {
+ dprintk(1, "reqbufs: streaming active\n");
+ return -EBUSY;
+ }
+
+ if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
+ /*
+ * 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;
+ }
+
+ __vb2_queue_free(q, q->num_buffers);
+
+ /*
+ * In case of REQBUFS(0) return immediately without calling
+ * driver's queue_setup() callback and allocating resources.
+ */
+ if (req->count == 0)
+ return 0;
+ }
+
+ /*
+ * Make sure the requested values and current defaults are sane.
+ */
+ num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = req->memory;
+
+ /*
+ * 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, NULL, &num_buffers, &num_planes,
+ q->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);
+ if (ret == 0) {
+ dprintk(1, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the allocated number of buffers.
+ */
+ if (allocated_buffers < num_buffers) {
+ num_buffers = allocated_buffers;
+
+ ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers = allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return ret;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ req->count = allocated_buffers;
+
+ return 0;
+}
+
+/**
+ * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
+ * type values.
+ * @q: videobuf2 queue
+ * @req: struct passed from userspace to vidioc_reqbufs handler in driver
+ */
+int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
+{
+ int ret = __verify_memory_type(q, req->memory, req->type);
+
+ return ret ? ret : __reqbufs(q, req);
+}
+EXPORT_SYMBOL_GPL(vb2_reqbufs);
+
+/**
+ * __create_bufs() - Allocate buffers and any required auxiliary structs
+ * @q: videobuf2 queue
+ * @create: creation parameters, passed from userspace to vidioc_create_bufs
+ * handler in driver
+ *
+ * Should be called from vidioc_create_bufs ioctl handler of a driver.
+ * This function:
+ * 1) verifies parameter sanity
+ * 2) calls the .queue_setup() queue operation
+ * 3) performs any necessary memory allocations
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_create_bufs handler in driver.
+ */
+static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ unsigned int num_planes = 0, num_buffers, allocated_buffers;
+ int ret;
+
+ if (q->num_buffers == VIDEO_MAX_FRAME) {
+ dprintk(1, "%s(): maximum number of buffers already allocated\n",
+ __func__);
+ return -ENOBUFS;
+ }
+
+ if (!q->num_buffers) {
+ memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
+ memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
+ q->memory = create->memory;
+ }
+
+ num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
+
+ /*
+ * Ask the driver, whether the requested number of buffers, planes per
+ * buffer and their sizes are acceptable
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+ if (ret)
+ return ret;
+
+ /* Finally, allocate buffers and video memory */
+ ret = __vb2_queue_alloc(q, create->memory, num_buffers,
+ num_planes);
+ if (ret == 0) {
+ dprintk(1, "Memory allocation failed\n");
+ return -ENOMEM;
+ }
+
+ allocated_buffers = ret;
+
+ /*
+ * Check if driver can handle the so far allocated number of buffers.
+ */
+ if (ret < num_buffers) {
+ num_buffers = ret;
+
+ /*
+ * q->num_buffers contains the total number of buffers, that the
+ * queue driver has set up
+ */
+ ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
+ &num_planes, q->plane_sizes, q->alloc_ctx);
+
+ if (!ret && allocated_buffers < num_buffers)
+ ret = -ENOMEM;
+
+ /*
+ * Either the driver has accepted a smaller number of buffers,
+ * or .queue_setup() returned an error
+ */
+ }
+
+ q->num_buffers += allocated_buffers;
+
+ if (ret < 0) {
+ __vb2_queue_free(q, allocated_buffers);
+ return -ENOMEM;
+ }
+
+ /*
+ * Return the number of successfully allocated buffers
+ * to the userspace.
+ */
+ create->count = allocated_buffers;
+
+ return 0;
+}
+
+/**
+ * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
+ * memory and type values.
+ * @q: videobuf2 queue
+ * @create: creation parameters, passed from userspace to vidioc_create_bufs
+ * handler in driver
+ */
+int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
+{
+ int ret = __verify_memory_type(q, create->memory, create->format.type);
+
+ create->index = q->num_buffers;
+ if (create->count == 0)
+ return ret != -EBUSY ? ret : 0;
+ return ret ? ret : __create_bufs(q, create);
+}
+EXPORT_SYMBOL_GPL(vb2_create_bufs);
+
+/**
+ * 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 || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_memop(q, 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 || !vb->planes[plane_no].mem_priv)
+ return NULL;
+
+ return call_memop(q, 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, const 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;
+ vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
+
+ return 0;
+}
+
+/**
+ * __qbuf_userptr() - handle qbuf of a USERPTR buffer
+ */
+static int __qbuf_userptr(struct vb2_buffer *vb, const 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 &&
+ 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);
+
+ /* Check if the provided plane buffer is large enough */
+ if (planes[plane].length < q->plane_sizes[plane]) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Release previously acquired memory if present */
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
+
+ /* Acquire each plane's memory */
+ mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
+ planes[plane].m.userptr,
+ planes[plane].length, write);
+ if (IS_ERR_OR_NULL(mem_priv)) {
+ dprintk(1, "qbuf: failed acquiring userspace "
+ "memory for plane %d\n", plane);
+ ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
+ 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 < vb->num_planes; ++plane) {
+ if (vb->planes[plane].mem_priv)
+ call_memop(q, put_userptr, vb->planes[plane].mem_priv);
+ vb->planes[plane].mem_priv = NULL;
+ vb->v4l2_planes[plane].m.userptr = 0;
+ vb->v4l2_planes[plane].length = 0;
+ }
+
+ return ret;
+}
+
+/**
+ * __qbuf_mmap() - handle qbuf of an MMAP buffer
+ */
+static int __qbuf_mmap(struct vb2_buffer *vb, const 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);
+}
+
+static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
+{
+ struct vb2_queue *q = vb->vb2_queue;
+ int ret;
+
+ switch (q->memory) {
+ case V4L2_MEMORY_MMAP:
+ ret = __qbuf_mmap(vb, b);
+ break;
+ case V4L2_MEMORY_USERPTR:
+ ret = __qbuf_userptr(vb, b);
+ break;
+ default:
+ WARN(1, "Invalid queue type\n");
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ ret = call_qop(q, buf_prepare, vb);
+ if (ret)
+ dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
+ else
+ vb->state = VB2_BUF_STATE_PREPARED;
+
+ return ret;
+}
+
+/**
+ * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
+ * @q: videobuf2 queue
+ * @b: buffer structure passed from userspace to vidioc_prepare_buf
+ * handler in driver
+ *
+ * Should be called from vidioc_prepare_buf 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,
+ *
+ * The return values from this function are intended to be directly returned
+ * from vidioc_prepare_buf handler in driver.
+ */
+int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
+{
+ struct vb2_buffer *vb;
+ int ret;
+
+ if (q->fileio) {
+ dprintk(1, "%s(): file io in progress\n", __func__);
+ return -EBUSY;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "%s(): invalid buffer type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "%s(): buffer index out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "%s(): buffer is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "%s(): invalid memory type\n", __func__);
+ return -EINVAL;
+ }
+
+ if (vb->state != VB2_BUF_STATE_DEQUEUED) {
+ dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
+ return -EINVAL;
+ }
+
+ ret = __buf_prepare(vb, b);
+ if (ret < 0)
+ return ret;
+
+ __fill_v4l2_buffer(vb, b);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_prepare_buf);
+
+/**
+ * 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) if necessary, 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 rw_semaphore *mmap_sem = NULL;
+ struct vb2_buffer *vb;
+ int ret = 0;
+
+ /*
+ * In case of user pointer buffers vb2 allocator needs to get direct
+ * access to userspace pages. This requires getting read access on
+ * mmap semaphore in the current process structure. The same
+ * semaphore is taken before calling mmap operation, while both mmap
+ * and qbuf are called by the driver or v4l2 core with driver's lock
+ * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
+ * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
+ * release driver's lock, takes mmap_sem and then takes again driver's
+ * lock.
+ *
+ * To avoid race with other vb2 calls, which might be called after
+ * releasing driver's lock, this operation is performed at the
+ * beggining of qbuf processing. This way the queue status is
+ * consistent after getting driver's lock back.
+ */
+ if (q->memory == V4L2_MEMORY_USERPTR) {
+ mmap_sem = &current->mm->mmap_sem;
+ call_qop(q, wait_prepare, q);
+ down_read(mmap_sem);
+ call_qop(q, wait_finish, q);
+ }
+
+ if (q->fileio) {
+ dprintk(1, "qbuf: file io in progress\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (b->type != q->type) {
+ dprintk(1, "qbuf: invalid buffer type\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (b->index >= q->num_buffers) {
+ dprintk(1, "qbuf: buffer index out of range\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ vb = q->bufs[b->index];
+ if (NULL == vb) {
+ /* Should never happen */
+ dprintk(1, "qbuf: buffer is NULL\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (b->memory != q->memory) {
+ dprintk(1, "qbuf: invalid memory type\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ switch (vb->state) {
+ case VB2_BUF_STATE_DEQUEUED:
+ ret = __buf_prepare(vb, b);
+ if (ret)
+ goto unlock;
+ case VB2_BUF_STATE_PREPARED:
+ break;
+ default:
+ dprintk(1, "qbuf: buffer already in use\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ /*
+ * 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);
+
+ /* Fill buffer information for the userspace */
+ __fill_v4l2_buffer(vb, b);
+
+ dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
+unlock:
+ if (mmap_sem)
+ up_read(mmap_sem);
+ return ret;
+}
+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_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);
+ atomic_set(&q->queued_count, 0);
+ 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_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) passes any previously queued buffers to the driver and starts streaming
+ *
+ * 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;
+ int ret;
+
+ 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;
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Let driver notice that streaming state has been enabled.
+ */
+ ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
+ if (ret) {
+ dprintk(1, "streamon: driver refused to start streaming\n");
+ __vb2_queue_cancel(q);
+ return ret;
+ }
+
+ q->streaming = 1;
+
+ dprintk(3, "Streamon successful\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_streamon);
+
+
+/**
+ * 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_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];
+
+ ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
+ if (ret)
+ return ret;
+
+ dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vb2_mmap);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ unsigned long off = pgoff << PAGE_SHIFT;
+ 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;
+ }
+
+ /*
+ * 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];
+
+ return (unsigned long)vb2_plane_vaddr(vb, plane);
+}
+EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
+#endif
+
+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.
+ *
+ * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any
+ * pending events.
+ *
+ * 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)
+{
+ struct video_device *vfd = video_devdata(file);
+ unsigned long req_events = poll_requested_events(wait);
+ struct vb2_buffer *vb = NULL;
+ unsigned int res = 0;
+ unsigned long flags;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ res = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->wait, wait);
+ }
+
+ /*
+ * Start file I/O emulator only 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) &&
+ (req_events & (POLLIN | POLLRDNORM))) {
+ if (__vb2_init_fileio(q, 1))
+ return res | POLLERR;
+ }
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+ (req_events & (POLLOUT | POLLWRNORM))) {
+ if (__vb2_init_fileio(q, 0))
+ return res | POLLERR;
+ /*
+ * Write to OUTPUT queue can be done immediately.
+ */
+ return res | POLLOUT | POLLWRNORM;
+ }
+ }
+
+ /*
+ * There is nothing to wait for if no buffers have already been queued.
+ */
+ if (list_empty(&q->queued_list))
+ return res | 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)) ?
+ res | POLLOUT | POLLWRNORM :
+ res | POLLIN | POLLRDNORM;
+ }
+ return res;
+}
+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)
+{
+ /*
+ * Sanity check
+ */
+ if (WARN_ON(!q) ||
+ WARN_ON(!q->ops) ||
+ WARN_ON(!q->mem_ops) ||
+ WARN_ON(!q->type) ||
+ WARN_ON(!q->io_modes) ||
+ WARN_ON(!q->ops->queue_setup) ||
+ WARN_ON(!q->ops->buf_queue))
+ return -EINVAL;
+
+ 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, q->num_buffers);
+}
+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) {
+ 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:
+ fileio->req.count = 0;
+ 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);
+
+
+/*
+ * The following functions are not part of the vb2 core API, but are helper
+ * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
+ * and struct vb2_ops.
+ * They contain boilerplate code that most if not all drivers have to do
+ * and so they simplify the driver code.
+ */
+
+/* The queue is busy if there is a owner and you are not that owner. */
+static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
+{
+ return vdev->queue->owner && vdev->queue->owner != file->private_data;
+}
+
+/* vb2 ioctl helpers */
+
+int vb2_ioctl_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = __verify_memory_type(vdev->queue, p->memory, p->type);
+
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ res = __reqbufs(vdev->queue, p);
+ /* If count == 0, then the owner has released all buffers and he
+ is no longer owner of the queue. Otherwise we have a new owner. */
+ if (res == 0)
+ vdev->queue->owner = p->count ? file->private_data : NULL;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
+
+int vb2_ioctl_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
+
+ p->index = vdev->queue->num_buffers;
+ /* If count == 0, then just check if memory and type are valid.
+ Any -EBUSY result from __verify_memory_type can be mapped to 0. */
+ if (p->count == 0)
+ return res != -EBUSY ? res : 0;
+ if (res)
+ return res;
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ res = __create_bufs(vdev->queue, p);
+ if (res == 0)
+ vdev->queue->owner = file->private_data;
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
+
+int vb2_ioctl_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_prepare_buf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
+
+int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
+ return vb2_querybuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
+
+int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_qbuf(vdev->queue, p);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
+
+int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
+
+int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_streamon(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
+
+int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (vb2_queue_is_busy(vdev, file))
+ return -EBUSY;
+ return vb2_streamoff(vdev->queue, i);
+}
+EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
+
+/* v4l2_file_operations helpers */
+
+int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_mmap(vdev->queue, vma);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_mmap);
+
+int vb2_fop_release(struct file *file)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ if (file->private_data == vdev->queue->owner) {
+ vb2_queue_release(vdev->queue);
+ vdev->queue->owner = NULL;
+ }
+ return v4l2_fh_release(file);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_release);
+
+ssize_t vb2_fop_write(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ int err = -EBUSY;
+
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev, file))
+ goto exit;
+ err = vb2_write(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (vdev->queue->fileio)
+ vdev->queue->owner = file->private_data;
+exit:
+ if (lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_write);
+
+ssize_t vb2_fop_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
+ int err = -EBUSY;
+
+ if (lock && mutex_lock_interruptible(lock))
+ return -ERESTARTSYS;
+ if (vb2_queue_is_busy(vdev, file))
+ goto exit;
+ err = vb2_read(vdev->queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (vdev->queue->fileio)
+ vdev->queue->owner = file->private_data;
+exit:
+ if (lock)
+ mutex_unlock(lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_read);
+
+unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct vb2_queue *q = vdev->queue;
+ struct mutex *lock = q->lock ? q->lock : vdev->lock;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned res;
+ void *fileio;
+ bool must_lock = false;
+
+ /* Try to be smart: only lock if polling might start fileio,
+ otherwise locking will only introduce unwanted delays. */
+ if (q->num_buffers == 0 && q->fileio == NULL) {
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+ (req_events & (POLLIN | POLLRDNORM)))
+ must_lock = true;
+ else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+ (req_events & (POLLOUT | POLLWRNORM)))
+ must_lock = true;
+ }
+
+ /* If locking is needed, but this helper doesn't know how, then you
+ shouldn't be using this helper but you should write your own. */
+ WARN_ON(must_lock && !lock);
+
+ if (must_lock && lock && mutex_lock_interruptible(lock))
+ return POLLERR;
+
+ fileio = q->fileio;
+
+ res = vb2_poll(vdev->queue, file, wait);
+
+ /* If fileio was started, then we have a new queue owner. */
+ if (must_lock && !fileio && q->fileio)
+ q->owner = file->private_data;
+ if (must_lock && lock)
+ mutex_unlock(lock);
+ return res;
+}
+EXPORT_SYMBOL_GPL(vb2_fop_poll);
+
+#ifndef CONFIG_MMU
+unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
+}
+EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
+#endif
+
+/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
+
+void vb2_ops_wait_prepare(struct vb2_queue *vq)
+{
+ mutex_unlock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
+
+void vb2_ops_wait_finish(struct vb2_queue *vq)
+{
+ mutex_lock(vq->lock);
+}
+EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
+
+MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
+MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 4b7132660a93..4b7132660a93 100644
--- a/drivers/media/video/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 25c3b360e1ad..25c3b360e1ad 100644
--- a/drivers/media/video/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/v4l2-core/videobuf2-memops.c
index 504cd4cbe29e..504cd4cbe29e 100644
--- a/drivers/media/video/videobuf2-memops.c
+++ b/drivers/media/v4l2-core/videobuf2-memops.c
diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c
new file mode 100644
index 000000000000..94efa04d8d55
--- /dev/null
+++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c
@@ -0,0 +1,223 @@
+/*
+ * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ *
+ * Author: Pawel Osciak <pawel@osciak.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/io.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h>
+#include <media/videobuf2-memops.h>
+
+struct vb2_vmalloc_buf {
+ void *vaddr;
+ struct page **pages;
+ struct vm_area_struct *vma;
+ int write;
+ unsigned long size;
+ unsigned int n_pages;
+ 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) {
+ pr_debug("vmalloc of size %ld failed\n", buf->size);
+ kfree(buf);
+ return NULL;
+ }
+
+ atomic_inc(&buf->refcount);
+ return buf;
+}
+
+static void vb2_vmalloc_put(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (atomic_dec_and_test(&buf->refcount)) {
+ vfree(buf->vaddr);
+ kfree(buf);
+ }
+}
+
+static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
+ unsigned long size, int write)
+{
+ struct vb2_vmalloc_buf *buf;
+ unsigned long first, last;
+ int n_pages, offset;
+ struct vm_area_struct *vma;
+ dma_addr_t physp;
+
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ buf->write = write;
+ offset = vaddr & ~PAGE_MASK;
+ buf->size = size;
+
+
+ vma = find_vma(current->mm, vaddr);
+ if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
+ if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
+ goto fail_pages_array_alloc;
+ buf->vma = vma;
+ buf->vaddr = ioremap_nocache(physp, size);
+ if (!buf->vaddr)
+ goto fail_pages_array_alloc;
+ } else {
+ first = vaddr >> PAGE_SHIFT;
+ last = (vaddr + size - 1) >> PAGE_SHIFT;
+ buf->n_pages = last - first + 1;
+ buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ /* current->mm->mmap_sem is taken by videobuf2 core */
+ n_pages = get_user_pages(current, current->mm,
+ vaddr & PAGE_MASK, buf->n_pages,
+ write, 1, /* force */
+ buf->pages, NULL);
+ if (n_pages != buf->n_pages)
+ goto fail_get_user_pages;
+
+ buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+ PAGE_KERNEL);
+ if (!buf->vaddr)
+ goto fail_get_user_pages;
+ }
+
+ buf->vaddr += offset;
+ return buf;
+
+fail_get_user_pages:
+ pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
+ buf->n_pages);
+ while (--n_pages >= 0)
+ put_page(buf->pages[n_pages]);
+ kfree(buf->pages);
+
+fail_pages_array_alloc:
+ kfree(buf);
+
+ return NULL;
+}
+
+static void vb2_vmalloc_put_userptr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+ unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
+ unsigned int i;
+
+ if (buf->pages) {
+ if (vaddr)
+ vm_unmap_ram((void *)vaddr, buf->n_pages);
+ for (i = 0; i < buf->n_pages; ++i) {
+ if (buf->write)
+ set_page_dirty_lock(buf->pages[i]);
+ put_page(buf->pages[i]);
+ }
+ kfree(buf->pages);
+ } else {
+ if (buf->vma)
+ vb2_put_vma(buf->vma);
+ iounmap(buf->vaddr);
+ }
+ kfree(buf);
+}
+
+static void *vb2_vmalloc_vaddr(void *buf_priv)
+{
+ struct vb2_vmalloc_buf *buf = buf_priv;
+
+ if (!buf->vaddr) {
+ pr_err("Address of an unallocated plane requested "
+ "or cannot map user pointer\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) {
+ pr_err("No memory to map\n");
+ return -EINVAL;
+ }
+
+ ret = remap_vmalloc_range(vma, buf->vaddr, 0);
+ if (ret) {
+ pr_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,
+ .get_userptr = vb2_vmalloc_get_userptr,
+ .put_userptr = vb2_vmalloc_put_userptr,
+ .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 <pawel@osciak.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
deleted file mode 100644
index c128fac0ce2c..000000000000
--- a/drivers/media/video/Kconfig
+++ /dev/null
@@ -1,1263 +0,0 @@
-#
-# Generic video config states
-#
-
-config VIDEO_V4L2
- tristate
- depends on VIDEO_DEV && VIDEO_V4L2_COMMON
- default y
-
-config VIDEOBUF_GEN
- tristate
-
-config VIDEOBUF_DMA_SG
- depends on HAS_DMA
- select VIDEOBUF_GEN
- tristate
-
-config VIDEOBUF_VMALLOC
- select VIDEOBUF_GEN
- tristate
-
-config VIDEOBUF_DMA_CONTIG
- depends on HAS_DMA
- select VIDEOBUF_GEN
- tristate
-
-config VIDEOBUF_DVB
- tristate
- select VIDEOBUF_GEN
-
-config VIDEO_BTCX
- depends on PCI
- tristate
-
-config VIDEO_TVEEPROM
- tristate
- depends on I2C
-
-config VIDEO_TUNER
- tristate
- depends on MEDIA_TUNER
-
-config V4L2_MEM2MEM_DEV
- tristate
- 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
-#
-
-menuconfig VIDEO_CAPTURE_DRIVERS
- bool "Video capture adapters"
- depends on VIDEO_V4L2
- depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT
- default y
- ---help---
- Say Y here to enable selecting the video adapters for
- webcams, analog TV, and hybrid analog/digital TV.
- Some of those devices also supports FM radio.
-
-if VIDEO_CAPTURE_DRIVERS && VIDEO_V4L2
-
-config VIDEO_ADV_DEBUG
- bool "Enable advanced debug functionality"
- default n
- ---help---
- Say Y here to enable advanced debugging functionality on some
- V4L devices.
- In doubt, say N.
-
-config VIDEO_FIXED_MINOR_RANGES
- bool "Enable old-style fixed minor ranges for video devices"
- default n
- ---help---
- Say Y here to enable the old-style fixed-range minor assignments.
- Only useful if you rely on the old behavior and use mknod instead of udev.
-
- When in doubt, say N.
-
-config VIDEO_HELPER_CHIPS_AUTO
- bool "Autoselect pertinent encoders/decoders and other helper chips"
- default y if !EXPERT
- ---help---
- Most video cards may require additional modules to encode or
- decode audio/video standards. This option will autoselect
- all pertinent modules to each selected video module.
-
- Unselect this only if you know exactly what you are doing, since
- it may break support on some boards.
-
- In doubt, say Y.
-
-config VIDEO_IR_I2C
- tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO
- depends on I2C && RC_CORE
- default y
- ---help---
- Most boards have an IR chip directly connected via GPIO. However,
- some video boards have the IR connected via I2C bus.
-
- If your board doesn't have an I2C IR chip, you may disable this
- option.
-
- In doubt, say Y.
-
-#
-# Encoder / Decoder module configuration
-#
-
-menu "Encoders, decoders, sensors and other helper chips"
- visible if !VIDEO_HELPER_CHIPS_AUTO
-
-comment "Audio decoders, processors and mixers"
-
-config VIDEO_TVAUDIO
- tristate "Simple audio decoder chips"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for several audio decoder chips found on some bt8xx boards:
- Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
- tea6320, tea6420, tda8425, ta8874z.
- Microchip: pic16c54 based design on ProVideo PV951 board.
-
- To compile this driver as a module, choose M here: the
- module will be called tvaudio.
-
-config VIDEO_TDA7432
- tristate "Philips TDA7432 audio processor"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for tda7432 audio decoder chip found on some bt8xx boards.
-
- To compile this driver as a module, choose M here: the
- module will be called tda7432.
-
-config VIDEO_TDA9840
- tristate "Philips TDA9840 audio processor"
- depends on I2C
- ---help---
- Support for tda9840 audio decoder chip found on some Zoran boards.
-
- To compile this driver as a module, choose M here: the
- module will be called tda9840.
-
-config VIDEO_TEA6415C
- tristate "Philips TEA6415C audio processor"
- depends on I2C
- ---help---
- Support for tea6415c audio decoder chip found on some bt8xx boards.
-
- To compile this driver as a module, choose M here: the
- module will be called tea6415c.
-
-config VIDEO_TEA6420
- tristate "Philips TEA6420 audio processor"
- depends on I2C
- ---help---
- Support for tea6420 audio decoder chip found on some bt8xx boards.
-
- To compile this driver as a module, choose M here: the
- module will be called tea6420.
-
-config VIDEO_MSP3400
- tristate "Micronas MSP34xx audio decoders"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Micronas MSP34xx series of audio decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called msp3400.
-
-config VIDEO_CS5345
- tristate "Cirrus Logic CS5345 audio ADC"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Cirrus Logic CS5345 24-bit, 192 kHz
- stereo A/D converter.
-
- To compile this driver as a module, choose M here: the
- module will be called cs5345.
-
-config VIDEO_CS53L32A
- tristate "Cirrus Logic CS53L32A audio ADC"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Cirrus Logic CS53L32A low voltage
- stereo A/D converter.
-
- To compile this driver as a module, choose M here: the
- module will be called cs53l32a.
-
-config VIDEO_TLV320AIC23B
- tristate "Texas Instruments TLV320AIC23B audio codec"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
- ---help---
- Support for the Texas Instruments TLV320AIC23B audio codec.
-
- To compile this driver as a module, choose M here: the
- module will be called tlv320aic23b.
-
-config VIDEO_WM8775
- tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Wolfson Microelectronics WM8775 high
- performance stereo A/D Converter with a 4 channel input mixer.
-
- To compile this driver as a module, choose M here: the
- module will be called wm8775.
-
-config VIDEO_WM8739
- tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Wolfson Microelectronics WM8739
- stereo A/D Converter.
-
- To compile this driver as a module, choose M here: the
- module will be called wm8739.
-
-config VIDEO_VP27SMPX
- tristate "Panasonic VP27s internal MPX"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the internal MPX of the Panasonic VP27s tuner.
-
- To compile this driver as a module, choose M here: the
- module will be called vp27smpx.
-
-comment "RDS decoders"
-
-config VIDEO_SAA6588
- tristate "SAA6588 Radio Chip RDS decoder support"
- depends on VIDEO_V4L2 && I2C
-
- help
- Support for this Radio Data System (RDS) decoder. This allows
- seeing radio station identification transmitted using this
- standard.
-
- To compile this driver as a module, choose M here: the
- module will be called saa6588.
-
-comment "Video decoders"
-
-config VIDEO_ADV7180
- tristate "Analog Devices ADV7180 decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Analog Devices ADV7180 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called adv7180.
-
-config VIDEO_ADV7183
- tristate "Analog Devices ADV7183 decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- V4l2 subdevice driver for the Analog Devices
- ADV7183 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called adv7183.
-
-config VIDEO_BT819
- tristate "BT819A VideoStream decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for BT819A video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called bt819.
-
-config VIDEO_BT856
- tristate "BT856 VideoStream decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for BT856 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called bt856.
-
-config VIDEO_BT866
- tristate "BT866 VideoStream decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for BT866 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called bt866.
-
-config VIDEO_KS0127
- tristate "KS0127 video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for KS0127 video decoder.
-
- This chip is used on AverMedia AVS6EYES Zoran-based MJPEG
- cards.
-
- To compile this driver as a module, choose M here: the
- module will be called ks0127.
-
-config VIDEO_SAA7110
- tristate "Philips SAA7110 video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7110 video decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7110.
-
-config VIDEO_SAA711X
- tristate "Philips SAA7111/3/4/5 video decoders"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7111/3/4/5 video decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7115.
-
-config VIDEO_SAA7191
- tristate "Philips SAA7191 video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7191 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7191.
-
-config VIDEO_TVP514X
- tristate "Texas Instruments TVP514x video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
- decoder. It is currently working with the TI OMAP3 camera
- controller.
-
- To compile this driver as a module, choose M here: the
- module will be called tvp514x.
-
-config VIDEO_TVP5150
- tristate "Texas Instruments TVP5150 video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Texas Instruments TVP5150 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called tvp5150.
-
-config VIDEO_TVP7002
- tristate "Texas Instruments TVP7002 video decoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Texas Instruments TVP7002 video decoder.
-
- To compile this driver as a module, choose M here: the
- module will be called tvp7002.
-
-config VIDEO_VPX3220
- tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for VPX322x video decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called vpx3220.
-
-comment "Video and audio decoders"
-
-config VIDEO_SAA717X
- tristate "Philips SAA7171/3/4 audio/video decoders"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7171/3/4 audio/video decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa717x.
-
-source "drivers/media/video/cx25840/Kconfig"
-
-comment "MPEG video encoders"
-
-config VIDEO_CX2341X
- tristate "Conexant CX2341x MPEG encoders"
- depends on VIDEO_V4L2 && VIDEO_V4L2_COMMON
- ---help---
- Support for the Conexant CX23416 MPEG encoders
- and CX23415 MPEG encoder/decoders.
-
- This module currently supports the encoding functions only.
-
- To compile this driver as a module, choose M here: the
- module will be called cx2341x.
-
-comment "Video encoders"
-
-config VIDEO_SAA7127
- tristate "Philips SAA7127/9 digital video encoders"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7127/9 digital video encoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7127.
-
-config VIDEO_SAA7185
- tristate "Philips SAA7185 video encoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Philips SAA7185 video encoder.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7185.
-
-config VIDEO_ADV7170
- tristate "Analog Devices ADV7170 video encoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Analog Devices ADV7170 video encoder driver
-
- To compile this driver as a module, choose M here: the
- module will be called adv7170.
-
-config VIDEO_ADV7175
- tristate "Analog Devices ADV7175 video encoder"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Analog Devices ADV7175 video encoder driver
-
- To compile this driver as a module, choose M here: the
- module will be called adv7175.
-
-config VIDEO_ADV7343
- tristate "ADV7343 video encoder"
- depends on I2C
- help
- Support for Analog Devices I2C bus based ADV7343 encoder.
-
- To compile this driver as a module, choose M here: the
- module will be called adv7343.
-
-config VIDEO_ADV7393
- tristate "ADV7393 video encoder"
- depends on I2C
- help
- Support for Analog Devices I2C bus based ADV7393 encoder.
-
- To compile this driver as a module, choose M here: the
- module will be called adv7393.
-
-config VIDEO_AK881X
- tristate "AK8813/AK8814 video encoders"
- depends on I2C
- help
- Video output driver for AKM AK8813 and AK8814 TV encoders
-
-comment "Camera sensor devices"
-
-config VIDEO_APTINA_PLL
- tristate
-
-config VIDEO_SMIAPP_PLL
- tristate
-
-config VIDEO_OV7670
- tristate "OmniVision OV7670 sensor support"
- depends on I2C && VIDEO_V4L2
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a Video4Linux2 sensor-level driver for the OmniVision
- OV7670 VGA camera. It currently only works with the M88ALP01
- controller.
-
-config VIDEO_VS6624
- tristate "ST VS6624 sensor support"
- depends on VIDEO_V4L2 && I2C
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a Video4Linux2 sensor-level driver for the ST VS6624
- camera.
-
- To compile this driver as a module, choose M here: the
- module will be called vs6624.
-
-config VIDEO_MT9M032
- tristate "MT9M032 camera sensor support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on MEDIA_CAMERA_SUPPORT
- select VIDEO_APTINA_PLL
- ---help---
- This driver supports MT9M032 camera sensors from Aptina, monochrome
- models only.
-
-config VIDEO_MT9P031
- tristate "Aptina MT9P031 support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on MEDIA_CAMERA_SUPPORT
- select VIDEO_APTINA_PLL
- ---help---
- This is a Video4Linux2 sensor-level driver for the Aptina
- (Micron) mt9p031 5 Mpixel camera.
-
-config VIDEO_MT9T001
- tristate "Aptina MT9T001 support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a Video4Linux2 sensor-level driver for the Aptina
- (Micron) mt0t001 3 Mpixel camera.
-
-config VIDEO_MT9V011
- tristate "Micron mt9v011 sensor support"
- depends on I2C && VIDEO_V4L2
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a Video4Linux2 sensor-level driver for the Micron
- mt0v011 1.3 Mpixel camera. It currently only works with the
- em28xx driver.
-
-config VIDEO_MT9V032
- tristate "Micron MT9V032 sensor support"
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a Video4Linux2 sensor-level driver for the Micron
- MT9V032 752x480 CMOS sensor.
-
-config VIDEO_TCM825X
- tristate "TCM825x camera sensor support"
- depends on I2C && VIDEO_V4L2
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a driver for the Toshiba TCM825x VGA camera sensor.
- It is used for example in Nokia N800.
-
-config VIDEO_SR030PC30
- tristate "Siliconfile SR030PC30 sensor support"
- depends on I2C && VIDEO_V4L2
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This driver supports SR030PC30 VGA camera from Siliconfile
-
-config VIDEO_NOON010PC30
- tristate "Siliconfile NOON010PC30 sensor support"
- depends on I2C && VIDEO_V4L2 && EXPERIMENTAL && VIDEO_V4L2_SUBDEV_API
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This driver supports NOON010PC30 CIF camera from Siliconfile
-
-source "drivers/media/video/m5mols/Kconfig"
-
-config VIDEO_S5K6AA
- tristate "Samsung S5K6AAFX sensor support"
- depends on MEDIA_CAMERA_SUPPORT
- depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
- ---help---
- This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
- camera sensor with an embedded SoC image signal processor.
-
-source "drivers/media/video/smiapp/Kconfig"
-
-comment "Flash devices"
-
-config VIDEO_ADP1653
- tristate "ADP1653 flash support"
- depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a driver for the ADP1653 flash controller. It is used for
- example in Nokia N900.
-
-config VIDEO_AS3645A
- tristate "AS3645A flash driver support"
- depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER
- depends on MEDIA_CAMERA_SUPPORT
- ---help---
- This is a driver for the AS3645A and LM3555 flash controllers. It has
- build in control for flash, torch and indicator LEDs.
-
-comment "Video improvement chips"
-
-config VIDEO_UPD64031A
- tristate "NEC Electronics uPD64031A Ghost Reduction"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the NEC Electronics uPD64031A Ghost Reduction
- video chip. It is most often found in NTSC TV cards made for
- Japan and is used to reduce the 'ghosting' effect that can
- be present in analog TV broadcasts.
-
- To compile this driver as a module, choose M here: the
- module will be called upd64031a.
-
-config VIDEO_UPD64083
- tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the NEC Electronics uPD64083 3-Dimensional Y/C
- separation video chip. It is used to improve the quality of
- the colors of a composite signal.
-
- To compile this driver as a module, choose M here: the
- module will be called upd64083.
-
-comment "Miscelaneous helper chips"
-
-config VIDEO_THS7303
- tristate "THS7303 Video Amplifier"
- depends on I2C
- help
- Support for TI THS7303 video amplifier
-
- To compile this driver as a module, choose M here: the
- module will be called ths7303.
-
-config VIDEO_M52790
- tristate "Mitsubishi M52790 A/V switch"
- depends on VIDEO_V4L2 && I2C
- ---help---
- Support for the Mitsubishi M52790 A/V switch.
-
- To compile this driver as a module, choose M here: the
- module will be called m52790.
-
-endmenu # encoder / decoder chips
-
-config VIDEO_VIVI
- tristate "Virtual Video Driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64
- depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
- select FONT_8x16
- select VIDEOBUF2_VMALLOC
- default n
- ---help---
- Enables a virtual video driver. This device shows a color bar
- and a timestamp, as a real device would generate by using V4L2
- api.
- Say Y here if you want to test video apps or debug V4L devices.
- In doubt, say N.
-
-#
-# USB Multimedia device configuration
-#
-
-menuconfig V4L_USB_DRIVERS
- bool "V4L USB devices"
- depends on USB
- default y
-
-if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
-
- comment "Webcam devices"
-
-source "drivers/media/video/uvc/Kconfig"
-
-source "drivers/media/video/gspca/Kconfig"
-
-source "drivers/media/video/pwc/Kconfig"
-
-source "drivers/media/video/cpia2/Kconfig"
-
-config USB_ZR364XX
- tristate "USB ZR364XX Camera support"
- depends on VIDEO_V4L2
- select VIDEOBUF_GEN
- select VIDEOBUF_VMALLOC
- ---help---
- Say Y here if you want to connect this type of camera to your
- computer's USB port.
- See <file:Documentation/video4linux/zr364xx.txt> for more info
- and list of supported cameras.
-
- To compile this driver as a module, choose M here: the
- module will be called zr364xx.
-
-config USB_STKWEBCAM
- tristate "USB Syntek DC1125 Camera support"
- depends on VIDEO_V4L2 && EXPERIMENTAL
- ---help---
- Say Y here if you want to use this type of camera.
- Supported devices are typically found in some Asus laptops,
- with USB id 174f:a311 and 05e1:0501. Other Syntek cameras
- may be supported by the stk11xx driver, from which this is
- derived, see <http://sourceforge.net/projects/syntekdriver/>
-
- To compile this driver as a module, choose M here: the
- module will be called stkwebcam.
-
-config USB_S2255
- tristate "USB Sensoray 2255 video capture device"
- depends on VIDEO_V4L2
- select VIDEOBUF_VMALLOC
- default n
- help
- Say Y here if you want support for the Sensoray 2255 USB device.
- This driver can be compiled as a module, called s2255drv.
-
-source "drivers/media/video/sn9c102/Kconfig"
-
-endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT
-
-if V4L_USB_DRIVERS
-
- comment "Webcam and/or TV USB devices"
-
-source "drivers/media/video/em28xx/Kconfig"
-
-endif
-
-if V4L_USB_DRIVERS && MEDIA_ANALOG_TV_SUPPORT
-
- comment "TV USB devices"
-
-source "drivers/media/video/au0828/Kconfig"
-
-source "drivers/media/video/pvrusb2/Kconfig"
-
-source "drivers/media/video/hdpvr/Kconfig"
-
-source "drivers/media/video/tlg2300/Kconfig"
-
-source "drivers/media/video/cx231xx/Kconfig"
-
-source "drivers/media/video/tm6000/Kconfig"
-
-source "drivers/media/video/usbvision/Kconfig"
-
-endif # V4L_USB_DRIVERS
-
-#
-# PCI drivers configuration - No devices here are for webcams
-#
-
-menuconfig V4L_PCI_DRIVERS
- bool "V4L PCI(e) devices"
- depends on PCI
- depends on MEDIA_ANALOG_TV_SUPPORT
- default y
- ---help---
- Say Y here to enable support for these PCI(e) drivers.
-
-if V4L_PCI_DRIVERS
-
-source "drivers/media/video/bt8xx/Kconfig"
-
-source "drivers/media/video/cx18/Kconfig"
-
-source "drivers/media/video/cx23885/Kconfig"
-
-source "drivers/media/video/cx25821/Kconfig"
-
-source "drivers/media/video/cx88/Kconfig"
-
-config VIDEO_HEXIUM_GEMINI
- tristate "Hexium Gemini frame grabber"
- depends on PCI && VIDEO_V4L2 && I2C
- select VIDEO_SAA7146_VV
- ---help---
- This is a video4linux driver for the Hexium Gemini frame
- grabber card by Hexium. Please note that the Gemini Dual
- card is *not* fully supported.
-
- To compile this driver as a module, choose M here: the
- module will be called hexium_gemini.
-
-config VIDEO_HEXIUM_ORION
- tristate "Hexium HV-PCI6 and Orion frame grabber"
- depends on PCI && VIDEO_V4L2 && I2C
- select VIDEO_SAA7146_VV
- ---help---
- This is a video4linux driver for the Hexium HV-PCI6 and
- Orion frame grabber cards by Hexium.
-
- To compile this driver as a module, choose M here: the
- module will be called hexium_orion.
-
-source "drivers/media/video/ivtv/Kconfig"
-
-config VIDEO_MEYE
- tristate "Sony Vaio Picturebook Motion Eye Video For Linux"
- depends on PCI && SONY_LAPTOP && VIDEO_V4L2
- ---help---
- This is the video4linux driver for the Motion Eye camera found
- in the Vaio Picturebook laptops. Please read the material in
- <file:Documentation/video4linux/meye.txt> for more information.
-
- If you say Y or M here, you need to say Y or M to "Sony Laptop
- Extras" in the misc device section.
-
- To compile this driver as a module, choose M here: the
- module will be called meye.
-
-config VIDEO_MXB
- tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
- depends on PCI && VIDEO_V4L2 && I2C
- select VIDEO_SAA7146_VV
- select VIDEO_TUNER
- select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- This is a video4linux driver for the 'Multimedia eXtension Board'
- TV card by Siemens-Nixdorf.
-
- To compile this driver as a module, choose M here: the
- module will be called mxb.
-
-source "drivers/media/video/saa7134/Kconfig"
-
-source "drivers/media/video/saa7164/Kconfig"
-
-source "drivers/media/video/zoran/Kconfig"
-
-config STA2X11_VIP
- tristate "STA2X11 VIP Video For Linux"
- depends on STA2X11
- select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEOBUF_DMA_CONTIG
- depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
- help
- Say Y for support for STA2X11 VIP (Video Input Port) capture
- device.
-
- To compile this driver as a module, choose M here: the
- module will be called sta2x11_vip.
-
-endif # V4L_PCI_DRIVERS
-
-#
-# ISA & parallel port drivers configuration
-# All devices here are webcam or grabber devices
-#
-
-menuconfig V4L_ISA_PARPORT_DRIVERS
- bool "V4L ISA and parallel port devices"
- depends on ISA || PARPORT
- depends on MEDIA_CAMERA_SUPPORT
- default n
- ---help---
- Say Y here to enable support for these ISA and parallel port drivers.
-
-if V4L_ISA_PARPORT_DRIVERS
-
-config VIDEO_BWQCAM
- tristate "Quickcam BW Video For Linux"
- depends on PARPORT && VIDEO_V4L2
- help
- Say Y have if you the black and white version of the QuickCam
- camera. See the next option for the color version.
-
- To compile this driver as a module, choose M here: the
- module will be called bw-qcam.
-
-config VIDEO_CQCAM
- tristate "QuickCam Colour Video For Linux"
- depends on PARPORT && VIDEO_V4L2
- help
- This is the video4linux driver for the colour version of the
- Connectix QuickCam. If you have one of these cameras, say Y here,
- otherwise say N. This driver does not work with the original
- monochrome QuickCam, QuickCam VC or QuickClip. It is also available
- as a module (c-qcam).
- Read <file:Documentation/video4linux/CQcam.txt> for more information.
-
-config VIDEO_PMS
- tristate "Mediavision Pro Movie Studio Video For Linux"
- depends on ISA && VIDEO_V4L2
- help
- Say Y if you have the ISA Mediavision Pro Movie Studio
- capture card.
-
- To compile this driver as a module, choose M here: the
- module will be called pms.
-
-config VIDEO_W9966
- tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux"
- depends on PARPORT_1284 && PARPORT && VIDEO_V4L2
- help
- Video4linux driver for Winbond's w9966 based Webcams.
- Currently tested with the LifeView FlyCam Supra.
- If you have one of these cameras, say Y here
- otherwise say N.
- This driver is also available as a module (w9966).
-
- Check out <file:Documentation/video4linux/w9966.txt> for more
- information.
-
-endif # V4L_ISA_PARPORT_DRIVERS
-
-#
-# Platform drivers
-# All drivers here are currently for webcam support
-
-menuconfig V4L_PLATFORM_DRIVERS
- bool "V4L platform devices"
- depends on MEDIA_CAMERA_SUPPORT
- default n
- ---help---
- Say Y here to enable support for platform-specific V4L drivers.
-
-if V4L_PLATFORM_DRIVERS
-
-source "drivers/media/video/marvell-ccic/Kconfig"
-
-config VIDEO_VIA_CAMERA
- tristate "VIAFB camera controller support"
- depends on FB_VIA
- select VIDEOBUF_DMA_SG
- select VIDEO_OV7670
- help
- Driver support for the integrated camera controller in VIA
- Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
- with ov7670 sensors.
-
-#
-# Platform multimedia device configuration
-#
-
-source "drivers/media/video/davinci/Kconfig"
-
-source "drivers/media/video/omap/Kconfig"
-
-source "drivers/media/video/blackfin/Kconfig"
-
-config VIDEO_SH_VOU
- tristate "SuperH VOU video output driver"
- depends on VIDEO_DEV && ARCH_SHMOBILE
- select VIDEOBUF_DMA_CONTIG
- help
- Support for the Video Output Unit (VOU) on SuperH SoCs.
-
-config VIDEO_VIU
- tristate "Freescale VIU Video Driver"
- depends on VIDEO_V4L2 && PPC_MPC512x
- select VIDEOBUF_DMA_CONTIG
- default y
- ---help---
- Support for Freescale VIU video driver. This device captures
- video data, or overlays video on DIU frame buffer.
-
- Say Y here if you want to enable VIU device on MPC5121e Rev2+.
- In doubt, say N.
-
-config VIDEO_TIMBERDALE
- tristate "Support for timberdale Video In/LogiWIN"
- depends on VIDEO_V4L2 && I2C && DMADEVICES
- select DMA_ENGINE
- select TIMB_DMA
- select VIDEO_ADV7180
- select VIDEOBUF_DMA_CONTIG
- ---help---
- Add support for the Video In peripherial of the timberdale FPGA.
-
-config VIDEO_VINO
- tristate "SGI Vino Video For Linux"
- depends on I2C && SGI_IP22 && VIDEO_V4L2
- select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO
- help
- Say Y here to build in support for the Vino video input system found
- on SGI Indy machines.
-
-config VIDEO_M32R_AR
- tristate "AR devices"
- depends on M32R && VIDEO_V4L2
- ---help---
- This is a video4linux driver for the Renesas AR (Artificial Retina)
- camera module.
-
-config VIDEO_M32R_AR_M64278
- tristate "AR device with color module M64278(VGA)"
- depends on PLAT_M32700UT
- select VIDEO_M32R_AR
- ---help---
- This is a video4linux driver for the Renesas AR (Artificial
- Retina) with M64278E-800 camera module.
- This module supports VGA(640x480 pixels) resolutions.
-
- To compile this driver as a module, choose M here: the
- module will be called arv.
-
-config VIDEO_OMAP3
- tristate "OMAP 3 Camera support (EXPERIMENTAL)"
- depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL
- ---help---
- Driver for an OMAP 3 camera controller.
-
-config VIDEO_OMAP3_DEBUG
- bool "OMAP 3 Camera debug messages"
- depends on VIDEO_OMAP3
- ---help---
- Enable debug messages on OMAP 3 camera controller driver.
-
-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
- directly to the data bus of an SoC.
-
-config SOC_CAMERA_IMX074
- tristate "imx074 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports IMX074 cameras from Sony
-
-config SOC_CAMERA_MT9M001
- tristate "mt9m001 support"
- depends on SOC_CAMERA && I2C
- select GPIO_PCA953X if MT9M001_PCA9536_SWITCH
- help
- This driver supports MT9M001 cameras from Micron, monochrome
- and colour models.
-
-config SOC_CAMERA_MT9M111
- tristate "mt9m111, mt9m112 and mt9m131 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports MT9M111, MT9M112 and MT9M131 cameras from
- Micron/Aptina
-
-config SOC_CAMERA_MT9T031
- tristate "mt9t031 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports MT9T031 cameras from Micron.
-
-config SOC_CAMERA_MT9T112
- tristate "mt9t112 support"
- depends on SOC_CAMERA && I2C
- help
- This driver supports MT9T112 cameras from Aptina.
-
-config SOC_CAMERA_MT9V022
- tristate "mt9v022 support"
- depends on SOC_CAMERA && I2C
- select GPIO_PCA953X if MT9V022_PCA9536_SWITCH
- help
- This driver supports MT9V022 cameras from Micron
-
-config SOC_CAMERA_RJ54N1
- tristate "rj54n1cb0c support"
- depends on SOC_CAMERA && I2C
- help
- This is a rj54n1cb0c video driver
-
-config SOC_CAMERA_TW9910
- tristate "tw9910 support"
- depends on SOC_CAMERA && I2C
- help
- This is a tw9910 video driver
-
-config SOC_CAMERA_PLATFORM
- tristate "platform camera support"
- depends on SOC_CAMERA
- help
- This is a generic SoC camera platform driver, useful for testing
-
-config SOC_CAMERA_OV2640
- tristate "ov2640 camera support"
- depends on SOC_CAMERA && I2C
- help
- This is a ov2640 camera driver
-
-config SOC_CAMERA_OV5642
- tristate "ov5642 camera support"
- depends on SOC_CAMERA && I2C
- help
- This is a V4L2 camera driver for the OmniVision OV5642 sensor
-
-config SOC_CAMERA_OV6650
- tristate "ov6650 sensor support"
- depends on SOC_CAMERA && I2C
- ---help---
- This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor
-
-config SOC_CAMERA_OV772X
- tristate "ov772x camera support"
- depends on SOC_CAMERA && I2C
- help
- This is a ov772x camera driver
-
-config SOC_CAMERA_OV9640
- tristate "ov9640 camera support"
- depends on SOC_CAMERA && I2C
- 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
-
-config VIDEO_MX1
- tristate "i.MX1/i.MXL CMOS Sensor Interface driver"
- depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA
- select FIQ
- select VIDEOBUF_DMA_CONTIG
- select MX1_VIDEO
- ---help---
- This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface
-
-config MX3_VIDEO
- bool
-
-config VIDEO_MX3
- tristate "i.MX3x Camera Sensor Interface driver"
- depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
- select VIDEOBUF2_DMA_CONTIG
- select MX3_VIDEO
- ---help---
- This is a v4l2 driver for the i.MX3x Camera Sensor Interface
-
-config VIDEO_PXA27x
- tristate "PXA27x Quick Capture Interface driver"
- depends on VIDEO_DEV && PXA27x && SOC_CAMERA
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the PXA27x Quick Capture Interface
-
-config VIDEO_SH_MOBILE_CSI2
- tristate "SuperH Mobile MIPI CSI-2 Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
- ---help---
- This is a v4l2 driver for the SuperH MIPI CSI-2 Interface
-
-config VIDEO_SH_MOBILE_CEU
- tristate "SuperH Mobile CEU Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK
- select VIDEOBUF2_DMA_CONTIG
- ---help---
- This is a v4l2 driver for the SuperH Mobile CEU Interface
-
-config VIDEO_OMAP1
- tristate "OMAP1 Camera Interface driver"
- depends on VIDEO_DEV && ARCH_OMAP1 && SOC_CAMERA
- select VIDEOBUF_DMA_CONTIG
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the TI OMAP1 camera interface
-
-config VIDEO_OMAP2
- tristate "OMAP2 Camera Capture Interface driver"
- depends on VIDEO_DEV && ARCH_OMAP2
- select VIDEOBUF_DMA_SG
- ---help---
- This is a v4l2 driver for the TI OMAP2 camera capture interface
-
-config VIDEO_MX2_HOSTSUPPORT
- bool
-
-config VIDEO_MX2
- tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
- depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25)
- select VIDEOBUF2_DMA_CONTIG
- select VIDEO_MX2_HOSTSUPPORT
- ---help---
- This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
- Interface
-
-config VIDEO_ATMEL_ISI
- tristate "ATMEL Image Sensor Interface (ISI) support"
- depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
- select VIDEOBUF2_DMA_CONTIG
- ---help---
- This module makes the ATMEL Image Sensor Interface available
- as a v4l2 device.
-
-source "drivers/media/video/s5p-fimc/Kconfig"
-source "drivers/media/video/s5p-tv/Kconfig"
-
-endif # V4L_PLATFORM_DRIVERS
-endif # VIDEO_CAPTURE_DRIVERS
-
-menuconfig V4L_MEM2MEM_DRIVERS
- bool "Memory-to-memory multimedia devices"
- depends on VIDEO_V4L2
- default n
- ---help---
- Say Y here to enable selecting drivers for V4L devices that
- use system memory for both source and destination buffers, as opposed
- to capture and output drivers, which use memory buffers for just
- one of those.
-
-if V4L_MEM2MEM_DRIVERS
-
-config VIDEO_MEM2MEM_TESTDEV
- tristate "Virtual test device for mem2mem framework"
- depends on VIDEO_DEV && VIDEO_V4L2
- select VIDEOBUF2_VMALLOC
- select V4L2_MEM2MEM_DEV
- default n
- ---help---
- This is a virtual test device for the memory-to-memory driver
- framework.
-
-config VIDEO_SAMSUNG_S5P_G2D
- tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- default n
- ---help---
- This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
- 2d graphics accelerator.
-
-config VIDEO_SAMSUNG_S5P_JPEG
- tristate "Samsung S5P/Exynos4 JPEG codec driver (EXPERIMENTAL)"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P && EXPERIMENTAL
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- ---help---
- This is a v4l2 driver for Samsung S5P and EXYNOS4 JPEG codec
-
-config VIDEO_SAMSUNG_S5P_MFC
- tristate "Samsung S5P MFC 5.1 Video Codec"
- depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P
- select VIDEOBUF2_DMA_CONTIG
- default n
- help
- MFC 5.1 driver for V4L2.
-
-config VIDEO_MX2_EMMAPRP
- tristate "MX2 eMMa-PrP support"
- depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- MX2X chips have a PrP that can be used to process buffers from
- memory to memory. Operations include resizing and format
- conversion.
-
-endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
deleted file mode 100644
index b7da9faa3b0a..000000000000
--- a/drivers/media/video/Makefile
+++ /dev/null
@@ -1,218 +0,0 @@
-#
-# Makefile for the video capture/playback device drivers.
-#
-
-tuner-objs := tuner-core.o
-
-msp3400-objs := msp3400-driver.o msp3400-kthreads.o
-
-stkwebcam-objs := stk-webcam.o stk-sensor.o
-
-omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
-
-videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
- v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
-ifeq ($(CONFIG_COMPAT),y)
- videodev-objs += v4l2-compat-ioctl32.o
-endif
-
-# V4L2 core modules
-
-obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
-obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
-
-# Helper modules
-
-obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
-
-# All i2c modules must come first:
-
-obj-$(CONFIG_VIDEO_TUNER) += tuner.o
-obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
-obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
-obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
-obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
-obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
-obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
-obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
-obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
-obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
-obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
-obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
-obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
-obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
-obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
-obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
-obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
-obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
-obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o
-obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
-obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
-obj-$(CONFIG_VIDEO_BT819) += bt819.o
-obj-$(CONFIG_VIDEO_BT856) += bt856.o
-obj-$(CONFIG_VIDEO_BT866) += bt866.o
-obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
-obj-$(CONFIG_VIDEO_THS7303) += ths7303.o
-obj-$(CONFIG_VIDEO_VINO) += indycam.o
-obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
-obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
-obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o
-obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
-obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
-obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
-obj-$(CONFIG_VIDEO_M52790) += m52790.o
-obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
-obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
-obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
-obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
-obj-$(CONFIG_VIDEO_CX25840) += cx25840/
-obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
-obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
-obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
-obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
-obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
-obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
-obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
-obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
-obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
-obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
-obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
-obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
-obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
-obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
-obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/
-obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
-obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
-
-obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
-
-obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
-obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
-obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
-obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
-obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o
-obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o
-obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o
-obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.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
-
-# And now the v4l2 drivers:
-
-obj-$(CONFIG_VIDEO_BT848) += bt8xx/
-obj-$(CONFIG_VIDEO_ZORAN) += zoran/
-obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
-obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
-obj-$(CONFIG_VIDEO_W9966) += w9966.o
-obj-$(CONFIG_VIDEO_PMS) += pms.o
-obj-$(CONFIG_VIDEO_VINO) += vino.o
-obj-$(CONFIG_VIDEO_MEYE) += meye.o
-obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
-obj-$(CONFIG_VIDEO_CX88) += cx88/
-obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
-obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/
-obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
-obj-$(CONFIG_VIDEO_CX25821) += cx25821/
-obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
-obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
-obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
-obj-$(CONFIG_VIDEO_TM6000) += tm6000/
-obj-$(CONFIG_VIDEO_MXB) += mxb.o
-obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
-obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
-obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
-obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
-
-obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
-obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
-obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o
-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
-
-obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
-
-obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
-obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
-
-obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
-
-obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
-
-obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
-obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
-
-obj-$(CONFIG_USB_SN9C102) += sn9c102/
-obj-$(CONFIG_USB_PWC) += pwc/
-obj-$(CONFIG_USB_GSPCA) += gspca/
-
-obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
-
-obj-$(CONFIG_USB_S2255) += s2255drv.o
-
-obj-$(CONFIG_VIDEO_IVTV) += ivtv/
-obj-$(CONFIG_VIDEO_CX18) += cx18/
-
-obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
-obj-$(CONFIG_VIDEO_VIVI) += vivi.o
-obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o
-obj-$(CONFIG_VIDEO_CX23885) += cx23885/
-
-obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
-
-obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
-obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
-obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
-# soc-camera host drivers have to be linked after camera drivers
-obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o
-obj-$(CONFIG_VIDEO_MX2) += mx2_camera.o
-obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
-obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
-obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o
-obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
-obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
-obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
-
-obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
-
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
-
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
-
-obj-$(CONFIG_BLACKFIN) += blackfin/
-
-obj-$(CONFIG_ARCH_DAVINCI) += davinci/
-
-obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
-
-obj-$(CONFIG_VIDEO_AU0828) += au0828/
-
-obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/
-obj-$(CONFIG_VIDEO_SAA7164) += saa7164/
-
-obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
-
-obj-y += davinci/
-
-obj-$(CONFIG_ARCH_OMAP) += omap/
-
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
-ccflags-y += -I$(srctree)/drivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
deleted file mode 100644
index 57e87090388d..000000000000
--- a/drivers/media/video/adp1653.c
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * drivers/media/video/adp1653.c
- *
- * Copyright (C) 2008--2011 Nokia Corporation
- *
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
- *
- * Contributors:
- * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
- * Tuukka Toivonen <tuukkat76@gmail.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- * TODO:
- * - fault interrupt handling
- * - hardware strobe
- * - power doesn't need to be ON if all lights are off
- *
- */
-
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <media/adp1653.h>
-#include <media/v4l2-device.h>
-
-#define TIMEOUT_MAX 820000
-#define TIMEOUT_STEP 54600
-#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \
- * TIMEOUT_STEP)
-#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \
- / TIMEOUT_STEP)
-#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP)
-
-/* Write values into ADP1653 registers. */
-static int adp1653_update_hw(struct adp1653_flash *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- u8 out_sel;
- u8 config = 0;
- int rval;
-
- out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
- flash->indicator_intensity->val)
- << ADP1653_REG_OUT_SEL_ILED_SHIFT;
-
- switch (flash->led_mode->val) {
- case V4L2_FLASH_LED_MODE_NONE:
- break;
- case V4L2_FLASH_LED_MODE_FLASH:
- /* Flash mode, light on with strobe, duration from timer */
- config = ADP1653_REG_CONFIG_TMR_CFG;
- config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val)
- << ADP1653_REG_CONFIG_TMR_SET_SHIFT;
- break;
- case V4L2_FLASH_LED_MODE_TORCH:
- /* Torch mode, light immediately on, duration indefinite */
- out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
- flash->torch_intensity->val)
- << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
- break;
- }
-
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
- if (rval < 0)
- return rval;
-
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config);
- if (rval < 0)
- return rval;
-
- return 0;
-}
-
-static int adp1653_get_fault(struct adp1653_flash *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int fault;
- int rval;
-
- fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT);
- if (IS_ERR_VALUE(fault))
- return fault;
-
- flash->fault |= fault;
-
- if (!flash->fault)
- return 0;
-
- /* Clear faults. */
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
- if (IS_ERR_VALUE(rval))
- return rval;
-
- flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE;
-
- rval = adp1653_update_hw(flash);
- if (IS_ERR_VALUE(rval))
- return rval;
-
- return flash->fault;
-}
-
-static int adp1653_strobe(struct adp1653_flash *flash, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG(
- flash->indicator_intensity->val)
- << ADP1653_REG_OUT_SEL_ILED_SHIFT;
- int rval;
-
- if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH)
- return -EBUSY;
-
- if (!enable)
- return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL,
- out_sel);
-
- out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG(
- flash->flash_intensity->val)
- << ADP1653_REG_OUT_SEL_HPLED_SHIFT;
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel);
- if (rval)
- return rval;
-
- /* Software strobe using i2c */
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE,
- ADP1653_REG_SW_STROBE_SW_STROBE);
- if (rval)
- return rval;
- return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0);
-}
-
-/* --------------------------------------------------------------------------
- * V4L2 controls
- */
-
-static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct adp1653_flash *flash =
- container_of(ctrl->handler, struct adp1653_flash, ctrls);
- int rval;
-
- rval = adp1653_get_fault(flash);
- if (IS_ERR_VALUE(rval))
- return rval;
-
- ctrl->cur.val = 0;
-
- if (flash->fault & ADP1653_REG_FAULT_FLT_SCP)
- ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
- if (flash->fault & ADP1653_REG_FAULT_FLT_OT)
- ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
- if (flash->fault & ADP1653_REG_FAULT_FLT_TMR)
- ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
- if (flash->fault & ADP1653_REG_FAULT_FLT_OV)
- ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
-
- flash->fault = 0;
-
- return 0;
-}
-
-static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct adp1653_flash *flash =
- container_of(ctrl->handler, struct adp1653_flash, ctrls);
- int rval;
-
- rval = adp1653_get_fault(flash);
- if (IS_ERR_VALUE(rval))
- return rval;
- if ((rval & (ADP1653_REG_FAULT_FLT_SCP |
- ADP1653_REG_FAULT_FLT_OT |
- ADP1653_REG_FAULT_FLT_OV)) &&
- (ctrl->id == V4L2_CID_FLASH_STROBE ||
- ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY ||
- ctrl->id == V4L2_CID_FLASH_LED_MODE))
- return -EBUSY;
-
- switch (ctrl->id) {
- case V4L2_CID_FLASH_STROBE:
- return adp1653_strobe(flash, 1);
- case V4L2_CID_FLASH_STROBE_STOP:
- return adp1653_strobe(flash, 0);
- }
-
- return adp1653_update_hw(flash);
-}
-
-static const struct v4l2_ctrl_ops adp1653_ctrl_ops = {
- .g_volatile_ctrl = adp1653_get_ctrl,
- .s_ctrl = adp1653_set_ctrl,
-};
-
-static int adp1653_init_controls(struct adp1653_flash *flash)
-{
- struct v4l2_ctrl *fault;
-
- v4l2_ctrl_handler_init(&flash->ctrls, 9);
-
- flash->led_mode =
- v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_LED_MODE,
- V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0);
- v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_STROBE_SOURCE,
- V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0);
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
- flash->flash_timeout =
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN,
- flash->platform_data->max_flash_timeout,
- TIMEOUT_STEP,
- flash->platform_data->max_flash_timeout);
- flash->flash_intensity =
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_INTENSITY,
- ADP1653_FLASH_INTENSITY_MIN,
- flash->platform_data->max_flash_intensity,
- 1, flash->platform_data->max_flash_intensity);
- flash->torch_intensity =
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_TORCH_INTENSITY,
- ADP1653_TORCH_INTENSITY_MIN,
- flash->platform_data->max_torch_intensity,
- ADP1653_FLASH_INTENSITY_STEP,
- flash->platform_data->max_torch_intensity);
- flash->indicator_intensity =
- v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_INDICATOR_INTENSITY,
- ADP1653_INDICATOR_INTENSITY_MIN,
- flash->platform_data->max_indicator_intensity,
- ADP1653_INDICATOR_INTENSITY_STEP,
- ADP1653_INDICATOR_INTENSITY_MIN);
- fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops,
- V4L2_CID_FLASH_FAULT, 0,
- V4L2_FLASH_FAULT_OVER_VOLTAGE
- | V4L2_FLASH_FAULT_OVER_TEMPERATURE
- | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
-
- if (flash->ctrls.error)
- return flash->ctrls.error;
-
- fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
-
- flash->subdev.ctrl_handler = &flash->ctrls;
- return 0;
-}
-
-/* --------------------------------------------------------------------------
- * V4L2 subdev operations
- */
-
-static int
-adp1653_init_device(struct adp1653_flash *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int rval;
-
- /* Clear FAULT register by writing zero to OUT_SEL */
- rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0);
- if (rval < 0) {
- dev_err(&client->dev, "failed writing fault register\n");
- return -EIO;
- }
-
- mutex_lock(flash->ctrls.lock);
- /* Reset faults before reading new ones. */
- flash->fault = 0;
- rval = adp1653_get_fault(flash);
- mutex_unlock(flash->ctrls.lock);
- if (rval > 0) {
- dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
- return -EIO;
- }
-
- mutex_lock(flash->ctrls.lock);
- rval = adp1653_update_hw(flash);
- mutex_unlock(flash->ctrls.lock);
- if (rval) {
- dev_err(&client->dev,
- "adp1653_update_hw failed at %s\n", __func__);
- return -EIO;
- }
-
- return 0;
-}
-
-static int
-__adp1653_set_power(struct adp1653_flash *flash, int on)
-{
- int ret;
-
- ret = flash->platform_data->power(&flash->subdev, on);
- if (ret < 0)
- return ret;
-
- if (!on)
- return 0;
-
- ret = adp1653_init_device(flash);
- if (ret < 0)
- flash->platform_data->power(&flash->subdev, 0);
-
- return ret;
-}
-
-static int
-adp1653_set_power(struct v4l2_subdev *subdev, int on)
-{
- struct adp1653_flash *flash = to_adp1653_flash(subdev);
- int ret = 0;
-
- mutex_lock(&flash->power_lock);
-
- /* If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (flash->power_count == !on) {
- ret = __adp1653_set_power(flash, !!on);
- if (ret < 0)
- goto done;
- }
-
- /* Update the power count. */
- flash->power_count += on ? 1 : -1;
- WARN_ON(flash->power_count < 0);
-
-done:
- mutex_unlock(&flash->power_lock);
- return ret;
-}
-
-static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return adp1653_set_power(sd, 1);
-}
-
-static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return adp1653_set_power(sd, 0);
-}
-
-static const struct v4l2_subdev_core_ops adp1653_core_ops = {
- .s_power = adp1653_set_power,
-};
-
-static const struct v4l2_subdev_ops adp1653_ops = {
- .core = &adp1653_core_ops,
-};
-
-static const struct v4l2_subdev_internal_ops adp1653_internal_ops = {
- .open = adp1653_open,
- .close = adp1653_close,
-};
-
-/* --------------------------------------------------------------------------
- * I2C driver
- */
-#ifdef CONFIG_PM
-
-static int adp1653_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct adp1653_flash *flash = to_adp1653_flash(subdev);
-
- if (!flash->power_count)
- return 0;
-
- return __adp1653_set_power(flash, 0);
-}
-
-static int adp1653_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct adp1653_flash *flash = to_adp1653_flash(subdev);
-
- if (!flash->power_count)
- return 0;
-
- return __adp1653_set_power(flash, 1);
-}
-
-#else
-
-#define adp1653_suspend NULL
-#define adp1653_resume NULL
-
-#endif /* CONFIG_PM */
-
-static int adp1653_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- struct adp1653_flash *flash;
- int ret;
-
- /* we couldn't work without platform data */
- if (client->dev.platform_data == NULL)
- return -ENODEV;
-
- flash = kzalloc(sizeof(*flash), GFP_KERNEL);
- if (flash == NULL)
- return -ENOMEM;
-
- flash->platform_data = client->dev.platform_data;
-
- mutex_init(&flash->power_lock);
-
- v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops);
- flash->subdev.internal_ops = &adp1653_internal_ops;
- flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- ret = adp1653_init_controls(flash);
- if (ret)
- goto free_and_quit;
-
- ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
- if (ret < 0)
- goto free_and_quit;
-
- flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
-
- return 0;
-
-free_and_quit:
- v4l2_ctrl_handler_free(&flash->ctrls);
- kfree(flash);
- return ret;
-}
-
-static int __exit adp1653_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct adp1653_flash *flash = to_adp1653_flash(subdev);
-
- v4l2_device_unregister_subdev(&flash->subdev);
- v4l2_ctrl_handler_free(&flash->ctrls);
- media_entity_cleanup(&flash->subdev.entity);
- kfree(flash);
- return 0;
-}
-
-static const struct i2c_device_id adp1653_id_table[] = {
- { ADP1653_NAME, 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
-
-static struct dev_pm_ops adp1653_pm_ops = {
- .suspend = adp1653_suspend,
- .resume = adp1653_resume,
-};
-
-static struct i2c_driver adp1653_i2c_driver = {
- .driver = {
- .name = ADP1653_NAME,
- .pm = &adp1653_pm_ops,
- },
- .probe = adp1653_probe,
- .remove = __exit_p(adp1653_remove),
- .id_table = adp1653_id_table,
-};
-
-module_i2c_driver(adp1653_i2c_driver);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
-MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c
deleted file mode 100644
index c4b03572dce8..000000000000
--- a/drivers/media/video/as3645a.c
+++ /dev/null
@@ -1,888 +0,0 @@
-/*
- * drivers/media/video/as3645a.c - AS3645A and LM3555 flash controllers driver
- *
- * Copyright (C) 2008-2011 Nokia Corporation
- * Copyright (c) 2011, Intel Corporation.
- *
- * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- * TODO:
- * - Check hardware FSTROBE control when sensor driver add support for this
- *
- */
-
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include <media/as3645a.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-
-#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50)
-#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100)
-
-/* Register definitions */
-
-/* Read-only Design info register: Reset state: xxxx 0001 */
-#define AS_DESIGN_INFO_REG 0x00
-#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4))
-#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f)
-
-/* Read-only Version control register: Reset state: 0000 0000
- * for first engineering samples
- */
-#define AS_VERSION_CONTROL_REG 0x01
-#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4))
-#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f)
-
-/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */
-#define AS_INDICATOR_AND_TIMER_REG 0x02
-#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0
-#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4
-#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6
-
-/* Read / Write (Current set register): Reset state: 0110 1001 */
-#define AS_CURRENT_SET_REG 0x03
-#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0
-#define AS_CURRENT_LED_DET_ON (1 << 3)
-#define AS_CURRENT_FLASH_CURRENT_SHIFT 4
-
-/* Read / Write (Control register): Reset state: 1011 0100 */
-#define AS_CONTROL_REG 0x04
-#define AS_CONTROL_MODE_SETTING_SHIFT 0
-#define AS_CONTROL_STROBE_ON (1 << 2)
-#define AS_CONTROL_OUT_ON (1 << 3)
-#define AS_CONTROL_EXT_TORCH_ON (1 << 4)
-#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5)
-#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5)
-#define AS_CONTROL_COIL_PEAK_SHIFT 6
-
-/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */
-#define AS_FAULT_INFO_REG 0x05
-#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1)
-#define AS_FAULT_INFO_INDICATOR_LED (1 << 2)
-#define AS_FAULT_INFO_LED_AMOUNT (1 << 3)
-#define AS_FAULT_INFO_TIMEOUT (1 << 4)
-#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5)
-#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6)
-#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7)
-
-/* Boost register */
-#define AS_BOOST_REG 0x0d
-#define AS_BOOST_CURRENT_DISABLE (0 << 0)
-#define AS_BOOST_CURRENT_ENABLE (1 << 0)
-
-/* Password register is used to unlock boost register writing */
-#define AS_PASSWORD_REG 0x0f
-#define AS_PASSWORD_UNLOCK_VALUE 0x55
-
-enum as_mode {
- AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT,
- AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT,
- AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT,
- AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT,
-};
-
-/*
- * struct as3645a
- *
- * @subdev: V4L2 subdev
- * @pdata: Flash platform data
- * @power_lock: Protects power_count
- * @power_count: Power reference count
- * @led_mode: V4L2 flash LED mode
- * @timeout: Flash timeout in microseconds
- * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum
- * values are 400mA for two LEDs and 500mA for one LED.
- * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA)
- * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA)
- * @strobe_source: Flash strobe source (software or external)
- */
-struct as3645a {
- struct v4l2_subdev subdev;
- const struct as3645a_platform_data *pdata;
-
- struct mutex power_lock;
- int power_count;
-
- /* Controls */
- struct v4l2_ctrl_handler ctrls;
-
- enum v4l2_flash_led_mode led_mode;
- unsigned int timeout;
- u8 flash_current;
- u8 assist_current;
- u8 indicator_current;
- enum v4l2_flash_strobe_source strobe_source;
-};
-
-#define to_as3645a(sd) container_of(sd, struct as3645a, subdev)
-
-/* Return negative errno else zero on success */
-static int as3645a_write(struct as3645a *flash, u8 addr, u8 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int rval;
-
- rval = i2c_smbus_write_byte_data(client, addr, val);
-
- dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val,
- rval < 0 ? "fail" : "ok");
-
- return rval;
-}
-
-/* Return negative errno else a data byte received from the device. */
-static int as3645a_read(struct as3645a *flash, u8 addr)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int rval;
-
- rval = i2c_smbus_read_byte_data(client, addr);
-
- dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval,
- rval < 0 ? "fail" : "ok");
-
- return rval;
-}
-
-/* -----------------------------------------------------------------------------
- * Hardware configuration and trigger
- */
-
-/*
- * as3645a_set_config - Set flash configuration registers
- * @flash: The flash
- *
- * Configure the hardware with flash, assist and indicator currents, as well as
- * flash timeout.
- *
- * Return 0 on success, or a negative error code if an I2C communication error
- * occurred.
- */
-static int as3645a_set_config(struct as3645a *flash)
-{
- int ret;
- u8 val;
-
- val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT)
- | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT)
- | AS_CURRENT_LED_DET_ON;
-
- ret = as3645a_write(flash, AS_CURRENT_SET_REG, val);
- if (ret < 0)
- return ret;
-
- val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000)
- << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT;
-
- val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT)
- | ((flash->indicator_current ? flash->indicator_current - 1 : 0)
- << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT);
-
- return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val);
-}
-
-/*
- * as3645a_set_control - Set flash control register
- * @flash: The flash
- * @mode: Desired output mode
- * @on: Desired output state
- *
- * Configure the hardware with output mode and state.
- *
- * Return 0 on success, or a negative error code if an I2C communication error
- * occurred.
- */
-static int
-as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on)
-{
- u8 reg;
-
- /* Configure output parameters and operation mode. */
- reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT)
- | (on ? AS_CONTROL_OUT_ON : 0)
- | mode;
-
- if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH &&
- flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) {
- reg |= AS_CONTROL_STROBE_TYPE_LEVEL
- | AS_CONTROL_STROBE_ON;
- }
-
- return as3645a_write(flash, AS_CONTROL_REG, reg);
-}
-
-/*
- * as3645a_set_output - Configure output and operation mode
- * @flash: Flash controller
- * @strobe: Strobe the flash (only valid in flash mode)
- *
- * Turn the LEDs output on/off and set the operation mode based on the current
- * parameters.
- *
- * The AS3645A can't control the indicator LED independently of the flash/torch
- * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the
- * chip to indicator mode. Otherwise set it to assist light (torch) or flash
- * mode.
- *
- * In indicator and assist modes, turn the output on/off based on the indicator
- * and torch currents. In software strobe flash mode, turn the output on/off
- * based on the strobe parameter.
- */
-static int as3645a_set_output(struct as3645a *flash, bool strobe)
-{
- enum as_mode mode;
- bool on;
-
- switch (flash->led_mode) {
- case V4L2_FLASH_LED_MODE_NONE:
- on = flash->indicator_current != 0;
- mode = AS_MODE_INDICATOR;
- break;
- case V4L2_FLASH_LED_MODE_TORCH:
- on = true;
- mode = AS_MODE_ASSIST;
- break;
- case V4L2_FLASH_LED_MODE_FLASH:
- on = strobe;
- mode = AS_MODE_FLASH;
- break;
- default:
- BUG();
- }
-
- /* Configure output parameters and operation mode. */
- return as3645a_set_control(flash, mode, on);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 controls
- */
-
-static int as3645a_is_active(struct as3645a *flash)
-{
- int ret;
-
- ret = as3645a_read(flash, AS_CONTROL_REG);
- return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON);
-}
-
-static int as3645a_read_fault(struct as3645a *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int rval;
-
- /* NOTE: reading register clear fault status */
- rval = as3645a_read(flash, AS_FAULT_INFO_REG);
- if (rval < 0)
- return rval;
-
- if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
- dev_dbg(&client->dev, "Inductor Peak limit fault\n");
-
- if (rval & AS_FAULT_INFO_INDICATOR_LED)
- dev_dbg(&client->dev, "Indicator LED fault: "
- "Short circuit or open loop\n");
-
- dev_dbg(&client->dev, "%u connected LEDs\n",
- rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1);
-
- if (rval & AS_FAULT_INFO_TIMEOUT)
- dev_dbg(&client->dev, "Timeout fault\n");
-
- if (rval & AS_FAULT_INFO_OVER_TEMPERATURE)
- dev_dbg(&client->dev, "Over temperature fault\n");
-
- if (rval & AS_FAULT_INFO_SHORT_CIRCUIT)
- dev_dbg(&client->dev, "Short circuit fault\n");
-
- if (rval & AS_FAULT_INFO_OVER_VOLTAGE)
- dev_dbg(&client->dev, "Over voltage fault: "
- "Indicates missing capacitor or open connection\n");
-
- return rval;
-}
-
-static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct as3645a *flash =
- container_of(ctrl->handler, struct as3645a, ctrls);
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int value;
-
- switch (ctrl->id) {
- case V4L2_CID_FLASH_FAULT:
- value = as3645a_read_fault(flash);
- if (value < 0)
- return value;
-
- ctrl->cur.val = 0;
- if (value & AS_FAULT_INFO_SHORT_CIRCUIT)
- ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
- if (value & AS_FAULT_INFO_OVER_TEMPERATURE)
- ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE;
- if (value & AS_FAULT_INFO_TIMEOUT)
- ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT;
- if (value & AS_FAULT_INFO_OVER_VOLTAGE)
- ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE;
- if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT)
- ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT;
- if (value & AS_FAULT_INFO_INDICATOR_LED)
- ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR;
- break;
-
- case V4L2_CID_FLASH_STROBE_STATUS:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
- ctrl->cur.val = 0;
- break;
- }
-
- value = as3645a_is_active(flash);
- if (value < 0)
- return value;
-
- ctrl->cur.val = value;
- break;
- }
-
- dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val);
-
- return 0;
-}
-
-static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct as3645a *flash =
- container_of(ctrl->handler, struct as3645a, ctrls);
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int ret;
-
- dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val);
-
- /* If a control that doesn't apply to the current mode is modified,
- * we store the value and return immediately. The setting will be
- * applied when the LED mode is changed. Otherwise we apply the setting
- * immediately.
- */
-
- switch (ctrl->id) {
- case V4L2_CID_FLASH_LED_MODE:
- if (flash->indicator_current)
- return -EBUSY;
-
- ret = as3645a_set_config(flash);
- if (ret < 0)
- return ret;
-
- flash->led_mode = ctrl->val;
- return as3645a_set_output(flash, false);
-
- case V4L2_CID_FLASH_STROBE_SOURCE:
- flash->strobe_source = ctrl->val;
-
- /* Applies to flash mode only. */
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- break;
-
- return as3645a_set_output(flash, false);
-
- case V4L2_CID_FLASH_STROBE:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- return -EBUSY;
-
- return as3645a_set_output(flash, true);
-
- case V4L2_CID_FLASH_STROBE_STOP:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- return -EBUSY;
-
- return as3645a_set_output(flash, false);
-
- case V4L2_CID_FLASH_TIMEOUT:
- flash->timeout = ctrl->val;
-
- /* Applies to flash mode only. */
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- break;
-
- return as3645a_set_config(flash);
-
- case V4L2_CID_FLASH_INTENSITY:
- flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN)
- / AS3645A_FLASH_INTENSITY_STEP;
-
- /* Applies to flash mode only. */
- if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH)
- break;
-
- return as3645a_set_config(flash);
-
- case V4L2_CID_FLASH_TORCH_INTENSITY:
- flash->assist_current =
- (ctrl->val - AS3645A_TORCH_INTENSITY_MIN)
- / AS3645A_TORCH_INTENSITY_STEP;
-
- /* Applies to torch mode only. */
- if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH)
- break;
-
- return as3645a_set_config(flash);
-
- case V4L2_CID_FLASH_INDICATOR_INTENSITY:
- if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE)
- return -EBUSY;
-
- flash->indicator_current =
- (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN)
- / AS3645A_INDICATOR_INTENSITY_STEP;
-
- ret = as3645a_set_config(flash);
- if (ret < 0)
- return ret;
-
- if ((ctrl->val == 0) == (ctrl->cur.val == 0))
- break;
-
- return as3645a_set_output(flash, false);
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops as3645a_ctrl_ops = {
- .g_volatile_ctrl = as3645a_get_ctrl,
- .s_ctrl = as3645a_set_ctrl,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-/* Put device into know state. */
-static int as3645a_setup(struct as3645a *flash)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev);
- int ret;
-
- /* clear errors */
- ret = as3645a_read(flash, AS_FAULT_INFO_REG);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Fault info: %02x\n", ret);
-
- ret = as3645a_set_config(flash);
- if (ret < 0)
- return ret;
-
- ret = as3645a_set_output(flash, false);
- if (ret < 0)
- return ret;
-
- /* read status */
- ret = as3645a_read_fault(flash);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n",
- as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG));
- dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n",
- as3645a_read(flash, AS_CURRENT_SET_REG));
- dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n",
- as3645a_read(flash, AS_CONTROL_REG));
-
- return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0;
-}
-
-static int __as3645a_set_power(struct as3645a *flash, int on)
-{
- int ret;
-
- if (!on)
- as3645a_set_control(flash, AS_MODE_EXT_TORCH, false);
-
- if (flash->pdata->set_power) {
- ret = flash->pdata->set_power(&flash->subdev, on);
- if (ret < 0)
- return ret;
- }
-
- if (!on)
- return 0;
-
- ret = as3645a_setup(flash);
- if (ret < 0) {
- if (flash->pdata->set_power)
- flash->pdata->set_power(&flash->subdev, 0);
- }
-
- return ret;
-}
-
-static int as3645a_set_power(struct v4l2_subdev *sd, int on)
-{
- struct as3645a *flash = to_as3645a(sd);
- int ret = 0;
-
- mutex_lock(&flash->power_lock);
-
- if (flash->power_count == !on) {
- ret = __as3645a_set_power(flash, !!on);
- if (ret < 0)
- goto done;
- }
-
- flash->power_count += on ? 1 : -1;
- WARN_ON(flash->power_count < 0);
-
-done:
- mutex_unlock(&flash->power_lock);
- return ret;
-}
-
-static int as3645a_registered(struct v4l2_subdev *sd)
-{
- struct as3645a *flash = to_as3645a(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int rval, man, model, rfu, version;
- const char *vendor;
-
- /* Power up the flash driver and read manufacturer ID, model ID, RFU
- * and version.
- */
- rval = as3645a_set_power(&flash->subdev, 1);
- if (rval < 0)
- return rval;
-
- rval = as3645a_read(flash, AS_DESIGN_INFO_REG);
- if (rval < 0)
- goto power_off;
-
- man = AS_DESIGN_INFO_FACTORY(rval);
- model = AS_DESIGN_INFO_MODEL(rval);
-
- rval = as3645a_read(flash, AS_VERSION_CONTROL_REG);
- if (rval < 0)
- goto power_off;
-
- rfu = AS_VERSION_CONTROL_RFU(rval);
- version = AS_VERSION_CONTROL_VERSION(rval);
-
- /* Verify the chip model and version. */
- if (model != 0x01 || rfu != 0x00) {
- dev_err(&client->dev, "AS3645A not detected "
- "(model %d rfu %d)\n", model, rfu);
- rval = -ENODEV;
- goto power_off;
- }
-
- switch (man) {
- case 1:
- vendor = "AMS, Austria Micro Systems";
- break;
- case 2:
- vendor = "ADI, Analog Devices Inc.";
- break;
- case 3:
- vendor = "NSC, National Semiconductor";
- break;
- case 4:
- vendor = "NXP";
- break;
- case 5:
- vendor = "TI, Texas Instrument";
- break;
- default:
- vendor = "Unknown";
- }
-
- dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor,
- man, version);
-
- rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE);
- if (rval < 0)
- goto power_off;
-
- rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE);
- if (rval < 0)
- goto power_off;
-
- /* Setup default values. This makes sure that the chip is in a known
- * state, in case the power rail can't be controlled.
- */
- rval = as3645a_setup(flash);
-
-power_off:
- as3645a_set_power(&flash->subdev, 0);
-
- return rval;
-}
-
-static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return as3645a_set_power(sd, 1);
-}
-
-static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return as3645a_set_power(sd, 0);
-}
-
-static const struct v4l2_subdev_core_ops as3645a_core_ops = {
- .s_power = as3645a_set_power,
-};
-
-static const struct v4l2_subdev_ops as3645a_ops = {
- .core = &as3645a_core_ops,
-};
-
-static const struct v4l2_subdev_internal_ops as3645a_internal_ops = {
- .registered = as3645a_registered,
- .open = as3645a_open,
- .close = as3645a_close,
-};
-
-/* -----------------------------------------------------------------------------
- * I2C driver
- */
-#ifdef CONFIG_PM
-
-static int as3645a_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct as3645a *flash = to_as3645a(subdev);
- int rval;
-
- if (flash->power_count == 0)
- return 0;
-
- rval = __as3645a_set_power(flash, 0);
-
- dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok");
-
- return rval;
-}
-
-static int as3645a_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct as3645a *flash = to_as3645a(subdev);
- int rval;
-
- if (flash->power_count == 0)
- return 0;
-
- rval = __as3645a_set_power(flash, 1);
-
- dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok");
-
- return rval;
-}
-
-#else
-
-#define as3645a_suspend NULL
-#define as3645a_resume NULL
-
-#endif /* CONFIG_PM */
-
-/*
- * as3645a_init_controls - Create controls
- * @flash: The flash
- *
- * The number of LEDs reported in platform data is used to compute default
- * limits. Parameters passed through platform data can override those limits.
- */
-static int __devinit as3645a_init_controls(struct as3645a *flash)
-{
- const struct as3645a_platform_data *pdata = flash->pdata;
- struct v4l2_ctrl *ctrl;
- int maximum;
-
- v4l2_ctrl_handler_init(&flash->ctrls, 10);
-
- /* V4L2_CID_FLASH_LED_MODE */
- v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_LED_MODE, 2, ~7,
- V4L2_FLASH_LED_MODE_NONE);
-
- /* V4L2_CID_FLASH_STROBE_SOURCE */
- v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_STROBE_SOURCE,
- pdata->ext_strobe ? 1 : 0,
- pdata->ext_strobe ? ~3 : ~1,
- V4L2_FLASH_STROBE_SOURCE_SOFTWARE);
-
- flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE;
-
- /* V4L2_CID_FLASH_STROBE */
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_STROBE, 0, 0, 0, 0);
-
- /* V4L2_CID_FLASH_STROBE_STOP */
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0);
-
- /* V4L2_CID_FLASH_STROBE_STATUS */
- ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1);
- if (ctrl != NULL)
- ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
-
- /* V4L2_CID_FLASH_TIMEOUT */
- maximum = pdata->timeout_max;
-
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN,
- maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum);
-
- flash->timeout = maximum;
-
- /* V4L2_CID_FLASH_INTENSITY */
- maximum = pdata->flash_max_current;
-
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN,
- maximum, AS3645A_FLASH_INTENSITY_STEP, maximum);
-
- flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN)
- / AS3645A_FLASH_INTENSITY_STEP;
-
- /* V4L2_CID_FLASH_TORCH_INTENSITY */
- maximum = pdata->torch_max_current;
-
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_TORCH_INTENSITY,
- AS3645A_TORCH_INTENSITY_MIN, maximum,
- AS3645A_TORCH_INTENSITY_STEP,
- AS3645A_TORCH_INTENSITY_MIN);
-
- flash->assist_current = 0;
-
- /* V4L2_CID_FLASH_INDICATOR_INTENSITY */
- v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_INDICATOR_INTENSITY,
- AS3645A_INDICATOR_INTENSITY_MIN,
- AS3645A_INDICATOR_INTENSITY_MAX,
- AS3645A_INDICATOR_INTENSITY_STEP,
- AS3645A_INDICATOR_INTENSITY_MIN);
-
- flash->indicator_current = 0;
-
- /* V4L2_CID_FLASH_FAULT */
- ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops,
- V4L2_CID_FLASH_FAULT, 0,
- V4L2_FLASH_FAULT_OVER_VOLTAGE |
- V4L2_FLASH_FAULT_TIMEOUT |
- V4L2_FLASH_FAULT_OVER_TEMPERATURE |
- V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0);
- if (ctrl != NULL)
- ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
-
- flash->subdev.ctrl_handler = &flash->ctrls;
-
- return flash->ctrls.error;
-}
-
-static int __devinit as3645a_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- struct as3645a *flash;
- int ret;
-
- if (client->dev.platform_data == NULL)
- return -ENODEV;
-
- flash = kzalloc(sizeof(*flash), GFP_KERNEL);
- if (flash == NULL)
- return -ENOMEM;
-
- flash->pdata = client->dev.platform_data;
-
- v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops);
- flash->subdev.internal_ops = &as3645a_internal_ops;
- flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- ret = as3645a_init_controls(flash);
- if (ret < 0)
- goto done;
-
- ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0);
- if (ret < 0)
- goto done;
-
- flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH;
-
- mutex_init(&flash->power_lock);
-
- flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
-
-done:
- if (ret < 0) {
- v4l2_ctrl_handler_free(&flash->ctrls);
- kfree(flash);
- }
-
- return ret;
-}
-
-static int __devexit as3645a_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct as3645a *flash = to_as3645a(subdev);
-
- v4l2_device_unregister_subdev(subdev);
- v4l2_ctrl_handler_free(&flash->ctrls);
- media_entity_cleanup(&flash->subdev.entity);
- mutex_destroy(&flash->power_lock);
- kfree(flash);
-
- return 0;
-}
-
-static const struct i2c_device_id as3645a_id_table[] = {
- { AS3645A_NAME, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2c, as3645a_id_table);
-
-static const struct dev_pm_ops as3645a_pm_ops = {
- .suspend = as3645a_suspend,
- .resume = as3645a_resume,
-};
-
-static struct i2c_driver as3645a_i2c_driver = {
- .driver = {
- .name = AS3645A_NAME,
- .pm = &as3645a_pm_ops,
- },
- .probe = as3645a_probe,
- .remove = __devexit_p(as3645a_remove),
- .id_table = as3645a_id_table,
-};
-
-module_i2c_driver(as3645a_i2c_driver);
-
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
deleted file mode 100644
index 23f7fd22f0eb..000000000000
--- a/drivers/media/video/au0828/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-
-config VIDEO_AU0828
- tristate "Auvitek AU0828 support"
- depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
- depends on DVB_CAPTURE_DRIVERS
- select I2C_ALGOBIT
- select VIDEO_TVEEPROM
- select VIDEOBUF_VMALLOC
- select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE
- select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- ---help---
- This is a video4linux driver for Auvitek's USB device.
-
- To compile this driver as a module, choose M here: the
- module will be called au0828
diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
deleted file mode 100644
index bd22223f8d9f..000000000000
--- a/drivers/media/video/au0828/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o au0828-vbi.o
-
-obj-$(CONFIG_VIDEO_AU0828) += au0828.o
-
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-
-ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
deleted file mode 100644
index e3fe9a6637f6..000000000000
--- a/drivers/media/video/au0828/au0828-cards.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * Driver for the Auvitek USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "au0828.h"
-#include "au0828-cards.h"
-#include "au8522.h"
-#include "media/tuner.h"
-#include "media/v4l2-common.h"
-
-void hvr950q_cs5340_audio(void *priv, int enable)
-{
- /* Because the HVR-950q shares an i2s bus between the cs5340 and the
- au8522, we need to hold cs5340 in reset when using the au8522 */
- struct au0828_dev *dev = priv;
- if (enable == 1)
- au0828_set(dev, REG_000, 0x10);
- else
- au0828_clear(dev, REG_000, 0x10);
-}
-
-struct au0828_board au0828_boards[] = {
- [AU0828_BOARD_UNKNOWN] = {
- .name = "Unknown board",
- .tuner_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- },
- [AU0828_BOARD_HAUPPAUGE_HVR850] = {
- .name = "Hauppauge HVR850",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x61,
- .i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
- .input = {
- {
- .type = AU0828_VMUX_TELEVISION,
- .vmux = AU8522_COMPOSITE_CH4_SIF,
- .amux = AU8522_AUDIO_SIF,
- },
- {
- .type = AU0828_VMUX_COMPOSITE,
- .vmux = AU8522_COMPOSITE_CH1,
- .amux = AU8522_AUDIO_NONE,
- .audio_setup = hvr950q_cs5340_audio,
- },
- {
- .type = AU0828_VMUX_SVIDEO,
- .vmux = AU8522_SVIDEO_CH13,
- .amux = AU8522_AUDIO_NONE,
- .audio_setup = hvr950q_cs5340_audio,
- },
- },
- },
- [AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
- .name = "Hauppauge HVR950Q",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x61,
- /* The au0828 hardware i2c implementation does not properly
- support the xc5000's i2c clock stretching. So we need to
- lower the clock frequency enough where the 15us clock
- stretch fits inside of a normal clock cycle, or else the
- au0828 fails to set the STOP bit. A 30 KHz clock puts the
- clock pulse width at 18us */
- .i2c_clk_divider = AU0828_I2C_CLK_30KHZ,
- .input = {
- {
- .type = AU0828_VMUX_TELEVISION,
- .vmux = AU8522_COMPOSITE_CH4_SIF,
- .amux = AU8522_AUDIO_SIF,
- },
- {
- .type = AU0828_VMUX_COMPOSITE,
- .vmux = AU8522_COMPOSITE_CH1,
- .amux = AU8522_AUDIO_NONE,
- .audio_setup = hvr950q_cs5340_audio,
- },
- {
- .type = AU0828_VMUX_SVIDEO,
- .vmux = AU8522_SVIDEO_CH13,
- .amux = AU8522_AUDIO_NONE,
- .audio_setup = hvr950q_cs5340_audio,
- },
- },
- },
- [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
- .name = "Hauppauge HVR950Q rev xxF8",
- .tuner_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
- },
- [AU0828_BOARD_DVICO_FUSIONHDTV7] = {
- .name = "DViCO FusionHDTV USB",
- .tuner_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
- },
- [AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
- .name = "Hauppauge Woodbury",
- .tuner_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
- },
-};
-
-/* Tuner callback function for au0828 boards. Currently only needed
- * for HVR1500Q, which has an xc5000 tuner.
- */
-int au0828_tuner_callback(void *priv, int component, int command, int arg)
-{
- struct au0828_dev *dev = priv;
-
- dprintk(1, "%s()\n", __func__);
-
- switch (dev->boardnr) {
- case AU0828_BOARD_HAUPPAUGE_HVR850:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
- case AU0828_BOARD_DVICO_FUSIONHDTV7:
- if (command == 0) {
- /* Tuner Reset Command from xc5000 */
- /* Drive the tuner into reset and out */
- au0828_clear(dev, REG_001, 2);
- mdelay(10);
- au0828_set(dev, REG_001, 2);
- mdelay(10);
- return 0;
- } else {
- printk(KERN_ERR
- "%s(): Unknown command.\n", __func__);
- return -EINVAL;
- }
- break;
- }
-
- return 0; /* Should never be here */
-}
-
-static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
-{
- struct tveeprom tv;
-
- tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
- dev->board.tuner_type = tv.tuner_type;
-
- /* Make sure we support the board model */
- switch (tv.model) {
- case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
- case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
- case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
- case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
- case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
- case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
- case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
- case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
- case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
- case 72261: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
- case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
- case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
- break;
- default:
- printk(KERN_WARNING "%s: warning: "
- "unknown hauppauge model #%d\n", __func__, tv.model);
- break;
- }
-
- printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
- __func__, tv.model);
-}
-
-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;
-
- dprintk(1, "%s()\n", __func__);
-
- memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board));
-
- if (dev->i2c_rc == 0) {
- dev->i2c_client.addr = 0xa0 >> 1;
- tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
- }
-
- switch (dev->boardnr) {
- case AU0828_BOARD_HAUPPAUGE_HVR850:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
- case AU0828_BOARD_HAUPPAUGE_WOODBURY:
- if (dev->i2c_rc == 0)
- hauppauge_eeprom(dev, eeprom+0xa0);
- break;
- }
-
- if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
- /* Load the analog demodulator driver (note this would need to
- be abstracted out if we ever need to support a different
- demod) */
- sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "au8522", 0x8e >> 1, NULL);
- if (sd == NULL)
- printk(KERN_ERR "analog subdev registration failed\n");
- }
-
- /* Setup tuners */
- if (dev->board.tuner_type != TUNER_ABSENT) {
- /* Load the tuner module, which does the attach */
- sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tuner", dev->board.tuner_addr, NULL);
- if (sd == NULL)
- printk(KERN_ERR "tuner subdev registration fail\n");
-
- tun_setup.mode_mask = mode_mask;
- tun_setup.type = dev->board.tuner_type;
- tun_setup.addr = dev->board.tuner_addr;
- tun_setup.tuner_callback = au0828_tuner_callback;
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
- &tun_setup);
- }
-}
-
-/*
- * The bridge has between 8 and 12 gpios.
- * Regs 1 and 0 deal with output enables.
- * Regs 3 and 2 deal with direction.
- */
-void au0828_gpio_setup(struct au0828_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- switch (dev->boardnr) {
- case AU0828_BOARD_HAUPPAUGE_HVR850:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
- case AU0828_BOARD_HAUPPAUGE_WOODBURY:
- /* GPIO's
- * 4 - CS5340
- * 5 - AU8522 Demodulator
- * 6 - eeprom W/P
- * 7 - power supply
- * 9 - XC5000 Tuner
- */
-
- /* Into reset */
- au0828_write(dev, REG_003, 0x02);
- au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
- au0828_write(dev, REG_001, 0x0);
- au0828_write(dev, REG_000, 0x0);
- msleep(100);
-
- /* Out of reset (leave the cs5340 in reset until needed) */
- au0828_write(dev, REG_003, 0x02);
- au0828_write(dev, REG_001, 0x02);
- au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
- au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
-
- msleep(250);
- break;
- case AU0828_BOARD_DVICO_FUSIONHDTV7:
- /* GPIO's
- * 6 - ?
- * 8 - AU8522 Demodulator
- * 9 - XC5000 Tuner
- */
-
- /* Into reset */
- au0828_write(dev, REG_003, 0x02);
- au0828_write(dev, REG_002, 0xa0);
- au0828_write(dev, REG_001, 0x0);
- au0828_write(dev, REG_000, 0x0);
- msleep(100);
-
- /* Out of reset */
- au0828_write(dev, REG_003, 0x02);
- au0828_write(dev, REG_002, 0xa0);
- au0828_write(dev, REG_001, 0x02);
- au0828_write(dev, REG_000, 0xa0);
- msleep(250);
- break;
- }
-}
-
-/* table of devices that work with this driver */
-struct usb_device_id au0828_usb_id_table[] = {
- { USB_DEVICE(0x2040, 0x7200),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x7240),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 },
- { USB_DEVICE(0x0fe9, 0xd620),
- .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 },
- { USB_DEVICE(0x2040, 0x7210),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x7217),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x721b),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x721e),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x721f),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x7280),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x0fd9, 0x0008),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x7201),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
- { USB_DEVICE(0x2040, 0x7211),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
- { USB_DEVICE(0x2040, 0x7281),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
- { USB_DEVICE(0x05e1, 0x0480),
- .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
- { USB_DEVICE(0x2040, 0x8200),
- .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
- { USB_DEVICE(0x2040, 0x7260),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { USB_DEVICE(0x2040, 0x7213),
- .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
- { },
-};
-
-MODULE_DEVICE_TABLE(usb, au0828_usb_id_table);
diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
deleted file mode 100644
index 1e4ce5068ec2..000000000000
--- a/drivers/media/video/au0828/au0828-core.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Driver for the Auvitek USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <linux/mutex.h>
-
-#include "au0828.h"
-
-/*
- * 1 = General debug messages
- * 2 = USB handling
- * 4 = I2C related
- * 8 = Bridge related
- */
-int au0828_debug;
-module_param_named(debug, au0828_debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable debug messages");
-
-static unsigned int disable_usb_speed_check;
-module_param(disable_usb_speed_check, int, 0444);
-MODULE_PARM_DESC(disable_usb_speed_check,
- "override min bandwidth requirement of 480M bps");
-
-#define _AU0828_BULKPIPE 0x03
-#define _BULKPIPESIZE 0xffff
-
-static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
- u16 index, unsigned char *cp, u16 size);
-static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
- u16 index, unsigned char *cp, u16 size);
-
-/* USB Direction */
-#define CMD_REQUEST_IN 0x00
-#define CMD_REQUEST_OUT 0x01
-
-u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
-{
- recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1);
- dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, dev->ctrlmsg[0]);
- return dev->ctrlmsg[0];
-}
-
-u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
-{
- dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
- return send_control_msg(dev, CMD_REQUEST_OUT, val, reg,
- dev->ctrlmsg, 0);
-}
-
-static void cmd_msg_dump(struct au0828_dev *dev)
-{
- int i;
-
- for (i = 0; i < sizeof(dev->ctrlmsg); i += 16)
- dprintk(2, "%s() %02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- __func__,
- dev->ctrlmsg[i+0], dev->ctrlmsg[i+1],
- dev->ctrlmsg[i+2], dev->ctrlmsg[i+3],
- dev->ctrlmsg[i+4], dev->ctrlmsg[i+5],
- dev->ctrlmsg[i+6], dev->ctrlmsg[i+7],
- dev->ctrlmsg[i+8], dev->ctrlmsg[i+9],
- dev->ctrlmsg[i+10], dev->ctrlmsg[i+11],
- dev->ctrlmsg[i+12], dev->ctrlmsg[i+13],
- dev->ctrlmsg[i+14], dev->ctrlmsg[i+15]);
-}
-
-static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
- u16 index, unsigned char *cp, u16 size)
-{
- int status = -ENODEV;
- mutex_lock(&dev->mutex);
- if (dev->usbdev) {
-
- /* cp must be memory that has been allocated by kmalloc */
- status = usb_control_msg(dev->usbdev,
- usb_sndctrlpipe(dev->usbdev, 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_ERR "%s() Failed sending control message, error %d.\n",
- __func__, status);
- }
-
- }
- mutex_unlock(&dev->mutex);
- return status;
-}
-
-static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
- u16 index, unsigned char *cp, u16 size)
-{
- int status = -ENODEV;
- mutex_lock(&dev->mutex);
- if (dev->usbdev) {
-
- memset(dev->ctrlmsg, 0, sizeof(dev->ctrlmsg));
-
- /* cp must be memory that has been allocated by kmalloc */
- status = usb_control_msg(dev->usbdev,
- usb_rcvctrlpipe(dev->usbdev, 0),
- request,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index,
- cp, size, 1000);
-
- status = min(status, 0);
-
- if (status < 0) {
- printk(KERN_ERR "%s() Failed receiving control message, error %d.\n",
- __func__, status);
- } else
- cmd_msg_dump(dev);
- }
- mutex_unlock(&dev->mutex);
- return status;
-}
-
-static void au0828_usb_disconnect(struct usb_interface *interface)
-{
- struct au0828_dev *dev = usb_get_intfdata(interface);
-
- dprintk(1, "%s()\n", __func__);
-
- /* Digital TV */
- au0828_dvb_unregister(dev);
-
- if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
- au0828_analog_unregister(dev);
-
- /* I2C */
- au0828_i2c_unregister(dev);
-
- v4l2_device_unregister(&dev->v4l2_dev);
-
- usb_set_intfdata(interface, NULL);
-
- mutex_lock(&dev->mutex);
- dev->usbdev = NULL;
- mutex_unlock(&dev->mutex);
-
- kfree(dev);
-
-}
-
-static int au0828_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- int ifnum, retval;
- struct au0828_dev *dev;
- struct usb_device *usbdev = interface_to_usbdev(interface);
-
- ifnum = interface->altsetting->desc.bInterfaceNumber;
-
- if (ifnum != 0)
- return -ENODEV;
-
- dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
- le16_to_cpu(usbdev->descriptor.idVendor),
- le16_to_cpu(usbdev->descriptor.idProduct),
- ifnum);
-
- /*
- * Make sure we have 480 Mbps of bandwidth, otherwise things like
- * video stream wouldn't likely work, since 12 Mbps is generally
- * not enough even for most Digital TV streams.
- */
- if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
- printk(KERN_ERR "au0828: Device initialization failed.\n");
- printk(KERN_ERR "au0828: Device must be connected to a "
- "high-speed USB 2.0 port.\n");
- return -ENODEV;
- }
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- printk(KERN_ERR "%s() Unable to allocate memory\n", __func__);
- return -ENOMEM;
- }
-
- mutex_init(&dev->mutex);
- mutex_init(&dev->dvb.lock);
- dev->usbdev = usbdev;
- dev->boardnr = id->driver_info;
-
- /* Create the v4l2_device */
- retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
- if (retval) {
- printk(KERN_ERR "%s() v4l2_device_register failed\n",
- __func__);
- kfree(dev);
- return -EIO;
- }
-
- /* Power Up the bridge */
- au0828_write(dev, REG_600, 1 << 4);
-
- /* Bring up the GPIO's and supporting devices */
- au0828_gpio_setup(dev);
-
- /* I2C */
- au0828_i2c_register(dev);
-
- /* Setup */
- au0828_card_setup(dev);
-
- /* Analog TV */
- if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
- au0828_analog_register(dev, interface);
-
- /* Digital TV */
- au0828_dvb_register(dev);
-
- /* Store the pointer to the au0828_dev so it can be accessed in
- au0828_usb_disconnect */
- usb_set_intfdata(interface, dev);
-
- printk(KERN_INFO "Registered device AU0828 [%s]\n",
- dev->board.name == NULL ? "Unset" : dev->board.name);
-
- return 0;
-}
-
-static struct usb_driver au0828_usb_driver = {
- .name = DRIVER_NAME,
- .probe = au0828_usb_probe,
- .disconnect = au0828_usb_disconnect,
- .id_table = au0828_usb_id_table,
-};
-
-static int __init au0828_init(void)
-{
- int ret;
-
- if (au0828_debug & 1)
- printk(KERN_INFO "%s() Debugging is enabled\n", __func__);
-
- if (au0828_debug & 2)
- printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__);
-
- if (au0828_debug & 4)
- printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__);
-
- if (au0828_debug & 8)
- printk(KERN_INFO "%s() Bridge Debugging is enabled\n",
- __func__);
-
- printk(KERN_INFO "au0828 driver loaded\n");
-
- ret = usb_register(&au0828_usb_driver);
- if (ret)
- printk(KERN_ERR "usb_register failed, error = %d\n", ret);
-
- return ret;
-}
-
-static void __exit au0828_exit(void)
-{
- usb_deregister(&au0828_usb_driver);
-}
-
-module_init(au0828_init);
-module_exit(au0828_exit);
-
-MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
-MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.2");
diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c
deleted file mode 100644
index 39ece8e24985..000000000000
--- a/drivers/media/video/au0828/au0828-dvb.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Driver for the Auvitek USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/suspend.h>
-#include <media/v4l2-common.h>
-#include <media/tuner.h>
-
-#include "au0828.h"
-#include "au8522.h"
-#include "xc5000.h"
-#include "mxl5007t.h"
-#include "tda18271.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define _AU0828_BULKPIPE 0x83
-#define _BULKPIPESIZE 0xe522
-
-static u8 hauppauge_hvr950q_led_states[] = {
- 0x00, /* off */
- 0x02, /* yellow */
- 0x04, /* green */
-};
-
-static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
- .gpio_output = 0x00e0,
- .gpio_output_enable = 0x6006,
- .gpio_output_disable = 0x0660,
-
- .gpio_leds = 0x00e2,
- .led_states = hauppauge_hvr950q_led_states,
- .num_led_states = sizeof(hauppauge_hvr950q_led_states),
-
- .vsb8_strong = 20 /* dB */ * 10,
- .qam64_strong = 25 /* dB */ * 10,
- .qam256_strong = 32 /* dB */ * 10,
-};
-
-static struct au8522_config hauppauge_hvr950q_config = {
- .demod_address = 0x8e >> 1,
- .status_mode = AU8522_DEMODLOCKING,
- .qam_if = AU8522_IF_6MHZ,
- .vsb_if = AU8522_IF_6MHZ,
- .led_cfg = &hauppauge_hvr950q_led_cfg,
-};
-
-static struct au8522_config fusionhdtv7usb_config = {
- .demod_address = 0x8e >> 1,
- .status_mode = AU8522_DEMODLOCKING,
- .qam_if = AU8522_IF_6MHZ,
- .vsb_if = AU8522_IF_6MHZ,
-};
-
-static struct au8522_config hauppauge_woodbury_config = {
- .demod_address = 0x8e >> 1,
- .status_mode = AU8522_DEMODLOCKING,
- .qam_if = AU8522_IF_4MHZ,
- .vsb_if = AU8522_IF_3_25MHZ,
-};
-
-static struct xc5000_config hauppauge_xc5000a_config = {
- .i2c_address = 0x61,
- .if_khz = 6000,
- .chip_id = XC5000A,
-};
-
-static struct xc5000_config hauppauge_xc5000c_config = {
- .i2c_address = 0x61,
- .if_khz = 6000,
- .chip_id = XC5000C,
-};
-
-static struct mxl5007t_config mxl5007t_hvr950q_config = {
- .xtal_freq_hz = MxL_XTAL_24_MHZ,
- .if_freq_hz = MxL_IF_6_MHZ,
-};
-
-static struct tda18271_config hauppauge_woodbury_tunerconfig = {
- .gate = TDA18271_GATE_DIGITAL,
-};
-
-/*-------------------------------------------------------------------*/
-static void urb_completion(struct urb *purb)
-{
- struct au0828_dev *dev = purb->context;
- int ptype = usb_pipetype(purb->pipe);
-
- dprintk(2, "%s()\n", __func__);
-
- if (!dev)
- return;
-
- if (dev->urb_streaming == 0)
- return;
-
- if (ptype != PIPE_BULK) {
- printk(KERN_ERR "%s() Unsupported URB type %d\n",
- __func__, ptype);
- return;
- }
-
- /* Feed the transport payload into the kernel demux */
- dvb_dmx_swfilter_packets(&dev->dvb.demux,
- purb->transfer_buffer, purb->actual_length / 188);
-
- /* Clean the buffer before we requeue */
- memset(purb->transfer_buffer, 0, URB_BUFSIZE);
-
- /* Requeue URB */
- usb_submit_urb(purb, GFP_ATOMIC);
-}
-
-static int stop_urb_transfer(struct au0828_dev *dev)
-{
- int i;
-
- dprintk(2, "%s()\n", __func__);
-
- for (i = 0; i < URB_COUNT; i++) {
- usb_kill_urb(dev->urbs[i]);
- kfree(dev->urbs[i]->transfer_buffer);
- usb_free_urb(dev->urbs[i]);
- }
-
- dev->urb_streaming = 0;
-
- return 0;
-}
-
-static int start_urb_transfer(struct au0828_dev *dev)
-{
- struct urb *purb;
- int i, ret = -ENOMEM;
-
- dprintk(2, "%s()\n", __func__);
-
- if (dev->urb_streaming) {
- dprintk(2, "%s: bulk xfer already running!\n", __func__);
- return 0;
- }
-
- for (i = 0; i < URB_COUNT; i++) {
-
- dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->urbs[i])
- goto err;
-
- purb = dev->urbs[i];
-
- purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL);
- if (!purb->transfer_buffer) {
- usb_free_urb(purb);
- dev->urbs[i] = NULL;
- goto err;
- }
-
- purb->status = -EINPROGRESS;
- usb_fill_bulk_urb(purb,
- dev->usbdev,
- usb_rcvbulkpipe(dev->usbdev,
- _AU0828_BULKPIPE),
- purb->transfer_buffer,
- URB_BUFSIZE,
- urb_completion,
- dev);
-
- }
-
- for (i = 0; i < URB_COUNT; i++) {
- ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
- if (ret != 0) {
- stop_urb_transfer(dev);
- printk(KERN_ERR "%s: failed urb submission, "
- "err = %d\n", __func__, ret);
- return ret;
- }
- }
-
- dev->urb_streaming = 1;
- ret = 0;
-
-err:
- return ret;
-}
-
-static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
- struct au0828_dvb *dvb = &dev->dvb;
- int ret = 0;
-
- dprintk(1, "%s()\n", __func__);
-
- if (!demux->dmx.frontend)
- return -EINVAL;
-
- if (dvb) {
- mutex_lock(&dvb->lock);
- if (dvb->feeding++ == 0) {
- /* Start transport */
- au0828_write(dev, 0x608, 0x90);
- au0828_write(dev, 0x609, 0x72);
- au0828_write(dev, 0x60a, 0x71);
- au0828_write(dev, 0x60b, 0x01);
- ret = start_urb_transfer(dev);
- }
- mutex_unlock(&dvb->lock);
- }
-
- return ret;
-}
-
-static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
- struct au0828_dvb *dvb = &dev->dvb;
- int ret = 0;
-
- dprintk(1, "%s()\n", __func__);
-
- if (dvb) {
- mutex_lock(&dvb->lock);
- if (--dvb->feeding == 0) {
- /* Stop transport */
- au0828_write(dev, 0x608, 0x00);
- au0828_write(dev, 0x609, 0x00);
- au0828_write(dev, 0x60a, 0x00);
- au0828_write(dev, 0x60b, 0x00);
- ret = stop_urb_transfer(dev);
- }
- mutex_unlock(&dvb->lock);
- }
-
- return ret;
-}
-
-static int dvb_register(struct au0828_dev *dev)
-{
- struct au0828_dvb *dvb = &dev->dvb;
- int result;
-
- dprintk(1, "%s()\n", __func__);
-
- /* register adapter */
- result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
- &dev->usbdev->dev, adapter_nr);
- if (result < 0) {
- printk(KERN_ERR "%s: dvb_register_adapter failed "
- "(errno = %d)\n", DRIVER_NAME, result);
- goto fail_adapter;
- }
- dvb->adapter.priv = dev;
-
- /* register frontend */
- result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
- if (result < 0) {
- printk(KERN_ERR "%s: dvb_register_frontend failed "
- "(errno = %d)\n", DRIVER_NAME, result);
- goto fail_frontend;
- }
-
- /* register demux stuff */
- dvb->demux.dmx.capabilities =
- DMX_TS_FILTERING | DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
- dvb->demux.priv = dev;
- dvb->demux.filternum = 256;
- dvb->demux.feednum = 256;
- dvb->demux.start_feed = au0828_dvb_start_feed;
- dvb->demux.stop_feed = au0828_dvb_stop_feed;
- result = dvb_dmx_init(&dvb->demux);
- if (result < 0) {
- printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
- DRIVER_NAME, result);
- goto fail_dmx;
- }
-
- dvb->dmxdev.filternum = 256;
- dvb->dmxdev.demux = &dvb->demux.dmx;
- dvb->dmxdev.capabilities = 0;
- result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
- if (result < 0) {
- printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
- DRIVER_NAME, result);
- goto fail_dmxdev;
- }
-
- dvb->fe_hw.source = DMX_FRONTEND_0;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_ERR "%s: add_frontend failed "
- "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
- goto fail_fe_hw;
- }
-
- dvb->fe_mem.source = DMX_MEMORY_FE;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- if (result < 0) {
- printk(KERN_ERR "%s: add_frontend failed "
- "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
- goto fail_fe_mem;
- }
-
- result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
- DRIVER_NAME, result);
- goto fail_fe_conn;
- }
-
- /* register network adapter */
- dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
- return 0;
-
-fail_fe_conn:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-fail_fe_mem:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-fail_fe_hw:
- dvb_dmxdev_release(&dvb->dmxdev);
-fail_dmxdev:
- dvb_dmx_release(&dvb->demux);
-fail_dmx:
- dvb_unregister_frontend(dvb->frontend);
-fail_frontend:
- dvb_frontend_detach(dvb->frontend);
- dvb_unregister_adapter(&dvb->adapter);
-fail_adapter:
- return result;
-}
-
-void au0828_dvb_unregister(struct au0828_dev *dev)
-{
- struct au0828_dvb *dvb = &dev->dvb;
-
- dprintk(1, "%s()\n", __func__);
-
- if (dvb->frontend == NULL)
- return;
-
- dvb_net_release(&dvb->net);
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- dvb_dmxdev_release(&dvb->dmxdev);
- dvb_dmx_release(&dvb->demux);
- dvb_unregister_frontend(dvb->frontend);
- dvb_frontend_detach(dvb->frontend);
- dvb_unregister_adapter(&dvb->adapter);
-}
-
-/* All the DVB attach calls go here, this function get's modified
- * for each new card. No other function in this file needs
- * to change.
- */
-int au0828_dvb_register(struct au0828_dev *dev)
-{
- struct au0828_dvb *dvb = &dev->dvb;
- int ret;
-
- dprintk(1, "%s()\n", __func__);
-
- /* init frontend */
- switch (dev->boardnr) {
- case AU0828_BOARD_HAUPPAUGE_HVR850:
- case AU0828_BOARD_HAUPPAUGE_HVR950Q:
- dvb->frontend = dvb_attach(au8522_attach,
- &hauppauge_hvr950q_config,
- &dev->i2c_adap);
- if (dvb->frontend != NULL)
- switch (dev->board.tuner_type) {
- default:
- case TUNER_XC5000:
- dvb_attach(xc5000_attach, dvb->frontend,
- &dev->i2c_adap,
- &hauppauge_xc5000a_config);
- break;
- case TUNER_XC5000C:
- dvb_attach(xc5000_attach, dvb->frontend,
- &dev->i2c_adap,
- &hauppauge_xc5000c_config);
- break;
- }
- break;
- case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
- dvb->frontend = dvb_attach(au8522_attach,
- &hauppauge_hvr950q_config,
- &dev->i2c_adap);
- if (dvb->frontend != NULL)
- dvb_attach(mxl5007t_attach, dvb->frontend,
- &dev->i2c_adap, 0x60,
- &mxl5007t_hvr950q_config);
- break;
- case AU0828_BOARD_HAUPPAUGE_WOODBURY:
- dvb->frontend = dvb_attach(au8522_attach,
- &hauppauge_woodbury_config,
- &dev->i2c_adap);
- if (dvb->frontend != NULL)
- dvb_attach(tda18271_attach, dvb->frontend,
- 0x60, &dev->i2c_adap,
- &hauppauge_woodbury_tunerconfig);
- break;
- case AU0828_BOARD_DVICO_FUSIONHDTV7:
- dvb->frontend = dvb_attach(au8522_attach,
- &fusionhdtv7usb_config,
- &dev->i2c_adap);
- if (dvb->frontend != NULL) {
- dvb_attach(xc5000_attach, dvb->frontend,
- &dev->i2c_adap,
- &hauppauge_xc5000a_config);
- }
- break;
- default:
- printk(KERN_WARNING "The frontend of your DVB/ATSC card "
- "isn't supported yet\n");
- break;
- }
- if (NULL == dvb->frontend) {
- printk(KERN_ERR "%s() Frontend initialization failed\n",
- __func__);
- return -1;
- }
- /* define general-purpose callback pointer */
- dvb->frontend->callback = au0828_tuner_callback;
-
- /* register everything */
- ret = dvb_register(dev);
- if (ret < 0) {
- if (dvb->frontend->ops.release)
- dvb->frontend->ops.release(dvb->frontend);
- return ret;
- }
-
- return 0;
-}
diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c
deleted file mode 100644
index 05c299fa5d79..000000000000
--- a/drivers/media/video/au0828/au0828-i2c.c
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Driver for the Auvitek AU0828 USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-
-#include "au0828.h"
-
-#include <media/v4l2-common.h>
-
-static int i2c_scan;
-module_param(i2c_scan, int, 0444);
-MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
-
-#define I2C_WAIT_DELAY 512
-#define I2C_WAIT_RETRY 64
-
-static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- return au0828_read(dev, AU0828_I2C_STATUS_201) &
- AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
-}
-
-static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- return au0828_read(dev, AU0828_I2C_STATUS_201) &
- AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
-}
-
-static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
-{
- int count;
-
- for (count = 0; count < I2C_WAIT_RETRY; count++) {
- if (!i2c_slave_did_read_ack(i2c_adap))
- break;
- udelay(I2C_WAIT_DELAY);
- }
-
- if (I2C_WAIT_RETRY == count)
- return 0;
-
- return 1;
-}
-
-static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- return au0828_read(dev, AU0828_I2C_STATUS_201) &
- AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
-}
-
-static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
-{
- int count;
-
- for (count = 0; count < I2C_WAIT_RETRY; count++) {
- if (!i2c_is_read_busy(i2c_adap))
- break;
- udelay(I2C_WAIT_DELAY);
- }
-
- if (I2C_WAIT_RETRY == count)
- return 0;
-
- return 1;
-}
-
-static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- return au0828_read(dev, AU0828_I2C_STATUS_201) &
- AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
-}
-
-static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
-{
- int count;
-
- for (count = 0; count < I2C_WAIT_RETRY; count++) {
- if (i2c_is_write_done(i2c_adap))
- break;
- udelay(I2C_WAIT_DELAY);
- }
-
- if (I2C_WAIT_RETRY == count)
- return 0;
-
- return 1;
-}
-
-static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- return au0828_read(dev, AU0828_I2C_STATUS_201) &
- AU0828_I2C_STATUS_BUSY ? 1 : 0;
-}
-
-static int i2c_wait_done(struct i2c_adapter *i2c_adap)
-{
- int count;
-
- for (count = 0; count < I2C_WAIT_RETRY; count++) {
- if (!i2c_is_busy(i2c_adap))
- break;
- udelay(I2C_WAIT_DELAY);
- }
-
- if (I2C_WAIT_RETRY == count)
- return 0;
-
- return 1;
-}
-
-/* FIXME: Implement join handling correctly */
-static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int joined_rlen)
-{
- int i, strobe = 0;
- struct au0828_dev *dev = i2c_adap->algo_data;
-
- dprintk(4, "%s()\n", __func__);
-
- au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
-
- /* Set the I2C clock */
- au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
- dev->board.i2c_clk_divider);
-
- /* Hardware needs 8 bit addresses */
- au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
-
- dprintk(4, "SEND: %02x\n", msg->addr);
-
- /* Deal with i2c_scan */
- if (msg->len == 0) {
- /* The analog tuner detection code makes use of the SMBUS_QUICK
- message (which involves a zero length i2c write). To avoid
- checking the status register when we didn't strobe out any
- actual bytes to the bus, just do a read check. This is
- consistent with how I saw i2c device checking done in the
- USB trace of the Windows driver */
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_READ);
-
- if (!i2c_wait_done(i2c_adap))
- return -EIO;
-
- if (i2c_wait_read_ack(i2c_adap))
- return -EIO;
-
- return 0;
- }
-
- for (i = 0; i < msg->len;) {
-
- dprintk(4, " %02x\n", msg->buf[i]);
-
- au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
-
- strobe++;
- i++;
-
- if ((strobe >= 4) || (i >= msg->len)) {
-
- /* Strobe the byte into the bus */
- if (i < msg->len)
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_WRITE |
- AU0828_I2C_TRIGGER_HOLD);
- else
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_WRITE);
-
- /* Reset strobe trigger */
- strobe = 0;
-
- if (!i2c_wait_write_done(i2c_adap))
- return -EIO;
-
- }
-
- }
- if (!i2c_wait_done(i2c_adap))
- return -EIO;
-
- dprintk(4, "\n");
-
- return msg->len;
-}
-
-/* FIXME: Implement join handling correctly */
-static int i2c_readbytes(struct i2c_adapter *i2c_adap,
- const struct i2c_msg *msg, int joined)
-{
- struct au0828_dev *dev = i2c_adap->algo_data;
- int i;
-
- dprintk(4, "%s()\n", __func__);
-
- au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
-
- /* Set the I2C clock */
- au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
- dev->board.i2c_clk_divider);
-
- /* Hardware needs 8 bit addresses */
- au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
-
- dprintk(4, " RECV:\n");
-
- /* Deal with i2c_scan */
- if (msg->len == 0) {
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_READ);
-
- if (i2c_wait_read_ack(i2c_adap))
- return -EIO;
- return 0;
- }
-
- for (i = 0; i < msg->len;) {
-
- i++;
-
- if (i < msg->len)
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_READ |
- AU0828_I2C_TRIGGER_HOLD);
- else
- au0828_write(dev, AU0828_I2C_TRIGGER_200,
- AU0828_I2C_TRIGGER_READ);
-
- if (!i2c_wait_read_done(i2c_adap))
- return -EIO;
-
- msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
- 0xff;
-
- dprintk(4, " %02x\n", msg->buf[i-1]);
- }
- if (!i2c_wait_done(i2c_adap))
- return -EIO;
-
- dprintk(4, "\n");
-
- return msg->len;
-}
-
-static int i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs, int num)
-{
- int i, retval = 0;
-
- dprintk(4, "%s(num = %d)\n", __func__, num);
-
- for (i = 0; i < num; i++) {
- dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
- __func__, num, msgs[i].addr, msgs[i].len);
- if (msgs[i].flags & I2C_M_RD) {
- /* read */
- retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
- } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
- msgs[i].addr == msgs[i + 1].addr) {
- /* write then read from same address */
- retval = i2c_sendbytes(i2c_adap, &msgs[i],
- msgs[i + 1].len);
- if (retval < 0)
- goto err;
- i++;
- retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
- } else {
- /* write */
- retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
- }
- if (retval < 0)
- goto err;
- }
- return num;
-
-err:
- return retval;
-}
-
-static u32 au0828_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
-}
-
-static struct i2c_algorithm au0828_i2c_algo_template = {
- .master_xfer = i2c_xfer,
- .functionality = au0828_functionality,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_adapter au0828_i2c_adap_template = {
- .name = DRIVER_NAME,
- .owner = THIS_MODULE,
- .algo = &au0828_i2c_algo_template,
-};
-
-static struct i2c_client au0828_i2c_client_template = {
- .name = "au0828 internal",
-};
-
-static char *i2c_devs[128] = {
- [0x8e >> 1] = "au8522",
- [0xa0 >> 1] = "eeprom",
- [0xc2 >> 1] = "tuner/xc5000",
-};
-
-static void do_i2c_scan(char *name, struct i2c_client *c)
-{
- unsigned char buf;
- int i, rc;
-
- for (i = 0; i < 128; i++) {
- c->addr = i;
- rc = i2c_master_recv(c, &buf, 0);
- if (rc < 0)
- continue;
- printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n",
- name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
- }
-}
-
-/* init + register i2c adapter */
-int au0828_i2c_register(struct au0828_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- memcpy(&dev->i2c_adap, &au0828_i2c_adap_template,
- sizeof(dev->i2c_adap));
- memcpy(&dev->i2c_algo, &au0828_i2c_algo_template,
- sizeof(dev->i2c_algo));
- memcpy(&dev->i2c_client, &au0828_i2c_client_template,
- sizeof(dev->i2c_client));
-
- dev->i2c_adap.dev.parent = &dev->usbdev->dev;
-
- strlcpy(dev->i2c_adap.name, DRIVER_NAME,
- sizeof(dev->i2c_adap.name));
-
- dev->i2c_adap.algo = &dev->i2c_algo;
- dev->i2c_adap.algo_data = dev;
- i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
- i2c_add_adapter(&dev->i2c_adap);
-
- dev->i2c_client.adapter = &dev->i2c_adap;
-
- if (0 == dev->i2c_rc) {
- printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME);
- if (i2c_scan)
- do_i2c_scan(DRIVER_NAME, &dev->i2c_client);
- } else
- printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME);
-
- return dev->i2c_rc;
-}
-
-int au0828_i2c_unregister(struct au0828_dev *dev)
-{
- i2c_del_adapter(&dev->i2c_adap);
- return 0;
-}
-
diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h
deleted file mode 100644
index c39f3d2b721e..000000000000
--- a/drivers/media/video/au0828/au0828-reg.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Driver for the Auvitek USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* We'll start to rename these registers once we have a better
- * understanding of their meaning.
- */
-#define REG_000 0x000
-#define REG_001 0x001
-#define REG_002 0x002
-#define REG_003 0x003
-
-#define AU0828_SENSORCTRL_100 0x100
-#define AU0828_SENSORCTRL_VBI_103 0x103
-
-/* I2C registers */
-#define AU0828_I2C_TRIGGER_200 0x200
-#define AU0828_I2C_STATUS_201 0x201
-#define AU0828_I2C_CLK_DIVIDER_202 0x202
-#define AU0828_I2C_DEST_ADDR_203 0x203
-#define AU0828_I2C_WRITE_FIFO_205 0x205
-#define AU0828_I2C_READ_FIFO_209 0x209
-#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
-
-/* Audio registers */
-#define AU0828_AUDIOCTRL_50C 0x50C
-
-#define REG_600 0x600
-
-/*********************************************************************/
-/* Here are constants for values associated with the above registers */
-
-/* I2C Trigger (Reg 0x200) */
-#define AU0828_I2C_TRIGGER_WRITE 0x01
-#define AU0828_I2C_TRIGGER_READ 0x20
-#define AU0828_I2C_TRIGGER_HOLD 0x40
-
-/* I2C Status (Reg 0x201) */
-#define AU0828_I2C_STATUS_READ_DONE 0x01
-#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
-#define AU0828_I2C_STATUS_WRITE_DONE 0x04
-#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
-#define AU0828_I2C_STATUS_BUSY 0x10
-
-/* I2C Clock Divider (Reg 0x202) */
-#define AU0828_I2C_CLK_250KHZ 0x07
-#define AU0828_I2C_CLK_100KHZ 0x14
-#define AU0828_I2C_CLK_30KHZ 0x40
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
deleted file mode 100644
index ac3dd733ab81..000000000000
--- a/drivers/media/video/au0828/au0828-video.c
+++ /dev/null
@@ -1,1998 +0,0 @@
-/*
- * Auvitek AU0828 USB Bridge (Analog video support)
- *
- * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
- * Copyright (C) 2005-2008 Auvitek International, Ltd.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * As published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- */
-
-/* Developer Notes:
- *
- * VBI support is not yet working
- * The hardware scaler supported is unimplemented
- * AC97 audio support is unimplemented (only i2s audio mode)
- *
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/suspend.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/tuner.h>
-#include "au0828.h"
-#include "au0828-reg.h"
-
-static DEFINE_MUTEX(au0828_sysfs_lock);
-
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-
-static unsigned int isoc_debug;
-module_param(isoc_debug, int, 0644);
-MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
-
-#define au0828_isocdbg(fmt, arg...) \
-do {\
- if (isoc_debug) { \
- printk(KERN_INFO "au0828 %s :"fmt, \
- __func__ , ##arg); \
- } \
- } while (0)
-
-static inline void print_err_status(struct au0828_dev *dev,
- int packet, int status)
-{
- char *errmsg = "Unknown";
-
- switch (status) {
- case -ENOENT:
- errmsg = "unlinked synchronuously";
- break;
- case -ECONNRESET:
- errmsg = "unlinked asynchronuously";
- break;
- case -ENOSR:
- errmsg = "Buffer error (overrun)";
- break;
- case -EPIPE:
- errmsg = "Stalled (device not responding)";
- break;
- case -EOVERFLOW:
- errmsg = "Babble (bad cable?)";
- break;
- case -EPROTO:
- errmsg = "Bit-stuff error (bad cable?)";
- break;
- case -EILSEQ:
- errmsg = "CRC/Timeout (could be anything)";
- break;
- case -ETIME:
- errmsg = "Device does not respond";
- break;
- }
- if (packet < 0) {
- au0828_isocdbg("URB status %d [%s].\n", status, errmsg);
- } else {
- au0828_isocdbg("URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
- }
-}
-
-static int check_dev(struct au0828_dev *dev)
-{
- if (dev->dev_state & DEV_DISCONNECTED) {
- printk(KERN_INFO "v4l2 ioctl: device not present\n");
- return -ENODEV;
- }
-
- if (dev->dev_state & DEV_MISCONFIGURED) {
- printk(KERN_INFO "v4l2 ioctl: device is misconfigured; "
- "close and open it again\n");
- return -EIO;
- }
- return 0;
-}
-
-/*
- * IRQ callback, called by URB callback
- */
-static void au0828_irq_callback(struct urb *urb)
-{
- struct au0828_dmaqueue *dma_q = urb->context;
- struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
- unsigned long flags = 0;
- int i;
-
- switch (urb->status) {
- case 0: /* success */
- case -ETIMEDOUT: /* NAK */
- break;
- case -ECONNRESET: /* kill */
- case -ENOENT:
- case -ESHUTDOWN:
- au0828_isocdbg("au0828_irq_callback called: status kill\n");
- return;
- default: /* unknown error */
- au0828_isocdbg("urb completition error %d.\n", urb->status);
- break;
- }
-
- /* Copy data from URB */
- spin_lock_irqsave(&dev->slock, flags);
- dev->isoc_ctl.isoc_copy(dev, urb);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- /* Reset urb buffers */
- for (i = 0; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].status = 0;
- urb->iso_frame_desc[i].actual_length = 0;
- }
- urb->status = 0;
-
- urb->status = usb_submit_urb(urb, GFP_ATOMIC);
- if (urb->status) {
- au0828_isocdbg("urb resubmit failed (error=%i)\n",
- urb->status);
- }
-}
-
-/*
- * Stop and Deallocate URBs
- */
-void au0828_uninit_isoc(struct au0828_dev *dev)
-{
- struct urb *urb;
- int i;
-
- au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
-
- dev->isoc_ctl.nfields = -1;
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = dev->isoc_ctl.urb[i];
- if (urb) {
- if (!irqs_disabled())
- usb_kill_urb(urb);
- else
- usb_unlink_urb(urb);
-
- if (dev->isoc_ctl.transfer_buffer[i]) {
- usb_free_coherent(dev->usbdev,
- urb->transfer_buffer_length,
- dev->isoc_ctl.transfer_buffer[i],
- urb->transfer_dma);
- }
- usb_free_urb(urb);
- dev->isoc_ctl.urb[i] = NULL;
- }
- dev->isoc_ctl.transfer_buffer[i] = NULL;
- }
-
- kfree(dev->isoc_ctl.urb);
- kfree(dev->isoc_ctl.transfer_buffer);
-
- dev->isoc_ctl.urb = NULL;
- dev->isoc_ctl.transfer_buffer = NULL;
- dev->isoc_ctl.num_bufs = 0;
-}
-
-/*
- * Allocate URBs and start IRQ
- */
-int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
- int num_bufs, int max_pkt_size,
- int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
-{
- struct au0828_dmaqueue *dma_q = &dev->vidq;
- int i;
- int sb_size, pipe;
- struct urb *urb;
- int j, k;
- int rc;
-
- au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
-
- /* De-allocates all pending stuff */
- au0828_uninit_isoc(dev);
-
- dev->isoc_ctl.isoc_copy = isoc_copy;
- dev->isoc_ctl.num_bufs = num_bufs;
-
- dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
- if (!dev->isoc_ctl.urb) {
- au0828_isocdbg("cannot alloc memory for usb buffers\n");
- return -ENOMEM;
- }
-
- dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
- if (!dev->isoc_ctl.transfer_buffer) {
- au0828_isocdbg("cannot allocate memory for usb transfer\n");
- kfree(dev->isoc_ctl.urb);
- return -ENOMEM;
- }
-
- dev->isoc_ctl.max_pkt_size = max_pkt_size;
- dev->isoc_ctl.buf = NULL;
-
- sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
-
- /* allocate urbs and transfer buffers */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = usb_alloc_urb(max_packets, GFP_KERNEL);
- if (!urb) {
- au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i);
- au0828_uninit_isoc(dev);
- return -ENOMEM;
- }
- dev->isoc_ctl.urb[i] = urb;
-
- dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->usbdev,
- sb_size, GFP_KERNEL, &urb->transfer_dma);
- if (!dev->isoc_ctl.transfer_buffer[i]) {
- printk("unable to allocate %i bytes for transfer"
- " buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
- au0828_uninit_isoc(dev);
- return -ENOMEM;
- }
- memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
-
- pipe = usb_rcvisocpipe(dev->usbdev,
- dev->isoc_in_endpointaddr),
-
- usb_fill_int_urb(urb, dev->usbdev, pipe,
- dev->isoc_ctl.transfer_buffer[i], sb_size,
- au0828_irq_callback, dma_q, 1);
-
- urb->number_of_packets = max_packets;
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-
- k = 0;
- for (j = 0; j < max_packets; j++) {
- urb->iso_frame_desc[j].offset = k;
- urb->iso_frame_desc[j].length =
- dev->isoc_ctl.max_pkt_size;
- k += dev->isoc_ctl.max_pkt_size;
- }
- }
-
- init_waitqueue_head(&dma_q->wq);
-
- /* submit urbs and enables IRQ */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
- if (rc) {
- au0828_isocdbg("submit of urb %i failed (error=%i)\n",
- i, rc);
- au0828_uninit_isoc(dev);
- return rc;
- }
- }
-
- return 0;
-}
-
-/*
- * Announces that a buffer were filled and request the next
- */
-static inline void buffer_filled(struct au0828_dev *dev,
- struct au0828_dmaqueue *dma_q,
- struct au0828_buffer *buf)
-{
- /* Advice that buffer was filled */
- au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
-
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- dev->isoc_ctl.buf = NULL;
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-static inline void vbi_buffer_filled(struct au0828_dev *dev,
- struct au0828_dmaqueue *dma_q,
- struct au0828_buffer *buf)
-{
- /* Advice that buffer was filled */
- au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
-
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- dev->isoc_ctl.vbi_buf = NULL;
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-/*
- * Identify the buffer header type and properly handles
- */
-static void au0828_copy_video(struct au0828_dev *dev,
- struct au0828_dmaqueue *dma_q,
- struct au0828_buffer *buf,
- unsigned char *p,
- unsigned char *outp, unsigned long len)
-{
- void *fieldstart, *startwrite, *startread;
- int linesdone, currlinedone, offset, lencopy, remain;
- int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
-
- if (len == 0)
- return;
-
- if (dma_q->pos + len > buf->vb.size)
- len = buf->vb.size - dma_q->pos;
-
- startread = p;
- remain = len;
-
- /* Interlaces frame */
- if (buf->top_field)
- fieldstart = outp;
- else
- fieldstart = outp + bytesperline;
-
- linesdone = dma_q->pos / bytesperline;
- currlinedone = dma_q->pos % bytesperline;
- offset = linesdone * bytesperline * 2 + currlinedone;
- startwrite = fieldstart + offset;
- lencopy = bytesperline - currlinedone;
- lencopy = lencopy > remain ? remain : lencopy;
-
- if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
- au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
- ((char *)startwrite + lencopy) -
- ((char *)outp + buf->vb.size));
- remain = (char *)outp + buf->vb.size - (char *)startwrite;
- lencopy = remain;
- }
- if (lencopy <= 0)
- return;
- memcpy(startwrite, startread, lencopy);
-
- remain -= lencopy;
-
- while (remain > 0) {
- startwrite += lencopy + bytesperline;
- startread += lencopy;
- if (bytesperline > remain)
- lencopy = remain;
- else
- lencopy = bytesperline;
-
- if ((char *)startwrite + lencopy > (char *)outp +
- buf->vb.size) {
- au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
- ((char *)startwrite + lencopy) -
- ((char *)outp + buf->vb.size));
- lencopy = remain = (char *)outp + buf->vb.size -
- (char *)startwrite;
- }
- if (lencopy <= 0)
- break;
-
- memcpy(startwrite, startread, lencopy);
-
- remain -= lencopy;
- }
-
- if (offset > 1440) {
- /* We have enough data to check for greenscreen */
- if (outp[0] < 0x60 && outp[1440] < 0x60)
- dev->greenscreen_detected = 1;
- }
-
- dma_q->pos += len;
-}
-
-/*
- * video-buf generic routine to get the next available buffer
- */
-static inline void get_next_buf(struct au0828_dmaqueue *dma_q,
- struct au0828_buffer **buf)
-{
- struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
-
- if (list_empty(&dma_q->active)) {
- au0828_isocdbg("No active queue to serve\n");
- dev->isoc_ctl.buf = NULL;
- *buf = NULL;
- return;
- }
-
- /* Get the next buffer */
- *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
- dev->isoc_ctl.buf = *buf;
-
- return;
-}
-
-static void au0828_copy_vbi(struct au0828_dev *dev,
- struct au0828_dmaqueue *dma_q,
- struct au0828_buffer *buf,
- unsigned char *p,
- unsigned char *outp, unsigned long len)
-{
- unsigned char *startwrite, *startread;
- int bytesperline;
- int i, j = 0;
-
- if (dev == NULL) {
- au0828_isocdbg("dev is null\n");
- return;
- }
-
- if (dma_q == NULL) {
- au0828_isocdbg("dma_q is null\n");
- return;
- }
- if (buf == NULL)
- return;
- if (p == NULL) {
- au0828_isocdbg("p is null\n");
- return;
- }
- if (outp == NULL) {
- au0828_isocdbg("outp is null\n");
- return;
- }
-
- bytesperline = dev->vbi_width;
-
- if (dma_q->pos + len > buf->vb.size)
- len = buf->vb.size - dma_q->pos;
-
- startread = p;
- startwrite = outp + (dma_q->pos / 2);
-
- /* Make sure the bottom field populates the second half of the frame */
- if (buf->top_field == 0)
- startwrite += bytesperline * dev->vbi_height;
-
- for (i = 0; i < len; i += 2)
- startwrite[j++] = startread[i+1];
-
- dma_q->pos += len;
-}
-
-
-/*
- * video-buf generic routine to get the next available VBI buffer
- */
-static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q,
- struct au0828_buffer **buf)
-{
- struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq);
- char *outp;
-
- if (list_empty(&dma_q->active)) {
- au0828_isocdbg("No active queue to serve\n");
- dev->isoc_ctl.vbi_buf = NULL;
- *buf = NULL;
- return;
- }
-
- /* Get the next buffer */
- *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
- /* Cleans up buffer - Useful for testing for frame/URB loss */
- outp = videobuf_to_vmalloc(&(*buf)->vb);
- memset(outp, 0x00, (*buf)->vb.size);
-
- dev->isoc_ctl.vbi_buf = *buf;
-
- return;
-}
-
-/*
- * Controls the isoc copy of each urb packet
- */
-static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
-{
- struct au0828_buffer *buf;
- struct au0828_buffer *vbi_buf;
- struct au0828_dmaqueue *dma_q = urb->context;
- struct au0828_dmaqueue *vbi_dma_q = &dev->vbiq;
- unsigned char *outp = NULL;
- unsigned char *vbioutp = NULL;
- int i, len = 0, rc = 1;
- unsigned char *p;
- unsigned char fbyte;
- unsigned int vbi_field_size;
- unsigned int remain, lencopy;
-
- if (!dev)
- return 0;
-
- if ((dev->dev_state & DEV_DISCONNECTED) ||
- (dev->dev_state & DEV_MISCONFIGURED))
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- buf = dev->isoc_ctl.buf;
- if (buf != NULL)
- outp = videobuf_to_vmalloc(&buf->vb);
-
- vbi_buf = dev->isoc_ctl.vbi_buf;
- if (vbi_buf != NULL)
- vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- if (urb->iso_frame_desc[i].status != -EPROTO)
- continue;
- }
-
- if (urb->iso_frame_desc[i].actual_length <= 0)
- continue;
-
- if (urb->iso_frame_desc[i].actual_length >
- dev->max_pkt_size) {
- au0828_isocdbg("packet bigger than packet size");
- continue;
- }
-
- p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- fbyte = p[0];
- len = urb->iso_frame_desc[i].actual_length - 4;
- p += 4;
-
- if (fbyte & 0x80) {
- len -= 4;
- p += 4;
- au0828_isocdbg("Video frame %s\n",
- (fbyte & 0x40) ? "odd" : "even");
- if (fbyte & 0x40) {
- /* VBI */
- if (vbi_buf != NULL)
- vbi_buffer_filled(dev,
- vbi_dma_q,
- vbi_buf);
- vbi_get_next_buf(vbi_dma_q, &vbi_buf);
- if (vbi_buf == NULL)
- vbioutp = NULL;
- else
- vbioutp = videobuf_to_vmalloc(
- &vbi_buf->vb);
-
- /* Video */
- if (buf != NULL)
- buffer_filled(dev, dma_q, buf);
- get_next_buf(dma_q, &buf);
- if (buf == NULL)
- outp = NULL;
- else
- outp = videobuf_to_vmalloc(&buf->vb);
-
- /* As long as isoc traffic is arriving, keep
- resetting the timer */
- if (dev->vid_timeout_running)
- mod_timer(&dev->vid_timeout,
- jiffies + (HZ / 10));
- if (dev->vbi_timeout_running)
- mod_timer(&dev->vbi_timeout,
- jiffies + (HZ / 10));
- }
-
- if (buf != NULL) {
- if (fbyte & 0x40)
- buf->top_field = 1;
- else
- buf->top_field = 0;
- }
-
- if (vbi_buf != NULL) {
- if (fbyte & 0x40)
- vbi_buf->top_field = 1;
- else
- vbi_buf->top_field = 0;
- }
-
- dev->vbi_read = 0;
- vbi_dma_q->pos = 0;
- dma_q->pos = 0;
- }
-
- vbi_field_size = dev->vbi_width * dev->vbi_height * 2;
- if (dev->vbi_read < vbi_field_size) {
- remain = vbi_field_size - dev->vbi_read;
- if (len < remain)
- lencopy = len;
- else
- lencopy = remain;
-
- if (vbi_buf != NULL)
- au0828_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
- vbioutp, len);
-
- len -= lencopy;
- p += lencopy;
- dev->vbi_read += lencopy;
- }
-
- if (dev->vbi_read >= vbi_field_size && buf != NULL)
- au0828_copy_video(dev, dma_q, buf, p, outp, len);
- }
- return rc;
-}
-
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct au0828_fh *fh = vq->priv_data;
- *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
-
- if (0 == *count)
- *count = AU0828_DEF_BUF;
-
- if (*count < AU0828_MIN_BUF)
- *count = AU0828_MIN_BUF;
- return 0;
-}
-
-/* This is called *without* dev->slock held; please keep it that way */
-static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
-{
- struct au0828_fh *fh = vq->priv_data;
- struct au0828_dev *dev = fh->dev;
- unsigned long flags = 0;
- if (in_interrupt())
- BUG();
-
- /* We used to wait for the buffer to finish here, but this didn't work
- because, as we were keeping the state as VIDEOBUF_QUEUED,
- videobuf_queue_cancel marked it as finished for us.
- (Also, it could wedge forever if the hardware was misconfigured.)
-
- This should be safe; by the time we get here, the buffer isn't
- queued anymore. If we ever start marking the buffers as
- VIDEOBUF_ACTIVE, it won't be, though.
- */
- spin_lock_irqsave(&dev->slock, flags);
- if (dev->isoc_ctl.buf == buf)
- dev->isoc_ctl.buf = NULL;
- spin_unlock_irqrestore(&dev->slock, flags);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct au0828_fh *fh = vq->priv_data;
- struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
- struct au0828_dev *dev = fh->dev;
- int rc = 0, urb_init = 0;
-
- buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
-
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0) {
- printk(KERN_INFO "videobuf_iolock failed\n");
- goto fail;
- }
- }
-
- if (!dev->isoc_ctl.num_bufs)
- urb_init = 1;
-
- if (urb_init) {
- rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
- AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
- au0828_isoc_copy);
- if (rc < 0) {
- printk(KERN_INFO "au0828_init_isoc failed\n");
- goto fail;
- }
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct au0828_buffer *buf = container_of(vb,
- struct au0828_buffer,
- vb);
- struct au0828_fh *fh = vq->priv_data;
- struct au0828_dev *dev = fh->dev;
- struct au0828_dmaqueue *vidq = &dev->vidq;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct au0828_buffer *buf = container_of(vb,
- struct au0828_buffer,
- vb);
-
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops au0828_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* ------------------------------------------------------------------
- V4L2 interface
- ------------------------------------------------------------------*/
-
-static int au0828_i2s_init(struct au0828_dev *dev)
-{
- /* Enable i2s mode */
- au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01);
- return 0;
-}
-
-/*
- * Auvitek au0828 analog stream enable
- * Please set interface0 to AS5 before enable the stream
- */
-int au0828_analog_stream_enable(struct au0828_dev *d)
-{
- dprintk(1, "au0828_analog_stream_enable called\n");
- au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
- au0828_writereg(d, 0x106, 0x00);
- /* set x position */
- au0828_writereg(d, 0x110, 0x00);
- au0828_writereg(d, 0x111, 0x00);
- au0828_writereg(d, 0x114, 0xa0);
- au0828_writereg(d, 0x115, 0x05);
- /* set y position */
- au0828_writereg(d, 0x112, 0x00);
- au0828_writereg(d, 0x113, 0x00);
- au0828_writereg(d, 0x116, 0xf2);
- au0828_writereg(d, 0x117, 0x00);
- au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
-
- return 0;
-}
-
-int au0828_analog_stream_disable(struct au0828_dev *d)
-{
- dprintk(1, "au0828_analog_stream_disable called\n");
- au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
- return 0;
-}
-
-void au0828_analog_stream_reset(struct au0828_dev *dev)
-{
- dprintk(1, "au0828_analog_stream_reset called\n");
- au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
- mdelay(30);
- au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3);
-}
-
-/*
- * Some operations needs to stop current streaming
- */
-static int au0828_stream_interrupt(struct au0828_dev *dev)
-{
- int ret = 0;
-
- dev->stream_state = STREAM_INTERRUPT;
- if (dev->dev_state == DEV_DISCONNECTED)
- return -ENODEV;
- else if (ret) {
- dev->dev_state = DEV_MISCONFIGURED;
- dprintk(1, "%s device is misconfigured!\n", __func__);
- return ret;
- }
- return 0;
-}
-
-/*
- * au0828_release_resources
- * unregister v4l2 devices
- */
-void au0828_analog_unregister(struct au0828_dev *dev)
-{
- dprintk(1, "au0828_release_resources called\n");
- mutex_lock(&au0828_sysfs_lock);
-
- if (dev->vdev)
- video_unregister_device(dev->vdev);
- if (dev->vbi_dev)
- video_unregister_device(dev->vbi_dev);
-
- mutex_unlock(&au0828_sysfs_lock);
-}
-
-
-/* Usage lock check functions */
-static int res_get(struct au0828_fh *fh, unsigned int bit)
-{
- struct au0828_dev *dev = fh->dev;
-
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&dev->lock);
- if (dev->resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&dev->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- dprintk(1, "res: get %d\n", bit);
- mutex_unlock(&dev->lock);
- return 1;
-}
-
-static int res_check(struct au0828_fh *fh, unsigned int bit)
-{
- return fh->resources & bit;
-}
-
-static int res_locked(struct au0828_dev *dev, unsigned int bit)
-{
- return dev->resources & bit;
-}
-
-static void res_free(struct au0828_fh *fh, unsigned int bits)
-{
- struct au0828_dev *dev = fh->dev;
-
- BUG_ON((fh->resources & bits) != bits);
-
- mutex_lock(&dev->lock);
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- dprintk(1, "res: put %d\n", bits);
- mutex_unlock(&dev->lock);
-}
-
-static int get_ressource(struct au0828_fh *fh)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return AU0828_RESOURCE_VIDEO;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return AU0828_RESOURCE_VBI;
- default:
- BUG();
- return 0;
- }
-}
-
-/* This function ensures that video frames continue to be delivered even if
- the ITU-656 input isn't receiving any data (thereby preventing applications
- such as tvtime from hanging) */
-void au0828_vid_buffer_timeout(unsigned long data)
-{
- struct au0828_dev *dev = (struct au0828_dev *) data;
- struct au0828_dmaqueue *dma_q = &dev->vidq;
- struct au0828_buffer *buf;
- unsigned char *vid_data;
- unsigned long flags = 0;
-
- spin_lock_irqsave(&dev->slock, flags);
-
- buf = dev->isoc_ctl.buf;
- if (buf != NULL) {
- vid_data = videobuf_to_vmalloc(&buf->vb);
- memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */
- buffer_filled(dev, dma_q, buf);
- }
- get_next_buf(dma_q, &buf);
-
- if (dev->vid_timeout_running == 1)
- mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
-
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-void au0828_vbi_buffer_timeout(unsigned long data)
-{
- struct au0828_dev *dev = (struct au0828_dev *) data;
- struct au0828_dmaqueue *dma_q = &dev->vbiq;
- struct au0828_buffer *buf;
- unsigned char *vbi_data;
- unsigned long flags = 0;
-
- spin_lock_irqsave(&dev->slock, flags);
-
- buf = dev->isoc_ctl.vbi_buf;
- if (buf != NULL) {
- vbi_data = videobuf_to_vmalloc(&buf->vb);
- memset(vbi_data, 0x00, buf->vb.size);
- vbi_buffer_filled(dev, dma_q, buf);
- }
- vbi_get_next_buf(dma_q, &buf);
-
- if (dev->vbi_timeout_running == 1)
- mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-
-static int au0828_v4l2_open(struct file *filp)
-{
- int ret = 0;
- struct video_device *vdev = video_devdata(filp);
- struct au0828_dev *dev = video_drvdata(filp);
- struct au0828_fh *fh;
- int type;
-
- 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;
- default:
- return -EINVAL;
- }
-
- fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
- if (NULL == fh) {
- dprintk(1, "Failed allocate au0828_fh struct!\n");
- return -ENOMEM;
- }
-
- fh->type = type;
- fh->dev = dev;
- filp->private_data = fh;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
- /* set au0828 interface0 to AS5 here again */
- ret = usb_set_interface(dev->usbdev, 0, 5);
- if (ret < 0) {
- printk(KERN_INFO "Au0828 can't set alternate to 5!\n");
- return -EBUSY;
- }
- dev->width = NTSC_STD_W;
- dev->height = NTSC_STD_H;
- dev->frame_size = dev->width * dev->height * 2;
- dev->field_size = dev->width * dev->height;
- dev->bytesperline = dev->width * 2;
-
- au0828_analog_stream_enable(dev);
- au0828_analog_stream_reset(dev);
-
- /* If we were doing ac97 instead of i2s, it would go here...*/
- au0828_i2s_init(dev);
-
- dev->stream_state = STREAM_OFF;
- dev->dev_state |= DEV_INITIALIZED;
- }
-
- dev->users++;
-
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
- NULL, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct au0828_buffer), fh, NULL);
-
- /* VBI Setup */
- dev->vbi_width = 720;
- dev->vbi_height = 1;
- videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops,
- NULL, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct au0828_buffer), fh, NULL);
-
- return ret;
-}
-
-static int au0828_v4l2_close(struct file *filp)
-{
- int ret;
- struct au0828_fh *fh = filp->private_data;
- struct au0828_dev *dev = fh->dev;
-
- if (res_check(fh, AU0828_RESOURCE_VIDEO)) {
- /* Cancel timeout thread in case they didn't call streamoff */
- dev->vid_timeout_running = 0;
- del_timer_sync(&dev->vid_timeout);
-
- videobuf_stop(&fh->vb_vidq);
- res_free(fh, AU0828_RESOURCE_VIDEO);
- }
-
- if (res_check(fh, AU0828_RESOURCE_VBI)) {
- /* Cancel timeout thread in case they didn't call streamoff */
- dev->vbi_timeout_running = 0;
- del_timer_sync(&dev->vbi_timeout);
-
- videobuf_stop(&fh->vb_vbiq);
- res_free(fh, AU0828_RESOURCE_VBI);
- }
-
- if (dev->users == 1) {
- if (dev->dev_state & DEV_DISCONNECTED) {
- au0828_analog_unregister(dev);
- kfree(dev);
- return 0;
- }
-
- au0828_analog_stream_disable(dev);
-
- au0828_uninit_isoc(dev);
-
- /* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
-
- /* When close the device, set the usb intf0 into alt0 to free
- USB bandwidth */
- ret = usb_set_interface(dev->usbdev, 0, 0);
- if (ret < 0)
- printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
- }
-
- videobuf_mmap_free(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vbiq);
- kfree(fh);
- dev->users--;
- wake_up_interruptible_nr(&dev->open, 1);
- return 0;
-}
-
-static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
- size_t count, loff_t *pos)
-{
- struct au0828_fh *fh = filp->private_data;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (res_locked(dev, AU0828_RESOURCE_VIDEO))
- return -EBUSY;
-
- return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
- filp->f_flags & O_NONBLOCK);
- }
-
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (!res_get(fh, AU0828_RESOURCE_VBI))
- return -EBUSY;
-
- if (dev->vbi_timeout_running == 0) {
- /* Handle case where caller tries to read without
- calling streamon first */
- dev->vbi_timeout_running = 1;
- mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
- }
-
- return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
- filp->f_flags & O_NONBLOCK);
- }
-
- return 0;
-}
-
-static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
-{
- struct au0828_fh *fh = filp->private_data;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (!res_get(fh, AU0828_RESOURCE_VIDEO))
- return POLLERR;
- return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (!res_get(fh, AU0828_RESOURCE_VBI))
- return POLLERR;
- return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
- } else {
- return POLLERR;
- }
-}
-
-static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct au0828_fh *fh = filp->private_data;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
-
- return rc;
-}
-
-static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
- struct v4l2_format *format)
-{
- int ret;
- int width = format->fmt.pix.width;
- int height = format->fmt.pix.height;
-
- if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* If they are demanding a format other than the one we support,
- bail out (tvtime asks for UYVY and then retries with YUYV) */
- if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
- return -EINVAL;
-
- /* format->fmt.pix.width only support 720 and height 480 */
- if (width != 720)
- width = 720;
- if (height != 480)
- height = 480;
-
- format->fmt.pix.width = width;
- format->fmt.pix.height = height;
- format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
- format->fmt.pix.bytesperline = width * 2;
- format->fmt.pix.sizeimage = width * height * 2;
- format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- format->fmt.pix.field = V4L2_FIELD_INTERLACED;
-
- if (cmd == VIDIOC_TRY_FMT)
- return 0;
-
- /* maybe set new image format, driver current only support 720*480 */
- dev->width = width;
- dev->height = height;
- dev->frame_size = width * height * 2;
- dev->field_size = width * height;
- dev->bytesperline = width * 2;
-
- if (dev->stream_state == STREAM_ON) {
- dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n");
- ret = au0828_stream_interrupt(dev);
- if (ret != 0) {
- dprintk(1, "error interrupting video stream!\n");
- return ret;
- }
- }
-
- /* set au0828 interface0 to AS5 here again */
- ret = usb_set_interface(dev->usbdev, 0, 5);
- if (ret < 0) {
- printk(KERN_INFO "Au0828 can't set alt setting to 5!\n");
- return -EBUSY;
- }
-
- au0828_analog_stream_enable(dev);
-
- return 0;
-}
-
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
- if (qc->type)
- return 0;
- else
- return -EINVAL;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- strlcpy(cap->driver, "au0828", sizeof(cap->driver));
- strlcpy(cap->card, dev->board.name, sizeof(cap->card));
- strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
-
- /*set the device capabilities */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_TUNER;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (f->index)
- return -EINVAL;
-
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(f->description, "Packed YUV2");
-
- f->flags = 0;
- f->pixelformat = V4L2_PIX_FMT_UYVY;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
- f->fmt.pix.bytesperline = dev->bytesperline;
- f->fmt.pix.sizeimage = dev->frame_size;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- mutex_lock(&dev->lock);
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- printk(KERN_INFO "%s queue busy\n", __func__);
- rc = -EBUSY;
- goto out;
- }
-
- rc = au0828_set_format(dev, VIDIOC_S_FMT, f);
-out:
- mutex_unlock(&dev->lock);
- return rc;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- /* FIXME: when we support something other than NTSC, we are going to
- have to make the au0828 bridge adjust the size of its capture
- buffer, which is currently hardcoded at 720x480 */
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm);
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- unsigned int tmp;
-
- static const char *inames[] = {
- [AU0828_VMUX_UNDEFINED] = "Undefined",
- [AU0828_VMUX_COMPOSITE] = "Composite",
- [AU0828_VMUX_SVIDEO] = "S-Video",
- [AU0828_VMUX_CABLE] = "Cable TV",
- [AU0828_VMUX_TELEVISION] = "Television",
- [AU0828_VMUX_DVB] = "DVB",
- [AU0828_VMUX_DEBUG] = "tv debug"
- };
-
- tmp = input->index;
-
- if (tmp >= AU0828_MAX_INPUT)
- return -EINVAL;
- if (AUVI_INPUT(tmp).type == 0)
- return -EINVAL;
-
- input->index = tmp;
- strcpy(input->name, inames[AUVI_INPUT(tmp).type]);
- if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) ||
- (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE))
- input->type |= V4L2_INPUT_TYPE_TUNER;
- else
- input->type |= V4L2_INPUT_TYPE_CAMERA;
-
- input->std = dev->vdev->tvnorms;
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- *i = dev->ctrl_input;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int i;
-
- dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
- index);
- if (index >= AU0828_MAX_INPUT)
- return -EINVAL;
- if (AUVI_INPUT(index).type == 0)
- return -EINVAL;
- dev->ctrl_input = index;
-
- switch (AUVI_INPUT(index).type) {
- case AU0828_VMUX_SVIDEO:
- dev->input_type = AU0828_VMUX_SVIDEO;
- break;
- case AU0828_VMUX_COMPOSITE:
- dev->input_type = AU0828_VMUX_COMPOSITE;
- break;
- case AU0828_VMUX_TELEVISION:
- dev->input_type = AU0828_VMUX_TELEVISION;
- break;
- default:
- dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n",
- AUVI_INPUT(index).type);
- break;
- }
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
- AUVI_INPUT(index).vmux, 0, 0);
-
- for (i = 0; i < AU0828_MAX_INPUT; i++) {
- int enable = 0;
- if (AUVI_INPUT(i).audio_setup == NULL)
- continue;
-
- if (i == index)
- enable = 1;
- else
- enable = 0;
- if (enable) {
- (AUVI_INPUT(i).audio_setup)(dev, enable);
- } else {
- /* Make sure we leave it turned on if some
- other input is routed to this callback */
- if ((AUVI_INPUT(i).audio_setup) !=
- ((AUVI_INPUT(index).audio_setup))) {
- (AUVI_INPUT(i).audio_setup)(dev, enable);
- }
- }
- }
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
- AUVI_INPUT(index).amux, 0, 0);
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- unsigned int index = a->index;
-
- if (a->index > 1)
- return -EINVAL;
-
- index = dev->ctrl_ainput;
- if (index == 0)
- strcpy(a->name, "Television");
- else
- strcpy(a->name, "Line in");
-
- a->capability = V4L2_AUDCAP_STEREO;
- a->index = index;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- if (a->index != dev->ctrl_ainput)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
- return 0;
-
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- if (t->index != 0)
- return -EINVAL;
-
- strcpy(t->name, "Auvitek tuner");
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- if (t->index != 0)
- return -EINVAL;
-
- t->type = V4L2_TUNER_ANALOG_TV;
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
- dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
- t->afc);
- return 0;
-
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- freq->type = V4L2_TUNER_ANALOG_TV;
- freq->frequency = dev->ctrl_freq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- if (freq->tuner != 0)
- return -EINVAL;
- if (freq->type != V4L2_TUNER_ANALOG_TV)
- return -EINVAL;
-
- dev->ctrl_freq = freq->frequency;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
-
- au0828_analog_stream_reset(dev);
-
- return 0;
-}
-
-
-/* RAW VBI ioctls */
-
-static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- format->fmt.vbi.samples_per_line = dev->vbi_width;
- format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- format->fmt.vbi.offset = 0;
- format->fmt.vbi.flags = 0;
- format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
-
- format->fmt.vbi.count[0] = dev->vbi_height;
- format->fmt.vbi.count[1] = dev->vbi_height;
- format->fmt.vbi.start[0] = 21;
- format->fmt.vbi.start[1] = 284;
-
- return 0;
-}
-
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
-
- if (v4l2_chip_match_host(&chip->match)) {
- chip->ident = V4L2_IDENT_AU0828;
- return 0;
- }
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
- if (chip->ident == V4L2_IDENT_NONE)
- return -EINVAL;
-
- return 0;
-}
-
-static int vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cc)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- cc->bounds.left = 0;
- cc->bounds.top = 0;
- cc->bounds.width = dev->width;
- cc->bounds.height = dev->height;
-
- cc->defrect = cc->bounds;
-
- cc->pixelaspect.numerator = 54;
- cc->pixelaspect.denominator = 59;
-
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc = -EINVAL;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (unlikely(type != fh->type))
- return -EINVAL;
-
- dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
- fh, type, fh->resources, dev->resources);
-
- if (unlikely(!res_get(fh, get_ressource(fh))))
- return -EBUSY;
-
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- au0828_analog_stream_enable(dev);
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
- }
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- rc = videobuf_streamon(&fh->vb_vidq);
- dev->vid_timeout_running = 1;
- mod_timer(&dev->vid_timeout, jiffies + (HZ / 10));
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- rc = videobuf_streamon(&fh->vb_vbiq);
- dev->vbi_timeout_running = 1;
- mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10));
- }
-
- return rc;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
- int i;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
- return -EINVAL;
- if (type != fh->type)
- return -EINVAL;
-
- dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
- fh, type, fh->resources, dev->resources);
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev->vid_timeout_running = 0;
- del_timer_sync(&dev->vid_timeout);
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
- rc = au0828_stream_interrupt(dev);
- if (rc != 0)
- return rc;
-
- for (i = 0; i < AU0828_MAX_INPUT; i++) {
- if (AUVI_INPUT(i).audio_setup == NULL)
- continue;
- (AUVI_INPUT(i).audio_setup)(dev, 0);
- }
-
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh, AU0828_RESOURCE_VIDEO);
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- dev->vbi_timeout_running = 0;
- del_timer_sync(&dev->vbi_timeout);
-
- videobuf_streamoff(&fh->vb_vbiq);
- res_free(fh, AU0828_RESOURCE_VBI);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- default:
- return -EINVAL;
- }
- return 0;
-}
-#endif
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_reqbufs(&fh->vb_vidq, rb);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_reqbufs(&fh->vb_vbiq, rb);
-
- return rc;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_querybuf(&fh->vb_vidq, b);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_querybuf(&fh->vb_vbiq, b);
-
- return rc;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_qbuf(&fh->vb_vidq, b);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_qbuf(&fh->vb_vbiq, b);
-
- return rc;
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct au0828_fh *fh = priv;
- struct au0828_dev *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- /* Workaround for a bug in the au0828 hardware design that sometimes
- results in the colorspace being inverted */
- if (dev->greenscreen_detected == 1) {
- dprintk(1, "Detected green frame. Resetting stream...\n");
- au0828_analog_stream_reset(dev);
- dev->greenscreen_detected = 0;
- }
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK);
-
- return rc;
-}
-
-static struct v4l2_file_operations au0828_v4l_fops = {
- .owner = THIS_MODULE,
- .open = au0828_v4l2_open,
- .release = au0828_v4l2_close,
- .read = au0828_v4l2_read,
- .poll = au0828_v4l2_poll,
- .mmap = au0828_v4l2_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_cropcap = vidioc_cropcap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-};
-
-static const struct video_device au0828_video_template = {
- .fops = &au0828_v4l_fops,
- .release = video_device_release,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = V4L2_STD_NTSC_M,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-/**************************************************************************/
-
-int au0828_analog_register(struct au0828_dev *dev,
- struct usb_interface *interface)
-{
- int retval = -ENOMEM;
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- int i, ret;
-
- dprintk(1, "au0828_analog_register called!\n");
-
- /* set au0828 usb interface0 to as5 */
- retval = usb_set_interface(dev->usbdev,
- interface->cur_altsetting->desc.bInterfaceNumber, 5);
- if (retval != 0) {
- printk(KERN_INFO "Failure setting usb interface0 to as5\n");
- return retval;
- }
-
- /* Figure out which endpoint has the isoc interface */
- iface_desc = interface->cur_altsetting;
- for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_ISOC)) {
-
- /* we find our isoc in endpoint */
- u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize);
- dev->max_pkt_size = (tmp & 0x07ff) *
- (((tmp & 0x1800) >> 11) + 1);
- dev->isoc_in_endpointaddr = endpoint->bEndpointAddress;
- }
- }
- if (!(dev->isoc_in_endpointaddr)) {
- printk(KERN_INFO "Could not locate isoc endpoint\n");
- kfree(dev);
- return -ENODEV;
- }
-
- init_waitqueue_head(&dev->open);
- spin_lock_init(&dev->slock);
- mutex_init(&dev->lock);
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
- INIT_LIST_HEAD(&dev->vbiq.active);
- INIT_LIST_HEAD(&dev->vbiq.queued);
-
- dev->vid_timeout.function = au0828_vid_buffer_timeout;
- dev->vid_timeout.data = (unsigned long) dev;
- init_timer(&dev->vid_timeout);
-
- dev->vbi_timeout.function = au0828_vbi_buffer_timeout;
- dev->vbi_timeout.data = (unsigned long) dev;
- init_timer(&dev->vbi_timeout);
-
- dev->width = NTSC_STD_W;
- dev->height = NTSC_STD_H;
- dev->field_size = dev->width * dev->height;
- dev->frame_size = dev->field_size << 1;
- dev->bytesperline = dev->width << 1;
- dev->ctrl_ainput = 0;
-
- /* allocate and fill v4l2 video struct */
- dev->vdev = video_device_alloc();
- if (NULL == dev->vdev) {
- dprintk(1, "Can't allocate video_device.\n");
- return -ENOMEM;
- }
-
- /* allocate the VBI struct */
- dev->vbi_dev = video_device_alloc();
- if (NULL == dev->vbi_dev) {
- dprintk(1, "Can't allocate vbi_device.\n");
- ret = -ENOMEM;
- goto err_vdev;
- }
-
- /* Fill the video capture device struct */
- *dev->vdev = au0828_video_template;
- dev->vdev->parent = &dev->usbdev->dev;
- strcpy(dev->vdev->name, "au0828a video");
-
- /* Setup the VBI device */
- *dev->vbi_dev = au0828_video_template;
- dev->vbi_dev->parent = &dev->usbdev->dev;
- strcpy(dev->vbi_dev->name, "au0828a vbi");
-
- /* Register the v4l2 device */
- video_set_drvdata(dev->vdev, dev);
- retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
- if (retval != 0) {
- dprintk(1, "unable to register video device (error = %d).\n",
- retval);
- ret = -ENODEV;
- goto err_vbi_dev;
- }
-
- /* Register the vbi device */
- video_set_drvdata(dev->vbi_dev, dev);
- retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
- if (retval != 0) {
- dprintk(1, "unable to register vbi device (error = %d).\n",
- retval);
- ret = -ENODEV;
- goto err_vbi_dev;
- }
-
- dprintk(1, "%s completed!\n", __func__);
-
- return 0;
-
-err_vbi_dev:
- video_device_release(dev->vbi_dev);
-err_vdev:
- video_device_release(dev->vdev);
- return ret;
-}
-
diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h
deleted file mode 100644
index 9cde35321824..000000000000
--- a/drivers/media/video/au0828/au0828.h
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Driver for the Auvitek AU0828 USB bridge
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <media/tveeprom.h>
-
-/* Analog */
-#include <linux/videodev2.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/v4l2-device.h>
-
-/* DVB */
-#include "demux.h"
-#include "dmxdev.h"
-#include "dvb_demux.h"
-#include "dvb_frontend.h"
-#include "dvb_net.h"
-#include "dvbdev.h"
-
-#include "au0828-reg.h"
-#include "au0828-cards.h"
-
-#define DRIVER_NAME "au0828"
-#define URB_COUNT 16
-#define URB_BUFSIZE (0xe522)
-
-/* Analog constants */
-#define NTSC_STD_W 720
-#define NTSC_STD_H 480
-
-#define AU0828_INTERLACED_DEFAULT 1
-#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
-
-/* Defination for AU0828 USB transfer */
-#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
-#define AU0828_ISO_PACKETS_PER_URB 128
-
-#define AU0828_MIN_BUF 4
-#define AU0828_DEF_BUF 8
-
-#define AU0828_MAX_INPUT 4
-
-/* au0828 resource types (used for res_get/res_lock etc */
-#define AU0828_RESOURCE_VIDEO 0x01
-#define AU0828_RESOURCE_VBI 0x02
-
-enum au0828_itype {
- AU0828_VMUX_UNDEFINED = 0,
- AU0828_VMUX_COMPOSITE,
- AU0828_VMUX_SVIDEO,
- AU0828_VMUX_CABLE,
- AU0828_VMUX_TELEVISION,
- AU0828_VMUX_DVB,
- AU0828_VMUX_DEBUG
-};
-
-struct au0828_input {
- enum au0828_itype type;
- unsigned int vmux;
- unsigned int amux;
- void (*audio_setup) (void *priv, int enable);
-};
-
-struct au0828_board {
- char *name;
- unsigned int tuner_type;
- unsigned char tuner_addr;
- unsigned char i2c_clk_divider;
- struct au0828_input input[AU0828_MAX_INPUT];
-
-};
-
-struct au0828_dvb {
- struct mutex lock;
- struct dvb_adapter adapter;
- struct dvb_frontend *frontend;
- struct dvb_demux demux;
- struct dmxdev dmxdev;
- struct dmx_frontend fe_hw;
- struct dmx_frontend fe_mem;
- struct dvb_net net;
- int feeding;
-};
-
-enum au0828_stream_state {
- STREAM_OFF,
- STREAM_INTERRUPT,
- STREAM_ON
-};
-
-#define AUVI_INPUT(nr) (dev->board.input[nr])
-
-/* device state */
-enum au0828_dev_state {
- DEV_INITIALIZED = 0x01,
- DEV_DISCONNECTED = 0x02,
- DEV_MISCONFIGURED = 0x04
-};
-
-struct au0828_fh {
- struct au0828_dev *dev;
- unsigned int resources;
-
- struct videobuf_queue vb_vidq;
- struct videobuf_queue vb_vbiq;
- enum v4l2_buf_type type;
-};
-
-struct au0828_usb_isoc_ctl {
- /* max packet size of isoc transaction */
- int max_pkt_size;
-
- /* number of allocated urbs */
- int num_bufs;
-
- /* urb for isoc transfers */
- struct urb **urb;
-
- /* transfer buffers for isoc transfer */
- char **transfer_buffer;
-
- /* Last buffer command and region */
- u8 cmd;
- int pos, size, pktsize;
-
- /* Last field: ODD or EVEN? */
- int field;
-
- /* Stores incomplete commands */
- u32 tmp_buf;
- int tmp_buf_len;
-
- /* Stores already requested buffers */
- struct au0828_buffer *buf;
- struct au0828_buffer *vbi_buf;
-
- /* Stores the number of received fields */
- int nfields;
-
- /* isoc urb callback */
- int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
-
-};
-
-/* buffer for one video frame */
-struct au0828_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- struct list_head frame;
- int top_field;
- int receiving;
-};
-
-struct au0828_dmaqueue {
- struct list_head active;
- struct list_head queued;
-
- wait_queue_head_t wq;
-
- /* Counters to control buffer fill */
- int pos;
-};
-
-struct au0828_dev {
- struct mutex mutex;
- struct usb_device *usbdev;
- int boardnr;
- struct au0828_board board;
- u8 ctrlmsg[64];
-
- /* I2C */
- struct i2c_adapter i2c_adap;
- struct i2c_algorithm i2c_algo;
- struct i2c_client i2c_client;
- u32 i2c_rc;
-
- /* Digital */
- struct au0828_dvb dvb;
-
- /* Analog */
- struct v4l2_device v4l2_dev;
- int users;
- unsigned int resources; /* resources in use */
- struct video_device *vdev;
- struct video_device *vbi_dev;
- struct timer_list vid_timeout;
- int vid_timeout_running;
- struct timer_list vbi_timeout;
- int vbi_timeout_running;
- int width;
- int height;
- int vbi_width;
- int vbi_height;
- u32 vbi_read;
- u32 field_size;
- u32 frame_size;
- u32 bytesperline;
- int type;
- u8 ctrl_ainput;
- __u8 isoc_in_endpointaddr;
- u8 isoc_init_ok;
- int greenscreen_detected;
- unsigned int frame_count;
- int ctrl_freq;
- int input_type;
- unsigned int ctrl_input;
- enum au0828_dev_state dev_state;
- enum au0828_stream_state stream_state;
- wait_queue_head_t open;
-
- struct mutex lock;
-
- /* Isoc control struct */
- struct au0828_dmaqueue vidq;
- struct au0828_dmaqueue vbiq;
- struct au0828_usb_isoc_ctl isoc_ctl;
- spinlock_t slock;
-
- /* usb transfer */
- int alt; /* alternate */
- int max_pkt_size; /* max packet size of isoc transaction */
- int num_alt; /* Number of alternative settings */
- unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
- struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
- char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
- transfer */
-
- /* USB / URB Related */
- int urb_streaming;
- struct urb *urbs[URB_COUNT];
-};
-
-/* ----------------------------------------------------------- */
-#define au0828_read(dev, reg) au0828_readreg(dev, reg)
-#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
-#define au0828_andor(dev, reg, mask, value) \
- au0828_writereg(dev, reg, \
- (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask)))
-
-#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit))
-#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0)
-
-/* ----------------------------------------------------------- */
-/* au0828-core.c */
-extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
-extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
-extern int au0828_debug;
-
-/* ----------------------------------------------------------- */
-/* au0828-cards.c */
-extern struct au0828_board au0828_boards[];
-extern struct usb_device_id au0828_usb_id_table[];
-extern void au0828_gpio_setup(struct au0828_dev *dev);
-extern int au0828_tuner_callback(void *priv, int component,
- int command, int arg);
-extern void au0828_card_setup(struct au0828_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* au0828-i2c.c */
-extern int au0828_i2c_register(struct au0828_dev *dev);
-extern int au0828_i2c_unregister(struct au0828_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* au0828-video.c */
-int au0828_analog_register(struct au0828_dev *dev,
- struct usb_interface *interface);
-int au0828_analog_stream_disable(struct au0828_dev *d);
-void au0828_analog_unregister(struct au0828_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* au0828-dvb.c */
-extern int au0828_dvb_register(struct au0828_dev *dev);
-extern void au0828_dvb_unregister(struct au0828_dev *dev);
-
-/* au0828-vbi.c */
-extern struct videobuf_queue_ops au0828_vbi_qops;
-
-#define dprintk(level, fmt, arg...)\
- do { if (au0828_debug & level)\
- printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\
- } while (0)
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
deleted file mode 100644
index 0aba45e34f70..000000000000
--- a/drivers/media/video/blackfin/bfin_capture.c
+++ /dev/null
@@ -1,1063 +0,0 @@
-/*
- * Analog Devices video capture driver
- *
- * Copyright (c) 2011 Analog Devices Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/types.h>
-
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include <asm/dma.h>
-
-#include <media/blackfin/bfin_capture.h>
-#include <media/blackfin/ppi.h>
-
-#define CAPTURE_DRV_NAME "bfin_capture"
-#define BCAP_MIN_NUM_BUF 2
-
-struct bcap_format {
- char *desc;
- u32 pixelformat;
- enum v4l2_mbus_pixelcode mbus_code;
- int bpp; /* bits per pixel */
-};
-
-struct bcap_buffer {
- struct vb2_buffer vb;
- struct list_head list;
-};
-
-struct bcap_device {
- /* capture device instance */
- struct v4l2_device v4l2_dev;
- /* v4l2 control handler */
- struct v4l2_ctrl_handler ctrl_handler;
- /* device node data */
- struct video_device *video_dev;
- /* sub device instance */
- struct v4l2_subdev *sd;
- /* capture config */
- struct bfin_capture_config *cfg;
- /* ppi interface */
- struct ppi_if *ppi;
- /* current input */
- unsigned int cur_input;
- /* current selected standard */
- v4l2_std_id std;
- /* used to store pixel format */
- struct v4l2_pix_format fmt;
- /* bits per pixel*/
- int bpp;
- /* used to store sensor supported format */
- struct bcap_format *sensor_formats;
- /* number of sensor formats array */
- int num_sensor_formats;
- /* pointing to current video buffer */
- struct bcap_buffer *cur_frm;
- /* pointing to next video buffer */
- struct bcap_buffer *next_frm;
- /* buffer queue used in videobuf2 */
- struct vb2_queue buffer_queue;
- /* allocator-specific contexts for each plane */
- struct vb2_alloc_ctx *alloc_ctx;
- /* queue of filled frames */
- struct list_head dma_queue;
- /* used in videobuf2 callback */
- spinlock_t lock;
- /* used to access capture device */
- struct mutex mutex;
- /* used to wait ppi to complete one transfer */
- struct completion comp;
- /* prepare to stop */
- bool stop;
-};
-
-struct bcap_fh {
- struct v4l2_fh fh;
- /* indicates whether this file handle is doing IO */
- bool io_allowed;
-};
-
-static const struct bcap_format bcap_formats[] = {
- {
- .desc = "YCbCr 4:2:2 Interleaved UYVY",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
- .bpp = 16,
- },
- {
- .desc = "YCbCr 4:2:2 Interleaved YUYV",
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
- .bpp = 16,
- },
- {
- .desc = "RGB 565",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
- .bpp = 16,
- },
- {
- .desc = "RGB 444",
- .pixelformat = V4L2_PIX_FMT_RGB444,
- .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
- .bpp = 16,
- },
-
-};
-#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
-
-static irqreturn_t bcap_isr(int irq, void *dev_id);
-
-static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
-{
- return container_of(vb, struct bcap_buffer, vb);
-}
-
-static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
-{
- enum v4l2_mbus_pixelcode code;
- struct bcap_format *sf;
- unsigned int num_formats = 0;
- int i, j;
-
- while (!v4l2_subdev_call(bcap_dev->sd, video,
- enum_mbus_fmt, num_formats, &code))
- num_formats++;
- if (!num_formats)
- return -ENXIO;
-
- sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);
- if (!sf)
- return -ENOMEM;
-
- for (i = 0; i < num_formats; i++) {
- v4l2_subdev_call(bcap_dev->sd, video,
- enum_mbus_fmt, i, &code);
- for (j = 0; j < BCAP_MAX_FMTS; j++)
- if (code == bcap_formats[j].mbus_code)
- break;
- if (j == BCAP_MAX_FMTS) {
- /* we don't allow this sensor working with our bridge */
- kfree(sf);
- return -EINVAL;
- }
- sf[i] = bcap_formats[j];
- }
- bcap_dev->sensor_formats = sf;
- bcap_dev->num_sensor_formats = num_formats;
- return 0;
-}
-
-static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
-{
- bcap_dev->num_sensor_formats = 0;
- kfree(bcap_dev->sensor_formats);
- bcap_dev->sensor_formats = NULL;
-}
-
-static int bcap_open(struct file *file)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct video_device *vfd = bcap_dev->video_dev;
- struct bcap_fh *bcap_fh;
-
- if (!bcap_dev->sd) {
- v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
- return -ENODEV;
- }
-
- bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
- if (!bcap_fh) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
-
- v4l2_fh_init(&bcap_fh->fh, vfd);
-
- /* store pointer to v4l2_fh in private_data member of file */
- file->private_data = &bcap_fh->fh;
- v4l2_fh_add(&bcap_fh->fh);
- bcap_fh->io_allowed = false;
- return 0;
-}
-
-static int bcap_release(struct file *file)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_fh *fh = file->private_data;
- struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
- /* if this instance is doing IO */
- if (bcap_fh->io_allowed)
- vb2_queue_release(&bcap_dev->buffer_queue);
-
- file->private_data = NULL;
- v4l2_fh_del(&bcap_fh->fh);
- v4l2_fh_exit(&bcap_fh->fh);
- kfree(bcap_fh);
- return 0;
-}
-
-static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return vb2_mmap(&bcap_dev->buffer_queue, vma);
-}
-
-#ifndef CONFIG_MMU
-static unsigned long bcap_get_unmapped_area(struct file *file,
- unsigned long addr,
- unsigned long len,
- unsigned long pgoff,
- unsigned long flags)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
- addr,
- len,
- pgoff,
- flags);
-}
-#endif
-
-static unsigned int bcap_poll(struct file *file, poll_table *wait)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return vb2_poll(&bcap_dev->buffer_queue, file, wait);
-}
-
-static int bcap_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
-
- if (*nbuffers < BCAP_MIN_NUM_BUF)
- *nbuffers = BCAP_MIN_NUM_BUF;
-
- *nplanes = 1;
- sizes[0] = bcap_dev->fmt.sizeimage;
- alloc_ctxs[0] = bcap_dev->alloc_ctx;
-
- return 0;
-}
-
-static int bcap_buffer_init(struct vb2_buffer *vb)
-{
- struct bcap_buffer *buf = to_bcap_vb(vb);
-
- INIT_LIST_HEAD(&buf->list);
- return 0;
-}
-
-static int bcap_buffer_prepare(struct vb2_buffer *vb)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct bcap_buffer *buf = to_bcap_vb(vb);
- unsigned long size;
-
- size = bcap_dev->fmt.sizeimage;
- if (vb2_plane_size(vb, 0) < size) {
- v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
- vb2_set_plane_payload(&buf->vb, 0, size);
-
- return 0;
-}
-
-static void bcap_buffer_queue(struct vb2_buffer *vb)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct bcap_buffer *buf = to_bcap_vb(vb);
- unsigned long flags;
-
- spin_lock_irqsave(&bcap_dev->lock, flags);
- list_add_tail(&buf->list, &bcap_dev->dma_queue);
- spin_unlock_irqrestore(&bcap_dev->lock, flags);
-}
-
-static void bcap_buffer_cleanup(struct vb2_buffer *vb)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
- struct bcap_buffer *buf = to_bcap_vb(vb);
- unsigned long flags;
-
- spin_lock_irqsave(&bcap_dev->lock, flags);
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&bcap_dev->lock, flags);
-}
-
-static void bcap_lock(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- mutex_lock(&bcap_dev->mutex);
-}
-
-static void bcap_unlock(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- mutex_unlock(&bcap_dev->mutex);
-}
-
-static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- struct ppi_if *ppi = bcap_dev->ppi;
- struct ppi_params params;
- int ret;
-
- /* enable streamon on the sub device */
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
- if (ret && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
- return ret;
- }
-
- /* set ppi params */
- params.width = bcap_dev->fmt.width;
- params.height = bcap_dev->fmt.height;
- params.bpp = bcap_dev->bpp;
- params.ppi_control = bcap_dev->cfg->ppi_control;
- params.int_mask = bcap_dev->cfg->int_mask;
- params.blank_clocks = bcap_dev->cfg->blank_clocks;
- ret = ppi->ops->set_params(ppi, &params);
- if (ret < 0) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Error in setting ppi params\n");
- return ret;
- }
-
- /* attach ppi DMA irq handler */
- ret = ppi->ops->attach_irq(ppi, bcap_isr);
- if (ret < 0) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Error in attaching interrupt handler\n");
- return ret;
- }
-
- INIT_COMPLETION(bcap_dev->comp);
- bcap_dev->stop = false;
- return 0;
-}
-
-static int bcap_stop_streaming(struct vb2_queue *vq)
-{
- struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
- struct ppi_if *ppi = bcap_dev->ppi;
- int ret;
-
- if (!vb2_is_streaming(vq))
- return 0;
-
- bcap_dev->stop = true;
- wait_for_completion(&bcap_dev->comp);
- ppi->ops->stop(ppi);
- ppi->ops->detach_irq(ppi);
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
- if (ret && (ret != -ENOIOCTLCMD))
- v4l2_err(&bcap_dev->v4l2_dev,
- "stream off failed in subdev\n");
-
- /* release all active buffers */
- while (!list_empty(&bcap_dev->dma_queue)) {
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- list_del(&bcap_dev->next_frm->list);
- vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
- }
- return 0;
-}
-
-static struct vb2_ops bcap_video_qops = {
- .queue_setup = bcap_queue_setup,
- .buf_init = bcap_buffer_init,
- .buf_prepare = bcap_buffer_prepare,
- .buf_cleanup = bcap_buffer_cleanup,
- .buf_queue = bcap_buffer_queue,
- .wait_prepare = bcap_unlock,
- .wait_finish = bcap_lock,
- .start_streaming = bcap_start_streaming,
- .stop_streaming = bcap_stop_streaming,
-};
-
-static int bcap_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req_buf)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct vb2_queue *vq = &bcap_dev->buffer_queue;
- struct v4l2_fh *fh = file->private_data;
- struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
- if (vb2_is_busy(vq))
- return -EBUSY;
-
- bcap_fh->io_allowed = true;
-
- return vb2_reqbufs(vq, req_buf);
-}
-
-static int bcap_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return vb2_querybuf(&bcap_dev->buffer_queue, buf);
-}
-
-static int bcap_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_fh *fh = file->private_data;
- struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
- if (!bcap_fh->io_allowed)
- return -EBUSY;
-
- return vb2_qbuf(&bcap_dev->buffer_queue, buf);
-}
-
-static int bcap_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_fh *fh = file->private_data;
- struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
-
- if (!bcap_fh->io_allowed)
- return -EBUSY;
-
- return vb2_dqbuf(&bcap_dev->buffer_queue,
- buf, file->f_flags & O_NONBLOCK);
-}
-
-static irqreturn_t bcap_isr(int irq, void *dev_id)
-{
- struct ppi_if *ppi = dev_id;
- struct bcap_device *bcap_dev = ppi->priv;
- struct timeval timevalue;
- struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
- dma_addr_t addr;
-
- spin_lock(&bcap_dev->lock);
-
- if (bcap_dev->cur_frm != bcap_dev->next_frm) {
- do_gettimeofday(&timevalue);
- vb->v4l2_buf.timestamp = timevalue;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- bcap_dev->cur_frm = bcap_dev->next_frm;
- }
-
- ppi->ops->stop(ppi);
-
- if (bcap_dev->stop) {
- complete(&bcap_dev->comp);
- } else {
- if (!list_empty(&bcap_dev->dma_queue)) {
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- list_del(&bcap_dev->next_frm->list);
- addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
- ppi->ops->update_addr(ppi, (unsigned long)addr);
- }
- ppi->ops->start(ppi);
- }
-
- spin_unlock(&bcap_dev->lock);
-
- return IRQ_HANDLED;
-}
-
-static int bcap_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bcap_fh *fh = file->private_data;
- struct ppi_if *ppi = bcap_dev->ppi;
- dma_addr_t addr;
- int ret;
-
- if (!fh->io_allowed)
- return -EBUSY;
-
- /* call streamon to start streaming in videobuf */
- ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
- if (ret)
- return ret;
-
- /* if dma queue is empty, return error */
- if (list_empty(&bcap_dev->dma_queue)) {
- v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
- ret = -EINVAL;
- goto err;
- }
-
- /* get the next frame from the dma queue */
- bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
- struct bcap_buffer, list);
- bcap_dev->cur_frm = bcap_dev->next_frm;
- /* remove buffer from the dma queue */
- list_del(&bcap_dev->cur_frm->list);
- addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
- /* update DMA address */
- ppi->ops->update_addr(ppi, (unsigned long)addr);
- /* enable ppi */
- ppi->ops->start(ppi);
-
- return 0;
-err:
- vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
- return ret;
-}
-
-static int bcap_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bcap_fh *fh = file->private_data;
-
- if (!fh->io_allowed)
- return -EBUSY;
-
- return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
-}
-
-static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
-}
-
-static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- *std = bcap_dev->std;
- return 0;
-}
-
-static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- int ret;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
- if (ret < 0)
- return ret;
-
- bcap_dev->std = *std;
- return 0;
-}
-
-static int bcap_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bfin_capture_config *config = bcap_dev->cfg;
- int ret;
- u32 status;
-
- if (input->index >= config->num_inputs)
- return -EINVAL;
-
- *input = config->inputs[input->index];
- /* get input status */
- ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
- if (!ret)
- input->status = status;
- return 0;
-}
-
-static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- *index = bcap_dev->cur_input;
- return 0;
-}
-
-static int bcap_s_input(struct file *file, void *priv, unsigned int index)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bfin_capture_config *config = bcap_dev->cfg;
- struct bcap_route *route;
- int ret;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- if (index >= config->num_inputs)
- return -EINVAL;
-
- route = &config->routes[index];
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
- route->input, route->output, 0);
- if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
- return ret;
- }
- bcap_dev->cur_input = index;
- return 0;
-}
-
-static int bcap_try_format(struct bcap_device *bcap,
- struct v4l2_pix_format *pixfmt,
- enum v4l2_mbus_pixelcode *mbus_code,
- int *bpp)
-{
- struct bcap_format *sf = bcap->sensor_formats;
- struct bcap_format *fmt = NULL;
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret, i;
-
- for (i = 0; i < bcap->num_sensor_formats; i++) {
- fmt = &sf[i];
- if (pixfmt->pixelformat == fmt->pixelformat)
- break;
- }
- if (i == bcap->num_sensor_formats)
- fmt = &sf[0];
-
- if (mbus_code)
- *mbus_code = fmt->mbus_code;
- if (bpp)
- *bpp = fmt->bpp;
- v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
- ret = v4l2_subdev_call(bcap->sd, video,
- try_mbus_fmt, &mbus_fmt);
- if (ret < 0)
- return ret;
- v4l2_fill_pix_format(pixfmt, &mbus_fmt);
- pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
- return 0;
-}
-
-static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct bcap_format *sf = bcap_dev->sensor_formats;
-
- if (fmt->index >= bcap_dev->num_sensor_formats)
- return -EINVAL;
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strlcpy(fmt->description,
- sf[fmt->index].desc,
- sizeof(fmt->description));
- fmt->pixelformat = sf[fmt->index].pixelformat;
- return 0;
-}
-
-static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
-}
-
-static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- fmt->fmt.pix = bcap_dev->fmt;
- return 0;
-}
-
-static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- struct v4l2_mbus_framefmt mbus_fmt;
- enum v4l2_mbus_pixelcode mbus_code;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- int ret, bpp;
-
- if (vb2_is_busy(&bcap_dev->buffer_queue))
- return -EBUSY;
-
- /* see if format works */
- ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
- if (ret < 0)
- return ret;
-
- v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
- ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
- if (ret < 0)
- return ret;
- bcap_dev->fmt = *pixfmt;
- bcap_dev->bpp = bpp;
- return 0;
-}
-
-static int bcap_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
- strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
- strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
- return 0;
-}
-
-static int bcap_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
-}
-
-static int bcap_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
-}
-
-static int bcap_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int bcap_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- g_register, reg);
-}
-
-static int bcap_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
-
- return v4l2_subdev_call(bcap_dev->sd, core,
- s_register, reg);
-}
-#endif
-
-static int bcap_log_status(struct file *file, void *priv)
-{
- struct bcap_device *bcap_dev = video_drvdata(file);
- /* status for sub devices */
- v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
- return 0;
-}
-
-static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
- .vidioc_querycap = bcap_querycap,
- .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
- .vidioc_enum_input = bcap_enum_input,
- .vidioc_g_input = bcap_g_input,
- .vidioc_s_input = bcap_s_input,
- .vidioc_querystd = bcap_querystd,
- .vidioc_s_std = bcap_s_std,
- .vidioc_g_std = bcap_g_std,
- .vidioc_reqbufs = bcap_reqbufs,
- .vidioc_querybuf = bcap_querybuf,
- .vidioc_qbuf = bcap_qbuf,
- .vidioc_dqbuf = bcap_dqbuf,
- .vidioc_streamon = bcap_streamon,
- .vidioc_streamoff = bcap_streamoff,
- .vidioc_g_parm = bcap_g_parm,
- .vidioc_s_parm = bcap_s_parm,
- .vidioc_g_chip_ident = bcap_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = bcap_dbg_g_register,
- .vidioc_s_register = bcap_dbg_s_register,
-#endif
- .vidioc_log_status = bcap_log_status,
-};
-
-static struct v4l2_file_operations bcap_fops = {
- .owner = THIS_MODULE,
- .open = bcap_open,
- .release = bcap_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = bcap_mmap,
-#ifndef CONFIG_MMU
- .get_unmapped_area = bcap_get_unmapped_area,
-#endif
- .poll = bcap_poll
-};
-
-static int __devinit bcap_probe(struct platform_device *pdev)
-{
- struct bcap_device *bcap_dev;
- struct video_device *vfd;
- struct i2c_adapter *i2c_adap;
- struct bfin_capture_config *config;
- struct vb2_queue *q;
- int ret;
-
- config = pdev->dev.platform_data;
- if (!config) {
- v4l2_err(pdev->dev.driver, "Unable to get board config\n");
- return -ENODEV;
- }
-
- bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
- if (!bcap_dev) {
- v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
- return -ENOMEM;
- }
-
- bcap_dev->cfg = config;
-
- bcap_dev->ppi = ppi_create_instance(config->ppi_info);
- if (!bcap_dev->ppi) {
- v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
- ret = -ENODEV;
- goto err_free_dev;
- }
- bcap_dev->ppi->priv = bcap_dev;
-
- bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(bcap_dev->alloc_ctx)) {
- ret = PTR_ERR(bcap_dev->alloc_ctx);
- goto err_free_ppi;
- }
-
- vfd = video_device_alloc();
- if (!vfd) {
- ret = -ENOMEM;
- v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
- goto err_cleanup_ctx;
- }
-
- /* initialize field of video device */
- vfd->release = video_device_release;
- vfd->fops = &bcap_fops;
- vfd->ioctl_ops = &bcap_ioctl_ops;
- vfd->tvnorms = 0;
- vfd->v4l2_dev = &bcap_dev->v4l2_dev;
- set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
- bcap_dev->video_dev = vfd;
-
- ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
- if (ret) {
- v4l2_err(pdev->dev.driver,
- "Unable to register v4l2 device\n");
- goto err_release_vdev;
- }
- v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
-
- bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
- ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to init control handler\n");
- goto err_unreg_v4l2;
- }
-
- spin_lock_init(&bcap_dev->lock);
- /* initialize queue */
- q = &bcap_dev->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP;
- q->drv_priv = bcap_dev;
- q->buf_struct_size = sizeof(struct bcap_buffer);
- q->ops = &bcap_video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
-
- vb2_queue_init(q);
-
- mutex_init(&bcap_dev->mutex);
- init_completion(&bcap_dev->comp);
-
- /* init video dma queues */
- INIT_LIST_HEAD(&bcap_dev->dma_queue);
-
- vfd->lock = &bcap_dev->mutex;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
-
- /* register video device */
- ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to register video device\n");
- goto err_free_handler;
- }
- video_set_drvdata(bcap_dev->video_dev, bcap_dev);
- v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
- video_device_node_name(vfd));
-
- /* load up the subdevice */
- i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
- if (!i2c_adap) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to find i2c adapter\n");
- goto err_unreg_vdev;
-
- }
- bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
- i2c_adap,
- &config->board_info,
- NULL);
- if (bcap_dev->sd) {
- int i;
- /* update tvnorms from the sub devices */
- for (i = 0; i < config->num_inputs; i++)
- vfd->tvnorms |= config->inputs[i].std;
- } else {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to register sub device\n");
- goto err_unreg_vdev;
- }
-
- v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
-
- /* now we can probe the default state */
- if (vfd->tvnorms) {
- v4l2_std_id std;
- ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to get std\n");
- goto err_unreg_vdev;
- }
- bcap_dev->std = std;
- }
- ret = bcap_init_sensor_formats(bcap_dev);
- if (ret) {
- v4l2_err(&bcap_dev->v4l2_dev,
- "Unable to create sensor formats table\n");
- goto err_unreg_vdev;
- }
- return 0;
-err_unreg_vdev:
- video_unregister_device(bcap_dev->video_dev);
- bcap_dev->video_dev = NULL;
-err_free_handler:
- v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
-err_unreg_v4l2:
- v4l2_device_unregister(&bcap_dev->v4l2_dev);
-err_release_vdev:
- if (bcap_dev->video_dev)
- video_device_release(bcap_dev->video_dev);
-err_cleanup_ctx:
- vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
-err_free_ppi:
- ppi_delete_instance(bcap_dev->ppi);
-err_free_dev:
- kfree(bcap_dev);
- return ret;
-}
-
-static int __devexit bcap_remove(struct platform_device *pdev)
-{
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct bcap_device *bcap_dev = container_of(v4l2_dev,
- struct bcap_device, v4l2_dev);
-
- bcap_free_sensor_formats(bcap_dev);
- video_unregister_device(bcap_dev->video_dev);
- v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
- v4l2_device_unregister(v4l2_dev);
- vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
- ppi_delete_instance(bcap_dev->ppi);
- kfree(bcap_dev);
- return 0;
-}
-
-static struct platform_driver bcap_driver = {
- .driver = {
- .name = CAPTURE_DRV_NAME,
- .owner = THIS_MODULE,
- },
- .probe = bcap_probe,
- .remove = __devexit_p(bcap_remove),
-};
-
-static __init int bcap_init(void)
-{
- return platform_driver_register(&bcap_driver);
-}
-
-static __exit void bcap_exit(void)
-{
- platform_driver_unregister(&bcap_driver);
-}
-
-module_init(bcap_init);
-module_exit(bcap_exit);
-
-MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
-MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
deleted file mode 100644
index 7da5c2e1fc12..000000000000
--- a/drivers/media/video/bt8xx/Kconfig
+++ /dev/null
@@ -1,27 +0,0 @@
-config VIDEO_BT848
- tristate "BT848 Video For Linux"
- depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2
- select I2C_ALGOBIT
- select VIDEO_BTCX
- select VIDEOBUF_DMA_SG
- depends on RC_CORE
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- Support for BT848 based frame grabber/overlay boards. This includes
- the Miro, Hauppauge and STB boards. Please read the material in
- <file:Documentation/video4linux/bttv/> for more information.
-
- To compile this driver as a module, choose M here: the
- module will be called bttv.
-
-config VIDEO_BT848_DVB
- bool "DVB/ATSC Support for bt878 based TV cards"
- depends on VIDEO_BT848 && DVB_CORE
- select DVB_BT8XX
- ---help---
- This adds support for DVB/ATSC cards based on the BT878 chip.
diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile
deleted file mode 100644
index 3f9a2b22d3d4..000000000000
--- a/drivers/media/video/bt8xx/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Makefile for the video capture/playback device drivers.
-#
-
-bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
- bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \
- bttv-input.o bttv-audio-hook.o
-
-obj-$(CONFIG_VIDEO_BT848) += bttv.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
deleted file mode 100644
index b58ff87db771..000000000000
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ /dev/null
@@ -1,4630 +0,0 @@
-/*
-
- bttv - Bt848 frame grabber driver
-
- Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de>
- & Marcus Metzler <mocm@thp.uni-koeln.de>
- (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
-
- some v4l2 code lines are taken from Justin's bttv2 driver which is
- (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
-
- V4L1 removal from:
- (c) 2005-2006 Nickolay V. Shmyrev <nshmyrev@yandex.ru>
-
- Fixes to be fully V4L2 compliant by
- (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
-
- Cropping and overscan support
- Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at>
- Sponsored by OPQ Systems AB
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/kdev_t.h>
-#include "bttvp.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/tvaudio.h>
-#include <media/msp3400.h>
-
-#include <linux/dma-mapping.h>
-
-#include <asm/io.h>
-#include <asm/byteorder.h>
-
-#include <media/saa6588.h>
-
-#define BTTV_VERSION "0.9.19"
-
-unsigned int bttv_num; /* number of Bt848s in use */
-struct bttv *bttvs[BTTV_MAX];
-
-unsigned int bttv_debug;
-unsigned int bttv_verbose = 1;
-unsigned int bttv_gpio;
-
-/* config variables */
-#ifdef __BIG_ENDIAN
-static unsigned int bigendian=1;
-#else
-static unsigned int bigendian;
-#endif
-static unsigned int radio[BTTV_MAX];
-static unsigned int irq_debug;
-static unsigned int gbuffers = 8;
-static unsigned int gbufsize = 0x208000;
-static unsigned int reset_crop = 1;
-
-static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
-static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
-static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 };
-static int debug_latency;
-static int disable_ir;
-
-static unsigned int fdsr;
-
-/* options */
-static unsigned int combfilter;
-static unsigned int lumafilter;
-static unsigned int automute = 1;
-static unsigned int chroma_agc;
-static unsigned int adc_crush = 1;
-static unsigned int whitecrush_upper = 0xCF;
-static unsigned int whitecrush_lower = 0x7F;
-static unsigned int vcr_hack;
-static unsigned int irq_iswitch;
-static unsigned int uv_ratio = 50;
-static unsigned int full_luma_range;
-static unsigned int coring;
-
-/* API features (turn on/off stuff for testing) */
-static unsigned int v4l2 = 1;
-
-/* insmod args */
-module_param(bttv_verbose, int, 0644);
-module_param(bttv_gpio, int, 0644);
-module_param(bttv_debug, int, 0644);
-module_param(irq_debug, int, 0644);
-module_param(debug_latency, int, 0644);
-module_param(disable_ir, int, 0444);
-
-module_param(fdsr, int, 0444);
-module_param(gbuffers, int, 0444);
-module_param(gbufsize, int, 0444);
-module_param(reset_crop, int, 0444);
-
-module_param(v4l2, int, 0644);
-module_param(bigendian, int, 0644);
-module_param(irq_iswitch, int, 0644);
-module_param(combfilter, int, 0444);
-module_param(lumafilter, int, 0444);
-module_param(automute, int, 0444);
-module_param(chroma_agc, int, 0444);
-module_param(adc_crush, int, 0444);
-module_param(whitecrush_upper, int, 0444);
-module_param(whitecrush_lower, int, 0444);
-module_param(vcr_hack, int, 0444);
-module_param(uv_ratio, int, 0444);
-module_param(full_luma_range, int, 0444);
-module_param(coring, int, 0444);
-
-module_param_array(radio, int, NULL, 0444);
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-module_param_array(vbi_nr, int, NULL, 0444);
-
-MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
-MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
-MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
-MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
-MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
-MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
-MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
-MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
-MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
-MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default "
- "is 1 (yes) for compatibility with older applications");
-MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
-MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
-MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
-MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207");
-MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127");
-MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)");
-MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler");
-MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50");
-MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)");
-MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)");
-MODULE_PARM_DESC(video_nr, "video device numbers");
-MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
-
-MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
-MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(BTTV_VERSION);
-
-/* ----------------------------------------------------------------------- */
-/* sysfs */
-
-static ssize_t show_card(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vfd = container_of(cd, struct video_device, dev);
- struct bttv *btv = video_get_drvdata(vfd);
- return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
-}
-static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
-
-/* ----------------------------------------------------------------------- */
-/* dvb auto-load setup */
-#if defined(CONFIG_MODULES) && defined(MODULE)
-static void request_module_async(struct work_struct *work)
-{
- request_module("dvb-bt8xx");
-}
-
-static void request_modules(struct bttv *dev)
-{
- INIT_WORK(&dev->request_module_wk, request_module_async);
- schedule_work(&dev->request_module_wk);
-}
-
-static void flush_request_modules(struct bttv *dev)
-{
- flush_work_sync(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#define flush_request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-
-/* ----------------------------------------------------------------------- */
-/* static data */
-
-/* special timing tables from conexant... */
-static u8 SRAM_Table[][60] =
-{
- /* PAL digital input over GPIO[7:0] */
- {
- 45, // 45 bytes following
- 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16,
- 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00,
- 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00,
- 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37,
- 0x37,0x00,0xAF,0x21,0x00
- },
- /* NTSC digital input over GPIO[7:0] */
- {
- 51, // 51 bytes following
- 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06,
- 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00,
- 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07,
- 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6,
- 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21,
- 0x00,
- },
- // TGB_NTSC392 // quartzsight
- // This table has been modified to be used for Fusion Rev D
- {
- 0x2A, // size of table = 42
- 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24,
- 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10,
- 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00,
- 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3,
- 0x20, 0x00
- }
-};
-
-/* minhdelayx1 first video pixel we can capture on a line and
- hdelayx1 start of active video, both relative to rising edge of
- /HRESET pulse (0H) in 1 / fCLKx1.
- swidth width of active video and
- totalwidth total line width, both in 1 / fCLKx1.
- sqwidth total line width in square pixels.
- vdelay start of active video in 2 * field lines relative to
- trailing edge of /VRESET pulse (VDELAY register).
- sheight height of active video in 2 * field lines.
- videostart0 ITU-R frame line number of the line corresponding
- to vdelay in the first field. */
-#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \
- vdelay, sheight, videostart0) \
- .cropcap.bounds.left = minhdelayx1, \
- /* * 2 because vertically we count field lines times two, */ \
- /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \
- .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \
- /* 4 is a safety margin at the end of the line. */ \
- .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \
- .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \
- .cropcap.defrect.left = hdelayx1, \
- .cropcap.defrect.top = (videostart0) * 2, \
- .cropcap.defrect.width = swidth, \
- .cropcap.defrect.height = sheight, \
- .cropcap.pixelaspect.numerator = totalwidth, \
- .cropcap.pixelaspect.denominator = sqwidth,
-
-const struct bttv_tvnorm bttv_tvnorms[] = {
- /* PAL-BDGHI */
- /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
- /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
- {
- .v4l2_id = V4L2_STD_PAL,
- .name = "PAL",
- .Fsc = 35468950,
- .swidth = 924,
- .sheight = 576,
- .totalwidth = 1135,
- .adelay = 0x7f,
- .bdelay = 0x72,
- .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
- .scaledtwidth = 1135,
- .hdelayx1 = 186,
- .hactivex1 = 924,
- .vdelay = 0x20,
- .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */
- .sram = 0,
- /* ITU-R frame line number of the first VBI line
- we can capture, of the first and second field.
- The last line is determined by cropcap.bounds. */
- .vbistart = { 7, 320 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 186,
- /* Should be (768 * 1135 + 944 / 2) / 944.
- cropcap.defrect is used for image width
- checks, so we keep the old value 924. */
- /* swidth */ 924,
- /* totalwidth */ 1135,
- /* sqwidth */ 944,
- /* vdelay */ 0x20,
- /* sheight */ 576,
- /* videostart0 */ 23)
- /* bt878 (and bt848?) can capture another
- line below active video. */
- .cropcap.bounds.height = (576 + 2) + 0x20 - 2,
- },{
- .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR,
- .name = "NTSC",
- .Fsc = 28636363,
- .swidth = 768,
- .sheight = 480,
- .totalwidth = 910,
- .adelay = 0x68,
- .bdelay = 0x5d,
- .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0),
- .scaledtwidth = 910,
- .hdelayx1 = 128,
- .hactivex1 = 910,
- .vdelay = 0x1a,
- .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */
- .sram = 1,
- .vbistart = { 10, 273 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 128,
- /* Should be (640 * 910 + 780 / 2) / 780? */
- /* swidth */ 768,
- /* totalwidth */ 910,
- /* sqwidth */ 780,
- /* vdelay */ 0x1a,
- /* sheight */ 480,
- /* videostart0 */ 23)
- },{
- .v4l2_id = V4L2_STD_SECAM,
- .name = "SECAM",
- .Fsc = 35468950,
- .swidth = 924,
- .sheight = 576,
- .totalwidth = 1135,
- .adelay = 0x7f,
- .bdelay = 0xb0,
- .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1),
- .scaledtwidth = 1135,
- .hdelayx1 = 186,
- .hactivex1 = 922,
- .vdelay = 0x20,
- .vbipack = 255,
- .sram = 0, /* like PAL, correct? */
- .vbistart = { 7, 320 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 186,
- /* swidth */ 924,
- /* totalwidth */ 1135,
- /* sqwidth */ 944,
- /* vdelay */ 0x20,
- /* sheight */ 576,
- /* videostart0 */ 23)
- },{
- .v4l2_id = V4L2_STD_PAL_Nc,
- .name = "PAL-Nc",
- .Fsc = 28636363,
- .swidth = 640,
- .sheight = 576,
- .totalwidth = 910,
- .adelay = 0x68,
- .bdelay = 0x5d,
- .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
- .scaledtwidth = 780,
- .hdelayx1 = 130,
- .hactivex1 = 734,
- .vdelay = 0x1a,
- .vbipack = 144,
- .sram = -1,
- .vbistart = { 7, 320 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 130,
- /* swidth */ (640 * 910 + 780 / 2) / 780,
- /* totalwidth */ 910,
- /* sqwidth */ 780,
- /* vdelay */ 0x1a,
- /* sheight */ 576,
- /* videostart0 */ 23)
- },{
- .v4l2_id = V4L2_STD_PAL_M,
- .name = "PAL-M",
- .Fsc = 28636363,
- .swidth = 640,
- .sheight = 480,
- .totalwidth = 910,
- .adelay = 0x68,
- .bdelay = 0x5d,
- .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
- .scaledtwidth = 780,
- .hdelayx1 = 135,
- .hactivex1 = 754,
- .vdelay = 0x1a,
- .vbipack = 144,
- .sram = -1,
- .vbistart = { 10, 273 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 135,
- /* swidth */ (640 * 910 + 780 / 2) / 780,
- /* totalwidth */ 910,
- /* sqwidth */ 780,
- /* vdelay */ 0x1a,
- /* sheight */ 480,
- /* videostart0 */ 23)
- },{
- .v4l2_id = V4L2_STD_PAL_N,
- .name = "PAL-N",
- .Fsc = 35468950,
- .swidth = 768,
- .sheight = 576,
- .totalwidth = 1135,
- .adelay = 0x7f,
- .bdelay = 0x72,
- .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
- .scaledtwidth = 944,
- .hdelayx1 = 186,
- .hactivex1 = 922,
- .vdelay = 0x20,
- .vbipack = 144,
- .sram = -1,
- .vbistart = { 7, 320 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 186,
- /* swidth */ (768 * 1135 + 944 / 2) / 944,
- /* totalwidth */ 1135,
- /* sqwidth */ 944,
- /* vdelay */ 0x20,
- /* sheight */ 576,
- /* videostart0 */ 23)
- },{
- .v4l2_id = V4L2_STD_NTSC_M_JP,
- .name = "NTSC-JP",
- .Fsc = 28636363,
- .swidth = 640,
- .sheight = 480,
- .totalwidth = 910,
- .adelay = 0x68,
- .bdelay = 0x5d,
- .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
- .scaledtwidth = 780,
- .hdelayx1 = 135,
- .hactivex1 = 754,
- .vdelay = 0x16,
- .vbipack = 144,
- .sram = -1,
- .vbistart = { 10, 273 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 135,
- /* swidth */ (640 * 910 + 780 / 2) / 780,
- /* totalwidth */ 910,
- /* sqwidth */ 780,
- /* vdelay */ 0x16,
- /* sheight */ 480,
- /* videostart0 */ 23)
- },{
- /* that one hopefully works with the strange timing
- * which video recorders produce when playing a NTSC
- * tape on a PAL TV ... */
- .v4l2_id = V4L2_STD_PAL_60,
- .name = "PAL-60",
- .Fsc = 35468950,
- .swidth = 924,
- .sheight = 480,
- .totalwidth = 1135,
- .adelay = 0x7f,
- .bdelay = 0x72,
- .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
- .scaledtwidth = 1135,
- .hdelayx1 = 186,
- .hactivex1 = 924,
- .vdelay = 0x1a,
- .vbipack = 255,
- .vtotal = 524,
- .sram = -1,
- .vbistart = { 10, 273 },
- CROPCAP(/* minhdelayx1 */ 68,
- /* hdelayx1 */ 186,
- /* swidth */ 924,
- /* totalwidth */ 1135,
- /* sqwidth */ 944,
- /* vdelay */ 0x1a,
- /* sheight */ 480,
- /* videostart0 */ 23)
- }
-};
-static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms);
-
-/* ----------------------------------------------------------------------- */
-/* bttv format list
- packed pixel formats must come first */
-static const struct bttv_format formats[] = {
- {
- .name = "8 bpp, gray",
- .fourcc = V4L2_PIX_FMT_GREY,
- .btformat = BT848_COLOR_FMT_Y8,
- .depth = 8,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "8 bpp, dithered color",
- .fourcc = V4L2_PIX_FMT_HI240,
- .btformat = BT848_COLOR_FMT_RGB8,
- .depth = 8,
- .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
- },{
- .name = "15 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .btformat = BT848_COLOR_FMT_RGB15,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "15 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .btformat = BT848_COLOR_FMT_RGB15,
- .btswap = 0x03, /* byteswap */
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "16 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .btformat = BT848_COLOR_FMT_RGB16,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "16 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .btformat = BT848_COLOR_FMT_RGB16,
- .btswap = 0x03, /* byteswap */
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "24 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .btformat = BT848_COLOR_FMT_RGB24,
- .depth = 24,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "32 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .btformat = BT848_COLOR_FMT_RGB32,
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "32 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .btformat = BT848_COLOR_FMT_RGB32,
- .btswap = 0x0f, /* byte+word swap */
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .btformat = BT848_COLOR_FMT_YUY2,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .btformat = BT848_COLOR_FMT_YUY2,
- .btswap = 0x03, /* byteswap */
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "4:2:2, planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .btformat = BT848_COLOR_FMT_YCrCb422,
- .depth = 16,
- .flags = FORMAT_FLAGS_PLANAR,
- .hshift = 1,
- .vshift = 0,
- },{
- .name = "4:2:0, planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .btformat = BT848_COLOR_FMT_YCrCb422,
- .depth = 12,
- .flags = FORMAT_FLAGS_PLANAR,
- .hshift = 1,
- .vshift = 1,
- },{
- .name = "4:2:0, planar, Y-Cr-Cb",
- .fourcc = V4L2_PIX_FMT_YVU420,
- .btformat = BT848_COLOR_FMT_YCrCb422,
- .depth = 12,
- .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
- .hshift = 1,
- .vshift = 1,
- },{
- .name = "4:1:1, planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV411P,
- .btformat = BT848_COLOR_FMT_YCrCb411,
- .depth = 12,
- .flags = FORMAT_FLAGS_PLANAR,
- .hshift = 2,
- .vshift = 0,
- },{
- .name = "4:1:0, planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV410,
- .btformat = BT848_COLOR_FMT_YCrCb411,
- .depth = 9,
- .flags = FORMAT_FLAGS_PLANAR,
- .hshift = 2,
- .vshift = 2,
- },{
- .name = "4:1:0, planar, Y-Cr-Cb",
- .fourcc = V4L2_PIX_FMT_YVU410,
- .btformat = BT848_COLOR_FMT_YCrCb411,
- .depth = 9,
- .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
- .hshift = 2,
- .vshift = 2,
- },{
- .name = "raw scanlines",
- .fourcc = -1,
- .btformat = BT848_COLOR_FMT_RAW,
- .depth = 8,
- .flags = FORMAT_FLAGS_RAW,
- }
-};
-static const unsigned int FORMATS = ARRAY_SIZE(formats);
-
-/* ----------------------------------------------------------------------- */
-
-#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0)
-#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1)
-#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2)
-#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3)
-#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4)
-#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5)
-#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6)
-#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7)
-#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8)
-#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9)
-#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10)
-#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11)
-
-static const struct v4l2_queryctrl no_ctl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-static const struct v4l2_queryctrl bttv_ctls[] = {
- /* --- video --- */
- {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 65535,
- .step = 256,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 65535,
- .step = 128,
- .default_value = 27648,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 65535,
- .step = 128,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = 0,
- .maximum = 65535,
- .step = 256,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- /* --- audio --- */
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 65535,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_BALANCE,
- .name = "Balance",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_BASS,
- .name = "Bass",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_AUDIO_TREBLE,
- .name = "Treble",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535/100,
- .default_value = 32768,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- /* --- private --- */
- {
- .id = V4L2_CID_PRIVATE_CHROMA_AGC,
- .name = "chroma agc",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_COMBFILTER,
- .name = "combfilter",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_AUTOMUTE,
- .name = "automute",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_LUMAFILTER,
- .name = "luma decimation filter",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_AGC_CRUSH,
- .name = "agc crush",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_VCR_HACK,
- .name = "vcr hack",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER,
- .name = "whitecrush upper",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 0xCF,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER,
- .name = "whitecrush lower",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 0x7F,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_UV_RATIO,
- .name = "uv ratio",
- .minimum = 0,
- .maximum = 100,
- .step = 1,
- .default_value = 50,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE,
- .name = "full luma range",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_CORING,
- .name = "coring",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-
-
-
-};
-
-static const struct v4l2_queryctrl *ctrl_by_id(int id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++)
- if (bttv_ctls[i].id == id)
- return bttv_ctls+i;
-
- return NULL;
-}
-
-/* ----------------------------------------------------------------------- */
-/* resource management */
-
-/*
- RESOURCE_ allocated by freed by
-
- VIDEO_READ bttv_read 1) bttv_read 2)
-
- VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF
- VIDIOC_QBUF 1) bttv_release
- VIDIOCMCAPTURE 1)
-
- OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off
- VIDIOC_OVERLAY on VIDIOC_OVERLAY off
- 3) bttv_release
-
- VBI VIDIOC_STREAMON VIDIOC_STREAMOFF
- VIDIOC_QBUF 1) bttv_release
- bttv_read, bttv_poll 1) 4)
-
- 1) The resource must be allocated when we enter buffer prepare functions
- and remain allocated while buffers are in the DMA queue.
- 2) This is a single frame read.
- 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when
- RESOURCE_OVERLAY is allocated.
- 4) This is a continuous read, implies VIDIOC_STREAMON.
-
- Note this driver permits video input and standard changes regardless if
- resources are allocated.
-*/
-
-#define VBI_RESOURCES (RESOURCE_VBI)
-#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \
- RESOURCE_VIDEO_STREAM | \
- RESOURCE_OVERLAY)
-
-static
-int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit)
-{
- int xbits; /* mutual exclusive resources */
-
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- xbits = bit;
- if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM))
- xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM;
-
- /* is it free? */
- if (btv->resources & xbits) {
- /* no, someone else uses it */
- goto fail;
- }
-
- if ((bit & VIDEO_RESOURCES)
- && 0 == (btv->resources & VIDEO_RESOURCES)) {
- /* Do crop - use current, don't - use default parameters. */
- __s32 top = btv->crop[!!fh->do_crop].rect.top;
-
- if (btv->vbi_end > top)
- goto fail;
-
- /* We cannot capture the same line as video and VBI data.
- Claim scan lines crop[].rect.top to bottom. */
- btv->crop_start = top;
- } else if (bit & VBI_RESOURCES) {
- __s32 end = fh->vbi_fmt.end;
-
- if (end > btv->crop_start)
- goto fail;
-
- /* Claim scan lines above fh->vbi_fmt.end. */
- btv->vbi_end = end;
- }
-
- /* it's free, grab it */
- fh->resources |= bit;
- btv->resources |= bit;
- return 1;
-
- fail:
- return 0;
-}
-
-static
-int check_btres(struct bttv_fh *fh, int bit)
-{
- return (fh->resources & bit);
-}
-
-static
-int locked_btres(struct bttv *btv, int bit)
-{
- return (btv->resources & bit);
-}
-
-/* Call with btv->lock down. */
-static void
-disclaim_vbi_lines(struct bttv *btv)
-{
- btv->vbi_end = 0;
-}
-
-/* Call with btv->lock down. */
-static void
-disclaim_video_lines(struct bttv *btv)
-{
- const struct bttv_tvnorm *tvnorm;
- u8 crop;
-
- tvnorm = &bttv_tvnorms[btv->tvnorm];
- btv->crop_start = tvnorm->cropcap.bounds.top
- + tvnorm->cropcap.bounds.height;
-
- /* VBI capturing ends at VDELAY, start of video capturing, no
- matter how many lines the VBI RISC program expects. When video
- capturing is off, it shall no longer "preempt" VBI capturing,
- so we set VDELAY to maximum. */
- crop = btread(BT848_E_CROP) | 0xc0;
- btwrite(crop, BT848_E_CROP);
- btwrite(0xfe, BT848_E_VDELAY_LO);
- btwrite(crop, BT848_O_CROP);
- btwrite(0xfe, BT848_O_VDELAY_LO);
-}
-
-static
-void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits)
-{
- if ((fh->resources & bits) != bits) {
- /* trying to free resources not allocated by us ... */
- pr_err("BUG! (btres)\n");
- }
- fh->resources &= ~bits;
- btv->resources &= ~bits;
-
- bits = btv->resources;
-
- if (0 == (bits & VIDEO_RESOURCES))
- disclaim_video_lines(btv);
-
- if (0 == (bits & VBI_RESOURCES))
- disclaim_vbi_lines(btv);
-}
-
-/* ----------------------------------------------------------------------- */
-/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */
-
-/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
- PLL_X = Reference pre-divider (0=1, 1=2)
- PLL_C = Post divider (0=6, 1=4)
- PLL_I = Integer input
- PLL_F = Fractional input
-
- F_input = 28.636363 MHz:
- PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
-*/
-
-static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
-{
- unsigned char fl, fh, fi;
-
- /* prevent overflows */
- fin/=4;
- fout/=4;
-
- fout*=12;
- fi=fout/fin;
-
- fout=(fout%fin)*256;
- fh=fout/fin;
-
- fout=(fout%fin)*256;
- fl=fout/fin;
-
- btwrite(fl, BT848_PLL_F_LO);
- btwrite(fh, BT848_PLL_F_HI);
- btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
-}
-
-static void set_pll(struct bttv *btv)
-{
- int i;
-
- if (!btv->pll.pll_crystal)
- return;
-
- if (btv->pll.pll_ofreq == btv->pll.pll_current) {
- dprintk("%d: PLL: no change required\n", btv->c.nr);
- return;
- }
-
- if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
- /* no PLL needed */
- if (btv->pll.pll_current == 0)
- return;
- if (bttv_verbose)
- pr_info("%d: PLL can sleep, using XTAL (%d)\n",
- btv->c.nr, btv->pll.pll_ifreq);
- btwrite(0x00,BT848_TGCTRL);
- btwrite(0x00,BT848_PLL_XCI);
- btv->pll.pll_current = 0;
- return;
- }
-
- if (bttv_verbose)
- pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n",
- btv->c.nr,
- btv->pll.pll_ifreq, btv->pll.pll_ofreq);
- set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
-
- for (i=0; i<10; i++) {
- /* Let other people run while the PLL stabilizes */
- msleep(10);
-
- if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
- btwrite(0,BT848_DSTATUS);
- } else {
- btwrite(0x08,BT848_TGCTRL);
- btv->pll.pll_current = btv->pll.pll_ofreq;
- if (bttv_verbose)
- pr_info("PLL set ok\n");
- return;
- }
- }
- btv->pll.pll_current = -1;
- if (bttv_verbose)
- pr_info("Setting PLL failed\n");
- return;
-}
-
-/* used to switch between the bt848's analog/digital video capture modes */
-static void bt848A_set_timing(struct bttv *btv)
-{
- int i, len;
- int table_idx = bttv_tvnorms[btv->tvnorm].sram;
- int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
-
- if (btv->input == btv->dig) {
- dprintk("%d: load digital timing table (table_idx=%d)\n",
- btv->c.nr,table_idx);
-
- /* timing change...reset timing generator address */
- btwrite(0x00, BT848_TGCTRL);
- btwrite(0x02, BT848_TGCTRL);
- btwrite(0x00, BT848_TGCTRL);
-
- len=SRAM_Table[table_idx][0];
- for(i = 1; i <= len; i++)
- btwrite(SRAM_Table[table_idx][i],BT848_TGLB);
- btv->pll.pll_ofreq = 27000000;
-
- set_pll(btv);
- btwrite(0x11, BT848_TGCTRL);
- btwrite(0x41, BT848_DVSIF);
- } else {
- btv->pll.pll_ofreq = fsc;
- set_pll(btv);
- btwrite(0x0, BT848_DVSIF);
- }
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void bt848_bright(struct bttv *btv, int bright)
-{
- int value;
-
- // printk("set bright: %d\n", bright); // DEBUG
- btv->bright = bright;
-
- /* We want -128 to 127 we get 0-65535 */
- value = (bright >> 8) - 128;
- btwrite(value & 0xff, BT848_BRIGHT);
-}
-
-static void bt848_hue(struct bttv *btv, int hue)
-{
- int value;
-
- btv->hue = hue;
-
- /* -128 to 127 */
- value = (hue >> 8) - 128;
- btwrite(value & 0xff, BT848_HUE);
-}
-
-static void bt848_contrast(struct bttv *btv, int cont)
-{
- int value,hibit;
-
- btv->contrast = cont;
-
- /* 0-511 */
- value = (cont >> 7);
- hibit = (value >> 6) & 4;
- btwrite(value & 0xff, BT848_CONTRAST_LO);
- btaor(hibit, ~4, BT848_E_CONTROL);
- btaor(hibit, ~4, BT848_O_CONTROL);
-}
-
-static void bt848_sat(struct bttv *btv, int color)
-{
- int val_u,val_v,hibits;
-
- btv->saturation = color;
-
- /* 0-511 for the color */
- val_u = ((color * btv->opt_uv_ratio) / 50) >> 7;
- val_v = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254;
- hibits = (val_u >> 7) & 2;
- hibits |= (val_v >> 8) & 1;
- btwrite(val_u & 0xff, BT848_SAT_U_LO);
- btwrite(val_v & 0xff, BT848_SAT_V_LO);
- btaor(hibits, ~3, BT848_E_CONTROL);
- btaor(hibits, ~3, BT848_O_CONTROL);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int
-video_mux(struct bttv *btv, unsigned int input)
-{
- int mux,mask2;
-
- if (input >= bttv_tvcards[btv->c.type].video_inputs)
- return -EINVAL;
-
- /* needed by RemoteVideo MX */
- mask2 = bttv_tvcards[btv->c.type].gpiomask2;
- if (mask2)
- gpio_inout(mask2,mask2);
-
- if (input == btv->svhs) {
- btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
- btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
- } else {
- btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
- btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
- }
- mux = bttv_muxsel(btv, input);
- btaor(mux<<5, ~(3<<5), BT848_IFORM);
- dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux);
-
- /* card specific hook */
- if(bttv_tvcards[btv->c.type].muxsel_hook)
- bttv_tvcards[btv->c.type].muxsel_hook (btv, input);
- return 0;
-}
-
-static char *audio_modes[] = {
- "audio: tuner", "audio: radio", "audio: extern",
- "audio: intern", "audio: mute"
-};
-
-static int
-audio_mux(struct bttv *btv, int input, int mute)
-{
- int gpio_val, signal;
- struct v4l2_control ctrl;
-
- gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
- bttv_tvcards[btv->c.type].gpiomask);
- signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
-
- btv->mute = mute;
- btv->audio = input;
-
- /* automute */
- mute = mute || (btv->opt_automute && !signal && !btv->radio_user);
-
- if (mute)
- gpio_val = bttv_tvcards[btv->c.type].gpiomute;
- else
- gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
-
- switch (btv->c.type) {
- case BTTV_BOARD_VOODOOTV_FM:
- case BTTV_BOARD_VOODOOTV_200:
- gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
- break;
-
- default:
- gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
- }
-
- if (bttv_gpio)
- bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
- if (in_interrupt())
- return 0;
-
- ctrl.id = V4L2_CID_AUDIO_MUTE;
- ctrl.value = btv->mute;
- bttv_call_all(btv, core, s_ctrl, &ctrl);
- if (btv->sd_msp34xx) {
- u32 in;
-
- /* Note: the inputs tuner/radio/extern/intern are translated
- to msp routings. This assumes common behavior for all msp3400
- based TV cards. When this assumption fails, then the
- specific MSP routing must be added to the card table.
- For now this is sufficient. */
- switch (input) {
- case TVAUDIO_INPUT_RADIO:
- /* Some boards need the msp do to the radio demod */
- if (btv->radio_uses_msp_demodulator) {
- in = MSP_INPUT_DEFAULT;
- break;
- }
- in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
- case TVAUDIO_INPUT_EXTERN:
- in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
- case TVAUDIO_INPUT_INTERN:
- /* Yes, this is the same input as for RADIO. I doubt
- if this is ever used. The only board with an INTERN
- input is the BTTV_BOARD_AVERMEDIA98. I wonder how
- that was tested. My guess is that the whole INTERN
- input does not work. */
- in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
- break;
- case TVAUDIO_INPUT_TUNER:
- default:
- /* This is the only card that uses TUNER2, and afaik,
- is the only difference between the VOODOOTV_FM
- and VOODOOTV_200 */
- if (btv->c.type == BTTV_BOARD_VOODOOTV_200)
- in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \
- MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER);
- else
- in = MSP_INPUT_DEFAULT;
- break;
- }
- v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing,
- in, MSP_OUTPUT_DEFAULT, 0);
- }
- if (btv->sd_tvaudio) {
- v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing,
- input, 0, 0);
- }
- return 0;
-}
-
-static inline int
-audio_mute(struct bttv *btv, int mute)
-{
- return audio_mux(btv, btv->audio, mute);
-}
-
-static inline int
-audio_input(struct bttv *btv, int input)
-{
- return audio_mux(btv, input, btv->mute);
-}
-
-static void
-bttv_crop_calc_limits(struct bttv_crop *c)
-{
- /* Scale factor min. 1:1, max. 16:1. Min. image size
- 48 x 32. Scaled width must be a multiple of 4. */
-
- if (1) {
- /* For bug compatibility with VIDIOCGCAP and image
- size checks in earlier driver versions. */
- c->min_scaled_width = 48;
- c->min_scaled_height = 32;
- } else {
- c->min_scaled_width =
- (max(48, c->rect.width >> 4) + 3) & ~3;
- c->min_scaled_height =
- max(32, c->rect.height >> 4);
- }
-
- c->max_scaled_width = c->rect.width & ~3;
- c->max_scaled_height = c->rect.height;
-}
-
-static void
-bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
-{
- c->rect = bttv_tvnorms[norm].cropcap.defrect;
- bttv_crop_calc_limits(c);
-}
-
-/* Call with btv->lock down. */
-static int
-set_tvnorm(struct bttv *btv, unsigned int norm)
-{
- const struct bttv_tvnorm *tvnorm;
- v4l2_std_id id;
-
- BUG_ON(norm >= BTTV_TVNORMS);
- BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
-
- tvnorm = &bttv_tvnorms[norm];
-
- if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
- sizeof (tvnorm->cropcap))) {
- bttv_crop_reset(&btv->crop[0], norm);
- btv->crop[1] = btv->crop[0]; /* current = default */
-
- if (0 == (btv->resources & VIDEO_RESOURCES)) {
- btv->crop_start = tvnorm->cropcap.bounds.top
- + tvnorm->cropcap.bounds.height;
- }
- }
-
- btv->tvnorm = norm;
-
- btwrite(tvnorm->adelay, BT848_ADELAY);
- btwrite(tvnorm->bdelay, BT848_BDELAY);
- btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
- BT848_IFORM);
- btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
- btwrite(1, BT848_VBI_PACK_DEL);
- bt848A_set_timing(btv);
-
- switch (btv->c.type) {
- case BTTV_BOARD_VOODOOTV_FM:
- case BTTV_BOARD_VOODOOTV_200:
- bttv_tda9880_setnorm(btv, gpio_read());
- break;
- }
- id = tvnorm->v4l2_id;
- bttv_call_all(btv, core, s_std, id);
-
- return 0;
-}
-
-/* Call with btv->lock down. */
-static void
-set_input(struct bttv *btv, unsigned int input, unsigned int norm)
-{
- unsigned long flags;
-
- btv->input = input;
- if (irq_iswitch) {
- spin_lock_irqsave(&btv->s_lock,flags);
- if (btv->curr.frame_irq) {
- /* active capture -> delayed input switch */
- btv->new_input = input;
- } else {
- video_mux(btv,input);
- }
- spin_unlock_irqrestore(&btv->s_lock,flags);
- } else {
- video_mux(btv,input);
- }
- audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
- TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
- set_tvnorm(btv, norm);
-}
-
-static void init_irqreg(struct bttv *btv)
-{
- /* clear status */
- btwrite(0xfffffUL, BT848_INT_STAT);
-
- if (bttv_tvcards[btv->c.type].no_video) {
- /* i2c only */
- btwrite(BT848_INT_I2CDONE,
- BT848_INT_MASK);
- } else {
- /* full video */
- btwrite((btv->triton1) |
- (btv->gpioirq ? BT848_INT_GPINT : 0) |
- BT848_INT_SCERR |
- (fdsr ? BT848_INT_FDSR : 0) |
- BT848_INT_RISCI | BT848_INT_OCERR |
- BT848_INT_FMTCHG|BT848_INT_HLOCK|
- BT848_INT_I2CDONE,
- BT848_INT_MASK);
- }
-}
-
-static void init_bt848(struct bttv *btv)
-{
- int val;
-
- if (bttv_tvcards[btv->c.type].no_video) {
- /* very basic init only */
- init_irqreg(btv);
- return;
- }
-
- btwrite(0x00, BT848_CAP_CTL);
- btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
- btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
-
- /* set planar and packed mode trigger points and */
- /* set rising edge of inverted GPINTR pin as irq trigger */
- btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
- BT848_GPIO_DMA_CTL_PLTP1_16|
- BT848_GPIO_DMA_CTL_PLTP23_16|
- BT848_GPIO_DMA_CTL_GPINTC|
- BT848_GPIO_DMA_CTL_GPINTI,
- BT848_GPIO_DMA_CTL);
-
- val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
- btwrite(val, BT848_E_SCLOOP);
- btwrite(val, BT848_O_SCLOOP);
-
- btwrite(0x20, BT848_E_VSCALE_HI);
- btwrite(0x20, BT848_O_VSCALE_HI);
- btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
- BT848_ADC);
-
- btwrite(whitecrush_upper, BT848_WC_UP);
- btwrite(whitecrush_lower, BT848_WC_DOWN);
-
- if (btv->opt_lumafilter) {
- btwrite(0, BT848_E_CONTROL);
- btwrite(0, BT848_O_CONTROL);
- } else {
- btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
- btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
- }
-
- bt848_bright(btv, btv->bright);
- bt848_hue(btv, btv->hue);
- bt848_contrast(btv, btv->contrast);
- bt848_sat(btv, btv->saturation);
-
- /* interrupt */
- init_irqreg(btv);
-}
-
-static void bttv_reinit_bt848(struct bttv *btv)
-{
- unsigned long flags;
-
- if (bttv_verbose)
- pr_info("%d: reset, reinitialize\n", btv->c.nr);
- spin_lock_irqsave(&btv->s_lock,flags);
- btv->errors=0;
- bttv_set_dma(btv,0);
- spin_unlock_irqrestore(&btv->s_lock,flags);
-
- init_bt848(btv);
- btv->pll.pll_current = -1;
- set_input(btv, btv->input, btv->tvnorm);
-}
-
-static int bttv_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *c)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value = btv->bright;
- break;
- case V4L2_CID_HUE:
- c->value = btv->hue;
- break;
- case V4L2_CID_CONTRAST:
- c->value = btv->contrast;
- break;
- case V4L2_CID_SATURATION:
- c->value = btv->saturation;
- break;
-
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- bttv_call_all(btv, core, g_ctrl, c);
- break;
-
- case V4L2_CID_PRIVATE_CHROMA_AGC:
- c->value = btv->opt_chroma_agc;
- break;
- case V4L2_CID_PRIVATE_COMBFILTER:
- c->value = btv->opt_combfilter;
- break;
- case V4L2_CID_PRIVATE_LUMAFILTER:
- c->value = btv->opt_lumafilter;
- break;
- case V4L2_CID_PRIVATE_AUTOMUTE:
- c->value = btv->opt_automute;
- break;
- case V4L2_CID_PRIVATE_AGC_CRUSH:
- c->value = btv->opt_adc_crush;
- break;
- case V4L2_CID_PRIVATE_VCR_HACK:
- c->value = btv->opt_vcr_hack;
- break;
- case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
- c->value = btv->opt_whitecrush_upper;
- break;
- case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
- c->value = btv->opt_whitecrush_lower;
- break;
- case V4L2_CID_PRIVATE_UV_RATIO:
- c->value = btv->opt_uv_ratio;
- break;
- case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
- c->value = btv->opt_full_luma_range;
- break;
- case V4L2_CID_PRIVATE_CORING:
- c->value = btv->opt_coring;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int bttv_s_ctrl(struct file *file, void *f,
- struct v4l2_control *c)
-{
- int err;
- int val;
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- err = v4l2_prio_check(&btv->prio, fh->prio);
- if (0 != err)
- return err;
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- bt848_bright(btv, c->value);
- break;
- case V4L2_CID_HUE:
- bt848_hue(btv, c->value);
- break;
- case V4L2_CID_CONTRAST:
- bt848_contrast(btv, c->value);
- break;
- case V4L2_CID_SATURATION:
- bt848_sat(btv, c->value);
- break;
- case V4L2_CID_AUDIO_MUTE:
- audio_mute(btv, c->value);
- /* fall through */
- case V4L2_CID_AUDIO_VOLUME:
- if (btv->volume_gpio)
- btv->volume_gpio(btv, c->value);
-
- bttv_call_all(btv, core, s_ctrl, c);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- bttv_call_all(btv, core, s_ctrl, c);
- break;
-
- case V4L2_CID_PRIVATE_CHROMA_AGC:
- btv->opt_chroma_agc = c->value;
- val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
- btwrite(val, BT848_E_SCLOOP);
- btwrite(val, BT848_O_SCLOOP);
- break;
- case V4L2_CID_PRIVATE_COMBFILTER:
- btv->opt_combfilter = c->value;
- break;
- case V4L2_CID_PRIVATE_LUMAFILTER:
- btv->opt_lumafilter = c->value;
- if (btv->opt_lumafilter) {
- btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
- btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
- } else {
- btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
- btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
- }
- break;
- case V4L2_CID_PRIVATE_AUTOMUTE:
- btv->opt_automute = c->value;
- break;
- case V4L2_CID_PRIVATE_AGC_CRUSH:
- btv->opt_adc_crush = c->value;
- btwrite(BT848_ADC_RESERVED |
- (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
- BT848_ADC);
- break;
- case V4L2_CID_PRIVATE_VCR_HACK:
- btv->opt_vcr_hack = c->value;
- break;
- case V4L2_CID_PRIVATE_WHITECRUSH_UPPER:
- btv->opt_whitecrush_upper = c->value;
- btwrite(c->value, BT848_WC_UP);
- break;
- case V4L2_CID_PRIVATE_WHITECRUSH_LOWER:
- btv->opt_whitecrush_lower = c->value;
- btwrite(c->value, BT848_WC_DOWN);
- break;
- case V4L2_CID_PRIVATE_UV_RATIO:
- btv->opt_uv_ratio = c->value;
- bt848_sat(btv, btv->saturation);
- break;
- case V4L2_CID_PRIVATE_FULL_LUMA_RANGE:
- btv->opt_full_luma_range = c->value;
- btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM);
- break;
- case V4L2_CID_PRIVATE_CORING:
- btv->opt_coring = c->value;
- btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-void bttv_gpio_tracking(struct bttv *btv, char *comment)
-{
- unsigned int outbits, data;
- outbits = btread(BT848_GPIO_OUT_EN);
- data = btread(BT848_GPIO_DATA);
- pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
- btv->c.nr, outbits, data & outbits, data & ~outbits, comment);
-}
-
-static void bttv_field_count(struct bttv *btv)
-{
- int need_count = 0;
-
- if (btv->users)
- need_count++;
-
- if (need_count) {
- /* start field counter */
- btor(BT848_INT_VSYNC,BT848_INT_MASK);
- } else {
- /* stop field counter */
- btand(~BT848_INT_VSYNC,BT848_INT_MASK);
- btv->field_count = 0;
- }
-}
-
-static const struct bttv_format*
-format_by_fourcc(int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < FORMATS; i++) {
- if (-1 == formats[i].fourcc)
- continue;
- if (formats[i].fourcc == fourcc)
- return formats+i;
- }
- return NULL;
-}
-
-/* ----------------------------------------------------------------------- */
-/* misc helpers */
-
-static int
-bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
- struct bttv_buffer *new)
-{
- struct bttv_buffer *old;
- unsigned long flags;
- int retval = 0;
-
- dprintk("switch_overlay: enter [new=%p]\n", new);
- if (new)
- new->vb.state = VIDEOBUF_DONE;
- spin_lock_irqsave(&btv->s_lock,flags);
- old = btv->screen;
- btv->screen = new;
- btv->loop_irq |= 1;
- bttv_set_dma(btv, 0x03);
- spin_unlock_irqrestore(&btv->s_lock,flags);
- if (NULL != old) {
- dprintk("switch_overlay: old=%p state is %d\n",
- old, old->vb.state);
- bttv_dma_free(&fh->cap,btv, old);
- kfree(old);
- }
- if (NULL == new)
- free_btres_lock(btv,fh,RESOURCE_OVERLAY);
- dprintk("switch_overlay: done\n");
- return retval;
-}
-
-/* ----------------------------------------------------------------------- */
-/* video4linux (1) interface */
-
-static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv,
- struct bttv_buffer *buf,
- const struct bttv_format *fmt,
- unsigned int width, unsigned int height,
- enum v4l2_field field)
-{
- struct bttv_fh *fh = q->priv_data;
- int redo_dma_risc = 0;
- struct bttv_crop c;
- int norm;
- int rc;
-
- /* check settings */
- if (NULL == fmt)
- return -EINVAL;
- if (fmt->btformat == BT848_COLOR_FMT_RAW) {
- width = RAW_BPL;
- height = RAW_LINES*2;
- if (width*height > buf->vb.bsize)
- return -EINVAL;
- buf->vb.size = buf->vb.bsize;
-
- /* Make sure tvnorm and vbi_end remain consistent
- until we're done. */
-
- norm = btv->tvnorm;
-
- /* In this mode capturing always starts at defrect.top
- (default VDELAY), ignoring cropping parameters. */
- if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) {
- return -EINVAL;
- }
-
- c.rect = bttv_tvnorms[norm].cropcap.defrect;
- } else {
- norm = btv->tvnorm;
- c = btv->crop[!!fh->do_crop];
-
- if (width < c.min_scaled_width ||
- width > c.max_scaled_width ||
- height < c.min_scaled_height)
- return -EINVAL;
-
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- /* btv->crop counts frame lines. Max. scale
- factor is 16:1 for frames, 8:1 for fields. */
- if (height * 2 > c.max_scaled_height)
- return -EINVAL;
- break;
-
- default:
- if (height > c.max_scaled_height)
- return -EINVAL;
- break;
- }
-
- buf->vb.size = (width * height * fmt->depth) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
- }
-
- /* alloc + fill struct bttv_buffer (if changed) */
- if (buf->vb.width != width || buf->vb.height != height ||
- buf->vb.field != field ||
- buf->tvnorm != norm || buf->fmt != fmt ||
- buf->crop.top != c.rect.top ||
- buf->crop.left != c.rect.left ||
- buf->crop.width != c.rect.width ||
- buf->crop.height != c.rect.height) {
- buf->vb.width = width;
- buf->vb.height = height;
- buf->vb.field = field;
- buf->tvnorm = norm;
- buf->fmt = fmt;
- buf->crop = c.rect;
- redo_dma_risc = 1;
- }
-
- /* alloc risc memory */
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- redo_dma_risc = 1;
- if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf)))
- goto fail;
- }
-
- if (redo_dma_risc)
- if (0 != (rc = bttv_buffer_risc(btv,buf)))
- goto fail;
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
- fail:
- bttv_dma_free(q,btv,buf);
- return rc;
-}
-
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- struct bttv_fh *fh = q->priv_data;
-
- *size = fh->fmt->depth*fh->width*fh->height >> 3;
- if (0 == *count)
- *count = gbuffers;
- if (*size * *count > gbuffers * gbufsize)
- *count = (gbuffers * gbufsize) / *size;
- return 0;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
- struct bttv_fh *fh = q->priv_data;
-
- return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt,
- fh->width, fh->height, field);
-}
-
-static void
-buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
- struct bttv_fh *fh = q->priv_data;
- struct bttv *btv = fh->btv;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue,&btv->capture);
- if (!btv->curr.frame_irq) {
- btv->loop_irq |= 1;
- bttv_set_dma(btv, 0x03);
- }
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb);
- struct bttv_fh *fh = q->priv_data;
-
- bttv_dma_free(q,fh->btv,buf);
-}
-
-static struct videobuf_queue_ops bttv_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- unsigned int i;
- int err;
-
- err = v4l2_prio_check(&btv->prio, fh->prio);
- if (err)
- goto err;
-
- for (i = 0; i < BTTV_TVNORMS; i++)
- if (*id & bttv_tvnorms[i].v4l2_id)
- break;
- if (i == BTTV_TVNORMS) {
- err = -EINVAL;
- goto err;
- }
-
- set_tvnorm(btv, i);
-
-err:
-
- return err;
-}
-
-static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
- *id = V4L2_STD_625_50;
- else
- *id = V4L2_STD_525_60;
- return 0;
-}
-
-static int bttv_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int rc = 0;
-
- if (i->index >= bttv_tvcards[btv->c.type].video_inputs) {
- rc = -EINVAL;
- goto err;
- }
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- i->audioset = 1;
-
- if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
- sprintf(i->name, "Television");
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->tuner = 0;
- } else if (i->index == btv->svhs) {
- sprintf(i->name, "S-Video");
- } else {
- sprintf(i->name, "Composite%d", i->index);
- }
-
- if (i->index == btv->input) {
- __u32 dstatus = btread(BT848_DSTATUS);
- if (0 == (dstatus & BT848_DSTATUS_PRES))
- i->status |= V4L2_IN_ST_NO_SIGNAL;
- if (0 == (dstatus & BT848_DSTATUS_HLOC))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- }
-
- i->std = BTTV_NORMS;
-
-err:
-
- return rc;
-}
-
-static int bttv_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- *i = btv->input;
-
- return 0;
-}
-
-static int bttv_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- int err;
-
- err = v4l2_prio_check(&btv->prio, fh->prio);
- if (unlikely(err))
- goto err;
-
- if (i > bttv_tvcards[btv->c.type].video_inputs) {
- err = -EINVAL;
- goto err;
- }
-
- set_input(btv, i, btv->tvnorm);
-
-err:
- return 0;
-}
-
-static int bttv_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int err;
-
- if (unlikely(0 != t->index))
- return -EINVAL;
-
- if (unlikely(btv->tuner_type == TUNER_ABSENT)) {
- err = -EINVAL;
- goto err;
- }
-
- err = v4l2_prio_check(&btv->prio, fh->prio);
- if (unlikely(err))
- goto err;
-
- bttv_call_all(btv, tuner, s_tuner, t);
-
- if (btv->audio_mode_gpio)
- btv->audio_mode_gpio(btv, t, 1);
-
-err:
-
- return 0;
-}
-
-static int bttv_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = btv->freq;
-
- return 0;
-}
-
-static int bttv_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int err;
-
- if (unlikely(f->tuner != 0))
- return -EINVAL;
-
- err = v4l2_prio_check(&btv->prio, fh->prio);
- if (unlikely(err))
- goto err;
-
- if (unlikely(f->type != (btv->radio_user
- ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) {
- err = -EINVAL;
- goto err;
- }
- btv->freq = f->frequency;
- bttv_call_all(btv, tuner, s_frequency, f);
- if (btv->has_matchbox && btv->radio_user)
- tea5757_set_freq(btv, btv->freq);
-err:
-
- return 0;
-}
-
-static int bttv_log_status(struct file *file, void *f)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- bttv_call_all(btv, core, log_status);
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int bttv_g_register(struct file *file, void *f,
- struct v4l2_dbg_register *reg)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- /* bt848 has a 12-bit register space */
- reg->reg &= 0xfff;
- reg->val = btread(reg->reg);
- reg->size = 1;
-
- return 0;
-}
-
-static int bttv_s_register(struct file *file, void *f,
- struct v4l2_dbg_register *reg)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- /* bt848 has a 12-bit register space */
- reg->reg &= 0xfff;
- btwrite(reg->val, reg->reg);
-
- return 0;
-}
-#endif
-
-/* Given cropping boundaries b and the scaled width and height of a
- single field or frame, which must not exceed hardware limits, this
- function adjusts the cropping parameters c. */
-static void
-bttv_crop_adjust (struct bttv_crop * c,
- const struct v4l2_rect * b,
- __s32 width,
- __s32 height,
- enum v4l2_field field)
-{
- __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field);
- __s32 max_left;
- __s32 max_top;
-
- if (width < c->min_scaled_width) {
- /* Max. hor. scale factor 16:1. */
- c->rect.width = width * 16;
- } else if (width > c->max_scaled_width) {
- /* Min. hor. scale factor 1:1. */
- c->rect.width = width;
-
- max_left = b->left + b->width - width;
- max_left = min(max_left, (__s32) MAX_HDELAY);
- if (c->rect.left > max_left)
- c->rect.left = max_left;
- }
-
- if (height < c->min_scaled_height) {
- /* Max. vert. scale factor 16:1, single fields 8:1. */
- c->rect.height = height * 16;
- } else if (frame_height > c->max_scaled_height) {
- /* Min. vert. scale factor 1:1.
- Top and height count field lines times two. */
- c->rect.height = (frame_height + 1) & ~1;
-
- max_top = b->top + b->height - c->rect.height;
- if (c->rect.top > max_top)
- c->rect.top = max_top;
- }
-
- bttv_crop_calc_limits(c);
-}
-
-/* Returns an error if scaling to a frame or single field with the given
- width and height is not possible with the current cropping parameters
- and width aligned according to width_mask. If adjust_size is TRUE the
- function may adjust the width and/or height instead, rounding width
- to (width + width_bias) & width_mask. If adjust_crop is TRUE it may
- also adjust the current cropping parameters to get closer to the
- desired image size. */
-static int
-limit_scaled_size_lock (struct bttv_fh * fh,
- __s32 * width,
- __s32 * height,
- enum v4l2_field field,
- unsigned int width_mask,
- unsigned int width_bias,
- int adjust_size,
- int adjust_crop)
-{
- struct bttv *btv = fh->btv;
- const struct v4l2_rect *b;
- struct bttv_crop *c;
- __s32 min_width;
- __s32 min_height;
- __s32 max_width;
- __s32 max_height;
- int rc;
-
- BUG_ON((int) width_mask >= 0 ||
- width_bias >= (unsigned int) -width_mask);
-
- /* Make sure tvnorm, vbi_end and the current cropping parameters
- remain consistent until we're done. */
-
- b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
-
- /* Do crop - use current, don't - use default parameters. */
- c = &btv->crop[!!fh->do_crop];
-
- if (fh->do_crop
- && adjust_size
- && adjust_crop
- && !locked_btres(btv, VIDEO_RESOURCES)) {
- min_width = 48;
- min_height = 32;
-
- /* We cannot scale up. When the scaled image is larger
- than crop.rect we adjust the crop.rect as required
- by the V4L2 spec, hence cropcap.bounds are our limit. */
- max_width = min(b->width, (__s32) MAX_HACTIVE);
- max_height = b->height;
-
- /* We cannot capture the same line as video and VBI data.
- Note btv->vbi_end is really a minimum, see
- bttv_vbi_try_fmt(). */
- if (btv->vbi_end > b->top) {
- max_height -= btv->vbi_end - b->top;
- rc = -EBUSY;
- if (min_height > max_height)
- goto fail;
- }
- } else {
- rc = -EBUSY;
- if (btv->vbi_end > c->rect.top)
- goto fail;
-
- min_width = c->min_scaled_width;
- min_height = c->min_scaled_height;
- max_width = c->max_scaled_width;
- max_height = c->max_scaled_height;
-
- adjust_crop = 0;
- }
-
- min_width = (min_width - width_mask - 1) & width_mask;
- max_width = max_width & width_mask;
-
- /* Max. scale factor is 16:1 for frames, 8:1 for fields. */
- min_height = min_height;
- /* Min. scale factor is 1:1. */
- max_height >>= !V4L2_FIELD_HAS_BOTH(field);
-
- if (adjust_size) {
- *width = clamp(*width, min_width, max_width);
- *height = clamp(*height, min_height, max_height);
-
- /* Round after clamping to avoid overflow. */
- *width = (*width + width_bias) & width_mask;
-
- if (adjust_crop) {
- bttv_crop_adjust(c, b, *width, *height, field);
-
- if (btv->vbi_end > c->rect.top) {
- /* Move the crop window out of the way. */
- c->rect.top = btv->vbi_end;
- }
- }
- } else {
- rc = -EINVAL;
- if (*width < min_width ||
- *height < min_height ||
- *width > max_width ||
- *height > max_height ||
- 0 != (*width & ~width_mask))
- goto fail;
- }
-
- rc = 0; /* success */
-
- fail:
-
- return rc;
-}
-
-/* Returns an error if the given overlay window dimensions are not
- possible with the current cropping parameters. If adjust_size is
- TRUE the function may adjust the window width and/or height
- instead, however it always rounds the horizontal position and
- width as btcx_align() does. If adjust_crop is TRUE the function
- may also adjust the current cropping parameters to get closer
- to the desired window size. */
-static int
-verify_window_lock (struct bttv_fh * fh,
- struct v4l2_window * win,
- int adjust_size,
- int adjust_crop)
-{
- enum v4l2_field field;
- unsigned int width_mask;
- int rc;
-
- if (win->w.width < 48 || win->w.height < 32)
- return -EINVAL;
- if (win->clipcount > 2048)
- return -EINVAL;
-
- field = win->field;
-
- if (V4L2_FIELD_ANY == field) {
- __s32 height2;
-
- height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (win->w.height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_TOP;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- /* 4-byte alignment. */
- if (NULL == fh->ovfmt)
- return -EINVAL;
- width_mask = ~0;
- switch (fh->ovfmt->depth) {
- case 8:
- case 24:
- width_mask = ~3;
- break;
- case 16:
- width_mask = ~1;
- break;
- case 32:
- break;
- default:
- BUG();
- }
-
- win->w.width -= win->w.left & ~width_mask;
- win->w.left = (win->w.left - width_mask - 1) & width_mask;
-
- rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height,
- field, width_mask,
- /* width_bias: round down */ 0,
- adjust_size, adjust_crop);
- if (0 != rc)
- return rc;
-
- win->field = field;
- return 0;
-}
-
-static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv,
- struct v4l2_window *win, int fixup)
-{
- struct v4l2_clip *clips = NULL;
- int n,size,retval = 0;
-
- if (NULL == fh->ovfmt)
- return -EINVAL;
- if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
- retval = verify_window_lock(fh, win,
- /* adjust_size */ fixup,
- /* adjust_crop */ fixup);
- if (0 != retval)
- return retval;
-
- /* copy clips -- luckily v4l1 + v4l2 are binary
- compatible here ...*/
- n = win->clipcount;
- size = sizeof(*clips)*(n+4);
- clips = kmalloc(size,GFP_KERNEL);
- if (NULL == clips)
- return -ENOMEM;
- if (n > 0) {
- if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
- kfree(clips);
- return -EFAULT;
- }
- }
-
- /* clip against screen */
- if (NULL != btv->fbuf.base)
- n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
- &win->w, clips, n);
- btcx_sort_clips(clips,n);
-
- /* 4-byte alignments */
- switch (fh->ovfmt->depth) {
- case 8:
- case 24:
- btcx_align(&win->w, clips, n, 3);
- break;
- case 16:
- btcx_align(&win->w, clips, n, 1);
- break;
- case 32:
- /* no alignment fixups needed */
- break;
- default:
- BUG();
- }
-
- kfree(fh->ov.clips);
- fh->ov.clips = clips;
- fh->ov.nclips = n;
-
- fh->ov.w = win->w;
- fh->ov.field = win->field;
- fh->ov.setup_ok = 1;
-
- btv->init.ov.w.width = win->w.width;
- btv->init.ov.w.height = win->w.height;
- btv->init.ov.field = win->field;
-
- /* update overlay if needed */
- retval = 0;
- if (check_btres(fh, RESOURCE_OVERLAY)) {
- struct bttv_buffer *new;
-
- new = videobuf_sg_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
- retval = bttv_switch_overlay(btv,fh,new);
- }
- return retval;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
-{
- struct videobuf_queue* q = NULL;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- q = &fh->cap;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- q = &fh->vbi;
- break;
- default:
- BUG();
- }
- return q;
-}
-
-static int bttv_resource(struct bttv_fh *fh)
-{
- int res = 0;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- res = RESOURCE_VIDEO_STREAM;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- res = RESOURCE_VBI;
- break;
- default:
- BUG();
- }
- return res;
-}
-
-static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
-{
- struct videobuf_queue *q = bttv_queue(fh);
- int res = bttv_resource(fh);
-
- if (check_btres(fh,res))
- return -EBUSY;
- if (videobuf_queue_is_busy(q))
- return -EBUSY;
- fh->type = type;
- return 0;
-}
-
-static void
-pix_format_set_size (struct v4l2_pix_format * f,
- const struct bttv_format * fmt,
- unsigned int width,
- unsigned int height)
-{
- f->width = width;
- f->height = height;
-
- if (fmt->flags & FORMAT_FLAGS_PLANAR) {
- f->bytesperline = width; /* Y plane */
- f->sizeimage = (width * height * fmt->depth) >> 3;
- } else {
- f->bytesperline = (width * fmt->depth) >> 3;
- f->sizeimage = height * f->bytesperline;
- }
-}
-
-static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct bttv_fh *fh = priv;
-
- pix_format_set_size(&f->fmt.pix, fh->fmt,
- fh->width, fh->height);
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
-
- return 0;
-}
-
-static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct bttv_fh *fh = priv;
-
- f->fmt.win.w = fh->ov.w;
- f->fmt.win.field = fh->ov.field;
-
- return 0;
-}
-
-static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- const struct bttv_format *fmt;
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- enum v4l2_field field;
- __s32 width, height;
- int rc;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
-
- if (V4L2_FIELD_ANY == field) {
- __s32 height2;
-
- height2 = btv->crop[!!fh->do_crop].rect.height >> 1;
- field = (f->fmt.pix.height > height2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
-
- if (V4L2_FIELD_SEQ_BT == field)
- field = V4L2_FIELD_SEQ_TB;
-
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_ALTERNATE:
- case V4L2_FIELD_INTERLACED:
- break;
- case V4L2_FIELD_SEQ_TB:
- if (fmt->flags & FORMAT_FLAGS_PLANAR)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- width = f->fmt.pix.width;
- height = f->fmt.pix.height;
-
- rc = limit_scaled_size_lock(fh, &width, &height, field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
- /* adjust_size */ 1,
- /* adjust_crop */ 0);
- if (0 != rc)
- return rc;
-
- /* update data for the application */
- f->fmt.pix.field = field;
- pix_format_set_size(&f->fmt.pix, fmt, width, height);
-
- return 0;
-}
-
-static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct bttv_fh *fh = priv;
-
- return verify_window_lock(fh, &f->fmt.win,
- /* adjust_size */ 1,
- /* adjust_crop */ 0);
-}
-
-static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int retval;
- const struct bttv_format *fmt;
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- __s32 width, height;
- enum v4l2_field field;
-
- retval = bttv_switch_type(fh, f->type);
- if (0 != retval)
- return retval;
-
- retval = bttv_try_fmt_vid_cap(file, priv, f);
- if (0 != retval)
- return retval;
-
- width = f->fmt.pix.width;
- height = f->fmt.pix.height;
- field = f->fmt.pix.field;
-
- retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field,
- /* width_mask: 4 pixels */ ~3,
- /* width_bias: nearest */ 2,
- /* adjust_size */ 1,
- /* adjust_crop */ 1);
- if (0 != retval)
- return retval;
-
- f->fmt.pix.field = field;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
- /* update our state informations */
- fh->fmt = fmt;
- fh->cap.field = f->fmt.pix.field;
- fh->cap.last = V4L2_FIELD_NONE;
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- btv->init.fmt = fmt;
- btv->init.width = f->fmt.pix.width;
- btv->init.height = f->fmt.pix.height;
-
- return 0;
-}
-
-static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (no_overlay > 0) {
- pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
-
- return setup_window_lock(fh, btv, &f->fmt.win, 1);
-}
-
-static int bttv_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (0 == v4l2)
- return -EINVAL;
-
- strlcpy(cap->driver, "bttv", sizeof(cap->driver));
- strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "PCI:%s", pci_name(btv->c.pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- if (no_overlay <= 0)
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-
- /*
- * No need to lock here: those vars are initialized during board
- * probe and remains untouched during the rest of the driver lifecycle
- */
- if (btv->has_saa6588)
- cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
- if (btv->tuner_type != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
-}
-
-static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
-{
- int index = -1, i;
-
- for (i = 0; i < FORMATS; i++) {
- if (formats[i].fourcc != -1)
- index++;
- if ((unsigned int)index == f->index)
- break;
- }
- if (FORMATS == i)
- return -EINVAL;
-
- f->pixelformat = formats[i].fourcc;
- strlcpy(f->description, formats[i].name, sizeof(f->description));
-
- return i;
-}
-
-static int bttv_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- int rc = bttv_enum_fmt_cap_ovr(f);
-
- if (rc < 0)
- return rc;
-
- return 0;
-}
-
-static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- int rc;
-
- if (no_overlay > 0) {
- pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
-
- rc = bttv_enum_fmt_cap_ovr(f);
-
- if (rc < 0)
- return rc;
-
- if (!(formats[rc].flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
-
- return 0;
-}
-
-static int bttv_g_fbuf(struct file *file, void *f,
- struct v4l2_framebuffer *fb)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- *fb = btv->fbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- if (fh->ovfmt)
- fb->fmt.pixelformat = fh->ovfmt->fourcc;
- return 0;
-}
-
-static int bttv_overlay(struct file *file, void *f, unsigned int on)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
- struct bttv_buffer *new;
- int retval = 0;
-
- if (on) {
- /* verify args */
- if (unlikely(!btv->fbuf.base)) {
- return -EINVAL;
- }
- if (unlikely(!fh->ov.setup_ok)) {
- dprintk("%d: overlay: !setup_ok\n", btv->c.nr);
- retval = -EINVAL;
- }
- if (retval)
- return retval;
- }
-
- if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY))
- return -EBUSY;
-
- if (on) {
- fh->ov.tvnorm = btv->tvnorm;
- new = videobuf_sg_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
- } else {
- new = NULL;
- }
-
- /* switch over */
- retval = bttv_switch_overlay(btv, fh, new);
- return retval;
-}
-
-static int bttv_s_fbuf(struct file *file, void *f,
- struct v4l2_framebuffer *fb)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
- const struct bttv_format *fmt;
- int retval;
-
- if (!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
- if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
- return -EINVAL;
-
- retval = -EINVAL;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- __s32 width = fb->fmt.width;
- __s32 height = fb->fmt.height;
-
- retval = limit_scaled_size_lock(fh, &width, &height,
- V4L2_FIELD_INTERLACED,
- /* width_mask */ ~3,
- /* width_bias */ 2,
- /* adjust_size */ 0,
- /* adjust_crop */ 0);
- if (0 != retval)
- return retval;
- }
-
- /* ok, accept it */
- btv->fbuf.base = fb->base;
- btv->fbuf.fmt.width = fb->fmt.width;
- btv->fbuf.fmt.height = fb->fmt.height;
- if (0 != fb->fmt.bytesperline)
- btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline;
- else
- btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8;
-
- retval = 0;
- fh->ovfmt = fmt;
- btv->init.ovfmt = fmt;
- if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
- fh->ov.w.left = 0;
- fh->ov.w.top = 0;
- fh->ov.w.width = fb->fmt.width;
- fh->ov.w.height = fb->fmt.height;
- btv->init.ov.w.width = fb->fmt.width;
- btv->init.ov.w.height = fb->fmt.height;
- kfree(fh->ov.clips);
- fh->ov.clips = NULL;
- fh->ov.nclips = 0;
-
- if (check_btres(fh, RESOURCE_OVERLAY)) {
- struct bttv_buffer *new;
-
- new = videobuf_sg_alloc(sizeof(*new));
- new->crop = btv->crop[!!fh->do_crop].rect;
- bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
- retval = bttv_switch_overlay(btv, fh, new);
- }
- }
- return retval;
-}
-
-static int bttv_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct bttv_fh *fh = priv;
- return videobuf_reqbufs(bttv_queue(fh), p);
-}
-
-static int bttv_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct bttv_fh *fh = priv;
- return videobuf_querybuf(bttv_queue(fh), b);
-}
-
-static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int res = bttv_resource(fh);
-
- if (!check_alloc_btres_lock(btv, fh, res))
- return -EBUSY;
-
- return videobuf_qbuf(bttv_queue(fh), b);
-}
-
-static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct bttv_fh *fh = priv;
- return videobuf_dqbuf(bttv_queue(fh), b,
- file->f_flags & O_NONBLOCK);
-}
-
-static int bttv_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int res = bttv_resource(fh);
-
- if (!check_alloc_btres_lock(btv, fh, res))
- return -EBUSY;
- return videobuf_streamon(bttv_queue(fh));
-}
-
-
-static int bttv_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- int retval;
- int res = bttv_resource(fh);
-
-
- retval = videobuf_streamoff(bttv_queue(fh));
- if (retval < 0)
- return retval;
- free_btres_lock(btv, fh, res);
- return 0;
-}
-
-static int bttv_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
- const struct v4l2_queryctrl *ctrl;
-
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
-
- if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME))
- *c = no_ctl;
- else {
- ctrl = ctrl_by_id(c->id);
-
- *c = (NULL != ctrl) ? *ctrl : no_ctl;
- }
-
- return 0;
-}
-
-static int bttv_g_parm(struct file *file, void *f,
- struct v4l2_streamparm *parm)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
- &parm->parm.capture.timeperframe);
-
- return 0;
-}
-
-static int bttv_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (btv->tuner_type == TUNER_ABSENT)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- t->rxsubchans = V4L2_TUNER_SUB_MONO;
- bttv_call_all(btv, tuner, g_tuner, t);
- strcpy(t->name, "Television");
- t->capability = V4L2_TUNER_CAP_NORM;
- t->type = V4L2_TUNER_ANALOG_TV;
- if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
- t->signal = 0xffff;
-
- if (btv->audio_mode_gpio)
- btv->audio_mode_gpio(btv, t, 0);
-
- return 0;
-}
-
-static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- *p = v4l2_prio_max(&btv->prio);
-
- return 0;
-}
-
-static int bttv_s_priority(struct file *file, void *f,
- enum v4l2_priority prio)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
- int rc;
-
- rc = v4l2_prio_change(&btv->prio, &fh->prio, prio);
-
- return rc;
-}
-
-static int bttv_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cap)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
-
- *cap = bttv_tvnorms[btv->tvnorm].cropcap;
-
- return 0;
-}
-
-static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
-
- /* No fh->do_crop = 1; because btv->crop[1] may be
- inconsistent with fh->width or fh->height and apps
- do not expect a change here. */
-
- crop->c = btv->crop[!!fh->do_crop].rect;
-
- return 0;
-}
-
-static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
- struct bttv_fh *fh = f;
- struct bttv *btv = fh->btv;
- const struct v4l2_rect *b;
- int retval;
- struct bttv_crop c;
- __s32 b_left;
- __s32 b_top;
- __s32 b_right;
- __s32 b_bottom;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
-
- /* Make sure tvnorm, vbi_end and the current cropping
- parameters remain consistent until we're done. Note
- read() may change vbi_end in check_alloc_btres_lock(). */
- retval = v4l2_prio_check(&btv->prio, fh->prio);
- if (0 != retval) {
- return retval;
- }
-
- retval = -EBUSY;
-
- if (locked_btres(fh->btv, VIDEO_RESOURCES)) {
- return retval;
- }
-
- b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds;
-
- b_left = b->left;
- b_right = b_left + b->width;
- b_bottom = b->top + b->height;
-
- b_top = max(b->top, btv->vbi_end);
- if (b_top + 32 >= b_bottom) {
- return retval;
- }
-
- /* Min. scaled size 48 x 32. */
- c.rect.left = clamp(crop->c.left, b_left, b_right - 48);
- c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY);
-
- c.rect.width = clamp(crop->c.width,
- 48, b_right - c.rect.left);
-
- c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32);
- /* Top and height must be a multiple of two. */
- c.rect.top = (c.rect.top + 1) & ~1;
-
- c.rect.height = clamp(crop->c.height,
- 32, b_bottom - c.rect.top);
- c.rect.height = (c.rect.height + 1) & ~1;
-
- bttv_crop_calc_limits(&c);
-
- btv->crop[1] = c;
-
- fh->do_crop = 1;
-
- if (fh->width < c.min_scaled_width) {
- fh->width = c.min_scaled_width;
- btv->init.width = c.min_scaled_width;
- } else if (fh->width > c.max_scaled_width) {
- fh->width = c.max_scaled_width;
- btv->init.width = c.max_scaled_width;
- }
-
- if (fh->height < c.min_scaled_height) {
- fh->height = c.min_scaled_height;
- btv->init.height = c.min_scaled_height;
- } else if (fh->height > c.max_scaled_height) {
- fh->height = c.max_scaled_height;
- btv->init.height = c.max_scaled_height;
- }
-
- return 0;
-}
-
-static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- strcpy(a->name, "audio");
- return 0;
-}
-
-static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- return 0;
-}
-
-static ssize_t bttv_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct bttv_fh *fh = file->private_data;
- int retval = 0;
-
- if (fh->btv->errors)
- bttv_reinit_bt848(fh->btv);
- dprintk("%d: read count=%d type=%s\n",
- fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]);
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) {
- /* VIDEO_READ in use by another fh,
- or VIDEO_STREAM by any fh. */
- return -EBUSY;
- }
- retval = videobuf_read_one(&fh->cap, data, count, ppos,
- file->f_flags & O_NONBLOCK);
- free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ);
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
- return -EBUSY;
- retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
- break;
- default:
- BUG();
- }
- return retval;
-}
-
-static unsigned int bttv_poll(struct file *file, poll_table *wait)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv_buffer *buf;
- enum v4l2_field field;
- unsigned int rc = POLLERR;
-
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
- if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI))
- return POLLERR;
- return videobuf_poll_stream(file, &fh->vbi, wait);
- }
-
- if (check_btres(fh,RESOURCE_VIDEO_STREAM)) {
- /* streaming capture */
- if (list_empty(&fh->cap.stream))
- goto err;
- buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream);
- } else {
- /* read() capture */
- if (NULL == fh->cap.read_buf) {
- /* need to capture a new frame */
- if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM))
- goto err;
- fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize);
- if (NULL == fh->cap.read_buf)
- goto err;
- fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR;
- field = videobuf_next_field(&fh->cap);
- if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) {
- kfree (fh->cap.read_buf);
- fh->cap.read_buf = NULL;
- goto err;
- }
- fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
- fh->cap.read_off = 0;
- }
- buf = (struct bttv_buffer*)fh->cap.read_buf;
- }
-
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- rc = POLLIN|POLLRDNORM;
- else
- rc = 0;
-err:
- return rc;
-}
-
-static int bttv_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct bttv *btv = video_drvdata(file);
- struct bttv_fh *fh;
- enum v4l2_buf_type type = 0;
-
- dprintk("open dev=%s\n", video_device_node_name(vdev));
-
- if (vdev->vfl_type == VFL_TYPE_GRABBER) {
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- } else if (vdev->vfl_type == VFL_TYPE_VBI) {
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- } else {
- WARN_ON(1);
- return -ENODEV;
- }
-
- dprintk("%d: open called (type=%s)\n",
- btv->c.nr, v4l2_type_names[type]);
-
- /* allocate per filehandle data */
- fh = kmalloc(sizeof(*fh), GFP_KERNEL);
- if (unlikely(!fh))
- return -ENOMEM;
- file->private_data = fh;
-
- *fh = btv->init;
-
- fh->type = type;
- fh->ov.setup_ok = 0;
-
- v4l2_prio_open(&btv->prio, &fh->prio);
-
- videobuf_queue_sg_init(&fh->cap, &bttv_video_qops,
- &btv->c.pci->dev, &btv->s_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct bttv_buffer),
- fh, &btv->lock);
- videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops,
- &btv->c.pci->dev, &btv->s_lock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct bttv_buffer),
- fh, &btv->lock);
- set_tvnorm(btv,btv->tvnorm);
- set_input(btv, btv->input, btv->tvnorm);
-
- btv->users++;
-
- /* The V4L2 spec requires one global set of cropping parameters
- which only change on request. These are stored in btv->crop[1].
- However for compatibility with V4L apps and cropping unaware
- V4L2 apps we now reset the cropping parameters as seen through
- this fh, which is to say VIDIOC_G_CROP and scaling limit checks
- will use btv->crop[0], the default cropping parameters for the
- current video standard, and VIDIOC_S_FMT will not implicitely
- change the cropping parameters until VIDIOC_S_CROP has been
- called. */
- fh->do_crop = !reset_crop; /* module parameter */
-
- /* Likewise there should be one global set of VBI capture
- parameters, but for compatibility with V4L apps and earlier
- driver versions each fh has its own parameters. */
- bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm);
-
- bttv_field_count(btv);
- return 0;
-}
-
-static int bttv_release(struct file *file)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
-
- /* turn off overlay */
- if (check_btres(fh, RESOURCE_OVERLAY))
- bttv_switch_overlay(btv,fh,NULL);
-
- /* stop video capture */
- if (check_btres(fh, RESOURCE_VIDEO_STREAM)) {
- videobuf_streamoff(&fh->cap);
- free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM);
- }
- if (fh->cap.read_buf) {
- buffer_release(&fh->cap,fh->cap.read_buf);
- kfree(fh->cap.read_buf);
- }
- if (check_btres(fh, RESOURCE_VIDEO_READ)) {
- free_btres_lock(btv, fh, RESOURCE_VIDEO_READ);
- }
-
- /* stop vbi capture */
- if (check_btres(fh, RESOURCE_VBI)) {
- videobuf_stop(&fh->vbi);
- free_btres_lock(btv,fh,RESOURCE_VBI);
- }
-
- /* free stuff */
-
- videobuf_mmap_free(&fh->cap);
- videobuf_mmap_free(&fh->vbi);
- v4l2_prio_close(&btv->prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
-
- btv->users--;
- bttv_field_count(btv);
-
- if (!btv->users)
- audio_mute(btv, 1);
-
- return 0;
-}
-
-static int
-bttv_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct bttv_fh *fh = file->private_data;
-
- dprintk("%d: mmap type=%s 0x%lx+%ld\n",
- fh->btv->c.nr, v4l2_type_names[fh->type],
- vma->vm_start, vma->vm_end - vma->vm_start);
- return videobuf_mmap_mapper(bttv_queue(fh),vma);
-}
-
-static const struct v4l2_file_operations bttv_fops =
-{
- .owner = THIS_MODULE,
- .open = bttv_open,
- .release = bttv_release,
- .unlocked_ioctl = video_ioctl2,
- .read = bttv_read,
- .mmap = bttv_mmap,
- .poll = bttv_poll,
-};
-
-static const struct v4l2_ioctl_ops bttv_ioctl_ops = {
- .vidioc_querycap = bttv_querycap,
- .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
- .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
- .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
- .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
- .vidioc_g_audio = bttv_g_audio,
- .vidioc_s_audio = bttv_s_audio,
- .vidioc_cropcap = bttv_cropcap,
- .vidioc_reqbufs = bttv_reqbufs,
- .vidioc_querybuf = bttv_querybuf,
- .vidioc_qbuf = bttv_qbuf,
- .vidioc_dqbuf = bttv_dqbuf,
- .vidioc_s_std = bttv_s_std,
- .vidioc_enum_input = bttv_enum_input,
- .vidioc_g_input = bttv_g_input,
- .vidioc_s_input = bttv_s_input,
- .vidioc_queryctrl = bttv_queryctrl,
- .vidioc_g_ctrl = bttv_g_ctrl,
- .vidioc_s_ctrl = bttv_s_ctrl,
- .vidioc_streamon = bttv_streamon,
- .vidioc_streamoff = bttv_streamoff,
- .vidioc_g_tuner = bttv_g_tuner,
- .vidioc_s_tuner = bttv_s_tuner,
- .vidioc_g_crop = bttv_g_crop,
- .vidioc_s_crop = bttv_s_crop,
- .vidioc_g_fbuf = bttv_g_fbuf,
- .vidioc_s_fbuf = bttv_s_fbuf,
- .vidioc_overlay = bttv_overlay,
- .vidioc_g_priority = bttv_g_priority,
- .vidioc_s_priority = bttv_s_priority,
- .vidioc_g_parm = bttv_g_parm,
- .vidioc_g_frequency = bttv_g_frequency,
- .vidioc_s_frequency = bttv_s_frequency,
- .vidioc_log_status = bttv_log_status,
- .vidioc_querystd = bttv_querystd,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = bttv_g_register,
- .vidioc_s_register = bttv_s_register,
-#endif
-};
-
-static struct video_device bttv_video_template = {
- .fops = &bttv_fops,
- .ioctl_ops = &bttv_ioctl_ops,
- .tvnorms = BTTV_NORMS,
- .current_norm = V4L2_STD_PAL,
-};
-
-/* ----------------------------------------------------------------------- */
-/* radio interface */
-
-static int radio_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct bttv *btv = video_drvdata(file);
- struct bttv_fh *fh;
-
- dprintk("open dev=%s\n", video_device_node_name(vdev));
-
- dprintk("%d: open called (radio)\n", btv->c.nr);
-
- /* allocate per filehandle data */
- fh = kmalloc(sizeof(*fh), GFP_KERNEL);
- if (unlikely(!fh))
- return -ENOMEM;
- file->private_data = fh;
- *fh = btv->init;
-
- v4l2_prio_open(&btv->prio, &fh->prio);
-
- btv->radio_user++;
-
- bttv_call_all(btv, tuner, s_radio);
- audio_input(btv,TVAUDIO_INPUT_RADIO);
-
- return 0;
-}
-
-static int radio_release(struct file *file)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
- struct saa6588_command cmd;
-
- v4l2_prio_close(&btv->prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
-
- btv->radio_user--;
-
- bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
-
- return 0;
-}
-
-static int radio_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- strcpy(cap->driver, "bttv");
- strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card));
- sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci));
- cap->capabilities = V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (btv->tuner_type == TUNER_ABSENT)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
-
- bttv_call_all(btv, tuner, g_tuner, t);
-
- if (btv->audio_mode_gpio)
- btv->audio_mode_gpio(btv, t, 0);
-
- return 0;
-}
-
-static int radio_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
-
- strcpy(i->name, "Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- strcpy(a->name, "Radio");
-
- return 0;
-}
-
-static int radio_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct bttv_fh *fh = priv;
- struct bttv *btv = fh->btv;
-
- if (0 != t->index)
- return -EINVAL;
-
- bttv_call_all(btv, tuner, s_tuner, t);
- return 0;
-}
-
-static int radio_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- return 0;
-}
-
-static int radio_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (unlikely(i))
- return -EINVAL;
-
- return 0;
-}
-
-static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- return 0;
-}
-
-static int radio_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- const struct v4l2_queryctrl *ctrl;
-
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
-
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- ctrl = ctrl_by_id(c->id);
- *c = *ctrl;
- } else
- *c = no_ctl;
-
- return 0;
-}
-
-static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static ssize_t radio_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
- struct saa6588_command cmd;
- cmd.block_count = count/3;
- cmd.buffer = data;
- cmd.instance = file;
- cmd.result = -ENODEV;
-
- bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd);
-
- return cmd.result;
-}
-
-static unsigned int radio_poll(struct file *file, poll_table *wait)
-{
- struct bttv_fh *fh = file->private_data;
- struct bttv *btv = fh->btv;
- struct saa6588_command cmd;
- cmd.instance = file;
- cmd.event_list = wait;
- cmd.result = -ENODEV;
- bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd);
-
- return cmd.result;
-}
-
-static const struct v4l2_file_operations radio_fops =
-{
- .owner = THIS_MODULE,
- .open = radio_open,
- .read = radio_read,
- .release = radio_release,
- .unlocked_ioctl = video_ioctl2,
- .poll = radio_poll,
-};
-
-static const struct v4l2_ioctl_ops radio_ioctl_ops = {
- .vidioc_querycap = radio_querycap,
- .vidioc_g_tuner = radio_g_tuner,
- .vidioc_enum_input = radio_enum_input,
- .vidioc_g_audio = radio_g_audio,
- .vidioc_s_tuner = radio_s_tuner,
- .vidioc_s_audio = radio_s_audio,
- .vidioc_s_input = radio_s_input,
- .vidioc_s_std = radio_s_std,
- .vidioc_queryctrl = radio_queryctrl,
- .vidioc_g_input = radio_g_input,
- .vidioc_g_ctrl = bttv_g_ctrl,
- .vidioc_s_ctrl = bttv_s_ctrl,
- .vidioc_g_frequency = bttv_g_frequency,
- .vidioc_s_frequency = bttv_s_frequency,
-};
-
-static struct video_device radio_template = {
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-/* some debug code */
-
-static int bttv_risc_decode(u32 risc)
-{
- static char *instr[16] = {
- [ BT848_RISC_WRITE >> 28 ] = "write",
- [ BT848_RISC_SKIP >> 28 ] = "skip",
- [ BT848_RISC_WRITEC >> 28 ] = "writec",
- [ BT848_RISC_JUMP >> 28 ] = "jump",
- [ BT848_RISC_SYNC >> 28 ] = "sync",
- [ BT848_RISC_WRITE123 >> 28 ] = "write123",
- [ BT848_RISC_SKIP123 >> 28 ] = "skip123",
- [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23",
- };
- static int incr[16] = {
- [ BT848_RISC_WRITE >> 28 ] = 2,
- [ BT848_RISC_JUMP >> 28 ] = 2,
- [ BT848_RISC_SYNC >> 28 ] = 2,
- [ BT848_RISC_WRITE123 >> 28 ] = 5,
- [ BT848_RISC_SKIP123 >> 28 ] = 2,
- [ BT848_RISC_WRITE1S23 >> 28 ] = 3,
- };
- static char *bits[] = {
- "be0", "be1", "be2", "be3/resync",
- "set0", "set1", "set2", "set3",
- "clr0", "clr1", "clr2", "clr3",
- "irq", "res", "eol", "sol",
- };
- int i;
-
- pr_cont("0x%08x [ %s", risc,
- instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
- for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
- if (risc & (1 << (i + 12)))
- pr_cont(" %s", bits[i]);
- pr_cont(" count=%d ]\n", risc & 0xfff);
- return incr[risc >> 28] ? incr[risc >> 28] : 1;
-}
-
-static void bttv_risc_disasm(struct bttv *btv,
- struct btcx_riscmem *risc)
-{
- unsigned int i,j,n;
-
- pr_info("%s: risc disasm: %p [dma=0x%08lx]\n",
- btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
- for (i = 0; i < (risc->size >> 2); i += n) {
- pr_info("%s: 0x%lx: ",
- btv->c.v4l2_dev.name,
- (unsigned long)(risc->dma + (i<<2)));
- n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
- for (j = 1; j < n; j++)
- pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
- btv->c.v4l2_dev.name,
- (unsigned long)(risc->dma + ((i+j)<<2)),
- risc->cpu[i+j], j);
- if (0 == risc->cpu[i])
- break;
- }
-}
-
-static void bttv_print_riscaddr(struct bttv *btv)
-{
- pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma);
- pr_info(" vbi : o=%08llx e=%08llx\n",
- btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0,
- btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0);
- pr_info(" cap : o=%08llx e=%08llx\n",
- btv->curr.top
- ? (unsigned long long)btv->curr.top->top.dma : 0,
- btv->curr.bottom
- ? (unsigned long long)btv->curr.bottom->bottom.dma : 0);
- pr_info(" scr : o=%08llx e=%08llx\n",
- btv->screen ? (unsigned long long)btv->screen->top.dma : 0,
- btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0);
- bttv_risc_disasm(btv, &btv->main);
-}
-
-/* ----------------------------------------------------------------------- */
-/* irq handler */
-
-static char *irq_name[] = {
- "FMTCHG", // format change detected (525 vs. 625)
- "VSYNC", // vertical sync (new field)
- "HSYNC", // horizontal sync
- "OFLOW", // chroma/luma AGC overflow
- "HLOCK", // horizontal lock changed
- "VPRES", // video presence changed
- "6", "7",
- "I2CDONE", // hw irc operation finished
- "GPINT", // gpio port triggered irq
- "10",
- "RISCI", // risc instruction triggered irq
- "FBUS", // pixel data fifo dropped data (high pci bus latencies)
- "FTRGT", // pixel data fifo overrun
- "FDSR", // fifo data stream resyncronisation
- "PPERR", // parity error (data transfer)
- "RIPERR", // parity error (read risc instructions)
- "PABORT", // pci abort
- "OCERR", // risc instruction error
- "SCERR", // syncronisation error
-};
-
-static void bttv_print_irqbits(u32 print, u32 mark)
-{
- unsigned int i;
-
- pr_cont("bits:");
- for (i = 0; i < ARRAY_SIZE(irq_name); i++) {
- if (print & (1 << i))
- pr_cont(" %s", irq_name[i]);
- if (mark & (1 << i))
- pr_cont("*");
- }
-}
-
-static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc)
-{
- pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n",
- btv->c.nr,
- (unsigned long)btv->main.dma,
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]),
- (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]),
- (unsigned long)rc);
-
- if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) {
- pr_notice("%d: Oh, there (temporarily?) is no input signal. "
- "Ok, then this is harmless, don't worry ;)\n",
- btv->c.nr);
- return;
- }
- pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n",
- btv->c.nr);
- pr_notice("%d: Lets try to catch the culpit red-handed ...\n",
- btv->c.nr);
- dump_stack();
-}
-
-static int
-bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set)
-{
- struct bttv_buffer *item;
-
- memset(set,0,sizeof(*set));
-
- /* capture request ? */
- if (!list_empty(&btv->capture)) {
- set->frame_irq = 1;
- item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
- if (V4L2_FIELD_HAS_TOP(item->vb.field))
- set->top = item;
- if (V4L2_FIELD_HAS_BOTTOM(item->vb.field))
- set->bottom = item;
-
- /* capture request for other field ? */
- if (!V4L2_FIELD_HAS_BOTH(item->vb.field) &&
- (item->vb.queue.next != &btv->capture)) {
- item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue);
- /* Mike Isely <isely@pobox.com> - Only check
- * and set up the bottom field in the logic
- * below. Don't ever do the top field. This
- * of course means that if we set up the
- * bottom field in the above code that we'll
- * actually skip a field. But that's OK.
- * Having processed only a single buffer this
- * time, then the next time around the first
- * available buffer should be for a top field.
- * That will then cause us here to set up a
- * top then a bottom field in the normal way.
- * The alternative to this understanding is
- * that we set up the second available buffer
- * as a top field, but that's out of order
- * since this driver always processes the top
- * field first - the effect will be the two
- * buffers being returned in the wrong order,
- * with the second buffer also being delayed
- * by one field time (owing to the fifo nature
- * of videobuf). Worse still, we'll be stuck
- * doing fields out of order now every time
- * until something else causes a field to be
- * dropped. By effectively forcing a field to
- * drop this way then we always get back into
- * sync within a single frame time. (Out of
- * order fields can screw up deinterlacing
- * algorithms.) */
- if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) {
- if (NULL == set->bottom &&
- V4L2_FIELD_BOTTOM == item->vb.field) {
- set->bottom = item;
- }
- if (NULL != set->top && NULL != set->bottom)
- set->top_irq = 2;
- }
- }
- }
-
- /* screen overlay ? */
- if (NULL != btv->screen) {
- if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) {
- if (NULL == set->top && NULL == set->bottom) {
- set->top = btv->screen;
- set->bottom = btv->screen;
- }
- } else {
- if (V4L2_FIELD_TOP == btv->screen->vb.field &&
- NULL == set->top) {
- set->top = btv->screen;
- }
- if (V4L2_FIELD_BOTTOM == btv->screen->vb.field &&
- NULL == set->bottom) {
- set->bottom = btv->screen;
- }
- }
- }
-
- dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n",
- btv->c.nr, set->top, set->bottom,
- btv->screen, set->frame_irq, set->top_irq);
- return 0;
-}
-
-static void
-bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup,
- struct bttv_buffer_set *curr, unsigned int state)
-{
- struct timeval ts;
-
- do_gettimeofday(&ts);
-
- if (wakeup->top == wakeup->bottom) {
- if (NULL != wakeup->top && curr->top != wakeup->top) {
- if (irq_debug > 1)
- pr_debug("%d: wakeup: both=%p\n",
- btv->c.nr, wakeup->top);
- wakeup->top->vb.ts = ts;
- wakeup->top->vb.field_count = btv->field_count;
- wakeup->top->vb.state = state;
- wake_up(&wakeup->top->vb.done);
- }
- } else {
- if (NULL != wakeup->top && curr->top != wakeup->top) {
- if (irq_debug > 1)
- pr_debug("%d: wakeup: top=%p\n",
- btv->c.nr, wakeup->top);
- wakeup->top->vb.ts = ts;
- wakeup->top->vb.field_count = btv->field_count;
- wakeup->top->vb.state = state;
- wake_up(&wakeup->top->vb.done);
- }
- if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) {
- if (irq_debug > 1)
- pr_debug("%d: wakeup: bottom=%p\n",
- btv->c.nr, wakeup->bottom);
- wakeup->bottom->vb.ts = ts;
- wakeup->bottom->vb.field_count = btv->field_count;
- wakeup->bottom->vb.state = state;
- wake_up(&wakeup->bottom->vb.done);
- }
- }
-}
-
-static void
-bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup,
- unsigned int state)
-{
- struct timeval ts;
-
- if (NULL == wakeup)
- return;
-
- do_gettimeofday(&ts);
- wakeup->vb.ts = ts;
- wakeup->vb.field_count = btv->field_count;
- wakeup->vb.state = state;
- wake_up(&wakeup->vb.done);
-}
-
-static void bttv_irq_timeout(unsigned long data)
-{
- struct bttv *btv = (struct bttv *)data;
- struct bttv_buffer_set old,new;
- struct bttv_buffer *ovbi;
- struct bttv_buffer *item;
- unsigned long flags;
-
- if (bttv_verbose) {
- pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ",
- btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total,
- btread(BT848_RISC_COUNT));
- bttv_print_irqbits(btread(BT848_INT_STAT),0);
- pr_cont("\n");
- }
-
- spin_lock_irqsave(&btv->s_lock,flags);
-
- /* deactivate stuff */
- memset(&new,0,sizeof(new));
- old = btv->curr;
- ovbi = btv->cvbi;
- btv->curr = new;
- btv->cvbi = NULL;
- btv->loop_irq = 0;
- bttv_buffer_activate_video(btv, &new);
- bttv_buffer_activate_vbi(btv, NULL);
- bttv_set_dma(btv, 0);
-
- /* wake up */
- bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR);
- bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR);
-
- /* cancel all outstanding capture / vbi requests */
- while (!list_empty(&btv->capture)) {
- item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
- list_del(&item->vb.queue);
- item->vb.state = VIDEOBUF_ERROR;
- wake_up(&item->vb.done);
- }
- while (!list_empty(&btv->vcapture)) {
- item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
- list_del(&item->vb.queue);
- item->vb.state = VIDEOBUF_ERROR;
- wake_up(&item->vb.done);
- }
-
- btv->errors++;
- spin_unlock_irqrestore(&btv->s_lock,flags);
-}
-
-static void
-bttv_irq_wakeup_top(struct bttv *btv)
-{
- struct bttv_buffer *wakeup = btv->curr.top;
-
- if (NULL == wakeup)
- return;
-
- spin_lock(&btv->s_lock);
- btv->curr.top_irq = 0;
- btv->curr.top = NULL;
- bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
-
- do_gettimeofday(&wakeup->vb.ts);
- wakeup->vb.field_count = btv->field_count;
- wakeup->vb.state = VIDEOBUF_DONE;
- wake_up(&wakeup->vb.done);
- spin_unlock(&btv->s_lock);
-}
-
-static inline int is_active(struct btcx_riscmem *risc, u32 rc)
-{
- if (rc < risc->dma)
- return 0;
- if (rc > risc->dma + risc->size)
- return 0;
- return 1;
-}
-
-static void
-bttv_irq_switch_video(struct bttv *btv)
-{
- struct bttv_buffer_set new;
- struct bttv_buffer_set old;
- dma_addr_t rc;
-
- spin_lock(&btv->s_lock);
-
- /* new buffer set */
- bttv_irq_next_video(btv, &new);
- rc = btread(BT848_RISC_COUNT);
- if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) ||
- (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) {
- btv->framedrop++;
- if (debug_latency)
- bttv_irq_debug_low_latency(btv, rc);
- spin_unlock(&btv->s_lock);
- return;
- }
-
- /* switch over */
- old = btv->curr;
- btv->curr = new;
- btv->loop_irq &= ~1;
- bttv_buffer_activate_video(btv, &new);
- bttv_set_dma(btv, 0);
-
- /* switch input */
- if (UNSET != btv->new_input) {
- video_mux(btv,btv->new_input);
- btv->new_input = UNSET;
- }
-
- /* wake up finished buffers */
- bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE);
- spin_unlock(&btv->s_lock);
-}
-
-static void
-bttv_irq_switch_vbi(struct bttv *btv)
-{
- struct bttv_buffer *new = NULL;
- struct bttv_buffer *old;
- u32 rc;
-
- spin_lock(&btv->s_lock);
-
- if (!list_empty(&btv->vcapture))
- new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
- old = btv->cvbi;
-
- rc = btread(BT848_RISC_COUNT);
- if (NULL != old && (is_active(&old->top, rc) ||
- is_active(&old->bottom, rc))) {
- btv->framedrop++;
- if (debug_latency)
- bttv_irq_debug_low_latency(btv, rc);
- spin_unlock(&btv->s_lock);
- return;
- }
-
- /* switch */
- btv->cvbi = new;
- btv->loop_irq &= ~4;
- bttv_buffer_activate_vbi(btv, new);
- bttv_set_dma(btv, 0);
-
- bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE);
- spin_unlock(&btv->s_lock);
-}
-
-static irqreturn_t bttv_irq(int irq, void *dev_id)
-{
- u32 stat,astat;
- u32 dstat;
- int count;
- struct bttv *btv;
- int handled = 0;
-
- btv=(struct bttv *)dev_id;
-
- count=0;
- while (1) {
- /* get/clear interrupt status bits */
- stat=btread(BT848_INT_STAT);
- astat=stat&btread(BT848_INT_MASK);
- if (!astat)
- break;
- handled = 1;
- btwrite(stat,BT848_INT_STAT);
-
- /* get device status bits */
- dstat=btread(BT848_DSTATUS);
-
- if (irq_debug) {
- pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ",
- btv->c.nr, count, btv->field_count,
- stat>>28, btread(BT848_RISC_COUNT));
- bttv_print_irqbits(stat,astat);
- if (stat & BT848_INT_HLOCK)
- pr_cont(" HLOC => %s",
- dstat & BT848_DSTATUS_HLOC
- ? "yes" : "no");
- if (stat & BT848_INT_VPRES)
- pr_cont(" PRES => %s",
- dstat & BT848_DSTATUS_PRES
- ? "yes" : "no");
- if (stat & BT848_INT_FMTCHG)
- pr_cont(" NUML => %s",
- dstat & BT848_DSTATUS_NUML
- ? "625" : "525");
- pr_cont("\n");
- }
-
- if (astat&BT848_INT_VSYNC)
- btv->field_count++;
-
- if ((astat & BT848_INT_GPINT) && btv->remote) {
- bttv_input_irq(btv);
- }
-
- if (astat & BT848_INT_I2CDONE) {
- btv->i2c_done = stat;
- wake_up(&btv->i2c_queue);
- }
-
- if ((astat & BT848_INT_RISCI) && (stat & (4<<28)))
- bttv_irq_switch_vbi(btv);
-
- if ((astat & BT848_INT_RISCI) && (stat & (2<<28)))
- bttv_irq_wakeup_top(btv);
-
- if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
- bttv_irq_switch_video(btv);
-
- if ((astat & BT848_INT_HLOCK) && btv->opt_automute)
- audio_mute(btv, btv->mute); /* trigger automute */
-
- if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
- pr_info("%d: %s%s @ %08x,",
- btv->c.nr,
- (astat & BT848_INT_SCERR) ? "SCERR" : "",
- (astat & BT848_INT_OCERR) ? "OCERR" : "",
- btread(BT848_RISC_COUNT));
- bttv_print_irqbits(stat,astat);
- pr_cont("\n");
- if (bttv_debug)
- bttv_print_riscaddr(btv);
- }
- if (fdsr && astat & BT848_INT_FDSR) {
- pr_info("%d: FDSR @ %08x\n",
- btv->c.nr, btread(BT848_RISC_COUNT));
- if (bttv_debug)
- bttv_print_riscaddr(btv);
- }
-
- count++;
- if (count > 4) {
-
- if (count > 8 || !(astat & BT848_INT_GPINT)) {
- btwrite(0, BT848_INT_MASK);
-
- pr_err("%d: IRQ lockup, cleared int mask [",
- btv->c.nr);
- } else {
- pr_err("%d: IRQ lockup, clearing GPINT from int mask [",
- btv->c.nr);
-
- btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT),
- BT848_INT_MASK);
- };
-
- bttv_print_irqbits(stat,astat);
-
- pr_cont("]\n");
- }
- }
- btv->irq_total++;
- if (handled)
- btv->irq_me++;
- return IRQ_RETVAL(handled);
-}
-
-
-/* ----------------------------------------------------------------------- */
-/* initialitation */
-
-static struct video_device *vdev_init(struct bttv *btv,
- const struct video_device *template,
- const char *type_name)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template;
- vfd->v4l2_dev = &btv->c.v4l2_dev;
- vfd->release = video_device_release;
- vfd->debug = bttv_debug;
- video_set_drvdata(vfd, btv);
- snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
- btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
- type_name, bttv_tvcards[btv->c.type].name);
- return vfd;
-}
-
-static void bttv_unregister_video(struct bttv *btv)
-{
- if (btv->video_dev) {
- if (video_is_registered(btv->video_dev))
- video_unregister_device(btv->video_dev);
- else
- video_device_release(btv->video_dev);
- btv->video_dev = NULL;
- }
- if (btv->vbi_dev) {
- if (video_is_registered(btv->vbi_dev))
- video_unregister_device(btv->vbi_dev);
- else
- video_device_release(btv->vbi_dev);
- btv->vbi_dev = NULL;
- }
- if (btv->radio_dev) {
- if (video_is_registered(btv->radio_dev))
- video_unregister_device(btv->radio_dev);
- else
- video_device_release(btv->radio_dev);
- btv->radio_dev = NULL;
- }
-}
-
-/* register video4linux devices */
-static int __devinit bttv_register_video(struct bttv *btv)
-{
- if (no_overlay > 0)
- pr_notice("Overlay support disabled\n");
-
- /* video */
- btv->video_dev = vdev_init(btv, &bttv_video_template, "video");
-
- if (NULL == btv->video_dev)
- goto err;
- if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER,
- video_nr[btv->c.nr]) < 0)
- goto err;
- pr_info("%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->video_dev));
- if (device_create_file(&btv->video_dev->dev,
- &dev_attr_card)<0) {
- pr_err("%d: device_create_file 'card' failed\n", btv->c.nr);
- goto err;
- }
-
- /* vbi */
- btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi");
-
- if (NULL == btv->vbi_dev)
- goto err;
- if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[btv->c.nr]) < 0)
- goto err;
- pr_info("%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->vbi_dev));
-
- if (!btv->has_radio)
- return 0;
- /* radio */
- btv->radio_dev = vdev_init(btv, &radio_template, "radio");
- if (NULL == btv->radio_dev)
- goto err;
- if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,
- radio_nr[btv->c.nr]) < 0)
- goto err;
- pr_info("%d: registered device %s\n",
- btv->c.nr, video_device_node_name(btv->radio_dev));
-
- /* all done */
- return 0;
-
- err:
- bttv_unregister_video(btv);
- return -1;
-}
-
-
-/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
-/* response on cards with no firmware is not enabled by OF */
-static void pci_set_command(struct pci_dev *dev)
-{
-#if defined(__powerpc__)
- unsigned int cmd;
-
- pci_read_config_dword(dev, PCI_COMMAND, &cmd);
- cmd = (cmd | PCI_COMMAND_MEMORY );
- pci_write_config_dword(dev, PCI_COMMAND, cmd);
-#endif
-}
-
-static int __devinit bttv_probe(struct pci_dev *dev,
- const struct pci_device_id *pci_id)
-{
- int result;
- unsigned char lat;
- struct bttv *btv;
-
- if (bttv_num == BTTV_MAX)
- return -ENOMEM;
- pr_info("Bt8xx card found (%d)\n", bttv_num);
- bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
- if (btv == NULL) {
- pr_err("out of memory\n");
- return -ENOMEM;
- }
- btv->c.nr = bttv_num;
- snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
- "bttv%d", btv->c.nr);
-
- /* initialize structs / fill in defaults */
- mutex_init(&btv->lock);
- spin_lock_init(&btv->s_lock);
- spin_lock_init(&btv->gpio_lock);
- init_waitqueue_head(&btv->i2c_queue);
- INIT_LIST_HEAD(&btv->c.subs);
- INIT_LIST_HEAD(&btv->capture);
- INIT_LIST_HEAD(&btv->vcapture);
- v4l2_prio_init(&btv->prio);
-
- init_timer(&btv->timeout);
- btv->timeout.function = bttv_irq_timeout;
- btv->timeout.data = (unsigned long)btv;
-
- btv->i2c_rc = -1;
- btv->tuner_type = UNSET;
- btv->new_input = UNSET;
- btv->has_radio=radio[btv->c.nr];
-
- /* pci stuff (init, get irq/mmio, ... */
- btv->c.pci = dev;
- btv->id = dev->device;
- if (pci_enable_device(dev)) {
- pr_warn("%d: Can't enable device\n", btv->c.nr);
- return -EIO;
- }
- if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
- pr_warn("%d: No suitable DMA available\n", btv->c.nr);
- return -EIO;
- }
- if (!request_mem_region(pci_resource_start(dev,0),
- pci_resource_len(dev,0),
- btv->c.v4l2_dev.name)) {
- pr_warn("%d: can't request iomem (0x%llx)\n",
- btv->c.nr,
- (unsigned long long)pci_resource_start(dev, 0));
- return -EBUSY;
- }
- pci_set_master(dev);
- pci_set_command(dev);
-
- result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
- if (result < 0) {
- pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr);
- goto fail0;
- }
-
- btv->revision = dev->revision;
- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
- pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n",
- bttv_num, btv->id, btv->revision, pci_name(dev),
- btv->c.pci->irq, lat,
- (unsigned long long)pci_resource_start(dev, 0));
- schedule();
-
- btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000);
- if (NULL == btv->bt848_mmio) {
- pr_err("%d: ioremap() failed\n", btv->c.nr);
- result = -EIO;
- goto fail1;
- }
-
- /* identify card */
- bttv_idcard(btv);
-
- /* disable irqs, register irq handler */
- btwrite(0, BT848_INT_MASK);
- result = request_irq(btv->c.pci->irq, bttv_irq,
- IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
- if (result < 0) {
- pr_err("%d: can't get IRQ %d\n",
- bttv_num, btv->c.pci->irq);
- goto fail1;
- }
-
- if (0 != bttv_handle_chipset(btv)) {
- result = -EIO;
- goto fail2;
- }
-
- /* init options from insmod args */
- btv->opt_combfilter = combfilter;
- btv->opt_lumafilter = lumafilter;
- btv->opt_automute = automute;
- btv->opt_chroma_agc = chroma_agc;
- btv->opt_adc_crush = adc_crush;
- btv->opt_vcr_hack = vcr_hack;
- btv->opt_whitecrush_upper = whitecrush_upper;
- btv->opt_whitecrush_lower = whitecrush_lower;
- btv->opt_uv_ratio = uv_ratio;
- btv->opt_full_luma_range = full_luma_range;
- btv->opt_coring = coring;
-
- /* fill struct bttv with some useful defaults */
- btv->init.btv = btv;
- btv->init.ov.w.width = 320;
- btv->init.ov.w.height = 240;
- btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
- btv->init.width = 320;
- btv->init.height = 240;
- btv->input = 0;
-
- /* initialize hardware */
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"pre-init");
-
- bttv_risc_init_main(btv);
- init_bt848(btv);
-
- /* gpio */
- btwrite(0x00, BT848_GPIO_REG_INP);
- btwrite(0x00, BT848_GPIO_OUT_EN);
- if (bttv_verbose)
- bttv_gpio_tracking(btv,"init");
-
- /* needs to be done before i2c is registered */
- bttv_init_card1(btv);
-
- /* register i2c + gpio */
- init_bttv_i2c(btv);
-
- /* some card-specific stuff (needs working i2c) */
- bttv_init_card2(btv);
- bttv_init_tuner(btv);
- init_irqreg(btv);
-
- /* register video4linux + input */
- if (!bttv_tvcards[btv->c.type].no_video) {
- bttv_register_video(btv);
- bt848_bright(btv,32768);
- bt848_contrast(btv, 27648);
- bt848_hue(btv,32768);
- bt848_sat(btv,32768);
- audio_mute(btv, 1);
- set_input(btv, 0, btv->tvnorm);
- bttv_crop_reset(&btv->crop[0], btv->tvnorm);
- btv->crop[1] = btv->crop[0]; /* current = default */
- disclaim_vbi_lines(btv);
- disclaim_video_lines(btv);
- }
-
- /* add subdevices and autoload dvb-bt8xx if needed */
- if (bttv_tvcards[btv->c.type].has_dvb) {
- bttv_sub_add_device(&btv->c, "dvb");
- request_modules(btv);
- }
-
- if (!disable_ir) {
- init_bttv_i2c_ir(btv);
- bttv_input_init(btv);
- }
-
- /* everything is fine */
- bttv_num++;
- return 0;
-
-fail2:
- free_irq(btv->c.pci->irq,btv);
-
-fail1:
- v4l2_device_unregister(&btv->c.v4l2_dev);
-
-fail0:
- if (btv->bt848_mmio)
- iounmap(btv->bt848_mmio);
- release_mem_region(pci_resource_start(btv->c.pci,0),
- pci_resource_len(btv->c.pci,0));
- return result;
-}
-
-static void __devexit bttv_remove(struct pci_dev *pci_dev)
-{
- struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
- struct bttv *btv = to_bttv(v4l2_dev);
-
- if (bttv_verbose)
- pr_info("%d: unloading\n", btv->c.nr);
-
- if (bttv_tvcards[btv->c.type].has_dvb)
- flush_request_modules(btv);
-
- /* shutdown everything (DMA+IRQs) */
- btand(~15, BT848_GPIO_DMA_CTL);
- btwrite(0, BT848_INT_MASK);
- btwrite(~0x0, BT848_INT_STAT);
- btwrite(0x0, BT848_GPIO_OUT_EN);
- if (bttv_gpio)
- bttv_gpio_tracking(btv,"cleanup");
-
- /* tell gpio modules we are leaving ... */
- btv->shutdown=1;
- bttv_input_fini(btv);
- bttv_sub_del_devices(&btv->c);
-
- /* unregister i2c_bus + input */
- fini_bttv_i2c(btv);
-
- /* unregister video4linux */
- bttv_unregister_video(btv);
-
- /* free allocated memory */
- btcx_riscmem_free(btv->c.pci,&btv->main);
-
- /* free ressources */
- free_irq(btv->c.pci->irq,btv);
- iounmap(btv->bt848_mmio);
- release_mem_region(pci_resource_start(btv->c.pci,0),
- pci_resource_len(btv->c.pci,0));
-
- v4l2_device_unregister(&btv->c.v4l2_dev);
- bttvs[btv->c.nr] = NULL;
- kfree(btv);
-
- return;
-}
-
-#ifdef CONFIG_PM
-static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
-{
- struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
- struct bttv *btv = to_bttv(v4l2_dev);
- struct bttv_buffer_set idle;
- unsigned long flags;
-
- dprintk("%d: suspend %d\n", btv->c.nr, state.event);
-
- /* stop dma + irqs */
- spin_lock_irqsave(&btv->s_lock,flags);
- memset(&idle, 0, sizeof(idle));
- btv->state.video = btv->curr;
- btv->state.vbi = btv->cvbi;
- btv->state.loop_irq = btv->loop_irq;
- btv->curr = idle;
- btv->loop_irq = 0;
- bttv_buffer_activate_video(btv, &idle);
- bttv_buffer_activate_vbi(btv, NULL);
- bttv_set_dma(btv, 0);
- btwrite(0, BT848_INT_MASK);
- spin_unlock_irqrestore(&btv->s_lock,flags);
-
- /* save bt878 state */
- btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN);
- btv->state.gpio_data = gpio_read();
-
- /* save pci state */
- pci_save_state(pci_dev);
- if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
- pci_disable_device(pci_dev);
- btv->state.disabled = 1;
- }
- return 0;
-}
-
-static int bttv_resume(struct pci_dev *pci_dev)
-{
- struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
- struct bttv *btv = to_bttv(v4l2_dev);
- unsigned long flags;
- int err;
-
- dprintk("%d: resume\n", btv->c.nr);
-
- /* restore pci state */
- if (btv->state.disabled) {
- err=pci_enable_device(pci_dev);
- if (err) {
- pr_warn("%d: Can't enable device\n", btv->c.nr);
- return err;
- }
- btv->state.disabled = 0;
- }
- err=pci_set_power_state(pci_dev, PCI_D0);
- if (err) {
- pci_disable_device(pci_dev);
- pr_warn("%d: Can't enable device\n", btv->c.nr);
- btv->state.disabled = 1;
- return err;
- }
-
- pci_restore_state(pci_dev);
-
- /* restore bt878 state */
- bttv_reinit_bt848(btv);
- gpio_inout(0xffffff, btv->state.gpio_enable);
- gpio_write(btv->state.gpio_data);
-
- /* restart dma */
- spin_lock_irqsave(&btv->s_lock,flags);
- btv->curr = btv->state.video;
- btv->cvbi = btv->state.vbi;
- btv->loop_irq = btv->state.loop_irq;
- bttv_buffer_activate_video(btv, &btv->curr);
- bttv_buffer_activate_vbi(btv, btv->cvbi);
- bttv_set_dma(btv, 0);
- spin_unlock_irqrestore(&btv->s_lock,flags);
- return 0;
-}
-#endif
-
-static struct pci_device_id bttv_pci_tbl[] = {
- {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0},
- {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0},
- {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0},
- {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0},
- {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
-
-static struct pci_driver bttv_pci_driver = {
- .name = "bttv",
- .id_table = bttv_pci_tbl,
- .probe = bttv_probe,
- .remove = __devexit_p(bttv_remove),
-#ifdef CONFIG_PM
- .suspend = bttv_suspend,
- .resume = bttv_resume,
-#endif
-};
-
-static int __init bttv_init_module(void)
-{
- int ret;
-
- bttv_num = 0;
-
- pr_info("driver version %s loaded\n", BTTV_VERSION);
- if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
- gbuffers = 2;
- if (gbufsize > BTTV_MAX_FBUF)
- gbufsize = BTTV_MAX_FBUF;
- gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
- if (bttv_verbose)
- pr_info("using %d buffers with %dk (%d pages) each for capture\n",
- gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
-
- bttv_check_chipset();
-
- ret = bus_register(&bttv_sub_bus_type);
- if (ret < 0) {
- pr_warn("bus_register error: %d\n", ret);
- return ret;
- }
- ret = pci_register_driver(&bttv_pci_driver);
- if (ret < 0)
- bus_unregister(&bttv_sub_bus_type);
-
- return ret;
-}
-
-static void __exit bttv_cleanup_module(void)
-{
- pci_unregister_driver(&bttv_pci_driver);
- bus_unregister(&bttv_sub_bus_type);
-}
-
-module_init(bttv_init_module);
-module_exit(bttv_cleanup_module);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
deleted file mode 100644
index 17188e234770..000000000000
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ /dev/null
@@ -1,2413 +0,0 @@
-/****************************************************************************
- *
- * Filename: cpia2_core.c
- *
- * Copyright 2001, STMicrolectronics, Inc.
- * Contact: steve.miller@st.com
- *
- * Description:
- * This is a USB driver for CPia2 based video cameras.
- * The infrastructure of this driver is based on the cpia usb driver by
- * Jochen Scharrlach and Johannes Erdfeldt.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- *
- * Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- ****************************************************************************/
-
-#include "cpia2.h"
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/firmware.h>
-
-/* #define _CPIA2_DEBUG_ */
-
-#ifdef _CPIA2_DEBUG_
-
-static const char *block_name[] = {
- "System",
- "VC",
- "VP",
- "IDATA"
-};
-#endif
-
-static unsigned int debugs_on; /* default 0 - DEBUG_REG */
-
-
-/******************************************************************************
- *
- * Forward Declarations
- *
- *****************************************************************************/
-static int apply_vp_patch(struct camera_data *cam);
-static int set_default_user_mode(struct camera_data *cam);
-static int set_vw_size(struct camera_data *cam, int size);
-static int configure_sensor(struct camera_data *cam,
- int reqwidth, int reqheight);
-static int config_sensor_410(struct camera_data *cam,
- int reqwidth, int reqheight);
-static int config_sensor_500(struct camera_data *cam,
- int reqwidth, int reqheight);
-static int set_all_properties(struct camera_data *cam);
-static void wake_system(struct camera_data *cam);
-static void set_lowlight_boost(struct camera_data *cam);
-static void reset_camera_struct(struct camera_data *cam);
-static int cpia2_set_high_power(struct camera_data *cam);
-
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the
- * area and marking the pages as reserved.
- */
-static inline unsigned long kvirt_to_pa(unsigned long adr)
-{
- unsigned long kva, ret;
-
- kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
- kva |= adr & (PAGE_SIZE-1); /* restore the offset */
- ret = __pa(kva);
- return ret;
-}
-
-static void *rvmalloc(unsigned long size)
-{
- void *mem;
- unsigned long adr;
-
- /* Round it off to PAGE_SIZE */
- 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 ((long)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;
-
- size = PAGE_ALIGN(size);
-
- adr = (unsigned long) mem;
- while ((long)size > 0) {
- ClearPageReserved(vmalloc_to_page((void *)adr));
- adr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vfree(mem);
-}
-
-/******************************************************************************
- *
- * cpia2_do_command
- *
- * Send an arbitrary command to the camera. For commands that read from
- * the camera, copy the buffers into the proper param structures.
- *****************************************************************************/
-int cpia2_do_command(struct camera_data *cam,
- u32 command, u8 direction, u8 param)
-{
- int retval = 0;
- struct cpia2_command cmd;
- unsigned int device = cam->params.pnp_id.device_type;
-
- cmd.command = command;
- cmd.reg_count = 2; /* default */
- cmd.direction = direction;
-
- /***
- * Set up the command.
- ***/
- switch (command) {
- case CPIA2_CMD_GET_VERSION:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.start = CPIA2_SYSTEM_DEVICE_HI;
- break;
- case CPIA2_CMD_GET_PNP_ID:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 8;
- cmd.start = CPIA2_SYSTEM_DESCRIP_VID_HI;
- break;
- case CPIA2_CMD_GET_ASIC_TYPE:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.start = CPIA2_VC_ASIC_ID;
- break;
- case CPIA2_CMD_GET_SENSOR:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.start = CPIA2_VP_SENSOR_FLAGS;
- break;
- case CPIA2_CMD_GET_VP_DEVICE:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.start = CPIA2_VP_DEVICEH;
- break;
- case CPIA2_CMD_SET_VP_BRIGHTNESS:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_BRIGHTNESS:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- if (device == DEVICE_STV_672)
- cmd.start = CPIA2_VP4_EXPOSURE_TARGET;
- else
- cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
- break;
- case CPIA2_CMD_SET_CONTRAST:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_CONTRAST:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_YRANGE;
- break;
- case CPIA2_CMD_SET_VP_SATURATION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_SATURATION:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- if (device == DEVICE_STV_672)
- cmd.start = CPIA2_VP_SATURATION;
- else
- cmd.start = CPIA2_VP5_MCUVSATURATION;
- break;
- case CPIA2_CMD_SET_VP_GPIO_DATA:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_GPIO_DATA:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_GPIO_DATA;
- break;
- case CPIA2_CMD_SET_VP_GPIO_DIRECTION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_GPIO_DIRECTION;
- break;
- case CPIA2_CMD_SET_VC_MP_GPIO_DATA:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VC_MP_DATA;
- break;
- case CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VC_MP_DIR;
- break;
- case CPIA2_CMD_ENABLE_PACKET_CTRL:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.start = CPIA2_SYSTEM_INT_PACKET_CTRL;
- cmd.reg_count = 1;
- cmd.buffer.block_data[0] = param;
- break;
- case CPIA2_CMD_SET_FLICKER_MODES:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_FLICKER_MODES:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_FLICKER_MODES;
- break;
- case CPIA2_CMD_RESET_FIFO: /* clear fifo and enable stream block */
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.reg_count = 2;
- cmd.start = 0;
- cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
- cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
- CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
- cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
- cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
- CPIA2_VC_ST_CTRL_DST_USB |
- CPIA2_VC_ST_CTRL_EOF_DETECT |
- CPIA2_VC_ST_CTRL_FIFO_ENABLE;
- break;
- case CPIA2_CMD_SET_HI_POWER:
- cmd.req_mode =
- CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 2;
- cmd.buffer.registers[0].index =
- CPIA2_SYSTEM_SYSTEM_CONTROL;
- cmd.buffer.registers[1].index =
- CPIA2_SYSTEM_SYSTEM_CONTROL;
- cmd.buffer.registers[0].value = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
- cmd.buffer.registers[1].value =
- CPIA2_SYSTEM_CONTROL_HIGH_POWER;
- break;
- case CPIA2_CMD_SET_LOW_POWER:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 1;
- cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
- cmd.buffer.block_data[0] = 0;
- break;
- case CPIA2_CMD_CLEAR_V2W_ERR:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 1;
- cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
- cmd.buffer.block_data[0] = CPIA2_SYSTEM_CONTROL_CLEAR_ERR;
- break;
- case CPIA2_CMD_SET_USER_MODE: /* Then fall through */
- cmd.buffer.block_data[0] = param;
- case CPIA2_CMD_GET_USER_MODE:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- if (device == DEVICE_STV_672)
- cmd.start = CPIA2_VP4_USER_MODE;
- else
- cmd.start = CPIA2_VP5_USER_MODE;
- break;
- case CPIA2_CMD_FRAMERATE_REQ:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- if (device == DEVICE_STV_672)
- cmd.start = CPIA2_VP4_FRAMERATE_REQUEST;
- else
- cmd.start = CPIA2_VP5_FRAMERATE_REQUEST;
- cmd.buffer.block_data[0] = param;
- break;
- case CPIA2_CMD_SET_WAKEUP:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_WAKEUP:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VC_WAKEUP;
- break;
- case CPIA2_CMD_SET_PW_CONTROL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_PW_CONTROL:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VC_PW_CTRL;
- break;
- case CPIA2_CMD_GET_VP_SYSTEM_STATE:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_SYSTEMSTATE;
- break;
- case CPIA2_CMD_SET_SYSTEM_CTRL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_SYSTEM_CTRL:
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 1;
- cmd.start = CPIA2_SYSTEM_SYSTEM_CONTROL;
- break;
- case CPIA2_CMD_SET_VP_SYSTEM_CTRL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_SYSTEMCTRL;
- break;
- case CPIA2_CMD_SET_VP_EXP_MODES:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VP_EXP_MODES:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_EXPOSURE_MODES;
- break;
- case CPIA2_CMD_SET_DEVICE_CONFIG:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_DEVICE_CONFIG:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_DEVICE_CONFIG;
- break;
- case CPIA2_CMD_SET_SERIAL_ADDR:
- cmd.buffer.block_data[0] = param;
- cmd.req_mode =
- CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 1;
- cmd.start = CPIA2_SYSTEM_VP_SERIAL_ADDR;
- break;
- case CPIA2_CMD_SET_SENSOR_CR1:
- cmd.buffer.block_data[0] = param;
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_SENSOR_CR1;
- break;
- case CPIA2_CMD_SET_VC_CONTROL:
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_VC_CONTROL:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VC_VC_CTRL;
- break;
- case CPIA2_CMD_SET_TARGET_KB:
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.reg_count = 1;
- cmd.buffer.registers[0].index = CPIA2_VC_VC_TARGET_KB;
- cmd.buffer.registers[0].value = param;
- break;
- case CPIA2_CMD_SET_DEF_JPEG_OPT:
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.reg_count = 4;
- cmd.buffer.registers[0].index = CPIA2_VC_VC_JPEG_OPT;
- cmd.buffer.registers[0].value =
- CPIA2_VC_VC_JPEG_OPT_DOUBLE_SQUEEZE;
- cmd.buffer.registers[1].index = CPIA2_VC_VC_USER_SQUEEZE;
- cmd.buffer.registers[1].value = 20;
- cmd.buffer.registers[2].index = CPIA2_VC_VC_CREEP_PERIOD;
- cmd.buffer.registers[2].value = 2;
- cmd.buffer.registers[3].index = CPIA2_VC_VC_JPEG_OPT;
- cmd.buffer.registers[3].value = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
- break;
- case CPIA2_CMD_REHASH_VP4:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP_REHASH_VALUES;
- cmd.buffer.block_data[0] = param;
- break;
- case CPIA2_CMD_SET_USER_EFFECTS: /* Note: Be careful with this as
- this register can also affect
- flicker modes */
- cmd.buffer.block_data[0] = param; /* Then fall through */
- case CPIA2_CMD_GET_USER_EFFECTS:
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 1;
- if (device == DEVICE_STV_672)
- cmd.start = CPIA2_VP4_USER_EFFECTS;
- else
- cmd.start = CPIA2_VP5_USER_EFFECTS;
- break;
- default:
- LOG("DoCommand received invalid command\n");
- return -EINVAL;
- }
-
- retval = cpia2_send_command(cam, &cmd);
- if (retval) {
- return retval;
- }
-
- /***
- * Now copy any results from a read into the appropriate param struct.
- ***/
- switch (command) {
- case CPIA2_CMD_GET_VERSION:
- cam->params.version.firmware_revision_hi =
- cmd.buffer.block_data[0];
- cam->params.version.firmware_revision_lo =
- cmd.buffer.block_data[1];
- break;
- case CPIA2_CMD_GET_PNP_ID:
- cam->params.pnp_id.vendor = (cmd.buffer.block_data[0] << 8) |
- cmd.buffer.block_data[1];
- cam->params.pnp_id.product = (cmd.buffer.block_data[2] << 8) |
- cmd.buffer.block_data[3];
- cam->params.pnp_id.device_revision =
- (cmd.buffer.block_data[4] << 8) |
- cmd.buffer.block_data[5];
- if (cam->params.pnp_id.vendor == 0x553) {
- if (cam->params.pnp_id.product == 0x100) {
- cam->params.pnp_id.device_type = DEVICE_STV_672;
- } else if (cam->params.pnp_id.product == 0x140 ||
- cam->params.pnp_id.product == 0x151) {
- cam->params.pnp_id.device_type = DEVICE_STV_676;
- }
- }
- break;
- case CPIA2_CMD_GET_ASIC_TYPE:
- cam->params.version.asic_id = cmd.buffer.block_data[0];
- cam->params.version.asic_rev = cmd.buffer.block_data[1];
- break;
- case CPIA2_CMD_GET_SENSOR:
- cam->params.version.sensor_flags = cmd.buffer.block_data[0];
- cam->params.version.sensor_rev = cmd.buffer.block_data[1];
- break;
- case CPIA2_CMD_GET_VP_DEVICE:
- cam->params.version.vp_device_hi = cmd.buffer.block_data[0];
- cam->params.version.vp_device_lo = cmd.buffer.block_data[1];
- break;
- case CPIA2_CMD_GET_VP_GPIO_DATA:
- cam->params.vp_params.gpio_data = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VP_GPIO_DIRECTION:
- cam->params.vp_params.gpio_direction = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION:
- cam->params.vc_params.vc_mp_direction =cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VC_MP_GPIO_DATA:
- cam->params.vc_params.vc_mp_data = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_FLICKER_MODES:
- cam->params.flicker_control.cam_register =
- cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_WAKEUP:
- cam->params.vc_params.wakeup = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_PW_CONTROL:
- cam->params.vc_params.pw_control = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_SYSTEM_CTRL:
- cam->params.camera_state.system_ctrl = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VP_SYSTEM_STATE:
- cam->params.vp_params.system_state = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VP_SYSTEM_CTRL:
- cam->params.vp_params.system_ctrl = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VP_EXP_MODES:
- cam->params.vp_params.exposure_modes = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_DEVICE_CONFIG:
- cam->params.vp_params.device_config = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VC_CONTROL:
- cam->params.vc_params.vc_control = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_USER_MODE:
- cam->params.vp_params.video_mode = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_USER_EFFECTS:
- cam->params.vp_params.user_effects = cmd.buffer.block_data[0];
- break;
- default:
- break;
- }
- return retval;
-}
-
-/******************************************************************************
- *
- * cpia2_send_command
- *
- *****************************************************************************/
-
-#define DIR(cmd) ((cmd->direction == TRANSFER_WRITE) ? "Write" : "Read")
-#define BINDEX(cmd) (cmd->req_mode & 0x03)
-
-int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd)
-{
- u8 count;
- u8 start;
- u8 *buffer;
- int retval;
-
- switch (cmd->req_mode & 0x0c) {
- case CAMERAACCESS_TYPE_RANDOM:
- count = cmd->reg_count * sizeof(struct cpia2_register);
- start = 0;
- buffer = (u8 *) & cmd->buffer;
- if (debugs_on & DEBUG_REG)
- DBG("%s Random: Register block %s\n", DIR(cmd),
- block_name[BINDEX(cmd)]);
- break;
- case CAMERAACCESS_TYPE_BLOCK:
- count = cmd->reg_count;
- start = cmd->start;
- buffer = cmd->buffer.block_data;
- if (debugs_on & DEBUG_REG)
- DBG("%s Block: Register block %s\n", DIR(cmd),
- block_name[BINDEX(cmd)]);
- break;
- case CAMERAACCESS_TYPE_MASK:
- count = cmd->reg_count * sizeof(struct cpia2_reg_mask);
- start = 0;
- buffer = (u8 *) & cmd->buffer;
- if (debugs_on & DEBUG_REG)
- DBG("%s Mask: Register block %s\n", DIR(cmd),
- block_name[BINDEX(cmd)]);
- break;
- case CAMERAACCESS_TYPE_REPEAT: /* For patch blocks only */
- count = cmd->reg_count;
- start = cmd->start;
- buffer = cmd->buffer.block_data;
- if (debugs_on & DEBUG_REG)
- DBG("%s Repeat: Register block %s\n", DIR(cmd),
- block_name[BINDEX(cmd)]);
- break;
- default:
- LOG("%s: invalid request mode\n",__func__);
- return -EINVAL;
- }
-
- retval = cpia2_usb_transfer_cmd(cam,
- buffer,
- cmd->req_mode,
- start, count, cmd->direction);
-#ifdef _CPIA2_DEBUG_
- if (debugs_on & DEBUG_REG) {
- int i;
- for (i = 0; i < cmd->reg_count; i++) {
- if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_BLOCK)
- KINFO("%s Block: [0x%02X] = 0x%02X\n",
- DIR(cmd), start + i, buffer[i]);
- if((cmd->req_mode & 0x0c) == CAMERAACCESS_TYPE_RANDOM)
- KINFO("%s Random: [0x%02X] = 0x%02X\n",
- DIR(cmd), cmd->buffer.registers[i].index,
- cmd->buffer.registers[i].value);
- }
- }
-#endif
-
- return retval;
-};
-
-/*************
- * Functions to implement camera functionality
- *************/
-/******************************************************************************
- *
- * cpia2_get_version_info
- *
- *****************************************************************************/
-static void cpia2_get_version_info(struct camera_data *cam)
-{
- cpia2_do_command(cam, CPIA2_CMD_GET_VERSION, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_PNP_ID, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_ASIC_TYPE, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_SENSOR, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_DEVICE, TRANSFER_READ, 0);
-}
-
-/******************************************************************************
- *
- * cpia2_reset_camera
- *
- * Called at least during the open process, sets up initial params.
- *****************************************************************************/
-int cpia2_reset_camera(struct camera_data *cam)
-{
- u8 tmp_reg;
- int retval = 0;
- int target_kb;
- int i;
- struct cpia2_command cmd;
-
- /***
- * VC setup
- ***/
- retval = configure_sensor(cam,
- cam->params.roi.width,
- cam->params.roi.height);
- if (retval < 0) {
- ERR("Couldn't configure sensor, error=%d\n", retval);
- return retval;
- }
-
- /* Clear FIFO and route/enable stream block */
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.direction = TRANSFER_WRITE;
- cmd.reg_count = 2;
- cmd.buffer.registers[0].index = CPIA2_VC_ST_CTRL;
- cmd.buffer.registers[0].value = CPIA2_VC_ST_CTRL_SRC_VC |
- CPIA2_VC_ST_CTRL_DST_USB | CPIA2_VC_ST_CTRL_EOF_DETECT;
- cmd.buffer.registers[1].index = CPIA2_VC_ST_CTRL;
- cmd.buffer.registers[1].value = CPIA2_VC_ST_CTRL_SRC_VC |
- CPIA2_VC_ST_CTRL_DST_USB |
- CPIA2_VC_ST_CTRL_EOF_DETECT | CPIA2_VC_ST_CTRL_FIFO_ENABLE;
-
- cpia2_send_command(cam, &cmd);
-
- cpia2_set_high_power(cam);
-
- if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
- /* Enable button notification */
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_SYSTEM;
- cmd.buffer.registers[0].index = CPIA2_SYSTEM_INT_PACKET_CTRL;
- cmd.buffer.registers[0].value =
- CPIA2_SYSTEM_INT_PACKET_CTRL_ENABLE_SW_XX;
- cmd.reg_count = 1;
- cpia2_send_command(cam, &cmd);
- }
-
- schedule_timeout_interruptible(msecs_to_jiffies(100));
-
- if (cam->params.pnp_id.device_type == DEVICE_STV_672)
- retval = apply_vp_patch(cam);
-
- /* wait for vp to go to sleep */
- schedule_timeout_interruptible(msecs_to_jiffies(100));
-
- /***
- * If this is a 676, apply VP5 fixes before we start streaming
- ***/
- if (cam->params.pnp_id.device_type == DEVICE_STV_676) {
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
-
- /* The following writes improve the picture */
- cmd.buffer.registers[0].index = CPIA2_VP5_MYBLACK_LEVEL;
- cmd.buffer.registers[0].value = 0; /* reduce from the default
- * rec 601 pedestal of 16 */
- cmd.buffer.registers[1].index = CPIA2_VP5_MCYRANGE;
- cmd.buffer.registers[1].value = 0x92; /* increase from 100% to
- * (256/256 - 31) to fill
- * available range */
- cmd.buffer.registers[2].index = CPIA2_VP5_MYCEILING;
- cmd.buffer.registers[2].value = 0xFF; /* Increase from the
- * default rec 601 ceiling
- * of 240 */
- cmd.buffer.registers[3].index = CPIA2_VP5_MCUVSATURATION;
- cmd.buffer.registers[3].value = 0xFF; /* Increase from the rec
- * 601 100% level (128)
- * to 145-192 */
- cmd.buffer.registers[4].index = CPIA2_VP5_ANTIFLKRSETUP;
- cmd.buffer.registers[4].value = 0x80; /* Inhibit the
- * anti-flicker */
-
- /* The following 4 writes are a fix to allow QVGA to work at 30 fps */
- cmd.buffer.registers[5].index = CPIA2_VP_RAM_ADDR_H;
- cmd.buffer.registers[5].value = 0x01;
- cmd.buffer.registers[6].index = CPIA2_VP_RAM_ADDR_L;
- cmd.buffer.registers[6].value = 0xE3;
- cmd.buffer.registers[7].index = CPIA2_VP_RAM_DATA;
- cmd.buffer.registers[7].value = 0x02;
- cmd.buffer.registers[8].index = CPIA2_VP_RAM_DATA;
- cmd.buffer.registers[8].value = 0xFC;
-
- cmd.direction = TRANSFER_WRITE;
- cmd.reg_count = 9;
-
- cpia2_send_command(cam, &cmd);
- }
-
- /* Activate all settings and start the data stream */
- /* Set user mode */
- set_default_user_mode(cam);
-
- /* Give VP time to wake up */
- schedule_timeout_interruptible(msecs_to_jiffies(100));
-
- set_all_properties(cam);
-
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
- DBG("After SetAllProperties(cam), user mode is 0x%0X\n",
- cam->params.vp_params.video_mode);
-
- /***
- * Set audio regulator off. This and the code to set the compresison
- * state are too complex to form a CPIA2_CMD_, and seem to be somewhat
- * intertwined. This stuff came straight from the windows driver.
- ***/
- /* Turn AutoExposure off in VP and enable the serial bridge to the sensor */
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
- tmp_reg = cam->params.vp_params.system_ctrl;
- cmd.buffer.registers[0].value = tmp_reg &
- (tmp_reg & (CPIA2_VP_SYSTEMCTRL_HK_CONTROL ^ 0xFF));
-
- cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
- cmd.buffer.registers[1].value = cam->params.vp_params.device_config |
- CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE;
- cmd.buffer.registers[0].index = CPIA2_VP_SYSTEMCTRL;
- cmd.buffer.registers[1].index = CPIA2_VP_DEVICE_CONFIG;
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
- cmd.reg_count = 2;
- cmd.direction = TRANSFER_WRITE;
- cmd.start = 0;
- cpia2_send_command(cam, &cmd);
-
- /* Set the correct I2C address in the CPiA-2 system register */
- cpia2_do_command(cam,
- CPIA2_CMD_SET_SERIAL_ADDR,
- TRANSFER_WRITE,
- CPIA2_SYSTEM_VP_SERIAL_ADDR_SENSOR);
-
- /* Now have sensor access - set bit to turn the audio regulator off */
- cpia2_do_command(cam,
- CPIA2_CMD_SET_SENSOR_CR1,
- TRANSFER_WRITE, CPIA2_SENSOR_CR1_DOWN_AUDIO_REGULATOR);
-
- /* Set the correct I2C address in the CPiA-2 system register */
- if (cam->params.pnp_id.device_type == DEVICE_STV_672)
- cpia2_do_command(cam,
- CPIA2_CMD_SET_SERIAL_ADDR,
- TRANSFER_WRITE,
- CPIA2_SYSTEM_VP_SERIAL_ADDR_VP); // 0x88
- else
- cpia2_do_command(cam,
- CPIA2_CMD_SET_SERIAL_ADDR,
- TRANSFER_WRITE,
- CPIA2_SYSTEM_VP_SERIAL_ADDR_676_VP); // 0x8a
-
- /* increase signal drive strength */
- if (cam->params.pnp_id.device_type == DEVICE_STV_676)
- cpia2_do_command(cam,
- CPIA2_CMD_SET_VP_EXP_MODES,
- TRANSFER_WRITE,
- CPIA2_VP_EXPOSURE_MODES_COMPILE_EXP);
-
- /* Start autoexposure */
- cpia2_do_command(cam, CPIA2_CMD_GET_DEVICE_CONFIG, TRANSFER_READ, 0);
- cmd.buffer.registers[0].value = cam->params.vp_params.device_config &
- (CPIA2_VP_DEVICE_CONFIG_SERIAL_BRIDGE ^ 0xFF);
-
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_CTRL, TRANSFER_READ, 0);
- cmd.buffer.registers[1].value =
- cam->params.vp_params.system_ctrl | CPIA2_VP_SYSTEMCTRL_HK_CONTROL;
-
- cmd.buffer.registers[0].index = CPIA2_VP_DEVICE_CONFIG;
- cmd.buffer.registers[1].index = CPIA2_VP_SYSTEMCTRL;
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VP;
- cmd.reg_count = 2;
- cmd.direction = TRANSFER_WRITE;
-
- cpia2_send_command(cam, &cmd);
-
- /* Set compression state */
- cpia2_do_command(cam, CPIA2_CMD_GET_VC_CONTROL, TRANSFER_READ, 0);
- if (cam->params.compression.inhibit_htables) {
- tmp_reg = cam->params.vc_params.vc_control |
- CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
- } else {
- tmp_reg = cam->params.vc_params.vc_control &
- ~CPIA2_VC_VC_CTRL_INHIBIT_H_TABLES;
- }
- cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg);
-
- /* Set target size (kb) on vc
- This is a heuristic based on the quality parameter and the raw
- framesize in kB divided by 16 (the compression factor when the
- quality is 100%) */
- target_kb = (cam->width * cam->height * 2 / 16384) *
- cam->params.vc_params.quality / 100;
- if (target_kb < 1)
- target_kb = 1;
- cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB,
- TRANSFER_WRITE, target_kb);
-
- /* Wiggle VC Reset */
- /***
- * First read and wait a bit.
- ***/
- for (i = 0; i < 50; i++) {
- cpia2_do_command(cam, CPIA2_CMD_GET_PW_CONTROL,
- TRANSFER_READ, 0);
- }
-
- tmp_reg = cam->params.vc_params.pw_control;
- tmp_reg &= ~CPIA2_VC_PW_CTRL_VC_RESET_N;
-
- cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
-
- tmp_reg |= CPIA2_VC_PW_CTRL_VC_RESET_N;
- cpia2_do_command(cam, CPIA2_CMD_SET_PW_CONTROL, TRANSFER_WRITE,tmp_reg);
-
- cpia2_do_command(cam, CPIA2_CMD_SET_DEF_JPEG_OPT, TRANSFER_WRITE, 0);
-
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_MODE, TRANSFER_READ, 0);
- DBG("After VC RESET, user mode is 0x%0X\n",
- cam->params.vp_params.video_mode);
-
- return retval;
-}
-
-/******************************************************************************
- *
- * cpia2_set_high_power
- *
- *****************************************************************************/
-static int cpia2_set_high_power(struct camera_data *cam)
-{
- int i;
- for (i = 0; i <= 50; i++) {
- /* Read system status */
- cpia2_do_command(cam,CPIA2_CMD_GET_SYSTEM_CTRL,TRANSFER_READ,0);
-
- /* If there is an error, clear it */
- if(cam->params.camera_state.system_ctrl &
- CPIA2_SYSTEM_CONTROL_V2W_ERR)
- cpia2_do_command(cam, CPIA2_CMD_CLEAR_V2W_ERR,
- TRANSFER_WRITE, 0);
-
- /* Try to set high power mode */
- cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL,
- TRANSFER_WRITE, 1);
-
- /* Try to read something in VP to check if everything is awake */
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_SYSTEM_STATE,
- TRANSFER_READ, 0);
- if (cam->params.vp_params.system_state &
- CPIA2_VP_SYSTEMSTATE_HK_ALIVE) {
- break;
- } else if (i == 50) {
- cam->params.camera_state.power_mode = LO_POWER_MODE;
- ERR("Camera did not wake up\n");
- return -EIO;
- }
- }
-
- DBG("System now in high power state\n");
- cam->params.camera_state.power_mode = HI_POWER_MODE;
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_set_low_power
- *
- *****************************************************************************/
-int cpia2_set_low_power(struct camera_data *cam)
-{
- cam->params.camera_state.power_mode = LO_POWER_MODE;
- cpia2_do_command(cam, CPIA2_CMD_SET_SYSTEM_CTRL, TRANSFER_WRITE, 0);
- return 0;
-}
-
-/******************************************************************************
- *
- * apply_vp_patch
- *
- *****************************************************************************/
-static int cpia2_send_onebyte_command(struct camera_data *cam,
- struct cpia2_command *cmd,
- u8 start, u8 datum)
-{
- cmd->buffer.block_data[0] = datum;
- cmd->start = start;
- cmd->reg_count = 1;
- return cpia2_send_command(cam, cmd);
-}
-
-static int apply_vp_patch(struct camera_data *cam)
-{
- const struct firmware *fw;
- const char fw_name[] = "cpia2/stv0672_vp4.bin";
- int i, ret;
- struct cpia2_command cmd;
-
- ret = request_firmware(&fw, fw_name, &cam->dev->dev);
- if (ret) {
- printk(KERN_ERR "cpia2: failed to load VP patch \"%s\"\n",
- fw_name);
- return ret;
- }
-
- cmd.req_mode = CAMERAACCESS_TYPE_REPEAT | CAMERAACCESS_VP;
- cmd.direction = TRANSFER_WRITE;
-
- /* First send the start address... */
- cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
- cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
-
- /* ... followed by the data payload */
- for (i = 2; i < fw->size; i += 64) {
- cmd.start = 0x0C; /* Data */
- cmd.reg_count = min_t(int, 64, fw->size - i);
- memcpy(cmd.buffer.block_data, &fw->data[i], cmd.reg_count);
- cpia2_send_command(cam, &cmd);
- }
-
- /* Next send the start address... */
- cpia2_send_onebyte_command(cam, &cmd, 0x0A, fw->data[0]); /* hi */
- cpia2_send_onebyte_command(cam, &cmd, 0x0B, fw->data[1]); /* lo */
-
- /* ... followed by the 'goto' command */
- cpia2_send_onebyte_command(cam, &cmd, 0x0D, 1);
-
- release_firmware(fw);
- return 0;
-}
-
-/******************************************************************************
- *
- * set_default_user_mode
- *
- *****************************************************************************/
-static int set_default_user_mode(struct camera_data *cam)
-{
- unsigned char user_mode;
- unsigned char frame_rate;
- int width = cam->params.roi.width;
- int height = cam->params.roi.height;
-
- switch (cam->params.version.sensor_flags) {
- case CPIA2_VP_SENSOR_FLAGS_404:
- case CPIA2_VP_SENSOR_FLAGS_407:
- case CPIA2_VP_SENSOR_FLAGS_409:
- case CPIA2_VP_SENSOR_FLAGS_410:
- if ((width > STV_IMAGE_QCIF_COLS)
- || (height > STV_IMAGE_QCIF_ROWS)) {
- user_mode = CPIA2_VP_USER_MODE_CIF;
- } else {
- user_mode = CPIA2_VP_USER_MODE_QCIFDS;
- }
- frame_rate = CPIA2_VP_FRAMERATE_30;
- break;
- case CPIA2_VP_SENSOR_FLAGS_500:
- if ((width > STV_IMAGE_CIF_COLS)
- || (height > STV_IMAGE_CIF_ROWS)) {
- user_mode = CPIA2_VP_USER_MODE_VGA;
- } else {
- user_mode = CPIA2_VP_USER_MODE_QVGADS;
- }
- if (cam->params.pnp_id.device_type == DEVICE_STV_672)
- frame_rate = CPIA2_VP_FRAMERATE_15;
- else
- frame_rate = CPIA2_VP_FRAMERATE_30;
- break;
- default:
- LOG("%s: Invalid sensor flag value 0x%0X\n",__func__,
- cam->params.version.sensor_flags);
- return -EINVAL;
- }
-
- DBG("Sensor flag = 0x%0x, user mode = 0x%0x, frame rate = 0x%X\n",
- cam->params.version.sensor_flags, user_mode, frame_rate);
- cpia2_do_command(cam, CPIA2_CMD_SET_USER_MODE, TRANSFER_WRITE,
- user_mode);
- if(cam->params.vp_params.frame_rate > 0 &&
- frame_rate > cam->params.vp_params.frame_rate)
- frame_rate = cam->params.vp_params.frame_rate;
-
- cpia2_set_fps(cam, frame_rate);
-
-// if (cam->params.pnp_id.device_type == DEVICE_STV_676)
-// cpia2_do_command(cam,
-// CPIA2_CMD_SET_VP_SYSTEM_CTRL,
-// TRANSFER_WRITE,
-// CPIA2_VP_SYSTEMCTRL_HK_CONTROL |
-// CPIA2_VP_SYSTEMCTRL_POWER_CONTROL);
-
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_match_video_size
- *
- * return the best match, where 'best' is as always
- * the largest that is not bigger than what is requested.
- *****************************************************************************/
-int cpia2_match_video_size(int width, int height)
-{
- if (width >= STV_IMAGE_VGA_COLS && height >= STV_IMAGE_VGA_ROWS)
- return VIDEOSIZE_VGA;
-
- if (width >= STV_IMAGE_CIF_COLS && height >= STV_IMAGE_CIF_ROWS)
- return VIDEOSIZE_CIF;
-
- if (width >= STV_IMAGE_QVGA_COLS && height >= STV_IMAGE_QVGA_ROWS)
- return VIDEOSIZE_QVGA;
-
- if (width >= 288 && height >= 216)
- return VIDEOSIZE_288_216;
-
- if (width >= 256 && height >= 192)
- return VIDEOSIZE_256_192;
-
- if (width >= 224 && height >= 168)
- return VIDEOSIZE_224_168;
-
- if (width >= 192 && height >= 144)
- return VIDEOSIZE_192_144;
-
- if (width >= STV_IMAGE_QCIF_COLS && height >= STV_IMAGE_QCIF_ROWS)
- return VIDEOSIZE_QCIF;
-
- return -1;
-}
-
-/******************************************************************************
- *
- * SetVideoSize
- *
- *****************************************************************************/
-static int set_vw_size(struct camera_data *cam, int size)
-{
- int retval = 0;
-
- cam->params.vp_params.video_size = size;
-
- switch (size) {
- case VIDEOSIZE_VGA:
- DBG("Setting size to VGA\n");
- cam->params.roi.width = STV_IMAGE_VGA_COLS;
- cam->params.roi.height = STV_IMAGE_VGA_ROWS;
- cam->width = STV_IMAGE_VGA_COLS;
- cam->height = STV_IMAGE_VGA_ROWS;
- break;
- case VIDEOSIZE_CIF:
- DBG("Setting size to CIF\n");
- cam->params.roi.width = STV_IMAGE_CIF_COLS;
- cam->params.roi.height = STV_IMAGE_CIF_ROWS;
- cam->width = STV_IMAGE_CIF_COLS;
- cam->height = STV_IMAGE_CIF_ROWS;
- break;
- case VIDEOSIZE_QVGA:
- DBG("Setting size to QVGA\n");
- cam->params.roi.width = STV_IMAGE_QVGA_COLS;
- cam->params.roi.height = STV_IMAGE_QVGA_ROWS;
- cam->width = STV_IMAGE_QVGA_COLS;
- cam->height = STV_IMAGE_QVGA_ROWS;
- break;
- case VIDEOSIZE_288_216:
- cam->params.roi.width = 288;
- cam->params.roi.height = 216;
- cam->width = 288;
- cam->height = 216;
- break;
- case VIDEOSIZE_256_192:
- cam->width = 256;
- cam->height = 192;
- cam->params.roi.width = 256;
- cam->params.roi.height = 192;
- break;
- case VIDEOSIZE_224_168:
- cam->width = 224;
- cam->height = 168;
- cam->params.roi.width = 224;
- cam->params.roi.height = 168;
- break;
- case VIDEOSIZE_192_144:
- cam->width = 192;
- cam->height = 144;
- cam->params.roi.width = 192;
- cam->params.roi.height = 144;
- break;
- case VIDEOSIZE_QCIF:
- DBG("Setting size to QCIF\n");
- cam->params.roi.width = STV_IMAGE_QCIF_COLS;
- cam->params.roi.height = STV_IMAGE_QCIF_ROWS;
- cam->width = STV_IMAGE_QCIF_COLS;
- cam->height = STV_IMAGE_QCIF_ROWS;
- break;
- default:
- retval = -EINVAL;
- }
- return retval;
-}
-
-/******************************************************************************
- *
- * configure_sensor
- *
- *****************************************************************************/
-static int configure_sensor(struct camera_data *cam,
- int req_width, int req_height)
-{
- int retval;
-
- switch (cam->params.version.sensor_flags) {
- case CPIA2_VP_SENSOR_FLAGS_404:
- case CPIA2_VP_SENSOR_FLAGS_407:
- case CPIA2_VP_SENSOR_FLAGS_409:
- case CPIA2_VP_SENSOR_FLAGS_410:
- retval = config_sensor_410(cam, req_width, req_height);
- break;
- case CPIA2_VP_SENSOR_FLAGS_500:
- retval = config_sensor_500(cam, req_width, req_height);
- break;
- default:
- return -EINVAL;
- }
-
- return retval;
-}
-
-/******************************************************************************
- *
- * config_sensor_410
- *
- *****************************************************************************/
-static int config_sensor_410(struct camera_data *cam,
- int req_width, int req_height)
-{
- struct cpia2_command cmd;
- int i = 0;
- int image_size;
- int image_type;
- int width = req_width;
- int height = req_height;
-
- /***
- * Make sure size doesn't exceed CIF.
- ***/
- if (width > STV_IMAGE_CIF_COLS)
- width = STV_IMAGE_CIF_COLS;
- if (height > STV_IMAGE_CIF_ROWS)
- height = STV_IMAGE_CIF_ROWS;
-
- image_size = cpia2_match_video_size(width, height);
-
- DBG("Config 410: width = %d, height = %d\n", width, height);
- DBG("Image size returned is %d\n", image_size);
- if (image_size >= 0) {
- set_vw_size(cam, image_size);
- width = cam->params.roi.width;
- height = cam->params.roi.height;
-
- DBG("After set_vw_size(), width = %d, height = %d\n",
- width, height);
- if (width <= 176 && height <= 144) {
- DBG("image type = VIDEOSIZE_QCIF\n");
- image_type = VIDEOSIZE_QCIF;
- }
- else if (width <= 320 && height <= 240) {
- DBG("image type = VIDEOSIZE_QVGA\n");
- image_type = VIDEOSIZE_QVGA;
- }
- else {
- DBG("image type = VIDEOSIZE_CIF\n");
- image_type = VIDEOSIZE_CIF;
- }
- } else {
- ERR("ConfigSensor410 failed\n");
- return -EINVAL;
- }
-
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.direction = TRANSFER_WRITE;
-
- /* VC Format */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
- if (image_type == VIDEOSIZE_CIF) {
- cmd.buffer.registers[i++].value =
- (u8) (CPIA2_VC_VC_FORMAT_UFIRST |
- CPIA2_VC_VC_FORMAT_SHORTLINE);
- } else {
- cmd.buffer.registers[i++].value =
- (u8) CPIA2_VC_VC_FORMAT_UFIRST;
- }
-
- /* VC Clocks */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
- if (image_type == VIDEOSIZE_QCIF) {
- if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
- cmd.buffer.registers[i++].value=
- (u8)(CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
- CPIA2_VC_VC_672_CLOCKS_SCALING |
- CPIA2_VC_VC_CLOCKS_LOGDIV2);
- DBG("VC_Clocks (0xc4) should be B\n");
- }
- else {
- cmd.buffer.registers[i++].value=
- (u8)(CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
- CPIA2_VC_VC_CLOCKS_LOGDIV2);
- }
- } else {
- if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
- cmd.buffer.registers[i++].value =
- (u8) (CPIA2_VC_VC_672_CLOCKS_CIF_DIV_BY_3 |
- CPIA2_VC_VC_CLOCKS_LOGDIV0);
- }
- else {
- cmd.buffer.registers[i++].value =
- (u8) (CPIA2_VC_VC_676_CLOCKS_CIF_DIV_BY_3 |
- CPIA2_VC_VC_676_CLOCKS_SCALING |
- CPIA2_VC_VC_CLOCKS_LOGDIV0);
- }
- }
- DBG("VC_Clocks (0xc4) = 0x%0X\n", cmd.buffer.registers[i-1].value);
-
- /* Input reqWidth from VC */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value =
- (u8) (STV_IMAGE_QCIF_COLS / 4);
- else
- cmd.buffer.registers[i++].value =
- (u8) (STV_IMAGE_CIF_COLS / 4);
-
- /* Timings */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 0;
- else
- cmd.buffer.registers[i++].value = (u8) 1;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 208;
- else
- cmd.buffer.registers[i++].value = (u8) 160;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 0;
- else
- cmd.buffer.registers[i++].value = (u8) 1;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 160;
- else
- cmd.buffer.registers[i++].value = (u8) 64;
-
- /* Output Image Size */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
- cmd.buffer.registers[i++].value = cam->params.roi.width / 4;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
- cmd.buffer.registers[i++].value = cam->params.roi.height / 4;
-
- /* Cropping */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
- else
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
- else
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
-
- /* Scaling registers (defaults) */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
- cmd.buffer.registers[i++].value = (u8) 31;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
- cmd.buffer.registers[i++].value = (u8) 31;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
- cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
- cmd.buffer.registers[i++].value = (u8) 0x81; /* = 8/1 = 8 (HIBYTE/LOBYTE) */
-
- cmd.reg_count = i;
-
- cpia2_send_command(cam, &cmd);
-
- return i;
-}
-
-
-/******************************************************************************
- *
- * config_sensor_500(cam)
- *
- *****************************************************************************/
-static int config_sensor_500(struct camera_data *cam,
- int req_width, int req_height)
-{
- struct cpia2_command cmd;
- int i = 0;
- int image_size = VIDEOSIZE_CIF;
- int image_type = VIDEOSIZE_VGA;
- int width = req_width;
- int height = req_height;
- unsigned int device = cam->params.pnp_id.device_type;
-
- image_size = cpia2_match_video_size(width, height);
-
- if (width > STV_IMAGE_CIF_COLS || height > STV_IMAGE_CIF_ROWS)
- image_type = VIDEOSIZE_VGA;
- else if (width > STV_IMAGE_QVGA_COLS || height > STV_IMAGE_QVGA_ROWS)
- image_type = VIDEOSIZE_CIF;
- else if (width > STV_IMAGE_QCIF_COLS || height > STV_IMAGE_QCIF_ROWS)
- image_type = VIDEOSIZE_QVGA;
- else
- image_type = VIDEOSIZE_QCIF;
-
- if (image_size >= 0) {
- set_vw_size(cam, image_size);
- width = cam->params.roi.width;
- height = cam->params.roi.height;
- } else {
- ERR("ConfigSensor500 failed\n");
- return -EINVAL;
- }
-
- DBG("image_size = %d, width = %d, height = %d, type = %d\n",
- image_size, width, height, image_type);
-
- cmd.req_mode = CAMERAACCESS_TYPE_RANDOM | CAMERAACCESS_VC;
- cmd.direction = TRANSFER_WRITE;
- i = 0;
-
- /* VC Format */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_FORMAT;
- cmd.buffer.registers[i].value = (u8) CPIA2_VC_VC_FORMAT_UFIRST;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i].value |= (u8) CPIA2_VC_VC_FORMAT_DECIMATING;
- i++;
-
- /* VC Clocks */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_CLOCKS;
- if (device == DEVICE_STV_672) {
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i].value =
- (u8)CPIA2_VC_VC_CLOCKS_LOGDIV1;
- else
- cmd.buffer.registers[i].value =
- (u8)(CPIA2_VC_VC_672_CLOCKS_SCALING |
- CPIA2_VC_VC_CLOCKS_LOGDIV3);
- } else {
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i].value =
- (u8)CPIA2_VC_VC_CLOCKS_LOGDIV0;
- else
- cmd.buffer.registers[i].value =
- (u8)(CPIA2_VC_VC_676_CLOCKS_SCALING |
- CPIA2_VC_VC_CLOCKS_LOGDIV2);
- }
- i++;
-
- DBG("VC_CLOCKS = 0x%X\n", cmd.buffer.registers[i-1].value);
-
- /* Input width from VP */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_IHSIZE_LO;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i].value =
- (u8) (STV_IMAGE_VGA_COLS / 4);
- else
- cmd.buffer.registers[i].value =
- (u8) (STV_IMAGE_QVGA_COLS / 4);
- i++;
- DBG("Input width = %d\n", cmd.buffer.registers[i-1].value);
-
- /* Timings */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_HI;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value = (u8) 2;
- else
- cmd.buffer.registers[i++].value = (u8) 1;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_XLIM_LO;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value = (u8) 250;
- else if (image_type == VIDEOSIZE_QVGA)
- cmd.buffer.registers[i++].value = (u8) 125;
- else
- cmd.buffer.registers[i++].value = (u8) 160;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_HI;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value = (u8) 2;
- else
- cmd.buffer.registers[i++].value = (u8) 1;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_YLIM_LO;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value = (u8) 12;
- else if (image_type == VIDEOSIZE_QVGA)
- cmd.buffer.registers[i++].value = (u8) 64;
- else
- cmd.buffer.registers[i++].value = (u8) 6;
-
- /* Output Image Size */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_OHSIZE;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = STV_IMAGE_CIF_COLS / 4;
- else
- cmd.buffer.registers[i++].value = width / 4;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_OVSIZE;
- if (image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = STV_IMAGE_CIF_ROWS / 4;
- else
- cmd.buffer.registers[i++].value = height / 4;
-
- /* Cropping */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HCROP;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_VGA_COLS / 4) - (width / 4)) / 2);
- else if (image_type == VIDEOSIZE_QVGA)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QVGA_COLS / 4) - (width / 4)) / 2);
- else if (image_type == VIDEOSIZE_CIF)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_CIF_COLS / 4) - (width / 4)) / 2);
- else /*if (image_type == VIDEOSIZE_QCIF)*/
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QCIF_COLS / 4) - (width / 4)) / 2);
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VCROP;
- if (image_type == VIDEOSIZE_VGA)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_VGA_ROWS / 4) - (height / 4)) / 2);
- else if (image_type == VIDEOSIZE_QVGA)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QVGA_ROWS / 4) - (height / 4)) / 2);
- else if (image_type == VIDEOSIZE_CIF)
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_CIF_ROWS / 4) - (height / 4)) / 2);
- else /*if (image_type == VIDEOSIZE_QCIF)*/
- cmd.buffer.registers[i++].value =
- (u8) (((STV_IMAGE_QCIF_ROWS / 4) - (height / 4)) / 2);
-
- /* Scaling registers (defaults) */
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HPHASE;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 36;
- else
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VPHASE;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 32;
- else
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HISPAN;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 26;
- else
- cmd.buffer.registers[i++].value = (u8) 31;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VISPAN;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 21;
- else
- cmd.buffer.registers[i++].value = (u8) 31;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HICROP;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VICROP;
- cmd.buffer.registers[i++].value = (u8) 0;
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_HFRACT;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 0x2B; /* 2/11 */
- else
- cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
-
- cmd.buffer.registers[i].index = CPIA2_VC_VC_VFRACT;
- if (image_type == VIDEOSIZE_CIF || image_type == VIDEOSIZE_QCIF)
- cmd.buffer.registers[i++].value = (u8) 0x13; /* 1/3 */
- else
- cmd.buffer.registers[i++].value = (u8) 0x81; /* 8/1 */
-
- cmd.reg_count = i;
-
- cpia2_send_command(cam, &cmd);
-
- return i;
-}
-
-
-/******************************************************************************
- *
- * setallproperties
- *
- * This sets all user changeable properties to the values in cam->params.
- *****************************************************************************/
-static int set_all_properties(struct camera_data *cam)
-{
- /**
- * Don't set target_kb here, it will be set later.
- * framerate and user_mode were already set (set_default_user_mode).
- **/
-
- cpia2_usb_change_streaming_alternate(cam,
- cam->params.camera_state.stream_mode);
-
- cpia2_do_command(cam,
- CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
- TRANSFER_WRITE, cam->params.vp_params.gpio_direction);
- cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE,
- cam->params.vp_params.gpio_data);
-
- v4l2_ctrl_handler_setup(&cam->hdl);
-
- wake_system(cam);
-
- set_lowlight_boost(cam);
-
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_save_camera_state
- *
- *****************************************************************************/
-void cpia2_save_camera_state(struct camera_data *cam)
-{
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ,
- 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DATA, TRANSFER_READ, 0);
- /* Don't get framerate or target_kb. Trust the values we already have */
-}
-
-
-/******************************************************************************
- *
- * cpia2_set_flicker_mode
- *
- *****************************************************************************/
-int cpia2_set_flicker_mode(struct camera_data *cam, int mode)
-{
- unsigned char cam_reg;
- int err = 0;
-
- if(cam->params.pnp_id.device_type != DEVICE_STV_672)
- return -EINVAL;
-
- /* Set the appropriate bits in FLICKER_MODES, preserving the rest */
- if((err = cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES,
- TRANSFER_READ, 0)))
- return err;
- cam_reg = cam->params.flicker_control.cam_register;
-
- switch(mode) {
- case NEVER_FLICKER:
- cam_reg |= CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
- cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
- break;
- case FLICKER_60:
- cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
- cam_reg &= ~CPIA2_VP_FLICKER_MODES_50HZ;
- break;
- case FLICKER_50:
- cam_reg &= ~CPIA2_VP_FLICKER_MODES_NEVER_FLICKER;
- cam_reg |= CPIA2_VP_FLICKER_MODES_50HZ;
- break;
- default:
- return -EINVAL;
- }
-
- if((err = cpia2_do_command(cam, CPIA2_CMD_SET_FLICKER_MODES,
- TRANSFER_WRITE, cam_reg)))
- return err;
-
- /* Set the appropriate bits in EXP_MODES, preserving the rest */
- if((err = cpia2_do_command(cam, CPIA2_CMD_GET_VP_EXP_MODES,
- TRANSFER_READ, 0)))
- return err;
- cam_reg = cam->params.vp_params.exposure_modes;
-
- if (mode == NEVER_FLICKER) {
- cam_reg |= CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
- } else {
- cam_reg &= ~CPIA2_VP_EXPOSURE_MODES_INHIBIT_FLICKER;
- }
-
- if((err = cpia2_do_command(cam, CPIA2_CMD_SET_VP_EXP_MODES,
- TRANSFER_WRITE, cam_reg)))
- return err;
-
- if((err = cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4,
- TRANSFER_WRITE, 1)))
- return err;
-
- switch(mode) {
- case NEVER_FLICKER:
- case FLICKER_60:
- case FLICKER_50:
- cam->params.flicker_control.flicker_mode_req = mode;
- break;
- default:
- err = -EINVAL;
- }
-
- return err;
-}
-
-/******************************************************************************
- *
- * cpia2_set_property_flip
- *
- *****************************************************************************/
-void cpia2_set_property_flip(struct camera_data *cam, int prop_val)
-{
- unsigned char cam_reg;
-
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
- cam_reg = cam->params.vp_params.user_effects;
-
- if (prop_val)
- {
- cam_reg |= CPIA2_VP_USER_EFFECTS_FLIP;
- }
- else
- {
- cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP;
- }
- cam->params.vp_params.user_effects = cam_reg;
- cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
- cam_reg);
-}
-
-/******************************************************************************
- *
- * cpia2_set_property_mirror
- *
- *****************************************************************************/
-void cpia2_set_property_mirror(struct camera_data *cam, int prop_val)
-{
- unsigned char cam_reg;
-
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
- cam_reg = cam->params.vp_params.user_effects;
-
- if (prop_val)
- {
- cam_reg |= CPIA2_VP_USER_EFFECTS_MIRROR;
- }
- else
- {
- cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR;
- }
- cam->params.vp_params.user_effects = cam_reg;
- cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
- cam_reg);
-}
-
-/******************************************************************************
- *
- * cpia2_set_gpio
- *
- *****************************************************************************/
-int cpia2_set_gpio(struct camera_data *cam, unsigned char setting)
-{
- int ret;
-
- /* Set the microport direction (register 0x90, should be defined
- * already) to 1 (user output), and set the microport data (0x91) to
- * the value in the ioctl argument.
- */
-
- ret = cpia2_do_command(cam,
- CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
- CPIA2_VC_MP_DIR_OUTPUT,
- 255);
- if (ret < 0)
- return ret;
- cam->params.vp_params.gpio_direction = 255;
-
- ret = cpia2_do_command(cam,
- CPIA2_CMD_SET_VC_MP_GPIO_DATA,
- CPIA2_VC_MP_DIR_OUTPUT,
- setting);
- if (ret < 0)
- return ret;
- cam->params.vp_params.gpio_data = setting;
-
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_set_fps
- *
- *****************************************************************************/
-int cpia2_set_fps(struct camera_data *cam, int framerate)
-{
- int retval;
-
- switch(framerate) {
- case CPIA2_VP_FRAMERATE_30:
- case CPIA2_VP_FRAMERATE_25:
- if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- cam->params.version.sensor_flags ==
- CPIA2_VP_SENSOR_FLAGS_500) {
- return -EINVAL;
- }
- /* Fall through */
- case CPIA2_VP_FRAMERATE_15:
- case CPIA2_VP_FRAMERATE_12_5:
- case CPIA2_VP_FRAMERATE_7_5:
- case CPIA2_VP_FRAMERATE_6_25:
- break;
- default:
- return -EINVAL;
- }
-
- if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- framerate == CPIA2_VP_FRAMERATE_15)
- framerate = 0; /* Work around bug in VP4 */
-
- retval = cpia2_do_command(cam,
- CPIA2_CMD_FRAMERATE_REQ,
- TRANSFER_WRITE,
- framerate);
-
- if(retval == 0)
- cam->params.vp_params.frame_rate = framerate;
-
- return retval;
-}
-
-/******************************************************************************
- *
- * cpia2_set_brightness
- *
- *****************************************************************************/
-void cpia2_set_brightness(struct camera_data *cam, unsigned char value)
-{
- /***
- * Don't let the register be set to zero - bug in VP4 - flash of full
- * brightness
- ***/
- if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0)
- value++;
- DBG("Setting brightness to %d (0x%0x)\n", value, value);
- cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value);
-}
-
-/******************************************************************************
- *
- * cpia2_set_contrast
- *
- *****************************************************************************/
-void cpia2_set_contrast(struct camera_data *cam, unsigned char value)
-{
- DBG("Setting contrast to %d (0x%0x)\n", value, value);
- cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value);
-}
-
-/******************************************************************************
- *
- * cpia2_set_saturation
- *
- *****************************************************************************/
-void cpia2_set_saturation(struct camera_data *cam, unsigned char value)
-{
- DBG("Setting saturation to %d (0x%0x)\n", value, value);
- cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value);
-}
-
-/******************************************************************************
- *
- * wake_system
- *
- *****************************************************************************/
-static void wake_system(struct camera_data *cam)
-{
- cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0);
-}
-
-/******************************************************************************
- *
- * set_lowlight_boost
- *
- * Valid for STV500 sensor only
- *****************************************************************************/
-static void set_lowlight_boost(struct camera_data *cam)
-{
- struct cpia2_command cmd;
-
- if (cam->params.pnp_id.device_type != DEVICE_STV_672 ||
- cam->params.version.sensor_flags != CPIA2_VP_SENSOR_FLAGS_500)
- return;
-
- cmd.direction = TRANSFER_WRITE;
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 3;
- cmd.start = CPIA2_VP_RAM_ADDR_H;
-
- cmd.buffer.block_data[0] = 0; /* High byte of address to write to */
- cmd.buffer.block_data[1] = 0x59; /* Low byte of address to write to */
- cmd.buffer.block_data[2] = 0; /* High byte of data to write */
-
- cpia2_send_command(cam, &cmd);
-
- if (cam->params.vp_params.lowlight_boost) {
- cmd.buffer.block_data[0] = 0x02; /* Low byte data to write */
- } else {
- cmd.buffer.block_data[0] = 0x06;
- }
- cmd.start = CPIA2_VP_RAM_DATA;
- cmd.reg_count = 1;
- cpia2_send_command(cam, &cmd);
-
- /* Rehash the VP4 values */
- cpia2_do_command(cam, CPIA2_CMD_REHASH_VP4, TRANSFER_WRITE, 1);
-}
-
-/******************************************************************************
- *
- * cpia2_set_format
- *
- * Assumes that new size is already set in param struct.
- *****************************************************************************/
-void cpia2_set_format(struct camera_data *cam)
-{
- cam->flush = true;
-
- cpia2_usb_stream_pause(cam);
-
- /* reset camera to new size */
- cpia2_set_low_power(cam);
- cpia2_reset_camera(cam);
- cam->flush = false;
-
- cpia2_dbg_dump_registers(cam);
-
- cpia2_usb_stream_resume(cam);
-}
-
-/******************************************************************************
- *
- * cpia2_dbg_dump_registers
- *
- *****************************************************************************/
-void cpia2_dbg_dump_registers(struct camera_data *cam)
-{
-#ifdef _CPIA2_DEBUG_
- struct cpia2_command cmd;
-
- if (!(debugs_on & DEBUG_DUMP_REGS))
- return;
-
- cmd.direction = TRANSFER_READ;
-
- /* Start with bank 0 (SYSTEM) */
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_SYSTEM;
- cmd.reg_count = 3;
- cmd.start = 0;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "System Device Hi = 0x%X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "System Device Lo = 0x%X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "System_system control = 0x%X\n",
- cmd.buffer.block_data[2]);
-
- /* Bank 1 (VC) */
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
- cmd.reg_count = 4;
- cmd.start = 0x80;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "ASIC_ID = 0x%X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "ASIC_REV = 0x%X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "PW_CONTRL = 0x%X\n",
- cmd.buffer.block_data[2]);
- printk(KERN_DEBUG "WAKEUP = 0x%X\n",
- cmd.buffer.block_data[3]);
-
- cmd.start = 0xA0; /* ST_CTRL */
- cmd.reg_count = 1;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "Stream ctrl = 0x%X\n",
- cmd.buffer.block_data[0]);
-
- cmd.start = 0xA4; /* Stream status */
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "Stream status = 0x%X\n",
- cmd.buffer.block_data[0]);
-
- cmd.start = 0xA8; /* USB status */
- cmd.reg_count = 3;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "USB_CTRL = 0x%X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "USB_STRM = 0x%X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "USB_STATUS = 0x%X\n",
- cmd.buffer.block_data[2]);
-
- cmd.start = 0xAF; /* USB settings */
- cmd.reg_count = 1;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "USB settings = 0x%X\n",
- cmd.buffer.block_data[0]);
-
- cmd.start = 0xC0; /* VC stuff */
- cmd.reg_count = 26;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VC Control = 0x%0X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "VC Format = 0x%0X\n",
- cmd.buffer.block_data[3]);
- printk(KERN_DEBUG "VC Clocks = 0x%0X\n",
- cmd.buffer.block_data[4]);
- printk(KERN_DEBUG "VC IHSize = 0x%0X\n",
- cmd.buffer.block_data[5]);
- printk(KERN_DEBUG "VC Xlim Hi = 0x%0X\n",
- cmd.buffer.block_data[6]);
- printk(KERN_DEBUG "VC XLim Lo = 0x%0X\n",
- cmd.buffer.block_data[7]);
- printk(KERN_DEBUG "VC YLim Hi = 0x%0X\n",
- cmd.buffer.block_data[8]);
- printk(KERN_DEBUG "VC YLim Lo = 0x%0X\n",
- cmd.buffer.block_data[9]);
- printk(KERN_DEBUG "VC OHSize = 0x%0X\n",
- cmd.buffer.block_data[10]);
- printk(KERN_DEBUG "VC OVSize = 0x%0X\n",
- cmd.buffer.block_data[11]);
- printk(KERN_DEBUG "VC HCrop = 0x%0X\n",
- cmd.buffer.block_data[12]);
- printk(KERN_DEBUG "VC VCrop = 0x%0X\n",
- cmd.buffer.block_data[13]);
- printk(KERN_DEBUG "VC HPhase = 0x%0X\n",
- cmd.buffer.block_data[14]);
- printk(KERN_DEBUG "VC VPhase = 0x%0X\n",
- cmd.buffer.block_data[15]);
- printk(KERN_DEBUG "VC HIspan = 0x%0X\n",
- cmd.buffer.block_data[16]);
- printk(KERN_DEBUG "VC VIspan = 0x%0X\n",
- cmd.buffer.block_data[17]);
- printk(KERN_DEBUG "VC HiCrop = 0x%0X\n",
- cmd.buffer.block_data[18]);
- printk(KERN_DEBUG "VC ViCrop = 0x%0X\n",
- cmd.buffer.block_data[19]);
- printk(KERN_DEBUG "VC HiFract = 0x%0X\n",
- cmd.buffer.block_data[20]);
- printk(KERN_DEBUG "VC ViFract = 0x%0X\n",
- cmd.buffer.block_data[21]);
- printk(KERN_DEBUG "VC JPeg Opt = 0x%0X\n",
- cmd.buffer.block_data[22]);
- printk(KERN_DEBUG "VC Creep Per = 0x%0X\n",
- cmd.buffer.block_data[23]);
- printk(KERN_DEBUG "VC User Sq. = 0x%0X\n",
- cmd.buffer.block_data[24]);
- printk(KERN_DEBUG "VC Target KB = 0x%0X\n",
- cmd.buffer.block_data[25]);
-
- /*** VP ***/
- cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VP;
- cmd.reg_count = 14;
- cmd.start = 0;
- cpia2_send_command(cam, &cmd);
-
- printk(KERN_DEBUG "VP Dev Hi = 0x%0X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "VP Dev Lo = 0x%0X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "VP Sys State = 0x%0X\n",
- cmd.buffer.block_data[2]);
- printk(KERN_DEBUG "VP Sys Ctrl = 0x%0X\n",
- cmd.buffer.block_data[3]);
- printk(KERN_DEBUG "VP Sensor flg = 0x%0X\n",
- cmd.buffer.block_data[5]);
- printk(KERN_DEBUG "VP Sensor Rev = 0x%0X\n",
- cmd.buffer.block_data[6]);
- printk(KERN_DEBUG "VP Dev Config = 0x%0X\n",
- cmd.buffer.block_data[7]);
- printk(KERN_DEBUG "VP GPIO_DIR = 0x%0X\n",
- cmd.buffer.block_data[8]);
- printk(KERN_DEBUG "VP GPIO_DATA = 0x%0X\n",
- cmd.buffer.block_data[9]);
- printk(KERN_DEBUG "VP Ram ADDR H = 0x%0X\n",
- cmd.buffer.block_data[10]);
- printk(KERN_DEBUG "VP Ram ADDR L = 0x%0X\n",
- cmd.buffer.block_data[11]);
- printk(KERN_DEBUG "VP RAM Data = 0x%0X\n",
- cmd.buffer.block_data[12]);
- printk(KERN_DEBUG "Do Call = 0x%0X\n",
- cmd.buffer.block_data[13]);
-
- if (cam->params.pnp_id.device_type == DEVICE_STV_672) {
- cmd.reg_count = 9;
- cmd.start = 0x0E;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
- cmd.buffer.block_data[2]);
- printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
- cmd.buffer.block_data[3]);
- printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
- cmd.buffer.block_data[4]);
- printk(KERN_DEBUG "VP White Bal = 0x%0X\n",
- cmd.buffer.block_data[5]);
- printk(KERN_DEBUG "VP WB thresh = 0x%0X\n",
- cmd.buffer.block_data[6]);
- printk(KERN_DEBUG "VP Exp Modes = 0x%0X\n",
- cmd.buffer.block_data[7]);
- printk(KERN_DEBUG "VP Exp Target = 0x%0X\n",
- cmd.buffer.block_data[8]);
-
- cmd.reg_count = 1;
- cmd.start = 0x1B;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VP FlickerMds = 0x%0X\n",
- cmd.buffer.block_data[0]);
- } else {
- cmd.reg_count = 8 ;
- cmd.start = 0x0E;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VP Clock Ctrl = 0x%0X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "VP Patch Rev = 0x%0X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "VP Vid Mode = 0x%0X\n",
- cmd.buffer.block_data[5]);
- printk(KERN_DEBUG "VP Framerate = 0x%0X\n",
- cmd.buffer.block_data[6]);
- printk(KERN_DEBUG "VP UserEffect = 0x%0X\n",
- cmd.buffer.block_data[7]);
-
- cmd.reg_count = 1;
- cmd.start = CPIA2_VP5_EXPOSURE_TARGET;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VP5 Exp Target= 0x%0X\n",
- cmd.buffer.block_data[0]);
-
- cmd.reg_count = 4;
- cmd.start = 0x3A;
- cpia2_send_command(cam, &cmd);
- printk(KERN_DEBUG "VP5 MY Black = 0x%0X\n",
- cmd.buffer.block_data[0]);
- printk(KERN_DEBUG "VP5 MCY Range = 0x%0X\n",
- cmd.buffer.block_data[1]);
- printk(KERN_DEBUG "VP5 MYCEILING = 0x%0X\n",
- cmd.buffer.block_data[2]);
- printk(KERN_DEBUG "VP5 MCUV Sat = 0x%0X\n",
- cmd.buffer.block_data[3]);
- }
-#endif
-}
-
-/******************************************************************************
- *
- * reset_camera_struct
- *
- * Sets all values to the defaults
- *****************************************************************************/
-static void reset_camera_struct(struct camera_data *cam)
-{
- /***
- * The following parameter values are the defaults from the register map.
- ***/
- cam->params.vp_params.lowlight_boost = 0;
-
- /* FlickerModes */
- cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER;
-
- /* jpeg params */
- cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
- cam->params.compression.creep_period = 2;
- cam->params.compression.user_squeeze = 20;
- cam->params.compression.inhibit_htables = false;
-
- /* gpio params */
- cam->params.vp_params.gpio_direction = 0; /* write, the default safe mode */
- cam->params.vp_params.gpio_data = 0;
-
- /* Target kb params */
- cam->params.vc_params.quality = 100;
-
- /***
- * Set Sensor FPS as fast as possible.
- ***/
- if(cam->params.pnp_id.device_type == DEVICE_STV_672) {
- if(cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
- cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_15;
- else
- cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
- } else {
- cam->params.vp_params.frame_rate = CPIA2_VP_FRAMERATE_30;
- }
-
- /***
- * Set default video mode as large as possible :
- * for vga sensor set to vga, for cif sensor set to CIF.
- ***/
- if (cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) {
- cam->sensor_type = CPIA2_SENSOR_500;
- cam->video_size = VIDEOSIZE_VGA;
- cam->params.roi.width = STV_IMAGE_VGA_COLS;
- cam->params.roi.height = STV_IMAGE_VGA_ROWS;
- } else {
- cam->sensor_type = CPIA2_SENSOR_410;
- cam->video_size = VIDEOSIZE_CIF;
- cam->params.roi.width = STV_IMAGE_CIF_COLS;
- cam->params.roi.height = STV_IMAGE_CIF_ROWS;
- }
-
- cam->width = cam->params.roi.width;
- cam->height = cam->params.roi.height;
-}
-
-/******************************************************************************
- *
- * cpia2_init_camera_struct
- *
- * Initializes camera struct, does not call reset to fill in defaults.
- *****************************************************************************/
-struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf)
-{
- struct camera_data *cam;
-
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
-
- if (!cam) {
- ERR("couldn't kmalloc cpia2 struct\n");
- return NULL;
- }
-
- cam->v4l2_dev.release = cpia2_camera_release;
- if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) {
- v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n");
- kfree(cam);
- return NULL;
- }
-
- mutex_init(&cam->v4l2_lock);
- init_waitqueue_head(&cam->wq_stream);
-
- return cam;
-}
-
-/******************************************************************************
- *
- * cpia2_init_camera
- *
- * Initializes camera.
- *****************************************************************************/
-int cpia2_init_camera(struct camera_data *cam)
-{
- DBG("Start\n");
-
- cam->mmapped = false;
-
- /* Get sensor and asic types before reset. */
- cpia2_set_high_power(cam);
- cpia2_get_version_info(cam);
- if (cam->params.version.asic_id != CPIA2_ASIC_672) {
- ERR("Device IO error (asicID has incorrect value of 0x%X\n",
- cam->params.version.asic_id);
- return -ENODEV;
- }
-
- /* Set GPIO direction and data to a safe state. */
- cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
- TRANSFER_WRITE, 0);
- cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA,
- TRANSFER_WRITE, 0);
-
- /* resetting struct requires version info for sensor and asic types */
- reset_camera_struct(cam);
-
- cpia2_set_low_power(cam);
-
- DBG("End\n");
-
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_allocate_buffers
- *
- *****************************************************************************/
-int cpia2_allocate_buffers(struct camera_data *cam)
-{
- int i;
-
- if(!cam->buffers) {
- u32 size = cam->num_frames*sizeof(struct framebuf);
- cam->buffers = kmalloc(size, GFP_KERNEL);
- if(!cam->buffers) {
- ERR("couldn't kmalloc frame buffer structures\n");
- return -ENOMEM;
- }
- }
-
- if(!cam->frame_buffer) {
- cam->frame_buffer = rvmalloc(cam->frame_size*cam->num_frames);
- if (!cam->frame_buffer) {
- ERR("couldn't vmalloc frame buffer data area\n");
- kfree(cam->buffers);
- cam->buffers = NULL;
- return -ENOMEM;
- }
- }
-
- for(i=0; i<cam->num_frames-1; ++i) {
- cam->buffers[i].next = &cam->buffers[i+1];
- cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
- cam->buffers[i].status = FRAME_EMPTY;
- cam->buffers[i].length = 0;
- cam->buffers[i].max_length = 0;
- cam->buffers[i].num = i;
- }
- cam->buffers[i].next = cam->buffers;
- cam->buffers[i].data = cam->frame_buffer +i*cam->frame_size;
- cam->buffers[i].status = FRAME_EMPTY;
- cam->buffers[i].length = 0;
- cam->buffers[i].max_length = 0;
- cam->buffers[i].num = i;
- cam->curbuff = cam->buffers;
- cam->workbuff = cam->curbuff->next;
- DBG("buffers=%p, curbuff=%p, workbuff=%p\n", cam->buffers, cam->curbuff,
- cam->workbuff);
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_free_buffers
- *
- *****************************************************************************/
-void cpia2_free_buffers(struct camera_data *cam)
-{
- if(cam->buffers) {
- kfree(cam->buffers);
- cam->buffers = NULL;
- }
- if(cam->frame_buffer) {
- rvfree(cam->frame_buffer, cam->frame_size*cam->num_frames);
- cam->frame_buffer = NULL;
- }
-}
-
-/******************************************************************************
- *
- * cpia2_read
- *
- *****************************************************************************/
-long cpia2_read(struct camera_data *cam,
- char __user *buf, unsigned long count, int noblock)
-{
- struct framebuf *frame;
-
- if (!count)
- return 0;
-
- if (!buf) {
- ERR("%s: buffer NULL\n",__func__);
- return -EINVAL;
- }
-
- if (!cam) {
- ERR("%s: Internal error, camera_data NULL!\n",__func__);
- return -EINVAL;
- }
-
- if (!cam->streaming) {
- /* Start streaming */
- cpia2_usb_stream_start(cam,
- cam->params.camera_state.stream_mode);
- }
-
- /* Copy cam->curbuff in case it changes while we're processing */
- frame = cam->curbuff;
- if (noblock && frame->status != FRAME_READY) {
- return -EAGAIN;
- }
-
- if (frame->status != FRAME_READY) {
- mutex_unlock(&cam->v4l2_lock);
- wait_event_interruptible(cam->wq_stream,
- !video_is_registered(&cam->vdev) ||
- (frame = cam->curbuff)->status == FRAME_READY);
- mutex_lock(&cam->v4l2_lock);
- if (signal_pending(current))
- return -ERESTARTSYS;
- if (!video_is_registered(&cam->vdev))
- return 0;
- }
-
- /* copy data to user space */
- if (frame->length > count)
- return -EFAULT;
- if (copy_to_user(buf, frame->data, frame->length))
- return -EFAULT;
-
- count = frame->length;
-
- frame->status = FRAME_EMPTY;
-
- return count;
-}
-
-/******************************************************************************
- *
- * cpia2_poll
- *
- *****************************************************************************/
-unsigned int cpia2_poll(struct camera_data *cam, struct file *filp,
- poll_table *wait)
-{
- unsigned int status = v4l2_ctrl_poll(filp, wait);
-
- if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) &&
- !cam->streaming) {
- /* Start streaming */
- cpia2_usb_stream_start(cam,
- cam->params.camera_state.stream_mode);
- }
-
- poll_wait(filp, &cam->wq_stream, wait);
-
- if (cam->curbuff->status == FRAME_READY)
- status |= POLLIN | POLLRDNORM;
-
- return status;
-}
-
-/******************************************************************************
- *
- * cpia2_remap_buffer
- *
- *****************************************************************************/
-int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma)
-{
- const char *adr = (const char *)vma->vm_start;
- unsigned long size = vma->vm_end-vma->vm_start;
- unsigned long start_offset = vma->vm_pgoff << PAGE_SHIFT;
- unsigned long start = (unsigned long) adr;
- unsigned long page, pos;
-
- DBG("mmap offset:%ld size:%ld\n", start_offset, size);
-
- if (!video_is_registered(&cam->vdev))
- return -ENODEV;
-
- if (size > cam->frame_size*cam->num_frames ||
- (start_offset % cam->frame_size) != 0 ||
- (start_offset+size > cam->frame_size*cam->num_frames))
- return -EINVAL;
-
- pos = ((unsigned long) (cam->frame_buffer)) + start_offset;
- while (size > 0) {
- page = kvirt_to_pa(pos);
- if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
-
- cam->mmapped = true;
- return 0;
-}
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
deleted file mode 100644
index a62a7b739991..000000000000
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ /dev/null
@@ -1,1250 +0,0 @@
-/****************************************************************************
- *
- * Filename: cpia2_v4l.c
- *
- * Copyright 2001, STMicrolectronics, Inc.
- * Contact: steve.miller@st.com
- * Copyright 2001,2005, Scott J. Bertin <scottbertin@yahoo.com>
- *
- * Description:
- * This is a USB driver for CPia2 based video cameras.
- * The infrastructure of this driver is based on the cpia usb driver by
- * Jochen Scharrlach and Johannes Erdfeldt.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- *
- * Stripped of 2.4 stuff ready for main kernel submit by
- * Alan Cox <alan@lxorguk.ukuu.org.uk>
- ****************************************************************************/
-
-#define CPIA_VERSION "3.0.1"
-
-#include <linux/module.h>
-#include <linux/time.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/videodev2.h>
-#include <linux/stringify.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-
-#include "cpia2.h"
-
-static int video_nr = -1;
-module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
-
-static int buffer_size = 68 * 1024;
-module_param(buffer_size, int, 0);
-MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)");
-
-static int num_buffers = 3;
-module_param(num_buffers, int, 0);
-MODULE_PARM_DESC(num_buffers, "Number of frame buffers (1-"
- __stringify(VIDEO_MAX_FRAME) ", default 3)");
-
-static int alternate = DEFAULT_ALT;
-module_param(alternate, int, 0);
-MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-"
- __stringify(USBIF_ISO_6) ", default "
- __stringify(DEFAULT_ALT) ")");
-
-static int flicker_mode;
-module_param(flicker_mode, int, 0);
-MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or "
- __stringify(60) ", default 0)");
-
-MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
-MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
-MODULE_SUPPORTED_DEVICE("video");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CPIA_VERSION);
-
-#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
-#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000)
-
-/******************************************************************************
- *
- * cpia2_open
- *
- *****************************************************************************/
-static int cpia2_open(struct file *file)
-{
- struct camera_data *cam = video_drvdata(file);
- int retval = v4l2_fh_open(file);
-
- if (retval)
- return retval;
-
- if (v4l2_fh_is_singular_file(file)) {
- if (cpia2_allocate_buffers(cam)) {
- v4l2_fh_release(file);
- return -ENOMEM;
- }
-
- /* reset the camera */
- if (cpia2_reset_camera(cam) < 0) {
- v4l2_fh_release(file);
- return -EIO;
- }
-
- cam->APP_len = 0;
- cam->COM_len = 0;
- }
-
- cpia2_dbg_dump_registers(cam);
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_close
- *
- *****************************************************************************/
-static int cpia2_close(struct file *file)
-{
- struct video_device *dev = video_devdata(file);
- struct camera_data *cam = video_get_drvdata(dev);
-
- if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) {
- cpia2_usb_stream_stop(cam);
-
- /* save camera state for later open */
- cpia2_save_camera_state(cam);
-
- cpia2_set_low_power(cam);
- cpia2_free_buffers(cam);
- }
-
- if (cam->stream_fh == file->private_data) {
- cam->stream_fh = NULL;
- cam->mmapped = 0;
- }
- return v4l2_fh_release(file);
-}
-
-/******************************************************************************
- *
- * cpia2_v4l_read
- *
- *****************************************************************************/
-static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
- loff_t *off)
-{
- struct camera_data *cam = video_drvdata(file);
- int noblock = file->f_flags&O_NONBLOCK;
-
- if(!cam)
- return -EINVAL;
-
- return cpia2_read(cam, buf, count, noblock);
-}
-
-
-/******************************************************************************
- *
- * cpia2_v4l_poll
- *
- *****************************************************************************/
-static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait)
-{
- struct camera_data *cam = video_drvdata(filp);
-
- return cpia2_poll(cam, filp, wait);
-}
-
-
-static int sync(struct camera_data *cam, int frame_nr)
-{
- struct framebuf *frame = &cam->buffers[frame_nr];
-
- while (1) {
- if (frame->status == FRAME_READY)
- return 0;
-
- if (!cam->streaming) {
- frame->status = FRAME_READY;
- frame->length = 0;
- return 0;
- }
-
- mutex_unlock(&cam->v4l2_lock);
- wait_event_interruptible(cam->wq_stream,
- !cam->streaming ||
- frame->status == FRAME_READY);
- mutex_lock(&cam->v4l2_lock);
- if (signal_pending(current))
- return -ERESTARTSYS;
- if (!video_is_registered(&cam->vdev))
- return -ENOTTY;
- }
-}
-
-/******************************************************************************
- *
- * ioctl_querycap
- *
- * V4L2 device capabilities
- *
- *****************************************************************************/
-
-static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *vc)
-{
- struct camera_data *cam = video_drvdata(file);
-
- strcpy(vc->driver, "cpia2");
-
- if (cam->params.pnp_id.product == 0x151)
- strcpy(vc->card, "QX5 Microscope");
- else
- strcpy(vc->card, "CPiA2 Camera");
- switch (cam->params.pnp_id.device_type) {
- case DEVICE_STV_672:
- strcat(vc->card, " (672/");
- break;
- case DEVICE_STV_676:
- strcat(vc->card, " (676/");
- break;
- default:
- strcat(vc->card, " (XXX/");
- break;
- }
- switch (cam->params.version.sensor_flags) {
- case CPIA2_VP_SENSOR_FLAGS_404:
- strcat(vc->card, "404)");
- break;
- case CPIA2_VP_SENSOR_FLAGS_407:
- strcat(vc->card, "407)");
- break;
- case CPIA2_VP_SENSOR_FLAGS_409:
- strcat(vc->card, "409)");
- break;
- case CPIA2_VP_SENSOR_FLAGS_410:
- strcat(vc->card, "410)");
- break;
- case CPIA2_VP_SENSOR_FLAGS_500:
- strcat(vc->card, "500)");
- break;
- default:
- strcat(vc->card, "XXX)");
- break;
- }
-
- if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
- memset(vc->bus_info,0, sizeof(vc->bus_info));
-
- vc->device_caps = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- vc->capabilities = vc->device_caps |
- V4L2_CAP_DEVICE_CAPS;
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_input
- *
- * V4L2 input get/set/enumerate
- *
- *****************************************************************************/
-
-static int cpia2_enum_input(struct file *file, void *fh, struct v4l2_input *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
- *
- * V4L2 format enumerate
- *
- *****************************************************************************/
-
-static int cpia2_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- int index = f->index;
-
- if (index < 0 || index > 1)
- return -EINVAL;
-
- memset(f, 0, sizeof(*f));
- f->index = index;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->flags = V4L2_FMT_FLAG_COMPRESSED;
- switch(index) {
- case 0:
- strcpy(f->description, "MJPEG");
- f->pixelformat = V4L2_PIX_FMT_MJPEG;
- break;
- case 1:
- strcpy(f->description, "JPEG");
- f->pixelformat = V4L2_PIX_FMT_JPEG;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_try_fmt
- *
- * V4L2 format try
- *
- *****************************************************************************/
-
-static int cpia2_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct camera_data *cam = video_drvdata(file);
-
- if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
- f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
- return -EINVAL;
-
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = cam->frame_size;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
- f->fmt.pix.priv = 0;
-
- switch (cpia2_match_video_size(f->fmt.pix.width, f->fmt.pix.height)) {
- case VIDEOSIZE_VGA:
- f->fmt.pix.width = 640;
- f->fmt.pix.height = 480;
- break;
- case VIDEOSIZE_CIF:
- f->fmt.pix.width = 352;
- f->fmt.pix.height = 288;
- break;
- case VIDEOSIZE_QVGA:
- f->fmt.pix.width = 320;
- f->fmt.pix.height = 240;
- break;
- case VIDEOSIZE_288_216:
- f->fmt.pix.width = 288;
- f->fmt.pix.height = 216;
- break;
- case VIDEOSIZE_256_192:
- f->fmt.pix.width = 256;
- f->fmt.pix.height = 192;
- break;
- case VIDEOSIZE_224_168:
- f->fmt.pix.width = 224;
- f->fmt.pix.height = 168;
- break;
- case VIDEOSIZE_192_144:
- f->fmt.pix.width = 192;
- f->fmt.pix.height = 144;
- break;
- case VIDEOSIZE_QCIF:
- default:
- f->fmt.pix.width = 176;
- f->fmt.pix.height = 144;
- break;
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_set_fmt
- *
- * V4L2 format set
- *
- *****************************************************************************/
-
-static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh,
- struct v4l2_format *f)
-{
- struct camera_data *cam = video_drvdata(file);
- int err, frame;
-
- err = cpia2_try_fmt_vid_cap(file, _fh, f);
- if(err != 0)
- return err;
-
- cam->pixelformat = f->fmt.pix.pixelformat;
-
- /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle
- * the missing Huffman table properly. */
- cam->params.compression.inhibit_htables = 0;
- /*f->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG;*/
-
- /* we set the video window to something smaller or equal to what
- * is requested by the user???
- */
- DBG("Requested width = %d, height = %d\n",
- f->fmt.pix.width, f->fmt.pix.height);
- if (f->fmt.pix.width != cam->width ||
- f->fmt.pix.height != cam->height) {
- cam->width = f->fmt.pix.width;
- cam->height = f->fmt.pix.height;
- cam->params.roi.width = f->fmt.pix.width;
- cam->params.roi.height = f->fmt.pix.height;
- cpia2_set_format(cam);
- }
-
- for (frame = 0; frame < cam->num_frames; ++frame) {
- if (cam->buffers[frame].status == FRAME_READING)
- if ((err = sync(cam, frame)) < 0)
- return err;
-
- cam->buffers[frame].status = FRAME_EMPTY;
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_get_fmt
- *
- * V4L2 format get
- *
- *****************************************************************************/
-
-static int cpia2_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct camera_data *cam = video_drvdata(file);
-
- f->fmt.pix.width = cam->width;
- f->fmt.pix.height = cam->height;
- f->fmt.pix.pixelformat = cam->pixelformat;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = cam->frame_size;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG;
- f->fmt.pix.priv = 0;
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_cropcap
- *
- * V4L2 query cropping capabilities
- * NOTE: cropping is currently disabled
- *
- *****************************************************************************/
-
-static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c)
-{
- struct camera_data *cam = video_drvdata(file);
-
- if (c->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- c->bounds.left = 0;
- c->bounds.top = 0;
- c->bounds.width = cam->width;
- c->bounds.height = cam->height;
- c->defrect.left = 0;
- c->defrect.top = 0;
- c->defrect.width = cam->width;
- c->defrect.height = cam->height;
- c->pixelaspect.numerator = 1;
- c->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-struct framerate_info {
- int value;
- struct v4l2_fract period;
-};
-
-static const struct framerate_info framerate_controls[] = {
- { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } },
- { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } },
- { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } },
- { CPIA2_VP_FRAMERATE_15, { 1, 15 } },
- { CPIA2_VP_FRAMERATE_25, { 1, 25 } },
- { CPIA2_VP_FRAMERATE_30, { 1, 30 } },
-};
-
-static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
-{
- struct camera_data *cam = video_drvdata(file);
- struct v4l2_captureparm *cap = &p->parm.capture;
- int i;
-
- if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- cap->capability = V4L2_CAP_TIMEPERFRAME;
- cap->readbuffers = cam->num_frames;
- for (i = 0; i < ARRAY_SIZE(framerate_controls); i++)
- if (cam->params.vp_params.frame_rate == framerate_controls[i].value) {
- cap->timeperframe = framerate_controls[i].period;
- break;
- }
- return 0;
-}
-
-static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
-{
- struct camera_data *cam = video_drvdata(file);
- struct v4l2_captureparm *cap = &p->parm.capture;
- struct v4l2_fract tpf = cap->timeperframe;
- int max = ARRAY_SIZE(framerate_controls) - 1;
- int ret;
- int i;
-
- ret = cpia2_g_parm(file, fh, p);
- if (ret || !tpf.denominator || !tpf.numerator)
- return ret;
-
- /* Maximum 15 fps for this model */
- if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
- max -= 2;
- for (i = 0; i <= max; i++) {
- struct v4l2_fract f1 = tpf;
- struct v4l2_fract f2 = framerate_controls[i].period;
-
- f1.numerator *= f2.denominator;
- f2.numerator *= f1.denominator;
- if (f1.numerator >= f2.numerator)
- break;
- }
- if (i > max)
- i = max;
- cap->timeperframe = framerate_controls[i].period;
- return cpia2_set_fps(cam, framerate_controls[i].value);
-}
-
-static const struct {
- u32 width;
- u32 height;
-} cpia2_framesizes[] = {
- { 640, 480 },
- { 352, 288 },
- { 320, 240 },
- { 288, 216 },
- { 256, 192 },
- { 224, 168 },
- { 192, 144 },
- { 176, 144 },
-};
-
-static int cpia2_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
-
- if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG &&
- fsize->pixel_format != V4L2_PIX_FMT_JPEG)
- return -EINVAL;
- if (fsize->index >= ARRAY_SIZE(cpia2_framesizes))
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = cpia2_framesizes[fsize->index].width;
- fsize->discrete.height = cpia2_framesizes[fsize->index].height;
-
- return 0;
-}
-
-static int cpia2_enum_frameintervals(struct file *file, void *fh,
- struct v4l2_frmivalenum *fival)
-{
- struct camera_data *cam = video_drvdata(file);
- int max = ARRAY_SIZE(framerate_controls) - 1;
- int i;
-
- if (fival->pixel_format != V4L2_PIX_FMT_MJPEG &&
- fival->pixel_format != V4L2_PIX_FMT_JPEG)
- return -EINVAL;
-
- /* Maximum 15 fps for this model */
- if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
- max -= 2;
- if (fival->index > max)
- return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++)
- if (fival->width == cpia2_framesizes[i].width &&
- fival->height == cpia2_framesizes[i].height)
- break;
- if (i == ARRAY_SIZE(cpia2_framesizes))
- return -EINVAL;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete = framerate_controls[fival->index].period;
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_s_ctrl
- *
- * V4L2 set the value of a control variable
- *
- *****************************************************************************/
-
-static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct camera_data *cam =
- container_of(ctrl->handler, struct camera_data, hdl);
- static const int flicker_table[] = {
- NEVER_FLICKER,
- FLICKER_50,
- FLICKER_60,
- };
-
- DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- cpia2_set_brightness(cam, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- cpia2_set_contrast(cam, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- cpia2_set_saturation(cam, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- cpia2_set_property_mirror(cam, ctrl->val);
- break;
- case V4L2_CID_VFLIP:
- cpia2_set_property_flip(cam, ctrl->val);
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]);
- case V4L2_CID_ILLUMINATORS_1:
- return cpia2_set_gpio(cam, (cam->top_light->val << 6) |
- (cam->bottom_light->val << 7));
- case V4L2_CID_JPEG_ACTIVE_MARKER:
- cam->params.compression.inhibit_htables =
- !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- cam->params.vc_params.quality = ctrl->val;
- break;
- case CPIA2_CID_USB_ALT:
- cam->params.camera_state.stream_mode = ctrl->val;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_g_jpegcomp
- *
- * V4L2 get the JPEG compression parameters
- *
- *****************************************************************************/
-
-static int cpia2_g_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
-{
- struct camera_data *cam = video_drvdata(file);
-
- memset(parms, 0, sizeof(*parms));
-
- parms->quality = 80; // TODO: Can this be made meaningful?
-
- parms->jpeg_markers = V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI;
- if(!cam->params.compression.inhibit_htables) {
- parms->jpeg_markers |= V4L2_JPEG_MARKER_DHT;
- }
-
- parms->APPn = cam->APPn;
- parms->APP_len = cam->APP_len;
- if(cam->APP_len > 0) {
- memcpy(parms->APP_data, cam->APP_data, cam->APP_len);
- parms->jpeg_markers |= V4L2_JPEG_MARKER_APP;
- }
-
- parms->COM_len = cam->COM_len;
- if(cam->COM_len > 0) {
- memcpy(parms->COM_data, cam->COM_data, cam->COM_len);
- parms->jpeg_markers |= JPEG_MARKER_COM;
- }
-
- DBG("G_JPEGCOMP APP_len:%d COM_len:%d\n",
- parms->APP_len, parms->COM_len);
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_s_jpegcomp
- *
- * V4L2 set the JPEG compression parameters
- * NOTE: quality and some jpeg_markers are ignored.
- *
- *****************************************************************************/
-
-static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompression *parms)
-{
- struct camera_data *cam = video_drvdata(file);
-
- DBG("S_JPEGCOMP APP_len:%d COM_len:%d\n",
- parms->APP_len, parms->COM_len);
-
- cam->params.compression.inhibit_htables =
- !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT);
- parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI |
- V4L2_JPEG_MARKER_DHT;
-
- if(parms->APP_len != 0) {
- if(parms->APP_len > 0 &&
- parms->APP_len <= sizeof(cam->APP_data) &&
- parms->APPn >= 0 && parms->APPn <= 15) {
- cam->APPn = parms->APPn;
- cam->APP_len = parms->APP_len;
- memcpy(cam->APP_data, parms->APP_data, parms->APP_len);
- } else {
- LOG("Bad APPn Params n=%d len=%d\n",
- parms->APPn, parms->APP_len);
- return -EINVAL;
- }
- } else {
- cam->APP_len = 0;
- }
-
- if(parms->COM_len != 0) {
- if(parms->COM_len > 0 &&
- parms->COM_len <= sizeof(cam->COM_data)) {
- cam->COM_len = parms->COM_len;
- memcpy(cam->COM_data, parms->COM_data, parms->COM_len);
- } else {
- LOG("Bad COM_len=%d\n", parms->COM_len);
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_reqbufs
- *
- * V4L2 Initiate memory mapping.
- * NOTE: The user's request is ignored. For now the buffers are fixed.
- *
- *****************************************************************************/
-
-static int cpia2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req)
-{
- struct camera_data *cam = video_drvdata(file);
-
- if(req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- req->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- DBG("REQBUFS requested:%d returning:%d\n", req->count, cam->num_frames);
- req->count = cam->num_frames;
- memset(&req->reserved, 0, sizeof(req->reserved));
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_querybuf
- *
- * V4L2 Query memory buffer status.
- *
- *****************************************************************************/
-
-static int cpia2_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct camera_data *cam = video_drvdata(file);
-
- if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->index > cam->num_frames)
- return -EINVAL;
-
- buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
- buf->length = cam->frame_size;
-
- buf->memory = V4L2_MEMORY_MMAP;
-
- if(cam->mmapped)
- buf->flags = V4L2_BUF_FLAG_MAPPED;
- else
- buf->flags = 0;
-
- switch (cam->buffers[buf->index].status) {
- case FRAME_EMPTY:
- case FRAME_ERROR:
- case FRAME_READING:
- buf->bytesused = 0;
- buf->flags = V4L2_BUF_FLAG_QUEUED;
- break;
- case FRAME_READY:
- buf->bytesused = cam->buffers[buf->index].length;
- buf->timestamp = cam->buffers[buf->index].timestamp;
- buf->sequence = cam->buffers[buf->index].seq;
- buf->flags = V4L2_BUF_FLAG_DONE;
- break;
- }
-
- DBG("QUERYBUF index:%d offset:%d flags:%d seq:%d bytesused:%d\n",
- buf->index, buf->m.offset, buf->flags, buf->sequence,
- buf->bytesused);
-
- return 0;
-}
-
-/******************************************************************************
- *
- * ioctl_qbuf
- *
- * V4L2 User is freeing buffer
- *
- *****************************************************************************/
-
-static int cpia2_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct camera_data *cam = video_drvdata(file);
-
- if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->memory != V4L2_MEMORY_MMAP ||
- buf->index > cam->num_frames)
- return -EINVAL;
-
- DBG("QBUF #%d\n", buf->index);
-
- if(cam->buffers[buf->index].status == FRAME_READY)
- cam->buffers[buf->index].status = FRAME_EMPTY;
-
- return 0;
-}
-
-/******************************************************************************
- *
- * find_earliest_filled_buffer
- *
- * Helper for ioctl_dqbuf. Find the next ready buffer.
- *
- *****************************************************************************/
-
-static int find_earliest_filled_buffer(struct camera_data *cam)
-{
- int i;
- int found = -1;
- for (i=0; i<cam->num_frames; i++) {
- if(cam->buffers[i].status == FRAME_READY) {
- if(found < 0) {
- found = i;
- } else {
- /* find which buffer is earlier */
- struct timeval *tv1, *tv2;
- tv1 = &cam->buffers[i].timestamp;
- tv2 = &cam->buffers[found].timestamp;
- if(tv1->tv_sec < tv2->tv_sec ||
- (tv1->tv_sec == tv2->tv_sec &&
- tv1->tv_usec < tv2->tv_usec))
- found = i;
- }
- }
- }
- return found;
-}
-
-/******************************************************************************
- *
- * ioctl_dqbuf
- *
- * V4L2 User is asking for a filled buffer.
- *
- *****************************************************************************/
-
-static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct camera_data *cam = video_drvdata(file);
- int frame;
-
- if(buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- buf->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- frame = find_earliest_filled_buffer(cam);
-
- if(frame < 0 && file->f_flags&O_NONBLOCK)
- return -EAGAIN;
-
- if(frame < 0) {
- /* Wait for a frame to become available */
- struct framebuf *cb=cam->curbuff;
- mutex_unlock(&cam->v4l2_lock);
- wait_event_interruptible(cam->wq_stream,
- !video_is_registered(&cam->vdev) ||
- (cb=cam->curbuff)->status == FRAME_READY);
- mutex_lock(&cam->v4l2_lock);
- if (signal_pending(current))
- return -ERESTARTSYS;
- if (!video_is_registered(&cam->vdev))
- return -ENOTTY;
- frame = cb->num;
- }
-
-
- buf->index = frame;
- buf->bytesused = cam->buffers[buf->index].length;
- buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
- buf->field = V4L2_FIELD_NONE;
- buf->timestamp = cam->buffers[buf->index].timestamp;
- buf->sequence = cam->buffers[buf->index].seq;
- buf->m.offset = cam->buffers[buf->index].data - cam->frame_buffer;
- buf->length = cam->frame_size;
- buf->reserved2 = 0;
- buf->reserved = 0;
- memset(&buf->timecode, 0, sizeof(buf->timecode));
-
- DBG("DQBUF #%d status:%d seq:%d length:%d\n", buf->index,
- cam->buffers[buf->index].status, buf->sequence, buf->bytesused);
-
- return 0;
-}
-
-static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
-{
- struct camera_data *cam = video_drvdata(file);
- int ret = -EINVAL;
-
- DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
- if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (!cam->streaming) {
- ret = cpia2_usb_stream_start(cam,
- cam->params.camera_state.stream_mode);
- if (!ret)
- v4l2_ctrl_grab(cam->usb_alt, true);
- }
- return ret;
-}
-
-static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
-{
- struct camera_data *cam = video_drvdata(file);
- int ret = -EINVAL;
-
- DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
- if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (cam->streaming) {
- ret = cpia2_usb_stream_stop(cam);
- if (!ret)
- v4l2_ctrl_grab(cam->usb_alt, false);
- }
- return ret;
-}
-
-/******************************************************************************
- *
- * cpia2_mmap
- *
- *****************************************************************************/
-static int cpia2_mmap(struct file *file, struct vm_area_struct *area)
-{
- struct camera_data *cam = video_drvdata(file);
- int retval;
-
- retval = cpia2_remap_buffer(cam, area);
-
- if(!retval)
- cam->stream_fh = file->private_data;
- return retval;
-}
-
-/******************************************************************************
- *
- * reset_camera_struct_v4l
- *
- * Sets all values to the defaults
- *****************************************************************************/
-static void reset_camera_struct_v4l(struct camera_data *cam)
-{
- cam->width = cam->params.roi.width;
- cam->height = cam->params.roi.height;
-
- cam->frame_size = buffer_size;
- cam->num_frames = num_buffers;
-
- /* Flicker modes */
- cam->params.flicker_control.flicker_mode_req = flicker_mode;
-
- /* stream modes */
- cam->params.camera_state.stream_mode = alternate;
-
- cam->pixelformat = V4L2_PIX_FMT_JPEG;
-}
-
-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_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_s_parm = cpia2_s_parm,
- .vidioc_g_parm = cpia2_g_parm,
- .vidioc_enum_framesizes = cpia2_enum_framesizes,
- .vidioc_enum_frameintervals = cpia2_enum_frameintervals,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/***
- * The v4l video device structure initialized for this device
- ***/
-static const struct v4l2_file_operations cpia2_fops = {
- .owner = THIS_MODULE,
- .open = cpia2_open,
- .release = cpia2_close,
- .read = cpia2_v4l_read,
- .poll = cpia2_v4l_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = cpia2_mmap,
-};
-
-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_empty,
-};
-
-void cpia2_camera_release(struct v4l2_device *v4l2_dev)
-{
- struct camera_data *cam =
- container_of(v4l2_dev, struct camera_data, v4l2_dev);
-
- v4l2_ctrl_handler_free(&cam->hdl);
- v4l2_device_unregister(&cam->v4l2_dev);
- kfree(cam);
-}
-
-static const struct v4l2_ctrl_ops cpia2_ctrl_ops = {
- .s_ctrl = cpia2_s_ctrl,
-};
-
-/******************************************************************************
- *
- * cpia2_register_camera
- *
- *****************************************************************************/
-int cpia2_register_camera(struct camera_data *cam)
-{
- struct v4l2_ctrl_handler *hdl = &cam->hdl;
- struct v4l2_ctrl_config cpia2_usb_alt = {
- .ops = &cpia2_ctrl_ops,
- .id = CPIA2_CID_USB_ALT,
- .name = "USB Alternate",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .min = USBIF_ISO_1,
- .max = USBIF_ISO_6,
- .step = 1,
- };
- int ret;
-
- v4l2_ctrl_handler_init(hdl, 12);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_BRIGHTNESS,
- cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0,
- 255, 1, DEFAULT_BRIGHTNESS);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_JPEG_ACTIVE_MARKER, 0,
- V4L2_JPEG_ACTIVE_MARKER_DHT, 0,
- V4L2_JPEG_ACTIVE_MARKER_DHT);
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY, 1,
- 100, 1, 100);
- cpia2_usb_alt.def = alternate;
- cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL);
- /* VP5 Only */
- if (cam->params.pnp_id.device_type != DEVICE_STV_672)
- v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- /* Flicker control only valid for 672 */
- if (cam->params.pnp_id.device_type == DEVICE_STV_672)
- v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
- /* Light control only valid for the QX5 Microscope */
- if (cam->params.pnp_id.product == 0x151) {
- cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
- cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
- V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
- v4l2_ctrl_cluster(2, &cam->top_light);
- }
-
- if (hdl->error) {
- ret = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- return ret;
- }
-
- cam->vdev = cpia2_template;
- video_set_drvdata(&cam->vdev, cam);
- cam->vdev.lock = &cam->v4l2_lock;
- cam->vdev.ctrl_handler = hdl;
- cam->vdev.v4l2_dev = &cam->v4l2_dev;
- set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &cam->vdev.flags);
-
- reset_camera_struct_v4l(cam);
-
- /* register v4l device */
- if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
- ERR("video_register_device failed\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
- * cpia2_unregister_camera
- *
- *****************************************************************************/
-void cpia2_unregister_camera(struct camera_data *cam)
-{
- video_unregister_device(&cam->vdev);
-}
-
-/******************************************************************************
- *
- * check_parameters
- *
- * Make sure that all user-supplied parameters are sensible
- *****************************************************************************/
-static void __init check_parameters(void)
-{
- if(buffer_size < PAGE_SIZE) {
- buffer_size = PAGE_SIZE;
- LOG("buffer_size too small, setting to %d\n", buffer_size);
- } else if(buffer_size > 1024*1024) {
- /* arbitrary upper limiit */
- buffer_size = 1024*1024;
- LOG("buffer_size ridiculously large, setting to %d\n",
- buffer_size);
- } else {
- buffer_size += PAGE_SIZE-1;
- buffer_size &= ~(PAGE_SIZE-1);
- }
-
- if(num_buffers < 1) {
- num_buffers = 1;
- LOG("num_buffers too small, setting to %d\n", num_buffers);
- } else if(num_buffers > VIDEO_MAX_FRAME) {
- num_buffers = VIDEO_MAX_FRAME;
- LOG("num_buffers too large, setting to %d\n", num_buffers);
- }
-
- if(alternate < USBIF_ISO_1 || alternate > USBIF_ISO_6) {
- alternate = DEFAULT_ALT;
- LOG("alternate specified is invalid, using %d\n", alternate);
- }
-
- if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) {
- flicker_mode = 0;
- LOG("Flicker mode specified is invalid, using %d\n",
- flicker_mode);
- }
-
- DBG("Using %d buffers, each %d bytes, alternate=%d\n",
- num_buffers, buffer_size, alternate);
-}
-
-/************ Module Stuff ***************/
-
-
-/******************************************************************************
- *
- * cpia2_init/module_init
- *
- *****************************************************************************/
-static int __init cpia2_init(void)
-{
- LOG("%s v%s\n",
- ABOUT, CPIA_VERSION);
- check_parameters();
- cpia2_usb_init();
- return 0;
-}
-
-
-/******************************************************************************
- *
- * cpia2_exit/module_exit
- *
- *****************************************************************************/
-static void __exit cpia2_exit(void)
-{
- cpia2_usb_cleanup();
- schedule_timeout(2 * HZ);
-}
-
-module_init(cpia2_init);
-module_exit(cpia2_exit);
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
deleted file mode 100644
index 53b3c7702573..000000000000
--- a/drivers/media/video/cx18/Kconfig
+++ /dev/null
@@ -1,35 +0,0 @@
-config VIDEO_CX18
- tristate "Conexant cx23418 MPEG encoder support"
- depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
- select I2C_ALGOBIT
- select VIDEOBUF_VMALLOC
- depends on RC_CORE
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_CX2341X
- select VIDEO_CS5345
- select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- ---help---
- This is a video4linux driver for Conexant cx23418 based
- PCI combo video recorder devices.
-
- This is used in devices such as the Hauppauge HVR-1600
- cards.
-
- To compile this driver as a module, choose M here: the
- module will be called cx18.
-
-config VIDEO_CX18_ALSA
- tristate "Conexant 23418 DMA audio support"
- depends on VIDEO_CX18 && SND && EXPERIMENTAL
- select SND_PCM
- ---help---
- This is a video4linux driver for direct (DMA) audio on
- Conexant 23418 based TV cards using ALSA.
-
- To compile this driver as a module, choose M here: the
- module will be called cx18-alsa.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
deleted file mode 100644
index a86bab5893ef..000000000000
--- a/drivers/media/video/cx18/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
- cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
- cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
- cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
- cx18-dvb.o cx18-io.o
-cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
-
-obj-$(CONFIG_VIDEO_CX18) += cx18.o
-obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
-
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
deleted file mode 100644
index 280aa4d22488..000000000000
--- a/drivers/media/video/cx18/cx18-av-firmware.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * cx18 ADEC firmware functions
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-#include "cx18-driver.h"
-#include "cx18-io.h"
-#include <linux/firmware.h>
-
-#define CX18_AUDIO_ENABLE 0xc72014
-#define CX18_AI1_MUX_MASK 0x30
-#define CX18_AI1_MUX_I2S1 0x00
-#define CX18_AI1_MUX_I2S2 0x10
-#define CX18_AI1_MUX_843_I2S 0x20
-#define CX18_AI1_MUX_INVALID 0x30
-
-#define FWFILE "v4l-cx23418-dig.fw"
-
-static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw)
-{
- struct v4l2_subdev *sd = &cx->av_state.sd;
- int ret = 0;
- const u8 *data;
- u32 size;
- int addr;
- u32 expected, dl_control;
-
- /* Ensure we put the 8051 in reset and enable firmware upload mode */
- dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
- do {
- dl_control &= 0x00ffffff;
- dl_control |= 0x0f000000;
- cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control);
- dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
- } while ((dl_control & 0xff000000) != 0x0f000000);
-
- /* Read and auto increment until at address 0x0000 */
- while (dl_control & 0x3fff)
- dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
-
- data = fw->data;
- size = fw->size;
- for (addr = 0; addr < size; addr++) {
- dl_control &= 0xffff3fff; /* ignore top 2 bits of address */
- expected = 0x0f000000 | ((u32)data[addr] << 16) | addr;
- if (expected != dl_control) {
- CX18_ERR_DEV(sd, "verification of %s firmware load "
- "failed: expected %#010x got %#010x\n",
- FWFILE, expected, dl_control);
- ret = -EIO;
- break;
- }
- dl_control = cx18_av_read4(cx, CXADEC_DL_CTL);
- }
- if (ret == 0)
- CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n",
- FWFILE, size);
- return ret;
-}
-
-int cx18_av_loadfw(struct cx18 *cx)
-{
- struct v4l2_subdev *sd = &cx->av_state.sd;
- const struct firmware *fw = NULL;
- u32 size;
- u32 u, v;
- const u8 *ptr;
- int i;
- int retries1 = 0;
-
- if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
- CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
- return -EINVAL;
- }
-
- /* The firmware load often has byte errors, so allow for several
- retries, both at byte level and at the firmware load level. */
- while (retries1 < 5) {
- cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000,
- 0x00008430, 0xffffffff); /* cx25843 */
- cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff);
-
- /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */
- cx18_av_write4_expect(cx, 0x8100, 0x00010000,
- 0x00008430, 0xffffffff); /* cx25843 */
-
- /* Put the 8051 in reset and enable firmware upload */
- cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000);
-
- ptr = fw->data;
- size = fw->size;
-
- for (i = 0; i < size; i++) {
- u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16);
- u32 value = 0;
- int retries2;
- int unrec_err = 0;
-
- for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES;
- retries2++) {
- cx18_av_write4_noretry(cx, CXADEC_DL_CTL,
- dl_control);
- udelay(10);
- value = cx18_av_read4(cx, CXADEC_DL_CTL);
- if (value == dl_control)
- break;
- /* Check if we can correct the byte by changing
- the address. We can only write the lower
- address byte of the address. */
- if ((value & 0x3F00) != (dl_control & 0x3F00)) {
- unrec_err = 1;
- break;
- }
- }
- if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES)
- break;
- }
- if (i == size)
- break;
- retries1++;
- }
- if (retries1 >= 5) {
- CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
- release_firmware(fw);
- return -EIO;
- }
-
- cx18_av_write4_expect(cx, CXADEC_DL_CTL,
- 0x03000000 | fw->size, 0x03000000, 0x13000000);
-
- CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
-
- if (cx18_av_verifyfw(cx, fw) == 0)
- cx18_av_write4_expect(cx, CXADEC_DL_CTL,
- 0x13000000 | fw->size, 0x13000000, 0x13000000);
-
- /* Output to the 416 */
- cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
-
- /* Audio input control 1 set to Sony mode */
- /* Audio output input 2 is 0 for slave operation input */
- /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
- /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
- after WS transition for first bit of audio word. */
- cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
-
- /* Audio output control 1 is set to Sony mode */
- /* Audio output control 2 is set to 1 for master mode */
- /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
- /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
- after WS transition for first bit of audio word. */
- /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
- are generated) */
- cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
-
- /* set alt I2s master clock to /0x16 and enable alt divider i2s
- passthrough */
- cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
-
- cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
- 0x3F00FFFF);
- /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
-
- /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
- /* Register 0x09CC is defined by the Merlin firmware, and doesn't
- have a name in the spec. */
- cx18_av_write4(cx, 0x09CC, 1);
-
- v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
- /* If bit 11 is 1, clear bit 10 */
- if (v & 0x800)
- cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
- 0, 0x400);
-
- /* Toggle the AI1 MUX */
- v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
- u = v & CX18_AI1_MUX_MASK;
- v &= ~CX18_AI1_MUX_MASK;
- if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) {
- /* Switch to I2S1 */
- v |= CX18_AI1_MUX_I2S1;
- cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
- v, CX18_AI1_MUX_MASK);
- /* Switch back to the A/V decoder core I2S output */
- v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S;
- } else {
- /* Switch to the A/V decoder core I2S output */
- v |= CX18_AI1_MUX_843_I2S;
- cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
- v, CX18_AI1_MUX_MASK);
- /* Switch back to I2S1 or I2S2 */
- v = (v & ~CX18_AI1_MUX_MASK) | u;
- }
- cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE,
- v, CX18_AI1_MUX_MASK);
-
- /* Enable WW auto audio standard detection */
- v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
- v |= 0xFF; /* Auto by default */
- v |= 0x400; /* Stereo by default */
- v |= 0x14000000;
- cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF);
-
- release_firmware(fw);
- return 0;
-}
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
deleted file mode 100644
index baa36fbcd4d4..000000000000
--- a/drivers/media/video/cx18/cx18-av-vbi.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * cx18 ADEC VBI functions
- *
- * Derived from cx25840-vbi.c
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-
-#include "cx18-driver.h"
-
-/*
- * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
- * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
- * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
- * (should!) look like:
- * 4 byte EAV code: 0xff 0x00 0x00 0xRP
- * unknown number of possible idle bytes
- * 3 byte Anc data preamble: 0x00 0xff 0xff
- * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
- * 1 byte secondary data id: nessssss (parity bits, SDID bits)
- * 1 byte data word count: necccccc (parity bits, NN Dword count)
- * 2 byte Internal DID: VBI-line-# 0x80
- * NN data bytes
- * 1 byte checksum
- * Fill bytes needed to fil out to 4*NN bytes of payload
- *
- * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
- * in the vertical blanking interval are:
- * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
- * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
- *
- * Since the V bit is only allowed to toggle in the EAV RP code, just
- * before the first active region line and for active lines, they are:
- * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
- * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
- *
- * The user application DID bytes we care about are:
- * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
- * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
- *
- */
-static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
-
-struct vbi_anc_data {
- /* u8 eav[4]; */
- /* u8 idle[]; Variable number of idle bytes */
- u8 preamble[3];
- u8 did;
- u8 sdid;
- u8 data_count;
- u8 idid[2];
- u8 payload[1]; /* data_count of payload */
- /* u8 checksum; */
- /* u8 fill[]; Variable number of fill bytes */
-};
-
-static int odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
-static int decode_vps(u8 *dst, u8 *p)
-{
- static const u8 biphase_tbl[] = {
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
- 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
- 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
- 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
- 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
- 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
- 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- };
-
- u8 c, err = 0;
- int i;
-
- for (i = 0; i < 2 * 13; i += 2) {
- err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
- c = (biphase_tbl[p[i + 1]] & 0xf) |
- ((biphase_tbl[p[i]] & 0xf) << 4);
- dst[i / 2] = c;
- }
-
- return err & 0xf0;
-}
-
-int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
- struct cx18_av_state *state = &cx->av_state;
- static const u16 lcr2vbi[] = {
- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
- 0, V4L2_SLICED_WSS_625, 0, /* 4 */
- V4L2_SLICED_CAPTION_525, /* 6 */
- 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
- 0, 0, 0, 0
- };
- int is_pal = !(state->std & V4L2_STD_525_60);
- int i;
-
- memset(svbi, 0, sizeof(*svbi));
- /* we're done if raw VBI is active */
- if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
- return 0;
-
- if (is_pal) {
- for (i = 7; i <= 23; i++) {
- u8 v = cx18_av_read(cx, 0x424 + i - 7);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- } else {
- for (i = 10; i <= 21; i++) {
- u8 v = cx18_av_read(cx, 0x424 + i - 10);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- }
- return 0;
-}
-
-int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
-{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
- struct cx18_av_state *state = &cx->av_state;
-
- /* Setup standard */
- cx18_av_std_setup(cx);
-
- /* VBI Offset */
- cx18_av_write(cx, 0x47f, state->slicer_line_delay);
- cx18_av_write(cx, 0x404, 0x2e);
- return 0;
-}
-
-int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
- struct cx18_av_state *state = &cx->av_state;
- int is_pal = !(state->std & V4L2_STD_525_60);
- int i, x;
- u8 lcr[24];
-
- for (x = 0; x <= 23; x++)
- lcr[x] = 0x00;
-
- /* Setup standard */
- cx18_av_std_setup(cx);
-
- /* Sliced VBI */
- cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
- cx18_av_write(cx, 0x406, 0x13);
- cx18_av_write(cx, 0x47f, state->slicer_line_delay);
-
- /* Force impossible lines to 0 */
- if (is_pal) {
- for (i = 0; i <= 6; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- } else {
- for (i = 0; i <= 9; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
-
- for (i = 22; i <= 23; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- }
-
- /* Build register values for requested service lines */
- for (i = 7; i <= 23; i++) {
- for (x = 0; x <= 1; x++) {
- switch (svbi->service_lines[1-x][i]) {
- case V4L2_SLICED_TELETEXT_B:
- lcr[i] |= 1 << (4 * x);
- break;
- case V4L2_SLICED_WSS_625:
- lcr[i] |= 4 << (4 * x);
- break;
- case V4L2_SLICED_CAPTION_525:
- lcr[i] |= 6 << (4 * x);
- break;
- case V4L2_SLICED_VPS:
- lcr[i] |= 9 << (4 * x);
- break;
- }
- }
- }
-
- if (is_pal) {
- for (x = 1, i = 0x424; i <= 0x434; i++, x++)
- cx18_av_write(cx, i, lcr[6 + x]);
- } else {
- for (x = 1, i = 0x424; i <= 0x430; i++, x++)
- cx18_av_write(cx, i, lcr[9 + x]);
- for (i = 0x431; i <= 0x434; i++)
- cx18_av_write(cx, i, 0);
- }
-
- cx18_av_write(cx, 0x43c, 0x16);
- /* Should match vblank set in cx18_av_std_setup() */
- cx18_av_write(cx, 0x474, is_pal ? 38 : 26);
- return 0;
-}
-
-int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
- struct v4l2_decode_vbi_line *vbi)
-{
- struct cx18 *cx = v4l2_get_subdevdata(sd);
- struct cx18_av_state *state = &cx->av_state;
- struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
- u8 *p;
- int did, sdid, l, err = 0;
-
- /*
- * Check for the ancillary data header for sliced VBI
- */
- if (anc->preamble[0] ||
- anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
- (anc->did != sliced_vbi_did[0] &&
- anc->did != sliced_vbi_did[1])) {
- vbi->line = vbi->type = 0;
- return 0;
- }
-
- did = anc->did;
- sdid = anc->sdid & 0xf;
- l = anc->idid[0] & 0x3f;
- l += state->slicer_line_offset;
- p = anc->payload;
-
- /* Decode the SDID set by the slicer */
- switch (sdid) {
- case 1:
- sdid = V4L2_SLICED_TELETEXT_B;
- break;
- case 4:
- sdid = V4L2_SLICED_WSS_625;
- break;
- case 6:
- sdid = V4L2_SLICED_CAPTION_525;
- err = !odd_parity(p[0]) || !odd_parity(p[1]);
- break;
- case 9:
- sdid = V4L2_SLICED_VPS;
- if (decode_vps(p, p) != 0)
- err = 1;
- break;
- default:
- sdid = 0;
- err = 1;
- break;
- }
-
- vbi->type = err ? 0 : sdid;
- vbi->line = err ? 0 : l;
- vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
- vbi->p = p;
- return 0;
-}
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
deleted file mode 100644
index 7e5ffd6f5178..000000000000
--- a/drivers/media/video/cx18/cx18-driver.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*
- * cx18 driver initialization and card probing
- *
- * Derived from ivtv-driver.c
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- */
-
-#include "cx18-driver.h"
-#include "cx18-io.h"
-#include "cx18-version.h"
-#include "cx18-cards.h"
-#include "cx18-i2c.h"
-#include "cx18-irq.h"
-#include "cx18-gpio.h"
-#include "cx18-firmware.h"
-#include "cx18-queue.h"
-#include "cx18-streams.h"
-#include "cx18-av-core.h"
-#include "cx18-scb.h"
-#include "cx18-mailbox.h"
-#include "cx18-ioctl.h"
-#include "cx18-controls.h"
-#include "tuner-xc2028.h"
-#include <linux/dma-mapping.h>
-#include <media/tveeprom.h>
-
-/* If you have already X v4l cards, then set this to X. This way
- the device numbers stay matched. Example: you have a WinTV card
- without radio and a Compro H900 with. Normally this would give a
- video1 device together with a radio0 device for the Compro. By
- setting this to 1 you ensure that radio0 is now also radio1. */
-int cx18_first_minor;
-
-/* Callback for registering extensions */
-int (*cx18_ext_init)(struct cx18 *);
-EXPORT_SYMBOL(cx18_ext_init);
-
-/* add your revision and whatnot here */
-static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
- {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
-
-static atomic_t cx18_instance = ATOMIC_INIT(0);
-
-/* Parameter declarations */
-static int cardtype[CX18_MAX_CARDS];
-static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
-static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
-static unsigned cardtype_c = 1;
-static unsigned tuner_c = 1;
-static unsigned radio_c = 1;
-static char pal[] = "--";
-static char secam[] = "--";
-static char ntsc[] = "-";
-
-/* Buffers */
-static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
-static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
-static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
-static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
-static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
-static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
-
-static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
-static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
-static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
-static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
-static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
-
-static int enc_ts_bufs = -1;
-static int enc_mpg_bufs = -1;
-static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
-static int enc_yuv_bufs = -1;
-static int enc_vbi_bufs = -1;
-static int enc_pcm_bufs = -1;
-
-
-static int cx18_pci_latency = 1;
-
-static int mmio_ndelay;
-static int retry_mmio = 1;
-
-int cx18_debug;
-
-module_param_array(tuner, int, &tuner_c, 0644);
-module_param_array(radio, int, &radio_c, 0644);
-module_param_array(cardtype, int, &cardtype_c, 0644);
-module_param_string(pal, pal, sizeof(pal), 0644);
-module_param_string(secam, secam, sizeof(secam), 0644);
-module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
-module_param_named(debug, cx18_debug, int, 0644);
-module_param(mmio_ndelay, int, 0644);
-module_param(retry_mmio, int, 0644);
-module_param(cx18_pci_latency, int, 0644);
-module_param(cx18_first_minor, int, 0644);
-
-module_param(enc_ts_buffers, int, 0644);
-module_param(enc_mpg_buffers, int, 0644);
-module_param(enc_idx_buffers, int, 0644);
-module_param(enc_yuv_buffers, int, 0644);
-module_param(enc_vbi_buffers, int, 0644);
-module_param(enc_pcm_buffers, int, 0644);
-
-module_param(enc_ts_bufsize, int, 0644);
-module_param(enc_mpg_bufsize, int, 0644);
-module_param(enc_idx_bufsize, int, 0644);
-module_param(enc_yuv_bufsize, int, 0644);
-module_param(enc_pcm_bufsize, int, 0644);
-
-module_param(enc_ts_bufs, int, 0644);
-module_param(enc_mpg_bufs, int, 0644);
-module_param(enc_idx_bufs, int, 0644);
-module_param(enc_yuv_bufs, int, 0644);
-module_param(enc_vbi_bufs, int, 0644);
-module_param(enc_pcm_bufs, int, 0644);
-
-MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
- "\t\t\tsee tuner.h for values");
-MODULE_PARM_DESC(radio,
- "Enable or disable the radio. Use only if autodetection\n"
- "\t\t\tfails. 0 = disable, 1 = enable");
-MODULE_PARM_DESC(cardtype,
- "Only use this option if your card is not detected properly.\n"
- "\t\tSpecify card type:\n"
- "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
- "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
- "\t\t\t 3 = Compro VideoMate H900\n"
- "\t\t\t 4 = Yuan MPC718\n"
- "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
- "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
- "\t\t\t 7 = Leadtek WinFast PVR2100\n"
- "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
- "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
- "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
- "\t\t\t 0 = Autodetect (default)\n"
- "\t\t\t-1 = Ignore this card\n\t\t");
-MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
-MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
-MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
-MODULE_PARM_DESC(debug,
- "Debug level (bitmask). Default: 0\n"
- "\t\t\t 1/0x0001: warning\n"
- "\t\t\t 2/0x0002: info\n"
- "\t\t\t 4/0x0004: mailbox\n"
- "\t\t\t 8/0x0008: dma\n"
- "\t\t\t 16/0x0010: ioctl\n"
- "\t\t\t 32/0x0020: file\n"
- "\t\t\t 64/0x0040: i2c\n"
- "\t\t\t128/0x0080: irq\n"
- "\t\t\t256/0x0100: high volume\n");
-MODULE_PARM_DESC(cx18_pci_latency,
- "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
- "\t\t\tDefault: Yes");
-MODULE_PARM_DESC(retry_mmio,
- "(Deprecated) MMIO writes are now always checked and retried\n"
- "\t\t\tEffectively: 1 [Yes]");
-MODULE_PARM_DESC(mmio_ndelay,
- "(Deprecated) MMIO accesses are now never purposely delayed\n"
- "\t\t\tEffectively: 0 ns");
-MODULE_PARM_DESC(enc_ts_buffers,
- "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
-MODULE_PARM_DESC(enc_ts_bufsize,
- "Size of an encoder TS buffer (kB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
-MODULE_PARM_DESC(enc_ts_bufs,
- "Number of encoder TS buffers\n"
- "\t\t\tDefault is computed from other enc_ts_* parameters");
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
-MODULE_PARM_DESC(enc_mpg_bufsize,
- "Size of an encoder MPG buffer (kB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
-MODULE_PARM_DESC(enc_mpg_bufs,
- "Number of encoder MPG buffers\n"
- "\t\t\tDefault is computed from other enc_mpg_* parameters");
-MODULE_PARM_DESC(enc_idx_buffers,
- "(Deprecated) Encoder IDX buffer memory (MB)\n"
- "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
- "\t\t\tDefault: 1 [Enabled]");
-MODULE_PARM_DESC(enc_idx_bufsize,
- "Size of an encoder IDX buffer (kB)\n"
- "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
- "\t\t\t(multiples of size required for 64 index entries)\n"
- "\t\t\tDefault: 2");
-MODULE_PARM_DESC(enc_idx_bufs,
- "Number of encoder IDX buffers\n"
- "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
-MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
-MODULE_PARM_DESC(enc_yuv_bufsize,
- "Size of an encoder YUV buffer (kB)\n"
- "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
- "\t\t\t(multiples of size required for 32 screen lines)\n"
- "\t\t\tDefault: 102");
-MODULE_PARM_DESC(enc_yuv_bufs,
- "Number of encoder YUV buffers\n"
- "\t\t\tDefault is computed from other enc_yuv_* parameters");
-MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
-MODULE_PARM_DESC(enc_vbi_bufs,
- "Number of encoder VBI buffers\n"
- "\t\t\tDefault is computed from enc_vbi_buffers");
-MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
-MODULE_PARM_DESC(enc_pcm_bufsize,
- "Size of an encoder PCM buffer (kB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
-MODULE_PARM_DESC(enc_pcm_bufs,
- "Number of encoder PCM buffers\n"
- "\t\t\tDefault is computed from other enc_pcm_* parameters");
-
-MODULE_PARM_DESC(cx18_first_minor,
- "Set device node number assigned to first card");
-
-MODULE_AUTHOR("Hans Verkuil");
-MODULE_DESCRIPTION("CX23418 driver");
-MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
-MODULE_LICENSE("GPL");
-
-MODULE_VERSION(CX18_VERSION);
-
-#if defined(CONFIG_MODULES) && defined(MODULE)
-static void request_module_async(struct work_struct *work)
-{
- struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
-
- /* Make sure cx18-alsa module is loaded */
- request_module("cx18-alsa");
-
- /* Initialize cx18-alsa for this instance of the cx18 device */
- if (cx18_ext_init != NULL)
- cx18_ext_init(dev);
-}
-
-static void request_modules(struct cx18 *dev)
-{
- INIT_WORK(&dev->request_module_wk, request_module_async);
- schedule_work(&dev->request_module_wk);
-}
-
-static void flush_request_modules(struct cx18 *dev)
-{
- flush_work_sync(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#define flush_request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-/* Generic utility functions */
-int cx18_msleep_timeout(unsigned int msecs, int intr)
-{
- long int timeout = msecs_to_jiffies(msecs);
- int sig;
-
- do {
- set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
- timeout = schedule_timeout(timeout);
- sig = intr ? signal_pending(current) : 0;
- } while (!sig && timeout);
- return sig;
-}
-
-/* Release ioremapped memory */
-static void cx18_iounmap(struct cx18 *cx)
-{
- if (cx == NULL)
- return;
-
- /* Release io memory */
- if (cx->enc_mem != NULL) {
- CX18_DEBUG_INFO("releasing enc_mem\n");
- iounmap(cx->enc_mem);
- cx->enc_mem = NULL;
- }
-}
-
-static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
-{
- int i;
-
- CX18_INFO("eeprom dump:\n");
- for (i = 0; i < len; i++) {
- if (0 == (i % 16))
- CX18_INFO("eeprom %02x:", i);
- printk(KERN_CONT " %02x", eedata[i]);
- if (15 == (i % 16))
- printk(KERN_CONT "\n");
- }
-}
-
-/* Hauppauge card? get values from tveeprom */
-void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
-{
- struct i2c_client c;
- u8 eedata[256];
-
- memset(&c, 0, sizeof(c));
- strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
- c.adapter = &cx->i2c_adap[0];
- c.addr = 0xA0 >> 1;
-
- memset(tv, 0, sizeof(*tv));
- if (tveeprom_read(&c, eedata, sizeof(eedata)))
- return;
-
- switch (cx->card->type) {
- case CX18_CARD_HVR_1600_ESMT:
- case CX18_CARD_HVR_1600_SAMSUNG:
- case CX18_CARD_HVR_1600_S5H1411:
- tveeprom_hauppauge_analog(&c, tv, eedata);
- break;
- case CX18_CARD_YUAN_MPC718:
- case CX18_CARD_GOTVIEW_PCI_DVD3:
- tv->model = 0x718;
- cx18_eeprom_dump(cx, eedata, sizeof(eedata));
- CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
- eedata[2], eedata[1], eedata[4], eedata[3]);
- break;
- default:
- tv->model = 0xffffffff;
- cx18_eeprom_dump(cx, eedata, sizeof(eedata));
- break;
- }
-}
-
-static void cx18_process_eeprom(struct cx18 *cx)
-{
- struct tveeprom tv;
-
- cx18_read_eeprom(cx, &tv);
-
- /* Many thanks to Steven Toth from Hauppauge for providing the
- model numbers */
- /* Note: the Samsung memory models cannot be reliably determined
- from the model number. Use the cardtype module option if you
- have one of these preproduction models. */
- switch (tv.model) {
- case 74301: /* Retail models */
- case 74321:
- case 74351: /* OEM models */
- case 74361:
- /* Digital side is s5h1411/tda18271 */
- cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
- break;
- case 74021: /* Retail models */
- case 74031:
- case 74041:
- case 74141:
- case 74541: /* OEM models */
- case 74551:
- case 74591:
- case 74651:
- case 74691:
- case 74751:
- case 74891:
- /* Digital side is s5h1409/mxl5005s */
- cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
- break;
- case 0x718:
- return;
- case 0xffffffff:
- CX18_INFO("Unknown EEPROM encoding\n");
- return;
- case 0:
- CX18_ERR("Invalid EEPROM\n");
- return;
- default:
- CX18_ERR("Unknown model %d, defaulting to original HVR-1600 "
- "(cardtype=1)\n", tv.model);
- cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
- break;
- }
-
- cx->v4l2_cap = cx->card->v4l2_capabilities;
- cx->card_name = cx->card->name;
- cx->card_i2c = cx->card->i2c;
-
- CX18_INFO("Autodetected %s\n", cx->card_name);
-
- if (tv.tuner_type == TUNER_ABSENT)
- CX18_ERR("tveeprom cannot autodetect tuner!\n");
-
- if (cx->options.tuner == -1)
- cx->options.tuner = tv.tuner_type;
- if (cx->options.radio == -1)
- cx->options.radio = (tv.has_radio != 0);
-
- if (cx->std != 0)
- /* user specified tuner standard */
- return;
-
- /* autodetect tuner standard */
-#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \
- V4L2_STD_MN | \
- V4L2_STD_PAL_I | \
- V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
- V4L2_STD_DK)
- if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
- == TVEEPROM_TUNER_FORMAT_ALL) {
- CX18_DEBUG_INFO("Worldwide tuner detected\n");
- cx->std = V4L2_STD_ALL;
- } else if (tv.tuner_formats & V4L2_STD_PAL) {
- CX18_DEBUG_INFO("PAL tuner detected\n");
- cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
- } else if (tv.tuner_formats & V4L2_STD_NTSC) {
- CX18_DEBUG_INFO("NTSC tuner detected\n");
- cx->std |= V4L2_STD_NTSC_M;
- } else if (tv.tuner_formats & V4L2_STD_SECAM) {
- CX18_DEBUG_INFO("SECAM tuner detected\n");
- cx->std |= V4L2_STD_SECAM_L;
- } else {
- CX18_INFO("No tuner detected, default to NTSC-M\n");
- cx->std |= V4L2_STD_NTSC_M;
- }
-}
-
-static v4l2_std_id cx18_parse_std(struct cx18 *cx)
-{
- switch (pal[0]) {
- case '6':
- return V4L2_STD_PAL_60;
- case 'b':
- case 'B':
- case 'g':
- case 'G':
- return V4L2_STD_PAL_BG;
- case 'h':
- case 'H':
- return V4L2_STD_PAL_H;
- case 'n':
- case 'N':
- if (pal[1] == 'c' || pal[1] == 'C')
- return V4L2_STD_PAL_Nc;
- return V4L2_STD_PAL_N;
- case 'i':
- case 'I':
- return V4L2_STD_PAL_I;
- case 'd':
- case 'D':
- case 'k':
- case 'K':
- return V4L2_STD_PAL_DK;
- case 'M':
- case 'm':
- return V4L2_STD_PAL_M;
- case '-':
- break;
- default:
- CX18_WARN("pal= argument not recognised\n");
- return 0;
- }
-
- switch (secam[0]) {
- case 'b':
- case 'B':
- case 'g':
- case 'G':
- case 'h':
- case 'H':
- return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
- case 'd':
- case 'D':
- case 'k':
- case 'K':
- return V4L2_STD_SECAM_DK;
- case 'l':
- case 'L':
- if (secam[1] == 'C' || secam[1] == 'c')
- return V4L2_STD_SECAM_LC;
- return V4L2_STD_SECAM_L;
- case '-':
- break;
- default:
- CX18_WARN("secam= argument not recognised\n");
- return 0;
- }
-
- switch (ntsc[0]) {
- case 'm':
- case 'M':
- return V4L2_STD_NTSC_M;
- case 'j':
- case 'J':
- return V4L2_STD_NTSC_M_JP;
- case 'k':
- case 'K':
- return V4L2_STD_NTSC_M_KR;
- case '-':
- break;
- default:
- CX18_WARN("ntsc= argument not recognised\n");
- return 0;
- }
-
- /* no match found */
- return 0;
-}
-
-static void cx18_process_options(struct cx18 *cx)
-{
- int i, j;
-
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
-
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
- cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
-
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
-
- /* Ensure stream_buffers & stream_buf_size are valid */
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */
- cx->options.megabytes[i] <= 0 || /* User said 0 MB total */
- cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */
- cx->options.megabytes[i] = 0;
- cx->stream_buffers[i] = 0;
- cx->stream_buf_size[i] = 0;
- continue;
- }
- /*
- * YUV is a special case where the stream_buf_size needs to be
- * an integral multiple of 33.75 kB (storage for 32 screens
- * lines to maintain alignment in case of lost buffers).
- *
- * IDX is a special case where the stream_buf_size should be
- * an integral multiple of 1.5 kB (storage for 64 index entries
- * to maintain alignment in case of lost buffers).
- *
- */
- if (i == CX18_ENC_STREAM_TYPE_YUV) {
- cx->stream_buf_size[i] *= 1024;
- cx->stream_buf_size[i] -=
- (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
-
- if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
- cx->stream_buf_size[i] =
- CX18_UNIT_ENC_YUV_BUFSIZE;
- } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
- cx->stream_buf_size[i] *= 1024;
- cx->stream_buf_size[i] -=
- (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
-
- if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
- cx->stream_buf_size[i] =
- CX18_UNIT_ENC_IDX_BUFSIZE;
- }
- /*
- * YUV and IDX are special cases where the stream_buf_size is
- * now in bytes.
- * VBI is a special case where the stream_buf_size is fixed
- * and already in bytes
- */
- if (i == CX18_ENC_STREAM_TYPE_VBI ||
- i == CX18_ENC_STREAM_TYPE_YUV ||
- i == CX18_ENC_STREAM_TYPE_IDX) {
- if (cx->stream_buffers[i] < 0) {
- cx->stream_buffers[i] =
- cx->options.megabytes[i] * 1024 * 1024
- / cx->stream_buf_size[i];
- } else {
- /* N.B. This might round down to 0 */
- cx->options.megabytes[i] =
- cx->stream_buffers[i]
- * cx->stream_buf_size[i]/(1024 * 1024);
- }
- } else {
- /* All other streams have stream_buf_size in kB here */
- if (cx->stream_buffers[i] < 0) {
- cx->stream_buffers[i] =
- cx->options.megabytes[i] * 1024
- / cx->stream_buf_size[i];
- } else {
- /* N.B. This might round down to 0 */
- cx->options.megabytes[i] =
- cx->stream_buffers[i]
- * cx->stream_buf_size[i] / 1024;
- }
- /* convert from kB to bytes */
- cx->stream_buf_size[i] *= 1024;
- }
- CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, "
- "%d bytes\n", i, cx->options.megabytes[i],
- cx->stream_buffers[i], cx->stream_buf_size[i]);
- }
-
- cx->options.cardtype = cardtype[cx->instance];
- cx->options.tuner = tuner[cx->instance];
- cx->options.radio = radio[cx->instance];
-
- cx->std = cx18_parse_std(cx);
- if (cx->options.cardtype == -1) {
- CX18_INFO("Ignore card\n");
- return;
- }
- cx->card = cx18_get_card(cx->options.cardtype - 1);
- if (cx->card)
- CX18_INFO("User specified %s card\n", cx->card->name);
- else if (cx->options.cardtype != 0)
- CX18_ERR("Unknown user specified type, trying to autodetect card\n");
- if (cx->card == NULL) {
- if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
- cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
- CX18_INFO("Autodetected Hauppauge card\n");
- }
- }
- if (cx->card == NULL) {
- for (i = 0; (cx->card = cx18_get_card(i)); i++) {
- if (cx->card->pci_list == NULL)
- continue;
- for (j = 0; cx->card->pci_list[j].device; j++) {
- if (cx->pci_dev->device !=
- cx->card->pci_list[j].device)
- continue;
- if (cx->pci_dev->subsystem_vendor !=
- cx->card->pci_list[j].subsystem_vendor)
- continue;
- if (cx->pci_dev->subsystem_device !=
- cx->card->pci_list[j].subsystem_device)
- continue;
- CX18_INFO("Autodetected %s card\n", cx->card->name);
- goto done;
- }
- }
- }
-done:
-
- if (cx->card == NULL) {
- cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
- CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
- cx->pci_dev->vendor, cx->pci_dev->device);
- CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
- cx->pci_dev->subsystem_vendor,
- cx->pci_dev->subsystem_device);
- CX18_ERR("Defaulting to %s card\n", cx->card->name);
- CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
- CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
- CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
- }
- cx->v4l2_cap = cx->card->v4l2_capabilities;
- cx->card_name = cx->card->name;
- cx->card_i2c = cx->card->i2c;
-}
-
-static int __devinit cx18_create_in_workq(struct cx18 *cx)
-{
- snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
- cx->v4l2_dev.name);
- cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0);
- if (cx->in_work_queue == NULL) {
- CX18_ERR("Unable to create incoming mailbox handler thread\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-static void __devinit cx18_init_in_work_orders(struct cx18 *cx)
-{
- int i;
- for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
- cx->in_work_order[i].cx = cx;
- cx->in_work_order[i].str = cx->epu_debug_str;
- INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
- }
-}
-
-/* Precondition: the cx18 structure has been memset to 0. Only
- the dev and instance fields have been filled in.
- No assumptions on the card type may be made here (see cx18_init_struct2
- for that).
- */
-static int __devinit cx18_init_struct1(struct cx18 *cx)
-{
- int ret;
-
- cx->base_addr = pci_resource_start(cx->pci_dev, 0);
-
- mutex_init(&cx->serialize_lock);
- mutex_init(&cx->gpio_lock);
- mutex_init(&cx->epu2apu_mb_lock);
- mutex_init(&cx->epu2cpu_mb_lock);
-
- ret = cx18_create_in_workq(cx);
- if (ret)
- return ret;
-
- cx18_init_in_work_orders(cx);
-
- /* start counting open_id at 1 */
- cx->open_id = 1;
-
- /* Initial settings */
- cx->cxhdl.port = CX2341X_PORT_MEMORY;
- cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
- cx->cxhdl.ops = &cx18_cxhdl_ops;
- cx->cxhdl.func = cx18_api_func;
- cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
- ret = cx2341x_handler_init(&cx->cxhdl, 50);
- if (ret)
- return ret;
- cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
-
- cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
- cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
- cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
- (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
- (cx->cxhdl.video_median_filter_type->cur.val << 2);
-
- init_waitqueue_head(&cx->cap_w);
- init_waitqueue_head(&cx->mb_apu_waitq);
- init_waitqueue_head(&cx->mb_cpu_waitq);
- init_waitqueue_head(&cx->dma_waitq);
-
- /* VBI */
- cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
-
- /* IVTV style VBI insertion into MPEG streams */
- INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
- INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
- INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
- list_add(&cx->vbi.sliced_mpeg_buf.list,
- &cx->vbi.sliced_mpeg_mdl.buf_list);
- return 0;
-}
-
-/* Second initialization part. Here the card type has been
- autodetected. */
-static void __devinit cx18_init_struct2(struct cx18 *cx)
-{
- int i;
-
- for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
- if (cx->card->video_inputs[i].video_type == 0)
- break;
- cx->nof_inputs = i;
- for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
- if (cx->card->audio_inputs[i].audio_type == 0)
- break;
- cx->nof_audio_inputs = i;
-
- /* Find tuner input */
- for (i = 0; i < cx->nof_inputs; i++) {
- if (cx->card->video_inputs[i].video_type ==
- CX18_CARD_INPUT_VID_TUNER)
- break;
- }
- if (i == cx->nof_inputs)
- i = 0;
- cx->active_input = i;
- cx->audio_input = cx->card->video_inputs[i].audio_index;
-}
-
-static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
-{
- u16 cmd;
- unsigned char pci_latency;
-
- CX18_DEBUG_INFO("Enabling pci device\n");
-
- if (pci_enable_device(pci_dev)) {
- CX18_ERR("Can't enable device %d!\n", cx->instance);
- return -EIO;
- }
- if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
- CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
- return -EIO;
- }
- if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
- CX18_ERR("Cannot request encoder memory region, card %d\n",
- cx->instance);
- return -EIO;
- }
-
- /* Enable bus mastering and memory mapped IO for the CX23418 */
- pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
- cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
- pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
-
- cx->card_rev = pci_dev->revision;
- pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
-
- if (pci_latency < 64 && cx18_pci_latency) {
- CX18_INFO("Unreasonably low latency timer, "
- "setting to 64 (was %d)\n", pci_latency);
- pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
- pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
- }
-
- CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
- "irq: %d, latency: %d, memory: 0x%llx\n",
- cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
- PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
- cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
-
- return 0;
-}
-
-static void cx18_init_subdevs(struct cx18 *cx)
-{
- u32 hw = cx->card->hw_all;
- u32 device;
- int i;
-
- for (i = 0, device = 1; i < 32; i++, device <<= 1) {
-
- if (!(device & hw))
- continue;
-
- switch (device) {
- case CX18_HW_DVB:
- case CX18_HW_TVEEPROM:
- /* These subordinate devices do not use probing */
- cx->hw_flags |= device;
- break;
- case CX18_HW_418_AV:
- /* The A/V decoder gets probed earlier to set PLLs */
- /* Just note that the card uses it (i.e. has analog) */
- cx->hw_flags |= device;
- break;
- case CX18_HW_GPIO_RESET_CTRL:
- /*
- * The Reset Controller gets probed and added to
- * hw_flags earlier for i2c adapter/bus initialization
- */
- break;
- case CX18_HW_GPIO_MUX:
- if (cx18_gpio_register(cx, device) == 0)
- cx->hw_flags |= device;
- break;
- default:
- if (cx18_i2c_register(cx, i) == 0)
- cx->hw_flags |= device;
- break;
- }
- }
-
- if (cx->hw_flags & CX18_HW_418_AV)
- cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
-
- if (cx->card->hw_muxer != 0)
- cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
-}
-
-static int __devinit cx18_probe(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
-{
- int retval = 0;
- int i;
- u32 devtype;
- struct cx18 *cx;
-
- /* FIXME - module parameter arrays constrain max instances */
- i = atomic_inc_return(&cx18_instance) - 1;
- if (i >= CX18_MAX_CARDS) {
- printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
- "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
- return -ENOMEM;
- }
-
- cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
- if (cx == NULL) {
- printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
- i);
- return -ENOMEM;
- }
- cx->pci_dev = pci_dev;
- cx->instance = i;
-
- retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
- if (retval) {
- printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
- "\n", cx->instance);
- kfree(cx);
- return retval;
- }
- snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
- cx->instance);
- CX18_INFO("Initializing card %d\n", cx->instance);
-
- cx18_process_options(cx);
- if (cx->options.cardtype == -1) {
- retval = -ENODEV;
- goto err;
- }
-
- retval = cx18_init_struct1(cx);
- if (retval)
- goto err;
-
- CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
-
- /* PCI Device Setup */
- retval = cx18_setup_pci(cx, pci_dev, pci_id);
- if (retval != 0)
- goto free_workqueues;
-
- /* map io memory */
- CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
- (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
- cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
- CX18_MEM_SIZE);
- if (!cx->enc_mem) {
- CX18_ERR("ioremap failed. Can't get a window into CX23418 "
- "memory and register space\n");
- CX18_ERR("Each capture card with a CX23418 needs 64 MB of "
- "vmalloc address space for the window\n");
- CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
- CX18_ERR("Use the vmalloc= kernel command line option to set "
- "VmallocTotal to a larger value\n");
- retval = -ENOMEM;
- goto free_mem;
- }
- cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
- devtype = cx18_read_reg(cx, 0xC72028);
- switch (devtype & 0xff000000) {
- case 0xff000000:
- CX18_INFO("cx23418 revision %08x (A)\n", devtype);
- break;
- case 0x01000000:
- CX18_INFO("cx23418 revision %08x (B)\n", devtype);
- break;
- default:
- CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
- break;
- }
-
- cx18_init_power(cx, 1);
- cx18_init_memory(cx);
-
- cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
- cx18_init_scb(cx);
-
- cx18_gpio_init(cx);
-
- /* Initialize integrated A/V decoder early to set PLLs, just in case */
- retval = cx18_av_probe(cx);
- if (retval) {
- CX18_ERR("Could not register A/V decoder subdevice\n");
- goto free_map;
- }
-
- /* Initialize GPIO Reset Controller to do chip resets during i2c init */
- if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
- if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
- CX18_WARN("Could not register GPIO reset controller"
- "subdevice; proceeding anyway.\n");
- else
- cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
- }
-
- /* active i2c */
- CX18_DEBUG_INFO("activating i2c...\n");
- retval = init_cx18_i2c(cx);
- if (retval) {
- CX18_ERR("Could not initialize i2c\n");
- goto free_map;
- }
-
- if (cx->card->hw_all & CX18_HW_TVEEPROM) {
- /* Based on the model number the cardtype may be changed.
- The PCI IDs are not always reliable. */
- const struct cx18_card *orig_card = cx->card;
- cx18_process_eeprom(cx);
-
- if (cx->card != orig_card) {
- /* Changed the cardtype; re-reset the I2C chips */
- cx18_gpio_init(cx);
- cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
- core, reset, (u32) CX18_GPIO_RESET_I2C);
- }
- }
- if (cx->card->comment)
- CX18_INFO("%s", cx->card->comment);
- if (cx->card->v4l2_capabilities == 0) {
- retval = -ENODEV;
- goto free_i2c;
- }
- cx18_init_memory(cx);
- cx18_init_scb(cx);
-
- /* Register IRQ */
- retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
- IRQF_SHARED | IRQF_DISABLED,
- cx->v4l2_dev.name, (void *)cx);
- if (retval) {
- CX18_ERR("Failed to register irq %d\n", retval);
- goto free_i2c;
- }
-
- if (cx->std == 0)
- cx->std = V4L2_STD_NTSC_M;
-
- if (cx->options.tuner == -1) {
- for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
- if ((cx->std & cx->card->tuners[i].std) == 0)
- continue;
- cx->options.tuner = cx->card->tuners[i].tuner;
- break;
- }
- }
- /* if no tuner was found, then pick the first tuner in the card list */
- if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
- cx->std = cx->card->tuners[0].std;
- if (cx->std & V4L2_STD_PAL)
- cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
- else if (cx->std & V4L2_STD_NTSC)
- cx->std = V4L2_STD_NTSC_M;
- else if (cx->std & V4L2_STD_SECAM)
- cx->std = V4L2_STD_SECAM_L;
- cx->options.tuner = cx->card->tuners[0].tuner;
- }
- if (cx->options.radio == -1)
- cx->options.radio = (cx->card->radio_input.audio_type != 0);
-
- /* The card is now fully identified, continue with card-specific
- initialization. */
- cx18_init_struct2(cx);
-
- cx18_init_subdevs(cx);
-
- if (cx->std & V4L2_STD_525_60)
- cx->is_60hz = 1;
- else
- cx->is_50hz = 1;
-
- cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
-
- if (cx->options.radio > 0)
- cx->v4l2_cap |= V4L2_CAP_RADIO;
-
- if (cx->options.tuner > -1) {
- struct tuner_setup setup;
-
- setup.addr = ADDR_UNSET;
- setup.type = cx->options.tuner;
- setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
- if (cx->options.radio > 0)
- setup.mode_mask |= T_RADIO;
- setup.tuner_callback = (setup.type == TUNER_XC2028) ?
- cx18_reset_tuner_gpio : NULL;
- cx18_call_all(cx, tuner, s_type_addr, &setup);
- if (setup.type == TUNER_XC2028) {
- static struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- };
- struct v4l2_priv_tun_config cfg = {
- .tuner = cx->options.tuner,
- .priv = &ctrl,
- };
- cx18_call_all(cx, tuner, s_config, &cfg);
- }
- }
-
- /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
- are not. */
- cx->tuner_std = cx->std;
- if (cx->std == V4L2_STD_ALL)
- cx->std = V4L2_STD_NTSC_M;
-
- retval = cx18_streams_setup(cx);
- if (retval) {
- CX18_ERR("Error %d setting up streams\n", retval);
- goto free_irq;
- }
- retval = cx18_streams_register(cx);
- if (retval) {
- CX18_ERR("Error %d registering devices\n", retval);
- goto free_streams;
- }
-
- CX18_INFO("Initialized card: %s\n", cx->card_name);
-
- /* Load cx18 submodules (cx18-alsa) */
- request_modules(cx);
- return 0;
-
-free_streams:
- cx18_streams_cleanup(cx, 1);
-free_irq:
- free_irq(cx->pci_dev->irq, (void *)cx);
-free_i2c:
- exit_cx18_i2c(cx);
-free_map:
- cx18_iounmap(cx);
-free_mem:
- release_mem_region(cx->base_addr, CX18_MEM_SIZE);
-free_workqueues:
- destroy_workqueue(cx->in_work_queue);
-err:
- if (retval == 0)
- retval = -ENODEV;
- CX18_ERR("Error %d on initialization\n", retval);
-
- v4l2_device_unregister(&cx->v4l2_dev);
- kfree(cx);
- return retval;
-}
-
-int cx18_init_on_first_open(struct cx18 *cx)
-{
- int video_input;
- int fw_retry_count = 3;
- struct v4l2_frequency vf;
- struct cx18_open_id fh;
- v4l2_std_id std;
-
- fh.cx = cx;
-
- if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
- return -ENXIO;
-
- if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
- return 0;
-
- while (--fw_retry_count > 0) {
- /* load firmware */
- if (cx18_firmware_init(cx) == 0)
- break;
- if (fw_retry_count > 1)
- CX18_WARN("Retry loading firmware\n");
- }
-
- if (fw_retry_count == 0) {
- set_bit(CX18_F_I_FAILED, &cx->i_flags);
- return -ENXIO;
- }
- set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
-
- /*
- * Init the firmware twice to work around a silicon bug
- * with the digital TS.
- *
- * The second firmware load requires us to normalize the APU state,
- * or the audio for the first analog capture will be badly incorrect.
- *
- * I can't seem to call APU_RESETAI and have it succeed without the
- * APU capturing audio, so we start and stop it here to do the reset
- */
-
- /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
- cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
- cx18_vapi(cx, CX18_APU_RESETAI, 0);
- cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
-
- fw_retry_count = 3;
- while (--fw_retry_count > 0) {
- /* load firmware */
- if (cx18_firmware_init(cx) == 0)
- break;
- if (fw_retry_count > 1)
- CX18_WARN("Retry loading firmware\n");
- }
-
- if (fw_retry_count == 0) {
- set_bit(CX18_F_I_FAILED, &cx->i_flags);
- return -ENXIO;
- }
-
- /*
- * The second firmware load requires us to normalize the APU state,
- * or the audio for the first analog capture will be badly incorrect.
- *
- * I can't seem to call APU_RESETAI and have it succeed without the
- * APU capturing audio, so we start and stop it here to do the reset
- */
-
- /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
- cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
- cx18_vapi(cx, CX18_APU_RESETAI, 0);
- cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
-
- /* Init the A/V decoder, if it hasn't been already */
- v4l2_subdev_call(cx->sd_av, core, load_fw);
-
- vf.tuner = 0;
- vf.type = V4L2_TUNER_ANALOG_TV;
- vf.frequency = 6400; /* the tuner 'baseline' frequency */
-
- /* Set initial frequency. For PAL/SECAM broadcasts no
- 'default' channel exists AFAIK. */
- if (cx->std == V4L2_STD_NTSC_M_JP)
- vf.frequency = 1460; /* ch. 1 91250*16/1000 */
- else if (cx->std & V4L2_STD_NTSC_M)
- vf.frequency = 1076; /* ch. 4 67250*16/1000 */
-
- video_input = cx->active_input;
- cx->active_input++; /* Force update of input */
- cx18_s_input(NULL, &fh, video_input);
-
- /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
- in one place. */
- cx->std++; /* Force full standard initialization */
- std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
- cx18_s_std(NULL, &fh, &std);
- cx18_s_frequency(NULL, &fh, &vf);
- return 0;
-}
-
-static void cx18_cancel_in_work_orders(struct cx18 *cx)
-{
- int i;
- for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
- cancel_work_sync(&cx->in_work_order[i].work);
-}
-
-static void cx18_cancel_out_work_orders(struct cx18 *cx)
-{
- int i;
- for (i = 0; i < CX18_MAX_STREAMS; i++)
- if (&cx->streams[i].video_dev != NULL)
- cancel_work_sync(&cx->streams[i].out_work_order);
-}
-
-static void cx18_remove(struct pci_dev *pci_dev)
-{
- struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
- struct cx18 *cx = to_cx18(v4l2_dev);
- int i;
-
- CX18_DEBUG_INFO("Removing Card\n");
-
- flush_request_modules(cx);
-
- /* Stop all captures */
- CX18_DEBUG_INFO("Stopping all streams\n");
- if (atomic_read(&cx->tot_capturing) > 0)
- cx18_stop_all_captures(cx);
-
- /* Stop interrupts that cause incoming work to be queued */
- cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
-
- /* Incoming work can cause outgoing work, so clean up incoming first */
- cx18_cancel_in_work_orders(cx);
- cx18_cancel_out_work_orders(cx);
-
- /* Stop ack interrupts that may have been needed for work to finish */
- cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
-
- cx18_halt_firmware(cx);
-
- destroy_workqueue(cx->in_work_queue);
-
- cx18_streams_cleanup(cx, 1);
-
- exit_cx18_i2c(cx);
-
- free_irq(cx->pci_dev->irq, (void *)cx);
-
- cx18_iounmap(cx);
-
- release_mem_region(cx->base_addr, CX18_MEM_SIZE);
-
- pci_disable_device(cx->pci_dev);
-
- if (cx->vbi.sliced_mpeg_data[0] != NULL)
- for (i = 0; i < CX18_VBI_FRAMES; i++)
- kfree(cx->vbi.sliced_mpeg_data[i]);
-
- v4l2_ctrl_handler_free(&cx->av_state.hdl);
-
- CX18_INFO("Removed %s\n", cx->card_name);
-
- v4l2_device_unregister(v4l2_dev);
- kfree(cx);
-}
-
-
-/* define a pci_driver for card detection */
-static struct pci_driver cx18_pci_driver = {
- .name = "cx18",
- .id_table = cx18_pci_tbl,
- .probe = cx18_probe,
- .remove = cx18_remove,
-};
-
-static int __init module_start(void)
-{
- printk(KERN_INFO "cx18: Start initialization, version %s\n",
- CX18_VERSION);
-
- /* Validate parameters */
- if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
- printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
- CX18_MAX_CARDS - 1);
- return -1;
- }
-
- if (cx18_debug < 0 || cx18_debug > 511) {
- cx18_debug = 0;
- printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
- }
-
- if (pci_register_driver(&cx18_pci_driver)) {
- printk(KERN_ERR "cx18: Error detecting PCI card\n");
- return -ENODEV;
- }
- printk(KERN_INFO "cx18: End initialization\n");
- return 0;
-}
-
-static void __exit module_cleanup(void)
-{
- pci_unregister_driver(&cx18_pci_driver);
-}
-
-module_init(module_start);
-module_exit(module_cleanup);
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
deleted file mode 100644
index f41922bd4020..000000000000
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * cx18 functions for DVB support
- *
- * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include "cx18-version.h"
-#include "cx18-dvb.h"
-#include "cx18-io.h"
-#include "cx18-queue.h"
-#include "cx18-streams.h"
-#include "cx18-cards.h"
-#include "cx18-gpio.h"
-#include "s5h1409.h"
-#include "mxl5005s.h"
-#include "s5h1411.h"
-#include "tda18271.h"
-#include "zl10353.h"
-
-#include <linux/firmware.h>
-#include "mt352.h"
-#include "mt352_priv.h"
-#include "tuner-xc2028.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
-#define CX18_CLOCK_ENABLE2 0xc71024
-#define CX18_DMUX_CLK_MASK 0x0080
-
-/*
- * CX18_CARD_HVR_1600_ESMT
- * CX18_CARD_HVR_1600_SAMSUNG
- */
-
-static struct mxl5005s_config hauppauge_hvr1600_tuner = {
- .i2c_address = 0xC6 >> 1,
- .if_freq = IF_FREQ_5380000HZ,
- .xtal_freq = CRYSTAL_FREQ_16000000HZ,
- .agc_mode = MXL_SINGLE_AGC,
- .tracking_filter = MXL_TF_C_H,
- .rssi_enable = MXL_RSSI_ENABLE,
- .cap_select = MXL_CAP_SEL_ENABLE,
- .div_out = MXL_DIV_OUT_4,
- .clock_out = MXL_CLOCK_OUT_DISABLE,
- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
- .top = MXL5005S_TOP_25P2,
- .mod_mode = MXL_DIGITAL_MODE,
- .if_mode = MXL_ZERO_IF,
- .qam_gain = 0x02,
- .AgcMasterByte = 0x00,
-};
-
-static struct s5h1409_config hauppauge_hvr1600_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_ON,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
- .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE
-};
-
-/*
- * CX18_CARD_HVR_1600_S5H1411
- */
-static struct s5h1411_config hcw_s5h1411_config = {
- .output_mode = S5H1411_SERIAL_OUTPUT,
- .gpio = S5H1411_GPIO_OFF,
- .vsb_if = S5H1411_IF_44000,
- .qam_if = S5H1411_IF_4000,
- .inversion = S5H1411_INVERSION_ON,
- .status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct tda18271_std_map hauppauge_tda18271_std_map = {
- .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
- .if_lvl = 6, .rfagc_top = 0x37 },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
- .if_lvl = 6, .rfagc_top = 0x37 },
-};
-
-static struct tda18271_config hauppauge_tda18271_config = {
- .std_map = &hauppauge_tda18271_std_map,
- .gate = TDA18271_GATE_DIGITAL,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-/*
- * CX18_CARD_LEADTEK_DVR3100H
- */
-/* Information/confirmation of proper config values provided by Terry Wu */
-static struct zl10353_config leadtek_dvr3100h_demod = {
- .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
- .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
- .parallel_ts = 1, /* Not a serial TS */
- .no_tuner = 1, /* XC3028 is not behind the gate */
- .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
-};
-
-/*
- * CX18_CARD_YUAN_MPC718
- */
-/*
- * Due to
- *
- * 1. an absence of information on how to prgram the MT352
- * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
- *
- * We have to use an init sequence that *you* must extract from the Windows
- * driver (yuanrap.sys) and which we load as a firmware.
- *
- * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual
- * with chip programming details, then I can remove this annoyance.
- */
-static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
- const struct firmware **fw)
-{
- struct cx18 *cx = stream->cx;
- const char *fn = "dvb-cx18-mpc718-mt352.fw";
- int ret;
-
- ret = request_firmware(fw, fn, &cx->pci_dev->dev);
- if (ret)
- CX18_ERR("Unable to open firmware file %s\n", fn);
- else {
- size_t sz = (*fw)->size;
- if (sz < 2 || sz > 64 || (sz % 2) != 0) {
- CX18_ERR("Firmware %s has a bad size: %lu bytes\n",
- fn, (unsigned long) sz);
- ret = -EILSEQ;
- release_firmware(*fw);
- *fw = NULL;
- }
- }
-
- if (ret) {
- CX18_ERR("The MPC718 board variant with the MT352 DVB-T"
- "demodualtor will not work without it\n");
- CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware "
- "mpc718' if you need the firmware\n");
- }
- return ret;
-}
-
-static int yuan_mpc718_mt352_init(struct dvb_frontend *fe)
-{
- struct cx18_dvb *dvb = container_of(fe->dvb,
- struct cx18_dvb, dvb_adapter);
- struct cx18_stream *stream = dvb->stream;
- const struct firmware *fw = NULL;
- int ret;
- int i;
- u8 buf[3];
-
- ret = yuan_mpc718_mt352_reqfw(stream, &fw);
- if (ret)
- return ret;
-
- /* Loop through all the register-value pairs in the firmware file */
- for (i = 0; i < fw->size; i += 2) {
- buf[0] = fw->data[i];
- /* Intercept a few registers we want to set ourselves */
- switch (buf[0]) {
- case TRL_NOMINAL_RATE_0:
- /* Set our custom OFDM bandwidth in the case below */
- break;
- case TRL_NOMINAL_RATE_1:
- /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */
- /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */
- /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */
- buf[1] = 0x72;
- buf[2] = 0x49;
- mt352_write(fe, buf, 3);
- break;
- case INPUT_FREQ_0:
- /* Set our custom IF in the case below */
- break;
- case INPUT_FREQ_1:
- /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */
- buf[1] = 0x31;
- buf[2] = 0xc0;
- mt352_write(fe, buf, 3);
- break;
- default:
- /* Pass through the register-value pair from the fw */
- buf[1] = fw->data[i+1];
- mt352_write(fe, buf, 2);
- break;
- }
- }
-
- buf[0] = (u8) TUNER_GO;
- buf[1] = 0x01; /* Go */
- mt352_write(fe, buf, 2);
- release_firmware(fw);
- return 0;
-}
-
-static struct mt352_config yuan_mpc718_mt352_demod = {
- .demod_address = 0x1e >> 1,
- .adc_clock = 20480, /* 20.480 MHz */
- .if2 = 4560, /* 4.560 MHz */
- .no_tuner = 1, /* XC3028 is not behind the gate */
- .demod_init = yuan_mpc718_mt352_init,
-};
-
-static struct zl10353_config yuan_mpc718_zl10353_demod = {
- .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
- .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
- .parallel_ts = 1, /* Not a serial TS */
- .no_tuner = 1, /* XC3028 is not behind the gate */
- .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
-};
-
-static struct zl10353_config gotview_dvd3_zl10353_demod = {
- .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
- .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
- .parallel_ts = 1, /* Not a serial TS */
- .no_tuner = 1, /* XC3028 is not behind the gate */
- .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
-};
-
-static int dvb_register(struct cx18_stream *stream);
-
-/* Kernel DVB framework calls this when the feed needs to start.
- * The CX18 framework should enable the transport DMA handling
- * and queue processing.
- */
-static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
- struct cx18 *cx;
- int ret;
- u32 v;
-
- if (!stream)
- return -EINVAL;
-
- cx = stream->cx;
- CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
- feed->pid, feed->index);
-
- mutex_lock(&cx->serialize_lock);
- ret = cx18_init_on_first_open(cx);
- mutex_unlock(&cx->serialize_lock);
- if (ret) {
- CX18_ERR("Failed to initialize firmware starting DVB feed\n");
- return ret;
- }
- ret = -EINVAL;
-
- switch (cx->card->type) {
- case CX18_CARD_HVR_1600_ESMT:
- case CX18_CARD_HVR_1600_SAMSUNG:
- case CX18_CARD_HVR_1600_S5H1411:
- v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
- v |= 0x00400000; /* Serial Mode */
- v |= 0x00002000; /* Data Length - Byte */
- v |= 0x00010000; /* Error - Polarity */
- v |= 0x00020000; /* Error - Passthru */
- v |= 0x000c0000; /* Error - Ignore */
- cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
- break;
-
- case CX18_CARD_LEADTEK_DVR3100H:
- case CX18_CARD_YUAN_MPC718:
- case CX18_CARD_GOTVIEW_PCI_DVD3:
- default:
- /* Assumption - Parallel transport - Signalling
- * undefined or default.
- */
- break;
- }
-
- if (!demux->dmx.frontend)
- return -EINVAL;
-
- mutex_lock(&stream->dvb->feedlock);
- if (stream->dvb->feeding++ == 0) {
- CX18_DEBUG_INFO("Starting Transport DMA\n");
- mutex_lock(&cx->serialize_lock);
- set_bit(CX18_F_S_STREAMING, &stream->s_flags);
- ret = cx18_start_v4l2_encode_stream(stream);
- if (ret < 0) {
- CX18_DEBUG_INFO("Failed to start Transport DMA\n");
- stream->dvb->feeding--;
- if (stream->dvb->feeding == 0)
- clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
- }
- mutex_unlock(&cx->serialize_lock);
- } else
- ret = 0;
- mutex_unlock(&stream->dvb->feedlock);
-
- return ret;
-}
-
-/* Kernel DVB framework calls this when the feed needs to stop. */
-static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
- struct cx18 *cx;
- int ret = -EINVAL;
-
- if (stream) {
- cx = stream->cx;
- CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
- feed->pid, feed->index);
-
- mutex_lock(&stream->dvb->feedlock);
- if (--stream->dvb->feeding == 0) {
- CX18_DEBUG_INFO("Stopping Transport DMA\n");
- mutex_lock(&cx->serialize_lock);
- ret = cx18_stop_v4l2_encode_stream(stream, 0);
- mutex_unlock(&cx->serialize_lock);
- } else
- ret = 0;
- mutex_unlock(&stream->dvb->feedlock);
- }
-
- return ret;
-}
-
-int cx18_dvb_register(struct cx18_stream *stream)
-{
- struct cx18 *cx = stream->cx;
- struct cx18_dvb *dvb = stream->dvb;
- struct dvb_adapter *dvb_adapter;
- struct dvb_demux *dvbdemux;
- struct dmx_demux *dmx;
- int ret;
-
- if (!dvb)
- return -EINVAL;
-
- dvb->enabled = 0;
- dvb->stream = stream;
-
- ret = dvb_register_adapter(&dvb->dvb_adapter,
- CX18_DRIVER_NAME,
- THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
- if (ret < 0)
- goto err_out;
-
- dvb_adapter = &dvb->dvb_adapter;
-
- dvbdemux = &dvb->demux;
-
- dvbdemux->priv = (void *)stream;
-
- dvbdemux->filternum = 256;
- dvbdemux->feednum = 256;
- dvbdemux->start_feed = cx18_dvb_start_feed;
- dvbdemux->stop_feed = cx18_dvb_stop_feed;
- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
- DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
- ret = dvb_dmx_init(dvbdemux);
- if (ret < 0)
- goto err_dvb_unregister_adapter;
-
- dmx = &dvbdemux->dmx;
-
- dvb->hw_frontend.source = DMX_FRONTEND_0;
- dvb->mem_frontend.source = DMX_MEMORY_FE;
- dvb->dmxdev.filternum = 256;
- dvb->dmxdev.demux = dmx;
-
- ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
- if (ret < 0)
- goto err_dvb_dmx_release;
-
- ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
- if (ret < 0)
- goto err_dvb_dmxdev_release;
-
- ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
- if (ret < 0)
- goto err_remove_hw_frontend;
-
- ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
- if (ret < 0)
- goto err_remove_mem_frontend;
-
- ret = dvb_register(stream);
- if (ret < 0)
- goto err_disconnect_frontend;
-
- dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
-
- CX18_INFO("DVB Frontend registered\n");
- CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n",
- stream->dvb->dvb_adapter.num, stream->name,
- stream->buffers, stream->buf_size/1024,
- (stream->buf_size * 100 / 1024) % 100);
-
- mutex_init(&dvb->feedlock);
- dvb->enabled = 1;
- return ret;
-
-err_disconnect_frontend:
- dmx->disconnect_frontend(dmx);
-err_remove_mem_frontend:
- dmx->remove_frontend(dmx, &dvb->mem_frontend);
-err_remove_hw_frontend:
- dmx->remove_frontend(dmx, &dvb->hw_frontend);
-err_dvb_dmxdev_release:
- dvb_dmxdev_release(&dvb->dmxdev);
-err_dvb_dmx_release:
- dvb_dmx_release(dvbdemux);
-err_dvb_unregister_adapter:
- dvb_unregister_adapter(dvb_adapter);
-err_out:
- return ret;
-}
-
-void cx18_dvb_unregister(struct cx18_stream *stream)
-{
- struct cx18 *cx = stream->cx;
- struct cx18_dvb *dvb = stream->dvb;
- struct dvb_adapter *dvb_adapter;
- struct dvb_demux *dvbdemux;
- struct dmx_demux *dmx;
-
- CX18_INFO("unregister DVB\n");
-
- if (dvb == NULL || !dvb->enabled)
- return;
-
- dvb_adapter = &dvb->dvb_adapter;
- dvbdemux = &dvb->demux;
- dmx = &dvbdemux->dmx;
-
- dmx->close(dmx);
- dvb_net_release(&dvb->dvbnet);
- dmx->remove_frontend(dmx, &dvb->mem_frontend);
- dmx->remove_frontend(dmx, &dvb->hw_frontend);
- dvb_dmxdev_release(&dvb->dmxdev);
- dvb_dmx_release(dvbdemux);
- dvb_unregister_frontend(dvb->fe);
- dvb_frontend_detach(dvb->fe);
- dvb_unregister_adapter(dvb_adapter);
-}
-
-/* All the DVB attach calls go here, this function get's modified
- * for each new card. cx18_dvb_start_feed() will also need changes.
- */
-static int dvb_register(struct cx18_stream *stream)
-{
- struct cx18_dvb *dvb = stream->dvb;
- struct cx18 *cx = stream->cx;
- int ret = 0;
-
- switch (cx->card->type) {
- case CX18_CARD_HVR_1600_ESMT:
- case CX18_CARD_HVR_1600_SAMSUNG:
- dvb->fe = dvb_attach(s5h1409_attach,
- &hauppauge_hvr1600_config,
- &cx->i2c_adap[0]);
- if (dvb->fe != NULL) {
- dvb_attach(mxl5005s_attach, dvb->fe,
- &cx->i2c_adap[0],
- &hauppauge_hvr1600_tuner);
- ret = 0;
- }
- break;
- case CX18_CARD_HVR_1600_S5H1411:
- dvb->fe = dvb_attach(s5h1411_attach,
- &hcw_s5h1411_config,
- &cx->i2c_adap[0]);
- if (dvb->fe != NULL)
- dvb_attach(tda18271_attach, dvb->fe,
- 0x60, &cx->i2c_adap[0],
- &hauppauge_tda18271_config);
- break;
- case CX18_CARD_LEADTEK_DVR3100H:
- dvb->fe = dvb_attach(zl10353_attach,
- &leadtek_dvr3100h_demod,
- &cx->i2c_adap[1]);
- if (dvb->fe != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &cx->i2c_adap[1],
- .i2c_addr = 0xc2 >> 1,
- .ctrl = NULL,
- };
- static struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_ZARLINK456,
- .type = XC2028_AUTO,
- };
-
- fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctrl);
- }
- break;
- case CX18_CARD_YUAN_MPC718:
- /*
- * TODO
- * Apparently, these cards also could instead have a
- * DiBcom demod supported by one of the db7000 drivers
- */
- dvb->fe = dvb_attach(mt352_attach,
- &yuan_mpc718_mt352_demod,
- &cx->i2c_adap[1]);
- if (dvb->fe == NULL)
- dvb->fe = dvb_attach(zl10353_attach,
- &yuan_mpc718_zl10353_demod,
- &cx->i2c_adap[1]);
- if (dvb->fe != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &cx->i2c_adap[1],
- .i2c_addr = 0xc2 >> 1,
- .ctrl = NULL,
- };
- static struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_ZARLINK456,
- .type = XC2028_AUTO,
- };
-
- fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctrl);
- }
- break;
- case CX18_CARD_GOTVIEW_PCI_DVD3:
- dvb->fe = dvb_attach(zl10353_attach,
- &gotview_dvd3_zl10353_demod,
- &cx->i2c_adap[1]);
- if (dvb->fe != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &cx->i2c_adap[1],
- .i2c_addr = 0xc2 >> 1,
- .ctrl = NULL,
- };
- static struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_ZARLINK456,
- .type = XC2028_AUTO,
- };
-
- fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctrl);
- }
- break;
- default:
- /* No Digital Tv Support */
- break;
- }
-
- if (dvb->fe == NULL) {
- CX18_ERR("frontend initialization failed\n");
- return -1;
- }
-
- dvb->fe->callback = cx18_reset_tuner_gpio;
-
- ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
- if (ret < 0) {
- if (dvb->fe->ops.release)
- dvb->fe->ops.release(dvb->fe);
- return ret;
- }
-
- /*
- * The firmware seems to enable the TS DMUX clock
- * under various circumstances. However, since we know we
- * might use it, let's just turn it on ourselves here.
- */
- cx18_write_reg_expect(cx,
- (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK,
- CX18_CLOCK_ENABLE2,
- CX18_DMUX_CLK_MASK,
- (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK);
-
- return ret;
-}
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
deleted file mode 100644
index b85c292a849a..000000000000
--- a/drivers/media/video/cx18/cx18-firmware.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * cx18 firmware functions
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- */
-
-#include "cx18-driver.h"
-#include "cx18-io.h"
-#include "cx18-scb.h"
-#include "cx18-irq.h"
-#include "cx18-firmware.h"
-#include "cx18-cards.h"
-#include <linux/firmware.h>
-
-#define CX18_PROC_SOFT_RESET 0xc70010
-#define CX18_DDR_SOFT_RESET 0xc70014
-#define CX18_CLOCK_SELECT1 0xc71000
-#define CX18_CLOCK_SELECT2 0xc71004
-#define CX18_HALF_CLOCK_SELECT1 0xc71008
-#define CX18_HALF_CLOCK_SELECT2 0xc7100C
-#define CX18_CLOCK_POLARITY1 0xc71010
-#define CX18_CLOCK_POLARITY2 0xc71014
-#define CX18_ADD_DELAY_ENABLE1 0xc71018
-#define CX18_ADD_DELAY_ENABLE2 0xc7101C
-#define CX18_CLOCK_ENABLE1 0xc71020
-#define CX18_CLOCK_ENABLE2 0xc71024
-
-#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
-
-#define CX18_FAST_CLOCK_PLL_INT 0xc78000
-#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004
-#define CX18_FAST_CLOCK_PLL_POST 0xc78008
-#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C
-#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
-
-#define CX18_SLOW_CLOCK_PLL_INT 0xc78014
-#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018
-#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C
-#define CX18_MPEG_CLOCK_PLL_INT 0xc78040
-#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044
-#define CX18_MPEG_CLOCK_PLL_POST 0xc78048
-#define CX18_PLL_POWER_DOWN 0xc78088
-#define CX18_SW1_INT_STATUS 0xc73104
-#define CX18_SW1_INT_ENABLE_PCI 0xc7311C
-#define CX18_SW2_INT_SET 0xc73140
-#define CX18_SW2_INT_STATUS 0xc73144
-#define CX18_ADEC_CONTROL 0xc78120
-
-#define CX18_DDR_REQUEST_ENABLE 0xc80000
-#define CX18_DDR_CHIP_CONFIG 0xc80004
-#define CX18_DDR_REFRESH 0xc80008
-#define CX18_DDR_TIMING1 0xc8000C
-#define CX18_DDR_TIMING2 0xc80010
-#define CX18_DDR_POWER_REG 0xc8001C
-
-#define CX18_DDR_TUNE_LANE 0xc80048
-#define CX18_DDR_INITIAL_EMRS 0xc80054
-#define CX18_DDR_MB_PER_ROW_7 0xc8009C
-#define CX18_DDR_BASE_63_ADDR 0xc804FC
-
-#define CX18_WMB_CLIENT02 0xc90108
-#define CX18_WMB_CLIENT05 0xc90114
-#define CX18_WMB_CLIENT06 0xc90118
-#define CX18_WMB_CLIENT07 0xc9011C
-#define CX18_WMB_CLIENT08 0xc90120
-#define CX18_WMB_CLIENT09 0xc90124
-#define CX18_WMB_CLIENT10 0xc90128
-#define CX18_WMB_CLIENT11 0xc9012C
-#define CX18_WMB_CLIENT12 0xc90130
-#define CX18_WMB_CLIENT13 0xc90134
-#define CX18_WMB_CLIENT14 0xc90138
-
-#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
-
-#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
-#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
-
-struct cx18_apu_rom_seghdr {
- u32 sync1;
- u32 sync2;
- u32 addr;
- u32 size;
-};
-
-static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
-{
- const struct firmware *fw = NULL;
- int i, j;
- unsigned size;
- u32 __iomem *dst = (u32 __iomem *)mem;
- const u32 *src;
-
- if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
- CX18_ERR("Unable to open firmware %s\n", fn);
- CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
- return -ENOMEM;
- }
-
- src = (const u32 *)fw->data;
-
- for (i = 0; i < fw->size; i += 4096) {
- cx18_setup_page(cx, i);
- for (j = i; j < fw->size && j < i + 4096; j += 4) {
- /* no need for endianness conversion on the ppc */
- cx18_raw_writel(cx, *src, dst);
- if (cx18_raw_readl(cx, dst) != *src) {
- CX18_ERR("Mismatch at offset %x\n", i);
- release_firmware(fw);
- cx18_setup_page(cx, 0);
- return -EIO;
- }
- dst++;
- src++;
- }
- }
- if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
- CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
- size = fw->size;
- release_firmware(fw);
- cx18_setup_page(cx, SCB_OFFSET);
- return size;
-}
-
-static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
- u32 *entry_addr)
-{
- const struct firmware *fw = NULL;
- int i, j;
- unsigned size;
- const u32 *src;
- struct cx18_apu_rom_seghdr seghdr;
- const u8 *vers;
- u32 offset = 0;
- u32 apu_version = 0;
- int sz;
-
- if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
- CX18_ERR("unable to open firmware %s\n", fn);
- CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
- cx18_setup_page(cx, 0);
- return -ENOMEM;
- }
-
- *entry_addr = 0;
- src = (const u32 *)fw->data;
- vers = fw->data + sizeof(seghdr);
- sz = fw->size;
-
- apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
- while (offset + sizeof(seghdr) < fw->size) {
- const u32 *shptr = src + offset / 4;
-
- seghdr.sync1 = le32_to_cpu(shptr[0]);
- seghdr.sync2 = le32_to_cpu(shptr[1]);
- seghdr.addr = le32_to_cpu(shptr[2]);
- seghdr.size = le32_to_cpu(shptr[3]);
-
- offset += sizeof(seghdr);
- if (seghdr.sync1 != APU_ROM_SYNC1 ||
- seghdr.sync2 != APU_ROM_SYNC2) {
- offset += seghdr.size;
- continue;
- }
- CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
- seghdr.addr + seghdr.size - 1);
- if (*entry_addr == 0)
- *entry_addr = seghdr.addr;
- if (offset + seghdr.size > sz)
- break;
- for (i = 0; i < seghdr.size; i += 4096) {
- cx18_setup_page(cx, seghdr.addr + i);
- for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
- /* no need for endianness conversion on the ppc */
- cx18_raw_writel(cx, src[(offset + j) / 4],
- dst + seghdr.addr + j);
- if (cx18_raw_readl(cx, dst + seghdr.addr + j)
- != src[(offset + j) / 4]) {
- CX18_ERR("Mismatch at offset %x\n",
- offset + j);
- release_firmware(fw);
- cx18_setup_page(cx, 0);
- return -EIO;
- }
- }
- }
- offset += seghdr.size;
- }
- if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
- CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
- fn, apu_version, fw->size);
- size = fw->size;
- release_firmware(fw);
- cx18_setup_page(cx, 0);
- return size;
-}
-
-void cx18_halt_firmware(struct cx18 *cx)
-{
- CX18_DEBUG_INFO("Preparing for firmware halt.\n");
- cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
- 0x0000000F, 0x000F000F);
- cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL,
- 0x00000002, 0x00020002);
-}
-
-void cx18_init_power(struct cx18 *cx, int lowpwr)
-{
- /* power-down Spare and AOM PLLs */
- /* power-up fast, slow and mpeg PLLs */
- cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN);
-
- /* ADEC out of sleep */
- cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL,
- 0x00000000, 0x00020002);
-
- /*
- * The PLL parameters are based on the external crystal frequency that
- * would ideally be:
- *
- * NTSC Color subcarrier freq * 8 =
- * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz
- *
- * The accidents of history and rationale that explain from where this
- * combination of magic numbers originate can be found in:
- *
- * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in
- * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80
- *
- * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the
- * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83
- *
- * As Mike Bradley has rightly pointed out, it's not the exact crystal
- * frequency that matters, only that all parts of the driver and
- * firmware are using the same value (close to the ideal value).
- *
- * Since I have a strong suspicion that, if the firmware ever assumes a
- * crystal value at all, it will assume 28.636360 MHz, the crystal
- * freq used in calculations in this driver will be:
- *
- * xtal_freq = 28.636360 MHz
- *
- * an error of less than 0.13 ppm which is way, way better than any off
- * the shelf crystal will have for accuracy anyway.
- *
- * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors.
- *
- * Many thanks to Jeff Campbell and Mike Bradley for their extensive
- * investigation, experimentation, testing, and suggested solutions of
- * of audio/video sync problems with SVideo and CVBS captures.
- */
-
- /* the fast clock is at 200/245 MHz */
- /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/
- /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/
- cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
- cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7,
- CX18_FAST_CLOCK_PLL_FRAC);
-
- cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST);
- cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE);
- cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
-
- /* set slow clock to 125/120 MHz */
- /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */
- /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */
- cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT);
- cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F,
- CX18_SLOW_CLOCK_PLL_FRAC);
- cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST);
-
- /* mpeg clock pll 54MHz */
- /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */
- cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT);
- cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
- cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
-
- /* Defaults */
- /* APU = SC or SC/2 = 125/62.5 */
- /* EPU = SC = 125 */
- /* DDR = FC = 180 */
- /* ENC = SC = 125 */
- /* AI1 = SC = 125 */
- /* VIM2 = disabled */
- /* PCI = FC/2 = 90 */
- /* AI2 = disabled */
- /* DEMUX = disabled */
- /* AO = SC/2 = 62.5 */
- /* SER = 54MHz */
- /* VFC = disabled */
- /* USB = disabled */
-
- if (lowpwr) {
- cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1,
- 0x00000020, 0xFFFFFFFF);
- cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2,
- 0x00000004, 0xFFFFFFFF);
- } else {
- /* This doesn't explicitly set every clock select */
- cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1,
- 0x00000004, 0x00060006);
- cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2,
- 0x00000006, 0x00060006);
- }
-
- cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1,
- 0x00000002, 0xFFFFFFFF);
- cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2,
- 0x00000104, 0xFFFFFFFF);
- cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1,
- 0x00009026, 0xFFFFFFFF);
- cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2,
- 0x00003105, 0xFFFFFFFF);
-}
-
-void cx18_init_memory(struct cx18 *cx)
-{
- cx18_msleep_timeout(10, 0);
- cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET,
- 0x00000000, 0x00010001);
- cx18_msleep_timeout(10, 0);
-
- cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
-
- cx18_msleep_timeout(10, 0);
-
- cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH);
- cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1);
- cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2);
-
- cx18_msleep_timeout(10, 0);
-
- /* Initialize DQS pad time */
- cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
- cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
-
- cx18_msleep_timeout(10, 0);
-
- cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET,
- 0x00000000, 0x00020002);
- cx18_msleep_timeout(10, 0);
-
- /* use power-down mode when idle */
- cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG);
-
- cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN,
- 0x00000001, 0x00010001);
-
- cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7);
- cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR);
-
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */
- cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */
-}
-
-int cx18_firmware_init(struct cx18 *cx)
-{
- u32 fw_entry_addr;
- int sz, retries;
- u32 api_args[MAX_MB_ARGUMENTS];
-
- /* Allow chip to control CLKRUN */
- cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
-
- /* Stop the firmware */
- cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET,
- 0x0000000F, 0x000F000F);
-
- cx18_msleep_timeout(1, 0);
-
- /* If the CPU is still running */
- if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
- CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
- return -EIO;
- }
-
- cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
- cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
-
- sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
- if (sz <= 0)
- return sz;
-
- /* The SCB & IPC area *must* be correct before starting the firmwares */
- cx18_init_scb(cx);
-
- fw_entry_addr = 0;
- sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
- &fw_entry_addr);
- if (sz <= 0)
- return sz;
-
- /* Start the CPU. The CPU will take care of the APU for us. */
- cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
- 0x00000000, 0x00080008);
-
- /* Wait up to 500 ms for the APU to come out of reset */
- for (retries = 0;
- retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
- retries++)
- cx18_msleep_timeout(10, 0);
-
- cx18_msleep_timeout(200, 0);
-
- if (retries == 50 &&
- (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
- CX18_ERR("Could not start the CPU\n");
- return -EIO;
- }
-
- /*
- * The CPU had once before set up to receive an interrupt for it's
- * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
- * interrupt when it sends us an ack, but by the time we process it,
- * that flag in the SW2 status register has been cleared by the CPU
- * firmware. We'll prevent that not so useful condition from happening
- * by clearing the CPU's interrupt enables for Ack IRQ's we want to
- * process.
- */
- cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
-
- /* Try a benign command to see if the CPU is alive and well */
- sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
- if (sz < 0)
- return sz;
-
- /* initialize GPIO */
- cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
- return 0;
-}
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
deleted file mode 100644
index e9912db3b496..000000000000
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ /dev/null
@@ -1,1194 +0,0 @@
-/*
- * cx18 ioctl system call
- *
- * Derived from ivtv-ioctl.c
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- */
-
-#include "cx18-driver.h"
-#include "cx18-io.h"
-#include "cx18-version.h"
-#include "cx18-mailbox.h"
-#include "cx18-i2c.h"
-#include "cx18-queue.h"
-#include "cx18-fileops.h"
-#include "cx18-vbi.h"
-#include "cx18-audio.h"
-#include "cx18-video.h"
-#include "cx18-streams.h"
-#include "cx18-ioctl.h"
-#include "cx18-gpio.h"
-#include "cx18-controls.h"
-#include "cx18-cards.h"
-#include "cx18-av-core.h"
-#include <media/tveeprom.h>
-#include <media/v4l2-chip-ident.h>
-
-u16 cx18_service2vbi(int type)
-{
- switch (type) {
- case V4L2_SLICED_TELETEXT_B:
- return CX18_SLICED_TYPE_TELETEXT_B;
- case V4L2_SLICED_CAPTION_525:
- return CX18_SLICED_TYPE_CAPTION_525;
- case V4L2_SLICED_WSS_625:
- return CX18_SLICED_TYPE_WSS_625;
- case V4L2_SLICED_VPS:
- return CX18_SLICED_TYPE_VPS;
- default:
- return 0;
- }
-}
-
-/* Check if VBI services are allowed on the (field, line) for the video std */
-static int valid_service_line(int field, int line, int is_pal)
-{
- return (is_pal && line >= 6 &&
- ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
- (!is_pal && line >= 10 && line < 22);
-}
-
-/*
- * For a (field, line, std) and inbound potential set of services for that line,
- * return the first valid service of those passed in the incoming set for that
- * line in priority order:
- * CC, VPS, or WSS over TELETEXT for well known lines
- * TELETEXT, before VPS, before CC, before WSS, for other lines
- */
-static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
-{
- u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
- int i;
-
- set = set & valid_set;
- if (set == 0 || !valid_service_line(field, line, is_pal))
- return 0;
- if (!is_pal) {
- if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
- return V4L2_SLICED_CAPTION_525;
- } else {
- if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
- return V4L2_SLICED_VPS;
- if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
- return V4L2_SLICED_WSS_625;
- if (line == 23)
- return 0;
- }
- for (i = 0; i < 32; i++) {
- if ((1 << i) & set)
- return 1 << i;
- }
- return 0;
-}
-
-/*
- * Expand the service_set of *fmt into valid service_lines for the std,
- * and clear the passed in fmt->service_set
- */
-void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
-{
- u16 set = fmt->service_set;
- int f, l;
-
- fmt->service_set = 0;
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++)
- fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
- }
-}
-
-/*
- * Sanitize the service_lines in *fmt per the video std, and return 1
- * if any service_line is left as valid after santization
- */
-static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
-{
- int f, l;
- u16 set = 0;
-
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
- set |= fmt->service_lines[f][l];
- }
- }
- return set != 0;
-}
-
-/* Compute the service_set from the assumed valid service_lines of *fmt */
-u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
-{
- int f, l;
- u16 set = 0;
-
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++)
- set |= fmt->service_lines[f][l];
- }
- return set;
-}
-
-static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- pixfmt->width = cx->cxhdl.width;
- pixfmt->height = cx->cxhdl.height;
- pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pixfmt->field = V4L2_FIELD_INTERLACED;
- pixfmt->priv = 0;
- if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
- pixfmt->pixelformat = s->pixelformat;
- pixfmt->sizeimage = s->vb_bytes_per_frame;
- pixfmt->bytesperline = 720;
- } else {
- pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
- pixfmt->sizeimage = 128 * 1024;
- pixfmt->bytesperline = 0;
- }
- return 0;
-}
-
-static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
-
- vbifmt->sampling_rate = 27000000;
- vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
- vbifmt->samples_per_line = vbi_active_samples - 4;
- vbifmt->sample_format = V4L2_PIX_FMT_GREY;
- vbifmt->start[0] = cx->vbi.start[0];
- vbifmt->start[1] = cx->vbi.start[1];
- vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count;
- vbifmt->flags = 0;
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- return 0;
-}
-
-static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-
- /* sane, V4L2 spec compliant, defaults */
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
- vbifmt->service_set = 0;
-
- /*
- * Fetch the configured service_lines and total service_set from the
- * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in
- * fmt->fmt.sliced under valid calling conditions
- */
- if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced))
- return -EINVAL;
-
- /* Ensure V4L2 spec compliant output */
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- vbifmt->service_set = cx18_get_service_set(vbifmt);
- return 0;
-}
-
-static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- int w = fmt->fmt.pix.width;
- int h = fmt->fmt.pix.height;
- int min_h = 2;
-
- w = min(w, 720);
- w = max(w, 2);
- if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
- /* YUV height must be a multiple of 32 */
- h &= ~0x1f;
- min_h = 32;
- }
- h = min(h, cx->is_50hz ? 576 : 480);
- h = max(h, min_h);
-
- fmt->fmt.pix.width = w;
- fmt->fmt.pix.height = h;
- return 0;
-}
-
-static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- return cx18_g_fmt_vbi_cap(file, fh, fmt);
-}
-
-static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
-
- /* If given a service set, expand it validly & clear passed in set */
- if (vbifmt->service_set)
- cx18_expand_service_set(vbifmt, cx->is_50hz);
- /* Sanitize the service_lines, and compute the new set if any valid */
- if (check_service_set(vbifmt, cx->is_50hz))
- vbifmt->service_set = cx18_get_service_set(vbifmt);
- return 0;
-}
-
-static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- struct v4l2_mbus_framefmt mbus_fmt;
- struct cx18_stream *s = &cx->streams[id->type];
- int ret;
- int w, h;
-
- ret = cx18_try_fmt_vid_cap(file, fh, fmt);
- if (ret)
- return ret;
- w = fmt->fmt.pix.width;
- h = fmt->fmt.pix.height;
-
- if (cx->cxhdl.width == w && cx->cxhdl.height == h &&
- s->pixelformat == fmt->fmt.pix.pixelformat)
- return 0;
-
- if (atomic_read(&cx->ana_capturing) > 0)
- return -EBUSY;
-
- s->pixelformat = fmt->fmt.pix.pixelformat;
- /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
- UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- s->vb_bytes_per_frame = h * 720 * 3 / 2;
- else
- s->vb_bytes_per_frame = h * 720 * 2;
-
- mbus_fmt.width = cx->cxhdl.width = w;
- mbus_fmt.height = cx->cxhdl.height = h;
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
- v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt);
- return cx18_g_fmt_vid_cap(file, fh, fmt);
-}
-
-static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- int ret;
-
- /*
- * Changing the Encoder's Raw VBI parameters won't have any effect
- * if any analog capture is ongoing
- */
- if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
- return -EBUSY;
-
- /*
- * Set the digitizer registers for raw active VBI.
- * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid
- * calling conditions
- */
- ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi);
- if (ret)
- return ret;
-
- /* Store our new v4l2 (non-)sliced VBI state */
- cx->vbi.sliced_in->service_set = 0;
- cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
-
- return cx18_g_fmt_vbi_cap(file, fh, fmt);
-}
-
-static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
- struct v4l2_format *fmt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- int ret;
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-
- cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
-
- /*
- * Changing the Encoder's Raw VBI parameters won't have any effect
- * if any analog capture is ongoing
- */
- if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
- return -EBUSY;
-
- /*
- * Set the service_lines requested in the digitizer/slicer registers.
- * Note, cx18_av_vbi() wipes some "impossible" service lines in the
- * passed in fmt->fmt.sliced under valid calling conditions
- */
- ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced);
- if (ret)
- return ret;
- /* Store our current v4l2 sliced VBI settings */
- cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
- return 0;
-}
-
-static int cx18_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- int err = 0;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- switch (chip->match.addr) {
- case 0:
- chip->ident = V4L2_IDENT_CX23418;
- chip->revision = cx18_read_reg(cx, 0xC72028);
- break;
- case 1:
- /*
- * The A/V decoder is always present, but in the rare
- * case that the card doesn't have analog, we don't
- * use it. We find it w/o using the cx->sd_av pointer
- */
- cx18_call_hw(cx, CX18_HW_418_AV,
- core, g_chip_ident, chip);
- break;
- default:
- /*
- * Could return ident = V4L2_IDENT_UNKNOWN if we had
- * other host chips at higher addresses, but we don't
- */
- err = -EINVAL; /* per V4L2 spec */
- break;
- }
- break;
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
- cx18_call_all(cx, core, g_chip_ident, chip);
- break;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /*
- * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
- * to look if a chip is at the address with no driver. That's a
- * dangerous thing to do with EEPROMs anyway.
- */
- cx18_call_all(cx, core, g_chip_ident, chip);
- break;
- default:
- err = -EINVAL;
- break;
- }
- return err;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
-{
- struct v4l2_dbg_register *regs = arg;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
- return -EINVAL;
-
- regs->size = 4;
- if (cmd == VIDIOC_DBG_S_REGISTER)
- cx18_write_enc(cx, regs->val, regs->reg);
- else
- regs->val = cx18_read_enc(cx, regs->reg);
- return 0;
-}
-
-static int cx18_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (v4l2_chip_match_host(&reg->match))
- return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
- /* FIXME - errors shouldn't be ignored */
- cx18_call_all(cx, core, g_register, reg);
- return 0;
-}
-
-static int cx18_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (v4l2_chip_match_host(&reg->match))
- return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
- /* FIXME - errors shouldn't be ignored */
- cx18_call_all(cx, core, s_register, reg);
- return 0;
-}
-#endif
-
-static int cx18_querycap(struct file *file, void *fh,
- struct v4l2_capability *vcap)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
- strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
- snprintf(vcap->bus_info, sizeof(vcap->bus_info),
- "PCI:%s", pci_name(cx->pci_dev));
- vcap->capabilities = cx->v4l2_cap; /* capabilities */
- if (id->type == CX18_ENC_STREAM_TYPE_YUV)
- vcap->capabilities |= V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- return cx18_get_audio_input(cx, vin->index, vin);
-}
-
-static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- vin->index = cx->audio_input;
- return cx18_get_audio_input(cx, vin->index, vin);
-}
-
-static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (vout->index >= cx->nof_audio_inputs)
- return -EINVAL;
- cx->audio_input = vout->index;
- cx18_audio_set_io(cx);
- return 0;
-}
-
-static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- /* set it to defaults from our table */
- return cx18_get_input(cx, vin->index, vin);
-}
-
-static int cx18_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cropcap)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cropcap->bounds.top = cropcap->bounds.left = 0;
- cropcap->bounds.width = 720;
- cropcap->bounds.height = cx->is_50hz ? 576 : 480;
- cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
- cropcap->defrect = cropcap->bounds;
- return 0;
-}
-
-static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
- return -EINVAL;
-}
-
-static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
- return -EINVAL;
-}
-
-static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- static const struct v4l2_fmtdesc formats[] = {
- { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
- "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 }
- },
- { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED,
- "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 }
- },
- { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
- "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 }
- },
- };
-
- if (fmt->index > ARRAY_SIZE(formats) - 1)
- return -EINVAL;
- *fmt = formats[fmt->index];
- return 0;
-}
-
-static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- *i = cx->active_input;
- return 0;
-}
-
-int cx18_s_input(struct file *file, void *fh, unsigned int inp)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- if (inp >= cx->nof_inputs)
- return -EINVAL;
-
- if (inp == cx->active_input) {
- CX18_DEBUG_INFO("Input unchanged\n");
- return 0;
- }
-
- CX18_DEBUG_INFO("Changing input from %d to %d\n",
- cx->active_input, inp);
-
- cx->active_input = inp;
- /* Set the audio input to whatever is appropriate for the input type. */
- cx->audio_input = cx->card->video_inputs[inp].audio_index;
-
- /* prevent others from messing with the streams until
- we're finished changing inputs. */
- cx18_mute(cx);
- cx18_video_set_io(cx);
- cx18_audio_set_io(cx);
- cx18_unmute(cx);
- return 0;
-}
-
-static int cx18_g_frequency(struct file *file, void *fh,
- struct v4l2_frequency *vf)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (vf->tuner != 0)
- return -EINVAL;
-
- cx18_call_all(cx, tuner, g_frequency, vf);
- return 0;
-}
-
-int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- if (vf->tuner != 0)
- return -EINVAL;
-
- cx18_mute(cx);
- CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- cx18_call_all(cx, tuner, s_frequency, vf);
- cx18_unmute(cx);
- return 0;
-}
-
-static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- *std = cx->std;
- return 0;
-}
-
-int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- if ((*std & V4L2_STD_ALL) == 0)
- return -EINVAL;
-
- if (*std == cx->std)
- return 0;
-
- if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
- atomic_read(&cx->ana_capturing) > 0) {
- /* Switching standard would turn off the radio or mess
- with already running streams, prevent that by
- returning EBUSY. */
- return -EBUSY;
- }
-
- cx->std = *std;
- cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- cx->is_50hz = !cx->is_60hz;
- cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz);
- cx->cxhdl.width = 720;
- cx->cxhdl.height = cx->is_50hz ? 576 : 480;
- cx->vbi.count = cx->is_50hz ? 18 : 12;
- cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
- cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
- CX18_DEBUG_INFO("Switching standard to %llx.\n",
- (unsigned long long) cx->std);
-
- /* Tuner */
- cx18_call_all(cx, core, s_std, cx->std);
- return 0;
-}
-
-static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
-
- if (vt->index != 0)
- return -EINVAL;
-
- cx18_call_all(cx, tuner, s_tuner, vt);
- return 0;
-}
-
-static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- if (vt->index != 0)
- return -EINVAL;
-
- cx18_call_all(cx, tuner, g_tuner, vt);
-
- if (vt->type == V4L2_TUNER_RADIO)
- strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
- else
- strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
- return 0;
-}
-
-static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
- struct v4l2_sliced_vbi_cap *cap)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
- int f, l;
-
- if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
-
- cap->service_set = 0;
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- if (valid_service_line(f, l, cx->is_50hz)) {
- /*
- * We can find all v4l2 supported vbi services
- * for the standard, on a valid line for the std
- */
- cap->service_lines[f][l] = set;
- cap->service_set |= set;
- } else
- cap->service_lines[f][l] = 0;
- }
- }
- for (f = 0; f < 3; f++)
- cap->reserved[f] = 0;
- return 0;
-}
-
-static int _cx18_process_idx_data(struct cx18_buffer *buf,
- struct v4l2_enc_idx *idx)
-{
- int consumed, remaining;
- struct v4l2_enc_idx_entry *e_idx;
- struct cx18_enc_idx_entry *e_buf;
-
- /* Frame type lookup: 1=I, 2=P, 4=B */
- const int mapping[8] = {
- -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
- -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
- };
-
- /*
- * Assumption here is that a buf holds an integral number of
- * struct cx18_enc_idx_entry objects and is properly aligned.
- * This is enforced by the module options on IDX buffer sizes.
- */
- remaining = buf->bytesused - buf->readpos;
- consumed = 0;
- e_idx = &idx->entry[idx->entries];
- e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
-
- while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
- idx->entries < V4L2_ENC_IDX_ENTRIES) {
-
- e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
- | le32_to_cpu(e_buf->offset_low);
-
- e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
- | le32_to_cpu(e_buf->pts_low);
-
- e_idx->length = le32_to_cpu(e_buf->length);
-
- e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
-
- e_idx->reserved[0] = 0;
- e_idx->reserved[1] = 0;
-
- idx->entries++;
- e_idx = &idx->entry[idx->entries];
- e_buf++;
-
- remaining -= sizeof(struct cx18_enc_idx_entry);
- consumed += sizeof(struct cx18_enc_idx_entry);
- }
-
- /* Swallow any partial entries at the end, if there are any */
- if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
- consumed += remaining;
-
- buf->readpos += consumed;
- return consumed;
-}
-
-static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
- struct v4l2_enc_idx *idx)
-{
- if (s->type != CX18_ENC_STREAM_TYPE_IDX)
- return -EINVAL;
-
- if (mdl->curr_buf == NULL)
- mdl->curr_buf = list_first_entry(&mdl->buf_list,
- struct cx18_buffer, list);
-
- if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
- /*
- * For some reason we've exhausted the buffers, but the MDL
- * object still said some data was unread.
- * Fix that and bail out.
- */
- mdl->readpos = mdl->bytesused;
- return 0;
- }
-
- list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
-
- /* Skip any empty buffers in the MDL */
- if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
- continue;
-
- mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
-
- /* exit when MDL drained or request satisfied */
- if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
- mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
- mdl->readpos >= mdl->bytesused)
- break;
- }
- return 0;
-}
-
-static int cx18_g_enc_index(struct file *file, void *fh,
- struct v4l2_enc_idx *idx)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
- s32 tmp;
- struct cx18_mdl *mdl;
-
- if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
- return -EINVAL;
-
- /* Compute the best case number of entries we can buffer */
- tmp = s->buffers -
- s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
- if (tmp <= 0)
- tmp = 1;
- tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
-
- /* Fill out the header of the return structure */
- idx->entries = 0;
- idx->entries_cap = tmp;
- memset(idx->reserved, 0, sizeof(idx->reserved));
-
- /* Pull IDX MDLs and buffers from q_full and populate the entries */
- do {
- mdl = cx18_dequeue(s, &s->q_full);
- if (mdl == NULL) /* No more IDX data right now */
- break;
-
- /* Extract the Index entry data from the MDL and buffers */
- cx18_process_idx_data(s, mdl, idx);
- if (mdl->readpos < mdl->bytesused) {
- /* We finished with data remaining, push the MDL back */
- cx18_push(s, mdl, &s->q_full);
- break;
- }
-
- /* We drained this MDL, schedule it to go to the firmware */
- cx18_enqueue(s, mdl, &s->q_free);
-
- } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
-
- /* Tell the work handler to send free IDX MDLs to the firmware */
- cx18_stream_load_fw_queue(s);
- return 0;
-}
-
-static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id)
-{
- struct videobuf_queue *q = NULL;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- switch (s->vb_type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- q = &s->vbuf_q;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- break;
- default:
- break;
- }
- return q;
-}
-
-static int cx18_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- /* Start the hardware only if we're the video device */
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- if (id->type != CX18_ENC_STREAM_TYPE_YUV)
- return -EINVAL;
-
- /* Establish a buffer timeout */
- mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies);
-
- return videobuf_streamon(cx18_vb_queue(id));
-}
-
-static int cx18_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- /* Start the hardware only if we're the video device */
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- if (id->type != CX18_ENC_STREAM_TYPE_YUV)
- return -EINVAL;
-
- return videobuf_streamoff(cx18_vb_queue(id));
-}
-
-static int cx18_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- return videobuf_reqbufs(cx18_vb_queue(id), rb);
-}
-
-static int cx18_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- return videobuf_querybuf(cx18_vb_queue(id), b);
-}
-
-static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- return videobuf_qbuf(cx18_vb_queue(id), b);
-}
-
-static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx18_open_id *id = file->private_data;
- struct cx18 *cx = id->cx;
- struct cx18_stream *s = &cx->streams[id->type];
-
- if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK);
-}
-
-static int cx18_encoder_cmd(struct file *file, void *fh,
- struct v4l2_encoder_cmd *enc)
-{
- struct cx18_open_id *id = fh2id(fh);
- struct cx18 *cx = id->cx;
- u32 h;
-
- switch (enc->cmd) {
- case V4L2_ENC_CMD_START:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
- enc->flags = 0;
- return cx18_start_capture(id);
-
- case V4L2_ENC_CMD_STOP:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
- enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
- cx18_stop_capture(id,
- enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
- break;
-
- case V4L2_ENC_CMD_PAUSE:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
- enc->flags = 0;
- if (!atomic_read(&cx->ana_capturing))
- return -EPERM;
- if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
- return 0;
- h = cx18_find_handle(cx);
- if (h == CX18_INVALID_TASK_HANDLE) {
- CX18_ERR("Can't find valid task handle for "
- "V4L2_ENC_CMD_PAUSE\n");
- return -EBADFD;
- }
- cx18_mute(cx);
- cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h);
- break;
-
- case V4L2_ENC_CMD_RESUME:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
- enc->flags = 0;
- if (!atomic_read(&cx->ana_capturing))
- return -EPERM;
- if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
- return 0;
- h = cx18_find_handle(cx);
- if (h == CX18_INVALID_TASK_HANDLE) {
- CX18_ERR("Can't find valid task handle for "
- "V4L2_ENC_CMD_RESUME\n");
- return -EBADFD;
- }
- cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h);
- cx18_unmute(cx);
- break;
-
- default:
- CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
- return -EINVAL;
- }
- return 0;
-}
-
-static int cx18_try_encoder_cmd(struct file *file, void *fh,
- struct v4l2_encoder_cmd *enc)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- switch (enc->cmd) {
- case V4L2_ENC_CMD_START:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
- enc->flags = 0;
- break;
-
- case V4L2_ENC_CMD_STOP:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
- enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
- break;
-
- case V4L2_ENC_CMD_PAUSE:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
- enc->flags = 0;
- break;
-
- case V4L2_ENC_CMD_RESUME:
- CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
- enc->flags = 0;
- break;
-
- default:
- CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
- return -EINVAL;
- }
- return 0;
-}
-
-static int cx18_log_status(struct file *file, void *fh)
-{
- struct cx18 *cx = fh2id(fh)->cx;
- struct v4l2_input vidin;
- struct v4l2_audio audin;
- int i;
-
- CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
- if (cx->hw_flags & CX18_HW_TVEEPROM) {
- struct tveeprom tv;
-
- cx18_read_eeprom(cx, &tv);
- }
- cx18_call_all(cx, core, log_status);
- cx18_get_input(cx, cx->active_input, &vidin);
- cx18_get_audio_input(cx, cx->audio_input, &audin);
- CX18_INFO("Video Input: %s\n", vidin.name);
- CX18_INFO("Audio Input: %s\n", audin.name);
- mutex_lock(&cx->gpio_lock);
- CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n",
- cx->gpio_dir, cx->gpio_val);
- mutex_unlock(&cx->gpio_lock);
- CX18_INFO("Tuner: %s\n",
- test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
- v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name);
- CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- struct cx18_stream *s = &cx->streams[i];
-
- if (s->video_dev == NULL || s->buffers == 0)
- continue;
- CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
- s->name, s->s_flags,
- atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
- / s->buffers,
- (s->buffers * s->buf_size) / 1024, s->buffers);
- }
- CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
- (long long)cx->mpg_data_received,
- (long long)cx->vbi_data_inserted);
- return 0;
-}
-
-static long cx18_default(struct file *file, void *fh, bool valid_prio,
- int cmd, void *arg)
-{
- struct cx18 *cx = fh2id(fh)->cx;
-
- switch (cmd) {
- case VIDIOC_INT_RESET: {
- u32 val = *(u32 *)arg;
-
- if ((val == 0) || (val & 0x01))
- cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
- (u32) CX18_GPIO_RESET_Z8F0811);
- break;
- }
-
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static const struct v4l2_ioctl_ops cx18_ioctl_ops = {
- .vidioc_querycap = cx18_querycap,
- .vidioc_s_audio = cx18_s_audio,
- .vidioc_g_audio = cx18_g_audio,
- .vidioc_enumaudio = cx18_enumaudio,
- .vidioc_enum_input = cx18_enum_input,
- .vidioc_cropcap = cx18_cropcap,
- .vidioc_s_crop = cx18_s_crop,
- .vidioc_g_crop = cx18_g_crop,
- .vidioc_g_input = cx18_g_input,
- .vidioc_s_input = cx18_s_input,
- .vidioc_g_frequency = cx18_g_frequency,
- .vidioc_s_frequency = cx18_s_frequency,
- .vidioc_s_tuner = cx18_s_tuner,
- .vidioc_g_tuner = cx18_g_tuner,
- .vidioc_g_enc_index = cx18_g_enc_index,
- .vidioc_g_std = cx18_g_std,
- .vidioc_s_std = cx18_s_std,
- .vidioc_log_status = cx18_log_status,
- .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap,
- .vidioc_encoder_cmd = cx18_encoder_cmd,
- .vidioc_try_encoder_cmd = cx18_try_encoder_cmd,
- .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap,
- .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap,
- .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap,
- .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap,
- .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap,
- .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap,
- .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap,
- .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap,
- .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap,
- .vidioc_g_chip_ident = cx18_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cx18_g_register,
- .vidioc_s_register = cx18_s_register,
-#endif
- .vidioc_default = cx18_default,
- .vidioc_streamon = cx18_streamon,
- .vidioc_streamoff = cx18_streamoff,
- .vidioc_reqbufs = cx18_reqbufs,
- .vidioc_querybuf = cx18_querybuf,
- .vidioc_qbuf = cx18_qbuf,
- .vidioc_dqbuf = cx18_dqbuf,
-};
-
-void cx18_set_funcs(struct video_device *vdev)
-{
- vdev->ioctl_ops = &cx18_ioctl_ops;
-}
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
deleted file mode 100644
index 9d598ab88615..000000000000
--- a/drivers/media/video/cx18/cx18-streams.c
+++ /dev/null
@@ -1,1060 +0,0 @@
-/*
- * cx18 init/start/stop/exit stream functions
- *
- * Derived from ivtv-streams.c
- *
- * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
- * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- * 02111-1307 USA
- */
-
-#include "cx18-driver.h"
-#include "cx18-io.h"
-#include "cx18-fileops.h"
-#include "cx18-mailbox.h"
-#include "cx18-i2c.h"
-#include "cx18-queue.h"
-#include "cx18-ioctl.h"
-#include "cx18-streams.h"
-#include "cx18-cards.h"
-#include "cx18-scb.h"
-#include "cx18-dvb.h"
-
-#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
-
-static struct v4l2_file_operations cx18_v4l2_enc_fops = {
- .owner = THIS_MODULE,
- .read = cx18_v4l2_read,
- .open = cx18_v4l2_open,
- .unlocked_ioctl = video_ioctl2,
- .release = cx18_v4l2_close,
- .poll = cx18_v4l2_enc_poll,
- .mmap = cx18_v4l2_mmap,
-};
-
-/* offset from 0 to register ts v4l2 minors on */
-#define CX18_V4L2_ENC_TS_OFFSET 16
-/* offset from 0 to register pcm v4l2 minors on */
-#define CX18_V4L2_ENC_PCM_OFFSET 24
-/* offset from 0 to register yuv v4l2 minors on */
-#define CX18_V4L2_ENC_YUV_OFFSET 32
-
-static struct {
- const char *name;
- int vfl_type;
- int num_offset;
- int dma;
- enum v4l2_buf_type buf_type;
-} cx18_stream_info[] = {
- { /* CX18_ENC_STREAM_TYPE_MPG */
- "encoder MPEG",
- VFL_TYPE_GRABBER, 0,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- },
- { /* CX18_ENC_STREAM_TYPE_TS */
- "TS",
- VFL_TYPE_GRABBER, -1,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- },
- { /* CX18_ENC_STREAM_TYPE_YUV */
- "encoder YUV",
- VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- },
- { /* CX18_ENC_STREAM_TYPE_VBI */
- "encoder VBI",
- VFL_TYPE_VBI, 0,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
- },
- { /* CX18_ENC_STREAM_TYPE_PCM */
- "encoder PCM audio",
- VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
- },
- { /* CX18_ENC_STREAM_TYPE_IDX */
- "encoder IDX",
- VFL_TYPE_GRABBER, -1,
- PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- },
- { /* CX18_ENC_STREAM_TYPE_RAD */
- "encoder radio",
- VFL_TYPE_RADIO, 0,
- PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
- },
-};
-
-
-void cx18_dma_free(struct videobuf_queue *q,
- struct cx18_stream *s, struct cx18_videobuf_buffer *buf)
-{
- videobuf_waiton(q, &buf->vb, 0, 0);
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int cx18_prepare_buffer(struct videobuf_queue *q,
- struct cx18_stream *s,
- struct cx18_videobuf_buffer *buf,
- u32 pixelformat,
- unsigned int width, unsigned int height,
- enum v4l2_field field)
-{
- struct cx18 *cx = s->cx;
- int rc = 0;
-
- /* check settings */
- buf->bytes_used = 0;
-
- if ((width < 48) || (height < 32))
- return -EINVAL;
-
- buf->vb.size = (width * height * 2);
- if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
- return -EINVAL;
-
- /* alloc + fill struct (if changed) */
- if (buf->vb.width != width || buf->vb.height != height ||
- buf->vb.field != field || s->pixelformat != pixelformat ||
- buf->tvnorm != cx->std) {
-
- buf->vb.width = width;
- buf->vb.height = height;
- buf->vb.field = field;
- buf->tvnorm = cx->std;
- s->pixelformat = pixelformat;
-
- /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
- UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- s->vb_bytes_per_frame = height * 720 * 3 / 2;
- else
- s->vb_bytes_per_frame = height * 720 * 2;
- cx18_dma_free(q, s, buf);
- }
-
- if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size))
- return -EINVAL;
-
- if (buf->vb.field == 0)
- buf->vb.field = V4L2_FIELD_INTERLACED;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- buf->vb.width = width;
- buf->vb.height = height;
- buf->vb.field = field;
- buf->tvnorm = cx->std;
- s->pixelformat = pixelformat;
-
- /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2)))
- UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- s->vb_bytes_per_frame = height * 720 * 3 / 2;
- else
- s->vb_bytes_per_frame = height * 720 * 2;
- rc = videobuf_iolock(q, &buf->vb, NULL);
- if (rc != 0)
- goto fail;
- }
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- cx18_dma_free(q, s, buf);
- return rc;
-
-}
-
-/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576)
- 1440 is a single line of 4:2:2 YUV at 720 luma samples wide
-*/
-#define VB_MIN_BUFFERS 32
-#define VB_MIN_BUFSIZE 4147200
-
-static int buffer_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx18_stream *s = q->priv_data;
- struct cx18 *cx = s->cx;
-
- *size = 2 * cx->cxhdl.width * cx->cxhdl.height;
- if (*count == 0)
- *count = VB_MIN_BUFFERS;
-
- while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE)
- (*count)--;
-
- q->field = V4L2_FIELD_INTERLACED;
- q->last = V4L2_FIELD_INTERLACED;
-
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx18_videobuf_buffer *buf =
- container_of(vb, struct cx18_videobuf_buffer, vb);
- struct cx18_stream *s = q->priv_data;
- struct cx18 *cx = s->cx;
-
- return cx18_prepare_buffer(q, s, buf, s->pixelformat,
- cx->cxhdl.width, cx->cxhdl.height, field);
-}
-
-static void buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx18_videobuf_buffer *buf =
- container_of(vb, struct cx18_videobuf_buffer, vb);
- struct cx18_stream *s = q->priv_data;
-
- cx18_dma_free(q, s, buf);
-}
-
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct cx18_videobuf_buffer *buf =
- container_of(vb, struct cx18_videobuf_buffer, vb);
- struct cx18_stream *s = q->priv_data;
-
- buf->vb.state = VIDEOBUF_QUEUED;
-
- list_add_tail(&buf->vb.queue, &s->vb_capture);
-}
-
-static struct videobuf_queue_ops cx18_videobuf_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-static void cx18_stream_init(struct cx18 *cx, int type)
-{
- struct cx18_stream *s = &cx->streams[type];
- struct video_device *video_dev = s->video_dev;
-
- /* we need to keep video_dev, so restore it afterwards */
- memset(s, 0, sizeof(*s));
- s->video_dev = video_dev;
-
- /* initialize cx18_stream fields */
- s->dvb = NULL;
- s->cx = cx;
- s->type = type;
- s->name = cx18_stream_info[type].name;
- s->handle = CX18_INVALID_TASK_HANDLE;
-
- s->dma = cx18_stream_info[type].dma;
- s->buffers = cx->stream_buffers[type];
- s->buf_size = cx->stream_buf_size[type];
- INIT_LIST_HEAD(&s->buf_pool);
- s->bufs_per_mdl = 1;
- s->mdl_size = s->buf_size * s->bufs_per_mdl;
-
- init_waitqueue_head(&s->waitq);
- s->id = -1;
- spin_lock_init(&s->q_free.lock);
- cx18_queue_init(&s->q_free);
- spin_lock_init(&s->q_busy.lock);
- cx18_queue_init(&s->q_busy);
- spin_lock_init(&s->q_full.lock);
- cx18_queue_init(&s->q_full);
- spin_lock_init(&s->q_idle.lock);
- cx18_queue_init(&s->q_idle);
-
- INIT_WORK(&s->out_work_order, cx18_out_work_handler);
-
- INIT_LIST_HEAD(&s->vb_capture);
- s->vb_timeout.function = cx18_vb_timeout;
- s->vb_timeout.data = (unsigned long)s;
- init_timer(&s->vb_timeout);
- spin_lock_init(&s->vb_lock);
- if (type == CX18_ENC_STREAM_TYPE_YUV) {
- spin_lock_init(&s->vbuf_q_lock);
-
- s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops,
- &cx->pci_dev->dev, &s->vbuf_q_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx18_videobuf_buffer),
- s, &cx->serialize_lock);
-
- /* Assume the previous pixel default */
- s->pixelformat = V4L2_PIX_FMT_HM12;
- s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2;
- }
-}
-
-static int cx18_prep_dev(struct cx18 *cx, int type)
-{
- struct cx18_stream *s = &cx->streams[type];
- u32 cap = cx->v4l2_cap;
- int num_offset = cx18_stream_info[type].num_offset;
- int num = cx->instance + cx18_first_minor + num_offset;
-
- /*
- * These five fields are always initialized.
- * For analog capture related streams, if video_dev == NULL then the
- * stream is not in use.
- * For the TS stream, if dvb == NULL then the stream is not in use.
- * In those cases no other fields but these four can be used.
- */
- s->video_dev = NULL;
- s->dvb = NULL;
- s->cx = cx;
- s->type = type;
- s->name = cx18_stream_info[type].name;
-
- /* Check whether the radio is supported */
- if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
- return 0;
-
- /* Check whether VBI is supported */
- if (type == CX18_ENC_STREAM_TYPE_VBI &&
- !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
- return 0;
-
- /* User explicitly selected 0 buffers for these streams, so don't
- create them. */
- if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
- cx->stream_buffers[type] == 0) {
- CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
- return 0;
- }
-
- cx18_stream_init(cx, type);
-
- /* Allocate the cx18_dvb struct only for the TS on cards with DTV */
- if (type == CX18_ENC_STREAM_TYPE_TS) {
- if (cx->card->hw_all & CX18_HW_DVB) {
- s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL);
- if (s->dvb == NULL) {
- CX18_ERR("Couldn't allocate cx18_dvb structure"
- " for %s\n", s->name);
- return -ENOMEM;
- }
- } else {
- /* Don't need buffers for the TS, if there is no DVB */
- s->buffers = 0;
- }
- }
-
- if (num_offset == -1)
- return 0;
-
- /* allocate and initialize the v4l2 video device structure */
- s->video_dev = video_device_alloc();
- if (s->video_dev == NULL) {
- CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
- s->name);
- return -ENOMEM;
- }
-
- snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
- cx->v4l2_dev.name, s->name);
-
- s->video_dev->num = num;
- s->video_dev->v4l2_dev = &cx->v4l2_dev;
- s->video_dev->fops = &cx18_v4l2_enc_fops;
- s->video_dev->release = video_device_release;
- s->video_dev->tvnorms = V4L2_STD_ALL;
- s->video_dev->lock = &cx->serialize_lock;
- set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags);
- cx18_set_funcs(s->video_dev);
- return 0;
-}
-
-/* Initialize v4l2 variables and register v4l2 devices */
-int cx18_streams_setup(struct cx18 *cx)
-{
- int type, ret;
-
- /* Setup V4L2 Devices */
- for (type = 0; type < CX18_MAX_STREAMS; type++) {
- /* Prepare device */
- ret = cx18_prep_dev(cx, type);
- if (ret < 0)
- break;
-
- /* Allocate Stream */
- ret = cx18_stream_alloc(&cx->streams[type]);
- if (ret < 0)
- break;
- }
- if (type == CX18_MAX_STREAMS)
- return 0;
-
- /* One or more streams could not be initialized. Clean 'em all up. */
- cx18_streams_cleanup(cx, 0);
- return ret;
-}
-
-static int cx18_reg_dev(struct cx18 *cx, int type)
-{
- struct cx18_stream *s = &cx->streams[type];
- int vfl_type = cx18_stream_info[type].vfl_type;
- const char *name;
- int num, ret;
-
- if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) {
- ret = cx18_dvb_register(s);
- if (ret < 0) {
- CX18_ERR("DVB failed to register\n");
- return ret;
- }
- }
-
- if (s->video_dev == NULL)
- return 0;
-
- num = s->video_dev->num;
- /* card number + user defined offset + device offset */
- if (type != CX18_ENC_STREAM_TYPE_MPG) {
- struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
-
- if (s_mpg->video_dev)
- num = s_mpg->video_dev->num
- + cx18_stream_info[type].num_offset;
- }
- video_set_drvdata(s->video_dev, s);
-
- /* Register device. First try the desired minor, then any free one. */
- ret = video_register_device_no_warn(s->video_dev, vfl_type, num);
- if (ret < 0) {
- CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
- s->name, num);
- video_device_release(s->video_dev);
- s->video_dev = NULL;
- return ret;
- }
-
- name = video_device_node_name(s->video_dev);
-
- switch (vfl_type) {
- case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n",
- name, s->name, cx->stream_buffers[type],
- cx->stream_buf_size[type] / 1024,
- (cx->stream_buf_size[type] * 100 / 1024) % 100);
- break;
-
- case VFL_TYPE_RADIO:
- CX18_INFO("Registered device %s for %s\n", name, s->name);
- break;
-
- case VFL_TYPE_VBI:
- if (cx->stream_buffers[type])
- CX18_INFO("Registered device %s for %s "
- "(%d x %d bytes)\n",
- name, s->name, cx->stream_buffers[type],
- cx->stream_buf_size[type]);
- else
- CX18_INFO("Registered device %s for %s\n",
- name, s->name);
- break;
- }
-
- return 0;
-}
-
-/* Register v4l2 devices */
-int cx18_streams_register(struct cx18 *cx)
-{
- int type;
- int err;
- int ret = 0;
-
- /* Register V4L2 devices */
- for (type = 0; type < CX18_MAX_STREAMS; type++) {
- err = cx18_reg_dev(cx, type);
- if (err && ret == 0)
- ret = err;
- }
-
- if (ret == 0)
- return 0;
-
- /* One or more streams could not be initialized. Clean 'em all up. */
- cx18_streams_cleanup(cx, 1);
- return ret;
-}
-
-/* Unregister v4l2 devices */
-void cx18_streams_cleanup(struct cx18 *cx, int unregister)
-{
- struct video_device *vdev;
- int type;
-
- /* Teardown all streams */
- for (type = 0; type < CX18_MAX_STREAMS; type++) {
-
- /* The TS has a cx18_dvb structure, not a video_device */
- if (type == CX18_ENC_STREAM_TYPE_TS) {
- if (cx->streams[type].dvb != NULL) {
- if (unregister)
- cx18_dvb_unregister(&cx->streams[type]);
- kfree(cx->streams[type].dvb);
- cx->streams[type].dvb = NULL;
- cx18_stream_free(&cx->streams[type]);
- }
- continue;
- }
-
- /* No struct video_device, but can have buffers allocated */
- if (type == CX18_ENC_STREAM_TYPE_IDX) {
- /* If the module params didn't inhibit IDX ... */
- if (cx->stream_buffers[type] != 0) {
- cx->stream_buffers[type] = 0;
- /*
- * Before calling cx18_stream_free(),
- * check if the IDX stream was actually set up.
- * Needed, since the cx18_probe() error path
- * exits through here as well as normal clean up
- */
- if (cx->streams[type].buffers != 0)
- cx18_stream_free(&cx->streams[type]);
- }
- continue;
- }
-
- /* If struct video_device exists, can have buffers allocated */
- vdev = cx->streams[type].video_dev;
-
- cx->streams[type].video_dev = NULL;
- if (vdev == NULL)
- continue;
-
- if (type == CX18_ENC_STREAM_TYPE_YUV)
- videobuf_mmap_free(&cx->streams[type].vbuf_q);
-
- cx18_stream_free(&cx->streams[type]);
-
- /* Unregister or release device */
- if (unregister)
- video_unregister_device(vdev);
- else
- video_device_release(vdev);
- }
-}
-
-static void cx18_vbi_setup(struct cx18_stream *s)
-{
- struct cx18 *cx = s->cx;
- int raw = cx18_raw_vbi(cx);
- u32 data[CX2341X_MBOX_MAX_DATA];
- int lines;
-
- if (cx->is_60hz) {
- cx->vbi.count = 12;
- cx->vbi.start[0] = 10;
- cx->vbi.start[1] = 273;
- } else { /* PAL/SECAM */
- cx->vbi.count = 18;
- cx->vbi.start[0] = 6;
- cx->vbi.start[1] = 318;
- }
-
- /* setup VBI registers */
- if (raw)
- v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi);
- else
- v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced);
-
- /*
- * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
- * VBI when the first analog capture channel starts, as once it starts
- * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
- * (i.e. for the VBI capture channels). We also send it for each
- * analog capture channel anyway just to make sure we get the proper
- * behavior
- */
- if (raw) {
- lines = cx->vbi.count * 2;
- } else {
- /*
- * For 525/60 systems, according to the VIP 2 & BT.656 std:
- * The EAV RP code's Field bit toggles on line 4, a few lines
- * after the Vertcal Blank bit has already toggled.
- * Tell the encoder to capture 21-4+1=18 lines per field,
- * since we want lines 10 through 21.
- *
- * For 625/50 systems, according to the VIP 2 & BT.656 std:
- * The EAV RP code's Field bit toggles on line 1, a few lines
- * after the Vertcal Blank bit has already toggled.
- * (We've actually set the digitizer so that the Field bit
- * toggles on line 2.) Tell the encoder to capture 23-2+1=22
- * lines per field, since we want lines 6 through 23.
- */
- lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2;
- }
-
- data[0] = s->handle;
- /* Lines per field */
- data[1] = (lines / 2) | ((lines / 2) << 16);
- /* bytes per line */
- data[2] = (raw ? vbi_active_samples
- : (cx->is_60hz ? vbi_hblank_samples_60Hz
- : vbi_hblank_samples_50Hz));
- /* Every X number of frames a VBI interrupt arrives
- (frames as in 25 or 30 fps) */
- data[3] = 1;
- /*
- * Set the SAV/EAV RP codes to look for as start/stop points
- * when in VIP-1.1 mode
- */
- if (raw) {
- /*
- * Start codes for beginning of "active" line in vertical blank
- * 0x20 ( VerticalBlank )
- * 0x60 ( EvenField VerticalBlank )
- */
- data[4] = 0x20602060;
- /*
- * End codes for end of "active" raw lines and regular lines
- * 0x30 ( VerticalBlank HorizontalBlank)
- * 0x70 ( EvenField VerticalBlank HorizontalBlank)
- * 0x90 (Task HorizontalBlank)
- * 0xd0 (Task EvenField HorizontalBlank)
- */
- data[5] = 0x307090d0;
- } else {
- /*
- * End codes for active video, we want data in the hblank region
- * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
- * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
- *
- * Since the V bit is only allowed to toggle in the EAV RP code,
- * just before the first active region line, these two
- * are problematic:
- * 0x90 (Task HorizontalBlank)
- * 0xd0 (Task EvenField HorizontalBlank)
- *
- * We have set the digitzer such that we don't have to worry
- * about these problem codes.
- */
- data[4] = 0xB0F0B0F0;
- /*
- * Start codes for beginning of active line in vertical blank
- * 0xa0 (Task VerticalBlank )
- * 0xe0 (Task EvenField VerticalBlank )
- */
- data[5] = 0xA0E0A0E0;
- }
-
- CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
- data[0], data[1], data[2], data[3], data[4], data[5]);
-
- cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
-}
-
-void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
-{
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
- struct cx18_mdl *mdl;
-
- if (!cx18_stream_enabled(s))
- return;
-
- /* Return if the firmware is not running low on MDLs */
- if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
- CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
- return;
-
- /* Return if there are no MDLs to rotate back to the firmware */
- if (atomic_read(&s->q_full.depth) < 2)
- return;
-
- /*
- * Take the oldest IDX MDL still holding data, and discard its index
- * entries by scheduling the MDL to go back to the firmware
- */
- mdl = cx18_dequeue(s, &s->q_full);
- if (mdl != NULL)
- cx18_enqueue(s, mdl, &s->q_free);
-}
-
-static
-struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
- struct cx18_mdl *mdl)
-{
- struct cx18 *cx = s->cx;
- struct cx18_queue *q;
-
- /* Don't give it to the firmware, if we're not running a capture */
- if (s->handle == CX18_INVALID_TASK_HANDLE ||
- test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
- !test_bit(CX18_F_S_STREAMING, &s->s_flags))
- return cx18_enqueue(s, mdl, &s->q_free);
-
- q = cx18_enqueue(s, mdl, &s->q_busy);
- if (q != &s->q_busy)
- return q; /* The firmware has the max MDLs it can handle */
-
- cx18_mdl_sync_for_device(s, mdl);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
- s->bufs_per_mdl, mdl->id, s->mdl_size);
- return q;
-}
-
-static
-void _cx18_stream_load_fw_queue(struct cx18_stream *s)
-{
- struct cx18_queue *q;
- struct cx18_mdl *mdl;
-
- if (atomic_read(&s->q_free.depth) == 0 ||
- atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
- return;
-
- /* Move from q_free to q_busy notifying the firmware, until the limit */
- do {
- mdl = cx18_dequeue(s, &s->q_free);
- if (mdl == NULL)
- break;
- q = _cx18_stream_put_mdl_fw(s, mdl);
- } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
- && q == &s->q_busy);
-}
-
-void cx18_out_work_handler(struct work_struct *work)
-{
- struct cx18_stream *s =
- container_of(work, struct cx18_stream, out_work_order);
-
- _cx18_stream_load_fw_queue(s);
-}
-
-static void cx18_stream_configure_mdls(struct cx18_stream *s)
-{
- cx18_unload_queues(s);
-
- switch (s->type) {
- case CX18_ENC_STREAM_TYPE_YUV:
- /*
- * Height should be a multiple of 32 lines.
- * Set the MDL size to the exact size needed for one frame.
- * Use enough buffers per MDL to cover the MDL size
- */
- if (s->pixelformat == V4L2_PIX_FMT_HM12)
- s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2;
- else
- s->mdl_size = 720 * s->cx->cxhdl.height * 2;
- s->bufs_per_mdl = s->mdl_size / s->buf_size;
- if (s->mdl_size % s->buf_size)
- s->bufs_per_mdl++;
- break;
- case CX18_ENC_STREAM_TYPE_VBI:
- s->bufs_per_mdl = 1;
- if (cx18_raw_vbi(s->cx)) {
- s->mdl_size = (s->cx->is_60hz ? 12 : 18)
- * 2 * vbi_active_samples;
- } else {
- /*
- * See comment in cx18_vbi_setup() below about the
- * extra lines we capture in sliced VBI mode due to
- * the lines on which EAV RP codes toggle.
- */
- s->mdl_size = s->cx->is_60hz
- ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz
- : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz;
- }
- break;
- default:
- s->bufs_per_mdl = 1;
- s->mdl_size = s->buf_size * s->bufs_per_mdl;
- break;
- }
-
- cx18_load_queues(s);
-}
-
-int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
-{
- u32 data[MAX_MB_ARGUMENTS];
- struct cx18 *cx = s->cx;
- int captype = 0;
- struct cx18_stream *s_idx;
-
- if (!cx18_stream_enabled(s))
- return -EINVAL;
-
- CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
-
- switch (s->type) {
- case CX18_ENC_STREAM_TYPE_MPG:
- captype = CAPTURE_CHANNEL_TYPE_MPEG;
- cx->mpg_data_received = cx->vbi_data_inserted = 0;
- cx->dualwatch_jiffies = jiffies;
- cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode);
- cx->search_pack_header = 0;
- break;
-
- case CX18_ENC_STREAM_TYPE_IDX:
- captype = CAPTURE_CHANNEL_TYPE_INDEX;
- break;
- case CX18_ENC_STREAM_TYPE_TS:
- captype = CAPTURE_CHANNEL_TYPE_TS;
- break;
- case CX18_ENC_STREAM_TYPE_YUV:
- captype = CAPTURE_CHANNEL_TYPE_YUV;
- break;
- case CX18_ENC_STREAM_TYPE_PCM:
- captype = CAPTURE_CHANNEL_TYPE_PCM;
- break;
- case CX18_ENC_STREAM_TYPE_VBI:
-#ifdef CX18_ENCODER_PARSES_SLICED
- captype = cx18_raw_vbi(cx) ?
- CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
-#else
- /*
- * Currently we set things up so that Sliced VBI from the
- * digitizer is handled as Raw VBI by the encoder
- */
- captype = CAPTURE_CHANNEL_TYPE_VBI;
-#endif
- cx->vbi.frame = 0;
- cx->vbi.inserted_frame = 0;
- memset(cx->vbi.sliced_mpeg_size,
- 0, sizeof(cx->vbi.sliced_mpeg_size));
- break;
- default:
- return -EINVAL;
- }
-
- /* Clear Streamoff flags in case left from last capture */
- clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
-
- cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
- s->handle = data[0];
- cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
-
- /*
- * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
- * set up all the parameters, as it is not obvious which parameters the
- * firmware shares across capture channel types and which it does not.
- *
- * Some of the cx18_vapi() calls below apply to only certain capture
- * channel types. We're hoping there's no harm in calling most of them
- * anyway, as long as the values are all consistent. Setting some
- * shared parameters will have no effect once an analog capture channel
- * has started streaming.
- */
- if (captype != CAPTURE_CHANNEL_TYPE_TS) {
- cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
-
- /*
- * Audio related reset according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
- */
- if (atomic_read(&cx->ana_capturing) == 0)
- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
- s->handle, 12);
-
- /*
- * Number of lines for Field 1 & Field 2 according to
- * Documentation/video4linux/cx2341x/fw-encoder-api.txt
- * Field 1 is 312 for 625 line systems in BT.656
- * Field 2 is 313 for 625 line systems in BT.656
- */
- cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
- s->handle, 312, 313);
-
- if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
- cx18_vbi_setup(s);
-
- /*
- * Select to receive I, P, and B frame index entries, if the
- * index stream is enabled. Otherwise disable index entry
- * generation.
- */
- s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
- cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
- s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
-
- /* Call out to the common CX2341x API setup for user controls */
- cx->cxhdl.priv = s;
- cx2341x_handler_setup(&cx->cxhdl);
-
- /*
- * When starting a capture and we're set for radio,
- * ensure the video is muted, despite the user control.
- */
- if (!cx->cxhdl.video_mute &&
- test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
- cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
- (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1);
-
- /* Enable the Video Format Converter for UYVY 4:2:2 support,
- * rather than the default HM12 Macroblovk 4:2:0 support.
- */
- if (captype == CAPTURE_CHANNEL_TYPE_YUV) {
- if (s->pixelformat == V4L2_PIX_FMT_UYVY)
- cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
- s->handle, 1);
- else
- /* If in doubt, default to HM12 */
- cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2,
- s->handle, 0);
- }
- }
-
- if (atomic_read(&cx->tot_capturing) == 0) {
- cx2341x_handler_set_busy(&cx->cxhdl, 1);
- clear_bit(CX18_F_I_EOS, &cx->i_flags);
- cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK);
- }
-
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
- (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
-
- /* Init all the cpu_mdls for this stream */
- cx18_stream_configure_mdls(s);
- _cx18_stream_load_fw_queue(s);
-
- /* begin_capture */
- if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
- CX18_DEBUG_WARN("Error starting capture!\n");
- /* Ensure we're really not capturing before releasing MDLs */
- set_bit(CX18_F_S_STOPPING, &s->s_flags);
- if (s->type == CX18_ENC_STREAM_TYPE_MPG)
- cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
- else
- cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
- clear_bit(CX18_F_S_STREAMING, &s->s_flags);
- /* FIXME - CX18_F_S_STREAMOFF as well? */
- cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
- cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- s->handle = CX18_INVALID_TASK_HANDLE;
- clear_bit(CX18_F_S_STOPPING, &s->s_flags);
- if (atomic_read(&cx->tot_capturing) == 0) {
- set_bit(CX18_F_I_EOS, &cx->i_flags);
- cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
- }
- return -EINVAL;
- }
-
- /* you're live! sit back and await interrupts :) */
- if (captype != CAPTURE_CHANNEL_TYPE_TS)
- atomic_inc(&cx->ana_capturing);
- atomic_inc(&cx->tot_capturing);
- return 0;
-}
-EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
-
-void cx18_stop_all_captures(struct cx18 *cx)
-{
- int i;
-
- for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
- struct cx18_stream *s = &cx->streams[i];
-
- if (!cx18_stream_enabled(s))
- continue;
- if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
- cx18_stop_v4l2_encode_stream(s, 0);
- }
-}
-
-int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
-{
- struct cx18 *cx = s->cx;
-
- if (!cx18_stream_enabled(s))
- return -EINVAL;
-
- /* This function assumes that you are allowed to stop the capture
- and that we are actually capturing */
-
- CX18_DEBUG_INFO("Stop Capture\n");
-
- if (atomic_read(&cx->tot_capturing) == 0)
- return 0;
-
- set_bit(CX18_F_S_STOPPING, &s->s_flags);
- if (s->type == CX18_ENC_STREAM_TYPE_MPG)
- cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
- else
- cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
-
- if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
- CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
- }
-
- if (s->type != CX18_ENC_STREAM_TYPE_TS)
- atomic_dec(&cx->ana_capturing);
- atomic_dec(&cx->tot_capturing);
-
- /* Clear capture and no-read bits */
- clear_bit(CX18_F_S_STREAMING, &s->s_flags);
-
- /* Tell the CX23418 it can't use our buffers anymore */
- cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
-
- cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- s->handle = CX18_INVALID_TASK_HANDLE;
- clear_bit(CX18_F_S_STOPPING, &s->s_flags);
-
- if (atomic_read(&cx->tot_capturing) > 0)
- return 0;
-
- cx2341x_handler_set_busy(&cx->cxhdl, 0);
- cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
- wake_up(&s->waitq);
-
- return 0;
-}
-EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
-
-u32 cx18_find_handle(struct cx18 *cx)
-{
- int i;
-
- /* find first available handle to be used for global settings */
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- struct cx18_stream *s = &cx->streams[i];
-
- if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
- return s->handle;
- }
- return CX18_INVALID_TASK_HANDLE;
-}
-
-struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
-{
- int i;
- struct cx18_stream *s;
-
- if (handle == CX18_INVALID_TASK_HANDLE)
- return NULL;
-
- for (i = 0; i < CX18_MAX_STREAMS; i++) {
- s = &cx->streams[i];
- if (s->handle != handle)
- continue;
- if (cx18_stream_enabled(s))
- return s;
- }
- return NULL;
-}
diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
deleted file mode 100644
index 446f692aabb7..000000000000
--- a/drivers/media/video/cx231xx/Kconfig
+++ /dev/null
@@ -1,51 +0,0 @@
-config VIDEO_CX231XX
- tristate "Conexant cx231xx USB video capture support"
- depends on VIDEO_DEV && I2C
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- depends on RC_CORE
- select VIDEOBUF_VMALLOC
- select VIDEO_CX25840
- select VIDEO_CX2341X
-
- ---help---
- This is a video4linux driver for Conexant 231xx USB based TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called cx231xx
-
-config VIDEO_CX231XX_RC
- bool "Conexant cx231xx Remote Controller additional support"
- depends on RC_CORE
- depends on VIDEO_CX231XX
- default y
- ---help---
- cx231xx hardware has a builtin RX/TX support. However, a few
- designs opted to not use it, but, instead, some other hardware.
- This module enables the usage of those other hardware, like the
- ones used with ISDB-T boards.
-
- On most cases, all you need for IR is mceusb module.
-
-config VIDEO_CX231XX_ALSA
- tristate "Conexant Cx231xx ALSA audio module"
- depends on VIDEO_CX231XX && SND
- select SND_PCM
-
- ---help---
- This is an ALSA driver for Cx231xx USB based TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called cx231xx-alsa
-
-config VIDEO_CX231XX_DVB
- tristate "DVB/ATSC Support for Cx231xx based TV cards"
- depends on VIDEO_CX231XX && DVB_CORE && DVB_CAPTURE_DRIVERS
- select VIDEOBUF_DVB
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select DVB_MB86A20S if !DVB_FE_CUSTOMISE
-
- ---help---
- This adds support for DVB cards based on the
- Conexant cx231xx chips.
diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
deleted file mode 100644
index b3348975c7c2..000000000000
--- a/drivers/media/video/cx231xx/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-cx231xx-y += cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o
-cx231xx-y += cx231xx-avcore.o cx231xx-417.o cx231xx-pcb-cfg.o cx231xx-vbi.o
-cx231xx-$(CONFIG_VIDEO_CX231XX_RC) += cx231xx-input.o
-
-cx231xx-alsa-objs := cx231xx-audio.o
-
-obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
-obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
-obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/dvb/dvb-usb
-
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
deleted file mode 100644
index ce2f62238a19..000000000000
--- a/drivers/media/video/cx231xx/cx231xx-417.c
+++ /dev/null
@@ -1,2195 +0,0 @@
-/*
- *
- * Support for a cx23417 mpeg encoder via cx231xx host port.
- *
- * (c) 2004 Jelle Foks <jelle@foks.us>
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2008 Steven Toth <stoth@linuxtv.org>
- * - CX23885/7/8 support
- *
- * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/),
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/vmalloc.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/cx2341x.h>
-#include <linux/usb.h>
-
-#include "cx231xx.h"
-/*#include "cx23885-ioctl.h"*/
-
-#define CX231xx_FIRM_IMAGE_SIZE 376836
-#define CX231xx_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
-
-/* for polaris ITVC */
-#define ITVC_WRITE_DIR 0x03FDFC00
-#define ITVC_READ_DIR 0x0001FC00
-
-#define MCI_MEMORY_DATA_BYTE0 0x00
-#define MCI_MEMORY_DATA_BYTE1 0x08
-#define MCI_MEMORY_DATA_BYTE2 0x10
-#define MCI_MEMORY_DATA_BYTE3 0x18
-
-#define MCI_MEMORY_ADDRESS_BYTE2 0x20
-#define MCI_MEMORY_ADDRESS_BYTE1 0x28
-#define MCI_MEMORY_ADDRESS_BYTE0 0x30
-
-#define MCI_REGISTER_DATA_BYTE0 0x40
-#define MCI_REGISTER_DATA_BYTE1 0x48
-#define MCI_REGISTER_DATA_BYTE2 0x50
-#define MCI_REGISTER_DATA_BYTE3 0x58
-
-#define MCI_REGISTER_ADDRESS_BYTE0 0x60
-#define MCI_REGISTER_ADDRESS_BYTE1 0x68
-
-#define MCI_REGISTER_MODE 0x70
-
-/* Read and write modes for polaris ITVC */
-#define MCI_MODE_REGISTER_READ 0x000
-#define MCI_MODE_REGISTER_WRITE 0x100
-#define MCI_MODE_MEMORY_READ 0x000
-#define MCI_MODE_MEMORY_WRITE 0x4000
-
-static unsigned int mpegbufs = 8;
-module_param(mpegbufs, int, 0644);
-MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
-static unsigned int mpeglines = 128;
-module_param(mpeglines, int, 0644);
-MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
-static unsigned int mpeglinesize = 512;
-module_param(mpeglinesize, int, 0644);
-MODULE_PARM_DESC(mpeglinesize,
- "number of bytes in each line of an MPEG buffer, range 512-1024");
-
-static unsigned int v4l_debug = 1;
-module_param(v4l_debug, int, 0644);
-MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
-struct cx231xx_dmaqueue *dma_qq;
-#define dprintk(level, fmt, arg...)\
- do { if (v4l_debug >= level) \
- printk(KERN_INFO "%s: " fmt, \
- (dev) ? dev->name : "cx231xx[?]", ## arg); \
- } while (0)
-
-static struct cx231xx_tvnorm cx231xx_tvnorms[] = {
- {
- .name = "NTSC-M",
- .id = V4L2_STD_NTSC_M,
- }, {
- .name = "NTSC-JP",
- .id = V4L2_STD_NTSC_M_JP,
- }, {
- .name = "PAL-BG",
- .id = V4L2_STD_PAL_BG,
- }, {
- .name = "PAL-DK",
- .id = V4L2_STD_PAL_DK,
- }, {
- .name = "PAL-I",
- .id = V4L2_STD_PAL_I,
- }, {
- .name = "PAL-M",
- .id = V4L2_STD_PAL_M,
- }, {
- .name = "PAL-N",
- .id = V4L2_STD_PAL_N,
- }, {
- .name = "PAL-Nc",
- .id = V4L2_STD_PAL_Nc,
- }, {
- .name = "PAL-60",
- .id = V4L2_STD_PAL_60,
- }, {
- .name = "SECAM-L",
- .id = V4L2_STD_SECAM_L,
- }, {
- .name = "SECAM-DK",
- .id = V4L2_STD_SECAM_DK,
- }
-};
-
-/* ------------------------------------------------------------------ */
-enum cx231xx_capture_type {
- CX231xx_MPEG_CAPTURE,
- CX231xx_RAW_CAPTURE,
- CX231xx_RAW_PASSTHRU_CAPTURE
-};
-enum cx231xx_capture_bits {
- CX231xx_RAW_BITS_NONE = 0x00,
- CX231xx_RAW_BITS_YUV_CAPTURE = 0x01,
- CX231xx_RAW_BITS_PCM_CAPTURE = 0x02,
- CX231xx_RAW_BITS_VBI_CAPTURE = 0x04,
- CX231xx_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
- CX231xx_RAW_BITS_TO_HOST_CAPTURE = 0x10
-};
-enum cx231xx_capture_end {
- CX231xx_END_AT_GOP, /* stop at the end of gop, generate irq */
- CX231xx_END_NOW, /* stop immediately, no irq */
-};
-enum cx231xx_framerate {
- CX231xx_FRAMERATE_NTSC_30, /* NTSC: 30fps */
- CX231xx_FRAMERATE_PAL_25 /* PAL: 25fps */
-};
-enum cx231xx_stream_port {
- CX231xx_OUTPUT_PORT_MEMORY,
- CX231xx_OUTPUT_PORT_STREAMING,
- CX231xx_OUTPUT_PORT_SERIAL
-};
-enum cx231xx_data_xfer_status {
- CX231xx_MORE_BUFFERS_FOLLOW,
- CX231xx_LAST_BUFFER,
-};
-enum cx231xx_picture_mask {
- CX231xx_PICTURE_MASK_NONE,
- CX231xx_PICTURE_MASK_I_FRAMES,
- CX231xx_PICTURE_MASK_I_P_FRAMES = 0x3,
- CX231xx_PICTURE_MASK_ALL_FRAMES = 0x7,
-};
-enum cx231xx_vbi_mode_bits {
- CX231xx_VBI_BITS_SLICED,
- CX231xx_VBI_BITS_RAW,
-};
-enum cx231xx_vbi_insertion_bits {
- CX231xx_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
- CX231xx_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
- CX231xx_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
- CX231xx_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
- CX231xx_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
-};
-enum cx231xx_dma_unit {
- CX231xx_DMA_BYTES,
- CX231xx_DMA_FRAMES,
-};
-enum cx231xx_dma_transfer_status_bits {
- CX231xx_DMA_TRANSFER_BITS_DONE = 0x01,
- CX231xx_DMA_TRANSFER_BITS_ERROR = 0x04,
- CX231xx_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
-};
-enum cx231xx_pause {
- CX231xx_PAUSE_ENCODING,
- CX231xx_RESUME_ENCODING,
-};
-enum cx231xx_copyright {
- CX231xx_COPYRIGHT_OFF,
- CX231xx_COPYRIGHT_ON,
-};
-enum cx231xx_notification_type {
- CX231xx_NOTIFICATION_REFRESH,
-};
-enum cx231xx_notification_status {
- CX231xx_NOTIFICATION_OFF,
- CX231xx_NOTIFICATION_ON,
-};
-enum cx231xx_notification_mailbox {
- CX231xx_NOTIFICATION_NO_MAILBOX = -1,
-};
-enum cx231xx_field1_lines {
- CX231xx_FIELD1_SAA7114 = 0x00EF, /* 239 */
- CX231xx_FIELD1_SAA7115 = 0x00F0, /* 240 */
- CX231xx_FIELD1_MICRONAS = 0x0105, /* 261 */
-};
-enum cx231xx_field2_lines {
- CX231xx_FIELD2_SAA7114 = 0x00EF, /* 239 */
- CX231xx_FIELD2_SAA7115 = 0x00F0, /* 240 */
- CX231xx_FIELD2_MICRONAS = 0x0106, /* 262 */
-};
-enum cx231xx_custom_data_type {
- CX231xx_CUSTOM_EXTENSION_USR_DATA,
- CX231xx_CUSTOM_PRIVATE_PACKET,
-};
-enum cx231xx_mute {
- CX231xx_UNMUTE,
- CX231xx_MUTE,
-};
-enum cx231xx_mute_video_mask {
- CX231xx_MUTE_VIDEO_V_MASK = 0x0000FF00,
- CX231xx_MUTE_VIDEO_U_MASK = 0x00FF0000,
- CX231xx_MUTE_VIDEO_Y_MASK = 0xFF000000,
-};
-enum cx231xx_mute_video_shift {
- CX231xx_MUTE_VIDEO_V_SHIFT = 8,
- CX231xx_MUTE_VIDEO_U_SHIFT = 16,
- CX231xx_MUTE_VIDEO_Y_SHIFT = 24,
-};
-
-/* defines below are from ivtv-driver.h */
-#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
-
-/* Firmware API commands */
-#define IVTV_API_STD_TIMEOUT 500
-
-/* Registers */
-/* IVTV_REG_OFFSET */
-#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
-#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
-#define IVTV_REG_SPU (0x9050)
-#define IVTV_REG_HW_BLOCKS (0x9054)
-#define IVTV_REG_VPU (0x9058)
-#define IVTV_REG_APU (0xA064)
-
-/*
- * Bit definitions for MC417_RWD and MC417_OEN registers
- *
- * bits 31-16
- *+-----------+
- *| Reserved |
- *|+-----------+
- *| bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
- *|+-------+-------+-------+-------+-------+-------+-------+-------+
- *|| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
- *|+-------+-------+-------+-------+-------+-------+-------+-------+
- *| bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
- *|+-------+-------+-------+-------+-------+-------+-------+-------+
- *||MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
- *|+-------+-------+-------+-------+-------+-------+-------+-------+
- */
-#define MC417_MIWR 0x8000
-#define MC417_MIRD 0x4000
-#define MC417_MICS 0x2000
-#define MC417_MIRDY 0x1000
-#define MC417_MIADDR 0x0F00
-#define MC417_MIDATA 0x00FF
-
-
-/* Bit definitions for MC417_CTL register ****
- *bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
- *+--------+-------------+--------+--------------+------------+
- *|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
- *+--------+-------------+--------+--------------+------------+
- */
-#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
-#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
-#define MC417_UART_GPIO_EN 0x00000001
-
-/* Values for speed control */
-#define MC417_SPD_CTL_SLOW 0x1
-#define MC417_SPD_CTL_MEDIUM 0x0
-#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
-
-/* Values for GPIO select */
-#define MC417_GPIO_SEL_GPIO3 0x3
-#define MC417_GPIO_SEL_GPIO2 0x2
-#define MC417_GPIO_SEL_GPIO1 0x1
-#define MC417_GPIO_SEL_GPIO0 0x0
-
-
-#define CX23417_GPIO_MASK 0xFC0003FF
-static int setITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 value)
-{
- int status = 0;
- u32 _gpio_direction = 0;
-
- _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
- _gpio_direction = _gpio_direction|gpio_direction;
- status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
- (u8 *)&value, 4, 0, 0);
- return status;
-}
-static int getITVCReg(struct cx231xx *dev, u32 gpio_direction, u32 *pValue)
-{
- int status = 0;
- u32 _gpio_direction = 0;
-
- _gpio_direction = _gpio_direction & CX23417_GPIO_MASK;
- _gpio_direction = _gpio_direction|gpio_direction;
-
- status = cx231xx_send_gpio_cmd(dev, _gpio_direction,
- (u8 *)pValue, 4, 0, 1);
- return status;
-}
-
-static int waitForMciComplete(struct cx231xx *dev)
-{
- u32 gpio;
- u32 gpio_driection = 0;
- u8 count = 0;
- getITVCReg(dev, gpio_driection, &gpio);
-
- while (!(gpio&0x020000)) {
- msleep(10);
-
- getITVCReg(dev, gpio_driection, &gpio);
-
- if (count++ > 100) {
- dprintk(3, "ERROR: Timeout - gpio=%x\n", gpio);
- return -1;
- }
- }
- return 0;
-}
-
-static int mc417_register_write(struct cx231xx *dev, u16 address, u32 value)
-{
- u32 temp;
- int status = 0;
-
- temp = 0x82|MCI_REGISTER_DATA_BYTE0|((value&0x000000FF)<<8);
- temp = temp<<10;
- status = setITVCReg(dev, ITVC_WRITE_DIR, temp);
- if (status < 0)
- return status;
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 1;*/
- temp = 0x82|MCI_REGISTER_DATA_BYTE1|(value&0x0000FF00);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 2;*/
- temp = 0x82|MCI_REGISTER_DATA_BYTE2|((value&0x00FF0000)>>8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 3;*/
- temp = 0x82|MCI_REGISTER_DATA_BYTE3|((value&0xFF000000)>>16);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write address byte 0;*/
- temp = 0x82|MCI_REGISTER_ADDRESS_BYTE0|((address&0x000000FF)<<8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write address byte 1;*/
- temp = 0x82|MCI_REGISTER_ADDRESS_BYTE1|(address&0x0000FF00);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*Write that the mode is write.*/
- temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_WRITE;
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- return waitForMciComplete(dev);
-}
-
-static int mc417_register_read(struct cx231xx *dev, u16 address, u32 *value)
-{
- /*write address byte 0;*/
- u32 temp;
- u32 return_value = 0;
- int ret = 0;
-
- temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE0 | ((address & 0x00FF) << 8);
- temp = temp << 10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp | ((0x05) << 10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write address byte 1;*/
- temp = 0x82 | MCI_REGISTER_ADDRESS_BYTE1 | (address & 0xFF00);
- temp = temp << 10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp | ((0x05) << 10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write that the mode is read;*/
- temp = 0x82 | MCI_REGISTER_MODE | MCI_MODE_REGISTER_READ;
- temp = temp << 10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp | ((0x05) << 10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*wait for the MIRDY line to be asserted ,
- signalling that the read is done;*/
- ret = waitForMciComplete(dev);
-
- /*switch the DATA- GPIO to input mode;*/
-
- /*Read data byte 0;*/
- temp = (0x82 | MCI_REGISTER_DATA_BYTE0) << 10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81 | MCI_REGISTER_DATA_BYTE0) << 10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp & 0x03FC0000) >> 18);
- setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
-
- /* Read data byte 1;*/
- temp = (0x82 | MCI_REGISTER_DATA_BYTE1) << 10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81 | MCI_REGISTER_DATA_BYTE1) << 10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
-
- return_value |= ((temp & 0x03FC0000) >> 10);
- setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
-
- /*Read data byte 2;*/
- temp = (0x82 | MCI_REGISTER_DATA_BYTE2) << 10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81 | MCI_REGISTER_DATA_BYTE2) << 10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp & 0x03FC0000) >> 2);
- setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
-
- /*Read data byte 3;*/
- temp = (0x82 | MCI_REGISTER_DATA_BYTE3) << 10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81 | MCI_REGISTER_DATA_BYTE3) << 10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp & 0x03FC0000) << 6);
- setITVCReg(dev, ITVC_READ_DIR, (0x87 << 10));
-
- *value = return_value;
-
-
- return ret;
-}
-
-static int mc417_memory_write(struct cx231xx *dev, u32 address, u32 value)
-{
- /*write data byte 0;*/
-
- u32 temp;
- int ret = 0;
-
- temp = 0x82 | MCI_MEMORY_DATA_BYTE0|((value & 0x000000FF) << 8);
- temp = temp << 10;
- ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
- if (ret < 0)
- return ret;
- temp = temp | ((0x05) << 10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 1;*/
- temp = 0x82 | MCI_MEMORY_DATA_BYTE1 | (value & 0x0000FF00);
- temp = temp << 10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp | ((0x05) << 10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 2;*/
- temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write data byte 3;*/
- temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /* write address byte 2;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
- ((address & 0x003F0000)>>8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /* write address byte 1;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /* write address byte 0;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*wait for MIRDY line;*/
- waitForMciComplete(dev);
-
- return 0;
-}
-
-static int mc417_memory_read(struct cx231xx *dev, u32 address, u32 *value)
-{
- u32 temp = 0;
- u32 return_value = 0;
- int ret = 0;
-
- /*write address byte 2;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_READ |
- ((address & 0x003F0000)>>8);
- temp = temp<<10;
- ret = setITVCReg(dev, ITVC_WRITE_DIR, temp);
- if (ret < 0)
- return ret;
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write address byte 1*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*write address byte 0*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0 | ((address & 0x00FF)<<8);
- temp = temp<<10;
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
- temp = temp|((0x05)<<10);
- setITVCReg(dev, ITVC_WRITE_DIR, temp);
-
- /*Wait for MIRDY line*/
- ret = waitForMciComplete(dev);
-
-
- /*Read data byte 3;*/
- temp = (0x82|MCI_MEMORY_DATA_BYTE3)<<10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81|MCI_MEMORY_DATA_BYTE3)<<10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp&0x03FC0000)<<6);
- setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
-
- /*Read data byte 2;*/
- temp = (0x82|MCI_MEMORY_DATA_BYTE2)<<10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81|MCI_MEMORY_DATA_BYTE2)<<10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp&0x03FC0000)>>2);
- setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
-
- /* Read data byte 1;*/
- temp = (0x82|MCI_MEMORY_DATA_BYTE1)<<10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81|MCI_MEMORY_DATA_BYTE1)<<10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp&0x03FC0000)>>10);
- setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
-
- /*Read data byte 0;*/
- temp = (0x82|MCI_MEMORY_DATA_BYTE0)<<10;
- setITVCReg(dev, ITVC_READ_DIR, temp);
- temp = ((0x81|MCI_MEMORY_DATA_BYTE0)<<10);
- setITVCReg(dev, ITVC_READ_DIR, temp);
- getITVCReg(dev, ITVC_READ_DIR, &temp);
- return_value |= ((temp&0x03FC0000)>>18);
- setITVCReg(dev, ITVC_READ_DIR, (0x87<<10));
-
- *value = return_value;
- return ret;
-}
-
-/* ------------------------------------------------------------------ */
-
-/* MPEG encoder API */
-static char *cmd_to_str(int cmd)
-{
- switch (cmd) {
- case CX2341X_ENC_PING_FW:
- return "PING_FW";
- case CX2341X_ENC_START_CAPTURE:
- return "START_CAPTURE";
- case CX2341X_ENC_STOP_CAPTURE:
- return "STOP_CAPTURE";
- case CX2341X_ENC_SET_AUDIO_ID:
- return "SET_AUDIO_ID";
- case CX2341X_ENC_SET_VIDEO_ID:
- return "SET_VIDEO_ID";
- case CX2341X_ENC_SET_PCR_ID:
- return "SET_PCR_PID";
- case CX2341X_ENC_SET_FRAME_RATE:
- return "SET_FRAME_RATE";
- case CX2341X_ENC_SET_FRAME_SIZE:
- return "SET_FRAME_SIZE";
- case CX2341X_ENC_SET_BIT_RATE:
- return "SET_BIT_RATE";
- case CX2341X_ENC_SET_GOP_PROPERTIES:
- return "SET_GOP_PROPERTIES";
- case CX2341X_ENC_SET_ASPECT_RATIO:
- return "SET_ASPECT_RATIO";
- case CX2341X_ENC_SET_DNR_FILTER_MODE:
- return "SET_DNR_FILTER_PROPS";
- case CX2341X_ENC_SET_DNR_FILTER_PROPS:
- return "SET_DNR_FILTER_PROPS";
- case CX2341X_ENC_SET_CORING_LEVELS:
- return "SET_CORING_LEVELS";
- case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
- return "SET_SPATIAL_FILTER_TYPE";
- case CX2341X_ENC_SET_VBI_LINE:
- return "SET_VBI_LINE";
- case CX2341X_ENC_SET_STREAM_TYPE:
- return "SET_STREAM_TYPE";
- case CX2341X_ENC_SET_OUTPUT_PORT:
- return "SET_OUTPUT_PORT";
- case CX2341X_ENC_SET_AUDIO_PROPERTIES:
- return "SET_AUDIO_PROPERTIES";
- case CX2341X_ENC_HALT_FW:
- return "HALT_FW";
- case CX2341X_ENC_GET_VERSION:
- return "GET_VERSION";
- case CX2341X_ENC_SET_GOP_CLOSURE:
- return "SET_GOP_CLOSURE";
- case CX2341X_ENC_GET_SEQ_END:
- return "GET_SEQ_END";
- case CX2341X_ENC_SET_PGM_INDEX_INFO:
- return "SET_PGM_INDEX_INFO";
- case CX2341X_ENC_SET_VBI_CONFIG:
- return "SET_VBI_CONFIG";
- case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
- return "SET_DMA_BLOCK_SIZE";
- case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
- return "GET_PREV_DMA_INFO_MB_10";
- case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
- return "GET_PREV_DMA_INFO_MB_9";
- case CX2341X_ENC_SCHED_DMA_TO_HOST:
- return "SCHED_DMA_TO_HOST";
- case CX2341X_ENC_INITIALIZE_INPUT:
- return "INITIALIZE_INPUT";
- case CX2341X_ENC_SET_FRAME_DROP_RATE:
- return "SET_FRAME_DROP_RATE";
- case CX2341X_ENC_PAUSE_ENCODER:
- return "PAUSE_ENCODER";
- case CX2341X_ENC_REFRESH_INPUT:
- return "REFRESH_INPUT";
- case CX2341X_ENC_SET_COPYRIGHT:
- return "SET_COPYRIGHT";
- case CX2341X_ENC_SET_EVENT_NOTIFICATION:
- return "SET_EVENT_NOTIFICATION";
- case CX2341X_ENC_SET_NUM_VSYNC_LINES:
- return "SET_NUM_VSYNC_LINES";
- case CX2341X_ENC_SET_PLACEHOLDER:
- return "SET_PLACEHOLDER";
- case CX2341X_ENC_MUTE_VIDEO:
- return "MUTE_VIDEO";
- case CX2341X_ENC_MUTE_AUDIO:
- return "MUTE_AUDIO";
- case CX2341X_ENC_MISC:
- return "MISC";
- default:
- return "UNKNOWN";
- }
-}
-
-static int cx231xx_mbox_func(void *priv,
- u32 command,
- int in,
- int out,
- u32 data[CX2341X_MBOX_MAX_DATA])
-{
- struct cx231xx *dev = priv;
- unsigned long timeout;
- u32 value, flag, retval = 0;
- int i;
-
- dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
- cmd_to_str(command));
-
- /* this may not be 100% safe if we can't read any memory location
- without side effects */
- mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
- if (value != 0x12345678) {
- dprintk(3,
- "Firmware and/or mailbox pointer not initialized "
- "or corrupted, signature = 0x%x, cmd = %s\n", value,
- cmd_to_str(command));
- return -1;
- }
-
- /* This read looks at 32 bits, but flag is only 8 bits.
- * Seems we also bail if CMD or TIMEOUT bytes are set???
- */
- mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
- if (flag) {
- dprintk(3, "ERROR: Mailbox appears to be in use "
- "(%x), cmd = %s\n", flag, cmd_to_str(command));
- return -1;
- }
-
- flag |= 1; /* tell 'em we're working on it */
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- /* write command + args + fill remaining with zeros */
- /* command code */
- mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
- mc417_memory_write(dev, dev->cx23417_mailbox + 3,
- IVTV_API_STD_TIMEOUT); /* timeout */
- for (i = 0; i < in; i++) {
- mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
- dprintk(3, "API Input %d = %d\n", i, data[i]);
- }
- for (; i < CX2341X_MBOX_MAX_DATA; i++)
- mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
-
- flag |= 3; /* tell 'em we're done writing */
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- /* wait for firmware to handle the API command */
- timeout = jiffies + msecs_to_jiffies(10);
- for (;;) {
- mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
- if (0 != (flag & 4))
- break;
- if (time_after(jiffies, timeout)) {
- dprintk(3, "ERROR: API Mailbox timeout\n");
- return -1;
- }
- udelay(10);
- }
-
- /* read output values */
- for (i = 0; i < out; i++) {
- mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
- dprintk(3, "API Output %d = %d\n", i, data[i]);
- }
-
- mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
- dprintk(3, "API result = %d\n", retval);
-
- flag = 0;
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- return retval;
-}
-
-/* We don't need to call the API often, so using just one
- * mailbox will probably suffice
- */
-static int cx231xx_api_cmd(struct cx231xx *dev,
- u32 command,
- u32 inputcnt,
- u32 outputcnt,
- ...)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- va_list vargs;
- int i, err;
-
- dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
-
- va_start(vargs, outputcnt);
- for (i = 0; i < inputcnt; i++)
- data[i] = va_arg(vargs, int);
-
- err = cx231xx_mbox_func(dev, command, inputcnt, outputcnt, data);
- for (i = 0; i < outputcnt; i++) {
- int *vptr = va_arg(vargs, int *);
- *vptr = data[i];
- }
- va_end(vargs);
-
- return err;
-}
-
-static int cx231xx_find_mailbox(struct cx231xx *dev)
-{
- u32 signature[4] = {
- 0x12345678, 0x34567812, 0x56781234, 0x78123456
- };
- int signaturecnt = 0;
- u32 value;
- int i;
- int ret = 0;
-
- dprintk(2, "%s()\n", __func__);
-
- for (i = 0; i < 0x100; i++) {/*CX231xx_FIRM_IMAGE_SIZE*/
- ret = mc417_memory_read(dev, i, &value);
- if (ret < 0)
- return ret;
- if (value == signature[signaturecnt])
- signaturecnt++;
- else
- signaturecnt = 0;
- if (4 == signaturecnt) {
- dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
- return i+1;
- }
- }
- dprintk(3, "Mailbox signature values not found!\n");
- return -1;
-}
-
-static void mciWriteMemoryToGPIO(struct cx231xx *dev, u32 address, u32 value,
- u32 *p_fw_image)
-{
-
- u32 temp = 0;
- int i = 0;
-
- temp = 0x82|MCI_MEMORY_DATA_BYTE0|((value&0x000000FF)<<8);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /*write data byte 1;*/
- temp = 0x82|MCI_MEMORY_DATA_BYTE1|(value&0x0000FF00);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /*write data byte 2;*/
- temp = 0x82|MCI_MEMORY_DATA_BYTE2|((value&0x00FF0000)>>8);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /*write data byte 3;*/
- temp = 0x82|MCI_MEMORY_DATA_BYTE3|((value&0xFF000000)>>16);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /* write address byte 2;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE2 | MCI_MODE_MEMORY_WRITE |
- ((address & 0x003F0000)>>8);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /* write address byte 1;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE1 | (address & 0xFF00);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- /* write address byte 0;*/
- temp = 0x82|MCI_MEMORY_ADDRESS_BYTE0|((address & 0x00FF)<<8);
- temp = temp<<10;
- *p_fw_image = temp;
- p_fw_image++;
- temp = temp|((0x05)<<10);
- *p_fw_image = temp;
- p_fw_image++;
-
- for (i = 0; i < 6; i++) {
- *p_fw_image = 0xFFFFFFFF;
- p_fw_image++;
- }
-}
-
-
-static int cx231xx_load_firmware(struct cx231xx *dev)
-{
- static const unsigned char magic[8] = {
- 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
- };
- const struct firmware *firmware;
- int i, retval = 0;
- u32 value = 0;
- u32 gpio_output = 0;
- /*u32 checksum = 0;*/
- /*u32 *dataptr;*/
- u32 transfer_size = 0;
- u32 fw_data = 0;
- u32 address = 0;
- /*u32 current_fw[800];*/
- u32 *p_current_fw, *p_fw;
- u32 *p_fw_data;
- int frame = 0;
- u16 _buffer_size = 4096;
- u8 *p_buffer;
-
- p_current_fw = vmalloc(1884180 * 4);
- p_fw = p_current_fw;
- if (p_current_fw == NULL) {
- dprintk(2, "FAIL!!!\n");
- return -1;
- }
-
- p_buffer = vmalloc(4096);
- if (p_buffer == NULL) {
- dprintk(2, "FAIL!!!\n");
- return -1;
- }
-
- dprintk(2, "%s()\n", __func__);
-
- /* Save GPIO settings before reset of APU */
- retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
- retval |= mc417_memory_read(dev, 0x900C, &value);
-
- retval = mc417_register_write(dev,
- IVTV_REG_VPU, 0xFFFFFFED);
- retval |= mc417_register_write(dev,
- IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
- retval |= mc417_register_write(dev,
- IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
- retval |= mc417_register_write(dev,
- IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
- retval |= mc417_register_write(dev,
- IVTV_REG_APU, 0);
-
- if (retval != 0) {
- printk(KERN_ERR "%s: Error with mc417_register_write\n",
- __func__);
- return -1;
- }
-
- retval = request_firmware(&firmware, CX231xx_FIRM_IMAGE_NAME,
- &dev->udev->dev);
-
- if (retval != 0) {
- printk(KERN_ERR
- "ERROR: Hotplug firmware request failed (%s).\n",
- CX231xx_FIRM_IMAGE_NAME);
- printk(KERN_ERR "Please fix your hotplug setup, the board will "
- "not work without firmware loaded!\n");
- return -1;
- }
-
- if (firmware->size != CX231xx_FIRM_IMAGE_SIZE) {
- printk(KERN_ERR "ERROR: Firmware size mismatch "
- "(have %zd, expected %d)\n",
- firmware->size, CX231xx_FIRM_IMAGE_SIZE);
- release_firmware(firmware);
- return -1;
- }
-
- if (0 != memcmp(firmware->data, magic, 8)) {
- printk(KERN_ERR
- "ERROR: Firmware magic mismatch, wrong file?\n");
- release_firmware(firmware);
- return -1;
- }
-
- initGPIO(dev);
-
- /* transfer to the chip */
- dprintk(2, "Loading firmware to GPIO...\n");
- p_fw_data = (u32 *)firmware->data;
- dprintk(2, "firmware->size=%zd\n", firmware->size);
- for (transfer_size = 0; transfer_size < firmware->size;
- transfer_size += 4) {
- fw_data = *p_fw_data;
-
- mciWriteMemoryToGPIO(dev, address, fw_data, p_current_fw);
- address = address + 1;
- p_current_fw += 20;
- p_fw_data += 1;
- }
-
- /*download the firmware by ep5-out*/
-
- for (frame = 0; frame < (int)(CX231xx_FIRM_IMAGE_SIZE*20/_buffer_size);
- frame++) {
- for (i = 0; i < _buffer_size; i++) {
- *(p_buffer + i) = (u8)(*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x000000FF);
- i++;
- *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x0000FF00) >> 8);
- i++;
- *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0x00FF0000) >> 16);
- i++;
- *(p_buffer + i) = (u8)((*(p_fw + (frame * 128 * 8 + (i / 4))) & 0xFF000000) >> 24);
- }
- cx231xx_ep5_bulkout(dev, p_buffer, _buffer_size);
- }
-
- p_current_fw = p_fw;
- vfree(p_current_fw);
- p_current_fw = NULL;
- uninitGPIO(dev);
- release_firmware(firmware);
- dprintk(1, "Firmware upload successful.\n");
-
- retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
- IVTV_CMD_HW_BLOCKS_RST);
- if (retval < 0) {
- printk(KERN_ERR "%s: Error with mc417_register_write\n",
- __func__);
- return retval;
- }
- /* F/W power up disturbs the GPIOs, restore state */
- retval |= mc417_register_write(dev, 0x9020, gpio_output);
- retval |= mc417_register_write(dev, 0x900C, value);
-
- retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
- retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
-
- if (retval < 0) {
- printk(KERN_ERR "%s: Error with mc417_register_write\n",
- __func__);
- return retval;
- }
- return 0;
-}
-
-static void cx231xx_417_check_encoder(struct cx231xx *dev)
-{
- u32 status, seq;
-
- status = 0;
- seq = 0;
- cx231xx_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
- dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
-}
-
-static void cx231xx_codec_settings(struct cx231xx *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- /* assign frame size */
- cx231xx_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
- dev->ts1.height, dev->ts1.width);
-
- dev->mpeg_params.width = dev->ts1.width;
- dev->mpeg_params.height = dev->ts1.height;
-
- cx2341x_update(dev, cx231xx_mbox_func, NULL, &dev->mpeg_params);
-
- cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
- cx231xx_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
-}
-
-static int cx231xx_initialize_codec(struct cx231xx *dev)
-{
- int version;
- int retval;
- u32 i;
- u32 val = 0;
-
- dprintk(1, "%s()\n", __func__);
- cx231xx_disable656(dev);
- retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
- if (retval < 0) {
- dprintk(2, "%s() PING OK\n", __func__);
- retval = cx231xx_load_firmware(dev);
- if (retval < 0) {
- printk(KERN_ERR "%s() f/w load failed\n", __func__);
- return retval;
- }
- retval = cx231xx_find_mailbox(dev);
- if (retval < 0) {
- printk(KERN_ERR "%s() mailbox < 0, error\n",
- __func__);
- return -1;
- }
- dev->cx23417_mailbox = retval;
- retval = cx231xx_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
- if (retval < 0) {
- printk(KERN_ERR
- "ERROR: cx23417 firmware ping failed!\n");
- return -1;
- }
- retval = cx231xx_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
- &version);
- if (retval < 0) {
- printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
- "version failed!\n");
- return -1;
- }
- dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
- msleep(200);
- }
-
- for (i = 0; i < 1; i++) {
- retval = mc417_register_read(dev, 0x20f8, &val);
- dprintk(3, "***before enable656() VIM Capture Lines =%d ***\n",
- val);
- if (retval < 0)
- return retval;
- }
-
- cx231xx_enable656(dev);
- /* stop mpeg capture */
- cx231xx_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE,
- 3, 0, 1, 3, 4);
-
- cx231xx_codec_settings(dev);
- msleep(60);
-
-/* cx231xx_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
- CX231xx_FIELD1_SAA7115, CX231xx_FIELD2_SAA7115);
- cx231xx_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
- CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0);
-*/
-
-#if 0
- /* TODO */
- u32 data[7];
-
- /* Setup to capture VBI */
- data[0] = 0x0001BD00;
- data[1] = 1; /* frames per interrupt */
- data[2] = 4; /* total bufs */
- data[3] = 0x91559155; /* start codes */
- data[4] = 0x206080C0; /* stop codes */
- data[5] = 6; /* lines */
- data[6] = 64; /* BPL */
-
- cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
- data[2], data[3], data[4], data[5], data[6]);
-
- for (i = 2; i <= 24; i++) {
- int valid;
-
- valid = ((i >= 19) && (i <= 21));
- cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
- valid, 0 , 0, 0);
- cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
- i | 0x80000000, valid, 0, 0, 0);
- }
-#endif
-/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE);
- msleep(60);
-*/
- /* initialize the video input */
- retval = cx231xx_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
- if (retval < 0)
- return retval;
- msleep(60);
-
- /* Enable VIP style pixel invalidation so we work with scaled mode */
- mc417_memory_write(dev, 2120, 0x00000080);
-
- /* start capturing to the host interface */
- retval = cx231xx_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
- CX231xx_MPEG_CAPTURE, CX231xx_RAW_BITS_NONE);
- if (retval < 0)
- return retval;
- msleep(10);
-
- for (i = 0; i < 1; i++) {
- mc417_register_read(dev, 0x20f8, &val);
- dprintk(3, "***VIM Capture Lines =%d ***\n", val);
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int bb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx231xx_fh *fh = q->priv_data;
-
- fh->dev->ts1.ts_packet_size = mpeglinesize;
- fh->dev->ts1.ts_packet_count = mpeglines;
-
- *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
- *count = mpegbufs;
-
- return 0;
-}
-static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
-{
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx *dev = fh->dev;
- unsigned long flags = 0;
-
- if (in_interrupt())
- BUG();
-
- spin_lock_irqsave(&dev->video_mode.slock, flags);
- if (dev->USE_ISO) {
- if (dev->video_mode.isoc_ctl.buf == buf)
- dev->video_mode.isoc_ctl.buf = NULL;
- } else {
- if (dev->video_mode.bulk_ctl.buf == buf)
- dev->video_mode.bulk_ctl.buf = NULL;
- }
- spin_unlock_irqrestore(&dev->video_mode.slock, flags);
- videobuf_waiton(vq, &buf->vb, 0, 0);
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static void buffer_copy(struct cx231xx *dev, char *data, int len, struct urb *urb,
- struct cx231xx_dmaqueue *dma_q)
-{
- void *vbuf;
- struct cx231xx_buffer *buf;
- u32 tail_data = 0;
- char *p_data;
-
- if (dma_q->mpeg_buffer_done == 0) {
- if (list_empty(&dma_q->active))
- return;
-
- buf = list_entry(dma_q->active.next,
- struct cx231xx_buffer, vb.queue);
- dev->video_mode.isoc_ctl.buf = buf;
- dma_q->mpeg_buffer_done = 1;
- }
- /* Fill buffer */
- buf = dev->video_mode.isoc_ctl.buf;
- vbuf = videobuf_to_vmalloc(&buf->vb);
-
- if ((dma_q->mpeg_buffer_completed+len) <
- mpeglines*mpeglinesize) {
- if (dma_q->add_ps_package_head ==
- CX231XX_NEED_ADD_PS_PACKAGE_HEAD) {
- memcpy(vbuf+dma_q->mpeg_buffer_completed,
- dma_q->ps_head, 3);
- dma_q->mpeg_buffer_completed =
- dma_q->mpeg_buffer_completed + 3;
- dma_q->add_ps_package_head =
- CX231XX_NONEED_PS_PACKAGE_HEAD;
- }
- memcpy(vbuf+dma_q->mpeg_buffer_completed, data, len);
- dma_q->mpeg_buffer_completed =
- dma_q->mpeg_buffer_completed + len;
- } else {
- dma_q->mpeg_buffer_done = 0;
-
- tail_data =
- mpeglines*mpeglinesize - dma_q->mpeg_buffer_completed;
- memcpy(vbuf+dma_q->mpeg_buffer_completed,
- data, tail_data);
-
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- dma_q->mpeg_buffer_completed = 0;
-
- if (len - tail_data > 0) {
- p_data = data + tail_data;
- dma_q->left_data_count = len - tail_data;
- memcpy(dma_q->p_left_data,
- p_data, len - tail_data);
- }
-
- }
-
- return;
-}
-
-static void buffer_filled(char *data, int len, struct urb *urb,
- struct cx231xx_dmaqueue *dma_q)
-{
- void *vbuf;
- struct cx231xx_buffer *buf;
-
- if (list_empty(&dma_q->active))
- return;
-
-
- buf = list_entry(dma_q->active.next,
- struct cx231xx_buffer, vb.queue);
-
-
- /* Fill buffer */
- vbuf = videobuf_to_vmalloc(&buf->vb);
- memcpy(vbuf, data, len);
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-
- return;
-}
-static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
-{
- struct cx231xx_dmaqueue *dma_q = urb->context;
- unsigned char *p_buffer;
- u32 buffer_size = 0;
- u32 i = 0;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- if (dma_q->left_data_count > 0) {
- buffer_copy(dev, dma_q->p_left_data,
- dma_q->left_data_count, urb, dma_q);
- dma_q->mpeg_buffer_completed = dma_q->left_data_count;
- dma_q->left_data_count = 0;
- }
-
- p_buffer = urb->transfer_buffer +
- urb->iso_frame_desc[i].offset;
- buffer_size = urb->iso_frame_desc[i].actual_length;
-
- if (buffer_size > 0)
- buffer_copy(dev, p_buffer, buffer_size, urb, dma_q);
- }
-
- return 0;
-}
-static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
-{
-
- /*char *outp;*/
- /*struct cx231xx_buffer *buf;*/
- struct cx231xx_dmaqueue *dma_q = urb->context;
- unsigned char *p_buffer, *buffer;
- u32 buffer_size = 0;
-
- p_buffer = urb->transfer_buffer;
- buffer_size = urb->actual_length;
-
- buffer = kmalloc(buffer_size, GFP_ATOMIC);
-
- memcpy(buffer, dma_q->ps_head, 3);
- memcpy(buffer+3, p_buffer, buffer_size-3);
- memcpy(dma_q->ps_head, p_buffer+buffer_size-3, 3);
-
- p_buffer = buffer;
- buffer_filled(p_buffer, buffer_size, urb, dma_q);
-
- kfree(buffer);
- return 0;
-}
-
-static int bb_buf_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct cx231xx_fh *fh = q->priv_data;
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- struct cx231xx *dev = fh->dev;
- int rc = 0, urb_init = 0;
- int size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
-
- dma_qq = &dev->video_mode.vidq;
-
- if (0 != buf->vb.baddr && buf->vb.bsize < size)
- return -EINVAL;
- buf->vb.width = fh->dev->ts1.ts_packet_size;
- buf->vb.height = fh->dev->ts1.ts_packet_count;
- buf->vb.size = size;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(q, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
-
- if (dev->USE_ISO) {
- if (!dev->video_mode.isoc_ctl.num_bufs)
- urb_init = 1;
- } else {
- if (!dev->video_mode.bulk_ctl.num_bufs)
- urb_init = 1;
- }
- /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
- urb_init, dev->video_mode.max_pkt_size);*/
- dev->mode_tv = 1;
-
- if (urb_init) {
- rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
- rc = cx231xx_unmute_audio(dev);
- if (dev->USE_ISO) {
- cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
- rc = cx231xx_init_isoc(dev, mpeglines,
- mpegbufs,
- dev->ts1_mode.max_pkt_size,
- cx231xx_isoc_copy);
- } else {
- cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
- rc = cx231xx_init_bulk(dev, mpeglines,
- mpegbufs,
- dev->ts1_mode.max_pkt_size,
- cx231xx_bulk_copy);
- }
- if (rc < 0)
- goto fail;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(q, buf);
- return rc;
-}
-
-static void bb_buf_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx231xx_fh *fh = q->priv_data;
-
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- struct cx231xx *dev = fh->dev;
- struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-
-}
-
-static void bb_buf_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- /*struct cx231xx_fh *fh = q->priv_data;*/
- /*struct cx231xx *dev = (struct cx231xx *)fh->dev;*/
-
- free_buffer(q, buf);
-}
-
-static struct videobuf_queue_ops cx231xx_qops = {
- .buf_setup = bb_buf_setup,
- .buf_prepare = bb_buf_prepare,
- .buf_queue = bb_buf_queue,
- .buf_release = bb_buf_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-static const u32 *ctrl_classes[] = {
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-static int cx231xx_queryctrl(struct cx231xx *dev,
- struct v4l2_queryctrl *qctrl)
-{
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- /* MPEG V4L2 controls */
- if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-
- return 0;
-}
-
-static int cx231xx_querymenu(struct cx231xx *dev,
- struct v4l2_querymenu *qmenu)
-{
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- cx231xx_queryctrl(dev, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
-}
-
-static int vidioc_g_std(struct file *file, void *fh0, v4l2_std_id *norm)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
-
- *norm = dev->encodernorm.id;
- return 0;
-}
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(cx231xx_tvnorms); i++)
- if (*id & cx231xx_tvnorms[i].id)
- break;
- if (i == ARRAY_SIZE(cx231xx_tvnorms))
- return -EINVAL;
- dev->encodernorm = cx231xx_tvnorms[i];
-
- if (dev->encodernorm.id & 0xb000) {
- dprintk(3, "encodernorm set to NTSC\n");
- dev->norm = V4L2_STD_NTSC;
- dev->ts1.height = 480;
- dev->mpeg_params.is_50hz = 0;
- } else {
- dprintk(3, "encodernorm set to PAL\n");
- dev->norm = V4L2_STD_PAL_B;
- dev->ts1.height = 576;
- dev->mpeg_params.is_50hz = 1;
- }
- call_all(dev, core, s_std, dev->norm);
- /* do mode control overrides */
- cx231xx_do_mode_ctrl_overrides(dev);
-
- dprintk(3, "exit vidioc_s_std() i=0x%x\n", i);
- return 0;
-}
-static int vidioc_g_audio(struct file *file, void *fh,
- struct v4l2_audio *a)
-{
- struct v4l2_audio *vin = a;
-
- int ret = -EINVAL;
- if (vin->index > 0)
- return ret;
- strncpy(vin->name, "VideoGrabber Audio", 14);
- vin->capability = V4L2_AUDCAP_STEREO;
-return 0;
-}
-static int vidioc_enumaudio(struct file *file, void *fh,
- struct v4l2_audio *a)
-{
- struct v4l2_audio *vin = a;
-
- int ret = -EINVAL;
-
- if (vin->index > 0)
- return ret;
- strncpy(vin->name, "VideoGrabber Audio", 14);
- vin->capability = V4L2_AUDCAP_STEREO;
-
-
-return 0;
-}
-static const char *iname[] = {
- [CX231XX_VMUX_COMPOSITE1] = "Composite1",
- [CX231XX_VMUX_SVIDEO] = "S-Video",
- [CX231XX_VMUX_TELEVISION] = "Television",
- [CX231XX_VMUX_CABLE] = "Cable TV",
- [CX231XX_VMUX_DVB] = "DVB",
- [CX231XX_VMUX_DEBUG] = "for debug only",
-};
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
- struct cx231xx_input *input;
- int n;
- dprintk(3, "enter vidioc_enum_input()i->index=%d\n", i->index);
-
- if (i->index >= 4)
- return -EINVAL;
-
-
- input = &cx231xx_boards[dev->model].input[i->index];
-
- if (input->type == 0)
- return -EINVAL;
-
- /* FIXME
- * strcpy(i->name, input->name); */
-
- n = i->index;
- strcpy(i->name, iname[INPUT(n)->type]);
-
- if (input->type == CX231XX_VMUX_TELEVISION ||
- input->type == CX231XX_VMUX_CABLE)
- i->type = V4L2_INPUT_TYPE_TUNER;
- else
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
-
- dprintk(3, "enter vidioc_s_input() i=%d\n", i);
-
- mutex_lock(&dev->lock);
-
- video_mux(dev, i);
-
- mutex_unlock(&dev->lock);
-
- if (i >= 4)
- return -EINVAL;
- dev->input = i;
- dprintk(3, "exit vidioc_s_input()\n");
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
-
-
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_s_ctrl()\n");
- /* Update the A/V core */
- call_all(dev, core, s_ctrl, ctl);
- dprintk(3, "exit vidioc_s_ctrl()\n");
- return 0;
-}
-static struct v4l2_capability pvr_capability = {
- .driver = "cx231xx",
- .card = "VideoGrabber",
- .bus_info = "usb",
- .version = 1,
- .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
- V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
-};
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
-
-
-
- memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
-
- if (f->index != 0)
- return -EINVAL;
-
- strlcpy(f->description, "MPEG", sizeof(f->description));
- f->pixelformat = V4L2_PIX_FMT_MPEG;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_g_fmt_vid_cap()\n");
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage =
- dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
- f->fmt.pix.colorspace = 0;
- f->fmt.pix.width = dev->ts1.width;
- f->fmt.pix.height = dev->ts1.height;
- f->fmt.pix.field = fh->vidq.field;
- dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
- dev->ts1.width, dev->ts1.height, fh->vidq.field);
- dprintk(3, "exit vidioc_g_fmt_vid_cap()\n");
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_try_fmt_vid_cap()\n");
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage =
- dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
- f->fmt.pix.colorspace = 0;
- dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
- dev->ts1.width, dev->ts1.height, fh->vidq.field);
- dprintk(3, "exit vidioc_try_fmt_vid_cap()\n");
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
-
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct cx231xx_fh *fh = file->private_data;
-
- return videobuf_reqbufs(&fh->vidq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx231xx_fh *fh = file->private_data;
-
- return videobuf_querybuf(&fh->vidq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx231xx_fh *fh = file->private_data;
-
- return videobuf_qbuf(&fh->vidq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx231xx_fh *fh = priv;
-
- return videobuf_dqbuf(&fh->vidq, b, file->f_flags & O_NONBLOCK);
-}
-
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct cx231xx_fh *fh = file->private_data;
-
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_streamon()\n");
- cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
- cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
- if (dev->USE_ISO)
- cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
- CX231XX_NUM_BUFS,
- dev->video_mode.max_pkt_size,
- cx231xx_isoc_copy);
- else {
- cx231xx_init_bulk(dev, 320,
- 5,
- dev->ts1_mode.max_pkt_size,
- cx231xx_bulk_copy);
- }
- dprintk(3, "exit vidioc_streamon()\n");
- return videobuf_streamon(&fh->vidq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx231xx_fh *fh = file->private_data;
-
- return videobuf_streamoff(&fh->vidq);
-}
-
-static int vidioc_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_g_ext_ctrls()\n");
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- dprintk(3, "exit vidioc_g_ext_ctrls()\n");
- return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
-}
-
-static int vidioc_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- struct cx2341x_mpeg_params p;
- int err;
- dprintk(3, "enter vidioc_s_ext_ctrls()\n");
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- p = dev->mpeg_params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
- if (err == 0) {
- err = cx2341x_update(dev, cx231xx_mbox_func,
- &dev->mpeg_params, &p);
- dev->mpeg_params = p;
- }
-
- return err;
-
-
-return 0;
-}
-
-static int vidioc_try_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- struct cx2341x_mpeg_params p;
- int err;
- dprintk(3, "enter vidioc_try_ext_ctrls()\n");
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- p = dev->mpeg_params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
- dprintk(3, "exit vidioc_try_ext_ctrls() err=%d\n", err);
- return err;
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- char name[32 + 2];
-
- snprintf(name, sizeof(name), "%s/2", dev->name);
- dprintk(3,
- "%s/2: ============ START LOG STATUS ============\n",
- dev->name);
- call_all(dev, core, log_status);
- cx2341x_log_status(&dev->mpeg_params, name);
- dprintk(3,
- "%s/2: ============= END LOG STATUS =============\n",
- dev->name);
- return 0;
-}
-
-static int vidioc_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *a)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_querymenu()\n");
- dprintk(3, "exit vidioc_querymenu()\n");
- return cx231xx_querymenu(dev, a);
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- dprintk(3, "enter vidioc_queryctrl()\n");
- dprintk(3, "exit vidioc_queryctrl()\n");
- return cx231xx_queryctrl(dev, c);
-}
-
-static int mpeg_open(struct file *file)
-{
- int minor = video_devdata(file)->minor;
- struct cx231xx *h, *dev = NULL;
- /*struct list_head *list;*/
- struct cx231xx_fh *fh;
- /*u32 value = 0;*/
-
- dprintk(2, "%s()\n", __func__);
-
- list_for_each_entry(h, &cx231xx_devlist, devlist) {
- if (h->v4l_device->minor == minor)
- dev = h;
- }
-
- if (dev == NULL)
- return -ENODEV;
-
- mutex_lock(&dev->lock);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- mutex_unlock(&dev->lock);
- return -ENOMEM;
- }
-
- file->private_data = fh;
- fh->dev = dev;
-
-
- videobuf_queue_vmalloc_init(&fh->vidq, &cx231xx_qops,
- NULL, &dev->video_mode.slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
- sizeof(struct cx231xx_buffer), fh, NULL);
-/*
- videobuf_queue_sg_init(&fh->vidq, &cx231xx_qops,
- &dev->udev->dev, &dev->ts1.slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx231xx_buffer),
- fh, NULL);
-*/
-
-
- cx231xx_set_alt_setting(dev, INDEX_VANC, 1);
- cx231xx_set_gpio_value(dev, 2, 0);
-
- cx231xx_initialize_codec(dev);
-
- mutex_unlock(&dev->lock);
- cx231xx_start_TS1(dev);
-
- return 0;
-}
-
-static int mpeg_release(struct file *file)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
-
- dprintk(3, "mpeg_release()! dev=0x%p\n", dev);
-
- if (!dev) {
- dprintk(3, "abort!!!\n");
- return 0;
- }
-
- mutex_lock(&dev->lock);
-
- cx231xx_stop_TS1(dev);
-
- /* do this before setting alternate! */
- if (dev->USE_ISO)
- cx231xx_uninit_isoc(dev);
- else
- cx231xx_uninit_bulk(dev);
- cx231xx_set_mode(dev, CX231XX_SUSPEND);
-
- cx231xx_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- CX231xx_END_NOW, CX231xx_MPEG_CAPTURE,
- CX231xx_RAW_BITS_NONE);
-
- /* FIXME: Review this crap */
- /* Shut device down on last close */
- if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
- if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
- /* stop mpeg capture */
-
- msleep(500);
- cx231xx_417_check_encoder(dev);
-
- }
- }
-
- if (fh->vidq.streaming)
- videobuf_streamoff(&fh->vidq);
- if (fh->vidq.reading)
- videobuf_read_stop(&fh->vidq);
-
- videobuf_mmap_free(&fh->vidq);
- file->private_data = NULL;
- kfree(fh);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static ssize_t mpeg_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
-
-
- /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
- /* Start mpeg encoder on first read. */
- if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
- if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
- if (cx231xx_initialize_codec(dev) < 0)
- return -EINVAL;
- }
- }
-
- return videobuf_read_stream(&fh->vidq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int mpeg_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cx231xx_fh *fh = file->private_data;
- /*struct cx231xx *dev = fh->dev;*/
-
- /*dprintk(2, "%s\n", __func__);*/
-
- return videobuf_poll_stream(file, &fh->vidq, wait);
-}
-
-static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct cx231xx_fh *fh = file->private_data;
- struct cx231xx *dev = fh->dev;
-
- dprintk(2, "%s()\n", __func__);
-
- return videobuf_mmap_mapper(&fh->vidq, vma);
-}
-
-static struct v4l2_file_operations mpeg_fops = {
- .owner = THIS_MODULE,
- .open = mpeg_open,
- .release = mpeg_release,
- .read = mpeg_read,
- .poll = mpeg_poll,
- .mmap = mpeg_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_enumaudio = vidioc_enumaudio,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
- .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_querymenu = vidioc_querymenu,
- .vidioc_queryctrl = vidioc_queryctrl,
-/* .vidioc_g_chip_ident = cx231xx_g_chip_ident,*/
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/* .vidioc_g_register = cx231xx_g_register,*/
-/* .vidioc_s_register = cx231xx_s_register,*/
-#endif
-};
-
-static struct video_device cx231xx_mpeg_template = {
- .name = "cx231xx",
- .fops = &mpeg_fops,
- .ioctl_ops = &mpeg_ioctl_ops,
- .minor = -1,
- .tvnorms = CX231xx_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-void cx231xx_417_unregister(struct cx231xx *dev)
-{
- dprintk(1, "%s()\n", __func__);
- dprintk(3, "%s()\n", __func__);
-
- if (dev->v4l_device) {
- if (-1 != dev->v4l_device->minor)
- video_unregister_device(dev->v4l_device);
- else
- video_device_release(dev->v4l_device);
- dev->v4l_device = NULL;
- }
-}
-
-static struct video_device *cx231xx_video_dev_alloc(
- struct cx231xx *dev,
- struct usb_device *usbdev,
- struct video_device *template,
- char *type)
-{
- struct video_device *vfd;
-
- dprintk(1, "%s()\n", __func__);
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template;
- vfd->minor = -1;
- snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
- type, cx231xx_boards[dev->model].name);
-
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
-
- return vfd;
-
-}
-
-int cx231xx_417_register(struct cx231xx *dev)
-{
- /* FIXME: Port1 hardcoded here */
- int err = -ENODEV;
- struct cx231xx_tsport *tsport = &dev->ts1;
-
- dprintk(1, "%s()\n", __func__);
-
- /* Set default TV standard */
- dev->encodernorm = cx231xx_tvnorms[0];
-
- if (dev->encodernorm.id & V4L2_STD_525_60)
- tsport->height = 480;
- else
- tsport->height = 576;
-
- tsport->width = 720;
- cx2341x_fill_defaults(&dev->mpeg_params);
- dev->norm = V4L2_STD_NTSC;
-
- dev->mpeg_params.port = CX2341X_PORT_SERIAL;
-
- /* Allocate and initialize V4L video device */
- dev->v4l_device = cx231xx_video_dev_alloc(dev,
- dev->udev, &cx231xx_mpeg_template, "mpeg");
- err = video_register_device(dev->v4l_device,
- VFL_TYPE_GRABBER, -1);
- if (err < 0) {
- dprintk(3, "%s: can't register mpeg device\n", dev->name);
- return err;
- }
-
- dprintk(3, "%s: registered device video%d [mpeg]\n",
- dev->name, dev->v4l_device->num);
-
- return 0;
-}
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
deleted file mode 100644
index 523aa49d6b86..000000000000
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ /dev/null
@@ -1,2645 +0,0 @@
-/*
- cx231xx-video.c - driver for Conexant Cx23100/101/102
- USB video capture devices
-
- Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
- Based on em28xx driver
- Based on cx23885 driver
- Based on cx88 driver
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitmap.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/msp3400.h>
-#include <media/tuner.h>
-
-#include "dvb_frontend.h"
-
-#include "cx231xx.h"
-#include "cx231xx-vbi.h"
-
-#define CX231XX_VERSION "0.0.2"
-
-#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
-#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
-
-#define cx231xx_videodbg(fmt, arg...) do {\
- if (video_debug) \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); } while (0)
-
-static unsigned int isoc_debug;
-module_param(isoc_debug, int, 0644);
-MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
-
-#define cx231xx_isocdbg(fmt, arg...) \
-do {\
- if (isoc_debug) { \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); \
- } \
- } while (0)
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CX231XX_VERSION);
-
-static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(card, int, NULL, 0444);
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(vbi_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-
-MODULE_PARM_DESC(card, "card type");
-MODULE_PARM_DESC(video_nr, "video device numbers");
-MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
-
-static unsigned int video_debug;
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-
-/* supported video standards */
-static struct cx231xx_fmt format[] = {
- {
- .name = "16bpp YUY2, 4:2:2, packed",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .reg = 0,
- },
-};
-
-/* supported controls */
-/* Common to all boards */
-
-/* ------------------------------------------------------------------- */
-
-static const struct v4l2_queryctrl no_ctl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-
-static struct cx231xx_ctrl cx231xx_ctls[] = {
- /* --- video --- */
- {
- .v = {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0x00,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = LUMA_CTRL,
- .mask = 0x00ff,
- .shift = 0,
- }, {
- .v = {
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = LUMA_CTRL,
- .mask = 0xff00,
- .shift = 8,
- }, {
- .v = {
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = CHROMA_CTRL,
- .mask = 0xff0000,
- .shift = 16,
- }, {
- /* strictly, this only describes only U saturation.
- * V saturation is handled specially through code.
- */
- .v = {
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = CHROMA_CTRL,
- .mask = 0x00ff,
- .shift = 0,
- }, {
- /* --- audio --- */
- .v = {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- .reg = PATH1_CTL1,
- .mask = (0x1f << 24),
- .shift = 24,
- }, {
- .v = {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .reg = PATH1_VOL_CTL,
- .mask = 0xff,
- .shift = 0,
- }
-};
-static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls);
-
-static const u32 cx231xx_user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
-#if 0
- V4L2_CID_AUDIO_BALANCE,
-#endif
- V4L2_CID_AUDIO_MUTE,
- 0
-};
-
-static const u32 *ctrl_classes[] = {
- cx231xx_user_ctrls,
- NULL
-};
-
-/* ------------------------------------------------------------------
- Video buffer and parser functions
- ------------------------------------------------------------------*/
-
-/*
- * Announces that a buffer were filled and request the next
- */
-static inline void buffer_filled(struct cx231xx *dev,
- struct cx231xx_dmaqueue *dma_q,
- struct cx231xx_buffer *buf)
-{
- /* Advice that buffer was filled */
- cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- if (dev->USE_ISO)
- dev->video_mode.isoc_ctl.buf = NULL;
- else
- dev->video_mode.bulk_ctl.buf = NULL;
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-static inline void print_err_status(struct cx231xx *dev, int packet, int status)
-{
- char *errmsg = "Unknown";
-
- switch (status) {
- case -ENOENT:
- errmsg = "unlinked synchronuously";
- break;
- case -ECONNRESET:
- errmsg = "unlinked asynchronuously";
- break;
- case -ENOSR:
- errmsg = "Buffer error (overrun)";
- break;
- case -EPIPE:
- errmsg = "Stalled (device not responding)";
- break;
- case -EOVERFLOW:
- errmsg = "Babble (bad cable?)";
- break;
- case -EPROTO:
- errmsg = "Bit-stuff error (bad cable?)";
- break;
- case -EILSEQ:
- errmsg = "CRC/Timeout (could be anything)";
- break;
- case -ETIME:
- errmsg = "Device does not respond";
- break;
- }
- if (packet < 0) {
- cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
- } else {
- cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
- }
-}
-
-/*
- * video-buf generic routine to get the next available buffer
- */
-static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
- struct cx231xx_buffer **buf)
-{
- struct cx231xx_video_mode *vmode =
- container_of(dma_q, struct cx231xx_video_mode, vidq);
- struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
-
- char *outp;
-
- if (list_empty(&dma_q->active)) {
- cx231xx_isocdbg("No active queue to serve\n");
- if (dev->USE_ISO)
- dev->video_mode.isoc_ctl.buf = NULL;
- else
- dev->video_mode.bulk_ctl.buf = NULL;
- *buf = NULL;
- return;
- }
-
- /* Get the next buffer */
- *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
-
- /* Cleans up buffer - Useful for testing for frame/URB loss */
- outp = videobuf_to_vmalloc(&(*buf)->vb);
- memset(outp, 0, (*buf)->vb.size);
-
- if (dev->USE_ISO)
- dev->video_mode.isoc_ctl.buf = *buf;
- else
- dev->video_mode.bulk_ctl.buf = *buf;
-
- return;
-}
-
-/*
- * Controls the isoc copy of each urb packet
- */
-static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
-{
- struct cx231xx_dmaqueue *dma_q = urb->context;
- int i, rc = 1;
- unsigned char *p_buffer;
- u32 bytes_parsed = 0, buffer_size = 0;
- u8 sav_eav = 0;
-
- if (!dev)
- return 0;
-
- if (dev->state & DEV_DISCONNECTED)
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- if (urb->iso_frame_desc[i].status != -EPROTO)
- continue;
- }
-
- if (urb->iso_frame_desc[i].actual_length <= 0) {
- /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
- continue;
- }
- if (urb->iso_frame_desc[i].actual_length >
- dev->video_mode.max_pkt_size) {
- cx231xx_isocdbg("packet bigger than packet size");
- continue;
- }
-
- /* get buffer pointer and length */
- p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- buffer_size = urb->iso_frame_desc[i].actual_length;
- bytes_parsed = 0;
-
- if (dma_q->is_partial_line) {
- /* Handle the case of a partial line */
- sav_eav = dma_q->last_sav;
- } else {
- /* Check for a SAV/EAV overlapping
- the buffer boundary */
- sav_eav =
- cx231xx_find_boundary_SAV_EAV(p_buffer,
- dma_q->partial_buf,
- &bytes_parsed);
- }
-
- sav_eav &= 0xF0;
- /* Get the first line if we have some portion of an SAV/EAV from
- the last buffer or a partial line */
- if (sav_eav) {
- bytes_parsed += cx231xx_get_video_line(dev, dma_q,
- sav_eav, /* SAV/EAV */
- p_buffer + bytes_parsed, /* p_buffer */
- buffer_size - bytes_parsed);/* buf size */
- }
-
- /* Now parse data that is completely in this buffer */
- /* dma_q->is_partial_line = 0; */
-
- while (bytes_parsed < buffer_size) {
- u32 bytes_used = 0;
-
- sav_eav = cx231xx_find_next_SAV_EAV(
- p_buffer + bytes_parsed, /* p_buffer */
- buffer_size - bytes_parsed, /* buf size */
- &bytes_used);/* bytes used to get SAV/EAV */
-
- bytes_parsed += bytes_used;
-
- sav_eav &= 0xF0;
- if (sav_eav && (bytes_parsed < buffer_size)) {
- bytes_parsed += cx231xx_get_video_line(dev,
- dma_q, sav_eav, /* SAV/EAV */
- p_buffer + bytes_parsed,/* p_buffer */
- buffer_size - bytes_parsed);/*buf size*/
- }
- }
-
- /* Save the last four bytes of the buffer so we can check the
- buffer boundary condition next time */
- memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
- bytes_parsed = 0;
-
- }
- return rc;
-}
-
-static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
-{
- struct cx231xx_dmaqueue *dma_q = urb->context;
- int rc = 1;
- unsigned char *p_buffer;
- u32 bytes_parsed = 0, buffer_size = 0;
- u8 sav_eav = 0;
-
- if (!dev)
- return 0;
-
- if (dev->state & DEV_DISCONNECTED)
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- if (1) {
-
- /* get buffer pointer and length */
- p_buffer = urb->transfer_buffer;
- buffer_size = urb->actual_length;
- bytes_parsed = 0;
-
- if (dma_q->is_partial_line) {
- /* Handle the case of a partial line */
- sav_eav = dma_q->last_sav;
- } else {
- /* Check for a SAV/EAV overlapping
- the buffer boundary */
- sav_eav =
- cx231xx_find_boundary_SAV_EAV(p_buffer,
- dma_q->partial_buf,
- &bytes_parsed);
- }
-
- sav_eav &= 0xF0;
- /* Get the first line if we have some portion of an SAV/EAV from
- the last buffer or a partial line */
- if (sav_eav) {
- bytes_parsed += cx231xx_get_video_line(dev, dma_q,
- sav_eav, /* SAV/EAV */
- p_buffer + bytes_parsed, /* p_buffer */
- buffer_size - bytes_parsed);/* buf size */
- }
-
- /* Now parse data that is completely in this buffer */
- /* dma_q->is_partial_line = 0; */
-
- while (bytes_parsed < buffer_size) {
- u32 bytes_used = 0;
-
- sav_eav = cx231xx_find_next_SAV_EAV(
- p_buffer + bytes_parsed, /* p_buffer */
- buffer_size - bytes_parsed, /* buf size */
- &bytes_used);/* bytes used to get SAV/EAV */
-
- bytes_parsed += bytes_used;
-
- sav_eav &= 0xF0;
- if (sav_eav && (bytes_parsed < buffer_size)) {
- bytes_parsed += cx231xx_get_video_line(dev,
- dma_q, sav_eav, /* SAV/EAV */
- p_buffer + bytes_parsed,/* p_buffer */
- buffer_size - bytes_parsed);/*buf size*/
- }
- }
-
- /* Save the last four bytes of the buffer so we can check the
- buffer boundary condition next time */
- memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
- bytes_parsed = 0;
-
- }
- return rc;
-}
-
-
-u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
- u32 *p_bytes_used)
-{
- u32 bytes_used;
- u8 boundary_bytes[8];
- u8 sav_eav = 0;
-
- *p_bytes_used = 0;
-
- /* Create an array of the last 4 bytes of the last buffer and the first
- 4 bytes of the current buffer. */
-
- memcpy(boundary_bytes, partial_buf, 4);
- memcpy(boundary_bytes + 4, p_buffer, 4);
-
- /* Check for the SAV/EAV in the boundary buffer */
- sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
- &bytes_used);
-
- if (sav_eav) {
- /* found a boundary SAV/EAV. Updates the bytes used to reflect
- only those used in the new buffer */
- *p_bytes_used = bytes_used - 4;
- }
-
- return sav_eav;
-}
-
-u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
-{
- u32 i;
- u8 sav_eav = 0;
-
- /*
- * Don't search if the buffer size is less than 4. It causes a page
- * fault since buffer_size - 4 evaluates to a large number in that
- * case.
- */
- if (buffer_size < 4) {
- *p_bytes_used = buffer_size;
- return 0;
- }
-
- for (i = 0; i < (buffer_size - 3); i++) {
-
- if ((p_buffer[i] == 0xFF) &&
- (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
-
- *p_bytes_used = i + 4;
- sav_eav = p_buffer[i + 3];
- return sav_eav;
- }
- }
-
- *p_bytes_used = buffer_size;
- return 0;
-}
-
-u32 cx231xx_get_video_line(struct cx231xx *dev,
- struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
- u8 *p_buffer, u32 buffer_size)
-{
- u32 bytes_copied = 0;
- int current_field = -1;
-
- switch (sav_eav) {
- case SAV_ACTIVE_VIDEO_FIELD1:
- /* looking for skipped line which occurred in PAL 720x480 mode.
- In this case, there will be no active data contained
- between the SAV and EAV */
- if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
- (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
- ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
- (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
- (p_buffer[3] == EAV_VBLANK_FIELD1) ||
- (p_buffer[3] == EAV_VBLANK_FIELD2)))
- return bytes_copied;
- current_field = 1;
- break;
-
- case SAV_ACTIVE_VIDEO_FIELD2:
- /* looking for skipped line which occurred in PAL 720x480 mode.
- In this case, there will be no active data contained between
- the SAV and EAV */
- if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
- (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
- ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
- (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
- (p_buffer[3] == EAV_VBLANK_FIELD1) ||
- (p_buffer[3] == EAV_VBLANK_FIELD2)))
- return bytes_copied;
- current_field = 2;
- break;
- }
-
- dma_q->last_sav = sav_eav;
-
- bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
- buffer_size, current_field);
-
- return bytes_copied;
-}
-
-u32 cx231xx_copy_video_line(struct cx231xx *dev,
- struct cx231xx_dmaqueue *dma_q, u8 *p_line,
- u32 length, int field_number)
-{
- u32 bytes_to_copy;
- struct cx231xx_buffer *buf;
- u32 _line_size = dev->width * 2;
-
- if (dma_q->current_field != field_number)
- cx231xx_reset_video_buffer(dev, dma_q);
-
- /* get the buffer pointer */
- if (dev->USE_ISO)
- buf = dev->video_mode.isoc_ctl.buf;
- else
- buf = dev->video_mode.bulk_ctl.buf;
-
- /* Remember the field number for next time */
- dma_q->current_field = field_number;
-
- bytes_to_copy = dma_q->bytes_left_in_line;
- if (bytes_to_copy > length)
- bytes_to_copy = length;
-
- if (dma_q->lines_completed >= dma_q->lines_per_field) {
- dma_q->bytes_left_in_line -= bytes_to_copy;
- dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
- 0 : 1;
- return 0;
- }
-
- dma_q->is_partial_line = 1;
-
- /* If we don't have a buffer, just return the number of bytes we would
- have copied if we had a buffer. */
- if (!buf) {
- dma_q->bytes_left_in_line -= bytes_to_copy;
- dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
- ? 0 : 1;
- return bytes_to_copy;
- }
-
- /* copy the data to video buffer */
- cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
-
- dma_q->pos += bytes_to_copy;
- dma_q->bytes_left_in_line -= bytes_to_copy;
-
- if (dma_q->bytes_left_in_line == 0) {
- dma_q->bytes_left_in_line = _line_size;
- dma_q->lines_completed++;
- dma_q->is_partial_line = 0;
-
- if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
- buffer_filled(dev, dma_q, buf);
-
- dma_q->pos = 0;
- buf = NULL;
- dma_q->lines_completed = 0;
- }
- }
-
- return bytes_to_copy;
-}
-
-void cx231xx_reset_video_buffer(struct cx231xx *dev,
- struct cx231xx_dmaqueue *dma_q)
-{
- struct cx231xx_buffer *buf;
-
- /* handle the switch from field 1 to field 2 */
- if (dma_q->current_field == 1) {
- if (dma_q->lines_completed >= dma_q->lines_per_field)
- dma_q->field1_done = 1;
- else
- dma_q->field1_done = 0;
- }
-
- if (dev->USE_ISO)
- buf = dev->video_mode.isoc_ctl.buf;
- else
- buf = dev->video_mode.bulk_ctl.buf;
-
- if (buf == NULL) {
- /* first try to get the buffer */
- get_next_buf(dma_q, &buf);
-
- dma_q->pos = 0;
- dma_q->field1_done = 0;
- dma_q->current_field = -1;
- }
-
- /* reset the counters */
- dma_q->bytes_left_in_line = dev->width << 1;
- dma_q->lines_completed = 0;
-}
-
-int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
- u8 *p_buffer, u32 bytes_to_copy)
-{
- u8 *p_out_buffer = NULL;
- u32 current_line_bytes_copied = 0;
- struct cx231xx_buffer *buf;
- u32 _line_size = dev->width << 1;
- void *startwrite;
- int offset, lencopy;
-
- if (dev->USE_ISO)
- buf = dev->video_mode.isoc_ctl.buf;
- else
- buf = dev->video_mode.bulk_ctl.buf;
-
- if (buf == NULL)
- return -1;
-
- p_out_buffer = videobuf_to_vmalloc(&buf->vb);
-
- current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
-
- /* Offset field 2 one line from the top of the buffer */
- offset = (dma_q->current_field == 1) ? 0 : _line_size;
-
- /* Offset for field 2 */
- startwrite = p_out_buffer + offset;
-
- /* lines already completed in the current field */
- startwrite += (dma_q->lines_completed * _line_size * 2);
-
- /* bytes already completed in the current line */
- startwrite += current_line_bytes_copied;
-
- lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
- bytes_to_copy : dma_q->bytes_left_in_line;
-
- if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
- return 0;
-
- /* The below copies the UYVY data straight into video buffer */
- cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
-
- return 0;
-}
-
-void cx231xx_swab(u16 *from, u16 *to, u16 len)
-{
- u16 i;
-
- if (len <= 0)
- return;
-
- for (i = 0; i < len / 2; i++)
- to[i] = (from[i] << 8) | (from[i] >> 8);
-}
-
-u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
-{
- u8 buffer_complete = 0;
-
- /* Dual field stream */
- buffer_complete = ((dma_q->current_field == 2) &&
- (dma_q->lines_completed >= dma_q->lines_per_field) &&
- dma_q->field1_done);
-
- return buffer_complete;
-}
-
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
-{
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx *dev = fh->dev;
-
- *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
- if (0 == *count)
- *count = CX231XX_DEF_BUF;
-
- if (*count < CX231XX_MIN_BUF)
- *count = CX231XX_MIN_BUF;
-
- return 0;
-}
-
-/* This is called *without* dev->slock held; please keep it that way */
-static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
-{
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx *dev = fh->dev;
- unsigned long flags = 0;
-
- if (in_interrupt())
- BUG();
-
- /* We used to wait for the buffer to finish here, but this didn't work
- because, as we were keeping the state as VIDEOBUF_QUEUED,
- videobuf_queue_cancel marked it as finished for us.
- (Also, it could wedge forever if the hardware was misconfigured.)
-
- This should be safe; by the time we get here, the buffer isn't
- queued anymore. If we ever start marking the buffers as
- VIDEOBUF_ACTIVE, it won't be, though.
- */
- spin_lock_irqsave(&dev->video_mode.slock, flags);
- if (dev->USE_ISO) {
- if (dev->video_mode.isoc_ctl.buf == buf)
- dev->video_mode.isoc_ctl.buf = NULL;
- } else {
- if (dev->video_mode.bulk_ctl.buf == buf)
- dev->video_mode.bulk_ctl.buf = NULL;
- }
- spin_unlock_irqrestore(&dev->video_mode.slock, flags);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- struct cx231xx *dev = fh->dev;
- int rc = 0, urb_init = 0;
-
- /* The only currently supported format is 16 bits/pixel */
- buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
- + 7) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
-
- if (dev->USE_ISO) {
- if (!dev->video_mode.isoc_ctl.num_bufs)
- urb_init = 1;
- } else {
- if (!dev->video_mode.bulk_ctl.num_bufs)
- urb_init = 1;
- }
- /*cx231xx_info("urb_init=%d dev->video_mode.max_pkt_size=%d\n",
- urb_init, dev->video_mode.max_pkt_size);*/
- if (urb_init) {
- dev->mode_tv = 0;
- if (dev->USE_ISO)
- rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
- CX231XX_NUM_BUFS,
- dev->video_mode.max_pkt_size,
- cx231xx_isoc_copy);
- else
- rc = cx231xx_init_bulk(dev, CX231XX_NUM_PACKETS,
- CX231XX_NUM_BUFS,
- dev->video_mode.max_pkt_size,
- cx231xx_bulk_copy);
- if (rc < 0)
- goto fail;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx *dev = fh->dev;
- struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct cx231xx_buffer *buf =
- container_of(vb, struct cx231xx_buffer, vb);
- struct cx231xx_fh *fh = vq->priv_data;
- struct cx231xx *dev = (struct cx231xx *)fh->dev;
-
- cx231xx_isocdbg("cx231xx: called buffer_release\n");
-
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops cx231xx_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/********************* v4l2 interface **************************************/
-
-void video_mux(struct cx231xx *dev, int index)
-{
- dev->video_input = index;
- dev->ctl_ainput = INPUT(index)->amux;
-
- cx231xx_set_video_input_mux(dev, index);
-
- cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0);
-
- cx231xx_set_audio_input(dev, dev->ctl_ainput);
-
- cx231xx_info("video_mux : %d\n", index);
-
- /* do mode control overrides if required */
- cx231xx_do_mode_ctrl_overrides(dev);
-}
-
-/* Usage lock check functions */
-static int res_get(struct cx231xx_fh *fh)
-{
- struct cx231xx *dev = fh->dev;
- int rc = 0;
-
- /* This instance already has stream_on */
- if (fh->stream_on)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (dev->stream_on)
- return -EBUSY;
- dev->stream_on = 1;
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (dev->vbi_stream_on)
- return -EBUSY;
- dev->vbi_stream_on = 1;
- } else
- return -EINVAL;
-
- fh->stream_on = 1;
-
- return rc;
-}
-
-static int res_check(struct cx231xx_fh *fh)
-{
- return fh->stream_on;
-}
-
-static void res_free(struct cx231xx_fh *fh)
-{
- struct cx231xx *dev = fh->dev;
-
- fh->stream_on = 0;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- dev->stream_on = 0;
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- dev->vbi_stream_on = 0;
-}
-
-static int check_dev(struct cx231xx *dev)
-{
- if (dev->state & DEV_DISCONNECTED) {
- cx231xx_errdev("v4l2 ioctl: device not present\n");
- return -ENODEV;
- }
- return 0;
-}
-
-/* ------------------------------------------------------------------
- IOCTL vidioc handling
- ------------------------------------------------------------------*/
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = dev->format->fourcc;
- f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
-
- return 0;
-}
-
-static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(format); i++)
- if (format[i].fourcc == fourcc)
- return &format[i];
-
- return NULL;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- unsigned int width = f->fmt.pix.width;
- unsigned int height = f->fmt.pix.height;
- unsigned int maxw = norm_maxw(dev);
- unsigned int maxh = norm_maxh(dev);
- struct cx231xx_fmt *fmt;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt) {
- cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- /* width must even because of the YUYV format
- height must be even because of interlacing */
- v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
-
- f->fmt.pix.width = width;
- f->fmt.pix.height = height;
- f->fmt.pix.pixelformat = fmt->fourcc;
- f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
- struct cx231xx_fmt *fmt;
- struct v4l2_mbus_framefmt mbus_fmt;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- vidioc_try_fmt_vid_cap(file, priv, f);
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt)
- return -EINVAL;
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- cx231xx_errdev("%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- if (dev->stream_on && !fh->stream_on) {
- cx231xx_errdev("%s device in use by another fh\n", __func__);
- return -EBUSY;
- }
-
- /* set new image size */
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- dev->format = fmt;
-
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
- call_all(dev, video, s_mbus_fmt, &mbus_fmt);
- v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
-
- return rc;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- *id = dev->norm;
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- struct v4l2_mbus_framefmt mbus_fmt;
- struct v4l2_format f;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
-
- dev->norm = *norm;
-
- /* Adjusts width/height, if needed */
- f.fmt.pix.width = dev->width;
- f.fmt.pix.height = dev->height;
- vidioc_try_fmt_vid_cap(file, priv, &f);
-
- call_all(dev, core, s_std, dev->norm);
-
- /* We need to reset basic properties in the decoder related to
- resolution (since a standard change effects things like the number
- of lines in VACT, etc) */
- v4l2_fill_mbus_format(&mbus_fmt, &f.fmt.pix, V4L2_MBUS_FMT_FIXED);
- call_all(dev, video, s_mbus_fmt, &mbus_fmt);
- v4l2_fill_pix_format(&f.fmt.pix, &mbus_fmt);
-
- /* set new image size */
- dev->width = f.fmt.pix.width;
- dev->height = f.fmt.pix.height;
-
- /* do mode control overrides */
- cx231xx_do_mode_ctrl_overrides(dev);
-
- return 0;
-}
-
-static const char *iname[] = {
- [CX231XX_VMUX_COMPOSITE1] = "Composite1",
- [CX231XX_VMUX_SVIDEO] = "S-Video",
- [CX231XX_VMUX_TELEVISION] = "Television",
- [CX231XX_VMUX_CABLE] = "Cable TV",
- [CX231XX_VMUX_DVB] = "DVB",
- [CX231XX_VMUX_DEBUG] = "for debug only",
-};
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- u32 gen_stat;
- unsigned int ret, n;
-
- n = i->index;
- if (n >= MAX_CX231XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(n)->type)
- return -EINVAL;
-
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- strcpy(i->name, iname[INPUT(n)->type]);
-
- if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (CX231XX_VMUX_CABLE == INPUT(n)->type))
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- i->std = dev->vdev->tvnorms;
-
- /* If they are asking about the active input, read signal status */
- if (n == dev->video_input) {
- ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
- GEN_STAT, 2, &gen_stat, 4);
- if (ret > 0) {
- if ((gen_stat & FLD_VPRES) == 0x00)
- i->status |= V4L2_IN_ST_NO_SIGNAL;
- if ((gen_stat & FLD_HLOCK) == 0x00)
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- }
- }
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- *i = dev->video_input;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- dev->mode_tv = 0;
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (i >= MAX_CX231XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(i)->type)
- return -EINVAL;
-
- video_mux(dev, i);
-
- if (INPUT(i)->type == CX231XX_VMUX_TELEVISION ||
- INPUT(i)->type == CX231XX_VMUX_CABLE) {
- /* There's a tuner, so reset the standard and put it on the
- last known frequency (since it was probably powered down
- until now */
- call_all(dev, core, s_std, dev->norm);
- }
-
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- switch (a->index) {
- case CX231XX_AMUX_VIDEO:
- strcpy(a->name, "Television");
- break;
- case CX231XX_AMUX_LINE_IN:
- strcpy(a->name, "Line In");
- break;
- default:
- return -EINVAL;
- }
-
- a->index = dev->ctl_ainput;
- a->capability = V4L2_AUDCAP_STEREO;
-
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int status = 0;
-
- /* Doesn't allow manual routing */
- if (a->index != dev->ctl_ainput)
- return -EINVAL;
-
- dev->ctl_ainput = INPUT(a->index)->amux;
- status = cx231xx_set_audio_input(dev, dev->ctl_ainput);
-
- return status;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int id = qc->id;
- int i;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
- if (unlikely(qc->id == 0))
- return -EINVAL;
-
- memset(qc, 0, sizeof(*qc));
-
- qc->id = id;
-
- if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1)
- return -EINVAL;
-
- for (i = 0; i < CX231XX_CTLS; i++)
- if (cx231xx_ctls[i].v.id == qc->id)
- break;
-
- if (i == CX231XX_CTLS) {
- *qc = no_ctl;
- return 0;
- }
- *qc = cx231xx_ctls[i].v;
-
- call_all(dev, 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)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- call_all(dev, core, g_ctrl, ctrl);
- return rc;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- call_all(dev, core, s_ctrl, ctrl);
- return rc;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Tuner");
-
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
- t->signal = 0xffff; /* LOCKED */
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != t->index)
- return -EINVAL;
-#if 0
- call_all(dev, tuner, s_tuner, t);
-#endif
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
-
- call_all(dev, tuner, g_frequency, f);
-
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
- u32 if_frequency = 5400000;
-
- cx231xx_info("Enter vidioc_s_frequency()f->frequency=%d;f->type=%d\n",
- f->frequency, f->type);
- /*cx231xx_info("f->type: 1-radio 2-analogTV 3-digitalTV\n");*/
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != f->tuner)
- return -EINVAL;
-
- if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
- if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
- return -EINVAL;
-
- /* set pre channel change settings in DIF first */
- rc = cx231xx_tuner_pre_channel_change(dev);
-
- dev->ctl_freq = f->frequency;
- call_all(dev, tuner, s_frequency, f);
-
- /* set post channel change settings in DIF first */
- rc = cx231xx_tuner_post_channel_change(dev);
-
- if (dev->tuner_type == TUNER_NXP_TDA18271) {
- if (dev->norm & (V4L2_STD_MN | V4L2_STD_NTSC_443))
- if_frequency = 5400000; /*5.4MHz */
- else if (dev->norm & V4L2_STD_B)
- if_frequency = 6000000; /*6.0MHz */
- else if (dev->norm & (V4L2_STD_PAL_DK | V4L2_STD_SECAM_DK))
- if_frequency = 6900000; /*6.9MHz */
- else if (dev->norm & V4L2_STD_GH)
- if_frequency = 7100000; /*7.1MHz */
- else if (dev->norm & V4L2_STD_PAL_I)
- if_frequency = 7250000; /*7.25MHz */
- else if (dev->norm & V4L2_STD_SECAM_L)
- if_frequency = 6900000; /*6.9MHz */
- else if (dev->norm & V4L2_STD_SECAM_LC)
- if_frequency = 1250000; /*1.25MHz */
-
- cx231xx_info("if_frequency is set to %d\n", if_frequency);
- cx231xx_set_Colibri_For_LowIF(dev, if_frequency, 1, 1);
-
- update_HH_register_after_set_DIF(dev);
- }
-
- cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
-
- return rc;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-
-/*
- -R, --list-registers=type=<host/i2cdrv/i2caddr>,
- chip=<chip>[,min=<addr>,max=<addr>]
- dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
- -r, --set-register=type=<host/i2cdrv/i2caddr>,
- chip=<chip>,reg=<addr>,val=<val>
- set the register [VIDIOC_DBG_S_REGISTER]
-
- if type == host, then <chip> is the hosts chip ID (default 0)
- if type == i2cdrv (default), then <chip> is the I2C driver name or ID
- if type == i2caddr, then <chip> is the 7-bit I2C address
-*/
-
-static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int ret = 0;
- u8 value[4] = { 0, 0, 0, 0 };
- u32 data = 0;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- switch (reg->match.addr) {
- case 0: /* Cx231xx - internal registers */
- ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
- (u16)reg->reg, value, 4);
- reg->val = value[0] | value[1] << 8 |
- value[2] << 16 | value[3] << 24;
- break;
- case 1: /* AFE - read byte */
- ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2, &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 14: /* AFE - read dword */
- ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- case 2: /* Video Block - read byte */
- ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2, &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 24: /* Video Block - read dword */
- ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- case 3: /* I2S block - read byte */
- ret = cx231xx_read_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- &data, 1);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 34: /* I2S Block - read dword */
- ret =
- cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1, &data, 4);
- reg->val = le32_to_cpu(data);
- break;
- }
- return ret < 0 ? ret : 0;
-
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- call_all(dev, core, g_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:/*for register debug*/
- switch (reg->match.addr) {
- case 0: /* Cx231xx - internal registers */
- ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
- (u16)reg->reg, value, 4);
- reg->val = value[0] | value[1] << 8 |
- value[2] << 16 | value[3] << 24;
-
- break;
- case 0x600:/* AFE - read byte */
- ret = cx231xx_read_i2c_master(dev, AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
-
- case 0x880:/* Video Block - read byte */
- if (reg->reg < 0x0b) {
- ret = cx231xx_read_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- } else {
- ret = cx231xx_read_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- &data, 4 , 0);
- reg->val = le32_to_cpu(data);
- }
- break;
- case 0x980:
- ret = cx231xx_read_i2c_master(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0x400:
- ret =
- cx231xx_read_i2c_master(dev, 0x40,
- (u16)reg->reg, 1,
- &data, 1 , 0);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0xc01:
- ret =
- cx231xx_read_i2c_master(dev, 0xc0,
- (u16)reg->reg, 2,
- &data, 38, 1);
- reg->val = le32_to_cpu(data);
- break;
- case 0x022:
- ret =
- cx231xx_read_i2c_master(dev, 0x02,
- (u16)reg->reg, 1,
- &data, 1, 2);
- reg->val = le32_to_cpu(data & 0xff);
- break;
- case 0x322:
- ret = cx231xx_read_i2c_master(dev,
- 0x32,
- (u16)reg->reg, 1,
- &data, 4 , 2);
- reg->val = le32_to_cpu(data);
- break;
- case 0x342:
- ret = cx231xx_read_i2c_master(dev,
- 0x34,
- (u16)reg->reg, 1,
- &data, 4 , 2);
- reg->val = le32_to_cpu(data);
- break;
-
- default:
- cx231xx_info("no match device address!!\n");
- break;
- }
- return ret < 0 ? ret : 0;
- /*return -EINVAL;*/
- default:
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- }
-
- call_all(dev, core, g_register, reg);
-
- return ret;
-}
-
-static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int ret = 0;
- __le64 buf;
- u32 value;
- u8 data[4] = { 0, 0, 0, 0 };
-
- buf = cpu_to_le64(reg->val);
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- {
- value = (u32) buf & 0xffffffff;
-
- switch (reg->match.addr) {
- case 0: /* cx231xx internal registers */
- data[0] = (u8) value;
- data[1] = (u8) (value >> 8);
- data[2] = (u8) (value >> 16);
- data[3] = (u8) (value >> 24);
- ret = cx231xx_write_ctrl_reg(dev,
- VRT_SET_REGISTER,
- (u16)reg->reg, data,
- 4);
- break;
- case 1: /* AFE - read byte */
- ret = cx231xx_write_i2c_data(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 1);
- break;
- case 14: /* AFE - read dword */
- ret = cx231xx_write_i2c_data(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 4);
- break;
- case 2: /* Video Block - read byte */
- ret =
- cx231xx_write_i2c_data(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 1);
- break;
- case 24: /* Video Block - read dword */
- ret =
- cx231xx_write_i2c_data(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 4);
- break;
- case 3: /* I2S block - read byte */
- ret =
- cx231xx_write_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 1);
- break;
- case 34: /* I2S block - read dword */
- ret =
- cx231xx_write_i2c_data(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 4);
- break;
- }
- }
- return ret < 0 ? ret : 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- {
- value = (u32) buf & 0xffffffff;
-
- switch (reg->match.addr) {
- case 0:/*cx231xx internal registers*/
- data[0] = (u8) value;
- data[1] = (u8) (value >> 8);
- data[2] = (u8) (value >> 16);
- data[3] = (u8) (value >> 24);
- ret = cx231xx_write_ctrl_reg(dev,
- VRT_SET_REGISTER,
- (u16)reg->reg, data,
- 4);
- break;
- case 0x600:/* AFE - read byte */
- ret = cx231xx_write_i2c_master(dev,
- AFE_DEVICE_ADDRESS,
- (u16)reg->reg, 2,
- value, 1 , 0);
- break;
-
- case 0x880:/* Video Block - read byte */
- if (reg->reg < 0x0b)
- cx231xx_write_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 1, 0);
- else
- cx231xx_write_i2c_master(dev,
- VID_BLK_I2C_ADDRESS,
- (u16)reg->reg, 2,
- value, 4, 0);
- break;
- case 0x980:
- ret =
- cx231xx_write_i2c_master(dev,
- I2S_BLK_DEVICE_ADDRESS,
- (u16)reg->reg, 1,
- value, 1, 0);
- break;
- case 0x400:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x40,
- (u16)reg->reg, 1,
- value, 1, 0);
- break;
- case 0xc01:
- ret =
- cx231xx_write_i2c_master(dev,
- 0xc0,
- (u16)reg->reg, 1,
- value, 1, 1);
- break;
-
- case 0x022:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x02,
- (u16)reg->reg, 1,
- value, 1, 2);
- case 0x322:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x32,
- (u16)reg->reg, 1,
- value, 4, 2);
- break;
-
- case 0x342:
- ret =
- cx231xx_write_i2c_master(dev,
- 0x34,
- (u16)reg->reg, 1,
- value, 4, 2);
- break;
- default:
- cx231xx_info("no match device address, "
- "the value is %x\n", reg->match.addr);
- break;
-
- }
-
- }
- default:
- break;
- }
-
- call_all(dev, core, s_register, reg);
-
- return ret;
-}
-#endif
-
-static int vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cc)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- cc->bounds.left = 0;
- cc->bounds.top = 0;
- cc->bounds.width = dev->width;
- cc->bounds.height = dev->height;
- cc->defrect = cc->bounds;
- cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
- cc->pixelaspect.denominator = 59;
-
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- rc = res_get(fh);
-
- if (likely(rc >= 0))
- rc = videobuf_streamon(&fh->vb_vidq);
-
- call_all(dev, video, s_stream, 1);
-
- return rc;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
- if (type != fh->type)
- return -EINVAL;
-
- cx25840_call(dev, video, s_stream, 0);
-
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
-
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
- strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-
- cap->capabilities = V4L2_CAP_VBI_CAPTURE |
-#if 0
- V4L2_CAP_SLICED_VBI_CAPTURE |
-#endif
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
-
- if (dev->tuner_type != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(format)))
- return -EINVAL;
-
- strlcpy(f->description, format[f->index].name, sizeof(f->description));
- f->pixelformat = format[f->index].fourcc;
-
- return 0;
-}
-
-/* Sliced VBI ioctls */
-static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- f->fmt.sliced.service_set = 0;
-
- call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
-
- if (f->fmt.sliced.service_set == 0)
- rc = -EINVAL;
-
- return rc;
-}
-
-static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- call_all(dev, vbi, g_sliced_fmt, &f->fmt.sliced);
-
- if (f->fmt.sliced.service_set == 0)
- return -EINVAL;
-
- return 0;
-}
-
-/* RAW VBI ioctls */
-
-static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- f->fmt.vbi.sampling_rate = 6750000 * 4;
- f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
- f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- f->fmt.vbi.offset = 0;
- f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
- f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_LINES : NTSC_VBI_LINES;
- f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
- f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
-
- return 0;
-
-}
-
-static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
-
- if (dev->vbi_stream_on && !fh->stream_on) {
- cx231xx_errdev("%s device in use by another fh\n", __func__);
- return -EBUSY;
- }
-
- f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
- f->fmt.vbi.sampling_rate = 6750000 * 4;
- f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
- f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- f->fmt.vbi.offset = 0;
- f->fmt.vbi.flags = 0;
- f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
- f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_LINES : NTSC_VBI_LINES;
- f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
- PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
- f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
-
- return 0;
-
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- return videobuf_reqbufs(&fh->vb_vidq, rb);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- return videobuf_querybuf(&fh->vb_vidq, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- return videobuf_qbuf(&fh->vb_vidq, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx231xx_fh *fh = priv;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
-}
-
-/* ----------------------------------------------------------- */
-/* RADIO ESPECIFIC IOCTLS */
-/* ----------------------------------------------------------- */
-
-static int radio_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
-
- strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
- strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
-static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
-
- if (unlikely(t->index > 0))
- return -EINVAL;
-
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
-
- call_all(dev, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
- strcpy(i->name, "Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- return 0;
-}
-
-static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
-
- if (0 != t->index)
- return -EINVAL;
-
- call_all(dev, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- return 0;
-}
-
-static int radio_s_input(struct file *file, void *fh, unsigned int i)
-{
- return 0;
-}
-
-static int radio_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- int i;
-
- if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- for (i = 0; i < CX231XX_CTLS; i++) {
- if (cx231xx_ctls[i].v.id == c->id)
- break;
- }
- if (i == CX231XX_CTLS)
- return -EINVAL;
- *c = cx231xx_ctls[i].v;
- } else
- *c = no_ctl;
- return 0;
-}
-
-/*
- * cx231xx_v4l2_open()
- * inits the device and starts isoc transfer
- */
-static int cx231xx_v4l2_open(struct file *filp)
-{
- int errCode = 0, radio = 0;
- struct video_device *vdev = video_devdata(filp);
- struct cx231xx *dev = video_drvdata(filp);
- struct cx231xx_fh *fh;
- enum v4l2_buf_type fh_type = 0;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- cx231xx_videodbg("open dev=%s type=%s users=%d\n",
- video_device_node_name(vdev), v4l2_type_names[fh_type],
- dev->users);
-
-#if 0
- errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
- if (errCode < 0) {
- cx231xx_errdev
- ("Device locked on digital mode. Can't open analog\n");
- return -EBUSY;
- }
-#endif
-
- fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
- if (!fh) {
- cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
- return -ENOMEM;
- }
- fh->dev = dev;
- fh->radio = radio;
- fh->type = fh_type;
- filp->private_data = fh;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
- dev->width = norm_maxw(dev);
- dev->height = norm_maxh(dev);
-
- /* Power up in Analog TV mode */
- if (dev->board.external_av)
- cx231xx_set_power_mode(dev,
- POLARIS_AVMODE_ENXTERNAL_AV);
- else
- cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
-
-#if 0
- cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
-#endif
-
- /* set video alternate setting */
- cx231xx_set_video_alternate(dev);
-
- /* Needed, since GPIO might have disabled power of
- some i2c device */
- cx231xx_config_i2c(dev);
-
- /* device needs to be initialized before isoc transfer */
- dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
-
- }
- if (fh->radio) {
- cx231xx_videodbg("video_open: setting radio device\n");
-
- /* cx231xx_start_radio(dev); */
-
- call_all(dev, tuner, s_radio);
- }
-
- dev->users++;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
- NULL, &dev->video_mode.slock,
- fh->type, V4L2_FIELD_INTERLACED,
- sizeof(struct cx231xx_buffer),
- fh, &dev->lock);
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- /* Set the required alternate setting VBI interface works in
- Bulk mode only */
- cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
-
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
- NULL, &dev->vbi_mode.slock,
- fh->type, V4L2_FIELD_SEQ_TB,
- sizeof(struct cx231xx_buffer),
- fh, &dev->lock);
- }
-
- return errCode;
-}
-
-/*
- * cx231xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-void cx231xx_release_analog_resources(struct cx231xx *dev)
-{
-
- /*FIXME: I2C IR should be disconnected */
-
- if (dev->radio_dev) {
- if (video_is_registered(dev->radio_dev))
- video_unregister_device(dev->radio_dev);
- else
- video_device_release(dev->radio_dev);
- dev->radio_dev = NULL;
- }
- if (dev->vbi_dev) {
- cx231xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vbi_dev));
- if (video_is_registered(dev->vbi_dev))
- video_unregister_device(dev->vbi_dev);
- else
- video_device_release(dev->vbi_dev);
- dev->vbi_dev = NULL;
- }
- if (dev->vdev) {
- cx231xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vdev));
-
- if (dev->board.has_417)
- cx231xx_417_unregister(dev);
-
- if (video_is_registered(dev->vdev))
- video_unregister_device(dev->vdev);
- else
- video_device_release(dev->vdev);
- dev->vdev = NULL;
- }
-}
-
-/*
- * cx231xx_v4l2_close()
- * stops streaming and deallocates all resources allocated by the v4l2
- * calls and ioctls
- */
-static int cx231xx_v4l2_close(struct file *filp)
-{
- struct cx231xx_fh *fh = filp->private_data;
- struct cx231xx *dev = fh->dev;
-
- cx231xx_videodbg("users=%d\n", dev->users);
-
- cx231xx_videodbg("users=%d\n", dev->users);
- if (res_check(fh))
- res_free(fh);
-
- /*
- * 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);
-
- /* the device is already disconnect,
- free the remaining resources */
- if (dev->state & DEV_DISCONNECTED) {
- if (atomic_read(&dev->devlist_count) > 0) {
- cx231xx_release_resources(dev);
- fh->dev = NULL;
- return 0;
- }
- return 0;
- }
-
- /* do this before setting alternate! */
- cx231xx_uninit_vbi_isoc(dev);
-
- /* set alternate 0 */
- if (!dev->vbi_or_sliced_cc_mode)
- cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
- else
- cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
-
- kfree(fh);
- dev->users--;
- wake_up_interruptible_nr(&dev->open, 1);
- return 0;
- }
-
- dev->users--;
- if (!dev->users) {
- videobuf_stop(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vidq);
-
- /* the device is already disconnect,
- free the remaining resources */
- if (dev->state & DEV_DISCONNECTED) {
- cx231xx_release_resources(dev);
- fh->dev = NULL;
- return 0;
- }
-
- /* Save some power by putting tuner to sleep */
- call_all(dev, core, s_power, 0);
-
- /* do this before setting alternate! */
- if (dev->USE_ISO)
- cx231xx_uninit_isoc(dev);
- else
- cx231xx_uninit_bulk(dev);
- cx231xx_set_mode(dev, CX231XX_SUSPEND);
-
- /* set alternate 0 */
- cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
- }
- kfree(fh);
- wake_up_interruptible_nr(&dev->open, 1);
- return 0;
-}
-
-/*
- * cx231xx_v4l2_read()
- * will allocate buffers when called for the first time
- */
-static ssize_t
-cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
- loff_t *pos)
-{
- struct cx231xx_fh *fh = filp->private_data;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
- (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
- rc = res_get(fh);
-
- if (unlikely(rc < 0))
- return rc;
-
- return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
- filp->f_flags & O_NONBLOCK);
- }
- return 0;
-}
-
-/*
- * cx231xx_v4l2_poll()
- * will allocate buffers when called for the first time
- */
-static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table *wait)
-{
- struct cx231xx_fh *fh = filp->private_data;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- rc = res_get(fh);
-
- if (unlikely(rc < 0))
- return POLLERR;
-
- if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
- (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type))
- return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
- else
- return POLLERR;
-}
-
-/*
- * cx231xx_v4l2_mmap()
- */
-static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct cx231xx_fh *fh = filp->private_data;
- struct cx231xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- rc = res_get(fh);
-
- if (unlikely(rc < 0))
- return rc;
-
- rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
-
- cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end -
- (unsigned long)vma->vm_start, rc);
-
- return rc;
-}
-
-static const struct v4l2_file_operations cx231xx_v4l_fops = {
- .owner = THIS_MODULE,
- .open = cx231xx_v4l2_open,
- .release = cx231xx_v4l2_close,
- .read = cx231xx_v4l2_read,
- .poll = cx231xx_v4l2_poll,
- .mmap = cx231xx_v4l2_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_cropcap = vidioc_cropcap,
- .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
- .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static struct video_device cx231xx_vbi_template;
-
-static const struct video_device cx231xx_video_template = {
- .fops = &cx231xx_v4l_fops,
- .release = video_device_release,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = V4L2_STD_ALL,
- .current_norm = V4L2_STD_PAL,
-};
-
-static const struct v4l2_file_operations radio_fops = {
- .owner = THIS_MODULE,
- .open = cx231xx_v4l2_open,
- .release = cx231xx_v4l2_close,
- .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_queryctrl = radio_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static struct video_device cx231xx_radio_template = {
- .name = "cx231xx-radio",
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-/******************************** usb interface ******************************/
-
-static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
- const struct video_device
- *template, const char *type_name)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
-
- *vfd = *template;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
- vfd->debug = video_debug;
- vfd->lock = &dev->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
-
- snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
-
- video_set_drvdata(vfd, dev);
- return vfd;
-}
-
-int cx231xx_register_analog_devices(struct cx231xx *dev)
-{
- int ret;
-
- cx231xx_info("%s: v4l2 driver version %s\n",
- dev->name, CX231XX_VERSION);
-
- /* set default norm */
- /*dev->norm = cx231xx_video_template.current_norm; */
- dev->width = norm_maxw(dev);
- dev->height = norm_maxh(dev);
- dev->interlaced = 0;
-
- /* Analog specific initialization */
- dev->format = &format[0];
-
- /* Set the initial input */
- video_mux(dev, dev->video_input);
-
- /* Audio defaults */
- dev->mute = 1;
- dev->volume = 0x1f;
-
- /* enable vbi capturing */
- /* write code here... */
-
- /* allocate and fill video video_device struct */
- dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
- if (!dev->vdev) {
- cx231xx_errdev("cannot allocate video_device.\n");
- return -ENODEV;
- }
-
- /* register v4l2 video video_device */
- ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
- video_nr[dev->devno]);
- if (ret) {
- cx231xx_errdev("unable to register video device (error=%i).\n",
- ret);
- return ret;
- }
-
- cx231xx_info("%s/0: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(dev->vdev));
-
- /* Initialize VBI template */
- memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
- sizeof(cx231xx_vbi_template));
- strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
-
- /* Allocate and fill vbi video_device struct */
- dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
-
- /* register v4l2 vbi video_device */
- ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[dev->devno]);
- if (ret < 0) {
- cx231xx_errdev("unable to register vbi device\n");
- return ret;
- }
-
- cx231xx_info("%s/0: registered device %s\n",
- dev->name, video_device_node_name(dev->vbi_dev));
-
- if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
- dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
- "radio");
- if (!dev->radio_dev) {
- cx231xx_errdev("cannot allocate video_device.\n");
- return -ENODEV;
- }
- ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
- radio_nr[dev->devno]);
- if (ret < 0) {
- cx231xx_errdev("can't register radio device\n");
- return ret;
- }
- cx231xx_info("Registered radio device as %s\n",
- video_device_node_name(dev->radio_dev));
- }
-
- cx231xx_info("V4L2 device registered as %s and %s\n",
- video_device_node_name(dev->vdev),
- video_device_node_name(dev->vbi_dev));
-
- return 0;
-}
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
deleted file mode 100644
index b391e9bda877..000000000000
--- a/drivers/media/video/cx23885/Kconfig
+++ /dev/null
@@ -1,46 +0,0 @@
-config VIDEO_CX23885
- tristate "Conexant cx23885 (2388x successor) support"
- depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND
- select SND_PCM
- select I2C_ALGOBIT
- select VIDEO_BTCX
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- depends on RC_CORE
- select VIDEOBUF_DVB
- select VIDEOBUF_DMA_SG
- select VIDEO_CX25840
- select VIDEO_CX2341X
- select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_TDA10048 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_STV6110 if !DVB_FE_CUSTOMISE
- select DVB_CX24116 if !DVB_FE_CUSTOMISE
- select DVB_STV0900 if !DVB_FE_CUSTOMISE
- select DVB_DS3000 if !DVB_FE_CUSTOMISE
- select DVB_STV0367 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
- ---help---
- This is a video4linux driver for Conexant 23885 based
- TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called cx23885
-
-config MEDIA_ALTERA_CI
- tristate "Altera FPGA based CI module"
- depends on VIDEO_CX23885 && DVB_CORE
- select ALTERA_STAPL
- ---help---
- An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card.
-
- To compile this driver as a module, choose M here: the
- module will be called altera-ci
diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
deleted file mode 100644
index f81f2796a0f9..000000000000
--- a/drivers/media/video/cx23885/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
- cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
- cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \
- cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \
- cx23885-f300.o cx23885-alsa.o
-
-obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
-obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-
-ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/cx23885/altera-ci.c b/drivers/media/video/cx23885/altera-ci.c
deleted file mode 100644
index 1fa8927f0d36..000000000000
--- a/drivers/media/video/cx23885/altera-ci.c
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- * altera-ci.c
- *
- * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
- *
- * Copyright (C) 2010,2011 NetUP Inc.
- * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- * currently cx23885 GPIO's used.
- * GPIO-0 ~INT in
- * GPIO-1 TMS out
- * GPIO-2 ~reset chips out
- * GPIO-3 to GPIO-10 data/addr for CA in/out
- * GPIO-11 ~CS out
- * GPIO-12 AD_RG out
- * GPIO-13 ~WR out
- * GPIO-14 ~RD out
- * GPIO-15 ~RDY in
- * GPIO-16 TCK out
- * GPIO-17 TDO in
- * GPIO-18 TDI out
- */
-/*
- * Bit definitions for MC417_RWD and MC417_OEN registers
- * bits 31-16
- * +-----------+
- * | Reserved |
- * +-----------+
- * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
- * +-------+-------+-------+-------+-------+-------+-------+-------+
- * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS |
- * +-------+-------+-------+-------+-------+-------+-------+-------+
- * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
- * +-------+-------+-------+-------+-------+-------+-------+-------+
- * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
- * +-------+-------+-------+-------+-------+-------+-------+-------+
- */
-#include <media/videobuf-dma-sg.h>
-#include <media/videobuf-dvb.h>
-#include "altera-ci.h"
-#include "dvb_ca_en50221.h"
-
-/* FPGA regs */
-#define NETUP_CI_INT_CTRL 0x00
-#define NETUP_CI_BUSCTRL2 0x01
-#define NETUP_CI_ADDR0 0x04
-#define NETUP_CI_ADDR1 0x05
-#define NETUP_CI_DATA 0x06
-#define NETUP_CI_BUSCTRL 0x07
-#define NETUP_CI_PID_ADDR0 0x08
-#define NETUP_CI_PID_ADDR1 0x09
-#define NETUP_CI_PID_DATA 0x0a
-#define NETUP_CI_TSA_DIV 0x0c
-#define NETUP_CI_TSB_DIV 0x0d
-#define NETUP_CI_REVISION 0x0f
-
-/* const for ci op */
-#define NETUP_CI_FLG_CTL 1
-#define NETUP_CI_FLG_RD 1
-#define NETUP_CI_FLG_AD 1
-
-static unsigned int ci_dbg;
-module_param(ci_dbg, int, 0644);
-MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
-
-static unsigned int pid_dbg;
-module_param(pid_dbg, int, 0644);
-MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
-
-MODULE_DESCRIPTION("altera FPGA CI module");
-MODULE_AUTHOR("Igor M. Liplianin <liplianin@netup.ru>");
-MODULE_LICENSE("GPL");
-
-#define ci_dbg_print(args...) \
- do { \
- if (ci_dbg) \
- printk(KERN_DEBUG args); \
- } while (0)
-
-#define pid_dbg_print(args...) \
- do { \
- if (pid_dbg) \
- printk(KERN_DEBUG args); \
- } while (0)
-
-struct altera_ci_state;
-struct netup_hw_pid_filter;
-
-struct fpga_internal {
- void *dev;
- struct mutex fpga_mutex;/* two CI's on the same fpga */
- struct netup_hw_pid_filter *pid_filt[2];
- struct altera_ci_state *state[2];
- struct work_struct work;
- int (*fpga_rw) (void *dev, int flag, int data, int rw);
- int cis_used;
- int filts_used;
- int strt_wrk;
-};
-
-/* stores all private variables for communication with CI */
-struct altera_ci_state {
- struct fpga_internal *internal;
- struct dvb_ca_en50221 ca;
- int status;
- int nr;
-};
-
-/* stores all private variables for hardware pid filtering */
-struct netup_hw_pid_filter {
- struct fpga_internal *internal;
- struct dvb_demux *demux;
- /* save old functions */
- int (*start_feed)(struct dvb_demux_feed *feed);
- int (*stop_feed)(struct dvb_demux_feed *feed);
-
- int status;
- int nr;
-};
-
-/* internal params node */
-struct fpga_inode {
- /* pointer for internal params, one for each pair of CI's */
- struct fpga_internal *internal;
- struct fpga_inode *next_inode;
-};
-
-/* first internal params */
-static struct fpga_inode *fpga_first_inode;
-
-/* find chip by dev */
-static struct fpga_inode *find_inode(void *dev)
-{
- struct fpga_inode *temp_chip = fpga_first_inode;
-
- if (temp_chip == NULL)
- return temp_chip;
-
- /*
- Search for the last fpga CI chip or
- find it by dev */
- while ((temp_chip != NULL) &&
- (temp_chip->internal->dev != dev))
- temp_chip = temp_chip->next_inode;
-
- return temp_chip;
-}
-/* check demux */
-static struct fpga_internal *check_filter(struct fpga_internal *temp_int,
- void *demux_dev, int filt_nr)
-{
- if (temp_int == NULL)
- return NULL;
-
- if ((temp_int->pid_filt[filt_nr]) == NULL)
- return NULL;
-
- if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
- return temp_int;
-
- return NULL;
-}
-
-/* find chip by demux */
-static struct fpga_inode *find_dinode(void *demux_dev)
-{
- struct fpga_inode *temp_chip = fpga_first_inode;
- struct fpga_internal *temp_int;
-
- /*
- * Search of the last fpga CI chip or
- * find it by demux
- */
- while (temp_chip != NULL) {
- if (temp_chip->internal != NULL) {
- temp_int = temp_chip->internal;
- if (check_filter(temp_int, demux_dev, 0))
- break;
- if (check_filter(temp_int, demux_dev, 1))
- break;
- }
-
- temp_chip = temp_chip->next_inode;
- }
-
- return temp_chip;
-}
-
-/* deallocating chip */
-static void remove_inode(struct fpga_internal *internal)
-{
- struct fpga_inode *prev_node = fpga_first_inode;
- struct fpga_inode *del_node = find_inode(internal->dev);
-
- if (del_node != NULL) {
- if (del_node == fpga_first_inode) {
- fpga_first_inode = del_node->next_inode;
- } else {
- while (prev_node->next_inode != del_node)
- prev_node = prev_node->next_inode;
-
- if (del_node->next_inode == NULL)
- prev_node->next_inode = NULL;
- else
- prev_node->next_inode =
- prev_node->next_inode->next_inode;
- }
-
- kfree(del_node);
- }
-}
-
-/* allocating new chip */
-static struct fpga_inode *append_internal(struct fpga_internal *internal)
-{
- struct fpga_inode *new_node = fpga_first_inode;
-
- if (new_node == NULL) {
- new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
- fpga_first_inode = new_node;
- } else {
- while (new_node->next_inode != NULL)
- new_node = new_node->next_inode;
-
- new_node->next_inode =
- kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
- if (new_node->next_inode != NULL)
- new_node = new_node->next_inode;
- else
- new_node = NULL;
- }
-
- if (new_node != NULL) {
- new_node->internal = internal;
- new_node->next_inode = NULL;
- }
-
- return new_node;
-}
-
-static int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
- u8 val, u8 read)
-{
- inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
- return inter->fpga_rw(inter->dev, 0, val, read);
-}
-
-/* flag - mem/io, read - read/write */
-int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
- u8 flag, u8 read, int addr, u8 val)
-{
-
- struct altera_ci_state *state = en50221->data;
- struct fpga_internal *inter = state->internal;
-
- u8 store;
- int mem = 0;
-
- if (0 != slot)
- return -EINVAL;
-
- mutex_lock(&inter->fpga_mutex);
-
- netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
- netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
- store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
-
- store &= 0x0f;
- store |= ((state->nr << 7) | (flag << 6));
-
- netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
- mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
-
- mutex_unlock(&inter->fpga_mutex);
-
- ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
- (read) ? "read" : "write", addr,
- (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
- (read) ? mem : val);
-
- return mem;
-}
-
-int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
- int slot, int addr)
-{
- return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
-}
-
-int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
- int slot, int addr, u8 data)
-{
- return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
-}
-
-int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
-{
- return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
- NETUP_CI_FLG_RD, addr, 0);
-}
-
-int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
- u8 addr, u8 data)
-{
- return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
-}
-
-int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
-{
- struct altera_ci_state *state = en50221->data;
- struct fpga_internal *inter = state->internal;
- /* reasonable timeout for CI reset is 10 seconds */
- unsigned long t_out = jiffies + msecs_to_jiffies(9999);
- int ret;
-
- ci_dbg_print("%s\n", __func__);
-
- if (0 != slot)
- return -EINVAL;
-
- mutex_lock(&inter->fpga_mutex);
-
- ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
- netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
- (ret & 0xcf) | (1 << (5 - state->nr)), 0);
-
- mutex_unlock(&inter->fpga_mutex);
-
- for (;;) {
- mdelay(50);
-
- mutex_lock(&inter->fpga_mutex);
-
- ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
- 0, NETUP_CI_FLG_RD);
- mutex_unlock(&inter->fpga_mutex);
-
- if ((ret & (1 << (5 - state->nr))) == 0)
- break;
- if (time_after(jiffies, t_out))
- break;
- }
-
-
- ci_dbg_print("%s: %d msecs\n", __func__,
- jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
-
- return 0;
-}
-
-int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
-{
- /* not implemented */
- return 0;
-}
-
-int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
-{
- struct altera_ci_state *state = en50221->data;
- struct fpga_internal *inter = state->internal;
- int ret;
-
- ci_dbg_print("%s\n", __func__);
-
- if (0 != slot)
- return -EINVAL;
-
- mutex_lock(&inter->fpga_mutex);
-
- ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
- netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
- (ret & 0x0f) | (1 << (3 - state->nr)), 0);
-
- mutex_unlock(&inter->fpga_mutex);
-
- return 0;
-}
-
-/* work handler */
-static void netup_read_ci_status(struct work_struct *work)
-{
- struct fpga_internal *inter =
- container_of(work, struct fpga_internal, work);
- int ret;
-
- ci_dbg_print("%s\n", __func__);
-
- mutex_lock(&inter->fpga_mutex);
- /* ack' irq */
- ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
- ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
-
- mutex_unlock(&inter->fpga_mutex);
-
- if (inter->state[1] != NULL) {
- inter->state[1]->status =
- ((ret & 1) == 0 ?
- DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY : 0);
- ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
- __func__, inter->state[1]->status);
- };
-
- if (inter->state[0] != NULL) {
- inter->state[0]->status =
- ((ret & 2) == 0 ?
- DVB_CA_EN50221_POLL_CAM_PRESENT |
- DVB_CA_EN50221_POLL_CAM_READY : 0);
- ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
- __func__, inter->state[0]->status);
- };
-}
-
-/* CI irq handler */
-int altera_ci_irq(void *dev)
-{
- struct fpga_inode *temp_int = NULL;
- struct fpga_internal *inter = NULL;
-
- ci_dbg_print("%s\n", __func__);
-
- if (dev != NULL) {
- temp_int = find_inode(dev);
- if (temp_int != NULL) {
- inter = temp_int->internal;
- schedule_work(&inter->work);
- }
- }
-
- return 1;
-}
-EXPORT_SYMBOL(altera_ci_irq);
-
-int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot,
- int open)
-{
- struct altera_ci_state *state = en50221->data;
-
- if (0 != slot)
- return -EINVAL;
-
- return state->status;
-}
-
-void altera_hw_filt_release(void *main_dev, int filt_nr)
-{
- struct fpga_inode *temp_int = find_inode(main_dev);
- struct netup_hw_pid_filter *pid_filt = NULL;
-
- ci_dbg_print("%s\n", __func__);
-
- if (temp_int != NULL) {
- pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
- /* stored old feed controls */
- pid_filt->demux->start_feed = pid_filt->start_feed;
- pid_filt->demux->stop_feed = pid_filt->stop_feed;
-
- if (((--(temp_int->internal->filts_used)) <= 0) &&
- ((temp_int->internal->cis_used) <= 0)) {
-
- ci_dbg_print("%s: Actually removing\n", __func__);
-
- remove_inode(temp_int->internal);
- kfree(pid_filt->internal);
- }
-
- kfree(pid_filt);
-
- }
-
-}
-EXPORT_SYMBOL(altera_hw_filt_release);
-
-void altera_ci_release(void *dev, int ci_nr)
-{
- struct fpga_inode *temp_int = find_inode(dev);
- struct altera_ci_state *state = NULL;
-
- ci_dbg_print("%s\n", __func__);
-
- if (temp_int != NULL) {
- state = temp_int->internal->state[ci_nr - 1];
- altera_hw_filt_release(dev, ci_nr);
-
-
- if (((temp_int->internal->filts_used) <= 0) &&
- ((--(temp_int->internal->cis_used)) <= 0)) {
-
- ci_dbg_print("%s: Actually removing\n", __func__);
-
- remove_inode(temp_int->internal);
- kfree(state->internal);
- }
-
- if (state != NULL) {
- if (state->ca.data != NULL)
- dvb_ca_en50221_release(&state->ca);
-
- kfree(state);
- }
- }
-
-}
-EXPORT_SYMBOL(altera_ci_release);
-
-static void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
- u16 pid, int onoff)
-{
- struct fpga_internal *inter = pid_filt->internal;
- u8 store = 0;
-
- /* pid 0-0x1f always enabled, don't touch them */
- if ((pid == 0x2000) || (pid < 0x20))
- return;
-
- mutex_lock(&inter->fpga_mutex);
-
- netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
- netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
- ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
-
- store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
-
- if (onoff)/* 0 - on, 1 - off */
- store |= (1 << (pid & 7));
- else
- store &= ~(1 << (pid & 7));
-
- netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
-
- mutex_unlock(&inter->fpga_mutex);
-
- pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
- pid_filt->nr, pid, pid, onoff ? "off" : "on");
-}
-
-static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
- int filt_nr, int onoff)
-{
- struct fpga_internal *inter = pid_filt->internal;
- u8 store = 0;
- int i;
-
- pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr,
- onoff ? "off" : "on");
-
- if (onoff)/* 0 - on, 1 - off */
- store = 0xff;/* ignore pid */
- else
- store = 0;/* enable pid */
-
- mutex_lock(&inter->fpga_mutex);
-
- for (i = 0; i < 1024; i++) {
- netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
-
- netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
- ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
- /* pid 0-0x1f always enabled */
- netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
- (i > 3 ? store : 0), 0);
- }
-
- mutex_unlock(&inter->fpga_mutex);
-}
-
-int altera_pid_feed_control(void *demux_dev, int filt_nr,
- struct dvb_demux_feed *feed, int onoff)
-{
- struct fpga_inode *temp_int = find_dinode(demux_dev);
- struct fpga_internal *inter = temp_int->internal;
- struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
-
- altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
- /* call old feed proc's */
- if (onoff)
- pid_filt->start_feed(feed);
- else
- pid_filt->stop_feed(feed);
-
- if (feed->pid == 0x2000)
- altera_toggle_fullts_streaming(pid_filt, filt_nr,
- onoff ? 0 : 1);
-
- return 0;
-}
-EXPORT_SYMBOL(altera_pid_feed_control);
-
-int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
-{
- altera_pid_feed_control(feed->demux, num, feed, 1);
-
- return 0;
-}
-
-int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
-{
- altera_pid_feed_control(feed->demux, num, feed, 0);
-
- return 0;
-}
-
-int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
-{
- return altera_ci_start_feed(feed, 1);
-}
-
-int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
-{
- return altera_ci_stop_feed(feed, 1);
-}
-
-int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
-{
- return altera_ci_start_feed(feed, 2);
-}
-
-int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
-{
- return altera_ci_stop_feed(feed, 2);
-}
-
-int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
-{
- struct netup_hw_pid_filter *pid_filt = NULL;
- struct fpga_inode *temp_int = find_inode(config->dev);
- struct fpga_internal *inter = NULL;
- int ret = 0;
-
- pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
-
- ci_dbg_print("%s\n", __func__);
-
- if (!pid_filt) {
- ret = -ENOMEM;
- goto err;
- }
-
- if (temp_int != NULL) {
- inter = temp_int->internal;
- (inter->filts_used)++;
- ci_dbg_print("%s: Find Internal Structure!\n", __func__);
- } else {
- inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
- if (!inter) {
- ret = -ENOMEM;
- goto err;
- }
-
- temp_int = append_internal(inter);
- inter->filts_used = 1;
- inter->dev = config->dev;
- inter->fpga_rw = config->fpga_rw;
- mutex_init(&inter->fpga_mutex);
- inter->strt_wrk = 1;
- ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
- }
-
- ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
- pid_filt, hw_filt_nr - 1);
- inter->pid_filt[hw_filt_nr - 1] = pid_filt;
- pid_filt->demux = config->demux;
- pid_filt->internal = inter;
- pid_filt->nr = hw_filt_nr - 1;
- /* store old feed controls */
- pid_filt->start_feed = config->demux->start_feed;
- pid_filt->stop_feed = config->demux->stop_feed;
- /* replace with new feed controls */
- if (hw_filt_nr == 1) {
- pid_filt->demux->start_feed = altera_ci_start_feed_1;
- pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
- } else if (hw_filt_nr == 2) {
- pid_filt->demux->start_feed = altera_ci_start_feed_2;
- pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
- }
-
- altera_toggle_fullts_streaming(pid_filt, 0, 1);
-
- return 0;
-err:
- ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
- __func__, ret);
-
- kfree(pid_filt);
-
- return ret;
-}
-EXPORT_SYMBOL(altera_hw_filt_init);
-
-int altera_ci_init(struct altera_ci_config *config, int ci_nr)
-{
- struct altera_ci_state *state;
- struct fpga_inode *temp_int = find_inode(config->dev);
- struct fpga_internal *inter = NULL;
- int ret = 0;
- u8 store = 0;
-
- state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
-
- ci_dbg_print("%s\n", __func__);
-
- if (!state) {
- ret = -ENOMEM;
- goto err;
- }
-
- if (temp_int != NULL) {
- inter = temp_int->internal;
- (inter->cis_used)++;
- ci_dbg_print("%s: Find Internal Structure!\n", __func__);
- } else {
- inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
- if (!inter) {
- ret = -ENOMEM;
- goto err;
- }
-
- temp_int = append_internal(inter);
- inter->cis_used = 1;
- inter->dev = config->dev;
- inter->fpga_rw = config->fpga_rw;
- mutex_init(&inter->fpga_mutex);
- inter->strt_wrk = 1;
- ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
- }
-
- ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
- state, ci_nr - 1);
- 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/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
deleted file mode 100644
index f5c79e53e5a1..000000000000
--- a/drivers/media/video/cx23885/cx23885-417.c
+++ /dev/null
@@ -1,1788 +0,0 @@
-/*
- *
- * Support for a cx23417 mpeg encoder via cx23885 host port.
- *
- * (c) 2004 Jelle Foks <jelle@foks.us>
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
- * (c) 2008 Steven Toth <stoth@linuxtv.org>
- * - CX23885/7/8 support
- *
- * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/cx2341x.h>
-
-#include "cx23885.h"
-#include "cx23885-ioctl.h"
-
-#define CX23885_FIRM_IMAGE_SIZE 376836
-#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw"
-
-static unsigned int mpegbufs = 32;
-module_param(mpegbufs, int, 0644);
-MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32");
-static unsigned int mpeglines = 32;
-module_param(mpeglines, int, 0644);
-MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32");
-static unsigned int mpeglinesize = 512;
-module_param(mpeglinesize, int, 0644);
-MODULE_PARM_DESC(mpeglinesize,
- "number of bytes in each line of an MPEG buffer, range 512-1024");
-
-static unsigned int v4l_debug;
-module_param(v4l_debug, int, 0644);
-MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages");
-
-#define dprintk(level, fmt, arg...)\
- do { if (v4l_debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, \
- (dev) ? dev->name : "cx23885[?]", ## arg); \
- } while (0)
-
-static struct cx23885_tvnorm cx23885_tvnorms[] = {
- {
- .name = "NTSC-M",
- .id = V4L2_STD_NTSC_M,
- }, {
- .name = "NTSC-JP",
- .id = V4L2_STD_NTSC_M_JP,
- }, {
- .name = "PAL-BG",
- .id = V4L2_STD_PAL_BG,
- }, {
- .name = "PAL-DK",
- .id = V4L2_STD_PAL_DK,
- }, {
- .name = "PAL-I",
- .id = V4L2_STD_PAL_I,
- }, {
- .name = "PAL-M",
- .id = V4L2_STD_PAL_M,
- }, {
- .name = "PAL-N",
- .id = V4L2_STD_PAL_N,
- }, {
- .name = "PAL-Nc",
- .id = V4L2_STD_PAL_Nc,
- }, {
- .name = "PAL-60",
- .id = V4L2_STD_PAL_60,
- }, {
- .name = "SECAM-L",
- .id = V4L2_STD_SECAM_L,
- }, {
- .name = "SECAM-DK",
- .id = V4L2_STD_SECAM_DK,
- }
-};
-
-/* ------------------------------------------------------------------ */
-enum cx23885_capture_type {
- CX23885_MPEG_CAPTURE,
- CX23885_RAW_CAPTURE,
- CX23885_RAW_PASSTHRU_CAPTURE
-};
-enum cx23885_capture_bits {
- CX23885_RAW_BITS_NONE = 0x00,
- CX23885_RAW_BITS_YUV_CAPTURE = 0x01,
- CX23885_RAW_BITS_PCM_CAPTURE = 0x02,
- CX23885_RAW_BITS_VBI_CAPTURE = 0x04,
- CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
- CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10
-};
-enum cx23885_capture_end {
- CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */
- CX23885_END_NOW, /* stop immediately, no irq */
-};
-enum cx23885_framerate {
- CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */
- CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */
-};
-enum cx23885_stream_port {
- CX23885_OUTPUT_PORT_MEMORY,
- CX23885_OUTPUT_PORT_STREAMING,
- CX23885_OUTPUT_PORT_SERIAL
-};
-enum cx23885_data_xfer_status {
- CX23885_MORE_BUFFERS_FOLLOW,
- CX23885_LAST_BUFFER,
-};
-enum cx23885_picture_mask {
- CX23885_PICTURE_MASK_NONE,
- CX23885_PICTURE_MASK_I_FRAMES,
- CX23885_PICTURE_MASK_I_P_FRAMES = 0x3,
- CX23885_PICTURE_MASK_ALL_FRAMES = 0x7,
-};
-enum cx23885_vbi_mode_bits {
- CX23885_VBI_BITS_SLICED,
- CX23885_VBI_BITS_RAW,
-};
-enum cx23885_vbi_insertion_bits {
- CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
- CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
- CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
- CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
- CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
-};
-enum cx23885_dma_unit {
- CX23885_DMA_BYTES,
- CX23885_DMA_FRAMES,
-};
-enum cx23885_dma_transfer_status_bits {
- CX23885_DMA_TRANSFER_BITS_DONE = 0x01,
- CX23885_DMA_TRANSFER_BITS_ERROR = 0x04,
- CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
-};
-enum cx23885_pause {
- CX23885_PAUSE_ENCODING,
- CX23885_RESUME_ENCODING,
-};
-enum cx23885_copyright {
- CX23885_COPYRIGHT_OFF,
- CX23885_COPYRIGHT_ON,
-};
-enum cx23885_notification_type {
- CX23885_NOTIFICATION_REFRESH,
-};
-enum cx23885_notification_status {
- CX23885_NOTIFICATION_OFF,
- CX23885_NOTIFICATION_ON,
-};
-enum cx23885_notification_mailbox {
- CX23885_NOTIFICATION_NO_MAILBOX = -1,
-};
-enum cx23885_field1_lines {
- CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */
- CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */
- CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */
-};
-enum cx23885_field2_lines {
- CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */
- CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */
- CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */
-};
-enum cx23885_custom_data_type {
- CX23885_CUSTOM_EXTENSION_USR_DATA,
- CX23885_CUSTOM_PRIVATE_PACKET,
-};
-enum cx23885_mute {
- CX23885_UNMUTE,
- CX23885_MUTE,
-};
-enum cx23885_mute_video_mask {
- CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00,
- CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000,
- CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000,
-};
-enum cx23885_mute_video_shift {
- CX23885_MUTE_VIDEO_V_SHIFT = 8,
- CX23885_MUTE_VIDEO_U_SHIFT = 16,
- CX23885_MUTE_VIDEO_Y_SHIFT = 24,
-};
-
-/* defines below are from ivtv-driver.h */
-#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
-
-/* Firmware API commands */
-#define IVTV_API_STD_TIMEOUT 500
-
-/* Registers */
-/* IVTV_REG_OFFSET */
-#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
-#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
-#define IVTV_REG_SPU (0x9050)
-#define IVTV_REG_HW_BLOCKS (0x9054)
-#define IVTV_REG_VPU (0x9058)
-#define IVTV_REG_APU (0xA064)
-
-/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
- bits 31-16
-+-----------+
-| Reserved |
-+-----------+
- bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
-+-------+-------+-------+-------+-------+-------+-------+-------+
-| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0|
-+-------+-------+-------+-------+-------+-------+-------+-------+
- bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
-+-------+-------+-------+-------+-------+-------+-------+-------+
-|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0|
-+-------+-------+-------+-------+-------+-------+-------+-------+
-***/
-#define MC417_MIWR 0x8000
-#define MC417_MIRD 0x4000
-#define MC417_MICS 0x2000
-#define MC417_MIRDY 0x1000
-#define MC417_MIADDR 0x0F00
-#define MC417_MIDATA 0x00FF
-
-/* MIADDR* nibble definitions */
-#define MCI_MEMORY_DATA_BYTE0 0x000
-#define MCI_MEMORY_DATA_BYTE1 0x100
-#define MCI_MEMORY_DATA_BYTE2 0x200
-#define MCI_MEMORY_DATA_BYTE3 0x300
-#define MCI_MEMORY_ADDRESS_BYTE2 0x400
-#define MCI_MEMORY_ADDRESS_BYTE1 0x500
-#define MCI_MEMORY_ADDRESS_BYTE0 0x600
-#define MCI_REGISTER_DATA_BYTE0 0x800
-#define MCI_REGISTER_DATA_BYTE1 0x900
-#define MCI_REGISTER_DATA_BYTE2 0xA00
-#define MCI_REGISTER_DATA_BYTE3 0xB00
-#define MCI_REGISTER_ADDRESS_BYTE0 0xC00
-#define MCI_REGISTER_ADDRESS_BYTE1 0xD00
-#define MCI_REGISTER_MODE 0xE00
-
-/* Read and write modes */
-#define MCI_MODE_REGISTER_READ 0
-#define MCI_MODE_REGISTER_WRITE 1
-#define MCI_MODE_MEMORY_READ 0
-#define MCI_MODE_MEMORY_WRITE 0x40
-
-/*** Bit definitions for MC417_CTL register ****
- bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0
-+--------+-------------+--------+--------------+------------+
-|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN|
-+--------+-------------+--------+--------------+------------+
-***/
-#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030)
-#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006)
-#define MC417_UART_GPIO_EN 0x00000001
-
-/* Values for speed control */
-#define MC417_SPD_CTL_SLOW 0x1
-#define MC417_SPD_CTL_MEDIUM 0x0
-#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */
-
-/* Values for GPIO select */
-#define MC417_GPIO_SEL_GPIO3 0x3
-#define MC417_GPIO_SEL_GPIO2 0x2
-#define MC417_GPIO_SEL_GPIO1 0x1
-#define MC417_GPIO_SEL_GPIO0 0x0
-
-void cx23885_mc417_init(struct cx23885_dev *dev)
-{
- u32 regval;
-
- dprintk(2, "%s()\n", __func__);
-
- /* Configure MC417_CTL register to defaults. */
- regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) |
- MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) |
- MC417_UART_GPIO_EN;
- cx_write(MC417_CTL, regval);
-
- /* Configure MC417_OEN to defaults. */
- regval = MC417_MIRDY;
- cx_write(MC417_OEN, regval);
-
- /* Configure MC417_RWD to defaults. */
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS;
- cx_write(MC417_RWD, regval);
-}
-
-static int mc417_wait_ready(struct cx23885_dev *dev)
-{
- u32 mi_ready;
- unsigned long timeout = jiffies + msecs_to_jiffies(1);
-
- for (;;) {
- mi_ready = cx_read(MC417_RWD) & MC417_MIRDY;
- if (mi_ready != 0)
- return 0;
- if (time_after(jiffies, timeout))
- return -1;
- udelay(1);
- }
-}
-
-int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value)
-{
- u32 regval;
-
- /* Enable MC417 GPIO outputs except for MC417_MIRDY,
- * which is an input.
- */
- cx_write(MC417_OEN, MC417_MIRDY);
-
- /* Write data byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 |
- (value & 0x000000FF);
- cx_write(MC417_RWD, regval);
-
- /* Transition CS/WR to effect write transaction across bus. */
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 |
- ((value >> 8) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 |
- ((value >> 16) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 3 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 |
- ((value >> 24) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
- (address & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
- ((address >> 8) & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Indicate that this is a write. */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
- MCI_MODE_REGISTER_WRITE;
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Wait for the trans to complete (MC417_MIRDY asserted). */
- return mc417_wait_ready(dev);
-}
-
-int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value)
-{
- int retval;
- u32 regval;
- u32 tempval;
- u32 dataval;
-
- /* Enable MC417 GPIO outputs except for MC417_MIRDY,
- * which is an input.
- */
- cx_write(MC417_OEN, MC417_MIRDY);
-
- /* Write address byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 |
- ((address & 0x00FF));
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 |
- ((address >> 8) & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Indicate that this is a register read. */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE |
- MCI_MODE_REGISTER_READ;
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Wait for the trans to complete (MC417_MIRDY asserted). */
- retval = mc417_wait_ready(dev);
-
- /* switch the DAT0-7 GPIO[10:3] to input mode */
- cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
-
- /* Read data byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
- cx_write(MC417_RWD, regval);
-
- /* Transition RD to effect read transaction across bus.
- * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)?
- * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its
- * input only...)
- */
- regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0;
- cx_write(MC417_RWD, regval);
-
- /* Collect byte */
- tempval = cx_read(MC417_RWD);
- dataval = tempval & 0x000000FF;
-
- /* Bring CS and RD high. */
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= ((tempval & 0x000000FF) << 8);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= ((tempval & 0x000000FF) << 16);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 3 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= ((tempval & 0x000000FF) << 24);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- *value = dataval;
-
- return retval;
-}
-
-int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value)
-{
- u32 regval;
-
- /* Enable MC417 GPIO outputs except for MC417_MIRDY,
- * which is an input.
- */
- cx_write(MC417_OEN, MC417_MIRDY);
-
- /* Write data byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 |
- (value & 0x000000FF);
- cx_write(MC417_RWD, regval);
-
- /* Transition CS/WR to effect write transaction across bus. */
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 |
- ((value >> 8) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 |
- ((value >> 16) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write data byte 3 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 |
- ((value >> 24) & 0x000000FF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
- MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
- ((address >> 8) & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
- (address & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Wait for the trans to complete (MC417_MIRDY asserted). */
- return mc417_wait_ready(dev);
-}
-
-int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value)
-{
- int retval;
- u32 regval;
- u32 tempval;
- u32 dataval;
-
- /* Enable MC417 GPIO outputs except for MC417_MIRDY,
- * which is an input.
- */
- cx_write(MC417_OEN, MC417_MIRDY);
-
- /* Write address byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 |
- MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 |
- ((address >> 8) & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Write address byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 |
- (address & 0xFF);
- cx_write(MC417_RWD, regval);
- regval |= MC417_MICS | MC417_MIWR;
- cx_write(MC417_RWD, regval);
-
- /* Wait for the trans to complete (MC417_MIRDY asserted). */
- retval = mc417_wait_ready(dev);
-
- /* switch the DAT0-7 GPIO[10:3] to input mode */
- cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA);
-
- /* Read data byte 3 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
- cx_write(MC417_RWD, regval);
-
- /* Transition RD to effect read transaction across bus. */
- regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3;
- cx_write(MC417_RWD, regval);
-
- /* Collect byte */
- tempval = cx_read(MC417_RWD);
- dataval = ((tempval & 0x000000FF) << 24);
-
- /* Bring CS and RD high. */
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 2 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= ((tempval & 0x000000FF) << 16);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 1 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= ((tempval & 0x000000FF) << 8);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- /* Read data byte 0 */
- regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
- cx_write(MC417_RWD, regval);
- regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0;
- cx_write(MC417_RWD, regval);
- tempval = cx_read(MC417_RWD);
- dataval |= (tempval & 0x000000FF);
- regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY;
- cx_write(MC417_RWD, regval);
-
- *value = dataval;
-
- return retval;
-}
-
-void mc417_gpio_set(struct cx23885_dev *dev, u32 mask)
-{
- u32 val;
-
- /* Set the gpio value */
- mc417_register_read(dev, 0x900C, &val);
- val |= (mask & 0x000ffff);
- mc417_register_write(dev, 0x900C, val);
-}
-
-void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask)
-{
- u32 val;
-
- /* Clear the gpio value */
- mc417_register_read(dev, 0x900C, &val);
- val &= ~(mask & 0x0000ffff);
- mc417_register_write(dev, 0x900C, val);
-}
-
-void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput)
-{
- u32 val;
-
- /* Enable GPIO direction bits */
- mc417_register_read(dev, 0x9020, &val);
- if (asoutput)
- val |= (mask & 0x0000ffff);
- else
- val &= ~(mask & 0x0000ffff);
-
- mc417_register_write(dev, 0x9020, val);
-}
-/* ------------------------------------------------------------------ */
-
-/* MPEG encoder API */
-static char *cmd_to_str(int cmd)
-{
- switch (cmd) {
- case CX2341X_ENC_PING_FW:
- return "PING_FW";
- case CX2341X_ENC_START_CAPTURE:
- return "START_CAPTURE";
- case CX2341X_ENC_STOP_CAPTURE:
- return "STOP_CAPTURE";
- case CX2341X_ENC_SET_AUDIO_ID:
- return "SET_AUDIO_ID";
- case CX2341X_ENC_SET_VIDEO_ID:
- return "SET_VIDEO_ID";
- case CX2341X_ENC_SET_PCR_ID:
- return "SET_PCR_ID";
- case CX2341X_ENC_SET_FRAME_RATE:
- return "SET_FRAME_RATE";
- case CX2341X_ENC_SET_FRAME_SIZE:
- return "SET_FRAME_SIZE";
- case CX2341X_ENC_SET_BIT_RATE:
- return "SET_BIT_RATE";
- case CX2341X_ENC_SET_GOP_PROPERTIES:
- return "SET_GOP_PROPERTIES";
- case CX2341X_ENC_SET_ASPECT_RATIO:
- return "SET_ASPECT_RATIO";
- case CX2341X_ENC_SET_DNR_FILTER_MODE:
- return "SET_DNR_FILTER_MODE";
- case CX2341X_ENC_SET_DNR_FILTER_PROPS:
- return "SET_DNR_FILTER_PROPS";
- case CX2341X_ENC_SET_CORING_LEVELS:
- return "SET_CORING_LEVELS";
- case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
- return "SET_SPATIAL_FILTER_TYPE";
- case CX2341X_ENC_SET_VBI_LINE:
- return "SET_VBI_LINE";
- case CX2341X_ENC_SET_STREAM_TYPE:
- return "SET_STREAM_TYPE";
- case CX2341X_ENC_SET_OUTPUT_PORT:
- return "SET_OUTPUT_PORT";
- case CX2341X_ENC_SET_AUDIO_PROPERTIES:
- return "SET_AUDIO_PROPERTIES";
- case CX2341X_ENC_HALT_FW:
- return "HALT_FW";
- case CX2341X_ENC_GET_VERSION:
- return "GET_VERSION";
- case CX2341X_ENC_SET_GOP_CLOSURE:
- return "SET_GOP_CLOSURE";
- case CX2341X_ENC_GET_SEQ_END:
- return "GET_SEQ_END";
- case CX2341X_ENC_SET_PGM_INDEX_INFO:
- return "SET_PGM_INDEX_INFO";
- case CX2341X_ENC_SET_VBI_CONFIG:
- return "SET_VBI_CONFIG";
- case CX2341X_ENC_SET_DMA_BLOCK_SIZE:
- return "SET_DMA_BLOCK_SIZE";
- case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10:
- return "GET_PREV_DMA_INFO_MB_10";
- case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9:
- return "GET_PREV_DMA_INFO_MB_9";
- case CX2341X_ENC_SCHED_DMA_TO_HOST:
- return "SCHED_DMA_TO_HOST";
- case CX2341X_ENC_INITIALIZE_INPUT:
- return "INITIALIZE_INPUT";
- case CX2341X_ENC_SET_FRAME_DROP_RATE:
- return "SET_FRAME_DROP_RATE";
- case CX2341X_ENC_PAUSE_ENCODER:
- return "PAUSE_ENCODER";
- case CX2341X_ENC_REFRESH_INPUT:
- return "REFRESH_INPUT";
- case CX2341X_ENC_SET_COPYRIGHT:
- return "SET_COPYRIGHT";
- case CX2341X_ENC_SET_EVENT_NOTIFICATION:
- return "SET_EVENT_NOTIFICATION";
- case CX2341X_ENC_SET_NUM_VSYNC_LINES:
- return "SET_NUM_VSYNC_LINES";
- case CX2341X_ENC_SET_PLACEHOLDER:
- return "SET_PLACEHOLDER";
- case CX2341X_ENC_MUTE_VIDEO:
- return "MUTE_VIDEO";
- case CX2341X_ENC_MUTE_AUDIO:
- return "MUTE_AUDIO";
- case CX2341X_ENC_MISC:
- return "MISC";
- default:
- return "UNKNOWN";
- }
-}
-
-static int cx23885_mbox_func(void *priv,
- u32 command,
- int in,
- int out,
- u32 data[CX2341X_MBOX_MAX_DATA])
-{
- struct cx23885_dev *dev = priv;
- unsigned long timeout;
- u32 value, flag, retval = 0;
- int i;
-
- dprintk(3, "%s: command(0x%X) = %s\n", __func__, command,
- cmd_to_str(command));
-
- /* this may not be 100% safe if we can't read any memory location
- without side effects */
- mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value);
- if (value != 0x12345678) {
- printk(KERN_ERR
- "Firmware and/or mailbox pointer not initialized "
- "or corrupted, signature = 0x%x, cmd = %s\n", value,
- cmd_to_str(command));
- return -1;
- }
-
- /* This read looks at 32 bits, but flag is only 8 bits.
- * Seems we also bail if CMD or TIMEOUT bytes are set???
- */
- mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
- if (flag) {
- printk(KERN_ERR "ERROR: Mailbox appears to be in use "
- "(%x), cmd = %s\n", flag, cmd_to_str(command));
- return -1;
- }
-
- flag |= 1; /* tell 'em we're working on it */
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- /* write command + args + fill remaining with zeros */
- /* command code */
- mc417_memory_write(dev, dev->cx23417_mailbox + 1, command);
- mc417_memory_write(dev, dev->cx23417_mailbox + 3,
- IVTV_API_STD_TIMEOUT); /* timeout */
- for (i = 0; i < in; i++) {
- mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]);
- dprintk(3, "API Input %d = %d\n", i, data[i]);
- }
- for (; i < CX2341X_MBOX_MAX_DATA; i++)
- mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0);
-
- flag |= 3; /* tell 'em we're done writing */
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- /* wait for firmware to handle the API command */
- timeout = jiffies + msecs_to_jiffies(10);
- for (;;) {
- mc417_memory_read(dev, dev->cx23417_mailbox, &flag);
- if (0 != (flag & 4))
- break;
- if (time_after(jiffies, timeout)) {
- printk(KERN_ERR "ERROR: API Mailbox timeout\n");
- return -1;
- }
- udelay(10);
- }
-
- /* read output values */
- for (i = 0; i < out; i++) {
- mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i);
- dprintk(3, "API Output %d = %d\n", i, data[i]);
- }
-
- mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval);
- dprintk(3, "API result = %d\n", retval);
-
- flag = 0;
- mc417_memory_write(dev, dev->cx23417_mailbox, flag);
-
- return retval;
-}
-
-/* We don't need to call the API often, so using just one
- * mailbox will probably suffice
- */
-static int cx23885_api_cmd(struct cx23885_dev *dev,
- u32 command,
- u32 inputcnt,
- u32 outputcnt,
- ...)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- va_list vargs;
- int i, err;
-
- dprintk(3, "%s() cmds = 0x%08x\n", __func__, command);
-
- va_start(vargs, outputcnt);
- for (i = 0; i < inputcnt; i++)
- data[i] = va_arg(vargs, int);
-
- err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data);
- for (i = 0; i < outputcnt; i++) {
- int *vptr = va_arg(vargs, int *);
- *vptr = data[i];
- }
- va_end(vargs);
-
- return err;
-}
-
-static int cx23885_find_mailbox(struct cx23885_dev *dev)
-{
- u32 signature[4] = {
- 0x12345678, 0x34567812, 0x56781234, 0x78123456
- };
- int signaturecnt = 0;
- u32 value;
- int i;
-
- dprintk(2, "%s()\n", __func__);
-
- for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) {
- mc417_memory_read(dev, i, &value);
- if (value == signature[signaturecnt])
- signaturecnt++;
- else
- signaturecnt = 0;
- if (4 == signaturecnt) {
- dprintk(1, "Mailbox signature found at 0x%x\n", i+1);
- return i+1;
- }
- }
- printk(KERN_ERR "Mailbox signature values not found!\n");
- return -1;
-}
-
-static int cx23885_load_firmware(struct cx23885_dev *dev)
-{
- static const unsigned char magic[8] = {
- 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
- };
- const struct firmware *firmware;
- int i, retval = 0;
- u32 value = 0;
- u32 gpio_output = 0;
- u32 gpio_value;
- u32 checksum = 0;
- u32 *dataptr;
-
- dprintk(2, "%s()\n", __func__);
-
- /* Save GPIO settings before reset of APU */
- retval |= mc417_memory_read(dev, 0x9020, &gpio_output);
- retval |= mc417_memory_read(dev, 0x900C, &gpio_value);
-
- retval = mc417_register_write(dev,
- IVTV_REG_VPU, 0xFFFFFFED);
- retval |= mc417_register_write(dev,
- IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
- retval |= mc417_register_write(dev,
- IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800);
- retval |= mc417_register_write(dev,
- IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
- retval |= mc417_register_write(dev,
- IVTV_REG_APU, 0);
-
- if (retval != 0) {
- printk(KERN_ERR "%s: Error with mc417_register_write\n",
- __func__);
- return -1;
- }
-
- retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME,
- &dev->pci->dev);
-
- if (retval != 0) {
- printk(KERN_ERR
- "ERROR: Hotplug firmware request failed (%s).\n",
- CX23885_FIRM_IMAGE_NAME);
- printk(KERN_ERR "Please fix your hotplug setup, the board will "
- "not work without firmware loaded!\n");
- return -1;
- }
-
- if (firmware->size != CX23885_FIRM_IMAGE_SIZE) {
- printk(KERN_ERR "ERROR: Firmware size mismatch "
- "(have %zd, expected %d)\n",
- firmware->size, CX23885_FIRM_IMAGE_SIZE);
- release_firmware(firmware);
- return -1;
- }
-
- if (0 != memcmp(firmware->data, magic, 8)) {
- printk(KERN_ERR
- "ERROR: Firmware magic mismatch, wrong file?\n");
- release_firmware(firmware);
- return -1;
- }
-
- /* transfer to the chip */
- dprintk(2, "Loading firmware ...\n");
- dataptr = (u32 *)firmware->data;
- for (i = 0; i < (firmware->size >> 2); i++) {
- value = *dataptr;
- checksum += ~value;
- if (mc417_memory_write(dev, i, value) != 0) {
- printk(KERN_ERR "ERROR: Loading firmware failed!\n");
- release_firmware(firmware);
- return -1;
- }
- dataptr++;
- }
-
- /* read back to verify with the checksum */
- dprintk(1, "Verifying firmware ...\n");
- for (i--; i >= 0; i--) {
- if (mc417_memory_read(dev, i, &value) != 0) {
- printk(KERN_ERR "ERROR: Reading firmware failed!\n");
- release_firmware(firmware);
- return -1;
- }
- checksum -= ~value;
- }
- if (checksum) {
- printk(KERN_ERR
- "ERROR: Firmware load failed (checksum mismatch).\n");
- release_firmware(firmware);
- return -1;
- }
- release_firmware(firmware);
- dprintk(1, "Firmware upload successful.\n");
-
- retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS,
- IVTV_CMD_HW_BLOCKS_RST);
-
- /* F/W power up disturbs the GPIOs, restore state */
- retval |= mc417_register_write(dev, 0x9020, gpio_output);
- retval |= mc417_register_write(dev, 0x900C, gpio_value);
-
- retval |= mc417_register_read(dev, IVTV_REG_VPU, &value);
- retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8);
-
- /* Hardcoded GPIO's here */
- retval |= mc417_register_write(dev, 0x9020, 0x4000);
- retval |= mc417_register_write(dev, 0x900C, 0x4000);
-
- mc417_register_read(dev, 0x9020, &gpio_output);
- mc417_register_read(dev, 0x900C, &gpio_value);
-
- if (retval < 0)
- printk(KERN_ERR "%s: Error with mc417_register_write\n",
- __func__);
- return 0;
-}
-
-void cx23885_417_check_encoder(struct cx23885_dev *dev)
-{
- u32 status, seq;
-
- status = seq = 0;
- cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq);
- dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq);
-}
-
-static void cx23885_codec_settings(struct cx23885_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- /* Dynamically change the height based on video standard */
- if (dev->encodernorm.id & V4L2_STD_525_60)
- dev->ts1.height = 480;
- else
- dev->ts1.height = 576;
-
- /* assign frame size */
- cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
- dev->ts1.height, dev->ts1.width);
-
- dev->mpeg_params.width = dev->ts1.width;
- dev->mpeg_params.height = dev->ts1.height;
- dev->mpeg_params.is_50hz =
- (dev->encodernorm.id & V4L2_STD_625_50) != 0;
-
- cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params);
-
- cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1);
- cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1);
-}
-
-static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder)
-{
- int version;
- int retval;
- u32 i, data[7];
-
- dprintk(1, "%s()\n", __func__);
-
- retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
- if (retval < 0) {
- dprintk(2, "%s() PING OK\n", __func__);
- retval = cx23885_load_firmware(dev);
- if (retval < 0) {
- printk(KERN_ERR "%s() f/w load failed\n", __func__);
- return retval;
- }
- retval = cx23885_find_mailbox(dev);
- if (retval < 0) {
- printk(KERN_ERR "%s() mailbox < 0, error\n",
- __func__);
- return -1;
- }
- dev->cx23417_mailbox = retval;
- retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0);
- if (retval < 0) {
- printk(KERN_ERR
- "ERROR: cx23417 firmware ping failed!\n");
- return -1;
- }
- retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1,
- &version);
- if (retval < 0) {
- printk(KERN_ERR "ERROR: cx23417 firmware get encoder :"
- "version failed!\n");
- return -1;
- }
- dprintk(1, "cx23417 firmware version is 0x%08x\n", version);
- msleep(200);
- }
-
- cx23885_codec_settings(dev);
- msleep(60);
-
- cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
- CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115);
- cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
- CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0);
-
- /* Setup to capture VBI */
- data[0] = 0x0001BD00;
- data[1] = 1; /* frames per interrupt */
- data[2] = 4; /* total bufs */
- data[3] = 0x91559155; /* start codes */
- data[4] = 0x206080C0; /* stop codes */
- data[5] = 6; /* lines */
- data[6] = 64; /* BPL */
-
- cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
- data[2], data[3], data[4], data[5], data[6]);
-
- for (i = 2; i <= 24; i++) {
- int valid;
-
- valid = ((i >= 19) && (i <= 21));
- cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i,
- valid, 0 , 0, 0);
- cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
- i | 0x80000000, valid, 0, 0, 0);
- }
-
- cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE);
- msleep(60);
-
- /* initialize the video input */
- cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
- msleep(60);
-
- /* Enable VIP style pixel invalidation so we work with scaled mode */
- mc417_memory_write(dev, 2120, 0x00000080);
-
- /* start capturing to the host interface */
- if (startencoder) {
- cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
- CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE);
- msleep(10);
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int bb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx23885_fh *fh = q->priv_data;
-
- fh->dev->ts1.ts_packet_size = mpeglinesize;
- fh->dev->ts1.ts_packet_count = mpeglines;
-
- *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count;
- *count = mpegbufs;
-
- return 0;
-}
-
-static int bb_buf_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct cx23885_fh *fh = q->priv_data;
- return cx23885_buf_prepare(q, &fh->dev->ts1,
- (struct cx23885_buffer *)vb,
- field);
-}
-
-static void bb_buf_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx23885_fh *fh = q->priv_data;
- cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb);
-}
-
-static void bb_buf_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
-}
-
-static struct videobuf_queue_ops cx23885_qops = {
- .buf_setup = bb_buf_setup,
- .buf_prepare = bb_buf_prepare,
- .buf_queue = bb_buf_queue,
- .buf_release = bb_buf_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-static const u32 *ctrl_classes[] = {
- cx2341x_mpeg_ctrls,
- NULL
-};
-
-static int cx23885_queryctrl(struct cx23885_dev *dev,
- struct v4l2_queryctrl *qctrl)
-{
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (qctrl->id == 0)
- return -EINVAL;
-
- /* MPEG V4L2 controls */
- if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl))
- qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
-
- return 0;
-}
-
-static int cx23885_querymenu(struct cx23885_dev *dev,
- struct v4l2_querymenu *qmenu)
-{
- struct v4l2_queryctrl qctrl;
-
- qctrl.id = qmenu->id;
- cx23885_queryctrl(dev, &qctrl);
- return v4l2_ctrl_query_menu(qmenu, &qctrl,
- cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id));
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- call_all(dev, core, g_std, id);
-
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++)
- if (*id & cx23885_tvnorms[i].id)
- break;
- if (i == ARRAY_SIZE(cx23885_tvnorms))
- return -EINVAL;
- dev->encodernorm = cx23885_tvnorms[i];
-
- /* Have the drier core notify the subdevices */
- mutex_lock(&dev->lock);
- cx23885_set_tvnorm(dev, *id);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- dprintk(1, "%s()\n", __func__);
- return cx23885_enum_input(dev, i);
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- return cx23885_get_input(file, priv, i);
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- return cx23885_set_input(file, priv, i);
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- if (UNSET == dev->tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- strcpy(t->name, "Television");
- call_all(dev, tuner, g_tuner, t);
-
- dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- if (UNSET == dev->tuner_type)
- return -EINVAL;
-
- /* Update the A/V core */
- call_all(dev, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- if (UNSET == dev->tuner_type)
- return -EINVAL;
- f->type = V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->freq;
-
- call_all(dev, tuner, g_frequency, f);
-
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- return cx23885_set_frequency(file, priv, f);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- return cx23885_get_control(dev, ctl);
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- return cx23885_set_control(dev, ctl);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
- struct cx23885_tsport *tsport = &dev->ts1;
-
- strlcpy(cap->driver, dev->name, sizeof(cap->driver));
- strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- 0;
- if (UNSET != dev->tuner_type)
- cap->capabilities |= V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (f->index != 0)
- return -EINVAL;
-
- strlcpy(f->description, "MPEG", sizeof(f->description));
- f->pixelformat = V4L2_PIX_FMT_MPEG;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage =
- dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
- f->fmt.pix.colorspace = 0;
- f->fmt.pix.width = dev->ts1.width;
- f->fmt.pix.height = dev->ts1.height;
- f->fmt.pix.field = fh->mpegq.field;
- dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
- dev->ts1.width, dev->ts1.height, fh->mpegq.field);
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage =
- dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
- f->fmt.pix.colorspace = 0;
- dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
- dev->ts1.width, dev->ts1.height, fh->mpegq.field);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage =
- dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
- f->fmt.pix.colorspace = 0;
- dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
- f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_reqbufs(&fh->mpegq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_querybuf(&fh->mpegq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_qbuf(&fh->mpegq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct cx23885_fh *fh = priv;
-
- return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK);
-}
-
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_streamon(&fh->mpegq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_streamoff(&fh->mpegq);
-}
-
-static int vidioc_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
- return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS);
-}
-
-static int vidioc_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- struct cx2341x_mpeg_params p;
- int err;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- p = dev->mpeg_params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS);
-
- if (err == 0) {
- err = cx2341x_update(dev, cx23885_mbox_func,
- &dev->mpeg_params, &p);
- dev->mpeg_params = p;
- }
- return err;
-}
-
-static int vidioc_try_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- struct cx2341x_mpeg_params p;
- int err;
-
- if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG)
- return -EINVAL;
-
- p = dev->mpeg_params;
- err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS);
- return err;
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- char name[32 + 2];
-
- snprintf(name, sizeof(name), "%s/2", dev->name);
- printk(KERN_INFO
- "%s/2: ============ START LOG STATUS ============\n",
- dev->name);
- call_all(dev, core, log_status);
- cx2341x_log_status(&dev->mpeg_params, name);
- printk(KERN_INFO
- "%s/2: ============= END LOG STATUS =============\n",
- dev->name);
- return 0;
-}
-
-static int vidioc_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *a)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
-
- return cx23885_querymenu(dev, a);
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
-
- return cx23885_queryctrl(dev, c);
-}
-
-static int mpeg_open(struct file *file)
-{
- struct cx23885_dev *dev = video_drvdata(file);
- struct cx23885_fh *fh;
-
- dprintk(2, "%s()\n", __func__);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (!fh)
- return -ENOMEM;
-
- file->private_data = fh;
- fh->dev = dev;
-
- videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops,
- &dev->pci->dev, &dev->ts1.slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx23885_buffer),
- fh, NULL);
- return 0;
-}
-
-static int mpeg_release(struct file *file)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- dprintk(2, "%s()\n", __func__);
-
- /* FIXME: Review this crap */
- /* Shut device down on last close */
- if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
- if (atomic_dec_return(&dev->v4l_reader_count) == 0) {
- /* stop mpeg capture */
- cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- CX23885_END_NOW, CX23885_MPEG_CAPTURE,
- CX23885_RAW_BITS_NONE);
-
- msleep(500);
- cx23885_417_check_encoder(dev);
-
- cx23885_cancel_buffers(&fh->dev->ts1);
- }
- }
-
- if (fh->mpegq.streaming)
- videobuf_streamoff(&fh->mpegq);
- if (fh->mpegq.reading)
- videobuf_read_stop(&fh->mpegq);
-
- videobuf_mmap_free(&fh->mpegq);
- file->private_data = NULL;
- kfree(fh);
-
- return 0;
-}
-
-static ssize_t mpeg_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- dprintk(2, "%s()\n", __func__);
-
- /* Deal w/ A/V decoder * and mpeg encoder sync issues. */
- /* Start mpeg encoder on first read. */
- if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
- if (atomic_inc_return(&dev->v4l_reader_count) == 1) {
- if (cx23885_initialize_codec(dev, 1) < 0)
- return -EINVAL;
- }
- }
-
- return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int mpeg_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- dprintk(2, "%s\n", __func__);
-
- return videobuf_poll_stream(file, &fh->mpegq, wait);
-}
-
-static int mpeg_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- dprintk(2, "%s()\n", __func__);
-
- return videobuf_mmap_mapper(&fh->mpegq, vma);
-}
-
-static struct v4l2_file_operations mpeg_fops = {
- .owner = THIS_MODULE,
- .open = mpeg_open,
- .release = mpeg_release,
- .read = mpeg_read,
- .poll = mpeg_poll,
- .mmap = mpeg_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
- .vidioc_querystd = vidioc_g_std,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
- .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_querymenu = vidioc_querymenu,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_chip_ident = cx23885_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cx23885_g_register,
- .vidioc_s_register = cx23885_s_register,
-#endif
-};
-
-static struct video_device cx23885_mpeg_template = {
- .name = "cx23885",
- .fops = &mpeg_fops,
- .ioctl_ops = &mpeg_ioctl_ops,
- .tvnorms = CX23885_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-void cx23885_417_unregister(struct cx23885_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- if (dev->v4l_device) {
- if (video_is_registered(dev->v4l_device))
- video_unregister_device(dev->v4l_device);
- else
- video_device_release(dev->v4l_device);
- dev->v4l_device = NULL;
- }
-}
-
-static struct video_device *cx23885_video_dev_alloc(
- struct cx23885_tsport *tsport,
- struct pci_dev *pci,
- struct video_device *template,
- char *type)
-{
- struct video_device *vfd;
- struct cx23885_dev *dev = tsport->dev;
-
- dprintk(1, "%s()\n", __func__);
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template;
- snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
- cx23885_boards[tsport->dev->board].name, type);
- vfd->parent = &pci->dev;
- vfd->release = video_device_release;
- return vfd;
-}
-
-int cx23885_417_register(struct cx23885_dev *dev)
-{
- /* FIXME: Port1 hardcoded here */
- int err = -ENODEV;
- struct cx23885_tsport *tsport = &dev->ts1;
-
- dprintk(1, "%s()\n", __func__);
-
- if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER)
- return err;
-
- /* Set default TV standard */
- dev->encodernorm = cx23885_tvnorms[0];
-
- if (dev->encodernorm.id & V4L2_STD_525_60)
- tsport->height = 480;
- else
- tsport->height = 576;
-
- tsport->width = 720;
- cx2341x_fill_defaults(&dev->mpeg_params);
-
- dev->mpeg_params.port = CX2341X_PORT_SERIAL;
-
- /* Allocate and initialize V4L video device */
- dev->v4l_device = cx23885_video_dev_alloc(tsport,
- dev->pci, &cx23885_mpeg_template, "mpeg");
- video_set_drvdata(dev->v4l_device, dev);
- err = video_register_device(dev->v4l_device,
- VFL_TYPE_GRABBER, -1);
- if (err < 0) {
- printk(KERN_INFO "%s: can't register mpeg device\n", dev->name);
- return err;
- }
-
- printk(KERN_INFO "%s: registered device %s [mpeg]\n",
- dev->name, video_device_node_name(dev->v4l_device));
-
- /* ST: Configure the encoder paramaters, but don't begin
- * encoding, this resolves an issue where the first time the
- * encoder is started video can be choppy.
- */
- cx23885_initialize_codec(dev, 0);
-
- return 0;
-}
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
deleted file mode 100644
index 080e11157e5f..000000000000
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ /dev/null
@@ -1,1680 +0,0 @@
-/*
- * Driver for the Conexant CX23885 PCIe bridge
- *
- * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <media/cx25840.h>
-#include <linux/firmware.h>
-#include <misc/altera.h>
-
-#include "cx23885.h"
-#include "tuner-xc2028.h"
-#include "netup-eeprom.h"
-#include "netup-init.h"
-#include "altera-ci.h"
-#include "xc4000.h"
-#include "xc5000.h"
-#include "cx23888-ir.h"
-
-static unsigned int netup_card_rev = 1;
-module_param(netup_card_rev, int, 0644);
-MODULE_PARM_DESC(netup_card_rev,
- "NetUP Dual DVB-T/C CI card revision");
-static unsigned int enable_885_ir;
-module_param(enable_885_ir, int, 0644);
-MODULE_PARM_DESC(enable_885_ir,
- "Enable integrated IR controller for supported\n"
- "\t\t CX2388[57] boards that are wired for it:\n"
- "\t\t\tHVR-1250 (reported safe)\n"
- "\t\t\tTeVii S470 (reported unsafe)\n"
- "\t\t This can cause an interrupt storm with some cards.\n"
- "\t\t Default: 0 [Disabled]");
-
-/* ------------------------------------------------------------------ */
-/* board config info */
-
-struct cx23885_board cx23885_boards[] = {
- [CX23885_BOARD_UNKNOWN] = {
- .name = "UNKNOWN/GENERIC",
- /* Ensure safe default for unknown boards */
- .clk_freq = 0,
- .input = {{
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE2,
- .vmux = 1,
- }, {
- .type = CX23885_VMUX_COMPOSITE3,
- .vmux = 2,
- }, {
- .type = CX23885_VMUX_COMPOSITE4,
- .vmux = 3,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1800lp] = {
- .name = "Hauppauge WinTV-HVR1800lp",
- .portc = CX23885_MPEG_DVB,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00,
- }, {
- .type = CX23885_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xff02,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1800] = {
- .name = "Hauppauge WinTV-HVR1800",
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_ENCODER,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_PHILIPS_TDA8290,
- .tuner_addr = 0x42, /* 0x84 >> 1 */
- .tuner_bus = 1,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1,
- .amux = CX25840_AUDIO8,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN6_CH1,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1250] = {
- .name = "Hauppauge WinTV-HVR1250",
- .porta = CX23885_ANALOG_VIDEO,
- .portc = CX23885_MPEG_DVB,
-#ifdef MT2131_NO_ANALOG_SUPPORT_YET
- .tuner_type = TUNER_PHILIPS_TDA8290,
- .tuner_addr = 0x42, /* 0x84 >> 1 */
- .tuner_bus = 1,
-#endif
- .force_bff = 1,
- .input = {{
-#ifdef MT2131_NO_ANALOG_SUPPORT_YET
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1,
- .amux = CX25840_AUDIO8,
- .gpio0 = 0xff00,
- }, {
-#endif
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN6_CH1,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0xff02,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0xff02,
- } },
- },
- [CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP] = {
- .name = "DViCO FusionHDTV5 Express",
- .portb = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = {
- .name = "Hauppauge WinTV-HVR1500Q",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1500] = {
- .name = "Hauppauge WinTV-HVR1500",
- .porta = CX23885_ANALOG_VIDEO,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61, /* 0xc2 >> 1 */
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN6_CH1,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .gpio0 = 0,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1200] = {
- .name = "Hauppauge WinTV-HVR1200",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1700] = {
- .name = "Hauppauge WinTV-HVR1700",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1400] = {
- .name = "Hauppauge WinTV-HVR1400",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
- .name = "DViCO FusionHDTV7 Dual Express",
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = {
- .name = "DViCO FusionHDTV DVB-T Dual Express",
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = {
- .name = "Leadtek Winfast PxDVR3200 H",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = {
- .name = "Leadtek Winfast PxDVR3200 H XC4000",
- .porta = CX23885_ANALOG_VIDEO,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_XC4000,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN2_CH1 |
- CX25840_VIN5_CH2 |
- CX25840_NONE0_CH3,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE1,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- }, {
- .type = CX23885_VMUX_COMPONENT,
- .vmux = CX25840_VIN7_CH1 |
- CX25840_VIN6_CH2 |
- CX25840_VIN8_CH3 |
- CX25840_COMPONENT_ON,
- } },
- },
- [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
- .name = "Compro VideoMate E650F",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_TBS_6920] = {
- .name = "TurboSight TBS 6920",
- .portb = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_TEVII_S470] = {
- .name = "TeVii S470",
- .portb = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_DVBWORLD_2005] = {
- .name = "DVBWorld DVB-S2 2005",
- .portb = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
- .ci_type = 1,
- .name = "NetUP Dual DVB-S2 CI",
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1270] = {
- .name = "Hauppauge WinTV-HVR1270",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1275] = {
- .name = "Hauppauge WinTV-HVR1275",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1255] = {
- .name = "Hauppauge WinTV-HVR1255",
- .porta = CX23885_ANALOG_VIDEO,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_ABSENT,
- .tuner_addr = 0x42, /* 0x84 >> 1 */
- .force_bff = 1,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1 |
- CX25840_DIF_ON,
- .amux = CX25840_AUDIO8,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN6_CH1,
- .amux = CX25840_AUDIO7,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .amux = CX25840_AUDIO7,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1255_22111] = {
- .name = "Hauppauge WinTV-HVR1255",
- .porta = CX23885_ANALOG_VIDEO,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_ABSENT,
- .tuner_addr = 0x42, /* 0x84 >> 1 */
- .force_bff = 1,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1 |
- CX25840_DIF_ON,
- .amux = CX25840_AUDIO8,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .amux = CX25840_AUDIO7,
- } },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1210] = {
- .name = "Hauppauge WinTV-HVR1210",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_MYGICA_X8506] = {
- .name = "Mygica X8506 DMB-TH",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x61,
- .tuner_bus = 1,
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_DVB,
- .input = {
- {
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_COMPOSITE2,
- },
- {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE8,
- },
- {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- },
- {
- .type = CX23885_VMUX_COMPONENT,
- .vmux = CX25840_COMPONENT_ON |
- CX25840_VIN1_CH1 |
- CX25840_VIN6_CH2 |
- CX25840_VIN7_CH3,
- },
- },
- },
- [CX23885_BOARD_MAGICPRO_PROHDTVE2] = {
- .name = "Magic-Pro ProHDTV Extreme 2",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x61,
- .tuner_bus = 1,
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_DVB,
- .input = {
- {
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_COMPOSITE2,
- },
- {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE8,
- },
- {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- },
- {
- .type = CX23885_VMUX_COMPONENT,
- .vmux = CX25840_COMPONENT_ON |
- CX25840_VIN1_CH1 |
- CX25840_VIN6_CH2 |
- CX25840_VIN7_CH3,
- },
- },
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1850] = {
- .name = "Hauppauge WinTV-HVR1850",
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_ENCODER,
- .portc = CX23885_MPEG_DVB,
- .tuner_type = TUNER_ABSENT,
- .tuner_addr = 0x42, /* 0x84 >> 1 */
- .force_bff = 1,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN5_CH2 |
- CX25840_VIN2_CH1 |
- CX25840_DIF_ON,
- .amux = CX25840_AUDIO8,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN6_CH1,
- .amux = CX25840_AUDIO7,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_VIN7_CH3 |
- CX25840_VIN4_CH2 |
- CX25840_VIN8_CH1 |
- CX25840_SVIDEO_ON,
- .amux = CX25840_AUDIO7,
- } },
- },
- [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = {
- .name = "Compro VideoMate E800",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_HAUPPAUGE_HVR1290] = {
- .name = "Hauppauge WinTV-HVR1290",
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_MYGICA_X8558PRO] = {
- .name = "Mygica X8558 PRO DMB-TH",
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = {
- .name = "LEADTEK WinFast PxTV1200",
- .porta = CX23885_ANALOG_VIDEO,
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .tuner_bus = 1,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN2_CH1 |
- CX25840_VIN5_CH2 |
- CX25840_NONE0_CH3,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE1,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- }, {
- .type = CX23885_VMUX_COMPONENT,
- .vmux = CX25840_VIN7_CH1 |
- CX25840_VIN6_CH2 |
- CX25840_VIN8_CH3 |
- CX25840_COMPONENT_ON,
- } },
- },
- [CX23885_BOARD_GOTVIEW_X5_3D_HYBRID] = {
- .name = "GoTView X5 3D Hybrid",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x64,
- .tuner_bus = 1,
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_DVB,
- .input = {{
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_VIN2_CH1 |
- CX25840_VIN5_CH2,
- .gpio0 = 0x02,
- }, {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX23885_VMUX_COMPOSITE1,
- }, {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- } },
- },
- [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = {
- .ci_type = 2,
- .name = "NetUP Dual DVB-T/C-CI RF",
- .porta = CX23885_ANALOG_VIDEO,
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- .num_fds_portb = 2,
- .num_fds_portc = 2,
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x64,
- .input = { {
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_COMPOSITE1,
- } },
- },
- [CX23885_BOARD_MPX885] = {
- .name = "MPX-885",
- .porta = CX23885_ANALOG_VIDEO,
- .input = {{
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE1,
- .amux = CX25840_AUDIO6,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE2,
- .vmux = CX25840_COMPOSITE2,
- .amux = CX25840_AUDIO6,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE3,
- .vmux = CX25840_COMPOSITE3,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0,
- }, {
- .type = CX23885_VMUX_COMPOSITE4,
- .vmux = CX25840_COMPOSITE4,
- .amux = CX25840_AUDIO7,
- .gpio0 = 0,
- } },
- },
- [CX23885_BOARD_MYGICA_X8507] = {
- .name = "Mygica X8507",
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x61,
- .tuner_bus = 1,
- .porta = CX23885_ANALOG_VIDEO,
- .input = {
- {
- .type = CX23885_VMUX_TELEVISION,
- .vmux = CX25840_COMPOSITE2,
- .amux = CX25840_AUDIO8,
- },
- {
- .type = CX23885_VMUX_COMPOSITE1,
- .vmux = CX25840_COMPOSITE8,
- },
- {
- .type = CX23885_VMUX_SVIDEO,
- .vmux = CX25840_SVIDEO_LUMA3 |
- CX25840_SVIDEO_CHROMA4,
- },
- {
- .type = CX23885_VMUX_COMPONENT,
- .vmux = CX25840_COMPONENT_ON |
- CX25840_VIN1_CH1 |
- CX25840_VIN6_CH2 |
- CX25840_VIN7_CH3,
- },
- },
- },
- [CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL] = {
- .name = "TerraTec Cinergy T PCIe Dual",
- .portb = CX23885_MPEG_DVB,
- .portc = CX23885_MPEG_DVB,
- },
- [CX23885_BOARD_TEVII_S471] = {
- .name = "TeVii S471",
- .portb = CX23885_MPEG_DVB,
- }
-};
-const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
-
-/* ------------------------------------------------------------------ */
-/* PCI subsystem IDs */
-
-struct cx23885_subid cx23885_subids[] = {
- {
- .subvendor = 0x0070,
- .subdevice = 0x3400,
- .card = CX23885_BOARD_UNKNOWN,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7600,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1800lp,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7800,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7801,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7809,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1800,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7911,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1250,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xd500,
- .card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7790,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7797,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7710,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x7717,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1500,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x71d1,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x71d3,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1200,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x8101,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1700,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x8010,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1400,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xd618,
- .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xdb78,
- .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6681,
- .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6f39,
- .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000,
- }, {
- .subvendor = 0x185b,
- .subdevice = 0xe800,
- .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
- }, {
- .subvendor = 0x6920,
- .subdevice = 0x8888,
- .card = CX23885_BOARD_TBS_6920,
- }, {
- .subvendor = 0xd470,
- .subdevice = 0x9022,
- .card = CX23885_BOARD_TEVII_S470,
- }, {
- .subvendor = 0x0001,
- .subdevice = 0x2005,
- .card = CX23885_BOARD_DVBWORLD_2005,
- }, {
- .subvendor = 0x1b55,
- .subdevice = 0x2a2c,
- .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2211,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1270,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2215,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x221d,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2251,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2259,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1255_22111,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2291,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2295,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x2299,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x229d,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f0,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f1,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f2,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f3,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f4,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x22f5,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0x8651,
- .card = CX23885_BOARD_MYGICA_X8506,
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0x8657,
- .card = CX23885_BOARD_MAGICPRO_PROHDTVE2,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x8541,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1850,
- }, {
- .subvendor = 0x1858,
- .subdevice = 0xe800,
- .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x8551,
- .card = CX23885_BOARD_HAUPPAUGE_HVR1290,
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0x8578,
- .card = CX23885_BOARD_MYGICA_X8558PRO,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6f22,
- .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200,
- }, {
- .subvendor = 0x5654,
- .subdevice = 0x2390,
- .card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID,
- }, {
- .subvendor = 0x1b55,
- .subdevice = 0xe2e4,
- .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF,
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0x8502,
- .card = CX23885_BOARD_MYGICA_X8507,
- }, {
- .subvendor = 0x153b,
- .subdevice = 0x117e,
- .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
- }, {
- .subvendor = 0xd471,
- .subdevice = 0x9022,
- .card = CX23885_BOARD_TEVII_S471,
- },
-};
-const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
-
-void cx23885_card_list(struct cx23885_dev *dev)
-{
- int i;
-
- if (0 == dev->pci->subsystem_vendor &&
- 0 == dev->pci->subsystem_device) {
- printk(KERN_INFO
- "%s: Board has no valid PCIe Subsystem ID and can't\n"
- "%s: be autodetected. Pass card=<n> insmod option\n"
- "%s: to workaround that. Redirect complaints to the\n"
- "%s: vendor of the TV card. Best regards,\n"
- "%s: -- tux\n",
- dev->name, dev->name, dev->name, dev->name, dev->name);
- } else {
- printk(KERN_INFO
- "%s: Your board isn't known (yet) to the driver.\n"
- "%s: Try to pick one of the existing card configs via\n"
- "%s: card=<n> insmod option. Updating to the latest\n"
- "%s: version might help as well.\n",
- dev->name, dev->name, dev->name, dev->name);
- }
- printk(KERN_INFO "%s: Here is a list of valid choices for the card=<n> insmod option:\n",
- dev->name);
- for (i = 0; i < cx23885_bcount; i++)
- printk(KERN_INFO "%s: card=%d -> %s\n",
- dev->name, i, cx23885_boards[i].name);
-}
-
-static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
-{
- struct tveeprom tv;
-
- tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv,
- eeprom_data);
-
- /* Make sure we support the board model */
- switch (tv.model) {
- case 22001:
- /* WinTV-HVR1270 (PCIe, Retail, half height)
- * ATSC/QAM and basic analog, IR Blast */
- case 22009:
- /* WinTV-HVR1210 (PCIe, Retail, half height)
- * DVB-T and basic analog, IR Blast */
- case 22011:
- /* WinTV-HVR1270 (PCIe, Retail, half height)
- * ATSC/QAM and basic analog, IR Recv */
- case 22019:
- /* WinTV-HVR1210 (PCIe, Retail, half height)
- * DVB-T and basic analog, IR Recv */
- case 22021:
- /* WinTV-HVR1275 (PCIe, Retail, half height)
- * ATSC/QAM and basic analog, IR Recv */
- case 22029:
- /* WinTV-HVR1210 (PCIe, Retail, half height)
- * DVB-T and basic analog, IR Recv */
- case 22101:
- /* WinTV-HVR1270 (PCIe, Retail, full height)
- * ATSC/QAM and basic analog, IR Blast */
- case 22109:
- /* WinTV-HVR1210 (PCIe, Retail, full height)
- * DVB-T and basic analog, IR Blast */
- case 22111:
- /* WinTV-HVR1270 (PCIe, Retail, full height)
- * ATSC/QAM and basic analog, IR Recv */
- case 22119:
- /* WinTV-HVR1210 (PCIe, Retail, full height)
- * DVB-T and basic analog, IR Recv */
- case 22121:
- /* WinTV-HVR1275 (PCIe, Retail, full height)
- * ATSC/QAM and basic analog, IR Recv */
- case 22129:
- /* WinTV-HVR1210 (PCIe, Retail, full height)
- * DVB-T and basic analog, IR Recv */
- case 71009:
- /* WinTV-HVR1200 (PCIe, Retail, full height)
- * DVB-T and basic analog */
- case 71359:
- /* WinTV-HVR1200 (PCIe, OEM, half height)
- * DVB-T and basic analog */
- case 71439:
- /* WinTV-HVR1200 (PCIe, OEM, half height)
- * DVB-T and basic analog */
- case 71449:
- /* WinTV-HVR1200 (PCIe, OEM, full height)
- * DVB-T and basic analog */
- case 71939:
- /* WinTV-HVR1200 (PCIe, OEM, half height)
- * DVB-T and basic analog */
- case 71949:
- /* WinTV-HVR1200 (PCIe, OEM, full height)
- * DVB-T and basic analog */
- case 71959:
- /* WinTV-HVR1200 (PCIe, OEM, full height)
- * DVB-T and basic analog */
- case 71979:
- /* WinTV-HVR1200 (PCIe, OEM, half height)
- * DVB-T and basic analog */
- case 71999:
- /* WinTV-HVR1200 (PCIe, OEM, full height)
- * DVB-T and basic analog */
- case 76601:
- /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual
- channel ATSC and MPEG2 HW Encoder */
- case 77001:
- /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC
- and Basic analog */
- case 77011:
- /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC
- and Basic analog */
- case 77041:
- /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM
- and Basic analog */
- case 77051:
- /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM
- and Basic analog */
- case 78011:
- /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- case 78501:
- /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- case 78521:
- /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- case 78531:
- /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- case 78631:
- /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- case 79001:
- /* WinTV-HVR1250 (PCIe, Retail, IR, full height,
- ATSC and Basic analog */
- case 79101:
- /* WinTV-HVR1250 (PCIe, Retail, IR, half height,
- ATSC and Basic analog */
- case 79501:
- /* WinTV-HVR1250 (PCIe, No IR, half height,
- ATSC [at least] and Basic analog) */
- case 79561:
- /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
- ATSC and Basic analog */
- case 79571:
- /* WinTV-HVR1250 (PCIe, OEM, No IR, full height,
- ATSC and Basic analog */
- case 79671:
- /* WinTV-HVR1250 (PCIe, OEM, No IR, half height,
- ATSC and Basic analog */
- case 80019:
- /* WinTV-HVR1400 (Express Card, Retail, IR,
- * DVB-T and Basic analog */
- case 81509:
- /* WinTV-HVR1700 (PCIe, OEM, No IR, half height)
- * DVB-T and MPEG2 HW Encoder */
- case 81519:
- /* WinTV-HVR1700 (PCIe, OEM, No IR, full height)
- * DVB-T and MPEG2 HW Encoder */
- break;
- case 85021:
- /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM,
- Dual channel ATSC and MPEG2 HW Encoder */
- break;
- case 85721:
- /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR,
- Dual channel ATSC and Basic analog */
- break;
- default:
- printk(KERN_WARNING "%s: warning: "
- "unknown hauppauge model #%d\n",
- dev->name, tv.model);
- break;
- }
-
- printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
- dev->name, tv.model);
-}
-
-int cx23885_tuner_callback(void *priv, int component, int command, int arg)
-{
- struct cx23885_tsport *port = priv;
- struct cx23885_dev *dev = port->dev;
- u32 bitmask = 0;
-
- if ((command == XC2028_RESET_CLK) || (command == XC2028_I2C_FLUSH))
- return 0;
-
- if (command != 0) {
- printk(KERN_ERR "%s(): Unknown command 0x%x.\n",
- __func__, command);
- return -EINVAL;
- }
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
- case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
- /* Tuner Reset Command */
- bitmask = 0x04;
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
- case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
- /* Two identical tuners on two different i2c buses,
- * we need to reset the correct gpio. */
- if (port->nr == 1)
- bitmask = 0x01;
- else if (port->nr == 2)
- bitmask = 0x04;
- break;
- case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
- /* Tuner Reset Command */
- bitmask = 0x02;
- break;
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- altera_ci_tuner_reset(dev, port->nr);
- break;
- }
-
- if (bitmask) {
- /* Drive the tuner into reset and back out */
- cx_clear(GP0_IO, bitmask);
- mdelay(200);
- cx_set(GP0_IO, bitmask);
- }
-
- return 0;
-}
-
-void cx23885_gpio_setup(struct cx23885_dev *dev)
-{
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- /* GPIO-0 cx24227 demodulator reset */
- cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- /* GPIO-0 cx24227 demodulator */
- /* GPIO-2 xc3028 tuner */
-
- /* Put the parts into reset */
- cx_set(GP0_IO, 0x00050000);
- cx_clear(GP0_IO, 0x00000005);
- msleep(5);
-
- /* Bring the parts out of reset */
- cx_set(GP0_IO, 0x00050005);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- /* GPIO-0 cx24227 demodulator reset */
- /* GPIO-2 xc5000 tuner reset */
- cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- /* GPIO-0 656_CLK */
- /* GPIO-1 656_D0 */
- /* GPIO-2 8295A Reset */
- /* GPIO-3-10 cx23417 data0-7 */
- /* GPIO-11-14 cx23417 addr0-3 */
- /* GPIO-15-18 cx23417 READY, CS, RD, WR */
- /* GPIO-19 IR_RX */
-
- /* CX23417 GPIO's */
- /* EIO15 Zilog Reset */
- /* EIO14 S5H1409/CX24227 Reset */
- mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1);
-
- /* Put the demod into reset and protect the eeprom */
- mc417_gpio_clear(dev, GPIO_15 | GPIO_14);
- mdelay(100);
-
- /* Bring the demod and blaster out of reset */
- mc417_gpio_set(dev, GPIO_15 | GPIO_14);
- mdelay(100);
-
- /* Force the TDA8295A into reset and back */
- cx23885_gpio_enable(dev, GPIO_2, 1);
- cx23885_gpio_set(dev, GPIO_2);
- mdelay(20);
- cx23885_gpio_clear(dev, GPIO_2);
- mdelay(20);
- cx23885_gpio_set(dev, GPIO_2);
- mdelay(20);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1200:
- /* GPIO-0 tda10048 demodulator reset */
- /* GPIO-2 tda18271 tuner reset */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x00050000);
- mdelay(20);
- cx_clear(GP0_IO, 0x00000005);
- mdelay(20);
- cx_set(GP0_IO, 0x00050005);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1700:
- /* GPIO-0 TDA10048 demodulator reset */
- /* GPIO-2 TDA8295A Reset */
- /* GPIO-3-10 cx23417 data0-7 */
- /* GPIO-11-14 cx23417 addr0-3 */
- /* GPIO-15-18 cx23417 READY, CS, RD, WR */
-
- /* The following GPIO's are on the interna AVCore (cx25840) */
- /* GPIO-19 IR_RX */
- /* GPIO-20 IR_TX 416/DVBT Select */
- /* GPIO-21 IIS DAT */
- /* GPIO-22 IIS WCLK */
- /* GPIO-23 IIS BCLK */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x00050000);
- mdelay(20);
- cx_clear(GP0_IO, 0x00000005);
- mdelay(20);
- cx_set(GP0_IO, 0x00050005);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- /* GPIO-0 Dibcom7000p demodulator reset */
- /* GPIO-2 xc3028L tuner reset */
- /* GPIO-13 LED */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x00050000);
- mdelay(20);
- cx_clear(GP0_IO, 0x00000005);
- mdelay(20);
- cx_set(GP0_IO, 0x00050005);
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
- /* GPIO-0 xc5000 tuner reset i2c bus 0 */
- /* GPIO-1 s5h1409 demod reset i2c bus 0 */
- /* GPIO-2 xc5000 tuner reset i2c bus 1 */
- /* GPIO-3 s5h1409 demod reset i2c bus 0 */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x000f0000);
- mdelay(20);
- cx_clear(GP0_IO, 0x0000000f);
- mdelay(20);
- cx_set(GP0_IO, 0x000f000f);
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
- /* GPIO-0 portb xc3028 reset */
- /* GPIO-1 portb zl10353 reset */
- /* GPIO-2 portc xc3028 reset */
- /* GPIO-3 portc zl10353 reset */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x000f0000);
- mdelay(20);
- cx_clear(GP0_IO, 0x0000000f);
- mdelay(20);
- cx_set(GP0_IO, 0x000f000f);
- break;
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
- case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
- /* GPIO-2 xc3028 tuner reset */
-
- /* The following GPIO's are on the internal AVCore (cx25840) */
- /* GPIO-? zl10353 demod reset */
-
- /* Put the parts into reset and back */
- cx_set(GP0_IO, 0x00040000);
- mdelay(20);
- cx_clear(GP0_IO, 0x00000004);
- mdelay(20);
- cx_set(GP0_IO, 0x00040004);
- break;
- case CX23885_BOARD_TBS_6920:
- cx_write(MC417_CTL, 0x00000036);
- cx_write(MC417_OEN, 0x00001000);
- cx_set(MC417_RWD, 0x00000002);
- mdelay(200);
- cx_clear(MC417_RWD, 0x00000800);
- mdelay(200);
- cx_set(MC417_RWD, 0x00000800);
- mdelay(200);
- break;
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- /* GPIO-0 INTA from CiMax1
- GPIO-1 INTB from CiMax2
- GPIO-2 reset chips
- GPIO-3 to GPIO-10 data/addr for CA
- GPIO-11 ~CS0 to CiMax1
- GPIO-12 ~CS1 to CiMax2
- GPIO-13 ADL0 load LSB addr
- GPIO-14 ADL1 load MSB addr
- GPIO-15 ~RDY from CiMax
- GPIO-17 ~RD to CiMax
- GPIO-18 ~WR to CiMax
- */
- cx_set(GP0_IO, 0x00040000); /* GPIO as out */
- /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
- cx_clear(GP0_IO, 0x00030004);
- mdelay(100);/* reset delay */
- cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
- cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
- /* GPIO-15 IN as ~ACK, rest as OUT */
- cx_write(MC417_OEN, 0x00001000);
- /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
- cx_write(MC417_RWD, 0x0000c300);
- /* enable irq */
- cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1210:
- /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */
- /* GPIO-6 I2C Gate which can isolate the demod from the bus */
- /* GPIO-9 Demod reset */
-
- /* Put the parts into reset and back */
- cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1);
- cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5);
- cx23885_gpio_clear(dev, GPIO_9);
- mdelay(20);
- cx23885_gpio_set(dev, GPIO_9);
- break;
- case CX23885_BOARD_MYGICA_X8506:
- case CX23885_BOARD_MAGICPRO_PROHDTVE2:
- case CX23885_BOARD_MYGICA_X8507:
- /* GPIO-0 (0)Analog / (1)Digital TV */
- /* GPIO-1 reset XC5000 */
- /* GPIO-2 reset LGS8GL5 / LGS8G75 */
- cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1);
- cx23885_gpio_clear(dev, GPIO_1 | GPIO_2);
- mdelay(100);
- cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2);
- mdelay(100);
- break;
- case CX23885_BOARD_MYGICA_X8558PRO:
- /* GPIO-0 reset first ATBM8830 */
- /* GPIO-1 reset second ATBM8830 */
- cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1);
- cx23885_gpio_clear(dev, GPIO_0 | GPIO_1);
- mdelay(100);
- cx23885_gpio_set(dev, GPIO_0 | GPIO_1);
- mdelay(100);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- /* GPIO-0 656_CLK */
- /* GPIO-1 656_D0 */
- /* GPIO-2 Wake# */
- /* GPIO-3-10 cx23417 data0-7 */
- /* GPIO-11-14 cx23417 addr0-3 */
- /* GPIO-15-18 cx23417 READY, CS, RD, WR */
- /* GPIO-19 IR_RX */
- /* GPIO-20 C_IR_TX */
- /* GPIO-21 I2S DAT */
- /* GPIO-22 I2S WCLK */
- /* GPIO-23 I2S BCLK */
- /* ALT GPIO: EXP GPIO LATCH */
-
- /* CX23417 GPIO's */
- /* GPIO-14 S5H1411/CX24228 Reset */
- /* GPIO-13 EEPROM write protect */
- mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1);
-
- /* Put the demod into reset and protect the eeprom */
- mc417_gpio_clear(dev, GPIO_14 | GPIO_13);
- mdelay(100);
-
- /* Bring the demod out of reset */
- mc417_gpio_set(dev, GPIO_14);
- mdelay(100);
-
- /* CX24228 GPIO */
- /* Connected to IF / Mux */
- break;
- case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
- cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */
- break;
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- /* GPIO-0 ~INT in
- GPIO-1 TMS out
- GPIO-2 ~reset chips out
- GPIO-3 to GPIO-10 data/addr for CA in/out
- GPIO-11 ~CS out
- GPIO-12 ADDR out
- GPIO-13 ~WR out
- GPIO-14 ~RD out
- GPIO-15 ~RDY in
- GPIO-16 TCK out
- GPIO-17 TDO in
- GPIO-18 TDI out
- */
- cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */
- /* GPIO-0 as INT, reset & TMS low */
- cx_clear(GP0_IO, 0x00010006);
- mdelay(100);/* reset delay */
- cx_set(GP0_IO, 0x00000004); /* reset high */
- cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */
- /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */
- cx_write(MC417_OEN, 0x00005000);
- /* ~RD, ~WR high; ADDR low; ~CS high */
- cx_write(MC417_RWD, 0x00000d00);
- /* enable irq */
- cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
- break;
- }
-}
-
-int cx23885_ir_init(struct cx23885_dev *dev)
-{
- static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = {
- {
- .flags = V4L2_SUBDEV_IO_PIN_INPUT,
- .pin = CX23885_PIN_IR_RX_GPIO19,
- .function = CX23885_PAD_IR_RX,
- .value = 0,
- .strength = CX25840_PIN_DRIVE_MEDIUM,
- }, {
- .flags = V4L2_SUBDEV_IO_PIN_OUTPUT,
- .pin = CX23885_PIN_IR_TX_GPIO20,
- .function = CX23885_PAD_IR_TX,
- .value = 0,
- .strength = CX25840_PIN_DRIVE_MEDIUM,
- }
- };
- const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg);
-
- static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = {
- {
- .flags = V4L2_SUBDEV_IO_PIN_INPUT,
- .pin = CX23885_PIN_IR_RX_GPIO19,
- .function = CX23885_PAD_IR_RX,
- .value = 0,
- .strength = CX25840_PIN_DRIVE_MEDIUM,
- }
- };
- const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg);
-
- struct v4l2_subdev_ir_parameters params;
- int ret = 0;
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- case CX23885_BOARD_HAUPPAUGE_HVR1200:
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1210:
- /* FIXME: Implement me */
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- ret = cx23888_ir_probe(dev);
- if (ret)
- break;
- dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
- v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
- ir_rx_pin_cfg_count, ir_rx_pin_cfg);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- ret = cx23888_ir_probe(dev);
- if (ret)
- break;
- dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR);
- v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
- ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
- /*
- * For these boards we need to invert the Tx output via the
- * IR controller to have the LED off while idle
- */
- v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, &params);
- params.enable = false;
- params.shutdown = false;
- params.invert_level = true;
- v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
- params.shutdown = true;
- v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, &params);
- break;
- case CX23885_BOARD_TEVII_S470:
- if (!enable_885_ir)
- break;
- dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
- if (dev->sd_ir == NULL) {
- ret = -ENODEV;
- break;
- }
- v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
- ir_rx_pin_cfg_count, ir_rx_pin_cfg);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- if (!enable_885_ir)
- break;
- dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE);
- if (dev->sd_ir == NULL) {
- ret = -ENODEV;
- break;
- }
- v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config,
- ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg);
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
- request_module("ir-kbd-i2c");
- break;
- }
-
- return ret;
-}
-
-void cx23885_ir_fini(struct cx23885_dev *dev)
-{
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- cx23885_irq_remove(dev, PCI_MSK_IR);
- cx23888_ir_remove(dev);
- dev->sd_ir = NULL;
- break;
- case CX23885_BOARD_TEVII_S470:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- cx23885_irq_remove(dev, PCI_MSK_AV_CORE);
- /* sd_ir is a duplicate pointer to the AV Core, just clear it */
- dev->sd_ir = NULL;
- break;
- }
-}
-
-int netup_jtag_io(void *device, int tms, int tdi, int read_tdo)
-{
- int data;
- int tdo = 0;
- struct cx23885_dev *dev = (struct cx23885_dev *)device;
- /*TMS*/
- data = ((cx_read(GP0_IO)) & (~0x00000002));
- data |= (tms ? 0x00020002 : 0x00020000);
- cx_write(GP0_IO, data);
-
- /*TDI*/
- data = ((cx_read(MC417_RWD)) & (~0x0000a000));
- data |= (tdi ? 0x00008000 : 0);
- cx_write(MC417_RWD, data);
- if (read_tdo)
- tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/
-
- cx_write(MC417_RWD, data | 0x00002000);
- udelay(1);
- /*TCK*/
- cx_write(MC417_RWD, data);
-
- return tdo;
-}
-
-void cx23885_ir_pci_int_enable(struct cx23885_dev *dev)
-{
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- if (dev->sd_ir)
- cx23885_irq_add_enable(dev, PCI_MSK_IR);
- break;
- case CX23885_BOARD_TEVII_S470:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- if (dev->sd_ir)
- cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE);
- break;
- }
-}
-
-void cx23885_card_setup(struct cx23885_dev *dev)
-{
- struct cx23885_tsport *ts1 = &dev->ts1;
- struct cx23885_tsport *ts2 = &dev->ts2;
-
- static u8 eeprom[256];
-
- if (dev->i2c_bus[0].i2c_rc == 0) {
- dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
- tveeprom_read(&dev->i2c_bus[0].i2c_client,
- eeprom, sizeof(eeprom));
- }
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- if (dev->i2c_bus[0].i2c_rc == 0) {
- if (eeprom[0x80] != 0x84)
- hauppauge_eeprom(dev, eeprom+0xc0);
- else
- hauppauge_eeprom(dev, eeprom+0x80);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- if (dev->i2c_bus[0].i2c_rc == 0)
- hauppauge_eeprom(dev, eeprom+0x80);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
- case CX23885_BOARD_HAUPPAUGE_HVR1200:
- case CX23885_BOARD_HAUPPAUGE_HVR1700:
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1210:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- if (dev->i2c_bus[0].i2c_rc == 0)
- hauppauge_eeprom(dev, eeprom+0xc0);
- break;
- }
-
- switch (dev->board) {
- case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
- case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP:
- ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- /* break omitted intentionally */
- case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
- ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- /* Defaults for VID B - Analog encoder */
- /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */
- ts1->gen_ctrl_val = 0x10e;
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
-
- /* APB_TSVALERR_POL (active low)*/
- ts1->vld_misc_val = 0x2000;
- ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc);
- cx_write(0x130184, 0xc);
-
- /* Defaults for VID C */
- ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_TBS_6920:
- ts1->gen_ctrl_val = 0x4; /* Parallel */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_TEVII_S470:
- case CX23885_BOARD_TEVII_S471:
- case CX23885_BOARD_DVBWORLD_2005:
- ts1->gen_ctrl_val = 0x5; /* Parallel */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
- ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_MYGICA_X8506:
- case CX23885_BOARD_MAGICPRO_PROHDTVE2:
- ts1->gen_ctrl_val = 0x5; /* Parallel */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_MYGICA_X8558PRO:
- ts1->gen_ctrl_val = 0x5; /* Parallel */
- ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
- case CX23885_BOARD_HAUPPAUGE_HVR1200:
- case CX23885_BOARD_HAUPPAUGE_HVR1700:
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1210:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
- default:
- ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
- ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
- ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
- }
-
- /* Certain boards support analog, or require the avcore to be
- * loaded, ensure this happens.
- */
- switch (dev->board) {
- case CX23885_BOARD_TEVII_S470:
- /* Currently only enabled for the integrated IR controller */
- if (!enable_885_ir)
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
- case CX23885_BOARD_HAUPPAUGE_HVR1700:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_MYGICA_X8506:
- case CX23885_BOARD_MAGICPRO_PROHDTVE2:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200:
- case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID:
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- case CX23885_BOARD_MPX885:
- case CX23885_BOARD_MYGICA_X8507:
- case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
- dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[2].i2c_adap,
- "cx25840", 0x88 >> 1, NULL);
- if (dev->sd_cx25840) {
- dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE;
- v4l2_subdev_call(dev->sd_cx25840, core, load_fw);
- }
- break;
- }
-
- /* AUX-PLL 27MHz CLK */
- switch (dev->board) {
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- netup_initialize(dev);
- break;
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
- int ret;
- const struct firmware *fw;
- const char *filename = "dvb-netup-altera-01.fw";
- char *action = "configure";
- static struct netup_card_info cinfo;
- struct altera_config netup_config = {
- .dev = dev,
- .action = action,
- .jtag_io = netup_jtag_io,
- };
-
- netup_initialize(dev);
-
- netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
- if (netup_card_rev)
- cinfo.rev = netup_card_rev;
-
- switch (cinfo.rev) {
- case 0x4:
- filename = "dvb-netup-altera-04.fw";
- break;
- default:
- filename = "dvb-netup-altera-01.fw";
- break;
- }
- printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n",
- cinfo.rev, filename);
-
- ret = request_firmware(&fw, filename, &dev->pci->dev);
- if (ret != 0)
- printk(KERN_ERR "did not find the firmware file. (%s) "
- "Please see linux/Documentation/dvb/ for more details "
- "on firmware-problems.", filename);
- else
- altera_init(&netup_config, fw);
-
- release_firmware(fw);
- break;
- }
- }
-}
-
-/* ------------------------------------------------------------------ */
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
deleted file mode 100644
index cd542684ba02..000000000000
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ /dev/null
@@ -1,1357 +0,0 @@
-/*
- * Driver for the Conexant CX23885 PCIe bridge
- *
- * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/suspend.h>
-
-#include "cx23885.h"
-#include <media/v4l2-common.h>
-
-#include "dvb_ca_en50221.h"
-#include "s5h1409.h"
-#include "s5h1411.h"
-#include "mt2131.h"
-#include "tda8290.h"
-#include "tda18271.h"
-#include "lgdt330x.h"
-#include "xc4000.h"
-#include "xc5000.h"
-#include "max2165.h"
-#include "tda10048.h"
-#include "tuner-xc2028.h"
-#include "tuner-simple.h"
-#include "dib7000p.h"
-#include "dibx000_common.h"
-#include "zl10353.h"
-#include "stv0900.h"
-#include "stv0900_reg.h"
-#include "stv6110.h"
-#include "lnbh24.h"
-#include "cx24116.h"
-#include "cimax2.h"
-#include "lgs8gxx.h"
-#include "netup-eeprom.h"
-#include "netup-init.h"
-#include "lgdt3305.h"
-#include "atbm8830.h"
-#include "ds3000.h"
-#include "cx23885-f300.h"
-#include "altera-ci.h"
-#include "stv0367.h"
-#include "drxk.h"
-#include "mt2063.h"
-
-static unsigned int debug;
-
-#define dprintk(level, fmt, arg...)\
- do { if (debug >= level)\
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\
- } while (0)
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int alt_tuner;
-module_param(alt_tuner, int, 0644);
-MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-/* ------------------------------------------------------------------ */
-
-static int dvb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx23885_tsport *port = q->priv_data;
-
- port->ts_packet_size = 188 * 4;
- port->ts_packet_count = 32;
-
- *size = port->ts_packet_size * port->ts_packet_count;
- *count = 32;
- return 0;
-}
-
-static int dvb_buf_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct cx23885_tsport *port = q->priv_data;
- return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field);
-}
-
-static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct cx23885_tsport *port = q->priv_data;
- cx23885_buf_queue(port, (struct cx23885_buffer *)vb);
-}
-
-static void dvb_buf_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- cx23885_free_buffer(q, (struct cx23885_buffer *)vb);
-}
-
-static int cx23885_dvb_set_frontend(struct dvb_frontend *fe);
-
-static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open)
-{
- struct videobuf_dvb_frontends *f;
- struct videobuf_dvb_frontend *fe;
-
- f = &port->frontends;
-
- if (f->gate <= 1) /* undefined or fe0 */
- fe = videobuf_dvb_get_frontend(f, 1);
- else
- fe = videobuf_dvb_get_frontend(f, f->gate);
-
- if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
- fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
-
- /*
- * FIXME: Improve this path to avoid calling the
- * cx23885_dvb_set_frontend() every time it passes here.
- */
- cx23885_dvb_set_frontend(fe->dvb.frontend);
-}
-
-static struct videobuf_queue_ops dvb_qops = {
- .buf_setup = dvb_buf_setup,
- .buf_prepare = dvb_buf_prepare,
- .buf_queue = dvb_buf_queue,
- .buf_release = dvb_buf_release,
-};
-
-static struct s5h1409_config hauppauge_generic_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_ON,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct tda10048_config hauppauge_hvr1200_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_SERIAL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_200,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3800,
- .dtv8_if_freq_khz = TDA10048_IF_4300,
- .clk_freq_khz = TDA10048_CLK_16000,
-};
-
-static struct tda10048_config hauppauge_hvr1210_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_SERIAL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_200,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3500,
- .dtv8_if_freq_khz = TDA10048_IF_4000,
- .clk_freq_khz = TDA10048_CLK_16000,
-};
-
-static struct s5h1409_config hauppauge_ezqam_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .qam_if = 4000,
- .inversion = S5H1409_INVERSION_ON,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct s5h1409_config hauppauge_hvr1800lp_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct s5h1409_config hauppauge_hvr1500_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct mt2131_config hauppauge_generic_tunerconfig = {
- 0x61
-};
-
-static struct lgdt330x_config fusionhdtv_5_express = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x40,
-};
-
-static struct s5h1409_config hauppauge_hvr1500q_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_ON,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct s5h1409_config dvico_s5h1409_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_ON,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct s5h1411_config dvico_s5h1411_config = {
- .output_mode = S5H1411_SERIAL_OUTPUT,
- .gpio = S5H1411_GPIO_ON,
- .qam_if = S5H1411_IF_44000,
- .vsb_if = S5H1411_IF_44000,
- .inversion = S5H1411_INVERSION_OFF,
- .status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct s5h1411_config hcw_s5h1411_config = {
- .output_mode = S5H1411_SERIAL_OUTPUT,
- .gpio = S5H1411_GPIO_OFF,
- .vsb_if = S5H1411_IF_44000,
- .qam_if = S5H1411_IF_4000,
- .inversion = S5H1411_INVERSION_ON,
- .status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
- .i2c_address = 0x61,
- .if_khz = 5380,
-};
-
-static struct xc5000_config dvico_xc5000_tunerconfig = {
- .i2c_address = 0x64,
- .if_khz = 5380,
-};
-
-static struct tda829x_config tda829x_no_probe = {
- .probe_tuner = TDA829X_DONT_PROBE,
-};
-
-static struct tda18271_std_map hauppauge_tda18271_std_map = {
- .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
- .if_lvl = 6, .rfagc_top = 0x37 },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
- .if_lvl = 6, .rfagc_top = 0x37 },
-};
-
-static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = {
- .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
- .if_lvl = 1, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config hauppauge_tda18271_config = {
- .std_map = &hauppauge_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct tda18271_config hauppauge_hvr1200_tuner_config = {
- .std_map = &hauppauge_hvr1200_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct tda18271_config hauppauge_hvr1210_tuner_config = {
- .gate = TDA18271_GATE_DIGITAL,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct tda18271_std_map hauppauge_hvr127x_std_map = {
- .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
- .if_lvl = 1, .rfagc_top = 0x58 },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
- .if_lvl = 1, .rfagc_top = 0x58 },
-};
-
-static struct tda18271_config hauppauge_hvr127x_config = {
- .std_map = &hauppauge_hvr127x_std_map,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct lgdt3305_config hauppauge_lgdt3305_config = {
- .i2c_addr = 0x0e,
- .mpeg_mode = LGDT3305_MPEG_SERIAL,
- .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
- .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 1,
- .qam_if_khz = 4000,
- .vsb_if_khz = 3250,
-};
-
-static struct dibx000_agc_config xc3028_agc_config = {
- BAND_VHF | BAND_UHF, /* band_caps */
-
- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0,
- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0,
- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0,
- * P_agc_nb_est=2, P_agc_write=0
- */
- (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) |
- (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */
-
- 712, /* inv_gain */
- 21, /* time_stabiliz */
-
- 0, /* alpha_level */
- 118, /* thlock */
-
- 0, /* wbd_inv */
- 2867, /* wbd_ref */
- 0, /* wbd_sel */
- 2, /* wbd_alpha */
-
- 0, /* agc1_max */
- 0, /* agc1_min */
- 39718, /* agc2_max */
- 9930, /* agc2_min */
- 0, /* agc1_pt1 */
- 0, /* agc1_pt2 */
- 0, /* agc1_pt3 */
- 0, /* agc1_slope1 */
- 0, /* agc1_slope2 */
- 0, /* agc2_pt1 */
- 128, /* agc2_pt2 */
- 29, /* agc2_slope1 */
- 29, /* agc2_slope2 */
-
- 17, /* alpha_mant */
- 27, /* alpha_exp */
- 23, /* beta_mant */
- 51, /* beta_exp */
-
- 1, /* perform_agc_softsplit */
-};
-
-/* PLL Configuration for COFDM BW_MHz = 8.000000
- * With external clock = 30.000000 */
-static struct dibx000_bandwidth_config xc3028_bw_config = {
- 60000, /* internal */
- 30000, /* sampling */
- 1, /* pll_cfg: prediv */
- 8, /* pll_cfg: ratio */
- 3, /* pll_cfg: range */
- 1, /* pll_cfg: reset */
- 0, /* pll_cfg: bypass */
- 0, /* misc: refdiv */
- 0, /* misc: bypclk_div */
- 1, /* misc: IO_CLK_en_core */
- 1, /* misc: ADClkSrc */
- 0, /* misc: modulo */
- (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */
- (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */
- 20452225, /* timf */
- 30000000 /* xtal_hz */
-};
-
-static struct dib7000p_config hauppauge_hvr1400_dib7000_config = {
- .output_mpeg2_in_188_bytes = 1,
- .hostbus_diversity = 1,
- .tuner_is_baseband = 0,
- .update_lna = NULL,
-
- .agc_config_count = 1,
- .agc = &xc3028_agc_config,
- .bw = &xc3028_bw_config,
-
- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS,
- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES,
- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS,
-
- .pwm_freq_div = 0,
- .agc_control = NULL,
- .spur_protect = 0,
-
- .output_mode = OUTMODE_MPEG2_SERIAL,
-};
-
-static struct zl10353_config dvico_fusionhdtv_xc3028 = {
- .demod_address = 0x0f,
- .if2 = 45600,
- .no_tuner = 1,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static struct stv0900_reg stv0900_ts_regs[] = {
- { R0900_TSGENERAL, 0x00 },
- { R0900_P1_TSSPEED, 0x40 },
- { R0900_P2_TSSPEED, 0x40 },
- { R0900_P1_TSCFGM, 0xc0 },
- { R0900_P2_TSCFGM, 0xc0 },
- { R0900_P1_TSCFGH, 0xe0 },
- { R0900_P2_TSCFGH, 0xe0 },
- { R0900_P1_TSCFGL, 0x20 },
- { R0900_P2_TSCFGL, 0x20 },
- { 0xffff, 0xff }, /* terminate */
-};
-
-static struct stv0900_config netup_stv0900_config = {
- .demod_address = 0x68,
- .demod_mode = 1, /* dual */
- .xtal = 8000000,
- .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
- .diseqc_mode = 2,/* 2/3 PWM */
- .ts_config_regs = stv0900_ts_regs,
- .tun1_maddress = 0,/* 0x60 */
- .tun2_maddress = 3,/* 0x63 */
- .tun1_adc = 1,/* 1 Vpp */
- .tun2_adc = 1,/* 1 Vpp */
-};
-
-static struct stv6110_config netup_stv6110_tunerconfig_a = {
- .i2c_address = 0x60,
- .mclk = 16000000,
- .clk_div = 1,
- .gain = 8, /* +16 dB - maximum gain */
-};
-
-static struct stv6110_config netup_stv6110_tunerconfig_b = {
- .i2c_address = 0x63,
- .mclk = 16000000,
- .clk_div = 1,
- .gain = 8, /* +16 dB - maximum gain */
-};
-
-static struct cx24116_config tbs_cx24116_config = {
- .demod_address = 0x55,
-};
-
-static struct ds3000_config tevii_ds3000_config = {
- .demod_address = 0x68,
-};
-
-static struct cx24116_config dvbworld_cx24116_config = {
- .demod_address = 0x05,
-};
-
-static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = {
- .prod = LGS8GXX_PROD_LGS8GL5,
- .demod_address = 0x19,
- .serial_ts = 0,
- .ts_clk_pol = 1,
- .ts_clk_gated = 1,
- .if_clk_freq = 30400, /* 30.4 MHz */
- .if_freq = 5380, /* 5.38 MHz */
- .if_neg_center = 1,
- .ext_adc = 0,
- .adc_signed = 0,
- .if_neg_edge = 0,
-};
-
-static struct xc5000_config mygica_x8506_xc5000_config = {
- .i2c_address = 0x61,
- .if_khz = 5380,
-};
-
-static int cx23885_dvb_set_frontend(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *p = &fe->dtv_property_cache;
- struct cx23885_tsport *port = fe->dvb->priv;
- struct cx23885_dev *dev = port->dev;
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- switch (p->modulation) {
- case VSB_8:
- cx23885_gpio_clear(dev, GPIO_5);
- break;
- case QAM_64:
- case QAM_256:
- default:
- cx23885_gpio_set(dev, GPIO_5);
- break;
- }
- break;
- case CX23885_BOARD_MYGICA_X8506:
- case CX23885_BOARD_MAGICPRO_PROHDTVE2:
- /* Select Digital TV */
- cx23885_gpio_set(dev, GPIO_0);
- break;
- }
- return 0;
-}
-
-static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = {
- .prod = LGS8GXX_PROD_LGS8G75,
- .demod_address = 0x19,
- .serial_ts = 0,
- .ts_clk_pol = 1,
- .ts_clk_gated = 1,
- .if_clk_freq = 30400, /* 30.4 MHz */
- .if_freq = 6500, /* 6.50 MHz */
- .if_neg_center = 1,
- .ext_adc = 0,
- .adc_signed = 1,
- .adc_vpp = 2, /* 1.6 Vpp */
- .if_neg_edge = 1,
-};
-
-static struct xc5000_config magicpro_prohdtve2_xc5000_config = {
- .i2c_address = 0x61,
- .if_khz = 6500,
-};
-
-static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = {
- .prod = ATBM8830_PROD_8830,
- .demod_address = 0x44,
- .serial_ts = 0,
- .ts_sampling_edge = 1,
- .ts_clk_gated = 0,
- .osc_clk_freq = 30400, /* in kHz */
- .if_freq = 0, /* zero IF */
- .zif_swap_iq = 1,
- .agc_min = 0x2E,
- .agc_max = 0xFF,
- .agc_hold_loop = 0,
-};
-
-static struct max2165_config mygic_x8558pro_max2165_cfg1 = {
- .i2c_address = 0x60,
- .osc_clk = 20
-};
-
-static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = {
- .prod = ATBM8830_PROD_8830,
- .demod_address = 0x44,
- .serial_ts = 1,
- .ts_sampling_edge = 1,
- .ts_clk_gated = 0,
- .osc_clk_freq = 30400, /* in kHz */
- .if_freq = 0, /* zero IF */
- .zif_swap_iq = 1,
- .agc_min = 0x2E,
- .agc_max = 0xFF,
- .agc_hold_loop = 0,
-};
-
-static struct max2165_config mygic_x8558pro_max2165_cfg2 = {
- .i2c_address = 0x60,
- .osc_clk = 20
-};
-static struct stv0367_config netup_stv0367_config[] = {
- {
- .demod_address = 0x1c,
- .xtal = 27000000,
- .if_khz = 4500,
- .if_iq_mode = 0,
- .ts_mode = 1,
- .clk_pol = 0,
- }, {
- .demod_address = 0x1d,
- .xtal = 27000000,
- .if_khz = 4500,
- .if_iq_mode = 0,
- .ts_mode = 1,
- .clk_pol = 0,
- },
-};
-
-static struct xc5000_config netup_xc5000_config[] = {
- {
- .i2c_address = 0x61,
- .if_khz = 4500,
- }, {
- .i2c_address = 0x64,
- .if_khz = 4500,
- },
-};
-
-static struct drxk_config terratec_drxk_config[] = {
- {
- .adr = 0x29,
- .no_i2c_bridge = 1,
- }, {
- .adr = 0x2a,
- .no_i2c_bridge = 1,
- },
-};
-
-static struct mt2063_config terratec_mt2063_config[] = {
- {
- .tuner_address = 0x60,
- }, {
- .tuner_address = 0x67,
- },
-};
-
-int netup_altera_fpga_rw(void *device, int flag, int data, int read)
-{
- struct cx23885_dev *dev = (struct cx23885_dev *)device;
- unsigned long timeout = jiffies + msecs_to_jiffies(1);
- uint32_t mem = 0;
-
- mem = cx_read(MC417_RWD);
- if (read)
- cx_set(MC417_OEN, ALT_DATA);
- else {
- cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */
- mem &= ~ALT_DATA;
- mem |= (data & ALT_DATA);
- }
-
- if (flag)
- mem |= ALT_AD_RG;
- else
- mem &= ~ALT_AD_RG;
-
- mem &= ~ALT_CS;
- if (read)
- mem = (mem & ~ALT_RD) | ALT_WR;
- else
- mem = (mem & ~ALT_WR) | ALT_RD;
-
- cx_write(MC417_RWD, mem); /* start RW cycle */
-
- for (;;) {
- mem = cx_read(MC417_RWD);
- if ((mem & ALT_RDY) == 0)
- break;
- if (time_after(jiffies, timeout))
- break;
- udelay(1);
- }
-
- cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS);
- if (read)
- return mem & ALT_DATA;
-
- return 0;
-};
-
-static int dvb_register(struct cx23885_tsport *port)
-{
- struct cx23885_dev *dev = port->dev;
- struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL;
- struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
- int mfe_shared = 0; /* bus not shared by default */
- int ret;
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
- /* init struct videobuf_dvb */
- fe0->dvb.name = dev->name;
-
- /* multi-frontend gate control is undefined or defaults to fe0 */
- port->frontends.gate = 0;
-
- /* Sets the gate control callback to be used by i2c command calls */
- port->gate_ctrl = cx23885_dvb_gate_ctrl;
-
- /* init frontend */
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &hauppauge_generic_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1275:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
- &hauppauge_lgdt3305_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_hvr127x_config);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &hcw_s5h1411_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
- }
-
- tda18271_attach(&dev->ts1.analog_fe,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
-
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1800:
- i2c_bus = &dev->i2c_bus[0];
- switch (alt_tuner) {
- case 1:
- fe0->dvb.frontend =
- dvb_attach(s5h1409_attach,
- &hauppauge_ezqam_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_bus[1].i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
- }
- break;
- case 0:
- default:
- fe0->dvb.frontend =
- dvb_attach(s5h1409_attach,
- &hauppauge_generic_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- break;
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &hauppauge_hvr1800lp_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(mt2131_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_generic_tunerconfig, 0);
- }
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &fusionhdtv_5_express,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap, 0x61,
- TUNER_LG_TDVS_H06XF);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
- i2c_bus = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &hauppauge_hvr1500q_config,
- &dev->i2c_bus[0].i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &hauppauge_hvr1500q_tunerconfig);
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1500:
- i2c_bus = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &hauppauge_hvr1500_config,
- &dev->i2c_bus[0].i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &i2c_bus->i2c_adap,
- .i2c_addr = 0x61,
- };
- static struct xc2028_ctrl ctl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_OREN538,
- };
-
- fe = dvb_attach(xc2028_attach,
- fe0->dvb.frontend, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctl);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1200:
- case CX23885_BOARD_HAUPPAUGE_HVR1700:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(tda10048_attach,
- &hauppauge_hvr1200_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_bus[1].i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_hvr1200_tuner_config);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1210:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(tda10048_attach,
- &hauppauge_hvr1210_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_hvr1210_tuner_config);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1400:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(dib7000p_attach,
- &i2c_bus->i2c_adap,
- 0x12, &hauppauge_hvr1400_dib7000_config);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &dev->i2c_bus[1].i2c_adap,
- .i2c_addr = 0x64,
- };
- static struct xc2028_ctrl ctl = {
- .fname = XC3028L_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_DIBCOM52,
- /* This is true for all demods with
- v36 firmware? */
- .type = XC2028_D2633,
- };
-
- fe = dvb_attach(xc2028_attach,
- fe0->dvb.frontend, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctl);
- }
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
- i2c_bus = &dev->i2c_bus[port->nr - 1];
-
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &dvico_s5h1409_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend == NULL)
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &dvico_s5h1411_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &dvico_xc5000_tunerconfig);
- break;
- case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: {
- i2c_bus = &dev->i2c_bus[port->nr - 1];
-
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_xc3028,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &i2c_bus->i2c_adap,
- .i2c_addr = 0x61,
- };
- static struct xc2028_ctrl ctl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_ZARLINK456,
- };
-
- fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
- &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctl);
- }
- break;
- }
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
- case CX23885_BOARD_COMPRO_VIDEOMATE_E800:
- i2c_bus = &dev->i2c_bus[0];
-
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_xc3028,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &dev->i2c_bus[1].i2c_adap,
- .i2c_addr = 0x61,
- };
- static struct xc2028_ctrl ctl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .demod = XC3028_FE_ZARLINK456,
- };
-
- fe = dvb_attach(xc2028_attach, fe0->dvb.frontend,
- &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctl);
- }
- break;
- case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000:
- i2c_bus = &dev->i2c_bus[0];
-
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_xc3028,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc4000_config cfg = {
- .i2c_address = 0x61,
- .default_pm = 0,
- .dvb_amplitude = 134,
- .set_smoothedcvbs = 1,
- .if_khz = 4560
- };
-
- fe = dvb_attach(xc4000_attach, fe0->dvb.frontend,
- &dev->i2c_bus[1].i2c_adap, &cfg);
- if (!fe) {
- printk(KERN_ERR "%s/2: xc4000 attach failed\n",
- dev->name);
- goto frontend_detach;
- }
- }
- break;
- case CX23885_BOARD_TBS_6920:
- i2c_bus = &dev->i2c_bus[1];
-
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &tbs_cx24116_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
-
- break;
- case CX23885_BOARD_TEVII_S470:
- i2c_bus = &dev->i2c_bus[1];
-
- fe0->dvb.frontend = dvb_attach(ds3000_attach,
- &tevii_ds3000_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = f300_set_voltage;
-
- break;
- case CX23885_BOARD_DVBWORLD_2005:
- i2c_bus = &dev->i2c_bus[1];
-
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &dvbworld_cx24116_config,
- &i2c_bus->i2c_adap);
- break;
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- i2c_bus = &dev->i2c_bus[0];
- switch (port->nr) {
- /* port B */
- case 1:
- fe0->dvb.frontend = dvb_attach(stv0900_attach,
- &netup_stv0900_config,
- &i2c_bus->i2c_adap, 0);
- if (fe0->dvb.frontend != NULL) {
- if (dvb_attach(stv6110_attach,
- fe0->dvb.frontend,
- &netup_stv6110_tunerconfig_a,
- &i2c_bus->i2c_adap)) {
- if (!dvb_attach(lnbh24_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- LNBH24_PCL | LNBH24_TTX,
- LNBH24_TEN, 0x09))
- printk(KERN_ERR
- "No LNBH24 found!\n");
-
- }
- }
- break;
- /* port C */
- case 2:
- fe0->dvb.frontend = dvb_attach(stv0900_attach,
- &netup_stv0900_config,
- &i2c_bus->i2c_adap, 1);
- if (fe0->dvb.frontend != NULL) {
- if (dvb_attach(stv6110_attach,
- fe0->dvb.frontend,
- &netup_stv6110_tunerconfig_b,
- &i2c_bus->i2c_adap)) {
- if (!dvb_attach(lnbh24_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- LNBH24_PCL | LNBH24_TTX,
- LNBH24_TEN, 0x0a))
- printk(KERN_ERR
- "No LNBH24 found!\n");
-
- }
- }
- break;
- }
- break;
- case CX23885_BOARD_MYGICA_X8506:
- i2c_bus = &dev->i2c_bus[0];
- i2c_bus2 = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
- &mygica_x8506_lgs8gl5_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus2->i2c_adap,
- &mygica_x8506_xc5000_config);
- }
- break;
- case CX23885_BOARD_MAGICPRO_PROHDTVE2:
- i2c_bus = &dev->i2c_bus[0];
- i2c_bus2 = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
- &magicpro_prohdtve2_lgs8g75_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus2->i2c_adap,
- &magicpro_prohdtve2_xc5000_config);
- }
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &hcw_s5h1411_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[0].i2c_adap,
- &hauppauge_tda18271_config);
-
- tda18271_attach(&dev->ts1.analog_fe,
- 0x60, &dev->i2c_bus[1].i2c_adap,
- &hauppauge_tda18271_config);
-
- break;
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &hcw_s5h1411_config,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_bus[0].i2c_adap,
- &hauppauge_tda18271_config);
- break;
- case CX23885_BOARD_MYGICA_X8558PRO:
- switch (port->nr) {
- /* port B */
- case 1:
- i2c_bus = &dev->i2c_bus[0];
- fe0->dvb.frontend = dvb_attach(atbm8830_attach,
- &mygica_x8558pro_atbm8830_cfg1,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(max2165_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &mygic_x8558pro_max2165_cfg1);
- }
- break;
- /* port C */
- case 2:
- i2c_bus = &dev->i2c_bus[1];
- fe0->dvb.frontend = dvb_attach(atbm8830_attach,
- &mygica_x8558pro_atbm8830_cfg2,
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(max2165_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &mygic_x8558pro_max2165_cfg2);
- }
- break;
- }
- break;
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- i2c_bus = &dev->i2c_bus[0];
- mfe_shared = 1;/* MFE */
- port->frontends.gate = 0;/* not clear for me yet */
- /* ports B, C */
- /* MFE frontend 1 DVB-T */
- fe0->dvb.frontend = dvb_attach(stv0367ter_attach,
- &netup_stv0367_config[port->nr - 1],
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (NULL == dvb_attach(xc5000_attach,
- fe0->dvb.frontend,
- &i2c_bus->i2c_adap,
- &netup_xc5000_config[port->nr - 1]))
- goto frontend_detach;
- /* load xc5000 firmware */
- fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend);
- }
- /* MFE frontend 2 */
- fe1 = videobuf_dvb_get_frontend(&port->frontends, 2);
- if (fe1 == NULL)
- goto frontend_detach;
- /* DVB-C init */
- fe1->dvb.frontend = dvb_attach(stv0367cab_attach,
- &netup_stv0367_config[port->nr - 1],
- &i2c_bus->i2c_adap);
- if (fe1->dvb.frontend != NULL) {
- fe1->dvb.frontend->id = 1;
- if (NULL == dvb_attach(xc5000_attach,
- fe1->dvb.frontend,
- &i2c_bus->i2c_adap,
- &netup_xc5000_config[port->nr - 1]))
- goto frontend_detach;
- }
- break;
- case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL:
- i2c_bus = &dev->i2c_bus[0];
- i2c_bus2 = &dev->i2c_bus[1];
-
- switch (port->nr) {
- /* port b */
- case 1:
- fe0->dvb.frontend = dvb_attach(drxk_attach,
- &terratec_drxk_config[0],
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(mt2063_attach,
- fe0->dvb.frontend,
- &terratec_mt2063_config[0],
- &i2c_bus2->i2c_adap))
- goto frontend_detach;
- }
- break;
- /* port c */
- case 2:
- fe0->dvb.frontend = dvb_attach(drxk_attach,
- &terratec_drxk_config[1],
- &i2c_bus->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(mt2063_attach,
- fe0->dvb.frontend,
- &terratec_mt2063_config[1],
- &i2c_bus2->i2c_adap))
- goto frontend_detach;
- }
- break;
- }
- break;
- case CX23885_BOARD_TEVII_S471:
- i2c_bus = &dev->i2c_bus[1];
-
- fe0->dvb.frontend = dvb_attach(ds3000_attach,
- &tevii_ds3000_config,
- &i2c_bus->i2c_adap);
- break;
- default:
- printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
- " isn't supported yet\n",
- dev->name);
- break;
- }
-
- if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) {
- printk(KERN_ERR "%s: frontend initialization failed\n",
- dev->name);
- goto frontend_detach;
- }
-
- /* define general-purpose callback pointer */
- fe0->dvb.frontend->callback = cx23885_tuner_callback;
- if (fe1)
- fe1->dvb.frontend->callback = cx23885_tuner_callback;
-#if 0
- /* Ensure all frontends negotiate bus access */
- fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
- if (fe1)
- fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl;
-#endif
-
- /* Put the analog decoder in standby to keep it quiet */
- call_all(dev, core, s_power, 0);
-
- if (fe0->dvb.frontend->ops.analog_ops.standby)
- fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
-
- /* register everything */
- ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
- &dev->pci->dev, adapter_nr, mfe_shared,
- NULL);
- if (ret)
- goto frontend_detach;
-
- /* init CI & MAC */
- switch (dev->board) {
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
- static struct netup_card_info cinfo;
-
- netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
- memcpy(port->frontends.adapter.proposed_mac,
- cinfo.port[port->nr - 1].mac, 6);
- printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n",
- port->nr, port->frontends.adapter.proposed_mac);
-
- netup_ci_init(port);
- break;
- }
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: {
- struct altera_ci_config netup_ci_cfg = {
- .dev = dev,/* magic number to identify*/
- .adapter = &port->frontends.adapter,/* for CI */
- .demux = &fe0->dvb.demux,/* for hw pid filter */
- .fpga_rw = netup_altera_fpga_rw,
- };
-
- altera_ci_init(&netup_ci_cfg, port->nr);
- break;
- }
- case CX23885_BOARD_TEVII_S470: {
- u8 eeprom[256]; /* 24C02 i2c eeprom */
-
- if (port->nr != 1)
- break;
-
- /* Read entire EEPROM */
- dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
- tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
- printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0);
- memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6);
- break;
- }
- }
-
- return ret;
-
-frontend_detach:
- port->gate_ctrl = NULL;
- videobuf_dvb_dealloc_frontends(&port->frontends);
- return -EINVAL;
-}
-
-int cx23885_dvb_register(struct cx23885_tsport *port)
-{
-
- struct videobuf_dvb_frontend *fe0;
- struct cx23885_dev *dev = port->dev;
- int err, i;
-
- /* Here we need to allocate the correct number of frontends,
- * as reflected in the cards struct. The reality is that currently
- * no cx23885 boards support this - yet. But, if we don't modify this
- * code then the second frontend would never be allocated (later)
- * and fail with error before the attach in dvb_register().
- * Without these changes we risk an OOPS later. The changes here
- * are for safety, and should provide a good foundation for the
- * future addition of any multi-frontend cx23885 based boards.
- */
- printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
- port->num_frontends);
-
- for (i = 1; i <= port->num_frontends; i++) {
- if (videobuf_dvb_alloc_frontend(
- &port->frontends, i) == NULL) {
- printk(KERN_ERR "%s() failed to alloc\n", __func__);
- return -ENOMEM;
- }
-
- fe0 = videobuf_dvb_get_frontend(&port->frontends, i);
- if (!fe0)
- err = -EINVAL;
-
- dprintk(1, "%s\n", __func__);
- dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n",
- dev->board,
- dev->name,
- dev->pci_bus,
- dev->pci_slot);
-
- err = -ENODEV;
-
- /* dvb stuff */
- /* We have to init the queue for each frontend on a port. */
- printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name);
- videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops,
- &dev->pci->dev, &port->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
- sizeof(struct cx23885_buffer), port, NULL);
- }
- err = dvb_register(port);
- if (err != 0)
- printk(KERN_ERR "%s() dvb_register failed err = %d\n",
- __func__, err);
-
- return err;
-}
-
-int cx23885_dvb_unregister(struct cx23885_tsport *port)
-{
- struct videobuf_dvb_frontend *fe0;
-
- /* FIXME: in an error condition where the we have
- * an expected number of frontends (attach problem)
- * then this might not clean up correctly, if 1
- * is invalid.
- * This comment only applies to future boards IF they
- * implement MFE support.
- */
- fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
- if (fe0 && fe0->dvb.frontend)
- videobuf_dvb_unregister_bus(&port->frontends);
-
- switch (port->dev->board) {
- case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
- netup_ci_exit(port);
- break;
- case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF:
- altera_ci_release(port->dev, port->nr);
- break;
- }
-
- port->gate_ctrl = NULL;
-
- return 0;
-}
-
diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
deleted file mode 100644
index ce765e3f77bd..000000000000
--- a/drivers/media/video/cx23885/cx23885-input.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Driver for the Conexant CX23885/7/8 PCIe bridge
- *
- * Infrared remote control input device
- *
- * Most of this file is
- *
- * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
- *
- * However, the cx23885_input_{init,fini} functions contained herein are
- * derived from Linux kernel files linux/media/video/.../...-input.c marked as:
- *
- * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
- * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- * Markus Rechberger <mrechberger@gmail.com>
- * Mauro Carvalho Chehab <mchehab@infradead.org>
- * Sascha Sommer <saschasommer@freenet.de>
- * Copyright (C) 2004, 2005 Chris Pascoe
- * Copyright (C) 2003, 2004 Gerd Knorr
- * Copyright (C) 2003 Pavel Machek
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-#include <linux/slab.h>
-#include <media/rc-core.h>
-#include <media/v4l2-subdev.h>
-
-#include "cx23885.h"
-
-#define MODULE_NAME "cx23885"
-
-static void cx23885_input_process_measurements(struct cx23885_dev *dev,
- bool overrun)
-{
- struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
-
- ssize_t num;
- int count, i;
- bool handle = false;
- struct ir_raw_event ir_core_event[64];
-
- do {
- num = 0;
- v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event,
- sizeof(ir_core_event), &num);
-
- count = num / sizeof(struct ir_raw_event);
-
- for (i = 0; i < count; i++) {
- ir_raw_event_store(kernel_ir->rc,
- &ir_core_event[i]);
- handle = true;
- }
- } while (num != 0);
-
- if (overrun)
- ir_raw_event_reset(kernel_ir->rc);
- else if (handle)
- ir_raw_event_handle(kernel_ir->rc);
-}
-
-void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
-{
- struct v4l2_subdev_ir_parameters params;
- int overrun, data_available;
-
- if (dev->sd_ir == NULL || events == 0)
- return;
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- case CX23885_BOARD_TEVII_S470:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- /*
- * The only boards we handle right now. However other boards
- * using the CX2388x integrated IR controller should be similar
- */
- break;
- default:
- return;
- }
-
- overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN |
- V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN);
-
- data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED |
- V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ);
-
- if (overrun) {
- /* If there was a FIFO overrun, stop the device */
- v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
- params.enable = false;
- /* Mitigate race with cx23885_input_ir_stop() */
- params.shutdown = atomic_read(&dev->ir_input_stopping);
- v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
- }
-
- if (data_available)
- cx23885_input_process_measurements(dev, overrun);
-
- if (overrun) {
- /* If there was a FIFO overrun, clear & restart the device */
- params.enable = true;
- /* Mitigate race with cx23885_input_ir_stop() */
- params.shutdown = atomic_read(&dev->ir_input_stopping);
- v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
- }
-}
-
-static int cx23885_input_ir_start(struct cx23885_dev *dev)
-{
- struct v4l2_subdev_ir_parameters params;
-
- if (dev->sd_ir == NULL)
- return -ENODEV;
-
- atomic_set(&dev->ir_input_stopping, 0);
-
- v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- /*
- * The IR controller on this board only returns pulse widths.
- * Any other mode setting will fail to set up the device.
- */
- params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- params.enable = true;
- params.interrupt_enable = true;
- params.shutdown = false;
-
- /* Setup for baseband compatible with both RC-5 and RC-6A */
- params.modulation = false;
- /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/
- /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/
- params.max_pulse_width = 3333333; /* ns */
- /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
- /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
- params.noise_filter_min_width = 333333; /* ns */
- /*
- * This board has inverted receive sense:
- * mark is received as low logic level;
- * falling edges are detected as rising edges; etc.
- */
- params.invert_level = true;
- break;
- case CX23885_BOARD_TEVII_S470:
- /*
- * The IR controller on this board only returns pulse widths.
- * Any other mode setting will fail to set up the device.
- */
- params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
- params.enable = true;
- params.interrupt_enable = true;
- params.shutdown = false;
-
- /* Setup for a standard NEC protocol */
- params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */
- params.carrier_range_lower = 33000; /* Hz */
- params.carrier_range_upper = 43000; /* Hz */
- params.duty_cycle = 33; /* percent, 33 percent for NEC */
-
- /*
- * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units
- * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns
- */
- params.max_pulse_width = 12378022; /* ns */
-
- /*
- * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit
- * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns
- */
- params.noise_filter_min_width = 351648; /* ns */
-
- params.modulation = false;
- params.invert_level = true;
- break;
- }
- v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
- return 0;
-}
-
-static int cx23885_input_ir_open(struct rc_dev *rc)
-{
- struct cx23885_kernel_ir *kernel_ir = rc->priv;
-
- if (kernel_ir->cx == NULL)
- return -ENODEV;
-
- return cx23885_input_ir_start(kernel_ir->cx);
-}
-
-static void cx23885_input_ir_stop(struct cx23885_dev *dev)
-{
- struct v4l2_subdev_ir_parameters params;
-
- if (dev->sd_ir == NULL)
- return;
-
- /*
- * Stop the sd_ir subdevice from generating notifications and
- * scheduling work.
- * It is shutdown this way in order to mitigate a race with
- * cx23885_input_rx_work_handler() in the overrun case, which could
- * re-enable the subdevice.
- */
- atomic_set(&dev->ir_input_stopping, 1);
- v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
- while (params.shutdown == false) {
- params.enable = false;
- params.interrupt_enable = false;
- params.shutdown = true;
- v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
- v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
- }
- flush_work_sync(&dev->cx25840_work);
- flush_work_sync(&dev->ir_rx_work);
- flush_work_sync(&dev->ir_tx_work);
-}
-
-static void cx23885_input_ir_close(struct rc_dev *rc)
-{
- struct cx23885_kernel_ir *kernel_ir = rc->priv;
-
- if (kernel_ir->cx != NULL)
- cx23885_input_ir_stop(kernel_ir->cx);
-}
-
-int cx23885_input_init(struct cx23885_dev *dev)
-{
- struct cx23885_kernel_ir *kernel_ir;
- struct rc_dev *rc;
- char *rc_map;
- enum rc_driver_type driver_type;
- unsigned long allowed_protos;
-
- int ret;
-
- /*
- * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't
- * encapsulated in a v4l2_subdev, then I'm not going to deal with it.
- */
- if (dev->sd_ir == NULL)
- return -ENODEV;
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1270:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- case CX23885_BOARD_HAUPPAUGE_HVR1290:
- case CX23885_BOARD_HAUPPAUGE_HVR1250:
- /* Integrated CX2388[58] IR controller */
- driver_type = RC_DRIVER_IR_RAW;
- allowed_protos = RC_TYPE_ALL;
- /* The grey Hauppauge RC-5 remote */
- rc_map = RC_MAP_HAUPPAUGE;
- break;
- case CX23885_BOARD_TEVII_S470:
- /* Integrated CX23885 IR controller */
- driver_type = RC_DRIVER_IR_RAW;
- allowed_protos = RC_TYPE_ALL;
- /* A guess at the remote */
- rc_map = RC_MAP_TEVII_NEC;
- break;
- default:
- return -ENODEV;
- }
-
- /* cx23885 board instance kernel IR state */
- kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
- if (kernel_ir == NULL)
- return -ENOMEM;
-
- kernel_ir->cx = dev;
- kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
- cx23885_boards[dev->board].name);
- kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
- pci_name(dev->pci));
-
- /* input device */
- rc = rc_allocate_device();
- if (!rc) {
- ret = -ENOMEM;
- goto err_out_free;
- }
-
- kernel_ir->rc = rc;
- rc->input_name = kernel_ir->name;
- rc->input_phys = kernel_ir->phys;
- rc->input_id.bustype = BUS_PCI;
- rc->input_id.version = 1;
- if (dev->pci->subsystem_vendor) {
- rc->input_id.vendor = dev->pci->subsystem_vendor;
- rc->input_id.product = dev->pci->subsystem_device;
- } else {
- rc->input_id.vendor = dev->pci->vendor;
- rc->input_id.product = dev->pci->device;
- }
- rc->dev.parent = &dev->pci->dev;
- rc->driver_type = driver_type;
- rc->allowed_protos = allowed_protos;
- rc->priv = kernel_ir;
- rc->open = cx23885_input_ir_open;
- rc->close = cx23885_input_ir_close;
- rc->map_name = rc_map;
- rc->driver_name = MODULE_NAME;
-
- /* Go */
- dev->kernel_ir = kernel_ir;
- ret = rc_register_device(rc);
- if (ret)
- goto err_out_stop;
-
- return 0;
-
-err_out_stop:
- cx23885_input_ir_stop(dev);
- dev->kernel_ir = NULL;
- rc_free_device(rc);
-err_out_free:
- kfree(kernel_ir->phys);
- kfree(kernel_ir->name);
- kfree(kernel_ir);
- return ret;
-}
-
-void cx23885_input_fini(struct cx23885_dev *dev)
-{
- /* Always stop the IR hardware from generating interrupts */
- cx23885_input_ir_stop(dev);
-
- if (dev->kernel_ir == NULL)
- return;
- rc_unregister_device(dev->kernel_ir->rc);
- kfree(dev->kernel_ir->phys);
- kfree(dev->kernel_ir->name);
- kfree(dev->kernel_ir);
- dev->kernel_ir = NULL;
-}
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
deleted file mode 100644
index 22f8e7fbd665..000000000000
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ /dev/null
@@ -1,1926 +0,0 @@
-/*
- * Driver for the Conexant CX23885 PCIe bridge
- *
- * Copyright (c) 2007 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <asm/div64.h>
-
-#include "cx23885.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include "cx23885-ioctl.h"
-#include "tuner-xc2028.h"
-
-#include <media/cx25840.h>
-
-MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
-MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(vbi_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-
-MODULE_PARM_DESC(video_nr, "video device numbers");
-MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
-
-static unsigned int video_debug;
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-
-static unsigned int irq_debug;
-module_param(irq_debug, int, 0644);
-MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
-
-static unsigned int vid_limit = 16;
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
-#define dprintk(level, fmt, arg...)\
- do { if (video_debug >= level)\
- printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
- } while (0)
-
-/* ------------------------------------------------------------------- */
-/* static data */
-
-#define FORMAT_FLAGS_PACKED 0x01
-#if 0
-static struct cx23885_fmt formats[] = {
- {
- .name = "8 bpp, gray",
- .fourcc = V4L2_PIX_FMT_GREY,
- .depth = 8,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "15 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "15 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "16 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "16 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "24 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .depth = 24,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "32 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "32 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },
-};
-#else
-static struct cx23885_fmt formats[] = {
- {
-#if 0
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
-#endif
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }
-};
-#endif
-
-static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(formats); i++)
- if (formats[i].fourcc == fourcc)
- return formats+i;
-
- printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__,
- (fourcc & 0xff),
- ((fourcc >> 8) & 0xff),
- ((fourcc >> 16) & 0xff),
- ((fourcc >> 24) & 0xff)
- );
- return NULL;
-}
-
-/* ------------------------------------------------------------------- */
-
-static const struct v4l2_queryctrl no_ctl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-
-static struct cx23885_ctrl cx23885_ctls[] = {
- /* --- video --- */
- {
- .v = {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0x00,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = LUMA_CTRL,
- .mask = 0x00ff,
- .shift = 0,
- }, {
- .v = {
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = LUMA_CTRL,
- .mask = 0xff00,
- .shift = 8,
- }, {
- .v = {
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = -127,
- .maximum = 128,
- .step = 1,
- .default_value = 0x0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 128,
- .reg = CHROMA_CTRL,
- .mask = 0xff0000,
- .shift = 16,
- }, {
- /* strictly, this only describes only U saturation.
- * V saturation is handled specially through code.
- */
- .v = {
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x3f,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .off = 0,
- .reg = CHROMA_CTRL,
- .mask = 0x00ff,
- .shift = 0,
- }, {
- /* --- audio --- */
- .v = {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- .reg = PATH1_CTL1,
- .mask = (0x1f << 24),
- .shift = 24,
- }, {
- .v = {
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 65535,
- .step = 65535 / 100,
- .default_value = 65535,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- .reg = PATH1_VOL_CTL,
- .mask = 0xff,
- .shift = 0,
- }
-};
-static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
-
-/* Must be sorted from low to high control ID! */
-static const u32 cx23885_user_ctrls[] = {
- V4L2_CID_USER_CLASS,
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_AUDIO_VOLUME,
- V4L2_CID_AUDIO_MUTE,
- 0
-};
-
-static const u32 *ctrl_classes[] = {
- cx23885_user_ctrls,
- NULL
-};
-
-void cx23885_video_wakeup(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q, u32 count)
-{
- struct cx23885_buffer *buf;
- int bc;
-
- for (bc = 0;; bc++) {
- if (list_empty(&q->active))
- break;
- buf = list_entry(q->active.next,
- struct cx23885_buffer, vb.queue);
-
- /* count comes from the hw and is is 16bit wide --
- * this trick handles wrap-arounds correctly for
- * up to 32767 buffers in flight... */
- if ((s16) (count - buf->count) < 0)
- break;
-
- do_gettimeofday(&buf->vb.ts);
- dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i,
- count, buf->count);
- buf->vb.state = VIDEOBUF_DONE;
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- }
- if (list_empty(&q->active))
- del_timer(&q->timeout);
- else
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- if (bc != 1)
- printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
- __func__, bc);
-}
-
-int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
-{
- dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
- __func__,
- (unsigned int)norm,
- v4l2_norm_to_name(norm));
-
- dev->tvnorm = norm;
-
- call_all(dev, core, s_std, norm);
-
- return 0;
-}
-
-static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
- struct pci_dev *pci,
- struct video_device *template,
- char *type)
-{
- struct video_device *vfd;
- dprintk(1, "%s()\n", __func__);
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name), "%s (%s)",
- cx23885_boards[dev->board].name, type);
- video_set_drvdata(vfd, dev);
- return vfd;
-}
-
-static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
-{
- int i;
-
- if (qctrl->id < V4L2_CID_BASE ||
- qctrl->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- for (i = 0; i < CX23885_CTLS; i++)
- if (cx23885_ctls[i].v.id == qctrl->id)
- break;
- if (i == CX23885_CTLS) {
- *qctrl = no_ctl;
- return 0;
- }
- *qctrl = cx23885_ctls[i].v;
- return 0;
-}
-
-/* ------------------------------------------------------------------- */
-/* resource management */
-
-static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
- unsigned int bit)
-{
- dprintk(1, "%s()\n", __func__);
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&dev->lock);
- if (dev->resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&dev->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- dprintk(1, "res: get %d\n", bit);
- mutex_unlock(&dev->lock);
- return 1;
-}
-
-static int res_check(struct cx23885_fh *fh, unsigned int bit)
-{
- return fh->resources & bit;
-}
-
-static int res_locked(struct cx23885_dev *dev, unsigned int bit)
-{
- return dev->resources & bit;
-}
-
-static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
- unsigned int bits)
-{
- BUG_ON((fh->resources & bits) != bits);
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&dev->lock);
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- dprintk(1, "res: put %d\n", bits);
- mutex_unlock(&dev->lock);
-}
-
-static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data)
-{
- /* 8 bit registers, 8 bit values */
- u8 buf[] = { reg, data };
-
- struct i2c_msg msg = { .addr = 0x98 >> 1,
- .flags = 0, .buf = buf, .len = 2 };
-
- return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1);
-}
-
-static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg)
-{
- /* 8 bit registers, 8 bit values */
- int ret;
- u8 b0[] = { reg };
- u8 b1[] = { 0 };
-
- struct i2c_msg msg[] = {
- { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 },
- { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 }
- };
-
- ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2);
- if (ret != 2)
- printk(KERN_ERR "%s() error\n", __func__);
-
- return b1[0];
-}
-
-static void cx23885_flatiron_dump(struct cx23885_dev *dev)
-{
- int i;
- dprintk(1, "Flatiron dump\n");
- for (i = 0; i < 0x24; i++) {
- dprintk(1, "FI[%02x] = %02x\n", i,
- cx23885_flatiron_read(dev, i));
- }
-}
-
-static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input)
-{
- u8 val;
- dprintk(1, "%s(input = %d)\n", __func__, input);
-
- if (input == 1)
- val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL;
- else if (input == 2)
- val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL;
- else
- return -EINVAL;
-
- val |= 0x20; /* Enable clock to delta-sigma and dec filter */
-
- cx23885_flatiron_write(dev, CH_PWR_CTRL1, val);
-
- /* Wake up */
- cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0);
-
- if (video_debug)
- cx23885_flatiron_dump(dev);
-
- return 0;
-}
-
-static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
-{
- dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
- __func__,
- input, INPUT(input)->vmux,
- INPUT(input)->gpio0, INPUT(input)->gpio1,
- INPUT(input)->gpio2, INPUT(input)->gpio3);
- dev->input = input;
-
- if (dev->board == CX23885_BOARD_MYGICA_X8506 ||
- dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 ||
- dev->board == CX23885_BOARD_MYGICA_X8507) {
- /* Select Analog TV */
- if (INPUT(input)->type == CX23885_VMUX_TELEVISION)
- cx23885_gpio_clear(dev, GPIO_0);
- }
-
- /* Tell the internal A/V decoder */
- v4l2_subdev_call(dev->sd_cx25840, video, s_routing,
- INPUT(input)->vmux, 0, 0);
-
- if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) ||
- (dev->board == CX23885_BOARD_MPX885) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) {
- /* Configure audio routing */
- v4l2_subdev_call(dev->sd_cx25840, audio, s_routing,
- INPUT(input)->amux, 0, 0);
-
- if (INPUT(input)->amux == CX25840_AUDIO7)
- cx23885_flatiron_mux(dev, 1);
- else if (INPUT(input)->amux == CX25840_AUDIO6)
- cx23885_flatiron_mux(dev, 2);
- }
-
- return 0;
-}
-
-static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input)
-{
- dprintk(1, "%s(input=%d)\n", __func__, input);
-
- /* The baseband video core of the cx23885 has two audio inputs.
- * LR1 and LR2. In almost every single case so far only HVR1xxx
- * cards we've only ever supported LR1. Time to support LR2,
- * which is available via the optional white breakout header on
- * the board.
- * We'll use a could of existing enums in the card struct to allow
- * devs to specify which baseband input they need, or just default
- * to what we've always used.
- */
- if (INPUT(input)->amux == CX25840_AUDIO7)
- cx23885_flatiron_mux(dev, 1);
- else if (INPUT(input)->amux == CX25840_AUDIO6)
- cx23885_flatiron_mux(dev, 2);
- else {
- /* Not specifically defined, assume the default. */
- cx23885_flatiron_mux(dev, 1);
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-static int cx23885_start_video_dma(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q,
- struct cx23885_buffer *buf)
-{
- dprintk(1, "%s()\n", __func__);
-
- /* Stop the dma/fifo before we tamper with it's risc programs */
- cx_clear(VID_A_DMA_CTL, 0x11);
-
- /* setup fifo + format */
- cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
- buf->bpl, buf->risc.dma);
-
- /* reset counter */
- cx_write(VID_A_GPCNT_CTL, 3);
- q->count = 1;
-
- /* enable irq */
- cx23885_irq_add_enable(dev, 0x01);
- cx_set(VID_A_INT_MSK, 0x000011);
-
- /* start dma */
- cx_set(DEV_CNTRL2, (1<<5));
- cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
-
- return 0;
-}
-
-
-static int cx23885_restart_video_queue(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q)
-{
- struct cx23885_buffer *buf, *prev;
- struct list_head *item;
- dprintk(1, "%s()\n", __func__);
-
- if (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx23885_buffer,
- vb.queue);
- dprintk(2, "restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
- cx23885_start_video_dma(dev, q, buf);
- list_for_each(item, &q->active) {
- buf = list_entry(item, struct cx23885_buffer,
- vb.queue);
- buf->count = q->count++;
- }
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
- }
-
- prev = NULL;
- for (;;) {
- if (list_empty(&q->queued))
- return 0;
- buf = list_entry(q->queued.next, struct cx23885_buffer,
- vb.queue);
- if (NULL == prev) {
- list_move_tail(&buf->vb.queue, &q->active);
- cx23885_start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2, "[%p/%d] restart_queue - first active\n",
- buf, buf->vb.i);
-
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
- dprintk(2, "[%p/%d] restart_queue - move to active\n",
- buf, buf->vb.i);
- } else {
- return 0;
- }
- prev = buf;
- }
-}
-
-static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
-{
- struct cx23885_fh *fh = q->priv_data;
-
- *size = fh->fmt->depth*fh->width*fh->height >> 3;
- if (0 == *count)
- *count = 32;
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx23885_fh *fh = q->priv_data;
- struct cx23885_dev *dev = fh->dev;
- struct cx23885_buffer *buf =
- container_of(vb, struct cx23885_buffer, vb);
- int rc, init_buffer = 0;
- u32 line0_offset, line1_offset;
- struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
- int field_tff;
-
- BUG_ON(NULL == fh->fmt);
- if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
- fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
- return -EINVAL;
- buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- if (buf->fmt != fh->fmt ||
- buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
- buf->vb.field != field) {
- buf->fmt = fh->fmt;
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.field = field;
- init_buffer = 1;
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- init_buffer = 1;
- rc = videobuf_iolock(q, &buf->vb, NULL);
- if (0 != rc)
- goto fail;
- }
-
- if (init_buffer) {
- buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
- switch (buf->vb.field) {
- case V4L2_FIELD_TOP:
- cx23885_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, UNSET,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_BOTTOM:
- cx23885_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, UNSET, 0,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_INTERLACED:
- if (dev->tvnorm & V4L2_STD_NTSC)
- /* NTSC or */
- field_tff = 1;
- else
- field_tff = 0;
-
- if (cx23885_boards[dev->board].force_bff)
- /* PAL / SECAM OR 888 in NTSC MODE */
- field_tff = 0;
-
- if (field_tff) {
- /* cx25840 transmits NTSC bottom field first */
- dprintk(1, "%s() Creating TFF/NTSC risc\n",
- __func__);
- line0_offset = buf->bpl;
- line1_offset = 0;
- } else {
- /* All other formats are top field first */
- dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n",
- __func__);
- line0_offset = 0;
- line1_offset = buf->bpl;
- }
- cx23885_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, line0_offset,
- line1_offset,
- buf->bpl, buf->bpl,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_TB:
- cx23885_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- 0, buf->bpl * (buf->vb.height >> 1),
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_BT:
- cx23885_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- buf->bpl * (buf->vb.height >> 1), 0,
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- default:
- BUG();
- }
- }
- dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
- buf, buf->vb.i,
- fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
- (unsigned long)buf->risc.dma);
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
- fail:
- cx23885_free_buffer(q, buf);
- return rc;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct cx23885_buffer *buf = container_of(vb,
- struct cx23885_buffer, vb);
- struct cx23885_buffer *prev;
- struct cx23885_fh *fh = vq->priv_data;
- struct cx23885_dev *dev = fh->dev;
- struct cx23885_dmaqueue *q = &dev->vidq;
-
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
- buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
-
- if (!list_empty(&q->queued)) {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
- buf, buf->vb.i);
-
- } else if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue, &q->active);
- cx23885_start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2, "[%p/%d] buffer_queue - first active\n",
- buf, buf->vb.i);
-
- } else {
- prev = list_entry(q->active.prev, struct cx23885_buffer,
- vb.queue);
- if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- /* 64 bit bits 63-32 */
- prev->risc.jmp[2] = cpu_to_le32(0);
- dprintk(2, "[%p/%d] buffer_queue - append to active\n",
- buf, buf->vb.i);
-
- } else {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - first queued\n",
- buf, buf->vb.i);
- }
- }
-}
-
-static void buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx23885_buffer *buf = container_of(vb,
- struct cx23885_buffer, vb);
-
- cx23885_free_buffer(q, buf);
-}
-
-static struct videobuf_queue_ops cx23885_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-static struct videobuf_queue *get_queue(struct cx23885_fh *fh)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &fh->vidq;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return &fh->vbiq;
- default:
- BUG();
- return NULL;
- }
-}
-
-static int get_resource(struct cx23885_fh *fh)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return RESOURCE_VIDEO;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return RESOURCE_VBI;
- default:
- BUG();
- return 0;
- }
-}
-
-static int video_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx23885_dev *dev = video_drvdata(file);
- struct cx23885_fh *fh;
- enum v4l2_buf_type type = 0;
- int radio = 0;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- dprintk(1, "open dev=%s radio=%d type=%s\n",
- video_device_node_name(vdev), radio, v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
-
- file->private_data = fh;
- fh->dev = dev;
- fh->radio = radio;
- fh->type = type;
- fh->width = 320;
- fh->height = 240;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV);
-
- videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx23885_buffer),
- fh, NULL);
-
- videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct cx23885_buffer),
- fh, NULL);
-
-
- dprintk(1, "post videobuf_queue_init()\n");
-
- return 0;
-}
-
-static ssize_t video_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct cx23885_fh *fh = file->private_data;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (res_locked(fh->dev, RESOURCE_VIDEO))
- return -EBUSY;
- return videobuf_read_one(&fh->vidq, data, count, ppos,
- file->f_flags & O_NONBLOCK);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (!res_get(fh->dev, fh, RESOURCE_VBI))
- return -EBUSY;
- return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
- default:
- BUG();
- return 0;
- }
-}
-
-static unsigned int video_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_buffer *buf;
- unsigned int rc = POLLERR;
-
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
- if (!res_get(fh->dev, fh, RESOURCE_VBI))
- return POLLERR;
- return videobuf_poll_stream(file, &fh->vbiq, wait);
- }
-
- mutex_lock(&fh->vidq.vb_lock);
- if (res_check(fh, RESOURCE_VIDEO)) {
- /* streaming capture */
- if (list_empty(&fh->vidq.stream))
- goto done;
- buf = list_entry(fh->vidq.stream.next,
- struct cx23885_buffer, vb.stream);
- } else {
- /* read() capture */
- buf = (struct cx23885_buffer *)fh->vidq.read_buf;
- if (NULL == buf)
- goto done;
- }
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- rc = POLLIN|POLLRDNORM;
- else
- rc = 0;
-done:
- mutex_unlock(&fh->vidq.vb_lock);
- return rc;
-}
-
-static int video_release(struct file *file)
-{
- struct cx23885_fh *fh = file->private_data;
- struct cx23885_dev *dev = fh->dev;
-
- /* turn off overlay */
- if (res_check(fh, RESOURCE_OVERLAY)) {
- /* FIXME */
- res_free(dev, fh, RESOURCE_OVERLAY);
- }
-
- /* stop video capture */
- if (res_check(fh, RESOURCE_VIDEO)) {
- videobuf_queue_cancel(&fh->vidq);
- res_free(dev, fh, RESOURCE_VIDEO);
- }
- if (fh->vidq.read_buf) {
- buffer_release(&fh->vidq, fh->vidq.read_buf);
- kfree(fh->vidq.read_buf);
- }
-
- /* stop vbi capture */
- if (res_check(fh, RESOURCE_VBI)) {
- if (fh->vbiq.streaming)
- videobuf_streamoff(&fh->vbiq);
- if (fh->vbiq.reading)
- videobuf_read_stop(&fh->vbiq);
- res_free(dev, fh, RESOURCE_VBI);
- }
-
- videobuf_mmap_free(&fh->vidq);
- videobuf_mmap_free(&fh->vbiq);
-
- file->private_data = NULL;
- kfree(fh);
-
- /* We are not putting the tuner to sleep here on exit, because
- * we want to use the mpeg encoder in another session to capture
- * tuner video. Closing this will result in no video to the encoder.
- */
-
- return 0;
-}
-
-static int video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct cx23885_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(get_queue(fh), vma);
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO CTRL IOCTLS */
-
-int cx23885_get_control(struct cx23885_dev *dev,
- struct v4l2_control *ctl)
-{
- dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
- call_all(dev, core, g_ctrl, ctl);
- return 0;
-}
-
-int cx23885_set_control(struct cx23885_dev *dev,
- struct v4l2_control *ctl)
-{
- dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__);
- call_all(dev, core, s_ctrl, ctl);
-
- return 0;
-}
-
-static void init_controls(struct cx23885_dev *dev)
-{
- struct v4l2_control ctrl;
- int i;
-
- for (i = 0; i < CX23885_CTLS; i++) {
- ctrl.id = cx23885_ctls[i].v.id;
- ctrl.value = cx23885_ctls[i].v.default_value;
-
- cx23885_set_control(dev, &ctrl);
- }
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO IOCTLS */
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_fh *fh = priv;
-
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->vidq.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- struct cx23885_fmt *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = norm_maxw(dev->tvnorm);
- maxh = norm_maxh(dev->tvnorm);
-
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
-
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
- &f->fmt.pix.height, 32, maxh, 0, 0);
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- struct v4l2_mbus_framefmt mbus_fmt;
- int err;
-
- dprintk(2, "%s()\n", __func__);
- err = vidioc_try_fmt_vid_cap(file, priv, f);
-
- if (0 != err)
- return err;
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->vidq.field = f->fmt.pix.field;
- dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
- fh->width, fh->height, fh->vidq.field);
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
- call_all(dev, video, s_mbus_fmt, &mbus_fmt);
- v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt);
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- strcpy(cap->driver, "cx23885");
- strlcpy(cap->card, cx23885_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_VBI_CAPTURE;
- if (UNSET != dev->tuner_type)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(formats)))
- return -EINVAL;
-
- strlcpy(f->description, formats[f->index].name,
- sizeof(f->description));
- f->pixelformat = formats[f->index].fourcc;
-
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct cx23885_fh *fh = priv;
- return videobuf_reqbufs(get_queue(fh), p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx23885_fh *fh = priv;
- return videobuf_querybuf(get_queue(fh), p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx23885_fh *fh = priv;
- return videobuf_qbuf(get_queue(fh), p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx23885_fh *fh = priv;
- return videobuf_dqbuf(get_queue(fh), p,
- file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- dprintk(1, "%s()\n", __func__);
-
- if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
- if (unlikely(i != fh->type))
- return -EINVAL;
-
- if (unlikely(!res_get(dev, fh, get_resource(fh))))
- return -EBUSY;
-
- /* Don't start VBI streaming unless vida streaming
- * has already started.
- */
- if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) &&
- ((cx_read(VID_A_DMA_CTL) & 0x11) == 0))
- return -EINVAL;
-
- return videobuf_streamon(get_queue(fh));
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- int err, res;
- dprintk(1, "%s()\n", __func__);
-
- if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
- if (i != fh->type)
- return -EINVAL;
-
- res = get_resource(fh);
- err = videobuf_streamoff(get_queue(fh));
- if (err < 0)
- return err;
- res_free(dev, fh, res);
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- dprintk(1, "%s()\n", __func__);
-
- call_all(dev, core, g_std, id);
-
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&dev->lock);
- cx23885_set_tvnorm(dev, *tvnorms);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
-{
- static const char *iname[] = {
- [CX23885_VMUX_COMPOSITE1] = "Composite1",
- [CX23885_VMUX_COMPOSITE2] = "Composite2",
- [CX23885_VMUX_COMPOSITE3] = "Composite3",
- [CX23885_VMUX_COMPOSITE4] = "Composite4",
- [CX23885_VMUX_SVIDEO] = "S-Video",
- [CX23885_VMUX_COMPONENT] = "Component",
- [CX23885_VMUX_TELEVISION] = "Television",
- [CX23885_VMUX_CABLE] = "Cable TV",
- [CX23885_VMUX_DVB] = "DVB",
- [CX23885_VMUX_DEBUG] = "for debug only",
- };
- unsigned int n;
- dprintk(1, "%s()\n", __func__);
-
- n = i->index;
- if (n >= MAX_CX23885_INPUT)
- return -EINVAL;
-
- if (0 == INPUT(n)->type)
- return -EINVAL;
-
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, iname[INPUT(n)->type]);
- if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
- (CX23885_VMUX_CABLE == INPUT(n)->type)) {
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->std = CX23885_NORMS;
- }
-
- /* Two selectable audio inputs for non-tv inputs */
- if (INPUT(n)->type != CX23885_VMUX_TELEVISION)
- i->audioset = 0x3;
-
- if (dev->input == n) {
- /* enum'd input matches our configured input.
- * Ask the video decoder to process the call
- * and give it an oppertunity to update the
- * status field.
- */
- call_all(dev, video, g_input_status, &i->status);
- }
-
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- dprintk(1, "%s()\n", __func__);
- return cx23885_enum_input(dev, i);
-}
-
-int cx23885_get_input(struct file *file, void *priv, unsigned int *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- *i = dev->input;
- dprintk(1, "%s() returns %d\n", __func__, *i);
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- return cx23885_get_input(file, priv, i);
-}
-
-int cx23885_set_input(struct file *file, void *priv, unsigned int i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- dprintk(1, "%s(%d)\n", __func__, i);
-
- if (i >= MAX_CX23885_INPUT) {
- dprintk(1, "%s() -EINVAL\n", __func__);
- return -EINVAL;
- }
-
- if (INPUT(i)->type == 0)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- cx23885_video_mux(dev, i);
-
- /* By default establish the default audio input for the card also */
- /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */
- cx23885_audio_mux(dev, i);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- return cx23885_set_input(file, priv, i);
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
-
- printk(KERN_INFO
- "%s/0: ============ START LOG STATUS ============\n",
- dev->name);
- call_all(dev, core, log_status);
- printk(KERN_INFO
- "%s/0: ============= END LOG STATUS =============\n",
- dev->name);
- return 0;
-}
-
-static int cx23885_query_audinput(struct file *file, void *priv,
- struct v4l2_audio *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- static const char *iname[] = {
- [0] = "Baseband L/R 1",
- [1] = "Baseband L/R 2",
- };
- unsigned int n;
- dprintk(1, "%s()\n", __func__);
-
- n = i->index;
- if (n >= 2)
- return -EINVAL;
-
- memset(i, 0, sizeof(*i));
- i->index = n;
- strcpy(i->name, iname[n]);
- i->capability = V4L2_AUDCAP_STEREO;
- i->mode = V4L2_AUDMODE_AVL;
- return 0;
-
-}
-
-static int vidioc_enum_audinput(struct file *file, void *priv,
- struct v4l2_audio *i)
-{
- return cx23885_query_audinput(file, priv, i);
-}
-
-static int vidioc_g_audinput(struct file *file, void *priv,
- struct v4l2_audio *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- i->index = dev->audinput;
- dprintk(1, "%s(input=%d)\n", __func__, i->index);
-
- return cx23885_query_audinput(file, priv, i);
-}
-
-static int vidioc_s_audinput(struct file *file, void *priv,
- struct v4l2_audio *i)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
- if (i->index >= 2)
- return -EINVAL;
-
- dprintk(1, "%s(%d)\n", __func__, i->index);
-
- dev->audinput = i->index;
-
- /* Skip the audio defaults from the cards struct, caller wants
- * directly touch the audio mux hardware. */
- cx23885_flatiron_mux(dev, dev->audinput + 1);
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
- if (unlikely(qctrl->id == 0))
- return -EINVAL;
- return cx23885_ctrl_query(qctrl);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- return cx23885_get_control(dev, ctl);
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- return cx23885_set_control(dev, ctl);
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Television");
-
- call_all(dev, tuner, g_tuner, t);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
-
- if (UNSET == dev->tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
- /* Update the A/V core */
- call_all(dev, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
-
- /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->freq;
-
- call_all(dev, tuner, g_frequency, f);
-
- return 0;
-}
-
-static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
-{
- struct v4l2_control ctrl;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
- if (unlikely(f->tuner != 0))
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- dev->freq = f->frequency;
-
- /* I need to mute audio here */
- ctrl.id = V4L2_CID_AUDIO_MUTE;
- ctrl.value = 1;
- cx23885_set_control(dev, &ctrl);
-
- call_all(dev, tuner, s_frequency, f);
-
- /* When changing channels it is required to reset TVAUDIO */
- msleep(100);
-
- /* I need to unmute audio here */
- ctrl.value = 0;
- cx23885_set_control(dev, &ctrl);
-
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int cx23885_set_freq_via_ops(struct cx23885_dev *dev,
- struct v4l2_frequency *f)
-{
- struct v4l2_control ctrl;
- struct videobuf_dvb_frontend *vfe;
- struct dvb_frontend *fe;
-
- struct analog_parameters params = {
- .mode = V4L2_TUNER_ANALOG_TV,
- .audmode = V4L2_TUNER_MODE_STEREO,
- .std = dev->tvnorm,
- .frequency = f->frequency
- };
-
- mutex_lock(&dev->lock);
- dev->freq = f->frequency;
-
- /* I need to mute audio here */
- ctrl.id = V4L2_CID_AUDIO_MUTE;
- ctrl.value = 1;
- cx23885_set_control(dev, &ctrl);
-
- /* If HVR1850 */
- dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__,
- params.frequency, f->tuner, params.std);
-
- vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1);
- if (!vfe) {
- mutex_unlock(&dev->lock);
- return -EINVAL;
- }
-
- fe = vfe->dvb.frontend;
-
- if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) ||
- (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111))
- fe = &dev->ts1.analog_fe;
-
- if (fe && fe->ops.tuner_ops.set_analog_params) {
- call_all(dev, core, s_std, dev->tvnorm);
- fe->ops.tuner_ops.set_analog_params(fe, &params);
- }
- else
- printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
-
- /* When changing channels it is required to reset TVAUDIO */
- msleep(100);
-
- /* I need to unmute audio here */
- ctrl.value = 0;
- cx23885_set_control(dev, &ctrl);
-
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-int cx23885_set_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx23885_fh *fh = priv;
- struct cx23885_dev *dev = fh->dev;
- int ret;
-
- switch (dev->board) {
- case CX23885_BOARD_HAUPPAUGE_HVR1255:
- case CX23885_BOARD_HAUPPAUGE_HVR1255_22111:
- case CX23885_BOARD_HAUPPAUGE_HVR1850:
- ret = cx23885_set_freq_via_ops(dev, f);
- break;
- default:
- ret = cx23885_set_freq(dev, f);
- }
-
- return ret;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- return cx23885_set_frequency(file, priv, f);
-}
-
-/* ----------------------------------------------------------- */
-
-static void cx23885_vid_timeout(unsigned long data)
-{
- struct cx23885_dev *dev = (struct cx23885_dev *)data;
- struct cx23885_dmaqueue *q = &dev->vidq;
- struct cx23885_buffer *buf;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->slock, flags);
- while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next,
- struct cx23885_buffer, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n",
- dev->name, buf, buf->vb.i,
- (unsigned long)buf->risc.dma);
- }
- cx23885_restart_video_queue(dev, q);
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
-{
- u32 mask, count;
- int handled = 0;
-
- mask = cx_read(VID_A_INT_MSK);
- if (0 == (status & mask))
- return handled;
-
- cx_write(VID_A_INT_STAT, status);
-
- /* risc op code error, fifo overflow or line sync detection error */
- if ((status & VID_BC_MSK_OPC_ERR) ||
- (status & VID_BC_MSK_SYNC) ||
- (status & VID_BC_MSK_OF)) {
-
- if (status & VID_BC_MSK_OPC_ERR) {
- dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n",
- VID_BC_MSK_OPC_ERR);
- printk(KERN_WARNING "%s: video risc op code error\n",
- dev->name);
- cx23885_sram_channel_dump(dev,
- &dev->sram_channels[SRAM_CH01]);
- }
-
- if (status & VID_BC_MSK_SYNC)
- dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) "
- "video lines miss-match\n",
- VID_BC_MSK_SYNC);
-
- if (status & VID_BC_MSK_OF)
- dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n",
- VID_BC_MSK_OF);
-
- }
-
- /* Video */
- if (status & VID_BC_MSK_RISCI1) {
- spin_lock(&dev->slock);
- count = cx_read(VID_A_GPCNT);
- cx23885_video_wakeup(dev, &dev->vidq, count);
- spin_unlock(&dev->slock);
- handled++;
- }
- if (status & VID_BC_MSK_RISCI2) {
- dprintk(2, "stopper video\n");
- spin_lock(&dev->slock);
- cx23885_restart_video_queue(dev, &dev->vidq);
- spin_unlock(&dev->slock);
- handled++;
- }
-
- /* Allow the VBI framework to process it's payload */
- handled += cx23885_vbi_irq(dev, status);
-
- return handled;
-}
-
-/* ----------------------------------------------------------- */
-/* exported stuff */
-
-static const struct v4l2_file_operations video_fops = {
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = video_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt,
- .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt,
- .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_querystd = vidioc_g_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_chip_ident = cx23885_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cx23885_g_register,
- .vidioc_s_register = cx23885_s_register,
-#endif
- .vidioc_enumaudio = vidioc_enum_audinput,
- .vidioc_g_audio = vidioc_g_audinput,
- .vidioc_s_audio = vidioc_s_audinput,
-};
-
-static struct video_device cx23885_vbi_template;
-static struct video_device cx23885_video_template = {
- .name = "cx23885-video",
- .fops = &video_fops,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX23885_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static const struct v4l2_file_operations radio_fops = {
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .ioctl = video_ioctl2,
-};
-
-
-void cx23885_video_unregister(struct cx23885_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
- cx23885_irq_remove(dev, 0x01);
-
- if (dev->vbi_dev) {
- if (video_is_registered(dev->vbi_dev))
- video_unregister_device(dev->vbi_dev);
- else
- video_device_release(dev->vbi_dev);
- dev->vbi_dev = NULL;
- btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
- }
- if (dev->video_dev) {
- if (video_is_registered(dev->video_dev))
- video_unregister_device(dev->video_dev);
- else
- video_device_release(dev->video_dev);
- dev->video_dev = NULL;
-
- btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
- }
-
- if (dev->audio_dev)
- cx23885_audio_unregister(dev);
-}
-
-int cx23885_video_register(struct cx23885_dev *dev)
-{
- int err;
-
- dprintk(1, "%s()\n", __func__);
- spin_lock_init(&dev->slock);
-
- /* Initialize VBI template */
- memcpy(&cx23885_vbi_template, &cx23885_video_template,
- sizeof(cx23885_vbi_template));
- strcpy(cx23885_vbi_template.name, "cx23885-vbi");
-
- dev->tvnorm = cx23885_video_template.current_norm;
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
- dev->vidq.timeout.function = cx23885_vid_timeout;
- dev->vidq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vidq.timeout);
- cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
- VID_A_DMA_CTL, 0x11, 0x00);
-
- /* init vbi dma queues */
- INIT_LIST_HEAD(&dev->vbiq.active);
- INIT_LIST_HEAD(&dev->vbiq.queued);
- dev->vbiq.timeout.function = cx23885_vbi_timeout;
- dev->vbiq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vbiq.timeout);
- cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
- VID_A_DMA_CTL, 0x22, 0x00);
-
- cx23885_irq_add_enable(dev, 0x01);
-
- if ((TUNER_ABSENT != dev->tuner_type) &&
- ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) {
- struct v4l2_subdev *sd = NULL;
-
- if (dev->tuner_addr)
- sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[dev->tuner_bus].i2c_adap,
- "tuner", dev->tuner_addr, NULL);
- else
- sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_bus[dev->tuner_bus].i2c_adap,
- "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV));
- if (sd) {
- struct tuner_setup tun_setup;
-
- memset(&tun_setup, 0, sizeof(tun_setup));
- tun_setup.mode_mask = T_ANALOG_TV;
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = v4l2_i2c_subdev_addr(sd);
- tun_setup.tuner_callback = cx23885_tuner_callback;
-
- v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
-
- if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) {
- struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64
- };
- struct v4l2_priv_tun_config cfg = {
- .tuner = dev->tuner_type,
- .priv = &ctrl
- };
- v4l2_subdev_call(sd, tuner, s_config, &cfg);
- }
- }
- }
-
- /* register Video device */
- dev->video_dev = cx23885_vdev_init(dev, dev->pci,
- &cx23885_video_template, "video");
- err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
- video_nr[dev->nr]);
- if (err < 0) {
- printk(KERN_INFO "%s: can't register video device\n",
- dev->name);
- goto fail_unreg;
- }
- printk(KERN_INFO "%s: registered device %s [v4l2]\n",
- dev->name, video_device_node_name(dev->video_dev));
-
- /* register VBI device */
- dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
- &cx23885_vbi_template, "vbi");
- err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[dev->nr]);
- if (err < 0) {
- printk(KERN_INFO "%s: can't register vbi device\n",
- dev->name);
- goto fail_unreg;
- }
- printk(KERN_INFO "%s: registered device %s\n",
- dev->name, video_device_node_name(dev->vbi_dev));
-
- /* Register ALSA audio device */
- dev->audio_dev = cx23885_audio_register(dev);
-
- /* initial device configuration */
- mutex_lock(&dev->lock);
- cx23885_set_tvnorm(dev, dev->tvnorm);
- init_controls(dev);
- cx23885_video_mux(dev, 0);
- cx23885_audio_mux(dev, 0);
- mutex_unlock(&dev->lock);
-
- return 0;
-
-fail_unreg:
- cx23885_video_unregister(dev);
- return err;
-}
-
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
deleted file mode 100644
index 5d560c747e09..000000000000
--- a/drivers/media/video/cx23885/cx23885.h
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * Driver for the Conexant CX23885 PCIe bridge
- *
- * Copyright (c) 2006 Steven Toth <stoth@linuxtv.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/kdev_t.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-device.h>
-#include <media/tuner.h>
-#include <media/tveeprom.h>
-#include <media/videobuf-dma-sg.h>
-#include <media/videobuf-dvb.h>
-#include <media/rc-core.h>
-
-#include "btcx-risc.h"
-#include "cx23885-reg.h"
-#include "media/cx2341x.h"
-
-#include <linux/mutex.h>
-
-#define CX23885_VERSION "0.0.3"
-
-#define UNSET (-1U)
-
-#define CX23885_MAXBOARDS 8
-
-/* Max number of inputs by card */
-#define MAX_CX23885_INPUT 8
-#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
-#define RESOURCE_OVERLAY 1
-#define RESOURCE_VIDEO 2
-#define RESOURCE_VBI 4
-
-#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
-
-#define CX23885_BOARD_NOAUTO UNSET
-#define CX23885_BOARD_UNKNOWN 0
-#define CX23885_BOARD_HAUPPAUGE_HVR1800lp 1
-#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
-#define CX23885_BOARD_HAUPPAUGE_HVR1250 3
-#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4
-#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
-#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
-#define CX23885_BOARD_HAUPPAUGE_HVR1200 7
-#define CX23885_BOARD_HAUPPAUGE_HVR1700 8
-#define CX23885_BOARD_HAUPPAUGE_HVR1400 9
-#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
-#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
-#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
-#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
-#define CX23885_BOARD_TBS_6920 14
-#define CX23885_BOARD_TEVII_S470 15
-#define CX23885_BOARD_DVBWORLD_2005 16
-#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17
-#define CX23885_BOARD_HAUPPAUGE_HVR1270 18
-#define CX23885_BOARD_HAUPPAUGE_HVR1275 19
-#define CX23885_BOARD_HAUPPAUGE_HVR1255 20
-#define CX23885_BOARD_HAUPPAUGE_HVR1210 21
-#define CX23885_BOARD_MYGICA_X8506 22
-#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23
-#define CX23885_BOARD_HAUPPAUGE_HVR1850 24
-#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25
-#define CX23885_BOARD_HAUPPAUGE_HVR1290 26
-#define CX23885_BOARD_MYGICA_X8558PRO 27
-#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28
-#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29
-#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30
-#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31
-#define CX23885_BOARD_MPX885 32
-#define CX23885_BOARD_MYGICA_X8507 33
-#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
-#define CX23885_BOARD_TEVII_S471 35
-#define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36
-
-#define GPIO_0 0x00000001
-#define GPIO_1 0x00000002
-#define GPIO_2 0x00000004
-#define GPIO_3 0x00000008
-#define GPIO_4 0x00000010
-#define GPIO_5 0x00000020
-#define GPIO_6 0x00000040
-#define GPIO_7 0x00000080
-#define GPIO_8 0x00000100
-#define GPIO_9 0x00000200
-#define GPIO_10 0x00000400
-#define GPIO_11 0x00000800
-#define GPIO_12 0x00001000
-#define GPIO_13 0x00002000
-#define GPIO_14 0x00004000
-#define GPIO_15 0x00008000
-
-/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
-#define CX23885_NORMS (\
- V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
- V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
- V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
- V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
-
-struct cx23885_fmt {
- char *name;
- u32 fourcc; /* v4l2 format id */
- int depth;
- int flags;
- u32 cxformat;
-};
-
-struct cx23885_ctrl {
- struct v4l2_queryctrl v;
- u32 off;
- u32 reg;
- u32 mask;
- u32 shift;
-};
-
-struct cx23885_tvnorm {
- char *name;
- v4l2_std_id id;
- u32 cxiformat;
- u32 cxoformat;
-};
-
-struct cx23885_fh {
- struct cx23885_dev *dev;
- enum v4l2_buf_type type;
- int radio;
- u32 resources;
-
- /* video overlay */
- struct v4l2_window win;
- struct v4l2_clip *clips;
- unsigned int nclips;
-
- /* video capture */
- struct cx23885_fmt *fmt;
- unsigned int width, height;
-
- /* vbi capture */
- struct videobuf_queue vidq;
- struct videobuf_queue vbiq;
-
- /* MPEG Encoder specifics ONLY */
- struct videobuf_queue mpegq;
- atomic_t v4l_reading;
-};
-
-enum cx23885_itype {
- CX23885_VMUX_COMPOSITE1 = 1,
- CX23885_VMUX_COMPOSITE2,
- CX23885_VMUX_COMPOSITE3,
- CX23885_VMUX_COMPOSITE4,
- CX23885_VMUX_SVIDEO,
- CX23885_VMUX_COMPONENT,
- CX23885_VMUX_TELEVISION,
- CX23885_VMUX_CABLE,
- CX23885_VMUX_DVB,
- CX23885_VMUX_DEBUG,
- CX23885_RADIO,
-};
-
-enum cx23885_src_sel_type {
- CX23885_SRC_SEL_EXT_656_VIDEO = 0,
- CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO
-};
-
-/* buffer for one video frame */
-struct cx23885_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- /* cx23885 specific */
- unsigned int bpl;
- struct btcx_riscmem risc;
- struct cx23885_fmt *fmt;
- u32 count;
-};
-
-struct cx23885_input {
- enum cx23885_itype type;
- unsigned int vmux;
- unsigned int amux;
- u32 gpio0, gpio1, gpio2, gpio3;
-};
-
-typedef enum {
- CX23885_MPEG_UNDEFINED = 0,
- CX23885_MPEG_DVB,
- CX23885_ANALOG_VIDEO,
- CX23885_MPEG_ENCODER,
-} port_t;
-
-struct cx23885_board {
- char *name;
- port_t porta, portb, portc;
- int num_fds_portb, num_fds_portc;
- unsigned int tuner_type;
- unsigned int radio_type;
- unsigned char tuner_addr;
- unsigned char radio_addr;
- unsigned int tuner_bus;
-
- /* Vendors can and do run the PCIe bridge at different
- * clock rates, driven physically by crystals on the PCBs.
- * The core has to accommodate this. This allows the user
- * to add new boards with new frequencys. The value is
- * expressed in Hz.
- *
- * The core framework will default this value based on
- * current designs, but it can vary.
- */
- u32 clk_freq;
- struct cx23885_input input[MAX_CX23885_INPUT];
- int ci_type; /* for NetUP */
- /* Force bottom field first during DMA (888 workaround) */
- u32 force_bff;
-};
-
-struct cx23885_subid {
- u16 subvendor;
- u16 subdevice;
- u32 card;
-};
-
-struct cx23885_i2c {
- struct cx23885_dev *dev;
-
- int nr;
-
- /* i2c i/o */
- struct i2c_adapter i2c_adap;
- struct i2c_client i2c_client;
- u32 i2c_rc;
-
- /* 885 registers used for raw addess */
- u32 i2c_period;
- u32 reg_ctrl;
- u32 reg_stat;
- u32 reg_addr;
- u32 reg_rdata;
- u32 reg_wdata;
-};
-
-struct cx23885_dmaqueue {
- struct list_head active;
- struct list_head queued;
- struct timer_list timeout;
- struct btcx_riscmem stopper;
- u32 count;
-};
-
-struct cx23885_tsport {
- struct cx23885_dev *dev;
-
- int nr;
- int sram_chno;
-
- struct videobuf_dvb_frontends frontends;
-
- /* dma queues */
- struct cx23885_dmaqueue mpegq;
- u32 ts_packet_size;
- u32 ts_packet_count;
-
- int width;
- int height;
-
- spinlock_t slock;
-
- /* registers */
- u32 reg_gpcnt;
- u32 reg_gpcnt_ctl;
- u32 reg_dma_ctl;
- u32 reg_lngth;
- u32 reg_hw_sop_ctrl;
- u32 reg_gen_ctrl;
- u32 reg_bd_pkt_status;
- u32 reg_sop_status;
- u32 reg_fifo_ovfl_stat;
- u32 reg_vld_misc;
- u32 reg_ts_clk_en;
- u32 reg_ts_int_msk;
- u32 reg_ts_int_stat;
- u32 reg_src_sel;
-
- /* Default register vals */
- int pci_irqmask;
- u32 dma_ctl_val;
- u32 ts_int_msk_val;
- u32 gen_ctrl_val;
- u32 ts_clk_en_val;
- u32 src_sel_val;
- u32 vld_misc_val;
- u32 hw_sop_ctrl_val;
-
- /* Allow a single tsport to have multiple frontends */
- u32 num_frontends;
- void (*gate_ctrl)(struct cx23885_tsport *port, int open);
- void *port_priv;
-
- /* Workaround for a temp dvb_frontend that the tuner can attached to */
- struct dvb_frontend analog_fe;
-};
-
-struct cx23885_kernel_ir {
- struct cx23885_dev *cx;
- char *name;
- char *phys;
-
- struct rc_dev *rc;
-};
-
-struct cx23885_audio_buffer {
- unsigned int bpl;
- struct btcx_riscmem risc;
- struct videobuf_dmabuf dma;
-};
-
-struct cx23885_audio_dev {
- struct cx23885_dev *dev;
-
- struct pci_dev *pci;
-
- struct snd_card *card;
-
- spinlock_t lock;
-
- atomic_t count;
-
- unsigned int dma_size;
- unsigned int period_size;
- unsigned int num_periods;
-
- struct videobuf_dmabuf *dma_risc;
-
- struct cx23885_audio_buffer *buf;
-
- struct snd_pcm_substream *substream;
-};
-
-struct cx23885_dev {
- atomic_t refcount;
- struct v4l2_device v4l2_dev;
-
- /* pci stuff */
- struct pci_dev *pci;
- unsigned char pci_rev, pci_lat;
- int pci_bus, pci_slot;
- u32 __iomem *lmmio;
- u8 __iomem *bmmio;
- int pci_irqmask;
- spinlock_t pci_irqmask_lock; /* protects mask reg too */
- int hwrevision;
-
- /* This valud is board specific and is used to configure the
- * AV core so we see nice clean and stable video and audio. */
- u32 clk_freq;
-
- /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
- struct cx23885_i2c i2c_bus[3];
-
- int nr;
- struct mutex lock;
- struct mutex gpio_lock;
-
- /* board details */
- unsigned int board;
- char name[32];
-
- struct cx23885_tsport ts1, ts2;
-
- /* sram configuration */
- struct sram_channel *sram_channels;
-
- enum {
- CX23885_BRIDGE_UNDEFINED = 0,
- CX23885_BRIDGE_885 = 885,
- CX23885_BRIDGE_887 = 887,
- CX23885_BRIDGE_888 = 888,
- } bridge;
-
- /* Analog video */
- u32 resources;
- unsigned int input;
- unsigned int audinput; /* Selectable audio input */
- u32 tvaudio;
- v4l2_std_id tvnorm;
- unsigned int tuner_type;
- unsigned char tuner_addr;
- unsigned int tuner_bus;
- unsigned int radio_type;
- unsigned char radio_addr;
- unsigned int has_radio;
- struct v4l2_subdev *sd_cx25840;
- struct work_struct cx25840_work;
-
- /* Infrared */
- struct v4l2_subdev *sd_ir;
- struct work_struct ir_rx_work;
- unsigned long ir_rx_notifications;
- struct work_struct ir_tx_work;
- unsigned long ir_tx_notifications;
-
- struct cx23885_kernel_ir *kernel_ir;
- atomic_t ir_input_stopping;
-
- /* V4l */
- u32 freq;
- struct video_device *video_dev;
- struct video_device *vbi_dev;
- struct video_device *radio_dev;
-
- struct cx23885_dmaqueue vidq;
- struct cx23885_dmaqueue vbiq;
- spinlock_t slock;
-
- /* MPEG Encoder ONLY settings */
- u32 cx23417_mailbox;
- struct cx2341x_mpeg_params mpeg_params;
- struct video_device *v4l_device;
- atomic_t v4l_reader_count;
- struct cx23885_tvnorm encodernorm;
-
- /* Analog raw audio */
- struct cx23885_audio_dev *audio_dev;
-
-};
-
-static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev);
-}
-
-#define call_all(dev, o, f, args...) \
- v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
-
-#define CX23885_HW_888_IR (1 << 0)
-#define CX23885_HW_AV_CORE (1 << 1)
-
-#define call_hw(dev, grpid, o, f, args...) \
- v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args)
-
-extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw);
-
-#define SRAM_CH01 0 /* Video A */
-#define SRAM_CH02 1 /* VBI A */
-#define SRAM_CH03 2 /* Video B */
-#define SRAM_CH04 3 /* Transport via B */
-#define SRAM_CH05 4 /* VBI B */
-#define SRAM_CH06 5 /* Video C */
-#define SRAM_CH07 6 /* Transport via C */
-#define SRAM_CH08 7 /* Audio Internal A */
-#define SRAM_CH09 8 /* Audio Internal B */
-#define SRAM_CH10 9 /* Audio External */
-#define SRAM_CH11 10 /* COMB_3D_N */
-#define SRAM_CH12 11 /* Comb 3D N1 */
-#define SRAM_CH13 12 /* Comb 3D N2 */
-#define SRAM_CH14 13 /* MOE Vid */
-#define SRAM_CH15 14 /* MOE RSLT */
-
-struct sram_channel {
- char *name;
- u32 cmds_start;
- u32 ctrl_start;
- u32 cdt;
- u32 fifo_start;
- u32 fifo_size;
- u32 ptr1_reg;
- u32 ptr2_reg;
- u32 cnt1_reg;
- u32 cnt2_reg;
- u32 jumponly;
-};
-
-/* ----------------------------------------------------------- */
-
-#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
-#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2))
-
-#define cx_andor(reg, mask, value) \
- writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
- ((value) & (mask)), dev->lmmio+((reg)>>2))
-
-#define cx_set(reg, bit) cx_andor((reg), (bit), (bit))
-#define cx_clear(reg, bit) cx_andor((reg), (bit), 0)
-
-/* ----------------------------------------------------------- */
-/* cx23885-core.c */
-
-extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
- struct sram_channel *ch,
- unsigned int bpl, u32 risc);
-
-extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
- struct sram_channel *ch);
-
-extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value);
-
-extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist,
- unsigned int top_offset, unsigned int bottom_offset,
- unsigned int bpl, unsigned int padding, unsigned int lines);
-
-extern int cx23885_risc_vbibuffer(struct pci_dev *pci,
- struct btcx_riscmem *risc, struct scatterlist *sglist,
- unsigned int top_offset, unsigned int bottom_offset,
- unsigned int bpl, unsigned int padding, unsigned int lines);
-
-void cx23885_cancel_buffers(struct cx23885_tsport *port);
-
-extern int cx23885_restart_queue(struct cx23885_tsport *port,
- struct cx23885_dmaqueue *q);
-
-extern void cx23885_wakeup(struct cx23885_tsport *port,
- struct cx23885_dmaqueue *q, u32 count);
-
-extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask);
-extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask);
-extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask);
-extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask,
- int asoutput);
-
-extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask);
-extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask);
-extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask);
-extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask);
-
-/* ----------------------------------------------------------- */
-/* cx23885-cards.c */
-extern struct cx23885_board cx23885_boards[];
-extern const unsigned int cx23885_bcount;
-
-extern struct cx23885_subid cx23885_subids[];
-extern const unsigned int cx23885_idcount;
-
-extern int cx23885_tuner_callback(void *priv, int component,
- int command, int arg);
-extern void cx23885_card_list(struct cx23885_dev *dev);
-extern int cx23885_ir_init(struct cx23885_dev *dev);
-extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev);
-extern void cx23885_ir_fini(struct cx23885_dev *dev);
-extern void cx23885_gpio_setup(struct cx23885_dev *dev);
-extern void cx23885_card_setup(struct cx23885_dev *dev);
-extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
-
-extern int cx23885_dvb_register(struct cx23885_tsport *port);
-extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
-
-extern int cx23885_buf_prepare(struct videobuf_queue *q,
- struct cx23885_tsport *port,
- struct cx23885_buffer *buf,
- enum v4l2_field field);
-extern void cx23885_buf_queue(struct cx23885_tsport *port,
- struct cx23885_buffer *buf);
-extern void cx23885_free_buffer(struct videobuf_queue *q,
- struct cx23885_buffer *buf);
-
-/* ----------------------------------------------------------- */
-/* cx23885-video.c */
-/* Video */
-extern int cx23885_video_register(struct cx23885_dev *dev);
-extern void cx23885_video_unregister(struct cx23885_dev *dev);
-extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
-extern void cx23885_video_wakeup(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q, u32 count);
-int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i);
-int cx23885_set_input(struct file *file, void *priv, unsigned int i);
-int cx23885_get_input(struct file *file, void *priv, unsigned int *i);
-int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f);
-int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
-int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl);
-int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm);
-
-/* ----------------------------------------------------------- */
-/* cx23885-vbi.c */
-extern int cx23885_vbi_fmt(struct file *file, void *priv,
- struct v4l2_format *f);
-extern void cx23885_vbi_timeout(unsigned long data);
-extern struct videobuf_queue_ops cx23885_vbi_qops;
-extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
- struct cx23885_dmaqueue *q);
-extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status);
-
-/* cx23885-i2c.c */
-extern int cx23885_i2c_register(struct cx23885_i2c *bus);
-extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
-extern void cx23885_av_clk(struct cx23885_dev *dev, int enable);
-
-/* ----------------------------------------------------------- */
-/* cx23885-417.c */
-extern int cx23885_417_register(struct cx23885_dev *dev);
-extern void cx23885_417_unregister(struct cx23885_dev *dev);
-extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status);
-extern void cx23885_417_check_encoder(struct cx23885_dev *dev);
-extern void cx23885_mc417_init(struct cx23885_dev *dev);
-extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value);
-extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value);
-extern int mc417_register_read(struct cx23885_dev *dev,
- u16 address, u32 *value);
-extern int mc417_register_write(struct cx23885_dev *dev,
- u16 address, u32 value);
-extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask);
-extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask);
-extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput);
-
-/* ----------------------------------------------------------- */
-/* cx23885-alsa.c */
-extern struct cx23885_audio_dev *cx23885_audio_register(
- struct cx23885_dev *dev);
-extern void cx23885_audio_unregister(struct cx23885_dev *dev);
-extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask);
-extern int cx23885_risc_databuffer(struct pci_dev *pci,
- struct btcx_riscmem *risc,
- struct scatterlist *sglist,
- unsigned int bpl,
- unsigned int lines,
- unsigned int lpi);
-
-/* ----------------------------------------------------------- */
-/* tv norms */
-
-static inline unsigned int norm_maxw(v4l2_std_id norm)
-{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
-}
-
-static inline unsigned int norm_maxh(v4l2_std_id norm)
-{
- return (norm & V4L2_STD_625_50) ? 576 : 480;
-}
-
-static inline unsigned int norm_swidth(v4l2_std_id norm)
-{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
-}
diff --git a/drivers/media/video/cx25821/Makefile b/drivers/media/video/cx25821/Makefile
deleted file mode 100644
index aedde18c68f9..000000000000
--- a/drivers/media/video/cx25821/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \
- cx25821-gpio.o cx25821-medusa-video.o \
- cx25821-video.o cx25821-video-upstream.o \
- cx25821-video-upstream-ch2.o \
- cx25821-audio-upstream.o
-
-obj-$(CONFIG_VIDEO_CX25821) += cx25821.o
-obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o
-
-ccflags-y := -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
deleted file mode 100644
index b38d4379cc36..000000000000
--- a/drivers/media/video/cx25821/cx25821-video.c
+++ /dev/null
@@ -1,1990 +0,0 @@
-/*
- * Driver for the Conexant CX25821 PCIe bridge
- *
- * Copyright (C) 2009 Conexant Systems Inc.
- * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
- * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
- * Parts adapted/taken from Eduardo Moscoso Rubino
- * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "cx25821-video.h"
-
-MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
-MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
-MODULE_LICENSE("GPL");
-
-static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-
-MODULE_PARM_DESC(video_nr, "video device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
-
-static unsigned int video_debug = VIDEO_DEBUG;
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-
-static unsigned int irq_debug;
-module_param(irq_debug, int, 0644);
-MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
-
-unsigned int vid_limit = 16;
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
-static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num);
-
-static const struct v4l2_file_operations video_fops;
-static const struct v4l2_ioctl_ops video_ioctl_ops;
-
-#define FORMAT_FLAGS_PACKED 0x01
-
-struct cx25821_fmt formats[] = {
- {
- .name = "8 bpp, gray",
- .fourcc = V4L2_PIX_FMT_GREY,
- .depth = 8,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:1:1, packed, Y41P",
- .fourcc = V4L2_PIX_FMT_Y41P,
- .depth = 12,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- }, {
- .name = "4:2:0, YUV",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .flags = FORMAT_FLAGS_PACKED,
- },
-};
-
-int cx25821_get_format_size(void)
-{
- return ARRAY_SIZE(formats);
-}
-
-struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P)
- return formats + 1;
-
- for (i = 0; i < ARRAY_SIZE(formats); i++)
- if (formats[i].fourcc == fourcc)
- return formats + i;
-
- pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc);
- return NULL;
-}
-
-void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
- u32 count)
-{
- struct cx25821_buffer *buf;
- int bc;
-
- for (bc = 0;; bc++) {
- if (list_empty(&q->active)) {
- dprintk(1, "bc=%d (=0: active empty)\n", bc);
- break;
- }
-
- buf = list_entry(q->active.next, struct cx25821_buffer,
- vb.queue);
-
- /* count comes from the hw and it is 16bit wide --
- * this trick handles wrap-arounds correctly for
- * up to 32767 buffers in flight... */
- if ((s16) (count - buf->count) < 0)
- break;
-
- do_gettimeofday(&buf->vb.ts);
- buf->vb.state = VIDEOBUF_DONE;
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- }
-
- if (list_empty(&q->active))
- del_timer(&q->timeout);
- else
- mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
- if (bc != 1)
- pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc);
-}
-
-#ifdef TUNER_FLAG
-int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm)
-{
- dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
- __func__, (unsigned int)norm, v4l2_norm_to_name(norm));
-
- dev->tvnorm = norm;
-
- /* Tell the internal A/V decoder */
- cx25821_call_all(dev, core, s_std, norm);
-
- return 0;
-}
-#endif
-
-struct video_device *cx25821_vdev_init(struct cx25821_dev *dev,
- struct pci_dev *pci,
- struct video_device *template,
- char *type)
-{
- struct video_device *vfd;
- dprintk(1, "%s()\n", __func__);
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type,
- cx25821_boards[dev->board].name);
- video_set_drvdata(vfd, dev);
- return vfd;
-}
-
-/*
-static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
-{
- int i;
-
- if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- for (i = 0; i < CX25821_CTLS; i++)
- if (cx25821_ctls[i].v.id == qctrl->id)
- break;
- if (i == CX25821_CTLS) {
- *qctrl = no_ctl;
- return 0;
- }
- *qctrl = cx25821_ctls[i].v;
- return 0;
-}
-*/
-
-/* resource management */
-int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bit)
-{
- dprintk(1, "%s()\n", __func__);
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&dev->lock);
- if (dev->channels[fh->channel_id].resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&dev->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->channels[fh->channel_id].resources |= bit;
- dprintk(1, "res: get %d\n", bit);
- mutex_unlock(&dev->lock);
- return 1;
-}
-
-int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit)
-{
- return fh->resources & bit;
-}
-
-int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit)
-{
- return fh->dev->channels[fh->channel_id].resources & bit;
-}
-
-void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bits)
-{
- BUG_ON((fh->resources & bits) != bits);
- dprintk(1, "%s()\n", __func__);
-
- mutex_lock(&dev->lock);
- fh->resources &= ~bits;
- dev->channels[fh->channel_id].resources &= ~bits;
- dprintk(1, "res: put %d\n", bits);
- mutex_unlock(&dev->lock);
-}
-
-int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input)
-{
- struct v4l2_routing route;
- memset(&route, 0, sizeof(route));
-
- dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
- __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0,
- INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3);
- dev->input = input;
-
- route.input = INPUT(input)->vmux;
-
- /* Tell the internal A/V decoder */
- cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0);
-
- return 0;
-}
-
-int cx25821_start_video_dma(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q,
- struct cx25821_buffer *buf,
- struct sram_channel *channel)
-{
- int tmp = 0;
-
- /* setup fifo + format */
- cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma);
-
- /* reset counter */
- cx_write(channel->gpcnt_ctl, 3);
- q->count = 1;
-
- /* enable irq */
- cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
- cx_set(channel->int_msk, 0x11);
-
- /* start dma */
- cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */
-
- /* make sure upstream setting if any is reversed */
- tmp = cx_read(VID_CH_MODE_SEL);
- cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
-
- return 0;
-}
-
-int cx25821_restart_video_queue(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q,
- struct sram_channel *channel)
-{
- struct cx25821_buffer *buf, *prev;
- struct list_head *item;
-
- if (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx25821_buffer,
- vb.queue);
-
- cx25821_start_video_dma(dev, q, buf, channel);
-
- list_for_each(item, &q->active) {
- buf = list_entry(item, struct cx25821_buffer, vb.queue);
- buf->count = q->count++;
- }
-
- mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
- return 0;
- }
-
- prev = NULL;
- for (;;) {
- if (list_empty(&q->queued))
- return 0;
-
- buf = list_entry(q->queued.next, struct cx25821_buffer,
- vb.queue);
-
- if (NULL == prev) {
- list_move_tail(&buf->vb.queue, &q->active);
- cx25821_start_video_dma(dev, q, buf, channel);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
- } else {
- return 0;
- }
- prev = buf;
- }
-}
-
-void cx25821_vid_timeout(unsigned long data)
-{
- struct cx25821_data *timeout_data = (struct cx25821_data *)data;
- struct cx25821_dev *dev = timeout_data->dev;
- struct sram_channel *channel = timeout_data->channel;
- struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq;
- struct cx25821_buffer *buf;
- unsigned long flags;
-
- /* cx25821_sram_channel_dump(dev, channel); */
- cx_clear(channel->dma_ctl, 0x11);
-
- spin_lock_irqsave(&dev->slock, flags);
- while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx25821_buffer,
- vb.queue);
- list_del(&buf->vb.queue);
-
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- }
-
- cx25821_restart_video_queue(dev, q, channel);
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
-{
- u32 count = 0;
- int handled = 0;
- u32 mask;
- struct sram_channel *channel = dev->channels[chan_num].sram_channels;
-
- mask = cx_read(channel->int_msk);
- if (0 == (status & mask))
- return handled;
-
- cx_write(channel->int_stat, status);
-
- /* risc op code error */
- if (status & (1 << 16)) {
- pr_warn("%s, %s: video risc op code error\n",
- dev->name, channel->name);
- cx_clear(channel->dma_ctl, 0x11);
- cx25821_sram_channel_dump(dev, channel);
- }
-
- /* risc1 y */
- if (status & FLD_VID_DST_RISC1) {
- spin_lock(&dev->slock);
- count = cx_read(channel->gpcnt);
- cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq,
- count);
- spin_unlock(&dev->slock);
- handled++;
- }
-
- /* risc2 y */
- if (status & 0x10) {
- dprintk(2, "stopper video\n");
- spin_lock(&dev->slock);
- cx25821_restart_video_queue(dev,
- &dev->channels[channel->i].vidq, channel);
- spin_unlock(&dev->slock);
- handled++;
- }
- return handled;
-}
-
-void cx25821_videoioctl_unregister(struct cx25821_dev *dev)
-{
- if (dev->ioctl_dev) {
- if (video_is_registered(dev->ioctl_dev))
- video_unregister_device(dev->ioctl_dev);
- else
- video_device_release(dev->ioctl_dev);
-
- dev->ioctl_dev = NULL;
- }
-}
-
-void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
-{
- cx_clear(PCI_INT_MSK, 1);
-
- if (dev->channels[chan_num].video_dev) {
- if (video_is_registered(dev->channels[chan_num].video_dev))
- video_unregister_device(
- dev->channels[chan_num].video_dev);
- else
- video_device_release(
- dev->channels[chan_num].video_dev);
-
- dev->channels[chan_num].video_dev = NULL;
-
- btcx_riscmem_free(dev->pci,
- &dev->channels[chan_num].vidq.stopper);
-
- pr_warn("device %d released!\n", chan_num);
- }
-
-}
-
-int cx25821_video_register(struct cx25821_dev *dev)
-{
- int err;
- int i;
-
- struct video_device cx25821_video_device = {
- .name = "cx25821-video",
- .fops = &video_fops,
- .minor = -1,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX25821_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
- };
-
- spin_lock_init(&dev->slock);
-
- for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) {
- cx25821_init_controls(dev, i);
-
- cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper,
- dev->channels[i].sram_channels->dma_ctl, 0x11, 0);
-
- dev->channels[i].sram_channels = &cx25821_sram_channels[i];
- dev->channels[i].video_dev = NULL;
- dev->channels[i].resources = 0;
-
- cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff);
-
- INIT_LIST_HEAD(&dev->channels[i].vidq.active);
- INIT_LIST_HEAD(&dev->channels[i].vidq.queued);
-
- dev->channels[i].timeout_data.dev = dev;
- dev->channels[i].timeout_data.channel =
- &cx25821_sram_channels[i];
- dev->channels[i].vidq.timeout.function = cx25821_vid_timeout;
- dev->channels[i].vidq.timeout.data =
- (unsigned long)&dev->channels[i].timeout_data;
- init_timer(&dev->channels[i].vidq.timeout);
-
- /* register v4l devices */
- dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci,
- &cx25821_video_device, "video");
-
- err = video_register_device(dev->channels[i].video_dev,
- VFL_TYPE_GRABBER, video_nr[dev->nr]);
-
- if (err < 0)
- goto fail_unreg;
-
- }
-
- /* set PCI interrupt */
- cx_set(PCI_INT_MSK, 0xff);
-
- /* initial device configuration */
- mutex_lock(&dev->lock);
-#ifdef TUNER_FLAG
- dev->tvnorm = cx25821_video_device.current_norm;
- cx25821_set_tvnorm(dev, dev->tvnorm);
-#endif
- mutex_unlock(&dev->lock);
-
- return 0;
-
-fail_unreg:
- cx25821_video_unregister(dev, i);
- return err;
-}
-
-int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
-{
- struct cx25821_fh *fh = q->priv_data;
-
- *size = fh->fmt->depth * fh->width * fh->height >> 3;
-
- if (0 == *count)
- *count = 32;
-
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
-
- return 0;
-}
-
-int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx25821_fh *fh = q->priv_data;
- struct cx25821_dev *dev = fh->dev;
- struct cx25821_buffer *buf =
- container_of(vb, struct cx25821_buffer, vb);
- int rc, init_buffer = 0;
- u32 line0_offset;
- struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
- int bpl_local = LINE_SIZE_D1;
- int channel_opened = fh->channel_id;
-
- BUG_ON(NULL == fh->fmt);
- if (fh->width < 48 || fh->width > 720 ||
- fh->height < 32 || fh->height > 576)
- return -EINVAL;
-
- buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
-
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- if (buf->fmt != fh->fmt ||
- buf->vb.width != fh->width ||
- buf->vb.height != fh->height || buf->vb.field != field) {
- buf->fmt = fh->fmt;
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.field = field;
- init_buffer = 1;
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- init_buffer = 1;
- rc = videobuf_iolock(q, &buf->vb, NULL);
- if (0 != rc) {
- printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n"));
- goto fail;
- }
- }
-
- dprintk(1, "init_buffer=%d\n", init_buffer);
-
- if (init_buffer) {
-
- channel_opened = dev->channel_opened;
- if (channel_opened < 0 || channel_opened > 7)
- channel_opened = 7;
-
- if (dev->channels[channel_opened].pixel_formats ==
- PIXEL_FRMT_411)
- buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3;
- else
- buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width);
-
- if (dev->channels[channel_opened].pixel_formats ==
- PIXEL_FRMT_411) {
- bpl_local = buf->bpl;
- } else {
- bpl_local = buf->bpl; /* Default */
-
- if (channel_opened >= 0 && channel_opened <= 7) {
- if (dev->channels[channel_opened]
- .use_cif_resolution) {
- if (dev->tvnorm & V4L2_STD_PAL_BG ||
- dev->tvnorm & V4L2_STD_PAL_DK)
- bpl_local = 352 << 1;
- else
- bpl_local = dev->channels[
- channel_opened].
- cif_width << 1;
- }
- }
- }
-
- switch (buf->vb.field) {
- case V4L2_FIELD_TOP:
- cx25821_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, UNSET,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_BOTTOM:
- cx25821_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, UNSET, 0,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_INTERLACED:
- /* All other formats are top field first */
- line0_offset = 0;
- dprintk(1, "top field first\n");
-
- cx25821_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, line0_offset,
- bpl_local, bpl_local, bpl_local,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_TB:
- cx25821_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- 0, buf->bpl * (buf->vb.height >> 1),
- buf->bpl, 0, buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_BT:
- cx25821_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- buf->bpl * (buf->vb.height >> 1), 0,
- buf->bpl, 0, buf->vb.height >> 1);
- break;
- default:
- BUG();
- }
- }
-
- dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
- buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth,
- fh->fmt->name, (unsigned long)buf->risc.dma);
-
- buf->vb.state = VIDEOBUF_PREPARED;
-
- return 0;
-
-fail:
- cx25821_free_buffer(q, buf);
- return rc;
-}
-
-void cx25821_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct cx25821_buffer *buf =
- container_of(vb, struct cx25821_buffer, vb);
-
- cx25821_free_buffer(q, buf);
-}
-
-struct videobuf_queue *get_queue(struct cx25821_fh *fh)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &fh->vidq;
- default:
- BUG();
- return NULL;
- }
-}
-
-int cx25821_get_resource(struct cx25821_fh *fh, int resource)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return resource;
- default:
- BUG();
- return 0;
- }
-}
-
-int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct cx25821_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(get_queue(fh), vma);
-}
-
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct cx25821_buffer *buf =
- container_of(vb, struct cx25821_buffer, vb);
- struct cx25821_buffer *prev;
- struct cx25821_fh *fh = vq->priv_data;
- struct cx25821_dev *dev = fh->dev;
- struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq;
-
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
- buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
-
- dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]);
-
- if (!list_empty(&q->queued)) {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf,
- buf->vb.i);
-
- } else if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue, &q->active);
- cx25821_start_video_dma(dev, q, buf,
- dev->channels[fh->channel_id].sram_channels);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT);
- dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n",
- buf, buf->vb.i, buf->count, q->count);
- } else {
- prev = list_entry(q->active.prev, struct cx25821_buffer,
- vb.queue);
- if (prev->vb.width == buf->vb.width
- && prev->vb.height == buf->vb.height
- && prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-
- /* 64 bit bits 63-32 */
- prev->risc.jmp[2] = cpu_to_le32(0);
- dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n",
- buf, buf->vb.i, buf->count);
-
- } else {
- list_add_tail(&buf->vb.queue, &q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf,
- buf->vb.i);
- }
- }
-
- if (list_empty(&q->active))
- dprintk(2, "active queue empty!\n");
-}
-
-static struct videobuf_queue_ops cx25821_video_qops = {
- .buf_setup = cx25821_buffer_setup,
- .buf_prepare = cx25821_buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = cx25821_buffer_release,
-};
-
-static int video_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx25821_dev *h, *dev = video_drvdata(file);
- struct cx25821_fh *fh;
- struct list_head *list;
- int minor = video_devdata(file)->minor;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- u32 pix_format;
- int ch_id = 0;
- int i;
-
- dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev),
- v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
-
- mutex_lock(&cx25821_devlist_mutex);
-
- list_for_each(list, &cx25821_devlist)
- {
- h = list_entry(list, struct cx25821_dev, devlist);
-
- for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) {
- if (h->channels[i].video_dev &&
- h->channels[i].video_dev->minor == minor) {
- dev = h;
- ch_id = i;
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- }
- }
- }
-
- if (NULL == dev) {
- mutex_unlock(&cx25821_devlist_mutex);
- kfree(fh);
- return -ENODEV;
- }
-
- file->private_data = fh;
- fh->dev = dev;
- fh->type = type;
- fh->width = 720;
- fh->channel_id = ch_id;
-
- if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK)
- fh->height = 576;
- else
- fh->height = 480;
-
- dev->channel_opened = fh->channel_id;
- if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411)
- pix_format = V4L2_PIX_FMT_Y41P;
- else
- pix_format = V4L2_PIX_FMT_YUYV;
- fh->fmt = cx25821_format_by_fourcc(pix_format);
-
- v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio);
-
- videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev,
- &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer),
- fh, NULL);
-
- dprintk(1, "post videobuf_queue_init()\n");
- mutex_unlock(&cx25821_devlist_mutex);
-
- return 0;
-}
-
-static ssize_t video_read(struct file *file, char __user * data, size_t count,
- loff_t *ppos)
-{
- struct cx25821_fh *fh = file->private_data;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (cx25821_res_locked(fh, RESOURCE_VIDEO0))
- return -EBUSY;
-
- return videobuf_read_one(&fh->vidq, data, count, ppos,
- file->f_flags & O_NONBLOCK);
-
- default:
- BUG();
- return 0;
- }
-}
-
-static unsigned int video_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_buffer *buf;
-
- if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
- /* streaming capture */
- if (list_empty(&fh->vidq.stream))
- return POLLERR;
- buf = list_entry(fh->vidq.stream.next,
- struct cx25821_buffer, vb.stream);
- } else {
- /* read() capture */
- buf = (struct cx25821_buffer *)fh->vidq.read_buf;
- if (NULL == buf)
- return POLLERR;
- }
-
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) {
- if (buf->vb.state == VIDEOBUF_DONE) {
- struct cx25821_dev *dev = fh->dev;
-
- if (dev && dev->channels[fh->channel_id]
- .use_cif_resolution) {
- u8 cam_id = *((char *)buf->vb.baddr + 3);
- memcpy((char *)buf->vb.baddr,
- (char *)buf->vb.baddr + (fh->width * 2),
- (fh->width * 2));
- *((char *)buf->vb.baddr + 3) = cam_id;
- }
- }
-
- return POLLIN | POLLRDNORM;
- }
-
- return 0;
-}
-
-static int video_release(struct file *file)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
-
- /* stop the risc engine and fifo */
- cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */
-
- /* stop video capture */
- if (cx25821_res_check(fh, RESOURCE_VIDEO0)) {
- videobuf_queue_cancel(&fh->vidq);
- cx25821_res_free(dev, fh, RESOURCE_VIDEO0);
- }
-
- if (fh->vidq.read_buf) {
- cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf);
- kfree(fh->vidq.read_buf);
- }
-
- videobuf_mmap_free(&fh->vidq);
-
- v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
-
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = fh->dev;
-
- if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
- return -EINVAL;
-
- if (unlikely(i != fh->type))
- return -EINVAL;
-
- if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh,
- RESOURCE_VIDEO0))))
- return -EBUSY;
-
- return videobuf_streamon(get_queue(fh));
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = fh->dev;
- int err, res;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (i != fh->type)
- return -EINVAL;
-
- res = cx25821_get_resource(fh, RESOURCE_VIDEO0);
- err = videobuf_streamoff(get_queue(fh));
- if (err < 0)
- return err;
- cx25821_res_free(dev, fh, res);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct v4l2_mbus_framefmt mbus_fmt;
- int err;
- int pix_format = PIXEL_FRMT_422;
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
-
- dprintk(2, "%s()\n", __func__);
- err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
-
- if (0 != err)
- return err;
-
- fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
- fh->vidq.field = f->fmt.pix.field;
-
- /* check if width and height is valid based on set standard */
- if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm))
- fh->width = f->fmt.pix.width;
-
- if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm))
- fh->height = f->fmt.pix.height;
-
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
- pix_format = PIXEL_FRMT_411;
- else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV)
- pix_format = PIXEL_FRMT_422;
- else
- return -EINVAL;
-
- cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
-
- /* check if cif resolution */
- if (fh->width == 320 || fh->width == 352)
- dev->channels[fh->channel_id].use_cif_resolution = 1;
- else
- dev->channels[fh->channel_id].use_cif_resolution = 0;
-
- dev->channels[fh->channel_id].cif_width = fh->width;
- medusa_set_resolution(dev, fh->width, SRAM_CH00);
-
- dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width,
- fh->height, fh->vidq.field);
- v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED);
- cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt);
-
- return 0;
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int ret_val = 0;
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
-
- ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK);
-
- p->sequence = dev->channels[fh->channel_id].vidq.count;
-
- return ret_val;
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
- char name[32 + 2];
-
- struct sram_channel *sram_ch = dev->channels[fh->channel_id]
- .sram_channels;
- u32 tmp = 0;
-
- snprintf(name, sizeof(name), "%s/2", dev->name);
- pr_info("%s/2: ============ START LOG STATUS ============\n",
- dev->name);
- cx25821_call_all(dev, core, log_status);
- tmp = cx_read(sram_ch->dma_ctl);
- pr_info("Video input 0 is %s\n",
- (tmp & 0x11) ? "streaming" : "stopped");
- pr_info("%s/2: ============= END LOG STATUS =============\n",
- dev->name);
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- int err;
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
-
- return cx25821_set_control(dev, ctl, fh->channel_id);
-}
-
-/* VIDEO IOCTLS */
-int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx25821_fh *fh = priv;
-
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->vidq.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx25821_fmt *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
-
- fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = 720;
- maxh = 576;
-
- if (V4L2_FIELD_ANY == field) {
- if (f->fmt.pix.height > maxh / 2)
- field = V4L2_FIELD_INTERLACED;
- else
- field = V4L2_FIELD_TOP;
- }
-
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-int cx25821_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
-
- strcpy(cap->driver, "cx25821");
- strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card));
- sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
- cap->version = CX25821_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
- if (UNSET != dev->tuner_type)
- cap->capabilities |= V4L2_CAP_TUNER;
- return 0;
-}
-
-int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(formats)))
- return -EINVAL;
-
- strlcpy(f->description, formats[f->index].name, sizeof(f->description));
- f->pixelformat = formats[f->index].fourcc;
-
- return 0;
-}
-
-int cx25821_vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct cx25821_fh *fh = priv;
- return videobuf_reqbufs(get_queue(fh), p);
-}
-
-int cx25821_vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct cx25821_fh *fh = priv;
- return videobuf_querybuf(get_queue(fh), p);
-}
-
-int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx25821_fh *fh = priv;
- return videobuf_qbuf(get_queue(fh), p);
-}
-
-int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
- struct cx25821_fh *fh = f;
-
- *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio);
-
- return 0;
-}
-
-int cx25821_vidioc_s_priority(struct file *file, void *f,
- enum v4l2_priority prio)
-{
- struct cx25821_fh *fh = f;
- struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev;
-
- return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio,
- prio);
-}
-
-#ifdef TUNER_FLAG
-int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- int err;
-
- dprintk(1, "%s()\n", __func__);
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
-
- if (dev->tvnorm == *tvnorms)
- return 0;
-
- mutex_lock(&dev->lock);
- cx25821_set_tvnorm(dev, *tvnorms);
- mutex_unlock(&dev->lock);
-
- medusa_set_videostandard(dev);
-
- return 0;
-}
-#endif
-
-int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i)
-{
- static const char * const iname[] = {
- [CX25821_VMUX_COMPOSITE] = "Composite",
- [CX25821_VMUX_SVIDEO] = "S-Video",
- [CX25821_VMUX_DEBUG] = "for debug only",
- };
- unsigned int n;
- dprintk(1, "%s()\n", __func__);
-
- n = i->index;
- if (n >= 2)
- return -EINVAL;
-
- if (0 == INPUT(n)->type)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, iname[INPUT(n)->type]);
-
- i->std = CX25821_NORMS;
- return 0;
-}
-
-int cx25821_vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- dprintk(1, "%s()\n", __func__);
- return cx25821_enum_input(dev, i);
-}
-
-int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
-
- *i = dev->input;
- dprintk(1, "%s(): returns %d\n", __func__, *i);
- return 0;
-}
-
-int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- int err;
-
- dprintk(1, "%s(%d)\n", __func__, i);
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
-
- if (i >= CX25821_NR_INPUT) {
- dprintk(1, "%s(): -EINVAL\n", __func__);
- return -EINVAL;
- }
-
- mutex_lock(&dev->lock);
- cx25821_video_mux(dev, i);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-#ifdef TUNER_FLAG
-int cx25821_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev = fh->dev;
-
- f->frequency = dev->freq;
-
- cx25821_call_all(dev, tuner, g_frequency, f);
-
- return 0;
-}
-
-int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f)
-{
- mutex_lock(&dev->lock);
- dev->freq = f->frequency;
-
- cx25821_call_all(dev, tuner, s_frequency, f);
-
- /* When changing channels it is required to reset TVAUDIO */
- msleep(10);
-
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-int cx25821_vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx25821_fh *fh = priv;
- struct cx25821_dev *dev;
- int err;
-
- if (fh) {
- dev = fh->dev;
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- } else {
- pr_err("Invalid fh pointer!\n");
- return -EINVAL;
- }
-
- return cx25821_set_freq(dev, f);
-}
-#endif
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-int cx25821_vidioc_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- cx25821_call_all(dev, core, g_register, reg);
-
- return 0;
-}
-
-int cx25821_vidioc_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
-
- cx25821_call_all(dev, core, s_register, reg);
-
- return 0;
-}
-
-#endif
-
-#ifdef TUNER_FLAG
-int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
-
- t->signal = 0xffff; /* LOCKED */
- return 0;
-}
-
-int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
- int err;
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
-
- dprintk(1, "%s()\n", __func__);
- if (UNSET == dev->tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- return 0;
-}
-
-#endif
-/*****************************************************************************/
-static const struct v4l2_queryctrl no_ctl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-
-static struct v4l2_queryctrl cx25821_ctls[] = {
- /* --- video --- */
- {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 10000,
- .step = 1,
- .default_value = 6200,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }, {
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 10000,
- .step = 1,
- .default_value = 5000,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }, {
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 10000,
- .step = 1,
- .default_value = 5000,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }, {
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = 0,
- .maximum = 10000,
- .step = 1,
- .default_value = 5000,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-};
-static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls);
-
-static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl)
-{
- int i;
-
- if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- for (i = 0; i < CX25821_CTLS; i++)
- if (cx25821_ctls[i].id == qctrl->id)
- break;
- if (i == CX25821_CTLS) {
- *qctrl = no_ctl;
- return 0;
- }
- *qctrl = cx25821_ctls[i];
- return 0;
-}
-
-int cx25821_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- return cx25821_ctrl_query(qctrl);
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO CTRL IOCTLS */
-
-static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id)
-{
- unsigned int i;
-
- for (i = 0; i < CX25821_CTLS; i++)
- if (cx25821_ctls[i].id == id)
- return cx25821_ctls + i;
- return NULL;
-}
-
-int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
-
- const struct v4l2_queryctrl *ctrl;
-
- ctrl = ctrl_by_id(ctl->id);
-
- if (NULL == ctrl)
- return -EINVAL;
- switch (ctl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctl->value = dev->channels[fh->channel_id].ctl_bright;
- break;
- case V4L2_CID_HUE:
- ctl->value = dev->channels[fh->channel_id].ctl_hue;
- break;
- case V4L2_CID_CONTRAST:
- ctl->value = dev->channels[fh->channel_id].ctl_contrast;
- break;
- case V4L2_CID_SATURATION:
- ctl->value = dev->channels[fh->channel_id].ctl_saturation;
- break;
- }
- return 0;
-}
-
-int cx25821_set_control(struct cx25821_dev *dev,
- struct v4l2_control *ctl, int chan_num)
-{
- int err;
- const struct v4l2_queryctrl *ctrl;
-
- err = -EINVAL;
-
- ctrl = ctrl_by_id(ctl->id);
-
- if (NULL == ctrl)
- return err;
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER:
- if (ctl->value < ctrl->minimum)
- ctl->value = ctrl->minimum;
- if (ctl->value > ctrl->maximum)
- ctl->value = ctrl->maximum;
- break;
- default:
- /* nothing */ ;
- }
-
- switch (ctl->id) {
- case V4L2_CID_BRIGHTNESS:
- dev->channels[chan_num].ctl_bright = ctl->value;
- medusa_set_brightness(dev, ctl->value, chan_num);
- break;
- case V4L2_CID_HUE:
- dev->channels[chan_num].ctl_hue = ctl->value;
- medusa_set_hue(dev, ctl->value, chan_num);
- break;
- case V4L2_CID_CONTRAST:
- dev->channels[chan_num].ctl_contrast = ctl->value;
- medusa_set_contrast(dev, ctl->value, chan_num);
- break;
- case V4L2_CID_SATURATION:
- dev->channels[chan_num].ctl_saturation = ctl->value;
- medusa_set_saturation(dev, ctl->value, chan_num);
- break;
- }
-
- err = 0;
-
- return err;
-}
-
-static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num)
-{
- struct v4l2_control ctrl;
- int i;
- for (i = 0; i < CX25821_CTLS; i++) {
- ctrl.id = cx25821_ctls[i].id;
- ctrl.value = cx25821_ctls[i].default_value;
-
- cx25821_set_control(dev, &ctrl, chan_num);
- }
-}
-
-int cx25821_vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cropcap)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- cropcap->bounds.top = 0;
- cropcap->bounds.left = 0;
- cropcap->bounds.width = 720;
- cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480;
- cropcap->pixelaspect.numerator =
- dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10;
- cropcap->pixelaspect.denominator =
- dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11;
- cropcap->defrect = cropcap->bounds;
- return 0;
-}
-
-int cx25821_vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev;
- struct cx25821_fh *fh = priv;
- int err;
-
- if (fh) {
- err = v4l2_prio_check(&dev->channels[fh->channel_id].prio,
- fh->prio);
- if (0 != err)
- return err;
- }
- /* cx25821_vidioc_s_crop not supported */
- return -EINVAL;
-}
-
-int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- /* cx25821_vidioc_g_crop not supported */
- return -EINVAL;
-}
-
-int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm)
-{
- /* medusa does not support video standard sensing of current input */
- *norm = CX25821_NORMS;
-
- return 0;
-}
-
-int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm)
-{
- if (tvnorm == V4L2_STD_PAL_BG) {
- if (width == 352 || width == 720)
- return 1;
- else
- return 0;
- }
-
- if (tvnorm == V4L2_STD_NTSC_M) {
- if (width == 320 || width == 352 || width == 720)
- return 1;
- else
- return 0;
- }
- return 0;
-}
-
-int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm)
-{
- if (tvnorm == V4L2_STD_PAL_BG) {
- if (height == 576 || height == 288)
- return 1;
- else
- return 0;
- }
-
- if (tvnorm == V4L2_STD_NTSC_M) {
- if (height == 480 || height == 240)
- return 1;
- else
- return 0;
- }
-
- return 0;
-}
-
-static long video_ioctl_upstream9(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
-
- data_from_user = (struct upstream_user_struct *)arg;
-
- if (!data_from_user) {
- pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
- return 0;
- }
-
- command = data_from_user->command;
-
- if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
- return 0;
-
- dev->input_filename = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname = data_from_user->vid_stdname;
- dev->pixel_format = data_from_user->pixel_format;
- dev->channel_select = data_from_user->channel_select;
- dev->command = data_from_user->command;
-
- switch (command) {
- case UPSTREAM_START_VIDEO:
- cx25821_start_upstream_video_ch1(dev, data_from_user);
- break;
-
- case UPSTREAM_STOP_VIDEO:
- cx25821_stop_upstream_video_ch1(dev);
- break;
- }
-
- return 0;
-}
-
-static long video_ioctl_upstream10(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
-
- data_from_user = (struct upstream_user_struct *)arg;
-
- if (!data_from_user) {
- pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
- return 0;
- }
-
- command = data_from_user->command;
-
- if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO)
- return 0;
-
- dev->input_filename_ch2 = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname_ch2 = data_from_user->vid_stdname;
- dev->pixel_format_ch2 = data_from_user->pixel_format;
- dev->channel_select_ch2 = data_from_user->channel_select;
- dev->command_ch2 = data_from_user->command;
-
- switch (command) {
- case UPSTREAM_START_VIDEO:
- cx25821_start_upstream_video_ch2(dev, data_from_user);
- break;
-
- case UPSTREAM_STOP_VIDEO:
- cx25821_stop_upstream_video_ch2(dev);
- break;
- }
-
- return 0;
-}
-
-static long video_ioctl_upstream11(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- int command = 0;
- struct upstream_user_struct *data_from_user;
-
- data_from_user = (struct upstream_user_struct *)arg;
-
- if (!data_from_user) {
- pr_err("%s(): Upstream data is INVALID. Returning\n", __func__);
- return 0;
- }
-
- command = data_from_user->command;
-
- if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO)
- return 0;
-
- dev->input_filename = data_from_user->input_filename;
- dev->input_audiofilename = data_from_user->input_filename;
- dev->vid_stdname = data_from_user->vid_stdname;
- dev->pixel_format = data_from_user->pixel_format;
- dev->channel_select = data_from_user->channel_select;
- dev->command = data_from_user->command;
-
- switch (command) {
- case UPSTREAM_START_AUDIO:
- cx25821_start_upstream_audio(dev, data_from_user);
- break;
-
- case UPSTREAM_STOP_AUDIO:
- cx25821_stop_upstream_audio(dev);
- break;
- }
-
- return 0;
-}
-
-static long video_ioctl_set(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct cx25821_fh *fh = file->private_data;
- struct cx25821_dev *dev = fh->dev;
- struct downstream_user_struct *data_from_user;
- int command;
- int width = 720;
- int selected_channel = 0;
- int pix_format = 0;
- int i = 0;
- int cif_enable = 0;
- int cif_width = 0;
-
- data_from_user = (struct downstream_user_struct *)arg;
-
- if (!data_from_user) {
- pr_err("%s(): User data is INVALID. Returning\n", __func__);
- return 0;
- }
-
- command = data_from_user->command;
-
- if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT
- && command != ENABLE_CIF_RESOLUTION && command != REG_READ
- && command != REG_WRITE && command != MEDUSA_READ
- && command != MEDUSA_WRITE) {
- return 0;
- }
-
- switch (command) {
- case SET_VIDEO_STD:
- if (!strcmp(data_from_user->vid_stdname, "PAL"))
- dev->tvnorm = V4L2_STD_PAL_BG;
- else
- dev->tvnorm = V4L2_STD_NTSC_M;
- medusa_set_videostandard(dev);
- break;
-
- case SET_PIXEL_FORMAT:
- selected_channel = data_from_user->decoder_select;
- pix_format = data_from_user->pixel_format;
-
- if (!(selected_channel <= 7 && selected_channel >= 0)) {
- selected_channel -= 4;
- selected_channel = selected_channel % 8;
- }
-
- if (selected_channel >= 0)
- cx25821_set_pixel_format(dev, selected_channel,
- pix_format);
-
- break;
-
- case ENABLE_CIF_RESOLUTION:
- selected_channel = data_from_user->decoder_select;
- cif_enable = data_from_user->cif_resolution_enable;
- cif_width = data_from_user->cif_width;
-
- if (cif_enable) {
- if (dev->tvnorm & V4L2_STD_PAL_BG
- || dev->tvnorm & V4L2_STD_PAL_DK) {
- width = 352;
- } else {
- width = cif_width;
- if (cif_width != 320 && cif_width != 352)
- width = 320;
- }
- }
-
- if (!(selected_channel <= 7 && selected_channel >= 0)) {
- selected_channel -= 4;
- selected_channel = selected_channel % 8;
- }
-
- if (selected_channel <= 7 && selected_channel >= 0) {
- dev->channels[selected_channel].use_cif_resolution =
- cif_enable;
- dev->channels[selected_channel].cif_width = width;
- } else {
- for (i = 0; i < VID_CHANNEL_NUM; i++) {
- dev->channels[i].use_cif_resolution =
- cif_enable;
- dev->channels[i].cif_width = width;
- }
- }
-
- medusa_set_resolution(dev, width, selected_channel);
- break;
- case REG_READ:
- data_from_user->reg_data = cx_read(data_from_user->reg_address);
- break;
- case REG_WRITE:
- cx_write(data_from_user->reg_address, data_from_user->reg_data);
- break;
- case MEDUSA_READ:
- cx25821_i2c_read(&dev->i2c_bus[0],
- (u16) data_from_user->reg_address,
- &data_from_user->reg_data);
- break;
- case MEDUSA_WRITE:
- cx25821_i2c_write(&dev->i2c_bus[0],
- (u16) data_from_user->reg_address,
- data_from_user->reg_data);
- break;
- }
-
- return 0;
-}
-
-static long cx25821_video_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- int ret = 0;
-
- struct cx25821_fh *fh = file->private_data;
-
- /* check to see if it's the video upstream */
- if (fh->channel_id == SRAM_CH09) {
- ret = video_ioctl_upstream9(file, cmd, arg);
- return ret;
- } else if (fh->channel_id == SRAM_CH10) {
- ret = video_ioctl_upstream10(file, cmd, arg);
- return ret;
- } else if (fh->channel_id == SRAM_CH11) {
- ret = video_ioctl_upstream11(file, cmd, arg);
- ret = video_ioctl_set(file, cmd, arg);
- return ret;
- }
-
- return video_ioctl2(file, cmd, arg);
-}
-
-/* exported stuff */
-static const struct v4l2_file_operations video_fops = {
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = cx25821_video_mmap,
- .ioctl = cx25821_video_ioctl,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = cx25821_vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = cx25821_vidioc_reqbufs,
- .vidioc_querybuf = cx25821_vidioc_querybuf,
- .vidioc_qbuf = cx25821_vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-#ifdef TUNER_FLAG
- .vidioc_s_std = cx25821_vidioc_s_std,
- .vidioc_querystd = cx25821_vidioc_querystd,
-#endif
- .vidioc_cropcap = cx25821_vidioc_cropcap,
- .vidioc_s_crop = cx25821_vidioc_s_crop,
- .vidioc_g_crop = cx25821_vidioc_g_crop,
- .vidioc_enum_input = cx25821_vidioc_enum_input,
- .vidioc_g_input = cx25821_vidioc_g_input,
- .vidioc_s_input = cx25821_vidioc_s_input,
- .vidioc_g_ctrl = cx25821_vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_queryctrl = cx25821_vidioc_queryctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_g_priority = cx25821_vidioc_g_priority,
- .vidioc_s_priority = cx25821_vidioc_s_priority,
-#ifdef TUNER_FLAG
- .vidioc_g_tuner = cx25821_vidioc_g_tuner,
- .vidioc_s_tuner = cx25821_vidioc_s_tuner,
- .vidioc_g_frequency = cx25821_vidioc_g_frequency,
- .vidioc_s_frequency = cx25821_vidioc_s_frequency,
-#endif
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = cx25821_vidioc_g_register,
- .vidioc_s_register = cx25821_vidioc_s_register,
-#endif
-};
-
-struct video_device cx25821_videoioctl_template = {
- .name = "cx25821-videoioctl",
- .fops = &video_fops,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX25821_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
-};
diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
deleted file mode 100644
index 9652a5e35ba2..000000000000
--- a/drivers/media/video/cx25821/cx25821-video.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Driver for the Conexant CX25821 PCIe bridge
- *
- * Copyright (C) 2009 Conexant Systems Inc.
- * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
- * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef CX25821_VIDEO_H_
-#define CX25821_VIDEO_H_
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <asm/div64.h>
-
-#include "cx25821.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-#define TUNER_FLAG
-
-#define VIDEO_DEBUG 0
-
-#define dprintk(level, fmt, arg...) \
-do { \
- if (VIDEO_DEBUG >= level) \
- printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \
-} while (0)
-
-/* For IOCTL to identify running upstream */
-#define UPSTREAM_START_VIDEO 700
-#define UPSTREAM_STOP_VIDEO 701
-#define UPSTREAM_START_AUDIO 702
-#define UPSTREAM_STOP_AUDIO 703
-#define UPSTREAM_DUMP_REGISTERS 702
-#define SET_VIDEO_STD 800
-#define SET_PIXEL_FORMAT 1000
-#define ENABLE_CIF_RESOLUTION 1001
-
-#define REG_READ 900
-#define REG_WRITE 901
-#define MEDUSA_READ 910
-#define MEDUSA_WRITE 911
-
-extern struct sram_channel *channel0;
-extern struct sram_channel *channel1;
-extern struct sram_channel *channel2;
-extern struct sram_channel *channel3;
-extern struct sram_channel *channel4;
-extern struct sram_channel *channel5;
-extern struct sram_channel *channel6;
-extern struct sram_channel *channel7;
-extern struct sram_channel *channel9;
-extern struct sram_channel *channel10;
-extern struct sram_channel *channel11;
-extern struct video_device cx25821_videoioctl_template;
-/* extern const u32 *ctrl_classes[]; */
-
-extern unsigned int vid_limit;
-
-#define FORMAT_FLAGS_PACKED 0x01
-extern struct cx25821_fmt formats[];
-extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
-extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
-
-extern void cx25821_video_wakeup(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q, u32 count);
-
-#ifdef TUNER_FLAG
-extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm);
-#endif
-
-extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bit);
-extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit);
-extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit);
-extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh,
- unsigned int bits);
-extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input);
-extern int cx25821_start_video_dma(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q,
- struct cx25821_buffer *buf,
- struct sram_channel *channel);
-
-extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width,
- unsigned int height, enum v4l2_field field);
-extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status);
-extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num);
-extern int cx25821_video_register(struct cx25821_dev *dev);
-extern int cx25821_get_format_size(void);
-
-extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size);
-extern int cx25821_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field);
-extern void cx25821_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb);
-extern struct videobuf_queue *get_queue(struct cx25821_fh *fh);
-extern int cx25821_get_resource(struct cx25821_fh *fh, int resource);
-extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma);
-extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f);
-extern int cx25821_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap);
-extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f);
-extern int cx25821_vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p);
-extern int cx25821_vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p);
-extern int cx25821_vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p);
-extern int cx25821_vidioc_s_std(struct file *file, void *priv,
- v4l2_std_id *tvnorms);
-extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i);
-extern int cx25821_vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i);
-extern int cx25821_vidioc_g_input(struct file *file, void *priv,
- unsigned int *i);
-extern int cx25821_vidioc_s_input(struct file *file, void *priv,
- unsigned int i);
-extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctl);
-extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f);
-extern int cx25821_vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f);
-extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f);
-extern int cx25821_vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f);
-extern int cx25821_vidioc_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg);
-extern int cx25821_vidioc_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg);
-extern int cx25821_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t);
-extern int cx25821_vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t);
-
-extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm);
-extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm);
-
-extern int cx25821_vidioc_g_priority(struct file *file, void *f,
- enum v4l2_priority *p);
-extern int cx25821_vidioc_s_priority(struct file *file, void *f,
- enum v4l2_priority prio);
-
-extern int cx25821_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl);
-extern int cx25821_set_control(struct cx25821_dev *dev,
- struct v4l2_control *ctrl, int chan_num);
-
-extern int cx25821_vidioc_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cropcap);
-extern int cx25821_vidioc_s_crop(struct file *file, void *priv,
- struct v4l2_crop *crop);
-extern int cx25821_vidioc_g_crop(struct file *file, void *priv,
- struct v4l2_crop *crop);
-
-extern int cx25821_vidioc_querystd(struct file *file, void *priv,
- v4l2_std_id *norm);
-#endif
diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile
deleted file mode 100644
index dc40dde2e0c8..000000000000
--- a/drivers/media/video/cx25840/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \
- cx25840-vbi.o cx25840-ir.o
-
-obj-$(CONFIG_VIDEO_CX25840) += cx25840.o
-
-ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
deleted file mode 100644
index 8150200511da..000000000000
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/* cx25840 firmware functions
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/firmware.h>
-#include <media/v4l2-common.h>
-#include <media/cx25840.h>
-
-#include "cx25840-core.h"
-
-/*
- * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
- * size of the firmware chunks sent down the I2C bus to the chip.
- * Previously this had been set to 1024 but unfortunately some I2C
- * implementations can't transfer data in such big gulps.
- * Specifically, the pvrusb2 driver has a hard limit of around 60
- * bytes, due to the encapsulation there of I2C traffic into USB
- * messages. So we have to significantly reduce this parameter.
- */
-#define FWSEND 48
-
-#define FWDEV(x) &((x)->dev)
-
-static char *firmware = "";
-
-module_param(firmware, charp, 0444);
-
-MODULE_PARM_DESC(firmware, "Firmware image to load");
-
-static void start_fw_load(struct i2c_client *client)
-{
- /* DL_ADDR_LB=0 DL_ADDR_HB=0 */
- cx25840_write(client, 0x800, 0x00);
- cx25840_write(client, 0x801, 0x00);
- // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
- cx25840_write(client, 0x803, 0x0b);
- /* AUTO_INC_DIS=1 */
- cx25840_write(client, 0x000, 0x20);
-}
-
-static void end_fw_load(struct i2c_client *client)
-{
- /* AUTO_INC_DIS=0 */
- cx25840_write(client, 0x000, 0x00);
- /* DL_ENABLE=0 */
- cx25840_write(client, 0x803, 0x03);
-}
-
-static const char *get_fw_name(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
-
- if (firmware[0])
- return firmware;
- if (is_cx2388x(state))
- return "v4l-cx23885-avcore-01.fw";
- if (is_cx231xx(state))
- return "v4l-cx231xx-avcore-01.fw";
- return "v4l-cx25840.fw";
-}
-
-static int check_fw_load(struct i2c_client *client, int size)
-{
- /* DL_ADDR_HB DL_ADDR_LB */
- int s = cx25840_read(client, 0x801) << 8;
- s |= cx25840_read(client, 0x800);
-
- if (size != s) {
- v4l_err(client, "firmware %s load failed\n",
- get_fw_name(client));
- return -EINVAL;
- }
-
- v4l_info(client, "loaded %s firmware (%d bytes)\n",
- get_fw_name(client), size);
- return 0;
-}
-
-static int fw_write(struct i2c_client *client, const u8 *data, int size)
-{
- if (i2c_master_send(client, data, size) < size) {
- v4l_err(client, "firmware load i2c failure\n");
- return -ENOSYS;
- }
-
- return 0;
-}
-
-int cx25840_loadfw(struct i2c_client *client)
-{
- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
- const struct firmware *fw = NULL;
- u8 buffer[FWSEND];
- const u8 *ptr;
- const char *fwname = get_fw_name(client);
- int size, retval;
- int MAX_BUF_SIZE = FWSEND;
- u32 gpio_oe = 0, gpio_da = 0;
-
- if (is_cx2388x(state)) {
- /* Preserve the GPIO OE and output bits */
- gpio_oe = cx25840_read(client, 0x160);
- gpio_da = cx25840_read(client, 0x164);
- }
-
- if (is_cx231xx(state) && MAX_BUF_SIZE > 16) {
- v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
- MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
- }
-
- if (request_firmware(&fw, fwname, FWDEV(client)) != 0) {
- v4l_err(client, "unable to open firmware %s\n", fwname);
- return -EINVAL;
- }
-
- start_fw_load(client);
-
- buffer[0] = 0x08;
- buffer[1] = 0x02;
-
- size = fw->size;
- ptr = fw->data;
- while (size > 0) {
- int len = min(MAX_BUF_SIZE - 2, size);
-
- memcpy(buffer + 2, ptr, len);
-
- retval = fw_write(client, buffer, len + 2);
-
- if (retval < 0) {
- release_firmware(fw);
- return retval;
- }
-
- size -= len;
- ptr += len;
- }
-
- end_fw_load(client);
-
- size = fw->size;
- release_firmware(fw);
-
- if (is_cx2388x(state)) {
- /* Restore GPIO configuration after f/w load */
- cx25840_write(client, 0x160, gpio_oe);
- cx25840_write(client, 0x164, gpio_da);
- }
-
- return check_fw_load(client, size);
-}
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
deleted file mode 100644
index 64a4004f8a97..000000000000
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/* cx25840 VBI functions
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <media/v4l2-common.h>
-#include <media/cx25840.h>
-
-#include "cx25840-core.h"
-
-static int odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
-static int decode_vps(u8 * dst, u8 * p)
-{
- static const u8 biphase_tbl[] = {
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
- 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
- 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
- 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
- 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
- 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
- 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- };
-
- u8 c, err = 0;
- int i;
-
- for (i = 0; i < 2 * 13; i += 2) {
- err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
- c = (biphase_tbl[p[i + 1]] & 0xf) |
- ((biphase_tbl[p[i]] & 0xf) << 4);
- dst[i / 2] = c;
- }
-
- return err & 0xf0;
-}
-
-int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct cx25840_state *state = to_state(sd);
- static const u16 lcr2vbi[] = {
- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
- 0, V4L2_SLICED_WSS_625, 0, /* 4 */
- V4L2_SLICED_CAPTION_525, /* 6 */
- 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
- 0, 0, 0, 0
- };
- int is_pal = !(state->std & V4L2_STD_525_60);
- int i;
-
- memset(svbi, 0, sizeof(*svbi));
- /* we're done if raw VBI is active */
- if ((cx25840_read(client, 0x404) & 0x10) == 0)
- return 0;
-
- if (is_pal) {
- for (i = 7; i <= 23; i++) {
- u8 v = cx25840_read(client, 0x424 + i - 7);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- } else {
- for (i = 10; i <= 21; i++) {
- u8 v = cx25840_read(client, 0x424 + i - 10);
-
- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
- svbi->service_set |= svbi->service_lines[0][i] |
- svbi->service_lines[1][i];
- }
- }
- return 0;
-}
-
-int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct cx25840_state *state = to_state(sd);
- int is_pal = !(state->std & V4L2_STD_525_60);
- int vbi_offset = is_pal ? 1 : 0;
-
- /* Setup standard */
- cx25840_std_setup(client);
-
- /* VBI Offset */
- cx25840_write(client, 0x47f, vbi_offset);
- cx25840_write(client, 0x404, 0x2e);
- return 0;
-}
-
-int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct cx25840_state *state = to_state(sd);
- int is_pal = !(state->std & V4L2_STD_525_60);
- int vbi_offset = is_pal ? 1 : 0;
- int i, x;
- u8 lcr[24];
-
- for (x = 0; x <= 23; x++)
- lcr[x] = 0x00;
-
- /* Setup standard */
- cx25840_std_setup(client);
-
- /* Sliced VBI */
- cx25840_write(client, 0x404, 0x32); /* Ancillary data */
- cx25840_write(client, 0x406, 0x13);
- cx25840_write(client, 0x47f, vbi_offset);
-
- if (is_pal) {
- for (i = 0; i <= 6; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- } else {
- for (i = 0; i <= 9; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
-
- for (i = 22; i <= 23; i++)
- svbi->service_lines[0][i] =
- svbi->service_lines[1][i] = 0;
- }
-
- for (i = 7; i <= 23; i++) {
- for (x = 0; x <= 1; x++) {
- switch (svbi->service_lines[1-x][i]) {
- case V4L2_SLICED_TELETEXT_B:
- lcr[i] |= 1 << (4 * x);
- break;
- case V4L2_SLICED_WSS_625:
- lcr[i] |= 4 << (4 * x);
- break;
- case V4L2_SLICED_CAPTION_525:
- lcr[i] |= 6 << (4 * x);
- break;
- case V4L2_SLICED_VPS:
- lcr[i] |= 9 << (4 * x);
- break;
- }
- }
- }
-
- if (is_pal) {
- for (x = 1, i = 0x424; i <= 0x434; i++, x++)
- cx25840_write(client, i, lcr[6 + x]);
- } else {
- for (x = 1, i = 0x424; i <= 0x430; i++, x++)
- cx25840_write(client, i, lcr[9 + x]);
- for (i = 0x431; i <= 0x434; i++)
- cx25840_write(client, i, 0);
- }
-
- cx25840_write(client, 0x43c, 0x16);
- cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
- return 0;
-}
-
-int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
-{
- struct cx25840_state *state = to_state(sd);
- u8 *p = vbi->p;
- int id1, id2, l, err = 0;
-
- if (p[0] || p[1] != 0xff || p[2] != 0xff ||
- (p[3] != 0x55 && p[3] != 0x91)) {
- vbi->line = vbi->type = 0;
- return 0;
- }
-
- p += 4;
- id1 = p[-1];
- id2 = p[0] & 0xf;
- l = p[2] & 0x3f;
- l += state->vbi_line_offset;
- p += 4;
-
- switch (id2) {
- case 1:
- id2 = V4L2_SLICED_TELETEXT_B;
- break;
- case 4:
- id2 = V4L2_SLICED_WSS_625;
- break;
- case 6:
- id2 = V4L2_SLICED_CAPTION_525;
- err = !odd_parity(p[0]) || !odd_parity(p[1]);
- break;
- case 9:
- id2 = V4L2_SLICED_VPS;
- if (decode_vps(p, p) != 0)
- err = 1;
- break;
- default:
- id2 = 0;
- err = 1;
- break;
- }
-
- vbi->type = err ? 0 : id2;
- vbi->line = err ? 0 : l;
- vbi->is_second_field = err ? 0 : (id1 == 0x55);
- vbi->p = p;
- return 0;
-}
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
deleted file mode 100644
index 3598dc087b08..000000000000
--- a/drivers/media/video/cx88/Kconfig
+++ /dev/null
@@ -1,86 +0,0 @@
-config VIDEO_CX88
- tristate "Conexant 2388x (bt878 successor) support"
- depends on VIDEO_DEV && PCI && I2C && RC_CORE
- select I2C_ALGOBIT
- select VIDEO_BTCX
- select VIDEOBUF_DMA_SG
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- This is a video4linux driver for Conexant 2388x based
- TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called cx8800
-
-config VIDEO_CX88_ALSA
- tristate "Conexant 2388x DMA audio support"
- depends on VIDEO_CX88 && SND
- select SND_PCM
- ---help---
- This is a video4linux driver for direct (DMA) audio on
- Conexant 2388x based TV cards using ALSA.
-
- It only works with boards with function 01 enabled.
- To check if your board supports, use lspci -n.
- If supported, you should see 14f1:8801 or 14f1:8811
- PCI device.
-
- To compile this driver as a module, choose M here: the
- module will be called cx88-alsa.
-
-config VIDEO_CX88_BLACKBIRD
- tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
- depends on VIDEO_CX88
- select VIDEO_CX2341X
- ---help---
- This adds support for MPEG encoder cards based on the
- Blackbird reference design, using the Conexant 2388x
- and 23416 chips.
-
- To compile this driver as a module, choose M here: the
- module will be called cx88-blackbird.
-
-config VIDEO_CX88_DVB
- tristate "DVB/ATSC Support for cx2388x based TV cards"
- depends on VIDEO_CX88 && DVB_CORE
- select VIDEOBUF_DVB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_OR51132 if !DVB_FE_CUSTOMISE
- select DVB_CX22702 if !DVB_FE_CUSTOMISE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_NXT200X if !DVB_FE_CUSTOMISE
- select DVB_CX24123 if !DVB_FE_CUSTOMISE
- select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select DVB_CX24116 if !DVB_FE_CUSTOMISE
- select DVB_STV0299 if !DVB_FE_CUSTOMISE
- select DVB_STV0288 if !DVB_FE_CUSTOMISE
- select DVB_STB6000 if !DVB_FE_CUSTOMISE
- select DVB_STV0900 if !DVB_FE_CUSTOMISE
- select DVB_STB6100 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- ---help---
- This adds support for DVB/ATSC cards based on the
- Conexant 2388x chip.
-
- To compile this driver as a module, choose M here: the
- module will be called cx88-dvb.
-
-config VIDEO_CX88_VP3054
- tristate "VP-3054 Secondary I2C Bus Support"
- default m
- depends on VIDEO_CX88_DVB && DVB_MT352
- ---help---
- This adds DVB-T support for cards based on the
- Conexant 2388x chip and the MT352 demodulator,
- which also require support for the VP-3054
- Secondary I2C bus, such at DNTV Live! DVB-T Pro.
-
-config VIDEO_CX88_MPEG
- tristate
- depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD
- default y
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
deleted file mode 100644
index c1a2785ba243..000000000000
--- a/drivers/media/video/cx88/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
- cx88-dsp.o cx88-input.o
-cx8800-objs := cx88-video.o cx88-vbi.o
-cx8802-objs := cx88-mpeg.o
-
-obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
-obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o
-obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
-obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
-obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
-obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
deleted file mode 100644
index dfac6e34859f..000000000000
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- *
- * Support for audio capture
- * PCI function #1 of the cx2388x.
- *
- * (c) 2007 Trent Piepho <xyzzy@speakeasy.org>
- * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org>
- * (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
- * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org>
- * Based on dummy.c by Jaroslav Kysela <perex@perex.cz>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/vmalloc.h>
-#include <linux/dma-mapping.h>
-#include <linux/pci.h>
-#include <linux/slab.h>
-
-#include <asm/delay.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/control.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <media/wm8775.h>
-
-#include "cx88.h"
-#include "cx88-reg.h"
-
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg)
-
-#define dprintk_core(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg)
-
-/****************************************************************************
- Data type declarations - Can be moded to a header file later
- ****************************************************************************/
-
-struct cx88_audio_buffer {
- unsigned int bpl;
- struct btcx_riscmem risc;
- struct videobuf_dmabuf dma;
-};
-
-struct cx88_audio_dev {
- struct cx88_core *core;
- struct cx88_dmaqueue q;
-
- /* pci i/o */
- struct pci_dev *pci;
-
- /* audio controls */
- int irq;
-
- struct snd_card *card;
-
- spinlock_t reg_lock;
- atomic_t count;
-
- unsigned int dma_size;
- unsigned int period_size;
- unsigned int num_periods;
-
- struct videobuf_dmabuf *dma_risc;
-
- struct cx88_audio_buffer *buf;
-
- struct snd_pcm_substream *substream;
-};
-typedef struct cx88_audio_dev snd_cx88_card_t;
-
-
-
-/****************************************************************************
- Module global static vars
- ****************************************************************************/
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
-static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
-static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
-
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled.");
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s).");
-
-
-/****************************************************************************
- Module macros
- ****************************************************************************/
-
-MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards");
-MODULE_AUTHOR("Ricardo Cerqueira");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CX88_VERSION);
-
-MODULE_SUPPORTED_DEVICE("{{Conexant,23881},"
- "{{Conexant,23882},"
- "{{Conexant,23883}");
-static unsigned int debug;
-module_param(debug,int,0644);
-MODULE_PARM_DESC(debug,"enable debug messages");
-
-/****************************************************************************
- Module specific funtions
- ****************************************************************************/
-
-/*
- * BOARD Specific: Sets audio DMA
- */
-
-static int _cx88_start_audio_dma(snd_cx88_card_t *chip)
-{
- struct cx88_audio_buffer *buf = chip->buf;
- struct cx88_core *core=chip->core;
- const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25];
-
- /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */
- cx_clear(MO_AUD_DMACNTRL, 0x11);
-
- /* setup fifo + format - out channel */
- cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma);
-
- /* sets bpl size */
- cx_write(MO_AUDD_LNGTH, buf->bpl);
-
- /* reset counter */
- cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
- atomic_set(&chip->count, 0);
-
- dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d "
- "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1,
- chip->num_periods, buf->bpl * chip->num_periods);
-
- /* Enables corresponding bits at AUD_INT_STAT */
- cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
- AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
-
- /* Clean any pending interrupt bits already set */
- cx_write(MO_AUD_INTSTAT, ~0);
-
- /* enable audio irqs */
- cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT);
-
- /* start dma */
- cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */
- cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */
-
- if (debug)
- cx88_sram_channel_dump(chip->core, audio_ch);
-
- return 0;
-}
-
-/*
- * BOARD Specific: Resets audio DMA
- */
-static int _cx88_stop_audio_dma(snd_cx88_card_t *chip)
-{
- struct cx88_core *core=chip->core;
- dprintk(1, "Stopping audio DMA\n");
-
- /* stop dma */
- cx_clear(MO_AUD_DMACNTRL, 0x11);
-
- /* disable irqs */
- cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
- cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC |
- AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1);
-
- if (debug)
- cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]);
-
- return 0;
-}
-
-#define MAX_IRQ_LOOP 50
-
-/*
- * BOARD Specific: IRQ dma bits
- */
-static const char *cx88_aud_irqs[32] = {
- "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */
- NULL, /* reserved */
- "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */
- NULL, /* reserved */
- "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */
- NULL, /* reserved */
- "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */
- NULL, /* reserved */
- "opc_err", "par_err", "rip_err", /* 16-18 */
- "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */
-};
-
-/*
- * BOARD Specific: Threats IRQ audio specific calls
- */
-static void cx8801_aud_irq(snd_cx88_card_t *chip)
-{
- struct cx88_core *core = chip->core;
- u32 status, mask;
-
- status = cx_read(MO_AUD_INTSTAT);
- mask = cx_read(MO_AUD_INTMSK);
- if (0 == (status & mask))
- return;
- cx_write(MO_AUD_INTSTAT, status);
- if (debug > 1 || (status & mask & ~0xff))
- cx88_print_irqbits(core->name, "irq aud",
- cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs),
- status, mask);
- /* risc op code error */
- if (status & AUD_INT_OPC_ERR) {
- printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name);
- cx_clear(MO_AUD_DMACNTRL, 0x11);
- cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]);
- }
- if (status & AUD_INT_DN_SYNC) {
- dprintk(1, "Downstream sync error\n");
- cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET);
- return;
- }
- /* risc1 downstream */
- if (status & AUD_INT_DN_RISCI1) {
- atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT));
- snd_pcm_period_elapsed(chip->substream);
- }
- /* FIXME: Any other status should deserve a special handling? */
-}
-
-/*
- * BOARD Specific: Handles IRQ calls
- */
-static irqreturn_t cx8801_irq(int irq, void *dev_id)
-{
- snd_cx88_card_t *chip = dev_id;
- struct cx88_core *core = chip->core;
- u32 status;
- int loop, handled = 0;
-
- for (loop = 0; loop < MAX_IRQ_LOOP; loop++) {
- status = cx_read(MO_PCI_INTSTAT) &
- (core->pci_irqmask | PCI_INT_AUDINT);
- if (0 == status)
- goto out;
- dprintk(3, "cx8801_irq loop %d/%d, status %x\n",
- loop, MAX_IRQ_LOOP, status);
- handled = 1;
- cx_write(MO_PCI_INTSTAT, status);
-
- if (status & core->pci_irqmask)
- cx88_core_irq(core, status);
- if (status & PCI_INT_AUDINT)
- cx8801_aud_irq(chip);
- }
-
- if (MAX_IRQ_LOOP == loop) {
- printk(KERN_ERR
- "%s/1: IRQ loop detected, disabling interrupts\n",
- core->name);
- cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT);
- }
-
- out:
- return IRQ_RETVAL(handled);
-}
-
-
-static int dsp_buffer_free(snd_cx88_card_t *chip)
-{
- BUG_ON(!chip->dma_size);
-
- dprintk(2,"Freeing buffer\n");
- videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc);
- videobuf_dma_free(chip->dma_risc);
- btcx_riscmem_free(chip->pci,&chip->buf->risc);
- kfree(chip->buf);
-
- chip->dma_risc = NULL;
- chip->dma_size = 0;
-
- return 0;
-}
-
-/****************************************************************************
- ALSA PCM Interface
- ****************************************************************************/
-
-/*
- * Digital hardware definition
- */
-#define DEFAULT_FIFO_SIZE 4096
-static const struct snd_pcm_hardware snd_cx88_digital_hw = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
-
- .rates = SNDRV_PCM_RATE_48000,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- /* Analog audio output will be full of clicks and pops if there
- are not exactly four lines in the SRAM FIFO buffer. */
- .period_bytes_min = DEFAULT_FIFO_SIZE/4,
- .period_bytes_max = DEFAULT_FIFO_SIZE/4,
- .periods_min = 1,
- .periods_max = 1024,
- .buffer_bytes_max = (1024*1024),
-};
-
-/*
- * audio pcm capture open callback
- */
-static int snd_cx88_pcm_open(struct snd_pcm_substream *substream)
-{
- snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- if (!chip) {
- printk(KERN_ERR "BUG: cx88 can't find device struct."
- " Can't proceed with open\n");
- return -ENODEV;
- }
-
- err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0)
- goto _error;
-
- chip->substream = substream;
-
- runtime->hw = snd_cx88_digital_hw;
-
- if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) {
- unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4;
- bpl &= ~7; /* must be multiple of 8 */
- runtime->hw.period_bytes_min = bpl;
- runtime->hw.period_bytes_max = bpl;
- }
-
- return 0;
-_error:
- dprintk(1,"Error opening PCM!\n");
- return err;
-}
-
-/*
- * audio close callback
- */
-static int snd_cx88_close(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-/*
- * hw_params callback
- */
-static int snd_cx88_hw_params(struct snd_pcm_substream * substream,
- struct snd_pcm_hw_params * hw_params)
-{
- snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
- struct videobuf_dmabuf *dma;
-
- struct cx88_audio_buffer *buf;
- int ret;
-
- if (substream->runtime->dma_area) {
- dsp_buffer_free(chip);
- substream->runtime->dma_area = NULL;
- }
-
- chip->period_size = params_period_bytes(hw_params);
- chip->num_periods = params_periods(hw_params);
- chip->dma_size = chip->period_size * params_periods(hw_params);
-
- BUG_ON(!chip->dma_size);
- BUG_ON(chip->num_periods & (chip->num_periods-1));
-
- buf = kzalloc(sizeof(*buf), GFP_KERNEL);
- if (NULL == buf)
- return -ENOMEM;
-
- buf->bpl = chip->period_size;
-
- dma = &buf->dma;
- videobuf_dma_init(dma);
- ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE,
- (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT));
- if (ret < 0)
- goto error;
-
- ret = videobuf_dma_map(&chip->pci->dev, dma);
- if (ret < 0)
- goto error;
-
- ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist,
- chip->period_size, chip->num_periods, 1);
- if (ret < 0)
- goto error;
-
- /* Loop back to start of program */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
-
- chip->buf = buf;
- chip->dma_risc = dma;
-
- substream->runtime->dma_area = chip->dma_risc->vaddr;
- substream->runtime->dma_bytes = chip->dma_size;
- substream->runtime->dma_addr = 0;
- return 0;
-
-error:
- kfree(buf);
- return ret;
-}
-
-/*
- * hw free callback
- */
-static int snd_cx88_hw_free(struct snd_pcm_substream * substream)
-{
-
- snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
-
- if (substream->runtime->dma_area) {
- dsp_buffer_free(chip);
- substream->runtime->dma_area = NULL;
- }
-
- return 0;
-}
-
-/*
- * prepare callback
- */
-static int snd_cx88_prepare(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-/*
- * trigger callback
- */
-static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
- int err;
-
- /* Local interrupts are already disabled by ALSA */
- spin_lock(&chip->reg_lock);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- err=_cx88_start_audio_dma(chip);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- err=_cx88_stop_audio_dma(chip);
- break;
- default:
- err=-EINVAL;
- break;
- }
-
- spin_unlock(&chip->reg_lock);
-
- return err;
-}
-
-/*
- * pointer callback
- */
-static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream)
-{
- snd_cx88_card_t *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- u16 count;
-
- count = atomic_read(&chip->count);
-
-// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__,
-// count, new, count & (runtime->periods-1),
-// runtime->period_size * (count & (runtime->periods-1)));
- return runtime->period_size * (count & (runtime->periods-1));
-}
-
-/*
- * page callback (needed for mmap)
- */
-static struct page *snd_cx88_page(struct snd_pcm_substream *substream,
- unsigned long offset)
-{
- void *pageptr = substream->runtime->dma_area + offset;
- return vmalloc_to_page(pageptr);
-}
-
-/*
- * operators
- */
-static struct snd_pcm_ops snd_cx88_pcm_ops = {
- .open = snd_cx88_pcm_open,
- .close = snd_cx88_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_cx88_hw_params,
- .hw_free = snd_cx88_hw_free,
- .prepare = snd_cx88_prepare,
- .trigger = snd_cx88_card_trigger,
- .pointer = snd_cx88_pointer,
- .page = snd_cx88_page,
-};
-
-/*
- * create a PCM device
- */
-static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name)
-{
- int err;
- struct snd_pcm *pcm;
-
- err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm);
- if (err < 0)
- return err;
- pcm->private_data = chip;
- strcpy(pcm->name, name);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops);
-
- return 0;
-}
-
-/****************************************************************************
- CONTROL INTERFACE
- ****************************************************************************/
-static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *info)
-{
- info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = 2;
- info->value.integer.min = 0;
- info->value.integer.max = 0x3f;
-
- return 0;
-}
-
-static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core=chip->core;
- int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
- bal = cx_read(AUD_BAL_CTL);
-
- value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
- vol -= (bal & 0x3f);
- value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
-
- return 0;
-}
-
-static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core = chip->core;
- int left = value->value.integer.value[0];
- int right = value->value.integer.value[1];
- int v, b;
-
- /* Pass volume & balance onto any WM8775 */
- if (left >= right) {
- v = left << 10;
- b = left ? (0x8000 * right) / left : 0x8000;
- } else {
- v = right << 10;
- b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
- }
- wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
- wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
-}
-
-/* OK - TODO: test it */
-static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core=chip->core;
- int left, right, v, b;
- int changed = 0;
- u32 old;
-
- if (core->board.audio_chip == V4L2_IDENT_WM8775)
- snd_cx88_wm8775_volume_put(kcontrol, value);
-
- left = value->value.integer.value[0] & 0x3f;
- right = value->value.integer.value[1] & 0x3f;
- b = right - left;
- if (b < 0) {
- v = 0x3f - left;
- b = (-b) | 0x40;
- } else {
- v = 0x3f - right;
- }
- /* Do we really know this will always be called with IRQs on? */
- spin_lock_irq(&chip->reg_lock);
- old = cx_read(AUD_VOL_CTL);
- if (v != (old & 0x3f)) {
- cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
- changed = 1;
- }
- if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
- cx_write(AUD_BAL_CTL, b);
- changed = 1;
- }
- spin_unlock_irq(&chip->reg_lock);
-
- return changed;
-}
-
-static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
-
-static const struct snd_kcontrol_new snd_cx88_volume = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
- SNDRV_CTL_ELEM_ACCESS_TLV_READ,
- .name = "Analog-TV Volume",
- .info = snd_cx88_volume_info,
- .get = snd_cx88_volume_get,
- .put = snd_cx88_volume_put,
- .tlv.p = snd_cx88_db_scale,
-};
-
-static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core = chip->core;
- u32 bit = kcontrol->private_value;
-
- value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit);
- return 0;
-}
-
-static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core = chip->core;
- u32 bit = kcontrol->private_value;
- int ret = 0;
- u32 vol;
-
- spin_lock_irq(&chip->reg_lock);
- vol = cx_read(AUD_VOL_CTL);
- if (value->value.integer.value[0] != !(vol & bit)) {
- vol ^= bit;
- cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol);
- /* Pass mute onto any WM8775 */
- if ((core->board.audio_chip == V4L2_IDENT_WM8775) &&
- ((1<<6) == bit))
- wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit));
- ret = 1;
- }
- spin_unlock_irq(&chip->reg_lock);
- return ret;
-}
-
-static const struct snd_kcontrol_new snd_cx88_dac_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Audio-Out Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = snd_cx88_switch_get,
- .put = snd_cx88_switch_put,
- .private_value = (1<<8),
-};
-
-static const struct snd_kcontrol_new snd_cx88_source_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Analog-TV Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = snd_cx88_switch_get,
- .put = snd_cx88_switch_put,
- .private_value = (1<<6),
-};
-
-static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core = chip->core;
- s32 val;
-
- val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS);
- value->value.integer.value[0] = val ? 1 : 0;
- return 0;
-}
-
-static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol);
- struct cx88_core *core = chip->core;
- struct v4l2_control client_ctl;
-
- memset(&client_ctl, 0, sizeof(client_ctl));
- client_ctl.value = 0 != value->value.integer.value[0];
- client_ctl.id = V4L2_CID_AUDIO_LOUDNESS;
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
-
- return 0;
-}
-
-static struct snd_kcontrol_new snd_cx88_alc_switch = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Line-In ALC Switch",
- .info = snd_ctl_boolean_mono_info,
- .get = snd_cx88_alc_get,
- .put = snd_cx88_alc_put,
-};
-
-/****************************************************************************
- Basic Flow for Sound Devices
- ****************************************************************************/
-
-/*
- * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio
- * Only boards with eeprom and byte 1 at eeprom=1 have it
- */
-
-static const struct pci_device_id const cx88_audio_pci_tbl[] __devinitdata = {
- {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
- {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
- {0, }
-};
-MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl);
-
-/*
- * Chip-specific destructor
- */
-
-static int snd_cx88_free(snd_cx88_card_t *chip)
-{
-
- if (chip->irq >= 0)
- free_irq(chip->irq, chip);
-
- cx88_core_put(chip->core,chip->pci);
-
- pci_disable_device(chip->pci);
- return 0;
-}
-
-/*
- * Component Destructor
- */
-static void snd_cx88_dev_free(struct snd_card * card)
-{
- snd_cx88_card_t *chip = card->private_data;
-
- snd_cx88_free(chip);
-}
-
-
-/*
- * Alsa Constructor - Component probe
- */
-
-static int devno;
-static int __devinit snd_cx88_create(struct snd_card *card,
- struct pci_dev *pci,
- snd_cx88_card_t **rchip,
- struct cx88_core **core_ptr)
-{
- snd_cx88_card_t *chip;
- struct cx88_core *core;
- int err;
- unsigned char pci_lat;
-
- *rchip = NULL;
-
- err = pci_enable_device(pci);
- if (err < 0)
- return err;
-
- pci_set_master(pci);
-
- chip = card->private_data;
-
- core = cx88_core_get(pci);
- if (NULL == core) {
- err = -EINVAL;
- return err;
- }
-
- if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) {
- dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name);
- err = -EIO;
- cx88_core_put(core, pci);
- return err;
- }
-
-
- /* pci init */
- chip->card = card;
- chip->pci = pci;
- chip->irq = -1;
- spin_lock_init(&chip->reg_lock);
-
- chip->core = core;
-
- /* get irq */
- err = request_irq(chip->pci->irq, cx8801_irq,
- IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip);
- if (err < 0) {
- dprintk(0, "%s: can't get IRQ %d\n",
- chip->core->name, chip->pci->irq);
- return err;
- }
-
- /* print pci info */
- pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat);
-
- dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%llx\n", core->name, devno,
- pci_name(pci), pci->revision, pci->irq,
- pci_lat, (unsigned long long)pci_resource_start(pci,0));
-
- chip->irq = pci->irq;
- synchronize_irq(chip->irq);
-
- snd_card_set_dev(card, &pci->dev);
-
- *rchip = chip;
- *core_ptr = core;
-
- return 0;
-}
-
-static int __devinit cx88_audio_initdev(struct pci_dev *pci,
- const struct pci_device_id *pci_id)
-{
- struct snd_card *card;
- snd_cx88_card_t *chip;
- struct cx88_core *core = NULL;
- int err;
-
- if (devno >= SNDRV_CARDS)
- return (-ENODEV);
-
- if (!enable[devno]) {
- ++devno;
- return (-ENOENT);
- }
-
- err = snd_card_create(index[devno], id[devno], THIS_MODULE,
- sizeof(snd_cx88_card_t), &card);
- if (err < 0)
- return err;
-
- card->private_free = snd_cx88_dev_free;
-
- err = snd_cx88_create(card, pci, &chip, &core);
- if (err < 0)
- goto error;
-
- err = snd_cx88_pcm(chip, 0, "CX88 Digital");
- if (err < 0)
- goto error;
-
- err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip));
- if (err < 0)
- goto error;
- err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip));
- if (err < 0)
- goto error;
- err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip));
- if (err < 0)
- goto error;
-
- /* If there's a wm8775 then add a Line-In ALC switch */
- if (core->board.audio_chip == V4L2_IDENT_WM8775)
- snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip));
-
- strcpy (card->driver, "CX88x");
- sprintf(card->shortname, "Conexant CX%x", pci->device);
- sprintf(card->longname, "%s at %#llx",
- card->shortname,(unsigned long long)pci_resource_start(pci, 0));
- strcpy (card->mixername, "CX88");
-
- dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
- card->driver,devno);
-
- err = snd_card_register(card);
- if (err < 0)
- goto error;
- pci_set_drvdata(pci,card);
-
- devno++;
- return 0;
-
-error:
- snd_card_free(card);
- return err;
-}
-/*
- * ALSA destructor
- */
-static void __devexit cx88_audio_finidev(struct pci_dev *pci)
-{
- struct cx88_audio_dev *card = pci_get_drvdata(pci);
-
- snd_card_free((void *)card);
-
- pci_set_drvdata(pci, NULL);
-
- devno--;
-}
-
-/*
- * PCI driver definition
- */
-
-static struct pci_driver cx88_audio_pci_driver = {
- .name = "cx88_audio",
- .id_table = cx88_audio_pci_tbl,
- .probe = cx88_audio_initdev,
- .remove = __devexit_p(cx88_audio_finidev),
-};
-
-/****************************************************************************
- LINUX MODULE INIT
- ****************************************************************************/
-
-/*
- * module init
- */
-static int __init cx88_audio_init(void)
-{
- printk(KERN_INFO "cx2388x alsa driver version %s loaded\n",
- CX88_VERSION);
- return pci_register_driver(&cx88_audio_pci_driver);
-}
-
-/*
- * module remove
- */
-static void __exit cx88_audio_fini(void)
-{
- pci_unregister_driver(&cx88_audio_pci_driver);
-}
-
-module_init(cx88_audio_init);
-module_exit(cx88_audio_fini);
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
deleted file mode 100644
index 843ffd9e533b..000000000000
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- *
- * Support for a cx23416 mpeg encoder via cx2388x host port.
- * "blackbird" reference design.
- *
- * (c) 2004 Jelle Foks <jelle@foks.us>
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org>
- *
- * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
- * - video_ioctl2 conversion
- *
- * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/firmware.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-#include <media/cx2341x.h>
-
-#include "cx88.h"
-
-MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards");
-MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CX88_VERSION);
-
-static unsigned int mpegbufs = 32;
-module_param(mpegbufs,int,0644);
-MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
-
-static unsigned int debug;
-module_param(debug,int,0644);
-MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
-
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
-
-
-/* ------------------------------------------------------------------ */
-
-#define BLACKBIRD_FIRM_IMAGE_SIZE 376836
-
-/* defines below are from ivtv-driver.h */
-
-#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
-
-/* Firmware API commands */
-#define IVTV_API_STD_TIMEOUT 500
-
-enum blackbird_capture_type {
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_CAPTURE,
- BLACKBIRD_RAW_PASSTHRU_CAPTURE
-};
-enum blackbird_capture_bits {
- BLACKBIRD_RAW_BITS_NONE = 0x00,
- BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01,
- BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02,
- BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04,
- BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08,
- BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10
-};
-enum blackbird_capture_end {
- BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */
- BLACKBIRD_END_NOW, /* stop immediately, no irq */
-};
-enum blackbird_framerate {
- BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */
- BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */
-};
-enum blackbird_stream_port {
- BLACKBIRD_OUTPUT_PORT_MEMORY,
- BLACKBIRD_OUTPUT_PORT_STREAMING,
- BLACKBIRD_OUTPUT_PORT_SERIAL
-};
-enum blackbird_data_xfer_status {
- BLACKBIRD_MORE_BUFFERS_FOLLOW,
- BLACKBIRD_LAST_BUFFER,
-};
-enum blackbird_picture_mask {
- BLACKBIRD_PICTURE_MASK_NONE,
- BLACKBIRD_PICTURE_MASK_I_FRAMES,
- BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3,
- BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7,
-};
-enum blackbird_vbi_mode_bits {
- BLACKBIRD_VBI_BITS_SLICED,
- BLACKBIRD_VBI_BITS_RAW,
-};
-enum blackbird_vbi_insertion_bits {
- BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA,
- BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1,
- BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1,
- BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1,
- BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1,
-};
-enum blackbird_dma_unit {
- BLACKBIRD_DMA_BYTES,
- BLACKBIRD_DMA_FRAMES,
-};
-enum blackbird_dma_transfer_status_bits {
- BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01,
- BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04,
- BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10,
-};
-enum blackbird_pause {
- BLACKBIRD_PAUSE_ENCODING,
- BLACKBIRD_RESUME_ENCODING,
-};
-enum blackbird_copyright {
- BLACKBIRD_COPYRIGHT_OFF,
- BLACKBIRD_COPYRIGHT_ON,
-};
-enum blackbird_notification_type {
- BLACKBIRD_NOTIFICATION_REFRESH,
-};
-enum blackbird_notification_status {
- BLACKBIRD_NOTIFICATION_OFF,
- BLACKBIRD_NOTIFICATION_ON,
-};
-enum blackbird_notification_mailbox {
- BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1,
-};
-enum blackbird_field1_lines {
- BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */
- BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */
- BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */
-};
-enum blackbird_field2_lines {
- BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */
- BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */
- BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */
-};
-enum blackbird_custom_data_type {
- BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
- BLACKBIRD_CUSTOM_PRIVATE_PACKET,
-};
-enum blackbird_mute {
- BLACKBIRD_UNMUTE,
- BLACKBIRD_MUTE,
-};
-enum blackbird_mute_video_mask {
- BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00,
- BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000,
- BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000,
-};
-enum blackbird_mute_video_shift {
- BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8,
- BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16,
- BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24,
-};
-
-/* Registers */
-#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/)
-#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/)
-#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/)
-#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/)
-#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/)
-#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/)
-
-/* ------------------------------------------------------------------ */
-
-static void host_setup(struct cx88_core *core)
-{
- /* toggle reset of the host */
- cx_write(MO_GPHST_SOFT_RST, 1);
- udelay(100);
- cx_write(MO_GPHST_SOFT_RST, 0);
- udelay(100);
-
- /* host port setup */
- cx_write(MO_GPHST_WSC, 0x44444444U);
- cx_write(MO_GPHST_XFR, 0);
- cx_write(MO_GPHST_WDTH, 15);
- cx_write(MO_GPHST_HDSHK, 0);
- cx_write(MO_GPHST_MUX16, 0x44448888U);
- cx_write(MO_GPHST_MODE, 0);
-}
-
-/* ------------------------------------------------------------------ */
-
-#define P1_MDATA0 0x390000
-#define P1_MDATA1 0x390001
-#define P1_MDATA2 0x390002
-#define P1_MDATA3 0x390003
-#define P1_MADDR2 0x390004
-#define P1_MADDR1 0x390005
-#define P1_MADDR0 0x390006
-#define P1_RDATA0 0x390008
-#define P1_RDATA1 0x390009
-#define P1_RDATA2 0x39000A
-#define P1_RDATA3 0x39000B
-#define P1_RADDR0 0x39000C
-#define P1_RADDR1 0x39000D
-#define P1_RRDWR 0x39000E
-
-static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
-{
- unsigned long timeout = jiffies + msecs_to_jiffies(1);
- u32 gpio0,need;
-
- need = state ? 2 : 0;
- for (;;) {
- gpio0 = cx_read(MO_GP0_IO) & 2;
- if (need == gpio0)
- return 0;
- if (time_after(jiffies,timeout))
- return -1;
- udelay(1);
- }
-}
-
-static int memory_write(struct cx88_core *core, u32 address, u32 value)
-{
- /* Warning: address is dword address (4 bytes) */
- cx_writeb(P1_MDATA0, (unsigned int)value);
- cx_writeb(P1_MDATA1, (unsigned int)(value >> 8));
- cx_writeb(P1_MDATA2, (unsigned int)(value >> 16));
- cx_writeb(P1_MDATA3, (unsigned int)(value >> 24));
- cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40);
- cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
- cx_writeb(P1_MADDR0, (unsigned int)address);
- cx_read(P1_MDATA0);
- cx_read(P1_MADDR0);
-
- return wait_ready_gpio0_bit1(core,1);
-}
-
-static int memory_read(struct cx88_core *core, u32 address, u32 *value)
-{
- int retval;
- u32 val;
-
- /* Warning: address is dword address (4 bytes) */
- cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
- cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
- cx_writeb(P1_MADDR0, (unsigned int)address);
- cx_read(P1_MADDR0);
-
- retval = wait_ready_gpio0_bit1(core,1);
-
- cx_writeb(P1_MDATA3, 0);
- val = (unsigned char)cx_read(P1_MDATA3) << 24;
- cx_writeb(P1_MDATA2, 0);
- val |= (unsigned char)cx_read(P1_MDATA2) << 16;
- cx_writeb(P1_MDATA1, 0);
- val |= (unsigned char)cx_read(P1_MDATA1) << 8;
- cx_writeb(P1_MDATA0, 0);
- val |= (unsigned char)cx_read(P1_MDATA0);
-
- *value = val;
- return retval;
-}
-
-static int register_write(struct cx88_core *core, u32 address, u32 value)
-{
- cx_writeb(P1_RDATA0, (unsigned int)value);
- cx_writeb(P1_RDATA1, (unsigned int)(value >> 8));
- cx_writeb(P1_RDATA2, (unsigned int)(value >> 16));
- cx_writeb(P1_RDATA3, (unsigned int)(value >> 24));
- cx_writeb(P1_RADDR0, (unsigned int)address);
- cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
- cx_writeb(P1_RRDWR, 1);
- cx_read(P1_RDATA0);
- cx_read(P1_RADDR0);
-
- return wait_ready_gpio0_bit1(core,1);
-}
-
-
-static int register_read(struct cx88_core *core, u32 address, u32 *value)
-{
- int retval;
- u32 val;
-
- cx_writeb(P1_RADDR0, (unsigned int)address);
- cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
- cx_writeb(P1_RRDWR, 0);
- cx_read(P1_RADDR0);
-
- retval = wait_ready_gpio0_bit1(core,1);
- val = (unsigned char)cx_read(P1_RDATA0);
- val |= (unsigned char)cx_read(P1_RDATA1) << 8;
- val |= (unsigned char)cx_read(P1_RDATA2) << 16;
- val |= (unsigned char)cx_read(P1_RDATA3) << 24;
-
- *value = val;
- return retval;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA])
-{
- struct cx8802_dev *dev = priv;
- unsigned long timeout;
- u32 value, flag, retval;
- int i;
-
- dprintk(1,"%s: 0x%X\n", __func__, command);
-
- /* this may not be 100% safe if we can't read any memory location
- without side effects */
- memory_read(dev->core, dev->mailbox - 4, &value);
- if (value != 0x12345678) {
- dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
- return -1;
- }
-
- memory_read(dev->core, dev->mailbox, &flag);
- if (flag) {
- dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag);
- return -1;
- }
-
- flag |= 1; /* tell 'em we're working on it */
- memory_write(dev->core, dev->mailbox, flag);
-
- /* write command + args + fill remaining with zeros */
- memory_write(dev->core, dev->mailbox + 1, command); /* command code */
- memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
- for (i = 0; i < in; i++) {
- memory_write(dev->core, dev->mailbox + 4 + i, data[i]);
- dprintk(1, "API Input %d = %d\n", i, data[i]);
- }
- for (; i < CX2341X_MBOX_MAX_DATA; i++)
- memory_write(dev->core, dev->mailbox + 4 + i, 0);
-
- flag |= 3; /* tell 'em we're done writing */
- memory_write(dev->core, dev->mailbox, flag);
-
- /* wait for firmware to handle the API command */
- timeout = jiffies + msecs_to_jiffies(10);
- for (;;) {
- memory_read(dev->core, dev->mailbox, &flag);
- if (0 != (flag & 4))
- break;
- if (time_after(jiffies,timeout)) {
- dprintk(0, "ERROR: API Mailbox timeout\n");
- return -1;
- }
- udelay(10);
- }
-
- /* read output values */
- for (i = 0; i < out; i++) {
- memory_read(dev->core, dev->mailbox + 4 + i, data + i);
- dprintk(1, "API Output %d = %d\n", i, data[i]);
- }
-
- memory_read(dev->core, dev->mailbox + 2, &retval);
- dprintk(1, "API result = %d\n",retval);
-
- flag = 0;
- memory_write(dev->core, dev->mailbox, flag);
- return retval;
-}
-/* ------------------------------------------------------------------ */
-
-/* We don't need to call the API often, so using just one mailbox will probably suffice */
-static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
- u32 inputcnt, u32 outputcnt, ...)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- va_list vargs;
- int i, err;
-
- va_start(vargs, outputcnt);
-
- for (i = 0; i < inputcnt; i++) {
- data[i] = va_arg(vargs, int);
- }
- err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data);
- for (i = 0; i < outputcnt; i++) {
- int *vptr = va_arg(vargs, int *);
- *vptr = data[i];
- }
- va_end(vargs);
- return err;
-}
-
-static int blackbird_find_mailbox(struct cx8802_dev *dev)
-{
- u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
- int signaturecnt=0;
- u32 value;
- int i;
-
- for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
- memory_read(dev->core, i, &value);
- if (value == signature[signaturecnt])
- signaturecnt++;
- else
- signaturecnt = 0;
- if (4 == signaturecnt) {
- dprintk(1, "Mailbox signature found\n");
- return i+1;
- }
- }
- dprintk(0, "Mailbox signature values not found!\n");
- return -1;
-}
-
-static int blackbird_load_firmware(struct cx8802_dev *dev)
-{
- static const unsigned char magic[8] = {
- 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
- };
- const struct firmware *firmware;
- int i, retval = 0;
- u32 value = 0;
- u32 checksum = 0;
- u32 *dataptr;
-
- retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
- retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
- retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
- retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
- msleep(1);
- retval |= register_write(dev->core, IVTV_REG_APU, 0);
-
- if (retval < 0)
- dprintk(0, "Error with register_write\n");
-
- retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME,
- &dev->pci->dev);
-
-
- if (retval != 0) {
- dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
- CX2341X_FIRM_ENC_FILENAME);
- dprintk(0, "Please fix your hotplug setup, the board will "
- "not work without firmware loaded!\n");
- return -1;
- }
-
- if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
- dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n",
- firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
- release_firmware(firmware);
- return -1;
- }
-
- if (0 != memcmp(firmware->data, magic, 8)) {
- dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
- release_firmware(firmware);
- return -1;
- }
-
- /* transfer to the chip */
- dprintk(1,"Loading firmware ...\n");
- dataptr = (u32*)firmware->data;
- for (i = 0; i < (firmware->size >> 2); i++) {
- value = le32_to_cpu(*dataptr);
- checksum += ~value;
- memory_write(dev->core, i, value);
- dataptr++;
- }
-
- /* read back to verify with the checksum */
- for (i--; i >= 0; i--) {
- memory_read(dev->core, i, &value);
- checksum -= ~value;
- }
- if (checksum) {
- dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
- release_firmware(firmware);
- return -1;
- }
- release_firmware(firmware);
- dprintk(0, "Firmware upload successful.\n");
-
- retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
- retval |= register_read(dev->core, IVTV_REG_SPU, &value);
- retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
- msleep(1);
-
- retval |= register_read(dev->core, IVTV_REG_VPU, &value);
- retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
-
- if (retval < 0)
- dprintk(0, "Error with register_write\n");
- return 0;
-}
-
-/**
- Settings used by the windows tv app for PVR2000:
-=================================================================================================================
-Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode
------------------------------------------------------------------------------------------------------------------
-MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
-MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
-VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
-DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
-DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo
-=================================================================================================================
-*DB: "DirectBurn"
-*/
-
-static void blackbird_codec_settings(struct cx8802_dev *dev)
-{
- /* assign frame size */
- blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
- dev->height, dev->width);
-
- dev->cxhdl.width = dev->width;
- dev->cxhdl.height = dev->height;
- cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50);
- cx2341x_handler_setup(&dev->cxhdl);
-}
-
-static int blackbird_initialize_codec(struct cx8802_dev *dev)
-{
- struct cx88_core *core = dev->core;
- int version;
- int retval;
-
- dprintk(1,"Initialize codec\n");
- retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
- if (retval < 0) {
-
- dev->mpeg_active = 0;
-
- /* ping was not successful, reset and upload firmware */
- cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */
- cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
- retval = blackbird_load_firmware(dev);
- if (retval < 0)
- return retval;
-
- retval = blackbird_find_mailbox(dev);
- if (retval < 0)
- return -1;
-
- dev->mailbox = retval;
-
- retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */
- if (retval < 0) {
- dprintk(0, "ERROR: Firmware ping failed!\n");
- return -1;
- }
-
- retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version);
- if (retval < 0) {
- dprintk(0, "ERROR: Firmware get encoder version failed!\n");
- return -1;
- }
- dprintk(0, "Firmware version is 0x%08x\n", version);
- }
-
- cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */
- cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */
- cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */
- cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */
-
- blackbird_codec_settings(dev);
-
- blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0,
- BLACKBIRD_FIELD1_SAA7115,
- BLACKBIRD_FIELD2_SAA7115
- );
-
- blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0,
- BLACKBIRD_CUSTOM_EXTENSION_USR_DATA,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- return 0;
-}
-
-static int blackbird_start_codec(struct file *file, void *priv)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
- /* start capturing to the host interface */
- u32 reg;
-
- int i;
- int lastchange = -1;
- int lastval = 0;
-
- for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) {
- reg = cx_read(AUD_STATUS);
-
- dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg);
- if ((reg & 0x0F) != lastval) {
- lastval = reg & 0x0F;
- lastchange = i;
- }
- msleep(100);
- }
-
- /* unmute audio source */
- cx_clear(AUD_VOL_CTL, (1 << 6));
-
- blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0);
-
- /* initialize the video input */
- blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0);
-
- cx2341x_handler_set_busy(&dev->cxhdl, 1);
-
- /* start capturing to the host interface */
- blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE
- );
-
- dev->mpeg_active = 1;
- return 0;
-}
-
-static int blackbird_stop_codec(struct cx8802_dev *dev)
-{
- blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0,
- BLACKBIRD_END_NOW,
- BLACKBIRD_MPEG_CAPTURE,
- BLACKBIRD_RAW_BITS_NONE
- );
-
- cx2341x_handler_set_busy(&dev->cxhdl, 0);
-
- dev->mpeg_active = 0;
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int bb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx8802_fh *fh = q->priv_data;
-
- fh->dev->ts_packet_size = 188 * 4; /* was: 512 */
- fh->dev->ts_packet_count = mpegbufs; /* was: 100 */
-
- *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
- *count = fh->dev->ts_packet_count;
- return 0;
-}
-
-static int
-bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx8802_fh *fh = q->priv_data;
- return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field);
-}
-
-static void
-bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct cx8802_fh *fh = q->priv_data;
- cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
-}
-
-static void
-bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- cx88_free_buffer(q, (struct cx88_buffer*)vb);
-}
-
-static struct videobuf_queue_ops blackbird_qops = {
- .buf_setup = bb_buf_setup,
- .buf_prepare = bb_buf_prepare,
- .buf_queue = bb_buf_queue,
- .buf_release = bb_buf_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
-
- strcpy(cap->driver, "cx88_blackbird");
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cx88_querycap(file, core, cap);
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (f->index != 0)
- return -EINVAL;
-
- strlcpy(f->description, "MPEG", sizeof(f->description));
- f->pixelformat = V4L2_PIX_FMT_MPEG;
- f->flags = V4L2_FMT_FLAG_COMPRESSED;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = fh->mpegq.field;
- dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n",
- dev->width, dev->height, fh->mpegq.field );
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
- dev->width, dev->height, fh->mpegq.field );
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
- struct cx88_core *core = dev->core;
-
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- fh->mpegq.field = f->fmt.pix.field;
- cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
- blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0,
- f->fmt.pix.height, f->fmt.pix.width);
- dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n",
- f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field );
- return 0;
-}
-
-static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_reqbufs(&fh->mpegq, p));
-}
-
-static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_querybuf(&fh->mpegq, p));
-}
-
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_qbuf(&fh->mpegq, p));
-}
-
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct cx8802_fh *fh = priv;
- return (videobuf_dqbuf(&fh->mpegq, p,
- file->f_flags & O_NONBLOCK));
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active)
- blackbird_start_codec(file, fh);
- return videobuf_streamon(&fh->mpegq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
-
- if (dev->mpeg_active)
- blackbird_stop_codec(dev);
- return videobuf_streamoff(&fh->mpegq);
-}
-
-static int vidioc_s_frequency (struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx8802_fh *fh = priv;
- struct cx8802_dev *dev = fh->dev;
- struct cx88_core *core = dev->core;
-
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (unlikely(f->tuner != 0))
- return -EINVAL;
- if (dev->mpeg_active)
- blackbird_stop_codec(dev);
-
- cx88_set_freq (core,f);
- blackbird_initialize_codec(dev);
- cx88_set_scale(dev->core, dev->width, dev->height,
- fh->mpegq.field);
- return 0;
-}
-
-static int vidioc_log_status (struct file *file, void *priv)
-{
- struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
- char name[32 + 2];
-
- snprintf(name, sizeof(name), "%s/2", core->name);
- call_all(core, core, log_status);
- v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name);
- return 0;
-}
-
-static int vidioc_enum_input (struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
- return cx88_enum_input (core,i);
-}
-
-static int vidioc_g_frequency (struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx8802_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
-
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (unlikely(f->tuner != 0))
- return -EINVAL;
-
- f->frequency = core->freq;
- call_all(core, tuner, g_frequency, f);
-
- return 0;
-}
-
-static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- *i = core->input;
- return 0;
-}
-
-static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- if (i >= 4)
- return -EINVAL;
- if (0 == INPUT(i).type)
- return -EINVAL;
-
- mutex_lock(&core->lock);
- cx88_newstation(core);
- cx88_video_mux(core,i);
- mutex_unlock(&core->lock);
- return 0;
-}
-
-static int vidioc_g_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
- u32 reg;
-
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Television");
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
- call_all(core, tuner, g_tuner, t);
-
- cx88_get_stereo(core ,t);
- reg = cx_read(MO_DEVICE_STATUS);
- t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
- return 0;
-}
-
-static int vidioc_s_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- if (UNSET == core->board.tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- cx88_set_stereo(core, t->audmode, 1);
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- *tvnorm = core->tvnorm;
- return 0;
-}
-
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
-{
- struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core;
-
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core,*id);
- mutex_unlock(&core->lock);
- return 0;
-}
-
-/* FIXME: cx88_ioctl_hook not implemented */
-
-static int mpeg_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8802_dev *dev = video_drvdata(file);
- struct cx8802_fh *fh;
- struct cx8802_driver *drv = NULL;
- int err;
-
- dprintk( 1, "%s\n", __func__);
-
- mutex_lock(&dev->core->lock);
-
- /* Make sure we can acquire the hardware */
- drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
- if (!drv) {
- dprintk(1, "%s: blackbird driver is not loaded\n", __func__);
- mutex_unlock(&dev->core->lock);
- return -ENODEV;
- }
-
- err = drv->request_acquire(drv);
- if (err != 0) {
- dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err);
- mutex_unlock(&dev->core->lock);
- return err;
- }
-
- if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) {
- drv->request_release(drv);
- mutex_unlock(&dev->core->lock);
- return -EINVAL;
- }
- dprintk(1, "open dev=%s\n", video_device_node_name(vdev));
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh) {
- drv->request_release(drv);
- mutex_unlock(&dev->core->lock);
- return -ENOMEM;
- }
- v4l2_fh_init(&fh->fh, vdev);
- file->private_data = fh;
- fh->dev = dev;
-
- videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx88_buffer),
- fh, NULL);
-
- /* FIXME: locking against other video device */
- cx88_set_scale(dev->core, dev->width, dev->height,
- fh->mpegq.field);
-
- dev->core->mpeg_users++;
- mutex_unlock(&dev->core->lock);
- v4l2_fh_add(&fh->fh);
- return 0;
-}
-
-static int mpeg_release(struct file *file)
-{
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
- struct cx8802_driver *drv = NULL;
-
- mutex_lock(&dev->core->lock);
-
- if (dev->mpeg_active && dev->core->mpeg_users == 1)
- blackbird_stop_codec(dev);
-
- cx8802_cancel_buffers(fh->dev);
- /* stop mpeg capture */
- videobuf_stop(&fh->mpegq);
-
- videobuf_mmap_free(&fh->mpegq);
-
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- file->private_data = NULL;
- kfree(fh);
-
- /* Make sure we release the hardware */
- drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD);
- WARN_ON(!drv);
- if (drv)
- drv->request_release(drv);
-
- dev->core->mpeg_users--;
-
- mutex_unlock(&dev->core->lock);
-
- return 0;
-}
-
-static ssize_t
-mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active)
- blackbird_start_codec(file, fh);
-
- return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0,
- file->f_flags & O_NONBLOCK);
-}
-
-static unsigned int
-mpeg_poll(struct file *file, struct poll_table_struct *wait)
-{
- unsigned long req_events = poll_requested_events(wait);
- struct cx8802_fh *fh = file->private_data;
- struct cx8802_dev *dev = fh->dev;
-
- if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM)))
- blackbird_start_codec(file, fh);
-
- return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait);
-}
-
-static int
-mpeg_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct cx8802_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->mpegq, vma);
-}
-
-static const struct v4l2_file_operations mpeg_fops =
-{
- .owner = THIS_MODULE,
- .open = mpeg_open,
- .release = mpeg_release,
- .read = mpeg_read,
- .poll = mpeg_poll,
- .mmap = mpeg_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static struct video_device cx8802_mpeg_template = {
- .name = "cx8802",
- .fops = &mpeg_fops,
- .ioctl_ops = &mpeg_ioctl_ops,
- .tvnorms = CX88_NORMS,
-};
-
-/* ------------------------------------------------------------------ */
-
-/* The CX8802 MPEG API will call this when we can use the hardware */
-static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- int err = 0;
-
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* By default, core setup will leave the cx22702 out of reset, on the bus.
- * We left the hardware on power up with the cx22702 active.
- * We're being given access to re-arrange the GPIOs.
- * Take the bus off the cx22702 and put the cx23416 on it.
- */
- /* Toggle reset on cx22702 leaving i2c active */
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- /* tri-state the cx22702 pins */
- cx_set(MO_GP0_IO, 0x00000004);
- udelay(1000);
- break;
- default:
- err = -ENODEV;
- }
- return err;
-}
-
-/* The CX8802 MPEG API will call this when we need to release the hardware */
-static int cx8802_blackbird_advise_release(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- int err = 0;
-
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* Exit leaving the cx23416 on the bus */
- break;
- default:
- err = -ENODEV;
- }
- return err;
-}
-
-static void blackbird_unregister_video(struct cx8802_dev *dev)
-{
- if (dev->mpeg_dev) {
- if (video_is_registered(dev->mpeg_dev))
- video_unregister_device(dev->mpeg_dev);
- else
- video_device_release(dev->mpeg_dev);
- dev->mpeg_dev = NULL;
- }
-}
-
-static int blackbird_register_video(struct cx8802_dev *dev)
-{
- int err;
-
- dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci,
- &cx8802_mpeg_template,"mpeg");
- dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl;
- video_set_drvdata(dev->mpeg_dev, dev);
- err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1);
- if (err < 0) {
- printk(KERN_INFO "%s/2: can't register mpeg device\n",
- dev->core->name);
- return err;
- }
- printk(KERN_INFO "%s/2: registered device %s [mpeg]\n",
- dev->core->name, video_device_node_name(dev->mpeg_dev));
- return 0;
-}
-
-/* ----------------------------------------------------------- */
-
-static int cx8802_blackbird_probe(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- struct cx8802_dev *dev = core->dvbdev;
- int err;
-
- dprintk( 1, "%s\n", __func__);
- dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
- core->boardnr,
- core->name,
- core->pci_bus,
- core->pci_slot);
-
- err = -ENODEV;
- if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))
- goto fail_core;
-
- dev->width = 720;
- if (core->tvnorm & V4L2_STD_525_60) {
- dev->height = 480;
- } else {
- dev->height = 576;
- }
- dev->cxhdl.port = CX2341X_PORT_STREAMING;
- dev->cxhdl.width = dev->width;
- dev->cxhdl.height = dev->height;
- dev->cxhdl.func = blackbird_mbox_func;
- dev->cxhdl.priv = dev;
- err = cx2341x_handler_init(&dev->cxhdl, 36);
- if (err)
- goto fail_core;
- v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl);
-
- /* blackbird stuff */
- printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
- core->name);
- host_setup(dev->core);
-
- blackbird_initialize_codec(dev);
-
- /* initial device configuration: needed ? */
-// init_controls(core);
- cx88_set_tvnorm(core,core->tvnorm);
- cx88_video_mux(core,0);
- cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576);
- cx2341x_handler_setup(&dev->cxhdl);
- blackbird_register_video(dev);
-
- return 0;
-
- fail_core:
- return err;
-}
-
-static int cx8802_blackbird_remove(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- struct cx8802_dev *dev = core->dvbdev;
-
- /* blackbird */
- blackbird_unregister_video(drv->core->dvbdev);
- v4l2_ctrl_handler_free(&dev->cxhdl.hdl);
-
- return 0;
-}
-
-static struct cx8802_driver cx8802_blackbird_driver = {
- .type_id = CX88_MPEG_BLACKBIRD,
- .hw_access = CX8802_DRVCTL_SHARED,
- .probe = cx8802_blackbird_probe,
- .remove = cx8802_blackbird_remove,
- .advise_acquire = cx8802_blackbird_advise_acquire,
- .advise_release = cx8802_blackbird_advise_release,
-};
-
-static int __init blackbird_init(void)
-{
- printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n",
- CX88_VERSION);
- return cx8802_register_driver(&cx8802_blackbird_driver);
-}
-
-static void __exit blackbird_fini(void)
-{
- cx8802_unregister_driver(&cx8802_blackbird_driver);
-}
-
-module_init(blackbird_init);
-module_exit(blackbird_fini);
-
-module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages [video]");
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
deleted file mode 100644
index 4e9d4f722960..000000000000
--- a/drivers/media/video/cx88/cx88-cards.c
+++ /dev/null
@@ -1,3811 +0,0 @@
-/*
- *
- * device driver for Conexant 2388x based TV cards
- * card-specific stuff.
- *
- * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include "cx88.h"
-#include "tea5767.h"
-#include "xc4000.h"
-
-static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(tuner, int, NULL, 0444);
-module_param_array(radio, int, NULL, 0444);
-module_param_array(card, int, NULL, 0444);
-
-MODULE_PARM_DESC(tuner,"tuner type");
-MODULE_PARM_DESC(radio,"radio tuner type");
-MODULE_PARM_DESC(card,"card type");
-
-static unsigned int latency = UNSET;
-module_param(latency,int,0444);
-MODULE_PARM_DESC(latency,"pci latency timer");
-
-static int disable_ir;
-module_param(disable_ir, int, 0444);
-MODULE_PARM_DESC(disable_ir, "Disable IR support");
-
-#define info_printk(core, fmt, arg...) \
- printk(KERN_INFO "%s: " fmt, core->name , ## arg)
-
-#define warn_printk(core, fmt, arg...) \
- printk(KERN_WARNING "%s: " fmt, core->name , ## arg)
-
-#define err_printk(core, fmt, arg...) \
- printk(KERN_ERR "%s: " fmt, core->name , ## arg)
-
-
-/* ------------------------------------------------------------------ */
-/* board config info */
-
-/* If radio_type !=UNSET, radio_addr should be specified
- */
-
-static const struct cx88_board cx88_boards[] = {
- [CX88_BOARD_UNKNOWN] = {
- .name = "UNKNOWN/GENERIC",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE2,
- .vmux = 1,
- },{
- .type = CX88_VMUX_COMPOSITE3,
- .vmux = 2,
- },{
- .type = CX88_VMUX_COMPOSITE4,
- .vmux = 3,
- }},
- },
- [CX88_BOARD_HAUPPAUGE] = {
- .name = "Hauppauge WinTV 34xxx models",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00, // internal decoder
- },{
- .type = CX88_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01, // mono from tuner chip
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xff02,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xff01,
- },
- },
- [CX88_BOARD_GDI] = {
- .name = "GDI Black Gold",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- },
- [CX88_BOARD_PIXELVIEW] = {
- .name = "PixelView",
- .tuner_type = TUNER_PHILIPS_PAL,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00, // internal decoder
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xff10,
- },
- },
- [CX88_BOARD_ATI_WONDER_PRO] = {
- .name = "ATI TV Wonder Pro",
- .tuner_type = TUNER_PHILIPS_4IN1,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x03ff,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x03fe,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x03fe,
- }},
- },
- [CX88_BOARD_WINFAST2000XP_EXPERT] = {
- .name = "Leadtek Winfast 2000XP Expert",
- .tuner_type = TUNER_PHILIPS_4IN1,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00F5e700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x00F5e700,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00F5c700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x00F5c700,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00F5c700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x00F5c700,
- .gpio3 = 0x02000000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00F5d700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x00F5d700,
- .gpio3 = 0x02000000,
- },
- },
- [CX88_BOARD_AVERTV_STUDIO_303] = {
- .name = "AverTV Studio 303 (M126)",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio1 = 0xe09f,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio1 = 0xe05f,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio1 = 0xe05f,
- }},
- .radio = {
- .gpio1 = 0xe0df,
- .type = CX88_RADIO,
- },
- },
- [CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
- // added gpio values thanks to Michal
- // values for PAL from DScaler
- .name = "MSI TV-@nywhere Master",
- .tuner_type = TUNER_MT2032,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x000040bf,
- .gpio1 = 0x000080c0,
- .gpio2 = 0x0000ff40,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000040bf,
- .gpio1 = 0x000080c0,
- .gpio2 = 0x0000ff40,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000040bf,
- .gpio1 = 0x000080c0,
- .gpio2 = 0x0000ff40,
- }},
- .radio = {
- .type = CX88_RADIO,
- .vmux = 3,
- .gpio0 = 0x000040bf,
- .gpio1 = 0x000080c0,
- .gpio2 = 0x0000ff20,
- },
- },
- [CX88_BOARD_WINFAST_DV2000] = {
- .name = "Leadtek Winfast DV2000",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0035e700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x0035e700,
- .gpio3 = 0x02000000,
- },{
-
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0035c700,
- .gpio1 = 0x00003004,
- .gpio2 = 0x0035c700,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0035c700,
- .gpio1 = 0x0035c700,
- .gpio2 = 0x02000000,
- .gpio3 = 0x02000000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0035d700,
- .gpio1 = 0x00007004,
- .gpio2 = 0x0035d700,
- .gpio3 = 0x02000000,
- },
- },
- [CX88_BOARD_LEADTEK_PVR2000] = {
- // gpio values for PAL version from regspy by DScaler
- .name = "Leadtek PVR 2000",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0000bde2,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0000bde6,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0000bde6,
- .audioroute = 1,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0000bd62,
- .audioroute = 1,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_IODATA_GVVCP3PCI] = {
- .name = "IODATA GV-VCP3/PCI",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE2,
- .vmux = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- },
- [CX88_BOARD_PROLINK_PLAYTVPVR] = {
- .name = "Prolink PlayTV PVR",
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xbff0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xbff3,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xbff3,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xbff0,
- },
- },
- [CX88_BOARD_ASUS_PVR_416] = {
- .name = "ASUS PVR-416",
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0000fde6,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
- .audioroute = 1,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0000fde2,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_MSI_TVANYWHERE] = {
- .name = "MSI TV-@nywhere",
- .tuner_type = TUNER_MT2032,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00000fbf,
- .gpio2 = 0x0000fc08,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00000fbf,
- .gpio2 = 0x0000fc68,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00000fbf,
- .gpio2 = 0x0000fc68,
- }},
- },
- [CX88_BOARD_KWORLD_DVB_T] = {
- .name = "KWorld/VStream XPert DVB-T",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
- .name = "DViCO FusionHDTV DVB-T1",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000027df,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000027df,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_KWORLD_LTV883] = {
- .name = "KWorld LTV883RF",
- .tuner_type = TUNER_TNF_8831BGFF,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x07f8,
- },{
- .type = CX88_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0x07f9, // mono from tuner chip
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000007fa,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000007fa,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x000007f8,
- },
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = {
- .name = "DViCO FusionHDTV 3 Gold-Q",
- .tuner_type = TUNER_MICROTUNE_4042FI5,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- /*
- GPIO[0] resets DT3302 DTV receiver
- 0 - reset asserted
- 1 - normal operation
- GPIO[1] mutes analog audio output connector
- 0 - enable selected source
- 1 - mute
- GPIO[2] selects source for analog audio output connector
- 0 - analog audio input connector on tab
- 1 - analog DAC output from CX23881 chip
- GPIO[3] selects RF input connector on tuner module
- 0 - RF connector labeled CABLE
- 1 - RF connector labeled ANT
- GPIO[4] selects high RF for QAM256 mode
- 0 - normal RF
- 1 - high RF
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0f0d,
- },{
- .type = CX88_VMUX_CABLE,
- .vmux = 0,
- .gpio0 = 0x0f05,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0f00,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0f00,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_DVB_T1] = {
- .name = "Hauppauge Nova-T DVB-T",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_CONEXANT_DVB_T1] = {
- .name = "Conexant DVB-T reference design",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PROVIDEO_PV259] = {
- .name = "Provideo PV259",
- .tuner_type = TUNER_PHILIPS_FQ1216ME,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .audioroute = 1,
- }},
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = {
- .name = "DViCO FusionHDTV DVB-T Plus",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000027df,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000027df,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DNTV_LIVE_DVB_T] = {
- .name = "digitalnow DNTV Live! DVB-T",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00000700,
- .gpio2 = 0x00000101,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00000700,
- .gpio2 = 0x00000101,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PCHDTV_HD3000] = {
- .name = "pcHDTV HD3000 HDTV",
- .tuner_type = TUNER_THOMSON_DTT761X,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- /* GPIO[2] = audio source for analog audio out connector
- * 0 = analog audio input connector
- * 1 = CX88 audio DACs
- *
- * GPIO[7] = input to CX88's audio/chroma ADC
- * 0 = FM 10.7 MHz IF
- * 1 = Sound 4.5 MHz IF
- *
- * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively
- *
- * GPIO[16] = Remote control input
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00008484,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00008400,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00008400,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00008404,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_ROSLYN] = {
- // entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu>
- // GPIO values obtained from regspy, courtesy Sean Covel
- .name = "Hauppauge WinTV 28xxx (Roslyn) models",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xed1a,
- .gpio2 = 0x00ff,
- },{
- .type = CX88_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xed92,
- .gpio2 = 0x00ff,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xed96,
- .gpio2 = 0x00ff,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_DIGITALLOGIC_MEC] = {
- .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00009d80,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00009d76,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00009d76,
- .audioroute = 1,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00009d00,
- .audioroute = 1,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_IODATA_GVBCTV7E] = {
- .name = "IODATA GV/BCTV7E",
- .tuner_type = TUNER_PHILIPS_FQ1286,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 1,
- .gpio1 = 0x0000e03f,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 2,
- .gpio1 = 0x0000e07f,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 3,
- .gpio1 = 0x0000e07f,
- }}
- },
- [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = {
- .name = "PixelView PlayTV Ultra Pro (Stereo)",
- /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- /* Some variants use a tda9874 and so need the tvaudio module. */
- .audio_chip = V4L2_IDENT_TVAUDIO,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xbf61, /* internal decoder */
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xbf63,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xbf63,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xbf60,
- },
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = {
- .name = "DViCO FusionHDTV 3 Gold-T",
- .tuner_type = TUNER_THOMSON_DTT761X,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x97ed,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x97e9,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x97e9,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_ADSTECH_DVB_T_PCI] = {
- .name = "ADS Tech Instant TV DVB-T PCI",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = {
- .name = "TerraTec Cinergy 1400 DVB-T",
- .tuner_type = TUNER_ABSENT,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 2,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = {
- .name = "DViCO FusionHDTV 5 Gold",
- .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x87fd,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x87f9,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x87f9,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = {
- .name = "AverMedia UltraTV Media Center PCI 550",
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 0,
- .gpio0 = 0x0000cd73,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 1,
- .gpio0 = 0x0000cd73,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 3,
- .gpio0 = 0x0000cdb3,
- .audioroute = 1,
- }},
- .radio = {
- .type = CX88_RADIO,
- .vmux = 2,
- .gpio0 = 0x0000cdf3,
- .audioroute = 1,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = {
- /* Alexander Wold <awold@bigfoot.com> */
- .name = "Kworld V-Stream Xpert DVD",
- .tuner_type = UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x03000000,
- .gpio1 = 0x01000000,
- .gpio2 = 0x02000000,
- .gpio3 = 0x00100000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x03000000,
- .gpio1 = 0x01000000,
- .gpio2 = 0x02000000,
- .gpio3 = 0x00100000,
- }},
- },
- [CX88_BOARD_ATI_HDTVWONDER] = {
- .name = "ATI HDTV Wonder",
- .tuner_type = TUNER_PHILIPS_TUV1236D,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00000ff7,
- .gpio1 = 0x000000ff,
- .gpio2 = 0x00000001,
- .gpio3 = 0x00000000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00000ffe,
- .gpio1 = 0x000000ff,
- .gpio2 = 0x00000001,
- .gpio3 = 0x00000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00000ffe,
- .gpio1 = 0x000000ff,
- .gpio2 = 0x00000001,
- .gpio3 = 0x00000000,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_WINFAST_DTV1000] = {
- .name = "WinFast DTV1000-T",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_AVERTV_303] = {
- .name = "AVerTV 303 (M126)",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00ff,
- .gpio1 = 0xe09f,
- .gpio2 = 0x0010,
- .gpio3 = 0x0000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00ff,
- .gpio1 = 0xe05f,
- .gpio2 = 0x0010,
- .gpio3 = 0x0000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00ff,
- .gpio1 = 0xe05f,
- .gpio2 = 0x0010,
- .gpio3 = 0x0000,
- }},
- },
- [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = {
- .name = "Hauppauge Nova-S-Plus DVB-S",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .audio_chip = V4L2_IDENT_WM8775,
- .i2sinputcntl = 2,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- /* 2: Line-In */
- .audioroute = 2,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = {
- .name = "Hauppauge Nova-SE2 DVB-S",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_KWORLD_DVBS_100] = {
- .name = "KWorld DVB-S 100",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .audio_chip = V4L2_IDENT_WM8775,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- /* 2: Line-In */
- .audioroute = 2,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_HVR1100] = {
- .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- }},
- /* fixme: Add radio support */
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_HVR1100LP] = {
- .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- }},
- /* fixme: Add radio support */
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = {
- .name = "digitalnow DNTV Live! DVB-T Pro",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xf80808,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xf80808,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xf80808,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xf80808,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_KWORLD_DVB_T_CX22702] = {
- /* Kworld V-stream Xpert DVB-T with Thomson tuner */
- /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */
- /* Manenti Marco <marco_manenti@colman.it> */
- .name = "KWorld/VStream XPert DVB-T with cx22702",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0700,
- .gpio2 = 0x0101,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = {
- .name = "DViCO FusionHDTV DVB-T Dual Digital",
- .tuner_type = TUNER_ABSENT, /* No analog tuner */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000067df,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000067df,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = {
- .name = "KWorld HardwareMpegTV XPert",
- .tuner_type = TUNER_PHILIPS_TDA8290,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x3de2,
- .gpio2 = 0x00ff,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x3de6,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x3de6,
- .audioroute = 1,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x3de6,
- .gpio2 = 0x00ff,
- },
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = {
- .name = "DViCO FusionHDTV DVB-T Hybrid",
- .tuner_type = TUNER_THOMSON_FE6600,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0000a75f,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0000a75b,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0000a75b,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PCHDTV_HD5500] = {
- .name = "pcHDTV HD5500 HDTV",
- .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x87fd,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x87f9,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x87f9,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_KWORLD_MCE200_DELUXE] = {
- /* FIXME: tested TV input only, disabled composite,
- svideo and radio until they can be tested also. */
- .name = "Kworld MCE 200 Deluxe",
- .tuner_type = TUNER_TENA_9533_DI,
- .radio_type = UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0000BDE6
- }},
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = {
- /* FIXME: SVideo, Composite and FM inputs are untested */
- .name = "PixelView PlayTV P7000",
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x5da6,
- }},
- .mpeg = CX88_MPEG_BLACKBIRD,
- },
- [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = {
- .name = "NPG Tech Real TV FM Top 10",
- .tuner_type = TUNER_TNF_5335MF, /* Actually a TNF9535 */
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0788,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x078b,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x078b,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x074a,
- },
- },
- [CX88_BOARD_WINFAST_DTV2000H] = {
- .name = "WinFast DTV2000 H",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00017304,
- .gpio1 = 0x00008203,
- .gpio2 = 0x00017304,
- .gpio3 = 0x02000000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0001d701,
- .gpio1 = 0x0000b207,
- .gpio2 = 0x0001d701,
- .gpio3 = 0x02000000,
- }, {
- .type = CX88_VMUX_COMPOSITE2,
- .vmux = 2,
- .gpio0 = 0x0001d503,
- .gpio1 = 0x0000b207,
- .gpio2 = 0x0001d503,
- .gpio3 = 0x02000000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 3,
- .gpio0 = 0x0001d701,
- .gpio1 = 0x0000b207,
- .gpio2 = 0x0001d701,
- .gpio3 = 0x02000000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00015702,
- .gpio1 = 0x0000f207,
- .gpio2 = 0x00015702,
- .gpio3 = 0x02000000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_WINFAST_DTV2000H_J] = {
- .name = "WinFast DTV2000 H rev. J",
- .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00017300,
- .gpio1 = 0x00008207,
- .gpio2 = 0x00000000,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00018300,
- .gpio1 = 0x0000f207,
- .gpio2 = 0x00017304,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00018301,
- .gpio1 = 0x0000f207,
- .gpio2 = 0x00017304,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00018301,
- .gpio1 = 0x0000f207,
- .gpio2 = 0x00017304,
- .gpio3 = 0x02000000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00015702,
- .gpio1 = 0x0000f207,
- .gpio2 = 0x00015702,
- .gpio3 = 0x02000000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_GENIATECH_DVBS] = {
- .name = "Geniatech DVB-S",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_HVR3000] = {
- .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x84bf,
- /* 1: TV Audio / FM Mono */
- .audioroute = 1,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x84bf,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x84bf,
- /* 2: Line-In */
- .audioroute = 2,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x84bf,
- /* 4: FM Stereo (untested) */
- .audioroute = 8,
- },
- .mpeg = CX88_MPEG_DVB,
- .num_frontends = 2,
- },
- [CX88_BOARD_NORWOOD_MICRO] = {
- .name = "Norwood Micro TV Tuner",
- .tuner_type = TUNER_TNF_5335MF,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0709,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x070b,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x070b,
- }},
- },
- [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
- .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x003fffff,
- .gpio1 = 0x00e00000,
- .gpio2 = 0x003fffff,
- .gpio3 = 0x02000000,
- }},
- },
- [CX88_BOARD_HAUPPAUGE_HVR1300] = {
- .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
- /*
- * gpio0 as reported by Mike Crash <mike AT mikecrash.com>
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xef88,
- /* 1: TV Audio / FM Mono */
- .audioroute = 1,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xef88,
- /* 2: Line-In */
- .audioroute = 2,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xef88,
- /* 2: Line-In */
- .audioroute = 2,
- }},
- .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD,
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xef88,
- /* 4: FM Stereo (untested) */
- .audioroute = 8,
- },
- },
- [CX88_BOARD_SAMSUNG_SMT_7020] = {
- .name = "Samsung SMT 7020 DVB-S",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_ADSTECH_PTV_390] = {
- .name = "ADS Tech Instant Video PCI",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DEBUG,
- .vmux = 3,
- .gpio0 = 0x04ff,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x07fa,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x07fa,
- }},
- },
- [CX88_BOARD_PINNACLE_PCTV_HD_800i] = {
- .name = "Pinnacle PCTV HD 800i",
- .tuner_type = TUNER_XC5000,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x04fb,
- .gpio1 = 0x10ff,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x04fb,
- .gpio1 = 0x10ef,
- .audioroute = 1,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x04fb,
- .gpio1 = 0x10ef,
- .audioroute = 1,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = {
- .name = "DViCO FusionHDTV 5 PCI nano",
- /* xc3008 tuner, digital only for now */
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x000027df, /* Unconfirmed */
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000027df, /* Unconfirmed */
- .audioroute = 1,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000027df, /* Unconfirmed */
- .audioroute = 1,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PINNACLE_HYBRID_PCTV] = {
- .name = "Pinnacle Hybrid PCTV",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x004ff,
- .gpio1 = 0x010ff,
- .gpio2 = 0x00001,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x004fb,
- .gpio1 = 0x010ef,
- .audioroute = 1,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x004fb,
- .gpio1 = 0x010ef,
- .audioroute = 1,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x004ff,
- .gpio1 = 0x010ff,
- .gpio2 = 0x0ff,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- /* Terry Wu <terrywu2009@gmail.com> */
- /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */
- /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */
- /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */
- /* Mute Audio : set GPIO 2 value to 1 */
- [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = {
- .name = "Leadtek TV2000 XP Global",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
- .gpio3 = 0x0000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
- .gpio3 = 0x0000,
- },
- },
- [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36] = {
- .name = "Leadtek TV2000 XP Global (SC4100)",
- .tuner_type = TUNER_XC4000,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */
- .gpio3 = 0x0000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x0000,
- .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */
- .gpio3 = 0x0000,
- },
- },
- [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43] = {
- .name = "Leadtek TV2000 XP Global (XC4100)",
- .tuner_type = TUNER_XC4000,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6040, /* pin 14 = 1, pin 13 = 0 */
- .gpio2 = 0x0000,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
- .gpio2 = 0x0000,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */
- .gpio2 = 0x0000,
- .gpio3 = 0x0000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6000, /* pin 14 = 1, pin 13 = 0 */
- .gpio2 = 0x0000,
- .gpio3 = 0x0000,
- },
- },
- [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = {
- .name = "PowerColor RA330", /* Long names may confuse LIRC. */
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .input = { {
- .type = CX88_VMUX_DEBUG,
- .vmux = 3, /* Due to the way the cx88 driver is written, */
- .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */
- .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */
- .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */
- }, { /* from the tuner on boot for a little while. */
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00ff,
- .gpio1 = 0xf35d,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00ff,
- .gpio1 = 0xf37d,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000ff,
- .gpio1 = 0x0f37d,
- .gpio3 = 0x00000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x000ff,
- .gpio1 = 0x0f35d,
- .gpio3 = 0x00000,
- },
- },
- [CX88_BOARD_GENIATECH_X8000_MT] = {
- /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */
- .name = "Geniatech X8000-MT DVBT",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x00000000,
- .gpio1 = 0x00e3e341,
- .gpio2 = 0x00000000,
- .gpio3 = 0x00000000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x00000000,
- .gpio1 = 0x00e3e361,
- .gpio2 = 0x00000000,
- .gpio3 = 0x00000000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x00000000,
- .gpio1 = 0x00e3e361,
- .gpio2 = 0x00000000,
- .gpio3 = 0x00000000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x00000000,
- .gpio1 = 0x00e3e341,
- .gpio2 = 0x00000000,
- .gpio3 = 0x00000000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = {
- .name = "DViCO FusionHDTV DVB-T PRO",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000067df,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000067df,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = {
- .name = "DViCO FusionHDTV 7 Gold",
- .tuner_type = TUNER_XC5000,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x10df,
- },{
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x16d9,
- },{
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x16d9,
- }},
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PROLINK_PV_8000GT] = {
- .name = "Prolink Pixelview MPEG 8000GT",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0ff,
- .gpio2 = 0x0cfb,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio2 = 0x0cfb,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio2 = 0x0cfb,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio2 = 0x0cfb,
- },
- },
- [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = {
- .name = "Prolink Pixelview Global Extreme",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x04fb,
- .gpio1 = 0x04080,
- .gpio2 = 0x0cf7,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x04fb,
- .gpio1 = 0x04080,
- .gpio2 = 0x0cfb,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x04fb,
- .gpio1 = 0x04080,
- .gpio2 = 0x0cfb,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x04ff,
- .gpio1 = 0x04080,
- .gpio2 = 0x0cf7,
- },
- },
- /* Both radio, analog and ATSC work with this board.
- However, for analog to work, s5h1409 gate should be open,
- otherwise, tuner-xc3028 won't be detected.
- A proper fix require using the newer i2c methods to add
- tuner-xc3028 without doing an i2c probe.
- */
- [CX88_BOARD_KWORLD_ATSC_120] = {
- .name = "Kworld PlusTV HD PCI 120 (ATSC 120)",
- .tuner_type = TUNER_XC2028,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x000000ff,
- .gpio1 = 0x0000f35d,
- .gpio2 = 0x00000000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x000000ff,
- .gpio1 = 0x0000f37e,
- .gpio2 = 0x00000000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x000000ff,
- .gpio1 = 0x0000f37e,
- .gpio2 = 0x00000000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x000000ff,
- .gpio1 = 0x0000f35d,
- .gpio2 = 0x00000000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_HVR4000] = {
- .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid",
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .tda9887_conf = TDA9887_PRESENT,
- .audio_chip = V4L2_IDENT_WM8775,
- /*
- * GPIO0 (WINTV2000)
- *
- * Analogue SAT DVB-T
- * Antenna 0xc4bf 0xc4bb
- * Composite 0xc4bf 0xc4bb
- * S-Video 0xc4bf 0xc4bb
- * Composite1 0xc4ff 0xc4fb
- * S-Video1 0xc4ff 0xc4fb
- *
- * BIT VALUE FUNCTION GP{x}_IO
- * 0 1 I:?
- * 1 1 I:?
- * 2 1 O:MPEG PORT 0=DVB-T 1=DVB-S
- * 3 1 I:?
- * 4 1 I:?
- * 5 1 I:?
- * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION
- * 7 1 O:DVB-T DEMOD RESET LOW
- *
- * BIT VALUE FUNCTION GP{x}_OE
- * 8 0 I
- * 9 0 I
- * a 1 O
- * b 0 I
- * c 0 I
- * d 0 I
- * e 1 O
- * f 1 O
- *
- * WM8775 ADC
- *
- * 1: TV Audio / FM Mono
- * 2: Line-In
- * 3: Line-In Expansion
- * 4: FM Stereo
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xc4bf,
- /* 1: TV Audio / FM Mono */
- .audioroute = 1,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xc4bf,
- /* 2: Line-In */
- .audioroute = 2,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xc4bf,
- /* 2: Line-In */
- .audioroute = 2,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0xc4bf,
- /* 4: FM Stereo */
- .audioroute = 8,
- },
- .mpeg = CX88_MPEG_DVB,
- .num_frontends = 2,
- },
- [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = {
- .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TEVII_S420] = {
- .name = "TeVii S420 DVB-S",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TEVII_S460] = {
- .name = "TeVii S460 DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TEVII_S464] = {
- .name = "TeVii S464 DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_OMICOM_SS4_PCI] = {
- .name = "Omicom SS4 DVB-S/S2 PCI",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TBS_8910] = {
- .name = "TBS 8910 DVB-S",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TBS_8920] = {
- .name = "TBS 8920 DVB-S/S2",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- .gpio0 = 0x8080,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PROF_6200] = {
- .name = "Prof 6200 DVB-S",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PROF_7300] = {
- .name = "PROF 7300 DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_SATTRADE_ST4200] = {
- .name = "SATTRADE ST4200 DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = {
- .name = "Terratec Cinergy HT PCI MKII",
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .radio_type = UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x004ff,
- .gpio1 = 0x010ff,
- .gpio2 = 0x00001,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x004fb,
- .gpio1 = 0x010ef,
- .audioroute = 1,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x004fb,
- .gpio1 = 0x010ef,
- .audioroute = 1,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x004ff,
- .gpio1 = 0x010ff,
- .gpio2 = 0x0ff,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_HAUPPAUGE_IRONLY] = {
- .name = "Hauppauge WinTV-IR Only",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- },
- [CX88_BOARD_WINFAST_DTV1800H] = {
- .name = "Leadtek WinFast DTV1800 Hybrid",
- .tuner_type = TUNER_XC2028,
- .radio_type = UNSET,
- .tuner_addr = 0x61,
- .radio_addr = ADDR_UNSET,
- /*
- * GPIO setting
- *
- * 2: mute (0=off,1=on)
- * 12: tuner reset pin
- * 13: audio source (0=tuner audio,1=line in)
- * 14: FM (0=on,1=off ???)
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
- .gpio2 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
- .gpio2 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
- .gpio2 = 0x0000,
- } },
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
- .gpio2 = 0x0000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_WINFAST_DTV1800H_XC4000] = {
- .name = "Leadtek WinFast DTV1800 H (XC4000)",
- .tuner_type = TUNER_XC4000,
- .radio_type = UNSET,
- .tuner_addr = 0x61,
- .radio_addr = ADDR_UNSET,
- /*
- * GPIO setting
- *
- * 2: mute (0=off,1=on)
- * 12: tuner reset pin
- * 13: audio source (0=tuner audio,1=line in)
- * 14: FM (0=on,1=off ???)
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */
- .gpio2 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
- .gpio2 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */
- .gpio2 = 0x0000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0400, /* pin 2 = 0 */
- .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */
- .gpio2 = 0x0000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_WINFAST_DTV2000H_PLUS] = {
- .name = "Leadtek WinFast DTV2000 H PLUS",
- .tuner_type = TUNER_XC4000,
- .radio_type = UNSET,
- .tuner_addr = 0x61,
- .radio_addr = ADDR_UNSET,
- /*
- * GPIO
- * 2: 1: mute audio
- * 12: 0: reset XC4000
- * 13: 1: audio input is line in (0: tuner)
- * 14: 0: FM radio
- * 16: 0: RF input is cable
- */
- .input = {{
- .type = CX88_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0x0403,
- .gpio1 = 0xF0D7,
- .gpio2 = 0x0101,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_CABLE,
- .vmux = 0,
- .gpio0 = 0x0403,
- .gpio1 = 0xF0D7,
- .gpio2 = 0x0100,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0x0403, /* was 0x0407 */
- .gpio1 = 0xF0F7,
- .gpio2 = 0x0101,
- .gpio3 = 0x0000,
- }, {
- .type = CX88_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0x0403, /* was 0x0407 */
- .gpio1 = 0xF0F7,
- .gpio2 = 0x0101,
- .gpio3 = 0x0000,
- }},
- .radio = {
- .type = CX88_RADIO,
- .gpio0 = 0x0403,
- .gpio1 = 0xF097,
- .gpio2 = 0x0100,
- .gpio3 = 0x0000,
- },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_PROF_7301] = {
- .name = "Prof 7301 DVB-S/S2",
- .tuner_type = UNSET,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = { {
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
- [CX88_BOARD_TWINHAN_VP1027_DVBS] = {
- .name = "Twinhan VP-1027 DVB-S",
- .tuner_type = TUNER_ABSENT,
- .radio_type = UNSET,
- .tuner_addr = ADDR_UNSET,
- .radio_addr = ADDR_UNSET,
- .input = {{
- .type = CX88_VMUX_DVB,
- .vmux = 0,
- } },
- .mpeg = CX88_MPEG_DVB,
- },
-};
-
-/* ------------------------------------------------------------------ */
-/* PCI subsystem IDs */
-
-static const struct cx88_subid cx88_subids[] = {
- {
- .subvendor = 0x0070,
- .subdevice = 0x3400,
- .card = CX88_BOARD_HAUPPAUGE,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x3401,
- .card = CX88_BOARD_HAUPPAUGE,
- },{
- .subvendor = 0x14c7,
- .subdevice = 0x0106,
- .card = CX88_BOARD_GDI,
- },{
- .subvendor = 0x14c7,
- .subdevice = 0x0107, /* with mpeg encoder */
- .card = CX88_BOARD_GDI,
- },{
- .subvendor = PCI_VENDOR_ID_ATI,
- .subdevice = 0x00f8,
- .card = CX88_BOARD_ATI_WONDER_PRO,
- }, {
- .subvendor = PCI_VENDOR_ID_ATI,
- .subdevice = 0x00f9,
- .card = CX88_BOARD_ATI_WONDER_PRO,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6611,
- .card = CX88_BOARD_WINFAST2000XP_EXPERT,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x6613, /* NTSC */
- .card = CX88_BOARD_WINFAST2000XP_EXPERT,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x6620,
- .card = CX88_BOARD_WINFAST_DV2000,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x663b,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x663c,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- },{
- .subvendor = 0x1461,
- .subdevice = 0x000b,
- .card = CX88_BOARD_AVERTV_STUDIO_303,
- },{
- .subvendor = 0x1462,
- .subdevice = 0x8606,
- .card = CX88_BOARD_MSI_TVANYWHERE_MASTER,
- },{
- .subvendor = 0x10fc,
- .subdevice = 0xd003,
- .card = CX88_BOARD_IODATA_GVVCP3PCI,
- },{
- .subvendor = 0x1043,
- .subdevice = 0x4823, /* with mpeg encoder */
- .card = CX88_BOARD_ASUS_PVR_416,
- },{
- .subvendor = 0x17de,
- .subdevice = 0x08a6,
- .card = CX88_BOARD_KWORLD_DVB_T,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xd810,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xd820,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb00,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9002,
- .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
- },{
- .subvendor = 0x14f1,
- .subdevice = 0x0187,
- .card = CX88_BOARD_CONEXANT_DVB_T1,
- },{
- .subvendor = 0x1540,
- .subdevice = 0x2580,
- .card = CX88_BOARD_PROVIDEO_PV259,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb10,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
- },{
- .subvendor = 0x1554,
- .subdevice = 0x4811,
- .card = CX88_BOARD_PIXELVIEW,
- },{
- .subvendor = 0x7063,
- .subdevice = 0x3000, /* HD-3000 card */
- .card = CX88_BOARD_PCHDTV_HD3000,
- },{
- .subvendor = 0x17de,
- .subdevice = 0xa8a6,
- .card = CX88_BOARD_DNTV_LIVE_DVB_T,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x2801,
- .card = CX88_BOARD_HAUPPAUGE_ROSLYN,
- },{
- .subvendor = 0x14f1,
- .subdevice = 0x0342,
- .card = CX88_BOARD_DIGITALLOGIC_MEC,
- },{
- .subvendor = 0x10fc,
- .subdevice = 0xd035,
- .card = CX88_BOARD_IODATA_GVBCTV7E,
- },{
- .subvendor = 0x1421,
- .subdevice = 0x0334,
- .card = CX88_BOARD_ADSTECH_DVB_T_PCI,
- },{
- .subvendor = 0x153b,
- .subdevice = 0x1166,
- .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xd500,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD,
- },{
- .subvendor = 0x1461,
- .subdevice = 0x8011,
- .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550,
- },{
- .subvendor = PCI_VENDOR_ID_ATI,
- .subdevice = 0xa101,
- .card = CX88_BOARD_ATI_HDTVWONDER,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x665f,
- .card = CX88_BOARD_WINFAST_DTV1000,
- },{
- .subvendor = 0x1461,
- .subdevice = 0x000a,
- .card = CX88_BOARD_AVERTV_303,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9200,
- .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9201,
- .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9202,
- .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1,
- },{
- .subvendor = 0x17de,
- .subdevice = 0x08b2,
- .card = CX88_BOARD_KWORLD_DVBS_100,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9400,
- .card = CX88_BOARD_HAUPPAUGE_HVR1100,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9402,
- .card = CX88_BOARD_HAUPPAUGE_HVR1100,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9800,
- .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9802,
- .card = CX88_BOARD_HAUPPAUGE_HVR1100LP,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9001,
- .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
- },{
- .subvendor = 0x1822,
- .subdevice = 0x0025,
- .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
- },{
- .subvendor = 0x17de,
- .subdevice = 0x08a1,
- .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb50,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb54,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL,
- /* Re-branded DViCO: DigitalNow DVB-T Dual */
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb11,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS,
- /* Re-branded DViCO: UltraView DVB-T Plus */
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xdb30,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO,
- }, {
- .subvendor = 0x17de,
- .subdevice = 0x0840,
- .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
- },{
- .subvendor = 0x1421,
- .subdevice = 0x0305,
- .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb40,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xdb44,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID,
- },{
- .subvendor = 0x7063,
- .subdevice = 0x5500,
- .card = CX88_BOARD_PCHDTV_HD5500,
- },{
- .subvendor = 0x17de,
- .subdevice = 0x0841,
- .card = CX88_BOARD_KWORLD_MCE200_DELUXE,
- },{
- .subvendor = 0x1822,
- .subdevice = 0x0019,
- .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO,
- },{
- .subvendor = 0x1554,
- .subdevice = 0x4813,
- .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000,
- },{
- .subvendor = 0x14f1,
- .subdevice = 0x0842,
- .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x665e,
- .card = CX88_BOARD_WINFAST_DTV2000H,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x6f2b,
- .card = CX88_BOARD_WINFAST_DTV2000H_J,
- },{
- .subvendor = 0x18ac,
- .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
- .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
- },{
- .subvendor = 0x14f1,
- .subdevice = 0x0084,
- .card = CX88_BOARD_GENIATECH_DVBS,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x1404,
- .card = CX88_BOARD_HAUPPAUGE_HVR3000,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xdc00,
- .card = CX88_BOARD_SAMSUNG_SMT_7020,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xdccd,
- .card = CX88_BOARD_SAMSUNG_SMT_7020,
- },{
- .subvendor = 0x1461,
- .subdevice = 0xc111, /* AverMedia M150-D */
- /* This board is known to work with the ASUS PVR416 config */
- .card = CX88_BOARD_ASUS_PVR_416,
- },{
- .subvendor = 0xc180,
- .subdevice = 0xc980,
- .card = CX88_BOARD_TE_DTV_250_OEM_SWANN,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9600,
- .card = CX88_BOARD_HAUPPAUGE_HVR1300,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9601,
- .card = CX88_BOARD_HAUPPAUGE_HVR1300,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9602,
- .card = CX88_BOARD_HAUPPAUGE_HVR1300,
- },{
- .subvendor = 0x107d,
- .subdevice = 0x6632,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- },{
- .subvendor = 0x12ab,
- .subdevice = 0x2300, /* Club3D Zap TV2100 */
- .card = CX88_BOARD_KWORLD_DVB_T_CX22702,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x9000,
- .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x1400,
- .card = CX88_BOARD_HAUPPAUGE_HVR3000,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x1401,
- .card = CX88_BOARD_HAUPPAUGE_HVR3000,
- },{
- .subvendor = 0x0070,
- .subdevice = 0x1402,
- .card = CX88_BOARD_HAUPPAUGE_HVR3000,
- },{
- .subvendor = 0x1421,
- .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */
- .card = CX88_BOARD_KWORLD_DVBS_100,
- },{
- .subvendor = 0x1421,
- .subdevice = 0x0390,
- .card = CX88_BOARD_ADSTECH_PTV_390,
- },{
- .subvendor = 0x11bd,
- .subdevice = 0x0051,
- .card = CX88_BOARD_PINNACLE_PCTV_HD_800i,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xd530,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO,
- }, {
- .subvendor = 0x12ab,
- .subdevice = 0x1788,
- .card = CX88_BOARD_PINNACLE_HYBRID_PCTV,
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0xea3d,
- .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6f18,
- .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
- }, {
- .subvendor = 0x14f1,
- .subdevice = 0x8852,
- .card = CX88_BOARD_GENIATECH_X8000_MT,
- }, {
- .subvendor = 0x18ac,
- .subdevice = 0xd610,
- .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD,
- }, {
- .subvendor = 0x1554,
- .subdevice = 0x4935,
- .card = CX88_BOARD_PROLINK_PV_8000GT,
- }, {
- .subvendor = 0x1554,
- .subdevice = 0x4976,
- .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME,
- }, {
- .subvendor = 0x17de,
- .subdevice = 0x08c1,
- .card = CX88_BOARD_KWORLD_ATSC_120,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x6900,
- .card = CX88_BOARD_HAUPPAUGE_HVR4000,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x6904,
- .card = CX88_BOARD_HAUPPAUGE_HVR4000,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x6902,
- .card = CX88_BOARD_HAUPPAUGE_HVR4000,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x6905,
- .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x6906,
- .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE,
- }, {
- .subvendor = 0xd420,
- .subdevice = 0x9022,
- .card = CX88_BOARD_TEVII_S420,
- }, {
- .subvendor = 0xd460,
- .subdevice = 0x9022,
- .card = CX88_BOARD_TEVII_S460,
- }, {
- .subvendor = 0xd464,
- .subdevice = 0x9022,
- .card = CX88_BOARD_TEVII_S464,
- }, {
- .subvendor = 0xA044,
- .subdevice = 0x2011,
- .card = CX88_BOARD_OMICOM_SS4_PCI,
- }, {
- .subvendor = 0x8910,
- .subdevice = 0x8888,
- .card = CX88_BOARD_TBS_8910,
- }, {
- .subvendor = 0x8920,
- .subdevice = 0x8888,
- .card = CX88_BOARD_TBS_8920,
- }, {
- .subvendor = 0xb022,
- .subdevice = 0x3022,
- .card = CX88_BOARD_PROF_6200,
- }, {
- .subvendor = 0xB033,
- .subdevice = 0x3033,
- .card = CX88_BOARD_PROF_7300,
- }, {
- .subvendor = 0xb200,
- .subdevice = 0x4200,
- .card = CX88_BOARD_SATTRADE_ST4200,
- }, {
- .subvendor = 0x153b,
- .subdevice = 0x1177,
- .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII,
- }, {
- .subvendor = 0x0070,
- .subdevice = 0x9290,
- .card = CX88_BOARD_HAUPPAUGE_IRONLY,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6654,
- .card = CX88_BOARD_WINFAST_DTV1800H,
- }, {
- /* WinFast DTV1800 H with XC4000 tuner */
- .subvendor = 0x107d,
- .subdevice = 0x6f38,
- .card = CX88_BOARD_WINFAST_DTV1800H_XC4000,
- }, {
- .subvendor = 0x107d,
- .subdevice = 0x6f42,
- .card = CX88_BOARD_WINFAST_DTV2000H_PLUS,
- }, {
- /* PVR2000 PAL Model [107d:6630] */
- .subvendor = 0x107d,
- .subdevice = 0x6630,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- }, {
- /* PVR2000 PAL Model [107d:6638] */
- .subvendor = 0x107d,
- .subdevice = 0x6638,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- }, {
- /* PVR2000 NTSC Model [107d:6631] */
- .subvendor = 0x107d,
- .subdevice = 0x6631,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- }, {
- /* PVR2000 NTSC Model [107d:6637] */
- .subvendor = 0x107d,
- .subdevice = 0x6637,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- }, {
- /* PVR2000 NTSC Model [107d:663d] */
- .subvendor = 0x107d,
- .subdevice = 0x663d,
- .card = CX88_BOARD_LEADTEK_PVR2000,
- }, {
- /* DV2000 NTSC Model [107d:6621] */
- .subvendor = 0x107d,
- .subdevice = 0x6621,
- .card = CX88_BOARD_WINFAST_DV2000,
- }, {
- /* TV2000 XP Global [107d:6618] */
- .subvendor = 0x107d,
- .subdevice = 0x6618,
- .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
- }, {
- /* TV2000 XP Global [107d:6618] */
- .subvendor = 0x107d,
- .subdevice = 0x6619,
- .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL,
- }, {
- /* WinFast TV2000 XP Global with XC4000 tuner */
- .subvendor = 0x107d,
- .subdevice = 0x6f36,
- .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36,
- }, {
- /* WinFast TV2000 XP Global with XC4000 tuner and different GPIOs */
- .subvendor = 0x107d,
- .subdevice = 0x6f43,
- .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43,
- }, {
- .subvendor = 0xb034,
- .subdevice = 0x3034,
- .card = CX88_BOARD_PROF_7301,
- }, {
- .subvendor = 0x1822,
- .subdevice = 0x0023,
- .card = CX88_BOARD_TWINHAN_VP1027_DVBS,
- },
-};
-
-/* ----------------------------------------------------------------------- */
-/* some leadtek specific stuff */
-
-static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data)
-{
- if (eeprom_data[4] != 0x7d ||
- eeprom_data[5] != 0x10 ||
- eeprom_data[7] != 0x66) {
- warn_printk(core, "Leadtek eeprom invalid.\n");
- return;
- }
-
- /* Terry Wu <terrywu2009@gmail.com> */
- switch (eeprom_data[6]) {
- case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */
- case 0x21: /* SSID 6621 for DV2000 NTSC Model */
- case 0x31: /* SSID 6631 for PVR2000 NTSC Model */
- case 0x37: /* SSID 6637 for PVR2000 NTSC Model */
- case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */
- core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3;
- break;
- default:
- core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3;
- break;
- }
-
- info_printk(core, "Leadtek Winfast 2000XP Expert config: "
- "tuner=%d, eeprom[0]=0x%02x\n",
- core->board.tuner_type, eeprom_data[0]);
-}
-
-static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
-{
- struct tveeprom tv;
-
- tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data);
- core->board.tuner_type = tv.tuner_type;
- core->tuner_formats = tv.tuner_formats;
- core->board.radio.type = tv.has_radio ? CX88_RADIO : 0;
-
- /* Make sure we support the board model */
- switch (tv.model)
- {
- case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */
- case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */
- case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */
- case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */
- case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */
- case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */
- case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */
- case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */
- case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */
- case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
- case 34519: /* WinTV-PCI-FM */
- case 69009:
- /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */
- case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */
- case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */
- case 69559:
- /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */
- case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */
- case 90002: /* Nova-T-PCI (9002) */
- case 92001: /* Nova-S-Plus (Video and IR) */
- case 92002: /* Nova-S-Plus (Video and IR) */
- case 90003: /* Nova-T-PCI (9002 No RF out) */
- case 90500: /* Nova-T-PCI (oem) */
- case 90501: /* Nova-T-PCI (oem/IR) */
- case 92000: /* Nova-SE2 (OEM, No Video or IR) */
- case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */
- case 94009: /* WinTV-HVR1100 (Video and IR Retail) */
- case 94501: /* WinTV-HVR1100 (Video and IR OEM) */
- case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */
- case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */
- case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */
- case 96569: /* WinTV-HVR1300 () */
- case 96659: /* WinTV-HVR1300 () */
- case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
- /* known */
- break;
- case CX88_BOARD_SAMSUNG_SMT_7020:
- cx_set(MO_GP0_IO, 0x008989FF);
- break;
- default:
- warn_printk(core, "warning: unknown hauppauge model #%d\n",
- tv.model);
- break;
- }
-
- info_printk(core, "hauppauge eeprom: model=%d\n", tv.model);
-}
-
-/* ----------------------------------------------------------------------- */
-/* some GDI (was: Modular Technology) specific stuff */
-
-static const struct {
- int id;
- int fm;
- const char *name;
-} gdi_tuner[] = {
- [ 0x01 ] = { .id = TUNER_ABSENT,
- .name = "NTSC_M" },
- [ 0x02 ] = { .id = TUNER_ABSENT,
- .name = "PAL_B" },
- [ 0x03 ] = { .id = TUNER_ABSENT,
- .name = "PAL_I" },
- [ 0x04 ] = { .id = TUNER_ABSENT,
- .name = "PAL_D" },
- [ 0x05 ] = { .id = TUNER_ABSENT,
- .name = "SECAM" },
-
- [ 0x10 ] = { .id = TUNER_ABSENT,
- .fm = 1,
- .name = "TEMIC_4049" },
- [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5,
- .name = "TEMIC_4136" },
- [ 0x12 ] = { .id = TUNER_ABSENT,
- .name = "TEMIC_4146" },
-
- [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME,
- .fm = 1,
- .name = "PHILIPS_FQ1216_MK3" },
- [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1,
- .name = "PHILIPS_FQ1236_MK3" },
- [ 0x22 ] = { .id = TUNER_ABSENT,
- .name = "PHILIPS_FI1236_MK3" },
- [ 0x23 ] = { .id = TUNER_ABSENT,
- .name = "PHILIPS_FI1216_MK3" },
-};
-
-static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data)
-{
- const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner))
- ? gdi_tuner[eeprom_data[0x0d]].name : NULL;
-
- info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown");
- if (NULL == name)
- return;
- core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id;
- core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ?
- CX88_RADIO : 0;
-}
-
-/* ------------------------------------------------------------------- */
-/* some Divco specific stuff */
-static int cx88_dvico_xc2028_callback(struct cx88_core *core,
- int command, int arg)
-{
- switch (command) {
- case XC2028_TUNER_RESET:
- switch (core->boardnr) {
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- /* GPIO-4 xc3028 tuner */
-
- cx_set(MO_GP0_IO, 0x00001000);
- cx_clear(MO_GP0_IO, 0x00000010);
- msleep(100);
- cx_set(MO_GP0_IO, 0x00000010);
- msleep(100);
- break;
- default:
- cx_write(MO_GP0_IO, 0x101000);
- mdelay(5);
- cx_set(MO_GP0_IO, 0x101010);
- }
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-
-/* ----------------------------------------------------------------------- */
-/* some Geniatech specific stuff */
-
-static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core,
- int command, int mode)
-{
- switch (command) {
- case XC2028_TUNER_RESET:
- switch (INPUT(core->input).type) {
- case CX88_RADIO:
- break;
- case CX88_VMUX_DVB:
- cx_write(MO_GP1_IO, 0x030302);
- mdelay(50);
- break;
- default:
- cx_write(MO_GP1_IO, 0x030301);
- mdelay(50);
- }
- cx_write(MO_GP1_IO, 0x101010);
- mdelay(50);
- cx_write(MO_GP1_IO, 0x101000);
- mdelay(50);
- cx_write(MO_GP1_IO, 0x101010);
- mdelay(50);
- return 0;
- }
- return -EINVAL;
-}
-
-static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core,
- int command, int arg)
-{
- switch (command) {
- case XC2028_TUNER_RESET:
- /* GPIO 12 (xc3028 tuner reset) */
- cx_set(MO_GP1_IO, 0x1010);
- mdelay(50);
- cx_clear(MO_GP1_IO, 0x10);
- mdelay(50);
- cx_set(MO_GP1_IO, 0x10);
- mdelay(50);
- return 0;
- }
- return -EINVAL;
-}
-
-static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core,
- int command, int arg)
-{
- switch (command) {
- case XC4000_TUNER_RESET:
- /* GPIO 12 (xc4000 tuner reset) */
- cx_set(MO_GP1_IO, 0x1010);
- mdelay(50);
- cx_clear(MO_GP1_IO, 0x10);
- mdelay(75);
- cx_set(MO_GP1_IO, 0x10);
- mdelay(75);
- return 0;
- }
- return -EINVAL;
-}
-
-/* ------------------------------------------------------------------- */
-/* some Divco specific stuff */
-static int cx88_pv_8000gt_callback(struct cx88_core *core,
- int command, int arg)
-{
- switch (command) {
- case XC2028_TUNER_RESET:
- cx_write(MO_GP2_IO, 0xcf7);
- mdelay(50);
- cx_write(MO_GP2_IO, 0xef5);
- mdelay(50);
- cx_write(MO_GP2_IO, 0xcf7);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-/* some DViCO specific stuff */
-
-static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core)
-{
- struct i2c_msg msg = { .addr = 0x45, .flags = 0 };
- int i, err;
- static u8 init_bufs[13][5] = {
- { 0x10, 0x00, 0x20, 0x01, 0x03 },
- { 0x10, 0x10, 0x01, 0x00, 0x21 },
- { 0x10, 0x10, 0x10, 0x00, 0xCA },
- { 0x10, 0x10, 0x12, 0x00, 0x08 },
- { 0x10, 0x10, 0x13, 0x00, 0x0A },
- { 0x10, 0x10, 0x16, 0x01, 0xC0 },
- { 0x10, 0x10, 0x22, 0x01, 0x3D },
- { 0x10, 0x10, 0x73, 0x01, 0x2E },
- { 0x10, 0x10, 0x72, 0x00, 0xC5 },
- { 0x10, 0x10, 0x71, 0x01, 0x97 },
- { 0x10, 0x10, 0x70, 0x00, 0x0F },
- { 0x10, 0x10, 0xB0, 0x00, 0x01 },
- { 0x03, 0x0C },
- };
-
- for (i = 0; i < ARRAY_SIZE(init_bufs); i++) {
- msg.buf = init_bufs[i];
- msg.len = (i != 12 ? 5 : 2);
- err = i2c_transfer(&core->i2c_adap, &msg, 1);
- if (err != 1) {
- warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d "
- "failed (err = %d)!\n", i, err);
- return;
- }
- }
-}
-
-static int cx88_xc2028_tuner_callback(struct cx88_core *core,
- int command, int arg)
-{
- /* Board-specific callbacks */
- switch (core->boardnr) {
- case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
- case CX88_BOARD_GENIATECH_X8000_MT:
- case CX88_BOARD_KWORLD_ATSC_120:
- return cx88_xc3028_geniatech_tuner_callback(core,
- command, arg);
- case CX88_BOARD_PROLINK_PV_8000GT:
- case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
- return cx88_pv_8000gt_callback(core, command, arg);
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- return cx88_dvico_xc2028_callback(core, command, arg);
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
- case CX88_BOARD_WINFAST_DTV1800H:
- return cx88_xc3028_winfast1800h_callback(core, command, arg);
- }
-
- switch (command) {
- case XC2028_TUNER_RESET:
- switch (INPUT(core->input).type) {
- case CX88_RADIO:
- info_printk(core, "setting GPIO to radio!\n");
- cx_write(MO_GP0_IO, 0x4ff);
- mdelay(250);
- cx_write(MO_GP2_IO, 0xff);
- mdelay(250);
- break;
- case CX88_VMUX_DVB: /* Digital TV*/
- default: /* Analog TV */
- info_printk(core, "setting GPIO to TV!\n");
- break;
- }
- cx_write(MO_GP1_IO, 0x101010);
- mdelay(250);
- cx_write(MO_GP1_IO, 0x101000);
- mdelay(250);
- cx_write(MO_GP1_IO, 0x101010);
- mdelay(250);
- return 0;
- }
- return -EINVAL;
-}
-
-static int cx88_xc4000_tuner_callback(struct cx88_core *core,
- int command, int arg)
-{
- /* Board-specific callbacks */
- switch (core->boardnr) {
- case CX88_BOARD_WINFAST_DTV1800H_XC4000:
- case CX88_BOARD_WINFAST_DTV2000H_PLUS:
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
- return cx88_xc4000_winfast2000h_plus_callback(core,
- command, arg);
- }
- return -EINVAL;
-}
-
-/* ----------------------------------------------------------------------- */
-/* Tuner callback function. Currently only needed for the Pinnacle *
- * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both *
- * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */
-
-static int cx88_xc5000_tuner_callback(struct cx88_core *core,
- int command, int arg)
-{
- switch (core->boardnr) {
- case CX88_BOARD_PINNACLE_PCTV_HD_800i:
- if (command == 0) { /* This is the reset command from xc5000 */
-
- /* djh - According to the engineer at PCTV Systems,
- the xc5000 reset pin is supposed to be on GPIO12.
- However, despite three nights of effort, pulling
- that GPIO low didn't reset the xc5000. While
- pulling MO_SRST_IO low does reset the xc5000, this
- also resets in the s5h1409 being reset as well.
- This causes tuning to always fail since the internal
- state of the s5h1409 does not match the driver's
- state. Given that the only two conditions in which
- the driver performs a reset is during firmware load
- and powering down the chip, I am taking out the
- reset. We know that the chip is being reset
- when the cx88 comes online, and not being able to
- do power management for this board is worse than
- not having any tuning at all. */
- return 0;
- } else {
- err_printk(core, "xc5000: unknown tuner "
- "callback command.\n");
- return -EINVAL;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
- if (command == 0) { /* This is the reset command from xc5000 */
- cx_clear(MO_GP0_IO, 0x00000010);
- msleep(10);
- cx_set(MO_GP0_IO, 0x00000010);
- return 0;
- } else {
- printk(KERN_ERR
- "xc5000: unknown tuner callback command.\n");
- return -EINVAL;
- }
- break;
- }
- return 0; /* Should never be here */
-}
-
-int cx88_tuner_callback(void *priv, int component, int command, int arg)
-{
- struct i2c_algo_bit_data *i2c_algo = priv;
- struct cx88_core *core;
-
- if (!i2c_algo) {
- printk(KERN_ERR "cx88: Error - i2c private data undefined.\n");
- return -EINVAL;
- }
-
- core = i2c_algo->data;
-
- if (!core) {
- printk(KERN_ERR "cx88: Error - device struct undefined.\n");
- return -EINVAL;
- }
-
- if (component != DVB_FRONTEND_COMPONENT_TUNER)
- return -EINVAL;
-
- switch (core->board.tuner_type) {
- case TUNER_XC2028:
- info_printk(core, "Calling XC2028/3028 callback\n");
- return cx88_xc2028_tuner_callback(core, command, arg);
- case TUNER_XC4000:
- info_printk(core, "Calling XC4000 callback\n");
- return cx88_xc4000_tuner_callback(core, command, arg);
- case TUNER_XC5000:
- info_printk(core, "Calling XC5000 callback\n");
- return cx88_xc5000_tuner_callback(core, command, arg);
- }
- err_printk(core, "Error: Calling callback for tuner %d\n",
- core->board.tuner_type);
- return -EINVAL;
-}
-EXPORT_SYMBOL(cx88_tuner_callback);
-
-/* ----------------------------------------------------------------------- */
-
-static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
-{
- int i;
-
- if (0 == pci->subsystem_vendor &&
- 0 == pci->subsystem_device) {
- printk(KERN_ERR
- "%s: Your board has no valid PCI Subsystem ID and thus can't\n"
- "%s: be autodetected. Please pass card=<n> insmod option to\n"
- "%s: workaround that. Redirect complaints to the vendor of\n"
- "%s: the TV card. Best regards,\n"
- "%s: -- tux\n",
- core->name,core->name,core->name,core->name,core->name);
- } else {
- printk(KERN_ERR
- "%s: Your board isn't known (yet) to the driver. You can\n"
- "%s: try to pick one of the existing card configs via\n"
- "%s: card=<n> insmod option. Updating to the latest\n"
- "%s: version might help as well.\n",
- core->name,core->name,core->name,core->name);
- }
- err_printk(core, "Here is a list of valid choices for the card=<n> "
- "insmod option:\n");
- for (i = 0; i < ARRAY_SIZE(cx88_boards); i++)
- printk(KERN_ERR "%s: card=%d -> %s\n",
- core->name, i, cx88_boards[i].name);
-}
-
-static void cx88_card_setup_pre_i2c(struct cx88_core *core)
-{
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- /*
- * Bring the 702 demod up before i2c scanning/attach or devices are hidden
- * We leave here with the 702 on the bus
- *
- * "reset the IR receiver on GPIO[3]"
- * Reported by Mike Crash <mike AT mikecrash.com>
- */
- cx_write(MO_GP0_IO, 0x0000ef88);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000088);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */
- udelay(1000);
- break;
-
- case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
- case CX88_BOARD_PROLINK_PV_8000GT:
- cx_write(MO_GP2_IO, 0xcf7);
- mdelay(50);
- cx_write(MO_GP2_IO, 0xef5);
- mdelay(50);
- cx_write(MO_GP2_IO, 0xcf7);
- msleep(10);
- break;
-
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
- /* Enable the xc5000 tuner */
- cx_set(MO_GP0_IO, 0x00001010);
- break;
-
- case CX88_BOARD_WINFAST_DTV2000H_J:
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- case CX88_BOARD_HAUPPAUGE_HVR4000:
- /* Init GPIO */
- cx_write(MO_GP0_IO, core->board.input[0].gpio0);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
- udelay(1000);
- break;
-
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
- case CX88_BOARD_WINFAST_DTV1800H:
- cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0);
- break;
-
- case CX88_BOARD_WINFAST_DTV1800H_XC4000:
- case CX88_BOARD_WINFAST_DTV2000H_PLUS:
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36:
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43:
- cx88_xc4000_winfast2000h_plus_callback(core,
- XC4000_TUNER_RESET, 0);
- break;
-
- case CX88_BOARD_TWINHAN_VP1027_DVBS:
- cx_write(MO_GP0_IO, 0x00003230);
- cx_write(MO_GP0_IO, 0x00003210);
- msleep(1);
- cx_write(MO_GP0_IO, 0x00001230);
- break;
- }
-}
-
-/*
- * Sets board-dependent xc3028 configuration
- */
-void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
-{
- memset(ctl, 0, sizeof(*ctl));
-
- ctl->fname = XC2028_DEFAULT_FIRMWARE;
- ctl->max_len = 64;
-
- switch (core->boardnr) {
- case CX88_BOARD_POWERCOLOR_REAL_ANGEL:
- /* Now works with firmware version 2.7 */
- if (core->i2c_algo.udelay < 16)
- core->i2c_algo.udelay = 16;
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
- case CX88_BOARD_WINFAST_DTV1800H:
- ctl->demod = XC3028_FE_ZARLINK456;
- break;
- case CX88_BOARD_KWORLD_ATSC_120:
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- ctl->demod = XC3028_FE_OREN538;
- break;
- case CX88_BOARD_GENIATECH_X8000_MT:
- /* FIXME: For this board, the xc3028 never recovers after being
- powered down (the reset GPIO probably is not set properly).
- We don't have access to the hardware so we cannot determine
- which GPIO is used for xc3028, so just disable power xc3028
- power management for now */
- ctl->disable_power_mgmt = 1;
- break;
- case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL:
- case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME:
- case CX88_BOARD_PROLINK_PV_8000GT:
- /*
- * Those boards uses non-MTS firmware
- */
- break;
- case CX88_BOARD_PINNACLE_HYBRID_PCTV:
- case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
- ctl->demod = XC3028_FE_ZARLINK456;
- ctl->mts = 1;
- break;
- default:
- ctl->demod = XC3028_FE_OREN538;
- ctl->mts = 1;
- }
-}
-EXPORT_SYMBOL_GPL(cx88_setup_xc3028);
-
-static void cx88_card_setup(struct cx88_core *core)
-{
- static u8 eeprom[256];
- struct tuner_setup tun_setup;
- unsigned int mode_mask = T_RADIO | T_ANALOG_TV;
-
- memset(&tun_setup, 0, sizeof(tun_setup));
-
- if (0 == core->i2c_rc) {
- core->i2c_client.addr = 0xa0 >> 1;
- tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom));
- }
-
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE:
- case CX88_BOARD_HAUPPAUGE_ROSLYN:
- if (0 == core->i2c_rc)
- hauppauge_eeprom(core, eeprom+8);
- break;
- case CX88_BOARD_GDI:
- if (0 == core->i2c_rc)
- gdi_eeprom(core, eeprom);
- break;
- case CX88_BOARD_LEADTEK_PVR2000:
- case CX88_BOARD_WINFAST_DV2000:
- case CX88_BOARD_WINFAST2000XP_EXPERT:
- if (0 == core->i2c_rc)
- leadtek_eeprom(core, eeprom);
- break;
- case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
- case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
- case CX88_BOARD_HAUPPAUGE_DVB_T1:
- case CX88_BOARD_HAUPPAUGE_HVR1100:
- case CX88_BOARD_HAUPPAUGE_HVR1100LP:
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- case CX88_BOARD_HAUPPAUGE_HVR4000:
- case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
- case CX88_BOARD_HAUPPAUGE_IRONLY:
- if (0 == core->i2c_rc)
- hauppauge_eeprom(core, eeprom);
- break;
- case CX88_BOARD_KWORLD_DVBS_100:
- cx_write(MO_GP0_IO, 0x000007f8);
- cx_write(MO_GP1_IO, 0x00000001);
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
- /* GPIO0:0 is hooked to demod reset */
- /* GPIO0:4 is hooked to xc3028 reset */
- cx_write(MO_GP0_IO, 0x00111100);
- msleep(1);
- cx_write(MO_GP0_IO, 0x00111111);
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
- /* GPIO0:6 is hooked to FX2 reset pin */
- cx_set(MO_GP0_IO, 0x00004040);
- cx_clear(MO_GP0_IO, 0x00000040);
- msleep(1000);
- cx_set(MO_GP0_IO, 0x00004040);
- /* FALLTHROUGH */
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
- /* GPIO0:0 is hooked to mt352 reset pin */
- cx_set(MO_GP0_IO, 0x00000101);
- cx_clear(MO_GP0_IO, 0x00000001);
- msleep(1);
- cx_set(MO_GP0_IO, 0x00000101);
- if (0 == core->i2c_rc &&
- core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID)
- dvico_fusionhdtv_hybrid_init(core);
- break;
- case CX88_BOARD_KWORLD_DVB_T:
- case CX88_BOARD_DNTV_LIVE_DVB_T:
- cx_set(MO_GP0_IO, 0x00000707);
- cx_set(MO_GP2_IO, 0x00000101);
- cx_clear(MO_GP2_IO, 0x00000001);
- msleep(1);
- cx_clear(MO_GP0_IO, 0x00000007);
- cx_set(MO_GP2_IO, 0x00000101);
- break;
- case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
- cx_write(MO_GP0_IO, 0x00080808);
- break;
- case CX88_BOARD_ATI_HDTVWONDER:
- if (0 == core->i2c_rc) {
- /* enable tuner */
- int i;
- static const u8 buffer [][2] = {
- {0x10,0x12},
- {0x13,0x04},
- {0x16,0x00},
- {0x14,0x04},
- {0x17,0x00}
- };
- core->i2c_client.addr = 0x0a;
-
- for (i = 0; i < ARRAY_SIZE(buffer); i++)
- if (2 != i2c_master_send(&core->i2c_client,
- buffer[i],2))
- warn_printk(core, "Unable to enable "
- "tuner(%i).\n", i);
- }
- break;
- case CX88_BOARD_MSI_TVANYWHERE_MASTER:
- {
- struct v4l2_priv_tun_config tea5767_cfg;
- struct tea5767_ctrl ctl;
-
- memset(&ctl, 0, sizeof(ctl));
-
- ctl.high_cut = 1;
- ctl.st_noise = 1;
- ctl.deemph_75 = 1;
- ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
-
- tea5767_cfg.tuner = TUNER_TEA5767;
- tea5767_cfg.priv = &ctl;
-
- call_all(core, tuner, s_config, &tea5767_cfg);
- break;
- }
- case CX88_BOARD_TEVII_S420:
- case CX88_BOARD_TEVII_S460:
- case CX88_BOARD_TEVII_S464:
- case CX88_BOARD_OMICOM_SS4_PCI:
- case CX88_BOARD_TBS_8910:
- case CX88_BOARD_TBS_8920:
- case CX88_BOARD_PROF_6200:
- case CX88_BOARD_PROF_7300:
- case CX88_BOARD_PROF_7301:
- case CX88_BOARD_SATTRADE_ST4200:
- cx_write(MO_GP0_IO, 0x8000);
- msleep(100);
- cx_write(MO_SRST_IO, 0);
- msleep(10);
- cx_write(MO_GP0_IO, 0x8080);
- msleep(100);
- cx_write(MO_SRST_IO, 1);
- msleep(100);
- break;
- } /*end switch() */
-
-
- /* Setup tuners */
- if ((core->board.radio_type != UNSET)) {
- tun_setup.mode_mask = T_RADIO;
- tun_setup.type = core->board.radio_type;
- tun_setup.addr = core->board.radio_addr;
- tun_setup.tuner_callback = cx88_tuner_callback;
- call_all(core, tuner, s_type_addr, &tun_setup);
- mode_mask &= ~T_RADIO;
- }
-
- if (core->board.tuner_type != TUNER_ABSENT) {
- tun_setup.mode_mask = mode_mask;
- tun_setup.type = core->board.tuner_type;
- tun_setup.addr = core->board.tuner_addr;
- tun_setup.tuner_callback = cx88_tuner_callback;
-
- call_all(core, tuner, s_type_addr, &tun_setup);
- }
-
- if (core->board.tda9887_conf) {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &core->board.tda9887_conf;
-
- call_all(core, tuner, s_config, &tda9887_cfg);
- }
-
- if (core->board.tuner_type == TUNER_XC2028) {
- struct v4l2_priv_tun_config xc2028_cfg;
- struct xc2028_ctrl ctl;
-
- /* Fills device-dependent initialization parameters */
- cx88_setup_xc3028(core, &ctl);
-
- /* Sends parameters to xc2028/3028 tuner */
- memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
- xc2028_cfg.tuner = TUNER_XC2028;
- xc2028_cfg.priv = &ctl;
- info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
- ctl.fname);
- call_all(core, tuner, s_config, &xc2028_cfg);
- }
- call_all(core, core, s_power, 0);
-}
-
-/* ------------------------------------------------------------------ */
-
-static int cx88_pci_quirks(const char *name, struct pci_dev *pci)
-{
- unsigned int lat = UNSET;
- u8 ctrl = 0;
- u8 value;
-
- /* check pci quirks */
- if (pci_pci_problems & PCIPCI_TRITON) {
- printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n",
- name);
- ctrl |= CX88X_EN_TBFX;
- }
- if (pci_pci_problems & PCIPCI_NATOMA) {
- printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n",
- name);
- ctrl |= CX88X_EN_TBFX;
- }
- if (pci_pci_problems & PCIPCI_VIAETBF) {
- printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n",
- name);
- ctrl |= CX88X_EN_TBFX;
- }
- if (pci_pci_problems & PCIPCI_VSFX) {
- printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n",
- name);
- ctrl |= CX88X_EN_VSFX;
- }
-#ifdef PCIPCI_ALIMAGIK
- if (pci_pci_problems & PCIPCI_ALIMAGIK) {
- printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
- name);
- lat = 0x0A;
- }
-#endif
-
- /* check insmod options */
- if (UNSET != latency)
- lat = latency;
-
- /* apply stuff */
- if (ctrl) {
- pci_read_config_byte(pci, CX88X_DEVCTRL, &value);
- value |= ctrl;
- pci_write_config_byte(pci, CX88X_DEVCTRL, value);
- }
- if (UNSET != lat) {
- printk(KERN_INFO "%s: setting pci latency timer to %d\n",
- name, latency);
- pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency);
- }
- return 0;
-}
-
-int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci)
-{
- if (request_mem_region(pci_resource_start(pci,0),
- pci_resource_len(pci,0),
- core->name))
- return 0;
- printk(KERN_ERR
- "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n",
- core->name, PCI_FUNC(pci->devfn),
- (unsigned long long)pci_resource_start(pci, 0),
- pci->subsystem_vendor, pci->subsystem_device);
- return -EBUSY;
-}
-
-/* Allocate and initialize the cx88 core struct. One should hold the
- * devlist mutex before calling this. */
-struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
-{
- struct cx88_core *core;
- int i;
-
- core = kzalloc(sizeof(*core), GFP_KERNEL);
- if (core == NULL)
- return NULL;
-
- atomic_inc(&core->refcount);
- core->pci_bus = pci->bus->number;
- core->pci_slot = PCI_SLOT(pci->devfn);
- core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT |
- PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT |
- PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT;
- mutex_init(&core->lock);
-
- core->nr = nr;
- sprintf(core->name, "cx88[%d]", core->nr);
-
- strcpy(core->v4l2_dev.name, core->name);
- if (v4l2_device_register(NULL, &core->v4l2_dev)) {
- kfree(core);
- return NULL;
- }
-
- if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) {
- v4l2_device_unregister(&core->v4l2_dev);
- kfree(core);
- return NULL;
- }
-
- if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) {
- v4l2_ctrl_handler_free(&core->video_hdl);
- v4l2_device_unregister(&core->v4l2_dev);
- kfree(core);
- return NULL;
- }
-
- if (0 != cx88_get_resources(core, pci)) {
- v4l2_ctrl_handler_free(&core->video_hdl);
- v4l2_ctrl_handler_free(&core->audio_hdl);
- v4l2_device_unregister(&core->v4l2_dev);
- kfree(core);
- return NULL;
- }
-
- /* PCI stuff */
- cx88_pci_quirks(core->name, pci);
- core->lmmio = ioremap(pci_resource_start(pci, 0),
- pci_resource_len(pci, 0));
- core->bmmio = (u8 __iomem *)core->lmmio;
-
- if (core->lmmio == NULL) {
- release_mem_region(pci_resource_start(pci, 0),
- pci_resource_len(pci, 0));
- v4l2_ctrl_handler_free(&core->video_hdl);
- v4l2_ctrl_handler_free(&core->audio_hdl);
- v4l2_device_unregister(&core->v4l2_dev);
- kfree(core);
- return NULL;
- }
-
- /* board config */
- core->boardnr = UNSET;
- if (card[core->nr] < ARRAY_SIZE(cx88_boards))
- core->boardnr = card[core->nr];
- for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++)
- if (pci->subsystem_vendor == cx88_subids[i].subvendor &&
- pci->subsystem_device == cx88_subids[i].subdevice)
- core->boardnr = cx88_subids[i].card;
- if (UNSET == core->boardnr) {
- core->boardnr = CX88_BOARD_UNKNOWN;
- cx88_card_list(core, pci);
- }
-
- memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board));
-
- if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB))
- core->board.num_frontends = 1;
-
- info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n",
- pci->subsystem_vendor, pci->subsystem_device, core->board.name,
- core->boardnr, card[core->nr] == core->boardnr ?
- "insmod option" : "autodetected",
- core->board.num_frontends);
-
- if (tuner[core->nr] != UNSET)
- core->board.tuner_type = tuner[core->nr];
- if (radio[core->nr] != UNSET)
- core->board.radio_type = radio[core->nr];
-
- info_printk(core, "TV tuner type %d, Radio tuner type %d\n",
- core->board.tuner_type, core->board.radio_type);
-
- /* init hardware */
- cx88_reset(core);
- cx88_card_setup_pre_i2c(core);
- cx88_i2c_init(core, pci);
-
- /* load tuner module, if needed */
- if (TUNER_ABSENT != core->board.tuner_type) {
- /* Ignore 0x6b and 0x6f on cx88 boards.
- * FusionHDTV5 RT Gold has an ir receiver at 0x6b
- * and an RTC at 0x6f which can get corrupted if probed. */
- static const unsigned short tv_addrs[] = {
- 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
- 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e,
- I2C_CLIENT_END
- };
- int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT);
-
- /* I don't trust the radio_type as is stored in the card
- definitions, so we just probe for it.
- The radio_type is sometimes missing, or set to UNSET but
- later code configures a tea5767.
- */
- v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
- "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO));
- if (has_demod)
- v4l2_i2c_new_subdev(&core->v4l2_dev,
- &core->i2c_adap, "tuner",
- 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
- if (core->board.tuner_addr == ADDR_UNSET) {
- v4l2_i2c_new_subdev(&core->v4l2_dev,
- &core->i2c_adap, "tuner",
- 0, has_demod ? tv_addrs + 4 : tv_addrs);
- } else {
- v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
- "tuner", core->board.tuner_addr, NULL);
- }
- }
-
- cx88_card_setup(core);
- if (!disable_ir) {
- cx88_i2c_init_ir(core);
- cx88_ir_init(core, pci);
- }
-
- return core;
-}
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
deleted file mode 100644
index e81c735f012a..000000000000
--- a/drivers/media/video/cx88/cx88-core.c
+++ /dev/null
@@ -1,1131 +0,0 @@
-/*
- *
- * device driver for Conexant 2388x based TV cards
- * driver core
- *
- * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
- * - Multituner support
- * - video_ioctl2 conversion
- * - PAL/M fixes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/kmod.h>
-#include <linux/sound.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-
-#include "cx88.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int core_debug;
-module_param(core_debug,int,0644);
-MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
-
-static unsigned int nicam;
-module_param(nicam,int,0644);
-MODULE_PARM_DESC(nicam,"tv audio is nicam");
-
-static unsigned int nocomb;
-module_param(nocomb,int,0644);
-MODULE_PARM_DESC(nocomb,"disable comb filter");
-
-#define dprintk(level,fmt, arg...) if (core_debug >= level) \
- printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
-
-static unsigned int cx88_devcount;
-static LIST_HEAD(cx88_devlist);
-static DEFINE_MUTEX(devlist);
-
-#define NO_SYNC_LINE (-1U)
-
-/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
- generated _after_ lpi lines are transferred. */
-static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
- unsigned int offset, u32 sync_line,
- unsigned int bpl, unsigned int padding,
- unsigned int lines, unsigned int lpi)
-{
- struct scatterlist *sg;
- unsigned int line,todo,sol;
-
- /* sync instruction */
- if (sync_line != NO_SYNC_LINE)
- *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
-
- /* scan lines */
- sg = sglist;
- for (line = 0; line < lines; line++) {
- while (offset && offset >= sg_dma_len(sg)) {
- offset -= sg_dma_len(sg);
- sg++;
- }
- if (lpi && line>0 && !(line % lpi))
- sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
- else
- sol = RISC_SOL;
- if (bpl <= sg_dma_len(sg)-offset) {
- /* fits into current chunk */
- *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
- *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
- offset+=bpl;
- } else {
- /* scanline needs to be split */
- todo = bpl;
- *(rp++)=cpu_to_le32(RISC_WRITE|sol|
- (sg_dma_len(sg)-offset));
- *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
- todo -= (sg_dma_len(sg)-offset);
- offset = 0;
- sg++;
- while (todo > sg_dma_len(sg)) {
- *(rp++)=cpu_to_le32(RISC_WRITE|
- sg_dma_len(sg));
- *(rp++)=cpu_to_le32(sg_dma_address(sg));
- todo -= sg_dma_len(sg);
- sg++;
- }
- *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
- *(rp++)=cpu_to_le32(sg_dma_address(sg));
- offset += todo;
- }
- offset += padding;
- }
-
- return rp;
-}
-
-int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist,
- unsigned int top_offset, unsigned int bottom_offset,
- unsigned int bpl, unsigned int padding, unsigned int lines)
-{
- u32 instructions,fields;
- __le32 *rp;
- int rc;
-
- fields = 0;
- if (UNSET != top_offset)
- fields++;
- if (UNSET != bottom_offset)
- fields++;
-
- /* estimate risc mem: worst case is one write per page border +
- one write per scan line + syncs + jump (all 2 dwords). Padding
- can cause next bpl to start close to a page border. First DMA
- region may be smaller than PAGE_SIZE */
- instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
- instructions += 2;
- if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
- return rc;
-
- /* write risc instructions */
- rp = risc->cpu;
- if (UNSET != top_offset)
- rp = cx88_risc_field(rp, sglist, top_offset, 0,
- bpl, padding, lines, 0);
- if (UNSET != bottom_offset)
- rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
- bpl, padding, lines, 0);
-
- /* save pointer to jmp instruction address */
- risc->jmp = rp;
- BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
- return 0;
-}
-
-int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist, unsigned int bpl,
- unsigned int lines, unsigned int lpi)
-{
- u32 instructions;
- __le32 *rp;
- int rc;
-
- /* estimate risc mem: worst case is one write per page border +
- one write per scan line + syncs + jump (all 2 dwords). Here
- there is no padding and no sync. First DMA region may be smaller
- than PAGE_SIZE */
- instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
- instructions += 1;
- if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
- return rc;
-
- /* write risc instructions */
- rp = risc->cpu;
- rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
-
- /* save pointer to jmp instruction address */
- risc->jmp = rp;
- BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
- return 0;
-}
-
-int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value)
-{
- __le32 *rp;
- int rc;
-
- if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
- return rc;
-
- /* write risc instructions */
- rp = risc->cpu;
- *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
- *(rp++) = cpu_to_le32(reg);
- *(rp++) = cpu_to_le32(value);
- *(rp++) = cpu_to_le32(mask);
- *(rp++) = cpu_to_le32(RISC_JUMP);
- *(rp++) = cpu_to_le32(risc->dma);
- return 0;
-}
-
-void
-cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
-{
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
- BUG_ON(in_interrupt());
- videobuf_waiton(q, &buf->vb, 0, 0);
- videobuf_dma_unmap(q->dev, dma);
- videobuf_dma_free(dma);
- btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-/* ------------------------------------------------------------------ */
-/* our SRAM memory layout */
-
-/* we are going to put all thr risc programs into host memory, so we
- * can use the whole SDRAM for the DMA fifos. To simplify things, we
- * use a static memory layout. That surely will waste memory in case
- * we don't use all DMA channels at the same time (which will be the
- * case most of the time). But that still gives us enough FIFO space
- * to be able to deal with insane long pci latencies ...
- *
- * FIFO space allocations:
- * channel 21 (y video) - 10.0k
- * channel 22 (u video) - 2.0k
- * channel 23 (v video) - 2.0k
- * channel 24 (vbi) - 4.0k
- * channels 25+26 (audio) - 4.0k
- * channel 28 (mpeg) - 4.0k
- * channel 27 (audio rds)- 3.0k
- * TOTAL = 29.0k
- *
- * Every channel has 160 bytes control data (64 bytes instruction
- * queue and 6 CDT entries), which is close to 2k total.
- *
- * Address layout:
- * 0x0000 - 0x03ff CMDs / reserved
- * 0x0400 - 0x0bff instruction queues + CDs
- * 0x0c00 - FIFOs
- */
-
-const struct sram_channel const cx88_sram_channels[] = {
- [SRAM_CH21] = {
- .name = "video y / packed",
- .cmds_start = 0x180040,
- .ctrl_start = 0x180400,
- .cdt = 0x180400 + 64,
- .fifo_start = 0x180c00,
- .fifo_size = 0x002800,
- .ptr1_reg = MO_DMA21_PTR1,
- .ptr2_reg = MO_DMA21_PTR2,
- .cnt1_reg = MO_DMA21_CNT1,
- .cnt2_reg = MO_DMA21_CNT2,
- },
- [SRAM_CH22] = {
- .name = "video u",
- .cmds_start = 0x180080,
- .ctrl_start = 0x1804a0,
- .cdt = 0x1804a0 + 64,
- .fifo_start = 0x183400,
- .fifo_size = 0x000800,
- .ptr1_reg = MO_DMA22_PTR1,
- .ptr2_reg = MO_DMA22_PTR2,
- .cnt1_reg = MO_DMA22_CNT1,
- .cnt2_reg = MO_DMA22_CNT2,
- },
- [SRAM_CH23] = {
- .name = "video v",
- .cmds_start = 0x1800c0,
- .ctrl_start = 0x180540,
- .cdt = 0x180540 + 64,
- .fifo_start = 0x183c00,
- .fifo_size = 0x000800,
- .ptr1_reg = MO_DMA23_PTR1,
- .ptr2_reg = MO_DMA23_PTR2,
- .cnt1_reg = MO_DMA23_CNT1,
- .cnt2_reg = MO_DMA23_CNT2,
- },
- [SRAM_CH24] = {
- .name = "vbi",
- .cmds_start = 0x180100,
- .ctrl_start = 0x1805e0,
- .cdt = 0x1805e0 + 64,
- .fifo_start = 0x184400,
- .fifo_size = 0x001000,
- .ptr1_reg = MO_DMA24_PTR1,
- .ptr2_reg = MO_DMA24_PTR2,
- .cnt1_reg = MO_DMA24_CNT1,
- .cnt2_reg = MO_DMA24_CNT2,
- },
- [SRAM_CH25] = {
- .name = "audio from",
- .cmds_start = 0x180140,
- .ctrl_start = 0x180680,
- .cdt = 0x180680 + 64,
- .fifo_start = 0x185400,
- .fifo_size = 0x001000,
- .ptr1_reg = MO_DMA25_PTR1,
- .ptr2_reg = MO_DMA25_PTR2,
- .cnt1_reg = MO_DMA25_CNT1,
- .cnt2_reg = MO_DMA25_CNT2,
- },
- [SRAM_CH26] = {
- .name = "audio to",
- .cmds_start = 0x180180,
- .ctrl_start = 0x180720,
- .cdt = 0x180680 + 64, /* same as audio IN */
- .fifo_start = 0x185400, /* same as audio IN */
- .fifo_size = 0x001000, /* same as audio IN */
- .ptr1_reg = MO_DMA26_PTR1,
- .ptr2_reg = MO_DMA26_PTR2,
- .cnt1_reg = MO_DMA26_CNT1,
- .cnt2_reg = MO_DMA26_CNT2,
- },
- [SRAM_CH28] = {
- .name = "mpeg",
- .cmds_start = 0x180200,
- .ctrl_start = 0x1807C0,
- .cdt = 0x1807C0 + 64,
- .fifo_start = 0x186400,
- .fifo_size = 0x001000,
- .ptr1_reg = MO_DMA28_PTR1,
- .ptr2_reg = MO_DMA28_PTR2,
- .cnt1_reg = MO_DMA28_CNT1,
- .cnt2_reg = MO_DMA28_CNT2,
- },
- [SRAM_CH27] = {
- .name = "audio rds",
- .cmds_start = 0x1801C0,
- .ctrl_start = 0x180860,
- .cdt = 0x180860 + 64,
- .fifo_start = 0x187400,
- .fifo_size = 0x000C00,
- .ptr1_reg = MO_DMA27_PTR1,
- .ptr2_reg = MO_DMA27_PTR2,
- .cnt1_reg = MO_DMA27_CNT1,
- .cnt2_reg = MO_DMA27_CNT2,
- },
-};
-
-int cx88_sram_channel_setup(struct cx88_core *core,
- const struct sram_channel *ch,
- unsigned int bpl, u32 risc)
-{
- unsigned int i,lines;
- u32 cdt;
-
- bpl = (bpl + 7) & ~7; /* alignment */
- cdt = ch->cdt;
- lines = ch->fifo_size / bpl;
- if (lines > 6)
- lines = 6;
- BUG_ON(lines < 2);
-
- /* write CDT */
- for (i = 0; i < lines; i++)
- cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
-
- /* write CMDS */
- cx_write(ch->cmds_start + 0, risc);
- cx_write(ch->cmds_start + 4, cdt);
- cx_write(ch->cmds_start + 8, (lines*16) >> 3);
- cx_write(ch->cmds_start + 12, ch->ctrl_start);
- cx_write(ch->cmds_start + 16, 64 >> 2);
- for (i = 20; i < 64; i += 4)
- cx_write(ch->cmds_start + i, 0);
-
- /* fill registers */
- cx_write(ch->ptr1_reg, ch->fifo_start);
- cx_write(ch->ptr2_reg, cdt);
- cx_write(ch->cnt1_reg, (bpl >> 3) -1);
- cx_write(ch->cnt2_reg, (lines*16) >> 3);
-
- dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* debug helper code */
-
-static int cx88_risc_decode(u32 risc)
-{
- static const char * const instr[16] = {
- [ RISC_SYNC >> 28 ] = "sync",
- [ RISC_WRITE >> 28 ] = "write",
- [ RISC_WRITEC >> 28 ] = "writec",
- [ RISC_READ >> 28 ] = "read",
- [ RISC_READC >> 28 ] = "readc",
- [ RISC_JUMP >> 28 ] = "jump",
- [ RISC_SKIP >> 28 ] = "skip",
- [ RISC_WRITERM >> 28 ] = "writerm",
- [ RISC_WRITECM >> 28 ] = "writecm",
- [ RISC_WRITECR >> 28 ] = "writecr",
- };
- static int const incr[16] = {
- [ RISC_WRITE >> 28 ] = 2,
- [ RISC_JUMP >> 28 ] = 2,
- [ RISC_WRITERM >> 28 ] = 3,
- [ RISC_WRITECM >> 28 ] = 3,
- [ RISC_WRITECR >> 28 ] = 4,
- };
- static const char * const bits[] = {
- "12", "13", "14", "resync",
- "cnt0", "cnt1", "18", "19",
- "20", "21", "22", "23",
- "irq1", "irq2", "eol", "sol",
- };
- int i;
-
- printk("0x%08x [ %s", risc,
- instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
- for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
- if (risc & (1 << (i + 12)))
- printk(" %s",bits[i]);
- printk(" count=%d ]\n", risc & 0xfff);
- return incr[risc >> 28] ? incr[risc >> 28] : 1;
-}
-
-
-void cx88_sram_channel_dump(struct cx88_core *core,
- const struct sram_channel *ch)
-{
- static const char * const name[] = {
- "initial risc",
- "cdt base",
- "cdt size",
- "iq base",
- "iq size",
- "risc pc",
- "iq wr ptr",
- "iq rd ptr",
- "cdt current",
- "pci target",
- "line / byte",
- };
- u32 risc;
- unsigned int i,j,n;
-
- printk("%s: %s - dma channel status dump\n",
- core->name,ch->name);
- for (i = 0; i < ARRAY_SIZE(name); i++)
- printk("%s: cmds: %-12s: 0x%08x\n",
- core->name,name[i],
- cx_read(ch->cmds_start + 4*i));
- for (n = 1, i = 0; i < 4; i++) {
- risc = cx_read(ch->cmds_start + 4 * (i+11));
- printk("%s: risc%d: ", core->name, i);
- if (--n)
- printk("0x%08x [ arg #%d ]\n", risc, n);
- else
- n = cx88_risc_decode(risc);
- }
- for (i = 0; i < 16; i += n) {
- risc = cx_read(ch->ctrl_start + 4 * i);
- printk("%s: iq %x: ", core->name, i);
- n = cx88_risc_decode(risc);
- for (j = 1; j < n; j++) {
- risc = cx_read(ch->ctrl_start + 4 * (i+j));
- printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
- core->name, i+j, risc, j);
- }
- }
-
- printk("%s: fifo: 0x%08x -> 0x%x\n",
- core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
- printk("%s: ctrl: 0x%08x -> 0x%x\n",
- core->name, ch->ctrl_start, ch->ctrl_start+6*16);
- printk("%s: ptr1_reg: 0x%08x\n",
- core->name,cx_read(ch->ptr1_reg));
- printk("%s: ptr2_reg: 0x%08x\n",
- core->name,cx_read(ch->ptr2_reg));
- printk("%s: cnt1_reg: 0x%08x\n",
- core->name,cx_read(ch->cnt1_reg));
- printk("%s: cnt2_reg: 0x%08x\n",
- core->name,cx_read(ch->cnt2_reg));
-}
-
-static const char *cx88_pci_irqs[32] = {
- "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
- "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
- "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
- "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
-};
-
-void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
- int len, u32 bits, u32 mask)
-{
- unsigned int i;
-
- printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
- for (i = 0; i < len; i++) {
- if (!(bits & (1 << i)))
- continue;
- if (strings[i])
- printk(" %s", strings[i]);
- else
- printk(" %d", i);
- if (!(mask & (1 << i)))
- continue;
- printk("*");
- }
- printk("\n");
-}
-
-/* ------------------------------------------------------------------ */
-
-int cx88_core_irq(struct cx88_core *core, u32 status)
-{
- int handled = 0;
-
- if (status & PCI_INT_IR_SMPINT) {
- cx88_ir_irq(core);
- handled++;
- }
- if (!handled)
- cx88_print_irqbits(core->name, "irq pci",
- cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
- status, core->pci_irqmask);
- return handled;
-}
-
-void cx88_wakeup(struct cx88_core *core,
- struct cx88_dmaqueue *q, u32 count)
-{
- struct cx88_buffer *buf;
- int bc;
-
- for (bc = 0;; bc++) {
- if (list_empty(&q->active))
- break;
- buf = list_entry(q->active.next,
- struct cx88_buffer, vb.queue);
- /* count comes from the hw and is is 16bit wide --
- * this trick handles wrap-arounds correctly for
- * up to 32767 buffers in flight... */
- if ((s16) (count - buf->count) < 0)
- break;
- do_gettimeofday(&buf->vb.ts);
- dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
- count, buf->count);
- buf->vb.state = VIDEOBUF_DONE;
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- }
- if (list_empty(&q->active)) {
- del_timer(&q->timeout);
- } else {
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- }
- if (bc != 1)
- dprintk(2, "%s: %d buffers handled (should be 1)\n",
- __func__, bc);
-}
-
-void cx88_shutdown(struct cx88_core *core)
-{
- /* disable RISC controller + IRQs */
- cx_write(MO_DEV_CNTRL2, 0);
-
- /* stop dma transfers */
- cx_write(MO_VID_DMACNTRL, 0x0);
- cx_write(MO_AUD_DMACNTRL, 0x0);
- cx_write(MO_TS_DMACNTRL, 0x0);
- cx_write(MO_VIP_DMACNTRL, 0x0);
- cx_write(MO_GPHST_DMACNTRL, 0x0);
-
- /* stop interrupts */
- cx_write(MO_PCI_INTMSK, 0x0);
- cx_write(MO_VID_INTMSK, 0x0);
- cx_write(MO_AUD_INTMSK, 0x0);
- cx_write(MO_TS_INTMSK, 0x0);
- cx_write(MO_VIP_INTMSK, 0x0);
- cx_write(MO_GPHST_INTMSK, 0x0);
-
- /* stop capturing */
- cx_write(VID_CAPTURE_CONTROL, 0);
-}
-
-int cx88_reset(struct cx88_core *core)
-{
- dprintk(1,"%s\n",__func__);
- cx88_shutdown(core);
-
- /* clear irq status */
- cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
- cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
- cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
-
- /* wait a bit */
- msleep(100);
-
- /* init sram */
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
-
- /* misc init ... */
- cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
- (1 << 12) | // agc gain
- (1 << 11) | // adaptibe agc
- (0 << 10) | // chroma agc
- (0 << 9) | // ckillen
- (7)));
-
- /* setup image format */
- cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
-
- /* setup FIFO Thresholds */
- cx_write(MO_PDMA_STHRSH, 0x0807);
- cx_write(MO_PDMA_DTHRSH, 0x0807);
-
- /* fixes flashing of image */
- cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
- cx_write(MO_AGC_BACK_VBI, 0x00E00555);
-
- cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
- cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
- cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
-
- /* Reset on-board parts */
- cx_write(MO_SRST_IO, 0);
- msleep(10);
- cx_write(MO_SRST_IO, 1);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int inline norm_swidth(v4l2_std_id norm)
-{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
-}
-
-static unsigned int inline norm_hdelay(v4l2_std_id norm)
-{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
-}
-
-static unsigned int inline norm_vdelay(v4l2_std_id norm)
-{
- return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
-}
-
-static unsigned int inline norm_fsc8(v4l2_std_id norm)
-{
- if (norm & V4L2_STD_PAL_M)
- return 28604892; // 3.575611 MHz
-
- if (norm & (V4L2_STD_PAL_Nc))
- return 28656448; // 3.582056 MHz
-
- if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
- return 28636360; // 3.57954545 MHz +/- 10 Hz
-
- /* SECAM have also different sub carrier for chroma,
- but step_db and step_dr, at cx88_set_tvnorm already handles that.
-
- The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
- */
-
- return 35468950; // 4.43361875 MHz +/- 5 Hz
-}
-
-static unsigned int inline norm_htotal(v4l2_std_id norm)
-{
-
- unsigned int fsc4=norm_fsc8(norm)/2;
-
- /* returns 4*FSC / vtotal / frames per seconds */
- return (norm & V4L2_STD_625_50) ?
- ((fsc4+312)/625+12)/25 :
- ((fsc4+262)/525*1001+15000)/30000;
-}
-
-static unsigned int inline norm_vbipack(v4l2_std_id norm)
-{
- return (norm & V4L2_STD_625_50) ? 511 : 400;
-}
-
-int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
- enum v4l2_field field)
-{
- unsigned int swidth = norm_swidth(core->tvnorm);
- unsigned int sheight = norm_maxh(core->tvnorm);
- u32 value;
-
- dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
- V4L2_FIELD_HAS_TOP(field) ? "T" : "",
- V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
- v4l2_norm_to_name(core->tvnorm));
- if (!V4L2_FIELD_HAS_BOTH(field))
- height *= 2;
-
- // recalc H delay and scale registers
- value = (width * norm_hdelay(core->tvnorm)) / swidth;
- value &= 0x3fe;
- cx_write(MO_HDELAY_EVEN, value);
- cx_write(MO_HDELAY_ODD, value);
- dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
-
- value = (swidth * 4096 / width) - 4096;
- cx_write(MO_HSCALE_EVEN, value);
- cx_write(MO_HSCALE_ODD, value);
- dprintk(1,"set_scale: hscale 0x%04x\n", value);
-
- cx_write(MO_HACTIVE_EVEN, width);
- cx_write(MO_HACTIVE_ODD, width);
- dprintk(1,"set_scale: hactive 0x%04x\n", width);
-
- // recalc V scale Register (delay is constant)
- cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
- cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
- dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
-
- value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
- cx_write(MO_VSCALE_EVEN, value);
- cx_write(MO_VSCALE_ODD, value);
- dprintk(1,"set_scale: vscale 0x%04x\n", value);
-
- cx_write(MO_VACTIVE_EVEN, sheight);
- cx_write(MO_VACTIVE_ODD, sheight);
- dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
-
- // setup filters
- value = 0;
- value |= (1 << 19); // CFILT (default)
- if (core->tvnorm & V4L2_STD_SECAM) {
- value |= (1 << 15);
- value |= (1 << 16);
- }
- if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
- value |= (1 << 13) | (1 << 5);
- if (V4L2_FIELD_INTERLACED == field)
- value |= (1 << 3); // VINT (interlaced vertical scaling)
- if (width < 385)
- value |= (1 << 0); // 3-tap interpolation
- if (width < 193)
- value |= (1 << 1); // 5-tap interpolation
- if (nocomb)
- value |= (3 << 5); // disable comb filter
-
- cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */
- cx_andor(MO_FILTER_ODD, 0x7ffc7f, value);
- dprintk(1,"set_scale: filter 0x%04x\n", value);
-
- return 0;
-}
-
-static const u32 xtal = 28636363;
-
-static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
-{
- static const u32 pre[] = { 0, 0, 0, 3, 2, 1 };
- u64 pll;
- u32 reg;
- int i;
-
- if (prescale < 2)
- prescale = 2;
- if (prescale > 5)
- prescale = 5;
-
- pll = ofreq * 8 * prescale * (u64)(1 << 20);
- do_div(pll,xtal);
- reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
- if (((reg >> 20) & 0x3f) < 14) {
- printk("%s/0: pll out of range\n",core->name);
- return -1;
- }
-
- dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
- reg, cx_read(MO_PLL_REG), ofreq);
- cx_write(MO_PLL_REG, reg);
- for (i = 0; i < 100; i++) {
- reg = cx_read(MO_DEVICE_STATUS);
- if (reg & (1<<2)) {
- dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
- prescale,ofreq);
- return 0;
- }
- dprintk(1,"pll not locked yet, waiting ...\n");
- msleep(10);
- }
- dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
- return -1;
-}
-
-int cx88_start_audio_dma(struct cx88_core *core)
-{
- /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
- int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
-
- int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
-
- /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
- if (cx_read(MO_AUD_DMACNTRL) & 0x10)
- return 0;
-
- /* setup fifo + format */
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
- rds_bpl, 0);
-
- cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
- cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
-
- /* enable Up, Down and Audio RDS fifo */
- cx_write(MO_AUD_DMACNTRL, 0x0007);
-
- return 0;
-}
-
-int cx88_stop_audio_dma(struct cx88_core *core)
-{
- /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
- if (cx_read(MO_AUD_DMACNTRL) & 0x10)
- return 0;
-
- /* stop dma */
- cx_write(MO_AUD_DMACNTRL, 0x0000);
-
- return 0;
-}
-
-static int set_tvaudio(struct cx88_core *core)
-{
- v4l2_std_id norm = core->tvnorm;
-
- if (CX88_VMUX_TELEVISION != INPUT(core->input).type &&
- CX88_VMUX_CABLE != INPUT(core->input).type)
- return 0;
-
- if (V4L2_STD_PAL_BG & norm) {
- core->tvaudio = WW_BG;
-
- } else if (V4L2_STD_PAL_DK & norm) {
- core->tvaudio = WW_DK;
-
- } else if (V4L2_STD_PAL_I & norm) {
- core->tvaudio = WW_I;
-
- } else if (V4L2_STD_SECAM_L & norm) {
- core->tvaudio = WW_L;
-
- } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
- core->tvaudio = WW_BG;
-
- } else if (V4L2_STD_SECAM_DK & norm) {
- core->tvaudio = WW_DK;
-
- } else if ((V4L2_STD_NTSC_M & norm) ||
- (V4L2_STD_PAL_M & norm)) {
- core->tvaudio = WW_BTSC;
-
- } else if (V4L2_STD_NTSC_M_JP & norm) {
- core->tvaudio = WW_EIAJ;
-
- } else {
- printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
- core->name, v4l2_norm_to_name(core->tvnorm));
- core->tvaudio = WW_NONE;
- return 0;
- }
-
- cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
- cx88_set_tvaudio(core);
- /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
-
-/*
- This should be needed only on cx88-alsa. It seems that some cx88 chips have
- bugs and does require DMA enabled for it to work.
- */
- cx88_start_audio_dma(core);
- return 0;
-}
-
-
-
-int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
-{
- u32 fsc8;
- u32 adc_clock;
- u32 vdec_clock;
- u32 step_db,step_dr;
- u64 tmp64;
- u32 bdelay,agcdelay,htotal;
- u32 cxiformat, cxoformat;
-
- core->tvnorm = norm;
- fsc8 = norm_fsc8(norm);
- adc_clock = xtal;
- vdec_clock = fsc8;
- step_db = fsc8;
- step_dr = fsc8;
-
- if (norm & V4L2_STD_NTSC_M_JP) {
- cxiformat = VideoFormatNTSCJapan;
- cxoformat = 0x181f0008;
- } else if (norm & V4L2_STD_NTSC_443) {
- cxiformat = VideoFormatNTSC443;
- cxoformat = 0x181f0008;
- } else if (norm & V4L2_STD_PAL_M) {
- cxiformat = VideoFormatPALM;
- cxoformat = 0x1c1f0008;
- } else if (norm & V4L2_STD_PAL_N) {
- cxiformat = VideoFormatPALN;
- cxoformat = 0x1c1f0008;
- } else if (norm & V4L2_STD_PAL_Nc) {
- cxiformat = VideoFormatPALNC;
- cxoformat = 0x1c1f0008;
- } else if (norm & V4L2_STD_PAL_60) {
- cxiformat = VideoFormatPAL60;
- cxoformat = 0x181f0008;
- } else if (norm & V4L2_STD_NTSC) {
- cxiformat = VideoFormatNTSC;
- cxoformat = 0x181f0008;
- } else if (norm & V4L2_STD_SECAM) {
- step_db = 4250000 * 8;
- step_dr = 4406250 * 8;
-
- cxiformat = VideoFormatSECAM;
- cxoformat = 0x181f0008;
- } else { /* PAL */
- cxiformat = VideoFormatPAL;
- cxoformat = 0x181f0008;
- }
-
- dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
- v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
- step_db, step_dr);
- set_pll(core,2,vdec_clock);
-
- dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
- cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
- /* Chroma AGC must be disabled if SECAM is used, we enable it
- by default on PAL and NTSC */
- cx_andor(MO_INPUT_FORMAT, 0x40f,
- norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
-
- // FIXME: as-is from DScaler
- dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
- cxoformat, cx_read(MO_OUTPUT_FORMAT));
- cx_write(MO_OUTPUT_FORMAT, cxoformat);
-
- // MO_SCONV_REG = adc clock / video dec clock * 2^17
- tmp64 = adc_clock * (u64)(1 << 17);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SCONV_REG));
- cx_write(MO_SCONV_REG, (u32)tmp64);
-
- // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
- tmp64 = step_db * (u64)(1 << 22);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SUB_STEP));
- cx_write(MO_SUB_STEP, (u32)tmp64);
-
- // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
- tmp64 = step_dr * (u64)(1 << 22);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SUB_STEP_DR));
- cx_write(MO_SUB_STEP_DR, (u32)tmp64);
-
- // bdelay + agcdelay
- bdelay = vdec_clock * 65 / 20000000 + 21;
- agcdelay = vdec_clock * 68 / 20000000 + 15;
- dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
- (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
- cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
-
- // htotal
- tmp64 = norm_htotal(norm) * (u64)vdec_clock;
- do_div(tmp64, fsc8);
- htotal = (u32)tmp64;
- dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
- htotal, cx_read(MO_HTOTAL), (u32)tmp64);
- cx_andor(MO_HTOTAL, 0x07ff, htotal);
-
- // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
- // the effective vbi offset ~244 samples, the same as the Bt8x8
- cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
-
- // this is needed as well to set all tvnorm parameter
- cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
-
- // audio
- set_tvaudio(core);
-
- // tell i2c chips
- call_all(core, core, s_std, norm);
-
- /* The chroma_agc control should be inaccessible if the video format is SECAM */
- v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM);
-
- // done
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-struct video_device *cx88_vdev_init(struct cx88_core *core,
- struct pci_dev *pci,
- const struct video_device *template_,
- const char *type)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
- *vfd = *template_;
- vfd->v4l2_dev = &core->v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
- core->name, type, core->board.name);
- set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- return vfd;
-}
-
-struct cx88_core* cx88_core_get(struct pci_dev *pci)
-{
- struct cx88_core *core;
-
- mutex_lock(&devlist);
- list_for_each_entry(core, &cx88_devlist, devlist) {
- if (pci->bus->number != core->pci_bus)
- continue;
- if (PCI_SLOT(pci->devfn) != core->pci_slot)
- continue;
-
- if (0 != cx88_get_resources(core, pci)) {
- mutex_unlock(&devlist);
- return NULL;
- }
- atomic_inc(&core->refcount);
- mutex_unlock(&devlist);
- return core;
- }
-
- core = cx88_core_create(pci, cx88_devcount);
- if (NULL != core) {
- cx88_devcount++;
- list_add_tail(&core->devlist, &cx88_devlist);
- }
-
- mutex_unlock(&devlist);
- return core;
-}
-
-void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
-{
- release_mem_region(pci_resource_start(pci,0),
- pci_resource_len(pci,0));
-
- if (!atomic_dec_and_test(&core->refcount))
- return;
-
- mutex_lock(&devlist);
- cx88_ir_fini(core);
- if (0 == core->i2c_rc) {
- if (core->i2c_rtc)
- i2c_unregister_device(core->i2c_rtc);
- i2c_del_adapter(&core->i2c_adap);
- }
- list_del(&core->devlist);
- iounmap(core->lmmio);
- cx88_devcount--;
- mutex_unlock(&devlist);
- v4l2_ctrl_handler_free(&core->video_hdl);
- v4l2_ctrl_handler_free(&core->audio_hdl);
- v4l2_device_unregister(&core->v4l2_dev);
- kfree(core);
-}
-
-/* ------------------------------------------------------------------ */
-
-EXPORT_SYMBOL(cx88_print_irqbits);
-
-EXPORT_SYMBOL(cx88_core_irq);
-EXPORT_SYMBOL(cx88_wakeup);
-EXPORT_SYMBOL(cx88_reset);
-EXPORT_SYMBOL(cx88_shutdown);
-
-EXPORT_SYMBOL(cx88_risc_buffer);
-EXPORT_SYMBOL(cx88_risc_databuffer);
-EXPORT_SYMBOL(cx88_risc_stopper);
-EXPORT_SYMBOL(cx88_free_buffer);
-
-EXPORT_SYMBOL(cx88_sram_channels);
-EXPORT_SYMBOL(cx88_sram_channel_setup);
-EXPORT_SYMBOL(cx88_sram_channel_dump);
-
-EXPORT_SYMBOL(cx88_set_tvnorm);
-EXPORT_SYMBOL(cx88_set_scale);
-
-EXPORT_SYMBOL(cx88_vdev_init);
-EXPORT_SYMBOL(cx88_core_get);
-EXPORT_SYMBOL(cx88_core_put);
-
-EXPORT_SYMBOL(cx88_ir_start);
-EXPORT_SYMBOL(cx88_ir_stop);
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
- */
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
deleted file mode 100644
index 003937cd72f5..000000000000
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ /dev/null
@@ -1,1778 +0,0 @@
-/*
- *
- * device driver for Conexant 2388x based TV cards
- * MPEG Transport Stream (DVB) routines
- *
- * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/suspend.h>
-
-#include "cx88.h"
-#include "dvb-pll.h"
-#include <media/v4l2-common.h>
-
-#include "mt352.h"
-#include "mt352_priv.h"
-#include "cx88-vp3054-i2c.h"
-#include "zl10353.h"
-#include "cx22702.h"
-#include "or51132.h"
-#include "lgdt330x.h"
-#include "s5h1409.h"
-#include "xc4000.h"
-#include "xc5000.h"
-#include "nxt200x.h"
-#include "cx24123.h"
-#include "isl6421.h"
-#include "tuner-simple.h"
-#include "tda9887.h"
-#include "s5h1411.h"
-#include "stv0299.h"
-#include "z0194a.h"
-#include "stv0288.h"
-#include "stb6000.h"
-#include "cx24116.h"
-#include "stv0900.h"
-#include "stb6100.h"
-#include "stb6100_proc.h"
-#include "mb86a16.h"
-#include "ds3000.h"
-
-MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
-MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CX88_VERSION);
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
-
-static unsigned int dvb_buf_tscnt = 32;
-module_param(dvb_buf_tscnt, int, 0644);
-MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk(level,fmt, arg...) if (debug >= level) \
- printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg)
-
-/* ------------------------------------------------------------------ */
-
-static int dvb_buf_setup(struct videobuf_queue *q,
- unsigned int *count, unsigned int *size)
-{
- struct cx8802_dev *dev = q->priv_data;
-
- dev->ts_packet_size = 188 * 4;
- dev->ts_packet_count = dvb_buf_tscnt;
-
- *size = dev->ts_packet_size * dev->ts_packet_count;
- *count = dvb_buf_tscnt;
- return 0;
-}
-
-static int dvb_buf_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct cx8802_dev *dev = q->priv_data;
- return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field);
-}
-
-static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct cx8802_dev *dev = q->priv_data;
- cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
-}
-
-static void dvb_buf_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- cx88_free_buffer(q, (struct cx88_buffer*)vb);
-}
-
-static const struct videobuf_queue_ops dvb_qops = {
- .buf_setup = dvb_buf_setup,
- .buf_prepare = dvb_buf_prepare,
- .buf_queue = dvb_buf_queue,
- .buf_release = dvb_buf_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx8802_driver *drv = NULL;
- int ret = 0;
- int fe_id;
-
- fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe);
- if (!fe_id) {
- printk(KERN_ERR "%s() No frontend found\n", __func__);
- return -EINVAL;
- }
-
- mutex_lock(&dev->core->lock);
- drv = cx8802_get_driver(dev, CX88_MPEG_DVB);
- if (drv) {
- if (acquire){
- dev->frontends.active_fe_id = fe_id;
- ret = drv->request_acquire(drv);
- } else {
- ret = drv->request_release(drv);
- dev->frontends.active_fe_id = 0;
- }
- }
- mutex_unlock(&dev->core->lock);
-
- return ret;
-}
-
-static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open)
-{
- struct videobuf_dvb_frontends *f;
- struct videobuf_dvb_frontend *fe;
-
- if (!core->dvbdev)
- return;
-
- f = &core->dvbdev->frontends;
-
- if (!f)
- return;
-
- if (f->gate <= 1) /* undefined or fe0 */
- fe = videobuf_dvb_get_frontend(f, 1);
- else
- fe = videobuf_dvb_get_frontend(f, f->gate);
-
- if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl)
- fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open);
-}
-
-/* ------------------------------------------------------------------ */
-
-static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
-{
- static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
- static const u8 reset [] = { RESET, 0x80 };
- static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
- static const u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 };
- static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
- static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
-
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
- return 0;
-}
-
-static int dvico_dual_demod_init(struct dvb_frontend *fe)
-{
- static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 };
- static const u8 reset [] = { RESET, 0x80 };
- static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
- static const u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
- static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
- static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
-
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
-
- return 0;
-}
-
-static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
-{
- static const u8 clock_config [] = { 0x89, 0x38, 0x39 };
- static const u8 reset [] = { 0x50, 0x80 };
- static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
- static const u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF,
- 0x00, 0xFF, 0x00, 0x40, 0x40 };
- static const u8 dntv_extra[] = { 0xB5, 0x7A };
- static const u8 capt_range_cfg[] = { 0x75, 0x32 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(2000);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
-
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- udelay(2000);
- mt352_write(fe, dntv_extra, sizeof(dntv_extra));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
-
- return 0;
-}
-
-static const struct mt352_config dvico_fusionhdtv = {
- .demod_address = 0x0f,
- .demod_init = dvico_fusionhdtv_demod_init,
-};
-
-static const struct mt352_config dntv_live_dvbt_config = {
- .demod_address = 0x0f,
- .demod_init = dntv_live_dvbt_demod_init,
-};
-
-static const struct mt352_config dvico_fusionhdtv_dual = {
- .demod_address = 0x0f,
- .demod_init = dvico_dual_demod_init,
-};
-
-static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .if2 = 45600,
-};
-
-static struct mb86a16_config twinhan_vp1027 = {
- .demod_address = 0x08,
-};
-
-#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
-static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
-{
- static const u8 clock_config [] = { 0x89, 0x38, 0x38 };
- static const u8 reset [] = { 0x50, 0x80 };
- static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 };
- static const u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF,
- 0x00, 0xFF, 0x00, 0x40, 0x40 };
- static const u8 dntv_extra[] = { 0xB5, 0x7A };
- static const u8 capt_range_cfg[] = { 0x75, 0x32 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(2000);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
-
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- udelay(2000);
- mt352_write(fe, dntv_extra, sizeof(dntv_extra));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
-
- return 0;
-}
-
-static const struct mt352_config dntv_live_dvbt_pro_config = {
- .demod_address = 0x0f,
- .no_tuner = 1,
- .demod_init = dntv_live_dvbt_pro_demod_init,
-};
-#endif
-
-static const struct zl10353_config dvico_fusionhdtv_hybrid = {
- .demod_address = 0x0f,
- .no_tuner = 1,
-};
-
-static const struct zl10353_config dvico_fusionhdtv_xc3028 = {
- .demod_address = 0x0f,
- .if2 = 45600,
- .no_tuner = 1,
-};
-
-static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = {
- .demod_address = 0x0f,
- .if2 = 4560,
- .no_tuner = 1,
- .demod_init = dvico_fusionhdtv_demod_init,
-};
-
-static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
- .demod_address = 0x0f,
-};
-
-static const struct cx22702_config connexant_refboard_config = {
- .demod_address = 0x43,
- .output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static const struct cx22702_config hauppauge_hvr_config = {
- .demod_address = 0x63,
- .output_mode = CX22702_SERIAL_OUTPUT,
-};
-
-static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
- return 0;
-}
-
-static const struct or51132_config pchdtv_hd3000 = {
- .demod_address = 0x15,
- .set_ts_params = or51132_set_ts_param,
-};
-
-static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- dprintk(1, "%s: index = %d\n", __func__, index);
- if (index == 0)
- cx_clear(MO_GP0_IO, 8);
- else
- cx_set(MO_GP0_IO, 8);
- return 0;
-}
-
-static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- if (is_punctured)
- dev->ts_gen_cntrl |= 0x04;
- else
- dev->ts_gen_cntrl &= ~0x04;
- return 0;
-}
-
-static struct lgdt330x_config fusionhdtv_3_gold = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3302,
- .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
-};
-
-static const struct lgdt330x_config fusionhdtv_5_gold = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
-};
-
-static const struct lgdt330x_config pchdtv_hd5500 = {
- .demod_address = 0x59,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
-};
-
-static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00;
- return 0;
-}
-
-static const struct nxt200x_config ati_hdtvwonder = {
- .demod_address = 0x0a,
- .set_ts_params = nxt200x_set_ts_param,
-};
-
-static int cx24123_set_ts_param(struct dvb_frontend* fe,
- int is_punctured)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- dev->ts_gen_cntrl = 0x02;
- return 0;
-}
-
-static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
- fe_sec_voltage_t voltage)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- if (voltage == SEC_VOLTAGE_OFF)
- cx_write(MO_GP0_IO, 0x000006fb);
- else
- cx_write(MO_GP0_IO, 0x000006f9);
-
- if (core->prev_set_voltage)
- return core->prev_set_voltage(fe, voltage);
- return 0;
-}
-
-static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- if (voltage == SEC_VOLTAGE_OFF) {
- dprintk(1,"LNB Voltage OFF\n");
- cx_write(MO_GP0_IO, 0x0000efff);
- }
-
- if (core->prev_set_voltage)
- return core->prev_set_voltage(fe, voltage);
- return 0;
-}
-
-static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
-{
- struct cx8802_dev *dev= fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- cx_set(MO_GP0_IO, 0x6040);
- switch (voltage) {
- case SEC_VOLTAGE_13:
- cx_clear(MO_GP0_IO, 0x20);
- break;
- case SEC_VOLTAGE_18:
- cx_set(MO_GP0_IO, 0x20);
- break;
- case SEC_VOLTAGE_OFF:
- cx_clear(MO_GP0_IO, 0x20);
- break;
- }
-
- if (core->prev_set_voltage)
- return core->prev_set_voltage(fe, voltage);
- return 0;
-}
-
-static int vp1027_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- switch (voltage) {
- case SEC_VOLTAGE_13:
- dprintk(1, "LNB SEC Voltage=13\n");
- cx_write(MO_GP0_IO, 0x00001220);
- break;
- case SEC_VOLTAGE_18:
- dprintk(1, "LNB SEC Voltage=18\n");
- cx_write(MO_GP0_IO, 0x00001222);
- break;
- case SEC_VOLTAGE_OFF:
- dprintk(1, "LNB Voltage OFF\n");
- cx_write(MO_GP0_IO, 0x00001230);
- break;
- }
-
- if (core->prev_set_voltage)
- return core->prev_set_voltage(fe, voltage);
- return 0;
-}
-
-static const struct cx24123_config geniatech_dvbs_config = {
- .demod_address = 0x55,
- .set_ts_params = cx24123_set_ts_param,
-};
-
-static const struct cx24123_config hauppauge_novas_config = {
- .demod_address = 0x55,
- .set_ts_params = cx24123_set_ts_param,
-};
-
-static const struct cx24123_config kworld_dvbs_100_config = {
- .demod_address = 0x15,
- .set_ts_params = cx24123_set_ts_param,
- .lnb_polarity = 1,
-};
-
-static const struct s5h1409_config pinnacle_pctv_hd_800i_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_PARALLEL_OUTPUT,
- .gpio = S5H1409_GPIO_ON,
- .qam_if = 44000,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK,
-};
-
-static const struct s5h1409_config dvico_hdtv5_pci_nano_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static const struct s5h1409_config kworld_atsc_120_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_SERIAL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = {
- .i2c_address = 0x64,
- .if_khz = 5380,
-};
-
-static const struct zl10353_config cx88_pinnacle_hybrid_pctv = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .if2 = 45600,
-};
-
-static const struct zl10353_config cx88_geniatech_x8000_mt = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static const struct s5h1411_config dvico_fusionhdtv7_config = {
- .output_mode = S5H1411_SERIAL_OUTPUT,
- .gpio = S5H1411_GPIO_ON,
- .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
- .qam_if = S5H1411_IF_44000,
- .vsb_if = S5H1411_IF_44000,
- .inversion = S5H1411_INVERSION_OFF,
- .status_mode = S5H1411_DEMODLOCKING
-};
-
-static const struct xc5000_config dvico_fusionhdtv7_tuner_config = {
- .i2c_address = 0xc2 >> 1,
- .if_khz = 5380,
-};
-
-static int attach_xc3028(u8 addr, struct cx8802_dev *dev)
-{
- struct dvb_frontend *fe;
- struct videobuf_dvb_frontend *fe0 = NULL;
- struct xc2028_ctrl ctl;
- struct xc2028_config cfg = {
- .i2c_adap = &dev->core->i2c_adap,
- .i2c_addr = addr,
- .ctrl = &ctl,
- };
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
- if (!fe0->dvb.frontend) {
- printk(KERN_ERR "%s/2: dvb frontend not attached. "
- "Can't attach xc3028\n",
- dev->core->name);
- return -EINVAL;
- }
-
- /*
- * Some xc3028 devices may be hidden by an I2C gate. This is known
- * to happen with some s5h1409-based devices.
- * Now that I2C gate is open, sets up xc3028 configuration
- */
- cx88_setup_xc3028(dev->core, &ctl);
-
- fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
- if (!fe) {
- printk(KERN_ERR "%s/2: xc3028 attach failed\n",
- dev->core->name);
- dvb_frontend_detach(fe0->dvb.frontend);
- dvb_unregister_frontend(fe0->dvb.frontend);
- fe0->dvb.frontend = NULL;
- return -EINVAL;
- }
-
- printk(KERN_INFO "%s/2: xc3028 attached\n",
- dev->core->name);
-
- return 0;
-}
-
-static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg)
-{
- struct dvb_frontend *fe;
- struct videobuf_dvb_frontend *fe0 = NULL;
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
- if (!fe0->dvb.frontend) {
- printk(KERN_ERR "%s/2: dvb frontend not attached. "
- "Can't attach xc4000\n",
- dev->core->name);
- return -EINVAL;
- }
-
- fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap,
- cfg);
- if (!fe) {
- printk(KERN_ERR "%s/2: xc4000 attach failed\n",
- dev->core->name);
- dvb_frontend_detach(fe0->dvb.frontend);
- dvb_unregister_frontend(fe0->dvb.frontend);
- fe0->dvb.frontend = NULL;
- return -EINVAL;
- }
-
- printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name);
-
- return 0;
-}
-
-static int cx24116_set_ts_param(struct dvb_frontend *fe,
- int is_punctured)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- dev->ts_gen_cntrl = 0x2;
-
- return 0;
-}
-
-static int stv0900_set_ts_param(struct dvb_frontend *fe,
- int is_punctured)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- dev->ts_gen_cntrl = 0;
-
- return 0;
-}
-
-static int cx24116_reset_device(struct dvb_frontend *fe)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- /* Reset the part */
- /* Put the cx24116 into reset */
- cx_write(MO_SRST_IO, 0);
- msleep(10);
- /* Take the cx24116 out of reset */
- cx_write(MO_SRST_IO, 1);
- msleep(10);
-
- return 0;
-}
-
-static const struct cx24116_config hauppauge_hvr4000_config = {
- .demod_address = 0x05,
- .set_ts_params = cx24116_set_ts_param,
- .reset_device = cx24116_reset_device,
-};
-
-static const struct cx24116_config tevii_s460_config = {
- .demod_address = 0x55,
- .set_ts_params = cx24116_set_ts_param,
- .reset_device = cx24116_reset_device,
-};
-
-static int ds3000_set_ts_param(struct dvb_frontend *fe,
- int is_punctured)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- dev->ts_gen_cntrl = 4;
-
- return 0;
-}
-
-static struct ds3000_config tevii_ds3000_config = {
- .demod_address = 0x68,
- .set_ts_params = ds3000_set_ts_param,
-};
-
-static const struct stv0900_config prof_7301_stv0900_config = {
- .demod_address = 0x6a,
-/* demod_mode = 0,*/
- .xtal = 27000000,
- .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
- .diseqc_mode = 2,/* 2/3 PWM */
- .tun1_maddress = 0,/* 0x60 */
- .tun1_adc = 0,/* 2 Vpp */
- .path1_mode = 3,
- .set_ts_params = stv0900_set_ts_param,
-};
-
-static const struct stb6100_config prof_7301_stb6100_config = {
- .tuner_address = 0x60,
- .refclock = 27000000,
-};
-
-static const struct stv0299_config tevii_tuner_sharp_config = {
- .demod_address = 0x68,
- .inittab = sharp_z0194a_inittab,
- .mclk = 88000000UL,
- .invert = 1,
- .skip_reinit = 0,
- .lock_output = 1,
- .volt13_op0_op1 = STV0299_VOLT13_OP1,
- .min_delay_ms = 100,
- .set_symbol_rate = sharp_z0194a_set_symbol_rate,
- .set_ts_params = cx24116_set_ts_param,
-};
-
-static const struct stv0288_config tevii_tuner_earda_config = {
- .demod_address = 0x68,
- .min_delay_ms = 100,
- .set_ts_params = cx24116_set_ts_param,
-};
-
-static int cx8802_alloc_frontends(struct cx8802_dev *dev)
-{
- struct cx88_core *core = dev->core;
- struct videobuf_dvb_frontend *fe = NULL;
- int i;
-
- mutex_init(&dev->frontends.lock);
- INIT_LIST_HEAD(&dev->frontends.felist);
-
- if (!core->board.num_frontends)
- return -ENODEV;
-
- printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__,
- core->board.num_frontends);
- for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_alloc_frontend(&dev->frontends, i);
- if (!fe) {
- printk(KERN_ERR "%s() failed to alloc\n", __func__);
- videobuf_dvb_dealloc_frontends(&dev->frontends);
- return -ENOMEM;
- }
- }
- return 0;
-}
-
-
-
-static const u8 samsung_smt_7020_inittab[] = {
- 0x01, 0x15,
- 0x02, 0x00,
- 0x03, 0x00,
- 0x04, 0x7D,
- 0x05, 0x0F,
- 0x06, 0x02,
- 0x07, 0x00,
- 0x08, 0x60,
-
- 0x0A, 0xC2,
- 0x0B, 0x00,
- 0x0C, 0x01,
- 0x0D, 0x81,
- 0x0E, 0x44,
- 0x0F, 0x09,
- 0x10, 0x3C,
- 0x11, 0x84,
- 0x12, 0xDA,
- 0x13, 0x99,
- 0x14, 0x8D,
- 0x15, 0xCE,
- 0x16, 0xE8,
- 0x17, 0x43,
- 0x18, 0x1C,
- 0x19, 0x1B,
- 0x1A, 0x1D,
-
- 0x1C, 0x12,
- 0x1D, 0x00,
- 0x1E, 0x00,
- 0x1F, 0x00,
- 0x20, 0x00,
- 0x21, 0x00,
- 0x22, 0x00,
- 0x23, 0x00,
-
- 0x28, 0x02,
- 0x29, 0x28,
- 0x2A, 0x14,
- 0x2B, 0x0F,
- 0x2C, 0x09,
- 0x2D, 0x05,
-
- 0x31, 0x1F,
- 0x32, 0x19,
- 0x33, 0xFC,
- 0x34, 0x13,
- 0xff, 0xff,
-};
-
-
-static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct cx8802_dev *dev = fe->dvb->priv;
- u8 buf[4];
- u32 div;
- struct i2c_msg msg = {
- .addr = 0x61,
- .flags = 0,
- .buf = buf,
- .len = sizeof(buf) };
-
- div = c->frequency / 125;
-
- buf[0] = (div >> 8) & 0x7f;
- buf[1] = div & 0xff;
- buf[2] = 0x84; /* 0xC4 */
- buf[3] = 0x00;
-
- if (c->frequency < 1500000)
- buf[3] |= 0x10;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
-
- if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1)
- return -EIO;
-
- return 0;
-}
-
-static int samsung_smt_7020_set_tone(struct dvb_frontend *fe,
- fe_sec_tone_mode_t tone)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- cx_set(MO_GP0_IO, 0x0800);
-
- switch (tone) {
- case SEC_TONE_ON:
- cx_set(MO_GP0_IO, 0x08);
- break;
- case SEC_TONE_OFF:
- cx_clear(MO_GP0_IO, 0x08);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe,
- fe_sec_voltage_t voltage)
-{
- struct cx8802_dev *dev = fe->dvb->priv;
- struct cx88_core *core = dev->core;
-
- u8 data;
- struct i2c_msg msg = {
- .addr = 8,
- .flags = 0,
- .buf = &data,
- .len = sizeof(data) };
-
- cx_set(MO_GP0_IO, 0x8000);
-
- switch (voltage) {
- case SEC_VOLTAGE_OFF:
- break;
- case SEC_VOLTAGE_13:
- data = ISL6421_EN1 | ISL6421_LLC1;
- cx_clear(MO_GP0_IO, 0x80);
- break;
- case SEC_VOLTAGE_18:
- data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1;
- cx_clear(MO_GP0_IO, 0x80);
- break;
- default:
- return -EINVAL;
- };
-
- return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO;
-}
-
-static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe,
- u32 srate, u32 ratio)
-{
- u8 aclk = 0;
- u8 bclk = 0;
-
- if (srate < 1500000) {
- aclk = 0xb7;
- bclk = 0x47;
- } else if (srate < 3000000) {
- aclk = 0xb7;
- bclk = 0x4b;
- } else if (srate < 7000000) {
- aclk = 0xb7;
- bclk = 0x4f;
- } else if (srate < 14000000) {
- aclk = 0xb7;
- bclk = 0x53;
- } else if (srate < 30000000) {
- aclk = 0xb6;
- bclk = 0x53;
- } else if (srate < 45000000) {
- aclk = 0xb4;
- bclk = 0x51;
- }
-
- stv0299_writereg(fe, 0x13, aclk);
- stv0299_writereg(fe, 0x14, bclk);
- stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
- stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
- stv0299_writereg(fe, 0x21, ratio & 0xf0);
-
- return 0;
-}
-
-
-static const struct stv0299_config samsung_stv0299_config = {
- .demod_address = 0x68,
- .inittab = samsung_smt_7020_inittab,
- .mclk = 88000000UL,
- .invert = 0,
- .skip_reinit = 0,
- .lock_output = STV0299_LOCKOUTPUT_LK,
- .volt13_op0_op1 = STV0299_VOLT13_OP1,
- .min_delay_ms = 100,
- .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate,
-};
-
-static int dvb_register(struct cx8802_dev *dev)
-{
- struct cx88_core *core = dev->core;
- struct videobuf_dvb_frontend *fe0, *fe1 = NULL;
- int mfe_shared = 0; /* bus not shared by default */
- int res = -EINVAL;
-
- if (0 != core->i2c_rc) {
- printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name);
- goto frontend_detach;
- }
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- goto frontend_detach;
-
- /* multi-frontend gate control is undefined or defaults to fe0 */
- dev->frontends.gate = 0;
-
- /* Sets the gate control callback to be used by i2c command calls */
- core->gate_ctrl = cx88_dvb_gate_ctrl;
-
- /* init frontend(s) */
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_DVB_T1:
- fe0->dvb.frontend = dvb_attach(cx22702_attach,
- &connexant_refboard_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x61, &core->i2c_adap,
- DVB_PLL_THOMSON_DTT759X))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
- case CX88_BOARD_CONEXANT_DVB_T1:
- case CX88_BOARD_KWORLD_DVB_T_CX22702:
- case CX88_BOARD_WINFAST_DTV1000:
- fe0->dvb.frontend = dvb_attach(cx22702_attach,
- &connexant_refboard_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x60, &core->i2c_adap,
- DVB_PLL_THOMSON_DTT7579))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_WINFAST_DTV2000H:
- case CX88_BOARD_HAUPPAUGE_HVR1100:
- case CX88_BOARD_HAUPPAUGE_HVR1100LP:
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- fe0->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_WINFAST_DTV2000H_J:
- fe0->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216MEX_MK3))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- /* MFE frontend 1 */
- mfe_shared = 1;
- dev->frontends.gate = 2;
- /* DVB-S init */
- fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &hauppauge_novas_config,
- &dev->core->i2c_adap);
- if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach,
- fe0->dvb.frontend,
- &dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
- goto frontend_detach;
- }
- /* MFE frontend 2 */
- fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (!fe1)
- goto frontend_detach;
- /* DVB-T init */
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if (!dvb_attach(simple_tuner_attach,
- fe1->dvb.frontend,
- &dev->core->i2c_adap,
- 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &dvico_fusionhdtv,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
- goto frontend_detach;
- break;
- }
- /* ZL10353 replaces MT352 on later cards */
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_plus_v1_1,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x60, NULL, DVB_PLL_THOMSON_DTT7579))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
- /* The tin box says DEE1601, but it seems to be DTT7579
- * compatible, with a slightly different MT352 AGC gain. */
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &dvico_fusionhdtv_dual,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
- goto frontend_detach;
- break;
- }
- /* ZL10353 replaces MT352 on later cards */
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_plus_v1_1,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x61, NULL, DVB_PLL_THOMSON_DTT7579))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &dvico_fusionhdtv,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x61, NULL, DVB_PLL_LG_Z201))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_KWORLD_DVB_T:
- case CX88_BOARD_DNTV_LIVE_DVB_T:
- case CX88_BOARD_ADSTECH_DVB_T_PCI:
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &dntv_live_dvbt_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend,
- 0x61, NULL, DVB_PLL_UNKNOWN_1))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
-#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
- /* MT352 is on a secondary I2C bus made from some GPIO lines */
- fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
- &dev->vp3054->adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3))
- goto frontend_detach;
- }
-#else
- printk(KERN_ERR "%s/2: built without vp3054 support\n",
- core->name);
-#endif
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_hybrid,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_THOMSON_FE6600))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &dvico_fusionhdtv_xc3028,
- &core->i2c_adap);
- if (fe0->dvb.frontend == NULL)
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &dvico_fusionhdtv_mt352_xc3028,
- &core->i2c_adap);
- /*
- * On this board, the demod provides the I2C bus pullup.
- * We must not permit gate_ctrl to be performed, or
- * the xc3028 cannot communicate on the bus.
- */
- if (fe0->dvb.frontend)
- fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
- break;
- case CX88_BOARD_PCHDTV_HD3000:
- fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_THOMSON_DTT761X))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
- dev->ts_gen_cntrl = 0x08;
-
- /* Do a hardware reset of chip before using it. */
- cx_clear(MO_GP0_IO, 1);
- mdelay(100);
- cx_set(MO_GP0_IO, 1);
- mdelay(200);
-
- /* Select RF connector callback */
- fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
- fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &fusionhdtv_3_gold,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_MICROTUNE_4042FI5))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T:
- dev->ts_gen_cntrl = 0x08;
-
- /* Do a hardware reset of chip before using it. */
- cx_clear(MO_GP0_IO, 1);
- mdelay(100);
- cx_set(MO_GP0_IO, 9);
- mdelay(200);
- fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &fusionhdtv_3_gold,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_THOMSON_DTT761X))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
- dev->ts_gen_cntrl = 0x08;
-
- /* Do a hardware reset of chip before using it. */
- cx_clear(MO_GP0_IO, 1);
- mdelay(100);
- cx_set(MO_GP0_IO, 1);
- mdelay(200);
- fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &fusionhdtv_5_gold,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_LG_TDVS_H06XF))
- goto frontend_detach;
- if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x43))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_PCHDTV_HD5500:
- dev->ts_gen_cntrl = 0x08;
-
- /* Do a hardware reset of chip before using it. */
- cx_clear(MO_GP0_IO, 1);
- mdelay(100);
- cx_set(MO_GP0_IO, 1);
- mdelay(200);
- fe0->dvb.frontend = dvb_attach(lgdt330x_attach,
- &pchdtv_hd5500,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_LG_TDVS_H06XF))
- goto frontend_detach;
- if (!dvb_attach(tda9887_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x43))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_ATI_HDTVWONDER:
- fe0->dvb.frontend = dvb_attach(nxt200x_attach,
- &ati_hdtvwonder,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x61,
- TUNER_PHILIPS_TUV1236D))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
- case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
- fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &hauppauge_novas_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &core->i2c_adap, 0x08, ISL6421_DCL, 0x00))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_KWORLD_DVBS_100:
- fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &kworld_dvbs_100_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
- }
- break;
- case CX88_BOARD_GENIATECH_DVBS:
- fe0->dvb.frontend = dvb_attach(cx24123_attach,
- &geniatech_dvbs_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
- }
- break;
- case CX88_BOARD_PINNACLE_PCTV_HD_800i:
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &pinnacle_pctv_hd_800i_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &core->i2c_adap,
- &pinnacle_pctv_hd_800i_tuner_config))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &dvico_hdtv5_pci_nano_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &core->i2c_adap,
- .i2c_addr = 0x61,
- };
- static struct xc2028_ctrl ctl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- .scode_table = XC3028_FE_OREN538,
- };
-
- fe = dvb_attach(xc2028_attach,
- fe0->dvb.frontend, &cfg);
- if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
- fe->ops.tuner_ops.set_config(fe, &ctl);
- }
- break;
- case CX88_BOARD_PINNACLE_HYBRID_PCTV:
- case CX88_BOARD_WINFAST_DTV1800H:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_pinnacle_hybrid_pctv,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_WINFAST_DTV1800H_XC4000:
- case CX88_BOARD_WINFAST_DTV2000H_PLUS:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_pinnacle_hybrid_pctv,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- struct xc4000_config cfg = {
- .i2c_address = 0x61,
- .default_pm = 0,
- .dvb_amplitude = 134,
- .set_smoothedcvbs = 1,
- .if_khz = 4560
- };
- fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
- if (attach_xc4000(dev, &cfg) < 0)
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_GENIATECH_X8000_MT:
- dev->ts_gen_cntrl = 0x00;
-
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_geniatech_x8000_mt,
- &core->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
- break;
- case CX88_BOARD_KWORLD_ATSC_120:
- fe0->dvb.frontend = dvb_attach(s5h1409_attach,
- &kworld_atsc_120_config,
- &core->i2c_adap);
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
- break;
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &dvico_fusionhdtv7_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &core->i2c_adap,
- &dvico_fusionhdtv7_tuner_config))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_HAUPPAUGE_HVR4000:
- /* MFE frontend 1 */
- mfe_shared = 1;
- dev->frontends.gate = 2;
- /* DVB-S/S2 Init */
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
- if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach,
- fe0->dvb.frontend,
- &dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
- goto frontend_detach;
- }
- /* MFE frontend 2 */
- fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2);
- if (!fe1)
- goto frontend_detach;
- /* DVB-T Init */
- fe1->dvb.frontend = dvb_attach(cx22702_attach,
- &hauppauge_hvr_config,
- &dev->core->i2c_adap);
- if (fe1->dvb.frontend) {
- fe1->dvb.frontend->id = 1;
- if (!dvb_attach(simple_tuner_attach,
- fe1->dvb.frontend,
- &dev->core->i2c_adap,
- 0x61, TUNER_PHILIPS_FMD1216ME_MK3))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &dev->core->i2c_adap);
- if (fe0->dvb.frontend) {
- if (!dvb_attach(isl6421_attach,
- fe0->dvb.frontend,
- &dev->core->i2c_adap,
- 0x08, ISL6421_DCL, 0x00))
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_PROF_6200:
- case CX88_BOARD_TBS_8910:
- case CX88_BOARD_TEVII_S420:
- fe0->dvb.frontend = dvb_attach(stv0299_attach,
- &tevii_tuner_sharp_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
- &core->i2c_adap, DVB_PLL_OPERA1))
- goto frontend_detach;
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
-
- } else {
- fe0->dvb.frontend = dvb_attach(stv0288_attach,
- &tevii_tuner_earda_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61,
- &core->i2c_adap))
- goto frontend_detach;
- core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- }
- }
- break;
- case CX88_BOARD_TEVII_S460:
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &tevii_s460_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- break;
- case CX88_BOARD_TEVII_S464:
- fe0->dvb.frontend = dvb_attach(ds3000_attach,
- &tevii_ds3000_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage =
- tevii_dvbs_set_voltage;
- break;
- case CX88_BOARD_OMICOM_SS4_PCI:
- case CX88_BOARD_TBS_8920:
- case CX88_BOARD_PROF_7300:
- case CX88_BOARD_SATTRADE_ST4200:
- fe0->dvb.frontend = dvb_attach(cx24116_attach,
- &hauppauge_hvr4000_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend != NULL)
- fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
- break;
- case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &cx88_terratec_cinergy_ht_pci_mkii_config,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
- if (attach_xc3028(0x61, dev) < 0)
- goto frontend_detach;
- }
- break;
- case CX88_BOARD_PROF_7301:{
- struct dvb_tuner_ops *tuner_ops = NULL;
-
- fe0->dvb.frontend = dvb_attach(stv0900_attach,
- &prof_7301_stv0900_config,
- &core->i2c_adap, 0);
- if (fe0->dvb.frontend != NULL) {
- if (!dvb_attach(stb6100_attach, fe0->dvb.frontend,
- &prof_7301_stb6100_config,
- &core->i2c_adap))
- goto frontend_detach;
-
- tuner_ops = &fe0->dvb.frontend->ops.tuner_ops;
- tuner_ops->set_frequency = stb6100_set_freq;
- tuner_ops->get_frequency = stb6100_get_freq;
- tuner_ops->set_bandwidth = stb6100_set_bandw;
- tuner_ops->get_bandwidth = stb6100_get_bandw;
-
- core->prev_set_voltage =
- fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage =
- tevii_dvbs_set_voltage;
- }
- break;
- }
- case CX88_BOARD_SAMSUNG_SMT_7020:
- dev->ts_gen_cntrl = 0x08;
-
- cx_set(MO_GP0_IO, 0x0101);
-
- cx_clear(MO_GP0_IO, 0x01);
- mdelay(100);
- cx_set(MO_GP0_IO, 0x01);
- mdelay(200);
-
- fe0->dvb.frontend = dvb_attach(stv0299_attach,
- &samsung_stv0299_config,
- &dev->core->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.tuner_ops.set_params =
- samsung_smt_7020_tuner_set_params;
- fe0->dvb.frontend->tuner_priv =
- &dev->core->i2c_adap;
- fe0->dvb.frontend->ops.set_voltage =
- samsung_smt_7020_set_voltage;
- fe0->dvb.frontend->ops.set_tone =
- samsung_smt_7020_set_tone;
- }
-
- break;
- case CX88_BOARD_TWINHAN_VP1027_DVBS:
- dev->ts_gen_cntrl = 0x00;
- fe0->dvb.frontend = dvb_attach(mb86a16_attach,
- &twinhan_vp1027,
- &core->i2c_adap);
- if (fe0->dvb.frontend) {
- core->prev_set_voltage =
- fe0->dvb.frontend->ops.set_voltage;
- fe0->dvb.frontend->ops.set_voltage =
- vp1027_set_voltage;
- }
- break;
-
- default:
- printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
- core->name);
- break;
- }
-
- if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) {
- printk(KERN_ERR
- "%s/2: frontend initialization failed\n",
- core->name);
- goto frontend_detach;
- }
- /* define general-purpose callback pointer */
- fe0->dvb.frontend->callback = cx88_tuner_callback;
-
- /* Ensure all frontends negotiate bus access */
- fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
- if (fe1)
- fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
-
- /* Put the analog decoder in standby to keep it quiet */
- call_all(core, core, s_power, 0);
-
- /* register everything */
- res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, mfe_shared, NULL);
- if (res)
- goto frontend_detach;
- return res;
-
-frontend_detach:
- core->gate_ctrl = NULL;
- videobuf_dvb_dealloc_frontends(&dev->frontends);
- return res;
-}
-
-/* ----------------------------------------------------------- */
-
-/* CX8802 MPEG -> mini driver - We have been given the hardware */
-static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- int err = 0;
- dprintk( 1, "%s\n", __func__);
-
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* We arrive here with either the cx23416 or the cx22702
- * on the bus. Take the bus from the cx23416 and enable the
- * cx22702 demod
- */
- /* Toggle reset on cx22702 leaving i2c active */
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- /* enable the cx22702 pins */
- cx_clear(MO_GP0_IO, 0x00000004);
- udelay(1000);
- break;
-
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- case CX88_BOARD_HAUPPAUGE_HVR4000:
- /* Toggle reset on cx22702 leaving i2c active */
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080);
- udelay(1000);
- switch (core->dvbdev->frontends.active_fe_id) {
- case 1: /* DVB-S/S2 Enabled */
- /* tri-state the cx22702 pins */
- cx_set(MO_GP0_IO, 0x00000004);
- /* Take the cx24116/cx24123 out of reset */
- cx_write(MO_SRST_IO, 1);
- core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
- break;
- case 2: /* DVB-T Enabled */
- /* Put the cx24116/cx24123 into reset */
- cx_write(MO_SRST_IO, 0);
- /* enable the cx22702 pins */
- cx_clear(MO_GP0_IO, 0x00000004);
- core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
- break;
- }
- udelay(1000);
- break;
-
- case CX88_BOARD_WINFAST_DTV2000H_PLUS:
- /* set RF input to AIR for DVB-T (GPIO 16) */
- cx_write(MO_GP2_IO, 0x0101);
- break;
-
- default:
- err = -ENODEV;
- }
- return err;
-}
-
-/* CX8802 MPEG -> mini driver - We no longer have the hardware */
-static int cx8802_dvb_advise_release(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- int err = 0;
- dprintk( 1, "%s\n", __func__);
-
- switch (core->boardnr) {
- case CX88_BOARD_HAUPPAUGE_HVR1300:
- /* Do Nothing, leave the cx22702 on the bus. */
- break;
- case CX88_BOARD_HAUPPAUGE_HVR3000:
- case CX88_BOARD_HAUPPAUGE_HVR4000:
- break;
- default:
- err = -ENODEV;
- }
- return err;
-}
-
-static int cx8802_dvb_probe(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- struct cx8802_dev *dev = drv->core->dvbdev;
- int err;
- struct videobuf_dvb_frontend *fe;
- int i;
-
- dprintk( 1, "%s\n", __func__);
- dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
- core->boardnr,
- core->name,
- core->pci_bus,
- core->pci_slot);
-
- err = -ENODEV;
- if (!(core->board.mpeg & CX88_MPEG_DVB))
- goto fail_core;
-
- /* If vp3054 isn't enabled, a stub will just return 0 */
- err = vp3054_i2c_probe(dev);
- if (0 != err)
- goto fail_core;
-
- /* dvb stuff */
- printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name);
- dev->ts_gen_cntrl = 0x0c;
-
- err = cx8802_alloc_frontends(dev);
- if (err)
- goto fail_core;
-
- err = -ENODEV;
- for (i = 1; i <= core->board.num_frontends; i++) {
- fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i);
- if (fe == NULL) {
- printk(KERN_ERR "%s() failed to get frontend(%d)\n",
- __func__, i);
- goto fail_probe;
- }
- videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_TOP,
- sizeof(struct cx88_buffer),
- dev, NULL);
- /* init struct videobuf_dvb */
- fe->dvb.name = dev->core->name;
- }
-
- err = dvb_register(dev);
- if (err)
- /* frontends/adapter de-allocated in dvb_register */
- printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n",
- core->name, err);
- return err;
-fail_probe:
- videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends);
-fail_core:
- return err;
-}
-
-static int cx8802_dvb_remove(struct cx8802_driver *drv)
-{
- struct cx88_core *core = drv->core;
- struct cx8802_dev *dev = drv->core->dvbdev;
-
- dprintk( 1, "%s\n", __func__);
-
- videobuf_dvb_unregister_bus(&dev->frontends);
-
- vp3054_i2c_remove(dev);
-
- core->gate_ctrl = NULL;
-
- return 0;
-}
-
-static struct cx8802_driver cx8802_dvb_driver = {
- .type_id = CX88_MPEG_DVB,
- .hw_access = CX8802_DRVCTL_SHARED,
- .probe = cx8802_dvb_probe,
- .remove = cx8802_dvb_remove,
- .advise_acquire = cx8802_dvb_advise_acquire,
- .advise_release = cx8802_dvb_advise_release,
-};
-
-static int __init dvb_init(void)
-{
- printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n",
- CX88_VERSION);
- return cx8802_register_driver(&cx8802_dvb_driver);
-}
-
-static void __exit dvb_fini(void)
-{
- cx8802_unregister_driver(&cx8802_dvb_driver);
-}
-
-module_init(dvb_init);
-module_exit(dvb_fini);
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
deleted file mode 100644
index f6fcc7e763ab..000000000000
--- a/drivers/media/video/cx88/cx88-video.c
+++ /dev/null
@@ -1,2075 +0,0 @@
-/*
- *
- * device driver for Conexant 2388x based TV cards
- * video4linux video interface
- *
- * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
- * - Multituner support
- * - video_ioctl2 conversion
- * - PAL/M fixes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <asm/div64.h>
-
-#include "cx88.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-event.h>
-#include <media/wm8775.h>
-
-MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(CX88_VERSION);
-
-/* ------------------------------------------------------------------ */
-
-static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(vbi_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-
-MODULE_PARM_DESC(video_nr,"video device numbers");
-MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
-MODULE_PARM_DESC(radio_nr,"radio device numbers");
-
-static unsigned int video_debug;
-module_param(video_debug,int,0644);
-MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
-
-static unsigned int irq_debug;
-module_param(irq_debug,int,0644);
-MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
-
-static unsigned int vid_limit = 16;
-module_param(vid_limit,int,0644);
-MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
-
-#define dprintk(level,fmt, arg...) if (video_debug >= level) \
- printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg)
-
-/* ------------------------------------------------------------------- */
-/* static data */
-
-static const struct cx8800_fmt formats[] = {
- {
- .name = "8 bpp, gray",
- .fourcc = V4L2_PIX_FMT_GREY,
- .cxformat = ColorFormatY8,
- .depth = 8,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "15 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .cxformat = ColorFormatRGB15,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "15 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "16 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .cxformat = ColorFormatRGB16,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "16 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "24 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .cxformat = ColorFormatRGB24,
- .depth = 24,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "32 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .cxformat = ColorFormatRGB32,
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "32 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP,
- .depth = 32,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .cxformat = ColorFormatYUY2,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },{
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
- .depth = 16,
- .flags = FORMAT_FLAGS_PACKED,
- },
-};
-
-static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(formats); i++)
- if (formats[i].fourcc == fourcc)
- return formats+i;
- return NULL;
-}
-
-/* ------------------------------------------------------------------- */
-
-struct cx88_ctrl {
- /* control information */
- u32 id;
- s32 minimum;
- s32 maximum;
- u32 step;
- s32 default_value;
-
- /* control register information */
- u32 off;
- u32 reg;
- u32 sreg;
- u32 mask;
- u32 shift;
-};
-
-static const struct cx88_ctrl cx8800_vid_ctls[] = {
- /* --- video --- */
- {
- .id = V4L2_CID_BRIGHTNESS,
- .minimum = 0x00,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .off = 128,
- .reg = MO_CONTR_BRIGHT,
- .mask = 0x00ff,
- .shift = 0,
- },{
- .id = V4L2_CID_CONTRAST,
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x3f,
- .off = 0,
- .reg = MO_CONTR_BRIGHT,
- .mask = 0xff00,
- .shift = 8,
- },{
- .id = V4L2_CID_HUE,
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .off = 128,
- .reg = MO_HUE,
- .mask = 0x00ff,
- .shift = 0,
- },{
- /* strictly, this only describes only U saturation.
- * V saturation is handled specially through code.
- */
- .id = V4L2_CID_SATURATION,
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f,
- .off = 0,
- .reg = MO_UV_SATURATION,
- .mask = 0x00ff,
- .shift = 0,
- }, {
- .id = V4L2_CID_SHARPNESS,
- .minimum = 0,
- .maximum = 4,
- .step = 1,
- .default_value = 0x0,
- .off = 0,
- /* NOTE: the value is converted and written to both even
- and odd registers in the code */
- .reg = MO_FILTER_ODD,
- .mask = 7 << 7,
- .shift = 7,
- }, {
- .id = V4L2_CID_CHROMA_AGC,
- .minimum = 0,
- .maximum = 1,
- .default_value = 0x1,
- .reg = MO_INPUT_FORMAT,
- .mask = 1 << 10,
- .shift = 10,
- }, {
- .id = V4L2_CID_COLOR_KILLER,
- .minimum = 0,
- .maximum = 1,
- .default_value = 0x1,
- .reg = MO_INPUT_FORMAT,
- .mask = 1 << 9,
- .shift = 9,
- }, {
- .id = V4L2_CID_BAND_STOP_FILTER,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0x0,
- .off = 0,
- .reg = MO_HTOTAL,
- .mask = 3 << 11,
- .shift = 11,
- }
-};
-
-static const struct cx88_ctrl cx8800_aud_ctls[] = {
- {
- /* --- audio --- */
- .id = V4L2_CID_AUDIO_MUTE,
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .reg = AUD_VOL_CTL,
- .sreg = SHADOW_AUD_VOL_CTL,
- .mask = (1 << 6),
- .shift = 6,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- .default_value = 0x3f,
- .reg = AUD_VOL_CTL,
- .sreg = SHADOW_AUD_VOL_CTL,
- .mask = 0x3f,
- .shift = 0,
- },{
- .id = V4L2_CID_AUDIO_BALANCE,
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x40,
- .reg = AUD_BAL_CTL,
- .sreg = SHADOW_AUD_BAL_CTL,
- .mask = 0x7f,
- .shift = 0,
- }
-};
-
-enum {
- CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
- CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
-};
-
-/* ------------------------------------------------------------------- */
-/* resource management */
-
-static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit)
-{
- struct cx88_core *core = dev->core;
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&core->lock);
- if (dev->resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&core->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- dprintk(1,"res: get %d\n",bit);
- mutex_unlock(&core->lock);
- return 1;
-}
-
-static
-int res_check(struct cx8800_fh *fh, unsigned int bit)
-{
- return (fh->resources & bit);
-}
-
-static
-int res_locked(struct cx8800_dev *dev, unsigned int bit)
-{
- return (dev->resources & bit);
-}
-
-static
-void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
-{
- struct cx88_core *core = dev->core;
- BUG_ON((fh->resources & bits) != bits);
-
- mutex_lock(&core->lock);
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- dprintk(1,"res: put %d\n",bits);
- mutex_unlock(&core->lock);
-}
-
-/* ------------------------------------------------------------------ */
-
-int cx88_video_mux(struct cx88_core *core, unsigned int input)
-{
- /* struct cx88_core *core = dev->core; */
-
- dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
- input, INPUT(input).vmux,
- INPUT(input).gpio0,INPUT(input).gpio1,
- INPUT(input).gpio2,INPUT(input).gpio3);
- core->input = input;
- cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
- cx_write(MO_GP3_IO, INPUT(input).gpio3);
- cx_write(MO_GP0_IO, INPUT(input).gpio0);
- cx_write(MO_GP1_IO, INPUT(input).gpio1);
- cx_write(MO_GP2_IO, INPUT(input).gpio2);
-
- switch (INPUT(input).type) {
- case CX88_VMUX_SVIDEO:
- cx_set(MO_AFECFG_IO, 0x00000001);
- cx_set(MO_INPUT_FORMAT, 0x00010010);
- cx_set(MO_FILTER_EVEN, 0x00002020);
- cx_set(MO_FILTER_ODD, 0x00002020);
- break;
- default:
- cx_clear(MO_AFECFG_IO, 0x00000001);
- cx_clear(MO_INPUT_FORMAT, 0x00010010);
- cx_clear(MO_FILTER_EVEN, 0x00002020);
- cx_clear(MO_FILTER_ODD, 0x00002020);
- break;
- }
-
- /* if there are audioroutes defined, we have an external
- ADC to deal with audio */
- if (INPUT(input).audioroute) {
- /* The wm8775 module has the "2" route hardwired into
- the initialization. Some boards may use different
- routes for different inputs. HVR-1300 surely does */
- if (core->board.audio_chip &&
- core->board.audio_chip == V4L2_IDENT_WM8775) {
- call_all(core, audio, s_routing,
- INPUT(input).audioroute, 0, 0);
- }
- /* cx2388's C-ADC is connected to the tuner only.
- When used with S-Video, that ADC is busy dealing with
- chroma, so an external must be used for baseband audio */
- if (INPUT(input).type != CX88_VMUX_TELEVISION &&
- INPUT(input).type != CX88_VMUX_CABLE) {
- /* "I2S ADC mode" */
- core->tvaudio = WW_I2SADC;
- cx88_set_tvaudio(core);
- } else {
- /* Normal mode */
- cx_write(AUD_I2SCNTL, 0x0);
- cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL(cx88_video_mux);
-
-/* ------------------------------------------------------------------ */
-
-static int start_video_dma(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q,
- struct cx88_buffer *buf)
-{
- struct cx88_core *core = dev->core;
-
- /* setup fifo + format */
- cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
- buf->bpl, buf->risc.dma);
- cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field);
- cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
-
- /* reset counter */
- cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
- q->count = 1;
-
- /* enable irqs */
- cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
-
- /* Enables corresponding bits at PCI_INT_STAT:
- bits 0 to 4: video, audio, transport stream, VIP, Host
- bit 7: timer
- bits 8 and 9: DMA complete for: SRC, DST
- bits 10 and 11: BERR signal asserted for RISC: RD, WR
- bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
- */
- cx_set(MO_VID_INTMSK, 0x0f0011);
-
- /* enable capture */
- cx_set(VID_CAPTURE_CONTROL,0x06);
-
- /* start dma */
- cx_set(MO_DEV_CNTRL2, (1<<5));
- cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int stop_video_dma(struct cx8800_dev *dev)
-{
- struct cx88_core *core = dev->core;
-
- /* stop dma */
- cx_clear(MO_VID_DMACNTRL, 0x11);
-
- /* disable capture */
- cx_clear(VID_CAPTURE_CONTROL,0x06);
-
- /* disable irqs */
- cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
- cx_clear(MO_VID_INTMSK, 0x0f0011);
- return 0;
-}
-#endif
-
-static int restart_video_queue(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q)
-{
- struct cx88_core *core = dev->core;
- struct cx88_buffer *buf, *prev;
-
- if (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
- dprintk(2,"restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
- start_video_dma(dev, q, buf);
- list_for_each_entry(buf, &q->active, vb.queue)
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
- }
-
- prev = NULL;
- for (;;) {
- if (list_empty(&q->queued))
- return 0;
- buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
- if (NULL == prev) {
- list_move_tail(&buf->vb.queue, &q->active);
- start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2,"[%p/%d] restart_queue - first active\n",
- buf,buf->vb.i);
-
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_move_tail(&buf->vb.queue, &q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- dprintk(2,"[%p/%d] restart_queue - move to active\n",
- buf,buf->vb.i);
- } else {
- return 0;
- }
- prev = buf;
- }
-}
-
-/* ------------------------------------------------------------------ */
-
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- struct cx8800_fh *fh = q->priv_data;
- struct cx8800_dev *dev = fh->dev;
-
- *size = dev->fmt->depth * dev->width * dev->height >> 3;
- if (0 == *count)
- *count = 32;
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
- return 0;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct cx8800_fh *fh = q->priv_data;
- struct cx8800_dev *dev = fh->dev;
- struct cx88_core *core = dev->core;
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
- int rc, init_buffer = 0;
-
- BUG_ON(NULL == dev->fmt);
- if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) ||
- dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
- return -EINVAL;
- buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- if (buf->fmt != dev->fmt ||
- buf->vb.width != dev->width ||
- buf->vb.height != dev->height ||
- buf->vb.field != field) {
- buf->fmt = dev->fmt;
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
- init_buffer = 1;
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- init_buffer = 1;
- if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
- goto fail;
- }
-
- if (init_buffer) {
- buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
- switch (buf->vb.field) {
- case V4L2_FIELD_TOP:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, UNSET,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_BOTTOM:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, UNSET, 0,
- buf->bpl, 0, buf->vb.height);
- break;
- case V4L2_FIELD_INTERLACED:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist, 0, buf->bpl,
- buf->bpl, buf->bpl,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_TB:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- 0, buf->bpl * (buf->vb.height >> 1),
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- case V4L2_FIELD_SEQ_BT:
- cx88_risc_buffer(dev->pci, &buf->risc,
- dma->sglist,
- buf->bpl * (buf->vb.height >> 1), 0,
- buf->bpl, 0,
- buf->vb.height >> 1);
- break;
- default:
- BUG();
- }
- }
- dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
- buf, buf->vb.i,
- dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
- (unsigned long)buf->risc.dma);
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
- fail:
- cx88_free_buffer(q,buf);
- return rc;
-}
-
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
- struct cx88_buffer *prev;
- struct cx8800_fh *fh = vq->priv_data;
- struct cx8800_dev *dev = fh->dev;
- struct cx88_core *core = dev->core;
- struct cx88_dmaqueue *q = &dev->vidq;
-
- /* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
- buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
-
- if (!list_empty(&q->queued)) {
- list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - append to queued\n",
- buf, buf->vb.i);
-
- } else if (list_empty(&q->active)) {
- list_add_tail(&buf->vb.queue,&q->active);
- start_video_dma(dev, q, buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2,"[%p/%d] buffer_queue - first active\n",
- buf, buf->vb.i);
-
- } else {
- prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue);
- if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue,&q->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->count = q->count++;
- prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
- dprintk(2,"[%p/%d] buffer_queue - append to active\n",
- buf, buf->vb.i);
-
- } else {
- list_add_tail(&buf->vb.queue,&q->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2,"[%p/%d] buffer_queue - first queued\n",
- buf, buf->vb.i);
- }
- }
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb);
-
- cx88_free_buffer(q,buf);
-}
-
-static const struct videobuf_queue_ops cx8800_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-
-/* ------------------------------------------------------------------ */
-
-static struct videobuf_queue *get_queue(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return &fh->vidq;
- case VFL_TYPE_VBI:
- return &fh->vbiq;
- default:
- BUG();
- return NULL;
- }
-}
-
-static int get_resource(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- return RESOURCE_VIDEO;
- case VFL_TYPE_VBI:
- return RESOURCE_VBI;
- default:
- BUG();
- return 0;
- }
-}
-
-static int video_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_dev *dev = video_drvdata(file);
- struct cx88_core *core = dev->core;
- struct cx8800_fh *fh;
- enum v4l2_buf_type type = 0;
- int radio = 0;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- dprintk(1, "open dev=%s radio=%d type=%s\n",
- video_device_node_name(vdev), radio, v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (unlikely(!fh))
- return -ENOMEM;
-
- v4l2_fh_init(&fh->fh, vdev);
- file->private_data = fh;
- fh->dev = dev;
-
- mutex_lock(&core->lock);
-
- videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct cx88_buffer),
- fh, NULL);
- videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct cx88_buffer),
- fh, NULL);
-
- if (vdev->vfl_type == VFL_TYPE_RADIO) {
- dprintk(1,"video_open: setting radio device\n");
- cx_write(MO_GP3_IO, core->board.radio.gpio3);
- cx_write(MO_GP0_IO, core->board.radio.gpio0);
- cx_write(MO_GP1_IO, core->board.radio.gpio1);
- cx_write(MO_GP2_IO, core->board.radio.gpio2);
- if (core->board.radio.audioroute) {
- if(core->board.audio_chip &&
- core->board.audio_chip == V4L2_IDENT_WM8775) {
- call_all(core, audio, s_routing,
- core->board.radio.audioroute, 0, 0);
- }
- /* "I2S ADC mode" */
- core->tvaudio = WW_I2SADC;
- cx88_set_tvaudio(core);
- } else {
- /* FM Mode */
- core->tvaudio = WW_FM;
- cx88_set_tvaudio(core);
- cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
- }
- call_all(core, tuner, s_radio);
- }
-
- core->users++;
- mutex_unlock(&core->lock);
- v4l2_fh_add(&fh->fh);
-
- return 0;
-}
-
-static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- return -EBUSY;
- return videobuf_read_one(&fh->vidq, data, count, ppos,
- file->f_flags & O_NONBLOCK);
- case VFL_TYPE_VBI:
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return -EBUSY;
- return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
- default:
- BUG();
- return 0;
- }
-}
-
-static unsigned int
-video_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = file->private_data;
- struct cx88_buffer *buf;
- unsigned int rc = v4l2_ctrl_poll(file, wait);
-
- if (vdev->vfl_type == VFL_TYPE_VBI) {
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return rc | POLLERR;
- return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
- }
- mutex_lock(&fh->vidq.vb_lock);
- if (res_check(fh,RESOURCE_VIDEO)) {
- /* streaming capture */
- if (list_empty(&fh->vidq.stream))
- goto done;
- buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream);
- } else {
- /* read() capture */
- buf = (struct cx88_buffer*)fh->vidq.read_buf;
- if (NULL == buf)
- goto done;
- }
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- rc |= POLLIN|POLLRDNORM;
-done:
- mutex_unlock(&fh->vidq.vb_lock);
- return rc;
-}
-
-static int video_release(struct file *file)
-{
- struct cx8800_fh *fh = file->private_data;
- struct cx8800_dev *dev = fh->dev;
-
- /* turn off overlay */
- if (res_check(fh, RESOURCE_OVERLAY)) {
- /* FIXME */
- res_free(dev,fh,RESOURCE_OVERLAY);
- }
-
- /* stop video capture */
- if (res_check(fh, RESOURCE_VIDEO)) {
- videobuf_queue_cancel(&fh->vidq);
- res_free(dev,fh,RESOURCE_VIDEO);
- }
- if (fh->vidq.read_buf) {
- buffer_release(&fh->vidq,fh->vidq.read_buf);
- kfree(fh->vidq.read_buf);
- }
-
- /* stop vbi capture */
- if (res_check(fh, RESOURCE_VBI)) {
- videobuf_stop(&fh->vbiq);
- res_free(dev,fh,RESOURCE_VBI);
- }
-
- videobuf_mmap_free(&fh->vidq);
- videobuf_mmap_free(&fh->vbiq);
-
- mutex_lock(&dev->core->lock);
- v4l2_fh_del(&fh->fh);
- v4l2_fh_exit(&fh->fh);
- file->private_data = NULL;
- kfree(fh);
-
- dev->core->users--;
- if (!dev->core->users)
- call_all(dev->core, core, s_power, 0);
- mutex_unlock(&dev->core->lock);
-
- return 0;
-}
-
-static int
-video_mmap(struct file *file, struct vm_area_struct * vma)
-{
- return videobuf_mmap_mapper(get_queue(file), vma);
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO CTRL IOCTLS */
-
-static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct cx88_core *core =
- container_of(ctrl->handler, struct cx88_core, video_hdl);
- const struct cx88_ctrl *cc = ctrl->priv;
- u32 value, mask;
-
- mask = cc->mask;
- switch (ctrl->id) {
- case V4L2_CID_SATURATION:
- /* special v_sat handling */
-
- value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
-
- if (core->tvnorm & V4L2_STD_SECAM) {
- /* For SECAM, both U and V sat should be equal */
- value = value << 8 | value;
- } else {
- /* Keeps U Saturation proportional to V Sat */
- value = (value * 0x5a) / 0x7f << 8 | value;
- }
- mask = 0xffff;
- break;
- case V4L2_CID_SHARPNESS:
- /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
- value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
- /* needs to be set for both fields */
- cx_andor(MO_FILTER_EVEN, mask, value);
- break;
- case V4L2_CID_CHROMA_AGC:
- value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
- break;
- default:
- value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
- break;
- }
- dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
- ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
- mask, cc->sreg ? " [shadowed]" : "");
- if (cc->sreg)
- cx_sandor(cc->sreg, cc->reg, mask, value);
- else
- cx_andor(cc->reg, mask, value);
- return 0;
-}
-
-static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct cx88_core *core =
- container_of(ctrl->handler, struct cx88_core, audio_hdl);
- const struct cx88_ctrl *cc = ctrl->priv;
- u32 value,mask;
-
- /* Pass changes onto any WM8775 */
- if (core->board.audio_chip == V4L2_IDENT_WM8775) {
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- wm8775_s_ctrl(core, ctrl->id, ctrl->val);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
- (0x90 + ctrl->val) << 8 : 0);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
- break;
- default:
- break;
- }
- }
-
- mask = cc->mask;
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_BALANCE:
- value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- value = 0x3f - (ctrl->val & 0x3f);
- break;
- default:
- value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
- break;
- }
- dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
- ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
- mask, cc->sreg ? " [shadowed]" : "");
- if (cc->sreg)
- cx_sandor(cc->sreg, cc->reg, mask, value);
- else
- cx_andor(cc->reg, mask, value);
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-/* VIDEO IOCTLS */
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = fh->vidq.field;
- f->fmt.pix.pixelformat = dev->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * dev->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
- const struct cx8800_fmt *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = norm_maxw(core->tvnorm);
- maxh = norm_maxh(core->tvnorm);
-
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
-
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
- &f->fmt.pix.height, 32, maxh, 0, 0);
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
- int err = vidioc_try_fmt_vid_cap (file,priv,f);
-
- if (0 != err)
- return err;
- dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- fh->vidq.field = f->fmt.pix.field;
- return 0;
-}
-
-void cx88_querycap(struct file *file, struct cx88_core *core,
- struct v4l2_capability *cap)
-{
- struct video_device *vdev = video_devdata(file);
-
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
- cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
- if (UNSET != core->board.tuner_type)
- cap->device_caps |= V4L2_CAP_TUNER;
- switch (vdev->vfl_type) {
- case VFL_TYPE_RADIO:
- cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
- break;
- case VFL_TYPE_GRABBER:
- cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
- break;
- }
- cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
- if (core->board.radio.type == CX88_RADIO)
- cap->capabilities |= V4L2_CAP_RADIO;
-}
-EXPORT_SYMBOL(cx88_querycap);
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
- struct cx88_core *core = dev->core;
-
- strcpy(cap->driver, "cx8800");
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cx88_querycap(file, core, cap);
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(formats)))
- return -EINVAL;
-
- strlcpy(f->description,formats[f->index].name,sizeof(f->description));
- f->pixelformat = formats[f->index].fourcc;
-
- return 0;
-}
-
-static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
-{
- return videobuf_reqbufs(get_queue(file), p);
-}
-
-static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_querybuf(get_queue(file), p);
-}
-
-static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_qbuf(get_queue(file), p);
-}
-
-static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
-{
- return videobuf_dqbuf(get_queue(file), p,
- file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
-
- if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
- (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- if (unlikely(!res_get(dev, fh, get_resource(file))))
- return -EBUSY;
- return videobuf_streamon(get_queue(file));
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
- struct cx8800_fh *fh = priv;
- struct cx8800_dev *dev = fh->dev;
- int err, res;
-
- if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
- (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
- return -EINVAL;
-
- res = get_resource(file);
- err = videobuf_streamoff(get_queue(file));
- if (err < 0)
- return err;
- res_free(dev,fh,res);
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- *tvnorm = core->tvnorm;
- return 0;
-}
-
-static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core,*tvnorms);
- mutex_unlock(&core->lock);
-
- return 0;
-}
-
-/* only one input in this sample driver */
-int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
-{
- static const char * const iname[] = {
- [ CX88_VMUX_COMPOSITE1 ] = "Composite1",
- [ CX88_VMUX_COMPOSITE2 ] = "Composite2",
- [ CX88_VMUX_COMPOSITE3 ] = "Composite3",
- [ CX88_VMUX_COMPOSITE4 ] = "Composite4",
- [ CX88_VMUX_SVIDEO ] = "S-Video",
- [ CX88_VMUX_TELEVISION ] = "Television",
- [ CX88_VMUX_CABLE ] = "Cable TV",
- [ CX88_VMUX_DVB ] = "DVB",
- [ CX88_VMUX_DEBUG ] = "for debug only",
- };
- unsigned int n = i->index;
-
- if (n >= 4)
- return -EINVAL;
- if (0 == INPUT(n).type)
- return -EINVAL;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name,iname[INPUT(n).type]);
- if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
- (CX88_VMUX_CABLE == INPUT(n).type)) {
- i->type = V4L2_INPUT_TYPE_TUNER;
- }
- i->std = CX88_NORMS;
- return 0;
-}
-EXPORT_SYMBOL(cx88_enum_input);
-
-static int vidioc_enum_input (struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
- return cx88_enum_input (core,i);
-}
-
-static int vidioc_g_input (struct file *file, void *priv, unsigned int *i)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- *i = core->input;
- return 0;
-}
-
-static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- if (i >= 4)
- return -EINVAL;
- if (0 == INPUT(i).type)
- return -EINVAL;
-
- mutex_lock(&core->lock);
- cx88_newstation(core);
- cx88_video_mux(core,i);
- mutex_unlock(&core->lock);
- return 0;
-}
-
-static int vidioc_g_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
- u32 reg;
-
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Television");
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
- call_all(core, tuner, g_tuner, t);
-
- cx88_get_stereo(core ,t);
- reg = cx_read(MO_DEVICE_STATUS);
- t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
- return 0;
-}
-
-static int vidioc_s_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- if (UNSET == core->board.tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- cx88_set_stereo(core, t->audmode, 1);
- return 0;
-}
-
-static int vidioc_g_frequency (struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx8800_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
-
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (f->tuner)
- return -EINVAL;
-
- f->frequency = core->freq;
-
- call_all(core, tuner, g_frequency, f);
-
- return 0;
-}
-
-int cx88_set_freq (struct cx88_core *core,
- struct v4l2_frequency *f)
-{
- if (unlikely(UNSET == core->board.tuner_type))
- return -EINVAL;
- if (unlikely(f->tuner != 0))
- return -EINVAL;
-
- mutex_lock(&core->lock);
- cx88_newstation(core);
- call_all(core, tuner, s_frequency, f);
- call_all(core, tuner, g_frequency, f);
- core->freq = f->frequency;
-
- /* When changing channels it is required to reset TVAUDIO */
- msleep (10);
- cx88_set_tvaudio(core);
-
- mutex_unlock(&core->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(cx88_set_freq);
-
-static int vidioc_s_frequency (struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct cx8800_fh *fh = priv;
- struct cx88_core *core = fh->dev->core;
-
- return cx88_set_freq(core, f);
-}
-
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- if (!v4l2_chip_match_host(&chip->match))
- return -EINVAL;
- chip->revision = 0;
- chip->ident = V4L2_IDENT_UNKNOWN;
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register (struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- /* cx2388x has a 24-bit register space */
- reg->val = cx_read(reg->reg & 0xffffff);
- reg->size = 4;
- return 0;
-}
-
-static int vidioc_s_register (struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- cx_write(reg->reg & 0xffffff, reg->val);
- return 0;
-}
-#endif
-
-/* ----------------------------------------------------------- */
-/* RADIO ESPECIFIC IOCTLS */
-/* ----------------------------------------------------------- */
-
-static int radio_g_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- if (unlikely(t->index > 0))
- return -EINVAL;
-
- strcpy(t->name, "Radio");
-
- call_all(core, tuner, g_tuner, t);
- return 0;
-}
-
-/* FIXME: Should add a standard for radio */
-
-static int radio_s_tuner (struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
-
- if (0 != t->index)
- return -EINVAL;
- if (t->audmode > V4L2_TUNER_MODE_STEREO)
- t->audmode = V4L2_TUNER_MODE_STEREO;
-
- call_all(core, tuner, s_tuner, t);
-
- return 0;
-}
-
-/* ----------------------------------------------------------- */
-
-static void cx8800_vid_timeout(unsigned long data)
-{
- struct cx8800_dev *dev = (struct cx8800_dev*)data;
- struct cx88_core *core = dev->core;
- struct cx88_dmaqueue *q = &dev->vidq;
- struct cx88_buffer *buf;
- unsigned long flags;
-
- cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
-
- cx_clear(MO_VID_DMACNTRL, 0x11);
- cx_clear(VID_CAPTURE_CONTROL, 0x06);
-
- spin_lock_irqsave(&dev->slock,flags);
- while (!list_empty(&q->active)) {
- buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
- buf, buf->vb.i, (unsigned long)buf->risc.dma);
- }
- restart_video_queue(dev,q);
- spin_unlock_irqrestore(&dev->slock,flags);
-}
-
-static const char *cx88_vid_irqs[32] = {
- "y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
- "y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
- "y_oflow", "u_oflow", "v_oflow", "vbi_oflow",
- "y_sync", "u_sync", "v_sync", "vbi_sync",
- "opc_err", "par_err", "rip_err", "pci_abort",
-};
-
-static void cx8800_vid_irq(struct cx8800_dev *dev)
-{
- struct cx88_core *core = dev->core;
- u32 status, mask, count;
-
- status = cx_read(MO_VID_INTSTAT);
- mask = cx_read(MO_VID_INTMSK);
- if (0 == (status & mask))
- return;
- cx_write(MO_VID_INTSTAT, status);
- if (irq_debug || (status & mask & ~0xff))
- cx88_print_irqbits(core->name, "irq vid",
- cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
- status, mask);
-
- /* risc op code error */
- if (status & (1 << 16)) {
- printk(KERN_WARNING "%s/0: video risc op code error\n",core->name);
- cx_clear(MO_VID_DMACNTRL, 0x11);
- cx_clear(VID_CAPTURE_CONTROL, 0x06);
- cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
- }
-
- /* risc1 y */
- if (status & 0x01) {
- spin_lock(&dev->slock);
- count = cx_read(MO_VIDY_GPCNT);
- cx88_wakeup(core, &dev->vidq, count);
- spin_unlock(&dev->slock);
- }
-
- /* risc1 vbi */
- if (status & 0x08) {
- spin_lock(&dev->slock);
- count = cx_read(MO_VBI_GPCNT);
- cx88_wakeup(core, &dev->vbiq, count);
- spin_unlock(&dev->slock);
- }
-
- /* risc2 y */
- if (status & 0x10) {
- dprintk(2,"stopper video\n");
- spin_lock(&dev->slock);
- restart_video_queue(dev,&dev->vidq);
- spin_unlock(&dev->slock);
- }
-
- /* risc2 vbi */
- if (status & 0x80) {
- dprintk(2,"stopper vbi\n");
- spin_lock(&dev->slock);
- cx8800_restart_vbi_queue(dev,&dev->vbiq);
- spin_unlock(&dev->slock);
- }
-}
-
-static irqreturn_t cx8800_irq(int irq, void *dev_id)
-{
- struct cx8800_dev *dev = dev_id;
- struct cx88_core *core = dev->core;
- u32 status;
- int loop, handled = 0;
-
- for (loop = 0; loop < 10; loop++) {
- status = cx_read(MO_PCI_INTSTAT) &
- (core->pci_irqmask | PCI_INT_VIDINT);
- if (0 == status)
- goto out;
- cx_write(MO_PCI_INTSTAT, status);
- handled = 1;
-
- if (status & core->pci_irqmask)
- cx88_core_irq(core,status);
- if (status & PCI_INT_VIDINT)
- cx8800_vid_irq(dev);
- };
- if (10 == loop) {
- printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n",
- core->name);
- cx_write(MO_PCI_INTMSK,0);
- }
-
- out:
- return IRQ_RETVAL(handled);
-}
-
-/* ----------------------------------------------------------- */
-/* exported stuff */
-
-static const struct v4l2_file_operations video_fops =
-{
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = video_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static const struct video_device cx8800_video_template = {
- .name = "cx8800-video",
- .fops = &video_fops,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = CX88_NORMS,
-};
-
-static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static const struct video_device cx8800_vbi_template = {
- .name = "cx8800-vbi",
- .fops = &video_fops,
- .ioctl_ops = &vbi_ioctl_ops,
- .tvnorms = CX88_NORMS,
-};
-
-static const struct v4l2_file_operations radio_fops =
-{
- .owner = THIS_MODULE,
- .open = video_open,
- .poll = v4l2_ctrl_poll,
- .release = video_release,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops radio_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = radio_g_tuner,
- .vidioc_s_tuner = radio_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static const struct video_device cx8800_radio_template = {
- .name = "cx8800-radio",
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
- .s_ctrl = cx8800_s_vid_ctrl,
-};
-
-static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
- .s_ctrl = cx8800_s_aud_ctrl,
-};
-
-/* ----------------------------------------------------------- */
-
-static void cx8800_unregister_video(struct cx8800_dev *dev)
-{
- if (dev->radio_dev) {
- if (video_is_registered(dev->radio_dev))
- video_unregister_device(dev->radio_dev);
- else
- video_device_release(dev->radio_dev);
- dev->radio_dev = NULL;
- }
- if (dev->vbi_dev) {
- if (video_is_registered(dev->vbi_dev))
- video_unregister_device(dev->vbi_dev);
- else
- video_device_release(dev->vbi_dev);
- dev->vbi_dev = NULL;
- }
- if (dev->video_dev) {
- if (video_is_registered(dev->video_dev))
- video_unregister_device(dev->video_dev);
- else
- video_device_release(dev->video_dev);
- dev->video_dev = NULL;
- }
-}
-
-static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
-{
- struct cx8800_dev *dev;
- struct cx88_core *core;
- int err;
- int i;
-
- dev = kzalloc(sizeof(*dev),GFP_KERNEL);
- if (NULL == dev)
- return -ENOMEM;
-
- /* pci init */
- dev->pci = pci_dev;
- if (pci_enable_device(pci_dev)) {
- err = -EIO;
- goto fail_free;
- }
- core = cx88_core_get(dev->pci);
- if (NULL == core) {
- err = -EINVAL;
- goto fail_free;
- }
- dev->core = core;
-
- /* print pci info */
- dev->pci_rev = pci_dev->revision;
- pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
- printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%llx\n", core->name,
- pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
- dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
-
- pci_set_master(pci_dev);
- if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) {
- printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
- err = -EIO;
- goto fail_core;
- }
-
- /* initialize driver struct */
- spin_lock_init(&dev->slock);
- core->tvnorm = V4L2_STD_NTSC_M;
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
- dev->vidq.timeout.function = cx8800_vid_timeout;
- dev->vidq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vidq.timeout);
- cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
- MO_VID_DMACNTRL,0x11,0x00);
-
- /* init vbi dma queues */
- INIT_LIST_HEAD(&dev->vbiq.active);
- INIT_LIST_HEAD(&dev->vbiq.queued);
- dev->vbiq.timeout.function = cx8800_vbi_timeout;
- dev->vbiq.timeout.data = (unsigned long)dev;
- init_timer(&dev->vbiq.timeout);
- cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
- MO_VID_DMACNTRL,0x88,0x00);
-
- /* get irq */
- err = request_irq(pci_dev->irq, cx8800_irq,
- IRQF_SHARED | IRQF_DISABLED, core->name, dev);
- if (err < 0) {
- printk(KERN_ERR "%s/0: can't get IRQ %d\n",
- core->name,pci_dev->irq);
- goto fail_core;
- }
- cx_set(MO_PCI_INTMSK, core->pci_irqmask);
-
- for (i = 0; i < CX8800_AUD_CTLS; i++) {
- const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
- struct v4l2_ctrl *vc;
-
- vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
- cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
- if (vc == NULL) {
- err = core->audio_hdl.error;
- goto fail_core;
- }
- vc->priv = (void *)cc;
- }
-
- for (i = 0; i < CX8800_VID_CTLS; i++) {
- const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
- struct v4l2_ctrl *vc;
-
- vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
- cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
- if (vc == NULL) {
- err = core->video_hdl.error;
- goto fail_core;
- }
- vc->priv = (void *)cc;
- if (vc->id == V4L2_CID_CHROMA_AGC)
- core->chroma_agc = vc;
- }
- v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl);
-
- /* load and configure helper modules */
-
- if (core->board.audio_chip == V4L2_IDENT_WM8775) {
- struct i2c_board_info wm8775_info = {
- .type = "wm8775",
- .addr = 0x36 >> 1,
- .platform_data = &core->wm8775_data,
- };
- struct v4l2_subdev *sd;
-
- if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
- core->wm8775_data.is_nova_s = true;
- else
- core->wm8775_data.is_nova_s = false;
-
- sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
- &wm8775_info, NULL);
- if (sd != NULL) {
- core->sd_wm8775 = sd;
- sd->grp_id = WM8775_GID;
- }
- }
-
- if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
- /* This probes for a tda9874 as is used on some
- Pixelview Ultra boards. */
- v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
- "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
- }
-
- switch (core->boardnr) {
- case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
- static const struct i2c_board_info rtc_info = {
- I2C_BOARD_INFO("isl1208", 0x6f)
- };
-
- request_module("rtc-isl1208");
- core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
- }
- /* break intentionally omitted */
- case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
- request_module("ir-kbd-i2c");
- }
-
- /* Sets device info at pci_dev */
- pci_set_drvdata(pci_dev, dev);
-
- dev->width = 320;
- dev->height = 240;
- dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
-
- /* initial device configuration */
- mutex_lock(&core->lock);
- cx88_set_tvnorm(core, core->tvnorm);
- v4l2_ctrl_handler_setup(&core->video_hdl);
- v4l2_ctrl_handler_setup(&core->audio_hdl);
- cx88_video_mux(core, 0);
-
- /* register v4l devices */
- dev->video_dev = cx88_vdev_init(core,dev->pci,
- &cx8800_video_template,"video");
- video_set_drvdata(dev->video_dev, dev);
- dev->video_dev->ctrl_handler = &core->video_hdl;
- err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
- video_nr[core->nr]);
- if (err < 0) {
- printk(KERN_ERR "%s/0: can't register video device\n",
- core->name);
- goto fail_unreg;
- }
- printk(KERN_INFO "%s/0: registered device %s [v4l2]\n",
- core->name, video_device_node_name(dev->video_dev));
-
- dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi");
- video_set_drvdata(dev->vbi_dev, dev);
- err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
- vbi_nr[core->nr]);
- if (err < 0) {
- printk(KERN_ERR "%s/0: can't register vbi device\n",
- core->name);
- goto fail_unreg;
- }
- printk(KERN_INFO "%s/0: registered device %s\n",
- core->name, video_device_node_name(dev->vbi_dev));
-
- if (core->board.radio.type == CX88_RADIO) {
- dev->radio_dev = cx88_vdev_init(core,dev->pci,
- &cx8800_radio_template,"radio");
- video_set_drvdata(dev->radio_dev, dev);
- dev->radio_dev->ctrl_handler = &core->audio_hdl;
- err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
- radio_nr[core->nr]);
- if (err < 0) {
- printk(KERN_ERR "%s/0: can't register radio device\n",
- core->name);
- goto fail_unreg;
- }
- printk(KERN_INFO "%s/0: registered device %s\n",
- core->name, video_device_node_name(dev->radio_dev));
- }
-
- /* start tvaudio thread */
- if (core->board.tuner_type != TUNER_ABSENT) {
- core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
- if (IS_ERR(core->kthread)) {
- err = PTR_ERR(core->kthread);
- printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n",
- core->name, err);
- }
- }
- mutex_unlock(&core->lock);
-
- return 0;
-
-fail_unreg:
- cx8800_unregister_video(dev);
- free_irq(pci_dev->irq, dev);
- mutex_unlock(&core->lock);
-fail_core:
- cx88_core_put(core,dev->pci);
-fail_free:
- kfree(dev);
- return err;
-}
-
-static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
-{
- struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
- struct cx88_core *core = dev->core;
-
- /* stop thread */
- if (core->kthread) {
- kthread_stop(core->kthread);
- core->kthread = NULL;
- }
-
- if (core->ir)
- cx88_ir_stop(core);
-
- cx88_shutdown(core); /* FIXME */
- pci_disable_device(pci_dev);
-
- /* unregister stuff */
-
- free_irq(pci_dev->irq, dev);
- cx8800_unregister_video(dev);
- pci_set_drvdata(pci_dev, NULL);
-
- /* free memory */
- btcx_riscmem_free(dev->pci,&dev->vidq.stopper);
- cx88_core_put(core,dev->pci);
- kfree(dev);
-}
-
-#ifdef CONFIG_PM
-static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
-{
- struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
- struct cx88_core *core = dev->core;
-
- /* stop video+vbi capture */
- spin_lock(&dev->slock);
- if (!list_empty(&dev->vidq.active)) {
- printk("%s/0: suspend video\n", core->name);
- stop_video_dma(dev);
- del_timer(&dev->vidq.timeout);
- }
- if (!list_empty(&dev->vbiq.active)) {
- printk("%s/0: suspend vbi\n", core->name);
- cx8800_stop_vbi_dma(dev);
- del_timer(&dev->vbiq.timeout);
- }
- spin_unlock(&dev->slock);
-
- if (core->ir)
- cx88_ir_stop(core);
- /* FIXME -- shutdown device */
- cx88_shutdown(core);
-
- pci_save_state(pci_dev);
- if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) {
- pci_disable_device(pci_dev);
- dev->state.disabled = 1;
- }
- return 0;
-}
-
-static int cx8800_resume(struct pci_dev *pci_dev)
-{
- struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
- struct cx88_core *core = dev->core;
- int err;
-
- if (dev->state.disabled) {
- err=pci_enable_device(pci_dev);
- if (err) {
- printk(KERN_ERR "%s/0: can't enable device\n",
- core->name);
- return err;
- }
-
- dev->state.disabled = 0;
- }
- err= pci_set_power_state(pci_dev, PCI_D0);
- if (err) {
- printk(KERN_ERR "%s/0: can't set power state\n", core->name);
- pci_disable_device(pci_dev);
- dev->state.disabled = 1;
-
- return err;
- }
- pci_restore_state(pci_dev);
-
- /* FIXME: re-initialize hardware */
- cx88_reset(core);
- if (core->ir)
- cx88_ir_start(core);
-
- cx_set(MO_PCI_INTMSK, core->pci_irqmask);
-
- /* restart video+vbi capture */
- spin_lock(&dev->slock);
- if (!list_empty(&dev->vidq.active)) {
- printk("%s/0: resume video\n", core->name);
- restart_video_queue(dev,&dev->vidq);
- }
- if (!list_empty(&dev->vbiq.active)) {
- printk("%s/0: resume vbi\n", core->name);
- cx8800_restart_vbi_queue(dev,&dev->vbiq);
- }
- spin_unlock(&dev->slock);
-
- return 0;
-}
-#endif
-
-/* ----------------------------------------------------------- */
-
-static const struct pci_device_id cx8800_pci_tbl[] = {
- {
- .vendor = 0x14f1,
- .device = 0x8800,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- },{
- /* --- end of list --- */
- }
-};
-MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
-
-static struct pci_driver cx8800_pci_driver = {
- .name = "cx8800",
- .id_table = cx8800_pci_tbl,
- .probe = cx8800_initdev,
- .remove = __devexit_p(cx8800_finidev),
-#ifdef CONFIG_PM
- .suspend = cx8800_suspend,
- .resume = cx8800_resume,
-#endif
-};
-
-static int __init cx8800_init(void)
-{
- printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n",
- CX88_VERSION);
- return pci_register_driver(&cx8800_pci_driver);
-}
-
-static void __exit cx8800_fini(void)
-{
- pci_unregister_driver(&cx8800_pci_driver);
-}
-
-module_init(cx8800_init);
-module_exit(cx8800_fini);
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
deleted file mode 100644
index 0cae0fd9e164..000000000000
--- a/drivers/media/video/cx88/cx88.h
+++ /dev/null
@@ -1,748 +0,0 @@
-/*
- *
- * v4l2 device driver for cx2388x based TV cards
- *
- * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/videodev2.h>
-#include <linux/kdev_t.h>
-
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/tuner.h>
-#include <media/tveeprom.h>
-#include <media/videobuf-dma-sg.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/cx2341x.h>
-#include <media/videobuf-dvb.h>
-#include <media/ir-kbd-i2c.h>
-#include <media/wm8775.h>
-
-#include "btcx-risc.h"
-#include "cx88-reg.h"
-#include "tuner-xc2028.h"
-
-#include <linux/mutex.h>
-
-#define CX88_VERSION "0.0.9"
-
-#define UNSET (-1U)
-
-#define CX88_MAXBOARDS 8
-
-/* Max number of inputs by card */
-#define MAX_CX88_INPUT 8
-
-/* ----------------------------------------------------------- */
-/* defines and enums */
-
-/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */
-#define CX88_NORMS (V4L2_STD_ALL \
- & ~V4L2_STD_PAL_H \
- & ~V4L2_STD_NTSC_M_KR \
- & ~V4L2_STD_SECAM_LC)
-
-#define FORMAT_FLAGS_PACKED 0x01
-#define FORMAT_FLAGS_PLANAR 0x02
-
-#define VBI_LINE_COUNT 17
-#define VBI_LINE_LENGTH 2048
-
-#define AUD_RDS_LINES 4
-
-/* need "shadow" registers for some write-only ones ... */
-#define SHADOW_AUD_VOL_CTL 1
-#define SHADOW_AUD_BAL_CTL 2
-#define SHADOW_MAX 3
-
-/* FM Radio deemphasis type */
-enum cx88_deemph_type {
- FM_NO_DEEMPH = 0,
- FM_DEEMPH_50,
- FM_DEEMPH_75
-};
-
-enum cx88_board_type {
- CX88_BOARD_NONE = 0,
- CX88_MPEG_DVB,
- CX88_MPEG_BLACKBIRD
-};
-
-enum cx8802_board_access {
- CX8802_DRVCTL_SHARED = 1,
- CX8802_DRVCTL_EXCLUSIVE = 2,
-};
-
-/* ----------------------------------------------------------- */
-/* tv norms */
-
-static unsigned int inline norm_maxw(v4l2_std_id norm)
-{
- return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
-}
-
-
-static unsigned int inline norm_maxh(v4l2_std_id norm)
-{
- return (norm & V4L2_STD_625_50) ? 576 : 480;
-}
-
-/* ----------------------------------------------------------- */
-/* static data */
-
-struct cx8800_fmt {
- const char *name;
- u32 fourcc; /* v4l2 format id */
- int depth;
- int flags;
- u32 cxformat;
-};
-
-/* ----------------------------------------------------------- */
-/* SRAM memory management data (see cx88-core.c) */
-
-#define SRAM_CH21 0 /* video */
-#define SRAM_CH22 1
-#define SRAM_CH23 2
-#define SRAM_CH24 3 /* vbi */
-#define SRAM_CH25 4 /* audio */
-#define SRAM_CH26 5
-#define SRAM_CH28 6 /* mpeg */
-#define SRAM_CH27 7 /* audio rds */
-/* more */
-
-struct sram_channel {
- const char *name;
- u32 cmds_start;
- u32 ctrl_start;
- u32 cdt;
- u32 fifo_start;
- u32 fifo_size;
- u32 ptr1_reg;
- u32 ptr2_reg;
- u32 cnt1_reg;
- u32 cnt2_reg;
-};
-extern const struct sram_channel const cx88_sram_channels[];
-
-/* ----------------------------------------------------------- */
-/* card configuration */
-
-#define CX88_BOARD_NOAUTO UNSET
-#define CX88_BOARD_UNKNOWN 0
-#define CX88_BOARD_HAUPPAUGE 1
-#define CX88_BOARD_GDI 2
-#define CX88_BOARD_PIXELVIEW 3
-#define CX88_BOARD_ATI_WONDER_PRO 4
-#define CX88_BOARD_WINFAST2000XP_EXPERT 5
-#define CX88_BOARD_AVERTV_STUDIO_303 6
-#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
-#define CX88_BOARD_WINFAST_DV2000 8
-#define CX88_BOARD_LEADTEK_PVR2000 9
-#define CX88_BOARD_IODATA_GVVCP3PCI 10
-#define CX88_BOARD_PROLINK_PLAYTVPVR 11
-#define CX88_BOARD_ASUS_PVR_416 12
-#define CX88_BOARD_MSI_TVANYWHERE 13
-#define CX88_BOARD_KWORLD_DVB_T 14
-#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
-#define CX88_BOARD_KWORLD_LTV883 16
-#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17
-#define CX88_BOARD_HAUPPAUGE_DVB_T1 18
-#define CX88_BOARD_CONEXANT_DVB_T1 19
-#define CX88_BOARD_PROVIDEO_PV259 20
-#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21
-#define CX88_BOARD_PCHDTV_HD3000 22
-#define CX88_BOARD_DNTV_LIVE_DVB_T 23
-#define CX88_BOARD_HAUPPAUGE_ROSLYN 24
-#define CX88_BOARD_DIGITALLOGIC_MEC 25
-#define CX88_BOARD_IODATA_GVBCTV7E 26
-#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27
-#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28
-#define CX88_BOARD_ADSTECH_DVB_T_PCI 29
-#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30
-#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31
-#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32
-#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33
-#define CX88_BOARD_ATI_HDTVWONDER 34
-#define CX88_BOARD_WINFAST_DTV1000 35
-#define CX88_BOARD_AVERTV_303 36
-#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37
-#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38
-#define CX88_BOARD_KWORLD_DVBS_100 39
-#define CX88_BOARD_HAUPPAUGE_HVR1100 40
-#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41
-#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42
-#define CX88_BOARD_KWORLD_DVB_T_CX22702 43
-#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44
-#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45
-#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46
-#define CX88_BOARD_PCHDTV_HD5500 47
-#define CX88_BOARD_KWORLD_MCE200_DELUXE 48
-#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49
-#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50
-#define CX88_BOARD_WINFAST_DTV2000H 51
-#define CX88_BOARD_GENIATECH_DVBS 52
-#define CX88_BOARD_HAUPPAUGE_HVR3000 53
-#define CX88_BOARD_NORWOOD_MICRO 54
-#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
-#define CX88_BOARD_HAUPPAUGE_HVR1300 56
-#define CX88_BOARD_ADSTECH_PTV_390 57
-#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58
-#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59
-#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60
-#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61
-#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62
-#define CX88_BOARD_GENIATECH_X8000_MT 63
-#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64
-#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65
-#define CX88_BOARD_PROLINK_PV_8000GT 66
-#define CX88_BOARD_KWORLD_ATSC_120 67
-#define CX88_BOARD_HAUPPAUGE_HVR4000 68
-#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69
-#define CX88_BOARD_TEVII_S460 70
-#define CX88_BOARD_OMICOM_SS4_PCI 71
-#define CX88_BOARD_TBS_8920 72
-#define CX88_BOARD_TEVII_S420 73
-#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74
-#define CX88_BOARD_PROF_7300 75
-#define CX88_BOARD_SATTRADE_ST4200 76
-#define CX88_BOARD_TBS_8910 77
-#define CX88_BOARD_PROF_6200 78
-#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
-#define CX88_BOARD_HAUPPAUGE_IRONLY 80
-#define CX88_BOARD_WINFAST_DTV1800H 81
-#define CX88_BOARD_WINFAST_DTV2000H_J 82
-#define CX88_BOARD_PROF_7301 83
-#define CX88_BOARD_SAMSUNG_SMT_7020 84
-#define CX88_BOARD_TWINHAN_VP1027_DVBS 85
-#define CX88_BOARD_TEVII_S464 86
-#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87
-#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88
-#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89
-#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90
-
-enum cx88_itype {
- CX88_VMUX_COMPOSITE1 = 1,
- CX88_VMUX_COMPOSITE2,
- CX88_VMUX_COMPOSITE3,
- CX88_VMUX_COMPOSITE4,
- CX88_VMUX_SVIDEO,
- CX88_VMUX_TELEVISION,
- CX88_VMUX_CABLE,
- CX88_VMUX_DVB,
- CX88_VMUX_DEBUG,
- CX88_RADIO,
-};
-
-struct cx88_input {
- enum cx88_itype type;
- u32 gpio0, gpio1, gpio2, gpio3;
- unsigned int vmux:2;
- unsigned int audioroute:4;
-};
-
-struct cx88_board {
- const char *name;
- unsigned int tuner_type;
- unsigned int radio_type;
- unsigned char tuner_addr;
- unsigned char radio_addr;
- int tda9887_conf;
- struct cx88_input input[MAX_CX88_INPUT];
- struct cx88_input radio;
- enum cx88_board_type mpeg;
- unsigned int audio_chip;
- int num_frontends;
-
- /* Used for I2S devices */
- int i2sinputcntl;
-};
-
-struct cx88_subid {
- u16 subvendor;
- u16 subdevice;
- u32 card;
-};
-
-enum cx88_tvaudio {
- WW_NONE = 1,
- WW_BTSC,
- WW_BG,
- WW_DK,
- WW_I,
- WW_L,
- WW_EIAJ,
- WW_I2SPT,
- WW_FM,
- WW_I2SADC,
- WW_M
-};
-
-#define INPUT(nr) (core->board.input[nr])
-
-/* ----------------------------------------------------------- */
-/* device / file handle status */
-
-#define RESOURCE_OVERLAY 1
-#define RESOURCE_VIDEO 2
-#define RESOURCE_VBI 4
-
-#define BUFFER_TIMEOUT msecs_to_jiffies(2000)
-
-/* buffer for one video frame */
-struct cx88_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- /* cx88 specific */
- unsigned int bpl;
- struct btcx_riscmem risc;
- const struct cx8800_fmt *fmt;
- u32 count;
-};
-
-struct cx88_dmaqueue {
- struct list_head active;
- struct list_head queued;
- struct timer_list timeout;
- struct btcx_riscmem stopper;
- u32 count;
-};
-
-struct cx88_core {
- struct list_head devlist;
- atomic_t refcount;
-
- /* board name */
- int nr;
- char name[32];
-
- /* pci stuff */
- int pci_bus;
- int pci_slot;
- u32 __iomem *lmmio;
- u8 __iomem *bmmio;
- u32 shadow[SHADOW_MAX];
- int pci_irqmask;
-
- /* i2c i/o */
- struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
- struct i2c_client i2c_client;
- u32 i2c_state, i2c_rc;
-
- /* config info -- analog */
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler video_hdl;
- struct v4l2_ctrl *chroma_agc;
- struct v4l2_ctrl_handler audio_hdl;
- struct v4l2_subdev *sd_wm8775;
- struct i2c_client *i2c_rtc;
- unsigned int boardnr;
- struct cx88_board board;
-
- /* Supported V4L _STD_ tuner formats */
- unsigned int tuner_formats;
-
- /* config info -- dvb */
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
- int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
-#endif
- void (*gate_ctrl)(struct cx88_core *core, int open);
-
- /* state info */
- struct task_struct *kthread;
- v4l2_std_id tvnorm;
- enum cx88_tvaudio tvaudio;
- u32 audiomode_manual;
- u32 audiomode_current;
- u32 input;
- u32 last_analog_input;
- u32 astat;
- u32 use_nicam;
- unsigned long last_change;
-
- /* IR remote control state */
- struct cx88_IR *ir;
-
- /* I2C remote data */
- struct IR_i2c_init_data init_data;
- struct wm8775_platform_data wm8775_data;
-
- struct mutex lock;
- /* various v4l controls */
- u32 freq;
- int users;
- int mpeg_users;
-
- /* cx88-video needs to access cx8802 for hybrid tuner pll access. */
- struct cx8802_dev *dvbdev;
- enum cx88_board_type active_type_id;
- int active_ref;
- int active_fe_id;
-};
-
-static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
-}
-
-#define call_hw(core, grpid, o, f, args...) \
- do { \
- if (!core->i2c_rc) { \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 1); \
- v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 0); \
- } \
- } while (0)
-
-#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args)
-
-#define WM8775_GID (1 << 0)
-
-#define wm8775_s_ctrl(core, id, val) \
- do { \
- struct v4l2_ctrl *ctrl_ = \
- v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
- if (ctrl_ && !core->i2c_rc) { \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 1); \
- v4l2_ctrl_s_ctrl(ctrl_, val); \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 0); \
- } \
- } while (0)
-
-#define wm8775_g_ctrl(core, id) \
- ({ \
- struct v4l2_ctrl *ctrl_ = \
- v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \
- s32 val = 0; \
- if (ctrl_ && !core->i2c_rc) { \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 1); \
- val = v4l2_ctrl_g_ctrl(ctrl_); \
- if (core->gate_ctrl) \
- core->gate_ctrl(core, 0); \
- } \
- val; \
- })
-
-struct cx8800_dev;
-struct cx8802_dev;
-
-/* ----------------------------------------------------------- */
-/* function 0: video stuff */
-
-struct cx8800_fh {
- struct v4l2_fh fh;
- struct cx8800_dev *dev;
- unsigned int resources;
-
- /* video capture */
- struct videobuf_queue vidq;
-
- /* vbi capture */
- struct videobuf_queue vbiq;
-};
-
-struct cx8800_suspend_state {
- int disabled;
-};
-
-struct cx8800_dev {
- struct cx88_core *core;
- spinlock_t slock;
-
- /* various device info */
- unsigned int resources;
- struct video_device *video_dev;
- struct video_device *vbi_dev;
- struct video_device *radio_dev;
-
- /* pci i/o */
- struct pci_dev *pci;
- unsigned char pci_rev,pci_lat;
-
- const struct cx8800_fmt *fmt;
- unsigned int width, height;
-
- /* capture queues */
- struct cx88_dmaqueue vidq;
- struct cx88_dmaqueue vbiq;
-
- /* various v4l controls */
-
- /* other global state info */
- struct cx8800_suspend_state state;
-};
-
-/* ----------------------------------------------------------- */
-/* function 1: audio/alsa stuff */
-/* =============> moved to cx88-alsa.c <====================== */
-
-
-/* ----------------------------------------------------------- */
-/* function 2: mpeg stuff */
-
-struct cx8802_fh {
- struct v4l2_fh fh;
- struct cx8802_dev *dev;
- struct videobuf_queue mpegq;
-};
-
-struct cx8802_suspend_state {
- int disabled;
-};
-
-struct cx8802_driver {
- struct cx88_core *core;
-
- /* List of drivers attached to device */
- struct list_head drvlist;
-
- /* Type of driver and access required */
- enum cx88_board_type type_id;
- enum cx8802_board_access hw_access;
-
- /* MPEG 8802 internal only */
- int (*suspend)(struct pci_dev *pci_dev, pm_message_t state);
- int (*resume)(struct pci_dev *pci_dev);
-
- /* Callers to the following functions must hold core->lock */
-
- /* MPEG 8802 -> mini driver - Driver probe and configuration */
- int (*probe)(struct cx8802_driver *drv);
- int (*remove)(struct cx8802_driver *drv);
-
- /* MPEG 8802 -> mini driver - Access for hardware control */
- int (*advise_acquire)(struct cx8802_driver *drv);
- int (*advise_release)(struct cx8802_driver *drv);
-
- /* MPEG 8802 <- mini driver - Access for hardware control */
- int (*request_acquire)(struct cx8802_driver *drv);
- int (*request_release)(struct cx8802_driver *drv);
-};
-
-struct cx8802_dev {
- struct cx88_core *core;
- spinlock_t slock;
-
- /* pci i/o */
- struct pci_dev *pci;
- unsigned char pci_rev,pci_lat;
-
- /* dma queues */
- struct cx88_dmaqueue mpegq;
- u32 ts_packet_size;
- u32 ts_packet_count;
-
- /* other global state info */
- struct cx8802_suspend_state state;
-
- /* for blackbird only */
- struct list_head devlist;
-#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \
- defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE)
- struct video_device *mpeg_dev;
- u32 mailbox;
- int width;
- int height;
- unsigned char mpeg_active; /* nonzero if mpeg encoder is active */
-
- /* mpeg params */
- struct cx2341x_handler cxhdl;
-#endif
-
-#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE)
- /* for dvb only */
- struct videobuf_dvb_frontends frontends;
-#endif
-
-#if defined(CONFIG_VIDEO_CX88_VP3054) || \
- defined(CONFIG_VIDEO_CX88_VP3054_MODULE)
- /* For VP3045 secondary I2C bus support */
- struct vp3054_i2c_state *vp3054;
-#endif
- /* for switching modulation types */
- unsigned char ts_gen_cntrl;
-
- /* List of attached drivers; must hold core->lock to access */
- struct list_head drvlist;
-
- struct work_struct request_module_wk;
-};
-
-/* ----------------------------------------------------------- */
-
-#define cx_read(reg) readl(core->lmmio + ((reg)>>2))
-#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2))
-#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg))
-
-#define cx_andor(reg,mask,value) \
- writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\
- ((value) & (mask)), core->lmmio+((reg)>>2))
-#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
-#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
-
-#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
-
-/* shadow registers */
-#define cx_sread(sreg) (core->shadow[sreg])
-#define cx_swrite(sreg,reg,value) \
- (core->shadow[sreg] = value, \
- writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
-#define cx_sandor(sreg,reg,mask,value) \
- (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \
- writel(core->shadow[sreg], core->lmmio + ((reg)>>2)))
-
-/* ----------------------------------------------------------- */
-/* cx88-core.c */
-
-extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[],
- int len, u32 bits, u32 mask);
-
-extern int cx88_core_irq(struct cx88_core *core, u32 status);
-extern void cx88_wakeup(struct cx88_core *core,
- struct cx88_dmaqueue *q, u32 count);
-extern void cx88_shutdown(struct cx88_core *core);
-extern int cx88_reset(struct cx88_core *core);
-
-extern int
-cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist,
- unsigned int top_offset, unsigned int bottom_offset,
- unsigned int bpl, unsigned int padding, unsigned int lines);
-extern int
-cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
- struct scatterlist *sglist, unsigned int bpl,
- unsigned int lines, unsigned int lpi);
-extern int
-cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
- u32 reg, u32 mask, u32 value);
-extern void
-cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf);
-
-extern void cx88_risc_disasm(struct cx88_core *core,
- struct btcx_riscmem *risc);
-extern int cx88_sram_channel_setup(struct cx88_core *core,
- const struct sram_channel *ch,
- unsigned int bpl, u32 risc);
-extern void cx88_sram_channel_dump(struct cx88_core *core,
- const struct sram_channel *ch);
-
-extern int cx88_set_scale(struct cx88_core *core, unsigned int width,
- unsigned int height, enum v4l2_field field);
-extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm);
-
-extern struct video_device *cx88_vdev_init(struct cx88_core *core,
- struct pci_dev *pci,
- const struct video_device *template_,
- const char *type);
-extern struct cx88_core* cx88_core_get(struct pci_dev *pci);
-extern void cx88_core_put(struct cx88_core *core,
- struct pci_dev *pci);
-
-extern int cx88_start_audio_dma(struct cx88_core *core);
-extern int cx88_stop_audio_dma(struct cx88_core *core);
-
-
-/* ----------------------------------------------------------- */
-/* cx88-vbi.c */
-
-/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */
-int cx8800_vbi_fmt (struct file *file, void *priv,
- struct v4l2_format *f);
-
-/*
-int cx8800_start_vbi_dma(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q,
- struct cx88_buffer *buf);
-*/
-int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
-int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q);
-void cx8800_vbi_timeout(unsigned long data);
-
-extern const struct videobuf_queue_ops cx8800_vbi_qops;
-
-/* ----------------------------------------------------------- */
-/* cx88-i2c.c */
-
-extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
-
-
-/* ----------------------------------------------------------- */
-/* cx88-cards.c */
-
-extern int cx88_tuner_callback(void *dev, int component, int command, int arg);
-extern int cx88_get_resources(const struct cx88_core *core,
- struct pci_dev *pci);
-extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr);
-extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl);
-
-/* ----------------------------------------------------------- */
-/* cx88-tvaudio.c */
-
-void cx88_set_tvaudio(struct cx88_core *core);
-void cx88_newstation(struct cx88_core *core);
-void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
-void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
-int cx88_audio_thread(void *data);
-
-int cx8802_register_driver(struct cx8802_driver *drv);
-int cx8802_unregister_driver(struct cx8802_driver *drv);
-
-/* Caller must hold core->lock */
-struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype);
-
-/* ----------------------------------------------------------- */
-/* cx88-dsp.c */
-
-s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core);
-
-/* ----------------------------------------------------------- */
-/* cx88-input.c */
-
-int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci);
-int cx88_ir_fini(struct cx88_core *core);
-void cx88_ir_irq(struct cx88_core *core);
-int cx88_ir_start(struct cx88_core *core);
-void cx88_ir_stop(struct cx88_core *core);
-extern void cx88_i2c_init_ir(struct cx88_core *core);
-
-/* ----------------------------------------------------------- */
-/* cx88-mpeg.c */
-
-int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev,
- struct cx88_buffer *buf, enum v4l2_field field);
-void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
-void cx8802_cancel_buffers(struct cx8802_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* cx88-video.c*/
-int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i);
-int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f);
-int cx88_video_mux(struct cx88_core *core, unsigned int input);
-void cx88_querycap(struct file *file, struct cx88_core *core,
- struct v4l2_capability *cap);
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
deleted file mode 100644
index 52c5ca68cb3d..000000000000
--- a/drivers/media/video/davinci/Kconfig
+++ /dev/null
@@ -1,121 +0,0 @@
-config VIDEO_DAVINCI_VPIF_DISPLAY
- tristate "DM646x/DA850/OMAPL138 EVM Video Display"
- depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
- select VIDEOBUF2_DMA_CONTIG
- select VIDEO_DAVINCI_VPIF
- select VIDEO_ADV7343 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_THS7303 if VIDEO_HELPER_CHIPS_AUTO
- help
- Enables Davinci VPIF module used for display devices.
- This module is common for following DM6467/DA850/OMAPL138
- based display devices.
-
- To compile this driver as a module, choose M here: the
- module will be called vpif_display.
-
-config VIDEO_DAVINCI_VPIF_CAPTURE
- tristate "DM646x/DA850/OMAPL138 EVM Video Capture"
- depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM)
- select VIDEOBUF2_DMA_CONTIG
- select VIDEO_DAVINCI_VPIF
- help
- Enables Davinci VPIF module used for captur devices.
- This module is common for following DM6467/DA850/OMAPL138
- based capture devices.
-
- To compile this driver as a module, choose M here: the
- module will be called vpif_capture.
-
-config VIDEO_DAVINCI_VPIF
- tristate "DaVinci VPIF Driver"
- depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE
- help
- Support for DaVinci VPIF Driver.
-
- To compile this driver as a module, choose M here: the
- module will be called vpif.
-
-config VIDEO_VPSS_SYSTEM
- tristate "VPSS System module driver"
- depends on ARCH_DAVINCI
- help
- Support for vpss system module for video driver
-
-config VIDEO_VPFE_CAPTURE
- tristate "VPFE Video Capture Driver"
- depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
- depends on I2C
- select VIDEOBUF_DMA_CONTIG
- help
- Support for DMx/AMx VPFE based frame grabber. This is the
- common V4L2 module for following DMx/AMx SoCs from Texas
- Instruments:- DM6446, DM365, DM355 & AM3517/05.
-
- To compile this driver as a module, choose M here: the
- module will be called vpfe-capture.
-
-config VIDEO_DM6446_CCDC
- tristate "DM6446 CCDC HW module"
- depends on VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
- help
- Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces
- with decoder modules such as TVP5146 over BT656 or
- sensor module such as MT9T001 over a raw interface. This
- module configures the interface and CCDC/ISIF to do
- video frame capture from slave decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
-
-config VIDEO_DM355_CCDC
- tristate "DM355 CCDC HW module"
- depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
- help
- Enables DM355 CCD hw module. DM355 CCDC hw interfaces
- with decoder modules such as TVP5146 over BT656 or
- sensor module such as MT9T001 over a raw interface. This
- module configures the interface and CCDC/ISIF to do
- video frame capture from a slave decoders
-
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
-
-config VIDEO_ISIF
- tristate "ISIF HW module"
- depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE
- select VIDEO_VPSS_SYSTEM
- default y
- help
- Enables ISIF hw module. This is the hardware module for
- configuring ISIF in VPFE to capture Raw Bayer RGB data from
- a image sensor or YUV data from a YUV source.
-
- To compile this driver as a module, choose M here: the
- module will be called vpfe.
-
-config VIDEO_DM644X_VPBE
- tristate "DM644X VPBE HW module"
- depends on ARCH_DAVINCI_DM644x
- select VIDEO_VPSS_SYSTEM
- select VIDEOBUF_DMA_CONTIG
- help
- Enables VPBE modules used for display on a DM644x
- SoC.
-
- To compile this driver as a module, choose M here: the
- module will be called vpbe.
-
-
-config VIDEO_VPBE_DISPLAY
- tristate "VPBE V4L2 Display driver"
- depends on ARCH_DAVINCI_DM644x
- select VIDEO_DM644X_VPBE
- help
- Enables VPBE V4L2 Display driver on a DM644x device
-
- To compile this driver as a module, choose M here: the
- module will be called vpbe_display.
diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c
deleted file mode 100644
index 5b68847d4017..000000000000
--- a/drivers/media/video/davinci/dm355_ccdc.c
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
- * Copyright (C) 2005-2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- *
- * CCDC hardware module for DM355
- * ------------------------------
- *
- * This module is for configuring DM355 CCD controller of VPFE to capture
- * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
- * such as Defect Pixel Correction, Color Space Conversion etc to
- * pre-process the Bayer RGB data, before writing it to SDRAM. This
- * module also allows application to configure individual
- * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
- * To do so, application include dm355_ccdc.h and vpfe_capture.h header
- * files. The setparams() API is called by vpfe_capture driver
- * to configure module parameters
- *
- * TODO: 1) Raw bayer parameter settings and bayer capture
- * 2) Split module parameter structure to module specific ioctl structs
- * 3) add support for lense shading correction
- * 4) investigate if enum used for user space type definition
- * to be replaced by #defines or integer
- */
-#include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/videodev2.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/module.h>
-
-#include <media/davinci/dm355_ccdc.h>
-#include <media/davinci/vpss.h>
-
-#include "dm355_ccdc_regs.h"
-#include "ccdc_hw_device.h"
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("CCDC Driver for DM355");
-MODULE_AUTHOR("Texas Instruments");
-
-static struct ccdc_oper_config {
- struct device *dev;
- /* CCDC interface type */
- enum vpfe_hw_if_type if_type;
- /* Raw Bayer configuration */
- struct ccdc_params_raw bayer;
- /* YCbCr configuration */
- struct ccdc_params_ycbcr ycbcr;
- /* Master clock */
- struct clk *mclk;
- /* slave clock */
- struct clk *sclk;
- /* ccdc base address */
- void __iomem *base_addr;
-} ccdc_cfg = {
- /* Raw configurations */
- .bayer = {
- .pix_fmt = CCDC_PIXFMT_RAW,
- .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
- .win = CCDC_WIN_VGA,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .gain = {
- .r_ye = 256,
- .gb_g = 256,
- .gr_cy = 256,
- .b_mg = 256
- },
- .config_params = {
- .datasft = 2,
- .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
- .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
- .alaw = {
- .gama_wd = 2,
- },
- .blk_clamp = {
- .sample_pixel = 1,
- .dc_sub = 25
- },
- .col_pat_field0 = {
- .olop = CCDC_GREEN_BLUE,
- .olep = CCDC_BLUE,
- .elop = CCDC_RED,
- .elep = CCDC_GREEN_RED
- },
- .col_pat_field1 = {
- .olop = CCDC_GREEN_BLUE,
- .olep = CCDC_BLUE,
- .elop = CCDC_RED,
- .elep = CCDC_GREEN_RED
- },
- },
- },
- /* YCbCr configuration */
- .ycbcr = {
- .win = CCDC_WIN_PAL,
- .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
- .frm_fmt = CCDC_FRMFMT_INTERLACED,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .bt656_enable = 1,
- .pix_order = CCDC_PIXORDER_CBYCRY,
- .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
- },
-};
-
-
-/* Raw Bayer formats */
-static u32 ccdc_raw_bayer_pix_formats[] =
- {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
-
-/* Raw YUV formats */
-static u32 ccdc_raw_yuv_pix_formats[] =
- {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
-
-/* register access routines */
-static inline u32 regr(u32 offset)
-{
- return __raw_readl(ccdc_cfg.base_addr + offset);
-}
-
-static inline void regw(u32 val, u32 offset)
-{
- __raw_writel(val, ccdc_cfg.base_addr + offset);
-}
-
-static void ccdc_enable(int en)
-{
- unsigned int temp;
- temp = regr(SYNCEN);
- temp &= (~CCDC_SYNCEN_VDHDEN_MASK);
- temp |= (en & CCDC_SYNCEN_VDHDEN_MASK);
- regw(temp, SYNCEN);
-}
-
-static void ccdc_enable_output_to_sdram(int en)
-{
- unsigned int temp;
- temp = regr(SYNCEN);
- temp &= (~(CCDC_SYNCEN_WEN_MASK));
- temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK);
- regw(temp, SYNCEN);
-}
-
-static void ccdc_config_gain_offset(void)
-{
- /* configure gain */
- regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN);
- regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN);
- regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN);
- regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN);
- /* configure offset */
- regw(ccdc_cfg.bayer.ccdc_offset, OFFSET);
-}
-
-/*
- * ccdc_restore_defaults()
- * This function restore power on defaults in the ccdc registers
- */
-static int ccdc_restore_defaults(void)
-{
- int i;
-
- dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults...");
- /* set all registers to zero */
- for (i = 0; i <= CCDC_REG_LAST; i += 4)
- regw(0, i);
-
- /* now override the values with power on defaults in registers */
- regw(MODESET_DEFAULT, MODESET);
- /* no culling support */
- regw(CULH_DEFAULT, CULH);
- regw(CULV_DEFAULT, CULV);
- /* Set default Gain and Offset */
- ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT;
- ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT;
- ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT;
- ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT;
- ccdc_config_gain_offset();
- regw(OUTCLIP_DEFAULT, OUTCLIP);
- regw(LSCCFG2_DEFAULT, LSCCFG2);
- /* select ccdc input */
- if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
- dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source");
- return -EFAULT;
- }
- /* select ccdc clock */
- if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
- dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock");
- return -EFAULT;
- }
- dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults...");
- return 0;
-}
-
-static int ccdc_open(struct device *device)
-{
- return ccdc_restore_defaults();
-}
-
-static int ccdc_close(struct device *device)
-{
- /* disable clock */
- vpss_enable_clock(VPSS_CCDC_CLOCK, 0);
- /* do nothing for now */
- return 0;
-}
-/*
- * ccdc_setwin()
- * This function will configure the window size to
- * be capture in CCDC reg.
- */
-static void ccdc_setwin(struct v4l2_rect *image_win,
- enum ccdc_frmfmt frm_fmt, int ppc)
-{
- int horz_start, horz_nr_pixels;
- int vert_start, vert_nr_lines;
- int mid_img = 0;
-
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
-
- /*
- * ppc - per pixel count. indicates how many pixels per cell
- * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
- * raw capture this is 1
- */
- horz_start = image_win->left << (ppc - 1);
- horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
-
- /* Writing the horizontal info into the registers */
- regw(horz_start, SPH);
- regw(horz_nr_pixels, NPH);
- vert_start = image_win->top;
-
- if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
- vert_nr_lines = (image_win->height >> 1) - 1;
- vert_start >>= 1;
- /* Since first line doesn't have any data */
- vert_start += 1;
- /* configure VDINT0 and VDINT1 */
- regw(vert_start, VDINT0);
- } else {
- /* Since first line doesn't have any data */
- vert_start += 1;
- vert_nr_lines = image_win->height - 1;
- /* configure VDINT0 and VDINT1 */
- mid_img = vert_start + (image_win->height / 2);
- regw(vert_start, VDINT0);
- regw(mid_img, VDINT1);
- }
- regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
- regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
- regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
- dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
-}
-
-static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
-{
- if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
- ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
- return -EINVAL;
- }
-
- if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
- ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
- return -EINVAL;
- }
-
- if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
- ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
- return -EINVAL;
- }
-
- if ((ccdcparam->med_filt_thres < 0) ||
- (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of median filter threshold\n");
- return -EINVAL;
- }
-
- if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
- ccdcparam->data_sz > CCDC_DATA_8BITS) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
- return -EINVAL;
- }
-
- if (ccdcparam->alaw.enable) {
- if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 ||
- ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) {
- dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
- return -EINVAL;
- }
- }
-
- if (ccdcparam->blk_clamp.b_clamp_enable) {
- if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
- ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of sample pixel\n");
- return -EINVAL;
- }
- if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
- ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
- dev_dbg(ccdc_cfg.dev,
- "Invalid value of sample lines\n");
- return -EINVAL;
- }
- }
- return 0;
-}
-
-/* Parameter operations */
-static int ccdc_set_params(void __user *params)
-{
- struct ccdc_config_params_raw ccdc_raw_params;
- int x;
-
- /* only raw module parameters can be set through the IOCTL */
- if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
- return -EINVAL;
-
- x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
- if (x) {
- dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc"
- "params, %d\n", x);
- return -EFAULT;
- }
-
- if (!validate_ccdc_param(&ccdc_raw_params)) {
- memcpy(&ccdc_cfg.bayer.config_params,
- &ccdc_raw_params,
- sizeof(ccdc_raw_params));
- return 0;
- }
- return -EINVAL;
-}
-
-/* This function will configure CCDC for YCbCr video capture */
-static void ccdc_config_ycbcr(void)
-{
- struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
- u32 temp;
-
- /* first set the CCDC power on defaults values in all registers */
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
- ccdc_restore_defaults();
-
- /* configure pixel format & video frame format */
- temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) <<
- CCDC_INPUT_MODE_SHIFT) |
- ((params->frm_fmt & CCDC_FRM_FMT_MASK) <<
- CCDC_FRM_FMT_SHIFT));
-
- /* setup BT.656 sync mode */
- if (params->bt656_enable) {
- regw(CCDC_REC656IF_BT656_EN, REC656IF);
- /*
- * configure the FID, VD, HD pin polarity fld,hd pol positive,
- * vd negative, 8-bit pack mode
- */
- temp |= CCDC_VD_POL_NEGATIVE;
- } else { /* y/c external sync mode */
- temp |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
- CCDC_FID_POL_SHIFT) |
- ((params->hd_pol & CCDC_HD_POL_MASK) <<
- CCDC_HD_POL_SHIFT) |
- ((params->vd_pol & CCDC_VD_POL_MASK) <<
- CCDC_VD_POL_SHIFT));
- }
-
- /* pack the data to 8-bit */
- temp |= CCDC_DATA_PACK_ENABLE;
-
- regw(temp, MODESET);
-
- /* configure video window */
- ccdc_setwin(&params->win, params->frm_fmt, 2);
-
- /* configure the order of y cb cr in SD-RAM */
- temp = (params->pix_order << CCDC_Y8POS_SHIFT);
- temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC;
- regw(temp, CCDCFG);
-
- /*
- * configure the horizontal line offset. This is done by rounding up
- * width to a multiple of 16 pixels and multiply by two to account for
- * y:cb:cr 4:2:2 data
- */
- regw(((params->win.width * 2 + 31) >> 5), HSIZE);
-
- /* configure the memory line offset */
- if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
- /* two fields are interleaved in memory */
- regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
- }
-
- dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
-}
-
-/*
- * ccdc_config_black_clamp()
- * configure parameters for Optical Black Clamp
- */
-static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
-{
- u32 val;
-
- if (!bclamp->b_clamp_enable) {
- /* configure DCSub */
- regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB);
- regw(0x0000, CLAMP);
- return;
- }
- /* Enable the Black clamping, set sample lines and pixels */
- val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) |
- ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
- CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE;
- regw(val, CLAMP);
-
- /* If Black clamping is enable then make dcsub 0 */
- val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK)
- << CCDC_NUM_LINE_CALC_SHIFT;
- regw(val, DCSUB);
-}
-
-/*
- * ccdc_config_black_compense()
- * configure parameters for Black Compensation
- */
-static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
-{
- u32 val;
-
- val = (bcomp->b & CCDC_BLK_COMP_MASK) |
- ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_GB_COMP_SHIFT);
- regw(val, BLKCMP1);
-
- val = ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_GR_COMP_SHIFT) |
- ((bcomp->r & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_R_COMP_SHIFT);
- regw(val, BLKCMP0);
-}
-
-/*
- * ccdc_write_dfc_entry()
- * write an entry in the dfc table.
- */
-int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
-{
-/* TODO This is to be re-visited and adjusted */
-#define DFC_WRITE_WAIT_COUNT 1000
- u32 val, count = DFC_WRITE_WAIT_COUNT;
-
- regw(dfc->dft_corr_vert[index], DFCMEM0);
- regw(dfc->dft_corr_horz[index], DFCMEM1);
- regw(dfc->dft_corr_sub1[index], DFCMEM2);
- regw(dfc->dft_corr_sub2[index], DFCMEM3);
- regw(dfc->dft_corr_sub3[index], DFCMEM4);
- /* set WR bit to write */
- val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK;
- regw(val, DFCMEMCTL);
-
- /*
- * Assume, it is very short. If we get an error, we need to
- * adjust this value
- */
- while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK)
- count--;
- /*
- * TODO We expect the count to be non-zero to be successful. Adjust
- * the count if write requires more time
- */
-
- if (count) {
- dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n");
- return -1;
- }
- return 0;
-}
-
-/*
- * ccdc_config_vdfc()
- * configure parameters for Vertical Defect Correction
- */
-static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
-{
- u32 val;
- int i;
-
- /* Configure General Defect Correction. The table used is from IPIPE */
- val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK;
-
- /* Configure Vertical Defect Correction if needed */
- if (!dfc->ver_dft_en) {
- /* Enable only General Defect Correction */
- regw(val, DFCCTL);
- return 0;
- }
-
- if (dfc->table_size > CCDC_DFT_TABLE_SIZE)
- return -EINVAL;
-
- val |= CCDC_DFCCTL_VDFC_DISABLE;
- val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) <<
- CCDC_DFCCTL_VDFCSL_SHIFT;
- val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) <<
- CCDC_DFCCTL_VDFCUDA_SHIFT;
- val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) <<
- CCDC_DFCCTL_VDFLSFT_SHIFT;
- regw(val , DFCCTL);
-
- /* clear address ptr to offset 0 */
- val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT;
-
- /* write defect table entries */
- for (i = 0; i < dfc->table_size; i++) {
- /* increment address for non zero index */
- if (i != 0)
- val = CCDC_DFCMEMCTL_INC_ADDR;
- regw(val, DFCMEMCTL);
- if (ccdc_write_dfc_entry(i, dfc) < 0)
- return -EFAULT;
- }
-
- /* update saturation level and enable dfc */
- regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT);
- val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK <<
- CCDC_DFCCTL_VDFCEN_SHIFT);
- regw(val, DFCCTL);
- return 0;
-}
-
-/*
- * ccdc_config_csc()
- * configure parameters for color space conversion
- * Each register CSCM0-7 has two values in S8Q5 format.
- */
-static void ccdc_config_csc(struct ccdc_csc *csc)
-{
- u32 val1, val2;
- int i;
-
- if (!csc->enable)
- return;
-
- /* Enable the CSC sub-module */
- regw(CCDC_CSC_ENABLE, CSCCTL);
-
- /* Converting the co-eff as per the format of the register */
- for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) {
- if ((i % 2) == 0) {
- /* CSCM - LSB */
- val1 = (csc->coeff[i].integer &
- CCDC_CSC_COEF_INTEG_MASK)
- << CCDC_CSC_COEF_INTEG_SHIFT;
- /*
- * convert decimal part to binary. Use 2 decimal
- * precision, user values range from .00 - 0.99
- */
- val1 |= (((csc->coeff[i].decimal &
- CCDC_CSC_COEF_DECIMAL_MASK) *
- CCDC_CSC_DEC_MAX) / 100);
- } else {
-
- /* CSCM - MSB */
- val2 = (csc->coeff[i].integer &
- CCDC_CSC_COEF_INTEG_MASK)
- << CCDC_CSC_COEF_INTEG_SHIFT;
- val2 |= (((csc->coeff[i].decimal &
- CCDC_CSC_COEF_DECIMAL_MASK) *
- CCDC_CSC_DEC_MAX) / 100);
- val2 <<= CCDC_CSCM_MSB_SHIFT;
- val2 |= val1;
- regw(val2, (CSCM0 + ((i - 1) << 1)));
- }
- }
-}
-
-/*
- * ccdc_config_color_patterns()
- * configure parameters for color patterns
- */
-static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
- struct ccdc_col_pat *pat1)
-{
- u32 val;
-
- val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) |
- (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) |
- (pat1->elop << 12) | (pat1->elep << 14));
- regw(val, COLPTN);
-}
-
-/* This function will configure CCDC for Raw mode image capture */
-static int ccdc_config_raw(void)
-{
- struct ccdc_params_raw *params = &ccdc_cfg.bayer;
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int val;
-
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
-
- /* restore power on defaults to register */
- ccdc_restore_defaults();
-
- /* CCDCFG register:
- * set CCD Not to swap input since input is RAW data
- * set FID detection function to Latch at V-Sync
- * set WENLOG - ccdc valid area to AND
- * set TRGSEL to WENBIT
- * set EXTRG to DISABLE
- * disable latching function on VSYNC - shadowed registers
- */
- regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC |
- CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN |
- CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG);
-
- /*
- * Set VDHD direction to input, input type to raw input
- * normal data polarity, do not use external WEN
- */
- val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL |
- CCDC_EXWEN_DISABLE);
-
- /*
- * Configure the vertical sync polarity (MODESET.VDPOL), horizontal
- * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL),
- * frame format(progressive or interlace), & pixel format (Input mode)
- */
- val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
- ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
- ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
- ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
- ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT));
-
- /* set pack for alaw compression */
- if ((config_params->data_sz == CCDC_DATA_8BITS) ||
- config_params->alaw.enable)
- val |= CCDC_DATA_PACK_ENABLE;
-
- /* Configure for LPF */
- if (config_params->lpf_enable)
- val |= (config_params->lpf_enable & CCDC_LPF_MASK) <<
- CCDC_LPF_SHIFT;
-
- /* Configure the data shift */
- val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
- CCDC_DATASFT_SHIFT;
- regw(val , MODESET);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val);
-
- /* Configure the Median Filter threshold */
- regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
-
- /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */
- val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT |
- CCDC_CFA_MOSAIC;
-
- /* Enable and configure aLaw register if needed */
- if (config_params->alaw.enable) {
- val |= (CCDC_ALAW_ENABLE |
- ((config_params->alaw.gama_wd &
- CCDC_ALAW_GAMA_WD_MASK) <<
- CCDC_GAMMAWD_INPUT_SHIFT));
- }
-
- /* Configure Median filter1 & filter2 */
- val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) |
- (config_params->mfilt2 << CCDC_MFILT2_SHIFT));
-
- regw(val, GAMMAWD);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val);
-
- /* configure video window */
- ccdc_setwin(&params->win, params->frm_fmt, 1);
-
- /* Optical Clamp Averaging */
- ccdc_config_black_clamp(&config_params->blk_clamp);
-
- /* Black level compensation */
- ccdc_config_black_compense(&config_params->blk_comp);
-
- /* Vertical Defect Correction if needed */
- if (ccdc_config_vdfc(&config_params->vertical_dft) < 0)
- return -EFAULT;
-
- /* color space conversion */
- ccdc_config_csc(&config_params->csc);
-
- /* color pattern */
- ccdc_config_color_patterns(&config_params->col_pat_field0,
- &config_params->col_pat_field1);
-
- /* Configure the Gain & offset control */
- ccdc_config_gain_offset();
-
- dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val);
-
- /* Configure DATAOFST register */
- val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
- CCDC_DATAOFST_H_SHIFT;
- val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) <<
- CCDC_DATAOFST_V_SHIFT;
- regw(val, DATAOFST);
-
- /* configuring HSIZE register */
- val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) <<
- CCDC_HSIZE_FLIP_SHIFT;
-
- /* If pack 8 is enable then 1 pixel will take 1 byte */
- if ((config_params->data_sz == CCDC_DATA_8BITS) ||
- config_params->alaw.enable) {
- val |= (((params->win.width) + 31) >> 5) &
- CCDC_HSIZE_VAL_MASK;
-
- /* adjust to multiple of 32 */
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
- (((params->win.width) + 31) >> 5) &
- CCDC_HSIZE_VAL_MASK);
- } else {
- /* else one pixel will take 2 byte */
- val |= (((params->win.width * 2) + 31) >> 5) &
- CCDC_HSIZE_VAL_MASK;
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
- (((params->win.width * 2) + 31) >> 5) &
- CCDC_HSIZE_VAL_MASK);
- }
- regw(val, HSIZE);
-
- /* Configure SDOFST register */
- if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
- if (params->image_invert_enable) {
- /* For interlace inverse mode */
- regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
- CCDC_SDOFST_INTERLACE_INVERSE);
- } else {
- /* For interlace non inverse mode */
- regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
- CCDC_SDOFST_INTERLACE_NORMAL);
- }
- } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
- if (params->image_invert_enable) {
- /* For progessive inverse mode */
- regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
- CCDC_SDOFST_PROGRESSIVE_INVERSE);
- } else {
- /* For progessive non inverse mode */
- regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
- CCDC_SDOFST_PROGRESSIVE_NORMAL);
- }
- }
- dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
- return 0;
-}
-
-static int ccdc_configure(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- return ccdc_config_raw();
- else
- ccdc_config_ycbcr();
- return 0;
-}
-
-static int ccdc_set_buftype(enum ccdc_buftype buf_type)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.buf_type = buf_type;
- else
- ccdc_cfg.ycbcr.buf_type = buf_type;
- return 0;
-}
-static enum ccdc_buftype ccdc_get_buftype(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- return ccdc_cfg.bayer.buf_type;
- return ccdc_cfg.ycbcr.buf_type;
-}
-
-static int ccdc_enum_pix(u32 *pix, int i)
-{
- int ret = -EINVAL;
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
- *pix = ccdc_raw_bayer_pix_formats[i];
- ret = 0;
- }
- } else {
- if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
- *pix = ccdc_raw_yuv_pix_formats[i];
- ret = 0;
- }
- }
- return ret;
-}
-
-static int ccdc_set_pixel_format(u32 pixfmt)
-{
- struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
-
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
- if (pixfmt == V4L2_PIX_FMT_SBGGR8)
- alaw->enable = 1;
- else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
- return -EINVAL;
- } else {
- if (pixfmt == V4L2_PIX_FMT_YUYV)
- ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
- else if (pixfmt == V4L2_PIX_FMT_UYVY)
- ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
- else
- return -EINVAL;
- }
- return 0;
-}
-static u32 ccdc_get_pixel_format(void)
-{
- struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
- u32 pixfmt;
-
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- if (alaw->enable)
- pixfmt = V4L2_PIX_FMT_SBGGR8;
- else
- pixfmt = V4L2_PIX_FMT_SBGGR16;
- else {
- if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
- pixfmt = V4L2_PIX_FMT_YUYV;
- else
- pixfmt = V4L2_PIX_FMT_UYVY;
- }
- return pixfmt;
-}
-static int ccdc_set_image_window(struct v4l2_rect *win)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.win = *win;
- else
- ccdc_cfg.ycbcr.win = *win;
- return 0;
-}
-
-static void ccdc_get_image_window(struct v4l2_rect *win)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- *win = ccdc_cfg.bayer.win;
- else
- *win = ccdc_cfg.ycbcr.win;
-}
-
-static unsigned int ccdc_get_line_length(void)
-{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int len;
-
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- if ((config_params->alaw.enable) ||
- (config_params->data_sz == CCDC_DATA_8BITS))
- len = ccdc_cfg.bayer.win.width;
- else
- len = ccdc_cfg.bayer.win.width * 2;
- } else
- len = ccdc_cfg.ycbcr.win.width * 2;
- return ALIGN(len, 32);
-}
-
-static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.frm_fmt = frm_fmt;
- else
- ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
- return 0;
-}
-
-static enum ccdc_frmfmt ccdc_get_frame_format(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- return ccdc_cfg.bayer.frm_fmt;
- else
- return ccdc_cfg.ycbcr.frm_fmt;
-}
-
-static int ccdc_getfid(void)
-{
- return (regr(MODESET) >> 15) & 1;
-}
-
-/* misc operations */
-static inline void ccdc_setfbaddr(unsigned long addr)
-{
- regw((addr >> 21) & 0x007f, STADRH);
- regw((addr >> 5) & 0x0ffff, STADRL);
-}
-
-static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
-{
- ccdc_cfg.if_type = params->if_type;
-
- switch (params->if_type) {
- case VPFE_BT656:
- case VPFE_YCBCR_SYNC_16:
- case VPFE_YCBCR_SYNC_8:
- ccdc_cfg.ycbcr.vd_pol = params->vdpol;
- ccdc_cfg.ycbcr.hd_pol = params->hdpol;
- break;
- default:
- /* TODO add support for raw bayer here */
- return -EINVAL;
- }
- return 0;
-}
-
-static struct ccdc_hw_device ccdc_hw_dev = {
- .name = "DM355 CCDC",
- .owner = THIS_MODULE,
- .hw_ops = {
- .open = ccdc_open,
- .close = ccdc_close,
- .enable = ccdc_enable,
- .enable_out_to_sdram = ccdc_enable_output_to_sdram,
- .set_hw_if_params = ccdc_set_hw_if_params,
- .set_params = ccdc_set_params,
- .configure = ccdc_configure,
- .set_buftype = ccdc_set_buftype,
- .get_buftype = ccdc_get_buftype,
- .enum_pix = ccdc_enum_pix,
- .set_pixel_format = ccdc_set_pixel_format,
- .get_pixel_format = ccdc_get_pixel_format,
- .set_frame_format = ccdc_set_frame_format,
- .get_frame_format = ccdc_get_frame_format,
- .set_image_window = ccdc_set_image_window,
- .get_image_window = ccdc_get_image_window,
- .get_line_length = ccdc_get_line_length,
- .setfbaddr = ccdc_setfbaddr,
- .getfid = ccdc_getfid,
- },
-};
-
-static int __init dm355_ccdc_probe(struct platform_device *pdev)
-{
- void (*setup_pinmux)(void);
- struct resource *res;
- int status = 0;
-
- /*
- * first try to register with vpfe. If not correct platform, then we
- * don't have to iomap
- */
- status = vpfe_register_ccdc_device(&ccdc_hw_dev);
- if (status < 0)
- return status;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- status = -ENODEV;
- goto fail_nores;
- }
-
- res = request_mem_region(res->start, resource_size(res), res->name);
- if (!res) {
- status = -EBUSY;
- goto fail_nores;
- }
-
- ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
- if (!ccdc_cfg.base_addr) {
- status = -ENOMEM;
- goto fail_nomem;
- }
-
- /* Get and enable Master clock */
- ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(ccdc_cfg.mclk)) {
- status = PTR_ERR(ccdc_cfg.mclk);
- goto fail_nomap;
- }
- if (clk_enable(ccdc_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Get and enable Slave clock */
- ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
- if (IS_ERR(ccdc_cfg.sclk)) {
- status = PTR_ERR(ccdc_cfg.sclk);
- goto fail_mclk;
- }
- if (clk_enable(ccdc_cfg.sclk)) {
- status = -ENODEV;
- goto fail_sclk;
- }
-
- /* Platform data holds setup_pinmux function ptr */
- if (NULL == pdev->dev.platform_data) {
- status = -ENODEV;
- goto fail_sclk;
- }
- setup_pinmux = pdev->dev.platform_data;
- /*
- * setup Mux configuration for ccdc which may be different for
- * different SoCs using this CCDC
- */
- setup_pinmux();
- ccdc_cfg.dev = &pdev->dev;
- printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
- return 0;
-fail_sclk:
- clk_put(ccdc_cfg.sclk);
-fail_mclk:
- clk_put(ccdc_cfg.mclk);
-fail_nomap:
- iounmap(ccdc_cfg.base_addr);
-fail_nomem:
- release_mem_region(res->start, resource_size(res));
-fail_nores:
- vpfe_unregister_ccdc_device(&ccdc_hw_dev);
- return status;
-}
-
-static int dm355_ccdc_remove(struct platform_device *pdev)
-{
- struct resource *res;
-
- clk_put(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.sclk);
- iounmap(ccdc_cfg.base_addr);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res)
- release_mem_region(res->start, resource_size(res));
- vpfe_unregister_ccdc_device(&ccdc_hw_dev);
- return 0;
-}
-
-static struct platform_driver dm355_ccdc_driver = {
- .driver = {
- .name = "dm355_ccdc",
- .owner = THIS_MODULE,
- },
- .remove = __devexit_p(dm355_ccdc_remove),
- .probe = dm355_ccdc_probe,
-};
-
-module_platform_driver(dm355_ccdc_driver);
diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c
deleted file mode 100644
index 9303fe553b07..000000000000
--- a/drivers/media/video/davinci/dm644x_ccdc.c
+++ /dev/null
@@ -1,1081 +0,0 @@
-/*
- * Copyright (C) 2006-2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- *
- * CCDC hardware module for DM6446
- * ------------------------------
- *
- * This module is for configuring CCD controller of DM6446 VPFE to capture
- * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
- * such as Defect Pixel Correction, Color Space Conversion etc to
- * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This
- * module also allows application to configure individual
- * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
- * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header
- * files. The setparams() API is called by vpfe_capture driver
- * to configure module parameters. This file is named DM644x so that other
- * variants such DM6443 may be supported using the same module.
- *
- * TODO: Test Raw bayer parameter settings and bayer capture
- * Split module parameter structure to module specific ioctl structs
- * investigate if enum used for user space type definition
- * to be replaced by #defines or integer
- */
-#include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/videodev2.h>
-#include <linux/gfp.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/module.h>
-
-#include <media/davinci/dm644x_ccdc.h>
-#include <media/davinci/vpss.h>
-
-#include "dm644x_ccdc_regs.h"
-#include "ccdc_hw_device.h"
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("CCDC Driver for DM6446");
-MODULE_AUTHOR("Texas Instruments");
-
-static struct ccdc_oper_config {
- struct device *dev;
- /* CCDC interface type */
- enum vpfe_hw_if_type if_type;
- /* Raw Bayer configuration */
- struct ccdc_params_raw bayer;
- /* YCbCr configuration */
- struct ccdc_params_ycbcr ycbcr;
- /* Master clock */
- struct clk *mclk;
- /* slave clock */
- struct clk *sclk;
- /* ccdc base address */
- void __iomem *base_addr;
-} ccdc_cfg = {
- /* Raw configurations */
- .bayer = {
- .pix_fmt = CCDC_PIXFMT_RAW,
- .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
- .win = CCDC_WIN_VGA,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .config_params = {
- .data_sz = CCDC_DATA_10BITS,
- },
- },
- .ycbcr = {
- .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
- .frm_fmt = CCDC_FRMFMT_INTERLACED,
- .win = CCDC_WIN_PAL,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .bt656_enable = 1,
- .pix_order = CCDC_PIXORDER_CBYCRY,
- .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
- },
-};
-
-#define CCDC_MAX_RAW_YUV_FORMATS 2
-
-/* Raw Bayer formats */
-static u32 ccdc_raw_bayer_pix_formats[] =
- {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
-
-/* Raw YUV formats */
-static u32 ccdc_raw_yuv_pix_formats[] =
- {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
-
-/* CCDC Save/Restore context */
-static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)];
-
-/* register access routines */
-static inline u32 regr(u32 offset)
-{
- return __raw_readl(ccdc_cfg.base_addr + offset);
-}
-
-static inline void regw(u32 val, u32 offset)
-{
- __raw_writel(val, ccdc_cfg.base_addr + offset);
-}
-
-static void ccdc_enable(int flag)
-{
- regw(flag, CCDC_PCR);
-}
-
-static void ccdc_enable_vport(int flag)
-{
- if (flag)
- /* enable video port */
- regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG);
- else
- regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG);
-}
-
-/*
- * ccdc_setwin()
- * This function will configure the window size
- * to be capture in CCDC reg
- */
-void ccdc_setwin(struct v4l2_rect *image_win,
- enum ccdc_frmfmt frm_fmt,
- int ppc)
-{
- int horz_start, horz_nr_pixels;
- int vert_start, vert_nr_lines;
- int val = 0, mid_img = 0;
-
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
- /*
- * ppc - per pixel count. indicates how many pixels per cell
- * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
- * raw capture this is 1
- */
- horz_start = image_win->left << (ppc - 1);
- horz_nr_pixels = (image_win->width << (ppc - 1)) - 1;
- regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels,
- CCDC_HORZ_INFO);
-
- vert_start = image_win->top;
-
- if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
- vert_nr_lines = (image_win->height >> 1) - 1;
- vert_start >>= 1;
- /* Since first line doesn't have any data */
- vert_start += 1;
- /* configure VDINT0 */
- val = (vert_start << CCDC_VDINT_VDINT0_SHIFT);
- regw(val, CCDC_VDINT);
-
- } else {
- /* Since first line doesn't have any data */
- vert_start += 1;
- vert_nr_lines = image_win->height - 1;
- /*
- * configure VDINT0 and VDINT1. VDINT1 will be at half
- * of image height
- */
- mid_img = vert_start + (image_win->height / 2);
- val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) |
- (mid_img & CCDC_VDINT_VDINT1_MASK);
- regw(val, CCDC_VDINT);
-
- }
- regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
- CCDC_VERT_START);
- regw(vert_nr_lines, CCDC_VERT_LINES);
- dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
-}
-
-static void ccdc_readregs(void)
-{
- unsigned int val = 0;
-
- val = regr(CCDC_ALAW);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val);
- val = regr(CCDC_CLAMP);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val);
- val = regr(CCDC_DCSUB);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val);
- val = regr(CCDC_BLKCMP);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val);
- val = regr(CCDC_FPC_ADDR);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val);
- val = regr(CCDC_FPC);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val);
- val = regr(CCDC_FMTCFG);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val);
- val = regr(CCDC_COLPTN);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val);
- val = regr(CCDC_FMT_HORZ);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val);
- val = regr(CCDC_FMT_VERT);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val);
- val = regr(CCDC_HSIZE_OFF);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
- val = regr(CCDC_SDOFST);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val);
- val = regr(CCDC_VP_OUT);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val);
- val = regr(CCDC_SYN_MODE);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val);
- val = regr(CCDC_HORZ_INFO);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val);
- val = regr(CCDC_VERT_START);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val);
- val = regr(CCDC_VERT_LINES);
- dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
-}
-
-static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
-{
- if (ccdcparam->alaw.enable) {
- if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) ||
- (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) ||
- (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) {
- dev_dbg(ccdc_cfg.dev, "\nInvalid data line select");
- return -1;
- }
- }
- return 0;
-}
-
-static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params)
-{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int *fpc_virtaddr = NULL;
- unsigned int *fpc_physaddr = NULL;
-
- memcpy(config_params, raw_params, sizeof(*raw_params));
- /*
- * allocate memory for fault pixel table and copy the user
- * values to the table
- */
- if (!config_params->fault_pxl.enable)
- return 0;
-
- fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
- fpc_virtaddr = (unsigned int *)phys_to_virt(
- (unsigned long)fpc_physaddr);
- /*
- * Allocate memory for FPC table if current
- * FPC table buffer is not big enough to
- * accommodate FPC Number requested
- */
- if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) {
- if (fpc_physaddr != NULL) {
- free_pages((unsigned long)fpc_physaddr,
- get_order
- (config_params->fault_pxl.fp_num *
- FP_NUM_BYTES));
- }
-
- /* Allocate memory for FPC table */
- fpc_virtaddr =
- (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA,
- get_order(raw_params->
- fault_pxl.fp_num *
- FP_NUM_BYTES));
-
- if (fpc_virtaddr == NULL) {
- dev_dbg(ccdc_cfg.dev,
- "\nUnable to allocate memory for FPC");
- return -EFAULT;
- }
- fpc_physaddr =
- (unsigned int *)virt_to_phys((void *)fpc_virtaddr);
- }
-
- /* Copy number of fault pixels and FPC table */
- config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num;
- if (copy_from_user(fpc_virtaddr,
- (void __user *)raw_params->fault_pxl.fpc_table_addr,
- config_params->fault_pxl.fp_num * FP_NUM_BYTES)) {
- dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed");
- return -EFAULT;
- }
- config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr;
- return 0;
-}
-
-static int ccdc_close(struct device *dev)
-{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL;
-
- fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr;
-
- if (fpc_physaddr != NULL) {
- fpc_virtaddr = (unsigned int *)
- phys_to_virt((unsigned long)fpc_physaddr);
- free_pages((unsigned long)fpc_virtaddr,
- get_order(config_params->fault_pxl.fp_num *
- FP_NUM_BYTES));
- }
- return 0;
-}
-
-/*
- * ccdc_restore_defaults()
- * This function will write defaults to all CCDC registers
- */
-static void ccdc_restore_defaults(void)
-{
- int i;
-
- /* disable CCDC */
- ccdc_enable(0);
- /* set all registers to default value */
- for (i = 4; i <= 0x94; i += 4)
- regw(0, i);
- regw(CCDC_NO_CULLING, CCDC_CULLING);
- regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW);
-}
-
-static int ccdc_open(struct device *device)
-{
- ccdc_restore_defaults();
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_enable_vport(1);
- return 0;
-}
-
-static void ccdc_sbl_reset(void)
-{
- vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
-}
-
-/* Parameter operations */
-static int ccdc_set_params(void __user *params)
-{
- struct ccdc_config_params_raw ccdc_raw_params;
- int x;
-
- if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
- return -EINVAL;
-
- x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
- if (x) {
- dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying"
- "ccdc params, %d\n", x);
- return -EFAULT;
- }
-
- if (!validate_ccdc_param(&ccdc_raw_params)) {
- if (!ccdc_update_raw_params(&ccdc_raw_params))
- return 0;
- }
- return -EINVAL;
-}
-
-/*
- * ccdc_config_ycbcr()
- * This function will configure CCDC for YCbCr video capture
- */
-void ccdc_config_ycbcr(void)
-{
- struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
- u32 syn_mode;
-
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
- /*
- * first restore the CCDC registers to default values
- * This is important since we assume default values to be set in
- * a lot of registers that we didn't touch
- */
- ccdc_restore_defaults();
-
- /*
- * configure pixel format, frame format, configure video frame
- * format, enable output to SDRAM, enable internal timing generator
- * and 8bit pack mode
- */
- syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) <<
- CCDC_SYN_MODE_INPMOD_SHIFT) |
- ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) <<
- CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE |
- CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE);
-
- /* setup BT.656 sync mode */
- if (params->bt656_enable) {
- regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF);
-
- /*
- * configure the FID, VD, HD pin polarity,
- * fld,hd pol positive, vd negative, 8-bit data
- */
- syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE;
- if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
- syn_mode |= CCDC_SYN_MODE_10BITS;
- else
- syn_mode |= CCDC_SYN_MODE_8BITS;
- } else {
- /* y/c external sync mode */
- syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
- CCDC_FID_POL_SHIFT) |
- ((params->hd_pol & CCDC_HD_POL_MASK) <<
- CCDC_HD_POL_SHIFT) |
- ((params->vd_pol & CCDC_VD_POL_MASK) <<
- CCDC_VD_POL_SHIFT));
- }
- regw(syn_mode, CCDC_SYN_MODE);
-
- /* configure video window */
- ccdc_setwin(&params->win, params->frm_fmt, 2);
-
- /*
- * configure the order of y cb cr in SDRAM, and disable latch
- * internal register on vsync
- */
- if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
- regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
- CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT,
- CCDC_CCDCFG);
- else
- regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
- CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
-
- /*
- * configure the horizontal line offset. This should be a
- * on 32 byte boundary. So clear LSB 5 bits
- */
- regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF);
-
- /* configure the memory line offset */
- if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
- /* two fields are interleaved in memory */
- regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
-
- ccdc_sbl_reset();
- dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
-}
-
-static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
-{
- u32 val;
-
- if (!bclamp->enable) {
- /* configure DCSub */
- val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
- regw(val, CCDC_DCSUB);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val);
- regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n");
- return;
- }
- /*
- * Configure gain, Start pixel, No of line to be avg,
- * No of pixel/line to be avg, & Enable the Black clamping
- */
- val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) |
- ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) <<
- CCDC_BLK_ST_PXL_SHIFT) |
- ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) <<
- CCDC_BLK_SAMPLE_LINE_SHIFT) |
- ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
- CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
- regw(val, CCDC_CLAMP);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val);
- /* If Black clamping is enable then make dcsub 0 */
- regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n");
-}
-
-static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
-{
- u32 val;
-
- val = ((bcomp->b & CCDC_BLK_COMP_MASK) |
- ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_GB_COMP_SHIFT) |
- ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_GR_COMP_SHIFT) |
- ((bcomp->r & CCDC_BLK_COMP_MASK) <<
- CCDC_BLK_COMP_R_COMP_SHIFT));
- regw(val, CCDC_BLKCMP);
-}
-
-static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc)
-{
- u32 val;
-
- /* Initially disable FPC */
- val = CCDC_FPC_DISABLE;
- regw(val, CCDC_FPC);
-
- if (!fpc->enable)
- return;
-
- /* Configure Fault pixel if needed */
- regw(fpc->fpc_table_addr, CCDC_FPC_ADDR);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n",
- (fpc->fpc_table_addr));
- /* Write the FPC params with FPC disable */
- val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK;
- regw(val, CCDC_FPC);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
- /* read the FPC register */
- val = regr(CCDC_FPC) | CCDC_FPC_ENABLE;
- regw(val, CCDC_FPC);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val);
-}
-
-/*
- * ccdc_config_raw()
- * This function will configure CCDC for Raw capture mode
- */
-void ccdc_config_raw(void)
-{
- struct ccdc_params_raw *params = &ccdc_cfg.bayer;
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int syn_mode = 0;
- unsigned int val;
-
- dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
-
- /* Reset CCDC */
- ccdc_restore_defaults();
-
- /* Disable latching function registers on VSYNC */
- regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
-
- /*
- * Configure the vertical sync polarity(SYN_MODE.VDPOL),
- * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
- * (SYN_MODE.FLDPOL), frame format(progressive or interlace),
- * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
- * SDRAM, enable internal timing generator
- */
- syn_mode =
- (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
- ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
- ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
- ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
- ((config_params->data_sz & CCDC_DATA_SZ_MASK) <<
- CCDC_DATA_SZ_SHIFT) |
- ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) |
- CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE);
-
- /* Enable and configure aLaw register if needed */
- if (config_params->alaw.enable) {
- val = ((config_params->alaw.gama_wd &
- CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE);
- regw(val, CCDC_ALAW);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
- }
-
- /* Configure video window */
- ccdc_setwin(&params->win, params->frm_fmt, CCDC_PPC_RAW);
-
- /* Configure Black Clamp */
- ccdc_config_black_clamp(&config_params->blk_clamp);
-
- /* Configure Black level compensation */
- ccdc_config_black_compense(&config_params->blk_comp);
-
- /* Configure Fault Pixel Correction */
- ccdc_config_fpc(&config_params->fault_pxl);
-
- /* If data size is 8 bit then pack the data */
- if ((config_params->data_sz == CCDC_DATA_8BITS) ||
- config_params->alaw.enable)
- syn_mode |= CCDC_DATA_PACK_ENABLE;
-
-#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE
- /* enable video port */
- val = CCDC_ENABLE_VIDEO_PORT;
-#else
- /* disable video port */
- val = CCDC_DISABLE_VIDEO_PORT;
-#endif
-
- if (config_params->data_sz == CCDC_DATA_8BITS)
- val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK)
- << CCDC_FMTCFG_VPIN_SHIFT;
- else
- val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK)
- << CCDC_FMTCFG_VPIN_SHIFT;
- /* Write value in FMTCFG */
- regw(val, CCDC_FMTCFG);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val);
- /* Configure the color pattern according to mt9t001 sensor */
- regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
- /*
- * Configure Data formatter(Video port) pixel selection
- * (FMT_HORZ, FMT_VERT)
- */
- val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) <<
- CCDC_FMT_HORZ_FMTSPH_SHIFT) |
- (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
- regw(val, CCDC_FMT_HORZ);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
- val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
- << CCDC_FMT_VERT_FMTSLV_SHIFT;
- if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
- val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK;
- else
- val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
-
- dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n",
- params->win.height);
- regw(val, CCDC_FMT_VERT);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val);
-
- dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)...");
-
- /*
- * Configure Horizontal offset register. If pack 8 is enabled then
- * 1 pixel will take 1 byte
- */
- if ((config_params->data_sz == CCDC_DATA_8BITS) ||
- config_params->alaw.enable)
- regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) &
- CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF);
- else
- /* else one pixel will take 2 byte */
- regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) +
- CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK,
- CCDC_HSIZE_OFF);
-
- /* Set value for SDOFST */
- if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
- if (params->image_invert_enable) {
- /* For intelace inverse mode */
- regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n");
- }
-
- else {
- /* For intelace non inverse mode */
- regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n");
- }
- } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
- regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n");
- }
-
- /*
- * Configure video port pixel selection (VPOUT)
- * Here -1 is to make the height value less than FMT_VERT.FMTLNV
- */
- if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
- val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK))
- << CCDC_VP_OUT_VERT_NUM_SHIFT;
- else
- val =
- ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) -
- 1) & CCDC_VP_OUT_VERT_NUM_MASK)) <<
- CCDC_VP_OUT_VERT_NUM_SHIFT;
-
- val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK)
- << CCDC_VP_OUT_HORZ_NUM_SHIFT;
- val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
- regw(val, CCDC_VP_OUT);
-
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val);
- regw(syn_mode, CCDC_SYN_MODE);
- dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
-
- ccdc_sbl_reset();
- dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
- ccdc_readregs();
-}
-
-static int ccdc_configure(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_config_raw();
- else
- ccdc_config_ycbcr();
- return 0;
-}
-
-static int ccdc_set_buftype(enum ccdc_buftype buf_type)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.buf_type = buf_type;
- else
- ccdc_cfg.ycbcr.buf_type = buf_type;
- return 0;
-}
-
-static enum ccdc_buftype ccdc_get_buftype(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- return ccdc_cfg.bayer.buf_type;
- return ccdc_cfg.ycbcr.buf_type;
-}
-
-static int ccdc_enum_pix(u32 *pix, int i)
-{
- int ret = -EINVAL;
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
- *pix = ccdc_raw_bayer_pix_formats[i];
- ret = 0;
- }
- } else {
- if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
- *pix = ccdc_raw_yuv_pix_formats[i];
- ret = 0;
- }
- }
- return ret;
-}
-
-static int ccdc_set_pixel_format(u32 pixfmt)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
- if (pixfmt == V4L2_PIX_FMT_SBGGR8)
- ccdc_cfg.bayer.config_params.alaw.enable = 1;
- else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
- return -EINVAL;
- } else {
- if (pixfmt == V4L2_PIX_FMT_YUYV)
- ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
- else if (pixfmt == V4L2_PIX_FMT_UYVY)
- ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
- else
- return -EINVAL;
- }
- return 0;
-}
-
-static u32 ccdc_get_pixel_format(void)
-{
- struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
- u32 pixfmt;
-
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- if (alaw->enable)
- pixfmt = V4L2_PIX_FMT_SBGGR8;
- else
- pixfmt = V4L2_PIX_FMT_SBGGR16;
- else {
- if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
- pixfmt = V4L2_PIX_FMT_YUYV;
- else
- pixfmt = V4L2_PIX_FMT_UYVY;
- }
- return pixfmt;
-}
-
-static int ccdc_set_image_window(struct v4l2_rect *win)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.win = *win;
- else
- ccdc_cfg.ycbcr.win = *win;
- return 0;
-}
-
-static void ccdc_get_image_window(struct v4l2_rect *win)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- *win = ccdc_cfg.bayer.win;
- else
- *win = ccdc_cfg.ycbcr.win;
-}
-
-static unsigned int ccdc_get_line_length(void)
-{
- struct ccdc_config_params_raw *config_params =
- &ccdc_cfg.bayer.config_params;
- unsigned int len;
-
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
- if ((config_params->alaw.enable) ||
- (config_params->data_sz == CCDC_DATA_8BITS))
- len = ccdc_cfg.bayer.win.width;
- else
- len = ccdc_cfg.bayer.win.width * 2;
- } else
- len = ccdc_cfg.ycbcr.win.width * 2;
- return ALIGN(len, 32);
-}
-
-static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- ccdc_cfg.bayer.frm_fmt = frm_fmt;
- else
- ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
- return 0;
-}
-
-static enum ccdc_frmfmt ccdc_get_frame_format(void)
-{
- if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
- return ccdc_cfg.bayer.frm_fmt;
- else
- return ccdc_cfg.ycbcr.frm_fmt;
-}
-
-static int ccdc_getfid(void)
-{
- return (regr(CCDC_SYN_MODE) >> 15) & 1;
-}
-
-/* misc operations */
-static inline void ccdc_setfbaddr(unsigned long addr)
-{
- regw(addr & 0xffffffe0, CCDC_SDR_ADDR);
-}
-
-static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
-{
- ccdc_cfg.if_type = params->if_type;
-
- switch (params->if_type) {
- case VPFE_BT656:
- case VPFE_YCBCR_SYNC_16:
- case VPFE_YCBCR_SYNC_8:
- case VPFE_BT656_10BIT:
- ccdc_cfg.ycbcr.vd_pol = params->vdpol;
- ccdc_cfg.ycbcr.hd_pol = params->hdpol;
- break;
- default:
- /* TODO add support for raw bayer here */
- return -EINVAL;
- }
- return 0;
-}
-
-static void ccdc_save_context(void)
-{
- ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR);
- ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE);
- ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID);
- ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES);
- ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO);
- ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START);
- ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES);
- ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING);
- ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF);
- ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST);
- ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR);
- ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP);
- ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB);
- ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN);
- ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP);
- ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC);
- ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR);
- ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT);
- ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW);
- ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF);
- ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG);
- ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG);
- ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ);
- ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT);
- ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0);
- ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1);
- ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2);
- ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3);
- ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4);
- ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5);
- ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6);
- ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7);
- ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0);
- ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1);
- ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0);
- ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1);
- ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT);
-}
-
-static void ccdc_restore_context(void)
-{
- regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE);
- regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID);
- regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES);
- regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO);
- regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START);
- regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES);
- regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING);
- regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF);
- regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST);
- regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR);
- regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP);
- regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB);
- regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN);
- regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP);
- regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC);
- regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR);
- regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT);
- regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW);
- regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF);
- regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG);
- regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG);
- regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ);
- regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT);
- regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0);
- regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1);
- regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2);
- regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3);
- regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4);
- regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5);
- regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6);
- regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7);
- regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0);
- regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1);
- regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0);
- regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1);
- regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT);
- regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR);
-}
-static struct ccdc_hw_device ccdc_hw_dev = {
- .name = "DM6446 CCDC",
- .owner = THIS_MODULE,
- .hw_ops = {
- .open = ccdc_open,
- .close = ccdc_close,
- .reset = ccdc_sbl_reset,
- .enable = ccdc_enable,
- .set_hw_if_params = ccdc_set_hw_if_params,
- .set_params = ccdc_set_params,
- .configure = ccdc_configure,
- .set_buftype = ccdc_set_buftype,
- .get_buftype = ccdc_get_buftype,
- .enum_pix = ccdc_enum_pix,
- .set_pixel_format = ccdc_set_pixel_format,
- .get_pixel_format = ccdc_get_pixel_format,
- .set_frame_format = ccdc_set_frame_format,
- .get_frame_format = ccdc_get_frame_format,
- .set_image_window = ccdc_set_image_window,
- .get_image_window = ccdc_get_image_window,
- .get_line_length = ccdc_get_line_length,
- .setfbaddr = ccdc_setfbaddr,
- .getfid = ccdc_getfid,
- },
-};
-
-static int __init dm644x_ccdc_probe(struct platform_device *pdev)
-{
- struct resource *res;
- int status = 0;
-
- /*
- * first try to register with vpfe. If not correct platform, then we
- * don't have to iomap
- */
- status = vpfe_register_ccdc_device(&ccdc_hw_dev);
- if (status < 0)
- return status;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- status = -ENODEV;
- goto fail_nores;
- }
-
- res = request_mem_region(res->start, resource_size(res), res->name);
- if (!res) {
- status = -EBUSY;
- goto fail_nores;
- }
-
- ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
- if (!ccdc_cfg.base_addr) {
- status = -ENOMEM;
- goto fail_nomem;
- }
-
- /* Get and enable Master clock */
- ccdc_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(ccdc_cfg.mclk)) {
- status = PTR_ERR(ccdc_cfg.mclk);
- goto fail_nomap;
- }
- if (clk_enable(ccdc_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Get and enable Slave clock */
- ccdc_cfg.sclk = clk_get(&pdev->dev, "slave");
- if (IS_ERR(ccdc_cfg.sclk)) {
- status = PTR_ERR(ccdc_cfg.sclk);
- goto fail_mclk;
- }
- if (clk_enable(ccdc_cfg.sclk)) {
- status = -ENODEV;
- goto fail_sclk;
- }
- ccdc_cfg.dev = &pdev->dev;
- printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
- return 0;
-fail_sclk:
- clk_put(ccdc_cfg.sclk);
-fail_mclk:
- clk_put(ccdc_cfg.mclk);
-fail_nomap:
- iounmap(ccdc_cfg.base_addr);
-fail_nomem:
- release_mem_region(res->start, resource_size(res));
-fail_nores:
- vpfe_unregister_ccdc_device(&ccdc_hw_dev);
- return status;
-}
-
-static int dm644x_ccdc_remove(struct platform_device *pdev)
-{
- struct resource *res;
-
- clk_put(ccdc_cfg.mclk);
- clk_put(ccdc_cfg.sclk);
- iounmap(ccdc_cfg.base_addr);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res)
- release_mem_region(res->start, resource_size(res));
- vpfe_unregister_ccdc_device(&ccdc_hw_dev);
- return 0;
-}
-
-static int dm644x_ccdc_suspend(struct device *dev)
-{
- /* Save CCDC context */
- ccdc_save_context();
- /* Disable CCDC */
- ccdc_enable(0);
- /* Disable both master and slave clock */
- clk_disable(ccdc_cfg.mclk);
- clk_disable(ccdc_cfg.sclk);
-
- return 0;
-}
-
-static int dm644x_ccdc_resume(struct device *dev)
-{
- /* Enable both master and slave clock */
- clk_enable(ccdc_cfg.mclk);
- clk_enable(ccdc_cfg.sclk);
- /* Restore CCDC context */
- ccdc_restore_context();
-
- return 0;
-}
-
-static const struct dev_pm_ops dm644x_ccdc_pm_ops = {
- .suspend = dm644x_ccdc_suspend,
- .resume = dm644x_ccdc_resume,
-};
-
-static struct platform_driver dm644x_ccdc_driver = {
- .driver = {
- .name = "dm644x_ccdc",
- .owner = THIS_MODULE,
- .pm = &dm644x_ccdc_pm_ops,
- },
- .remove = __devexit_p(dm644x_ccdc_remove),
- .probe = dm644x_ccdc_probe,
-};
-
-module_platform_driver(dm644x_ccdc_driver);
diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c
deleted file mode 100644
index 5278fe7d6d0c..000000000000
--- a/drivers/media/video/davinci/isif.c
+++ /dev/null
@@ -1,1162 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- *
- * Image Sensor Interface (ISIF) driver
- *
- * This driver is for configuring the ISIF IP available on DM365 or any other
- * TI SoCs. This is used for capturing yuv or bayer video or image data
- * from a decoder or sensor. This IP is similar to the CCDC IP on DM355
- * and DM6446, but with enhanced or additional ip blocks. The driver
- * configures the ISIF upon commands from the vpfe bridge driver through
- * ccdc_hw_device interface.
- *
- * TODO: 1) Raw bayer parameter settings and bayer capture
- * 2) Add support for control ioctl
- */
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/videodev2.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/module.h>
-
-#include <mach/mux.h>
-
-#include <media/davinci/isif.h>
-#include <media/davinci/vpss.h>
-
-#include "isif_regs.h"
-#include "ccdc_hw_device.h"
-
-/* Defaults for module configuration parameters */
-static struct isif_config_params_raw isif_config_defaults = {
- .linearize = {
- .en = 0,
- .corr_shft = ISIF_NO_SHIFT,
- .scale_fact = {1, 0},
- },
- .df_csc = {
- .df_or_csc = 0,
- .csc = {
- .en = 0,
- },
- },
- .dfc = {
- .en = 0,
- },
- .bclamp = {
- .en = 0,
- },
- .gain_offset = {
- .gain = {
- .r_ye = {1, 0},
- .gr_cy = {1, 0},
- .gb_g = {1, 0},
- .b_mg = {1, 0},
- },
- },
- .culling = {
- .hcpat_odd = 0xff,
- .hcpat_even = 0xff,
- .vcpat = 0xff,
- },
- .compress = {
- .alg = ISIF_ALAW,
- },
-};
-
-/* ISIF operation configuration */
-static struct isif_oper_config {
- struct device *dev;
- enum vpfe_hw_if_type if_type;
- struct isif_ycbcr_config ycbcr;
- struct isif_params_raw bayer;
- enum isif_data_pack data_pack;
- /* Master clock */
- struct clk *mclk;
- /* ISIF base address */
- void __iomem *base_addr;
- /* ISIF Linear Table 0 */
- void __iomem *linear_tbl0_addr;
- /* ISIF Linear Table 1 */
- void __iomem *linear_tbl1_addr;
-} isif_cfg = {
- .ycbcr = {
- .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
- .frm_fmt = CCDC_FRMFMT_INTERLACED,
- .win = ISIF_WIN_NTSC,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .pix_order = CCDC_PIXORDER_CBYCRY,
- .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
- },
- .bayer = {
- .pix_fmt = CCDC_PIXFMT_RAW,
- .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
- .win = ISIF_WIN_VGA,
- .fid_pol = VPFE_PINPOL_POSITIVE,
- .vd_pol = VPFE_PINPOL_POSITIVE,
- .hd_pol = VPFE_PINPOL_POSITIVE,
- .gain = {
- .r_ye = {1, 0},
- .gr_cy = {1, 0},
- .gb_g = {1, 0},
- .b_mg = {1, 0},
- },
- .cfa_pat = ISIF_CFA_PAT_MOSAIC,
- .data_msb = ISIF_BIT_MSB_11,
- .config_params = {
- .data_shift = ISIF_NO_SHIFT,
- .col_pat_field0 = {
- .olop = ISIF_GREEN_BLUE,
- .olep = ISIF_BLUE,
- .elop = ISIF_RED,
- .elep = ISIF_GREEN_RED,
- },
- .col_pat_field1 = {
- .olop = ISIF_GREEN_BLUE,
- .olep = ISIF_BLUE,
- .elop = ISIF_RED,
- .elep = ISIF_GREEN_RED,
- },
- .test_pat_gen = 0,
- },
- },
- .data_pack = ISIF_DATA_PACK8,
-};
-
-/* Raw Bayer formats */
-static const u32 isif_raw_bayer_pix_formats[] = {
- V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
-
-/* Raw YUV formats */
-static const u32 isif_raw_yuv_pix_formats[] = {
- V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
-
-/* register access routines */
-static inline u32 regr(u32 offset)
-{
- return __raw_readl(isif_cfg.base_addr + offset);
-}
-
-static inline void regw(u32 val, u32 offset)
-{
- __raw_writel(val, isif_cfg.base_addr + offset);
-}
-
-/* reg_modify() - read, modify and write register */
-static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
-{
- u32 new_val = (regr(offset) & ~mask) | (val & mask);
-
- regw(new_val, offset);
- return new_val;
-}
-
-static inline void regw_lin_tbl(u32 val, u32 offset, int i)
-{
- if (!i)
- __raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
- else
- __raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
-}
-
-static void isif_disable_all_modules(void)
-{
- /* disable BC */
- regw(0, CLAMPCFG);
- /* disable vdfc */
- regw(0, DFCCTL);
- /* disable CSC */
- regw(0, CSCCTL);
- /* disable linearization */
- regw(0, LINCFG0);
- /* disable other modules here as they are supported */
-}
-
-static void isif_enable(int en)
-{
- if (!en) {
- /* Before disable isif, disable all ISIF modules */
- isif_disable_all_modules();
- /*
- * wait for next VD. Assume lowest scan rate is 12 Hz. So
- * 100 msec delay is good enough
- */
- msleep(100);
- }
- reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
-}
-
-static void isif_enable_output_to_sdram(int en)
-{
- reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
-}
-
-static void isif_config_culling(struct isif_cul *cul)
-{
- u32 val;
-
- /* Horizontal pattern */
- val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
- regw(val, CULH);
-
- /* vertical pattern */
- regw(cul->vcpat, CULV);
-
- /* LPF */
- reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
- cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
-}
-
-static void isif_config_gain_offset(void)
-{
- struct isif_gain_offsets_adj *gain_off_p =
- &isif_cfg.bayer.config_params.gain_offset;
- u32 val;
-
- val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
- (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
- (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
- (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
- (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
- (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
-
- reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
-
- val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
- gain_off_p->gain.r_ye.decimal;
- regw(val, CRGAIN);
-
- val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
- gain_off_p->gain.gr_cy.decimal;
- regw(val, CGRGAIN);
-
- val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
- gain_off_p->gain.gb_g.decimal;
- regw(val, CGBGAIN);
-
- val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
- gain_off_p->gain.b_mg.decimal;
- regw(val, CBGAIN);
-
- regw(gain_off_p->offset, COFSTA);
-}
-
-static void isif_restore_defaults(void)
-{
- enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
-
- dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
- isif_cfg.bayer.config_params = isif_config_defaults;
- /* Enable clock to ISIF, IPIPEIF and BL */
- vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
- vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
- vpss_enable_clock(VPSS_BL_CLOCK, 1);
- /* Set default offset and gain */
- isif_config_gain_offset();
- vpss_select_ccdc_source(source);
- dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
-}
-
-static int isif_open(struct device *device)
-{
- isif_restore_defaults();
- return 0;
-}
-
-/* This function will configure the window size to be capture in ISIF reg */
-static void isif_setwin(struct v4l2_rect *image_win,
- enum ccdc_frmfmt frm_fmt, int ppc)
-{
- int horz_start, horz_nr_pixels;
- int vert_start, vert_nr_lines;
- int mid_img = 0;
-
- dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
- /*
- * ppc - per pixel count. indicates how many pixels per cell
- * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
- * raw capture this is 1
- */
- horz_start = image_win->left << (ppc - 1);
- horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
-
- /* Writing the horizontal info into the registers */
- regw(horz_start & START_PX_HOR_MASK, SPH);
- regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
- vert_start = image_win->top;
-
- if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
- vert_nr_lines = (image_win->height >> 1) - 1;
- vert_start >>= 1;
- /* To account for VD since line 0 doesn't have any data */
- vert_start += 1;
- } else {
- /* To account for VD since line 0 doesn't have any data */
- vert_start += 1;
- vert_nr_lines = image_win->height - 1;
- /* configure VDINT0 and VDINT1 */
- mid_img = vert_start + (image_win->height / 2);
- regw(mid_img, VDINT1);
- }
-
- regw(0, VDINT0);
- regw(vert_start & START_VER_ONE_MASK, SLV0);
- regw(vert_start & START_VER_TWO_MASK, SLV1);
- regw(vert_nr_lines & NUM_LINES_VER, LNV);
-}
-
-static void isif_config_bclamp(struct isif_black_clamp *bc)
-{
- u32 val;
-
- /*
- * DC Offset is always added to image data irrespective of bc enable
- * status
- */
- regw(bc->dc_offset, CLDCOFST);
-
- if (bc->en) {
- val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
-
- /* Enable BC and horizontal clamp caculation paramaters */
- val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
-
- regw(val, CLAMPCFG);
-
- if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
- /*
- * Window count for calculation
- * Base window selection
- * pixel limit
- * Horizontal size of window
- * vertical size of the window
- * Horizontal start position of the window
- * Vertical start position of the window
- */
- val = bc->horz.win_count_calc |
- ((!!bc->horz.base_win_sel_calc) <<
- ISIF_HORZ_BC_WIN_SEL_SHIFT) |
- ((!!bc->horz.clamp_pix_limit) <<
- ISIF_HORZ_BC_PIX_LIMIT_SHIFT) |
- (bc->horz.win_h_sz_calc <<
- ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) |
- (bc->horz.win_v_sz_calc <<
- ISIF_HORZ_BC_WIN_V_SIZE_SHIFT);
- regw(val, CLHWIN0);
-
- regw(bc->horz.win_start_h_calc, CLHWIN1);
- regw(bc->horz.win_start_v_calc, CLHWIN2);
- }
-
- /* vertical clamp caculation paramaters */
-
- /* Reset clamp value sel for previous line */
- val |=
- (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) |
- (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT);
- regw(val, CLVWIN0);
-
- /* Optical Black horizontal start position */
- regw(bc->vert.ob_start_h, CLVWIN1);
- /* Optical Black vertical start position */
- regw(bc->vert.ob_start_v, CLVWIN2);
- /* Optical Black vertical size for calculation */
- regw(bc->vert.ob_v_sz_calc, CLVWIN3);
- /* Vertical start position for BC subtraction */
- regw(bc->vert_start_sub, CLSV);
- }
-}
-
-static void isif_config_linearization(struct isif_linearize *linearize)
-{
- u32 val, i;
-
- if (!linearize->en) {
- regw(0, LINCFG0);
- return;
- }
-
- /* shift value for correction & enable linearization (set lsb) */
- val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1;
- regw(val, LINCFG0);
-
- /* Scale factor */
- val = ((!!linearize->scale_fact.integer) <<
- ISIF_LIN_SCALE_FACT_INTEG_SHIFT) |
- linearize->scale_fact.decimal;
- regw(val, LINCFG1);
-
- for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) {
- if (i % 2)
- regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1);
- else
- regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0);
- }
-}
-
-static int isif_config_dfc(struct isif_dfc *vdfc)
-{
- /* initialize retries to loop for max ~ 250 usec */
- u32 val, count, retries = loops_per_jiffy / (4000/HZ);
- int i;
-
- if (!vdfc->en)
- return 0;
-
- /* Correction mode */
- val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT);
-
- /* Correct whole line or partial */
- if (vdfc->corr_whole_line)
- val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT;
-
- /* level shift value */
- val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT;
-
- regw(val, DFCCTL);
-
- /* Defect saturation level */
- regw(vdfc->def_sat_level, VDFSATLV);
-
- regw(vdfc->table[0].pos_vert, DFCMEM0);
- regw(vdfc->table[0].pos_horz, DFCMEM1);
- if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
- vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
- regw(vdfc->table[0].level_at_pos, DFCMEM2);
- regw(vdfc->table[0].level_up_pixels, DFCMEM3);
- regw(vdfc->table[0].level_low_pixels, DFCMEM4);
- }
-
- /* set DFCMARST and set DFCMWR */
- val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1;
- regw(val, DFCMEMCTL);
-
- count = retries;
- while (count && (regr(DFCMEMCTL) & 0x1))
- count--;
-
- if (!count) {
- dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n");
- return -1;
- }
-
- for (i = 1; i < vdfc->num_vdefects; i++) {
- regw(vdfc->table[i].pos_vert, DFCMEM0);
- regw(vdfc->table[i].pos_horz, DFCMEM1);
- if (vdfc->corr_mode == ISIF_VDFC_NORMAL ||
- vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) {
- regw(vdfc->table[i].level_at_pos, DFCMEM2);
- regw(vdfc->table[i].level_up_pixels, DFCMEM3);
- regw(vdfc->table[i].level_low_pixels, DFCMEM4);
- }
- val = regr(DFCMEMCTL);
- /* clear DFCMARST and set DFCMWR */
- val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT);
- val |= 1;
- regw(val, DFCMEMCTL);
-
- count = retries;
- while (count && (regr(DFCMEMCTL) & 0x1))
- count--;
-
- if (!count) {
- dev_err(isif_cfg.dev,
- "defect table write timeout !!!\n");
- return -1;
- }
- }
- if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) {
- /* Extra cycle needed */
- regw(0, DFCMEM0);
- regw(0x1FFF, DFCMEM1);
- regw(1, DFCMEMCTL);
- }
-
- /* enable VDFC */
- reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT),
- DFCCTL);
- return 0;
-}
-
-static void isif_config_csc(struct isif_df_csc *df_csc)
-{
- u32 val1 = 0, val2 = 0, i;
-
- if (!df_csc->csc.en) {
- regw(0, CSCCTL);
- return;
- }
- for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) {
- if ((i % 2) == 0) {
- /* CSCM - LSB */
- val1 = (df_csc->csc.coeff[i].integer <<
- ISIF_CSC_COEF_INTEG_SHIFT) |
- df_csc->csc.coeff[i].decimal;
- } else {
-
- /* CSCM - MSB */
- val2 = (df_csc->csc.coeff[i].integer <<
- ISIF_CSC_COEF_INTEG_SHIFT) |
- df_csc->csc.coeff[i].decimal;
- val2 <<= ISIF_CSCM_MSB_SHIFT;
- val2 |= val1;
- regw(val2, (CSCM0 + ((i - 1) << 1)));
- }
- }
-
- /* program the active area */
- regw(df_csc->start_pix, FMTSPH);
- /*
- * one extra pixel as required for CSC. Actually number of
- * pixel - 1 should be configured in this register. So we
- * need to subtract 1 before writing to FMTSPH, but we will
- * not do this since csc requires one extra pixel
- */
- regw(df_csc->num_pixels, FMTLNH);
- regw(df_csc->start_line, FMTSLV);
- /*
- * one extra line as required for CSC. See reason documented for
- * num_pixels
- */
- regw(df_csc->num_lines, FMTLNV);
-
- /* Enable CSC */
- regw(1, CSCCTL);
-}
-
-static int isif_config_raw(void)
-{
- struct isif_params_raw *params = &isif_cfg.bayer;
- struct isif_config_params_raw *module_params =
- &isif_cfg.bayer.config_params;
- struct vpss_pg_frame_size frame_size;
- struct vpss_sync_pol sync;
- u32 val;
-
- dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n");
-
- /*
- * Configure CCDCFG register:-
- * Set CCD Not to swap input since input is RAW data
- * Set FID detection function to Latch at V-Sync
- * Set WENLOG - isif valid area
- * Set TRGSEL
- * Set EXTRG
- * Packed to 8 or 16 bits
- */
-
- val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
- ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
- ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack;
-
- dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val);
- regw(val, CCDCFG);
-
- /*
- * Configure the vertical sync polarity(MODESET.VDPOL)
- * Configure the horizontal sync polarity (MODESET.HDPOL)
- * Configure frame id polarity (MODESET.FLDPOL)
- * Configure data polarity
- * Configure External WEN Selection
- * Configure frame format(progressive or interlace)
- * Configure pixel format (Input mode)
- * Configure the data shift
- */
-
- val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) |
- (params->hd_pol << ISIF_HD_POL_SHIFT) |
- (params->fid_pol << ISIF_FID_POL_SHIFT) |
- (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) |
- (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) |
- (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
- (params->pix_fmt << ISIF_INPUT_SHIFT) |
- (params->config_params.data_shift << ISIF_DATASFT_SHIFT);
-
- regw(val, MODESET);
- dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val);
-
- /*
- * Configure GAMMAWD register
- * CFA pattern setting
- */
- val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT;
-
- /* Gamma msb */
- if (module_params->compress.alg == ISIF_ALAW)
- val |= ISIF_ALAW_ENABLE;
-
- val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT);
- regw(val, CGAMMAWD);
-
- /* Configure DPCM compression settings */
- if (module_params->compress.alg == ISIF_DPCM) {
- val = BIT(ISIF_DPCM_EN_SHIFT) |
- (module_params->compress.pred <<
- ISIF_DPCM_PREDICTOR_SHIFT);
- }
-
- regw(val, MISC);
-
- /* Configure Gain & Offset */
- isif_config_gain_offset();
-
- /* Configure Color pattern */
- val = (params->config_params.col_pat_field0.olop) |
- (params->config_params.col_pat_field0.olep << 2) |
- (params->config_params.col_pat_field0.elop << 4) |
- (params->config_params.col_pat_field0.elep << 6) |
- (params->config_params.col_pat_field1.olop << 8) |
- (params->config_params.col_pat_field1.olep << 10) |
- (params->config_params.col_pat_field1.elop << 12) |
- (params->config_params.col_pat_field1.elep << 14);
- regw(val, CCOLP);
- dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val);
-
- /* Configure HSIZE register */
- val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT;
-
- /* calculate line offset in 32 bytes based on pack value */
- if (isif_cfg.data_pack == ISIF_PACK_8BIT)
- val |= ((params->win.width + 31) >> 5);
- else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
- val |= (((params->win.width +
- (params->win.width >> 2)) + 31) >> 5);
- else
- val |= (((params->win.width * 2) + 31) >> 5);
- regw(val, HSIZE);
-
- /* Configure SDOFST register */
- if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
- if (params->image_invert_en) {
- /* For interlace inverse mode */
- regw(0x4B6D, SDOFST);
- dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n");
- } else {
- /* For interlace non inverse mode */
- regw(0x0B6D, SDOFST);
- dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n");
- }
- } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
- if (params->image_invert_en) {
- /* For progressive inverse mode */
- regw(0x4000, SDOFST);
- dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n");
- } else {
- /* For progressive non inverse mode */
- regw(0x0000, SDOFST);
- dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n");
- }
- }
-
- /* Configure video window */
- isif_setwin(&params->win, params->frm_fmt, 1);
-
- /* Configure Black Clamp */
- isif_config_bclamp(&module_params->bclamp);
-
- /* Configure Vertical Defection Pixel Correction */
- if (isif_config_dfc(&module_params->dfc) < 0)
- return -EFAULT;
-
- if (!module_params->df_csc.df_or_csc)
- /* Configure Color Space Conversion */
- isif_config_csc(&module_params->df_csc);
-
- isif_config_linearization(&module_params->linearize);
-
- /* Configure Culling */
- isif_config_culling(&module_params->culling);
-
- /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
- regw(module_params->horz_offset, DATAHOFST);
- regw(module_params->vert_offset, DATAVOFST);
-
- /* Setup test pattern if enabled */
- if (params->config_params.test_pat_gen) {
- /* Use the HD/VD pol settings from user */
- sync.ccdpg_hdpol = params->hd_pol;
- sync.ccdpg_vdpol = params->vd_pol;
- dm365_vpss_set_sync_pol(sync);
- frame_size.hlpfr = isif_cfg.bayer.win.width;
- frame_size.pplen = isif_cfg.bayer.win.height;
- dm365_vpss_set_pg_frame_size(frame_size);
- vpss_select_ccdc_source(VPSS_PGLPBK);
- }
-
- dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n");
- return 0;
-}
-
-static int isif_set_buftype(enum ccdc_buftype buf_type)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- isif_cfg.bayer.buf_type = buf_type;
- else
- isif_cfg.ycbcr.buf_type = buf_type;
-
- return 0;
-
-}
-static enum ccdc_buftype isif_get_buftype(void)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- return isif_cfg.bayer.buf_type;
-
- return isif_cfg.ycbcr.buf_type;
-}
-
-static int isif_enum_pix(u32 *pix, int i)
-{
- int ret = -EINVAL;
-
- if (isif_cfg.if_type == VPFE_RAW_BAYER) {
- if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) {
- *pix = isif_raw_bayer_pix_formats[i];
- ret = 0;
- }
- } else {
- if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) {
- *pix = isif_raw_yuv_pix_formats[i];
- ret = 0;
- }
- }
-
- return ret;
-}
-
-static int isif_set_pixel_format(unsigned int pixfmt)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER) {
- if (pixfmt == V4L2_PIX_FMT_SBGGR8) {
- if ((isif_cfg.bayer.config_params.compress.alg !=
- ISIF_ALAW) &&
- (isif_cfg.bayer.config_params.compress.alg !=
- ISIF_DPCM)) {
- dev_dbg(isif_cfg.dev,
- "Either configure A-Law or DPCM\n");
- return -EINVAL;
- }
- isif_cfg.data_pack = ISIF_PACK_8BIT;
- } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) {
- isif_cfg.bayer.config_params.compress.alg =
- ISIF_NO_COMPRESSION;
- isif_cfg.data_pack = ISIF_PACK_16BIT;
- } else
- return -EINVAL;
- isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
- } else {
- if (pixfmt == V4L2_PIX_FMT_YUYV)
- isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
- else if (pixfmt == V4L2_PIX_FMT_UYVY)
- isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
- else
- return -EINVAL;
- isif_cfg.data_pack = ISIF_PACK_8BIT;
- }
- return 0;
-}
-
-static u32 isif_get_pixel_format(void)
-{
- u32 pixfmt;
-
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW ||
- isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM)
- pixfmt = V4L2_PIX_FMT_SBGGR8;
- else
- pixfmt = V4L2_PIX_FMT_SBGGR16;
- else {
- if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
- pixfmt = V4L2_PIX_FMT_YUYV;
- else
- pixfmt = V4L2_PIX_FMT_UYVY;
- }
- return pixfmt;
-}
-
-static int isif_set_image_window(struct v4l2_rect *win)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER) {
- isif_cfg.bayer.win.top = win->top;
- isif_cfg.bayer.win.left = win->left;
- isif_cfg.bayer.win.width = win->width;
- isif_cfg.bayer.win.height = win->height;
- } else {
- isif_cfg.ycbcr.win.top = win->top;
- isif_cfg.ycbcr.win.left = win->left;
- isif_cfg.ycbcr.win.width = win->width;
- isif_cfg.ycbcr.win.height = win->height;
- }
- return 0;
-}
-
-static void isif_get_image_window(struct v4l2_rect *win)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- *win = isif_cfg.bayer.win;
- else
- *win = isif_cfg.ycbcr.win;
-}
-
-static unsigned int isif_get_line_length(void)
-{
- unsigned int len;
-
- if (isif_cfg.if_type == VPFE_RAW_BAYER) {
- if (isif_cfg.data_pack == ISIF_PACK_8BIT)
- len = ((isif_cfg.bayer.win.width));
- else if (isif_cfg.data_pack == ISIF_PACK_12BIT)
- len = (((isif_cfg.bayer.win.width * 2) +
- (isif_cfg.bayer.win.width >> 2)));
- else
- len = (((isif_cfg.bayer.win.width * 2)));
- } else
- len = (((isif_cfg.ycbcr.win.width * 2)));
- return ALIGN(len, 32);
-}
-
-static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- isif_cfg.bayer.frm_fmt = frm_fmt;
- else
- isif_cfg.ycbcr.frm_fmt = frm_fmt;
- return 0;
-}
-static enum ccdc_frmfmt isif_get_frame_format(void)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- return isif_cfg.bayer.frm_fmt;
- return isif_cfg.ycbcr.frm_fmt;
-}
-
-static int isif_getfid(void)
-{
- return (regr(MODESET) >> 15) & 0x1;
-}
-
-/* misc operations */
-static void isif_setfbaddr(unsigned long addr)
-{
- regw((addr >> 21) & 0x07ff, CADU);
- regw((addr >> 5) & 0x0ffff, CADL);
-}
-
-static int isif_set_hw_if_params(struct vpfe_hw_if_param *params)
-{
- isif_cfg.if_type = params->if_type;
-
- switch (params->if_type) {
- case VPFE_BT656:
- case VPFE_BT656_10BIT:
- case VPFE_YCBCR_SYNC_8:
- isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT;
- isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
- break;
- case VPFE_BT1120:
- case VPFE_YCBCR_SYNC_16:
- isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT;
- isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
- break;
- case VPFE_RAW_BAYER:
- isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
- break;
- default:
- dev_dbg(isif_cfg.dev, "Invalid interface type\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* This function will configure ISIF for YCbCr parameters. */
-static int isif_config_ycbcr(void)
-{
- struct isif_ycbcr_config *params = &isif_cfg.ycbcr;
- struct vpss_pg_frame_size frame_size;
- u32 modeset = 0, ccdcfg = 0;
- struct vpss_sync_pol sync;
-
- dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr...");
-
- /* configure pixel format or input mode */
- modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) |
- (params->frm_fmt << ISIF_FRM_FMT_SHIFT) |
- (params->fid_pol << ISIF_FID_POL_SHIFT) |
- (params->hd_pol << ISIF_HD_POL_SHIFT) |
- (params->vd_pol << ISIF_VD_POL_SHIFT);
-
- /* pack the data to 8-bit ISIFCFG */
- switch (isif_cfg.if_type) {
- case VPFE_BT656:
- if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
- dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
- return -EINVAL;
- }
- modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT);
- regw(3, REC656IF);
- ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR;
- break;
- case VPFE_BT656_10BIT:
- if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
- dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
- return -EINVAL;
- }
- /* setup BT.656, embedded sync */
- regw(3, REC656IF);
- /* enable 10 bit mode in ccdcfg */
- ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
- ISIF_BW656_ENABLE;
- break;
- case VPFE_BT1120:
- if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
- dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
- return -EINVAL;
- }
- regw(3, REC656IF);
- break;
-
- case VPFE_YCBCR_SYNC_8:
- ccdcfg |= ISIF_DATA_PACK8;
- ccdcfg |= ISIF_YCINSWP_YCBCR;
- if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) {
- dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
- return -EINVAL;
- }
- break;
- case VPFE_YCBCR_SYNC_16:
- if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) {
- dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n");
- return -EINVAL;
- }
- break;
- default:
- /* should never come here */
- dev_dbg(isif_cfg.dev, "Invalid interface type\n");
- return -EINVAL;
- }
-
- regw(modeset, MODESET);
-
- /* Set up pix order */
- ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT;
-
- regw(ccdcfg, CCDCFG);
-
- /* configure video window */
- if ((isif_cfg.if_type == VPFE_BT1120) ||
- (isif_cfg.if_type == VPFE_YCBCR_SYNC_16))
- isif_setwin(&params->win, params->frm_fmt, 1);
- else
- isif_setwin(&params->win, params->frm_fmt, 2);
-
- /*
- * configure the horizontal line offset
- * this is done by rounding up width to a multiple of 16 pixels
- * and multiply by two to account for y:cb:cr 4:2:2 data
- */
- regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE);
-
- /* configure the memory line offset */
- if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) &&
- (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED))
- /* two fields are interleaved in memory */
- regw(0x00000249, SDOFST);
-
- /* Setup test pattern if enabled */
- if (isif_cfg.bayer.config_params.test_pat_gen) {
- sync.ccdpg_hdpol = params->hd_pol;
- sync.ccdpg_vdpol = params->vd_pol;
- dm365_vpss_set_sync_pol(sync);
- dm365_vpss_set_pg_frame_size(frame_size);
- }
- return 0;
-}
-
-static int isif_configure(void)
-{
- if (isif_cfg.if_type == VPFE_RAW_BAYER)
- return isif_config_raw();
- return isif_config_ycbcr();
-}
-
-static int isif_close(struct device *device)
-{
- /* copy defaults to module params */
- isif_cfg.bayer.config_params = isif_config_defaults;
- return 0;
-}
-
-static struct ccdc_hw_device isif_hw_dev = {
- .name = "ISIF",
- .owner = THIS_MODULE,
- .hw_ops = {
- .open = isif_open,
- .close = isif_close,
- .enable = isif_enable,
- .enable_out_to_sdram = isif_enable_output_to_sdram,
- .set_hw_if_params = isif_set_hw_if_params,
- .configure = isif_configure,
- .set_buftype = isif_set_buftype,
- .get_buftype = isif_get_buftype,
- .enum_pix = isif_enum_pix,
- .set_pixel_format = isif_set_pixel_format,
- .get_pixel_format = isif_get_pixel_format,
- .set_frame_format = isif_set_frame_format,
- .get_frame_format = isif_get_frame_format,
- .set_image_window = isif_set_image_window,
- .get_image_window = isif_get_image_window,
- .get_line_length = isif_get_line_length,
- .setfbaddr = isif_setfbaddr,
- .getfid = isif_getfid,
- },
-};
-
-static int __init isif_probe(struct platform_device *pdev)
-{
- void (*setup_pinmux)(void);
- struct resource *res;
- void *__iomem addr;
- int status = 0, i;
-
- /*
- * first try to register with vpfe. If not correct platform, then we
- * don't have to iomap
- */
- status = vpfe_register_ccdc_device(&isif_hw_dev);
- if (status < 0)
- return status;
-
- /* Get and enable Master clock */
- isif_cfg.mclk = clk_get(&pdev->dev, "master");
- if (IS_ERR(isif_cfg.mclk)) {
- status = PTR_ERR(isif_cfg.mclk);
- goto fail_mclk;
- }
- if (clk_enable(isif_cfg.mclk)) {
- status = -ENODEV;
- goto fail_mclk;
- }
-
- /* Platform data holds setup_pinmux function ptr */
- if (NULL == pdev->dev.platform_data) {
- status = -ENODEV;
- goto fail_mclk;
- }
- setup_pinmux = pdev->dev.platform_data;
- /*
- * setup Mux configuration for ccdc which may be different for
- * different SoCs using this CCDC
- */
- setup_pinmux();
-
- i = 0;
- /* Get the ISIF base address, linearization table0 and table1 addr. */
- while (i < 3) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- if (!res) {
- status = -ENODEV;
- goto fail_nobase_res;
- }
- res = request_mem_region(res->start, resource_size(res),
- res->name);
- if (!res) {
- status = -EBUSY;
- goto fail_nobase_res;
- }
- addr = ioremap_nocache(res->start, resource_size(res));
- if (!addr) {
- status = -ENOMEM;
- goto fail_base_iomap;
- }
- switch (i) {
- case 0:
- /* ISIF base address */
- isif_cfg.base_addr = addr;
- break;
- case 1:
- /* ISIF linear tbl0 address */
- isif_cfg.linear_tbl0_addr = addr;
- break;
- default:
- /* ISIF linear tbl0 address */
- isif_cfg.linear_tbl1_addr = addr;
- break;
- }
- i++;
- }
- isif_cfg.dev = &pdev->dev;
-
- printk(KERN_NOTICE "%s is registered with vpfe.\n",
- isif_hw_dev.name);
- return 0;
-fail_base_iomap:
- release_mem_region(res->start, resource_size(res));
- i--;
-fail_nobase_res:
- if (isif_cfg.base_addr)
- iounmap(isif_cfg.base_addr);
- if (isif_cfg.linear_tbl0_addr)
- iounmap(isif_cfg.linear_tbl0_addr);
-
- while (i >= 0) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- release_mem_region(res->start, resource_size(res));
- i--;
- }
-fail_mclk:
- clk_put(isif_cfg.mclk);
- vpfe_unregister_ccdc_device(&isif_hw_dev);
- return status;
-}
-
-static int isif_remove(struct platform_device *pdev)
-{
- struct resource *res;
- int i = 0;
-
- iounmap(isif_cfg.base_addr);
- iounmap(isif_cfg.linear_tbl0_addr);
- iounmap(isif_cfg.linear_tbl1_addr);
- while (i < 3) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, i);
- if (res)
- release_mem_region(res->start, resource_size(res));
- i++;
- }
- vpfe_unregister_ccdc_device(&isif_hw_dev);
- return 0;
-}
-
-static struct platform_driver isif_driver = {
- .driver = {
- .name = "isif",
- .owner = THIS_MODULE,
- },
- .remove = __devexit_p(isif_remove),
- .probe = isif_probe,
-};
-
-module_platform_driver(isif_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
deleted file mode 100644
index 6fe7034bea7c..000000000000
--- a/drivers/media/video/davinci/vpbe_display.c
+++ /dev/null
@@ -1,1830 +0,0 @@
-/*
- * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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 version 2.
- *
- * This program is distributed WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/string.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-
-#include <asm/pgtable.h>
-#include <mach/cputype.h>
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/davinci/vpbe_display.h>
-#include <media/davinci/vpbe_types.h>
-#include <media/davinci/vpbe.h>
-#include <media/davinci/vpbe_venc.h>
-#include <media/davinci/vpbe_osd.h>
-#include "vpbe_venc_regs.h"
-
-#define VPBE_DISPLAY_DRIVER "vpbe-v4l2"
-
-static int debug;
-
-#define VPBE_DEFAULT_NUM_BUFS 3
-
-module_param(debug, int, 0644);
-
-static int venc_is_second_field(struct vpbe_display *disp_dev)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int ret;
- int val;
-
- ret = v4l2_subdev_call(vpbe_dev->venc,
- core,
- ioctl,
- VENC_GET_FLD,
- &val);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in getting Field ID 0\n");
- }
- return val;
-}
-
-static void vpbe_isr_even_field(struct vpbe_display *disp_obj,
- struct vpbe_layer *layer)
-{
- struct timespec timevalue;
-
- if (layer->cur_frm == layer->next_frm)
- return;
- ktime_get_ts(&timevalue);
- layer->cur_frm->ts.tv_sec = timevalue.tv_sec;
- layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC;
- layer->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&layer->cur_frm->done);
- /* Make cur_frm pointing to next_frm */
- layer->cur_frm = layer->next_frm;
-}
-
-static void vpbe_isr_odd_field(struct vpbe_display *disp_obj,
- struct vpbe_layer *layer)
-{
- struct osd_state *osd_device = disp_obj->osd_device;
- unsigned long addr;
-
- spin_lock(&disp_obj->dma_queue_lock);
- if (list_empty(&layer->dma_queue) ||
- (layer->cur_frm != layer->next_frm)) {
- spin_unlock(&disp_obj->dma_queue_lock);
- return;
- }
- /*
- * one field is displayed configure
- * the next frame if it is available
- * otherwise hold on current frame
- * Get next from the buffer queue
- */
- layer->next_frm = list_entry(
- layer->dma_queue.next,
- struct videobuf_buffer,
- queue);
- /* Remove that from the buffer queue */
- list_del(&layer->next_frm->queue);
- spin_unlock(&disp_obj->dma_queue_lock);
- /* Mark state of the frame to active */
- layer->next_frm->state = VIDEOBUF_ACTIVE;
- addr = videobuf_to_dma_contig(layer->next_frm);
- osd_device->ops.start_layer(osd_device,
- layer->layer_info.id,
- addr,
- disp_obj->cbcr_ofst);
-}
-
-/* interrupt service routine */
-static irqreturn_t venc_isr(int irq, void *arg)
-{
- struct vpbe_display *disp_dev = (struct vpbe_display *)arg;
- struct vpbe_layer *layer;
- static unsigned last_event;
- unsigned event = 0;
- int fid;
- int i;
-
- if ((NULL == arg) || (NULL == disp_dev->dev[0]))
- return IRQ_HANDLED;
-
- if (venc_is_second_field(disp_dev))
- event |= VENC_SECOND_FIELD;
- else
- event |= VENC_FIRST_FIELD;
-
- if (event == (last_event & ~VENC_END_OF_FRAME)) {
- /*
- * If the display is non-interlaced, then we need to flag the
- * end-of-frame event at every interrupt regardless of the
- * value of the FIDST bit. We can conclude that the display is
- * non-interlaced if the value of the FIDST bit is unchanged
- * from the previous interrupt.
- */
- event |= VENC_END_OF_FRAME;
- } else if (event == VENC_SECOND_FIELD) {
- /* end-of-frame for interlaced display */
- event |= VENC_END_OF_FRAME;
- }
- last_event = event;
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- layer = disp_dev->dev[i];
- /* If streaming is started in this layer */
- if (!layer->started)
- continue;
-
- if (layer->layer_first_int) {
- layer->layer_first_int = 0;
- continue;
- }
- /* Check the field format */
- if ((V4L2_FIELD_NONE == layer->pix_fmt.field) &&
- (event & VENC_END_OF_FRAME)) {
- /* Progressive mode */
-
- vpbe_isr_even_field(disp_dev, layer);
- vpbe_isr_odd_field(disp_dev, layer);
- } else {
- /* Interlaced mode */
-
- layer->field_id ^= 1;
- if (event & VENC_FIRST_FIELD)
- fid = 0;
- else
- fid = 1;
-
- /*
- * If field id does not match with store
- * field id
- */
- if (fid != layer->field_id) {
- /* Make them in sync */
- layer->field_id = fid;
- continue;
- }
- /*
- * device field id and local field id are
- * in sync. If this is even field
- */
- if (0 == fid)
- vpbe_isr_even_field(disp_dev, layer);
- else /* odd field */
- vpbe_isr_odd_field(disp_dev, layer);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * vpbe_buffer_prepare()
- * This is the callback function called from videobuf_qbuf() function
- * the buffer is prepared and user space virtual address is converted into
- * physical address
- */
-static int vpbe_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct vpbe_fh *fh = q->priv_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- unsigned long addr;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_prepare\n");
-
- /* If buffer is not initialized, initialize it */
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = layer->pix_fmt.width;
- vb->height = layer->pix_fmt.height;
- vb->size = layer->pix_fmt.sizeimage;
- vb->field = field;
-
- ret = videobuf_iolock(q, vb, NULL);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \
- user address\n");
- return -EINVAL;
- }
-
- addr = videobuf_to_dma_contig(vb);
-
- if (q->streaming) {
- if (!IS_ALIGNED(addr, 8)) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "buffer_prepare:offset is \
- not aligned to 32 bytes\n");
- return -EINVAL;
- }
- }
- vb->state = VIDEOBUF_PREPARED;
- }
- return 0;
-}
-
-/*
- * vpbe_buffer_setup()
- * This function allocates memory for the buffers
- */
-static int vpbe_buffer_setup(struct videobuf_queue *q,
- unsigned int *count,
- unsigned int *size)
-{
- /* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n");
-
- *size = layer->pix_fmt.sizeimage;
-
- /* Store number of buffers allocated in numbuffer member */
- if (*count < VPBE_DEFAULT_NUM_BUFS)
- *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS;
-
- return 0;
-}
-
-/*
- * vpbe_buffer_queue()
- * This function adds the buffer to DMA queue
- */
-static void vpbe_buffer_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- /* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- unsigned long flags;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_queue\n");
-
- /* add the buffer to the DMA queue */
- spin_lock_irqsave(&disp->dma_queue_lock, flags);
- list_add_tail(&vb->queue, &layer->dma_queue);
- spin_unlock_irqrestore(&disp->dma_queue_lock, flags);
- /* Change state of the buffer */
- vb->state = VIDEOBUF_QUEUED;
-}
-
-/*
- * vpbe_buffer_release()
- * This function is called from the videobuf layer to free memory allocated to
- * the buffers
- */
-static void vpbe_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- /* Get the file handle object and layer object */
- struct vpbe_fh *fh = q->priv_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe_buffer_release\n");
-
- if (V4L2_MEMORY_USERPTR != layer->memory)
- videobuf_dma_contig_free(q, vb);
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = vpbe_buffer_setup,
- .buf_prepare = vpbe_buffer_prepare,
- .buf_queue = vpbe_buffer_queue,
- .buf_release = vpbe_buffer_release,
-};
-
-static
-struct vpbe_layer*
-_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer)
-{
- enum vpbe_display_device_id thiswin, otherwin;
- thiswin = layer->device_id;
-
- otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ?
- VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0;
- return disp_dev->dev[otherwin];
-}
-
-static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer)
-{
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct osd_state *osd_device = disp_dev->osd_device;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- unsigned long addr;
- int ret;
-
- addr = videobuf_to_dma_contig(layer->cur_frm);
- /* Set address in the display registers */
- osd_device->ops.start_layer(osd_device,
- layer->layer_info.id,
- addr,
- disp_dev->cbcr_ofst);
-
- ret = osd_device->ops.enable_layer(osd_device,
- layer->layer_info.id, 0);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in enabling osd window layer 0\n");
- return -1;
- }
-
- /* Enable the window */
- layer->layer_info.enable = 1;
- if (cfg->pixfmt == PIXFMT_NV12) {
- struct vpbe_layer *otherlayer =
- _vpbe_display_get_other_win_layer(disp_dev, layer);
-
- ret = osd_device->ops.enable_layer(osd_device,
- otherlayer->layer_info.id, 1);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in enabling osd window layer 1\n");
- return -1;
- }
- otherlayer->layer_info.enable = 1;
- }
- return 0;
-}
-
-static void
-vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer,
- int expected_xsize, int expected_ysize)
-{
- struct display_layer_info *layer_info = &layer->layer_info;
- struct v4l2_pix_format *pixfmt = &layer->pix_fmt;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int calculated_xsize;
- int h_exp = 0;
- int v_exp = 0;
- int h_scale;
- int v_scale;
-
- v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id;
-
- /*
- * Application initially set the image format. Current display
- * size is obtained from the vpbe display controller. expected_xsize
- * and expected_ysize are set through S_CROP ioctl. Based on this,
- * driver will calculate the scale factors for vertical and
- * horizontal direction so that the image is displayed scaled
- * and expanded. Application uses expansion to display the image
- * in a square pixel. Otherwise it is displayed using displays
- * pixel aspect ratio.It is expected that application chooses
- * the crop coordinates for cropped or scaled display. if crop
- * size is less than the image size, it is displayed cropped or
- * it is displayed scaled and/or expanded.
- *
- * to begin with, set the crop window same as expected. Later we
- * will override with scaled window size
- */
-
- cfg->xsize = pixfmt->width;
- cfg->ysize = pixfmt->height;
- layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */
- layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */
- layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */
- layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */
-
- if (pixfmt->width < expected_xsize) {
- h_scale = vpbe_dev->current_timings.xres / pixfmt->width;
- if (h_scale < 2)
- h_scale = 1;
- else if (h_scale >= 4)
- h_scale = 4;
- else
- h_scale = 2;
- cfg->xsize *= h_scale;
- if (cfg->xsize < expected_xsize) {
- if ((standard_id & V4L2_STD_525_60) ||
- (standard_id & V4L2_STD_625_50)) {
- calculated_xsize = (cfg->xsize *
- VPBE_DISPLAY_H_EXP_RATIO_N) /
- VPBE_DISPLAY_H_EXP_RATIO_D;
- if (calculated_xsize <= expected_xsize) {
- h_exp = 1;
- cfg->xsize = calculated_xsize;
- }
- }
- }
- if (h_scale == 2)
- layer_info->h_zoom = ZOOM_X2;
- else if (h_scale == 4)
- layer_info->h_zoom = ZOOM_X4;
- if (h_exp)
- layer_info->h_exp = H_EXP_9_OVER_8;
- } else {
- /* no scaling, only cropping. Set display area to crop area */
- cfg->xsize = expected_xsize;
- }
-
- if (pixfmt->height < expected_ysize) {
- v_scale = expected_ysize / pixfmt->height;
- if (v_scale < 2)
- v_scale = 1;
- else if (v_scale >= 4)
- v_scale = 4;
- else
- v_scale = 2;
- cfg->ysize *= v_scale;
- if (cfg->ysize < expected_ysize) {
- if ((standard_id & V4L2_STD_625_50)) {
- calculated_xsize = (cfg->ysize *
- VPBE_DISPLAY_V_EXP_RATIO_N) /
- VPBE_DISPLAY_V_EXP_RATIO_D;
- if (calculated_xsize <= expected_ysize) {
- v_exp = 1;
- cfg->ysize = calculated_xsize;
- }
- }
- }
- if (v_scale == 2)
- layer_info->v_zoom = ZOOM_X2;
- else if (v_scale == 4)
- layer_info->v_zoom = ZOOM_X4;
- if (v_exp)
- layer_info->h_exp = V_EXP_6_OVER_5;
- } else {
- /* no scaling, only cropping. Set display area to crop area */
- cfg->ysize = expected_ysize;
- }
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "crop display xsize = %d, ysize = %d\n",
- cfg->xsize, cfg->ysize);
-}
-
-static void vpbe_disp_adj_position(struct vpbe_display *disp_dev,
- struct vpbe_layer *layer,
- int top, int left)
-{
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
-
- cfg->xpos = min((unsigned int)left,
- vpbe_dev->current_timings.xres - cfg->xsize);
- cfg->ypos = min((unsigned int)top,
- vpbe_dev->current_timings.yres - cfg->ysize);
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "new xpos = %d, ypos = %d\n",
- cfg->xpos, cfg->ypos);
-}
-
-static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev,
- struct v4l2_rect *c)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
-
- if ((c->width == 0) ||
- ((c->width + c->left) > vpbe_dev->current_timings.xres))
- c->width = vpbe_dev->current_timings.xres - c->left;
-
- if ((c->height == 0) || ((c->height + c->top) >
- vpbe_dev->current_timings.yres))
- c->height = vpbe_dev->current_timings.yres - c->top;
-
- /* window height must be even for interlaced display */
- if (vpbe_dev->current_timings.interlaced)
- c->height &= (~0x01);
-
-}
-
-/**
- * vpbe_try_format()
- * If user application provides width and height, and have bytesperline set
- * to zero, driver calculates bytesperline and sizeimage based on hardware
- * limits.
- */
-static int vpbe_try_format(struct vpbe_display *disp_dev,
- struct v4l2_pix_format *pixfmt, int check)
-{
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- int min_height = 1;
- int min_width = 32;
- int max_height;
- int max_width;
- int bpp;
-
- if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) &&
- (pixfmt->pixelformat != V4L2_PIX_FMT_NV12))
- /* choose default as V4L2_PIX_FMT_UYVY */
- pixfmt->pixelformat = V4L2_PIX_FMT_UYVY;
-
- /* Check the field format */
- if ((pixfmt->field != V4L2_FIELD_INTERLACED) &&
- (pixfmt->field != V4L2_FIELD_NONE)) {
- if (vpbe_dev->current_timings.interlaced)
- pixfmt->field = V4L2_FIELD_INTERLACED;
- else
- pixfmt->field = V4L2_FIELD_NONE;
- }
-
- if (pixfmt->field == V4L2_FIELD_INTERLACED)
- min_height = 2;
-
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
- bpp = 1;
- else
- bpp = 2;
-
- max_width = vpbe_dev->current_timings.xres;
- max_height = vpbe_dev->current_timings.yres;
-
- min_width /= bpp;
-
- if (!pixfmt->width || (pixfmt->width < min_width) ||
- (pixfmt->width > max_width)) {
- pixfmt->width = vpbe_dev->current_timings.xres;
- }
-
- if (!pixfmt->height || (pixfmt->height < min_height) ||
- (pixfmt->height > max_height)) {
- pixfmt->height = vpbe_dev->current_timings.yres;
- }
-
- if (pixfmt->bytesperline < (pixfmt->width * bpp))
- pixfmt->bytesperline = pixfmt->width * bpp;
-
- /* Make the bytesperline 32 byte aligned */
- pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31);
-
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height +
- (pixfmt->bytesperline * pixfmt->height >> 1);
- else
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
-
- return 0;
-}
-
-static int vpbe_display_g_priority(struct file *file, void *priv,
- enum v4l2_priority *p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
-
- *p = v4l2_prio_max(&layer->prio);
-
- return 0;
-}
-
-static int vpbe_display_s_priority(struct file *file, void *priv,
- enum v4l2_priority p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- int ret;
-
- ret = v4l2_prio_change(&layer->prio, &fh->prio, p);
-
- return ret;
-}
-
-static int vpbe_display_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- cap->version = VPBE_DISPLAY_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver));
- strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info));
- strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card));
-
- return 0;
-}
-
-static int vpbe_display_s_crop(struct file *file, void *priv,
- struct v4l2_crop *crop)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct osd_state *osd_device = disp_dev->osd_device;
- struct v4l2_rect *rect = &crop->c;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_S_CROP, layer id = %d\n", layer->device_id);
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
-
- if (rect->top < 0)
- rect->top = 0;
- if (rect->left < 0)
- rect->left = 0;
-
- vpbe_disp_check_window_params(disp_dev, rect);
-
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
-
- vpbe_disp_calculate_scale_factor(disp_dev, layer,
- rect->width,
- rect->height);
- vpbe_disp_adj_position(disp_dev, layer, rect->top,
- rect->left);
- ret = osd_device->ops.set_layer_config(osd_device,
- layer->layer_info.id, cfg);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in set layer config:\n");
- return -EINVAL;
- }
-
- /* apply zooming and h or v expansion */
- osd_device->ops.set_zoom(osd_device,
- layer->layer_info.id,
- layer->layer_info.h_zoom,
- layer->layer_info.v_zoom);
- ret = osd_device->ops.set_vid_expansion(osd_device,
- layer->layer_info.h_exp,
- layer->layer_info.v_exp);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in set vid expansion:\n");
- return -EINVAL;
- }
-
- if ((layer->layer_info.h_zoom != ZOOM_X1) ||
- (layer->layer_info.v_zoom != ZOOM_X1) ||
- (layer->layer_info.h_exp != H_EXP_OFF) ||
- (layer->layer_info.v_exp != V_EXP_OFF))
- /* Enable expansion filter */
- osd_device->ops.set_interpolation_filter(osd_device, 1);
- else
- osd_device->ops.set_interpolation_filter(osd_device, 0);
-
- return 0;
-}
-
-static int vpbe_display_g_crop(struct file *file, void *priv,
- struct v4l2_crop *crop)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = fh->disp_dev->osd_device;
- struct v4l2_rect *rect = &crop->c;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_G_CROP, layer id = %d\n",
- layer->device_id);
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n");
- ret = -EINVAL;
- }
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
- rect->top = cfg->ypos;
- rect->left = cfg->xpos;
- rect->width = cfg->xsize;
- rect->height = cfg->ysize;
-
- return 0;
-}
-
-static int vpbe_display_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cropcap)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n");
-
- cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- cropcap->bounds.left = 0;
- cropcap->bounds.top = 0;
- cropcap->bounds.width = vpbe_dev->current_timings.xres;
- cropcap->bounds.height = vpbe_dev->current_timings.yres;
- cropcap->pixelaspect = vpbe_dev->current_timings.aspect;
- cropcap->defrect = cropcap->bounds;
- return 0;
-}
-
-static int vpbe_display_g_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_G_FMT, layer id = %d\n",
- layer->device_id);
-
- /* If buffer type is video output */
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
- /* Fill in the information about format */
- fmt->fmt.pix = layer->pix_fmt;
-
- return 0;
-}
-
-static int vpbe_display_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- unsigned int index = 0;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_ENUM_FMT, layer id = %d\n",
- layer->device_id);
- if (fmt->index > 1) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n");
- return -EINVAL;
- }
-
- /* Fill in the information about format */
- index = fmt->index;
- memset(fmt, 0, sizeof(*fmt));
- fmt->index = index;
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- if (index == 0) {
- strcpy(fmt->description, "YUV 4:2:2 - UYVY");
- fmt->pixelformat = V4L2_PIX_FMT_UYVY;
- } else {
- strcpy(fmt->description, "Y/CbCr 4:2:0");
- fmt->pixelformat = V4L2_PIX_FMT_NV12;
- }
-
- return 0;
-}
-
-static int vpbe_display_s_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- struct osd_state *osd_device = disp_dev->osd_device;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_S_FMT, layer id = %d\n",
- layer->device_id);
-
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
- return -EBUSY;
- }
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
- /* Check for valid pixel format */
- ret = vpbe_try_format(disp_dev, pixfmt, 1);
- if (ret)
- return ret;
-
- /* YUV420 is requested, check availability of the
- other video window */
-
- layer->pix_fmt = *pixfmt;
-
- /* Get osd layer config */
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
- /* Store the pixel format in the layer object */
- cfg->xsize = pixfmt->width;
- cfg->ysize = pixfmt->height;
- cfg->line_length = pixfmt->bytesperline;
- cfg->ypos = 0;
- cfg->xpos = 0;
- cfg->interlaced = vpbe_dev->current_timings.interlaced;
-
- if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat)
- cfg->pixfmt = PIXFMT_YCbCrI;
-
- /* Change of the default pixel format for both video windows */
- if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) {
- struct vpbe_layer *otherlayer;
- cfg->pixfmt = PIXFMT_NV12;
- otherlayer = _vpbe_display_get_other_win_layer(disp_dev,
- layer);
- otherlayer->layer_info.config.pixfmt = PIXFMT_NV12;
- }
-
- /* Set the layer config in the osd window */
- ret = osd_device->ops.set_layer_config(osd_device,
- layer->layer_info.id, cfg);
- if (ret < 0) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Error in S_FMT params:\n");
- return -EINVAL;
- }
-
- /* Readback and fill the local copy of current pix format */
- osd_device->ops.get_layer_config(osd_device,
- layer->layer_info.id, cfg);
-
- return 0;
-}
-
-static int vpbe_display_try_fmt(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n");
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n");
- return -EINVAL;
- }
-
- /* Check for valid field format */
- return vpbe_try_format(disp_dev, pixfmt, 0);
-
-}
-
-/**
- * vpbe_display_s_std - Set the given standard in the encoder
- *
- * Sets the standard if supported by the current encoder. Return the status.
- * 0 - success & -EINVAL on error
- */
-static int vpbe_display_s_std(struct file *file, void *priv,
- v4l2_std_id *std_id)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n");
-
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
- return -EBUSY;
- }
- if (NULL != vpbe_dev->ops.s_std) {
- ret = vpbe_dev->ops.s_std(vpbe_dev, std_id);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set standard for sub devices\n");
- return -EINVAL;
- }
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * vpbe_display_g_std - Get the standard in the current encoder
- *
- * Get the standard in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int vpbe_display_g_std(struct file *file, void *priv,
- v4l2_std_id *std_id)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n");
-
- /* Get the standard from the current encoder */
- if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) {
- *std_id = vpbe_dev->current_timings.timings.std_id;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/**
- * vpbe_display_enum_output - enumerate outputs
- *
- * Enumerates the outputs available at the vpbe display
- * returns the status, -EINVAL if end of output list
- */
-static int vpbe_display_enum_output(struct file *file, void *priv,
- struct v4l2_output *output)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n");
-
- /* Enumerate outputs */
-
- if (NULL == vpbe_dev->ops.enum_outputs)
- return -EINVAL;
-
- ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output);
- if (ret) {
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "Failed to enumerate outputs\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * vpbe_display_s_output - Set output to
- * the output specified by the index
- */
-static int vpbe_display_s_output(struct file *file, void *priv,
- unsigned int i)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n");
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
- return -EBUSY;
- }
- if (NULL == vpbe_dev->ops.set_output)
- return -EINVAL;
-
- ret = vpbe_dev->ops.set_output(vpbe_dev, i);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set output for sub devices\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * vpbe_display_g_output - Get output from subdevice
- * for a given by the index
- */
-static int vpbe_display_g_output(struct file *file, void *priv,
- unsigned int *i)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n");
- /* Get the standard from the current encoder */
- *i = vpbe_dev->current_out_index;
-
- return 0;
-}
-
-/**
- * vpbe_display_enum_dv_presets - Enumerate the dv presets
- *
- * enum the preset in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_enum_dv_presets(struct file *file, void *priv,
- struct v4l2_dv_enum_preset *preset)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n");
-
- /* Enumerate outputs */
- if (NULL == vpbe_dev->ops.enum_dv_presets)
- return -EINVAL;
-
- ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to enumerate dv presets info\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-/**
- * vpbe_display_s_dv_preset - Set the dv presets
- *
- * Set the preset in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_s_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n");
-
-
- /* If streaming is started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n");
- return -EBUSY;
- }
-
- /* Set the given standard in the encoder */
- if (!vpbe_dev->ops.s_dv_preset)
- return -EINVAL;
-
- ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Failed to set the dv presets info\n");
- return -EINVAL;
- }
- /* set the current norm to zero to be consistent. If STD is used
- * v4l2 layer will set the norm properly on successful s_std call
- */
- layer->video_dev.current_norm = 0;
-
- return 0;
-}
-
-/**
- * vpbe_display_g_dv_preset - Set the dv presets
- *
- * Get the preset in the current encoder. Return the status. 0 - success
- * -EINVAL on error
- */
-static int
-vpbe_display_g_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *dv_preset)
-{
- struct vpbe_fh *fh = priv;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n");
-
- /* Get the given standard in the encoder */
-
- if (vpbe_dev->current_timings.timings_type &
- VPBE_ENC_DV_PRESET) {
- dv_preset->preset =
- vpbe_dev->current_timings.timings.dv_preset;
- } else {
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vpbe_display_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = fh->disp_dev->osd_device;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_STREAMOFF,layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If io is allowed for this file handle, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
-
- /* If streaming is not started, return error */
- if (!layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer"
- " id = %d\n", layer->device_id);
- return -EINVAL;
- }
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- layer->started = 0;
- ret = videobuf_streamoff(&layer->buffer_queue);
-
- return ret;
-}
-
-static int vpbe_display_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
- int ret;
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If file handle is not allowed IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
- /* If Streaming is already started, return error */
- if (layer->started) {
- v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n");
- return -EBUSY;
- }
-
- /*
- * Call videobuf_streamon to start streaming
- * in videobuf
- */
- ret = videobuf_streamon(&layer->buffer_queue);
- if (ret) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "error in videobuf_streamon\n");
- return ret;
- }
- /* If buffer queue is empty, return error */
- if (list_empty(&layer->dma_queue)) {
- v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n");
- goto streamoff;
- }
- /* Get the next frame from the buffer queue */
- layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next,
- struct videobuf_buffer, queue);
- /* Remove buffer from the buffer queue */
- list_del(&layer->cur_frm->queue);
- /* Mark state of the current frame to active */
- layer->cur_frm->state = VIDEOBUF_ACTIVE;
- /* Initialize field_id and started member */
- layer->field_id = 0;
-
- /* Set parameters in OSD and VENC */
- ret = vpbe_set_osd_display_params(disp_dev, layer);
- if (ret < 0)
- goto streamoff;
-
- /*
- * if request format is yuv420 semiplanar, need to
- * enable both video windows
- */
- layer->started = 1;
-
- layer->layer_first_int = 1;
-
- return ret;
-streamoff:
- ret = videobuf_streamoff(&layer->buffer_queue);
- return ret;
-}
-
-static int vpbe_display_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_DQBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
- /* If this file handle is not allowed to do IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
- if (file->f_flags & O_NONBLOCK)
- /* Call videobuf_dqbuf for non blocking mode */
- ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1);
- else
- /* Call videobuf_dqbuf for blocking mode */
- ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0);
-
- return ret;
-}
-
-static int vpbe_display_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_QBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If this file handle is not allowed to do IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n");
- return -EACCES;
- }
-
- return videobuf_qbuf(&layer->buffer_queue, p);
-}
-
-static int vpbe_display_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "VIDIOC_QUERYBUF, layer id = %d\n",
- layer->device_id);
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* Call videobuf_querybuf to get information */
- ret = videobuf_querybuf(&layer->buffer_queue, buf);
-
- return ret;
-}
-
-static int vpbe_display_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req_buf)
-{
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- int ret;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n");
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) {
- v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- /* If io users of the layer is not zero, return error */
- if (0 != layer->io_usrs) {
- v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n");
- return -EBUSY;
- }
- /* Initialize videobuf queue as per the buffer type */
- videobuf_queue_dma_contig_init(&layer->buffer_queue,
- &video_qops,
- vpbe_dev->pdev,
- &layer->irqlock,
- V4L2_BUF_TYPE_VIDEO_OUTPUT,
- layer->pix_fmt.field,
- sizeof(struct videobuf_buffer),
- fh, NULL);
-
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed = 1;
- /* Increment io usrs member of layer object to 1 */
- layer->io_usrs = 1;
- /* Store type of memory requested in layer object */
- layer->memory = req_buf->memory;
- /* Initialize buffer queue */
- INIT_LIST_HEAD(&layer->dma_queue);
- /* Allocate buffers */
- ret = videobuf_reqbufs(&layer->buffer_queue, req_buf);
-
- return ret;
-}
-
-/*
- * vpbe_display_mmap()
- * It is used to map kernel space buffers into user spaces
- */
-static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- /* Get the layer object and file handle object */
- struct vpbe_fh *fh = filep->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n");
-
- return videobuf_mmap_mapper(&layer->buffer_queue, vma);
-}
-
-/* vpbe_display_poll(): It is used for select/poll system call
- */
-static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait)
-{
- struct vpbe_fh *fh = filep->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
- unsigned int err = 0;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n");
- if (layer->started)
- err = videobuf_poll_stream(filep, &layer->buffer_queue, wait);
- return err;
-}
-
-/*
- * vpbe_display_open()
- * It creates object of file handle structure and stores it in private_data
- * member of filepointer
- */
-static int vpbe_display_open(struct file *file)
-{
- struct vpbe_fh *fh = NULL;
- struct vpbe_layer *layer = video_drvdata(file);
- struct vpbe_display *disp_dev = layer->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
- int err;
-
- /* Allocate memory for the file handle object */
- fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL);
- if (fh == NULL) {
- v4l2_err(&vpbe_dev->v4l2_dev,
- "unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe display open plane = %d\n",
- layer->device_id);
-
- /* store pointer to fh in private_data member of filep */
- file->private_data = fh;
- fh->layer = layer;
- fh->disp_dev = disp_dev;
-
- if (!layer->usrs) {
-
- /* First claim the layer for this device */
- err = osd_device->ops.request_layer(osd_device,
- layer->layer_info.id);
- if (err < 0) {
- /* Couldn't get layer */
- v4l2_err(&vpbe_dev->v4l2_dev,
- "Display Manager failed to allocate layer\n");
- kfree(fh);
- return -EINVAL;
- }
- }
- /* Increment layer usrs counter */
- layer->usrs++;
- /* Set io_allowed member to false */
- fh->io_allowed = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&layer->prio, &fh->prio);
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev,
- "vpbe display device opened successfully\n");
- return 0;
-}
-
-/*
- * vpbe_display_release()
- * This function deletes buffer queue, frees the buffers and the davinci
- * display file * handle
- */
-static int vpbe_display_release(struct file *file)
-{
- /* Get the layer object and file handle object */
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_layer *layer = fh->layer;
- struct osd_layer_config *cfg = &layer->layer_info.config;
- struct vpbe_display *disp_dev = fh->disp_dev;
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct osd_state *osd_device = disp_dev->osd_device;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n");
-
- /* if this instance is doing IO */
- if (fh->io_allowed) {
- /* Reset io_usrs member of layer object */
- layer->io_usrs = 0;
-
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- layer->started = 0;
- /* Free buffers allocated */
- videobuf_queue_cancel(&layer->buffer_queue);
- videobuf_mmap_free(&layer->buffer_queue);
- }
-
- /* Decrement layer usrs counter */
- layer->usrs--;
- /* If this file handle has initialize encoder device, reset it */
- if (!layer->usrs) {
- if (cfg->pixfmt == PIXFMT_NV12) {
- struct vpbe_layer *otherlayer;
- otherlayer =
- _vpbe_display_get_other_win_layer(disp_dev, layer);
- osd_device->ops.disable_layer(osd_device,
- otherlayer->layer_info.id);
- osd_device->ops.release_layer(osd_device,
- otherlayer->layer_info.id);
- }
- osd_device->ops.disable_layer(osd_device,
- layer->layer_info.id);
- osd_device->ops.release_layer(osd_device,
- layer->layer_info.id);
- }
- /* Close the priority */
- v4l2_prio_close(&layer->prio, fh->prio);
- file->private_data = NULL;
-
- /* Free memory allocated to file handle object */
- kfree(fh);
-
- disp_dev->cbcr_ofst = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vpbe_display_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct v4l2_dbg_match *match = &reg->match;
- struct vpbe_fh *fh = file->private_data;
- struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev;
-
- if (match->type >= 2) {
- v4l2_subdev_call(vpbe_dev->venc,
- core,
- g_register,
- reg);
- }
-
- return 0;
-}
-
-static int vpbe_display_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- return 0;
-}
-#endif
-
-/* vpbe capture ioctl operations */
-static const struct v4l2_ioctl_ops vpbe_ioctl_ops = {
- .vidioc_querycap = vpbe_display_querycap,
- .vidioc_g_fmt_vid_out = vpbe_display_g_fmt,
- .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt,
- .vidioc_s_fmt_vid_out = vpbe_display_s_fmt,
- .vidioc_try_fmt_vid_out = vpbe_display_try_fmt,
- .vidioc_reqbufs = vpbe_display_reqbufs,
- .vidioc_querybuf = vpbe_display_querybuf,
- .vidioc_qbuf = vpbe_display_qbuf,
- .vidioc_dqbuf = vpbe_display_dqbuf,
- .vidioc_streamon = vpbe_display_streamon,
- .vidioc_streamoff = vpbe_display_streamoff,
- .vidioc_cropcap = vpbe_display_cropcap,
- .vidioc_g_crop = vpbe_display_g_crop,
- .vidioc_s_crop = vpbe_display_s_crop,
- .vidioc_g_priority = vpbe_display_g_priority,
- .vidioc_s_priority = vpbe_display_s_priority,
- .vidioc_s_std = vpbe_display_s_std,
- .vidioc_g_std = vpbe_display_g_std,
- .vidioc_enum_output = vpbe_display_enum_output,
- .vidioc_s_output = vpbe_display_s_output,
- .vidioc_g_output = vpbe_display_g_output,
- .vidioc_s_dv_preset = vpbe_display_s_dv_preset,
- .vidioc_g_dv_preset = vpbe_display_g_dv_preset,
- .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpbe_display_g_register,
- .vidioc_s_register = vpbe_display_s_register,
-#endif
-};
-
-static struct v4l2_file_operations vpbe_fops = {
- .owner = THIS_MODULE,
- .open = vpbe_display_open,
- .release = vpbe_display_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vpbe_display_mmap,
- .poll = vpbe_display_poll
-};
-
-static int vpbe_device_get(struct device *dev, void *data)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct vpbe_display *vpbe_disp = data;
-
- if (strcmp("vpbe_controller", pdev->name) == 0)
- vpbe_disp->vpbe_dev = platform_get_drvdata(pdev);
-
- if (strcmp("vpbe-osd", pdev->name) == 0)
- vpbe_disp->osd_device = platform_get_drvdata(pdev);
-
- return 0;
-}
-
-static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
- struct platform_device *pdev)
-{
- struct vpbe_layer *vpbe_display_layer = NULL;
- struct video_device *vbd = NULL;
-
- /* Allocate memory for four plane display objects */
-
- disp_dev->dev[i] =
- kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL);
-
- /* If memory allocation fails, return error */
- if (!disp_dev->dev[i]) {
- printk(KERN_ERR "ran out of memory\n");
- return -ENOMEM;
- }
- spin_lock_init(&disp_dev->dev[i]->irqlock);
- mutex_init(&disp_dev->dev[i]->opslock);
-
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[i];
- vbd = &vpbe_display_layer->video_dev;
- /* Initialize field of video device */
- vbd->release = video_device_release_empty;
- vbd->fops = &vpbe_fops;
- vbd->ioctl_ops = &vpbe_ioctl_ops;
- vbd->minor = -1;
- vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vbd->flags);
- vbd->lock = &vpbe_display_layer->opslock;
-
- if (disp_dev->vpbe_dev->current_timings.timings_type &
- VPBE_ENC_STD) {
- vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50);
- vbd->current_norm =
- disp_dev->vpbe_dev->
- current_timings.timings.std_id;
- } else
- vbd->current_norm = 0;
-
- snprintf(vbd->name, sizeof(vbd->name),
- "DaVinci_VPBE Display_DRIVER_V%d.%d.%d",
- (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff,
- (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff,
- (VPBE_DISPLAY_VERSION_CODE) & 0xff);
-
- vpbe_display_layer->device_id = i;
-
- vpbe_display_layer->layer_info.id =
- ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1);
-
- /* Initialize prio member of layer object */
- v4l2_prio_init(&vpbe_display_layer->prio);
-
- return 0;
-}
-
-static __devinit int register_device(struct vpbe_layer *vpbe_display_layer,
- struct vpbe_display *disp_dev,
- struct platform_device *pdev) {
- int err;
-
- v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
- "Trying to register VPBE display device.\n");
- v4l2_info(&disp_dev->vpbe_dev->v4l2_dev,
- "layer=%x,layer->video_dev=%x\n",
- (int)vpbe_display_layer,
- (int)&vpbe_display_layer->video_dev);
-
- err = video_register_device(&vpbe_display_layer->video_dev,
- VFL_TYPE_GRABBER,
- -1);
- if (err)
- return -ENODEV;
-
- vpbe_display_layer->disp_dev = disp_dev;
- /* set the driver data in platform device */
- platform_set_drvdata(pdev, disp_dev);
- video_set_drvdata(&vpbe_display_layer->video_dev,
- vpbe_display_layer);
-
- return 0;
-}
-
-
-
-/*
- * vpbe_display_probe()
- * This function creates device entries by register itself to the V4L2 driver
- * and initializes fields of each layer objects
- */
-static __devinit int vpbe_display_probe(struct platform_device *pdev)
-{
- struct vpbe_layer *vpbe_display_layer;
- struct vpbe_display *disp_dev;
- struct resource *res = NULL;
- int k;
- int i;
- int err;
- int irq;
-
- printk(KERN_DEBUG "vpbe_display_probe\n");
- /* Allocate memory for vpbe_display */
- disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
- if (!disp_dev) {
- printk(KERN_ERR "ran out of memory\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&disp_dev->dma_queue_lock);
- /*
- * Scan all the platform devices to find the vpbe
- * controller device and get the vpbe_dev object
- */
- err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev,
- vpbe_device_get);
- if (err < 0)
- return err;
- /* Initialize the vpbe display controller */
- if (NULL != disp_dev->vpbe_dev->ops.initialize) {
- err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev,
- disp_dev->vpbe_dev);
- if (err) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Error initing vpbe\n");
- err = -ENOMEM;
- goto probe_out;
- }
- }
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- if (init_vpbe_layer(i, disp_dev, pdev)) {
- err = -ENODEV;
- goto probe_out;
- }
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Unable to get VENC interrupt resource\n");
- err = -ENODEV;
- goto probe_out;
- }
-
- irq = res->start;
- if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
- disp_dev)) {
- v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
- "Unable to request interrupt\n");
- err = -ENODEV;
- goto probe_out;
- }
-
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
- err = -ENODEV;
- goto probe_out_irq;
- }
- }
-
- printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
- return 0;
-
-probe_out_irq:
- free_irq(res->start, disp_dev);
-probe_out:
- for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[k];
- /* Unregister video device */
- if (vpbe_display_layer) {
- video_unregister_device(
- &vpbe_display_layer->video_dev);
- kfree(disp_dev->dev[k]);
- }
- }
- kfree(disp_dev);
- return err;
-}
-
-/*
- * vpbe_display_remove()
- * It un-register hardware layer from V4L2 driver
- */
-static int vpbe_display_remove(struct platform_device *pdev)
-{
- struct vpbe_layer *vpbe_display_layer;
- struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
- struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct resource *res;
- int i;
-
- v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
-
- /* unregister irq */
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- free_irq(res->start, disp_dev);
-
- /* deinitialize the vpbe display controller */
- if (NULL != vpbe_dev->ops.deinitialize)
- vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
- /* un-register device */
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- /* Get the pointer to the layer object */
- vpbe_display_layer = disp_dev->dev[i];
- /* Unregister video device */
- video_unregister_device(&vpbe_display_layer->video_dev);
-
- }
- for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
- kfree(disp_dev->dev[i]);
- disp_dev->dev[i] = NULL;
- }
-
- return 0;
-}
-
-static struct platform_driver vpbe_display_driver = {
- .driver = {
- .name = VPBE_DISPLAY_DRIVER,
- .owner = THIS_MODULE,
- .bus = &platform_bus_type,
- },
- .probe = vpbe_display_probe,
- .remove = __devexit_p(vpbe_display_remove),
-};
-
-module_platform_driver(vpbe_display_driver);
-
-MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Texas Instruments");
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
deleted file mode 100644
index 49a845fb804a..000000000000
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ /dev/null
@@ -1,2079 +0,0 @@
-/*
- * Copyright (C) 2008-2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- *
- * Driver name : VPFE Capture driver
- * VPFE Capture driver allows applications to capture and stream video
- * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as
- * TVP5146 or Raw Bayer RGB image data from an image sensor
- * such as Microns' MT9T001, MT9T031 etc.
- *
- * These SoCs have, in common, a Video Processing Subsystem (VPSS) that
- * consists of a Video Processing Front End (VPFE) for capturing
- * video/raw image data and Video Processing Back End (VPBE) for displaying
- * YUV data through an in-built analog encoder or Digital LCD port. This
- * driver is for capture through VPFE. A typical EVM using these SoCs have
- * following high level configuration.
- *
- *
- * decoder(TVP5146/ YUV/
- * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF)
- * data input | |
- * V |
- * SDRAM |
- * V
- * Image Processor
- * |
- * V
- * SDRAM
- * The data flow happens from a decoder connected to the VPFE over a
- * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface
- * and to the input of VPFE through an optional MUX (if more inputs are
- * to be interfaced on the EVM). The input data is first passed through
- * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC
- * does very little or no processing on YUV data and does pre-process Raw
- * Bayer RGB data through modules such as Defect Pixel Correction (DFC)
- * Color Space Conversion (CSC), data gain/offset etc. After this, data
- * can be written to SDRAM or can be connected to the image processing
- * block such as IPIPE (on DM355 only).
- *
- * Features supported
- * - MMAP IO
- * - Capture using TVP5146 over BT.656
- * - support for interfacing decoders using sub device model
- * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV
- * data capture to SDRAM.
- * TODO list
- * - Support multiple REQBUF after open
- * - Support for de-allocating buffers through REQBUF
- * - Support for Raw Bayer RGB capture
- * - Support for chaining Image Processor
- * - Support for static allocation of buffers
- * - Support for USERPTR IO
- * - Support for STREAMON before QBUF
- * - Support for control ioctls
- */
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <media/v4l2-common.h>
-#include <linux/io.h>
-#include <media/davinci/vpfe_capture.h>
-#include "ccdc_hw_device.h"
-
-static int debug;
-static u32 numbuffers = 3;
-static u32 bufsize = (720 * 576 * 2);
-
-module_param(numbuffers, uint, S_IRUGO);
-module_param(bufsize, uint, S_IRUGO);
-module_param(debug, int, 0644);
-
-MODULE_PARM_DESC(numbuffers, "buffer count (default:3)");
-MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)");
-MODULE_PARM_DESC(debug, "Debug level 0-1");
-
-MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Texas Instruments");
-
-/* standard information */
-struct vpfe_standard {
- v4l2_std_id std_id;
- unsigned int width;
- unsigned int height;
- struct v4l2_fract pixelaspect;
- /* 0 - progressive, 1 - interlaced */
- int frame_format;
-};
-
-/* ccdc configuration */
-struct ccdc_config {
- /* This make sure vpfe is probed and ready to go */
- int vpfe_probed;
- /* name of ccdc device */
- char name[32];
-};
-
-/* data structures */
-static struct vpfe_config_params config_params = {
- .min_numbuffers = 3,
- .numbuffers = 3,
- .min_bufsize = 720 * 480 * 2,
- .device_bufsize = 720 * 576 * 2,
-};
-
-/* ccdc device registered */
-static struct ccdc_hw_device *ccdc_dev;
-/* lock for accessing ccdc information */
-static DEFINE_MUTEX(ccdc_lock);
-/* ccdc configuration */
-static struct ccdc_config *ccdc_cfg;
-
-const struct vpfe_standard vpfe_standards[] = {
- {V4L2_STD_525_60, 720, 480, {11, 10}, 1},
- {V4L2_STD_625_50, 720, 576, {54, 59}, 1},
-};
-
-/* Used when raw Bayer image from ccdc is directly captured to SDRAM */
-static const struct vpfe_pixel_format vpfe_pix_fmts[] = {
- {
- .fmtdesc = {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "Bayer GrRBGb 8bit A-Law compr.",
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- },
- .bpp = 1,
- },
- {
- .fmtdesc = {
- .index = 1,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "Bayer GrRBGb - 16bit",
- .pixelformat = V4L2_PIX_FMT_SBGGR16,
- },
- .bpp = 2,
- },
- {
- .fmtdesc = {
- .index = 2,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "Bayer GrRBGb 8bit DPCM compr.",
- .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8,
- },
- .bpp = 1,
- },
- {
- .fmtdesc = {
- .index = 3,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "YCbCr 4:2:2 Interleaved UYVY",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- },
- .bpp = 2,
- },
- {
- .fmtdesc = {
- .index = 4,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "YCbCr 4:2:2 Interleaved YUYV",
- .pixelformat = V4L2_PIX_FMT_YUYV,
- },
- .bpp = 2,
- },
- {
- .fmtdesc = {
- .index = 5,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .description = "Y/CbCr 4:2:0 - Semi planar",
- .pixelformat = V4L2_PIX_FMT_NV12,
- },
- .bpp = 1,
- },
-};
-
-/*
- * vpfe_lookup_pix_format()
- * lookup an entry in the vpfe pix format table based on pix_format
- */
-static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) {
- if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat)
- return &vpfe_pix_fmts[i];
- }
- return NULL;
-}
-
-/*
- * vpfe_register_ccdc_device. CCDC module calls this to
- * register with vpfe capture
- */
-int vpfe_register_ccdc_device(struct ccdc_hw_device *dev)
-{
- int ret = 0;
- printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name);
-
- BUG_ON(!dev->hw_ops.open);
- BUG_ON(!dev->hw_ops.enable);
- BUG_ON(!dev->hw_ops.set_hw_if_params);
- BUG_ON(!dev->hw_ops.configure);
- BUG_ON(!dev->hw_ops.set_buftype);
- BUG_ON(!dev->hw_ops.get_buftype);
- BUG_ON(!dev->hw_ops.enum_pix);
- BUG_ON(!dev->hw_ops.set_frame_format);
- BUG_ON(!dev->hw_ops.get_frame_format);
- BUG_ON(!dev->hw_ops.get_pixel_format);
- BUG_ON(!dev->hw_ops.set_pixel_format);
- BUG_ON(!dev->hw_ops.set_image_window);
- BUG_ON(!dev->hw_ops.get_image_window);
- BUG_ON(!dev->hw_ops.get_line_length);
- BUG_ON(!dev->hw_ops.getfid);
-
- mutex_lock(&ccdc_lock);
- if (NULL == ccdc_cfg) {
- /*
- * TODO. Will this ever happen? if so, we need to fix it.
- * Proabably we need to add the request to a linked list and
- * walk through it during vpfe probe
- */
- printk(KERN_ERR "vpfe capture not initialized\n");
- ret = -EFAULT;
- goto unlock;
- }
-
- if (strcmp(dev->name, ccdc_cfg->name)) {
- /* ignore this ccdc */
- ret = -EINVAL;
- goto unlock;
- }
-
- if (ccdc_dev) {
- printk(KERN_ERR "ccdc already registered\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- ccdc_dev = dev;
-unlock:
- mutex_unlock(&ccdc_lock);
- return ret;
-}
-EXPORT_SYMBOL(vpfe_register_ccdc_device);
-
-/*
- * vpfe_unregister_ccdc_device. CCDC module calls this to
- * unregister with vpfe capture
- */
-void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev)
-{
- if (NULL == dev) {
- printk(KERN_ERR "invalid ccdc device ptr\n");
- return;
- }
-
- printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n",
- dev->name);
-
- if (strcmp(dev->name, ccdc_cfg->name)) {
- /* ignore this ccdc */
- return;
- }
-
- mutex_lock(&ccdc_lock);
- ccdc_dev = NULL;
- mutex_unlock(&ccdc_lock);
- return;
-}
-EXPORT_SYMBOL(vpfe_unregister_ccdc_device);
-
-/*
- * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings
- */
-static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev,
- struct v4l2_format *f)
-{
- struct v4l2_rect image_win;
- enum ccdc_buftype buf_type;
- enum ccdc_frmfmt frm_fmt;
-
- memset(f, 0, sizeof(*f));
- f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ccdc_dev->hw_ops.get_image_window(&image_win);
- f->fmt.pix.width = image_win.width;
- f->fmt.pix.height = image_win.height;
- f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length();
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
- f->fmt.pix.height;
- buf_type = ccdc_dev->hw_ops.get_buftype();
- f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format();
- frm_fmt = ccdc_dev->hw_ops.get_frame_format();
- if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
- f->fmt.pix.field = V4L2_FIELD_NONE;
- else if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
- if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED)
- f->fmt.pix.field = V4L2_FIELD_SEQ_TB;
- else {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n");
- return -EINVAL;
- }
- } else {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * vpfe_config_ccdc_image_format()
- * For a pix format, configure ccdc to setup the capture
- */
-static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev)
-{
- enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED;
- int ret = 0;
-
- if (ccdc_dev->hw_ops.set_pixel_format(
- vpfe_dev->fmt.fmt.pix.pixelformat) < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "couldn't set pix format in ccdc\n");
- return -EINVAL;
- }
- /* configure the image window */
- ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop);
-
- switch (vpfe_dev->fmt.fmt.pix.field) {
- case V4L2_FIELD_INTERLACED:
- /* do nothing, since it is default */
- ret = ccdc_dev->hw_ops.set_buftype(
- CCDC_BUFTYPE_FLD_INTERLEAVED);
- break;
- case V4L2_FIELD_NONE:
- frm_fmt = CCDC_FRMFMT_PROGRESSIVE;
- /* buffer type only applicable for interlaced scan */
- break;
- case V4L2_FIELD_SEQ_TB:
- ret = ccdc_dev->hw_ops.set_buftype(
- CCDC_BUFTYPE_FLD_SEPARATED);
- break;
- default:
- return -EINVAL;
- }
-
- /* set the frame format */
- if (!ret)
- ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt);
- return ret;
-}
-/*
- * vpfe_config_image_format()
- * For a given standard, this functions sets up the default
- * pix format & crop values in the vpfe device and ccdc. It first
- * starts with defaults based values from the standard table.
- * It then checks if sub device support g_mbus_fmt and then override the
- * values based on that.Sets crop values to match with scan resolution
- * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the
- * values in ccdc
- */
-static int vpfe_config_image_format(struct vpfe_device *vpfe_dev,
- const v4l2_std_id *std_id)
-{
- struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev;
- struct v4l2_mbus_framefmt mbus_fmt;
- struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix;
- int i, ret = 0;
-
- for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) {
- if (vpfe_standards[i].std_id & *std_id) {
- vpfe_dev->std_info.active_pixels =
- vpfe_standards[i].width;
- vpfe_dev->std_info.active_lines =
- vpfe_standards[i].height;
- vpfe_dev->std_info.frame_format =
- vpfe_standards[i].frame_format;
- vpfe_dev->std_index = i;
- break;
- }
- }
-
- if (i == ARRAY_SIZE(vpfe_standards)) {
- v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n");
- return -EINVAL;
- }
-
- vpfe_dev->crop.top = 0;
- vpfe_dev->crop.left = 0;
- vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels;
- vpfe_dev->crop.height = vpfe_dev->std_info.active_lines;
- pix->width = vpfe_dev->crop.width;
- pix->height = vpfe_dev->crop.height;
-
- /* first field and frame format based on standard frame format */
- if (vpfe_dev->std_info.frame_format) {
- pix->field = V4L2_FIELD_INTERLACED;
- /* assume V4L2_PIX_FMT_UYVY as default */
- pix->pixelformat = V4L2_PIX_FMT_UYVY;
- v4l2_fill_mbus_format(&mbus_fmt, pix,
- V4L2_MBUS_FMT_YUYV10_2X10);
- } else {
- pix->field = V4L2_FIELD_NONE;
- /* assume V4L2_PIX_FMT_SBGGR8 */
- pix->pixelformat = V4L2_PIX_FMT_SBGGR8;
- v4l2_fill_mbus_format(&mbus_fmt, pix,
- V4L2_MBUS_FMT_SBGGR8_1X8);
- }
-
- /* if sub device supports g_mbus_fmt, override the defaults */
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
- sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt);
-
- if (ret && ret != -ENOIOCTLCMD) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "error in getting g_mbus_fmt from sub device\n");
- return ret;
- }
- v4l2_fill_pix_format(pix, &mbus_fmt);
- pix->bytesperline = pix->width * 2;
- pix->sizeimage = pix->bytesperline * pix->height;
-
- /* Sets the values in CCDC */
- ret = vpfe_config_ccdc_image_format(vpfe_dev);
- if (ret)
- return ret;
-
- /* Update the values of sizeimage and bytesperline */
- if (!ret) {
- pix->bytesperline = ccdc_dev->hw_ops.get_line_length();
- pix->sizeimage = pix->bytesperline * pix->height;
- }
- return ret;
-}
-
-static int vpfe_initialize_device(struct vpfe_device *vpfe_dev)
-{
- int ret = 0;
-
- /* set first input of current subdevice as the current input */
- vpfe_dev->current_input = 0;
-
- /* set default standard */
- vpfe_dev->std_index = 0;
-
- /* Configure the default format information */
- ret = vpfe_config_image_format(vpfe_dev,
- &vpfe_standards[vpfe_dev->std_index].std_id);
- if (ret)
- return ret;
-
- /* now open the ccdc device to initialize it */
- mutex_lock(&ccdc_lock);
- if (NULL == ccdc_dev) {
- v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n");
- ret = -ENODEV;
- goto unlock;
- }
-
- if (!try_module_get(ccdc_dev->owner)) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n");
- ret = -ENODEV;
- goto unlock;
- }
- ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev);
- if (!ret)
- vpfe_dev->initialized = 1;
-
- /* Clear all VPFE/CCDC interrupts */
- if (vpfe_dev->cfg->clr_intr)
- vpfe_dev->cfg->clr_intr(-1);
-
-unlock:
- mutex_unlock(&ccdc_lock);
- return ret;
-}
-
-/*
- * vpfe_open : It creates object of file handle structure and
- * stores it in private_data member of filepointer
- */
-static int vpfe_open(struct file *file)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n");
-
- if (!vpfe_dev->cfg->num_subdevs) {
- v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n");
- return -ENODEV;
- }
-
- /* Allocate memory for the file handle object */
- fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL);
- if (NULL == fh) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
- /* store pointer to fh in private_data member of file */
- file->private_data = fh;
- fh->vpfe_dev = vpfe_dev;
- mutex_lock(&vpfe_dev->lock);
- /* If decoder is not initialized. initialize it */
- if (!vpfe_dev->initialized) {
- if (vpfe_initialize_device(vpfe_dev)) {
- mutex_unlock(&vpfe_dev->lock);
- return -ENODEV;
- }
- }
- /* Increment device usrs counter */
- vpfe_dev->usrs++;
- /* Set io_allowed member to false */
- fh->io_allowed = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&vpfe_dev->prio, &fh->prio);
- mutex_unlock(&vpfe_dev->lock);
- return 0;
-}
-
-static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev)
-{
- unsigned long addr;
-
- vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
- struct videobuf_buffer, queue);
- list_del(&vpfe_dev->next_frm->queue);
- vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE;
- addr = videobuf_to_dma_contig(vpfe_dev->next_frm);
-
- ccdc_dev->hw_ops.setfbaddr(addr);
-}
-
-static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev)
-{
- unsigned long addr;
-
- addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
- addr += vpfe_dev->field_off;
- ccdc_dev->hw_ops.setfbaddr(addr);
-}
-
-static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev)
-{
- struct timeval timevalue;
-
- do_gettimeofday(&timevalue);
- vpfe_dev->cur_frm->ts = timevalue;
- vpfe_dev->cur_frm->state = VIDEOBUF_DONE;
- vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage;
- wake_up_interruptible(&vpfe_dev->cur_frm->done);
- vpfe_dev->cur_frm = vpfe_dev->next_frm;
-}
-
-/* ISR for VINT0*/
-static irqreturn_t vpfe_isr(int irq, void *dev_id)
-{
- struct vpfe_device *vpfe_dev = dev_id;
- enum v4l2_field field;
- int fid;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n");
- field = vpfe_dev->fmt.fmt.pix.field;
-
- /* if streaming not started, don't do anything */
- if (!vpfe_dev->started)
- goto clear_intr;
-
- /* only for 6446 this will be applicable */
- if (NULL != ccdc_dev->hw_ops.reset)
- ccdc_dev->hw_ops.reset();
-
- if (field == V4L2_FIELD_NONE) {
- /* handle progressive frame capture */
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "frame format is progressive...\n");
- if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
- vpfe_process_buffer_complete(vpfe_dev);
- goto clear_intr;
- }
-
- /* interlaced or TB capture check which field we are in hardware */
- fid = ccdc_dev->hw_ops.getfid();
-
- /* switch the software maintained field id */
- vpfe_dev->field_id ^= 1;
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n",
- fid, vpfe_dev->field_id);
- if (fid == vpfe_dev->field_id) {
- /* we are in-sync here,continue */
- if (fid == 0) {
- /*
- * One frame is just being captured. If the next frame
- * is available, release the current frame and move on
- */
- if (vpfe_dev->cur_frm != vpfe_dev->next_frm)
- vpfe_process_buffer_complete(vpfe_dev);
- /*
- * based on whether the two fields are stored
- * interleavely or separately in memory, reconfigure
- * the CCDC memory address
- */
- if (field == V4L2_FIELD_SEQ_TB) {
- vpfe_schedule_bottom_field(vpfe_dev);
- }
- goto clear_intr;
- }
- /*
- * if one field is just being captured configure
- * the next frame get the next frame from the empty
- * queue if no frame is available hold on to the
- * current buffer
- */
- spin_lock(&vpfe_dev->dma_queue_lock);
- if (!list_empty(&vpfe_dev->dma_queue) &&
- vpfe_dev->cur_frm == vpfe_dev->next_frm)
- vpfe_schedule_next_buffer(vpfe_dev);
- spin_unlock(&vpfe_dev->dma_queue_lock);
- } else if (fid == 0) {
- /*
- * out of sync. Recover from any hardware out-of-sync.
- * May loose one frame
- */
- vpfe_dev->field_id = fid;
- }
-clear_intr:
- if (vpfe_dev->cfg->clr_intr)
- vpfe_dev->cfg->clr_intr(irq);
-
- return IRQ_HANDLED;
-}
-
-/* vdint1_isr - isr handler for VINT1 interrupt */
-static irqreturn_t vdint1_isr(int irq, void *dev_id)
-{
- struct vpfe_device *vpfe_dev = dev_id;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n");
-
- /* if streaming not started, don't do anything */
- if (!vpfe_dev->started) {
- if (vpfe_dev->cfg->clr_intr)
- vpfe_dev->cfg->clr_intr(irq);
- return IRQ_HANDLED;
- }
-
- spin_lock(&vpfe_dev->dma_queue_lock);
- if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) &&
- !list_empty(&vpfe_dev->dma_queue) &&
- vpfe_dev->cur_frm == vpfe_dev->next_frm)
- vpfe_schedule_next_buffer(vpfe_dev);
- spin_unlock(&vpfe_dev->dma_queue_lock);
-
- if (vpfe_dev->cfg->clr_intr)
- vpfe_dev->cfg->clr_intr(irq);
-
- return IRQ_HANDLED;
-}
-
-static void vpfe_detach_irq(struct vpfe_device *vpfe_dev)
-{
- enum ccdc_frmfmt frame_format;
-
- frame_format = ccdc_dev->hw_ops.get_frame_format();
- if (frame_format == CCDC_FRMFMT_PROGRESSIVE)
- free_irq(vpfe_dev->ccdc_irq1, vpfe_dev);
-}
-
-static int vpfe_attach_irq(struct vpfe_device *vpfe_dev)
-{
- enum ccdc_frmfmt frame_format;
-
- frame_format = ccdc_dev->hw_ops.get_frame_format();
- if (frame_format == CCDC_FRMFMT_PROGRESSIVE) {
- return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr,
- IRQF_DISABLED, "vpfe_capture1",
- vpfe_dev);
- }
- return 0;
-}
-
-/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */
-static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev)
-{
- vpfe_dev->started = 0;
- ccdc_dev->hw_ops.enable(0);
- if (ccdc_dev->hw_ops.enable_out_to_sdram)
- ccdc_dev->hw_ops.enable_out_to_sdram(0);
-}
-
-/*
- * vpfe_release : This function deletes buffer queue, frees the
- * buffers and the vpfe file handle
- */
-static int vpfe_release(struct file *file)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh = file->private_data;
- struct vpfe_subdev_info *sdinfo;
- int ret;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n");
-
- /* Get the device lock */
- mutex_lock(&vpfe_dev->lock);
- /* if this instance is doing IO */
- if (fh->io_allowed) {
- if (vpfe_dev->started) {
- sdinfo = vpfe_dev->current_subdev;
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev,
- sdinfo->grp_id,
- video, s_stream, 0);
- if (ret && (ret != -ENOIOCTLCMD))
- v4l2_err(&vpfe_dev->v4l2_dev,
- "stream off failed in subdev\n");
- vpfe_stop_ccdc_capture(vpfe_dev);
- vpfe_detach_irq(vpfe_dev);
- videobuf_streamoff(&vpfe_dev->buffer_queue);
- }
- vpfe_dev->io_usrs = 0;
- vpfe_dev->numbuffers = config_params.numbuffers;
- }
-
- /* Decrement device usrs counter */
- vpfe_dev->usrs--;
- /* Close the priority */
- v4l2_prio_close(&vpfe_dev->prio, fh->prio);
- /* If this is the last file handle */
- if (!vpfe_dev->usrs) {
- vpfe_dev->initialized = 0;
- if (ccdc_dev->hw_ops.close)
- ccdc_dev->hw_ops.close(vpfe_dev->pdev);
- module_put(ccdc_dev->owner);
- }
- mutex_unlock(&vpfe_dev->lock);
- file->private_data = NULL;
- /* Free memory allocated to file handle object */
- kfree(fh);
- return 0;
-}
-
-/*
- * vpfe_mmap : It is used to map kernel space buffers
- * into user spaces
- */
-static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
-{
- /* Get the device object and file handle object */
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n");
-
- return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma);
-}
-
-/*
- * vpfe_poll: It is used for select/poll system call
- */
-static unsigned int vpfe_poll(struct file *file, poll_table *wait)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n");
-
- if (vpfe_dev->started)
- return videobuf_poll_stream(file,
- &vpfe_dev->buffer_queue, wait);
- return 0;
-}
-
-/* vpfe capture driver file operations */
-static const struct v4l2_file_operations vpfe_fops = {
- .owner = THIS_MODULE,
- .open = vpfe_open,
- .release = vpfe_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vpfe_mmap,
- .poll = vpfe_poll
-};
-
-/*
- * vpfe_check_format()
- * This function adjust the input pixel format as per hardware
- * capabilities and update the same in pixfmt.
- * Following algorithm used :-
- *
- * If given pixformat is not in the vpfe list of pix formats or not
- * supported by the hardware, current value of pixformat in the device
- * is used
- * If given field is not supported, then current field is used. If field
- * is different from current, then it is matched with that from sub device.
- * Minimum height is 2 lines for interlaced or tb field and 1 line for
- * progressive. Maximum height is clamped to active active lines of scan
- * Minimum width is 32 bytes in memory and width is clamped to active
- * pixels of scan.
- * bytesperline is a multiple of 32.
- */
-static const struct vpfe_pixel_format *
- vpfe_check_format(struct vpfe_device *vpfe_dev,
- struct v4l2_pix_format *pixfmt)
-{
- u32 min_height = 1, min_width = 32, max_width, max_height;
- const struct vpfe_pixel_format *vpfe_pix_fmt;
- u32 pix;
- int temp, found;
-
- vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
- if (NULL == vpfe_pix_fmt) {
- /*
- * use current pixel format in the vpfe device. We
- * will find this pix format in the table
- */
- pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
- vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
- }
-
- /* check if hw supports it */
- temp = 0;
- found = 0;
- while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) {
- if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) {
- found = 1;
- break;
- }
- temp++;
- }
-
- if (!found) {
- /* use current pixel format */
- pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat;
- /*
- * Since this is currently used in the vpfe device, we
- * will find this pix format in the table
- */
- vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat);
- }
-
- /* check what field format is supported */
- if (pixfmt->field == V4L2_FIELD_ANY) {
- /* if field is any, use current value as default */
- pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
- }
-
- /*
- * if field is not same as current field in the vpfe device
- * try matching the field with the sub device field
- */
- if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) {
- /*
- * If field value is not in the supported fields, use current
- * field used in the device as default
- */
- switch (pixfmt->field) {
- case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- /* if sub device is supporting progressive, use that */
- if (!vpfe_dev->std_info.frame_format)
- pixfmt->field = V4L2_FIELD_NONE;
- break;
- case V4L2_FIELD_NONE:
- if (vpfe_dev->std_info.frame_format)
- pixfmt->field = V4L2_FIELD_INTERLACED;
- break;
-
- default:
- /* use current field as default */
- pixfmt->field = vpfe_dev->fmt.fmt.pix.field;
- break;
- }
- }
-
- /* Now adjust image resolutions supported */
- if (pixfmt->field == V4L2_FIELD_INTERLACED ||
- pixfmt->field == V4L2_FIELD_SEQ_TB)
- min_height = 2;
-
- max_width = vpfe_dev->std_info.active_pixels;
- max_height = vpfe_dev->std_info.active_lines;
- min_width /= vpfe_pix_fmt->bpp;
-
- v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n",
- pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp);
-
- pixfmt->width = clamp((pixfmt->width), min_width, max_width);
- pixfmt->height = clamp((pixfmt->height), min_height, max_height);
-
- /* If interlaced, adjust height to be a multiple of 2 */
- if (pixfmt->field == V4L2_FIELD_INTERLACED)
- pixfmt->height &= (~1);
- /*
- * recalculate bytesperline and sizeimage since width
- * and height might have changed
- */
- pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31)
- & ~31);
- if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12)
- pixfmt->sizeimage =
- pixfmt->bytesperline * pixfmt->height +
- ((pixfmt->bytesperline * pixfmt->height) >> 1);
- else
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
-
- v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height ="
- " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n",
- pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp,
- pixfmt->bytesperline, pixfmt->sizeimage);
- return vpfe_pix_fmt;
-}
-
-static int vpfe_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n");
-
- cap->version = VPFE_CAPTURE_VERSION_CODE;
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
- strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info));
- strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card));
- return 0;
-}
-
-static int vpfe_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n");
- /* Fill in the information about format */
- *fmt = vpfe_dev->fmt;
- return ret;
-}
-
-static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- const struct vpfe_pixel_format *pix_fmt;
- int temp_index;
- u32 pix;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n");
-
- if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0)
- return -EINVAL;
-
- /* Fill in the information about format */
- pix_fmt = vpfe_lookup_pix_format(pix);
- if (NULL != pix_fmt) {
- temp_index = fmt->index;
- *fmt = pix_fmt->fmtdesc;
- fmt->index = temp_index;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vpfe_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- const struct vpfe_pixel_format *pix_fmts;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n");
-
- /* If streaming is started, return error */
- if (vpfe_dev->started) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n");
- return -EBUSY;
- }
-
- /* Check for valid frame format */
- pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix);
-
- if (NULL == pix_fmts)
- return -EINVAL;
-
- /* store the pixel format in the device object */
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- /* First detach any IRQ if currently attached */
- vpfe_detach_irq(vpfe_dev);
- vpfe_dev->fmt = *fmt;
- /* set image capture parameters in the ccdc */
- ret = vpfe_config_ccdc_image_format(vpfe_dev);
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- const struct vpfe_pixel_format *pix_fmts;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n");
-
- pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix);
- if (NULL == pix_fmts)
- return -EINVAL;
- return 0;
-}
-
-/*
- * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a
- * given app input index
- */
-static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev,
- int *subdev_index,
- int *subdev_input_index,
- int app_input_index)
-{
- struct vpfe_config *cfg = vpfe_dev->cfg;
- struct vpfe_subdev_info *sdinfo;
- int i, j = 0;
-
- for (i = 0; i < cfg->num_subdevs; i++) {
- sdinfo = &cfg->sub_devs[i];
- if (app_input_index < (j + sdinfo->num_inputs)) {
- *subdev_index = i;
- *subdev_input_index = app_input_index - j;
- return 0;
- }
- j += sdinfo->num_inputs;
- }
- return -EINVAL;
-}
-
-/*
- * vpfe_get_app_input - Get app input index for a given subdev input index
- * driver stores the input index of the current sub device and translate it
- * when application request the current input
- */
-static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev,
- int *app_input_index)
-{
- struct vpfe_config *cfg = vpfe_dev->cfg;
- struct vpfe_subdev_info *sdinfo;
- int i, j = 0;
-
- for (i = 0; i < cfg->num_subdevs; i++) {
- sdinfo = &cfg->sub_devs[i];
- if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) {
- if (vpfe_dev->current_input >= sdinfo->num_inputs)
- return -1;
- *app_input_index = j + vpfe_dev->current_input;
- return 0;
- }
- j += sdinfo->num_inputs;
- }
- return -EINVAL;
-}
-
-static int vpfe_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
- int subdev, index ;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n");
-
- if (vpfe_get_subdev_input_index(vpfe_dev,
- &subdev,
- &index,
- inp->index) < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev, "input information not found"
- " for the subdev\n");
- return -EINVAL;
- }
- sdinfo = &vpfe_dev->cfg->sub_devs[subdev];
- memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input));
- return 0;
-}
-
-static int vpfe_g_input(struct file *file, void *priv, unsigned int *index)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n");
-
- return vpfe_get_app_input_index(vpfe_dev, index);
-}
-
-
-static int vpfe_s_input(struct file *file, void *priv, unsigned int index)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
- int subdev_index, inp_index;
- struct vpfe_route *route;
- u32 input = 0, output = 0;
- int ret = -EINVAL;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n");
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- /*
- * If streaming is started return device busy
- * error
- */
- if (vpfe_dev->started) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n");
- ret = -EBUSY;
- goto unlock_out;
- }
-
- if (vpfe_get_subdev_input_index(vpfe_dev,
- &subdev_index,
- &inp_index,
- index) < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n");
- goto unlock_out;
- }
-
- sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index];
- route = &sdinfo->routes[inp_index];
- if (route && sdinfo->can_route) {
- input = route->input;
- output = route->output;
- }
-
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- video, s_routing, input, output, 0);
-
- if (ret) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "vpfe_doioctl:error in setting input in decoder\n");
- ret = -EINVAL;
- goto unlock_out;
- }
- vpfe_dev->current_subdev = sdinfo;
- vpfe_dev->current_input = index;
- vpfe_dev->std_index = 0;
-
- /* set the bus/interface parameter for the sub device in ccdc */
- ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params);
- if (ret)
- goto unlock_out;
-
- /* set the default image parameters in the device */
- ret = vpfe_config_image_format(vpfe_dev,
- &vpfe_standards[vpfe_dev->std_index].std_id);
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n");
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- sdinfo = vpfe_dev->current_subdev;
- if (ret)
- return ret;
- /* Call querystd function of decoder device */
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- video, querystd, std_id);
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n");
-
- /* Call decoder driver function to set the standard */
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- sdinfo = vpfe_dev->current_subdev;
- /* If streaming is started, return device busy error */
- if (vpfe_dev->started) {
- v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n");
- ret = -EBUSY;
- goto unlock_out;
- }
-
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, s_std, *std_id);
- if (ret < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n");
- goto unlock_out;
- }
- ret = vpfe_config_image_format(vpfe_dev, std_id);
-
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n");
-
- *std_id = vpfe_standards[vpfe_dev->std_index].std_id;
- return 0;
-}
-/*
- * Videobuf operations
- */
-static int vpfe_videobuf_setup(struct videobuf_queue *vq,
- unsigned int *count,
- unsigned int *size)
-{
- struct vpfe_fh *fh = vq->priv_data;
- struct vpfe_device *vpfe_dev = fh->vpfe_dev;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n");
- *size = vpfe_dev->fmt.fmt.pix.sizeimage;
- if (vpfe_dev->memory == V4L2_MEMORY_MMAP &&
- vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize)
- *size = config_params.device_bufsize;
-
- if (*count < config_params.min_numbuffers)
- *count = config_params.min_numbuffers;
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "count=%d, size=%d\n", *count, *size);
- return 0;
-}
-
-static int vpfe_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct vpfe_fh *fh = vq->priv_data;
- struct vpfe_device *vpfe_dev = fh->vpfe_dev;
- unsigned long addr;
- int ret;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n");
-
- /* If buffer is not initialized, initialize it */
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = vpfe_dev->fmt.fmt.pix.width;
- vb->height = vpfe_dev->fmt.fmt.pix.height;
- vb->size = vpfe_dev->fmt.fmt.pix.sizeimage;
- vb->field = field;
-
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret < 0)
- return ret;
-
- addr = videobuf_to_dma_contig(vb);
- /* Make sure user addresses are aligned to 32 bytes */
- if (!ALIGN(addr, 32))
- return -EINVAL;
-
- vb->state = VIDEOBUF_PREPARED;
- }
- return 0;
-}
-
-static void vpfe_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- /* Get the file handle object and device object */
- struct vpfe_fh *fh = vq->priv_data;
- struct vpfe_device *vpfe_dev = fh->vpfe_dev;
- unsigned long flags;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n");
-
- /* add the buffer to the DMA queue */
- spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
- list_add_tail(&vb->queue, &vpfe_dev->dma_queue);
- spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
-
- /* Change state of the buffer */
- vb->state = VIDEOBUF_QUEUED;
-}
-
-static void vpfe_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct vpfe_fh *fh = vq->priv_data;
- struct vpfe_device *vpfe_dev = fh->vpfe_dev;
- unsigned long flags;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n");
-
- /*
- * We need to flush the buffer from the dma queue since
- * they are de-allocated
- */
- spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags);
- INIT_LIST_HEAD(&vpfe_dev->dma_queue);
- spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags);
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static struct videobuf_queue_ops vpfe_videobuf_qops = {
- .buf_setup = vpfe_videobuf_setup,
- .buf_prepare = vpfe_videobuf_prepare,
- .buf_queue = vpfe_videobuf_queue,
- .buf_release = vpfe_videobuf_release,
-};
-
-/*
- * vpfe_reqbufs. currently support REQBUF only once opening
- * the device.
- */
-static int vpfe_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req_buf)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh = file->private_data;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n");
- return -EINVAL;
- }
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- if (vpfe_dev->io_usrs != 0) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n");
- ret = -EBUSY;
- goto unlock_out;
- }
-
- vpfe_dev->memory = req_buf->memory;
- videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue,
- &vpfe_videobuf_qops,
- vpfe_dev->pdev,
- &vpfe_dev->irqlock,
- req_buf->type,
- vpfe_dev->fmt.fmt.pix.field,
- sizeof(struct videobuf_buffer),
- fh, NULL);
-
- fh->io_allowed = 1;
- vpfe_dev->io_usrs = 1;
- INIT_LIST_HEAD(&vpfe_dev->dma_queue);
- ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf);
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
-
- if (vpfe_dev->memory != V4L2_MEMORY_MMAP) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n");
- return -EINVAL;
- }
- /* Call videobuf_querybuf to get information */
- return videobuf_querybuf(&vpfe_dev->buffer_queue, buf);
-}
-
-static int vpfe_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh = file->private_data;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
-
- /*
- * If this file handle is not allowed to do IO,
- * return error
- */
- if (!fh->io_allowed) {
- v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
- return -EACCES;
- }
- return videobuf_qbuf(&vpfe_dev->buffer_queue, p);
-}
-
-static int vpfe_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
- return videobuf_dqbuf(&vpfe_dev->buffer_queue,
- buf, file->f_flags & O_NONBLOCK);
-}
-
-static int vpfe_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, queryctrl, qctrl);
-
-}
-
-static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, g_ctrl, ctrl);
-}
-
-static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_subdev_info *sdinfo;
-
- sdinfo = vpfe_dev->current_subdev;
-
- return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- core, s_ctrl, ctrl);
-}
-
-/*
- * vpfe_calculate_offsets : This function calculates buffers offset
- * for top and bottom field
- */
-static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev)
-{
- struct v4l2_rect image_win;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n");
-
- ccdc_dev->hw_ops.get_image_window(&image_win);
- vpfe_dev->field_off = image_win.height * image_win.width;
-}
-
-/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */
-static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev)
-{
- ccdc_dev->hw_ops.enable(1);
- if (ccdc_dev->hw_ops.enable_out_to_sdram)
- ccdc_dev->hw_ops.enable_out_to_sdram(1);
- vpfe_dev->started = 1;
-}
-
-/*
- * vpfe_streamon. Assume the DMA queue is not empty.
- * application is expected to call QBUF before calling
- * this ioctl. If not, driver returns error
- */
-static int vpfe_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh = file->private_data;
- struct vpfe_subdev_info *sdinfo;
- unsigned long addr;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
-
- /* If file handle is not allowed IO, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
- return -EACCES;
- }
-
- sdinfo = vpfe_dev->current_subdev;
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- video, s_stream, 1);
-
- if (ret && (ret != -ENOIOCTLCMD)) {
- v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n");
- return -EINVAL;
- }
-
- /* If buffer queue is empty, return error */
- if (list_empty(&vpfe_dev->buffer_queue.stream)) {
- v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n");
- return -EIO;
- }
-
- /* Call videobuf_streamon to start streaming * in videobuf */
- ret = videobuf_streamon(&vpfe_dev->buffer_queue);
- if (ret)
- return ret;
-
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- goto streamoff;
- /* Get the next frame from the buffer queue */
- vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next,
- struct videobuf_buffer, queue);
- vpfe_dev->cur_frm = vpfe_dev->next_frm;
- /* Remove buffer from the buffer queue */
- list_del(&vpfe_dev->cur_frm->queue);
- /* Mark state of the current frame to active */
- vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE;
- /* Initialize field_id and started member */
- vpfe_dev->field_id = 0;
- addr = videobuf_to_dma_contig(vpfe_dev->cur_frm);
-
- /* Calculate field offset */
- vpfe_calculate_offsets(vpfe_dev);
-
- if (vpfe_attach_irq(vpfe_dev) < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "Error in attaching interrupt handle\n");
- ret = -EFAULT;
- goto unlock_out;
- }
- if (ccdc_dev->hw_ops.configure() < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "Error in configuring ccdc\n");
- ret = -EINVAL;
- goto unlock_out;
- }
- ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr));
- vpfe_start_ccdc_capture(vpfe_dev);
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
-streamoff:
- ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
- return ret;
-}
-
-static int vpfe_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- struct vpfe_fh *fh = file->private_data;
- struct vpfe_subdev_info *sdinfo;
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n");
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n");
- return -EINVAL;
- }
-
- /* If io is allowed for this file handle, return error */
- if (!fh->io_allowed) {
- v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n");
- return -EACCES;
- }
-
- /* If streaming is not started, return error */
- if (!vpfe_dev->started) {
- v4l2_err(&vpfe_dev->v4l2_dev, "device started\n");
- return -EINVAL;
- }
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- vpfe_stop_ccdc_capture(vpfe_dev);
- vpfe_detach_irq(vpfe_dev);
-
- sdinfo = vpfe_dev->current_subdev;
- ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id,
- video, s_stream, 0);
-
- if (ret && (ret != -ENOIOCTLCMD))
- v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n");
- ret = videobuf_streamoff(&vpfe_dev->buffer_queue);
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-static int vpfe_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *crop)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n");
-
- if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards))
- return -EINVAL;
-
- memset(crop, 0, sizeof(struct v4l2_cropcap));
- crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- crop->bounds.width = crop->defrect.width =
- vpfe_standards[vpfe_dev->std_index].width;
- crop->bounds.height = crop->defrect.height =
- vpfe_standards[vpfe_dev->std_index].height;
- crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect;
- return 0;
-}
-
-static int vpfe_g_crop(struct file *file, void *priv,
- struct v4l2_crop *crop)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n");
-
- crop->c = vpfe_dev->crop;
- return 0;
-}
-
-static int vpfe_s_crop(struct file *file, void *priv,
- struct v4l2_crop *crop)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- int ret = 0;
-
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n");
-
- if (vpfe_dev->started) {
- /* make sure streaming is not started */
- v4l2_err(&vpfe_dev->v4l2_dev,
- "Cannot change crop when streaming is ON\n");
- return -EBUSY;
- }
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- if (crop->c.top < 0 || crop->c.left < 0) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "doesn't support negative values for top & left\n");
- ret = -EINVAL;
- goto unlock_out;
- }
-
- /* adjust the width to 16 pixel boundary */
- crop->c.width = ((crop->c.width + 15) & ~0xf);
-
- /* make sure parameters are valid */
- if ((crop->c.left + crop->c.width >
- vpfe_dev->std_info.active_pixels) ||
- (crop->c.top + crop->c.height >
- vpfe_dev->std_info.active_lines)) {
- v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n");
- ret = -EINVAL;
- goto unlock_out;
- }
- ccdc_dev->hw_ops.set_image_window(&crop->c);
- vpfe_dev->fmt.fmt.pix.width = crop->c.width;
- vpfe_dev->fmt.fmt.pix.height = crop->c.height;
- vpfe_dev->fmt.fmt.pix.bytesperline =
- ccdc_dev->hw_ops.get_line_length();
- vpfe_dev->fmt.fmt.pix.sizeimage =
- vpfe_dev->fmt.fmt.pix.bytesperline *
- vpfe_dev->fmt.fmt.pix.height;
- vpfe_dev->crop = crop->c;
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-
-static long vpfe_param_handler(struct file *file, void *priv,
- bool valid_prio, int cmd, void *param)
-{
- struct vpfe_device *vpfe_dev = video_drvdata(file);
- int ret = 0;
-
- v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n");
-
- if (vpfe_dev->started) {
- /* only allowed if streaming is not started */
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "device already started\n");
- return -EBUSY;
- }
-
- ret = mutex_lock_interruptible(&vpfe_dev->lock);
- if (ret)
- return ret;
-
- switch (cmd) {
- case VPFE_CMD_S_CCDC_RAW_PARAMS:
- v4l2_warn(&vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
- if (ccdc_dev->hw_ops.set_params) {
- ret = ccdc_dev->hw_ops.set_params(param);
- if (ret) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Error setting parameters in CCDC\n");
- goto unlock_out;
- }
- if (vpfe_get_ccdc_image_format(vpfe_dev,
- &vpfe_dev->fmt) < 0) {
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "Invalid image format at CCDC\n");
- goto unlock_out;
- }
- } else {
- ret = -EINVAL;
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
- }
- break;
- default:
- ret = -ENOTTY;
- }
-unlock_out:
- mutex_unlock(&vpfe_dev->lock);
- return ret;
-}
-
-
-/* vpfe capture ioctl operations */
-static const struct v4l2_ioctl_ops vpfe_ioctl_ops = {
- .vidioc_querycap = vpfe_querycap,
- .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap,
- .vidioc_enum_input = vpfe_enum_input,
- .vidioc_g_input = vpfe_g_input,
- .vidioc_s_input = vpfe_s_input,
- .vidioc_querystd = vpfe_querystd,
- .vidioc_s_std = vpfe_s_std,
- .vidioc_g_std = vpfe_g_std,
- .vidioc_queryctrl = vpfe_queryctrl,
- .vidioc_g_ctrl = vpfe_g_ctrl,
- .vidioc_s_ctrl = vpfe_s_ctrl,
- .vidioc_reqbufs = vpfe_reqbufs,
- .vidioc_querybuf = vpfe_querybuf,
- .vidioc_qbuf = vpfe_qbuf,
- .vidioc_dqbuf = vpfe_dqbuf,
- .vidioc_streamon = vpfe_streamon,
- .vidioc_streamoff = vpfe_streamoff,
- .vidioc_cropcap = vpfe_cropcap,
- .vidioc_g_crop = vpfe_g_crop,
- .vidioc_s_crop = vpfe_s_crop,
- .vidioc_default = vpfe_param_handler,
-};
-
-static struct vpfe_device *vpfe_initialize(void)
-{
- struct vpfe_device *vpfe_dev;
-
- /* Default number of buffers should be 3 */
- if ((numbuffers > 0) &&
- (numbuffers < config_params.min_numbuffers))
- numbuffers = config_params.min_numbuffers;
-
- /*
- * Set buffer size to min buffers size if invalid buffer size is
- * given
- */
- if (bufsize < config_params.min_bufsize)
- bufsize = config_params.min_bufsize;
-
- config_params.numbuffers = numbuffers;
-
- if (numbuffers)
- config_params.device_bufsize = bufsize;
-
- /* Allocate memory for device objects */
- vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL);
-
- return vpfe_dev;
-}
-
-/*
- * vpfe_probe : This function creates device entries by register
- * itself to the V4L2 driver and initializes fields of each
- * device objects
- */
-static __init int vpfe_probe(struct platform_device *pdev)
-{
- struct vpfe_subdev_info *sdinfo;
- struct vpfe_config *vpfe_cfg;
- struct resource *res1;
- struct vpfe_device *vpfe_dev;
- struct i2c_adapter *i2c_adap;
- struct video_device *vfd;
- int ret = -ENOMEM, i, j;
- int num_subdevs = 0;
-
- /* Get the pointer to the device object */
- vpfe_dev = vpfe_initialize();
-
- if (!vpfe_dev) {
- v4l2_err(pdev->dev.driver,
- "Failed to allocate memory for vpfe_dev\n");
- return ret;
- }
-
- vpfe_dev->pdev = &pdev->dev;
-
- if (NULL == pdev->dev.platform_data) {
- v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n");
- ret = -ENODEV;
- goto probe_free_dev_mem;
- }
-
- vpfe_cfg = pdev->dev.platform_data;
- vpfe_dev->cfg = vpfe_cfg;
- if (NULL == vpfe_cfg->ccdc ||
- NULL == vpfe_cfg->card_name ||
- NULL == vpfe_cfg->sub_devs) {
- v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n");
- ret = -ENOENT;
- goto probe_free_dev_mem;
- }
-
- /* Allocate memory for ccdc configuration */
- ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL);
- if (NULL == ccdc_cfg) {
- v4l2_err(pdev->dev.driver,
- "Memory allocation failed for ccdc_cfg\n");
- goto probe_free_lock;
- }
-
- mutex_lock(&ccdc_lock);
-
- strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32);
- /* Get VINT0 irq resource */
- res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res1) {
- v4l2_err(pdev->dev.driver,
- "Unable to get interrupt for VINT0\n");
- ret = -ENODEV;
- goto probe_free_ccdc_cfg_mem;
- }
- vpfe_dev->ccdc_irq0 = res1->start;
-
- /* Get VINT1 irq resource */
- res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!res1) {
- v4l2_err(pdev->dev.driver,
- "Unable to get interrupt for VINT1\n");
- ret = -ENODEV;
- goto probe_free_ccdc_cfg_mem;
- }
- vpfe_dev->ccdc_irq1 = res1->start;
-
- ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED,
- "vpfe_capture0", vpfe_dev);
-
- if (0 != ret) {
- v4l2_err(pdev->dev.driver, "Unable to request interrupt\n");
- goto probe_free_ccdc_cfg_mem;
- }
-
- /* Allocate memory for video device */
- vfd = video_device_alloc();
- if (NULL == vfd) {
- ret = -ENOMEM;
- v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
- goto probe_out_release_irq;
- }
-
- /* Initialize field of video device */
- vfd->release = video_device_release;
- vfd->fops = &vpfe_fops;
- vfd->ioctl_ops = &vpfe_ioctl_ops;
- vfd->tvnorms = 0;
- vfd->current_norm = V4L2_STD_PAL;
- vfd->v4l2_dev = &vpfe_dev->v4l2_dev;
- snprintf(vfd->name, sizeof(vfd->name),
- "%s_V%d.%d.%d",
- CAPTURE_DRV_NAME,
- (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff,
- (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff,
- (VPFE_CAPTURE_VERSION_CODE) & 0xff);
- /* Set video_dev to the video device */
- vpfe_dev->video_dev = vfd;
-
- ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev);
- if (ret) {
- v4l2_err(pdev->dev.driver,
- "Unable to register v4l2 device.\n");
- goto probe_out_video_release;
- }
- v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n");
- spin_lock_init(&vpfe_dev->irqlock);
- spin_lock_init(&vpfe_dev->dma_queue_lock);
- mutex_init(&vpfe_dev->lock);
-
- /* Initialize field of the device objects */
- vpfe_dev->numbuffers = config_params.numbuffers;
-
- /* Initialize prio member of device object */
- v4l2_prio_init(&vpfe_dev->prio);
- /* register video device */
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "trying to register vpfe device.\n");
- v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
- "video_dev=%x\n", (int)&vpfe_dev->video_dev);
- vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ret = video_register_device(vpfe_dev->video_dev,
- VFL_TYPE_GRABBER, -1);
-
- if (ret) {
- v4l2_err(pdev->dev.driver,
- "Unable to register video device.\n");
- goto probe_out_v4l2_unregister;
- }
-
- v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n");
- /* set the driver data in platform device */
- platform_set_drvdata(pdev, vpfe_dev);
- /* set driver private data */
- video_set_drvdata(vpfe_dev->video_dev, vpfe_dev);
- i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id);
- num_subdevs = vpfe_cfg->num_subdevs;
- vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs,
- GFP_KERNEL);
- if (NULL == vpfe_dev->sd) {
- v4l2_err(&vpfe_dev->v4l2_dev,
- "unable to allocate memory for subdevice pointers\n");
- ret = -ENOMEM;
- goto probe_out_video_unregister;
- }
-
- for (i = 0; i < num_subdevs; i++) {
- struct v4l2_input *inps;
-
- sdinfo = &vpfe_cfg->sub_devs[i];
-
- /* Load up the subdevice */
- vpfe_dev->sd[i] =
- v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev,
- i2c_adap,
- &sdinfo->board_info,
- NULL);
- if (vpfe_dev->sd[i]) {
- v4l2_info(&vpfe_dev->v4l2_dev,
- "v4l2 sub device %s registered\n",
- sdinfo->name);
- vpfe_dev->sd[i]->grp_id = sdinfo->grp_id;
- /* update tvnorms from the sub devices */
- for (j = 0; j < sdinfo->num_inputs; j++) {
- inps = &sdinfo->inputs[j];
- vfd->tvnorms |= inps->std;
- }
- } else {
- v4l2_info(&vpfe_dev->v4l2_dev,
- "v4l2 sub device %s register fails\n",
- sdinfo->name);
- goto probe_sd_out;
- }
- }
-
- /* set first sub device as current one */
- vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0];
-
- /* We have at least one sub device to work with */
- mutex_unlock(&ccdc_lock);
- return 0;
-
-probe_sd_out:
- kfree(vpfe_dev->sd);
-probe_out_video_unregister:
- video_unregister_device(vpfe_dev->video_dev);
-probe_out_v4l2_unregister:
- v4l2_device_unregister(&vpfe_dev->v4l2_dev);
-probe_out_video_release:
- if (!video_is_registered(vpfe_dev->video_dev))
- video_device_release(vpfe_dev->video_dev);
-probe_out_release_irq:
- free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
-probe_free_ccdc_cfg_mem:
- kfree(ccdc_cfg);
-probe_free_lock:
- mutex_unlock(&ccdc_lock);
-probe_free_dev_mem:
- kfree(vpfe_dev);
- return ret;
-}
-
-/*
- * vpfe_remove : It un-register device from V4L2 driver
- */
-static int __devexit vpfe_remove(struct platform_device *pdev)
-{
- struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev);
-
- v4l2_info(pdev->dev.driver, "vpfe_remove\n");
-
- free_irq(vpfe_dev->ccdc_irq0, vpfe_dev);
- kfree(vpfe_dev->sd);
- v4l2_device_unregister(&vpfe_dev->v4l2_dev);
- video_unregister_device(vpfe_dev->video_dev);
- kfree(vpfe_dev);
- kfree(ccdc_cfg);
- return 0;
-}
-
-static int vpfe_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int vpfe_resume(struct device *dev)
-{
- return 0;
-}
-
-static const struct dev_pm_ops vpfe_dev_pm_ops = {
- .suspend = vpfe_suspend,
- .resume = vpfe_resume,
-};
-
-static struct platform_driver vpfe_driver = {
- .driver = {
- .name = CAPTURE_DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &vpfe_dev_pm_ops,
- },
- .probe = vpfe_probe,
- .remove = __devexit_p(vpfe_remove),
-};
-
-module_platform_driver(vpfe_driver);
diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c
deleted file mode 100644
index b3637aff8fee..000000000000
--- a/drivers/media/video/davinci/vpif.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * vpif - Video Port Interface driver
- * VPIF is a receiver and transmitter for video data. It has two channels(0, 1)
- * that receiveing video byte stream and two channels(2, 3) for video output.
- * The hardware supports SDTV, HDTV formats, raw data capture.
- * Currently, the driver supports NTSC and PAL standards.
- *
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <mach/hardware.h>
-
-#include "vpif.h"
-
-MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver");
-MODULE_LICENSE("GPL");
-
-#define VPIF_CH0_MAX_MODES (22)
-#define VPIF_CH1_MAX_MODES (02)
-#define VPIF_CH2_MAX_MODES (15)
-#define VPIF_CH3_MAX_MODES (02)
-
-static resource_size_t res_len;
-static struct resource *res;
-spinlock_t vpif_lock;
-
-void __iomem *vpif_base;
-struct clk *vpif_clk;
-
-/**
- * ch_params: video standard configuration parameters for vpif
- * The table must include all presets from supported subdevices.
- */
-const struct vpif_channel_config_params ch_params[] = {
- /* HDTV formats */
- {
- .name = "480p59_94",
- .width = 720,
- .height = 480,
- .frm_fmt = 1,
- .ycmux_mode = 0,
- .eav2sav = 138-8,
- .sav2eav = 720,
- .l1 = 1,
- .l3 = 43,
- .l5 = 523,
- .vsize = 525,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_480P59_94,
- },
- {
- .name = "576p50",
- .width = 720,
- .height = 576,
- .frm_fmt = 1,
- .ycmux_mode = 0,
- .eav2sav = 144-8,
- .sav2eav = 720,
- .l1 = 1,
- .l3 = 45,
- .l5 = 621,
- .vsize = 625,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_576P50,
- },
- {
- .name = "720p50",
- .width = 1280,
- .height = 720,
- .frm_fmt = 1,
- .ycmux_mode = 0,
- .eav2sav = 700-8,
- .sav2eav = 1280,
- .l1 = 1,
- .l3 = 26,
- .l5 = 746,
- .vsize = 750,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_720P50,
- },
- {
- .name = "720p60",
- .width = 1280,
- .height = 720,
- .frm_fmt = 1,
- .ycmux_mode = 0,
- .eav2sav = 370 - 8,
- .sav2eav = 1280,
- .l1 = 1,
- .l3 = 26,
- .l5 = 746,
- .vsize = 750,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_720P60,
- },
- {
- .name = "1080I50",
- .width = 1920,
- .height = 1080,
- .frm_fmt = 0,
- .ycmux_mode = 0,
- .eav2sav = 720 - 8,
- .sav2eav = 1920,
- .l1 = 1,
- .l3 = 21,
- .l5 = 561,
- .l7 = 563,
- .l9 = 584,
- .l11 = 1124,
- .vsize = 1125,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_1080I50,
- },
- {
- .name = "1080I60",
- .width = 1920,
- .height = 1080,
- .frm_fmt = 0,
- .ycmux_mode = 0,
- .eav2sav = 280 - 8,
- .sav2eav = 1920,
- .l1 = 1,
- .l3 = 21,
- .l5 = 561,
- .l7 = 563,
- .l9 = 584,
- .l11 = 1124,
- .vsize = 1125,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_1080I60,
- },
- {
- .name = "1080p60",
- .width = 1920,
- .height = 1080,
- .frm_fmt = 1,
- .ycmux_mode = 0,
- .eav2sav = 280 - 8,
- .sav2eav = 1920,
- .l1 = 1,
- .l3 = 42,
- .l5 = 1122,
- .vsize = 1125,
- .capture_format = 0,
- .vbi_supported = 0,
- .hd_sd = 1,
- .dv_preset = V4L2_DV_1080P60,
- },
-
- /* SDTV formats */
- {
- .name = "NTSC_M",
- .width = 720,
- .height = 480,
- .frm_fmt = 0,
- .ycmux_mode = 1,
- .eav2sav = 268,
- .sav2eav = 1440,
- .l1 = 1,
- .l3 = 23,
- .l5 = 263,
- .l7 = 266,
- .l9 = 286,
- .l11 = 525,
- .vsize = 525,
- .capture_format = 0,
- .vbi_supported = 1,
- .hd_sd = 0,
- .stdid = V4L2_STD_525_60,
- },
- {
- .name = "PAL_BDGHIK",
- .width = 720,
- .height = 576,
- .frm_fmt = 0,
- .ycmux_mode = 1,
- .eav2sav = 280,
- .sav2eav = 1440,
- .l1 = 1,
- .l3 = 23,
- .l5 = 311,
- .l7 = 313,
- .l9 = 336,
- .l11 = 624,
- .vsize = 625,
- .capture_format = 0,
- .vbi_supported = 1,
- .hd_sd = 0,
- .stdid = V4L2_STD_625_50,
- },
-};
-
-const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params);
-
-static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val)
-{
- if (val)
- vpif_set_bit(reg, bit);
- else
- vpif_clr_bit(reg, bit);
-}
-
-/* This structure is used to keep track of VPIF size register's offsets */
-struct vpif_registers {
- u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl;
- u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt;
- u32 vanc1_size, width_mask, len_mask;
- u8 max_modes;
-};
-
-static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = {
- /* Channel0 */
- {
- VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01,
- VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL,
- VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
- VPIF_CH0_MAX_MODES,
- },
- /* Channel1 */
- {
- VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01,
- VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL,
- VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF,
- VPIF_CH1_MAX_MODES,
- },
- /* Channel2 */
- {
- VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01,
- VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL,
- VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE,
- VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF,
- VPIF_CH2_MAX_MODES
- },
- /* Channel3 */
- {
- VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01,
- VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL,
- VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE,
- VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF,
- VPIF_CH3_MAX_MODES
- },
-};
-
-/* vpif_set_mode_info:
- * This function is used to set horizontal and vertical config parameters
- * As per the standard in the channel, configure the values of L1, L3,
- * L5, L7 L9, L11 in VPIF Register , also write width and height
- */
-static void vpif_set_mode_info(const struct vpif_channel_config_params *config,
- u8 channel_id, u8 config_channel_id)
-{
- u32 value;
-
- value = (config->eav2sav & vpifregs[config_channel_id].width_mask);
- value <<= VPIF_CH_LEN_SHIFT;
- value |= (config->sav2eav & vpifregs[config_channel_id].width_mask);
- regw(value, vpifregs[channel_id].h_cfg);
-
- value = (config->l1 & vpifregs[config_channel_id].len_mask);
- value <<= VPIF_CH_LEN_SHIFT;
- value |= (config->l3 & vpifregs[config_channel_id].len_mask);
- regw(value, vpifregs[channel_id].v_cfg_00);
-
- value = (config->l5 & vpifregs[config_channel_id].len_mask);
- value <<= VPIF_CH_LEN_SHIFT;
- value |= (config->l7 & vpifregs[config_channel_id].len_mask);
- regw(value, vpifregs[channel_id].v_cfg_01);
-
- value = (config->l9 & vpifregs[config_channel_id].len_mask);
- value <<= VPIF_CH_LEN_SHIFT;
- value |= (config->l11 & vpifregs[config_channel_id].len_mask);
- regw(value, vpifregs[channel_id].v_cfg_02);
-
- value = (config->vsize & vpifregs[config_channel_id].len_mask);
- regw(value, vpifregs[channel_id].v_cfg);
-}
-
-/* config_vpif_params
- * Function to set the parameters of a channel
- * Mainly modifies the channel ciontrol register
- * It sets frame format, yc mux mode
- */
-static void config_vpif_params(struct vpif_params *vpifparams,
- u8 channel_id, u8 found)
-{
- const struct vpif_channel_config_params *config = &vpifparams->std_info;
- u32 value, ch_nip, reg;
- u8 start, end;
- int i;
-
- start = channel_id;
- end = channel_id + found;
-
- for (i = start; i < end; i++) {
- reg = vpifregs[i].ch_ctrl;
- if (channel_id < 2)
- ch_nip = VPIF_CAPTURE_CH_NIP;
- else
- ch_nip = VPIF_DISPLAY_CH_NIP;
-
- vpif_wr_bit(reg, ch_nip, config->frm_fmt);
- vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode);
- vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT,
- vpifparams->video_params.storage_mode);
-
- /* Set raster scanning SDR Format */
- vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT);
- vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format);
-
- if (channel_id > 1) /* Set the Pixel enable bit */
- vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT);
- else if (config->capture_format) {
- /* Set the polarity of various pins */
- vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT,
- vpifparams->iface.fid_pol);
- vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT,
- vpifparams->iface.vd_pol);
- vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT,
- vpifparams->iface.hd_pol);
-
- value = regr(reg);
- /* Set data width */
- value &= ~(0x3u <<
- VPIF_CH_DATA_WIDTH_BIT);
- value |= ((vpifparams->params.data_sz) <<
- VPIF_CH_DATA_WIDTH_BIT);
- regw(value, reg);
- }
-
- /* Write the pitch in the driver */
- regw((vpifparams->video_params.hpitch),
- vpifregs[i].line_offset);
- }
-}
-
-/* vpif_set_video_params
- * This function is used to set video parameters in VPIF register
- */
-int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id)
-{
- const struct vpif_channel_config_params *config = &vpifparams->std_info;
- int found = 1;
-
- vpif_set_mode_info(config, channel_id, channel_id);
- if (!config->ycmux_mode) {
- /* YC are on separate channels (HDTV formats) */
- vpif_set_mode_info(config, channel_id + 1, channel_id);
- found = 2;
- }
-
- config_vpif_params(vpifparams, channel_id, found);
-
- regw(0x80, VPIF_REQ_SIZE);
- regw(0x01, VPIF_EMULATION_CTRL);
-
- return found;
-}
-EXPORT_SYMBOL(vpif_set_video_params);
-
-void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
- u8 channel_id)
-{
- u32 value;
-
- value = 0x3F8 & (vbiparams->hstart0);
- value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16);
- regw(value, vpifregs[channel_id].vanc0_strt);
-
- value = 0x3F8 & (vbiparams->hstart1);
- value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16);
- regw(value, vpifregs[channel_id].vanc1_strt);
-
- value = 0x3F8 & (vbiparams->hsize0);
- value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16);
- regw(value, vpifregs[channel_id].vanc0_size);
-
- value = 0x3F8 & (vbiparams->hsize1);
- value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16);
- regw(value, vpifregs[channel_id].vanc1_size);
-
-}
-EXPORT_SYMBOL(vpif_set_vbi_display_params);
-
-int vpif_channel_getfid(u8 channel_id)
-{
- return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK)
- >> VPIF_CH_FID_SHIFT;
-}
-EXPORT_SYMBOL(vpif_channel_getfid);
-
-static int __init vpif_probe(struct platform_device *pdev)
-{
- int status = 0;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOENT;
-
- res_len = resource_size(res);
-
- res = request_mem_region(res->start, res_len, res->name);
- if (!res)
- return -EBUSY;
-
- vpif_base = ioremap(res->start, res_len);
- if (!vpif_base) {
- status = -EBUSY;
- goto fail;
- }
-
- vpif_clk = clk_get(&pdev->dev, "vpif");
- if (IS_ERR(vpif_clk)) {
- status = PTR_ERR(vpif_clk);
- goto clk_fail;
- }
- clk_enable(vpif_clk);
-
- spin_lock_init(&vpif_lock);
- dev_info(&pdev->dev, "vpif probe success\n");
- return 0;
-
-clk_fail:
- iounmap(vpif_base);
-fail:
- release_mem_region(res->start, res_len);
- return status;
-}
-
-static int __devexit vpif_remove(struct platform_device *pdev)
-{
- if (vpif_clk) {
- clk_disable(vpif_clk);
- clk_put(vpif_clk);
- }
-
- iounmap(vpif_base);
- release_mem_region(res->start, res_len);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int vpif_suspend(struct device *dev)
-{
- clk_disable(vpif_clk);
- return 0;
-}
-
-static int vpif_resume(struct device *dev)
-{
- clk_enable(vpif_clk);
- return 0;
-}
-
-static const struct dev_pm_ops vpif_pm = {
- .suspend = vpif_suspend,
- .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_pm)
-#else
-#define vpif_pm_ops NULL
-#endif
-
-static struct platform_driver vpif_driver = {
- .driver = {
- .name = "vpif",
- .owner = THIS_MODULE,
- .pm = vpif_pm_ops,
- },
- .remove = __devexit_p(vpif_remove),
- .probe = vpif_probe,
-};
-
-static void vpif_exit(void)
-{
- platform_driver_unregister(&vpif_driver);
-}
-
-static int __init vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-subsys_initcall(vpif_init);
-module_exit(vpif_exit);
-
diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h
deleted file mode 100644
index c2ce4d97c279..000000000000
--- a/drivers/media/video/davinci/vpif.h
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * VPIF header file
- *
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef VPIF_H
-#define VPIF_H
-
-#include <linux/io.h>
-#include <linux/videodev2.h>
-#include <media/davinci/vpif_types.h>
-
-/* Maximum channel allowed */
-#define VPIF_NUM_CHANNELS (4)
-#define VPIF_CAPTURE_NUM_CHANNELS (2)
-#define VPIF_DISPLAY_NUM_CHANNELS (2)
-
-/* Macros to read/write registers */
-extern void __iomem *vpif_base;
-extern spinlock_t vpif_lock;
-
-#define regr(reg) readl((reg) + vpif_base)
-#define regw(value, reg) writel(value, (reg + vpif_base))
-
-/* Register Address Offsets */
-#define VPIF_PID (0x0000)
-#define VPIF_CH0_CTRL (0x0004)
-#define VPIF_CH1_CTRL (0x0008)
-#define VPIF_CH2_CTRL (0x000C)
-#define VPIF_CH3_CTRL (0x0010)
-
-#define VPIF_INTEN (0x0020)
-#define VPIF_INTEN_SET (0x0024)
-#define VPIF_INTEN_CLR (0x0028)
-#define VPIF_STATUS (0x002C)
-#define VPIF_STATUS_CLR (0x0030)
-#define VPIF_EMULATION_CTRL (0x0034)
-#define VPIF_REQ_SIZE (0x0038)
-
-#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040)
-#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044)
-#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048)
-#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c)
-#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050)
-#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054)
-#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058)
-#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c)
-#define VPIF_CH0_SP_CFG (0x0060)
-#define VPIF_CH0_IMG_ADD_OFST (0x0064)
-#define VPIF_CH0_HANC_ADD_OFST (0x0068)
-#define VPIF_CH0_H_CFG (0x006c)
-#define VPIF_CH0_V_CFG_00 (0x0070)
-#define VPIF_CH0_V_CFG_01 (0x0074)
-#define VPIF_CH0_V_CFG_02 (0x0078)
-#define VPIF_CH0_V_CFG_03 (0x007c)
-
-#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080)
-#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084)
-#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088)
-#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c)
-#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090)
-#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094)
-#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098)
-#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c)
-#define VPIF_CH1_SP_CFG (0x00a0)
-#define VPIF_CH1_IMG_ADD_OFST (0x00a4)
-#define VPIF_CH1_HANC_ADD_OFST (0x00a8)
-#define VPIF_CH1_H_CFG (0x00ac)
-#define VPIF_CH1_V_CFG_00 (0x00b0)
-#define VPIF_CH1_V_CFG_01 (0x00b4)
-#define VPIF_CH1_V_CFG_02 (0x00b8)
-#define VPIF_CH1_V_CFG_03 (0x00bc)
-
-#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0)
-#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4)
-#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8)
-#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc)
-#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0)
-#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4)
-#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8)
-#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc)
-#define VPIF_CH2_SP_CFG (0x00e0)
-#define VPIF_CH2_IMG_ADD_OFST (0x00e4)
-#define VPIF_CH2_HANC_ADD_OFST (0x00e8)
-#define VPIF_CH2_H_CFG (0x00ec)
-#define VPIF_CH2_V_CFG_00 (0x00f0)
-#define VPIF_CH2_V_CFG_01 (0x00f4)
-#define VPIF_CH2_V_CFG_02 (0x00f8)
-#define VPIF_CH2_V_CFG_03 (0x00fc)
-#define VPIF_CH2_HANC0_STRT (0x0100)
-#define VPIF_CH2_HANC0_SIZE (0x0104)
-#define VPIF_CH2_HANC1_STRT (0x0108)
-#define VPIF_CH2_HANC1_SIZE (0x010c)
-#define VPIF_CH2_VANC0_STRT (0x0110)
-#define VPIF_CH2_VANC0_SIZE (0x0114)
-#define VPIF_CH2_VANC1_STRT (0x0118)
-#define VPIF_CH2_VANC1_SIZE (0x011c)
-
-#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140)
-#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144)
-#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148)
-#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c)
-#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150)
-#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154)
-#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158)
-#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c)
-#define VPIF_CH3_SP_CFG (0x0160)
-#define VPIF_CH3_IMG_ADD_OFST (0x0164)
-#define VPIF_CH3_HANC_ADD_OFST (0x0168)
-#define VPIF_CH3_H_CFG (0x016c)
-#define VPIF_CH3_V_CFG_00 (0x0170)
-#define VPIF_CH3_V_CFG_01 (0x0174)
-#define VPIF_CH3_V_CFG_02 (0x0178)
-#define VPIF_CH3_V_CFG_03 (0x017c)
-#define VPIF_CH3_HANC0_STRT (0x0180)
-#define VPIF_CH3_HANC0_SIZE (0x0184)
-#define VPIF_CH3_HANC1_STRT (0x0188)
-#define VPIF_CH3_HANC1_SIZE (0x018c)
-#define VPIF_CH3_VANC0_STRT (0x0190)
-#define VPIF_CH3_VANC0_SIZE (0x0194)
-#define VPIF_CH3_VANC1_STRT (0x0198)
-#define VPIF_CH3_VANC1_SIZE (0x019c)
-
-#define VPIF_IODFT_CTRL (0x01c0)
-
-/* Functions for bit Manipulation */
-static inline void vpif_set_bit(u32 reg, u32 bit)
-{
- regw((regr(reg)) | (0x01 << bit), reg);
-}
-
-static inline void vpif_clr_bit(u32 reg, u32 bit)
-{
- regw(((regr(reg)) & ~(0x01 << bit)), reg);
-}
-
-/* Macro for Generating mask */
-#ifdef GENERATE_MASK
-#undef GENERATE_MASK
-#endif
-
-#define GENERATE_MASK(bits, pos) \
- ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos)
-
-/* Bit positions in the channel control registers */
-#define VPIF_CH_DATA_MODE_BIT (2)
-#define VPIF_CH_YC_MUX_BIT (3)
-#define VPIF_CH_SDR_FMT_BIT (4)
-#define VPIF_CH_HANC_EN_BIT (8)
-#define VPIF_CH_VANC_EN_BIT (9)
-
-#define VPIF_CAPTURE_CH_NIP (10)
-#define VPIF_DISPLAY_CH_NIP (11)
-
-#define VPIF_DISPLAY_PIX_EN_BIT (10)
-
-#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12)
-
-#define VPIF_CH_FID_POLARITY_BIT (15)
-#define VPIF_CH_V_VALID_POLARITY_BIT (14)
-#define VPIF_CH_H_VALID_POLARITY_BIT (13)
-#define VPIF_CH_DATA_WIDTH_BIT (28)
-
-#define VPIF_CH_CLK_EDGE_CTRL_BIT (31)
-
-/* Mask various length */
-#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0)
-#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0)
-#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0)
-#define VPIF_CH_LEN_SHIFT (16)
-
-/* VPIF masks for registers */
-#define VPIF_REQ_SIZE_MASK (0x1ff)
-
-/* bit posotion of interrupt vpif_ch_intr register */
-#define VPIF_INTEN_FRAME_CH0 (0x00000001)
-#define VPIF_INTEN_FRAME_CH1 (0x00000002)
-#define VPIF_INTEN_FRAME_CH2 (0x00000004)
-#define VPIF_INTEN_FRAME_CH3 (0x00000008)
-
-/* bit position of clock and channel enable in vpif_chn_ctrl register */
-
-#define VPIF_CH0_CLK_EN (0x00000002)
-#define VPIF_CH0_EN (0x00000001)
-#define VPIF_CH1_CLK_EN (0x00000002)
-#define VPIF_CH1_EN (0x00000001)
-#define VPIF_CH2_CLK_EN (0x00000002)
-#define VPIF_CH2_EN (0x00000001)
-#define VPIF_CH3_CLK_EN (0x00000002)
-#define VPIF_CH3_EN (0x00000001)
-#define VPIF_CH_CLK_EN (0x00000002)
-#define VPIF_CH_EN (0x00000001)
-
-#define VPIF_INT_TOP (0x00)
-#define VPIF_INT_BOTTOM (0x01)
-#define VPIF_INT_BOTH (0x02)
-
-#define VPIF_CH0_INT_CTRL_SHIFT (6)
-#define VPIF_CH1_INT_CTRL_SHIFT (6)
-#define VPIF_CH2_INT_CTRL_SHIFT (6)
-#define VPIF_CH3_INT_CTRL_SHIFT (6)
-#define VPIF_CH_INT_CTRL_SHIFT (6)
-
-#define VPIF_CH2_CLIP_ANC_EN 14
-#define VPIF_CH2_CLIP_ACTIVE_EN 13
-
-#define VPIF_CH3_CLIP_ANC_EN 14
-#define VPIF_CH3_CLIP_ACTIVE_EN 13
-
-/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
-#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\
- (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL))
-
-/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
-#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\
- (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL))
-
-/* enabled interrupt on both the fields on vpid_ch0_ctrl register */
-#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\
- (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL))
-
-/* enabled interrupt on both the fields on vpid_ch1_ctrl register */
-#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\
- (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL))
-
-#define VPIF_CH_FID_MASK (0x20)
-#define VPIF_CH_FID_SHIFT (5)
-
-#define VPIF_NTSC_VBI_START_FIELD0 (1)
-#define VPIF_NTSC_VBI_START_FIELD1 (263)
-#define VPIF_PAL_VBI_START_FIELD0 (624)
-#define VPIF_PAL_VBI_START_FIELD1 (311)
-
-#define VPIF_NTSC_HBI_START_FIELD0 (1)
-#define VPIF_NTSC_HBI_START_FIELD1 (263)
-#define VPIF_PAL_HBI_START_FIELD0 (624)
-#define VPIF_PAL_HBI_START_FIELD1 (311)
-
-#define VPIF_NTSC_VBI_COUNT_FIELD0 (20)
-#define VPIF_NTSC_VBI_COUNT_FIELD1 (19)
-#define VPIF_PAL_VBI_COUNT_FIELD0 (24)
-#define VPIF_PAL_VBI_COUNT_FIELD1 (25)
-
-#define VPIF_NTSC_HBI_COUNT_FIELD0 (263)
-#define VPIF_NTSC_HBI_COUNT_FIELD1 (262)
-#define VPIF_PAL_HBI_COUNT_FIELD0 (312)
-#define VPIF_PAL_HBI_COUNT_FIELD1 (313)
-
-#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720)
-#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720)
-#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268)
-#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280)
-
-#define VPIF_CH_VANC_EN (0x20)
-#define VPIF_DMA_REQ_SIZE (0x080)
-#define VPIF_EMULATION_DISABLE (0x01)
-
-extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS];
-
-/* inline function to enable/disable channel0 */
-static inline void enable_channel0(int enable)
-{
- if (enable)
- regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL);
- else
- regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL);
-}
-
-/* inline function to enable/disable channel1 */
-static inline void enable_channel1(int enable)
-{
- if (enable)
- regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL);
- else
- regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL);
-}
-
-/* inline function to enable interrupt for channel0 */
-static inline void channel0_intr_enable(int enable)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&vpif_lock, flags);
-
- if (enable) {
- regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
-
- regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
- VPIF_INTEN_SET);
- } else {
- regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0),
- VPIF_INTEN_SET);
- }
- spin_unlock_irqrestore(&vpif_lock, flags);
-}
-
-/* inline function to enable interrupt for channel1 */
-static inline void channel1_intr_enable(int enable)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&vpif_lock, flags);
-
- if (enable) {
- regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
-
- regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
- VPIF_INTEN_SET);
- } else {
- regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1),
- VPIF_INTEN_SET);
- }
- spin_unlock_irqrestore(&vpif_lock, flags);
-}
-
-/* inline function to set buffer addresses in case of Y/C non mux mode */
-static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
-}
-
-/* inline function to set buffer addresses in VPIF registers for video data */
-static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA);
-}
-
-static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
-
- regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA);
-}
-
-static inline void ch0_set_vbi_addr(unsigned long top_vbi,
- unsigned long btm_vbi, unsigned long a, unsigned long b)
-{
- regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC);
- regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC);
-}
-
-static inline void ch0_set_hbi_addr(unsigned long top_vbi,
- unsigned long btm_vbi, unsigned long a, unsigned long b)
-{
- regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC);
- regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC);
-}
-
-static inline void ch1_set_vbi_addr(unsigned long top_vbi,
- unsigned long btm_vbi, unsigned long a, unsigned long b)
-{
- regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC);
- regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC);
-}
-
-static inline void ch1_set_hbi_addr(unsigned long top_vbi,
- unsigned long btm_vbi, unsigned long a, unsigned long b)
-{
- regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC);
- regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC);
-}
-
-/* Inline function to enable raw vbi in the given channel */
-static inline void disable_raw_feature(u8 channel_id, u8 index)
-{
- u32 ctrl_reg;
- if (0 == channel_id)
- ctrl_reg = VPIF_CH0_CTRL;
- else
- ctrl_reg = VPIF_CH1_CTRL;
-
- if (1 == index)
- vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
- else
- vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
-}
-
-static inline void enable_raw_feature(u8 channel_id, u8 index)
-{
- u32 ctrl_reg;
- if (0 == channel_id)
- ctrl_reg = VPIF_CH0_CTRL;
- else
- ctrl_reg = VPIF_CH1_CTRL;
-
- if (1 == index)
- vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT);
- else
- vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT);
-}
-
-/* inline function to enable/disable channel2 */
-static inline void enable_channel2(int enable)
-{
- if (enable) {
- regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
- regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL);
- } else {
- regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL);
- regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL);
- }
-}
-
-/* inline function to enable/disable channel3 */
-static inline void enable_channel3(int enable)
-{
- if (enable) {
- regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
- regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL);
- } else {
- regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL);
- regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL);
- }
-}
-
-/* inline function to enable interrupt for channel2 */
-static inline void channel2_intr_enable(int enable)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&vpif_lock, flags);
-
- if (enable) {
- regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
- regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
- VPIF_INTEN_SET);
- } else {
- regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2),
- VPIF_INTEN_SET);
- }
- spin_unlock_irqrestore(&vpif_lock, flags);
-}
-
-/* inline function to enable interrupt for channel3 */
-static inline void channel3_intr_enable(int enable)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&vpif_lock, flags);
-
- if (enable) {
- regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET);
-
- regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
- VPIF_INTEN_SET);
- } else {
- regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN);
- regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3),
- VPIF_INTEN_SET);
- }
- spin_unlock_irqrestore(&vpif_lock, flags);
-}
-
-/* inline function to enable raw vbi data for channel2 */
-static inline void channel2_raw_enable(int enable, u8 index)
-{
- u32 mask;
-
- if (1 == index)
- mask = VPIF_CH_VANC_EN_BIT;
- else
- mask = VPIF_CH_HANC_EN_BIT;
-
- if (enable)
- vpif_set_bit(VPIF_CH2_CTRL, mask);
- else
- vpif_clr_bit(VPIF_CH2_CTRL, mask);
-}
-
-/* inline function to enable raw vbi data for channel3*/
-static inline void channel3_raw_enable(int enable, u8 index)
-{
- u32 mask;
-
- if (1 == index)
- mask = VPIF_CH_VANC_EN_BIT;
- else
- mask = VPIF_CH_HANC_EN_BIT;
-
- if (enable)
- vpif_set_bit(VPIF_CH3_CTRL, mask);
- else
- vpif_clr_bit(VPIF_CH3_CTRL, mask);
-}
-
-/* function to enable clipping (for both active and blanking regions) on ch 2 */
-static inline void channel2_clipping_enable(int enable)
-{
- if (enable) {
- vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
- vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
- } else {
- vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN);
- vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN);
- }
-}
-
-/* function to enable clipping (for both active and blanking regions) on ch 2 */
-static inline void channel3_clipping_enable(int enable)
-{
- if (enable) {
- vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
- vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
- } else {
- vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN);
- vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN);
- }
-}
-
-/* inline function to set buffer addresses in case of Y/C non mux mode */
-static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
-}
-
-/* inline function to set buffer addresses in VPIF registers for video data */
-static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA);
-}
-
-static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA);
- regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA);
- regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA);
- regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA);
-}
-
-/* inline function to set buffer addresses in VPIF registers for vbi data */
-static inline void ch2_set_vbi_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC);
- regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC);
-}
-
-static inline void ch3_set_vbi_addr(unsigned long top_strt_luma,
- unsigned long btm_strt_luma,
- unsigned long top_strt_chroma,
- unsigned long btm_strt_chroma)
-{
- regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC);
- regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC);
-}
-
-static inline int vpif_intr_status(int channel)
-{
- int status = 0;
- int mask;
-
- if (channel < 0 || channel > 3)
- return 0;
-
- mask = 1 << channel;
- status = regr(VPIF_STATUS) & mask;
- regw(status, VPIF_STATUS_CLR);
-
- return status;
-}
-
-#define VPIF_MAX_NAME (30)
-
-/* This structure will store size parameters as per the mode selected by user */
-struct vpif_channel_config_params {
- char name[VPIF_MAX_NAME]; /* Name of the mode */
- u16 width; /* Indicates width of the image */
- u16 height; /* Indicates height of the image */
- u8 frm_fmt; /* Interlaced (0) or progressive (1) */
- u8 ycmux_mode; /* This mode requires one (0) or two (1)
- channels */
- u16 eav2sav; /* length of eav 2 sav */
- u16 sav2eav; /* length of sav 2 eav */
- u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */
- u16 vsize; /* Vertical size of the image */
- u8 capture_format; /* Indicates whether capture format
- * is in BT or in CCD/CMOS */
- u8 vbi_supported; /* Indicates whether this mode
- * supports capturing vbi or not */
- u8 hd_sd; /* HDTV (1) or SDTV (0) format */
- v4l2_std_id stdid; /* SDTV format */
- u32 dv_preset; /* HDTV format */
-};
-
-extern const unsigned int vpif_ch_params_count;
-extern const struct vpif_channel_config_params ch_params[];
-
-struct vpif_video_params;
-struct vpif_params;
-struct vpif_vbi_params;
-
-int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id);
-void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams,
- u8 channel_id);
-int vpif_channel_getfid(u8 channel_id);
-
-enum data_size {
- _8BITS = 0,
- _10BITS,
- _12BITS,
-};
-
-/* Structure for vpif parameters for raw vbi data */
-struct vpif_vbi_params {
- __u32 hstart0; /* Horizontal start of raw vbi data for first field */
- __u32 vstart0; /* Vertical start of raw vbi data for first field */
- __u32 hsize0; /* Horizontal size of raw vbi data for first field */
- __u32 vsize0; /* Vertical size of raw vbi data for first field */
- __u32 hstart1; /* Horizontal start of raw vbi data for second field */
- __u32 vstart1; /* Vertical start of raw vbi data for second field */
- __u32 hsize1; /* Horizontal size of raw vbi data for second field */
- __u32 vsize1; /* Vertical size of raw vbi data for second field */
-};
-
-/* structure for vpif parameters */
-struct vpif_video_params {
- __u8 storage_mode; /* Indicates field or frame mode */
- unsigned long hpitch;
- v4l2_std_id stdid;
-};
-
-struct vpif_params {
- struct vpif_interface iface;
- struct vpif_video_params video_params;
- struct vpif_channel_config_params std_info;
- union param {
- struct vpif_vbi_params vbi_params;
- enum data_size data_sz;
- } params;
-};
-
-#endif /* End of #ifndef VPIF_H */
-
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
deleted file mode 100644
index 266025e5d81d..000000000000
--- a/drivers/media/video/davinci/vpif_capture.c
+++ /dev/null
@@ -1,2444 +0,0 @@
-/*
- * Copyright (C) 2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- *
- * TODO : add support for VBI & HBI data service
- * add static buffer allocation
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-
-#include "vpif_capture.h"
-#include "vpif.h"
-
-MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(VPIF_CAPTURE_VERSION);
-
-#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
-#define vpif_dbg(level, debug, fmt, arg...) \
- v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
-
-static int debug = 1;
-static u32 ch0_numbuffers = 3;
-static u32 ch1_numbuffers = 3;
-static u32 ch0_bufsize = 1920 * 1080 * 2;
-static u32 ch1_bufsize = 720 * 576 * 2;
-
-module_param(debug, int, 0644);
-module_param(ch0_numbuffers, uint, S_IRUGO);
-module_param(ch1_numbuffers, uint, S_IRUGO);
-module_param(ch0_bufsize, uint, S_IRUGO);
-module_param(ch1_bufsize, uint, S_IRUGO);
-
-MODULE_PARM_DESC(debug, "Debug level 0-1");
-MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)");
-MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)");
-MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)");
-MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)");
-
-static struct vpif_config_params config_params = {
- .min_numbuffers = 3,
- .numbuffers[0] = 3,
- .numbuffers[1] = 3,
- .min_bufsize[0] = 720 * 480 * 2,
- .min_bufsize[1] = 720 * 480 * 2,
- .channel_bufsize[0] = 1920 * 1080 * 2,
- .channel_bufsize[1] = 720 * 576 * 2,
-};
-
-/* global variables */
-static struct vpif_device vpif_obj = { {NULL} };
-static struct device *vpif_dev;
-static void vpif_calculate_offsets(struct channel_obj *ch);
-static void vpif_config_addr(struct channel_obj *ch, int muxmode);
-
-/**
- * buffer_prepare : callback function for buffer prepare
- * @vb: ptr to vb2_buffer
- *
- * This is the callback function for buffer prepare when vb2_qbuf()
- * function is called. The buffer is prepared and user space virtual address
- * or user address is converted into physical address
- */
-static int vpif_buffer_prepare(struct vb2_buffer *vb)
-{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vb2_queue *q = vb->vb2_queue;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long addr;
-
- vpif_dbg(2, debug, "vpif_buffer_prepare\n");
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (vb->state != VB2_BUF_STATE_ACTIVE &&
- vb->state != VB2_BUF_STATE_PREPARED) {
- vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- goto exit;
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
-
- if (q->streaming) {
- if (!IS_ALIGNED((addr + common->ytop_off), 8) ||
- !IS_ALIGNED((addr + common->ybtm_off), 8) ||
- !IS_ALIGNED((addr + common->ctop_off), 8) ||
- !IS_ALIGNED((addr + common->cbtm_off), 8))
- goto exit;
- }
- }
- return 0;
-exit:
- vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n");
- return -EINVAL;
-}
-
-/**
- * vpif_buffer_queue_setup : Callback function for buffer setup.
- * @vq: vb2_queue ptr
- * @fmt: v4l2 format
- * @nbuffers: ptr to number of buffers requested by application
- * @nplanes:: contains number of distinct video planes needed to hold a frame
- * @sizes[]: contains the size (in bytes) of each plane.
- * @alloc_ctxs: ptr to allocation context
- *
- * This callback function is called when reqbuf() is called to adjust
- * the buffer count and buffer size
- */
-static int vpif_buffer_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long size;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_buffer_setup\n");
-
- /* If memory type is not mmap, return */
- if (V4L2_MEMORY_MMAP == common->memory) {
- /* Calculate the size of the buffer */
- size = config_params.channel_bufsize[ch->channel_id];
- /*
- * Checking if the buffer size exceeds the available buffer
- * ycmux_mode = 0 means 1 channel mode HD and
- * ycmux_mode = 1 means 2 channels mode SD
- */
- if (ch->vpifparams.std_info.ycmux_mode == 0) {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- (config_params.video_limit[0]
- + config_params.video_limit[1]))
- (*nbuffers)--;
- } else {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- config_params.video_limit[ch->channel_id])
- (*nbuffers)--;
- }
-
- } else {
- size = common->fmt.fmt.pix.sizeimage;
- }
-
- if (*nbuffers < config_params.min_numbuffers)
- *nbuffers = config_params.min_numbuffers;
-
- *nplanes = 1;
- sizes[0] = size;
- alloc_ctxs[0] = common->alloc_ctx;
-
- return 0;
-}
-
-/**
- * vpif_buffer_queue : Callback function to add buffer to DMA queue
- * @vb: ptr to vb2_buffer
- */
-static void vpif_buffer_queue(struct vb2_buffer *vb)
-{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct channel_obj *ch = fh->channel;
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_buffer_queue\n");
-
- /* add the buffer to the DMA queue */
- list_add_tail(&buf->list, &common->dma_queue);
-}
-
-/**
- * vpif_buf_cleanup : Callback function to free buffer
- * @vb: ptr to vb2_buffer
- *
- * This function is called from the videobuf2 layer to free memory
- * allocated to the buffers
- */
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
- /* Get the file handle object and channel object */
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long flags;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- spin_lock_irqsave(&common->irqlock, flags);
- if (vb->state == VB2_BUF_STATE_ACTIVE)
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
-
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
- struct vpif_cap_buffer *buf = container_of(vb,
- struct vpif_cap_buffer, vb);
-
- INIT_LIST_HEAD(&buf->list);
-
- return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] =
- { {1, 1} };
-
-static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct vpif_capture_config *vpif_config_data =
- vpif_dev->platform_data;
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_params *vpif = &ch->vpifparams;
- unsigned long addr = 0;
- int ret;
-
- /* If buffer queue is empty, return error */
- if (list_empty(&common->dma_queue)) {
- vpif_dbg(1, debug, "buffer queue is empty\n");
- return -EIO;
- }
-
- /* Get the next frame from the buffer queue */
- common->cur_frm = common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_cap_buffer, list);
- /* Remove buffer from the buffer queue */
- list_del(&common->cur_frm->list);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
- /* Initialize field_id and started member */
- ch->field_id = 0;
- common->started = 1;
- addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
-
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((vpif->std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) &&
- (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ||
- (!vpif->std_info.frm_fmt &&
- (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_dbg(1, debug, "conflict in field format and std format\n");
- return -EINVAL;
- }
-
- /* configure 1 or 2 channel mode */
- ret = vpif_config_data->setup_input_channel_mode
- (vpif->std_info.ycmux_mode);
-
- if (ret < 0) {
- vpif_dbg(1, debug, "can't set vpif channel mode\n");
- return ret;
- }
-
- /* Call vpif_set_params function to set the parameters and addresses */
- ret = vpif_set_video_params(vpif, ch->channel_id);
-
- if (ret < 0) {
- vpif_dbg(1, debug, "can't set video params\n");
- return ret;
- }
-
- common->started = ret;
- vpif_config_addr(ch, ret);
-
- common->set_addr(addr + common->ytop_off,
- addr + common->ybtm_off,
- addr + common->ctop_off,
- addr + common->cbtm_off);
-
- /**
- * Set interrupt for both the fields in VPIF Register enable channel in
- * VPIF register
- */
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) {
- channel0_intr_assert();
- channel0_intr_enable(1);
- enable_channel0(1);
- }
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (common->started == 2)) {
- channel1_intr_assert();
- channel1_intr_enable(1);
- enable_channel1(1);
- }
- channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-
- return 0;
-}
-
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- if (!vb2_is_streaming(vq))
- return 0;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* release all active buffers */
- while (!list_empty(&common->dma_queue)) {
- common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_cap_buffer, list);
- list_del(&common->next_frm->list);
- vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
- }
-
- return 0;
-}
-
-static struct vb2_ops video_qops = {
- .queue_setup = vpif_buffer_queue_setup,
- .wait_prepare = vpif_wait_prepare,
- .wait_finish = vpif_wait_finish,
- .buf_init = vpif_buffer_init,
- .buf_prepare = vpif_buffer_prepare,
- .start_streaming = vpif_start_streaming,
- .stop_streaming = vpif_stop_streaming,
- .buf_cleanup = vpif_buf_cleanup,
- .buf_queue = vpif_buffer_queue,
-};
-
-/**
- * vpif_process_buffer_complete: process a completed buffer
- * @common: ptr to common channel object
- *
- * This function time stamp the buffer and mark it as DONE. It also
- * wake up any process waiting on the QUEUE and set the next buffer
- * as current
- */
-static void vpif_process_buffer_complete(struct common_obj *common)
-{
- do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
- vb2_buffer_done(&common->cur_frm->vb,
- VB2_BUF_STATE_DONE);
- /* Make curFrm pointing to nextFrm */
- common->cur_frm = common->next_frm;
-}
-
-/**
- * vpif_schedule_next_buffer: set next buffer address for capture
- * @common : ptr to common channel object
- *
- * This function will get next buffer from the dma queue and
- * set the buffer address in the vpif register for capture.
- * the buffer is marked active
- */
-static void vpif_schedule_next_buffer(struct common_obj *common)
-{
- unsigned long addr = 0;
-
- common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_cap_buffer, list);
- /* Remove that buffer from the buffer queue */
- list_del(&common->next_frm->list);
- common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
- addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
-
- /* Set top and bottom field addresses in VPIF registers */
- common->set_addr(addr + common->ytop_off,
- addr + common->ybtm_off,
- addr + common->ctop_off,
- addr + common->cbtm_off);
-}
-
-/**
- * vpif_channel_isr : ISR handler for vpif capture
- * @irq: irq number
- * @dev_id: dev_id ptr
- *
- * It changes status of the captured buffer, takes next buffer from the queue
- * and sets its address in VPIF registers
- */
-static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
-{
- struct vpif_device *dev = &vpif_obj;
- struct common_obj *common;
- struct channel_obj *ch;
- enum v4l2_field field;
- int channel_id = 0;
- int fid = -1, i;
-
- channel_id = *(int *)(dev_id);
- if (!vpif_intr_status(channel_id))
- return IRQ_NONE;
-
- ch = dev->dev[channel_id];
-
- field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
-
- for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) {
- common = &ch->common[i];
- /* skip If streaming is not started in this channel */
- if (0 == common->started)
- continue;
-
- /* Check the field format */
- if (1 == ch->vpifparams.std_info.frm_fmt) {
- /* Progressive mode */
- if (list_empty(&common->dma_queue))
- continue;
-
- if (!channel_first_int[i][channel_id])
- vpif_process_buffer_complete(common);
-
- channel_first_int[i][channel_id] = 0;
-
- vpif_schedule_next_buffer(common);
-
-
- channel_first_int[i][channel_id] = 0;
- } else {
- /**
- * Interlaced mode. If it is first interrupt, ignore
- * it
- */
- if (channel_first_int[i][channel_id]) {
- channel_first_int[i][channel_id] = 0;
- continue;
- }
- if (0 == i) {
- ch->field_id ^= 1;
- /* Get field id from VPIF registers */
- fid = vpif_channel_getfid(ch->channel_id);
- if (fid != ch->field_id) {
- /**
- * If field id does not match stored
- * field id, make them in sync
- */
- if (0 == fid)
- ch->field_id = fid;
- return IRQ_HANDLED;
- }
- }
- /* device field id and local field id are in sync */
- if (0 == fid) {
- /* this is even field */
- if (common->cur_frm == common->next_frm)
- continue;
-
- /* mark the current buffer as done */
- vpif_process_buffer_complete(common);
- } else if (1 == fid) {
- /* odd field */
- if (list_empty(&common->dma_queue) ||
- (common->cur_frm != common->next_frm))
- continue;
-
- vpif_schedule_next_buffer(common);
- }
- }
- }
- return IRQ_HANDLED;
-}
-
-/**
- * vpif_update_std_info() - update standard related info
- * @ch: ptr to channel object
- *
- * For a given standard selected by application, update values
- * in the device data structures
- */
-static int vpif_update_std_info(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_params *vpifparams = &ch->vpifparams;
- const struct vpif_channel_config_params *config;
- struct vpif_channel_config_params *std_info = &vpifparams->std_info;
- struct video_obj *vid_ch = &ch->video;
- int index;
-
- vpif_dbg(2, debug, "vpif_update_std_info\n");
-
- for (index = 0; index < vpif_ch_params_count; index++) {
- config = &ch_params[index];
- if (config->hd_sd == 0) {
- vpif_dbg(2, debug, "SD format\n");
- if (config->stdid & vid_ch->stdid) {
- memcpy(std_info, config, sizeof(*config));
- break;
- }
- } else {
- vpif_dbg(2, debug, "HD format\n");
- if (config->dv_preset == vid_ch->dv_preset) {
- memcpy(std_info, config, sizeof(*config));
- break;
- }
- }
- }
-
- /* standard not found */
- if (index == vpif_ch_params_count)
- return -EINVAL;
-
- common->fmt.fmt.pix.width = std_info->width;
- common->width = std_info->width;
- common->fmt.fmt.pix.height = std_info->height;
- common->height = std_info->height;
- common->fmt.fmt.pix.bytesperline = std_info->width;
- vpifparams->video_params.hpitch = std_info->width;
- vpifparams->video_params.storage_mode = std_info->frm_fmt;
-
- return 0;
-}
-
-/**
- * vpif_calculate_offsets : This function calculates buffers offsets
- * @ch : ptr to channel object
- *
- * This function calculates buffer offsets for Y and C in the top and
- * bottom field
- */
-static void vpif_calculate_offsets(struct channel_obj *ch)
-{
- unsigned int hpitch, vpitch, sizeimage;
- struct video_obj *vid_ch = &(ch->video);
- struct vpif_params *vpifparams = &ch->vpifparams;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- enum v4l2_field field = common->fmt.fmt.pix.field;
-
- vpif_dbg(2, debug, "vpif_calculate_offsets\n");
-
- if (V4L2_FIELD_ANY == field) {
- if (vpifparams->std_info.frm_fmt)
- vid_ch->buf_field = V4L2_FIELD_NONE;
- else
- vid_ch->buf_field = V4L2_FIELD_INTERLACED;
- } else
- vid_ch->buf_field = common->fmt.fmt.pix.field;
-
- sizeimage = common->fmt.fmt.pix.sizeimage;
-
- hpitch = common->fmt.fmt.pix.bytesperline;
- vpitch = sizeimage / (hpitch * 2);
-
- if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
- (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
- /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
- common->ytop_off = 0;
- common->ybtm_off = hpitch;
- common->ctop_off = sizeimage / 2;
- common->cbtm_off = sizeimage / 2 + hpitch;
- } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
- /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
- common->ytop_off = 0;
- common->ybtm_off = sizeimage / 4;
- common->ctop_off = sizeimage / 2;
- common->cbtm_off = common->ctop_off + sizeimage / 4;
- } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
- /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */
- common->ybtm_off = 0;
- common->ytop_off = sizeimage / 4;
- common->cbtm_off = sizeimage / 2;
- common->ctop_off = common->cbtm_off + sizeimage / 4;
- }
- if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
- (V4L2_FIELD_INTERLACED == vid_ch->buf_field))
- vpifparams->video_params.storage_mode = 1;
- else
- vpifparams->video_params.storage_mode = 0;
-
- if (1 == vpifparams->std_info.frm_fmt)
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline;
- else {
- if ((field == V4L2_FIELD_ANY)
- || (field == V4L2_FIELD_INTERLACED))
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline * 2;
- else
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline;
- }
-
- ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid;
-}
-
-/**
- * vpif_config_format: configure default frame format in the device
- * ch : ptr to channel object
- */
-static void vpif_config_format(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_config_format\n");
-
- common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if (config_params.numbuffers[ch->channel_id] == 0)
- common->memory = V4L2_MEMORY_USERPTR;
- else
- common->memory = V4L2_MEMORY_MMAP;
-
- common->fmt.fmt.pix.sizeimage
- = config_params.channel_bufsize[ch->channel_id];
-
- if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER)
- common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;
- else
- common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
- common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-}
-
-/**
- * vpif_get_default_field() - Get default field type based on interface
- * @vpif_params - ptr to vpif params
- */
-static inline enum v4l2_field vpif_get_default_field(
- struct vpif_interface *iface)
-{
- return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE :
- V4L2_FIELD_INTERLACED;
-}
-
-/**
- * vpif_check_format() - check given pixel format for compatibility
- * @ch - channel ptr
- * @pixfmt - Given pixel format
- * @update - update the values as per hardware requirement
- *
- * Check the application pixel format for S_FMT and update the input
- * values as per hardware limits for TRY_FMT. The default pixel and
- * field format is selected based on interface type.
- */
-static int vpif_check_format(struct channel_obj *ch,
- struct v4l2_pix_format *pixfmt,
- int update)
-{
- struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
- struct vpif_params *vpif_params = &ch->vpifparams;
- enum v4l2_field field = pixfmt->field;
- u32 sizeimage, hpitch, vpitch;
- int ret = -EINVAL;
-
- vpif_dbg(2, debug, "vpif_check_format\n");
- /**
- * first check for the pixel format. If if_type is Raw bayer,
- * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only
- * V4L2_PIX_FMT_YUV422P is supported
- */
- if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) {
- if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) {
- if (!update) {
- vpif_dbg(2, debug, "invalid pix format\n");
- goto exit;
- }
- pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
- }
- } else {
- if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) {
- if (!update) {
- vpif_dbg(2, debug, "invalid pixel format\n");
- goto exit;
- }
- pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P;
- }
- }
-
- if (!(VPIF_VALID_FIELD(field))) {
- if (!update) {
- vpif_dbg(2, debug, "invalid field format\n");
- goto exit;
- }
- /**
- * By default use FIELD_NONE for RAW Bayer capture
- * and FIELD_INTERLACED for other interfaces
- */
- field = vpif_get_default_field(&vpif_params->iface);
- } else if (field == V4L2_FIELD_ANY)
- /* unsupported field. Use default */
- field = vpif_get_default_field(&vpif_params->iface);
-
- /* validate the hpitch */
- hpitch = pixfmt->bytesperline;
- if (hpitch < vpif_params->std_info.width) {
- if (!update) {
- vpif_dbg(2, debug, "invalid hpitch\n");
- goto exit;
- }
- hpitch = vpif_params->std_info.width;
- }
-
- sizeimage = pixfmt->sizeimage;
-
- vpitch = sizeimage / (hpitch * 2);
-
- /* validate the vpitch */
- if (vpitch < vpif_params->std_info.height) {
- if (!update) {
- vpif_dbg(2, debug, "Invalid vpitch\n");
- goto exit;
- }
- vpitch = vpif_params->std_info.height;
- }
-
- /* Check for 8 byte alignment */
- if (!ALIGN(hpitch, 8)) {
- if (!update) {
- vpif_dbg(2, debug, "invalid pitch alignment\n");
- goto exit;
- }
- /* adjust to next 8 byte boundary */
- hpitch = (((hpitch + 7) / 8) * 8);
- }
- /* if update is set, modify the bytesperline and sizeimage */
- if (update) {
- pixfmt->bytesperline = hpitch;
- pixfmt->sizeimage = hpitch * vpitch * 2;
- }
- /**
- * Image width and height is always based on current standard width and
- * height
- */
- pixfmt->width = common->fmt.fmt.pix.width;
- pixfmt->height = common->fmt.fmt.pix.height;
- return 0;
-exit:
- return ret;
-}
-
-/**
- * vpif_config_addr() - function to configure buffer address in vpif
- * @ch - channel ptr
- * @muxmode - channel mux mode
- */
-static void vpif_config_addr(struct channel_obj *ch, int muxmode)
-{
- struct common_obj *common;
-
- vpif_dbg(2, debug, "vpif_config_addr\n");
-
- common = &(ch->common[VPIF_VIDEO_INDEX]);
-
- if (VPIF_CHANNEL1_VIDEO == ch->channel_id)
- common->set_addr = ch1_set_videobuf_addr;
- else if (2 == muxmode)
- common->set_addr = ch0_set_videobuf_addr_yc_nmux;
- else
- common->set_addr = ch0_set_videobuf_addr;
-}
-
-/**
- * vpif_mmap : It is used to map kernel space buffers into user spaces
- * @filep: file pointer
- * @vma: ptr to vm_area_struct
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- /* Get the channel object and file handle object */
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
-
- vpif_dbg(2, debug, "vpif_mmap\n");
-
- return vb2_mmap(&common->buffer_queue, vma);
-}
-
-/**
- * vpif_poll: It is used for select/poll system call
- * @filep: file pointer
- * @wait: poll table to wait
- */
-static unsigned int vpif_poll(struct file *filep, poll_table * wait)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *channel = fh->channel;
- struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]);
-
- vpif_dbg(2, debug, "vpif_poll\n");
-
- if (common->started)
- return vb2_poll(&common->buffer_queue, filep, wait);
- return 0;
-}
-
-/**
- * vpif_open : vpif open handler
- * @filep: file ptr
- *
- * It creates object of file handle structure and stores it in private_data
- * member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
- struct vpif_capture_config *config = vpif_dev->platform_data;
- struct video_device *vdev = video_devdata(filep);
- struct common_obj *common;
- struct video_obj *vid_ch;
- struct channel_obj *ch;
- struct vpif_fh *fh;
- int i;
-
- vpif_dbg(2, debug, "vpif_open\n");
-
- ch = video_get_drvdata(vdev);
-
- vid_ch = &ch->video;
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (NULL == ch->curr_subdev_info) {
- /**
- * search through the sub device to see a registered
- * sub device and make it as current sub device
- */
- for (i = 0; i < config->subdev_count; i++) {
- if (vpif_obj.sd[i]) {
- /* the sub device is registered */
- ch->curr_subdev_info = &config->subdev_info[i];
- /* make first input as the current input */
- vid_ch->input_idx = 0;
- break;
- }
- }
- if (i == config->subdev_count) {
- vpif_err("No sub device registered\n");
- return -ENOENT;
- }
- }
-
- /* Allocate memory for the file handle object */
- fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
- if (NULL == fh) {
- vpif_err("unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
-
- /* store pointer to fh in private_data member of filep */
- filep->private_data = fh;
- fh->channel = ch;
- fh->initialized = 0;
- /* If decoder is not initialized. initialize it */
- if (!ch->initialized) {
- fh->initialized = 1;
- ch->initialized = 1;
- memset(&(ch->vpifparams), 0, sizeof(struct vpif_params));
- }
- /* Increment channel usrs counter */
- ch->usrs++;
- /* Set io_allowed member to false */
- fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&ch->prio, &fh->prio);
- return 0;
-}
-
-/**
- * vpif_release : function to clean up file close
- * @filep: file pointer
- *
- * This function deletes buffer queue, frees the buffers and the vpif file
- * handle
- */
-static int vpif_release(struct file *filep)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- vpif_dbg(2, debug, "vpif_release\n");
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* if this instance is doing IO */
- if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
- /* Reset io_usrs member of channel object */
- common->io_usrs = 0;
- /* Disable channel as per its device type and channel id */
- if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
- enable_channel0(0);
- channel0_intr_enable(0);
- }
- if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
- common->started = 0;
- /* Free buffers allocated */
- vb2_queue_release(&common->buffer_queue);
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
- }
-
- /* Decrement channel usrs counter */
- ch->usrs--;
-
- /* Close the priority */
- v4l2_prio_close(&ch->prio, fh->prio);
-
- if (fh->initialized)
- ch->initialized = 0;
-
- filep->private_data = NULL;
- kfree(fh);
- return 0;
-}
-
-/**
- * vpif_reqbufs() - request buffer handler
- * @file: file ptr
- * @priv: file handle
- * @reqbuf: request buffer structure ptr
- */
-static int vpif_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- u8 index = 0;
- struct vb2_queue *q;
-
- vpif_dbg(2, debug, "vpif_reqbufs\n");
-
- /**
- * This file handle has not initialized the channel,
- * It is not allowed to do settings
- */
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev)
- return -EINVAL;
-
- index = VPIF_VIDEO_INDEX;
-
- common = &ch->common[index];
-
- if (0 != common->io_usrs)
- return -EBUSY;
-
- /* Initialize videobuf2 queue as per the buffer type */
- common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (!common->alloc_ctx) {
- vpif_err("Failed to get the context\n");
- return -EINVAL;
- }
- q = &common->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = fh;
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpif_cap_buffer);
-
- vb2_queue_init(q);
-
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed[index] = 1;
- /* Increment io usrs member of channel object to 1 */
- common->io_usrs = 1;
- /* Store type of memory requested in channel object */
- common->memory = reqbuf->memory;
- INIT_LIST_HEAD(&common->dma_queue);
-
- /* Allocate buffers */
- return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-
-/**
- * vpif_querybuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_querybuf\n");
-
- if (common->fmt.type != buf->type)
- return -EINVAL;
-
- if (common->memory != V4L2_MEMORY_MMAP) {
- vpif_dbg(1, debug, "Invalid memory\n");
- return -EINVAL;
- }
-
- return vb2_querybuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_qbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct v4l2_buffer tbuf = *buf;
-
- vpif_dbg(2, debug, "vpif_qbuf\n");
-
- if (common->fmt.type != tbuf.type) {
- vpif_err("invalid buffer type\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh io not allowed\n");
- return -EACCES;
- }
-
- return vb2_qbuf(&common->buffer_queue, buf);
-}
-
-/**
- * vpif_dqbuf() - query buffer handler
- * @file: file ptr
- * @priv: file handle
- * @buf: v4l2 buffer structure ptr
- */
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- vpif_dbg(2, debug, "vpif_dqbuf\n");
-
- return vb2_dqbuf(&common->buffer_queue, buf,
- (file->f_flags & O_NONBLOCK));
-}
-
-/**
- * vpif_streamon() - streamon handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
- struct vpif_params *vpif;
- int ret = 0;
-
- vpif_dbg(2, debug, "vpif_streamon\n");
-
- vpif = &ch->vpifparams;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- vpif_dbg(1, debug, "buffer type not supported\n");
- return -EINVAL;
- }
-
- /* If file handle is not allowed IO, return error */
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_dbg(1, debug, "io not allowed\n");
- return -EACCES;
- }
-
- /* If Streaming is already started, return error */
- if (common->started) {
- vpif_dbg(1, debug, "channel->started\n");
- return -EBUSY;
- }
-
- if ((ch->channel_id == VPIF_CHANNEL0_VIDEO &&
- oth_ch->common[VPIF_VIDEO_INDEX].started &&
- vpif->std_info.ycmux_mode == 0) ||
- ((ch->channel_id == VPIF_CHANNEL1_VIDEO) &&
- (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
- vpif_dbg(1, debug, "other channel is being used\n");
- return -EBUSY;
- }
-
- ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0);
- if (ret)
- return ret;
-
- /* Enable streamon on the sub device */
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
- s_stream, 1);
-
- if (ret && (ret != -ENOIOCTLCMD)) {
- vpif_dbg(1, debug, "stream on failed in subdev\n");
- return ret;
- }
-
- /* Call vb2_streamon to start streaming in videobuf2 */
- ret = vb2_streamon(&common->buffer_queue, buftype);
- if (ret) {
- vpif_dbg(1, debug, "vb2_streamon\n");
- return ret;
- }
-
- return ret;
-}
-
-/**
- * vpif_streamoff() - streamoff handler
- * @file: file ptr
- * @priv: file handle
- * @buftype: v4l2 buffer type
- */
-static int vpif_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
-
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret;
-
- vpif_dbg(2, debug, "vpif_streamoff\n");
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- vpif_dbg(1, debug, "buffer type not supported\n");
- return -EINVAL;
- }
-
- /* If io is allowed for this file handle, return error */
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_dbg(1, debug, "io not allowed\n");
- return -EACCES;
- }
-
- /* If streaming is not started, return error */
- if (!common->started) {
- vpif_dbg(1, debug, "channel->started\n");
- return -EINVAL;
- }
-
- /* disable channel */
- if (VPIF_CHANNEL0_VIDEO == ch->channel_id) {
- enable_channel0(0);
- channel0_intr_enable(0);
- } else {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
-
- common->started = 0;
-
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
- s_stream, 0);
-
- if (ret && (ret != -ENOIOCTLCMD))
- vpif_dbg(1, debug, "stream off failed in subdev\n");
-
- return vb2_streamoff(&common->buffer_queue, buftype);
-}
-
-/**
- * vpif_map_sub_device_to_input() - Maps sub device to input
- * @ch - ptr to channel
- * @config - ptr to capture configuration
- * @input_index - Given input index from application
- * @sub_device_index - index into sd table
- *
- * lookup the sub device information for a given input index.
- * we report all the inputs to application. inputs table also
- * has sub device name for the each input
- */
-static struct vpif_subdev_info *vpif_map_sub_device_to_input(
- struct channel_obj *ch,
- struct vpif_capture_config *vpif_cfg,
- int input_index,
- int *sub_device_index)
-{
- struct vpif_capture_chan_config *chan_cfg;
- struct vpif_subdev_info *subdev_info = NULL;
- const char *subdev_name = NULL;
- int i;
-
- vpif_dbg(2, debug, "vpif_map_sub_device_to_input\n");
-
- chan_cfg = &vpif_cfg->chan_config[ch->channel_id];
-
- /**
- * search through the inputs to find the sub device supporting
- * the input
- */
- for (i = 0; i < chan_cfg->input_count; i++) {
- /* For each sub device, loop through input */
- if (i == input_index) {
- subdev_name = chan_cfg->inputs[i].subdev_name;
- break;
- }
- }
-
- /* if reached maximum. return null */
- if (i == chan_cfg->input_count || (NULL == subdev_name))
- return subdev_info;
-
- /* loop through the sub device list to get the sub device info */
- for (i = 0; i < vpif_cfg->subdev_count; i++) {
- subdev_info = &vpif_cfg->subdev_info[i];
- if (!strcmp(subdev_info->name, subdev_name))
- break;
- }
-
- if (i == vpif_cfg->subdev_count)
- return subdev_info;
-
- /* check if the sub device is registered */
- if (NULL == vpif_obj.sd[i])
- return NULL;
-
- *sub_device_index = i;
- return subdev_info;
-}
-
-/**
- * vpif_querystd() - querystd handler
- * @file: file ptr
- * @priv: file handle
- * @std_id: ptr to std id
- *
- * This function is called to detect standard at the selected input
- */
-static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- int ret = 0;
-
- vpif_dbg(2, debug, "vpif_querystd\n");
-
- /* Call querystd function of decoder device */
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], video,
- querystd, std_id);
- if (ret < 0)
- vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
-
- return ret;
-}
-
-/**
- * vpif_g_std() - get STD handler
- * @file: file ptr
- * @priv: file handle
- * @std_id: ptr to std id
- */
-static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- vpif_dbg(2, debug, "vpif_g_std\n");
-
- *std = ch->video.stdid;
- return 0;
-}
-
-/**
- * vpif_s_std() - set STD handler
- * @file: file ptr
- * @priv: file handle
- * @std_id: ptr to std id
- */
-static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- vpif_dbg(2, debug, "vpif_s_std\n");
-
- if (common->started) {
- vpif_err("streaming in progress\n");
- return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
-
- fh->initialized = 1;
-
- /* Call encoder subdevice function to set the standard */
- ch->video.stdid = *std_id;
- ch->video.dv_preset = V4L2_DV_INVALID;
- memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
-
- /* Get the information about the standard */
- if (vpif_update_std_info(ch)) {
- vpif_err("Error getting the standard info\n");
- return -EINVAL;
- }
-
- /* Configure the default format information */
- vpif_config_format(ch);
-
- /* set standard in the sub device */
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
- s_std, *std_id);
- if (ret < 0)
- vpif_dbg(1, debug, "Failed to set standard for sub devices\n");
- return ret;
-}
-
-/**
- * vpif_enum_input() - ENUMINPUT handler
- * @file: file ptr
- * @priv: file handle
- * @input: ptr to input structure
- */
-static int vpif_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
-
- struct vpif_capture_config *config = vpif_dev->platform_data;
- struct vpif_capture_chan_config *chan_cfg;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- chan_cfg = &config->chan_config[ch->channel_id];
-
- if (input->index >= chan_cfg->input_count) {
- vpif_dbg(1, debug, "Invalid input index\n");
- return -EINVAL;
- }
-
- memcpy(input, &chan_cfg->inputs[input->index].input,
- sizeof(*input));
- return 0;
-}
-
-/**
- * vpif_g_input() - Get INPUT handler
- * @file: file ptr
- * @priv: file handle
- * @index: ptr to input index
- */
-static int vpif_g_input(struct file *file, void *priv, unsigned int *index)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
-
- *index = vid_ch->input_idx;
-
- return 0;
-}
-
-/**
- * vpif_s_input() - Set INPUT handler
- * @file: file ptr
- * @priv: file handle
- * @index: input index
- */
-static int vpif_s_input(struct file *file, void *priv, unsigned int index)
-{
- struct vpif_capture_config *config = vpif_dev->platform_data;
- struct vpif_capture_chan_config *chan_cfg;
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct video_obj *vid_ch = &ch->video;
- struct vpif_subdev_info *subdev_info;
- int ret = 0, sd_index = 0;
- u32 input = 0, output = 0;
-
- chan_cfg = &config->chan_config[ch->channel_id];
-
- if (common->started) {
- vpif_err("Streaming in progress\n");
- return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
-
- fh->initialized = 1;
- subdev_info = vpif_map_sub_device_to_input(ch, config, index,
- &sd_index);
- if (NULL == subdev_info) {
- vpif_dbg(1, debug,
- "couldn't lookup sub device for the input index\n");
- return -EINVAL;
- }
-
- /* first setup input path from sub device to vpif */
- if (config->setup_input_path) {
- ret = config->setup_input_path(ch->channel_id,
- subdev_info->name);
- if (ret < 0) {
- vpif_dbg(1, debug, "couldn't setup input path for the"
- " sub device %s, for input index %d\n",
- subdev_info->name, index);
- return ret;
- }
- }
-
- if (subdev_info->can_route) {
- input = subdev_info->input;
- output = subdev_info->output;
- ret = v4l2_subdev_call(vpif_obj.sd[sd_index], video, s_routing,
- input, output, 0);
- if (ret < 0) {
- vpif_dbg(1, debug, "Failed to set input\n");
- return ret;
- }
- }
- vid_ch->input_idx = index;
- ch->curr_subdev_info = subdev_info;
- ch->curr_sd_index = sd_index;
- /* copy interface parameters to vpif */
- ch->vpifparams.iface = subdev_info->vpif_if;
-
- /* update tvnorms from the sub device input info */
- ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std;
- return ret;
-}
-
-/**
- * vpif_enum_fmt_vid_cap() - ENUM_FMT handler
- * @file: file ptr
- * @priv: file handle
- * @index: input index
- */
-static int vpif_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- if (fmt->index != 0) {
- vpif_dbg(1, debug, "Invalid format index\n");
- return -EINVAL;
- }
-
- /* Fill in the information about format */
- if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) {
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb");
- fmt->pixelformat = V4L2_PIX_FMT_SBGGR8;
- } else {
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
- fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
- }
- return 0;
-}
-
-/**
- * vpif_try_fmt_vid_cap() - TRY_FMT handler
- * @file: file ptr
- * @priv: file handle
- * @fmt: ptr to v4l2 format structure
- */
-static int vpif_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- return vpif_check_format(ch, pixfmt, 1);
-}
-
-
-/**
- * vpif_g_fmt_vid_cap() - Set INPUT handler
- * @file: file ptr
- * @priv: file handle
- * @fmt: ptr to v4l2 format structure
- */
-static int vpif_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* Check the validity of the buffer type */
- if (common->fmt.type != fmt->type)
- return -EINVAL;
-
- /* Fill in the information about format */
- *fmt = common->fmt;
- return 0;
-}
-
-/**
- * vpif_s_fmt_vid_cap() - Set FMT handler
- * @file: file ptr
- * @priv: file handle
- * @fmt: ptr to v4l2 format structure
- */
-static int vpif_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct v4l2_pix_format *pixfmt;
- int ret = 0;
-
- vpif_dbg(2, debug, "%s\n", __func__);
-
- /* If streaming is started, return error */
- if (common->started) {
- vpif_dbg(1, debug, "Streaming is started\n");
- return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
-
- fh->initialized = 1;
-
- pixfmt = &fmt->fmt.pix;
- /* Check for valid field format */
- ret = vpif_check_format(ch, pixfmt, 0);
-
- if (ret)
- return ret;
- /* store the format in the channel object */
- common->fmt = *fmt;
- return 0;
-}
-
-/**
- * vpif_querycap() - QUERYCAP handler
- * @file: file ptr
- * @priv: file handle
- * @cap: ptr to v4l2_capability structure
- */
-static int vpif_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vpif_capture_config *config = vpif_dev->platform_data;
-
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, "vpif capture", sizeof(cap->driver));
- strlcpy(cap->bus_info, "VPIF Platform", sizeof(cap->bus_info));
- strlcpy(cap->card, config->card_name, sizeof(cap->card));
-
- return 0;
-}
-
-/**
- * vpif_g_priority() - get priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_g_priority(struct file *file, void *priv,
- enum v4l2_priority *prio)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *prio = v4l2_prio_max(&ch->prio);
-
- return 0;
-}
-
-/**
- * vpif_s_priority() - set priority handler
- * @file: file ptr
- * @priv: file handle
- * @prio: ptr to v4l2_priority structure
- */
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
-/**
- * vpif_cropcap() - cropcap handler
- * @file: file ptr
- * @priv: file handle
- * @crop: ptr to v4l2_cropcap structure
- */
-static int vpif_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *crop)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type)
- return -EINVAL;
-
- crop->bounds.left = 0;
- crop->bounds.top = 0;
- crop->bounds.height = common->height;
- crop->bounds.width = common->width;
- crop->defrect = crop->bounds;
- return 0;
-}
-
-/**
- * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_enum_dv_presets(struct file *file, void *priv,
- struct v4l2_dv_enum_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
- video, enum_dv_presets, preset);
-}
-
-/**
- * vpif_query_dv_presets() - QUERY_DV_PRESET handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_query_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
- video, query_dv_preset, preset);
-}
-/**
- * vpif_s_dv_presets() - S_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_s_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- if (common->started) {
- vpif_dbg(1, debug, "streaming in progress\n");
- return -EBUSY;
- }
-
- if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ||
- (VPIF_CHANNEL1_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (ret)
- return ret;
-
- fh->initialized = 1;
-
- /* Call encoder subdevice function to set the standard */
- if (mutex_lock_interruptible(&common->lock))
- return -ERESTARTSYS;
-
- ch->video.dv_preset = preset->preset;
- ch->video.stdid = V4L2_STD_UNKNOWN;
- memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
-
- /* Get the information about the standard */
- if (vpif_update_std_info(ch)) {
- vpif_dbg(1, debug, "Error getting the standard info\n");
- ret = -EINVAL;
- } else {
- /* Configure the default format information */
- vpif_config_format(ch);
-
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
- video, s_dv_preset, preset);
- }
-
- mutex_unlock(&common->lock);
-
- return ret;
-}
-/**
- * vpif_g_dv_presets() - G_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_g_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- preset->preset = ch->video.dv_preset;
-
- return 0;
-}
-
-/**
- * vpif_s_dv_timings() - S_DV_TIMINGS handler
- * @file: file ptr
- * @priv: file handle
- * @timings: digital video timings
- */
-static int vpif_s_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct vpif_params *vpifparams = &ch->vpifparams;
- struct vpif_channel_config_params *std_info = &vpifparams->std_info;
- struct video_obj *vid_ch = &ch->video;
- struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
- int ret;
-
- if (timings->type != V4L2_DV_BT_656_1120) {
- vpif_dbg(2, debug, "Timing type not defined\n");
- return -EINVAL;
- }
-
- /* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index],
- video, s_dv_timings, timings);
- if (ret == -ENOIOCTLCMD) {
- vpif_dbg(2, debug, "Custom DV timings not supported by "
- "subdevice\n");
- return -EINVAL;
- }
- if (ret < 0) {
- vpif_dbg(2, debug, "Error setting custom DV timings\n");
- return ret;
- }
-
- if (!(timings->bt.width && timings->bt.height &&
- (timings->bt.hbackporch ||
- timings->bt.hfrontporch ||
- timings->bt.hsync) &&
- timings->bt.vfrontporch &&
- (timings->bt.vbackporch ||
- timings->bt.vsync))) {
- vpif_dbg(2, debug, "Timings for width, height, "
- "horizontal back porch, horizontal sync, "
- "horizontal front porch, vertical back porch, "
- "vertical sync and vertical back porch "
- "must be defined\n");
- return -EINVAL;
- }
-
- *bt = timings->bt;
-
- /* Configure video port timings */
-
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
- std_info->sav2eav = bt->width;
-
- std_info->l1 = 1;
- std_info->l3 = bt->vsync + bt->vbackporch + 1;
-
- if (bt->interlaced) {
- if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
- std_info->l5 = std_info->vsize/2 -
- (bt->vfrontporch - 1);
- std_info->l7 = std_info->vsize/2 + 1;
- std_info->l9 = std_info->l7 + bt->il_vsync +
- bt->il_vbackporch + 1;
- std_info->l11 = std_info->vsize -
- (bt->il_vfrontporch - 1);
- } else {
- vpif_dbg(2, debug, "Required timing values for "
- "interlaced BT format missing\n");
- return -EINVAL;
- }
- } else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
- std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
- }
- strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
- std_info->width = bt->width;
- std_info->height = bt->height;
- std_info->frm_fmt = bt->interlaced ? 0 : 1;
- std_info->ycmux_mode = 0;
- std_info->capture_format = 0;
- std_info->vbi_supported = 0;
- std_info->hd_sd = 1;
- std_info->stdid = 0;
- std_info->dv_preset = V4L2_DV_INVALID;
-
- vid_ch->stdid = 0;
- vid_ch->dv_preset = V4L2_DV_INVALID;
- return 0;
-}
-
-/**
- * vpif_g_dv_timings() - G_DV_TIMINGS handler
- * @file: file ptr
- * @priv: file handle
- * @timings: digital video timings
- */
-static int vpif_g_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
- struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
-
- timings->bt = *bt;
-
- return 0;
-}
-
-/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
- g_register, reg);
-}
-
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_subdev_call(vpif_obj.sd[ch->curr_sd_index], core,
- s_register, reg);
-}
-#endif
-
-/*
- * vpif_log_status() - Status information
- * @file: file ptr
- * @priv: file handle
- *
- * Returns zero.
- */
-static int vpif_log_status(struct file *filep, void *priv)
-{
- /* status for sub devices */
- v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
-
- return 0;
-}
-
-/* vpif capture ioctl operations */
-static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
- .vidioc_querycap = vpif_querycap,
- .vidioc_g_priority = vpif_g_priority,
- .vidioc_s_priority = vpif_s_priority,
- .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap,
- .vidioc_enum_input = vpif_enum_input,
- .vidioc_s_input = vpif_s_input,
- .vidioc_g_input = vpif_g_input,
- .vidioc_reqbufs = vpif_reqbufs,
- .vidioc_querybuf = vpif_querybuf,
- .vidioc_querystd = vpif_querystd,
- .vidioc_s_std = vpif_s_std,
- .vidioc_g_std = vpif_g_std,
- .vidioc_qbuf = vpif_qbuf,
- .vidioc_dqbuf = vpif_dqbuf,
- .vidioc_streamon = vpif_streamon,
- .vidioc_streamoff = vpif_streamoff,
- .vidioc_cropcap = vpif_cropcap,
- .vidioc_enum_dv_presets = vpif_enum_dv_presets,
- .vidioc_s_dv_preset = vpif_s_dv_preset,
- .vidioc_g_dv_preset = vpif_g_dv_preset,
- .vidioc_query_dv_preset = vpif_query_dv_preset,
- .vidioc_s_dv_timings = vpif_s_dv_timings,
- .vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
- .vidioc_log_status = vpif_log_status,
-};
-
-/* vpif file operations */
-static struct v4l2_file_operations vpif_fops = {
- .owner = THIS_MODULE,
- .open = vpif_open,
- .release = vpif_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vpif_mmap,
- .poll = vpif_poll
-};
-
-/* vpif video template */
-static struct video_device vpif_video_template = {
- .name = "vpif",
- .fops = &vpif_fops,
- .minor = -1,
- .ioctl_ops = &vpif_ioctl_ops,
-};
-
-/**
- * initialize_vpif() - Initialize vpif data structures
- *
- * Allocate memory for data structures and initialize them
- */
-static int initialize_vpif(void)
-{
- int err = 0, i, j;
- int free_channel_objects_index;
-
- /* Default number of buffers should be 3 */
- if ((ch0_numbuffers > 0) &&
- (ch0_numbuffers < config_params.min_numbuffers))
- ch0_numbuffers = config_params.min_numbuffers;
- if ((ch1_numbuffers > 0) &&
- (ch1_numbuffers < config_params.min_numbuffers))
- ch1_numbuffers = config_params.min_numbuffers;
-
- /* Set buffer size to min buffers size if it is invalid */
- if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO])
- ch0_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL0_VIDEO];
- if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO])
- ch1_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL1_VIDEO];
-
- config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers;
- config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers;
- if (ch0_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO]
- = ch0_bufsize;
- }
- if (ch1_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO]
- = ch1_bufsize;
- }
-
- /* Allocate memory for six channel objects */
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
- vpif_obj.dev[i] =
- kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL);
- /* If memory allocation fails, return error */
- if (!vpif_obj.dev[i]) {
- free_channel_objects_index = i;
- err = -ENOMEM;
- goto vpif_init_free_channel_objects;
- }
- }
- return 0;
-
-vpif_init_free_channel_objects:
- for (j = 0; j < free_channel_objects_index; j++)
- kfree(vpif_obj.dev[j]);
- return err;
-}
-
-/**
- * vpif_probe : This function probes the vpif capture driver
- * @pdev: platform device pointer
- *
- * This creates device entries by register itself to the V4L2 driver and
- * initializes fields of each channel objects
- */
-static __init int vpif_probe(struct platform_device *pdev)
-{
- struct vpif_subdev_info *subdevdata;
- struct vpif_capture_config *config;
- int i, j, k, m, q, err;
- struct i2c_adapter *i2c_adap;
- struct channel_obj *ch;
- struct common_obj *common;
- struct video_device *vfd;
- struct resource *res;
- int subdev_count;
- size_t size;
-
- vpif_dev = &pdev->dev;
-
- err = initialize_vpif();
- if (err) {
- v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
- return err;
- }
-
- err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
- if (err) {
- v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
- return err;
- }
-
- k = 0;
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Capture",
- (void *)(&vpif_obj.dev[k]->channel_id))) {
- err = -EBUSY;
- i--;
- goto vpif_int_err;
- }
- }
- k++;
- }
-
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- /* Allocate memory for video device */
- vfd = video_device_alloc();
- if (NULL == vfd) {
- for (j = 0; j < i; j++) {
- ch = vpif_obj.dev[j];
- video_device_release(ch->video_dev);
- }
- err = -ENOMEM;
- goto vpif_dev_alloc_err;
- }
-
- /* Initialize field of video device */
- *vfd = vpif_video_template;
- vfd->v4l2_dev = &vpif_obj.v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name),
- "VPIF_Capture_DRIVER_V%s",
- VPIF_CAPTURE_VERSION);
- /* Set video_dev to the video device */
- ch->video_dev = vfd;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- size = resource_size(res);
- /* The resources are divided into two equal memory and when we
- * have HD output we can add them together
- */
- for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
- /* only enabled if second resource exists */
- config_params.video_limit[ch->channel_id] = 0;
- if (size)
- config_params.video_limit[ch->channel_id] =
- size/2;
- }
- }
-
- for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
- common = &(ch->common[VPIF_VIDEO_INDEX]);
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags);
- ch->video_dev->lock = &common->lock;
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 1 : 0));
- if (err)
- goto probe_out;
-
- video_set_drvdata(ch->video_dev, ch);
-
- }
-
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
-
- subdev_count = config->subdev_count;
- vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
- GFP_KERNEL);
- if (vpif_obj.sd == NULL) {
- vpif_err("unable to allocate memory for subdevice pointers\n");
- err = -ENOMEM;
- goto probe_out;
- }
-
- for (i = 0; i < subdev_count; i++) {
- subdevdata = &config->subdev_info[i];
- vpif_obj.sd[i] =
- v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata->board_info,
- NULL);
-
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- goto probe_subdev_out;
- }
- v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
- subdevdata->name);
-
- if (vpif_obj.sd[i])
- vpif_obj.sd[i]->grp_id = 1 << i;
- }
-
- v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
- return 0;
-
-probe_subdev_out:
- /* free sub devices memory */
- kfree(vpif_obj.sd);
-
- j = VPIF_CAPTURE_MAX_DEVICES;
-probe_out:
- for (k = 0; k < j; k++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[k];
- /* Unregister video device */
- video_unregister_device(ch->video_dev);
- }
-
-vpif_dev_alloc_err:
- k = VPIF_CAPTURE_MAX_DEVICES-1;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, k);
- i = res->end;
-
-vpif_int_err:
- for (q = k; q >= 0; q--) {
- for (m = i; m >= (int)res->start; m--)
- free_irq(m, (void *)(&vpif_obj.dev[q]->channel_id));
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, q-1);
- if (res)
- i = res->end;
- }
- v4l2_device_unregister(&vpif_obj.v4l2_dev);
- return err;
-}
-
-/**
- * vpif_remove() - driver remove handler
- * @device: ptr to platform device structure
- *
- * The vidoe device is unregistered
- */
-static int vpif_remove(struct platform_device *device)
-{
- int i;
- struct channel_obj *ch;
-
- v4l2_device_unregister(&vpif_obj.v4l2_dev);
-
- /* un-register device */
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- /* Unregister video device */
- video_unregister_device(ch->video_dev);
- }
- return 0;
-}
-
-#ifdef CONFIG_PM
-/**
- * vpif_suspend: vpif device suspend
- */
-static int vpif_suspend(struct device *dev)
-{
-
- struct common_obj *common;
- struct channel_obj *ch;
- int i;
-
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
- if (ch->usrs && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
- enable_channel0(0);
- channel0_intr_enable(0);
- }
- if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
- common->started == 2) {
- enable_channel1(0);
- channel1_intr_enable(0);
- }
- }
- mutex_unlock(&common->lock);
- }
-
- return 0;
-}
-
-/*
- * vpif_resume: vpif device suspend
- */
-static int vpif_resume(struct device *dev)
-{
- struct common_obj *common;
- struct channel_obj *ch;
- int i;
-
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
- if (ch->usrs && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL0_VIDEO) {
- enable_channel0(1);
- channel0_intr_enable(1);
- }
- if (ch->channel_id == VPIF_CHANNEL1_VIDEO ||
- common->started == 2) {
- enable_channel1(1);
- channel1_intr_enable(1);
- }
- }
- mutex_unlock(&common->lock);
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops vpif_dev_pm_ops = {
- .suspend = vpif_suspend,
- .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_dev_pm_ops)
-#else
-#define vpif_pm_ops NULL
-#endif
-
-static __refdata struct platform_driver vpif_driver = {
- .driver = {
- .name = "vpif_capture",
- .owner = THIS_MODULE,
- .pm = vpif_pm_ops,
- },
- .probe = vpif_probe,
- .remove = vpif_remove,
-};
-
-/**
- * vpif_init: initialize the vpif driver
- *
- * This function registers device and driver to the kernel, requests irq
- * handler and allocates memory
- * for channel objects
- */
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/**
- * vpif_cleanup : This function clean up the vpif capture resources
- *
- * This will un-registers device and driver to the kernel, frees
- * requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
-
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-/* Function for module initialization and cleanup */
-module_init(vpif_init);
-module_exit(vpif_cleanup);
diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h
deleted file mode 100644
index 3511510f43ee..000000000000
--- a/drivers/media/video/davinci/vpif_capture.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2009 Texas Instruments Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 VPIF_CAPTURE_H
-#define VPIF_CAPTURE_H
-
-#ifdef __KERNEL__
-
-/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/videobuf-core.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
-
-#include "vpif.h"
-
-/* Macros */
-#define VPIF_CAPTURE_VERSION "0.0.2"
-
-#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \
- (V4L2_FIELD_NONE == field)) || \
- (((V4L2_FIELD_INTERLACED == field) || \
- (V4L2_FIELD_SEQ_TB == field)) || \
- (V4L2_FIELD_SEQ_BT == field)))
-
-#define VPIF_CAPTURE_MAX_DEVICES 2
-#define VPIF_VIDEO_INDEX 0
-#define VPIF_NUMBER_OF_OBJECTS 1
-
-/* Enumerated data type to give id to each device per channel */
-enum vpif_channel_id {
- VPIF_CHANNEL0_VIDEO = 0,
- VPIF_CHANNEL1_VIDEO,
-};
-
-struct video_obj {
- enum v4l2_field buf_field;
- /* Currently selected or default standard */
- v4l2_std_id stdid;
- u32 dv_preset;
- struct v4l2_bt_timings bt_timings;
- /* This is to track the last input that is passed to application */
- u32 input_idx;
-};
-
-struct vpif_cap_buffer {
- struct vb2_buffer vb;
- struct list_head list;
-};
-
-struct common_obj {
- /* Pointer pointing to current v4l2_buffer */
- struct vpif_cap_buffer *cur_frm;
- /* Pointer pointing to current v4l2_buffer */
- struct vpif_cap_buffer *next_frm;
- /*
- * This field keeps track of type of buffer exchange mechanism
- * user has selected
- */
- enum v4l2_memory memory;
- /* Used to store pixel format */
- struct v4l2_format fmt;
- /* Buffer queue used in video-buf */
- struct vb2_queue buffer_queue;
- /* allocator-specific contexts for each plane */
- struct vb2_alloc_ctx *alloc_ctx;
- /* Queue of filled frames */
- struct list_head dma_queue;
- /* Used in video-buf */
- spinlock_t irqlock;
- /* lock used to access this structure */
- struct mutex lock;
- /* number of users performing IO */
- u32 io_usrs;
- /* Indicates whether streaming started */
- u8 started;
- /* Function pointer to set the addresses */
- void (*set_addr) (unsigned long, unsigned long, unsigned long,
- unsigned long);
- /* offset where Y top starts from the starting of the buffer */
- u32 ytop_off;
- /* offset where Y bottom starts from the starting of the buffer */
- u32 ybtm_off;
- /* offset where C top starts from the starting of the buffer */
- u32 ctop_off;
- /* offset where C bottom starts from the starting of the buffer */
- u32 cbtm_off;
- /* Indicates width of the image data */
- u32 width;
- /* Indicates height of the image data */
- u32 height;
-};
-
-struct channel_obj {
- /* Identifies video device for this channel */
- struct video_device *video_dev;
- /* Used to keep track of state of the priority */
- struct v4l2_prio_state prio;
- /* number of open instances of the channel */
- int usrs;
- /* Indicates id of the field which is being displayed */
- u32 field_id;
- /* flag to indicate whether decoder is initialized */
- u8 initialized;
- /* Identifies channel */
- enum vpif_channel_id channel_id;
- /* index into sd table */
- int curr_sd_index;
- /* ptr to current sub device information */
- struct vpif_subdev_info *curr_subdev_info;
- /* vpif configuration params */
- struct vpif_params vpifparams;
- /* common object array */
- struct common_obj common[VPIF_NUMBER_OF_OBJECTS];
- /* video object */
- struct video_obj video;
-};
-
-/* File handle structure */
-struct vpif_fh {
- /* pointer to channel object for opened device */
- struct channel_obj *channel;
- /* Indicates whether this file handle is doing IO */
- u8 io_allowed[VPIF_NUMBER_OF_OBJECTS];
- /* Used to keep track priority of this instance */
- enum v4l2_priority prio;
- /* Used to indicate channel is initialize or not */
- u8 initialized;
-};
-
-struct vpif_device {
- struct v4l2_device v4l2_dev;
- struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
- struct v4l2_subdev **sd;
-};
-
-struct vpif_config_params {
- u8 min_numbuffers;
- u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS];
- s8 device_type;
- u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
- u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS];
- u8 default_device[VPIF_CAPTURE_NUM_CHANNELS];
- u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS];
- u8 max_device_type;
-};
-/* Struct which keeps track of the line numbers for the sliced vbi service */
-struct vpif_service_line {
- u16 service_id;
- u16 service_line[2];
-};
-#endif /* End of __KERNEL__ */
-#endif /* VPIF_CAPTURE_H */
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
deleted file mode 100644
index e129c98921ad..000000000000
--- a/drivers/media/video/davinci/vpif_display.c
+++ /dev/null
@@ -1,1998 +0,0 @@
-/*
- * vpif-display - VPIF display driver
- * Display driver for TI DaVinci VPIF
- *
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/workqueue.h>
-#include <linux/string.h>
-#include <linux/videodev2.h>
-#include <linux/wait.h>
-#include <linux/time.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#include <asm/irq.h>
-#include <asm/page.h>
-
-#include <media/adv7343.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-
-#include "vpif_display.h"
-#include "vpif.h"
-
-MODULE_DESCRIPTION("TI DaVinci VPIF Display driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(VPIF_DISPLAY_VERSION);
-
-#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50)
-
-#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg)
-#define vpif_dbg(level, debug, fmt, arg...) \
- v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg)
-
-static int debug = 1;
-static u32 ch2_numbuffers = 3;
-static u32 ch3_numbuffers = 3;
-static u32 ch2_bufsize = 1920 * 1080 * 2;
-static u32 ch3_bufsize = 720 * 576 * 2;
-
-module_param(debug, int, 0644);
-module_param(ch2_numbuffers, uint, S_IRUGO);
-module_param(ch3_numbuffers, uint, S_IRUGO);
-module_param(ch2_bufsize, uint, S_IRUGO);
-module_param(ch3_bufsize, uint, S_IRUGO);
-
-MODULE_PARM_DESC(debug, "Debug level 0-1");
-MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)");
-MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)");
-MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)");
-MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)");
-
-static struct vpif_config_params config_params = {
- .min_numbuffers = 3,
- .numbuffers[0] = 3,
- .numbuffers[1] = 3,
- .min_bufsize[0] = 720 * 480 * 2,
- .min_bufsize[1] = 720 * 480 * 2,
- .channel_bufsize[0] = 1920 * 1080 * 2,
- .channel_bufsize[1] = 720 * 576 * 2,
-};
-
-static struct vpif_device vpif_obj = { {NULL} };
-static struct device *vpif_dev;
-static void vpif_calculate_offsets(struct channel_obj *ch);
-static void vpif_config_addr(struct channel_obj *ch, int muxmode);
-
-/*
- * buffer_prepare: This is the callback function called from vb2_qbuf()
- * function the buffer is prepared and user space virtual address is converted
- * into physical address
- */
-static int vpif_buffer_prepare(struct vb2_buffer *vb)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vb2_queue *q = vb->vb2_queue;
- struct common_obj *common;
- unsigned long addr;
-
- common = &fh->channel->common[VPIF_VIDEO_INDEX];
- if (vb->state != VB2_BUF_STATE_ACTIVE &&
- vb->state != VB2_BUF_STATE_PREPARED) {
- vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0))
- goto buf_align_exit;
-
- addr = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (q->streaming &&
- (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) {
- if (!ISALIGNED(addr + common->ytop_off) ||
- !ISALIGNED(addr + common->ybtm_off) ||
- !ISALIGNED(addr + common->ctop_off) ||
- !ISALIGNED(addr + common->cbtm_off))
- goto buf_align_exit;
- }
- }
- return 0;
-
-buf_align_exit:
- vpif_err("buffer offset not aligned to 8 bytes\n");
- return -EINVAL;
-}
-
-/*
- * vpif_buffer_queue_setup: This function allocates memory for the buffers
- */
-static int vpif_buffer_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- unsigned long size;
-
- if (V4L2_MEMORY_MMAP == common->memory) {
- size = config_params.channel_bufsize[ch->channel_id];
- /*
- * Checking if the buffer size exceeds the available buffer
- * ycmux_mode = 0 means 1 channel mode HD and
- * ycmux_mode = 1 means 2 channels mode SD
- */
- if (ch->vpifparams.std_info.ycmux_mode == 0) {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- (config_params.video_limit[0]
- + config_params.video_limit[1]))
- (*nbuffers)--;
- } else {
- if (config_params.video_limit[ch->channel_id])
- while (size * *nbuffers >
- config_params.video_limit[ch->channel_id])
- (*nbuffers)--;
- }
- } else {
- size = common->fmt.fmt.pix.sizeimage;
- }
-
- if (*nbuffers < config_params.min_numbuffers)
- *nbuffers = config_params.min_numbuffers;
-
- *nplanes = 1;
- sizes[0] = size;
- alloc_ctxs[0] = common->alloc_ctx;
- return 0;
-}
-
-/*
- * vpif_buffer_queue: This function adds the buffer to DMA queue
- */
-static void vpif_buffer_queue(struct vb2_buffer *vb)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* add the buffer to the DMA queue */
- list_add_tail(&buf->list, &common->dma_queue);
-}
-
-/*
- * vpif_buf_cleanup: This function is called from the videobuf2 layer to
- * free memory allocated to the buffers
- */
-static void vpif_buf_cleanup(struct vb2_buffer *vb)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue);
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- unsigned long flags;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- spin_lock_irqsave(&common->irqlock, flags);
- if (vb->state == VB2_BUF_STATE_ACTIVE)
- list_del_init(&buf->list);
- spin_unlock_irqrestore(&common->irqlock, flags);
-}
-
-static void vpif_wait_prepare(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_unlock(&common->lock);
-}
-
-static void vpif_wait_finish(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
-}
-
-static int vpif_buffer_init(struct vb2_buffer *vb)
-{
- struct vpif_disp_buffer *buf = container_of(vb,
- struct vpif_disp_buffer, vb);
-
- INIT_LIST_HEAD(&buf->list);
-
- return 0;
-}
-
-static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} };
-
-static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct vpif_display_config *vpif_config_data =
- vpif_dev->platform_data;
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_params *vpif = &ch->vpifparams;
- unsigned long addr = 0;
- int ret;
-
- /* If buffer queue is empty, return error */
- if (list_empty(&common->dma_queue)) {
- vpif_err("buffer queue is empty\n");
- return -EIO;
- }
-
- /* Get the next frame from the buffer queue */
- common->next_frm = common->cur_frm =
- list_entry(common->dma_queue.next,
- struct vpif_disp_buffer, list);
-
- list_del(&common->cur_frm->list);
- /* Mark state of the current frame to active */
- common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE;
-
- /* Initialize field_id and started member */
- ch->field_id = 0;
- common->started = 1;
- addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0);
- /* Calculate the offset for Y and C data in the buffer */
- vpif_calculate_offsets(ch);
-
- if ((ch->vpifparams.std_info.frm_fmt &&
- ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE)
- && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY)))
- || (!ch->vpifparams.std_info.frm_fmt
- && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) {
- vpif_err("conflict in field format and std format\n");
- return -EINVAL;
- }
-
- /* clock settings */
- ret =
- vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode,
- ch->vpifparams.std_info.hd_sd);
- if (ret < 0) {
- vpif_err("can't set clock\n");
- return ret;
- }
-
- /* set the parameters and addresses */
- ret = vpif_set_video_params(vpif, ch->channel_id + 2);
- if (ret < 0)
- return ret;
-
- common->started = ret;
- vpif_config_addr(ch, ret);
- common->set_addr((addr + common->ytop_off),
- (addr + common->ybtm_off),
- (addr + common->ctop_off),
- (addr + common->cbtm_off));
-
- /* Set interrupt for both the fields in VPIF
- Register enable channel in VPIF register */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- channel2_intr_assert();
- channel2_intr_enable(1);
- enable_channel2(1);
- if (vpif_config_data->ch2_clip_en)
- channel2_clipping_enable(1);
- }
-
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id)
- || (common->started == 2)) {
- channel3_intr_assert();
- channel3_intr_enable(1);
- enable_channel3(1);
- if (vpif_config_data->ch3_clip_en)
- channel3_clipping_enable(1);
- }
- channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1;
-
- return 0;
-}
-
-/* abort streaming and wait for last buffer */
-static int vpif_stop_streaming(struct vb2_queue *vq)
-{
- struct vpif_fh *fh = vb2_get_drv_priv(vq);
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
-
- if (!vb2_is_streaming(vq))
- return 0;
-
- common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* release all active buffers */
- while (!list_empty(&common->dma_queue)) {
- common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_disp_buffer, list);
- list_del(&common->next_frm->list);
- vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR);
- }
-
- return 0;
-}
-
-static struct vb2_ops video_qops = {
- .queue_setup = vpif_buffer_queue_setup,
- .wait_prepare = vpif_wait_prepare,
- .wait_finish = vpif_wait_finish,
- .buf_init = vpif_buffer_init,
- .buf_prepare = vpif_buffer_prepare,
- .start_streaming = vpif_start_streaming,
- .stop_streaming = vpif_stop_streaming,
- .buf_cleanup = vpif_buf_cleanup,
- .buf_queue = vpif_buffer_queue,
-};
-
-static void process_progressive_mode(struct common_obj *common)
-{
- unsigned long addr = 0;
-
- /* Get the next buffer from buffer queue */
- common->next_frm = list_entry(common->dma_queue.next,
- struct vpif_disp_buffer, list);
- /* Remove that buffer from the buffer queue */
- list_del(&common->next_frm->list);
- /* Mark status of the buffer as active */
- common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE;
-
- /* Set top and bottom field addrs in VPIF registers */
- addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0);
- common->set_addr(addr + common->ytop_off,
- addr + common->ybtm_off,
- addr + common->ctop_off,
- addr + common->cbtm_off);
-}
-
-static void process_interlaced_mode(int fid, struct common_obj *common)
-{
- /* device field id and local field id are in sync */
- /* If this is even field */
- if (0 == fid) {
- if (common->cur_frm == common->next_frm)
- return;
-
- /* one frame is displayed If next frame is
- * available, release cur_frm and move on */
- /* Copy frame display time */
- do_gettimeofday(&common->cur_frm->vb.v4l2_buf.timestamp);
- /* Change status of the cur_frm */
- vb2_buffer_done(&common->cur_frm->vb,
- VB2_BUF_STATE_DONE);
- /* Make cur_frm pointing to next_frm */
- common->cur_frm = common->next_frm;
-
- } else if (1 == fid) { /* odd field */
- if (list_empty(&common->dma_queue)
- || (common->cur_frm != common->next_frm)) {
- return;
- }
- /* one field is displayed configure the next
- * frame if it is available else hold on current
- * frame */
- /* Get next from the buffer queue */
- process_progressive_mode(common);
-
- }
-}
-
-/*
- * vpif_channel_isr: It changes status of the displayed buffer, takes next
- * buffer from the queue and sets its address in VPIF registers
- */
-static irqreturn_t vpif_channel_isr(int irq, void *dev_id)
-{
- struct vpif_device *dev = &vpif_obj;
- struct channel_obj *ch;
- struct common_obj *common;
- enum v4l2_field field;
- int fid = -1, i;
- int channel_id = 0;
-
- channel_id = *(int *)(dev_id);
- if (!vpif_intr_status(channel_id + 2))
- return IRQ_NONE;
-
- ch = dev->dev[channel_id];
- field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field;
- for (i = 0; i < VPIF_NUMOBJECTS; i++) {
- common = &ch->common[i];
- /* If streaming is started in this channel */
- if (0 == common->started)
- continue;
-
- if (1 == ch->vpifparams.std_info.frm_fmt) {
- if (list_empty(&common->dma_queue))
- continue;
-
- /* Progressive mode */
- if (!channel_first_int[i][channel_id]) {
- /* Mark status of the cur_frm to
- * done and unlock semaphore on it */
- do_gettimeofday(&common->cur_frm->vb.
- v4l2_buf.timestamp);
- vb2_buffer_done(&common->cur_frm->vb,
- VB2_BUF_STATE_DONE);
- /* Make cur_frm pointing to next_frm */
- common->cur_frm = common->next_frm;
- }
-
- channel_first_int[i][channel_id] = 0;
- process_progressive_mode(common);
- } else {
- /* Interlaced mode */
- /* If it is first interrupt, ignore it */
-
- if (channel_first_int[i][channel_id]) {
- channel_first_int[i][channel_id] = 0;
- continue;
- }
-
- if (0 == i) {
- ch->field_id ^= 1;
- /* Get field id from VPIF registers */
- fid = vpif_channel_getfid(ch->channel_id + 2);
- /* If fid does not match with stored field id */
- if (fid != ch->field_id) {
- /* Make them in sync */
- if (0 == fid)
- ch->field_id = fid;
-
- return IRQ_HANDLED;
- }
- }
- process_interlaced_mode(fid, common);
- }
- }
-
- return IRQ_HANDLED;
-}
-
-static int vpif_update_std_info(struct channel_obj *ch)
-{
- struct video_obj *vid_ch = &ch->video;
- struct vpif_params *vpifparams = &ch->vpifparams;
- struct vpif_channel_config_params *std_info = &vpifparams->std_info;
- const struct vpif_channel_config_params *config;
-
- int i;
-
- for (i = 0; i < vpif_ch_params_count; i++) {
- config = &ch_params[i];
- if (config->hd_sd == 0) {
- vpif_dbg(2, debug, "SD format\n");
- if (config->stdid & vid_ch->stdid) {
- memcpy(std_info, config, sizeof(*config));
- break;
- }
- } else {
- vpif_dbg(2, debug, "HD format\n");
- if (config->dv_preset == vid_ch->dv_preset) {
- memcpy(std_info, config, sizeof(*config));
- break;
- }
- }
- }
-
- if (i == vpif_ch_params_count) {
- vpif_dbg(1, debug, "Format not found\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vpif_update_resolution(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct video_obj *vid_ch = &ch->video;
- struct vpif_params *vpifparams = &ch->vpifparams;
- struct vpif_channel_config_params *std_info = &vpifparams->std_info;
-
- if (!vid_ch->stdid && !vid_ch->dv_preset && !vid_ch->bt_timings.height)
- return -EINVAL;
-
- if (vid_ch->stdid || vid_ch->dv_preset) {
- if (vpif_update_std_info(ch))
- return -EINVAL;
- }
-
- common->fmt.fmt.pix.width = std_info->width;
- common->fmt.fmt.pix.height = std_info->height;
- vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n",
- common->fmt.fmt.pix.width, common->fmt.fmt.pix.height);
-
- /* Set height and width paramateres */
- common->height = std_info->height;
- common->width = std_info->width;
-
- return 0;
-}
-
-/*
- * vpif_calculate_offsets: This function calculates buffers offset for Y and C
- * in the top and bottom field
- */
-static void vpif_calculate_offsets(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_params *vpifparams = &ch->vpifparams;
- enum v4l2_field field = common->fmt.fmt.pix.field;
- struct video_obj *vid_ch = &ch->video;
- unsigned int hpitch, vpitch, sizeimage;
-
- if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) {
- if (ch->vpifparams.std_info.frm_fmt)
- vid_ch->buf_field = V4L2_FIELD_NONE;
- else
- vid_ch->buf_field = V4L2_FIELD_INTERLACED;
- } else {
- vid_ch->buf_field = common->fmt.fmt.pix.field;
- }
-
- sizeimage = common->fmt.fmt.pix.sizeimage;
-
- hpitch = common->fmt.fmt.pix.bytesperline;
- vpitch = sizeimage / (hpitch * 2);
- if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
- (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
- common->ytop_off = 0;
- common->ybtm_off = hpitch;
- common->ctop_off = sizeimage / 2;
- common->cbtm_off = sizeimage / 2 + hpitch;
- } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) {
- common->ytop_off = 0;
- common->ybtm_off = sizeimage / 4;
- common->ctop_off = sizeimage / 2;
- common->cbtm_off = common->ctop_off + sizeimage / 4;
- } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) {
- common->ybtm_off = 0;
- common->ytop_off = sizeimage / 4;
- common->cbtm_off = sizeimage / 2;
- common->ctop_off = common->cbtm_off + sizeimage / 4;
- }
-
- if ((V4L2_FIELD_NONE == vid_ch->buf_field) ||
- (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) {
- vpifparams->video_params.storage_mode = 1;
- } else {
- vpifparams->video_params.storage_mode = 0;
- }
-
- if (ch->vpifparams.std_info.frm_fmt == 1) {
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline;
- } else {
- if ((field == V4L2_FIELD_ANY) ||
- (field == V4L2_FIELD_INTERLACED))
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline * 2;
- else
- vpifparams->video_params.hpitch =
- common->fmt.fmt.pix.bytesperline;
- }
-
- ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid;
-}
-
-static void vpif_config_format(struct channel_obj *ch)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- common->fmt.fmt.pix.field = V4L2_FIELD_ANY;
- if (config_params.numbuffers[ch->channel_id] == 0)
- common->memory = V4L2_MEMORY_USERPTR;
- else
- common->memory = V4L2_MEMORY_MMAP;
-
- common->fmt.fmt.pix.sizeimage =
- config_params.channel_bufsize[ch->channel_id];
- common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P;
- common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-}
-
-static int vpif_check_format(struct channel_obj *ch,
- struct v4l2_pix_format *pixfmt)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- enum v4l2_field field = pixfmt->field;
- u32 sizeimage, hpitch, vpitch;
-
- if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P)
- goto invalid_fmt_exit;
-
- if (!(VPIF_VALID_FIELD(field)))
- goto invalid_fmt_exit;
-
- if (pixfmt->bytesperline <= 0)
- goto invalid_pitch_exit;
-
- sizeimage = pixfmt->sizeimage;
-
- if (vpif_update_resolution(ch))
- return -EINVAL;
-
- hpitch = pixfmt->bytesperline;
- vpitch = sizeimage / (hpitch * 2);
-
- /* Check for valid value of pitch */
- if ((hpitch < ch->vpifparams.std_info.width) ||
- (vpitch < ch->vpifparams.std_info.height))
- goto invalid_pitch_exit;
-
- /* Check for 8 byte alignment */
- if (!ISALIGNED(hpitch)) {
- vpif_err("invalid pitch alignment\n");
- return -EINVAL;
- }
- pixfmt->width = common->fmt.fmt.pix.width;
- pixfmt->height = common->fmt.fmt.pix.height;
-
- return 0;
-
-invalid_fmt_exit:
- vpif_err("invalid field format\n");
- return -EINVAL;
-
-invalid_pitch_exit:
- vpif_err("invalid pitch\n");
- return -EINVAL;
-}
-
-static void vpif_config_addr(struct channel_obj *ch, int muxmode)
-{
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (VPIF_CHANNEL3_VIDEO == ch->channel_id) {
- common->set_addr = ch3_set_videobuf_addr;
- } else {
- if (2 == muxmode)
- common->set_addr = ch2_set_videobuf_addr_yc_nmux;
- else
- common->set_addr = ch2_set_videobuf_addr;
- }
-}
-
-/*
- * vpif_mmap: It is used to map kernel space buffers into user spaces
- */
-static int vpif_mmap(struct file *filep, struct vm_area_struct *vma)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]);
-
- vpif_dbg(2, debug, "vpif_mmap\n");
-
- return vb2_mmap(&common->buffer_queue, vma);
-}
-
-/*
- * vpif_poll: It is used for select/poll system call
- */
-static unsigned int vpif_poll(struct file *filep, poll_table *wait)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (common->started)
- return vb2_poll(&common->buffer_queue, filep, wait);
-
- return 0;
-}
-
-/*
- * vpif_open: It creates object of file handle structure and stores it in
- * private_data member of filepointer
- */
-static int vpif_open(struct file *filep)
-{
- struct video_device *vdev = video_devdata(filep);
- struct channel_obj *ch = NULL;
- struct vpif_fh *fh = NULL;
-
- ch = video_get_drvdata(vdev);
- /* Allocate memory for the file handle object */
- fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL);
- if (fh == NULL) {
- vpif_err("unable to allocate memory for file handle object\n");
- return -ENOMEM;
- }
-
- /* store pointer to fh in private_data member of filep */
- filep->private_data = fh;
- fh->channel = ch;
- fh->initialized = 0;
- if (!ch->initialized) {
- fh->initialized = 1;
- ch->initialized = 1;
- memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
- }
-
- /* Increment channel usrs counter */
- atomic_inc(&ch->usrs);
- /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */
- fh->io_allowed[VPIF_VIDEO_INDEX] = 0;
- /* Initialize priority of this instance to default priority */
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&ch->prio, &fh->prio);
-
- return 0;
-}
-
-/*
- * vpif_release: This function deletes buffer queue, frees the buffers and
- * the vpif file handle
- */
-static int vpif_release(struct file *filep)
-{
- struct vpif_fh *fh = filep->private_data;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* if this instance is doing IO */
- if (fh->io_allowed[VPIF_VIDEO_INDEX]) {
- /* Reset io_usrs member of channel object */
- common->io_usrs = 0;
- /* Disable channel */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- common->started = 0;
-
- /* Free buffers allocated */
- vb2_queue_release(&common->buffer_queue);
- vb2_dma_contig_cleanup_ctx(common->alloc_ctx);
-
- common->numbuffers =
- config_params.numbuffers[ch->channel_id];
- }
-
- /* Decrement channel usrs counter */
- atomic_dec(&ch->usrs);
- /* If this file handle has initialize encoder device, reset it */
- if (fh->initialized)
- ch->initialized = 0;
-
- /* Close the priority */
- v4l2_prio_close(&ch->prio, fh->prio);
- filep->private_data = NULL;
- fh->initialized = 0;
- kfree(fh);
-
- return 0;
-}
-
-/* functions implementing ioctls */
-/**
- * vpif_querycap() - QUERYCAP handler
- * @file: file ptr
- * @priv: file handle
- * @cap: ptr to v4l2_capability structure
- */
-static int vpif_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vpif_display_config *config = vpif_dev->platform_data;
-
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- strlcpy(cap->driver, "vpif display", sizeof(cap->driver));
- strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info));
- strlcpy(cap->card, config->card_name, sizeof(cap->card));
-
- return 0;
-}
-
-static int vpif_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- if (fmt->index != 0) {
- vpif_err("Invalid format index\n");
- return -EINVAL;
- }
-
- /* Fill in the information about format */
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- strcpy(fmt->description, "YCbCr4:2:2 YC Planar");
- fmt->pixelformat = V4L2_PIX_FMT_YUV422P;
-
- return 0;
-}
-
-static int vpif_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- /* Check the validity of the buffer type */
- if (common->fmt.type != fmt->type)
- return -EINVAL;
-
- if (vpif_update_resolution(ch))
- return -EINVAL;
- *fmt = common->fmt;
- return 0;
-}
-
-static int vpif_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct v4l2_pix_format *pixfmt;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_dbg(1, debug, "Channel Busy\n");
- return -EBUSY;
- }
-
- /* Check for the priority */
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (0 != ret)
- return ret;
- fh->initialized = 1;
- }
-
- if (common->started) {
- vpif_dbg(1, debug, "Streaming in progress\n");
- return -EBUSY;
- }
-
- pixfmt = &fmt->fmt.pix;
- /* Check for valid field format */
- ret = vpif_check_format(ch, pixfmt);
- if (ret)
- return ret;
-
- /* store the pix format in the channel object */
- common->fmt.fmt.pix = *pixfmt;
- /* store the format in the channel object */
- common->fmt = *fmt;
- return 0;
-}
-
-static int vpif_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
- int ret = 0;
-
- ret = vpif_check_format(ch, pixfmt);
- if (ret) {
- *pixfmt = common->fmt.fmt.pix;
- pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2;
- }
-
- return ret;
-}
-
-static int vpif_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common;
- enum v4l2_field field;
- struct vb2_queue *q;
- u8 index = 0;
-
- /* This file handle has not initialized the channel,
- It is not allowed to do settings */
- if ((VPIF_CHANNEL2_VIDEO == ch->channel_id)
- || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) {
- if (!fh->initialized) {
- vpif_err("Channel Busy\n");
- return -EBUSY;
- }
- }
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type)
- return -EINVAL;
-
- index = VPIF_VIDEO_INDEX;
-
- common = &ch->common[index];
-
- if (common->fmt.type != reqbuf->type || !vpif_dev)
- return -EINVAL;
- if (0 != common->io_usrs)
- return -EBUSY;
-
- if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_INTERLACED;
- else
- field = common->fmt.fmt.pix.field;
- } else {
- field = V4L2_VBI_INTERLACED;
- }
- /* Initialize videobuf2 queue as per the buffer type */
- common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev);
- if (!common->alloc_ctx) {
- vpif_err("Failed to get the context\n");
- return -EINVAL;
- }
- q = &common->buffer_queue;
- q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = fh;
- q->ops = &video_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct vpif_disp_buffer);
-
- vb2_queue_init(q);
-
- /* Set io allowed member of file handle to TRUE */
- fh->io_allowed[index] = 1;
- /* Increment io usrs member of channel object to 1 */
- common->io_usrs = 1;
- /* Store type of memory requested in channel object */
- common->memory = reqbuf->memory;
- INIT_LIST_HEAD(&common->dma_queue);
- /* Allocate buffers */
- return vb2_reqbufs(&common->buffer_queue, reqbuf);
-}
-
-static int vpif_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *tbuf)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- if (common->fmt.type != tbuf->type)
- return -EINVAL;
-
- return vb2_querybuf(&common->buffer_queue, tbuf);
-}
-
-static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct vpif_fh *fh = NULL;
- struct channel_obj *ch = NULL;
- struct common_obj *common = NULL;
-
- if (!buf || !priv)
- return -EINVAL;
-
- fh = priv;
- ch = fh->channel;
- if (!ch)
- return -EINVAL;
-
- common = &(ch->common[VPIF_VIDEO_INDEX]);
- if (common->fmt.type != buf->type)
- return -EINVAL;
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
-
- return vb2_qbuf(&common->buffer_queue, buf);
-}
-
-static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- if (!(*std_id & VPIF_V4L2_STD))
- return -EINVAL;
-
- if (common->started) {
- vpif_err("streaming in progress\n");
- return -EBUSY;
- }
-
- /* Call encoder subdevice function to set the standard */
- ch->video.stdid = *std_id;
- ch->video.dv_preset = V4L2_DV_INVALID;
- memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
-
- /* Get the information about the standard */
- if (vpif_update_resolution(ch))
- return -EINVAL;
-
- if ((ch->vpifparams.std_info.width *
- ch->vpifparams.std_info.height * 2) >
- config_params.channel_bufsize[ch->channel_id]) {
- vpif_err("invalid std for this size\n");
- return -EINVAL;
- }
-
- common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width;
- /* Configure the default format information */
- vpif_config_format(ch);
-
- ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
- s_std_output, *std_id);
- if (ret < 0) {
- vpif_err("Failed to set output standard\n");
- return ret;
- }
-
- ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core,
- s_std, *std_id);
- if (ret < 0)
- vpif_err("Failed to set standard for sub devices\n");
- return ret;
-}
-
-static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *std = ch->video.stdid;
- return 0;
-}
-
-static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
-
- return vb2_dqbuf(&common->buffer_queue, p,
- (file->f_flags & O_NONBLOCK));
-}
-
-static int vpif_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id];
- int ret = 0;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- vpif_err("buffer type not supported\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
-
- /* If Streaming is already started, return error */
- if (common->started) {
- vpif_err("channel->started\n");
- return -EBUSY;
- }
-
- if ((ch->channel_id == VPIF_CHANNEL2_VIDEO
- && oth_ch->common[VPIF_VIDEO_INDEX].started &&
- ch->vpifparams.std_info.ycmux_mode == 0)
- || ((ch->channel_id == VPIF_CHANNEL3_VIDEO)
- && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) {
- vpif_err("other channel is using\n");
- return -EBUSY;
- }
-
- ret = vpif_check_format(ch, &common->fmt.fmt.pix);
- if (ret < 0)
- return ret;
-
- /* Call vb2_streamon to start streaming in videobuf2 */
- ret = vb2_streamon(&common->buffer_queue, buftype);
- if (ret < 0) {
- vpif_err("vb2_streamon\n");
- return ret;
- }
-
- return ret;
-}
-
-static int vpif_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct vpif_display_config *vpif_config_data =
- vpif_dev->platform_data;
-
- if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- vpif_err("buffer type not supported\n");
- return -EINVAL;
- }
-
- if (!fh->io_allowed[VPIF_VIDEO_INDEX]) {
- vpif_err("fh->io_allowed\n");
- return -EACCES;
- }
-
- if (!common->started) {
- vpif_err("channel->started\n");
- return -EINVAL;
- }
-
- if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- /* disable channel */
- if (VPIF_CHANNEL2_VIDEO == ch->channel_id) {
- if (vpif_config_data->ch2_clip_en)
- channel2_clipping_enable(0);
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ||
- (2 == common->started)) {
- if (vpif_config_data->ch3_clip_en)
- channel3_clipping_enable(0);
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- }
-
- common->started = 0;
- return vb2_streamoff(&common->buffer_queue, buftype);
-}
-
-static int vpif_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *crop)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type)
- return -EINVAL;
-
- crop->bounds.left = crop->bounds.top = 0;
- crop->defrect.left = crop->defrect.top = 0;
- crop->defrect.height = crop->bounds.height = common->height;
- crop->defrect.width = crop->bounds.width = common->width;
-
- return 0;
-}
-
-static int vpif_enum_output(struct file *file, void *fh,
- struct v4l2_output *output)
-{
-
- struct vpif_display_config *config = vpif_dev->platform_data;
-
- if (output->index >= config->output_count) {
- vpif_dbg(1, debug, "Invalid output index\n");
- return -EINVAL;
- }
-
- strcpy(output->name, config->output[output->index]);
- output->type = V4L2_OUTPUT_TYPE_ANALOG;
- output->std = VPIF_V4L2_STD;
-
- return 0;
-}
-
-static int vpif_s_output(struct file *file, void *priv, unsigned int i)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- int ret = 0;
-
- if (common->started) {
- vpif_err("Streaming in progress\n");
- return -EBUSY;
- }
-
- ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video,
- s_routing, 0, i, 0);
-
- if (ret < 0)
- vpif_err("Failed to set output standard\n");
-
- vid_ch->output_id = i;
- return ret;
-}
-
-static int vpif_g_output(struct file *file, void *priv, unsigned int *i)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
-
- *i = vid_ch->output_id;
-
- return 0;
-}
-
-static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- *p = v4l2_prio_max(&ch->prio);
-
- return 0;
-}
-
-static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- return v4l2_prio_change(&ch->prio, &fh->prio, p);
-}
-
-/**
- * vpif_enum_dv_presets() - ENUM_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_enum_dv_presets(struct file *file, void *priv,
- struct v4l2_dv_enum_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
-
- return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
- video, enum_dv_presets, preset);
-}
-
-/**
- * vpif_s_dv_presets() - S_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_s_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX];
- struct video_obj *vid_ch = &ch->video;
- int ret = 0;
-
- if (common->started) {
- vpif_dbg(1, debug, "streaming in progress\n");
- return -EBUSY;
- }
-
- ret = v4l2_prio_check(&ch->prio, fh->prio);
- if (ret != 0)
- return ret;
-
- fh->initialized = 1;
-
- /* Call encoder subdevice function to set the standard */
- if (mutex_lock_interruptible(&common->lock))
- return -ERESTARTSYS;
-
- ch->video.dv_preset = preset->preset;
- ch->video.stdid = V4L2_STD_UNKNOWN;
- memset(&ch->video.bt_timings, 0, sizeof(ch->video.bt_timings));
-
- /* Get the information about the standard */
- if (vpif_update_resolution(ch)) {
- ret = -EINVAL;
- } else {
- /* Configure the default format information */
- vpif_config_format(ch);
-
- ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
- video, s_dv_preset, preset);
- }
-
- mutex_unlock(&common->lock);
-
- return ret;
-}
-/**
- * vpif_g_dv_presets() - G_DV_PRESETS handler
- * @file: file ptr
- * @priv: file handle
- * @preset: input preset
- */
-static int vpif_g_dv_preset(struct file *file, void *priv,
- struct v4l2_dv_preset *preset)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
-
- preset->preset = ch->video.dv_preset;
-
- return 0;
-}
-/**
- * vpif_s_dv_timings() - S_DV_TIMINGS handler
- * @file: file ptr
- * @priv: file handle
- * @timings: digital video timings
- */
-static int vpif_s_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct vpif_params *vpifparams = &ch->vpifparams;
- struct vpif_channel_config_params *std_info = &vpifparams->std_info;
- struct video_obj *vid_ch = &ch->video;
- struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
- int ret;
-
- if (timings->type != V4L2_DV_BT_656_1120) {
- vpif_dbg(2, debug, "Timing type not defined\n");
- return -EINVAL;
- }
-
- /* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id],
- video, s_dv_timings, timings);
- if (ret == -ENOIOCTLCMD) {
- vpif_dbg(2, debug, "Custom DV timings not supported by "
- "subdevice\n");
- return -EINVAL;
- }
- if (ret < 0) {
- vpif_dbg(2, debug, "Error setting custom DV timings\n");
- return ret;
- }
-
- if (!(timings->bt.width && timings->bt.height &&
- (timings->bt.hbackporch ||
- timings->bt.hfrontporch ||
- timings->bt.hsync) &&
- timings->bt.vfrontporch &&
- (timings->bt.vbackporch ||
- timings->bt.vsync))) {
- vpif_dbg(2, debug, "Timings for width, height, "
- "horizontal back porch, horizontal sync, "
- "horizontal front porch, vertical back porch, "
- "vertical sync and vertical back porch "
- "must be defined\n");
- return -EINVAL;
- }
-
- *bt = timings->bt;
-
- /* Configure video port timings */
-
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
- std_info->sav2eav = bt->width;
-
- std_info->l1 = 1;
- std_info->l3 = bt->vsync + bt->vbackporch + 1;
-
- if (bt->interlaced) {
- if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
- std_info->l5 = std_info->vsize/2 -
- (bt->vfrontporch - 1);
- std_info->l7 = std_info->vsize/2 + 1;
- std_info->l9 = std_info->l7 + bt->il_vsync +
- bt->il_vbackporch + 1;
- std_info->l11 = std_info->vsize -
- (bt->il_vfrontporch - 1);
- } else {
- vpif_dbg(2, debug, "Required timing values for "
- "interlaced BT format missing\n");
- return -EINVAL;
- }
- } else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
- std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
- }
- strncpy(std_info->name, "Custom timings BT656/1120",
- VPIF_MAX_NAME);
- std_info->width = bt->width;
- std_info->height = bt->height;
- std_info->frm_fmt = bt->interlaced ? 0 : 1;
- std_info->ycmux_mode = 0;
- std_info->capture_format = 0;
- std_info->vbi_supported = 0;
- std_info->hd_sd = 1;
- std_info->stdid = 0;
- std_info->dv_preset = V4L2_DV_INVALID;
-
- vid_ch->stdid = 0;
- vid_ch->dv_preset = V4L2_DV_INVALID;
-
- return 0;
-}
-
-/**
- * vpif_g_dv_timings() - G_DV_TIMINGS handler
- * @file: file ptr
- * @priv: file handle
- * @timings: digital video timings
- */
-static int vpif_g_dv_timings(struct file *file, void *priv,
- struct v4l2_dv_timings *timings)
-{
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
- struct v4l2_bt_timings *bt = &vid_ch->bt_timings;
-
- timings->bt = *bt;
-
- return 0;
-}
-
-/*
- * vpif_g_chip_ident() - Identify the chip
- * @file: file ptr
- * @priv: file handle
- * @chip: chip identity
- *
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) {
- vpif_dbg(2, debug, "match_type is invalid.\n");
- return -EINVAL;
- }
-
- return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core,
- g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-/*
- * vpif_dbg_g_register() - Read register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be read
- *
- * Debugging only
- * Returns zero or -EINVAL if read operations fails.
- */
-static int vpif_dbg_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
-
- return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
- g_register, reg);
-}
-
-/*
- * vpif_dbg_s_register() - Write to register
- * @file: file ptr
- * @priv: file handle
- * @reg: register to be modified
- *
- * Debugging only
- * Returns zero or -EINVAL if write operations fails.
- */
-static int vpif_dbg_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg){
- struct vpif_fh *fh = priv;
- struct channel_obj *ch = fh->channel;
- struct video_obj *vid_ch = &ch->video;
-
- return v4l2_subdev_call(vpif_obj.sd[vid_ch->output_id], core,
- s_register, reg);
-}
-#endif
-
-/*
- * vpif_log_status() - Status information
- * @file: file ptr
- * @priv: file handle
- *
- * Returns zero.
- */
-static int vpif_log_status(struct file *filep, void *priv)
-{
- /* status for sub devices */
- v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status);
-
- return 0;
-}
-
-/* vpif display ioctl operations */
-static const struct v4l2_ioctl_ops vpif_ioctl_ops = {
- .vidioc_querycap = vpif_querycap,
- .vidioc_g_priority = vpif_g_priority,
- .vidioc_s_priority = vpif_s_priority,
- .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out,
- .vidioc_reqbufs = vpif_reqbufs,
- .vidioc_querybuf = vpif_querybuf,
- .vidioc_qbuf = vpif_qbuf,
- .vidioc_dqbuf = vpif_dqbuf,
- .vidioc_streamon = vpif_streamon,
- .vidioc_streamoff = vpif_streamoff,
- .vidioc_s_std = vpif_s_std,
- .vidioc_g_std = vpif_g_std,
- .vidioc_enum_output = vpif_enum_output,
- .vidioc_s_output = vpif_s_output,
- .vidioc_g_output = vpif_g_output,
- .vidioc_cropcap = vpif_cropcap,
- .vidioc_enum_dv_presets = vpif_enum_dv_presets,
- .vidioc_s_dv_preset = vpif_s_dv_preset,
- .vidioc_g_dv_preset = vpif_g_dv_preset,
- .vidioc_s_dv_timings = vpif_s_dv_timings,
- .vidioc_g_dv_timings = vpif_g_dv_timings,
- .vidioc_g_chip_ident = vpif_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vpif_dbg_g_register,
- .vidioc_s_register = vpif_dbg_s_register,
-#endif
- .vidioc_log_status = vpif_log_status,
-};
-
-static const struct v4l2_file_operations vpif_fops = {
- .owner = THIS_MODULE,
- .open = vpif_open,
- .release = vpif_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = vpif_mmap,
- .poll = vpif_poll
-};
-
-static struct video_device vpif_video_template = {
- .name = "vpif",
- .fops = &vpif_fops,
- .ioctl_ops = &vpif_ioctl_ops,
- .tvnorms = VPIF_V4L2_STD,
- .current_norm = V4L2_STD_625_50,
-
-};
-
-/*Configure the channels, buffer sizei, request irq */
-static int initialize_vpif(void)
-{
- int free_channel_objects_index;
- int free_buffer_channel_index;
- int free_buffer_index;
- int err = 0, i, j;
-
- /* Default number of buffers should be 3 */
- if ((ch2_numbuffers > 0) &&
- (ch2_numbuffers < config_params.min_numbuffers))
- ch2_numbuffers = config_params.min_numbuffers;
- if ((ch3_numbuffers > 0) &&
- (ch3_numbuffers < config_params.min_numbuffers))
- ch3_numbuffers = config_params.min_numbuffers;
-
- /* Set buffer size to min buffers size if invalid buffer size is
- * given */
- if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO])
- ch2_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL2_VIDEO];
- if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO])
- ch3_bufsize =
- config_params.min_bufsize[VPIF_CHANNEL3_VIDEO];
-
- config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers;
-
- if (ch2_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] =
- ch2_bufsize;
- }
- config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers;
-
- if (ch3_numbuffers) {
- config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] =
- ch3_bufsize;
- }
-
- /* Allocate memory for six channel objects */
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
- vpif_obj.dev[i] =
- kzalloc(sizeof(struct channel_obj), GFP_KERNEL);
- /* If memory allocation fails, return error */
- if (!vpif_obj.dev[i]) {
- free_channel_objects_index = i;
- err = -ENOMEM;
- goto vpif_init_free_channel_objects;
- }
- }
-
- free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES;
- free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS;
- free_buffer_index = config_params.numbuffers[i - 1];
-
- return 0;
-
-vpif_init_free_channel_objects:
- for (j = 0; j < free_channel_objects_index; j++)
- kfree(vpif_obj.dev[j]);
- return err;
-}
-
-/*
- * vpif_probe: This function creates device entries by register itself to the
- * V4L2 driver and initializes fields of each channel objects
- */
-static __init int vpif_probe(struct platform_device *pdev)
-{
- struct vpif_subdev_info *subdevdata;
- struct vpif_display_config *config;
- int i, j = 0, k, q, m, err = 0;
- struct i2c_adapter *i2c_adap;
- struct common_obj *common;
- struct channel_obj *ch;
- struct video_device *vfd;
- struct resource *res;
- int subdev_count;
- size_t size;
-
- vpif_dev = &pdev->dev;
- err = initialize_vpif();
-
- if (err) {
- v4l2_err(vpif_dev->driver, "Error initializing vpif\n");
- return err;
- }
-
- err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev);
- if (err) {
- v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n");
- return err;
- }
-
- k = 0;
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) {
- for (i = res->start; i <= res->end; i++) {
- if (request_irq(i, vpif_channel_isr, IRQF_SHARED,
- "VPIF_Display",
- (void *)(&vpif_obj.dev[k]->channel_id))) {
- err = -EBUSY;
- goto vpif_int_err;
- }
- }
- k++;
- }
-
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
-
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
-
- /* Allocate memory for video device */
- vfd = video_device_alloc();
- if (vfd == NULL) {
- for (j = 0; j < i; j++) {
- ch = vpif_obj.dev[j];
- video_device_release(ch->video_dev);
- }
- err = -ENOMEM;
- goto vpif_int_err;
- }
-
- /* Initialize field of video device */
- *vfd = vpif_video_template;
- vfd->v4l2_dev = &vpif_obj.v4l2_dev;
- vfd->release = video_device_release;
- snprintf(vfd->name, sizeof(vfd->name),
- "VPIF_Display_DRIVER_V%s",
- VPIF_DISPLAY_VERSION);
-
- /* Set video_dev to the video device */
- ch->video_dev = vfd;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- size = resource_size(res);
- /* The resources are divided into two equal memory and when
- * we have HD output we can add them together
- */
- for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
-
- /* only enabled if second resource exists */
- config_params.video_limit[ch->channel_id] = 0;
- if (size)
- config_params.video_limit[ch->channel_id] =
- size/2;
- }
- }
-
- for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- /* Initialize field of the channel objects */
- atomic_set(&ch->usrs, 0);
- for (k = 0; k < VPIF_NUMOBJECTS; k++) {
- ch->common[k].numbuffers = 0;
- common = &ch->common[k];
- common->io_usrs = 0;
- common->started = 0;
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- common->numbuffers = 0;
- common->set_addr = NULL;
- common->ytop_off = common->ybtm_off = 0;
- common->ctop_off = common->cbtm_off = 0;
- common->cur_frm = common->next_frm = NULL;
- memset(&common->fmt, 0, sizeof(common->fmt));
- common->numbuffers = config_params.numbuffers[k];
-
- }
- ch->initialized = 0;
- ch->channel_id = j;
- if (j < 2)
- ch->common[VPIF_VIDEO_INDEX].numbuffers =
- config_params.numbuffers[ch->channel_id];
- else
- ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
-
- memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
-
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- ch->common[VPIF_VIDEO_INDEX].fmt.type =
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags);
- ch->video_dev->lock = &common->lock;
-
- /* register video device */
- vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
- (int)ch, (int)&ch->video_dev);
-
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 3 : 2));
- if (err < 0)
- goto probe_out;
-
- video_set_drvdata(ch->video_dev, ch);
- }
-
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
- subdev_count = config->subdev_count;
- subdevdata = config->subdevinfo;
- vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
- GFP_KERNEL);
- if (vpif_obj.sd == NULL) {
- vpif_err("unable to allocate memory for subdevice pointers\n");
- err = -ENOMEM;
- goto probe_out;
- }
-
- for (i = 0; i < subdev_count; i++) {
- vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata[i].board_info,
- NULL);
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- goto probe_subdev_out;
- }
-
- if (vpif_obj.sd[i])
- vpif_obj.sd[i]->grp_id = 1 << i;
- }
-
- v4l2_info(&vpif_obj.v4l2_dev,
- " VPIF display driver initialized\n");
- return 0;
-
-probe_subdev_out:
- kfree(vpif_obj.sd);
-probe_out:
- for (k = 0; k < j; k++) {
- ch = vpif_obj.dev[k];
- video_unregister_device(ch->video_dev);
- video_device_release(ch->video_dev);
- ch->video_dev = NULL;
- }
-vpif_int_err:
- v4l2_device_unregister(&vpif_obj.v4l2_dev);
- vpif_err("VPIF IRQ request failed\n");
- for (q = k; k >= 0; k--) {
- for (m = i; m >= res->start; m--)
- free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id));
- res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1);
- m = res->end;
- }
-
- return err;
-}
-
-/*
- * vpif_remove: It un-register channels from V4L2 driver
- */
-static int vpif_remove(struct platform_device *device)
-{
- struct channel_obj *ch;
- int i;
-
- v4l2_device_unregister(&vpif_obj.v4l2_dev);
-
- /* un-register device */
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- /* Unregister video device */
- video_unregister_device(ch->video_dev);
-
- ch->video_dev = NULL;
- }
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int vpif_suspend(struct device *dev)
-{
- struct common_obj *common;
- struct channel_obj *ch;
- int i;
-
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
- if (atomic_read(&ch->usrs) && common->io_usrs) {
- /* Disable channel */
- if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
- enable_channel2(0);
- channel2_intr_enable(0);
- }
- if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
- common->started == 2) {
- enable_channel3(0);
- channel3_intr_enable(0);
- }
- }
- mutex_unlock(&common->lock);
- }
-
- return 0;
-}
-
-static int vpif_resume(struct device *dev)
-{
-
- struct common_obj *common;
- struct channel_obj *ch;
- int i;
-
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[i];
- common = &ch->common[VPIF_VIDEO_INDEX];
- mutex_lock(&common->lock);
- if (atomic_read(&ch->usrs) && common->io_usrs) {
- /* Enable channel */
- if (ch->channel_id == VPIF_CHANNEL2_VIDEO) {
- enable_channel2(1);
- channel2_intr_enable(1);
- }
- if (ch->channel_id == VPIF_CHANNEL3_VIDEO ||
- common->started == 2) {
- enable_channel3(1);
- channel3_intr_enable(1);
- }
- }
- mutex_unlock(&common->lock);
- }
-
- return 0;
-}
-
-static const struct dev_pm_ops vpif_pm = {
- .suspend = vpif_suspend,
- .resume = vpif_resume,
-};
-
-#define vpif_pm_ops (&vpif_pm)
-#else
-#define vpif_pm_ops NULL
-#endif
-
-static __refdata struct platform_driver vpif_driver = {
- .driver = {
- .name = "vpif_display",
- .owner = THIS_MODULE,
- .pm = vpif_pm_ops,
- },
- .probe = vpif_probe,
- .remove = vpif_remove,
-};
-
-static __init int vpif_init(void)
-{
- return platform_driver_register(&vpif_driver);
-}
-
-/*
- * vpif_cleanup: This function un-registers device and driver to the kernel,
- * frees requested irq handler and de-allocates memory allocated for channel
- * objects.
- */
-static void vpif_cleanup(void)
-{
- struct platform_device *pdev;
- struct resource *res;
- int irq_num;
- int i = 0;
-
- pdev = container_of(vpif_dev, struct platform_device, dev);
-
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) {
- for (irq_num = res->start; irq_num <= res->end; irq_num++)
- free_irq(irq_num,
- (void *)(&vpif_obj.dev[i]->channel_id));
- i++;
- }
-
- platform_driver_unregister(&vpif_driver);
- kfree(vpif_obj.sd);
- for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++)
- kfree(vpif_obj.dev[i]);
-}
-
-module_init(vpif_init);
-module_exit(vpif_cleanup);
diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h
deleted file mode 100644
index 8967ffb44058..000000000000
--- a/drivers/media/video/davinci/vpif_display.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * VPIF display header file
- *
- * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef DAVINCIHD_DISPLAY_H
-#define DAVINCIHD_DISPLAY_H
-
-/* Header files */
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/videobuf-core.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/davinci/vpif_types.h>
-
-#include "vpif.h"
-
-/* Macros */
-#define VPIF_DISPLAY_VERSION "0.0.2"
-
-#define VPIF_VALID_FIELD(field) \
- (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \
- (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \
- (V4L2_FIELD_SEQ_BT == field)))
-
-#define VPIF_DISPLAY_MAX_DEVICES (2)
-#define VPIF_SLICED_BUF_SIZE (256)
-#define VPIF_SLICED_MAX_SERVICES (3)
-#define VPIF_VIDEO_INDEX (0)
-#define VPIF_VBI_INDEX (1)
-#define VPIF_HBI_INDEX (2)
-
-/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/
-#define VPIF_NUMOBJECTS (1)
-
-/* Macros */
-#define ISALIGNED(a) (0 == ((a) & 7))
-
-/* enumerated data types */
-/* Enumerated data type to give id to each device per channel */
-enum vpif_channel_id {
- VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */
- VPIF_CHANNEL3_VIDEO, /* Channel3 Video */
-};
-
-/* structures */
-
-struct video_obj {
- enum v4l2_field buf_field;
- u32 latest_only; /* indicate whether to return
- * most recent displayed frame only */
- v4l2_std_id stdid; /* Currently selected or default
- * standard */
- u32 dv_preset;
- struct v4l2_bt_timings bt_timings;
- u32 output_id; /* Current output id */
-};
-
-struct vbi_obj {
- int num_services;
- struct vpif_vbi_params vbiparams; /* vpif parameters for the raw
- * vbi data */
-};
-
-struct vpif_disp_buffer {
- struct vb2_buffer vb;
- struct list_head list;
-};
-
-struct common_obj {
- /* Buffer specific parameters */
- u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for
- * storing frames */
- u32 numbuffers; /* number of buffers */
- struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current
- * vb2_buffer */
- struct vpif_disp_buffer *next_frm; /* Pointer pointing to next
- * vb2_buffer */
- enum v4l2_memory memory; /* This field keeps track of
- * type of buffer exchange
- * method user has selected */
- struct v4l2_format fmt; /* Used to store the format */
- struct vb2_queue buffer_queue; /* Buffer queue used in
- * video-buf */
- /* allocator-specific contexts for each plane */
- struct vb2_alloc_ctx *alloc_ctx;
-
- struct list_head dma_queue; /* Queue of filled frames */
- spinlock_t irqlock; /* Used in video-buf */
-
- /* channel specific parameters */
- struct mutex lock; /* lock used to access this
- * structure */
- u32 io_usrs; /* number of users performing
- * IO */
- u8 started; /* Indicates whether streaming
- * started */
- u32 ytop_off; /* offset of Y top from the
- * starting of the buffer */
- u32 ybtm_off; /* offset of Y bottom from the
- * starting of the buffer */
- u32 ctop_off; /* offset of C top from the
- * starting of the buffer */
- u32 cbtm_off; /* offset of C bottom from the
- * starting of the buffer */
- /* Function pointer to set the addresses */
- void (*set_addr) (unsigned long, unsigned long,
- unsigned long, unsigned long);
- u32 height;
- u32 width;
-};
-
-struct channel_obj {
- /* V4l2 specific parameters */
- struct video_device *video_dev; /* Identifies video device for
- * this channel */
- struct v4l2_prio_state prio; /* Used to keep track of state of
- * the priority */
- atomic_t usrs; /* number of open instances of
- * the channel */
- u32 field_id; /* Indicates id of the field
- * which is being displayed */
- u8 initialized; /* flag to indicate whether
- * encoder is initialized */
-
- enum vpif_channel_id channel_id;/* Identifies channel */
- struct vpif_params vpifparams;
- struct common_obj common[VPIF_NUMOBJECTS];
- struct video_obj video;
- struct vbi_obj vbi;
-};
-
-/* File handle structure */
-struct vpif_fh {
- struct channel_obj *channel; /* pointer to channel object for
- * opened device */
- u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle
- * is doing IO */
- enum v4l2_priority prio; /* Used to keep track priority of
- * this instance */
- u8 initialized; /* Used to keep track of whether this
- * file handle has initialized
- * channel or not */
-};
-
-/* vpif device structure */
-struct vpif_device {
- struct v4l2_device v4l2_dev;
- struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
- struct v4l2_subdev **sd;
-
-};
-
-struct vpif_config_params {
- u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
- u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS];
- u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS];
- u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS];
- u8 min_numbuffers;
-};
-
-/* Struct which keeps track of the line numbers for the sliced vbi service */
-struct vpif_service_line {
- u16 service_id;
- u16 service_line[2];
- u16 enc_service_id;
- u8 bytestowrite;
-};
-
-#endif /* DAVINCIHD_DISPLAY_H */
diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c
deleted file mode 100644
index 3e5cf27ec2b2..000000000000
--- a/drivers/media/video/davinci/vpss.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright (C) 2009 Texas Instruments.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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 vpss system module platform driver for all video drivers.
- */
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/spinlock.h>
-#include <linux/compiler.h>
-#include <linux/io.h>
-#include <mach/hardware.h>
-#include <media/davinci/vpss.h>
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("VPSS Driver");
-MODULE_AUTHOR("Texas Instruments");
-
-/* DM644x defines */
-#define DM644X_SBL_PCR_VPSS (4)
-
-#define DM355_VPSSBL_INTSEL 0x10
-#define DM355_VPSSBL_EVTSEL 0x14
-/* vpss BL register offsets */
-#define DM355_VPSSBL_CCDCMUX 0x1c
-/* vpss CLK register offsets */
-#define DM355_VPSSCLK_CLKCTRL 0x04
-/* masks and shifts */
-#define VPSS_HSSISEL_SHIFT 4
-/*
- * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4,
- * IPIPE_INT1_SDR - vpss_int5
- */
-#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10
-/* VENCINT - vpss_int8 */
-#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4
-
-#define DM365_ISP5_PCCR 0x04
-#define DM365_ISP5_INTSEL1 0x10
-#define DM365_ISP5_INTSEL2 0x14
-#define DM365_ISP5_INTSEL3 0x18
-#define DM365_ISP5_CCDCMUX 0x20
-#define DM365_ISP5_PG_FRAME_SIZE 0x28
-#define DM365_VPBE_CLK_CTRL 0x00
-/*
- * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1,
- * AF - vpss_int3
- */
-#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100
-/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */
-#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f
-/* VENC - vpss_int8 */
-#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015
-
-/* masks and shifts for DM365*/
-#define DM365_CCDC_PG_VD_POL_SHIFT 0
-#define DM365_CCDC_PG_HD_POL_SHIFT 1
-
-#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4))
-#define CCD_SRC_SEL_SHIFT 4
-
-/* Different SoC platforms supported by this driver */
-enum vpss_platform_type {
- DM644X,
- DM355,
- DM365,
-};
-
-/*
- * vpss operations. Depends on platform. Not all functions are available
- * on all platforms. The api, first check if a functio is available before
- * invoking it. In the probe, the function ptrs are initialized based on
- * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc.
- */
-struct vpss_hw_ops {
- /* enable clock */
- int (*enable_clock)(enum vpss_clock_sel clock_sel, int en);
- /* select input to ccdc */
- void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel);
- /* clear wbl overflow bit */
- int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel);
-};
-
-/* vpss configuration */
-struct vpss_oper_config {
- __iomem void *vpss_regs_base0;
- __iomem void *vpss_regs_base1;
- enum vpss_platform_type platform;
- spinlock_t vpss_lock;
- struct vpss_hw_ops hw_ops;
-};
-
-static struct vpss_oper_config oper_cfg;
-
-/* register access routines */
-static inline u32 bl_regr(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
-}
-
-static inline void bl_regw(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
-}
-
-static inline u32 vpss_regr(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base1 + offset);
-}
-
-static inline void vpss_regw(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base1 + offset);
-}
-
-/* For DM365 only */
-static inline u32 isp5_read(u32 offset)
-{
- return __raw_readl(oper_cfg.vpss_regs_base0 + offset);
-}
-
-/* For DM365 only */
-static inline void isp5_write(u32 val, u32 offset)
-{
- __raw_writel(val, oper_cfg.vpss_regs_base0 + offset);
-}
-
-static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK;
-
- /* if we are using pattern generator, enable it */
- if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG)
- temp |= 0x08;
-
- temp |= (src_sel << CCD_SRC_SEL_SHIFT);
- isp5_write(temp, DM365_ISP5_CCDCMUX);
-}
-
-static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX);
-}
-
-int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel)
-{
- if (!oper_cfg.hw_ops.select_ccdc_source)
- return -EINVAL;
-
- oper_cfg.hw_ops.select_ccdc_source(src_sel);
- return 0;
-}
-EXPORT_SYMBOL(vpss_select_ccdc_source);
-
-static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
-{
- u32 mask = 1, val;
-
- if (wbl_sel < VPSS_PCR_AEW_WBL_0 ||
- wbl_sel > VPSS_PCR_CCDC_WBL_O)
- return -EINVAL;
-
- /* writing a 0 clear the overflow */
- mask = ~(mask << wbl_sel);
- val = bl_regr(DM644X_SBL_PCR_VPSS) & mask;
- bl_regw(val, DM644X_SBL_PCR_VPSS);
- return 0;
-}
-
-int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel)
-{
- if (!oper_cfg.hw_ops.clear_wbl_overflow)
- return -EINVAL;
-
- return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel);
-}
-EXPORT_SYMBOL(vpss_clear_wbl_overflow);
-
-/*
- * dm355_enable_clock - Enable VPSS Clock
- * @clock_sel: CLock to be enabled/disabled
- * @en: enable/disable flag
- *
- * This is called to enable or disable a vpss clock
- */
-static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- unsigned long flags;
- u32 utemp, mask = 0x1, shift = 0;
-
- switch (clock_sel) {
- case VPSS_VPBE_CLOCK:
- /* nothing since lsb */
- break;
- case VPSS_VENC_CLOCK_SEL:
- shift = 2;
- break;
- case VPSS_CFALD_CLOCK:
- shift = 3;
- break;
- case VPSS_H3A_CLOCK:
- shift = 4;
- break;
- case VPSS_IPIPE_CLOCK:
- shift = 5;
- break;
- case VPSS_CCDC_CLOCK:
- shift = 6;
- break;
- default:
- printk(KERN_ERR "dm355_enable_clock:"
- " Invalid selector: %d\n", clock_sel);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
- utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL);
- if (!en)
- utemp &= ~(mask << shift);
- else
- utemp |= (mask << shift);
-
- vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL);
- spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
- return 0;
-}
-
-static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- unsigned long flags;
- u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR;
- u32 (*read)(u32 offset) = isp5_read;
- void(*write)(u32 val, u32 offset) = isp5_write;
-
- switch (clock_sel) {
- case VPSS_BL_CLOCK:
- break;
- case VPSS_CCDC_CLOCK:
- shift = 1;
- break;
- case VPSS_H3A_CLOCK:
- shift = 2;
- break;
- case VPSS_RSZ_CLOCK:
- shift = 3;
- break;
- case VPSS_IPIPE_CLOCK:
- shift = 4;
- break;
- case VPSS_IPIPEIF_CLOCK:
- shift = 5;
- break;
- case VPSS_PCLK_INTERNAL:
- shift = 6;
- break;
- case VPSS_PSYNC_CLOCK_SEL:
- shift = 7;
- break;
- case VPSS_VPBE_CLOCK:
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_VENC_CLOCK_SEL:
- shift = 2;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_LDC_CLOCK:
- shift = 3;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_FDIF_CLOCK:
- shift = 4;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_OSD_CLOCK_SEL:
- shift = 6;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- case VPSS_LDC_CLOCK_SEL:
- shift = 7;
- read = vpss_regr;
- write = vpss_regw;
- offset = DM365_VPBE_CLK_CTRL;
- break;
- default:
- printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n",
- clock_sel);
- return -1;
- }
-
- spin_lock_irqsave(&oper_cfg.vpss_lock, flags);
- utemp = read(offset);
- if (!en) {
- mask = ~mask;
- utemp &= (mask << shift);
- } else
- utemp |= (mask << shift);
-
- write(utemp, offset);
- spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags);
-
- return 0;
-}
-
-int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en)
-{
- if (!oper_cfg.hw_ops.enable_clock)
- return -EINVAL;
-
- return oper_cfg.hw_ops.enable_clock(clock_sel, en);
-}
-EXPORT_SYMBOL(vpss_enable_clock);
-
-void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync)
-{
- int val = 0;
- val = isp5_read(DM365_ISP5_CCDCMUX);
-
- val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT);
- val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT);
-
- isp5_write(val, DM365_ISP5_CCDCMUX);
-}
-EXPORT_SYMBOL(dm365_vpss_set_sync_pol);
-
-void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size)
-{
- int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16;
-
- current_reg |= (frame_size.pplen - 1);
- isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE);
-}
-EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
-
-static int __init vpss_probe(struct platform_device *pdev)
-{
- struct resource *r1, *r2;
- char *platform_name;
- int status;
-
- if (!pdev->dev.platform_data) {
- dev_err(&pdev->dev, "no platform data\n");
- return -ENOENT;
- }
-
- platform_name = pdev->dev.platform_data;
- if (!strcmp(platform_name, "dm355_vpss"))
- oper_cfg.platform = DM355;
- else if (!strcmp(platform_name, "dm365_vpss"))
- oper_cfg.platform = DM365;
- else if (!strcmp(platform_name, "dm644x_vpss"))
- oper_cfg.platform = DM644X;
- else {
- dev_err(&pdev->dev, "vpss driver not supported on"
- " this platform\n");
- return -ENODEV;
- }
-
- dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
- r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r1)
- return -ENOENT;
-
- r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
- if (!r1)
- return -EBUSY;
-
- oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
- if (!oper_cfg.vpss_regs_base0) {
- status = -EBUSY;
- goto fail1;
- }
-
- if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!r2) {
- status = -ENOENT;
- goto fail2;
- }
- r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
- if (!r2) {
- status = -EBUSY;
- goto fail2;
- }
-
- oper_cfg.vpss_regs_base1 = ioremap(r2->start,
- resource_size(r2));
- if (!oper_cfg.vpss_regs_base1) {
- status = -EBUSY;
- goto fail3;
- }
- }
-
- if (oper_cfg.platform == DM355) {
- oper_cfg.hw_ops.enable_clock = dm355_enable_clock;
- oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source;
- /* Setup vpss interrupts */
- bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL);
- bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL);
- } else if (oper_cfg.platform == DM365) {
- oper_cfg.hw_ops.enable_clock = dm365_enable_clock;
- oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source;
- /* Setup vpss interrupts */
- isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1);
- isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2);
- isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3);
- } else
- oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow;
-
- spin_lock_init(&oper_cfg.vpss_lock);
- dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
- return 0;
-
-fail3:
- release_mem_region(r2->start, resource_size(r2));
-fail2:
- iounmap(oper_cfg.vpss_regs_base0);
-fail1:
- release_mem_region(r1->start, resource_size(r1));
- return status;
-}
-
-static int __devexit vpss_remove(struct platform_device *pdev)
-{
- struct resource *res;
-
- iounmap(oper_cfg.vpss_regs_base0);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- iounmap(oper_cfg.vpss_regs_base1);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, resource_size(res));
- }
- return 0;
-}
-
-static struct platform_driver vpss_driver = {
- .driver = {
- .name = "vpss",
- .owner = THIS_MODULE,
- },
- .remove = __devexit_p(vpss_remove),
- .probe = vpss_probe,
-};
-
-static void vpss_exit(void)
-{
- platform_driver_unregister(&vpss_driver);
-}
-
-static int __init vpss_init(void)
-{
- return platform_driver_register(&vpss_driver);
-}
-subsys_initcall(vpss_init);
-module_exit(vpss_exit);
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
deleted file mode 100644
index 928ef0d0429f..000000000000
--- a/drivers/media/video/em28xx/Kconfig
+++ /dev/null
@@ -1,58 +0,0 @@
-config VIDEO_EM28XX
- tristate "Empia EM28xx USB video capture support"
- depends on VIDEO_DEV && I2C
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEOBUF_VMALLOC
- select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO
-
- ---help---
- This is a video4linux driver for Empia 28xx based TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called em28xx
-
-config VIDEO_EM28XX_ALSA
- depends on VIDEO_EM28XX && SND
- select SND_PCM
- tristate "Empia EM28xx ALSA audio module"
- ---help---
- This is an ALSA driver for some Empia 28xx based TV cards.
-
- This is not required for em2800/em2820/em2821 boards. However,
- newer em28xx devices uses Vendor Class for audio, instead of
- implementing the USB Audio Class. For those chips, this module
- will enable digital audio.
-
- To compile this driver as a module, choose M here: the
- module will be called em28xx-alsa
-
-config VIDEO_EM28XX_DVB
- tristate "DVB/ATSC Support for em28xx based TV cards"
- depends on VIDEO_EM28XX && DVB_CORE
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_TDA10023 if !DVB_FE_CUSTOMISE
- select DVB_S921 if !DVB_FE_CUSTOMISE
- select DVB_DRXD if !DVB_FE_CUSTOMISE
- select DVB_CXD2820R if !DVB_FE_CUSTOMISE
- select DVB_DRXK if !DVB_FE_CUSTOMISE
- select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE
- select DVB_TDA10071 if !DVB_FE_CUSTOMISE
- select DVB_A8293 if !DVB_FE_CUSTOMISE
- select VIDEOBUF_DVB
- ---help---
- This adds support for DVB cards based on the
- Empiatech em28xx chips.
-
-config VIDEO_EM28XX_RC
- tristate "EM28XX Remote Controller support"
- depends on RC_CORE
- depends on VIDEO_EM28XX
- depends on !(RC_CORE=m && VIDEO_EM28XX=y)
- default VIDEO_EM28XX
- ---help---
- Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
deleted file mode 100644
index c8b338d4be05..000000000000
--- a/drivers/media/video/em28xx/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o
-em28xx-y += em28xx-core.o em28xx-vbi.o
-
-em28xx-alsa-objs := em28xx-audio.o
-em28xx-rc-objs := em28xx-input.o
-
-obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
-obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
-obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
-obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
deleted file mode 100644
index 07dc594e79f0..000000000000
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Empiatech em28x1 audio extension
- *
- * Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
- *
- * Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
- * - Port to work with the in-kernel driver
- * - Cleanups, fixes, alsa-controls, etc.
- *
- * This driver is based on my previous au600 usb pstn audio driver
- * and inherits all the copyrights
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/init.h>
-#include <linux/sound.h>
-#include <linux/spinlock.h>
-#include <linux/soundcard.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/proc_fs.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <sound/tlv.h>
-#include <sound/ac97_codec.h>
-#include <media/v4l2-common.h>
-#include "em28xx.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-#define dprintk(fmt, arg...) do { \
- if (debug) \
- printk(KERN_INFO "em28xx-audio %s: " fmt, \
- __func__, ##arg); \
- } while (0)
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
-
-static int em28xx_deinit_isoc_audio(struct em28xx *dev)
-{
- int i;
-
- dprintk("Stopping isoc\n");
- for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
- if (!irqs_disabled())
- usb_kill_urb(dev->adev.urb[i]);
- else
- usb_unlink_urb(dev->adev.urb[i]);
-
- usb_free_urb(dev->adev.urb[i]);
- dev->adev.urb[i] = NULL;
-
- kfree(dev->adev.transfer_buffer[i]);
- dev->adev.transfer_buffer[i] = NULL;
- }
-
- return 0;
-}
-
-static void em28xx_audio_isocirq(struct urb *urb)
-{
- struct em28xx *dev = urb->context;
- int i;
- unsigned int oldptr;
- int period_elapsed = 0;
- int status;
- unsigned char *cp;
- unsigned int stride;
- struct snd_pcm_substream *substream;
- struct snd_pcm_runtime *runtime;
-
- switch (urb->status) {
- case 0: /* success */
- case -ETIMEDOUT: /* NAK */
- break;
- case -ECONNRESET: /* kill */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- default: /* error */
- dprintk("urb completition error %d.\n", urb->status);
- break;
- }
-
- if (atomic_read(&dev->stream_started) == 0)
- return;
-
- if (dev->adev.capture_pcm_substream) {
- substream = dev->adev.capture_pcm_substream;
- runtime = substream->runtime;
- stride = runtime->frame_bits >> 3;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int length =
- urb->iso_frame_desc[i].actual_length / stride;
- cp = (unsigned char *)urb->transfer_buffer +
- urb->iso_frame_desc[i].offset;
-
- if (!length)
- continue;
-
- oldptr = dev->adev.hwptr_done_capture;
- if (oldptr + length >= runtime->buffer_size) {
- unsigned int cnt =
- runtime->buffer_size - oldptr;
- memcpy(runtime->dma_area + oldptr * stride, cp,
- cnt * stride);
- memcpy(runtime->dma_area, cp + cnt * stride,
- length * stride - cnt * stride);
- } else {
- memcpy(runtime->dma_area + oldptr * stride, cp,
- length * stride);
- }
-
- snd_pcm_stream_lock(substream);
-
- dev->adev.hwptr_done_capture += length;
- if (dev->adev.hwptr_done_capture >=
- runtime->buffer_size)
- dev->adev.hwptr_done_capture -=
- runtime->buffer_size;
-
- dev->adev.capture_transfer_done += length;
- if (dev->adev.capture_transfer_done >=
- runtime->period_size) {
- dev->adev.capture_transfer_done -=
- runtime->period_size;
- period_elapsed = 1;
- }
-
- snd_pcm_stream_unlock(substream);
- }
- if (period_elapsed)
- snd_pcm_period_elapsed(substream);
- }
- urb->status = 0;
-
- status = usb_submit_urb(urb, GFP_ATOMIC);
- if (status < 0) {
- em28xx_errdev("resubmit of audio urb failed (error=%i)\n",
- status);
- }
- return;
-}
-
-static int em28xx_init_audio_isoc(struct em28xx *dev)
-{
- int i, errCode;
- const int sb_size = EM28XX_NUM_AUDIO_PACKETS *
- EM28XX_AUDIO_MAX_PACKET_SIZE;
-
- dprintk("Starting isoc transfers\n");
-
- for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
- struct urb *urb;
- int j, k;
-
- dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC);
- if (!dev->adev.transfer_buffer[i])
- return -ENOMEM;
-
- memset(dev->adev.transfer_buffer[i], 0x80, sb_size);
- urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
- if (!urb) {
- em28xx_errdev("usb_alloc_urb failed!\n");
- for (j = 0; j < i; j++) {
- usb_free_urb(dev->adev.urb[j]);
- kfree(dev->adev.transfer_buffer[j]);
- }
- return -ENOMEM;
- }
-
- urb->dev = dev->udev;
- urb->context = dev;
- urb->pipe = usb_rcvisocpipe(dev->udev, EM28XX_EP_AUDIO);
- urb->transfer_flags = URB_ISO_ASAP;
- urb->transfer_buffer = dev->adev.transfer_buffer[i];
- urb->interval = 1;
- urb->complete = em28xx_audio_isocirq;
- urb->number_of_packets = EM28XX_NUM_AUDIO_PACKETS;
- urb->transfer_buffer_length = sb_size;
-
- for (j = k = 0; j < EM28XX_NUM_AUDIO_PACKETS;
- j++, k += EM28XX_AUDIO_MAX_PACKET_SIZE) {
- urb->iso_frame_desc[j].offset = k;
- urb->iso_frame_desc[j].length =
- EM28XX_AUDIO_MAX_PACKET_SIZE;
- }
- dev->adev.urb[i] = urb;
- }
-
- for (i = 0; i < EM28XX_AUDIO_BUFS; i++) {
- errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
- if (errCode) {
- em28xx_errdev("submit of audio urb failed\n");
- em28xx_deinit_isoc_audio(dev);
- atomic_set(&dev->stream_started, 0);
- return errCode;
- }
-
- }
-
- return 0;
-}
-
-static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
- size_t size)
-{
- struct snd_pcm_runtime *runtime = subs->runtime;
-
- dprintk("Allocating vbuffer\n");
- if (runtime->dma_area) {
- if (runtime->dma_bytes > size)
- return 0;
-
- vfree(runtime->dma_area);
- }
- runtime->dma_area = vmalloc(size);
- if (!runtime->dma_area)
- return -ENOMEM;
-
- runtime->dma_bytes = size;
-
- return 0;
-}
-
-static struct snd_pcm_hardware snd_em28xx_hw_capture = {
- .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_MMAP_VALID,
-
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
-
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
-
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */
- .period_bytes_min = 64, /* 12544/2, */
- .period_bytes_max = 12544,
- .periods_min = 2,
- .periods_max = 98, /* 12544, */
-};
-
-static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
-{
- struct em28xx *dev = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret = 0;
-
- dprintk("opening device and trying to acquire exclusive lock\n");
-
- if (!dev) {
- em28xx_err("BUG: em28xx can't find device struct."
- " Can't proceed with open\n");
- return -ENODEV;
- }
-
- runtime->hw = snd_em28xx_hw_capture;
- if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
- if (dev->audio_ifnum)
- dev->alt = 1;
- else
- dev->alt = 7;
-
- dprintk("changing alternate number on interface %d to %d\n",
- dev->audio_ifnum, dev->alt);
- usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
-
- /* Sets volume, mute, etc */
- dev->mute = 0;
- mutex_lock(&dev->lock);
- ret = em28xx_audio_analog_set(dev);
- if (ret < 0)
- goto err;
-
- dev->adev.users++;
- mutex_unlock(&dev->lock);
- }
-
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- dev->adev.capture_pcm_substream = substream;
- runtime->private_data = dev;
-
- return 0;
-err:
- mutex_unlock(&dev->lock);
-
- em28xx_err("Error while configuring em28xx mixer\n");
- return ret;
-}
-
-static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
-{
- struct em28xx *dev = snd_pcm_substream_chip(substream);
-
- dprintk("closing device\n");
-
- dev->mute = 1;
- mutex_lock(&dev->lock);
- dev->adev.users--;
- if (atomic_read(&dev->stream_started) > 0) {
- atomic_set(&dev->stream_started, 0);
- schedule_work(&dev->wq_trigger);
- }
-
- em28xx_audio_analog_set(dev);
- if (substream->runtime->dma_area) {
- dprintk("freeing\n");
- vfree(substream->runtime->dma_area);
- substream->runtime->dma_area = NULL;
- }
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int ret;
-
- dprintk("Setting capture parameters\n");
-
- ret = snd_pcm_alloc_vmalloc_buffer(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0)
- return ret;
-#if 0
- /* TODO: set up em28xx audio chip to deliver the correct audio format,
- current default is 48000hz multiplexed => 96000hz mono
- which shouldn't matter since analogue TV only supports mono */
- unsigned int channels, rate, format;
-
- format = params_format(hw_params);
- rate = params_rate(hw_params);
- channels = params_channels(hw_params);
-#endif
-
- return 0;
-}
-
-static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
-{
- struct em28xx *dev = snd_pcm_substream_chip(substream);
-
- dprintk("Stop capture, if needed\n");
-
- if (atomic_read(&dev->stream_started) > 0) {
- atomic_set(&dev->stream_started, 0);
- schedule_work(&dev->wq_trigger);
- }
-
- return 0;
-}
-
-static int snd_em28xx_prepare(struct snd_pcm_substream *substream)
-{
- struct em28xx *dev = snd_pcm_substream_chip(substream);
-
- dev->adev.hwptr_done_capture = 0;
- dev->adev.capture_transfer_done = 0;
-
- return 0;
-}
-
-static void audio_trigger(struct work_struct *work)
-{
- struct em28xx *dev = container_of(work, struct em28xx, wq_trigger);
-
- if (atomic_read(&dev->stream_started)) {
- dprintk("starting capture");
- em28xx_init_audio_isoc(dev);
- } else {
- dprintk("stopping capture");
- em28xx_deinit_isoc_audio(dev);
- }
-}
-
-static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
- int cmd)
-{
- struct em28xx *dev = snd_pcm_substream_chip(substream);
- int retval = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
- case SNDRV_PCM_TRIGGER_START:
- atomic_set(&dev->stream_started, 1);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
- case SNDRV_PCM_TRIGGER_STOP:
- atomic_set(&dev->stream_started, 0);
- break;
- default:
- retval = -EINVAL;
- }
- schedule_work(&dev->wq_trigger);
- return retval;
-}
-
-static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
- *substream)
-{
- unsigned long flags;
- struct em28xx *dev;
- snd_pcm_uframes_t hwptr_done;
-
- dev = snd_pcm_substream_chip(substream);
- spin_lock_irqsave(&dev->adev.slock, flags);
- hwptr_done = dev->adev.hwptr_done_capture;
- spin_unlock_irqrestore(&dev->adev.slock, flags);
-
- return hwptr_done;
-}
-
-static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
- unsigned long offset)
-{
- void *pageptr = subs->runtime->dma_area + offset;
-
- return vmalloc_to_page(pageptr);
-}
-
-/*
- * AC97 volume control support
- */
-static int em28xx_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *info)
-{
- info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- info->count = 2;
- info->value.integer.min = 0;
- info->value.integer.max = 0x1f;
-
- return 0;
-}
-
-static int em28xx_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- struct em28xx *dev = snd_kcontrol_chip(kcontrol);
- u16 val = (0x1f - (value->value.integer.value[0] & 0x1f)) |
- (0x1f - (value->value.integer.value[1] & 0x1f)) << 8;
- int rc;
-
- mutex_lock(&dev->lock);
- rc = em28xx_read_ac97(dev, kcontrol->private_value);
- if (rc < 0)
- goto err;
-
- val |= rc & 0x8000; /* Preserve the mute flag */
-
- rc = em28xx_write_ac97(dev, kcontrol->private_value, val);
- if (rc < 0)
- goto err;
-
- dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
- (val & 0x8000) ? "muted " : "",
- 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
- val, (int)kcontrol->private_value);
-
-err:
- mutex_unlock(&dev->lock);
- return rc;
-}
-
-static int em28xx_vol_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- struct em28xx *dev = snd_kcontrol_chip(kcontrol);
- int val;
-
- mutex_lock(&dev->lock);
- val = em28xx_read_ac97(dev, kcontrol->private_value);
- mutex_unlock(&dev->lock);
- if (val < 0)
- return val;
-
- dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
- (val & 0x8000) ? "muted " : "",
- 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
- val, (int)kcontrol->private_value);
-
- value->value.integer.value[0] = 0x1f - (val & 0x1f);
- value->value.integer.value[1] = 0x1f - ((val << 8) & 0x1f);
-
- return 0;
-}
-
-static int em28xx_vol_put_mute(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- struct em28xx *dev = snd_kcontrol_chip(kcontrol);
- u16 val = value->value.integer.value[0];
- int rc;
-
- mutex_lock(&dev->lock);
- rc = em28xx_read_ac97(dev, kcontrol->private_value);
- if (rc < 0)
- goto err;
-
- if (val)
- rc &= 0x1f1f;
- else
- rc |= 0x8000;
-
- rc = em28xx_write_ac97(dev, kcontrol->private_value, rc);
- if (rc < 0)
- goto err;
-
- dprintk("%sleft vol %d, right vol %d (0x%04x) to ac97 volume control 0x%04x\n",
- (val & 0x8000) ? "muted " : "",
- 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
- val, (int)kcontrol->private_value);
-
-err:
- mutex_unlock(&dev->lock);
- return rc;
-}
-
-static int em28xx_vol_get_mute(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *value)
-{
- struct em28xx *dev = snd_kcontrol_chip(kcontrol);
- int val;
-
- mutex_lock(&dev->lock);
- val = em28xx_read_ac97(dev, kcontrol->private_value);
- mutex_unlock(&dev->lock);
- if (val < 0)
- return val;
-
- if (val & 0x8000)
- value->value.integer.value[0] = 0;
- else
- value->value.integer.value[0] = 1;
-
- dprintk("%sleft vol %d, right vol %d (0x%04x) from ac97 volume control 0x%04x\n",
- (val & 0x8000) ? "muted " : "",
- 0x1f - ((val >> 8) & 0x1f), 0x1f - (val & 0x1f),
- val, (int)kcontrol->private_value);
-
- return 0;
-}
-
-static const DECLARE_TLV_DB_SCALE(em28xx_db_scale, -3450, 150, 0);
-
-static int em28xx_cvol_new(struct snd_card *card, struct em28xx *dev,
- char *name, int id)
-{
- int err;
- char ctl_name[44];
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new tmp;
-
- memset (&tmp, 0, sizeof(tmp));
- tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- tmp.private_value = id,
- tmp.name = ctl_name,
-
- /* Add Mute Control */
- sprintf(ctl_name, "%s Switch", name);
- tmp.get = em28xx_vol_get_mute;
- tmp.put = em28xx_vol_put_mute;
- tmp.info = snd_ctl_boolean_mono_info;
- kctl = snd_ctl_new1(&tmp, dev);
- err = snd_ctl_add(card, kctl);
- if (err < 0)
- return err;
- dprintk("Added control %s for ac97 volume control 0x%04x\n",
- ctl_name, id);
-
- memset (&tmp, 0, sizeof(tmp));
- tmp.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- tmp.private_value = id,
- tmp.name = ctl_name,
-
- /* Add Volume Control */
- sprintf(ctl_name, "%s Volume", name);
- tmp.get = em28xx_vol_get;
- tmp.put = em28xx_vol_put;
- tmp.info = em28xx_vol_info;
- tmp.tlv.p = em28xx_db_scale,
- kctl = snd_ctl_new1(&tmp, dev);
- err = snd_ctl_add(card, kctl);
- if (err < 0)
- return err;
- dprintk("Added control %s for ac97 volume control 0x%04x\n",
- ctl_name, id);
-
- return 0;
-}
-
-/*
- * register/unregister code and data
- */
-static struct snd_pcm_ops snd_em28xx_pcm_capture = {
- .open = snd_em28xx_capture_open,
- .close = snd_em28xx_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_em28xx_hw_capture_params,
- .hw_free = snd_em28xx_hw_capture_free,
- .prepare = snd_em28xx_prepare,
- .trigger = snd_em28xx_capture_trigger,
- .pointer = snd_em28xx_capture_pointer,
- .page = snd_pcm_get_vmalloc_page,
-};
-
-static int em28xx_audio_init(struct em28xx *dev)
-{
- struct em28xx_audio *adev = &dev->adev;
- struct snd_pcm *pcm;
- struct snd_card *card;
- static int devnr;
- int err;
-
- if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
- /* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module or
- doesn't have analog audio support at all) */
- return 0;
- }
-
- printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
- printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
- "Rechberger\n");
- printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
-
- err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
- &card);
- if (err < 0)
- return err;
-
- spin_lock_init(&adev->slock);
- err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_em28xx_pcm_capture);
- pcm->info_flags = 0;
- pcm->private_data = dev;
- strcpy(pcm->name, "Empia 28xx Capture");
-
- snd_card_set_dev(card, &dev->udev->dev);
- strcpy(card->driver, "Em28xx-Audio");
- strcpy(card->shortname, "Em28xx Audio");
- strcpy(card->longname, "Empia Em28xx Audio");
-
- INIT_WORK(&dev->wq_trigger, audio_trigger);
-
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
- em28xx_cvol_new(card, dev, "Video", AC97_VIDEO);
- em28xx_cvol_new(card, dev, "Line In", AC97_LINE);
- em28xx_cvol_new(card, dev, "Phone", AC97_PHONE);
- em28xx_cvol_new(card, dev, "Microphone", AC97_MIC);
- em28xx_cvol_new(card, dev, "CD", AC97_CD);
- em28xx_cvol_new(card, dev, "AUX", AC97_AUX);
- em28xx_cvol_new(card, dev, "PCM", AC97_PCM);
-
- em28xx_cvol_new(card, dev, "Master", AC97_MASTER);
- em28xx_cvol_new(card, dev, "Line", AC97_HEADPHONE);
- em28xx_cvol_new(card, dev, "Mono", AC97_MASTER_MONO);
- em28xx_cvol_new(card, dev, "LFE", AC97_CENTER_LFE_MASTER);
- em28xx_cvol_new(card, dev, "Surround", AC97_SURROUND_MASTER);
- }
-
- err = snd_card_register(card);
- if (err < 0) {
- snd_card_free(card);
- return err;
- }
- adev->sndcard = card;
- adev->udev = dev->udev;
-
- return 0;
-}
-
-static int em28xx_audio_fini(struct em28xx *dev)
-{
- if (dev == NULL)
- return 0;
-
- if (dev->has_alsa_audio != 1) {
- /* This device does not support the extension (in this case
- the device is expecting the snd-usb-audio module or
- doesn't have analog audio support at all) */
- return 0;
- }
-
- if (dev->adev.sndcard) {
- snd_card_free(dev->adev.sndcard);
- dev->adev.sndcard = NULL;
- }
-
- return 0;
-}
-
-static struct em28xx_ops audio_ops = {
- .id = EM28XX_AUDIO,
- .name = "Em28xx Audio Extension",
- .init = em28xx_audio_init,
- .fini = em28xx_audio_fini,
-};
-
-static int __init em28xx_alsa_register(void)
-{
- return em28xx_register_extension(&audio_ops);
-}
-
-static void __exit em28xx_alsa_unregister(void)
-{
- em28xx_unregister_extension(&audio_ops);
-}
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
-MODULE_DESCRIPTION("Em28xx Audio driver");
-
-module_init(em28xx_alsa_register);
-module_exit(em28xx_alsa_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
deleted file mode 100644
index ca62b9981380..000000000000
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ /dev/null
@@ -1,3417 +0,0 @@
-/*
- em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB
- video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/usb.h>
-#include <media/tuner.h>
-#include <media/msp3400.h>
-#include <media/saa7115.h>
-#include <media/tvp5150.h>
-#include <media/tvaudio.h>
-#include <media/mt9v011.h>
-#include <media/i2c-addr.h>
-#include <media/tveeprom.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-chip-ident.h>
-
-#include "em28xx.h"
-
-#define DRIVER_NAME "em28xx"
-
-static int tuner = -1;
-module_param(tuner, int, 0444);
-MODULE_PARM_DESC(tuner, "tuner type");
-
-static unsigned int disable_ir;
-module_param(disable_ir, int, 0444);
-MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
-
-static unsigned int disable_usb_speed_check;
-module_param(disable_usb_speed_check, int, 0444);
-MODULE_PARM_DESC(disable_usb_speed_check,
- "override min bandwidth requirement of 480M bps");
-
-static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-module_param_array(card, int, NULL, 0444);
-MODULE_PARM_DESC(card, "card type");
-
-/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS - 1 */
-static unsigned long em28xx_devused;
-
-struct em28xx_hash_table {
- unsigned long hash;
- unsigned int model;
- unsigned int tuner;
-};
-
-static void em28xx_pre_card_setup(struct em28xx *dev);
-
-/*
- * Reset sequences for analog/digital modes
- */
-
-/* Reset for the most [analog] boards */
-static struct em28xx_reg_seq default_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq default_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 analog */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
- {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
- {0x05, 0xff, 0x10, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x04, 0x0f, 10},
- {EM2880_R04_GPO, 0x0c, 0x0f, 10},
- { -1, -1, -1, -1},
-};
-
-/* Board Hauppauge WinTV HVR 900 (R2) digital */
-static struct em28xx_reg_seq hauppauge_wintv_hvr_900R2_digital[] = {
- {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x0c, 0x0f, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = {
- {EM28XX_R08_GPIO, 0x69, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Boards - EM2880 MSI DIGIVOX AD and EM2880_BOARD_MSI_DIGIVOX_AD_II */
-
-/* Board - EM2870 Kworld 355u
- Analog - No input analog */
-
-/* Board - EM2882 Kworld 315U digital */
-static struct em28xx_reg_seq em2882_kworld_315u_digital[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
- {EM2880_R04_GPO, 0x04, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 10},
- {EM28XX_R08_GPIO, 0x7e, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = {
- {EM2880_R04_GPO, 0x08, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 10},
- {EM2880_R04_GPO, 0x08, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq kworld_330u_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x00, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq kworld_330u_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x08, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/* Evga inDtube
- GPIO0 - Enable digital power (s5h1409) - low to enable
- GPIO1 - Enable analog power (tvp5150/emp202) - low to enable
- GPIO4 - xc3028 reset
- GOP3 - s5h1409 reset
- */
-static struct em28xx_reg_seq evga_indtube_analog[] = {
- {EM28XX_R08_GPIO, 0x79, 0xff, 60},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq evga_indtube_digital[] = {
- {EM28XX_R08_GPIO, 0x7a, 0xff, 1},
- {EM2880_R04_GPO, 0x04, 0xff, 10},
- {EM2880_R04_GPO, 0x0c, 0xff, 1},
- { -1, -1, -1, -1},
-};
-
-/*
- * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
- * EM_GPIO_0 - currently unknown
- * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
- * EM_GPIO_2 - currently unknown
- * EM_GPIO_3 - currently unknown
- * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
- * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
- * EM_GPIO_6 - currently unknown
- * EM_GPIO_7 - currently unknown
- */
-static struct em28xx_reg_seq kworld_a340_digital[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Pinnacle Hybrid Pro eb1a:2881 */
-static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
- {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
- {EM2880_R04_GPO, 0x0c, 0xff, 1},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_analog[] = {
- {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x00, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq terratec_cinergy_USB_XS_FR_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x08, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/* eb1a:2868 Reddo DVB-C USB TV Box
- GPIO4 - CU1216L NIM
- Other GPIOs seems to be don't care. */
-static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
- {EM28XX_R08_GPIO, 0xde, 0xff, 10},
- {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0x7f, 0xff, 10},
- {EM28XX_R08_GPIO, 0x6f, 0xff, 10},
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {-1, -1, -1, -1},
-};
-
-/* Callback for the most boards */
-static struct em28xx_reg_seq default_tuner_gpio[] = {
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
- {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
- { -1, -1, -1, -1},
-};
-
-/* Mute/unmute */
-static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
- {EM28XX_R08_GPIO, 5, 7, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
- {EM28XX_R08_GPIO, 4, 7, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq compro_mute_gpio[] = {
- {EM28XX_R08_GPIO, 6, 7, 10},
- { -1, -1, -1, -1},
-};
-
-/* Terratec AV350 */
-static struct em28xx_reg_seq terratec_av350_mute_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0x7f, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq silvercrest_reg_seq[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM28XX_R08_GPIO, 0x01, 0xf7, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq vc211a_enable[] = {
- {EM28XX_R08_GPIO, 0xff, 0x07, 10},
- {EM28XX_R08_GPIO, 0xff, 0x0f, 10},
- {EM28XX_R08_GPIO, 0xff, 0x0b, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq dikom_dk300_digital[] = {
- {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
- {EM2880_R04_GPO, 0x08, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-
-/* Reset for the most [digital] boards */
-static struct em28xx_reg_seq leadership_digital[] = {
- {EM2874_R80_GPIO, 0x70, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq leadership_reset[] = {
- {EM2874_R80_GPIO, 0xf0, 0xff, 10},
- {EM2874_R80_GPIO, 0xb0, 0xff, 10},
- {EM2874_R80_GPIO, 0xf0, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-/* 2013:024f PCTV nanoStick T2 290e
- * GPIO_6 - demod reset
- * GPIO_7 - LED
- */
-static struct em28xx_reg_seq pctv_290e[] = {
- {EM2874_R80_GPIO, 0x00, 0xff, 80},
- {EM2874_R80_GPIO, 0x40, 0xff, 80}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0xc0, 0xff, 80}, /* GPIO_7 = 1 */
- {-1, -1, -1, -1},
-};
-
-#if 0
-static struct em28xx_reg_seq terratec_h5_gpio[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf2, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 50},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq terratec_h5_digital[] = {
- {EM2874_R80_GPIO, 0xf6, 0xff, 10},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 10},
- { -1, -1, -1, -1},
-};
-#endif
-
-/* 2013:024f PCTV DVB-S2 Stick 460e
- * GPIO_0 - POWER_ON
- * GPIO_1 - BOOST
- * GPIO_2 - VUV_LNB (red LED)
- * GPIO_3 - EXT_12V
- * GPIO_4 - INT_DEM (DEMOD GPIO_0)
- * GPIO_5 - INT_LNB
- * GPIO_6 - RESET_DEM
- * GPIO_7 - LED (green LED)
- */
-static struct em28xx_reg_seq pctv_460e[] = {
- {EM2874_R80_GPIO, 0x01, 0xff, 50},
- {0x0d, 0xff, 0xff, 50},
- {EM2874_R80_GPIO, 0x41, 0xff, 50}, /* GPIO_6=1 */
- {0x0d, 0x42, 0xff, 50},
- {EM2874_R80_GPIO, 0x61, 0xff, 50}, /* GPIO_5=1 */
- { -1, -1, -1, -1},
-};
-
-#if 0
-static struct em28xx_reg_seq hauppauge_930c_gpio[] = {
- {EM2874_R80_GPIO, 0x6f, 0xff, 10},
- {EM2874_R80_GPIO, 0x4f, 0xff, 10}, /* xc5000 reset */
- {EM2874_R80_GPIO, 0x6f, 0xff, 10},
- {EM2874_R80_GPIO, 0x4f, 0xff, 10},
- { -1, -1, -1, -1},
-};
-
-static struct em28xx_reg_seq hauppauge_930c_digital[] = {
- {EM2874_R80_GPIO, 0xf6, 0xff, 10},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 10},
- { -1, -1, -1, -1},
-};
-#endif
-
-/* 1b80:e425 MaxMedia UB425-TC
- * GPIO_6 - demod reset, 0=active
- * GPIO_7 - LED, 0=active
- */
-static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
- {EM2874_R80_GPIO, 0x83, 0xff, 100},
- {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
- {-1, -1, -1, -1},
-};
-
-/* 2304:0242 PCTV QuatroStick (510e)
- * GPIO_2: decoder reset, 0=active
- * GPIO_4: decoder suspend, 0=active
- * GPIO_6: demod reset, 0=active
- * GPIO_7: LED, 1=active
- */
-static struct em28xx_reg_seq pctv_510e[] = {
- {EM2874_R80_GPIO, 0x10, 0xff, 100},
- {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
- {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
- { -1, -1, -1, -1},
-};
-
-/* 2013:0251 PCTV QuatroStick nano (520e)
- * GPIO_2: decoder reset, 0=active
- * GPIO_4: decoder suspend, 0=active
- * GPIO_6: demod reset, 0=active
- * GPIO_7: LED, 1=active
- */
-static struct em28xx_reg_seq pctv_520e[] = {
- {EM2874_R80_GPIO, 0x10, 0xff, 100},
- {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
- {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
- {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
- { -1, -1, -1, -1},
-};
-
-/*
- * Board definitions
- */
-struct em28xx_board em28xx_boards[] = {
- [EM2750_BOARD_UNKNOWN] = {
- .name = "EM2710/EM2750/EM2751 webcam grabber",
- .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = silvercrest_reg_seq,
- } },
- },
- [EM2800_BOARD_UNKNOWN] = {
- .name = "Unknown EM2800 video grabber",
- .is_em2800 = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .tuner_type = TUNER_ABSENT,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_UNKNOWN] = {
- .name = "Unknown EM2750/28xx video grabber",
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1, /* To enable sensor probe */
- },
- [EM2750_BOARD_DLCW_130] = {
- /* Beijing Huaqi Information Digital Technology Co., Ltd */
- .name = "Huaqi DLCW-130",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = EM28XX_AMUX_VIDEO,
- } },
- },
- [EM2820_BOARD_KWORLD_PVRTV2800RF] = {
- .name = "Kworld PVR TV 2800 RF",
- .tuner_type = TUNER_TEMIC_PAL,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_GADMEI_TVR200] = {
- .name = "Gadmei TVR200",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_TERRATEC_CINERGY_250] = {
- .name = "Terratec Cinergy 250 USB",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .has_ir_i2c = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_PINNACLE_USB_2] = {
- .name = "Pinnacle PCTV USB 2",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .has_ir_i2c = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_HAUPPAUGE_WINTV_USB_2] = {
- .name = "Hauppauge WinTV USB 2",
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .tda9887_conf = TDA9887_PRESENT |
- TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE,
- .decoder = EM28XX_TVP5150,
- .has_msp34xx = 1,
- .has_ir_i2c = 1,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = MSP_INPUT_DEFAULT,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
- MSP_DSP_IN_SCART, MSP_DSP_IN_SCART),
- } },
- },
- [EM2820_BOARD_DLINK_USB_TV] = {
- .name = "D-Link DUB-T210 TV Tuner",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_HERCULES_SMART_TV_USB2] = {
- .name = "Hercules Smart TV USB 2.0",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_PINNACLE_USB_2_FM1216ME] = {
- .name = "Pinnacle PCTV USB 2 (Philips FM1216ME)",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_GADMEI_UTV310] = {
- .name = "Gadmei UTV310",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_TNF_5335MF,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE] = {
- .name = "Leadtek Winfast USB II Deluxe",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .has_ir_i2c = 1,
- .tvaudio_addr = 0x58,
- .tda9887_conf = TDA9887_PRESENT |
- TDA9887_PORT2_ACTIVE |
- TDA9887_QSS,
- .decoder = EM28XX_SAA711X,
- .adecoder = EM28XX_TVAUDIO,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE4,
- .amux = EM28XX_AMUX_AUX,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE5,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- .radio = {
- .type = EM28XX_RADIO,
- .amux = EM28XX_AMUX_AUX,
- }
- },
- [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = {
- .name = "Videology 20K14XUSB USB2.0",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = EM28XX_AMUX_VIDEO,
- } },
- },
- [EM2820_BOARD_SILVERCREST_WEBCAM] = {
- .name = "Silvercrest Webcam 1.3mpix",
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = silvercrest_reg_seq,
- } },
- },
- [EM2821_BOARD_SUPERCOMP_USB_2] = {
- .name = "Supercomp USB 2.0 TV",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .tda9887_conf = TDA9887_PRESENT |
- TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2821_BOARD_USBGEAR_VD204] = {
- .name = "Usbgear VD204v9",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT, /* Capture only device */
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_NETGMBH_CAM] = {
- /* Beijing Huaqi Information Digital Technology Co., Ltd */
- .name = "NetGMBH Cam",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT,
- .is_webcam = 1,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = 0,
- .amux = EM28XX_AMUX_VIDEO,
- } },
- },
- [EM2860_BOARD_TYPHOON_DVD_MAKER] = {
- .name = "Typhoon DVD Maker",
- .decoder = EM28XX_SAA711X,
- .tuner_type = TUNER_ABSENT, /* Capture only device */
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_GADMEI_UTV330] = {
- .name = "Gadmei UTV330",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_TNF_5335MF,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2861_BOARD_GADMEI_UTV330PLUS] = {
- .name = "Gadmei UTV330+",
- .tuner_type = TUNER_TNF_5335MF,
- .tda9887_conf = TDA9887_PRESENT,
- .ir_codes = RC_MAP_GADMEI_RM008Z,
- .decoder = EM28XX_SAA711X,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_TERRATEC_HYBRID_XS] = {
- .name = "Terratec Cinergy A Hybrid XS",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
-
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2861_BOARD_KWORLD_PVRTV_300U] = {
- .name = "KWorld PVRTV 300U",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
- .name = "Yakumo MovieMixer",
- .tuner_type = TUNER_ABSENT, /* Capture only device */
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_TVP5150_REFERENCE_DESIGN] = {
- .name = "EM2860/TVP5150 Reference Design",
- .tuner_type = TUNER_ABSENT, /* Capture only device */
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2861_BOARD_PLEXTOR_PX_TV100U] = {
- .name = "Plextor ConvertX PX-TV100U",
- .tuner_type = TUNER_TNF_5335MF,
- .xclk = EM28XX_XCLK_I2S_MSB_TIMING |
- EM28XX_XCLK_FREQUENCY_12MHZ,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_TVP5150,
- .has_msp34xx = 1,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = pinnacle_hybrid_pro_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = pinnacle_hybrid_pro_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = pinnacle_hybrid_pro_analog,
- } },
- },
-
- /* Those boards with em2870 are DVB Only*/
-
- [EM2870_BOARD_TERRATEC_XS] = {
- .name = "Terratec Cinergy T XS",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- },
- [EM2870_BOARD_TERRATEC_XS_MT2060] = {
- .name = "Terratec Cinergy T XS (MT2060)",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT, /* MT2060 */
- },
- [EM2870_BOARD_KWORLD_350U] = {
- .name = "Kworld 350 U DVB-T",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- },
- [EM2870_BOARD_KWORLD_355U] = {
- .name = "Kworld 355 U DVB-T",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = default_tuner_gpio,
- .has_dvb = 1,
- .dvb_gpio = default_digital,
- },
- [EM2870_BOARD_PINNACLE_PCTV_DVB] = {
- .name = "Pinnacle PCTV DVB-T",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT, /* MT2060 */
- /* djh - I have serious doubts this is right... */
- .xclk = EM28XX_XCLK_IR_RC5_MODE |
- EM28XX_XCLK_FREQUENCY_10MHZ,
- },
- [EM2870_BOARD_COMPRO_VIDEOMATE] = {
- .name = "Compro, VideoMate U3",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_ABSENT, /* MT2060 */
- },
-
- [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = {
- .name = "Terratec Hybrid XS Secam",
- .has_msp34xx = 1,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .has_dvb = 1,
- .dvb_gpio = terratec_cinergy_USB_XS_FR_digital,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = terratec_cinergy_USB_XS_FR_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = terratec_cinergy_USB_XS_FR_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = terratec_cinergy_USB_XS_FR_analog,
- } },
- },
- [EM2884_BOARD_TERRATEC_H5] = {
- .name = "Terratec Cinergy H5",
- .has_dvb = 1,
-#if 0
- .tuner_type = TUNER_PHILIPS_TDA8290,
- .tuner_addr = 0x41,
- .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */
- .tuner_gpio = terratec_h5_gpio,
-#else
- .tuner_type = TUNER_ABSENT,
-#endif
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
- [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = {
- .name = "Hauppauge WinTV HVR 930C",
- .has_dvb = 1,
-#if 0 /* FIXME: Add analog support */
- .tuner_type = TUNER_XC5000,
- .tuner_addr = 0x41,
- .dvb_gpio = hauppauge_930c_digital,
- .tuner_gpio = hauppauge_930c_gpio,
-#else
- .tuner_type = TUNER_ABSENT,
-#endif
- .ir_codes = RC_MAP_HAUPPAUGE,
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
- [EM2884_BOARD_CINERGY_HTC_STICK] = {
- .name = "Terratec Cinergy HTC Stick",
- .has_dvb = 1,
- .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
- .tuner_type = TUNER_ABSENT,
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
- [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900] = {
- .name = "Hauppauge WinTV HVR 900",
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_HAUPPAUGE,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
- .name = "Hauppauge WinTV HVR 900 (R2)",
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
- .ir_codes = RC_MAP_HAUPPAUGE,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850] = {
- .name = "Hauppauge WinTV HVR 850",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_HAUPPAUGE,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950] = {
- .name = "Hauppauge WinTV HVR 950",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_HAUPPAUGE,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
- .name = "Pinnacle PCTV HD Pro Stick",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600] = {
- .name = "AMD ATI TV Wonder HD 600",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_ATI_TV_WONDER_HD_600,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2880_BOARD_TERRATEC_HYBRID_XS] = {
- .name = "Terratec Hybrid XS",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .has_dvb = 1,
- .dvb_gpio = default_digital,
- .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- } },
- },
- /* maybe there's a reason behind it why Terratec sells the Hybrid XS
- as Prodigy XS with a different PID, let's keep it separated for now
- maybe we'll need it lateron */
- [EM2880_BOARD_TERRATEC_PRODIGY_XS] = {
- .name = "Terratec Prodigy XS",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2820_BOARD_MSI_VOX_USB_2] = {
- .name = "MSI VOX USB 2.0",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT |
- TDA9887_PORT1_ACTIVE |
- TDA9887_PORT2_ACTIVE,
- .max_range_640_480 = 1,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE4,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2800_BOARD_TERRATEC_CINERGY_200] = {
- .name = "Terratec Cinergy 200 USB",
- .is_em2800 = 1,
- .has_ir_i2c = 1,
- .tuner_type = TUNER_LG_TALN,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2800_BOARD_GRABBEEX_USB2800] = {
- .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
- .is_em2800 = 1,
- .decoder = EM28XX_SAA711X,
- .tuner_type = TUNER_ABSENT, /* capture only board */
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2800_BOARD_VC211A] = {
- .name = "Actionmaster/LinXcel/Digitus VC211A",
- .is_em2800 = 1,
- .tuner_type = TUNER_ABSENT, /* Capture-only board */
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = vc211a_enable,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = vc211a_enable,
- } },
- },
- [EM2800_BOARD_LEADTEK_WINFAST_USBII] = {
- .name = "Leadtek Winfast USB II",
- .is_em2800 = 1,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2800_BOARD_KWORLD_USB2800] = {
- .name = "Kworld USB2800",
- .is_em2800 = 1,
- .tuner_type = TUNER_PHILIPS_FCV1236D,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_PINNACLE_DVC_90] = {
- .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker "
- "/ Kworld DVD Maker 2 / Plextor ConvertX PX-AV100U",
- .tuner_type = TUNER_ABSENT, /* capture only board */
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2800_BOARD_VGEAR_POCKETTV] = {
- .name = "V-Gear PocketTV",
- .is_em2800 = 1,
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2] = {
- .name = "Pixelview PlayTV Box 4 USB 2.0",
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_YMEC_TVF_5533MF,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- .aout = EM28XX_AOUT_MONO | /* I2S */
- EM28XX_AOUT_MASTER, /* Line out pin */
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
- .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
- .has_snapshot_button = 1,
- .tda9887_conf = TDA9887_PRESENT,
- .tuner_type = TUNER_YMEC_TVF_5533MF,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- .aout = EM28XX_AOUT_MONO | /* I2S */
- EM28XX_AOUT_MASTER, /* Line out pin */
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = {
- .name = "EM2860/SAA711X Reference Design",
- .has_snapshot_button = 1,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- } },
- },
-
- [EM2874_BOARD_LEADERSHIP_ISDBT] = {
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_100_KHZ,
- .xclk = EM28XX_XCLK_FREQUENCY_10MHZ,
- .name = "EM2874 Leadership ISDBT",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = leadership_reset,
- .dvb_gpio = leadership_digital,
- .has_dvb = 1,
- },
-
- [EM2880_BOARD_MSI_DIGIVOX_AD] = {
- .name = "MSI DigiVox A/D",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = em2880_msi_digivox_ad_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2880_msi_digivox_ad_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2880_msi_digivox_ad_analog,
- } },
- },
- [EM2880_BOARD_MSI_DIGIVOX_AD_II] = {
- .name = "MSI DigiVox A/D II",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = em2880_msi_digivox_ad_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2880_msi_digivox_ad_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2880_msi_digivox_ad_analog,
- } },
- },
- [EM2880_BOARD_KWORLD_DVB_305U] = {
- .name = "KWorld DVB-T 305U",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2880_BOARD_KWORLD_DVB_310U] = {
- .name = "KWorld DVB-T 310U",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .has_dvb = 1,
- .dvb_gpio = default_digital,
- .mts_firmware = 1,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- }, { /* S-video has not been tested yet */
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- } },
- },
- [EM2882_BOARD_KWORLD_ATSC_315U] = {
- .name = "KWorld ATSC 315U HDTV TV Box",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_THOMSON_DTT761X,
- .tuner_gpio = em2882_kworld_315u_tuner_gpio,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_SAA711X,
- .has_dvb = 1,
- .dvb_gpio = em2882_kworld_315u_digital,
- .ir_codes = RC_MAP_KWORLD_315U,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE,
- /* Analog mode - still not ready */
- /*.input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = SAA7115_COMPOSITE2,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = em2882_kworld_315u_analog,
- .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2882_kworld_315u_analog1,
- .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = em2882_kworld_315u_analog1,
- .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- } }, */
- },
- [EM2880_BOARD_EMPIRE_DUAL_TV] = {
- .name = "Empire dual TV",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .has_dvb = 1,
- .dvb_gpio = default_digital,
- .mts_firmware = 1,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- } },
- },
- [EM2881_BOARD_DNT_DA2_HYBRID] = {
- .name = "DNT DA2 Hybrid",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
- } },
- },
- [EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
- .name = "Pinnacle Hybrid Pro",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .has_dvb = 1,
- .dvb_gpio = pinnacle_hybrid_pro_digital,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = pinnacle_hybrid_pro_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = pinnacle_hybrid_pro_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = pinnacle_hybrid_pro_analog,
- } },
- },
- [EM2882_BOARD_PINNACLE_HYBRID_PRO_330E] = {
- .name = "Pinnacle Hybrid Pro (330e)",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900R2_digital,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2882_BOARD_KWORLD_VS_DVBT] = {
- .name = "Kworld VS-DVB-T 323UR",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = kworld_330u_digital,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
- .ir_codes = RC_MAP_KWORLD_315U,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2882_BOARD_TERRATEC_HYBRID_XS] = {
- .name = "Terratec Cinnergy Hybrid T USB XS (em2882)",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .mts_firmware = 1,
- .decoder = EM28XX_TVP5150,
- .has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
- .ir_codes = RC_MAP_TERRATEC_CINERGY_XS,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = hauppauge_wintv_hvr_900_analog,
- } },
- },
- [EM2882_BOARD_DIKOM_DK300] = {
- .name = "Dikom DK300",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = dikom_dk300_digital,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
- } },
- },
- [EM2883_BOARD_KWORLD_HYBRID_330U] = {
- .name = "Kworld PlusTV HD Hybrid 330",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = kworld_330u_digital,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_EEPROM_ON_BOARD |
- EM28XX_I2C_EEPROM_KEY_VALID,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = kworld_330u_analog,
- .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = kworld_330u_analog,
- .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = kworld_330u_analog,
- } },
- },
- [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = {
- .name = "Compro VideoMate ForYou/Stereo",
- .tuner_type = TUNER_LG_PAL_NEW_TAPC,
- .tvaudio_addr = 0xb0,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_TVP5150,
- .adecoder = EM28XX_TVAUDIO,
- .mute_gpio = compro_mute_gpio,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = compro_unmute_tv_gpio,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = compro_unmute_svid_gpio,
- } },
- },
- [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
- .name = "Kaiomy TVnPC U2",
- .vchannels = 3,
- .tuner_type = TUNER_XC2028,
- .tuner_addr = 0x61,
- .mts_firmware = 1,
- .decoder = EM28XX_TVP5150,
- .tuner_gpio = default_tuner_gpio,
- .ir_codes = RC_MAP_KAIOMY,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
-
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- .radio = {
- .type = EM28XX_RADIO,
- .amux = EM28XX_AMUX_LINE_IN,
- }
- },
- [EM2860_BOARD_EASYCAP] = {
- .name = "Easy Cap Capture DC-60",
- .vchannels = 2,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2820_BOARD_IODATA_GVMVP_SZ] = {
- .name = "IO-DATA GV-MVP/SZ",
- .tuner_type = TUNER_PHILIPS_FM1236_MK3,
- .tuner_gpio = default_tuner_gpio,
- .tda9887_conf = TDA9887_PRESENT,
- .decoder = EM28XX_TVP5150,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- }, { /* Composite has not been tested yet */
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_VIDEO,
- }, { /* S-video has not been tested yet */
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_VIDEO,
- } },
- },
- [EM2860_BOARD_TERRATEC_GRABBY] = {
- .name = "Terratec Grabby",
- .vchannels = 2,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA711X,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- [EM2860_BOARD_TERRATEC_AV350] = {
- .name = "Terratec AV350",
- .vchannels = 2,
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_TVP5150,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
- .mute_gpio = terratec_av350_mute_gpio,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AUDIO_SRC_LINE,
- .gpio = terratec_av350_unmute_gpio,
-
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AUDIO_SRC_LINE,
- .gpio = terratec_av350_unmute_gpio,
- } },
- },
-
- [EM2860_BOARD_ELGATO_VIDEO_CAPTURE] = {
- .name = "Elgato Video Capture",
- .decoder = EM28XX_SAA711X,
- .tuner_type = TUNER_ABSENT, /* Capture only device */
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3,
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
-
- [EM2882_BOARD_EVGA_INDTUBE] = {
- .name = "Evga inDtube",
- .tuner_type = TUNER_XC2028,
- .tuner_gpio = default_tuner_gpio,
- .decoder = EM28XX_TVP5150,
- .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
- .mts_firmware = 1,
- .has_dvb = 1,
- .dvb_gpio = evga_indtube_digital,
- .ir_codes = RC_MAP_EVGA_INDTUBE,
- .input = { {
- .type = EM28XX_VMUX_TELEVISION,
- .vmux = TVP5150_COMPOSITE0,
- .amux = EM28XX_AMUX_VIDEO,
- .gpio = evga_indtube_analog,
- }, {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = TVP5150_COMPOSITE1,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = evga_indtube_analog,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = TVP5150_SVIDEO,
- .amux = EM28XX_AMUX_LINE_IN,
- .gpio = evga_indtube_analog,
- } },
- },
- /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
- Infineon TUA6034) */
- [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
- .name = "Reddo DVB-C USB TV Box",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = reddo_dvb_c_usb_box,
- .has_dvb = 1,
- },
- /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
- * initially as the KWorld PlusTV 340U, then as the UB435-Q.
- * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
- [EM2870_BOARD_KWORLD_A340] = {
- .name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
- .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
- .has_dvb = 1,
- .dvb_gpio = kworld_a340_digital,
- .tuner_gpio = default_tuner_gpio,
- },
- /* 2013:024f PCTV nanoStick T2 290e.
- * Empia EM28174, Sony CXD2820R and NXP TDA18271HD/C2 */
- [EM28174_BOARD_PCTV_290E] = {
- .name = "PCTV nanoStick T2 290e",
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_100_KHZ,
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = pctv_290e,
- .has_dvb = 1,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- },
- /* 2013:024f PCTV DVB-S2 Stick 460e
- * Empia EM28174, NXP TDA10071, Conexant CX24118A and Allegro A8293 */
- [EM28174_BOARD_PCTV_460E] = {
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ,
- .name = "PCTV DVB-S2 Stick (460e)",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = pctv_460e,
- .has_dvb = 1,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- },
- /* eb1a:5006 Honestech VIDBOX NW03
- * Empia EM2860, Philips SAA7113, Empia EMP202, No Tuner */
- [EM2860_BOARD_HT_VIDBOX_NW03] = {
- .name = "Honestech Vidbox NW03",
- .tuner_type = TUNER_ABSENT,
- .decoder = EM28XX_SAA711X,
- .input = { {
- .type = EM28XX_VMUX_COMPOSITE1,
- .vmux = SAA7115_COMPOSITE0,
- .amux = EM28XX_AMUX_LINE_IN,
- }, {
- .type = EM28XX_VMUX_SVIDEO,
- .vmux = SAA7115_SVIDEO3, /* S-VIDEO needs confirming */
- .amux = EM28XX_AMUX_LINE_IN,
- } },
- },
- /* 1b80:e425 MaxMedia UB425-TC
- * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
- [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
- .name = "MaxMedia UB425-TC",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = maxmedia_ub425_tc,
- .has_dvb = 1,
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
- /* 2304:0242 PCTV QuatroStick (510e)
- * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
- [EM2884_BOARD_PCTV_510E] = {
- .name = "PCTV QuatroStick (510e)",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = pctv_510e,
- .has_dvb = 1,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
- /* 2013:0251 PCTV QuatroStick nano (520e)
- * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
- [EM2884_BOARD_PCTV_520E] = {
- .name = "PCTV QuatroStick nano (520e)",
- .tuner_type = TUNER_ABSENT,
- .tuner_gpio = pctv_520e,
- .has_dvb = 1,
- .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
- .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
- EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_400_KHZ,
- },
-};
-const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
-
-/* table of devices that work with this driver */
-struct usb_device_id em28xx_id_table[] = {
- { USB_DEVICE(0xeb1a, 0x2750),
- .driver_info = EM2750_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2751),
- .driver_info = EM2750_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2800),
- .driver_info = EM2800_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2710),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2820),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2821),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2860),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2861),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2862),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2863),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2870),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2881),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2883),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2868),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0x2875),
- .driver_info = EM2820_BOARD_UNKNOWN },
- { USB_DEVICE(0xeb1a, 0xe300),
- .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
- { USB_DEVICE(0xeb1a, 0xe303),
- .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
- { USB_DEVICE(0xeb1a, 0xe305),
- .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
- { USB_DEVICE(0xeb1a, 0xe310),
- .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD },
- { USB_DEVICE(0xeb1a, 0xa313),
- .driver_info = EM2882_BOARD_KWORLD_ATSC_315U },
- { USB_DEVICE(0xeb1a, 0xa316),
- .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U },
- { USB_DEVICE(0xeb1a, 0xe320),
- .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD_II },
- { USB_DEVICE(0xeb1a, 0xe323),
- .driver_info = EM2882_BOARD_KWORLD_VS_DVBT },
- { USB_DEVICE(0xeb1a, 0xe350),
- .driver_info = EM2870_BOARD_KWORLD_350U },
- { USB_DEVICE(0xeb1a, 0xe355),
- .driver_info = EM2870_BOARD_KWORLD_355U },
- { USB_DEVICE(0xeb1a, 0x2801),
- .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
- { USB_DEVICE(0xeb1a, 0xe357),
- .driver_info = EM2870_BOARD_KWORLD_355U },
- { USB_DEVICE(0xeb1a, 0xe359),
- .driver_info = EM2870_BOARD_KWORLD_355U },
- { USB_DEVICE(0x1b80, 0xe302),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
- { USB_DEVICE(0x1b80, 0xe304),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */
- { USB_DEVICE(0x0ccd, 0x0036),
- .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
- { USB_DEVICE(0x0ccd, 0x004c),
- .driver_info = EM2880_BOARD_TERRATEC_HYBRID_XS_FR },
- { USB_DEVICE(0x0ccd, 0x004f),
- .driver_info = EM2860_BOARD_TERRATEC_HYBRID_XS },
- { USB_DEVICE(0x0ccd, 0x005e),
- .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
- { USB_DEVICE(0x0ccd, 0x0042),
- .driver_info = EM2882_BOARD_TERRATEC_HYBRID_XS },
- { USB_DEVICE(0x0ccd, 0x0043),
- .driver_info = EM2870_BOARD_TERRATEC_XS },
- { USB_DEVICE(0x0ccd, 0x008e), /* Cinergy HTC USB XS Rev. 1 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
- { USB_DEVICE(0x0ccd, 0x00ac), /* Cinergy HTC USB XS Rev. 2 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
- { USB_DEVICE(0x0ccd, 0x10a2), /* H5 Rev. 1 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
- { USB_DEVICE(0x0ccd, 0x10ad), /* H5 Rev. 2 */
- .driver_info = EM2884_BOARD_TERRATEC_H5 },
- { USB_DEVICE(0x0ccd, 0x0084),
- .driver_info = EM2860_BOARD_TERRATEC_AV350 },
- { USB_DEVICE(0x0ccd, 0x0096),
- .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
- { USB_DEVICE(0x0ccd, 0x10AF),
- .driver_info = EM2860_BOARD_TERRATEC_GRABBY },
- { USB_DEVICE(0x0ccd, 0x00b2),
- .driver_info = EM2884_BOARD_CINERGY_HTC_STICK },
- { USB_DEVICE(0x0fd9, 0x0033),
- .driver_info = EM2860_BOARD_ELGATO_VIDEO_CAPTURE},
- { USB_DEVICE(0x185b, 0x2870),
- .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE },
- { USB_DEVICE(0x185b, 0x2041),
- .driver_info = EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU },
- { USB_DEVICE(0x2040, 0x4200),
- .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
- { USB_DEVICE(0x2040, 0x4201),
- .driver_info = EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 },
- { USB_DEVICE(0x2040, 0x6500),
- .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
- { USB_DEVICE(0x2040, 0x6502),
- .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
- { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
- { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
- { USB_DEVICE(0x2040, 0x651b), /* RP HVR-950 */
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 },
- { USB_DEVICE(0x2040, 0x651f),
- .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 },
- { USB_DEVICE(0x0438, 0xb002),
- .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 },
- { USB_DEVICE(0x2001, 0xf112),
- .driver_info = EM2820_BOARD_DLINK_USB_TV },
- { USB_DEVICE(0x2304, 0x0207),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
- { USB_DEVICE(0x2304, 0x0208),
- .driver_info = EM2820_BOARD_PINNACLE_USB_2 },
- { USB_DEVICE(0x2304, 0x021a),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
- { USB_DEVICE(0x2304, 0x0226),
- .driver_info = EM2882_BOARD_PINNACLE_HYBRID_PRO_330E },
- { USB_DEVICE(0x2304, 0x0227),
- .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
- { USB_DEVICE(0x0413, 0x6023),
- .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
- { USB_DEVICE(0x093b, 0xa003),
- .driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
- { USB_DEVICE(0x093b, 0xa005),
- .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
- { USB_DEVICE(0x04bb, 0x0515),
- .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
- { USB_DEVICE(0xeb1a, 0x50a6),
- .driver_info = EM2860_BOARD_GADMEI_UTV330 },
- { USB_DEVICE(0x1b80, 0xa340),
- .driver_info = EM2870_BOARD_KWORLD_A340 },
- { USB_DEVICE(0x2013, 0x024f),
- .driver_info = EM28174_BOARD_PCTV_290E },
- { USB_DEVICE(0x2013, 0x024c),
- .driver_info = EM28174_BOARD_PCTV_460E },
- { USB_DEVICE(0x2040, 0x1605),
- .driver_info = EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C },
- { USB_DEVICE(0xeb1a, 0x5006),
- .driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
- { USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
- .driver_info = EM2860_BOARD_EASYCAP },
- { USB_DEVICE(0x1b80, 0xe425),
- .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
- { USB_DEVICE(0x2304, 0x0242),
- .driver_info = EM2884_BOARD_PCTV_510E },
- { USB_DEVICE(0x2013, 0x0251),
- .driver_info = EM2884_BOARD_PCTV_520E },
- { },
-};
-MODULE_DEVICE_TABLE(usb, em28xx_id_table);
-
-/*
- * EEPROM hash table for devices with generic USB IDs
- */
-static struct em28xx_hash_table em28xx_eeprom_hash[] = {
- /* P/N: SA 60002070465 Tuner: TVF7533-MF */
- {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
- {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
- {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
- {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
- {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
- {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
- {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
- {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028},
-};
-
-/* I2C devicelist hash table for devices with generic USB IDs */
-static struct em28xx_hash_table em28xx_i2c_hash[] = {
- {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC},
- {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC},
- {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT},
- {0x77800080, EM2860_BOARD_TVP5150_REFERENCE_DESIGN, TUNER_ABSENT},
- {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
- {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF},
- {0x6b800080, EM2874_BOARD_LEADERSHIP_ISDBT, TUNER_ABSENT},
-};
-
-/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */
-static unsigned short saa711x_addrs[] = {
- 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
- 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
- I2C_CLIENT_END };
-
-static unsigned short tvp5150_addrs[] = {
- 0xb8 >> 1,
- 0xba >> 1,
- I2C_CLIENT_END
-};
-
-static unsigned short msp3400_addrs[] = {
- 0x80 >> 1,
- 0x88 >> 1,
- I2C_CLIENT_END
-};
-
-int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
-{
- int rc = 0;
- struct em28xx *dev = ptr;
-
- if (dev->tuner_type != TUNER_XC2028 && dev->tuner_type != TUNER_XC5000)
- return 0;
-
- if (command != XC2028_TUNER_RESET && command != XC5000_TUNER_RESET)
- return 0;
-
- rc = em28xx_gpio_set(dev, dev->board.tuner_gpio);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
-
-static inline void em28xx_set_model(struct em28xx *dev)
-{
- memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
-
- /* Those are the default values for the majority of boards
- Use those values if not specified otherwise at boards entry
- */
- if (!dev->board.xclk)
- dev->board.xclk = EM28XX_XCLK_IR_RC5_MODE |
- EM28XX_XCLK_FREQUENCY_12MHZ;
-
- if (!dev->board.i2c_speed)
- dev->board.i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
- EM28XX_I2C_FREQ_100_KHZ;
-}
-
-
-/* FIXME: Should be replaced by a proper mt9m111 driver */
-static int em28xx_initialize_mt9m111(struct em28xx *dev)
-{
- int i;
- unsigned char regs[][3] = {
- { 0x0d, 0x00, 0x01, }, /* reset and use defaults */
- { 0x0d, 0x00, 0x00, },
- { 0x0a, 0x00, 0x21, },
- { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
- };
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
-
- return 0;
-}
-
-
-/* FIXME: Should be replaced by a proper mt9m001 driver */
-static int em28xx_initialize_mt9m001(struct em28xx *dev)
-{
- int i;
- unsigned char regs[][3] = {
- { 0x0d, 0x00, 0x01, },
- { 0x0d, 0x00, 0x00, },
- { 0x04, 0x05, 0x00, }, /* hres = 1280 */
- { 0x03, 0x04, 0x00, }, /* vres = 1024 */
- { 0x20, 0x11, 0x00, },
- { 0x06, 0x00, 0x10, },
- { 0x2b, 0x00, 0x24, },
- { 0x2e, 0x00, 0x24, },
- { 0x35, 0x00, 0x24, },
- { 0x2d, 0x00, 0x20, },
- { 0x2c, 0x00, 0x20, },
- { 0x09, 0x0a, 0xd4, },
- { 0x35, 0x00, 0x57, },
- };
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
-
- return 0;
-}
-
-/* HINT method: webcam I2C chips
- *
- * This method works for webcams with Micron sensors
- */
-static int em28xx_hint_sensor(struct em28xx *dev)
-{
- int rc;
- char *sensor_name;
- unsigned char cmd;
- __be16 version_be;
- u16 version;
-
- /* Micron sensor detection */
- dev->i2c_client.addr = 0xba >> 1;
- cmd = 0;
- i2c_master_send(&dev->i2c_client, &cmd, 1);
- rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
- if (rc != 2)
- return -EINVAL;
-
- version = be16_to_cpu(version_be);
- switch (version) {
- case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
- case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
- dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
- em28xx_set_model(dev);
-
- sensor_name = "mt9v011";
- dev->em28xx_sensor = EM28XX_MT9V011;
- dev->sensor_xres = 640;
- dev->sensor_yres = 480;
- /*
- * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
- * the Silvercrest cam I have here for testing - for higher
- * resolutions, a high clock cause horizontal artifacts, so we
- * need to use a lower xclk frequency.
- * Yet, it would be possible to adjust xclk depending on the
- * desired resolution, since this affects directly the
- * frame rate.
- */
- dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
- dev->sensor_xtal = 4300000;
-
- /* probably means GRGB 16 bit bayer */
- dev->vinmode = 0x0d;
- dev->vinctl = 0x00;
-
- break;
-
- case 0x143a: /* MT9M111 as found in the ECS G200 */
- dev->model = EM2750_BOARD_UNKNOWN;
- em28xx_set_model(dev);
-
- sensor_name = "mt9m111";
- dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
- dev->em28xx_sensor = EM28XX_MT9M111;
- em28xx_initialize_mt9m111(dev);
- dev->sensor_xres = 640;
- dev->sensor_yres = 512;
-
- dev->vinmode = 0x0a;
- dev->vinctl = 0x00;
-
- break;
-
- case 0x8431:
- dev->model = EM2750_BOARD_UNKNOWN;
- em28xx_set_model(dev);
-
- sensor_name = "mt9m001";
- dev->em28xx_sensor = EM28XX_MT9M001;
- em28xx_initialize_mt9m001(dev);
- dev->sensor_xres = 1280;
- dev->sensor_yres = 1024;
-
- /* probably means BGGR 16 bit bayer */
- dev->vinmode = 0x0c;
- dev->vinctl = 0x00;
-
- break;
- default:
- printk("Unknown Micron Sensor 0x%04x\n", version);
- return -EINVAL;
- }
-
- /* Setup webcam defaults */
- em28xx_pre_card_setup(dev);
-
- em28xx_errdev("Sensor is %s, using model %s entry.\n",
- sensor_name, em28xx_boards[dev->model].name);
-
- return 0;
-}
-
-/* Since em28xx_pre_card_setup() requires a proper dev->model,
- * this won't work for boards with generic PCI IDs
- */
-static void em28xx_pre_card_setup(struct em28xx *dev)
-{
- /* Set the initial XCLK and I2C clock values based on the board
- definition */
- em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
- if (!dev->board.is_em2800)
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
- msleep(50);
-
- /* request some modules */
- switch (dev->model) {
- case EM2861_BOARD_PLEXTOR_PX_TV100U:
- /* Sets the msp34xx I2S speed */
- dev->i2s_speed = 2048000;
- break;
- case EM2861_BOARD_KWORLD_PVRTV_300U:
- case EM2880_BOARD_KWORLD_DVB_305U:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x6d);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0x7d);
- msleep(10);
- break;
- case EM2870_BOARD_COMPRO_VIDEOMATE:
- /* TODO: someone can do some cleanup here...
- not everything's needed */
- em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
- msleep(10);
- em28xx_write_reg(dev, EM2880_R04_GPO, 0x01);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xdc);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfc);
- mdelay(70);
- break;
- case EM2870_BOARD_TERRATEC_XS_MT2060:
- /* this device needs some gpio writes to get the DVB-T
- demod work */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- mdelay(70);
- break;
- case EM2870_BOARD_PINNACLE_PCTV_DVB:
- /* this device needs some gpio writes to get the
- DVB-T demod work */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xde);
- mdelay(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- mdelay(70);
- break;
- case EM2820_BOARD_GADMEI_UTV310:
- case EM2820_BOARD_MSI_VOX_USB_2:
- /* enables audio for that devices */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
- break;
-
- case EM2882_BOARD_KWORLD_ATSC_315U:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- msleep(10);
- em28xx_write_reg(dev, EM2880_R04_GPO, 0x00);
- msleep(10);
- em28xx_write_reg(dev, EM2880_R04_GPO, 0x08);
- msleep(10);
- break;
-
- case EM2860_BOARD_KAIOMY_TVNPC_U2:
- em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
- em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
- em28xx_write_regs(dev, 0x0d, "\x42", 1);
- em28xx_write_regs(dev, 0x08, "\xfd", 1);
- msleep(10);
- em28xx_write_regs(dev, 0x08, "\xff", 1);
- msleep(10);
- em28xx_write_regs(dev, 0x08, "\x7f", 1);
- msleep(10);
- em28xx_write_regs(dev, 0x08, "\x6b", 1);
-
- break;
- case EM2860_BOARD_EASYCAP:
- em28xx_write_regs(dev, 0x08, "\xf8", 1);
- break;
-
- case EM2820_BOARD_IODATA_GVMVP_SZ:
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- msleep(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
- msleep(70);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
- msleep(70);
- break;
- }
-
- em28xx_gpio_set(dev, dev->board.tuner_gpio);
- em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
-
- /* Unlock device */
- em28xx_set_mode(dev, EM28XX_SUSPEND);
-}
-
-static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
-{
- memset(ctl, 0, sizeof(*ctl));
-
- ctl->fname = XC2028_DEFAULT_FIRMWARE;
- ctl->max_len = 64;
- ctl->mts = em28xx_boards[dev->model].mts_firmware;
-
- switch (dev->model) {
- case EM2880_BOARD_EMPIRE_DUAL_TV:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2882_BOARD_TERRATEC_HYBRID_XS:
- ctl->demod = XC3028_FE_ZARLINK456;
- break;
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
- case EM2881_BOARD_PINNACLE_HYBRID_PRO:
- ctl->demod = XC3028_FE_ZARLINK456;
- break;
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
- case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
- ctl->demod = XC3028_FE_DEFAULT;
- break;
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
- ctl->demod = XC3028_FE_DEFAULT;
- ctl->fname = XC3028L_DEFAULT_FIRMWARE;
- break;
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- /* FIXME: Better to specify the needed IF */
- ctl->demod = XC3028_FE_DEFAULT;
- break;
- case EM2883_BOARD_KWORLD_HYBRID_330U:
- case EM2882_BOARD_DIKOM_DK300:
- case EM2882_BOARD_KWORLD_VS_DVBT:
- ctl->demod = XC3028_FE_CHINA;
- ctl->fname = XC2028_DEFAULT_FIRMWARE;
- break;
- case EM2882_BOARD_EVGA_INDTUBE:
- ctl->demod = XC3028_FE_CHINA;
- ctl->fname = XC3028L_DEFAULT_FIRMWARE;
- break;
- default:
- ctl->demod = XC3028_FE_OREN538;
- }
-}
-
-static void em28xx_tuner_setup(struct em28xx *dev)
-{
- struct tuner_setup tun_setup;
- struct v4l2_frequency f;
-
- if (dev->tuner_type == TUNER_ABSENT)
- return;
-
- memset(&tun_setup, 0, sizeof(tun_setup));
-
- tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
- tun_setup.tuner_callback = em28xx_tuner_callback;
-
- if (dev->board.radio.type) {
- tun_setup.type = dev->board.radio.type;
- tun_setup.addr = dev->board.radio_addr;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
- }
-
- if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) {
- tun_setup.type = dev->tuner_type;
- tun_setup.addr = dev->tuner_addr;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup);
- }
-
- if (dev->tda9887_conf) {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &dev->tda9887_conf;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg);
- }
-
- if (dev->tuner_type == TUNER_XC2028) {
- struct v4l2_priv_tun_config xc2028_cfg;
- struct xc2028_ctrl ctl;
-
- memset(&xc2028_cfg, 0, sizeof(xc2028_cfg));
- memset(&ctl, 0, sizeof(ctl));
-
- em28xx_setup_xc3028(dev, &ctl);
-
- xc2028_cfg.tuner = TUNER_XC2028;
- xc2028_cfg.priv = &ctl;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg);
- }
-
- /* configure tuner */
- f.tuner = 0;
- f.type = V4L2_TUNER_ANALOG_TV;
- f.frequency = 9076; /* just a magic number */
- dev->ctl_freq = f.frequency;
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
-}
-
-static int em28xx_hint_board(struct em28xx *dev)
-{
- int i;
-
- /* HINT method: EEPROM
- *
- * This method works only for boards with eeprom.
- * Uses a hash of all eeprom bytes. The hash should be
- * unique for a vendor/tuner pair.
- * There are a high chance that tuners for different
- * video standards produce different hashes.
- */
- for (i = 0; i < ARRAY_SIZE(em28xx_eeprom_hash); i++) {
- if (dev->hash == em28xx_eeprom_hash[i].hash) {
- dev->model = em28xx_eeprom_hash[i].model;
- dev->tuner_type = em28xx_eeprom_hash[i].tuner;
-
- em28xx_errdev("Your board has no unique USB ID.\n");
- em28xx_errdev("A hint were successfully done, "
- "based on eeprom hash.\n");
- em28xx_errdev("This method is not 100%% failproof.\n");
- em28xx_errdev("If the board were missdetected, "
- "please email this log to:\n");
- em28xx_errdev("\tV4L Mailing List "
- " <linux-media@vger.kernel.org>\n");
- em28xx_errdev("Board detected as %s\n",
- em28xx_boards[dev->model].name);
-
- return 0;
- }
- }
-
- /* HINT method: I2C attached devices
- *
- * This method works for all boards.
- * Uses a hash of i2c scanned devices.
- * Devices with the same i2c attached chips will
- * be considered equal.
- * This method is less precise than the eeprom one.
- */
-
- /* user did not request i2c scanning => do it now */
- if (!dev->i2c_hash)
- em28xx_do_i2c_scan(dev);
-
- for (i = 0; i < ARRAY_SIZE(em28xx_i2c_hash); i++) {
- if (dev->i2c_hash == em28xx_i2c_hash[i].hash) {
- dev->model = em28xx_i2c_hash[i].model;
- dev->tuner_type = em28xx_i2c_hash[i].tuner;
- em28xx_errdev("Your board has no unique USB ID.\n");
- em28xx_errdev("A hint were successfully done, "
- "based on i2c devicelist hash.\n");
- em28xx_errdev("This method is not 100%% failproof.\n");
- em28xx_errdev("If the board were missdetected, "
- "please email this log to:\n");
- em28xx_errdev("\tV4L Mailing List "
- " <linux-media@vger.kernel.org>\n");
- em28xx_errdev("Board detected as %s\n",
- em28xx_boards[dev->model].name);
-
- return 0;
- }
- }
-
- em28xx_errdev("Your board has no unique USB ID and thus need a "
- "hint to be detected.\n");
- em28xx_errdev("You may try to use card=<n> insmod option to "
- "workaround that.\n");
- em28xx_errdev("Please send an email with this log to:\n");
- em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n");
- em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash);
- em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash);
-
- em28xx_errdev("Here is a list of valid choices for the card=<n>"
- " insmod option:\n");
- for (i = 0; i < em28xx_bcount; i++) {
- em28xx_errdev(" card=%d -> %s\n",
- i, em28xx_boards[i].name);
- }
- return -1;
-}
-
-static void em28xx_card_setup(struct em28xx *dev)
-{
- /*
- * If the device can be a webcam, seek for a sensor.
- * If sensor is not found, then it isn't a webcam.
- */
- if (dev->board.is_webcam) {
- if (em28xx_hint_sensor(dev) < 0)
- dev->board.is_webcam = 0;
- else
- dev->progressive = 1;
- }
-
- if (!dev->board.is_webcam) {
- switch (dev->model) {
- case EM2820_BOARD_UNKNOWN:
- case EM2800_BOARD_UNKNOWN:
- /*
- * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
- *
- * This occurs because they share identical USB vendor and
- * product IDs.
- *
- * What we do here is look up the EEPROM hash of the K-WORLD
- * and if it is found then we decide that we do not have
- * a DIGIVOX and reset the device to the K-WORLD instead.
- *
- * This solution is only valid if they do not share eeprom
- * hash identities which has not been determined as yet.
- */
- if (em28xx_hint_board(dev) < 0)
- em28xx_errdev("Board not discovered\n");
- else {
- em28xx_set_model(dev);
- em28xx_pre_card_setup(dev);
- }
- break;
- default:
- em28xx_set_model(dev);
- }
- }
-
- em28xx_info("Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
-
- dev->tuner_type = em28xx_boards[dev->model].tuner_type;
- if (em28xx_boards[dev->model].tuner_addr)
- dev->tuner_addr = em28xx_boards[dev->model].tuner_addr;
-
- if (em28xx_boards[dev->model].tda9887_conf)
- dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf;
-
- /* request some modules */
- switch (dev->model) {
- case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- {
- struct tveeprom tv;
-#if defined(CONFIG_MODULES) && defined(MODULE)
- request_module("tveeprom");
-#endif
- /* Call first TVeeprom */
-
- dev->i2c_client.addr = 0xa0 >> 1;
- tveeprom_hauppauge_analog(&dev->i2c_client, &tv, dev->eedata);
-
- dev->tuner_type = tv.tuner_type;
-
- if (tv.audio_processor == V4L2_IDENT_MSPX4XX) {
- dev->i2s_speed = 2048000;
- dev->board.has_msp34xx = 1;
- }
- break;
- }
- case EM2882_BOARD_KWORLD_ATSC_315U:
- em28xx_write_reg(dev, 0x0d, 0x42);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
- msleep(10);
- break;
- case EM2820_BOARD_KWORLD_PVRTV2800RF:
- /* GPIO enables sound on KWORLD PVR TV 2800RF */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9);
- break;
- case EM2820_BOARD_UNKNOWN:
- case EM2800_BOARD_UNKNOWN:
- /*
- * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD.
- *
- * This occurs because they share identical USB vendor and
- * product IDs.
- *
- * What we do here is look up the EEPROM hash of the K-WORLD
- * and if it is found then we decide that we do not have
- * a DIGIVOX and reset the device to the K-WORLD instead.
- *
- * This solution is only valid if they do not share eeprom
- * hash identities which has not been determined as yet.
- */
- case EM2880_BOARD_MSI_DIGIVOX_AD:
- if (!em28xx_hint_board(dev))
- em28xx_set_model(dev);
-
- /* In cases where we had to use a board hint, the call to
- em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
- so make the call now so the analog GPIOs are set properly
- before probing the i2c bus. */
- em28xx_gpio_set(dev, dev->board.tuner_gpio);
- em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
- break;
-
-/*
- * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR.
- *
- * This occurs because they share identical USB vendor and
- * product IDs.
- *
- * What we do here is look up the EEPROM hash of the Dikom
- * and if it is found then we decide that we do not have
- * a Kworld and reset the device to the Dikom instead.
- *
- * This solution is only valid if they do not share eeprom
- * hash identities which has not been determined as yet.
- */
- case EM2882_BOARD_KWORLD_VS_DVBT:
- if (!em28xx_hint_board(dev))
- em28xx_set_model(dev);
-
- /* In cases where we had to use a board hint, the call to
- em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
- so make the call now so the analog GPIOs are set properly
- before probing the i2c bus. */
- em28xx_gpio_set(dev, dev->board.tuner_gpio);
- em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
- break;
- }
-
- if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
- em28xx_errdev("\n\n");
- em28xx_errdev("The support for this board weren't "
- "valid yet.\n");
- em28xx_errdev("Please send a report of having this working\n");
- em28xx_errdev("not to V4L mailing list (and/or to other "
- "addresses)\n\n");
- }
-
- /* Allow override tuner type by a module parameter */
- if (tuner >= 0)
- dev->tuner_type = tuner;
-
- /* request some modules */
- if (dev->board.has_msp34xx)
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "msp3400", 0, msp3400_addrs);
-
- if (dev->board.decoder == EM28XX_SAA711X)
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "saa7115_auto", 0, saa711x_addrs);
-
- if (dev->board.decoder == EM28XX_TVP5150)
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tvp5150", 0, tvp5150_addrs);
-
- if (dev->em28xx_sensor == EM28XX_MT9V011) {
- struct mt9v011_platform_data pdata;
- struct i2c_board_info mt9v011_info = {
- .type = "mt9v011",
- .addr = 0xba >> 1,
- .platform_data = &pdata,
- };
-
- pdata.xtal = dev->sensor_xtal;
- v4l2_i2c_new_subdev_board(&dev->v4l2_dev, &dev->i2c_adap,
- &mt9v011_info, NULL);
- }
-
-
- if (dev->board.adecoder == EM28XX_TVAUDIO)
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tvaudio", dev->board.tvaudio_addr, NULL);
-
- if (dev->board.tuner_type != TUNER_ABSENT) {
- int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
-
- if (dev->board.radio.type)
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tuner", dev->board.radio_addr, NULL);
-
- if (has_demod)
- v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_adap, "tuner",
- 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
- if (dev->tuner_addr == 0) {
- enum v4l2_i2c_tuner_type type =
- has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
- struct v4l2_subdev *sd;
-
- sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
- &dev->i2c_adap, "tuner",
- 0, v4l2_i2c_tuner_addrs(type));
-
- if (sd)
- dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
- } else {
- v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tuner", dev->tuner_addr, NULL);
- }
- }
-
- em28xx_tuner_setup(dev);
-}
-
-
-#if defined(CONFIG_MODULES) && defined(MODULE)
-static void request_module_async(struct work_struct *work)
-{
- struct em28xx *dev = container_of(work,
- struct em28xx, request_module_wk);
-
- if (dev->has_audio_class)
- request_module("snd-usb-audio");
- else if (dev->has_alsa_audio)
- request_module("em28xx-alsa");
-
- if (dev->board.has_dvb)
- request_module("em28xx-dvb");
- if (dev->board.ir_codes && !disable_ir)
- request_module("em28xx-rc");
-}
-
-static void request_modules(struct em28xx *dev)
-{
- INIT_WORK(&dev->request_module_wk, request_module_async);
- schedule_work(&dev->request_module_wk);
-}
-
-static void flush_request_modules(struct em28xx *dev)
-{
- flush_work_sync(&dev->request_module_wk);
-}
-#else
-#define request_modules(dev)
-#define flush_request_modules(dev)
-#endif /* CONFIG_MODULES */
-
-/*
- * em28xx_release_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconnected or at module unload
-*/
-void em28xx_release_resources(struct em28xx *dev)
-{
- /*FIXME: I2C IR should be disconnected */
-
- em28xx_release_analog_resources(dev);
-
- em28xx_i2c_unregister(dev);
-
- v4l2_device_unregister(&dev->v4l2_dev);
-
- usb_put_dev(dev->udev);
-
- /* Mark device as unused */
- clear_bit(dev->devno, &em28xx_devused);
-};
-
-/*
- * em28xx_init_dev()
- * allocates and inits the device structs, registers i2c bus and v4l device
- */
-static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
- struct usb_interface *interface,
- int minor)
-{
- int retval;
-
- dev->udev = udev;
- mutex_init(&dev->ctrl_urb_lock);
- spin_lock_init(&dev->slock);
-
- dev->em28xx_write_regs = em28xx_write_regs;
- dev->em28xx_read_reg = em28xx_read_reg;
- dev->em28xx_read_reg_req_len = em28xx_read_reg_req_len;
- dev->em28xx_write_regs_req = em28xx_write_regs_req;
- dev->em28xx_read_reg_req = em28xx_read_reg_req;
- dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
-
- em28xx_set_model(dev);
-
- /* Set the default GPO/GPIO for legacy devices */
- dev->reg_gpo_num = EM2880_R04_GPO;
- dev->reg_gpio_num = EM28XX_R08_GPIO;
-
- dev->wait_after_write = 5;
-
- /* Based on the Chip ID, set the device configuration */
- retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
- if (retval > 0) {
- dev->chip_id = retval;
-
- switch (dev->chip_id) {
- case CHIP_ID_EM2800:
- em28xx_info("chip ID is em2800\n");
- break;
- case CHIP_ID_EM2710:
- em28xx_info("chip ID is em2710\n");
- break;
- case CHIP_ID_EM2750:
- em28xx_info("chip ID is em2750\n");
- break;
- case CHIP_ID_EM2820:
- em28xx_info("chip ID is em2820 (or em2710)\n");
- break;
- case CHIP_ID_EM2840:
- em28xx_info("chip ID is em2840\n");
- break;
- case CHIP_ID_EM2860:
- em28xx_info("chip ID is em2860\n");
- break;
- case CHIP_ID_EM2870:
- em28xx_info("chip ID is em2870\n");
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2874:
- em28xx_info("chip ID is em2874\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM28174:
- em28xx_info("chip ID is em28174\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2883:
- em28xx_info("chip ID is em2882/em2883\n");
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2884:
- em28xx_info("chip ID is em2884\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- default:
- em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
- }
- }
-
- if (dev->is_audio_only) {
- retval = em28xx_audio_setup(dev);
- if (retval)
- return -ENODEV;
- em28xx_init_extension(dev);
-
- return 0;
- }
-
- /* Prepopulate cached GPO register content */
- retval = em28xx_read_reg(dev, dev->reg_gpo_num);
- if (retval >= 0)
- dev->reg_gpo = retval;
-
- em28xx_pre_card_setup(dev);
-
- if (!dev->board.is_em2800) {
- /* Resets I2C speed */
- retval = em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, dev->board.i2c_speed);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_reg failed!"
- " retval [%d]\n",
- __func__, retval);
- return retval;
- }
- }
-
- retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
- if (retval < 0) {
- em28xx_errdev("Call to v4l2_device_register() failed!\n");
- return retval;
- }
-
- /* register i2c bus */
- retval = em28xx_i2c_register(dev);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_i2c_register - error [%d]!\n",
- __func__, retval);
- goto unregister_dev;
- }
-
- /*
- * Default format, used for tvp5150 or saa711x output formats
- */
- dev->vinmode = 0x10;
- dev->vinctl = EM28XX_VINCTRL_INTERLACED |
- EM28XX_VINCTRL_CCIR656_ENABLE;
-
- /* Do board specific init and eeprom reading */
- em28xx_card_setup(dev);
-
- /* Configure audio */
- retval = em28xx_audio_setup(dev);
- if (retval < 0) {
- em28xx_errdev("%s: Error while setting audio - error [%d]!\n",
- __func__, retval);
- goto fail;
- }
-
- /* wake i2c devices */
- em28xx_wake_i2c(dev);
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vbiq.active);
-
- if (dev->board.has_msp34xx) {
- /* Send a reset to other chips via gpio */
- retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_reg - "
- "msp34xx(1) failed! error [%d]\n",
- __func__, retval);
- goto fail;
- }
- msleep(3);
-
- retval = em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
- if (retval < 0) {
- em28xx_errdev("%s: em28xx_write_reg - "
- "msp34xx(2) failed! error [%d]\n",
- __func__, retval);
- goto fail;
- }
- msleep(3);
- }
-
- retval = em28xx_register_analog_devices(dev);
- if (retval < 0) {
- goto fail;
- }
-
- /* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
-
- return 0;
-
-fail:
- em28xx_i2c_unregister(dev);
-
-unregister_dev:
- v4l2_device_unregister(&dev->v4l2_dev);
-
- return retval;
-}
-
-/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
-#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
-
-/*
- * em28xx_usb_probe()
- * checks for supported devices
- */
-static int em28xx_usb_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct usb_device *udev;
- struct em28xx *dev = NULL;
- int retval;
- bool has_audio = false, has_video = false, has_dvb = false;
- int i, nr;
- const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
- char *speed;
-
- udev = usb_get_dev(interface_to_usbdev(interface));
-
- /* Check to see next free device and mark as used */
- do {
- nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
- if (nr >= EM28XX_MAXBOARDS) {
- /* No free device slots */
- printk(DRIVER_NAME ": Supports only %i em28xx boards.\n",
- EM28XX_MAXBOARDS);
- retval = -ENOMEM;
- goto err_no_slot;
- }
- } while (test_and_set_bit(nr, &em28xx_devused));
-
- /* Don't register audio interfaces */
- if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- em28xx_err(DRIVER_NAME " audio device (%04x:%04x): "
- "interface %i, class %i\n",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting[0].desc.bInterfaceClass);
-
- retval = -ENODEV;
- goto err;
- }
-
- /* allocate memory for our device state and initialize it */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
- em28xx_err(DRIVER_NAME ": out of memory!\n");
- retval = -ENOMEM;
- goto err;
- }
-
- /* compute alternate max packet sizes */
- dev->alt_max_pkt_size = kmalloc(sizeof(dev->alt_max_pkt_size[0]) *
- interface->num_altsetting, GFP_KERNEL);
- if (dev->alt_max_pkt_size == NULL) {
- em28xx_errdev("out of memory!\n");
- kfree(dev);
- retval = -ENOMEM;
- goto err;
- }
-
- /* Get endpoints */
- for (i = 0; i < interface->num_altsetting; i++) {
- int ep;
-
- for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
- const struct usb_endpoint_descriptor *e;
- int sizedescr, size;
-
- e = &interface->altsetting[i].endpoint[ep].desc;
-
- sizedescr = le16_to_cpu(e->wMaxPacketSize);
- size = sizedescr & 0x7ff;
-
- if (udev->speed == USB_SPEED_HIGH)
- size = size * hb_mult(sizedescr);
-
- if (usb_endpoint_xfer_isoc(e) &&
- usb_endpoint_dir_in(e)) {
- switch (e->bEndpointAddress) {
- case EM28XX_EP_AUDIO:
- has_audio = true;
- break;
- case EM28XX_EP_ANALOG:
- has_video = true;
- dev->alt_max_pkt_size[i] = size;
- break;
- case EM28XX_EP_DIGITAL:
- has_dvb = true;
- if (size > dev->dvb_max_pkt_size) {
- dev->dvb_max_pkt_size = size;
- dev->dvb_alt = i;
- }
- break;
- }
- }
- }
- }
-
- if (!(has_audio || has_video || has_dvb)) {
- retval = -ENODEV;
- goto err_free;
- }
-
- 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";
- }
-
- printk(KERN_INFO DRIVER_NAME
- ": New device %s %s @ %s Mbps "
- "(%04x:%04x, interface %d, class %d)\n",
- udev->manufacturer ? udev->manufacturer : "",
- udev->product ? udev->product : "",
- speed,
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct),
- ifnum,
- interface->altsetting->desc.bInterfaceNumber);
-
- if (has_audio)
- printk(KERN_INFO DRIVER_NAME
- ": Audio Vendor Class interface %i found\n",
- ifnum);
- if (has_video)
- printk(KERN_INFO DRIVER_NAME
- ": Video interface %i found\n",
- ifnum);
- if (has_dvb)
- printk(KERN_INFO DRIVER_NAME
- ": DVB interface %i found\n",
- ifnum);
-
- /*
- * Make sure we have 480 Mbps of bandwidth, otherwise things like
- * video stream wouldn't likely work, since 12 Mbps is generally
- * not enough even for most Digital TV streams.
- */
- if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
- printk(DRIVER_NAME ": Device initialization failed.\n");
- printk(DRIVER_NAME ": Device must be connected to a high-speed"
- " USB 2.0 port.\n");
- retval = -ENODEV;
- goto err_free;
- }
-
- snprintf(dev->name, sizeof(dev->name), "em28xx #%d", nr);
- dev->devno = nr;
- dev->model = id->driver_info;
- dev->alt = -1;
- dev->is_audio_only = has_audio && !(has_video || has_dvb);
- dev->has_alsa_audio = has_audio;
- dev->audio_ifnum = ifnum;
-
- /* Checks if audio is provided by some interface */
- for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
- struct usb_interface *uif = udev->config->interface[i];
- if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
- dev->has_audio_class = 1;
- break;
- }
- }
-
- dev->num_alt = interface->num_altsetting;
-
- if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
- dev->model = card[nr];
-
- /* save our data pointer in this interface device */
- usb_set_intfdata(interface, dev);
-
- /* allocate device struct */
- mutex_init(&dev->lock);
- mutex_lock(&dev->lock);
- retval = em28xx_init_dev(dev, udev, interface, nr);
- if (retval) {
- goto unlock_and_free;
- }
-
- if (has_dvb) {
- /* pre-allocate DVB isoc transfer buffers */
- retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
- EM28XX_DVB_MAX_PACKETS,
- EM28XX_DVB_NUM_BUFS,
- dev->dvb_max_pkt_size);
- if (retval) {
- goto unlock_and_free;
- }
- }
-
- request_modules(dev);
-
- /* Should be the last thing to do, to avoid newer udev's to
- open the device before fully initializing it
- */
- mutex_unlock(&dev->lock);
-
- /*
- * These extensions can be modules. If the modules are already
- * loaded then we can initialise the device now, otherwise we
- * will initialise it when the modules load instead.
- */
- em28xx_init_extension(dev);
-
- return 0;
-
-unlock_and_free:
- mutex_unlock(&dev->lock);
-
-err_free:
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
-
-err:
- clear_bit(nr, &em28xx_devused);
-
-err_no_slot:
- usb_put_dev(udev);
- return retval;
-}
-
-/*
- * em28xx_usb_disconnect()
- * called when the device gets disconnected
- * video device will be unregistered on v4l2_close in case it is still open
- */
-static void em28xx_usb_disconnect(struct usb_interface *interface)
-{
- struct em28xx *dev;
-
- dev = usb_get_intfdata(interface);
- usb_set_intfdata(interface, NULL);
-
- if (!dev)
- return;
-
- if (dev->is_audio_only) {
- mutex_lock(&dev->lock);
- em28xx_close_extension(dev);
- mutex_unlock(&dev->lock);
- return;
- }
-
- em28xx_info("disconnecting %s\n", dev->vdev->name);
-
- flush_request_modules(dev);
-
- /* wait until all current v4l2 io is finished then deallocate
- resources */
- mutex_lock(&dev->lock);
-
- v4l2_device_disconnect(&dev->v4l2_dev);
-
- if (dev->users) {
- em28xx_warn
- ("device %s is open! Deregistration and memory "
- "deallocation are deferred on close.\n",
- video_device_node_name(dev->vdev));
-
- dev->state |= DEV_MISCONFIGURED;
- em28xx_uninit_isoc(dev, dev->mode);
- dev->state |= DEV_DISCONNECTED;
- } else {
- dev->state |= DEV_DISCONNECTED;
- em28xx_release_resources(dev);
- }
-
- /* free DVB isoc buffers */
- em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
-
- mutex_unlock(&dev->lock);
-
- em28xx_close_extension(dev);
-
- if (!dev->users) {
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
- }
-}
-
-static struct usb_driver em28xx_usb_driver = {
- .name = "em28xx",
- .probe = em28xx_usb_probe,
- .disconnect = em28xx_usb_disconnect,
- .id_table = em28xx_id_table,
-};
-
-module_usb_driver(em28xx_usb_driver);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
deleted file mode 100644
index de2cb20ad2cc..000000000000
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ /dev/null
@@ -1,1264 +0,0 @@
-/*
- em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/vmalloc.h>
-#include <sound/ac97_codec.h>
-#include <media/v4l2-common.h>
-
-#include "em28xx.h"
-
-/* #define ENABLE_DEBUG_ISOC_FRAMES */
-
-static unsigned int core_debug;
-module_param(core_debug, int, 0644);
-MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
-
-#define em28xx_coredbg(fmt, arg...) do {\
- if (core_debug) \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); } while (0)
-
-static unsigned int reg_debug;
-module_param(reg_debug, int, 0644);
-MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
-
-#define em28xx_regdbg(fmt, arg...) do {\
- if (reg_debug) \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); } while (0)
-
-static int alt;
-module_param(alt, int, 0644);
-MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
-
-static unsigned int disable_vbi;
-module_param(disable_vbi, int, 0644);
-MODULE_PARM_DESC(disable_vbi, "disable vbi support");
-
-/* FIXME */
-#define em28xx_isocdbg(fmt, arg...) do {\
- if (core_debug) \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); } while (0)
-
-/*
- * em28xx_read_reg_req()
- * reads data from the usb device specifying bRequest
- */
-int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
- char *buf, int len)
-{
- int ret;
- int pipe = usb_rcvctrlpipe(dev->udev, 0);
-
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
-
- if (len > URB_MAX_CTRL_SIZE)
- return -EINVAL;
-
- if (reg_debug) {
- printk(KERN_DEBUG "(pipe 0x%08x): "
- "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
- pipe,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8);
- }
-
- mutex_lock(&dev->ctrl_urb_lock);
- ret = usb_control_msg(dev->udev, pipe, req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0000, reg, dev->urb_buf, len, HZ);
- if (ret < 0) {
- if (reg_debug)
- printk(" failed!\n");
- mutex_unlock(&dev->ctrl_urb_lock);
- return ret;
- }
-
- if (len)
- memcpy(buf, dev->urb_buf, len);
-
- mutex_unlock(&dev->ctrl_urb_lock);
-
- if (reg_debug) {
- int byte;
-
- printk("<<<");
- for (byte = 0; byte < len; byte++)
- printk(" %02x", (unsigned char)buf[byte]);
- printk("\n");
- }
-
- return ret;
-}
-
-/*
- * em28xx_read_reg_req()
- * reads data from the usb device specifying bRequest
- */
-int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
-{
- int ret;
- u8 val;
-
- ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
- if (ret < 0)
- return ret;
-
- return val;
-}
-
-int em28xx_read_reg(struct em28xx *dev, u16 reg)
-{
- return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg);
-}
-EXPORT_SYMBOL_GPL(em28xx_read_reg);
-
-/*
- * em28xx_write_regs_req()
- * sends data to the usb device, specifying bRequest
- */
-int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
- int len)
-{
- int ret;
- int pipe = usb_sndctrlpipe(dev->udev, 0);
-
- if (dev->state & DEV_DISCONNECTED)
- return -ENODEV;
-
- if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
- return -EINVAL;
-
- if (reg_debug) {
- int byte;
-
- printk(KERN_DEBUG "(pipe 0x%08x): "
- "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
- pipe,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- req, 0, 0,
- reg & 0xff, reg >> 8,
- len & 0xff, len >> 8);
-
- for (byte = 0; byte < len; byte++)
- printk(" %02x", (unsigned char)buf[byte]);
- printk("\n");
- }
-
- mutex_lock(&dev->ctrl_urb_lock);
- memcpy(dev->urb_buf, buf, len);
- ret = usb_control_msg(dev->udev, pipe, req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x0000, reg, dev->urb_buf, len, HZ);
- mutex_unlock(&dev->ctrl_urb_lock);
-
- if (dev->wait_after_write)
- msleep(dev->wait_after_write);
-
- return ret;
-}
-
-int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
-{
- int rc;
-
- rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
-
- /* Stores GPO/GPIO values at the cache, if changed
- Only write values should be stored, since input on a GPIO
- register will return the input bits.
- Not sure what happens on reading GPO register.
- */
- if (rc >= 0) {
- if (reg == dev->reg_gpo_num)
- dev->reg_gpo = buf[0];
- else if (reg == dev->reg_gpio_num)
- dev->reg_gpio = buf[0];
- }
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(em28xx_write_regs);
-
-/* Write a single register */
-int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
-{
- return em28xx_write_regs(dev, reg, &val, 1);
-}
-EXPORT_SYMBOL_GPL(em28xx_write_reg);
-
-/*
- * em28xx_write_reg_bits()
- * sets only some bits (specified by bitmask) of a register, by first reading
- * the actual value
- */
-int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
- u8 bitmask)
-{
- int oldval;
- u8 newval;
-
- /* Uses cache for gpo/gpio registers */
- if (reg == dev->reg_gpo_num)
- oldval = dev->reg_gpo;
- else if (reg == dev->reg_gpio_num)
- oldval = dev->reg_gpio;
- else
- oldval = em28xx_read_reg(dev, reg);
-
- if (oldval < 0)
- return oldval;
-
- newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
-
- return em28xx_write_regs(dev, reg, &newval, 1);
-}
-EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
-
-/*
- * em28xx_is_ac97_ready()
- * Checks if ac97 is ready
- */
-static int em28xx_is_ac97_ready(struct em28xx *dev)
-{
- int ret, i;
-
- /* Wait up to 50 ms for AC97 command to complete */
- for (i = 0; i < 10; i++, msleep(5)) {
- ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
- if (ret < 0)
- return ret;
-
- if (!(ret & 0x01))
- return 0;
- }
-
- em28xx_warn("AC97 command still being executed: not handled properly!\n");
- return -EBUSY;
-}
-
-/*
- * em28xx_read_ac97()
- * write a 16 bit value to the specified AC97 address (LSB first!)
- */
-int em28xx_read_ac97(struct em28xx *dev, u8 reg)
-{
- int ret;
- u8 addr = (reg & 0x7f) | 0x80;
- u16 val;
-
- ret = em28xx_is_ac97_ready(dev);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
- if (ret < 0)
- return ret;
-
- ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
- (u8 *)&val, sizeof(val));
-
- if (ret < 0)
- return ret;
- return le16_to_cpu(val);
-}
-EXPORT_SYMBOL_GPL(em28xx_read_ac97);
-
-/*
- * em28xx_write_ac97()
- * write a 16 bit value to the specified AC97 address (LSB first!)
- */
-int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
-{
- int ret;
- u8 addr = reg & 0x7f;
- __le16 value;
-
- value = cpu_to_le16(val);
-
- ret = em28xx_is_ac97_ready(dev);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *) &value, 2);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(em28xx_write_ac97);
-
-struct em28xx_vol_itable {
- enum em28xx_amux mux;
- u8 reg;
-};
-
-static struct em28xx_vol_itable inputs[] = {
- { EM28XX_AMUX_VIDEO, AC97_VIDEO },
- { EM28XX_AMUX_LINE_IN, AC97_LINE },
- { EM28XX_AMUX_PHONE, AC97_PHONE },
- { EM28XX_AMUX_MIC, AC97_MIC },
- { EM28XX_AMUX_CD, AC97_CD },
- { EM28XX_AMUX_AUX, AC97_AUX },
- { EM28XX_AMUX_PCM_OUT, AC97_PCM },
-};
-
-static int set_ac97_input(struct em28xx *dev)
-{
- int ret, i;
- enum em28xx_amux amux = dev->ctl_ainput;
-
- /* EM28XX_AMUX_VIDEO2 is a special case used to indicate that
- em28xx should point to LINE IN, while AC97 should use VIDEO
- */
- if (amux == EM28XX_AMUX_VIDEO2)
- amux = EM28XX_AMUX_VIDEO;
-
- /* Mute all entres but the one that were selected */
- for (i = 0; i < ARRAY_SIZE(inputs); i++) {
- if (amux == inputs[i].mux)
- ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
- else
- ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
-
- if (ret < 0)
- em28xx_warn("couldn't setup AC97 register %d\n",
- inputs[i].reg);
- }
- return 0;
-}
-
-static int em28xx_set_audio_source(struct em28xx *dev)
-{
- int ret;
- u8 input;
-
- if (dev->board.is_em2800) {
- if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
- input = EM2800_AUDIO_SRC_TUNER;
- else
- input = EM2800_AUDIO_SRC_LINE;
-
- ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
- if (ret < 0)
- return ret;
- }
-
- if (dev->board.has_msp34xx)
- input = EM28XX_AUDIO_SRC_TUNER;
- else {
- switch (dev->ctl_ainput) {
- case EM28XX_AMUX_VIDEO:
- input = EM28XX_AUDIO_SRC_TUNER;
- break;
- default:
- input = EM28XX_AUDIO_SRC_LINE;
- break;
- }
- }
-
- if (dev->board.mute_gpio && dev->mute)
- em28xx_gpio_set(dev, dev->board.mute_gpio);
- else
- em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
-
- ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
- if (ret < 0)
- return ret;
- msleep(5);
-
- switch (dev->audio_mode.ac97) {
- case EM28XX_NO_AC97:
- break;
- default:
- ret = set_ac97_input(dev);
- }
-
- return ret;
-}
-
-struct em28xx_vol_otable {
- enum em28xx_aout mux;
- u8 reg;
-};
-
-static const struct em28xx_vol_otable outputs[] = {
- { EM28XX_AOUT_MASTER, AC97_MASTER },
- { EM28XX_AOUT_LINE, AC97_HEADPHONE },
- { EM28XX_AOUT_MONO, AC97_MASTER_MONO },
- { EM28XX_AOUT_LFE, AC97_CENTER_LFE_MASTER },
- { EM28XX_AOUT_SURR, AC97_SURROUND_MASTER },
-};
-
-int em28xx_audio_analog_set(struct em28xx *dev)
-{
- int ret, i;
- u8 xclk;
-
- if (!dev->audio_mode.has_audio)
- return 0;
-
- /* It is assumed that all devices use master volume for output.
- It would be possible to use also line output.
- */
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
- /* Mute all outputs */
- for (i = 0; i < ARRAY_SIZE(outputs); i++) {
- ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
- if (ret < 0)
- em28xx_warn("couldn't setup AC97 register %d\n",
- outputs[i].reg);
- }
- }
-
- xclk = dev->board.xclk & 0x7f;
- if (!dev->mute)
- xclk |= EM28XX_XCLK_AUDIO_UNMUTE;
-
- ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
- if (ret < 0)
- return ret;
- msleep(10);
-
- /* Selects the proper audio input */
- ret = em28xx_set_audio_source(dev);
-
- /* Sets volume */
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
- int vol;
-
- em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200);
- em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031);
- em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80);
-
- /* LSB: left channel - both channels with the same level */
- vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
-
- /* Mute device, if needed */
- if (dev->mute)
- vol |= 0x8000;
-
- /* Sets volume */
- for (i = 0; i < ARRAY_SIZE(outputs); i++) {
- if (dev->ctl_aoutput & outputs[i].mux)
- ret = em28xx_write_ac97(dev, outputs[i].reg,
- vol);
- if (ret < 0)
- em28xx_warn("couldn't setup AC97 register %d\n",
- outputs[i].reg);
- }
-
- if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
- int sel = ac97_return_record_select(dev->ctl_aoutput);
-
- /* Use the same input for both left and right
- channels */
- sel |= (sel << 8);
-
- em28xx_write_ac97(dev, AC97_REC_SEL, sel);
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
-
-int em28xx_audio_setup(struct em28xx *dev)
-{
- int vid1, vid2, feat, cfg;
- u32 vid;
-
- if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
- || dev->chip_id == CHIP_ID_EM28174) {
- /* Digital only device - don't load any alsa module */
- dev->audio_mode.has_audio = false;
- dev->has_audio_class = false;
- dev->has_alsa_audio = false;
- return 0;
- }
-
- dev->audio_mode.has_audio = true;
-
- /* See how this device is configured */
- cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
- em28xx_info("Config register raw data: 0x%02x\n", cfg);
- if (cfg < 0) {
- /* Register read error? */
- cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
- } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
- /* The device doesn't have vendor audio at all */
- dev->has_alsa_audio = false;
- dev->audio_mode.has_audio = false;
- return 0;
- } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
- EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
- em28xx_info("I2S Audio (3 sample rates)\n");
- dev->audio_mode.i2s_3rates = 1;
- } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
- EM28XX_CHIPCFG_I2S_5_SAMPRATES) {
- em28xx_info("I2S Audio (5 sample rates)\n");
- dev->audio_mode.i2s_5rates = 1;
- }
-
- if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
- /* Skip the code that does AC97 vendor detection */
- dev->audio_mode.ac97 = EM28XX_NO_AC97;
- goto init_audio;
- }
-
- dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
-
- vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
- if (vid1 < 0) {
- /*
- * Device likely doesn't support AC97
- * Note: (some) em2800 devices without eeprom reports 0x91 on
- * CHIPCFG register, even not having an AC97 chip
- */
- em28xx_warn("AC97 chip type couldn't be determined\n");
- dev->audio_mode.ac97 = EM28XX_NO_AC97;
- dev->has_alsa_audio = false;
- dev->audio_mode.has_audio = false;
- goto init_audio;
- }
-
- vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
- if (vid2 < 0)
- goto init_audio;
-
- vid = vid1 << 16 | vid2;
-
- dev->audio_mode.ac97_vendor_id = vid;
- em28xx_warn("AC97 vendor ID = 0x%08x\n", vid);
-
- feat = em28xx_read_ac97(dev, AC97_RESET);
- if (feat < 0)
- goto init_audio;
-
- dev->audio_mode.ac97_feat = feat;
- em28xx_warn("AC97 features = 0x%04x\n", feat);
-
- /* Try to identify what audio processor we have */
- if (((vid == 0xffffffff) || (vid == 0x83847650)) && (feat == 0x6a90))
- dev->audio_mode.ac97 = EM28XX_AC97_EM202;
- else if ((vid >> 8) == 0x838476)
- dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
-
-init_audio:
- /* Reports detected AC97 processor */
- switch (dev->audio_mode.ac97) {
- case EM28XX_NO_AC97:
- em28xx_info("No AC97 audio processor\n");
- break;
- case EM28XX_AC97_EM202:
- em28xx_info("Empia 202 AC97 audio processor detected\n");
- break;
- case EM28XX_AC97_SIGMATEL:
- em28xx_info("Sigmatel audio processor detected(stac 97%02x)\n",
- dev->audio_mode.ac97_vendor_id & 0xff);
- break;
- case EM28XX_AC97_OTHER:
- em28xx_warn("Unknown AC97 audio processor detected!\n");
- break;
- default:
- break;
- }
-
- return em28xx_audio_analog_set(dev);
-}
-EXPORT_SYMBOL_GPL(em28xx_audio_setup);
-
-int em28xx_colorlevels_set_default(struct em28xx *dev)
-{
- em28xx_write_reg(dev, EM28XX_R20_YGAIN, 0x10); /* contrast */
- em28xx_write_reg(dev, EM28XX_R21_YOFFSET, 0x00); /* brightness */
- em28xx_write_reg(dev, EM28XX_R22_UVGAIN, 0x10); /* saturation */
- em28xx_write_reg(dev, EM28XX_R23_UOFFSET, 0x00);
- em28xx_write_reg(dev, EM28XX_R24_VOFFSET, 0x00);
- em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, 0x00);
-
- em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20);
- em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20);
- em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20);
- em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20);
- em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00);
- em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00);
- return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00);
-}
-
-int em28xx_capture_start(struct em28xx *dev, int start)
-{
- int rc;
-
- if (dev->chip_id == CHIP_ID_EM2874 ||
- dev->chip_id == CHIP_ID_EM2884 ||
- dev->chip_id == CHIP_ID_EM28174) {
- /* The Transport Stream Enable Register moved in em2874 */
- if (!start) {
- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
- 0x00,
- EM2874_TS1_CAPTURE_ENABLE);
- return rc;
- }
-
- /* Enable Transport Stream */
- rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE,
- EM2874_TS1_CAPTURE_ENABLE,
- EM2874_TS1_CAPTURE_ENABLE);
- return rc;
- }
-
-
- /* FIXME: which is the best order? */
- /* video registers are sampled by VREF */
- rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
- start ? 0x10 : 0x00, 0x10);
- if (rc < 0)
- return rc;
-
- if (!start) {
- /* disable video capture */
- rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
- return rc;
- }
-
- if (dev->board.is_webcam)
- rc = em28xx_write_reg(dev, 0x13, 0x0c);
-
- /* enable video capture */
- rc = em28xx_write_reg(dev, 0x48, 0x00);
-
- if (dev->mode == EM28XX_ANALOG_MODE)
- rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
- else
- rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
-
- msleep(6);
-
- return rc;
-}
-
-int em28xx_vbi_supported(struct em28xx *dev)
-{
- /* Modprobe option to manually disable */
- if (disable_vbi == 1)
- return 0;
-
- if (dev->chip_id == CHIP_ID_EM2860 ||
- dev->chip_id == CHIP_ID_EM2883)
- return 1;
-
- /* Version of em28xx that does not support VBI */
- return 0;
-}
-
-int em28xx_set_outfmt(struct em28xx *dev)
-{
- int ret;
- u8 vinctrl;
-
- ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
- dev->format->reg | 0x20, 0xff);
- if (ret < 0)
- return ret;
-
- ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
- if (ret < 0)
- return ret;
-
- vinctrl = dev->vinctl;
- if (em28xx_vbi_supported(dev) == 1) {
- vinctrl |= EM28XX_VINCTRL_VBI_RAW;
- em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
- em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4);
- em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height);
- if (dev->norm & V4L2_STD_525_60) {
- /* NTSC */
- em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
- } else if (dev->norm & V4L2_STD_625_50) {
- /* PAL */
- em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07);
- }
- }
-
- return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
-}
-
-static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
- u8 ymin, u8 ymax)
-{
- em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n",
- xmin, ymin, xmax, ymax);
-
- em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1);
- em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1);
- em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1);
- return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1);
-}
-
-static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart,
- u16 width, u16 height)
-{
- u8 cwidth = width;
- u8 cheight = height;
- u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01);
-
- em28xx_coredbg("em28xx Area Set: (%d,%d)\n",
- (width | (overflow & 2) << 7),
- (height | (overflow & 1) << 8));
-
- em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1);
- em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1);
- em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1);
- em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1);
- return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1);
-}
-
-static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
-{
- u8 mode;
- /* the em2800 scaler only supports scaling down to 50% */
-
- if (dev->board.is_em2800) {
- mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
- } else {
- u8 buf[2];
-
- buf[0] = h;
- buf[1] = h >> 8;
- em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
-
- buf[0] = v;
- buf[1] = v >> 8;
- em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
- /* it seems that both H and V scalers must be active
- to work correctly */
- mode = (h || v) ? 0x30 : 0x00;
- }
- return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
-}
-
-/* FIXME: this only function read values from dev */
-int em28xx_resolution_set(struct em28xx *dev)
-{
- int width, height;
- width = norm_maxw(dev);
- height = norm_maxh(dev);
-
- /* Properly setup VBI */
- dev->vbi_width = 720;
- if (dev->norm & V4L2_STD_525_60)
- dev->vbi_height = 12;
- else
- dev->vbi_height = 18;
-
- if (!dev->progressive)
- height >>= norm_maxh(dev);
-
- em28xx_set_outfmt(dev);
-
-
- em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
-
- /* If we don't set the start position to 2 in VBI mode, we end up
- with line 20/21 being YUYV encoded instead of being in 8-bit
- greyscale. The core of the issue is that line 21 (and line 23 for
- PAL WSS) are inside of active video region, and as a result they
- get the pixelformatting associated with that area. So by cropping
- it out, we end up with the same format as the rest of the VBI
- region */
- if (em28xx_vbi_supported(dev) == 1)
- em28xx_capture_area_set(dev, 0, 2, width >> 2, height >> 2);
- else
- em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
-
- return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
-}
-
-int em28xx_set_alternate(struct em28xx *dev)
-{
- int errCode, prev_alt = dev->alt;
- int i;
- unsigned int min_pkt_size = dev->width * 2 + 4;
-
- /*
- * alt = 0 is used only for control messages, so, only values
- * greater than 0 can be used for streaming.
- */
- if (alt && alt < dev->num_alt) {
- em28xx_coredbg("alternate forced to %d\n", dev->alt);
- dev->alt = alt;
- goto set_alt;
- }
-
- /* When image size is bigger than a certain value,
- the frame size should be increased, otherwise, only
- green screen will be received.
- */
- if (dev->width * 2 * dev->height > 720 * 240 * 2)
- min_pkt_size *= 2;
-
- for (i = 0; i < dev->num_alt; i++) {
- /* stop when the selected alt setting offers enough bandwidth */
- if (dev->alt_max_pkt_size[i] >= min_pkt_size) {
- dev->alt = i;
- break;
- /* otherwise make sure that we end up with the maximum bandwidth
- because the min_pkt_size equation might be wrong...
- */
- } else if (dev->alt_max_pkt_size[i] >
- dev->alt_max_pkt_size[dev->alt])
- dev->alt = i;
- }
-
-set_alt:
- if (dev->alt != prev_alt) {
- em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
- min_pkt_size, dev->alt);
- dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt];
- em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
- dev->alt, dev->max_pkt_size);
- errCode = usb_set_interface(dev->udev, 0, dev->alt);
- if (errCode < 0) {
- em28xx_errdev("cannot change alternate number to %d (error=%i)\n",
- dev->alt, errCode);
- return errCode;
- }
- }
- return 0;
-}
-
-int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
-{
- int rc = 0;
-
- if (!gpio)
- return rc;
-
- if (dev->mode != EM28XX_SUSPEND) {
- em28xx_write_reg(dev, 0x48, 0x00);
- if (dev->mode == EM28XX_ANALOG_MODE)
- em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
- else
- em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
- msleep(6);
- }
-
- /* Send GPIO reset sequences specified at board entry */
- while (gpio->sleep >= 0) {
- if (gpio->reg >= 0) {
- rc = em28xx_write_reg_bits(dev,
- gpio->reg,
- gpio->val,
- gpio->mask);
- if (rc < 0)
- return rc;
- }
- if (gpio->sleep > 0)
- msleep(gpio->sleep);
-
- gpio++;
- }
- return rc;
-}
-EXPORT_SYMBOL_GPL(em28xx_gpio_set);
-
-int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
-{
- if (dev->mode == set_mode)
- return 0;
-
- if (set_mode == EM28XX_SUSPEND) {
- dev->mode = set_mode;
-
- /* FIXME: add suspend support for ac97 */
-
- return em28xx_gpio_set(dev, dev->board.suspend_gpio);
- }
-
- dev->mode = set_mode;
-
- if (dev->mode == EM28XX_DIGITAL_MODE)
- return em28xx_gpio_set(dev, dev->board.dvb_gpio);
- else
- return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
-}
-EXPORT_SYMBOL_GPL(em28xx_set_mode);
-
-/* ------------------------------------------------------------------
- URB control
- ------------------------------------------------------------------*/
-
-/*
- * IRQ callback, called by URB callback
- */
-static void em28xx_irq_callback(struct urb *urb)
-{
- struct em28xx *dev = urb->context;
- int i;
-
- switch (urb->status) {
- case 0: /* success */
- case -ETIMEDOUT: /* NAK */
- break;
- case -ECONNRESET: /* kill */
- case -ENOENT:
- case -ESHUTDOWN:
- return;
- default: /* error */
- em28xx_isocdbg("urb completition error %d.\n", urb->status);
- break;
- }
-
- /* Copy data from URB */
- spin_lock(&dev->slock);
- dev->isoc_ctl.isoc_copy(dev, urb);
- spin_unlock(&dev->slock);
-
- /* Reset urb buffers */
- for (i = 0; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].status = 0;
- urb->iso_frame_desc[i].actual_length = 0;
- }
- urb->status = 0;
-
- urb->status = usb_submit_urb(urb, GFP_ATOMIC);
- if (urb->status) {
- em28xx_isocdbg("urb resubmit failed (error=%i)\n",
- urb->status);
- }
-}
-
-/*
- * Stop and Deallocate URBs
- */
-void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
-{
- struct urb *urb;
- struct em28xx_usb_isoc_bufs *isoc_bufs;
- int i;
-
- em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode);
-
- if (mode == EM28XX_DIGITAL_MODE)
- isoc_bufs = &dev->isoc_ctl.digital_bufs;
- else
- isoc_bufs = &dev->isoc_ctl.analog_bufs;
-
- for (i = 0; i < isoc_bufs->num_bufs; i++) {
- urb = isoc_bufs->urb[i];
- if (urb) {
- if (!irqs_disabled())
- usb_kill_urb(urb);
- else
- usb_unlink_urb(urb);
-
- if (isoc_bufs->transfer_buffer[i]) {
- usb_free_coherent(dev->udev,
- urb->transfer_buffer_length,
- isoc_bufs->transfer_buffer[i],
- urb->transfer_dma);
- }
- usb_free_urb(urb);
- isoc_bufs->urb[i] = NULL;
- }
- isoc_bufs->transfer_buffer[i] = NULL;
- }
-
- kfree(isoc_bufs->urb);
- kfree(isoc_bufs->transfer_buffer);
-
- isoc_bufs->urb = NULL;
- isoc_bufs->transfer_buffer = NULL;
- isoc_bufs->num_bufs = 0;
-
- em28xx_capture_start(dev, 0);
-}
-EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
-
-/*
- * Stop URBs
- */
-void em28xx_stop_urbs(struct em28xx *dev)
-{
- int i;
- struct urb *urb;
- struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs;
-
- em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n");
-
- for (i = 0; i < isoc_bufs->num_bufs; i++) {
- urb = isoc_bufs->urb[i];
- if (urb) {
- if (!irqs_disabled())
- usb_kill_urb(urb);
- else
- usb_unlink_urb(urb);
- }
- }
-
- em28xx_capture_start(dev, 0);
-}
-EXPORT_SYMBOL_GPL(em28xx_stop_urbs);
-
-/*
- * Allocate URBs
- */
-int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
- int max_packets, int num_bufs, int max_pkt_size)
-{
- struct em28xx_usb_isoc_bufs *isoc_bufs;
- int i;
- int sb_size, pipe;
- struct urb *urb;
- int j, k;
-
- em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
-
- if (mode == EM28XX_DIGITAL_MODE)
- isoc_bufs = &dev->isoc_ctl.digital_bufs;
- else
- isoc_bufs = &dev->isoc_ctl.analog_bufs;
-
- /* De-allocates all pending stuff */
- em28xx_uninit_isoc(dev, mode);
-
- isoc_bufs->num_bufs = num_bufs;
-
- isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
- if (!isoc_bufs->urb) {
- em28xx_errdev("cannot alloc memory for usb buffers\n");
- return -ENOMEM;
- }
-
- isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
- if (!isoc_bufs->transfer_buffer) {
- em28xx_errdev("cannot allocate memory for usb transfer\n");
- kfree(isoc_bufs->urb);
- return -ENOMEM;
- }
-
- isoc_bufs->max_pkt_size = max_pkt_size;
- isoc_bufs->num_packets = max_packets;
- dev->isoc_ctl.vid_buf = NULL;
- dev->isoc_ctl.vbi_buf = NULL;
-
- sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size;
-
- /* allocate urbs and transfer buffers */
- for (i = 0; i < isoc_bufs->num_bufs; i++) {
- urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL);
- if (!urb) {
- em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
- em28xx_uninit_isoc(dev, mode);
- return -ENOMEM;
- }
- isoc_bufs->urb[i] = urb;
-
- isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
- sb_size, GFP_KERNEL, &urb->transfer_dma);
- if (!isoc_bufs->transfer_buffer[i]) {
- em28xx_err("unable to allocate %i bytes for transfer"
- " buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
- em28xx_uninit_isoc(dev, mode);
- return -ENOMEM;
- }
- memset(isoc_bufs->transfer_buffer[i], 0, sb_size);
-
- /* FIXME: this is a hack - should be
- 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
- should also be using 'desc.bInterval'
- */
- pipe = usb_rcvisocpipe(dev->udev,
- mode == EM28XX_ANALOG_MODE ?
- EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL);
-
- usb_fill_int_urb(urb, dev->udev, pipe,
- isoc_bufs->transfer_buffer[i], sb_size,
- em28xx_irq_callback, dev, 1);
-
- urb->number_of_packets = isoc_bufs->num_packets;
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-
- k = 0;
- for (j = 0; j < isoc_bufs->num_packets; j++) {
- urb->iso_frame_desc[j].offset = k;
- urb->iso_frame_desc[j].length =
- isoc_bufs->max_pkt_size;
- k += isoc_bufs->max_pkt_size;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(em28xx_alloc_isoc);
-
-/*
- * Allocate URBs and start IRQ
- */
-int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
- int max_packets, int num_bufs, int max_pkt_size,
- int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
-{
- struct em28xx_dmaqueue *dma_q = &dev->vidq;
- struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
- struct em28xx_usb_isoc_bufs *isoc_bufs;
- int i;
- int rc;
- int alloc;
-
- em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode);
-
- dev->isoc_ctl.isoc_copy = isoc_copy;
-
- if (mode == EM28XX_DIGITAL_MODE) {
- isoc_bufs = &dev->isoc_ctl.digital_bufs;
- /* no need to free/alloc isoc buffers in digital mode */
- alloc = 0;
- } else {
- isoc_bufs = &dev->isoc_ctl.analog_bufs;
- alloc = 1;
- }
-
- if (alloc) {
- rc = em28xx_alloc_isoc(dev, mode, max_packets,
- num_bufs, max_pkt_size);
- if (rc)
- return rc;
- }
-
- init_waitqueue_head(&dma_q->wq);
- init_waitqueue_head(&vbi_dma_q->wq);
-
- em28xx_capture_start(dev, 1);
-
- /* submit urbs and enables IRQ */
- for (i = 0; i < isoc_bufs->num_bufs; i++) {
- rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC);
- if (rc) {
- em28xx_err("submit of urb %i failed (error=%i)\n", i,
- rc);
- em28xx_uninit_isoc(dev, mode);
- return rc;
- }
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(em28xx_init_isoc);
-
-/*
- * em28xx_wake_i2c()
- * configure i2c attached devices
- */
-void em28xx_wake_i2c(struct em28xx *dev)
-{
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0);
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
- INPUT(dev->ctl_input)->vmux, 0, 0);
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
-}
-
-/*
- * Device control list
- */
-
-static LIST_HEAD(em28xx_devlist);
-static DEFINE_MUTEX(em28xx_devlist_mutex);
-
-/*
- * Extension interface
- */
-
-static LIST_HEAD(em28xx_extension_devlist);
-
-int em28xx_register_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&ops->next, &em28xx_extension_devlist);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->init(dev);
- }
- mutex_unlock(&em28xx_devlist_mutex);
- printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
- return 0;
-}
-EXPORT_SYMBOL(em28xx_register_extension);
-
-void em28xx_unregister_extension(struct em28xx_ops *ops)
-{
- struct em28xx *dev = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(dev, &em28xx_devlist, devlist) {
- ops->fini(dev);
- }
- list_del(&ops->next);
- mutex_unlock(&em28xx_devlist_mutex);
- printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
-}
-EXPORT_SYMBOL(em28xx_unregister_extension);
-
-void em28xx_init_extension(struct em28xx *dev)
-{
- const struct em28xx_ops *ops = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_add_tail(&dev->devlist, &em28xx_devlist);
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->init)
- ops->init(dev);
- }
- mutex_unlock(&em28xx_devlist_mutex);
-}
-
-void em28xx_close_extension(struct em28xx *dev)
-{
- const struct em28xx_ops *ops = NULL;
-
- mutex_lock(&em28xx_devlist_mutex);
- list_for_each_entry(ops, &em28xx_extension_devlist, next) {
- if (ops->fini)
- ops->fini(dev);
- }
- list_del(&dev->devlist);
- mutex_unlock(&em28xx_devlist_mutex);
-}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
deleted file mode 100644
index a16531fa937a..000000000000
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ /dev/null
@@ -1,1197 +0,0 @@
-/*
- DVB device driver for em28xx
-
- (c) 2008-2011 Mauro Carvalho Chehab <mchehab@infradead.org>
-
- (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
- - Fixes for the driver to properly work with HVR-950
- - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
- - Fixes for the driver to properly work with AMD ATI TV Wonder HD 600
-
- (c) 2008 Aidan Thornton <makosoft@googlemail.com>
-
- Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by:
- (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au>
- (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License.
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-
-#include "em28xx.h"
-#include <media/v4l2-common.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/tuner.h>
-#include "tuner-simple.h"
-
-#include "lgdt330x.h"
-#include "lgdt3305.h"
-#include "zl10353.h"
-#include "s5h1409.h"
-#include "mt352.h"
-#include "mt352_priv.h" /* FIXME */
-#include "tda1002x.h"
-#include "tda18271.h"
-#include "s921.h"
-#include "drxd.h"
-#include "cxd2820r.h"
-#include "tda18271c2dd.h"
-#include "drxk.h"
-#include "tda10071.h"
-#include "a8293.h"
-#include "qt1010.h"
-
-MODULE_DESCRIPTION("driver for em28xx based DVB cards");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_LICENSE("GPL");
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk(level, fmt, arg...) do { \
-if (debug >= level) \
- printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
-} while (0)
-
-struct em28xx_dvb {
- struct dvb_frontend *fe[2];
-
- /* feed count management */
- struct mutex lock;
- int nfeeds;
-
- /* general boilerplate stuff */
- struct dvb_adapter adapter;
- struct dvb_demux demux;
- struct dmxdev dmxdev;
- struct dmx_frontend fe_hw;
- struct dmx_frontend fe_mem;
- struct dvb_net net;
-
- /* Due to DRX-K - probably need changes */
- int (*gate_ctrl)(struct dvb_frontend *, int);
- struct semaphore pll_mutex;
- bool dont_attach_fe1;
-};
-
-
-static inline void print_err_status(struct em28xx *dev,
- int packet, int status)
-{
- char *errmsg = "Unknown";
-
- switch (status) {
- case -ENOENT:
- errmsg = "unlinked synchronuously";
- break;
- case -ECONNRESET:
- errmsg = "unlinked asynchronuously";
- break;
- case -ENOSR:
- errmsg = "Buffer error (overrun)";
- break;
- case -EPIPE:
- errmsg = "Stalled (device not responding)";
- break;
- case -EOVERFLOW:
- errmsg = "Babble (bad cable?)";
- break;
- case -EPROTO:
- errmsg = "Bit-stuff error (bad cable?)";
- break;
- case -EILSEQ:
- errmsg = "CRC/Timeout (could be anything)";
- break;
- case -ETIME:
- errmsg = "Device does not respond";
- break;
- }
- if (packet < 0) {
- dprintk(1, "URB status %d [%s].\n", status, errmsg);
- } else {
- dprintk(1, "URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
- }
-}
-
-static inline int em28xx_dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
-{
- int i;
-
- if (!dev)
- return 0;
-
- if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- if (urb->iso_frame_desc[i].status != -EPROTO)
- continue;
- }
-
- dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
- urb->iso_frame_desc[i].offset,
- urb->iso_frame_desc[i].actual_length);
- }
-
- return 0;
-}
-
-static int em28xx_start_streaming(struct em28xx_dvb *dvb)
-{
- int rc;
- struct em28xx *dev = dvb->adapter.priv;
- int max_dvb_packet_size;
-
- usb_set_interface(dev->udev, 0, dev->dvb_alt);
- rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
- if (rc < 0)
- return rc;
-
- max_dvb_packet_size = dev->dvb_max_pkt_size;
- if (max_dvb_packet_size < 0)
- return max_dvb_packet_size;
- dprintk(1, "Using %d buffers each with %d x %d bytes\n",
- EM28XX_DVB_NUM_BUFS,
- EM28XX_DVB_MAX_PACKETS,
- max_dvb_packet_size);
-
- return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE,
- EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS,
- max_dvb_packet_size, em28xx_dvb_isoc_copy);
-}
-
-static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
-{
- struct em28xx *dev = dvb->adapter.priv;
-
- em28xx_stop_urbs(dev);
-
- em28xx_set_mode(dev, EM28XX_SUSPEND);
-
- return 0;
-}
-
-static int em28xx_start_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct em28xx_dvb *dvb = demux->priv;
- int rc, ret;
-
- if (!demux->dmx.frontend)
- return -EINVAL;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds++;
- rc = dvb->nfeeds;
-
- if (dvb->nfeeds == 1) {
- ret = em28xx_start_streaming(dvb);
- if (ret < 0)
- rc = ret;
- }
-
- mutex_unlock(&dvb->lock);
- return rc;
-}
-
-static int em28xx_stop_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct em28xx_dvb *dvb = demux->priv;
- int err = 0;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds--;
-
- if (0 == dvb->nfeeds)
- err = em28xx_stop_streaming(dvb);
-
- mutex_unlock(&dvb->lock);
- return err;
-}
-
-
-
-/* ------------------------------------------------------------------ */
-static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
-{
- struct em28xx *dev = fe->dvb->priv;
-
- if (acquire)
- return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
- else
- return em28xx_set_mode(dev, EM28XX_SUSPEND);
-}
-
-/* ------------------------------------------------------------------ */
-
-static struct lgdt330x_config em2880_lgdt3303_dev = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3303,
-};
-
-static struct lgdt3305_config em2870_lgdt3304_dev = {
- .i2c_addr = 0x0e,
- .demod_chip = LGDT3304,
- .spectral_inversion = 1,
- .deny_i2c_rptr = 1,
- .mpeg_mode = LGDT3305_MPEG_PARALLEL,
- .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
- .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
- .vsb_if_khz = 3250,
- .qam_if_khz = 4000,
-};
-
-static struct s921_config sharp_isdbt = {
- .demod_address = 0x30 >> 1
-};
-
-static struct zl10353_config em28xx_zl10353_with_xc3028 = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .parallel_ts = 1,
- .if2 = 45600,
-};
-
-static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_PARALLEL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .inversion = S5H1409_INVERSION_OFF,
- .status_mode = S5H1409_DEMODLOCKING,
- .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
-};
-
-static struct tda18271_std_map kworld_a340_std_map = {
- .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1,
- .if_lvl = 1, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config kworld_a340_config = {
- .std_map = &kworld_a340_std_map,
-};
-
-static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .disable_i2c_gate_ctrl = 1,
- .parallel_ts = 1,
- .if2 = 45600,
-};
-
-static struct drxd_config em28xx_drxd = {
- .demod_address = 0x70,
- .demod_revision = 0xa2,
- .pll_type = DRXD_PLL_NONE,
- .clock = 12000,
- .insert_rs_byte = 1,
- .IF = 42800000,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static struct drxk_config terratec_h5_drxk = {
- .adr = 0x29,
- .single_master = 1,
- .no_i2c_bridge = 1,
- .microcode_name = "dvb-usb-terratec-h5-drxk.fw",
- .qam_demod_parameter_count = 2,
-};
-
-static struct drxk_config hauppauge_930c_drxk = {
- .adr = 0x29,
- .single_master = 1,
- .no_i2c_bridge = 1,
- .microcode_name = "dvb-usb-hauppauge-hvr930c-drxk.fw",
- .chunk_size = 56,
- .qam_demod_parameter_count = 2,
-};
-
-struct drxk_config terratec_htc_stick_drxk = {
- .adr = 0x29,
- .single_master = 1,
- .no_i2c_bridge = 1,
- .microcode_name = "dvb-usb-terratec-htc-stick-drxk.fw",
- .chunk_size = 54,
- .qam_demod_parameter_count = 2,
- /* Required for the antenna_gpio to disable LNA. */
- .antenna_dvbt = true,
- /* The windows driver uses the same. This will disable LNA. */
- .antenna_gpio = 0x6,
-};
-
-static struct drxk_config maxmedia_ub425_tc_drxk = {
- .adr = 0x29,
- .single_master = 1,
- .no_i2c_bridge = 1,
-};
-
-static struct drxk_config pctv_520e_drxk = {
- .adr = 0x29,
- .single_master = 1,
- .microcode_name = "dvb-demod-drxk-pctv.fw",
- .qam_demod_parameter_count = 2,
- .chunk_size = 58,
- .antenna_dvbt = true, /* disable LNA */
- .antenna_gpio = (1 << 2), /* disable LNA */
-};
-
-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct em28xx_dvb *dvb = fe->sec_priv;
- int status;
-
- if (!dvb)
- return -EINVAL;
-
- if (enable) {
- down(&dvb->pll_mutex);
- status = dvb->gate_ctrl(fe, 1);
- } else {
- status = dvb->gate_ctrl(fe, 0);
- up(&dvb->pll_mutex);
- }
- return status;
-}
-
-static void hauppauge_hvr930c_init(struct em28xx *dev)
-{
- int i;
-
- struct em28xx_reg_seq hauppauge_hvr930c_init[] = {
- {EM2874_R80_GPIO, 0xff, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xfb, 0xff, 0x32},
- {EM2874_R80_GPIO, 0xff, 0xff, 0xb8},
- { -1, -1, -1, -1},
- };
- struct em28xx_reg_seq hauppauge_hvr930c_end[] = {
- {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
- {EM2874_R80_GPIO, 0xaf, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x76},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x01},
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x40},
-
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
- {EM2874_R80_GPIO, 0xcf, 0xff, 0x0b},
- {EM2874_R80_GPIO, 0xef, 0xff, 0x65},
-
- { -1, -1, -1, -1},
- };
-
- struct {
- unsigned char r[4];
- int len;
- } regs[] = {
- {{ 0x06, 0x02, 0x00, 0x31 }, 4},
- {{ 0x01, 0x02 }, 2},
- {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0xff, 0xaf }, 4},
- {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0x73, 0xaf }, 4},
- {{ 0x04, 0x00 }, 2},
- {{ 0x00, 0x04 }, 2},
- {{ 0x00, 0x04, 0x00, 0x0a }, 4},
- {{ 0x04, 0x14 }, 2},
- {{ 0x04, 0x14, 0x00, 0x00 }, 4},
- };
-
- em28xx_gpio_set(dev, hauppauge_hvr930c_init);
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(10);
-
- dev->i2c_client.addr = 0x82 >> 1;
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
- em28xx_gpio_set(dev, hauppauge_hvr930c_end);
-
- msleep(100);
-
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(30);
-
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
- msleep(10);
-
-}
-
-static void terratec_h5_init(struct em28xx *dev)
-{
- int i;
- struct em28xx_reg_seq terratec_h5_init[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf2, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- { -1, -1, -1, -1},
- };
- struct em28xx_reg_seq terratec_h5_end[] = {
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- {EM2874_R80_GPIO, 0xa6, 0xff, 50},
- {EM2874_R80_GPIO, 0xe6, 0xff, 100},
- { -1, -1, -1, -1},
- };
- struct {
- unsigned char r[4];
- int len;
- } regs[] = {
- {{ 0x06, 0x02, 0x00, 0x31 }, 4},
- {{ 0x01, 0x02 }, 2},
- {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0xff, 0xaf }, 4},
- {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0x73, 0xaf }, 4},
- {{ 0x04, 0x00 }, 2},
- {{ 0x00, 0x04 }, 2},
- {{ 0x00, 0x04, 0x00, 0x0a }, 4},
- {{ 0x04, 0x14 }, 2},
- {{ 0x04, 0x14, 0x00, 0x00 }, 4},
- };
-
- em28xx_gpio_set(dev, terratec_h5_init);
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x45);
- msleep(10);
-
- dev->i2c_client.addr = 0x82 >> 1;
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
- em28xx_gpio_set(dev, terratec_h5_end);
-};
-
-static void terratec_htc_stick_init(struct em28xx *dev)
-{
- int i;
-
- /*
- * GPIO configuration:
- * 0xff: unknown (does not affect DVB-T).
- * 0xf6: DRX-K (demodulator).
- * 0xe6: unknown (does not affect DVB-T).
- * 0xb6: unknown (does not affect DVB-T).
- */
- struct em28xx_reg_seq terratec_htc_stick_init[] = {
- {EM28XX_R08_GPIO, 0xff, 0xff, 10},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- {EM2874_R80_GPIO, 0xe6, 0xff, 50},
- {EM2874_R80_GPIO, 0xf6, 0xff, 100},
- { -1, -1, -1, -1},
- };
- struct em28xx_reg_seq terratec_htc_stick_end[] = {
- {EM2874_R80_GPIO, 0xb6, 0xff, 100},
- {EM2874_R80_GPIO, 0xf6, 0xff, 50},
- { -1, -1, -1, -1},
- };
-
- /* Init the analog decoder? */
- struct {
- unsigned char r[4];
- int len;
- } regs[] = {
- {{ 0x06, 0x02, 0x00, 0x31 }, 4},
- {{ 0x01, 0x02 }, 2},
- {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0xff, 0xaf }, 4},
- };
-
- em28xx_gpio_set(dev, terratec_htc_stick_init);
-
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x40);
- msleep(10);
- em28xx_write_reg(dev, EM28XX_R06_I2C_CLK, 0x44);
- msleep(10);
-
- dev->i2c_client.addr = 0x82 >> 1;
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
-
- em28xx_gpio_set(dev, terratec_htc_stick_end);
-};
-
-static void pctv_520e_init(struct em28xx *dev)
-{
- /*
- * Init AVF4910B analog decoder. Looks like I2C traffic to
- * digital demodulator and tuner are routed via AVF4910B.
- */
- int i;
- struct {
- unsigned char r[4];
- int len;
- } regs[] = {
- {{ 0x06, 0x02, 0x00, 0x31 }, 4},
- {{ 0x01, 0x02 }, 2},
- {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0xff, 0xaf }, 4},
- {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
- {{ 0x01, 0x00 }, 2},
- {{ 0x01, 0x00, 0x73, 0xaf }, 4},
- };
-
- dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */
-
- for (i = 0; i < ARRAY_SIZE(regs); i++)
- i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
-};
-
-static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
-{
- /* Values extracted from a USB trace of the Terratec Windows driver */
- static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
- static u8 reset[] = { RESET, 0x80 };
- static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
- static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 };
- static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 };
- static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d };
- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
- static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 };
- static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 };
- static u8 tuner_go[] = { TUNER_GO, 0x01};
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg));
- mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
- mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg));
- mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg));
- mt352_write(fe, tuner_go, sizeof(tuner_go));
- return 0;
-}
-
-static struct mt352_config terratec_xs_mt352_cfg = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .if2 = 45600,
- .demod_init = em28xx_mt352_terratec_xs_init,
-};
-
-static struct tda10023_config em28xx_tda10023_config = {
- .demod_address = 0x0c,
- .invert = 1,
-};
-
-static struct cxd2820r_config em28xx_cxd2820r_config = {
- .i2c_address = (0xd8 >> 1),
- .ts_mode = CXD2820R_TS_SERIAL,
-
- /* enable LNA for DVB-T, DVB-T2 and DVB-C */
- .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
- .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
-};
-
-static struct tda18271_config em28xx_cxd2820r_tda18271_config = {
- .output_opt = TDA18271_OUTPUT_LT_OFF,
- .gate = TDA18271_GATE_DIGITAL,
-};
-
-static const struct tda10071_config em28xx_tda10071_config = {
- .i2c_address = 0x55, /* (0xaa >> 1) */
- .i2c_wr_max = 64,
- .ts_mode = TDA10071_TS_SERIAL,
- .spec_inv = 0,
- .xtal = 40444000, /* 40.444 MHz */
- .pll_multiplier = 20,
-};
-
-static const struct a8293_config em28xx_a8293_config = {
- .i2c_addr = 0x08, /* (0x10 >> 1) */
-};
-
-static struct zl10353_config em28xx_zl10353_no_i2c_gate_dev = {
- .demod_address = (0x1e >> 1),
- .disable_i2c_gate_ctrl = 1,
- .no_tuner = 1,
- .parallel_ts = 1,
-};
-static struct qt1010_config em28xx_qt1010_config = {
- .i2c_address = 0x62
-
-};
-
-/* ------------------------------------------------------------------ */
-
-static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev)
-{
- struct dvb_frontend *fe;
- struct xc2028_config cfg;
-
- memset(&cfg, 0, sizeof(cfg));
- cfg.i2c_adap = &dev->i2c_adap;
- cfg.i2c_addr = addr;
-
- if (!dev->dvb->fe[0]) {
- em28xx_errdev("/2: dvb frontend not attached. "
- "Can't attach xc3028\n");
- return -EINVAL;
- }
-
- fe = dvb_attach(xc2028_attach, dev->dvb->fe[0], &cfg);
- if (!fe) {
- em28xx_errdev("/2: xc3028 attach failed\n");
- dvb_frontend_detach(dev->dvb->fe[0]);
- dev->dvb->fe[0] = NULL;
- return -EINVAL;
- }
-
- em28xx_info("%s/2: xc3028 attached\n", dev->name);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int em28xx_register_dvb(struct em28xx_dvb *dvb, struct module *module,
- struct em28xx *dev, struct device *device)
-{
- int result;
-
- mutex_init(&dvb->lock);
-
- /* register adapter */
- result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
- adapter_nr);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
- dev->name, result);
- goto fail_adapter;
- }
-
- /* Ensure all frontends negotiate bus access */
- dvb->fe[0]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
- if (dvb->fe[1])
- dvb->fe[1]->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl;
-
- dvb->adapter.priv = dev;
-
- /* register frontend */
- result = dvb_register_frontend(&dvb->adapter, dvb->fe[0]);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
- dev->name, result);
- goto fail_frontend0;
- }
-
- /* register 2nd frontend */
- if (dvb->fe[1]) {
- result = dvb_register_frontend(&dvb->adapter, dvb->fe[1]);
- if (result < 0) {
- printk(KERN_WARNING "%s: 2nd dvb_register_frontend failed (errno = %d)\n",
- dev->name, result);
- goto fail_frontend1;
- }
- }
-
- /* register demux stuff */
- dvb->demux.dmx.capabilities =
- DMX_TS_FILTERING | DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
- dvb->demux.priv = dvb;
- dvb->demux.filternum = 256;
- dvb->demux.feednum = 256;
- dvb->demux.start_feed = em28xx_start_feed;
- dvb->demux.stop_feed = em28xx_stop_feed;
-
- result = dvb_dmx_init(&dvb->demux);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
- dev->name, result);
- goto fail_dmx;
- }
-
- dvb->dmxdev.filternum = 256;
- dvb->dmxdev.demux = &dvb->demux.dmx;
- dvb->dmxdev.capabilities = 0;
- result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
- dev->name, result);
- goto fail_dmxdev;
- }
-
- dvb->fe_hw.source = DMX_FRONTEND_0;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
- dev->name, result);
- goto fail_fe_hw;
- }
-
- dvb->fe_mem.source = DMX_MEMORY_FE;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
- dev->name, result);
- goto fail_fe_mem;
- }
-
- result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
- dev->name, result);
- goto fail_fe_conn;
- }
-
- /* register network adapter */
- dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
- return 0;
-
-fail_fe_conn:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-fail_fe_mem:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-fail_fe_hw:
- dvb_dmxdev_release(&dvb->dmxdev);
-fail_dmxdev:
- dvb_dmx_release(&dvb->demux);
-fail_dmx:
- if (dvb->fe[1])
- dvb_unregister_frontend(dvb->fe[1]);
- dvb_unregister_frontend(dvb->fe[0]);
-fail_frontend1:
- if (dvb->fe[1])
- dvb_frontend_detach(dvb->fe[1]);
-fail_frontend0:
- dvb_frontend_detach(dvb->fe[0]);
- dvb_unregister_adapter(&dvb->adapter);
-fail_adapter:
- return result;
-}
-
-static void em28xx_unregister_dvb(struct em28xx_dvb *dvb)
-{
- dvb_net_release(&dvb->net);
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- dvb_dmxdev_release(&dvb->dmxdev);
- dvb_dmx_release(&dvb->demux);
- if (dvb->fe[1])
- dvb_unregister_frontend(dvb->fe[1]);
- dvb_unregister_frontend(dvb->fe[0]);
- if (dvb->fe[1] && !dvb->dont_attach_fe1)
- dvb_frontend_detach(dvb->fe[1]);
- dvb_frontend_detach(dvb->fe[0]);
- dvb_unregister_adapter(&dvb->adapter);
-}
-
-static int em28xx_dvb_init(struct em28xx *dev)
-{
- int result = 0, mfe_shared = 0;
- struct em28xx_dvb *dvb;
-
- if (!dev->board.has_dvb) {
- /* This device does not support the extension */
- printk(KERN_INFO "em28xx_dvb: This device does not support the extension\n");
- return 0;
- }
-
- dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
-
- if (dvb == NULL) {
- em28xx_info("em28xx_dvb: memory allocation failed\n");
- return -ENOMEM;
- }
- dev->dvb = dvb;
- dvb->fe[0] = dvb->fe[1] = NULL;
-
- mutex_lock(&dev->lock);
- em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
- /* init frontend */
- switch (dev->model) {
- case EM2874_BOARD_LEADERSHIP_ISDBT:
- dvb->fe[0] = dvb_attach(s921_attach,
- &sharp_isdbt, &dev->i2c_adap);
-
- if (!dvb->fe[0]) {
- result = -EINVAL;
- goto out_free;
- }
-
- break;
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
- case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
- case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
- case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
- dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap);
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2880_BOARD_KWORLD_DVB_310U:
- dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_with_xc3028,
- &dev->i2c_adap);
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2882_BOARD_TERRATEC_HYBRID_XS:
- case EM2880_BOARD_EMPIRE_DUAL_TV:
- dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap);
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
- case EM2880_BOARD_TERRATEC_HYBRID_XS_FR:
- case EM2881_BOARD_PINNACLE_HYBRID_PRO:
- case EM2882_BOARD_DIKOM_DK300:
- case EM2882_BOARD_KWORLD_VS_DVBT:
- dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_xc3028_no_i2c_gate,
- &dev->i2c_adap);
- if (dvb->fe[0] == NULL) {
- /* This board could have either a zl10353 or a mt352.
- If the chip id isn't for zl10353, try mt352 */
- dvb->fe[0] = dvb_attach(mt352_attach,
- &terratec_xs_mt352_cfg,
- &dev->i2c_adap);
- }
-
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2870_BOARD_KWORLD_355U:
- dvb->fe[0] = dvb_attach(zl10353_attach,
- &em28xx_zl10353_no_i2c_gate_dev,
- &dev->i2c_adap);
- if (dvb->fe[0] != NULL)
- dvb_attach(qt1010_attach, dvb->fe[0],
- &dev->i2c_adap, &em28xx_qt1010_config);
- break;
- case EM2883_BOARD_KWORLD_HYBRID_330U:
- case EM2882_BOARD_EVGA_INDTUBE:
- dvb->fe[0] = dvb_attach(s5h1409_attach,
- &em28xx_s5h1409_with_xc3028,
- &dev->i2c_adap);
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2882_BOARD_KWORLD_ATSC_315U:
- dvb->fe[0] = dvb_attach(lgdt330x_attach,
- &em2880_lgdt3303_dev,
- &dev->i2c_adap);
- if (dvb->fe[0] != NULL) {
- if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) {
- result = -EINVAL;
- goto out_free;
- }
- }
- break;
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
- case EM2882_BOARD_PINNACLE_HYBRID_PRO_330E:
- dvb->fe[0] = dvb_attach(drxd_attach, &em28xx_drxd, NULL,
- &dev->i2c_adap, &dev->udev->dev);
- if (em28xx_attach_xc3028(0x61, dev) < 0) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
- /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
- dvb->fe[0] = dvb_attach(tda10023_attach,
- &em28xx_tda10023_config,
- &dev->i2c_adap, 0x48);
- if (dvb->fe[0]) {
- if (!dvb_attach(simple_tuner_attach, dvb->fe[0],
- &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
- result = -EINVAL;
- goto out_free;
- }
- }
- break;
- case EM2870_BOARD_KWORLD_A340:
- dvb->fe[0] = dvb_attach(lgdt3305_attach,
- &em2870_lgdt3304_dev,
- &dev->i2c_adap);
- if (dvb->fe[0] != NULL)
- dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- &dev->i2c_adap, &kworld_a340_config);
- break;
- case EM28174_BOARD_PCTV_290E:
- dvb->fe[0] = dvb_attach(cxd2820r_attach,
- &em28xx_cxd2820r_config,
- &dev->i2c_adap);
- if (dvb->fe[0]) {
- /* FE 0 attach tuner */
- if (!dvb_attach(tda18271_attach,
- dvb->fe[0],
- 0x60,
- &dev->i2c_adap,
- &em28xx_cxd2820r_tda18271_config)) {
-
- dvb_frontend_detach(dvb->fe[0]);
- result = -EINVAL;
- goto out_free;
- }
- }
- break;
- case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C:
- {
- struct xc5000_config cfg;
- hauppauge_hvr930c_init(dev);
-
- dvb->fe[0] = dvb_attach(drxk_attach,
- &hauppauge_930c_drxk, &dev->i2c_adap);
- if (!dvb->fe[0]) {
- result = -EINVAL;
- goto out_free;
- }
- /* FIXME: do we need a pll semaphore? */
- dvb->fe[0]->sec_priv = dvb;
- sema_init(&dvb->pll_mutex, 1);
- dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
- dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
-
- /* Attach xc5000 */
- memset(&cfg, 0, sizeof(cfg));
- cfg.i2c_address = 0x61;
- cfg.if_khz = 4000;
-
- if (dvb->fe[0]->ops.i2c_gate_ctrl)
- dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
- if (!dvb_attach(xc5000_attach, dvb->fe[0], &dev->i2c_adap,
- &cfg)) {
- result = -EINVAL;
- goto out_free;
- }
- if (dvb->fe[0]->ops.i2c_gate_ctrl)
- dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
-
- break;
- }
- case EM2884_BOARD_TERRATEC_H5:
- terratec_h5_init(dev);
-
- dvb->fe[0] = dvb_attach(drxk_attach, &terratec_h5_drxk, &dev->i2c_adap);
- if (!dvb->fe[0]) {
- result = -EINVAL;
- goto out_free;
- }
- /* FIXME: do we need a pll semaphore? */
- dvb->fe[0]->sec_priv = dvb;
- sema_init(&dvb->pll_mutex, 1);
- dvb->gate_ctrl = dvb->fe[0]->ops.i2c_gate_ctrl;
- dvb->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl;
-
- /* Attach tda18271 to DVB-C frontend */
- if (dvb->fe[0]->ops.i2c_gate_ctrl)
- dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 1);
- if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0], &dev->i2c_adap, 0x60)) {
- result = -EINVAL;
- goto out_free;
- }
- if (dvb->fe[0]->ops.i2c_gate_ctrl)
- dvb->fe[0]->ops.i2c_gate_ctrl(dvb->fe[0], 0);
-
- break;
- case EM28174_BOARD_PCTV_460E:
- /* attach demod */
- dvb->fe[0] = dvb_attach(tda10071_attach,
- &em28xx_tda10071_config, &dev->i2c_adap);
-
- /* attach SEC */
- if (dvb->fe[0])
- dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
- &em28xx_a8293_config);
- break;
- case EM2874_BOARD_MAXMEDIA_UB425_TC:
- /* attach demodulator */
- dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
- &dev->i2c_adap);
-
- if (dvb->fe[0]) {
- /* disable I2C-gate */
- dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
-
- /* attach tuner */
- if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
- &dev->i2c_adap, 0x60)) {
- dvb_frontend_detach(dvb->fe[0]);
- result = -EINVAL;
- goto out_free;
- }
- }
-
- /* TODO: we need drx-3913k firmware in order to support DVB-T */
- em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
- "driver version\n");
-
- break;
- case EM2884_BOARD_PCTV_510E:
- case EM2884_BOARD_PCTV_520E:
- pctv_520e_init(dev);
-
- /* attach demodulator */
- dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
- &dev->i2c_adap);
-
- if (dvb->fe[0]) {
- /* attach tuner */
- if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- &dev->i2c_adap,
- &em28xx_cxd2820r_tda18271_config)) {
- dvb_frontend_detach(dvb->fe[0]);
- result = -EINVAL;
- goto out_free;
- }
- }
- break;
- case EM2884_BOARD_CINERGY_HTC_STICK:
- terratec_htc_stick_init(dev);
-
- /* attach demodulator */
- dvb->fe[0] = dvb_attach(drxk_attach, &terratec_htc_stick_drxk,
- &dev->i2c_adap);
- if (!dvb->fe[0]) {
- result = -EINVAL;
- goto out_free;
- }
-
- /* Attach the demodulator. */
- if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
- &dev->i2c_adap,
- &em28xx_cxd2820r_tda18271_config)) {
- result = -EINVAL;
- goto out_free;
- }
- break;
- default:
- em28xx_errdev("/2: The frontend of your DVB/ATSC card"
- " isn't supported yet\n");
- break;
- }
- if (NULL == dvb->fe[0]) {
- em28xx_errdev("/2: frontend initialization failed\n");
- result = -EINVAL;
- goto out_free;
- }
- /* define general-purpose callback pointer */
- dvb->fe[0]->callback = em28xx_tuner_callback;
- if (dvb->fe[1])
- dvb->fe[1]->callback = em28xx_tuner_callback;
-
- /* register everything */
- result = em28xx_register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
-
- if (result < 0)
- goto out_free;
-
- /* MFE lock */
- dvb->adapter.mfe_shared = mfe_shared;
-
- em28xx_info("Successfully loaded em28xx-dvb\n");
-ret:
- em28xx_set_mode(dev, EM28XX_SUSPEND);
- mutex_unlock(&dev->lock);
- return result;
-
-out_free:
- kfree(dvb);
- dev->dvb = NULL;
- goto ret;
-}
-
-static inline void prevent_sleep(struct dvb_frontend_ops *ops)
-{
- ops->set_voltage = NULL;
- ops->sleep = NULL;
- ops->tuner_ops.sleep = NULL;
-}
-
-static int em28xx_dvb_fini(struct em28xx *dev)
-{
- if (!dev->board.has_dvb) {
- /* This device does not support the extension */
- return 0;
- }
-
- if (dev->dvb) {
- struct em28xx_dvb *dvb = dev->dvb;
-
- if (dev->state & DEV_DISCONNECTED) {
- /* We cannot tell the device to sleep
- * once it has been unplugged. */
- if (dvb->fe[0])
- prevent_sleep(&dvb->fe[0]->ops);
- if (dvb->fe[1])
- prevent_sleep(&dvb->fe[1]->ops);
- }
-
- em28xx_unregister_dvb(dvb);
- kfree(dvb);
- dev->dvb = NULL;
- }
-
- return 0;
-}
-
-static struct em28xx_ops dvb_ops = {
- .id = EM28XX_DVB,
- .name = "Em28xx dvb Extension",
- .init = em28xx_dvb_init,
- .fini = em28xx_dvb_fini,
-};
-
-static int __init em28xx_dvb_register(void)
-{
- return em28xx_register_extension(&dvb_ops);
-}
-
-static void __exit em28xx_dvb_unregister(void)
-{
- em28xx_unregister_extension(&dvb_ops);
-}
-
-module_init(em28xx_dvb_register);
-module_exit(em28xx_dvb_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
deleted file mode 100644
index 50f5f4fc2148..000000000000
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ /dev/null
@@ -1,2606 +0,0 @@
-/*
- em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB
- video capture devices
-
- Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
- Markus Rechberger <mrechberger@gmail.com>
- Mauro Carvalho Chehab <mchehab@infradead.org>
- Sascha Sommer <saschasommer@freenet.de>
-
- Some parts based on SN9C10x PC Camera Controllers GPL driver made
- by Luca Risolia <luca.risolia@studio.unibo.it>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitmap.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-
-#include "em28xx.h"
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/msp3400.h>
-#include <media/tuner.h>
-
-#define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
- "Markus Rechberger <mrechberger@gmail.com>, " \
- "Mauro Carvalho Chehab <mchehab@infradead.org>, " \
- "Sascha Sommer <saschasommer@freenet.de>"
-
-#define DRIVER_DESC "Empia em28xx based USB video device driver"
-
-#define EM28XX_VERSION "0.1.3"
-
-#define em28xx_videodbg(fmt, arg...) do {\
- if (video_debug) \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); } while (0)
-
-static unsigned int isoc_debug;
-module_param(isoc_debug, int, 0644);
-MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
-
-#define em28xx_isocdbg(fmt, arg...) \
-do {\
- if (isoc_debug) { \
- printk(KERN_INFO "%s %s :"fmt, \
- dev->name, __func__ , ##arg); \
- } \
- } while (0)
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(EM28XX_VERSION);
-
-static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
-
-module_param_array(video_nr, int, NULL, 0444);
-module_param_array(vbi_nr, int, NULL, 0444);
-module_param_array(radio_nr, int, NULL, 0444);
-MODULE_PARM_DESC(video_nr, "video device numbers");
-MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
-MODULE_PARM_DESC(radio_nr, "radio device numbers");
-
-static unsigned int video_debug;
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
-
-/* supported video standards */
-static struct em28xx_fmt format[] = {
- {
- .name = "16 bpp YUY2, 4:2:2, packed",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
- }, {
- .name = "16 bpp RGB 565, LE",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- .reg = EM28XX_OUTFMT_RGB_16_656,
- }, {
- .name = "8 bpp Bayer BGBG..GRGR",
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .depth = 8,
- .reg = EM28XX_OUTFMT_RGB_8_BGBG,
- }, {
- .name = "8 bpp Bayer GRGR..BGBG",
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .depth = 8,
- .reg = EM28XX_OUTFMT_RGB_8_GRGR,
- }, {
- .name = "8 bpp Bayer GBGB..RGRG",
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .depth = 8,
- .reg = EM28XX_OUTFMT_RGB_8_GBGB,
- }, {
- .name = "12 bpp YUV411",
- .fourcc = V4L2_PIX_FMT_YUV411P,
- .depth = 12,
- .reg = EM28XX_OUTFMT_YUV411,
- },
-};
-
-/* supported controls */
-/* Common to all boards */
-static struct v4l2_queryctrl ac97_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0x0,
- .maximum = 0x1f,
- .step = 0x1,
- .default_value = 0x1f,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- }, {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- .flags = 0,
- }
-};
-
-/* ------------------------------------------------------------------
- DMA and thread functions
- ------------------------------------------------------------------*/
-
-/*
- * Announces that a buffer were filled and request the next
- */
-static inline void buffer_filled(struct em28xx *dev,
- struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer *buf)
-{
- /* Advice that buffer was filled */
- em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- dev->isoc_ctl.vid_buf = NULL;
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-static inline void vbi_buffer_filled(struct em28xx *dev,
- struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer *buf)
-{
- /* Advice that buffer was filled */
- em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
-
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- dev->isoc_ctl.vbi_buf = NULL;
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-/*
- * Identify the buffer header type and properly handles
- */
-static void em28xx_copy_video(struct em28xx *dev,
- struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer *buf,
- unsigned char *p,
- unsigned char *outp, unsigned long len)
-{
- void *fieldstart, *startwrite, *startread;
- int linesdone, currlinedone, offset, lencopy, remain;
- int bytesperline = dev->width << 1;
-
- if (dma_q->pos + len > buf->vb.size)
- len = buf->vb.size - dma_q->pos;
-
- startread = p;
- remain = len;
-
- if (dev->progressive)
- fieldstart = outp;
- else {
- /* Interlaces two half frames */
- if (buf->top_field)
- fieldstart = outp;
- else
- fieldstart = outp + bytesperline;
- }
-
- linesdone = dma_q->pos / bytesperline;
- currlinedone = dma_q->pos % bytesperline;
-
- if (dev->progressive)
- offset = linesdone * bytesperline + currlinedone;
- else
- offset = linesdone * bytesperline * 2 + currlinedone;
-
- startwrite = fieldstart + offset;
- lencopy = bytesperline - currlinedone;
- lencopy = lencopy > remain ? remain : lencopy;
-
- if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
- em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
- ((char *)startwrite + lencopy) -
- ((char *)outp + buf->vb.size));
- remain = (char *)outp + buf->vb.size - (char *)startwrite;
- lencopy = remain;
- }
- if (lencopy <= 0)
- return;
- memcpy(startwrite, startread, lencopy);
-
- remain -= lencopy;
-
- while (remain > 0) {
- startwrite += lencopy + bytesperline;
- startread += lencopy;
- if (bytesperline > remain)
- lencopy = remain;
- else
- lencopy = bytesperline;
-
- if ((char *)startwrite + lencopy > (char *)outp +
- buf->vb.size) {
- em28xx_isocdbg("Overflow of %zi bytes past buffer end"
- "(2)\n",
- ((char *)startwrite + lencopy) -
- ((char *)outp + buf->vb.size));
- lencopy = remain = (char *)outp + buf->vb.size -
- (char *)startwrite;
- }
- if (lencopy <= 0)
- break;
-
- memcpy(startwrite, startread, lencopy);
-
- remain -= lencopy;
- }
-
- dma_q->pos += len;
-}
-
-static void em28xx_copy_vbi(struct em28xx *dev,
- struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer *buf,
- unsigned char *p,
- unsigned char *outp, unsigned long len)
-{
- void *startwrite, *startread;
- int offset;
- int bytesperline;
-
- if (dev == NULL) {
- em28xx_isocdbg("dev is null\n");
- return;
- }
- bytesperline = dev->vbi_width;
-
- if (dma_q == NULL) {
- em28xx_isocdbg("dma_q is null\n");
- return;
- }
- if (buf == NULL) {
- return;
- }
- if (p == NULL) {
- em28xx_isocdbg("p is null\n");
- return;
- }
- if (outp == NULL) {
- em28xx_isocdbg("outp is null\n");
- return;
- }
-
- if (dma_q->pos + len > buf->vb.size)
- len = buf->vb.size - dma_q->pos;
-
- startread = p;
-
- startwrite = outp + dma_q->pos;
- offset = dma_q->pos;
-
- /* Make sure the bottom field populates the second half of the frame */
- if (buf->top_field == 0) {
- startwrite += bytesperline * dev->vbi_height;
- offset += bytesperline * dev->vbi_height;
- }
-
- memcpy(startwrite, startread, len);
- dma_q->pos += len;
-}
-
-static inline void print_err_status(struct em28xx *dev,
- int packet, int status)
-{
- char *errmsg = "Unknown";
-
- switch (status) {
- case -ENOENT:
- errmsg = "unlinked synchronuously";
- break;
- case -ECONNRESET:
- errmsg = "unlinked asynchronuously";
- break;
- case -ENOSR:
- errmsg = "Buffer error (overrun)";
- break;
- case -EPIPE:
- errmsg = "Stalled (device not responding)";
- break;
- case -EOVERFLOW:
- errmsg = "Babble (bad cable?)";
- break;
- case -EPROTO:
- errmsg = "Bit-stuff error (bad cable?)";
- break;
- case -EILSEQ:
- errmsg = "CRC/Timeout (could be anything)";
- break;
- case -ETIME:
- errmsg = "Device does not respond";
- break;
- }
- if (packet < 0) {
- em28xx_isocdbg("URB status %d [%s].\n", status, errmsg);
- } else {
- em28xx_isocdbg("URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
- }
-}
-
-/*
- * video-buf generic routine to get the next available buffer
- */
-static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer **buf)
-{
- struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
- char *outp;
-
- if (list_empty(&dma_q->active)) {
- em28xx_isocdbg("No active queue to serve\n");
- dev->isoc_ctl.vid_buf = NULL;
- *buf = NULL;
- return;
- }
-
- /* Get the next buffer */
- *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
-
- /* Cleans up buffer - Useful for testing for frame/URB loss */
- outp = videobuf_to_vmalloc(&(*buf)->vb);
- memset(outp, 0, (*buf)->vb.size);
-
- dev->isoc_ctl.vid_buf = *buf;
-
- return;
-}
-
-/*
- * video-buf generic routine to get the next available VBI buffer
- */
-static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
- struct em28xx_buffer **buf)
-{
- struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
- char *outp;
-
- if (list_empty(&dma_q->active)) {
- em28xx_isocdbg("No active queue to serve\n");
- dev->isoc_ctl.vbi_buf = NULL;
- *buf = NULL;
- return;
- }
-
- /* Get the next buffer */
- *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
- /* Cleans up buffer - Useful for testing for frame/URB loss */
- outp = videobuf_to_vmalloc(&(*buf)->vb);
- memset(outp, 0x00, (*buf)->vb.size);
-
- dev->isoc_ctl.vbi_buf = *buf;
-
- return;
-}
-
-/*
- * Controls the isoc copy of each urb packet
- */
-static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
-{
- struct em28xx_buffer *buf;
- struct em28xx_dmaqueue *dma_q = &dev->vidq;
- unsigned char *outp = NULL;
- int i, len = 0, rc = 1;
- unsigned char *p;
-
- if (!dev)
- return 0;
-
- if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- buf = dev->isoc_ctl.vid_buf;
- if (buf != NULL)
- outp = videobuf_to_vmalloc(&buf->vb);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- if (urb->iso_frame_desc[i].status != -EPROTO)
- continue;
- }
-
- len = urb->iso_frame_desc[i].actual_length - 4;
-
- if (urb->iso_frame_desc[i].actual_length <= 0) {
- /* em28xx_isocdbg("packet %d is empty",i); - spammy */
- continue;
- }
- if (urb->iso_frame_desc[i].actual_length >
- dev->max_pkt_size) {
- em28xx_isocdbg("packet bigger than packet size");
- continue;
- }
-
- p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-
- /* FIXME: incomplete buffer checks where removed to make
- logic simpler. Impacts of those changes should be evaluated
- */
- if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) {
- em28xx_isocdbg("VBI HEADER!!!\n");
- /* FIXME: Should add vbi copy */
- continue;
- }
- if (p[0] == 0x22 && p[1] == 0x5a) {
- em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
- len, (p[2] & 1) ? "odd" : "even");
-
- if (dev->progressive || !(p[2] & 1)) {
- if (buf != NULL)
- buffer_filled(dev, dma_q, buf);
- get_next_buf(dma_q, &buf);
- if (buf == NULL)
- outp = NULL;
- else
- outp = videobuf_to_vmalloc(&buf->vb);
- }
-
- if (buf != NULL) {
- if (p[2] & 1)
- buf->top_field = 0;
- else
- buf->top_field = 1;
- }
-
- dma_q->pos = 0;
- }
- if (buf != NULL) {
- if (p[0] != 0x88 && p[0] != 0x22) {
- em28xx_isocdbg("frame is not complete\n");
- len += 4;
- } else {
- p += 4;
- }
- em28xx_copy_video(dev, dma_q, buf, p, outp, len);
- }
- }
- return rc;
-}
-
-/* Version of isoc handler that takes into account a mixture of video and
- VBI data */
-static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
-{
- struct em28xx_buffer *buf, *vbi_buf;
- struct em28xx_dmaqueue *dma_q = &dev->vidq;
- struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
- unsigned char *outp = NULL;
- unsigned char *vbioutp = NULL;
- int i, len = 0, rc = 1;
- unsigned char *p;
- int vbi_size;
-
- if (!dev)
- return 0;
-
- if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
- return 0;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- if (urb->status == -ENOENT)
- return 0;
- }
-
- buf = dev->isoc_ctl.vid_buf;
- if (buf != NULL)
- outp = videobuf_to_vmalloc(&buf->vb);
-
- vbi_buf = dev->isoc_ctl.vbi_buf;
- if (vbi_buf != NULL)
- vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
-
- for (i = 0; i < urb->number_of_packets; i++) {
- int status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- if (urb->iso_frame_desc[i].status != -EPROTO)
- continue;
- }
-
- len = urb->iso_frame_desc[i].actual_length;
- if (urb->iso_frame_desc[i].actual_length <= 0) {
- /* em28xx_isocdbg("packet %d is empty",i); - spammy */
- continue;
- }
- if (urb->iso_frame_desc[i].actual_length >
- dev->max_pkt_size) {
- em28xx_isocdbg("packet bigger than packet size");
- continue;
- }
-
- p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-
- /* capture type 0 = vbi start
- capture type 1 = video start
- capture type 2 = video in progress */
- if (p[0] == 0x33 && p[1] == 0x95) {
- dev->capture_type = 0;
- dev->vbi_read = 0;
- em28xx_isocdbg("VBI START HEADER!!!\n");
- dev->cur_field = p[2];
- p += 4;
- len -= 4;
- } else if (p[0] == 0x88 && p[1] == 0x88 &&
- p[2] == 0x88 && p[3] == 0x88) {
- /* continuation */
- p += 4;
- len -= 4;
- } else if (p[0] == 0x22 && p[1] == 0x5a) {
- /* start video */
- p += 4;
- len -= 4;
- }
-
- vbi_size = dev->vbi_width * dev->vbi_height;
-
- if (dev->capture_type == 0) {
- if (dev->vbi_read >= vbi_size) {
- /* We've already read all the VBI data, so
- treat the rest as video */
- em28xx_isocdbg("dev->vbi_read > vbi_size\n");
- } else if ((dev->vbi_read + len) < vbi_size) {
- /* This entire frame is VBI data */
- if (dev->vbi_read == 0 &&
- (!(dev->cur_field & 1))) {
- /* Brand new frame */
- if (vbi_buf != NULL)
- vbi_buffer_filled(dev,
- vbi_dma_q,
- vbi_buf);
- vbi_get_next_buf(vbi_dma_q, &vbi_buf);
- if (vbi_buf == NULL)
- vbioutp = NULL;
- else
- vbioutp = videobuf_to_vmalloc(
- &vbi_buf->vb);
- }
-
- if (dev->vbi_read == 0) {
- vbi_dma_q->pos = 0;
- if (vbi_buf != NULL) {
- if (dev->cur_field & 1)
- vbi_buf->top_field = 0;
- else
- vbi_buf->top_field = 1;
- }
- }
-
- dev->vbi_read += len;
- em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
- vbioutp, len);
- } else {
- /* Some of this frame is VBI data and some is
- video data */
- int vbi_data_len = vbi_size - dev->vbi_read;
- dev->vbi_read += vbi_data_len;
- em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
- vbioutp, vbi_data_len);
- dev->capture_type = 1;
- p += vbi_data_len;
- len -= vbi_data_len;
- }
- }
-
- if (dev->capture_type == 1) {
- dev->capture_type = 2;
- if (dev->progressive || !(dev->cur_field & 1)) {
- if (buf != NULL)
- buffer_filled(dev, dma_q, buf);
- get_next_buf(dma_q, &buf);
- if (buf == NULL)
- outp = NULL;
- else
- outp = videobuf_to_vmalloc(&buf->vb);
- }
- if (buf != NULL) {
- if (dev->cur_field & 1)
- buf->top_field = 0;
- else
- buf->top_field = 1;
- }
-
- dma_q->pos = 0;
- }
-
- if (buf != NULL && dev->capture_type == 2) {
- if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 &&
- p[2] == 0x88 && p[3] == 0x88) {
- p += 4;
- len -= 4;
- }
- if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) {
- em28xx_isocdbg("Video frame %d, len=%i, %s\n",
- p[2], len, (p[2] & 1) ?
- "odd" : "even");
- p += 4;
- len -= 4;
- }
-
- if (len > 0)
- em28xx_copy_video(dev, dma_q, buf, p, outp,
- len);
- }
- }
- return rc;
-}
-
-
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
-{
- struct em28xx_fh *fh = vq->priv_data;
- struct em28xx *dev = fh->dev;
- struct v4l2_frequency f;
-
- *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
- >> 3;
-
- if (0 == *count)
- *count = EM28XX_DEF_BUF;
-
- if (*count < EM28XX_MIN_BUF)
- *count = EM28XX_MIN_BUF;
-
- /* Ask tuner to go to analog or radio mode */
- memset(&f, 0, sizeof(f));
- f.frequency = dev->ctl_freq;
- f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f);
-
- return 0;
-}
-
-/* This is called *without* dev->slock held; please keep it that way */
-static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
-{
- struct em28xx_fh *fh = vq->priv_data;
- struct em28xx *dev = fh->dev;
- unsigned long flags = 0;
- if (in_interrupt())
- BUG();
-
- /* We used to wait for the buffer to finish here, but this didn't work
- because, as we were keeping the state as VIDEOBUF_QUEUED,
- videobuf_queue_cancel marked it as finished for us.
- (Also, it could wedge forever if the hardware was misconfigured.)
-
- This should be safe; by the time we get here, the buffer isn't
- queued anymore. If we ever start marking the buffers as
- VIDEOBUF_ACTIVE, it won't be, though.
- */
- spin_lock_irqsave(&dev->slock, flags);
- if (dev->isoc_ctl.vid_buf == buf)
- dev->isoc_ctl.vid_buf = NULL;
- spin_unlock_irqrestore(&dev->slock, flags);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct em28xx_fh *fh = vq->priv_data;
- struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
- struct em28xx *dev = fh->dev;
- int rc = 0, urb_init = 0;
-
- buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
- + 7) >> 3;
-
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- buf->vb.width = dev->width;
- buf->vb.height = dev->height;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
-
- if (!dev->isoc_ctl.analog_bufs.num_bufs)
- urb_init = 1;
-
- if (urb_init) {
- if (em28xx_vbi_supported(dev) == 1)
- rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
- EM28XX_NUM_PACKETS,
- EM28XX_NUM_BUFS,
- dev->max_pkt_size,
- em28xx_isoc_copy_vbi);
- else
- rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
- EM28XX_NUM_PACKETS,
- EM28XX_NUM_BUFS,
- dev->max_pkt_size,
- em28xx_isoc_copy);
- if (rc < 0)
- goto fail;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct em28xx_buffer *buf = container_of(vb,
- struct em28xx_buffer,
- vb);
- struct em28xx_fh *fh = vq->priv_data;
- struct em28xx *dev = fh->dev;
- struct em28xx_dmaqueue *vidq = &dev->vidq;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct em28xx_buffer *buf = container_of(vb,
- struct em28xx_buffer,
- vb);
- struct em28xx_fh *fh = vq->priv_data;
- struct em28xx *dev = (struct em28xx *)fh->dev;
-
- em28xx_isocdbg("em28xx: called buffer_release\n");
-
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops em28xx_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/********************* v4l2 interface **************************************/
-
-static void video_mux(struct em28xx *dev, int index)
-{
- dev->ctl_input = index;
- dev->ctl_ainput = INPUT(index)->amux;
- dev->ctl_aoutput = INPUT(index)->aout;
-
- if (!dev->ctl_aoutput)
- dev->ctl_aoutput = EM28XX_AOUT_MASTER;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing,
- INPUT(index)->vmux, 0, 0);
-
- if (dev->board.has_msp34xx) {
- if (dev->i2s_speed) {
- v4l2_device_call_all(&dev->v4l2_dev, 0, audio,
- s_i2s_clock_freq, dev->i2s_speed);
- }
- /* Note: this is msp3400 specific */
- v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
- dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0);
- }
-
- if (dev->board.adecoder != EM28XX_NOADECODER) {
- v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing,
- dev->ctl_ainput, dev->ctl_aoutput, 0);
- }
-
- em28xx_audio_analog_set(dev);
-}
-
-/* Usage lock check functions */
-static int res_get(struct em28xx_fh *fh, unsigned int bit)
-{
- struct em28xx *dev = fh->dev;
-
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- if (dev->resources & bit) {
- /* no, someone else uses it */
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- em28xx_videodbg("res: get %d\n", bit);
- return 1;
-}
-
-static int res_check(struct em28xx_fh *fh, unsigned int bit)
-{
- return fh->resources & bit;
-}
-
-static int res_locked(struct em28xx *dev, unsigned int bit)
-{
- return dev->resources & bit;
-}
-
-static void res_free(struct em28xx_fh *fh, unsigned int bits)
-{
- struct em28xx *dev = fh->dev;
-
- BUG_ON((fh->resources & bits) != bits);
-
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- em28xx_videodbg("res: put %d\n", bits);
-}
-
-static int get_ressource(struct em28xx_fh *fh)
-{
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return EM28XX_RESOURCE_VIDEO;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- return EM28XX_RESOURCE_VBI;
- default:
- BUG();
- return 0;
- }
-}
-
-/*
- * ac97_queryctrl()
- * return the ac97 supported controls
- */
-static int ac97_queryctrl(struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
- if (qc->id && qc->id == ac97_qctrl[i].id) {
- memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
-
- /* Control is not ac97 related */
- return 1;
-}
-
-/*
- * ac97_get_ctrl()
- * return the current values for ac97 mute and volume
- */
-static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
-{
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->mute;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->volume;
- return 0;
- default:
- /* Control is not ac97 related */
- return 1;
- }
-}
-
-/*
- * ac97_set_ctrl()
- * set values for ac97 mute and volume
- */
-static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
- if (ctrl->id == ac97_qctrl[i].id)
- goto handle;
-
- /* Announce that hasn't handle it */
- return 1;
-
-handle:
- if (ctrl->value < ac97_qctrl[i].minimum ||
- ctrl->value > ac97_qctrl[i].maximum)
- return -ERANGE;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- dev->mute = ctrl->value;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- dev->volume = ctrl->value;
- break;
- }
-
- return em28xx_audio_analog_set(dev);
-}
-
-static int check_dev(struct em28xx *dev)
-{
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_errdev("v4l2 ioctl: device not present\n");
- return -ENODEV;
- }
-
- if (dev->state & DEV_MISCONFIGURED) {
- em28xx_errdev("v4l2 ioctl: device is misconfigured; "
- "close and open it again\n");
- return -EIO;
- }
- return 0;
-}
-
-static void get_scale(struct em28xx *dev,
- unsigned int width, unsigned int height,
- unsigned int *hscale, unsigned int *vscale)
-{
- unsigned int maxw = norm_maxw(dev);
- unsigned int maxh = norm_maxh(dev);
-
- *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
- if (*hscale >= 0x4000)
- *hscale = 0x3fff;
-
- *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
- if (*vscale >= 0x4000)
- *vscale = 0x3fff;
-}
-
-/* ------------------------------------------------------------------
- IOCTL vidioc handling
- ------------------------------------------------------------------*/
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.pixelformat = dev->format->fourcc;
- f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
- if (dev->progressive)
- f->fmt.pix.field = V4L2_FIELD_NONE;
- else
- f->fmt.pix.field = dev->interlaced ?
- V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
- return 0;
-}
-
-static struct em28xx_fmt *format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(format); i++)
- if (format[i].fourcc == fourcc)
- return &format[i];
-
- return NULL;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- unsigned int width = f->fmt.pix.width;
- unsigned int height = f->fmt.pix.height;
- unsigned int maxw = norm_maxw(dev);
- unsigned int maxh = norm_maxh(dev);
- unsigned int hscale, vscale;
- struct em28xx_fmt *fmt;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt) {
- em28xx_videodbg("Fourcc format (%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- if (dev->board.is_em2800) {
- /* the em2800 can only scale down to 50% */
- height = height > (3 * maxh / 4) ? maxh : maxh / 2;
- width = width > (3 * maxw / 4) ? maxw : maxw / 2;
- /* MaxPacketSize for em2800 is too small to capture at full resolution
- * use half of maxw as the scaler can only scale to 50% */
- if (width == maxw && height == maxh)
- width /= 2;
- } else {
- /* width must even because of the YUYV format
- height must be even because of interlacing */
- v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
- 1, 0);
- }
-
- get_scale(dev, width, height, &hscale, &vscale);
-
- width = (((unsigned long)maxw) << 12) / (hscale + 4096L);
- height = (((unsigned long)maxh) << 12) / (vscale + 4096L);
-
- f->fmt.pix.width = width;
- f->fmt.pix.height = height;
- f->fmt.pix.pixelformat = fmt->fourcc;
- f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- if (dev->progressive)
- f->fmt.pix.field = V4L2_FIELD_NONE;
- else
- f->fmt.pix.field = dev->interlaced ?
- V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
-
- return 0;
-}
-
-static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
- unsigned width, unsigned height)
-{
- struct em28xx_fmt *fmt;
-
- fmt = format_by_fourcc(fourcc);
- if (!fmt)
- return -EINVAL;
-
- dev->format = fmt;
- dev->width = width;
- dev->height = height;
-
- /* set new image size */
- get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
-
- em28xx_set_alternate(dev);
- em28xx_resolution_set(dev);
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- vidioc_try_fmt_vid_cap(file, priv, f);
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- em28xx_errdev("%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- return em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
- f->fmt.pix.width, f->fmt.pix.height);
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- *norm = dev->norm;
-
- return 0;
-}
-
-static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *norm)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm);
-
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- struct v4l2_format f;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- dev->norm = *norm;
-
- /* Adjusts width/height, if needed */
- f.fmt.pix.width = dev->width;
- f.fmt.pix.height = dev->height;
- vidioc_try_fmt_vid_cap(file, priv, &f);
-
- /* set new image size */
- dev->width = f.fmt.pix.width;
- dev->height = f.fmt.pix.height;
- get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
-
- em28xx_resolution_set(dev);
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
-
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *p)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc = 0;
-
- if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (dev->board.is_webcam)
- rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0,
- video, g_parm, p);
- else
- v4l2_video_std_frame_period(dev->norm,
- &p->parm.capture.timeperframe);
-
- return rc;
-}
-
-static int vidioc_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *p)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- if (!dev->board.is_webcam)
- return -EINVAL;
-
- if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p);
-}
-
-static const char *iname[] = {
- [EM28XX_VMUX_COMPOSITE1] = "Composite1",
- [EM28XX_VMUX_COMPOSITE2] = "Composite2",
- [EM28XX_VMUX_COMPOSITE3] = "Composite3",
- [EM28XX_VMUX_COMPOSITE4] = "Composite4",
- [EM28XX_VMUX_SVIDEO] = "S-Video",
- [EM28XX_VMUX_TELEVISION] = "Television",
- [EM28XX_VMUX_CABLE] = "Cable TV",
- [EM28XX_VMUX_DVB] = "DVB",
- [EM28XX_VMUX_DEBUG] = "for debug only",
-};
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- unsigned int n;
-
- n = i->index;
- if (n >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(n)->type)
- return -EINVAL;
-
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- strcpy(i->name, iname[INPUT(n)->type]);
-
- if ((EM28XX_VMUX_TELEVISION == INPUT(n)->type) ||
- (EM28XX_VMUX_CABLE == INPUT(n)->type))
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- i->std = dev->vdev->tvnorms;
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- *i = dev->ctl_input;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (i >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(i)->type)
- return -EINVAL;
-
- video_mux(dev, i);
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- if (!dev->audio_mode.has_audio)
- return -EINVAL;
-
- switch (a->index) {
- case EM28XX_AMUX_VIDEO:
- strcpy(a->name, "Television");
- break;
- case EM28XX_AMUX_LINE_IN:
- strcpy(a->name, "Line In");
- break;
- case EM28XX_AMUX_VIDEO2:
- strcpy(a->name, "Television alt");
- break;
- case EM28XX_AMUX_PHONE:
- strcpy(a->name, "Phone");
- break;
- case EM28XX_AMUX_MIC:
- strcpy(a->name, "Mic");
- break;
- case EM28XX_AMUX_CD:
- strcpy(a->name, "CD");
- break;
- case EM28XX_AMUX_AUX:
- strcpy(a->name, "Aux");
- break;
- case EM28XX_AMUX_PCM_OUT:
- strcpy(a->name, "PCM");
- break;
- default:
- return -EINVAL;
- }
-
- a->index = dev->ctl_ainput;
- a->capability = V4L2_AUDCAP_STEREO;
-
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
-
- if (!dev->audio_mode.has_audio)
- return -EINVAL;
-
- if (a->index >= MAX_EM28XX_INPUT)
- return -EINVAL;
- if (0 == INPUT(a->index)->type)
- return -EINVAL;
-
- dev->ctl_ainput = INPUT(a->index)->amux;
- dev->ctl_aoutput = INPUT(a->index)->aout;
-
- if (!dev->ctl_aoutput)
- dev->ctl_aoutput = EM28XX_AOUT_MASTER;
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int id = qc->id;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- memset(qc, 0, sizeof(*qc));
-
- qc->id = id;
-
- /* enumerate AC97 controls */
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
- rc = ac97_queryctrl(qc);
- if (!rc)
- return 0;
- }
-
- /* enumerate V4L2 device controls */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
-
- if (qc->type)
- return 0;
- else
- 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)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
- rc = 0;
-
- /* Set an AC97 control */
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
- rc = ac97_get_ctrl(dev, ctrl);
- else
- rc = 1;
-
- /* 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;
- }
-
- return rc;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- /* Set an AC97 control */
- if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
- rc = ac97_set_ctrl(dev, ctrl);
- else
- rc = 1;
-
- /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
- if (rc == 1) {
- 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
- * and to adjust audio volume. However, the value ranges
- * should be checked by the corresponding V4L subdriver.
- */
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- dev->mute = ctrl->value;
- rc = em28xx_audio_analog_set(dev);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- dev->volume = ctrl->value;
- rc = em28xx_audio_analog_set(dev);
- }
- }
- return (rc < 0) ? rc : 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Tuner");
- t->type = V4L2_TUNER_ANALOG_TV;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != t->index)
- return -EINVAL;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (0 != f->tuner)
- return -EINVAL;
-
- if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
- return -EINVAL;
- if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
- return -EINVAL;
-
- dev->ctl_freq = f->frequency;
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f);
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int em28xx_reg_len(int reg)
-{
- switch (reg) {
- case EM28XX_R40_AC97LSB:
- case EM28XX_R30_HSCALELOW:
- case EM28XX_R32_VSCALELOW:
- return 2;
- default:
- return 1;
- }
-}
-
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
-
- return 0;
-}
-
-
-static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int ret;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_AC97:
- ret = em28xx_read_ac97(dev, reg->reg);
- if (ret < 0)
- return ret;
-
- reg->val = ret;
- reg->size = 1;
- return 0;
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- }
-
- /* Match host */
- reg->size = em28xx_reg_len(reg->reg);
- if (reg->size == 1) {
- ret = em28xx_read_reg(dev, reg->reg);
-
- if (ret < 0)
- return ret;
-
- reg->val = ret;
- } else {
- __le16 val = 0;
- ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
- reg->reg, (char *)&val, 2);
- if (ret < 0)
- return ret;
-
- reg->val = le16_to_cpu(val);
- }
-
- return 0;
-}
-
-static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- __le16 buf;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_AC97:
- return em28xx_write_ac97(dev, reg->reg, reg->val);
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- /* TODO: is this correct? */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
- return 0;
- default:
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- }
-
- /* Match host */
- buf = cpu_to_le16(reg->val);
-
- return em28xx_write_regs(dev, reg->reg, (char *)&buf,
- em28xx_reg_len(reg->reg));
-}
-#endif
-
-
-static int vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cc)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- cc->bounds.left = 0;
- cc->bounds.top = 0;
- cc->bounds.width = dev->width;
- cc->bounds.height = dev->height;
- cc->defrect = cc->bounds;
- cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */
- cc->pixelaspect.denominator = 59;
-
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc = -EINVAL;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (unlikely(type != fh->type))
- return -EINVAL;
-
- em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
- fh, type, fh->resources, dev->resources);
-
- if (unlikely(!res_get(fh, get_ressource(fh))))
- return -EBUSY;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_streamon(&fh->vb_vidq);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_streamon(&fh->vb_vbiq);
-
- return rc;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
- return -EINVAL;
- if (type != fh->type)
- return -EINVAL;
-
- em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
- fh, type, fh->resources, dev->resources);
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh, EM28XX_RESOURCE_VIDEO);
- }
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (res_check(fh, EM28XX_RESOURCE_VBI)) {
- videobuf_streamoff(&fh->vb_vbiq);
- res_free(fh, EM28XX_RESOURCE_VBI);
- }
- }
-
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
- strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-
- cap->capabilities =
- V4L2_CAP_SLICED_VBI_CAPTURE |
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
-
- if (dev->vbi_dev)
- cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
-
- if (dev->audio_mode.has_audio)
- cap->capabilities |= V4L2_CAP_AUDIO;
-
- if (dev->tuner_type != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(format)))
- return -EINVAL;
-
- strlcpy(f->description, format[f->index].name, sizeof(f->description));
- f->pixelformat = format[f->index].fourcc;
-
- return 0;
-}
-
-static int vidioc_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- struct em28xx_fmt *fmt;
- unsigned int maxw = norm_maxw(dev);
- unsigned int maxh = norm_maxh(dev);
-
- fmt = format_by_fourcc(fsize->pixel_format);
- if (!fmt) {
- em28xx_videodbg("Fourcc format (%08x) invalid.\n",
- fsize->pixel_format);
- return -EINVAL;
- }
-
- if (dev->board.is_em2800) {
- if (fsize->index > 1)
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = maxw / (1 + fsize->index);
- fsize->discrete.height = maxh / (1 + fsize->index);
- return 0;
- }
-
- if (fsize->index != 0)
- return -EINVAL;
-
- /* Report a continuous range */
- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise.min_width = 48;
- fsize->stepwise.min_height = 32;
- fsize->stepwise.max_width = maxw;
- fsize->stepwise.max_height = maxh;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
- return 0;
-}
-
-/* Sliced VBI ioctls */
-static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- f->fmt.sliced.service_set = 0;
- v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
-
- if (f->fmt.sliced.service_set == 0)
- rc = -EINVAL;
-
- return rc;
-}
-
-static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced);
-
- if (f->fmt.sliced.service_set == 0)
- return -EINVAL;
-
- return 0;
-}
-
-/* RAW VBI ioctls */
-
-static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- format->fmt.vbi.samples_per_line = dev->vbi_width;
- format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- format->fmt.vbi.offset = 0;
- format->fmt.vbi.flags = 0;
- format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
- format->fmt.vbi.count[0] = dev->vbi_height;
- format->fmt.vbi.count[1] = dev->vbi_height;
-
- /* Varies by video standard (NTSC, PAL, etc.) */
- if (dev->norm & V4L2_STD_525_60) {
- /* NTSC */
- format->fmt.vbi.start[0] = 10;
- format->fmt.vbi.start[1] = 273;
- } else if (dev->norm & V4L2_STD_625_50) {
- /* PAL */
- format->fmt.vbi.start[0] = 6;
- format->fmt.vbi.start[1] = 318;
- }
-
- return 0;
-}
-
-static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *format)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
-
- format->fmt.vbi.samples_per_line = dev->vbi_width;
- format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- format->fmt.vbi.offset = 0;
- format->fmt.vbi.flags = 0;
- format->fmt.vbi.sampling_rate = 6750000 * 4 / 2;
- format->fmt.vbi.count[0] = dev->vbi_height;
- format->fmt.vbi.count[1] = dev->vbi_height;
-
- /* Varies by video standard (NTSC, PAL, etc.) */
- if (dev->norm & V4L2_STD_525_60) {
- /* NTSC */
- format->fmt.vbi.start[0] = 10;
- format->fmt.vbi.start[1] = 273;
- } else if (dev->norm & V4L2_STD_625_50) {
- /* PAL */
- format->fmt.vbi.start[0] = 6;
- format->fmt.vbi.start[1] = 318;
- }
-
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_reqbufs(&fh->vb_vidq, rb);
- else
- return videobuf_reqbufs(&fh->vb_vbiq, rb);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_querybuf(&fh->vb_vidq, b);
- else {
- /* FIXME: I'm not sure yet whether this is a bug in zvbi or
- the videobuf framework, but we probably shouldn't be
- returning a buffer larger than that which was asked for.
- At a minimum, it causes a crash in zvbi since it does
- a memcpy based on the source buffer length */
- int result = videobuf_querybuf(&fh->vb_vbiq, b);
- b->length = dev->vbi_width * dev->vbi_height * 2;
-
- return result;
- }
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_qbuf(&fh->vb_vidq, b);
- else
- return videobuf_qbuf(&fh->vb_vbiq, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct em28xx_fh *fh = priv;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
- O_NONBLOCK);
- else
- return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
- O_NONBLOCK);
-}
-
-/* ----------------------------------------------------------- */
-/* RADIO ESPECIFIC IOCTLS */
-/* ----------------------------------------------------------- */
-
-static int radio_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
-
- strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
- strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
-
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
-static int radio_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
-
- if (unlikely(t->index > 0))
- return -EINVAL;
-
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
-
- return 0;
-}
-
-static int radio_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
- strcpy(i->name, "Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- if (unlikely(a->index))
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- return 0;
-}
-
-static int radio_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct em28xx *dev = ((struct em28xx_fh *)priv)->dev;
-
- if (0 != t->index)
- return -EINVAL;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int radio_s_audio(struct file *file, void *fh,
- struct v4l2_audio *a)
-{
- return 0;
-}
-
-static int radio_s_input(struct file *file, void *fh, unsigned int i)
-{
- return 0;
-}
-
-static int radio_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- if (qc->id < V4L2_CID_BASE ||
- qc->id >= V4L2_CID_LASTP1)
- return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
- if (qc->id && qc->id == ac97_qctrl[i].id) {
- memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-/*
- * em28xx_v4l2_open()
- * inits the device and starts isoc transfer
- */
-static int em28xx_v4l2_open(struct file *filp)
-{
- int errCode = 0, radio = 0;
- struct video_device *vdev = video_devdata(filp);
- struct em28xx *dev = video_drvdata(filp);
- enum v4l2_buf_type fh_type = 0;
- struct em28xx_fh *fh;
- enum v4l2_field field;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- em28xx_videodbg("open dev=%s type=%s users=%d\n",
- video_device_node_name(vdev), v4l2_type_names[fh_type],
- dev->users);
-
-
- fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
- if (!fh) {
- em28xx_errdev("em28xx-video.c: Out of memory?!\n");
- return -ENOMEM;
- }
- fh->dev = dev;
- fh->radio = radio;
- fh->type = fh_type;
- filp->private_data = fh;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
- em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
- em28xx_set_alternate(dev);
- em28xx_resolution_set(dev);
-
- /* Needed, since GPIO might have disabled power of
- some i2c device
- */
- em28xx_wake_i2c(dev);
-
- }
- if (fh->radio) {
- em28xx_videodbg("video_open: setting radio device\n");
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
- }
-
- dev->users++;
-
- if (dev->progressive)
- field = V4L2_FIELD_NONE;
- else
- field = V4L2_FIELD_INTERLACED;
-
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
- NULL, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
- sizeof(struct em28xx_buffer), fh, &dev->lock);
-
- videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
- NULL, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct em28xx_buffer), fh, &dev->lock);
-
- return errCode;
-}
-
-/*
- * em28xx_realease_resources()
- * unregisters the v4l2,i2c and usb devices
- * called when the device gets disconected or at module unload
-*/
-void em28xx_release_analog_resources(struct em28xx *dev)
-{
-
- /*FIXME: I2C IR should be disconnected */
-
- if (dev->radio_dev) {
- if (video_is_registered(dev->radio_dev))
- video_unregister_device(dev->radio_dev);
- else
- video_device_release(dev->radio_dev);
- dev->radio_dev = NULL;
- }
- if (dev->vbi_dev) {
- em28xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vbi_dev));
- if (video_is_registered(dev->vbi_dev))
- video_unregister_device(dev->vbi_dev);
- else
- video_device_release(dev->vbi_dev);
- dev->vbi_dev = NULL;
- }
- if (dev->vdev) {
- em28xx_info("V4L2 device %s deregistered\n",
- video_device_node_name(dev->vdev));
- if (video_is_registered(dev->vdev))
- video_unregister_device(dev->vdev);
- else
- video_device_release(dev->vdev);
- dev->vdev = NULL;
- }
-}
-
-/*
- * em28xx_v4l2_close()
- * stops streaming and deallocates all resources allocated by the v4l2
- * calls and ioctls
- */
-static int em28xx_v4l2_close(struct file *filp)
-{
- struct em28xx_fh *fh = filp->private_data;
- struct em28xx *dev = fh->dev;
- int errCode;
-
- em28xx_videodbg("users=%d\n", dev->users);
-
- if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
- videobuf_stop(&fh->vb_vidq);
- res_free(fh, EM28XX_RESOURCE_VIDEO);
- }
-
- if (res_check(fh, EM28XX_RESOURCE_VBI)) {
- videobuf_stop(&fh->vb_vbiq);
- res_free(fh, EM28XX_RESOURCE_VBI);
- }
-
- if (dev->users == 1) {
- /* the device is already disconnect,
- free the remaining resources */
- if (dev->state & DEV_DISCONNECTED) {
- em28xx_release_resources(dev);
- kfree(dev->alt_max_pkt_size);
- kfree(dev);
- kfree(fh);
- return 0;
- }
-
- /* Save some power by putting tuner to sleep */
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
-
- /* do this before setting alternate! */
- em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE);
- em28xx_set_mode(dev, EM28XX_SUSPEND);
-
- /* set alternate 0 */
- dev->alt = 0;
- em28xx_videodbg("setting alternate 0\n");
- errCode = usb_set_interface(dev->udev, 0, 0);
- if (errCode < 0) {
- em28xx_errdev("cannot change alternate number to "
- "0 (error=%i)\n", errCode);
- }
- }
-
- videobuf_mmap_free(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vbiq);
- kfree(fh);
- dev->users--;
- return 0;
-}
-
-/*
- * em28xx_v4l2_read()
- * will allocate buffers when called for the first time
- */
-static ssize_t
-em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
- loff_t *pos)
-{
- struct em28xx_fh *fh = filp->private_data;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- /* FIXME: read() is not prepared to allow changing the video
- resolution while streaming. Seems a bug at em28xx_set_fmt
- */
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
- return -EBUSY;
-
- return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
- filp->f_flags & O_NONBLOCK);
- }
-
-
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (!res_get(fh, EM28XX_RESOURCE_VBI))
- return -EBUSY;
-
- return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
- filp->f_flags & O_NONBLOCK);
- }
-
- return 0;
-}
-
-/*
- * em28xx_v4l2_poll()
- * will allocate buffers when called for the first time
- */
-static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
-{
- struct em28xx_fh *fh = filp->private_data;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
- return POLLERR;
- return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
- } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- if (!res_get(fh, EM28XX_RESOURCE_VBI))
- return POLLERR;
- return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
- } else {
- return POLLERR;
- }
-}
-
-/*
- * em28xx_v4l2_mmap()
- */
-static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct em28xx_fh *fh = filp->private_data;
- struct em28xx *dev = fh->dev;
- int rc;
-
- rc = check_dev(dev);
- if (rc < 0)
- return rc;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
- else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
-
- em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
- rc);
-
- return rc;
-}
-
-static const struct v4l2_file_operations em28xx_v4l_fops = {
- .owner = THIS_MODULE,
- .open = em28xx_v4l2_open,
- .release = em28xx_v4l2_close,
- .read = em28xx_v4l2_read,
- .poll = em28xx_v4l2_poll,
- .mmap = em28xx_v4l2_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
- .vidioc_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_cropcap = vidioc_cropcap,
- .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
- .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
- .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_querystd = vidioc_querystd,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
-#endif
-};
-
-static const struct video_device em28xx_video_template = {
- .fops = &em28xx_v4l_fops,
- .release = video_device_release,
- .ioctl_ops = &video_ioctl_ops,
-
- .tvnorms = V4L2_STD_ALL,
- .current_norm = V4L2_STD_PAL,
-};
-
-static const struct v4l2_file_operations radio_fops = {
- .owner = THIS_MODULE,
- .open = em28xx_v4l2_open,
- .release = em28xx_v4l2_close,
- .unlocked_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_queryctrl = radio_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static struct video_device em28xx_radio_template = {
- .name = "em28xx-radio",
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-/******************************** usb interface ******************************/
-
-
-
-static struct video_device *em28xx_vdev_init(struct em28xx *dev,
- const struct video_device *template,
- const char *type_name)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
-
- *vfd = *template;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
- vfd->debug = video_debug;
- vfd->lock = &dev->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
-
- snprintf(vfd->name, sizeof(vfd->name), "%s %s",
- dev->name, type_name);
-
- video_set_drvdata(vfd, dev);
- return vfd;
-}
-
-int em28xx_register_analog_devices(struct em28xx *dev)
-{
- u8 val;
- int ret;
- unsigned int maxw;
-
- printk(KERN_INFO "%s: v4l2 driver version %s\n",
- dev->name, EM28XX_VERSION);
-
- /* set default norm */
- dev->norm = em28xx_video_template.current_norm;
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
- dev->interlaced = EM28XX_INTERLACED_DEFAULT;
-
- /* Analog specific initialization */
- dev->format = &format[0];
-
- maxw = norm_maxw(dev);
- /* MaxPacketSize for em2800 is too small to capture at full resolution
- * use half of maxw as the scaler can only scale to 50% */
- if (dev->board.is_em2800)
- maxw /= 2;
-
- em28xx_set_video_format(dev, format[0].fourcc,
- maxw, norm_maxh(dev));
-
- video_mux(dev, 0);
-
- /* Audio defaults */
- dev->mute = 1;
- dev->volume = 0x1f;
-
-/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
- val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
- em28xx_write_reg(dev, EM28XX_R0F_XCLK,
- (EM28XX_XCLK_AUDIO_UNMUTE | val));
-
- em28xx_set_outfmt(dev);
- em28xx_colorlevels_set_default(dev);
- em28xx_compression_disable(dev);
-
- /* allocate and fill video video_device struct */
- dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video");
- if (!dev->vdev) {
- em28xx_errdev("cannot allocate video_device.\n");
- return -ENODEV;
- }
-
- /* register v4l2 video video_device */
- ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
- video_nr[dev->devno]);
- if (ret) {
- em28xx_errdev("unable to register video device (error=%i).\n",
- ret);
- return ret;
- }
-
- /* Allocate and fill vbi video_device struct */
- if (em28xx_vbi_supported(dev) == 1) {
- dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
- "vbi");
-
- /* register v4l2 vbi video_device */
- ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[dev->devno]);
- if (ret < 0) {
- em28xx_errdev("unable to register vbi device\n");
- return ret;
- }
- }
-
- if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
- dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
- "radio");
- if (!dev->radio_dev) {
- em28xx_errdev("cannot allocate video_device.\n");
- return -ENODEV;
- }
- ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
- radio_nr[dev->devno]);
- if (ret < 0) {
- em28xx_errdev("can't register radio device\n");
- return ret;
- }
- em28xx_info("Registered radio device as %s\n",
- video_device_node_name(dev->radio_dev));
- }
-
- em28xx_info("V4L2 video device registered as %s\n",
- video_device_node_name(dev->vdev));
-
- if (dev->vbi_dev)
- em28xx_info("V4L2 VBI device registered as %s\n",
- video_device_node_name(dev->vbi_dev));
-
- return 0;
-}
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
deleted file mode 100644
index 777486f7cadb..000000000000
--- a/drivers/media/video/fsl-viu.c
+++ /dev/null
@@ -1,1673 +0,0 @@
-/*
- * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * Freescale VIU video driver
- *
- * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
- * Porting to 2.6.35 by DENX Software Engineering,
- * Anatolij Gustschin <agust@denx.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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf-dma-contig.h>
-
-#define DRV_NAME "fsl_viu"
-#define VIU_VERSION "0.5.1"
-
-#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
-
-#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */
-
-/* I2C address of video decoder chip is 0x4A */
-#define VIU_VIDEO_DECODER_ADDR 0x25
-
-/* supported controls */
-static struct v4l2_queryctrl viu_qctrl[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 0x10,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 127,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 0x1,
- .default_value = 0,
- .flags = 0,
- }
-};
-
-static int qctl_regs[ARRAY_SIZE(viu_qctrl)];
-
-static int info_level;
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (level <= info_level) \
- printk(KERN_DEBUG "viu: " fmt , ## arg); \
- } while (0)
-
-/*
- * Basic structures
- */
-struct viu_fmt {
- char name[32];
- u32 fourcc; /* v4l2 format id */
- u32 pixelformat;
- int depth;
-};
-
-static struct viu_fmt formats[] = {
- {
- .name = "RGB-16 (5/B-6/G-5/R)",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .pixelformat = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- }, {
- .name = "RGB-32 (A-R-G-B)",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- }
-};
-
-struct viu_dev;
-struct viu_buf;
-
-/* buffer for one video frame */
-struct viu_buf {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- struct viu_fmt *fmt;
-};
-
-struct viu_dmaqueue {
- struct viu_dev *dev;
- struct list_head active;
- struct list_head queued;
- struct timer_list timeout;
-};
-
-struct viu_status {
- u32 field_irq;
- u32 vsync_irq;
- u32 hsync_irq;
- u32 vstart_irq;
- u32 dma_end_irq;
- u32 error_irq;
-};
-
-struct viu_reg {
- u32 status_cfg;
- u32 luminance;
- u32 chroma_r;
- u32 chroma_g;
- u32 chroma_b;
- u32 field_base_addr;
- u32 dma_inc;
- u32 picture_count;
- u32 req_alarm;
- u32 alpha;
-} __attribute__ ((packed));
-
-struct viu_dev {
- struct v4l2_device v4l2_dev;
- struct mutex lock;
- spinlock_t slock;
- int users;
-
- struct device *dev;
- /* various device info */
- struct video_device *vdev;
- struct viu_dmaqueue vidq;
- enum v4l2_field capfield;
- int field;
- int first;
- int dma_done;
-
- /* Hardware register area */
- struct viu_reg *vr;
-
- /* Interrupt vector */
- int irq;
- struct viu_status irqs;
-
- /* video overlay */
- struct v4l2_framebuffer ovbuf;
- struct viu_fmt *ovfmt;
- unsigned int ovenable;
- enum v4l2_field ovfield;
-
- /* crop */
- struct v4l2_rect crop_current;
-
- /* clock pointer */
- struct clk *clk;
-
- /* decoder */
- struct v4l2_subdev *decoder;
-
- v4l2_std_id std;
-};
-
-struct viu_fh {
- struct viu_dev *dev;
-
- /* video capture */
- struct videobuf_queue vb_vidq;
- spinlock_t vbq_lock; /* spinlock for the videobuf queue */
-
- /* video overlay */
- struct v4l2_window win;
- struct v4l2_clip clips[1];
-
- /* video capture */
- struct viu_fmt *fmt;
- int width, height, sizeimage;
- enum v4l2_buf_type type;
-};
-
-static struct viu_reg reg_val;
-
-/*
- * Macro definitions of VIU registers
- */
-
-/* STATUS_CONFIG register */
-enum status_config {
- SOFT_RST = 1 << 0,
-
- ERR_MASK = 0x0f << 4, /* Error code mask */
- ERR_NO = 0x00, /* No error */
- ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */
- ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */
- ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */
- ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */
- ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */
- ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */
- ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */
- ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */
- ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */
- ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */
-
- INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */
- INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */
- INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */
- INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */
- INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */
- INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */
- INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */
-
- INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */
- INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */
- INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */
- INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */
- INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */
- INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */
-
- DMA_ACT = 0x01 << 27, /* Enable DMA transfer */
- FIELD_NO = 0x01 << 28, /* Field number */
- DITHER_ON = 0x01 << 29, /* Dithering is on */
- ROUND_ON = 0x01 << 30, /* Round is on */
- MODE_32BIT = 0x01 << 31, /* Data in RGBa888,
- * 0 in RGB565
- */
-};
-
-#define norm_maxw() 720
-#define norm_maxh() 576
-
-#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \
- INT_HSYNC_STATUS | INT_VSTART_STATUS | \
- INT_DMA_END_STATUS | INT_ERROR_STATUS)
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-static irqreturn_t viu_intr(int irq, void *dev_id);
-
-struct viu_fmt *format_by_fourcc(int fourcc)
-{
- int i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].pixelformat == fourcc)
- return formats + i;
- }
-
- dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc);
- return NULL;
-}
-
-void viu_start_dma(struct viu_dev *dev)
-{
- struct viu_reg *vr = dev->vr;
-
- dev->field = 0;
-
- /* Enable DMA operation */
- out_be32(&vr->status_cfg, SOFT_RST);
- out_be32(&vr->status_cfg, INT_FIELD_EN);
-}
-
-void viu_stop_dma(struct viu_dev *dev)
-{
- struct viu_reg *vr = dev->vr;
- int cnt = 100;
- u32 status_cfg;
-
- out_be32(&vr->status_cfg, 0);
-
- /* Clear pending interrupts */
- status_cfg = in_be32(&vr->status_cfg);
- if (status_cfg & 0x3f0000)
- out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
-
- if (status_cfg & DMA_ACT) {
- do {
- status_cfg = in_be32(&vr->status_cfg);
- if (status_cfg & INT_DMA_END_STATUS)
- break;
- } while (cnt--);
-
- if (cnt < 0) {
- /* timed out, issue soft reset */
- out_be32(&vr->status_cfg, SOFT_RST);
- out_be32(&vr->status_cfg, 0);
- } else {
- /* clear DMA_END and other pending irqs */
- out_be32(&vr->status_cfg, status_cfg & 0x3f0000);
- }
- }
-
- dev->field = 0;
-}
-
-static int restart_video_queue(struct viu_dmaqueue *vidq)
-{
- struct viu_buf *buf, *prev;
-
- dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq);
- if (!list_empty(&vidq->active)) {
- buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
- dprintk(2, "restart_queue [%p/%d]: restart dma\n",
- buf, buf->vb.i);
-
- viu_stop_dma(vidq->dev);
-
- /* cancel all outstanding capture requests */
- list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) {
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- }
- mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
- }
-
- prev = NULL;
- for (;;) {
- if (list_empty(&vidq->queued))
- return 0;
- buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue);
- if (prev == NULL) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &vidq->active);
-
- dprintk(1, "Restarting video dma\n");
- viu_stop_dma(vidq->dev);
- viu_start_dma(vidq->dev);
-
- buf->vb.state = VIDEOBUF_ACTIVE;
- mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2, "[%p/%d] restart_queue - first active\n",
- buf, buf->vb.i);
-
- } else if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &vidq->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- dprintk(2, "[%p/%d] restart_queue - move to active\n",
- buf, buf->vb.i);
- } else {
- return 0;
- }
- prev = buf;
- }
-}
-
-static void viu_vid_timeout(unsigned long data)
-{
- struct viu_dev *dev = (struct viu_dev *)data;
- struct viu_buf *buf;
- struct viu_dmaqueue *vidq = &dev->vidq;
-
- while (!list_empty(&vidq->active)) {
- buf = list_entry(vidq->active.next, struct viu_buf, vb.queue);
- list_del(&buf->vb.queue);
- buf->vb.state = VIDEOBUF_ERROR;
- wake_up(&buf->vb.done);
- dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i);
- }
-
- restart_video_queue(vidq);
-}
-
-/*
- * Videobuf operations
- */
-static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct viu_fh *fh = vq->priv_data;
-
- *size = fh->width * fh->height * fh->fmt->depth >> 3;
- if (*count == 0)
- *count = 32;
-
- while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024)
- (*count)--;
-
- dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size);
- return 0;
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf)
-{
- struct videobuf_buffer *vb = &buf->vb;
- void *vaddr = NULL;
-
- BUG_ON(in_interrupt());
-
- videobuf_waiton(vq, &buf->vb, 0, 0);
-
- if (vq->int_ops && vq->int_ops->vaddr)
- vaddr = vq->int_ops->vaddr(vb);
-
- if (vaddr)
- videobuf_dma_contig_free(vq, &buf->vb);
-
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf)
-{
- struct viu_reg *vr = dev->vr;
- int bpp;
-
- /* setup the DMA base address */
- reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb);
-
- dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n",
- buf, buf->vb.i, (unsigned long)reg_val.field_base_addr);
-
- /* interlace is on by default, set horizontal DMA increment */
- reg_val.status_cfg = 0;
- bpp = buf->fmt->depth >> 3;
- switch (bpp) {
- case 2:
- reg_val.status_cfg &= ~MODE_32BIT;
- reg_val.dma_inc = buf->vb.width * 2;
- break;
- case 4:
- reg_val.status_cfg |= MODE_32BIT;
- reg_val.dma_inc = buf->vb.width * 4;
- break;
- default:
- dprintk(0, "doesn't support color depth(%d)\n",
- bpp * 8);
- return -EINVAL;
- }
-
- /* setup picture_count register */
- reg_val.picture_count = (buf->vb.height / 2) << 16 |
- buf->vb.width;
-
- reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
-
- buf->vb.state = VIDEOBUF_ACTIVE;
- dev->capfield = buf->vb.field;
-
- /* reset dma increment if needed */
- if (!V4L2_FIELD_HAS_BOTH(buf->vb.field))
- reg_val.dma_inc = 0;
-
- out_be32(&vr->dma_inc, reg_val.dma_inc);
- out_be32(&vr->picture_count, reg_val.picture_count);
- out_be32(&vr->field_base_addr, reg_val.field_base_addr);
- mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT);
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct viu_fh *fh = vq->priv_data;
- struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
- int rc;
-
- BUG_ON(fh->fmt == NULL);
-
- if (fh->width < 48 || fh->width > norm_maxw() ||
- fh->height < 32 || fh->height > norm_maxh())
- return -EINVAL;
- buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
- if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- if (buf->fmt != fh->fmt ||
- buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
- buf->vb.field != field) {
- buf->fmt = fh->fmt;
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.field = field;
- }
-
- if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc != 0)
- goto fail;
-
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.field = field;
- buf->fmt = fh->fmt;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
- struct viu_fh *fh = vq->priv_data;
- struct viu_dev *dev = fh->dev;
- struct viu_dmaqueue *vidq = &dev->vidq;
- struct viu_buf *prev;
-
- if (!list_empty(&vidq->queued)) {
- dprintk(1, "adding vb queue=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
- dprintk(1, "vidq pointer 0x%p, queued 0x%p\n",
- vidq, &vidq->queued);
- dprintk(1, "dev %p, queued: self %p, next %p, head %p\n",
- dev, &vidq->queued, vidq->queued.next,
- vidq->queued.prev);
- list_add_tail(&buf->vb.queue, &vidq->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
- buf, buf->vb.i);
- } else if (list_empty(&vidq->active)) {
- dprintk(1, "adding vb active=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
- list_add_tail(&buf->vb.queue, &vidq->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT);
- dprintk(2, "[%p/%d] buffer_queue - first active\n",
- buf, buf->vb.i);
-
- buffer_activate(dev, buf);
- } else {
- dprintk(1, "adding vb queue2=0x%08lx\n",
- (unsigned long)&buf->vb.queue);
- prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue);
- if (prev->vb.width == buf->vb.width &&
- prev->vb.height == buf->vb.height &&
- prev->fmt == buf->fmt) {
- list_add_tail(&buf->vb.queue, &vidq->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- dprintk(2, "[%p/%d] buffer_queue - append to active\n",
- buf, buf->vb.i);
- } else {
- list_add_tail(&buf->vb.queue, &vidq->queued);
- buf->vb.state = VIDEOBUF_QUEUED;
- dprintk(2, "[%p/%d] buffer_queue - first queued\n",
- buf, buf->vb.i);
- }
- }
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct viu_buf *buf = container_of(vb, struct viu_buf, vb);
- struct viu_fh *fh = vq->priv_data;
- struct viu_dev *dev = (struct viu_dev *)fh->dev;
-
- viu_stop_dma(dev);
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops viu_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/*
- * IOCTL vidioc handling
- */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strcpy(cap->driver, "viu");
- strcpy(cap->card, "viu");
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_OVERLAY |
- V4L2_CAP_READWRITE;
- return 0;
-}
-
-static int vidioc_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- int index = f->index;
-
- if (f->index > NUM_FORMATS)
- return -EINVAL;
-
- strlcpy(f->description, formats[index].name, sizeof(f->description));
- f->pixelformat = formats[index].fourcc;
- return 0;
-}
-
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct viu_fh *fh = priv;
-
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->vb_vidq.field;
- f->fmt.pix.pixelformat = fh->fmt->pixelformat;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage = fh->sizeimage;
- return 0;
-}
-
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct viu_fmt *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt) {
- dprintk(1, "Fourcc format (0x%08x) invalid.",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY) {
- field = V4L2_FIELD_INTERLACED;
- } else if (field != V4L2_FIELD_INTERLACED) {
- dprintk(1, "Field type invalid.\n");
- return -EINVAL;
- }
-
- maxw = norm_maxw();
- maxh = norm_maxh();
-
- f->fmt.pix.field = field;
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
-
- return 0;
-}
-
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct viu_fh *fh = priv;
- int ret;
-
- ret = vidioc_try_fmt_cap(file, fh, f);
- if (ret < 0)
- return ret;
-
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->sizeimage = f->fmt.pix.sizeimage;
- fh->vb_vidq.field = f->fmt.pix.field;
- fh->type = f->type;
- dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name);
- return 0;
-}
-
-static int vidioc_g_fmt_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct viu_fh *fh = priv;
-
- f->fmt.win = fh->win;
- return 0;
-}
-
-static int verify_preview(struct viu_dev *dev, struct v4l2_window *win)
-{
- enum v4l2_field field;
- int maxw, maxh;
-
- if (dev->ovbuf.base == NULL)
- return -EINVAL;
- if (dev->ovfmt == NULL)
- return -EINVAL;
- if (win->w.width < 48 || win->w.height < 32)
- return -EINVAL;
-
- field = win->field;
- maxw = dev->crop_current.width;
- maxh = dev->crop_current.height;
-
- if (field == V4L2_FIELD_ANY) {
- field = (win->w.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_TOP;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- win->field = field;
- if (win->w.width > maxw)
- win->w.width = maxw;
- if (win->w.height > maxh)
- win->w.height = maxh;
- return 0;
-}
-
-inline void viu_activate_overlay(struct viu_reg *viu_reg)
-{
- struct viu_reg *vr = viu_reg;
-
- out_be32(&vr->field_base_addr, reg_val.field_base_addr);
- out_be32(&vr->dma_inc, reg_val.dma_inc);
- out_be32(&vr->picture_count, reg_val.picture_count);
-}
-
-static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh)
-{
- int bpp;
-
- dprintk(1, "%s %dx%d %s\n", __func__,
- fh->win.w.width, fh->win.w.height, dev->ovfmt->name);
-
- reg_val.status_cfg = 0;
-
- /* setup window */
- reg_val.picture_count = (fh->win.w.height / 2) << 16 |
- fh->win.w.width;
-
- /* setup color depth and dma increment */
- bpp = dev->ovfmt->depth / 8;
- switch (bpp) {
- case 2:
- reg_val.status_cfg &= ~MODE_32BIT;
- reg_val.dma_inc = fh->win.w.width * 2;
- break;
- case 4:
- reg_val.status_cfg |= MODE_32BIT;
- reg_val.dma_inc = fh->win.w.width * 4;
- break;
- default:
- dprintk(0, "device doesn't support color depth(%d)\n",
- bpp * 8);
- return -EINVAL;
- }
-
- dev->ovfield = fh->win.field;
- if (!V4L2_FIELD_HAS_BOTH(dev->ovfield))
- reg_val.dma_inc = 0;
-
- reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN;
-
- /* setup the base address of the overlay buffer */
- reg_val.field_base_addr = (u32)dev->ovbuf.base;
-
- return 0;
-}
-
-static int vidioc_s_fmt_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct viu_fh *fh = priv;
- struct viu_dev *dev = (struct viu_dev *)fh->dev;
- unsigned long flags;
- int err;
-
- err = verify_preview(dev, &f->fmt.win);
- if (err)
- return err;
-
- fh->win = f->fmt.win;
-
- spin_lock_irqsave(&dev->slock, flags);
- viu_setup_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
- return 0;
-}
-
-static int vidioc_try_fmt_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return 0;
-}
-
-static int vidioc_overlay(struct file *file, void *priv, unsigned int on)
-{
- struct viu_fh *fh = priv;
- struct viu_dev *dev = (struct viu_dev *)fh->dev;
- unsigned long flags;
-
- if (on) {
- spin_lock_irqsave(&dev->slock, flags);
- viu_activate_overlay(dev->vr);
- dev->ovenable = 1;
-
- /* start dma */
- viu_start_dma(dev);
- spin_unlock_irqrestore(&dev->slock, flags);
- } else {
- viu_stop_dma(dev);
- dev->ovenable = 0;
- }
-
- return 0;
-}
-
-int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
-{
- struct viu_fh *fh = priv;
- struct viu_dev *dev = fh->dev;
- struct v4l2_framebuffer *fb = arg;
-
- *fb = dev->ovbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
- return 0;
-}
-
-int vidioc_s_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg)
-{
- struct viu_fh *fh = priv;
- struct viu_dev *dev = fh->dev;
- struct v4l2_framebuffer *fb = arg;
- struct viu_fmt *fmt;
-
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (fmt == NULL)
- return -EINVAL;
-
- /* ok, accept it */
- dev->ovbuf = *fb;
- dev->ovfmt = fmt;
- if (dev->ovbuf.fmt.bytesperline == 0) {
- dev->ovbuf.fmt.bytesperline =
- dev->ovbuf.fmt.width * fmt->depth / 8;
- }
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct viu_fh *fh = priv;
-
- return videobuf_reqbufs(&fh->vb_vidq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct viu_fh *fh = priv;
-
- return videobuf_querybuf(&fh->vb_vidq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct viu_fh *fh = priv;
-
- return videobuf_qbuf(&fh->vb_vidq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct viu_fh *fh = priv;
-
- return videobuf_dqbuf(&fh->vb_vidq, p,
- file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct viu_fh *fh = priv;
- struct viu_dev *dev = fh->dev;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (fh->type != i)
- return -EINVAL;
-
- if (dev->ovenable)
- dev->ovenable = 0;
-
- viu_start_dma(fh->dev);
-
- return videobuf_streamon(&fh->vb_vidq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct viu_fh *fh = priv;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (fh->type != i)
- return -EINVAL;
-
- viu_stop_dma(fh->dev);
-
- return videobuf_streamoff(&fh->vb_vidq);
-}
-
-#define decoder_call(viu, o, f, args...) \
- v4l2_subdev_call(viu->decoder, o, f, ##args)
-
-static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct viu_fh *fh = priv;
-
- decoder_call(fh->dev, video, querystd, std_id);
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct viu_fh *fh = priv;
-
- fh->dev->std = *id;
- decoder_call(fh->dev, core, s_std, *id);
- return 0;
-}
-
-static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct viu_fh *fh = priv;
-
- *std_id = fh->dev->std;
- return 0;
-}
-
-/* only one input in this driver */
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct viu_fh *fh = priv;
-
- if (inp->index != 0)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = fh->dev->vdev->tvnorms;
- strcpy(inp->name, "Camera");
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct viu_fh *fh = priv;
-
- if (i > 1)
- return -EINVAL;
-
- decoder_call(fh->dev, video, s_routing, i, 0, 0);
- return 0;
-}
-
-/* Controls */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (qc->id && qc->id == viu_qctrl[i].id) {
- memcpy(qc, &(viu_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (ctrl->id == viu_qctrl[i].id) {
- ctrl->value = qctl_regs[i];
- return 0;
- }
- }
- return -EINVAL;
-}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (ctrl->id == viu_qctrl[i].id) {
- if (ctrl->value < viu_qctrl[i].minimum
- || ctrl->value > viu_qctrl[i].maximum)
- return -ERANGE;
- qctl_regs[i] = ctrl->value;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-inline void viu_activate_next_buf(struct viu_dev *dev,
- struct viu_dmaqueue *viuq)
-{
- struct viu_dmaqueue *vidq = viuq;
- struct viu_buf *buf;
-
- /* launch another DMA operation for an active/queued buffer */
- if (!list_empty(&vidq->active)) {
- buf = list_entry(vidq->active.next, struct viu_buf,
- vb.queue);
- dprintk(1, "start another queued buffer: 0x%p\n", buf);
- buffer_activate(dev, buf);
- } else if (!list_empty(&vidq->queued)) {
- buf = list_entry(vidq->queued.next, struct viu_buf,
- vb.queue);
- list_del(&buf->vb.queue);
-
- dprintk(1, "start another queued buffer: 0x%p\n", buf);
- list_add_tail(&buf->vb.queue, &vidq->active);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buffer_activate(dev, buf);
- }
-}
-
-inline void viu_default_settings(struct viu_reg *viu_reg)
-{
- struct viu_reg *vr = viu_reg;
-
- out_be32(&vr->luminance, 0x9512A254);
- out_be32(&vr->chroma_r, 0x03310000);
- out_be32(&vr->chroma_g, 0x06600F38);
- out_be32(&vr->chroma_b, 0x00000409);
- out_be32(&vr->alpha, 0x000000ff);
- out_be32(&vr->req_alarm, 0x00000090);
- dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n",
- in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr));
-}
-
-static void viu_overlay_intr(struct viu_dev *dev, u32 status)
-{
- struct viu_reg *vr = dev->vr;
-
- if (status & INT_DMA_END_STATUS)
- dev->dma_done = 1;
-
- if (status & INT_FIELD_STATUS) {
- if (dev->dma_done) {
- u32 addr = reg_val.field_base_addr;
-
- dev->dma_done = 0;
- if (status & FIELD_NO)
- addr += reg_val.dma_inc;
-
- out_be32(&vr->field_base_addr, addr);
- out_be32(&vr->dma_inc, reg_val.dma_inc);
- out_be32(&vr->status_cfg,
- (status & 0xffc0ffff) |
- (status & INT_ALL_STATUS) |
- reg_val.status_cfg);
- } else if (status & INT_VSYNC_STATUS) {
- out_be32(&vr->status_cfg,
- (status & 0xffc0ffff) |
- (status & INT_ALL_STATUS) |
- reg_val.status_cfg);
- }
- }
-}
-
-static void viu_capture_intr(struct viu_dev *dev, u32 status)
-{
- struct viu_dmaqueue *vidq = &dev->vidq;
- struct viu_reg *vr = dev->vr;
- struct viu_buf *buf;
- int field_num;
- int need_two;
- int dma_done = 0;
-
- field_num = status & FIELD_NO;
- need_two = V4L2_FIELD_HAS_BOTH(dev->capfield);
-
- if (status & INT_DMA_END_STATUS) {
- dma_done = 1;
- if (((field_num == 0) && (dev->field == 0)) ||
- (field_num && (dev->field == 1)))
- dev->field++;
- }
-
- if (status & INT_FIELD_STATUS) {
- dprintk(1, "irq: field %d, done %d\n",
- !!field_num, dma_done);
- if (unlikely(dev->first)) {
- if (field_num == 0) {
- dev->first = 0;
- dprintk(1, "activate first buf\n");
- viu_activate_next_buf(dev, vidq);
- } else
- dprintk(1, "wait field 0\n");
- return;
- }
-
- /* setup buffer address for next dma operation */
- if (!list_empty(&vidq->active)) {
- u32 addr = reg_val.field_base_addr;
-
- if (field_num && need_two) {
- addr += reg_val.dma_inc;
- dprintk(1, "field 1, 0x%lx, dev field %d\n",
- (unsigned long)addr, dev->field);
- }
- out_be32(&vr->field_base_addr, addr);
- out_be32(&vr->dma_inc, reg_val.dma_inc);
- out_be32(&vr->status_cfg,
- (status & 0xffc0ffff) |
- (status & INT_ALL_STATUS) |
- reg_val.status_cfg);
- return;
- }
- }
-
- if (dma_done && field_num && (dev->field == 2)) {
- dev->field = 0;
- buf = list_entry(vidq->active.next,
- struct viu_buf, vb.queue);
- dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n",
- buf, buf->vb.i,
- (unsigned long)videobuf_to_dma_contig(&buf->vb),
- (unsigned long)in_be32(&vr->field_base_addr));
-
- if (waitqueue_active(&buf->vb.done)) {
- list_del(&buf->vb.queue);
- do_gettimeofday(&buf->vb.ts);
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- wake_up(&buf->vb.done);
- }
- /* activate next dma buffer */
- viu_activate_next_buf(dev, vidq);
- }
-}
-
-static irqreturn_t viu_intr(int irq, void *dev_id)
-{
- struct viu_dev *dev = (struct viu_dev *)dev_id;
- struct viu_reg *vr = dev->vr;
- u32 status;
- u32 error;
-
- status = in_be32(&vr->status_cfg);
-
- if (status & INT_ERROR_STATUS) {
- dev->irqs.error_irq++;
- error = status & ERR_MASK;
- if (error)
- dprintk(1, "Err: error(%d), times:%d!\n",
- error >> 4, dev->irqs.error_irq);
- /* Clear interrupt error bit and error flags */
- out_be32(&vr->status_cfg,
- (status & 0xffc0ffff) | INT_ERROR_STATUS);
- }
-
- if (status & INT_DMA_END_STATUS) {
- dev->irqs.dma_end_irq++;
- dev->dma_done = 1;
- dprintk(2, "VIU DMA end interrupt times: %d\n",
- dev->irqs.dma_end_irq);
- }
-
- if (status & INT_HSYNC_STATUS)
- dev->irqs.hsync_irq++;
-
- if (status & INT_FIELD_STATUS) {
- dev->irqs.field_irq++;
- dprintk(2, "VIU field interrupt times: %d\n",
- dev->irqs.field_irq);
- }
-
- if (status & INT_VSTART_STATUS)
- dev->irqs.vstart_irq++;
-
- if (status & INT_VSYNC_STATUS) {
- dev->irqs.vsync_irq++;
- dprintk(2, "VIU vsync interrupt times: %d\n",
- dev->irqs.vsync_irq);
- }
-
- /* clear all pending irqs */
- status = in_be32(&vr->status_cfg);
- out_be32(&vr->status_cfg,
- (status & 0xffc0ffff) | (status & INT_ALL_STATUS));
-
- if (dev->ovenable) {
- viu_overlay_intr(dev, status);
- return IRQ_HANDLED;
- }
-
- /* Capture mode */
- viu_capture_intr(dev, status);
- return IRQ_HANDLED;
-}
-
-/*
- * File operations for the device
- */
-static int viu_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct viu_dev *dev = video_get_drvdata(vdev);
- struct viu_fh *fh;
- struct viu_reg *vr;
- int minor = vdev->minor;
- u32 status_cfg;
- int i;
-
- dprintk(1, "viu: open (minor=%d)\n", minor);
-
- dev->users++;
- if (dev->users > 1) {
- dev->users--;
- return -EBUSY;
- }
-
- vr = dev->vr;
-
- dprintk(1, "open minor=%d type=%s users=%d\n", minor,
- v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
-
- /* allocate and initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (!fh) {
- dev->users--;
- return -ENOMEM;
- }
-
- file->private_data = fh;
- fh->dev = dev;
-
- fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32);
- fh->width = norm_maxw();
- fh->height = norm_maxh();
- dev->crop_current.width = fh->width;
- dev->crop_current.height = fh->height;
-
- /* Put all controls at a sane state */
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++)
- qctl_regs[i] = viu_qctrl[i].default_value;
-
- dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&dev->vidq);
- dprintk(1, "Open: list_empty queued=%d\n",
- list_empty(&dev->vidq.queued));
- dprintk(1, "Open: list_empty active=%d\n",
- list_empty(&dev->vidq.active));
-
- viu_default_settings(vr);
-
- status_cfg = in_be32(&vr->status_cfg);
- out_be32(&vr->status_cfg,
- status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN |
- INT_FIELD_EN | INT_VSTART_EN |
- INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN));
-
- status_cfg = in_be32(&vr->status_cfg);
- out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS);
-
- spin_lock_init(&fh->vbq_lock);
- videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops,
- dev->dev, &fh->vbq_lock,
- fh->type, V4L2_FIELD_INTERLACED,
- sizeof(struct viu_buf), fh,
- &fh->dev->lock);
- return 0;
-}
-
-static ssize_t viu_read(struct file *file, char __user *data, size_t count,
- loff_t *ppos)
-{
- struct viu_fh *fh = file->private_data;
- struct viu_dev *dev = fh->dev;
- int ret = 0;
-
- dprintk(2, "%s\n", __func__);
- if (dev->ovenable)
- dev->ovenable = 0;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- viu_start_dma(dev);
- ret = videobuf_read_stream(&fh->vb_vidq, data, count,
- ppos, 0, file->f_flags & O_NONBLOCK);
- return ret;
- }
- return 0;
-}
-
-static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct viu_fh *fh = file->private_data;
- struct videobuf_queue *q = &fh->vb_vidq;
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
- return POLLERR;
-
- return videobuf_poll_stream(file, q, wait);
-}
-
-static int viu_release(struct file *file)
-{
- struct viu_fh *fh = file->private_data;
- struct viu_dev *dev = fh->dev;
- int minor = video_devdata(file)->minor;
-
- viu_stop_dma(dev);
- videobuf_stop(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vidq);
-
- kfree(fh);
-
- dev->users--;
- dprintk(1, "close (minor=%d, users=%d)\n",
- minor, dev->users);
- return 0;
-}
-
-void viu_reset(struct viu_reg *reg)
-{
- out_be32(&reg->status_cfg, 0);
- out_be32(&reg->luminance, 0x9512a254);
- out_be32(&reg->chroma_r, 0x03310000);
- out_be32(&reg->chroma_g, 0x06600f38);
- out_be32(&reg->chroma_b, 0x00000409);
- out_be32(&reg->field_base_addr, 0);
- out_be32(&reg->dma_inc, 0);
- out_be32(&reg->picture_count, 0x01e002d0);
- out_be32(&reg->req_alarm, 0x00000090);
- out_be32(&reg->alpha, 0x000000ff);
-}
-
-static int viu_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct viu_fh *fh = file->private_data;
- int ret;
-
- dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
-
- ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
-
- dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
- ret);
-
- return ret;
-}
-
-static struct v4l2_file_operations viu_fops = {
- .owner = THIS_MODULE,
- .open = viu_open,
- .release = viu_release,
- .read = viu_read,
- .poll = viu_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = viu_mmap,
-};
-
-static const struct v4l2_ioctl_ops viu_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap,
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_overlay,
- .vidioc_overlay = vidioc_overlay,
- .vidioc_g_fbuf = vidioc_g_fbuf,
- .vidioc_s_fbuf = vidioc_s_fbuf,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_g_std = vidioc_g_std,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_querystd = vidioc_querystd,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-};
-
-static struct video_device viu_template = {
- .name = "FSL viu",
- .fops = &viu_fops,
- .minor = -1,
- .ioctl_ops = &viu_ioctl_ops,
- .release = video_device_release,
-
- .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static int __devinit viu_of_probe(struct platform_device *op)
-{
- struct viu_dev *viu_dev;
- struct video_device *vdev;
- struct resource r;
- struct viu_reg __iomem *viu_regs;
- struct i2c_adapter *ad;
- int ret, viu_irq;
-
- ret = of_address_to_resource(op->dev.of_node, 0, &r);
- if (ret) {
- dev_err(&op->dev, "Can't parse device node resource\n");
- return -ENODEV;
- }
-
- viu_irq = irq_of_parse_and_map(op->dev.of_node, 0);
- if (viu_irq == NO_IRQ) {
- dev_err(&op->dev, "Error while mapping the irq\n");
- return -EINVAL;
- }
-
- /* request mem region */
- if (!devm_request_mem_region(&op->dev, r.start,
- sizeof(struct viu_reg), DRV_NAME)) {
- dev_err(&op->dev, "Error while requesting mem region\n");
- ret = -EBUSY;
- goto err;
- }
-
- /* remap registers */
- viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg));
- if (!viu_regs) {
- dev_err(&op->dev, "Can't map register set\n");
- ret = -ENOMEM;
- goto err;
- }
-
- /* Prepare our private structure */
- viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC);
- if (!viu_dev) {
- dev_err(&op->dev, "Can't allocate private structure\n");
- ret = -ENOMEM;
- goto err;
- }
-
- viu_dev->vr = viu_regs;
- viu_dev->irq = viu_irq;
- viu_dev->dev = &op->dev;
-
- /* init video dma queues */
- INIT_LIST_HEAD(&viu_dev->vidq.active);
- INIT_LIST_HEAD(&viu_dev->vidq.queued);
-
- snprintf(viu_dev->v4l2_dev.name,
- sizeof(viu_dev->v4l2_dev.name), "%s", "VIU");
- ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev);
- if (ret < 0) {
- dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret);
- goto err;
- }
-
- ad = i2c_get_adapter(0);
- viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad,
- "saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
-
- viu_dev->vidq.timeout.function = viu_vid_timeout;
- viu_dev->vidq.timeout.data = (unsigned long)viu_dev;
- init_timer(&viu_dev->vidq.timeout);
- viu_dev->first = 1;
-
- /* Allocate memory for video device */
- vdev = video_device_alloc();
- if (vdev == NULL) {
- ret = -ENOMEM;
- goto err_vdev;
- }
-
- memcpy(vdev, &viu_template, sizeof(viu_template));
-
- vdev->v4l2_dev = &viu_dev->v4l2_dev;
-
- viu_dev->vdev = vdev;
-
- /* initialize locks */
- mutex_init(&viu_dev->lock);
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &viu_dev->vdev->flags);
- viu_dev->vdev->lock = &viu_dev->lock;
- spin_lock_init(&viu_dev->slock);
-
- video_set_drvdata(viu_dev->vdev, viu_dev);
-
- mutex_lock(&viu_dev->lock);
-
- ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- video_device_release(viu_dev->vdev);
- goto err_vdev;
- }
-
- /* enable VIU clock */
- viu_dev->clk = clk_get(&op->dev, "viu_clk");
- if (IS_ERR(viu_dev->clk)) {
- dev_err(&op->dev, "failed to find the clock module!\n");
- ret = -ENODEV;
- goto err_clk;
- } else {
- clk_enable(viu_dev->clk);
- }
-
- /* reset VIU module */
- viu_reset(viu_dev->vr);
-
- /* install interrupt handler */
- if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) {
- dev_err(&op->dev, "Request VIU IRQ failed.\n");
- ret = -ENODEV;
- goto err_irq;
- }
-
- mutex_unlock(&viu_dev->lock);
-
- dev_info(&op->dev, "Freescale VIU Video Capture Board\n");
- return ret;
-
-err_irq:
- clk_disable(viu_dev->clk);
- clk_put(viu_dev->clk);
-err_clk:
- video_unregister_device(viu_dev->vdev);
-err_vdev:
- mutex_unlock(&viu_dev->lock);
- i2c_put_adapter(ad);
- v4l2_device_unregister(&viu_dev->v4l2_dev);
-err:
- irq_dispose_mapping(viu_irq);
- return ret;
-}
-
-static int __devexit viu_of_remove(struct platform_device *op)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
- struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
- struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next,
- struct v4l2_subdev, list);
- struct i2c_client *client = v4l2_get_subdevdata(sdev);
-
- free_irq(dev->irq, (void *)dev);
- irq_dispose_mapping(dev->irq);
-
- clk_disable(dev->clk);
- clk_put(dev->clk);
-
- video_unregister_device(dev->vdev);
- i2c_put_adapter(client->adapter);
- v4l2_device_unregister(&dev->v4l2_dev);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int viu_suspend(struct platform_device *op, pm_message_t state)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
- struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
-
- clk_disable(dev->clk);
- return 0;
-}
-
-static int viu_resume(struct platform_device *op)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev);
- struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev);
-
- clk_enable(dev->clk);
- return 0;
-}
-#endif
-
-/*
- * Initialization and module stuff
- */
-static struct of_device_id mpc512x_viu_of_match[] = {
- {
- .compatible = "fsl,mpc5121-viu",
- },
- {},
-};
-MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match);
-
-static struct platform_driver viu_of_platform_driver = {
- .probe = viu_of_probe,
- .remove = __devexit_p(viu_of_remove),
-#ifdef CONFIG_PM
- .suspend = viu_suspend,
- .resume = viu_resume,
-#endif
- .driver = {
- .name = DRV_NAME,
- .owner = THIS_MODULE,
- .of_match_table = mpc512x_viu_of_match,
- },
-};
-
-module_platform_driver(viu_of_platform_driver);
-
-MODULE_DESCRIPTION("Freescale Video-In(VIU)");
-MODULE_AUTHOR("Hongjun Chen");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(VIU_VERSION);
diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
deleted file mode 100644
index dfe268bfa4f8..000000000000
--- a/drivers/media/video/gspca/Kconfig
+++ /dev/null
@@ -1,425 +0,0 @@
-menuconfig USB_GSPCA
- tristate "GSPCA based webcams"
- depends on VIDEO_V4L2
- default m
- ---help---
- Say Y here if you want to enable selecting webcams based
- on the GSPCA framework.
-
- See <file:Documentation/video4linux/gspca.txt> for more info.
-
- This driver uses the Video For Linux API. You must say Y or M to
- "Video For Linux" to use this driver.
-
- To compile this driver as modules, choose M here: the
- module will be called gspca_main.
-
-
-if USB_GSPCA && VIDEO_V4L2
-
-source "drivers/media/video/gspca/m5602/Kconfig"
-source "drivers/media/video/gspca/stv06xx/Kconfig"
-source "drivers/media/video/gspca/gl860/Kconfig"
-
-config USB_GSPCA_BENQ
- tristate "Benq USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for the Benq DC E300 camera.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_benq.
-
-config USB_GSPCA_CONEX
- tristate "Conexant Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Conexant chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_conex.
-
-config USB_GSPCA_CPIA1
- tristate "cpia CPiA (version 1) Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for USB cameras based on the cpia
- CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
- applications to understand the videoformat generated by this driver.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_cpia1.
-
-config USB_GSPCA_ETOMS
- tristate "Etoms USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Etoms chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_etoms.
-
-config USB_GSPCA_FINEPIX
- tristate "Fujifilm FinePix USB V4L2 driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the FinePix chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_finepix.
-
-config USB_GSPCA_JEILINJ
- tristate "Jeilin JPEG USB V4L2 driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on this Jeilin chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_jeilinj.
-
-config USB_GSPCA_JL2005BCD
- tristate "JL2005B/C/D USB V4L2 driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based the
- JL2005B, JL2005C, or JL2005D chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_jl2005bcd.
-
-config USB_GSPCA_KINECT
- tristate "Kinect sensor device USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for the Microsoft Kinect sensor device.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_kinect.
-
-config USB_GSPCA_KONICA
- tristate "Konica USB Camera V4L2 driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Konica chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_konica.
-
-config USB_GSPCA_MARS
- tristate "Mars USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Mars chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_mars.
-
-config USB_GSPCA_MR97310A
- tristate "Mars-Semi MR97310A USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the MR97310A chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_mr97310a.
-
-config USB_GSPCA_NW80X
- tristate "Divio based (NW80x) USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the NW80x chips.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_nw80x.
-
-config USB_GSPCA_OV519
- tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on one of these:
- OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_ov519.
-
-config USB_GSPCA_OV534
- tristate "OV534 OV772x USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the OV534 chip
- and sensor OV772x (e.g. Sony Playstation EYE)
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_ov534.
-
-config USB_GSPCA_OV534_9
- tristate "OV534 OV965x USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the OV534 chip
- and sensor OV965x (e.g. Hercules Dualpix)
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_ov534_9.
-
-config USB_GSPCA_PAC207
- tristate "Pixart PAC207 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the PAC207 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_pac207.
-
-config USB_GSPCA_PAC7302
- tristate "Pixart PAC7302 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the PAC7302 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_pac7302.
-
-config USB_GSPCA_PAC7311
- tristate "Pixart PAC7311 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the PAC7311 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_pac7311.
-
-config USB_GSPCA_SE401
- tristate "SE401 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the
- Endpoints (formerly known as AOX) se401 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_se401.
-
-config USB_GSPCA_SN9C2028
- tristate "SONIX Dual-Mode USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want streaming support for Sonix SN9C2028 cameras.
- These are supported as stillcams in libgphoto2/camlibs/sonix.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sn9c2028.
-
-config USB_GSPCA_SN9C20X
- tristate "SN9C20X USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the
- sn9c20x chips (SN9C201 and SN9C202).
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sn9c20x.
-
-config USB_GSPCA_SONIXB
- tristate "SONIX Bayer USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Sonix
- chips with Bayer format (SN9C101, SN9C102 and SN9C103).
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sonixb.
-
-config USB_GSPCA_SONIXJ
- tristate "SONIX JPEG USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Sonix
- chips with JPEG format (SN9C102P, SN9C105 and >= SN9C110).
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sonixj
-
-config USB_GSPCA_SPCA500
- tristate "SPCA500 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA500 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca500.
-
-config USB_GSPCA_SPCA501
- tristate "SPCA501 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA501 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca501.
-
-config USB_GSPCA_SPCA505
- tristate "SPCA505 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA505 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca505.
-
-config USB_GSPCA_SPCA506
- tristate "SPCA506 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA506 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca506.
-
-config USB_GSPCA_SPCA508
- tristate "SPCA508 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA508 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca508.
-
-config USB_GSPCA_SPCA561
- tristate "SPCA561 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA561 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca561.
-
-config USB_GSPCA_SPCA1528
- tristate "SPCA1528 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SPCA1528 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_spca1528.
-
-config USB_GSPCA_SQ905
- tristate "SQ Technologies SQ905 based USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SQ905 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sq905.
-
-config USB_GSPCA_SQ905C
- tristate "SQ Technologies SQ905C based USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SQ905C chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sq905c.
-
-config USB_GSPCA_SQ930X
- tristate "SQ Technologies SQ930X based USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the SQ930X chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sq930x.
-
-config USB_GSPCA_STK014
- tristate "Syntek DV4000 (STK014) USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the STK014 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_stk014.
-
-config USB_GSPCA_STV0680
- tristate "STV0680 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the STV0680 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_stv0680.
-
-config USB_GSPCA_SUNPLUS
- tristate "SUNPLUS USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the Sunplus
- SPCA504(abc) SPCA533 SPCA536 chips.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_sunplus.
-
-config USB_GSPCA_T613
- tristate "T613 (JPEG Compliance) USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the T613 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_t613.
-
-config USB_GSPCA_TOPRO
- tristate "TOPRO USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the
- TP6800 and TP6810 Topro chips.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_topro.
-
-config USB_GSPCA_TV8532
- tristate "TV8532 USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the TV8531 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_tv8532.
-
-config USB_GSPCA_VC032X
- tristate "VC032X USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the VC032X chip.
-
- 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
- help
- Say Y here if you want support for Xirlink C-It bases cameras.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_xirlink_cit.
-
-config USB_GSPCA_ZC3XX
- tristate "ZC3XX USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
- Say Y here if you want support for cameras based on the ZC3XX chip.
-
- To compile this driver as a module, choose M here: the
- module will be called gspca_zc3xx.
-
-endif
diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
deleted file mode 100644
index 2499a881d9a3..000000000000
--- a/drivers/media/video/gspca/cpia1.c
+++ /dev/null
@@ -1,1905 +0,0 @@
-/*
- * cpia CPiA (1) gspca driver
- *
- * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
- *
- * This module is adapted from the in kernel v4l1 cpia driver which is :
- *
- * (C) Copyright 1999-2000 Peter Pregler
- * (C) Copyright 1999-2000 Scott J. Bertin
- * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
- * (C) Copyright 2000 STMicroelectronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "cpia1"
-
-#include <linux/input.h>
-#include "gspca.h"
-
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
-MODULE_DESCRIPTION("Vision CPiA");
-MODULE_LICENSE("GPL");
-
-/* constant value's */
-#define MAGIC_0 0x19
-#define MAGIC_1 0x68
-#define DATA_IN 0xc0
-#define DATA_OUT 0x40
-#define VIDEOSIZE_QCIF 0 /* 176x144 */
-#define VIDEOSIZE_CIF 1 /* 352x288 */
-#define SUBSAMPLE_420 0
-#define SUBSAMPLE_422 1
-#define YUVORDER_YUYV 0
-#define YUVORDER_UYVY 1
-#define NOT_COMPRESSED 0
-#define COMPRESSED 1
-#define NO_DECIMATION 0
-#define DECIMATION_ENAB 1
-#define EOI 0xff /* End Of Image */
-#define EOL 0xfd /* End Of Line */
-#define FRAME_HEADER_SIZE 64
-
-/* Image grab modes */
-#define CPIA_GRAB_SINGLE 0
-#define CPIA_GRAB_CONTINEOUS 1
-
-/* Compression parameters */
-#define CPIA_COMPRESSION_NONE 0
-#define CPIA_COMPRESSION_AUTO 1
-#define CPIA_COMPRESSION_MANUAL 2
-#define CPIA_COMPRESSION_TARGET_QUALITY 0
-#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
-
-/* Return offsets for GetCameraState */
-#define SYSTEMSTATE 0
-#define GRABSTATE 1
-#define STREAMSTATE 2
-#define FATALERROR 3
-#define CMDERROR 4
-#define DEBUGFLAGS 5
-#define VPSTATUS 6
-#define ERRORCODE 7
-
-/* SystemState */
-#define UNINITIALISED_STATE 0
-#define PASS_THROUGH_STATE 1
-#define LO_POWER_STATE 2
-#define HI_POWER_STATE 3
-#define WARM_BOOT_STATE 4
-
-/* GrabState */
-#define GRAB_IDLE 0
-#define GRAB_ACTIVE 1
-#define GRAB_DONE 2
-
-/* StreamState */
-#define STREAM_NOT_READY 0
-#define STREAM_READY 1
-#define STREAM_OPEN 2
-#define STREAM_PAUSED 3
-#define STREAM_FINISHED 4
-
-/* Fatal Error, CmdError, and DebugFlags */
-#define CPIA_FLAG 1
-#define SYSTEM_FLAG 2
-#define INT_CTRL_FLAG 4
-#define PROCESS_FLAG 8
-#define COM_FLAG 16
-#define VP_CTRL_FLAG 32
-#define CAPTURE_FLAG 64
-#define DEBUG_FLAG 128
-
-/* VPStatus */
-#define VP_STATE_OK 0x00
-
-#define VP_STATE_FAILED_VIDEOINIT 0x01
-#define VP_STATE_FAILED_AECACBINIT 0x02
-#define VP_STATE_AEC_MAX 0x04
-#define VP_STATE_ACB_BMAX 0x08
-
-#define VP_STATE_ACB_RMIN 0x10
-#define VP_STATE_ACB_GMIN 0x20
-#define VP_STATE_ACB_RMAX 0x40
-#define VP_STATE_ACB_GMAX 0x80
-
-/* default (minimum) compensation values */
-#define COMP_RED 220
-#define COMP_GREEN1 214
-#define COMP_GREEN2 COMP_GREEN1
-#define COMP_BLUE 230
-
-/* exposure status */
-#define EXPOSURE_VERY_LIGHT 0
-#define EXPOSURE_LIGHT 1
-#define EXPOSURE_NORMAL 2
-#define EXPOSURE_DARK 3
-#define EXPOSURE_VERY_DARK 4
-
-#define CPIA_MODULE_CPIA (0 << 5)
-#define CPIA_MODULE_SYSTEM (1 << 5)
-#define CPIA_MODULE_VP_CTRL (5 << 5)
-#define CPIA_MODULE_CAPTURE (6 << 5)
-#define CPIA_MODULE_DEBUG (7 << 5)
-
-#define INPUT (DATA_IN << 8)
-#define OUTPUT (DATA_OUT << 8)
-
-#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
-#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
-#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
-#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
-#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
-#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
-#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
-#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
-
-#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
-#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
-#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
-#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
-#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
-#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
-#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
-#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
-#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
-#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
-#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
-#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
-#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
-
-#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
-#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
-#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
-#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
-#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
-#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
-#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
-#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
-#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
-#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
-#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
-#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
-#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
-#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
-#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
-#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
-#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
-
-#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
-#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
-#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
-#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
-#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
-#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
-#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
-#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
-#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
-#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
-#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
-#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
-#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
-#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
-#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
-
-#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
-#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
-#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
-#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
-#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
-#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
-#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
-#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
-
-#define ROUND_UP_EXP_FOR_FLICKER 15
-
-/* Constants for automatic frame rate adjustment */
-#define MAX_EXP 302
-#define MAX_EXP_102 255
-#define LOW_EXP 140
-#define VERY_LOW_EXP 70
-#define TC 94
-#define EXP_ACC_DARK 50
-#define EXP_ACC_LIGHT 90
-#define HIGH_COMP_102 160
-#define MAX_COMP 239
-#define DARK_TIME 3
-#define LIGHT_TIME 3
-
-#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
- sd->params.version.firmwareRevision == (y))
-
-#define CPIA1_CID_COMP_TARGET (V4L2_CTRL_CLASS_USER + 0x1000)
-#define BRIGHTNESS_DEF 50
-#define CONTRAST_DEF 48
-#define SATURATION_DEF 50
-#define FREQ_DEF V4L2_CID_POWER_LINE_FREQUENCY_50HZ
-#define ILLUMINATORS_1_DEF 0
-#define ILLUMINATORS_2_DEF 0
-#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
-
-/* Developer's Guide Table 5 p 3-34
- * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
-static u8 flicker_jumps[2][2][4] =
-{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
- { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
-};
-
-struct cam_params {
- struct {
- u8 firmwareVersion;
- u8 firmwareRevision;
- u8 vcVersion;
- u8 vcRevision;
- } version;
- struct {
- u16 vendor;
- u16 product;
- u16 deviceRevision;
- } pnpID;
- struct {
- u8 vpVersion;
- u8 vpRevision;
- u16 cameraHeadID;
- } vpVersion;
- struct {
- u8 systemState;
- u8 grabState;
- u8 streamState;
- u8 fatalError;
- u8 cmdError;
- u8 debugFlags;
- u8 vpStatus;
- u8 errorCode;
- } status;
- struct {
- u8 brightness;
- u8 contrast;
- u8 saturation;
- } colourParams;
- struct {
- u8 gainMode;
- u8 expMode;
- u8 compMode;
- u8 centreWeight;
- u8 gain;
- u8 fineExp;
- u8 coarseExpLo;
- u8 coarseExpHi;
- u8 redComp;
- u8 green1Comp;
- u8 green2Comp;
- u8 blueComp;
- } exposure;
- struct {
- u8 balanceMode;
- u8 redGain;
- u8 greenGain;
- u8 blueGain;
- } colourBalance;
- struct {
- u8 divisor;
- u8 baserate;
- } sensorFps;
- struct {
- u8 gain1;
- u8 gain2;
- u8 gain4;
- u8 gain8;
- } apcor;
- struct {
- u8 disabled;
- u8 flickerMode;
- u8 coarseJump;
- u8 allowableOverExposure;
- } flickerControl;
- struct {
- u8 gain1;
- u8 gain2;
- u8 gain4;
- u8 gain8;
- } vlOffset;
- struct {
- u8 mode;
- u8 decimation;
- } compression;
- struct {
- u8 frTargeting;
- u8 targetFR;
- u8 targetQ;
- } compressionTarget;
- struct {
- u8 yThreshold;
- u8 uvThreshold;
- } yuvThreshold;
- struct {
- u8 hysteresis;
- u8 threshMax;
- u8 smallStep;
- u8 largeStep;
- u8 decimationHysteresis;
- u8 frDiffStepThresh;
- u8 qDiffStepThresh;
- u8 decimationThreshMod;
- } compressionParams;
- struct {
- u8 videoSize; /* CIF/QCIF */
- u8 subSample;
- u8 yuvOrder;
- } format;
- struct { /* Intel QX3 specific data */
- u8 qx3_detected; /* a QX3 is present */
- u8 toplight; /* top light lit , R/W */
- u8 bottomlight; /* bottom light lit, R/W */
- u8 button; /* snapshot button pressed (R/O) */
- u8 cradled; /* microscope is in cradle (R/O) */
- } qx3;
- struct {
- u8 colStart; /* skip first 8*colStart pixels */
- u8 colEnd; /* finish at 8*colEnd pixels */
- u8 rowStart; /* skip first 4*rowStart lines */
- u8 rowEnd; /* finish at 4*rowEnd lines */
- } roi;
- u8 ecpTiming;
- u8 streamStartLine;
-};
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- struct cam_params params; /* camera settings */
-
- atomic_t cam_exposure;
- atomic_t fps;
- int exposure_count;
- u8 exposure_status;
- struct v4l2_ctrl *freq;
- u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
- u8 first_frame;
-};
-
-static const struct v4l2_pix_format mode[] = {
- {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
- /* The sizeimage is trial and error, as with low framerates
- the camera will pad out usb frames, making the image
- data larger then strictly necessary */
- .bytesperline = 160,
- .sizeimage = 65536,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
- .bytesperline = 172,
- .sizeimage = 65536,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 2},
- {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 262144,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 262144,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-
-/**********************************************************************
- *
- * General functions
- *
- **********************************************************************/
-
-static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
-{
- u8 requesttype;
- unsigned int pipe;
- int ret, databytes = command[6] | (command[7] << 8);
- /* Sometimes we see spurious EPIPE errors */
- int retries = 3;
-
- if (command[0] == DATA_IN) {
- pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
- requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
- } else if (command[0] == DATA_OUT) {
- pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
- requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
- } else {
- PDEBUG(D_ERR, "Unexpected first byte of command: %x",
- command[0]);
- return -EINVAL;
- }
-
-retry:
- ret = usb_control_msg(gspca_dev->dev, pipe,
- command[1],
- requesttype,
- command[2] | (command[3] << 8),
- command[4] | (command[5] << 8),
- gspca_dev->usb_buf, databytes, 1000);
-
- if (ret < 0)
- pr_err("usb_control_msg %02x, error %d\n", command[1], ret);
-
- if (ret == -EPIPE && retries > 0) {
- retries--;
- goto retry;
- }
-
- return (ret < 0) ? ret : 0;
-}
-
-/* send an arbitrary command to the camera */
-static int do_command(struct gspca_dev *gspca_dev, u16 command,
- u8 a, u8 b, u8 c, u8 d)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret, datasize;
- u8 cmd[8];
-
- switch (command) {
- case CPIA_COMMAND_GetCPIAVersion:
- case CPIA_COMMAND_GetPnPID:
- case CPIA_COMMAND_GetCameraStatus:
- case CPIA_COMMAND_GetVPVersion:
- case CPIA_COMMAND_GetColourParams:
- case CPIA_COMMAND_GetColourBalance:
- case CPIA_COMMAND_GetExposure:
- datasize = 8;
- break;
- case CPIA_COMMAND_ReadMCPorts:
- case CPIA_COMMAND_ReadVCRegs:
- datasize = 4;
- break;
- default:
- datasize = 0;
- break;
- }
-
- cmd[0] = command >> 8;
- cmd[1] = command & 0xff;
- cmd[2] = a;
- cmd[3] = b;
- cmd[4] = c;
- cmd[5] = d;
- cmd[6] = datasize;
- cmd[7] = 0;
-
- ret = cpia_usb_transferCmd(gspca_dev, cmd);
- if (ret)
- return ret;
-
- switch (command) {
- case CPIA_COMMAND_GetCPIAVersion:
- sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
- sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
- sd->params.version.vcVersion = gspca_dev->usb_buf[2];
- sd->params.version.vcRevision = gspca_dev->usb_buf[3];
- break;
- case CPIA_COMMAND_GetPnPID:
- sd->params.pnpID.vendor =
- gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
- sd->params.pnpID.product =
- gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
- sd->params.pnpID.deviceRevision =
- gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
- break;
- case CPIA_COMMAND_GetCameraStatus:
- sd->params.status.systemState = gspca_dev->usb_buf[0];
- sd->params.status.grabState = gspca_dev->usb_buf[1];
- sd->params.status.streamState = gspca_dev->usb_buf[2];
- sd->params.status.fatalError = gspca_dev->usb_buf[3];
- sd->params.status.cmdError = gspca_dev->usb_buf[4];
- sd->params.status.debugFlags = gspca_dev->usb_buf[5];
- sd->params.status.vpStatus = gspca_dev->usb_buf[6];
- sd->params.status.errorCode = gspca_dev->usb_buf[7];
- break;
- case CPIA_COMMAND_GetVPVersion:
- sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
- sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
- sd->params.vpVersion.cameraHeadID =
- gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
- break;
- case CPIA_COMMAND_GetColourParams:
- sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
- sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
- sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
- break;
- case CPIA_COMMAND_GetColourBalance:
- sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
- sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
- sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
- break;
- case CPIA_COMMAND_GetExposure:
- sd->params.exposure.gain = gspca_dev->usb_buf[0];
- sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
- sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
- sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
- sd->params.exposure.redComp = gspca_dev->usb_buf[4];
- sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
- sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
- sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
- break;
-
- case CPIA_COMMAND_ReadMCPorts:
- /* test button press */
- a = ((gspca_dev->usb_buf[1] & 0x02) == 0);
- if (a != sd->params.qx3.button) {
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, a);
- input_sync(gspca_dev->input_dev);
-#endif
- sd->params.qx3.button = a;
- }
- if (sd->params.qx3.button) {
- /* button pressed - unlock the latch */
- do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
- 3, 0xdf, 0xdf, 0);
- do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
- 3, 0xff, 0xff, 0);
- }
-
- /* test whether microscope is cradled */
- sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
- break;
- }
-
- return 0;
-}
-
-/* send a command to the camera with an additional data transaction */
-static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
- u8 a, u8 b, u8 c, u8 d,
- u8 e, u8 f, u8 g, u8 h,
- u8 i, u8 j, u8 k, u8 l)
-{
- u8 cmd[8];
-
- cmd[0] = command >> 8;
- cmd[1] = command & 0xff;
- cmd[2] = a;
- cmd[3] = b;
- cmd[4] = c;
- cmd[5] = d;
- cmd[6] = 8;
- cmd[7] = 0;
- gspca_dev->usb_buf[0] = e;
- gspca_dev->usb_buf[1] = f;
- gspca_dev->usb_buf[2] = g;
- gspca_dev->usb_buf[3] = h;
- gspca_dev->usb_buf[4] = i;
- gspca_dev->usb_buf[5] = j;
- gspca_dev->usb_buf[6] = k;
- gspca_dev->usb_buf[7] = l;
-
- return cpia_usb_transferCmd(gspca_dev, cmd);
-}
-
-/* find_over_exposure
- * Finds a suitable value of OverExposure for use with SetFlickerCtrl
- * Some calculation is required because this value changes with the brightness
- * set with SetColourParameters
- *
- * Parameters: Brightness - last brightness value set with SetColourParameters
- *
- * Returns: OverExposure value to use with SetFlickerCtrl
- */
-#define FLICKER_MAX_EXPOSURE 250
-#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
-#define FLICKER_BRIGHTNESS_CONSTANT 59
-static int find_over_exposure(int brightness)
-{
- int MaxAllowableOverExposure, OverExposure;
-
- MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
- FLICKER_BRIGHTNESS_CONSTANT;
-
- if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
- OverExposure = MaxAllowableOverExposure;
- else
- OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
-
- return OverExposure;
-}
-#undef FLICKER_MAX_EXPOSURE
-#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
-#undef FLICKER_BRIGHTNESS_CONSTANT
-
-/* initialise cam_data structure */
-static void reset_camera_params(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam_params *params = &sd->params;
-
- /* The following parameter values are the defaults from
- * "Software Developer's Guide for CPiA Cameras". Any changes
- * to the defaults are noted in comments. */
- params->colourParams.brightness = BRIGHTNESS_DEF;
- params->colourParams.contrast = CONTRAST_DEF;
- params->colourParams.saturation = SATURATION_DEF;
- params->exposure.gainMode = 4;
- params->exposure.expMode = 2; /* AEC */
- params->exposure.compMode = 1;
- params->exposure.centreWeight = 1;
- params->exposure.gain = 0;
- params->exposure.fineExp = 0;
- params->exposure.coarseExpLo = 185;
- params->exposure.coarseExpHi = 0;
- params->exposure.redComp = COMP_RED;
- params->exposure.green1Comp = COMP_GREEN1;
- params->exposure.green2Comp = COMP_GREEN2;
- params->exposure.blueComp = COMP_BLUE;
- params->colourBalance.balanceMode = 2; /* ACB */
- params->colourBalance.redGain = 32;
- params->colourBalance.greenGain = 6;
- params->colourBalance.blueGain = 92;
- params->apcor.gain1 = 0x18;
- params->apcor.gain2 = 0x16;
- params->apcor.gain4 = 0x24;
- params->apcor.gain8 = 0x34;
- params->vlOffset.gain1 = 20;
- params->vlOffset.gain2 = 24;
- params->vlOffset.gain4 = 26;
- params->vlOffset.gain8 = 26;
- params->compressionParams.hysteresis = 3;
- params->compressionParams.threshMax = 11;
- params->compressionParams.smallStep = 1;
- params->compressionParams.largeStep = 3;
- params->compressionParams.decimationHysteresis = 2;
- params->compressionParams.frDiffStepThresh = 5;
- params->compressionParams.qDiffStepThresh = 3;
- params->compressionParams.decimationThreshMod = 2;
- /* End of default values from Software Developer's Guide */
-
- /* Set Sensor FPS to 15fps. This seems better than 30fps
- * for indoor lighting. */
- params->sensorFps.divisor = 1;
- params->sensorFps.baserate = 1;
-
- params->flickerControl.flickerMode = 0;
- params->flickerControl.disabled = 1;
- params->flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [params->sensorFps.baserate]
- [params->sensorFps.divisor];
- params->flickerControl.allowableOverExposure =
- find_over_exposure(params->colourParams.brightness);
-
- params->yuvThreshold.yThreshold = 6; /* From windows driver */
- params->yuvThreshold.uvThreshold = 6; /* From windows driver */
-
- params->format.subSample = SUBSAMPLE_420;
- params->format.yuvOrder = YUVORDER_YUYV;
-
- params->compression.mode = CPIA_COMPRESSION_AUTO;
- params->compression.decimation = NO_DECIMATION;
-
- params->compressionTarget.frTargeting = COMP_TARGET_DEF;
- params->compressionTarget.targetFR = 15; /* From windows driver */
- params->compressionTarget.targetQ = 5; /* From windows driver */
-
- params->qx3.qx3_detected = 0;
- params->qx3.toplight = 0;
- params->qx3.bottomlight = 0;
- params->qx3.button = 0;
- params->qx3.cradled = 0;
-}
-
-static void printstatus(struct cam_params *params)
-{
- PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
- params->status.systemState, params->status.grabState,
- params->status.streamState, params->status.fatalError,
- params->status.cmdError, params->status.debugFlags,
- params->status.vpStatus, params->status.errorCode);
-}
-
-static int goto_low_power(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- if (sd->params.status.systemState != LO_POWER_STATE) {
- if (sd->params.status.systemState != WARM_BOOT_STATE) {
- PDEBUG(D_ERR,
- "unexpected state after lo power cmd: %02x",
- sd->params.status.systemState);
- printstatus(&sd->params);
- }
- return -EIO;
- }
-
- PDEBUG(D_CONF, "camera now in LOW power state");
- return 0;
-}
-
-static int goto_high_power(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- msleep_interruptible(40); /* windows driver does it too */
-
- if (signal_pending(current))
- return -EINTR;
-
- do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- if (sd->params.status.systemState != HI_POWER_STATE) {
- PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
- sd->params.status.systemState);
- printstatus(&sd->params);
- return -EIO;
- }
-
- PDEBUG(D_CONF, "camera now in HIGH power state");
- return 0;
-}
-
-static int get_version_information(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- /* GetCPIAVersion */
- ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- /* GetPnPID */
- return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
-}
-
-static int save_camera_state(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
-}
-
-static int command_setformat(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
- sd->params.format.videoSize,
- sd->params.format.subSample,
- sd->params.format.yuvOrder, 0);
- if (ret)
- return ret;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetROI,
- sd->params.roi.colStart, sd->params.roi.colEnd,
- sd->params.roi.rowStart, sd->params.roi.rowEnd);
-}
-
-static int command_setcolourparams(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
- sd->params.colourParams.brightness,
- sd->params.colourParams.contrast,
- sd->params.colourParams.saturation, 0);
-}
-
-static int command_setapcor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
- sd->params.apcor.gain1,
- sd->params.apcor.gain2,
- sd->params.apcor.gain4,
- sd->params.apcor.gain8);
-}
-
-static int command_setvloffset(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
- sd->params.vlOffset.gain1,
- sd->params.vlOffset.gain2,
- sd->params.vlOffset.gain4,
- sd->params.vlOffset.gain8);
-}
-
-static int command_setexposure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
- sd->params.exposure.gainMode,
- 1,
- sd->params.exposure.compMode,
- sd->params.exposure.centreWeight,
- sd->params.exposure.gain,
- sd->params.exposure.fineExp,
- sd->params.exposure.coarseExpLo,
- sd->params.exposure.coarseExpHi,
- sd->params.exposure.redComp,
- sd->params.exposure.green1Comp,
- sd->params.exposure.green2Comp,
- sd->params.exposure.blueComp);
- if (ret)
- return ret;
-
- if (sd->params.exposure.expMode != 1) {
- ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
- 0,
- sd->params.exposure.expMode,
- 0, 0,
- sd->params.exposure.gain,
- sd->params.exposure.fineExp,
- sd->params.exposure.coarseExpLo,
- sd->params.exposure.coarseExpHi,
- 0, 0, 0, 0);
- }
-
- return ret;
-}
-
-static int command_setcolourbalance(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->params.colourBalance.balanceMode == 1) {
- int ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
- 1,
- sd->params.colourBalance.redGain,
- sd->params.colourBalance.greenGain,
- sd->params.colourBalance.blueGain);
- if (ret)
- return ret;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
- 3, 0, 0, 0);
- }
- if (sd->params.colourBalance.balanceMode == 2) {
- return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
- 2, 0, 0, 0);
- }
- if (sd->params.colourBalance.balanceMode == 3) {
- return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
- 3, 0, 0, 0);
- }
-
- return -EINVAL;
-}
-
-static int command_setcompressiontarget(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
- sd->params.compressionTarget.frTargeting,
- sd->params.compressionTarget.targetFR,
- sd->params.compressionTarget.targetQ, 0);
-}
-
-static int command_setyuvtresh(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
- sd->params.yuvThreshold.yThreshold,
- sd->params.yuvThreshold.uvThreshold, 0, 0);
-}
-
-static int command_setcompressionparams(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command_extended(gspca_dev,
- CPIA_COMMAND_SetCompressionParams,
- 0, 0, 0, 0,
- sd->params.compressionParams.hysteresis,
- sd->params.compressionParams.threshMax,
- sd->params.compressionParams.smallStep,
- sd->params.compressionParams.largeStep,
- sd->params.compressionParams.decimationHysteresis,
- sd->params.compressionParams.frDiffStepThresh,
- sd->params.compressionParams.qDiffStepThresh,
- sd->params.compressionParams.decimationThreshMod);
-}
-
-static int command_setcompression(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
- sd->params.compression.mode,
- sd->params.compression.decimation, 0, 0);
-}
-
-static int command_setsensorfps(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
- sd->params.sensorFps.divisor,
- sd->params.sensorFps.baserate, 0, 0);
-}
-
-static int command_setflickerctrl(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
- sd->params.flickerControl.flickerMode,
- sd->params.flickerControl.coarseJump,
- sd->params.flickerControl.allowableOverExposure,
- 0);
-}
-
-static int command_setecptiming(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
- sd->params.ecpTiming, 0, 0, 0);
-}
-
-static int command_pause(struct gspca_dev *gspca_dev)
-{
- return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
-}
-
-static int command_resume(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
- 0, sd->params.streamStartLine, 0, 0);
-}
-
-static int command_setlights(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret, p1, p2;
-
- p1 = (sd->params.qx3.bottomlight == 0) << 1;
- p2 = (sd->params.qx3.toplight == 0) << 3;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
- 0x90, 0x8f, 0x50, 0);
- if (ret)
- return ret;
-
- return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
- p1 | p2 | 0xe0, 0);
-}
-
-static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
-{
- /* Everything in here is from the Windows driver */
-/* define for compgain calculation */
-#if 0
-#define COMPGAIN(base, curexp, newexp) \
- (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
-#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
- (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
- (float)(u8)(basecomp - 128))
-#else
- /* equivalent functions without floating point math */
-#define COMPGAIN(base, curexp, newexp) \
- (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
-#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
- (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
-#endif
-
- struct sd *sd = (struct sd *) gspca_dev;
- int currentexp = sd->params.exposure.coarseExpLo +
- sd->params.exposure.coarseExpHi * 256;
- int ret, startexp;
-
- if (on) {
- int cj = sd->params.flickerControl.coarseJump;
- sd->params.flickerControl.flickerMode = 1;
- sd->params.flickerControl.disabled = 0;
- if (sd->params.exposure.expMode != 2) {
- sd->params.exposure.expMode = 2;
- sd->exposure_status = EXPOSURE_NORMAL;
- }
- currentexp = currentexp << sd->params.exposure.gain;
- sd->params.exposure.gain = 0;
- /* round down current exposure to nearest value */
- startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
- if (startexp < 1)
- startexp = 1;
- startexp = (startexp * cj) - 1;
- if (FIRMWARE_VERSION(1, 2))
- while (startexp > MAX_EXP_102)
- startexp -= cj;
- else
- while (startexp > MAX_EXP)
- startexp -= cj;
- sd->params.exposure.coarseExpLo = startexp & 0xff;
- sd->params.exposure.coarseExpHi = startexp >> 8;
- if (currentexp > startexp) {
- if (currentexp > (2 * startexp))
- currentexp = 2 * startexp;
- sd->params.exposure.redComp =
- COMPGAIN(COMP_RED, currentexp, startexp);
- sd->params.exposure.green1Comp =
- COMPGAIN(COMP_GREEN1, currentexp, startexp);
- sd->params.exposure.green2Comp =
- COMPGAIN(COMP_GREEN2, currentexp, startexp);
- sd->params.exposure.blueComp =
- COMPGAIN(COMP_BLUE, currentexp, startexp);
- } else {
- sd->params.exposure.redComp = COMP_RED;
- sd->params.exposure.green1Comp = COMP_GREEN1;
- sd->params.exposure.green2Comp = COMP_GREEN2;
- sd->params.exposure.blueComp = COMP_BLUE;
- }
- if (FIRMWARE_VERSION(1, 2))
- sd->params.exposure.compMode = 0;
- else
- sd->params.exposure.compMode = 1;
-
- sd->params.apcor.gain1 = 0x18;
- sd->params.apcor.gain2 = 0x18;
- sd->params.apcor.gain4 = 0x16;
- sd->params.apcor.gain8 = 0x14;
- } else {
- sd->params.flickerControl.flickerMode = 0;
- sd->params.flickerControl.disabled = 1;
- /* Average equivalent coarse for each comp channel */
- startexp = EXP_FROM_COMP(COMP_RED,
- sd->params.exposure.redComp, currentexp);
- startexp += EXP_FROM_COMP(COMP_GREEN1,
- sd->params.exposure.green1Comp, currentexp);
- startexp += EXP_FROM_COMP(COMP_GREEN2,
- sd->params.exposure.green2Comp, currentexp);
- startexp += EXP_FROM_COMP(COMP_BLUE,
- sd->params.exposure.blueComp, currentexp);
- startexp = startexp >> 2;
- while (startexp > MAX_EXP && sd->params.exposure.gain <
- sd->params.exposure.gainMode - 1) {
- startexp = startexp >> 1;
- ++sd->params.exposure.gain;
- }
- if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
- startexp = MAX_EXP_102;
- if (startexp > MAX_EXP)
- startexp = MAX_EXP;
- sd->params.exposure.coarseExpLo = startexp & 0xff;
- sd->params.exposure.coarseExpHi = startexp >> 8;
- sd->params.exposure.redComp = COMP_RED;
- sd->params.exposure.green1Comp = COMP_GREEN1;
- sd->params.exposure.green2Comp = COMP_GREEN2;
- sd->params.exposure.blueComp = COMP_BLUE;
- sd->params.exposure.compMode = 1;
- sd->params.apcor.gain1 = 0x18;
- sd->params.apcor.gain2 = 0x16;
- sd->params.apcor.gain4 = 0x24;
- sd->params.apcor.gain8 = 0x34;
- }
- sd->params.vlOffset.gain1 = 20;
- sd->params.vlOffset.gain2 = 24;
- sd->params.vlOffset.gain4 = 26;
- sd->params.vlOffset.gain8 = 26;
-
- if (apply) {
- ret = command_setexposure(gspca_dev);
- if (ret)
- return ret;
-
- ret = command_setapcor(gspca_dev);
- if (ret)
- return ret;
-
- ret = command_setvloffset(gspca_dev);
- if (ret)
- return ret;
-
- ret = command_setflickerctrl(gspca_dev);
- if (ret)
- return ret;
- }
-
- return 0;
-#undef EXP_FROM_COMP
-#undef COMPGAIN
-}
-
-/* monitor the exposure and adjust the sensor frame rate if needed */
-static void monitor_exposure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 exp_acc, bcomp, cmd[8];
- int ret, light_exp, dark_exp, very_dark_exp;
- int old_exposure, new_exposure, framerate;
- int setfps = 0, setexp = 0, setflicker = 0;
-
- /* get necessary stats and register settings from camera */
- /* do_command can't handle this, so do it ourselves */
- cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
- cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
- cmd[2] = 30;
- cmd[3] = 4;
- cmd[4] = 9;
- cmd[5] = 8;
- cmd[6] = 8;
- cmd[7] = 0;
- ret = cpia_usb_transferCmd(gspca_dev, cmd);
- if (ret) {
- pr_err("ReadVPRegs(30,4,9,8) - failed: %d\n", ret);
- return;
- }
- exp_acc = gspca_dev->usb_buf[0];
- bcomp = gspca_dev->usb_buf[1];
-
- light_exp = sd->params.colourParams.brightness +
- TC - 50 + EXP_ACC_LIGHT;
- if (light_exp > 255)
- light_exp = 255;
- dark_exp = sd->params.colourParams.brightness +
- TC - 50 - EXP_ACC_DARK;
- if (dark_exp < 0)
- dark_exp = 0;
- very_dark_exp = dark_exp / 2;
-
- old_exposure = sd->params.exposure.coarseExpHi * 256 +
- sd->params.exposure.coarseExpLo;
-
- if (!sd->params.flickerControl.disabled) {
- /* Flicker control on */
- int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
- HIGH_COMP_102;
- bcomp += 128; /* decode */
- if (bcomp >= max_comp && exp_acc < dark_exp) {
- /* dark */
- if (exp_acc < very_dark_exp) {
- /* very dark */
- if (sd->exposure_status == EXPOSURE_VERY_DARK)
- ++sd->exposure_count;
- else {
- sd->exposure_status =
- EXPOSURE_VERY_DARK;
- sd->exposure_count = 1;
- }
- } else {
- /* just dark */
- if (sd->exposure_status == EXPOSURE_DARK)
- ++sd->exposure_count;
- else {
- sd->exposure_status = EXPOSURE_DARK;
- sd->exposure_count = 1;
- }
- }
- } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
- /* light */
- if (old_exposure <= VERY_LOW_EXP) {
- /* very light */
- if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
- ++sd->exposure_count;
- else {
- sd->exposure_status =
- EXPOSURE_VERY_LIGHT;
- sd->exposure_count = 1;
- }
- } else {
- /* just light */
- if (sd->exposure_status == EXPOSURE_LIGHT)
- ++sd->exposure_count;
- else {
- sd->exposure_status = EXPOSURE_LIGHT;
- sd->exposure_count = 1;
- }
- }
- } else {
- /* not dark or light */
- sd->exposure_status = EXPOSURE_NORMAL;
- }
- } else {
- /* Flicker control off */
- if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
- /* dark */
- if (exp_acc < very_dark_exp) {
- /* very dark */
- if (sd->exposure_status == EXPOSURE_VERY_DARK)
- ++sd->exposure_count;
- else {
- sd->exposure_status =
- EXPOSURE_VERY_DARK;
- sd->exposure_count = 1;
- }
- } else {
- /* just dark */
- if (sd->exposure_status == EXPOSURE_DARK)
- ++sd->exposure_count;
- else {
- sd->exposure_status = EXPOSURE_DARK;
- sd->exposure_count = 1;
- }
- }
- } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
- /* light */
- if (old_exposure <= VERY_LOW_EXP) {
- /* very light */
- if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
- ++sd->exposure_count;
- else {
- sd->exposure_status =
- EXPOSURE_VERY_LIGHT;
- sd->exposure_count = 1;
- }
- } else {
- /* just light */
- if (sd->exposure_status == EXPOSURE_LIGHT)
- ++sd->exposure_count;
- else {
- sd->exposure_status = EXPOSURE_LIGHT;
- sd->exposure_count = 1;
- }
- }
- } else {
- /* not dark or light */
- sd->exposure_status = EXPOSURE_NORMAL;
- }
- }
-
- framerate = atomic_read(&sd->fps);
- if (framerate > 30 || framerate < 1)
- framerate = 1;
-
- if (!sd->params.flickerControl.disabled) {
- /* Flicker control on */
- if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
- sd->exposure_status == EXPOSURE_DARK) &&
- sd->exposure_count >= DARK_TIME * framerate &&
- sd->params.sensorFps.divisor < 2) {
-
- /* dark for too long */
- ++sd->params.sensorFps.divisor;
- setfps = 1;
-
- sd->params.flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [sd->params.sensorFps.baserate]
- [sd->params.sensorFps.divisor];
- setflicker = 1;
-
- new_exposure = sd->params.flickerControl.coarseJump-1;
- while (new_exposure < old_exposure / 2)
- new_exposure +=
- sd->params.flickerControl.coarseJump;
- sd->params.exposure.coarseExpLo = new_exposure & 0xff;
- sd->params.exposure.coarseExpHi = new_exposure >> 8;
- setexp = 1;
- sd->exposure_status = EXPOSURE_NORMAL;
- PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
-
- } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
- sd->exposure_status == EXPOSURE_LIGHT) &&
- sd->exposure_count >= LIGHT_TIME * framerate &&
- sd->params.sensorFps.divisor > 0) {
-
- /* light for too long */
- int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
- MAX_EXP;
- --sd->params.sensorFps.divisor;
- setfps = 1;
-
- sd->params.flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [sd->params.sensorFps.baserate]
- [sd->params.sensorFps.divisor];
- setflicker = 1;
-
- new_exposure = sd->params.flickerControl.coarseJump-1;
- while (new_exposure < 2 * old_exposure &&
- new_exposure +
- sd->params.flickerControl.coarseJump < max_exp)
- new_exposure +=
- sd->params.flickerControl.coarseJump;
- sd->params.exposure.coarseExpLo = new_exposure & 0xff;
- sd->params.exposure.coarseExpHi = new_exposure >> 8;
- setexp = 1;
- sd->exposure_status = EXPOSURE_NORMAL;
- PDEBUG(D_CONF, "Automatically increasing sensor_fps");
- }
- } else {
- /* Flicker control off */
- if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
- sd->exposure_status == EXPOSURE_DARK) &&
- sd->exposure_count >= DARK_TIME * framerate &&
- sd->params.sensorFps.divisor < 2) {
-
- /* dark for too long */
- ++sd->params.sensorFps.divisor;
- setfps = 1;
-
- if (sd->params.exposure.gain > 0) {
- --sd->params.exposure.gain;
- setexp = 1;
- }
- sd->exposure_status = EXPOSURE_NORMAL;
- PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
-
- } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
- sd->exposure_status == EXPOSURE_LIGHT) &&
- sd->exposure_count >= LIGHT_TIME * framerate &&
- sd->params.sensorFps.divisor > 0) {
-
- /* light for too long */
- --sd->params.sensorFps.divisor;
- setfps = 1;
-
- if (sd->params.exposure.gain <
- sd->params.exposure.gainMode - 1) {
- ++sd->params.exposure.gain;
- setexp = 1;
- }
- sd->exposure_status = EXPOSURE_NORMAL;
- PDEBUG(D_CONF, "Automatically increasing sensor_fps");
- }
- }
-
- if (setexp)
- command_setexposure(gspca_dev);
-
- if (setfps)
- command_setsensorfps(gspca_dev);
-
- if (setflicker)
- command_setflickerctrl(gspca_dev);
-}
-
-/*-----------------------------------------------------------------*/
-/* if flicker is switched off, this function switches it back on.It checks,
- however, that conditions are suitable before restarting it.
- This should only be called for firmware version 1.2.
-
- It also adjust the colour balance when an exposure step is detected - as
- long as flicker is running
-*/
-static void restart_flicker(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int cam_exposure, old_exp;
-
- if (!FIRMWARE_VERSION(1, 2))
- return;
-
- cam_exposure = atomic_read(&sd->cam_exposure);
-
- if (sd->params.flickerControl.flickerMode == 0 ||
- cam_exposure == 0)
- return;
-
- old_exp = sd->params.exposure.coarseExpLo +
- sd->params.exposure.coarseExpHi*256;
- /*
- see how far away camera exposure is from a valid
- flicker exposure value
- */
- cam_exposure %= sd->params.flickerControl.coarseJump;
- if (!sd->params.flickerControl.disabled &&
- cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
- /* Flicker control auto-disabled */
- sd->params.flickerControl.disabled = 1;
- }
-
- if (sd->params.flickerControl.disabled &&
- old_exp > sd->params.flickerControl.coarseJump +
- ROUND_UP_EXP_FOR_FLICKER) {
- /* exposure is now high enough to switch
- flicker control back on */
- set_flicker(gspca_dev, 1, 1);
- }
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- sd->mainsFreq = FREQ_DEF == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
- reset_camera_params(gspca_dev);
-
- PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
- id->idVendor, id->idProduct);
-
- cam = &gspca_dev->cam;
- cam->cam_mode = mode;
- cam->nmodes = ARRAY_SIZE(mode);
-
- goto_low_power(gspca_dev);
- /* Check the firmware version. */
- sd->params.version.firmwareVersion = 0;
- get_version_information(gspca_dev);
- if (sd->params.version.firmwareVersion != 1) {
- PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
- sd->params.version.firmwareVersion);
- return -ENODEV;
- }
-
- /* A bug in firmware 1-02 limits gainMode to 2 */
- if (sd->params.version.firmwareRevision <= 2 &&
- sd->params.exposure.gainMode > 2) {
- sd->params.exposure.gainMode = 2;
- }
-
- /* set QX3 detected flag */
- sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
- sd->params.pnpID.product == 0x0001);
- return 0;
-}
-
-/* -- start the camera -- */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int priv, ret;
-
- /* Start the camera in low power mode */
- if (goto_low_power(gspca_dev)) {
- if (sd->params.status.systemState != WARM_BOOT_STATE) {
- PDEBUG(D_ERR, "unexpected systemstate: %02x",
- sd->params.status.systemState);
- printstatus(&sd->params);
- return -ENODEV;
- }
-
- /* FIXME: this is just dirty trial and error */
- ret = goto_high_power(gspca_dev);
- if (ret)
- return ret;
-
- ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
- 0, 0, 0, 0);
- if (ret)
- return ret;
-
- ret = goto_low_power(gspca_dev);
- if (ret)
- return ret;
- }
-
- /* procedure described in developer's guide p3-28 */
-
- /* Check the firmware version. */
- sd->params.version.firmwareVersion = 0;
- get_version_information(gspca_dev);
-
- /* The fatal error checking should be done after
- * the camera powers up (developer's guide p 3-38) */
-
- /* Set streamState before transition to high power to avoid bug
- * in firmware 1-02 */
- ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
- STREAMSTATE, 0, STREAM_NOT_READY, 0);
- if (ret)
- return ret;
-
- /* GotoHiPower */
- ret = goto_high_power(gspca_dev);
- if (ret)
- return ret;
-
- /* Check the camera status */
- ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- if (sd->params.status.fatalError) {
- PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
- sd->params.status.fatalError,
- sd->params.status.vpStatus);
- return -EIO;
- }
-
- /* VPVersion can't be retrieved before the camera is in HiPower,
- * so get it here instead of in get_version_information. */
- ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
- if (ret)
- return ret;
-
- /* Determine video mode settings */
- sd->params.streamStartLine = 120;
-
- priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- if (priv & 0x01) { /* crop */
- sd->params.roi.colStart = 2;
- sd->params.roi.rowStart = 6;
- } else {
- sd->params.roi.colStart = 0;
- sd->params.roi.rowStart = 0;
- }
-
- if (priv & 0x02) { /* quarter */
- sd->params.format.videoSize = VIDEOSIZE_QCIF;
- sd->params.roi.colStart /= 2;
- sd->params.roi.rowStart /= 2;
- sd->params.streamStartLine /= 2;
- } else
- sd->params.format.videoSize = VIDEOSIZE_CIF;
-
- sd->params.roi.colEnd = sd->params.roi.colStart +
- (gspca_dev->width >> 3);
- sd->params.roi.rowEnd = sd->params.roi.rowStart +
- (gspca_dev->height >> 2);
-
- /* And now set the camera to a known state */
- ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
- CPIA_GRAB_CONTINEOUS, 0, 0, 0);
- if (ret)
- return ret;
- /* We start with compression disabled, as we need one uncompressed
- frame to handle later compressed frames */
- ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
- CPIA_COMPRESSION_NONE,
- NO_DECIMATION, 0, 0);
- if (ret)
- return ret;
- ret = command_setcompressiontarget(gspca_dev);
- if (ret)
- return ret;
- ret = command_setcolourparams(gspca_dev);
- if (ret)
- return ret;
- ret = command_setformat(gspca_dev);
- if (ret)
- return ret;
- ret = command_setyuvtresh(gspca_dev);
- if (ret)
- return ret;
- ret = command_setecptiming(gspca_dev);
- if (ret)
- return ret;
- ret = command_setcompressionparams(gspca_dev);
- if (ret)
- return ret;
- ret = command_setexposure(gspca_dev);
- if (ret)
- return ret;
- ret = command_setcolourbalance(gspca_dev);
- if (ret)
- return ret;
- ret = command_setsensorfps(gspca_dev);
- if (ret)
- return ret;
- ret = command_setapcor(gspca_dev);
- if (ret)
- return ret;
- ret = command_setflickerctrl(gspca_dev);
- if (ret)
- return ret;
- ret = command_setvloffset(gspca_dev);
- if (ret)
- return ret;
-
- /* Start stream */
- ret = command_resume(gspca_dev);
- if (ret)
- return ret;
-
- /* Wait 6 frames before turning compression on for the sensor to get
- all settings and AEC/ACB to settle */
- sd->first_frame = 6;
- sd->exposure_status = EXPOSURE_NORMAL;
- sd->exposure_count = 0;
- atomic_set(&sd->cam_exposure, 0);
- atomic_set(&sd->fps, 0);
-
- return 0;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- command_pause(gspca_dev);
-
- /* save camera state for later open (developers guide ch 3.5.3) */
- save_camera_state(gspca_dev);
-
- /* GotoLoPower */
- goto_low_power(gspca_dev);
-
- /* Update the camera status */
- do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- /* If the last button state is pressed, release it now! */
- if (sd->params.qx3.button) {
- /* The camera latch will hold the pressed state until we reset
- the latch, so we do not reset sd->params.qx3.button now, to
- avoid a false keypress being reported the next sd_start */
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- }
-#endif
-}
-
-/* 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;
- int ret;
-
- /* Start / Stop the camera to make sure we are talking to
- a supported camera, and to get some information from it
- to print. */
- ret = sd_start(gspca_dev);
- if (ret)
- return ret;
-
- /* Ensure the QX3 illuminators' states are restored upon resume,
- or disable the illuminator controls, if this isn't a QX3 */
- if (sd->params.qx3.qx3_detected)
- command_setlights(gspca_dev);
-
- sd_stopN(gspca_dev);
-
- PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)",
- sd->params.version.firmwareVersion,
- sd->params.version.firmwareRevision,
- sd->params.version.vcVersion,
- sd->params.version.vcRevision);
- PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x",
- sd->params.pnpID.vendor, sd->params.pnpID.product,
- sd->params.pnpID.deviceRevision);
- PDEBUG(D_PROBE, "VP-Version: %d.%d %04x",
- sd->params.vpVersion.vpVersion,
- sd->params.vpVersion.vpRevision,
- sd->params.vpVersion.cameraHeadID);
-
- return 0;
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data,
- int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* Check for SOF */
- if (len >= 64 &&
- data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
- data[16] == sd->params.format.videoSize &&
- data[17] == sd->params.format.subSample &&
- data[18] == sd->params.format.yuvOrder &&
- data[24] == sd->params.roi.colStart &&
- data[25] == sd->params.roi.colEnd &&
- data[26] == sd->params.roi.rowStart &&
- data[27] == sd->params.roi.rowEnd) {
- u8 *image;
-
- atomic_set(&sd->cam_exposure, data[39] * 2);
- atomic_set(&sd->fps, data[41]);
-
- /* Check for proper EOF for last frame */
- image = gspca_dev->image;
- if (image != NULL &&
- gspca_dev->image_len > 4 &&
- image[gspca_dev->image_len - 4] == 0xff &&
- image[gspca_dev->image_len - 3] == 0xff &&
- image[gspca_dev->image_len - 2] == 0xff &&
- image[gspca_dev->image_len - 1] == 0xff)
- gspca_frame_add(gspca_dev, LAST_PACKET,
- NULL, 0);
-
- gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
- return;
- }
-
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static void sd_dq_callback(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* Set the normal compression settings once we have captured a
- few uncompressed frames (and AEC has hopefully settled) */
- if (sd->first_frame) {
- sd->first_frame--;
- if (sd->first_frame == 0)
- command_setcompression(gspca_dev);
- }
-
- /* Switch flicker control back on if it got turned off */
- restart_flicker(gspca_dev);
-
- /* If AEC is enabled, monitor the exposure and
- adjust the sensor frame rate if needed */
- if (sd->params.exposure.expMode == 2)
- monitor_exposure(gspca_dev);
-
- /* Update our knowledge of the camera state */
- do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
- do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- sd->params.colourParams.brightness = ctrl->val;
- sd->params.flickerControl.allowableOverExposure =
- find_over_exposure(sd->params.colourParams.brightness);
- gspca_dev->usb_err = command_setcolourparams(gspca_dev);
- if (!gspca_dev->usb_err)
- gspca_dev->usb_err = command_setflickerctrl(gspca_dev);
- break;
- case V4L2_CID_CONTRAST:
- sd->params.colourParams.contrast = ctrl->val;
- gspca_dev->usb_err = command_setcolourparams(gspca_dev);
- break;
- case V4L2_CID_SATURATION:
- sd->params.colourParams.saturation = ctrl->val;
- gspca_dev->usb_err = command_setcolourparams(gspca_dev);
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- sd->mainsFreq = ctrl->val == V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
- sd->params.flickerControl.coarseJump =
- flicker_jumps[sd->mainsFreq]
- [sd->params.sensorFps.baserate]
- [sd->params.sensorFps.divisor];
-
- gspca_dev->usb_err = set_flicker(gspca_dev,
- ctrl->val != V4L2_CID_POWER_LINE_FREQUENCY_DISABLED,
- gspca_dev->streaming);
- break;
- case V4L2_CID_ILLUMINATORS_1:
- sd->params.qx3.bottomlight = ctrl->val;
- gspca_dev->usb_err = command_setlights(gspca_dev);
- break;
- case V4L2_CID_ILLUMINATORS_2:
- sd->params.qx3.toplight = ctrl->val;
- gspca_dev->usb_err = command_setlights(gspca_dev);
- break;
- case CPIA1_CID_COMP_TARGET:
- sd->params.compressionTarget.frTargeting = ctrl->val;
- gspca_dev->usb_err = command_setcompressiontarget(gspca_dev);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- static const char * const comp_target_menu[] = {
- "Quality",
- "Framerate",
- NULL
- };
- static const struct v4l2_ctrl_config comp_target = {
- .ops = &sd_ctrl_ops,
- .id = CPIA1_CID_COMP_TARGET,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Compression Target",
- .qmenu = comp_target_menu,
- .max = 1,
- .def = COMP_TARGET_DEF,
- };
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 7);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 100, 1, BRIGHTNESS_DEF);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 96, 8, CONTRAST_DEF);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 0, 100, 1, SATURATION_DEF);
- sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
- FREQ_DEF);
- if (sd->params.qx3.qx3_detected) {
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_ILLUMINATORS_1, 0, 1, 1,
- ILLUMINATORS_1_DEF);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_ILLUMINATORS_2, 0, 1, 1,
- ILLUMINATORS_2_DEF);
- }
- v4l2_ctrl_new_custom(hdl, &comp_target, NULL);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .stopN = sd_stopN,
- .dq_callback = sd_dq_callback,
- .pkt_scan = sd_pkt_scan,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .other_input = 1,
-#endif
-};
-
-/* -- module initialisation -- */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x0553, 0x0002)},
- {USB_DEVICE(0x0813, 0x0001)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
deleted file mode 100644
index c8f2201cc35a..000000000000
--- a/drivers/media/video/gspca/finepix.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Fujifilm Finepix subdriver
- *
- * Copyright (C) 2008 Frank Zago
- *
- * This program is free software; you can redistribute it and/or modify
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "finepix"
-
-#include "gspca.h"
-
-MODULE_AUTHOR("Frank Zago <frank@zago.net>");
-MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
-MODULE_LICENSE("GPL");
-
-/* Default timeout, in ms */
-#define FPIX_TIMEOUT 250
-
-/* Maximum transfer size to use. The windows driver reads by chunks of
- * 0x2000 bytes, so do the same. Note: reading more seems to work
- * too. */
-#define FPIX_MAX_TRANSFER 0x2000
-
-/* Structure to hold all of our device specific stuff */
-struct usb_fpix {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
-};
-
-/* Delay after which claim the next frame. If the delay is too small,
- * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
- * will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
- * 30ms is bad while 35ms is perfect. */
-#define NEXT_FRAME_DELAY 35
-
-/* These cameras only support 320x200. */
-static const struct v4l2_pix_format fpix_mode[1] = {
- { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0}
-};
-
-/* send a command to the webcam */
-static int command(struct gspca_dev *gspca_dev,
- int order) /* 0: reset, 1: frame request */
-{
- static u8 order_values[2][12] = {
- {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */
- {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */
- };
-
- memcpy(gspca_dev->usb_buf, order_values[order], 12);
- return usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_GET_STATUS,
- USB_DIR_OUT | USB_TYPE_CLASS |
- USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
- 12, FPIX_TIMEOUT);
-}
-
-/* workqueue */
-static void dostream(struct work_struct *work)
-{
- struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct);
- struct gspca_dev *gspca_dev = &dev->gspca_dev;
- struct urb *urb = gspca_dev->urb[0];
- u8 *data = urb->transfer_buffer;
- int ret = 0;
- int len;
-
- /* synchronize with the main driver */
- mutex_lock(&gspca_dev->usb_lock);
- mutex_unlock(&gspca_dev->usb_lock);
- PDEBUG(D_STREAM, "dostream started");
-
- /* loop reading a frame */
-again:
- while (gspca_dev->dev && gspca_dev->streaming) {
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
-
- /* request a frame */
- mutex_lock(&gspca_dev->usb_lock);
- ret = command(gspca_dev, 1);
- mutex_unlock(&gspca_dev->usb_lock);
- if (ret < 0)
- break;
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
- if (!gspca_dev->dev || !gspca_dev->streaming)
- break;
-
- /* the frame comes in parts */
- for (;;) {
- ret = usb_bulk_msg(gspca_dev->dev,
- urb->pipe,
- data,
- FPIX_MAX_TRANSFER,
- &len, FPIX_TIMEOUT);
- if (ret < 0) {
- /* Most of the time we get a timeout
- * error. Just restart. */
- goto again;
- }
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- goto out;
-#endif
- if (!gspca_dev->dev || !gspca_dev->streaming)
- goto out;
- if (len < FPIX_MAX_TRANSFER ||
- (data[len - 2] == 0xff &&
- data[len - 1] == 0xd9)) {
-
- /* If the result is less than what was asked
- * for, then it's the end of the
- * frame. Sometimes the jpeg is not complete,
- * but there's nothing we can do. We also end
- * here if the the jpeg ends right at the end
- * of the frame. */
- gspca_frame_add(gspca_dev, LAST_PACKET,
- data, len);
- break;
- }
-
- /* got a partial image */
- gspca_frame_add(gspca_dev,
- gspca_dev->last_packet_type
- == LAST_PACKET
- ? FIRST_PACKET : INTER_PACKET,
- data, len);
- }
-
- /* We must wait before trying reading the next
- * frame. If we don't, or if the delay is too short,
- * the camera will disconnect. */
- msleep(NEXT_FRAME_DELAY);
- }
-
-out:
- PDEBUG(D_STREAM, "dostream stopped");
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
- struct cam *cam = &gspca_dev->cam;
-
- cam->cam_mode = fpix_mode;
- cam->nmodes = 1;
- cam->bulk = 1;
- cam->bulk_size = FPIX_MAX_TRANSFER;
-
- INIT_WORK(&dev->work_struct, dostream);
-
- return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- return 0;
-}
-
-/* start the camera */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
- int ret, len;
-
- /* Init the device */
- ret = command(gspca_dev, 0);
- if (ret < 0) {
- pr_err("init failed %d\n", ret);
- return ret;
- }
-
- /* Read the result of the command. Ignore the result, for it
- * varies with the device. */
- ret = usb_bulk_msg(gspca_dev->dev,
- gspca_dev->urb[0]->pipe,
- gspca_dev->urb[0]->transfer_buffer,
- FPIX_MAX_TRANSFER, &len,
- FPIX_TIMEOUT);
- if (ret < 0) {
- pr_err("usb_bulk_msg failed %d\n", ret);
- return ret;
- }
-
- /* Request a frame, but don't read it */
- ret = command(gspca_dev, 1);
- if (ret < 0) {
- pr_err("frame request failed %d\n", ret);
- return ret;
- }
-
- /* Again, reset bulk in endpoint */
- usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
-
- /* Start the workqueue function to do the streaming */
- dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
- queue_work(dev->work_thread, &dev->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 usb_fpix *dev = (struct usb_fpix *) gspca_dev;
-
- /* wait for the work queue to terminate */
- mutex_unlock(&gspca_dev->usb_lock);
- destroy_workqueue(dev->work_thread);
- mutex_lock(&gspca_dev->usb_lock);
- dev->work_thread = NULL;
-}
-
-/* Table of supported USB devices */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x04cb, 0x0104)},
- {USB_DEVICE(0x04cb, 0x0109)},
- {USB_DEVICE(0x04cb, 0x010b)},
- {USB_DEVICE(0x04cb, 0x010f)},
- {USB_DEVICE(0x04cb, 0x0111)},
- {USB_DEVICE(0x04cb, 0x0113)},
- {USB_DEVICE(0x04cb, 0x0115)},
- {USB_DEVICE(0x04cb, 0x0117)},
- {USB_DEVICE(0x04cb, 0x0119)},
- {USB_DEVICE(0x04cb, 0x011b)},
- {USB_DEVICE(0x04cb, 0x011d)},
- {USB_DEVICE(0x04cb, 0x0121)},
- {USB_DEVICE(0x04cb, 0x0123)},
- {USB_DEVICE(0x04cb, 0x0125)},
- {USB_DEVICE(0x04cb, 0x0127)},
- {USB_DEVICE(0x04cb, 0x0129)},
- {USB_DEVICE(0x04cb, 0x012b)},
- {USB_DEVICE(0x04cb, 0x012d)},
- {USB_DEVICE(0x04cb, 0x012f)},
- {USB_DEVICE(0x04cb, 0x0131)},
- {USB_DEVICE(0x04cb, 0x013b)},
- {USB_DEVICE(0x04cb, 0x013d)},
- {USB_DEVICE(0x04cb, 0x013f)},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .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 usb_fpix),
- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile
deleted file mode 100644
index 773ea3426561..000000000000
--- a/drivers/media/video/gspca/gl860/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-obj-$(CONFIG_USB_GL860) += gspca_gl860.o
-
-gspca_gl860-objs := gl860.o \
- gl860-mi1320.o \
- gl860-ov2640.o \
- gl860-ov9655.o \
- gl860-mi2020.o
-
-ccflags-y += -I$(srctree)/drivers/media/video/gspca
-
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
deleted file mode 100644
index d4e8343f5b10..000000000000
--- a/drivers/media/video/gspca/gspca.c
+++ /dev/null
@@ -1,2456 +0,0 @@
-/*
- * Main USB camera driver
- *
- * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
- *
- * Camera button input handling by Márton Németh
- * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define GSPCA_VERSION "2.14.0"
-
-#include <linux/init.h>
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/io.h>
-#include <asm/page.h>
-#include <linux/uaccess.h>
-#include <linux/ktime.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-#include "gspca.h"
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-#include <linux/input.h>
-#include <linux/usb/input.h>
-#endif
-
-/* global values */
-#define DEF_NURBS 3 /* default number of URBs */
-#if DEF_NURBS > MAX_NURBS
-#error "DEF_NURBS too big"
-#endif
-
-MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
-MODULE_DESCRIPTION("GSPCA USB Camera Driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(GSPCA_VERSION);
-
-#ifdef GSPCA_DEBUG
-int gspca_debug = D_ERR | D_PROBE;
-EXPORT_SYMBOL(gspca_debug);
-
-static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
-{
- if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') {
- PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d",
- txt,
- pixfmt & 0xff,
- (pixfmt >> 8) & 0xff,
- (pixfmt >> 16) & 0xff,
- pixfmt >> 24,
- w, h);
- } else {
- PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d",
- txt,
- pixfmt,
- w, h);
- }
-}
-#else
-#define PDEBUG_MODE(txt, pixfmt, w, h)
-#endif
-
-/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
-#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
-#define GSPCA_MEMORY_READ 7
-
-#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)
-
-/*
- * VMA operations.
- */
-static void gspca_vm_open(struct vm_area_struct *vma)
-{
- struct gspca_frame *frame = vma->vm_private_data;
-
- frame->vma_use_count++;
- frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED;
-}
-
-static void gspca_vm_close(struct vm_area_struct *vma)
-{
- struct gspca_frame *frame = vma->vm_private_data;
-
- if (--frame->vma_use_count <= 0)
- frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED;
-}
-
-static const struct vm_operations_struct gspca_vm_ops = {
- .open = gspca_vm_open,
- .close = gspca_vm_close,
-};
-
-/*
- * Input and interrupt endpoint handling functions
- */
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static void int_irq(struct urb *urb)
-{
- struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
- int ret;
-
- ret = urb->status;
- switch (ret) {
- case 0:
- if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
- urb->transfer_buffer, urb->actual_length) < 0) {
- PDEBUG(D_ERR, "Unknown packet received");
- }
- break;
-
- case -ENOENT:
- case -ECONNRESET:
- case -ENODEV:
- case -ESHUTDOWN:
- /* Stop is requested either by software or hardware is gone,
- * keep the ret value non-zero and don't resubmit later.
- */
- break;
-
- default:
- PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status);
- urb->status = 0;
- ret = 0;
- }
-
- if (ret == 0) {
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret < 0)
- pr_err("Resubmit URB failed with error %i\n", ret);
- }
-}
-
-static int gspca_input_connect(struct gspca_dev *dev)
-{
- struct input_dev *input_dev;
- int err = 0;
-
- dev->input_dev = NULL;
- if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) {
- input_dev = input_allocate_device();
- if (!input_dev)
- return -ENOMEM;
-
- usb_make_path(dev->dev, dev->phys, sizeof(dev->phys));
- strlcat(dev->phys, "/input0", sizeof(dev->phys));
-
- input_dev->name = dev->sd_desc->name;
- input_dev->phys = dev->phys;
-
- usb_to_input_id(dev->dev, &input_dev->id);
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY);
- input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
- input_dev->dev.parent = &dev->dev->dev;
-
- err = input_register_device(input_dev);
- if (err) {
- pr_err("Input device registration failed with error %i\n",
- err);
- input_dev->dev.parent = NULL;
- input_free_device(input_dev);
- } else {
- dev->input_dev = input_dev;
- }
- }
-
- return err;
-}
-
-static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev,
- struct usb_endpoint_descriptor *ep)
-{
- unsigned int buffer_len;
- int interval;
- struct urb *urb;
- struct usb_device *dev;
- void *buffer = NULL;
- int ret = -EINVAL;
-
- buffer_len = le16_to_cpu(ep->wMaxPacketSize);
- interval = ep->bInterval;
- PDEBUG(D_CONF, "found int in endpoint: 0x%x, "
- "buffer_len=%u, interval=%u",
- ep->bEndpointAddress, buffer_len, interval);
-
- dev = gspca_dev->dev;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- ret = -ENOMEM;
- goto error;
- }
-
- buffer = usb_alloc_coherent(dev, buffer_len,
- GFP_KERNEL, &urb->transfer_dma);
- if (!buffer) {
- ret = -ENOMEM;
- goto error_buffer;
- }
- usb_fill_int_urb(urb, dev,
- usb_rcvintpipe(dev, ep->bEndpointAddress),
- buffer, buffer_len,
- int_irq, (void *)gspca_dev, interval);
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- ret = usb_submit_urb(urb, GFP_KERNEL);
- if (ret < 0) {
- PDEBUG(D_ERR, "submit int URB failed with error %i", ret);
- goto error_submit;
- }
- gspca_dev->int_urb = urb;
- return ret;
-
-error_submit:
- usb_free_coherent(dev,
- urb->transfer_buffer_length,
- urb->transfer_buffer,
- urb->transfer_dma);
-error_buffer:
- usb_free_urb(urb);
-error:
- return ret;
-}
-
-static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
-{
- struct usb_interface *intf;
- struct usb_host_interface *intf_desc;
- struct usb_endpoint_descriptor *ep;
- int i;
-
- if (gspca_dev->sd_desc->int_pkt_scan) {
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- intf_desc = intf->cur_altsetting;
- for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
- ep = &intf_desc->endpoint[i].desc;
- if (usb_endpoint_dir_in(ep) &&
- usb_endpoint_xfer_int(ep)) {
-
- alloc_and_submit_int_urb(gspca_dev, ep);
- break;
- }
- }
- }
-}
-
-static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
-{
- struct urb *urb;
-
- urb = gspca_dev->int_urb;
- if (urb) {
- gspca_dev->int_urb = NULL;
- usb_kill_urb(urb);
- usb_free_coherent(gspca_dev->dev,
- urb->transfer_buffer_length,
- urb->transfer_buffer,
- urb->transfer_dma);
- usb_free_urb(urb);
- }
-}
-#else
-static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev)
-{
-}
-
-static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev)
-{
-}
-
-static inline int gspca_input_connect(struct gspca_dev *dev)
-{
- return 0;
-}
-#endif
-
-/*
- * fill a video frame from an URB and resubmit
- */
-static void fill_frame(struct gspca_dev *gspca_dev,
- struct urb *urb)
-{
- u8 *data; /* address of data in the iso message */
- int i, len, st;
- cam_pkt_op pkt_scan;
-
- if (urb->status != 0) {
- if (urb->status == -ESHUTDOWN)
- return; /* disconnection */
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- return;
-#endif
- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- urb->status = 0;
- goto resubmit;
- }
- pkt_scan = gspca_dev->sd_desc->pkt_scan;
- for (i = 0; i < urb->number_of_packets; i++) {
- len = urb->iso_frame_desc[i].actual_length;
-
- /* check the packet status and length */
- st = urb->iso_frame_desc[i].status;
- if (st) {
- pr_err("ISOC data error: [%d] len=%d, status=%d\n",
- i, len, st);
- gspca_dev->last_packet_type = DISCARD_PACKET;
- continue;
- }
- if (len == 0) {
- if (gspca_dev->empty_packet == 0)
- gspca_dev->empty_packet = 1;
- continue;
- }
-
- /* let the packet be analyzed by the subdriver */
- PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
- i, urb->iso_frame_desc[i].offset, len);
- data = (u8 *) urb->transfer_buffer
- + urb->iso_frame_desc[i].offset;
- pkt_scan(gspca_dev, data, len);
- }
-
-resubmit:
- /* resubmit the URB */
- st = usb_submit_urb(urb, GFP_ATOMIC);
- if (st < 0)
- pr_err("usb_submit_urb() ret %d\n", st);
-}
-
-/*
- * ISOC message interrupt from the USB device
- *
- * Analyse each packet and call the subdriver for copy to the frame buffer.
- */
-static void isoc_irq(struct urb *urb)
-{
- struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
-
- PDEBUG(D_PACK, "isoc irq");
- if (!gspca_dev->streaming)
- return;
- fill_frame(gspca_dev, urb);
-}
-
-/*
- * bulk message interrupt from the USB device
- */
-static void bulk_irq(struct urb *urb)
-{
- struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
- int st;
-
- PDEBUG(D_PACK, "bulk irq");
- if (!gspca_dev->streaming)
- return;
- switch (urb->status) {
- case 0:
- break;
- case -ESHUTDOWN:
- return; /* disconnection */
- default:
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- return;
-#endif
- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
- urb->status = 0;
- goto resubmit;
- }
-
- PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
- gspca_dev->sd_desc->pkt_scan(gspca_dev,
- urb->transfer_buffer,
- urb->actual_length);
-
-resubmit:
- /* resubmit the URB */
- if (gspca_dev->cam.bulk_nurbs != 0) {
- st = usb_submit_urb(urb, GFP_ATOMIC);
- if (st < 0)
- pr_err("usb_submit_urb() ret %d\n", st);
- }
-}
-
-/*
- * add data to the current frame
- *
- * This function is called by the subdrivers at interrupt level.
- *
- * To build a frame, these ones must add
- * - one FIRST_PACKET
- * - 0 or many INTER_PACKETs
- * - one LAST_PACKET
- * DISCARD_PACKET invalidates the whole frame.
- */
-void gspca_frame_add(struct gspca_dev *gspca_dev,
- enum gspca_packet_type packet_type,
- const u8 *data,
- int len)
-{
- struct gspca_frame *frame;
- int i, j;
-
- PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
-
- if (packet_type == FIRST_PACKET) {
- i = atomic_read(&gspca_dev->fr_i);
-
- /* if there are no queued buffer, discard the whole frame */
- if (i == atomic_read(&gspca_dev->fr_q)) {
- gspca_dev->last_packet_type = DISCARD_PACKET;
- gspca_dev->sequence++;
- return;
- }
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- frame->v4l2_buf.timestamp = ktime_to_timeval(ktime_get());
- frame->v4l2_buf.sequence = gspca_dev->sequence++;
- gspca_dev->image = frame->data;
- gspca_dev->image_len = 0;
- } else {
- switch (gspca_dev->last_packet_type) {
- case DISCARD_PACKET:
- if (packet_type == LAST_PACKET) {
- gspca_dev->last_packet_type = packet_type;
- gspca_dev->image = NULL;
- gspca_dev->image_len = 0;
- }
- return;
- case LAST_PACKET:
- return;
- }
- }
-
- /* append the packet to the frame buffer */
- if (len > 0) {
- if (gspca_dev->image_len + len > gspca_dev->frsz) {
- PDEBUG(D_ERR|D_PACK, "frame overflow %d > %d",
- gspca_dev->image_len + len,
- gspca_dev->frsz);
- packet_type = DISCARD_PACKET;
- } else {
-/* !! image is NULL only when last pkt is LAST or DISCARD
- if (gspca_dev->image == NULL) {
- pr_err("gspca_frame_add() image == NULL\n");
- return;
- }
- */
- memcpy(gspca_dev->image + gspca_dev->image_len,
- data, len);
- gspca_dev->image_len += len;
- }
- }
- gspca_dev->last_packet_type = packet_type;
-
- /* if last packet, invalidate packet concatenation until
- * next first packet, wake up the application and advance
- * in the queue */
- if (packet_type == LAST_PACKET) {
- i = atomic_read(&gspca_dev->fr_i);
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
- frame->v4l2_buf.bytesused = gspca_dev->image_len;
- frame->v4l2_buf.flags = (frame->v4l2_buf.flags
- | V4L2_BUF_FLAG_DONE)
- & ~V4L2_BUF_FLAG_QUEUED;
- i = (i + 1) % GSPCA_MAX_FRAMES;
- atomic_set(&gspca_dev->fr_i, i);
- wake_up_interruptible(&gspca_dev->wq); /* event = new frame */
- PDEBUG(D_FRAM, "frame complete len:%d",
- frame->v4l2_buf.bytesused);
- gspca_dev->image = NULL;
- gspca_dev->image_len = 0;
- }
-}
-EXPORT_SYMBOL(gspca_frame_add);
-
-static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory, unsigned int count)
-{
- struct gspca_frame *frame;
- unsigned int frsz;
- int i;
-
- i = gspca_dev->curr_mode;
- frsz = gspca_dev->cam.cam_mode[i].sizeimage;
- PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz);
- frsz = PAGE_ALIGN(frsz);
- if (count >= GSPCA_MAX_FRAMES)
- count = GSPCA_MAX_FRAMES - 1;
- gspca_dev->frbuf = vmalloc_32(frsz * count);
- if (!gspca_dev->frbuf) {
- pr_err("frame alloc failed\n");
- return -ENOMEM;
- }
- gspca_dev->capt_file = file;
- gspca_dev->memory = memory;
- gspca_dev->frsz = frsz;
- gspca_dev->nframes = count;
- for (i = 0; i < count; i++) {
- frame = &gspca_dev->frame[i];
- frame->v4l2_buf.index = i;
- frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- frame->v4l2_buf.flags = 0;
- frame->v4l2_buf.field = V4L2_FIELD_NONE;
- frame->v4l2_buf.length = frsz;
- frame->v4l2_buf.memory = memory;
- frame->v4l2_buf.sequence = 0;
- frame->data = gspca_dev->frbuf + i * frsz;
- frame->v4l2_buf.m.offset = i * frsz;
- }
- atomic_set(&gspca_dev->fr_q, 0);
- atomic_set(&gspca_dev->fr_i, 0);
- gspca_dev->fr_o = 0;
- return 0;
-}
-
-static void frame_free(struct gspca_dev *gspca_dev)
-{
- int i;
-
- PDEBUG(D_STREAM, "frame free");
- if (gspca_dev->frbuf != NULL) {
- vfree(gspca_dev->frbuf);
- gspca_dev->frbuf = NULL;
- for (i = 0; i < gspca_dev->nframes; i++)
- gspca_dev->frame[i].data = NULL;
- }
- gspca_dev->nframes = 0;
- gspca_dev->frsz = 0;
- gspca_dev->capt_file = NULL;
- gspca_dev->memory = GSPCA_MEMORY_NO;
-}
-
-static void destroy_urbs(struct gspca_dev *gspca_dev)
-{
- struct urb *urb;
- unsigned int i;
-
- PDEBUG(D_STREAM, "kill transfer");
- for (i = 0; i < MAX_NURBS; i++) {
- urb = gspca_dev->urb[i];
- if (urb == NULL)
- break;
-
- gspca_dev->urb[i] = NULL;
- usb_kill_urb(urb);
- if (urb->transfer_buffer != NULL)
- usb_free_coherent(gspca_dev->dev,
- urb->transfer_buffer_length,
- urb->transfer_buffer,
- urb->transfer_dma);
- usb_free_urb(urb);
- }
-}
-
-static int gspca_set_alt0(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- if (gspca_dev->alt == 0)
- return 0;
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
- if (ret < 0)
- pr_err("set alt 0 err %d\n", ret);
- return ret;
-}
-
-/* Note: both the queue and the usb locks should be held when calling this */
-static void gspca_stream_off(struct gspca_dev *gspca_dev)
-{
- gspca_dev->streaming = 0;
- gspca_dev->usb_err = 0;
- if (gspca_dev->sd_desc->stopN)
- gspca_dev->sd_desc->stopN(gspca_dev);
- destroy_urbs(gspca_dev);
- gspca_input_destroy_urb(gspca_dev);
- gspca_set_alt0(gspca_dev);
- gspca_input_create_urb(gspca_dev);
- if (gspca_dev->sd_desc->stop0)
- gspca_dev->sd_desc->stop0(gspca_dev);
- PDEBUG(D_STREAM, "stream off OK");
-}
-
-/*
- * look for an input transfer endpoint in an alternate setting
- */
-static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
- int xfer)
-{
- struct usb_host_endpoint *ep;
- int i, attr;
-
- for (i = 0; i < alt->desc.bNumEndpoints; i++) {
- ep = &alt->endpoint[i];
- attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
- if (attr == xfer
- && ep->desc.wMaxPacketSize != 0
- && usb_endpoint_dir_in(&ep->desc))
- return ep;
- }
- return NULL;
-}
-
-/* compute the minimum bandwidth for the current transfer */
-static u32 which_bandwidth(struct gspca_dev *gspca_dev)
-{
- u32 bandwidth;
- int i;
-
- /* get the (max) image size */
- i = gspca_dev->curr_mode;
- bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
-
- /* if the image is compressed, estimate its mean size */
- if (!gspca_dev->cam.needs_full_bandwidth &&
- bandwidth < gspca_dev->cam.cam_mode[i].width *
- gspca_dev->cam.cam_mode[i].height)
- bandwidth = bandwidth * 3 / 8; /* 0.375 */
-
- /* estimate the frame rate */
- if (gspca_dev->sd_desc->get_streamparm) {
- struct v4l2_streamparm parm;
-
- gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
- bandwidth *= parm.parm.capture.timeperframe.denominator;
- bandwidth /= parm.parm.capture.timeperframe.numerator;
- } else {
-
- /* don't hope more than 15 fps with USB 1.1 and
- * image resolution >= 640x480 */
- if (gspca_dev->width >= 640
- && gspca_dev->dev->speed == USB_SPEED_FULL)
- bandwidth *= 15; /* 15 fps */
- else
- bandwidth *= 30; /* 30 fps */
- }
-
- PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
- return bandwidth;
-}
-
-/* endpoint table */
-#define MAX_ALT 16
-struct ep_tb_s {
- u32 alt;
- u32 bandwidth;
-};
-
-/*
- * build the table of the endpoints
- * and compute the minimum bandwidth for the image transfer
- */
-static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
- struct usb_interface *intf,
- struct ep_tb_s *ep_tb)
-{
- struct usb_host_endpoint *ep;
- int i, j, nbalt, psize, found;
- u32 bandwidth, last_bw;
-
- nbalt = intf->num_altsetting;
- if (nbalt > MAX_ALT)
- nbalt = MAX_ALT; /* fixme: should warn */
-
- /* build the endpoint table */
- i = 0;
- last_bw = 0;
- for (;;) {
- ep_tb->bandwidth = 2000 * 2000 * 120;
- found = 0;
- for (j = 0; j < nbalt; j++) {
- ep = alt_xfer(&intf->altsetting[j],
- USB_ENDPOINT_XFER_ISOC);
- if (ep == NULL)
- continue;
- if (ep->desc.bInterval == 0) {
- pr_err("alt %d iso endp with 0 interval\n", j);
- continue;
- }
- psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- bandwidth = psize * 1000;
- if (gspca_dev->dev->speed == USB_SPEED_HIGH
- || gspca_dev->dev->speed == USB_SPEED_SUPER)
- bandwidth *= 8;
- bandwidth /= 1 << (ep->desc.bInterval - 1);
- if (bandwidth <= last_bw)
- continue;
- if (bandwidth < ep_tb->bandwidth) {
- ep_tb->bandwidth = bandwidth;
- ep_tb->alt = j;
- found = 1;
- }
- }
- if (!found)
- break;
- PDEBUG(D_STREAM, "alt %d bandwidth %d",
- ep_tb->alt, ep_tb->bandwidth);
- last_bw = ep_tb->bandwidth;
- i++;
- ep_tb++;
- }
-
- /*
- * If the camera:
- * has a usb audio class interface (a built in usb mic); and
- * is a usb 1 full speed device; and
- * uses the max full speed iso bandwidth; and
- * and has more than 1 alt setting
- * then skip the highest alt setting to spare bandwidth for the mic
- */
- if (gspca_dev->audio &&
- gspca_dev->dev->speed == USB_SPEED_FULL &&
- last_bw >= 1000000 &&
- i > 1) {
- PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt");
- i--;
- ep_tb--;
- }
-
- /* get the requested bandwidth and start at the highest atlsetting */
- bandwidth = which_bandwidth(gspca_dev);
- ep_tb--;
- while (i > 1) {
- ep_tb--;
- if (ep_tb->bandwidth < bandwidth)
- break;
- i--;
- }
- return i;
-}
-
-/*
- * create the URBs for image transfer
- */
-static int create_urbs(struct gspca_dev *gspca_dev,
- struct usb_host_endpoint *ep)
-{
- struct urb *urb;
- int n, nurbs, i, psize, npkt, bsize;
-
- /* calculate the packet size and the number of packets */
- psize = le16_to_cpu(ep->desc.wMaxPacketSize);
-
- if (!gspca_dev->cam.bulk) { /* isoc */
-
- /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
- if (gspca_dev->pkt_size == 0)
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- else
- psize = gspca_dev->pkt_size;
- npkt = gspca_dev->cam.npkt;
- if (npkt == 0)
- npkt = 32; /* default value */
- bsize = psize * npkt;
- PDEBUG(D_STREAM,
- "isoc %d pkts size %d = bsize:%d",
- npkt, psize, bsize);
- nurbs = DEF_NURBS;
- } else { /* bulk */
- npkt = 0;
- bsize = gspca_dev->cam.bulk_size;
- if (bsize == 0)
- bsize = psize;
- PDEBUG(D_STREAM, "bulk bsize:%d", bsize);
- if (gspca_dev->cam.bulk_nurbs != 0)
- nurbs = gspca_dev->cam.bulk_nurbs;
- else
- nurbs = 1;
- }
-
- for (n = 0; n < nurbs; n++) {
- urb = usb_alloc_urb(npkt, GFP_KERNEL);
- if (!urb) {
- pr_err("usb_alloc_urb failed\n");
- return -ENOMEM;
- }
- gspca_dev->urb[n] = urb;
- urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev,
- bsize,
- GFP_KERNEL,
- &urb->transfer_dma);
-
- if (urb->transfer_buffer == NULL) {
- pr_err("usb_alloc_coherent failed\n");
- return -ENOMEM;
- }
- urb->dev = gspca_dev->dev;
- urb->context = gspca_dev;
- urb->transfer_buffer_length = bsize;
- if (npkt != 0) { /* ISOC */
- urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
- ep->desc.bEndpointAddress);
- urb->transfer_flags = URB_ISO_ASAP
- | URB_NO_TRANSFER_DMA_MAP;
- urb->interval = 1 << (ep->desc.bInterval - 1);
- urb->complete = isoc_irq;
- urb->number_of_packets = npkt;
- for (i = 0; i < npkt; i++) {
- urb->iso_frame_desc[i].length = psize;
- urb->iso_frame_desc[i].offset = psize * i;
- }
- } else { /* bulk */
- urb->pipe = usb_rcvbulkpipe(gspca_dev->dev,
- ep->desc.bEndpointAddress);
- urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
- urb->complete = bulk_irq;
- }
- }
- return 0;
-}
-
-/*
- * start the USB transfer
- */
-static int gspca_init_transfer(struct gspca_dev *gspca_dev)
-{
- struct usb_interface *intf;
- struct usb_host_endpoint *ep;
- struct urb *urb;
- struct ep_tb_s ep_tb[MAX_ALT];
- int n, ret, xfer, alt, alt_idx;
-
- /* reset the streaming variables */
- gspca_dev->image = NULL;
- gspca_dev->image_len = 0;
- gspca_dev->last_packet_type = DISCARD_PACKET;
- gspca_dev->sequence = 0;
-
- gspca_dev->usb_err = 0;
-
- /* do the specific subdriver stuff before endpoint selection */
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0;
- if (gspca_dev->sd_desc->isoc_init) {
- ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
- if (ret < 0)
- return ret;
- }
- xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
- : USB_ENDPOINT_XFER_ISOC;
-
- /* if bulk or the subdriver forced an altsetting, get the endpoint */
- if (gspca_dev->alt != 0) {
- gspca_dev->alt--; /* (previous version compatibility) */
- ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
- if (ep == NULL) {
- pr_err("bad altsetting %d\n", gspca_dev->alt);
- return -EIO;
- }
- ep_tb[0].alt = gspca_dev->alt;
- alt_idx = 1;
- } else {
-
- /* else, compute the minimum bandwidth
- * and build the endpoint table */
- alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
- if (alt_idx <= 0) {
- pr_err("no transfer endpoint found\n");
- return -EIO;
- }
- }
-
- /* set the highest alternate setting and
- * loop until urb submit succeeds */
- gspca_input_destroy_urb(gspca_dev);
-
- gspca_dev->alt = ep_tb[--alt_idx].alt;
- alt = -1;
- for (;;) {
- if (alt != gspca_dev->alt) {
- alt = gspca_dev->alt;
- if (intf->num_altsetting > 1) {
- ret = usb_set_interface(gspca_dev->dev,
- gspca_dev->iface,
- alt);
- if (ret < 0) {
- if (ret == -ENOSPC)
- goto retry; /*fixme: ugly*/
- pr_err("set alt %d err %d\n", alt, ret);
- goto out;
- }
- }
- }
- if (!gspca_dev->cam.no_urb_create) {
- PDEBUG(D_STREAM, "init transfer alt %d", alt);
- ret = create_urbs(gspca_dev,
- alt_xfer(&intf->altsetting[alt], xfer));
- if (ret < 0) {
- destroy_urbs(gspca_dev);
- goto out;
- }
- }
-
- /* clear the bulk endpoint */
- if (gspca_dev->cam.bulk)
- usb_clear_halt(gspca_dev->dev,
- gspca_dev->urb[0]->pipe);
-
- /* start the cam */
- ret = gspca_dev->sd_desc->start(gspca_dev);
- if (ret < 0) {
- destroy_urbs(gspca_dev);
- goto out;
- }
- gspca_dev->streaming = 1;
- v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
-
- /* some bulk transfers are started by the subdriver */
- if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0)
- break;
-
- /* submit the URBs */
- for (n = 0; n < MAX_NURBS; n++) {
- urb = gspca_dev->urb[n];
- if (urb == NULL)
- break;
- ret = usb_submit_urb(urb, GFP_KERNEL);
- if (ret < 0)
- break;
- }
- if (ret >= 0)
- break; /* transfer is started */
-
- /* something when wrong
- * stop the webcam and free the transfer resources */
- gspca_stream_off(gspca_dev);
- if (ret != -ENOSPC) {
- pr_err("usb_submit_urb alt %d err %d\n",
- gspca_dev->alt, ret);
- goto out;
- }
-
- /* the bandwidth is not wide enough
- * negotiate or try a lower alternate setting */
-retry:
- PDEBUG(D_ERR|D_STREAM,
- "alt %d - bandwidth not wide enough - trying again",
- alt);
- msleep(20); /* wait for kill complete */
- if (gspca_dev->sd_desc->isoc_nego) {
- ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
- if (ret < 0)
- goto out;
- } else {
- if (alt_idx <= 0) {
- pr_err("no transfer endpoint found\n");
- ret = -EIO;
- goto out;
- }
- gspca_dev->alt = ep_tb[--alt_idx].alt;
- }
- }
-out:
- gspca_input_create_urb(gspca_dev);
- return ret;
-}
-
-static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
-{
- struct gspca_ctrl *ctrl;
- int i;
-
- i = gspca_dev->cam.nmodes - 1; /* take the highest mode */
- gspca_dev->curr_mode = i;
- gspca_dev->width = gspca_dev->cam.cam_mode[i].width;
- gspca_dev->height = gspca_dev->cam.cam_mode[i].height;
- gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;
-
- /* set the current control values to their default values
- * which may have changed in sd_init() */
- /* does nothing if ctrl_handler == NULL */
- v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
- ctrl = gspca_dev->cam.ctrls;
- if (ctrl != NULL) {
- for (i = 0;
- i < gspca_dev->sd_desc->nctrls;
- i++, ctrl++)
- ctrl->val = ctrl->def;
- }
-}
-
-static int wxh_to_mode(struct gspca_dev *gspca_dev,
- int width, int height)
-{
- int i;
-
- for (i = gspca_dev->cam.nmodes; --i > 0; ) {
- if (width >= gspca_dev->cam.cam_mode[i].width
- && height >= gspca_dev->cam.cam_mode[i].height)
- break;
- }
- return i;
-}
-
-/*
- * search a mode with the right pixel format
- */
-static int gspca_get_mode(struct gspca_dev *gspca_dev,
- int mode,
- int pixfmt)
-{
- int modeU, modeD;
-
- modeU = modeD = mode;
- while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) {
- if (--modeD >= 0) {
- if (gspca_dev->cam.cam_mode[modeD].pixelformat
- == pixfmt)
- return modeD;
- }
- if (++modeU < gspca_dev->cam.nmodes) {
- if (gspca_dev->cam.cam_mode[modeU].pixelformat
- == pixfmt)
- return modeU;
- }
- }
- return -EINVAL;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->get_register(gspca_dev, reg);
-}
-
-static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->set_register(gspca_dev, reg);
-}
-#endif
-
-static int vidioc_g_chip_ident(struct file *file, void *priv,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmtdesc)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i, j, index;
- __u32 fmt_tb[8];
-
- /* give an index to each format */
- index = 0;
- j = 0;
- for (i = gspca_dev->cam.nmodes; --i >= 0; ) {
- fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat;
- j = 0;
- for (;;) {
- if (fmt_tb[j] == fmt_tb[index])
- break;
- j++;
- }
- if (j == index) {
- if (fmtdesc->index == index)
- break; /* new format */
- index++;
- if (index >= ARRAY_SIZE(fmt_tb))
- return -EINVAL;
- }
- }
- if (i < 0)
- return -EINVAL; /* no more format */
-
- fmtdesc->pixelformat = fmt_tb[index];
- if (gspca_dev->cam.cam_mode[i].sizeimage <
- gspca_dev->cam.cam_mode[i].width *
- gspca_dev->cam.cam_mode[i].height)
- fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
- fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
- fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
- fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
- fmtdesc->description[3] = fmtdesc->pixelformat >> 24;
- fmtdesc->description[4] = '\0';
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int mode;
-
- mode = gspca_dev->curr_mode;
- fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
- /* some drivers use priv internally, zero it before giving it to
- userspace */
- fmt->fmt.pix.priv = 0;
- return 0;
-}
-
-static int try_fmt_vid_cap(struct gspca_dev *gspca_dev,
- struct v4l2_format *fmt)
-{
- int w, h, mode, mode2;
-
- w = fmt->fmt.pix.width;
- h = fmt->fmt.pix.height;
-
-#ifdef GSPCA_DEBUG
- if (gspca_debug & D_CONF)
- PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);
-#endif
- /* search the closest mode for width and height */
- mode = wxh_to_mode(gspca_dev, w, h);
-
- /* OK if right palette */
- if (gspca_dev->cam.cam_mode[mode].pixelformat
- != fmt->fmt.pix.pixelformat) {
-
- /* else, search the closest mode with the same pixel format */
- mode2 = gspca_get_mode(gspca_dev, mode,
- fmt->fmt.pix.pixelformat);
- if (mode2 >= 0)
- mode = mode2;
-/* else
- ; * no chance, return this mode */
- }
- fmt->fmt.pix = gspca_dev->cam.cam_mode[mode];
- /* some drivers use priv internally, zero it before giving it to
- userspace */
- fmt->fmt.pix.priv = 0;
- return mode; /* used when s_fmt */
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file,
- void *priv,
- struct v4l2_format *fmt)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- ret = try_fmt_vid_cap(gspca_dev, fmt);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- ret = try_fmt_vid_cap(gspca_dev, fmt);
- if (ret < 0)
- goto out;
-
- if (gspca_dev->nframes != 0
- && fmt->fmt.pix.sizeimage > gspca_dev->frsz) {
- ret = -EINVAL;
- goto out;
- }
-
- if (ret == gspca_dev->curr_mode) {
- ret = 0;
- goto out; /* same mode */
- }
-
- if (gspca_dev->streaming) {
- ret = -EBUSY;
- goto out;
- }
- gspca_dev->width = fmt->fmt.pix.width;
- gspca_dev->height = fmt->fmt.pix.height;
- gspca_dev->pixfmt = fmt->fmt.pix.pixelformat;
- gspca_dev->curr_mode = ret;
-
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-static int vidioc_enum_framesizes(struct file *file, void *priv,
- struct v4l2_frmsizeenum *fsize)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i;
- __u32 index = 0;
-
- for (i = 0; i < gspca_dev->cam.nmodes; i++) {
- if (fsize->pixel_format !=
- gspca_dev->cam.cam_mode[i].pixelformat)
- continue;
-
- if (fsize->index == index) {
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width =
- gspca_dev->cam.cam_mode[i].width;
- fsize->discrete.height =
- gspca_dev->cam.cam_mode[i].height;
- return 0;
- }
- index++;
- }
-
- return -EINVAL;
-}
-
-static int vidioc_enum_frameintervals(struct file *filp, void *priv,
- struct v4l2_frmivalenum *fival)
-{
- struct gspca_dev *gspca_dev = video_drvdata(filp);
- int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
- __u32 i;
-
- if (gspca_dev->cam.mode_framerates == NULL ||
- gspca_dev->cam.mode_framerates[mode].nrates == 0)
- return -EINVAL;
-
- if (fival->pixel_format !=
- gspca_dev->cam.cam_mode[mode].pixelformat)
- return -EINVAL;
-
- for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
- if (fival->index == i) {
- fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fival->discrete.numerator = 1;
- fival->discrete.denominator =
- gspca_dev->cam.mode_framerates[mode].rates[i];
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
-static void gspca_release(struct v4l2_device *v4l2_device)
-{
- struct gspca_dev *gspca_dev =
- container_of(v4l2_device, struct gspca_dev, v4l2_dev);
-
- v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
- v4l2_device_unregister(&gspca_dev->v4l2_dev);
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
-}
-
-static int dev_open(struct file *file)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- PDEBUG(D_STREAM, "[%s] open", current->comm);
-
- /* protect the subdriver against rmmod */
- if (!try_module_get(gspca_dev->module))
- return -ENODEV;
-
-#ifdef GSPCA_DEBUG
- /* activate the v4l2 debug */
- if (gspca_debug & D_V4L2)
- gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL
- | V4L2_DEBUG_IOCTL_ARG;
- else
- gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
- | V4L2_DEBUG_IOCTL_ARG);
-#endif
- return v4l2_fh_open(file);
-}
-
-static int dev_close(struct file *file)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- PDEBUG(D_STREAM, "[%s] close", current->comm);
-
- /* Needed for gspca_stream_off, always lock before queue_lock! */
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock)) {
- mutex_unlock(&gspca_dev->usb_lock);
- return -ERESTARTSYS;
- }
-
- /* if the file did the capture, free the streaming resources */
- if (gspca_dev->capt_file == file) {
- if (gspca_dev->streaming)
- gspca_stream_off(gspca_dev);
- frame_free(gspca_dev);
- }
- module_put(gspca_dev->module);
- mutex_unlock(&gspca_dev->queue_lock);
- mutex_unlock(&gspca_dev->usb_lock);
-
- PDEBUG(D_STREAM, "close done");
-
- return v4l2_fh_release(file);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
- sizeof cap->driver);
- if (gspca_dev->dev->product != NULL) {
- strlcpy((char *) cap->card, gspca_dev->dev->product,
- sizeof cap->card);
- } else {
- snprintf((char *) cap->card, sizeof cap->card,
- "USB Camera (%04x:%04x)",
- le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
- le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
- }
- usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
- sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
- | V4L2_CAP_STREAMING
- | V4L2_CAP_READWRITE;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int get_ctrl(struct gspca_dev *gspca_dev,
- int id)
-{
- const struct ctrl *ctrls;
- int i;
-
- for (i = 0, ctrls = gspca_dev->sd_desc->ctrls;
- i < gspca_dev->sd_desc->nctrls;
- i++, ctrls++) {
- if (gspca_dev->ctrl_dis & (1 << i))
- continue;
- if (id == ctrls->qctrl.id)
- return i;
- }
- return -1;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *q_ctrl)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- const struct ctrl *ctrls;
- struct gspca_ctrl *gspca_ctrl;
- int i, idx;
- u32 id;
-
- id = q_ctrl->id;
- if (id & V4L2_CTRL_FLAG_NEXT_CTRL) {
- id &= V4L2_CTRL_ID_MASK;
- id++;
- idx = -1;
- for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
- if (gspca_dev->ctrl_dis & (1 << i))
- continue;
- if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id)
- continue;
- if (idx >= 0
- && gspca_dev->sd_desc->ctrls[i].qctrl.id
- > gspca_dev->sd_desc->ctrls[idx].qctrl.id)
- continue;
- idx = i;
- }
- } else {
- idx = get_ctrl(gspca_dev, id);
- }
- if (idx < 0)
- return -EINVAL;
- ctrls = &gspca_dev->sd_desc->ctrls[idx];
- memcpy(q_ctrl, &ctrls->qctrl, sizeof *q_ctrl);
- if (gspca_dev->cam.ctrls != NULL) {
- gspca_ctrl = &gspca_dev->cam.ctrls[idx];
- q_ctrl->default_value = gspca_ctrl->def;
- q_ctrl->minimum = gspca_ctrl->min;
- q_ctrl->maximum = gspca_ctrl->max;
- }
- if (gspca_dev->ctrl_inac & (1 << idx))
- q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- const struct ctrl *ctrls;
- struct gspca_ctrl *gspca_ctrl;
- int idx;
-
- idx = get_ctrl(gspca_dev, ctrl->id);
- if (idx < 0)
- return -EINVAL;
- if (gspca_dev->ctrl_inac & (1 << idx))
- return -EINVAL;
- ctrls = &gspca_dev->sd_desc->ctrls[idx];
- if (gspca_dev->cam.ctrls != NULL) {
- gspca_ctrl = &gspca_dev->cam.ctrls[idx];
- if (ctrl->value < gspca_ctrl->min
- || ctrl->value > gspca_ctrl->max)
- return -ERANGE;
- } else {
- gspca_ctrl = NULL;
- if (ctrl->value < ctrls->qctrl.minimum
- || ctrl->value > ctrls->qctrl.maximum)
- return -ERANGE;
- }
- PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
- gspca_dev->usb_err = 0;
- if (ctrls->set != NULL)
- return ctrls->set(gspca_dev, ctrl->value);
- if (gspca_ctrl != NULL) {
- gspca_ctrl->val = ctrl->value;
- if (ctrls->set_control != NULL
- && gspca_dev->streaming)
- ctrls->set_control(gspca_dev);
- }
- return gspca_dev->usb_err;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- const struct ctrl *ctrls;
- int idx;
-
- idx = get_ctrl(gspca_dev, ctrl->id);
- if (idx < 0)
- return -EINVAL;
- ctrls = &gspca_dev->sd_desc->ctrls[idx];
-
- gspca_dev->usb_err = 0;
- if (ctrls->get != NULL)
- return ctrls->get(gspca_dev, &ctrl->value);
- if (gspca_dev->cam.ctrls != NULL)
- ctrl->value = gspca_dev->cam.ctrls[idx].val;
- return 0;
-}
-
-static int vidioc_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *qmenu)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- if (!gspca_dev->sd_desc->querymenu)
- return -ENOTTY;
- return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *input)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- if (input->index != 0)
- return -EINVAL;
- input->type = V4L2_INPUT_TYPE_CAMERA;
- input->status = gspca_dev->cam.input_flags;
- strlcpy(input->name, gspca_dev->sd_desc->name,
- sizeof input->name);
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
- return (0);
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *rb)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i, ret = 0, streaming;
-
- i = rb->memory; /* (avoid compilation warning) */
- switch (i) {
- case GSPCA_MEMORY_READ: /* (internal call) */
- case V4L2_MEMORY_MMAP:
- case V4L2_MEMORY_USERPTR:
- break;
- default:
- return -EINVAL;
- }
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- if (gspca_dev->memory != GSPCA_MEMORY_NO
- && gspca_dev->memory != GSPCA_MEMORY_READ
- && gspca_dev->memory != rb->memory) {
- ret = -EBUSY;
- goto out;
- }
-
- /* only one file may do the capture */
- if (gspca_dev->capt_file != NULL
- && gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- /* if allocated, the buffers must not be mapped */
- for (i = 0; i < gspca_dev->nframes; i++) {
- if (gspca_dev->frame[i].vma_use_count) {
- ret = -EBUSY;
- goto out;
- }
- }
-
- /* stop streaming */
- streaming = gspca_dev->streaming;
- if (streaming) {
- gspca_stream_off(gspca_dev);
-
- /* Don't restart the stream when switching from read
- * to mmap mode */
- if (gspca_dev->memory == GSPCA_MEMORY_READ)
- streaming = 0;
- }
-
- /* free the previous allocated buffers, if any */
- if (gspca_dev->nframes != 0)
- frame_free(gspca_dev);
- if (rb->count == 0) /* unrequest */
- goto out;
- ret = frame_alloc(gspca_dev, file, rb->memory, rb->count);
- if (ret == 0) {
- rb->count = gspca_dev->nframes;
- if (streaming)
- ret = gspca_init_transfer(gspca_dev);
- }
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count);
- return ret;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
-
- if (v4l2_buf->index < 0
- || v4l2_buf->index >= gspca_dev->nframes)
- return -EINVAL;
-
- frame = &gspca_dev->frame[v4l2_buf->index];
- memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int ret;
-
- if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- /* check the capture file */
- if (gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- if (gspca_dev->nframes == 0
- || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
- ret = -EINVAL;
- goto out;
- }
- if (!gspca_dev->streaming) {
- ret = gspca_init_transfer(gspca_dev);
- if (ret < 0)
- goto out;
- }
-#ifdef GSPCA_DEBUG
- if (gspca_debug & D_STREAM) {
- PDEBUG_MODE("stream on OK",
- gspca_dev->pixfmt,
- gspca_dev->width,
- gspca_dev->height);
- }
-#endif
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buf_type)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- int i, ret;
-
- if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- if (!gspca_dev->streaming) {
- ret = 0;
- goto out;
- }
-
- /* check the capture file */
- if (gspca_dev->capt_file != file) {
- ret = -EBUSY;
- goto out;
- }
-
- /* stop streaming */
- gspca_stream_off(gspca_dev);
- /* In case another thread is waiting in dqbuf */
- wake_up_interruptible(&gspca_dev->wq);
-
- /* empty the transfer queues */
- for (i = 0; i < gspca_dev->nframes; i++)
- gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS;
- atomic_set(&gspca_dev->fr_q, 0);
- atomic_set(&gspca_dev->fr_i, 0);
- gspca_dev->fr_o = 0;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-static int vidioc_g_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *jpegcomp)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
-}
-
-static int vidioc_s_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *jpegcomp)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
-
- gspca_dev->usb_err = 0;
- return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
-}
-
-static int vidioc_g_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct gspca_dev *gspca_dev = video_drvdata(filp);
-
- parm->parm.capture.readbuffers = gspca_dev->nbufread;
-
- if (gspca_dev->sd_desc->get_streamparm) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
- return gspca_dev->usb_err;
- }
- return 0;
-}
-
-static int vidioc_s_parm(struct file *filp, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct gspca_dev *gspca_dev = video_drvdata(filp);
- int n;
-
- n = parm->parm.capture.readbuffers;
- if (n == 0 || n >= GSPCA_MAX_FRAMES)
- parm->parm.capture.readbuffers = gspca_dev->nbufread;
- else
- gspca_dev->nbufread = n;
-
- if (gspca_dev->sd_desc->set_streamparm) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
- return gspca_dev->usb_err;
- }
-
- return 0;
-}
-
-static int dev_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- struct page *page;
- unsigned long addr, start, size;
- int i, ret;
-
- start = vma->vm_start;
- size = vma->vm_end - vma->vm_start;
- PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size);
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- if (gspca_dev->capt_file != file) {
- ret = -EINVAL;
- goto out;
- }
-
- frame = NULL;
- for (i = 0; i < gspca_dev->nframes; ++i) {
- if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) {
- PDEBUG(D_STREAM, "mmap bad memory type");
- break;
- }
- if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT)
- == vma->vm_pgoff) {
- frame = &gspca_dev->frame[i];
- break;
- }
- }
- if (frame == NULL) {
- PDEBUG(D_STREAM, "mmap no frame buffer found");
- ret = -EINVAL;
- goto out;
- }
- if (size != frame->v4l2_buf.length) {
- PDEBUG(D_STREAM, "mmap bad size");
- ret = -EINVAL;
- goto out;
- }
-
- /*
- * - VM_IO marks the area as being a mmaped region for I/O to a
- * device. It also prevents the region from being core dumped.
- */
- vma->vm_flags |= VM_IO;
-
- addr = (unsigned long) frame->data;
- while (size > 0) {
- page = vmalloc_to_page((void *) addr);
- ret = vm_insert_page(vma, start, page);
- if (ret < 0)
- goto out;
- start += PAGE_SIZE;
- addr += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- vma->vm_ops = &gspca_vm_ops;
- vma->vm_private_data = frame;
- gspca_vm_open(vma);
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory)
-{
- if (!gspca_dev->present)
- return -ENODEV;
- if (gspca_dev->capt_file != file || gspca_dev->memory != memory ||
- !gspca_dev->streaming)
- return -EINVAL;
-
- /* check if a frame is ready */
- return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i);
-}
-
-static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
- enum v4l2_memory memory)
-{
- int ret;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- ret = frame_ready_nolock(gspca_dev, file, memory);
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-/*
- * dequeue a video buffer
- *
- * If nonblock_ing is false, block until a buffer is available.
- */
-static int vidioc_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- int i, j, ret;
-
- PDEBUG(D_FRAM, "dqbuf");
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- for (;;) {
- ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory);
- if (ret < 0)
- goto out;
- if (ret > 0)
- break;
-
- mutex_unlock(&gspca_dev->queue_lock);
-
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
-
- /* wait till a frame is ready */
- ret = wait_event_interruptible_timeout(gspca_dev->wq,
- frame_ready(gspca_dev, file, v4l2_buf->memory),
- msecs_to_jiffies(3000));
- if (ret < 0)
- return ret;
- if (ret == 0)
- return -EIO;
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
- }
-
- i = gspca_dev->fr_o;
- j = gspca_dev->fr_queue[i];
- frame = &gspca_dev->frame[j];
-
- gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
-
- frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
- memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
- PDEBUG(D_FRAM, "dqbuf %d", j);
- ret = 0;
-
- if (gspca_dev->memory == V4L2_MEMORY_USERPTR) {
- if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr,
- frame->data,
- frame->v4l2_buf.bytesused)) {
- PDEBUG(D_ERR|D_STREAM,
- "dqbuf cp to user failed");
- ret = -EFAULT;
- }
- }
-out:
- mutex_unlock(&gspca_dev->queue_lock);
-
- if (ret == 0 && gspca_dev->sd_desc->dq_callback) {
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- gspca_dev->sd_desc->dq_callback(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- }
-
- return ret;
-}
-
-/*
- * queue a video buffer
- *
- * Attempting to queue a buffer that has already been
- * queued will return -EINVAL.
- */
-static int vidioc_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *v4l2_buf)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- int i, index, ret;
-
- PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index);
-
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
- return -ERESTARTSYS;
-
- index = v4l2_buf->index;
- if ((unsigned) index >= gspca_dev->nframes) {
- PDEBUG(D_FRAM,
- "qbuf idx %d >= %d", index, gspca_dev->nframes);
- ret = -EINVAL;
- goto out;
- }
- if (v4l2_buf->memory != gspca_dev->memory) {
- PDEBUG(D_FRAM, "qbuf bad memory type");
- ret = -EINVAL;
- goto out;
- }
-
- frame = &gspca_dev->frame[index];
- if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) {
- PDEBUG(D_FRAM, "qbuf bad state");
- ret = -EINVAL;
- goto out;
- }
-
- frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED;
-
- if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) {
- frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr;
- frame->v4l2_buf.length = v4l2_buf->length;
- }
-
- /* put the buffer in the 'queued' queue */
- i = atomic_read(&gspca_dev->fr_q);
- gspca_dev->fr_queue[i] = index;
- atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES);
-
- v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
- v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->queue_lock);
- return ret;
-}
-
-/*
- * allocate the resources for read()
- */
-static int read_alloc(struct gspca_dev *gspca_dev,
- struct file *file)
-{
- struct v4l2_buffer v4l2_buf;
- int i, ret;
-
- PDEBUG(D_STREAM, "read alloc");
-
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
-
- if (gspca_dev->nframes == 0) {
- struct v4l2_requestbuffers rb;
-
- memset(&rb, 0, sizeof rb);
- rb.count = gspca_dev->nbufread;
- rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rb.memory = GSPCA_MEMORY_READ;
- ret = vidioc_reqbufs(file, gspca_dev, &rb);
- if (ret != 0) {
- PDEBUG(D_STREAM, "read reqbuf err %d", ret);
- goto out;
- }
- memset(&v4l2_buf, 0, sizeof v4l2_buf);
- v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = GSPCA_MEMORY_READ;
- for (i = 0; i < gspca_dev->nbufread; i++) {
- v4l2_buf.index = i;
- ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- PDEBUG(D_STREAM, "read qbuf err: %d", ret);
- goto out;
- }
- }
- }
-
- /* start streaming */
- ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (ret != 0)
- PDEBUG(D_STREAM, "read streamon err %d", ret);
-out:
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
-}
-
-static unsigned int dev_poll(struct file *file, poll_table *wait)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- unsigned long req_events = poll_requested_events(wait);
- int ret = 0;
-
- PDEBUG(D_FRAM, "poll");
-
- if (req_events & POLLPRI)
- ret |= v4l2_ctrl_poll(file, wait);
-
- if (req_events & (POLLIN | POLLRDNORM)) {
- /* if reqbufs is not done, the user would use read() */
- if (gspca_dev->memory == GSPCA_MEMORY_NO) {
- if (read_alloc(gspca_dev, file) != 0) {
- ret |= POLLERR;
- goto out;
- }
- }
-
- poll_wait(file, &gspca_dev->wq, wait);
-
- /* check if an image has been received */
- if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) {
- ret |= POLLERR;
- goto out;
- }
- if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
- ret |= POLLIN | POLLRDNORM;
- mutex_unlock(&gspca_dev->queue_lock);
- }
-
-out:
- if (!gspca_dev->present)
- ret |= POLLHUP;
-
- return ret;
-}
-
-static ssize_t dev_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct gspca_dev *gspca_dev = video_drvdata(file);
- struct gspca_frame *frame;
- struct v4l2_buffer v4l2_buf;
- struct timeval timestamp;
- int n, ret, ret2;
-
- PDEBUG(D_FRAM, "read (%zd)", count);
- if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */
- ret = read_alloc(gspca_dev, file);
- if (ret != 0)
- return ret;
- }
-
- /* get a frame */
- timestamp = ktime_to_timeval(ktime_get());
- timestamp.tv_sec--;
- n = 2;
- for (;;) {
- memset(&v4l2_buf, 0, sizeof v4l2_buf);
- v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = GSPCA_MEMORY_READ;
- ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- PDEBUG(D_STREAM, "read dqbuf err %d", ret);
- return ret;
- }
-
- /* if the process slept for more than 1 second,
- * get a newer frame */
- frame = &gspca_dev->frame[v4l2_buf.index];
- if (--n < 0)
- break; /* avoid infinite loop */
- if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec)
- break;
- ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret != 0) {
- PDEBUG(D_STREAM, "read qbuf err %d", ret);
- return ret;
- }
- }
-
- /* copy the frame */
- if (count > frame->v4l2_buf.bytesused)
- count = frame->v4l2_buf.bytesused;
- ret = copy_to_user(data, frame->data, count);
- if (ret != 0) {
- PDEBUG(D_ERR|D_STREAM,
- "read cp to user lack %d / %zd", ret, count);
- ret = -EFAULT;
- goto out;
- }
- ret = count;
-out:
- /* in each case, requeue the buffer */
- ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
- if (ret2 != 0)
- return ret2;
- return ret;
-}
-
-static struct v4l2_file_operations dev_fops = {
- .owner = THIS_MODULE,
- .open = dev_open,
- .release = dev_close,
- .read = dev_read,
- .mmap = dev_mmap,
- .unlocked_ioctl = video_ioctl2,
- .poll = dev_poll,
-};
-
-static const struct v4l2_ioctl_ops dev_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_querymenu = vidioc_querymenu,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
- .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_enum_framesizes = vidioc_enum_framesizes,
- .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
- .vidioc_g_chip_ident = vidioc_g_chip_ident,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static const struct video_device gspca_template = {
- .name = "gspca main driver",
- .fops = &dev_fops,
- .ioctl_ops = &dev_ioctl_ops,
- .release = video_device_release_empty, /* We use v4l2_dev.release */
-};
-
-/* initialize the controls */
-static void ctrls_init(struct gspca_dev *gspca_dev)
-{
- struct gspca_ctrl *ctrl;
- int i;
-
- for (i = 0, ctrl = gspca_dev->cam.ctrls;
- i < gspca_dev->sd_desc->nctrls;
- i++, ctrl++) {
- ctrl->def = gspca_dev->sd_desc->ctrls[i].qctrl.default_value;
- ctrl->val = ctrl->def;
- ctrl->min = gspca_dev->sd_desc->ctrls[i].qctrl.minimum;
- ctrl->max = gspca_dev->sd_desc->ctrls[i].qctrl.maximum;
- }
-}
-
-/*
- * probe and create a new gspca device
- *
- * This function must be called by the sub-driver when it is
- * called for probing a new device.
- */
-int gspca_dev_probe2(struct usb_interface *intf,
- const struct usb_device_id *id,
- const struct sd_desc *sd_desc,
- int dev_size,
- struct module *module)
-{
- struct gspca_dev *gspca_dev;
- struct usb_device *dev = interface_to_usbdev(intf);
- int ret;
-
- pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n",
- sd_desc->name, id->idVendor, id->idProduct);
-
- /* create the device */
- if (dev_size < sizeof *gspca_dev)
- dev_size = sizeof *gspca_dev;
- gspca_dev = kzalloc(dev_size, GFP_KERNEL);
- if (!gspca_dev) {
- pr_err("couldn't kzalloc gspca struct\n");
- return -ENOMEM;
- }
- gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL);
- if (!gspca_dev->usb_buf) {
- pr_err("out of memory\n");
- ret = -ENOMEM;
- goto out;
- }
- gspca_dev->dev = dev;
- gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
-
- /* check if any audio device */
- if (dev->actconfig->desc.bNumInterfaces != 1) {
- int i;
- struct usb_interface *intf2;
-
- for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
- intf2 = dev->actconfig->interface[i];
- if (intf2 != NULL
- && intf2->altsetting != NULL
- && intf2->altsetting->desc.bInterfaceClass ==
- USB_CLASS_AUDIO) {
- gspca_dev->audio = 1;
- break;
- }
- }
- }
-
- gspca_dev->v4l2_dev.release = gspca_release;
- ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
- if (ret)
- goto out;
- gspca_dev->sd_desc = sd_desc;
- gspca_dev->nbufread = 2;
- gspca_dev->empty_packet = -1; /* don't check the empty packets */
- gspca_dev->vdev = gspca_template;
- gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
- video_set_drvdata(&gspca_dev->vdev, gspca_dev);
- set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags);
- gspca_dev->module = module;
- gspca_dev->present = 1;
-
- mutex_init(&gspca_dev->usb_lock);
- gspca_dev->vdev.lock = &gspca_dev->usb_lock;
- mutex_init(&gspca_dev->queue_lock);
- init_waitqueue_head(&gspca_dev->wq);
-
- /* configure the subdriver and initialize the USB device */
- ret = sd_desc->config(gspca_dev, id);
- if (ret < 0)
- goto out;
- if (gspca_dev->cam.ctrls != NULL)
- ctrls_init(gspca_dev);
- ret = sd_desc->init(gspca_dev);
- if (ret < 0)
- goto out;
- if (sd_desc->init_controls)
- ret = sd_desc->init_controls(gspca_dev);
- if (ret < 0)
- goto out;
- gspca_set_default_mode(gspca_dev);
-
- ret = gspca_input_connect(gspca_dev);
- if (ret)
- goto out;
-
- /*
- * Don't take usb_lock for these ioctls. This improves latency if
- * usb_lock is taken for a long time, e.g. when changing a control
- * value, and a new frame is ready to be dequeued.
- */
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
- v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
- if (!gspca_dev->sd_desc->get_chip_ident)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_CHIP_IDENT);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- if (!gspca_dev->sd_desc->get_chip_ident ||
- !gspca_dev->sd_desc->get_register)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER);
- if (!gspca_dev->sd_desc->get_chip_ident ||
- !gspca_dev->sd_desc->set_register)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER);
-#endif
- if (!gspca_dev->sd_desc->get_jcomp)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP);
- if (!gspca_dev->sd_desc->set_jcomp)
- v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP);
-
- /* init video stuff */
- ret = video_register_device(&gspca_dev->vdev,
- VFL_TYPE_GRABBER,
- -1);
- if (ret < 0) {
- pr_err("video_register_device err %d\n", ret);
- goto out;
- }
-
- usb_set_intfdata(intf, gspca_dev);
- PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev));
-
- gspca_input_create_urb(gspca_dev);
-
- return 0;
-out:
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- if (gspca_dev->input_dev)
- input_unregister_device(gspca_dev->input_dev);
-#endif
- v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
- kfree(gspca_dev->usb_buf);
- kfree(gspca_dev);
- return ret;
-}
-EXPORT_SYMBOL(gspca_dev_probe2);
-
-/* same function as the previous one, but check the interface */
-int gspca_dev_probe(struct usb_interface *intf,
- const struct usb_device_id *id,
- const struct sd_desc *sd_desc,
- int dev_size,
- struct module *module)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
-
- /* we don't handle multi-config cameras */
- if (dev->descriptor.bNumConfigurations != 1) {
- pr_err("%04x:%04x too many config\n",
- id->idVendor, id->idProduct);
- return -ENODEV;
- }
-
- /* the USB video interface must be the first one */
- if (dev->actconfig->desc.bNumInterfaces != 1
- && intf->cur_altsetting->desc.bInterfaceNumber != 0)
- return -ENODEV;
-
- return gspca_dev_probe2(intf, id, sd_desc, dev_size, module);
-}
-EXPORT_SYMBOL(gspca_dev_probe);
-
-/*
- * USB disconnection
- *
- * This function must be called by the sub-driver
- * when the device disconnects, after the specific resources are freed.
- */
-void gspca_disconnect(struct usb_interface *intf)
-{
- struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- struct input_dev *input_dev;
-#endif
-
- PDEBUG(D_PROBE, "%s disconnect",
- video_device_node_name(&gspca_dev->vdev));
-
- mutex_lock(&gspca_dev->usb_lock);
-
- usb_set_intfdata(intf, NULL);
- gspca_dev->dev = NULL;
- gspca_dev->present = 0;
- destroy_urbs(gspca_dev);
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- gspca_input_destroy_urb(gspca_dev);
- input_dev = gspca_dev->input_dev;
- if (input_dev) {
- gspca_dev->input_dev = NULL;
- input_unregister_device(input_dev);
- }
-#endif
- /* Free subdriver's streaming resources / stop sd workqueue(s) */
- if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
- gspca_dev->sd_desc->stop0(gspca_dev);
- gspca_dev->streaming = 0;
- wake_up_interruptible(&gspca_dev->wq);
-
- v4l2_device_disconnect(&gspca_dev->v4l2_dev);
- video_unregister_device(&gspca_dev->vdev);
-
- mutex_unlock(&gspca_dev->usb_lock);
-
- /* (this will call gspca_release() immediately or on last close) */
- v4l2_device_put(&gspca_dev->v4l2_dev);
-}
-EXPORT_SYMBOL(gspca_disconnect);
-
-#ifdef CONFIG_PM
-int gspca_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
-
- if (!gspca_dev->streaming)
- return 0;
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->frozen = 1; /* avoid urb error messages */
- gspca_dev->usb_err = 0;
- if (gspca_dev->sd_desc->stopN)
- gspca_dev->sd_desc->stopN(gspca_dev);
- destroy_urbs(gspca_dev);
- gspca_input_destroy_urb(gspca_dev);
- gspca_set_alt0(gspca_dev);
- if (gspca_dev->sd_desc->stop0)
- gspca_dev->sd_desc->stop0(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- return 0;
-}
-EXPORT_SYMBOL(gspca_suspend);
-
-int gspca_resume(struct usb_interface *intf)
-{
- struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
- int streaming, ret = 0;
-
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->frozen = 0;
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->init(gspca_dev);
- gspca_input_create_urb(gspca_dev);
- /*
- * Most subdrivers send all ctrl values on sd_start and thus
- * only write to the device registers on s_ctrl when streaming ->
- * Clear streaming to avoid setting all ctrls twice.
- */
- streaming = gspca_dev->streaming;
- gspca_dev->streaming = 0;
- if (streaming)
- ret = gspca_init_transfer(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
-}
-EXPORT_SYMBOL(gspca_resume);
-#endif
-
-/* -- module insert / remove -- */
-static int __init gspca_init(void)
-{
- pr_info("v" GSPCA_VERSION " registered\n");
- return 0;
-}
-static void __exit gspca_exit(void)
-{
-}
-
-module_init(gspca_init);
-module_exit(gspca_exit);
-
-#ifdef GSPCA_DEBUG
-module_param_named(debug, gspca_debug, int, 0644);
-MODULE_PARM_DESC(debug,
- "Debug (bit) 0x01:error 0x02:probe 0x04:config"
- " 0x08:stream 0x10:frame 0x20:packet"
- " 0x0100: v4l2");
-#endif
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
deleted file mode 100644
index dc688c7f5e48..000000000000
--- a/drivers/media/video/gspca/gspca.h
+++ /dev/null
@@ -1,259 +0,0 @@
-#ifndef GSPCAV2_H
-#define GSPCAV2_H
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <linux/mutex.h>
-
-/* compilation option */
-/*#define GSPCA_DEBUG 1*/
-
-#ifdef GSPCA_DEBUG
-/* GSPCA our debug messages */
-extern int gspca_debug;
-#define PDEBUG(level, fmt, ...) \
-do { \
- if (gspca_debug & (level)) \
- pr_info(fmt, ##__VA_ARGS__); \
-} while (0)
-
-#define D_ERR 0x01
-#define D_PROBE 0x02
-#define D_CONF 0x04
-#define D_STREAM 0x08
-#define D_FRAM 0x10
-#define D_PACK 0x20
-#define D_USBI 0x00
-#define D_USBO 0x00
-#define D_V4L2 0x0100
-#else
-#define PDEBUG(level, fmt, ...)
-#endif
-
-#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
-/* image transfers */
-#define MAX_NURBS 4 /* max number of URBs */
-
-
-/* used to list framerates supported by a camera mode (resolution) */
-struct framerates {
- const u8 *rates;
- int nrates;
-};
-
-/* control definition */
-struct gspca_ctrl {
- s16 val; /* current value */
- s16 def; /* default value */
- s16 min, max; /* minimum and maximum values */
-};
-
-/* device information - set at probe time */
-struct cam {
- const struct v4l2_pix_format *cam_mode; /* size nmodes */
- const struct framerates *mode_framerates; /* must have size nmodes,
- * just like cam_mode */
- struct gspca_ctrl *ctrls; /* control table - size nctrls */
- /* may be NULL */
- u32 bulk_size; /* buffer size when image transfer by bulk */
- u32 input_flags; /* value for ENUM_INPUT status flags */
- u8 nmodes; /* size of cam_mode */
- u8 no_urb_create; /* don't create transfer URBs */
- u8 bulk_nurbs; /* number of URBs in bulk mode
- * - cannot be > MAX_NURBS
- * - when 0 and bulk_size != 0 means
- * 1 URB and submit done by subdriver */
- u8 bulk; /* image transfer by 0:isoc / 1:bulk */
- u8 npkt; /* number of packets in an ISOC message
- * 0 is the default value: 32 packets */
- u8 needs_full_bandwidth;/* Set this flag to notify the bandwidth calc.
- * code that the cam fills all image buffers to
- * the max, even when using compression. */
-};
-
-struct gspca_dev;
-struct gspca_frame;
-
-/* subdriver operations */
-typedef int (*cam_op) (struct gspca_dev *);
-typedef void (*cam_v_op) (struct gspca_dev *);
-typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
-typedef int (*cam_jpg_op) (struct gspca_dev *,
- struct v4l2_jpegcompression *);
-typedef int (*cam_reg_op) (struct gspca_dev *,
- struct v4l2_dbg_register *);
-typedef int (*cam_ident_op) (struct gspca_dev *,
- struct v4l2_dbg_chip_ident *);
-typedef void (*cam_streamparm_op) (struct gspca_dev *,
- struct v4l2_streamparm *);
-typedef int (*cam_qmnu_op) (struct gspca_dev *,
- struct v4l2_querymenu *);
-typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
- u8 *data,
- int len);
-typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev,
- u8 *data,
- int len);
-
-struct ctrl {
- struct v4l2_queryctrl qctrl;
- int (*set)(struct gspca_dev *, __s32);
- int (*get)(struct gspca_dev *, __s32 *);
- cam_v_op set_control;
-};
-
-/* subdriver description */
-struct sd_desc {
-/* information */
- const char *name; /* sub-driver name */
-/* controls */
- const struct ctrl *ctrls; /* static control definition */
- int nctrls;
-/* mandatory operations */
- cam_cf_op config; /* called on probe */
- cam_op init; /* called on probe and resume */
- cam_op init_controls; /* called on probe */
- cam_op start; /* called on stream on after URBs creation */
- cam_pkt_op pkt_scan;
-/* optional operations */
- cam_op isoc_init; /* called on stream on before getting the EP */
- cam_op isoc_nego; /* called when URB submit failed with NOSPC */
- cam_v_op stopN; /* called on stream off - main alt */
- cam_v_op stop0; /* called on stream off & disconnect - alt 0 */
- cam_v_op dq_callback; /* called when a frame has been dequeued */
- cam_jpg_op get_jcomp;
- cam_jpg_op set_jcomp;
- cam_qmnu_op querymenu;
- cam_streamparm_op get_streamparm;
- cam_streamparm_op set_streamparm;
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- cam_reg_op set_register;
- cam_reg_op get_register;
-#endif
- cam_ident_op get_chip_ident;
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- cam_int_pkt_op int_pkt_scan;
- /* other_input makes the gspca core create gspca_dev->input even when
- int_pkt_scan is NULL, for cams with non interrupt driven buttons */
- u8 other_input;
-#endif
-};
-
-/* packet types when moving from iso buf to frame buf */
-enum gspca_packet_type {
- DISCARD_PACKET,
- FIRST_PACKET,
- INTER_PACKET,
- LAST_PACKET
-};
-
-struct gspca_frame {
- __u8 *data; /* frame buffer */
- int vma_use_count;
- struct v4l2_buffer v4l2_buf;
-};
-
-struct gspca_dev {
- struct video_device vdev; /* !! must be the first item */
- struct module *module; /* subdriver handling the device */
- struct v4l2_device v4l2_dev;
- struct usb_device *dev;
- struct file *capt_file; /* file doing video capture */
- /* protected by queue_lock */
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- struct input_dev *input_dev;
- char phys[64]; /* physical device path */
-#endif
-
- struct cam cam; /* device information */
- const struct sd_desc *sd_desc; /* subdriver description */
- unsigned ctrl_dis; /* disabled controls (bit map) */
- unsigned ctrl_inac; /* inactive controls (bit map) */
- struct v4l2_ctrl_handler ctrl_handler;
-
- /* autogain and exposure or gain control cluster, these are global as
- the autogain/exposure functions in autogain_functions.c use them */
- struct {
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *gain;
- int exp_too_low_cnt, exp_too_high_cnt;
- };
-
-#define USB_BUF_SZ 64
- __u8 *usb_buf; /* buffer for USB exchanges */
- struct urb *urb[MAX_NURBS];
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- struct urb *int_urb;
-#endif
-
- __u8 *frbuf; /* buffer for nframes */
- struct gspca_frame frame[GSPCA_MAX_FRAMES];
- u8 *image; /* image beeing filled */
- __u32 frsz; /* frame size */
- u32 image_len; /* current length of image */
- atomic_t fr_q; /* next frame to queue */
- atomic_t fr_i; /* frame being filled */
- signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */
- char nframes; /* number of frames */
- u8 fr_o; /* next frame to dequeue */
- __u8 last_packet_type;
- __s8 empty_packet; /* if (-1) don't check empty packets */
- __u8 streaming; /* protected by both mutexes (*) */
-
- __u8 curr_mode; /* current camera mode */
- __u32 pixfmt; /* current mode parameters */
- __u16 width;
- __u16 height;
- __u32 sequence; /* frame sequence number */
-
- wait_queue_head_t wq; /* wait queue */
- struct mutex usb_lock; /* usb exchange protection */
- struct mutex queue_lock; /* ISOC queue protection */
- int usb_err; /* USB error - protected by usb_lock */
- u16 pkt_size; /* ISOC packet size */
-#ifdef CONFIG_PM
- char frozen; /* suspend - resume */
-#endif
- char present; /* device connected */
- char nbufread; /* number of buffers for read() */
- char memory; /* memory type (V4L2_MEMORY_xxx) */
- __u8 iface; /* USB interface number */
- __u8 alt; /* USB alternate setting */
- u8 audio; /* presence of audio device */
-
- /* (*) These variables are proteced by both usb_lock and queue_lock,
- that is any code setting them is holding *both*, which means that
- any code getting them needs to hold at least one of them */
-};
-
-int gspca_dev_probe(struct usb_interface *intf,
- const struct usb_device_id *id,
- const struct sd_desc *sd_desc,
- int dev_size,
- struct module *module);
-int gspca_dev_probe2(struct usb_interface *intf,
- const struct usb_device_id *id,
- const struct sd_desc *sd_desc,
- int dev_size,
- struct module *module);
-void gspca_disconnect(struct usb_interface *intf);
-void gspca_frame_add(struct gspca_dev *gspca_dev,
- enum gspca_packet_type packet_type,
- const u8 *data,
- int len);
-#ifdef CONFIG_PM
-int gspca_suspend(struct usb_interface *intf, pm_message_t message);
-int gspca_resume(struct usb_interface *intf);
-#endif
-int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum,
- int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
-int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
- int avg_lum, int desired_avg_lum, int deadzone);
-
-#endif /* GSPCAV2_H */
diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
deleted file mode 100644
index 26b99310d628..000000000000
--- a/drivers/media/video/gspca/jeilinj.c
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * Jeilinj subdriver
- *
- * Supports some Jeilin dual-mode cameras which use bulk transport and
- * download raw JPEG data.
- *
- * Copyright (C) 2009 Theodore Kilgore
- *
- * Sportscam DV15 support and control settings are
- * Copyright (C) 2011 Patrice Chotard
- *
- * This program is free software; you can redistribute it and/or modify
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "jeilinj"
-
-#include <linux/slab.h>
-#include "gspca.h"
-#include "jpeg.h"
-
-MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
-MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* Default timeouts, in ms */
-#define JEILINJ_CMD_TIMEOUT 500
-#define JEILINJ_CMD_DELAY 160
-#define JEILINJ_DATA_TIMEOUT 1000
-
-/* Maximum transfer size to use. */
-#define JEILINJ_MAX_TRANSFER 0x200
-#define FRAME_HEADER_LEN 0x10
-#define FRAME_START 0xFFFFFFFF
-
-enum {
- SAKAR_57379,
- SPORTSCAM_DV15,
-};
-
-#define CAMQUALITY_MIN 0 /* highest cam quality */
-#define CAMQUALITY_MAX 97 /* lowest cam quality */
-
-/* Structure to hold all of our device specific stuff */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- int blocks_left;
- const struct v4l2_pix_format *cap_mode;
- struct v4l2_ctrl *freq;
- struct v4l2_ctrl *jpegqual;
- /* Driver stuff */
- u8 type;
- u8 quality; /* image quality */
-#define QUALITY_MIN 35
-#define QUALITY_MAX 85
-#define QUALITY_DEF 85
- u8 jpeg_hdr[JPEG_HDR_SZ];
-};
-
-struct jlj_command {
- unsigned char instruction[2];
- unsigned char ack_wanted;
- unsigned char delay;
-};
-
-/* AFAICT these cameras will only do 320x240. */
-static struct v4l2_pix_format jlj_mode[] = {
- { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
- { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0}
-};
-
-/*
- * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
- * and 0x82 for bulk transfer.
- */
-
-/* All commands are two bytes only */
-static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
-{
- int retval;
-
- if (gspca_dev->usb_err < 0)
- return;
- memcpy(gspca_dev->usb_buf, command, 2);
- retval = usb_bulk_msg(gspca_dev->dev,
- usb_sndbulkpipe(gspca_dev->dev, 3),
- gspca_dev->usb_buf, 2, NULL, 500);
- if (retval < 0) {
- pr_err("command write [%02x] error %d\n",
- gspca_dev->usb_buf[0], retval);
- gspca_dev->usb_err = retval;
- }
-}
-
-/* Responses are one byte only */
-static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
-{
- int retval;
-
- if (gspca_dev->usb_err < 0)
- return;
- retval = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x84),
- gspca_dev->usb_buf, 1, NULL, 500);
- response = gspca_dev->usb_buf[0];
- if (retval < 0) {
- pr_err("read command [%02x] error %d\n",
- gspca_dev->usb_buf[0], retval);
- gspca_dev->usb_err = retval;
- }
-}
-
-static void setfreq(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 freq_commands[][2] = {
- {0x71, 0x80},
- {0x70, 0x07}
- };
-
- freq_commands[0][1] |= val >> 1;
-
- jlj_write2(gspca_dev, freq_commands[0]);
- jlj_write2(gspca_dev, freq_commands[1]);
-}
-
-static void setcamquality(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 quality_commands[][2] = {
- {0x71, 0x1E},
- {0x70, 0x06}
- };
- u8 camquality;
-
- /* adapt camera quality from jpeg quality */
- camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX)
- / (QUALITY_MAX - QUALITY_MIN);
- quality_commands[0][1] += camquality;
-
- jlj_write2(gspca_dev, quality_commands[0]);
- jlj_write2(gspca_dev, quality_commands[1]);
-}
-
-static void setautogain(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 autogain_commands[][2] = {
- {0x94, 0x02},
- {0xcf, 0x00}
- };
-
- autogain_commands[1][1] = val << 4;
-
- jlj_write2(gspca_dev, autogain_commands[0]);
- jlj_write2(gspca_dev, autogain_commands[1]);
-}
-
-static void setred(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 setred_commands[][2] = {
- {0x94, 0x02},
- {0xe6, 0x00}
- };
-
- setred_commands[1][1] = val;
-
- jlj_write2(gspca_dev, setred_commands[0]);
- jlj_write2(gspca_dev, setred_commands[1]);
-}
-
-static void setgreen(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 setgreen_commands[][2] = {
- {0x94, 0x02},
- {0xe7, 0x00}
- };
-
- setgreen_commands[1][1] = val;
-
- jlj_write2(gspca_dev, setgreen_commands[0]);
- jlj_write2(gspca_dev, setgreen_commands[1]);
-}
-
-static void setblue(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 setblue_commands[][2] = {
- {0x94, 0x02},
- {0xe9, 0x00}
- };
-
- setblue_commands[1][1] = val;
-
- jlj_write2(gspca_dev, setblue_commands[0]);
- jlj_write2(gspca_dev, setblue_commands[1]);
-}
-
-static int jlj_start(struct gspca_dev *gspca_dev)
-{
- int i;
- int start_commands_size;
- u8 response = 0xff;
- struct sd *sd = (struct sd *) gspca_dev;
- struct jlj_command start_commands[] = {
- {{0x71, 0x81}, 0, 0},
- {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY},
- {{0x95, 0x70}, 1, 0},
- {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0},
- {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY},
- {{0x95, 0x70}, 1, 0},
- {{0x71, 0x00}, 0, 0}, /* start streaming ??*/
- {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY},
- {{0x95, 0x70}, 1, 0},
-#define SPORTSCAM_DV15_CMD_SIZE 9
- {{0x94, 0x02}, 0, 0},
- {{0xde, 0x24}, 0, 0},
- {{0x94, 0x02}, 0, 0},
- {{0xdd, 0xf0}, 0, 0},
- {{0x94, 0x02}, 0, 0},
- {{0xe3, 0x2c}, 0, 0},
- {{0x94, 0x02}, 0, 0},
- {{0xe4, 0x00}, 0, 0},
- {{0x94, 0x02}, 0, 0},
- {{0xe5, 0x00}, 0, 0},
- {{0x94, 0x02}, 0, 0},
- {{0xe6, 0x2c}, 0, 0},
- {{0x94, 0x03}, 0, 0},
- {{0xaa, 0x00}, 0, 0}
- };
-
- sd->blocks_left = 0;
- /* Under Windows, USB spy shows that only the 9 first start
- * commands are used for SPORTSCAM_DV15 webcam
- */
- if (sd->type == SPORTSCAM_DV15)
- start_commands_size = SPORTSCAM_DV15_CMD_SIZE;
- else
- start_commands_size = ARRAY_SIZE(start_commands);
-
- for (i = 0; i < start_commands_size; i++) {
- jlj_write2(gspca_dev, start_commands[i].instruction);
- if (start_commands[i].delay)
- msleep(start_commands[i].delay);
- if (start_commands[i].ack_wanted)
- jlj_read1(gspca_dev, response);
- }
- setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
- msleep(2);
- setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq));
- if (gspca_dev->usb_err < 0)
- PDEBUG(D_ERR, "Start streaming command failed");
- return gspca_dev->usb_err;
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int packet_type;
- u32 header_marker;
-
- PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0",
- len, JEILINJ_MAX_TRANSFER);
- if (len != JEILINJ_MAX_TRANSFER) {
- PDEBUG(D_PACK, "bad length");
- goto discard;
- }
- /* check if it's start of frame */
- header_marker = ((u32 *)data)[0];
- if (header_marker == FRAME_START) {
- sd->blocks_left = data[0x0a] - 1;
- PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left);
- /* Start a new frame, and add the JPEG header, first thing */
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
- /* Toss line 0 of data block 0, keep the rest. */
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data + FRAME_HEADER_LEN,
- JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
- } else if (sd->blocks_left > 0) {
- PDEBUG(D_STREAM, "%d blocks remaining for frame",
- sd->blocks_left);
- sd->blocks_left -= 1;
- if (sd->blocks_left == 0)
- packet_type = LAST_PACKET;
- else
- packet_type = INTER_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- data, JEILINJ_MAX_TRANSFER);
- } else
- goto discard;
- return;
-discard:
- /* Discard data until a new frame starts. */
- gspca_dev->last_packet_type = DISCARD_PACKET;
-}
-
-/* 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 *dev = (struct sd *) gspca_dev;
-
- dev->type = id->driver_info;
- dev->quality = QUALITY_DEF;
-
- cam->cam_mode = jlj_mode;
- cam->nmodes = ARRAY_SIZE(jlj_mode);
- cam->bulk = 1;
- cam->bulk_nurbs = 1;
- cam->bulk_size = JEILINJ_MAX_TRANSFER;
- return 0;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- int i;
- u8 *buf;
- static u8 stop_commands[][2] = {
- {0x71, 0x00},
- {0x70, 0x09},
- {0x71, 0x80},
- {0x70, 0x05}
- };
-
- for (;;) {
- /* get the image remaining blocks */
- usb_bulk_msg(gspca_dev->dev,
- gspca_dev->urb[0]->pipe,
- gspca_dev->urb[0]->transfer_buffer,
- JEILINJ_MAX_TRANSFER, NULL,
- JEILINJ_DATA_TIMEOUT);
-
- /* search for 0xff 0xd9 (EOF for JPEG) */
- i = 0;
- buf = gspca_dev->urb[0]->transfer_buffer;
- while ((i < (JEILINJ_MAX_TRANSFER - 1)) &&
- ((buf[i] != 0xff) || (buf[i+1] != 0xd9)))
- i++;
-
- if (i != (JEILINJ_MAX_TRANSFER - 1))
- /* last remaining block found */
- break;
- }
-
- for (i = 0; i < ARRAY_SIZE(stop_commands); i++)
- jlj_write2(gspca_dev, stop_commands[i]);
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- return gspca_dev->usb_err;
-}
-
-/* Set up for getting frames. */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *dev = (struct sd *) gspca_dev;
-
- /* create the JPEG header */
- jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
- 0x21); /* JPEG 422 */
- jpeg_set_qual(dev->jpeg_hdr, dev->quality);
- PDEBUG(D_STREAM, "Start streaming at %dx%d",
- gspca_dev->height, gspca_dev->width);
- jlj_start(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-/* Table of supported USB devices */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379},
- {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- setfreq(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_RED_BALANCE:
- setred(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_GAIN:
- setgreen(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_BLUE_BALANCE:
- setblue(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_AUTOGAIN:
- setautogain(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
- setcamquality(gspca_dev, ctrl->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- static const struct v4l2_ctrl_config custom_autogain = {
- .ops = &sd_ctrl_ops,
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Automatic Gain (and Exposure)",
- .max = 3,
- .step = 1,
- .def = 0,
- };
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 6);
- sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ);
- v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 3, 1, 2);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 3, 1, 2);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2);
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- return 0;
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
- return 0;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
-
-/* sub-driver description */
-static const struct sd_desc sd_desc_sakar_57379 = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .pkt_scan = sd_pkt_scan,
-};
-
-/* sub-driver description */
-static const struct sd_desc sd_desc_sportscam_dv15 = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .stopN = sd_stopN,
- .pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
-};
-
-static const struct sd_desc *sd_desc[2] = {
- &sd_desc_sakar_57379,
- &sd_desc_sportscam_dv15
-};
-
-/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- return gspca_dev_probe(intf, id,
- sd_desc[id->driver_info],
- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c
deleted file mode 100644
index cf9d9fca5b84..000000000000
--- a/drivers/media/video/gspca/jl2005bcd.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Jeilin JL2005B/C/D library
- *
- * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * 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 "jl2005bcd"
-
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-#include "gspca.h"
-
-
-MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
-MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* Default timeouts, in ms */
-#define JL2005C_CMD_TIMEOUT 500
-#define JL2005C_DATA_TIMEOUT 1000
-
-/* Maximum transfer size to use. */
-#define JL2005C_MAX_TRANSFER 0x200
-#define FRAME_HEADER_LEN 16
-
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char firmware_id[6];
- const struct v4l2_pix_format *cap_mode;
- /* Driver stuff */
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
- u8 frame_brightness;
- int block_size; /* block size of camera */
- int vga; /* 1 if vga cam, 0 if cif cam */
-};
-
-
-/* Camera has two resolution settings. What they are depends on model. */
-static const struct v4l2_pix_format cif_mode[] = {
- {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-
-/*
- * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
- * and 0x82 for bulk data transfer.
- */
-
-/* All commands are two bytes only */
-static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
-{
- int retval;
-
- memcpy(gspca_dev->usb_buf, command, 2);
- retval = usb_bulk_msg(gspca_dev->dev,
- usb_sndbulkpipe(gspca_dev->dev, 3),
- gspca_dev->usb_buf, 2, NULL, 500);
- if (retval < 0)
- pr_err("command write [%02x] error %d\n",
- gspca_dev->usb_buf[0], retval);
- return retval;
-}
-
-/* Response to a command is one byte in usb_buf[0], only if requested. */
-static int jl2005c_read1(struct gspca_dev *gspca_dev)
-{
- int retval;
-
- retval = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x84),
- gspca_dev->usb_buf, 1, NULL, 500);
- if (retval < 0)
- pr_err("read command [0x%02x] error %d\n",
- gspca_dev->usb_buf[0], retval);
- return retval;
-}
-
-/* Response appears in gspca_dev->usb_buf[0] */
-static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
-{
- int retval;
-
- static u8 instruction[2] = {0x95, 0x00};
- /* put register to read in byte 1 */
- instruction[1] = reg;
- /* Send the read request */
- retval = jl2005c_write2(gspca_dev, instruction);
- if (retval < 0)
- return retval;
- retval = jl2005c_read1(gspca_dev);
-
- return retval;
-}
-
-static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
-{
- int i;
- int retval;
- int frame_brightness = 0;
-
- static u8 instruction[2] = {0x7f, 0x01};
-
- retval = jl2005c_write2(gspca_dev, instruction);
- if (retval < 0)
- return retval;
-
- i = 0;
- while (i < 20 && !frame_brightness) {
- /* If we tried 20 times, give up. */
- retval = jl2005c_read_reg(gspca_dev, 0x7e);
- if (retval < 0)
- return retval;
- frame_brightness = gspca_dev->usb_buf[0];
- retval = jl2005c_read_reg(gspca_dev, 0x7d);
- if (retval < 0)
- return retval;
- i++;
- }
- PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
- return retval;
-}
-
-static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
- unsigned char value)
-{
- int retval;
- u8 instruction[2];
-
- instruction[0] = reg;
- instruction[1] = value;
-
- retval = jl2005c_write2(gspca_dev, instruction);
- if (retval < 0)
- return retval;
-
- return retval;
-}
-
-static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- int i = 0;
- int retval = -1;
- unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
-
- PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
- /* Read the first ID byte once for warmup */
- retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
- PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
- if (retval < 0)
- return retval;
- /* Now actually get the ID string */
- for (i = 0; i < 6; i++) {
- retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
- if (retval < 0)
- return retval;
- sd->firmware_id[i] = gspca_dev->usb_buf[0];
- }
- PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
- sd->firmware_id[0],
- sd->firmware_id[1],
- sd->firmware_id[2],
- sd->firmware_id[3],
- sd->firmware_id[4],
- sd->firmware_id[5]);
- return 0;
-}
-
-static int jl2005c_stream_start_vga_lg
- (struct gspca_dev *gspca_dev)
-{
- int i;
- int retval = -1;
- static u8 instruction[][2] = {
- {0x05, 0x00},
- {0x7c, 0x00},
- {0x7d, 0x18},
- {0x02, 0x00},
- {0x01, 0x00},
- {0x04, 0x52},
- };
-
- for (i = 0; i < ARRAY_SIZE(instruction); i++) {
- msleep(60);
- retval = jl2005c_write2(gspca_dev, instruction[i]);
- if (retval < 0)
- return retval;
- }
- msleep(60);
- return retval;
-}
-
-static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
-{
- int i;
- int retval = -1;
- static u8 instruction[][2] = {
- {0x06, 0x00},
- {0x7c, 0x00},
- {0x7d, 0x1a},
- {0x02, 0x00},
- {0x01, 0x00},
- {0x04, 0x52},
- };
-
- for (i = 0; i < ARRAY_SIZE(instruction); i++) {
- msleep(60);
- retval = jl2005c_write2(gspca_dev, instruction[i]);
- if (retval < 0)
- return retval;
- }
- msleep(60);
- return retval;
-}
-
-static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
-{
- int i;
- int retval = -1;
- static u8 instruction[][2] = {
- {0x05, 0x00},
- {0x7c, 0x00},
- {0x7d, 0x30},
- {0x02, 0x00},
- {0x01, 0x00},
- {0x04, 0x42},
- };
-
- for (i = 0; i < ARRAY_SIZE(instruction); i++) {
- msleep(60);
- retval = jl2005c_write2(gspca_dev, instruction[i]);
- if (retval < 0)
- return retval;
- }
- msleep(60);
- return retval;
-}
-
-static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
-{
- int i;
- int retval = -1;
- static u8 instruction[][2] = {
- {0x06, 0x00},
- {0x7c, 0x00},
- {0x7d, 0x32},
- {0x02, 0x00},
- {0x01, 0x00},
- {0x04, 0x42},
- };
-
- for (i = 0; i < ARRAY_SIZE(instruction); i++) {
- msleep(60);
- retval = jl2005c_write2(gspca_dev, instruction[i]);
- if (retval < 0)
- return retval;
- }
- msleep(60);
- return retval;
-}
-
-
-static int jl2005c_stop(struct gspca_dev *gspca_dev)
-{
- int retval;
-
- retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
- return retval;
-}
-
-/* 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 the gspca usb_lock is
- * used when performing the one USB control operation inside the workqueue,
- * which tells the camera to close the stream. In practice the only thing
- * which needs to be protected against is the usb_set_interface call that
- * gspca makes during stream_off. Otherwise the camera doesn't provide any
- * controls that the user could try to change.
- */
-static void jl2005c_dostream(struct work_struct *work)
-{
- struct sd *dev = container_of(work, struct sd, work_struct);
- struct gspca_dev *gspca_dev = &dev->gspca_dev;
- int bytes_left = 0; /* bytes remaining in current frame. */
- int data_len; /* size to use for the next read. */
- int header_read = 0;
- unsigned char header_sig[2] = {0x4a, 0x4c};
- int act_len;
- int packet_type;
- int ret;
- u8 *buffer;
-
- buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
- if (!buffer) {
- pr_err("Couldn't allocate USB buffer\n");
- goto quit_stream;
- }
-
- while (gspca_dev->dev && gspca_dev->streaming) {
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
- /* Check if this is a new frame. If so, start the frame first */
- if (!header_read) {
- mutex_lock(&gspca_dev->usb_lock);
- ret = jl2005c_start_new_frame(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- if (ret < 0)
- goto quit_stream;
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x82),
- buffer, JL2005C_MAX_TRANSFER, &act_len,
- JL2005C_DATA_TIMEOUT);
- PDEBUG(D_PACK,
- "Got %d bytes out of %d for header",
- act_len, JL2005C_MAX_TRANSFER);
- if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
- goto quit_stream;
- /* Check whether we actually got the first blodk */
- if (memcmp(header_sig, buffer, 2) != 0) {
- pr_err("First block is not the first block\n");
- goto quit_stream;
- }
- /* total size to fetch is byte 7, times blocksize
- * of which we already got act_len */
- bytes_left = buffer[0x07] * dev->block_size - act_len;
- PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
- /* We keep the header. It has other information, too.*/
- packet_type = FIRST_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- buffer, act_len);
- header_read = 1;
- }
- while (bytes_left > 0 && gspca_dev->dev) {
- data_len = bytes_left > JL2005C_MAX_TRANSFER ?
- JL2005C_MAX_TRANSFER : bytes_left;
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x82),
- buffer, data_len, &act_len,
- JL2005C_DATA_TIMEOUT);
- if (ret < 0 || act_len < data_len)
- goto quit_stream;
- PDEBUG(D_PACK,
- "Got %d bytes out of %d for frame",
- data_len, bytes_left);
- bytes_left -= data_len;
- if (bytes_left == 0) {
- packet_type = LAST_PACKET;
- header_read = 0;
- } else
- packet_type = INTER_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- buffer, data_len);
- }
- }
-quit_stream:
- if (gspca_dev->dev) {
- mutex_lock(&gspca_dev->usb_lock);
- jl2005c_stop(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- }
- kfree(buffer);
-}
-
-
-
-
-/* This function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct cam *cam;
- struct sd *sd = (struct sd *) gspca_dev;
-
- cam = &gspca_dev->cam;
- /* We don't use the buffer gspca allocates so make it small. */
- cam->bulk_size = 64;
- cam->bulk = 1;
- /* For the rest, the camera needs to be detected */
- jl2005c_get_firmware_id(gspca_dev);
- /* Here are some known firmware IDs
- * First some JL2005B cameras
- * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam
- * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B
- * JL2005C cameras
- * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512
- * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly
- * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz
- *
- * Based upon this scanty evidence, we can detect a CIF camera by
- * testing byte 0 for 0x4x.
- */
- if ((sd->firmware_id[0] & 0xf0) == 0x40) {
- cam->cam_mode = cif_mode;
- cam->nmodes = ARRAY_SIZE(cif_mode);
- sd->block_size = 0x80;
- } else {
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- sd->block_size = 0x200;
- }
-
- INIT_WORK(&sd->work_struct, jl2005c_dostream);
-
- return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- return 0;
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
-
- struct sd *sd = (struct sd *) gspca_dev;
- sd->cap_mode = gspca_dev->cam.cam_mode;
-
- switch (gspca_dev->width) {
- case 640:
- PDEBUG(D_STREAM, "Start streaming at vga resolution");
- jl2005c_stream_start_vga_lg(gspca_dev);
- break;
- case 320:
- PDEBUG(D_STREAM, "Start streaming at qvga resolution");
- jl2005c_stream_start_vga_small(gspca_dev);
- break;
- case 352:
- PDEBUG(D_STREAM, "Start streaming at cif resolution");
- jl2005c_stream_start_cif_lg(gspca_dev);
- break;
- case 176:
- PDEBUG(D_STREAM, "Start streaming at qcif resolution");
- jl2005c_stream_start_cif_small(gspca_dev);
- break;
- default:
- pr_err("Unknown resolution specified\n");
- return -1;
- }
-
- /* 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 sq905c_dostream to finish */
- destroy_workqueue(dev->work_thread);
- dev->work_thread = NULL;
- mutex_lock(&gspca_dev->usb_lock);
-}
-
-
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .start = sd_start,
- .stop0 = sd_stop0,
-};
-
-/* -- module initialisation -- */
-static const __devinitdata struct usb_device_id device_table[] = {
- {USB_DEVICE(0x0979, 0x0227)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-/* -- module insert / remove -- */
-static int __init sd_mod_init(void)
-{
- int ret;
-
- ret = usb_register(&sd_driver);
- if (ret < 0)
- return ret;
- return 0;
-}
-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/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
deleted file mode 100644
index 575b75bacb62..000000000000
--- a/drivers/media/video/gspca/m5602/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-obj-$(CONFIG_USB_M5602) += gspca_m5602.o
-
-gspca_m5602-objs := m5602_core.o \
- m5602_ov9650.o \
- m5602_ov7660.o \
- m5602_mt9m111.o \
- m5602_po1030.o \
- m5602_s5k83a.o \
- m5602_s5k4aa.o
-
-ccflags-y += -I$(srctree)/drivers/media/video/gspca
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
deleted file mode 100644
index bfc7cefa59f8..000000000000
--- a/drivers/media/video/gspca/ov519.c
+++ /dev/null
@@ -1,4991 +0,0 @@
-/**
- * OV519 driver
- *
- * Copyright (C) 2008-2011 Jean-François Moine <moinejf@free.fr>
- * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
- *
- * This module is adapted from the ov51x-jpeg package, which itself
- * was adapted from the ov511 driver.
- *
- * Original copyright for the ov511 driver is:
- *
- * Copyright (c) 1999-2006 Mark W. McClelland
- * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
- * Many improvements by Bret Wallach <bwallac1@san.rr.com>
- * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
- * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
- * Changes by Claudio Matsuoka <claudio@conectiva.com>
- *
- * ov51x-jpeg original copyright is:
- *
- * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
- * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "ov519"
-
-#include <linux/input.h>
-#include "gspca.h"
-
-/* The jpeg_hdr is used by w996Xcf only */
-/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
-#define CONEX_CAM
-#include "jpeg.h"
-
-MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
-MODULE_DESCRIPTION("OV519 USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* global parameters */
-static int frame_rate;
-
-/* Number of times to retry a failed I2C transaction. Increase this if you
- * are getting "Failed to read sensor ID..." */
-static int i2c_detect_tries = 10;
-
-/* ov519 device descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct v4l2_ctrl *jpegqual;
- struct v4l2_ctrl *freq;
- struct { /* h/vflip control cluster */
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- };
- struct { /* autobrightness/brightness control cluster */
- struct v4l2_ctrl *autobright;
- struct v4l2_ctrl *brightness;
- };
-
- u8 packet_nr;
-
- char bridge;
-#define BRIDGE_OV511 0
-#define BRIDGE_OV511PLUS 1
-#define BRIDGE_OV518 2
-#define BRIDGE_OV518PLUS 3
-#define BRIDGE_OV519 4 /* = ov530 */
-#define BRIDGE_OVFX2 5
-#define BRIDGE_W9968CF 6
-#define BRIDGE_MASK 7
-
- char invert_led;
-#define BRIDGE_INVERT_LED 8
-
- char snapshot_pressed;
- char snapshot_needs_reset;
-
- /* Determined by sensor type */
- u8 sif;
-
-#define QUALITY_MIN 50
-#define QUALITY_MAX 70
-#define QUALITY_DEF 50
-
- u8 stopped; /* Streaming is temporarily paused */
- u8 first_frame;
-
- u8 frame_rate; /* current Framerate */
- u8 clockdiv; /* clockdiv override */
-
- s8 sensor; /* Type of image sensor chip (SEN_*) */
-
- u8 sensor_addr;
- u16 sensor_width;
- u16 sensor_height;
- s16 sensor_reg_cache[256];
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
-};
-enum sensors {
- SEN_OV2610,
- SEN_OV2610AE,
- SEN_OV3610,
- SEN_OV6620,
- SEN_OV6630,
- SEN_OV66308AF,
- SEN_OV7610,
- SEN_OV7620,
- SEN_OV7620AE,
- SEN_OV7640,
- SEN_OV7648,
- SEN_OV7660,
- SEN_OV7670,
- SEN_OV76BE,
- SEN_OV8610,
- SEN_OV9600,
-};
-
-/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
- the ov sensors which is already present here. When we have the time we
- really should move the sensor drivers to v4l2 sub drivers. */
-#include "w996Xcf.c"
-
-/* table of the disabled controls */
-struct ctrl_valid {
- int has_brightness:1;
- int has_contrast:1;
- int has_exposure:1;
- int has_autogain:1;
- int has_sat:1;
- int has_hvflip:1;
- int has_autobright:1;
- int has_freq:1;
-};
-
-static const struct ctrl_valid valid_controls[] = {
- [SEN_OV2610] = {
- .has_exposure = 1,
- .has_autogain = 1,
- },
- [SEN_OV2610AE] = {
- .has_exposure = 1,
- .has_autogain = 1,
- },
- [SEN_OV3610] = {
- /* No controls */
- },
- [SEN_OV6620] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV6630] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV66308AF] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV7610] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV7620] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV7620AE] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV7640] = {
- .has_brightness = 1,
- .has_sat = 1,
- .has_freq = 1,
- },
- [SEN_OV7648] = {
- .has_brightness = 1,
- .has_sat = 1,
- .has_freq = 1,
- },
- [SEN_OV7660] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_hvflip = 1,
- .has_freq = 1,
- },
- [SEN_OV7670] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_hvflip = 1,
- .has_freq = 1,
- },
- [SEN_OV76BE] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- .has_freq = 1,
- },
- [SEN_OV8610] = {
- .has_brightness = 1,
- .has_contrast = 1,
- .has_sat = 1,
- .has_autobright = 1,
- },
- [SEN_OV9600] = {
- .has_exposure = 1,
- .has_autogain = 1,
- },
-};
-
-static const struct v4l2_pix_format ov519_vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-static const struct v4l2_pix_format ov519_sif_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-/* Note some of the sizeimage values for the ov511 / ov518 may seem
- larger then necessary, however they need to be this big as the ov511 /
- ov518 always fills the entire isoc frame, using 0 padding bytes when
- it doesn't have any data. So with low framerates the amount of data
- transferred can become quite large (libv4l will remove all the 0 padding
- in userspace). */
-static const struct v4l2_pix_format ov518_vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 2,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-static const struct v4l2_pix_format ov518_sif_mode[] = {
- {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 70000,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 70000,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-static const struct v4l2_pix_format ov511_vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 2,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-static const struct v4l2_pix_format ov511_sif_mode[] = {
- {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 70000,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 70000,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-static const struct v4l2_pix_format ovfx2_vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-static const struct v4l2_pix_format ovfx2_cif_mode[] = {
- {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 3},
- {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .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,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 800,
- .sizeimage = 800 * 600,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 1024,
- .sizeimage = 1024 * 768,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 1600,
- .sizeimage = 1600 * 1200,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 2048,
- .sizeimage = 2048 * 1536,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-static const struct v4l2_pix_format ovfx2_ov9600_mode[] = {
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 1280,
- .sizeimage = 1280 * 1024,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-
-/* Registers common to OV511 / OV518 */
-#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
-#define R51x_SYS_RESET 0x50
- /* Reset type flags */
- #define OV511_RESET_OMNICE 0x08
-#define R51x_SYS_INIT 0x53
-#define R51x_SYS_SNAP 0x52
-#define R51x_SYS_CUST_ID 0x5f
-#define R51x_COMP_LUT_BEGIN 0x80
-
-/* OV511 Camera interface register numbers */
-#define R511_CAM_DELAY 0x10
-#define R511_CAM_EDGE 0x11
-#define R511_CAM_PXCNT 0x12
-#define R511_CAM_LNCNT 0x13
-#define R511_CAM_PXDIV 0x14
-#define R511_CAM_LNDIV 0x15
-#define R511_CAM_UV_EN 0x16
-#define R511_CAM_LINE_MODE 0x17
-#define R511_CAM_OPTS 0x18
-
-#define R511_SNAP_FRAME 0x19
-#define R511_SNAP_PXCNT 0x1a
-#define R511_SNAP_LNCNT 0x1b
-#define R511_SNAP_PXDIV 0x1c
-#define R511_SNAP_LNDIV 0x1d
-#define R511_SNAP_UV_EN 0x1e
-#define R511_SNAP_OPTS 0x1f
-
-#define R511_DRAM_FLOW_CTL 0x20
-#define R511_FIFO_OPTS 0x31
-#define R511_I2C_CTL 0x40
-#define R511_SYS_LED_CTL 0x55 /* OV511+ only */
-#define R511_COMP_EN 0x78
-#define R511_COMP_LUT_EN 0x79
-
-/* OV518 Camera interface register numbers */
-#define R518_GPIO_OUT 0x56 /* OV518(+) only */
-#define R518_GPIO_CTL 0x57 /* OV518(+) only */
-
-/* OV519 Camera interface register numbers */
-#define OV519_R10_H_SIZE 0x10
-#define OV519_R11_V_SIZE 0x11
-#define OV519_R12_X_OFFSETL 0x12
-#define OV519_R13_X_OFFSETH 0x13
-#define OV519_R14_Y_OFFSETL 0x14
-#define OV519_R15_Y_OFFSETH 0x15
-#define OV519_R16_DIVIDER 0x16
-#define OV519_R20_DFR 0x20
-#define OV519_R25_FORMAT 0x25
-
-/* OV519 System Controller register numbers */
-#define OV519_R51_RESET1 0x51
-#define OV519_R54_EN_CLK1 0x54
-#define OV519_R57_SNAPSHOT 0x57
-
-#define OV519_GPIO_DATA_OUT0 0x71
-#define OV519_GPIO_IO_CTRL0 0x72
-
-/*#define OV511_ENDPOINT_ADDRESS 1 * Isoc endpoint number */
-
-/*
- * The FX2 chip does not give us a zero length read at end of frame.
- * It does, however, give a short read at the end of a frame, if
- * necessary, rather than run two frames together.
- *
- * By choosing the right bulk transfer size, we are guaranteed to always
- * get a short read for the last read of each frame. Frame sizes are
- * always a composite number (width * height, or a multiple) so if we
- * choose a prime number, we are guaranteed that the last read of a
- * frame will be short.
- *
- * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
- * otherwise EOVERFLOW "babbling" errors occur. I have not been able
- * to figure out why. [PMiller]
- *
- * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
- *
- * It isn't enough to know the number of bytes per frame, in case we
- * have data dropouts or buffer overruns (even though the FX2 double
- * buffers, there are some pretty strict real time constraints for
- * isochronous transfer for larger frame sizes).
- */
-/*jfm: this value does not work for 800x600 - see isoc_init */
-#define OVFX2_BULK_SIZE (13 * 4096)
-
-/* I2C registers */
-#define R51x_I2C_W_SID 0x41
-#define R51x_I2C_SADDR_3 0x42
-#define R51x_I2C_SADDR_2 0x43
-#define R51x_I2C_R_SID 0x44
-#define R51x_I2C_DATA 0x45
-#define R518_I2C_CTL 0x47 /* OV518(+) only */
-#define OVFX2_I2C_ADDR 0x00
-
-/* I2C ADDRESSES */
-#define OV7xx0_SID 0x42
-#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */
-#define OV8xx0_SID 0xa0
-#define OV6xx0_SID 0xc0
-
-/* OV7610 registers */
-#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */
-#define OV7610_REG_BLUE 0x01 /* blue channel balance */
-#define OV7610_REG_RED 0x02 /* red channel balance */
-#define OV7610_REG_SAT 0x03 /* saturation */
-#define OV8610_REG_HUE 0x04 /* 04 reserved */
-#define OV7610_REG_CNT 0x05 /* Y contrast */
-#define OV7610_REG_BRT 0x06 /* Y brightness */
-#define OV7610_REG_COM_C 0x14 /* misc common regs */
-#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */
-#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */
-#define OV7610_REG_COM_I 0x29 /* misc settings */
-
-/* OV7660 and OV7670 registers */
-#define OV7670_R00_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */
-#define OV7670_R01_BLUE 0x01 /* blue gain */
-#define OV7670_R02_RED 0x02 /* red gain */
-#define OV7670_R03_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */
-#define OV7670_R04_COM1 0x04 /* Control 1 */
-/*#define OV7670_R07_AECHH 0x07 * AEC MS 5 bits */
-#define OV7670_R0C_COM3 0x0c /* Control 3 */
-#define OV7670_R0D_COM4 0x0d /* Control 4 */
-#define OV7670_R0E_COM5 0x0e /* All "reserved" */
-#define OV7670_R0F_COM6 0x0f /* Control 6 */
-#define OV7670_R10_AECH 0x10 /* More bits of AEC value */
-#define OV7670_R11_CLKRC 0x11 /* Clock control */
-#define OV7670_R12_COM7 0x12 /* Control 7 */
-#define OV7670_COM7_FMT_VGA 0x00
-/*#define OV7670_COM7_YUV 0x00 * YUV */
-#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */
-#define OV7670_COM7_FMT_MASK 0x38
-#define OV7670_COM7_RESET 0x80 /* Register reset */
-#define OV7670_R13_COM8 0x13 /* Control 8 */
-#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */
-#define OV7670_COM8_AWB 0x02 /* White balance enable */
-#define OV7670_COM8_AGC 0x04 /* Auto gain enable */
-#define OV7670_COM8_BFILT 0x20 /* Band filter enable */
-#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */
-#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */
-#define OV7670_R14_COM9 0x14 /* Control 9 - gain ceiling */
-#define OV7670_R15_COM10 0x15 /* Control 10 */
-#define OV7670_R17_HSTART 0x17 /* Horiz start high bits */
-#define OV7670_R18_HSTOP 0x18 /* Horiz stop high bits */
-#define OV7670_R19_VSTART 0x19 /* Vert start high bits */
-#define OV7670_R1A_VSTOP 0x1a /* Vert stop high bits */
-#define OV7670_R1E_MVFP 0x1e /* Mirror / vflip */
-#define OV7670_MVFP_VFLIP 0x10 /* vertical flip */
-#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */
-#define OV7670_R24_AEW 0x24 /* AGC upper limit */
-#define OV7670_R25_AEB 0x25 /* AGC lower limit */
-#define OV7670_R26_VPT 0x26 /* AGC/AEC fast mode op region */
-#define OV7670_R32_HREF 0x32 /* HREF pieces */
-#define OV7670_R3A_TSLB 0x3a /* lots of stuff */
-#define OV7670_R3B_COM11 0x3b /* Control 11 */
-#define OV7670_COM11_EXP 0x02
-#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
-#define OV7670_R3C_COM12 0x3c /* Control 12 */
-#define OV7670_R3D_COM13 0x3d /* Control 13 */
-#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */
-#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */
-#define OV7670_R3E_COM14 0x3e /* Control 14 */
-#define OV7670_R3F_EDGE 0x3f /* Edge enhancement factor */
-#define OV7670_R40_COM15 0x40 /* Control 15 */
-/*#define OV7670_COM15_R00FF 0xc0 * 00 to FF */
-#define OV7670_R41_COM16 0x41 /* Control 16 */
-#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */
-/* end of ov7660 common registers */
-#define OV7670_R55_BRIGHT 0x55 /* Brightness */
-#define OV7670_R56_CONTRAS 0x56 /* Contrast control */
-#define OV7670_R69_GFIX 0x69 /* Fix gain control */
-/*#define OV7670_R8C_RGB444 0x8c * RGB 444 control */
-#define OV7670_R9F_HAECC1 0x9f /* Hist AEC/AGC control 1 */
-#define OV7670_RA0_HAECC2 0xa0 /* Hist AEC/AGC control 2 */
-#define OV7670_RA5_BD50MAX 0xa5 /* 50hz banding step limit */
-#define OV7670_RA6_HAECC3 0xa6 /* Hist AEC/AGC control 3 */
-#define OV7670_RA7_HAECC4 0xa7 /* Hist AEC/AGC control 4 */
-#define OV7670_RA8_HAECC5 0xa8 /* Hist AEC/AGC control 5 */
-#define OV7670_RA9_HAECC6 0xa9 /* Hist AEC/AGC control 6 */
-#define OV7670_RAA_HAECC7 0xaa /* Hist AEC/AGC control 7 */
-#define OV7670_RAB_BD60MAX 0xab /* 60hz banding step limit */
-
-struct ov_regvals {
- u8 reg;
- u8 val;
-};
-struct ov_i2c_regvals {
- u8 reg;
- u8 val;
-};
-
-/* Settings for OV2610 camera chip */
-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
- * (0x12) to change the sensor mode, registers related to the
- * sensor’s cropping window will be reset back to their default
- * values."
- *
- * "wait 4096 external clock ... to make sure the sensor is
- * stable and ready to access registers" i.e. 160us at 24MHz
- */
- { 0x12, 0x80 }, /* COMH reset */
- { 0x12, 0x00 }, /* QXGA, master */
-
- /*
- * 11 CLKRC "Clock Rate Control"
- * [7] internal frequency doublers: on
- * [6] video port mode: master
- * [5:0] clock divider: 1
- */
- { 0x11, 0x80 },
-
- /*
- * 13 COMI "Common Control I"
- * = 192 (0xC0) 11000000
- * COMI[7] "AEC speed selection"
- * = 1 (0x01) 1....... "Faster AEC correction"
- * COMI[6] "AEC speed step selection"
- * = 1 (0x01) .1...... "Big steps, fast"
- * COMI[5] "Banding filter on off"
- * = 0 (0x00) ..0..... "Off"
- * COMI[4] "Banding filter option"
- * = 0 (0x00) ...0.... "Main clock is 48 MHz and
- * the PLL is ON"
- * COMI[3] "Reserved"
- * = 0 (0x00) ....0...
- * COMI[2] "AGC auto manual control selection"
- * = 0 (0x00) .....0.. "Manual"
- * COMI[1] "AWB auto manual control selection"
- * = 0 (0x00) ......0. "Manual"
- * COMI[0] "Exposure control"
- * = 0 (0x00) .......0 "Manual"
- */
- { 0x13, 0xc0 },
-
- /*
- * 09 COMC "Common Control C"
- * = 8 (0x08) 00001000
- * COMC[7:5] "Reserved"
- * = 0 (0x00) 000.....
- * COMC[4] "Sleep Mode Enable"
- * = 0 (0x00) ...0.... "Normal mode"
- * COMC[3:2] "Sensor sampling reset timing selection"
- * = 2 (0x02) ....10.. "Longer reset time"
- * COMC[1:0] "Output drive current select"
- * = 0 (0x00) ......00 "Weakest"
- */
- { 0x09, 0x08 },
-
- /*
- * 0C COMD "Common Control D"
- * = 8 (0x08) 00001000
- * COMD[7] "Reserved"
- * = 0 (0x00) 0.......
- * COMD[6] "Swap MSB and LSB at the output port"
- * = 0 (0x00) .0...... "False"
- * COMD[5:3] "Reserved"
- * = 1 (0x01) ..001...
- * COMD[2] "Output Average On Off"
- * = 0 (0x00) .....0.. "Output Normal"
- * COMD[1] "Sensor precharge voltage selection"
- * = 0 (0x00) ......0. "Selects internal
- * reference precharge
- * voltage"
- * COMD[0] "Snapshot option"
- * = 0 (0x00) .......0 "Enable live video output
- * after snapshot sequence"
- */
- { 0x0c, 0x08 },
-
- /*
- * 0D COME "Common Control E"
- * = 161 (0xA1) 10100001
- * COME[7] "Output average option"
- * = 1 (0x01) 1....... "Output average of 4 pixels"
- * COME[6] "Anti-blooming control"
- * = 0 (0x00) .0...... "Off"
- * COME[5:3] "Reserved"
- * = 4 (0x04) ..100...
- * COME[2] "Clock output power down pin status"
- * = 0 (0x00) .....0.. "Tri-state data output pin
- * on power down"
- * COME[1] "Data output pin status selection at power down"
- * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK,
- * HREF, and CHSYNC pins on
- * power down"
- * COME[0] "Auto zero circuit select"
- * = 1 (0x01) .......1 "On"
- */
- { 0x0d, 0xa1 },
-
- /*
- * 0E COMF "Common Control F"
- * = 112 (0x70) 01110000
- * COMF[7] "System clock selection"
- * = 0 (0x00) 0....... "Use 24 MHz system clock"
- * COMF[6:4] "Reserved"
- * = 7 (0x07) .111....
- * COMF[3] "Manual auto negative offset canceling selection"
- * = 0 (0x00) ....0... "Auto detect negative
- * offset and cancel it"
- * COMF[2:0] "Reserved"
- * = 0 (0x00) .....000
- */
- { 0x0e, 0x70 },
-
- /*
- * 0F COMG "Common Control G"
- * = 66 (0x42) 01000010
- * COMG[7] "Optical black output selection"
- * = 0 (0x00) 0....... "Disable"
- * COMG[6] "Black level calibrate selection"
- * = 1 (0x01) .1...... "Use optical black pixels
- * to calibrate"
- * COMG[5:4] "Reserved"
- * = 0 (0x00) ..00....
- * COMG[3] "Channel offset adjustment"
- * = 0 (0x00) ....0... "Disable offset adjustment"
- * COMG[2] "ADC black level calibration option"
- * = 0 (0x00) .....0.. "Use B/G line and G/R
- * line to calibrate each
- * channel's black level"
- * COMG[1] "Reserved"
- * = 1 (0x01) ......1.
- * COMG[0] "ADC black level calibration enable"
- * = 0 (0x00) .......0 "Disable"
- */
- { 0x0f, 0x42 },
-
- /*
- * 14 COMJ "Common Control J"
- * = 198 (0xC6) 11000110
- * COMJ[7:6] "AGC gain ceiling"
- * = 3 (0x03) 11...... "8x"
- * COMJ[5:4] "Reserved"
- * = 0 (0x00) ..00....
- * COMJ[3] "Auto banding filter"
- * = 0 (0x00) ....0... "Banding filter is always
- * on off depending on
- * COMI[5] setting"
- * COMJ[2] "VSYNC drop option"
- * = 1 (0x01) .....1.. "SYNC is dropped if frame
- * data is dropped"
- * COMJ[1] "Frame data drop"
- * = 1 (0x01) ......1. "Drop frame data if
- * exposure is not within
- * tolerance. In AEC mode,
- * data is normally dropped
- * when data is out of
- * range."
- * COMJ[0] "Reserved"
- * = 0 (0x00) .......0
- */
- { 0x14, 0xc6 },
-
- /*
- * 15 COMK "Common Control K"
- * = 2 (0x02) 00000010
- * COMK[7] "CHSYNC pin output swap"
- * = 0 (0x00) 0....... "CHSYNC"
- * COMK[6] "HREF pin output swap"
- * = 0 (0x00) .0...... "HREF"
- * COMK[5] "PCLK output selection"
- * = 0 (0x00) ..0..... "PCLK always output"
- * COMK[4] "PCLK edge selection"
- * = 0 (0x00) ...0.... "Data valid on falling edge"
- * COMK[3] "HREF output polarity"
- * = 0 (0x00) ....0... "positive"
- * COMK[2] "Reserved"
- * = 0 (0x00) .....0..
- * COMK[1] "VSYNC polarity"
- * = 1 (0x01) ......1. "negative"
- * COMK[0] "HSYNC polarity"
- * = 0 (0x00) .......0 "positive"
- */
- { 0x15, 0x02 },
-
- /*
- * 33 CHLF "Current Control"
- * = 9 (0x09) 00001001
- * CHLF[7:6] "Sensor current control"
- * = 0 (0x00) 00......
- * CHLF[5] "Sensor current range control"
- * = 0 (0x00) ..0..... "normal range"
- * CHLF[4] "Sensor current"
- * = 0 (0x00) ...0.... "normal current"
- * CHLF[3] "Sensor buffer current control"
- * = 1 (0x01) ....1... "half current"
- * CHLF[2] "Column buffer current control"
- * = 0 (0x00) .....0.. "normal current"
- * CHLF[1] "Analog DSP current control"
- * = 0 (0x00) ......0. "normal current"
- * CHLF[1] "ADC current control"
- * = 0 (0x00) ......0. "normal current"
- */
- { 0x33, 0x09 },
-
- /*
- * 34 VBLM "Blooming Control"
- * = 80 (0x50) 01010000
- * VBLM[7] "Hard soft reset switch"
- * = 0 (0x00) 0....... "Hard reset"
- * VBLM[6:4] "Blooming voltage selection"
- * = 5 (0x05) .101....
- * VBLM[3:0] "Sensor current control"
- * = 0 (0x00) ....0000
- */
- { 0x34, 0x50 },
-
- /*
- * 36 VCHG "Sensor Precharge Voltage Control"
- * = 0 (0x00) 00000000
- * VCHG[7] "Reserved"
- * = 0 (0x00) 0.......
- * VCHG[6:4] "Sensor precharge voltage control"
- * = 0 (0x00) .000....
- * VCHG[3:0] "Sensor array common reference"
- * = 0 (0x00) ....0000
- */
- { 0x36, 0x00 },
-
- /*
- * 37 ADC "ADC Reference Control"
- * = 4 (0x04) 00000100
- * ADC[7:4] "Reserved"
- * = 0 (0x00) 0000....
- * ADC[3] "ADC input signal range"
- * = 0 (0x00) ....0... "Input signal 1.0x"
- * ADC[2:0] "ADC range control"
- * = 4 (0x04) .....100
- */
- { 0x37, 0x04 },
-
- /*
- * 38 ACOM "Analog Common Ground"
- * = 82 (0x52) 01010010
- * ACOM[7] "Analog gain control"
- * = 0 (0x00) 0....... "Gain 1x"
- * ACOM[6] "Analog black level calibration"
- * = 1 (0x01) .1...... "On"
- * ACOM[5:0] "Reserved"
- * = 18 (0x12) ..010010
- */
- { 0x38, 0x52 },
-
- /*
- * 3A FREFA "Internal Reference Adjustment"
- * = 0 (0x00) 00000000
- * FREFA[7:0] "Range"
- * = 0 (0x00) 00000000
- */
- { 0x3a, 0x00 },
-
- /*
- * 3C FVOPT "Internal Reference Adjustment"
- * = 31 (0x1F) 00011111
- * FVOPT[7:0] "Range"
- * = 31 (0x1F) 00011111
- */
- { 0x3c, 0x1f },
-
- /*
- * 44 Undocumented = 0 (0x00) 00000000
- * 44[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x44, 0x00 },
-
- /*
- * 40 Undocumented = 0 (0x00) 00000000
- * 40[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x40, 0x00 },
-
- /*
- * 41 Undocumented = 0 (0x00) 00000000
- * 41[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x41, 0x00 },
-
- /*
- * 42 Undocumented = 0 (0x00) 00000000
- * 42[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x42, 0x00 },
-
- /*
- * 43 Undocumented = 0 (0x00) 00000000
- * 43[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x43, 0x00 },
-
- /*
- * 45 Undocumented = 128 (0x80) 10000000
- * 45[7:0] "It's a secret"
- * = 128 (0x80) 10000000
- */
- { 0x45, 0x80 },
-
- /*
- * 48 Undocumented = 192 (0xC0) 11000000
- * 48[7:0] "It's a secret"
- * = 192 (0xC0) 11000000
- */
- { 0x48, 0xc0 },
-
- /*
- * 49 Undocumented = 25 (0x19) 00011001
- * 49[7:0] "It's a secret"
- * = 25 (0x19) 00011001
- */
- { 0x49, 0x19 },
-
- /*
- * 4B Undocumented = 128 (0x80) 10000000
- * 4B[7:0] "It's a secret"
- * = 128 (0x80) 10000000
- */
- { 0x4b, 0x80 },
-
- /*
- * 4D Undocumented = 196 (0xC4) 11000100
- * 4D[7:0] "It's a secret"
- * = 196 (0xC4) 11000100
- */
- { 0x4d, 0xc4 },
-
- /*
- * 35 VREF "Reference Voltage Control"
- * = 76 (0x4c) 01001100
- * VREF[7:5] "Column high reference control"
- * = 2 (0x02) 010..... "higher voltage"
- * VREF[4:2] "Column low reference control"
- * = 3 (0x03) ...011.. "Highest voltage"
- * VREF[1:0] "Reserved"
- * = 0 (0x00) ......00
- */
- { 0x35, 0x4c },
-
- /*
- * 3D Undocumented = 0 (0x00) 00000000
- * 3D[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x3d, 0x00 },
-
- /*
- * 3E Undocumented = 0 (0x00) 00000000
- * 3E[7:0] "It's a secret"
- * = 0 (0x00) 00000000
- */
- { 0x3e, 0x00 },
-
- /*
- * 3B FREFB "Internal Reference Adjustment"
- * = 24 (0x18) 00011000
- * FREFB[7:0] "Range"
- * = 24 (0x18) 00011000
- */
- { 0x3b, 0x18 },
-
- /*
- * 33 CHLF "Current Control"
- * = 25 (0x19) 00011001
- * CHLF[7:6] "Sensor current control"
- * = 0 (0x00) 00......
- * CHLF[5] "Sensor current range control"
- * = 0 (0x00) ..0..... "normal range"
- * CHLF[4] "Sensor current"
- * = 1 (0x01) ...1.... "double current"
- * CHLF[3] "Sensor buffer current control"
- * = 1 (0x01) ....1... "half current"
- * CHLF[2] "Column buffer current control"
- * = 0 (0x00) .....0.. "normal current"
- * CHLF[1] "Analog DSP current control"
- * = 0 (0x00) ......0. "normal current"
- * CHLF[1] "ADC current control"
- * = 0 (0x00) ......0. "normal current"
- */
- { 0x33, 0x19 },
-
- /*
- * 34 VBLM "Blooming Control"
- * = 90 (0x5A) 01011010
- * VBLM[7] "Hard soft reset switch"
- * = 0 (0x00) 0....... "Hard reset"
- * VBLM[6:4] "Blooming voltage selection"
- * = 5 (0x05) .101....
- * VBLM[3:0] "Sensor current control"
- * = 10 (0x0A) ....1010
- */
- { 0x34, 0x5a },
-
- /*
- * 3B FREFB "Internal Reference Adjustment"
- * = 0 (0x00) 00000000
- * FREFB[7:0] "Range"
- * = 0 (0x00) 00000000
- */
- { 0x3b, 0x00 },
-
- /*
- * 33 CHLF "Current Control"
- * = 9 (0x09) 00001001
- * CHLF[7:6] "Sensor current control"
- * = 0 (0x00) 00......
- * CHLF[5] "Sensor current range control"
- * = 0 (0x00) ..0..... "normal range"
- * CHLF[4] "Sensor current"
- * = 0 (0x00) ...0.... "normal current"
- * CHLF[3] "Sensor buffer current control"
- * = 1 (0x01) ....1... "half current"
- * CHLF[2] "Column buffer current control"
- * = 0 (0x00) .....0.. "normal current"
- * CHLF[1] "Analog DSP current control"
- * = 0 (0x00) ......0. "normal current"
- * CHLF[1] "ADC current control"
- * = 0 (0x00) ......0. "normal current"
- */
- { 0x33, 0x09 },
-
- /*
- * 34 VBLM "Blooming Control"
- * = 80 (0x50) 01010000
- * VBLM[7] "Hard soft reset switch"
- * = 0 (0x00) 0....... "Hard reset"
- * VBLM[6:4] "Blooming voltage selection"
- * = 5 (0x05) .101....
- * VBLM[3:0] "Sensor current control"
- * = 0 (0x00) ....0000
- */
- { 0x34, 0x50 },
-
- /*
- * 12 COMH "Common Control H"
- * = 64 (0x40) 01000000
- * COMH[7] "SRST"
- * = 0 (0x00) 0....... "No-op"
- * COMH[6:4] "Resolution selection"
- * = 4 (0x04) .100.... "XGA"
- * COMH[3] "Master slave selection"
- * = 0 (0x00) ....0... "Master mode"
- * COMH[2] "Internal B/R channel option"
- * = 0 (0x00) .....0.. "B/R use same channel"
- * COMH[1] "Color bar test pattern"
- * = 0 (0x00) ......0. "Off"
- * COMH[0] "Reserved"
- * = 0 (0x00) .......0
- */
- { 0x12, 0x40 },
-
- /*
- * 17 HREFST "Horizontal window start"
- * = 31 (0x1F) 00011111
- * HREFST[7:0] "Horizontal window start, 8 MSBs"
- * = 31 (0x1F) 00011111
- */
- { 0x17, 0x1f },
-
- /*
- * 18 HREFEND "Horizontal window end"
- * = 95 (0x5F) 01011111
- * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
- * = 95 (0x5F) 01011111
- */
- { 0x18, 0x5f },
-
- /*
- * 19 VSTRT "Vertical window start"
- * = 0 (0x00) 00000000
- * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
- * = 0 (0x00) 00000000
- */
- { 0x19, 0x00 },
-
- /*
- * 1A VEND "Vertical window end"
- * = 96 (0x60) 01100000
- * VEND[7:0] "Vertical Window End, 8 MSBs"
- * = 96 (0x60) 01100000
- */
- { 0x1a, 0x60 },
-
- /*
- * 32 COMM "Common Control M"
- * = 18 (0x12) 00010010
- * COMM[7:6] "Pixel clock divide option"
- * = 0 (0x00) 00...... "/1"
- * COMM[5:3] "Horizontal window end position, 3 LSBs"
- * = 2 (0x02) ..010...
- * COMM[2:0] "Horizontal window start position, 3 LSBs"
- * = 2 (0x02) .....010
- */
- { 0x32, 0x12 },
-
- /*
- * 03 COMA "Common Control A"
- * = 74 (0x4A) 01001010
- * COMA[7:4] "AWB Update Threshold"
- * = 4 (0x04) 0100....
- * COMA[3:2] "Vertical window end line control 2 LSBs"
- * = 2 (0x02) ....10..
- * COMA[1:0] "Vertical window start line control 2 LSBs"
- * = 2 (0x02) ......10
- */
- { 0x03, 0x4a },
-
- /*
- * 11 CLKRC "Clock Rate Control"
- * = 128 (0x80) 10000000
- * CLKRC[7] "Internal frequency doublers on off seclection"
- * = 1 (0x01) 1....... "On"
- * CLKRC[6] "Digital video master slave selection"
- * = 0 (0x00) .0...... "Master mode, sensor
- * provides PCLK"
- * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
- * = 0 (0x00) ..000000
- */
- { 0x11, 0x80 },
-
- /*
- * 12 COMH "Common Control H"
- * = 0 (0x00) 00000000
- * COMH[7] "SRST"
- * = 0 (0x00) 0....... "No-op"
- * COMH[6:4] "Resolution selection"
- * = 0 (0x00) .000.... "QXGA"
- * COMH[3] "Master slave selection"
- * = 0 (0x00) ....0... "Master mode"
- * COMH[2] "Internal B/R channel option"
- * = 0 (0x00) .....0.. "B/R use same channel"
- * COMH[1] "Color bar test pattern"
- * = 0 (0x00) ......0. "Off"
- * COMH[0] "Reserved"
- * = 0 (0x00) .......0
- */
- { 0x12, 0x00 },
-
- /*
- * 12 COMH "Common Control H"
- * = 64 (0x40) 01000000
- * COMH[7] "SRST"
- * = 0 (0x00) 0....... "No-op"
- * COMH[6:4] "Resolution selection"
- * = 4 (0x04) .100.... "XGA"
- * COMH[3] "Master slave selection"
- * = 0 (0x00) ....0... "Master mode"
- * COMH[2] "Internal B/R channel option"
- * = 0 (0x00) .....0.. "B/R use same channel"
- * COMH[1] "Color bar test pattern"
- * = 0 (0x00) ......0. "Off"
- * COMH[0] "Reserved"
- * = 0 (0x00) .......0
- */
- { 0x12, 0x40 },
-
- /*
- * 17 HREFST "Horizontal window start"
- * = 31 (0x1F) 00011111
- * HREFST[7:0] "Horizontal window start, 8 MSBs"
- * = 31 (0x1F) 00011111
- */
- { 0x17, 0x1f },
-
- /*
- * 18 HREFEND "Horizontal window end"
- * = 95 (0x5F) 01011111
- * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
- * = 95 (0x5F) 01011111
- */
- { 0x18, 0x5f },
-
- /*
- * 19 VSTRT "Vertical window start"
- * = 0 (0x00) 00000000
- * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
- * = 0 (0x00) 00000000
- */
- { 0x19, 0x00 },
-
- /*
- * 1A VEND "Vertical window end"
- * = 96 (0x60) 01100000
- * VEND[7:0] "Vertical Window End, 8 MSBs"
- * = 96 (0x60) 01100000
- */
- { 0x1a, 0x60 },
-
- /*
- * 32 COMM "Common Control M"
- * = 18 (0x12) 00010010
- * COMM[7:6] "Pixel clock divide option"
- * = 0 (0x00) 00...... "/1"
- * COMM[5:3] "Horizontal window end position, 3 LSBs"
- * = 2 (0x02) ..010...
- * COMM[2:0] "Horizontal window start position, 3 LSBs"
- * = 2 (0x02) .....010
- */
- { 0x32, 0x12 },
-
- /*
- * 03 COMA "Common Control A"
- * = 74 (0x4A) 01001010
- * COMA[7:4] "AWB Update Threshold"
- * = 4 (0x04) 0100....
- * COMA[3:2] "Vertical window end line control 2 LSBs"
- * = 2 (0x02) ....10..
- * COMA[1:0] "Vertical window start line control 2 LSBs"
- * = 2 (0x02) ......10
- */
- { 0x03, 0x4a },
-
- /*
- * 02 RED "Red Gain Control"
- * = 175 (0xAF) 10101111
- * RED[7] "Action"
- * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
- * RED[6:0] "Value"
- * = 47 (0x2F) .0101111
- */
- { 0x02, 0xaf },
-
- /*
- * 2D ADDVSL "VSYNC Pulse Width"
- * = 210 (0xD2) 11010010
- * ADDVSL[7:0] "VSYNC pulse width, LSB"
- * = 210 (0xD2) 11010010
- */
- { 0x2d, 0xd2 },
-
- /*
- * 00 GAIN = 24 (0x18) 00011000
- * GAIN[7:6] "Reserved"
- * = 0 (0x00) 00......
- * GAIN[5] "Double"
- * = 0 (0x00) ..0..... "False"
- * GAIN[4] "Double"
- * = 1 (0x01) ...1.... "True"
- * GAIN[3:0] "Range"
- * = 8 (0x08) ....1000
- */
- { 0x00, 0x18 },
-
- /*
- * 01 BLUE "Blue Gain Control"
- * = 240 (0xF0) 11110000
- * BLUE[7] "Action"
- * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
- * BLUE[6:0] "Value"
- * = 112 (0x70) .1110000
- */
- { 0x01, 0xf0 },
-
- /*
- * 10 AEC "Automatic Exposure Control"
- * = 10 (0x0A) 00001010
- * AEC[7:0] "Automatic Exposure Control, 8 MSBs"
- * = 10 (0x0A) 00001010
- */
- { 0x10, 0x0a },
-
- { 0xe1, 0x67 },
- { 0xe3, 0x03 },
- { 0xe4, 0x26 },
- { 0xe5, 0x3e },
- { 0xf8, 0x01 },
- { 0xff, 0x01 },
-};
-
-static const struct ov_i2c_regvals norm_6x20[] = {
- { 0x12, 0x80 }, /* reset */
- { 0x11, 0x01 },
- { 0x03, 0x60 },
- { 0x05, 0x7f }, /* For when autoadjust is off */
- { 0x07, 0xa8 },
- /* The ratio of 0x0c and 0x0d controls the white point */
- { 0x0c, 0x24 },
- { 0x0d, 0x24 },
- { 0x0f, 0x15 }, /* COMS */
- { 0x10, 0x75 }, /* AEC Exposure time */
- { 0x12, 0x24 }, /* Enable AGC */
- { 0x14, 0x04 },
- /* 0x16: 0x06 helps frame stability with moving objects */
- { 0x16, 0x06 },
-/* { 0x20, 0x30 }, * Aperture correction enable */
- { 0x26, 0xb2 }, /* BLC enable */
- /* 0x28: 0x05 Selects RGB format if RGB on */
- { 0x28, 0x05 },
- { 0x2a, 0x04 }, /* Disable framerate adjust */
-/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */
- { 0x2d, 0x85 },
- { 0x33, 0xa0 }, /* Color Processing Parameter */
- { 0x34, 0xd2 }, /* Max A/D range */
- { 0x38, 0x8b },
- { 0x39, 0x40 },
-
- { 0x3c, 0x39 }, /* Enable AEC mode changing */
- { 0x3c, 0x3c }, /* Change AEC mode */
- { 0x3c, 0x24 }, /* Disable AEC mode changing */
-
- { 0x3d, 0x80 },
- /* These next two registers (0x4a, 0x4b) are undocumented.
- * They control the color balance */
- { 0x4a, 0x80 },
- { 0x4b, 0x80 },
- { 0x4d, 0xd2 }, /* This reduces noise a bit */
- { 0x4e, 0xc1 },
- { 0x4f, 0x04 },
-/* Do 50-53 have any effect? */
-/* Toggle 0x12[2] off and on here? */
-};
-
-static const struct ov_i2c_regvals norm_6x30[] = {
- { 0x12, 0x80 }, /* Reset */
- { 0x00, 0x1f }, /* Gain */
- { 0x01, 0x99 }, /* Blue gain */
- { 0x02, 0x7c }, /* Red gain */
- { 0x03, 0xc0 }, /* Saturation */
- { 0x05, 0x0a }, /* Contrast */
- { 0x06, 0x95 }, /* Brightness */
- { 0x07, 0x2d }, /* Sharpness */
- { 0x0c, 0x20 },
- { 0x0d, 0x20 },
- { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */
- { 0x0f, 0x05 },
- { 0x10, 0x9a },
- { 0x11, 0x00 }, /* Pixel clock = fastest */
- { 0x12, 0x24 }, /* Enable AGC and AWB */
- { 0x13, 0x21 },
- { 0x14, 0x80 },
- { 0x15, 0x01 },
- { 0x16, 0x03 },
- { 0x17, 0x38 },
- { 0x18, 0xea },
- { 0x19, 0x04 },
- { 0x1a, 0x93 },
- { 0x1b, 0x00 },
- { 0x1e, 0xc4 },
- { 0x1f, 0x04 },
- { 0x20, 0x20 },
- { 0x21, 0x10 },
- { 0x22, 0x88 },
- { 0x23, 0xc0 }, /* Crystal circuit power level */
- { 0x25, 0x9a }, /* Increase AEC black ratio */
- { 0x26, 0xb2 }, /* BLC enable */
- { 0x27, 0xa2 },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x84 }, /* 60 Hz power */
- { 0x2b, 0xa8 }, /* 60 Hz power */
- { 0x2c, 0xa0 },
- { 0x2d, 0x95 }, /* Enable auto-brightness */
- { 0x2e, 0x88 },
- { 0x33, 0x26 },
- { 0x34, 0x03 },
- { 0x36, 0x8f },
- { 0x37, 0x80 },
- { 0x38, 0x83 },
- { 0x39, 0x80 },
- { 0x3a, 0x0f },
- { 0x3b, 0x3c },
- { 0x3c, 0x1a },
- { 0x3d, 0x80 },
- { 0x3e, 0x80 },
- { 0x3f, 0x0e },
- { 0x40, 0x00 }, /* White bal */
- { 0x41, 0x00 }, /* White bal */
- { 0x42, 0x80 },
- { 0x43, 0x3f }, /* White bal */
- { 0x44, 0x80 },
- { 0x45, 0x20 },
- { 0x46, 0x20 },
- { 0x47, 0x80 },
- { 0x48, 0x7f },
- { 0x49, 0x00 },
- { 0x4a, 0x00 },
- { 0x4b, 0x80 },
- { 0x4c, 0xd0 },
- { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
- { 0x4e, 0x40 },
- { 0x4f, 0x07 }, /* UV avg., col. killer: max */
- { 0x50, 0xff },
- { 0x54, 0x23 }, /* Max AGC gain: 18dB */
- { 0x55, 0xff },
- { 0x56, 0x12 },
- { 0x57, 0x81 },
- { 0x58, 0x75 },
- { 0x59, 0x01 }, /* AGC dark current comp.: +1 */
- { 0x5a, 0x2c },
- { 0x5b, 0x0f }, /* AWB chrominance levels */
- { 0x5c, 0x10 },
- { 0x3d, 0x80 },
- { 0x27, 0xa6 },
- { 0x12, 0x20 }, /* Toggle AWB */
- { 0x12, 0x24 },
-};
-
-/* Lawrence Glaister <lg@jfm.bc.ca> reports:
- *
- * Register 0x0f in the 7610 has the following effects:
- *
- * 0x85 (AEC method 1): Best overall, good contrast range
- * 0x45 (AEC method 2): Very overexposed
- * 0xa5 (spec sheet default): Ok, but the black level is
- * shifted resulting in loss of contrast
- * 0x05 (old driver setting): very overexposed, too much
- * contrast
- */
-static const struct ov_i2c_regvals norm_7610[] = {
- { 0x10, 0xff },
- { 0x16, 0x06 },
- { 0x28, 0x24 },
- { 0x2b, 0xac },
- { 0x12, 0x00 },
- { 0x38, 0x81 },
- { 0x28, 0x24 }, /* 0c */
- { 0x0f, 0x85 }, /* lg's setting */
- { 0x15, 0x01 },
- { 0x20, 0x1c },
- { 0x23, 0x2a },
- { 0x24, 0x10 },
- { 0x25, 0x8a },
- { 0x26, 0xa2 },
- { 0x27, 0xc2 },
- { 0x2a, 0x04 },
- { 0x2c, 0xfe },
- { 0x2d, 0x93 },
- { 0x30, 0x71 },
- { 0x31, 0x60 },
- { 0x32, 0x26 },
- { 0x33, 0x20 },
- { 0x34, 0x48 },
- { 0x12, 0x24 },
- { 0x11, 0x01 },
- { 0x0c, 0x24 },
- { 0x0d, 0x24 },
-};
-
-static const struct ov_i2c_regvals norm_7620[] = {
- { 0x12, 0x80 }, /* reset */
- { 0x00, 0x00 }, /* gain */
- { 0x01, 0x80 }, /* blue gain */
- { 0x02, 0x80 }, /* red gain */
- { 0x03, 0xc0 }, /* OV7670_R03_VREF */
- { 0x06, 0x60 },
- { 0x07, 0x00 },
- { 0x0c, 0x24 },
- { 0x0c, 0x24 },
- { 0x0d, 0x24 },
- { 0x11, 0x01 },
- { 0x12, 0x24 },
- { 0x13, 0x01 },
- { 0x14, 0x84 },
- { 0x15, 0x01 },
- { 0x16, 0x03 },
- { 0x17, 0x2f },
- { 0x18, 0xcf },
- { 0x19, 0x06 },
- { 0x1a, 0xf5 },
- { 0x1b, 0x00 },
- { 0x20, 0x18 },
- { 0x21, 0x80 },
- { 0x22, 0x80 },
- { 0x23, 0x00 },
- { 0x26, 0xa2 },
- { 0x27, 0xea },
- { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */
- { 0x29, 0x00 },
- { 0x2a, 0x10 },
- { 0x2b, 0x00 },
- { 0x2c, 0x88 },
- { 0x2d, 0x91 },
- { 0x2e, 0x80 },
- { 0x2f, 0x44 },
- { 0x60, 0x27 },
- { 0x61, 0x02 },
- { 0x62, 0x5f },
- { 0x63, 0xd5 },
- { 0x64, 0x57 },
- { 0x65, 0x83 },
- { 0x66, 0x55 },
- { 0x67, 0x92 },
- { 0x68, 0xcf },
- { 0x69, 0x76 },
- { 0x6a, 0x22 },
- { 0x6b, 0x00 },
- { 0x6c, 0x02 },
- { 0x6d, 0x44 },
- { 0x6e, 0x80 },
- { 0x6f, 0x1d },
- { 0x70, 0x8b },
- { 0x71, 0x00 },
- { 0x72, 0x14 },
- { 0x73, 0x54 },
- { 0x74, 0x00 },
- { 0x75, 0x8e },
- { 0x76, 0x00 },
- { 0x77, 0xff },
- { 0x78, 0x80 },
- { 0x79, 0x80 },
- { 0x7a, 0x80 },
- { 0x7b, 0xe2 },
- { 0x7c, 0x00 },
-};
-
-/* 7640 and 7648. The defaults should be OK for most registers. */
-static const struct ov_i2c_regvals norm_7640[] = {
- { 0x12, 0x80 },
- { 0x12, 0x14 },
-};
-
-static const struct ov_regvals init_519_ov7660[] = {
- { 0x5d, 0x03 }, /* Turn off suspend mode */
- { 0x53, 0x9b }, /* 0x9f enables the (unused) microcontroller */
- { 0x54, 0x0f }, /* bit2 (jpeg enable) */
- { 0xa2, 0x20 }, /* a2-a5 are undocumented */
- { 0xa3, 0x18 },
- { 0xa4, 0x04 },
- { 0xa5, 0x28 },
- { 0x37, 0x00 }, /* SetUsbInit */
- { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
- /* Enable both fields, YUV Input, disable defect comp (why?) */
- { 0x20, 0x0c }, /* 0x0d does U <-> V swap */
- { 0x21, 0x38 },
- { 0x22, 0x1d },
- { 0x17, 0x50 }, /* undocumented */
- { 0x37, 0x00 }, /* undocumented */
- { 0x40, 0xff }, /* I2C timeout counter */
- { 0x46, 0x00 }, /* I2C clock prescaler */
-};
-static const struct ov_i2c_regvals norm_7660[] = {
- {OV7670_R12_COM7, OV7670_COM7_RESET},
- {OV7670_R11_CLKRC, 0x81},
- {0x92, 0x00}, /* DM_LNL */
- {0x93, 0x00}, /* DM_LNH */
- {0x9d, 0x4c}, /* BD50ST */
- {0x9e, 0x3f}, /* BD60ST */
- {OV7670_R3B_COM11, 0x02},
- {OV7670_R13_COM8, 0xf5},
- {OV7670_R10_AECH, 0x00},
- {OV7670_R00_GAIN, 0x00},
- {OV7670_R01_BLUE, 0x7c},
- {OV7670_R02_RED, 0x9d},
- {OV7670_R12_COM7, 0x00},
- {OV7670_R04_COM1, 00},
- {OV7670_R18_HSTOP, 0x01},
- {OV7670_R17_HSTART, 0x13},
- {OV7670_R32_HREF, 0x92},
- {OV7670_R19_VSTART, 0x02},
- {OV7670_R1A_VSTOP, 0x7a},
- {OV7670_R03_VREF, 0x00},
- {OV7670_R0E_COM5, 0x04},
- {OV7670_R0F_COM6, 0x62},
- {OV7670_R15_COM10, 0x00},
- {0x16, 0x02}, /* RSVD */
- {0x1b, 0x00}, /* PSHFT */
- {OV7670_R1E_MVFP, 0x01},
- {0x29, 0x3c}, /* RSVD */
- {0x33, 0x00}, /* CHLF */
- {0x34, 0x07}, /* ARBLM */
- {0x35, 0x84}, /* RSVD */
- {0x36, 0x00}, /* RSVD */
- {0x37, 0x04}, /* ADC */
- {0x39, 0x43}, /* OFON */
- {OV7670_R3A_TSLB, 0x00},
- {OV7670_R3C_COM12, 0x6c},
- {OV7670_R3D_COM13, 0x98},
- {OV7670_R3F_EDGE, 0x23},
- {OV7670_R40_COM15, 0xc1},
- {OV7670_R41_COM16, 0x22},
- {0x6b, 0x0a}, /* DBLV */
- {0xa1, 0x08}, /* RSVD */
- {0x69, 0x80}, /* HV */
- {0x43, 0xf0}, /* RSVD.. */
- {0x44, 0x10},
- {0x45, 0x78},
- {0x46, 0xa8},
- {0x47, 0x60},
- {0x48, 0x80},
- {0x59, 0xba},
- {0x5a, 0x9a},
- {0x5b, 0x22},
- {0x5c, 0xb9},
- {0x5d, 0x9b},
- {0x5e, 0x10},
- {0x5f, 0xe0},
- {0x60, 0x85},
- {0x61, 0x60},
- {0x9f, 0x9d}, /* RSVD */
- {0xa0, 0xa0}, /* DSPC2 */
- {0x4f, 0x60}, /* matrix */
- {0x50, 0x64},
- {0x51, 0x04},
- {0x52, 0x18},
- {0x53, 0x3c},
- {0x54, 0x54},
- {0x55, 0x40},
- {0x56, 0x40},
- {0x57, 0x40},
- {0x58, 0x0d}, /* matrix sign */
- {0x8b, 0xcc}, /* RSVD */
- {0x8c, 0xcc},
- {0x8d, 0xcf},
- {0x6c, 0x40}, /* gamma curve */
- {0x6d, 0xe0},
- {0x6e, 0xa0},
- {0x6f, 0x80},
- {0x70, 0x70},
- {0x71, 0x80},
- {0x72, 0x60},
- {0x73, 0x60},
- {0x74, 0x50},
- {0x75, 0x40},
- {0x76, 0x38},
- {0x77, 0x3c},
- {0x78, 0x32},
- {0x79, 0x1a},
- {0x7a, 0x28},
- {0x7b, 0x24},
- {0x7c, 0x04}, /* gamma curve */
- {0x7d, 0x12},
- {0x7e, 0x26},
- {0x7f, 0x46},
- {0x80, 0x54},
- {0x81, 0x64},
- {0x82, 0x70},
- {0x83, 0x7c},
- {0x84, 0x86},
- {0x85, 0x8e},
- {0x86, 0x9c},
- {0x87, 0xab},
- {0x88, 0xc4},
- {0x89, 0xd1},
- {0x8a, 0xe5},
- {OV7670_R14_COM9, 0x1e},
- {OV7670_R24_AEW, 0x80},
- {OV7670_R25_AEB, 0x72},
- {OV7670_R26_VPT, 0xb3},
- {0x62, 0x80}, /* LCC1 */
- {0x63, 0x80}, /* LCC2 */
- {0x64, 0x06}, /* LCC3 */
- {0x65, 0x00}, /* LCC4 */
- {0x66, 0x01}, /* LCC5 */
- {0x94, 0x0e}, /* RSVD.. */
- {0x95, 0x14},
- {OV7670_R13_COM8, OV7670_COM8_FASTAEC
- | OV7670_COM8_AECSTEP
- | OV7670_COM8_BFILT
- | 0x10
- | OV7670_COM8_AGC
- | OV7670_COM8_AWB
- | OV7670_COM8_AEC},
- {0xa1, 0xc8}
-};
-static const struct ov_i2c_regvals norm_9600[] = {
- {0x12, 0x80},
- {0x0c, 0x28},
- {0x11, 0x80},
- {0x13, 0xb5},
- {0x14, 0x3e},
- {0x1b, 0x04},
- {0x24, 0xb0},
- {0x25, 0x90},
- {0x26, 0x94},
- {0x35, 0x90},
- {0x37, 0x07},
- {0x38, 0x08},
- {0x01, 0x8e},
- {0x02, 0x85}
-};
-
-/* 7670. Defaults taken from OmniVision provided data,
-* as provided by Jonathan Corbet of OLPC */
-static const struct ov_i2c_regvals norm_7670[] = {
- { OV7670_R12_COM7, OV7670_COM7_RESET },
- { OV7670_R3A_TSLB, 0x04 }, /* OV */
- { OV7670_R12_COM7, OV7670_COM7_FMT_VGA }, /* VGA */
- { OV7670_R11_CLKRC, 0x01 },
-/*
- * Set the hardware window. These values from OV don't entirely
- * make sense - hstop is less than hstart. But they work...
- */
- { OV7670_R17_HSTART, 0x13 },
- { OV7670_R18_HSTOP, 0x01 },
- { OV7670_R32_HREF, 0xb6 },
- { OV7670_R19_VSTART, 0x02 },
- { OV7670_R1A_VSTOP, 0x7a },
- { OV7670_R03_VREF, 0x0a },
-
- { OV7670_R0C_COM3, 0x00 },
- { OV7670_R3E_COM14, 0x00 },
-/* Mystery scaling numbers */
- { 0x70, 0x3a },
- { 0x71, 0x35 },
- { 0x72, 0x11 },
- { 0x73, 0xf0 },
- { 0xa2, 0x02 },
-/* { OV7670_R15_COM10, 0x0 }, */
-
-/* Gamma curve values */
- { 0x7a, 0x20 },
- { 0x7b, 0x10 },
- { 0x7c, 0x1e },
- { 0x7d, 0x35 },
- { 0x7e, 0x5a },
- { 0x7f, 0x69 },
- { 0x80, 0x76 },
- { 0x81, 0x80 },
- { 0x82, 0x88 },
- { 0x83, 0x8f },
- { 0x84, 0x96 },
- { 0x85, 0xa3 },
- { 0x86, 0xaf },
- { 0x87, 0xc4 },
- { 0x88, 0xd7 },
- { 0x89, 0xe8 },
-
-/* AGC and AEC parameters. Note we start by disabling those features,
- then turn them only after tweaking the values. */
- { OV7670_R13_COM8, OV7670_COM8_FASTAEC
- | OV7670_COM8_AECSTEP
- | OV7670_COM8_BFILT },
- { OV7670_R00_GAIN, 0x00 },
- { OV7670_R10_AECH, 0x00 },
- { OV7670_R0D_COM4, 0x40 }, /* magic reserved bit */
- { OV7670_R14_COM9, 0x18 }, /* 4x gain + magic rsvd bit */
- { OV7670_RA5_BD50MAX, 0x05 },
- { OV7670_RAB_BD60MAX, 0x07 },
- { OV7670_R24_AEW, 0x95 },
- { OV7670_R25_AEB, 0x33 },
- { OV7670_R26_VPT, 0xe3 },
- { OV7670_R9F_HAECC1, 0x78 },
- { OV7670_RA0_HAECC2, 0x68 },
- { 0xa1, 0x03 }, /* magic */
- { OV7670_RA6_HAECC3, 0xd8 },
- { OV7670_RA7_HAECC4, 0xd8 },
- { OV7670_RA8_HAECC5, 0xf0 },
- { OV7670_RA9_HAECC6, 0x90 },
- { OV7670_RAA_HAECC7, 0x94 },
- { OV7670_R13_COM8, OV7670_COM8_FASTAEC
- | OV7670_COM8_AECSTEP
- | OV7670_COM8_BFILT
- | OV7670_COM8_AGC
- | OV7670_COM8_AEC },
-
-/* Almost all of these are magic "reserved" values. */
- { OV7670_R0E_COM5, 0x61 },
- { OV7670_R0F_COM6, 0x4b },
- { 0x16, 0x02 },
- { OV7670_R1E_MVFP, 0x07 },
- { 0x21, 0x02 },
- { 0x22, 0x91 },
- { 0x29, 0x07 },
- { 0x33, 0x0b },
- { 0x35, 0x0b },
- { 0x37, 0x1d },
- { 0x38, 0x71 },
- { 0x39, 0x2a },
- { OV7670_R3C_COM12, 0x78 },
- { 0x4d, 0x40 },
- { 0x4e, 0x20 },
- { OV7670_R69_GFIX, 0x00 },
- { 0x6b, 0x4a },
- { 0x74, 0x10 },
- { 0x8d, 0x4f },
- { 0x8e, 0x00 },
- { 0x8f, 0x00 },
- { 0x90, 0x00 },
- { 0x91, 0x00 },
- { 0x96, 0x00 },
- { 0x9a, 0x00 },
- { 0xb0, 0x84 },
- { 0xb1, 0x0c },
- { 0xb2, 0x0e },
- { 0xb3, 0x82 },
- { 0xb8, 0x0a },
-
-/* More reserved magic, some of which tweaks white balance */
- { 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 }, /* "9e for advance AWB" */
- { 0x6a, 0x40 },
- { OV7670_R01_BLUE, 0x40 },
- { OV7670_R02_RED, 0x60 },
- { OV7670_R13_COM8, OV7670_COM8_FASTAEC
- | OV7670_COM8_AECSTEP
- | OV7670_COM8_BFILT
- | OV7670_COM8_AGC
- | OV7670_COM8_AEC
- | OV7670_COM8_AWB },
-
-/* Matrix coefficients */
- { 0x4f, 0x80 },
- { 0x50, 0x80 },
- { 0x51, 0x00 },
- { 0x52, 0x22 },
- { 0x53, 0x5e },
- { 0x54, 0x80 },
- { 0x58, 0x9e },
-
- { OV7670_R41_COM16, OV7670_COM16_AWBGAIN },
- { OV7670_R3F_EDGE, 0x00 },
- { 0x75, 0x05 },
- { 0x76, 0xe1 },
- { 0x4c, 0x00 },
- { 0x77, 0x01 },
- { OV7670_R3D_COM13, OV7670_COM13_GAMMA
- | OV7670_COM13_UVSAT
- | 2}, /* was 3 */
- { 0x4b, 0x09 },
- { 0xc9, 0x60 },
- { OV7670_R41_COM16, 0x38 },
- { 0x56, 0x40 },
-
- { 0x34, 0x11 },
- { OV7670_R3B_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO },
- { 0xa4, 0x88 },
- { 0x96, 0x00 },
- { 0x97, 0x30 },
- { 0x98, 0x20 },
- { 0x99, 0x30 },
- { 0x9a, 0x84 },
- { 0x9b, 0x29 },
- { 0x9c, 0x03 },
- { 0x9d, 0x4c },
- { 0x9e, 0x3f },
- { 0x78, 0x04 },
-
-/* Extra-weird stuff. Some sort of multiplexor register */
- { 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, 0x40 },
- { 0x79, 0x05 },
- { 0xc8, 0x30 },
- { 0x79, 0x26 },
-};
-
-static const struct ov_i2c_regvals norm_8610[] = {
- { 0x12, 0x80 },
- { 0x00, 0x00 },
- { 0x01, 0x80 },
- { 0x02, 0x80 },
- { 0x03, 0xc0 },
- { 0x04, 0x30 },
- { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */
- { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */
- { 0x0a, 0x86 },
- { 0x0b, 0xb0 },
- { 0x0c, 0x20 },
- { 0x0d, 0x20 },
- { 0x11, 0x01 },
- { 0x12, 0x25 },
- { 0x13, 0x01 },
- { 0x14, 0x04 },
- { 0x15, 0x01 }, /* Lin and Win think different about UV order */
- { 0x16, 0x03 },
- { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */
- { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */
- { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */
- { 0x1a, 0xf5 },
- { 0x1b, 0x00 },
- { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */
- { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */
- { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */
- { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */
- { 0x26, 0xa2 },
- { 0x27, 0xea },
- { 0x28, 0x00 },
- { 0x29, 0x00 },
- { 0x2a, 0x80 },
- { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */
- { 0x2c, 0xac },
- { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */
- { 0x2e, 0x80 },
- { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */
- { 0x4c, 0x00 },
- { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */
- { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */
- { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */
- { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */
- { 0x63, 0xff },
- { 0x64, 0x53 }, /* new windrv 090403 says 0x57,
- * maybe thats wrong */
- { 0x65, 0x00 },
- { 0x66, 0x55 },
- { 0x67, 0xb0 },
- { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */
- { 0x69, 0x02 },
- { 0x6a, 0x22 },
- { 0x6b, 0x00 },
- { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but
- * deleting bit7 colors the first images red */
- { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */
- { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */
- { 0x6f, 0x01 },
- { 0x70, 0x8b },
- { 0x71, 0x00 },
- { 0x72, 0x14 },
- { 0x73, 0x54 },
- { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */
- { 0x75, 0x0e },
- { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */
- { 0x77, 0xff },
- { 0x78, 0x80 },
- { 0x79, 0x80 },
- { 0x7a, 0x80 },
- { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */
- { 0x7c, 0x00 },
- { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */
- { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */
- { 0x7f, 0xfb },
- { 0x80, 0x28 },
- { 0x81, 0x00 },
- { 0x82, 0x23 },
- { 0x83, 0x0b },
- { 0x84, 0x00 },
- { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */
- { 0x86, 0xc9 },
- { 0x87, 0x00 },
- { 0x88, 0x00 },
- { 0x89, 0x01 },
- { 0x12, 0x20 },
- { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */
-};
-
-static unsigned char ov7670_abs_to_sm(unsigned char v)
-{
- if (v > 127)
- return v & 0x7f;
- return (128 - v) | 0x80;
-}
-
-/* Write a OV519 register */
-static void reg_w(struct sd *sd, u16 index, u16 value)
-{
- int ret, req = 0;
-
- if (sd->gspca_dev.usb_err < 0)
- return;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- req = 2;
- break;
- case BRIDGE_OVFX2:
- req = 0x0a;
- /* fall through */
- case BRIDGE_W9968CF:
- PDEBUG(D_USBO, "SET %02x %04x %04x",
- req, value, index);
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_sndctrlpipe(sd->gspca_dev.dev, 0),
- req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 500);
- goto leave;
- default:
- req = 1;
- }
-
- PDEBUG(D_USBO, "SET %02x 0000 %04x %02x",
- req, index, value);
- sd->gspca_dev.usb_buf[0] = value;
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_sndctrlpipe(sd->gspca_dev.dev, 0),
- req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index,
- sd->gspca_dev.usb_buf, 1, 500);
-leave:
- if (ret < 0) {
- pr_err("reg_w %02x failed %d\n", index, ret);
- sd->gspca_dev.usb_err = ret;
- return;
- }
-}
-
-/* Read from a OV519 register, note not valid for the w9968cf!! */
-/* returns: negative is error, pos or zero is data */
-static int reg_r(struct sd *sd, u16 index)
-{
- int ret;
- int req;
-
- if (sd->gspca_dev.usb_err < 0)
- return -1;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- req = 3;
- break;
- case BRIDGE_OVFX2:
- req = 0x0b;
- break;
- default:
- req = 1;
- }
-
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
- req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index, sd->gspca_dev.usb_buf, 1, 500);
-
- if (ret >= 0) {
- ret = sd->gspca_dev.usb_buf[0];
- PDEBUG(D_USBI, "GET %02x 0000 %04x %02x",
- req, index, ret);
- } else {
- pr_err("reg_r %02x failed %d\n", index, ret);
- sd->gspca_dev.usb_err = ret;
- }
-
- return ret;
-}
-
-/* Read 8 values from a OV519 register */
-static int reg_r8(struct sd *sd,
- u16 index)
-{
- int ret;
-
- if (sd->gspca_dev.usb_err < 0)
- return -1;
-
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
- 1, /* REQ_IO */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index, sd->gspca_dev.usb_buf, 8, 500);
-
- if (ret >= 0) {
- ret = sd->gspca_dev.usb_buf[0];
- } else {
- pr_err("reg_r8 %02x failed %d\n", index, ret);
- sd->gspca_dev.usb_err = ret;
- }
-
- return ret;
-}
-
-/*
- * Writes bits at positions specified by mask to an OV51x reg. Bits that are in
- * the same position as 1's in "mask" are cleared and set to "value". Bits
- * that are in the same position as 0's in "mask" are preserved, regardless
- * of their respective state in "value".
- */
-static void reg_w_mask(struct sd *sd,
- u16 index,
- u8 value,
- u8 mask)
-{
- int ret;
- u8 oldval;
-
- if (mask != 0xff) {
- value &= mask; /* Enforce mask on value */
- ret = reg_r(sd, index);
- if (ret < 0)
- return;
-
- oldval = ret & ~mask; /* Clear the masked bits */
- value |= oldval; /* Set the desired bits */
- }
- reg_w(sd, index, value);
-}
-
-/*
- * Writes multiple (n) byte value to a single register. Only valid with certain
- * registers (0x30 and 0xc4 - 0xce).
- */
-static void ov518_reg_w32(struct sd *sd, u16 index, u32 value, int n)
-{
- int ret;
-
- if (sd->gspca_dev.usb_err < 0)
- return;
-
- *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
-
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_sndctrlpipe(sd->gspca_dev.dev, 0),
- 1 /* REG_IO */,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index,
- sd->gspca_dev.usb_buf, n, 500);
- if (ret < 0) {
- pr_err("reg_w32 %02x failed %d\n", index, ret);
- sd->gspca_dev.usb_err = ret;
- }
-}
-
-static void ov511_i2c_w(struct sd *sd, u8 reg, u8 value)
-{
- int rc, retries;
-
- PDEBUG(D_USBO, "ov511_i2c_w %02x %02x", reg, value);
-
- /* Three byte write cycle */
- for (retries = 6; ; ) {
- /* Select camera register */
- reg_w(sd, R51x_I2C_SADDR_3, reg);
-
- /* Write "value" to I2C data port of OV511 */
- reg_w(sd, R51x_I2C_DATA, value);
-
- /* Initiate 3-byte write cycle */
- reg_w(sd, R511_I2C_CTL, 0x01);
-
- do {
- rc = reg_r(sd, R511_I2C_CTL);
- } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
-
- if (rc < 0)
- return;
-
- if ((rc & 2) == 0) /* Ack? */
- break;
- if (--retries < 0) {
- PDEBUG(D_USBO, "i2c write retries exhausted");
- return;
- }
- }
-}
-
-static int ov511_i2c_r(struct sd *sd, u8 reg)
-{
- int rc, value, retries;
-
- /* Two byte write cycle */
- for (retries = 6; ; ) {
- /* Select camera register */
- reg_w(sd, R51x_I2C_SADDR_2, reg);
-
- /* Initiate 2-byte write cycle */
- reg_w(sd, R511_I2C_CTL, 0x03);
-
- do {
- rc = reg_r(sd, R511_I2C_CTL);
- } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
-
- if (rc < 0)
- return rc;
-
- if ((rc & 2) == 0) /* Ack? */
- break;
-
- /* I2C abort */
- reg_w(sd, R511_I2C_CTL, 0x10);
-
- if (--retries < 0) {
- PDEBUG(D_USBI, "i2c write retries exhausted");
- return -1;
- }
- }
-
- /* Two byte read cycle */
- for (retries = 6; ; ) {
- /* Initiate 2-byte read cycle */
- reg_w(sd, R511_I2C_CTL, 0x05);
-
- do {
- rc = reg_r(sd, R511_I2C_CTL);
- } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
-
- if (rc < 0)
- return rc;
-
- if ((rc & 2) == 0) /* Ack? */
- break;
-
- /* I2C abort */
- reg_w(sd, R511_I2C_CTL, 0x10);
-
- if (--retries < 0) {
- PDEBUG(D_USBI, "i2c read retries exhausted");
- return -1;
- }
- }
-
- value = reg_r(sd, R51x_I2C_DATA);
-
- PDEBUG(D_USBI, "ov511_i2c_r %02x %02x", reg, value);
-
- /* This is needed to make i2c_w() work */
- reg_w(sd, R511_I2C_CTL, 0x05);
-
- return value;
-}
-
-/*
- * The OV518 I2C I/O procedure is different, hence, this function.
- * This is normally only called from i2c_w(). Note that this function
- * always succeeds regardless of whether the sensor is present and working.
- */
-static void ov518_i2c_w(struct sd *sd,
- u8 reg,
- u8 value)
-{
- PDEBUG(D_USBO, "ov518_i2c_w %02x %02x", reg, value);
-
- /* Select camera register */
- reg_w(sd, R51x_I2C_SADDR_3, reg);
-
- /* Write "value" to I2C data port of OV511 */
- reg_w(sd, R51x_I2C_DATA, value);
-
- /* Initiate 3-byte write cycle */
- reg_w(sd, R518_I2C_CTL, 0x01);
-
- /* wait for write complete */
- msleep(4);
- reg_r8(sd, R518_I2C_CTL);
-}
-
-/*
- * returns: negative is error, pos or zero is data
- *
- * The OV518 I2C I/O procedure is different, hence, this function.
- * This is normally only called from i2c_r(). Note that this function
- * always succeeds regardless of whether the sensor is present and working.
- */
-static int ov518_i2c_r(struct sd *sd, u8 reg)
-{
- int value;
-
- /* Select camera register */
- reg_w(sd, R51x_I2C_SADDR_2, reg);
-
- /* Initiate 2-byte write cycle */
- reg_w(sd, R518_I2C_CTL, 0x03);
- reg_r8(sd, R518_I2C_CTL);
-
- /* Initiate 2-byte read cycle */
- reg_w(sd, R518_I2C_CTL, 0x05);
- reg_r8(sd, R518_I2C_CTL);
-
- value = reg_r(sd, R51x_I2C_DATA);
- PDEBUG(D_USBI, "ov518_i2c_r %02x %02x", reg, value);
- return value;
-}
-
-static void ovfx2_i2c_w(struct sd *sd, u8 reg, u8 value)
-{
- int ret;
-
- if (sd->gspca_dev.usb_err < 0)
- return;
-
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_sndctrlpipe(sd->gspca_dev.dev, 0),
- 0x02,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (u16) value, (u16) reg, NULL, 0, 500);
-
- if (ret < 0) {
- pr_err("ovfx2_i2c_w %02x failed %d\n", reg, ret);
- sd->gspca_dev.usb_err = ret;
- }
-
- PDEBUG(D_USBO, "ovfx2_i2c_w %02x %02x", reg, value);
-}
-
-static int ovfx2_i2c_r(struct sd *sd, u8 reg)
-{
- int ret;
-
- if (sd->gspca_dev.usb_err < 0)
- return -1;
-
- ret = usb_control_msg(sd->gspca_dev.dev,
- usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
- 0x03,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, (u16) reg, sd->gspca_dev.usb_buf, 1, 500);
-
- if (ret >= 0) {
- ret = sd->gspca_dev.usb_buf[0];
- PDEBUG(D_USBI, "ovfx2_i2c_r %02x %02x", reg, ret);
- } else {
- pr_err("ovfx2_i2c_r %02x failed %d\n", reg, ret);
- sd->gspca_dev.usb_err = ret;
- }
-
- return ret;
-}
-
-static void i2c_w(struct sd *sd, u8 reg, u8 value)
-{
- if (sd->sensor_reg_cache[reg] == value)
- return;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- ov511_i2c_w(sd, reg, value);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- case BRIDGE_OV519:
- ov518_i2c_w(sd, reg, value);
- break;
- case BRIDGE_OVFX2:
- ovfx2_i2c_w(sd, reg, value);
- break;
- case BRIDGE_W9968CF:
- w9968cf_i2c_w(sd, reg, value);
- break;
- }
-
- if (sd->gspca_dev.usb_err >= 0) {
- /* Up on sensor reset empty the register cache */
- if (reg == 0x12 && (value & 0x80))
- memset(sd->sensor_reg_cache, -1,
- sizeof(sd->sensor_reg_cache));
- else
- sd->sensor_reg_cache[reg] = value;
- }
-}
-
-static int i2c_r(struct sd *sd, u8 reg)
-{
- int ret = -1;
-
- if (sd->sensor_reg_cache[reg] != -1)
- return sd->sensor_reg_cache[reg];
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- ret = ov511_i2c_r(sd, reg);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- case BRIDGE_OV519:
- ret = ov518_i2c_r(sd, reg);
- break;
- case BRIDGE_OVFX2:
- ret = ovfx2_i2c_r(sd, reg);
- break;
- case BRIDGE_W9968CF:
- ret = w9968cf_i2c_r(sd, reg);
- break;
- }
-
- if (ret >= 0)
- sd->sensor_reg_cache[reg] = ret;
-
- return ret;
-}
-
-/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
- * the same position as 1's in "mask" are cleared and set to "value". Bits
- * that are in the same position as 0's in "mask" are preserved, regardless
- * of their respective state in "value".
- */
-static void i2c_w_mask(struct sd *sd,
- u8 reg,
- u8 value,
- u8 mask)
-{
- int rc;
- u8 oldval;
-
- value &= mask; /* Enforce mask on value */
- rc = i2c_r(sd, reg);
- if (rc < 0)
- return;
- oldval = rc & ~mask; /* Clear the masked bits */
- value |= oldval; /* Set the desired bits */
- i2c_w(sd, reg, value);
-}
-
-/* Temporarily stops OV511 from functioning. Must do this before changing
- * registers while the camera is streaming */
-static inline void ov51x_stop(struct sd *sd)
-{
- PDEBUG(D_STREAM, "stopping");
- sd->stopped = 1;
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- reg_w(sd, R51x_SYS_RESET, 0x3d);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
- break;
- case BRIDGE_OV519:
- reg_w(sd, OV519_R51_RESET1, 0x0f);
- reg_w(sd, OV519_R51_RESET1, 0x00);
- reg_w(sd, 0x22, 0x00); /* FRAR */
- break;
- case BRIDGE_OVFX2:
- reg_w_mask(sd, 0x0f, 0x00, 0x02);
- break;
- case BRIDGE_W9968CF:
- reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
- break;
- }
-}
-
-/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
- * actually stopped (for performance). */
-static inline void ov51x_restart(struct sd *sd)
-{
- PDEBUG(D_STREAM, "restarting");
- if (!sd->stopped)
- return;
- sd->stopped = 0;
-
- /* Reinitialize the stream */
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- reg_w(sd, R51x_SYS_RESET, 0x00);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- reg_w(sd, 0x2f, 0x80);
- reg_w(sd, R51x_SYS_RESET, 0x00);
- break;
- case BRIDGE_OV519:
- reg_w(sd, OV519_R51_RESET1, 0x0f);
- reg_w(sd, OV519_R51_RESET1, 0x00);
- reg_w(sd, 0x22, 0x1d); /* FRAR */
- break;
- case BRIDGE_OVFX2:
- reg_w_mask(sd, 0x0f, 0x02, 0x02);
- break;
- case BRIDGE_W9968CF:
- reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
- break;
- }
-}
-
-static void ov51x_set_slave_ids(struct sd *sd, u8 slave);
-
-/* This does an initial reset of an OmniVision sensor and ensures that I2C
- * is synchronized. Returns <0 on failure.
- */
-static int init_ov_sensor(struct sd *sd, u8 slave)
-{
- int i;
-
- ov51x_set_slave_ids(sd, slave);
-
- /* Reset the sensor */
- i2c_w(sd, 0x12, 0x80);
-
- /* Wait for it to initialize */
- msleep(150);
-
- for (i = 0; i < i2c_detect_tries; i++) {
- if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f &&
- i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) {
- PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i);
- return 0;
- }
-
- /* Reset the sensor */
- i2c_w(sd, 0x12, 0x80);
-
- /* Wait for it to initialize */
- msleep(150);
-
- /* Dummy read to sync I2C */
- if (i2c_r(sd, 0x00) < 0)
- return -1;
- }
- return -1;
-}
-
-/* Set the read and write slave IDs. The "slave" argument is the write slave,
- * and the read slave will be set to (slave + 1).
- * This should not be called from outside the i2c I/O functions.
- * Sets I2C read and write slave IDs. Returns <0 for error
- */
-static void ov51x_set_slave_ids(struct sd *sd,
- u8 slave)
-{
- switch (sd->bridge) {
- case BRIDGE_OVFX2:
- reg_w(sd, OVFX2_I2C_ADDR, slave);
- return;
- case BRIDGE_W9968CF:
- sd->sensor_addr = slave;
- return;
- }
-
- reg_w(sd, R51x_I2C_W_SID, slave);
- reg_w(sd, R51x_I2C_R_SID, slave + 1);
-}
-
-static void write_regvals(struct sd *sd,
- const struct ov_regvals *regvals,
- int n)
-{
- while (--n >= 0) {
- reg_w(sd, regvals->reg, regvals->val);
- regvals++;
- }
-}
-
-static void write_i2c_regvals(struct sd *sd,
- const struct ov_i2c_regvals *regvals,
- int n)
-{
- while (--n >= 0) {
- i2c_w(sd, regvals->reg, regvals->val);
- regvals++;
- }
-}
-
-/****************************************************************************
- *
- * OV511 and sensor configuration
- *
- ***************************************************************************/
-
-/* This initializes the OV2x10 / OV3610 / OV3620 / OV9600 */
-static void ov_hires_configure(struct sd *sd)
-{
- int high, low;
-
- if (sd->bridge != BRIDGE_OVFX2) {
- pr_err("error hires sensors only supported with ovfx2\n");
- return;
- }
-
- PDEBUG(D_PROBE, "starting ov hires configuration");
-
- /* Detect sensor (sub)type */
- high = i2c_r(sd, 0x0a);
- low = i2c_r(sd, 0x0b);
- /* info("%x, %x", high, low); */
- switch (high) {
- case 0x96:
- switch (low) {
- case 0x40:
- PDEBUG(D_PROBE, "Sensor is a OV2610");
- sd->sensor = SEN_OV2610;
- return;
- case 0x41:
- PDEBUG(D_PROBE, "Sensor is a OV2610AE");
- sd->sensor = SEN_OV2610AE;
- return;
- case 0xb1:
- PDEBUG(D_PROBE, "Sensor is a OV9600");
- sd->sensor = SEN_OV9600;
- return;
- }
- break;
- case 0x36:
- if ((low & 0x0f) == 0x00) {
- PDEBUG(D_PROBE, "Sensor is a OV3610");
- sd->sensor = SEN_OV3610;
- return;
- }
- break;
- }
- pr_err("Error unknown sensor type: %02x%02x\n", high, low);
-}
-
-/* This initializes the OV8110, OV8610 sensor. The OV8110 uses
- * the same register settings as the OV8610, since they are very similar.
- */
-static void ov8xx0_configure(struct sd *sd)
-{
- int rc;
-
- PDEBUG(D_PROBE, "starting ov8xx0 configuration");
-
- /* Detect sensor (sub)type */
- rc = i2c_r(sd, OV7610_REG_COM_I);
- if (rc < 0) {
- PDEBUG(D_ERR, "Error detecting sensor type");
- return;
- }
- if ((rc & 3) == 1)
- sd->sensor = SEN_OV8610;
- else
- pr_err("Unknown image sensor version: %d\n", rc & 3);
-}
-
-/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses
- * the same register settings as the OV7610, since they are very similar.
- */
-static void ov7xx0_configure(struct sd *sd)
-{
- int rc, high, low;
-
- PDEBUG(D_PROBE, "starting OV7xx0 configuration");
-
- /* Detect sensor (sub)type */
- rc = i2c_r(sd, OV7610_REG_COM_I);
-
- /* add OV7670 here
- * it appears to be wrongly detected as a 7610 by default */
- if (rc < 0) {
- pr_err("Error detecting sensor type\n");
- return;
- }
- if ((rc & 3) == 3) {
- /* quick hack to make OV7670s work */
- high = i2c_r(sd, 0x0a);
- low = i2c_r(sd, 0x0b);
- /* info("%x, %x", high, low); */
- if (high == 0x76 && (low & 0xf0) == 0x70) {
- PDEBUG(D_PROBE, "Sensor is an OV76%02x", low);
- sd->sensor = SEN_OV7670;
- } else {
- PDEBUG(D_PROBE, "Sensor is an OV7610");
- sd->sensor = SEN_OV7610;
- }
- } else if ((rc & 3) == 1) {
- /* I don't know what's different about the 76BE yet. */
- if (i2c_r(sd, 0x15) & 1) {
- PDEBUG(D_PROBE, "Sensor is an OV7620AE");
- sd->sensor = SEN_OV7620AE;
- } else {
- PDEBUG(D_PROBE, "Sensor is an OV76BE");
- sd->sensor = SEN_OV76BE;
- }
- } else if ((rc & 3) == 0) {
- /* try to read product id registers */
- high = i2c_r(sd, 0x0a);
- if (high < 0) {
- pr_err("Error detecting camera chip PID\n");
- return;
- }
- low = i2c_r(sd, 0x0b);
- if (low < 0) {
- pr_err("Error detecting camera chip VER\n");
- return;
- }
- if (high == 0x76) {
- switch (low) {
- case 0x30:
- pr_err("Sensor is an OV7630/OV7635\n");
- pr_err("7630 is not supported by this driver\n");
- return;
- case 0x40:
- PDEBUG(D_PROBE, "Sensor is an OV7645");
- sd->sensor = SEN_OV7640; /* FIXME */
- break;
- case 0x45:
- PDEBUG(D_PROBE, "Sensor is an OV7645B");
- sd->sensor = SEN_OV7640; /* FIXME */
- break;
- case 0x48:
- PDEBUG(D_PROBE, "Sensor is an OV7648");
- sd->sensor = SEN_OV7648;
- break;
- case 0x60:
- PDEBUG(D_PROBE, "Sensor is a OV7660");
- sd->sensor = SEN_OV7660;
- break;
- default:
- pr_err("Unknown sensor: 0x76%02x\n", low);
- return;
- }
- } else {
- PDEBUG(D_PROBE, "Sensor is an OV7620");
- sd->sensor = SEN_OV7620;
- }
- } else {
- pr_err("Unknown image sensor version: %d\n", rc & 3);
- }
-}
-
-/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
-static void ov6xx0_configure(struct sd *sd)
-{
- int rc;
- PDEBUG(D_PROBE, "starting OV6xx0 configuration");
-
- /* Detect sensor (sub)type */
- rc = i2c_r(sd, OV7610_REG_COM_I);
- if (rc < 0) {
- pr_err("Error detecting sensor type\n");
- return;
- }
-
- /* Ugh. The first two bits are the version bits, but
- * the entire register value must be used. I guess OVT
- * underestimated how many variants they would make. */
- switch (rc) {
- case 0x00:
- sd->sensor = SEN_OV6630;
- pr_warn("WARNING: Sensor is an OV66308. Your camera may have been misdetected in previous driver versions.\n");
- break;
- case 0x01:
- sd->sensor = SEN_OV6620;
- PDEBUG(D_PROBE, "Sensor is an OV6620");
- break;
- case 0x02:
- sd->sensor = SEN_OV6630;
- PDEBUG(D_PROBE, "Sensor is an OV66308AE");
- break;
- case 0x03:
- sd->sensor = SEN_OV66308AF;
- PDEBUG(D_PROBE, "Sensor is an OV66308AF");
- break;
- case 0x90:
- sd->sensor = SEN_OV6630;
- pr_warn("WARNING: Sensor is an OV66307. Your camera may have been misdetected in previous driver versions.\n");
- break;
- default:
- pr_err("FATAL: Unknown sensor version: 0x%02x\n", rc);
- return;
- }
-
- /* Set sensor-specific vars */
- sd->sif = 1;
-}
-
-/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */
-static void ov51x_led_control(struct sd *sd, int on)
-{
- if (sd->invert_led)
- on = !on;
-
- switch (sd->bridge) {
- /* OV511 has no LED control */
- case BRIDGE_OV511PLUS:
- reg_w(sd, R511_SYS_LED_CTL, on);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- reg_w_mask(sd, R518_GPIO_OUT, 0x02 * on, 0x02);
- break;
- case BRIDGE_OV519:
- reg_w_mask(sd, OV519_GPIO_DATA_OUT0, on, 1);
- break;
- }
-}
-
-static void sd_reset_snapshot(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!sd->snapshot_needs_reset)
- return;
-
- /* Note it is important that we clear sd->snapshot_needs_reset,
- before actually clearing the snapshot state in the bridge
- otherwise we might race with the pkt_scan interrupt handler */
- sd->snapshot_needs_reset = 0;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- reg_w(sd, R51x_SYS_SNAP, 0x02);
- reg_w(sd, R51x_SYS_SNAP, 0x00);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */
- reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */
- break;
- case BRIDGE_OV519:
- reg_w(sd, R51x_SYS_RESET, 0x40);
- reg_w(sd, R51x_SYS_RESET, 0x00);
- break;
- }
-}
-
-static void ov51x_upload_quan_tables(struct sd *sd)
-{
- const unsigned char yQuanTable511[] = {
- 0, 1, 1, 2, 2, 3, 3, 4,
- 1, 1, 1, 2, 2, 3, 4, 4,
- 1, 1, 2, 2, 3, 4, 4, 4,
- 2, 2, 2, 3, 4, 4, 4, 4,
- 2, 2, 3, 4, 4, 5, 5, 5,
- 3, 3, 4, 4, 5, 5, 5, 5,
- 3, 4, 4, 4, 5, 5, 5, 5,
- 4, 4, 4, 4, 5, 5, 5, 5
- };
-
- const unsigned char uvQuanTable511[] = {
- 0, 2, 2, 3, 4, 4, 4, 4,
- 2, 2, 2, 4, 4, 4, 4, 4,
- 2, 2, 3, 4, 4, 4, 4, 4,
- 3, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4
- };
-
- /* OV518 quantization tables are 8x4 (instead of 8x8) */
- const unsigned char yQuanTable518[] = {
- 5, 4, 5, 6, 6, 7, 7, 7,
- 5, 5, 5, 5, 6, 7, 7, 7,
- 6, 6, 6, 6, 7, 7, 7, 8,
- 7, 7, 6, 7, 7, 7, 8, 8
- };
- const unsigned char uvQuanTable518[] = {
- 6, 6, 6, 7, 7, 7, 7, 7,
- 6, 6, 6, 7, 7, 7, 7, 7,
- 6, 6, 6, 7, 7, 7, 7, 8,
- 7, 7, 7, 7, 7, 7, 8, 8
- };
-
- const unsigned char *pYTable, *pUVTable;
- unsigned char val0, val1;
- int i, size, reg = R51x_COMP_LUT_BEGIN;
-
- PDEBUG(D_PROBE, "Uploading quantization tables");
-
- if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) {
- pYTable = yQuanTable511;
- pUVTable = uvQuanTable511;
- size = 32;
- } else {
- pYTable = yQuanTable518;
- pUVTable = uvQuanTable518;
- size = 16;
- }
-
- for (i = 0; i < size; i++) {
- val0 = *pYTable++;
- val1 = *pYTable++;
- val0 &= 0x0f;
- val1 &= 0x0f;
- val0 |= val1 << 4;
- reg_w(sd, reg, val0);
-
- val0 = *pUVTable++;
- val1 = *pUVTable++;
- val0 &= 0x0f;
- val1 &= 0x0f;
- val0 |= val1 << 4;
- reg_w(sd, reg + size, val0);
-
- reg++;
- }
-}
-
-/* This initializes the OV511/OV511+ and the sensor */
-static void ov511_configure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* For 511 and 511+ */
- const struct ov_regvals init_511[] = {
- { R51x_SYS_RESET, 0x7f },
- { R51x_SYS_INIT, 0x01 },
- { R51x_SYS_RESET, 0x7f },
- { R51x_SYS_INIT, 0x01 },
- { R51x_SYS_RESET, 0x3f },
- { R51x_SYS_INIT, 0x01 },
- { R51x_SYS_RESET, 0x3d },
- };
-
- const struct ov_regvals norm_511[] = {
- { R511_DRAM_FLOW_CTL, 0x01 },
- { R51x_SYS_SNAP, 0x00 },
- { R51x_SYS_SNAP, 0x02 },
- { R51x_SYS_SNAP, 0x00 },
- { R511_FIFO_OPTS, 0x1f },
- { R511_COMP_EN, 0x00 },
- { R511_COMP_LUT_EN, 0x03 },
- };
-
- const struct ov_regvals norm_511_p[] = {
- { R511_DRAM_FLOW_CTL, 0xff },
- { R51x_SYS_SNAP, 0x00 },
- { R51x_SYS_SNAP, 0x02 },
- { R51x_SYS_SNAP, 0x00 },
- { R511_FIFO_OPTS, 0xff },
- { R511_COMP_EN, 0x00 },
- { R511_COMP_LUT_EN, 0x03 },
- };
-
- const struct ov_regvals compress_511[] = {
- { 0x70, 0x1f },
- { 0x71, 0x05 },
- { 0x72, 0x06 },
- { 0x73, 0x06 },
- { 0x74, 0x14 },
- { 0x75, 0x03 },
- { 0x76, 0x04 },
- { 0x77, 0x04 },
- };
-
- PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID));
-
- write_regvals(sd, init_511, ARRAY_SIZE(init_511));
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- write_regvals(sd, norm_511, ARRAY_SIZE(norm_511));
- break;
- case BRIDGE_OV511PLUS:
- write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p));
- break;
- }
-
- /* Init compression */
- write_regvals(sd, compress_511, ARRAY_SIZE(compress_511));
-
- ov51x_upload_quan_tables(sd);
-}
-
-/* This initializes the OV518/OV518+ and the sensor */
-static void ov518_configure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* For 518 and 518+ */
- const struct ov_regvals init_518[] = {
- { R51x_SYS_RESET, 0x40 },
- { R51x_SYS_INIT, 0xe1 },
- { R51x_SYS_RESET, 0x3e },
- { R51x_SYS_INIT, 0xe1 },
- { R51x_SYS_RESET, 0x00 },
- { R51x_SYS_INIT, 0xe1 },
- { 0x46, 0x00 },
- { 0x5d, 0x03 },
- };
-
- const struct ov_regvals norm_518[] = {
- { R51x_SYS_SNAP, 0x02 }, /* Reset */
- { R51x_SYS_SNAP, 0x01 }, /* Enable */
- { 0x31, 0x0f },
- { 0x5d, 0x03 },
- { 0x24, 0x9f },
- { 0x25, 0x90 },
- { 0x20, 0x00 },
- { 0x51, 0x04 },
- { 0x71, 0x19 },
- { 0x2f, 0x80 },
- };
-
- const struct ov_regvals norm_518_p[] = {
- { R51x_SYS_SNAP, 0x02 }, /* Reset */
- { R51x_SYS_SNAP, 0x01 }, /* Enable */
- { 0x31, 0x0f },
- { 0x5d, 0x03 },
- { 0x24, 0x9f },
- { 0x25, 0x90 },
- { 0x20, 0x60 },
- { 0x51, 0x02 },
- { 0x71, 0x19 },
- { 0x40, 0xff },
- { 0x41, 0x42 },
- { 0x46, 0x00 },
- { 0x33, 0x04 },
- { 0x21, 0x19 },
- { 0x3f, 0x10 },
- { 0x2f, 0x80 },
- };
-
- /* First 5 bits of custom ID reg are a revision ID on OV518 */
- PDEBUG(D_PROBE, "Device revision %d",
- 0x1f & reg_r(sd, R51x_SYS_CUST_ID));
-
- write_regvals(sd, init_518, ARRAY_SIZE(init_518));
-
- /* Set LED GPIO pin to output mode */
- reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02);
-
- switch (sd->bridge) {
- case BRIDGE_OV518:
- write_regvals(sd, norm_518, ARRAY_SIZE(norm_518));
- break;
- case BRIDGE_OV518PLUS:
- write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p));
- break;
- }
-
- ov51x_upload_quan_tables(sd);
-
- reg_w(sd, 0x2f, 0x80);
-}
-
-static void ov519_configure(struct sd *sd)
-{
- static const struct ov_regvals init_519[] = {
- { 0x5a, 0x6d }, /* EnableSystem */
- { 0x53, 0x9b }, /* don't enable the microcontroller */
- { OV519_R54_EN_CLK1, 0xff }, /* set bit2 to enable jpeg */
- { 0x5d, 0x03 },
- { 0x49, 0x01 },
- { 0x48, 0x00 },
- /* Set LED pin to output mode. Bit 4 must be cleared or sensor
- * detection will fail. This deserves further investigation. */
- { OV519_GPIO_IO_CTRL0, 0xee },
- { OV519_R51_RESET1, 0x0f },
- { OV519_R51_RESET1, 0x00 },
- { 0x22, 0x00 },
- /* windows reads 0x55 at this point*/
- };
-
- write_regvals(sd, init_519, ARRAY_SIZE(init_519));
-}
-
-static void ovfx2_configure(struct sd *sd)
-{
- static const struct ov_regvals init_fx2[] = {
- { 0x00, 0x60 },
- { 0x02, 0x01 },
- { 0x0f, 0x1d },
- { 0xe9, 0x82 },
- { 0xea, 0xc7 },
- { 0xeb, 0x10 },
- { 0xec, 0xf6 },
- };
-
- sd->stopped = 1;
-
- write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
-}
-
-/* set the mode */
-/* This function works for ov7660 only */
-static void ov519_set_mode(struct sd *sd)
-{
- static const struct ov_regvals bridge_ov7660[2][10] = {
- {{0x10, 0x14}, {0x11, 0x1e}, {0x12, 0x00}, {0x13, 0x00},
- {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
- {0x25, 0x01}, {0x26, 0x00}},
- {{0x10, 0x28}, {0x11, 0x3c}, {0x12, 0x00}, {0x13, 0x00},
- {0x14, 0x00}, {0x15, 0x00}, {0x16, 0x00}, {0x20, 0x0c},
- {0x25, 0x03}, {0x26, 0x00}}
- };
- static const struct ov_i2c_regvals sensor_ov7660[2][3] = {
- {{0x12, 0x00}, {0x24, 0x00}, {0x0c, 0x0c}},
- {{0x12, 0x00}, {0x04, 0x00}, {0x0c, 0x00}}
- };
- static const struct ov_i2c_regvals sensor_ov7660_2[] = {
- {OV7670_R17_HSTART, 0x13},
- {OV7670_R18_HSTOP, 0x01},
- {OV7670_R32_HREF, 0x92},
- {OV7670_R19_VSTART, 0x02},
- {OV7670_R1A_VSTOP, 0x7a},
- {OV7670_R03_VREF, 0x00},
-/* {0x33, 0x00}, */
-/* {0x34, 0x07}, */
-/* {0x36, 0x00}, */
-/* {0x6b, 0x0a}, */
- };
-
- write_regvals(sd, bridge_ov7660[sd->gspca_dev.curr_mode],
- ARRAY_SIZE(bridge_ov7660[0]));
- write_i2c_regvals(sd, sensor_ov7660[sd->gspca_dev.curr_mode],
- ARRAY_SIZE(sensor_ov7660[0]));
- write_i2c_regvals(sd, sensor_ov7660_2,
- ARRAY_SIZE(sensor_ov7660_2));
-}
-
-/* set the frame rate */
-/* This function works for sensors ov7640, ov7648 ov7660 and ov7670 only */
-static void ov519_set_fr(struct sd *sd)
-{
- int fr;
- u8 clock;
- /* frame rate table with indices:
- * - mode = 0: 320x240, 1: 640x480
- * - fr rate = 0: 30, 1: 25, 2: 20, 3: 15, 4: 10, 5: 5
- * - reg = 0: bridge a4, 1: bridge 23, 2: sensor 11 (clock)
- */
- static const u8 fr_tb[2][6][3] = {
- {{0x04, 0xff, 0x00},
- {0x04, 0x1f, 0x00},
- {0x04, 0x1b, 0x00},
- {0x04, 0x15, 0x00},
- {0x04, 0x09, 0x00},
- {0x04, 0x01, 0x00}},
- {{0x0c, 0xff, 0x00},
- {0x0c, 0x1f, 0x00},
- {0x0c, 0x1b, 0x00},
- {0x04, 0xff, 0x01},
- {0x04, 0x1f, 0x01},
- {0x04, 0x1b, 0x01}},
- };
-
- if (frame_rate > 0)
- sd->frame_rate = frame_rate;
- if (sd->frame_rate >= 30)
- fr = 0;
- else if (sd->frame_rate >= 25)
- fr = 1;
- else if (sd->frame_rate >= 20)
- fr = 2;
- else if (sd->frame_rate >= 15)
- fr = 3;
- else if (sd->frame_rate >= 10)
- fr = 4;
- else
- fr = 5;
- reg_w(sd, 0xa4, fr_tb[sd->gspca_dev.curr_mode][fr][0]);
- reg_w(sd, 0x23, fr_tb[sd->gspca_dev.curr_mode][fr][1]);
- clock = fr_tb[sd->gspca_dev.curr_mode][fr][2];
- if (sd->sensor == SEN_OV7660)
- clock |= 0x80; /* enable double clock */
- ov518_i2c_w(sd, OV7670_R11_CLKRC, clock);
-}
-
-static void setautogain(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w_mask(sd, 0x13, val ? 0x05 : 0x00, 0x05);
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam = &gspca_dev->cam;
-
- sd->bridge = id->driver_info & BRIDGE_MASK;
- sd->invert_led = (id->driver_info & BRIDGE_INVERT_LED) != 0;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- cam->cam_mode = ov511_vga_mode;
- cam->nmodes = ARRAY_SIZE(ov511_vga_mode);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- cam->cam_mode = ov518_vga_mode;
- cam->nmodes = ARRAY_SIZE(ov518_vga_mode);
- break;
- case BRIDGE_OV519:
- cam->cam_mode = ov519_vga_mode;
- cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
- break;
- case BRIDGE_OVFX2:
- cam->cam_mode = ov519_vga_mode;
- cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
- cam->bulk_size = OVFX2_BULK_SIZE;
- cam->bulk_nurbs = MAX_NURBS;
- cam->bulk = 1;
- break;
- case BRIDGE_W9968CF:
- cam->cam_mode = w9968cf_vga_mode;
- cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
- break;
- }
-
- sd->frame_rate = 15;
-
- 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;
- struct cam *cam = &gspca_dev->cam;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- ov511_configure(gspca_dev);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- ov518_configure(gspca_dev);
- break;
- case BRIDGE_OV519:
- ov519_configure(sd);
- break;
- case BRIDGE_OVFX2:
- ovfx2_configure(sd);
- break;
- case BRIDGE_W9968CF:
- w9968cf_configure(sd);
- break;
- }
-
- /* The OV519 must be more aggressive about sensor detection since
- * I2C write will never fail if the sensor is not present. We have
- * to try to initialize the sensor to detect its presence */
- sd->sensor = -1;
-
- /* Test for 76xx */
- if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
- ov7xx0_configure(sd);
-
- /* Test for 6xx0 */
- } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
- ov6xx0_configure(sd);
-
- /* Test for 8xx0 */
- } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
- ov8xx0_configure(sd);
-
- /* Test for 3xxx / 2xxx */
- } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
- ov_hires_configure(sd);
- } else {
- pr_err("Can't determine sensor slave IDs\n");
- goto error;
- }
-
- if (sd->sensor < 0)
- goto error;
-
- ov51x_led_control(sd, 0); /* turn LED off */
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- if (sd->sif) {
- cam->cam_mode = ov511_sif_mode;
- cam->nmodes = ARRAY_SIZE(ov511_sif_mode);
- }
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- if (sd->sif) {
- cam->cam_mode = ov518_sif_mode;
- cam->nmodes = ARRAY_SIZE(ov518_sif_mode);
- }
- break;
- case BRIDGE_OV519:
- if (sd->sif) {
- cam->cam_mode = ov519_sif_mode;
- cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
- }
- break;
- case BRIDGE_OVFX2:
- switch (sd->sensor) {
- case SEN_OV2610:
- case SEN_OV2610AE:
- cam->cam_mode = ovfx2_ov2610_mode;
- cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
- break;
- case SEN_OV3610:
- cam->cam_mode = ovfx2_ov3610_mode;
- cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
- break;
- case SEN_OV9600:
- cam->cam_mode = ovfx2_ov9600_mode;
- cam->nmodes = ARRAY_SIZE(ovfx2_ov9600_mode);
- break;
- default:
- if (sd->sif) {
- cam->cam_mode = ov519_sif_mode;
- cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
- }
- break;
- }
- break;
- case BRIDGE_W9968CF:
- if (sd->sif)
- cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode) - 1;
-
- /* w9968cf needs initialisation once the sensor is known */
- w9968cf_init(sd);
- break;
- }
-
- /* initialize the sensor */
- switch (sd->sensor) {
- case SEN_OV2610:
- write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610));
-
- /* 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));
-
- /* Enable autogain, autoexpo, awb, bandfilter */
- i2c_w_mask(sd, 0x13, 0x27, 0x27);
- break;
- case SEN_OV6620:
- write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20));
- break;
- case SEN_OV6630:
- case SEN_OV66308AF:
- write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30));
- break;
- default:
-/* case SEN_OV7610: */
-/* case SEN_OV76BE: */
- write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610));
- i2c_w_mask(sd, 0x0e, 0x00, 0x40);
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620));
- break;
- case SEN_OV7640:
- case SEN_OV7648:
- write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640));
- break;
- case SEN_OV7660:
- i2c_w(sd, OV7670_R12_COM7, OV7670_COM7_RESET);
- msleep(14);
- reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
- write_regvals(sd, init_519_ov7660,
- ARRAY_SIZE(init_519_ov7660));
- write_i2c_regvals(sd, norm_7660, ARRAY_SIZE(norm_7660));
- sd->gspca_dev.curr_mode = 1; /* 640x480 */
- ov519_set_mode(sd);
- ov519_set_fr(sd);
- sd_reset_snapshot(gspca_dev);
- ov51x_restart(sd);
- ov51x_stop(sd); /* not in win traces */
- ov51x_led_control(sd, 0);
- break;
- case SEN_OV7670:
- write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670));
- break;
- case SEN_OV8610:
- write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610));
- break;
- case SEN_OV9600:
- write_i2c_regvals(sd, norm_9600, ARRAY_SIZE(norm_9600));
-
- /* enable autoexpo */
-/* i2c_w_mask(sd, 0x13, 0x05, 0x05); */
- break;
- }
- return gspca_dev->usb_err;
-error:
- PDEBUG(D_ERR, "OV519 Config failed");
- 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 != 800)
- 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)
- */
-static void ov511_mode_init_regs(struct sd *sd)
-{
- int hsegs, vsegs, packet_size, fps, needed;
- int interlaced = 0;
- struct usb_host_interface *alt;
- struct usb_interface *intf;
-
- intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
- alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
- if (!alt) {
- pr_err("Couldn't get altsetting\n");
- sd->gspca_dev.usb_err = -EIO;
- return;
- }
-
- packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
- reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5);
-
- reg_w(sd, R511_CAM_UV_EN, 0x01);
- reg_w(sd, R511_SNAP_UV_EN, 0x01);
- reg_w(sd, R511_SNAP_OPTS, 0x03);
-
- /* Here I'm assuming that snapshot size == image size.
- * I hope that's always true. --claudio
- */
- hsegs = (sd->gspca_dev.width >> 3) - 1;
- vsegs = (sd->gspca_dev.height >> 3) - 1;
-
- reg_w(sd, R511_CAM_PXCNT, hsegs);
- reg_w(sd, R511_CAM_LNCNT, vsegs);
- reg_w(sd, R511_CAM_PXDIV, 0x00);
- reg_w(sd, R511_CAM_LNDIV, 0x00);
-
- /* YUV420, low pass filter on */
- reg_w(sd, R511_CAM_OPTS, 0x03);
-
- /* Snapshot additions */
- reg_w(sd, R511_SNAP_PXCNT, hsegs);
- reg_w(sd, R511_SNAP_LNCNT, vsegs);
- reg_w(sd, R511_SNAP_PXDIV, 0x00);
- reg_w(sd, R511_SNAP_LNDIV, 0x00);
-
- /******** Set the framerate ********/
- if (frame_rate > 0)
- sd->frame_rate = frame_rate;
-
- switch (sd->sensor) {
- case SEN_OV6620:
- /* No framerate control, doesn't like higher rates yet */
- sd->clockdiv = 3;
- break;
-
- /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
- for more sensors we need to do this for them too */
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV7640:
- case SEN_OV7648:
- case SEN_OV76BE:
- if (sd->gspca_dev.width == 320)
- interlaced = 1;
- /* Fall through */
- case SEN_OV6630:
- case SEN_OV7610:
- case SEN_OV7670:
- switch (sd->frame_rate) {
- case 30:
- case 25:
- /* Not enough bandwidth to do 640x480 @ 30 fps */
- if (sd->gspca_dev.width != 640) {
- sd->clockdiv = 0;
- break;
- }
- /* Fall through for 640x480 case */
- default:
-/* case 20: */
-/* case 15: */
- sd->clockdiv = 1;
- break;
- case 10:
- sd->clockdiv = 2;
- break;
- case 5:
- sd->clockdiv = 5;
- break;
- }
- if (interlaced) {
- sd->clockdiv = (sd->clockdiv + 1) * 2 - 1;
- /* Higher then 10 does not work */
- if (sd->clockdiv > 10)
- sd->clockdiv = 10;
- }
- break;
-
- case SEN_OV8610:
- /* No framerate control ?? */
- sd->clockdiv = 0;
- break;
- }
-
- /* Check if we have enough bandwidth to disable compression */
- fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1;
- needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2;
- /* 1000 isoc packets/sec */
- if (needed > 1000 * packet_size) {
- /* Enable Y and UV quantization and compression */
- reg_w(sd, R511_COMP_EN, 0x07);
- reg_w(sd, R511_COMP_LUT_EN, 0x03);
- } else {
- reg_w(sd, R511_COMP_EN, 0x06);
- reg_w(sd, R511_COMP_LUT_EN, 0x00);
- }
-
- reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE);
- reg_w(sd, R51x_SYS_RESET, 0);
-}
-
-/* Sets up the OV518/OV518+ with the given image parameters
- *
- * OV518 needs a completely different approach, until we can figure out what
- * the individual registers do. Also, only 15 FPS is supported now.
- *
- * Do not put any sensor-specific code in here (including I2C I/O functions)
- */
-static void ov518_mode_init_regs(struct sd *sd)
-{
- int hsegs, vsegs, packet_size;
- struct usb_host_interface *alt;
- struct usb_interface *intf;
-
- intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
- alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
- if (!alt) {
- pr_err("Couldn't get altsetting\n");
- sd->gspca_dev.usb_err = -EIO;
- return;
- }
-
- packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
- ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2);
-
- /******** Set the mode ********/
- reg_w(sd, 0x2b, 0);
- reg_w(sd, 0x2c, 0);
- reg_w(sd, 0x2d, 0);
- reg_w(sd, 0x2e, 0);
- reg_w(sd, 0x3b, 0);
- reg_w(sd, 0x3c, 0);
- reg_w(sd, 0x3d, 0);
- reg_w(sd, 0x3e, 0);
-
- if (sd->bridge == BRIDGE_OV518) {
- /* Set 8-bit (YVYU) input format */
- reg_w_mask(sd, 0x20, 0x08, 0x08);
-
- /* Set 12-bit (4:2:0) output format */
- reg_w_mask(sd, 0x28, 0x80, 0xf0);
- reg_w_mask(sd, 0x38, 0x80, 0xf0);
- } else {
- reg_w(sd, 0x28, 0x80);
- reg_w(sd, 0x38, 0x80);
- }
-
- hsegs = sd->gspca_dev.width / 16;
- vsegs = sd->gspca_dev.height / 4;
-
- reg_w(sd, 0x29, hsegs);
- reg_w(sd, 0x2a, vsegs);
-
- reg_w(sd, 0x39, hsegs);
- reg_w(sd, 0x3a, vsegs);
-
- /* Windows driver does this here; who knows why */
- reg_w(sd, 0x2f, 0x80);
-
- /******** Set the framerate ********/
- sd->clockdiv = 1;
-
- /* Mode independent, but framerate dependent, regs */
- /* 0x51: Clock divider; Only works on some cams which use 2 crystals */
- reg_w(sd, 0x51, 0x04);
- reg_w(sd, 0x22, 0x18);
- reg_w(sd, 0x23, 0xff);
-
- if (sd->bridge == BRIDGE_OV518PLUS) {
- switch (sd->sensor) {
- case SEN_OV7620AE:
- if (sd->gspca_dev.width == 320) {
- reg_w(sd, 0x20, 0x00);
- reg_w(sd, 0x21, 0x19);
- } else {
- reg_w(sd, 0x20, 0x60);
- reg_w(sd, 0x21, 0x1f);
- }
- break;
- case SEN_OV7620:
- reg_w(sd, 0x20, 0x00);
- reg_w(sd, 0x21, 0x19);
- break;
- default:
- reg_w(sd, 0x21, 0x19);
- }
- } else
- reg_w(sd, 0x71, 0x17); /* Compression-related? */
-
- /* FIXME: Sensor-specific */
- /* Bit 5 is what matters here. Of course, it is "reserved" */
- i2c_w(sd, 0x54, 0x23);
-
- reg_w(sd, 0x2f, 0x80);
-
- if (sd->bridge == BRIDGE_OV518PLUS) {
- reg_w(sd, 0x24, 0x94);
- reg_w(sd, 0x25, 0x90);
- ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
- ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */
- ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */
- ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */
- ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */
- ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */
- ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */
- ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */
- ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */
- } else {
- reg_w(sd, 0x24, 0x9f);
- reg_w(sd, 0x25, 0x90);
- ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */
- ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */
- ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */
- ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */
- ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */
- ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */
- ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */
- ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */
- ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */
- }
-
- reg_w(sd, 0x2f, 0x80);
-}
-
-/* Sets up the OV519 with the given image parameters
- *
- * OV519 needs a completely different approach, until we can figure out what
- * the individual registers do.
- *
- * Do not put any sensor-specific code in here (including I2C I/O functions)
- */
-static void ov519_mode_init_regs(struct sd *sd)
-{
- static const struct ov_regvals mode_init_519_ov7670[] = {
- { 0x5d, 0x03 }, /* Turn off suspend mode */
- { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
- { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
- { 0xa2, 0x20 }, /* a2-a5 are undocumented */
- { 0xa3, 0x18 },
- { 0xa4, 0x04 },
- { 0xa5, 0x28 },
- { 0x37, 0x00 }, /* SetUsbInit */
- { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
- /* Enable both fields, YUV Input, disable defect comp (why?) */
- { 0x20, 0x0c },
- { 0x21, 0x38 },
- { 0x22, 0x1d },
- { 0x17, 0x50 }, /* undocumented */
- { 0x37, 0x00 }, /* undocumented */
- { 0x40, 0xff }, /* I2C timeout counter */
- { 0x46, 0x00 }, /* I2C clock prescaler */
- { 0x59, 0x04 }, /* new from windrv 090403 */
- { 0xff, 0x00 }, /* undocumented */
- /* windows reads 0x55 at this point, why? */
- };
-
- static const struct ov_regvals mode_init_519[] = {
- { 0x5d, 0x03 }, /* Turn off suspend mode */
- { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */
- { OV519_R54_EN_CLK1, 0x0f }, /* bit2 (jpeg enable) */
- { 0xa2, 0x20 }, /* a2-a5 are undocumented */
- { 0xa3, 0x18 },
- { 0xa4, 0x04 },
- { 0xa5, 0x28 },
- { 0x37, 0x00 }, /* SetUsbInit */
- { 0x55, 0x02 }, /* 4.096 Mhz audio clock */
- /* Enable both fields, YUV Input, disable defect comp (why?) */
- { 0x22, 0x1d },
- { 0x17, 0x50 }, /* undocumented */
- { 0x37, 0x00 }, /* undocumented */
- { 0x40, 0xff }, /* I2C timeout counter */
- { 0x46, 0x00 }, /* I2C clock prescaler */
- { 0x59, 0x04 }, /* new from windrv 090403 */
- { 0xff, 0x00 }, /* undocumented */
- /* windows reads 0x55 at this point, why? */
- };
-
- /******** Set the mode ********/
- switch (sd->sensor) {
- default:
- write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519));
- if (sd->sensor == SEN_OV7640 ||
- sd->sensor == SEN_OV7648) {
- /* Select 8-bit input mode */
- reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
- }
- break;
- case SEN_OV7660:
- return; /* done by ov519_set_mode/fr() */
- case SEN_OV7670:
- write_regvals(sd, mode_init_519_ov7670,
- ARRAY_SIZE(mode_init_519_ov7670));
- break;
- }
-
- reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4);
- reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3);
- if (sd->sensor == SEN_OV7670 &&
- sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
- reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
- else if (sd->sensor == SEN_OV7648 &&
- sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
- reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
- else
- reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
- reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
- reg_w(sd, OV519_R14_Y_OFFSETL, 0x00);
- reg_w(sd, OV519_R15_Y_OFFSETH, 0x00);
- reg_w(sd, OV519_R16_DIVIDER, 0x00);
- reg_w(sd, OV519_R25_FORMAT, 0x03); /* YUV422 */
- reg_w(sd, 0x26, 0x00); /* Undocumented */
-
- /******** Set the framerate ********/
- if (frame_rate > 0)
- sd->frame_rate = frame_rate;
-
-/* FIXME: These are only valid at the max resolution. */
- sd->clockdiv = 0;
- switch (sd->sensor) {
- case SEN_OV7640:
- case SEN_OV7648:
- switch (sd->frame_rate) {
- default:
-/* case 30: */
- reg_w(sd, 0xa4, 0x0c);
- reg_w(sd, 0x23, 0xff);
- break;
- case 25:
- reg_w(sd, 0xa4, 0x0c);
- reg_w(sd, 0x23, 0x1f);
- break;
- case 20:
- reg_w(sd, 0xa4, 0x0c);
- reg_w(sd, 0x23, 0x1b);
- break;
- case 15:
- reg_w(sd, 0xa4, 0x04);
- reg_w(sd, 0x23, 0xff);
- sd->clockdiv = 1;
- break;
- case 10:
- reg_w(sd, 0xa4, 0x04);
- reg_w(sd, 0x23, 0x1f);
- sd->clockdiv = 1;
- break;
- case 5:
- reg_w(sd, 0xa4, 0x04);
- reg_w(sd, 0x23, 0x1b);
- sd->clockdiv = 1;
- break;
- }
- break;
- case SEN_OV8610:
- switch (sd->frame_rate) {
- default: /* 15 fps */
-/* case 15: */
- reg_w(sd, 0xa4, 0x06);
- reg_w(sd, 0x23, 0xff);
- break;
- case 10:
- reg_w(sd, 0xa4, 0x06);
- reg_w(sd, 0x23, 0x1f);
- break;
- case 5:
- reg_w(sd, 0xa4, 0x06);
- reg_w(sd, 0x23, 0x1b);
- break;
- }
- break;
- case SEN_OV7670: /* guesses, based on 7640 */
- PDEBUG(D_STREAM, "Setting framerate to %d fps",
- (sd->frame_rate == 0) ? 15 : sd->frame_rate);
- reg_w(sd, 0xa4, 0x10);
- switch (sd->frame_rate) {
- case 30:
- reg_w(sd, 0x23, 0xff);
- break;
- case 20:
- reg_w(sd, 0x23, 0x1b);
- break;
- default:
-/* case 15: */
- reg_w(sd, 0x23, 0xff);
- sd->clockdiv = 1;
- break;
- }
- break;
- }
-}
-
-static void mode_init_ov_sensor_regs(struct sd *sd)
-{
- struct gspca_dev *gspca_dev;
- int qvga, xstart, xend, ystart, yend;
- u8 v;
-
- gspca_dev = &sd->gspca_dev;
- qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
-
- /******** Mode (VGA/QVGA) and sensor specific regs ********/
- switch (sd->sensor) {
- case SEN_OV2610:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
- i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
- i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
- i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
- 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);
- ystart = (776 - gspca_dev->height) / 2;
- } else {
- xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
- ystart = (1544 - gspca_dev->height) / 2;
- }
- xend = xstart + gspca_dev->width;
- yend = ystart + gspca_dev->height;
- /* Writing to the COMH register resets the other windowing regs
- to their default values, so we must do this first. */
- i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
- i2c_w_mask(sd, 0x32,
- (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
- 0x3f);
- i2c_w_mask(sd, 0x03,
- (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
- 0x0f);
- i2c_w(sd, 0x17, xstart >> 4);
- i2c_w(sd, 0x18, xend >> 4);
- i2c_w(sd, 0x19, ystart >> 3);
- i2c_w(sd, 0x1a, yend >> 3);
- return;
- case SEN_OV8610:
- /* For OV8610 qvga means qsvga */
- i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
- i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
- i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
- i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
- i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
- break;
- case SEN_OV7610:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
- i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
- i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- case SEN_OV76BE:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
- i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
- i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
- i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
- i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
- i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
- i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
- if (sd->sensor == SEN_OV76BE)
- i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
- break;
- case SEN_OV7640:
- case SEN_OV7648:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
- /* Setting this undocumented bit in qvga mode removes a very
- annoying vertical shaking of the image */
- i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
- /* Unknown */
- i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
- /* Allow higher automatic gain (to allow higher framerates) */
- i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
- break;
- case SEN_OV7670:
- /* set COM7_FMT_VGA or COM7_FMT_QVGA
- * do we need to set anything else?
- * HSTART etc are set in set_ov_sensor_window itself */
- i2c_w_mask(sd, OV7670_R12_COM7,
- qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
- OV7670_COM7_FMT_MASK);
- i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
- i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_AWB,
- OV7670_COM8_AWB);
- if (qvga) { /* QVGA from ov7670.c by
- * Jonathan Corbet */
- xstart = 164;
- xend = 28;
- ystart = 14;
- yend = 494;
- } else { /* VGA */
- xstart = 158;
- xend = 14;
- ystart = 10;
- yend = 490;
- }
- /* OV7670 hardware window registers are split across
- * multiple locations */
- i2c_w(sd, OV7670_R17_HSTART, xstart >> 3);
- i2c_w(sd, OV7670_R18_HSTOP, xend >> 3);
- v = i2c_r(sd, OV7670_R32_HREF);
- v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
- msleep(10); /* need to sleep between read and write to
- * same reg! */
- i2c_w(sd, OV7670_R32_HREF, v);
-
- i2c_w(sd, OV7670_R19_VSTART, ystart >> 2);
- i2c_w(sd, OV7670_R1A_VSTOP, yend >> 2);
- v = i2c_r(sd, OV7670_R03_VREF);
- v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
- msleep(10); /* need to sleep between read and write to
- * same reg! */
- i2c_w(sd, OV7670_R03_VREF, v);
- break;
- case SEN_OV6620:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
- i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
- break;
- case SEN_OV6630:
- case SEN_OV66308AF:
- i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
- i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
- break;
- case SEN_OV9600: {
- const struct ov_i2c_regvals *vals;
- static const struct ov_i2c_regvals sxga_15[] = {
- {0x11, 0x80}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
- };
- static const struct ov_i2c_regvals sxga_7_5[] = {
- {0x11, 0x81}, {0x14, 0x3e}, {0x24, 0x85}, {0x25, 0x75}
- };
- static const struct ov_i2c_regvals vga_30[] = {
- {0x11, 0x81}, {0x14, 0x7e}, {0x24, 0x70}, {0x25, 0x60}
- };
- static const struct ov_i2c_regvals vga_15[] = {
- {0x11, 0x83}, {0x14, 0x3e}, {0x24, 0x80}, {0x25, 0x70}
- };
-
- /* frame rates:
- * 15fps / 7.5 fps for 1280x1024
- * 30fps / 15fps for 640x480
- */
- i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0x40);
- if (qvga)
- vals = sd->frame_rate < 30 ? vga_15 : vga_30;
- else
- vals = sd->frame_rate < 15 ? sxga_7_5 : sxga_15;
- write_i2c_regvals(sd, vals, ARRAY_SIZE(sxga_15));
- return;
- }
- default:
- return;
- }
-
- /******** Clock programming ********/
- i2c_w(sd, 0x11, sd->clockdiv);
-}
-
-/* this function works for bridge ov519 and sensors ov7660 and ov7670 only */
-static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->gspca_dev.streaming)
- reg_w(sd, OV519_R51_RESET1, 0x0f); /* block stream */
- i2c_w_mask(sd, OV7670_R1E_MVFP,
- OV7670_MVFP_MIRROR * hflip | OV7670_MVFP_VFLIP * vflip,
- OV7670_MVFP_MIRROR | OV7670_MVFP_VFLIP);
- if (sd->gspca_dev.streaming)
- reg_w(sd, OV519_R51_RESET1, 0x00); /* restart stream */
-}
-
-static void set_ov_sensor_window(struct sd *sd)
-{
- struct gspca_dev *gspca_dev;
- int qvga, crop;
- int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
-
- /* 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:
- case SEN_OV9600:
- mode_init_ov_sensor_regs(sd);
- return;
- case SEN_OV7660:
- ov519_set_mode(sd);
- ov519_set_fr(sd);
- return;
- }
-
- gspca_dev = &sd->gspca_dev;
- qvga = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 1;
- crop = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv & 2;
-
- /* The different sensor ICs handle setting up of window differently.
- * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */
- switch (sd->sensor) {
- case SEN_OV8610:
- hwsbase = 0x1e;
- hwebase = 0x1e;
- vwsbase = 0x02;
- vwebase = 0x02;
- break;
- case SEN_OV7610:
- case SEN_OV76BE:
- hwsbase = 0x38;
- hwebase = 0x3a;
- vwsbase = vwebase = 0x05;
- break;
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV66308AF:
- hwsbase = 0x38;
- hwebase = 0x3a;
- vwsbase = 0x05;
- vwebase = 0x06;
- if (sd->sensor == SEN_OV66308AF && qvga)
- /* HDG: this fixes U and V getting swapped */
- hwsbase++;
- if (crop) {
- hwsbase += 8;
- hwebase += 8;
- vwsbase += 11;
- vwebase += 11;
- }
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
- hwebase = 0x2f;
- vwsbase = vwebase = 0x05;
- break;
- case SEN_OV7640:
- case SEN_OV7648:
- hwsbase = 0x1a;
- hwebase = 0x1a;
- vwsbase = vwebase = 0x03;
- break;
- default:
- return;
- }
-
- switch (sd->sensor) {
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV66308AF:
- if (qvga) { /* QCIF */
- hwscale = 0;
- vwscale = 0;
- } else { /* CIF */
- hwscale = 1;
- vwscale = 1; /* The datasheet says 0;
- * it's wrong */
- }
- break;
- case SEN_OV8610:
- if (qvga) { /* QSVGA */
- hwscale = 1;
- vwscale = 1;
- } else { /* SVGA */
- hwscale = 2;
- vwscale = 2;
- }
- break;
- default: /* SEN_OV7xx0 */
- if (qvga) { /* QVGA */
- hwscale = 1;
- vwscale = 0;
- } else { /* VGA */
- hwscale = 2;
- vwscale = 1;
- }
- }
-
- mode_init_ov_sensor_regs(sd);
-
- i2c_w(sd, 0x17, hwsbase);
- i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
- i2c_w(sd, 0x19, vwsbase);
- i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
-}
-
-/* -- start the camera -- */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* Default for most bridges, allow bridge_mode_init_regs to override */
- sd->sensor_width = sd->gspca_dev.width;
- sd->sensor_height = sd->gspca_dev.height;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- ov511_mode_init_regs(sd);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- ov518_mode_init_regs(sd);
- break;
- case BRIDGE_OV519:
- ov519_mode_init_regs(sd);
- break;
- /* case BRIDGE_OVFX2: nothing to do */
- case BRIDGE_W9968CF:
- w9968cf_mode_init_regs(sd);
- break;
- }
-
- set_ov_sensor_window(sd);
-
- /* Force clear snapshot state in case the snapshot button was
- pressed while we weren't streaming */
- sd->snapshot_needs_reset = 1;
- sd_reset_snapshot(gspca_dev);
-
- sd->first_frame = 3;
-
- ov51x_restart(sd);
- ov51x_led_control(sd, 1);
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- ov51x_stop(sd);
- ov51x_led_control(sd, 0);
-}
-
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!sd->gspca_dev.present)
- return;
- if (sd->bridge == BRIDGE_W9968CF)
- w9968cf_stop0(sd);
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- /* If the last button state is pressed, release it now! */
- if (sd->snapshot_pressed) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- sd->snapshot_pressed = 0;
- }
-#endif
- if (sd->bridge == BRIDGE_OV519)
- reg_w(sd, OV519_R57_SNAPSHOT, 0x23);
-}
-
-static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->snapshot_pressed != state) {
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, state);
- input_sync(gspca_dev->input_dev);
-#endif
- if (state)
- sd->snapshot_needs_reset = 1;
-
- sd->snapshot_pressed = state;
- } else {
- /* On the ov511 / ov519 we need to reset the button state
- multiple times, as resetting does not work as long as the
- button stays pressed */
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- case BRIDGE_OV519:
- if (state)
- sd->snapshot_needs_reset = 1;
- break;
- }
- }
-}
-
-static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *in, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
- * byte non-zero. The EOF packet has image width/height in the
- * 10th and 11th bytes. The 9th byte is given as follows:
- *
- * bit 7: EOF
- * 6: compression enabled
- * 5: 422/420/400 modes
- * 4: 422/420/400 modes
- * 3: 1
- * 2: snapshot button on
- * 1: snapshot frame
- * 0: even/odd field
- */
- if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) &&
- (in[8] & 0x08)) {
- ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1);
- if (in[8] & 0x80) {
- /* Frame end */
- if ((in[9] + 1) * 8 != gspca_dev->width ||
- (in[10] + 1) * 8 != gspca_dev->height) {
- PDEBUG(D_ERR, "Invalid frame size, got: %dx%d,"
- " requested: %dx%d\n",
- (in[9] + 1) * 8, (in[10] + 1) * 8,
- gspca_dev->width, gspca_dev->height);
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- /* Add 11 byte footer to frame, might be useful */
- gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
- return;
- } else {
- /* Frame start */
- gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
- sd->packet_nr = 0;
- }
- }
-
- /* Ignore the packet number */
- len--;
-
- /* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
-}
-
-static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* A false positive here is likely, until OVT gives me
- * the definitive SOF/EOF format */
- if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
- ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1);
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
- sd->packet_nr = 0;
- }
-
- if (gspca_dev->last_packet_type == DISCARD_PACKET)
- return;
-
- /* Does this device use packet numbers ? */
- if (len & 7) {
- len--;
- if (sd->packet_nr == data[len])
- sd->packet_nr++;
- /* The last few packets of the frame (which are all 0's
- except that they may contain part of the footer), are
- numbered 0 */
- else if (sd->packet_nr == 0 || data[len]) {
- PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)",
- (int)data[len], (int)sd->packet_nr);
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- }
-
- /* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- /* Header of ov519 is 16 bytes:
- * Byte Value Description
- * 0 0xff magic
- * 1 0xff magic
- * 2 0xff magic
- * 3 0xXX 0x50 = SOF, 0x51 = EOF
- * 9 0xXX 0x01 initial frame without data,
- * 0x00 standard frame with image
- * 14 Lo in EOF: length of image data / 8
- * 15 Hi
- */
-
- if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) {
- switch (data[3]) {
- case 0x50: /* start of frame */
- /* Don't check the button state here, as the state
- usually (always ?) changes at EOF and checking it
- here leads to unnecessary snapshot state resets. */
-#define HDRSZ 16
- data += HDRSZ;
- len -= HDRSZ;
-#undef HDRSZ
- if (data[0] == 0xff || data[1] == 0xd8)
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- data, len);
- else
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- case 0x51: /* end of frame */
- ov51x_handle_button(gspca_dev, data[11] & 1);
- if (data[9] != 0)
- gspca_dev->last_packet_type = DISCARD_PACKET;
- gspca_frame_add(gspca_dev, LAST_PACKET,
- NULL, 0);
- return;
- }
- }
-
- /* intermediate packet */
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-
- /* A short read signals EOF */
- if (len < gspca_dev->cam.bulk_size) {
- /* If the frame is short, and it is one of the first ones
- the sensor and bridge are still syncing, so drop it. */
- if (sd->first_frame) {
- sd->first_frame--;
- if (gspca_dev->image_len <
- sd->gspca_dev.width * sd->gspca_dev.height)
- gspca_dev->last_packet_type = DISCARD_PACKET;
- }
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
- }
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->bridge) {
- case BRIDGE_OV511:
- case BRIDGE_OV511PLUS:
- ov511_pkt_scan(gspca_dev, data, len);
- break;
- case BRIDGE_OV518:
- case BRIDGE_OV518PLUS:
- ov518_pkt_scan(gspca_dev, data, len);
- break;
- case BRIDGE_OV519:
- ov519_pkt_scan(gspca_dev, data, len);
- break;
- case BRIDGE_OVFX2:
- ovfx2_pkt_scan(gspca_dev, data, len);
- break;
- case BRIDGE_W9968CF:
- w9968cf_pkt_scan(gspca_dev, data, len);
- break;
- }
-}
-
-/* -- management routines -- */
-
-static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct ov_i2c_regvals brit_7660[][7] = {
- {{0x0f, 0x6a}, {0x24, 0x40}, {0x25, 0x2b}, {0x26, 0x90},
- {0x27, 0xe0}, {0x28, 0xe0}, {0x2c, 0xe0}},
- {{0x0f, 0x6a}, {0x24, 0x50}, {0x25, 0x40}, {0x26, 0xa1},
- {0x27, 0xc0}, {0x28, 0xc0}, {0x2c, 0xc0}},
- {{0x0f, 0x6a}, {0x24, 0x68}, {0x25, 0x58}, {0x26, 0xc2},
- {0x27, 0xa0}, {0x28, 0xa0}, {0x2c, 0xa0}},
- {{0x0f, 0x6a}, {0x24, 0x70}, {0x25, 0x68}, {0x26, 0xd3},
- {0x27, 0x80}, {0x28, 0x80}, {0x2c, 0x80}},
- {{0x0f, 0x6a}, {0x24, 0x80}, {0x25, 0x70}, {0x26, 0xd3},
- {0x27, 0x20}, {0x28, 0x20}, {0x2c, 0x20}},
- {{0x0f, 0x6a}, {0x24, 0x88}, {0x25, 0x78}, {0x26, 0xd3},
- {0x27, 0x40}, {0x28, 0x40}, {0x2c, 0x40}},
- {{0x0f, 0x6a}, {0x24, 0x90}, {0x25, 0x80}, {0x26, 0xd4},
- {0x27, 0x60}, {0x28, 0x60}, {0x2c, 0x60}}
- };
-
- switch (sd->sensor) {
- case SEN_OV8610:
- case SEN_OV7610:
- case SEN_OV76BE:
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV66308AF:
- case SEN_OV7640:
- case SEN_OV7648:
- i2c_w(sd, OV7610_REG_BRT, val);
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- i2c_w(sd, OV7610_REG_BRT, val);
- break;
- case SEN_OV7660:
- write_i2c_regvals(sd, brit_7660[val],
- ARRAY_SIZE(brit_7660[0]));
- break;
- case SEN_OV7670:
-/*win trace
- * i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_AEC); */
- i2c_w(sd, OV7670_R55_BRIGHT, ov7670_abs_to_sm(val));
- break;
- }
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct ov_i2c_regvals contrast_7660[][31] = {
- {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0xa0},
- {0x70, 0x58}, {0x71, 0x38}, {0x72, 0x30}, {0x73, 0x30},
- {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x24}, {0x77, 0x24},
- {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x34},
- {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x65},
- {0x80, 0x70}, {0x81, 0x77}, {0x82, 0x7d}, {0x83, 0x83},
- {0x84, 0x88}, {0x85, 0x8d}, {0x86, 0x96}, {0x87, 0x9f},
- {0x88, 0xb0}, {0x89, 0xc4}, {0x8a, 0xd9}},
- {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf8}, {0x6f, 0x94},
- {0x70, 0x58}, {0x71, 0x40}, {0x72, 0x30}, {0x73, 0x30},
- {0x74, 0x30}, {0x75, 0x30}, {0x76, 0x2c}, {0x77, 0x24},
- {0x78, 0x22}, {0x79, 0x28}, {0x7a, 0x2a}, {0x7b, 0x31},
- {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3d}, {0x7f, 0x62},
- {0x80, 0x6d}, {0x81, 0x75}, {0x82, 0x7b}, {0x83, 0x81},
- {0x84, 0x87}, {0x85, 0x8d}, {0x86, 0x98}, {0x87, 0xa1},
- {0x88, 0xb2}, {0x89, 0xc6}, {0x8a, 0xdb}},
- {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x84},
- {0x70, 0x58}, {0x71, 0x48}, {0x72, 0x40}, {0x73, 0x40},
- {0x74, 0x28}, {0x75, 0x28}, {0x76, 0x28}, {0x77, 0x24},
- {0x78, 0x26}, {0x79, 0x28}, {0x7a, 0x28}, {0x7b, 0x34},
- {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x5d},
- {0x80, 0x68}, {0x81, 0x71}, {0x82, 0x79}, {0x83, 0x81},
- {0x84, 0x86}, {0x85, 0x8b}, {0x86, 0x95}, {0x87, 0x9e},
- {0x88, 0xb1}, {0x89, 0xc5}, {0x8a, 0xd9}},
- {{0x6c, 0xf0}, {0x6d, 0xf0}, {0x6e, 0xf0}, {0x6f, 0x70},
- {0x70, 0x58}, {0x71, 0x58}, {0x72, 0x48}, {0x73, 0x48},
- {0x74, 0x38}, {0x75, 0x40}, {0x76, 0x34}, {0x77, 0x34},
- {0x78, 0x2e}, {0x79, 0x28}, {0x7a, 0x24}, {0x7b, 0x22},
- {0x7c, 0x0f}, {0x7d, 0x1e}, {0x7e, 0x3c}, {0x7f, 0x58},
- {0x80, 0x63}, {0x81, 0x6e}, {0x82, 0x77}, {0x83, 0x80},
- {0x84, 0x87}, {0x85, 0x8f}, {0x86, 0x9c}, {0x87, 0xa9},
- {0x88, 0xc0}, {0x89, 0xd4}, {0x8a, 0xe6}},
- {{0x6c, 0xa0}, {0x6d, 0xf0}, {0x6e, 0x90}, {0x6f, 0x80},
- {0x70, 0x70}, {0x71, 0x80}, {0x72, 0x60}, {0x73, 0x60},
- {0x74, 0x58}, {0x75, 0x60}, {0x76, 0x4c}, {0x77, 0x38},
- {0x78, 0x38}, {0x79, 0x2a}, {0x7a, 0x20}, {0x7b, 0x0e},
- {0x7c, 0x0a}, {0x7d, 0x14}, {0x7e, 0x26}, {0x7f, 0x46},
- {0x80, 0x54}, {0x81, 0x64}, {0x82, 0x70}, {0x83, 0x7c},
- {0x84, 0x87}, {0x85, 0x93}, {0x86, 0xa6}, {0x87, 0xb4},
- {0x88, 0xd0}, {0x89, 0xe5}, {0x8a, 0xf5}},
- {{0x6c, 0x60}, {0x6d, 0x80}, {0x6e, 0x60}, {0x6f, 0x80},
- {0x70, 0x80}, {0x71, 0x80}, {0x72, 0x88}, {0x73, 0x30},
- {0x74, 0x70}, {0x75, 0x68}, {0x76, 0x64}, {0x77, 0x50},
- {0x78, 0x3c}, {0x79, 0x22}, {0x7a, 0x10}, {0x7b, 0x08},
- {0x7c, 0x06}, {0x7d, 0x0e}, {0x7e, 0x1a}, {0x7f, 0x3a},
- {0x80, 0x4a}, {0x81, 0x5a}, {0x82, 0x6b}, {0x83, 0x7b},
- {0x84, 0x89}, {0x85, 0x96}, {0x86, 0xaf}, {0x87, 0xc3},
- {0x88, 0xe1}, {0x89, 0xf2}, {0x8a, 0xfa}},
- {{0x6c, 0x20}, {0x6d, 0x40}, {0x6e, 0x20}, {0x6f, 0x60},
- {0x70, 0x88}, {0x71, 0xc8}, {0x72, 0xc0}, {0x73, 0xb8},
- {0x74, 0xa8}, {0x75, 0xb8}, {0x76, 0x80}, {0x77, 0x5c},
- {0x78, 0x26}, {0x79, 0x10}, {0x7a, 0x08}, {0x7b, 0x04},
- {0x7c, 0x02}, {0x7d, 0x06}, {0x7e, 0x0a}, {0x7f, 0x22},
- {0x80, 0x33}, {0x81, 0x4c}, {0x82, 0x64}, {0x83, 0x7b},
- {0x84, 0x90}, {0x85, 0xa7}, {0x86, 0xc7}, {0x87, 0xde},
- {0x88, 0xf1}, {0x89, 0xf9}, {0x8a, 0xfd}},
- };
-
- switch (sd->sensor) {
- case SEN_OV7610:
- case SEN_OV6620:
- i2c_w(sd, OV7610_REG_CNT, val);
- break;
- case SEN_OV6630:
- case SEN_OV66308AF:
- i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f);
- break;
- case SEN_OV8610: {
- static const u8 ctab[] = {
- 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f
- };
-
- /* Use Y gamma control instead. Bit 0 enables it. */
- i2c_w(sd, 0x64, ctab[val >> 5]);
- break;
- }
- case SEN_OV7620:
- case SEN_OV7620AE: {
- static const u8 ctab[] = {
- 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
- 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
- };
-
- /* Use Y gamma control instead. Bit 0 enables it. */
- i2c_w(sd, 0x64, ctab[val >> 4]);
- break;
- }
- case SEN_OV7660:
- write_i2c_regvals(sd, contrast_7660[val],
- ARRAY_SIZE(contrast_7660[0]));
- break;
- case SEN_OV7670:
- /* check that this isn't just the same as ov7610 */
- i2c_w(sd, OV7670_R56_CONTRAS, val >> 1);
- break;
- }
-}
-
-static void setexposure(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w(sd, 0x10, val);
-}
-
-static void setcolors(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct ov_i2c_regvals colors_7660[][6] = {
- {{0x4f, 0x28}, {0x50, 0x2a}, {0x51, 0x02}, {0x52, 0x0a},
- {0x53, 0x19}, {0x54, 0x23}},
- {{0x4f, 0x47}, {0x50, 0x4a}, {0x51, 0x03}, {0x52, 0x11},
- {0x53, 0x2c}, {0x54, 0x3e}},
- {{0x4f, 0x66}, {0x50, 0x6b}, {0x51, 0x05}, {0x52, 0x19},
- {0x53, 0x40}, {0x54, 0x59}},
- {{0x4f, 0x84}, {0x50, 0x8b}, {0x51, 0x06}, {0x52, 0x20},
- {0x53, 0x53}, {0x54, 0x73}},
- {{0x4f, 0xa3}, {0x50, 0xab}, {0x51, 0x08}, {0x52, 0x28},
- {0x53, 0x66}, {0x54, 0x8e}},
- };
-
- switch (sd->sensor) {
- case SEN_OV8610:
- case SEN_OV7610:
- case SEN_OV76BE:
- case SEN_OV6620:
- case SEN_OV6630:
- case SEN_OV66308AF:
- i2c_w(sd, OV7610_REG_SAT, val);
- break;
- case SEN_OV7620:
- case SEN_OV7620AE:
- /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
-/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
- if (rc < 0)
- goto out; */
- i2c_w(sd, OV7610_REG_SAT, val);
- break;
- case SEN_OV7640:
- case SEN_OV7648:
- i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
- break;
- case SEN_OV7660:
- write_i2c_regvals(sd, colors_7660[val],
- ARRAY_SIZE(colors_7660[0]));
- break;
- case SEN_OV7670:
- /* supported later once I work out how to do it
- * transparently fail now! */
- /* set REG_COM13 values for UV sat auto mode */
- break;
- }
-}
-
-static void setautobright(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w_mask(sd, 0x2d, val ? 0x10 : 0x00, 0x10);
-}
-
-static void setfreq_i(struct sd *sd, s32 val)
-{
- if (sd->sensor == SEN_OV7660
- || sd->sensor == SEN_OV7670) {
- switch (val) {
- case 0: /* Banding filter disabled */
- i2c_w_mask(sd, OV7670_R13_COM8, 0, OV7670_COM8_BFILT);
- break;
- case 1: /* 50 hz */
- i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
- OV7670_COM8_BFILT);
- i2c_w_mask(sd, OV7670_R3B_COM11, 0x08, 0x18);
- break;
- case 2: /* 60 hz */
- i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
- OV7670_COM8_BFILT);
- i2c_w_mask(sd, OV7670_R3B_COM11, 0x00, 0x18);
- break;
- case 3: /* Auto hz - ov7670 only */
- i2c_w_mask(sd, OV7670_R13_COM8, OV7670_COM8_BFILT,
- OV7670_COM8_BFILT);
- i2c_w_mask(sd, OV7670_R3B_COM11, OV7670_COM11_HZAUTO,
- 0x18);
- break;
- }
- } else {
- switch (val) {
- case 0: /* Banding filter disabled */
- i2c_w_mask(sd, 0x2d, 0x00, 0x04);
- i2c_w_mask(sd, 0x2a, 0x00, 0x80);
- break;
- case 1: /* 50 hz (filter on and framerate adj) */
- i2c_w_mask(sd, 0x2d, 0x04, 0x04);
- i2c_w_mask(sd, 0x2a, 0x80, 0x80);
- /* 20 fps -> 16.667 fps */
- if (sd->sensor == SEN_OV6620 ||
- sd->sensor == SEN_OV6630 ||
- sd->sensor == SEN_OV66308AF)
- i2c_w(sd, 0x2b, 0x5e);
- else
- i2c_w(sd, 0x2b, 0xac);
- break;
- case 2: /* 60 hz (filter on, ...) */
- i2c_w_mask(sd, 0x2d, 0x04, 0x04);
- if (sd->sensor == SEN_OV6620 ||
- sd->sensor == SEN_OV6630 ||
- sd->sensor == SEN_OV66308AF) {
- /* 20 fps -> 15 fps */
- i2c_w_mask(sd, 0x2a, 0x80, 0x80);
- i2c_w(sd, 0x2b, 0xa8);
- } else {
- /* no framerate adj. */
- i2c_w_mask(sd, 0x2a, 0x00, 0x80);
- }
- break;
- }
- }
-}
-
-static void setfreq(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- setfreq_i(sd, val);
-
- /* Ugly but necessary */
- if (sd->bridge == BRIDGE_W9968CF)
- w9968cf_set_crop_window(sd);
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->bridge != BRIDGE_W9968CF)
- return -ENOTTY;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
- V4L2_JPEG_MARKER_DRI;
- return 0;
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->bridge != BRIDGE_W9968CF)
- return -ENOTTY;
-
- v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
- return 0;
-}
-
-static int sd_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- gspca_dev->exposure->val = i2c_r(sd, 0x10);
- break;
- }
- return 0;
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- setbrightness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- setcontrast(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- setfreq(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_AUTOBRIGHTNESS:
- if (ctrl->is_new)
- setautobright(gspca_dev, ctrl->val);
- if (!ctrl->val && sd->brightness->is_new)
- setbrightness(gspca_dev, sd->brightness->val);
- break;
- case V4L2_CID_SATURATION:
- setcolors(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
- break;
- case V4L2_CID_AUTOGAIN:
- if (ctrl->is_new)
- setautogain(gspca_dev, ctrl->val);
- if (!ctrl->val && gspca_dev->exposure->is_new)
- setexposure(gspca_dev, gspca_dev->exposure->val);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- return -EBUSY; /* Should never happen, as we grab the ctrl */
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .g_volatile_ctrl = sd_g_volatile_ctrl,
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 10);
- if (valid_controls[sd->sensor].has_brightness)
- sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0,
- sd->sensor == SEN_OV7660 ? 6 : 255, 1,
- sd->sensor == SEN_OV7660 ? 3 : 127);
- if (valid_controls[sd->sensor].has_contrast) {
- if (sd->sensor == SEN_OV7660)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 6, 1, 3);
- else
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1,
- (sd->sensor == SEN_OV6630 ||
- sd->sensor == SEN_OV66308AF) ? 200 : 127);
- }
- if (valid_controls[sd->sensor].has_sat)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 0,
- sd->sensor == SEN_OV7660 ? 4 : 255, 1,
- sd->sensor == SEN_OV7660 ? 2 : 127);
- if (valid_controls[sd->sensor].has_exposure)
- gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 255, 1, 127);
- if (valid_controls[sd->sensor].has_hvflip) {
- sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- }
- if (valid_controls[sd->sensor].has_autobright)
- sd->autobright = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
- if (valid_controls[sd->sensor].has_autogain)
- gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- if (valid_controls[sd->sensor].has_freq) {
- if (sd->sensor == SEN_OV7670)
- sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
- V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
- else
- sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
- }
- if (sd->bridge == BRIDGE_W9968CF)
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- if (gspca_dev->autogain)
- v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, true);
- if (sd->autobright)
- v4l2_ctrl_auto_cluster(2, &sd->autobright, 0, false);
- if (sd->hflip)
- v4l2_ctrl_cluster(2, &sd->hflip);
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .isoc_init = sd_isoc_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
- .dq_callback = sd_reset_snapshot,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .other_input = 1,
-#endif
-};
-
-/* -- module initialisation -- */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
- {USB_DEVICE(0x041e, 0x4052),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
- {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x045e, 0x028c),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
- {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
- {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
- {USB_DEVICE(0x05a9, 0x0519),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
- {USB_DEVICE(0x05a9, 0x0530),
- .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
- {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
- {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
- {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
- {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
- {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
- {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
- {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
- {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
- {USB_DEVICE(0x8020, 0xef04), .driver_info = BRIDGE_OVFX2 },
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
-
-module_param(frame_rate, int, 0644);
-MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)");
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
deleted file mode 100644
index 4877f7ab3d59..000000000000
--- a/drivers/media/video/gspca/pac7302.c
+++ /dev/null
@@ -1,932 +0,0 @@
-/*
- * Pixart PAC7302 driver
- *
- * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
- * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
- *
- * Separated from Pixart PAC7311 library by Márton Németh
- * Camera button input handling by Márton Németh <nm127@freemail.hu>
- * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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
- */
-
-/*
- * Some documentation about various registers as determined by trial and error.
- *
- * Register page 1:
- *
- * Address Description
- * 0x78 Global control, bit 6 controls the LED (inverted)
- * 0x80 Compression balance, 2 interesting settings:
- * 0x0f Default
- * 0x50 Values >= this switch the camera to a lower compression,
- * using the same table for both luminance and chrominance.
- * This gives a sharper picture. Only usable when running
- * at < 15 fps! Note currently the driver does not use this
- * as the quality gain is small and the generated JPG-s are
- * only understood by v4l-utils >= 0.8.9
- *
- * Register page 3:
- *
- * Address Description
- * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
- * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
- * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
- * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
- * 63 -> ~27 fps, the 2 msb's must always be 1 !!
- * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
- * 1 -> ~30 fps, 2 -> ~20 fps
- * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
- * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
- * 0x10 Gain 0-31
- * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an
- * amplification value of 1 rather then 0 at its lowest setting
- * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
- * 0x80 Another framerate control, best left at 1, moving it from 1 to
- * 2 causes the framerate to become 3/4th of what it was, and
- * also seems to cause pixel averaging, resulting in an effective
- * resolution of 320x240 and thus a much blockier image
- *
- * The registers are accessed in the following functions:
- *
- * Page | Register | Function
- * -----+------------+---------------------------------------------------
- * 0 | 0x0f..0x20 | setcolors()
- * 0 | 0xa2..0xab | setbrightcont()
- * 0 | 0xc5 | setredbalance()
- * 0 | 0xc6 | setwhitebalance()
- * 0 | 0xc7 | setbluebalance()
- * 0 | 0xdc | setbrightcont(), setcolors()
- * 3 | 0x02 | setexposure()
- * 3 | 0x10, 0x12 | setgain()
- * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
- * 3 | 0x21 | sethvflip()
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/input.h>
-#include <media/v4l2-chip-ident.h>
-#include "gspca.h"
-/* Include pac common sof detection functions */
-#include "pac_common.h"
-
-#define PAC7302_GAIN_DEFAULT 15
-#define PAC7302_GAIN_KNEE 42
-#define PAC7302_EXPOSURE_DEFAULT 66 /* 33 ms / 30 fps */
-#define PAC7302_EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
-
-MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
- "Thomas Kaiser thomas@kaiser-linux.li");
-MODULE_DESCRIPTION("Pixart PAC7302");
-MODULE_LICENSE("GPL");
-
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct { /* brightness / contrast cluster */
- struct v4l2_ctrl *brightness;
- struct v4l2_ctrl *contrast;
- };
- struct v4l2_ctrl *saturation;
- struct v4l2_ctrl *white_balance;
- struct v4l2_ctrl *red_balance;
- struct v4l2_ctrl *blue_balance;
- struct { /* flip cluster */
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- };
- u8 flags;
-#define FL_HFLIP 0x01 /* mirrored by default */
-#define FL_VFLIP 0x02 /* vertical flipped by default */
-
- u8 sof_read;
- s8 autogain_ignore_frames;
-
- atomic_t avg_lum;
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
-};
-
-#define LOAD_PAGE3 255
-#define END_OF_SEQUENCE 0
-
-static const u8 init_7302[] = {
-/* index,value */
- 0xff, 0x01, /* page 1 */
- 0x78, 0x00, /* deactivate */
- 0xff, 0x01,
- 0x78, 0x40, /* led off */
-};
-static const u8 start_7302[] = {
-/* index, len, [value]* */
- 0xff, 1, 0x00, /* page 0 */
- 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
- 0x00, 0x00, 0x00, 0x00,
- 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
- 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
- 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
- 0x26, 2, 0xaa, 0xaa,
- 0x2e, 1, 0x31,
- 0x38, 1, 0x01,
- 0x3a, 3, 0x14, 0xff, 0x5a,
- 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
- 0x00, 0x54, 0x11,
- 0x55, 1, 0x00,
- 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
- 0x6b, 1, 0x00,
- 0x6e, 3, 0x08, 0x06, 0x00,
- 0x72, 3, 0x00, 0xff, 0x00,
- 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
- 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
- 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
- 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
- 0xd2, 0xeb,
- 0xaf, 1, 0x02,
- 0xb5, 2, 0x08, 0x08,
- 0xb8, 2, 0x08, 0x88,
- 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
- 0xcc, 1, 0x00,
- 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
- 0xc1, 0xd7, 0xec,
- 0xdc, 1, 0x01,
- 0xff, 1, 0x01, /* page 1 */
- 0x12, 3, 0x02, 0x00, 0x01,
- 0x3e, 2, 0x00, 0x00,
- 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
- 0x7c, 1, 0x00,
- 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
- 0x02, 0x00,
- 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
- 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
- 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
- 0xd8, 1, 0x01,
- 0xdb, 2, 0x00, 0x01,
- 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
- 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
- 0xeb, 1, 0x00,
- 0xff, 1, 0x02, /* page 2 */
- 0x22, 1, 0x00,
- 0xff, 1, 0x03, /* page 3 */
- 0, LOAD_PAGE3, /* load the page 3 */
- 0x11, 1, 0x01,
- 0xff, 1, 0x02, /* page 2 */
- 0x13, 1, 0x00,
- 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
- 0x27, 2, 0x14, 0x0c,
- 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
- 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
- 0x6e, 1, 0x08,
- 0xff, 1, 0x01, /* page 1 */
- 0x78, 1, 0x00,
- 0, END_OF_SEQUENCE /* end of sequence */
-};
-
-#define SKIP 0xaa
-/* page 3 - the value SKIP says skip the index - see reg_w_page() */
-static const u8 page3_7302[] = {
- 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
- 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
- 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
- 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
- 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
- 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
- 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
- SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
- 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
- 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
- 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
- 0x00
-};
-
-static void reg_w_buf(struct gspca_dev *gspca_dev,
- u8 index,
- const u8 *buffer, int len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- memcpy(gspca_dev->usb_buf, buffer, len);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, len,
- 500);
- if (ret < 0) {
- pr_err("reg_w_buf failed i: %02x error %d\n",
- index, ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-
-static void reg_w(struct gspca_dev *gspca_dev,
- u8 index,
- u8 value)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- gspca_dev->usb_buf[0] = value;
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index, gspca_dev->usb_buf, 1,
- 500);
- if (ret < 0) {
- pr_err("reg_w() failed i: %02x v: %02x error %d\n",
- index, value, ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void reg_w_seq(struct gspca_dev *gspca_dev,
- const u8 *seq, int len)
-{
- while (--len >= 0) {
- reg_w(gspca_dev, seq[0], seq[1]);
- seq += 2;
- }
-}
-
-/* load the beginning of a page */
-static void reg_w_page(struct gspca_dev *gspca_dev,
- const u8 *page, int len)
-{
- int index;
- int ret = 0;
-
- if (gspca_dev->usb_err < 0)
- return;
- for (index = 0; index < len; index++) {
- if (page[index] == SKIP) /* skip this index */
- continue;
- gspca_dev->usb_buf[0] = page[index];
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index, gspca_dev->usb_buf, 1,
- 500);
- if (ret < 0) {
- pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
- index, page[index], ret);
- gspca_dev->usb_err = ret;
- break;
- }
- }
-}
-
-/* output a variable sequence */
-static void reg_w_var(struct gspca_dev *gspca_dev,
- const u8 *seq,
- const u8 *page3, unsigned int page3_len)
-{
- int index, len;
-
- for (;;) {
- index = *seq++;
- len = *seq++;
- switch (len) {
- case END_OF_SEQUENCE:
- return;
- case LOAD_PAGE3:
- reg_w_page(gspca_dev, page3, page3_len);
- break;
- default:
-#ifdef GSPCA_DEBUG
- if (len > USB_BUF_SZ) {
- PDEBUG(D_ERR|D_STREAM,
- "Incorrect variable sequence");
- return;
- }
-#endif
- while (len > 0) {
- if (len < 8) {
- reg_w_buf(gspca_dev,
- index, seq, len);
- seq += len;
- break;
- }
- reg_w_buf(gspca_dev, index, seq, 8);
- seq += 8;
- index += 8;
- len -= 8;
- }
- }
- }
- /* not reached */
-}
-
-/* this function is called at probe time for pac7302 */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- cam = &gspca_dev->cam;
-
- cam->cam_mode = vga_mode; /* only 640x480 */
- cam->nmodes = ARRAY_SIZE(vga_mode);
-
- sd->flags = id->driver_info;
- return 0;
-}
-
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, v;
- static const u8 max[10] =
- {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
- 0xd4, 0xec};
- static const u8 delta[10] =
- {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
- 0x11, 0x0b};
-
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- for (i = 0; i < 10; i++) {
- v = max[i];
- v += (sd->brightness->val - sd->brightness->maximum)
- * 150 / sd->brightness->maximum; /* 200 ? */
- v -= delta[i] * sd->contrast->val / sd->contrast->maximum;
- if (v < 0)
- v = 0;
- else if (v > 0xff)
- v = 0xff;
- reg_w(gspca_dev, 0xa2 + i, v);
- }
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
-static void setcolors(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, v;
- static const int a[9] =
- {217, -212, 0, -101, 170, -67, -38, -315, 355};
- static const int b[9] =
- {19, 106, 0, 19, 106, 1, 19, 106, 1};
-
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x11, 0x01);
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- for (i = 0; i < 9; i++) {
- v = a[i] * sd->saturation->val / sd->saturation->maximum;
- v += b[i];
- reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
- reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
- }
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
-static void setwhitebalance(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc6, sd->white_balance->val);
-
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
-static void setredbalance(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc5, sd->red_balance->val);
-
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
-static void setbluebalance(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc7, sd->blue_balance->val);
-
- reg_w(gspca_dev, 0xdc, 0x01);
-}
-
-static void setgain(struct gspca_dev *gspca_dev)
-{
- u8 reg10, reg12;
-
- if (gspca_dev->gain->val < 32) {
- reg10 = gspca_dev->gain->val;
- reg12 = 0;
- } else {
- reg10 = 31;
- reg12 = gspca_dev->gain->val - 31;
- }
-
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x10, reg10);
- reg_w(gspca_dev, 0x12, reg12);
-
- /* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
-}
-
-static void setexposure(struct gspca_dev *gspca_dev)
-{
- u8 clockdiv;
- u16 exposure;
-
- /*
- * Register 2 of frame 3 contains the clock divider configuring the
- * no fps according to the formula: 90 / reg. sd->exposure is the
- * desired exposure time in 0.5 ms.
- */
- clockdiv = (90 * gspca_dev->exposure->val + 1999) / 2000;
-
- /*
- * Note clockdiv = 3 also works, but when running at 30 fps, depending
- * on the scene being recorded, the camera switches to another
- * quantization table for certain JPEG blocks, and we don't know how
- * to decompress these blocks. So we cap the framerate at 15 fps.
- */
- if (clockdiv < 6)
- clockdiv = 6;
- else if (clockdiv > 63)
- clockdiv = 63;
-
- /*
- * Register 2 MUST be a multiple of 3, except when between 6 and 12?
- * Always round up, otherwise we cannot get the desired frametime
- * using the partial frame time exposure control.
- */
- if (clockdiv < 6 || clockdiv > 12)
- clockdiv = ((clockdiv + 2) / 3) * 3;
-
- /*
- * frame exposure time in ms = 1000 * clockdiv / 90 ->
- * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
- */
- exposure = (gspca_dev->exposure->val * 45 * 448) / (1000 * clockdiv);
- /* 0 = use full frametime, 448 = no exposure, reverse it */
- exposure = 448 - exposure;
-
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x02, clockdiv);
- reg_w(gspca_dev, 0x0e, exposure & 0xff);
- reg_w(gspca_dev, 0x0f, exposure >> 8);
-
- /* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
-}
-
-static void sethvflip(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 data, hflip, vflip;
-
- hflip = sd->hflip->val;
- if (sd->flags & FL_HFLIP)
- hflip = !hflip;
- vflip = sd->vflip->val;
- if (sd->flags & FL_VFLIP)
- vflip = !vflip;
-
- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
- reg_w(gspca_dev, 0x21, data);
-
- /* load registers to sensor (Bit 0, auto clear) */
- reg_w(gspca_dev, 0x11, 0x01);
-}
-
-/* this function is called at probe and resume time for pac7302 */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
- return gspca_dev->usb_err;
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
- gspca_dev->exposure->val = PAC7302_EXPOSURE_DEFAULT;
- gspca_dev->gain->val = PAC7302_GAIN_DEFAULT;
- sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
- }
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- setbrightcont(gspca_dev);
- break;
- case V4L2_CID_SATURATION:
- setcolors(gspca_dev);
- break;
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
- setwhitebalance(gspca_dev);
- break;
- case V4L2_CID_RED_BALANCE:
- setredbalance(gspca_dev);
- break;
- case V4L2_CID_BLUE_BALANCE:
- setbluebalance(gspca_dev);
- break;
- case V4L2_CID_AUTOGAIN:
- if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
- setexposure(gspca_dev);
- if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
- setgain(gspca_dev);
- break;
- case V4L2_CID_HFLIP:
- sethvflip(gspca_dev);
- break;
- default:
- return -EINVAL;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-/* this function is called at probe time */
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 11);
-
- sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 32, 1, 16);
- sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 127);
-
- sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 127);
- sd->white_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_WHITE_BALANCE_TEMPERATURE,
- 0, 255, 1, 4);
- sd->red_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
- sd->blue_balance = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 3, 1, 1);
-
- gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 1023, 1,
- PAC7302_EXPOSURE_DEFAULT);
- gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 62, 1,
- PAC7302_GAIN_DEFAULT);
-
- sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
-
- v4l2_ctrl_cluster(2, &sd->brightness);
- v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
- v4l2_ctrl_cluster(2, &sd->hflip);
- return 0;
-}
-
-/* -- start the camera -- */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w_var(gspca_dev, start_7302,
- page3_7302, sizeof(page3_7302));
- setbrightcont(gspca_dev);
- setcolors(gspca_dev);
- setwhitebalance(gspca_dev);
- setredbalance(gspca_dev);
- setbluebalance(gspca_dev);
- setexposure(gspca_dev);
- setgain(gspca_dev);
- sethvflip(gspca_dev);
-
- sd->sof_read = 0;
- sd->autogain_ignore_frames = 0;
- atomic_set(&sd->avg_lum, 270 + sd->brightness->val);
-
- /* start stream */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x01);
-
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
-
- /* stop stream */
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x00);
-}
-
-/* called on streamoff with alt 0 and on disconnect for pac7302 */
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
- if (!gspca_dev->present)
- return;
- reg_w(gspca_dev, 0xff, 0x01);
- reg_w(gspca_dev, 0x78, 0x40);
-}
-
-static void do_autogain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int avg_lum = atomic_read(&sd->avg_lum);
- int desired_lum;
- const int deadzone = 30;
-
- if (sd->autogain_ignore_frames < 0)
- return;
-
- if (sd->autogain_ignore_frames > 0) {
- sd->autogain_ignore_frames--;
- } else {
- desired_lum = 270 + sd->brightness->val;
-
- if (gspca_expo_autogain(gspca_dev, avg_lum, desired_lum,
- deadzone, PAC7302_GAIN_KNEE,
- PAC7302_EXPOSURE_KNEE))
- sd->autogain_ignore_frames =
- PAC_AUTOGAIN_IGNORE_FRAMES;
- }
-}
-
-/* JPEG header */
-static const u8 jpeg_header[] = {
- 0xff, 0xd8, /* SOI: Start of Image */
-
- 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
- 0x00, 0x11, /* length = 17 bytes (including this length field) */
- 0x08, /* Precision: 8 */
- 0x02, 0x80, /* height = 640 (image rotated) */
- 0x01, 0xe0, /* width = 480 */
- 0x03, /* Number of image components: 3 */
- 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
- 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
- 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
-
- 0xff, 0xda, /* SOS: Start Of Scan */
- 0x00, 0x0c, /* length = 12 bytes (including this length field) */
- 0x03, /* number of components: 3 */
- 0x01, 0x00, /* selector 1, table 0x00 */
- 0x02, 0x11, /* selector 2, table 0x11 */
- 0x03, 0x11, /* selector 3, table 0x11 */
- 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
- 0x00 /* Successive approximation: 0 */
-};
-
-/* this function is run at interrupt level */
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 *image;
- u8 *sof;
-
- sof = pac_find_sof(&sd->sof_read, data, len);
- if (sof) {
- int n, lum_offset, footer_length;
-
- /*
- * 6 bytes after the FF D9 EOF marker a number of lumination
- * bytes are send corresponding to different parts of the
- * image, the 14th and 15th byte after the EOF seem to
- * correspond to the center of the image.
- */
- lum_offset = 61 + sizeof pac_sof_marker;
- footer_length = 74;
-
- /* Finish decoding current frame */
- n = (sof - data) - (footer_length + sizeof pac_sof_marker);
- if (n < 0) {
- gspca_dev->image_len += n;
- n = 0;
- } else {
- gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
- }
-
- image = gspca_dev->image;
- if (image != NULL
- && image[gspca_dev->image_len - 2] == 0xff
- && image[gspca_dev->image_len - 1] == 0xd9)
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
-
- n = sof - data;
- len -= n;
- data = sof;
-
- /* Get average lumination */
- if (gspca_dev->last_packet_type == LAST_PACKET &&
- n >= lum_offset)
- atomic_set(&sd->avg_lum, data[-lum_offset] +
- data[-lum_offset + 1]);
-
- /* Start the new frame with the jpeg header */
- /* The PAC7302 has the image rotated 90 degrees */
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- jpeg_header, sizeof jpeg_header);
- }
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_register *reg)
-{
- u8 index;
- u8 value;
-
- /*
- * reg->reg: bit0..15: reserved for register index (wIndex is 16bit
- * long on the USB bus)
- */
- if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
- reg->match.addr == 0 &&
- (reg->reg < 0x000000ff) &&
- (reg->val <= 0x000000ff)
- ) {
- /* Currently writing to page 0 is only supported. */
- /* reg_w() only supports 8bit index */
- index = reg->reg;
- value = reg->val;
-
- /*
- * Note that there shall be no access to other page
- * by any other function between the page switch and
- * the actual register write.
- */
- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, index, value);
-
- reg_w(gspca_dev, 0xdc, 0x01);
- }
- return gspca_dev->usb_err;
-}
-
-static int sd_chip_ident(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_chip_ident *chip)
-{
- int ret = -EINVAL;
-
- if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
- chip->match.addr == 0) {
- chip->revision = 0;
- chip->ident = V4L2_IDENT_UNKNOWN;
- ret = 0;
- }
- return ret;
-}
-#endif
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* interrupt packet data */
- int len) /* interrput packet length */
-{
- int ret = -EINVAL;
- u8 data0, data1;
-
- if (len == 2) {
- data0 = data[0];
- data1 = data[1];
- if ((data0 == 0x00 && data1 == 0x11) ||
- (data0 == 0x22 && data1 == 0x33) ||
- (data0 == 0x44 && data1 == 0x55) ||
- (data0 == 0x66 && data1 == 0x77) ||
- (data0 == 0x88 && data1 == 0x99) ||
- (data0 == 0xaa && data1 == 0xbb) ||
- (data0 == 0xcc && data1 == 0xdd) ||
- (data0 == 0xee && data1 == 0xff)) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
- input_sync(gspca_dev->input_dev);
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- ret = 0;
- }
- }
-
- return ret;
-}
-#endif
-
-/* sub-driver description for pac7302 */
-static const struct sd_desc sd_desc = {
- .name = KBUILD_MODNAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
- .dq_callback = do_autogain,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .set_register = sd_dbg_s_register,
- .get_chip_ident = sd_chip_ident,
-#endif
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .int_pkt_scan = sd_int_pkt_scan,
-#endif
-};
-
-/* -- module initialisation -- */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x06f8, 0x3009)},
- {USB_DEVICE(0x06f8, 0x301b)},
- {USB_DEVICE(0x093a, 0x2620)},
- {USB_DEVICE(0x093a, 0x2621)},
- {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
- {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
- {USB_DEVICE(0x093a, 0x2625)},
- {USB_DEVICE(0x093a, 0x2626)},
- {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP},
- {USB_DEVICE(0x093a, 0x2628)},
- {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
- {USB_DEVICE(0x093a, 0x262a)},
- {USB_DEVICE(0x093a, 0x262c)},
- {USB_DEVICE(0x145f, 0x013c)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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 = KBUILD_MODNAME,
- .id_table = device_table,
- .probe = sd_probe,
- .disconnect = gspca_disconnect,
-#ifdef CONFIG_PM
- .suspend = gspca_suspend,
- .resume = gspca_resume,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
deleted file mode 100644
index b9c6f17eabb2..000000000000
--- a/drivers/media/video/gspca/sn9c20x.c
+++ /dev/null
@@ -1,2428 +0,0 @@
-/*
- * Sonix sn9c201 sn9c202 library
- *
- * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
- * Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
- * Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/input.h>
-
-#include "gspca.h"
-#include "jpeg.h"
-
-#include <media/v4l2-chip-ident.h>
-#include <linux/dmi.h>
-
-MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
- "microdia project <microdia@googlegroups.com>");
-MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/*
- * Pixel format private data
- */
-#define SCALE_MASK 0x0f
-#define SCALE_160x120 0
-#define SCALE_320x240 1
-#define SCALE_640x480 2
-#define SCALE_1280x1024 3
-#define MODE_RAW 0x10
-#define MODE_JPEG 0x20
-#define MODE_SXGA 0x80
-
-#define SENSOR_OV9650 0
-#define SENSOR_OV9655 1
-#define SENSOR_SOI968 2
-#define SENSOR_OV7660 3
-#define SENSOR_OV7670 4
-#define SENSOR_MT9V011 5
-#define SENSOR_MT9V111 6
-#define SENSOR_MT9V112 7
-#define SENSOR_MT9M001 8
-#define SENSOR_MT9M111 9
-#define SENSOR_MT9M112 10
-#define SENSOR_HV7131R 11
-#define SENSOR_MT9VPRB 12
-
-/* camera flags */
-#define HAS_NO_BUTTON 0x1
-#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
-#define FLIP_DETECT 0x4
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev;
-
- struct { /* color control cluster */
- struct v4l2_ctrl *brightness;
- struct v4l2_ctrl *contrast;
- struct v4l2_ctrl *saturation;
- struct v4l2_ctrl *hue;
- };
- struct { /* blue/red balance control cluster */
- struct v4l2_ctrl *blue;
- struct v4l2_ctrl *red;
- };
- struct { /* h/vflip control cluster */
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- };
- struct v4l2_ctrl *gamma;
- struct { /* autogain and exposure or gain control cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *gain;
- };
- struct v4l2_ctrl *jpegqual;
-
- struct work_struct work;
- struct workqueue_struct *work_thread;
-
- u32 pktsz; /* (used by pkt_scan) */
- u16 npkt;
- s8 nchg;
- u8 fmt; /* (used for JPEG QTAB update */
-
-#define MIN_AVG_LUM 80
-#define MAX_AVG_LUM 130
- atomic_t avg_lum;
- u8 old_step;
- u8 older_step;
- u8 exposure_step;
-
- u8 i2c_addr;
- u8 i2c_intf;
- u8 sensor;
- u8 hstart;
- u8 vstart;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
-
- u8 flags;
-};
-
-static void qual_upd(struct work_struct *work);
-
-struct i2c_reg_u8 {
- u8 reg;
- u8 val;
-};
-
-struct i2c_reg_u16 {
- u8 reg;
- u16 val;
-};
-
-static const struct dmi_system_id flip_dmi_table[] = {
- {
- .ident = "MSI MS-1034",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "0341")
- }
- },
- {
- .ident = "MSI MS-1632",
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
- DMI_MATCH(DMI_BOARD_NAME, "MS-1632")
- }
- },
- {
- .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"),
- DMI_MATCH(DMI_BOARD_NAME, "MS-1635X")
- }
- },
- {
- .ident = "ASUSTeK W7J",
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
- DMI_MATCH(DMI_BOARD_NAME, "W7J ")
- }
- },
- {}
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_160x120 | MODE_JPEG},
- {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_160x120 | MODE_RAW},
- {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 240 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_160x120},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_320x240 | MODE_JPEG},
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 ,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_320x240 | MODE_RAW},
- {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 480 * 240 ,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_320x240},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_640x480 | MODE_JPEG},
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_640x480 | MODE_RAW},
- {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 960 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_640x480},
-};
-
-static const struct v4l2_pix_format sxga_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_160x120 | MODE_JPEG},
- {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_160x120 | MODE_RAW},
- {160, 120, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 240 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_160x120},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_320x240 | MODE_JPEG},
- {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 ,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_320x240 | MODE_RAW},
- {320, 240, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 480 * 240 ,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_320x240},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = SCALE_640x480 | MODE_JPEG},
- {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_640x480 | MODE_RAW},
- {640, 480, V4L2_PIX_FMT_SN9C20X_I420, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 960 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_640x480},
- {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 1280,
- .sizeimage = 1280 * 1024,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
-};
-
-static const struct v4l2_pix_format mono_mode[] = {
- {160, 120, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_160x120 | MODE_RAW},
- {320, 240, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 ,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_320x240 | MODE_RAW},
- {640, 480, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_640x480 | MODE_RAW},
- {1280, 1024, V4L2_PIX_FMT_GREY, V4L2_FIELD_NONE,
- .bytesperline = 1280,
- .sizeimage = 1280 * 1024,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = SCALE_1280x1024 | MODE_RAW | MODE_SXGA},
-};
-
-static const s16 hsv_red_x[] = {
- 41, 44, 46, 48, 50, 52, 54, 56,
- 58, 60, 62, 64, 66, 68, 70, 72,
- 74, 76, 78, 80, 81, 83, 85, 87,
- 88, 90, 92, 93, 95, 97, 98, 100,
- 101, 102, 104, 105, 107, 108, 109, 110,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 123, 124, 125, 125,
- 126, 127, 127, 128, 128, 129, 129, 129,
- 130, 130, 130, 130, 131, 131, 131, 131,
- 131, 131, 131, 131, 130, 130, 130, 130,
- 129, 129, 129, 128, 128, 127, 127, 126,
- 125, 125, 124, 123, 122, 122, 121, 120,
- 119, 118, 117, 116, 115, 114, 112, 111,
- 110, 109, 107, 106, 105, 103, 102, 101,
- 99, 98, 96, 94, 93, 91, 90, 88,
- 86, 84, 83, 81, 79, 77, 75, 74,
- 72, 70, 68, 66, 64, 62, 60, 58,
- 56, 54, 52, 49, 47, 45, 43, 41,
- 39, 36, 34, 32, 30, 28, 25, 23,
- 21, 19, 16, 14, 12, 9, 7, 5,
- 3, 0, -1, -3, -6, -8, -10, -12,
- -15, -17, -19, -22, -24, -26, -28, -30,
- -33, -35, -37, -39, -41, -44, -46, -48,
- -50, -52, -54, -56, -58, -60, -62, -64,
- -66, -68, -70, -72, -74, -76, -78, -80,
- -81, -83, -85, -87, -88, -90, -92, -93,
- -95, -97, -98, -100, -101, -102, -104, -105,
- -107, -108, -109, -110, -112, -113, -114, -115,
- -116, -117, -118, -119, -120, -121, -122, -123,
- -123, -124, -125, -125, -126, -127, -127, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -128, -127, -127, -126, -125, -125, -124, -123,
- -122, -122, -121, -120, -119, -118, -117, -116,
- -115, -114, -112, -111, -110, -109, -107, -106,
- -105, -103, -102, -101, -99, -98, -96, -94,
- -93, -91, -90, -88, -86, -84, -83, -81,
- -79, -77, -75, -74, -72, -70, -68, -66,
- -64, -62, -60, -58, -56, -54, -52, -49,
- -47, -45, -43, -41, -39, -36, -34, -32,
- -30, -28, -25, -23, -21, -19, -16, -14,
- -12, -9, -7, -5, -3, 0, 1, 3,
- 6, 8, 10, 12, 15, 17, 19, 22,
- 24, 26, 28, 30, 33, 35, 37, 39, 41
-};
-
-static const s16 hsv_red_y[] = {
- 82, 80, 78, 76, 74, 73, 71, 69,
- 67, 65, 63, 61, 58, 56, 54, 52,
- 50, 48, 46, 44, 41, 39, 37, 35,
- 32, 30, 28, 26, 23, 21, 19, 16,
- 14, 12, 10, 7, 5, 3, 0, -1,
- -3, -6, -8, -10, -13, -15, -17, -19,
- -22, -24, -26, -29, -31, -33, -35, -38,
- -40, -42, -44, -46, -48, -51, -53, -55,
- -57, -59, -61, -63, -65, -67, -69, -71,
- -73, -75, -77, -79, -81, -82, -84, -86,
- -88, -89, -91, -93, -94, -96, -98, -99,
- -101, -102, -104, -105, -106, -108, -109, -110,
- -112, -113, -114, -115, -116, -117, -119, -120,
- -120, -121, -122, -123, -124, -125, -126, -126,
- -127, -128, -128, -128, -128, -128, -128, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -128, -128, -128, -128, -128, -128, -128, -128,
- -127, -127, -126, -125, -125, -124, -123, -122,
- -121, -120, -119, -118, -117, -116, -115, -114,
- -113, -111, -110, -109, -107, -106, -105, -103,
- -102, -100, -99, -97, -96, -94, -92, -91,
- -89, -87, -85, -84, -82, -80, -78, -76,
- -74, -73, -71, -69, -67, -65, -63, -61,
- -58, -56, -54, -52, -50, -48, -46, -44,
- -41, -39, -37, -35, -32, -30, -28, -26,
- -23, -21, -19, -16, -14, -12, -10, -7,
- -5, -3, 0, 1, 3, 6, 8, 10,
- 13, 15, 17, 19, 22, 24, 26, 29,
- 31, 33, 35, 38, 40, 42, 44, 46,
- 48, 51, 53, 55, 57, 59, 61, 63,
- 65, 67, 69, 71, 73, 75, 77, 79,
- 81, 82, 84, 86, 88, 89, 91, 93,
- 94, 96, 98, 99, 101, 102, 104, 105,
- 106, 108, 109, 110, 112, 113, 114, 115,
- 116, 117, 119, 120, 120, 121, 122, 123,
- 124, 125, 126, 126, 127, 128, 128, 129,
- 129, 130, 130, 131, 131, 131, 131, 132,
- 132, 132, 132, 132, 132, 132, 132, 132,
- 132, 132, 132, 131, 131, 131, 130, 130,
- 130, 129, 129, 128, 127, 127, 126, 125,
- 125, 124, 123, 122, 121, 120, 119, 118,
- 117, 116, 115, 114, 113, 111, 110, 109,
- 107, 106, 105, 103, 102, 100, 99, 97,
- 96, 94, 92, 91, 89, 87, 85, 84, 82
-};
-
-static const s16 hsv_green_x[] = {
- -124, -124, -125, -125, -125, -125, -125, -125,
- -125, -126, -126, -125, -125, -125, -125, -125,
- -125, -124, -124, -124, -123, -123, -122, -122,
- -121, -121, -120, -120, -119, -118, -117, -117,
- -116, -115, -114, -113, -112, -111, -110, -109,
- -108, -107, -105, -104, -103, -102, -100, -99,
- -98, -96, -95, -93, -92, -91, -89, -87,
- -86, -84, -83, -81, -79, -77, -76, -74,
- -72, -70, -69, -67, -65, -63, -61, -59,
- -57, -55, -53, -51, -49, -47, -45, -43,
- -41, -39, -37, -35, -33, -30, -28, -26,
- -24, -22, -20, -18, -15, -13, -11, -9,
- -7, -4, -2, 0, 1, 3, 6, 8,
- 10, 12, 14, 17, 19, 21, 23, 25,
- 27, 29, 32, 34, 36, 38, 40, 42,
- 44, 46, 48, 50, 52, 54, 56, 58,
- 60, 62, 64, 66, 68, 70, 71, 73,
- 75, 77, 78, 80, 82, 83, 85, 87,
- 88, 90, 91, 93, 94, 96, 97, 98,
- 100, 101, 102, 104, 105, 106, 107, 108,
- 109, 111, 112, 113, 113, 114, 115, 116,
- 117, 118, 118, 119, 120, 120, 121, 122,
- 122, 123, 123, 124, 124, 124, 125, 125,
- 125, 125, 125, 125, 125, 126, 126, 125,
- 125, 125, 125, 125, 125, 124, 124, 124,
- 123, 123, 122, 122, 121, 121, 120, 120,
- 119, 118, 117, 117, 116, 115, 114, 113,
- 112, 111, 110, 109, 108, 107, 105, 104,
- 103, 102, 100, 99, 98, 96, 95, 93,
- 92, 91, 89, 87, 86, 84, 83, 81,
- 79, 77, 76, 74, 72, 70, 69, 67,
- 65, 63, 61, 59, 57, 55, 53, 51,
- 49, 47, 45, 43, 41, 39, 37, 35,
- 33, 30, 28, 26, 24, 22, 20, 18,
- 15, 13, 11, 9, 7, 4, 2, 0,
- -1, -3, -6, -8, -10, -12, -14, -17,
- -19, -21, -23, -25, -27, -29, -32, -34,
- -36, -38, -40, -42, -44, -46, -48, -50,
- -52, -54, -56, -58, -60, -62, -64, -66,
- -68, -70, -71, -73, -75, -77, -78, -80,
- -82, -83, -85, -87, -88, -90, -91, -93,
- -94, -96, -97, -98, -100, -101, -102, -104,
- -105, -106, -107, -108, -109, -111, -112, -113,
- -113, -114, -115, -116, -117, -118, -118, -119,
- -120, -120, -121, -122, -122, -123, -123, -124, -124
-};
-
-static const s16 hsv_green_y[] = {
- -100, -99, -98, -97, -95, -94, -93, -91,
- -90, -89, -87, -86, -84, -83, -81, -80,
- -78, -76, -75, -73, -71, -70, -68, -66,
- -64, -63, -61, -59, -57, -55, -53, -51,
- -49, -48, -46, -44, -42, -40, -38, -36,
- -34, -32, -30, -27, -25, -23, -21, -19,
- -17, -15, -13, -11, -9, -7, -4, -2,
- 0, 1, 3, 5, 7, 9, 11, 14,
- 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46,
- 48, 50, 52, 54, 56, 58, 59, 61,
- 63, 65, 67, 68, 70, 72, 74, 75,
- 77, 78, 80, 82, 83, 85, 86, 88,
- 89, 90, 92, 93, 95, 96, 97, 98,
- 100, 101, 102, 103, 104, 105, 106, 107,
- 108, 109, 110, 111, 112, 112, 113, 114,
- 115, 115, 116, 116, 117, 117, 118, 118,
- 119, 119, 119, 120, 120, 120, 120, 120,
- 121, 121, 121, 121, 121, 121, 120, 120,
- 120, 120, 120, 119, 119, 119, 118, 118,
- 117, 117, 116, 116, 115, 114, 114, 113,
- 112, 111, 111, 110, 109, 108, 107, 106,
- 105, 104, 103, 102, 100, 99, 98, 97,
- 95, 94, 93, 91, 90, 89, 87, 86,
- 84, 83, 81, 80, 78, 76, 75, 73,
- 71, 70, 68, 66, 64, 63, 61, 59,
- 57, 55, 53, 51, 49, 48, 46, 44,
- 42, 40, 38, 36, 34, 32, 30, 27,
- 25, 23, 21, 19, 17, 15, 13, 11,
- 9, 7, 4, 2, 0, -1, -3, -5,
- -7, -9, -11, -14, -16, -18, -20, -22,
- -24, -26, -28, -30, -32, -34, -36, -38,
- -40, -42, -44, -46, -48, -50, -52, -54,
- -56, -58, -59, -61, -63, -65, -67, -68,
- -70, -72, -74, -75, -77, -78, -80, -82,
- -83, -85, -86, -88, -89, -90, -92, -93,
- -95, -96, -97, -98, -100, -101, -102, -103,
- -104, -105, -106, -107, -108, -109, -110, -111,
- -112, -112, -113, -114, -115, -115, -116, -116,
- -117, -117, -118, -118, -119, -119, -119, -120,
- -120, -120, -120, -120, -121, -121, -121, -121,
- -121, -121, -120, -120, -120, -120, -120, -119,
- -119, -119, -118, -118, -117, -117, -116, -116,
- -115, -114, -114, -113, -112, -111, -111, -110,
- -109, -108, -107, -106, -105, -104, -103, -102, -100
-};
-
-static const s16 hsv_blue_x[] = {
- 112, 113, 114, 114, 115, 116, 117, 117,
- 118, 118, 119, 119, 120, 120, 120, 121,
- 121, 121, 122, 122, 122, 122, 122, 122,
- 122, 122, 122, 122, 122, 122, 121, 121,
- 121, 120, 120, 120, 119, 119, 118, 118,
- 117, 116, 116, 115, 114, 113, 113, 112,
- 111, 110, 109, 108, 107, 106, 105, 104,
- 103, 102, 100, 99, 98, 97, 95, 94,
- 93, 91, 90, 88, 87, 85, 84, 82,
- 80, 79, 77, 76, 74, 72, 70, 69,
- 67, 65, 63, 61, 60, 58, 56, 54,
- 52, 50, 48, 46, 44, 42, 40, 38,
- 36, 34, 32, 30, 28, 26, 24, 22,
- 19, 17, 15, 13, 11, 9, 7, 5,
- 2, 0, -1, -3, -5, -7, -9, -12,
- -14, -16, -18, -20, -22, -24, -26, -28,
- -31, -33, -35, -37, -39, -41, -43, -45,
- -47, -49, -51, -53, -54, -56, -58, -60,
- -62, -64, -66, -67, -69, -71, -73, -74,
- -76, -78, -79, -81, -83, -84, -86, -87,
- -89, -90, -92, -93, -94, -96, -97, -98,
- -99, -101, -102, -103, -104, -105, -106, -107,
- -108, -109, -110, -111, -112, -113, -114, -114,
- -115, -116, -117, -117, -118, -118, -119, -119,
- -120, -120, -120, -121, -121, -121, -122, -122,
- -122, -122, -122, -122, -122, -122, -122, -122,
- -122, -122, -121, -121, -121, -120, -120, -120,
- -119, -119, -118, -118, -117, -116, -116, -115,
- -114, -113, -113, -112, -111, -110, -109, -108,
- -107, -106, -105, -104, -103, -102, -100, -99,
- -98, -97, -95, -94, -93, -91, -90, -88,
- -87, -85, -84, -82, -80, -79, -77, -76,
- -74, -72, -70, -69, -67, -65, -63, -61,
- -60, -58, -56, -54, -52, -50, -48, -46,
- -44, -42, -40, -38, -36, -34, -32, -30,
- -28, -26, -24, -22, -19, -17, -15, -13,
- -11, -9, -7, -5, -2, 0, 1, 3,
- 5, 7, 9, 12, 14, 16, 18, 20,
- 22, 24, 26, 28, 31, 33, 35, 37,
- 39, 41, 43, 45, 47, 49, 51, 53,
- 54, 56, 58, 60, 62, 64, 66, 67,
- 69, 71, 73, 74, 76, 78, 79, 81,
- 83, 84, 86, 87, 89, 90, 92, 93,
- 94, 96, 97, 98, 99, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111, 112
-};
-
-static const s16 hsv_blue_y[] = {
- -11, -13, -15, -17, -19, -21, -23, -25,
- -27, -29, -31, -33, -35, -37, -39, -41,
- -43, -45, -46, -48, -50, -52, -54, -55,
- -57, -59, -61, -62, -64, -66, -67, -69,
- -71, -72, -74, -75, -77, -78, -80, -81,
- -83, -84, -86, -87, -88, -90, -91, -92,
- -93, -95, -96, -97, -98, -99, -100, -101,
- -102, -103, -104, -105, -106, -106, -107, -108,
- -109, -109, -110, -111, -111, -112, -112, -113,
- -113, -114, -114, -114, -115, -115, -115, -115,
- -116, -116, -116, -116, -116, -116, -116, -116,
- -116, -115, -115, -115, -115, -114, -114, -114,
- -113, -113, -112, -112, -111, -111, -110, -110,
- -109, -108, -108, -107, -106, -105, -104, -103,
- -102, -101, -100, -99, -98, -97, -96, -95,
- -94, -93, -91, -90, -89, -88, -86, -85,
- -84, -82, -81, -79, -78, -76, -75, -73,
- -71, -70, -68, -67, -65, -63, -62, -60,
- -58, -56, -55, -53, -51, -49, -47, -45,
- -44, -42, -40, -38, -36, -34, -32, -30,
- -28, -26, -24, -22, -20, -18, -16, -14,
- -12, -10, -8, -6, -4, -2, 0, 1,
- 3, 5, 7, 9, 11, 13, 15, 17,
- 19, 21, 23, 25, 27, 29, 31, 33,
- 35, 37, 39, 41, 43, 45, 46, 48,
- 50, 52, 54, 55, 57, 59, 61, 62,
- 64, 66, 67, 69, 71, 72, 74, 75,
- 77, 78, 80, 81, 83, 84, 86, 87,
- 88, 90, 91, 92, 93, 95, 96, 97,
- 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 106, 107, 108, 109, 109, 110, 111,
- 111, 112, 112, 113, 113, 114, 114, 114,
- 115, 115, 115, 115, 116, 116, 116, 116,
- 116, 116, 116, 116, 116, 115, 115, 115,
- 115, 114, 114, 114, 113, 113, 112, 112,
- 111, 111, 110, 110, 109, 108, 108, 107,
- 106, 105, 104, 103, 102, 101, 100, 99,
- 98, 97, 96, 95, 94, 93, 91, 90,
- 89, 88, 86, 85, 84, 82, 81, 79,
- 78, 76, 75, 73, 71, 70, 68, 67,
- 65, 63, 62, 60, 58, 56, 55, 53,
- 51, 49, 47, 45, 44, 42, 40, 38,
- 36, 34, 32, 30, 28, 26, 24, 22,
- 20, 18, 16, 14, 12, 10, 8, 6,
- 4, 2, 0, -1, -3, -5, -7, -9, -11
-};
-
-static const u16 i2c_ident[] = {
- V4L2_IDENT_OV9650,
- V4L2_IDENT_OV9655,
- V4L2_IDENT_SOI968,
- V4L2_IDENT_OV7660,
- V4L2_IDENT_OV7670,
- V4L2_IDENT_MT9V011,
- V4L2_IDENT_MT9V111,
- V4L2_IDENT_MT9V112,
- V4L2_IDENT_MT9M001C12ST,
- V4L2_IDENT_MT9M111,
- V4L2_IDENT_MT9M112,
- V4L2_IDENT_HV7131R,
-[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN,
-};
-
-static const u16 bridge_init[][2] = {
- {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
- {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
- {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10},
- {0x106b, 0x08}, {0x1188, 0x87}, {0x11a1, 0x00},
- {0x11a2, 0x00}, {0x11a3, 0x6a}, {0x11a4, 0x50},
- {0x11ab, 0x00}, {0x11ac, 0x00}, {0x11ad, 0x50},
- {0x11ae, 0x3c}, {0x118a, 0x04}, {0x0395, 0x04},
- {0x11b8, 0x3a}, {0x118b, 0x0e}, {0x10f7, 0x05},
- {0x10f8, 0x14}, {0x10fa, 0xff}, {0x10f9, 0x00},
- {0x11ba, 0x0a}, {0x11a5, 0x2d}, {0x11a6, 0x2d},
- {0x11a7, 0x3a}, {0x11a8, 0x05}, {0x11a9, 0x04},
- {0x11aa, 0x3f}, {0x11af, 0x28}, {0x11b0, 0xd8},
- {0x11b1, 0x14}, {0x11b2, 0xec}, {0x11b3, 0x32},
- {0x11b4, 0xdd}, {0x11b5, 0x32}, {0x11b6, 0xdd},
- {0x10e0, 0x2c}, {0x11bc, 0x40}, {0x11bd, 0x01},
- {0x11be, 0xf0}, {0x11bf, 0x00}, {0x118c, 0x1f},
- {0x118d, 0x1f}, {0x118e, 0x1f}, {0x118f, 0x1f},
- {0x1180, 0x01}, {0x1181, 0x00}, {0x1182, 0x01},
- {0x1183, 0x00}, {0x1184, 0x50}, {0x1185, 0x80},
- {0x1007, 0x00}
-};
-
-/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
-static const u8 ov_gain[] = {
- 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
- 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
- 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
- 0x30 /* 4x */, 0x31 /* 4.25x */, 0x32 /* 4.5x */, 0x33 /* 4.75x */,
- 0x34 /* 5x */, 0x35 /* 5.25x */, 0x36 /* 5.5x */, 0x37 /* 5.75x */,
- 0x38 /* 6x */, 0x39 /* 6.25x */, 0x3a /* 6.5x */, 0x3b /* 6.75x */,
- 0x3c /* 7x */, 0x3d /* 7.25x */, 0x3e /* 7.5x */, 0x3f /* 7.75x */,
- 0x70 /* 8x */
-};
-
-/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
-static const u16 micron1_gain[] = {
- /* 1x 1.25x 1.5x 1.75x */
- 0x0020, 0x0028, 0x0030, 0x0038,
- /* 2x 2.25x 2.5x 2.75x */
- 0x00a0, 0x00a4, 0x00a8, 0x00ac,
- /* 3x 3.25x 3.5x 3.75x */
- 0x00b0, 0x00b4, 0x00b8, 0x00bc,
- /* 4x 4.25x 4.5x 4.75x */
- 0x00c0, 0x00c4, 0x00c8, 0x00cc,
- /* 5x 5.25x 5.5x 5.75x */
- 0x00d0, 0x00d4, 0x00d8, 0x00dc,
- /* 6x 6.25x 6.5x 6.75x */
- 0x00e0, 0x00e4, 0x00e8, 0x00ec,
- /* 7x 7.25x 7.5x 7.75x */
- 0x00f0, 0x00f4, 0x00f8, 0x00fc,
- /* 8x */
- 0x01c0
-};
-
-/* mt9m001 sensor uses a different gain formula then other micron sensors */
-/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
-static const u16 micron2_gain[] = {
- /* 1x 1.25x 1.5x 1.75x */
- 0x0008, 0x000a, 0x000c, 0x000e,
- /* 2x 2.25x 2.5x 2.75x */
- 0x0010, 0x0012, 0x0014, 0x0016,
- /* 3x 3.25x 3.5x 3.75x */
- 0x0018, 0x001a, 0x001c, 0x001e,
- /* 4x 4.25x 4.5x 4.75x */
- 0x0020, 0x0051, 0x0052, 0x0053,
- /* 5x 5.25x 5.5x 5.75x */
- 0x0054, 0x0055, 0x0056, 0x0057,
- /* 6x 6.25x 6.5x 6.75x */
- 0x0058, 0x0059, 0x005a, 0x005b,
- /* 7x 7.25x 7.5x 7.75x */
- 0x005c, 0x005d, 0x005e, 0x005f,
- /* 8x */
- 0x0060
-};
-
-/* Gain = .5 + bit[7:0] / 16 */
-static const u8 hv7131r_gain[] = {
- 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
- 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
- 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
- 0x38 /* 4x */, 0x3c /* 4.25x */, 0x40 /* 4.5x */, 0x44 /* 4.75x */,
- 0x48 /* 5x */, 0x4c /* 5.25x */, 0x50 /* 5.5x */, 0x54 /* 5.75x */,
- 0x58 /* 6x */, 0x5c /* 6.25x */, 0x60 /* 6.5x */, 0x64 /* 6.75x */,
- 0x68 /* 7x */, 0x6c /* 7.25x */, 0x70 /* 7.5x */, 0x74 /* 7.75x */,
- 0x78 /* 8x */
-};
-
-static const struct i2c_reg_u8 soi968_init[] = {
- {0x0c, 0x00}, {0x0f, 0x1f},
- {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
- {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
- {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
- {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20},
- {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e},
- {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13},
- {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79},
- {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40},
- {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32},
- {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
-};
-
-static const 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, 0x00},
- {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50},
-};
-
-static const struct i2c_reg_u8 ov7670_init[] = {
- {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
- {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
- {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
- {0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
- {0x0d, 0x40}, {0x14, 0x28}, {0xa5, 0x05}, {0xab, 0x07},
- {0x24, 0x95}, {0x25, 0x33}, {0x26, 0xe3}, {0x9f, 0x75},
- {0xa0, 0x65}, {0xa1, 0x0b}, {0xa6, 0xd8}, {0xa7, 0xd8},
- {0xa8, 0xf0}, {0xa9, 0x90}, {0xaa, 0x94}, {0x13, 0xe5},
- {0x0e, 0x61}, {0x0f, 0x4b}, {0x16, 0x02}, {0x1e, 0x27},
- {0x21, 0x02}, {0x22, 0x91}, {0x29, 0x07}, {0x33, 0x0b},
- {0x35, 0x0b}, {0x37, 0x1d}, {0x38, 0x71}, {0x39, 0x2a},
- {0x3c, 0x78}, {0x4d, 0x40}, {0x4e, 0x20}, {0x69, 0x00},
- {0x74, 0x19}, {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, 0x20},
- {0x46, 0x7d}, {0x47, 0x29}, {0x48, 0x4a}, {0x59, 0x8c},
- {0x5a, 0xa5}, {0x5b, 0xde}, {0x5c, 0x96}, {0x5d, 0x66},
- {0x5e, 0x10}, {0x6c, 0x0a}, {0x6d, 0x55}, {0x6e, 0x11},
- {0x6f, 0x9e}, {0x6a, 0x40}, {0x01, 0x40}, {0x02, 0x40},
- {0x13, 0xe7}, {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x02},
- {0x52, 0x1d}, {0x53, 0x56}, {0x54, 0x73}, {0x55, 0x0a},
- {0x56, 0x55}, {0x57, 0x80}, {0x58, 0x9e}, {0x41, 0x08},
- {0x3f, 0x02}, {0x75, 0x03}, {0x76, 0x63}, {0x4c, 0x04},
- {0x77, 0x06}, {0x3d, 0x02}, {0x4b, 0x09}, {0xc9, 0x30},
- {0x41, 0x08}, {0x56, 0x48}, {0x34, 0x11}, {0xa4, 0x88},
- {0x96, 0x00}, {0x97, 0x30}, {0x98, 0x20}, {0x99, 0x30},
- {0x9a, 0x84}, {0x9b, 0x29}, {0x9c, 0x03}, {0x9d, 0x99},
- {0x9e, 0x7f}, {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, 0x40}, {0x79, 0x05}, {0xc8, 0x30},
- {0x79, 0x26}, {0x62, 0x20}, {0x63, 0x00}, {0x64, 0x06},
- {0x65, 0x00}, {0x66, 0x05}, {0x94, 0x05}, {0x95, 0x0a},
- {0x17, 0x13}, {0x18, 0x01}, {0x19, 0x02}, {0x1a, 0x7a},
- {0x46, 0x59}, {0x47, 0x30}, {0x58, 0x9a}, {0x59, 0x84},
- {0x5a, 0x91}, {0x5b, 0x57}, {0x5c, 0x75}, {0x5d, 0x6d},
- {0x5e, 0x13}, {0x64, 0x07}, {0x94, 0x07}, {0x95, 0x0d},
- {0xa6, 0xdf}, {0xa7, 0xdf}, {0x48, 0x4d}, {0x51, 0x00},
- {0x6b, 0x0a}, {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00},
- {0x92, 0x00}, {0x93, 0x00}, {0x55, 0x0a}, {0x56, 0x60},
- {0x4f, 0x6e}, {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d},
- {0x53, 0x56}, {0x54, 0x73}, {0x58, 0x9a}, {0x4f, 0x6e},
- {0x50, 0x70}, {0x51, 0x00}, {0x52, 0x1d}, {0x53, 0x56},
- {0x54, 0x73}, {0x58, 0x9a}, {0x3f, 0x01}, {0x7b, 0x03},
- {0x7c, 0x09}, {0x7d, 0x16}, {0x7e, 0x38}, {0x7f, 0x47},
- {0x80, 0x53}, {0x81, 0x5e}, {0x82, 0x6a}, {0x83, 0x74},
- {0x84, 0x80}, {0x85, 0x8c}, {0x86, 0x9b}, {0x87, 0xb2},
- {0x88, 0xcc}, {0x89, 0xe5}, {0x7a, 0x24}, {0x3b, 0x00},
- {0x9f, 0x76}, {0xa0, 0x65}, {0x13, 0xe2}, {0x6b, 0x0a},
- {0x11, 0x80}, {0x2a, 0x00}, {0x2b, 0x00}, {0x92, 0x00},
- {0x93, 0x00},
-};
-
-static const struct i2c_reg_u8 ov9650_init[] = {
- {0x00, 0x00}, {0x01, 0x78},
- {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
- {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
- {0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
- {0x0e, 0xa0}, {0x0f, 0x52}, {0x10, 0x7c},
- {0x11, 0x80}, {0x12, 0x45}, {0x13, 0xc2},
- {0x14, 0x2e}, {0x15, 0x00}, {0x16, 0x07},
- {0x17, 0x24}, {0x18, 0xc5}, {0x19, 0x00},
- {0x1a, 0x3c}, {0x1b, 0x00}, {0x1e, 0x04},
- {0x1f, 0x00}, {0x24, 0x78}, {0x25, 0x68},
- {0x26, 0xd4}, {0x27, 0x80}, {0x28, 0x80},
- {0x29, 0x30}, {0x2a, 0x00}, {0x2b, 0x00},
- {0x2c, 0x80}, {0x2d, 0x00}, {0x2e, 0x00},
- {0x2f, 0x00}, {0x30, 0x08}, {0x31, 0x30},
- {0x32, 0x84}, {0x33, 0xe2}, {0x34, 0xbf},
- {0x35, 0x81}, {0x36, 0xf9}, {0x37, 0x00},
- {0x38, 0x93}, {0x39, 0x50}, {0x3a, 0x01},
- {0x3b, 0x01}, {0x3c, 0x73}, {0x3d, 0x19},
- {0x3e, 0x0b}, {0x3f, 0x80}, {0x40, 0xc1},
- {0x41, 0x00}, {0x42, 0x08}, {0x67, 0x80},
- {0x68, 0x80}, {0x69, 0x40}, {0x6a, 0x00},
- {0x6b, 0x0a}, {0x8b, 0x06}, {0x8c, 0x20},
- {0x8d, 0x00}, {0x8e, 0x00}, {0x8f, 0xdf},
- {0x92, 0x00}, {0x93, 0x00}, {0x94, 0x88},
- {0x95, 0x88}, {0x96, 0x04}, {0xa1, 0x00},
- {0xa5, 0x80}, {0xa8, 0x80}, {0xa9, 0xb8},
- {0xaa, 0x92}, {0xab, 0x0a},
-};
-
-static const struct i2c_reg_u8 ov9655_init[] = {
- {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
- {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
- {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
- {0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
- {0x3a, 0x00}, {0x3b, 0xcc}, {0x3c, 0x0c}, {0x3d, 0x19},
- {0x3e, 0x0c}, {0x3f, 0x01}, {0x41, 0x40}, {0x42, 0x80},
- {0x45, 0x46}, {0x46, 0x62}, {0x47, 0x2a}, {0x48, 0x3c},
- {0x4a, 0xf0}, {0x4b, 0xdc}, {0x4c, 0xdc}, {0x4d, 0xdc},
- {0x4e, 0xdc}, {0x6c, 0x04}, {0x6f, 0x9e}, {0x70, 0x05},
- {0x71, 0x78}, {0x77, 0x02}, {0x8a, 0x23}, {0x90, 0x7e},
- {0x91, 0x7c}, {0x9f, 0x6e}, {0xa0, 0x6e}, {0xa5, 0x68},
- {0xa6, 0x60}, {0xa8, 0xc1}, {0xa9, 0xfa}, {0xaa, 0x92},
- {0xab, 0x04}, {0xac, 0x80}, {0xad, 0x80}, {0xae, 0x80},
- {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, {0xb5, 0x00},
- {0xb6, 0xaf}, {0xbb, 0xae}, {0xbc, 0x44}, {0xbd, 0x44},
- {0xbe, 0x3b}, {0xbf, 0x3a}, {0xc1, 0xc8}, {0xc2, 0x01},
- {0xc4, 0x00}, {0xc6, 0x85}, {0xc7, 0x81}, {0xc9, 0xe0},
- {0xca, 0xe8}, {0xcc, 0xd8}, {0xcd, 0x93}, {0x2d, 0x00},
- {0x2e, 0x00}, {0x01, 0x80}, {0x02, 0x80}, {0x12, 0x61},
- {0x36, 0xfa}, {0x8c, 0x8d}, {0xc0, 0xaa}, {0x69, 0x0a},
- {0x03, 0x09}, {0x17, 0x16}, {0x18, 0x6e}, {0x19, 0x01},
- {0x1a, 0x3e}, {0x32, 0x09}, {0x2a, 0x10}, {0x2b, 0x0a},
- {0x92, 0x00}, {0x93, 0x00}, {0xa1, 0x00}, {0x10, 0x7c},
- {0x04, 0x03}, {0x00, 0x13},
-};
-
-static const struct i2c_reg_u16 mt9v112_init[] = {
- {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
- {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
- {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
- {0x05, 0x0000}, {0x06, 0x340c}, {0x3b, 0x042a},
- {0x3c, 0x0400}, {0xf0, 0x0002}, {0x2e, 0x0c58},
- {0x5b, 0x0001}, {0xc8, 0x9f0b}, {0xf0, 0x0001},
- {0x9b, 0x5300}, {0xf0, 0x0000}, {0x2b, 0x0020},
- {0x2c, 0x002a}, {0x2d, 0x0032}, {0x2e, 0x0020},
- {0x09, 0x01dc}, {0x01, 0x000c}, {0x02, 0x0020},
- {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
- {0x05, 0x0098}, {0x20, 0x0703}, {0x09, 0x01f2},
- {0x2b, 0x00a0}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
- {0x2e, 0x00a0}, {0x01, 0x000c}, {0x02, 0x0020},
- {0x03, 0x01e0}, {0x04, 0x0280}, {0x06, 0x000c},
- {0x05, 0x0098}, {0x09, 0x01c1}, {0x2b, 0x00ae},
- {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
-};
-
-static const struct i2c_reg_u16 mt9v111_init[] = {
- {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
- {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0},
- {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e},
- {0x08, 0x0480}, {0x01, 0x0004}, {0x02, 0x0016},
- {0x03, 0x01e7}, {0x04, 0x0287}, {0x05, 0x0004},
- {0x06, 0x002d}, {0x07, 0x3002}, {0x08, 0x0008},
- {0x0e, 0x0008}, {0x20, 0x0000}
-};
-
-static const struct i2c_reg_u16 mt9v011_init[] = {
- {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
- {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1},
- {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006},
- {0x0d, 0x0002}, {0x0a, 0x0000}, {0x0b, 0x0000},
- {0x0c, 0x0000}, {0x0d, 0x0000}, {0x0e, 0x0000},
- {0x0f, 0x0000}, {0x10, 0x0000}, {0x11, 0x0000},
- {0x12, 0x0000}, {0x13, 0x0000}, {0x14, 0x0000},
- {0x15, 0x0000}, {0x16, 0x0000}, {0x17, 0x0000},
- {0x18, 0x0000}, {0x19, 0x0000}, {0x1a, 0x0000},
- {0x1b, 0x0000}, {0x1c, 0x0000}, {0x1d, 0x0000},
- {0x32, 0x0000}, {0x20, 0x1101}, {0x21, 0x0000},
- {0x22, 0x0000}, {0x23, 0x0000}, {0x24, 0x0000},
- {0x25, 0x0000}, {0x26, 0x0000}, {0x27, 0x0024},
- {0x2f, 0xf7b0}, {0x30, 0x0005}, {0x31, 0x0000},
- {0x32, 0x0000}, {0x33, 0x0000}, {0x34, 0x0100},
- {0x3d, 0x068f}, {0x40, 0x01e0}, {0x41, 0x00d1},
- {0x44, 0x0082}, {0x5a, 0x0000}, {0x5b, 0x0000},
- {0x5c, 0x0000}, {0x5d, 0x0000}, {0x5e, 0x0000},
- {0x5f, 0xa31d}, {0x62, 0x0611}, {0x0a, 0x0000},
- {0x06, 0x0029}, {0x05, 0x0009}, {0x20, 0x1101},
- {0x20, 0x1101}, {0x09, 0x0064}, {0x07, 0x0003},
- {0x2b, 0x0033}, {0x2c, 0x00a0}, {0x2d, 0x00a0},
- {0x2e, 0x0033}, {0x07, 0x0002}, {0x06, 0x0000},
- {0x06, 0x0029}, {0x05, 0x0009},
-};
-
-static const struct i2c_reg_u16 mt9m001_init[] = {
- {0x0d, 0x0001},
- {0x0d, 0x0000},
- {0x04, 0x0500}, /* hres = 1280 */
- {0x03, 0x0400}, /* vres = 1024 */
- {0x20, 0x1100},
- {0x06, 0x0010},
- {0x2b, 0x0024},
- {0x2e, 0x0024},
- {0x35, 0x0024},
- {0x2d, 0x0020},
- {0x2c, 0x0020},
- {0x09, 0x0ad4},
- {0x35, 0x0057},
-};
-
-static const struct i2c_reg_u16 mt9m111_init[] = {
- {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
- {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
- {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
- {0xf0, 0x0000},
-};
-
-static const struct i2c_reg_u16 mt9m112_init[] = {
- {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
- {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
- {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
- {0xf0, 0x0000},
-};
-
-static const struct i2c_reg_u8 hv7131r_init[] = {
- {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
- {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
- {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
- {0x01, 0x08}, {0x01, 0x08}, {0x25, 0x07},
- {0x26, 0xc3}, {0x27, 0x50}, {0x30, 0x62},
- {0x31, 0x10}, {0x32, 0x06}, {0x33, 0x10},
- {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00},
- {0x23, 0x09}, {0x01, 0x08},
-};
-
-static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
-{
- struct usb_device *dev = gspca_dev->dev;
- int result;
-
- if (gspca_dev->usb_err < 0)
- return;
- result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- 0x00,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- reg,
- 0x00,
- gspca_dev->usb_buf,
- length,
- 500);
- if (unlikely(result < 0 || result != length)) {
- pr_err("Read register %02x failed %d\n", reg, result);
- gspca_dev->usb_err = result;
- }
-}
-
-static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
- const u8 *buffer, int length)
-{
- struct usb_device *dev = gspca_dev->dev;
- int result;
-
- if (gspca_dev->usb_err < 0)
- return;
- memcpy(gspca_dev->usb_buf, buffer, length);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- reg,
- 0x00,
- gspca_dev->usb_buf,
- length,
- 500);
- if (unlikely(result < 0 || result != length)) {
- pr_err("Write register %02x failed %d\n", reg, result);
- gspca_dev->usb_err = result;
- }
-}
-
-static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
-{
- reg_w(gspca_dev, reg, &value, 1);
-}
-
-static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
-{
- int i;
-
- reg_w(gspca_dev, 0x10c0, buffer, 8);
- for (i = 0; i < 5; i++) {
- reg_r(gspca_dev, 0x10c0, 1);
- if (gspca_dev->usb_err < 0)
- return;
- if (gspca_dev->usb_buf[0] & 0x04) {
- if (gspca_dev->usb_buf[0] & 0x08) {
- pr_err("i2c_w error\n");
- gspca_dev->usb_err = -EIO;
- }
- return;
- }
- msleep(10);
- }
- pr_err("i2c_w reg %02x no response\n", buffer[2]);
-/* gspca_dev->usb_err = -EIO; fixme: may occur */
-}
-
-static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 row[8];
-
- /*
- * from the point of view of the bridge, the length
- * includes the address
- */
- row[0] = sd->i2c_intf | (2 << 4);
- row[1] = sd->i2c_addr;
- row[2] = reg;
- row[3] = val;
- row[4] = 0x00;
- row[5] = 0x00;
- row[6] = 0x00;
- row[7] = 0x10;
-
- i2c_w(gspca_dev, row);
-}
-
-static void i2c_w1_buf(struct gspca_dev *gspca_dev,
- const struct i2c_reg_u8 *buf, int sz)
-{
- while (--sz >= 0) {
- i2c_w1(gspca_dev, buf->reg, buf->val);
- buf++;
- }
-}
-
-static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 row[8];
-
- /*
- * from the point of view of the bridge, the length
- * includes the address
- */
- row[0] = sd->i2c_intf | (3 << 4);
- row[1] = sd->i2c_addr;
- row[2] = reg;
- row[3] = val >> 8;
- row[4] = val;
- row[5] = 0x00;
- row[6] = 0x00;
- row[7] = 0x10;
-
- i2c_w(gspca_dev, row);
-}
-
-static void i2c_w2_buf(struct gspca_dev *gspca_dev,
- const struct i2c_reg_u16 *buf, int sz)
-{
- while (--sz >= 0) {
- i2c_w2(gspca_dev, buf->reg, buf->val);
- buf++;
- }
-}
-
-static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 row[8];
-
- row[0] = sd->i2c_intf | (1 << 4);
- row[1] = sd->i2c_addr;
- row[2] = reg;
- row[3] = 0;
- row[4] = 0;
- row[5] = 0;
- row[6] = 0;
- row[7] = 0x10;
- i2c_w(gspca_dev, row);
- row[0] = sd->i2c_intf | (1 << 4) | 0x02;
- row[2] = 0;
- i2c_w(gspca_dev, row);
- reg_r(gspca_dev, 0x10c2, 5);
- *val = gspca_dev->usb_buf[4];
-}
-
-static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 row[8];
-
- row[0] = sd->i2c_intf | (1 << 4);
- row[1] = sd->i2c_addr;
- row[2] = reg;
- row[3] = 0;
- row[4] = 0;
- row[5] = 0;
- row[6] = 0;
- row[7] = 0x10;
- i2c_w(gspca_dev, row);
- row[0] = sd->i2c_intf | (2 << 4) | 0x02;
- row[2] = 0;
- i2c_w(gspca_dev, row);
- reg_r(gspca_dev, 0x10c2, 5);
- *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
-}
-
-static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
-{
- u16 id;
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_r2(gspca_dev, 0x1c, &id);
- if (gspca_dev->usb_err < 0)
- return;
-
- if (id != 0x7fa2) {
- pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
- gspca_dev->usb_err = -ENODEV;
- return;
- }
-
- i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(200);
- i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
- if (gspca_dev->usb_err < 0)
- pr_err("OV9650 sensor initialization failed\n");
- sd->hstart = 1;
- sd->vstart = 7;
-}
-
-static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(200);
- i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
- if (gspca_dev->usb_err < 0)
- pr_err("OV9655 sensor initialization failed\n");
-
- sd->hstart = 1;
- sd->vstart = 2;
-}
-
-static void soi968_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(200);
- i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
- if (gspca_dev->usb_err < 0)
- pr_err("SOI968 sensor initialization failed\n");
-
- sd->hstart = 60;
- sd->vstart = 11;
-}
-
-static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(200);
- i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
- if (gspca_dev->usb_err < 0)
- pr_err("OV7660 sensor initialization failed\n");
- sd->hstart = 3;
- sd->vstart = 3;
-}
-
-static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(200);
- i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
- if (gspca_dev->usb_err < 0)
- pr_err("OV7670 sensor initialization failed\n");
-
- sd->hstart = 0;
- sd->vstart = 1;
-}
-
-static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 value;
-
- sd->i2c_addr = 0x5d;
- i2c_r2(gspca_dev, 0xff, &value);
- if (gspca_dev->usb_err >= 0
- && value == 0x8243) {
- i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
- if (gspca_dev->usb_err < 0) {
- pr_err("MT9V011 sensor initialization failed\n");
- return;
- }
- sd->hstart = 2;
- sd->vstart = 2;
- sd->sensor = SENSOR_MT9V011;
- pr_info("MT9V011 sensor detected\n");
- return;
- }
-
- gspca_dev->usb_err = 0;
- sd->i2c_addr = 0x5c;
- i2c_w2(gspca_dev, 0x01, 0x0004);
- i2c_r2(gspca_dev, 0xff, &value);
- if (gspca_dev->usb_err >= 0
- && value == 0x823a) {
- i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
- if (gspca_dev->usb_err < 0) {
- pr_err("MT9V111 sensor initialization failed\n");
- return;
- }
- sd->hstart = 2;
- sd->vstart = 2;
- sd->sensor = SENSOR_MT9V111;
- pr_info("MT9V111 sensor detected\n");
- return;
- }
-
- gspca_dev->usb_err = 0;
- sd->i2c_addr = 0x5d;
- i2c_w2(gspca_dev, 0xf0, 0x0000);
- if (gspca_dev->usb_err < 0) {
- gspca_dev->usb_err = 0;
- sd->i2c_addr = 0x48;
- i2c_w2(gspca_dev, 0xf0, 0x0000);
- }
- i2c_r2(gspca_dev, 0x00, &value);
- if (gspca_dev->usb_err >= 0
- && value == 0x1229) {
- i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
- if (gspca_dev->usb_err < 0) {
- pr_err("MT9V112 sensor initialization failed\n");
- return;
- }
- sd->hstart = 6;
- sd->vstart = 2;
- sd->sensor = SENSOR_MT9V112;
- pr_info("MT9V112 sensor detected\n");
- return;
- }
-
- gspca_dev->usb_err = -ENODEV;
-}
-
-static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
- if (gspca_dev->usb_err < 0)
- pr_err("MT9M112 sensor initialization failed\n");
-
- sd->hstart = 0;
- sd->vstart = 2;
-}
-
-static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
- if (gspca_dev->usb_err < 0)
- pr_err("MT9M111 sensor initialization failed\n");
-
- sd->hstart = 0;
- sd->vstart = 2;
-}
-
-static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 id;
-
- i2c_r2(gspca_dev, 0x00, &id);
- if (gspca_dev->usb_err < 0)
- return;
-
- /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
- switch (id) {
- case 0x8411:
- case 0x8421:
- pr_info("MT9M001 color sensor detected\n");
- break;
- case 0x8431:
- pr_info("MT9M001 mono sensor detected\n");
- break;
- default:
- pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
- gspca_dev->usb_err = -ENODEV;
- return;
- }
-
- i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
- if (gspca_dev->usb_err < 0)
- pr_err("MT9M001 sensor initialization failed\n");
-
- sd->hstart = 1;
- sd->vstart = 1;
-}
-
-static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
- if (gspca_dev->usb_err < 0)
- pr_err("HV7131R Sensor initialization failed\n");
-
- sd->hstart = 0;
- sd->vstart = 1;
-}
-
-static void set_cmatrix(struct gspca_dev *gspca_dev,
- s32 brightness, s32 contrast, s32 satur, s32 hue)
-{
- s32 hue_coord, hue_index = 180 + hue;
- u8 cmatrix[21];
-
- memset(cmatrix, 0, sizeof cmatrix);
- cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
- cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
- cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
- cmatrix[18] = brightness - 0x80;
-
- hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
- cmatrix[6] = hue_coord;
- cmatrix[7] = (hue_coord >> 8) & 0x0f;
-
- hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
- cmatrix[8] = hue_coord;
- cmatrix[9] = (hue_coord >> 8) & 0x0f;
-
- hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
- cmatrix[10] = hue_coord;
- cmatrix[11] = (hue_coord >> 8) & 0x0f;
-
- hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
- cmatrix[12] = hue_coord;
- cmatrix[13] = (hue_coord >> 8) & 0x0f;
-
- hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
- cmatrix[14] = hue_coord;
- cmatrix[15] = (hue_coord >> 8) & 0x0f;
-
- hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
- cmatrix[16] = hue_coord;
- cmatrix[17] = (hue_coord >> 8) & 0x0f;
-
- reg_w(gspca_dev, 0x10e1, cmatrix, 21);
-}
-
-static void set_gamma(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 gamma[17];
- u8 gval = val * 0xb8 / 0x100;
-
- gamma[0] = 0x0a;
- gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
- gamma[2] = 0x25 + (gval * (0xee - 0x25) / 0xb8);
- gamma[3] = 0x37 + (gval * (0xfa - 0x37) / 0xb8);
- gamma[4] = 0x45 + (gval * (0xfc - 0x45) / 0xb8);
- gamma[5] = 0x55 + (gval * (0xfb - 0x55) / 0xb8);
- gamma[6] = 0x65 + (gval * (0xfc - 0x65) / 0xb8);
- gamma[7] = 0x74 + (gval * (0xfd - 0x74) / 0xb8);
- gamma[8] = 0x83 + (gval * (0xfe - 0x83) / 0xb8);
- gamma[9] = 0x92 + (gval * (0xfc - 0x92) / 0xb8);
- gamma[10] = 0xa1 + (gval * (0xfc - 0xa1) / 0xb8);
- gamma[11] = 0xb0 + (gval * (0xfc - 0xb0) / 0xb8);
- gamma[12] = 0xbf + (gval * (0xfb - 0xbf) / 0xb8);
- gamma[13] = 0xce + (gval * (0xfb - 0xce) / 0xb8);
- gamma[14] = 0xdf + (gval * (0xfd - 0xdf) / 0xb8);
- gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
- gamma[16] = 0xf5;
-
- reg_w(gspca_dev, 0x1190, gamma, 17);
-}
-
-static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red)
-{
- reg_w1(gspca_dev, 0x118c, red);
- reg_w1(gspca_dev, 0x118f, blue);
-}
-
-static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
-{
- u8 value, tslb;
- u16 value2;
- struct sd *sd = (struct sd *) gspca_dev;
-
- if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
- hflip = !hflip;
- vflip = !vflip;
- }
-
- 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;
- tslb = 0x01;
- if (hflip)
- value |= 0x20;
- if (vflip) {
- value |= 0x10;
- tslb = 0x49;
- }
- i2c_w1(gspca_dev, 0x1e, value);
- i2c_w1(gspca_dev, 0x3a, tslb);
- break;
- case SENSOR_MT9V111:
- case SENSOR_MT9V011:
- i2c_r2(gspca_dev, 0x20, &value2);
- value2 &= ~0xc0a0;
- if (hflip)
- value2 |= 0x8080;
- if (vflip)
- value2 |= 0x4020;
- i2c_w2(gspca_dev, 0x20, value2);
- break;
- case SENSOR_MT9M112:
- case SENSOR_MT9M111:
- case SENSOR_MT9V112:
- i2c_r2(gspca_dev, 0x20, &value2);
- value2 &= ~0x0003;
- if (hflip)
- value2 |= 0x0002;
- if (vflip)
- value2 |= 0x0001;
- i2c_w2(gspca_dev, 0x20, value2);
- break;
- case SENSOR_HV7131R:
- i2c_r1(gspca_dev, 0x01, &value);
- value &= ~0x03;
- if (vflip)
- value |= 0x01;
- if (hflip)
- value |= 0x02;
- i2c_w1(gspca_dev, 0x01, value);
- break;
- }
-}
-
-static void set_exposure(struct gspca_dev *gspca_dev, s32 expo)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 exp[8] = {sd->i2c_intf, sd->i2c_addr,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
- int expo2;
-
- if (gspca_dev->streaming)
- exp[7] = 0x1e;
-
- switch (sd->sensor) {
- case SENSOR_OV7660:
- case SENSOR_OV7670:
- case SENSOR_OV9655:
- case SENSOR_OV9650:
- if (expo > 547)
- expo2 = 547;
- else
- expo2 = expo;
- exp[0] |= (2 << 4);
- exp[2] = 0x10; /* AECH */
- exp[3] = expo2 >> 2;
- exp[7] = 0x10;
- i2c_w(gspca_dev, exp);
- exp[2] = 0x04; /* COM1 */
- exp[3] = expo2 & 0x0003;
- exp[7] = 0x10;
- i2c_w(gspca_dev, exp);
- expo -= expo2;
- exp[7] = 0x1e;
- exp[0] |= (3 << 4);
- exp[2] = 0x2d; /* ADVFL & ADVFH */
- exp[3] = expo;
- exp[4] = expo >> 8;
- break;
- case SENSOR_MT9M001:
- case SENSOR_MT9V112:
- case SENSOR_MT9V011:
- exp[0] |= (3 << 4);
- exp[2] = 0x09;
- exp[3] = expo >> 8;
- exp[4] = expo;
- break;
- case SENSOR_HV7131R:
- exp[0] |= (4 << 4);
- exp[2] = 0x25;
- exp[3] = expo >> 5;
- exp[4] = expo << 3;
- exp[5] = 0;
- break;
- default:
- return;
- }
- i2c_w(gspca_dev, exp);
-}
-
-static void set_gain(struct gspca_dev *gspca_dev, s32 g)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 gain[8] = {sd->i2c_intf, sd->i2c_addr,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
-
- if (gspca_dev->streaming)
- gain[7] = 0x15; /* or 1d ? */
-
- switch (sd->sensor) {
- case SENSOR_OV7660:
- case SENSOR_OV7670:
- case SENSOR_SOI968:
- case SENSOR_OV9655:
- case SENSOR_OV9650:
- gain[0] |= (2 << 4);
- gain[3] = ov_gain[g];
- break;
- case SENSOR_MT9V011:
- gain[0] |= (3 << 4);
- gain[2] = 0x35;
- gain[3] = micron1_gain[g] >> 8;
- gain[4] = micron1_gain[g];
- break;
- case SENSOR_MT9V112:
- gain[0] |= (3 << 4);
- gain[2] = 0x2f;
- gain[3] = micron1_gain[g] >> 8;
- gain[4] = micron1_gain[g];
- break;
- case SENSOR_MT9M001:
- gain[0] |= (3 << 4);
- gain[2] = 0x2f;
- gain[3] = micron2_gain[g] >> 8;
- gain[4] = micron2_gain[g];
- break;
- case SENSOR_HV7131R:
- gain[0] |= (2 << 4);
- gain[2] = 0x30;
- gain[3] = hv7131r_gain[g];
- break;
- default:
- return;
- }
- i2c_w(gspca_dev, gain);
-}
-
-static void set_quality(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- jpeg_set_qual(sd->jpeg_hdr, val);
- reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
- reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
- reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
- reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
- reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */
- reg_w1(gspca_dev, 0x10e0, sd->fmt);
- sd->fmt ^= 0x0c; /* invert QTAB use + write */
- reg_w1(gspca_dev, 0x10e0, sd->fmt);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_register *reg)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (reg->match.addr != 0)
- return -EINVAL;
- if (reg->reg < 0x1000 || reg->reg > 0x11ff)
- return -EINVAL;
- reg_r(gspca_dev, reg->reg, 1);
- reg->val = gspca_dev->usb_buf[0];
- return gspca_dev->usb_err;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (reg->match.addr != sd->i2c_addr)
- return -EINVAL;
- if (sd->sensor >= SENSOR_MT9V011 &&
- sd->sensor <= SENSOR_MT9M112) {
- i2c_r2(gspca_dev, reg->reg, (u16 *) &reg->val);
- } else {
- i2c_r1(gspca_dev, reg->reg, (u8 *) &reg->val);
- }
- return gspca_dev->usb_err;
- }
- return -EINVAL;
-}
-
-static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_register *reg)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (reg->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (reg->match.addr != 0)
- return -EINVAL;
- if (reg->reg < 0x1000 || reg->reg > 0x11ff)
- return -EINVAL;
- reg_w1(gspca_dev, reg->reg, reg->val);
- return gspca_dev->usb_err;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (reg->match.addr != sd->i2c_addr)
- return -EINVAL;
- if (sd->sensor >= SENSOR_MT9V011 &&
- sd->sensor <= SENSOR_MT9M112) {
- i2c_w2(gspca_dev, reg->reg, reg->val);
- } else {
- i2c_w1(gspca_dev, reg->reg, reg->val);
- }
- return gspca_dev->usb_err;
- }
- return -EINVAL;
-}
-#endif
-
-static int sd_chip_ident(struct gspca_dev *gspca_dev,
- struct v4l2_dbg_chip_ident *chip)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (chip->match.type) {
- case V4L2_CHIP_MATCH_HOST:
- if (chip->match.addr != 0)
- return -EINVAL;
- chip->revision = 0;
- chip->ident = V4L2_IDENT_SN9C20X;
- return 0;
- case V4L2_CHIP_MATCH_I2C_ADDR:
- if (chip->match.addr != sd->i2c_addr)
- return -EINVAL;
- chip->revision = 0;
- chip->ident = i2c_ident[sd->sensor];
- return 0;
- }
- return -EINVAL;
-}
-
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- cam = &gspca_dev->cam;
- cam->needs_full_bandwidth = 1;
-
- sd->sensor = id->driver_info >> 8;
- sd->i2c_addr = id->driver_info;
- sd->flags = id->driver_info >> 16;
- sd->i2c_intf = 0x80; /* i2c 100 Kb/s */
-
- switch (sd->sensor) {
- case SENSOR_MT9M112:
- case SENSOR_MT9M111:
- case SENSOR_OV9650:
- case SENSOR_SOI968:
- cam->cam_mode = sxga_mode;
- cam->nmodes = ARRAY_SIZE(sxga_mode);
- break;
- case SENSOR_MT9M001:
- cam->cam_mode = mono_mode;
- cam->nmodes = ARRAY_SIZE(mono_mode);
- break;
- case SENSOR_HV7131R:
- sd->i2c_intf = 0x81; /* i2c 400 Kb/s */
- /* fall thru */
- default:
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- break;
- }
-
- sd->old_step = 0;
- sd->older_step = 0;
- sd->exposure_step = 16;
-
- INIT_WORK(&sd->work, qual_upd);
-
- return 0;
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- /* color control cluster */
- case V4L2_CID_BRIGHTNESS:
- set_cmatrix(gspca_dev, sd->brightness->val,
- sd->contrast->val, sd->saturation->val, sd->hue->val);
- break;
- case V4L2_CID_GAMMA:
- set_gamma(gspca_dev, ctrl->val);
- break;
- /* blue/red balance cluster */
- case V4L2_CID_BLUE_BALANCE:
- set_redblue(gspca_dev, sd->blue->val, sd->red->val);
- break;
- /* h/vflip cluster */
- case V4L2_CID_HFLIP:
- set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
- break;
- /* standalone exposure control */
- case V4L2_CID_EXPOSURE:
- set_exposure(gspca_dev, ctrl->val);
- break;
- /* standalone gain control */
- case V4L2_CID_GAIN:
- set_gain(gspca_dev, ctrl->val);
- break;
- /* autogain + exposure or gain control cluster */
- case V4L2_CID_AUTOGAIN:
- if (sd->sensor == SENSOR_SOI968)
- set_gain(gspca_dev, sd->gain->val);
- else
- set_exposure(gspca_dev, sd->exposure->val);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- set_quality(gspca_dev, ctrl->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 13);
-
- sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
- sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 127);
- sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 127);
- sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HUE, -180, 180, 1, 0);
-
- sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAMMA, 0, 255, 1, 0x10);
-
- sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28);
- sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28);
-
- if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 &&
- sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 &&
- sd->sensor != SENSOR_MT9VPRB) {
- sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- }
-
- if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB &&
- sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 &&
- sd->sensor != SENSOR_MT9V111)
- sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33);
-
- if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 &&
- sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) {
- sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 28, 1, 0);
- sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- }
-
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80);
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
-
- v4l2_ctrl_cluster(4, &sd->brightness);
- v4l2_ctrl_cluster(2, &sd->blue);
- if (sd->hflip)
- v4l2_ctrl_cluster(2, &sd->hflip);
- if (sd->autogain) {
- if (sd->sensor == SENSOR_SOI968)
- /* this sensor doesn't have the exposure control and
- autogain is clustered with gain instead. This works
- because sd->exposure == NULL. */
- v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false);
- else
- /* Otherwise autogain is clustered with exposure. */
- v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
- }
- return 0;
-}
-
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
- u8 value;
- u8 i2c_init[9] =
- {0x80, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03};
-
- for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
- value = bridge_init[i][1];
- reg_w(gspca_dev, bridge_init[i][0], &value, 1);
- if (gspca_dev->usb_err < 0) {
- pr_err("Device initialization failed\n");
- return gspca_dev->usb_err;
- }
- }
-
- if (sd->flags & LED_REVERSE)
- reg_w1(gspca_dev, 0x1006, 0x00);
- else
- reg_w1(gspca_dev, 0x1006, 0x20);
-
- reg_w(gspca_dev, 0x10c0, i2c_init, 9);
- if (gspca_dev->usb_err < 0) {
- pr_err("Device initialization failed\n");
- return gspca_dev->usb_err;
- }
-
- switch (sd->sensor) {
- case SENSOR_OV9650:
- ov9650_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("OV9650 sensor detected\n");
- break;
- case SENSOR_OV9655:
- ov9655_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("OV9655 sensor detected\n");
- break;
- case SENSOR_SOI968:
- soi968_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("SOI968 sensor detected\n");
- break;
- case SENSOR_OV7660:
- ov7660_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("OV7660 sensor detected\n");
- break;
- case SENSOR_OV7670:
- ov7670_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("OV7670 sensor detected\n");
- break;
- case SENSOR_MT9VPRB:
- mt9v_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("MT9VPRB sensor detected\n");
- break;
- case SENSOR_MT9M111:
- mt9m111_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("MT9M111 sensor detected\n");
- break;
- case SENSOR_MT9M112:
- mt9m112_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("MT9M112 sensor detected\n");
- break;
- case SENSOR_MT9M001:
- mt9m001_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- break;
- case SENSOR_HV7131R:
- hv7131r_init_sensor(gspca_dev);
- if (gspca_dev->usb_err < 0)
- break;
- pr_info("HV7131R sensor detected\n");
- break;
- default:
- pr_err("Unsupported sensor\n");
- gspca_dev->usb_err = -ENODEV;
- }
- return gspca_dev->usb_err;
-}
-
-static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 value;
-
- switch (sd->sensor) {
- case SENSOR_SOI968:
- if (mode & MODE_SXGA) {
- i2c_w1(gspca_dev, 0x17, 0x1d);
- i2c_w1(gspca_dev, 0x18, 0xbd);
- i2c_w1(gspca_dev, 0x19, 0x01);
- i2c_w1(gspca_dev, 0x1a, 0x81);
- i2c_w1(gspca_dev, 0x12, 0x00);
- sd->hstart = 140;
- sd->vstart = 19;
- } else {
- i2c_w1(gspca_dev, 0x17, 0x13);
- i2c_w1(gspca_dev, 0x18, 0x63);
- i2c_w1(gspca_dev, 0x19, 0x01);
- i2c_w1(gspca_dev, 0x1a, 0x79);
- i2c_w1(gspca_dev, 0x12, 0x40);
- sd->hstart = 60;
- sd->vstart = 11;
- }
- break;
- case SENSOR_OV9650:
- if (mode & MODE_SXGA) {
- i2c_w1(gspca_dev, 0x17, 0x1b);
- i2c_w1(gspca_dev, 0x18, 0xbc);
- i2c_w1(gspca_dev, 0x19, 0x01);
- i2c_w1(gspca_dev, 0x1a, 0x82);
- i2c_r1(gspca_dev, 0x12, &value);
- i2c_w1(gspca_dev, 0x12, value & 0x07);
- } else {
- i2c_w1(gspca_dev, 0x17, 0x24);
- i2c_w1(gspca_dev, 0x18, 0xc5);
- i2c_w1(gspca_dev, 0x19, 0x00);
- i2c_w1(gspca_dev, 0x1a, 0x3c);
- i2c_r1(gspca_dev, 0x12, &value);
- i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40);
- }
- break;
- case SENSOR_MT9M112:
- case SENSOR_MT9M111:
- if (mode & MODE_SXGA) {
- i2c_w2(gspca_dev, 0xf0, 0x0002);
- i2c_w2(gspca_dev, 0xc8, 0x970b);
- i2c_w2(gspca_dev, 0xf0, 0x0000);
- } else {
- i2c_w2(gspca_dev, 0xf0, 0x0002);
- i2c_w2(gspca_dev, 0xc8, 0x8000);
- i2c_w2(gspca_dev, 0xf0, 0x0000);
- }
- break;
- }
-}
-
-static int sd_isoc_init(struct gspca_dev *gspca_dev)
-{
- struct usb_interface *intf;
- u32 flags = gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv;
-
- /*
- * When using the SN9C20X_I420 fmt the sn9c20x needs more bandwidth
- * than our regular bandwidth calculations reserve, so we force the
- * use of a specific altsetting when using the SN9C20X_I420 fmt.
- */
- if (!(flags & (MODE_RAW | MODE_JPEG))) {
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
-
- if (intf->num_altsetting != 9) {
- pr_warn("sn9c20x camera with unknown number of alt "
- "settings (%d), please report!\n",
- intf->num_altsetting);
- gspca_dev->alt = intf->num_altsetting;
- return 0;
- }
-
- switch (gspca_dev->width) {
- case 160: /* 160x120 */
- gspca_dev->alt = 2;
- break;
- case 320: /* 320x240 */
- gspca_dev->alt = 6;
- break;
- default: /* >= 640x480 */
- gspca_dev->alt = 9;
- break;
- }
- }
-
- return 0;
-}
-
-#define HW_WIN(mode, hstart, vstart) \
-((const u8 []){hstart, 0, vstart, 0, \
-(mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
-(mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
-
-#define CLR_WIN(width, height) \
-((const u8 [])\
-{0, width >> 2, 0, height >> 1,\
-((width >> 10) & 0x01) | ((height >> 8) & 0x6)})
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
- int width = gspca_dev->width;
- int height = gspca_dev->height;
- u8 fmt, scale = 0;
-
- jpeg_define(sd->jpeg_hdr, height, width,
- 0x21);
- jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
-
- if (mode & MODE_RAW)
- fmt = 0x2d;
- else if (mode & MODE_JPEG)
- fmt = 0x24;
- else
- fmt = 0x2f; /* YUV 420 */
- sd->fmt = fmt;
-
- switch (mode & SCALE_MASK) {
- case SCALE_1280x1024:
- scale = 0xc0;
- pr_info("Set 1280x1024\n");
- break;
- case SCALE_640x480:
- scale = 0x80;
- pr_info("Set 640x480\n");
- break;
- case SCALE_320x240:
- scale = 0x90;
- pr_info("Set 320x240\n");
- break;
- case SCALE_160x120:
- scale = 0xa0;
- pr_info("Set 160x120\n");
- break;
- }
-
- configure_sensor_output(gspca_dev, mode);
- reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
- reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
- reg_w(gspca_dev, 0x10fb, CLR_WIN(width, height), 5);
- reg_w(gspca_dev, 0x1180, HW_WIN(mode, sd->hstart, sd->vstart), 6);
- reg_w1(gspca_dev, 0x1189, scale);
- reg_w1(gspca_dev, 0x10e0, fmt);
-
- set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness),
- v4l2_ctrl_g_ctrl(sd->contrast),
- v4l2_ctrl_g_ctrl(sd->saturation),
- v4l2_ctrl_g_ctrl(sd->hue));
- set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
- set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- if (sd->gain)
- set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
- if (sd->exposure)
- set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
- if (sd->hflip)
- set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
- v4l2_ctrl_g_ctrl(sd->vflip));
-
- reg_w1(gspca_dev, 0x1007, 0x20);
- reg_w1(gspca_dev, 0x1061, 0x03);
-
- /* if JPEG, prepare the compression quality update */
- if (mode & MODE_JPEG) {
- sd->pktsz = sd->npkt = 0;
- sd->nchg = 0;
- sd->work_thread =
- create_singlethread_workqueue(KBUILD_MODNAME);
- }
-
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- reg_w1(gspca_dev, 0x1007, 0x00);
- reg_w1(gspca_dev, 0x1061, 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_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure);
- s32 max = sd->exposure->maximum - sd->exposure_step;
- s32 min = sd->exposure->minimum + sd->exposure_step;
- s16 new_exp;
-
- /*
- * some hardcoded values are present
- * like those for maximal/minimal exposure
- * and exposure steps
- */
- if (avg_lum < MIN_AVG_LUM) {
- if (cur_exp > max)
- return;
-
- new_exp = cur_exp + sd->exposure_step;
- if (new_exp > max)
- new_exp = max;
- if (new_exp < min)
- new_exp = min;
- v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
-
- sd->older_step = sd->old_step;
- sd->old_step = 1;
-
- if (sd->old_step ^ sd->older_step)
- sd->exposure_step /= 2;
- else
- sd->exposure_step += 2;
- }
- if (avg_lum > MAX_AVG_LUM) {
- if (cur_exp < min)
- return;
- new_exp = cur_exp - sd->exposure_step;
- if (new_exp > max)
- new_exp = max;
- if (new_exp < min)
- new_exp = min;
- v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
- sd->older_step = sd->old_step;
- sd->old_step = 0;
-
- if (sd->old_step ^ sd->older_step)
- sd->exposure_step /= 2;
- else
- sd->exposure_step += 2;
- }
-}
-
-static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
-
- if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum)
- v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1);
- if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum)
- v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1);
-}
-
-static void sd_dqcallback(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int avg_lum;
-
- if (sd->autogain == NULL || !v4l2_ctrl_g_ctrl(sd->autogain))
- return;
-
- avg_lum = atomic_read(&sd->avg_lum);
- if (sd->sensor == SENSOR_SOI968)
- do_autogain(gspca_dev, avg_lum);
- else
- do_autoexposure(gspca_dev, avg_lum);
-}
-
-/* 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;
- s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual);
-
- mutex_lock(&gspca_dev->usb_lock);
- PDEBUG(D_STREAM, "qual_upd %d%%", qual);
- set_quality(gspca_dev, qual);
- mutex_unlock(&gspca_dev->usb_lock);
-}
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* interrupt packet */
- int len) /* interrupt packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
- input_sync(gspca_dev->input_dev);
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- return 0;
- }
- return -EINVAL;
-}
-#endif
-
-/* check the JPEG compression */
-static void transfer_check(struct gspca_dev *gspca_dev,
- u8 *data)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int new_qual, r;
-
- new_qual = 0;
-
- /* if USB error, discard the frame and decrease the quality */
- if (data[6] & 0x08) { /* USB FIFO full */
- gspca_dev->last_packet_type = DISCARD_PACKET;
- new_qual = -5;
- } else {
-
- /* else, compute the filling rate and a new JPEG quality */
- 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) {
- /* Note: we are in interrupt context, so we can't
- use v4l2_ctrl_g/s_ctrl here. Access the value
- directly instead. */
- s32 curqual = sd->jpegqual->cur.val;
- sd->nchg = 0;
- new_qual += curqual;
- if (new_qual < sd->jpegqual->minimum)
- new_qual = sd->jpegqual->minimum;
- else if (new_qual > sd->jpegqual->maximum)
- new_qual = sd->jpegqual->maximum;
- if (new_qual != curqual) {
- sd->jpegqual->cur.val = new_qual;
- queue_work(sd->work_thread, &sd->work);
- }
- }
- } else {
- sd->nchg = 0;
- }
- sd->pktsz = sd->npkt = 0;
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int avg_lum, is_jpeg;
- static const u8 frame_header[] =
- {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
-
- is_jpeg = (sd->fmt & 0x03) == 0;
- if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
- avg_lum = ((data[35] >> 2) & 3) |
- (data[20] << 2) |
- (data[19] << 10);
- avg_lum += ((data[35] >> 4) & 3) |
- (data[22] << 2) |
- (data[21] << 10);
- avg_lum += ((data[35] >> 6) & 3) |
- (data[24] << 2) |
- (data[23] << 10);
- avg_lum += (data[36] & 3) |
- (data[26] << 2) |
- (data[25] << 10);
- avg_lum += ((data[36] >> 2) & 3) |
- (data[28] << 2) |
- (data[27] << 10);
- avg_lum += ((data[36] >> 4) & 3) |
- (data[30] << 2) |
- (data[29] << 10);
- avg_lum += ((data[36] >> 6) & 3) |
- (data[32] << 2) |
- (data[31] << 10);
- avg_lum += ((data[44] >> 4) & 3) |
- (data[34] << 2) |
- (data[33] << 10);
- avg_lum >>= 9;
- atomic_set(&sd->avg_lum, avg_lum);
-
- if (is_jpeg)
- transfer_check(gspca_dev, data);
-
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- len -= 64;
- if (len == 0)
- return;
- data += 64;
- }
- if (gspca_dev->last_packet_type == LAST_PACKET) {
- if (is_jpeg) {
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data, len);
- } else {
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- data, len);
- }
- } else {
- /* if JPEG, count the packets and their size */
- if (is_jpeg) {
- sd->npkt++;
- sd->pktsz += len;
- }
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
- }
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = KBUILD_MODNAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .isoc_init = sd_isoc_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .int_pkt_scan = sd_int_pkt_scan,
-#endif
- .dq_callback = sd_dqcallback,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .set_register = sd_dbg_s_register,
- .get_register = sd_dbg_g_register,
-#endif
- .get_chip_ident = sd_chip_ident,
-};
-
-#define SN9C20X(sensor, i2c_addr, flags) \
- .driver_info = ((flags & 0xff) << 16) \
- | (SENSOR_ ## sensor << 8) \
- | (i2c_addr)
-
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x0c45, 0x6240), SN9C20X(MT9M001, 0x5d, 0)},
- {USB_DEVICE(0x0c45, 0x6242), SN9C20X(MT9M111, 0x5d, 0)},
- {USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
- {USB_DEVICE(0x0c45, 0x624c), SN9C20X(MT9M112, 0x5d, 0)},
- {USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30, LED_REVERSE)},
- {USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30,
- (FLIP_DETECT | HAS_NO_BUTTON))},
- {USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
- {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, 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)},
- {USB_DEVICE(0x0c45, 0x6282), SN9C20X(MT9M111, 0x5d, 0)},
- {USB_DEVICE(0x0c45, 0x6288), SN9C20X(OV9655, 0x30, 0)},
- {USB_DEVICE(0x0c45, 0x628c), SN9C20X(MT9M112, 0x5d, 0)},
- {USB_DEVICE(0x0c45, 0x628e), SN9C20X(SOI968, 0x30, 0)},
- {USB_DEVICE(0x0c45, 0x628f), SN9C20X(OV9650, 0x30, 0)},
- {USB_DEVICE(0x0c45, 0x62a0), SN9C20X(OV7670, 0x21, 0)},
- {USB_DEVICE(0x0c45, 0x62b0), SN9C20X(MT9VPRB, 0x00, 0)},
- {USB_DEVICE(0x0c45, 0x62b3), SN9C20X(OV9655, 0x30, LED_REVERSE)},
- {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)},
- {USB_DEVICE(0x0458, 0x7029), SN9C20X(HV7131R, 0x11, 0)},
- {USB_DEVICE(0x0458, 0x704a), SN9C20X(MT9M112, 0x5d, 0)},
- {USB_DEVICE(0x0458, 0x704c), SN9C20X(MT9M112, 0x5d, 0)},
- {USB_DEVICE(0xa168, 0x0610), SN9C20X(HV7131R, 0x11, 0)},
- {USB_DEVICE(0xa168, 0x0611), SN9C20X(HV7131R, 0x11, 0)},
- {USB_DEVICE(0xa168, 0x0613), SN9C20X(HV7131R, 0x11, 0)},
- {USB_DEVICE(0xa168, 0x0618), SN9C20X(HV7131R, 0x11, 0)},
- {USB_DEVICE(0xa168, 0x0614), SN9C20X(MT9M111, 0x5d, 0)},
- {USB_DEVICE(0xa168, 0x0615), SN9C20X(MT9M111, 0x5d, 0)},
- {USB_DEVICE(0xa168, 0x0617), SN9C20X(MT9M111, 0x5d, 0)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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 = KBUILD_MODNAME,
- .id_table = device_table,
- .probe = sd_probe,
- .disconnect = gspca_disconnect,
-#ifdef CONFIG_PM
- .suspend = gspca_suspend,
- .resume = gspca_resume,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
deleted file mode 100644
index 150b2df40f7f..000000000000
--- a/drivers/media/video/gspca/sonixj.c
+++ /dev/null
@@ -1,3206 +0,0 @@
-/*
- * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
- *
- * Copyright (C) 2009-2011 Jean-François Moine <http://moinejf.free.fr>
- * Copyright (C) 2005 Michel Xhaard mxhaard@magic.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; 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "sonixj"
-
-#include <linux/input.h>
-#include "gspca.h"
-#include "jpeg.h"
-
-MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
-MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- COLORS,
- BLUE,
- RED,
- GAMMA,
- EXPOSURE,
- AUTOGAIN,
- GAIN,
- HFLIP,
- VFLIP,
- SHARPNESS,
- ILLUM,
- FREQ,
- NCTRLS /* number of controls */
-};
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct gspca_ctrl ctrls[NCTRLS];
-
- atomic_t avg_lum;
- u32 exposure;
-
- struct work_struct work;
- struct workqueue_struct *work_thread;
-
- u32 pktsz; /* (used by pkt_scan) */
- u16 npkt;
- s8 nchg;
- s8 short_mark;
-
- u8 quality; /* image quality */
-#define QUALITY_MIN 25
-#define QUALITY_MAX 90
-#define QUALITY_DEF 70
-
- u8 reg01;
- u8 reg17;
- u8 reg18;
- u8 flags;
-
- s8 ag_cnt;
-#define AG_CNT_START 13
-
- u8 bridge;
-#define BRIDGE_SN9C102P 0
-#define BRIDGE_SN9C105 1
-#define BRIDGE_SN9C110 2
-#define BRIDGE_SN9C120 3
- u8 sensor; /* Type of image sensor chip */
- u8 i2c_addr;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
-};
-enum sensors {
- SENSOR_ADCM1700,
- SENSOR_GC0307,
- SENSOR_HV7131R,
- SENSOR_MI0360,
- SENSOR_MI0360B,
- SENSOR_MO4000,
- SENSOR_MT9V111,
- SENSOR_OM6802,
- SENSOR_OV7630,
- SENSOR_OV7648,
- SENSOR_OV7660,
- SENSOR_PO1030,
- SENSOR_PO2030N,
- SENSOR_SOI768,
- 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 */
-
-/* sn9c1xx definitions */
-/* register 0x01 */
-#define S_PWR_DN 0x01 /* sensor power down */
-#define S_PDN_INV 0x02 /* inverse pin S_PWR_DN */
-#define V_TX_EN 0x04 /* video transfer enable */
-#define LED 0x08 /* output to pin LED */
-#define SCL_SEL_OD 0x20 /* open-drain mode */
-#define SYS_SEL_48M 0x40 /* system clock 0: 24MHz, 1: 48MHz */
-/* register 0x17 */
-#define MCK_SIZE_MASK 0x1f /* sensor master clock */
-#define SEN_CLK_EN 0x20 /* enable sensor clock */
-#define DEF_EN 0x80 /* defect pixel by 0: soft, 1: hard */
-
-/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setredblue(struct gspca_dev *gspca_dev);
-static void setgamma(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setgain(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void setillum(struct gspca_dev *gspca_dev);
-static void setfreq(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x80,
- },
- .set_control = setbrightness
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
-#define CONTRAST_MAX 127
- .maximum = CONTRAST_MAX,
- .step = 1,
- .default_value = 20,
- },
- .set_control = setcontrast
- },
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 40,
- .step = 1,
-#define COLORS_DEF 25
- .default_value = COLORS_DEF,
- },
- .set_control = setcolors
- },
-[BLUE] = {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 24,
- .maximum = 40,
- .step = 1,
- .default_value = 32,
- },
- .set_control = setredblue
- },
-[RED] = {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 24,
- .maximum = 40,
- .step = 1,
- .default_value = 32,
- },
- .set_control = setredblue
- },
-[GAMMA] = {
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 40,
- .step = 1,
-#define GAMMA_DEF 20
- .default_value = GAMMA_DEF,
- },
- .set_control = setgamma
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 500,
- .maximum = 1500,
- .step = 1,
- .default_value = 1024
- },
- .set_control = setexposure
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1
- },
- .set = sd_setautogain,
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 4,
- .maximum = 49,
- .step = 1,
- .default_value = 15
- },
- .set_control = setgain
- },
-[HFLIP] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip
- },
-[VFLIP] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vflip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = sethvflip
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 90,
- },
- .set_control = setsharpness
- },
-[ILLUM] = {
- {
- .id = V4L2_CID_ILLUMINATORS_1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Illuminator / infrared",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = setillum
- },
-/* ov7630/ov7648/ov7660 only */
-[FREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
- .default_value = 1,
- },
- .set_control = setfreq
- },
-};
-
-/* table of the disabled controls */
-static const __u32 ctrl_dis[] = {
-[SENSOR_ADCM1700] = (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_GC0307] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_HV7131R] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << FREQ),
-
-[SENSOR_MI0360] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_MI0360B] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_MO4000] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_MT9V111] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_OM6802] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_OV7630] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP),
-
-[SENSOR_OV7648] = (1 << EXPOSURE) |
- (1 << GAIN) |
- (1 << HFLIP),
-
-[SENSOR_OV7660] = (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP),
-
-[SENSOR_PO1030] = (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_PO2030N] = (1 << FREQ),
-
-[SENSOR_SOI768] = (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-
-[SENSOR_SP80708] = (1 << EXPOSURE) |
- (1 << AUTOGAIN) |
- (1 << GAIN) |
- (1 << HFLIP) |
- (1 << VFLIP) |
- (1 << FREQ),
-};
-
-static const struct v4l2_pix_format cif_mode[] = {
- {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-static const struct v4l2_pix_format vga_mode[] = {
- {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */
- .sizeimage = 640 * 480 * 3 / 4 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-static const u8 sn_adcm1700[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42,
-/* reg18 reg19 reg1a reg1b */
- 0x06, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_gc0307[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x62, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x80, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x03, 0x01, 0x08, 0x28, 0x1e, 0x02,
-/* reg18 reg19 reg1a reg1b */
- 0x06, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_hv7131[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x03, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
-/* reg18 reg19 reg1a reg1b */
- 0x0a, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_mi0360[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 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 */
- 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
-/* reg18 reg19 reg1a reg1b */
- 0x06, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_mi0360b[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x40,
-/* reg18 reg19 reg1a reg1b */
- 0x06, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_mo4000[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40,
-/* reg18 reg19 reg1a reg1b */
- 0x08, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_mt9v111[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
-/* reg18 reg19 reg1a reg1b */
- 0x06, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_om6802[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40,
-/* reg18 reg19 reg1a reg1b */
- 0x05, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_ov7630[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
-/* reg18 reg19 reg1a reg1b */
- 0x0b, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_ov7648[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b */
- 0x0b, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_ov7660[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
-/* reg18 reg19 reg1a reg1b */
- 0x07, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_po1030[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b */
- 0x07, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_po2030n[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x63, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x01, 0x14, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b */
- 0x07, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_soi768[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x21, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x01, 0x08, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b */
- 0x07, 0x00, 0x00, 0x00
-};
-
-static const u8 sn_sp80708[0x1c] = {
-/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
- 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
-/* reg8 reg9 rega regb regc regd rege regf */
- 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
- 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
-/* reg18 reg19 reg1a reg1b */
- 0x07, 0x00, 0x00, 0x00
-};
-
-/* sequence specific to the sensors - !! index = SENSOR_xxx */
-static const u8 *sn_tb[] = {
-[SENSOR_ADCM1700] = sn_adcm1700,
-[SENSOR_GC0307] = sn_gc0307,
-[SENSOR_HV7131R] = sn_hv7131,
-[SENSOR_MI0360] = sn_mi0360,
-[SENSOR_MI0360B] = sn_mi0360b,
-[SENSOR_MO4000] = sn_mo4000,
-[SENSOR_MT9V111] = sn_mt9v111,
-[SENSOR_OM6802] = sn_om6802,
-[SENSOR_OV7630] = sn_ov7630,
-[SENSOR_OV7648] = sn_ov7648,
-[SENSOR_OV7660] = sn_ov7660,
-[SENSOR_PO1030] = sn_po1030,
-[SENSOR_PO2030N] = sn_po2030n,
-[SENSOR_SOI768] = sn_soi768,
-[SENSOR_SP80708] = sn_sp80708,
-};
-
-/* default gamma table */
-static const u8 gamma_def[17] = {
- 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
- 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
-};
-/* gamma for sensor ADCM1700 */
-static const u8 gamma_spec_0[17] = {
- 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
- 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
-};
-/* gamma for sensors HV7131R and MT9V111 */
-static const u8 gamma_spec_1[17] = {
- 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
- 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5
-};
-/* gamma for sensor GC0307 */
-static const u8 gamma_spec_2[17] = {
- 0x14, 0x37, 0x50, 0x6a, 0x7c, 0x8d, 0x9d, 0xab,
- 0xb5, 0xbf, 0xc2, 0xcb, 0xd1, 0xd6, 0xdb, 0xe1, 0xeb
-};
-/* gamma for sensor SP80708 */
-static const u8 gamma_spec_3[17] = {
- 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab,
- 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6
-};
-
-/* color matrix and offsets */
-static const u8 reg84[] = {
- 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
- 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
- 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
- 0x00, 0x00, 0x00 /* YUV offsets */
-};
-
-#define DELAY 0xdd
-
-static const u8 adcm1700_sensor_init[][8] = {
- {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */
- {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
- {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 adcm1700_sensor_param1[][8] = {
- {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */
- {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
-
- {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
- {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */
-
- {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 gc0307_sensor_init[][8] = {
- {0xa0, 0x21, 0x43, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x44, 0xa2, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x01, 0x6a, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x02, 0x70, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x11, 0x05, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x08, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x09, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0a, 0xe8, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0b, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0d, 0x22, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0e, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x0f, 0xb2, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x12, 0x70, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 10ms*/
- {0xa0, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x15, 0xb8, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x16, 0x13, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x17, 0x52, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x18, 0x50, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x1e, 0x0d, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x1f, 0x32, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x61, 0x90, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x63, 0x70, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x65, 0x98, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x67, 0x90, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x04, 0x96, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x45, 0x27, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x47, 0x2c, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x43, 0x47, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x44, 0xd8, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 gc0307_sensor_param1[][8] = {
- {0xa0, 0x21, 0x68, 0x13, 0x00, 0x00, 0x00, 0x10},
- {0xd0, 0x21, 0x61, 0x80, 0x00, 0x80, 0x00, 0x10},
- {0xc0, 0x21, 0x65, 0x80, 0x00, 0x80, 0x00, 0x10},
- {0xc0, 0x21, 0x63, 0xa0, 0x00, 0xa6, 0x00, 0x10},
-/*param3*/
- {0xa0, 0x21, 0x01, 0x6e, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x21, 0x02, 0x88, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-
-static const u8 hv7131r_sensor_init[][8] = {
- {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
- {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10},
-/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */
- {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10},
-/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */
-
- {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10},
- {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
- {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10},
- {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10},
- {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
- {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
-
- {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
-
- {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
- /* set sensor clock */
- {}
-};
-static const u8 mi0360_sensor_init[][8] = {
- {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
- {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
- {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
- {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
- {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
- {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
- {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
- {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
- {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
- {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
-
- {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10},
- {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10},
-
- {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
- {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
-
- {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10},
- {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */
-/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
-/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
- {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
- {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
- {}
-};
-static const u8 mi0360b_sensor_init[][8] = {
- {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
- {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*delay 20ms*/
- {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
- {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
- {0xd1, 0x5d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
- {0xd1, 0x5d, 0x2f, 0xf7, 0xb0, 0x00, 0x04, 0x10},
- {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
- {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
- {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
- {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
- {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
-
- {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
- {0xd1, 0x5d, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10},
- {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10},
- {}
-};
-static const u8 mi0360b_sensor_param1[][8] = {
- {0xb1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x06, 0x00, 0x53, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x00, 0x10},
- {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
-
- {0xd1, 0x5d, 0x2b, 0x00, 0xd1, 0x01, 0xc9, 0x10},
- {0xd1, 0x5d, 0x2d, 0x00, 0xed, 0x00, 0xd1, 0x10},
- {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
- {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
- {}
-};
-static const u8 mo4000_sensor_init[][8] = {
- {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 mt9v111_sensor_init[][8] = {
- {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {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, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
- {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
- {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 */
- {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */
- {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */
- {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */
- {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */
- {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
- {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
- {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 mt9v111_sensor_param1[][8] = {
- {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 */
- {}
-};
-static const u8 om6802_init0[2][8] = {
-/*fixme: variable*/
- {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
-};
-static const u8 om6802_sensor_init[][8] = {
- {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
- /* factory mode */
- {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
- /* output raw RGB */
- {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
-/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
- {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
- /* auto-exposure speed (0) / white balance mode (auto RGB) */
-/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
- * set color mode */
-/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
- * max AGC value in AE */
-/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10},
- * preset AGC */
-/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10},
- * preset brightness */
-/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10},
- * preset contrast */
-/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
- * preset gamma */
- {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
- /* luminance mode (0x4f -> AutoExpo on) */
- {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
- /* preset shutter */
-/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
- * auto frame rate */
-/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
- {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 om6802_sensor_param1[][8] = {
- {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 ov7630_sensor_init[][8] = {
- {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
-/* win: i2c_r from 00 to 80 */
- {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
- {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10},
-/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30)
- 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */
- {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10},
- {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
- {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10},
- {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10},
- {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10},
- {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10},
- {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10},
- {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10},
- {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10},
- {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10},
- {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10},
- {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10},
- {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10},
- {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10},
- {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 ov7630_sensor_param1[][8] = {
- {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
-/*fixme: + 0x12, 0x04*/
-/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN
- * set by setvflip */
- {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10},
-/* */
-/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
-/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */
-/* */
- {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10},
-/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */
- {}
-};
-
-static const u8 ov7648_sensor_init[][8] = {
- {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
- {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
- {0xc1, 0x21, 0x13, 0xa0, 0x04, 0x84, 0x00, 0x10},
- {0xd1, 0x21, 0x17, 0x1a, 0x02, 0xba, 0xf4, 0x10},
- {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x1f, 0x41, 0xc0, 0x80, 0x80, 0x10},
- {0xd1, 0x21, 0x23, 0xde, 0xa0, 0x80, 0x32, 0x10},
- {0xd1, 0x21, 0x27, 0xfe, 0xa0, 0x00, 0x91, 0x10},
- {0xd1, 0x21, 0x2b, 0x00, 0x88, 0x85, 0x80, 0x10},
- {0xc1, 0x21, 0x2f, 0x9c, 0x00, 0xc4, 0x00, 0x10},
- {0xd1, 0x21, 0x60, 0xa6, 0x60, 0x88, 0x12, 0x10},
- {0xd1, 0x21, 0x64, 0x88, 0x00, 0x00, 0x94, 0x10},
- {0xd1, 0x21, 0x68, 0x7a, 0x0c, 0x00, 0x00, 0x10},
- {0xd1, 0x21, 0x6c, 0x11, 0x33, 0x22, 0x00, 0x10},
- {0xd1, 0x21, 0x70, 0x11, 0x00, 0x10, 0x50, 0x10},
- {0xd1, 0x21, 0x74, 0x20, 0x06, 0x00, 0xb5, 0x10},
- {0xd1, 0x21, 0x78, 0x8a, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x7c, 0x00, 0x43, 0x00, 0x00, 0x10},
-
- {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10},
-/* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
-/* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
-/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
- {}
-};
-static const u8 ov7648_sensor_param1[][8] = {
-/* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
- * set by setvflip */
- {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
-/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, * GAIN - def */
-/* {0xb1, 0x21, 0x01, 0x6c, 0x6c, 0x00, 0x00, 0x10}, * B R - def: 80 */
-/*...*/
- {0xa1, 0x21, 0x11, 0x81, 0x00, 0x00, 0x00, 0x10}, /* CLKRC */
-/* {0xa1, 0x21, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x2a, 0x91, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
-/* {0xb1, 0x21, 0x01, 0x64, 0x84, 0x00, 0x00, 0x10}, * B R - def: 80 */
-
- {}
-};
-
-static const u8 ov7660_sensor_init[][8] = {
- {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
- /* Outformat = rawRGB */
- {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
- {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10},
- /* GAIN BLUE RED VREF */
- {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10},
- /* COM 1 BAVE GEAVE AECHH */
- {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
- {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
- {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
- /* AECH CLKRC COM7 COM8 */
- {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
- {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
- /* HSTART HSTOP VSTRT VSTOP */
- {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */
- {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */
- {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10},
- /* BOS GBOS GROS ROS (BGGR offset) */
-/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, */
- {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10},
- /* AEW AEB VPT BBIAS */
- {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10},
- /* GbBIAS RSVD EXHCH EXHCL */
- {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10},
- /* RBIAS ADVFL ASDVFH YAVE */
- {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10},
- /* HSYST HSYEN HREF */
- {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */
- {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10},
- /* ADC ACOM OFON TSLB */
- {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10},
- /* COM11 COM12 COM13 COM14 */
- {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10},
- /* EDGE COM15 COM16 COM17 */
- {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */
- {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */
- {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */
- {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */
- {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */
- {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */
- {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */
- {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */
- {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */
- {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10},
- /* LCC1 LCC2 LCC3 LCC4 */
- {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */
- {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, /* MANU */
- {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10},
- /* band gap reference [0:3] DBLV */
- {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */
- {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */
- {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
- {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
- {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
-/* not in all ms-win traces*/
- {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 ov7660_sensor_param1[][8] = {
- {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
- /* bits[3..0]reserved */
- {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
- /* VREF vertical frame ctrl */
- {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* AECH 0x20 */
- {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFL */
- {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* ADVFH */
- {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
-/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
-/****** (some exchanges in the win trace) ******/
-/*fixme:param2*/
- {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
- {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
- {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
- {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCL */
-/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */
-/****** (some exchanges in the win trace) ******/
-/******!! startsensor KO if changed !!****/
-/*fixme: param3*/
- {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-
-static const u8 po1030_sensor_init[][8] = {
-/* the sensor registers are described in m5602/m5602_po1030.h */
- {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
- {DELAY, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
- {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
- {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
- {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
- {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
- {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
- {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
- {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
- {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
- {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
- {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
- {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
- {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
- {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
- {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
- {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
- {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
-
- {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
- {}
-};
-static const u8 po1030_sensor_param1[][8] = {
-/* from ms-win traces - these values change with auto gain/expo/wb.. */
- {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
-/* mean values */
- {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
- {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
- {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
-
- {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
- {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
- {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
-/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
- {}
-};
-
-static const u8 po2030n_sensor_init[][8] = {
- {0xa1, 0x6e, 0x1e, 0x1a, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x1f, 0x99, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
- {0xa1, 0x6e, 0x1e, 0x0a, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x1f, 0x19, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 10ms */
- {0xa1, 0x6e, 0x20, 0x44, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x05, 0x70, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x08, 0x00, 0xd0, 0x00, 0x08, 0x10},
- {0xd1, 0x6e, 0x0c, 0x03, 0x50, 0x01, 0xe8, 0x10},
- {0xd1, 0x6e, 0x1d, 0x20, 0x0a, 0x19, 0x44, 0x10},
- {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x29, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x41, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x45, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x4d, 0x00, 0x00, 0x00, 0xed, 0x10},
- {0xd1, 0x6e, 0x51, 0x17, 0x4a, 0x2f, 0xc0, 0x10},
- {0xd1, 0x6e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x59, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x61, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x75, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x79, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x85, 0x00, 0x00, 0x00, 0x08, 0x10},
- {0xd1, 0x6e, 0x89, 0x01, 0xe8, 0x00, 0x01, 0x10},
- {0xa1, 0x6e, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x25, 0x00, 0x00, 0x00, 0x01, 0x10},
- {0xd1, 0x6e, 0x29, 0xe6, 0x00, 0xbd, 0x03, 0x10},
- {0xd1, 0x6e, 0x2d, 0x41, 0x38, 0x68, 0x40, 0x10},
- {0xd1, 0x6e, 0x31, 0x2b, 0x00, 0x36, 0x00, 0x10},
- {0xd1, 0x6e, 0x35, 0x30, 0x30, 0x08, 0x00, 0x10},
- {0xd1, 0x6e, 0x39, 0x00, 0x00, 0x33, 0x06, 0x10},
- {0xb1, 0x6e, 0x3d, 0x06, 0x02, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 po2030n_sensor_param1[][8] = {
- {0xa1, 0x6e, 0x1a, 0x01, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 8ms */
- {0xa1, 0x6e, 0x1b, 0xf4, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xd1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x40, 0x10}, /* RGBG gains */
-/*param2*/
- {0xa1, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x04, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-
-static const u8 soi768_sensor_init[][8] = {
- {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
- {DELAY, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 96ms */
- {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x19, 0x00, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 soi768_sensor_param1[][8] = {
- {0xa1, 0x21, 0x10, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x01, 0x7f, 0x7f, 0x00, 0x00, 0x10},
-/* */
-/* {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, */
-/* {0xa1, 0x21, 0x2d, 0x25, 0x00, 0x00, 0x00, 0x10}, */
- {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
-/* {0xb1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, */
- {0xa1, 0x21, 0x02, 0x8d, 0x00, 0x00, 0x00, 0x10},
-/* the next sequence should be used for auto gain */
- {0xa1, 0x21, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10},
- /* global gain ? : 07 - change with 0x15 at the end */
- {0xa1, 0x21, 0x10, 0x3f, 0x00, 0x00, 0x00, 0x10}, /* ???? : 063f */
- {0xa1, 0x21, 0x04, 0x06, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x21, 0x2d, 0x63, 0x03, 0x00, 0x00, 0x10},
- /* exposure ? : 0200 - change with 0x1e at the end */
- {}
-};
-
-static const u8 sp80708_sensor_init[][8] = {
- {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
- {}
-};
-static const u8 sp80708_sensor_param1[][8] = {
- {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
- {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10},
- {}
-};
-
-static const u8 (*sensor_init[])[8] = {
-[SENSOR_ADCM1700] = adcm1700_sensor_init,
-[SENSOR_GC0307] = gc0307_sensor_init,
-[SENSOR_HV7131R] = hv7131r_sensor_init,
-[SENSOR_MI0360] = mi0360_sensor_init,
-[SENSOR_MI0360B] = mi0360b_sensor_init,
-[SENSOR_MO4000] = mo4000_sensor_init,
-[SENSOR_MT9V111] = mt9v111_sensor_init,
-[SENSOR_OM6802] = om6802_sensor_init,
-[SENSOR_OV7630] = ov7630_sensor_init,
-[SENSOR_OV7648] = ov7648_sensor_init,
-[SENSOR_OV7660] = ov7660_sensor_init,
-[SENSOR_PO1030] = po1030_sensor_init,
-[SENSOR_PO2030N] = po2030n_sensor_init,
-[SENSOR_SOI768] = soi768_sensor_init,
-[SENSOR_SP80708] = sp80708_sensor_init,
-};
-
-/* read <len> bytes to gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev,
- u16 value, int len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
-#ifdef GSPCA_DEBUG
- if (len > USB_BUF_SZ) {
- pr_err("reg_r: buffer overflow\n");
- return;
- }
-#endif
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, 0,
- gspca_dev->usb_buf, len,
- 500);
- PDEBUG(D_USBI, "reg_r [%02x] -> %02x", value, gspca_dev->usb_buf[0]);
- if (ret < 0) {
- pr_err("reg_r err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void reg_w1(struct gspca_dev *gspca_dev,
- u16 value,
- u8 data)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
- gspca_dev->usb_buf[0] = data;
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value,
- 0,
- gspca_dev->usb_buf, 1,
- 500);
- if (ret < 0) {
- pr_err("reg_w1 err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-static void reg_w(struct gspca_dev *gspca_dev,
- u16 value,
- const u8 *buffer,
- int len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
- value, buffer[0], buffer[1]);
-#ifdef GSPCA_DEBUG
- if (len > USB_BUF_SZ) {
- pr_err("reg_w: buffer overflow\n");
- return;
- }
-#endif
- memcpy(gspca_dev->usb_buf, buffer, len);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- value, 0,
- gspca_dev->usb_buf, len,
- 500);
- if (ret < 0) {
- pr_err("reg_w err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-/* I2C write 1 byte */
-static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "i2c_w1 [%02x] = %02x", reg, val);
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_OM6802:
- case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
- gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
- break;
- default: /* i2c command = a1 (400 kHz) */
- gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
- break;
- }
- gspca_dev->usb_buf[1] = sd->i2c_addr;
- gspca_dev->usb_buf[2] = reg;
- gspca_dev->usb_buf[3] = val;
- gspca_dev->usb_buf[4] = 0;
- gspca_dev->usb_buf[5] = 0;
- gspca_dev->usb_buf[6] = 0;
- gspca_dev->usb_buf[7] = 0x10;
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- 0x08, /* value = i2c */
- 0,
- gspca_dev->usb_buf, 8,
- 500);
- if (ret < 0) {
- pr_err("i2c_w1 err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-/* I2C write 8 bytes */
-static void i2c_w8(struct gspca_dev *gspca_dev,
- const u8 *buffer)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "i2c_w8 [%02x] = %02x ..",
- buffer[2], buffer[3]);
- memcpy(gspca_dev->usb_buf, buffer, 8);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x08,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- 0x08, 0, /* value, index */
- gspca_dev->usb_buf, 8,
- 500);
- msleep(2);
- if (ret < 0) {
- pr_err("i2c_w8 err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
-static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 mode[8];
-
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_OM6802:
- case SENSOR_GC0307: /* i2c command = a0 (100 kHz) */
- mode[0] = 0x80 | 0x10;
- break;
- default: /* i2c command = 91 (400 kHz) */
- mode[0] = 0x81 | 0x10;
- break;
- }
- mode[1] = sd->i2c_addr;
- mode[2] = reg;
- mode[3] = 0;
- mode[4] = 0;
- mode[5] = 0;
- mode[6] = 0;
- mode[7] = 0x10;
- i2c_w8(gspca_dev, mode);
- msleep(2);
- mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
- mode[2] = 0;
- i2c_w8(gspca_dev, mode);
- msleep(2);
- reg_r(gspca_dev, 0x0a, 5);
-}
-
-static void i2c_w_seq(struct gspca_dev *gspca_dev,
- const u8 (*data)[8])
-{
- while ((*data)[0] != 0) {
- if ((*data)[0] != DELAY)
- i2c_w8(gspca_dev, *data);
- else
- msleep((*data)[1]);
- data++;
- }
-}
-
-/* check the ID of the hv7131 sensor */
-/* this sequence is needed because it activates the sensor */
-static void hv7131r_probe(struct gspca_dev *gspca_dev)
-{
- i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
- msleep(10);
- reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
- msleep(10);
- i2c_r(gspca_dev, 0, 5); /* read sensor id */
- if (gspca_dev->usb_buf[0] == 0x02 /* chip ID (02 is R) */
- && gspca_dev->usb_buf[1] == 0x09
- && gspca_dev->usb_buf[2] == 0x01) {
- PDEBUG(D_PROBE, "Sensor HV7131R found");
- return;
- }
- pr_warn("Erroneous HV7131R ID 0x%02x 0x%02x 0x%02x\n",
- gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
- gspca_dev->usb_buf[2]);
-}
-
-static void mi0360_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, j;
- u16 val = 0;
- static const u8 probe_tb[][4][8] = {
- { /* mi0360 */
- {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
- {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}
- },
- { /* mt9v111 */
- {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10},
- {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10},
- {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
- {}
- },
- };
-
- for (i = 0; i < ARRAY_SIZE(probe_tb); i++) {
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x08);
- for (j = 0; j < 3; j++)
- i2c_w8(gspca_dev, probe_tb[i][j]);
- msleep(2);
- reg_r(gspca_dev, 0x0a, 5);
- val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- if (probe_tb[i][3][0] != 0)
- i2c_w8(gspca_dev, probe_tb[i][3]);
- reg_w1(gspca_dev, 0x01, 0x29);
- reg_w1(gspca_dev, 0x17, 0x42);
- if (val != 0xffff)
- break;
- }
- if (gspca_dev->usb_err < 0)
- return;
- switch (val) {
- case 0x8221:
- PDEBUG(D_PROBE, "Sensor mi0360b");
- sd->sensor = SENSOR_MI0360B;
- break;
- case 0x823a:
- PDEBUG(D_PROBE, "Sensor mt9v111");
- sd->sensor = SENSOR_MT9V111;
- break;
- case 0x8243:
- PDEBUG(D_PROBE, "Sensor mi0360");
- break;
- default:
- PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val);
- break;
- }
-}
-
-static void ov7630_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 val;
-
- /* check ov76xx */
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x08);
- sd->i2c_addr = 0x21;
- i2c_r(gspca_dev, 0x0a, 2);
- val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- reg_w1(gspca_dev, 0x01, 0x29);
- reg_w1(gspca_dev, 0x17, 0x42);
- if (gspca_dev->usb_err < 0)
- return;
- if (val == 0x7628) { /* soi768 */
- sd->sensor = SENSOR_SOI768;
-/*fixme: only valid for 0c45:613e?*/
- gspca_dev->cam.input_flags =
- V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
- PDEBUG(D_PROBE, "Sensor soi768");
- return;
- }
- PDEBUG(D_PROBE, "Sensor ov%04x", val);
-}
-
-static void ov7648_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 val;
-
- /* check ov76xx */
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x08);
- sd->i2c_addr = 0x21;
- i2c_r(gspca_dev, 0x0a, 2);
- val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- reg_w1(gspca_dev, 0x01, 0x29);
- reg_w1(gspca_dev, 0x17, 0x42);
- if ((val & 0xff00) == 0x7600) { /* ov76xx */
- PDEBUG(D_PROBE, "Sensor ov%04x", val);
- return;
- }
-
- /* check po1030 */
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x08);
- sd->i2c_addr = 0x6e;
- i2c_r(gspca_dev, 0x00, 2);
- val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- reg_w1(gspca_dev, 0x01, 0x29);
- reg_w1(gspca_dev, 0x17, 0x42);
- if (gspca_dev->usb_err < 0)
- return;
- if (val == 0x1030) { /* po1030 */
- PDEBUG(D_PROBE, "Sensor po1030");
- sd->sensor = SENSOR_PO1030;
- return;
- }
- pr_err("Unknown sensor %04x\n", val);
-}
-
-/* 0c45:6142 sensor may be po2030n, gc0305 or gc0307 */
-static void po2030n_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u16 val;
-
- /* check gc0307 */
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x08);
- reg_w1(gspca_dev, 0x02, 0x22);
- sd->i2c_addr = 0x21;
- i2c_r(gspca_dev, 0x00, 1);
- val = gspca_dev->usb_buf[4];
- reg_w1(gspca_dev, 0x01, 0x29); /* reset */
- reg_w1(gspca_dev, 0x17, 0x42);
- if (val == 0x99) { /* gc0307 (?) */
- PDEBUG(D_PROBE, "Sensor gc0307");
- sd->sensor = SENSOR_GC0307;
- return;
- }
-
- /* check po2030n */
- reg_w1(gspca_dev, 0x17, 0x62);
- reg_w1(gspca_dev, 0x01, 0x0a);
- sd->i2c_addr = 0x6e;
- i2c_r(gspca_dev, 0x00, 2);
- val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- reg_w1(gspca_dev, 0x01, 0x29);
- reg_w1(gspca_dev, 0x17, 0x42);
- if (gspca_dev->usb_err < 0)
- return;
- if (val == 0x2030) {
- PDEBUG(D_PROBE, "Sensor po2030n");
-/* sd->sensor = SENSOR_PO2030N; */
- } else {
- pr_err("Unknown sensor ID %04x\n", val);
- }
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- sd->bridge = id->driver_info >> 16;
- sd->sensor = id->driver_info >> 8;
- sd->flags = id->driver_info;
-
- cam = &gspca_dev->cam;
- if (sd->sensor == SENSOR_ADCM1700) {
- cam->cam_mode = cif_mode;
- cam->nmodes = ARRAY_SIZE(cif_mode);
- } else {
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- }
- cam->npkt = 24; /* 24 packets per ISOC message */
- cam->ctrls = sd->ctrls;
-
- sd->ag_cnt = -1;
- sd->quality = QUALITY_DEF;
-
- INIT_WORK(&sd->work, qual_upd);
-
- 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;
- const u8 *sn9c1xx;
- u8 regGpio[] = { 0x29, 0x70 }; /* no audio */
- u8 regF1;
-
- /* setup a selector by bridge */
- reg_w1(gspca_dev, 0xf1, 0x01);
- reg_r(gspca_dev, 0x00, 1);
- reg_w1(gspca_dev, 0xf1, 0x00);
- reg_r(gspca_dev, 0x00, 1); /* get sonix chip id */
- regF1 = gspca_dev->usb_buf[0];
- 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:
- if (regF1 != 0x11)
- return -ENODEV;
- break;
- default:
-/* case BRIDGE_SN9C110: */
-/* case BRIDGE_SN9C120: */
- if (regF1 != 0x12)
- return -ENODEV;
- }
-
- switch (sd->sensor) {
- case SENSOR_MI0360:
- mi0360_probe(gspca_dev);
- break;
- case SENSOR_OV7630:
- ov7630_probe(gspca_dev);
- break;
- case SENSOR_OV7648:
- ov7648_probe(gspca_dev);
- break;
- case SENSOR_PO2030N:
- po2030n_probe(gspca_dev);
- break;
- }
-
- switch (sd->bridge) {
- case BRIDGE_SN9C102P:
- reg_w1(gspca_dev, 0x02, regGpio[1]);
- break;
- default:
- reg_w(gspca_dev, 0x01, regGpio, 2);
- break;
- }
-
- if (sd->sensor == SENSOR_OM6802)
- sd->ctrls[SHARPNESS].def = 0x10;
-
- /* Note we do not disable the sensor clock here (power saving mode),
- as that also disables the button on the cam. */
- reg_w1(gspca_dev, 0xf1, 0x00);
-
- /* set the i2c address */
- sn9c1xx = sn_tb[sd->sensor];
- sd->i2c_addr = sn9c1xx[9];
-
- gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
- if (!(sd->flags & F_ILLUM))
- gspca_dev->ctrl_dis |= (1 << ILLUM);
-
- return gspca_dev->usb_err;
-}
-
-static u32 expo_adjust(struct gspca_dev *gspca_dev,
- u32 expo)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->sensor) {
- case SENSOR_GC0307: {
- int a, b;
-
- /* expo = 0..255 -> a = 19..43 */
- a = 19 + expo * 25 / 256;
- i2c_w1(gspca_dev, 0x68, a);
- a -= 12;
- b = a * a * 4; /* heuristic */
- i2c_w1(gspca_dev, 0x03, b >> 8);
- i2c_w1(gspca_dev, 0x04, b);
- break;
- }
- case SENSOR_HV7131R: {
- u8 Expodoit[] =
- { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
-
- Expodoit[3] = expo >> 16;
- Expodoit[4] = expo >> 8;
- Expodoit[5] = expo;
- i2c_w8(gspca_dev, Expodoit);
- break;
- }
- case SENSOR_MI0360:
- case SENSOR_MI0360B: {
- u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
- { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
- static const u8 doit[] = /* update sensor */
- { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
- static const u8 sensorgo[] = /* sensor on */
- { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
-
- if (expo > 0x0635)
- expo = 0x0635;
- else if (expo < 0x0001)
- expo = 0x0001;
- expoMi[3] = expo >> 8;
- expoMi[4] = expo;
- i2c_w8(gspca_dev, expoMi);
- i2c_w8(gspca_dev, doit);
- i2c_w8(gspca_dev, sensorgo);
- break;
- }
- case SENSOR_MO4000: {
- u8 expoMof[] =
- { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
- u8 expoMo10[] =
- { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
- static const u8 gainMo[] =
- { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
-
- if (expo > 0x1fff)
- expo = 0x1fff;
- else if (expo < 0x0001)
- expo = 0x0001;
- expoMof[3] = (expo & 0x03fc) >> 2;
- i2c_w8(gspca_dev, expoMof);
- expoMo10[3] = ((expo & 0x1c00) >> 10)
- | ((expo & 0x0003) << 4);
- i2c_w8(gspca_dev, expoMo10);
- i2c_w8(gspca_dev, gainMo);
- PDEBUG(D_FRAM, "set exposure %d",
- ((expoMo10[3] & 0x07) << 10)
- | (expoMof[3] << 2)
- | ((expoMo10[3] & 0x30) >> 4));
- break;
- }
- case SENSOR_MT9V111: {
- u8 expo_c1[] =
- { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
-
- 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);
- break;
- }
- case SENSOR_OM6802: {
- u8 gainOm[] =
- { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
- /* preset AGC - works when AutoExpo = off */
-
- if (expo > 0x03ff)
- expo = 0x03ff;
- if (expo < 0x0001)
- expo = 0x0001;
- gainOm[3] = expo >> 2;
- i2c_w8(gspca_dev, gainOm);
- reg_w1(gspca_dev, 0x96, expo >> 5);
- PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
- break;
- }
- }
- return expo;
-}
-
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- unsigned int expo;
- int brightness;
- u8 k2;
-
- brightness = sd->ctrls[BRIGHTNESS].val;
- k2 = (brightness - 0x80) >> 2;
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- if (k2 > 0x1f)
- k2 = 0; /* only positive Y offset */
- break;
- case SENSOR_HV7131R:
- expo = brightness << 12;
- if (expo > 0x002dc6c0)
- expo = 0x002dc6c0;
- else if (expo < 0x02a0)
- expo = 0x02a0;
- sd->exposure = expo_adjust(gspca_dev, expo);
- break;
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- expo = brightness << 4;
- sd->exposure = expo_adjust(gspca_dev, expo);
- break;
- case SENSOR_MI0360B:
- expo = brightness << 2;
- sd->exposure = expo_adjust(gspca_dev, expo);
- break;
- case SENSOR_GC0307:
- expo = brightness;
- sd->exposure = expo_adjust(gspca_dev, expo);
- return; /* don't set the Y offset */
- case SENSOR_MT9V111:
- expo = brightness << 2;
- sd->exposure = expo_adjust(gspca_dev, expo);
- return; /* don't set the Y offset */
- case SENSOR_OM6802:
- expo = brightness << 2;
- sd->exposure = expo_adjust(gspca_dev, expo);
- return; /* Y offset already set */
- }
-
- reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 k2;
- u8 contrast[6];
-
- k2 = sd->ctrls[CONTRAST].val * 37 / (CONTRAST_MAX + 1)
- + 37; /* 37..73 */
- contrast[0] = (k2 + 1) / 2; /* red */
- contrast[1] = 0;
- contrast[2] = k2; /* green */
- contrast[3] = 0;
- contrast[4] = k2 / 5; /* blue */
- contrast[5] = 0;
- reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
-}
-
-static void setcolors(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, v, colors;
- const s16 *uv;
- u8 reg8a[12]; /* U & V gains */
- static const s16 uv_com[6] = { /* same as reg84 in signed decimal */
- -24, -38, 64, /* UR UG UB */
- 62, -51, -9 /* VR VG VB */
- };
- static const s16 uv_mi0360b[6] = {
- -20, -38, 64, /* UR UG UB */
- 60, -51, -9 /* VR VG VB */
- };
-
- colors = sd->ctrls[COLORS].val;
- if (sd->sensor == SENSOR_MI0360B)
- uv = uv_mi0360b;
- else
- uv = uv_com;
- for (i = 0; i < 6; i++) {
- v = uv[i] * colors / COLORS_DEF;
- reg8a[i * 2] = v;
- reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
- }
- reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
-}
-
-static void setredblue(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_PO2030N) {
- u8 rg1b[] = /* red green1 blue (no g2) */
- {0xc1, 0x6e, 0x16, 0x00, 0x40, 0x00, 0x00, 0x10};
-
- /* 0x40 = normal value = gain x 1 */
- rg1b[3] = sd->ctrls[RED].val * 2;
- rg1b[5] = sd->ctrls[BLUE].val * 2;
- i2c_w8(gspca_dev, rg1b);
- return;
- }
- reg_w1(gspca_dev, 0x05, sd->ctrls[RED].val);
-/* reg_w1(gspca_dev, 0x07, 32); */
- reg_w1(gspca_dev, 0x06, sd->ctrls[BLUE].val);
-}
-
-static void setgamma(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, val;
- u8 gamma[17];
- const u8 *gamma_base;
- static const u8 delta[17] = {
- 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a,
- 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00
- };
-
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- gamma_base = gamma_spec_0;
- break;
- case SENSOR_HV7131R:
- case SENSOR_MI0360B:
- case SENSOR_MT9V111:
- gamma_base = gamma_spec_1;
- break;
- case SENSOR_GC0307:
- gamma_base = gamma_spec_2;
- break;
- case SENSOR_SP80708:
- gamma_base = gamma_spec_3;
- break;
- default:
- gamma_base = gamma_def;
- break;
- }
-
- val = sd->ctrls[GAMMA].val;
- for (i = 0; i < sizeof gamma; i++)
- gamma[i] = gamma_base[i]
- + delta[i] * (val - GAMMA_DEF) / 32;
- reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
-}
-
-static void setexposure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_PO2030N) {
- u8 rexpo[] = /* 1a: expo H, 1b: expo M */
- {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
-
- rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
- i2c_w8(gspca_dev, rexpo);
- msleep(6);
- rexpo[2] = 0x1b;
- rexpo[3] = sd->ctrls[EXPOSURE].val;
- i2c_w8(gspca_dev, rexpo);
- }
-}
-
-static void setautogain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
- return;
- switch (sd->sensor) {
- case SENSOR_OV7630:
- case SENSOR_OV7648: {
- u8 comb;
-
- if (sd->sensor == SENSOR_OV7630)
- comb = 0xc0;
- else
- comb = 0xa0;
- if (sd->ctrls[AUTOGAIN].val)
- comb |= 0x03;
- i2c_w1(&sd->gspca_dev, 0x13, comb);
- return;
- }
- }
- if (sd->ctrls[AUTOGAIN].val)
- sd->ag_cnt = AG_CNT_START;
- else
- sd->ag_cnt = -1;
-}
-
-static void setgain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_PO2030N) {
- u8 rgain[] = /* 15: gain */
- {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
-
- rgain[3] = sd->ctrls[GAIN].val;
- i2c_w8(gspca_dev, rgain);
- }
-}
-
-static void sethvflip(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 comn;
-
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- comn = 0x18; /* clkdiv = 1, ablcen = 1 */
- if (sd->ctrls[VFLIP].val)
- comn |= 0x01;
- i2c_w1(gspca_dev, 0x01, comn); /* sctra */
- break;
- case SENSOR_OV7630:
- comn = 0x02;
- if (!sd->ctrls[VFLIP].val)
- comn |= 0x80;
- i2c_w1(gspca_dev, 0x75, comn);
- break;
- case SENSOR_OV7648:
- comn = 0x06;
- if (sd->ctrls[VFLIP].val)
- comn |= 0x80;
- i2c_w1(gspca_dev, 0x75, comn);
- break;
- case SENSOR_PO2030N:
- /* Reg. 0x1E: Timing Generator Control Register 2 (Tgcontrol2)
- * (reset value: 0x0A)
- * bit7: HM: Horizontal Mirror: 0: disable, 1: enable
- * bit6: VM: Vertical Mirror: 0: disable, 1: enable
- * bit5: ST: Shutter Selection: 0: electrical, 1: mechanical
- * bit4: FT: Single Frame Transfer: 0: disable, 1: enable
- * bit3-0: X
- */
- comn = 0x0a;
- if (sd->ctrls[HFLIP].val)
- comn |= 0x80;
- if (sd->ctrls[VFLIP].val)
- comn |= 0x40;
- i2c_w1(&sd->gspca_dev, 0x1e, comn);
- break;
- }
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w1(gspca_dev, 0x99, sd->ctrls[SHARPNESS].val);
-}
-
-static void setillum(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << ILLUM))
- return;
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- reg_w1(gspca_dev, 0x02, /* gpio */
- sd->ctrls[ILLUM].val ? 0x64 : 0x60);
- break;
- case SENSOR_MT9V111:
- reg_w1(gspca_dev, 0x02,
- sd->ctrls[ILLUM].val ? 0x77 : 0x74);
-/* should have been: */
-/* 0x55 : 0x54); * 370i */
-/* 0x66 : 0x64); * Clip */
- break;
- }
-}
-
-static void setfreq(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (gspca_dev->ctrl_dis & (1 << FREQ))
- return;
- if (sd->sensor == SENSOR_OV7660) {
- u8 com8;
-
- com8 = 0xdf; /* auto gain/wb/expo */
- switch (sd->ctrls[FREQ].val) {
- case 0: /* Banding filter disabled */
- i2c_w1(gspca_dev, 0x13, com8 | 0x20);
- break;
- case 1: /* 50 hz */
- i2c_w1(gspca_dev, 0x13, com8);
- i2c_w1(gspca_dev, 0x3b, 0x0a);
- break;
- case 2: /* 60 hz */
- i2c_w1(gspca_dev, 0x13, com8);
- i2c_w1(gspca_dev, 0x3b, 0x02);
- break;
- }
- } else {
- u8 reg2a = 0, reg2b = 0, reg2d = 0;
-
- /* Get reg2a / reg2d base values */
- switch (sd->sensor) {
- case SENSOR_OV7630:
- reg2a = 0x08;
- reg2d = 0x01;
- break;
- case SENSOR_OV7648:
- reg2a = 0x11;
- reg2d = 0x81;
- break;
- }
-
- switch (sd->ctrls[FREQ].val) {
- case 0: /* Banding filter disabled */
- break;
- case 1: /* 50 hz (filter on and framerate adj) */
- reg2a |= 0x80;
- reg2b = 0xac;
- reg2d |= 0x04;
- break;
- case 2: /* 60 hz (filter on, no framerate adj) */
- reg2a |= 0x80;
- reg2d |= 0x04;
- break;
- }
- i2c_w1(gspca_dev, 0x2a, reg2a);
- i2c_w1(gspca_dev, 0x2b, reg2b);
- i2c_w1(gspca_dev, 0x2d, reg2d);
- }
-}
-
-static void setjpegqual(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
-#if USB_BUF_SZ < 64
-#error "No room enough in usb_buf for quantization table"
-#endif
- 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,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- 0x0100, 0,
- gspca_dev->usb_buf, 64,
- 500);
- 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,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
- 0x0140, 0,
- gspca_dev->usb_buf, 64,
- 500);
-
- sd->reg18 ^= 0x40;
- 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)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
- u8 reg01, reg17;
- u8 reg0102[2];
- const u8 *sn9c1xx;
- const u8 (*init)[8];
- const u8 *reg9a;
- int mode;
- static const u8 reg9a_def[] =
- {0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
- static const u8 reg9a_spec[] =
- {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
- static const u8 regd4[] = {0x60, 0x00, 0x00};
- static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
- static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
- static const u8 CA_adcm1700[] =
- { 0x14, 0xec, 0x0a, 0xf6 };
- static const u8 CA_po2030n[] =
- { 0x1e, 0xe2, 0x14, 0xec };
- static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
- static const u8 CE_gc0307[] =
- { 0x32, 0xce, 0x2d, 0xd3 };
- static const u8 CE_ov76xx[] =
- { 0x32, 0xdd, 0x32, 0xdd };
- static const u8 CE_po2030n[] =
- { 0x14, 0xe7, 0x1e, 0xdd };
-
- /* create the JPEG header */
- jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
- 0x21); /* JPEG 422 */
-
- /* initialize the bridge */
- sn9c1xx = sn_tb[sd->sensor];
-
- /* sensor clock already enabled in sd_init */
- /* reg_w1(gspca_dev, 0xf1, 0x00); */
- reg01 = sn9c1xx[1];
- if (sd->flags & F_PDN_INV)
- reg01 ^= S_PDN_INV; /* power down inverted */
- reg_w1(gspca_dev, 0x01, reg01);
-
- /* configure gpio */
- reg0102[0] = reg01;
- reg0102[1] = sn9c1xx[2];
- if (gspca_dev->audio)
- reg0102[1] |= 0x04; /* keep the audio connection */
- reg_w(gspca_dev, 0x01, reg0102, 2);
- reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
- reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
- switch (sd->sensor) {
- case SENSOR_GC0307:
- case SENSOR_OV7660:
- case SENSOR_PO1030:
- case SENSOR_PO2030N:
- case SENSOR_SOI768:
- case SENSOR_SP80708:
- reg9a = reg9a_spec;
- break;
- default:
- reg9a = reg9a_def;
- break;
- }
- reg_w(gspca_dev, 0x9a, reg9a, 6);
-
- reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
-
- reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
-
- reg17 = sn9c1xx[0x17];
- switch (sd->sensor) {
- case SENSOR_GC0307:
- msleep(50); /*fixme: is it useful? */
- break;
- case SENSOR_OM6802:
- msleep(10);
- reg_w1(gspca_dev, 0x02, 0x73);
- reg17 |= SEN_CLK_EN;
- reg_w1(gspca_dev, 0x17, reg17);
- reg_w1(gspca_dev, 0x01, 0x22);
- msleep(100);
- reg01 = SCL_SEL_OD | S_PDN_INV;
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x04; /* clock / 4 */
- break;
- }
- reg01 |= SYS_SEL_48M;
- reg_w1(gspca_dev, 0x01, reg01);
- reg17 |= SEN_CLK_EN;
- reg_w1(gspca_dev, 0x17, reg17);
- reg01 &= ~S_PWR_DN; /* sensor power on */
- reg_w1(gspca_dev, 0x01, reg01);
- reg01 &= ~SCL_SEL_OD; /* remove open-drain mode */
- reg_w1(gspca_dev, 0x01, reg01);
-
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- hv7131r_probe(gspca_dev); /*fixme: is it useful? */
- break;
- case SENSOR_OM6802:
- msleep(10);
- reg_w1(gspca_dev, 0x01, reg01);
- i2c_w8(gspca_dev, om6802_init0[0]);
- i2c_w8(gspca_dev, om6802_init0[1]);
- msleep(15);
- reg_w1(gspca_dev, 0x02, 0x71);
- msleep(150);
- break;
- case SENSOR_SP80708:
- msleep(100);
- reg_w1(gspca_dev, 0x02, 0x62);
- break;
- }
-
- /* initialize the sensor */
- i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
-
- reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
- reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
- reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
- reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
- reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
- if (sd->sensor == SENSOR_ADCM1700) {
- reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */
- reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */
- } else {
- reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */
- reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */
- }
- reg_w1(gspca_dev, 0xc6, 0x00);
- reg_w1(gspca_dev, 0xc7, 0x00);
- if (sd->sensor == SENSOR_ADCM1700) {
- reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */
- reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */
- } else {
- reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */
- reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */
- }
- reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
- switch (sd->sensor) {
- case SENSOR_OM6802:
-/* case SENSOR_OV7648: * fixme: sometimes */
- break;
- default:
- reg17 |= DEF_EN;
- break;
- }
- reg_w1(gspca_dev, 0x17, reg17);
-
- reg_w1(gspca_dev, 0x05, 0x00); /* red */
- reg_w1(gspca_dev, 0x07, 0x00); /* green */
- reg_w1(gspca_dev, 0x06, 0x00); /* blue */
- reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
-
- setgamma(gspca_dev);
-
-/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
- for (i = 0; i < 8; i++)
- reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_OV7660:
- case SENSOR_SP80708:
- reg_w1(gspca_dev, 0x9a, 0x05);
- break;
- case SENSOR_GC0307:
- case SENSOR_MT9V111:
- case SENSOR_MI0360B:
- reg_w1(gspca_dev, 0x9a, 0x07);
- break;
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- reg_w1(gspca_dev, 0x9a, 0x0a);
- break;
- case SENSOR_PO2030N:
- case SENSOR_SOI768:
- reg_w1(gspca_dev, 0x9a, 0x06);
- break;
- default:
- reg_w1(gspca_dev, 0x9a, 0x08);
- break;
- }
- setsharpness(gspca_dev);
-
- reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
- reg_w1(gspca_dev, 0x05, 0x20); /* red */
- reg_w1(gspca_dev, 0x07, 0x20); /* green */
- reg_w1(gspca_dev, 0x06, 0x20); /* blue */
-
- init = NULL;
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- reg01 |= SYS_SEL_48M | V_TX_EN;
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x02; /* clock / 2 */
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- init = adcm1700_sensor_param1;
- break;
- case SENSOR_GC0307:
- init = gc0307_sensor_param1;
- break;
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- if (!mode)
- reg01 &= ~SYS_SEL_48M; /* 640x480: clk 24Mhz */
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x01; /* clock / 1 */
- break;
- case SENSOR_MI0360B:
- init = mi0360b_sensor_param1;
- break;
- case SENSOR_MO4000:
- if (mode) { /* if 320x240 */
- reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x01; /* clock / 1 */
- }
- break;
- case SENSOR_MT9V111:
- init = mt9v111_sensor_param1;
- break;
- case SENSOR_OM6802:
- init = om6802_sensor_param1;
- if (!mode) { /* if 640x480 */
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x04; /* clock / 4 */
- } else {
- reg01 &= ~SYS_SEL_48M; /* clk 24Mz */
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x02; /* clock / 2 */
- }
- break;
- case SENSOR_OV7630:
- init = ov7630_sensor_param1;
- break;
- case SENSOR_OV7648:
- init = ov7648_sensor_param1;
- reg17 &= ~MCK_SIZE_MASK;
- reg17 |= 0x01; /* clock / 1 */
- break;
- case SENSOR_OV7660:
- init = ov7660_sensor_param1;
- break;
- case SENSOR_PO1030:
- init = po1030_sensor_param1;
- break;
- case SENSOR_PO2030N:
- init = po2030n_sensor_param1;
- break;
- case SENSOR_SOI768:
- init = soi768_sensor_param1;
- break;
- case SENSOR_SP80708:
- init = sp80708_sensor_param1;
- break;
- }
-
- /* more sensor initialization - param1 */
- if (init != NULL) {
- i2c_w_seq(gspca_dev, init);
-/* init = NULL; */
- }
-
- reg_w(gspca_dev, 0xc0, C0, 6);
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_GC0307:
- case SENSOR_SOI768:
- reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
- break;
- case SENSOR_PO2030N:
- reg_w(gspca_dev, 0xca, CA_po2030n, 4);
- break;
- default:
- reg_w(gspca_dev, 0xca, CA, 4);
- break;
- }
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- case SENSOR_OV7660:
- case SENSOR_SOI768:
- reg_w(gspca_dev, 0xce, CE_ov76xx, 4);
- break;
- case SENSOR_GC0307:
- reg_w(gspca_dev, 0xce, CE_gc0307, 4);
- break;
- case SENSOR_PO2030N:
- reg_w(gspca_dev, 0xce, CE_po2030n, 4);
- break;
- default:
- reg_w(gspca_dev, 0xce, CE, 4);
- /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */
- break;
- }
-
- /* here change size mode 0 -> VGA; 1 -> CIF */
- sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
- reg_w1(gspca_dev, 0x18, sd->reg18);
- setjpegqual(gspca_dev);
-
- reg_w1(gspca_dev, 0x17, reg17);
- reg_w1(gspca_dev, 0x01, reg01);
- sd->reg01 = reg01;
- sd->reg17 = reg17;
-
- sethvflip(gspca_dev);
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
- setcolors(gspca_dev);
- setautogain(gspca_dev);
- if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
- setexposure(gspca_dev);
- setgain(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;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const u8 stophv7131[] =
- { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
- static const u8 stopmi0360[] =
- { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
- static const u8 stopov7648[] =
- { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
- static const u8 stopsoi768[] =
- { 0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10 };
- u8 reg01;
- u8 reg17;
-
- reg01 = sd->reg01;
- reg17 = sd->reg17 & ~SEN_CLK_EN;
- switch (sd->sensor) {
- case SENSOR_ADCM1700:
- case SENSOR_GC0307:
- case SENSOR_PO2030N:
- case SENSOR_SP80708:
- reg01 |= LED;
- reg_w1(gspca_dev, 0x01, reg01);
- reg01 &= ~(LED | V_TX_EN);
- reg_w1(gspca_dev, 0x01, reg01);
-/* reg_w1(gspca_dev, 0x02, 0x??); * LED off ? */
- break;
- case SENSOR_HV7131R:
- reg01 &= ~V_TX_EN;
- reg_w1(gspca_dev, 0x01, reg01);
- i2c_w8(gspca_dev, stophv7131);
- break;
- case SENSOR_MI0360:
- case SENSOR_MI0360B:
- reg01 &= ~V_TX_EN;
- reg_w1(gspca_dev, 0x01, reg01);
-/* reg_w1(gspca_dev, 0x02, 0x40); * LED off ? */
- i2c_w8(gspca_dev, stopmi0360);
- break;
- case SENSOR_MT9V111:
- case SENSOR_OM6802:
- case SENSOR_PO1030:
- reg01 &= ~V_TX_EN;
- reg_w1(gspca_dev, 0x01, reg01);
- break;
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- reg01 &= ~V_TX_EN;
- reg_w1(gspca_dev, 0x01, reg01);
- i2c_w8(gspca_dev, stopov7648);
- break;
- case SENSOR_OV7660:
- reg01 &= ~V_TX_EN;
- reg_w1(gspca_dev, 0x01, reg01);
- break;
- case SENSOR_SOI768:
- i2c_w8(gspca_dev, stopsoi768);
- break;
- }
-
- reg01 |= SCL_SEL_OD;
- reg_w1(gspca_dev, 0x01, reg01);
- reg01 |= S_PWR_DN; /* sensor power down */
- reg_w1(gspca_dev, 0x01, reg01);
- reg_w1(gspca_dev, 0x17, reg17);
- reg01 &= ~SYS_SEL_48M; /* clock 24MHz */
- reg_w1(gspca_dev, 0x01, reg01);
- reg01 |= LED;
- reg_w1(gspca_dev, 0x01, reg01);
- /* Don't disable sensor clock as that disables the button on the cam */
- /* 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;
- }
-}
-
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
-static void do_autogain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int delta;
- int expotimes;
- u8 luma_mean = 130;
- u8 luma_delta = 20;
-
- /* Thanks S., without your advice, autobright should not work :) */
- if (sd->ag_cnt < 0)
- return;
- if (--sd->ag_cnt >= 0)
- return;
- sd->ag_cnt = AG_CNT_START;
-
- delta = atomic_read(&sd->avg_lum);
- PDEBUG(D_FRAM, "mean lum %d", delta);
-
- if (sd->sensor == SENSOR_PO2030N) {
- auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
- 15, 1024);
- return;
- }
-
- if (delta < luma_mean - luma_delta ||
- delta > luma_mean + luma_delta) {
- switch (sd->sensor) {
- case SENSOR_GC0307:
- expotimes = sd->exposure;
- expotimes += (luma_mean - delta) >> 6;
- if (expotimes < 0)
- expotimes = 0;
- sd->exposure = expo_adjust(gspca_dev,
- (unsigned int) expotimes);
- break;
- case SENSOR_HV7131R:
- expotimes = sd->exposure >> 8;
- expotimes += (luma_mean - delta) >> 4;
- if (expotimes < 0)
- expotimes = 0;
- sd->exposure = expo_adjust(gspca_dev,
- (unsigned int) (expotimes << 8));
- break;
- case SENSOR_OM6802:
- case SENSOR_MT9V111:
- expotimes = sd->exposure;
- expotimes += (luma_mean - delta) >> 2;
- if (expotimes < 0)
- expotimes = 0;
- sd->exposure = expo_adjust(gspca_dev,
- (unsigned int) expotimes);
- setredblue(gspca_dev);
- break;
- default:
-/* case SENSOR_MO4000: */
-/* case SENSOR_MI0360: */
-/* case SENSOR_MI0360B: */
- expotimes = sd->exposure;
- expotimes += (luma_mean - delta) >> 6;
- if (expotimes < 0)
- expotimes = 0;
- sd->exposure = expo_adjust(gspca_dev,
- (unsigned int) expotimes);
- setredblue(gspca_dev);
- break;
- }
- }
-}
-
-/* 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,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
- 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').
- */
-
- /* count the packets and their size */
- sd->npkt++;
- sd->pktsz += len;
-
-/*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;
- }
-
- /* 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);
- 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;
- }
- }
-
- gspca_frame_add(gspca_dev, LAST_PACKET, data, i);
-
- /* compute the filling rate and a new JPEG quality */
- if (new_qual == 0) {
- int r;
-
- 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 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_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ctrls[AUTOGAIN].val = val;
- if (val)
- gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
- else
- gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
- break;
- }
- return -EINVAL;
-}
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* interrupt packet data */
- int len) /* interrupt packet length */
-{
- int ret = -EINVAL;
-
- if (len == 1 && data[0] == 1) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
- input_sync(gspca_dev->input_dev);
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- ret = 0;
- }
-
- return ret;
-}
-#endif
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = NCTRLS,
- .config = sd_config,
- .init = sd_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
- .dq_callback = do_autogain,
- .querymenu = sd_querymenu,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .int_pkt_scan = sd_int_pkt_scan,
-#endif
-};
-
-/* -- module initialisation -- */
-#define BS(bridge, sensor) \
- .driver_info = (BRIDGE_ ## bridge << 16) \
- | (SENSOR_ ## sensor << 8)
-#define BSF(bridge, sensor, flags) \
- .driver_info = (BRIDGE_ ## bridge << 16) \
- | (SENSOR_ ## sensor << 8) \
- | (flags)
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x0458, 0x7025), BSF(SN9C120, MI0360B, F_PDN_INV)},
- {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
- {USB_DEVICE(0x045e, 0x00f5), BSF(SN9C105, OV7660, F_PDN_INV)},
- {USB_DEVICE(0x045e, 0x00f7), BSF(SN9C105, OV7660, F_PDN_INV)},
- {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
- {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
- {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
- {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
- {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
-/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
- {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
-/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
-/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
- {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
-/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
- {USB_DEVICE(0x0c45, 0x60c0), BSF(SN9C105, MI0360, F_ILLUM)},
- /* or MT9V111 */
-/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
-/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
-/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
- {USB_DEVICE(0x0c45, 0x60ce), BS(SN9C105, SP80708)},
- {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
-/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
-/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
-/* {USB_DEVICE(0x0c45, 0x60f2), BS(SN9C105, OV7660)}, */
- {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
- {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
- {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
- {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, PO2030N)}, /* /GC0305*/
-/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
- {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/
- {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/
-/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
-/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
-/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
- {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/
-/*bw600.inf:*/
- {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/
- {USB_DEVICE(0x0c45, 0x612b), BS(SN9C110, ADCM1700)},
- {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
- {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
-/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
- {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
- /* or MT9V111 / MI0360B */
-/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
- {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
- {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
- {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
- {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
- {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
- {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, /*sn9c120b*/
- /* or GC0305 / GC0307 */
- {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
- {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
- {USB_DEVICE(0x0c45, 0x614a), BSF(SN9C120, ADCM1700, F_ILLUM)},
-/* {USB_DEVICE(0x0c45, 0x614c), BS(SN9C120, GC0306)}, */ /*sn9c120b*/
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
deleted file mode 100644
index 969bb5a4cd93..000000000000
--- a/drivers/media/video/gspca/spca506.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * SPCA506 chip based cameras function
- * M Xhaard 15/04/2004 based on different work Mark Taylor and others
- * and my own snoopy file on a pv-321c donate by a german compagny
- * "Firma Frank Gmbh" from Saarbruecken
- *
- * V4L2 by Jean-Francois Moine <http://moinejf.free.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; 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 "spca506"
-
-#include "gspca.h"
-
-MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
-MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- char norme;
- char channel;
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 3 / 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 5},
- {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 4},
- {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 2},
- {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3 / 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-
-#define SPCA50X_OFFSET_DATA 10
-
-#define SAA7113_bright 0x0a /* defaults 0x80 */
-#define SAA7113_contrast 0x0b /* defaults 0x47 */
-#define SAA7113_saturation 0x0c /* defaults 0x40 */
-#define SAA7113_hue 0x0d /* defaults 0x00 */
-#define SAA7113_I2C_BASE_WRITE 0x4a
-
-/* read 'len' bytes to gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev,
- __u16 req,
- __u16 index,
- __u16 length)
-{
- usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, /* value */
- index, gspca_dev->usb_buf, length,
- 500);
-}
-
-static void reg_w(struct usb_device *dev,
- __u16 req,
- __u16 value,
- __u16 index)
-{
- usb_control_msg(dev,
- usb_sndctrlpipe(dev, 0),
- req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index,
- NULL, 0, 500);
-}
-
-static void spca506_Initi2c(struct gspca_dev *gspca_dev)
-{
- reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
-}
-
-static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
- __u16 reg)
-{
- int retry = 60;
-
- reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
- reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
- while (retry--) {
- reg_r(gspca_dev, 0x07, 0x0003, 2);
- if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
- break;
- }
-}
-
-static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
- __u16 norme,
- __u16 channel)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-/* fixme: check if channel == 0..3 and 6..9 (8 values) */
- __u8 setbit0 = 0x00;
- __u8 setbit1 = 0x00;
- __u8 videomask = 0x00;
-
- PDEBUG(D_STREAM, "** Open Set Norme **");
- spca506_Initi2c(gspca_dev);
- /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
- /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
- /* and exclude SAA7113 reserved channel set default 0 otherwise */
- if (norme & V4L2_STD_NTSC)
- setbit0 = 0x01;
- if (channel == 4 || channel == 5 || channel > 9)
- channel = 0;
- if (channel < 4)
- setbit1 = 0x02;
- videomask = (0x48 | setbit0 | setbit1);
- reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
- spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
-
- if (norme & V4L2_STD_NTSC)
- spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
- /* Chrominance Control NTSC N */
- else if (norme & V4L2_STD_SECAM)
- spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
- /* Chrominance Control SECAM */
- else
- spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
- /* Chrominance Control PAL BGHIV */
-
- sd->norme = norme;
- sd->channel = channel;
- PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
- PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
-}
-
-static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
- __u16 *norme, __u16 *channel)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* Read the register is not so good value change so
- we use your own copy in spca50x struct */
- *norme = sd->norme;
- *channel = sd->channel;
- PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
-}
-
-static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
- __u16 xmult, __u16 ymult)
-{
- struct usb_device *dev = gspca_dev->dev;
-
- PDEBUG(D_STREAM, "** SetSize **");
- reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
- /* Soft snap 0x40 Hard 0x41 */
- reg_w(dev, 0x04, 0x41, 0x0001);
- reg_w(dev, 0x04, 0x00, 0x0002);
- /* reserved */
- reg_w(dev, 0x04, 0x00, 0x0003);
-
- /* reserved */
- reg_w(dev, 0x04, 0x00, 0x0004);
- /* reserved */
- reg_w(dev, 0x04, 0x01, 0x0005);
- /* reserced */
- reg_w(dev, 0x04, xmult, 0x0006);
- /* reserved */
- reg_w(dev, 0x04, ymult, 0x0007);
- /* compression 1 */
- reg_w(dev, 0x04, 0x00, 0x0008);
- /* T=64 -> 2 */
- reg_w(dev, 0x04, 0x00, 0x0009);
- /* threshold2D */
- reg_w(dev, 0x04, 0x21, 0x000a);
- /* quantization */
- reg_w(dev, 0x04, 0x00, 0x000b);
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct cam *cam;
-
- cam = &gspca_dev->cam;
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- return 0;
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- struct usb_device *dev = gspca_dev->dev;
-
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0xFF, 0x0003);
- reg_w(dev, 0x03, 0x00, 0x0000);
- reg_w(dev, 0x03, 0x1c, 0x0001);
- reg_w(dev, 0x03, 0x18, 0x0001);
- /* Init on PAL and composite input0 */
- spca506_SetNormeInput(gspca_dev, 0, 0);
- reg_w(dev, 0x03, 0x1c, 0x0001);
- reg_w(dev, 0x03, 0x18, 0x0001);
- reg_w(dev, 0x05, 0x00, 0x0000);
- reg_w(dev, 0x05, 0xef, 0x0001);
- reg_w(dev, 0x05, 0x00, 0x00c1);
- reg_w(dev, 0x05, 0x00, 0x00c2);
- reg_w(dev, 0x06, 0x18, 0x0002);
- reg_w(dev, 0x06, 0xf5, 0x0011);
- reg_w(dev, 0x06, 0x02, 0x0012);
- reg_w(dev, 0x06, 0xfb, 0x0013);
- reg_w(dev, 0x06, 0x00, 0x0014);
- reg_w(dev, 0x06, 0xa4, 0x0051);
- reg_w(dev, 0x06, 0x40, 0x0052);
- reg_w(dev, 0x06, 0x71, 0x0053);
- reg_w(dev, 0x06, 0x40, 0x0054);
- /************************************************/
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0x00, 0x0003);
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0xFF, 0x0003);
- reg_w(dev, 0x02, 0x00, 0x0000);
- reg_w(dev, 0x03, 0x60, 0x0000);
- reg_w(dev, 0x03, 0x18, 0x0001);
- /* for a better reading mx :) */
- /*sdca506_WriteI2c(value,register) */
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, 0x08, 0x01);
- spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
- /* input composite video */
- spca506_WriteI2c(gspca_dev, 0x33, 0x03);
- spca506_WriteI2c(gspca_dev, 0x00, 0x04);
- spca506_WriteI2c(gspca_dev, 0x00, 0x05);
- spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
- spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
- spca506_WriteI2c(gspca_dev, 0x98, 0x08);
- spca506_WriteI2c(gspca_dev, 0x03, 0x09);
- spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
- spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
- spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
- spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
- spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */
- spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
- spca506_WriteI2c(gspca_dev, 0x00, 0x10);
- spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
- spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
- spca506_WriteI2c(gspca_dev, 0x01, 0x13);
- spca506_WriteI2c(gspca_dev, 0x00, 0x14);
- spca506_WriteI2c(gspca_dev, 0x00, 0x15);
- spca506_WriteI2c(gspca_dev, 0x00, 0x16);
- spca506_WriteI2c(gspca_dev, 0x00, 0x17);
- spca506_WriteI2c(gspca_dev, 0x00, 0x18);
- spca506_WriteI2c(gspca_dev, 0x00, 0x19);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
- spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
- spca506_WriteI2c(gspca_dev, 0x02, 0x40);
- spca506_WriteI2c(gspca_dev, 0xff, 0x41);
- spca506_WriteI2c(gspca_dev, 0xff, 0x42);
- spca506_WriteI2c(gspca_dev, 0xff, 0x43);
- spca506_WriteI2c(gspca_dev, 0xff, 0x44);
- spca506_WriteI2c(gspca_dev, 0xff, 0x45);
- spca506_WriteI2c(gspca_dev, 0xff, 0x46);
- spca506_WriteI2c(gspca_dev, 0xff, 0x47);
- spca506_WriteI2c(gspca_dev, 0xff, 0x48);
- spca506_WriteI2c(gspca_dev, 0xff, 0x49);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
- spca506_WriteI2c(gspca_dev, 0xff, 0x50);
- spca506_WriteI2c(gspca_dev, 0xff, 0x51);
- spca506_WriteI2c(gspca_dev, 0xff, 0x52);
- spca506_WriteI2c(gspca_dev, 0xff, 0x53);
- spca506_WriteI2c(gspca_dev, 0xff, 0x54);
- spca506_WriteI2c(gspca_dev, 0xff, 0x55);
- spca506_WriteI2c(gspca_dev, 0xff, 0x56);
- spca506_WriteI2c(gspca_dev, 0xff, 0x57);
- spca506_WriteI2c(gspca_dev, 0x00, 0x58);
- spca506_WriteI2c(gspca_dev, 0x54, 0x59);
- spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
- spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
- spca506_WriteI2c(gspca_dev, 0x00, 0x60);
- spca506_WriteI2c(gspca_dev, 0x05, 0x61);
- spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
- PDEBUG(D_STREAM, "** Close Init *");
- return 0;
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct usb_device *dev = gspca_dev->dev;
- __u16 norme;
- __u16 channel;
-
- /**************************************/
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0x00, 0x0003);
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0xFF, 0x0003);
- reg_w(dev, 0x02, 0x00, 0x0000);
- reg_w(dev, 0x03, 0x60, 0x0000);
- reg_w(dev, 0x03, 0x18, 0x0001);
-
- /*sdca506_WriteI2c(value,register) */
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */
-/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
- spca506_WriteI2c(gspca_dev, 0x33, 0x03);
- /* Analog Input Control 2 */
- spca506_WriteI2c(gspca_dev, 0x00, 0x04);
- /* Analog Input Control 3 */
- spca506_WriteI2c(gspca_dev, 0x00, 0x05);
- /* Analog Input Control 4 */
- spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
- /* Horizontal Sync Start 0xe9-0x0d */
- spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
- /* Horizontal Sync Stop 0x0d-0xf0 */
-
- spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */
-/* Defaults value */
- spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */
- spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
- /* Luminance Brightness */
- spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */
- spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
- /* Chrominance Saturation */
- spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
- /* Chrominance Hue Control */
- spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
- /* Chrominance Gain Control */
- /**************************************/
- spca506_WriteI2c(gspca_dev, 0x00, 0x10);
- /* Format/Delay Control */
- spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */
- spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */
- spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */
- spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */
- spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */
- spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */
- spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */
- spca506_WriteI2c(gspca_dev, 0x00, 0x18);
- spca506_WriteI2c(gspca_dev, 0x00, 0x19);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
- spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
- spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
- spca506_WriteI2c(gspca_dev, 0x02, 0x40);
- spca506_WriteI2c(gspca_dev, 0xff, 0x41);
- spca506_WriteI2c(gspca_dev, 0xff, 0x42);
- spca506_WriteI2c(gspca_dev, 0xff, 0x43);
- spca506_WriteI2c(gspca_dev, 0xff, 0x44);
- spca506_WriteI2c(gspca_dev, 0xff, 0x45);
- spca506_WriteI2c(gspca_dev, 0xff, 0x46);
- spca506_WriteI2c(gspca_dev, 0xff, 0x47);
- spca506_WriteI2c(gspca_dev, 0xff, 0x48);
- spca506_WriteI2c(gspca_dev, 0xff, 0x49);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
- spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
- spca506_WriteI2c(gspca_dev, 0xff, 0x50);
- spca506_WriteI2c(gspca_dev, 0xff, 0x51);
- spca506_WriteI2c(gspca_dev, 0xff, 0x52);
- spca506_WriteI2c(gspca_dev, 0xff, 0x53);
- spca506_WriteI2c(gspca_dev, 0xff, 0x54);
- spca506_WriteI2c(gspca_dev, 0xff, 0x55);
- spca506_WriteI2c(gspca_dev, 0xff, 0x56);
- spca506_WriteI2c(gspca_dev, 0xff, 0x57);
- spca506_WriteI2c(gspca_dev, 0x00, 0x58);
- spca506_WriteI2c(gspca_dev, 0x54, 0x59);
- spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
- spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
- spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
- spca506_WriteI2c(gspca_dev, 0x00, 0x60);
- spca506_WriteI2c(gspca_dev, 0x05, 0x61);
- spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
- /**************************************/
- reg_w(dev, 0x05, 0x00, 0x0003);
- reg_w(dev, 0x05, 0x00, 0x0004);
- reg_w(dev, 0x03, 0x10, 0x0001);
- reg_w(dev, 0x03, 0x78, 0x0000);
- switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
- case 0:
- spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
- break;
- case 1:
- spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
- break;
- case 2:
- spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
- break;
- case 4:
- spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
- break;
- default:
-/* case 5: */
- spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
- break;
- }
-
- /* compress setting and size */
- /* set i2c luma */
- reg_w(dev, 0x02, 0x01, 0x0000);
- reg_w(dev, 0x03, 0x12, 0x0000);
- reg_r(gspca_dev, 0x04, 0x0001, 2);
- PDEBUG(D_STREAM, "webcam started");
- spca506_GetNormeInput(gspca_dev, &norme, &channel);
- spca506_SetNormeInput(gspca_dev, norme, channel);
- return 0;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct usb_device *dev = gspca_dev->dev;
-
- reg_w(dev, 0x02, 0x00, 0x0000);
- reg_w(dev, 0x03, 0x00, 0x0004);
- reg_w(dev, 0x03, 0x00, 0x0003);
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- switch (data[0]) {
- case 0: /* start of frame */
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- data += SPCA50X_OFFSET_DATA;
- len -= SPCA50X_OFFSET_DATA;
- gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
- break;
- case 0xff: /* drop */
-/* gspca_dev->last_packet_type = DISCARD_PACKET; */
- break;
- default:
- data += 1;
- len -= 1;
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
- break;
- }
-}
-
-static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
-{
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
- spca506_WriteI2c(gspca_dev, 0x01, 0x09);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
-{
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
- spca506_WriteI2c(gspca_dev, 0x01, 0x09);
-}
-
-static void setcolors(struct gspca_dev *gspca_dev, s32 val)
-{
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
- spca506_WriteI2c(gspca_dev, 0x01, 0x09);
-}
-
-static void sethue(struct gspca_dev *gspca_dev, s32 val)
-{
- spca506_Initi2c(gspca_dev);
- spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
- spca506_WriteI2c(gspca_dev, 0x01, 0x09);
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- setbrightness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- setcontrast(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- setcolors(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_HUE:
- sethue(gspca_dev, ctrl->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 0, 255, 1, 0x40);
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HUE, 0, 255, 1, 0);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .stopN = sd_stopN,
- .pkt_scan = sd_pkt_scan,
-};
-
-/* -- module initialisation -- */
-static const struct usb_device_id device_table[] __devinitconst = {
- {USB_DEVICE(0x06e1, 0xa190)},
-/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
- {USB_DEVICE(0x0733, 0x0430)}, */
- {USB_DEVICE(0x0734, 0x043b)},
- {USB_DEVICE(0x99fa, 0x8988)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- device connect -- */
-static int __devinit 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
deleted file mode 100644
index a8ac97931ad6..000000000000
--- a/drivers/media/video/gspca/sq905.c
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * SQ905 subdriver
- *
- * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
- *
- * This program is free software; you can redistribute it and/or modify
- * 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
- */
-
-/*
- * History and Acknowledgments
- *
- * The original Linux driver for SQ905 based cameras was written by
- * Marcell Lengyel and furter developed by many other contributors
- * and is available from http://sourceforge.net/projects/sqcam/
- *
- * This driver takes advantage of the reverse engineering work done for
- * that driver and for libgphoto2 but shares no code with them.
- *
- * This driver has used as a base the finepix driver and other gspca
- * based drivers and may still contain code fragments taken from those
- * drivers.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "sq905"
-
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-#include "gspca.h"
-
-MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, "
- "Theodore Kilgore <kilgota@auburn.edu>");
-MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* Default timeouts, in ms */
-#define SQ905_CMD_TIMEOUT 500
-#define SQ905_DATA_TIMEOUT 1000
-
-/* Maximum transfer size to use. */
-#define SQ905_MAX_TRANSFER 0x8000
-#define FRAME_HEADER_LEN 64
-
-/* The known modes, or registers. These go in the "value" slot. */
-
-/* 00 is "none" obviously */
-
-#define SQ905_BULK_READ 0x03 /* precedes any bulk read */
-#define SQ905_COMMAND 0x06 /* precedes the command codes below */
-#define SQ905_PING 0x07 /* when reading an "idling" command */
-#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
-
-/* Any non-zero value in the bottom 2 bits of the 2nd byte of
- * the ID appears to indicate the camera can do 640*480. If the
- * LSB of that byte is set the image is just upside down, otherwise
- * it is rotated 180 degrees. */
-#define SQ905_HIRES_MASK 0x00000300
-#define SQ905_ORIENTATION_MASK 0x00000100
-
-/* Some command codes. These go in the "index" slot. */
-
-#define SQ905_ID 0xf0 /* asks for model string */
-#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
-#define SQ905_DATA 0x30 /* accesses photo data, not used here */
-#define SQ905_CLEAR 0xa0 /* clear everything */
-#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
-#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
-#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
-/* note that the capture command also controls the output dimensions */
-
-/* Structure to hold all of our device specific stuff */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- /*
- * Driver stuff
- */
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
-};
-
-static struct v4l2_pix_format sq905_mode[] = {
- { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0}
-};
-
-/*
- * Send a command to the camera.
- */
-static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
-{
- int ret;
-
- gspca_dev->usb_buf[0] = '\0';
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
- SQ905_CMD_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
- return ret;
- }
-
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- SQ905_PING, 0, gspca_dev->usb_buf, 1,
- SQ905_CMD_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * Acknowledge the end of a frame - see warning on sq905_command.
- */
-static int sq905_ack_frame(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- gspca_dev->usb_buf[0] = '\0';
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
- SQ905_CMD_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * request and read a block of data - see warning on sq905_command.
- */
-static int
-sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
-{
- int ret;
- int act_len;
-
- gspca_dev->usb_buf[0] = '\0';
- if (need_lock)
- mutex_lock(&gspca_dev->usb_lock);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- SQ905_BULK_READ, size, gspca_dev->usb_buf,
- 1, SQ905_CMD_TIMEOUT);
- if (need_lock)
- mutex_unlock(&gspca_dev->usb_lock);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
- return ret;
- }
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x81),
- data, size, &act_len, SQ905_DATA_TIMEOUT);
-
- /* successful, it returns 0, otherwise negative */
- if (ret < 0 || act_len != size) {
- pr_err("bulk read fail (%d) len %d/%d\n", 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 sq905_dostream(struct work_struct *work)
-{
- struct sd *dev = container_of(work, struct sd, work_struct);
- struct gspca_dev *gspca_dev = &dev->gspca_dev;
- int bytes_left; /* bytes remaining in current frame. */
- int data_len; /* size to use for the next read. */
- int header_read; /* true if we have already read the frame header. */
- int packet_type;
- int frame_sz;
- int ret;
- u8 *data;
- u8 *buffer;
-
- buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
- if (!buffer) {
- pr_err("Couldn't allocate USB buffer\n");
- goto quit_stream;
- }
-
- frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
- + FRAME_HEADER_LEN;
-
- while (gspca_dev->dev && gspca_dev->streaming) {
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
- /* request some data and then read it until we have
- * a complete frame. */
- bytes_left = frame_sz;
- header_read = 0;
-
- /* Note we do not check for gspca_dev->streaming here, as
- we must finish reading an entire frame, otherwise the
- next time we stream we start reading in the middle of a
- frame. */
- while (bytes_left > 0 && gspca_dev->dev) {
- data_len = bytes_left > SQ905_MAX_TRANSFER ?
- SQ905_MAX_TRANSFER : bytes_left;
- ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
- if (ret < 0)
- goto quit_stream;
- PDEBUG(D_PACK,
- "Got %d bytes out of %d for frame",
- data_len, bytes_left);
- bytes_left -= data_len;
- data = buffer;
- if (!header_read) {
- packet_type = FIRST_PACKET;
- /* The first 64 bytes of each frame are
- * a header full of FF 00 bytes */
- data += FRAME_HEADER_LEN;
- data_len -= FRAME_HEADER_LEN;
- header_read = 1;
- } else if (bytes_left == 0) {
- packet_type = LAST_PACKET;
- } else {
- packet_type = INTER_PACKET;
- }
- gspca_frame_add(gspca_dev, packet_type,
- data, data_len);
- /* If entire frame fits in one packet we still
- need to add a LAST_PACKET */
- if (packet_type == FIRST_PACKET &&
- bytes_left == 0)
- gspca_frame_add(gspca_dev, LAST_PACKET,
- NULL, 0);
- }
- if (gspca_dev->dev) {
- /* acknowledge the frame */
- mutex_lock(&gspca_dev->usb_lock);
- ret = sq905_ack_frame(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- if (ret < 0)
- goto quit_stream;
- }
- }
-quit_stream:
- if (gspca_dev->dev) {
- mutex_lock(&gspca_dev->usb_lock);
- sq905_command(gspca_dev, SQ905_CLEAR);
- mutex_unlock(&gspca_dev->usb_lock);
- }
- 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 *dev = (struct sd *) gspca_dev;
-
- /* We don't use the buffer gspca allocates so make it small. */
- cam->bulk = 1;
- cam->bulk_size = 64;
-
- INIT_WORK(&dev->work_struct, sq905_dostream);
-
- 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 sq905_dostream to finish */
- destroy_workqueue(dev->work_thread);
- dev->work_thread = NULL;
- mutex_lock(&gspca_dev->usb_lock);
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- u32 ident;
- int ret;
-
- /* connect to the camera and read
- * the model ID and process that and put it away.
- */
- ret = sq905_command(gspca_dev, SQ905_CLEAR);
- if (ret < 0)
- return ret;
- ret = sq905_command(gspca_dev, SQ905_ID);
- if (ret < 0)
- return ret;
- ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
- if (ret < 0)
- return ret;
- /* usb_buf is allocated with kmalloc so is aligned.
- * Camera model number is the right way round if we assume this
- * reverse engineered ID is supposed to be big endian. */
- ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
- ret = sq905_command(gspca_dev, SQ905_CLEAR);
- if (ret < 0)
- return ret;
- PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
- gspca_dev->cam.cam_mode = sq905_mode;
- gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
- if (!(ident & SQ905_HIRES_MASK))
- gspca_dev->cam.nmodes--;
-
- if (ident & SQ905_ORIENTATION_MASK)
- gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
- else
- gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
- V4L2_IN_ST_HFLIP;
- return 0;
-}
-
-/* Set up for getting frames. */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *dev = (struct sd *) gspca_dev;
- int ret;
-
- /* "Open the shutter" and set size, to start capture */
- switch (gspca_dev->curr_mode) {
- default:
-/* case 2: */
- PDEBUG(D_STREAM, "Start streaming at high resolution");
- ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
- break;
- case 1:
- PDEBUG(D_STREAM, "Start streaming at medium resolution");
- ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
- break;
- case 0:
- PDEBUG(D_STREAM, "Start streaming at low resolution");
- ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
- }
-
- if (ret < 0) {
- PDEBUG(D_ERR, "Start streaming command failed");
- return ret;
- }
- /* Start the workqueue function to do the streaming */
- dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
- queue_work(dev->work_thread, &dev->work_struct);
-
- return 0;
-}
-
-/* Table of supported USB devices */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x2770, 0x9120)},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
deleted file mode 100644
index 2c2f3d2f357f..000000000000
--- a/drivers/media/video/gspca/sq905c.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * SQ905C subdriver
- *
- * Copyright (C) 2009 Theodore Kilgore
- *
- * This program is free software; you can redistribute it and/or modify
- * 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
- */
-
-/*
- *
- * This driver uses work done in
- * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
- *
- * This driver has also used as a base the sq905c driver
- * and may contain code fragments from it.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "sq905c"
-
-#include <linux/workqueue.h>
-#include <linux/slab.h>
-#include "gspca.h"
-
-MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
-MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* Default timeouts, in ms */
-#define SQ905C_CMD_TIMEOUT 500
-#define SQ905C_DATA_TIMEOUT 1000
-
-/* Maximum transfer size to use. */
-#define SQ905C_MAX_TRANSFER 0x8000
-
-#define FRAME_HEADER_LEN 0x50
-
-/* Commands. These go in the "value" slot. */
-#define SQ905C_CLEAR 0xa0 /* clear everything */
-#define SQ905C_GET_ID 0x14f4 /* Read version number */
-#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
-#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
-#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
-
-/* For capture, this must go in the "index" slot. */
-#define SQ905C_CAPTURE_INDEX 0x110f
-
-/* Structure to hold all of our device specific stuff */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- const struct v4l2_pix_format *cap_mode;
- /* Driver stuff */
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
-};
-
-/*
- * Most of these cameras will do 640x480 and 320x240. 160x120 works
- * in theory but gives very poor output. Therefore, not supported.
- * The 0x2770:0x9050 cameras have max resolution of 320x240.
- */
-static struct v4l2_pix_format sq905c_mode[] = {
- { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0}
-};
-
-/* Send a command to the camera. */
-static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
-{
- int ret;
-
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- command, index, NULL, 0,
- SQ905C_CMD_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
- return ret;
- }
-
- return 0;
-}
-
-static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
- int size)
-{
- int ret;
-
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- USB_REQ_SYNCH_FRAME, /* request */
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- command, index, gspca_dev->usb_buf, size,
- SQ905C_CMD_TIMEOUT);
- if (ret < 0) {
- pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
- return ret;
- }
-
- 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 the gspca usb_lock is
- * used when performing the one USB control operation inside the workqueue,
- * which tells the camera to close the stream. In practice the only thing
- * which needs to be protected against is the usb_set_interface call that
- * gspca makes during stream_off. Otherwise the camera doesn't provide any
- * controls that the user could try to change.
- */
-static void sq905c_dostream(struct work_struct *work)
-{
- struct sd *dev = container_of(work, struct sd, work_struct);
- struct gspca_dev *gspca_dev = &dev->gspca_dev;
- int bytes_left; /* bytes remaining in current frame. */
- int data_len; /* size to use for the next read. */
- int act_len;
- int packet_type;
- int ret;
- u8 *buffer;
-
- buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
- if (!buffer) {
- pr_err("Couldn't allocate USB buffer\n");
- goto quit_stream;
- }
-
- while (gspca_dev->dev && gspca_dev->streaming) {
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
- /* Request the header, which tells the size to download */
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x81),
- buffer, FRAME_HEADER_LEN, &act_len,
- SQ905C_DATA_TIMEOUT);
- PDEBUG(D_STREAM,
- "Got %d bytes out of %d for header",
- act_len, FRAME_HEADER_LEN);
- if (ret < 0 || act_len < FRAME_HEADER_LEN)
- goto quit_stream;
- /* size is read from 4 bytes starting 0x40, little endian */
- bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
- |(buffer[0x43]<<24);
- PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
- /* We keep the header. It has other information, too. */
- packet_type = FIRST_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- buffer, FRAME_HEADER_LEN);
- while (bytes_left > 0 && gspca_dev->dev) {
- data_len = bytes_left > SQ905C_MAX_TRANSFER ?
- SQ905C_MAX_TRANSFER : bytes_left;
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x81),
- buffer, data_len, &act_len,
- SQ905C_DATA_TIMEOUT);
- if (ret < 0 || act_len < data_len)
- goto quit_stream;
- PDEBUG(D_STREAM,
- "Got %d bytes out of %d for frame",
- data_len, bytes_left);
- bytes_left -= data_len;
- if (bytes_left == 0)
- packet_type = LAST_PACKET;
- else
- packet_type = INTER_PACKET;
- gspca_frame_add(gspca_dev, packet_type,
- buffer, data_len);
- }
- }
-quit_stream:
- if (gspca_dev->dev) {
- mutex_lock(&gspca_dev->usb_lock);
- sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
- mutex_unlock(&gspca_dev->usb_lock);
- }
- 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 *dev = (struct sd *) gspca_dev;
- int ret;
-
- PDEBUG(D_PROBE,
- "SQ9050 camera detected"
- " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
-
- ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
- if (ret < 0) {
- PDEBUG(D_ERR, "Get version command failed");
- return ret;
- }
-
- ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
- if (ret < 0) {
- PDEBUG(D_ERR, "Reading version command failed");
- return ret;
- }
- /* Note we leave out the usb id and the manufacturing date */
- PDEBUG(D_PROBE,
- "SQ9050 ID string: %02x - %02x %02x %02x %02x %02x %02x",
- gspca_dev->usb_buf[3],
- gspca_dev->usb_buf[14], gspca_dev->usb_buf[15],
- gspca_dev->usb_buf[16], gspca_dev->usb_buf[17],
- gspca_dev->usb_buf[18], gspca_dev->usb_buf[19]);
-
- cam->cam_mode = sq905c_mode;
- cam->nmodes = 2;
- if (gspca_dev->usb_buf[15] == 0)
- cam->nmodes = 1;
- /* We don't use the buffer gspca allocates so make it small. */
- cam->bulk_size = 32;
- cam->bulk = 1;
- INIT_WORK(&dev->work_struct, sq905c_dostream);
- 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 sq905c_dostream to finish */
- destroy_workqueue(dev->work_thread);
- dev->work_thread = NULL;
- mutex_lock(&gspca_dev->usb_lock);
-}
-
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
-{
- int ret;
-
- /* connect to the camera and reset it. */
- ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
- return ret;
-}
-
-/* Set up for getting frames. */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *dev = (struct sd *) gspca_dev;
- int ret;
-
- dev->cap_mode = gspca_dev->cam.cam_mode;
- /* "Open the shutter" and set size, to start capture */
- switch (gspca_dev->width) {
- case 640:
- PDEBUG(D_STREAM, "Start streaming at high resolution");
- dev->cap_mode++;
- ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
- SQ905C_CAPTURE_INDEX);
- break;
- default: /* 320 */
- PDEBUG(D_STREAM, "Start streaming at medium resolution");
- ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
- SQ905C_CAPTURE_INDEX);
- }
-
- if (ret < 0) {
- PDEBUG(D_ERR, "Start streaming command failed");
- return ret;
- }
- /* Start the workqueue function to do the streaming */
- dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
- queue_work(dev->work_thread, &dev->work_struct);
-
- return 0;
-}
-
-/* Table of supported USB devices */
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x2770, 0x905c)},
- {USB_DEVICE(0x2770, 0x9050)},
- {USB_DEVICE(0x2770, 0x9051)},
- {USB_DEVICE(0x2770, 0x9052)},
- {USB_DEVICE(0x2770, 0x913d)},
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/sq930x.c b/drivers/media/video/gspca/sq930x.c
deleted file mode 100644
index 3e1e486af883..000000000000
--- a/drivers/media/video/gspca/sq930x.c
+++ /dev/null
@@ -1,1172 +0,0 @@
-/*
- * SQ930x subdriver
- *
- * Copyright (C) 2010 Jean-François Moine <http://moinejf.free.fr>
- * Copyright (C) 2006 -2008 Gerard Klaver <gerard at gkall dot hobby dot nl>
- * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
- *
- * This program is free software; you can redistribute it and/or modify
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "sq930x"
-
-#include "gspca.h"
-
-MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>\n"
- "Gerard Klaver <gerard at gkall dot hobby dot nl\n"
- "Sam Revitch <samr7@cs.washington.edu>");
-MODULE_DESCRIPTION("GSPCA/SQ930x USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* Structure to hold all of our device specific stuff */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct { /* exposure/gain control cluster */
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *gain;
- };
-
- u8 do_ctrl;
- u8 gpio[2];
- u8 sensor;
- u8 type;
-#define Generic 0
-#define Creative_live_motion 1
-};
-enum sensors {
- SENSOR_ICX098BQ,
- SENSOR_LZ24BP,
- SENSOR_MI0360,
- SENSOR_MT9V111, /* = MI360SOC */
- SENSOR_OV7660,
- SENSOR_OV9630,
-};
-
-static struct v4l2_pix_format vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
- {640, 480, V4L2_PIX_FMT_SRGGB8, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
-};
-
-/* sq930x registers */
-#define SQ930_CTRL_UCBUS_IO 0x0001
-#define SQ930_CTRL_I2C_IO 0x0002
-#define SQ930_CTRL_GPIO 0x0005
-#define SQ930_CTRL_CAP_START 0x0010
-#define SQ930_CTRL_CAP_STOP 0x0011
-#define SQ930_CTRL_SET_EXPOSURE 0x001d
-#define SQ930_CTRL_RESET 0x001e
-#define SQ930_CTRL_GET_DEV_INFO 0x001f
-
-/* gpio 1 (8..15) */
-#define SQ930_GPIO_DFL_I2C_SDA 0x0001
-#define SQ930_GPIO_DFL_I2C_SCL 0x0002
-#define SQ930_GPIO_RSTBAR 0x0004
-#define SQ930_GPIO_EXTRA1 0x0040
-#define SQ930_GPIO_EXTRA2 0x0080
-/* gpio 3 (24..31) */
-#define SQ930_GPIO_POWER 0x0200
-#define SQ930_GPIO_DFL_LED 0x1000
-
-struct ucbus_write_cmd {
- u16 bw_addr;
- u8 bw_data;
-};
-struct i2c_write_cmd {
- u8 reg;
- u16 val;
-};
-
-static const struct ucbus_write_cmd icx098bq_start_0[] = {
- {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xce},
- {0xf802, 0xc1}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x0e},
- {0xf80a, 0x01}, {0xf80b, 0xee}, {0xf807, 0x60}, {0xf80c, 0x02},
- {0xf80d, 0xf0}, {0xf80e, 0x03}, {0xf80f, 0x0a}, {0xf81c, 0x02},
- {0xf81d, 0xf0}, {0xf81e, 0x03}, {0xf81f, 0x0a}, {0xf83a, 0x00},
- {0xf83b, 0x10}, {0xf83c, 0x00}, {0xf83d, 0x4e}, {0xf810, 0x04},
- {0xf811, 0x00}, {0xf812, 0x02}, {0xf813, 0x10}, {0xf803, 0x00},
- {0xf814, 0x01}, {0xf815, 0x18}, {0xf816, 0x00}, {0xf817, 0x48},
- {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
- {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
- {0xf823, 0x07}, {0xf824, 0xff}, {0xf825, 0x03}, {0xf826, 0xff},
- {0xf827, 0x06}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
- {0xf82b, 0x0c}, {0xf82c, 0xfd}, {0xf82d, 0x01}, {0xf82e, 0x00},
- {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
- {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
- {0xf854, 0x00}, {0xf855, 0x18}, {0xf856, 0x00}, {0xf857, 0x3c},
- {0xf858, 0x00}, {0xf859, 0x0c}, {0xf85a, 0x00}, {0xf85b, 0x30},
- {0xf85c, 0x00}, {0xf85d, 0x0c}, {0xf85e, 0x00}, {0xf85f, 0x30},
- {0xf860, 0x00}, {0xf861, 0x48}, {0xf862, 0x01}, {0xf863, 0xdc},
- {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
- {0xf868, 0xff}, {0xf869, 0x70}, {0xf86c, 0xff}, {0xf86d, 0x00},
- {0xf86a, 0xff}, {0xf86b, 0x48}, {0xf86e, 0xff}, {0xf86f, 0x00},
- {0xf870, 0x01}, {0xf871, 0xdb}, {0xf872, 0x01}, {0xf873, 0xfa},
- {0xf874, 0x01}, {0xf875, 0xdb}, {0xf876, 0x01}, {0xf877, 0xfa},
- {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
- {0xf800, 0x03}
-};
-static const struct ucbus_write_cmd icx098bq_start_1[] = {
- {0xf5f0, 0x00}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xc0},
- {0xf5f0, 0x49}, {0xf5f1, 0xcd}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xc0},
- {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
- {0xf5f9, 0x00}
-};
-
-static const struct ucbus_write_cmd icx098bq_start_2[] = {
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x82}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x40}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xcf}, {0xf806, 0xd0},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03}
-};
-
-static const struct ucbus_write_cmd lz24bp_start_0[] = {
- {0x0354, 0x00}, {0x03fa, 0x00}, {0xf800, 0x02}, {0xf801, 0xbe},
- {0xf802, 0xc6}, {0xf804, 0x00}, {0xf808, 0x00}, {0xf809, 0x06},
- {0xf80a, 0x01}, {0xf80b, 0xfe}, {0xf807, 0x84}, {0xf80c, 0x02},
- {0xf80d, 0xf7}, {0xf80e, 0x03}, {0xf80f, 0x0b}, {0xf81c, 0x00},
- {0xf81d, 0x49}, {0xf81e, 0x03}, {0xf81f, 0x0b}, {0xf83a, 0x00},
- {0xf83b, 0x01}, {0xf83c, 0x00}, {0xf83d, 0x6b}, {0xf810, 0x03},
- {0xf811, 0x10}, {0xf812, 0x02}, {0xf813, 0x6f}, {0xf803, 0x00},
- {0xf814, 0x00}, {0xf815, 0x44}, {0xf816, 0x00}, {0xf817, 0x48},
- {0xf818, 0x00}, {0xf819, 0x25}, {0xf81a, 0x00}, {0xf81b, 0x3c},
- {0xf82f, 0x03}, {0xf820, 0xff}, {0xf821, 0x0d}, {0xf822, 0xff},
- {0xf823, 0x07}, {0xf824, 0xfd}, {0xf825, 0x07}, {0xf826, 0xf0},
- {0xf827, 0x0c}, {0xf828, 0xff}, {0xf829, 0x03}, {0xf82a, 0xff},
- {0xf82b, 0x0c}, {0xf82c, 0xfc}, {0xf82d, 0x01}, {0xf82e, 0x00},
- {0xf830, 0x00}, {0xf831, 0x47}, {0xf832, 0x00}, {0xf833, 0x00},
- {0xf850, 0x00}, {0xf851, 0x00}, {0xf852, 0x00}, {0xf853, 0x24},
- {0xf854, 0x00}, {0xf855, 0x0c}, {0xf856, 0x00}, {0xf857, 0x30},
- {0xf858, 0x00}, {0xf859, 0x18}, {0xf85a, 0x00}, {0xf85b, 0x3c},
- {0xf85c, 0x00}, {0xf85d, 0x18}, {0xf85e, 0x00}, {0xf85f, 0x3c},
- {0xf860, 0xff}, {0xf861, 0x37}, {0xf862, 0xff}, {0xf863, 0x1d},
- {0xf864, 0xff}, {0xf865, 0x98}, {0xf866, 0xff}, {0xf867, 0xc0},
- {0xf868, 0x00}, {0xf869, 0x37}, {0xf86c, 0x02}, {0xf86d, 0x1d},
- {0xf86a, 0x00}, {0xf86b, 0x37}, {0xf86e, 0x02}, {0xf86f, 0x1d},
- {0xf870, 0x01}, {0xf871, 0xc6}, {0xf872, 0x02}, {0xf873, 0x04},
- {0xf874, 0x01}, {0xf875, 0xc6}, {0xf876, 0x02}, {0xf877, 0x04},
- {0xf878, 0x0f}, {0xf879, 0x0f}, {0xf87a, 0xff}, {0xf87b, 0xff},
- {0xf800, 0x03}
-};
-static const struct ucbus_write_cmd lz24bp_start_1_gen[] = {
- {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xb3},
- {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xb3},
- {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
- {0xf5f9, 0x00}
-};
-
-static const struct ucbus_write_cmd lz24bp_start_1_clm[] = {
- {0xf5f0, 0x00}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
- {0xf5f4, 0xc0},
- {0xf5f0, 0x40}, {0xf5f1, 0xff}, {0xf5f2, 0x88}, {0xf5f3, 0x88},
- {0xf5f4, 0xc0},
- {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
- {0xf5f9, 0x00}
-};
-
-static const struct ucbus_write_cmd lz24bp_start_2[] = {
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x80}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x4e}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0xc0}, {0xf806, 0x48},
- {0xf807, 0x7f}, {0xf800, 0x03},
- {0xf800, 0x02}, {0xf807, 0xff}, {0xf805, 0x00}, {0xf806, 0x00},
- {0xf807, 0x7f}, {0xf800, 0x03}
-};
-
-static const struct ucbus_write_cmd mi0360_start_0[] = {
- {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0xcc}, {0xf333, 0xcc},
- {0xf334, 0xcc}, {0xf335, 0xcc}, {0xf33f, 0x00}
-};
-static const struct i2c_write_cmd mi0360_init_23[] = {
- {0x30, 0x0040}, /* reserved - def 0x0005 */
- {0x31, 0x0000}, /* reserved - def 0x002a */
- {0x34, 0x0100}, /* reserved - def 0x0100 */
- {0x3d, 0x068f}, /* reserved - def 0x068f */
-};
-static const struct i2c_write_cmd mi0360_init_24[] = {
- {0x03, 0x01e5}, /* window height */
- {0x04, 0x0285}, /* window width */
-};
-static const struct i2c_write_cmd mi0360_init_25[] = {
- {0x35, 0x0020}, /* global gain */
- {0x2b, 0x0020}, /* green1 gain */
- {0x2c, 0x002a}, /* blue gain */
- {0x2d, 0x0028}, /* red gain */
- {0x2e, 0x0020}, /* green2 gain */
-};
-static const struct ucbus_write_cmd mi0360_start_1[] = {
- {0xf5f0, 0x11}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xa6},
- {0xf5f0, 0x51}, {0xf5f1, 0x99}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xa6},
- {0xf5fa, 0x00}, {0xf5f6, 0x00}, {0xf5f7, 0x00}, {0xf5f8, 0x00},
- {0xf5f9, 0x00}
-};
-static const struct i2c_write_cmd mi0360_start_2[] = {
- {0x62, 0x041d}, /* reserved - def 0x0418 */
-};
-static const struct i2c_write_cmd mi0360_start_3[] = {
- {0x05, 0x007b}, /* horiz blanking */
-};
-static const struct i2c_write_cmd mi0360_start_4[] = {
- {0x05, 0x03f5}, /* horiz blanking */
-};
-
-static const struct i2c_write_cmd mt9v111_init_0[] = {
- {0x01, 0x0001}, /* select IFP/SOC registers */
- {0x06, 0x300c}, /* operating mode control */
- {0x08, 0xcc00}, /* output format control (RGB) */
- {0x01, 0x0004}, /* select sensor core registers */
-};
-static const struct i2c_write_cmd mt9v111_init_1[] = {
- {0x03, 0x01e5}, /* window height */
- {0x04, 0x0285}, /* window width */
-};
-static const struct i2c_write_cmd mt9v111_init_2[] = {
- {0x30, 0x7800},
- {0x31, 0x0000},
- {0x07, 0x3002}, /* output control */
- {0x35, 0x0020}, /* global gain */
- {0x2b, 0x0020}, /* green1 gain */
- {0x2c, 0x0020}, /* blue gain */
- {0x2d, 0x0020}, /* red gain */
- {0x2e, 0x0020}, /* green2 gain */
-};
-static const struct ucbus_write_cmd mt9v111_start_1[] = {
- {0xf5f0, 0x11}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xaa},
- {0xf5f0, 0x51}, {0xf5f1, 0x96}, {0xf5f2, 0x80}, {0xf5f3, 0x80},
- {0xf5f4, 0xaa},
- {0xf5fa, 0x00}, {0xf5f6, 0x0a}, {0xf5f7, 0x0a}, {0xf5f8, 0x0a},
- {0xf5f9, 0x0a}
-};
-static const struct i2c_write_cmd mt9v111_init_3[] = {
- {0x62, 0x0405},
-};
-static const struct i2c_write_cmd mt9v111_init_4[] = {
-/* {0x05, 0x00ce}, */
- {0x05, 0x005d}, /* horizontal blanking */
-};
-
-static const struct ucbus_write_cmd ov7660_start_0[] = {
- {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0xc0},
- {0xf334, 0x39}, {0xf335, 0xe7}, {0xf33f, 0x03}
-};
-
-static const struct ucbus_write_cmd ov9630_start_0[] = {
- {0x0354, 0x00}, {0x03fa, 0x00}, {0xf332, 0x00}, {0xf333, 0x00},
- {0xf334, 0x3e}, {0xf335, 0xf8}, {0xf33f, 0x03}
-};
-
-/* start parameters indexed by [sensor][mode] */
-static const struct cap_s {
- u8 cc_sizeid;
- u8 cc_bytes[32];
-} capconfig[4][2] = {
- [SENSOR_ICX098BQ] = {
- {2, /* Bayer 320x240 */
- {0x05, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- {4, /* Bayer 640x480 */
- {0x01, 0x1f, 0x20, 0x0e, 0x00, 0x9f, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- },
- [SENSOR_LZ24BP] = {
- {2, /* Bayer 320x240 */
- {0x05, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
- 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- {4, /* Bayer 640x480 */
- {0x01, 0x22, 0x20, 0x0e, 0x00, 0xa2, 0x02, 0xee,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- },
- [SENSOR_MI0360] = {
- {2, /* Bayer 320x240 */
- {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
- 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- {4, /* Bayer 640x480 */
- {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- },
- [SENSOR_MT9V111] = {
- {2, /* Bayer 320x240 */
- {0x05, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
- 0x01, 0x01, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- {4, /* Bayer 640x480 */
- {0x01, 0x02, 0x20, 0x01, 0x20, 0x82, 0x02, 0xe1,
- 0x01, 0x02, 0x00, 0x08, 0x18, 0x12, 0x78, 0xc8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} },
- },
-};
-
-struct sensor_s {
- const char *name;
- u8 i2c_addr;
- u8 i2c_dum;
- u8 gpio[5];
- u8 cmd_len;
- const struct ucbus_write_cmd *cmd;
-};
-
-static const struct sensor_s sensor_tb[] = {
- [SENSOR_ICX098BQ] = {
- "icx098bp",
- 0x00, 0x00,
- {0,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- SQ930_GPIO_RSTBAR
- },
- 8, icx098bq_start_0
- },
- [SENSOR_LZ24BP] = {
- "lz24bp",
- 0x00, 0x00,
- {0,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- SQ930_GPIO_RSTBAR
- },
- 8, lz24bp_start_0
- },
- [SENSOR_MI0360] = {
- "mi0360",
- 0x5d, 0x80,
- {SQ930_GPIO_RSTBAR,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- 0
- },
- 7, mi0360_start_0
- },
- [SENSOR_MT9V111] = {
- "mt9v111",
- 0x5c, 0x7f,
- {SQ930_GPIO_RSTBAR,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- 0
- },
- 7, mi0360_start_0
- },
- [SENSOR_OV7660] = {
- "ov7660",
- 0x21, 0x00,
- {0,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- SQ930_GPIO_RSTBAR
- },
- 7, ov7660_start_0
- },
- [SENSOR_OV9630] = {
- "ov9630",
- 0x30, 0x00,
- {0,
- SQ930_GPIO_DFL_I2C_SDA | SQ930_GPIO_DFL_I2C_SCL,
- SQ930_GPIO_DFL_I2C_SDA,
- 0,
- SQ930_GPIO_RSTBAR
- },
- 7, ov9630_start_0
- },
-};
-
-static void reg_r(struct gspca_dev *gspca_dev,
- u16 value, int len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0x0c,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, 0, gspca_dev->usb_buf, len,
- 500);
- if (ret < 0) {
- pr_err("reg_r %04x failed %d\n", value, ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "reg_w v: %04x i: %04x", value, index);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x0c, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0,
- 500);
- msleep(30);
- if (ret < 0) {
- pr_err("reg_w %04x %04x failed %d\n", value, index, ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void reg_wb(struct gspca_dev *gspca_dev, u16 value, u16 index,
- const u8 *data, int len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "reg_wb v: %04x i: %04x %02x...%02x",
- value, index, *data, data[len - 1]);
- memcpy(gspca_dev->usb_buf, data, len);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x0c, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, gspca_dev->usb_buf, len,
- 1000);
- msleep(30);
- if (ret < 0) {
- pr_err("reg_wb %04x %04x failed %d\n", value, index, ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void i2c_write(struct sd *sd,
- const struct i2c_write_cmd *cmd,
- int ncmds)
-{
- struct gspca_dev *gspca_dev = &sd->gspca_dev;
- const struct sensor_s *sensor;
- u16 val, idx;
- u8 *buf;
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
-
- sensor = &sensor_tb[sd->sensor];
-
- val = (sensor->i2c_addr << 8) | SQ930_CTRL_I2C_IO;
- idx = (cmd->val & 0xff00) | cmd->reg;
-
- buf = gspca_dev->usb_buf;
- *buf++ = sensor->i2c_dum;
- *buf++ = cmd->val;
-
- while (--ncmds > 0) {
- cmd++;
- *buf++ = cmd->reg;
- *buf++ = cmd->val >> 8;
- *buf++ = sensor->i2c_dum;
- *buf++ = cmd->val;
- }
-
- PDEBUG(D_USBO, "i2c_w v: %04x i: %04x %02x...%02x",
- val, idx, gspca_dev->usb_buf[0], buf[-1]);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x0c, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- val, idx,
- gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
- 500);
- if (ret < 0) {
- pr_err("i2c_write failed %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void ucbus_write(struct gspca_dev *gspca_dev,
- const struct ucbus_write_cmd *cmd,
- int ncmds,
- int batchsize)
-{
- u8 *buf;
- u16 val, idx;
- int len, ret;
-
- if (gspca_dev->usb_err < 0)
- return;
-
-#ifdef GSPCA_DEBUG
- if ((batchsize - 1) * 3 > USB_BUF_SZ) {
- pr_err("Bug: usb_buf overflow\n");
- gspca_dev->usb_err = -ENOMEM;
- return;
- }
-#endif
-
- for (;;) {
- len = ncmds;
- if (len > batchsize)
- len = batchsize;
- ncmds -= len;
-
- val = (cmd->bw_addr << 8) | SQ930_CTRL_UCBUS_IO;
- idx = (cmd->bw_data << 8) | (cmd->bw_addr >> 8);
-
- buf = gspca_dev->usb_buf;
- while (--len > 0) {
- cmd++;
- *buf++ = cmd->bw_addr;
- *buf++ = cmd->bw_addr >> 8;
- *buf++ = cmd->bw_data;
- }
- if (buf != gspca_dev->usb_buf)
- PDEBUG(D_USBO, "ucbus v: %04x i: %04x %02x...%02x",
- val, idx,
- gspca_dev->usb_buf[0], buf[-1]);
- else
- PDEBUG(D_USBO, "ucbus v: %04x i: %04x",
- val, idx);
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0x0c, /* request */
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- val, idx,
- gspca_dev->usb_buf, buf - gspca_dev->usb_buf,
- 500);
- if (ret < 0) {
- pr_err("ucbus_write failed %d\n", ret);
- gspca_dev->usb_err = ret;
- return;
- }
- msleep(30);
- if (ncmds <= 0)
- break;
- cmd++;
- }
-}
-
-static void gpio_set(struct sd *sd, u16 val, u16 mask)
-{
- struct gspca_dev *gspca_dev = &sd->gspca_dev;
-
- if (mask & 0x00ff) {
- sd->gpio[0] &= ~mask;
- sd->gpio[0] |= val;
- reg_w(gspca_dev, 0x0100 | SQ930_CTRL_GPIO,
- ~sd->gpio[0] << 8);
- }
- mask >>= 8;
- val >>= 8;
- if (mask) {
- sd->gpio[1] &= ~mask;
- sd->gpio[1] |= val;
- reg_w(gspca_dev, 0x0300 | SQ930_CTRL_GPIO,
- ~sd->gpio[1] << 8);
- }
-}
-
-static void gpio_init(struct sd *sd,
- const u8 *gpio)
-{
- gpio_set(sd, *gpio++, 0x000f);
- gpio_set(sd, *gpio++, 0x000f);
- gpio_set(sd, *gpio++, 0x000f);
- gpio_set(sd, *gpio++, 0x000f);
- gpio_set(sd, *gpio, 0x000f);
-}
-
-static void bridge_init(struct sd *sd)
-{
- static const struct ucbus_write_cmd clkfreq_cmd = {
- 0xf031, 0 /* SQ930_CLKFREQ_60MHZ */
- };
-
- ucbus_write(&sd->gspca_dev, &clkfreq_cmd, 1, 1);
-
- gpio_set(sd, SQ930_GPIO_POWER, 0xff00);
-}
-
-static void cmos_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
- const struct sensor_s *sensor;
- static const u8 probe_order[] = {
-/* SENSOR_LZ24BP, (tested as ccd) */
- SENSOR_OV9630,
- SENSOR_MI0360,
- SENSOR_OV7660,
- SENSOR_MT9V111,
- };
-
- for (i = 0; i < ARRAY_SIZE(probe_order); i++) {
- sensor = &sensor_tb[probe_order[i]];
- ucbus_write(&sd->gspca_dev, sensor->cmd, sensor->cmd_len, 8);
- gpio_init(sd, sensor->gpio);
- msleep(100);
- reg_r(gspca_dev, (sensor->i2c_addr << 8) | 0x001c, 1);
- msleep(100);
- if (gspca_dev->usb_buf[0] != 0)
- break;
- }
- if (i >= ARRAY_SIZE(probe_order)) {
- pr_err("Unknown sensor\n");
- gspca_dev->usb_err = -EINVAL;
- return;
- }
- sd->sensor = probe_order[i];
- switch (sd->sensor) {
- case SENSOR_OV7660:
- case SENSOR_OV9630:
- pr_err("Sensor %s not yet treated\n",
- sensor_tb[sd->sensor].name);
- gspca_dev->usb_err = -EINVAL;
- break;
- }
-}
-
-static void mt9v111_init(struct gspca_dev *gspca_dev)
-{
- int i, nwait;
- static const u8 cmd_001b[] = {
- 0x00, 0x3b, 0xf6, 0x01, 0x03, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x00
- };
- static const u8 cmd_011b[][7] = {
- {0x10, 0x01, 0x66, 0x08, 0x00, 0x00, 0x00},
- {0x01, 0x00, 0x1a, 0x04, 0x00, 0x00, 0x00},
- {0x20, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00},
- {0x02, 0x01, 0xae, 0x01, 0x00, 0x00, 0x00},
- };
-
- reg_wb(gspca_dev, 0x001b, 0x0000, cmd_001b, sizeof cmd_001b);
- for (i = 0; i < ARRAY_SIZE(cmd_011b); i++) {
- reg_wb(gspca_dev, 0x001b, 0x0000, cmd_011b[i],
- ARRAY_SIZE(cmd_011b[0]));
- msleep(400);
- nwait = 20;
- for (;;) {
- reg_r(gspca_dev, 0x031b, 1);
- if (gspca_dev->usb_buf[0] == 0
- || gspca_dev->usb_err != 0)
- break;
- if (--nwait < 0) {
- PDEBUG(D_PROBE, "mt9v111_init timeout");
- gspca_dev->usb_err = -ETIME;
- return;
- }
- msleep(50);
- }
- }
-}
-
-static void global_init(struct sd *sd, int first_time)
-{
- switch (sd->sensor) {
- case SENSOR_ICX098BQ:
- if (first_time)
- ucbus_write(&sd->gspca_dev,
- icx098bq_start_0,
- 8, 8);
- gpio_init(sd, sensor_tb[sd->sensor].gpio);
- break;
- case SENSOR_LZ24BP:
- if (sd->type != Creative_live_motion)
- gpio_set(sd, SQ930_GPIO_EXTRA1, 0x00ff);
- else
- gpio_set(sd, 0, 0x00ff);
- msleep(50);
- if (first_time)
- ucbus_write(&sd->gspca_dev,
- lz24bp_start_0,
- 8, 8);
- gpio_init(sd, sensor_tb[sd->sensor].gpio);
- break;
- case SENSOR_MI0360:
- if (first_time)
- ucbus_write(&sd->gspca_dev,
- mi0360_start_0,
- ARRAY_SIZE(mi0360_start_0),
- 8);
- gpio_init(sd, sensor_tb[sd->sensor].gpio);
- gpio_set(sd, SQ930_GPIO_EXTRA2, SQ930_GPIO_EXTRA2);
- break;
- default:
-/* case SENSOR_MT9V111: */
- if (first_time)
- mt9v111_init(&sd->gspca_dev);
- else
- gpio_init(sd, sensor_tb[sd->sensor].gpio);
- break;
- }
-}
-
-static void lz24bp_ppl(struct sd *sd, u16 ppl)
-{
- struct ucbus_write_cmd cmds[2] = {
- {0xf810, ppl >> 8},
- {0xf811, ppl}
- };
-
- ucbus_write(&sd->gspca_dev, cmds, ARRAY_SIZE(cmds), 2);
-}
-
-static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, integclks, intstartclk, frameclks, min_frclk;
- const struct sensor_s *sensor;
- u16 cmd;
- u8 buf[15];
-
- integclks = expo;
- i = 0;
- cmd = SQ930_CTRL_SET_EXPOSURE;
-
- switch (sd->sensor) {
- case SENSOR_ICX098BQ: /* ccd */
- case SENSOR_LZ24BP:
- min_frclk = sd->sensor == SENSOR_ICX098BQ ? 0x210 : 0x26f;
- if (integclks >= min_frclk) {
- intstartclk = 0;
- frameclks = integclks;
- } else {
- intstartclk = min_frclk - integclks;
- frameclks = min_frclk;
- }
- buf[i++] = intstartclk >> 8;
- buf[i++] = intstartclk;
- buf[i++] = frameclks >> 8;
- buf[i++] = frameclks;
- buf[i++] = gain;
- break;
- default: /* cmos */
-/* case SENSOR_MI0360: */
-/* case SENSOR_MT9V111: */
- cmd |= 0x0100;
- sensor = &sensor_tb[sd->sensor];
- buf[i++] = sensor->i2c_addr; /* i2c_slave_addr */
- buf[i++] = 0x08; /* 2 * ni2c */
- buf[i++] = 0x09; /* reg = shutter width */
- buf[i++] = integclks >> 8; /* val H */
- buf[i++] = sensor->i2c_dum;
- buf[i++] = integclks; /* val L */
- buf[i++] = 0x35; /* reg = global gain */
- buf[i++] = 0x00; /* val H */
- buf[i++] = sensor->i2c_dum;
- buf[i++] = 0x80 + gain / 2; /* val L */
- buf[i++] = 0x00;
- buf[i++] = 0x00;
- buf[i++] = 0x00;
- buf[i++] = 0x00;
- buf[i++] = 0x83;
- break;
- }
- reg_wb(gspca_dev, cmd, 0, buf, i);
-}
-
-/* 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 sd *sd = (struct sd *) gspca_dev;
- struct cam *cam = &gspca_dev->cam;
-
- sd->sensor = id->driver_info >> 8;
- sd->type = id->driver_info;
-
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
-
- cam->bulk = 1;
-
- 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;
-
- sd->gpio[0] = sd->gpio[1] = 0xff; /* force gpio rewrite */
-
-/*fixme: is this needed for icx098bp and mi0360?
- if (sd->sensor != SENSOR_LZ24BP)
- reg_w(gspca_dev, SQ930_CTRL_RESET, 0x0000);
- */
-
- reg_r(gspca_dev, SQ930_CTRL_GET_DEV_INFO, 8);
- if (gspca_dev->usb_err < 0)
- return gspca_dev->usb_err;
-
-/* it returns:
- * 03 00 12 93 0b f6 c9 00 live! ultra
- * 03 00 07 93 0b f6 ca 00 live! ultra for notebook
- * 03 00 12 93 0b fe c8 00 Trust WB-3500T
- * 02 00 06 93 0b fe c8 00 Joy-IT 318S
- * 03 00 12 93 0b f6 cf 00 icam tracer - sensor icx098bq
- * 02 00 12 93 0b fe cf 00 ProQ Motion Webcam
- *
- * byte
- * 0: 02 = usb 1.0 (12Mbit) / 03 = usb2.0 (480Mbit)
- * 1: 00
- * 2: 06 / 07 / 12 = mode webcam? firmware??
- * 3: 93 chip = 930b (930b or 930c)
- * 4: 0b
- * 5: f6 = cdd (icx098bq, lz24bp) / fe or de = cmos (i2c) (other sensors)
- * 6: c8 / c9 / ca / cf = mode webcam?, sensor? webcam?
- * 7: 00
- */
- PDEBUG(D_PROBE, "info: %02x %02x %02x %02x %02x %02x %02x %02x",
- gspca_dev->usb_buf[0],
- gspca_dev->usb_buf[1],
- gspca_dev->usb_buf[2],
- gspca_dev->usb_buf[3],
- gspca_dev->usb_buf[4],
- gspca_dev->usb_buf[5],
- gspca_dev->usb_buf[6],
- gspca_dev->usb_buf[7]);
-
- bridge_init(sd);
-
- if (sd->sensor == SENSOR_MI0360) {
-
- /* no sensor probe for icam tracer */
- if (gspca_dev->usb_buf[5] == 0xf6) /* if ccd */
- sd->sensor = SENSOR_ICX098BQ;
- else
- cmos_probe(gspca_dev);
- }
- if (gspca_dev->usb_err >= 0) {
- PDEBUG(D_PROBE, "Sensor %s", sensor_tb[sd->sensor].name);
- global_init(sd, 1);
- }
- return gspca_dev->usb_err;
-}
-
-/* send the start/stop commands to the webcam */
-static void send_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- const struct cap_s *cap;
- int mode;
-
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- cap = &capconfig[sd->sensor][mode];
- reg_wb(gspca_dev, 0x0900 | SQ930_CTRL_CAP_START,
- 0x0a00 | cap->cc_sizeid,
- cap->cc_bytes, 32);
-}
-
-static void send_stop(struct gspca_dev *gspca_dev)
-{
- reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0);
-}
-
-/* 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;
-
- gspca_dev->cam.bulk_nurbs = 1; /* there must be one URB only */
- sd->do_ctrl = 0;
- gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height + 8;
- return 0;
-}
-
-/* start the capture */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int mode;
-
- bridge_init(sd);
- global_init(sd, 0);
- msleep(100);
-
- switch (sd->sensor) {
- case SENSOR_ICX098BQ:
- ucbus_write(gspca_dev, icx098bq_start_0,
- ARRAY_SIZE(icx098bq_start_0),
- 8);
- ucbus_write(gspca_dev, icx098bq_start_1,
- ARRAY_SIZE(icx098bq_start_1),
- 5);
- ucbus_write(gspca_dev, icx098bq_start_2,
- ARRAY_SIZE(icx098bq_start_2),
- 6);
- msleep(50);
-
- /* 1st start */
- send_start(gspca_dev);
- gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
- msleep(70);
- reg_w(gspca_dev, SQ930_CTRL_CAP_STOP, 0x0000);
- gpio_set(sd, 0x7f, 0x00ff);
-
- /* 2nd start */
- send_start(gspca_dev);
- gpio_set(sd, SQ930_GPIO_EXTRA2 | SQ930_GPIO_RSTBAR, 0x00ff);
- goto out;
- case SENSOR_LZ24BP:
- ucbus_write(gspca_dev, lz24bp_start_0,
- ARRAY_SIZE(lz24bp_start_0),
- 8);
- if (sd->type != Creative_live_motion)
- ucbus_write(gspca_dev, lz24bp_start_1_gen,
- ARRAY_SIZE(lz24bp_start_1_gen),
- 5);
- else
- ucbus_write(gspca_dev, lz24bp_start_1_clm,
- ARRAY_SIZE(lz24bp_start_1_clm),
- 5);
- ucbus_write(gspca_dev, lz24bp_start_2,
- ARRAY_SIZE(lz24bp_start_2),
- 6);
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- lz24bp_ppl(sd, mode == 1 ? 0x0564 : 0x0310);
- msleep(10);
- break;
- case SENSOR_MI0360:
- ucbus_write(gspca_dev, mi0360_start_0,
- ARRAY_SIZE(mi0360_start_0),
- 8);
- i2c_write(sd, mi0360_init_23,
- ARRAY_SIZE(mi0360_init_23));
- i2c_write(sd, mi0360_init_24,
- ARRAY_SIZE(mi0360_init_24));
- i2c_write(sd, mi0360_init_25,
- ARRAY_SIZE(mi0360_init_25));
- ucbus_write(gspca_dev, mi0360_start_1,
- ARRAY_SIZE(mi0360_start_1),
- 5);
- i2c_write(sd, mi0360_start_2,
- ARRAY_SIZE(mi0360_start_2));
- i2c_write(sd, mi0360_start_3,
- ARRAY_SIZE(mi0360_start_3));
-
- /* 1st start */
- send_start(gspca_dev);
- msleep(60);
- send_stop(gspca_dev);
-
- i2c_write(sd,
- mi0360_start_4, ARRAY_SIZE(mi0360_start_4));
- break;
- default:
-/* case SENSOR_MT9V111: */
- ucbus_write(gspca_dev, mi0360_start_0,
- ARRAY_SIZE(mi0360_start_0),
- 8);
- i2c_write(sd, mt9v111_init_0,
- ARRAY_SIZE(mt9v111_init_0));
- i2c_write(sd, mt9v111_init_1,
- ARRAY_SIZE(mt9v111_init_1));
- i2c_write(sd, mt9v111_init_2,
- ARRAY_SIZE(mt9v111_init_2));
- ucbus_write(gspca_dev, mt9v111_start_1,
- ARRAY_SIZE(mt9v111_start_1),
- 5);
- i2c_write(sd, mt9v111_init_3,
- ARRAY_SIZE(mt9v111_init_3));
- i2c_write(sd, mt9v111_init_4,
- ARRAY_SIZE(mt9v111_init_4));
- break;
- }
-
- send_start(gspca_dev);
-out:
- msleep(1000);
-
- if (sd->sensor == SENSOR_MT9V111)
- gpio_set(sd, SQ930_GPIO_DFL_LED, SQ930_GPIO_DFL_LED);
-
- sd->do_ctrl = 1; /* set the exposure */
-
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_MT9V111)
- gpio_set(sd, 0, SQ930_GPIO_DFL_LED);
- send_stop(gspca_dev);
-}
-
-/* function called when the application gets a new frame */
-/* It sets the exposure if required and restart the bulk transfer. */
-static void sd_dq_callback(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- if (!sd->do_ctrl || gspca_dev->cam.bulk_nurbs != 0)
- return;
- sd->do_ctrl = 0;
-
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure),
- v4l2_ctrl_g_ctrl(sd->gain));
-
- gspca_dev->cam.bulk_nurbs = 1;
- ret = usb_submit_urb(gspca_dev->urb[0], GFP_ATOMIC);
- if (ret < 0)
- pr_err("sd_dq_callback() err %d\n", ret);
-
- /* wait a little time, otherwise the webcam crashes */
- msleep(100);
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso packet length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->do_ctrl)
- gspca_dev->cam.bulk_nurbs = 0;
- gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len - 8);
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *) gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_EXPOSURE:
- setexposure(gspca_dev, ctrl->val, sd->gain->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- struct sd *sd = (struct sd *) gspca_dev;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 2);
- sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, 0xfff, 1, 0x356);
- sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 1, 255, 1, 0x8d);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- v4l2_ctrl_cluster(2, &sd->exposure);
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .isoc_init = sd_isoc_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .pkt_scan = sd_pkt_scan,
- .dq_callback = sd_dq_callback,
-};
-
-/* Table of supported USB devices */
-#define ST(sensor, type) \
- .driver_info = (SENSOR_ ## sensor << 8) \
- | (type)
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x041e, 0x4038), ST(MI0360, 0)},
- {USB_DEVICE(0x041e, 0x403c), ST(LZ24BP, 0)},
- {USB_DEVICE(0x041e, 0x403d), ST(LZ24BP, 0)},
- {USB_DEVICE(0x041e, 0x4041), ST(LZ24BP, Creative_live_motion)},
- {USB_DEVICE(0x2770, 0x930b), ST(MI0360, 0)},
- {USB_DEVICE(0x2770, 0x930c), ST(MI0360, 0)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
deleted file mode 100644
index 38bc41061d83..000000000000
--- a/drivers/media/video/gspca/stv06xx/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o
-
-gspca_stv06xx-objs := stv06xx.o \
- stv06xx_vv6410.o \
- stv06xx_hdcs.o \
- stv06xx_pb0100.o \
- stv06xx_st6422.o
-
-ccflags-y += -I$(srctree)/drivers/media/video/gspca
-
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
deleted file mode 100644
index a6055246cb9d..000000000000
--- a/drivers/media/video/gspca/topro.c
+++ /dev/null
@@ -1,4969 +0,0 @@
-/*
- * Topro TP6800/6810 webcam driver.
- *
- * Copyright (C) 2011 Jean-François Moine (http://moinejf.free.fr)
- * Copyright (C) 2009 Anders Blomdell (anders.blomdell@control.lth.se)
- * Copyright (C) 2008 Thomas Champagne (lafeuil@gmail.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; If not, see <http://www.gnu.org/licenses/>.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "gspca.h"
-
-MODULE_DESCRIPTION("Topro TP6800/6810 gspca webcam driver");
-MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
- "Anders Blomdell <anders.blomdell@control.lth.se>");
-MODULE_LICENSE("GPL");
-
-static int force_sensor = -1;
-
-/* JPEG header */
-static const u8 jpeg_head[] = {
- 0xff, 0xd8, /* jpeg */
-
-/* quantization table quality 50% */
- 0xff, 0xdb, 0x00, 0x84, /* DQT */
-0,
-#define JPEG_QT0_OFFSET 7
- 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
- 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
- 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
- 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
- 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
- 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
- 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
- 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
-1,
-#define JPEG_QT1_OFFSET 72
- 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
- 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-
- /* Define Huffman table (thanks to Thomas Kaiser) */
- 0xff, 0xc4, 0x01, 0x5e,
- 0x00, 0x00, 0x02, 0x03,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
- 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
- 0x07, 0x05, 0x04, 0x06, 0x01, 0x00, 0x00, 0x57,
- 0x01, 0x02, 0x03, 0x00, 0x11, 0x04, 0x12, 0x21,
- 0x31, 0x13, 0x41, 0x51, 0x61, 0x05, 0x22, 0x32,
- 0x14, 0x71, 0x81, 0x91, 0x15, 0x23, 0x42, 0x52,
- 0x62, 0xa1, 0xb1, 0x06, 0x33, 0x72, 0xc1, 0xd1,
- 0x24, 0x43, 0x53, 0x82, 0x16, 0x34, 0x92, 0xa2,
- 0xe1, 0xf1, 0xf0, 0x07, 0x08, 0x17, 0x18, 0x25,
- 0x26, 0x27, 0x28, 0x35, 0x36, 0x37, 0x38, 0x44,
- 0x45, 0x46, 0x47, 0x48, 0x54, 0x55, 0x56, 0x57,
- 0x58, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x73,
- 0x74, 0x75, 0x76, 0x77, 0x78, 0x83, 0x84, 0x85,
- 0x86, 0x87, 0x88, 0x93, 0x94, 0x95, 0x96, 0x97,
- 0x98, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2,
- 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3,
- 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4,
- 0xd5, 0xd6, 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5,
- 0xe6, 0xe7, 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
- 0xf7, 0xf8, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
- 0x05, 0x06, 0x07, 0x08, 0x09, 0x11, 0x00, 0x02,
- 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
- 0x04, 0x06, 0x01, 0x00, 0x00, 0x57, 0x00, 0x01,
- 0x11, 0x02, 0x21, 0x03, 0x12, 0x31, 0x41, 0x13,
- 0x22, 0x51, 0x61, 0x04, 0x32, 0x71, 0x05, 0x14,
- 0x23, 0x42, 0x33, 0x52, 0x81, 0x91, 0xa1, 0xb1,
- 0xf0, 0x06, 0x15, 0xc1, 0xd1, 0xe1, 0x24, 0x43,
- 0x62, 0xf1, 0x16, 0x25, 0x34, 0x53, 0x72, 0x82,
- 0x92, 0x07, 0x08, 0x17, 0x18, 0x26, 0x27, 0x28,
- 0x35, 0x36, 0x37, 0x38, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x63, 0x64,
- 0x65, 0x66, 0x67, 0x68, 0x73, 0x74, 0x75, 0x76,
- 0x77, 0x78, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
- 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0xa2, 0xa3,
- 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xb2, 0xb3, 0xb4,
- 0xb5, 0xb6, 0xb7, 0xb8, 0xc2, 0xc3, 0xc4, 0xc5,
- 0xc6, 0xc7, 0xc8, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
- 0xd7, 0xd8, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
- 0xe8, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
- 0x08, /* data precision */
-#define JPEG_HEIGHT_OFFSET 493
- 0x01, 0xe0, /* height */
- 0x02, 0x80, /* width */
- 0x03, /* component number */
- 0x01,
- 0x21, /* samples Y = jpeg 422 */
- 0x00, /* quant Y */
- 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
- 0x03, 0x11, 0x01,
-
- 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
- 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
-#define JPEG_HDR_SZ 521
-};
-
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- struct v4l2_ctrl *jpegqual;
- struct v4l2_ctrl *sharpness;
- struct v4l2_ctrl *gamma;
- struct v4l2_ctrl *blue;
- struct v4l2_ctrl *red;
-
- u8 framerate;
- u8 quality; /* webcam current JPEG quality (0..16) */
- s8 ag_cnt; /* autogain / start counter for tp6810 */
-#define AG_CNT_START 13 /* check gain every N frames */
-
- u8 bridge;
- u8 sensor;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
-};
-
-enum bridges {
- BRIDGE_TP6800,
- BRIDGE_TP6810,
-};
-
-enum sensors {
- SENSOR_CX0342,
- SENSOR_SOI763A, /* ~= ov7630 / ov7648 */
- NSENSORS
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 4 / 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}
-};
-
-/*
- * JPEG quality
- * index: webcam compression
- * value: JPEG quality in %
- */
-static const u8 jpeg_q[17] = {
- 88, 77, 67, 57, 55, 55, 45, 45, 36, 36, 30, 30, 26, 26, 22, 22, 94
-};
-
-#define BULK_OUT_SIZE 0x20
-#if BULK_OUT_SIZE > USB_BUF_SZ
-#error "USB buffer too small"
-#endif
-
-static const u8 rates[] = {30, 20, 15, 10, 7, 5};
-static const struct framerates framerates[] = {
- {
- .rates = rates,
- .nrates = ARRAY_SIZE(rates)
- },
- {
- .rates = rates,
- .nrates = ARRAY_SIZE(rates)
- }
-};
-static const u8 rates_6810[] = {30, 15, 10, 7, 5};
-static const struct framerates framerates_6810[] = {
- {
- .rates = rates_6810,
- .nrates = ARRAY_SIZE(rates_6810)
- },
- {
- .rates = rates_6810,
- .nrates = ARRAY_SIZE(rates_6810)
- }
-};
-
-/*
- * webcam quality in %
- * the last value is the ultra fine quality
- */
-
-/* TP6800 register offsets */
-#define TP6800_R10_SIF_TYPE 0x10
-#define TP6800_R11_SIF_CONTROL 0x11
-#define TP6800_R12_SIF_ADDR_S 0x12
-#define TP6800_R13_SIF_TX_DATA 0x13
-#define TP6800_R14_SIF_RX_DATA 0x14
-#define TP6800_R15_GPIO_PU 0x15
-#define TP6800_R16_GPIO_PD 0x16
-#define TP6800_R17_GPIO_IO 0x17
-#define TP6800_R18_GPIO_DATA 0x18
-#define TP6800_R19_SIF_ADDR_S2 0x19
-#define TP6800_R1A_SIF_TX_DATA2 0x1a
-#define TP6800_R1B_SIF_RX_DATA2 0x1b
-#define TP6800_R21_ENDP_1_CTL 0x21
-#define TP6800_R2F_TIMING_CFG 0x2f
-#define TP6800_R30_SENSOR_CFG 0x30
-#define TP6800_R31_PIXEL_START 0x31
-#define TP6800_R32_PIXEL_END_L 0x32
-#define TP6800_R33_PIXEL_END_H 0x33
-#define TP6800_R34_LINE_START 0x34
-#define TP6800_R35_LINE_END_L 0x35
-#define TP6800_R36_LINE_END_H 0x36
-#define TP6800_R37_FRONT_DARK_ST 0x37
-#define TP6800_R38_FRONT_DARK_END 0x38
-#define TP6800_R39_REAR_DARK_ST_L 0x39
-#define TP6800_R3A_REAR_DARK_ST_H 0x3a
-#define TP6800_R3B_REAR_DARK_END_L 0x3b
-#define TP6800_R3C_REAR_DARK_END_H 0x3c
-#define TP6800_R3D_HORIZ_DARK_LINE_L 0x3d
-#define TP6800_R3E_HORIZ_DARK_LINE_H 0x3e
-#define TP6800_R3F_FRAME_RATE 0x3f
-#define TP6800_R50 0x50
-#define TP6800_R51 0x51
-#define TP6800_R52 0x52
-#define TP6800_R53 0x53
-#define TP6800_R54_DARK_CFG 0x54
-#define TP6800_R55_GAMMA_R 0x55
-#define TP6800_R56_GAMMA_G 0x56
-#define TP6800_R57_GAMMA_B 0x57
-#define TP6800_R5C_EDGE_THRLD 0x5c
-#define TP6800_R5D_DEMOSAIC_CFG 0x5d
-#define TP6800_R78_FORMAT 0x78
-#define TP6800_R79_QUALITY 0x79
-#define TP6800_R7A_BLK_THRLD 0x7a
-
-/* CX0342 register offsets */
-
-#define CX0342_SENSOR_ID 0x00
-#define CX0342_VERSION_NO 0x01
-#define CX0342_ORG_X_L 0x02
-#define CX0342_ORG_X_H 0x03
-#define CX0342_ORG_Y_L 0x04
-#define CX0342_ORG_Y_H 0x05
-#define CX0342_STOP_X_L 0x06
-#define CX0342_STOP_X_H 0x07
-#define CX0342_STOP_Y_L 0x08
-#define CX0342_STOP_Y_H 0x09
-#define CX0342_FRAME_WIDTH_L 0x0a
-#define CX0342_FRAME_WIDTH_H 0x0b
-#define CX0342_FRAME_HEIGH_L 0x0c
-#define CX0342_FRAME_HEIGH_H 0x0d
-#define CX0342_EXPO_LINE_L 0x10
-#define CX0342_EXPO_LINE_H 0x11
-#define CX0342_EXPO_CLK_L 0x12
-#define CX0342_EXPO_CLK_H 0x13
-#define CX0342_RAW_GRGAIN_L 0x14
-#define CX0342_RAW_GRGAIN_H 0x15
-#define CX0342_RAW_GBGAIN_L 0x16
-#define CX0342_RAW_GBGAIN_H 0x17
-#define CX0342_RAW_RGAIN_L 0x18
-#define CX0342_RAW_RGAIN_H 0x19
-#define CX0342_RAW_BGAIN_L 0x1a
-#define CX0342_RAW_BGAIN_H 0x1b
-#define CX0342_GLOBAL_GAIN 0x1c
-#define CX0342_SYS_CTRL_0 0x20
-#define CX0342_SYS_CTRL_1 0x21
-#define CX0342_SYS_CTRL_2 0x22
-#define CX0342_BYPASS_MODE 0x23
-#define CX0342_SYS_CTRL_3 0x24
-#define CX0342_TIMING_EN 0x25
-#define CX0342_OUTPUT_CTRL 0x26
-#define CX0342_AUTO_ADC_CALIB 0x27
-#define CX0342_SYS_CTRL_4 0x28
-#define CX0342_ADCGN 0x30
-#define CX0342_SLPCR 0x31
-#define CX0342_SLPFN_LO 0x32
-#define CX0342_ADC_CTL 0x33
-#define CX0342_LVRST_BLBIAS 0x34
-#define CX0342_VTHSEL 0x35
-#define CX0342_RAMP_RIV 0x36
-#define CX0342_LDOSEL 0x37
-#define CX0342_CLOCK_GEN 0x40
-#define CX0342_SOFT_RESET 0x41
-#define CX0342_PLL 0x42
-#define CX0342_DR_ENH_PULSE_OFFSET_L 0x43
-#define CX0342_DR_ENH_PULSE_OFFSET_H 0x44
-#define CX0342_DR_ENH_PULSE_POS_L 0x45
-#define CX0342_DR_ENH_PULSE_POS_H 0x46
-#define CX0342_DR_ENH_PULSE_WIDTH 0x47
-#define CX0342_AS_CURRENT_CNT_L 0x48
-#define CX0342_AS_CURRENT_CNT_H 0x49
-#define CX0342_AS_PREVIOUS_CNT_L 0x4a
-#define CX0342_AS_PREVIOUS_CNT_H 0x4b
-#define CX0342_SPV_VALUE_L 0x4c
-#define CX0342_SPV_VALUE_H 0x4d
-#define CX0342_GPXLTHD_L 0x50
-#define CX0342_GPXLTHD_H 0x51
-#define CX0342_RBPXLTHD_L 0x52
-#define CX0342_RBPXLTHD_H 0x53
-#define CX0342_PLANETHD_L 0x54
-#define CX0342_PLANETHD_H 0x55
-#define CX0342_ROWDARK_TH 0x56
-#define CX0342_ROWDARK_TOL 0x57
-#define CX0342_RB_GAP_L 0x58
-#define CX0342_RB_GAP_H 0x59
-#define CX0342_G_GAP_L 0x5a
-#define CX0342_G_GAP_H 0x5b
-#define CX0342_AUTO_ROW_DARK 0x60
-#define CX0342_MANUAL_DARK_VALUE 0x61
-#define CX0342_GB_DARK_OFFSET 0x62
-#define CX0342_GR_DARK_OFFSET 0x63
-#define CX0342_RED_DARK_OFFSET 0x64
-#define CX0342_BLUE_DARK_OFFSET 0x65
-#define CX0342_DATA_SCALING_MULTI 0x66
-#define CX0342_AUTOD_Q_FRAME 0x67
-#define CX0342_AUTOD_ALLOW_VARI 0x68
-#define CX0342_AUTO_DARK_VALUE_L 0x69
-#define CX0342_AUTO_DARK_VALUE_H 0x6a
-#define CX0342_IO_CTRL_0 0x70
-#define CX0342_IO_CTRL_1 0x71
-#define CX0342_IO_CTRL_2 0x72
-#define CX0342_IDLE_CTRL 0x73
-#define CX0342_TEST_MODE 0x74
-#define CX0342_FRAME_FIX_DATA_TEST 0x75
-#define CX0342_FRAME_CNT_TEST 0x76
-#define CX0342_RST_OVERFLOW_L 0x80
-#define CX0342_RST_OVERFLOW_H 0x81
-#define CX0342_RST_UNDERFLOW_L 0x82
-#define CX0342_RST_UNDERFLOW_H 0x83
-#define CX0342_DATA_OVERFLOW_L 0x84
-#define CX0342_DATA_OVERFLOW_H 0x85
-#define CX0342_DATA_UNDERFLOW_L 0x86
-#define CX0342_DATA_UNDERFLOW_H 0x87
-#define CX0342_CHANNEL_0_0_L_irst 0x90
-#define CX0342_CHANNEL_0_0_H_irst 0x91
-#define CX0342_CHANNEL_0_1_L_irst 0x92
-#define CX0342_CHANNEL_0_1_H_irst 0x93
-#define CX0342_CHANNEL_0_2_L_irst 0x94
-#define CX0342_CHANNEL_0_2_H_irst 0x95
-#define CX0342_CHANNEL_0_3_L_irst 0x96
-#define CX0342_CHANNEL_0_3_H_irst 0x97
-#define CX0342_CHANNEL_0_4_L_irst 0x98
-#define CX0342_CHANNEL_0_4_H_irst 0x99
-#define CX0342_CHANNEL_0_5_L_irst 0x9a
-#define CX0342_CHANNEL_0_5_H_irst 0x9b
-#define CX0342_CHANNEL_0_6_L_irst 0x9c
-#define CX0342_CHANNEL_0_6_H_irst 0x9d
-#define CX0342_CHANNEL_0_7_L_irst 0x9e
-#define CX0342_CHANNEL_0_7_H_irst 0x9f
-#define CX0342_CHANNEL_1_0_L_itx 0xa0
-#define CX0342_CHANNEL_1_0_H_itx 0xa1
-#define CX0342_CHANNEL_1_1_L_itx 0xa2
-#define CX0342_CHANNEL_1_1_H_itx 0xa3
-#define CX0342_CHANNEL_1_2_L_itx 0xa4
-#define CX0342_CHANNEL_1_2_H_itx 0xa5
-#define CX0342_CHANNEL_1_3_L_itx 0xa6
-#define CX0342_CHANNEL_1_3_H_itx 0xa7
-#define CX0342_CHANNEL_1_4_L_itx 0xa8
-#define CX0342_CHANNEL_1_4_H_itx 0xa9
-#define CX0342_CHANNEL_1_5_L_itx 0xaa
-#define CX0342_CHANNEL_1_5_H_itx 0xab
-#define CX0342_CHANNEL_1_6_L_itx 0xac
-#define CX0342_CHANNEL_1_6_H_itx 0xad
-#define CX0342_CHANNEL_1_7_L_itx 0xae
-#define CX0342_CHANNEL_1_7_H_itx 0xaf
-#define CX0342_CHANNEL_2_0_L_iwl 0xb0
-#define CX0342_CHANNEL_2_0_H_iwl 0xb1
-#define CX0342_CHANNEL_2_1_L_iwl 0xb2
-#define CX0342_CHANNEL_2_1_H_iwl 0xb3
-#define CX0342_CHANNEL_2_2_L_iwl 0xb4
-#define CX0342_CHANNEL_2_2_H_iwl 0xb5
-#define CX0342_CHANNEL_2_3_L_iwl 0xb6
-#define CX0342_CHANNEL_2_3_H_iwl 0xb7
-#define CX0342_CHANNEL_2_4_L_iwl 0xb8
-#define CX0342_CHANNEL_2_4_H_iwl 0xb9
-#define CX0342_CHANNEL_2_5_L_iwl 0xba
-#define CX0342_CHANNEL_2_5_H_iwl 0xbb
-#define CX0342_CHANNEL_2_6_L_iwl 0xbc
-#define CX0342_CHANNEL_2_6_H_iwl 0xbd
-#define CX0342_CHANNEL_2_7_L_iwl 0xbe
-#define CX0342_CHANNEL_2_7_H_iwl 0xbf
-#define CX0342_CHANNEL_3_0_L_ensp 0xc0
-#define CX0342_CHANNEL_3_0_H_ensp 0xc1
-#define CX0342_CHANNEL_3_1_L_ensp 0xc2
-#define CX0342_CHANNEL_3_1_H_ensp 0xc3
-#define CX0342_CHANNEL_3_2_L_ensp 0xc4
-#define CX0342_CHANNEL_3_2_H_ensp 0xc5
-#define CX0342_CHANNEL_3_3_L_ensp 0xc6
-#define CX0342_CHANNEL_3_3_H_ensp 0xc7
-#define CX0342_CHANNEL_3_4_L_ensp 0xc8
-#define CX0342_CHANNEL_3_4_H_ensp 0xc9
-#define CX0342_CHANNEL_3_5_L_ensp 0xca
-#define CX0342_CHANNEL_3_5_H_ensp 0xcb
-#define CX0342_CHANNEL_3_6_L_ensp 0xcc
-#define CX0342_CHANNEL_3_6_H_ensp 0xcd
-#define CX0342_CHANNEL_3_7_L_ensp 0xce
-#define CX0342_CHANNEL_3_7_H_ensp 0xcf
-#define CX0342_CHANNEL_4_0_L_sela 0xd0
-#define CX0342_CHANNEL_4_0_H_sela 0xd1
-#define CX0342_CHANNEL_4_1_L_sela 0xd2
-#define CX0342_CHANNEL_4_1_H_sela 0xd3
-#define CX0342_CHANNEL_5_0_L_intla 0xe0
-#define CX0342_CHANNEL_5_0_H_intla 0xe1
-#define CX0342_CHANNEL_5_1_L_intla 0xe2
-#define CX0342_CHANNEL_5_1_H_intla 0xe3
-#define CX0342_CHANNEL_5_2_L_intla 0xe4
-#define CX0342_CHANNEL_5_2_H_intla 0xe5
-#define CX0342_CHANNEL_5_3_L_intla 0xe6
-#define CX0342_CHANNEL_5_3_H_intla 0xe7
-#define CX0342_CHANNEL_6_0_L_xa_sel_pos 0xf0
-#define CX0342_CHANNEL_6_0_H_xa_sel_pos 0xf1
-#define CX0342_CHANNEL_7_1_L_cds_pos 0xf2
-#define CX0342_CHANNEL_7_1_H_cds_pos 0xf3
-#define CX0342_SENSOR_HEIGHT_L 0xfb
-#define CX0342_SENSOR_HEIGHT_H 0xfc
-#define CX0342_SENSOR_WIDTH_L 0xfd
-#define CX0342_SENSOR_WIDTH_H 0xfe
-#define CX0342_VSYNC_HSYNC_READ 0xff
-
-struct cmd {
- u8 reg;
- u8 val;
-};
-
-static const u8 DQT[17][130] = {
- /* Define quantization table (thanks to Thomas Kaiser) */
- { /* Quality 0 */
- 0x00,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x01,
- 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0b, 0x06,
- 0x06, 0x0b, 0x18, 0x10, 0x0e, 0x10, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- },
- { /* Quality 1 */
- 0x00,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
- 0x01,
- 0x08, 0x09, 0x09, 0x0c, 0x0a, 0x0c, 0x17, 0x0d,
- 0x0d, 0x17, 0x31, 0x21, 0x1c, 0x21, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
- },
- { /* Quality 2 */
- 0x00,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x06, 0x06, 0x06, 0x04, 0x04, 0x04,
- 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x01,
- 0x0c, 0x0d, 0x0d, 0x12, 0x0f, 0x12, 0x23, 0x13,
- 0x13, 0x23, 0x4a, 0x31, 0x2a, 0x31, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
- },
- { /* Quality 3 */
- 0x00,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x08, 0x08, 0x08, 0x04, 0x04, 0x04,
- 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x08, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13,
- 0x01,
- 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
- 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- },
- { /* Quality 4 */
- 0x00,
- 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
- 0x05, 0x05, 0x0a, 0x0a, 0x0a, 0x05, 0x05, 0x05,
- 0x05, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x0a, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
- 0x01,
- 0x11, 0x16, 0x16, 0x1e, 0x1a, 0x1e, 0x3a, 0x20,
- 0x20, 0x3a, 0x7b, 0x52, 0x46, 0x52, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b, 0x7b,
- },
- { /* Quality 5 */
- 0x00,
- 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
- 0x06, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06,
- 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x0c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x01,
- 0x11, 0x1b, 0x1b, 0x24, 0x1f, 0x24, 0x46, 0x27,
- 0x27, 0x46, 0x94, 0x63, 0x54, 0x63, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94,
- },
- { /* Quality 6 */
- 0x00,
- 0x05, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
- 0x07, 0x07, 0x0e, 0x0e, 0x0e, 0x07, 0x07, 0x07,
- 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x0e, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21,
- 0x01,
- 0x15, 0x1f, 0x1f, 0x2a, 0x24, 0x2a, 0x52, 0x2d,
- 0x2d, 0x52, 0xad, 0x73, 0x62, 0x73, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad,
- },
- { /* Quality 7 */
- 0x00,
- 0x05, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
- 0x08, 0x08, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08,
- 0x08, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x10, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,
- 0x01,
- 0x15, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34,
- 0x34, 0x5e, 0xc6, 0x84, 0x70, 0x84, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- },
- { /* Quality 8 */
- 0x00,
- 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x0a, 0x14, 0x14, 0x14, 0x0a, 0x0a, 0x0a,
- 0x0a, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x14, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f,
- 0x01,
- 0x19, 0x2d, 0x2d, 0x3c, 0x34, 0x3c, 0x75, 0x41,
- 0x41, 0x75, 0xf7, 0xa5, 0x8c, 0xa5, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- },
- { /* Quality 9 */
- 0x00,
- 0x06, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x0c, 0x0c,
- 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x18, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
- 0x01,
- 0x19, 0x36, 0x36, 0x48, 0x3f, 0x48, 0x8d, 0x4e,
- 0x4e, 0x8d, 0xff, 0xc6, 0xa8, 0xc6, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 10 */
- 0x00,
- 0x07, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x0e, 0x1c, 0x1c, 0x1c, 0x0e, 0x0e, 0x0e,
- 0x0e, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x1c, 0x42, 0x42, 0x42, 0x42, 0x42,
- 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
- 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
- 0x01,
- 0x1d, 0x3f, 0x3f, 0x54, 0x49, 0x54, 0xa4, 0x5b,
- 0x5b, 0xa4, 0xff, 0xe7, 0xc4, 0xe7, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 11 */
- 0x00,
- 0x07, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
- 0x10, 0x10, 0x20, 0x20, 0x20, 0x10, 0x10, 0x10,
- 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
- 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
- 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,
- 0x01,
- 0x1d, 0x48, 0x48, 0x60, 0x54, 0x60, 0xbc, 0x68,
- 0x68, 0xbc, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 12 */
- 0x00,
- 0x08, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
- 0x14, 0x14, 0x28, 0x28, 0x28, 0x14, 0x14, 0x14,
- 0x14, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
- 0x28, 0x28, 0x28, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
- 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
- 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x5f,
- 0x01,
- 0x22, 0x5a, 0x5a, 0x78, 0x69, 0x78, 0xeb, 0x82,
- 0x82, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 13 */
- 0x00,
- 0x08, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
- 0x18, 0x18, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18,
- 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
- 0x30, 0x30, 0x30, 0x72, 0x72, 0x72, 0x72, 0x72,
- 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
- 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72,
- 0x01,
- 0x22, 0x6c, 0x6c, 0x90, 0x7e, 0x90, 0xff, 0x9c,
- 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 14 */
- 0x00,
- 0x0a, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x1c, 0x38, 0x38, 0x38, 0x1c, 0x1c, 0x1c,
- 0x1c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
- 0x38, 0x38, 0x38, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
- 0x01,
- 0x2a, 0x7e, 0x7e, 0xa8, 0x93, 0xa8, 0xff, 0xb6,
- 0xb6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 15 */
- 0x00,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20,
- 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
- 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
- 0x40, 0x40, 0x40, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
- 0x01,
- 0x2a, 0x90, 0x90, 0xc0, 0xa8, 0xc0, 0xff, 0xd0,
- 0xd0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- },
- { /* Quality 16-31 */
- 0x00,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x01,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
- }
-};
-
-static const struct cmd tp6810_cx_init_common[] = {
- {0x1c, 0x00},
- {TP6800_R10_SIF_TYPE, 0x00},
- {0x4e, 0x00},
- {0x4f, 0x00},
- {TP6800_R50, 0xff},
- {TP6800_R51, 0x03},
- {0x00, 0x07},
- {TP6800_R79_QUALITY, 0x03},
- {TP6800_R2F_TIMING_CFG, 0x37},
- {TP6800_R30_SENSOR_CFG, 0x10},
- {TP6800_R21_ENDP_1_CTL, 0x00},
- {TP6800_R52, 0x40},
- {TP6800_R53, 0x40},
- {TP6800_R54_DARK_CFG, 0x40},
- {TP6800_R30_SENSOR_CFG, 0x18},
- {0x4b, 0x00},
- {TP6800_R3F_FRAME_RATE, 0x83},
- {TP6800_R79_QUALITY, 0x05},
- {TP6800_R21_ENDP_1_CTL, 0x00},
- {0x7c, 0x04},
- {0x25, 0x14},
- {0x26, 0x0f},
- {0x7b, 0x10},
-};
-
-static const struct cmd tp6810_ov_init_common[] = {
- {0x1c, 0x00},
- {TP6800_R10_SIF_TYPE, 0x00},
- {0x4e, 0x00},
- {0x4f, 0x00},
- {TP6800_R50, 0xff},
- {TP6800_R51, 0x03},
- {0x00, 0x07},
- {TP6800_R52, 0x40},
- {TP6800_R53, 0x40},
- {TP6800_R54_DARK_CFG, 0x40},
- {TP6800_R79_QUALITY, 0x03},
- {TP6800_R2F_TIMING_CFG, 0x17},
- {TP6800_R30_SENSOR_CFG, 0x18},
- {TP6800_R21_ENDP_1_CTL, 0x00},
- {TP6800_R3F_FRAME_RATE, 0x86},
- {0x25, 0x18},
- {0x26, 0x0f},
- {0x7b, 0x90},
-};
-
-static const struct cmd tp6810_bridge_start[] = {
- {0x59, 0x88},
- {0x5a, 0x0f},
- {0x5b, 0x4e},
- {TP6800_R5C_EDGE_THRLD, 0x63},
- {TP6800_R5D_DEMOSAIC_CFG, 0x00},
- {0x03, 0x7f},
- {0x04, 0x80},
- {0x06, 0x00},
- {0x00, 0x00},
-};
-
-static const struct cmd tp6810_late_start[] = {
- {0x7d, 0x01},
- {0xb0, 0x04},
- {0xb1, 0x04},
- {0xb2, 0x04},
- {0xb3, 0x04},
- {0xb4, 0x04},
- {0xb5, 0x04},
- {0xb6, 0x08},
- {0xb7, 0x08},
- {0xb8, 0x04},
- {0xb9, 0x04},
- {0xba, 0x04},
- {0xbb, 0x04},
- {0xbc, 0x04},
- {0xbd, 0x08},
- {0xbe, 0x08},
- {0xbf, 0x08},
- {0xc0, 0x04},
- {0xc1, 0x04},
- {0xc2, 0x08},
- {0xc3, 0x08},
- {0xc4, 0x08},
- {0xc5, 0x08},
- {0xc6, 0x08},
- {0xc7, 0x13},
- {0xc8, 0x04},
- {0xc9, 0x08},
- {0xca, 0x08},
- {0xcb, 0x08},
- {0xcc, 0x08},
- {0xcd, 0x08},
- {0xce, 0x13},
- {0xcf, 0x13},
- {0xd0, 0x08},
- {0xd1, 0x08},
- {0xd2, 0x08},
- {0xd3, 0x08},
- {0xd4, 0x08},
- {0xd5, 0x13},
- {0xd6, 0x13},
- {0xd7, 0x13},
- {0xd8, 0x08},
- {0xd9, 0x08},
- {0xda, 0x08},
- {0xdb, 0x08},
- {0xdc, 0x13},
- {0xdd, 0x13},
- {0xde, 0x13},
- {0xdf, 0x13},
- {0xe0, 0x08},
- {0xe1, 0x08},
- {0xe2, 0x08},
- {0xe3, 0x13},
- {0xe4, 0x13},
- {0xe5, 0x13},
- {0xe6, 0x13},
- {0xe7, 0x13},
- {0xe8, 0x08},
- {0xe9, 0x08},
- {0xea, 0x13},
- {0xeb, 0x13},
- {0xec, 0x13},
- {0xed, 0x13},
- {0xee, 0x13},
- {0xef, 0x13},
- {0x7d, 0x02},
-
- /* later after isoc start */
- {0x7d, 0x08},
- {0x7d, 0x00},
-};
-
-static const struct cmd cx0342_timing_seq[] = {
- {CX0342_CHANNEL_0_1_L_irst, 0x20},
- {CX0342_CHANNEL_0_2_L_irst, 0x24},
- {CX0342_CHANNEL_0_2_H_irst, 0x00},
- {CX0342_CHANNEL_0_3_L_irst, 0x2f},
- {CX0342_CHANNEL_0_3_H_irst, 0x00},
- {CX0342_CHANNEL_1_0_L_itx, 0x02},
- {CX0342_CHANNEL_1_0_H_itx, 0x00},
- {CX0342_CHANNEL_1_1_L_itx, 0x20},
- {CX0342_CHANNEL_1_1_H_itx, 0x00},
- {CX0342_CHANNEL_1_2_L_itx, 0xe4},
- {CX0342_CHANNEL_1_2_H_itx, 0x00},
- {CX0342_CHANNEL_1_3_L_itx, 0xee},
- {CX0342_CHANNEL_1_3_H_itx, 0x00},
- {CX0342_CHANNEL_2_0_L_iwl, 0x30},
- {CX0342_CHANNEL_2_0_H_iwl, 0x00},
- {CX0342_CHANNEL_3_0_L_ensp, 0x34},
- {CX0342_CHANNEL_3_1_L_ensp, 0xe2},
- {CX0342_CHANNEL_3_1_H_ensp, 0x00},
- {CX0342_CHANNEL_3_2_L_ensp, 0xf6},
- {CX0342_CHANNEL_3_2_H_ensp, 0x00},
- {CX0342_CHANNEL_3_3_L_ensp, 0xf4},
- {CX0342_CHANNEL_3_3_H_ensp, 0x02},
- {CX0342_CHANNEL_4_0_L_sela, 0x26},
- {CX0342_CHANNEL_4_0_H_sela, 0x00},
- {CX0342_CHANNEL_4_1_L_sela, 0xe2},
- {CX0342_CHANNEL_4_1_H_sela, 0x00},
- {CX0342_CHANNEL_5_0_L_intla, 0x26},
- {CX0342_CHANNEL_5_1_L_intla, 0x29},
- {CX0342_CHANNEL_5_2_L_intla, 0xf0},
- {CX0342_CHANNEL_5_2_H_intla, 0x00},
- {CX0342_CHANNEL_5_3_L_intla, 0xf3},
- {CX0342_CHANNEL_5_3_H_intla, 0x00},
- {CX0342_CHANNEL_6_0_L_xa_sel_pos, 0x24},
- {CX0342_CHANNEL_7_1_L_cds_pos, 0x02},
- {CX0342_TIMING_EN, 0x01},
-};
-
-/* define the JPEG header */
-static void jpeg_define(u8 *jpeg_hdr,
- int height,
- int width)
-{
- memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
- jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
- jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height;
- jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
- jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width;
-}
-
-/* set the JPEG quality for sensor soi763a */
-static void jpeg_set_qual(u8 *jpeg_hdr,
- int quality)
-{
- int i, sc;
-
- if (quality < 50)
- sc = 5000 / quality;
- else
- sc = 200 - quality * 2;
- for (i = 0; i < 64; i++) {
- jpeg_hdr[JPEG_QT0_OFFSET + i] =
- (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
- jpeg_hdr[JPEG_QT1_OFFSET + i] =
- (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
- }
-}
-
-static void reg_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
-{
- struct usb_device *dev = gspca_dev->dev;
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- 0x0e,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 500);
- if (ret < 0) {
- pr_err("reg_w err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-/* the returned value is in gspca_dev->usb_buf */
-static void reg_r(struct gspca_dev *gspca_dev, u8 index)
-{
- struct usb_device *dev = gspca_dev->dev;
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- 0x0d,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, index, gspca_dev->usb_buf, 1, 500);
- if (ret < 0) {
- pr_err("reg_r err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static void reg_w_buf(struct gspca_dev *gspca_dev,
- const struct cmd *p, int l)
-{
- do {
- reg_w(gspca_dev, p->reg, p->val);
- p++;
- } while (--l > 0);
-}
-
-static int i2c_w(struct gspca_dev *gspca_dev, u8 index, u8 value)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
- reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
- reg_w(gspca_dev, TP6800_R13_SIF_TX_DATA, value);
- reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x01);
- if (sd->bridge == BRIDGE_TP6800)
- return 0;
- msleep(5);
- reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
- if (gspca_dev->usb_buf[0] == 0)
- return 0;
- reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
- return -1; /* error */
-}
-
-static void i2c_w_buf(struct gspca_dev *gspca_dev,
- const struct cmd *p, int l)
-{
- do {
- i2c_w(gspca_dev, p->reg, p->val);
- p++;
- } while (--l > 0);
-}
-
-static int i2c_r(struct gspca_dev *gspca_dev, u8 index, int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int v;
-
- reg_w(gspca_dev, TP6800_R19_SIF_ADDR_S2, index);
- reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x02);
- msleep(5);
- reg_r(gspca_dev, TP6800_R14_SIF_RX_DATA);
- v = gspca_dev->usb_buf[0];
- if (sd->bridge == BRIDGE_TP6800)
- return v;
- if (len > 1) {
- reg_r(gspca_dev, TP6800_R1B_SIF_RX_DATA2);
- v |= (gspca_dev->usb_buf[0] << 8);
- }
- reg_r(gspca_dev, TP6800_R11_SIF_CONTROL);
- if (gspca_dev->usb_buf[0] == 0)
- return v;
- reg_w(gspca_dev, TP6800_R11_SIF_CONTROL, 0x00);
- return -1;
-}
-
-static void bulk_w(struct gspca_dev *gspca_dev,
- u8 tag,
- const u8 *data,
- int length)
-{
- struct usb_device *dev = gspca_dev->dev;
- int count, actual_count, ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- for (;;) {
- count = length > BULK_OUT_SIZE - 1
- ? BULK_OUT_SIZE - 1 : length;
- gspca_dev->usb_buf[0] = tag;
- memcpy(&gspca_dev->usb_buf[1], data, count);
- ret = usb_bulk_msg(dev,
- usb_sndbulkpipe(dev, 3),
- gspca_dev->usb_buf, count + 1,
- &actual_count, 500);
- if (ret < 0) {
- pr_err("bulk write error %d tag=%02x\n",
- ret, tag);
- gspca_dev->usb_err = ret;
- return;
- }
- length -= count;
- if (length <= 0)
- break;
- data += count;
- }
-}
-
-static int probe_6810(struct gspca_dev *gspca_dev)
-{
- u8 gpio;
- int ret;
-
- reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
- gpio = gspca_dev->usb_buf[0];
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21); /* ov??? */
- reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x00);
- if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
- return SENSOR_SOI763A;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x7f); /* (unknown i2c) */
- if (i2c_w(gspca_dev, 0x00, 0x00) >= 0)
- return -2;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x11); /* tas??? / hv??? */
- ret = i2c_r(gspca_dev, 0x00, 1);
- if (ret > 0)
- return -3;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x6e); /* po??? */
- ret = i2c_r(gspca_dev, 0x00, 1);
- if (ret > 0)
- return -4;
-
- ret = i2c_r(gspca_dev, 0x01, 1);
- if (ret > 0)
- return -5;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x04); /* i2c 16 bits */
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5d); /* mi/mt??? */
- ret = i2c_r(gspca_dev, 0x00, 2);
- if (ret > 0)
- return -6;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x5c); /* mi/mt??? */
- ret = i2c_r(gspca_dev, 0x36, 2);
- if (ret > 0)
- return -7;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x61); /* (unknown i2c) */
- reg_w(gspca_dev, TP6800_R1A_SIF_TX_DATA2, 0x10);
- if (i2c_w(gspca_dev, 0xff, 0x00) >= 0)
- return -8;
-
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio);
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, gpio | 0x20);
- reg_w(gspca_dev, TP6800_R10_SIF_TYPE, 0x00); /* i2c 8 bits */
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 */
- ret = i2c_r(gspca_dev, 0x00, 1);
- if (ret > 0)
- return SENSOR_CX0342;
- return -9;
-}
-
-static void cx0342_6810_init(struct gspca_dev *gspca_dev)
-{
- static const struct cmd reg_init_1[] = {
- {TP6800_R2F_TIMING_CFG, 0x2f},
- {0x25, 0x02},
- {TP6800_R21_ENDP_1_CTL, 0x00},
- {TP6800_R3F_FRAME_RATE, 0x80},
- {TP6800_R2F_TIMING_CFG, 0x2f},
- {TP6800_R18_GPIO_DATA, 0xe1},
- {TP6800_R18_GPIO_DATA, 0xc1},
- {TP6800_R18_GPIO_DATA, 0xe1},
- {TP6800_R11_SIF_CONTROL, 0x00},
- };
- static const struct cmd reg_init_2[] = {
- {TP6800_R78_FORMAT, 0x48},
- {TP6800_R11_SIF_CONTROL, 0x00},
- };
- static const struct cmd sensor_init[] = {
- {CX0342_OUTPUT_CTRL, 0x07},
- {CX0342_BYPASS_MODE, 0x58},
- {CX0342_GPXLTHD_L, 0x28},
- {CX0342_RBPXLTHD_L, 0x28},
- {CX0342_PLANETHD_L, 0x50},
- {CX0342_PLANETHD_H, 0x03},
- {CX0342_RB_GAP_L, 0xff},
- {CX0342_RB_GAP_H, 0x07},
- {CX0342_G_GAP_L, 0xff},
- {CX0342_G_GAP_H, 0x07},
- {CX0342_RST_OVERFLOW_L, 0x5c},
- {CX0342_RST_OVERFLOW_H, 0x01},
- {CX0342_DATA_OVERFLOW_L, 0xfc},
- {CX0342_DATA_OVERFLOW_H, 0x03},
- {CX0342_DATA_UNDERFLOW_L, 0x00},
- {CX0342_DATA_UNDERFLOW_H, 0x00},
- {CX0342_SYS_CTRL_0, 0x40},
- {CX0342_GLOBAL_GAIN, 0x01},
- {CX0342_CLOCK_GEN, 0x00},
- {CX0342_SYS_CTRL_0, 0x02},
- {CX0342_IDLE_CTRL, 0x05},
- {CX0342_ADCGN, 0x00},
- {CX0342_ADC_CTL, 0x00},
- {CX0342_LVRST_BLBIAS, 0x01},
- {CX0342_VTHSEL, 0x0b},
- {CX0342_RAMP_RIV, 0x0b},
- {CX0342_LDOSEL, 0x07},
- {CX0342_SPV_VALUE_L, 0x40},
- {CX0342_SPV_VALUE_H, 0x02},
-
- {CX0342_AUTO_ADC_CALIB, 0x81},
- {CX0342_TIMING_EN, 0x01},
- };
-
- reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
- reg_w_buf(gspca_dev, tp6810_cx_init_common,
- ARRAY_SIZE(tp6810_cx_init_common));
- reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
-
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20); /* cx0342 I2C addr */
- i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
- i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
-}
-
-static void soi763a_6810_init(struct gspca_dev *gspca_dev)
-{
- static const struct cmd reg_init_1[] = {
- {TP6800_R2F_TIMING_CFG, 0x2f},
- {TP6800_R18_GPIO_DATA, 0xe1},
- {0x25, 0x02},
- {TP6800_R21_ENDP_1_CTL, 0x00},
- {TP6800_R3F_FRAME_RATE, 0x80},
- {TP6800_R2F_TIMING_CFG, 0x2f},
- {TP6800_R18_GPIO_DATA, 0xc1},
- };
- static const struct cmd reg_init_2[] = {
- {TP6800_R78_FORMAT, 0x54},
- };
- static const struct cmd sensor_init[] = {
- {0x00, 0x00},
- {0x01, 0x80},
- {0x02, 0x80},
- {0x03, 0x90},
- {0x04, 0x20},
- {0x05, 0x20},
- {0x06, 0x80},
- {0x07, 0x00},
- {0x08, 0xff},
- {0x09, 0xff},
- {0x0a, 0x76}, /* 7630 = soi673a */
- {0x0b, 0x30},
- {0x0c, 0x20},
- {0x0d, 0x20},
- {0x0e, 0xff},
- {0x0f, 0xff},
- {0x10, 0x41},
- {0x15, 0x14},
- {0x11, 0x40},
- {0x12, 0x48},
- {0x13, 0x80},
- {0x14, 0x80},
- {0x16, 0x03},
- {0x28, 0xb0},
- {0x71, 0x20},
- {0x75, 0x8e},
- {0x17, 0x1b},
- {0x18, 0xbd},
- {0x19, 0x05},
- {0x1a, 0xf6},
- {0x1b, 0x04},
- {0x1c, 0x7f}, /* omnivision */
- {0x1d, 0xa2},
- {0x1e, 0x00},
- {0x1f, 0x00},
- {0x20, 0x45},
- {0x21, 0x80},
- {0x22, 0x80},
- {0x23, 0xee},
- {0x24, 0x50},
- {0x25, 0x7a},
- {0x26, 0xa0},
- {0x27, 0x9a},
- {0x29, 0x30},
- {0x2a, 0x80},
- {0x2b, 0x00},
- {0x2c, 0xac},
- {0x2d, 0x05},
- {0x2e, 0x80},
- {0x2f, 0x3c},
- {0x30, 0x22},
- {0x31, 0x00},
- {0x32, 0x86},
- {0x33, 0x08},
- {0x34, 0xff},
- {0x35, 0xff},
- {0x36, 0xff},
- {0x37, 0xff},
- {0x38, 0xff},
- {0x39, 0xff},
- {0x3a, 0xfe},
- {0x3b, 0xfe},
- {0x3c, 0xfe},
- {0x3d, 0xfe},
- {0x3e, 0xfe},
- {0x3f, 0x71},
- {0x40, 0xff},
- {0x41, 0xff},
- {0x42, 0xff},
- {0x43, 0xff},
- {0x44, 0xff},
- {0x45, 0xff},
- {0x46, 0xff},
- {0x47, 0xff},
- {0x48, 0xff},
- {0x49, 0xff},
- {0x4a, 0xfe},
- {0x4b, 0xff},
- {0x4c, 0x00},
- {0x4d, 0x00},
- {0x4e, 0xff},
- {0x4f, 0xff},
- {0x50, 0xff},
- {0x51, 0xff},
- {0x52, 0xff},
- {0x53, 0xff},
- {0x54, 0xff},
- {0x55, 0xff},
- {0x56, 0xff},
- {0x57, 0xff},
- {0x58, 0xff},
- {0x59, 0xff},
- {0x5a, 0xff},
- {0x5b, 0xfe},
- {0x5c, 0xff},
- {0x5d, 0x8f},
- {0x5e, 0xff},
- {0x5f, 0x8f},
- {0x60, 0xa2},
- {0x61, 0x4a},
- {0x62, 0xf3},
- {0x63, 0x75},
- {0x64, 0xf0},
- {0x65, 0x00},
- {0x66, 0x55},
- {0x67, 0x92},
- {0x68, 0xa0},
- {0x69, 0x4a},
- {0x6a, 0x22},
- {0x6b, 0x00},
- {0x6c, 0x33},
- {0x6d, 0x44},
- {0x6e, 0x22},
- {0x6f, 0x84},
- {0x70, 0x0b},
- {0x72, 0x10},
- {0x73, 0x50},
- {0x74, 0x21},
- {0x76, 0x00},
- {0x77, 0xa5},
- {0x78, 0x80},
- {0x79, 0x80},
- {0x7a, 0x80},
- {0x7b, 0xe2},
- {0x7c, 0x00},
- {0x7d, 0xf7},
- {0x7e, 0x00},
- {0x7f, 0x00},
- };
-
- reg_w_buf(gspca_dev, reg_init_1, ARRAY_SIZE(reg_init_1));
- reg_w_buf(gspca_dev, tp6810_ov_init_common,
- ARRAY_SIZE(tp6810_ov_init_common));
- reg_w_buf(gspca_dev, reg_init_2, ARRAY_SIZE(reg_init_2));
-
- i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(10);
- i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
-}
-
-/* set the gain and exposure */
-static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
- s32 blue, s32 red)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor == SENSOR_CX0342) {
- expo = (expo << 2) - 1;
- i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
- gain >> 8);
- i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
- gain >> 8);
- i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
- if (sd->sensor == SENSOR_CX0342) {
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
- blue >> 8);
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
- if (sd->bridge == BRIDGE_TP6800)
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
- red >> 8);
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
- }
- i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
- sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
- return;
- }
-
- /* soi763a */
- i2c_w(gspca_dev, 0x10, /* AEC_H (exposure time) */
- expo);
-/* i2c_w(gspca_dev, 0x76, 0x02); * AEC_L ([1:0] */
- i2c_w(gspca_dev, 0x00, /* gain */
- gain);
-}
-
-/* set the JPEG quantization tables */
-static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* update the jpeg quantization tables */
- PDEBUG(D_STREAM, "q %d -> %d", sd->quality, q);
- sd->quality = q;
- if (q > 16)
- q = 16;
- if (sd->sensor == SENSOR_SOI763A)
- jpeg_set_qual(sd->jpeg_hdr, jpeg_q[q]);
- else
- memcpy(&sd->jpeg_hdr[JPEG_QT0_OFFSET - 1],
- DQT[q], sizeof DQT[0]);
-}
-
-/* set the JPEG compression quality factor */
-static void setquality(struct gspca_dev *gspca_dev, s32 q)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (q != 16)
- q = 15 - q;
-
- reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x00);
- reg_w(gspca_dev, TP6800_R79_QUALITY, 0x04);
- reg_w(gspca_dev, TP6800_R79_QUALITY, q);
-
- /* auto quality */
- if (q == 15 && sd->bridge == BRIDGE_TP6810) {
- msleep(4);
- reg_w(gspca_dev, TP6800_R7A_BLK_THRLD, 0x19);
- }
-}
-
-static const u8 color_null[18] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-static const u8 color_gain[NSENSORS][18] = {
-[SENSOR_CX0342] =
- {0x4c, 0x00, 0xa9, 0x00, 0x31, 0x00, /* Y R/G/B (LE values) */
- 0xb6, 0x03, 0x6c, 0x03, 0xe0, 0x00, /* U R/G/B */
- 0xdf, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
-[SENSOR_SOI763A] =
- {0x4c, 0x00, 0x95, 0x00, 0x1d, 0x00, /* Y R/G/B (LE values) */
- 0xb6, 0x03, 0x6c, 0x03, 0xd7, 0x00, /* U R/G/B */
- 0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03}, /* V R/G/B */
-};
-
-static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-#define NGAMMA 6
- static const u8 gamma_tb[NGAMMA][3][1024] = {
- { /* gamma 0 - from tp6800 + soi763a */
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
- 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
- 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
- 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
- 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
- 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
- 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
- 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
- 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
- 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
- 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
- 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
- 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
- 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
- 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
- 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
- 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
- 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
- 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
- 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
- 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
- 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
- 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
- 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
- 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
- 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
- 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
- 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
- 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
- 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
- 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
- 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
- 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
- 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
- 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
- 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
- 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
- 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
- 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
- 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
- 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
- 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
- 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
- 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
- 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
- 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
- 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
- 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
- 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
- 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
- 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
- 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
- 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
- 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
- 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
- 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
- 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
- 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
- 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
- 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
- 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
- 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
- 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
- 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
- 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
- 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
- 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
- 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
- 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
- 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
- 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
- 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
- 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
- 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
- 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
- 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
- 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02,
- 0x02, 0x03, 0x05, 0x07, 0x07, 0x08, 0x09, 0x09,
- 0x0a, 0x0c, 0x0c, 0x0d, 0x0e, 0x0e, 0x10, 0x11,
- 0x11, 0x12, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17,
- 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
- 0x1e, 0x1f, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x23,
- 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28,
- 0x29, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f,
- 0x2f, 0x30, 0x30, 0x31, 0x31, 0x33, 0x33, 0x34,
- 0x34, 0x34, 0x35, 0x35, 0x37, 0x37, 0x38, 0x38,
- 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c,
- 0x3c, 0x3d, 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x40,
- 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45,
- 0x45, 0x47, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
- 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c,
- 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50, 0x50,
- 0x52, 0x52, 0x52, 0x53, 0x53, 0x54, 0x54, 0x54,
- 0x55, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58, 0x58,
- 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
- 0x5b, 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5e, 0x5f,
- 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61,
- 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x65, 0x65,
- 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
- 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x6a, 0x6a,
- 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
- 0x70, 0x71, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73,
- 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76,
- 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79,
- 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
- 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x80,
- 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
- 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
- 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
- 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90,
- 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
- 0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93,
- 0x94, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96,
- 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
- 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
- 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
- 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
- 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
- },
- { /* gamma 1 - from tp6810 + soi763a */
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x03, 0x05, 0x07, 0x08, 0x09, 0x0a,
- 0x0c, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
- 0x1f, 0x20, 0x22, 0x22, 0x23, 0x25, 0x26, 0x27,
- 0x27, 0x28, 0x29, 0x2b, 0x2b, 0x2c, 0x2d, 0x2f,
- 0x2f, 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x35,
- 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c,
- 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
- 0x43, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48, 0x49,
- 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4d,
- 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
- 0x54, 0x54, 0x55, 0x56, 0x56, 0x58, 0x58, 0x59,
- 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5e,
- 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61,
- 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x65, 0x66,
- 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69,
- 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
- 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
- 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
- 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
- 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x80,
- 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84, 0x84,
- 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
- 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
- 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
- 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91,
- 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x93, 0x93,
- 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x97,
- 0x97, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
- 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e,
- 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
- 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
- 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xab,
- 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xae,
- 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
- 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4,
- 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
- 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
- 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
- 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
- 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
- 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
- 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
- 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed,
- 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
- 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
- 0x05, 0x07, 0x07, 0x08, 0x09, 0x0a, 0x0c, 0x0d,
- 0x0e, 0x10, 0x10, 0x11, 0x12, 0x14, 0x15, 0x15,
- 0x16, 0x17, 0x18, 0x1a, 0x1a, 0x1b, 0x1c, 0x1e,
- 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x25,
- 0x26, 0x27, 0x27, 0x28, 0x29, 0x29, 0x2b, 0x2c,
- 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x31,
- 0x33, 0x34, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38,
- 0x39, 0x39, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d,
- 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x43,
- 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x48,
- 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4b, 0x4b, 0x4c,
- 0x4c, 0x4d, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x50,
- 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55,
- 0x55, 0x56, 0x56, 0x56, 0x58, 0x58, 0x59, 0x59,
- 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
- 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
- 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x65,
- 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
- 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a,
- 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e,
- 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
- 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74,
- 0x75, 0x75, 0x75, 0x77, 0x77, 0x77, 0x78, 0x78,
- 0x78, 0x79, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a,
- 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d,
- 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
- 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x82,
- 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x86,
- 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89,
- 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
- 0x8b, 0x8d, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
- 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
- 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
- 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
- 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbc, 0xbc,
- 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
- 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcf,
- 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
- 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
- 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
- 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
- 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
- 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5,
- 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x05, 0x07,
- 0x08, 0x09, 0x0a, 0x0a, 0x0c, 0x0d, 0x0e, 0x0e,
- 0x10, 0x11, 0x12, 0x12, 0x14, 0x15, 0x16, 0x16,
- 0x17, 0x18, 0x18, 0x1a, 0x1b, 0x1b, 0x1c, 0x1e,
- 0x1e, 0x1f, 0x1f, 0x20, 0x22, 0x22, 0x23, 0x23,
- 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x29, 0x29,
- 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2f, 0x30,
- 0x30, 0x31, 0x31, 0x33, 0x33, 0x34, 0x34, 0x35,
- 0x35, 0x37, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a,
- 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3d, 0x3d,
- 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x42, 0x42, 0x43,
- 0x43, 0x44, 0x44, 0x45, 0x45, 0x47, 0x47, 0x47,
- 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4b,
- 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4f,
- 0x4f, 0x50, 0x50, 0x50, 0x52, 0x52, 0x52, 0x53,
- 0x53, 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56,
- 0x56, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a,
- 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c,
- 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60,
- 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63,
- 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66,
- 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x68, 0x69,
- 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
- 0x6c, 0x6d, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e,
- 0x6f, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
- 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74, 0x74,
- 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 0x77, 0x77,
- 0x77, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79,
- 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d,
- 0x7d, 0x7f, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80,
- 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82,
- 0x82, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
- 0x85, 0x86, 0x86, 0x86, 0x86, 0x88, 0x88, 0x88,
- 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a, 0x8a,
- 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e,
- 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
- 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94,
- 0x94, 0x94, 0x94, 0x96, 0x96, 0x96, 0x96, 0x96,
- 0x97, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98, 0x98,
- 0x98, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3,
- 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4,
- 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6,
- 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab,
- 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae,
- 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
- 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0,
- 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4,
- 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
- 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
- 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
- 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef,
- 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1,
- 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
- 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd,
- 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
- },
- { /* gamma 2 */
- {0x00, 0x01, 0x02, 0x05, 0x07, 0x08, 0x0a, 0x0c,
- 0x0d, 0x0e, 0x10, 0x12, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x22,
- 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
- 0x2d, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34,
- 0x35, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3b, 0x3b,
- 0x3c, 0x3d, 0x3f, 0x3f, 0x40, 0x42, 0x42, 0x43,
- 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x49, 0x49,
- 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4f, 0x4f,
- 0x50, 0x50, 0x52, 0x53, 0x53, 0x54, 0x54, 0x55,
- 0x55, 0x56, 0x56, 0x58, 0x58, 0x59, 0x5a, 0x5a,
- 0x5b, 0x5b, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
- 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63,
- 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
- 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
- 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70,
- 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x73, 0x74,
- 0x74, 0x75, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78,
- 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
- 0x7f, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82,
- 0x82, 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85,
- 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x89, 0x89,
- 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
- 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
- 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
- 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
- 0x97, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e,
- 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2, 0xa3,
- 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5,
- 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8,
- 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
- 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0,
- 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4,
- 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
- 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce,
- 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
- 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4,
- 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
- 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
- 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9,
- 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
- 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
- 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
- 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9,
- 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05,
- 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e, 0x10, 0x11,
- 0x12, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 0x1a,
- 0x1b, 0x1c, 0x1e, 0x1f, 0x20, 0x20, 0x22, 0x23,
- 0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2b,
- 0x2c, 0x2d, 0x2d, 0x2f, 0x30, 0x30, 0x31, 0x33,
- 0x33, 0x34, 0x35, 0x35, 0x37, 0x38, 0x38, 0x39,
- 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3d, 0x3f,
- 0x3f, 0x40, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
- 0x45, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49, 0x4a,
- 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d,
- 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
- 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x56, 0x58,
- 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b,
- 0x5c, 0x5c, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
- 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x63,
- 0x63, 0x63, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67,
- 0x67, 0x67, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69,
- 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d,
- 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70,
- 0x70, 0x71, 0x71, 0x71, 0x73, 0x73, 0x73, 0x73,
- 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77, 0x77,
- 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79,
- 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7b, 0x7c,
- 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f,
- 0x7f, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
- 0x82, 0x82, 0x82, 0x82, 0x84, 0x84, 0x84, 0x84,
- 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x88,
- 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x8a,
- 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8b, 0x8d,
- 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f,
- 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x90, 0x91,
- 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
- 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
- 0x94, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
- 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99,
- 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9a, 0x9b,
- 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9c,
- 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e,
- 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa1,
- 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
- 0xac, 0xac, 0xad, 0xad, 0xad, 0xad, 0xad, 0xae,
- 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2,
- 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
- 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xca,
- 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
- 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
- 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
- 0xdb, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
- 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
- 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef,
- 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
- 0xf1, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4,
- 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x00, 0x00, 0x00, 0x01, 0x02, 0x05, 0x07, 0x08,
- 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x12, 0x14,
- 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1e,
- 0x1f, 0x20, 0x20, 0x22, 0x23, 0x25, 0x26, 0x27,
- 0x28, 0x28, 0x29, 0x2b, 0x2c, 0x2d, 0x2d, 0x2f,
- 0x30, 0x31, 0x31, 0x33, 0x34, 0x35, 0x35, 0x37,
- 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c,
- 0x3d, 0x3f, 0x3f, 0x40, 0x40, 0x42, 0x43, 0x43,
- 0x44, 0x44, 0x45, 0x47, 0x47, 0x48, 0x48, 0x49,
- 0x4a, 0x4a, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d,
- 0x4f, 0x4f, 0x50, 0x50, 0x52, 0x52, 0x53, 0x53,
- 0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x58, 0x58,
- 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
- 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
- 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65,
- 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68,
- 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6c, 0x6c,
- 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f,
- 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x73, 0x73,
- 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x77,
- 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a,
- 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
- 0x7c, 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80,
- 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82,
- 0x82, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85,
- 0x86, 0x86, 0x86, 0x88, 0x88, 0x88, 0x88, 0x89,
- 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b,
- 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e,
- 0x8e, 0x8f, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x90,
- 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92,
- 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x94,
- 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97,
- 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
- 0x9a, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b,
- 0x9b, 0x9c, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d,
- 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa5, 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8,
- 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
- 0xab, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac, 0xac,
- 0xad, 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
- 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0,
- 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb3, 0xb4, 0xb3, 0xb4, 0xb4, 0xb4,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe,
- 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
- 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xda,
- 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
- 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
- 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec,
- 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed,
- 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1,
- 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
- },
- { /* gamma 3 - from tp6810 + cx0342 */
- {0x08, 0x09, 0x0c, 0x0d, 0x10, 0x11, 0x14, 0x15,
- 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x1f, 0x20, 0x23,
- 0x25, 0x26, 0x27, 0x28, 0x2b, 0x2c, 0x2d, 0x2f,
- 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38, 0x39,
- 0x3a, 0x3b, 0x3c, 0x3d, 0x3f, 0x40, 0x42, 0x43,
- 0x44, 0x45, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
- 0x4c, 0x4d, 0x4d, 0x4f, 0x50, 0x52, 0x53, 0x53,
- 0x54, 0x55, 0x56, 0x56, 0x58, 0x59, 0x5a, 0x5a,
- 0x5b, 0x5c, 0x5c, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
- 0x61, 0x62, 0x63, 0x63, 0x65, 0x66, 0x66, 0x67,
- 0x68, 0x68, 0x69, 0x69, 0x6a, 0x6c, 0x6c, 0x6d,
- 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x73,
- 0x73, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77, 0x78,
- 0x78, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c,
- 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81,
- 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
- 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f,
- 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93,
- 0x93, 0x93, 0x94, 0x94, 0x96, 0x96, 0x97, 0x97,
- 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
- 0x9e, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
- 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
- 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
- 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
- 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
- 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
- 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf,
- 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
- 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
- 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
- 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
- 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce,
- 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
- 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda,
- 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd,
- 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8,
- 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
- 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
- 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0d, 0x10,
- 0x11, 0x12, 0x14, 0x15, 0x17, 0x18, 0x1a, 0x1b,
- 0x1c, 0x1e, 0x1f, 0x20, 0x22, 0x23, 0x25, 0x26,
- 0x27, 0x28, 0x29, 0x2b, 0x2c, 0x2c, 0x2d, 0x2f,
- 0x30, 0x31, 0x33, 0x33, 0x34, 0x35, 0x37, 0x38,
- 0x38, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f,
- 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
- 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b,
- 0x4c, 0x4d, 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52,
- 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, 0x58,
- 0x58, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c,
- 0x5c, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
- 0x61, 0x62, 0x62, 0x63, 0x63, 0x65, 0x65, 0x66,
- 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69,
- 0x6a, 0x6a, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71,
- 0x71, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75,
- 0x77, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x79,
- 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c,
- 0x7d, 0x7d, 0x7d, 0x7f, 0x7f, 0x7f, 0x80, 0x80,
- 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x84,
- 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86,
- 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a,
- 0x8a, 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e,
- 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90,
- 0x90, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92,
- 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96,
- 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98,
- 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a,
- 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e,
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1,
- 0xa2, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3,
- 0xa4, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8,
- 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab,
- 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad,
- 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1,
- 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2,
- 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4,
- 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8, 0xb9,
- 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba,
- 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
- 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4,
- 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca,
- 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
- 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0,
- 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3, 0xd4,
- 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
- 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
- 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
- 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec,
- 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee,
- 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
- 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
- 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x07, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14,
- 0x16, 0x17, 0x18, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
- 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2d,
- 0x2f, 0x30, 0x31, 0x33, 0x34, 0x35, 0x37, 0x38,
- 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3f, 0x40,
- 0x42, 0x43, 0x44, 0x44, 0x45, 0x47, 0x48, 0x49,
- 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4f, 0x50,
- 0x52, 0x52, 0x53, 0x54, 0x55, 0x55, 0x56, 0x58,
- 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e,
- 0x5f, 0x5f, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63,
- 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
- 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e,
- 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73, 0x73, 0x74,
- 0x74, 0x75, 0x75, 0x77, 0x77, 0x78, 0x78, 0x79,
- 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d,
- 0x7d, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81,
- 0x82, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
- 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8b, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f,
- 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
- 0x92, 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96,
- 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
- 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
- 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
- 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
- 0xa3, 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9,
- 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac, 0xac, 0xac,
- 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
- 0xb4, 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
- 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
- 0xb9, 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc,
- 0xbd, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
- 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2,
- 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc3, 0xc4,
- 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb,
- 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf,
- 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1,
- 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
- 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xde,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5,
- 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb, 0xeb, 0xeb,
- 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
- 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
- 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd,
- 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
- },
- { /* gamma 4 - from tp6800 + soi763a */
- {0x11, 0x14, 0x15, 0x17, 0x1a, 0x1b, 0x1e, 0x1f,
- 0x22, 0x23, 0x25, 0x27, 0x28, 0x2b, 0x2c, 0x2d,
- 0x2f, 0x31, 0x33, 0x34, 0x35, 0x38, 0x39, 0x3a,
- 0x3b, 0x3c, 0x3d, 0x40, 0x42, 0x43, 0x44, 0x45,
- 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4f,
- 0x50, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56, 0x58,
- 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5f, 0x60,
- 0x61, 0x61, 0x62, 0x63, 0x65, 0x65, 0x66, 0x67,
- 0x68, 0x68, 0x69, 0x6a, 0x6c, 0x6c, 0x6d, 0x6e,
- 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x73, 0x74, 0x74,
- 0x75, 0x77, 0x77, 0x78, 0x79, 0x79, 0x7a, 0x7a,
- 0x7b, 0x7c, 0x7c, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
- 0x81, 0x81, 0x82, 0x84, 0x84, 0x85, 0x85, 0x86,
- 0x86, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8b, 0x8b,
- 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x90, 0x90, 0x91,
- 0x91, 0x92, 0x92, 0x93, 0x93, 0x94, 0x94, 0x96,
- 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
- 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa8, 0xa8, 0xa9, 0xa9, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
- 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1,
- 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4,
- 0xb4, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8,
- 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbc,
- 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf,
- 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2,
- 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc9,
- 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0,
- 0xd0, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
- 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb,
- 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf,
- 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
- 0xe9, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
- 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
- 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
- 0xf9, 0xf9, 0xfa, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x11, 0x14, 0x15,
- 0x16, 0x17, 0x1a, 0x1b, 0x1c, 0x1e, 0x1f, 0x20,
- 0x23, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2b, 0x2c,
- 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34, 0x34, 0x35,
- 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d,
- 0x3f, 0x40, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45,
- 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b, 0x4b, 0x4c,
- 0x4d, 0x4f, 0x4f, 0x50, 0x52, 0x52, 0x53, 0x54,
- 0x54, 0x55, 0x55, 0x56, 0x58, 0x58, 0x59, 0x5a,
- 0x5a, 0x5b, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f, 0x5f,
- 0x60, 0x60, 0x61, 0x61, 0x62, 0x63, 0x63, 0x65,
- 0x65, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69,
- 0x69, 0x6a, 0x6a, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e,
- 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, 0x73,
- 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x77, 0x77,
- 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f,
- 0x7f, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82,
- 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86,
- 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a,
- 0x8b, 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e,
- 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91,
- 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94,
- 0x94, 0x94, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
- 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c,
- 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0xa0,
- 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
- 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa4,
- 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa6,
- 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xa9, 0xab,
- 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad,
- 0xad, 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf,
- 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
- 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3,
- 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb6, 0xb6,
- 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8,
- 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc9, 0xc9, 0xc9, 0xc9,
- 0xca, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
- 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
- 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
- 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda,
- 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
- 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf,
- 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2,
- 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
- 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
- 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
- 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
- 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
- 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb},
- {0x0d, 0x10, 0x11, 0x14, 0x15, 0x17, 0x18, 0x1b,
- 0x1c, 0x1e, 0x20, 0x22, 0x23, 0x26, 0x27, 0x28,
- 0x29, 0x2b, 0x2d, 0x2f, 0x30, 0x31, 0x33, 0x34,
- 0x35, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
- 0x3f, 0x40, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48,
- 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, 0x4f, 0x50,
- 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x56, 0x58,
- 0x59, 0x5a, 0x5a, 0x5b, 0x5c, 0x5e, 0x5e, 0x5f,
- 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x65, 0x65,
- 0x66, 0x67, 0x67, 0x68, 0x69, 0x69, 0x6a, 0x6c,
- 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
- 0x71, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x77,
- 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7c, 0x7c, 0x7d, 0x7d, 0x7f, 0x7f, 0x80, 0x80,
- 0x81, 0x81, 0x82, 0x82, 0x84, 0x84, 0x85, 0x85,
- 0x86, 0x86, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a,
- 0x8b, 0x8b, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f,
- 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92,
- 0x93, 0x93, 0x94, 0x94, 0x94, 0x96, 0x96, 0x97,
- 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d,
- 0x9d, 0x9e, 0x9e, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1,
- 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa8, 0xa8,
- 0xa8, 0xa9, 0xa9, 0xa9, 0xab, 0xab, 0xab, 0xac,
- 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1,
- 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4,
- 0xb4, 0xb4, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7,
- 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba,
- 0xba, 0xba, 0xba, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
- 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
- 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0xc2, 0xc2, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7,
- 0xc7, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
- 0xca, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc,
- 0xcd, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce,
- 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd0,
- 0xd1, 0xd1, 0xd1, 0xd1, 0xd3, 0xd3, 0xd3, 0xd3,
- 0xd4, 0xd4, 0xd4, 0xd4, 0xd6, 0xd6, 0xd6, 0xd6,
- 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8,
- 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda,
- 0xdb, 0xdb, 0xdb, 0xdb, 0xdd, 0xdd, 0xdd, 0xdd,
- 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
- 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1,
- 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6,
- 0xe7, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8,
- 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xeb, 0xeb,
- 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed,
- 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee,
- 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0,
- 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf3,
- 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6,
- 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8,
- 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9,
- 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb}
- },
- { /* gamma 5 */
- {0x16, 0x18, 0x19, 0x1b, 0x1d, 0x1e, 0x20, 0x21,
- 0x23, 0x24, 0x25, 0x27, 0x28, 0x2a, 0x2b, 0x2c,
- 0x2d, 0x2f, 0x30, 0x31, 0x32, 0x34, 0x35, 0x36,
- 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
- 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x56, 0x56, 0x57, 0x58, 0x59, 0x59, 0x5a, 0x5b,
- 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61,
- 0x62, 0x62, 0x63, 0x64, 0x64, 0x65, 0x66, 0x66,
- 0x67, 0x68, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6b,
- 0x6c, 0x6d, 0x6d, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
- 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75,
- 0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x79,
- 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e,
- 0x7e, 0x7f, 0x7f, 0x80, 0x80, 0x81, 0x81, 0x82,
- 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
- 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89, 0x89,
- 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8d,
- 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
- 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x94,
- 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97,
- 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d,
- 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0,
- 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3,
- 0xa3, 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6,
- 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8,
- 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
- 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae,
- 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3,
- 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5,
- 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
- 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc,
- 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1,
- 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9,
- 0xc9, 0xca, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
- 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd,
- 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
- 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
- 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5,
- 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
- 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9,
- 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde,
- 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0,
- 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4,
- 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5,
- 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
- 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec,
- 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee,
- 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1,
- 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
- 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5,
- 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
- 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x0f, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19,
- 0x1a, 0x1b, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22,
- 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
- 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32,
- 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x38, 0x39,
- 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f,
- 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x44, 0x45,
- 0x46, 0x47, 0x47, 0x48, 0x49, 0x49, 0x4a, 0x4b,
- 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4e, 0x4f, 0x50,
- 0x50, 0x51, 0x51, 0x52, 0x53, 0x53, 0x54, 0x54,
- 0x55, 0x55, 0x56, 0x56, 0x57, 0x58, 0x58, 0x59,
- 0x59, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d,
- 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x61,
- 0x61, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x65,
- 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x68, 0x68,
- 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c,
- 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f,
- 0x6f, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72,
- 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75,
- 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78,
- 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b,
- 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e,
- 0x7e, 0x7f, 0x7f, 0x7f, 0x80, 0x80, 0x80, 0x81,
- 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
- 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86,
- 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88,
- 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b,
- 0x8b, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d,
- 0x8d, 0x8e, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
- 0x90, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x91,
- 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x94,
- 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96,
- 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x98, 0x98,
- 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c,
- 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e,
- 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, 0x9f, 0xa0, 0xa0,
- 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa3, 0xa4,
- 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5,
- 0xa6, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa7,
- 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9,
- 0xa9, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xac, 0xad,
- 0xad, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xae,
- 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
- 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb2,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5,
- 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb7,
- 0xb7, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb8,
- 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xba, 0xba,
- 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd,
- 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe,
- 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0,
- 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1,
- 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4,
- 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7,
- 0xc7, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xca,
- 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc,
- 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd,
- 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce,
- 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0,
- 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd5,
- 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7,
- 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8,
- 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xd9, 0xda,
- 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde,
- 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf,
- 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0,
- 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2,
- 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3,
- 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
- 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea,
- 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb,
- 0xec, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed,
- 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee,
- 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0,
- 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1,
- 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
- 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
- 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5,
- 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7,
- 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd,
- 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
- 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- {0x13, 0x15, 0x16, 0x18, 0x19, 0x1b, 0x1c, 0x1e,
- 0x1f, 0x20, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31,
- 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41,
- 0x42, 0x43, 0x44, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x49, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, 0x4e,
- 0x4f, 0x50, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54,
- 0x55, 0x55, 0x56, 0x57, 0x57, 0x58, 0x59, 0x59,
- 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5d, 0x5e, 0x5f,
- 0x5f, 0x60, 0x60, 0x61, 0x62, 0x62, 0x63, 0x63,
- 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
- 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c,
- 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70,
- 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74,
- 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78,
- 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c,
- 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f,
- 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83,
- 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86,
- 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89,
- 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, 0x8c, 0x8c,
- 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f,
- 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x92, 0x92,
- 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 0x95,
- 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
- 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a,
- 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d,
- 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f,
- 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2,
- 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4,
- 0xa4, 0xa5, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6,
- 0xa7, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9,
- 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab,
- 0xab, 0xab, 0xac, 0xac, 0xac, 0xac, 0xad, 0xad,
- 0xad, 0xae, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
- 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1,
- 0xb2, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb3,
- 0xb4, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5,
- 0xb6, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb7,
- 0xb8, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9,
- 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb,
- 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd,
- 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf,
- 0xbf, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1,
- 0xc1, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3,
- 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5,
- 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc6, 0xc7,
- 0xc7, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc8,
- 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
- 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc,
- 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xce,
- 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf,
- 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1,
- 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3,
- 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
- 0xd5, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6,
- 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8,
- 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9,
- 0xd9, 0xda, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb,
- 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdc,
- 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde,
- 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xe0,
- 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1,
- 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3,
- 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4,
- 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6,
- 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe7,
- 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9,
- 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea,
- 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec,
- 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xed,
- 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
- 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
- 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2,
- 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3,
- 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5,
- 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6,
- 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8,
- 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
- 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
- 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
- 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe,
- 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
- },
- };
-
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
- if (sd->bridge == BRIDGE_TP6810)
- reg_w(gspca_dev, 0x02, 0x28);
-/* msleep(50); */
- bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
- bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
- bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
- if (sd->bridge == BRIDGE_TP6810) {
- int i;
-
- reg_w(gspca_dev, 0x02, 0x2b);
- reg_w(gspca_dev, 0x02, 0x28);
- for (i = 0; i < 6; i++)
- reg_w(gspca_dev, TP6800_R55_GAMMA_R,
- gamma_tb[gamma][0][i]);
- reg_w(gspca_dev, 0x02, 0x2b);
- reg_w(gspca_dev, 0x02, 0x28);
- for (i = 0; i < 6; i++)
- reg_w(gspca_dev, TP6800_R56_GAMMA_G,
- gamma_tb[gamma][1][i]);
- reg_w(gspca_dev, 0x02, 0x2b);
- reg_w(gspca_dev, 0x02, 0x28);
- for (i = 0; i < 6; i++)
- reg_w(gspca_dev, TP6800_R57_GAMMA_B,
- gamma_tb[gamma][2][i]);
- reg_w(gspca_dev, 0x02, 0x28);
- }
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
-/* msleep(50); */
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->bridge == BRIDGE_TP6800) {
- val |= 0x08; /* grid compensation enable */
- if (gspca_dev->width == 640)
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
- else
- val |= 0x04; /* scaling down enable */
- reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
- } else {
- val = (val << 5) | 0x08;
- reg_w(gspca_dev, 0x59, val);
- }
-}
-
-static void setautogain(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ag_cnt = val ? AG_CNT_START : -1;
-}
-
-/* set the resolution for sensor cx0342 */
-static void set_resolution(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
- if (gspca_dev->width == 320) {
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
- msleep(100);
- i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
- msleep(100);
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
- reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x0d);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0x37);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x01);
- } else {
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x05);
- msleep(100);
- i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
- msleep(100);
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x03);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
- reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, 0x09);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_L, 0xcf);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
- }
- i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
- bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
- ARRAY_SIZE(color_gain[0]));
- setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
- if (sd->sensor == SENSOR_SOI763A)
- setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
-}
-
-/* convert the frame rate to a tp68x0 value */
-static int get_fr_idx(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
-
- if (sd->bridge == BRIDGE_TP6800) {
- for (i = 0; i < ARRAY_SIZE(rates) - 1; i++) {
- if (sd->framerate >= rates[i])
- break;
- }
- i = 6 - i; /* 1 = 5fps .. 6 = 30fps */
-
- /* 640x480 * 30 fps does not work */
- if (i == 6 /* if 30 fps */
- && gspca_dev->width == 640)
- i = 0x05; /* 15 fps */
- } else {
- for (i = 0; i < ARRAY_SIZE(rates_6810) - 1; i++) {
- if (sd->framerate >= rates_6810[i])
- break;
- }
- i = 7 - i; /* 3 = 5fps .. 7 = 30fps */
-
- /* 640x480 * 30 fps does not work */
- if (i == 7 /* if 30 fps */
- && gspca_dev->width == 640)
- i = 6; /* 15 fps */
- i |= 0x80; /* clock * 1 */
- }
- return i;
-}
-
-static void setframerate(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 fr_idx;
-
- fr_idx = get_fr_idx(gspca_dev);
-
- if (sd->bridge == BRIDGE_TP6810) {
- reg_r(gspca_dev, 0x7b);
- reg_w(gspca_dev, 0x7b,
- sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
- if (val >= 128)
- fr_idx = 0xf0; /* lower frame rate */
- }
-
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, fr_idx);
-
- if (sd->sensor == SENSOR_CX0342)
- i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
-}
-
-static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
-{
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
- i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
- i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- s32 val = gspca_dev->gain->val;
-
- if (sd->sensor == SENSOR_CX0342) {
- s32 old = gspca_dev->gain->cur.val ?
- gspca_dev->gain->cur.val : 1;
-
- sd->blue->val = sd->blue->val * val / old;
- if (sd->blue->val > 4095)
- sd->blue->val = 4095;
- sd->red->val = sd->red->val * val / old;
- if (sd->red->val > 4095)
- sd->red->val = 4095;
- }
- if (gspca_dev->streaming) {
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev, gspca_dev->exposure->val,
- gspca_dev->gain->val,
- sd->blue->val, sd->red->val);
- else
- setexposure(gspca_dev, gspca_dev->exposure->val,
- gspca_dev->gain->val, 0, 0);
- }
- return gspca_dev->usb_err;
-}
-
-static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
-{
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
- i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
- i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->bridge = id->driver_info;
-
- gspca_dev->cam.cam_mode = vga_mode;
- gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
- gspca_dev->cam.mode_framerates = sd->bridge == BRIDGE_TP6800 ?
- framerates : framerates_6810;
-
- sd->framerate = 30; /* default: 30 fps */
- 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;
- static const struct cmd tp6800_preinit[] = {
- {TP6800_R10_SIF_TYPE, 0x01}, /* sif */
- {TP6800_R11_SIF_CONTROL, 0x01},
- {TP6800_R15_GPIO_PU, 0x9f},
- {TP6800_R16_GPIO_PD, 0x9f},
- {TP6800_R17_GPIO_IO, 0x80},
- {TP6800_R18_GPIO_DATA, 0x40}, /* LED off */
- };
- static const struct cmd tp6810_preinit[] = {
- {TP6800_R2F_TIMING_CFG, 0x2f},
- {TP6800_R15_GPIO_PU, 0x6f},
- {TP6800_R16_GPIO_PD, 0x40},
- {TP6800_R17_GPIO_IO, 0x9f},
- {TP6800_R18_GPIO_DATA, 0xc1}, /* LED off */
- };
-
- if (sd->bridge == BRIDGE_TP6800)
- reg_w_buf(gspca_dev, tp6800_preinit,
- ARRAY_SIZE(tp6800_preinit));
- else
- reg_w_buf(gspca_dev, tp6810_preinit,
- ARRAY_SIZE(tp6810_preinit));
- msleep(15);
- reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
- PDEBUG(D_PROBE, "gpio: %02x", gspca_dev->usb_buf[0]);
-/* values:
- * 0x80: snapshot button
- * 0x40: LED
- * 0x20: (bridge / sensor) reset for tp6810 ?
- * 0x07: sensor type ?
- */
-
- /* guess the sensor type */
- if (force_sensor >= 0) {
- sd->sensor = force_sensor;
- } else {
- if (sd->bridge == BRIDGE_TP6800) {
-/*fixme: not sure this is working*/
- switch (gspca_dev->usb_buf[0] & 0x07) {
- case 0:
- sd->sensor = SENSOR_SOI763A;
- break;
- case 1:
- sd->sensor = SENSOR_CX0342;
- break;
- }
- } else {
- int sensor;
-
- sensor = probe_6810(gspca_dev);
- if (sensor < 0) {
- pr_warn("Unknown sensor %d - forced to soi763a\n",
- -sensor);
- sensor = SENSOR_SOI763A;
- }
- sd->sensor = sensor;
- }
- }
- if (sd->sensor == SENSOR_SOI763A) {
- pr_info("Sensor soi763a\n");
- if (sd->bridge == BRIDGE_TP6810) {
- soi763a_6810_init(gspca_dev);
- }
- } else {
- pr_info("Sensor cx0342\n");
- if (sd->bridge == BRIDGE_TP6810) {
- cx0342_6810_init(gspca_dev);
- }
- }
-
- set_dqt(gspca_dev, 0);
- return 0;
-}
-
-/* This function is called before choosing the alt setting */
-static int sd_isoc_init(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct cmd cx_sensor_init[] = {
- {CX0342_AUTO_ADC_CALIB, 0x81},
- {CX0342_EXPO_LINE_L, 0x37},
- {CX0342_EXPO_LINE_H, 0x01},
- {CX0342_RAW_GRGAIN_L, 0x00},
- {CX0342_RAW_GBGAIN_L, 0x00},
- {CX0342_RAW_RGAIN_L, 0x00},
- {CX0342_RAW_BGAIN_L, 0x00},
- {CX0342_SYS_CTRL_0, 0x81},
- };
- static const struct cmd cx_bridge_init[] = {
- {0x4d, 0x00},
- {0x4c, 0xff},
- {0x4e, 0xff},
- {0x4f, 0x00},
- };
- static const struct cmd ov_sensor_init[] = {
- {0x10, 0x75}, /* exposure */
- {0x76, 0x03},
- {0x00, 0x00}, /* gain */
- };
- static const struct cmd ov_bridge_init[] = {
- {0x7b, 0x90},
- {TP6800_R3F_FRAME_RATE, 0x87},
- };
-
- if (sd->bridge == BRIDGE_TP6800)
- return 0;
- if (sd->sensor == SENSOR_CX0342) {
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x20);
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
- i2c_w_buf(gspca_dev, cx_sensor_init,
- ARRAY_SIZE(cx_sensor_init));
- reg_w_buf(gspca_dev, cx_bridge_init,
- ARRAY_SIZE(cx_bridge_init));
- bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
- reg_w(gspca_dev, 0x59, 0x40);
- } else {
- reg_w(gspca_dev, TP6800_R12_SIF_ADDR_S, 0x21);
- i2c_w_buf(gspca_dev, ov_sensor_init,
- ARRAY_SIZE(ov_sensor_init));
- reg_r(gspca_dev, 0x7b);
- reg_w_buf(gspca_dev, ov_bridge_init,
- ARRAY_SIZE(ov_bridge_init));
- }
- reg_w(gspca_dev, TP6800_R78_FORMAT,
- gspca_dev->curr_mode ? 0x00 : 0x01);
- return gspca_dev->usb_err;
-}
-
-static void set_led(struct gspca_dev *gspca_dev, int on)
-{
- u8 data;
-
- reg_r(gspca_dev, TP6800_R18_GPIO_DATA);
- data = gspca_dev->usb_buf[0];
- if (on)
- data &= ~0x40;
- else
- data |= 0x40;
- reg_w(gspca_dev, TP6800_R18_GPIO_DATA, data);
-}
-
-static void cx0342_6800_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct cmd reg_init[] = {
- /* fixme: is this useful? */
- {TP6800_R17_GPIO_IO, 0x9f},
- {TP6800_R16_GPIO_PD, 0x40},
- {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
- {TP6800_R50, 0x00},
- {TP6800_R51, 0x00},
- {TP6800_R52, 0xff},
- {TP6800_R53, 0x03},
- {TP6800_R54_DARK_CFG, 0x07},
- {TP6800_R5C_EDGE_THRLD, 0x40},
- {TP6800_R7A_BLK_THRLD, 0x40},
- {TP6800_R2F_TIMING_CFG, 0x17},
- {TP6800_R30_SENSOR_CFG, 0x18}, /* G1B..RG0 */
- {TP6800_R37_FRONT_DARK_ST, 0x00},
- {TP6800_R38_FRONT_DARK_END, 0x00},
- {TP6800_R39_REAR_DARK_ST_L, 0x00},
- {TP6800_R3A_REAR_DARK_ST_H, 0x00},
- {TP6800_R3B_REAR_DARK_END_L, 0x00},
- {TP6800_R3C_REAR_DARK_END_H, 0x00},
- {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
- {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
- {TP6800_R21_ENDP_1_CTL, 0x03},
-
- {TP6800_R31_PIXEL_START, 0x0b},
- {TP6800_R32_PIXEL_END_L, 0x8a},
- {TP6800_R33_PIXEL_END_H, 0x02},
- {TP6800_R34_LINE_START, 0x0e},
- {TP6800_R35_LINE_END_L, 0xf4},
- {TP6800_R36_LINE_END_H, 0x01},
- {TP6800_R78_FORMAT, 0x00},
- {TP6800_R12_SIF_ADDR_S, 0x20}, /* cx0342 i2c addr */
- };
- static const struct cmd sensor_init[] = {
- {CX0342_OUTPUT_CTRL, 0x07},
- {CX0342_BYPASS_MODE, 0x58},
- {CX0342_GPXLTHD_L, 0x16},
- {CX0342_RBPXLTHD_L, 0x16},
- {CX0342_PLANETHD_L, 0xc0},
- {CX0342_PLANETHD_H, 0x03},
- {CX0342_RB_GAP_L, 0xff},
- {CX0342_RB_GAP_H, 0x07},
- {CX0342_G_GAP_L, 0xff},
- {CX0342_G_GAP_H, 0x07},
- {CX0342_RST_OVERFLOW_L, 0x5c},
- {CX0342_RST_OVERFLOW_H, 0x01},
- {CX0342_DATA_OVERFLOW_L, 0xfc},
- {CX0342_DATA_OVERFLOW_H, 0x03},
- {CX0342_DATA_UNDERFLOW_L, 0x00},
- {CX0342_DATA_UNDERFLOW_H, 0x00},
- {CX0342_SYS_CTRL_0, 0x40},
- {CX0342_GLOBAL_GAIN, 0x01},
- {CX0342_CLOCK_GEN, 0x00},
- {CX0342_SYS_CTRL_0, 0x02},
- {CX0342_IDLE_CTRL, 0x05},
- {CX0342_ADCGN, 0x00},
- {CX0342_ADC_CTL, 0x00},
- {CX0342_LVRST_BLBIAS, 0x01},
- {CX0342_VTHSEL, 0x0b},
- {CX0342_RAMP_RIV, 0x0b},
- {CX0342_LDOSEL, 0x07},
- {CX0342_SPV_VALUE_L, 0x40},
- {CX0342_SPV_VALUE_H, 0x02},
- };
-
- reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
- i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
- i2c_w_buf(gspca_dev, cx0342_timing_seq, ARRAY_SIZE(cx0342_timing_seq));
- reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
- reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
- i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
- i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain),
- v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- else
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
- set_led(gspca_dev, 1);
- set_resolution(gspca_dev);
-}
-
-static void cx0342_6810_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct cmd sensor_init_2[] = {
- {CX0342_EXPO_LINE_L, 0x6f},
- {CX0342_EXPO_LINE_H, 0x02},
- {CX0342_RAW_GRGAIN_L, 0x00},
- {CX0342_RAW_GBGAIN_L, 0x00},
- {CX0342_RAW_RGAIN_L, 0x00},
- {CX0342_RAW_BGAIN_L, 0x00},
- {CX0342_SYS_CTRL_0, 0x81},
- };
- static const struct cmd bridge_init_2[] = {
- {0x4d, 0x00},
- {0x4c, 0xff},
- {0x4e, 0xff},
- {0x4f, 0x00},
- {TP6800_R7A_BLK_THRLD, 0x00},
- {TP6800_R79_QUALITY, 0x04},
- {TP6800_R79_QUALITY, 0x01},
- };
- static const struct cmd bridge_init_3[] = {
- {TP6800_R31_PIXEL_START, 0x08},
- {TP6800_R32_PIXEL_END_L, 0x87},
- {TP6800_R33_PIXEL_END_H, 0x02},
- {TP6800_R34_LINE_START, 0x0e},
- {TP6800_R35_LINE_END_L, 0xf4},
- {TP6800_R36_LINE_END_H, 0x01},
- };
- static const struct cmd sensor_init_3[] = {
- {CX0342_AUTO_ADC_CALIB, 0x81},
- {CX0342_EXPO_LINE_L, 0x6f},
- {CX0342_EXPO_LINE_H, 0x02},
- {CX0342_RAW_GRGAIN_L, 0x00},
- {CX0342_RAW_GBGAIN_L, 0x00},
- {CX0342_RAW_RGAIN_L, 0x00},
- {CX0342_RAW_BGAIN_L, 0x00},
- {CX0342_SYS_CTRL_0, 0x81},
- };
- static const struct cmd bridge_init_5[] = {
- {0x4d, 0x00},
- {0x4c, 0xff},
- {0x4e, 0xff},
- {0x4f, 0x00},
- };
- static const struct cmd sensor_init_4[] = {
- {CX0342_EXPO_LINE_L, 0xd3},
- {CX0342_EXPO_LINE_H, 0x01},
-/*fixme: gains, but 00..80 only*/
- {CX0342_RAW_GRGAIN_L, 0x40},
- {CX0342_RAW_GBGAIN_L, 0x40},
- {CX0342_RAW_RGAIN_L, 0x40},
- {CX0342_RAW_BGAIN_L, 0x40},
- {CX0342_SYS_CTRL_0, 0x81},
- };
- static const struct cmd sensor_init_5[] = {
- {CX0342_IDLE_CTRL, 0x05},
- {CX0342_ADCGN, 0x00},
- {CX0342_ADC_CTL, 0x00},
- {CX0342_LVRST_BLBIAS, 0x01},
- {CX0342_VTHSEL, 0x0b},
- {CX0342_RAMP_RIV, 0x0b},
- {CX0342_LDOSEL, 0x07},
- {CX0342_SPV_VALUE_L, 0x40},
- {CX0342_SPV_VALUE_H, 0x02},
- {CX0342_AUTO_ADC_CALIB, 0x81},
- };
-
- reg_w(gspca_dev, 0x22, gspca_dev->alt);
- i2c_w_buf(gspca_dev, sensor_init_2, ARRAY_SIZE(sensor_init_2));
- reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
- reg_w_buf(gspca_dev, tp6810_cx_init_common,
- ARRAY_SIZE(tp6810_cx_init_common));
- reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
- if (gspca_dev->curr_mode) {
- reg_w(gspca_dev, 0x4a, 0x7f);
- reg_w(gspca_dev, 0x07, 0x05);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
- } else {
- reg_w(gspca_dev, 0x4a, 0xff);
- reg_w(gspca_dev, 0x07, 0x85);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
- }
- setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
- reg_w_buf(gspca_dev, tp6810_bridge_start,
- ARRAY_SIZE(tp6810_bridge_start));
- setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
- bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
- ARRAY_SIZE(color_gain[0]));
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
- i2c_w_buf(gspca_dev, sensor_init_3, ARRAY_SIZE(sensor_init_3));
- reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
- i2c_w_buf(gspca_dev, sensor_init_4, ARRAY_SIZE(sensor_init_4));
- reg_w_buf(gspca_dev, bridge_init_5, ARRAY_SIZE(bridge_init_5));
- i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
-
- set_led(gspca_dev, 1);
-/* setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
-}
-
-static void soi763a_6800_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct cmd reg_init[] = {
- {TP6800_R79_QUALITY, 0x04},
- {TP6800_R79_QUALITY, 0x01},
- {TP6800_R10_SIF_TYPE, 0x00}, /* i2c 8 bits */
-
- {TP6800_R50, 0x00},
- {TP6800_R51, 0x00},
- {TP6800_R52, 0xff},
- {TP6800_R53, 0x03},
- {TP6800_R54_DARK_CFG, 0x07},
- {TP6800_R5C_EDGE_THRLD, 0x40},
-
- {TP6800_R79_QUALITY, 0x03},
- {TP6800_R7A_BLK_THRLD, 0x40},
-
- {TP6800_R2F_TIMING_CFG, 0x46},
- {TP6800_R30_SENSOR_CFG, 0x10}, /* BG1..G0R */
- {TP6800_R37_FRONT_DARK_ST, 0x00},
- {TP6800_R38_FRONT_DARK_END, 0x00},
- {TP6800_R39_REAR_DARK_ST_L, 0x00},
- {TP6800_R3A_REAR_DARK_ST_H, 0x00},
- {TP6800_R3B_REAR_DARK_END_L, 0x00},
- {TP6800_R3C_REAR_DARK_END_H, 0x00},
- {TP6800_R3D_HORIZ_DARK_LINE_L, 0x00},
- {TP6800_R3E_HORIZ_DARK_LINE_H, 0x00},
- {TP6800_R21_ENDP_1_CTL, 0x03},
-
- {TP6800_R3F_FRAME_RATE, 0x04}, /* 15 fps */
- {TP6800_R5D_DEMOSAIC_CFG, 0x0e}, /* scale down - medium edge */
-
- {TP6800_R31_PIXEL_START, 0x1b},
- {TP6800_R32_PIXEL_END_L, 0x9a},
- {TP6800_R33_PIXEL_END_H, 0x02},
- {TP6800_R34_LINE_START, 0x0f},
- {TP6800_R35_LINE_END_L, 0xf4},
- {TP6800_R36_LINE_END_H, 0x01},
- {TP6800_R78_FORMAT, 0x01}, /* qvga */
- {TP6800_R12_SIF_ADDR_S, 0x21}, /* soi763a i2c addr */
- {TP6800_R1A_SIF_TX_DATA2, 0x00},
- };
- static const struct cmd sensor_init[] = {
- {0x12, 0x48}, /* mirror - RGB */
- {0x13, 0xa0}, /* clock - no AGC nor AEC */
- {0x03, 0xa4}, /* saturation */
- {0x04, 0x30}, /* hue */
- {0x05, 0x88}, /* contrast */
- {0x06, 0x60}, /* brightness */
- {0x10, 0x41}, /* AEC */
- {0x11, 0x40}, /* clock rate */
- {0x13, 0xa0},
- {0x14, 0x00}, /* 640x480 */
- {0x15, 0x14},
- {0x1f, 0x41},
- {0x20, 0x80},
- {0x23, 0xee},
- {0x24, 0x50},
- {0x25, 0x7a},
- {0x26, 0x00},
- {0x27, 0xe2},
- {0x28, 0xb0},
- {0x2a, 0x00},
- {0x2b, 0x00},
- {0x2d, 0x81},
- {0x2f, 0x9d},
- {0x60, 0x80},
- {0x61, 0x00},
- {0x62, 0x88},
- {0x63, 0x11},
- {0x64, 0x89},
- {0x65, 0x00},
- {0x67, 0x94},
- {0x68, 0x7a},
- {0x69, 0x0f},
- {0x6c, 0x80},
- {0x6d, 0x80},
- {0x6e, 0x80},
- {0x6f, 0xff},
- {0x71, 0x20},
- {0x74, 0x20},
- {0x75, 0x86},
- {0x77, 0xb5},
- {0x17, 0x18}, /* H href start */
- {0x18, 0xbf}, /* H href end */
- {0x19, 0x03}, /* V start */
- {0x1a, 0xf8}, /* V end */
- {0x01, 0x80}, /* blue gain */
- {0x02, 0x80}, /* red gain */
- };
-
- reg_w_buf(gspca_dev, reg_init, ARRAY_SIZE(reg_init));
-
- i2c_w(gspca_dev, 0x12, 0x80); /* sensor reset */
- msleep(10);
-
- i2c_w_buf(gspca_dev, sensor_init, ARRAY_SIZE(sensor_init));
-
- reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
- reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
-
- setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
-
- bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
- ARRAY_SIZE(color_gain[0]));
-
- set_led(gspca_dev, 1);
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain),
- v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- else
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
- if (sd->sensor == SENSOR_SOI763A)
- setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
- setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
-}
-
-static void soi763a_6810_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const struct cmd bridge_init_2[] = {
- {TP6800_R7A_BLK_THRLD, 0x00},
- {TP6800_R79_QUALITY, 0x04},
- {TP6800_R79_QUALITY, 0x01},
- };
- static const struct cmd bridge_init_3[] = {
- {TP6800_R31_PIXEL_START, 0x20},
- {TP6800_R32_PIXEL_END_L, 0x9f},
- {TP6800_R33_PIXEL_END_H, 0x02},
- {TP6800_R34_LINE_START, 0x13},
- {TP6800_R35_LINE_END_L, 0xf8},
- {TP6800_R36_LINE_END_H, 0x01},
- };
- static const struct cmd bridge_init_6[] = {
- {0x08, 0xff},
- {0x09, 0xff},
- {0x0a, 0x5f},
- {0x0b, 0x80},
- };
-
- reg_w(gspca_dev, 0x22, gspca_dev->alt);
- bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
- reg_w(gspca_dev, 0x59, 0x40);
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain),
- v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- else
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
- reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
- reg_w_buf(gspca_dev, tp6810_ov_init_common,
- ARRAY_SIZE(tp6810_ov_init_common));
- reg_w_buf(gspca_dev, bridge_init_3, ARRAY_SIZE(bridge_init_3));
- if (gspca_dev->curr_mode) {
- reg_w(gspca_dev, 0x4a, 0x7f);
- reg_w(gspca_dev, 0x07, 0x05);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
- } else {
- reg_w(gspca_dev, 0x4a, 0xff);
- reg_w(gspca_dev, 0x07, 0x85);
- reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01); /* qvga */
- }
- setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
- reg_w_buf(gspca_dev, tp6810_bridge_start,
- ARRAY_SIZE(tp6810_bridge_start));
-
- if (gspca_dev->curr_mode) {
- reg_w(gspca_dev, 0x4f, 0x00);
- reg_w(gspca_dev, 0x4e, 0x7c);
- }
-
- reg_w(gspca_dev, 0x00, 0x00);
-
- setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
- bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
- ARRAY_SIZE(color_gain[0]));
- set_led(gspca_dev, 1);
- reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain),
- v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- else
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
- reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
-}
-
-/* -- start the camera -- */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width);
- set_dqt(gspca_dev, sd->quality);
- if (sd->bridge == BRIDGE_TP6800) {
- if (sd->sensor == SENSOR_CX0342)
- cx0342_6800_start(gspca_dev);
- else
- soi763a_6800_start(gspca_dev);
- } else {
- if (sd->sensor == SENSOR_CX0342)
- cx0342_6810_start(gspca_dev);
- else
- soi763a_6810_start(gspca_dev);
- reg_w_buf(gspca_dev, tp6810_late_start,
- ARRAY_SIZE(tp6810_late_start));
- reg_w(gspca_dev, 0x80, 0x03);
- reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
-
- if (sd->sensor == SENSOR_CX0342)
- setexposure(gspca_dev,
- v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain),
- v4l2_ctrl_g_ctrl(sd->blue),
- v4l2_ctrl_g_ctrl(sd->red));
- else
- setexposure(gspca_dev,
- v4l2_ctrl_g_ctrl(gspca_dev->exposure),
- v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
- if (sd->sensor == SENSOR_SOI763A)
- setquality(gspca_dev,
- v4l2_ctrl_g_ctrl(sd->jpegqual));
- if (sd->bridge == BRIDGE_TP6810)
- setautogain(gspca_dev,
- v4l2_ctrl_g_ctrl(gspca_dev->autogain));
- }
-
- setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
-
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->bridge == BRIDGE_TP6800)
- reg_w(gspca_dev, TP6800_R2F_TIMING_CFG, 0x03);
- set_led(gspca_dev, 0);
- reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data,
- int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* the start of frame contains:
- * ff d8
- * ff fe
- * width / 16
- * height / 8
- * quality
- */
- if (sd->bridge == BRIDGE_TP6810) {
- if (*data != 0x5a) {
-/*fixme: don't discard the whole frame..*/
- if (*data == 0xaa || *data == 0x00)
- return;
- if (*data > 0xc0) {
- PDEBUG(D_FRAM, "bad frame");
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- }
- data++;
- len--;
- if (*data == 0xff && data[1] == 0xd8) {
-/*fixme: there may be information in the 4 high bits*/
- if ((data[6] & 0x0f) != sd->quality)
- set_dqt(gspca_dev, data[6] & 0x0f);
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data + 7, len - 7);
- } else if (data[len - 2] == 0xff && data[len - 1] == 0xd9) {
- gspca_frame_add(gspca_dev, LAST_PACKET,
- data, len);
- } else {
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data, len);
- }
- return;
- }
-
- switch (*data) {
- case 0x55:
- gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
-
- if (len < 8
- || data[1] != 0xff || data[2] != 0xd8
- || data[3] != 0xff || data[4] != 0xfe) {
-
- /* Have only seen this with corrupt frames */
- gspca_dev->last_packet_type = DISCARD_PACKET;
- return;
- }
- if (data[7] != sd->quality)
- set_dqt(gspca_dev, data[7]);
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data + 8, len - 8);
- break;
- case 0xaa:
- gspca_dev->last_packet_type = DISCARD_PACKET;
- break;
- case 0xcc:
- if (data[1] != 0xff || data[2] != 0xd8)
- gspca_frame_add(gspca_dev, INTER_PACKET,
- data + 1, len - 1);
- else
- gspca_dev->last_packet_type = DISCARD_PACKET;
- break;
- }
-}
-
-static void sd_dq_callback(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret, alen;
- int luma, expo;
-
- if (sd->ag_cnt < 0)
- return;
- if (--sd->ag_cnt > 5)
- return;
- switch (sd->ag_cnt) {
-/* case 5: */
- default:
- reg_w(gspca_dev, 0x7d, 0x00);
- break;
- case 4:
- reg_w(gspca_dev, 0x27, 0xb0);
- break;
- case 3:
- reg_w(gspca_dev, 0x0c, 0x01);
- break;
- case 2:
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x02),
- gspca_dev->usb_buf,
- 32,
- &alen,
- 500);
- if (ret < 0) {
- pr_err("bulk err %d\n", ret);
- break;
- }
- /* values not used (unknown) */
- break;
- case 1:
- reg_w(gspca_dev, 0x27, 0xd0);
- break;
- case 0:
- ret = usb_bulk_msg(gspca_dev->dev,
- usb_rcvbulkpipe(gspca_dev->dev, 0x02),
- gspca_dev->usb_buf,
- 32,
- &alen,
- 500);
- if (ret < 0) {
- pr_err("bulk err %d\n", ret);
- break;
- }
- luma = ((gspca_dev->usb_buf[8] << 8) + gspca_dev->usb_buf[7] +
- (gspca_dev->usb_buf[11] << 8) + gspca_dev->usb_buf[10] +
- (gspca_dev->usb_buf[14] << 8) + gspca_dev->usb_buf[13] +
- (gspca_dev->usb_buf[17] << 8) + gspca_dev->usb_buf[16] +
- (gspca_dev->usb_buf[20] << 8) + gspca_dev->usb_buf[19] +
- (gspca_dev->usb_buf[23] << 8) + gspca_dev->usb_buf[22] +
- (gspca_dev->usb_buf[26] << 8) + gspca_dev->usb_buf[25] +
- (gspca_dev->usb_buf[29] << 8) + gspca_dev->usb_buf[28])
- / 8;
- if (gspca_dev->width == 640)
- luma /= 4;
- reg_w(gspca_dev, 0x7d, 0x00);
-
- expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
- ret = gspca_expo_autogain(gspca_dev, luma,
- 60, /* desired luma */
- 6, /* dead zone */
- 2, /* gain knee */
- 70); /* expo knee */
- sd->ag_cnt = AG_CNT_START;
- if (sd->bridge == BRIDGE_TP6810) {
- int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
-
- if ((expo >= 128 && new_expo < 128)
- || (expo < 128 && new_expo >= 128))
- setframerate(gspca_dev, new_expo);
- }
- break;
- }
-}
-
-/* get stream parameters (framerate) */
-static void sd_get_streamparm(struct gspca_dev *gspca_dev,
- struct v4l2_streamparm *parm)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct v4l2_captureparm *cp = &parm->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
- int fr, i;
-
- cp->capability |= V4L2_CAP_TIMEPERFRAME;
- tpf->numerator = 1;
- i = get_fr_idx(gspca_dev);
- if (i & 0x80) {
- if (sd->bridge == BRIDGE_TP6800)
- fr = rates[6 - (i & 0x07)];
- else
- fr = rates_6810[7 - (i & 0x07)];
- } else {
- fr = rates[6 - i];
- }
- tpf->denominator = fr;
-}
-
-/* set stream parameters (framerate) */
-static void sd_set_streamparm(struct gspca_dev *gspca_dev,
- struct v4l2_streamparm *parm)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct v4l2_captureparm *cp = &parm->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
- int fr, i;
-
- sd->framerate = tpf->denominator / tpf->numerator;
- if (gspca_dev->streaming)
- setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
-
- /* Return the actual framerate */
- i = get_fr_idx(gspca_dev);
- if (i & 0x80)
- fr = rates_6810[7 - (i & 0x07)];
- else
- fr = rates[6 - i];
- tpf->numerator = 1;
- tpf->denominator = fr;
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor != SENSOR_SOI763A)
- return -ENOTTY;
- v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
- return 0;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor != SENSOR_SOI763A)
- return -ENOTTY;
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- setsharpness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_GAMMA:
- setgamma(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_BLUE_BALANCE:
- setbgain(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_RED_BALANCE:
- setrgain(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_EXPOSURE:
- sd_setgain(gspca_dev);
- break;
- case V4L2_CID_AUTOGAIN:
- if (ctrl->val)
- break;
- sd_setgain(gspca_dev);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 4);
- gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
- if (sd->sensor == SENSOR_CX0342) {
- sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
- sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
- }
- if (sd->sensor == SENSOR_SOI763A)
- gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 15, 1, 3);
- else
- gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 4095, 1, 256);
- sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 3, 1, 2);
- sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
- (sd->sensor == SENSOR_SOI763A &&
- sd->bridge == BRIDGE_TP6800) ? 0 : 1);
- if (sd->bridge == BRIDGE_TP6810)
- gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- if (sd->sensor == SENSOR_SOI763A)
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- 0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- if (gspca_dev->autogain)
- v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
- else
- v4l2_ctrl_cluster(2, &gspca_dev->exposure);
- return 0;
-}
-
-static const struct sd_desc sd_desc = {
- .name = KBUILD_MODNAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .isoc_init = sd_isoc_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .pkt_scan = sd_pkt_scan,
- .dq_callback = sd_dq_callback,
- .get_streamparm = sd_get_streamparm,
- .set_streamparm = sd_set_streamparm,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
-};
-
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x06a2, 0x0003), .driver_info = BRIDGE_TP6800},
- {USB_DEVICE(0x06a2, 0x6810), .driver_info = BRIDGE_TP6810},
- {} /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, device_table);
-
-static int sd_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- return gspca_dev_probe(interface, id, &sd_desc, sizeof(struct sd),
- THIS_MODULE);
-}
-
-static struct usb_driver sd_driver = {
- .name = KBUILD_MODNAME,
- .id_table = device_table,
- .probe = sd_probe,
- .disconnect = gspca_disconnect,
-#ifdef CONFIG_PM
- .suspend = gspca_suspend,
- .resume = gspca_resume,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
-
-module_param(force_sensor, int, 0644);
-MODULE_PARM_DESC(force_sensor,
- "Force sensor. 0: cx0342, 1: soi763a");
diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
deleted file mode 100644
index f21fd1677c38..000000000000
--- a/drivers/media/video/gspca/vc032x.c
+++ /dev/null
@@ -1,3853 +0,0 @@
-/*
- * Z-star vc0321 library
- *
- * Copyright (C) 2009-2010 Jean-François Moine <http://moinejf.free.fr>
- * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl
- * Copyright (C) 2006 Michel Xhaard
- *
- * This program is free software; you can redistribute it and/or modify
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "vc032x"
-
-#include "gspca.h"
-
-MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
-MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- struct { /* hvflip cluster */
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- };
-
- u8 image_offset;
-
- u8 bridge;
- u8 sensor;
- u8 flags;
-#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */
-#define FL_HFLIP 0x02 /* mirrored by default */
-#define FL_VFLIP 0x04 /* vertical flipped by default */
-};
-enum bridges {
- BRIDGE_VC0321,
- BRIDGE_VC0323,
-};
-enum sensors {
- SENSOR_HV7131R,
- SENSOR_MI0360,
- SENSOR_MI1310_SOC,
- SENSOR_MI1320,
- SENSOR_MI1320_SOC,
- SENSOR_OV7660,
- SENSOR_OV7670,
- SENSOR_PO1200,
- SENSOR_PO3130NC,
- SENSOR_POxxxx,
- NSENSORS
-};
-
-
-static const struct v4l2_pix_format vc0321_mode[] = {
- {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-static const struct v4l2_pix_format vc0323_mode[] = {
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
- {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */
- .bytesperline = 1280,
- .sizeimage = 1280 * 960 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 2},
-};
-static const struct v4l2_pix_format bi_mode[] = {
- {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 2},
- {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 1},
- {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
- .bytesperline = 1280,
- .sizeimage = 1280 * 1024 * 2,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .priv = 0},
-};
-static const struct v4l2_pix_format svga_mode[] = {
- {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 800,
- .sizeimage = 800 * 600 * 1 / 4 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-/* OV7660/7670 registers */
-#define OV7660_REG_MVFP 0x1e
-#define OV7660_MVFP_MIRROR 0x20
-#define OV7660_MVFP_VFLIP 0x10
-
-static const u8 mi0360_matrix[9] = {
- 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
-};
-
-static const u8 mi0360_initVGA_JPG[][4] = {
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xbc, 0x00, 0x71, 0xcc},
- {0xb8, 0x00, 0x13, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc},
- {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc},
- {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc},
- {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc},
- {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x50, 0xcc},
- {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc},
- {0xb8, 0x37, 0x00, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc},
- {0xb8, 0x08, 0xe0, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc},
- {0xb8, 0x14, 0x18, 0xcc},
- {0xb8, 0xb2, 0x0a, 0xcc},
- {0xb8, 0xb4, 0x0a, 0xcc},
- {0xb8, 0xb5, 0x0a, 0xcc},
- {0xb8, 0xfe, 0x00, 0xcc},
- {0xb8, 0xff, 0x28, 0xcc},
- {0xb9, 0x00, 0x28, 0xcc},
- {0xb9, 0x01, 0x28, 0xcc},
- {0xb9, 0x02, 0x28, 0xcc},
- {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc},
- {0xb9, 0x05, 0x3c, 0xcc},
- {0xb9, 0x06, 0x3c, 0xcc},
- {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc},
- {0x31, 0x00, 0x00, 0xbb},
- {0x09, 0x01, 0xc7, 0xbb},
- {0x34, 0x01, 0x00, 0xbb},
- {0x2b, 0x00, 0x28, 0xbb},
- {0x2c, 0x00, 0x30, 0xbb},
- {0x2d, 0x00, 0x30, 0xbb},
- {0x2e, 0x00, 0x28, 0xbb},
- {0x62, 0x04, 0x11, 0xbb},
- {0x03, 0x01, 0xe0, 0xbb},
- {0x2c, 0x00, 0x2c, 0xbb},
- {0x20, 0xd0, 0x00, 0xbb},
- {0x01, 0x00, 0x08, 0xbb},
- {0x06, 0x00, 0x10, 0xbb},
- {0x05, 0x00, 0x20, 0xbb},
- {0x20, 0x00, 0x00, 0xbb},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x02, 0xcc},
- {0xb6, 0x02, 0x80, 0xcc},
- {0xb6, 0x05, 0x01, 0xcc},
- {0xb6, 0x04, 0xe0, 0xcc},
- {0xb6, 0x12, 0x78, 0xcc},
- {0xb6, 0x18, 0x02, 0xcc},
- {0xb6, 0x17, 0x58, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x10, 0xcc},
- {0xb9, 0x12, 0x00, 0xcc},
- {0xb9, 0x13, 0x0a, 0xcc},
- {0xb9, 0x14, 0x0a, 0xcc},
- {0xb9, 0x15, 0x0a, 0xcc},
- {0xb9, 0x16, 0x0a, 0xcc},
- {0xb9, 0x18, 0x00, 0xcc},
- {0xb9, 0x19, 0x0f, 0xcc},
- {0xb9, 0x1a, 0x0f, 0xcc},
- {0xb9, 0x1b, 0x0f, 0xcc},
- {0xb9, 0x1c, 0x0f, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb8, 0x0c, 0x20, 0xcc},
- {0xb8, 0x0d, 0x70, 0xcc},
- {0xb6, 0x13, 0x13, 0xcc},
- {0x35, 0x00, 0x60, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-static const u8 mi0360_initQVGA_JPG[][4] = {
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xbc, 0x00, 0xd1, 0xcc},
- {0xb8, 0x00, 0x13, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc},
- {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc},
- {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc},
- {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc},
- {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x50, 0xcc},
- {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc},
- {0xb8, 0x37, 0x00, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc},
- {0xb8, 0x08, 0xe0, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc},
- {0xb8, 0x14, 0x18, 0xcc},
- {0xb8, 0xb2, 0x0a, 0xcc},
- {0xb8, 0xb4, 0x0a, 0xcc},
- {0xb8, 0xb5, 0x0a, 0xcc},
- {0xb8, 0xfe, 0x00, 0xcc},
- {0xb8, 0xff, 0x28, 0xcc},
- {0xb9, 0x00, 0x28, 0xcc},
- {0xb9, 0x01, 0x28, 0xcc},
- {0xb9, 0x02, 0x28, 0xcc},
- {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc},
- {0xb9, 0x05, 0x3c, 0xcc},
- {0xb9, 0x06, 0x3c, 0xcc},
- {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc},
- {0x31, 0x00, 0x00, 0xbb},
- {0x09, 0x01, 0xc7, 0xbb},
- {0x34, 0x01, 0x00, 0xbb},
- {0x2b, 0x00, 0x28, 0xbb},
- {0x2c, 0x00, 0x30, 0xbb},
- {0x2d, 0x00, 0x30, 0xbb},
- {0x2e, 0x00, 0x28, 0xbb},
- {0x62, 0x04, 0x11, 0xbb},
- {0x03, 0x01, 0xe0, 0xbb},
- {0x2c, 0x00, 0x2c, 0xbb},
- {0x20, 0xd0, 0x00, 0xbb},
- {0x01, 0x00, 0x08, 0xbb},
- {0x06, 0x00, 0x10, 0xbb},
- {0x05, 0x00, 0x20, 0xbb},
- {0x20, 0x00, 0x00, 0xbb},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x01, 0xcc},
- {0xb6, 0x02, 0x40, 0xcc},
- {0xb6, 0x05, 0x00, 0xcc},
- {0xb6, 0x04, 0xf0, 0xcc},
- {0xb6, 0x12, 0x78, 0xcc},
- {0xb6, 0x18, 0x00, 0xcc},
- {0xb6, 0x17, 0x96, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x10, 0xcc},
- {0xb9, 0x12, 0x00, 0xcc},
- {0xb9, 0x13, 0x0a, 0xcc},
- {0xb9, 0x14, 0x0a, 0xcc},
- {0xb9, 0x15, 0x0a, 0xcc},
- {0xb9, 0x16, 0x0a, 0xcc},
- {0xb9, 0x18, 0x00, 0xcc},
- {0xb9, 0x19, 0x0f, 0xcc},
- {0xb9, 0x1a, 0x0f, 0xcc},
- {0xb9, 0x1b, 0x0f, 0xcc},
- {0xb9, 0x1c, 0x0f, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x13, 0xcc},
- {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc},
- {0xb8, 0x0c, 0x20, 0xcc},
- {0xb8, 0x0d, 0x70, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
- {0x35, 0x00, 0xef, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-
-static const u8 mi1310_socinitVGA_JPG[][4] = {
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x03, 0xcc},
- {0xb3, 0x23, 0xc0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x04, 0xcc},
- {0xb3, 0x17, 0xff, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc},
- {0xb8, 0x00, 0x00, 0xcc},
- {0xbc, 0x00, 0xd0, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0xc8, 0x9f, 0x0b, 0xbb},
- {0x5b, 0x00, 0x01, 0xbb},
- {0x2f, 0xde, 0x20, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
- {0xf0, 0x00, 0x01, 0xbb},
- {0x05, 0x00, 0x07, 0xbb},
- {0x34, 0x00, 0x00, 0xbb},
- {0x35, 0xff, 0x00, 0xbb},
- {0xdc, 0x07, 0x02, 0xbb},
- {0xdd, 0x3c, 0x18, 0xbb},
- {0xde, 0x92, 0x6d, 0xbb},
- {0xdf, 0xcd, 0xb1, 0xbb},
- {0xe0, 0xff, 0xe7, 0xbb},
- {0x06, 0xf0, 0x0d, 0xbb},
- {0x06, 0x70, 0x0e, 0xbb},
- {0x4c, 0x00, 0x01, 0xbb},
- {0x4d, 0x00, 0x01, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x2e, 0x0c, 0x55, 0xbb},
- {0x21, 0xb6, 0x6e, 0xbb},
- {0x36, 0x30, 0x10, 0xbb},
- {0x37, 0x00, 0xc1, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x07, 0x00, 0x84, 0xbb},
- {0x08, 0x02, 0x4a, 0xbb},
- {0x05, 0x01, 0x10, 0xbb},
- {0x06, 0x00, 0x39, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x58, 0x02, 0x67, 0xbb},
- {0x57, 0x02, 0x00, 0xbb},
- {0x5a, 0x02, 0x67, 0xbb},
- {0x59, 0x02, 0x00, 0xbb},
- {0x5c, 0x12, 0x0d, 0xbb},
- {0x5d, 0x16, 0x11, 0xbb},
- {0x39, 0x06, 0x18, 0xbb},
- {0x3a, 0x06, 0x18, 0xbb},
- {0x3b, 0x06, 0x18, 0xbb},
- {0x3c, 0x06, 0x18, 0xbb},
- {0x64, 0x7b, 0x5b, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x36, 0x30, 0x10, 0xbb},
- {0x37, 0x00, 0xc0, 0xbb},
- {0xbc, 0x0e, 0x00, 0xcc},
- {0xbc, 0x0f, 0x05, 0xcc},
- {0xbc, 0x10, 0xc0, 0xcc},
- {0xbc, 0x11, 0x03, 0xcc},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x02, 0xcc},
- {0xb6, 0x02, 0x80, 0xcc},
- {0xb6, 0x05, 0x01, 0xcc},
- {0xb6, 0x04, 0xe0, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x25, 0xcc},
- {0xb6, 0x18, 0x02, 0xcc},
- {0xb6, 0x17, 0x58, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x00, 0xcc},
- {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x80, 0x00, 0x03, 0xbb},
- {0x81, 0xc7, 0x14, 0xbb},
- {0x82, 0xeb, 0xe8, 0xbb},
- {0x83, 0xfe, 0xf4, 0xbb},
- {0x84, 0xcd, 0x10, 0xbb},
- {0x85, 0xf3, 0xee, 0xbb},
- {0x86, 0xff, 0xf1, 0xbb},
- {0x87, 0xcd, 0x10, 0xbb},
- {0x88, 0xf3, 0xee, 0xbb},
- {0x89, 0x01, 0xf1, 0xbb},
- {0x8a, 0xe5, 0x17, 0xbb},
- {0x8b, 0xe8, 0xe2, 0xbb},
- {0x8c, 0xf7, 0xed, 0xbb},
- {0x8d, 0x00, 0xff, 0xbb},
- {0x8e, 0xec, 0x10, 0xbb},
- {0x8f, 0xf0, 0xed, 0xbb},
- {0x90, 0xf9, 0xf2, 0xbb},
- {0x91, 0x00, 0x00, 0xbb},
- {0x92, 0xe9, 0x0d, 0xbb},
- {0x93, 0xf4, 0xf2, 0xbb},
- {0x94, 0xfb, 0xf5, 0xbb},
- {0x95, 0x00, 0xff, 0xbb},
- {0xb6, 0x0f, 0x08, 0xbb},
- {0xb7, 0x3d, 0x16, 0xbb},
- {0xb8, 0x0c, 0x04, 0xbb},
- {0xb9, 0x1c, 0x07, 0xbb},
- {0xba, 0x0a, 0x03, 0xbb},
- {0xbb, 0x1b, 0x09, 0xbb},
- {0xbc, 0x17, 0x0d, 0xbb},
- {0xbd, 0x23, 0x1d, 0xbb},
- {0xbe, 0x00, 0x28, 0xbb},
- {0xbf, 0x11, 0x09, 0xbb},
- {0xc0, 0x16, 0x15, 0xbb},
- {0xc1, 0x00, 0x1b, 0xbb},
- {0xc2, 0x0e, 0x07, 0xbb},
- {0xc3, 0x14, 0x10, 0xbb},
- {0xc4, 0x00, 0x17, 0xbb},
- {0x06, 0x74, 0x8e, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0xf4, 0x8e, 0xbb},
- {0x00, 0x00, 0x50, 0xdd},
- {0x06, 0x74, 0x8e, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x24, 0x50, 0x20, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x34, 0x0c, 0x50, 0xbb},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x03, 0x03, 0xc0, 0xbb},
- {},
-};
-static const u8 mi1310_socinitQVGA_JPG[][4] = {
- {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc},
- {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc},
- {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc},
- {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc},
- {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
- {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb},
- {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
- {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
- {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
- {0xf0, 0x00, 0x01, 0xbb},
- {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb},
- {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb},
- {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb},
- {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb},
- {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb},
- {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb},
- {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
- {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
- {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb},
- {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb},
- {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb},
- {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
- {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb},
- {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb},
- {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
- {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
- {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
- {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc},
- {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc},
- {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb},
- {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb},
- {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb},
- {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb},
- {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb},
- {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb},
- {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb},
- {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb},
- {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb},
- {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
- {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb},
- {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb},
- {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb},
- {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb},
- {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb},
- {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb},
- {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb},
- {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb},
- {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb},
- {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb},
- {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb},
- {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
- {0x03, 0x03, 0xc0, 0xbb},
- {},
-};
-static const u8 mi1310_soc_InitSXGA_JPG[][4] = {
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xdd, 0xcc},
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x0d, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x03, 0xcc},
- {0xb3, 0x23, 0xc0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x04, 0xcc},
- {0xb3, 0x17, 0xff, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc},
- {0xb8, 0x00, 0x00, 0xcc},
- {0xbc, 0x00, 0x70, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0xc8, 0x9f, 0x0b, 0xbb},
- {0x5b, 0x00, 0x01, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */
- {0xf0, 0x00, 0x01, 0xbb},
- {0x05, 0x00, 0x07, 0xbb},
- {0x34, 0x00, 0x00, 0xbb},
- {0x35, 0xff, 0x00, 0xbb},
- {0xdc, 0x07, 0x02, 0xbb},
- {0xdd, 0x3c, 0x18, 0xbb},
- {0xde, 0x92, 0x6d, 0xbb},
- {0xdf, 0xcd, 0xb1, 0xbb},
- {0xe0, 0xff, 0xe7, 0xbb},
- {0x06, 0xf0, 0x0d, 0xbb},
- {0x06, 0x70, 0x0e, 0xbb},
- {0x4c, 0x00, 0x01, 0xbb},
- {0x4d, 0x00, 0x01, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x2e, 0x0c, 0x60, 0xbb},
- {0x21, 0xb6, 0x6e, 0xbb},
- {0x37, 0x01, 0x40, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x07, 0x00, 0x84, 0xbb},
- {0x08, 0x02, 0x4a, 0xbb},
- {0x05, 0x01, 0x10, 0xbb},
- {0x06, 0x00, 0x39, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x58, 0x02, 0x67, 0xbb},
- {0x57, 0x02, 0x00, 0xbb},
- {0x5a, 0x02, 0x67, 0xbb},
- {0x59, 0x02, 0x00, 0xbb},
- {0x5c, 0x12, 0x0d, 0xbb},
- {0x5d, 0x16, 0x11, 0xbb},
- {0x39, 0x06, 0x18, 0xbb},
- {0x3a, 0x06, 0x18, 0xbb},
- {0x3b, 0x06, 0x18, 0xbb},
- {0x3c, 0x06, 0x18, 0xbb},
- {0x64, 0x7b, 0x5b, 0xbb},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x05, 0xcc},
- {0xb6, 0x02, 0x00, 0xcc},
- {0xb6, 0x05, 0x03, 0xcc},
- {0xb6, 0x04, 0xc0, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x29, 0xcc},
- {0xb6, 0x18, 0x09, 0xcc},
- {0xb6, 0x17, 0x60, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x00, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0x00, 0x00, 0x80, 0xdd},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x00, 0x00, 0x10, 0xdd},
- {0x22, 0xa0, 0x78, 0xbb},
- {0x23, 0xa0, 0x78, 0xbb},
- {0x24, 0x7f, 0x00, 0xbb},
- {0x28, 0xea, 0x02, 0xbb},
- {0x29, 0x86, 0x7a, 0xbb},
- {0x5e, 0x52, 0x4c, 0xbb},
- {0x5f, 0x20, 0x24, 0xbb},
- {0x60, 0x00, 0x02, 0xbb},
- {0x02, 0x00, 0xee, 0xbb},
- {0x03, 0x39, 0x23, 0xbb},
- {0x04, 0x07, 0x24, 0xbb},
- {0x09, 0x00, 0xc0, 0xbb},
- {0x0a, 0x00, 0x79, 0xbb},
- {0x0b, 0x00, 0x04, 0xbb},
- {0x0c, 0x00, 0x5c, 0xbb},
- {0x0d, 0x00, 0xd9, 0xbb},
- {0x0e, 0x00, 0x53, 0xbb},
- {0x0f, 0x00, 0x21, 0xbb},
- {0x10, 0x00, 0xa4, 0xbb},
- {0x11, 0x00, 0xe5, 0xbb},
- {0x15, 0x00, 0x00, 0xbb},
- {0x16, 0x00, 0x00, 0xbb},
- {0x17, 0x00, 0x00, 0xbb},
- {0x18, 0x00, 0x00, 0xbb},
- {0x19, 0x00, 0x00, 0xbb},
- {0x1a, 0x00, 0x00, 0xbb},
- {0x1b, 0x00, 0x00, 0xbb},
- {0x1c, 0x00, 0x00, 0xbb},
- {0x1d, 0x00, 0x00, 0xbb},
- {0x1e, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x00, 0x00, 0x20, 0xdd},
- {0x06, 0xf0, 0x8e, 0xbb},
- {0x00, 0x00, 0x80, 0xdd},
- {0x06, 0x70, 0x8e, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x00, 0x00, 0x20, 0xdd},
- {0x5e, 0x6a, 0x53, 0xbb},
- {0x5f, 0x40, 0x2c, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x00, 0x00, 0x20, 0xdd},
- {0x58, 0x00, 0x00, 0xbb},
- {0x53, 0x09, 0x03, 0xbb},
- {0x54, 0x31, 0x18, 0xbb},
- {0x55, 0x8b, 0x5f, 0xbb},
- {0x56, 0xc0, 0xa9, 0xbb},
- {0x57, 0xe0, 0xd2, 0xbb},
- {0xe1, 0x00, 0x00, 0xbb},
- {0xdc, 0x09, 0x03, 0xbb},
- {0xdd, 0x31, 0x18, 0xbb},
- {0xde, 0x8b, 0x5f, 0xbb},
- {0xdf, 0xc0, 0xa9, 0xbb},
- {0xe0, 0xe0, 0xd2, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0xf0, 0x8e, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x2f, 0xde, 0x20, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x24, 0x50, 0x20, 0xbb},
- {0xbc, 0x0e, 0x00, 0xcc},
- {0xbc, 0x0f, 0x05, 0xcc},
- {0xbc, 0x10, 0xc0, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x34, 0x0c, 0x50, 0xbb},
- {0xbc, 0x11, 0x03, 0xcc},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x80, 0x00, 0x03, 0xbb},
- {0x81, 0xc7, 0x14, 0xbb},
- {0x82, 0xeb, 0xe8, 0xbb},
- {0x83, 0xfe, 0xf4, 0xbb},
- {0x84, 0xcd, 0x10, 0xbb},
- {0x85, 0xf3, 0xee, 0xbb},
- {0x86, 0xff, 0xf1, 0xbb},
- {0x87, 0xcd, 0x10, 0xbb},
- {0x88, 0xf3, 0xee, 0xbb},
- {0x89, 0x01, 0xf1, 0xbb},
- {0x8a, 0xe5, 0x17, 0xbb},
- {0x8b, 0xe8, 0xe2, 0xbb},
- {0x8c, 0xf7, 0xed, 0xbb},
- {0x8d, 0x00, 0xff, 0xbb},
- {0x8e, 0xec, 0x10, 0xbb},
- {0x8f, 0xf0, 0xed, 0xbb},
- {0x90, 0xf9, 0xf2, 0xbb},
- {0x91, 0x00, 0x00, 0xbb},
- {0x92, 0xe9, 0x0d, 0xbb},
- {0x93, 0xf4, 0xf2, 0xbb},
- {0x94, 0xfb, 0xf5, 0xbb},
- {0x95, 0x00, 0xff, 0xbb},
- {0xb6, 0x0f, 0x08, 0xbb},
- {0xb7, 0x3d, 0x16, 0xbb},
- {0xb8, 0x0c, 0x04, 0xbb},
- {0xb9, 0x1c, 0x07, 0xbb},
- {0xba, 0x0a, 0x03, 0xbb},
- {0xbb, 0x1b, 0x09, 0xbb},
- {0xbc, 0x17, 0x0d, 0xbb},
- {0xbd, 0x23, 0x1d, 0xbb},
- {0xbe, 0x00, 0x28, 0xbb},
- {0xbf, 0x11, 0x09, 0xbb},
- {0xc0, 0x16, 0x15, 0xbb},
- {0xc1, 0x00, 0x1b, 0xbb},
- {0xc2, 0x0e, 0x07, 0xbb},
- {0xc3, 0x14, 0x10, 0xbb},
- {0xc4, 0x00, 0x17, 0xbb},
- {0x06, 0x74, 0x8e, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x03, 0x03, 0xc0, 0xbb},
- {}
-};
-
-static const u8 mi1320_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
-};
-static const u8 mi1320_matrix[9] = {
- 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52
-};
-static const u8 mi1320_initVGA_data[][4] = {
- {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
- {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
- {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd},
- {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
- {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
- {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
- {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
- {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd},
- {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb},
- {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
- {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb},
- {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb},
- {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb},
- {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb},
- {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb},
- {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb},
- {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb},
- {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb},
- {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb},
- {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb},
- {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb},
- {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb},
- {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb},
- {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb},
- {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb},
- {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb},
- {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb},
- {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb},
- {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb},
- {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb},
- {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb},
- {0x08, 0x00, 0x27, 0xbb},
- {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */
- {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb},
- {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb},
- {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb},
- {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb},
- {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
- {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
- {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
- {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
- {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd},
- {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
- {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
- {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc},
- {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc},
- {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc},
- {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
- {}
-};
-static const u8 mi1320_initQVGA_data[][4] = {
- {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
- {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc},
- {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb},
- {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb},
- {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb},
- {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb},
- {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb},
- {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb},
- {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
- {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb},
- {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb},
- {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
- {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb},
- {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb},
- {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb},
- {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb},
- {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb},
- {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb},
- {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb},
- {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb},
- {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb},
- {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
- {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb},
- {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb},
- {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb},
- {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb},
- {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb},
- {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb},
- {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb},
- {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb},
- {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb},
- {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb},
- {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb},
- {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb},
- {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb},
- {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb},
- {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb},
- {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb},
- {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb},
- {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb},
- {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb},
- {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc},
- {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {}
-};
-
-static const u8 mi1320_soc_InitVGA[][4] = {
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x30, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb8, 0x00, 0x00, 0xcc},
- {0xbc, 0x00, 0x71, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x00, 0x00, 0x10, 0xdd},
- {0xc8, 0x00, 0x00, 0xbb},
- {0x00, 0x00, 0x30, 0xdd},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x00, 0x00, 0x10, 0xdd},
- {0x07, 0x00, 0xe0, 0xbb},
- {0x08, 0x00, 0x0b, 0xbb},
- {0x21, 0x00, 0x0c, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x05, 0x01, 0x78, 0xbb},
- {0x06, 0x00, 0x11, 0xbb},
- {0x07, 0x01, 0x42, 0xbb},
- {0x08, 0x00, 0x11, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0x21, 0x80, 0x00, 0xbb},
- {0x22, 0x0d, 0x0f, 0xbb},
- {0x24, 0x80, 0x00, 0xbb},
- {0x59, 0x00, 0xff, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x39, 0x03, 0xca, 0xbb},
- {0x3a, 0x06, 0x80, 0xbb},
- {0x3b, 0x01, 0x52, 0xbb},
- {0x3c, 0x05, 0x40, 0xbb},
- {0x57, 0x01, 0x9c, 0xbb},
- {0x58, 0x01, 0xee, 0xbb},
- {0x59, 0x00, 0xf0, 0xbb},
- {0x5a, 0x01, 0x20, 0xbb},
- {0x5c, 0x1d, 0x17, 0xbb},
- {0x5d, 0x22, 0x1c, 0xbb},
- {0x64, 0x1e, 0x1c, 0xbb},
- {0x5b, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x22, 0xa0, 0x78, 0xbb},
- {0x23, 0xa0, 0x78, 0xbb},
- {0x24, 0x7f, 0x00, 0xbb},
- {0x28, 0xea, 0x02, 0xbb},
- {0x29, 0x86, 0x7a, 0xbb},
- {0x5e, 0x52, 0x4c, 0xbb},
- {0x5f, 0x20, 0x24, 0xbb},
- {0x60, 0x00, 0x02, 0xbb},
- {0x02, 0x00, 0xee, 0xbb},
- {0x03, 0x39, 0x23, 0xbb},
- {0x04, 0x07, 0x24, 0xbb},
- {0x09, 0x00, 0xc0, 0xbb},
- {0x0a, 0x00, 0x79, 0xbb},
- {0x0b, 0x00, 0x04, 0xbb},
- {0x0c, 0x00, 0x5c, 0xbb},
- {0x0d, 0x00, 0xd9, 0xbb},
- {0x0e, 0x00, 0x53, 0xbb},
- {0x0f, 0x00, 0x21, 0xbb},
- {0x10, 0x00, 0xa4, 0xbb},
- {0x11, 0x00, 0xe5, 0xbb},
- {0x15, 0x00, 0x00, 0xbb},
- {0x16, 0x00, 0x00, 0xbb},
- {0x17, 0x00, 0x00, 0xbb},
- {0x18, 0x00, 0x00, 0xbb},
- {0x19, 0x00, 0x00, 0xbb},
- {0x1a, 0x00, 0x00, 0xbb},
- {0x1b, 0x00, 0x00, 0xbb},
- {0x1c, 0x00, 0x00, 0xbb},
- {0x1d, 0x00, 0x00, 0xbb},
- {0x1e, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0xe0, 0x0e, 0xbb},
- {0x06, 0x60, 0x0e, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-static const u8 mi1320_soc_InitQVGA[][4] = {
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x30, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc},
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb8, 0x00, 0x00, 0xcc},
- {0xbc, 0x00, 0xd1, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x00, 0x00, 0x10, 0xdd},
- {0xc8, 0x00, 0x00, 0xbb},
- {0x00, 0x00, 0x30, 0xdd},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x00, 0x00, 0x10, 0xdd},
- {0x07, 0x00, 0xe0, 0xbb},
- {0x08, 0x00, 0x0b, 0xbb},
- {0x21, 0x00, 0x0c, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc},
- {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x05, 0x01, 0x78, 0xbb},
- {0x06, 0x00, 0x11, 0xbb},
- {0x07, 0x01, 0x42, 0xbb},
- {0x08, 0x00, 0x11, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0x21, 0x80, 0x00, 0xbb},
- {0x22, 0x0d, 0x0f, 0xbb},
- {0x24, 0x80, 0x00, 0xbb},
- {0x59, 0x00, 0xff, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x39, 0x03, 0xca, 0xbb},
- {0x3a, 0x06, 0x80, 0xbb},
- {0x3b, 0x01, 0x52, 0xbb},
- {0x3c, 0x05, 0x40, 0xbb},
- {0x57, 0x01, 0x9c, 0xbb},
- {0x58, 0x01, 0xee, 0xbb},
- {0x59, 0x00, 0xf0, 0xbb},
- {0x5a, 0x01, 0x20, 0xbb},
- {0x5c, 0x1d, 0x17, 0xbb},
- {0x5d, 0x22, 0x1c, 0xbb},
- {0x64, 0x1e, 0x1c, 0xbb},
- {0x5b, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x22, 0xa0, 0x78, 0xbb},
- {0x23, 0xa0, 0x78, 0xbb},
- {0x24, 0x7f, 0x00, 0xbb},
- {0x28, 0xea, 0x02, 0xbb},
- {0x29, 0x86, 0x7a, 0xbb},
- {0x5e, 0x52, 0x4c, 0xbb},
- {0x5f, 0x20, 0x24, 0xbb},
- {0x60, 0x00, 0x02, 0xbb},
- {0x02, 0x00, 0xee, 0xbb},
- {0x03, 0x39, 0x23, 0xbb},
- {0x04, 0x07, 0x24, 0xbb},
- {0x09, 0x00, 0xc0, 0xbb},
- {0x0a, 0x00, 0x79, 0xbb},
- {0x0b, 0x00, 0x04, 0xbb},
- {0x0c, 0x00, 0x5c, 0xbb},
- {0x0d, 0x00, 0xd9, 0xbb},
- {0x0e, 0x00, 0x53, 0xbb},
- {0x0f, 0x00, 0x21, 0xbb},
- {0x10, 0x00, 0xa4, 0xbb},
- {0x11, 0x00, 0xe5, 0xbb},
- {0x15, 0x00, 0x00, 0xbb},
- {0x16, 0x00, 0x00, 0xbb},
- {0x17, 0x00, 0x00, 0xbb},
- {0x18, 0x00, 0x00, 0xbb},
- {0x19, 0x00, 0x00, 0xbb},
- {0x1a, 0x00, 0x00, 0xbb},
- {0x1b, 0x00, 0x00, 0xbb},
- {0x1c, 0x00, 0x00, 0xbb},
- {0x1d, 0x00, 0x00, 0xbb},
- {0x1e, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0xe0, 0x0e, 0xbb},
- {0x06, 0x60, 0x0e, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-static const u8 mi1320_soc_InitSXGA[][4] = {
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb0, 0x03, 0x19, 0xcc},
- {0x00, 0x00, 0x30, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x02, 0xcc},
- {0xb3, 0x35, 0xc8, 0xcc},
- {0xb3, 0x02, 0x00, 0xcc},
- {0xb3, 0x03, 0x0a, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x04, 0xcc},
- {0xb3, 0x23, 0x00, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x04, 0xcc},
- {0xb3, 0x17, 0xff, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xbc, 0x00, 0x71, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x00, 0x00, 0x30, 0xdd},
- {0xc8, 0x9f, 0x0b, 0xbb},
- {0x00, 0x00, 0x20, 0xdd},
- {0x5b, 0x00, 0x01, 0xbb},
- {0x00, 0x00, 0x20, 0xdd},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x00, 0x00, 0x30, 0xdd},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0x00, 0x00, 0x20, 0xdd},
- {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x05, 0x01, 0x78, 0xbb},
- {0x06, 0x00, 0x11, 0xbb},
- {0x07, 0x01, 0x42, 0xbb},
- {0x08, 0x00, 0x11, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0x21, 0x80, 0x00, 0xbb},
- {0x22, 0x0d, 0x0f, 0xbb},
- {0x24, 0x80, 0x00, 0xbb},
- {0x59, 0x00, 0xff, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x39, 0x03, 0xca, 0xbb},
- {0x3a, 0x06, 0x80, 0xbb},
- {0x3b, 0x01, 0x52, 0xbb},
- {0x3c, 0x05, 0x40, 0xbb},
- {0x57, 0x01, 0x9c, 0xbb},
- {0x58, 0x01, 0xee, 0xbb},
- {0x59, 0x00, 0xf0, 0xbb},
- {0x5a, 0x01, 0x20, 0xbb},
- {0x5c, 0x1d, 0x17, 0xbb},
- {0x5d, 0x22, 0x1c, 0xbb},
- {0x64, 0x1e, 0x1c, 0xbb},
- {0x5b, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x22, 0xa0, 0x78, 0xbb},
- {0x23, 0xa0, 0x78, 0xbb},
- {0x24, 0x7f, 0x00, 0xbb},
- {0x28, 0xea, 0x02, 0xbb},
- {0x29, 0x86, 0x7a, 0xbb},
- {0x5e, 0x52, 0x4c, 0xbb},
- {0x5f, 0x20, 0x24, 0xbb},
- {0x60, 0x00, 0x02, 0xbb},
- {0x02, 0x00, 0xee, 0xbb},
- {0x03, 0x39, 0x23, 0xbb},
- {0x04, 0x07, 0x24, 0xbb},
- {0x09, 0x00, 0xc0, 0xbb},
- {0x0a, 0x00, 0x79, 0xbb},
- {0x0b, 0x00, 0x04, 0xbb},
- {0x0c, 0x00, 0x5c, 0xbb},
- {0x0d, 0x00, 0xd9, 0xbb},
- {0x0e, 0x00, 0x53, 0xbb},
- {0x0f, 0x00, 0x21, 0xbb},
- {0x10, 0x00, 0xa4, 0xbb},
- {0x11, 0x00, 0xe5, 0xbb},
- {0x15, 0x00, 0x00, 0xbb},
- {0x16, 0x00, 0x00, 0xbb},
- {0x17, 0x00, 0x00, 0xbb},
- {0x18, 0x00, 0x00, 0xbb},
- {0x19, 0x00, 0x00, 0xbb},
- {0x1a, 0x00, 0x00, 0xbb},
- {0x1b, 0x00, 0x00, 0xbb},
- {0x1c, 0x00, 0x00, 0xbb},
- {0x1d, 0x00, 0x00, 0xbb},
- {0x1e, 0x00, 0x00, 0xbb},
- {0xf0, 0x00, 0x01, 0xbb},
- {0x06, 0xe0, 0x0e, 0xbb},
- {0x06, 0x60, 0x0e, 0xbb},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xf0, 0x00, 0x00, 0xbb},
- {0x05, 0x01, 0x13, 0xbb},
- {0x06, 0x00, 0x11, 0xbb},
- {0x07, 0x00, 0x85, 0xbb},
- {0x08, 0x00, 0x27, 0xbb},
- {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */
- {0x21, 0x80, 0x00, 0xbb},
- {0x22, 0x0d, 0x0f, 0xbb},
- {0x24, 0x80, 0x00, 0xbb},
- {0x59, 0x00, 0xff, 0xbb},
- {0xf0, 0x00, 0x02, 0xbb},
- {0x39, 0x03, 0x0d, 0xbb},
- {0x3a, 0x06, 0x1b, 0xbb},
- {0x3b, 0x00, 0x95, 0xbb},
- {0x3c, 0x04, 0xdb, 0xbb},
- {0x57, 0x02, 0x00, 0xbb},
- {0x58, 0x02, 0x66, 0xbb},
- {0x59, 0x00, 0xff, 0xbb},
- {0x5a, 0x01, 0x33, 0xbb},
- {0x5c, 0x12, 0x0d, 0xbb},
- {0x5d, 0x16, 0x11, 0xbb},
- {0x64, 0x5e, 0x1c, 0xbb},
- {}
-};
-static const u8 po3130_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
-};
-static const u8 po3130_matrix[9] = {
- 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
-};
-
-static const u8 po3130_initVGA_data[][4] = {
- {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
- {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
- {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
- {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc},
- {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
- {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
- {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
- {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
- {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
- {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
- {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
- {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
- {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
- {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
- {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
- {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
- {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
- {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
- {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
- {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
- {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
- {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
- {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
- {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
- {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
- {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
- {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
- {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
- {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
- {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
- {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
- {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
- {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
- {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
- {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
- {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
- {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
- {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
- {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
- {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
- {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
- {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa},
- {0x00, 0x4c, 0x07, 0xaa},
- {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
- {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
-/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
- {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */
- {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
- {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
- {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
- {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
- {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
- {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
- {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
- {}
-};
-static const u8 po3130_rundata[][4] = {
- {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa},
- {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa},
- {0x00, 0x44, 0x40, 0xaa},
-/* {0x00, 0xd5, 0x7c, 0xaa}, */
- {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa},
- {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa},
- {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa},
- {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa},
- {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa},
- {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa},
- {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
- {}
-};
-
-static const u8 po3130_initQVGA_data[][4] = {
- {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc},
- {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc},
- {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc},
- {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc},
- {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc},
- {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa},
- {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa},
- {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa},
- {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa},
- {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa},
- {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa},
- {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa},
- {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa},
- {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa},
- {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa},
- {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa},
- {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa},
- {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa},
- {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa},
- {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa},
- {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa},
- {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa},
- {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa},
- {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa},
- {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa},
- {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa},
- {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa},
- {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa},
- {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa},
- {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa},
- {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
- {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
- {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa},
- {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa},
- {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa},
- {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa},
- {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa},
- {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa},
- {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa},
- {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa},
- {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa},
- {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa},
- {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa},
- {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa},
- {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa},
- {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
- {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
- {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
- {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
- {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa},
- {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc},
- {}
-};
-
-static const u8 hv7131r_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
-};
-static const u8 hv7131r_matrix[9] = {
- 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63
-};
-static const u8 hv7131r_initVGA_data[][4] = {
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x20, 0xdd},
- {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x01, 0x45, 0xcc},
- {0xb3, 0x03, 0x0b, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x02, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */
- {0xb3, 0x00, 0x27, 0xcc},
- {0xbc, 0x00, 0x73, 0xcc},
- {0xb8, 0x00, 0x23, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc},
- {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc},
- {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc},
- {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc},
- {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x58, 0xcc},
- {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc},
- {0xb8, 0x37, 0x00, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x01, 0x7d, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0x00, 0x01, 0x0c, 0xaa},
- {0x00, 0x14, 0x01, 0xaa},
- {0x00, 0x15, 0xe6, 0xaa},
- {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x17, 0x86, 0xaa},
- {0x00, 0x23, 0x00, 0xaa},
- {0x00, 0x25, 0x03, 0xaa},
- {0x00, 0x26, 0xa9, 0xaa},
- {0x00, 0x27, 0x80, 0xaa},
- {0x00, 0x30, 0x18, 0xaa},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x02, 0xcc},
- {0xb6, 0x02, 0x80, 0xcc},
- {0xb6, 0x05, 0x01, 0xcc},
- {0xb6, 0x04, 0xe0, 0xcc},
- {0xb6, 0x12, 0x78, 0xcc},
- {0xb6, 0x18, 0x02, 0xcc},
- {0xb6, 0x17, 0x58, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x10, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x13, 0xcc},
- {0xb9, 0x12, 0x00, 0xcc},
- {0xb9, 0x13, 0x0a, 0xcc},
- {0xb9, 0x14, 0x0a, 0xcc},
- {0xb9, 0x15, 0x0a, 0xcc},
- {0xb9, 0x16, 0x0a, 0xcc},
- {0xb8, 0x0c, 0x20, 0xcc},
- {0xb8, 0x0d, 0x70, 0xcc},
- {0xb9, 0x18, 0x00, 0xcc},
- {0xb9, 0x19, 0x0f, 0xcc},
- {0xb9, 0x1a, 0x0f, 0xcc},
- {0xb9, 0x1b, 0x0f, 0xcc},
- {0xb9, 0x1c, 0x0f, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-
-static const u8 hv7131r_initQVGA_data[][4] = {
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb0, 0x03, 0x19, 0xcc},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x20, 0xdd},
- {0xb3, 0x00, 0x24, 0xcc},
- {0xb3, 0x00, 0x25, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x01, 0x45, 0xcc},
- {0xb3, 0x03, 0x0b, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x02, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0x91, 0xcc},
- {0xb3, 0x00, 0x27, 0xcc},
- {0xbc, 0x00, 0xd3, 0xcc},
- {0xb8, 0x00, 0x23, 0xcc},
- {0xb8, 0x2c, 0x50, 0xcc},
- {0xb8, 0x2d, 0xf8, 0xcc},
- {0xb8, 0x2e, 0xf8, 0xcc},
- {0xb8, 0x2f, 0xf8, 0xcc},
- {0xb8, 0x30, 0x50, 0xcc},
- {0xb8, 0x31, 0xf8, 0xcc},
- {0xb8, 0x32, 0xf8, 0xcc},
- {0xb8, 0x33, 0xf8, 0xcc},
- {0xb8, 0x34, 0x58, 0xcc},
- {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc},
- {0xb8, 0x37, 0x00, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x01, 0x7d, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc},
- {0x00, 0x01, 0x0c, 0xaa},
- {0x00, 0x14, 0x01, 0xaa},
- {0x00, 0x15, 0xe6, 0xaa},
- {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x17, 0x86, 0xaa},
- {0x00, 0x23, 0x00, 0xaa},
- {0x00, 0x25, 0x03, 0xaa},
- {0x00, 0x26, 0xa9, 0xaa},
- {0x00, 0x27, 0x80, 0xaa},
- {0x00, 0x30, 0x18, 0xaa},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x01, 0xcc},
- {0xb6, 0x02, 0x40, 0xcc},
- {0xb6, 0x05, 0x00, 0xcc},
- {0xb6, 0x04, 0xf0, 0xcc},
- {0xb6, 0x12, 0x78, 0xcc},
- {0xb6, 0x18, 0x00, 0xcc},
- {0xb6, 0x17, 0x96, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x10, 0xcc},
- {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
- {0xb9, 0x12, 0x00, 0xcc},
- {0xb9, 0x13, 0x0a, 0xcc},
- {0xb9, 0x14, 0x0a, 0xcc},
- {0xb9, 0x15, 0x0a, 0xcc},
- {0xb9, 0x16, 0x0a, 0xcc},
- {0xb9, 0x18, 0x00, 0xcc},
- {0xb9, 0x19, 0x0f, 0xcc},
- {0xb8, 0x0c, 0x20, 0xcc},
- {0xb8, 0x0d, 0x70, 0xcc},
- {0xb9, 0x1a, 0x0f, 0xcc},
- {0xb9, 0x1b, 0x0f, 0xcc},
- {0xb9, 0x1c, 0x0f, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x13, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {}
-};
-
-static const u8 ov7660_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
-};
-static const u8 ov7660_matrix[9] = {
- 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62
-};
-static const u8 ov7660_initVGA_data[][4] = {
- {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
- {0x00, 0x00, 0x50, 0xdd},
- {0xb0, 0x03, 0x01, 0xcc},
- {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
- {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
- {0xb3, 0x1f, 0x02, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
- {0xb3, 0x00, 0x26, 0xcc},
- {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
- {0xb8, 0x01, 0x7d, 0xcc},
- {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc},
- {0xb8, 0x8f, 0x50, 0xcc},
- {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
- {0x00, 0x12, 0x80, 0xaa},
- {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
- {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
- {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
- {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
- {0x00, 0x13, 0xa7, 0xaa},
- {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
- {0x00, 0x36, 0x00, 0xaa},
- {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
- {0x00, 0x39, 0x43, 0xaa},
- {0x00, 0x8d, 0xcf, 0xaa},
- {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
- {0x00, 0x0f, 0x62, 0xaa},
- {0x00, 0x35, 0x84, 0xaa},
- {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
- {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
- {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
- {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
- {0x00, 0x01, 0x80, 0xaa},
- {0x00, 0x02, 0x80, 0xaa},
- {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc},
- {0xb9, 0x00, 0x28, 0xcc},
- {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc},
- {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc},
- {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc},
- {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc},
-
- {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc},
-
- {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc},
- {}
-};
-static const u8 ov7660_initQVGA_data[][4] = {
- {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc},
- {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc},
- {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */
- {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
- {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
- {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
- {0xb8, 0x01, 0x7d, 0xcc},
-/* sizer */
- {0xbc, 0x00, 0xd3, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
- {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
- {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
- {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
- {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
- {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
- {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
- {0x00, 0x13, 0xa7, 0xaa},
- {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa},
- {0x00, 0x36, 0x00, 0xaa},
- {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa},
- {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa},
- {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa},
- {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa},
- {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */
- {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/
- {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */
- {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc},
- {0x00, 0x01, 0x80, 0xaa},
- {0x00, 0x02, 0x80, 0xaa},
-/* sizer filters */
- {0xbc, 0x02, 0x08, 0xcc},
- {0xbc, 0x03, 0x70, 0xcc},
- {0xb8, 0x35, 0x00, 0xcc},
- {0xb8, 0x36, 0x00, 0xcc},
- {0xb8, 0x37, 0x00, 0xcc},
- {0xbc, 0x04, 0x08, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x3c, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x04, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
-/* */
- {0xb8, 0xfe, 0x00, 0xcc},
- {0xb8, 0xff, 0x28, 0xcc},
-/* */
- {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc},
- {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc},
- {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc},
- {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc},
- {0xb9, 0x08, 0x3c, 0xcc},
-/* */
- {0xb8, 0x8e, 0x00, 0xcc},
- {0xb8, 0x8f, 0xff, 0xcc}, /* ff */
- {0x00, 0x29, 0x3c, 0xaa},
- {0xb3, 0x01, 0x45, 0xcc}, /* 45 */
- {}
-};
-
-static const u8 ov7660_50HZ[][4] = {
- {0x00, 0x3b, 0x08, 0xaa},
- {0x00, 0x9d, 0x40, 0xaa},
- {0x00, 0x13, 0xa7, 0xaa},
- {}
-};
-
-static const u8 ov7660_60HZ[][4] = {
- {0x00, 0x3b, 0x00, 0xaa},
- {0x00, 0x9e, 0x40, 0xaa},
- {0x00, 0x13, 0xa7, 0xaa},
- {}
-};
-
-static const u8 ov7660_NoFliker[][4] = {
- {0x00, 0x13, 0x87, 0xaa},
- {}
-};
-
-static const u8 ov7670_InitVGA[][4] = {
- {0xb3, 0x01, 0x05, 0xcc},
- {0x00, 0x00, 0x30, 0xdd},
- {0xb0, 0x03, 0x19, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb3, 0x00, 0x66, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb0, 0x16, 0x01, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xb3, 0x03, 0x1f, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xbc, 0x00, 0x41, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0x00, 0x12, 0x80, 0xaa},
- {0x00, 0x00, 0x20, 0xdd},
- {0x00, 0x12, 0x00, 0xaa},
- {0x00, 0x11, 0x40, 0xaa},
- {0x00, 0x6b, 0x0a, 0xaa},
- {0x00, 0x3a, 0x04, 0xaa},
- {0x00, 0x40, 0xc0, 0xaa},
- {0x00, 0x8c, 0x00, 0xaa},
- {0x00, 0x7a, 0x29, 0xaa},
- {0x00, 0x7b, 0x0e, 0xaa},
- {0x00, 0x7c, 0x1a, 0xaa},
- {0x00, 0x7d, 0x31, 0xaa},
- {0x00, 0x7e, 0x53, 0xaa},
- {0x00, 0x7f, 0x60, 0xaa},
- {0x00, 0x80, 0x6b, 0xaa},
- {0x00, 0x81, 0x73, 0xaa},
- {0x00, 0x82, 0x7b, 0xaa},
- {0x00, 0x83, 0x82, 0xaa},
- {0x00, 0x84, 0x89, 0xaa},
- {0x00, 0x85, 0x96, 0xaa},
- {0x00, 0x86, 0xa1, 0xaa},
- {0x00, 0x87, 0xb7, 0xaa},
- {0x00, 0x88, 0xcc, 0xaa},
- {0x00, 0x89, 0xe1, 0xaa},
- {0x00, 0x13, 0xe0, 0xaa},
- {0x00, 0x00, 0x00, 0xaa},
- {0x00, 0x10, 0x00, 0xaa},
- {0x00, 0x0d, 0x40, 0xaa},
- {0x00, 0x14, 0x28, 0xaa},
- {0x00, 0xa5, 0x05, 0xaa},
- {0x00, 0xab, 0x07, 0xaa},
- {0x00, 0x24, 0x95, 0xaa},
- {0x00, 0x25, 0x33, 0xaa},
- {0x00, 0x26, 0xe3, 0xaa},
- {0x00, 0x9f, 0x88, 0xaa},
- {0x00, 0xa0, 0x78, 0xaa},
- {0x00, 0x55, 0x90, 0xaa},
- {0x00, 0xa1, 0x03, 0xaa},
- {0x00, 0xa6, 0xe0, 0xaa},
- {0x00, 0xa7, 0xd8, 0xaa},
- {0x00, 0xa8, 0xf0, 0xaa},
- {0x00, 0xa9, 0x90, 0xaa},
- {0x00, 0xaa, 0x14, 0xaa},
- {0x00, 0x13, 0xe5, 0xaa},
- {0x00, 0x0e, 0x61, 0xaa},
- {0x00, 0x0f, 0x4b, 0xaa},
- {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
- {0x00, 0x21, 0x02, 0xaa},
- {0x00, 0x22, 0x91, 0xaa},
- {0x00, 0x29, 0x07, 0xaa},
- {0x00, 0x33, 0x0b, 0xaa},
- {0x00, 0x35, 0x0b, 0xaa},
- {0x00, 0x37, 0x1d, 0xaa},
- {0x00, 0x38, 0x71, 0xaa},
- {0x00, 0x39, 0x2a, 0xaa},
- {0x00, 0x3c, 0x78, 0xaa},
- {0x00, 0x4d, 0x40, 0xaa},
- {0x00, 0x4e, 0x20, 0xaa},
- {0x00, 0x74, 0x19, 0xaa},
- {0x00, 0x8d, 0x4f, 0xaa},
- {0x00, 0x8e, 0x00, 0xaa},
- {0x00, 0x8f, 0x00, 0xaa},
- {0x00, 0x90, 0x00, 0xaa},
- {0x00, 0x91, 0x00, 0xaa},
- {0x00, 0x96, 0x00, 0xaa},
- {0x00, 0x9a, 0x80, 0xaa},
- {0x00, 0xb0, 0x84, 0xaa},
- {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0xb2, 0x0e, 0xaa},
- {0x00, 0xb3, 0x82, 0xaa},
- {0x00, 0xb8, 0x0a, 0xaa},
- {0x00, 0x43, 0x14, 0xaa},
- {0x00, 0x44, 0xf0, 0xaa},
- {0x00, 0x45, 0x45, 0xaa},
- {0x00, 0x46, 0x63, 0xaa},
- {0x00, 0x47, 0x2d, 0xaa},
- {0x00, 0x48, 0x46, 0xaa},
- {0x00, 0x59, 0x88, 0xaa},
- {0x00, 0x5a, 0xa0, 0xaa},
- {0x00, 0x5b, 0xc6, 0xaa},
- {0x00, 0x5c, 0x7d, 0xaa},
- {0x00, 0x5d, 0x5f, 0xaa},
- {0x00, 0x5e, 0x19, 0xaa},
- {0x00, 0x6c, 0x0a, 0xaa},
- {0x00, 0x6d, 0x55, 0xaa},
- {0x00, 0x6e, 0x11, 0xaa},
- {0x00, 0x6f, 0x9e, 0xaa},
- {0x00, 0x69, 0x00, 0xaa},
- {0x00, 0x6a, 0x40, 0xaa},
- {0x00, 0x01, 0x40, 0xaa},
- {0x00, 0x02, 0x40, 0xaa},
- {0x00, 0x13, 0xe7, 0xaa},
- {0x00, 0x5f, 0xf0, 0xaa},
- {0x00, 0x60, 0xf0, 0xaa},
- {0x00, 0x61, 0xf0, 0xaa},
- {0x00, 0x27, 0xa0, 0xaa},
- {0x00, 0x28, 0x80, 0xaa},
- {0x00, 0x2c, 0x90, 0xaa},
- {0x00, 0x4f, 0x66, 0xaa},
- {0x00, 0x50, 0x66, 0xaa},
- {0x00, 0x51, 0x00, 0xaa},
- {0x00, 0x52, 0x22, 0xaa},
- {0x00, 0x53, 0x5e, 0xaa},
- {0x00, 0x54, 0x80, 0xaa},
- {0x00, 0x58, 0x9e, 0xaa},
- {0x00, 0x41, 0x08, 0xaa},
- {0x00, 0x3f, 0x00, 0xaa},
- {0x00, 0x75, 0x85, 0xaa},
- {0x00, 0x76, 0xe1, 0xaa},
- {0x00, 0x4c, 0x00, 0xaa},
- {0x00, 0x77, 0x0a, 0xaa},
- {0x00, 0x3d, 0x88, 0xaa},
- {0x00, 0x4b, 0x09, 0xaa},
- {0x00, 0xc9, 0x60, 0xaa},
- {0x00, 0x41, 0x38, 0xaa},
- {0x00, 0x62, 0x30, 0xaa},
- {0x00, 0x63, 0x30, 0xaa},
- {0x00, 0x64, 0x08, 0xaa},
- {0x00, 0x94, 0x07, 0xaa},
- {0x00, 0x95, 0x0b, 0xaa},
- {0x00, 0x65, 0x00, 0xaa},
- {0x00, 0x66, 0x05, 0xaa},
- {0x00, 0x56, 0x50, 0xaa},
- {0x00, 0x34, 0x11, 0xaa},
- {0x00, 0xa4, 0x88, 0xaa},
- {0x00, 0x96, 0x00, 0xaa},
- {0x00, 0x97, 0x30, 0xaa},
- {0x00, 0x98, 0x20, 0xaa},
- {0x00, 0x99, 0x30, 0xaa},
- {0x00, 0x9a, 0x84, 0xaa},
- {0x00, 0x9b, 0x29, 0xaa},
- {0x00, 0x9c, 0x03, 0xaa},
- {0x00, 0x78, 0x04, 0xaa},
- {0x00, 0x79, 0x01, 0xaa},
- {0x00, 0xc8, 0xf0, 0xaa},
- {0x00, 0x79, 0x0f, 0xaa},
- {0x00, 0xc8, 0x00, 0xaa},
- {0x00, 0x79, 0x10, 0xaa},
- {0x00, 0xc8, 0x7e, 0xaa},
- {0x00, 0x79, 0x0a, 0xaa},
- {0x00, 0xc8, 0x80, 0xaa},
- {0x00, 0x79, 0x0b, 0xaa},
- {0x00, 0xc8, 0x01, 0xaa},
- {0x00, 0x79, 0x0c, 0xaa},
- {0x00, 0xc8, 0x0f, 0xaa},
- {0x00, 0x79, 0x0d, 0xaa},
- {0x00, 0xc8, 0x20, 0xaa},
- {0x00, 0x79, 0x09, 0xaa},
- {0x00, 0xc8, 0x80, 0xaa},
- {0x00, 0x79, 0x02, 0xaa},
- {0x00, 0xc8, 0xc0, 0xaa},
- {0x00, 0x79, 0x03, 0xaa},
- {0x00, 0xc8, 0x40, 0xaa},
- {0x00, 0x79, 0x05, 0xaa},
- {0x00, 0xc8, 0x30, 0xaa},
- {0x00, 0x79, 0x26, 0xaa},
- {0x00, 0x11, 0x40, 0xaa},
- {0x00, 0x3a, 0x04, 0xaa},
- {0x00, 0x12, 0x00, 0xaa},
- {0x00, 0x40, 0xc0, 0xaa},
- {0x00, 0x8c, 0x00, 0xaa},
- {0x00, 0x17, 0x14, 0xaa},
- {0x00, 0x18, 0x02, 0xaa},
- {0x00, 0x32, 0x92, 0xaa},
- {0x00, 0x19, 0x02, 0xaa},
- {0x00, 0x1a, 0x7a, 0xaa},
- {0x00, 0x03, 0x0a, 0xaa},
- {0x00, 0x0c, 0x00, 0xaa},
- {0x00, 0x3e, 0x00, 0xaa},
- {0x00, 0x70, 0x3a, 0xaa},
- {0x00, 0x71, 0x35, 0xaa},
- {0x00, 0x72, 0x11, 0xaa},
- {0x00, 0x73, 0xf0, 0xaa},
- {0x00, 0xa2, 0x02, 0xaa},
- {0x00, 0xb1, 0x00, 0xaa},
- {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
- {0x00, 0xaa, 0x14, 0xaa},
- {0x00, 0x24, 0x80, 0xaa},
- {0x00, 0x25, 0x74, 0xaa},
- {0x00, 0x26, 0xd3, 0xaa},
- {0x00, 0x0d, 0x00, 0xaa},
- {0x00, 0x14, 0x18, 0xaa},
- {0x00, 0x9d, 0x99, 0xaa},
- {0x00, 0x9e, 0x7f, 0xaa},
- {0x00, 0x64, 0x08, 0xaa},
- {0x00, 0x94, 0x07, 0xaa},
- {0x00, 0x95, 0x06, 0xaa},
- {0x00, 0x66, 0x05, 0xaa},
- {0x00, 0x41, 0x08, 0xaa},
- {0x00, 0x3f, 0x00, 0xaa},
- {0x00, 0x75, 0x07, 0xaa},
- {0x00, 0x76, 0xe1, 0xaa},
- {0x00, 0x4c, 0x00, 0xaa},
- {0x00, 0x77, 0x00, 0xaa},
- {0x00, 0x3d, 0xc2, 0xaa},
- {0x00, 0x4b, 0x09, 0xaa},
- {0x00, 0xc9, 0x60, 0xaa},
- {0x00, 0x41, 0x38, 0xaa},
- {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xb3, 0x01, 0x45, 0xcc},
- {0x00, 0x77, 0x05, 0xaa},
- {},
-};
-
-static const u8 ov7670_InitQVGA[][4] = {
- {0xb3, 0x01, 0x05, 0xcc},
- {0x00, 0x00, 0x30, 0xdd},
- {0xb0, 0x03, 0x19, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb3, 0x00, 0x66, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb0, 0x16, 0x01, 0xcc},
- {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x02, 0x02, 0xcc},
- {0xb3, 0x03, 0x1f, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc},
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xbc, 0x00, 0xd1, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0x00, 0x12, 0x80, 0xaa},
- {0x00, 0x00, 0x20, 0xdd},
- {0x00, 0x12, 0x00, 0xaa},
- {0x00, 0x11, 0x40, 0xaa},
- {0x00, 0x6b, 0x0a, 0xaa},
- {0x00, 0x3a, 0x04, 0xaa},
- {0x00, 0x40, 0xc0, 0xaa},
- {0x00, 0x8c, 0x00, 0xaa},
- {0x00, 0x7a, 0x29, 0xaa},
- {0x00, 0x7b, 0x0e, 0xaa},
- {0x00, 0x7c, 0x1a, 0xaa},
- {0x00, 0x7d, 0x31, 0xaa},
- {0x00, 0x7e, 0x53, 0xaa},
- {0x00, 0x7f, 0x60, 0xaa},
- {0x00, 0x80, 0x6b, 0xaa},
- {0x00, 0x81, 0x73, 0xaa},
- {0x00, 0x82, 0x7b, 0xaa},
- {0x00, 0x83, 0x82, 0xaa},
- {0x00, 0x84, 0x89, 0xaa},
- {0x00, 0x85, 0x96, 0xaa},
- {0x00, 0x86, 0xa1, 0xaa},
- {0x00, 0x87, 0xb7, 0xaa},
- {0x00, 0x88, 0xcc, 0xaa},
- {0x00, 0x89, 0xe1, 0xaa},
- {0x00, 0x13, 0xe0, 0xaa},
- {0x00, 0x00, 0x00, 0xaa},
- {0x00, 0x10, 0x00, 0xaa},
- {0x00, 0x0d, 0x40, 0xaa},
- {0x00, 0x14, 0x28, 0xaa},
- {0x00, 0xa5, 0x05, 0xaa},
- {0x00, 0xab, 0x07, 0xaa},
- {0x00, 0x24, 0x95, 0xaa},
- {0x00, 0x25, 0x33, 0xaa},
- {0x00, 0x26, 0xe3, 0xaa},
- {0x00, 0x9f, 0x88, 0xaa},
- {0x00, 0xa0, 0x78, 0xaa},
- {0x00, 0x55, 0x90, 0xaa},
- {0x00, 0xa1, 0x03, 0xaa},
- {0x00, 0xa6, 0xe0, 0xaa},
- {0x00, 0xa7, 0xd8, 0xaa},
- {0x00, 0xa8, 0xf0, 0xaa},
- {0x00, 0xa9, 0x90, 0xaa},
- {0x00, 0xaa, 0x14, 0xaa},
- {0x00, 0x13, 0xe5, 0xaa},
- {0x00, 0x0e, 0x61, 0xaa},
- {0x00, 0x0f, 0x4b, 0xaa},
- {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
- {0x00, 0x21, 0x02, 0xaa},
- {0x00, 0x22, 0x91, 0xaa},
- {0x00, 0x29, 0x07, 0xaa},
- {0x00, 0x33, 0x0b, 0xaa},
- {0x00, 0x35, 0x0b, 0xaa},
- {0x00, 0x37, 0x1d, 0xaa},
- {0x00, 0x38, 0x71, 0xaa},
- {0x00, 0x39, 0x2a, 0xaa},
- {0x00, 0x3c, 0x78, 0xaa},
- {0x00, 0x4d, 0x40, 0xaa},
- {0x00, 0x4e, 0x20, 0xaa},
- {0x00, 0x74, 0x19, 0xaa},
- {0x00, 0x8d, 0x4f, 0xaa},
- {0x00, 0x8e, 0x00, 0xaa},
- {0x00, 0x8f, 0x00, 0xaa},
- {0x00, 0x90, 0x00, 0xaa},
- {0x00, 0x91, 0x00, 0xaa},
- {0x00, 0x96, 0x00, 0xaa},
- {0x00, 0x9a, 0x80, 0xaa},
- {0x00, 0xb0, 0x84, 0xaa},
- {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0xb2, 0x0e, 0xaa},
- {0x00, 0xb3, 0x82, 0xaa},
- {0x00, 0xb8, 0x0a, 0xaa},
- {0x00, 0x43, 0x14, 0xaa},
- {0x00, 0x44, 0xf0, 0xaa},
- {0x00, 0x45, 0x45, 0xaa},
- {0x00, 0x46, 0x63, 0xaa},
- {0x00, 0x47, 0x2d, 0xaa},
- {0x00, 0x48, 0x46, 0xaa},
- {0x00, 0x59, 0x88, 0xaa},
- {0x00, 0x5a, 0xa0, 0xaa},
- {0x00, 0x5b, 0xc6, 0xaa},
- {0x00, 0x5c, 0x7d, 0xaa},
- {0x00, 0x5d, 0x5f, 0xaa},
- {0x00, 0x5e, 0x19, 0xaa},
- {0x00, 0x6c, 0x0a, 0xaa},
- {0x00, 0x6d, 0x55, 0xaa},
- {0x00, 0x6e, 0x11, 0xaa},
- {0x00, 0x6f, 0x9e, 0xaa},
- {0x00, 0x69, 0x00, 0xaa},
- {0x00, 0x6a, 0x40, 0xaa},
- {0x00, 0x01, 0x40, 0xaa},
- {0x00, 0x02, 0x40, 0xaa},
- {0x00, 0x13, 0xe7, 0xaa},
- {0x00, 0x5f, 0xf0, 0xaa},
- {0x00, 0x60, 0xf0, 0xaa},
- {0x00, 0x61, 0xf0, 0xaa},
- {0x00, 0x27, 0xa0, 0xaa},
- {0x00, 0x28, 0x80, 0xaa},
- {0x00, 0x2c, 0x90, 0xaa},
- {0x00, 0x4f, 0x66, 0xaa},
- {0x00, 0x50, 0x66, 0xaa},
- {0x00, 0x51, 0x00, 0xaa},
- {0x00, 0x52, 0x22, 0xaa},
- {0x00, 0x53, 0x5e, 0xaa},
- {0x00, 0x54, 0x80, 0xaa},
- {0x00, 0x58, 0x9e, 0xaa},
- {0x00, 0x41, 0x08, 0xaa},
- {0x00, 0x3f, 0x00, 0xaa},
- {0x00, 0x75, 0x85, 0xaa},
- {0x00, 0x76, 0xe1, 0xaa},
- {0x00, 0x4c, 0x00, 0xaa},
- {0x00, 0x77, 0x0a, 0xaa},
- {0x00, 0x3d, 0x88, 0xaa},
- {0x00, 0x4b, 0x09, 0xaa},
- {0x00, 0xc9, 0x60, 0xaa},
- {0x00, 0x41, 0x38, 0xaa},
- {0x00, 0x62, 0x30, 0xaa},
- {0x00, 0x63, 0x30, 0xaa},
- {0x00, 0x64, 0x08, 0xaa},
- {0x00, 0x94, 0x07, 0xaa},
- {0x00, 0x95, 0x0b, 0xaa},
- {0x00, 0x65, 0x00, 0xaa},
- {0x00, 0x66, 0x05, 0xaa},
- {0x00, 0x56, 0x50, 0xaa},
- {0x00, 0x34, 0x11, 0xaa},
- {0x00, 0xa4, 0x88, 0xaa},
- {0x00, 0x96, 0x00, 0xaa},
- {0x00, 0x97, 0x30, 0xaa},
- {0x00, 0x98, 0x20, 0xaa},
- {0x00, 0x99, 0x30, 0xaa},
- {0x00, 0x9a, 0x84, 0xaa},
- {0x00, 0x9b, 0x29, 0xaa},
- {0x00, 0x9c, 0x03, 0xaa},
- {0x00, 0x78, 0x04, 0xaa},
- {0x00, 0x79, 0x01, 0xaa},
- {0x00, 0xc8, 0xf0, 0xaa},
- {0x00, 0x79, 0x0f, 0xaa},
- {0x00, 0xc8, 0x00, 0xaa},
- {0x00, 0x79, 0x10, 0xaa},
- {0x00, 0xc8, 0x7e, 0xaa},
- {0x00, 0x79, 0x0a, 0xaa},
- {0x00, 0xc8, 0x80, 0xaa},
- {0x00, 0x79, 0x0b, 0xaa},
- {0x00, 0xc8, 0x01, 0xaa},
- {0x00, 0x79, 0x0c, 0xaa},
- {0x00, 0xc8, 0x0f, 0xaa},
- {0x00, 0x79, 0x0d, 0xaa},
- {0x00, 0xc8, 0x20, 0xaa},
- {0x00, 0x79, 0x09, 0xaa},
- {0x00, 0xc8, 0x80, 0xaa},
- {0x00, 0x79, 0x02, 0xaa},
- {0x00, 0xc8, 0xc0, 0xaa},
- {0x00, 0x79, 0x03, 0xaa},
- {0x00, 0xc8, 0x40, 0xaa},
- {0x00, 0x79, 0x05, 0xaa},
- {0x00, 0xc8, 0x30, 0xaa},
- {0x00, 0x79, 0x26, 0xaa},
- {0x00, 0x11, 0x40, 0xaa},
- {0x00, 0x3a, 0x04, 0xaa},
- {0x00, 0x12, 0x00, 0xaa},
- {0x00, 0x40, 0xc0, 0xaa},
- {0x00, 0x8c, 0x00, 0xaa},
- {0x00, 0x17, 0x14, 0xaa},
- {0x00, 0x18, 0x02, 0xaa},
- {0x00, 0x32, 0x92, 0xaa},
- {0x00, 0x19, 0x02, 0xaa},
- {0x00, 0x1a, 0x7a, 0xaa},
- {0x00, 0x03, 0x0a, 0xaa},
- {0x00, 0x0c, 0x00, 0xaa},
- {0x00, 0x3e, 0x00, 0xaa},
- {0x00, 0x70, 0x3a, 0xaa},
- {0x00, 0x71, 0x35, 0xaa},
- {0x00, 0x72, 0x11, 0xaa},
- {0x00, 0x73, 0xf0, 0xaa},
- {0x00, 0xa2, 0x02, 0xaa},
- {0x00, 0xb1, 0x00, 0xaa},
- {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
- {0x00, 0xaa, 0x14, 0xaa},
- {0x00, 0x24, 0x80, 0xaa},
- {0x00, 0x25, 0x74, 0xaa},
- {0x00, 0x26, 0xd3, 0xaa},
- {0x00, 0x0d, 0x00, 0xaa},
- {0x00, 0x14, 0x18, 0xaa},
- {0x00, 0x9d, 0x99, 0xaa},
- {0x00, 0x9e, 0x7f, 0xaa},
- {0x00, 0x64, 0x08, 0xaa},
- {0x00, 0x94, 0x07, 0xaa},
- {0x00, 0x95, 0x06, 0xaa},
- {0x00, 0x66, 0x05, 0xaa},
- {0x00, 0x41, 0x08, 0xaa},
- {0x00, 0x3f, 0x00, 0xaa},
- {0x00, 0x75, 0x07, 0xaa},
- {0x00, 0x76, 0xe1, 0xaa},
- {0x00, 0x4c, 0x00, 0xaa},
- {0x00, 0x77, 0x00, 0xaa},
- {0x00, 0x3d, 0xc2, 0xaa},
- {0x00, 0x4b, 0x09, 0xaa},
- {0x00, 0xc9, 0x60, 0xaa},
- {0x00, 0x41, 0x38, 0xaa},
- {0xbc, 0x02, 0x18, 0xcc},
- {0xbc, 0x03, 0x50, 0xcc},
- {0xbc, 0x04, 0x18, 0xcc},
- {0xbc, 0x05, 0x00, 0xcc},
- {0xbc, 0x06, 0x00, 0xcc},
- {0xbc, 0x08, 0x30, 0xcc},
- {0xbc, 0x09, 0x40, 0xcc},
- {0xbc, 0x0a, 0x10, 0xcc},
- {0xbc, 0x0b, 0x00, 0xcc},
- {0xbc, 0x0c, 0x00, 0xcc},
- {0xbf, 0xc0, 0x26, 0xcc},
- {0xbf, 0xc1, 0x02, 0xcc},
- {0xbf, 0xcc, 0x04, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xb3, 0x01, 0x45, 0xcc},
- {0x00, 0x77, 0x05, 0xaa},
- {},
-};
-
-/* PO1200 - values from usbvm326.inf and ms-win trace */
-static const u8 po1200_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
-};
-static const u8 po1200_matrix[9] = {
- 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
-};
-static const u8 po1200_initVGA_data[][4] = {
- {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
- {0xb0, 0x03, 0x19, 0xcc},
-/* {0x00, 0x00, 0x33, 0xdd}, */
- {0xb0, 0x04, 0x02, 0xcc},
- {0xb0, 0x02, 0x02, 0xcc},
- {0xb3, 0x5d, 0x00, 0xcc},
- {0xb3, 0x01, 0x01, 0xcc},
- {0xb3, 0x00, 0x64, 0xcc},
- {0xb3, 0x00, 0x65, 0xcc},
- {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x02, 0xb2, 0xcc},
- {0xb3, 0x03, 0x18, 0xcc},
- {0xb3, 0x04, 0x15, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x02, 0xcc},
- {0xb3, 0x23, 0x58, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x03, 0xcc},
- {0xb3, 0x17, 0x1f, 0xcc},
- {0xbc, 0x00, 0x71, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xb0, 0x54, 0x13, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x13, 0x02, 0xaa},
- {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
- {0x00, 0x21, 0x00, 0xaa},
- {0x00, 0x25, 0x02, 0xaa},
- {0x00, 0x3c, 0x4f, 0xaa},
- {0x00, 0x3f, 0xe0, 0xaa},
- {0x00, 0x42, 0xff, 0xaa},
- {0x00, 0x45, 0x34, 0xaa},
- {0x00, 0x55, 0xfe, 0xaa},
- {0x00, 0x59, 0xd3, 0xaa},
- {0x00, 0x5e, 0x04, 0xaa},
- {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
- {0x00, 0x62, 0x02, 0xaa},
- {0x00, 0xa7, 0x31, 0xaa},
- {0x00, 0xa9, 0x66, 0xaa},
- {0x00, 0xb0, 0x00, 0xaa},
- {0x00, 0xb1, 0x00, 0xaa},
- {0x00, 0xb3, 0x11, 0xaa},
- {0x00, 0xb6, 0x26, 0xaa},
- {0x00, 0xb7, 0x20, 0xaa},
- {0x00, 0xba, 0x04, 0xaa},
- {0x00, 0x88, 0x42, 0xaa},
- {0x00, 0x89, 0x9a, 0xaa},
- {0x00, 0x8a, 0x88, 0xaa},
- {0x00, 0x8b, 0x8e, 0xaa},
- {0x00, 0x8c, 0x3e, 0xaa},
- {0x00, 0x8d, 0x90, 0xaa},
- {0x00, 0x8e, 0x87, 0xaa},
- {0x00, 0x8f, 0x96, 0xaa},
- {0x00, 0x90, 0x3d, 0xaa},
- {0x00, 0x64, 0x00, 0xaa},
- {0x00, 0x65, 0x10, 0xaa},
- {0x00, 0x66, 0x20, 0xaa},
- {0x00, 0x67, 0x2b, 0xaa},
- {0x00, 0x68, 0x36, 0xaa},
- {0x00, 0x69, 0x49, 0xaa},
- {0x00, 0x6a, 0x5a, 0xaa},
- {0x00, 0x6b, 0x7f, 0xaa},
- {0x00, 0x6c, 0x9b, 0xaa},
- {0x00, 0x6d, 0xba, 0xaa},
- {0x00, 0x6e, 0xd4, 0xaa},
- {0x00, 0x6f, 0xea, 0xaa},
- {0x00, 0x70, 0x00, 0xaa},
- {0x00, 0x71, 0x10, 0xaa},
- {0x00, 0x72, 0x20, 0xaa},
- {0x00, 0x73, 0x2b, 0xaa},
- {0x00, 0x74, 0x36, 0xaa},
- {0x00, 0x75, 0x49, 0xaa},
- {0x00, 0x76, 0x5a, 0xaa},
- {0x00, 0x77, 0x7f, 0xaa},
- {0x00, 0x78, 0x9b, 0xaa},
- {0x00, 0x79, 0xba, 0xaa},
- {0x00, 0x7a, 0xd4, 0xaa},
- {0x00, 0x7b, 0xea, 0xaa},
- {0x00, 0x7c, 0x00, 0xaa},
- {0x00, 0x7d, 0x10, 0xaa},
- {0x00, 0x7e, 0x20, 0xaa},
- {0x00, 0x7f, 0x2b, 0xaa},
- {0x00, 0x80, 0x36, 0xaa},
- {0x00, 0x81, 0x49, 0xaa},
- {0x00, 0x82, 0x5a, 0xaa},
- {0x00, 0x83, 0x7f, 0xaa},
- {0x00, 0x84, 0x9b, 0xaa},
- {0x00, 0x85, 0xba, 0xaa},
- {0x00, 0x86, 0xd4, 0xaa},
- {0x00, 0x87, 0xea, 0xaa},
- {0x00, 0x57, 0x2a, 0xaa},
- {0x00, 0x03, 0x01, 0xaa},
- {0x00, 0x04, 0x10, 0xaa},
- {0x00, 0x05, 0x10, 0xaa},
- {0x00, 0x06, 0x10, 0xaa},
- {0x00, 0x07, 0x10, 0xaa},
- {0x00, 0x08, 0x13, 0xaa},
- {0x00, 0x0a, 0x00, 0xaa},
- {0x00, 0x0b, 0x10, 0xaa},
- {0x00, 0x0c, 0x20, 0xaa},
- {0x00, 0x0d, 0x18, 0xaa},
- {0x00, 0x22, 0x01, 0xaa},
- {0x00, 0x23, 0x60, 0xaa},
- {0x00, 0x25, 0x08, 0xaa},
- {0x00, 0x26, 0x82, 0xaa},
- {0x00, 0x2e, 0x0f, 0xaa},
- {0x00, 0x2f, 0x1e, 0xaa},
- {0x00, 0x30, 0x2d, 0xaa},
- {0x00, 0x31, 0x3c, 0xaa},
- {0x00, 0x32, 0x4b, 0xaa},
- {0x00, 0x33, 0x5a, 0xaa},
- {0x00, 0x34, 0x69, 0xaa},
- {0x00, 0x35, 0x78, 0xaa},
- {0x00, 0x36, 0x87, 0xaa},
- {0x00, 0x37, 0x96, 0xaa},
- {0x00, 0x38, 0xa5, 0xaa},
- {0x00, 0x39, 0xb4, 0xaa},
- {0x00, 0x3a, 0xc3, 0xaa},
- {0x00, 0x3b, 0xd2, 0xaa},
- {0x00, 0x3c, 0xe1, 0xaa},
- {0x00, 0x3e, 0xff, 0xaa},
- {0x00, 0x3f, 0xff, 0xaa},
- {0x00, 0x40, 0xff, 0xaa},
- {0x00, 0x41, 0xff, 0xaa},
- {0x00, 0x42, 0xff, 0xaa},
- {0x00, 0x43, 0xff, 0xaa},
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x20, 0xc4, 0xaa},
- {0x00, 0x13, 0x03, 0xaa},
- {0x00, 0x3c, 0x50, 0xaa},
- {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
- {0x00, 0x51, 0x5b, 0xaa},
- {0x00, 0x52, 0x91, 0xaa},
- {0x00, 0x53, 0x4c, 0xaa},
- {0x00, 0x54, 0x50, 0xaa},
- {0x00, 0x56, 0x02, 0xaa},
- {0xb6, 0x00, 0x00, 0xcc},
- {0xb6, 0x03, 0x03, 0xcc},
- {0xb6, 0x02, 0x20, 0xcc},
- {0xb6, 0x05, 0x02, 0xcc},
- {0xb6, 0x04, 0x58, 0xcc},
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x21, 0xcc},
- {0xb6, 0x18, 0x03, 0xcc},
- {0xb6, 0x17, 0xa9, 0xcc},
- {0xb6, 0x16, 0x80, 0xcc},
- {0xb6, 0x22, 0x12, 0xcc},
- {0xb6, 0x23, 0x0b, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},
- {0xbf, 0xc1, 0x04, 0xcc},
- {0xbf, 0xcc, 0x00, 0xcc},
- {0xb8, 0x06, 0x20, 0xcc},
- {0xb8, 0x07, 0x03, 0xcc},
- {0xb8, 0x08, 0x58, 0xcc},
- {0xb8, 0x09, 0x02, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0xd9, 0x0f, 0xaa},
- {0x00, 0xda, 0xaa, 0xaa},
- {0x00, 0xd9, 0x10, 0xaa},
- {0x00, 0xda, 0xaa, 0xaa},
- {0x00, 0xd9, 0x11, 0xaa},
- {0x00, 0xda, 0x00, 0xaa},
- {0x00, 0xd9, 0x12, 0xaa},
- {0x00, 0xda, 0xff, 0xaa},
- {0x00, 0xd9, 0x13, 0xaa},
- {0x00, 0xda, 0xff, 0xaa},
- {0x00, 0xe8, 0x11, 0xaa},
- {0x00, 0xe9, 0x12, 0xaa},
- {0x00, 0xea, 0x5c, 0xaa},
- {0x00, 0xeb, 0xff, 0xaa},
- {0x00, 0xd8, 0x80, 0xaa},
- {0x00, 0xe6, 0x02, 0xaa},
- {0x00, 0xd6, 0x40, 0xaa},
- {0x00, 0xe3, 0x05, 0xaa},
- {0x00, 0xe0, 0x40, 0xaa},
- {0x00, 0xde, 0x03, 0xaa},
- {0x00, 0xdf, 0x03, 0xaa},
- {0x00, 0xdb, 0x02, 0xaa},
- {0x00, 0xdc, 0x00, 0xaa},
- {0x00, 0xdd, 0x03, 0xaa},
- {0x00, 0xe1, 0x08, 0xaa},
- {0x00, 0xe2, 0x01, 0xaa},
- {0x00, 0xd6, 0x40, 0xaa},
- {0x00, 0xe4, 0x40, 0xaa},
- {0x00, 0xa8, 0x8f, 0xaa},
- {0x00, 0xb4, 0x16, 0xaa},
- {0xb0, 0x02, 0x06, 0xcc},
- {0xb0, 0x18, 0x06, 0xcc},
- {0xb0, 0x19, 0x06, 0xcc},
- {0xb3, 0x5d, 0x18, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0x00, 0xb4, 0x0e, 0xaa},
- {0x00, 0xb5, 0x49, 0xaa},
- {0x00, 0xb6, 0x1c, 0xaa},
- {0x00, 0xb7, 0x96, 0xaa},
-/* end of usbvm326.inf - start of ms-win trace */
- {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x3d, 0xcc},
-/*read b306*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x1a, 0x09, 0xaa},
- {0x00, 0x1b, 0x8a, 0xaa},
-/*read b827*/
- {0xb8, 0x27, 0x00, 0xcc},
- {0xb8, 0x26, 0x60, 0xcc},
- {0xb8, 0x26, 0x60, 0xcc},
-/*gamma - to do?*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0xae, 0x84, 0xaa},
-/*gamma again*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x96, 0xa0, 0xaa},
-/*matrix*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x91, 0x35, 0xaa},
- {0x00, 0x92, 0x22, 0xaa},
-/*gamma*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x95, 0x85, 0xaa},
-/*matrix*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x4d, 0x20, 0xaa},
- {0xb8, 0x22, 0x40, 0xcc},
- {0xb8, 0x23, 0x40, 0xcc},
- {0xb8, 0x24, 0x40, 0xcc},
- {0xb8, 0x81, 0x09, 0xcc},
- {0x00, 0x00, 0x64, 0xdd},
- {0x00, 0x03, 0x01, 0xaa},
-/*read 46*/
- {0x00, 0x46, 0x3c, 0xaa},
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0x16, 0x40, 0xaa},
- {0x00, 0x17, 0x40, 0xaa},
- {0x00, 0x18, 0x40, 0xaa},
- {0x00, 0x19, 0x41, 0xaa},
- {0x00, 0x03, 0x01, 0xaa},
- {0x00, 0x46, 0x3c, 0xaa},
- {0x00, 0x00, 0x18, 0xdd},
-/*read bfff*/
- {0x00, 0x03, 0x00, 0xaa},
- {0x00, 0xb4, 0x1c, 0xaa},
- {0x00, 0xb5, 0x92, 0xaa},
- {0x00, 0xb6, 0x39, 0xaa},
- {0x00, 0xb7, 0x24, 0xaa},
-/*write 89 0400 1415*/
- {}
-};
-
-static const u8 poxxxx_init_common[][4] = {
- {0xb3, 0x00, 0x04, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb3, 0x00, 0x64, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb3, 0x00, 0x65, 0xcc},
- {0x00, 0x00, 0x10, 0xdd},
- {0xb3, 0x00, 0x67, 0xcc},
- {0xb0, 0x03, 0x09, 0xcc},
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc},
- {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
- {0xb3, 0x02, 0xb0, 0xcc},
- {0xb3, 0x03, 0x18, 0xcc},
- {0xb3, 0x04, 0x15, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc},
- {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x04, 0xcc}, /* sensor height = 1024 */
- {0xb3, 0x23, 0x00, 0xcc},
- {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc},
- {0xb3, 0x16, 0x04, 0xcc}, /* sensor width = 1280 */
- {0xb3, 0x17, 0xff, 0xcc},
- {0xb3, 0x2c, 0x03, 0xcc},
- {0xb3, 0x2d, 0x56, 0xcc},
- {0xb3, 0x2e, 0x02, 0xcc},
- {0xb3, 0x2f, 0x0a, 0xcc},
- {0xb3, 0x40, 0x00, 0xcc},
- {0xb3, 0x41, 0x34, 0xcc},
- {0xb3, 0x42, 0x01, 0xcc},
- {0xb3, 0x43, 0xe0, 0xcc},
- {0xbc, 0x00, 0x71, 0xcc},
- {0xbc, 0x01, 0x01, 0xcc},
- {0xb3, 0x01, 0x41, 0xcc},
- {0xb3, 0x4d, 0x00, 0xcc},
- {0x00, 0x0b, 0x2a, 0xaa},
- {0x00, 0x0e, 0x03, 0xaa},
- {0x00, 0x0f, 0xea, 0xaa},
- {0x00, 0x12, 0x08, 0xaa},
- {0x00, 0x1e, 0x06, 0xaa},
- {0x00, 0x21, 0x00, 0xaa},
- {0x00, 0x31, 0x1f, 0xaa},
- {0x00, 0x33, 0x38, 0xaa},
- {0x00, 0x36, 0xc0, 0xaa},
- {0x00, 0x37, 0xc8, 0xaa},
- {0x00, 0x3b, 0x36, 0xaa},
- {0x00, 0x4b, 0xfe, 0xaa},
- {0x00, 0x4d, 0x2e, 0xaa},
- {0x00, 0x51, 0x1c, 0xaa},
- {0x00, 0x52, 0x01, 0xaa},
- {0x00, 0x55, 0x0a, 0xaa},
- {0x00, 0x56, 0x0a, 0xaa},
- {0x00, 0x57, 0x07, 0xaa},
- {0x00, 0x58, 0x07, 0xaa},
- {0x00, 0x59, 0x04, 0xaa},
- {0x00, 0x70, 0x68, 0xaa},
- {0x00, 0x71, 0x04, 0xaa},
- {0x00, 0x72, 0x10, 0xaa},
- {0x00, 0x80, 0x71, 0xaa},
- {0x00, 0x81, 0x08, 0xaa},
- {0x00, 0x82, 0x00, 0xaa},
- {0x00, 0x83, 0x55, 0xaa},
- {0x00, 0x84, 0x06, 0xaa},
- {0x00, 0x85, 0x06, 0xaa},
- {0x00, 0x8b, 0x25, 0xaa},
- {0x00, 0x8c, 0x00, 0xaa},
- {0x00, 0x8d, 0x86, 0xaa},
- {0x00, 0x8e, 0x82, 0xaa},
- {0x00, 0x8f, 0x2d, 0xaa},
- {0x00, 0x90, 0x8b, 0xaa},
- {0x00, 0x91, 0x81, 0xaa},
- {0x00, 0x92, 0x81, 0xaa},
- {0x00, 0x93, 0x23, 0xaa},
- {0x00, 0xa3, 0x2a, 0xaa},
- {0x00, 0xa4, 0x03, 0xaa},
- {0x00, 0xa5, 0xea, 0xaa},
- {0x00, 0xb0, 0x68, 0xaa},
- {0x00, 0xbc, 0x04, 0xaa},
- {0x00, 0xbe, 0x3b, 0xaa},
- {0x00, 0x4e, 0x40, 0xaa},
- {0x00, 0x06, 0x04, 0xaa},
- {0x00, 0x07, 0x03, 0xaa},
- {0x00, 0xcd, 0x18, 0xaa},
- {0x00, 0x28, 0x03, 0xaa},
- {0x00, 0x29, 0xef, 0xaa},
-/* reinit on alt 2 (qvga) or alt7 (vga) */
- {0xb3, 0x05, 0x00, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0xb8, 0x00, 0x01, 0xcc},
-
- {0x00, 0x1d, 0x85, 0xaa},
- {0x00, 0x1e, 0xc6, 0xaa},
- {0x00, 0x00, 0x40, 0xdd},
- {0x00, 0x1d, 0x05, 0xaa},
- {}
-};
-static const u8 poxxxx_gamma[][4] = {
- {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
- {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x0a, 0xaa},
- {0x00, 0x75, 0x16, 0xaa},
- {0x00, 0x76, 0x25, 0xaa},
- {0x00, 0x77, 0x34, 0xaa},
- {0x00, 0x78, 0x49, 0xaa},
- {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa},
- {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa},
- {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa},
-
- {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */
- {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x0a, 0xaa},
- {0x00, 0x75, 0x16, 0xaa},
- {0x00, 0x76, 0x25, 0xaa},
- {0x00, 0x77, 0x34, 0xaa},
- {0x00, 0x78, 0x49, 0xaa},
- {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa},
- {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa},
- {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa},
-
- {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */
- {0x00, 0x73, 0x00, 0xaa},
- {0x00, 0x74, 0x0a, 0xaa},
- {0x00, 0x75, 0x16, 0xaa},
- {0x00, 0x76, 0x25, 0xaa},
- {0x00, 0x77, 0x34, 0xaa},
- {0x00, 0x78, 0x49, 0xaa},
- {0x00, 0x79, 0x5a, 0xaa},
- {0x00, 0x7a, 0x7f, 0xaa},
- {0x00, 0x7b, 0x9b, 0xaa},
- {0x00, 0x7c, 0xba, 0xaa},
- {0x00, 0x7d, 0xd4, 0xaa},
- {0x00, 0x7e, 0xea, 0xaa},
- {}
-};
-static const u8 poxxxx_init_start_3[][4] = {
- {0x00, 0xb8, 0x28, 0xaa},
- {0x00, 0xb9, 0x1e, 0xaa},
- {0x00, 0xb6, 0x14, 0xaa},
- {0x00, 0xb7, 0x0f, 0xaa},
- {0x00, 0x5c, 0x10, 0xaa},
- {0x00, 0x5d, 0x18, 0xaa},
- {0x00, 0x5e, 0x24, 0xaa},
- {0x00, 0x5f, 0x24, 0xaa},
- {0x00, 0x86, 0x1a, 0xaa},
- {0x00, 0x60, 0x00, 0xaa},
- {0x00, 0x61, 0x1b, 0xaa},
- {0x00, 0x62, 0x30, 0xaa},
- {0x00, 0x63, 0x40, 0xaa},
- {0x00, 0x87, 0x1a, 0xaa},
- {0x00, 0x64, 0x00, 0xaa},
- {0x00, 0x65, 0x08, 0xaa},
- {0x00, 0x66, 0x10, 0xaa},
- {0x00, 0x67, 0x20, 0xaa},
- {0x00, 0x88, 0x10, 0xaa},
- {0x00, 0x68, 0x00, 0xaa},
- {0x00, 0x69, 0x08, 0xaa},
- {0x00, 0x6a, 0x0f, 0xaa},
- {0x00, 0x6b, 0x0f, 0xaa},
- {0x00, 0x89, 0x07, 0xaa},
- {0x00, 0xd5, 0x4c, 0xaa},
- {0x00, 0x0a, 0x00, 0xaa},
- {0x00, 0x0b, 0x2a, 0xaa},
- {0x00, 0x0e, 0x03, 0xaa},
- {0x00, 0x0f, 0xea, 0xaa},
- {0x00, 0xa2, 0x00, 0xaa},
- {0x00, 0xa3, 0x2a, 0xaa},
- {0x00, 0xa4, 0x03, 0xaa},
- {0x00, 0xa5, 0xea, 0xaa},
- {}
-};
-static const u8 poxxxx_initVGA[][4] = {
- {0x00, 0x20, 0x11, 0xaa},
- {0x00, 0x33, 0x38, 0xaa},
- {0x00, 0xbb, 0x0d, 0xaa},
- {0xb3, 0x22, 0x01, 0xcc}, /* change to 640x480 */
- {0xb3, 0x23, 0xe0, 0xcc},
- {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc},
- {0xb3, 0x02, 0xb0, 0xcc},
- {0xb3, 0x06, 0x00, 0xcc},
- {0xb3, 0x5c, 0x01, 0xcc},
- {0x00, 0x04, 0x06, 0xaa},
- {0x00, 0x05, 0x3f, 0xaa},
- {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
- {}
-};
-static const u8 poxxxx_initQVGA[][4] = {
- {0x00, 0x20, 0x33, 0xaa},
- {0x00, 0x33, 0x38, 0xaa},
- {0x00, 0xbb, 0x0d, 0xaa},
- {0xb3, 0x22, 0x00, 0xcc}, /* change to 320x240 */
- {0xb3, 0x23, 0xf0, 0xcc},
- {0xb3, 0x16, 0x01, 0xcc},
- {0xb3, 0x17, 0x3f, 0xcc},
- {0xb3, 0x02, 0xb0, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc},
- {0xb3, 0x5c, 0x00, 0xcc},
- {0x00, 0x04, 0x06, 0xaa},
- {0x00, 0x05, 0x3f, 0xaa},
- {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
- {}
-};
-static const u8 poxxxx_init_end_1[][4] = {
- {0x00, 0x47, 0x25, 0xaa},
- {0x00, 0x48, 0x80, 0xaa},
- {0x00, 0x49, 0x1f, 0xaa},
- {0x00, 0x4a, 0x40, 0xaa},
- {0x00, 0x44, 0x40, 0xaa},
- {0x00, 0xab, 0x4a, 0xaa},
- {0x00, 0xb1, 0x00, 0xaa},
- {0x00, 0xb2, 0x04, 0xaa},
- {0x00, 0xb3, 0x08, 0xaa},
- {0x00, 0xb4, 0x0b, 0xaa},
- {0x00, 0xb5, 0x0d, 0xaa},
- {}
-};
-static const u8 poxxxx_init_end_2[][4] = {
- {0x00, 0x1d, 0x85, 0xaa},
- {0x00, 0x1e, 0x06, 0xaa},
- {0x00, 0x1d, 0x05, 0xaa},
- {}
-};
-
-struct sensor_info {
- s8 sensorId;
- u8 I2cAdd;
- u8 IdAdd;
- u16 VpId;
- u8 m1;
- u8 m2;
- u8 op;
-};
-
-/* probe values */
-static const struct sensor_info vc0321_probe_data[] = {
-/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
-/* 0 OV9640 */
- {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
-/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
- {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
-/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
- {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 3 MI1310 */
- {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 4 MI360 - tested in vc032x_probe_sensor */
-/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
-/* 5 7131R */
- {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
-/* 6 OV7649 */
- {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
-/* 7 PAS302BCW */
- {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
-/* 8 OV7660 */
- {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
-/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
-/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
-/* 10 PO1030KC */
- {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 11 MI1310_SOC */
- {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
-/* 12 OV9650 */
- {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
-/* 13 S5K532 */
- {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
-/* 14 MI360_SOC - ??? */
-/* 15 PO1200N */
- {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
-/* 16 PO3030K */
- {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 17 PO2030 */
- {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* ?? */
- {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
- {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
-};
-static const struct sensor_info vc0323_probe_data[] = {
-/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
-/* 0 OV9640 */
- {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
-/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
- {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
-/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
- {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 3 MI1310 */
- {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 4 MI360 - tested in vc032x_probe_sensor */
-/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
-/* 5 7131R */
- {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
-/* 6 OV7649 */
- {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
-/* 7 PAS302BCW */
- {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
-/* 8 OV7660 */
- {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
-/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
-/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
-/* 10 PO1030KC */
- {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* 11 MI1310_SOC */
- {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
-/* 12 OV9650 */
- {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
-/* 13 S5K532 */
- {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
-/* 14 MI360_SOC - ??? */
-/* 15 PO1200N */
- {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
-/* 16 ?? */
- {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
-/* 17 PO2030 */
- {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
-/* ?? */
- {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
- {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
-/*fixme: not in the ms-win probe - may be found before? */
- {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
-};
-
-/* read 'len' bytes in gspca_dev->usb_buf */
-static void reg_r_i(struct gspca_dev *gspca_dev,
- u16 req,
- u16 index,
- u16 len)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- req,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 1, /* value */
- index, gspca_dev->usb_buf, len,
- 500);
- if (ret < 0) {
- pr_err("reg_r err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-static void reg_r(struct gspca_dev *gspca_dev,
- u16 req,
- u16 index,
- u16 len)
-{
- reg_r_i(gspca_dev, req, index, len);
-#ifdef GSPCA_DEBUG
- if (gspca_dev->usb_err < 0)
- return;
- if (len == 1)
- PDEBUG(D_USBI, "GET %02x 0001 %04x %02x", req, index,
- gspca_dev->usb_buf[0]);
- else
- PDEBUG(D_USBI, "GET %02x 0001 %04x %02x %02x %02x",
- req, index,
- gspca_dev->usb_buf[0],
- gspca_dev->usb_buf[1],
- gspca_dev->usb_buf[2]);
-#endif
-}
-
-static void reg_w_i(struct gspca_dev *gspca_dev,
- u16 req,
- u16 value,
- u16 index)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- req,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0,
- 500);
- if (ret < 0) {
- pr_err("reg_w err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-static void reg_w(struct gspca_dev *gspca_dev,
- u16 req,
- u16 value,
- u16 index)
-{
-#ifdef GSPCA_DEBUG
- if (gspca_dev->usb_err < 0)
- return;
- PDEBUG(D_USBO, "SET %02x %04x %04x", req, value, index);
-#endif
- reg_w_i(gspca_dev, req, value, index);
-}
-
-static u16 read_sensor_register(struct gspca_dev *gspca_dev,
- u16 address)
-{
- u8 ldata, mdata, hdata;
- int retry = 50;
-
- reg_r(gspca_dev, 0xa1, 0xb33f, 1);
- if (!(gspca_dev->usb_buf[0] & 0x02)) {
- pr_err("I2c Bus Busy Wait %02x\n", gspca_dev->usb_buf[0]);
- return 0;
- }
- reg_w(gspca_dev, 0xa0, address, 0xb33a);
- reg_w(gspca_dev, 0xa0, 0x02, 0xb339);
-
- do {
- reg_r(gspca_dev, 0xa1, 0xb33b, 1);
- if (gspca_dev->usb_buf[0] == 0x00)
- break;
- msleep(40);
- } while (--retry >= 0);
-
- reg_r(gspca_dev, 0xa1, 0xb33e, 1);
- ldata = gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0xa1, 0xb33d, 1);
- mdata = gspca_dev->usb_buf[0];
- reg_r(gspca_dev, 0xa1, 0xb33c, 1);
- hdata = gspca_dev->usb_buf[0];
- if (hdata != 0 && mdata != 0 && ldata != 0)
- PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
- hdata, mdata, ldata);
- reg_r(gspca_dev, 0xa1, 0xb334, 1);
- if (gspca_dev->usb_buf[0] == 0x02)
- return (hdata << 8) + mdata;
- return hdata;
-}
-
-static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, n;
- u16 value;
- const struct sensor_info *ptsensor_info;
-
-/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/
- if (sd->flags & FL_SAMSUNG) {
- reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
- reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
- /* select the back sensor */
- }
-
- reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
- PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
- sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
- if (sd->bridge == BRIDGE_VC0321) {
- ptsensor_info = vc0321_probe_data;
- n = ARRAY_SIZE(vc0321_probe_data);
- } else {
- ptsensor_info = vc0323_probe_data;
- n = ARRAY_SIZE(vc0323_probe_data);
- }
- for (i = 0; i < n; i++) {
- reg_w(gspca_dev, 0xa0, 0x02, 0xb334);
- reg_w(gspca_dev, 0xa0, ptsensor_info->m1, 0xb300);
- reg_w(gspca_dev, 0xa0, ptsensor_info->m2, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x01, 0xb308);
- reg_w(gspca_dev, 0xa0, 0x0c, 0xb309);
- reg_w(gspca_dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
- reg_w(gspca_dev, 0xa0, ptsensor_info->op, 0xb301);
- value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd);
- if (value == 0 && ptsensor_info->IdAdd == 0x82)
- value = read_sensor_register(gspca_dev, 0x83);
- if (value != 0) {
- PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)",
- value, i);
- if (value == ptsensor_info->VpId)
- return ptsensor_info->sensorId;
-
- switch (value) {
- case 0x3130:
- return SENSOR_PO3130NC;
- case 0x7673:
- return SENSOR_OV7670;
- case 0x8243:
- return SENSOR_MI0360;
- }
- }
- ptsensor_info++;
- }
- return -1;
-}
-
-static void i2c_write(struct gspca_dev *gspca_dev,
- u8 reg, const u8 *val,
- u8 size) /* 1 or 2 */
-{
- int retry;
-
-#ifdef GSPCA_DEBUG
- if (gspca_dev->usb_err < 0)
- return;
- if (size == 1)
- PDEBUG(D_USBO, "i2c_w %02x %02x", reg, *val);
- else
- PDEBUG(D_USBO, "i2c_w %02x %02x%02x", reg, *val, val[1]);
-#endif
- reg_r_i(gspca_dev, 0xa1, 0xb33f, 1);
-/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/
- reg_w_i(gspca_dev, 0xa0, size, 0xb334);
- reg_w_i(gspca_dev, 0xa0, reg, 0xb33a);
- reg_w_i(gspca_dev, 0xa0, val[0], 0xb336);
- if (size > 1)
- reg_w_i(gspca_dev, 0xa0, val[1], 0xb337);
- reg_w_i(gspca_dev, 0xa0, 0x01, 0xb339);
- retry = 4;
- do {
- reg_r_i(gspca_dev, 0xa1, 0xb33b, 1);
- if (gspca_dev->usb_buf[0] == 0)
- break;
- msleep(20);
- } while (--retry > 0);
- if (retry <= 0)
- pr_err("i2c_write timeout\n");
-}
-
-static void put_tab_to_reg(struct gspca_dev *gspca_dev,
- const u8 *tab, u8 tabsize, u16 addr)
-{
- int j;
- u16 ad = addr;
-
- for (j = 0; j < tabsize; j++)
- reg_w(gspca_dev, 0xa0, tab[j], ad++);
-}
-
-static void usb_exchange(struct gspca_dev *gspca_dev,
- const u8 data[][4])
-{
- int i = 0;
-
- for (;;) {
- switch (data[i][3]) {
- default:
- return;
- case 0xcc: /* normal write */
- reg_w(gspca_dev, 0xa0, data[i][2],
- (data[i][0]) << 8 | data[i][1]);
- break;
- case 0xaa: /* i2c op */
- i2c_write(gspca_dev, data[i][1], &data[i][2], 1);
- break;
- case 0xbb: /* i2c op */
- i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
- break;
- case 0xdd:
- msleep(data[i][1] * 256 + data[i][2] + 10);
- break;
- }
- i++;
- }
- /*not reached*/
-}
-
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->bridge = id->driver_info >> 8;
- sd->flags = id->driver_info & 0xff;
-
- if (id->idVendor == 0x046d &&
- (id->idProduct == 0x0892 || id->idProduct == 0x0896))
- sd->sensor = SENSOR_POxxxx; /* no probe */
-
- 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;
- struct cam *cam;
- int sensor;
- /* number of packets per ISOC message */
- static u8 npkt[NSENSORS] = {
- [SENSOR_HV7131R] = 64,
- [SENSOR_MI0360] = 32,
- [SENSOR_MI1310_SOC] = 32,
- [SENSOR_MI1320] = 64,
- [SENSOR_MI1320_SOC] = 128,
- [SENSOR_OV7660] = 32,
- [SENSOR_OV7670] = 64,
- [SENSOR_PO1200] = 128,
- [SENSOR_PO3130NC] = 128,
- [SENSOR_POxxxx] = 128,
- };
-
- if (sd->sensor != SENSOR_POxxxx)
- sensor = vc032x_probe_sensor(gspca_dev);
- else
- sensor = sd->sensor;
-
- switch (sensor) {
- case -1:
- pr_err("Unknown sensor...\n");
- return -EINVAL;
- case SENSOR_HV7131R:
- PDEBUG(D_PROBE, "Find Sensor HV7131R");
- break;
- case SENSOR_MI0360:
- PDEBUG(D_PROBE, "Find Sensor MI0360");
- sd->bridge = BRIDGE_VC0323;
- break;
- case SENSOR_MI1310_SOC:
- PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
- break;
- case SENSOR_MI1320:
- PDEBUG(D_PROBE, "Find Sensor MI1320");
- break;
- case SENSOR_MI1320_SOC:
- PDEBUG(D_PROBE, "Find Sensor MI1320_SOC");
- break;
- case SENSOR_OV7660:
- PDEBUG(D_PROBE, "Find Sensor OV7660");
- break;
- case SENSOR_OV7670:
- PDEBUG(D_PROBE, "Find Sensor OV7670");
- break;
- case SENSOR_PO1200:
- PDEBUG(D_PROBE, "Find Sensor PO1200");
- break;
- case SENSOR_PO3130NC:
- PDEBUG(D_PROBE, "Find Sensor PO3130NC");
- break;
- case SENSOR_POxxxx:
- PDEBUG(D_PROBE, "Sensor POxxxx");
- break;
- }
- sd->sensor = sensor;
-
- cam = &gspca_dev->cam;
- if (sd->bridge == BRIDGE_VC0321) {
- cam->cam_mode = vc0321_mode;
- cam->nmodes = ARRAY_SIZE(vc0321_mode);
- } else {
- switch (sensor) {
- case SENSOR_PO1200:
- cam->cam_mode = svga_mode;
- cam->nmodes = ARRAY_SIZE(svga_mode);
- break;
- case SENSOR_MI1310_SOC:
- cam->cam_mode = vc0323_mode;
- cam->nmodes = ARRAY_SIZE(vc0323_mode);
- break;
- case SENSOR_MI1320_SOC:
- cam->cam_mode = bi_mode;
- cam->nmodes = ARRAY_SIZE(bi_mode);
- break;
- case SENSOR_OV7670:
- cam->cam_mode = bi_mode;
- cam->nmodes = ARRAY_SIZE(bi_mode) - 1;
- break;
- default:
- cam->cam_mode = vc0323_mode;
- cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1;
- break;
- }
- }
- cam->npkt = npkt[sd->sensor];
-
- if (sd->sensor == SENSOR_OV7670)
- sd->flags |= FL_HFLIP | FL_VFLIP;
-
- if (sd->bridge == BRIDGE_VC0321) {
- reg_r(gspca_dev, 0x8a, 0, 3);
- reg_w(gspca_dev, 0x87, 0x00, 0x0f0f);
- reg_r(gspca_dev, 0x8b, 0, 3);
- reg_w(gspca_dev, 0x88, 0x00, 0x0202);
- if (sd->sensor == SENSOR_POxxxx) {
- reg_r(gspca_dev, 0xa1, 0xb300, 1);
- if (gspca_dev->usb_buf[0] != 0) {
- reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
- }
- reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
- }
- }
- return gspca_dev->usb_err;
-}
-
-static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 data;
-
- data = val;
- if (data >= 0x80)
- data &= 0x7f;
- else
- data = 0xff ^ data;
- i2c_write(gspca_dev, 0x98, &data, 1);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev, u8 val)
-{
- i2c_write(gspca_dev, 0x99, &val, 1);
-}
-
-static void setcolors(struct gspca_dev *gspca_dev, u8 val)
-{
- u8 data;
-
- data = val - (val >> 3) - 1;
- i2c_write(gspca_dev, 0x94, &data, 1);
- i2c_write(gspca_dev, 0x95, &val, 1);
-}
-
-static void sethvflip(struct gspca_dev *gspca_dev, bool hflip, bool vflip)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 data[2];
-
- if (sd->flags & FL_HFLIP)
- hflip = !hflip;
- if (sd->flags & FL_VFLIP)
- vflip = !vflip;
- switch (sd->sensor) {
- case SENSOR_MI1310_SOC:
- case SENSOR_MI1320:
- case SENSOR_MI1320_SOC:
- data[0] = data[1] = 0; /* select page 0 */
- i2c_write(gspca_dev, 0xf0, data, 2);
- data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01;
- data[1] = 0x02 * hflip
- | 0x01 * vflip;
- i2c_write(gspca_dev, 0x20, data, 2);
- break;
- case SENSOR_OV7660:
- case SENSOR_OV7670:
- data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07;
- data[0] |= OV7660_MVFP_MIRROR * hflip
- | OV7660_MVFP_VFLIP * vflip;
- i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1);
- break;
- case SENSOR_PO1200:
- data[0] = 0;
- i2c_write(gspca_dev, 0x03, data, 1);
- data[0] = 0x80 * hflip
- | 0x40 * vflip
- | 0x06;
- i2c_write(gspca_dev, 0x1e, data, 1);
- break;
- }
-}
-
-static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- static const u8 (*ov7660_freq_tb[3])[4] =
- {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ};
-
- if (sd->sensor != SENSOR_OV7660)
- return;
- usb_exchange(gspca_dev, ov7660_freq_tb[val]);
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 data;
-
- switch (sd->sensor) {
- case SENSOR_PO1200:
- data = 0;
- i2c_write(gspca_dev, 0x03, &data, 1);
- if (val < 0)
- data = 0x6a;
- else
- data = 0xb5 + val * 3;
- i2c_write(gspca_dev, 0x61, &data, 1);
- break;
- case SENSOR_POxxxx:
- if (val < 0)
- data = 0x7e; /* def = max */
- else
- data = 0x60 + val * 0x0f;
- i2c_write(gspca_dev, 0x59, &data, 1);
- break;
- }
-}
-static void setgain(struct gspca_dev *gspca_dev, u8 val)
-{
- i2c_write(gspca_dev, 0x15, &val, 1);
-}
-
-static void setexposure(struct gspca_dev *gspca_dev, s32 val)
-{
- u8 data;
-
- data = val >> 8;
- i2c_write(gspca_dev, 0x1a, &data, 1);
- data = val;
- i2c_write(gspca_dev, 0x1b, &data, 1);
-}
-
-static void setautogain(struct gspca_dev *gspca_dev, s32 val)
-{
- static const u8 data[2] = {0x28, 0x3c};
-
- i2c_write(gspca_dev, 0xd1, &data[val], 1);
-}
-
-static void setgamma(struct gspca_dev *gspca_dev)
-{
-/*fixme:to do */
- usb_exchange(gspca_dev, poxxxx_gamma);
-}
-
-static void setbacklight(struct gspca_dev *gspca_dev, s32 val)
-{
- u16 v;
- u8 data;
-
- data = (val << 4) | 0x0f;
- i2c_write(gspca_dev, 0xaa, &data, 1);
- v = 613 + 12 * val;
- data = v >> 8;
- i2c_write(gspca_dev, 0xc4, &data, 1);
- data = v;
- i2c_write(gspca_dev, 0xc5, &data, 1);
- v = 1093 - 12 * val;
- data = v >> 8;
- i2c_write(gspca_dev, 0xc6, &data, 1);
- data = v;
- i2c_write(gspca_dev, 0xc7, &data, 1);
- v = 342 + 9 * val;
- data = v >> 8;
- i2c_write(gspca_dev, 0xc8, &data, 1);
- data = v;
- i2c_write(gspca_dev, 0xc9, &data, 1);
- v = 702 - 9 * val;
- data = v >> 8;
- i2c_write(gspca_dev, 0xca, &data, 1);
- data = v;
- i2c_write(gspca_dev, 0xcb, &data, 1);
-}
-
-static void setwb(struct gspca_dev *gspca_dev)
-{
-/*fixme:to do - valid when reg d1 = 0x1c - (reg16 + reg15 = 0xa3)*/
- static const u8 data[2] = {0x00, 0x00};
-
- i2c_write(gspca_dev, 0x16, &data[0], 1);
- i2c_write(gspca_dev, 0x18, &data[1], 1);
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- const u8 (*init)[4];
- const u8 *GammaT = NULL;
- const u8 *MatrixT = NULL;
- int mode;
- static const u8 (*mi1320_soc_init[])[4] = {
- mi1320_soc_InitSXGA,
- mi1320_soc_InitVGA,
- mi1320_soc_InitQVGA,
- };
-
-/*fixme: back sensor only*/
- if (sd->flags & FL_SAMSUNG) {
- reg_w(gspca_dev, 0x89, 0xf0ff, 0xffff);
- reg_w(gspca_dev, 0xa9, 0x8348, 0x000e);
- reg_w(gspca_dev, 0xa9, 0x0000, 0x001a);
- }
-
- /* Assume start use the good resolution from gspca_dev->mode */
- if (sd->bridge == BRIDGE_VC0321) {
- reg_w(gspca_dev, 0xa0, 0xff, 0xbfec);
- reg_w(gspca_dev, 0xa0, 0xff, 0xbfed);
- reg_w(gspca_dev, 0xa0, 0xff, 0xbfee);
- reg_w(gspca_dev, 0xa0, 0xff, 0xbfef);
- sd->image_offset = 46;
- } else {
- if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat
- == V4L2_PIX_FMT_JPEG)
- sd->image_offset = 0;
- else
- sd->image_offset = 32;
- }
-
- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- GammaT = hv7131r_gamma;
- MatrixT = hv7131r_matrix;
- if (mode)
- init = hv7131r_initQVGA_data; /* 320x240 */
- else
- init = hv7131r_initVGA_data; /* 640x480 */
- break;
- case SENSOR_OV7660:
- GammaT = ov7660_gamma;
- MatrixT = ov7660_matrix;
- if (mode)
- init = ov7660_initQVGA_data; /* 320x240 */
- else
- init = ov7660_initVGA_data; /* 640x480 */
- break;
- case SENSOR_MI0360:
- GammaT = mi1320_gamma;
- MatrixT = mi0360_matrix;
- if (mode)
- init = mi0360_initQVGA_JPG; /* 320x240 */
- else
- init = mi0360_initVGA_JPG; /* 640x480 */
- break;
- case SENSOR_MI1310_SOC:
- GammaT = mi1320_gamma;
- MatrixT = mi1320_matrix;
- switch (mode) {
- case 1:
- init = mi1310_socinitQVGA_JPG; /* 320x240 */
- break;
- case 0:
- init = mi1310_socinitVGA_JPG; /* 640x480 */
- break;
- default:
- init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */
- break;
- }
- break;
- case SENSOR_MI1320:
- GammaT = mi1320_gamma;
- MatrixT = mi1320_matrix;
- if (mode)
- init = mi1320_initQVGA_data; /* 320x240 */
- else
- init = mi1320_initVGA_data; /* 640x480 */
- break;
- case SENSOR_MI1320_SOC:
- GammaT = mi1320_gamma;
- MatrixT = mi1320_matrix;
- init = mi1320_soc_init[mode];
- break;
- case SENSOR_OV7670:
- init = mode == 1 ? ov7670_InitVGA : ov7670_InitQVGA;
- break;
- case SENSOR_PO3130NC:
- GammaT = po3130_gamma;
- MatrixT = po3130_matrix;
- if (mode)
- init = po3130_initQVGA_data; /* 320x240 */
- else
- init = po3130_initVGA_data; /* 640x480 */
- usb_exchange(gspca_dev, init);
- init = po3130_rundata;
- break;
- case SENSOR_PO1200:
- GammaT = po1200_gamma;
- MatrixT = po1200_matrix;
- init = po1200_initVGA_data;
- break;
- default:
-/* case SENSOR_POxxxx: */
- usb_exchange(gspca_dev, poxxxx_init_common);
- setgamma(gspca_dev);
- usb_exchange(gspca_dev, poxxxx_init_start_3);
- if (mode)
- init = poxxxx_initQVGA;
- else
- init = poxxxx_initVGA;
- usb_exchange(gspca_dev, init);
- reg_r(gspca_dev, 0x8c, 0x0000, 3);
- reg_w(gspca_dev, 0xa0,
- gspca_dev->usb_buf[2] & 1 ? 0 : 1,
- 0xb35c);
- msleep(300);
-/*fixme: i2c read 04 and 05*/
- init = poxxxx_init_end_1;
- break;
- }
- usb_exchange(gspca_dev, init);
- if (GammaT && MatrixT) {
- put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
- put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b);
- put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
- put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
-
- switch (sd->sensor) {
- case SENSOR_PO1200:
- case SENSOR_HV7131R:
- reg_w(gspca_dev, 0x89, 0x0400, 0x1415);
- break;
- case SENSOR_MI1310_SOC:
- reg_w(gspca_dev, 0x89, 0x058c, 0x0000);
- break;
- }
- msleep(100);
- }
- switch (sd->sensor) {
- case SENSOR_OV7670:
- reg_w(gspca_dev, 0x87, 0xffff, 0xffff);
- reg_w(gspca_dev, 0x88, 0xff00, 0xf0f1);
- reg_w(gspca_dev, 0xa0, 0x0000, 0xbfff);
- break;
- case SENSOR_POxxxx:
- usb_exchange(gspca_dev, poxxxx_init_end_2);
- setwb(gspca_dev);
- msleep(80); /* led on */
- reg_w(gspca_dev, 0x89, 0xffff, 0xfdff);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->sensor) {
- case SENSOR_MI1310_SOC:
- reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
- break;
- case SENSOR_POxxxx:
- return;
- default:
- if (!(sd->flags & FL_SAMSUNG))
- reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
- break;
- }
- reg_w(gspca_dev, 0xa0, 0x01, 0xb301);
- reg_w(gspca_dev, 0xa0, 0x09, 0xb003);
-}
-
-/* called on streamoff with alt 0 and on disconnect */
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (!gspca_dev->present)
- return;
-/*fixme: is this useful?*/
- if (sd->sensor == SENSOR_MI1310_SOC)
- reg_w(gspca_dev, 0x89, 0x058c, 0x00ff);
- else if (!(sd->flags & FL_SAMSUNG))
- reg_w(gspca_dev, 0x89, 0xffff, 0xffff);
-
- if (sd->sensor == SENSOR_POxxxx) {
- reg_w(gspca_dev, 0xa0, 0x26, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x04, 0xb300);
- reg_w(gspca_dev, 0xa0, 0x00, 0xb300);
- }
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* isoc packet */
- int len) /* iso pkt length */
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (data[0] == 0xff && data[1] == 0xd8) {
- PDEBUG(D_PACK,
- "vc032x header packet found len %d", len);
- gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- data += sd->image_offset;
- len -= sd->image_offset;
- gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
- return;
- }
-
- /* The vc0321 sends some additional data after sending the complete
- * frame, we ignore this. */
- if (sd->bridge == BRIDGE_VC0321) {
- int size, l;
-
- l = gspca_dev->image_len;
- size = gspca_dev->frsz;
- if (len > size - l)
- len = size - l;
- }
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming && ctrl->id != V4L2_CID_POWER_LINE_FREQUENCY)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- setbrightness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- setcontrast(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_SATURATION:
- setcolors(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- sethvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
- break;
- case V4L2_CID_SHARPNESS:
- setsharpness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_AUTOGAIN:
- setautogain(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_GAIN:
- setgain(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_EXPOSURE:
- setexposure(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_BACKLIGHT_COMPENSATION:
- setbacklight(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- setlightfreq(gspca_dev, ctrl->val);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- bool has_brightness = false;
- bool has_contrast = false;
- bool has_sat = false;
- bool has_hvflip = false;
- bool has_freq = false;
- bool has_backlight = false;
- bool has_exposure = false;
- bool has_autogain = false;
- bool has_gain = false;
- bool has_sharpness = false;
-
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_PO3130NC:
- break;
- case SENSOR_MI1310_SOC:
- case SENSOR_MI1320:
- case SENSOR_MI1320_SOC:
- case SENSOR_OV7660:
- has_hvflip = true;
- break;
- case SENSOR_OV7670:
- has_hvflip = has_freq = true;
- break;
- case SENSOR_PO1200:
- has_hvflip = has_sharpness = true;
- break;
- case SENSOR_POxxxx:
- has_brightness = has_contrast = has_sat = has_backlight =
- has_exposure = has_autogain = has_gain =
- has_sharpness = true;
- break;
- }
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 8);
- if (has_brightness)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- if (has_contrast)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 127);
- if (has_sat)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SATURATION, 1, 127, 1, 63);
- if (has_hvflip) {
- sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- }
- if (has_sharpness)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SHARPNESS, -1, 2, 1, -1);
- if (has_freq)
- v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
- V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
- if (has_autogain)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- if (has_gain)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_GAIN, 0, 78, 1, 0);
- if (has_exposure)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 4095, 1, 450);
- if (has_backlight)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BACKLIGHT_COMPENSATION, 0, 15, 1, 15);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- if (sd->hflip)
- v4l2_ctrl_cluster(2, &sd->hflip);
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .init_controls = sd_init_controls,
- .config = sd_config,
- .init = sd_init,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
-};
-
-/* -- module initialisation -- */
-#define BF(bridge, flags) \
- .driver_info = (BRIDGE_ ## bridge << 8) \
- | (flags)
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)},
- {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)},
- {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)},
- {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)},
- {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)},
- {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)},
- {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)},
- {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)},
- {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)},
- {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)},
- {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)},
- {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)},
- {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)},
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
deleted file mode 100644
index b1a64b912666..000000000000
--- a/drivers/media/video/gspca/vicam.c
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#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"
-
-#define VICAM_FIRMWARE "vicam/firmware.fw"
-
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
-MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
-MODULE_LICENSE("GPL");
-MODULE_FIRMWARE(VICAM_FIRMWARE);
-
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- struct work_struct work_struct;
- struct workqueue_struct *work_thread;
-};
-
-/* 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 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)
- pr_err("control msg req %02X error %d\n", 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)
-{
- int ret, unscaled_height, act_len = 0;
- u8 *req_data = gspca_dev->usb_buf;
- s32 expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
- s32 gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
-
- memset(req_data, 0, 16);
- req_data[0] = gain;
- 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 (expo < 256) {
- /* Frame rate maxed out, use partial frame expo time */
- req_data[4] = 255 - expo;
- 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] = expo & 0xFF;
- req_data[7] = expo >> 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) {
- pr_err("bulk read fail (%d) len %d/%d\n",
- 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) {
- pr_err("Couldn't allocate USB buffer\n");
- goto exit;
- }
-
- while (gspca_dev->dev && gspca_dev->streaming) {
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- break;
-#endif
- 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);
-
- 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,
- &gspca_dev->dev->dev);
- if (ret) {
- pr_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);
-
- if (gspca_dev->dev)
- vicam_set_camera_power(gspca_dev, 0);
-}
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 2);
- gspca_dev->exposure = v4l2_ctrl_new_std(hdl, NULL,
- V4L2_CID_EXPOSURE, 0, 2047, 1, 256);
- gspca_dev->gain = v4l2_ctrl_new_std(hdl, NULL,
- V4L2_CID_GAIN, 0, 255, 1, 200);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- return 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,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/xirlink_cit.c b/drivers/media/video/gspca/xirlink_cit.c
deleted file mode 100644
index 13b8d395d210..000000000000
--- a/drivers/media/video/gspca/xirlink_cit.c
+++ /dev/null
@@ -1,3145 +0,0 @@
-/*
- * USB IBM C-It Video Camera driver
- *
- * Supports Xirlink C-It Video Camera, IBM PC Camera,
- * IBM NetCamera and Veo Stingray.
- *
- * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
- *
- * This driver is based on earlier work of:
- *
- * (C) Copyright 1999 Johannes Erdfelt
- * (C) Copyright 1999 Randy Dunlap
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define MODULE_NAME "xirlink-cit"
-
-#include <linux/input.h>
-#include "gspca.h"
-
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
-MODULE_DESCRIPTION("Xirlink C-IT");
-MODULE_LICENSE("GPL");
-
-/* FIXME we should autodetect this */
-static int ibm_netcam_pro;
-module_param(ibm_netcam_pro, int, 0);
-MODULE_PARM_DESC(ibm_netcam_pro,
- "Use IBM Netcamera Pro init sequences for Model 3 cams");
-
-/* FIXME this should be handled through the V4L2 input selection API */
-static int rca_input;
-module_param(rca_input, int, 0644);
-MODULE_PARM_DESC(rca_input,
- "Use rca input instead of ccd sensor on Model 3 cams");
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
- struct v4l2_ctrl *lighting;
- u8 model;
-#define CIT_MODEL0 0 /* bcd version 0.01 cams ie the xvp-500 */
-#define CIT_MODEL1 1 /* The model 1 - 4 nomenclature comes from the old */
-#define CIT_MODEL2 2 /* ibmcam driver */
-#define CIT_MODEL3 3
-#define CIT_MODEL4 4
-#define CIT_IBM_NETCAM_PRO 5
- u8 input_index;
- u8 button_state;
- u8 stop_on_control_change;
- u8 sof_read;
- u8 sof_len;
-};
-
-static void sd_stop0(struct gspca_dev *gspca_dev);
-
-static const struct v4l2_pix_format cif_yuv_mode[] = {
- {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {352, 288, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-
-static const struct v4l2_pix_format vga_yuv_mode[] = {
- {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {640, 480, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-
-static const struct v4l2_pix_format model0_mode[] = {
- {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {320, 240, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-
-static const struct v4l2_pix_format model2_mode[] = {
- {160, 120, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 160,
- .sizeimage = 160 * 120 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {176, 144, V4L2_PIX_FMT_CIT_YYVYUY, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 2 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {320, 240, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
- {352, 288, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 + 4,
- .colorspace = V4L2_COLORSPACE_SRGB},
-};
-
-/*
- * 01.01.08 - Added for RCA video in support -LO
- * This struct is used to init the Model3 cam to use the RCA video in port
- * instead of the CCD sensor.
- */
-static const u16 rca_initdata[][3] = {
- {0, 0x0000, 0x010c},
- {0, 0x0006, 0x012c},
- {0, 0x0078, 0x012d},
- {0, 0x0046, 0x012f},
- {0, 0xd141, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfea8, 0x0124},
- {1, 0x0000, 0x0116},
- {0, 0x0064, 0x0116},
- {1, 0x0000, 0x0115},
- {0, 0x0003, 0x0115},
- {0, 0x0008, 0x0123},
- {0, 0x0000, 0x0117},
- {0, 0x0000, 0x0112},
- {0, 0x0080, 0x0100},
- {0, 0x0000, 0x0100},
- {1, 0x0000, 0x0116},
- {0, 0x0060, 0x0116},
- {0, 0x0002, 0x0112},
- {0, 0x0000, 0x0123},
- {0, 0x0001, 0x0117},
- {0, 0x0040, 0x0108},
- {0, 0x0019, 0x012c},
- {0, 0x0040, 0x0116},
- {0, 0x000a, 0x0115},
- {0, 0x000b, 0x0115},
- {0, 0x0078, 0x012d},
- {0, 0x0046, 0x012f},
- {0, 0xd141, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfea8, 0x0124},
- {0, 0x0064, 0x0116},
- {0, 0x0000, 0x0115},
- {0, 0x0001, 0x0115},
- {0, 0xffff, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00aa, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xffff, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00f2, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x000f, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xffff, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00f8, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00fc, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xffff, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00f9, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x003c, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xffff, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0027, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0019, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0021, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0006, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0045, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002a, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x000e, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002b, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00f4, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002c, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0004, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002d, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0014, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002e, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0003, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x002f, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0003, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0014, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0053, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0x0000, 0x0101},
- {0, 0x00a0, 0x0103},
- {0, 0x0078, 0x0105},
- {0, 0x0000, 0x010a},
- {0, 0x0024, 0x010b},
- {0, 0x0028, 0x0119},
- {0, 0x0088, 0x011b},
- {0, 0x0002, 0x011d},
- {0, 0x0003, 0x011e},
- {0, 0x0000, 0x0129},
- {0, 0x00fc, 0x012b},
- {0, 0x0008, 0x0102},
- {0, 0x0000, 0x0104},
- {0, 0x0008, 0x011a},
- {0, 0x0028, 0x011c},
- {0, 0x0021, 0x012a},
- {0, 0x0000, 0x0118},
- {0, 0x0000, 0x0132},
- {0, 0x0000, 0x0109},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0031, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x00dc, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0032, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0020, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0001, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0040, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0037, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0030, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0xfff9, 0x0124},
- {0, 0x0086, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0038, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0008, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0x0000, 0x0127},
- {0, 0xfff8, 0x0124},
- {0, 0xfffd, 0x0124},
- {0, 0xfffa, 0x0124},
- {0, 0x0003, 0x0111},
-};
-
-/* TESTME the old ibmcam driver repeats certain commands to Model1 cameras, we
- do the same for now (testing needed to see if this is really necessary) */
-static const int cit_model1_ntries = 5;
-static const int cit_model1_ntries2 = 2;
-
-static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
-{
- struct usb_device *udev = gspca_dev->dev;
- int err;
-
- err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
- value, index, NULL, 0, 1000);
- if (err < 0)
- pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
- index, value, err);
-
- return 0;
-}
-
-static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index, int verbose)
-{
- struct usb_device *udev = gspca_dev->dev;
- __u8 *buf = gspca_dev->usb_buf;
- int res;
-
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x01,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
- 0x00, index, buf, 8, 1000);
- if (res < 0) {
- pr_err("Failed to read a register (index 0x%04X, error %d)\n",
- index, res);
- return res;
- }
-
- if (verbose)
- PDEBUG(D_PROBE, "Register %04x value: %02x", index, buf[0]);
-
- return 0;
-}
-
-/*
- * cit_send_FF_04_02()
- *
- * This procedure sends magic 3-command prefix to the camera.
- * The purpose of this prefix is not known.
- *
- * History:
- * 1/2/00 Created.
- */
-static void cit_send_FF_04_02(struct gspca_dev *gspca_dev)
-{
- cit_write_reg(gspca_dev, 0x00FF, 0x0127);
- cit_write_reg(gspca_dev, 0x0004, 0x0124);
- cit_write_reg(gspca_dev, 0x0002, 0x0124);
-}
-
-static void cit_send_00_04_06(struct gspca_dev *gspca_dev)
-{
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0x0004, 0x0124);
- cit_write_reg(gspca_dev, 0x0006, 0x0124);
-}
-
-static void cit_send_x_00(struct gspca_dev *gspca_dev, unsigned short x)
-{
- cit_write_reg(gspca_dev, x, 0x0127);
- cit_write_reg(gspca_dev, 0x0000, 0x0124);
-}
-
-static void cit_send_x_00_05(struct gspca_dev *gspca_dev, unsigned short x)
-{
- cit_send_x_00(gspca_dev, x);
- cit_write_reg(gspca_dev, 0x0005, 0x0124);
-}
-
-static void cit_send_x_00_05_02(struct gspca_dev *gspca_dev, unsigned short x)
-{
- cit_write_reg(gspca_dev, x, 0x0127);
- cit_write_reg(gspca_dev, 0x0000, 0x0124);
- cit_write_reg(gspca_dev, 0x0005, 0x0124);
- cit_write_reg(gspca_dev, 0x0002, 0x0124);
-}
-
-static void cit_send_x_01_00_05(struct gspca_dev *gspca_dev, u16 x)
-{
- cit_write_reg(gspca_dev, x, 0x0127);
- cit_write_reg(gspca_dev, 0x0001, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0124);
- cit_write_reg(gspca_dev, 0x0005, 0x0124);
-}
-
-static void cit_send_x_00_05_02_01(struct gspca_dev *gspca_dev, u16 x)
-{
- cit_write_reg(gspca_dev, x, 0x0127);
- cit_write_reg(gspca_dev, 0x0000, 0x0124);
- cit_write_reg(gspca_dev, 0x0005, 0x0124);
- cit_write_reg(gspca_dev, 0x0002, 0x0124);
- cit_write_reg(gspca_dev, 0x0001, 0x0124);
-}
-
-static void cit_send_x_00_05_02_08_01(struct gspca_dev *gspca_dev, u16 x)
-{
- cit_write_reg(gspca_dev, x, 0x0127);
- cit_write_reg(gspca_dev, 0x0000, 0x0124);
- cit_write_reg(gspca_dev, 0x0005, 0x0124);
- cit_write_reg(gspca_dev, 0x0002, 0x0124);
- cit_write_reg(gspca_dev, 0x0008, 0x0124);
- cit_write_reg(gspca_dev, 0x0001, 0x0124);
-}
-
-static void cit_Packet_Format1(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
-{
- cit_send_x_01_00_05(gspca_dev, 0x0088);
- cit_send_x_00_05(gspca_dev, fkey);
- cit_send_x_00_05_02_08_01(gspca_dev, val);
- cit_send_x_00_05(gspca_dev, 0x0088);
- cit_send_x_00_05_02_01(gspca_dev, fkey);
- cit_send_x_00_05(gspca_dev, 0x0089);
- cit_send_x_00(gspca_dev, fkey);
- cit_send_00_04_06(gspca_dev);
- cit_read_reg(gspca_dev, 0x0126, 0);
- cit_send_FF_04_02(gspca_dev);
-}
-
-static void cit_PacketFormat2(struct gspca_dev *gspca_dev, u16 fkey, u16 val)
-{
- cit_send_x_01_00_05(gspca_dev, 0x0088);
- cit_send_x_00_05(gspca_dev, fkey);
- cit_send_x_00_05_02(gspca_dev, val);
-}
-
-static void cit_model2_Packet2(struct gspca_dev *gspca_dev)
-{
- cit_write_reg(gspca_dev, 0x00ff, 0x012d);
- cit_write_reg(gspca_dev, 0xfea3, 0x0124);
-}
-
-static void cit_model2_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
-{
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x00ff, 0x012e);
- cit_write_reg(gspca_dev, v1, 0x012f);
- cit_write_reg(gspca_dev, 0x00ff, 0x0130);
- cit_write_reg(gspca_dev, 0xc719, 0x0124);
- cit_write_reg(gspca_dev, v2, 0x0127);
-
- cit_model2_Packet2(gspca_dev);
-}
-
-/*
- * cit_model3_Packet1()
- *
- * 00_0078_012d
- * 00_0097_012f
- * 00_d141_0124
- * 00_0096_0127
- * 00_fea8_0124
-*/
-static void cit_model3_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
-{
- cit_write_reg(gspca_dev, 0x0078, 0x012d);
- cit_write_reg(gspca_dev, v1, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, v2, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
-}
-
-static void cit_model4_Packet1(struct gspca_dev *gspca_dev, u16 v1, u16 v2)
-{
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, v1, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, v2, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
-}
-
-static void cit_model4_BrightnessPacket(struct gspca_dev *gspca_dev, u16 val)
-{
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0026, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, val, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0038, 0x012d);
- cit_write_reg(gspca_dev, 0x0004, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
-
- sd->model = id->driver_info;
- if (sd->model == CIT_MODEL3 && ibm_netcam_pro)
- sd->model = CIT_IBM_NETCAM_PRO;
-
- cam = &gspca_dev->cam;
- switch (sd->model) {
- case CIT_MODEL0:
- cam->cam_mode = model0_mode;
- cam->nmodes = ARRAY_SIZE(model0_mode);
- sd->sof_len = 4;
- break;
- case CIT_MODEL1:
- cam->cam_mode = cif_yuv_mode;
- cam->nmodes = ARRAY_SIZE(cif_yuv_mode);
- sd->sof_len = 4;
- break;
- case CIT_MODEL2:
- cam->cam_mode = model2_mode + 1; /* no 160x120 */
- cam->nmodes = 3;
- break;
- case CIT_MODEL3:
- cam->cam_mode = vga_yuv_mode;
- cam->nmodes = ARRAY_SIZE(vga_yuv_mode);
- sd->stop_on_control_change = 1;
- sd->sof_len = 4;
- break;
- case CIT_MODEL4:
- cam->cam_mode = model2_mode;
- cam->nmodes = ARRAY_SIZE(model2_mode);
- break;
- case CIT_IBM_NETCAM_PRO:
- cam->cam_mode = vga_yuv_mode;
- cam->nmodes = 2; /* no 640 x 480 */
- cam->input_flags = V4L2_IN_ST_VFLIP;
- sd->stop_on_control_change = 1;
- sd->sof_len = 4;
- break;
- }
-
- return 0;
-}
-
-static int cit_init_model0(struct gspca_dev *gspca_dev)
-{
- cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
- cit_write_reg(gspca_dev, 0x0001, 0x0112); /* turn on autogain ? */
- cit_write_reg(gspca_dev, 0x0000, 0x0400);
- cit_write_reg(gspca_dev, 0x0001, 0x0400);
- cit_write_reg(gspca_dev, 0x0000, 0x0420);
- cit_write_reg(gspca_dev, 0x0001, 0x0420);
- cit_write_reg(gspca_dev, 0x000d, 0x0409);
- cit_write_reg(gspca_dev, 0x0002, 0x040a);
- cit_write_reg(gspca_dev, 0x0018, 0x0405);
- cit_write_reg(gspca_dev, 0x0008, 0x0435);
- cit_write_reg(gspca_dev, 0x0026, 0x040b);
- cit_write_reg(gspca_dev, 0x0007, 0x0437);
- cit_write_reg(gspca_dev, 0x0015, 0x042f);
- cit_write_reg(gspca_dev, 0x002b, 0x0439);
- cit_write_reg(gspca_dev, 0x0026, 0x043a);
- cit_write_reg(gspca_dev, 0x0008, 0x0438);
- cit_write_reg(gspca_dev, 0x001e, 0x042b);
- cit_write_reg(gspca_dev, 0x0041, 0x042c);
-
- return 0;
-}
-
-static int cit_init_ibm_netcam_pro(struct gspca_dev *gspca_dev)
-{
- cit_read_reg(gspca_dev, 0x128, 1);
- cit_write_reg(gspca_dev, 0x0003, 0x0133);
- cit_write_reg(gspca_dev, 0x0000, 0x0117);
- cit_write_reg(gspca_dev, 0x0008, 0x0123);
- cit_write_reg(gspca_dev, 0x0000, 0x0100);
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- cit_write_reg(gspca_dev, 0x0002, 0x0112);
- cit_write_reg(gspca_dev, 0x0000, 0x0133);
- cit_write_reg(gspca_dev, 0x0000, 0x0123);
- cit_write_reg(gspca_dev, 0x0001, 0x0117);
- cit_write_reg(gspca_dev, 0x0040, 0x0108);
- cit_write_reg(gspca_dev, 0x0019, 0x012c);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- cit_write_reg(gspca_dev, 0x0002, 0x0115);
- cit_write_reg(gspca_dev, 0x000b, 0x0115);
-
- cit_write_reg(gspca_dev, 0x0078, 0x012d);
- cit_write_reg(gspca_dev, 0x0001, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0079, 0x012d);
- cit_write_reg(gspca_dev, 0x00ff, 0x0130);
- cit_write_reg(gspca_dev, 0xcd41, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_read_reg(gspca_dev, 0x0126, 1);
-
- cit_model3_Packet1(gspca_dev, 0x0000, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0000, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x000b, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x000c, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x000d, 0x003a);
- cit_model3_Packet1(gspca_dev, 0x000e, 0x0060);
- cit_model3_Packet1(gspca_dev, 0x000f, 0x0060);
- cit_model3_Packet1(gspca_dev, 0x0010, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0011, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x0012, 0x0028);
- cit_model3_Packet1(gspca_dev, 0x0013, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0014, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0015, 0x00fb);
- cit_model3_Packet1(gspca_dev, 0x0016, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0017, 0x0037);
- cit_model3_Packet1(gspca_dev, 0x0018, 0x0036);
- cit_model3_Packet1(gspca_dev, 0x001e, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x001f, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0020, 0x00c1);
- cit_model3_Packet1(gspca_dev, 0x0021, 0x0034);
- cit_model3_Packet1(gspca_dev, 0x0022, 0x0034);
- cit_model3_Packet1(gspca_dev, 0x0025, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0028, 0x0022);
- cit_model3_Packet1(gspca_dev, 0x0029, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x002b, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x002c, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x002d, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x002e, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x002f, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x0030, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x0031, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x0032, 0x0007);
- cit_model3_Packet1(gspca_dev, 0x0033, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x0037, 0x0040);
- cit_model3_Packet1(gspca_dev, 0x0039, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x003a, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x003b, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x003c, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0040, 0x000c);
- cit_model3_Packet1(gspca_dev, 0x0041, 0x00fb);
- cit_model3_Packet1(gspca_dev, 0x0042, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0043, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0045, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0048, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x004a, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x004b, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x004c, 0x00ff);
- cit_model3_Packet1(gspca_dev, 0x004f, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0050, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0051, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0055, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0056, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0057, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0058, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0059, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x005c, 0x0016);
- cit_model3_Packet1(gspca_dev, 0x005d, 0x0022);
- cit_model3_Packet1(gspca_dev, 0x005e, 0x003c);
- cit_model3_Packet1(gspca_dev, 0x005f, 0x0050);
- cit_model3_Packet1(gspca_dev, 0x0060, 0x0044);
- cit_model3_Packet1(gspca_dev, 0x0061, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x006a, 0x007e);
- cit_model3_Packet1(gspca_dev, 0x006f, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0072, 0x001b);
- cit_model3_Packet1(gspca_dev, 0x0073, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x0074, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0075, 0x001b);
- cit_model3_Packet1(gspca_dev, 0x0076, 0x002a);
- cit_model3_Packet1(gspca_dev, 0x0077, 0x003c);
- cit_model3_Packet1(gspca_dev, 0x0078, 0x0050);
- cit_model3_Packet1(gspca_dev, 0x007b, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x007c, 0x0011);
- cit_model3_Packet1(gspca_dev, 0x007d, 0x0024);
- cit_model3_Packet1(gspca_dev, 0x007e, 0x0043);
- cit_model3_Packet1(gspca_dev, 0x007f, 0x005a);
- cit_model3_Packet1(gspca_dev, 0x0084, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x0085, 0x0033);
- cit_model3_Packet1(gspca_dev, 0x0086, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0087, 0x0030);
- cit_model3_Packet1(gspca_dev, 0x0088, 0x0070);
- cit_model3_Packet1(gspca_dev, 0x008b, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x008f, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0090, 0x0006);
- cit_model3_Packet1(gspca_dev, 0x0091, 0x0028);
- cit_model3_Packet1(gspca_dev, 0x0092, 0x005a);
- cit_model3_Packet1(gspca_dev, 0x0093, 0x0082);
- cit_model3_Packet1(gspca_dev, 0x0096, 0x0014);
- cit_model3_Packet1(gspca_dev, 0x0097, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x0098, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00b0, 0x0046);
- cit_model3_Packet1(gspca_dev, 0x00b1, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00b2, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00b3, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x00b4, 0x0007);
- cit_model3_Packet1(gspca_dev, 0x00b6, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x00b7, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x00bb, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00bc, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00bd, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00bf, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00c0, 0x00c8);
- cit_model3_Packet1(gspca_dev, 0x00c1, 0x0014);
- cit_model3_Packet1(gspca_dev, 0x00c2, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00c3, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00c4, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x00cb, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00cc, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00cd, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00ce, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00cf, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x00d0, 0x0040);
- cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00d1, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00d2, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00d3, 0x00bf);
- cit_model3_Packet1(gspca_dev, 0x00ea, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x00eb, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00ec, 0x00e8);
- cit_model3_Packet1(gspca_dev, 0x00ed, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00ef, 0x0022);
- cit_model3_Packet1(gspca_dev, 0x00f0, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00f2, 0x0028);
- cit_model3_Packet1(gspca_dev, 0x00f4, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x00f5, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00fa, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00fb, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00fc, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00fd, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00fe, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00ff, 0x0000);
-
- cit_model3_Packet1(gspca_dev, 0x00be, 0x0003);
- cit_model3_Packet1(gspca_dev, 0x00c8, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00c9, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x00ca, 0x0040);
- cit_model3_Packet1(gspca_dev, 0x0053, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0082, 0x000e);
- cit_model3_Packet1(gspca_dev, 0x0083, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x0034, 0x003c);
- cit_model3_Packet1(gspca_dev, 0x006e, 0x0055);
- cit_model3_Packet1(gspca_dev, 0x0062, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x0063, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0066, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0067, 0x0006);
- cit_model3_Packet1(gspca_dev, 0x006b, 0x0010);
- cit_model3_Packet1(gspca_dev, 0x005a, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x005b, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0023, 0x0006);
- cit_model3_Packet1(gspca_dev, 0x0026, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x0036, 0x0069);
- cit_model3_Packet1(gspca_dev, 0x0038, 0x0064);
- cit_model3_Packet1(gspca_dev, 0x003d, 0x0003);
- cit_model3_Packet1(gspca_dev, 0x003e, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00b8, 0x0014);
- cit_model3_Packet1(gspca_dev, 0x00b9, 0x0014);
- cit_model3_Packet1(gspca_dev, 0x00e6, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x00e8, 0x0001);
-
- 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;
-
- switch (sd->model) {
- case CIT_MODEL0:
- cit_init_model0(gspca_dev);
- sd_stop0(gspca_dev);
- break;
- case CIT_MODEL1:
- case CIT_MODEL2:
- case CIT_MODEL3:
- case CIT_MODEL4:
- break; /* All is done in sd_start */
- case CIT_IBM_NETCAM_PRO:
- cit_init_ibm_netcam_pro(gspca_dev);
- sd_stop0(gspca_dev);
- break;
- }
- return 0;
-}
-
-static int cit_set_brightness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_IBM_NETCAM_PRO:
- /* No (known) brightness control for these */
- break;
- case CIT_MODEL1:
- /* Model 1: Brightness range 0 - 63 */
- cit_Packet_Format1(gspca_dev, 0x0031, val);
- cit_Packet_Format1(gspca_dev, 0x0032, val);
- cit_Packet_Format1(gspca_dev, 0x0033, val);
- break;
- case CIT_MODEL2:
- /* Model 2: Brightness range 0x60 - 0xee */
- /* Scale 0 - 63 to 0x60 - 0xee */
- i = 0x60 + val * 2254 / 1000;
- cit_model2_Packet1(gspca_dev, 0x001a, i);
- break;
- case CIT_MODEL3:
- /* Model 3: Brightness range 'i' in [0x0C..0x3F] */
- i = val;
- if (i < 0x0c)
- i = 0x0c;
- cit_model3_Packet1(gspca_dev, 0x0036, i);
- break;
- case CIT_MODEL4:
- /* Model 4: Brightness range 'i' in [0x04..0xb4] */
- /* Scale 0 - 63 to 0x04 - 0xb4 */
- i = 0x04 + val * 2794 / 1000;
- cit_model4_BrightnessPacket(gspca_dev, i);
- break;
- }
-
- return 0;
-}
-
-static int cit_set_contrast(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0: {
- int i;
- /* gain 0-15, 0-20 -> 0-15 */
- i = val * 1000 / 1333;
- cit_write_reg(gspca_dev, i, 0x0422);
- /* gain 0-31, may not be lower then 0x0422, 0-20 -> 0-31 */
- i = val * 2000 / 1333;
- cit_write_reg(gspca_dev, i, 0x0423);
- /* gain 0-127, may not be lower then 0x0423, 0-20 -> 0-63 */
- i = val * 4000 / 1333;
- cit_write_reg(gspca_dev, i, 0x0424);
- /* gain 0-127, may not be lower then 0x0424, , 0-20 -> 0-127 */
- i = val * 8000 / 1333;
- cit_write_reg(gspca_dev, i, 0x0425);
- break;
- }
- case CIT_MODEL2:
- case CIT_MODEL4:
- /* These models do not have this control. */
- break;
- case CIT_MODEL1:
- {
- /* Scale 0 - 20 to 15 - 0 */
- int i, new_contrast = (20 - val) * 1000 / 1333;
- for (i = 0; i < cit_model1_ntries; i++) {
- cit_Packet_Format1(gspca_dev, 0x0014, new_contrast);
- cit_send_FF_04_02(gspca_dev);
- }
- break;
- }
- case CIT_MODEL3:
- { /* Preset hardware values */
- static const struct {
- unsigned short cv1;
- unsigned short cv2;
- unsigned short cv3;
- } cv[7] = {
- { 0x05, 0x05, 0x0f }, /* Minimum */
- { 0x04, 0x04, 0x16 },
- { 0x02, 0x03, 0x16 },
- { 0x02, 0x08, 0x16 },
- { 0x01, 0x0c, 0x16 },
- { 0x01, 0x0e, 0x16 },
- { 0x01, 0x10, 0x16 } /* Maximum */
- };
- int i = val / 3;
- cit_model3_Packet1(gspca_dev, 0x0067, cv[i].cv1);
- cit_model3_Packet1(gspca_dev, 0x005b, cv[i].cv2);
- cit_model3_Packet1(gspca_dev, 0x005c, cv[i].cv3);
- break;
- }
- case CIT_IBM_NETCAM_PRO:
- cit_model3_Packet1(gspca_dev, 0x005b, val + 1);
- break;
- }
- return 0;
-}
-
-static int cit_set_hue(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_MODEL1:
- case CIT_IBM_NETCAM_PRO:
- /* No hue control for these models */
- break;
- case CIT_MODEL2:
- cit_model2_Packet1(gspca_dev, 0x0024, val);
- /* cit_model2_Packet1(gspca_dev, 0x0020, sat); */
- break;
- case CIT_MODEL3: {
- /* Model 3: Brightness range 'i' in [0x05..0x37] */
- /* TESTME according to the ibmcam driver this does not work */
- if (0) {
- /* Scale 0 - 127 to 0x05 - 0x37 */
- int i = 0x05 + val * 1000 / 2540;
- cit_model3_Packet1(gspca_dev, 0x007e, i);
- }
- break;
- }
- case CIT_MODEL4:
- /* HDG: taken from ibmcam, setting the color gains does not
- * really belong here.
- *
- * I am not sure r/g/b_gain variables exactly control gain
- * of those channels. Most likely they subtly change some
- * very internal image processing settings in the camera.
- * In any case, here is what they do, and feel free to tweak:
- *
- * r_gain: seriously affects red gain
- * g_gain: seriously affects green gain
- * b_gain: seriously affects blue gain
- * hue: changes average color from violet (0) to red (0xFF)
- */
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 160, 0x0127); /* Green gain */
- cit_write_reg(gspca_dev, 160, 0x012e); /* Red gain */
- cit_write_reg(gspca_dev, 160, 0x0130); /* Blue gain */
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, val, 0x012d); /* Hue */
- cit_write_reg(gspca_dev, 0xf545, 0x0124);
- break;
- }
- return 0;
-}
-
-static int cit_set_sharpness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_MODEL2:
- case CIT_MODEL4:
- case CIT_IBM_NETCAM_PRO:
- /* These models do not have this control */
- break;
- case CIT_MODEL1: {
- int i;
- const unsigned short sa[] = {
- 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a };
-
- for (i = 0; i < cit_model1_ntries; i++)
- cit_PacketFormat2(gspca_dev, 0x0013, sa[val]);
- break;
- }
- case CIT_MODEL3:
- { /*
- * "Use a table of magic numbers.
- * This setting doesn't really change much.
- * But that's how Windows does it."
- */
- static const struct {
- unsigned short sv1;
- unsigned short sv2;
- unsigned short sv3;
- unsigned short sv4;
- } sv[7] = {
- { 0x00, 0x00, 0x05, 0x14 }, /* Smoothest */
- { 0x01, 0x04, 0x05, 0x14 },
- { 0x02, 0x04, 0x05, 0x14 },
- { 0x03, 0x04, 0x05, 0x14 },
- { 0x03, 0x05, 0x05, 0x14 },
- { 0x03, 0x06, 0x05, 0x14 },
- { 0x03, 0x07, 0x05, 0x14 } /* Sharpest */
- };
- cit_model3_Packet1(gspca_dev, 0x0060, sv[val].sv1);
- cit_model3_Packet1(gspca_dev, 0x0061, sv[val].sv2);
- cit_model3_Packet1(gspca_dev, 0x0062, sv[val].sv3);
- cit_model3_Packet1(gspca_dev, 0x0063, sv[val].sv4);
- break;
- }
- }
- return 0;
-}
-
-/*
- * cit_set_lighting()
- *
- * Camera model 1:
- * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low.
- *
- * Camera model 2:
- * We have 16 levels of lighting, 0 for bright light and up to 15 for
- * low light. But values above 5 or so are useless because camera is
- * not really capable to produce anything worth viewing at such light.
- * This setting may be altered only in certain camera state.
- *
- * Low lighting forces slower FPS.
- *
- * History:
- * 1/5/00 Created.
- * 2/20/00 Added support for Model 2 cameras.
- */
-static void cit_set_lighting(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_MODEL2:
- case CIT_MODEL3:
- case CIT_MODEL4:
- case CIT_IBM_NETCAM_PRO:
- break;
- case CIT_MODEL1: {
- int i;
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x0027, val);
- break;
- }
- }
-}
-
-static void cit_set_hflip(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0:
- if (val)
- cit_write_reg(gspca_dev, 0x0020, 0x0115);
- else
- cit_write_reg(gspca_dev, 0x0040, 0x0115);
- break;
- case CIT_MODEL1:
- case CIT_MODEL2:
- case CIT_MODEL3:
- case CIT_MODEL4:
- case CIT_IBM_NETCAM_PRO:
- break;
- }
-}
-
-static int cit_restart_stream(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_MODEL1:
- cit_write_reg(gspca_dev, 0x0001, 0x0114);
- /* Fall through */
- case CIT_MODEL2:
- case CIT_MODEL4:
- cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
- usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
- break;
- case CIT_MODEL3:
- case CIT_IBM_NETCAM_PRO:
- cit_write_reg(gspca_dev, 0x0001, 0x0114);
- cit_write_reg(gspca_dev, 0x00c0, 0x010c); /* Go! */
- usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
- /* Clear button events from while we were not streaming */
- cit_write_reg(gspca_dev, 0x0001, 0x0113);
- break;
- }
-
- sd->sof_read = 0;
-
- return 0;
-}
-
-static int cit_get_packet_size(struct gspca_dev *gspca_dev)
-{
- struct usb_host_interface *alt;
- struct usb_interface *intf;
-
- intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
- if (!alt) {
- pr_err("Couldn't get altsetting\n");
- return -EIO;
- }
-
- return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
-}
-
-/* Calculate the clockdiv giving us max fps given the available bandwidth */
-static int cit_get_clock_div(struct gspca_dev *gspca_dev)
-{
- int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
- int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
- int packet_size;
-
- packet_size = cit_get_packet_size(gspca_dev);
- if (packet_size < 0)
- return packet_size;
-
- while (clock_div > 3 &&
- 1000 * packet_size >
- gspca_dev->width * gspca_dev->height *
- fps[clock_div - 1] * 3 / 2)
- clock_div--;
-
- PDEBUG(D_PROBE,
- "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
- packet_size, gspca_dev->width, gspca_dev->height, clock_div,
- fps[clock_div]);
-
- return clock_div;
-}
-
-static int cit_start_model0(struct gspca_dev *gspca_dev)
-{
- const unsigned short compression = 0; /* 0=none, 7=best frame rate */
- int clock_div;
-
- clock_div = cit_get_clock_div(gspca_dev);
- if (clock_div < 0)
- return clock_div;
-
- cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
- cit_write_reg(gspca_dev, 0x0003, 0x0438);
- cit_write_reg(gspca_dev, 0x001e, 0x042b);
- cit_write_reg(gspca_dev, 0x0041, 0x042c);
- cit_write_reg(gspca_dev, 0x0008, 0x0436);
- cit_write_reg(gspca_dev, 0x0024, 0x0403);
- cit_write_reg(gspca_dev, 0x002c, 0x0404);
- cit_write_reg(gspca_dev, 0x0002, 0x0426);
- cit_write_reg(gspca_dev, 0x0014, 0x0427);
-
- switch (gspca_dev->width) {
- case 160: /* 160x120 */
- cit_write_reg(gspca_dev, 0x0004, 0x010b);
- cit_write_reg(gspca_dev, 0x0001, 0x010a);
- cit_write_reg(gspca_dev, 0x0010, 0x0102);
- cit_write_reg(gspca_dev, 0x00a0, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x0078, 0x0105);
- break;
-
- case 176: /* 176x144 */
- cit_write_reg(gspca_dev, 0x0006, 0x010b);
- cit_write_reg(gspca_dev, 0x0000, 0x010a);
- cit_write_reg(gspca_dev, 0x0005, 0x0102);
- cit_write_reg(gspca_dev, 0x00b0, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x0090, 0x0105);
- break;
-
- case 320: /* 320x240 */
- cit_write_reg(gspca_dev, 0x0008, 0x010b);
- cit_write_reg(gspca_dev, 0x0004, 0x010a);
- cit_write_reg(gspca_dev, 0x0005, 0x0102);
- cit_write_reg(gspca_dev, 0x00a0, 0x0103);
- cit_write_reg(gspca_dev, 0x0010, 0x0104);
- cit_write_reg(gspca_dev, 0x0078, 0x0105);
- break;
- }
-
- cit_write_reg(gspca_dev, compression, 0x0109);
- cit_write_reg(gspca_dev, clock_div, 0x0111);
-
- return 0;
-}
-
-static int cit_start_model1(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, clock_div;
-
- clock_div = cit_get_clock_div(gspca_dev);
- if (clock_div < 0)
- return clock_div;
-
- cit_read_reg(gspca_dev, 0x0128, 1);
- cit_read_reg(gspca_dev, 0x0100, 0);
- cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
- cit_read_reg(gspca_dev, 0x0100, 0);
- cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
- cit_read_reg(gspca_dev, 0x0100, 0);
- cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
- cit_write_reg(gspca_dev, 0x01, 0x0108);
-
- cit_write_reg(gspca_dev, 0x03, 0x0112);
- cit_read_reg(gspca_dev, 0x0115, 0);
- cit_write_reg(gspca_dev, 0x06, 0x0115);
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x44, 0x0116);
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x40, 0x0116);
- cit_read_reg(gspca_dev, 0x0115, 0);
- cit_write_reg(gspca_dev, 0x0e, 0x0115);
- cit_write_reg(gspca_dev, 0x19, 0x012c);
-
- cit_Packet_Format1(gspca_dev, 0x00, 0x1e);
- cit_Packet_Format1(gspca_dev, 0x39, 0x0d);
- cit_Packet_Format1(gspca_dev, 0x39, 0x09);
- cit_Packet_Format1(gspca_dev, 0x3b, 0x00);
- cit_Packet_Format1(gspca_dev, 0x28, 0x22);
- cit_Packet_Format1(gspca_dev, 0x27, 0x00);
- cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
- cit_Packet_Format1(gspca_dev, 0x39, 0x08);
-
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x2c, 0x00);
-
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x30, 0x14);
-
- cit_PacketFormat2(gspca_dev, 0x39, 0x02);
- cit_PacketFormat2(gspca_dev, 0x01, 0xe1);
- cit_PacketFormat2(gspca_dev, 0x02, 0xcd);
- cit_PacketFormat2(gspca_dev, 0x03, 0xcd);
- cit_PacketFormat2(gspca_dev, 0x04, 0xfa);
- cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
- cit_PacketFormat2(gspca_dev, 0x39, 0x00);
-
- cit_PacketFormat2(gspca_dev, 0x39, 0x02);
- cit_PacketFormat2(gspca_dev, 0x0a, 0x37);
- cit_PacketFormat2(gspca_dev, 0x0b, 0xb8);
- cit_PacketFormat2(gspca_dev, 0x0c, 0xf3);
- cit_PacketFormat2(gspca_dev, 0x0d, 0xe3);
- cit_PacketFormat2(gspca_dev, 0x0e, 0x0d);
- cit_PacketFormat2(gspca_dev, 0x0f, 0xf2);
- cit_PacketFormat2(gspca_dev, 0x10, 0xd5);
- cit_PacketFormat2(gspca_dev, 0x11, 0xba);
- cit_PacketFormat2(gspca_dev, 0x12, 0x53);
- cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
- cit_PacketFormat2(gspca_dev, 0x39, 0x00);
-
- cit_PacketFormat2(gspca_dev, 0x39, 0x02);
- cit_PacketFormat2(gspca_dev, 0x16, 0x00);
- cit_PacketFormat2(gspca_dev, 0x17, 0x28);
- cit_PacketFormat2(gspca_dev, 0x18, 0x7d);
- cit_PacketFormat2(gspca_dev, 0x19, 0xbe);
- cit_PacketFormat2(gspca_dev, 0x3f, 0xff);
- cit_PacketFormat2(gspca_dev, 0x39, 0x00);
-
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x00, 0x18);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x13, 0x18);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x14, 0x06);
-
- /* TESTME These are handled through controls
- KEEP until someone can test leaving this out is ok */
- if (0) {
- /* This is default brightness */
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x31, 0x37);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x32, 0x46);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x33, 0x55);
- }
-
- cit_Packet_Format1(gspca_dev, 0x2e, 0x04);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x2d, 0x04);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x29, 0x80);
- cit_Packet_Format1(gspca_dev, 0x2c, 0x01);
- cit_Packet_Format1(gspca_dev, 0x30, 0x17);
- cit_Packet_Format1(gspca_dev, 0x39, 0x08);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x34, 0x00);
-
- cit_write_reg(gspca_dev, 0x00, 0x0101);
- cit_write_reg(gspca_dev, 0x00, 0x010a);
-
- switch (gspca_dev->width) {
- case 128: /* 128x96 */
- cit_write_reg(gspca_dev, 0x80, 0x0103);
- cit_write_reg(gspca_dev, 0x60, 0x0105);
- cit_write_reg(gspca_dev, 0x0c, 0x010b);
- cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x0b, 0x011d);
- cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x00, 0x0129);
- break;
- case 176: /* 176x144 */
- cit_write_reg(gspca_dev, 0xb0, 0x0103);
- cit_write_reg(gspca_dev, 0x8f, 0x0105);
- cit_write_reg(gspca_dev, 0x06, 0x010b);
- cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x0d, 0x011d);
- cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x03, 0x0129);
- break;
- case 352: /* 352x288 */
- cit_write_reg(gspca_dev, 0xb0, 0x0103);
- cit_write_reg(gspca_dev, 0x90, 0x0105);
- cit_write_reg(gspca_dev, 0x02, 0x010b);
- cit_write_reg(gspca_dev, 0x04, 0x011b); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x05, 0x011d);
- cit_write_reg(gspca_dev, 0x00, 0x011e); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x00, 0x0129);
- break;
- }
-
- cit_write_reg(gspca_dev, 0xff, 0x012b);
-
- /* TESTME These are handled through controls
- KEEP until someone can test leaving this out is ok */
- if (0) {
- /* This is another brightness - don't know why */
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x31, 0xc3);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x32, 0xd2);
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x33, 0xe1);
-
- /* Default contrast */
- for (i = 0; i < cit_model1_ntries; i++)
- cit_Packet_Format1(gspca_dev, 0x14, 0x0a);
-
- /* Default sharpness */
- for (i = 0; i < cit_model1_ntries2; i++)
- cit_PacketFormat2(gspca_dev, 0x13, 0x1a);
-
- /* Default lighting conditions */
- cit_Packet_Format1(gspca_dev, 0x0027,
- v4l2_ctrl_g_ctrl(sd->lighting));
- }
-
- /* Assorted init */
- switch (gspca_dev->width) {
- case 128: /* 128x96 */
- cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
- cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x36, 0x0102);
- cit_write_reg(gspca_dev, 0x1a, 0x0104);
- cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x2b, 0x011c);
- cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
- break;
- case 176: /* 176x144 */
- cit_Packet_Format1(gspca_dev, 0x2b, 0x1e);
- cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x04, 0x0102);
- cit_write_reg(gspca_dev, 0x02, 0x0104);
- cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x2b, 0x011c);
- cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
- break;
- case 352: /* 352x288 */
- cit_Packet_Format1(gspca_dev, 0x2b, 0x1f);
- cit_write_reg(gspca_dev, 0xc9, 0x0119); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x80, 0x0109); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x08, 0x0102);
- cit_write_reg(gspca_dev, 0x01, 0x0104);
- cit_write_reg(gspca_dev, 0x04, 0x011a); /* Same everywhere */
- cit_write_reg(gspca_dev, 0x2f, 0x011c);
- cit_write_reg(gspca_dev, 0x23, 0x012a); /* Same everywhere */
- break;
- }
-
- cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On */
- cit_write_reg(gspca_dev, clock_div, 0x0111);
-
- return 0;
-}
-
-static int cit_start_model2(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int clock_div = 0;
-
- cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- cit_write_reg(gspca_dev, 0x0002, 0x0112);
- cit_write_reg(gspca_dev, 0x00bc, 0x012c);
- cit_write_reg(gspca_dev, 0x0008, 0x012b);
- cit_write_reg(gspca_dev, 0x0000, 0x0108);
- cit_write_reg(gspca_dev, 0x0001, 0x0133);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- switch (gspca_dev->width) {
- case 176: /* 176x144 */
- cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
- cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
- cit_write_reg(gspca_dev, 0x00b9, 0x010a); /* Unique to this mode */
- cit_write_reg(gspca_dev, 0x0038, 0x0119); /* Unique to this mode */
- /* TESTME HDG: this does not seem right
- (it is 2 for all other resolutions) */
- sd->sof_len = 10;
- break;
- case 320: /* 320x240 */
- cit_write_reg(gspca_dev, 0x0028, 0x0103); /* Unique to this mode */
- cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
- cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
- cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
- sd->sof_len = 2;
- break;
- /* case VIDEOSIZE_352x240: */
- cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
- cit_write_reg(gspca_dev, 0x001e, 0x0105); /* 320x240, 352x240 */
- cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
- sd->sof_len = 2;
- break;
- case 352: /* 352x288 */
- cit_write_reg(gspca_dev, 0x002c, 0x0103); /* All except 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x0104); /* Same */
- cit_write_reg(gspca_dev, 0x0024, 0x0105); /* 176x144, 352x288 */
- cit_write_reg(gspca_dev, 0x0039, 0x010a); /* All except 176x144 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119); /* All except 176x144 */
- sd->sof_len = 2;
- break;
- }
-
- cit_write_reg(gspca_dev, 0x0000, 0x0100); /* LED on */
-
- switch (gspca_dev->width) {
- case 176: /* 176x144 */
- cit_write_reg(gspca_dev, 0x0050, 0x0111);
- cit_write_reg(gspca_dev, 0x00d0, 0x0111);
- break;
- case 320: /* 320x240 */
- case 352: /* 352x288 */
- cit_write_reg(gspca_dev, 0x0040, 0x0111);
- cit_write_reg(gspca_dev, 0x00c0, 0x0111);
- break;
- }
- cit_write_reg(gspca_dev, 0x009b, 0x010f);
- cit_write_reg(gspca_dev, 0x00bb, 0x010f);
-
- /*
- * Hardware settings, may affect CMOS sensor; not user controls!
- * -------------------------------------------------------------
- * 0x0004: no effect
- * 0x0006: hardware effect
- * 0x0008: no effect
- * 0x000a: stops video stream, probably important h/w setting
- * 0x000c: changes color in hardware manner (not user setting)
- * 0x0012: changes number of colors (does not affect speed)
- * 0x002a: no effect
- * 0x002c: hardware setting (related to scan lines)
- * 0x002e: stops video stream, probably important h/w setting
- */
- cit_model2_Packet1(gspca_dev, 0x000a, 0x005c);
- cit_model2_Packet1(gspca_dev, 0x0004, 0x0000);
- cit_model2_Packet1(gspca_dev, 0x0006, 0x00fb);
- cit_model2_Packet1(gspca_dev, 0x0008, 0x0000);
- cit_model2_Packet1(gspca_dev, 0x000c, 0x0009);
- cit_model2_Packet1(gspca_dev, 0x0012, 0x000a);
- cit_model2_Packet1(gspca_dev, 0x002a, 0x0000);
- cit_model2_Packet1(gspca_dev, 0x002c, 0x0000);
- cit_model2_Packet1(gspca_dev, 0x002e, 0x0008);
-
- /*
- * Function 0x0030 pops up all over the place. Apparently
- * it is a hardware control register, with every bit assigned to
- * do something.
- */
- cit_model2_Packet1(gspca_dev, 0x0030, 0x0000);
-
- /*
- * Magic control of CMOS sensor. Only lower values like
- * 0-3 work, and picture shifts left or right. Don't change.
- */
- switch (gspca_dev->width) {
- case 176: /* 176x144 */
- cit_model2_Packet1(gspca_dev, 0x0014, 0x0002);
- cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
- cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
- clock_div = 6;
- break;
- case 320: /* 320x240 */
- cit_model2_Packet1(gspca_dev, 0x0014, 0x0009);
- cit_model2_Packet1(gspca_dev, 0x0016, 0x0005); /* Horizontal shift */
- cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Another hardware setting */
- clock_div = 8;
- break;
- /* case VIDEOSIZE_352x240: */
- /* This mode doesn't work as Windows programs it; changed to work */
- cit_model2_Packet1(gspca_dev, 0x0014, 0x0009); /* Windows sets this to 8 */
- cit_model2_Packet1(gspca_dev, 0x0016, 0x0003); /* Horizontal shift */
- cit_model2_Packet1(gspca_dev, 0x0018, 0x0044); /* Windows sets this to 0x0045 */
- clock_div = 10;
- break;
- case 352: /* 352x288 */
- cit_model2_Packet1(gspca_dev, 0x0014, 0x0003);
- cit_model2_Packet1(gspca_dev, 0x0016, 0x0002); /* Horizontal shift */
- cit_model2_Packet1(gspca_dev, 0x0018, 0x004a); /* Another hardware setting */
- clock_div = 16;
- break;
- }
-
- /* TESTME These are handled through controls
- KEEP until someone can test leaving this out is ok */
- if (0)
- cit_model2_Packet1(gspca_dev, 0x001a, 0x005a);
-
- /*
- * We have our own frame rate setting varying from 0 (slowest) to 6
- * (fastest). The camera model 2 allows frame rate in range [0..0x1F]
- # where 0 is also the slowest setting. However for all practical
- # reasons high settings make no sense because USB is not fast enough
- # to support high FPS. Be aware that the picture datastream will be
- # severely disrupted if you ask for frame rate faster than allowed
- # for the video size - see below:
- *
- * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz):
- * -----------------------------------------------------------------
- * 176x144: [6..31]
- * 320x240: [8..31]
- * 352x240: [10..31]
- * 352x288: [16..31] I have to raise lower threshold for stability...
- *
- * As usual, slower FPS provides better sensitivity.
- */
- cit_model2_Packet1(gspca_dev, 0x001c, clock_div);
-
- /*
- * This setting does not visibly affect pictures; left it here
- * because it was present in Windows USB data stream. This function
- * does not allow arbitrary values and apparently is a bit mask, to
- * be activated only at appropriate time. Don't change it randomly!
- */
- switch (gspca_dev->width) {
- case 176: /* 176x144 */
- cit_model2_Packet1(gspca_dev, 0x0026, 0x00c2);
- break;
- case 320: /* 320x240 */
- cit_model2_Packet1(gspca_dev, 0x0026, 0x0044);
- break;
- /* case VIDEOSIZE_352x240: */
- cit_model2_Packet1(gspca_dev, 0x0026, 0x0046);
- break;
- case 352: /* 352x288 */
- cit_model2_Packet1(gspca_dev, 0x0026, 0x0048);
- break;
- }
-
- cit_model2_Packet1(gspca_dev, 0x0028, v4l2_ctrl_g_ctrl(sd->lighting));
- /* model2 cannot change the backlight compensation while streaming */
- v4l2_ctrl_grab(sd->lighting, true);
-
- /* color balance rg2 */
- cit_model2_Packet1(gspca_dev, 0x001e, 0x002f);
- /* saturation */
- cit_model2_Packet1(gspca_dev, 0x0020, 0x0034);
- /* color balance yb */
- cit_model2_Packet1(gspca_dev, 0x0022, 0x00a0);
-
- /* Hardware control command */
- cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
-
- return 0;
-}
-
-static int cit_start_model3(struct gspca_dev *gspca_dev)
-{
- const unsigned short compression = 0; /* 0=none, 7=best frame rate */
- int i, clock_div = 0;
-
- /* HDG not in ibmcam driver, added to see if it helps with
- auto-detecting between model3 and ibm netcamera pro */
- cit_read_reg(gspca_dev, 0x128, 1);
-
- cit_write_reg(gspca_dev, 0x0000, 0x0100);
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- cit_write_reg(gspca_dev, 0x0002, 0x0112);
- cit_write_reg(gspca_dev, 0x0000, 0x0123);
- cit_write_reg(gspca_dev, 0x0001, 0x0117);
- cit_write_reg(gspca_dev, 0x0040, 0x0108);
- cit_write_reg(gspca_dev, 0x0019, 0x012c);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- cit_write_reg(gspca_dev, 0x0002, 0x0115);
- cit_write_reg(gspca_dev, 0x0003, 0x0115);
- cit_read_reg(gspca_dev, 0x0115, 0);
- cit_write_reg(gspca_dev, 0x000b, 0x0115);
-
- /* TESTME HDG not in ibmcam driver, added to see if it helps with
- auto-detecting between model3 and ibm netcamera pro */
- if (0) {
- cit_write_reg(gspca_dev, 0x0078, 0x012d);
- cit_write_reg(gspca_dev, 0x0001, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0079, 0x012d);
- cit_write_reg(gspca_dev, 0x00ff, 0x0130);
- cit_write_reg(gspca_dev, 0xcd41, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_read_reg(gspca_dev, 0x0126, 1);
- }
-
- cit_model3_Packet1(gspca_dev, 0x000a, 0x0040);
- cit_model3_Packet1(gspca_dev, 0x000b, 0x00f6);
- cit_model3_Packet1(gspca_dev, 0x000c, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x000d, 0x0020);
- cit_model3_Packet1(gspca_dev, 0x000e, 0x0033);
- cit_model3_Packet1(gspca_dev, 0x000f, 0x0007);
- cit_model3_Packet1(gspca_dev, 0x0010, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0011, 0x0070);
- cit_model3_Packet1(gspca_dev, 0x0012, 0x0030);
- cit_model3_Packet1(gspca_dev, 0x0013, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0014, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0015, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0016, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0017, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0018, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x001e, 0x00c3);
- cit_model3_Packet1(gspca_dev, 0x0020, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0028, 0x0010);
- cit_model3_Packet1(gspca_dev, 0x0029, 0x0054);
- cit_model3_Packet1(gspca_dev, 0x002a, 0x0013);
- cit_model3_Packet1(gspca_dev, 0x002b, 0x0007);
- cit_model3_Packet1(gspca_dev, 0x002d, 0x0028);
- cit_model3_Packet1(gspca_dev, 0x002e, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0031, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0032, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0033, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0034, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0035, 0x0038);
- cit_model3_Packet1(gspca_dev, 0x003a, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x003c, 0x001e);
- cit_model3_Packet1(gspca_dev, 0x003f, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0041, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0046, 0x003f);
- cit_model3_Packet1(gspca_dev, 0x0047, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0050, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x0052, 0x001a);
- cit_model3_Packet1(gspca_dev, 0x0053, 0x0003);
- cit_model3_Packet1(gspca_dev, 0x005a, 0x006b);
- cit_model3_Packet1(gspca_dev, 0x005d, 0x001e);
- cit_model3_Packet1(gspca_dev, 0x005e, 0x0030);
- cit_model3_Packet1(gspca_dev, 0x005f, 0x0041);
- cit_model3_Packet1(gspca_dev, 0x0064, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0065, 0x0015);
- cit_model3_Packet1(gspca_dev, 0x0068, 0x000f);
- cit_model3_Packet1(gspca_dev, 0x0079, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x007a, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x007c, 0x003f);
- cit_model3_Packet1(gspca_dev, 0x0082, 0x000f);
- cit_model3_Packet1(gspca_dev, 0x0085, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0099, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x009b, 0x0023);
- cit_model3_Packet1(gspca_dev, 0x009c, 0x0022);
- cit_model3_Packet1(gspca_dev, 0x009d, 0x0096);
- cit_model3_Packet1(gspca_dev, 0x009e, 0x0096);
- cit_model3_Packet1(gspca_dev, 0x009f, 0x000a);
-
- switch (gspca_dev->width) {
- case 160:
- cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
- cit_write_reg(gspca_dev, 0x0024, 0x010b); /* Differs everywhere */
- cit_write_reg(gspca_dev, 0x00a9, 0x0119);
- cit_write_reg(gspca_dev, 0x0016, 0x011b);
- cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
- cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
- cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
- cit_write_reg(gspca_dev, 0x0018, 0x0102);
- cit_write_reg(gspca_dev, 0x0004, 0x0104);
- cit_write_reg(gspca_dev, 0x0004, 0x011a);
- cit_write_reg(gspca_dev, 0x0028, 0x011c);
- cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
- cit_write_reg(gspca_dev, 0x0000, 0x0118);
- cit_write_reg(gspca_dev, 0x0000, 0x0132);
- cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
- cit_write_reg(gspca_dev, compression, 0x0109);
- clock_div = 3;
- break;
- case 320:
- cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
- cit_write_reg(gspca_dev, 0x0028, 0x010b); /* Differs everywhere */
- cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same */
- cit_write_reg(gspca_dev, 0x0000, 0x011e);
- cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
- cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
- /* 4 commands from 160x120 skipped */
- cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
- cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
- cit_write_reg(gspca_dev, compression, 0x0109);
- cit_write_reg(gspca_dev, 0x00d9, 0x0119);
- cit_write_reg(gspca_dev, 0x0006, 0x011b);
- cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x0010, 0x0104);
- cit_write_reg(gspca_dev, 0x0004, 0x011a);
- cit_write_reg(gspca_dev, 0x003f, 0x011c);
- cit_write_reg(gspca_dev, 0x001c, 0x0118);
- cit_write_reg(gspca_dev, 0x0000, 0x0132);
- clock_div = 5;
- break;
- case 640:
- cit_write_reg(gspca_dev, 0x00f0, 0x0105);
- cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
- cit_write_reg(gspca_dev, 0x0038, 0x010b); /* Differs everywhere */
- cit_write_reg(gspca_dev, 0x00d9, 0x0119); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x0006, 0x011b); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x0004, 0x011d); /* NC */
- cit_write_reg(gspca_dev, 0x0003, 0x011e); /* Same on 160x120, 640x480 */
- cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
- cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
- cit_write_reg(gspca_dev, 0x0021, 0x0102); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x0016, 0x0104); /* NC */
- cit_write_reg(gspca_dev, 0x0004, 0x011a); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x003f, 0x011c); /* Same on 320x240, 640x480 */
- cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
- cit_write_reg(gspca_dev, 0x001c, 0x0118); /* Same on 320x240, 640x480 */
- cit_model3_Packet1(gspca_dev, 0x0021, 0x0001); /* Same */
- cit_write_reg(gspca_dev, compression, 0x0109);
- cit_write_reg(gspca_dev, 0x0040, 0x0101);
- cit_write_reg(gspca_dev, 0x0040, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0132); /* Same on 320x240, 640x480 */
- clock_div = 7;
- break;
- }
-
- cit_model3_Packet1(gspca_dev, 0x007e, 0x000e); /* Hue */
- cit_model3_Packet1(gspca_dev, 0x0036, 0x0011); /* Brightness */
- cit_model3_Packet1(gspca_dev, 0x0060, 0x0002); /* Sharpness */
- cit_model3_Packet1(gspca_dev, 0x0061, 0x0004); /* Sharpness */
- cit_model3_Packet1(gspca_dev, 0x0062, 0x0005); /* Sharpness */
- cit_model3_Packet1(gspca_dev, 0x0063, 0x0014); /* Sharpness */
- cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
- cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
- cit_model3_Packet1(gspca_dev, 0x0067, 0x0001); /* Contrast */
- cit_model3_Packet1(gspca_dev, 0x005b, 0x000c); /* Contrast */
- cit_model3_Packet1(gspca_dev, 0x005c, 0x0016); /* Contrast */
- cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
- cit_model3_Packet1(gspca_dev, 0x002c, 0x0003); /* Was 1, broke 640x480 */
- cit_model3_Packet1(gspca_dev, 0x002f, 0x002a);
- cit_model3_Packet1(gspca_dev, 0x0030, 0x0029);
- cit_model3_Packet1(gspca_dev, 0x0037, 0x0002);
- cit_model3_Packet1(gspca_dev, 0x0038, 0x0059);
- cit_model3_Packet1(gspca_dev, 0x003d, 0x002e);
- cit_model3_Packet1(gspca_dev, 0x003e, 0x0028);
- cit_model3_Packet1(gspca_dev, 0x0078, 0x0005);
- cit_model3_Packet1(gspca_dev, 0x007b, 0x0011);
- cit_model3_Packet1(gspca_dev, 0x007d, 0x004b);
- cit_model3_Packet1(gspca_dev, 0x007f, 0x0022);
- cit_model3_Packet1(gspca_dev, 0x0080, 0x000c);
- cit_model3_Packet1(gspca_dev, 0x0081, 0x000b);
- cit_model3_Packet1(gspca_dev, 0x0083, 0x00fd);
- cit_model3_Packet1(gspca_dev, 0x0086, 0x000b);
- cit_model3_Packet1(gspca_dev, 0x0087, 0x000b);
- cit_model3_Packet1(gspca_dev, 0x007e, 0x000e);
- cit_model3_Packet1(gspca_dev, 0x0096, 0x00a0); /* Red sharpness */
- cit_model3_Packet1(gspca_dev, 0x0097, 0x0096); /* Blue sharpness */
- cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
-
- /* FIXME we should probably use cit_get_clock_div() here (in
- combination with isoc negotiation using the programmable isoc size)
- like with the IBM netcam pro). */
- cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
-
- switch (gspca_dev->width) {
- case 160:
- cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
- cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
- cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
- cit_model3_Packet1(gspca_dev, 0x0040, 0x000a);
- cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
- break;
- case 320:
- cit_model3_Packet1(gspca_dev, 0x001f, 0x0000); /* Same */
- cit_model3_Packet1(gspca_dev, 0x0039, 0x001f); /* Same */
- cit_model3_Packet1(gspca_dev, 0x003b, 0x003c); /* Same */
- cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0051, 0x000b);
- break;
- case 640:
- cit_model3_Packet1(gspca_dev, 0x001f, 0x0002); /* !Same */
- cit_model3_Packet1(gspca_dev, 0x0039, 0x003e); /* !Same */
- cit_model3_Packet1(gspca_dev, 0x0040, 0x0008);
- cit_model3_Packet1(gspca_dev, 0x0051, 0x000a);
- break;
- }
-
-/* if (sd->input_index) { */
- if (rca_input) {
- for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
- if (rca_initdata[i][0])
- cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
- else
- cit_write_reg(gspca_dev, rca_initdata[i][1],
- rca_initdata[i][2]);
- }
- }
-
- return 0;
-}
-
-static int cit_start_model4(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- cit_write_reg(gspca_dev, 0x0000, 0x0100);
- cit_write_reg(gspca_dev, 0x00c0, 0x0111);
- cit_write_reg(gspca_dev, 0x00bc, 0x012c);
- cit_write_reg(gspca_dev, 0x0080, 0x012b);
- cit_write_reg(gspca_dev, 0x0000, 0x0108);
- cit_write_reg(gspca_dev, 0x0001, 0x0133);
- cit_write_reg(gspca_dev, 0x009b, 0x010f);
- cit_write_reg(gspca_dev, 0x00bb, 0x010f);
- cit_model4_Packet1(gspca_dev, 0x0038, 0x0000);
- cit_model4_Packet1(gspca_dev, 0x000a, 0x005c);
-
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0004, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0x00fb, 0x012e);
- cit_write_reg(gspca_dev, 0x0000, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x000c, 0x0127);
- cit_write_reg(gspca_dev, 0x0009, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
-
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0012, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0008, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x002a, 0x012d);
- cit_write_reg(gspca_dev, 0x0000, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_model4_Packet1(gspca_dev, 0x0034, 0x0000);
-
- switch (gspca_dev->width) {
- case 128: /* 128x96 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119);
- cit_write_reg(gspca_dev, 0x00d0, 0x0111);
- cit_write_reg(gspca_dev, 0x0039, 0x010a);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- cit_write_reg(gspca_dev, 0x0028, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x001e, 0x0105);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0016, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x000a, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0014, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012e);
- cit_write_reg(gspca_dev, 0x001a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
- cit_write_reg(gspca_dev, 0x005a, 0x012d);
- cit_write_reg(gspca_dev, 0x9545, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x0127);
- cit_write_reg(gspca_dev, 0x0018, 0x012e);
- cit_write_reg(gspca_dev, 0x0043, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x001c, 0x0127);
- cit_write_reg(gspca_dev, 0x00eb, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0032, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0036, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0017, 0x0127);
- cit_write_reg(gspca_dev, 0x0013, 0x012e);
- cit_write_reg(gspca_dev, 0x0031, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x0017, 0x012d);
- cit_write_reg(gspca_dev, 0x0078, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
- sd->sof_len = 2;
- break;
- case 160: /* 160x120 */
- cit_write_reg(gspca_dev, 0x0038, 0x0119);
- cit_write_reg(gspca_dev, 0x00d0, 0x0111);
- cit_write_reg(gspca_dev, 0x00b9, 0x010a);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- cit_write_reg(gspca_dev, 0x0028, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x001e, 0x0105);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0016, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x000b, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0014, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012e);
- cit_write_reg(gspca_dev, 0x001a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
- cit_write_reg(gspca_dev, 0x005a, 0x012d);
- cit_write_reg(gspca_dev, 0x9545, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x0127);
- cit_write_reg(gspca_dev, 0x0018, 0x012e);
- cit_write_reg(gspca_dev, 0x0043, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x001c, 0x0127);
- cit_write_reg(gspca_dev, 0x00c7, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0032, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0025, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0036, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0048, 0x0127);
- cit_write_reg(gspca_dev, 0x0035, 0x012e);
- cit_write_reg(gspca_dev, 0x00d0, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x0048, 0x012d);
- cit_write_reg(gspca_dev, 0x0090, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x0001, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
- sd->sof_len = 2;
- break;
- case 176: /* 176x144 */
- cit_write_reg(gspca_dev, 0x0038, 0x0119);
- cit_write_reg(gspca_dev, 0x00d0, 0x0111);
- cit_write_reg(gspca_dev, 0x00b9, 0x010a);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- cit_write_reg(gspca_dev, 0x002c, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x0024, 0x0105);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0016, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0007, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0014, 0x012d);
- cit_write_reg(gspca_dev, 0x0001, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012e);
- cit_write_reg(gspca_dev, 0x001a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
- cit_write_reg(gspca_dev, 0x005e, 0x012d);
- cit_write_reg(gspca_dev, 0x9545, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x0127);
- cit_write_reg(gspca_dev, 0x0018, 0x012e);
- cit_write_reg(gspca_dev, 0x0049, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x001c, 0x0127);
- cit_write_reg(gspca_dev, 0x00c7, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0032, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0028, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0036, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0010, 0x0127);
- cit_write_reg(gspca_dev, 0x0013, 0x012e);
- cit_write_reg(gspca_dev, 0x002a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x0010, 0x012d);
- cit_write_reg(gspca_dev, 0x006d, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x0001, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
- /* TESTME HDG: this does not seem right
- (it is 2 for all other resolutions) */
- sd->sof_len = 10;
- break;
- case 320: /* 320x240 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119);
- cit_write_reg(gspca_dev, 0x00d0, 0x0111);
- cit_write_reg(gspca_dev, 0x0039, 0x010a);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- cit_write_reg(gspca_dev, 0x0028, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x001e, 0x0105);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0016, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x000a, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0014, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012e);
- cit_write_reg(gspca_dev, 0x001a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
- cit_write_reg(gspca_dev, 0x005a, 0x012d);
- cit_write_reg(gspca_dev, 0x9545, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x0127);
- cit_write_reg(gspca_dev, 0x0018, 0x012e);
- cit_write_reg(gspca_dev, 0x0043, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x001c, 0x0127);
- cit_write_reg(gspca_dev, 0x00eb, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0032, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0036, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0017, 0x0127);
- cit_write_reg(gspca_dev, 0x0013, 0x012e);
- cit_write_reg(gspca_dev, 0x0031, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x0017, 0x012d);
- cit_write_reg(gspca_dev, 0x0078, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
- sd->sof_len = 2;
- break;
- case 352: /* 352x288 */
- cit_write_reg(gspca_dev, 0x0070, 0x0119);
- cit_write_reg(gspca_dev, 0x00c0, 0x0111);
- cit_write_reg(gspca_dev, 0x0039, 0x010a);
- cit_write_reg(gspca_dev, 0x0001, 0x0102);
- cit_write_reg(gspca_dev, 0x002c, 0x0103);
- cit_write_reg(gspca_dev, 0x0000, 0x0104);
- cit_write_reg(gspca_dev, 0x0024, 0x0105);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0016, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0006, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0014, 0x012d);
- cit_write_reg(gspca_dev, 0x0002, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012e);
- cit_write_reg(gspca_dev, 0x001a, 0x0130);
- cit_write_reg(gspca_dev, 0x8a0a, 0x0124);
- cit_write_reg(gspca_dev, 0x005e, 0x012d);
- cit_write_reg(gspca_dev, 0x9545, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x0127);
- cit_write_reg(gspca_dev, 0x0018, 0x012e);
- cit_write_reg(gspca_dev, 0x0049, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012f);
- cit_write_reg(gspca_dev, 0xd055, 0x0124);
- cit_write_reg(gspca_dev, 0x001c, 0x0127);
- cit_write_reg(gspca_dev, 0x00cf, 0x012e);
- cit_write_reg(gspca_dev, 0xaa28, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x0032, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0x00aa, 0x0130);
- cit_write_reg(gspca_dev, 0x82a8, 0x0124);
- cit_write_reg(gspca_dev, 0x0036, 0x012d);
- cit_write_reg(gspca_dev, 0x0008, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0xfffa, 0x0124);
- cit_write_reg(gspca_dev, 0x00aa, 0x012d);
- cit_write_reg(gspca_dev, 0x001e, 0x012f);
- cit_write_reg(gspca_dev, 0xd141, 0x0124);
- cit_write_reg(gspca_dev, 0x0010, 0x0127);
- cit_write_reg(gspca_dev, 0x0013, 0x012e);
- cit_write_reg(gspca_dev, 0x0025, 0x0130);
- cit_write_reg(gspca_dev, 0x8a28, 0x0124);
- cit_write_reg(gspca_dev, 0x0010, 0x012d);
- cit_write_reg(gspca_dev, 0x0048, 0x012f);
- cit_write_reg(gspca_dev, 0xd145, 0x0124);
- cit_write_reg(gspca_dev, 0x0000, 0x0127);
- cit_write_reg(gspca_dev, 0xfea8, 0x0124);
- sd->sof_len = 2;
- break;
- }
-
- cit_model4_Packet1(gspca_dev, 0x0038, 0x0004);
-
- return 0;
-}
-
-static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
-{
- const unsigned short compression = 0; /* 0=none, 7=best frame rate */
- int i, clock_div;
-
- clock_div = cit_get_clock_div(gspca_dev);
- if (clock_div < 0)
- return clock_div;
-
- cit_write_reg(gspca_dev, 0x0003, 0x0133);
- cit_write_reg(gspca_dev, 0x0000, 0x0117);
- cit_write_reg(gspca_dev, 0x0008, 0x0123);
- cit_write_reg(gspca_dev, 0x0000, 0x0100);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- /* cit_write_reg(gspca_dev, 0x0002, 0x0112); see sd_stop0 */
- cit_write_reg(gspca_dev, 0x0000, 0x0133);
- cit_write_reg(gspca_dev, 0x0000, 0x0123);
- cit_write_reg(gspca_dev, 0x0001, 0x0117);
- cit_write_reg(gspca_dev, 0x0040, 0x0108);
- cit_write_reg(gspca_dev, 0x0019, 0x012c);
- cit_write_reg(gspca_dev, 0x0060, 0x0116);
- /* cit_write_reg(gspca_dev, 0x000b, 0x0115); see sd_stop0 */
-
- cit_model3_Packet1(gspca_dev, 0x0049, 0x0000);
-
- cit_write_reg(gspca_dev, 0x0000, 0x0101); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x003a, 0x0102); /* Hstart */
- cit_write_reg(gspca_dev, 0x00a0, 0x0103); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0078, 0x0105); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x010a); /* Same */
- cit_write_reg(gspca_dev, 0x0002, 0x011d); /* Same on 160x120, 320x240 */
- cit_write_reg(gspca_dev, 0x0000, 0x0129); /* Same */
- cit_write_reg(gspca_dev, 0x00fc, 0x012b); /* Same */
- cit_write_reg(gspca_dev, 0x0022, 0x012a); /* Same */
-
- switch (gspca_dev->width) {
- case 160: /* 160x120 */
- cit_write_reg(gspca_dev, 0x0024, 0x010b);
- cit_write_reg(gspca_dev, 0x0089, 0x0119);
- cit_write_reg(gspca_dev, 0x000a, 0x011b);
- cit_write_reg(gspca_dev, 0x0003, 0x011e);
- cit_write_reg(gspca_dev, 0x0007, 0x0104);
- cit_write_reg(gspca_dev, 0x0009, 0x011a);
- cit_write_reg(gspca_dev, 0x008b, 0x011c);
- cit_write_reg(gspca_dev, 0x0008, 0x0118);
- cit_write_reg(gspca_dev, 0x0000, 0x0132);
- break;
- case 320: /* 320x240 */
- cit_write_reg(gspca_dev, 0x0028, 0x010b);
- cit_write_reg(gspca_dev, 0x00d9, 0x0119);
- cit_write_reg(gspca_dev, 0x0006, 0x011b);
- cit_write_reg(gspca_dev, 0x0000, 0x011e);
- cit_write_reg(gspca_dev, 0x000e, 0x0104);
- cit_write_reg(gspca_dev, 0x0004, 0x011a);
- cit_write_reg(gspca_dev, 0x003f, 0x011c);
- cit_write_reg(gspca_dev, 0x000c, 0x0118);
- cit_write_reg(gspca_dev, 0x0000, 0x0132);
- break;
- }
-
- cit_model3_Packet1(gspca_dev, 0x0019, 0x0031);
- cit_model3_Packet1(gspca_dev, 0x001a, 0x0003);
- cit_model3_Packet1(gspca_dev, 0x001b, 0x0038);
- cit_model3_Packet1(gspca_dev, 0x001c, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0024, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0027, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x002a, 0x0004);
- cit_model3_Packet1(gspca_dev, 0x0035, 0x000b);
- cit_model3_Packet1(gspca_dev, 0x003f, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x0044, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x0054, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00c4, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00e7, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00e9, 0x0001);
- cit_model3_Packet1(gspca_dev, 0x00ee, 0x0000);
- cit_model3_Packet1(gspca_dev, 0x00f3, 0x00c0);
-
- cit_write_reg(gspca_dev, compression, 0x0109);
- cit_write_reg(gspca_dev, clock_div, 0x0111);
-
-/* if (sd->input_index) { */
- if (rca_input) {
- for (i = 0; i < ARRAY_SIZE(rca_initdata); i++) {
- if (rca_initdata[i][0])
- cit_read_reg(gspca_dev, rca_initdata[i][2], 0);
- else
- cit_write_reg(gspca_dev, rca_initdata[i][1],
- rca_initdata[i][2]);
- }
- }
-
- return 0;
-}
-
-/* -- start the camera -- */
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int packet_size;
-
- packet_size = cit_get_packet_size(gspca_dev);
- if (packet_size < 0)
- return packet_size;
-
- switch (sd->model) {
- case CIT_MODEL0:
- cit_start_model0(gspca_dev);
- break;
- case CIT_MODEL1:
- cit_start_model1(gspca_dev);
- break;
- case CIT_MODEL2:
- cit_start_model2(gspca_dev);
- break;
- case CIT_MODEL3:
- cit_start_model3(gspca_dev);
- break;
- case CIT_MODEL4:
- cit_start_model4(gspca_dev);
- break;
- case CIT_IBM_NETCAM_PRO:
- cit_start_ibm_netcam_pro(gspca_dev);
- break;
- }
-
- /* Program max isoc packet size */
- cit_write_reg(gspca_dev, packet_size >> 8, 0x0106);
- cit_write_reg(gspca_dev, packet_size & 0xff, 0x0107);
-
- cit_restart_stream(gspca_dev);
-
- return 0;
-}
-
-static int sd_isoc_init(struct gspca_dev *gspca_dev)
-{
- struct usb_host_interface *alt;
- int max_packet_size;
-
- switch (gspca_dev->width) {
- case 160:
- max_packet_size = 450;
- break;
- case 176:
- max_packet_size = 600;
- break;
- default:
- max_packet_size = 1022;
- break;
- }
-
- /* Start isoc bandwidth "negotiation" at max isoc bandwidth */
- alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
- alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
-
- return 0;
-}
-
-static int sd_isoc_nego(struct gspca_dev *gspca_dev)
-{
- int ret, packet_size, min_packet_size;
- struct usb_host_interface *alt;
-
- switch (gspca_dev->width) {
- case 160:
- min_packet_size = 200;
- break;
- case 176:
- min_packet_size = 266;
- break;
- default:
- min_packet_size = 400;
- break;
- }
-
- alt = &gspca_dev->dev->actconfig->intf_cache[0]->altsetting[1];
- packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
- if (packet_size <= min_packet_size)
- return -EIO;
-
- packet_size -= 100;
- if (packet_size < min_packet_size)
- packet_size = min_packet_size;
- alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
-
- ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
- if (ret < 0)
- pr_err("set alt 1 err %d\n", ret);
-
- return ret;
-}
-
-static void sd_stopN(struct gspca_dev *gspca_dev)
-{
- cit_write_reg(gspca_dev, 0x0000, 0x010c);
-}
-
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* We cannot use gspca_dev->present here as that is not set when
- sd_init gets called and we get called from sd_init */
- if (!gspca_dev->dev)
- return;
-
- switch (sd->model) {
- case CIT_MODEL0:
- /* HDG windows does this, but it causes the cams autogain to
- restart from a gain of 0, which does not look good when
- changing resolutions. */
- /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
- cit_write_reg(gspca_dev, 0x00c0, 0x0100); /* LED Off */
- break;
- case CIT_MODEL1:
- cit_send_FF_04_02(gspca_dev);
- cit_read_reg(gspca_dev, 0x0100, 0);
- cit_write_reg(gspca_dev, 0x81, 0x0100); /* LED Off */
- break;
- case CIT_MODEL2:
- v4l2_ctrl_grab(sd->lighting, false);
- /* Fall through! */
- case CIT_MODEL4:
- cit_model2_Packet1(gspca_dev, 0x0030, 0x0004);
-
- cit_write_reg(gspca_dev, 0x0080, 0x0100); /* LED Off */
- cit_write_reg(gspca_dev, 0x0020, 0x0111);
- cit_write_reg(gspca_dev, 0x00a0, 0x0111);
-
- cit_model2_Packet1(gspca_dev, 0x0030, 0x0002);
-
- cit_write_reg(gspca_dev, 0x0020, 0x0111);
- cit_write_reg(gspca_dev, 0x0000, 0x0112);
- break;
- case CIT_MODEL3:
- cit_write_reg(gspca_dev, 0x0006, 0x012c);
- cit_model3_Packet1(gspca_dev, 0x0046, 0x0000);
- cit_read_reg(gspca_dev, 0x0116, 0);
- cit_write_reg(gspca_dev, 0x0064, 0x0116);
- cit_read_reg(gspca_dev, 0x0115, 0);
- cit_write_reg(gspca_dev, 0x0003, 0x0115);
- cit_write_reg(gspca_dev, 0x0008, 0x0123);
- cit_write_reg(gspca_dev, 0x0000, 0x0117);
- cit_write_reg(gspca_dev, 0x0000, 0x0112);
- cit_write_reg(gspca_dev, 0x0080, 0x0100);
- break;
- case CIT_IBM_NETCAM_PRO:
- cit_model3_Packet1(gspca_dev, 0x0049, 0x00ff);
- cit_write_reg(gspca_dev, 0x0006, 0x012c);
- cit_write_reg(gspca_dev, 0x0000, 0x0116);
- /* HDG windows does this, but I cannot get the camera
- to restart with this without redoing the entire init
- sequence which makes switching modes really slow */
- /* cit_write_reg(gspca_dev, 0x0006, 0x0115); */
- cit_write_reg(gspca_dev, 0x0008, 0x0123);
- cit_write_reg(gspca_dev, 0x0000, 0x0117);
- cit_write_reg(gspca_dev, 0x0003, 0x0133);
- cit_write_reg(gspca_dev, 0x0000, 0x0111);
- /* HDG windows does this, but I get a green picture when
- restarting the stream after this */
- /* cit_write_reg(gspca_dev, 0x0000, 0x0112); */
- cit_write_reg(gspca_dev, 0x00c0, 0x0100);
- break;
- }
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- /* If the last button state is pressed, release it now! */
- if (sd->button_state) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- sd->button_state = 0;
- }
-#endif
-}
-
-static u8 *cit_find_sof(struct gspca_dev *gspca_dev, u8 *data, int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 byte3 = 0, byte4 = 0;
- int i;
-
- switch (sd->model) {
- case CIT_MODEL0:
- case CIT_MODEL1:
- case CIT_MODEL3:
- case CIT_IBM_NETCAM_PRO:
- switch (gspca_dev->width) {
- case 160: /* 160x120 */
- byte3 = 0x02;
- byte4 = 0x0a;
- break;
- case 176: /* 176x144 */
- byte3 = 0x02;
- byte4 = 0x0e;
- break;
- case 320: /* 320x240 */
- byte3 = 0x02;
- byte4 = 0x08;
- break;
- case 352: /* 352x288 */
- byte3 = 0x02;
- byte4 = 0x00;
- break;
- case 640:
- byte3 = 0x03;
- byte4 = 0x08;
- break;
- }
-
- /* These have a different byte3 */
- if (sd->model <= CIT_MODEL1)
- byte3 = 0x00;
-
- for (i = 0; i < len; i++) {
- /* For this model the SOF always starts at offset 0
- so no need to search the entire frame */
- if (sd->model == CIT_MODEL0 && sd->sof_read != i)
- break;
-
- switch (sd->sof_read) {
- case 0:
- if (data[i] == 0x00)
- sd->sof_read++;
- break;
- case 1:
- if (data[i] == 0xff)
- sd->sof_read++;
- else if (data[i] == 0x00)
- sd->sof_read = 1;
- else
- sd->sof_read = 0;
- break;
- case 2:
- if (data[i] == byte3)
- sd->sof_read++;
- else if (data[i] == 0x00)
- sd->sof_read = 1;
- else
- sd->sof_read = 0;
- break;
- case 3:
- if (data[i] == byte4) {
- sd->sof_read = 0;
- return data + i + (sd->sof_len - 3);
- }
- if (byte3 == 0x00 && data[i] == 0xff)
- sd->sof_read = 2;
- else if (data[i] == 0x00)
- sd->sof_read = 1;
- else
- sd->sof_read = 0;
- break;
- }
- }
- break;
- case CIT_MODEL2:
- case CIT_MODEL4:
- /* TESTME we need to find a longer sof signature to avoid
- false positives */
- for (i = 0; i < len; i++) {
- switch (sd->sof_read) {
- case 0:
- if (data[i] == 0x00)
- sd->sof_read++;
- break;
- case 1:
- sd->sof_read = 0;
- if (data[i] == 0xff) {
- if (i >= 4)
- PDEBUG(D_FRAM,
- "header found at offset: %d: %02x %02x 00 %02x %02x %02x\n",
- i - 1,
- data[i - 4],
- data[i - 3],
- data[i],
- data[i + 1],
- data[i + 2]);
- else
- PDEBUG(D_FRAM,
- "header found at offset: %d: 00 %02x %02x %02x\n",
- i - 1,
- data[i],
- data[i + 1],
- data[i + 2]);
- return data + i + (sd->sof_len - 1);
- }
- break;
- }
- }
- break;
- }
- return NULL;
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- unsigned char *sof;
-
- sof = cit_find_sof(gspca_dev, data, len);
- if (sof) {
- int n;
-
- /* finish decoding current frame */
- n = sof - data;
- if (n > sd->sof_len)
- n -= sd->sof_len;
- else
- n = 0;
- gspca_frame_add(gspca_dev, LAST_PACKET,
- data, n);
- gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
- len -= sof - data;
- data = sof;
- }
-
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static void cit_check_button(struct gspca_dev *gspca_dev)
-{
- int new_button_state;
- struct sd *sd = (struct sd *)gspca_dev;
-
- switch (sd->model) {
- case CIT_MODEL3:
- case CIT_IBM_NETCAM_PRO:
- break;
- default: /* TEST ME unknown if this works on other models too */
- return;
- }
-
- /* Read the button state */
- cit_read_reg(gspca_dev, 0x0113, 0);
- new_button_state = !gspca_dev->usb_buf[0];
-
- /* Tell the cam we've seen the button press, notice that this
- is a nop (iow the cam keeps reporting pressed) until the
- button is actually released. */
- if (new_button_state)
- cit_write_reg(gspca_dev, 0x01, 0x0113);
-
- if (sd->button_state != new_button_state) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA,
- new_button_state);
- input_sync(gspca_dev->input_dev);
- sd->button_state = new_button_state;
- }
-}
-#endif
-
-static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- gspca_dev->usb_err = 0;
-
- if (!gspca_dev->streaming)
- return 0;
-
- if (sd->stop_on_control_change)
- sd_stopN(gspca_dev);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- cit_set_brightness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- cit_set_contrast(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_HUE:
- cit_set_hue(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_HFLIP:
- cit_set_hflip(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_SHARPNESS:
- cit_set_sharpness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_BACKLIGHT_COMPENSATION:
- cit_set_lighting(gspca_dev, ctrl->val);
- break;
- }
- if (sd->stop_on_control_change)
- cit_restart_stream(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops sd_ctrl_ops = {
- .s_ctrl = sd_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- bool has_brightness;
- bool has_contrast;
- bool has_hue;
- bool has_sharpness;
- bool has_lighting;
- bool has_hflip;
-
- has_brightness = has_contrast = has_hue =
- has_sharpness = has_hflip = has_lighting = false;
- switch (sd->model) {
- case CIT_MODEL0:
- has_contrast = has_hflip = true;
- break;
- case CIT_MODEL1:
- has_brightness = has_contrast =
- has_sharpness = has_lighting = true;
- break;
- case CIT_MODEL2:
- has_brightness = has_hue = has_lighting = true;
- break;
- case CIT_MODEL3:
- has_brightness = has_contrast = has_sharpness = true;
- break;
- case CIT_MODEL4:
- has_brightness = has_hue = true;
- break;
- case CIT_IBM_NETCAM_PRO:
- has_brightness = has_hue =
- has_sharpness = has_hflip = has_lighting = true;
- break;
- }
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 5);
- if (has_brightness)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 63, 1, 32);
- if (has_contrast)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 20, 1, 10);
- if (has_hue)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HUE, 0, 127, 1, 63);
- if (has_sharpness)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 6, 1, 3);
- if (has_lighting)
- sd->lighting = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_BACKLIGHT_COMPENSATION, 0, 2, 1, 1);
- if (has_hflip)
- v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
-
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- return 0;
-}
-
-/* sub-driver description */
-static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .dq_callback = cit_check_button,
- .other_input = 1,
-#endif
-};
-
-static const struct sd_desc sd_desc_isoc_nego = {
- .name = MODULE_NAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .start = sd_start,
- .isoc_init = sd_isoc_init,
- .isoc_nego = sd_isoc_nego,
- .stopN = sd_stopN,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .dq_callback = cit_check_button,
- .other_input = 1,
-#endif
-};
-
-/* -- module initialisation -- */
-static const struct usb_device_id device_table[] = {
- { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
- { USB_DEVICE_VER(0x0545, 0x8080, 0x0002, 0x0002), .driver_info = CIT_MODEL1 },
- { USB_DEVICE_VER(0x0545, 0x8080, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
- { USB_DEVICE_VER(0x0545, 0x8080, 0x0301, 0x0301), .driver_info = CIT_MODEL3 },
- { USB_DEVICE_VER(0x0545, 0x8002, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
- { USB_DEVICE_VER(0x0545, 0x800c, 0x030a, 0x030a), .driver_info = CIT_MODEL2 },
- { USB_DEVICE_VER(0x0545, 0x800d, 0x030a, 0x030a), .driver_info = CIT_MODEL4 },
- {}
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- device connect -- */
-static int sd_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- const struct sd_desc *desc = &sd_desc;
-
- switch (id->driver_info) {
- case CIT_MODEL0:
- case CIT_MODEL1:
- if (intf->cur_altsetting->desc.bInterfaceNumber != 2)
- return -ENODEV;
- break;
- case CIT_MODEL2:
- case CIT_MODEL4:
- if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
- return -ENODEV;
- break;
- case CIT_MODEL3:
- if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
- return -ENODEV;
- /* FIXME this likely applies to all model3 cams and probably
- to other models too. */
- if (ibm_netcam_pro)
- desc = &sd_desc_isoc_nego;
- break;
- }
-
- return gspca_dev_probe2(intf, id, 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,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
deleted file mode 100644
index f0bacee33ef9..000000000000
--- a/drivers/media/video/gspca/zc3xx.c
+++ /dev/null
@@ -1,7024 +0,0 @@
-/*
- * Z-Star/Vimicro zc301/zc302p/vc30x driver
- *
- * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
- * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.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; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/input.h>
-#include "gspca.h"
-#include "jpeg.h"
-
-MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
- "Serge A. Suchkov <Serge.A.S@tochka.ru>");
-MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
-MODULE_LICENSE("GPL");
-
-static int force_sensor = -1;
-
-#define REG08_DEF 3 /* default JPEG compression (75%) */
-#include "zc3xx-reg.h"
-
-/* specific webcam descriptor */
-struct sd {
- struct gspca_dev gspca_dev; /* !! must be the first item */
-
- struct { /* gamma/brightness/contrast control cluster */
- struct v4l2_ctrl *gamma;
- struct v4l2_ctrl *brightness;
- struct v4l2_ctrl *contrast;
- };
- struct { /* autogain/exposure control cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *exposure;
- };
- struct v4l2_ctrl *plfreq;
- struct v4l2_ctrl *sharpness;
- struct v4l2_ctrl *jpegqual;
-
- struct work_struct work;
- struct workqueue_struct *work_thread;
-
- u8 reg08; /* webcam compression quality */
-
- u8 bridge;
- u8 sensor; /* Type of image sensor chip */
- u16 chip_revision;
-
- u8 jpeg_hdr[JPEG_HDR_SZ];
-};
-enum bridges {
- BRIDGE_ZC301,
- BRIDGE_ZC303,
-};
-enum sensors {
- SENSOR_ADCM2700,
- SENSOR_CS2102,
- SENSOR_CS2102K,
- SENSOR_GC0303,
- SENSOR_GC0305,
- SENSOR_HDCS2020,
- SENSOR_HV7131B,
- SENSOR_HV7131R,
- SENSOR_ICM105A,
- SENSOR_MC501CB,
- SENSOR_MT9V111_1, /* (mi360soc) zc301 */
- SENSOR_MT9V111_3, /* (mi360soc) zc303 */
- SENSOR_OV7620, /* OV7648 - same values */
- SENSOR_OV7630C,
- SENSOR_PAS106,
- SENSOR_PAS202B,
- SENSOR_PB0330,
- SENSOR_PO2030,
- SENSOR_TAS5130C,
- SENSOR_MAX
-};
-
-static const struct v4l2_pix_format vga_mode[] = {
- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 240 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 480 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-static const struct v4l2_pix_format broken_vga_mode[] = {
- {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 320,
- .sizeimage = 320 * 232 * 4 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 640,
- .sizeimage = 640 * 472 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-static const struct v4l2_pix_format sif_mode[] = {
- {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 176,
- .sizeimage = 176 * 144 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 1},
- {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
- .bytesperline = 352,
- .sizeimage = 352 * 288 * 3 / 8 + 590,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
-};
-
-/*
- * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest
- * quality setting is not usable as USB 1 does not have enough bandwidth.
- */
-static u8 jpeg_qual[] = {50, 75, 87, /* 94 */};
-
-/* usb exchanges */
-struct usb_action {
- u8 req;
- u8 val;
- u16 idx;
-};
-
-static const struct usb_action adcm2700_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
- {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
- {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
- {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
- {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
- {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
- {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
- {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
- {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
- {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
- {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
- {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */
- {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
- {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */
- {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
-/*mswin+*/
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
- {0xaa, 0xfe, 0x0002},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xaa, 0xb4, 0xcd37},
- {0xaa, 0xa4, 0x0004},
- {0xaa, 0xa8, 0x0007},
- {0xaa, 0xac, 0x0004},
-/*mswin-*/
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
- {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
- {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
- {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
- {}
-};
-static const struct usb_action adcm2700_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
- {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
- {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
- {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
- {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
- {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */
- {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
- {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
- {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
- {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
- {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
- {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
- {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
- {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */
- {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
- {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */
- {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
- /*******/
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
- {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
- {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
- {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
- {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
- {}
-};
-static const struct usb_action adcm2700_50HZ[] = {
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */
- {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */
- {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
- {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */
- {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
- {}
-};
-static const struct usb_action adcm2700_60HZ[] = {
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
- {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */
- {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
- {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */
- {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
- {}
-};
-static const struct usb_action adcm2700_NoFliker[] = {
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
- {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
- {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */
- {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
- {}
-};
-static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
- {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
- {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
- {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
- {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
- {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
- {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xaa, 0x02, 0x0008},
- {0xaa, 0x03, 0x0000},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x12, 0x0089},
- {0xaa, 0x13, 0x0000},
- {0xaa, 0x14, 0x00e9},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x22, 0x0000},
- {0xaa, 0x0b, 0x0004},
- {0xaa, 0x30, 0x0030},
- {0xaa, 0x31, 0x0030},
- {0xaa, 0x32, 0x0030},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x10, 0x01ae},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
- {0xa0, 0x00, 0x01ad},
- {}
-};
-
-static const struct usb_action cs2102_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH},
- {0xa0, 0x21, ZC3XX_R081_HBLANKLOW},
- {0xa0, 0x30, ZC3XX_R083_RGAINADDR},
- {0xa0, 0x31, ZC3XX_R084_GGAINADDR},
- {0xa0, 0x32, ZC3XX_R085_BGAINADDR},
- {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH},
- {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xaa, 0x02, 0x0008},
- {0xaa, 0x03, 0x0000},
- {0xaa, 0x11, 0x0001},
- {0xaa, 0x12, 0x0087},
- {0xaa, 0x13, 0x0001},
- {0xaa, 0x14, 0x00e7},
- {0xaa, 0x20, 0x0000},
- {0xaa, 0x22, 0x0000},
- {0xaa, 0x0b, 0x0004},
- {0xaa, 0x30, 0x0030},
- {0xaa, 0x31, 0x0030},
- {0xaa, 0x32, 0x0030},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
- {0xa0, 0x00, 0x01ad},
- {}
-};
-static const struct usb_action cs2102_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0001},
- {0xaa, 0x24, 0x005f},
- {0xaa, 0x25, 0x0090},
- {0xaa, 0x21, 0x00dd},
- {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action cs2102_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0000},
- {0xaa, 0x24, 0x00af},
- {0xaa, 0x25, 0x00c8},
- {0xaa, 0x21, 0x0068},
- {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action cs2102_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0001},
- {0xaa, 0x24, 0x0055},
- {0xaa, 0x25, 0x00cc},
- {0xaa, 0x21, 0x003f},
- {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action cs2102_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0000},
- {0xaa, 0x24, 0x00aa},
- {0xaa, 0x25, 0x00e6},
- {0xaa, 0x21, 0x003f},
- {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action cs2102_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0001},
- {0xaa, 0x24, 0x005f},
- {0xaa, 0x25, 0x0000},
- {0xaa, 0x21, 0x0001},
- {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action cs2102_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x23, 0x0000},
- {0xaa, 0x24, 0x00af},
- {0xaa, 0x25, 0x0080},
- {0xaa, 0x21, 0x0001},
- {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-
-/* CS2102_KOCOM */
-static const struct usb_action cs2102K_InitialScale[] = {
- {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x01, 0x01b1},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {}
-};
-
-static const struct usb_action cs2102K_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
-/*fixme: next sequence = i2c exchanges*/
- {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x01, 0x01b1},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
-/*fixme:what does the next sequence?*/
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {}
-};
-
-static const struct usb_action gc0305_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
- {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */
- {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */
- {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
- {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
- {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
- {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
- {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
- {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
- {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
- {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
- {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
- {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
- {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
- {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
- {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
- {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
- {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
- {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */
- {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
- {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
- {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */
- {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
- {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
- {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
- {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
- {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
- {}
-};
-static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */
- {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
- {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */
- {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
- {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */
- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
- {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */
- {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */
- {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */
- {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */
- {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */
- {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
- {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */
- {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */
- {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */
- {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */
- {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */
- {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */
- {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */
- {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */
- {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */
- {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */
- {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */
- {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */
- {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */
- {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
- {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */
- {}
-};
-static const struct usb_action gc0305_50HZ[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */
- {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
- {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
- /* win: 01,92,10 */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */
- /* win: 01,97,ec */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
-/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc *
- * if 640x480 */
- {}
-};
-static const struct usb_action gc0305_60HZ[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
- {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */
- {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
- {}
-};
-
-static const struct usb_action gc0305_NoFliker[] = {
- {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
- {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */
- {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */
- {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
- {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */
- {}
-};
-
-static const struct usb_action hdcs2020_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */
- {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x1c, 0x0000},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0006},
- {0xaa, 0x0c, 0x007b},
- {0xaa, 0x0d, 0x00a7},
- {0xaa, 0x03, 0x00fb},
- {0xaa, 0x05, 0x0000},
- {0xaa, 0x06, 0x0003},
- {0xaa, 0x09, 0x0008},
-
- {0xaa, 0x0f, 0x0018}, /* set sensor gain */
- {0xaa, 0x10, 0x0018},
- {0xaa, 0x11, 0x0018},
- {0xaa, 0x12, 0x0018},
-
- {0xaa, 0x15, 0x004e},
- {0xaa, 0x1c, 0x0004},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
-
- {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xed, ZC3XX_R10B_RGB01},
- {0xa0, 0xed, ZC3XX_R10C_RGB02},
- {0xa0, 0xed, ZC3XX_R10D_RGB10},
- {0xa0, 0x66, ZC3XX_R10E_RGB11},
- {0xa0, 0xed, ZC3XX_R10F_RGB12},
- {0xa0, 0xed, ZC3XX_R110_RGB20},
- {0xa0, 0xed, ZC3XX_R111_RGB21},
- {0xa0, 0x66, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x13, 0x0031},
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x0e, 0x0004},
- {0xaa, 0x19, 0x00cd},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action hdcs2020_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x1c, 0x0000},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0006},
- {0xaa, 0x0c, 0x007a},
- {0xaa, 0x0d, 0x00a7},
- {0xaa, 0x03, 0x00fb},
- {0xaa, 0x05, 0x0000},
- {0xaa, 0x06, 0x0003},
- {0xaa, 0x09, 0x0008},
- {0xaa, 0x0f, 0x0018}, /* original setting */
- {0xaa, 0x10, 0x0018},
- {0xaa, 0x11, 0x0018},
- {0xaa, 0x12, 0x0018},
- {0xaa, 0x15, 0x004e},
- {0xaa, 0x1c, 0x0004},
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xed, ZC3XX_R10B_RGB01},
- {0xa0, 0xed, ZC3XX_R10C_RGB02},
- {0xa0, 0xed, ZC3XX_R10D_RGB10},
- {0xa0, 0x66, ZC3XX_R10E_RGB11},
- {0xa0, 0xed, ZC3XX_R10F_RGB12},
- {0xa0, 0xed, ZC3XX_R110_RGB20},
- {0xa0, 0xed, ZC3XX_R111_RGB21},
- {0xa0, 0x66, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- /**** set exposure ***/
- {0xaa, 0x13, 0x0031},
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x0e, 0x0004},
- {0xaa, 0x19, 0x00cd},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x18, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x41, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action hdcs2020_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */
- {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
- {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */
- {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
- {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
- {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */
- {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */
- {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */
- {}
-};
-static const struct usb_action hdcs2020_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */
- {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
- {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
- {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
- {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
- {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */
- {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */
- {}
-};
-static const struct usb_action hdcs2020_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */
- {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */
- {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */
- {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
- {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */
- {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */
- {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */
- {}
-};
-
-static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xaa, 0x30, 0x002d},
- {0xaa, 0x01, 0x0005},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e8},
- {0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0086}, /* 00,17,88,aa */
- {0xaa, 0x31, 0x0038},
- {0xaa, 0x32, 0x0038},
- {0xaa, 0x33, 0x0038},
- {0xaa, 0x5b, 0x0001},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0xc0, 0x019b},
- {0xa0, 0xa0, 0x019c},
- {0xa0, 0x02, ZC3XX_R188_MINGAIN},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xaa, 0x02, 0x0090}, /* 00,02,80,aa */
- {}
-};
-
-static const struct usb_action hv7131b_Initial[] = { /* 640x480*/
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xaa, 0x30, 0x002d},
- {0xaa, 0x01, 0x0005},
- {0xaa, 0x11, 0x0001},
- {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e6},
- {0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0086},
- {0xaa, 0x31, 0x0038},
- {0xaa, 0x32, 0x0038},
- {0xaa, 0x33, 0x0038},
- {0xaa, 0x5b, 0x0001},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0xc0, 0x019b},
- {0xa0, 0xa0, 0x019c},
- {0xa0, 0x02, ZC3XX_R188_MINGAIN},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */
- {}
-};
-static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
- {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
- {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
- {0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
- {0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
- {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
- {0xa0, 0x1b, ZC3XX_R01F_HSYNC_2}, /* 00,1f,1b,cc */
- {0xa0, 0xfc, ZC3XX_R020_HSYNC_3}, /* 00,20,fc,cc */
- {}
-};
-static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
- {0xaa, 0x26, 0x0053}, /* 00,26,53,aa */
- {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
- {0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
- {0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
- {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
- {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
- {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
- {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
- {0xa0, 0x12, ZC3XX_R01F_HSYNC_2}, /* 00,1f,12,cc */
- {0xa0, 0x80, ZC3XX_R020_HSYNC_3}, /* 00,20,80,cc */
- {}
-};
-static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
- {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
- {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
- {0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
- {0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
- {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
- {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, /* 00,1f,13,cc */
- {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, /* 00,20,4c,cc */
- {}
-};
-static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0007}, /* 00,25,07,aa */
- {0xaa, 0x26, 0x00a1}, /* 00,26,a1,aa */
- {0xaa, 0x27, 0x0020}, /* 00,27,20,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
- {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
- {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
- {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
- {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
- {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
- {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
- {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
- {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
- {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
- {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
- {}
-};
-static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
- {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
- {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
- {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
- {0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
- {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0x10, ZC3XX_R01E_HSYNC_1}, /* 00,1e,10,cc */
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2}, /* 00,1f,00,cc */
- {0xa0, 0x03, ZC3XX_R020_HSYNC_3}, /* 00,20,03,cc */
- {}
-};
-static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x25, 0x0003}, /* 00,25,03,aa */
- {0xaa, 0x26, 0x0000}, /* 00,26,00,aa */
- {0xaa, 0x27, 0x0000}, /* 00,27,00,aa */
- {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */
- {0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
- {0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
- {0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
- {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
- {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, /* 00,1d,00,cc */
- {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,a0,cc */
- {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, /* 00,1f,16,cc */
- {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, /* 00,20,40,cc */
- {}
-};
-
-/* from lPEPI264v.inf (hv7131b!) */
-static const struct usb_action hv7131r_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x000c},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x13, 0x0000},
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e8},
- {0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0088},
- {0xaa, 0x30, 0x000b},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0xc0, 0x019b},
- {0xa0, 0xa0, 0x019c},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {}
-};
-static const struct usb_action hv7131r_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x000c},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x13, 0x0000},
- {0xaa, 0x14, 0x0001},
- {0xaa, 0x15, 0x00e6},
- {0xaa, 0x16, 0x0002},
- {0xaa, 0x17, 0x0086},
- {0xaa, 0x30, 0x000b},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0xc0, 0x019b},
- {0xa0, 0xa0, 0x019c},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {}
-};
-static const struct usb_action hv7131r_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x68, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action hv7131r_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xd1, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x40, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action hv7131r_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x06, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1a, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action hv7131r_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x0c, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x18, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action hv7131r_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action hv7131r_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x04, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0xb0, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x00, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0x08, ZC3XX_R020_HSYNC_3},
- {}
-};
-
-static const struct usb_action icm105a_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
- {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
- {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xaa, 0x01, 0x0010},
- {0xaa, 0x03, 0x0000},
- {0xaa, 0x04, 0x0001},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0001},
- {0xaa, 0x04, 0x0011},
- {0xaa, 0x05, 0x00a0},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0002},
- {0xaa, 0x04, 0x0013},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0003},
- {0xaa, 0x04, 0x0015},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0004},
- {0xaa, 0x04, 0x0017},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x000d},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0005},
- {0xaa, 0x04, 0x0019},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0006},
- {0xaa, 0x04, 0x0017},
- {0xaa, 0x05, 0x0026},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0007},
- {0xaa, 0x04, 0x0019},
- {0xaa, 0x05, 0x0022},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0008},
- {0xaa, 0x04, 0x0021},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0009},
- {0xaa, 0x04, 0x0023},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x000d},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000a},
- {0xaa, 0x04, 0x0025},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000b},
- {0xaa, 0x04, 0x00ec},
- {0xaa, 0x05, 0x002e},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000c},
- {0xaa, 0x04, 0x00fa},
- {0xaa, 0x05, 0x002a},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x07, 0x000d},
- {0xaa, 0x01, 0x0005},
- {0xaa, 0x94, 0x0002},
- {0xaa, 0x90, 0x0000},
- {0xaa, 0x91, 0x001f},
- {0xaa, 0x10, 0x0064},
- {0xaa, 0x9b, 0x00f0},
- {0xaa, 0x9c, 0x0002},
- {0xaa, 0x14, 0x001a},
- {0xaa, 0x20, 0x0080},
- {0xaa, 0x22, 0x0080},
- {0xaa, 0x24, 0x0080},
- {0xaa, 0x26, 0x0080},
- {0xaa, 0x00, 0x0084},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xaa, 0xa8, 0x00c0},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0008},
-
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf7, ZC3XX_R10B_RGB01},
- {0xa0, 0xf7, ZC3XX_R10C_RGB02},
- {0xa0, 0xf7, ZC3XX_R10D_RGB10},
- {0xa0, 0x52, ZC3XX_R10E_RGB11},
- {0xa0, 0xf7, ZC3XX_R10F_RGB12},
- {0xa0, 0xf7, ZC3XX_R110_RGB20},
- {0xa0, 0xf7, ZC3XX_R111_RGB21},
- {0xa0, 0x52, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x0d, 0x0003},
- {0xaa, 0x0c, 0x008c},
- {0xaa, 0x0e, 0x0095},
- {0xaa, 0x0f, 0x0002},
- {0xaa, 0x1c, 0x0094},
- {0xaa, 0x1d, 0x0002},
- {0xaa, 0x20, 0x0080},
- {0xaa, 0x22, 0x0080},
- {0xaa, 0x24, 0x0080},
- {0xaa, 0x26, 0x0080},
- {0xaa, 0x00, 0x0084},
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xec, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-
-static const struct usb_action icm105a_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH},
- {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH},
- {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xaa, 0x01, 0x0010},
- {0xaa, 0x03, 0x0000},
- {0xaa, 0x04, 0x0001},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0001},
- {0xaa, 0x04, 0x0011},
- {0xaa, 0x05, 0x00a0},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0002},
- {0xaa, 0x04, 0x0013},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0001},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0003},
- {0xaa, 0x04, 0x0015},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0004},
- {0xaa, 0x04, 0x0017},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x000d},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0005},
- {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT},
- {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE},
- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
- {0xa1, 0x01, 0x0091},
- {0xaa, 0x05, 0x0020},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0006},
- {0xaa, 0x04, 0x0017},
- {0xaa, 0x05, 0x0026},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0007},
- {0xaa, 0x04, 0x0019},
- {0xaa, 0x05, 0x0022},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0008},
- {0xaa, 0x04, 0x0021},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x0009},
- {0xaa, 0x04, 0x0023},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x000d},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000a},
- {0xaa, 0x04, 0x0025},
- {0xaa, 0x05, 0x00aa},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000b},
- {0xaa, 0x04, 0x00ec},
- {0xaa, 0x05, 0x002e},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x03, 0x000c},
- {0xaa, 0x04, 0x00fa},
- {0xaa, 0x05, 0x002a},
- {0xaa, 0x06, 0x0005},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x07, 0x000d},
- {0xaa, 0x01, 0x0005},
- {0xaa, 0x94, 0x0002},
- {0xaa, 0x90, 0x0000},
- {0xaa, 0x91, 0x0010},
- {0xaa, 0x10, 0x0064},
- {0xaa, 0x9b, 0x00f0},
- {0xaa, 0x9c, 0x0002},
- {0xaa, 0x14, 0x001a},
- {0xaa, 0x20, 0x0080},
- {0xaa, 0x22, 0x0080},
- {0xaa, 0x24, 0x0080},
- {0xaa, 0x26, 0x0080},
- {0xaa, 0x00, 0x0084},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xaa, 0xa8, 0x0080},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0008},
-
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
-
- {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf7, ZC3XX_R10B_RGB01},
- {0xa0, 0xf7, ZC3XX_R10C_RGB02},
- {0xa0, 0xf7, ZC3XX_R10D_RGB10},
- {0xa0, 0x52, ZC3XX_R10E_RGB11},
- {0xa0, 0xf7, ZC3XX_R10F_RGB12},
- {0xa0, 0xf7, ZC3XX_R110_RGB20},
- {0xa0, 0xf7, ZC3XX_R111_RGB21},
- {0xa0, 0x52, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x0d, 0x0003},
- {0xaa, 0x0c, 0x0020},
- {0xaa, 0x0e, 0x000e},
- {0xaa, 0x0f, 0x0002},
- {0xaa, 0x1c, 0x000d},
- {0xaa, 0x1d, 0x0002},
- {0xaa, 0x20, 0x0080},
- {0xaa, 0x22, 0x0080},
- {0xaa, 0x24, 0x0080},
- {0xaa, 0x26, 0x0080},
- {0xaa, 0x00, 0x0084},
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH},
- {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action icm105a_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
- {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
- {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
- {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */
- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {}
-};
-static const struct usb_action icm105a_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
- {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
- {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */
- {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */
- {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
- {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
- {}
-};
-static const struct usb_action icm105a_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
- {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
- {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
- {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
- {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {}
-};
-static const struct usb_action icm105a_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
- {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */
- {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */
- {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */
- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
- {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
- {}
-};
-static const struct usb_action icm105a_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
- {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
- {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
- {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {}
-};
-static const struct usb_action icm105a_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
- {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
- {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */
- {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */
- {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */
- {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */
- {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */
- {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */
- {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */
- {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */
- {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */
- {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */
- {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */
- {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
- {}
-};
-
-static const struct usb_action mc501cb_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
- {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
- {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
- {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
- {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
- {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
- {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
- {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
- {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
- {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
- {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
- {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
- {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
- {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
- {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */
- {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
- {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */
- {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
- {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
- {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
- {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
- {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
- {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
- {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
- {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
- {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
- {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
- {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
- {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
- {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
- {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
- {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
- {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
- {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
- {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
- {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
- {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
- {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
- {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
- {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
- {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
- {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
- {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
- {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
- {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
- {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
- {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
- {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
- {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
- {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
- {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
- {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
- {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
- {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
- {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
- {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
- {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
- {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */
- {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
- {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */
- {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */
- {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */
- {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */
- {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
- {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
- {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */
- {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
- {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
- {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
- {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
- {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
- {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
-
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
- {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
- {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
- {}
-};
-
-static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */
- {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */
- {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */
- {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
- {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */
- {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */
- {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
- {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */
- {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */
- {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
- {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
- {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */
- {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */
- {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */
- {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
- {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */
- {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */
- {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
- {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */
- {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */
- {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */
- {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */
- {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */
- {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */
- {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */
- {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */
- {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */
- {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */
- {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */
- {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */
- {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */
- {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */
- {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */
- {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */
- {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */
- {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */
- {0xaa, 0xa0, 0x001a}, /* 00,a0,1a,aa */
- {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */
- {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */
- {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */
- {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */
- {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */
- {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */
- {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */
- {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */
- {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */
- {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */
- {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */
- {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */
- {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */
- {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */
- {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */
- {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */
- {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */
- {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */
- {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */
- {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */
- {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */
- {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */
- {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */
- {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */
- {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
- {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */
- {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */
- {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */
- {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */
- {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */
- {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */
- {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */
- {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */
- {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */
- {}
-};
-
-static const struct usb_action mc501cb_50HZ[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
- {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */
- {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */
- {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */
- {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */
- {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */
- {}
-};
-
-static const struct usb_action mc501cb_50HZScale[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
- {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */
- {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */
- {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */
- {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */
- {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */
- {}
-};
-
-static const struct usb_action mc501cb_60HZ[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
- {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
- {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
- {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
- {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
- {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
- {}
-};
-
-static const struct usb_action mc501cb_60HZScale[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
- {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
- {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
- {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
- {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
- {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
- {}
-};
-
-static const struct usb_action mc501cb_NoFliker[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
- {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */
- {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */
- {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */
- {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */
- {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */
- {}
-};
-
-static const struct usb_action mc501cb_NoFlikerScale[] = {
- {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
- {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
- {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
- {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */
- {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */
- {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */
- {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */
- {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */
- {}
-};
-
-/* from zs211.inf */
-static const struct usb_action ov7620_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
- {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
- {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
- {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
- {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
- {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
- {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
- {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
- {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
- {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
- {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
- {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
- {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
- {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */
- {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
- {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
- {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
- {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
- {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
- {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
- {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
- {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
- {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
- {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
- {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
- {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
- {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
- {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
- {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
- {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
- {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
- {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
- {}
-};
-static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
- /* mx change? */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
- {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
- {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */
- {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */
- {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */
- {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */
- /* OV7648 00,9c,d8,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */
- {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
- {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
- {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */
- {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
- {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */
- {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */
- {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */
- {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */
- {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */
- {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */
- {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */
- {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */
- {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */
- {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
- {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */
- {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
- {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
- {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */
- {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */
- {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */
- {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */
- {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */
- {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */
- {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */
- {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
- {}
-};
-static const struct usb_action ov7620_50HZ[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
- {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
- {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
- {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */
- {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
-/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
- * if mode0 (640x480) */
- {}
-};
-static const struct usb_action ov7620_60HZ[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- /* (bug in zs211.inf) */
- {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
- {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
- {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */
- {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
- {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */
- {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */
-/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc
- * if mode0 (640x480) */
-/* ?? in gspca v1, it was
- {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
- {0xa1, 0x01, 0x0037}, */
- {}
-};
-static const struct usb_action ov7620_NoFliker[] = {
- {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
- /* (bug in zs211.inf) */
- {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
- {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */
- {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */
- {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */
- {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */
-/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc
- * if mode1 (320x240) */
-/* ?? was
- {0xa0, 0x00, 0x0039}, * 00,00,00,dd *
- {0xa1, 0x01, 0x0037}, */
- {}
-};
-
-static const struct usb_action ov7630c_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x12, 0x0080},
- {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
- {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
- {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
- {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x12, 0x0069},
- {0xaa, 0x04, 0x0020},
- {0xaa, 0x06, 0x0050},
- {0xaa, 0x13, 0x0083},
- {0xaa, 0x14, 0x0000},
- {0xaa, 0x15, 0x0024},
- {0xaa, 0x17, 0x0018},
- {0xaa, 0x18, 0x00ba},
- {0xaa, 0x19, 0x0002},
- {0xaa, 0x1a, 0x00f6},
- {0xaa, 0x1b, 0x0002},
- {0xaa, 0x20, 0x00c2},
- {0xaa, 0x24, 0x0060},
- {0xaa, 0x25, 0x0040},
- {0xaa, 0x26, 0x0030},
- {0xaa, 0x27, 0x00ea},
- {0xaa, 0x28, 0x00a0},
- {0xaa, 0x21, 0x0000},
- {0xaa, 0x2a, 0x0081},
- {0xaa, 0x2b, 0x0096},
- {0xaa, 0x2d, 0x0094},
- {0xaa, 0x2f, 0x003d},
- {0xaa, 0x30, 0x0024},
- {0xaa, 0x60, 0x0000},
- {0xaa, 0x61, 0x0040},
- {0xaa, 0x68, 0x007c},
- {0xaa, 0x6f, 0x0015},
- {0xaa, 0x75, 0x0088},
- {0xaa, 0x77, 0x00b5},
- {0xaa, 0x01, 0x0060},
- {0xaa, 0x02, 0x0060},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x46, ZC3XX_R118_BGAIN},
- {0xa0, 0x04, ZC3XX_R113_RGB03},
-/* 0x10, */
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/
- {0xa0, 0x0c, ZC3XX_R121_GAMMA01},
- {0xa0, 0x1f, ZC3XX_R122_GAMMA02},
- {0xa0, 0x3a, ZC3XX_R123_GAMMA03},
- {0xa0, 0x53, ZC3XX_R124_GAMMA04},
- {0xa0, 0x6d, ZC3XX_R125_GAMMA05},
- {0xa0, 0x85, ZC3XX_R126_GAMMA06},
- {0xa0, 0x9c, ZC3XX_R127_GAMMA07},
- {0xa0, 0xb0, ZC3XX_R128_GAMMA08},
- {0xa0, 0xc2, ZC3XX_R129_GAMMA09},
- {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xde, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x05, ZC3XX_R130_GAMMA10},
- {0xa0, 0x0f, ZC3XX_R131_GAMMA11},
- {0xa0, 0x16, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1a, ZC3XX_R133_GAMMA13},
- {0xa0, 0x19, ZC3XX_R134_GAMMA14},
- {0xa0, 0x19, ZC3XX_R135_GAMMA15},
- {0xa0, 0x17, ZC3XX_R136_GAMMA16},
- {0xa0, 0x15, ZC3XX_R137_GAMMA17},
- {0xa0, 0x12, ZC3XX_R138_GAMMA18},
- {0xa0, 0x10, ZC3XX_R139_GAMMA19},
- {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x09, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x08, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x06, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x03, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
- {0xa0, 0x50, ZC3XX_R10E_RGB11},
- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
- {0xa0, 0xf8, ZC3XX_R110_RGB20},
- {0xa0, 0xf8, ZC3XX_R111_RGB21},
- {0xa0, 0x50, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xaa, 0x10, 0x001b},
- {0xaa, 0x76, 0x0002},
- {0xaa, 0x2a, 0x0081},
- {0xaa, 0x2b, 0x0000},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xaa, 0x13, 0x0083}, /* 40 */
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-
-static const struct usb_action ov7630c_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
-
- {0xaa, 0x12, 0x0080},
- {0xa0, 0x02, ZC3XX_R083_RGAINADDR},
- {0xa0, 0x01, ZC3XX_R085_BGAINADDR},
- {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH},
- {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x12, 0x0069}, /* i2c */
- {0xaa, 0x04, 0x0020},
- {0xaa, 0x06, 0x0050},
- {0xaa, 0x13, 0x00c3},
- {0xaa, 0x14, 0x0000},
- {0xaa, 0x15, 0x0024},
- {0xaa, 0x19, 0x0003},
- {0xaa, 0x1a, 0x00f6},
- {0xaa, 0x1b, 0x0002},
- {0xaa, 0x20, 0x00c2},
- {0xaa, 0x24, 0x0060},
- {0xaa, 0x25, 0x0040},
- {0xaa, 0x26, 0x0030},
- {0xaa, 0x27, 0x00ea},
- {0xaa, 0x28, 0x00a0},
- {0xaa, 0x21, 0x0000},
- {0xaa, 0x2a, 0x0081},
- {0xaa, 0x2b, 0x0096},
- {0xaa, 0x2d, 0x0084},
- {0xaa, 0x2f, 0x003d},
- {0xaa, 0x30, 0x0024},
- {0xaa, 0x60, 0x0000},
- {0xaa, 0x61, 0x0040},
- {0xaa, 0x68, 0x007c},
- {0xaa, 0x6f, 0x0015},
- {0xaa, 0x75, 0x0088},
- {0xaa, 0x77, 0x00b5},
- {0xaa, 0x01, 0x0060},
- {0xaa, 0x02, 0x0060},
- {0xaa, 0x17, 0x0018},
- {0xaa, 0x18, 0x00ba},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R116_RGAIN},
- {0xa0, 0x46, ZC3XX_R118_BGAIN},
- {0xa0, 0x04, ZC3XX_R113_RGB03},
-
- {0xa1, 0x01, 0x0002},
- {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xfe, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf7, ZC3XX_R10D_RGB10},
- {0xa0, 0x4d, ZC3XX_R10E_RGB11},
- {0xa0, 0xfc, ZC3XX_R10F_RGB12},
- {0xa0, 0x00, ZC3XX_R110_RGB20},
- {0xa0, 0xf6, ZC3XX_R111_RGB21},
- {0xa0, 0x4a, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
- {0xa0, 0x3a, ZC3XX_R121_GAMMA01},
- {0xa0, 0x5b, ZC3XX_R122_GAMMA02},
- {0xa0, 0x7c, ZC3XX_R123_GAMMA03},
- {0xa0, 0x94, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa9, ZC3XX_R125_GAMMA05},
- {0xa0, 0xbb, ZC3XX_R126_GAMMA06},
- {0xa0, 0xca, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd7, ZC3XX_R128_GAMMA08},
- {0xa0, 0xe1, ZC3XX_R129_GAMMA09},
- {0xa0, 0xea, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x20, ZC3XX_R130_GAMMA10},
- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
- {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xfe, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf7, ZC3XX_R10D_RGB10},
- {0xa0, 0x4d, ZC3XX_R10E_RGB11},
- {0xa0, 0xfc, ZC3XX_R10F_RGB12},
- {0xa0, 0x00, ZC3XX_R110_RGB20},
- {0xa0, 0xf6, ZC3XX_R111_RGB21},
- {0xa0, 0x4a, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xaa, 0x10, 0x000d},
- {0xaa, 0x76, 0x0002},
- {0xaa, 0x2a, 0x0081},
- {0xaa, 0x2b, 0x0000},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xaa, 0x13, 0x00c3},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-
-static const struct usb_action pas106b_Initial_com[] = {
-/* Sream and Sensor specific */
- {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */
-/* System */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */
-/* Picture size */
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */
- {0xa0, 0x03, 0x003a},
- {0xa0, 0x0c, 0x003b},
- {0xa0, 0x04, 0x0038},
- {}
-};
-
-static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-/* Sream and Sensor specific */
- {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
-/* Picture size */
- {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW},
-/* System */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-/* Sream and Sensor specific */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
-/* Sensor Interface */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
-/* Window inside sensor array */
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
-/* Init the sensor */
- {0xaa, 0x02, 0x0004},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x09, 0x0005},
- {0xaa, 0x0a, 0x0002},
- {0xaa, 0x0b, 0x0002},
- {0xaa, 0x0c, 0x0005},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x0e, 0x0002},
- {0xaa, 0x14, 0x0081},
-/* Other registers */
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
-/* Frame retreiving */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-/* Gains */
- {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
-/* Unknown */
- {0xa0, 0x00, 0x01ad},
-/* Sharpness */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registers */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-/* Auto exposure and white balance */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-/*Dead pixels */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-/* EEPROM */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
-/* Other registers */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-/* Auto exposure and white balance */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-/*Dead pixels */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-/* EEPROM */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
-
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
-/* Auto correction */
- {0xa0, 0x03, ZC3XX_R181_WINXSTART},
- {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
- {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
- {0xa0, 0x03, ZC3XX_R184_WINYSTART},
- {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
- {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-/* Auto exposure and white balance */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
-/* sensor on */
- {0xaa, 0x07, 0x00b1},
- {0xaa, 0x05, 0x0003},
- {0xaa, 0x04, 0x0001},
- {0xaa, 0x03, 0x003b},
-/* Gains */
- {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
-/* Auto correction */
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-/* Gains */
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
- {}
-};
-
-static const struct usb_action pas106b_Initial[] = { /* 352x288 */
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
-/* Sream and Sensor specific */
- {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT},
-/* Picture size */
- {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW},
-/* System */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
-/* Sream and Sensor specific */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
-/* Sensor Interface */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
-/* Window inside sensor array */
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW},
-/* Init the sensor */
- {0xaa, 0x02, 0x0004},
- {0xaa, 0x08, 0x0000},
- {0xaa, 0x09, 0x0005},
- {0xaa, 0x0a, 0x0002},
- {0xaa, 0x0b, 0x0002},
- {0xaa, 0x0c, 0x0005},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x0e, 0x0002},
- {0xaa, 0x14, 0x0081},
-/* Other registers */
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
-/* Frame retreiving */
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
-/* Gains */
- {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN},
-/* Unknown */
- {0xa0, 0x00, 0x01ad},
-/* Sharpness */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
-/* Other registers */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-/* Auto exposure and white balance */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x80, ZC3XX_R18D_YTARGET},
-/*Dead pixels */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-/* EEPROM */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
-/* Other registers */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
-/* Auto exposure and white balance */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
-/*Dead pixels */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
-/* EEPROM */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
-/* JPEG control */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05},
-
- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
- {0xa0, 0x58, ZC3XX_R10E_RGB11},
- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
- {0xa0, 0xf4, ZC3XX_R110_RGB20},
- {0xa0, 0xf4, ZC3XX_R111_RGB21},
- {0xa0, 0x58, ZC3XX_R112_RGB22},
-/* Auto correction */
- {0xa0, 0x03, ZC3XX_R181_WINXSTART},
- {0xa0, 0x08, ZC3XX_R182_WINXWIDTH},
- {0xa0, 0x16, ZC3XX_R183_WINXCENTER},
- {0xa0, 0x03, ZC3XX_R184_WINYSTART},
- {0xa0, 0x05, ZC3XX_R185_WINYWIDTH},
- {0xa0, 0x14, ZC3XX_R186_WINYCENTER},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
-
-/* Auto exposure and white balance */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW},
-
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW},
-
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
-/* sensor on */
- {0xaa, 0x07, 0x00b1},
- {0xaa, 0x05, 0x0003},
- {0xaa, 0x04, 0x0001},
- {0xaa, 0x03, 0x003b},
-/* Gains */
- {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
-/* Auto correction */
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
-/* Gains */
- {0xa0, 0x40, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x40, ZC3XX_R118_BGAIN},
-
- {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */
- {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */
- {}
-};
-static const struct usb_action pas106b_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
- {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
- {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */
- {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */
- {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */
- {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */
- {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
- {}
-};
-static const struct usb_action pas106b_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
- {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */
- {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */
- {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */
- {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
- {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */
- {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */
- {}
-};
-static const struct usb_action pas106b_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */
- {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */
- {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */
- {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */
- {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {}
-};
-
-/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
-static const struct usb_action pas202b_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
- {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
- {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
- {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
- {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
- {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
- {0xaa, 0x0c, 0x0006},
- {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
- {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
- {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
- {}
-};
-static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
- {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
- {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
- {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
- {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
- {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
- {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
- {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
- {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
- {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
- {0xaa, 0x0c, 0x0006},
- {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
- {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
- {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
- {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
- {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
- {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
- {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
- {}
-};
-static const struct usb_action pas202b_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x001b},
- {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
- {0xaa, 0x04, 0x0008},
- {0xaa, 0x05, 0x001b},
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
- {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-static const struct usb_action pas202b_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x003d},
- {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x003d},
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-static const struct usb_action pas202b_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
- {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
- {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
- {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
- {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
- {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-static const struct usb_action pas202b_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x0008},
- {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x0008},
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
- {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-static const struct usb_action pas202b_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
- {0xaa, 0x21, 0x0006},
- {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
- {0xaa, 0x05, 0x0006},
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-static const struct usb_action pas202b_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x000c},
- {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x000c},
- {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
- {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
- {}
-};
-
-/* mt9v111 (mi0360soc) and pb0330 from vm30x.inf 0ac8:301b 07/02/13 */
-static const struct usb_action mt9v111_1_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0001},
- {0xaa, 0x06, 0x0000},
- {0xaa, 0x08, 0x0483},
- {0xaa, 0x01, 0x0004},
- {0xaa, 0x08, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
- {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100},
- {0xaa, 0x35, 0x507f},
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x007f},
- {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
- {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
- {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action mt9v111_1_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0001},
- {0xaa, 0x06, 0x0000},
- {0xaa, 0x08, 0x0483},
- {0xaa, 0x01, 0x0004},
- {0xaa, 0x08, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e7},
- {0xaa, 0x04, 0x0287},
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x5100},
- {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/
- {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/
- {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
- {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action mt9v111_1_AE50HZ[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0562},
- {0xbb, 0x01, 0x09aa},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_1_AE50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0509},
- {0xbb, 0x01, 0x0934},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_1_AE60HZ[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x003d},
- {0xaa, 0x09, 0x016e},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_1_AE60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0509},
- {0xbb, 0x01, 0x0983},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_1_AENoFliker[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0509},
- {0xbb, 0x01, 0x0960},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_1_AENoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0534},
- {0xbb, 0x02, 0x0960},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-/* from usbvm303.inf 0ac8:303b 07/03/25 (3 - tas5130c) */
-static const struct usb_action mt9v111_3_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0001}, /* select IFP/SOC registers */
- {0xaa, 0x06, 0x0000}, /* operating mode control */
- {0xaa, 0x08, 0x0483}, /* output format control */
- /* H red first, V red or blue first,
- * raw Bayer, auto flicker */
- {0xaa, 0x01, 0x0004}, /* select sensor core registers */
- {0xaa, 0x08, 0x0006}, /* row start */
- {0xaa, 0x02, 0x0011}, /* column start */
- {0xaa, 0x03, 0x01e5}, /* window height - 1 */
- {0xaa, 0x04, 0x0285}, /* window width - 1 */
- {0xaa, 0x07, 0x3002}, /* output control */
- {0xaa, 0x20, 0x1100}, /* read mode: bits 8 & 12 (?) */
- {0xaa, 0x35, 0x007f}, /* global gain */
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x007f}, /* green1 gain */
- {0xaa, 0x2c, 0x007f}, /* blue gain */
- {0xaa, 0x2d, 0x007f}, /* red gain */
- {0xaa, 0x2e, 0x007f}, /* green2 gain */
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x80, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action mt9v111_3_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0001},
- {0xaa, 0x06, 0x0000},
- {0xaa, 0x08, 0x0483},
- {0xaa, 0x01, 0x0004},
- {0xaa, 0x08, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e7},
- {0xaa, 0x04, 0x0287},
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x35, 0x007f},
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xaa, 0x2b, 0x007f},
- {0xaa, 0x2c, 0x007f},
- {0xaa, 0x2d, 0x007f},
- {0xaa, 0x2e, 0x007f},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x80, ZC3XX_R18D_YTARGET},
- {0xa0, 0x61, ZC3XX_R116_RGAIN},
- {0xa0, 0x65, ZC3XX_R118_BGAIN},
- {}
-};
-static const struct usb_action mt9v111_3_AE50HZ[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009}, /* horizontal blanking */
- {0xaa, 0x09, 0x01ce}, /* shutter width */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_3_AE50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009},
- {0xaa, 0x09, 0x01ce},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_3_AE60HZ[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009},
- {0xaa, 0x09, 0x0083},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_3_AE60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0009},
- {0xaa, 0x09, 0x0083},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_3_AENoFliker[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0034},
- {0xaa, 0x09, 0x0260},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-static const struct usb_action mt9v111_3_AENoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xaa, 0x05, 0x0034},
- {0xaa, 0x09, 0x0260},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {}
-};
-
-static const struct usb_action pb0330_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
- {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
- {0xaa, 0x06, 0x0003},
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x2f, 0xf7b0},
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x34, 0x0100},
- {0xaa, 0x35, 0x0060},
- {0xaa, 0x3d, 0x068f},
- {0xaa, 0x40, 0x01e0},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
- {}
-};
-static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
- {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
- {0xdd, 0x00, 0x0200},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xaa, 0x01, 0x0006},
- {0xaa, 0x02, 0x0011},
- {0xaa, 0x03, 0x01e7},
- {0xaa, 0x04, 0x0287},
- {0xaa, 0x06, 0x0003},
- {0xaa, 0x07, 0x3002},
- {0xaa, 0x20, 0x1100},
- {0xaa, 0x2f, 0xf7b0},
- {0xaa, 0x30, 0x0005},
- {0xaa, 0x31, 0x0000},
- {0xaa, 0x34, 0x0100},
- {0xaa, 0x35, 0x0060},
- {0xaa, 0x3d, 0x068f},
- {0xaa, 0x40, 0x01e0},
- {0xaa, 0x58, 0x0078},
- {0xaa, 0x62, 0x0411},
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x09, 0x01ad},
- {0xa0, 0x15, 0x01ae},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
- {}
-};
-static const struct usb_action pb0330_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x055c},
- {0xbb, 0x01, 0x09aa},
- {0xbb, 0x00, 0x1001},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action pb0330_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0566},
- {0xbb, 0x02, 0x09b2},
- {0xbb, 0x00, 0x1002},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action pb0330_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0535},
- {0xbb, 0x01, 0x0974},
- {0xbb, 0x00, 0x1001},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action pb0330_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0535},
- {0xbb, 0x02, 0x096c},
- {0xbb, 0x00, 0x1002},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action pb0330_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0509},
- {0xbb, 0x02, 0x0940},
- {0xbb, 0x00, 0x1002},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {}
-};
-static const struct usb_action pb0330_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xbb, 0x00, 0x0535},
- {0xbb, 0x01, 0x0980},
- {0xbb, 0x00, 0x1001},
- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
- {}
-};
-
-/* from oem9.inf */
-static const struct usb_action po2030_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
- {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
- {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
- {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
- {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
- {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
- {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
- {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
- {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */
- {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
- {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */
- {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */
- {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
- {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
- {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
- {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
- {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
- {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
- {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
- {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
- {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
- {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
- {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
- {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
- {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
- {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
- {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
- {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
- {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
- {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
- {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
- {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
- {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
- {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
- {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
- {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
- {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
- {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
- {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
- {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
- {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
- {}
-};
-
-/* from oem9.inf */
-static const struct usb_action po2030_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
- {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */
- {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */
- {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */
- {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */
- {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */
- {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */
- {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */
- {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
- {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */
- {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */
- {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */
- {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */
- {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */
- {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */
- {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */
- {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */
- {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */
- {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */
- {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */
- {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */
- {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */
- {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */
- {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */
- {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */
- {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */
- {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */
- {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */
- {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */
- {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */
- {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */
- {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
- {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */
- {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */
- {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */
- {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */
- {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */
- {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */
- {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */
- {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */
- {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */
- {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */
- {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
- {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
- {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */
- {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */
- {}
-};
-
-static const struct usb_action po2030_50HZ[] = {
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
- {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
- {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */
- {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */
- {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */
- {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */
- {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
- {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {}
-};
-
-static const struct usb_action po2030_60HZ[] = {
- {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
- {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */
- {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */
- {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */
- {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */
- {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
- {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */
- {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */
- /* win: 01,8d,80 */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */
- {}
-};
-
-static const struct usb_action po2030_NoFliker[] = {
- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
- {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
- {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */
- {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */
- {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */
- {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */
- {}
-};
-
-static const struct usb_action tas5130c_InitialScale[] = { /* 320x240 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
-
- {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
- {}
-};
-static const struct usb_action tas5130c_Initial[] = { /* 640x480 */
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
- {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
- {}
-};
-static const struct usb_action tas5130c_50HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
- {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
- {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-static const struct usb_action tas5130c_50HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
- {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-static const struct usb_action tas5130c_60HZ[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
- {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
- {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-static const struct usb_action tas5130c_60HZScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
- {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
- {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
- {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-static const struct usb_action tas5130c_NoFliker[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
- {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
- {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-
-static const struct usb_action tas5130c_NoFlikerScale[] = {
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
- {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */
- {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */
- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
- {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
- {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
- {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
- {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
- {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
- {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
- {}
-};
-
-/* from usbvm305.inf 0ac8:305b 07/06/15 (3 - tas5130c) */
-static const struct usb_action gc0303_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
- {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc,
- * 6<->8 */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc,
- * 6<->8 */
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
- {0xaa, 0x01, 0x0000},
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
- {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
- {0xaa, 0x1b, 0x0000},
- {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
- {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
- {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
- {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
- {0xaa, 0x0a, 0x0002},
- {0xaa, 0x0b, 0x0000},
- {0xaa, 0x0c, 0x0002},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x0e, 0x0002},
- {0xaa, 0x0f, 0x0000},
- {0xaa, 0x10, 0x0002},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
- {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */
- {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
- {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */
- {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
- {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
- {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
- {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
- {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
- {0xaa, 0x1b, 0x0000},
- {}
-};
-
-static const struct usb_action gc0303_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
- {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
- {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */
- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */
- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */
- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */
- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc,
- * 8<->6 */
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc,
- * 8<->6 */
- {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */
- {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */
- {0xaa, 0x01, 0x0000},
- {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
- {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
- {0xaa, 0x1b, 0x0000},
- {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */
- {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */
- {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */
- {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0000},
- {0xaa, 0x0c, 0x0001},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xaa, 0x10, 0x0001},
- {0xaa, 0x11, 0x0000},
- {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
- {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
- {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
- {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */
- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */
- {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */
- {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */
- {0xa0, 0x58, ZC3XX_R1A8_DIGITALGAIN},
- {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */
- {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
- {0xaa, 0x1b, 0x0000},
- {}
-};
-static const struct usb_action gc0303_50HZ[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
- {0xaa, 0x84, 0x0063},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
- {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
- {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
- {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
- {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
- {}
-};
-
-static const struct usb_action gc0303_50HZScale[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */
- {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
- {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */
- {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
- {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
- {0xa0, 0x48, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
- {0xa0, 0x7f, ZC3XX_R18D_YTARGET},
- {}
-};
-
-static const struct usb_action gc0303_60HZ[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000},
- {0xaa, 0x84, 0x003b},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
- {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */
- {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
- {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
- {0xa0, 0x80, ZC3XX_R18D_YTARGET},
- {}
-};
-
-static const struct usb_action gc0303_60HZScale[] = {
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000},
- {0xaa, 0x84, 0x0076},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
- {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */
- {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */
- {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */
- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */
- {0xa0, 0x80, ZC3XX_R18D_YTARGET},
- {}
-};
-
-static const struct usb_action gc0303_NoFliker[] = {
- {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
- {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */
- {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
- {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
- {}
-};
-
-static const struct usb_action gc0303_NoFlikerScale[] = {
- {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
- {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
- {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
- {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */
- {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */
- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */
- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */
- {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */
- {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */
- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */
- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */
- {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */
- {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */
- {}
-};
-
-static u8 reg_r(struct gspca_dev *gspca_dev,
- u16 index)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return 0;
- ret = usb_control_msg(gspca_dev->dev,
- usb_rcvctrlpipe(gspca_dev->dev, 0),
- 0xa1,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0x01, /* value */
- index, gspca_dev->usb_buf, 1,
- 500);
- if (ret < 0) {
- pr_err("reg_r err %d\n", ret);
- gspca_dev->usb_err = ret;
- return 0;
- }
- return gspca_dev->usb_buf[0];
-}
-
-static void reg_w(struct gspca_dev *gspca_dev,
- u8 value,
- u16 index)
-{
- int ret;
-
- if (gspca_dev->usb_err < 0)
- return;
- ret = usb_control_msg(gspca_dev->dev,
- usb_sndctrlpipe(gspca_dev->dev, 0),
- 0xa0,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0,
- 500);
- if (ret < 0) {
- pr_err("reg_w_i err %d\n", ret);
- gspca_dev->usb_err = ret;
- }
-}
-
-static u16 i2c_read(struct gspca_dev *gspca_dev,
- u8 reg)
-{
- u8 retbyte;
- u16 retval;
-
- if (gspca_dev->usb_err < 0)
- return 0;
- reg_w(gspca_dev, reg, 0x0092);
- reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */
- msleep(20);
- retbyte = reg_r(gspca_dev, 0x0091); /* read status */
- if (retbyte != 0x00)
- pr_err("i2c_r status error %02x\n", retbyte);
- retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */
- retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */
- return retval;
-}
-
-static u8 i2c_write(struct gspca_dev *gspca_dev,
- u8 reg,
- u8 valL,
- u8 valH)
-{
- u8 retbyte;
-
- if (gspca_dev->usb_err < 0)
- return 0;
- reg_w(gspca_dev, reg, 0x92);
- reg_w(gspca_dev, valL, 0x93);
- reg_w(gspca_dev, valH, 0x94);
- reg_w(gspca_dev, 0x01, 0x90); /* <- write command */
- msleep(1);
- retbyte = reg_r(gspca_dev, 0x0091); /* read status */
- if (retbyte != 0x00)
- pr_err("i2c_w status error %02x\n", retbyte);
- return retbyte;
-}
-
-static void usb_exchange(struct gspca_dev *gspca_dev,
- const struct usb_action *action)
-{
- while (action->req) {
- switch (action->req) {
- case 0xa0: /* write register */
- reg_w(gspca_dev, action->val, action->idx);
- break;
- case 0xa1: /* read status */
- reg_r(gspca_dev, action->idx);
- break;
- case 0xaa:
- i2c_write(gspca_dev,
- action->val, /* reg */
- action->idx & 0xff, /* valL */
- action->idx >> 8); /* valH */
- break;
- case 0xbb:
- i2c_write(gspca_dev,
- action->idx >> 8, /* reg */
- action->idx & 0xff, /* valL */
- action->val); /* valH */
- break;
- default:
-/* case 0xdd: * delay */
- msleep(action->idx);
- break;
- }
- action++;
- msleep(1);
- }
-}
-
-static void setmatrix(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
- const u8 *matrix;
- static const u8 adcm2700_matrix[9] =
-/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
-/*ms-win*/
- {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
- static const u8 gc0305_matrix[9] =
- {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
- static const u8 ov7620_matrix[9] =
- {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
- static const u8 pas202b_matrix[9] =
- {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
- static const u8 po2030_matrix[9] =
- {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
- static const u8 tas5130c_matrix[9] =
- {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
- static const u8 gc0303_matrix[9] =
- {0x6c, 0xea, 0xea, 0xea, 0x6c, 0xea, 0xea, 0xea, 0x6c};
- static const u8 *matrix_tb[SENSOR_MAX] = {
- [SENSOR_ADCM2700] = adcm2700_matrix,
- [SENSOR_CS2102] = ov7620_matrix,
- [SENSOR_CS2102K] = NULL,
- [SENSOR_GC0303] = gc0303_matrix,
- [SENSOR_GC0305] = gc0305_matrix,
- [SENSOR_HDCS2020] = NULL,
- [SENSOR_HV7131B] = NULL,
- [SENSOR_HV7131R] = po2030_matrix,
- [SENSOR_ICM105A] = po2030_matrix,
- [SENSOR_MC501CB] = NULL,
- [SENSOR_MT9V111_1] = gc0305_matrix,
- [SENSOR_MT9V111_3] = gc0305_matrix,
- [SENSOR_OV7620] = ov7620_matrix,
- [SENSOR_OV7630C] = NULL,
- [SENSOR_PAS106] = NULL,
- [SENSOR_PAS202B] = pas202b_matrix,
- [SENSOR_PB0330] = gc0305_matrix,
- [SENSOR_PO2030] = po2030_matrix,
- [SENSOR_TAS5130C] = tas5130c_matrix,
- };
-
- matrix = matrix_tb[sd->sensor];
- if (matrix == NULL)
- return; /* matrix already loaded */
- for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
- reg_w(gspca_dev, matrix[i], 0x010a + i);
-}
-
-static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
-{
- static const u8 sharpness_tb[][2] = {
- {0x02, 0x03},
- {0x04, 0x07},
- {0x08, 0x0f},
- {0x10, 0x1e}
- };
-
- reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6);
- reg_r(gspca_dev, 0x01c8);
- reg_r(gspca_dev, 0x01c9);
- reg_r(gspca_dev, 0x01ca);
- reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev,
- s32 gamma, s32 brightness, s32 contrast)
-{
- const u8 *Tgamma;
- int g, i, adj, gp1, gp2;
- u8 gr[16];
- static const u8 delta_b[16] = /* delta for brightness */
- {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
- 0x1d, 0x1b, 0x1b, 0x1b, 0x19, 0x18, 0x18, 0x18};
- static const u8 delta_c[16] = /* delta for contrast */
- {0x2c, 0x1a, 0x12, 0x0c, 0x0a, 0x06, 0x06, 0x06,
- 0x04, 0x06, 0x04, 0x04, 0x03, 0x03, 0x02, 0x02};
- static const u8 gamma_tb[6][16] = {
- {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
- 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
- {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
- 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
- {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
- 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
- {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
- {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
- 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
- {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
- 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
- };
-
- Tgamma = gamma_tb[gamma - 1];
-
- contrast -= 128; /* -128 / 127 */
- brightness -= 128; /* -128 / 92 */
- adj = 0;
- gp1 = gp2 = 0;
- for (i = 0; i < 16; i++) {
- g = Tgamma[i] + delta_b[i] * brightness / 256
- - delta_c[i] * contrast / 256 - adj / 2;
- if (g > 0xff)
- g = 0xff;
- else if (g < 0)
- g = 0;
- reg_w(gspca_dev, g, 0x0120 + i); /* gamma */
- if (contrast > 0)
- adj--;
- else if (contrast < 0)
- adj++;
- if (i > 1)
- gr[i - 1] = (g - gp2) / 2;
- else if (i != 0)
- gr[0] = gp1 == 0 ? 0 : (g - gp1);
- gp2 = gp1;
- gp1 = g;
- }
- gr[15] = (0xff - gp2) / 2;
- for (i = 0; i < 16; i++)
- reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */
-}
-
-static s32 getexposure(struct gspca_dev *gspca_dev)
-{
- return (i2c_read(gspca_dev, 0x25) << 9)
- | (i2c_read(gspca_dev, 0x26) << 1)
- | (i2c_read(gspca_dev, 0x27) >> 7);
-}
-
-static void setexposure(struct gspca_dev *gspca_dev, s32 val)
-{
- i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
- i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
- i2c_write(gspca_dev, 0x27, val << 7, 0x00);
-}
-
-static void setquality(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]);
- reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
-}
-
-/* Matches the sensor's internal frame rate to the lighting frequency.
- * Valid frequencies are:
- * 50Hz, for European and Asian lighting (default)
- * 60Hz, for American lighting
- * 0 = No Fliker (for outdoore usage)
- */
-static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i, mode;
- const struct usb_action *zc3_freq;
- static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
- [SENSOR_ADCM2700] =
- {adcm2700_NoFliker, adcm2700_NoFliker,
- adcm2700_50HZ, adcm2700_50HZ,
- adcm2700_60HZ, adcm2700_60HZ},
- [SENSOR_CS2102] =
- {cs2102_NoFliker, cs2102_NoFlikerScale,
- cs2102_50HZ, cs2102_50HZScale,
- cs2102_60HZ, cs2102_60HZScale},
- [SENSOR_CS2102K] =
- {cs2102_NoFliker, cs2102_NoFlikerScale,
- NULL, NULL, /* currently disabled */
- NULL, NULL},
- [SENSOR_GC0303] =
- {gc0303_NoFliker, gc0303_NoFlikerScale,
- gc0303_50HZ, gc0303_50HZScale,
- gc0303_60HZ, gc0303_60HZScale},
- [SENSOR_GC0305] =
- {gc0305_NoFliker, gc0305_NoFliker,
- gc0305_50HZ, gc0305_50HZ,
- gc0305_60HZ, gc0305_60HZ},
- [SENSOR_HDCS2020] =
- {hdcs2020_NoFliker, hdcs2020_NoFliker,
- hdcs2020_50HZ, hdcs2020_50HZ,
- hdcs2020_60HZ, hdcs2020_60HZ},
- [SENSOR_HV7131B] =
- {hv7131b_NoFliker, hv7131b_NoFlikerScale,
- hv7131b_50HZ, hv7131b_50HZScale,
- hv7131b_60HZ, hv7131b_60HZScale},
- [SENSOR_HV7131R] =
- {hv7131r_NoFliker, hv7131r_NoFlikerScale,
- hv7131r_50HZ, hv7131r_50HZScale,
- hv7131r_60HZ, hv7131r_60HZScale},
- [SENSOR_ICM105A] =
- {icm105a_NoFliker, icm105a_NoFlikerScale,
- icm105a_50HZ, icm105a_50HZScale,
- icm105a_60HZ, icm105a_60HZScale},
- [SENSOR_MC501CB] =
- {mc501cb_NoFliker, mc501cb_NoFlikerScale,
- mc501cb_50HZ, mc501cb_50HZScale,
- mc501cb_60HZ, mc501cb_60HZScale},
- [SENSOR_MT9V111_1] =
- {mt9v111_1_AENoFliker, mt9v111_1_AENoFlikerScale,
- mt9v111_1_AE50HZ, mt9v111_1_AE50HZScale,
- mt9v111_1_AE60HZ, mt9v111_1_AE60HZScale},
- [SENSOR_MT9V111_3] =
- {mt9v111_3_AENoFliker, mt9v111_3_AENoFlikerScale,
- mt9v111_3_AE50HZ, mt9v111_3_AE50HZScale,
- mt9v111_3_AE60HZ, mt9v111_3_AE60HZScale},
- [SENSOR_OV7620] =
- {ov7620_NoFliker, ov7620_NoFliker,
- ov7620_50HZ, ov7620_50HZ,
- ov7620_60HZ, ov7620_60HZ},
- [SENSOR_OV7630C] =
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
- [SENSOR_PAS106] =
- {pas106b_NoFliker, pas106b_NoFliker,
- pas106b_50HZ, pas106b_50HZ,
- pas106b_60HZ, pas106b_60HZ},
- [SENSOR_PAS202B] =
- {pas202b_NoFliker, pas202b_NoFlikerScale,
- pas202b_50HZ, pas202b_50HZScale,
- pas202b_60HZ, pas202b_60HZScale},
- [SENSOR_PB0330] =
- {pb0330_NoFliker, pb0330_NoFlikerScale,
- pb0330_50HZ, pb0330_50HZScale,
- pb0330_60HZ, pb0330_60HZScale},
- [SENSOR_PO2030] =
- {po2030_NoFliker, po2030_NoFliker,
- po2030_50HZ, po2030_50HZ,
- po2030_60HZ, po2030_60HZ},
- [SENSOR_TAS5130C] =
- {tas5130c_NoFliker, tas5130c_NoFlikerScale,
- tas5130c_50HZ, tas5130c_50HZScale,
- tas5130c_60HZ, tas5130c_60HZScale},
- };
-
- i = val * 2;
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- if (mode)
- i++; /* 320x240 */
- zc3_freq = freq_tb[sd->sensor][i];
- if (zc3_freq == NULL)
- return;
- usb_exchange(gspca_dev, zc3_freq);
- switch (sd->sensor) {
- case SENSOR_GC0305:
- if (mode /* if 320x240 */
- && val == 1) /* and 50Hz */
- reg_w(gspca_dev, 0x85, 0x018d);
- /* win: 0x80, 0x018d */
- break;
- case SENSOR_OV7620:
- if (!mode) { /* if 640x480 */
- if (val != 0) /* and filter */
- reg_w(gspca_dev, 0x40, 0x0002);
- else
- reg_w(gspca_dev, 0x44, 0x0002);
- }
- break;
- case SENSOR_PAS202B:
- reg_w(gspca_dev, 0x00, 0x01a7);
- break;
- }
-}
-
-static void setautogain(struct gspca_dev *gspca_dev, s32 val)
-{
- reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
-}
-
-/*
- * Update the transfer parameters.
- * This function is executed from a work queue.
- */
-static void transfer_update(struct work_struct *work)
-{
- struct sd *sd = container_of(work, struct sd, work);
- struct gspca_dev *gspca_dev = &sd->gspca_dev;
- int change, good;
- u8 reg07, reg11;
-
- /* reg07 gets set to 0 by sd_start before starting us */
- reg07 = 0;
-
- good = 0;
- for (;;) {
- msleep(100);
-
- mutex_lock(&gspca_dev->usb_lock);
-#ifdef CONFIG_PM
- if (gspca_dev->frozen)
- goto err;
-#endif
- if (!gspca_dev->dev || !gspca_dev->streaming)
- goto err;
-
- /* Bit 0 of register 11 indicates FIFO overflow */
- gspca_dev->usb_err = 0;
- reg11 = reg_r(gspca_dev, 0x0011);
- if (gspca_dev->usb_err)
- goto err;
-
- change = reg11 & 0x01;
- if (change) { /* overflow */
- good = 0;
-
- if (reg07 == 0) /* Bit Rate Control not enabled? */
- reg07 = 0x32; /* Allow 98 bytes / unit */
- else if (reg07 > 2)
- reg07 -= 2; /* Decrease allowed bytes / unit */
- else
- change = 0;
- } else { /* no overflow */
- good++;
- if (good >= 10) {
- good = 0;
- if (reg07) { /* BRC enabled? */
- change = 1;
- if (reg07 < 0x32)
- reg07 += 2;
- else
- reg07 = 0;
- }
- }
- }
- if (change) {
- gspca_dev->usb_err = 0;
- reg_w(gspca_dev, reg07, 0x0007);
- if (gspca_dev->usb_err)
- goto err;
- }
- mutex_unlock(&gspca_dev->usb_lock);
- }
- return;
-err:
- mutex_unlock(&gspca_dev->usb_lock);
-}
-
-static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
-{
- reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */
- switch (sensor) {
- case SENSOR_PAS106:
- reg_w(gspca_dev, 0x03, 0x003a);
- reg_w(gspca_dev, 0x0c, 0x003b);
- reg_w(gspca_dev, 0x08, 0x0038);
- break;
- case SENSOR_ADCM2700:
- case SENSOR_GC0305:
- case SENSOR_OV7620:
- case SENSOR_MT9V111_1:
- case SENSOR_MT9V111_3:
- case SENSOR_PB0330:
- case SENSOR_PO2030:
- reg_w(gspca_dev, 0x0d, 0x003a);
- reg_w(gspca_dev, 0x02, 0x003b);
- reg_w(gspca_dev, 0x00, 0x0038);
- break;
- case SENSOR_HV7131R:
- case SENSOR_PAS202B:
- reg_w(gspca_dev, 0x03, 0x003b);
- reg_w(gspca_dev, 0x0c, 0x003a);
- reg_w(gspca_dev, 0x0b, 0x0039);
- if (sensor == SENSOR_PAS202B)
- reg_w(gspca_dev, 0x0b, 0x0038);
- break;
- }
-}
-
-/* start probe 2 wires */
-static void start_2wr_probe(struct gspca_dev *gspca_dev, int sensor)
-{
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, sensor, 0x0010);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0x03, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0012);
-/* msleep(2); */
-}
-
-static int sif_probe(struct gspca_dev *gspca_dev)
-{
- u16 checkword;
-
- start_2wr_probe(gspca_dev, 0x0f); /* PAS106 */
- reg_w(gspca_dev, 0x08, 0x008d);
- msleep(150);
- checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4)
- | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4);
- PDEBUG(D_PROBE, "probe sif 0x%04x", checkword);
- if (checkword == 0x0007) {
- send_unknown(gspca_dev, SENSOR_PAS106);
- return 0x0f; /* PAS106 */
- }
- return -1;
-}
-
-static int vga_2wr_probe(struct gspca_dev *gspca_dev)
-{
- u16 retword;
-
- start_2wr_probe(gspca_dev, 0x00); /* HV7131B */
- i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
- retword = i2c_read(gspca_dev, 0x01);
- if (retword != 0)
- return 0x00; /* HV7131B */
-
- start_2wr_probe(gspca_dev, 0x04); /* CS2102 */
- i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
- retword = i2c_read(gspca_dev, 0x01);
- if (retword != 0)
- return 0x04; /* CS2102 */
-
- start_2wr_probe(gspca_dev, 0x06); /* OmniVision */
- reg_w(gspca_dev, 0x08, 0x008d);
- i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
- retword = i2c_read(gspca_dev, 0x11);
- if (retword != 0) {
- /* (should have returned 0xaa) --> Omnivision? */
- /* reg_r 0x10 -> 0x06 --> */
- goto ov_check;
- }
-
- start_2wr_probe(gspca_dev, 0x08); /* HDCS2020 */
- i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
- i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
- retword = i2c_read(gspca_dev, 0x15);
- if (retword != 0)
- return 0x08; /* HDCS2020 */
-
- start_2wr_probe(gspca_dev, 0x0a); /* PB0330 */
- i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
- retword = i2c_read(gspca_dev, 0x07);
- if (retword != 0)
- return 0x0a; /* PB0330 */
- retword = i2c_read(gspca_dev, 0x03);
- if (retword != 0)
- return 0x0a; /* PB0330 ?? */
- retword = i2c_read(gspca_dev, 0x04);
- if (retword != 0)
- return 0x0a; /* PB0330 ?? */
-
- start_2wr_probe(gspca_dev, 0x0c); /* ICM105A */
- i2c_write(gspca_dev, 0x01, 0x11, 0x00);
- retword = i2c_read(gspca_dev, 0x01);
- if (retword != 0)
- return 0x0c; /* ICM105A */
-
- start_2wr_probe(gspca_dev, 0x0e); /* PAS202BCB */
- reg_w(gspca_dev, 0x08, 0x008d);
- i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
- msleep(50);
- retword = i2c_read(gspca_dev, 0x03);
- if (retword != 0) {
- send_unknown(gspca_dev, SENSOR_PAS202B);
- return 0x0e; /* PAS202BCB */
- }
-
- start_2wr_probe(gspca_dev, 0x02); /* TAS5130C */
- i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
- retword = i2c_read(gspca_dev, 0x01);
- if (retword != 0)
- return 0x02; /* TAS5130C */
-ov_check:
- reg_r(gspca_dev, 0x0010); /* ?? */
- reg_r(gspca_dev, 0x0010);
-
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0x06, 0x0010); /* OmniVision */
- reg_w(gspca_dev, 0xa1, 0x008b);
- reg_w(gspca_dev, 0x08, 0x008d);
- msleep(500);
- reg_w(gspca_dev, 0x01, 0x0012);
- i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
- retword = i2c_read(gspca_dev, 0x0a) << 8;
- retword |= i2c_read(gspca_dev, 0x0b);
- PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
- switch (retword) {
- case 0x7631: /* OV7630C */
- reg_w(gspca_dev, 0x06, 0x0010);
- break;
- case 0x7620: /* OV7620 */
- case 0x7648: /* OV7648 */
- break;
- default:
- return -1; /* not OmniVision */
- }
- return retword;
-}
-
-struct sensor_by_chipset_revision {
- u16 revision;
- u8 internal_sensor_id;
-};
-static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
- {0xc000, 0x12}, /* TAS5130C */
- {0xc001, 0x13}, /* MT9V111 */
- {0xe001, 0x13},
- {0x8001, 0x13},
- {0x8000, 0x14}, /* CS2102K */
- {0x8400, 0x15}, /* MT9V111 */
- {0xe400, 0x15},
-};
-
-static int vga_3wr_probe(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
- u16 retword;
-
-/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
- reg_w(gspca_dev, 0x02, 0x0010);
- reg_r(gspca_dev, 0x0010);
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, 0x00, 0x0010);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0x91, 0x008b);
- reg_w(gspca_dev, 0x03, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0012);
- reg_w(gspca_dev, 0x05, 0x0012);
- retword = i2c_read(gspca_dev, 0x14);
- if (retword != 0)
- return 0x11; /* HV7131R */
- retword = i2c_read(gspca_dev, 0x15);
- if (retword != 0)
- return 0x11; /* HV7131R */
- retword = i2c_read(gspca_dev, 0x16);
- if (retword != 0)
- return 0x11; /* HV7131R */
-
- reg_w(gspca_dev, 0x02, 0x0010);
- retword = reg_r(gspca_dev, 0x000b) << 8;
- retword |= reg_r(gspca_dev, 0x000a);
- PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
- reg_r(gspca_dev, 0x0010);
- if ((retword & 0xff00) == 0x6400)
- return 0x02; /* TAS5130C */
- for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
- if (chipset_revision_sensor[i].revision == retword) {
- sd->chip_revision = retword;
- send_unknown(gspca_dev, SENSOR_PB0330);
- return chipset_revision_sensor[i].internal_sensor_id;
- }
- }
-
- reg_w(gspca_dev, 0x01, 0x0000); /* check PB0330 */
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0xdd, 0x008b);
- reg_w(gspca_dev, 0x0a, 0x0010);
- reg_w(gspca_dev, 0x03, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0012);
- retword = i2c_read(gspca_dev, 0x00);
- if (retword != 0) {
- PDEBUG(D_PROBE, "probe 3wr vga type 0a");
- return 0x0a; /* PB0330 */
- }
-
- /* probe gc0303 / gc0305 */
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0x98, 0x008b);
- reg_w(gspca_dev, 0x01, 0x0010);
- reg_w(gspca_dev, 0x03, 0x0012);
- msleep(2);
- reg_w(gspca_dev, 0x01, 0x0012);
- retword = i2c_read(gspca_dev, 0x00);
- if (retword != 0) {
- PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
- if (retword == 0x0011) /* gc0303 */
- return 0x0303;
- if (retword == 0x0029) /* gc0305 */
- send_unknown(gspca_dev, SENSOR_GC0305);
- return retword;
- }
-
- reg_w(gspca_dev, 0x01, 0x0000); /* check OmniVision */
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0xa1, 0x008b);
- reg_w(gspca_dev, 0x08, 0x008d);
- reg_w(gspca_dev, 0x06, 0x0010);
- reg_w(gspca_dev, 0x01, 0x0012);
- reg_w(gspca_dev, 0x05, 0x0012);
- if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
- && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
- send_unknown(gspca_dev, SENSOR_OV7620);
- return 0x06; /* OmniVision confirm ? */
- }
-
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, 0x00, 0x0002);
- reg_w(gspca_dev, 0x01, 0x0010);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0xee, 0x008b);
- reg_w(gspca_dev, 0x03, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0012);
- reg_w(gspca_dev, 0x05, 0x0012);
- retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
- retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
- PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
- if (retword == 0x2030) {
-#ifdef GSPCA_DEBUG
- u8 retbyte;
-
- retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
- PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
-#endif
- send_unknown(gspca_dev, SENSOR_PO2030);
- return retword;
- }
-
- reg_w(gspca_dev, 0x01, 0x0000);
- reg_w(gspca_dev, 0x0a, 0x0010);
- reg_w(gspca_dev, 0xd3, 0x008b);
- reg_w(gspca_dev, 0x01, 0x0001);
- reg_w(gspca_dev, 0x03, 0x0012);
- reg_w(gspca_dev, 0x01, 0x0012);
- reg_w(gspca_dev, 0x05, 0x0012);
- reg_w(gspca_dev, 0xd3, 0x008b);
- retword = i2c_read(gspca_dev, 0x01);
- if (retword != 0) {
- PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
- return 0x16; /* adcm2700 (6100/6200) */
- }
- return -1;
-}
-
-static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int sensor;
-
- switch (sd->sensor) {
- case SENSOR_MC501CB:
- return -1; /* don't probe */
- case SENSOR_GC0303:
- /* may probe but with no write in reg 0x0010 */
- return -1; /* don't probe */
- case SENSOR_PAS106:
- sensor = sif_probe(gspca_dev);
- if (sensor >= 0)
- return sensor;
- break;
- }
- sensor = vga_2wr_probe(gspca_dev);
- if (sensor >= 0)
- return sensor;
- return vga_3wr_probe(gspca_dev);
-}
-
-/* this function is called at probe time */
-static int sd_config(struct gspca_dev *gspca_dev,
- const struct usb_device_id *id)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (id->idProduct == 0x301b)
- sd->bridge = BRIDGE_ZC301;
- else
- sd->bridge = BRIDGE_ZC303;
-
- /* define some sensors from the vendor/product */
- sd->sensor = id->driver_info;
-
- sd->reg08 = REG08_DEF;
-
- INIT_WORK(&sd->work, transfer_update);
-
- return 0;
-}
-
-static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- gspca_dev->usb_err = 0;
- if (ctrl->val && sd->exposure && gspca_dev->streaming)
- sd->exposure->val = getexposure(gspca_dev);
- return gspca_dev->usb_err;
- }
- return -EINVAL;
-}
-
-static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct gspca_dev *gspca_dev =
- container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
- struct sd *sd = (struct sd *)gspca_dev;
- int i, qual;
-
- gspca_dev->usb_err = 0;
-
- if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) {
- qual = sd->reg08 >> 1;
-
- for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
- if (ctrl->val <= jpeg_qual[i])
- break;
- }
- if (i > 0 && i == qual && ctrl->val < jpeg_qual[i])
- i--;
-
- /* With high quality settings we need max bandwidth */
- if (i >= 2 && gspca_dev->streaming &&
- !gspca_dev->cam.needs_full_bandwidth)
- return -EBUSY;
-
- sd->reg08 = (i << 1) | 1;
- ctrl->val = jpeg_qual[i];
- }
-
- if (!gspca_dev->streaming)
- return 0;
-
- switch (ctrl->id) {
- /* gamma/brightness/contrast cluster */
- case V4L2_CID_GAMMA:
- setcontrast(gspca_dev, sd->gamma->val,
- sd->brightness->val, sd->contrast->val);
- break;
- /* autogain/exposure cluster */
- case V4L2_CID_AUTOGAIN:
- setautogain(gspca_dev, ctrl->val);
- if (!gspca_dev->usb_err && !ctrl->val && sd->exposure)
- setexposure(gspca_dev, sd->exposure->val);
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- setlightfreq(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_SHARPNESS:
- setsharpness(gspca_dev, ctrl->val);
- break;
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- setquality(gspca_dev);
- break;
- }
- return gspca_dev->usb_err;
-}
-
-static const struct v4l2_ctrl_ops zcxx_ctrl_ops = {
- .g_volatile_ctrl = zcxx_g_volatile_ctrl,
- .s_ctrl = zcxx_s_ctrl,
-};
-
-static int sd_init_controls(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *)gspca_dev;
- struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
- static const u8 gamma[SENSOR_MAX] = {
- [SENSOR_ADCM2700] = 4,
- [SENSOR_CS2102] = 4,
- [SENSOR_CS2102K] = 5,
- [SENSOR_GC0303] = 3,
- [SENSOR_GC0305] = 4,
- [SENSOR_HDCS2020] = 4,
- [SENSOR_HV7131B] = 4,
- [SENSOR_HV7131R] = 4,
- [SENSOR_ICM105A] = 4,
- [SENSOR_MC501CB] = 4,
- [SENSOR_MT9V111_1] = 4,
- [SENSOR_MT9V111_3] = 4,
- [SENSOR_OV7620] = 3,
- [SENSOR_OV7630C] = 4,
- [SENSOR_PAS106] = 4,
- [SENSOR_PAS202B] = 4,
- [SENSOR_PB0330] = 4,
- [SENSOR_PO2030] = 4,
- [SENSOR_TAS5130C] = 3,
- };
-
- gspca_dev->vdev.ctrl_handler = hdl;
- v4l2_ctrl_handler_init(hdl, 8);
- sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 255, 1, 128);
- sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]);
- if (sd->sensor == SENSOR_HV7131R)
- sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
- sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- if (sd->sensor != SENSOR_OV7630C)
- sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops,
- V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
- V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
- sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 3, 1,
- sd->sensor == SENSOR_PO2030 ? 0 : 2);
- sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1,
- jpeg_qual[REG08_DEF >> 1]);
- if (hdl->error) {
- pr_err("Could not initialize controls\n");
- return hdl->error;
- }
- v4l2_ctrl_cluster(3, &sd->gamma);
- if (sd->sensor == SENSOR_HV7131R)
- v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
- 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;
- struct cam *cam;
- int sensor;
- static const u8 mode_tb[SENSOR_MAX] = {
- [SENSOR_ADCM2700] = 2,
- [SENSOR_CS2102] = 1,
- [SENSOR_CS2102K] = 1,
- [SENSOR_GC0303] = 1,
- [SENSOR_GC0305] = 1,
- [SENSOR_HDCS2020] = 1,
- [SENSOR_HV7131B] = 1,
- [SENSOR_HV7131R] = 1,
- [SENSOR_ICM105A] = 1,
- [SENSOR_MC501CB] = 2,
- [SENSOR_MT9V111_1] = 1,
- [SENSOR_MT9V111_3] = 1,
- [SENSOR_OV7620] = 2,
- [SENSOR_OV7630C] = 1,
- [SENSOR_PAS106] = 0,
- [SENSOR_PAS202B] = 1,
- [SENSOR_PB0330] = 1,
- [SENSOR_PO2030] = 1,
- [SENSOR_TAS5130C] = 1,
- };
-
- sensor = zcxx_probeSensor(gspca_dev);
- if (sensor >= 0)
- PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
- if ((unsigned) force_sensor < SENSOR_MAX) {
- sd->sensor = force_sensor;
- PDEBUG(D_PROBE, "sensor forced to %d", force_sensor);
- } else {
- switch (sensor) {
- case -1:
- switch (sd->sensor) {
- case SENSOR_MC501CB:
- PDEBUG(D_PROBE, "Sensor MC501CB");
- break;
- case SENSOR_GC0303:
- PDEBUG(D_PROBE, "Sensor GC0303");
- break;
- default:
- pr_warn("Unknown sensor - set to TAS5130C\n");
- sd->sensor = SENSOR_TAS5130C;
- }
- break;
- case 0:
- /* check the sensor type */
- sensor = i2c_read(gspca_dev, 0x00);
- PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
- switch (sensor) {
- case 0: /* hv7131b */
- case 1: /* hv7131e */
- PDEBUG(D_PROBE, "Find Sensor HV7131B");
- sd->sensor = SENSOR_HV7131B;
- break;
- default:
-/* case 2: * hv7131r */
- PDEBUG(D_PROBE, "Find Sensor HV7131R");
- sd->sensor = SENSOR_HV7131R;
- break;
- }
- break;
- case 0x02:
- PDEBUG(D_PROBE, "Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130C;
- break;
- case 0x04:
- PDEBUG(D_PROBE, "Find Sensor CS2102");
- sd->sensor = SENSOR_CS2102;
- break;
- case 0x08:
- PDEBUG(D_PROBE, "Find Sensor HDCS2020");
- sd->sensor = SENSOR_HDCS2020;
- break;
- case 0x0a:
- PDEBUG(D_PROBE,
- "Find Sensor PB0330. Chip revision %x",
- sd->chip_revision);
- sd->sensor = SENSOR_PB0330;
- break;
- case 0x0c:
- PDEBUG(D_PROBE, "Find Sensor ICM105A");
- sd->sensor = SENSOR_ICM105A;
- break;
- case 0x0e:
- PDEBUG(D_PROBE, "Find Sensor PAS202B");
- sd->sensor = SENSOR_PAS202B;
- break;
- case 0x0f:
- PDEBUG(D_PROBE, "Find Sensor PAS106");
- sd->sensor = SENSOR_PAS106;
- break;
- case 0x10:
- case 0x12:
- PDEBUG(D_PROBE, "Find Sensor TAS5130C");
- sd->sensor = SENSOR_TAS5130C;
- break;
- case 0x11:
- PDEBUG(D_PROBE, "Find Sensor HV7131R");
- sd->sensor = SENSOR_HV7131R;
- break;
- case 0x13:
- case 0x15:
- PDEBUG(D_PROBE,
- "Sensor MT9V111. Chip revision %04x",
- sd->chip_revision);
- sd->sensor = sd->bridge == BRIDGE_ZC301
- ? SENSOR_MT9V111_1
- : SENSOR_MT9V111_3;
- break;
- case 0x14:
- PDEBUG(D_PROBE,
- "Find Sensor CS2102K?. Chip revision %x",
- sd->chip_revision);
- sd->sensor = SENSOR_CS2102K;
- break;
- case 0x16:
- PDEBUG(D_PROBE, "Find Sensor ADCM2700");
- sd->sensor = SENSOR_ADCM2700;
- break;
- case 0x29:
- PDEBUG(D_PROBE, "Find Sensor GC0305");
- sd->sensor = SENSOR_GC0305;
- break;
- case 0x0303:
- PDEBUG(D_PROBE, "Sensor GC0303");
- sd->sensor = SENSOR_GC0303;
- break;
- case 0x2030:
- PDEBUG(D_PROBE, "Find Sensor PO2030");
- sd->sensor = SENSOR_PO2030;
- break;
- case 0x7620:
- PDEBUG(D_PROBE, "Find Sensor OV7620");
- sd->sensor = SENSOR_OV7620;
- break;
- case 0x7631:
- PDEBUG(D_PROBE, "Find Sensor OV7630C");
- sd->sensor = SENSOR_OV7630C;
- break;
- case 0x7648:
- PDEBUG(D_PROBE, "Find Sensor OV7648");
- sd->sensor = SENSOR_OV7620; /* same sensor (?) */
- break;
- default:
- pr_err("Unknown sensor %04x\n", sensor);
- return -EINVAL;
- }
- }
- if (sensor < 0x20) {
- if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
- reg_w(gspca_dev, 0x02, 0x0010);
- reg_r(gspca_dev, 0x0010);
- }
-
- cam = &gspca_dev->cam;
- switch (mode_tb[sd->sensor]) {
- case 0:
- cam->cam_mode = sif_mode;
- cam->nmodes = ARRAY_SIZE(sif_mode);
- break;
- case 1:
- cam->cam_mode = vga_mode;
- cam->nmodes = ARRAY_SIZE(vga_mode);
- break;
- default:
-/* case 2: */
- cam->cam_mode = broken_vga_mode;
- cam->nmodes = ARRAY_SIZE(broken_vga_mode);
- break;
- }
-
- /* switch off the led */
- reg_w(gspca_dev, 0x01, 0x0000);
- return gspca_dev->usb_err;
-}
-
-static int sd_pre_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0;
- return 0;
-}
-
-static int sd_start(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int mode;
- static const struct usb_action *init_tb[SENSOR_MAX][2] = {
- [SENSOR_ADCM2700] =
- {adcm2700_Initial, adcm2700_InitialScale},
- [SENSOR_CS2102] =
- {cs2102_Initial, cs2102_InitialScale},
- [SENSOR_CS2102K] =
- {cs2102K_Initial, cs2102K_InitialScale},
- [SENSOR_GC0303] =
- {gc0303_Initial, gc0303_InitialScale},
- [SENSOR_GC0305] =
- {gc0305_Initial, gc0305_InitialScale},
- [SENSOR_HDCS2020] =
- {hdcs2020_Initial, hdcs2020_InitialScale},
- [SENSOR_HV7131B] =
- {hv7131b_Initial, hv7131b_InitialScale},
- [SENSOR_HV7131R] =
- {hv7131r_Initial, hv7131r_InitialScale},
- [SENSOR_ICM105A] =
- {icm105a_Initial, icm105a_InitialScale},
- [SENSOR_MC501CB] =
- {mc501cb_Initial, mc501cb_InitialScale},
- [SENSOR_MT9V111_1] =
- {mt9v111_1_Initial, mt9v111_1_InitialScale},
- [SENSOR_MT9V111_3] =
- {mt9v111_3_Initial, mt9v111_3_InitialScale},
- [SENSOR_OV7620] =
- {ov7620_Initial, ov7620_InitialScale},
- [SENSOR_OV7630C] =
- {ov7630c_Initial, ov7630c_InitialScale},
- [SENSOR_PAS106] =
- {pas106b_Initial, pas106b_InitialScale},
- [SENSOR_PAS202B] =
- {pas202b_Initial, pas202b_InitialScale},
- [SENSOR_PB0330] =
- {pb0330_Initial, pb0330_InitialScale},
- [SENSOR_PO2030] =
- {po2030_Initial, po2030_InitialScale},
- [SENSOR_TAS5130C] =
- {tas5130c_Initial, tas5130c_InitialScale},
- };
-
- /* create the JPEG header */
- jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
- 0x21); /* JPEG 422 */
-
- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- zcxx_probeSensor(gspca_dev);
- break;
- case SENSOR_PAS106:
- usb_exchange(gspca_dev, pas106b_Initial_com);
- break;
- }
- usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
-
- switch (sd->sensor) {
- case SENSOR_ADCM2700:
- case SENSOR_GC0305:
- case SENSOR_OV7620:
- case SENSOR_PO2030:
- case SENSOR_TAS5130C:
- case SENSOR_GC0303:
-/* msleep(100); * ?? */
- reg_r(gspca_dev, 0x0002); /* --> 0x40 */
- reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(gspca_dev, 0x15, 0x01ae);
- if (sd->sensor == SENSOR_TAS5130C)
- break;
- reg_w(gspca_dev, 0x0d, 0x003a);
- reg_w(gspca_dev, 0x02, 0x003b);
- reg_w(gspca_dev, 0x00, 0x0038);
- break;
- case SENSOR_HV7131R:
- case SENSOR_PAS202B:
- reg_w(gspca_dev, 0x03, 0x003b);
- reg_w(gspca_dev, 0x0c, 0x003a);
- reg_w(gspca_dev, 0x0b, 0x0039);
- if (sd->sensor == SENSOR_HV7131R)
- reg_w(gspca_dev, 0x50, ZC3XX_R11D_GLOBALGAIN);
- break;
- }
-
- setmatrix(gspca_dev);
- switch (sd->sensor) {
- case SENSOR_ADCM2700:
- case SENSOR_OV7620:
- reg_r(gspca_dev, 0x0008);
- reg_w(gspca_dev, 0x00, 0x0008);
- break;
- case SENSOR_PAS202B:
- case SENSOR_GC0305:
- case SENSOR_HV7131R:
- case SENSOR_TAS5130C:
- reg_r(gspca_dev, 0x0008);
- /* fall thru */
- case SENSOR_PO2030:
- reg_w(gspca_dev, 0x03, 0x0008);
- break;
- }
- setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
-
- /* set the gamma tables when not set */
- switch (sd->sensor) {
- case SENSOR_CS2102K: /* gamma set in xxx_Initial */
- case SENSOR_HDCS2020:
- case SENSOR_OV7630C:
- break;
- default:
- setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma),
- v4l2_ctrl_g_ctrl(sd->brightness),
- v4l2_ctrl_g_ctrl(sd->contrast));
- break;
- }
- setmatrix(gspca_dev); /* one more time? */
- switch (sd->sensor) {
- case SENSOR_OV7620:
- case SENSOR_PAS202B:
- reg_r(gspca_dev, 0x0180); /* from win */
- reg_w(gspca_dev, 0x00, 0x0180);
- break;
- }
- setquality(gspca_dev);
- /* Start with BRC disabled, transfer_update will enable it if needed */
- reg_w(gspca_dev, 0x00, 0x0007);
- if (sd->plfreq)
- setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
-
- switch (sd->sensor) {
- case SENSOR_ADCM2700:
- reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(gspca_dev, 0x15, 0x01ae);
- reg_w(gspca_dev, 0x02, 0x0180);
- /* ms-win + */
- reg_w(gspca_dev, 0x40, 0x0117);
- break;
- case SENSOR_HV7131R:
- setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
- reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
- break;
- case SENSOR_GC0305:
- case SENSOR_TAS5130C:
- reg_w(gspca_dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(gspca_dev, 0x15, 0x01ae);
- /* fall thru */
- case SENSOR_PAS202B:
- case SENSOR_PO2030:
-/* reg_w(gspca_dev, 0x40, ZC3XX_R117_GGAIN); in win traces */
- reg_r(gspca_dev, 0x0180);
- break;
- case SENSOR_OV7620:
- reg_w(gspca_dev, 0x09, 0x01ad);
- reg_w(gspca_dev, 0x15, 0x01ae);
- i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
- i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
- /*fixme: returned value to send? */
- reg_w(gspca_dev, 0x40, 0x0117);
- reg_r(gspca_dev, 0x0180);
- break;
- }
-
- setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
-
- if (gspca_dev->usb_err < 0)
- return gspca_dev->usb_err;
-
- /* Start the transfer parameters update thread */
- sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME);
- queue_work(sd->work_thread, &sd->work);
-
- return 0;
-}
-
-/* called on streamoff with alt 0 and on disconnect */
-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;
- }
- if (!gspca_dev->dev)
- return;
- send_unknown(gspca_dev, sd->sensor);
-}
-
-static void sd_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data,
- int len)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* check the JPEG end of frame */
- if (len >= 3
- && data[len - 3] == 0xff && data[len - 2] == 0xd9) {
-/*fixme: what does the last byte mean?*/
- gspca_frame_add(gspca_dev, LAST_PACKET,
- data, len - 1);
- return;
- }
-
- /* check the JPEG start of a frame */
- if (data[0] == 0xff && data[1] == 0xd8) {
- /* put the JPEG header in the new frame */
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- sd->jpeg_hdr, JPEG_HDR_SZ);
-
- /* remove the webcam's header:
- * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
- * - 'ss ss' is the frame sequence number (BE)
- * - 'ww ww' and 'hh hh' are the window dimensions (BE)
- * - 'pp pp' is the packet sequence number (BE)
- */
- data += 18;
- len -= 18;
- }
- gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
-}
-
-static int sd_set_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int ret;
-
- ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
- if (ret)
- return ret;
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- return 0;
-}
-
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
-}
-
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
-static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
- u8 *data, /* interrupt packet data */
- int len) /* interrput packet length */
-{
- if (len == 8 && data[4] == 1) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
- input_sync(gspca_dev->input_dev);
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- }
-
- return 0;
-}
-#endif
-
-static const struct sd_desc sd_desc = {
- .name = KBUILD_MODNAME,
- .config = sd_config,
- .init = sd_init,
- .init_controls = sd_init_controls,
- .isoc_init = sd_pre_start,
- .start = sd_start,
- .stop0 = sd_stop0,
- .pkt_scan = sd_pkt_scan,
- .get_jcomp = sd_get_jcomp,
- .set_jcomp = sd_set_jcomp,
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
- .int_pkt_scan = sd_int_pkt_scan,
-#endif
-};
-
-static const struct usb_device_id device_table[] = {
- {USB_DEVICE(0x03f0, 0x1b07)},
- {USB_DEVICE(0x041e, 0x041e)},
- {USB_DEVICE(0x041e, 0x4017)},
- {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x041e, 0x401e)},
- {USB_DEVICE(0x041e, 0x401f)},
- {USB_DEVICE(0x041e, 0x4022)},
- {USB_DEVICE(0x041e, 0x4029)},
- {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x041e, 0x4036)},
- {USB_DEVICE(0x041e, 0x403a)},
- {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_GC0303},
- {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_GC0303},
- {USB_DEVICE(0x0458, 0x7007)},
- {USB_DEVICE(0x0458, 0x700c)},
- {USB_DEVICE(0x0458, 0x700f)},
- {USB_DEVICE(0x0461, 0x0a00)},
- {USB_DEVICE(0x046d, 0x089d), .driver_info = SENSOR_MC501CB},
- {USB_DEVICE(0x046d, 0x08a0)},
- {USB_DEVICE(0x046d, 0x08a1)},
- {USB_DEVICE(0x046d, 0x08a2)},
- {USB_DEVICE(0x046d, 0x08a3)},
- {USB_DEVICE(0x046d, 0x08a6)},
- {USB_DEVICE(0x046d, 0x08a7)},
- {USB_DEVICE(0x046d, 0x08a9)},
- {USB_DEVICE(0x046d, 0x08aa)},
- {USB_DEVICE(0x046d, 0x08ac)},
- {USB_DEVICE(0x046d, 0x08ad)},
- {USB_DEVICE(0x046d, 0x08ae)},
- {USB_DEVICE(0x046d, 0x08af)},
- {USB_DEVICE(0x046d, 0x08b9)},
- {USB_DEVICE(0x046d, 0x08d7)},
- {USB_DEVICE(0x046d, 0x08d8)},
- {USB_DEVICE(0x046d, 0x08d9)},
- {USB_DEVICE(0x046d, 0x08da)},
- {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB},
- {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x055f, 0xc005)},
- {USB_DEVICE(0x055f, 0xd003)},
- {USB_DEVICE(0x055f, 0xd004)},
- {USB_DEVICE(0x0698, 0x2003)},
- {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
- {USB_DEVICE(0x0ac8, 0x301b)},
- {USB_DEVICE(0x0ac8, 0x303b)},
- {USB_DEVICE(0x0ac8, 0x305b)},
- {USB_DEVICE(0x0ac8, 0x307b)},
- {USB_DEVICE(0x10fd, 0x0128)},
- {USB_DEVICE(0x10fd, 0x804d)},
- {USB_DEVICE(0x10fd, 0x8050)},
- {} /* end of entry */
-};
-MODULE_DEVICE_TABLE(usb, device_table);
-
-/* -- 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);
-}
-
-/* USB driver */
-static struct usb_driver sd_driver = {
- .name = KBUILD_MODNAME,
- .id_table = device_table,
- .probe = sd_probe,
- .disconnect = gspca_disconnect,
-#ifdef CONFIG_PM
- .suspend = gspca_suspend,
- .resume = gspca_resume,
- .reset_resume = gspca_resume,
-#endif
-};
-
-module_usb_driver(sd_driver);
-
-module_param(force_sensor, int, 0644);
-MODULE_PARM_DESC(force_sensor,
- "Force sensor. Only for experts!!!");
diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
deleted file mode 100644
index 52f057f24e39..000000000000
--- a/drivers/media/video/hdpvr/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
-
-obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
-
-ccflags-y += -Idrivers/media/video
-
-ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
deleted file mode 100644
index 0e9e156bb2aa..000000000000
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ /dev/null
@@ -1,1289 +0,0 @@
-/*
- * Hauppauge HD PVR USB driver - video 4 linux 2 interface
- *
- * Copyright (C) 2008 Janne Grunau (j@jannau.net)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/uaccess.h>
-#include <linux/usb.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include "hdpvr.h"
-
-#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
-
-#define print_buffer_status() { \
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
- "%s:%d buffer stat: %d free, %d proc\n", \
- __func__, __LINE__, \
- list_size(&dev->free_buff_list), \
- list_size(&dev->rec_buff_list)); }
-
-struct hdpvr_fh {
- struct hdpvr_device *dev;
-};
-
-static uint list_size(struct list_head *list)
-{
- struct list_head *tmp;
- uint count = 0;
-
- list_for_each(tmp, list) {
- count++;
- }
-
- return count;
-}
-
-/*=========================================================================*/
-/* urb callback */
-static void hdpvr_read_bulk_callback(struct urb *urb)
-{
- struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
- struct hdpvr_device *dev = buf->dev;
-
- /* marking buffer as received and wake waiting */
- buf->status = BUFSTAT_READY;
- wake_up_interruptible(&dev->wait_data);
-}
-
-/*=========================================================================*/
-/* bufffer bits */
-
-/* function expects dev->io_mutex to be hold by caller */
-int hdpvr_cancel_queue(struct hdpvr_device *dev)
-{
- struct hdpvr_buffer *buf;
-
- list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
- usb_kill_urb(buf->urb);
- buf->status = BUFSTAT_AVAILABLE;
- }
-
- list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
-
- return 0;
-}
-
-static int hdpvr_free_queue(struct list_head *q)
-{
- struct list_head *tmp;
- struct list_head *p;
- struct hdpvr_buffer *buf;
- struct urb *urb;
-
- for (p = q->next; p != q;) {
- buf = list_entry(p, struct hdpvr_buffer, buff_list);
-
- urb = buf->urb;
- usb_free_coherent(urb->dev, urb->transfer_buffer_length,
- urb->transfer_buffer, urb->transfer_dma);
- usb_free_urb(urb);
- tmp = p->next;
- list_del(p);
- kfree(buf);
- p = tmp;
- }
-
- return 0;
-}
-
-/* function expects dev->io_mutex to be hold by caller */
-int hdpvr_free_buffers(struct hdpvr_device *dev)
-{
- hdpvr_cancel_queue(dev);
-
- hdpvr_free_queue(&dev->free_buff_list);
- hdpvr_free_queue(&dev->rec_buff_list);
-
- return 0;
-}
-
-/* function expects dev->io_mutex to be hold by caller */
-int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
-{
- uint i;
- int retval = -ENOMEM;
- u8 *mem;
- struct hdpvr_buffer *buf;
- struct urb *urb;
-
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "allocating %u buffers\n", count);
-
- for (i = 0; i < count; i++) {
-
- buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
- if (!buf) {
- v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
- goto exit;
- }
- buf->dev = dev;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
- goto exit_urb;
- }
- buf->urb = urb;
-
- mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
- &urb->transfer_dma);
- if (!mem) {
- v4l2_err(&dev->v4l2_dev,
- "cannot allocate usb transfer buffer\n");
- goto exit_urb_buffer;
- }
-
- usb_fill_bulk_urb(buf->urb, dev->udev,
- usb_rcvbulkpipe(dev->udev,
- dev->bulk_in_endpointAddr),
- mem, dev->bulk_in_size,
- hdpvr_read_bulk_callback, buf);
-
- buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- buf->status = BUFSTAT_AVAILABLE;
- list_add_tail(&buf->buff_list, &dev->free_buff_list);
- }
- return 0;
-exit_urb_buffer:
- usb_free_urb(urb);
-exit_urb:
- kfree(buf);
-exit:
- hdpvr_free_buffers(dev);
- return retval;
-}
-
-static int hdpvr_submit_buffers(struct hdpvr_device *dev)
-{
- struct hdpvr_buffer *buf;
- struct urb *urb;
- int ret = 0, err_count = 0;
-
- mutex_lock(&dev->io_mutex);
-
- while (dev->status == STATUS_STREAMING &&
- !list_empty(&dev->free_buff_list)) {
-
- buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
- buff_list);
- if (buf->status != BUFSTAT_AVAILABLE) {
- v4l2_err(&dev->v4l2_dev,
- "buffer not marked as available\n");
- ret = -EFAULT;
- goto err;
- }
-
- urb = buf->urb;
- urb->status = 0;
- urb->actual_length = 0;
- ret = usb_submit_urb(urb, GFP_KERNEL);
- if (ret) {
- v4l2_err(&dev->v4l2_dev,
- "usb_submit_urb in %s returned %d\n",
- __func__, ret);
- if (++err_count > 2)
- break;
- continue;
- }
- buf->status = BUFSTAT_INPROGRESS;
- list_move_tail(&buf->buff_list, &dev->rec_buff_list);
- }
-err:
- print_buffer_status();
- mutex_unlock(&dev->io_mutex);
- return ret;
-}
-
-static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
-{
- struct hdpvr_buffer *buf;
-
- mutex_lock(&dev->io_mutex);
-
- if (list_empty(&dev->rec_buff_list)) {
- mutex_unlock(&dev->io_mutex);
- return NULL;
- }
-
- buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
- buff_list);
- mutex_unlock(&dev->io_mutex);
-
- return buf;
-}
-
-static void hdpvr_transmit_buffers(struct work_struct *work)
-{
- struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
- worker);
-
- while (dev->status == STATUS_STREAMING) {
-
- if (hdpvr_submit_buffers(dev)) {
- v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
- goto error;
- }
- if (wait_event_interruptible(dev->wait_buffer,
- !list_empty(&dev->free_buff_list) ||
- dev->status != STATUS_STREAMING))
- goto error;
- }
-
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "transmit worker exited\n");
- return;
-error:
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "transmit buffers errored\n");
- dev->status = STATUS_ERROR;
-}
-
-/* function expects dev->io_mutex to be hold by caller */
-static int hdpvr_start_streaming(struct hdpvr_device *dev)
-{
- int ret;
- struct hdpvr_video_info *vidinf;
-
- if (dev->status == STATUS_STREAMING)
- return 0;
- else if (dev->status != STATUS_IDLE)
- return -EAGAIN;
-
- vidinf = get_video_info(dev);
-
- if (vidinf) {
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "video signal: %dx%d@%dhz\n", vidinf->width,
- vidinf->height, vidinf->fps);
- kfree(vidinf);
-
- /* start streaming 2 request */
- ret = usb_control_msg(dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "encoder start control request returned %d\n", ret);
-
- hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
-
- dev->status = STATUS_STREAMING;
-
- INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
- queue_work(dev->workqueue, &dev->worker);
-
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "streaming started\n");
-
- return 0;
- }
- msleep(250);
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "no video signal at input %d\n", dev->options.video_input);
- return -EAGAIN;
-}
-
-
-/* function expects dev->io_mutex to be hold by caller */
-static int hdpvr_stop_streaming(struct hdpvr_device *dev)
-{
- int actual_length;
- uint c = 0;
- u8 *buf;
-
- if (dev->status == STATUS_IDLE)
- return 0;
- else if (dev->status != STATUS_STREAMING)
- return -EAGAIN;
-
- buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
- if (!buf)
- v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
- "for emptying the internal device buffer. "
- "Next capture start will be slow\n");
-
- dev->status = STATUS_SHUTTING_DOWN;
- hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
- mutex_unlock(&dev->io_mutex);
-
- wake_up_interruptible(&dev->wait_buffer);
- msleep(50);
-
- flush_workqueue(dev->workqueue);
-
- mutex_lock(&dev->io_mutex);
- /* kill the still outstanding urbs */
- hdpvr_cancel_queue(dev);
-
- /* emptying the device buffer beforeshutting it down */
- while (buf && ++c < 500 &&
- !usb_bulk_msg(dev->udev,
- usb_rcvbulkpipe(dev->udev,
- dev->bulk_in_endpointAddr),
- buf, dev->bulk_in_size, &actual_length,
- BULK_URB_TIMEOUT)) {
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "%2d: got %d bytes\n", c, actual_length);
- }
- kfree(buf);
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "used %d urbs to empty device buffers\n", c-1);
- msleep(10);
-
- dev->status = STATUS_IDLE;
-
- return 0;
-}
-
-
-/*=======================================================================*/
-/*
- * video 4 linux 2 file operations
- */
-
-static int hdpvr_open(struct file *file)
-{
- struct hdpvr_device *dev;
- struct hdpvr_fh *fh;
- int retval = -ENOMEM;
-
- dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
- if (!dev) {
- pr_err("open failing with with ENODEV\n");
- retval = -ENODEV;
- goto err;
- }
-
- fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
- if (!fh) {
- v4l2_err(&dev->v4l2_dev, "Out of memory\n");
- goto err;
- }
- /* lock the device to allow correctly handling errors
- * in resumption */
- mutex_lock(&dev->io_mutex);
- dev->open_count++;
- mutex_unlock(&dev->io_mutex);
-
- fh->dev = dev;
-
- /* save our object in the file's private structure */
- file->private_data = fh;
-
- retval = 0;
-err:
- return retval;
-}
-
-static int hdpvr_release(struct file *file)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
-
- if (!dev)
- return -ENODEV;
-
- mutex_lock(&dev->io_mutex);
- if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
- hdpvr_stop_streaming(dev);
-
- mutex_unlock(&dev->io_mutex);
-
- return 0;
-}
-
-/*
- * hdpvr_v4l2_read()
- * will allocate buffers when called for the first time
- */
-static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
- loff_t *pos)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- struct hdpvr_buffer *buf = NULL;
- struct urb *urb;
- unsigned int ret = 0;
- int rem, cnt;
-
- if (*pos)
- return -ESPIPE;
-
- if (!dev)
- return -ENODEV;
-
- mutex_lock(&dev->io_mutex);
- if (dev->status == STATUS_IDLE) {
- if (hdpvr_start_streaming(dev)) {
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "start_streaming failed\n");
- ret = -EIO;
- msleep(200);
- dev->status = STATUS_IDLE;
- mutex_unlock(&dev->io_mutex);
- goto err;
- }
- print_buffer_status();
- }
- mutex_unlock(&dev->io_mutex);
-
- /* wait for the first buffer */
- if (!(file->f_flags & O_NONBLOCK)) {
- if (wait_event_interruptible(dev->wait_data,
- hdpvr_get_next_buffer(dev)))
- return -ERESTARTSYS;
- }
-
- buf = hdpvr_get_next_buffer(dev);
-
- while (count > 0 && buf) {
-
- if (buf->status != BUFSTAT_READY &&
- dev->status != STATUS_DISCONNECTED) {
- /* return nonblocking */
- if (file->f_flags & O_NONBLOCK) {
- if (!ret)
- ret = -EAGAIN;
- goto err;
- }
-
- if (wait_event_interruptible(dev->wait_data,
- buf->status == BUFSTAT_READY)) {
- ret = -ERESTARTSYS;
- goto err;
- }
- }
-
- if (buf->status != BUFSTAT_READY)
- break;
-
- /* set remaining bytes to copy */
- urb = buf->urb;
- rem = urb->actual_length - buf->pos;
- cnt = rem > count ? count : rem;
-
- if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
- cnt)) {
- v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
- if (!ret)
- ret = -EFAULT;
- goto err;
- }
-
- buf->pos += cnt;
- count -= cnt;
- buffer += cnt;
- ret += cnt;
-
- /* finished, take next buffer */
- if (buf->pos == urb->actual_length) {
- mutex_lock(&dev->io_mutex);
- buf->pos = 0;
- buf->status = BUFSTAT_AVAILABLE;
-
- list_move_tail(&buf->buff_list, &dev->free_buff_list);
-
- print_buffer_status();
-
- mutex_unlock(&dev->io_mutex);
-
- wake_up_interruptible(&dev->wait_buffer);
-
- buf = hdpvr_get_next_buffer(dev);
- }
- }
-err:
- if (!ret && !buf)
- ret = -EAGAIN;
- return ret;
-}
-
-static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
-{
- struct hdpvr_buffer *buf = NULL;
- struct hdpvr_fh *fh = filp->private_data;
- struct hdpvr_device *dev = fh->dev;
- unsigned int mask = 0;
-
- mutex_lock(&dev->io_mutex);
-
- if (!video_is_registered(dev->video_dev)) {
- mutex_unlock(&dev->io_mutex);
- return -EIO;
- }
-
- if (dev->status == STATUS_IDLE) {
- if (hdpvr_start_streaming(dev)) {
- v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
- "start_streaming failed\n");
- dev->status = STATUS_IDLE;
- }
-
- print_buffer_status();
- }
- mutex_unlock(&dev->io_mutex);
-
- buf = hdpvr_get_next_buffer(dev);
- /* only wait if no data is available */
- if (!buf || buf->status != BUFSTAT_READY) {
- poll_wait(filp, &dev->wait_data, wait);
- buf = hdpvr_get_next_buffer(dev);
- }
- if (buf && buf->status == BUFSTAT_READY)
- mask |= POLLIN | POLLRDNORM;
-
- return mask;
-}
-
-
-static const struct v4l2_file_operations hdpvr_fops = {
- .owner = THIS_MODULE,
- .open = hdpvr_open,
- .release = hdpvr_release,
- .read = hdpvr_read,
- .poll = hdpvr_poll,
- .unlocked_ioctl = video_ioctl2,
-};
-
-/*=======================================================================*/
-/*
- * V4L2 ioctl handling
- */
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct hdpvr_device *dev = video_drvdata(file);
-
- strcpy(cap->driver, "hdpvr");
- strcpy(cap->card, "Hauppauge HD PVR");
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE;
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *private_data,
- v4l2_std_id *std)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- u8 std_type = 1;
-
- if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
- std_type = 0;
-
- return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
-}
-
-static const char *iname[] = {
- [HDPVR_COMPONENT] = "Component",
- [HDPVR_SVIDEO] = "S-Video",
- [HDPVR_COMPOSITE] = "Composite",
-};
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- unsigned int n;
-
- n = i->index;
- if (n >= HDPVR_VIDEO_INPUTS)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- strncpy(i->name, iname[n], sizeof(i->name) - 1);
- i->name[sizeof(i->name) - 1] = '\0';
-
- i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
-
- i->std = dev->video_dev->tvnorms;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *private_data,
- unsigned int index)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int retval;
-
- if (index >= HDPVR_VIDEO_INPUTS)
- return -EINVAL;
-
- if (dev->status != STATUS_IDLE)
- return -EAGAIN;
-
- retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
- if (!retval)
- dev->options.video_input = index;
-
- return retval;
-}
-
-static int vidioc_g_input(struct file *file, void *private_data,
- unsigned int *index)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
-
- *index = dev->options.video_input;
- return 0;
-}
-
-
-static const char *audio_iname[] = {
- [HDPVR_RCA_FRONT] = "RCA front",
- [HDPVR_RCA_BACK] = "RCA back",
- [HDPVR_SPDIF] = "SPDIF",
-};
-
-static int vidioc_enumaudio(struct file *file, void *priv,
- struct v4l2_audio *audio)
-{
- unsigned int n;
-
- n = audio->index;
- if (n >= HDPVR_AUDIO_INPUTS)
- return -EINVAL;
-
- audio->capability = V4L2_AUDCAP_STEREO;
-
- strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
- audio->name[sizeof(audio->name) - 1] = '\0';
-
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *private_data,
- struct v4l2_audio *audio)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int retval;
-
- if (audio->index >= HDPVR_AUDIO_INPUTS)
- return -EINVAL;
-
- if (dev->status != STATUS_IDLE)
- return -EAGAIN;
-
- retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
- if (!retval)
- dev->options.audio_input = audio->index;
-
- return retval;
-}
-
-static int vidioc_g_audio(struct file *file, void *private_data,
- struct v4l2_audio *audio)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
-
- audio->index = dev->options.audio_input;
- audio->capability = V4L2_AUDCAP_STEREO;
- strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
- audio->name[sizeof(audio->name) - 1] = '\0';
- return 0;
-}
-
-static const s32 supported_v4l2_ctrls[] = {
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- V4L2_CID_SHARPNESS,
- V4L2_CID_MPEG_AUDIO_ENCODING,
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
- V4L2_CID_MPEG_VIDEO_BITRATE,
- V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
-};
-
-static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
- int ac3, int fw_ver)
-{
- int err;
-
- if (fw_ver > 0x15) {
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x40);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0x0, 0x1e, 1, 0xf);
- case V4L2_CID_SHARPNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- }
- } else {
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- case V4L2_CID_SHARPNESS:
- return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
- }
- }
-
- switch (qc->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return v4l2_ctrl_query_fill(
- qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
- ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
- : V4L2_MPEG_AUDIO_ENCODING_AAC,
- 1, V4L2_MPEG_AUDIO_ENCODING_AAC);
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return v4l2_ctrl_query_fill(
- qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
-
-/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
-/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return v4l2_ctrl_query_fill(
- qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
-
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
- 6500000);
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
- 9000000);
- if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
- qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
- return err;
- default:
- return -EINVAL;
- }
-}
-
-static int vidioc_queryctrl(struct file *file, void *private_data,
- struct v4l2_queryctrl *qc)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int i, next;
- u32 id = qc->id;
-
- memset(qc, 0, sizeof(*qc));
-
- next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
- qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
-
- for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
- if (next) {
- if (qc->id < supported_v4l2_ctrls[i])
- qc->id = supported_v4l2_ctrls[i];
- else
- continue;
- }
-
- if (qc->id == supported_v4l2_ctrls[i])
- return fill_queryctrl(&dev->options, qc,
- dev->flags & HDPVR_FLAG_AC3_CAP,
- dev->fw_ver);
-
- if (qc->id < supported_v4l2_ctrls[i])
- break;
- }
-
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *private_data,
- struct v4l2_control *ctrl)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dev->options.brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dev->options.contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dev->options.saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dev->options.hue;
- break;
- case V4L2_CID_SHARPNESS:
- ctrl->value = dev->options.sharpness;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *private_data,
- struct v4l2_control *ctrl)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int retval;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
- if (!retval)
- dev->options.brightness = ctrl->value;
- break;
- case V4L2_CID_CONTRAST:
- retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
- if (!retval)
- dev->options.contrast = ctrl->value;
- break;
- case V4L2_CID_SATURATION:
- retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
- if (!retval)
- dev->options.saturation = ctrl->value;
- break;
- case V4L2_CID_HUE:
- retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
- if (!retval)
- dev->options.hue = ctrl->value;
- break;
- case V4L2_CID_SHARPNESS:
- retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
- if (!retval)
- dev->options.sharpness = ctrl->value;
- break;
- default:
- return -EINVAL;
- }
-
- return retval;
-}
-
-
-static int hdpvr_get_ctrl(struct hdpvr_options *opt,
- struct v4l2_ext_control *ctrl)
-{
- switch (ctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- ctrl->value = opt->audio_codec;
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
- break;
-/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
-/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
-/* break; */
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
- ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
- : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- ctrl->value = opt->bitrate * 100000;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- ctrl->value = opt->peak_bitrate * 100000;
- break;
- case V4L2_CID_MPEG_STREAM_TYPE:
- ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int i, err = 0;
-
- if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = ctrls->controls + i;
-
- err = hdpvr_get_ctrl(&dev->options, ctrl);
- if (err) {
- ctrls->error_idx = i;
- break;
- }
- }
- return err;
-
- }
-
- return -EINVAL;
-}
-
-
-static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
-{
- int ret = -EINVAL;
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
- (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
- ret = 0;
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
- ret = 0;
- break;
-/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
-/* if (ctrl->value == 0 || ctrl->value == 128) */
-/* ret = 0; */
-/* break; */
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
- ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
- ret = 0;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- {
- uint bitrate = ctrl->value / 100000;
- if (bitrate >= 10 && bitrate <= 135)
- ret = 0;
- break;
- }
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
- {
- uint peak_bitrate = ctrl->value / 100000;
- if (peak_bitrate >= 10 && peak_bitrate <= 202)
- ret = 0;
- break;
- }
- case V4L2_CID_MPEG_STREAM_TYPE:
- if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
- ret = 0;
- break;
- default:
- return -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_try_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int i, err = 0;
-
- if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = ctrls->controls + i;
-
- err = hdpvr_try_ctrl(ctrl,
- dev->flags & HDPVR_FLAG_AC3_CAP);
- if (err) {
- ctrls->error_idx = i;
- break;
- }
- }
- return err;
- }
-
- return -EINVAL;
-}
-
-
-static int hdpvr_set_ctrl(struct hdpvr_device *dev,
- struct v4l2_ext_control *ctrl)
-{
- struct hdpvr_options *opt = &dev->options;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- if (dev->flags & HDPVR_FLAG_AC3_CAP) {
- opt->audio_codec = ctrl->value;
- ret = hdpvr_set_audio(dev, opt->audio_input,
- opt->audio_codec);
- }
- break;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- break;
-/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
-/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
-/* opt->gop_mode |= 0x2; */
-/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
-/* opt->gop_mode); */
-/* } */
-/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
-/* opt->gop_mode &= ~0x2; */
-/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
-/* opt->gop_mode); */
-/* } */
-/* break; */
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
- opt->bitrate_mode != HDPVR_CONSTANT) {
- opt->bitrate_mode = HDPVR_CONSTANT;
- hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
- opt->bitrate_mode);
- }
- if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
- opt->bitrate_mode == HDPVR_CONSTANT) {
- opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
- hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
- opt->bitrate_mode);
- }
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE: {
- uint bitrate = ctrl->value / 100000;
-
- opt->bitrate = bitrate;
- if (bitrate >= opt->peak_bitrate)
- opt->peak_bitrate = bitrate+1;
-
- hdpvr_set_bitrate(dev);
- break;
- }
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
- uint peak_bitrate = ctrl->value / 100000;
-
- if (opt->bitrate_mode == HDPVR_CONSTANT)
- break;
-
- if (opt->bitrate < peak_bitrate) {
- opt->peak_bitrate = peak_bitrate;
- hdpvr_set_bitrate(dev);
- } else
- ret = -EINVAL;
- break;
- }
- case V4L2_CID_MPEG_STREAM_TYPE:
- break;
- default:
- return -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctrls)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- int i, err = 0;
-
- if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = ctrls->controls + i;
-
- err = hdpvr_try_ctrl(ctrl,
- dev->flags & HDPVR_FLAG_AC3_CAP);
- if (err) {
- ctrls->error_idx = i;
- break;
- }
- err = hdpvr_set_ctrl(dev, ctrl);
- if (err) {
- ctrls->error_idx = i;
- break;
- }
- }
- return err;
-
- }
-
- return -EINVAL;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
- struct v4l2_fmtdesc *f)
-{
-
- if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- f->flags = V4L2_FMT_FLAG_COMPRESSED;
- strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
- f->pixelformat = V4L2_PIX_FMT_MPEG;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
- struct v4l2_format *f)
-{
- struct hdpvr_fh *fh = file->private_data;
- struct hdpvr_device *dev = fh->dev;
- struct hdpvr_video_info *vid_info;
-
- if (!dev)
- return -ENODEV;
-
- vid_info = get_video_info(dev);
- if (!vid_info)
- return -EFAULT;
-
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
- f->fmt.pix.width = vid_info->width;
- f->fmt.pix.height = vid_info->height;
- f->fmt.pix.sizeimage = dev->bulk_in_size;
- f->fmt.pix.colorspace = 0;
- f->fmt.pix.bytesperline = 0;
- f->fmt.pix.field = V4L2_FIELD_ANY;
-
- kfree(vid_info);
- return 0;
-}
-
-static int vidioc_encoder_cmd(struct file *filp, void *priv,
- struct v4l2_encoder_cmd *a)
-{
- struct hdpvr_fh *fh = filp->private_data;
- struct hdpvr_device *dev = fh->dev;
- int res;
-
- mutex_lock(&dev->io_mutex);
-
- memset(&a->raw, 0, sizeof(a->raw));
- switch (a->cmd) {
- case V4L2_ENC_CMD_START:
- a->flags = 0;
- res = hdpvr_start_streaming(dev);
- break;
- case V4L2_ENC_CMD_STOP:
- res = hdpvr_stop_streaming(dev);
- break;
- default:
- v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
- "Unsupported encoder cmd %d\n", a->cmd);
- res = -EINVAL;
- }
- mutex_unlock(&dev->io_mutex);
- return res;
-}
-
-static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
- struct v4l2_encoder_cmd *a)
-{
- switch (a->cmd) {
- case V4L2_ENC_CMD_START:
- case V4L2_ENC_CMD_STOP:
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_enumaudio = vidioc_enumaudio,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
- .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
- .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_encoder_cmd = vidioc_encoder_cmd,
- .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
-};
-
-static void hdpvr_device_release(struct video_device *vdev)
-{
- struct hdpvr_device *dev = video_get_drvdata(vdev);
-
- hdpvr_delete(dev);
- mutex_lock(&dev->io_mutex);
- destroy_workqueue(dev->workqueue);
- mutex_unlock(&dev->io_mutex);
-
- v4l2_device_unregister(&dev->v4l2_dev);
-
- /* deregister I2C adapter */
-#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE)
- mutex_lock(&dev->i2c_mutex);
- i2c_del_adapter(&dev->i2c_adapter);
- mutex_unlock(&dev->i2c_mutex);
-#endif /* CONFIG_I2C */
-
- kfree(dev->usbc_buf);
- kfree(dev);
-}
-
-static const struct video_device hdpvr_video_template = {
-/* .type = VFL_TYPE_GRABBER, */
-/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
- .fops = &hdpvr_fops,
- .release = hdpvr_device_release,
- .ioctl_ops = &hdpvr_ioctl_ops,
- .tvnorms =
- V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B |
- V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
- V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
- V4L2_STD_PAL_60,
- .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
- V4L2_STD_PAL_60,
-};
-
-int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
- int devnum)
-{
- /* setup and register video device */
- dev->video_dev = video_device_alloc();
- if (!dev->video_dev) {
- v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
- goto error;
- }
-
- *(dev->video_dev) = hdpvr_video_template;
- strcpy(dev->video_dev->name, "Hauppauge HD PVR");
- dev->video_dev->parent = parent;
- video_set_drvdata(dev->video_dev, dev);
-
- if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
- v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
- goto error;
- }
-
- return 0;
-error:
- return -ENOMEM;
-}
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
deleted file mode 100644
index 351e9bafe8fe..000000000000
--- a/drivers/media/video/imx074.c
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Driver for IMX074 CMOS Image Sensor from Sony
- *
- * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * Partially inspired by the IMX074 driver from the Android / MSM tree
- *
- * 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/delay.h>
-#include <linux/i2c.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
-
-/* IMX074 registers */
-
-#define MODE_SELECT 0x0100
-#define IMAGE_ORIENTATION 0x0101
-#define GROUPED_PARAMETER_HOLD 0x0104
-
-/* Integration Time */
-#define COARSE_INTEGRATION_TIME_HI 0x0202
-#define COARSE_INTEGRATION_TIME_LO 0x0203
-/* Gain */
-#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
-#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
-
-/* PLL registers */
-#define PRE_PLL_CLK_DIV 0x0305
-#define PLL_MULTIPLIER 0x0307
-#define PLSTATIM 0x302b
-#define VNDMY_ABLMGSHLMT 0x300a
-#define Y_OPBADDR_START_DI 0x3014
-/* mode setting */
-#define FRAME_LENGTH_LINES_HI 0x0340
-#define FRAME_LENGTH_LINES_LO 0x0341
-#define LINE_LENGTH_PCK_HI 0x0342
-#define LINE_LENGTH_PCK_LO 0x0343
-#define YADDR_START 0x0347
-#define YADDR_END 0x034b
-#define X_OUTPUT_SIZE_MSB 0x034c
-#define X_OUTPUT_SIZE_LSB 0x034d
-#define Y_OUTPUT_SIZE_MSB 0x034e
-#define Y_OUTPUT_SIZE_LSB 0x034f
-#define X_EVEN_INC 0x0381
-#define X_ODD_INC 0x0383
-#define Y_EVEN_INC 0x0385
-#define Y_ODD_INC 0x0387
-
-#define HMODEADD 0x3001
-#define VMODEADD 0x3016
-#define VAPPLINE_START 0x3069
-#define VAPPLINE_END 0x306b
-#define SHUTTER 0x3086
-#define HADDAVE 0x30e8
-#define LANESEL 0x3301
-
-/* IMX074 supported geometry */
-#define IMX074_WIDTH 1052
-#define IMX074_HEIGHT 780
-
-/* IMX074 has only one fixed colorspace per pixelcode */
-struct imx074_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-struct imx074 {
- struct v4l2_subdev subdev;
- const struct imx074_datafmt *fmt;
-};
-
-static const struct imx074_datafmt imx074_colour_fmts[] = {
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
-};
-
-static struct imx074 *to_imx074(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct imx074, subdev);
-}
-
-/* Find a data format by a pixel code in an array */
-static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
- if (imx074_colour_fmts[i].code == code)
- return imx074_colour_fmts + i;
-
- return NULL;
-}
-
-static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
-{
- struct i2c_adapter *adap = client->adapter;
- struct i2c_msg msg;
- unsigned char tx[3];
- int ret;
-
- msg.addr = client->addr;
- msg.buf = tx;
- msg.len = 3;
- msg.flags = 0;
-
- tx[0] = addr >> 8;
- tx[1] = addr & 0xff;
- tx[2] = data;
-
- ret = i2c_transfer(adap, &msg, 1);
-
- mdelay(2);
-
- return ret == 1 ? 0 : -EIO;
-}
-
-static int reg_read(struct i2c_client *client, const u16 addr)
-{
- u8 buf[2] = {addr >> 8, addr & 0xff};
- int ret;
- struct i2c_msg msgs[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 2,
- .buf = buf,
- }, {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = 2,
- .buf = buf,
- },
- };
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret < 0) {
- dev_warn(&client->dev, "Reading register %x from %x failed\n",
- addr, client->addr);
- return ret;
- }
-
- return buf[0] & 0xff; /* no sign-extension */
-}
-
-static int imx074_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
-
- dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
-
- if (!fmt) {
- mf->code = imx074_colour_fmts[0].code;
- mf->colorspace = imx074_colour_fmts[0].colorspace;
- }
-
- mf->width = IMX074_WIDTH;
- mf->height = IMX074_HEIGHT;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int imx074_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct imx074 *priv = to_imx074(client);
-
- dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
-
- /* MIPI CSI could have changed the format, double-check */
- if (!imx074_find_datafmt(mf->code))
- return -EINVAL;
-
- imx074_try_fmt(sd, mf);
-
- priv->fmt = imx074_find_datafmt(mf->code);
-
- return 0;
-}
-
-static int imx074_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct imx074 *priv = to_imx074(client);
-
- const struct imx074_datafmt *fmt = priv->fmt;
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
- mf->width = IMX074_WIDTH;
- mf->height = IMX074_HEIGHT;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct v4l2_rect *rect = &a->c;
-
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rect->top = 0;
- rect->left = 0;
- rect->width = IMX074_WIDTH;
- rect->height = IMX074_HEIGHT;
-
- return 0;
-}
-
-static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = IMX074_WIDTH;
- a->bounds.height = IMX074_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
- return -EINVAL;
-
- *code = imx074_colour_fmts[index].code;
- return 0;
-}
-
-static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* MODE_SELECT: stream or standby */
- return reg_write(client, MODE_SELECT, !!enable);
-}
-
-static int imx074_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_IMX074;
- id->revision = 0;
-
- return 0;
-}
-
-static int imx074_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- cfg->type = V4L2_MBUS_CSI2;
- cfg->flags = V4L2_MBUS_CSI2_2_LANE |
- V4L2_MBUS_CSI2_CHANNEL_0 |
- V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
- .s_stream = imx074_s_stream,
- .s_mbus_fmt = imx074_s_fmt,
- .g_mbus_fmt = imx074_g_fmt,
- .try_mbus_fmt = imx074_try_fmt,
- .enum_mbus_fmt = imx074_enum_fmt,
- .g_crop = imx074_g_crop,
- .cropcap = imx074_cropcap,
- .g_mbus_config = imx074_g_mbus_config,
-};
-
-static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
- .g_chip_ident = imx074_g_chip_ident,
-};
-
-static struct v4l2_subdev_ops imx074_subdev_ops = {
- .core = &imx074_subdev_core_ops,
- .video = &imx074_subdev_video_ops,
-};
-
-static int imx074_video_probe(struct i2c_client *client)
-{
- int ret;
- u16 id;
-
- /* Read sensor Model ID */
- ret = reg_read(client, 0);
- if (ret < 0)
- return ret;
-
- id = ret << 8;
-
- ret = reg_read(client, 1);
- if (ret < 0)
- return ret;
-
- id |= ret;
-
- dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
-
- if (id != 0x74)
- return -ENODEV;
-
- /* PLL Setting EXTCLK=24MHz, 22.5times */
- reg_write(client, PLL_MULTIPLIER, 0x2D);
- reg_write(client, PRE_PLL_CLK_DIV, 0x02);
- reg_write(client, PLSTATIM, 0x4B);
-
- /* 2-lane mode */
- reg_write(client, 0x3024, 0x00);
-
- reg_write(client, IMAGE_ORIENTATION, 0x00);
-
- /* select RAW mode:
- * 0x08+0x08 = top 8 bits
- * 0x0a+0x08 = compressed 8-bits
- * 0x0a+0x0a = 10 bits
- */
- reg_write(client, 0x0112, 0x08);
- reg_write(client, 0x0113, 0x08);
-
- /* Base setting for High frame mode */
- reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
- reg_write(client, Y_OPBADDR_START_DI, 0x08);
- reg_write(client, 0x3015, 0x37);
- reg_write(client, 0x301C, 0x01);
- reg_write(client, 0x302C, 0x05);
- reg_write(client, 0x3031, 0x26);
- reg_write(client, 0x3041, 0x60);
- reg_write(client, 0x3051, 0x24);
- reg_write(client, 0x3053, 0x34);
- reg_write(client, 0x3057, 0xC0);
- reg_write(client, 0x305C, 0x09);
- reg_write(client, 0x305D, 0x07);
- reg_write(client, 0x3060, 0x30);
- reg_write(client, 0x3065, 0x00);
- reg_write(client, 0x30AA, 0x08);
- reg_write(client, 0x30AB, 0x1C);
- reg_write(client, 0x30B0, 0x32);
- reg_write(client, 0x30B2, 0x83);
- reg_write(client, 0x30D3, 0x04);
- reg_write(client, 0x3106, 0x78);
- reg_write(client, 0x310C, 0x82);
- reg_write(client, 0x3304, 0x05);
- reg_write(client, 0x3305, 0x04);
- reg_write(client, 0x3306, 0x11);
- reg_write(client, 0x3307, 0x02);
- reg_write(client, 0x3308, 0x0C);
- reg_write(client, 0x3309, 0x06);
- reg_write(client, 0x330A, 0x08);
- reg_write(client, 0x330B, 0x04);
- reg_write(client, 0x330C, 0x08);
- reg_write(client, 0x330D, 0x06);
- reg_write(client, 0x330E, 0x01);
- reg_write(client, 0x3381, 0x00);
-
- /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
- /* 1608 = 1560 + 48 (black lines) */
- reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
- reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
- reg_write(client, YADDR_START, 0x00);
- reg_write(client, YADDR_END, 0x2F);
- /* 0x838 == 2104 */
- reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
- reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
- /* 0x618 == 1560 */
- reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
- reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
- reg_write(client, X_EVEN_INC, 0x01);
- reg_write(client, X_ODD_INC, 0x03);
- reg_write(client, Y_EVEN_INC, 0x01);
- reg_write(client, Y_ODD_INC, 0x03);
- reg_write(client, HMODEADD, 0x00);
- reg_write(client, VMODEADD, 0x16);
- reg_write(client, VAPPLINE_START, 0x24);
- reg_write(client, VAPPLINE_END, 0x53);
- reg_write(client, SHUTTER, 0x00);
- reg_write(client, HADDAVE, 0x80);
-
- reg_write(client, LANESEL, 0x00);
-
- reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */
-
- return 0;
-}
-
-static int imx074_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct imx074 *priv;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "IMX074: missing platform data!\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
- return -EIO;
- }
-
- priv = kzalloc(sizeof(struct imx074), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
-
- priv->fmt = &imx074_colour_fmts[0];
-
- ret = imx074_video_probe(client);
- if (ret < 0) {
- kfree(priv);
- return ret;
- }
-
- return ret;
-}
-
-static int imx074_remove(struct i2c_client *client)
-{
- struct imx074 *priv = to_imx074(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- if (icl->free_bus)
- icl->free_bus(icl);
- kfree(priv);
-
- return 0;
-}
-
-static const struct i2c_device_id imx074_id[] = {
- { "imx074", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, imx074_id);
-
-static struct i2c_driver imx074_i2c_driver = {
- .driver = {
- .name = "imx074",
- },
- .probe = imx074_probe,
- .remove = imx074_remove,
- .id_table = imx074_id,
-};
-
-module_i2c_driver(imx074_i2c_driver);
-
-MODULE_DESCRIPTION("Sony IMX074 Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig
deleted file mode 100644
index 89f65914cc8e..000000000000
--- a/drivers/media/video/ivtv/Kconfig
+++ /dev/null
@@ -1,45 +0,0 @@
-config VIDEO_IVTV
- tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support"
- depends on VIDEO_V4L2 && PCI && I2C
- select I2C_ALGOBIT
- depends on RC_CORE
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_CX2341X
- select VIDEO_CX25840
- select VIDEO_MSP3400
- select VIDEO_SAA711X
- select VIDEO_SAA717X
- select VIDEO_SAA7127
- select VIDEO_CS53L32A
- select VIDEO_M52790
- select VIDEO_WM8775
- select VIDEO_WM8739
- select VIDEO_VP27SMPX
- select VIDEO_UPD64031A
- select VIDEO_UPD64083
- ---help---
- This is a video4linux driver for Conexant cx23416 or cx23415 based
- PCI personal video recorder devices.
-
- This is used in devices such as the Hauppauge PVR-150/250/350/500
- cards. There is a driver homepage at <http://www.ivtvdriver.org>.
-
- To compile this driver as a module, choose M here: the
- module will be called ivtv.
-
-config VIDEO_FB_IVTV
- tristate "Conexant cx23415 framebuffer support"
- depends on VIDEO_IVTV && FB
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- ---help---
- This is a framebuffer driver for the Conexant cx23415 MPEG
- encoder/decoder.
-
- This is used in the Hauppauge PVR-350 card. There is a driver
- homepage at <http://www.ivtvdriver.org>.
-
- To compile this driver as a module, choose M here: the
- module will be called ivtvfb.
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
deleted file mode 100644
index 77de8a45b46f..000000000000
--- a/drivers/media/video/ivtv/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
- ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \
- ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \
- ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \
- ivtv-vbi.o ivtv-yuv.o
-
-obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
-obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
-
-ccflags-y += -I$(srctree)/drivers/media/video
-ccflags-y += -I$(srctree)/drivers/media/common/tuners
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
-
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
deleted file mode 100644
index 5462ce2f60ea..000000000000
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ /dev/null
@@ -1,1498 +0,0 @@
-/*
- ivtv driver initialization and card probing
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* Main Driver file for the ivtv project:
- * Driver for the Conexant CX23415/CX23416 chip.
- * Author: Kevin Thayer (nufan_wfk at yahoo.com)
- * License: GPL
- * http://www.ivtvdriver.org
- *
- * -----
- * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
- * and Takeru KOMORIYA<komoriya@paken.org>
- *
- * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
- * using information provided by Jiun-Kuei Jung @ AVerMedia.
- *
- * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp>
- * using information from T.Adachi,Takeru KOMORIYA and others :-)
- *
- * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX
- * version by T.Adachi. Special thanks Mr.Suzuki
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-version.h"
-#include "ivtv-fileops.h"
-#include "ivtv-i2c.h"
-#include "ivtv-firmware.h"
-#include "ivtv-queue.h"
-#include "ivtv-udma.h"
-#include "ivtv-irq.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-streams.h"
-#include "ivtv-ioctl.h"
-#include "ivtv-cards.h"
-#include "ivtv-vbi.h"
-#include "ivtv-routing.h"
-#include "ivtv-controls.h"
-#include "ivtv-gpio.h"
-#include <linux/dma-mapping.h>
-#include <media/tveeprom.h>
-#include <media/saa7115.h>
-#include <media/v4l2-chip-ident.h>
-#include "tuner-xc2028.h"
-
-/* If you have already X v4l cards, then set this to X. This way
- the device numbers stay matched. Example: you have a WinTV card
- without radio and a PVR-350 with. Normally this would give a
- video1 device together with a radio0 device for the PVR. By
- setting this to 1 you ensure that radio0 is now also radio1. */
-int ivtv_first_minor;
-
-/* add your revision and whatnot here */
-static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
- {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
-};
-
-MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
-
-/* ivtv instance counter */
-static atomic_t ivtv_instance = ATOMIC_INIT(0);
-
-/* Parameter declarations */
-static int cardtype[IVTV_MAX_CARDS];
-static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
-static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
-static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1 };
-
-static unsigned int cardtype_c = 1;
-static unsigned int tuner_c = 1;
-static int radio_c = 1;
-static unsigned int i2c_clock_period_c = 1;
-static char pal[] = "---";
-static char secam[] = "--";
-static char ntsc[] = "-";
-
-/* Buffers */
-
-/* DMA Buffers, Default size in MB allocated */
-#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4
-#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2
-#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1
-/* Exception: size in kB for this stream (MB is overkill) */
-#define IVTV_DEFAULT_ENC_PCM_BUFFERS 320
-#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1
-#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1
-/* Exception: size in kB for this stream (MB is way overkill) */
-#define IVTV_DEFAULT_DEC_VBI_BUFFERS 64
-
-static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS;
-static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS;
-static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS;
-static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS;
-static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS;
-static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS;
-static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS;
-
-static int ivtv_yuv_mode;
-static int ivtv_yuv_threshold = -1;
-static int ivtv_pci_latency = 1;
-
-int ivtv_debug;
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-int ivtv_fw_debug;
-#endif
-
-static int tunertype = -1;
-static int newi2c = -1;
-
-module_param_array(tuner, int, &tuner_c, 0644);
-module_param_array(radio, int, &radio_c, 0644);
-module_param_array(cardtype, int, &cardtype_c, 0644);
-module_param_string(pal, pal, sizeof(pal), 0644);
-module_param_string(secam, secam, sizeof(secam), 0644);
-module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
-module_param_named(debug,ivtv_debug, int, 0644);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-module_param_named(fw_debug, ivtv_fw_debug, int, 0644);
-#endif
-module_param(ivtv_pci_latency, int, 0644);
-module_param(ivtv_yuv_mode, int, 0644);
-module_param(ivtv_yuv_threshold, int, 0644);
-module_param(ivtv_first_minor, int, 0644);
-
-module_param(enc_mpg_buffers, int, 0644);
-module_param(enc_yuv_buffers, int, 0644);
-module_param(enc_vbi_buffers, int, 0644);
-module_param(enc_pcm_buffers, int, 0644);
-module_param(dec_mpg_buffers, int, 0644);
-module_param(dec_yuv_buffers, int, 0644);
-module_param(dec_vbi_buffers, int, 0644);
-
-module_param(tunertype, int, 0644);
-module_param(newi2c, int, 0644);
-module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644);
-
-MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
- "\t\t\tsee tuner.h for values");
-MODULE_PARM_DESC(radio,
- "Enable or disable the radio. Use only if autodetection\n"
- "\t\t\tfails. 0 = disable, 1 = enable");
-MODULE_PARM_DESC(cardtype,
- "Only use this option if your card is not detected properly.\n"
- "\t\tSpecify card type:\n"
- "\t\t\t 1 = WinTV PVR 250\n"
- "\t\t\t 2 = WinTV PVR 350\n"
- "\t\t\t 3 = WinTV PVR-150 or PVR-500\n"
- "\t\t\t 4 = AVerMedia M179\n"
- "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n"
- "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n"
- "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n"
- "\t\t\t 8 = Adaptec AVC-2410\n"
- "\t\t\t 9 = Adaptec AVC-2010\n"
- "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n"
- "\t\t\t11 = AOpen VA2000MAX-STN6\n"
- "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n"
- "\t\t\t13 = I/O Data GV-MVP/RX\n"
- "\t\t\t14 = I/O Data GV-MVP/RX2E\n"
- "\t\t\t15 = GOTVIEW PCI DVD\n"
- "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n"
- "\t\t\t17 = Yuan MPC622\n"
- "\t\t\t18 = Digital Cowboy DCT-MTVP1\n"
- "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite\n"
- "\t\t\t20 = Club3D ZAP-TV1x01\n"
- "\t\t\t21 = AverTV MCE 116 Plus\n"
- "\t\t\t22 = ASUS Falcon2\n"
- "\t\t\t23 = AverMedia PVR-150 Plus\n"
- "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
- "\t\t\t25 = AverMedia M104 (not yet working)\n"
- "\t\t\t26 = Buffalo PC-MV5L/PCI\n"
- "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n"
- "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n"
- "\t\t\t 0 = Autodetect (default)\n"
- "\t\t\t-1 = Ignore this card\n\t\t");
-MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
-MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC");
-MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)");
-MODULE_PARM_DESC(tunertype,
- "Specify tuner type:\n"
- "\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n"
- "\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n"
- "\t\t\t-1 = Autodetect (default)\n");
-MODULE_PARM_DESC(debug,
- "Debug level (bitmask). Default: 0\n"
- "\t\t\t 1/0x0001: warning\n"
- "\t\t\t 2/0x0002: info\n"
- "\t\t\t 4/0x0004: mailbox\n"
- "\t\t\t 8/0x0008: ioctl\n"
- "\t\t\t 16/0x0010: file\n"
- "\t\t\t 32/0x0020: dma\n"
- "\t\t\t 64/0x0040: irq\n"
- "\t\t\t 128/0x0080: decoder\n"
- "\t\t\t 256/0x0100: yuv\n"
- "\t\t\t 512/0x0200: i2c\n"
- "\t\t\t1024/0x0400: high volume\n");
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-MODULE_PARM_DESC(fw_debug,
- "Enable code for debugging firmware problems. Default: 0\n");
-#endif
-MODULE_PARM_DESC(ivtv_pci_latency,
- "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
- "\t\t\tDefault: Yes");
-MODULE_PARM_DESC(ivtv_yuv_mode,
- "Specify the yuv playback mode:\n"
- "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n"
- "\t\t\tDefault: 0 (interlaced)");
-MODULE_PARM_DESC(ivtv_yuv_threshold,
- "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n"
- "\t\t\tDefault: 480");
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS));
-MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS));
-MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS));
-MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffers (in kB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS));
-MODULE_PARM_DESC(dec_mpg_buffers,
- "Decoder MPG buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS));
-MODULE_PARM_DESC(dec_yuv_buffers,
- "Decoder YUV buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS));
-MODULE_PARM_DESC(dec_vbi_buffers,
- "Decoder VBI buffers (in kB)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS));
-MODULE_PARM_DESC(newi2c,
- "Use new I2C implementation\n"
- "\t\t\t-1 is autodetect, 0 is off, 1 is on\n"
- "\t\t\tDefault is autodetect");
-MODULE_PARM_DESC(i2c_clock_period,
- "Period of SCL for the I2C bus controlled by the CX23415/6\n"
- "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n"
- "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD));
-
-MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card");
-
-MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
-MODULE_DESCRIPTION("CX23415/CX23416 driver");
-MODULE_SUPPORTED_DEVICE
- ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n"
- "\t\t\tYuan MPG series and similar)");
-MODULE_LICENSE("GPL");
-
-MODULE_VERSION(IVTV_VERSION);
-
-void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask)
-{
- itv->irqmask &= ~mask;
- write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
-}
-
-void ivtv_set_irq_mask(struct ivtv *itv, u32 mask)
-{
- itv->irqmask |= mask;
- write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK);
-}
-
-int ivtv_set_output_mode(struct ivtv *itv, int mode)
-{
- int old_mode;
-
- spin_lock(&itv->lock);
- old_mode = itv->output_mode;
- if (old_mode == 0)
- itv->output_mode = old_mode = mode;
- spin_unlock(&itv->lock);
- return old_mode;
-}
-
-struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv)
-{
- switch (itv->output_mode) {
- case OUT_MPG:
- return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
- case OUT_YUV:
- return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
- default:
- return NULL;
- }
-}
-
-int ivtv_waitq(wait_queue_head_t *waitq)
-{
- DEFINE_WAIT(wait);
-
- prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
- schedule();
- finish_wait(waitq, &wait);
- return signal_pending(current) ? -EINTR : 0;
-}
-
-/* Generic utility functions */
-int ivtv_msleep_timeout(unsigned int msecs, int intr)
-{
- int timeout = msecs_to_jiffies(msecs);
-
- do {
- set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
- timeout = schedule_timeout(timeout);
- if (intr) {
- int ret = signal_pending(current);
-
- if (ret)
- return ret;
- }
- } while (timeout);
- return 0;
-}
-
-/* Release ioremapped memory */
-static void ivtv_iounmap(struct ivtv *itv)
-{
- if (itv == NULL)
- return;
-
- /* Release registers memory */
- if (itv->reg_mem != NULL) {
- IVTV_DEBUG_INFO("releasing reg_mem\n");
- iounmap(itv->reg_mem);
- itv->reg_mem = NULL;
- }
- /* Release io memory */
- if (itv->has_cx23415 && itv->dec_mem != NULL) {
- IVTV_DEBUG_INFO("releasing dec_mem\n");
- iounmap(itv->dec_mem);
- }
- itv->dec_mem = NULL;
-
- /* Release io memory */
- if (itv->enc_mem != NULL) {
- IVTV_DEBUG_INFO("releasing enc_mem\n");
- iounmap(itv->enc_mem);
- itv->enc_mem = NULL;
- }
-}
-
-/* Hauppauge card? get values from tveeprom */
-void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
-{
- u8 eedata[256];
-
- itv->i2c_client.addr = 0xA0 >> 1;
- tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata));
- tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata);
-}
-
-static void ivtv_process_eeprom(struct ivtv *itv)
-{
- struct tveeprom tv;
- int pci_slot = PCI_SLOT(itv->pdev->devfn);
-
- ivtv_read_eeprom(itv, &tv);
-
- /* Many thanks to Steven Toth from Hauppauge for providing the
- model numbers */
- switch (tv.model) {
- /* In a few cases the PCI subsystem IDs do not correctly
- identify the card. A better method is to check the
- model number from the eeprom instead. */
- case 30012 ... 30039: /* Low profile PVR250 */
- case 32000 ... 32999:
- case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */
- case 48400 ... 48599:
- itv->card = ivtv_get_card(IVTV_CARD_PVR_250);
- break;
- case 48100 ... 48399:
- case 48600 ... 48999:
- itv->card = ivtv_get_card(IVTV_CARD_PVR_350);
- break;
- case 23000 ... 23999: /* PVR500 */
- case 25000 ... 25999: /* Low profile PVR150 */
- case 26000 ... 26999: /* Regular PVR150 */
- itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
- break;
- case 0:
- IVTV_ERR("Invalid EEPROM\n");
- return;
- default:
- IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model);
- itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
- break;
- }
-
- switch (tv.model) {
- /* Old style PVR350 (with an saa7114) uses this input for
- the tuner. */
- case 48254:
- itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1);
- break;
- default:
- break;
- }
-
- itv->v4l2_cap = itv->card->v4l2_capabilities;
- itv->card_name = itv->card->name;
- itv->card_i2c = itv->card->i2c;
-
- /* If this is a PVR500 then it should be possible to detect whether it is the
- first or second unit by looking at the subsystem device ID: is bit 4 is
- set, then it is the second unit (according to info from Hauppauge).
-
- However, while this works for most cards, I have seen a few PVR500 cards
- where both units have the same subsystem ID.
-
- So instead I look at the reported 'PCI slot' (which is the slot on the PVR500
- PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise
- it is the second unit. It is possible that it is a different slot when ivtv is
- used in Xen, in that case I ignore this card here. The worst that can happen
- is that the card presents itself with a non-working radio device.
-
- This detection is needed since the eeprom reports incorrectly that a radio is
- present on the second unit. */
- if (tv.model / 1000 == 23) {
- static const struct ivtv_card_tuner_i2c ivtv_i2c_radio = {
- .radio = { 0x60, I2C_CLIENT_END },
- .demod = { 0x43, I2C_CLIENT_END },
- .tv = { 0x61, I2C_CLIENT_END },
- };
-
- itv->card_name = "WinTV PVR 500";
- itv->card_i2c = &ivtv_i2c_radio;
- if (pci_slot == 8 || pci_slot == 9) {
- int is_first = (pci_slot & 1) == 0;
-
- itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" :
- "WinTV PVR 500 (unit #2)";
- if (!is_first) {
- IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n");
- tv.has_radio = 0;
- }
- }
- }
- IVTV_INFO("Autodetected %s\n", itv->card_name);
-
- switch (tv.tuner_hauppauge_model) {
- case 85:
- case 99:
- case 112:
- itv->pvr150_workaround = 1;
- break;
- default:
- break;
- }
- if (tv.tuner_type == TUNER_ABSENT)
- IVTV_ERR("tveeprom cannot autodetect tuner!\n");
-
- if (itv->options.tuner == -1)
- itv->options.tuner = tv.tuner_type;
- if (itv->options.radio == -1)
- itv->options.radio = (tv.has_radio != 0);
- /* only enable newi2c if an IR blaster is present */
- if (itv->options.newi2c == -1 && tv.has_ir) {
- itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0;
- if (itv->options.newi2c) {
- IVTV_INFO("Reopen i2c bus for IR-blaster support\n");
- exit_ivtv_i2c(itv);
- init_ivtv_i2c(itv);
- }
- }
-
- if (itv->std != 0)
- /* user specified tuner standard */
- return;
-
- /* autodetect tuner standard */
- if (tv.tuner_formats & V4L2_STD_PAL) {
- IVTV_DEBUG_INFO("PAL tuner detected\n");
- itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
- } else if (tv.tuner_formats & V4L2_STD_NTSC) {
- IVTV_DEBUG_INFO("NTSC tuner detected\n");
- itv->std |= V4L2_STD_NTSC_M;
- } else if (tv.tuner_formats & V4L2_STD_SECAM) {
- IVTV_DEBUG_INFO("SECAM tuner detected\n");
- itv->std |= V4L2_STD_SECAM_L;
- } else {
- IVTV_INFO("No tuner detected, default to NTSC-M\n");
- itv->std |= V4L2_STD_NTSC_M;
- }
-}
-
-static v4l2_std_id ivtv_parse_std(struct ivtv *itv)
-{
- switch (pal[0]) {
- case '6':
- tunertype = 0;
- return V4L2_STD_PAL_60;
- case 'b':
- case 'B':
- case 'g':
- case 'G':
- case 'h':
- case 'H':
- tunertype = 0;
- return V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
- case 'n':
- case 'N':
- tunertype = 1;
- if (pal[1] == 'c' || pal[1] == 'C')
- return V4L2_STD_PAL_Nc;
- return V4L2_STD_PAL_N;
- case 'i':
- case 'I':
- tunertype = 0;
- return V4L2_STD_PAL_I;
- case 'd':
- case 'D':
- case 'k':
- case 'K':
- tunertype = 0;
- return V4L2_STD_PAL_DK;
- case 'M':
- case 'm':
- tunertype = 1;
- return V4L2_STD_PAL_M;
- case '-':
- break;
- default:
- IVTV_WARN("pal= argument not recognised\n");
- return 0;
- }
-
- switch (secam[0]) {
- case 'b':
- case 'B':
- case 'g':
- case 'G':
- case 'h':
- case 'H':
- tunertype = 0;
- return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
- case 'd':
- case 'D':
- case 'k':
- case 'K':
- tunertype = 0;
- return V4L2_STD_SECAM_DK;
- case 'l':
- case 'L':
- tunertype = 0;
- if (secam[1] == 'C' || secam[1] == 'c')
- return V4L2_STD_SECAM_LC;
- return V4L2_STD_SECAM_L;
- case '-':
- break;
- default:
- IVTV_WARN("secam= argument not recognised\n");
- return 0;
- }
-
- switch (ntsc[0]) {
- case 'm':
- case 'M':
- tunertype = 1;
- return V4L2_STD_NTSC_M;
- case 'j':
- case 'J':
- tunertype = 1;
- return V4L2_STD_NTSC_M_JP;
- case 'k':
- case 'K':
- tunertype = 1;
- return V4L2_STD_NTSC_M_KR;
- case '-':
- break;
- default:
- IVTV_WARN("ntsc= argument not recognised\n");
- return 0;
- }
-
- /* no match found */
- return 0;
-}
-
-static void ivtv_process_options(struct ivtv *itv)
-{
- const char *chipname;
- int i, j;
-
- itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers * 1024;
- itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers * 1024;
- itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers * 1024;
- itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
- itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
- itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
- itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
- itv->options.cardtype = cardtype[itv->instance];
- itv->options.tuner = tuner[itv->instance];
- itv->options.radio = radio[itv->instance];
-
- itv->options.i2c_clock_period = i2c_clock_period[itv->instance];
- if (itv->options.i2c_clock_period == -1)
- itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD;
- else if (itv->options.i2c_clock_period < 10)
- itv->options.i2c_clock_period = 10;
- else if (itv->options.i2c_clock_period > 4500)
- itv->options.i2c_clock_period = 4500;
-
- itv->options.newi2c = newi2c;
- if (tunertype < -1 || tunertype > 1) {
- IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
- tunertype = -1;
- }
- itv->std = ivtv_parse_std(itv);
- if (itv->std == 0 && tunertype >= 0)
- itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN);
- itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15);
- chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
- if (itv->options.cardtype == -1) {
- IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
- return;
- }
- if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) {
- IVTV_INFO("User specified %s card (detected %s based chip)\n",
- itv->card->name, chipname);
- } else if (itv->options.cardtype != 0) {
- IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
- }
- if (itv->card == NULL) {
- if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
- itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
- itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
- itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
- IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
- chipname);
- }
- }
- if (itv->card == NULL) {
- for (i = 0; (itv->card = ivtv_get_card(i)); i++) {
- if (itv->card->pci_list == NULL)
- continue;
- for (j = 0; itv->card->pci_list[j].device; j++) {
- if (itv->pdev->device !=
- itv->card->pci_list[j].device)
- continue;
- if (itv->pdev->subsystem_vendor !=
- itv->card->pci_list[j].subsystem_vendor)
- continue;
- if (itv->pdev->subsystem_device !=
- itv->card->pci_list[j].subsystem_device)
- continue;
- IVTV_INFO("Autodetected %s card (%s based)\n",
- itv->card->name, chipname);
- goto done;
- }
- }
- }
-done:
-
- if (itv->card == NULL) {
- itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
- IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
- itv->pdev->vendor, itv->pdev->device);
- IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n",
- itv->pdev->subsystem_vendor, itv->pdev->subsystem_device);
- IVTV_ERR(" %s based\n", chipname);
- IVTV_ERR("Defaulting to %s card\n", itv->card->name);
- IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
- IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
- IVTV_ERR("Prefix your subject line with [UNKNOWN IVTV CARD].\n");
- }
- itv->v4l2_cap = itv->card->v4l2_capabilities;
- itv->card_name = itv->card->name;
- itv->card_i2c = itv->card->i2c;
-}
-
-/* Precondition: the ivtv structure has been memset to 0. Only
- the dev and num fields have been filled in.
- No assumptions on the card type may be made here (see ivtv_init_struct2
- for that).
- */
-static int __devinit ivtv_init_struct1(struct ivtv *itv)
-{
- struct sched_param param = { .sched_priority = 99 };
-
- itv->base_addr = pci_resource_start(itv->pdev, 0);
- itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
- itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
-
- mutex_init(&itv->serialize_lock);
- mutex_init(&itv->i2c_bus_lock);
- mutex_init(&itv->udma.lock);
-
- spin_lock_init(&itv->lock);
- spin_lock_init(&itv->dma_reg_lock);
-
- init_kthread_worker(&itv->irq_worker);
- itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker,
- itv->v4l2_dev.name);
- if (IS_ERR(itv->irq_worker_task)) {
- IVTV_ERR("Could not create ivtv task\n");
- return -1;
- }
- /* must use the FIFO scheduler as it is realtime sensitive */
- sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, &param);
-
- init_kthread_work(&itv->irq_work, ivtv_irq_work_handler);
-
- /* Initial settings */
- itv->cxhdl.port = CX2341X_PORT_MEMORY;
- itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
- init_waitqueue_head(&itv->eos_waitq);
- init_waitqueue_head(&itv->event_waitq);
- init_waitqueue_head(&itv->vsync_waitq);
- init_waitqueue_head(&itv->dma_waitq);
- init_timer(&itv->dma_timer);
- itv->dma_timer.function = ivtv_unfinished_dma;
- itv->dma_timer.data = (unsigned long)itv;
-
- itv->cur_dma_stream = -1;
- itv->cur_pio_stream = -1;
-
- /* Ctrls */
- itv->speed = 1000;
-
- /* VBI */
- itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced;
-
- /* Init the sg table for osd/yuv output */
- sg_init_table(itv->udma.SGlist, IVTV_DMA_SG_OSD_ENT);
-
- /* OSD */
- itv->osd_global_alpha_state = 1;
- itv->osd_global_alpha = 255;
-
- /* YUV */
- atomic_set(&itv->yuv_info.next_dma_frame, -1);
- itv->yuv_info.lace_mode = ivtv_yuv_mode;
- itv->yuv_info.lace_threshold = ivtv_yuv_threshold;
- itv->yuv_info.max_frames_buffered = 3;
- itv->yuv_info.track_osd = 1;
- return 0;
-}
-
-/* Second initialization part. Here the card type has been
- autodetected. */
-static void __devinit ivtv_init_struct2(struct ivtv *itv)
-{
- int i;
-
- for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++)
- if (itv->card->video_inputs[i].video_type == 0)
- break;
- itv->nof_inputs = i;
- for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++)
- if (itv->card->audio_inputs[i].audio_type == 0)
- break;
- itv->nof_audio_inputs = i;
-
- if (itv->card->hw_all & IVTV_HW_CX25840) {
- itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */
- } else {
- itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */
- }
-
- /* Find tuner input */
- for (i = 0; i < itv->nof_inputs; i++) {
- if (itv->card->video_inputs[i].video_type ==
- IVTV_CARD_INPUT_VID_TUNER)
- break;
- }
- if (i == itv->nof_inputs)
- i = 0;
- itv->active_input = i;
- itv->audio_input = itv->card->video_inputs[i].audio_index;
-}
-
-static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
- const struct pci_device_id *pci_id)
-{
- u16 cmd;
- unsigned char pci_latency;
-
- IVTV_DEBUG_INFO("Enabling pci device\n");
-
- if (pci_enable_device(pdev)) {
- IVTV_ERR("Can't enable device!\n");
- return -EIO;
- }
- if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
- IVTV_ERR("No suitable DMA available.\n");
- return -EIO;
- }
- if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
- IVTV_ERR("Cannot request encoder memory region.\n");
- return -EIO;
- }
-
- if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
- IVTV_REG_SIZE, "ivtv registers")) {
- IVTV_ERR("Cannot request register memory region.\n");
- release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
- return -EIO;
- }
-
- if (itv->has_cx23415 &&
- !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
- IVTV_DECODER_SIZE, "ivtv decoder")) {
- IVTV_ERR("Cannot request decoder memory region.\n");
- release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
- release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
- return -EIO;
- }
-
- /* Check for bus mastering */
- pci_read_config_word(pdev, PCI_COMMAND, &cmd);
- if (!(cmd & PCI_COMMAND_MASTER)) {
- IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
- pci_set_master(pdev);
- pci_read_config_word(pdev, PCI_COMMAND, &cmd);
- if (!(cmd & PCI_COMMAND_MASTER)) {
- IVTV_ERR("Bus Mastering is not enabled\n");
- return -ENXIO;
- }
- }
- IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
-
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
-
- if (pci_latency < 64 && ivtv_pci_latency) {
- IVTV_INFO("Unreasonably low latency timer, "
- "setting to 64 (was %d)\n", pci_latency);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
- }
- /* This config space value relates to DMA latencies. The
- default value 0x8080 is too low however and will lead
- to DMA errors. 0xffff is the max value which solves
- these problems. */
- pci_write_config_dword(pdev, 0x40, 0xffff);
-
- IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
- "irq: %d, latency: %d, memory: 0x%llx\n",
- pdev->device, pdev->revision, pdev->bus->number,
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
- pdev->irq, pci_latency, (u64)itv->base_addr);
-
- return 0;
-}
-
-static void ivtv_load_and_init_modules(struct ivtv *itv)
-{
- u32 hw = itv->card->hw_all;
- unsigned i;
-
- /* check which i2c devices are actually found */
- for (i = 0; i < 32; i++) {
- u32 device = 1 << i;
-
- if (!(device & hw))
- continue;
- if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) {
- /* GPIO and TVEEPROM do not use i2c probing */
- itv->hw_flags |= device;
- continue;
- }
- if (ivtv_i2c_register(itv, i) == 0)
- itv->hw_flags |= device;
- }
-
- /* probe for legacy IR controllers that aren't in card definitions */
- if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0)
- ivtv_i2c_new_ir_legacy(itv);
-
- if (itv->card->hw_all & IVTV_HW_CX25840)
- itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
- else if (itv->card->hw_all & IVTV_HW_SAA717X)
- itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
- else if (itv->card->hw_all & IVTV_HW_SAA7114)
- itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
- else
- itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
- itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
- itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
-
- hw = itv->hw_flags;
-
- if (itv->card->type == IVTV_CARD_CX23416GYC) {
- /* Several variations of this card exist, detect which card
- type should be used. */
- if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0)
- itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS);
- else if ((hw & IVTV_HW_UPD64031A) == 0)
- itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR);
- }
- else if (itv->card->type == IVTV_CARD_GV_MVPRX ||
- itv->card->type == IVTV_CARD_GV_MVPRX2E) {
- /* The crystal frequency of GVMVPRX is 24.576MHz */
- v4l2_subdev_call(itv->sd_video, video, s_crystal_freq,
- SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC);
- }
-
- if (hw & IVTV_HW_CX25840) {
- itv->vbi.raw_decoder_line_size = 1444;
- itv->vbi.raw_decoder_sav_odd_field = 0x20;
- itv->vbi.raw_decoder_sav_even_field = 0x60;
- itv->vbi.sliced_decoder_line_size = 272;
- itv->vbi.sliced_decoder_sav_odd_field = 0xB0;
- itv->vbi.sliced_decoder_sav_even_field = 0xF0;
- }
-
- if (hw & IVTV_HW_SAA711X) {
- struct v4l2_dbg_chip_ident v;
-
- /* determine the exact saa711x model */
- itv->hw_flags &= ~IVTV_HW_SAA711X;
-
- v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER;
- strlcpy(v.match.name, "saa7115", sizeof(v.match.name));
- ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
- if (v.ident == V4L2_IDENT_SAA7114) {
- itv->hw_flags |= IVTV_HW_SAA7114;
- /* VBI is not yet supported by the saa7114 driver. */
- itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE);
- } else {
- itv->hw_flags |= IVTV_HW_SAA7115;
- }
- itv->vbi.raw_decoder_line_size = 1443;
- itv->vbi.raw_decoder_sav_odd_field = 0x25;
- itv->vbi.raw_decoder_sav_even_field = 0x62;
- itv->vbi.sliced_decoder_line_size = 51;
- itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
- itv->vbi.sliced_decoder_sav_even_field = 0xEC;
- }
-
- if (hw & IVTV_HW_SAA717X) {
- itv->vbi.raw_decoder_line_size = 1443;
- itv->vbi.raw_decoder_sav_odd_field = 0x25;
- itv->vbi.raw_decoder_sav_even_field = 0x62;
- itv->vbi.sliced_decoder_line_size = 51;
- itv->vbi.sliced_decoder_sav_odd_field = 0xAB;
- itv->vbi.sliced_decoder_sav_even_field = 0xEC;
- }
-}
-
-static int __devinit ivtv_probe(struct pci_dev *pdev,
- const struct pci_device_id *pci_id)
-{
- int retval = 0;
- int vbi_buf_size;
- struct ivtv *itv;
-
- itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
- if (itv == NULL)
- return -ENOMEM;
- itv->pdev = pdev;
- itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv",
- &ivtv_instance);
-
- retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev);
- if (retval) {
- kfree(itv);
- return retval;
- }
- IVTV_INFO("Initializing card %d\n", itv->instance);
-
- ivtv_process_options(itv);
- if (itv->options.cardtype == -1) {
- retval = -ENODEV;
- goto err;
- }
- if (ivtv_init_struct1(itv)) {
- retval = -ENOMEM;
- goto err;
- }
- retval = cx2341x_handler_init(&itv->cxhdl, 50);
- if (retval)
- goto err;
- itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
- itv->cxhdl.ops = &ivtv_cxhdl_ops;
- itv->cxhdl.priv = itv;
- itv->cxhdl.func = ivtv_api_func;
-
- IVTV_DEBUG_INFO("base addr: 0x%llx\n", (u64)itv->base_addr);
-
- /* PCI Device Setup */
- retval = ivtv_setup_pci(itv, pdev, pci_id);
- if (retval == -EIO)
- goto free_worker;
- if (retval == -ENXIO)
- goto free_mem;
-
- /* map io memory */
- IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
- (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE);
- itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET,
- IVTV_ENCODER_SIZE);
- if (!itv->enc_mem) {
- IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
- "encoder memory\n");
- IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of "
- "vmalloc address space for this window\n");
- IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
- IVTV_ERR("Use the vmalloc= kernel command line option to set "
- "VmallocTotal to a larger value\n");
- retval = -ENOMEM;
- goto free_mem;
- }
-
- if (itv->has_cx23415) {
- IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
- (u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
- itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET,
- IVTV_DECODER_SIZE);
- if (!itv->dec_mem) {
- IVTV_ERR("ioremap failed. Can't get a window into "
- "CX23415 decoder memory\n");
- IVTV_ERR("Each capture card with a CX23415 needs 8 MB "
- "of vmalloc address space for this window\n");
- IVTV_ERR("Check the output of 'grep Vmalloc "
- "/proc/meminfo'\n");
- IVTV_ERR("Use the vmalloc= kernel command line option "
- "to set VmallocTotal to a larger value\n");
- retval = -ENOMEM;
- goto free_mem;
- }
- }
- else {
- itv->dec_mem = itv->enc_mem;
- }
-
- /* map registers memory */
- IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
- (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
- itv->reg_mem =
- ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
- if (!itv->reg_mem) {
- IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 "
- "register space\n");
- IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of "
- "vmalloc address space for this window\n");
- IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
- IVTV_ERR("Use the vmalloc= kernel command line option to set "
- "VmallocTotal to a larger value\n");
- retval = -ENOMEM;
- goto free_io;
- }
-
- retval = ivtv_gpio_init(itv);
- if (retval)
- goto free_io;
-
- /* active i2c */
- IVTV_DEBUG_INFO("activating i2c...\n");
- if (init_ivtv_i2c(itv)) {
- IVTV_ERR("Could not initialize i2c\n");
- goto free_io;
- }
-
- if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
- /* Based on the model number the cardtype may be changed.
- The PCI IDs are not always reliable. */
- ivtv_process_eeprom(itv);
- }
- if (itv->card->comment)
- IVTV_INFO("%s", itv->card->comment);
- if (itv->card->v4l2_capabilities == 0) {
- /* card was detected but is not supported */
- retval = -ENODEV;
- goto free_i2c;
- }
-
- if (itv->std == 0) {
- itv->std = V4L2_STD_NTSC_M;
- }
-
- if (itv->options.tuner == -1) {
- int i;
-
- for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) {
- if ((itv->std & itv->card->tuners[i].std) == 0)
- continue;
- itv->options.tuner = itv->card->tuners[i].tuner;
- break;
- }
- }
- /* if no tuner was found, then pick the first tuner in the card list */
- if (itv->options.tuner == -1 && itv->card->tuners[0].std) {
- itv->std = itv->card->tuners[0].std;
- if (itv->std & V4L2_STD_PAL)
- itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
- else if (itv->std & V4L2_STD_NTSC)
- itv->std = V4L2_STD_NTSC_M;
- else if (itv->std & V4L2_STD_SECAM)
- itv->std = V4L2_STD_SECAM_L;
- itv->options.tuner = itv->card->tuners[0].tuner;
- }
- if (itv->options.radio == -1)
- itv->options.radio = (itv->card->radio_input.audio_type != 0);
-
- /* The card is now fully identified, continue with card-specific
- initialization. */
- ivtv_init_struct2(itv);
-
- ivtv_load_and_init_modules(itv);
-
- if (itv->std & V4L2_STD_525_60) {
- itv->is_60hz = 1;
- itv->is_out_60hz = 1;
- } else {
- itv->is_50hz = 1;
- itv->is_out_50hz = 1;
- }
-
- itv->yuv_info.osd_full_w = 720;
- itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480;
- itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
- itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;
-
- cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
-
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
- itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000;
- itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000;
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000;
-
- /* Setup VBI Raw Size. Should be big enough to hold PAL.
- It is possible to switch between PAL and NTSC, so we need to
- take the largest size here. */
- /* 1456 is multiple of 16, real size = 1444 */
- itv->vbi.raw_size = 1456;
- /* We use a buffer size of 1/2 of the total size needed for a
- frame. This is actually very useful, since we now receive
- a field at a time and that makes 'compressing' the raw data
- down to size by stripping off the SAV codes a lot easier.
- Note: having two different buffer sizes prevents standard
- switching on the fly. We need to find a better solution... */
- vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2;
- itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
- itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36;
-
- if (itv->options.radio > 0)
- itv->v4l2_cap |= V4L2_CAP_RADIO;
-
- if (itv->options.tuner > -1) {
- struct tuner_setup setup;
-
- setup.addr = ADDR_UNSET;
- setup.type = itv->options.tuner;
- setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
- if (itv->options.radio > 0)
- setup.mode_mask |= T_RADIO;
- setup.tuner_callback = (setup.type == TUNER_XC2028) ?
- ivtv_reset_tuner_gpio : NULL;
- ivtv_call_all(itv, tuner, s_type_addr, &setup);
- if (setup.type == TUNER_XC2028) {
- static struct xc2028_ctrl ctrl = {
- .fname = XC2028_DEFAULT_FIRMWARE,
- .max_len = 64,
- };
- struct v4l2_priv_tun_config cfg = {
- .tuner = itv->options.tuner,
- .priv = &ctrl,
- };
- ivtv_call_all(itv, tuner, s_config, &cfg);
- }
- }
-
- /* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
- are not. */
- itv->tuner_std = itv->std;
-
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
-
- itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0);
- itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0);
- /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
- mask that menu item. */
- itv->ctrl_audio_playback =
- v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
- 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
- itv->ctrl_audio_multilingual_playback =
- v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
- 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
- if (hdl->error) {
- retval = hdl->error;
- goto free_i2c;
- }
- v4l2_ctrl_cluster(2, &itv->ctrl_pts);
- v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
- ivtv_call_all(itv, video, s_std_output, itv->std);
- /* Turn off the output signal. The mpeg decoder is not yet
- active so without this you would get a green image until the
- mpeg decoder becomes active. */
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
- }
-
- /* clear interrupt mask, effectively disabling interrupts */
- ivtv_set_irq_mask(itv, 0xffffffff);
-
- /* Register IRQ */
- retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
- IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
- if (retval) {
- IVTV_ERR("Failed to register irq %d\n", retval);
- goto free_i2c;
- }
-
- retval = ivtv_streams_setup(itv);
- if (retval) {
- IVTV_ERR("Error %d setting up streams\n", retval);
- goto free_irq;
- }
- retval = ivtv_streams_register(itv);
- if (retval) {
- IVTV_ERR("Error %d registering devices\n", retval);
- goto free_streams;
- }
- IVTV_INFO("Initialized card: %s\n", itv->card_name);
- return 0;
-
-free_streams:
- ivtv_streams_cleanup(itv, 1);
-free_irq:
- free_irq(itv->pdev->irq, (void *)itv);
-free_i2c:
- v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
- exit_ivtv_i2c(itv);
-free_io:
- ivtv_iounmap(itv);
-free_mem:
- release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
- release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
- if (itv->has_cx23415)
- release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
-free_worker:
- kthread_stop(itv->irq_worker_task);
-err:
- if (retval == 0)
- retval = -ENODEV;
- IVTV_ERR("Error %d on initialization\n", retval);
-
- v4l2_device_unregister(&itv->v4l2_dev);
- kfree(itv);
- return retval;
-}
-
-int ivtv_init_on_first_open(struct ivtv *itv)
-{
- struct v4l2_frequency vf;
- /* Needed to call ioctls later */
- struct ivtv_open_id fh;
- int fw_retry_count = 3;
- int video_input;
-
- fh.itv = itv;
-
- if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
- return -ENXIO;
-
- if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags))
- return 0;
-
- while (--fw_retry_count > 0) {
- /* load firmware */
- if (ivtv_firmware_init(itv) == 0)
- break;
- if (fw_retry_count > 1)
- IVTV_WARN("Retry loading firmware\n");
- }
-
- if (fw_retry_count == 0) {
- set_bit(IVTV_F_I_FAILED, &itv->i_flags);
- return -ENXIO;
- }
-
- /* Try and get firmware versions */
- IVTV_DEBUG_INFO("Getting firmware version..\n");
- ivtv_firmware_versions(itv);
-
- if (itv->card->hw_all & IVTV_HW_CX25840)
- v4l2_subdev_call(itv->sd_video, core, load_fw);
-
- vf.tuner = 0;
- vf.type = V4L2_TUNER_ANALOG_TV;
- vf.frequency = 6400; /* the tuner 'baseline' frequency */
-
- /* Set initial frequency. For PAL/SECAM broadcasts no
- 'default' channel exists AFAIK. */
- if (itv->std == V4L2_STD_NTSC_M_JP) {
- vf.frequency = 1460; /* ch. 1 91250*16/1000 */
- }
- else if (itv->std & V4L2_STD_NTSC_M) {
- vf.frequency = 1076; /* ch. 4 67250*16/1000 */
- }
-
- video_input = itv->active_input;
- itv->active_input++; /* Force update of input */
- ivtv_s_input(NULL, &fh, video_input);
-
- /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
- in one place. */
- itv->std++; /* Force full standard initialization */
- itv->std_out = itv->std;
- ivtv_s_frequency(NULL, &fh, &vf);
-
- if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
- /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
- the mpeg decoder so now the saa7127 receives a proper
- signal. */
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
- ivtv_init_mpeg_decoder(itv);
- }
-
- /* On a cx23416 this seems to be able to enable DMA to the chip? */
- if (!itv->has_cx23415)
- write_reg_sync(0x03, IVTV_REG_DMACONTROL);
-
- ivtv_s_std_enc(itv, &itv->tuner_std);
-
- /* Default interrupts enabled. For the PVR350 this includes the
- decoder VSYNC interrupt, which is always on. It is not only used
- during decoding but also by the OSD.
- Some old PVR250 cards had a cx23415, so testing for that is too
- general. Instead test if the card has video output capability. */
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC);
- ivtv_set_osd_alpha(itv);
- ivtv_s_std_dec(itv, &itv->tuner_std);
- } else {
- ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT);
- }
-
- /* Setup initial controls */
- cx2341x_handler_setup(&itv->cxhdl);
- return 0;
-}
-
-static void ivtv_remove(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct ivtv *itv = to_ivtv(v4l2_dev);
- int i;
-
- IVTV_DEBUG_INFO("Removing card\n");
-
- if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
- /* Stop all captures */
- IVTV_DEBUG_INFO("Stopping all streams\n");
- if (atomic_read(&itv->capturing) > 0)
- ivtv_stop_all_captures(itv);
-
- /* Stop all decoding */
- IVTV_DEBUG_INFO("Stopping decoding\n");
-
- /* Turn off the TV-out */
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
- if (atomic_read(&itv->decoding) > 0) {
- int type;
-
- if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
- type = IVTV_DEC_STREAM_TYPE_YUV;
- else
- type = IVTV_DEC_STREAM_TYPE_MPG;
- ivtv_stop_v4l2_decode_stream(&itv->streams[type],
- V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
- }
- ivtv_halt_firmware(itv);
- }
-
- /* Interrupts */
- ivtv_set_irq_mask(itv, 0xffffffff);
- del_timer_sync(&itv->dma_timer);
-
- /* Kill irq worker */
- flush_kthread_worker(&itv->irq_worker);
- kthread_stop(itv->irq_worker_task);
-
- ivtv_streams_cleanup(itv, 1);
- ivtv_udma_free(itv);
-
- v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
-
- exit_ivtv_i2c(itv);
-
- free_irq(itv->pdev->irq, (void *)itv);
- ivtv_iounmap(itv);
-
- release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
- release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
- if (itv->has_cx23415)
- release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
-
- pci_disable_device(itv->pdev);
- for (i = 0; i < IVTV_VBI_FRAMES; i++)
- kfree(itv->vbi.sliced_mpeg_data[i]);
-
- printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
-
- v4l2_device_unregister(&itv->v4l2_dev);
- kfree(itv);
-}
-
-/* define a pci_driver for card detection */
-static struct pci_driver ivtv_pci_driver = {
- .name = "ivtv",
- .id_table = ivtv_pci_tbl,
- .probe = ivtv_probe,
- .remove = ivtv_remove,
-};
-
-static int __init module_start(void)
-{
- printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
-
- /* Validate parameters */
- if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
- IVTV_MAX_CARDS - 1);
- return -1;
- }
-
- if (ivtv_debug < 0 || ivtv_debug > 2047) {
- ivtv_debug = 0;
- printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
- }
-
- if (pci_register_driver(&ivtv_pci_driver)) {
- printk(KERN_ERR "ivtv: Error detecting PCI card\n");
- return -ENODEV;
- }
- printk(KERN_INFO "ivtv: End initialization\n");
- return 0;
-}
-
-static void __exit module_cleanup(void)
-{
- pci_unregister_driver(&ivtv_pci_driver);
-}
-
-/* Note: These symbols are exported because they are used by the ivtvfb
- framebuffer module and an infrared module for the IR-blaster. */
-EXPORT_SYMBOL(ivtv_set_irq_mask);
-EXPORT_SYMBOL(ivtv_api);
-EXPORT_SYMBOL(ivtv_vapi);
-EXPORT_SYMBOL(ivtv_vapi_result);
-EXPORT_SYMBOL(ivtv_clear_irq_mask);
-EXPORT_SYMBOL(ivtv_debug);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-EXPORT_SYMBOL(ivtv_fw_debug);
-#endif
-EXPORT_SYMBOL(ivtv_reset_ir_gpio);
-EXPORT_SYMBOL(ivtv_udma_setup);
-EXPORT_SYMBOL(ivtv_udma_unmap);
-EXPORT_SYMBOL(ivtv_udma_alloc);
-EXPORT_SYMBOL(ivtv_udma_prepare);
-EXPORT_SYMBOL(ivtv_init_on_first_open);
-EXPORT_SYMBOL(ivtv_firmware_check);
-
-module_init(module_start);
-module_exit(module_cleanup);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
deleted file mode 100644
index a7e00f8938f8..000000000000
--- a/drivers/media/video/ivtv/ivtv-driver.h
+++ /dev/null
@@ -1,839 +0,0 @@
-/*
- ivtv driver internal defines and structures
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef IVTV_DRIVER_H
-#define IVTV_DRIVER_H
-
-/* Internal header for ivtv project:
- * Driver for the cx23415/6 chip.
- * Author: Kevin Thayer (nufan_wfk at yahoo.com)
- * License: GPL
- * http://www.ivtvdriver.org
- *
- * -----
- * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
- * and Takeru KOMORIYA<komoriya@paken.org>
- *
- * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
- * using information provided by Jiun-Kuei Jung @ AVerMedia.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/list.h>
-#include <linux/unistd.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-#include <linux/kthread.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-
-#include <linux/dvb/video.h>
-#include <linux/dvb/audio.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/tuner.h>
-#include <media/cx2341x.h>
-#include <media/ir-kbd-i2c.h>
-
-#include <linux/ivtv.h>
-
-/* Memory layout */
-#define IVTV_ENCODER_OFFSET 0x00000000
-#define IVTV_ENCODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
-#define IVTV_DECODER_OFFSET 0x01000000
-#define IVTV_DECODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */
-#define IVTV_REG_OFFSET 0x02000000
-#define IVTV_REG_SIZE 0x00010000
-
-/* Maximum ivtv driver instances. Some people have a huge number of
- capture cards, so set this to a high value. */
-#define IVTV_MAX_CARDS 32
-
-#define IVTV_ENC_STREAM_TYPE_MPG 0
-#define IVTV_ENC_STREAM_TYPE_YUV 1
-#define IVTV_ENC_STREAM_TYPE_VBI 2
-#define IVTV_ENC_STREAM_TYPE_PCM 3
-#define IVTV_ENC_STREAM_TYPE_RAD 4
-#define IVTV_DEC_STREAM_TYPE_MPG 5
-#define IVTV_DEC_STREAM_TYPE_VBI 6
-#define IVTV_DEC_STREAM_TYPE_VOUT 7
-#define IVTV_DEC_STREAM_TYPE_YUV 8
-#define IVTV_MAX_STREAMS 9
-
-#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */
-
-/* DMA Registers */
-#define IVTV_REG_DMAXFER (0x0000)
-#define IVTV_REG_DMASTATUS (0x0004)
-#define IVTV_REG_DECDMAADDR (0x0008)
-#define IVTV_REG_ENCDMAADDR (0x000c)
-#define IVTV_REG_DMACONTROL (0x0010)
-#define IVTV_REG_IRQSTATUS (0x0040)
-#define IVTV_REG_IRQMASK (0x0048)
-
-/* Setup Registers */
-#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8)
-#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC)
-#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8)
-#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC)
-#define IVTV_REG_VDM (0x2800)
-#define IVTV_REG_AO (0x2D00)
-#define IVTV_REG_BYTEFLUSH (0x2D24)
-#define IVTV_REG_SPU (0x9050)
-#define IVTV_REG_HW_BLOCKS (0x9054)
-#define IVTV_REG_VPU (0x9058)
-#define IVTV_REG_APU (0xA064)
-
-/* Other registers */
-#define IVTV_REG_DEC_LINE_FIELD (0x28C0)
-
-/* debugging */
-extern int ivtv_debug;
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-extern int ivtv_fw_debug;
-#endif
-
-#define IVTV_DBGFLG_WARN (1 << 0)
-#define IVTV_DBGFLG_INFO (1 << 1)
-#define IVTV_DBGFLG_MB (1 << 2)
-#define IVTV_DBGFLG_IOCTL (1 << 3)
-#define IVTV_DBGFLG_FILE (1 << 4)
-#define IVTV_DBGFLG_DMA (1 << 5)
-#define IVTV_DBGFLG_IRQ (1 << 6)
-#define IVTV_DBGFLG_DEC (1 << 7)
-#define IVTV_DBGFLG_YUV (1 << 8)
-#define IVTV_DBGFLG_I2C (1 << 9)
-/* Flag to turn on high volume debugging */
-#define IVTV_DBGFLG_HIGHVOL (1 << 10)
-
-#define IVTV_DEBUG(x, type, fmt, args...) \
- do { \
- if ((x) & ivtv_debug) \
- v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
- } while (0)
-#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
-#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
-#define IVTV_DEBUG_MB(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_MB, "mb", fmt , ## args)
-#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
-#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
-#define IVTV_DEBUG_FILE(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_FILE, "file", fmt , ## args)
-#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
-#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
-#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
-#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
-
-#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
- do { \
- if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
- v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
- } while (0)
-#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
-#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
-#define IVTV_DEBUG_HI_MB(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_MB, "mb", fmt , ## args)
-#define IVTV_DEBUG_HI_DMA(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA, "dma", fmt , ## args)
-#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args)
-#define IVTV_DEBUG_HI_FILE(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_FILE, "file", fmt , ## args)
-#define IVTV_DEBUG_HI_I2C(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C, "i2c", fmt , ## args)
-#define IVTV_DEBUG_HI_IRQ(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ, "irq", fmt , ## args)
-#define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args)
-#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
-
-/* Standard kernel messages */
-#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args)
-#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args)
-#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args)
-
-/* output modes (cx23415 only) */
-#define OUT_NONE 0
-#define OUT_MPG 1
-#define OUT_YUV 2
-#define OUT_UDMA_YUV 3
-#define OUT_PASSTHROUGH 4
-
-#define IVTV_MAX_PGM_INDEX (400)
-
-/* Default I2C SCL period in microseconds */
-#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20
-
-struct ivtv_options {
- int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */
- int cardtype; /* force card type on load */
- int tuner; /* set tuner on load */
- int radio; /* enable/disable radio */
- int newi2c; /* new I2C algorithm */
- int i2c_clock_period; /* period of SCL for I2C bus */
-};
-
-/* ivtv-specific mailbox template */
-struct ivtv_mailbox {
- u32 flags;
- u32 cmd;
- u32 retval;
- u32 timeout;
- u32 data[CX2341X_MBOX_MAX_DATA];
-};
-
-struct ivtv_api_cache {
- unsigned long last_jiffies; /* when last command was issued */
- u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */
-};
-
-struct ivtv_mailbox_data {
- volatile struct ivtv_mailbox __iomem *mbox;
- /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes.
- If the bit is set, then the corresponding mailbox is in use by the driver. */
- unsigned long busy;
- u8 max_mbox;
-};
-
-/* per-buffer bit flags */
-#define IVTV_F_B_NEED_BUF_SWAP (1 << 0) /* this buffer should be byte swapped */
-
-/* per-stream, s_flags */
-#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */
-#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */
-#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */
-
-#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */
-#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
-#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
-#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */
-#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */
-#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */
-
-#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */
-#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */
-
-/* per-ivtv, i_flags */
-#define IVTV_F_I_DMA 0 /* DMA in progress */
-#define IVTV_F_I_UDMA 1 /* UDMA in progress */
-#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */
-#define IVTV_F_I_SPEED_CHANGE 3 /* a speed change is in progress */
-#define IVTV_F_I_EOS 4 /* end of encoder stream reached */
-#define IVTV_F_I_RADIO_USER 5 /* the radio tuner is selected */
-#define IVTV_F_I_DIG_RST 6 /* reset digitizer */
-#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */
-#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */
-#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */
-#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */
-#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */
-#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */
-#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */
-#define IVTV_F_I_HAVE_WORK 15 /* used in the interrupt handler: there is work to be done */
-#define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */
-#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */
-#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */
-#define IVTV_F_I_PIO 19 /* PIO in progress */
-#define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */
-#define IVTV_F_I_INITED 21 /* set after first open */
-#define IVTV_F_I_FAILED 22 /* set if first open failed */
-
-/* Event notifications */
-#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */
-#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */
-#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */
-#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */
-
-/* Scatter-Gather array element, used in DMA transfers */
-struct ivtv_sg_element {
- __le32 src;
- __le32 dst;
- __le32 size;
-};
-
-struct ivtv_sg_host_element {
- u32 src;
- u32 dst;
- u32 size;
-};
-
-struct ivtv_user_dma {
- struct mutex lock;
- int page_count;
- struct page *map[IVTV_DMA_SG_OSD_ENT];
- /* Needed when dealing with highmem userspace buffers */
- struct page *bouncemap[IVTV_DMA_SG_OSD_ENT];
-
- /* Base Dev SG Array for cx23415/6 */
- struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT];
- dma_addr_t SG_handle;
- int SG_length;
-
- /* SG List of Buffers */
- struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT];
-};
-
-struct ivtv_dma_page_info {
- unsigned long uaddr;
- unsigned long first;
- unsigned long last;
- unsigned int offset;
- unsigned int tail;
- int page_count;
-};
-
-struct ivtv_buffer {
- struct list_head list;
- dma_addr_t dma_handle;
- unsigned short b_flags;
- unsigned short dma_xfer_cnt;
- char *buf;
- u32 bytesused;
- u32 readpos;
-};
-
-struct ivtv_queue {
- struct list_head list; /* the list of buffers in this queue */
- u32 buffers; /* number of buffers in this queue */
- u32 length; /* total number of bytes of available buffer space */
- u32 bytesused; /* total number of bytes used in this queue */
-};
-
-struct ivtv; /* forward reference */
-
-struct ivtv_stream {
- /* These first four fields are always set, even if the stream
- is not actually created. */
- struct video_device *vdev; /* NULL when stream not created */
- struct ivtv *itv; /* for ease of use */
- const char *name; /* name of the stream */
- int type; /* stream type */
- u32 caps; /* V4L2 capabilities */
-
- struct v4l2_fh *fh; /* pointer to the streaming filehandle */
- spinlock_t qlock; /* locks access to the queues */
- unsigned long s_flags; /* status flags, see above */
- int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */
- u32 pending_offset;
- u32 pending_backup;
- u64 pending_pts;
-
- u32 dma_offset;
- u32 dma_backup;
- u64 dma_pts;
-
- int subtype;
- wait_queue_head_t waitq;
- u32 dma_last_offset;
-
- /* Buffer Stats */
- u32 buffers;
- u32 buf_size;
- u32 buffers_stolen;
-
- /* Buffer Queues */
- struct ivtv_queue q_free; /* free buffers */
- struct ivtv_queue q_full; /* full buffers */
- struct ivtv_queue q_io; /* waiting for I/O */
- struct ivtv_queue q_dma; /* waiting for DMA */
- struct ivtv_queue q_predma; /* waiting for DMA */
-
- /* DMA xfer counter, buffers belonging to the same DMA
- xfer will have the same dma_xfer_cnt. */
- u16 dma_xfer_cnt;
-
- /* Base Dev SG Array for cx23415/6 */
- struct ivtv_sg_host_element *sg_pending;
- struct ivtv_sg_host_element *sg_processing;
- struct ivtv_sg_element *sg_dma;
- dma_addr_t sg_handle;
- int sg_pending_size;
- int sg_processing_size;
- int sg_processed;
-
- /* SG List of Buffers */
- struct scatterlist *SGlist;
-};
-
-struct ivtv_open_id {
- struct v4l2_fh fh;
- int type; /* stream type */
- int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */
- struct ivtv *itv;
-};
-
-static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh)
-{
- return container_of(fh, struct ivtv_open_id, fh);
-}
-
-struct yuv_frame_info
-{
- u32 update;
- s32 src_x;
- s32 src_y;
- u32 src_w;
- u32 src_h;
- s32 dst_x;
- s32 dst_y;
- u32 dst_w;
- u32 dst_h;
- s32 pan_x;
- s32 pan_y;
- u32 vis_w;
- u32 vis_h;
- u32 interlaced_y;
- u32 interlaced_uv;
- s32 tru_x;
- u32 tru_w;
- u32 tru_h;
- u32 offset_y;
- s32 lace_mode;
- u32 sync_field;
- u32 delay;
- u32 interlaced;
-};
-
-#define IVTV_YUV_MODE_INTERLACED 0x00
-#define IVTV_YUV_MODE_PROGRESSIVE 0x01
-#define IVTV_YUV_MODE_AUTO 0x02
-#define IVTV_YUV_MODE_MASK 0x03
-
-#define IVTV_YUV_SYNC_EVEN 0x00
-#define IVTV_YUV_SYNC_ODD 0x04
-#define IVTV_YUV_SYNC_MASK 0x04
-
-#define IVTV_YUV_BUFFERS 8
-
-struct yuv_playback_info
-{
- u32 reg_2834;
- u32 reg_2838;
- u32 reg_283c;
- u32 reg_2840;
- u32 reg_2844;
- u32 reg_2848;
- u32 reg_2854;
- u32 reg_285c;
- u32 reg_2864;
-
- u32 reg_2870;
- u32 reg_2874;
- u32 reg_2890;
- u32 reg_2898;
- u32 reg_289c;
-
- u32 reg_2918;
- u32 reg_291c;
- u32 reg_2920;
- u32 reg_2924;
- u32 reg_2928;
- u32 reg_292c;
- u32 reg_2930;
-
- u32 reg_2934;
-
- u32 reg_2938;
- u32 reg_293c;
- u32 reg_2940;
- u32 reg_2944;
- u32 reg_2948;
- u32 reg_294c;
- u32 reg_2950;
- u32 reg_2954;
- u32 reg_2958;
- u32 reg_295c;
- u32 reg_2960;
- u32 reg_2964;
- u32 reg_2968;
- u32 reg_296c;
-
- u32 reg_2970;
-
- int v_filter_1;
- int v_filter_2;
- int h_filter;
-
- u8 track_osd; /* Should yuv output track the OSD size & position */
-
- u32 osd_x_offset;
- u32 osd_y_offset;
-
- u32 osd_x_pan;
- u32 osd_y_pan;
-
- u32 osd_vis_w;
- u32 osd_vis_h;
-
- u32 osd_full_w;
- u32 osd_full_h;
-
- int decode_height;
-
- int lace_mode;
- int lace_threshold;
- int lace_sync_field;
-
- atomic_t next_dma_frame;
- atomic_t next_fill_frame;
-
- u32 yuv_forced_update;
- int update_frame;
-
- u8 fields_lapsed; /* Counter used when delaying a frame */
-
- struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS];
- struct yuv_frame_info old_frame_info;
- struct yuv_frame_info old_frame_info_args;
-
- void *blanking_ptr;
- dma_addr_t blanking_dmaptr;
-
- int stream_size;
-
- u8 draw_frame; /* PVR350 buffer to draw into */
- u8 max_frames_buffered; /* Maximum number of frames to buffer */
-
- struct v4l2_rect main_rect;
- u32 v4l2_src_w;
- u32 v4l2_src_h;
-
- u8 running; /* Have any frames been displayed */
-};
-
-#define IVTV_VBI_FRAMES 32
-
-/* VBI data */
-struct vbi_cc {
- u8 odd[2]; /* two-byte payload of odd field */
- u8 even[2]; /* two-byte payload of even field */;
-};
-
-struct vbi_vps {
- u8 data[5]; /* five-byte VPS payload */
-};
-
-struct vbi_info {
- /* VBI general data, does not change during streaming */
-
- u32 raw_decoder_line_size; /* raw VBI line size from digitizer */
- u8 raw_decoder_sav_odd_field; /* raw VBI Start Active Video digitizer code of odd field */
- u8 raw_decoder_sav_even_field; /* raw VBI Start Active Video digitizer code of even field */
- u32 sliced_decoder_line_size; /* sliced VBI line size from digitizer */
- u8 sliced_decoder_sav_odd_field; /* sliced VBI Start Active Video digitizer code of odd field */
- u8 sliced_decoder_sav_even_field; /* sliced VBI Start Active Video digitizer code of even field */
-
- u32 start[2]; /* start of first VBI line in the odd/even fields */
- u32 count; /* number of VBI lines per field */
- u32 raw_size; /* size of raw VBI line from the digitizer */
- u32 sliced_size; /* size of sliced VBI line from the digitizer */
-
- u32 dec_start; /* start in decoder memory of VBI re-insertion buffers */
- u32 enc_start; /* start in encoder memory of VBI capture buffers */
- u32 enc_size; /* size of VBI capture area */
- int fpi; /* number of VBI frames per interrupt */
-
- struct v4l2_format in; /* current VBI capture format */
- struct v4l2_sliced_vbi_format *sliced_in; /* convenience pointer to sliced struct in vbi.in union */
- int insert_mpeg; /* if non-zero, then embed VBI data in MPEG stream */
-
- /* Raw VBI compatibility hack */
-
- u32 frame; /* frame counter hack needed for backwards compatibility
- of old VBI software */
-
- /* Sliced VBI output data */
-
- struct vbi_cc cc_payload[256]; /* sliced VBI CC payload array: it is an array to
- prevent dropping CC data if they couldn't be
- processed fast enough */
- int cc_payload_idx; /* index in cc_payload */
- u8 cc_missing_cnt; /* counts number of frames without CC for passthrough mode */
- int wss_payload; /* sliced VBI WSS payload */
- u8 wss_missing_cnt; /* counts number of frames without WSS for passthrough mode */
- struct vbi_vps vps_payload; /* sliced VBI VPS payload */
-
- /* Sliced VBI capture data */
-
- struct v4l2_sliced_vbi_data sliced_data[36]; /* sliced VBI storage for VBI encoder stream */
- struct v4l2_sliced_vbi_data sliced_dec_data[36];/* sliced VBI storage for VBI decoder stream */
-
- /* VBI Embedding data */
-
- /* Buffer for VBI data inserted into MPEG stream.
- The first byte is a dummy byte that's never used.
- The next 16 bytes contain the MPEG header for the VBI data,
- the remainder is the actual VBI data.
- The max size accepted by the MPEG VBI reinsertion turns out
- to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
- where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
- a single line header byte and 2 * 18 is the number of VBI lines per frame.
-
- However, it seems that the data must be 1K aligned, so we have to
- pad the data until the 1 or 2 K boundary.
-
- This pointer array will allocate 2049 bytes to store each VBI frame. */
- u8 *sliced_mpeg_data[IVTV_VBI_FRAMES];
- u32 sliced_mpeg_size[IVTV_VBI_FRAMES];
- struct ivtv_buffer sliced_mpeg_buf; /* temporary buffer holding data from sliced_mpeg_data */
- u32 inserted_frame; /* index in sliced_mpeg_size of next sliced data
- to be inserted in the MPEG stream */
-};
-
-/* forward declaration of struct defined in ivtv-cards.h */
-struct ivtv_card;
-
-/* Struct to hold info about ivtv cards */
-struct ivtv {
- /* General fixed card data */
- struct pci_dev *pdev; /* PCI device */
- const struct ivtv_card *card; /* card information */
- const char *card_name; /* full name of the card */
- const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
- u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */
- u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */
- u8 nof_inputs; /* number of video inputs */
- u8 nof_audio_inputs; /* number of audio inputs */
- u32 v4l2_cap; /* V4L2 capabilities of card */
- u32 hw_flags; /* hardware description of the board */
- v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
- struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
- struct v4l2_subdev *sd_audio; /* controlling audio subdev */
- struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
- resource_size_t base_addr; /* PCI resource base address */
- volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
- volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
- volatile void __iomem *reg_mem; /* pointer to mapped registers */
- struct ivtv_options options; /* user options */
-
- struct v4l2_device v4l2_dev;
- struct cx2341x_handler cxhdl;
- struct {
- /* PTS/Frame count control cluster */
- struct v4l2_ctrl *ctrl_pts;
- struct v4l2_ctrl *ctrl_frame;
- };
- struct {
- /* Audio Playback control cluster */
- struct v4l2_ctrl *ctrl_audio_playback;
- struct v4l2_ctrl *ctrl_audio_multilingual_playback;
- };
- struct v4l2_ctrl_handler hdl_gpio;
- struct v4l2_subdev sd_gpio; /* GPIO sub-device */
- u16 instance;
-
- /* High-level state info */
- unsigned long i_flags; /* global ivtv flags */
- u8 is_50hz; /* 1 if the current capture standard is 50 Hz */
- u8 is_60hz /* 1 if the current capture standard is 60 Hz */;
- u8 is_out_50hz /* 1 if the current TV output standard is 50 Hz */;
- u8 is_out_60hz /* 1 if the current TV output standard is 60 Hz */;
- int output_mode; /* decoder output mode: NONE, MPG, YUV, UDMA YUV, passthrough */
- u32 audio_input; /* current audio input */
- u32 active_input; /* current video input */
- u32 active_output; /* current video output */
- v4l2_std_id std; /* current capture TV standard */
- v4l2_std_id std_out; /* current TV output standard */
- u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
- u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
-
- /* Locking */
- spinlock_t lock; /* lock access to this struct */
- struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
-
- /* Streams */
- int stream_buf_size[IVTV_MAX_STREAMS]; /* stream buffer size */
- struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* stream data */
- atomic_t capturing; /* count number of active capture streams */
- atomic_t decoding; /* count number of active decoding streams */
-
-
- /* Interrupts & DMA */
- u32 irqmask; /* active interrupts */
- u32 irq_rr_idx; /* round-robin stream index */
- struct kthread_worker irq_worker; /* kthread worker for PIO/YUV/VBI actions */
- struct task_struct *irq_worker_task; /* task for irq_worker */
- struct kthread_work irq_work; /* kthread work entry */
- spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
- int cur_dma_stream; /* index of current stream doing DMA (-1 if none) */
- int cur_pio_stream; /* index of current stream doing PIO (-1 if none) */
- u32 dma_data_req_offset; /* store offset in decoder memory of current DMA request */
- u32 dma_data_req_size; /* store size of current DMA request */
- int dma_retries; /* current DMA retry attempt */
- struct ivtv_user_dma udma; /* user based DMA for OSD */
- struct timer_list dma_timer; /* timer used to catch unfinished DMAs */
- u32 last_vsync_field; /* last seen vsync field */
- wait_queue_head_t dma_waitq; /* wake up when the current DMA is finished */
- wait_queue_head_t eos_waitq; /* wake up when EOS arrives */
- wait_queue_head_t event_waitq; /* wake up when the next decoder event arrives */
- wait_queue_head_t vsync_waitq; /* wake up when the next decoder vsync arrives */
-
-
- /* Mailbox */
- struct ivtv_mailbox_data enc_mbox; /* encoder mailboxes */
- struct ivtv_mailbox_data dec_mbox; /* decoder mailboxes */
- struct ivtv_api_cache api_cache[256]; /* cached API commands */
-
-
- /* I2C */
- struct i2c_adapter i2c_adap;
- struct i2c_algo_bit_data i2c_algo;
- struct i2c_client i2c_client;
- int i2c_state; /* i2c bit state */
- struct mutex i2c_bus_lock; /* lock i2c bus */
-
- struct IR_i2c_init_data ir_i2c_init_data;
-
- /* Program Index information */
- u32 pgm_info_offset; /* start of pgm info in encoder memory */
- u32 pgm_info_num; /* number of elements in the pgm cyclic buffer in encoder memory */
- u32 pgm_info_write_idx; /* last index written by the card that was transferred to pgm_info[] */
- u32 pgm_info_read_idx; /* last index in pgm_info read by the application */
- struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; /* filled from the pgm cyclic buffer on the card */
-
-
- /* Miscellaneous */
- u32 open_id; /* incremented each time an open occurs, is >= 1 */
- int search_pack_header; /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */
- int speed; /* current playback speed setting */
- u8 speed_mute_audio; /* 1 if audio should be muted when fast forward */
- u64 mpg_data_received; /* number of bytes received from the MPEG stream */
- u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */
- u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */
- unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */
- u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */
-
-
- /* VBI state info */
- struct vbi_info vbi; /* VBI-specific data */
-
-
- /* YUV playback */
- struct yuv_playback_info yuv_info; /* YUV playback data */
-
-
- /* OSD support */
- unsigned long osd_video_pbase;
- int osd_global_alpha_state; /* 1 = global alpha is on */
- int osd_local_alpha_state; /* 1 = local alpha is on */
- int osd_chroma_key_state; /* 1 = chroma-keying is on */
- u8 osd_global_alpha; /* current global alpha */
- u32 osd_chroma_key; /* current chroma key */
- struct v4l2_rect osd_rect; /* current OSD position and size */
- struct v4l2_rect main_rect; /* current Main window position and size */
- struct osd_info *osd_info; /* ivtvfb private OSD info */
- void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */
-};
-
-static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct ivtv, v4l2_dev);
-}
-
-/* Globals */
-extern int ivtv_first_minor;
-
-/*==============Prototypes==================*/
-
-/* Hardware/IRQ */
-void ivtv_set_irq_mask(struct ivtv *itv, u32 mask);
-void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask);
-
-/* try to set output mode, return current mode. */
-int ivtv_set_output_mode(struct ivtv *itv, int mode);
-
-/* return current output stream based on current mode */
-struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv);
-
-/* Return non-zero if a signal is pending */
-int ivtv_msleep_timeout(unsigned int msecs, int intr);
-
-/* Wait on queue, returns -EINTR if interrupted */
-int ivtv_waitq(wait_queue_head_t *waitq);
-
-/* Read Hauppauge eeprom */
-struct tveeprom; /* forward reference */
-void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv);
-
-/* First-open initialization: load firmware, init cx25840, etc. */
-int ivtv_init_on_first_open(struct ivtv *itv);
-
-/* Test if the current VBI mode is raw (1) or sliced (0) */
-static inline int ivtv_raw_vbi(const struct ivtv *itv)
-{
- return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
-}
-
-/* This is a PCI post thing, where if the pci register is not read, then
- the write doesn't always take effect right away. By reading back the
- register any pending PCI writes will be performed (in order), and so
- you can be sure that the writes are guaranteed to be done.
-
- Rarely needed, only in some timing sensitive cases.
- Apparently if this is not done some motherboards seem
- to kill the firmware and get into the broken state until computer is
- rebooted. */
-#define write_sync(val, reg) \
- do { writel(val, reg); readl(reg); } while (0)
-
-#define read_reg(reg) readl(itv->reg_mem + (reg))
-#define write_reg(val, reg) writel(val, itv->reg_mem + (reg))
-#define write_reg_sync(val, reg) \
- do { write_reg(val, reg); read_reg(reg); } while (0)
-
-#define read_enc(addr) readl(itv->enc_mem + (u32)(addr))
-#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr))
-#define write_enc_sync(val, addr) \
- do { write_enc(val, addr); read_enc(addr); } while (0)
-
-#define read_dec(addr) readl(itv->dec_mem + (u32)(addr))
-#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr))
-#define write_dec_sync(val, addr) \
- do { write_dec(val, addr); read_dec(addr); } while (0)
-
-/* Call the specified callback for all subdevs matching hw (if 0, then
- match them all). Ignore any errors. */
-#define ivtv_call_hw(itv, hw, o, f, args...) \
- do { \
- struct v4l2_subdev *__sd; \
- __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \
- !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
- } while (0)
-
-#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
-
-/* Call the specified callback for all subdevs matching hw (if 0, then
- match them all). If the callback returns an error other than 0 or
- -ENOIOCTLCMD, then return with that error code. */
-#define ivtv_call_hw_err(itv, hw, o, f, args...) \
-({ \
- struct v4l2_subdev *__sd; \
- __v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \
- !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \
-})
-
-#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
-
-#endif
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
deleted file mode 100644
index 9ff69b5a87e2..000000000000
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- file operation functions
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-fileops.h"
-#include "ivtv-i2c.h"
-#include "ivtv-queue.h"
-#include "ivtv-udma.h"
-#include "ivtv-irq.h"
-#include "ivtv-vbi.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-routing.h"
-#include "ivtv-streams.h"
-#include "ivtv-yuv.h"
-#include "ivtv-ioctl.h"
-#include "ivtv-cards.h"
-#include "ivtv-firmware.h"
-#include <media/v4l2-event.h>
-#include <media/saa7115.h>
-
-/* This function tries to claim the stream for a specific file descriptor.
- If no one else is using this stream then the stream is claimed and
- associated VBI streams are also automatically claimed.
- Possible error returns: -EBUSY if someone else has claimed
- the stream or 0 on success. */
-static int ivtv_claim_stream(struct ivtv_open_id *id, int type)
-{
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[type];
- struct ivtv_stream *s_vbi;
- int vbi_type;
-
- if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
- /* someone already claimed this stream */
- if (s->fh == &id->fh) {
- /* yes, this file descriptor did. So that's OK. */
- return 0;
- }
- if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI ||
- type == IVTV_ENC_STREAM_TYPE_VBI)) {
- /* VBI is handled already internally, now also assign
- the file descriptor to this stream for external
- reading of the stream. */
- s->fh = &id->fh;
- IVTV_DEBUG_INFO("Start Read VBI\n");
- return 0;
- }
- /* someone else is using this stream already */
- IVTV_DEBUG_INFO("Stream %d is busy\n", type);
- return -EBUSY;
- }
- s->fh = &id->fh;
- if (type == IVTV_DEC_STREAM_TYPE_VBI) {
- /* Enable reinsertion interrupt */
- ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
- }
-
- /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI,
- IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI
- (provided VBI insertion is on and sliced VBI is selected), for all
- other streams we're done */
- if (type == IVTV_DEC_STREAM_TYPE_MPG) {
- vbi_type = IVTV_DEC_STREAM_TYPE_VBI;
- } else if (type == IVTV_ENC_STREAM_TYPE_MPG &&
- itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) {
- vbi_type = IVTV_ENC_STREAM_TYPE_VBI;
- } else {
- return 0;
- }
- s_vbi = &itv->streams[vbi_type];
-
- if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) {
- /* Enable reinsertion interrupt */
- if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI)
- ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
- }
- /* mark that it is used internally */
- set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags);
- return 0;
-}
-
-/* This function releases a previously claimed stream. It will take into
- account associated VBI streams. */
-void ivtv_release_stream(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
- struct ivtv_stream *s_vbi;
-
- s->fh = NULL;
- if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) &&
- test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
- /* this stream is still in use internally */
- return;
- }
- if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
- IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name);
- return;
- }
-
- ivtv_flush_queues(s);
-
- /* disable reinsertion interrupt */
- if (s->type == IVTV_DEC_STREAM_TYPE_VBI)
- ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
-
- /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI,
- IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI,
- for all other streams we're done */
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
- s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
- else if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
- s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- else
- return;
-
- /* clear internal use flag */
- if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
- /* was already cleared */
- return;
- }
- if (s_vbi->fh) {
- /* VBI stream still claimed by a file descriptor */
- return;
- }
- /* disable reinsertion interrupt */
- if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI)
- ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT);
- clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags);
- ivtv_flush_queues(s_vbi);
-}
-
-static void ivtv_dualwatch(struct ivtv *itv)
-{
- struct v4l2_tuner vt;
- u32 new_stereo_mode;
- const u32 dual = 0x02;
-
- new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
- memset(&vt, 0, sizeof(vt));
- ivtv_call_all(itv, tuner, g_tuner, &vt);
- if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
- new_stereo_mode = dual;
-
- if (new_stereo_mode == itv->dualwatch_stereo_mode)
- return;
-
- IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
- itv->dualwatch_stereo_mode, new_stereo_mode);
- if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
- IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
-}
-
-static void ivtv_update_pgm_info(struct ivtv *itv)
-{
- u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24;
- int cnt;
- int i = 0;
-
- if (wr_idx >= itv->pgm_info_num) {
- IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num);
- return;
- }
- cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num;
- while (i < cnt) {
- int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
- struct v4l2_enc_idx_entry *e = itv->pgm_info + idx;
- u32 addr = itv->pgm_info_offset + 4 + idx * 24;
- const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1,
- V4L2_ENC_IDX_FRAME_B, -1, -1, -1 };
- // 1=I, 2=P, 4=B
-
- e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32);
- if (e->offset > itv->mpg_data_received) {
- break;
- }
- e->offset += itv->vbi_data_inserted;
- e->length = read_enc(addr);
- e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32);
- e->flags = mapping[read_enc(addr + 12) & 7];
- i++;
- }
- itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num;
-}
-
-static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err)
-{
- struct ivtv *itv = s->itv;
- struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- struct ivtv_buffer *buf;
- DEFINE_WAIT(wait);
-
- *err = 0;
- while (1) {
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG) {
- /* Process pending program info updates and pending VBI data */
- ivtv_update_pgm_info(itv);
-
- if (time_after(jiffies,
- itv->dualwatch_jiffies +
- msecs_to_jiffies(1000))) {
- itv->dualwatch_jiffies = jiffies;
- ivtv_dualwatch(itv);
- }
-
- if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
- !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
- while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) {
- /* byteswap and process VBI data */
- ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type);
- ivtv_enqueue(s_vbi, buf, &s_vbi->q_free);
- }
- }
- buf = &itv->vbi.sliced_mpeg_buf;
- if (buf->readpos != buf->bytesused) {
- return buf;
- }
- }
-
- /* do we have leftover data? */
- buf = ivtv_dequeue(s, &s->q_io);
- if (buf)
- return buf;
-
- /* do we have new data? */
- buf = ivtv_dequeue(s, &s->q_full);
- if (buf) {
- if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0)
- return buf;
- buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP;
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
- /* byteswap MPG data */
- ivtv_buf_swap(buf);
- else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) {
- /* byteswap and process VBI data */
- ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type);
- }
- return buf;
- }
-
- /* return if end of stream */
- if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
- IVTV_DEBUG_INFO("EOS %s\n", s->name);
- return NULL;
- }
-
- /* return if file was opened with O_NONBLOCK */
- if (non_block) {
- *err = -EAGAIN;
- return NULL;
- }
-
- /* wait for more data to arrive */
- mutex_unlock(&itv->serialize_lock);
- prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
- /* New buffers might have become available before we were added to the waitqueue */
- if (!s->q_full.buffers)
- schedule();
- finish_wait(&s->waitq, &wait);
- mutex_lock(&itv->serialize_lock);
- if (signal_pending(current)) {
- /* return if a signal was received */
- IVTV_DEBUG_INFO("User stopped %s\n", s->name);
- *err = -EINTR;
- return NULL;
- }
- }
-}
-
-static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv)
-{
- int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
-
- itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx];
- itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx];
- itv->vbi.sliced_mpeg_buf.readpos = 0;
-}
-
-static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf,
- char __user *ubuf, size_t ucount)
-{
- struct ivtv *itv = s->itv;
- size_t len = buf->bytesused - buf->readpos;
-
- if (len > ucount) len = ucount;
- if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG &&
- !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) {
- const char *start = buf->buf + buf->readpos;
- const char *p = start + 1;
- const u8 *q;
- u8 ch = itv->search_pack_header ? 0xba : 0xe0;
- int stuffing, i;
-
- while (start + len > p && (q = memchr(p, 0, start + len - p))) {
- p = q + 1;
- if ((char *)q + 15 >= buf->buf + buf->bytesused ||
- q[1] != 0 || q[2] != 1 || q[3] != ch) {
- continue;
- }
- if (!itv->search_pack_header) {
- if ((q[6] & 0xc0) != 0x80)
- continue;
- if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) ||
- ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) {
- ch = 0xba;
- itv->search_pack_header = 1;
- p = q + 9;
- }
- continue;
- }
- stuffing = q[13] & 7;
- /* all stuffing bytes must be 0xff */
- for (i = 0; i < stuffing; i++)
- if (q[14 + i] != 0xff)
- break;
- if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 &&
- q[14 + stuffing] == 0 && q[15 + stuffing] == 0 &&
- q[16 + stuffing] == 1) {
- itv->search_pack_header = 0;
- len = (char *)q - start;
- ivtv_setup_sliced_vbi_buf(itv);
- break;
- }
- }
- }
- if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
- IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name);
- return -EFAULT;
- }
- /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount,
- buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len,
- buf == &itv->vbi.sliced_mpeg_buf); */
- buf->readpos += len;
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf)
- itv->mpg_data_received += len;
- return len;
-}
-
-static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block)
-{
- struct ivtv *itv = s->itv;
- size_t tot_written = 0;
- int single_frame = 0;
-
- if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) {
- /* shouldn't happen */
- IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name);
- return -EIO;
- }
-
- /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should
- arrive one-by-one, so make sure we never output more than one VBI frame at a time */
- if (s->type == IVTV_DEC_STREAM_TYPE_VBI ||
- (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv)))
- single_frame = 1;
-
- for (;;) {
- struct ivtv_buffer *buf;
- int rc;
-
- buf = ivtv_get_buffer(s, non_block, &rc);
- /* if there is no data available... */
- if (buf == NULL) {
- /* if we got data, then return that regardless */
- if (tot_written)
- break;
- /* EOS condition */
- if (rc == 0) {
- clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
- clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- ivtv_release_stream(s);
- }
- /* set errno */
- return rc;
- }
- rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written);
- if (buf != &itv->vbi.sliced_mpeg_buf) {
- ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io);
- }
- else if (buf->readpos == buf->bytesused) {
- int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES;
- itv->vbi.sliced_mpeg_size[idx] = 0;
- itv->vbi.inserted_frame++;
- itv->vbi_data_inserted += buf->bytesused;
- }
- if (rc < 0)
- return rc;
- tot_written += rc;
-
- if (tot_written == tot_count || single_frame)
- break;
- }
- return tot_written;
-}
-
-static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count,
- loff_t *pos, int non_block)
-{
- ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0;
- struct ivtv *itv = s->itv;
-
- IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
- if (rc > 0)
- pos += rc;
- return rc;
-}
-
-int ivtv_start_capture(struct ivtv_open_id *id)
-{
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- struct ivtv_stream *s_vbi;
-
- if (s->type == IVTV_ENC_STREAM_TYPE_RAD ||
- s->type == IVTV_DEC_STREAM_TYPE_MPG ||
- s->type == IVTV_DEC_STREAM_TYPE_YUV ||
- s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
- /* you cannot read from these stream types. */
- return -EPERM;
- }
-
- /* Try to claim this stream. */
- if (ivtv_claim_stream(id, s->type))
- return -EBUSY;
-
- /* This stream does not need to start capturing */
- if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
- set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- return 0;
- }
-
- /* If capture is already in progress, then we also have to
- do nothing extra. */
- if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
- set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- return 0;
- }
-
- /* Start VBI capture if required */
- s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
- test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
- !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
- /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed
- automatically when the MPG stream is claimed.
- We only need to start the VBI capturing. */
- if (ivtv_start_v4l2_encode_stream(s_vbi)) {
- IVTV_DEBUG_WARN("VBI capture start failed\n");
-
- /* Failure, clean up and return an error */
- clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
- /* also releases the associated VBI stream */
- ivtv_release_stream(s);
- return -EIO;
- }
- IVTV_DEBUG_INFO("VBI insertion started\n");
- }
-
- /* Tell the card to start capturing */
- if (!ivtv_start_v4l2_encode_stream(s)) {
- /* We're done */
- set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- /* Resume a possibly paused encoder */
- if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
- ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
- return 0;
- }
-
- /* failure, clean up */
- IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
-
- /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released
- automatically when the MPG stream is released.
- We only need to stop the VBI capturing. */
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG &&
- test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) {
- ivtv_stop_v4l2_encode_stream(s_vbi, 0);
- clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags);
- }
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
- ivtv_release_stream(s);
- return -EIO;
-}
-
-ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos)
-{
- struct ivtv_open_id *id = fh2id(filp->private_data);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- int rc;
-
- IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
-
- rc = ivtv_start_capture(id);
- if (rc)
- return rc;
- return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
-}
-
-int ivtv_start_decoding(struct ivtv_open_id *id, int speed)
-{
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- int rc;
-
- if (atomic_read(&itv->decoding) == 0) {
- if (ivtv_claim_stream(id, s->type)) {
- /* someone else is using this stream already */
- IVTV_DEBUG_WARN("start decode, stream already claimed\n");
- return -EBUSY;
- }
- rc = ivtv_start_v4l2_decode_stream(s, 0);
- if (rc < 0) {
- if (rc == -EAGAIN)
- rc = ivtv_start_v4l2_decode_stream(s, 0);
- if (rc < 0)
- return rc;
- }
- }
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
- return ivtv_set_speed(itv, speed);
- return 0;
-}
-
-ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos)
-{
- struct ivtv_open_id *id = fh2id(filp->private_data);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- struct yuv_playback_info *yi = &itv->yuv_info;
- struct ivtv_buffer *buf;
- struct ivtv_queue q;
- int bytes_written = 0;
- int mode;
- int rc;
- DEFINE_WAIT(wait);
-
- IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name);
-
- if (s->type != IVTV_DEC_STREAM_TYPE_MPG &&
- s->type != IVTV_DEC_STREAM_TYPE_YUV &&
- s->type != IVTV_DEC_STREAM_TYPE_VOUT)
- /* not decoder streams */
- return -EPERM;
-
- /* Try to claim this stream */
- if (ivtv_claim_stream(id, s->type))
- return -EBUSY;
-
- /* This stream does not need to start any decoding */
- if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) {
- int elems = count / sizeof(struct v4l2_sliced_vbi_data);
-
- set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- return ivtv_write_vbi_from_user(itv,
- (const struct v4l2_sliced_vbi_data __user *)user_buf, elems);
- }
-
- mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV;
-
- if (ivtv_set_output_mode(itv, mode) != mode) {
- ivtv_release_stream(s);
- return -EBUSY;
- }
- ivtv_queue_init(&q);
- set_bit(IVTV_F_S_APPL_IO, &s->s_flags);
-
- /* Start decoder (returns 0 if already started) */
- rc = ivtv_start_decoding(id, itv->speed);
- if (rc) {
- IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name);
-
- /* failure, clean up */
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
- clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- return rc;
- }
-
-retry:
- /* If possible, just DMA the entire frame - Check the data transfer size
- since we may get here before the stream has been fully set-up */
- if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) {
- while (count >= itv->dma_data_req_size) {
- rc = ivtv_yuv_udma_stream_frame(itv, (void __user *)user_buf);
-
- if (rc < 0)
- return rc;
-
- bytes_written += itv->dma_data_req_size;
- user_buf += itv->dma_data_req_size;
- count -= itv->dma_data_req_size;
- }
- if (count == 0) {
- IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
- return bytes_written;
- }
- }
-
- for (;;) {
- /* Gather buffers */
- while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io)))
- ivtv_enqueue(s, buf, &q);
- while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) {
- ivtv_enqueue(s, buf, &q);
- }
- if (q.buffers)
- break;
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- mutex_unlock(&itv->serialize_lock);
- prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
- /* New buffers might have become free before we were added to the waitqueue */
- if (!s->q_free.buffers)
- schedule();
- finish_wait(&s->waitq, &wait);
- mutex_lock(&itv->serialize_lock);
- if (signal_pending(current)) {
- IVTV_DEBUG_INFO("User stopped %s\n", s->name);
- return -EINTR;
- }
- }
-
- /* copy user data into buffers */
- while ((buf = ivtv_dequeue(s, &q))) {
- /* yuv is a pain. Don't copy more data than needed for a single
- frame, otherwise we lose sync with the incoming stream */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
- yi->stream_size + count > itv->dma_data_req_size)
- rc = ivtv_buf_copy_from_user(s, buf, user_buf,
- itv->dma_data_req_size - yi->stream_size);
- else
- rc = ivtv_buf_copy_from_user(s, buf, user_buf, count);
-
- /* Make sure we really got all the user data */
- if (rc < 0) {
- ivtv_queue_move(s, &q, NULL, &s->q_free, 0);
- return rc;
- }
- user_buf += rc;
- count -= rc;
- bytes_written += rc;
-
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
- yi->stream_size += rc;
- /* If we have a complete yuv frame, break loop now */
- if (yi->stream_size == itv->dma_data_req_size) {
- ivtv_enqueue(s, buf, &s->q_full);
- yi->stream_size = 0;
- break;
- }
- }
-
- if (buf->bytesused != s->buf_size) {
- /* incomplete, leave in q_io for next time */
- ivtv_enqueue(s, buf, &s->q_io);
- break;
- }
- /* Byteswap MPEG buffer */
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG)
- ivtv_buf_swap(buf);
- ivtv_enqueue(s, buf, &s->q_full);
- }
-
- if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) {
- if (s->q_full.length >= itv->dma_data_req_size) {
- int got_sig;
-
- if (mode == OUT_YUV)
- ivtv_yuv_setup_stream_frame(itv);
-
- mutex_unlock(&itv->serialize_lock);
- prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
- while (!(got_sig = signal_pending(current)) &&
- test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) {
- schedule();
- }
- finish_wait(&itv->dma_waitq, &wait);
- mutex_lock(&itv->serialize_lock);
- if (got_sig) {
- IVTV_DEBUG_INFO("User interrupted %s\n", s->name);
- return -EINTR;
- }
-
- clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
- ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
- ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1);
- }
- }
- /* more user data is available, wait until buffers become free
- to transfer the rest. */
- if (count && !(filp->f_flags & O_NONBLOCK))
- goto retry;
- IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused);
- return bytes_written;
-}
-
-unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
-{
- struct ivtv_open_id *id = fh2id(filp->private_data);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- int res = 0;
-
- /* add stream's waitq to the poll list */
- IVTV_DEBUG_HI_FILE("Decoder poll\n");
-
- /* If there are subscribed events, then only use the new event
- API instead of the old video.h based API. */
- if (!list_empty(&id->fh.subscribed)) {
- poll_wait(filp, &id->fh.wait, wait);
- /* Turn off the old-style vsync events */
- clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
- if (v4l2_event_pending(&id->fh))
- res = POLLPRI;
- } else {
- /* This is the old-style API which is here only for backwards
- compatibility. */
- poll_wait(filp, &s->waitq, wait);
- set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
- if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) ||
- test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
- res = POLLPRI;
- }
-
- /* Allow write if buffers are available for writing */
- if (s->q_free.buffers)
- res |= POLLOUT | POLLWRNORM;
- return res;
-}
-
-unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait)
-{
- unsigned long req_events = poll_requested_events(wait);
- struct ivtv_open_id *id = fh2id(filp->private_data);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
- int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
- unsigned res = 0;
-
- /* Start a capture if there is none */
- if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) &&
- (req_events & (POLLIN | POLLRDNORM))) {
- int rc;
-
- rc = ivtv_start_capture(id);
- if (rc) {
- IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n",
- s->name, rc);
- return POLLERR;
- }
- IVTV_DEBUG_FILE("Encoder poll started capture\n");
- }
-
- /* add stream's waitq to the poll list */
- IVTV_DEBUG_HI_FILE("Encoder poll\n");
- poll_wait(filp, &s->waitq, wait);
- if (v4l2_event_pending(&id->fh))
- res |= POLLPRI;
- else
- poll_wait(filp, &id->fh.wait, wait);
-
- if (s->q_full.length || s->q_io.length)
- return res | POLLIN | POLLRDNORM;
- if (eof)
- return res | POLLHUP;
- return res;
-}
-
-void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end)
-{
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
-
- IVTV_DEBUG_FILE("close() of %s\n", s->name);
-
- /* 'Unclaim' this stream */
-
- /* Stop capturing */
- if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
- struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
-
- IVTV_DEBUG_INFO("close stopping capture\n");
- /* Special case: a running VBI capture for VBI insertion
- in the mpeg stream. Need to stop that too. */
- if (id->type == IVTV_ENC_STREAM_TYPE_MPG &&
- test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) &&
- !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) {
- IVTV_DEBUG_INFO("close stopping embedded VBI capture\n");
- ivtv_stop_v4l2_encode_stream(s_vbi, 0);
- }
- if ((id->type == IVTV_DEC_STREAM_TYPE_VBI ||
- id->type == IVTV_ENC_STREAM_TYPE_VBI) &&
- test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) {
- /* Also used internally, don't stop capturing */
- s->fh = NULL;
- }
- else {
- ivtv_stop_v4l2_encode_stream(s, gop_end);
- }
- }
- if (!gop_end) {
- clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
- ivtv_release_stream(s);
- }
-}
-
-static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts)
-{
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
-
- IVTV_DEBUG_FILE("close() of %s\n", s->name);
-
- if (id->type == IVTV_DEC_STREAM_TYPE_YUV &&
- test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) {
- /* Restore registers we've changed & clean up any mess */
- ivtv_yuv_close(itv);
- }
-
- /* Stop decoding */
- if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
- IVTV_DEBUG_INFO("close stopping decode\n");
-
- ivtv_stop_v4l2_decode_stream(s, flags, pts);
- itv->output_mode = OUT_NONE;
- }
- clear_bit(IVTV_F_S_APPL_IO, &s->s_flags);
- clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
-
- if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames)
- itv->output_mode = OUT_NONE;
-
- itv->speed = 0;
- clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
- ivtv_release_stream(s);
-}
-
-int ivtv_v4l2_close(struct file *filp)
-{
- struct v4l2_fh *fh = filp->private_data;
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
-
- IVTV_DEBUG_FILE("close %s\n", s->name);
-
- /* Stop radio */
- if (id->type == IVTV_ENC_STREAM_TYPE_RAD &&
- v4l2_fh_is_singular_file(filp)) {
- /* Closing radio device, return to TV mode */
- ivtv_mute(itv);
- /* Mark that the radio is no longer in use */
- clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
- /* Switch tuner to TV */
- ivtv_call_all(itv, core, s_std, itv->std);
- /* Select correct audio input (i.e. TV tuner or Line in) */
- ivtv_audio_set_io(itv);
- if (itv->hw_flags & IVTV_HW_SAA711X) {
- ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
- SAA7115_FREQ_32_11_MHZ, 0);
- }
- if (atomic_read(&itv->capturing) > 0) {
- /* Undo video mute */
- ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
- (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
- }
- /* Done! Unmute and continue. */
- ivtv_unmute(itv);
- }
-
- v4l2_fh_del(fh);
- v4l2_fh_exit(fh);
-
- /* Easy case first: this stream was never claimed by us */
- if (s->fh != &id->fh) {
- kfree(id);
- return 0;
- }
-
- /* 'Unclaim' this stream */
-
- if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
- struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
-
- ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
-
- /* If all output streams are closed, and if the user doesn't have
- IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
- if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) {
- /* disable CC on TV-out */
- ivtv_disable_cc(itv);
- }
- } else {
- ivtv_stop_capture(id, 0);
- }
- kfree(id);
- return 0;
-}
-
-int ivtv_v4l2_open(struct file *filp)
-{
- struct video_device *vdev = video_devdata(filp);
- struct ivtv_stream *s = video_get_drvdata(vdev);
- struct ivtv *itv = s->itv;
- struct ivtv_open_id *item;
- int res = 0;
-
- IVTV_DEBUG_FILE("open %s\n", s->name);
-
- if (ivtv_init_on_first_open(itv)) {
- IVTV_ERR("Failed to initialize on device %s\n",
- video_device_node_name(vdev));
- return -ENXIO;
- }
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- /* Unless ivtv_fw_debug is set, error out if firmware dead. */
- if (ivtv_fw_debug) {
- IVTV_WARN("Opening %s with dead firmware lockout disabled\n",
- video_device_node_name(vdev));
- IVTV_WARN("Selected firmware errors will be ignored\n");
- } else {
-#else
- if (1) {
-#endif
- res = ivtv_firmware_check(itv, "ivtv_serialized_open");
- if (res == -EAGAIN)
- res = ivtv_firmware_check(itv, "ivtv_serialized_open");
- if (res < 0)
- return -EIO;
- }
-
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG &&
- test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags))
- return -EBUSY;
-
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV &&
- test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags))
- return -EBUSY;
-
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
- if (read_reg(0x82c) == 0) {
- IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n");
- /* return -ENODEV; */
- }
- ivtv_udma_alloc(itv);
- }
-
- /* Allocate memory */
- item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
- if (NULL == item) {
- IVTV_DEBUG_WARN("nomem on v4l2 open\n");
- return -ENOMEM;
- }
- v4l2_fh_init(&item->fh, s->vdev);
- item->itv = itv;
- item->type = s->type;
-
- filp->private_data = &item->fh;
- v4l2_fh_add(&item->fh);
-
- if (item->type == IVTV_ENC_STREAM_TYPE_RAD &&
- v4l2_fh_is_singular_file(filp)) {
- if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
- if (atomic_read(&itv->capturing) > 0) {
- /* switching to radio while capture is
- in progress is not polite */
- v4l2_fh_del(&item->fh);
- v4l2_fh_exit(&item->fh);
- kfree(item);
- return -EBUSY;
- }
- }
- /* Mark that the radio is being used. */
- set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
- /* We have the radio */
- ivtv_mute(itv);
- /* Switch tuner to radio */
- ivtv_call_all(itv, tuner, s_radio);
- /* Select the correct audio input (i.e. radio tuner) */
- ivtv_audio_set_io(itv);
- if (itv->hw_flags & IVTV_HW_SAA711X) {
- ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq,
- SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL);
- }
- /* Done! Unmute and continue. */
- ivtv_unmute(itv);
- }
-
- /* YUV or MPG Decoding Mode? */
- if (s->type == IVTV_DEC_STREAM_TYPE_MPG) {
- clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
- } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) {
- set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags);
- /* For yuv, we need to know the dma size before we start */
- itv->dma_data_req_size =
- 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
- itv->yuv_info.stream_size = 0;
- }
- return 0;
-}
-
-void ivtv_mute(struct ivtv *itv)
-{
- if (atomic_read(&itv->capturing))
- ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1);
- IVTV_DEBUG_INFO("Mute\n");
-}
-
-void ivtv_unmute(struct ivtv *itv)
-{
- if (atomic_read(&itv->capturing)) {
- ivtv_msleep_timeout(100, 0);
- ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
- ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0);
- }
- IVTV_DEBUG_INFO("Unmute\n");
-}
diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h
deleted file mode 100644
index 049a2923965d..000000000000
--- a/drivers/media/video/ivtv/ivtv-fileops.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- file operation functions
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef IVTV_FILEOPS_H
-#define IVTV_FILEOPS_H
-
-/* Testing/Debugging */
-int ivtv_v4l2_open(struct file *filp);
-ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count,
- loff_t * pos);
-ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count,
- loff_t * pos);
-int ivtv_v4l2_close(struct file *filp);
-unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait);
-unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait);
-int ivtv_start_capture(struct ivtv_open_id *id);
-void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end);
-int ivtv_start_decoding(struct ivtv_open_id *id, int speed);
-void ivtv_mute(struct ivtv *itv);
-void ivtv_unmute(struct ivtv *itv);
-
-/* Utilities */
-
-/* Release a previously claimed stream. */
-void ivtv_release_stream(struct ivtv_stream *s);
-
-#endif
diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
deleted file mode 100644
index 02c5adebf517..000000000000
--- a/drivers/media/video/ivtv/ivtv-firmware.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- ivtv firmware functions.
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-firmware.h"
-#include "ivtv-yuv.h"
-#include "ivtv-ioctl.h"
-#include "ivtv-cards.h"
-#include <linux/firmware.h>
-#include <media/saa7127.h>
-
-#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE
-#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6
-#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB
-#define IVTV_CMD_VDM_STOP 0x00000000
-#define IVTV_CMD_AO_STOP 0x00000005
-#define IVTV_CMD_APU_PING 0x00000000
-#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE
-#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE
-#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF
-#define IVTV_CMD_SPU_STOP 0x00000001
-#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A
-#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640
-#define IVTV_SDRAM_SLEEPTIME 600
-
-#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg"
-#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024)
-
-/* Encoder/decoder firmware sizes */
-#define IVTV_FW_ENC_SIZE (376836)
-#define IVTV_FW_DEC_SIZE (256*1024)
-
-static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size)
-{
- const struct firmware *fw = NULL;
- int retries = 3;
-
-retry:
- if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) {
- int i;
- volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
- const u32 *src = (const u32 *)fw->data;
-
- if (fw->size != size) {
- /* Due to race conditions in firmware loading (esp. with udev <0.95)
- the wrong file was sometimes loaded. So we check filesizes to
- see if at least the right-sized file was loaded. If not, then we
- retry. */
- IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size);
- release_firmware(fw);
- retries--;
- goto retry;
- }
- for (i = 0; i < fw->size; i += 4) {
- /* no need for endianness conversion on the ppc */
- __raw_writel(*src, dst);
- dst++;
- src++;
- }
- IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size);
- release_firmware(fw);
- return size;
- }
- IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size);
- IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n");
- return -ENOMEM;
-}
-
-void ivtv_halt_firmware(struct ivtv *itv)
-{
- IVTV_DEBUG_INFO("Preparing for firmware halt.\n");
- if (itv->has_cx23415 && itv->dec_mbox.mbox)
- ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0);
- if (itv->enc_mbox.mbox)
- ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0);
-
- ivtv_msleep_timeout(10, 0);
- itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL;
-
- IVTV_DEBUG_INFO("Stopping VDM\n");
- write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM);
-
- IVTV_DEBUG_INFO("Stopping AO\n");
- write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO);
-
- IVTV_DEBUG_INFO("pinging (?) APU\n");
- write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU);
-
- IVTV_DEBUG_INFO("Stopping VPU\n");
- if (!itv->has_cx23415)
- write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU);
- else
- write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU);
-
- IVTV_DEBUG_INFO("Resetting Hw Blocks\n");
- write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS);
-
- IVTV_DEBUG_INFO("Stopping SPU\n");
- write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU);
-
- ivtv_msleep_timeout(10, 0);
-
- IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n");
- write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE);
-
- IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n");
- write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH);
-
- if (itv->has_cx23415) {
- IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n");
- write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE);
-
- IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n");
- write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH);
- }
-
- IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME);
- ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0);
-}
-
-void ivtv_firmware_versions(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
-
- /* Encoder */
- ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0);
- IVTV_INFO("Encoder revision: 0x%08x\n", data[0]);
-
- if (data[0] != 0x02060039)
- IVTV_WARN("Recommended firmware version is 0x02060039.\n");
-
- if (itv->has_cx23415) {
- /* Decoder */
- ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0);
- IVTV_INFO("Decoder revision: 0x%08x\n", data[0]);
- }
-}
-
-static int ivtv_firmware_copy(struct ivtv *itv)
-{
- IVTV_DEBUG_INFO("Loading encoder image\n");
- if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME,
- itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) {
- IVTV_DEBUG_WARN("failed loading encoder firmware\n");
- return -3;
- }
- if (!itv->has_cx23415)
- return 0;
-
- IVTV_DEBUG_INFO("Loading decoder image\n");
- if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME,
- itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) {
- IVTV_DEBUG_WARN("failed loading decoder firmware\n");
- return -1;
- }
- return 0;
-}
-
-static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size)
-{
- int i;
-
- /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte
- address boundary */
- for (i = 0; i < size; i += 0x100) {
- if (readl(mem + i) == 0x12345678 &&
- readl(mem + i + 4) == 0x34567812 &&
- readl(mem + i + 8) == 0x56781234 &&
- readl(mem + i + 12) == 0x78123456) {
- return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16);
- }
- }
- return NULL;
-}
-
-int ivtv_firmware_init(struct ivtv *itv)
-{
- int err;
-
- ivtv_halt_firmware(itv);
-
- /* load firmware */
- err = ivtv_firmware_copy(itv);
- if (err) {
- IVTV_DEBUG_WARN("Error %d loading firmware\n", err);
- return err;
- }
-
- /* start firmware */
- write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU);
- ivtv_msleep_timeout(100, 0);
- if (itv->has_cx23415)
- write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU);
- else
- write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU);
- ivtv_msleep_timeout(100, 0);
-
- /* find mailboxes and ping firmware */
- itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE);
- if (itv->enc_mbox.mbox == NULL)
- IVTV_ERR("Encoder mailbox not found\n");
- else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) {
- IVTV_ERR("Encoder firmware dead!\n");
- itv->enc_mbox.mbox = NULL;
- }
- if (itv->enc_mbox.mbox == NULL)
- return -ENODEV;
-
- if (!itv->has_cx23415)
- return 0;
-
- itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE);
- if (itv->dec_mbox.mbox == NULL) {
- IVTV_ERR("Decoder mailbox not found\n");
- } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) {
- IVTV_ERR("Decoder firmware dead!\n");
- itv->dec_mbox.mbox = NULL;
- } else {
- /* Firmware okay, so check yuv output filter table */
- ivtv_yuv_filter_check(itv);
- }
- return itv->dec_mbox.mbox ? 0 : -ENODEV;
-}
-
-void ivtv_init_mpeg_decoder(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- long readbytes;
- volatile u8 __iomem *mem_offset;
-
- data[0] = 0;
- data[1] = itv->cxhdl.width; /* YUV source width */
- data[2] = itv->cxhdl.height;
- data[3] = itv->cxhdl.audio_properties; /* Audio settings to use,
- bitmap. see docs. */
- if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) {
- IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n");
- return;
- }
-
- if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) {
- IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n");
- return;
- }
- ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
- mem_offset = itv->dec_mem + data[1];
-
- if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME,
- mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) {
- IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n",
- IVTV_DECODE_INIT_MPEG_FILENAME);
- } else {
- ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0);
- ivtv_msleep_timeout(100, 0);
- }
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1);
-}
-
-/* Try to restart the card & restore previous settings */
-int ivtv_firmware_restart(struct ivtv *itv)
-{
- int rc = 0;
- v4l2_std_id std;
-
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- /* Display test image during restart */
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
- SAA7127_INPUT_TYPE_TEST_IMAGE,
- itv->card->video_outputs[itv->active_output].video_output,
- 0);
-
- mutex_lock(&itv->udma.lock);
-
- rc = ivtv_firmware_init(itv);
- if (rc) {
- mutex_unlock(&itv->udma.lock);
- return rc;
- }
-
- /* Allow settings to reload */
- ivtv_mailbox_cache_invalidate(itv);
-
- /* Restore encoder video standard */
- std = itv->std;
- itv->std = 0;
- ivtv_s_std_enc(itv, &std);
-
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- ivtv_init_mpeg_decoder(itv);
-
- /* Restore decoder video standard */
- std = itv->std_out;
- itv->std_out = 0;
- ivtv_s_std_dec(itv, &std);
-
- /* Restore framebuffer if active */
- if (itv->ivtvfb_restore)
- itv->ivtvfb_restore(itv);
-
- /* Restore alpha settings */
- ivtv_set_osd_alpha(itv);
-
- /* Restore normal output */
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
- SAA7127_INPUT_TYPE_NORMAL,
- itv->card->video_outputs[itv->active_output].video_output,
- 0);
- }
-
- mutex_unlock(&itv->udma.lock);
- return rc;
-}
-
-/* Check firmware running state. The checks fall through
- allowing multiple failures to be logged. */
-int ivtv_firmware_check(struct ivtv *itv, char *where)
-{
- int res = 0;
-
- /* Check encoder is still running */
- if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) {
- IVTV_WARN("Encoder has died : %s\n", where);
- res = -1;
- }
-
- /* Also check audio. Only check if not in use & encoder is okay */
- if (!res && !atomic_read(&itv->capturing) &&
- (!atomic_read(&itv->decoding) ||
- (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV,
- &itv->i_flags)))) {
-
- if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) {
- IVTV_WARN("Audio has died (Encoder OK) : %s\n", where);
- res = -2;
- }
- }
-
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- /* Second audio check. Skip if audio already failed */
- if (res != -2 && read_dec(0x100) != read_dec(0x104)) {
- /* Wait & try again to be certain. */
- ivtv_msleep_timeout(14, 0);
- if (read_dec(0x100) != read_dec(0x104)) {
- IVTV_WARN("Audio has died (Decoder) : %s\n",
- where);
- res = -1;
- }
- }
-
- /* Check decoder is still running */
- if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) {
- IVTV_WARN("Decoder has died : %s\n", where);
- res = -1;
- }
- }
-
- /* If something failed & currently idle, try to reload */
- if (res && !atomic_read(&itv->capturing) &&
- !atomic_read(&itv->decoding)) {
- IVTV_INFO("Detected in %s that firmware had failed - "
- "Reloading\n", where);
- res = ivtv_firmware_restart(itv);
- /*
- * Even if restarted ok, still signal a problem had occurred.
- * The caller can come through this function again to check
- * if things are really ok after the restart.
- */
- if (!res) {
- IVTV_INFO("Firmware restart okay\n");
- res = -EAGAIN;
- } else {
- IVTV_INFO("Firmware restart failed\n");
- }
- } else if (res) {
- res = -EIO;
- }
-
- return res;
-}
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
deleted file mode 100644
index 32a591062d0b..000000000000
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ /dev/null
@@ -1,1899 +0,0 @@
-/*
- ioctl system call
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-version.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-i2c.h"
-#include "ivtv-queue.h"
-#include "ivtv-fileops.h"
-#include "ivtv-vbi.h"
-#include "ivtv-routing.h"
-#include "ivtv-streams.h"
-#include "ivtv-yuv.h"
-#include "ivtv-ioctl.h"
-#include "ivtv-gpio.h"
-#include "ivtv-controls.h"
-#include "ivtv-cards.h"
-#include <media/saa7127.h>
-#include <media/tveeprom.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-event.h>
-#include <linux/dvb/audio.h>
-
-u16 ivtv_service2vbi(int type)
-{
- switch (type) {
- case V4L2_SLICED_TELETEXT_B:
- return IVTV_SLICED_TYPE_TELETEXT_B;
- case V4L2_SLICED_CAPTION_525:
- return IVTV_SLICED_TYPE_CAPTION_525;
- case V4L2_SLICED_WSS_625:
- return IVTV_SLICED_TYPE_WSS_625;
- case V4L2_SLICED_VPS:
- return IVTV_SLICED_TYPE_VPS;
- default:
- return 0;
- }
-}
-
-static int valid_service_line(int field, int line, int is_pal)
-{
- return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
- (!is_pal && line >= 10 && line < 22);
-}
-
-static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
-{
- u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
- int i;
-
- set = set & valid_set;
- if (set == 0 || !valid_service_line(field, line, is_pal)) {
- return 0;
- }
- if (!is_pal) {
- if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
- return V4L2_SLICED_CAPTION_525;
- }
- else {
- if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
- return V4L2_SLICED_VPS;
- if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
- return V4L2_SLICED_WSS_625;
- if (line == 23)
- return 0;
- }
- for (i = 0; i < 32; i++) {
- if ((1 << i) & set)
- return 1 << i;
- }
- return 0;
-}
-
-void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
-{
- u16 set = fmt->service_set;
- int f, l;
-
- fmt->service_set = 0;
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
- }
- }
-}
-
-static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
-{
- int f, l;
-
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
- }
- }
-}
-
-u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt)
-{
- int f, l;
- u16 set = 0;
-
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- set |= fmt->service_lines[f][l];
- }
- }
- return set;
-}
-
-void ivtv_set_osd_alpha(struct ivtv *itv)
-{
- ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3,
- itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state);
- ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key);
-}
-
-int ivtv_set_speed(struct ivtv *itv, int speed)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- int single_step = (speed == 1 || speed == -1);
- DEFINE_WAIT(wait);
-
- if (speed == 0) speed = 1000;
-
- /* No change? */
- if (speed == itv->speed && !single_step)
- return 0;
-
- if (single_step && (speed < 0) == (itv->speed < 0)) {
- /* Single step video and no need to change direction */
- ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
- itv->speed = speed;
- return 0;
- }
- if (single_step)
- /* Need to change direction */
- speed = speed < 0 ? -1000 : 1000;
-
- data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0;
- data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0;
- data[1] = (speed < 0);
- data[2] = speed < 0 ? 3 : 7;
- data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames);
- data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0;
- data[5] = 0;
- data[6] = 0;
-
- if (speed == 1500 || speed == -1500) data[0] |= 1;
- else if (speed == 2000 || speed == -2000) data[0] |= 2;
- else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed);
- else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed);
-
- /* If not decoding, just change speed setting */
- if (atomic_read(&itv->decoding) > 0) {
- int got_sig = 0;
-
- /* Stop all DMA and decoding activity */
- ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0);
-
- /* Wait for any DMA to finish */
- mutex_unlock(&itv->serialize_lock);
- prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE);
- while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
- got_sig = signal_pending(current);
- if (got_sig)
- break;
- got_sig = 0;
- schedule();
- }
- finish_wait(&itv->dma_waitq, &wait);
- mutex_lock(&itv->serialize_lock);
- if (got_sig)
- return -EINTR;
-
- /* Change Speed safely */
- ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data);
- IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
- data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
- }
- if (single_step) {
- speed = (speed < 0) ? -1 : 1;
- ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
- }
- itv->speed = speed;
- return 0;
-}
-
-static int ivtv_validate_speed(int cur_speed, int new_speed)
-{
- int fact = new_speed < 0 ? -1 : 1;
- int s;
-
- if (cur_speed == 0)
- cur_speed = 1000;
- if (new_speed < 0)
- new_speed = -new_speed;
- if (cur_speed < 0)
- cur_speed = -cur_speed;
-
- if (cur_speed <= new_speed) {
- if (new_speed > 1500)
- return fact * 2000;
- if (new_speed > 1000)
- return fact * 1500;
- }
- else {
- if (new_speed >= 2000)
- return fact * 2000;
- if (new_speed >= 1500)
- return fact * 1500;
- if (new_speed >= 1000)
- return fact * 1000;
- }
- if (new_speed == 0)
- return 1000;
- if (new_speed == 1 || new_speed == 1000)
- return fact * new_speed;
-
- s = new_speed;
- new_speed = 1000 / new_speed;
- if (1000 / cur_speed == new_speed)
- new_speed += (cur_speed < s) ? -1 : 1;
- if (new_speed > 60) return 1000 / (fact * 60);
- return 1000 / (fact * new_speed);
-}
-
-static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
- struct v4l2_decoder_cmd *dc, int try)
-{
- struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
-
- switch (dc->cmd) {
- case V4L2_DEC_CMD_START: {
- dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
- dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
- if (dc->start.speed < 0)
- dc->start.format = V4L2_DEC_START_FMT_GOP;
- else
- dc->start.format = V4L2_DEC_START_FMT_NONE;
- if (dc->start.speed != 500 && dc->start.speed != 1500)
- dc->flags = dc->start.speed == 1000 ? 0 :
- V4L2_DEC_CMD_START_MUTE_AUDIO;
- if (try) break;
-
- itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
- if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
- return -EBUSY;
- if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
- /* forces ivtv_set_speed to be called */
- itv->speed = 0;
- }
- return ivtv_start_decoding(id, dc->start.speed);
- }
-
- case V4L2_DEC_CMD_STOP:
- dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
- if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
- dc->stop.pts = 0;
- if (try) break;
- if (atomic_read(&itv->decoding) == 0)
- return 0;
- if (itv->output_mode != OUT_MPG)
- return -EBUSY;
-
- itv->output_mode = OUT_NONE;
- return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
-
- case V4L2_DEC_CMD_PAUSE:
- dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
- if (try) break;
- if (itv->output_mode != OUT_MPG)
- return -EBUSY;
- if (atomic_read(&itv->decoding) > 0) {
- ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
- (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
- set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
- }
- break;
-
- case V4L2_DEC_CMD_RESUME:
- dc->flags = 0;
- if (try) break;
- if (itv->output_mode != OUT_MPG)
- return -EBUSY;
- if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
- int speed = itv->speed;
- itv->speed = 0;
- return ivtv_start_decoding(id, speed);
- }
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
-
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
- return -EINVAL;
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- if (itv->is_60hz) {
- vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- } else {
- vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
- vbifmt->service_lines[0][16] = V4L2_SLICED_VPS;
- }
- vbifmt->service_set = ivtv_get_service_set(vbifmt);
- return 0;
-}
-
-static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- pixfmt->width = itv->cxhdl.width;
- pixfmt->height = itv->cxhdl.height;
- pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pixfmt->field = V4L2_FIELD_INTERLACED;
- pixfmt->priv = 0;
- if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
- pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
- /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
- pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
- pixfmt->bytesperline = 720;
- } else {
- pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
- pixfmt->sizeimage = 128 * 1024;
- pixfmt->bytesperline = 0;
- }
- return 0;
-}
-
-static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
-
- vbifmt->sampling_rate = 27000000;
- vbifmt->offset = 248;
- vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4;
- vbifmt->sample_format = V4L2_PIX_FMT_GREY;
- vbifmt->start[0] = itv->vbi.start[0];
- vbifmt->start[1] = itv->vbi.start[1];
- vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count;
- vbifmt->flags = 0;
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- return 0;
-}
-
-static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
-
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
-
- if (id->type == IVTV_DEC_STREAM_TYPE_VBI) {
- vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 :
- V4L2_SLICED_VBI_525;
- ivtv_expand_service_set(vbifmt, itv->is_50hz);
- return 0;
- }
-
- v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt);
- vbifmt->service_set = ivtv_get_service_set(vbifmt);
- return 0;
-}
-
-static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- pixfmt->width = itv->main_rect.width;
- pixfmt->height = itv->main_rect.height;
- pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- pixfmt->field = V4L2_FIELD_INTERLACED;
- pixfmt->priv = 0;
- if (id->type == IVTV_DEC_STREAM_TYPE_YUV) {
- switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) {
- case IVTV_YUV_MODE_INTERLACED:
- pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ?
- V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB;
- break;
- case IVTV_YUV_MODE_PROGRESSIVE:
- pixfmt->field = V4L2_FIELD_NONE;
- break;
- default:
- pixfmt->field = V4L2_FIELD_ANY;
- break;
- }
- pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
- pixfmt->bytesperline = 720;
- pixfmt->width = itv->yuv_info.v4l2_src_w;
- pixfmt->height = itv->yuv_info.v4l2_src_h;
- /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
- pixfmt->sizeimage =
- 1080 * ((pixfmt->height + 31) & ~31);
- } else {
- pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
- pixfmt->sizeimage = 128 * 1024;
- pixfmt->bytesperline = 0;
- }
- return 0;
-}
-
-static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- struct v4l2_window *winfmt = &fmt->fmt.win;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- winfmt->chromakey = itv->osd_chroma_key;
- winfmt->global_alpha = itv->osd_global_alpha;
- winfmt->field = V4L2_FIELD_INTERLACED;
- winfmt->clips = NULL;
- winfmt->clipcount = 0;
- winfmt->bitmap = NULL;
- winfmt->w.top = winfmt->w.left = 0;
- winfmt->w.width = itv->osd_rect.width;
- winfmt->w.height = itv->osd_rect.height;
- return 0;
-}
-
-static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
-}
-
-static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- int w = fmt->fmt.pix.width;
- int h = fmt->fmt.pix.height;
- int min_h = 2;
-
- w = min(w, 720);
- w = max(w, 2);
- if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
- /* YUV height must be a multiple of 32 */
- h &= ~0x1f;
- min_h = 32;
- }
- h = min(h, itv->is_50hz ? 576 : 480);
- h = max(h, min_h);
- ivtv_g_fmt_vid_cap(file, fh, fmt);
- fmt->fmt.pix.width = w;
- fmt->fmt.pix.height = h;
- return 0;
-}
-
-static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- return ivtv_g_fmt_vbi_cap(file, fh, fmt);
-}
-
-static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
-
- if (id->type == IVTV_DEC_STREAM_TYPE_VBI)
- return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt);
-
- /* set sliced VBI capture format */
- vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
- vbifmt->reserved[0] = 0;
- vbifmt->reserved[1] = 0;
-
- if (vbifmt->service_set)
- ivtv_expand_service_set(vbifmt, itv->is_50hz);
- check_service_set(vbifmt, itv->is_50hz);
- vbifmt->service_set = ivtv_get_service_set(vbifmt);
- return 0;
-}
-
-static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- s32 w = fmt->fmt.pix.width;
- s32 h = fmt->fmt.pix.height;
- int field = fmt->fmt.pix.field;
- int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
-
- w = min(w, 720);
- w = max(w, 2);
- /* Why can the height be 576 even when the output is NTSC?
-
- Internally the buffers of the PVR350 are always set to 720x576. The
- decoded video frame will always be placed in the top left corner of
- this buffer. For any video which is not 720x576, the buffer will
- then be cropped to remove the unused right and lower areas, with
- the remaining image being scaled by the hardware to fit the display
- area. The video can be scaled both up and down, so a 720x480 video
- can be displayed full-screen on PAL and a 720x576 video can be
- displayed without cropping on NTSC.
-
- Note that the scaling only occurs on the video stream, the osd
- resolution is locked to the broadcast standard and not scaled.
-
- Thanks to Ian Armstrong for this explanation. */
- h = min(h, 576);
- h = max(h, 2);
- if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
- fmt->fmt.pix.field = field;
- fmt->fmt.pix.width = w;
- fmt->fmt.pix.height = h;
- return ret;
-}
-
-static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- u32 chromakey = fmt->fmt.win.chromakey;
- u8 global_alpha = fmt->fmt.win.global_alpha;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- ivtv_g_fmt_vid_out_overlay(file, fh, fmt);
- fmt->fmt.win.chromakey = chromakey;
- fmt->fmt.win.global_alpha = global_alpha;
- return 0;
-}
-
-static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt);
-}
-
-static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct v4l2_mbus_framefmt mbus_fmt;
- int ret = ivtv_try_fmt_vid_cap(file, fh, fmt);
- int w = fmt->fmt.pix.width;
- int h = fmt->fmt.pix.height;
-
- if (ret)
- return ret;
-
- if (itv->cxhdl.width == w && itv->cxhdl.height == h)
- return 0;
-
- if (atomic_read(&itv->capturing) > 0)
- return -EBUSY;
-
- itv->cxhdl.width = w;
- itv->cxhdl.height = h;
- if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
- fmt->fmt.pix.width /= 2;
- mbus_fmt.width = fmt->fmt.pix.width;
- mbus_fmt.height = h;
- mbus_fmt.code = V4L2_MBUS_FMT_FIXED;
- v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt);
- return ivtv_g_fmt_vid_cap(file, fh, fmt);
-}
-
-static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
- return -EBUSY;
- itv->vbi.sliced_in->service_set = 0;
- itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi);
- return ivtv_g_fmt_vbi_cap(file, fh, fmt);
-}
-
-static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt);
-
- if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI)
- return ret;
-
- check_service_set(vbifmt, itv->is_50hz);
- if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
- return -EBUSY;
- itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt);
- memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
- return 0;
-}
-
-static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
- int ret = ivtv_try_fmt_vid_out(file, fh, fmt);
-
- if (ret)
- return ret;
-
- if (id->type != IVTV_DEC_STREAM_TYPE_YUV)
- return 0;
-
- /* Return now if we already have some frame data */
- if (yi->stream_size)
- return -EBUSY;
-
- yi->v4l2_src_w = fmt->fmt.pix.width;
- yi->v4l2_src_h = fmt->fmt.pix.height;
-
- switch (fmt->fmt.pix.field) {
- case V4L2_FIELD_NONE:
- yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE;
- break;
- case V4L2_FIELD_ANY:
- yi->lace_mode = IVTV_YUV_MODE_AUTO;
- break;
- case V4L2_FIELD_INTERLACED_BT:
- yi->lace_mode =
- IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD;
- break;
- case V4L2_FIELD_INTERLACED_TB:
- default:
- yi->lace_mode = IVTV_YUV_MODE_INTERLACED;
- break;
- }
- yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1;
-
- if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
- itv->dma_data_req_size =
- 1080 * ((yi->v4l2_src_h + 31) & ~31);
-
- return 0;
-}
-
-static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt);
-
- if (ret == 0) {
- itv->osd_chroma_key = fmt->fmt.win.chromakey;
- itv->osd_global_alpha = fmt->fmt.win.global_alpha;
- ivtv_set_osd_alpha(itv);
- }
- return ret;
-}
-
-static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- chip->ident = V4L2_IDENT_NONE;
- chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
- if (v4l2_chip_match_host(&chip->match))
- chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
- return 0;
- }
- if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
- chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
- /* TODO: is this correct? */
- return ivtv_call_all_err(itv, core, g_chip_ident, chip);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- struct v4l2_dbg_register *regs = arg;
- volatile u8 __iomem *reg_start;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE)
- reg_start = itv->reg_mem - IVTV_REG_OFFSET;
- else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET &&
- regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE)
- reg_start = itv->dec_mem - IVTV_DECODER_OFFSET;
- else if (regs->reg < IVTV_ENCODER_SIZE)
- reg_start = itv->enc_mem;
- else
- return -EINVAL;
-
- regs->size = 4;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- regs->val = readl(regs->reg + reg_start);
- else
- writel(regs->val, regs->reg + reg_start);
- return 0;
-}
-
-static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (v4l2_chip_match_host(&reg->match))
- return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- ivtv_call_all(itv, core, g_register, reg);
- return 0;
-}
-
-static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (v4l2_chip_match_host(&reg->match))
- return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
- /* TODO: subdev errors should not be ignored, this should become a
- subdev helper function. */
- ivtv_call_all(itv, core, s_register, reg);
- return 0;
-}
-#endif
-
-static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
-{
- struct ivtv_open_id *id = fh2id(file->private_data);
- struct ivtv *itv = id->itv;
- struct ivtv_stream *s = &itv->streams[id->type];
-
- strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
- strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
- snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
- vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
- vcap->device_caps = s->caps;
- return 0;
-}
-
-static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- return ivtv_get_audio_input(itv, vin->index, vin);
-}
-
-static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- vin->index = itv->audio_input;
- return ivtv_get_audio_input(itv, vin->index, vin);
-}
-
-static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (vout->index >= itv->nof_audio_inputs)
- return -EINVAL;
-
- itv->audio_input = vout->index;
- ivtv_audio_set_io(itv);
-
- return 0;
-}
-
-static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- /* set it to defaults from our table */
- return ivtv_get_audio_output(itv, vin->index, vin);
-}
-
-static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- vin->index = 0;
- return ivtv_get_audio_output(itv, vin->index, vin);
-}
-
-static int ivtv_s_audout(struct file *file, void *fh, struct v4l2_audioout *vout)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- return ivtv_get_audio_output(itv, vout->index, vout);
-}
-
-static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- /* set it to defaults from our table */
- return ivtv_get_input(itv, vin->index, vin);
-}
-
-static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- return ivtv_get_output(itv, vout->index, vout);
-}
-
-static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
- int streamtype;
-
- streamtype = id->type;
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
- cropcap->bounds.top = cropcap->bounds.left = 0;
- cropcap->bounds.width = 720;
- if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- cropcap->bounds.height = itv->is_50hz ? 576 : 480;
- cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11;
- } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
- if (yi->track_osd) {
- cropcap->bounds.width = yi->osd_full_w;
- cropcap->bounds.height = yi->osd_full_h;
- } else {
- cropcap->bounds.width = 720;
- cropcap->bounds.height =
- itv->is_out_50hz ? 576 : 480;
- }
- cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
- } else {
- cropcap->bounds.height = itv->is_out_50hz ? 576 : 480;
- cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10;
- cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11;
- }
- cropcap->defrect = cropcap->bounds;
- return 0;
-}
-
-static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
- int streamtype;
-
- streamtype = id->type;
-
- if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
- yi->main_rect = crop->c;
- return 0;
- } else {
- if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
- crop->c.width, crop->c.height, crop->c.left, crop->c.top)) {
- itv->main_rect = crop->c;
- return 0;
- }
- }
- return -EINVAL;
- }
- return -EINVAL;
-}
-
-static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
- int streamtype;
-
- streamtype = id->type;
-
- if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (streamtype == IVTV_DEC_STREAM_TYPE_YUV)
- crop->c = yi->main_rect;
- else
- crop->c = itv->main_rect;
- return 0;
- }
- return -EINVAL;
-}
-
-static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
- { 0, 0, 0, 0 }
- },
- { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
- "MPEG", V4L2_PIX_FMT_MPEG,
- { 0, 0, 0, 0 }
- }
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (fmt->index > 1)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
- return 0;
-}
-
-static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- static struct v4l2_fmtdesc formats[] = {
- { 0, 0, 0,
- "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12,
- { 0, 0, 0, 0 }
- },
- { 1, 0, V4L2_FMT_FLAG_COMPRESSED,
- "MPEG", V4L2_PIX_FMT_MPEG,
- { 0, 0, 0, 0 }
- }
- };
- enum v4l2_buf_type type = fmt->type;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
-
- if (fmt->index > 1)
- return -EINVAL;
-
- *fmt = formats[fmt->index];
- fmt->type = type;
-
- return 0;
-}
-
-static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- *i = itv->active_input;
-
- return 0;
-}
-
-int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (inp < 0 || inp >= itv->nof_inputs)
- return -EINVAL;
-
- if (inp == itv->active_input) {
- IVTV_DEBUG_INFO("Input unchanged\n");
- return 0;
- }
-
- if (atomic_read(&itv->capturing) > 0) {
- return -EBUSY;
- }
-
- IVTV_DEBUG_INFO("Changing input from %d to %d\n",
- itv->active_input, inp);
-
- itv->active_input = inp;
- /* Set the audio input to whatever is appropriate for the
- input type. */
- itv->audio_input = itv->card->video_inputs[inp].audio_index;
-
- /* prevent others from messing with the streams until
- we're finished changing inputs. */
- ivtv_mute(itv);
- ivtv_video_set_io(itv);
- ivtv_audio_set_io(itv);
- ivtv_unmute(itv);
-
- return 0;
-}
-
-static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
-
- *i = itv->active_output;
-
- return 0;
-}
-
-static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (outp >= itv->card->nof_outputs)
- return -EINVAL;
-
- if (outp == itv->active_output) {
- IVTV_DEBUG_INFO("Output unchanged\n");
- return 0;
- }
- IVTV_DEBUG_INFO("Changing output from %d to %d\n",
- itv->active_output, outp);
-
- itv->active_output = outp;
- ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing,
- SAA7127_INPUT_TYPE_NORMAL,
- itv->card->video_outputs[outp].video_output, 0);
-
- return 0;
-}
-
-static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (vf->tuner != 0)
- return -EINVAL;
-
- ivtv_call_all(itv, tuner, g_frequency, vf);
- return 0;
-}
-
-int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (vf->tuner != 0)
- return -EINVAL;
-
- ivtv_mute(itv);
- IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- ivtv_call_all(itv, tuner, s_frequency, vf);
- ivtv_unmute(itv);
- return 0;
-}
-
-static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- *std = itv->std;
- return 0;
-}
-
-void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std)
-{
- itv->std = *std;
- itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- itv->is_50hz = !itv->is_60hz;
- cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);
- itv->cxhdl.width = 720;
- itv->cxhdl.height = itv->is_50hz ? 576 : 480;
- itv->vbi.count = itv->is_50hz ? 18 : 12;
- itv->vbi.start[0] = itv->is_50hz ? 6 : 10;
- itv->vbi.start[1] = itv->is_50hz ? 318 : 273;
-
- if (itv->hw_flags & IVTV_HW_CX25840)
- itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284;
-
- /* Tuner */
- ivtv_call_all(itv, core, s_std, itv->std);
-}
-
-void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std)
-{
- struct yuv_playback_info *yi = &itv->yuv_info;
- DEFINE_WAIT(wait);
- int f;
-
- /* set display standard */
- itv->std_out = *std;
- itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0;
- itv->is_out_50hz = !itv->is_out_60hz;
- ivtv_call_all(itv, video, s_std_output, itv->std_out);
-
- /*
- * The next firmware call is time sensitive. Time it to
- * avoid risk of a hard lock, by trying to ensure the call
- * happens within the first 100 lines of the top field.
- * Make 4 attempts to sync to the decoder before giving up.
- */
- mutex_unlock(&itv->serialize_lock);
- for (f = 0; f < 4; f++) {
- prepare_to_wait(&itv->vsync_waitq, &wait,
- TASK_UNINTERRUPTIBLE);
- if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100)
- break;
- schedule_timeout(msecs_to_jiffies(25));
- }
- finish_wait(&itv->vsync_waitq, &wait);
- mutex_lock(&itv->serialize_lock);
-
- if (f == 4)
- IVTV_WARN("Mode change failed to sync to decoder\n");
-
- ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
- itv->main_rect.left = 0;
- itv->main_rect.top = 0;
- itv->main_rect.width = 720;
- itv->main_rect.height = itv->is_out_50hz ? 576 : 480;
- ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4,
- 720, itv->main_rect.height, 0, 0);
- yi->main_rect = itv->main_rect;
- if (!itv->osd_info) {
- yi->osd_full_w = 720;
- yi->osd_full_h = itv->is_out_50hz ? 576 : 480;
- }
-}
-
-int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if ((*std & V4L2_STD_ALL) == 0)
- return -EINVAL;
-
- if (*std == itv->std)
- return 0;
-
- if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ||
- atomic_read(&itv->capturing) > 0 ||
- atomic_read(&itv->decoding) > 0) {
- /* Switching standard would mess with already running
- streams, prevent that by returning EBUSY. */
- return -EBUSY;
- }
-
- IVTV_DEBUG_INFO("Switching standard to %llx.\n",
- (unsigned long long)itv->std);
-
- ivtv_s_std_enc(itv, std);
- if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- ivtv_s_std_dec(itv, std);
-
- return 0;
-}
-
-static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
-
- if (vt->index != 0)
- return -EINVAL;
-
- ivtv_call_all(itv, tuner, s_tuner, vt);
-
- return 0;
-}
-
-static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (vt->index != 0)
- return -EINVAL;
-
- ivtv_call_all(itv, tuner, g_tuner, vt);
-
- if (vt->type == V4L2_TUNER_RADIO)
- strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
- else
- strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name));
- return 0;
-}
-
-static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
- int f, l;
-
- if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- for (f = 0; f < 2; f++) {
- for (l = 0; l < 24; l++) {
- if (valid_service_line(f, l, itv->is_50hz))
- cap->service_lines[f][l] = set;
- }
- }
- } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) {
- if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT))
- return -EINVAL;
- if (itv->is_60hz) {
- cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- } else {
- cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
- cap->service_lines[0][16] = V4L2_SLICED_VPS;
- }
- } else {
- return -EINVAL;
- }
-
- set = 0;
- for (f = 0; f < 2; f++)
- for (l = 0; l < 24; l++)
- set |= cap->service_lines[f][l];
- cap->service_set = set;
- return 0;
-}
-
-static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- struct v4l2_enc_idx_entry *e = idx->entry;
- int entries;
- int i;
-
- entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) %
- IVTV_MAX_PGM_INDEX;
- if (entries > V4L2_ENC_IDX_ENTRIES)
- entries = V4L2_ENC_IDX_ENTRIES;
- idx->entries = 0;
- for (i = 0; i < entries; i++) {
- *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX];
- if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) {
- idx->entries++;
- e++;
- }
- }
- itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX;
- return 0;
-}
-
-static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
-
-
- switch (enc->cmd) {
- case V4L2_ENC_CMD_START:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
- enc->flags = 0;
- return ivtv_start_capture(id);
-
- case V4L2_ENC_CMD_STOP:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
- enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
- ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
- return 0;
-
- case V4L2_ENC_CMD_PAUSE:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
- enc->flags = 0;
-
- if (!atomic_read(&itv->capturing))
- return -EPERM;
- if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
- return 0;
-
- ivtv_mute(itv);
- ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0);
- break;
-
- case V4L2_ENC_CMD_RESUME:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
- enc->flags = 0;
-
- if (!atomic_read(&itv->capturing))
- return -EPERM;
-
- if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags))
- return 0;
-
- ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1);
- ivtv_unmute(itv);
- break;
- default:
- IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- switch (enc->cmd) {
- case V4L2_ENC_CMD_START:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n");
- enc->flags = 0;
- return 0;
-
- case V4L2_ENC_CMD_STOP:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n");
- enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
- return 0;
-
- case V4L2_ENC_CMD_PAUSE:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n");
- enc->flags = 0;
- return 0;
-
- case V4L2_ENC_CMD_RESUME:
- IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n");
- enc->flags = 0;
- return 0;
- default:
- IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd);
- return -EINVAL;
- }
-}
-
-static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct yuv_playback_info *yi = &itv->yuv_info;
-
- int pixfmt;
- static u32 pixel_format[16] = {
- V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */
- V4L2_PIX_FMT_RGB565,
- V4L2_PIX_FMT_RGB555,
- V4L2_PIX_FMT_RGB444,
- V4L2_PIX_FMT_RGB32,
- 0,
- 0,
- 0,
- V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */
- V4L2_PIX_FMT_YUV565,
- V4L2_PIX_FMT_YUV555,
- V4L2_PIX_FMT_YUV444,
- V4L2_PIX_FMT_YUV32,
- 0,
- 0,
- 0,
- };
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
- return -EINVAL;
- if (!itv->osd_video_pbase)
- return -EINVAL;
-
- fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY |
- V4L2_FBUF_CAP_GLOBAL_ALPHA;
-
- ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
- data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
- pixfmt = (data[0] >> 3) & 0xf;
-
- fb->fmt.pixelformat = pixel_format[pixfmt];
- fb->fmt.width = itv->osd_rect.width;
- fb->fmt.height = itv->osd_rect.height;
- fb->fmt.field = V4L2_FIELD_INTERLACED;
- fb->fmt.bytesperline = fb->fmt.width;
- fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M;
- fb->fmt.field = V4L2_FIELD_INTERLACED;
- fb->fmt.priv = 0;
- if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8)
- fb->fmt.bytesperline *= 2;
- if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 ||
- fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32)
- fb->fmt.bytesperline *= 2;
- fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height;
- fb->base = (void *)itv->osd_video_pbase;
- fb->flags = 0;
-
- if (itv->osd_chroma_key_state)
- fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
-
- if (itv->osd_global_alpha_state)
- fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA;
-
- if (yi->track_osd)
- fb->flags |= V4L2_FBUF_FLAG_OVERLAY;
-
- pixfmt &= 7;
-
- /* no local alpha for RGB565 or unknown formats */
- if (pixfmt == 1 || pixfmt > 4)
- return 0;
-
- /* 16-bit formats have inverted local alpha */
- if (pixfmt == 2 || pixfmt == 3)
- fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA;
- else
- fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA;
-
- if (itv->osd_local_alpha_state) {
- /* 16-bit formats have inverted local alpha */
- if (pixfmt == 2 || pixfmt == 3)
- fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
- else
- fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
- }
-
- return 0;
-}
-
-static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
- return -EINVAL;
- if (!itv->osd_video_pbase)
- return -EINVAL;
-
- itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0;
- itv->osd_local_alpha_state =
- (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0;
- itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0;
- ivtv_set_osd_alpha(itv);
- yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0;
- return ivtv_g_fbuf(file, fh, fb);
-}
-
-static int ivtv_overlay(struct file *file, void *fh, unsigned int on)
-{
- struct ivtv_open_id *id = fh2id(fh);
- struct ivtv *itv = id->itv;
-
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY))
- return -EINVAL;
-
- ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0);
-
- return 0;
-}
-
-static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub)
-{
- switch (sub->type) {
- case V4L2_EVENT_VSYNC:
- case V4L2_EVENT_EOS:
- return v4l2_event_subscribe(fh, sub, 0, NULL);
- case V4L2_EVENT_CTRL:
- return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
- default:
- return -EINVAL;
- }
-}
-
-static int ivtv_log_status(struct file *file, void *fh)
-{
- struct ivtv *itv = fh2id(fh)->itv;
- u32 data[CX2341X_MBOX_MAX_DATA];
-
- int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT;
- struct v4l2_input vidin;
- struct v4l2_audio audin;
- int i;
-
- IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
- if (itv->hw_flags & IVTV_HW_TVEEPROM) {
- struct tveeprom tv;
-
- ivtv_read_eeprom(itv, &tv);
- }
- ivtv_call_all(itv, core, log_status);
- ivtv_get_input(itv, itv->active_input, &vidin);
- ivtv_get_audio_input(itv, itv->audio_input, &audin);
- IVTV_INFO("Video Input: %s\n", vidin.name);
- IVTV_INFO("Audio Input: %s%s\n", audin.name,
- (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : "");
- if (has_output) {
- struct v4l2_output vidout;
- struct v4l2_audioout audout;
- int mode = itv->output_mode;
- static const char * const output_modes[5] = {
- "None",
- "MPEG Streaming",
- "YUV Streaming",
- "YUV Frames",
- "Passthrough",
- };
- static const char * const alpha_mode[4] = {
- "None",
- "Global",
- "Local",
- "Global and Local"
- };
- static const char * const pixel_format[16] = {
- "ARGB Indexed",
- "RGB 5:6:5",
- "ARGB 1:5:5:5",
- "ARGB 1:4:4:4",
- "ARGB 8:8:8:8",
- "5",
- "6",
- "7",
- "AYUV Indexed",
- "YUV 5:6:5",
- "AYUV 1:5:5:5",
- "AYUV 1:4:4:4",
- "AYUV 8:8:8:8",
- "13",
- "14",
- "15",
- };
-
- ivtv_get_output(itv, itv->active_output, &vidout);
- ivtv_get_audio_output(itv, 0, &audout);
- IVTV_INFO("Video Output: %s\n", vidout.name);
- if (mode < 0 || mode > OUT_PASSTHROUGH)
- mode = OUT_NONE;
- IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
- ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0);
- data[0] |= (read_reg(0x2a00) >> 7) & 0x40;
- IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n",
- data[0] & 1 ? "On" : "Off",
- alpha_mode[(data[0] >> 1) & 0x3],
- pixel_format[(data[0] >> 3) & 0xf]);
- }
- IVTV_INFO("Tuner: %s\n",
- test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
- v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name);
- IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
- for (i = 0; i < IVTV_MAX_STREAMS; i++) {
- struct ivtv_stream *s = &itv->streams[i];
-
- if (s->vdev == NULL || s->buffers == 0)
- continue;
- IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
- (s->buffers - s->q_free.buffers) * 100 / s->buffers,
- (s->buffers * s->buf_size) / 1024, s->buffers);
- }
-
- IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
- (long long)itv->mpg_data_received,
- (long long)itv->vbi_data_inserted);
- return 0;
-}
-
-static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
-{
- struct ivtv_open_id *id = fh2id(file->private_data);
- struct ivtv *itv = id->itv;
-
- IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
- return ivtv_video_command(itv, id, dec, false);
-}
-
-static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
-{
- struct ivtv_open_id *id = fh2id(file->private_data);
- struct ivtv *itv = id->itv;
-
- IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
- return ivtv_video_command(itv, id, dec, true);
-}
-
-static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
-{
- struct ivtv_open_id *id = fh2id(filp->private_data);
- struct ivtv *itv = id->itv;
- int nonblocking = filp->f_flags & O_NONBLOCK;
- struct ivtv_stream *s = &itv->streams[id->type];
- unsigned long iarg = (unsigned long)arg;
-
- switch (cmd) {
- case IVTV_IOC_DMA_FRAME: {
- struct ivtv_dma_frame *args = arg;
-
- IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n");
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
- if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL)
- return 0;
- if (ivtv_start_decoding(id, id->type)) {
- return -EBUSY;
- }
- if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) {
- ivtv_release_stream(s);
- return -EBUSY;
- }
- /* Mark that this file handle started the UDMA_YUV mode */
- id->yuv_frames = 1;
- if (args->y_source == NULL)
- return 0;
- return ivtv_yuv_prep_frame(itv, args);
- }
-
- case IVTV_IOC_PASSTHROUGH_MODE:
- IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- return ivtv_passthrough_mode(itv, *(int *)arg != 0);
-
- case VIDEO_GET_PTS: {
- s64 *pts = arg;
- s64 frame;
-
- IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
- if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
- *pts = s->dma_pts;
- break;
- }
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- return ivtv_g_pts_frame(itv, pts, &frame);
- }
-
- case VIDEO_GET_FRAME_COUNT: {
- s64 *frame = arg;
- s64 pts;
-
- IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
- if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
- *frame = 0;
- break;
- }
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- return ivtv_g_pts_frame(itv, &pts, frame);
- }
-
- case VIDEO_PLAY: {
- struct v4l2_decoder_cmd dc;
-
- IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
- memset(&dc, 0, sizeof(dc));
- dc.cmd = V4L2_DEC_CMD_START;
- return ivtv_video_command(itv, id, &dc, 0);
- }
-
- case VIDEO_STOP: {
- struct v4l2_decoder_cmd dc;
-
- IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
- memset(&dc, 0, sizeof(dc));
- dc.cmd = V4L2_DEC_CMD_STOP;
- dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
- return ivtv_video_command(itv, id, &dc, 0);
- }
-
- case VIDEO_FREEZE: {
- struct v4l2_decoder_cmd dc;
-
- IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
- memset(&dc, 0, sizeof(dc));
- dc.cmd = V4L2_DEC_CMD_PAUSE;
- return ivtv_video_command(itv, id, &dc, 0);
- }
-
- case VIDEO_CONTINUE: {
- struct v4l2_decoder_cmd dc;
-
- IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
- memset(&dc, 0, sizeof(dc));
- dc.cmd = V4L2_DEC_CMD_RESUME;
- return ivtv_video_command(itv, id, &dc, 0);
- }
-
- case VIDEO_COMMAND:
- case VIDEO_TRY_COMMAND: {
- /* Note: struct v4l2_decoder_cmd has the same layout as
- struct video_command */
- struct v4l2_decoder_cmd *dc = arg;
- int try = (cmd == VIDEO_TRY_COMMAND);
-
- if (try)
- IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
- else
- IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
- return ivtv_video_command(itv, id, dc, try);
- }
-
- case VIDEO_GET_EVENT: {
- struct video_event *ev = arg;
- DEFINE_WAIT(wait);
-
- IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n");
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- memset(ev, 0, sizeof(*ev));
- set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags);
-
- while (1) {
- if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags))
- ev->type = VIDEO_EVENT_DECODER_STOPPED;
- else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) {
- ev->type = VIDEO_EVENT_VSYNC;
- ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ?
- VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN;
- if (itv->output_mode == OUT_UDMA_YUV &&
- (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) ==
- IVTV_YUV_MODE_PROGRESSIVE) {
- ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE;
- }
- }
- if (ev->type)
- return 0;
- if (nonblocking)
- return -EAGAIN;
- /* Wait for event. Note that serialize_lock is locked,
- so to allow other processes to access the driver while
- we are waiting unlock first and later lock again. */
- mutex_unlock(&itv->serialize_lock);
- prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE);
- if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) &&
- !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags))
- schedule();
- finish_wait(&itv->event_waitq, &wait);
- mutex_lock(&itv->serialize_lock);
- if (signal_pending(current)) {
- /* return if a signal was received */
- IVTV_DEBUG_INFO("User stopped wait for event\n");
- return -EINTR;
- }
- }
- break;
- }
-
- case VIDEO_SELECT_SOURCE:
- IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n");
- if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return -EINVAL;
- return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX);
-
- case AUDIO_SET_MUTE:
- IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n");
- itv->speed_mute_audio = iarg;
- return 0;
-
- case AUDIO_CHANNEL_SELECT:
- IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
- if (iarg > AUDIO_STEREO_SWAPPED)
- return -EINVAL;
- return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1);
-
- case AUDIO_BILINGUAL_CHANNEL_SELECT:
- IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
- if (iarg > AUDIO_STEREO_SWAPPED)
- return -EINVAL;
- return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1);
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static long ivtv_default(struct file *file, void *fh, bool valid_prio,
- int cmd, void *arg)
-{
- struct ivtv *itv = fh2id(fh)->itv;
-
- if (!valid_prio) {
- switch (cmd) {
- case IVTV_IOC_PASSTHROUGH_MODE:
- case VIDEO_PLAY:
- case VIDEO_STOP:
- case VIDEO_FREEZE:
- case VIDEO_CONTINUE:
- case VIDEO_COMMAND:
- case VIDEO_SELECT_SOURCE:
- case AUDIO_SET_MUTE:
- case AUDIO_CHANNEL_SELECT:
- case AUDIO_BILINGUAL_CHANNEL_SELECT:
- return -EBUSY;
- }
- }
-
- switch (cmd) {
- case VIDIOC_INT_RESET: {
- u32 val = *(u32 *)arg;
-
- if ((val == 0 && itv->options.newi2c) || (val & 0x01))
- ivtv_reset_ir_gpio(itv);
- if (val & 0x02)
- v4l2_subdev_call(itv->sd_video, core, reset, 0);
- break;
- }
-
- case IVTV_IOC_DMA_FRAME:
- case IVTV_IOC_PASSTHROUGH_MODE:
- case VIDEO_GET_PTS:
- case VIDEO_GET_FRAME_COUNT:
- case VIDEO_GET_EVENT:
- case VIDEO_PLAY:
- case VIDEO_STOP:
- case VIDEO_FREEZE:
- case VIDEO_CONTINUE:
- case VIDEO_COMMAND:
- case VIDEO_TRY_COMMAND:
- case VIDEO_SELECT_SOURCE:
- case AUDIO_SET_MUTE:
- case AUDIO_CHANNEL_SELECT:
- case AUDIO_BILINGUAL_CHANNEL_SELECT:
- return ivtv_decoder_ioctls(file, cmd, (void *)arg);
-
- default:
- return -ENOTTY;
- }
- return 0;
-}
-
-static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
- .vidioc_querycap = ivtv_querycap,
- .vidioc_s_audio = ivtv_s_audio,
- .vidioc_g_audio = ivtv_g_audio,
- .vidioc_enumaudio = ivtv_enumaudio,
- .vidioc_s_audout = ivtv_s_audout,
- .vidioc_g_audout = ivtv_g_audout,
- .vidioc_enum_input = ivtv_enum_input,
- .vidioc_enum_output = ivtv_enum_output,
- .vidioc_enumaudout = ivtv_enumaudout,
- .vidioc_cropcap = ivtv_cropcap,
- .vidioc_s_crop = ivtv_s_crop,
- .vidioc_g_crop = ivtv_g_crop,
- .vidioc_g_input = ivtv_g_input,
- .vidioc_s_input = ivtv_s_input,
- .vidioc_g_output = ivtv_g_output,
- .vidioc_s_output = ivtv_s_output,
- .vidioc_g_frequency = ivtv_g_frequency,
- .vidioc_s_frequency = ivtv_s_frequency,
- .vidioc_s_tuner = ivtv_s_tuner,
- .vidioc_g_tuner = ivtv_g_tuner,
- .vidioc_g_enc_index = ivtv_g_enc_index,
- .vidioc_g_fbuf = ivtv_g_fbuf,
- .vidioc_s_fbuf = ivtv_s_fbuf,
- .vidioc_g_std = ivtv_g_std,
- .vidioc_s_std = ivtv_s_std,
- .vidioc_overlay = ivtv_overlay,
- .vidioc_log_status = ivtv_log_status,
- .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap,
- .vidioc_encoder_cmd = ivtv_encoder_cmd,
- .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd,
- .vidioc_decoder_cmd = ivtv_decoder_cmd,
- .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd,
- .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap,
- .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap,
- .vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap,
- .vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out,
- .vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay,
- .vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out,
- .vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap,
- .vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap,
- .vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap,
- .vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out,
- .vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay,
- .vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out,
- .vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap,
- .vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap,
- .vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap,
- .vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out,
- .vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay,
- .vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out,
- .vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap,
- .vidioc_g_chip_ident = ivtv_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = ivtv_g_register,
- .vidioc_s_register = ivtv_s_register,
-#endif
- .vidioc_default = ivtv_default,
- .vidioc_subscribe_event = ivtv_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-void ivtv_set_funcs(struct video_device *vdev)
-{
- vdev->ioctl_ops = &ivtv_ioctl_ops;
-}
diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
deleted file mode 100644
index 1b3b9578bf47..000000000000
--- a/drivers/media/video/ivtv/ivtv-irq.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/* interrupt handling
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-queue.h"
-#include "ivtv-udma.h"
-#include "ivtv-irq.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-vbi.h"
-#include "ivtv-yuv.h"
-#include <media/v4l2-event.h>
-
-#define DMA_MAGIC_COOKIE 0x000001fe
-
-static void ivtv_dma_dec_start(struct ivtv_stream *s);
-
-static const int ivtv_stream_map[] = {
- IVTV_ENC_STREAM_TYPE_MPG,
- IVTV_ENC_STREAM_TYPE_YUV,
- IVTV_ENC_STREAM_TYPE_PCM,
- IVTV_ENC_STREAM_TYPE_VBI,
-};
-
-
-static void ivtv_pio_work_handler(struct ivtv *itv)
-{
- struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream];
- struct ivtv_buffer *buf;
- int i = 0;
-
- IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
- if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
- s->vdev == NULL || !ivtv_use_pio(s)) {
- itv->cur_pio_stream = -1;
- /* trigger PIO complete user interrupt */
- write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
- return;
- }
- IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name);
- list_for_each_entry(buf, &s->q_dma.list, list) {
- u32 size = s->sg_processing[i].size & 0x3ffff;
-
- /* Copy the data from the card to the buffer */
- if (s->type == IVTV_DEC_STREAM_TYPE_VBI) {
- memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size);
- }
- else {
- memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size);
- }
- i++;
- if (i == s->sg_processing_size)
- break;
- }
- write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
-}
-
-void ivtv_irq_work_handler(struct kthread_work *work)
-{
- struct ivtv *itv = container_of(work, struct ivtv, irq_work);
-
- if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags))
- ivtv_pio_work_handler(itv);
-
- if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags))
- ivtv_vbi_work_handler(itv);
-
- if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags))
- ivtv_yuv_work_handler(itv);
-}
-
-/* Determine the required DMA size, setup enough buffers in the predma queue and
- actually copy the data from the card to the buffers in case a PIO transfer is
- required for this stream.
- */
-static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA])
-{
- struct ivtv *itv = s->itv;
- struct ivtv_buffer *buf;
- u32 bytes_needed = 0;
- u32 offset, size;
- u32 UVoffset = 0, UVsize = 0;
- int skip_bufs = s->q_predma.buffers;
- int idx = s->sg_pending_size;
- int rc;
-
- /* sanity checks */
- if (s->vdev == NULL) {
- IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
- return -1;
- }
- if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) {
- IVTV_DEBUG_WARN("Stream %s not open\n", s->name);
- return -1;
- }
-
- /* determine offset, size and PTS for the various streams */
- switch (s->type) {
- case IVTV_ENC_STREAM_TYPE_MPG:
- offset = data[1];
- size = data[2];
- s->pending_pts = 0;
- break;
-
- case IVTV_ENC_STREAM_TYPE_YUV:
- offset = data[1];
- size = data[2];
- UVoffset = data[3];
- UVsize = data[4];
- s->pending_pts = ((u64) data[5] << 32) | data[6];
- break;
-
- case IVTV_ENC_STREAM_TYPE_PCM:
- offset = data[1] + 12;
- size = data[2] - 12;
- s->pending_pts = read_dec(offset - 8) |
- ((u64)(read_dec(offset - 12)) << 32);
- if (itv->has_cx23415)
- offset += IVTV_DECODER_OFFSET;
- break;
-
- case IVTV_ENC_STREAM_TYPE_VBI:
- size = itv->vbi.enc_size * itv->vbi.fpi;
- offset = read_enc(itv->vbi.enc_start - 4) + 12;
- if (offset == 12) {
- IVTV_DEBUG_INFO("VBI offset == 0\n");
- return -1;
- }
- s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32);
- break;
-
- case IVTV_DEC_STREAM_TYPE_VBI:
- size = read_dec(itv->vbi.dec_start + 4) + 8;
- offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start;
- s->pending_pts = 0;
- offset += IVTV_DECODER_OFFSET;
- break;
- default:
- /* shouldn't happen */
- return -1;
- }
-
- /* if this is the start of the DMA then fill in the magic cookie */
- if (s->sg_pending_size == 0 && ivtv_use_dma(s)) {
- if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
- s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
- s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET);
- write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET);
- }
- else {
- s->pending_backup = read_enc(offset);
- write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset);
- }
- s->pending_offset = offset;
- }
-
- bytes_needed = size;
- if (s->type == IVTV_ENC_STREAM_TYPE_YUV) {
- /* The size for the Y samples needs to be rounded upwards to a
- multiple of the buf_size. The UV samples then start in the
- next buffer. */
- bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size);
- bytes_needed += UVsize;
- }
-
- IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n",
- ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset);
-
- rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed);
- if (rc < 0) { /* Insufficient buffers */
- IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n",
- bytes_needed, s->name);
- return -1;
- }
- if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) {
- IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name);
- IVTV_WARN("Cause: the application is not reading fast enough.\n");
- }
- s->buffers_stolen = rc;
-
- /* got the buffers, now fill in sg_pending */
- buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list);
- memset(buf->buf, 0, 128);
- list_for_each_entry(buf, &s->q_predma.list, list) {
- if (skip_bufs-- > 0)
- continue;
- s->sg_pending[idx].dst = buf->dma_handle;
- s->sg_pending[idx].src = offset;
- s->sg_pending[idx].size = s->buf_size;
- buf->bytesused = min(size, s->buf_size);
- buf->dma_xfer_cnt = s->dma_xfer_cnt;
-
- s->q_predma.bytesused += buf->bytesused;
- size -= buf->bytesused;
- offset += s->buf_size;
-
- /* Sync SG buffers */
- ivtv_buf_sync_for_device(s, buf);
-
- if (size == 0) { /* YUV */
- /* process the UV section */
- offset = UVoffset;
- size = UVsize;
- }
- idx++;
- }
- s->sg_pending_size = idx;
- return 0;
-}
-
-static void dma_post(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
- struct ivtv_buffer *buf = NULL;
- struct list_head *p;
- u32 offset;
- __le32 *u32buf;
- int x = 0;
-
- IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA",
- s->name, s->dma_offset);
- list_for_each(p, &s->q_dma.list) {
- buf = list_entry(p, struct ivtv_buffer, list);
- u32buf = (__le32 *)buf->buf;
-
- /* Sync Buffer */
- ivtv_buf_sync_for_cpu(s, buf);
-
- if (x == 0 && ivtv_use_dma(s)) {
- offset = s->dma_last_offset;
- if (u32buf[offset / 4] != DMA_MAGIC_COOKIE)
- {
- for (offset = 0; offset < 64; offset++) {
- if (u32buf[offset] == DMA_MAGIC_COOKIE) {
- break;
- }
- }
- offset *= 4;
- if (offset == 256) {
- IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name);
- offset = s->dma_last_offset;
- }
- if (s->dma_last_offset != offset)
- IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset);
- s->dma_last_offset = offset;
- }
- if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM ||
- s->type == IVTV_DEC_STREAM_TYPE_VBI)) {
- write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET);
- }
- else {
- write_enc_sync(0, s->dma_offset);
- }
- if (offset) {
- buf->bytesused -= offset;
- memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset);
- }
- *u32buf = cpu_to_le32(s->dma_backup);
- }
- x++;
- /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG ||
- s->type == IVTV_ENC_STREAM_TYPE_VBI)
- buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP;
- }
- if (buf)
- buf->bytesused += s->dma_last_offset;
- if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) {
- list_for_each_entry(buf, &s->q_dma.list, list) {
- /* Parse and Groom VBI Data */
- s->q_dma.bytesused -= buf->bytesused;
- ivtv_process_vbi_data(itv, buf, 0, s->type);
- s->q_dma.bytesused += buf->bytesused;
- }
- if (s->fh == NULL) {
- ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
- return;
- }
- }
- ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused);
- if (s->fh)
- wake_up(&s->waitq);
-}
-
-void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock)
-{
- struct ivtv *itv = s->itv;
- struct yuv_playback_info *yi = &itv->yuv_info;
- u8 frame = yi->draw_frame;
- struct yuv_frame_info *f = &yi->new_frame_info[frame];
- struct ivtv_buffer *buf;
- u32 y_size = 720 * ((f->src_h + 31) & ~31);
- u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET;
- int y_done = 0;
- int bytes_written = 0;
- unsigned long flags = 0;
- int idx = 0;
-
- IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset);
-
- /* Insert buffer block for YUV if needed */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) {
- if (yi->blanking_dmaptr) {
- s->sg_pending[idx].src = yi->blanking_dmaptr;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size = 720 * 16;
- }
- offset += 720 * 16;
- idx++;
- }
-
- list_for_each_entry(buf, &s->q_predma.list, list) {
- /* YUV UV Offset from Y Buffer */
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done &&
- (bytes_written + buf->bytesused) >= y_size) {
- s->sg_pending[idx].src = buf->dma_handle;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size = y_size - bytes_written;
- offset = uv_offset;
- if (s->sg_pending[idx].size != buf->bytesused) {
- idx++;
- s->sg_pending[idx].src =
- buf->dma_handle + s->sg_pending[idx - 1].size;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size =
- buf->bytesused - s->sg_pending[idx - 1].size;
- offset += s->sg_pending[idx].size;
- }
- y_done = 1;
- } else {
- s->sg_pending[idx].src = buf->dma_handle;
- s->sg_pending[idx].dst = offset;
- s->sg_pending[idx].size = buf->bytesused;
- offset += buf->bytesused;
- }
- bytes_written += buf->bytesused;
-
- /* Sync SG buffers */
- ivtv_buf_sync_for_device(s, buf);
- idx++;
- }
- s->sg_pending_size = idx;
-
- /* Sync Hardware SG List of buffers */
- ivtv_stream_sync_for_device(s);
- if (lock)
- spin_lock_irqsave(&itv->dma_reg_lock, flags);
- if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
- ivtv_dma_dec_start(s);
- }
- else {
- set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags);
- }
- if (lock)
- spin_unlock_irqrestore(&itv->dma_reg_lock, flags);
-}
-
-static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
-
- s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
- s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
- s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
- s->sg_processed++;
- /* Sync Hardware SG List of buffers */
- ivtv_stream_sync_for_device(s);
- write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR);
- write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER);
- itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
- add_timer(&itv->dma_timer);
-}
-
-static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
-
- s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src);
- s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst);
- s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000);
- s->sg_processed++;
- /* Sync Hardware SG List of buffers */
- ivtv_stream_sync_for_device(s);
- write_reg(s->sg_handle, IVTV_REG_DECDMAADDR);
- write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER);
- itv->dma_timer.expires = jiffies + msecs_to_jiffies(300);
- add_timer(&itv->dma_timer);
-}
-
-/* start the encoder DMA */
-static void ivtv_dma_enc_start(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
- struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- int i;
-
- IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name);
-
- if (s->q_predma.bytesused)
- ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
-
- if (ivtv_use_dma(s))
- s->sg_pending[s->sg_pending_size - 1].size += 256;
-
- /* If this is an MPEG stream, and VBI data is also pending, then append the
- VBI DMA to the MPEG DMA and transfer both sets of data at once.
-
- VBI DMA is a second class citizen compared to MPEG and mixing them together
- will confuse the firmware (the end of a VBI DMA is seen as the end of a
- MPEG DMA, thus effectively dropping an MPEG frame). So instead we make
- sure we only use the MPEG DMA to transfer the VBI DMA if both are in
- use. This way no conflicts occur. */
- clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size &&
- s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) {
- ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused);
- if (ivtv_use_dma(s_vbi))
- s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256;
- for (i = 0; i < s_vbi->sg_pending_size; i++) {
- s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i];
- }
- s_vbi->dma_offset = s_vbi->pending_offset;
- s_vbi->sg_pending_size = 0;
- s_vbi->dma_xfer_cnt++;
- set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags);
- IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name);
- }
-
- s->dma_xfer_cnt++;
- memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
- s->sg_processing_size = s->sg_pending_size;
- s->sg_pending_size = 0;
- s->sg_processed = 0;
- s->dma_offset = s->pending_offset;
- s->dma_backup = s->pending_backup;
- s->dma_pts = s->pending_pts;
-
- if (ivtv_use_pio(s)) {
- set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags);
- set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
- set_bit(IVTV_F_I_PIO, &itv->i_flags);
- itv->cur_pio_stream = s->type;
- }
- else {
- itv->dma_retries = 0;
- ivtv_dma_enc_start_xfer(s);
- set_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = s->type;
- }
-}
-
-static void ivtv_dma_dec_start(struct ivtv_stream *s)
-{
- struct ivtv *itv = s->itv;
-
- if (s->q_predma.bytesused)
- ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused);
- s->dma_xfer_cnt++;
- memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size);
- s->sg_processing_size = s->sg_pending_size;
- s->sg_pending_size = 0;
- s->sg_processed = 0;
-
- IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name);
- itv->dma_retries = 0;
- ivtv_dma_dec_start_xfer(s);
- set_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = s->type;
-}
-
-static void ivtv_irq_dma_read(struct ivtv *itv)
-{
- struct ivtv_stream *s = NULL;
- struct ivtv_buffer *buf;
- int hw_stream_type = 0;
-
- IVTV_DEBUG_HI_IRQ("DEC DMA READ\n");
-
- del_timer(&itv->dma_timer);
-
- if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0)
- return;
-
- if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
- s = &itv->streams[itv->cur_dma_stream];
- ivtv_stream_sync_for_cpu(s);
-
- if (read_reg(IVTV_REG_DMASTATUS) & 0x14) {
- IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n",
- read_reg(IVTV_REG_DMASTATUS),
- s->sg_processed, s->sg_processing_size, itv->dma_retries);
- write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
- if (itv->dma_retries == 3) {
- /* Too many retries, give up on this frame */
- itv->dma_retries = 0;
- s->sg_processed = s->sg_processing_size;
- }
- else {
- /* Retry, starting with the first xfer segment.
- Just retrying the current segment is not sufficient. */
- s->sg_processed = 0;
- itv->dma_retries++;
- }
- }
- if (s->sg_processed < s->sg_processing_size) {
- /* DMA next buffer */
- ivtv_dma_dec_start_xfer(s);
- return;
- }
- if (s->type == IVTV_DEC_STREAM_TYPE_YUV)
- hw_stream_type = 2;
- IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused);
-
- /* For some reason must kick the firmware, like PIO mode,
- I think this tells the firmware we are done and the size
- of the xfer so it can calculate what we need next.
- I think we can do this part ourselves but would have to
- fully calculate xfer info ourselves and not use interrupts
- */
- ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused,
- hw_stream_type);
-
- /* Free last DMA call */
- while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) {
- ivtv_buf_sync_for_cpu(s, buf);
- ivtv_enqueue(s, buf, &s->q_free);
- }
- wake_up(&s->waitq);
- }
- clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
- clear_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = -1;
- wake_up(&itv->dma_waitq);
-}
-
-static void ivtv_irq_enc_dma_complete(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s;
-
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
- IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream);
-
- del_timer(&itv->dma_timer);
-
- if (itv->cur_dma_stream < 0)
- return;
-
- s = &itv->streams[itv->cur_dma_stream];
- ivtv_stream_sync_for_cpu(s);
-
- if (data[0] & 0x18) {
- IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0],
- s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries);
- write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
- if (itv->dma_retries == 3) {
- /* Too many retries, give up on this frame */
- itv->dma_retries = 0;
- s->sg_processed = s->sg_processing_size;
- }
- else {
- /* Retry, starting with the first xfer segment.
- Just retrying the current segment is not sufficient. */
- s->sg_processed = 0;
- itv->dma_retries++;
- }
- }
- if (s->sg_processed < s->sg_processing_size) {
- /* DMA next buffer */
- ivtv_dma_enc_start_xfer(s);
- return;
- }
- clear_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = -1;
- dma_post(s);
- if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
- s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- dma_post(s);
- }
- s->sg_processing_size = 0;
- s->sg_processed = 0;
- wake_up(&itv->dma_waitq);
-}
-
-static void ivtv_irq_enc_pio_complete(struct ivtv *itv)
-{
- struct ivtv_stream *s;
-
- if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) {
- itv->cur_pio_stream = -1;
- return;
- }
- s = &itv->streams[itv->cur_pio_stream];
- IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name);
- clear_bit(IVTV_F_I_PIO, &itv->i_flags);
- itv->cur_pio_stream = -1;
- dma_post(s);
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG)
- ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0);
- else if (s->type == IVTV_ENC_STREAM_TYPE_YUV)
- ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1);
- else if (s->type == IVTV_ENC_STREAM_TYPE_PCM)
- ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2);
- clear_bit(IVTV_F_I_PIO, &itv->i_flags);
- if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) {
- s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
- dma_post(s);
- }
- wake_up(&itv->dma_waitq);
-}
-
-static void ivtv_irq_dma_err(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- u32 status;
-
- del_timer(&itv->dma_timer);
-
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data);
- status = read_reg(IVTV_REG_DMASTATUS);
- IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1],
- status, itv->cur_dma_stream);
- /*
- * We do *not* write back to the IVTV_REG_DMASTATUS register to
- * clear the error status, if either the encoder write (0x02) or
- * decoder read (0x01) bus master DMA operation do not indicate
- * completed. We can race with the DMA engine, which may have
- * transitioned to completed status *after* we read the register.
- * Setting a IVTV_REG_DMASTATUS flag back to "busy" status, after the
- * DMA engine has completed, will cause the DMA engine to stop working.
- */
- status &= 0x3;
- if (status == 0x3)
- write_reg(status, IVTV_REG_DMASTATUS);
-
- if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) &&
- itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) {
- struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream];
-
- if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
- /* retry */
- /*
- * FIXME - handle cases of DMA error similar to
- * encoder below, except conditioned on status & 0x1
- */
- ivtv_dma_dec_start(s);
- return;
- } else {
- if ((status & 0x2) == 0) {
- /*
- * CX2341x Bus Master DMA write is ongoing.
- * Reset the timer and let it complete.
- */
- itv->dma_timer.expires =
- jiffies + msecs_to_jiffies(600);
- add_timer(&itv->dma_timer);
- return;
- }
-
- if (itv->dma_retries < 3) {
- /*
- * CX2341x Bus Master DMA write has ended.
- * Retry the write, starting with the first
- * xfer segment. Just retrying the current
- * segment is not sufficient.
- */
- s->sg_processed = 0;
- itv->dma_retries++;
- ivtv_dma_enc_start_xfer(s);
- return;
- }
- /* Too many retries, give up on this one */
- }
-
- }
- if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) {
- ivtv_udma_start(itv);
- return;
- }
- clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
- clear_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = -1;
- wake_up(&itv->dma_waitq);
-}
-
-static void ivtv_irq_enc_start_cap(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s;
-
- /* Get DMA destination and size arguments from card */
- ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data);
- IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]);
-
- if (data[0] > 2 || data[1] == 0 || data[2] == 0) {
- IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n",
- data[0], data[1], data[2]);
- return;
- }
- s = &itv->streams[ivtv_stream_map[data[0]]];
- if (!stream_enc_dma_append(s, data)) {
- set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
- }
-}
-
-static void ivtv_irq_enc_vbi_cap(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s;
-
- IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n");
- s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI];
-
- if (!stream_enc_dma_append(s, data))
- set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags);
-}
-
-static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI];
-
- IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n");
- if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) &&
- !stream_enc_dma_append(s, data)) {
- set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags);
- }
-}
-
-static void ivtv_irq_dec_data_req(struct ivtv *itv)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s;
-
- /* YUV or MPG */
-
- if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) {
- ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data);
- itv->dma_data_req_size =
- 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31);
- itv->dma_data_req_offset = data[1];
- if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0)
- ivtv_yuv_frame_complete(itv);
- s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
- }
- else {
- ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data);
- itv->dma_data_req_size = min_t(u32, data[2], 0x10000);
- itv->dma_data_req_offset = data[1];
- s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
- }
- IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused,
- itv->dma_data_req_offset, itv->dma_data_req_size);
- if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) {
- set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
- }
- else {
- if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags))
- ivtv_yuv_setup_stream_frame(itv);
- clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
- ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size);
- ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0);
- }
-}
-
-static void ivtv_irq_vsync(struct ivtv *itv)
-{
- /* The vsync interrupt is unusual in that it won't clear until
- * the end of the first line for the current field, at which
- * point it clears itself. This can result in repeated vsync
- * interrupts, or a missed vsync. Read some of the registers
- * to determine the line being displayed and ensure we handle
- * one vsync per frame.
- */
- unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1;
- struct yuv_playback_info *yi = &itv->yuv_info;
- int last_dma_frame = atomic_read(&yi->next_dma_frame);
- struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame];
-
- if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n");
-
- if (((frame ^ f->sync_field) == 0 &&
- ((itv->last_vsync_field & 1) ^ f->sync_field)) ||
- (frame != (itv->last_vsync_field & 1) && !f->interlaced)) {
- int next_dma_frame = last_dma_frame;
-
- if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) {
- if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) {
- write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c);
- write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830);
- write_reg(yuv_offset[next_dma_frame] >> 4, 0x834);
- write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838);
- next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS;
- atomic_set(&yi->next_dma_frame, next_dma_frame);
- yi->fields_lapsed = -1;
- yi->running = 1;
- }
- }
- }
- if (frame != (itv->last_vsync_field & 1)) {
- static const struct v4l2_event evtop = {
- .type = V4L2_EVENT_VSYNC,
- .u.vsync.field = V4L2_FIELD_TOP,
- };
- static const struct v4l2_event evbottom = {
- .type = V4L2_EVENT_VSYNC,
- .u.vsync.field = V4L2_FIELD_BOTTOM,
- };
- struct ivtv_stream *s = ivtv_get_output_stream(itv);
-
- itv->last_vsync_field += 1;
- if (frame == 0) {
- clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
- clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
- }
- else {
- set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags);
- }
- if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) {
- set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags);
- wake_up(&itv->event_waitq);
- if (s)
- wake_up(&s->waitq);
- }
- if (s && s->vdev)
- v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom);
- wake_up(&itv->vsync_waitq);
-
- /* Send VBI to saa7127 */
- if (frame && (itv->output_mode == OUT_PASSTHROUGH ||
- test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) ||
- test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) ||
- test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) {
- set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags);
- set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
- }
-
- /* Check if we need to update the yuv registers */
- if (yi->running && (yi->yuv_forced_update || f->update)) {
- if (!f->update) {
- last_dma_frame =
- (u8)(atomic_read(&yi->next_dma_frame) -
- 1) % IVTV_YUV_BUFFERS;
- f = &yi->new_frame_info[last_dma_frame];
- }
-
- if (f->src_w) {
- yi->update_frame = last_dma_frame;
- f->update = 0;
- yi->yuv_forced_update = 0;
- set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags);
- set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags);
- }
- }
-
- yi->fields_lapsed++;
- }
-}
-
-#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT)
-
-irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
-{
- struct ivtv *itv = (struct ivtv *)dev_id;
- u32 combo;
- u32 stat;
- int i;
- u8 vsync_force = 0;
-
- spin_lock(&itv->dma_reg_lock);
- /* get contents of irq status register */
- stat = read_reg(IVTV_REG_IRQSTATUS);
-
- combo = ~itv->irqmask & stat;
-
- /* Clear out IRQ */
- if (combo) write_reg(combo, IVTV_REG_IRQSTATUS);
-
- if (0 == combo) {
- /* The vsync interrupt is unusual and clears itself. If we
- * took too long, we may have missed it. Do some checks
- */
- if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
- /* vsync is enabled, see if we're in a new field */
- if ((itv->last_vsync_field & 1) !=
- (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) {
- /* New field, looks like we missed it */
- IVTV_DEBUG_YUV("VSync interrupt missed %d\n",
- read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16);
- vsync_force = 1;
- }
- }
-
- if (!vsync_force) {
- /* No Vsync expected, wasn't for us */
- spin_unlock(&itv->dma_reg_lock);
- return IRQ_NONE;
- }
- }
-
- /* Exclude interrupts noted below from the output, otherwise the log is flooded with
- these messages */
- if (combo & ~0xff6d0400)
- IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo);
-
- if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) {
- IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n");
- }
-
- if (combo & IVTV_IRQ_DMA_READ) {
- ivtv_irq_dma_read(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) {
- ivtv_irq_enc_dma_complete(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) {
- ivtv_irq_enc_pio_complete(itv);
- }
-
- if (combo & IVTV_IRQ_DMA_ERR) {
- ivtv_irq_dma_err(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_START_CAP) {
- ivtv_irq_enc_start_cap(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_VBI_CAP) {
- ivtv_irq_enc_vbi_cap(itv);
- }
-
- if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) {
- ivtv_irq_dec_vbi_reinsert(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_EOS) {
- IVTV_DEBUG_IRQ("ENC EOS\n");
- set_bit(IVTV_F_I_EOS, &itv->i_flags);
- wake_up(&itv->eos_waitq);
- }
-
- if (combo & IVTV_IRQ_DEC_DATA_REQ) {
- ivtv_irq_dec_data_req(itv);
- }
-
- /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */
- if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) {
- ivtv_irq_vsync(itv);
- }
-
- if (combo & IVTV_IRQ_ENC_VIM_RST) {
- IVTV_DEBUG_IRQ("VIM RST\n");
- /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */
- }
-
- if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) {
- IVTV_DEBUG_INFO("Stereo mode changed\n");
- }
-
- if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) {
- itv->irq_rr_idx++;
- for (i = 0; i < IVTV_MAX_STREAMS; i++) {
- int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
- struct ivtv_stream *s = &itv->streams[idx];
-
- if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags))
- continue;
- if (s->type >= IVTV_DEC_STREAM_TYPE_MPG)
- ivtv_dma_dec_start(s);
- else
- ivtv_dma_enc_start(s);
- break;
- }
-
- if (i == IVTV_MAX_STREAMS &&
- test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags))
- ivtv_udma_start(itv);
- }
-
- if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) {
- itv->irq_rr_idx++;
- for (i = 0; i < IVTV_MAX_STREAMS; i++) {
- int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS;
- struct ivtv_stream *s = &itv->streams[idx];
-
- if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags))
- continue;
- if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG)
- ivtv_dma_enc_start(s);
- break;
- }
- }
-
- if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
- queue_kthread_work(&itv->irq_worker, &itv->irq_work);
- }
-
- spin_unlock(&itv->dma_reg_lock);
-
- /* If we've just handled a 'forced' vsync, it's safest to say it
- * wasn't ours. Another device may have triggered it at just
- * the right time.
- */
- return vsync_force ? IRQ_NONE : IRQ_HANDLED;
-}
-
-void ivtv_unfinished_dma(unsigned long arg)
-{
- struct ivtv *itv = (struct ivtv *)arg;
-
- if (!test_bit(IVTV_F_I_DMA, &itv->i_flags))
- return;
- IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream);
-
- write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS);
- clear_bit(IVTV_F_I_UDMA, &itv->i_flags);
- clear_bit(IVTV_F_I_DMA, &itv->i_flags);
- itv->cur_dma_stream = -1;
- wake_up(&itv->dma_waitq);
-}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
deleted file mode 100644
index 87990c5f0910..000000000000
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ /dev/null
@@ -1,1018 +0,0 @@
-/*
- init/start/stop/exit stream functions
- Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
- Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* License: GPL
- * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
- *
- * This file will hold API related functions, both internal (firmware api)
- * and external (v4l2, etc)
- *
- * -----
- * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com>
- * and Takeru KOMORIYA<komoriya@paken.org>
- *
- * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org>
- * using information provided by Jiun-Kuei Jung @ AVerMedia.
- */
-
-#include "ivtv-driver.h"
-#include "ivtv-fileops.h"
-#include "ivtv-queue.h"
-#include "ivtv-mailbox.h"
-#include "ivtv-ioctl.h"
-#include "ivtv-irq.h"
-#include "ivtv-yuv.h"
-#include "ivtv-cards.h"
-#include "ivtv-streams.h"
-#include "ivtv-firmware.h"
-#include <media/v4l2-event.h>
-
-static const struct v4l2_file_operations ivtv_v4l2_enc_fops = {
- .owner = THIS_MODULE,
- .read = ivtv_v4l2_read,
- .write = ivtv_v4l2_write,
- .open = ivtv_v4l2_open,
- .unlocked_ioctl = video_ioctl2,
- .release = ivtv_v4l2_close,
- .poll = ivtv_v4l2_enc_poll,
-};
-
-static const struct v4l2_file_operations ivtv_v4l2_dec_fops = {
- .owner = THIS_MODULE,
- .read = ivtv_v4l2_read,
- .write = ivtv_v4l2_write,
- .open = ivtv_v4l2_open,
- .unlocked_ioctl = video_ioctl2,
- .release = ivtv_v4l2_close,
- .poll = ivtv_v4l2_dec_poll,
-};
-
-#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */
-#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */
-#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */
-#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */
-#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */
-#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */
-
-static struct {
- const char *name;
- int vfl_type;
- int num_offset;
- int dma, pio;
- enum v4l2_buf_type buf_type;
- u32 v4l2_caps;
- const struct v4l2_file_operations *fops;
-} ivtv_stream_info[] = {
- { /* IVTV_ENC_STREAM_TYPE_MPG */
- "encoder MPG",
- VFL_TYPE_GRABBER, 0,
- PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_ENC_STREAM_TYPE_YUV */
- "encoder YUV",
- VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
- PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_ENC_STREAM_TYPE_VBI */
- "encoder VBI",
- VFL_TYPE_VBI, 0,
- PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
- V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_ENC_STREAM_TYPE_PCM */
- "encoder PCM",
- VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
- PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
- V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_ENC_STREAM_TYPE_RAD */
- "encoder radio",
- VFL_TYPE_RADIO, 0,
- PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
- V4L2_CAP_RADIO | V4L2_CAP_TUNER,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_DEC_STREAM_TYPE_MPG */
- "decoder MPG",
- VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
- PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_dec_fops
- },
- { /* IVTV_DEC_STREAM_TYPE_VBI */
- "decoder VBI",
- VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
- PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
- &ivtv_v4l2_enc_fops
- },
- { /* IVTV_DEC_STREAM_TYPE_VOUT */
- "decoder VOUT",
- VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
- PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
- V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_dec_fops
- },
- { /* IVTV_DEC_STREAM_TYPE_YUV */
- "decoder YUV",
- VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
- PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
- &ivtv_v4l2_dec_fops
- }
-};
-
-static void ivtv_stream_init(struct ivtv *itv, int type)
-{
- struct ivtv_stream *s = &itv->streams[type];
- struct video_device *vdev = s->vdev;
-
- /* we need to keep vdev, so restore it afterwards */
- memset(s, 0, sizeof(*s));
- s->vdev = vdev;
-
- /* initialize ivtv_stream fields */
- s->itv = itv;
- s->type = type;
- s->name = ivtv_stream_info[type].name;
- s->caps = ivtv_stream_info[type].v4l2_caps;
-
- if (ivtv_stream_info[type].pio)
- s->dma = PCI_DMA_NONE;
- else
- s->dma = ivtv_stream_info[type].dma;
- s->buf_size = itv->stream_buf_size[type];
- if (s->buf_size)
- s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size;
- spin_lock_init(&s->qlock);
- init_waitqueue_head(&s->waitq);
- s->sg_handle = IVTV_DMA_UNMAPPED;
- ivtv_queue_init(&s->q_free);
- ivtv_queue_init(&s->q_full);
- ivtv_queue_init(&s->q_dma);
- ivtv_queue_init(&s->q_predma);
- ivtv_queue_init(&s->q_io);
-}
-
-static int ivtv_prep_dev(struct ivtv *itv, int type)
-{
- struct ivtv_stream *s = &itv->streams[type];
- int num_offset = ivtv_stream_info[type].num_offset;
- int num = itv->instance + ivtv_first_minor + num_offset;
-
- /* These four fields are always initialized. If vdev == NULL, then
- this stream is not in use. In that case no other fields but these
- four can be used. */
- s->vdev = NULL;
- s->itv = itv;
- s->type = type;
- s->name = ivtv_stream_info[type].name;
-
- /* Check whether the radio is supported */
- if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO))
- return 0;
- if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
- return 0;
-
- /* User explicitly selected 0 buffers for these streams, so don't
- create them. */
- if (ivtv_stream_info[type].dma != PCI_DMA_NONE &&
- itv->options.kilobytes[type] == 0) {
- IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name);
- return 0;
- }
-
- ivtv_stream_init(itv, type);
-
- /* allocate and initialize the v4l2 video device structure */
- s->vdev = video_device_alloc();
- if (s->vdev == NULL) {
- IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
- return -ENOMEM;
- }
-
- snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
- itv->v4l2_dev.name, s->name);
-
- s->vdev->num = num;
- s->vdev->v4l2_dev = &itv->v4l2_dev;
- s->vdev->fops = ivtv_stream_info[type].fops;
- s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
- s->vdev->release = video_device_release;
- s->vdev->tvnorms = V4L2_STD_ALL;
- s->vdev->lock = &itv->serialize_lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &s->vdev->flags);
- set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags);
- ivtv_set_funcs(s->vdev);
- return 0;
-}
-
-/* Initialize v4l2 variables and prepare v4l2 devices */
-int ivtv_streams_setup(struct ivtv *itv)
-{
- int type;
-
- /* Setup V4L2 Devices */
- for (type = 0; type < IVTV_MAX_STREAMS; type++) {
- /* Prepare device */
- if (ivtv_prep_dev(itv, type))
- break;
-
- if (itv->streams[type].vdev == NULL)
- continue;
-
- /* Allocate Stream */
- if (ivtv_stream_alloc(&itv->streams[type]))
- break;
- }
- if (type == IVTV_MAX_STREAMS)
- return 0;
-
- /* One or more streams could not be initialized. Clean 'em all up. */
- ivtv_streams_cleanup(itv, 0);
- return -ENOMEM;
-}
-
-static int ivtv_reg_dev(struct ivtv *itv, int type)
-{
- struct ivtv_stream *s = &itv->streams[type];
- int vfl_type = ivtv_stream_info[type].vfl_type;
- const char *name;
- int num;
-
- if (s->vdev == NULL)
- return 0;
-
- num = s->vdev->num;
- /* card number + user defined offset + device offset */
- if (type != IVTV_ENC_STREAM_TYPE_MPG) {
- struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
-
- if (s_mpg->vdev)
- num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
- }
- video_set_drvdata(s->vdev, s);
-
- /* Register device. First try the desired minor, then any free one. */
- if (video_register_device_no_warn(s->vdev, vfl_type, num)) {
- IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n",
- s->name, num);
- video_device_release(s->vdev);
- s->vdev = NULL;
- return -ENOMEM;
- }
- name = video_device_node_name(s->vdev);
-
- switch (vfl_type) {
- case VFL_TYPE_GRABBER:
- IVTV_INFO("Registered device %s for %s (%d kB)\n",
- name, s->name, itv->options.kilobytes[type]);
- break;
- case VFL_TYPE_RADIO:
- IVTV_INFO("Registered device %s for %s\n",
- name, s->name);
- break;
- case VFL_TYPE_VBI:
- if (itv->options.kilobytes[type])
- IVTV_INFO("Registered device %s for %s (%d kB)\n",
- name, s->name, itv->options.kilobytes[type]);
- else
- IVTV_INFO("Registered device %s for %s\n",
- name, s->name);
- break;
- }
- return 0;
-}
-
-/* Register v4l2 devices */
-int ivtv_streams_register(struct ivtv *itv)
-{
- int type;
- int err = 0;
-
- /* Register V4L2 devices */
- for (type = 0; type < IVTV_MAX_STREAMS; type++)
- err |= ivtv_reg_dev(itv, type);
-
- if (err == 0)
- return 0;
-
- /* One or more streams could not be initialized. Clean 'em all up. */
- ivtv_streams_cleanup(itv, 1);
- return -ENOMEM;
-}
-
-/* Unregister v4l2 devices */
-void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
-{
- int type;
-
- /* Teardown all streams */
- for (type = 0; type < IVTV_MAX_STREAMS; type++) {
- struct video_device *vdev = itv->streams[type].vdev;
-
- itv->streams[type].vdev = NULL;
- if (vdev == NULL)
- continue;
-
- ivtv_stream_free(&itv->streams[type]);
- /* Unregister or release device */
- if (unregister)
- video_unregister_device(vdev);
- else
- video_device_release(vdev);
- }
-}
-
-static void ivtv_vbi_setup(struct ivtv *itv)
-{
- int raw = ivtv_raw_vbi(itv);
- u32 data[CX2341X_MBOX_MAX_DATA];
- int lines;
- int i;
-
- /* Reset VBI */
- ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
-
- /* setup VBI registers */
- if (raw)
- v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi);
- else
- v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced);
-
- /* determine number of lines and total number of VBI bytes.
- A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
- The '- 1' byte is probably an unused U or V byte. Or something...
- A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
- header, 42 data bytes + checksum (to be confirmed) */
- if (raw) {
- lines = itv->vbi.count * 2;
- } else {
- lines = itv->is_60hz ? 24 : 38;
- if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840))
- lines += 2;
- }
-
- itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
-
- /* Note: sliced vs raw flag doesn't seem to have any effect
- TODO: check mode (0x02) value with older ivtv versions. */
- data[0] = raw | 0x02 | (0xbd << 8);
-
- /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */
- data[1] = 1;
- /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */
- data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size);
- /* The start/stop codes determine which VBI lines end up in the raw VBI data area.
- The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line
- is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video)
- code. These values for raw VBI are obtained from a driver disassembly. The sliced
- start/stop codes was deduced from this, but they do not appear in the driver.
- Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54.
- However, I have no idea what these values are for. */
- if (itv->hw_flags & IVTV_HW_CX25840) {
- /* Setup VBI for the cx25840 digitizer */
- if (raw) {
- data[3] = 0x20602060;
- data[4] = 0x30703070;
- } else {
- data[3] = 0xB0F0B0F0;
- data[4] = 0xA0E0A0E0;
- }
- /* Lines per frame */
- data[5] = lines;
- /* bytes per line */
- data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size);
- } else {
- /* Setup VBI for the saa7115 digitizer */
- if (raw) {
- data[3] = 0x25256262;
- data[4] = 0x387F7F7F;
- } else {
- data[3] = 0xABABECEC;
- data[4] = 0xB6F1F1F1;
- }
- /* Lines per frame */
- data[5] = lines;
- /* bytes per line */
- data[6] = itv->vbi.enc_size / lines;
- }
-
- IVTV_DEBUG_INFO(
- "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n",
- data[0], data[1], data[2], data[5], data[6]);
-
- ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data);
-
- /* returns the VBI encoder memory area. */
- itv->vbi.enc_start = data[2];
- itv->vbi.fpi = data[0];
- if (!itv->vbi.fpi)
- itv->vbi.fpi = 1;
-
- IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n",
- itv->vbi.enc_start, data[1], itv->vbi.fpi);
-
- /* select VBI lines.
- Note that the sliced argument seems to have no effect. */
- for (i = 2; i <= 24; i++) {
- int valid;
-
- if (itv->is_60hz) {
- valid = i >= 10 && i < 22;
- } else {
- valid = i >= 6 && i < 24;
- }
- ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1,
- valid, 0 , 0, 0);
- ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000,
- valid, 0, 0, 0);
- }
-
- /* Remaining VBI questions:
- - Is it possible to select particular VBI lines only for inclusion in the MPEG
- stream? Currently you can only get the first X lines.
- - Is mixed raw and sliced VBI possible?
- - What's the meaning of the raw/sliced flag?
- - What's the meaning of params 2, 3 & 4 of the Select VBI command? */
-}
-
-int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv *itv = s->itv;
- int captype = 0, subtype = 0;
- int enable_passthrough = 0;
-
- if (s->vdev == NULL)
- return -EINVAL;
-
- IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
-
- switch (s->type) {
- case IVTV_ENC_STREAM_TYPE_MPG:
- captype = 0;
- subtype = 3;
-
- /* Stop Passthrough */
- if (itv->output_mode == OUT_PASSTHROUGH) {
- ivtv_passthrough_mode(itv, 0);
- enable_passthrough = 1;
- }
- itv->mpg_data_received = itv->vbi_data_inserted = 0;
- itv->dualwatch_jiffies = jiffies;
- itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
- itv->search_pack_header = 0;
- break;
-
- case IVTV_ENC_STREAM_TYPE_YUV:
- if (itv->output_mode == OUT_PASSTHROUGH) {
- captype = 2;
- subtype = 11; /* video+audio+decoder */
- break;
- }
- captype = 1;
- subtype = 1;
- break;
- case IVTV_ENC_STREAM_TYPE_PCM:
- captype = 1;
- subtype = 2;
- break;
- case IVTV_ENC_STREAM_TYPE_VBI:
- captype = 1;
- subtype = 4;
-
- itv->vbi.frame = 0;
- itv->vbi.inserted_frame = 0;
- memset(itv->vbi.sliced_mpeg_size,
- 0, sizeof(itv->vbi.sliced_mpeg_size));
- break;
- default:
- return -EINVAL;
- }
- s->subtype = subtype;
- s->buffers_stolen = 0;
-
- /* Clear Streamoff flags in case left from last capture */
- clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
-
- if (atomic_read(&itv->capturing) == 0) {
- int digitizer;
-
- /* Always use frame based mode. Experiments have demonstrated that byte
- stream based mode results in dropped frames and corruption. Not often,
- but occasionally. Many thanks go to Leonard Orb who spent a lot of
- effort and time trying to trace the cause of the drop outs. */
- /* 1 frame per DMA */
- /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */
- ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1);
-
- /* Stuff from Windows, we don't know what it is */
- ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0);
- /* According to the docs, this should be correct. However, this is
- untested. I don't dare enable this without having tested it.
- Only very few old cards actually have this hardware combination.
- ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1,
- ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0);
- */
- ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415);
- ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0);
- ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1);
- ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12);
-
- /* assign placeholder */
- ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
-
- if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X))
- digitizer = 0xF1;
- else if (itv->card->hw_all & IVTV_HW_SAA7114)
- digitizer = 0xEF;
- else /* cx25840 */
- digitizer = 0x140;
-
- ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer);
-
- /* Setup VBI */
- if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) {
- ivtv_vbi_setup(itv);
- }
-
- /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */
- ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400);
- itv->pgm_info_offset = data[0];
- itv->pgm_info_num = data[1];
- itv->pgm_info_write_idx = 0;
- itv->pgm_info_read_idx = 0;
-
- IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n",
- itv->pgm_info_offset, itv->pgm_info_num);
-
- /* Setup API for Stream */
- cx2341x_handler_setup(&itv->cxhdl);
-
- /* mute if capturing radio */
- if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags))
- ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
- 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
- }
-
- /* Vsync Setup */
- if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
- /* event notification (on) */
- ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1);
- ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
- }
-
- if (atomic_read(&itv->capturing) == 0) {
- /* Clear all Pending Interrupts */
- ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
-
- clear_bit(IVTV_F_I_EOS, &itv->i_flags);
-
- cx2341x_handler_set_busy(&itv->cxhdl, 1);
-
- /* Initialize Digitizer for Capture */
- /* Avoid tinny audio problem - ensure audio clocks are going */
- v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1);
- /* Avoid unpredictable PCI bus hang - disable video clocks */
- v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
- ivtv_msleep_timeout(300, 0);
- ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
- v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
- }
-
- /* begin_capture */
- if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype))
- {
- IVTV_DEBUG_WARN( "Error starting capture!\n");
- return -EINVAL;
- }
-
- /* Start Passthrough */
- if (enable_passthrough) {
- ivtv_passthrough_mode(itv, 1);
- }
-
- if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
- ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
- else
- ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
-
- /* you're live! sit back and await interrupts :) */
- atomic_inc(&itv->capturing);
- return 0;
-}
-
-static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
-{
- u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv *itv = s->itv;
- int datatype;
- u16 width;
- u16 height;
-
- if (s->vdev == NULL)
- return -EINVAL;
-
- IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
-
- width = itv->cxhdl.width;
- height = itv->cxhdl.height;
-
- /* set audio mode to left/stereo for dual/stereo mode. */
- ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
-
- /* set number of internal decoder buffers */
- ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0);
-
- /* prebuffering */
- ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1);
-
- /* extract from user packets */
- ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1);
- itv->vbi.dec_start = data[0];
-
- IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n",
- itv->vbi.dec_start, data[1]);
-
- /* set decoder source settings */
- /* Data type: 0 = mpeg from host,
- 1 = yuv from encoder,
- 2 = yuv_from_host */
- switch (s->type) {
- case IVTV_DEC_STREAM_TYPE_YUV:
- if (itv->output_mode == OUT_PASSTHROUGH) {
- datatype = 1;
- } else {
- /* Fake size to avoid switching video standard */
- datatype = 2;
- width = 720;
- height = itv->is_out_50hz ? 576 : 480;
- }
- IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype);
- break;
- case IVTV_DEC_STREAM_TYPE_MPG:
- default:
- datatype = 0;
- break;
- }
- if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype,
- width, height, itv->cxhdl.audio_properties)) {
- IVTV_DEBUG_WARN("Couldn't initialize decoder source\n");
- }
-
- /* Decoder sometimes dies here, so wait a moment */
- ivtv_msleep_timeout(10, 0);
-
- /* Known failure point for firmware, so check */
- return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream");
-}
-
-int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
-{
- struct ivtv *itv = s->itv;
- int rc;
-
- if (s->vdev == NULL)
- return -EINVAL;
-
- if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
- return 0; /* already started */
-
- IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset);
-
- rc = ivtv_setup_v4l2_decode_stream(s);
- if (rc < 0) {
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
- return rc;
- }
-
- /* set dma size to 65536 bytes */
- ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536);
-
- /* Clear Streamoff */
- clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
-
- /* Zero out decoder counters */
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]);
- writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]);
-
- /* turn on notification of dual/stereo mode change */
- ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
-
- /* start playback */
- ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0);
-
- /* Let things settle before we actually start */
- ivtv_msleep_timeout(10, 0);
-
- /* Clear the following Interrupt mask bits for decoding */
- ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
- IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask);
-
- /* you're live! sit back and await interrupts :) */
- atomic_inc(&itv->decoding);
- return 0;
-}
-
-void ivtv_stop_all_captures(struct ivtv *itv)
-{
- int i;
-
- for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
- struct ivtv_stream *s = &itv->streams[i];
-
- if (s->vdev == NULL)
- continue;
- if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
- ivtv_stop_v4l2_encode_stream(s, 0);
- }
- }
-}
-
-int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
-{
- struct ivtv *itv = s->itv;
- DECLARE_WAITQUEUE(wait, current);
- int cap_type;
- int stopmode;
-
- if (s->vdev == NULL)
- return -EINVAL;
-
- /* This function assumes that you are allowed to stop the capture
- and that we are actually capturing */
-
- IVTV_DEBUG_INFO("Stop Capture\n");
-
- if (s->type == IVTV_DEC_STREAM_TYPE_VOUT)
- return 0;
- if (atomic_read(&itv->capturing) == 0)
- return 0;
-
- switch (s->type) {
- case IVTV_ENC_STREAM_TYPE_YUV:
- cap_type = 1;
- break;
- case IVTV_ENC_STREAM_TYPE_PCM:
- cap_type = 1;
- break;
- case IVTV_ENC_STREAM_TYPE_VBI:
- cap_type = 1;
- break;
- case IVTV_ENC_STREAM_TYPE_MPG:
- default:
- cap_type = 0;
- break;
- }
-
- /* Stop Capture Mode */
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
- stopmode = 0;
- } else {
- stopmode = 1;
- }
-
- /* end_capture */
- /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */
- ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype);
-
- if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) {
- if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) {
- /* only run these if we're shutting down the last cap */
- unsigned long duration;
- unsigned long then = jiffies;
-
- add_wait_queue(&itv->eos_waitq, &wait);
-
- set_current_state(TASK_INTERRUPTIBLE);
-
- /* wait 2s for EOS interrupt */
- while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) &&
- time_before(jiffies,
- then + msecs_to_jiffies(2000))) {
- schedule_timeout(msecs_to_jiffies(10));
- }
-
- /* To convert jiffies to ms, we must multiply by 1000
- * and divide by HZ. To avoid runtime division, we
- * convert this to multiplication by 1000/HZ.
- * Since integer division truncates, we get the best
- * accuracy if we do a rounding calculation of the constant.
- * Think of the case where HZ is 1024.
- */
- duration = ((1000 + HZ / 2) / HZ) * (jiffies - then);
-
- if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) {
- IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name);
- IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration);
- } else {
- IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&itv->eos_waitq, &wait);
- set_bit(IVTV_F_S_STREAMOFF, &s->s_flags);
- }
-
- /* Handle any pending interrupts */
- ivtv_msleep_timeout(100, 0);
- }
-
- atomic_dec(&itv->capturing);
-
- /* Clear capture and no-read bits */
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
-
- if (s->type == IVTV_ENC_STREAM_TYPE_VBI)
- ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP);
-
- if (atomic_read(&itv->capturing) > 0) {
- return 0;
- }
-
- cx2341x_handler_set_busy(&itv->cxhdl, 0);
-
- /* Set the following Interrupt mask bits for capture */
- ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
- del_timer(&itv->dma_timer);
-
- /* event notification (off) */
- if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) {
- /* type: 0 = refresh */
- /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */
- ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1);
- ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST);
- }
-
- /* Raw-passthrough is implied on start. Make sure it's stopped so
- the encoder will re-initialize when next started */
- ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7);
-
- wake_up(&s->waitq);
-
- return 0;
-}
-
-int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
-{
- static const struct v4l2_event ev = {
- .type = V4L2_EVENT_EOS,
- };
- struct ivtv *itv = s->itv;
-
- if (s->vdev == NULL)
- return -EINVAL;
-
- if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
- return -EINVAL;
-
- if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags))
- return 0;
-
- IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
-
- /* Stop Decoder */
- if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
- u32 tmp = 0;
-
- /* Wait until the decoder is no longer running */
- if (pts) {
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3,
- 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32));
- }
- while (1) {
- u32 data[CX2341X_MBOX_MAX_DATA];
- ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0);
- if (s->q_full.buffers + s->q_dma.buffers == 0) {
- if (tmp == data[3])
- break;
- tmp = data[3];
- }
- if (ivtv_msleep_timeout(100, 1))
- break;
- }
- }
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
-
- /* turn off notification of dual/stereo mode change */
- ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
-
- ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
- del_timer(&itv->dma_timer);
-
- clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags);
- clear_bit(IVTV_F_S_STREAMING, &s->s_flags);
- ivtv_flush_queues(s);
-
- /* decoder needs time to settle */
- ivtv_msleep_timeout(40, 0);
-
- /* decrement decoding */
- atomic_dec(&itv->decoding);
-
- set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags);
- wake_up(&itv->event_waitq);
- v4l2_event_queue(s->vdev, &ev);
-
- /* wake up wait queues */
- wake_up(&s->waitq);
-
- return 0;
-}
-
-int ivtv_passthrough_mode(struct ivtv *itv, int enable)
-{
- struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
- struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
-
- if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
- return -EINVAL;
-
- IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
-
- /* Prevent others from starting/stopping streams while we
- initiate/terminate passthrough mode */
- if (enable) {
- if (itv->output_mode == OUT_PASSTHROUGH) {
- return 0;
- }
- if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH)
- return -EBUSY;
-
- /* Fully initialize stream, and then unflag init */
- set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
- set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
-
- /* Setup YUV Decoder */
- ivtv_setup_v4l2_decode_stream(dec_stream);
-
- /* Start Decoder */
- ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1);
- atomic_inc(&itv->decoding);
-
- /* Setup capture if not already done */
- if (atomic_read(&itv->capturing) == 0) {
- cx2341x_handler_setup(&itv->cxhdl);
- cx2341x_handler_set_busy(&itv->cxhdl, 1);
- }
-
- /* Start Passthrough Mode */
- ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11);
- atomic_inc(&itv->capturing);
- return 0;
- }
-
- if (itv->output_mode != OUT_PASSTHROUGH)
- return 0;
-
- /* Stop Passthrough Mode */
- ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11);
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0);
-
- atomic_dec(&itv->capturing);
- atomic_dec(&itv->decoding);
- clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags);
- clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags);
- itv->output_mode = OUT_NONE;
- if (atomic_read(&itv->capturing) == 0)
- cx2341x_handler_set_busy(&itv->cxhdl, 0);
-
- return 0;
-}
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
deleted file mode 100644
index ee7ca2dcca2f..000000000000
--- a/drivers/media/video/ks0127.c
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * Video Capture Driver (Video for Linux 1/2)
- * for the Matrox Marvel G200,G400 and Rainbow Runner-G series
- *
- * This module is an interface to the KS0127 video decoder chip.
- *
- * Copyright (C) 1999 Ryan Drake <stiletto@mediaone.net>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- *****************************************************************************
- *
- * Modified and extended by
- * Mike Bernson <mike@mlb.org>
- * Gerard v.d. Horst
- * Leon van Stuivenberg <l.vanstuivenberg@chello.nl>
- * Gernot Ziegler <gz@lysator.liu.se>
- *
- * Version History:
- * V1.0 Ryan Drake Initial version by Ryan Drake
- * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/slab.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include "ks0127.h"
-
-MODULE_DESCRIPTION("KS0127 video decoder driver");
-MODULE_AUTHOR("Ryan Drake");
-MODULE_LICENSE("GPL");
-
-/* Addresses */
-#define I2C_KS0127_ADDON 0xD8
-#define I2C_KS0127_ONBOARD 0xDA
-
-
-/* ks0127 control registers */
-#define KS_STAT 0x00
-#define KS_CMDA 0x01
-#define KS_CMDB 0x02
-#define KS_CMDC 0x03
-#define KS_CMDD 0x04
-#define KS_HAVB 0x05
-#define KS_HAVE 0x06
-#define KS_HS1B 0x07
-#define KS_HS1E 0x08
-#define KS_HS2B 0x09
-#define KS_HS2E 0x0a
-#define KS_AGC 0x0b
-#define KS_HXTRA 0x0c
-#define KS_CDEM 0x0d
-#define KS_PORTAB 0x0e
-#define KS_LUMA 0x0f
-#define KS_CON 0x10
-#define KS_BRT 0x11
-#define KS_CHROMA 0x12
-#define KS_CHROMB 0x13
-#define KS_DEMOD 0x14
-#define KS_SAT 0x15
-#define KS_HUE 0x16
-#define KS_VERTIA 0x17
-#define KS_VERTIB 0x18
-#define KS_VERTIC 0x19
-#define KS_HSCLL 0x1a
-#define KS_HSCLH 0x1b
-#define KS_VSCLL 0x1c
-#define KS_VSCLH 0x1d
-#define KS_OFMTA 0x1e
-#define KS_OFMTB 0x1f
-#define KS_VBICTL 0x20
-#define KS_CCDAT2 0x21
-#define KS_CCDAT1 0x22
-#define KS_VBIL30 0x23
-#define KS_VBIL74 0x24
-#define KS_VBIL118 0x25
-#define KS_VBIL1512 0x26
-#define KS_TTFRAM 0x27
-#define KS_TESTA 0x28
-#define KS_UVOFFH 0x29
-#define KS_UVOFFL 0x2a
-#define KS_UGAIN 0x2b
-#define KS_VGAIN 0x2c
-#define KS_VAVB 0x2d
-#define KS_VAVE 0x2e
-#define KS_CTRACK 0x2f
-#define KS_POLCTL 0x30
-#define KS_REFCOD 0x31
-#define KS_INVALY 0x32
-#define KS_INVALU 0x33
-#define KS_INVALV 0x34
-#define KS_UNUSEY 0x35
-#define KS_UNUSEU 0x36
-#define KS_UNUSEV 0x37
-#define KS_USRSAV 0x38
-#define KS_USREAV 0x39
-#define KS_SHS1A 0x3a
-#define KS_SHS1B 0x3b
-#define KS_SHS1C 0x3c
-#define KS_CMDE 0x3d
-#define KS_VSDEL 0x3e
-#define KS_CMDF 0x3f
-#define KS_GAMMA0 0x40
-#define KS_GAMMA1 0x41
-#define KS_GAMMA2 0x42
-#define KS_GAMMA3 0x43
-#define KS_GAMMA4 0x44
-#define KS_GAMMA5 0x45
-#define KS_GAMMA6 0x46
-#define KS_GAMMA7 0x47
-#define KS_GAMMA8 0x48
-#define KS_GAMMA9 0x49
-#define KS_GAMMA10 0x4a
-#define KS_GAMMA11 0x4b
-#define KS_GAMMA12 0x4c
-#define KS_GAMMA13 0x4d
-#define KS_GAMMA14 0x4e
-#define KS_GAMMA15 0x4f
-#define KS_GAMMA16 0x50
-#define KS_GAMMA17 0x51
-#define KS_GAMMA18 0x52
-#define KS_GAMMA19 0x53
-#define KS_GAMMA20 0x54
-#define KS_GAMMA21 0x55
-#define KS_GAMMA22 0x56
-#define KS_GAMMA23 0x57
-#define KS_GAMMA24 0x58
-#define KS_GAMMA25 0x59
-#define KS_GAMMA26 0x5a
-#define KS_GAMMA27 0x5b
-#define KS_GAMMA28 0x5c
-#define KS_GAMMA29 0x5d
-#define KS_GAMMA30 0x5e
-#define KS_GAMMA31 0x5f
-#define KS_GAMMAD0 0x60
-#define KS_GAMMAD1 0x61
-#define KS_GAMMAD2 0x62
-#define KS_GAMMAD3 0x63
-#define KS_GAMMAD4 0x64
-#define KS_GAMMAD5 0x65
-#define KS_GAMMAD6 0x66
-#define KS_GAMMAD7 0x67
-#define KS_GAMMAD8 0x68
-#define KS_GAMMAD9 0x69
-#define KS_GAMMAD10 0x6a
-#define KS_GAMMAD11 0x6b
-#define KS_GAMMAD12 0x6c
-#define KS_GAMMAD13 0x6d
-#define KS_GAMMAD14 0x6e
-#define KS_GAMMAD15 0x6f
-#define KS_GAMMAD16 0x70
-#define KS_GAMMAD17 0x71
-#define KS_GAMMAD18 0x72
-#define KS_GAMMAD19 0x73
-#define KS_GAMMAD20 0x74
-#define KS_GAMMAD21 0x75
-#define KS_GAMMAD22 0x76
-#define KS_GAMMAD23 0x77
-#define KS_GAMMAD24 0x78
-#define KS_GAMMAD25 0x79
-#define KS_GAMMAD26 0x7a
-#define KS_GAMMAD27 0x7b
-#define KS_GAMMAD28 0x7c
-#define KS_GAMMAD29 0x7d
-#define KS_GAMMAD30 0x7e
-#define KS_GAMMAD31 0x7f
-
-
-/****************************************************************************
-* mga_dev : represents one ks0127 chip.
-****************************************************************************/
-
-struct adjust {
- int contrast;
- int bright;
- int hue;
- int ugain;
- int vgain;
-};
-
-struct ks0127 {
- struct v4l2_subdev sd;
- v4l2_std_id norm;
- int ident;
- u8 regs[256];
-};
-
-static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct ks0127, sd);
-}
-
-
-static int debug; /* insmod parameter */
-
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug output");
-
-static u8 reg_defaults[64];
-
-static void init_reg_defaults(void)
-{
- static int initialized;
- u8 *table = reg_defaults;
-
- if (initialized)
- return;
- initialized = 1;
-
- table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */
- table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */
- table[KS_CMDC] = 0x00; /* Test options */
- /* clock & input select, write 1 to PORTA */
- table[KS_CMDD] = 0x01;
- table[KS_HAVB] = 0x00; /* HAV Start Control */
- table[KS_HAVE] = 0x00; /* HAV End Control */
- table[KS_HS1B] = 0x10; /* HS1 Start Control */
- table[KS_HS1E] = 0x00; /* HS1 End Control */
- table[KS_HS2B] = 0x00; /* HS2 Start Control */
- table[KS_HS2E] = 0x00; /* HS2 End Control */
- table[KS_AGC] = 0x53; /* Manual setting for AGC */
- table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */
- table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */
- table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */
- table[KS_LUMA] = 0x01; /* Luma control */
- table[KS_CON] = 0x00; /* Contrast Control */
- table[KS_BRT] = 0x00; /* Brightness Control */
- table[KS_CHROMA] = 0x2a; /* Chroma control A */
- table[KS_CHROMB] = 0x90; /* Chroma control B */
- table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */
- table[KS_SAT] = 0x00; /* Color Saturation Control*/
- table[KS_HUE] = 0x00; /* Hue Control */
- table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */
- /* Vertical Processing Control B, luma 1 line delayed */
- table[KS_VERTIB] = 0x12;
- table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */
- table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */
- table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */
- table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */
- table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */
- /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */
- table[KS_OFMTA] = 0x30;
- table[KS_OFMTB] = 0x00; /* Output Control B */
- /* VBI Decoder Control; 4bit fmt: avoid Y overflow */
- table[KS_VBICTL] = 0x5d;
- table[KS_CCDAT2] = 0x00; /* Read Only register */
- table[KS_CCDAT1] = 0x00; /* Read Only register */
- table[KS_VBIL30] = 0xa8; /* VBI data decoding options */
- table[KS_VBIL74] = 0xaa; /* VBI data decoding options */
- table[KS_VBIL118] = 0x2a; /* VBI data decoding options */
- table[KS_VBIL1512] = 0x00; /* VBI data decoding options */
- table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */
- table[KS_TESTA] = 0x00; /* test register, shouldn't be written */
- table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */
- table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */
- table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */
- table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */
- table[KS_VAVB] = 0x07; /* VAV Begin */
- table[KS_VAVE] = 0x00; /* VAV End */
- table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */
- table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */
- table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */
- table[KS_INVALY] = 0x10; /* Invalid Y Code */
- table[KS_INVALU] = 0x80; /* Invalid U Code */
- table[KS_INVALV] = 0x80; /* Invalid V Code */
- table[KS_UNUSEY] = 0x10; /* Unused Y Code */
- table[KS_UNUSEU] = 0x80; /* Unused U Code */
- table[KS_UNUSEV] = 0x80; /* Unused V Code */
- table[KS_USRSAV] = 0x00; /* reserved */
- table[KS_USREAV] = 0x00; /* reserved */
- table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */
- /* User Defined SHS1 B, ALT656=1 on 0127B */
- table[KS_SHS1B] = 0x80;
- table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */
- table[KS_CMDE] = 0x00; /* Command Register E */
- table[KS_VSDEL] = 0x00; /* VS Delay Control */
- /* Command Register F, update -immediately- */
- /* (there might come no vsync)*/
- table[KS_CMDF] = 0x02;
-}
-
-
-/* We need to manually read because of a bug in the KS0127 chip.
- *
- * An explanation from kayork@mail.utexas.edu:
- *
- * During I2C reads, the KS0127 only samples for a stop condition
- * during the place where the acknowledge bit should be. Any standard
- * I2C implementation (correctly) throws in another clock transition
- * at the 9th bit, and the KS0127 will not recognize the stop condition
- * and will continue to clock out data.
- *
- * So we have to do the read ourself. Big deal.
- * workaround in i2c-algo-bit
- */
-
-
-static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- char val = 0;
- struct i2c_msg msgs[] = {
- { client->addr, 0, sizeof(reg), &reg },
- { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val }
- };
- int ret;
-
- ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret != ARRAY_SIZE(msgs))
- v4l2_dbg(1, debug, sd, "read error\n");
-
- return val;
-}
-
-
-static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ks0127 *ks = to_ks0127(sd);
- char msg[] = { reg, val };
-
- if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg))
- v4l2_dbg(1, debug, sd, "write error\n");
-
- ks->regs[reg] = val;
-}
-
-
-/* generic bit-twiddling */
-static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v)
-{
- struct ks0127 *ks = to_ks0127(sd);
-
- u8 val = ks->regs[reg];
- val = (val & and_v) | or_v;
- ks0127_write(sd, reg, val);
-}
-
-
-
-/****************************************************************************
-* ks0127 private api
-****************************************************************************/
-static void ks0127_init(struct v4l2_subdev *sd)
-{
- struct ks0127 *ks = to_ks0127(sd);
- u8 *table = reg_defaults;
- int i;
-
- ks->ident = V4L2_IDENT_KS0127;
-
- v4l2_dbg(1, debug, sd, "reset\n");
- msleep(1);
-
- /* initialize all registers to known values */
- /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */
-
- for (i = 1; i < 33; i++)
- ks0127_write(sd, i, table[i]);
-
- for (i = 35; i < 40; i++)
- ks0127_write(sd, i, table[i]);
-
- for (i = 41; i < 56; i++)
- ks0127_write(sd, i, table[i]);
-
- for (i = 58; i < 64; i++)
- ks0127_write(sd, i, table[i]);
-
-
- if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) {
- ks->ident = V4L2_IDENT_KS0122S;
- v4l2_dbg(1, debug, sd, "ks0122s found\n");
- return;
- }
-
- switch (ks0127_read(sd, KS_CMDE) & 0x0f) {
- case 0:
- v4l2_dbg(1, debug, sd, "ks0127 found\n");
- break;
-
- case 9:
- ks->ident = V4L2_IDENT_KS0127B;
- v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n");
- break;
-
- default:
- v4l2_dbg(1, debug, sd, "unknown revision\n");
- break;
- }
-}
-
-static int ks0127_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct ks0127 *ks = to_ks0127(sd);
-
- switch (input) {
- case KS_INPUT_COMPOSITE_1:
- case KS_INPUT_COMPOSITE_2:
- case KS_INPUT_COMPOSITE_3:
- case KS_INPUT_COMPOSITE_4:
- case KS_INPUT_COMPOSITE_5:
- case KS_INPUT_COMPOSITE_6:
- v4l2_dbg(1, debug, sd,
- "s_routing %d: Composite\n", input);
- /* autodetect 50/60 Hz */
- ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
- /* VSE=0 */
- ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
- /* set input line */
- ks0127_and_or(sd, KS_CMDB, 0xb0, input);
- /* non-freerunning mode */
- ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
- /* analog input */
- ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
- /* enable chroma demodulation */
- ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
- /* chroma trap, HYBWR=1 */
- ks0127_and_or(sd, KS_LUMA, 0x00,
- (reg_defaults[KS_LUMA])|0x0c);
- /* scaler fullbw, luma comb off */
- ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
- /* manual chroma comb .25 .5 .25 */
- ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90);
-
- /* chroma path delay */
- ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90);
-
- ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
- ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
- ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
- ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
- break;
-
- case KS_INPUT_SVIDEO_1:
- case KS_INPUT_SVIDEO_2:
- case KS_INPUT_SVIDEO_3:
- v4l2_dbg(1, debug, sd,
- "s_routing %d: S-Video\n", input);
- /* autodetect 50/60 Hz */
- ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
- /* VSE=0 */
- ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
- /* set input line */
- ks0127_and_or(sd, KS_CMDB, 0xb0, input);
- /* non-freerunning mode */
- ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
- /* analog input */
- ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
- /* enable chroma demodulation */
- ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
- ks0127_and_or(sd, KS_LUMA, 0x00,
- reg_defaults[KS_LUMA]);
- /* disable luma comb */
- ks0127_and_or(sd, KS_VERTIA, 0x08,
- (reg_defaults[KS_VERTIA]&0xf0)|0x01);
- ks0127_and_or(sd, KS_VERTIC, 0x0f,
- reg_defaults[KS_VERTIC]&0xf0);
-
- ks0127_and_or(sd, KS_CHROMB, 0x0f,
- reg_defaults[KS_CHROMB]&0xf0);
-
- ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
- ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
- ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
- ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
- break;
-
- case KS_INPUT_YUV656:
- v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n");
- if (ks->norm & V4L2_STD_525_60)
- /* force 60 Hz */
- ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03);
- else
- /* force 50 Hz */
- ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02);
-
- ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */
- /* set input line and VALIGN */
- ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40));
- /* freerunning mode, */
- /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/
- ks0127_and_or(sd, KS_CMDC, 0x70, 0x87);
- /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */
- ks0127_and_or(sd, KS_CMDD, 0x03, 0x08);
- /* disable chroma demodulation */
- ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30);
- /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */
- ks0127_and_or(sd, KS_LUMA, 0x00, 0x71);
- ks0127_and_or(sd, KS_VERTIC, 0x0f,
- reg_defaults[KS_VERTIC]&0xf0);
-
- /* scaler fullbw, luma comb off */
- ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
-
- ks0127_and_or(sd, KS_CHROMB, 0x0f,
- reg_defaults[KS_CHROMB]&0xf0);
-
- ks0127_and_or(sd, KS_CON, 0x00, 0x00);
- ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */
- /* spec: 229 (e5) */
- ks0127_and_or(sd, KS_SAT, 0x00, 0xe8);
- ks0127_and_or(sd, KS_HUE, 0x00, 0);
-
- ks0127_and_or(sd, KS_UGAIN, 0x00, 238);
- ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00);
-
- /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */
- ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f);
- ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00);
- break;
-
- default:
- v4l2_dbg(1, debug, sd,
- "s_routing: Unknown input %d\n", input);
- break;
- }
-
- /* hack: CDMLPF sometimes spontaneously switches on; */
- /* force back off */
- ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]);
- return 0;
-}
-
-static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct ks0127 *ks = to_ks0127(sd);
-
- /* Set to automatic SECAM/Fsc mode */
- ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
-
- ks->norm = std;
- if (std & V4L2_STD_NTSC) {
- v4l2_dbg(1, debug, sd,
- "s_std: NTSC_M\n");
- ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
- } else if (std & V4L2_STD_PAL_N) {
- v4l2_dbg(1, debug, sd,
- "s_std: NTSC_N (fixme)\n");
- ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
- } else if (std & V4L2_STD_PAL) {
- v4l2_dbg(1, debug, sd,
- "s_std: PAL_N\n");
- ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
- } else if (std & V4L2_STD_PAL_M) {
- v4l2_dbg(1, debug, sd,
- "s_std: PAL_M (fixme)\n");
- ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
- } else if (std & V4L2_STD_SECAM) {
- v4l2_dbg(1, debug, sd,
- "s_std: SECAM\n");
-
- /* set to secam autodetection */
- ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20);
- ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
- schedule_timeout_interruptible(HZ/10+1);
-
- /* did it autodetect? */
- if (!(ks0127_read(sd, KS_DEMOD) & 0x40))
- /* force to secam mode */
- ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f);
- } else {
- v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n",
- (unsigned long long)std);
- }
- return 0;
-}
-
-static int ks0127_s_stream(struct v4l2_subdev *sd, int enable)
-{
- v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable);
- if (enable) {
- /* All output pins on */
- ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30);
- /* Obey the OEN pin */
- ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00);
- } else {
- /* Video output pins off */
- ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00);
- /* Ignore the OEN pin */
- ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80);
- }
- return 0;
-}
-
-static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
-{
- int stat = V4L2_IN_ST_NO_SIGNAL;
- u8 status;
- v4l2_std_id std = V4L2_STD_ALL;
-
- status = ks0127_read(sd, KS_STAT);
- if (!(status & 0x20)) /* NOVID not set */
- stat = 0;
- if (!(status & 0x01)) /* CLOCK set */
- stat |= V4L2_IN_ST_NO_COLOR;
- if ((status & 0x08)) /* PALDET set */
- std = V4L2_STD_PAL;
- else
- std = V4L2_STD_NTSC;
- if (pstd)
- *pstd = std;
- if (pstatus)
- *pstatus = stat;
- return 0;
-}
-
-static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- v4l2_dbg(1, debug, sd, "querystd\n");
- return ks0127_status(sd, NULL, std);
-}
-
-static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
-{
- v4l2_dbg(1, debug, sd, "g_input_status\n");
- return ks0127_status(sd, status, NULL);
-}
-
-static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ks0127 *ks = to_ks0127(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops ks0127_core_ops = {
- .g_chip_ident = ks0127_g_chip_ident,
- .s_std = ks0127_s_std,
-};
-
-static const struct v4l2_subdev_video_ops ks0127_video_ops = {
- .s_routing = ks0127_s_routing,
- .s_stream = ks0127_s_stream,
- .querystd = ks0127_querystd,
- .g_input_status = ks0127_g_input_status,
-};
-
-static const struct v4l2_subdev_ops ks0127_ops = {
- .core = &ks0127_core_ops,
- .video = &ks0127_video_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-
-static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct ks0127 *ks;
- struct v4l2_subdev *sd;
-
- v4l_info(client, "%s chip found @ 0x%x (%s)\n",
- client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
- client->addr << 1, client->adapter->name);
-
- ks = kzalloc(sizeof(*ks), GFP_KERNEL);
- if (ks == NULL)
- return -ENOMEM;
- sd = &ks->sd;
- v4l2_i2c_subdev_init(sd, client, &ks0127_ops);
-
- /* power up */
- init_reg_defaults();
- ks0127_write(sd, KS_CMDA, 0x2c);
- mdelay(10);
-
- /* reset the device */
- ks0127_init(sd);
- return 0;
-}
-
-static int ks0127_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */
- ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */
- kfree(to_ks0127(sd));
- return 0;
-}
-
-static const struct i2c_device_id ks0127_id[] = {
- { "ks0127", 0 },
- { "ks0127b", 0 },
- { "ks0122s", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ks0127_id);
-
-static struct i2c_driver ks0127_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "ks0127",
- },
- .probe = ks0127_probe,
- .remove = ks0127_remove,
- .id_table = ks0127_id,
-};
-
-module_i2c_driver(ks0127_driver);
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
deleted file mode 100644
index 527e7b2965df..000000000000
--- a/drivers/media/video/m5mols/m5mols.h
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Header for M-5MOLS 8M Pixel camera sensor with ISP
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim <riverful.kim@samsung.com>
- *
- * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim <dongsoo45.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 version.
- */
-
-#ifndef M5MOLS_H
-#define M5MOLS_H
-
-#include <media/v4l2-subdev.h>
-#include "m5mols_reg.h"
-
-extern int m5mols_debug;
-
-enum m5mols_restype {
- M5MOLS_RESTYPE_MONITOR,
- M5MOLS_RESTYPE_CAPTURE,
- M5MOLS_RESTYPE_MAX,
-};
-
-/**
- * struct m5mols_resolution - structure for the resolution
- * @type: resolution type according to the pixel code
- * @width: width of the resolution
- * @height: height of the resolution
- * @reg: resolution preset register value
- */
-struct m5mols_resolution {
- u8 reg;
- enum m5mols_restype type;
- u16 width;
- u16 height;
-};
-
-/**
- * struct m5mols_exif - structure for the EXIF information of M-5MOLS
- * @exposure_time: exposure time register value
- * @shutter_speed: speed of the shutter register value
- * @aperture: aperture register value
- * @exposure_bias: it calls also EV bias
- * @iso_speed: ISO register value
- * @flash: status register value of the flash
- * @sdr: status register value of the Subject Distance Range
- * @qval: not written exact meaning in document
- */
-struct m5mols_exif {
- u32 exposure_time;
- u32 shutter_speed;
- u32 aperture;
- u32 brightness;
- u32 exposure_bias;
- u16 iso_speed;
- u16 flash;
- u16 sdr;
- u16 qval;
-};
-
-/**
- * struct m5mols_capture - Structure for the capture capability
- * @exif: EXIF information
- * @main: size in bytes of the main image
- * @thumb: size in bytes of the thumb image, if it was accompanied
- * @total: total size in bytes of the produced image
- */
-struct m5mols_capture {
- struct m5mols_exif exif;
- u32 main;
- u32 thumb;
- u32 total;
-};
-
-/**
- * struct m5mols_scenemode - structure for the scenemode capability
- * @metering: metering light register value
- * @ev_bias: EV bias register value
- * @wb_mode: mode which means the WhiteBalance is Auto or Manual
- * @wb_preset: whitebalance preset register value in the Manual mode
- * @chroma_en: register value whether the Chroma capability is enabled or not
- * @chroma_lvl: chroma's level register value
- * @edge_en: register value Whether the Edge capability is enabled or not
- * @edge_lvl: edge's level register value
- * @af_range: Auto Focus's range
- * @fd_mode: Face Detection mode
- * @mcc: Multi-axis Color Conversion which means emotion color
- * @light: status of the Light
- * @flash: status of the Flash
- * @tone: Tone color which means Contrast
- * @iso: ISO register value
- * @capt_mode: Mode of the Image Stabilization while the camera capturing
- * @wdr: Wide Dynamic Range register value
- *
- * The each value according to each scenemode is recommended in the documents.
- */
-struct m5mols_scenemode {
- u8 metering;
- u8 ev_bias;
- u8 wb_mode;
- u8 wb_preset;
- u8 chroma_en;
- u8 chroma_lvl;
- u8 edge_en;
- u8 edge_lvl;
- u8 af_range;
- u8 fd_mode;
- u8 mcc;
- u8 light;
- u8 flash;
- u8 tone;
- u8 iso;
- u8 capt_mode;
- u8 wdr;
-};
-
-/**
- * struct m5mols_version - firmware version information
- * @customer: customer information
- * @project: version of project information according to customer
- * @fw: firmware revision
- * @hw: hardware revision
- * @param: version of the parameter
- * @awb: Auto WhiteBalance algorithm version
- * @str: information about manufacturer and packaging vendor
- * @af: Auto Focus version
- *
- * The register offset starts the customer version at 0x0, and it ends
- * the awb version at 0x09. The customer, project information occupies 1 bytes
- * each. And also the fw, hw, param, awb each requires 2 bytes. The str is
- * unique string associated with firmware's version. It includes information
- * about manufacturer and the vendor of the sensor's packaging. The least
- * significant 2 bytes of the string indicate packaging manufacturer.
- */
-#define VERSION_STRING_SIZE 22
-struct m5mols_version {
- u8 customer;
- u8 project;
- u16 fw;
- u16 hw;
- u16 param;
- u16 awb;
- u8 str[VERSION_STRING_SIZE];
- u8 af;
-};
-
-/**
- * struct m5mols_info - M-5MOLS driver data structure
- * @pdata: platform data
- * @sd: v4l-subdev instance
- * @pad: media pad
- * @ffmt: current fmt according to resolution type
- * @res_type: current resolution type
- * @irq_waitq: waitqueue for the capture
- * @irq_done: set to 1 in the interrupt handler
- * @handle: control handler
- * @auto_exposure: auto/manual exposure control
- * @exposure_bias: exposure compensation control
- * @exposure: manual exposure control
- * @metering: exposure metering control
- * @auto_iso: auto/manual ISO sensitivity control
- * @iso: manual ISO sensitivity control
- * @auto_wb: auto white balance control
- * @lock_3a: 3A lock control
- * @colorfx: color effect control
- * @saturation: saturation control
- * @zoom: zoom control
- * @wdr: wide dynamic range control
- * @stabilization: image stabilization control
- * @jpeg_quality: JPEG compression quality control
- * @ver: information of the version
- * @cap: the capture mode attributes
- * @isp_ready: 1 when the ISP controller has completed booting
- * @power: current sensor's power status
- * @ctrl_sync: 1 when the control handler state is restored in H/W
- * @resolution: register value for current resolution
- * @mode: register value for current operation mode
- * @set_power: optional power callback to the board code
- */
-struct m5mols_info {
- const struct m5mols_platform_data *pdata;
- struct v4l2_subdev sd;
- struct media_pad pad;
- struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX];
- int res_type;
-
- wait_queue_head_t irq_waitq;
- atomic_t irq_done;
-
- struct v4l2_ctrl_handler handle;
- struct {
- /* exposure/exposure bias/auto exposure cluster */
- struct v4l2_ctrl *auto_exposure;
- struct v4l2_ctrl *exposure_bias;
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *metering;
- };
- struct {
- /* iso/auto iso cluster */
- struct v4l2_ctrl *auto_iso;
- struct v4l2_ctrl *iso;
- };
- struct v4l2_ctrl *auto_wb;
-
- struct v4l2_ctrl *lock_3a;
- struct v4l2_ctrl *colorfx;
- struct v4l2_ctrl *saturation;
- struct v4l2_ctrl *zoom;
- struct v4l2_ctrl *wdr;
- struct v4l2_ctrl *stabilization;
- struct v4l2_ctrl *jpeg_quality;
-
- struct m5mols_version ver;
- struct m5mols_capture cap;
-
- unsigned int isp_ready:1;
- unsigned int power:1;
- unsigned int ctrl_sync:1;
-
- u8 resolution;
- u8 mode;
-
- int (*set_power)(struct device *dev, int on);
-};
-
-#define is_available_af(__info) (__info->ver.af)
-#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code)
-#define is_manufacturer(__info, __manufacturer) \
- (__info->ver.str[0] == __manufacturer[0] && \
- __info->ver.str[1] == __manufacturer[1])
-/*
- * I2C operation of the M-5MOLS
- *
- * The I2C read operation of the M-5MOLS requires 2 messages. The first
- * message sends the information about the command, command category, and total
- * message size. The second message is used to retrieve the data specifed in
- * the first message
- *
- * 1st message 2nd message
- * +-------+---+----------+-----+-------+ +------+------+------+------+
- * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] |
- * +-------+---+----------+-----+-------+ +------+------+------+------+
- * - size1: message data size(5 in this case)
- * - size2: desired buffer size of the 2nd message
- * - d[0..3]: according to size2
- *
- * The I2C write operation needs just one message. The message includes
- * category, command, total size, and desired data.
- *
- * 1st message
- * +-------+---+----------+-----+------+------+------+------+
- * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] |
- * +-------+---+----------+-----+------+------+------+------+
- * - d[0..3]: according to size1
- */
-int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val);
-int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val);
-int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val);
-int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val);
-
-int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
- int timeout);
-
-/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */
-#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16)
-/* ISP state transition timeout, in ms */
-#define M5MOLS_MODE_CHANGE_TIMEOUT 200
-#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250
-
-/*
- * Mode operation of the M-5MOLS
- *
- * Changing the mode of the M-5MOLS is needed right executing order.
- * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed
- * by user. There are various categories associated with each mode.
- *
- * +============================================================+
- * | mode | category |
- * +============================================================+
- * | FLASH | FLASH(only after Stand-by or Power-on) |
- * | SYSTEM | SYSTEM(only after sensor arm-booting) |
- * | PARAMETER | PARAMETER |
- * | MONITOR | MONITOR(preview), Auto Focus, Face Detection |
- * | CAPTURE | Single CAPTURE, Preview(recording) |
- * +============================================================+
- *
- * The available executing order between each modes are as follows:
- * PARAMETER <---> MONITOR <---> CAPTURE
- */
-int m5mols_set_mode(struct m5mols_info *info, u8 mode);
-
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
-int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout);
-int m5mols_restore_controls(struct m5mols_info *info);
-int m5mols_start_capture(struct m5mols_info *info);
-int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
-int m5mols_lock_3a(struct m5mols_info *info, bool lock);
-int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
-int m5mols_init_controls(struct v4l2_subdev *sd);
-
-/* The firmware function */
-int m5mols_update_fw(struct v4l2_subdev *sd,
- int (*set_power)(struct m5mols_info *, bool));
-
-static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev)
-{
- return container_of(subdev, struct m5mols_info, sd);
-}
-
-static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
-{
- struct m5mols_info *info = container_of(ctrl->handler,
- struct m5mols_info, handle);
- return &info->sd;
-}
-
-static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl,
- unsigned int mode)
-{
- ctrl->priv = (void *)(uintptr_t)mode;
-}
-
-static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl)
-{
- return (unsigned int)(uintptr_t)ctrl->priv;
-}
-
-#endif /* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
deleted file mode 100644
index 8bf6599a4c1a..000000000000
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ /dev/null
@@ -1,992 +0,0 @@
-/*
- * Driver for M-5MOLS 8M Pixel camera sensor with ISP
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Author: HeungJun Kim <riverful.kim@samsung.com>
- *
- * Copyright (C) 2009 Samsung Electronics Co., Ltd.
- * Author: Dongsoo Nathaniel Kim <dongsoo45.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 version.
- */
-
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-#include <media/m5mols.h>
-
-#include "m5mols.h"
-#include "m5mols_reg.h"
-
-int m5mols_debug;
-module_param(m5mols_debug, int, 0644);
-
-#define MODULE_NAME "M5MOLS"
-#define M5MOLS_I2C_CHECK_RETRY 500
-
-/* The regulator consumer names for external voltage regulators */
-static struct regulator_bulk_data supplies[] = {
- {
- .supply = "core", /* ARM core power, 1.2V */
- }, {
- .supply = "dig_18", /* digital power 1, 1.8V */
- }, {
- .supply = "d_sensor", /* sensor power 1, 1.8V */
- }, {
- .supply = "dig_28", /* digital power 2, 2.8V */
- }, {
- .supply = "a_sensor", /* analog power */
- }, {
- .supply = "dig_12", /* digital power 3, 1.2V */
- },
-};
-
-static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = {
- [M5MOLS_RESTYPE_MONITOR] = {
- .width = 1920,
- .height = 1080,
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- [M5MOLS_RESTYPE_CAPTURE] = {
- .width = 1920,
- .height = 1080,
- .code = V4L2_MBUS_FMT_JPEG_1X8,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
-};
-#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt)
-
-static const struct m5mols_resolution m5mols_reg_res[] = {
- { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */
- { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */
- { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */
- { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 },
- { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */
- { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */
- { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */
- { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */
- { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */
- { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 },
- { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */
- { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */
- { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 },
- { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */
- { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */
- { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */
- { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */
- { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */
- { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */
-
- { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */
- { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */
- { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 },
- { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */
- { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */
- { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */
- { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */
- { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */
- { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */
- { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */
- { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */
- { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 },
- { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */
- { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 },
- { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */
- { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */
- { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 },
- { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */
-};
-
-/**
- * m5mols_swap_byte - an byte array to integer conversion function
- * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet
- *
- * Convert I2C data byte array with performing any required byte
- * reordering to assure proper values for each data type, regardless
- * of the architecture endianness.
- */
-static u32 m5mols_swap_byte(u8 *data, u8 length)
-{
- if (length == 1)
- return *data;
- else if (length == 2)
- return be16_to_cpu(*((u16 *)data));
- else
- return be32_to_cpu(*((u32 *)data));
-}
-
-/**
- * m5mols_read - I2C read function
- * @reg: combination of size, category and command for the I2C packet
- * @size: desired size of I2C packet
- * @val: read value
- *
- * Returns 0 on success, or else negative errno.
- */
-static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct m5mols_info *info = to_m5mols(sd);
- u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1];
- u8 category = I2C_CATEGORY(reg);
- u8 cmd = I2C_COMMAND(reg);
- struct i2c_msg msg[2];
- u8 wbuf[5];
- int ret;
-
- if (!client->adapter)
- return -ENODEV;
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 5;
- msg[0].buf = wbuf;
- wbuf[0] = 5;
- wbuf[1] = M5MOLS_BYTE_READ;
- wbuf[2] = category;
- wbuf[3] = cmd;
- wbuf[4] = size;
-
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = size + 1;
- msg[1].buf = rbuf;
-
- /* minimum stabilization time */
- usleep_range(200, 200);
-
- ret = i2c_transfer(client->adapter, msg, 2);
-
- if (ret == 2) {
- *val = m5mols_swap_byte(&rbuf[1], size);
- return 0;
- }
-
- if (info->isp_ready)
- v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n",
- size, category, cmd, ret);
-
- return ret < 0 ? ret : -EIO;
-}
-
-int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val)
-{
- u32 val_32;
- int ret;
-
- if (I2C_SIZE(reg) != 1) {
- v4l2_err(sd, "Wrong data size\n");
- return -EINVAL;
- }
-
- ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
- if (ret)
- return ret;
-
- *val = (u8)val_32;
- return ret;
-}
-
-int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val)
-{
- u32 val_32;
- int ret;
-
- if (I2C_SIZE(reg) != 2) {
- v4l2_err(sd, "Wrong data size\n");
- return -EINVAL;
- }
-
- ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32);
- if (ret)
- return ret;
-
- *val = (u16)val_32;
- return ret;
-}
-
-int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val)
-{
- if (I2C_SIZE(reg) != 4) {
- v4l2_err(sd, "Wrong data size\n");
- return -EINVAL;
- }
-
- return m5mols_read(sd, I2C_SIZE(reg), reg, val);
-}
-
-/**
- * m5mols_write - I2C command write function
- * @reg: combination of size, category and command for the I2C packet
- * @val: value to write
- *
- * Returns 0 on success, or else negative errno.
- */
-int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct m5mols_info *info = to_m5mols(sd);
- u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4];
- u8 category = I2C_CATEGORY(reg);
- u8 cmd = I2C_COMMAND(reg);
- u8 size = I2C_SIZE(reg);
- u32 *buf = (u32 *)&wbuf[4];
- struct i2c_msg msg[1];
- int ret;
-
- if (!client->adapter)
- return -ENODEV;
-
- if (size != 1 && size != 2 && size != 4) {
- v4l2_err(sd, "Wrong data size\n");
- return -EINVAL;
- }
-
- msg->addr = client->addr;
- msg->flags = 0;
- msg->len = (u16)size + 4;
- msg->buf = wbuf;
- wbuf[0] = size + 4;
- wbuf[1] = M5MOLS_BYTE_WRITE;
- wbuf[2] = category;
- wbuf[3] = cmd;
-
- *buf = m5mols_swap_byte((u8 *)&val, size);
-
- usleep_range(200, 200);
-
- ret = i2c_transfer(client->adapter, msg, 1);
- if (ret == 1)
- return 0;
-
- if (info->isp_ready)
- v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n",
- category, cmd, ret);
-
- return ret < 0 ? ret : -EIO;
-}
-
-/**
- * m5mols_busy_wait - Busy waiting with I2C register polling
- * @reg: the I2C_REG() address of an 8-bit status register to check
- * @value: expected status register value
- * @mask: bit mask for the read status register value
- * @timeout: timeout in miliseconds, or -1 for default timeout
- *
- * The @reg register value is ORed with @mask before comparing with @value.
- *
- * Return: 0 if the requested condition became true within less than
- * @timeout ms, or else negative errno.
- */
-int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
- int timeout)
-{
- int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout;
- unsigned long end = jiffies + msecs_to_jiffies(ms);
- u8 status;
-
- do {
- int ret = m5mols_read_u8(sd, reg, &status);
-
- if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL))
- return ret;
- if (!ret && (status & mask & 0xff) == (value & 0xff))
- return 0;
- usleep_range(100, 250);
- } while (ms > 0 && time_is_after_jiffies(end));
-
- return -EBUSY;
-}
-
-/**
- * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts
- *
- * Before writing desired interrupt value the INT_FACTOR register should
- * be read to clear pending interrupts.
- */
-int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg)
-{
- struct m5mols_info *info = to_m5mols(sd);
- u8 mask = is_available_af(info) ? REG_INT_AF : 0;
- u8 dummy;
- int ret;
-
- ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy);
- if (!ret)
- ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask);
- return ret;
-}
-
-int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout)
-{
- struct m5mols_info *info = to_m5mols(sd);
-
- int ret = wait_event_interruptible_timeout(info->irq_waitq,
- atomic_add_unless(&info->irq_done, -1, 0),
- msecs_to_jiffies(timeout));
- if (ret <= 0)
- return ret ? ret : -ETIMEDOUT;
-
- return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask,
- M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1);
-}
-
-/**
- * m5mols_reg_mode - Write the mode and check busy status
- *
- * It always accompanies a little delay changing the M-5MOLS mode, so it is
- * needed checking current busy status to guarantee right mode.
- */
-static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
-{
- int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode);
- if (ret < 0)
- return ret;
- return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff,
- M5MOLS_MODE_CHANGE_TIMEOUT);
-}
-
-/**
- * m5mols_set_mode - set the M-5MOLS controller mode
- * @mode: the required operation mode
- *
- * The commands of M-5MOLS are grouped into specific modes. Each functionality
- * can be guaranteed only when the sensor is operating in mode which a command
- * belongs to.
- */
-int m5mols_set_mode(struct m5mols_info *info, u8 mode)
-{
- struct v4l2_subdev *sd = &info->sd;
- int ret = -EINVAL;
- u8 reg;
-
- if (mode < REG_PARAMETER || mode > REG_CAPTURE)
- return ret;
-
- ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, &reg);
- if (ret || reg == mode)
- return ret;
-
- switch (reg) {
- case REG_PARAMETER:
- ret = m5mols_reg_mode(sd, REG_MONITOR);
- if (mode == REG_MONITOR)
- break;
- if (!ret)
- ret = m5mols_reg_mode(sd, REG_CAPTURE);
- break;
-
- case REG_MONITOR:
- if (mode == REG_PARAMETER) {
- ret = m5mols_reg_mode(sd, REG_PARAMETER);
- break;
- }
-
- ret = m5mols_reg_mode(sd, REG_CAPTURE);
- break;
-
- case REG_CAPTURE:
- ret = m5mols_reg_mode(sd, REG_MONITOR);
- if (mode == REG_MONITOR)
- break;
- if (!ret)
- ret = m5mols_reg_mode(sd, REG_PARAMETER);
- break;
-
- default:
- v4l2_warn(sd, "Wrong mode: %d\n", mode);
- }
-
- if (!ret)
- info->mode = mode;
-
- return ret;
-}
-
-/**
- * m5mols_get_version - retrieve full revisions information of M-5MOLS
- *
- * The version information includes revisions of hardware and firmware,
- * AutoFocus alghorithm version and the version string.
- */
-static int m5mols_get_version(struct v4l2_subdev *sd)
-{
- struct m5mols_info *info = to_m5mols(sd);
- struct m5mols_version *ver = &info->ver;
- u8 *str = ver->str;
- int i;
- int ret;
-
- ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer);
- if (!ret)
- ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project);
- if (!ret)
- ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw);
- if (!ret)
- ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw);
- if (!ret)
- ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param);
- if (!ret)
- ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb);
- if (!ret)
- ret = m5mols_read_u8(sd, AF_VERSION, &ver->af);
- if (ret)
- return ret;
-
- for (i = 0; i < VERSION_STRING_SIZE; i++) {
- ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]);
- if (ret)
- return ret;
- }
-
- ver->fw = be16_to_cpu(ver->fw);
- ver->hw = be16_to_cpu(ver->hw);
- ver->param = be16_to_cpu(ver->param);
- ver->awb = be16_to_cpu(ver->awb);
-
- v4l2_info(sd, "Manufacturer\t[%s]\n",
- is_manufacturer(info, REG_SAMSUNG_ELECTRO) ?
- "Samsung Electro-Machanics" :
- is_manufacturer(info, REG_SAMSUNG_OPTICS) ?
- "Samsung Fiber-Optics" :
- is_manufacturer(info, REG_SAMSUNG_TECHWIN) ?
- "Samsung Techwin" : "None");
- v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n",
- info->ver.customer, info->ver.project);
-
- if (!is_available_af(info))
- v4l2_info(sd, "No support Auto Focus on this firmware\n");
-
- return ret;
-}
-
-/**
- * __find_restype - Lookup M-5MOLS resolution type according to pixel code
- * @code: pixel code
- */
-static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code)
-{
- enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR;
-
- do {
- if (code == m5mols_default_ffmt[type].code)
- return type;
- } while (type++ != SIZE_DEFAULT_FFMT);
-
- return 0;
-}
-
-/**
- * __find_resolution - Lookup preset and type of M-5MOLS's resolution
- * @mf: pixel format to find/negotiate the resolution preset for
- * @type: M-5MOLS resolution type
- * @resolution: M-5MOLS resolution preset register value
- *
- * Find nearest resolution matching resolution preset and adjust mf
- * to supported values.
- */
-static int __find_resolution(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf,
- enum m5mols_restype *type,
- u32 *resolution)
-{
- const struct m5mols_resolution *fsize = &m5mols_reg_res[0];
- const struct m5mols_resolution *match = NULL;
- enum m5mols_restype stype = __find_restype(mf->code);
- int i = ARRAY_SIZE(m5mols_reg_res);
- unsigned int min_err = ~0;
-
- while (i--) {
- int err;
- if (stype == fsize->type) {
- 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;
- *resolution = match->reg;
- *type = stype;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info,
- struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which,
- enum m5mols_restype type)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL;
-
- return &info->ffmt[type];
-}
-
-static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct m5mols_info *info = to_m5mols(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __find_format(info, fh, fmt->which, info->res_type);
- if (!format)
- return -EINVAL;
-
- fmt->format = *format;
- return 0;
-}
-
-static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct m5mols_info *info = to_m5mols(sd);
- struct v4l2_mbus_framefmt *format = &fmt->format;
- struct v4l2_mbus_framefmt *sfmt;
- enum m5mols_restype type;
- u32 resolution = 0;
- int ret;
-
- ret = __find_resolution(sd, format, &type, &resolution);
- if (ret < 0)
- return ret;
-
- sfmt = __find_format(info, fh, fmt->which, type);
- if (!sfmt)
- return 0;
-
-
- format->code = m5mols_default_ffmt[type].code;
- format->colorspace = V4L2_COLORSPACE_JPEG;
- format->field = V4L2_FIELD_NONE;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- *sfmt = *format;
- info->resolution = resolution;
- info->res_type = type;
- }
-
- return 0;
-}
-
-static int m5mols_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (!code || code->index >= SIZE_DEFAULT_FFMT)
- return -EINVAL;
-
- code->code = m5mols_default_ffmt[code->index].code;
-
- return 0;
-}
-
-static struct v4l2_subdev_pad_ops m5mols_pad_ops = {
- .enum_mbus_code = m5mols_enum_mbus_code,
- .get_fmt = m5mols_get_fmt,
- .set_fmt = m5mols_set_fmt,
-};
-
-/**
- * m5mols_restore_controls - Apply current control values to the registers
- *
- * m5mols_do_scenemode() handles all parameters for which there is yet no
- * individual control. It should be replaced at some point by setting each
- * control individually, in required register set up order.
- */
-int m5mols_restore_controls(struct m5mols_info *info)
-{
- int ret;
-
- if (info->ctrl_sync)
- return 0;
-
- ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL);
- if (ret)
- return ret;
-
- ret = v4l2_ctrl_handler_setup(&info->handle);
- info->ctrl_sync = !ret;
-
- return ret;
-}
-
-/**
- * m5mols_start_monitor - Start the monitor mode
- *
- * Before applying the controls setup the resolution and frame rate
- * in PARAMETER mode, and then switch over to MONITOR mode.
- */
-static int m5mols_start_monitor(struct m5mols_info *info)
-{
- struct v4l2_subdev *sd = &info->sd;
- int ret;
-
- ret = m5mols_set_mode(info, REG_PARAMETER);
- if (!ret)
- ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
- if (!ret)
- ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
- if (!ret)
- ret = m5mols_set_mode(info, REG_MONITOR);
- if (!ret)
- ret = m5mols_restore_controls(info);
-
- return ret;
-}
-
-static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct m5mols_info *info = to_m5mols(sd);
- u32 code = info->ffmt[info->res_type].code;
-
- if (enable) {
- int ret = -EINVAL;
-
- if (is_code(code, M5MOLS_RESTYPE_MONITOR))
- ret = m5mols_start_monitor(info);
- if (is_code(code, M5MOLS_RESTYPE_CAPTURE))
- ret = m5mols_start_capture(info);
-
- return ret;
- }
-
- return m5mols_set_mode(info, REG_PARAMETER);
-}
-
-static const struct v4l2_subdev_video_ops m5mols_video_ops = {
- .s_stream = m5mols_s_stream,
-};
-
-static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
-{
- struct v4l2_subdev *sd = &info->sd;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- const struct m5mols_platform_data *pdata = info->pdata;
- int ret;
-
- if (info->power == enable)
- return 0;
-
- if (enable) {
- if (info->set_power) {
- ret = info->set_power(&client->dev, 1);
- if (ret)
- return ret;
- }
-
- ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
- if (ret) {
- info->set_power(&client->dev, 0);
- return ret;
- }
-
- gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity);
- info->power = 1;
-
- return ret;
- }
-
- ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
- if (ret)
- return ret;
-
- if (info->set_power)
- info->set_power(&client->dev, 0);
-
- gpio_set_value(pdata->gpio_reset, pdata->reset_polarity);
-
- info->isp_ready = 0;
- info->power = 0;
-
- return ret;
-}
-
-/* m5mols_update_fw - optional firmware update routine */
-int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd,
- int (*set_power)(struct m5mols_info *, bool))
-{
- return 0;
-}
-
-/**
- * m5mols_fw_start - M-5MOLS internal ARM controller initialization
- *
- * Execute the M-5MOLS internal ARM controller initialization sequence.
- * This function should be called after the supply voltage has been
- * applied and before any requests to the device are made.
- */
-static int m5mols_fw_start(struct v4l2_subdev *sd)
-{
- struct m5mols_info *info = to_m5mols(sd);
- int ret;
-
- atomic_set(&info->irq_done, 0);
- /* Wait until I2C slave is initialized in Flash Writer mode */
- ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE,
- M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1);
- if (!ret)
- ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT);
- if (!ret)
- ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000);
- if (ret < 0)
- return ret;
-
- info->isp_ready = 1;
-
- ret = m5mols_get_version(sd);
- if (!ret)
- ret = m5mols_update_fw(sd, m5mols_sensor_power);
- if (ret)
- return ret;
-
- v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n");
-
- ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI);
- if (!ret)
- ret = m5mols_enable_interrupt(sd,
- REG_INT_AF | REG_INT_CAPTURE);
-
- return ret;
-}
-
-/**
- * m5mols_s_power - Main sensor power control function
- *
- * To prevent breaking the lens when the sensor is powered off the Soft-Landing
- * algorithm is called where available. The Soft-Landing algorithm availability
- * dependends on the firmware provider.
- */
-static int m5mols_s_power(struct v4l2_subdev *sd, int on)
-{
- struct m5mols_info *info = to_m5mols(sd);
- int ret;
-
- if (on) {
- ret = m5mols_sensor_power(info, true);
- if (!ret)
- ret = m5mols_fw_start(sd);
- return ret;
- }
-
- if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
- ret = m5mols_set_mode(info, REG_MONITOR);
- if (!ret)
- ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
- if (!ret)
- ret = m5mols_write(sd, AF_MODE, REG_AF_POWEROFF);
- if (!ret)
- ret = m5mols_busy_wait(sd, SYSTEM_STATUS, REG_AF_IDLE,
- 0xff, -1);
- if (ret < 0)
- v4l2_warn(sd, "Soft landing lens failed\n");
- }
-
- ret = m5mols_sensor_power(info, false);
- info->ctrl_sync = 0;
-
- return ret;
-}
-
-static int m5mols_log_status(struct v4l2_subdev *sd)
-{
- struct m5mols_info *info = to_m5mols(sd);
-
- v4l2_ctrl_handler_log_status(&info->handle, sd->name);
-
- return 0;
-}
-
-static const struct v4l2_subdev_core_ops m5mols_core_ops = {
- .s_power = m5mols_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 = m5mols_log_status,
-};
-
-/*
- * V4L2 subdev internal operations
- */
-static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
- *format = m5mols_default_ffmt[0];
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = {
- .open = m5mols_open,
-};
-
-static const struct v4l2_subdev_ops m5mols_ops = {
- .core = &m5mols_core_ops,
- .pad = &m5mols_pad_ops,
- .video = &m5mols_video_ops,
-};
-
-static irqreturn_t m5mols_irq_handler(int irq, void *data)
-{
- struct m5mols_info *info = to_m5mols(data);
-
- atomic_set(&info->irq_done, 1);
- wake_up_interruptible(&info->irq_waitq);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit m5mols_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- const struct m5mols_platform_data *pdata = client->dev.platform_data;
- struct m5mols_info *info;
- struct v4l2_subdev *sd;
- int ret;
-
- if (pdata == NULL) {
- dev_err(&client->dev, "No platform data\n");
- return -EINVAL;
- }
-
- if (!gpio_is_valid(pdata->gpio_reset)) {
- dev_err(&client->dev, "No valid RESET GPIO specified\n");
- return -EINVAL;
- }
-
- if (!client->irq) {
- dev_err(&client->dev, "Interrupt not assigned\n");
- return -EINVAL;
- }
-
- info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- info->pdata = pdata;
- info->set_power = pdata->set_power;
-
- ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST");
- if (ret) {
- dev_err(&client->dev, "Failed to request gpio: %d\n", ret);
- goto out_free;
- }
- gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity);
-
- ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies);
- if (ret) {
- dev_err(&client->dev, "Failed to get regulators: %d\n", ret);
- goto out_gpio;
- }
-
- sd = &info->sd;
- v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
- strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- sd->internal_ops = &m5mols_subdev_internal_ops;
- info->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, 1, &info->pad, 0);
- if (ret < 0)
- goto out_reg;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
-
- init_waitqueue_head(&info->irq_waitq);
- ret = request_irq(client->irq, m5mols_irq_handler,
- IRQF_TRIGGER_RISING, MODULE_NAME, sd);
- if (ret) {
- dev_err(&client->dev, "Interrupt request failed: %d\n", ret);
- goto out_me;
- }
- info->res_type = M5MOLS_RESTYPE_MONITOR;
- info->ffmt[0] = m5mols_default_ffmt[0];
- info->ffmt[1] = m5mols_default_ffmt[1];
-
- ret = m5mols_sensor_power(info, true);
- if (ret)
- goto out_irq;
-
- ret = m5mols_fw_start(sd);
- if (!ret)
- ret = m5mols_init_controls(sd);
-
- m5mols_sensor_power(info, false);
- if (!ret)
- return 0;
-out_irq:
- free_irq(client->irq, sd);
-out_me:
- media_entity_cleanup(&sd->entity);
-out_reg:
- regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
-out_gpio:
- gpio_free(pdata->gpio_reset);
-out_free:
- kfree(info);
- return ret;
-}
-
-static int __devexit m5mols_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct m5mols_info *info = to_m5mols(sd);
-
- v4l2_device_unregister_subdev(sd);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
- free_irq(client->irq, sd);
-
- regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
- gpio_free(info->pdata->gpio_reset);
- media_entity_cleanup(&sd->entity);
- kfree(info);
- return 0;
-}
-
-static const struct i2c_device_id m5mols_id[] = {
- { MODULE_NAME, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2c, m5mols_id);
-
-static struct i2c_driver m5mols_i2c_driver = {
- .driver = {
- .name = MODULE_NAME,
- },
- .probe = m5mols_probe,
- .remove = __devexit_p(m5mols_remove),
- .id_table = m5mols_id,
-};
-
-module_i2c_driver(m5mols_i2c_driver);
-
-MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
-MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
-MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
deleted file mode 100644
index 0b91a5cd38eb..000000000000
--- a/drivers/media/video/mem2mem_testdev.c
+++ /dev/null
@@ -1,1120 +0,0 @@
-/*
- * A virtual v4l2-mem2mem example device.
- *
- * This is a virtual device driver for testing mem-to-mem videobuf framework.
- * It simulates a device that uses memory buffers for both source and
- * destination, processes the data and issues an "irq" (simulated by a timer).
- * The device is capable of multi-instance, multi-buffer-per-transaction
- * operation (via the mem2mem framework).
- *
- * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
- * Pawel Osciak, <pawel@osciak.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; either version 2 of the
- * License, or (at your option) any later version
- */
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/videobuf2-vmalloc.h>
-
-#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
-
-MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
-MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.1.1");
-
-#define MIN_W 32
-#define MIN_H 32
-#define MAX_W 640
-#define MAX_H 480
-#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
-
-/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
-
-#define MEM2MEM_NAME "m2m-testdev"
-
-/* Per queue */
-#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME
-/* In bytes, per queue */
-#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
-
-/* Default transaction time in msec */
-#define MEM2MEM_DEF_TRANSTIME 1000
-/* Default number of buffers per transaction */
-#define MEM2MEM_DEF_TRANSLEN 1
-#define MEM2MEM_COLOR_STEP (0xff >> 4)
-#define MEM2MEM_NUM_TILES 8
-
-/* Flags that indicate processing mode */
-#define MEM2MEM_HFLIP (1 << 0)
-#define MEM2MEM_VFLIP (1 << 1)
-
-#define dprintk(dev, fmt, arg...) \
- v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
-
-
-void m2mtest_dev_release(struct device *dev)
-{}
-
-static struct platform_device m2mtest_pdev = {
- .name = MEM2MEM_NAME,
- .dev.release = m2mtest_dev_release,
-};
-
-struct m2mtest_fmt {
- char *name;
- u32 fourcc;
- int depth;
- /* Types the format can be used for */
- u32 types;
-};
-
-static struct m2mtest_fmt formats[] = {
- {
- .name = "RGB565 (BE)",
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .depth = 16,
- /* Both capture and output format */
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
- },
- {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- /* Output-only format */
- .types = MEM2MEM_OUTPUT,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-/* Per-queue, driver-specific private data */
-struct m2mtest_q_data {
- unsigned int width;
- unsigned int height;
- unsigned int sizeimage;
- struct m2mtest_fmt *fmt;
-};
-
-enum {
- V4L2_M2M_SRC = 0,
- V4L2_M2M_DST = 1,
-};
-
-#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
-#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
-
-static struct m2mtest_fmt *find_format(struct v4l2_format *f)
-{
- struct m2mtest_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < NUM_FORMATS; k++) {
- fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
- break;
- }
-
- if (k == NUM_FORMATS)
- return NULL;
-
- return &formats[k];
-}
-
-struct m2mtest_dev {
- struct v4l2_device v4l2_dev;
- struct video_device *vfd;
-
- atomic_t num_inst;
- struct mutex dev_mutex;
- spinlock_t irqlock;
-
- struct timer_list timer;
-
- struct v4l2_m2m_dev *m2m_dev;
-};
-
-struct m2mtest_ctx {
- struct v4l2_fh fh;
- struct m2mtest_dev *dev;
-
- struct v4l2_ctrl_handler hdl;
-
- /* Processed buffers in this transaction */
- u8 num_processed;
-
- /* Transaction length (i.e. how many buffers per transaction) */
- u32 translen;
- /* Transaction time (i.e. simulated processing time) in milliseconds */
- u32 transtime;
-
- /* Abort requested by m2m */
- int aborting;
-
- /* Processing mode */
- int mode;
-
- enum v4l2_colorspace colorspace;
-
- struct v4l2_m2m_ctx *m2m_ctx;
-
- /* Source and destination queue data */
- struct m2mtest_q_data q_data[2];
-};
-
-static inline struct m2mtest_ctx *file2ctx(struct file *file)
-{
- return container_of(file->private_data, struct m2mtest_ctx, fh);
-}
-
-static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
- enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return &ctx->q_data[V4L2_M2M_SRC];
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &ctx->q_data[V4L2_M2M_DST];
- default:
- BUG();
- }
- return NULL;
-}
-
-
-static int device_process(struct m2mtest_ctx *ctx,
- 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;
- int width, height, bytesperline;
-
- q_data = get_q_data(ctx, 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 (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 = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
- / MEM2MEM_NUM_TILES;
- bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
- w = 0;
-
- switch (ctx->mode) {
- case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
- p_out += bytesperline * height - bytes_left;
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
- p_out -= bytes_left;
- }
- break;
-
- case MEM2MEM_HFLIP:
- for (y = 0; y < height; ++y) {
- p_out += MEM2MEM_NUM_TILES * tile_w;
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x01) {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *--p_out = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
- p_out += bytesperline;
- }
- break;
-
- case MEM2MEM_VFLIP:
- p_out += bytesperline * (height - 1);
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
- p_out += bytes_left - 2 * bytesperline;
- }
- break;
-
- default:
- for (y = 0; y < height; ++y) {
- for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
- if (w & 0x1) {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ +
- MEM2MEM_COLOR_STEP;
- } else {
- for (x = 0; x < tile_w; ++x)
- *p_out++ = *p_in++ -
- MEM2MEM_COLOR_STEP;
- }
- ++w;
- }
- p_in += bytes_left;
- p_out += bytes_left;
- }
- }
-
- return 0;
-}
-
-static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)
-{
- dprintk(dev, "Scheduling a simulated irq\n");
- mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
-}
-
-/*
- * mem2mem callbacks
- */
-
-/**
- * job_ready() - check whether an instance is ready to be scheduled to run
- */
-static int job_ready(void *priv)
-{
- struct m2mtest_ctx *ctx = priv;
-
- if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen
- || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {
- dprintk(ctx->dev, "Not enough buffers available\n");
- return 0;
- }
-
- return 1;
-}
-
-static void job_abort(void *priv)
-{
- struct m2mtest_ctx *ctx = priv;
-
- /* Will cancel the transaction in the next interrupt handler */
- 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
- * a device. This will be called by the framework when it decides to schedule
- * a particular instance.
- */
-static void device_run(void *priv)
-{
- struct m2mtest_ctx *ctx = priv;
- struct m2mtest_dev *dev = ctx->dev;
- 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);
-
- device_process(ctx, src_buf, dst_buf);
-
- /* Run a timer, which simulates a hardware irq */
- 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 vb2_buffer *src_vb, *dst_vb;
- unsigned long flags;
-
- curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
-
- if (NULL == curr_ctx) {
- printk(KERN_ERR
- "Instance released before the end of transaction\n");
- return;
- }
-
- 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;
- v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
- } else {
- device_run(curr_ctx);
- }
-}
-
-/*
- * video ioctls
- */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
- strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
- strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
-{
- int i, num;
- struct m2mtest_fmt *fmt;
-
- num = 0;
-
- for (i = 0; i < NUM_FORMATS; ++i) {
- if (formats[i].types & type) {
- /* index-th format of type type found ? */
- if (num == f->index)
- break;
- /* Correct type but haven't reached our index yet,
- * just increment per-type index */
- ++num;
- }
- }
-
- if (i < NUM_FORMATS) {
- /* Format found */
- fmt = &formats[i];
- strncpy(f->description, fmt->name, sizeof(f->description) - 1);
- f->pixelformat = fmt->fourcc;
- return 0;
- }
-
- /* Format not found */
- return -EINVAL;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_CAPTURE);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_OUTPUT);
-}
-
-static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct m2mtest_q_data *q_data;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
-
- f->fmt.pix.width = q_data->width;
- f->fmt.pix.height = q_data->height;
- 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;
- f->fmt.pix.colorspace = ctx->colorspace;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(file2ctx(file), f);
-}
-
-static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
-{
- enum v4l2_field field;
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != field)
- return -EINVAL;
-
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
- f->fmt.pix.field = field;
-
- if (f->fmt.pix.height < MIN_H)
- f->fmt.pix.height = MIN_H;
- else if (f->fmt.pix.height > MAX_H)
- f->fmt.pix.height = MAX_H;
-
- if (f->fmt.pix.width < MIN_W)
- f->fmt.pix.width = MIN_W;
- else if (f->fmt.pix.width > MAX_W)
- f->fmt.pix.width = MAX_W;
-
- f->fmt.pix.width &= ~DIM_ALIGN_MASK;
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
- f->fmt.pix.colorspace = ctx->colorspace;
-
- return vidioc_try_fmt(f, fmt);
-}
-
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct m2mtest_fmt *fmt;
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
- if (!f->fmt.pix.colorspace)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
-
- return vidioc_try_fmt(f, fmt);
-}
-
-static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
-{
- struct m2mtest_q_data *q_data;
- struct vb2_queue *vq;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- if (vb2_is_busy(vq)) {
- v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- q_data->fmt = find_format(f);
- q_data->width = f->fmt.pix.width;
- q_data->height = f->fmt.pix.height;
- q_data->sizeimage = q_data->width * q_data->height
- * q_data->fmt->depth >> 3;
-
- dprintk(ctx->dev,
- "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return vidioc_s_fmt(file2ctx(file), f);
-}
-
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
- int ret;
-
- ret = vidioc_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- ret = vidioc_s_fmt(file2ctx(file), f);
- if (!ret)
- ctx->colorspace = f->fmt.pix.colorspace;
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct m2mtest_ctx *ctx =
- container_of(ctrl->handler, struct m2mtest_ctx, hdl);
-
- switch (ctrl->id) {
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- ctx->mode |= MEM2MEM_HFLIP;
- else
- ctx->mode &= ~MEM2MEM_HFLIP;
- break;
-
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- ctx->mode |= MEM2MEM_VFLIP;
- else
- ctx->mode &= ~MEM2MEM_VFLIP;
- break;
-
- case V4L2_CID_TRANS_TIME_MSEC:
- ctx->transtime = ctrl->val;
- break;
-
- case V4L2_CID_TRANS_NUM_BUFS:
- ctx->translen = ctrl->val;
- break;
-
- default:
- v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
- .s_ctrl = m2mtest_s_ctrl,
-};
-
-
-static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
-
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-
-/*
- * Queue operations
- */
-
-static int m2mtest_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- 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(ctx, vq->type);
-
- size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
-
- while (size * count > MEM2MEM_VID_MEM_LIMIT)
- (count)--;
-
- *nplanes = 1;
- *nbuffers = count;
- sizes[0] = 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 vb2_buffer *vb)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct m2mtest_q_data *q_data;
-
- dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
-
- 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;
- }
-
- vb2_set_plane_payload(vb, 0, q_data->sizeimage);
-
- return 0;
-}
-
-static void m2mtest_buf_queue(struct vb2_buffer *vb)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void m2mtest_wait_prepare(struct vb2_queue *q)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_unlock(ctx);
-}
-
-static void m2mtest_wait_finish(struct vb2_queue *q)
-{
- struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
- m2mtest_lock(ctx);
-}
-
-static struct vb2_ops m2mtest_qops = {
- .queue_setup = m2mtest_queue_setup,
- .buf_prepare = m2mtest_buf_prepare,
- .buf_queue = m2mtest_buf_queue,
- .wait_prepare = m2mtest_wait_prepare,
- .wait_finish = m2mtest_wait_finish,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
-{
- struct m2mtest_ctx *ctx = priv;
- int ret;
-
- 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);
-}
-
-static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
- .ops = &m2mtest_ctrl_ops,
- .id = V4L2_CID_TRANS_TIME_MSEC,
- .name = "Transaction Time (msec)",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .def = 1001,
- .min = 1,
- .max = 10001,
- .step = 100,
-};
-
-static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
- .ops = &m2mtest_ctrl_ops,
- .id = V4L2_CID_TRANS_NUM_BUFS,
- .name = "Buffers Per Transaction",
- .type = V4L2_CTRL_TYPE_INTEGER,
- .def = 1,
- .min = 1,
- .max = MEM2MEM_DEF_NUM_BUFS,
- .step = 1,
-};
-
-/*
- * File operations
- */
-static int m2mtest_open(struct file *file)
-{
- struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = NULL;
- struct v4l2_ctrl_handler *hdl;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- ctx->dev = dev;
- hdl = &ctx->hdl;
- v4l2_ctrl_handler_init(hdl, 4);
- v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
- v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
- if (hdl->error) {
- int err = hdl->error;
-
- v4l2_ctrl_handler_free(hdl);
- return err;
- }
- ctx->fh.ctrl_handler = hdl;
- v4l2_ctrl_handler_setup(hdl);
-
- ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
- ctx->q_data[V4L2_M2M_SRC].width = 640;
- ctx->q_data[V4L2_M2M_SRC].height = 480;
- ctx->q_data[V4L2_M2M_SRC].sizeimage =
- ctx->q_data[V4L2_M2M_SRC].width *
- ctx->q_data[V4L2_M2M_SRC].height *
- (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
- ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
- ctx->colorspace = V4L2_COLORSPACE_REC709;
-
- 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);
-
- v4l2_ctrl_handler_free(hdl);
- kfree(ctx);
- return ret;
- }
-
- v4l2_fh_add(&ctx->fh);
- atomic_inc(&dev->num_inst);
-
- dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
-
- return 0;
-}
-
-static int m2mtest_release(struct file *file)
-{
- struct m2mtest_dev *dev = video_drvdata(file);
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- dprintk(dev, "Releasing instance %p\n", ctx);
-
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- v4l2_ctrl_handler_free(&ctx->hdl);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- kfree(ctx);
-
- atomic_dec(&dev->num_inst);
-
- return 0;
-}
-
-static unsigned int m2mtest_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct m2mtest_ctx *ctx = file2ctx(file);
-
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-}
-
-static const struct v4l2_file_operations m2mtest_fops = {
- .owner = THIS_MODULE,
- .open = m2mtest_open,
- .release = m2mtest_release,
- .poll = m2mtest_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = m2mtest_mmap,
-};
-
-static struct video_device m2mtest_videodev = {
- .name = MEM2MEM_NAME,
- .fops = &m2mtest_fops,
- .ioctl_ops = &m2mtest_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
-};
-
-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)
-{
- struct m2mtest_dev *dev;
- struct video_device *vfd;
- int ret;
-
- dev = kzalloc(sizeof *dev, GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- spin_lock_init(&dev->irqlock);
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- goto free_dev;
-
- atomic_set(&dev->num_inst, 0);
- mutex_init(&dev->dev_mutex);
-
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto unreg_dev;
- }
-
- *vfd = m2mtest_videodev;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->lock = &dev->dev_mutex;
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto rel_vdev;
- }
-
- video_set_drvdata(vfd, dev);
- snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
- dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
- "Device registered as /dev/video%d\n", vfd->num);
-
- setup_timer(&dev->timer, device_isr, (long)dev);
- platform_set_drvdata(pdev, dev);
-
- dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(dev->m2m_dev)) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
- ret = PTR_ERR(dev->m2m_dev);
- goto err_m2m;
- }
-
- return 0;
-
- v4l2_m2m_release(dev->m2m_dev);
-err_m2m:
- video_unregister_device(dev->vfd);
-rel_vdev:
- video_device_release(vfd);
-unreg_dev:
- v4l2_device_unregister(&dev->v4l2_dev);
-free_dev:
- kfree(dev);
-
- return ret;
-}
-
-static int m2mtest_remove(struct platform_device *pdev)
-{
- struct m2mtest_dev *dev =
- (struct m2mtest_dev *)platform_get_drvdata(pdev);
-
- v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
- v4l2_m2m_release(dev->m2m_dev);
- del_timer_sync(&dev->timer);
- video_unregister_device(dev->vfd);
- v4l2_device_unregister(&dev->v4l2_dev);
- kfree(dev);
-
- return 0;
-}
-
-static struct platform_driver m2mtest_pdrv = {
- .probe = m2mtest_probe,
- .remove = m2mtest_remove,
- .driver = {
- .name = MEM2MEM_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static void __exit m2mtest_exit(void)
-{
- platform_driver_unregister(&m2mtest_pdrv);
- platform_device_unregister(&m2mtest_pdev);
-}
-
-static int __init m2mtest_init(void)
-{
- int ret;
-
- ret = platform_device_register(&m2mtest_pdev);
- if (ret)
- return ret;
-
- ret = platform_driver_register(&m2mtest_pdrv);
- if (ret)
- platform_device_unregister(&m2mtest_pdev);
-
- return 0;
-}
-
-module_init(m2mtest_init);
-module_exit(m2mtest_exit);
-
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
deleted file mode 100644
index aeb22be7dcbd..000000000000
--- a/drivers/media/video/msp3400-driver.c
+++ /dev/null
@@ -1,899 +0,0 @@
-/*
- * Programming the mspx4xx sound processor family
- *
- * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org>
- *
- * what works and what doesn't:
- *
- * AM-Mono
- * Support for Hauppauge cards added (decoding handled by tuner) added by
- * Frederic Crozat <fcrozat@mail.dotcom.fr>
- *
- * FM-Mono
- * should work. The stereo modes are backward compatible to FM-mono,
- * therefore FM-Mono should be allways available.
- *
- * FM-Stereo (B/G, used in germany)
- * should work, with autodetect
- *
- * FM-Stereo (satellite)
- * should work, no autodetect (i.e. default is mono, but you can
- * switch to stereo -- untested)
- *
- * NICAM (B/G, L , used in UK, Scandinavia, Spain and France)
- * should work, with autodetect. Support for NICAM was added by
- * Pekka Pietikainen <pp@netppl.fi>
- *
- * TODO:
- * - better SAT support
- *
- * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch)
- * using soundcore instead of OSS
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/msp3400.h>
-#include <media/tvaudio.h>
-#include "msp3400-driver.h"
-
-/* ---------------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("device driver for msp34xx TV sound processor");
-MODULE_AUTHOR("Gerd Knorr");
-MODULE_LICENSE("GPL");
-
-/* module parameters */
-static int opmode = OPMODE_AUTO;
-int msp_debug; /* msp_debug output */
-bool msp_once; /* no continuous stereo monitoring */
-bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france),
- the autoscan seems work well only with FM... */
-int msp_standard = 1; /* Override auto detect of audio msp_standard,
- if needed. */
-bool msp_dolby;
-
-int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual
- (msp34xxg only) 0x00a0-0x03c0 */
-
-/* read-only */
-module_param(opmode, int, 0444);
-
-/* read-write */
-module_param_named(once, msp_once, bool, 0644);
-module_param_named(debug, msp_debug, int, 0644);
-module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644);
-module_param_named(standard, msp_standard, int, 0644);
-module_param_named(amsound, msp_amsound, bool, 0644);
-module_param_named(dolby, msp_dolby, bool, 0644);
-
-MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect");
-MODULE_PARM_DESC(once, "No continuous stereo monitoring");
-MODULE_PARM_DESC(debug, "Enable debug messages [0-3]");
-MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo");
-MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect");
-MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan");
-MODULE_PARM_DESC(dolby, "Activates Dolby processing");
-
-/* ---------------------------------------------------------------------- */
-
-/* control subaddress */
-#define I2C_MSP_CONTROL 0x00
-/* demodulator unit subaddress */
-#define I2C_MSP_DEM 0x10
-/* DSP unit subaddress */
-#define I2C_MSP_DSP 0x12
-
-
-/* ----------------------------------------------------------------------- */
-/* functions for talking to the MSP3400C Sound processor */
-
-int msp_reset(struct i2c_client *client)
-{
- /* reset and read revision code */
- static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 };
- static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 };
- static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e };
- u8 read[2];
- struct i2c_msg reset[2] = {
- { client->addr, I2C_M_IGNORE_NAK, 3, reset_off },
- { client->addr, I2C_M_IGNORE_NAK, 3, reset_on },
- };
- struct i2c_msg test[2] = {
- { client->addr, 0, 3, write },
- { client->addr, I2C_M_RD, 2, read },
- };
-
- v4l_dbg(3, msp_debug, client, "msp_reset\n");
- if (i2c_transfer(client->adapter, &reset[0], 1) != 1 ||
- i2c_transfer(client->adapter, &reset[1], 1) != 1 ||
- i2c_transfer(client->adapter, test, 2) != 2) {
- v4l_err(client, "chip reset failed\n");
- return -1;
- }
- return 0;
-}
-
-static int msp_read(struct i2c_client *client, int dev, int addr)
-{
- int err, retval;
- u8 write[3];
- u8 read[2];
- struct i2c_msg msgs[2] = {
- { client->addr, 0, 3, write },
- { client->addr, I2C_M_RD, 2, read }
- };
-
- write[0] = dev + 1;
- write[1] = addr >> 8;
- write[2] = addr & 0xff;
-
- for (err = 0; err < 3; err++) {
- if (i2c_transfer(client->adapter, msgs, 2) == 2)
- break;
- v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err,
- dev, addr);
- schedule_timeout_interruptible(msecs_to_jiffies(10));
- }
- if (err == 3) {
- v4l_warn(client, "resetting chip, sound will go off.\n");
- msp_reset(client);
- return -1;
- }
- retval = read[0] << 8 | read[1];
- v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n",
- dev, addr, retval);
- return retval;
-}
-
-int msp_read_dem(struct i2c_client *client, int addr)
-{
- return msp_read(client, I2C_MSP_DEM, addr);
-}
-
-int msp_read_dsp(struct i2c_client *client, int addr)
-{
- return msp_read(client, I2C_MSP_DSP, addr);
-}
-
-static int msp_write(struct i2c_client *client, int dev, int addr, int val)
-{
- int err;
- u8 buffer[5];
-
- buffer[0] = dev;
- buffer[1] = addr >> 8;
- buffer[2] = addr & 0xff;
- buffer[3] = val >> 8;
- buffer[4] = val & 0xff;
-
- v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n",
- dev, addr, val);
- for (err = 0; err < 3; err++) {
- if (i2c_master_send(client, buffer, 5) == 5)
- break;
- v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err,
- dev, addr);
- schedule_timeout_interruptible(msecs_to_jiffies(10));
- }
- if (err == 3) {
- v4l_warn(client, "resetting chip, sound will go off.\n");
- msp_reset(client);
- return -1;
- }
- return 0;
-}
-
-int msp_write_dem(struct i2c_client *client, int addr, int val)
-{
- return msp_write(client, I2C_MSP_DEM, addr, val);
-}
-
-int msp_write_dsp(struct i2c_client *client, int addr, int val)
-{
- return msp_write(client, I2C_MSP_DSP, addr, val);
-}
-
-/* ----------------------------------------------------------------------- *
- * bits 9 8 5 - SCART DSP input Select:
- * 0 0 0 - SCART 1 to DSP input (reset position)
- * 0 1 0 - MONO to DSP input
- * 1 0 0 - SCART 2 to DSP input
- * 1 1 1 - Mute DSP input
- *
- * bits 11 10 6 - SCART 1 Output Select:
- * 0 0 0 - undefined (reset position)
- * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS)
- * 1 0 0 - MONO input to SCART 1 Output
- * 1 1 0 - SCART 1 DA to SCART 1 Output
- * 0 0 1 - SCART 2 DA to SCART 1 Output
- * 0 1 1 - SCART 1 Input to SCART 1 Output
- * 1 1 1 - Mute SCART 1 Output
- *
- * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART):
- * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position)
- * 0 1 0 - SCART 1 Input to SCART 2 Output
- * 1 0 0 - MONO input to SCART 2 Output
- * 0 0 1 - SCART 2 DA to SCART 2 Output
- * 0 1 1 - SCART 2 Input to SCART 2 Output
- * 1 1 0 - Mute SCART 2 Output
- *
- * Bits 4 to 0 should be zero.
- * ----------------------------------------------------------------------- */
-
-static int scarts[3][9] = {
- /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */
- /* SCART DSP Input select */
- { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 },
- /* SCART1 Output select */
- { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 },
- /* SCART2 Output select */
- { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 },
-};
-
-static char *scart_names[] = {
- "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute"
-};
-
-void msp_set_scart(struct i2c_client *client, int in, int out)
-{
- struct msp_state *state = to_state(i2c_get_clientdata(client));
-
- state->in_scart = in;
-
- if (in >= 0 && in <= 7 && out >= 0 && out <= 2) {
- if (-1 == scarts[out][in + 1])
- return;
-
- state->acb &= ~scarts[out][0];
- state->acb |= scarts[out][in + 1];
- } else
- state->acb = 0xf60; /* Mute Input and SCART 1 Output */
-
- v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n",
- scart_names[in], out, state->acb);
- msp_write_dsp(client, 0x13, state->acb);
-
- /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
- if (state->has_i2s_conf)
- msp_write_dem(client, 0x40, state->i2s_mode);
-}
-
-/* ------------------------------------------------------------------------ */
-
-static void msp_wake_thread(struct i2c_client *client)
-{
- struct msp_state *state = to_state(i2c_get_clientdata(client));
-
- if (NULL == state->kthread)
- return;
- state->watch_stereo = 0;
- state->restart = 1;
- wake_up_interruptible(&state->wq);
-}
-
-int msp_sleep(struct msp_state *state, int timeout)
-{
- DECLARE_WAITQUEUE(wait, current);
-
- add_wait_queue(&state->wq, &wait);
- if (!kthread_should_stop()) {
- if (timeout < 0) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- } else {
- schedule_timeout_interruptible
- (msecs_to_jiffies(timeout));
- }
- }
-
- remove_wait_queue(&state->wq, &wait);
- try_to_freeze();
- return state->restart;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static int msp_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct msp_state *state = ctrl_to_state(ctrl);
- struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
- int val = ctrl->val;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME: {
- /* audio volume cluster */
- int reallymuted = state->muted->val | state->scan_in_progress;
-
- if (!reallymuted)
- val = (val * 0x7f / 65535) << 8;
-
- v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n",
- state->muted->val ? "on" : "off",
- state->scan_in_progress ? "yes" : "no",
- state->volume->val);
-
- msp_write_dsp(client, 0x0000, val);
- msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_scart2_out_volume)
- msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1));
- if (state->has_headphones)
- msp_write_dsp(client, 0x0006, val);
- break;
- }
-
- case V4L2_CID_AUDIO_BASS:
- val = ((val - 32768) * 0x60 / 65535) << 8;
- msp_write_dsp(client, 0x0002, val);
- if (state->has_headphones)
- msp_write_dsp(client, 0x0031, val);
- break;
-
- case V4L2_CID_AUDIO_TREBLE:
- val = ((val - 32768) * 0x60 / 65535) << 8;
- msp_write_dsp(client, 0x0003, val);
- if (state->has_headphones)
- msp_write_dsp(client, 0x0032, val);
- break;
-
- case V4L2_CID_AUDIO_LOUDNESS:
- val = val ? ((5 * 4) << 8) : 0;
- msp_write_dsp(client, 0x0004, val);
- if (state->has_headphones)
- msp_write_dsp(client, 0x0033, val);
- break;
-
- case V4L2_CID_AUDIO_BALANCE:
- val = (u8)((val / 256) - 128);
- msp_write_dsp(client, 0x0001, val << 8);
- if (state->has_headphones)
- msp_write_dsp(client, 0x0030, val << 8);
- break;
-
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-void msp_update_volume(struct msp_state *state)
-{
- /* Force an update of the volume/mute cluster */
- v4l2_ctrl_lock(state->volume);
- state->volume->val = state->volume->cur.val;
- state->muted->val = state->muted->cur.val;
- msp_s_ctrl(state->volume);
- v4l2_ctrl_unlock(state->volume);
-}
-
-/* --- v4l2 ioctls --- */
-static int msp_s_radio(struct v4l2_subdev *sd)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (state->radio)
- return 0;
- state->radio = 1;
- v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
- state->watch_stereo = 0;
- switch (state->opmode) {
- case OPMODE_MANUAL:
- /* set msp3400 to FM radio mode */
- msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
- msp3400c_set_carrier(client, MSP_CARRIER(10.7),
- MSP_CARRIER(10.7));
- msp_update_volume(state);
- break;
- case OPMODE_AUTODETECT:
- case OPMODE_AUTOSELECT:
- /* the thread will do for us */
- msp_wake_thread(client);
- break;
- }
- return 0;
-}
-
-static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* new channel -- kick audio carrier scan */
- msp_wake_thread(client);
- return 0;
-}
-
-static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- *id &= state->detected_std;
-
- v4l_dbg(2, msp_debug, client,
- "detected standard: %s(0x%08Lx)\n",
- msp_standard_std_name(state->std), state->detected_std);
-
- return 0;
-}
-
-static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int update = state->radio || state->v4l2_std != id;
-
- state->v4l2_std = id;
- state->radio = 0;
- if (update)
- msp_wake_thread(client);
- return 0;
-}
-
-static int msp_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int tuner = (input >> 3) & 1;
- int sc_in = input & 0x7;
- int sc1_out = output & 0xf;
- int sc2_out = (output >> 4) & 0xf;
- u16 val, reg;
- int i;
- int extern_input = 1;
-
- if (state->route_in == input && state->route_out == output)
- return 0;
- state->route_in = input;
- state->route_out = output;
- /* check if the tuner input is used */
- for (i = 0; i < 5; i++) {
- if (((input >> (4 + i * 4)) & 0xf) == 0)
- extern_input = 0;
- }
- state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
- state->rxsubchans = V4L2_TUNER_SUB_STEREO;
- msp_set_scart(client, sc_in, 0);
- msp_set_scart(client, sc1_out, 1);
- msp_set_scart(client, sc2_out, 2);
- msp_set_audmode(client);
- reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
- val = msp_read_dem(client, reg);
- msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
- /* wake thread when a new input is chosen */
- msp_wake_thread(client);
- return 0;
-}
-
-static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (vt->type != V4L2_TUNER_ANALOG_TV)
- return 0;
- if (!state->radio) {
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- vt->rxsubchans = state->rxsubchans;
- }
- vt->audmode = state->audmode;
- vt->capability |= V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- return 0;
-}
-
-static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (state->radio) /* TODO: add mono/stereo support for radio */
- return 0;
- if (state->audmode == vt->audmode)
- return 0;
- state->audmode = vt->audmode;
- /* only set audmode */
- msp_set_audmode(client);
- return 0;
-}
-
-static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
-
- switch (freq) {
- case 1024000:
- state->i2s_mode = 0;
- break;
- case 2048000:
- state->i2s_mode = 1;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident,
- (state->rev1 << 16) | state->rev2);
-}
-
-static int msp_log_status(struct v4l2_subdev *sd)
-{
- struct msp_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- const char *p;
- char prefix[V4L2_SUBDEV_NAME_SIZE + 20];
-
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
- client->name, state->rev1, state->rev2);
- snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name);
- v4l2_ctrl_handler_log_status(&state->hdl, prefix);
- switch (state->mode) {
- case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
- case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
- case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break;
- case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break;
- case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break;
- case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break;
- case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break;
- case MSP_MODE_BTSC: p = "BTSC"; break;
- case MSP_MODE_EXTERN: p = "External input"; break;
- default: p = "unknown"; break;
- }
- if (state->mode == MSP_MODE_EXTERN) {
- v4l_info(client, "Mode: %s\n", p);
- } else if (state->opmode == OPMODE_MANUAL) {
- v4l_info(client, "Mode: %s (%s%s)\n", p,
- (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
- (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- } else {
- if (state->opmode == OPMODE_AUTODETECT)
- v4l_info(client, "Mode: %s\n", p);
- v4l_info(client, "Standard: %s (%s%s)\n",
- msp_standard_std_name(state->std),
- (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
- (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- }
- v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
- v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
- state->route_in, state->route_out);
- v4l_info(client, "ACB: 0x%04x\n", state->acb);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int msp_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- v4l_dbg(1, msp_debug, client, "suspend\n");
- msp_reset(client);
- return 0;
-}
-
-static int msp_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- v4l_dbg(1, msp_debug, client, "resume\n");
- msp_wake_thread(client);
- return 0;
-}
-#endif
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_ctrl_ops msp_ctrl_ops = {
- .s_ctrl = msp_s_ctrl,
-};
-
-static const struct v4l2_subdev_core_ops msp_core_ops = {
- .log_status = msp_log_status,
- .g_chip_ident = msp_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,
- .s_std = msp_s_std,
-};
-
-static const struct v4l2_subdev_video_ops msp_video_ops = {
- .querystd = msp_querystd,
-};
-
-static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
- .s_frequency = msp_s_frequency,
- .g_tuner = msp_g_tuner,
- .s_tuner = msp_s_tuner,
- .s_radio = msp_s_radio,
-};
-
-static const struct v4l2_subdev_audio_ops msp_audio_ops = {
- .s_routing = msp_s_routing,
- .s_i2s_clock_freq = msp_s_i2s_clock_freq,
-};
-
-static const struct v4l2_subdev_ops msp_ops = {
- .core = &msp_core_ops,
- .video = &msp_video_ops,
- .tuner = &msp_tuner_ops,
- .audio = &msp_audio_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct msp_state *state;
- struct v4l2_subdev *sd;
- struct v4l2_ctrl_handler *hdl;
- int (*thread_func)(void *data) = NULL;
- int msp_hard;
- int msp_family;
- int msp_revision;
- int msp_product, msp_prod_hi, msp_prod_lo;
- int msp_rom;
-
- if (!id)
- strlcpy(client->name, "msp3400", sizeof(client->name));
-
- if (msp_reset(client) == -1) {
- v4l_dbg(1, msp_debug, client, "msp3400 not found\n");
- return -ENODEV;
- }
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &msp_ops);
-
- state->v4l2_std = V4L2_STD_NTSC;
- state->detected_std = V4L2_STD_ALL;
- state->audmode = V4L2_TUNER_MODE_STEREO;
- state->input = -1;
- state->i2s_mode = 0;
- init_waitqueue_head(&state->wq);
- /* These are the reset input/output positions */
- state->route_in = MSP_INPUT_DEFAULT;
- state->route_out = MSP_OUTPUT_DEFAULT;
-
- state->rev1 = msp_read_dsp(client, 0x1e);
- if (state->rev1 != -1)
- state->rev2 = msp_read_dsp(client, 0x1f);
- v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n",
- state->rev1, state->rev2);
- if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) {
- v4l_dbg(1, msp_debug, client,
- "not an msp3400 (cannot read chip version)\n");
- kfree(state);
- return -ENODEV;
- }
-
- msp_family = ((state->rev1 >> 4) & 0x0f) + 3;
- msp_product = (state->rev2 >> 8) & 0xff;
- msp_prod_hi = msp_product / 10;
- msp_prod_lo = msp_product % 10;
- msp_revision = (state->rev1 & 0x0f) + '@';
- msp_hard = ((state->rev1 >> 8) & 0xff) + '@';
- msp_rom = state->rev2 & 0x1f;
- /* Rev B=2, C=3, D=4, G=7 */
- state->ident = msp_family * 10000 + 4000 + msp_product * 10 +
- msp_revision - '@';
-
- /* Has NICAM support: all mspx41x and mspx45x products have NICAM */
- state->has_nicam =
- msp_prod_hi == 1 || msp_prod_hi == 5;
- /* Has radio support: was added with revision G */
- state->has_radio =
- msp_revision >= 'G';
- /* Has headphones output: not for stripped down products */
- state->has_headphones =
- msp_prod_lo < 5;
- /* Has scart2 input: not in stripped down products of the '3' family */
- state->has_scart2 =
- msp_family >= 4 || msp_prod_lo < 7;
- /* Has scart3 input: not in stripped down products of the '3' family */
- state->has_scart3 =
- msp_family >= 4 || msp_prod_lo < 5;
- /* Has scart4 input: not in pre D revisions, not in stripped D revs */
- state->has_scart4 =
- msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5);
- /* Has scart2 output: not in stripped down products of
- * the '3' family */
- state->has_scart2_out =
- msp_family >= 4 || msp_prod_lo < 5;
- /* Has scart2 a volume control? Not in pre-D revisions. */
- state->has_scart2_out_volume =
- msp_revision > 'C' && state->has_scart2_out;
- /* Has a configurable i2s out? */
- state->has_i2s_conf =
- msp_revision >= 'G' && msp_prod_lo < 7;
- /* Has subwoofer output: not in pre-D revs and not in stripped down
- * products */
- state->has_subwoofer =
- msp_revision >= 'D' && msp_prod_lo < 5;
- /* Has soundprocessing (bass/treble/balance/loudness/equalizer):
- * not in stripped down products */
- state->has_sound_processing =
- msp_prod_lo < 7;
- /* Has Virtual Dolby Surround: only in msp34x1 */
- state->has_virtual_dolby_surround =
- msp_revision == 'G' && msp_prod_lo == 1;
- /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
- state->has_dolby_pro_logic =
- msp_revision == 'G' && msp_prod_lo == 2;
- /* The msp343xG supports BTSC only and cannot do Automatic Standard
- * Detection. */
- state->force_btsc =
- msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3;
-
- state->opmode = opmode;
- if (state->opmode == OPMODE_AUTO) {
- /* MSP revision G and up have both autodetect and autoselect */
- if (msp_revision >= 'G')
- state->opmode = OPMODE_AUTOSELECT;
- /* MSP revision D and up have autodetect */
- else if (msp_revision >= 'D')
- state->opmode = OPMODE_AUTODETECT;
- else
- state->opmode = OPMODE_MANUAL;
- }
-
- hdl = &state->hdl;
- v4l2_ctrl_handler_init(hdl, 6);
- if (state->has_sound_processing) {
- v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768);
- v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768);
- v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0);
- }
- state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880);
- v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768);
- state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops,
- V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0);
- sd->ctrl_handler = hdl;
- if (hdl->error) {
- int err = hdl->error;
-
- v4l2_ctrl_handler_free(hdl);
- kfree(state);
- return err;
- }
-
- v4l2_ctrl_cluster(2, &state->volume);
- v4l2_ctrl_handler_setup(hdl);
-
- /* hello world :-) */
- v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n",
- msp_family, msp_product,
- msp_revision, msp_hard, msp_rom,
- client->addr << 1, client->adapter->name);
- v4l_info(client, "%s ", client->name);
- if (state->has_nicam && state->has_radio)
- printk(KERN_CONT "supports nicam and radio, ");
- else if (state->has_nicam)
- printk(KERN_CONT "supports nicam, ");
- else if (state->has_radio)
- printk(KERN_CONT "supports radio, ");
- printk(KERN_CONT "mode is ");
-
- /* version-specific initialization */
- switch (state->opmode) {
- case OPMODE_MANUAL:
- printk(KERN_CONT "manual");
- thread_func = msp3400c_thread;
- break;
- case OPMODE_AUTODETECT:
- printk(KERN_CONT "autodetect");
- thread_func = msp3410d_thread;
- break;
- case OPMODE_AUTOSELECT:
- printk(KERN_CONT "autodetect and autoselect");
- thread_func = msp34xxg_thread;
- break;
- }
- printk(KERN_CONT "\n");
-
- /* startup control thread if needed */
- if (thread_func) {
- state->kthread = kthread_run(thread_func, client, "msp34xx");
-
- if (IS_ERR(state->kthread))
- v4l_warn(client, "kernel_thread() failed\n");
- msp_wake_thread(client);
- }
- return 0;
-}
-
-static int msp_remove(struct i2c_client *client)
-{
- struct msp_state *state = to_state(i2c_get_clientdata(client));
-
- v4l2_device_unregister_subdev(&state->sd);
- /* shutdown control thread */
- if (state->kthread) {
- state->restart = 1;
- kthread_stop(state->kthread);
- }
- msp_reset(client);
-
- v4l2_ctrl_handler_free(&state->hdl);
- kfree(state);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct dev_pm_ops msp3400_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume)
-};
-
-static const struct i2c_device_id msp_id[] = {
- { "msp3400", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, msp_id);
-
-static struct i2c_driver msp_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "msp3400",
- .pm = &msp3400_pm_ops,
- },
- .probe = msp_probe,
- .remove = msp_remove,
- .id_table = msp_id,
-};
-
-module_i2c_driver(msp_driver);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
deleted file mode 100644
index 00583f5fd26b..000000000000
--- a/drivers/media/video/mt9m001.c
+++ /dev/null
@@ -1,737 +0,0 @@
-/*
- * Driver for MT9M001 CMOS Image Sensor from Micron
- *
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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/videodev2.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/log2.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-
-/*
- * mt9m001 i2c address 0x5d
- * The platform has to define struct i2c_board_info objects and link to them
- * from struct soc_camera_link
- */
-
-/* mt9m001 selected register addresses */
-#define MT9M001_CHIP_VERSION 0x00
-#define MT9M001_ROW_START 0x01
-#define MT9M001_COLUMN_START 0x02
-#define MT9M001_WINDOW_HEIGHT 0x03
-#define MT9M001_WINDOW_WIDTH 0x04
-#define MT9M001_HORIZONTAL_BLANKING 0x05
-#define MT9M001_VERTICAL_BLANKING 0x06
-#define MT9M001_OUTPUT_CONTROL 0x07
-#define MT9M001_SHUTTER_WIDTH 0x09
-#define MT9M001_FRAME_RESTART 0x0b
-#define MT9M001_SHUTTER_DELAY 0x0c
-#define MT9M001_RESET 0x0d
-#define MT9M001_READ_OPTIONS1 0x1e
-#define MT9M001_READ_OPTIONS2 0x20
-#define MT9M001_GLOBAL_GAIN 0x35
-#define MT9M001_CHIP_ENABLE 0xF1
-
-#define MT9M001_MAX_WIDTH 1280
-#define MT9M001_MAX_HEIGHT 1024
-#define MT9M001_MIN_WIDTH 48
-#define MT9M001_MIN_HEIGHT 32
-#define MT9M001_COLUMN_SKIP 20
-#define MT9M001_ROW_SKIP 12
-
-/* MT9M001 has only one fixed colorspace per pixelcode */
-struct mt9m001_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-/* Find a data format by a pixel code in an array */
-static const struct mt9m001_datafmt *mt9m001_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt,
- int n)
-{
- int i;
- for (i = 0; i < n; i++)
- if (fmt[i].code == code)
- return fmt + i;
-
- return NULL;
-}
-
-static const struct mt9m001_datafmt mt9m001_colour_fmts[] = {
- /*
- * Order important: first natively supported,
- * second supported with a GPIO extender
- */
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
-};
-
-static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = {
- /* Order important - see above */
- {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
-};
-
-struct mt9m001 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct {
- /* exposure/auto-exposure cluster */
- struct v4l2_ctrl *autoexposure;
- struct v4l2_ctrl *exposure;
- };
- struct v4l2_rect rect; /* Sensor window */
- const struct mt9m001_datafmt *fmt;
- const struct mt9m001_datafmt *fmts;
- int num_fmts;
- int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
- unsigned int total_h;
- unsigned short y_skip_top; /* Lines to skip at the top */
-};
-
-static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
-}
-
-static int reg_read(struct i2c_client *client, const u8 reg)
-{
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int reg_write(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static int reg_set(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret | data);
-}
-
-static int reg_clear(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret & ~data);
-}
-
-static int mt9m001_init(struct i2c_client *client)
-{
- int ret;
-
- dev_dbg(&client->dev, "%s\n", __func__);
-
- /*
- * We don't know, whether platform provides reset, issue a soft reset
- * too. This returns all registers to their default values.
- */
- ret = reg_write(client, MT9M001_RESET, 1);
- if (!ret)
- ret = reg_write(client, MT9M001_RESET, 0);
-
- /* Disable chip, synchronous option update */
- if (!ret)
- ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
-
- return ret;
-}
-
-static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* Switch to master "normal" mode or stop sensor readout */
- if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
- return -EIO;
- return 0;
-}
-
-static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct v4l2_rect rect = a->c;
- int ret;
- const u16 hblank = 9, vblank = 25;
-
- if (mt9m001->fmts == mt9m001_colour_fmts)
- /*
- * Bayer format - even number of rows for simplicity,
- * but let the user play with the top row.
- */
- rect.height = ALIGN(rect.height, 2);
-
- /* Datasheet requirement: see register description */
- rect.width = ALIGN(rect.width, 2);
- rect.left = ALIGN(rect.left, 2);
-
- soc_camera_limit_side(&rect.left, &rect.width,
- MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
-
- soc_camera_limit_side(&rect.top, &rect.height,
- MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
-
- mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank;
-
- /* Blanking and start values - default... */
- ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
- if (!ret)
- ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank);
-
- /*
- * The caller provides a supported format, as verified per
- * call to .try_mbus_fmt()
- */
- if (!ret)
- ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
- if (!ret)
- ret = reg_write(client, MT9M001_ROW_START, rect.top);
- if (!ret)
- ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
- if (!ret)
- ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
- rect.height + mt9m001->y_skip_top - 1);
- if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO)
- ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h);
-
- if (!ret)
- mt9m001->rect = rect;
-
- return ret;
-}
-
-static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- a->c = mt9m001->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = MT9M001_COLUMN_SKIP;
- a->bounds.top = MT9M001_ROW_SKIP;
- a->bounds.width = MT9M001_MAX_WIDTH;
- a->bounds.height = MT9M001_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int mt9m001_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- mf->width = mt9m001->rect.width;
- mf->height = mt9m001->rect.height;
- mf->code = mt9m001->fmt->code;
- mf->colorspace = mt9m001->fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9m001_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct v4l2_crop a = {
- .c = {
- .left = mt9m001->rect.left,
- .top = mt9m001->rect.top,
- .width = mf->width,
- .height = mf->height,
- },
- };
- int ret;
-
- /* No support for scaling so far, just crop. TODO: use skipping */
- ret = mt9m001_s_crop(sd, &a);
- if (!ret) {
- mf->width = mt9m001->rect.width;
- mf->height = mt9m001->rect.height;
- mt9m001->fmt = mt9m001_find_datafmt(mf->code,
- mt9m001->fmts, mt9m001->num_fmts);
- mf->colorspace = mt9m001->fmt->colorspace;
- }
-
- return ret;
-}
-
-static int mt9m001_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- const struct mt9m001_datafmt *fmt;
-
- v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH,
- MT9M001_MAX_WIDTH, 1,
- &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top,
- MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0);
-
- if (mt9m001->fmts == mt9m001_colour_fmts)
- mf->height = ALIGN(mf->height - 1, 2);
-
- fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts,
- mt9m001->num_fmts);
- if (!fmt) {
- fmt = mt9m001->fmt;
- mf->code = fmt->code;
- }
-
- mf->colorspace = fmt->colorspace;
-
- return 0;
-}
-
-static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9m001->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m001_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- reg->size = 2;
- reg->val = reg_read(client, reg->reg);
-
- if (reg->val > 0xffff)
- return -EIO;
-
- return 0;
-}
-
-static int mt9m001_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- if (reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9m001 *mt9m001 = container_of(ctrl->handler,
- struct mt9m001, hdl);
- s32 min, max;
-
- switch (ctrl->id) {
- case V4L2_CID_EXPOSURE_AUTO:
- min = mt9m001->exposure->minimum;
- max = mt9m001->exposure->maximum;
- mt9m001->exposure->val =
- (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min;
- break;
- }
- return 0;
-}
-
-static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9m001 *mt9m001 = container_of(ctrl->handler,
- struct mt9m001, hdl);
- struct v4l2_subdev *sd = &mt9m001->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct v4l2_ctrl *exp = mt9m001->exposure;
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000);
- else
- data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000);
- if (data < 0)
- return -EIO;
- return 0;
-
- case V4L2_CID_GAIN:
- /* See Datasheet Table 7, Gain settings. */
- if (ctrl->val <= ctrl->default_value) {
- /* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = ctrl->default_value - ctrl->minimum;
- data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
-
- dev_dbg(&client->dev, "Setting gain %d\n", data);
- data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
- } else {
- /* Pack it into 1.125..15 variable step, register values 9..67 */
- /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = ctrl->maximum - ctrl->default_value - 1;
- unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
- 111 + range / 2) / range + 9;
-
- if (gain <= 32)
- data = gain;
- else if (gain <= 64)
- data = ((gain - 32) * 16 + 16) / 32 + 80;
- else
- data = ((gain - 64) * 7 + 28) / 56 + 96;
-
- dev_dbg(&client->dev, "Setting gain from %d to %d\n",
- reg_read(client, MT9M001_GLOBAL_GAIN), data);
- data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
- }
- return 0;
-
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
- unsigned long range = exp->maximum - exp->minimum;
- unsigned long shutter = ((exp->val - exp->minimum) * 1048 +
- range / 2) / range + 1;
-
- dev_dbg(&client->dev,
- "Setting shutter width from %d to %lu\n",
- reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
- if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
- return -EIO;
- } else {
- const u16 vblank = 25;
-
- mt9m001->total_h = mt9m001->rect.height +
- mt9m001->y_skip_top + vblank;
- if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0)
- return -EIO;
- }
- return 0;
- }
- return -EINVAL;
-}
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int mt9m001_video_probe(struct soc_camera_link *icl,
- struct i2c_client *client)
-{
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- s32 data;
- unsigned long flags;
- int ret;
-
- /* Enable the chip */
- data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
- dev_dbg(&client->dev, "write: %d\n", data);
-
- /* Read out the chip version register */
- data = reg_read(client, MT9M001_CHIP_VERSION);
-
- /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
- switch (data) {
- case 0x8411:
- case 0x8421:
- mt9m001->model = V4L2_IDENT_MT9M001C12ST;
- mt9m001->fmts = mt9m001_colour_fmts;
- break;
- case 0x8431:
- mt9m001->model = V4L2_IDENT_MT9M001C12STM;
- mt9m001->fmts = mt9m001_monochrome_fmts;
- break;
- default:
- dev_err(&client->dev,
- "No MT9M001 chip detected, register read %x\n", data);
- return -ENODEV;
- }
-
- mt9m001->num_fmts = 0;
-
- /*
- * This is a 10bit sensor, so by default we only allow 10bit.
- * The platform may support different bus widths due to
- * different routing of the data lines.
- */
- if (icl->query_bus_param)
- flags = icl->query_bus_param(icl);
- else
- flags = SOCAM_DATAWIDTH_10;
-
- if (flags & SOCAM_DATAWIDTH_10)
- mt9m001->num_fmts++;
- else
- mt9m001->fmts++;
-
- if (flags & SOCAM_DATAWIDTH_8)
- mt9m001->num_fmts++;
-
- mt9m001->fmt = &mt9m001->fmts[0];
-
- dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
- data == 0x8431 ? "C12STM" : "C12ST");
-
- ret = mt9m001_init(client);
- if (ret < 0)
- dev_err(&client->dev, "Failed to initialise the camera\n");
-
- /* mt9m001_init() has reset the chip, returning registers to defaults */
- return v4l2_ctrl_handler_setup(&mt9m001->hdl);
-}
-
-static void mt9m001_video_remove(struct soc_camera_link *icl)
-{
- if (icl->free_bus)
- icl->free_bus(icl);
-}
-
-static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- *lines = mt9m001->y_skip_top;
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = {
- .g_volatile_ctrl = mt9m001_g_volatile_ctrl,
- .s_ctrl = mt9m001_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
- .g_chip_ident = mt9m001_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9m001_g_register,
- .s_register = mt9m001_s_register,
-#endif
-};
-
-static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
-
- if (index >= mt9m001->num_fmts)
- return -EINVAL;
-
- *code = mt9m001->fmts[index].code;
- return 0;
-}
-
-static int mt9m001_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- /* MT9M001 has all capture_format parameters fixed */
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int mt9m001_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- const struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample;
-
- if (icl->set_bus_param)
- return icl->set_bus_param(icl, 1 << (bps - 1));
-
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- return bps == 10 ? 0 : -EINVAL;
-}
-
-static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
- .s_stream = mt9m001_s_stream,
- .s_mbus_fmt = mt9m001_s_fmt,
- .g_mbus_fmt = mt9m001_g_fmt,
- .try_mbus_fmt = mt9m001_try_fmt,
- .s_crop = mt9m001_s_crop,
- .g_crop = mt9m001_g_crop,
- .cropcap = mt9m001_cropcap,
- .enum_mbus_fmt = mt9m001_enum_fmt,
- .g_mbus_config = mt9m001_g_mbus_config,
- .s_mbus_config = mt9m001_s_mbus_config,
-};
-
-static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = {
- .g_skip_top_lines = mt9m001_g_skip_top_lines,
-};
-
-static struct v4l2_subdev_ops mt9m001_subdev_ops = {
- .core = &mt9m001_subdev_core_ops,
- .video = &mt9m001_subdev_video_ops,
- .sensor = &mt9m001_subdev_sensor_ops,
-};
-
-static int mt9m001_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9m001 *mt9m001;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "MT9M001 driver needs platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL);
- if (!mt9m001)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
- v4l2_ctrl_handler_init(&mt9m001->hdl, 4);
- v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 64);
- mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, 255, 1, 255);
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl,
- &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
- V4L2_EXPOSURE_AUTO);
- mt9m001->subdev.ctrl_handler = &mt9m001->hdl;
- if (mt9m001->hdl.error) {
- int err = mt9m001->hdl.error;
-
- kfree(mt9m001);
- return err;
- }
- v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure,
- V4L2_EXPOSURE_MANUAL, true);
-
- /* Second stage probe - when a capture adapter is there */
- mt9m001->y_skip_top = 0;
- mt9m001->rect.left = MT9M001_COLUMN_SKIP;
- mt9m001->rect.top = MT9M001_ROW_SKIP;
- mt9m001->rect.width = MT9M001_MAX_WIDTH;
- mt9m001->rect.height = MT9M001_MAX_HEIGHT;
-
- ret = mt9m001_video_probe(icl, client);
- if (ret) {
- v4l2_ctrl_handler_free(&mt9m001->hdl);
- kfree(mt9m001);
- }
-
- return ret;
-}
-
-static int mt9m001_remove(struct i2c_client *client)
-{
- struct mt9m001 *mt9m001 = to_mt9m001(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- v4l2_device_unregister_subdev(&mt9m001->subdev);
- v4l2_ctrl_handler_free(&mt9m001->hdl);
- mt9m001_video_remove(icl);
- kfree(mt9m001);
-
- return 0;
-}
-
-static const struct i2c_device_id mt9m001_id[] = {
- { "mt9m001", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9m001_id);
-
-static struct i2c_driver mt9m001_i2c_driver = {
- .driver = {
- .name = "mt9m001",
- },
- .probe = mt9m001_probe,
- .remove = mt9m001_remove,
- .id_table = mt9m001_id,
-};
-
-module_i2c_driver(mt9m001_i2c_driver);
-
-MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c
deleted file mode 100644
index 445359c96113..000000000000
--- a/drivers/media/video/mt9m032.c
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * Driver for MT9M032 CMOS Image Sensor from Micron
- *
- * Copyright (C) 2010-2011 Lund Engineering
- * Contact: Gil Lund <gwlund@lundeng.com>
- * Author: Martin Hostettler <martin@neutronstar.dyndns.org>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-
-#include <media/media-entity.h>
-#include <media/mt9m032.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#include "aptina-pll.h"
-
-/*
- * width and height include active boundary and black parts
- *
- * column 0- 15 active boundary
- * column 16-1455 image
- * column 1456-1471 active boundary
- * column 1472-1599 black
- *
- * row 0- 51 black
- * row 53- 59 active boundary
- * row 60-1139 image
- * row 1140-1147 active boundary
- * row 1148-1151 black
- */
-
-#define MT9M032_PIXEL_ARRAY_WIDTH 1600
-#define MT9M032_PIXEL_ARRAY_HEIGHT 1152
-
-#define MT9M032_CHIP_VERSION 0x00
-#define MT9M032_CHIP_VERSION_VALUE 0x1402
-#define MT9M032_ROW_START 0x01
-#define MT9M032_ROW_START_MIN 0
-#define MT9M032_ROW_START_MAX 1152
-#define MT9M032_ROW_START_DEF 60
-#define MT9M032_COLUMN_START 0x02
-#define MT9M032_COLUMN_START_MIN 0
-#define MT9M032_COLUMN_START_MAX 1600
-#define MT9M032_COLUMN_START_DEF 16
-#define MT9M032_ROW_SIZE 0x03
-#define MT9M032_ROW_SIZE_MIN 32
-#define MT9M032_ROW_SIZE_MAX 1152
-#define MT9M032_ROW_SIZE_DEF 1080
-#define MT9M032_COLUMN_SIZE 0x04
-#define MT9M032_COLUMN_SIZE_MIN 32
-#define MT9M032_COLUMN_SIZE_MAX 1600
-#define MT9M032_COLUMN_SIZE_DEF 1440
-#define MT9M032_HBLANK 0x05
-#define MT9M032_VBLANK 0x06
-#define MT9M032_VBLANK_MAX 0x7ff
-#define MT9M032_SHUTTER_WIDTH_HIGH 0x08
-#define MT9M032_SHUTTER_WIDTH_LOW 0x09
-#define MT9M032_SHUTTER_WIDTH_MIN 1
-#define MT9M032_SHUTTER_WIDTH_MAX 1048575
-#define MT9M032_SHUTTER_WIDTH_DEF 1943
-#define MT9M032_PIX_CLK_CTRL 0x0a
-#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000
-#define MT9M032_RESTART 0x0b
-#define MT9M032_RESET 0x0d
-#define MT9M032_PLL_CONFIG1 0x11
-#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f
-#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8
-#define MT9M032_READ_MODE1 0x1e
-#define MT9M032_READ_MODE2 0x20
-#define MT9M032_READ_MODE2_VFLIP_SHIFT 15
-#define MT9M032_READ_MODE2_HFLIP_SHIFT 14
-#define MT9M032_READ_MODE2_ROW_BLC 0x40
-#define MT9M032_GAIN_GREEN1 0x2b
-#define MT9M032_GAIN_BLUE 0x2c
-#define MT9M032_GAIN_RED 0x2d
-#define MT9M032_GAIN_GREEN2 0x2e
-
-/* write only */
-#define MT9M032_GAIN_ALL 0x35
-#define MT9M032_GAIN_DIGITAL_MASK 0x7f
-#define MT9M032_GAIN_DIGITAL_SHIFT 8
-#define MT9M032_GAIN_AMUL_SHIFT 6
-#define MT9M032_GAIN_ANALOG_MASK 0x3f
-#define MT9M032_FORMATTER1 0x9e
-#define MT9M032_FORMATTER2 0x9f
-#define MT9M032_FORMATTER2_DOUT_EN 0x1000
-#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000
-
-/*
- * The available MT9M032 datasheet is missing documentation for register 0x10
- * MT9P031 seems to be close enough, so use constants from that datasheet for
- * now.
- * But keep the name MT9P031 to remind us, that this isn't really confirmed
- * for this sensor.
- */
-#define MT9P031_PLL_CONTROL 0x10
-#define MT9P031_PLL_CONTROL_PWROFF 0x0050
-#define MT9P031_PLL_CONTROL_PWRON 0x0051
-#define MT9P031_PLL_CONTROL_USEPLL 0x0052
-#define MT9P031_PLL_CONFIG2 0x11
-#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f
-
-struct mt9m032 {
- struct v4l2_subdev subdev;
- struct media_pad pad;
- struct mt9m032_platform_data *pdata;
-
- unsigned int pix_clock;
-
- struct v4l2_ctrl_handler ctrls;
- struct {
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- };
-
- struct mutex lock; /* Protects streaming, format, interval and crop */
-
- bool streaming;
-
- struct v4l2_mbus_framefmt format;
- struct v4l2_rect crop;
- struct v4l2_fract frame_interval;
-};
-
-#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev)
-#define to_dev(sensor) \
- (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev)
-
-static int mt9m032_read(struct i2c_client *client, u8 reg)
-{
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data)
-{
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width)
-{
- unsigned int effective_width;
- u32 ns;
-
- effective_width = width + 716; /* empirical value */
- ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock);
- dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns);
- return ns;
-}
-
-static int mt9m032_update_timing(struct mt9m032 *sensor,
- struct v4l2_fract *interval)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- struct v4l2_rect *crop = &sensor->crop;
- unsigned int min_vblank;
- unsigned int vblank;
- u32 row_time;
-
- if (!interval)
- interval = &sensor->frame_interval;
-
- row_time = mt9m032_row_time(sensor, crop->width);
-
- vblank = div_u64(1000000000ULL * interval->numerator,
- (u64)row_time * interval->denominator)
- - crop->height;
-
- if (vblank > MT9M032_VBLANK_MAX) {
- /* hardware limits to 11 bit values */
- interval->denominator = 1000;
- interval->numerator =
- div_u64((crop->height + MT9M032_VBLANK_MAX) *
- (u64)row_time * interval->denominator,
- 1000000000ULL);
- vblank = div_u64(1000000000ULL * interval->numerator,
- (u64)row_time * interval->denominator)
- - crop->height;
- }
- /* enforce minimal 1.6ms blanking time. */
- min_vblank = 1600000 / row_time;
- vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX);
-
- return mt9m032_write(client, MT9M032_VBLANK, vblank);
-}
-
-static int mt9m032_update_geom_timing(struct mt9m032 *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- int ret;
-
- ret = mt9m032_write(client, MT9M032_COLUMN_SIZE,
- sensor->crop.width - 1);
- if (!ret)
- ret = mt9m032_write(client, MT9M032_ROW_SIZE,
- sensor->crop.height - 1);
- if (!ret)
- ret = mt9m032_write(client, MT9M032_COLUMN_START,
- sensor->crop.left);
- if (!ret)
- ret = mt9m032_write(client, MT9M032_ROW_START,
- sensor->crop.top);
- if (!ret)
- ret = mt9m032_update_timing(sensor, NULL);
- return ret;
-}
-
-static int update_formatter2(struct mt9m032 *sensor, bool streaming)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- u16 reg_val = MT9M032_FORMATTER2_DOUT_EN
- | 0x0070; /* parts reserved! */
- /* possibly for changing to 14-bit mode */
-
- if (streaming)
- reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */
-
- return mt9m032_write(client, MT9M032_FORMATTER2, reg_val);
-}
-
-static int mt9m032_setup_pll(struct mt9m032 *sensor)
-{
- static const struct aptina_pll_limits limits = {
- .ext_clock_min = 8000000,
- .ext_clock_max = 16500000,
- .int_clock_min = 2000000,
- .int_clock_max = 24000000,
- .out_clock_min = 322000000,
- .out_clock_max = 693000000,
- .pix_clock_max = 99000000,
- .n_min = 1,
- .n_max = 64,
- .m_min = 16,
- .m_max = 255,
- .p1_min = 1,
- .p1_max = 128,
- };
-
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- struct mt9m032_platform_data *pdata = sensor->pdata;
- struct aptina_pll pll;
- int ret;
-
- pll.ext_clock = pdata->ext_clock;
- pll.pix_clock = pdata->pix_clock;
-
- ret = aptina_pll_calculate(&client->dev, &limits, &pll);
- if (ret < 0)
- return ret;
-
- sensor->pix_clock = pdata->pix_clock;
-
- ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
- (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
- | (pll.p1 - 1));
- if (!ret)
- ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
- if (!ret)
- ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
- MT9P031_PLL_CONTROL_PWRON |
- MT9P031_PLL_CONTROL_USEPLL);
- if (!ret) /* more reserved, Continuous, Master Mode */
- ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
- if (!ret) /* Set 14-bit mode, select 7 divider */
- ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Subdev pad operations
- */
-
-static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index != 0)
- return -EINVAL;
-
- code->code = V4L2_MBUS_FMT_Y8_1X8;
- return 0;
-}
-
-static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
- return -EINVAL;
-
- fse->min_width = MT9M032_COLUMN_SIZE_DEF;
- fse->max_width = MT9M032_COLUMN_SIZE_DEF;
- fse->min_height = MT9M032_ROW_SIZE_DEF;
- fse->max_height = MT9M032_ROW_SIZE_DEF;
-
- return 0;
-}
-
-/**
- * __mt9m032_get_pad_crop() - get crop rect
- * @sensor: pointer to the sensor struct
- * @fh: file handle for getting the try crop rect from
- * @which: select try or active crop rect
- *
- * Returns a pointer the current active or fh relative try crop rect
- */
-static struct v4l2_rect *
-__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_crop(fh, 0);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &sensor->crop;
- default:
- return NULL;
- }
-}
-
-/**
- * __mt9m032_get_pad_format() - get format
- * @sensor: pointer to the sensor struct
- * @fh: file handle for getting the try format from
- * @which: select try or active format
- *
- * Returns a pointer the current active or fh relative try format
- */
-static struct v4l2_mbus_framefmt *
-__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_format(fh, 0);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &sensor->format;
- default:
- return NULL;
- }
-}
-
-static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
-
- mutex_lock(&sensor->lock);
- fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
- mutex_unlock(&sensor->lock);
-
- return 0;
-}
-
-static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
- int ret;
-
- mutex_lock(&sensor->lock);
-
- if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- ret = -EBUSY;
- goto done;
- }
-
- /* Scaling is not supported, the format is thus fixed. */
- fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
- ret = 0;
-
-done:
- mutex_unlock(&sensor->lock);
- return ret;
-}
-
-static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
-
- mutex_lock(&sensor->lock);
- crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
- mutex_unlock(&sensor->lock);
-
- return 0;
-}
-
-static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *__crop;
- struct v4l2_rect rect;
- int ret = 0;
-
- mutex_lock(&sensor->lock);
-
- if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- ret = -EBUSY;
- goto done;
- }
-
- /* Clamp the crop rectangle boundaries and align them to a multiple of 2
- * pixels to ensure a GRBG Bayer pattern.
- */
- rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
- MT9M032_COLUMN_START_MAX);
- rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
- MT9M032_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
- MT9M032_COLUMN_SIZE_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
- MT9M032_ROW_SIZE_MAX);
-
- rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
-
- __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
-
- if (rect.width != __crop->width || rect.height != __crop->height) {
- /* Reset the output image size if the crop rectangle size has
- * been modified.
- */
- format = __mt9m032_get_pad_format(sensor, fh, crop->which);
- format->width = rect.width;
- format->height = rect.height;
- }
-
- *__crop = rect;
- crop->rect = rect;
-
- if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- ret = mt9m032_update_geom_timing(sensor);
-
-done:
- mutex_unlock(&sensor->lock);
- return ret;
-}
-
-static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev,
- struct v4l2_subdev_frame_interval *fi)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
-
- mutex_lock(&sensor->lock);
- memset(fi, 0, sizeof(*fi));
- fi->interval = sensor->frame_interval;
- mutex_unlock(&sensor->lock);
-
- return 0;
-}
-
-static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev,
- struct v4l2_subdev_frame_interval *fi)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
- int ret;
-
- mutex_lock(&sensor->lock);
-
- if (sensor->streaming) {
- ret = -EBUSY;
- goto done;
- }
-
- /* Avoid divisions by 0. */
- if (fi->interval.denominator == 0)
- fi->interval.denominator = 1;
-
- ret = mt9m032_update_timing(sensor, &fi->interval);
- if (!ret)
- sensor->frame_interval = fi->interval;
-
-done:
- mutex_unlock(&sensor->lock);
- return ret;
-}
-
-static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming)
-{
- struct mt9m032 *sensor = to_mt9m032(subdev);
- int ret;
-
- mutex_lock(&sensor->lock);
- ret = update_formatter2(sensor, streaming);
- if (!ret)
- sensor->streaming = streaming;
- mutex_unlock(&sensor->lock);
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m032_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct mt9m032 *sensor = to_mt9m032(sd);
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- int val;
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- val = mt9m032_read(client, reg->reg);
- if (val < 0)
- return -EIO;
-
- reg->size = 2;
- reg->val = val;
-
- return 0;
-}
-
-static int mt9m032_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct mt9m032 *sensor = to_mt9m032(sd);
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- return mt9m032_write(client, reg->reg, reg->val);
-}
-#endif
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev control operations
- */
-
-static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT)
- | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT)
- | MT9M032_READ_MODE2_ROW_BLC
- | 0x0007;
-
- return mt9m032_write(client, MT9M032_READ_MODE2, reg_val);
-}
-
-static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- int digital_gain_val; /* in 1/8th (0..127) */
- int analog_mul; /* 0 or 1 */
- int analog_gain_val; /* in 1/16th. (0..63) */
- u16 reg_val;
-
- digital_gain_val = 51; /* from setup example */
-
- if (val < 63) {
- analog_mul = 0;
- analog_gain_val = val;
- } else {
- analog_mul = 1;
- analog_gain_val = val / 2;
- }
-
- /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */
- /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
-
- reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK)
- << MT9M032_GAIN_DIGITAL_SHIFT)
- | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT)
- | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
-
- return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val);
-}
-
-static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl)
-{
- if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) {
- /* round because of multiplier used for values >= 63 */
- ctrl->val &= ~1;
- }
-
- return 0;
-}
-
-static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9m032 *sensor =
- container_of(ctrl->handler, struct mt9m032, ctrls);
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
- int ret;
-
- switch (ctrl->id) {
- case V4L2_CID_GAIN:
- return mt9m032_set_gain(sensor, ctrl->val);
-
- case V4L2_CID_HFLIP:
- /* case V4L2_CID_VFLIP: -- In the same cluster */
- return update_read_mode2(sensor, sensor->vflip->val,
- sensor->hflip->val);
-
- case V4L2_CID_EXPOSURE:
- ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH,
- (ctrl->val >> 16) & 0xffff);
- if (ret < 0)
- return ret;
-
- return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW,
- ctrl->val & 0xffff);
- }
-
- return 0;
-}
-
-static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
- .s_ctrl = mt9m032_set_ctrl,
- .try_ctrl = mt9m032_try_ctrl,
-};
-
-/* -------------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops mt9m032_core_ops = {
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9m032_g_register,
- .s_register = mt9m032_s_register,
-#endif
-};
-
-static const struct v4l2_subdev_video_ops mt9m032_video_ops = {
- .s_stream = mt9m032_s_stream,
- .g_frame_interval = mt9m032_get_frame_interval,
- .s_frame_interval = mt9m032_set_frame_interval,
-};
-
-static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = {
- .enum_mbus_code = mt9m032_enum_mbus_code,
- .enum_frame_size = mt9m032_enum_frame_size,
- .get_fmt = mt9m032_get_pad_format,
- .set_fmt = mt9m032_set_pad_format,
- .set_crop = mt9m032_set_pad_crop,
- .get_crop = mt9m032_get_pad_crop,
-};
-
-static const struct v4l2_subdev_ops mt9m032_ops = {
- .core = &mt9m032_core_ops,
- .video = &mt9m032_video_ops,
- .pad = &mt9m032_pad_ops,
-};
-
-/* -----------------------------------------------------------------------------
- * Driver initialization and probing
- */
-
-static int mt9m032_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- struct mt9m032_platform_data *pdata = client->dev.platform_data;
- struct i2c_adapter *adapter = client->adapter;
- struct mt9m032 *sensor;
- int chip_version;
- int ret;
-
- if (pdata == NULL) {
- dev_err(&client->dev, "No platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&client->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- if (!client->dev.platform_data)
- return -ENODEV;
-
- sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
- if (sensor == NULL)
- return -ENOMEM;
-
- mutex_init(&sensor->lock);
-
- sensor->pdata = pdata;
-
- v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
- sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
- if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
- dev_err(&client->dev, "MT9M032 not detected, wrong version "
- "0x%04x\n", chip_version);
- ret = -ENODEV;
- goto error_sensor;
- }
-
- dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
- client->addr);
-
- sensor->frame_interval.numerator = 1;
- sensor->frame_interval.denominator = 30;
-
- sensor->crop.left = MT9M032_COLUMN_START_DEF;
- sensor->crop.top = MT9M032_ROW_START_DEF;
- sensor->crop.width = MT9M032_COLUMN_SIZE_DEF;
- sensor->crop.height = MT9M032_ROW_SIZE_DEF;
-
- sensor->format.width = sensor->crop.width;
- sensor->format.height = sensor->crop.height;
- sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
- sensor->format.field = V4L2_FIELD_NONE;
- sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
-
- v4l2_ctrl_handler_init(&sensor->ctrls, 5);
-
- v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 64);
-
- sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
- &mt9m032_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
- &mt9m032_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-
- v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
- V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
- MT9M032_SHUTTER_WIDTH_MAX, 1,
- MT9M032_SHUTTER_WIDTH_DEF);
- v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
- V4L2_CID_PIXEL_RATE, pdata->pix_clock,
- pdata->pix_clock, 1, pdata->pix_clock);
-
- if (sensor->ctrls.error) {
- ret = sensor->ctrls.error;
- dev_err(&client->dev, "control initialization error %d\n", ret);
- goto error_ctrl;
- }
-
- v4l2_ctrl_cluster(2, &sensor->hflip);
-
- sensor->subdev.ctrl_handler = &sensor->ctrls;
- sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
- if (ret < 0)
- goto error_ctrl;
-
- ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */
- if (ret < 0)
- goto error_entity;
- mt9m032_write(client, MT9M032_RESET, 0); /* reset off */
- if (ret < 0)
- goto error_entity;
-
- ret = mt9m032_setup_pll(sensor);
- if (ret < 0)
- goto error_entity;
- usleep_range(10000, 11000);
-
- ret = v4l2_ctrl_handler_setup(&sensor->ctrls);
- if (ret < 0)
- goto error_entity;
-
- /* SIZE */
- ret = mt9m032_update_geom_timing(sensor);
- if (ret < 0)
- goto error_entity;
-
- ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */
- if (ret < 0)
- goto error_entity;
- ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */
- if (ret < 0)
- goto error_entity;
- ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */
- if (ret < 0)
- goto error_entity;
- ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */
- if (ret < 0)
- goto error_entity;
- if (sensor->pdata->invert_pixclock) {
- ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
- MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
- if (ret < 0)
- goto error_entity;
- }
-
- ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
- if (ret < 0)
- goto error_entity;
- msleep(100);
- ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
- if (ret < 0)
- goto error_entity;
- msleep(100);
- ret = update_formatter2(sensor, false);
- if (ret < 0)
- goto error_entity;
-
- return ret;
-
-error_entity:
- media_entity_cleanup(&sensor->subdev.entity);
-error_ctrl:
- v4l2_ctrl_handler_free(&sensor->ctrls);
-error_sensor:
- mutex_destroy(&sensor->lock);
- kfree(sensor);
- return ret;
-}
-
-static int mt9m032_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct mt9m032 *sensor = to_mt9m032(subdev);
-
- v4l2_device_unregister_subdev(subdev);
- v4l2_ctrl_handler_free(&sensor->ctrls);
- media_entity_cleanup(&subdev->entity);
- mutex_destroy(&sensor->lock);
- kfree(sensor);
- return 0;
-}
-
-static const struct i2c_device_id mt9m032_id_table[] = {
- { MT9M032_NAME, 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(i2c, mt9m032_id_table);
-
-static struct i2c_driver mt9m032_i2c_driver = {
- .driver = {
- .name = MT9M032_NAME,
- },
- .probe = mt9m032_probe,
- .remove = mt9m032_remove,
- .id_table = mt9m032_id_table,
-};
-
-module_i2c_driver(mt9m032_i2c_driver);
-
-MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>");
-MODULE_DESCRIPTION("MT9M032 camera sensor driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
deleted file mode 100644
index 863d722dda06..000000000000
--- a/drivers/media/video/mt9m111.c
+++ /dev/null
@@ -1,1014 +0,0 @@
-/*
- * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina
- *
- * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
- *
- * 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/videodev2.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/log2.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
-
-/*
- * MT9M111, MT9M112 and MT9M131:
- * i2c address is 0x48 or 0x5d (depending on SADDR pin)
- * The platform has to define i2c_board_info and call i2c_register_board_info()
- */
-
-/*
- * Sensor core register addresses (0x000..0x0ff)
- */
-#define MT9M111_CHIP_VERSION 0x000
-#define MT9M111_ROW_START 0x001
-#define MT9M111_COLUMN_START 0x002
-#define MT9M111_WINDOW_HEIGHT 0x003
-#define MT9M111_WINDOW_WIDTH 0x004
-#define MT9M111_HORIZONTAL_BLANKING_B 0x005
-#define MT9M111_VERTICAL_BLANKING_B 0x006
-#define MT9M111_HORIZONTAL_BLANKING_A 0x007
-#define MT9M111_VERTICAL_BLANKING_A 0x008
-#define MT9M111_SHUTTER_WIDTH 0x009
-#define MT9M111_ROW_SPEED 0x00a
-#define MT9M111_EXTRA_DELAY 0x00b
-#define MT9M111_SHUTTER_DELAY 0x00c
-#define MT9M111_RESET 0x00d
-#define MT9M111_READ_MODE_B 0x020
-#define MT9M111_READ_MODE_A 0x021
-#define MT9M111_FLASH_CONTROL 0x023
-#define MT9M111_GREEN1_GAIN 0x02b
-#define MT9M111_BLUE_GAIN 0x02c
-#define MT9M111_RED_GAIN 0x02d
-#define MT9M111_GREEN2_GAIN 0x02e
-#define MT9M111_GLOBAL_GAIN 0x02f
-#define MT9M111_CONTEXT_CONTROL 0x0c8
-#define MT9M111_PAGE_MAP 0x0f0
-#define MT9M111_BYTE_WISE_ADDR 0x0f1
-
-#define MT9M111_RESET_SYNC_CHANGES (1 << 15)
-#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9)
-#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8)
-#define MT9M111_RESET_RESET_SOC (1 << 5)
-#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4)
-#define MT9M111_RESET_CHIP_ENABLE (1 << 3)
-#define MT9M111_RESET_ANALOG_STANDBY (1 << 2)
-#define MT9M111_RESET_RESTART_FRAME (1 << 1)
-#define MT9M111_RESET_RESET_MODE (1 << 0)
-
-#define MT9M111_RM_FULL_POWER_RD (0 << 10)
-#define MT9M111_RM_LOW_POWER_RD (1 << 10)
-#define MT9M111_RM_COL_SKIP_4X (1 << 5)
-#define MT9M111_RM_ROW_SKIP_4X (1 << 4)
-#define MT9M111_RM_COL_SKIP_2X (1 << 3)
-#define MT9M111_RM_ROW_SKIP_2X (1 << 2)
-#define MT9M111_RMB_MIRROR_COLS (1 << 1)
-#define MT9M111_RMB_MIRROR_ROWS (1 << 0)
-#define MT9M111_CTXT_CTRL_RESTART (1 << 15)
-#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12)
-#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10)
-#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9)
-#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8)
-#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7)
-#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3)
-#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2)
-#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1)
-#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0)
-
-/*
- * Colorpipe register addresses (0x100..0x1ff)
- */
-#define MT9M111_OPER_MODE_CTRL 0x106
-#define MT9M111_OUTPUT_FORMAT_CTRL 0x108
-#define MT9M111_REDUCER_XZOOM_B 0x1a0
-#define MT9M111_REDUCER_XSIZE_B 0x1a1
-#define MT9M111_REDUCER_YZOOM_B 0x1a3
-#define MT9M111_REDUCER_YSIZE_B 0x1a4
-#define MT9M111_REDUCER_XZOOM_A 0x1a6
-#define MT9M111_REDUCER_XSIZE_A 0x1a7
-#define MT9M111_REDUCER_YZOOM_A 0x1a9
-#define MT9M111_REDUCER_YSIZE_A 0x1aa
-
-#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a
-#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b
-
-#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14)
-#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1)
-#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9)
-#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8)
-#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14)
-#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10)
-#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9)
-#define MT9M111_OUTFMT_RGB (1 << 8)
-#define MT9M111_OUTFMT_RGB565 (0 << 6)
-#define MT9M111_OUTFMT_RGB555 (1 << 6)
-#define MT9M111_OUTFMT_RGB444x (2 << 6)
-#define MT9M111_OUTFMT_RGBx444 (3 << 6)
-#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4)
-#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4)
-#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3)
-#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2)
-#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1)
-#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0)
-
-/*
- * Camera control register addresses (0x200..0x2ff not implemented)
- */
-
-#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg)
-#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val))
-#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val))
-#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val))
-#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \
- (val), (mask))
-
-#define MT9M111_MIN_DARK_ROWS 8
-#define MT9M111_MIN_DARK_COLS 26
-#define MT9M111_MAX_HEIGHT 1024
-#define MT9M111_MAX_WIDTH 1280
-
-struct mt9m111_context {
- u16 read_mode;
- u16 blanking_h;
- u16 blanking_v;
- u16 reducer_xzoom;
- u16 reducer_yzoom;
- u16 reducer_xsize;
- u16 reducer_ysize;
- u16 output_fmt_ctrl2;
- u16 control;
-};
-
-static struct mt9m111_context context_a = {
- .read_mode = MT9M111_READ_MODE_A,
- .blanking_h = MT9M111_HORIZONTAL_BLANKING_A,
- .blanking_v = MT9M111_VERTICAL_BLANKING_A,
- .reducer_xzoom = MT9M111_REDUCER_XZOOM_A,
- .reducer_yzoom = MT9M111_REDUCER_YZOOM_A,
- .reducer_xsize = MT9M111_REDUCER_XSIZE_A,
- .reducer_ysize = MT9M111_REDUCER_YSIZE_A,
- .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A,
- .control = MT9M111_CTXT_CTRL_RESTART,
-};
-
-static struct mt9m111_context context_b = {
- .read_mode = MT9M111_READ_MODE_B,
- .blanking_h = MT9M111_HORIZONTAL_BLANKING_B,
- .blanking_v = MT9M111_VERTICAL_BLANKING_B,
- .reducer_xzoom = MT9M111_REDUCER_XZOOM_B,
- .reducer_yzoom = MT9M111_REDUCER_YZOOM_B,
- .reducer_xsize = MT9M111_REDUCER_XSIZE_B,
- .reducer_ysize = MT9M111_REDUCER_YSIZE_B,
- .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B,
- .control = MT9M111_CTXT_CTRL_RESTART |
- MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B |
- MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B |
- MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B |
- MT9M111_CTXT_CTRL_HBLANK_SEL_B,
-};
-
-/* MT9M111 has only one fixed colorspace per pixelcode */
-struct mt9m111_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-static const struct mt9m111_datafmt mt9m111_colour_fmts[] = {
- {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
-};
-
-struct mt9m111 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct v4l2_ctrl *gain;
- int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code
- * from v4l2-chip-ident.h */
- struct mt9m111_context *ctx;
- struct v4l2_rect rect; /* cropping rectangle */
- int width; /* output */
- int height; /* sizes */
- struct mutex power_lock; /* lock to protect power_count */
- int power_count;
- const struct mt9m111_datafmt *fmt;
- int lastpage; /* PageMap cache value */
-};
-
-/* Find a data format by a pixel code */
-static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111,
- enum v4l2_mbus_pixelcode code)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++)
- if (mt9m111_colour_fmts[i].code == code)
- return mt9m111_colour_fmts + i;
-
- return mt9m111->fmt;
-}
-
-static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct mt9m111, subdev);
-}
-
-static int reg_page_map_set(struct i2c_client *client, const u16 reg)
-{
- int ret;
- u16 page;
- struct mt9m111 *mt9m111 = to_mt9m111(client);
-
- page = (reg >> 8);
- if (page == mt9m111->lastpage)
- return 0;
- if (page > 2)
- return -EINVAL;
-
- ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page);
- if (!ret)
- mt9m111->lastpage = page;
- return ret;
-}
-
-static int mt9m111_reg_read(struct i2c_client *client, const u16 reg)
-{
- int ret;
-
- ret = reg_page_map_set(client, reg);
- if (!ret)
- ret = i2c_smbus_read_word_swapped(client, reg & 0xff);
-
- dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret);
- return ret;
-}
-
-static int mt9m111_reg_write(struct i2c_client *client, const u16 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_page_map_set(client, reg);
- if (!ret)
- ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data);
- dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
- return ret;
-}
-
-static int mt9m111_reg_set(struct i2c_client *client, const u16 reg,
- const u16 data)
-{
- int ret;
-
- ret = mt9m111_reg_read(client, reg);
- if (ret >= 0)
- ret = mt9m111_reg_write(client, reg, ret | data);
- return ret;
-}
-
-static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
- const u16 data)
-{
- int ret;
-
- ret = mt9m111_reg_read(client, reg);
- if (ret >= 0)
- ret = mt9m111_reg_write(client, reg, ret & ~data);
- return ret;
-}
-
-static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg,
- const u16 data, const u16 mask)
-{
- int ret;
-
- ret = mt9m111_reg_read(client, reg);
- if (ret >= 0)
- ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data);
- return ret;
-}
-
-static int mt9m111_set_context(struct mt9m111 *mt9m111,
- struct mt9m111_context *ctx)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- return reg_write(CONTEXT_CONTROL, ctx->control);
-}
-
-static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111,
- struct mt9m111_context *ctx, struct v4l2_rect *rect,
- unsigned int width, unsigned int height)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width);
- if (!ret)
- ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height);
- if (!ret)
- ret = mt9m111_reg_write(client, ctx->reducer_xsize, width);
- if (!ret)
- ret = mt9m111_reg_write(client, ctx->reducer_ysize, height);
- return ret;
-}
-
-static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect,
- int width, int height, enum v4l2_mbus_pixelcode code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
-
- ret = reg_write(COLUMN_START, rect->left);
- if (!ret)
- ret = reg_write(ROW_START, rect->top);
-
- if (!ret)
- ret = reg_write(WINDOW_WIDTH, rect->width);
- if (!ret)
- ret = reg_write(WINDOW_HEIGHT, rect->height);
-
- if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
- /* IFP in use, down-scaling possible */
- if (!ret)
- ret = mt9m111_setup_rect_ctx(mt9m111, &context_b,
- rect, width, height);
- if (!ret)
- ret = mt9m111_setup_rect_ctx(mt9m111, &context_a,
- rect, width, height);
- }
-
- dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n",
- __func__, code, rect->width, rect->height, rect->left, rect->top,
- width, height, ret);
-
- return ret;
-}
-
-static int mt9m111_enable(struct mt9m111 *mt9m111)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE);
-}
-
-static int mt9m111_reset(struct mt9m111 *mt9m111)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
-
- ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
- if (!ret)
- ret = reg_set(RESET, MT9M111_RESET_RESET_SOC);
- if (!ret)
- ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
- | MT9M111_RESET_RESET_SOC);
-
- return ret;
-}
-
-static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct v4l2_rect rect = a->c;
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- int width, height;
- int ret;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
- /* Bayer format - even size lengths */
- rect.width = ALIGN(rect.width, 2);
- rect.height = ALIGN(rect.height, 2);
- /* Let the user play with the starting pixel */
- }
-
- /* FIXME: the datasheet doesn't specify minimum sizes */
- soc_camera_limit_side(&rect.left, &rect.width,
- MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH);
-
- soc_camera_limit_side(&rect.top, &rect.height,
- MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
-
- width = min(mt9m111->width, rect.width);
- height = min(mt9m111->height, rect.height);
-
- ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code);
- if (!ret) {
- mt9m111->rect = rect;
- mt9m111->width = width;
- mt9m111->height = height;
- }
-
- return ret;
-}
-
-static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-
- a->c = mt9m111->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- a->bounds.left = MT9M111_MIN_DARK_COLS;
- a->bounds.top = MT9M111_MIN_DARK_ROWS;
- a->bounds.width = MT9M111_MAX_WIDTH;
- a->bounds.height = MT9M111_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int mt9m111_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-
- mf->width = mt9m111->width;
- mf->height = mt9m111->height;
- mf->code = mt9m111->fmt->code;
- mf->colorspace = mt9m111->fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
- enum v4l2_mbus_pixelcode code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
- MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB |
- MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 |
- MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 |
- MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
- MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
- int ret;
-
- switch (code) {
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER |
- MT9M111_OUTFMT_RGB;
- break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
- data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB;
- break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 |
- MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
- break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
- break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
- MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
- break;
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
- break;
- case V4L2_MBUS_FMT_BGR565_2X8_BE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
- MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
- break;
- case V4L2_MBUS_FMT_BGR565_2X8_LE:
- data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 |
- MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
- MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
- break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- data_outfmt2 = 0;
- break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
- data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
- break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN;
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN |
- MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B;
- break;
- default:
- dev_err(&client->dev, "Pixel format not handled: %x\n", code);
- return -EINVAL;
- }
-
- ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2,
- data_outfmt2, mask_outfmt2);
- if (!ret)
- ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2,
- data_outfmt2, mask_outfmt2);
-
- return ret;
-}
-
-static int mt9m111_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- const struct mt9m111_datafmt *fmt;
- struct v4l2_rect *rect = &mt9m111->rect;
- bool bayer;
-
- fmt = mt9m111_find_datafmt(mt9m111, mf->code);
-
- bayer = fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE;
-
- /*
- * With Bayer format enforce even side lengths, but let the user play
- * with the starting pixel
- */
- if (bayer) {
- rect->width = ALIGN(rect->width, 2);
- rect->height = ALIGN(rect->height, 2);
- }
-
- if (fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) {
- /* IFP bypass mode, no scaling */
- mf->width = rect->width;
- mf->height = rect->height;
- } else {
- /* No upscaling */
- if (mf->width > rect->width)
- mf->width = rect->width;
- if (mf->height > rect->height)
- mf->height = rect->height;
- }
-
- dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__,
- mf->width, mf->height, fmt->code);
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
-
- return 0;
-}
-
-static int mt9m111_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct mt9m111_datafmt *fmt;
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- struct v4l2_rect *rect = &mt9m111->rect;
- int ret;
-
- mt9m111_try_fmt(sd, mf);
- fmt = mt9m111_find_datafmt(mt9m111, mf->code);
- /* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */
-
- ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code);
- if (!ret)
- ret = mt9m111_set_pixfmt(mt9m111, mf->code);
- if (!ret) {
- mt9m111->width = mf->width;
- mt9m111->height = mf->height;
- mt9m111->fmt = fmt;
- }
-
- return ret;
-}
-
-static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9m111->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m111_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int val;
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
- return -EINVAL;
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- val = mt9m111_reg_read(client, reg->reg);
- reg->size = 2;
- reg->val = (u64)val;
-
- if (reg->val > 0xffff)
- return -EIO;
-
- return 0;
-}
-
-static int mt9m111_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- if (mt9m111_reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
-
- if (flip)
- ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask);
- else
- ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask);
-
- return ret;
-}
-
-static int mt9m111_get_global_gain(struct mt9m111 *mt9m111)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int data;
-
- data = reg_read(GLOBAL_GAIN);
- if (data >= 0)
- return (data & 0x2f) * (1 << ((data >> 10) & 1)) *
- (1 << ((data >> 9) & 1));
- return data;
-}
-
-static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- u16 val;
-
- if (gain > 63 * 2 * 2)
- return -EINVAL;
-
- if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
- val = (1 << 10) | (1 << 9) | (gain / 4);
- else if ((gain >= 64) && (gain < 64 * 2))
- val = (1 << 9) | (gain / 2);
- else
- val = gain;
-
- return reg_write(GLOBAL_GAIN, val);
-}
-
-static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-
- if (on)
- return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
- return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN);
-}
-
-static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
-
- if (on)
- return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
- return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN);
-}
-
-static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9m111 *mt9m111 = container_of(ctrl->handler,
- struct mt9m111, hdl);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- return mt9m111_set_flip(mt9m111, ctrl->val,
- MT9M111_RMB_MIRROR_ROWS);
- case V4L2_CID_HFLIP:
- return mt9m111_set_flip(mt9m111, ctrl->val,
- MT9M111_RMB_MIRROR_COLS);
- case V4L2_CID_GAIN:
- return mt9m111_set_global_gain(mt9m111, ctrl->val);
- case V4L2_CID_EXPOSURE_AUTO:
- return mt9m111_set_autoexposure(mt9m111, ctrl->val);
- case V4L2_CID_AUTO_WHITE_BALANCE:
- return mt9m111_set_autowhitebalance(mt9m111, ctrl->val);
- }
-
- return -EINVAL;
-}
-
-static int mt9m111_suspend(struct mt9m111 *mt9m111)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
-
- v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111));
-
- ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
- if (!ret)
- ret = reg_set(RESET, MT9M111_RESET_RESET_SOC |
- MT9M111_RESET_OUTPUT_DISABLE |
- MT9M111_RESET_ANALOG_STANDBY);
- if (!ret)
- ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
-
- return ret;
-}
-
-static void mt9m111_restore_state(struct mt9m111 *mt9m111)
-{
- mt9m111_set_context(mt9m111, mt9m111->ctx);
- mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code);
- mt9m111_setup_geometry(mt9m111, &mt9m111->rect,
- mt9m111->width, mt9m111->height, mt9m111->fmt->code);
- v4l2_ctrl_handler_setup(&mt9m111->hdl);
-}
-
-static int mt9m111_resume(struct mt9m111 *mt9m111)
-{
- int ret = mt9m111_enable(mt9m111);
- if (!ret)
- ret = mt9m111_reset(mt9m111);
- if (!ret)
- mt9m111_restore_state(mt9m111);
-
- return ret;
-}
-
-static int mt9m111_init(struct mt9m111 *mt9m111)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev);
- int ret;
-
- /* Default HIGHPOWER context */
- mt9m111->ctx = &context_b;
- ret = mt9m111_enable(mt9m111);
- if (!ret)
- ret = mt9m111_reset(mt9m111);
- if (!ret)
- ret = mt9m111_set_context(mt9m111, mt9m111->ctx);
- if (ret)
- dev_err(&client->dev, "mt9m111 init failed: %d\n", ret);
- return ret;
-}
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int mt9m111_video_probe(struct i2c_client *client)
-{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
- s32 data;
- int ret;
-
- data = reg_read(CHIP_VERSION);
-
- switch (data) {
- case 0x143a: /* MT9M111 or MT9M131 */
- mt9m111->model = V4L2_IDENT_MT9M111;
- dev_info(&client->dev,
- "Detected a MT9M111/MT9M131 chip ID %x\n", data);
- break;
- case 0x148c: /* MT9M112 */
- mt9m111->model = V4L2_IDENT_MT9M112;
- dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data);
- break;
- default:
- dev_err(&client->dev,
- "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n",
- data);
- return -ENODEV;
- }
-
- ret = mt9m111_init(mt9m111);
- if (ret)
- return ret;
- return v4l2_ctrl_handler_setup(&mt9m111->hdl);
-}
-
-static int mt9m111_s_power(struct v4l2_subdev *sd, int on)
-{
- struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
-
- mutex_lock(&mt9m111->power_lock);
-
- /*
- * If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (mt9m111->power_count == !on) {
- if (on) {
- ret = mt9m111_resume(mt9m111);
- if (ret) {
- dev_err(&client->dev,
- "Failed to resume the sensor: %d\n", ret);
- goto out;
- }
- } else {
- mt9m111_suspend(mt9m111);
- }
- }
-
- /* Update the power count. */
- mt9m111->power_count += on ? 1 : -1;
- WARN_ON(mt9m111->power_count < 0);
-
-out:
- mutex_unlock(&mt9m111->power_lock);
- return ret;
-}
-
-static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = {
- .s_ctrl = mt9m111_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
- .g_chip_ident = mt9m111_g_chip_ident,
- .s_power = mt9m111_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9m111_g_register,
- .s_register = mt9m111_s_register,
-#endif
-};
-
-static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(mt9m111_colour_fmts))
- return -EINVAL;
-
- *code = mt9m111_colour_fmts[index].code;
- return 0;
-}
-
-static int mt9m111_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
- .s_mbus_fmt = mt9m111_s_fmt,
- .g_mbus_fmt = mt9m111_g_fmt,
- .try_mbus_fmt = mt9m111_try_fmt,
- .s_crop = mt9m111_s_crop,
- .g_crop = mt9m111_g_crop,
- .cropcap = mt9m111_cropcap,
- .enum_mbus_fmt = mt9m111_enum_fmt,
- .g_mbus_config = mt9m111_g_mbus_config,
-};
-
-static struct v4l2_subdev_ops mt9m111_subdev_ops = {
- .core = &mt9m111_subdev_core_ops,
- .video = &mt9m111_subdev_video_ops,
-};
-
-static int mt9m111_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9m111 *mt9m111;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "mt9m111: driver needs platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL);
- if (!mt9m111)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
- v4l2_ctrl_handler_init(&mt9m111->hdl, 5);
- v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
- V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
- mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops,
- V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32);
- v4l2_ctrl_new_std_menu(&mt9m111->hdl,
- &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
- V4L2_EXPOSURE_AUTO);
- mt9m111->subdev.ctrl_handler = &mt9m111->hdl;
- if (mt9m111->hdl.error) {
- int err = mt9m111->hdl.error;
-
- kfree(mt9m111);
- return err;
- }
-
- /* Second stage probe - when a capture adapter is there */
- mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
- mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
- mt9m111->rect.width = MT9M111_MAX_WIDTH;
- mt9m111->rect.height = MT9M111_MAX_HEIGHT;
- mt9m111->fmt = &mt9m111_colour_fmts[0];
- mt9m111->lastpage = -1;
- mutex_init(&mt9m111->power_lock);
-
- ret = mt9m111_video_probe(client);
- if (ret) {
- v4l2_ctrl_handler_free(&mt9m111->hdl);
- kfree(mt9m111);
- }
-
- return ret;
-}
-
-static int mt9m111_remove(struct i2c_client *client)
-{
- struct mt9m111 *mt9m111 = to_mt9m111(client);
-
- v4l2_device_unregister_subdev(&mt9m111->subdev);
- v4l2_ctrl_handler_free(&mt9m111->hdl);
- kfree(mt9m111);
-
- return 0;
-}
-
-static const struct i2c_device_id mt9m111_id[] = {
- { "mt9m111", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9m111_id);
-
-static struct i2c_driver mt9m111_i2c_driver = {
- .driver = {
- .name = "mt9m111",
- },
- .probe = mt9m111_probe,
- .remove = mt9m111_remove,
- .id_table = mt9m111_id,
-};
-
-module_i2c_driver(mt9m111_i2c_driver);
-
-MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
-MODULE_AUTHOR("Robert Jarzmik");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
deleted file mode 100644
index 3be537ef22d2..000000000000
--- a/drivers/media/video/mt9p031.c
+++ /dev/null
@@ -1,1071 +0,0 @@
-/*
- * Driver for MT9P031 CMOS Image Sensor from Aptina
- *
- * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
- * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * Based on the MT9V032 driver and Bastian Hecht's code.
- *
- * 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/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/log2.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-
-#include <media/mt9p031.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#include "aptina-pll.h"
-
-#define MT9P031_PIXEL_ARRAY_WIDTH 2752
-#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
-
-#define MT9P031_CHIP_VERSION 0x00
-#define MT9P031_CHIP_VERSION_VALUE 0x1801
-#define MT9P031_ROW_START 0x01
-#define MT9P031_ROW_START_MIN 0
-#define MT9P031_ROW_START_MAX 2004
-#define MT9P031_ROW_START_DEF 54
-#define MT9P031_COLUMN_START 0x02
-#define MT9P031_COLUMN_START_MIN 0
-#define MT9P031_COLUMN_START_MAX 2750
-#define MT9P031_COLUMN_START_DEF 16
-#define MT9P031_WINDOW_HEIGHT 0x03
-#define MT9P031_WINDOW_HEIGHT_MIN 2
-#define MT9P031_WINDOW_HEIGHT_MAX 2006
-#define MT9P031_WINDOW_HEIGHT_DEF 1944
-#define MT9P031_WINDOW_WIDTH 0x04
-#define MT9P031_WINDOW_WIDTH_MIN 2
-#define MT9P031_WINDOW_WIDTH_MAX 2752
-#define MT9P031_WINDOW_WIDTH_DEF 2592
-#define MT9P031_HORIZONTAL_BLANK 0x05
-#define MT9P031_HORIZONTAL_BLANK_MIN 0
-#define MT9P031_HORIZONTAL_BLANK_MAX 4095
-#define MT9P031_VERTICAL_BLANK 0x06
-#define MT9P031_VERTICAL_BLANK_MIN 0
-#define MT9P031_VERTICAL_BLANK_MAX 4095
-#define MT9P031_VERTICAL_BLANK_DEF 25
-#define MT9P031_OUTPUT_CONTROL 0x07
-#define MT9P031_OUTPUT_CONTROL_CEN 2
-#define MT9P031_OUTPUT_CONTROL_SYN 1
-#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82
-#define MT9P031_SHUTTER_WIDTH_UPPER 0x08
-#define MT9P031_SHUTTER_WIDTH_LOWER 0x09
-#define MT9P031_SHUTTER_WIDTH_MIN 1
-#define MT9P031_SHUTTER_WIDTH_MAX 1048575
-#define MT9P031_SHUTTER_WIDTH_DEF 1943
-#define MT9P031_PLL_CONTROL 0x10
-#define MT9P031_PLL_CONTROL_PWROFF 0x0050
-#define MT9P031_PLL_CONTROL_PWRON 0x0051
-#define MT9P031_PLL_CONTROL_USEPLL 0x0052
-#define MT9P031_PLL_CONFIG_1 0x11
-#define MT9P031_PLL_CONFIG_2 0x12
-#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a
-#define MT9P031_FRAME_RESTART 0x0b
-#define MT9P031_SHUTTER_DELAY 0x0c
-#define MT9P031_RST 0x0d
-#define MT9P031_RST_ENABLE 1
-#define MT9P031_RST_DISABLE 0
-#define MT9P031_READ_MODE_1 0x1e
-#define MT9P031_READ_MODE_2 0x20
-#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15)
-#define MT9P031_READ_MODE_2_COL_MIR (1 << 14)
-#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6)
-#define MT9P031_ROW_ADDRESS_MODE 0x22
-#define MT9P031_COLUMN_ADDRESS_MODE 0x23
-#define MT9P031_GLOBAL_GAIN 0x35
-#define MT9P031_GLOBAL_GAIN_MIN 8
-#define MT9P031_GLOBAL_GAIN_MAX 1024
-#define MT9P031_GLOBAL_GAIN_DEF 8
-#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
-#define MT9P031_ROW_BLACK_TARGET 0x49
-#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
-#define MT9P031_GREEN1_OFFSET 0x60
-#define MT9P031_GREEN2_OFFSET 0x61
-#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62
-#define MT9P031_BLC_MANUAL_BLC (1 << 0)
-#define MT9P031_RED_OFFSET 0x63
-#define MT9P031_BLUE_OFFSET 0x64
-#define MT9P031_TEST_PATTERN 0xa0
-#define MT9P031_TEST_PATTERN_SHIFT 3
-#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
-#define MT9P031_TEST_PATTERN_DISABLE (0 << 0)
-#define MT9P031_TEST_PATTERN_GREEN 0xa1
-#define MT9P031_TEST_PATTERN_RED 0xa2
-#define MT9P031_TEST_PATTERN_BLUE 0xa3
-
-enum mt9p031_model {
- MT9P031_MODEL_COLOR,
- MT9P031_MODEL_MONOCHROME,
-};
-
-struct mt9p031 {
- struct v4l2_subdev subdev;
- struct media_pad pad;
- struct v4l2_rect crop; /* Sensor window */
- struct v4l2_mbus_framefmt format;
- struct mt9p031_platform_data *pdata;
- struct mutex power_lock; /* lock to protect power_count */
- int power_count;
-
- enum mt9p031_model model;
- struct aptina_pll pll;
- int reset;
-
- struct v4l2_ctrl_handler ctrls;
- struct v4l2_ctrl *blc_auto;
- struct v4l2_ctrl *blc_offset;
-
- /* Registers cache */
- u16 output_control;
- u16 mode2;
-};
-
-static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct mt9p031, subdev);
-}
-
-static int mt9p031_read(struct i2c_client *client, u8 reg)
-{
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data)
-{
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
- u16 set)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- u16 value = (mt9p031->output_control & ~clear) | set;
- int ret;
-
- ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value);
- if (ret < 0)
- return ret;
-
- mt9p031->output_control = value;
- return 0;
-}
-
-static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- u16 value = (mt9p031->mode2 & ~clear) | set;
- int ret;
-
- ret = mt9p031_write(client, MT9P031_READ_MODE_2, value);
- if (ret < 0)
- return ret;
-
- mt9p031->mode2 = value;
- return 0;
-}
-
-static int mt9p031_reset(struct mt9p031 *mt9p031)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- int ret;
-
- /* Disable chip output, synchronous option update */
- ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
- if (ret < 0)
- return ret;
-
- return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN,
- 0);
-}
-
-static int mt9p031_pll_setup(struct mt9p031 *mt9p031)
-{
- static const struct aptina_pll_limits limits = {
- .ext_clock_min = 6000000,
- .ext_clock_max = 27000000,
- .int_clock_min = 2000000,
- .int_clock_max = 13500000,
- .out_clock_min = 180000000,
- .out_clock_max = 360000000,
- .pix_clock_max = 96000000,
- .n_min = 1,
- .n_max = 64,
- .m_min = 16,
- .m_max = 255,
- .p1_min = 1,
- .p1_max = 128,
- };
-
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- struct mt9p031_platform_data *pdata = mt9p031->pdata;
-
- mt9p031->pll.ext_clock = pdata->ext_freq;
- mt9p031->pll.pix_clock = pdata->target_freq;
-
- return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
-}
-
-static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- int ret;
-
- ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
- MT9P031_PLL_CONTROL_PWRON);
- if (ret < 0)
- return ret;
-
- ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
- (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
- if (ret < 0)
- return ret;
-
- ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
- if (ret < 0)
- return ret;
-
- usleep_range(1000, 2000);
- ret = mt9p031_write(client, MT9P031_PLL_CONTROL,
- MT9P031_PLL_CONTROL_PWRON |
- MT9P031_PLL_CONTROL_USEPLL);
- return ret;
-}
-
-static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
-
- return mt9p031_write(client, MT9P031_PLL_CONTROL,
- MT9P031_PLL_CONTROL_PWROFF);
-}
-
-static int mt9p031_power_on(struct mt9p031 *mt9p031)
-{
- /* Ensure RESET_BAR is low */
- if (mt9p031->reset != -1) {
- gpio_set_value(mt9p031->reset, 0);
- usleep_range(1000, 2000);
- }
-
- /* Emable clock */
- if (mt9p031->pdata->set_xclk)
- mt9p031->pdata->set_xclk(&mt9p031->subdev,
- mt9p031->pdata->ext_freq);
-
- /* Now RESET_BAR must be high */
- if (mt9p031->reset != -1) {
- gpio_set_value(mt9p031->reset, 1);
- usleep_range(1000, 2000);
- }
-
- return 0;
-}
-
-static void mt9p031_power_off(struct mt9p031 *mt9p031)
-{
- if (mt9p031->reset != -1) {
- gpio_set_value(mt9p031->reset, 0);
- usleep_range(1000, 2000);
- }
-
- if (mt9p031->pdata->set_xclk)
- mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
-}
-
-static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- int ret;
-
- if (!on) {
- mt9p031_power_off(mt9p031);
- return 0;
- }
-
- ret = mt9p031_power_on(mt9p031);
- if (ret < 0)
- return ret;
-
- ret = mt9p031_reset(mt9p031);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to reset the camera\n");
- return ret;
- }
-
- return v4l2_ctrl_handler_setup(&mt9p031->ctrls);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev video operations
- */
-
-static int mt9p031_set_params(struct mt9p031 *mt9p031)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- struct v4l2_mbus_framefmt *format = &mt9p031->format;
- const struct v4l2_rect *crop = &mt9p031->crop;
- unsigned int hblank;
- unsigned int vblank;
- unsigned int xskip;
- unsigned int yskip;
- unsigned int xbin;
- unsigned int ybin;
- int ret;
-
- /* Windows position and size.
- *
- * TODO: Make sure the start coordinates and window size match the
- * skipping, binning and mirroring (see description of registers 2 and 4
- * in table 13, and Binning section on page 41).
- */
- ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_ROW_START, crop->top);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1);
- if (ret < 0)
- return ret;
-
- /* Row and column binning and skipping. Use the maximum binning value
- * compatible with the skipping settings.
- */
- xskip = DIV_ROUND_CLOSEST(crop->width, format->width);
- yskip = DIV_ROUND_CLOSEST(crop->height, format->height);
- xbin = 1 << (ffs(xskip) - 1);
- ybin = 1 << (ffs(yskip) - 1);
-
- ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE,
- ((xbin - 1) << 4) | (xskip - 1));
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE,
- ((ybin - 1) << 4) | (yskip - 1));
- if (ret < 0)
- return ret;
-
- /* Blanking - use minimum value for horizontal blanking and default
- * value for vertical blanking.
- */
- hblank = 346 * ybin + 64 + (80 >> max_t(unsigned int, xbin, 3));
- vblank = MT9P031_VERTICAL_BLANK_DEF;
-
- ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank);
- if (ret < 0)
- return ret;
-
- return ret;
-}
-
-static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- int ret;
-
- if (!enable) {
- /* Stop sensor readout */
- ret = mt9p031_set_output_control(mt9p031,
- MT9P031_OUTPUT_CONTROL_CEN, 0);
- if (ret < 0)
- return ret;
-
- return mt9p031_pll_disable(mt9p031);
- }
-
- ret = mt9p031_set_params(mt9p031);
- if (ret < 0)
- return ret;
-
- /* Switch to master "normal" mode */
- ret = mt9p031_set_output_control(mt9p031, 0,
- MT9P031_OUTPUT_CONTROL_CEN);
- if (ret < 0)
- return ret;
-
- return mt9p031_pll_enable(mt9p031);
-}
-
-static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
-
- if (code->pad || code->index)
- return -EINVAL;
-
- code->code = mt9p031->format.code;
- return 0;
-}
-
-static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
-
- if (fse->index >= 8 || fse->code != mt9p031->format.code)
- return -EINVAL;
-
- fse->min_width = MT9P031_WINDOW_WIDTH_DEF
- / min_t(unsigned int, 7, fse->index + 1);
- fse->max_width = fse->min_width;
- fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1);
- fse->max_height = fse->min_height;
-
- return 0;
-}
-
-static struct v4l2_mbus_framefmt *
-__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
- unsigned int pad, u32 which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_format(fh, pad);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &mt9p031->format;
- default:
- return NULL;
- }
-}
-
-static struct v4l2_rect *
-__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh,
- unsigned int pad, u32 which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_crop(fh, pad);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &mt9p031->crop;
- default:
- return NULL;
- }
-}
-
-static int mt9p031_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
-
- fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad,
- fmt->which);
- return 0;
-}
-
-static int mt9p031_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *format)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- struct v4l2_mbus_framefmt *__format;
- struct v4l2_rect *__crop;
- unsigned int width;
- unsigned int height;
- unsigned int hratio;
- unsigned int vratio;
-
- __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad,
- format->which);
-
- /* Clamp the width and height to avoid dividing by zero. */
- width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
- max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN),
- __crop->width);
- height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
- max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN),
- __crop->height);
-
- hratio = DIV_ROUND_CLOSEST(__crop->width, width);
- vratio = DIV_ROUND_CLOSEST(__crop->height, height);
-
- __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad,
- format->which);
- __format->width = __crop->width / hratio;
- __format->height = __crop->height / vratio;
-
- format->format = *__format;
-
- return 0;
-}
-
-static int mt9p031_get_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
-
- crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
- crop->which);
- return 0;
-}
-
-static int mt9p031_set_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- struct v4l2_mbus_framefmt *__format;
- struct v4l2_rect *__crop;
- struct v4l2_rect rect;
-
- /* Clamp the crop rectangle boundaries and align them to a multiple of 2
- * pixels to ensure a GRBG Bayer pattern.
- */
- rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN,
- MT9P031_COLUMN_START_MAX);
- rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN,
- MT9P031_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2),
- MT9P031_WINDOW_WIDTH_MIN,
- MT9P031_WINDOW_WIDTH_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2),
- MT9P031_WINDOW_HEIGHT_MIN,
- MT9P031_WINDOW_HEIGHT_MAX);
-
- rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top);
-
- __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
-
- if (rect.width != __crop->width || rect.height != __crop->height) {
- /* Reset the output image size if the crop rectangle size has
- * been modified.
- */
- __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad,
- crop->which);
- __format->width = rect.width;
- __format->height = rect.height;
- }
-
- *__crop = rect;
- crop->rect = rect;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev control operations
- */
-
-#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
-#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002)
-#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003)
-#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004)
-#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005)
-
-static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9p031 *mt9p031 =
- container_of(ctrl->handler, struct mt9p031, ctrls);
- struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- u16 data;
- int ret;
-
- switch (ctrl->id) {
- case V4L2_CID_EXPOSURE:
- ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER,
- (ctrl->val >> 16) & 0xffff);
- if (ret < 0)
- return ret;
-
- return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER,
- ctrl->val & 0xffff);
-
- case V4L2_CID_GAIN:
- /* Gain is controlled by 2 analog stages and a digital stage.
- * Valid values for the 3 stages are
- *
- * Stage Min Max Step
- * ------------------------------------------
- * First analog stage x1 x2 1
- * Second analog stage x1 x4 0.125
- * Digital stage x1 x16 0.125
- *
- * To minimize noise, the gain stages should be used in the
- * second analog stage, first analog stage, digital stage order.
- * Gain from a previous stage should be pushed to its maximum
- * value before the next stage is used.
- */
- if (ctrl->val <= 32) {
- data = ctrl->val;
- } else if (ctrl->val <= 64) {
- ctrl->val &= ~1;
- data = (1 << 6) | (ctrl->val >> 1);
- } else {
- ctrl->val &= ~7;
- data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
- }
-
- return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
-
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- return mt9p031_set_mode2(mt9p031,
- 0, MT9P031_READ_MODE_2_COL_MIR);
- else
- return mt9p031_set_mode2(mt9p031,
- MT9P031_READ_MODE_2_COL_MIR, 0);
-
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- return mt9p031_set_mode2(mt9p031,
- 0, MT9P031_READ_MODE_2_ROW_MIR);
- else
- return mt9p031_set_mode2(mt9p031,
- MT9P031_READ_MODE_2_ROW_MIR, 0);
-
- case V4L2_CID_TEST_PATTERN:
- if (!ctrl->val) {
- /* Restore the black level compensation settings. */
- if (mt9p031->blc_auto->cur.val != 0) {
- ret = mt9p031_s_ctrl(mt9p031->blc_auto);
- if (ret < 0)
- return ret;
- }
- if (mt9p031->blc_offset->cur.val != 0) {
- ret = mt9p031_s_ctrl(mt9p031->blc_offset);
- if (ret < 0)
- return ret;
- }
- return mt9p031_write(client, MT9P031_TEST_PATTERN,
- MT9P031_TEST_PATTERN_DISABLE);
- }
-
- ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0);
- if (ret < 0)
- return ret;
-
- /* Disable digital black level compensation when using a test
- * pattern.
- */
- ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
- 0);
- if (ret < 0)
- return ret;
-
- ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
- if (ret < 0)
- return ret;
-
- return mt9p031_write(client, MT9P031_TEST_PATTERN,
- ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
- | MT9P031_TEST_PATTERN_ENABLE);
-
- case V4L2_CID_BLC_AUTO:
- ret = mt9p031_set_mode2(mt9p031,
- ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
- ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
- if (ret < 0)
- return ret;
-
- return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
- ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
-
- case V4L2_CID_BLC_TARGET_LEVEL:
- return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
- ctrl->val);
-
- case V4L2_CID_BLC_ANALOG_OFFSET:
- data = ctrl->val & ((1 << 9) - 1);
-
- ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
- if (ret < 0)
- return ret;
- ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
- if (ret < 0)
- return ret;
- return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
-
- case V4L2_CID_BLC_DIGITAL_OFFSET:
- return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
- ctrl->val & ((1 << 12) - 1));
- }
-
- return 0;
-}
-
-static struct v4l2_ctrl_ops mt9p031_ctrl_ops = {
- .s_ctrl = mt9p031_s_ctrl,
-};
-
-static const char * const mt9p031_test_pattern_menu[] = {
- "Disabled",
- "Color Field",
- "Horizontal Gradient",
- "Vertical Gradient",
- "Diagonal Gradient",
- "Classic Test Pattern",
- "Walking 1s",
- "Monochrome Horizontal Bars",
- "Monochrome Vertical Bars",
- "Vertical Color Bars",
-};
-
-static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
- {
- .ops = &mt9p031_ctrl_ops,
- .id = V4L2_CID_TEST_PATTERN,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Test Pattern",
- .min = 0,
- .max = ARRAY_SIZE(mt9p031_test_pattern_menu) - 1,
- .step = 0,
- .def = 0,
- .flags = 0,
- .menu_skip_mask = 0,
- .qmenu = mt9p031_test_pattern_menu,
- }, {
- .ops = &mt9p031_ctrl_ops,
- .id = V4L2_CID_BLC_AUTO,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "BLC, Auto",
- .min = 0,
- .max = 1,
- .step = 1,
- .def = 1,
- .flags = 0,
- }, {
- .ops = &mt9p031_ctrl_ops,
- .id = V4L2_CID_BLC_TARGET_LEVEL,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "BLC Target Level",
- .min = 0,
- .max = 4095,
- .step = 1,
- .def = 168,
- .flags = 0,
- }, {
- .ops = &mt9p031_ctrl_ops,
- .id = V4L2_CID_BLC_ANALOG_OFFSET,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "BLC Analog Offset",
- .min = -255,
- .max = 255,
- .step = 1,
- .def = 32,
- .flags = 0,
- }, {
- .ops = &mt9p031_ctrl_ops,
- .id = V4L2_CID_BLC_DIGITAL_OFFSET,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "BLC Digital Offset",
- .min = -2048,
- .max = 2047,
- .step = 1,
- .def = 40,
- .flags = 0,
- }
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-static int mt9p031_set_power(struct v4l2_subdev *subdev, int on)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- int ret = 0;
-
- mutex_lock(&mt9p031->power_lock);
-
- /* If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (mt9p031->power_count == !on) {
- ret = __mt9p031_set_power(mt9p031, !!on);
- if (ret < 0)
- goto out;
- }
-
- /* Update the power count. */
- mt9p031->power_count += on ? 1 : -1;
- WARN_ON(mt9p031->power_count < 0);
-
-out:
- mutex_unlock(&mt9p031->power_lock);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev internal operations
- */
-
-static int mt9p031_registered(struct v4l2_subdev *subdev)
-{
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- s32 data;
- int ret;
-
- ret = mt9p031_power_on(mt9p031);
- if (ret < 0) {
- dev_err(&client->dev, "MT9P031 power up failed\n");
- return ret;
- }
-
- /* Read out the chip version register */
- data = mt9p031_read(client, MT9P031_CHIP_VERSION);
- if (data != MT9P031_CHIP_VERSION_VALUE) {
- dev_err(&client->dev, "MT9P031 not detected, wrong version "
- "0x%04x\n", data);
- return -ENODEV;
- }
-
- mt9p031_power_off(mt9p031);
-
- dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n",
- client->addr);
-
- return ret;
-}
-
-static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
-{
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- crop = v4l2_subdev_get_try_crop(fh, 0);
- crop->left = MT9P031_COLUMN_START_DEF;
- crop->top = MT9P031_ROW_START_DEF;
- crop->width = MT9P031_WINDOW_WIDTH_DEF;
- crop->height = MT9P031_WINDOW_HEIGHT_DEF;
-
- format = v4l2_subdev_get_try_format(fh, 0);
-
- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- format->code = V4L2_MBUS_FMT_Y12_1X12;
- else
- format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
-
- format->width = MT9P031_WINDOW_WIDTH_DEF;
- format->height = MT9P031_WINDOW_HEIGHT_DEF;
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- return mt9p031_set_power(subdev, 1);
-}
-
-static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
-{
- return mt9p031_set_power(subdev, 0);
-}
-
-static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
- .s_power = mt9p031_set_power,
-};
-
-static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
- .s_stream = mt9p031_s_stream,
-};
-
-static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
- .enum_mbus_code = mt9p031_enum_mbus_code,
- .enum_frame_size = mt9p031_enum_frame_size,
- .get_fmt = mt9p031_get_format,
- .set_fmt = mt9p031_set_format,
- .get_crop = mt9p031_get_crop,
- .set_crop = mt9p031_set_crop,
-};
-
-static struct v4l2_subdev_ops mt9p031_subdev_ops = {
- .core = &mt9p031_subdev_core_ops,
- .video = &mt9p031_subdev_video_ops,
- .pad = &mt9p031_subdev_pad_ops,
-};
-
-static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
- .registered = mt9p031_registered,
- .open = mt9p031_open,
- .close = mt9p031_close,
-};
-
-/* -----------------------------------------------------------------------------
- * Driver initialization and probing
- */
-
-static int mt9p031_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9p031_platform_data *pdata = client->dev.platform_data;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct mt9p031 *mt9p031;
- unsigned int i;
- int ret;
-
- if (pdata == NULL) {
- dev_err(&client->dev, "No platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&client->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL);
- if (mt9p031 == NULL)
- return -ENOMEM;
-
- mt9p031->pdata = pdata;
- mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
- mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
- mt9p031->model = did->driver_data;
- mt9p031->reset = -1;
-
- v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 5);
-
- v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
- V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN,
- MT9P031_SHUTTER_WIDTH_MAX, 1,
- MT9P031_SHUTTER_WIDTH_DEF);
- v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
- V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN,
- MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF);
- v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops,
- V4L2_CID_PIXEL_RATE, pdata->target_freq,
- pdata->target_freq, 1, pdata->target_freq);
-
- for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i)
- v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL);
-
- mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
-
- if (mt9p031->ctrls.error) {
- printk(KERN_INFO "%s: control initialization error %d\n",
- __func__, mt9p031->ctrls.error);
- ret = mt9p031->ctrls.error;
- goto done;
- }
-
- mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
- mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
- V4L2_CID_BLC_DIGITAL_OFFSET);
-
- mutex_init(&mt9p031->power_lock);
- v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
- mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
-
- mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
- if (ret < 0)
- goto done;
-
- mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF;
- mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF;
- mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
- mt9p031->crop.top = MT9P031_ROW_START_DEF;
-
- if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
- mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
- else
- mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
-
- mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
- mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
- mt9p031->format.field = V4L2_FIELD_NONE;
- mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
-
- if (pdata->reset != -1) {
- ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW,
- "mt9p031_rst");
- if (ret < 0)
- goto done;
-
- mt9p031->reset = pdata->reset;
- }
-
- ret = mt9p031_pll_setup(mt9p031);
-
-done:
- if (ret < 0) {
- if (mt9p031->reset != -1)
- gpio_free(mt9p031->reset);
-
- v4l2_ctrl_handler_free(&mt9p031->ctrls);
- media_entity_cleanup(&mt9p031->subdev.entity);
- kfree(mt9p031);
- }
-
- return ret;
-}
-
-static int mt9p031_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct mt9p031 *mt9p031 = to_mt9p031(subdev);
-
- v4l2_ctrl_handler_free(&mt9p031->ctrls);
- v4l2_device_unregister_subdev(subdev);
- media_entity_cleanup(&subdev->entity);
- if (mt9p031->reset != -1)
- gpio_free(mt9p031->reset);
- kfree(mt9p031);
-
- return 0;
-}
-
-static const struct i2c_device_id mt9p031_id[] = {
- { "mt9p031", MT9P031_MODEL_COLOR },
- { "mt9p031m", MT9P031_MODEL_MONOCHROME },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9p031_id);
-
-static struct i2c_driver mt9p031_i2c_driver = {
- .driver = {
- .name = "mt9p031",
- },
- .probe = mt9p031_probe,
- .remove = mt9p031_remove,
- .id_table = mt9p031_id,
-};
-
-module_i2c_driver(mt9p031_i2c_driver);
-
-MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
-MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
deleted file mode 100644
index 1415074138a5..000000000000
--- a/drivers/media/video/mt9t031.c
+++ /dev/null
@@ -1,857 +0,0 @@
-/*
- * Driver for MT9T031 CMOS Image Sensor from Micron
- *
- * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering <lg@denx.de>
- *
- * 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/device.h>
-#include <linux/i2c.h>
-#include <linux/log2.h>
-#include <linux/pm.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-ctrls.h>
-
-/*
- * ATTENTION: this driver still cannot be used outside of the soc-camera
- * framework because of its PM implementation, using the video_device node.
- * If hardware becomes available for testing, alternative PM approaches shall
- * be considered and tested.
- */
-
-/*
- * mt9t031 i2c address 0x5d
- * The platform has to define i2c_board_info and link to it from
- * struct soc_camera_link
- */
-
-/* mt9t031 selected register addresses */
-#define MT9T031_CHIP_VERSION 0x00
-#define MT9T031_ROW_START 0x01
-#define MT9T031_COLUMN_START 0x02
-#define MT9T031_WINDOW_HEIGHT 0x03
-#define MT9T031_WINDOW_WIDTH 0x04
-#define MT9T031_HORIZONTAL_BLANKING 0x05
-#define MT9T031_VERTICAL_BLANKING 0x06
-#define MT9T031_OUTPUT_CONTROL 0x07
-#define MT9T031_SHUTTER_WIDTH_UPPER 0x08
-#define MT9T031_SHUTTER_WIDTH 0x09
-#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a
-#define MT9T031_FRAME_RESTART 0x0b
-#define MT9T031_SHUTTER_DELAY 0x0c
-#define MT9T031_RESET 0x0d
-#define MT9T031_READ_MODE_1 0x1e
-#define MT9T031_READ_MODE_2 0x20
-#define MT9T031_READ_MODE_3 0x21
-#define MT9T031_ROW_ADDRESS_MODE 0x22
-#define MT9T031_COLUMN_ADDRESS_MODE 0x23
-#define MT9T031_GLOBAL_GAIN 0x35
-#define MT9T031_CHIP_ENABLE 0xF8
-
-#define MT9T031_MAX_HEIGHT 1536
-#define MT9T031_MAX_WIDTH 2048
-#define MT9T031_MIN_HEIGHT 2
-#define MT9T031_MIN_WIDTH 18
-#define MT9T031_HORIZONTAL_BLANK 142
-#define MT9T031_VERTICAL_BLANK 25
-#define MT9T031_COLUMN_SKIP 32
-#define MT9T031_ROW_SKIP 20
-
-struct mt9t031 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct {
- /* exposure/auto-exposure cluster */
- struct v4l2_ctrl *autoexposure;
- struct v4l2_ctrl *exposure;
- };
- struct v4l2_rect rect; /* Sensor window */
- int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
- u16 xskip;
- u16 yskip;
- unsigned int total_h;
- unsigned short y_skip_top; /* Lines to skip at the top */
-};
-
-static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
-}
-
-static int reg_read(struct i2c_client *client, const u8 reg)
-{
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int reg_write(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static int reg_set(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret | data);
-}
-
-static int reg_clear(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret & ~data);
-}
-
-static int set_shutter(struct i2c_client *client, const u32 data)
-{
- int ret;
-
- ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16);
-
- if (ret >= 0)
- ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff);
-
- return ret;
-}
-
-static int get_shutter(struct i2c_client *client, u32 *data)
-{
- int ret;
-
- ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER);
- *data = ret << 16;
-
- if (ret >= 0)
- ret = reg_read(client, MT9T031_SHUTTER_WIDTH);
- *data |= ret & 0xffff;
-
- return ret < 0 ? ret : 0;
-}
-
-static int mt9t031_idle(struct i2c_client *client)
-{
- int ret;
-
- /* Disable chip output, synchronous option update */
- ret = reg_write(client, MT9T031_RESET, 1);
- if (ret >= 0)
- ret = reg_write(client, MT9T031_RESET, 0);
- if (ret >= 0)
- ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
-
- return ret >= 0 ? 0 : -EIO;
-}
-
-static int mt9t031_disable(struct i2c_client *client)
-{
- /* Disable the chip */
- reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
-
- return 0;
-}
-
-static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- if (enable)
- /* Switch to master "normal" mode */
- ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2);
- else
- /* Stop sensor readout */
- ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
-
- if (ret < 0)
- return -EIO;
-
- return 0;
-}
-
-/* target must be _even_ */
-static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
-{
- unsigned int skip;
-
- if (*source < target + target / 2) {
- *source = target;
- return 1;
- }
-
- skip = min(max, *source + target / 2) / target;
- if (skip > 8)
- skip = 8;
- *source = target * skip;
-
- return skip;
-}
-
-/* rect is the sensor rectangle, the caller guarantees parameter validity */
-static int mt9t031_set_params(struct i2c_client *client,
- struct v4l2_rect *rect, u16 xskip, u16 yskip)
-{
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- int ret;
- u16 xbin, ybin;
- const u16 hblank = MT9T031_HORIZONTAL_BLANK,
- vblank = MT9T031_VERTICAL_BLANK;
-
- xbin = min(xskip, (u16)3);
- ybin = min(yskip, (u16)3);
-
- /*
- * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper.
- * There is always a valid suitably aligned value. The worst case is
- * xbin = 3, width = 2048. Then we will start at 36, the last read out
- * pixel will be 2083, which is < 2085 - first black pixel.
- *
- * MT9T031 datasheet imposes window left border alignment, depending on
- * the selected xskip. Failing to conform to this requirement produces
- * dark horizontal stripes in the image. However, even obeying to this
- * requirement doesn't eliminate the stripes in all configurations. They
- * appear "locally reproducibly," but can differ between tests under
- * different lighting conditions.
- */
- switch (xbin) {
- case 1:
- rect->left &= ~1;
- break;
- case 2:
- rect->left &= ~3;
- break;
- case 3:
- rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ?
- (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6);
- }
-
- rect->top &= ~1;
-
- dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
- xskip, yskip, rect->width, rect->height, rect->left, rect->top);
-
- /* Disable register update, reconfigure atomically */
- ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
- if (ret < 0)
- return ret;
-
- /* Blanking and start values - default... */
- ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank);
- if (ret >= 0)
- ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank);
-
- if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
- /* Binning, skipping */
- if (ret >= 0)
- ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
- ((xbin - 1) << 4) | (xskip - 1));
- if (ret >= 0)
- ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
- ((ybin - 1) << 4) | (yskip - 1));
- }
- dev_dbg(&client->dev, "new physical left %u, top %u\n",
- rect->left, rect->top);
-
- /*
- * The caller provides a supported format, as guaranteed by
- * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap()
- */
- if (ret >= 0)
- ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
- if (ret >= 0)
- ret = reg_write(client, MT9T031_ROW_START, rect->top);
- if (ret >= 0)
- ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
- if (ret >= 0)
- ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
- rect->height + mt9t031->y_skip_top - 1);
- if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) {
- mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank;
-
- ret = set_shutter(client, mt9t031->total_h);
- }
-
- /* Re-enable register update, commit all changes */
- if (ret >= 0)
- ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
-
- if (ret >= 0) {
- mt9t031->rect = *rect;
- mt9t031->xskip = xskip;
- mt9t031->yskip = yskip;
- }
-
- return ret < 0 ? ret : 0;
-}
-
-static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct v4l2_rect rect = a->c;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- rect.width = ALIGN(rect.width, 2);
- rect.height = ALIGN(rect.height, 2);
-
- soc_camera_limit_side(&rect.left, &rect.width,
- MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH);
-
- soc_camera_limit_side(&rect.top, &rect.height,
- MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
-
- return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip);
-}
-
-static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- a->c = mt9t031->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = MT9T031_COLUMN_SKIP;
- a->bounds.top = MT9T031_ROW_SKIP;
- a->bounds.width = MT9T031_MAX_WIDTH;
- a->bounds.height = MT9T031_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int mt9t031_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- mf->width = mt9t031->rect.width / mt9t031->xskip;
- mf->height = mt9t031->rect.height / mt9t031->yskip;
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9t031_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- u16 xskip, yskip;
- struct v4l2_rect rect = mt9t031->rect;
-
- /*
- * try_fmt has put width and height within limits.
- * S_FMT: use binning and skipping for scaling
- */
- xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH);
- yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT);
-
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
- mf->colorspace = V4L2_COLORSPACE_SRGB;
-
- /* mt9t031_set_params() doesn't change width and height */
- return mt9t031_set_params(client, &rect, xskip, yskip);
-}
-
-/*
- * If a user window larger than sensor window is requested, we'll increase the
- * sensor window.
- */
-static int mt9t031_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- v4l_bound_align_image(
- &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1,
- &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0);
-
- mf->code = V4L2_MBUS_FMT_SBGGR10_1X10;
- mf->colorspace = V4L2_COLORSPACE_SRGB;
-
- return 0;
-}
-
-static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9t031->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9t031_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- reg->val = reg_read(client, reg->reg);
-
- if (reg->val > 0xffff)
- return -EIO;
-
- return 0;
-}
-
-static int mt9t031_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- if (reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9t031 *mt9t031 = container_of(ctrl->handler,
- struct mt9t031, hdl);
- const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK;
- s32 min, max;
-
- switch (ctrl->id) {
- case V4L2_CID_EXPOSURE_AUTO:
- min = mt9t031->exposure->minimum;
- max = mt9t031->exposure->maximum;
- mt9t031->exposure->val =
- (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min))
- / shutter_max + min;
- break;
- }
- return 0;
-}
-
-static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9t031 *mt9t031 = container_of(ctrl->handler,
- struct mt9t031, hdl);
- struct v4l2_subdev *sd = &mt9t031->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct v4l2_ctrl *exp = mt9t031->exposure;
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- data = reg_set(client, MT9T031_READ_MODE_2, 0x8000);
- else
- data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- data = reg_set(client, MT9T031_READ_MODE_2, 0x4000);
- else
- data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_GAIN:
- /* See Datasheet Table 7, Gain settings. */
- if (ctrl->val <= ctrl->default_value) {
- /* Pack it into 0..1 step 0.125, register values 0..8 */
- unsigned long range = ctrl->default_value - ctrl->minimum;
- data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range;
-
- dev_dbg(&client->dev, "Setting gain %d\n", data);
- data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
- } else {
- /* Pack it into 1.125..128 variable step, register values 9..0x7860 */
- /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
- unsigned long range = ctrl->maximum - ctrl->default_value - 1;
- /* calculated gain: map 65..127 to 9..1024 step 0.125 */
- unsigned long gain = ((ctrl->val - ctrl->default_value - 1) *
- 1015 + range / 2) / range + 9;
-
- if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
- data = gain;
- else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */
- data = ((gain - 32) * 16 + 16) / 32 + 80;
- else
- /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
- data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
-
- dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n",
- reg_read(client, MT9T031_GLOBAL_GAIN), data);
- data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
- if (data < 0)
- return -EIO;
- }
- return 0;
-
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->val == V4L2_EXPOSURE_MANUAL) {
- unsigned int range = exp->maximum - exp->minimum;
- unsigned int shutter = ((exp->val - exp->minimum) * 1048 +
- range / 2) / range + 1;
- u32 old;
-
- get_shutter(client, &old);
- dev_dbg(&client->dev, "Set shutter from %u to %u\n",
- old, shutter);
- if (set_shutter(client, shutter) < 0)
- return -EIO;
- } else {
- const u16 vblank = MT9T031_VERTICAL_BLANK;
- mt9t031->total_h = mt9t031->rect.height +
- mt9t031->y_skip_top + vblank;
-
- if (set_shutter(client, mt9t031->total_h) < 0)
- return -EIO;
- }
- return 0;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * Power Management:
- * This function does nothing for now but must be present for pm to work
- */
-static int mt9t031_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
-/*
- * Power Management:
- * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged
- * they are however changed at reset if the platform hook is present
- * thus we rewrite them with the values stored by the driver
- */
-static int mt9t031_runtime_resume(struct device *dev)
-{
- struct video_device *vdev = to_video_device(dev);
- struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- int ret;
- u16 xbin, ybin;
-
- xbin = min(mt9t031->xskip, (u16)3);
- ybin = min(mt9t031->yskip, (u16)3);
-
- ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE,
- ((xbin - 1) << 4) | (mt9t031->xskip - 1));
- if (ret < 0)
- return ret;
-
- ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
- ((ybin - 1) << 4) | (mt9t031->yskip - 1));
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct dev_pm_ops mt9t031_dev_pm_ops = {
- .runtime_suspend = mt9t031_runtime_suspend,
- .runtime_resume = mt9t031_runtime_resume,
-};
-
-static struct device_type mt9t031_dev_type = {
- .name = "MT9T031",
- .pm = &mt9t031_dev_pm_ops,
-};
-
-static int mt9t031_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct video_device *vdev = soc_camera_i2c_to_vdev(client);
-
- if (on)
- vdev->dev.type = &mt9t031_dev_type;
- else
- vdev->dev.type = NULL;
-
- return 0;
-}
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int mt9t031_video_probe(struct i2c_client *client)
-{
- struct mt9t031 *mt9t031 = to_mt9t031(client);
- s32 data;
- int ret;
-
- /* Enable the chip */
- data = reg_write(client, MT9T031_CHIP_ENABLE, 1);
- dev_dbg(&client->dev, "write: %d\n", data);
-
- /* Read out the chip version register */
- data = reg_read(client, MT9T031_CHIP_VERSION);
-
- switch (data) {
- case 0x1621:
- mt9t031->model = V4L2_IDENT_MT9T031;
- break;
- default:
- dev_err(&client->dev,
- "No MT9T031 chip detected, register read %x\n", data);
- return -ENODEV;
- }
-
- dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
-
- ret = mt9t031_idle(client);
- if (ret < 0)
- dev_err(&client->dev, "Failed to initialise the camera\n");
- else
- v4l2_ctrl_handler_setup(&mt9t031->hdl);
-
- return ret;
-}
-
-static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- *lines = mt9t031->y_skip_top;
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = {
- .g_volatile_ctrl = mt9t031_g_volatile_ctrl,
- .s_ctrl = mt9t031_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
- .g_chip_ident = mt9t031_g_chip_ident,
- .s_power = mt9t031_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9t031_g_register,
- .s_register = mt9t031_s_register,
-#endif
-};
-
-static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index)
- return -EINVAL;
-
- *code = V4L2_MBUS_FMT_SBGGR10_1X10;
- return 0;
-}
-
-static int mt9t031_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int mt9t031_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- if (soc_camera_apply_board_flags(icl, cfg) &
- V4L2_MBUS_PCLK_SAMPLE_FALLING)
- return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
- else
- return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
-}
-
-static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
- .s_stream = mt9t031_s_stream,
- .s_mbus_fmt = mt9t031_s_fmt,
- .g_mbus_fmt = mt9t031_g_fmt,
- .try_mbus_fmt = mt9t031_try_fmt,
- .s_crop = mt9t031_s_crop,
- .g_crop = mt9t031_g_crop,
- .cropcap = mt9t031_cropcap,
- .enum_mbus_fmt = mt9t031_enum_fmt,
- .g_mbus_config = mt9t031_g_mbus_config,
- .s_mbus_config = mt9t031_s_mbus_config,
-};
-
-static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = {
- .g_skip_top_lines = mt9t031_g_skip_top_lines,
-};
-
-static struct v4l2_subdev_ops mt9t031_subdev_ops = {
- .core = &mt9t031_subdev_core_ops,
- .video = &mt9t031_subdev_video_ops,
- .sensor = &mt9t031_subdev_sensor_ops,
-};
-
-static int mt9t031_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9t031 *mt9t031;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "MT9T031 driver needs platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9t031 = kzalloc(sizeof(struct mt9t031), GFP_KERNEL);
- if (!mt9t031)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
- v4l2_ctrl_handler_init(&mt9t031->hdl, 5);
- v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 64);
-
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl,
- &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
- V4L2_EXPOSURE_AUTO);
- mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, 255, 1, 255);
-
- mt9t031->subdev.ctrl_handler = &mt9t031->hdl;
- if (mt9t031->hdl.error) {
- int err = mt9t031->hdl.error;
-
- kfree(mt9t031);
- return err;
- }
- v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure,
- V4L2_EXPOSURE_MANUAL, true);
-
- mt9t031->y_skip_top = 0;
- mt9t031->rect.left = MT9T031_COLUMN_SKIP;
- mt9t031->rect.top = MT9T031_ROW_SKIP;
- mt9t031->rect.width = MT9T031_MAX_WIDTH;
- mt9t031->rect.height = MT9T031_MAX_HEIGHT;
-
- mt9t031->xskip = 1;
- mt9t031->yskip = 1;
-
- mt9t031_idle(client);
-
- ret = mt9t031_video_probe(client);
-
- mt9t031_disable(client);
-
- if (ret) {
- v4l2_ctrl_handler_free(&mt9t031->hdl);
- kfree(mt9t031);
- }
-
- return ret;
-}
-
-static int mt9t031_remove(struct i2c_client *client)
-{
- struct mt9t031 *mt9t031 = to_mt9t031(client);
-
- v4l2_device_unregister_subdev(&mt9t031->subdev);
- v4l2_ctrl_handler_free(&mt9t031->hdl);
- kfree(mt9t031);
-
- return 0;
-}
-
-static const struct i2c_device_id mt9t031_id[] = {
- { "mt9t031", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9t031_id);
-
-static struct i2c_driver mt9t031_i2c_driver = {
- .driver = {
- .name = "mt9t031",
- },
- .probe = mt9t031_probe,
- .remove = mt9t031_remove,
- .id_table = mt9t031_id,
-};
-
-module_i2c_driver(mt9t031_i2c_driver);
-
-MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
deleted file mode 100644
index e1ae46a7ee96..000000000000
--- a/drivers/media/video/mt9t112.c
+++ /dev/null
@@ -1,1125 +0,0 @@
-/*
- * mt9t112 Camera Driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov772x driver, mt9m111 driver,
- *
- * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
- * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr>
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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/delay.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/mt9t112.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
-
-/* you can check PLL/clock info */
-/* #define EXT_CLOCK 24000000 */
-
-/************************************************************************
- macro
-************************************************************************/
-/*
- * frame size
- */
-#define MAX_WIDTH 2048
-#define MAX_HEIGHT 1536
-
-#define VGA_WIDTH 640
-#define VGA_HEIGHT 480
-
-/*
- * macro of read/write
- */
-#define ECHECKER(ret, x) \
- do { \
- (ret) = (x); \
- if ((ret) < 0) \
- return (ret); \
- } while (0)
-
-#define mt9t112_reg_write(ret, client, a, b) \
- ECHECKER(ret, __mt9t112_reg_write(client, a, b))
-#define mt9t112_mcu_write(ret, client, a, b) \
- ECHECKER(ret, __mt9t112_mcu_write(client, a, b))
-
-#define mt9t112_reg_mask_set(ret, client, a, b, c) \
- ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c))
-#define mt9t112_mcu_mask_set(ret, client, a, b, c) \
- ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c))
-
-#define mt9t112_reg_read(ret, client, a) \
- ECHECKER(ret, __mt9t112_reg_read(client, a))
-
-/*
- * Logical address
- */
-#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff))
-#define VAR(id, offset) _VAR(id, offset, 0x0000)
-#define VAR8(id, offset) _VAR(id, offset, 0x8000)
-
-/************************************************************************
- struct
-************************************************************************/
-struct mt9t112_format {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
- u16 fmt;
- u16 order;
-};
-
-struct mt9t112_priv {
- struct v4l2_subdev subdev;
- struct mt9t112_camera_info *info;
- struct i2c_client *client;
- struct v4l2_rect frame;
- const struct mt9t112_format *format;
- int model;
- u32 flags;
-/* for flags */
-#define INIT_DONE (1 << 0)
-#define PCLK_RISING (1 << 1)
-};
-
-/************************************************************************
- supported format
-************************************************************************/
-
-static const struct mt9t112_format mt9t112_cfmts[] = {
- {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .fmt = 1,
- .order = 0,
- }, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .fmt = 1,
- .order = 1,
- }, {
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .fmt = 1,
- .order = 2,
- }, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .fmt = 1,
- .order = 3,
- }, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 8,
- .order = 2,
- }, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .fmt = 4,
- .order = 2,
- },
-};
-
-/************************************************************************
- general function
-************************************************************************/
-static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client),
- struct mt9t112_priv,
- subdev);
-}
-
-static int __mt9t112_reg_read(const struct i2c_client *client, u16 command)
-{
- struct i2c_msg msg[2];
- u8 buf[2];
- int ret;
-
- command = swab16(command);
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 2;
- msg[0].buf = (u8 *)&command;
-
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 2;
- msg[1].buf = buf;
-
- /*
- * if return value of this function is < 0,
- * it mean error.
- * else, under 16bit is valid data.
- */
- ret = i2c_transfer(client->adapter, msg, 2);
- if (ret < 0)
- return ret;
-
- memcpy(&ret, buf, 2);
- return swab16(ret);
-}
-
-static int __mt9t112_reg_write(const struct i2c_client *client,
- u16 command, u16 data)
-{
- struct i2c_msg msg;
- u8 buf[4];
- int ret;
-
- command = swab16(command);
- data = swab16(data);
-
- memcpy(buf + 0, &command, 2);
- memcpy(buf + 2, &data, 2);
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 4;
- msg.buf = buf;
-
- /*
- * i2c_transfer return message length,
- * but this function should return 0 if correct case
- */
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret >= 0)
- ret = 0;
-
- return ret;
-}
-
-static int __mt9t112_reg_mask_set(const struct i2c_client *client,
- u16 command,
- u16 mask,
- u16 set)
-{
- int val = __mt9t112_reg_read(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return __mt9t112_reg_write(client, command, val);
-}
-
-/* mcu access */
-static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command)
-{
- int ret;
-
- ret = __mt9t112_reg_write(client, 0x098E, command);
- if (ret < 0)
- return ret;
-
- return __mt9t112_reg_read(client, 0x0990);
-}
-
-static int __mt9t112_mcu_write(const struct i2c_client *client,
- u16 command, u16 data)
-{
- int ret;
-
- ret = __mt9t112_reg_write(client, 0x098E, command);
- if (ret < 0)
- return ret;
-
- return __mt9t112_reg_write(client, 0x0990, data);
-}
-
-static int __mt9t112_mcu_mask_set(const struct i2c_client *client,
- u16 command,
- u16 mask,
- u16 set)
-{
- int val = __mt9t112_mcu_read(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return __mt9t112_mcu_write(client, command, val);
-}
-
-static int mt9t112_reset(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001);
- msleep(1);
- mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000);
-
- return ret;
-}
-
-#ifndef EXT_CLOCK
-#define CLOCK_INFO(a, b)
-#else
-#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b)
-static int mt9t112_clock_info(const struct i2c_client *client, u32 ext)
-{
- int m, n, p1, p2, p3, p4, p5, p6, p7;
- u32 vco, clk;
- char *enable;
-
- ext /= 1000; /* kbyte order */
-
- mt9t112_reg_read(n, client, 0x0012);
- p1 = n & 0x000f;
- n = n >> 4;
- p2 = n & 0x000f;
- n = n >> 4;
- p3 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x002a);
- p4 = n & 0x000f;
- n = n >> 4;
- p5 = n & 0x000f;
- n = n >> 4;
- p6 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x002c);
- p7 = n & 0x000f;
-
- mt9t112_reg_read(n, client, 0x0010);
- m = n & 0x00ff;
- n = (n >> 8) & 0x003f;
-
- enable = ((6000 > ext) || (54000 < ext)) ? "X" : "";
- dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable);
-
- vco = 2 * m * ext / (n+1);
- enable = ((384000 > vco) || (768000 < vco)) ? "X" : "";
- dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable);
-
- clk = vco / (p1+1) / (p2+1);
- enable = (96000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p3+1);
- enable = (768000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p6+1);
- enable = (96000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p5+1);
- enable = (54000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p4+1);
- enable = (70000 < clk) ? "X" : "";
- dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable);
-
- clk = vco / (p7+1);
- dev_dbg(&client->dev, "External sensor : %10u K\n", clk);
-
- clk = ext / (n+1);
- enable = ((2000 > clk) || (24000 < clk)) ? "X" : "";
- dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable);
-
- return 0;
-}
-#endif
-
-static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top)
-{
- soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH);
- soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT);
-}
-
-static int mt9t112_set_a_frame_size(const struct i2c_client *client,
- u16 width,
- u16 height)
-{
- int ret;
- u16 wstart = (MAX_WIDTH - width) / 2;
- u16 hstart = (MAX_HEIGHT - height) / 2;
-
- /* (Context A) Image Width/Height */
- mt9t112_mcu_write(ret, client, VAR(26, 0), width);
- mt9t112_mcu_write(ret, client, VAR(26, 2), height);
-
- /* (Context A) Output Width/Height */
- mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width);
- mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height);
-
- /* (Context A) Start Row/Column */
- mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart);
- mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart);
-
- /* (Context A) End Row/Column */
- mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart);
- mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart);
-
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- return ret;
-}
-
-static int mt9t112_set_pll_dividers(const struct i2c_client *client,
- u8 m, u8 n,
- u8 p1, u8 p2, u8 p3,
- u8 p4, u8 p5, u8 p6,
- u8 p7)
-{
- int ret;
- u16 val;
-
- /* N/M */
- val = (n << 8) |
- (m << 0);
- mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val);
-
- /* P1/P2/P3 */
- val = ((p3 & 0x0F) << 8) |
- ((p2 & 0x0F) << 4) |
- ((p1 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val);
-
- /* P4/P5/P6 */
- val = (0x7 << 12) |
- ((p6 & 0x0F) << 8) |
- ((p5 & 0x0F) << 4) |
- ((p4 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val);
-
- /* P7 */
- val = (0x1 << 12) |
- ((p7 & 0x0F) << 0);
- mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val);
-
- return ret;
-}
-
-static int mt9t112_init_pll(const struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
- int data, i, ret;
-
- mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001);
-
- /* PLL control: BYPASS PLL = 8517 */
- mt9t112_reg_write(ret, client, 0x0014, 0x2145);
-
- /* Replace these registers when new timing parameters are generated */
- mt9t112_set_pll_dividers(client,
- priv->info->divider.m,
- priv->info->divider.n,
- priv->info->divider.p1,
- priv->info->divider.p2,
- priv->info->divider.p3,
- priv->info->divider.p4,
- priv->info->divider.p5,
- priv->info->divider.p6,
- priv->info->divider.p7);
-
- /*
- * TEST_BYPASS on
- * PLL_ENABLE on
- * SEL_LOCK_DET on
- * TEST_BYPASS off
- */
- mt9t112_reg_write(ret, client, 0x0014, 0x2525);
- mt9t112_reg_write(ret, client, 0x0014, 0x2527);
- mt9t112_reg_write(ret, client, 0x0014, 0x3427);
- mt9t112_reg_write(ret, client, 0x0014, 0x3027);
-
- mdelay(10);
-
- /*
- * PLL_BYPASS off
- * Reference clock count
- * I2C Master Clock Divider
- */
- mt9t112_reg_write(ret, client, 0x0014, 0x3046);
- mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */
- mt9t112_reg_write(ret, client, 0x0022, 0x0190);
- mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
-
- /* External sensor clock is PLL bypass */
- mt9t112_reg_write(ret, client, 0x002E, 0x0500);
-
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002);
- mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004);
-
- /* MCU disabled */
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004);
-
- /* out of standby */
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0);
-
- mdelay(50);
-
- /*
- * Standby Workaround
- * Disable Secondary I2C Pads
- */
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
- mt9t112_reg_write(ret, client, 0x0614, 0x0001);
- mdelay(1);
-
- /* poll to verify out of standby. Must Poll this bit */
- for (i = 0; i < 100; i++) {
- mt9t112_reg_read(data, client, 0x0018);
- if (!(0x4000 & data))
- break;
-
- mdelay(10);
- }
-
- return ret;
-}
-
-static int mt9t112_init_setting(const struct i2c_client *client)
-{
-
- int ret;
-
- /* Adaptive Output Clock (A) */
- mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000);
-
- /* Read Mode (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024);
-
- /* Fine Correction (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC);
-
- /* Fine IT Min (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1);
-
- /* Fine IT Max Margin (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF);
-
- /* Base Frame Lines (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D);
-
- /* Min Line Length (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a);
-
- /* Line Length (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0);
-
- /* Adaptive Output Clock (B) */
- mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000);
-
- /* Row Start (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004);
-
- /* Column Start (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004);
-
- /* Row End (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B);
-
- /* Column End (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B);
-
- /* Fine Correction (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C);
-
- /* Fine IT Min (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1);
-
- /* Fine IT Max Margin (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF);
-
- /* Base Frame Lines (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668);
-
- /* Min Line Length (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0);
-
- /* Line Length (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0);
-
- /*
- * Flicker Dectection registers
- * This section should be replaced whenever new Timing file is generated
- * All the following registers need to be replaced
- * Following registers are generated from Register Wizard but user can
- * modify them. For detail see auto flicker detection tuning
- */
-
- /* FD_FDPERIOD_SELECT */
- mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01);
-
- /* PRI_B_CONFIG_FD_ALGO_RUN */
- mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003);
-
- /* PRI_A_CONFIG_FD_ALGO_RUN */
- mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003);
-
- /*
- * AFD range detection tuning registers
- */
-
- /* search_f1_50 */
- mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25);
-
- /* search_f2_50 */
- mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28);
-
- /* search_f1_60 */
- mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C);
-
- /* search_f2_60 */
- mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F);
-
- /* period_50Hz (A) */
- mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA);
-
- /* secret register by aptina */
- /* period_50Hz (A MSB) */
- mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00);
-
- /* period_60Hz (A) */
- mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B);
-
- /* secret register by aptina */
- /* period_60Hz (A MSB) */
- mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00);
-
- /* period_50Hz (B) */
- mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82);
-
- /* secret register by aptina */
- /* period_50Hz (B) MSB */
- mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00);
-
- /* period_60Hz (B) */
- mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D);
-
- /* secret register by aptina */
- /* period_60Hz (B) MSB */
- mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00);
-
- /* FD Mode */
- mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10);
-
- /* Stat_min */
- mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02);
-
- /* Stat_max */
- mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03);
-
- /* Min_amplitude */
- mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A);
-
- /* RX FIFO Watermark (A) */
- mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014);
-
- /* RX FIFO Watermark (B) */
- mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014);
-
- /* MCLK: 16MHz
- * PCLK: 73MHz
- * CorePixCLK: 36.5 MHz
- */
- mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108);
-
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32);
- mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35);
-
- return ret;
-}
-
-static int mt9t112_auto_focus_setting(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F);
- mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- mt9t112_reg_write(ret, client, 0x0614, 0x0000);
-
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
- mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02);
- mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002);
- mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001);
- mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025);
- mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193);
- mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05);
-
- return ret;
-}
-
-static int mt9t112_auto_focus_trigger(const struct i2c_client *client)
-{
- int ret;
-
- mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01);
-
- return ret;
-}
-
-static int mt9t112_init_camera(const struct i2c_client *client)
-{
- int ret;
-
- ECHECKER(ret, mt9t112_reset(client));
-
- ECHECKER(ret, mt9t112_init_pll(client));
-
- ECHECKER(ret, mt9t112_init_setting(client));
-
- ECHECKER(ret, mt9t112_auto_focus_setting(client));
-
- mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0);
-
- /* Analog setting B */
- mt9t112_reg_write(ret, client, 0x3084, 0x2409);
- mt9t112_reg_write(ret, client, 0x3092, 0x0A49);
- mt9t112_reg_write(ret, client, 0x3094, 0x4949);
- mt9t112_reg_write(ret, client, 0x3096, 0x4950);
-
- /*
- * Disable adaptive clock
- * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR
- * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR
- */
- mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E);
- mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E);
-
- /* Configure STatus in Status_before_length Format and enable header */
- /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
- mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4);
-
- /* Enable JPEG in context B */
- /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */
- mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01);
-
- /* Disable Dac_TXLO */
- mt9t112_reg_write(ret, client, 0x316C, 0x350F);
-
- /* Set max slew rates */
- mt9t112_reg_write(ret, client, 0x1E, 0x777);
-
- return ret;
-}
-
-/************************************************************************
- v4l2_subdev_core_ops
-************************************************************************/
-static int mt9t112_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9t112_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- reg->size = 2;
- mt9t112_reg_read(ret, client, reg->reg);
-
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int mt9t112_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- mt9t112_reg_write(ret, client, reg->reg, reg->val);
-
- return ret;
-}
-#endif
-
-static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = {
- .g_chip_ident = mt9t112_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9t112_g_register,
- .s_register = mt9t112_s_register,
-#endif
-};
-
-
-/************************************************************************
- v4l2_subdev_video_ops
-************************************************************************/
-static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- int ret = 0;
-
- if (!enable) {
- /* FIXME
- *
- * If user selected large output size,
- * and used it long time,
- * mt9t112 camera will be very warm.
- *
- * But current driver can not stop mt9t112 camera.
- * So, set small size here to solve this problem.
- */
- mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT);
- return ret;
- }
-
- if (!(priv->flags & INIT_DONE)) {
- u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000;
-
- ECHECKER(ret, mt9t112_init_camera(client));
-
- /* Invert PCLK (Data sampled on falling edge of pixclk) */
- mt9t112_reg_write(ret, client, 0x3C20, param);
-
- mdelay(5);
-
- priv->flags |= INIT_DONE;
- }
-
- mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt);
- mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order);
- mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06);
-
- mt9t112_set_a_frame_size(client,
- priv->frame.width,
- priv->frame.height);
-
- ECHECKER(ret, mt9t112_auto_focus_trigger(client));
-
- dev_dbg(&client->dev, "format : %d\n", priv->format->code);
- dev_dbg(&client->dev, "size : %d x %d\n",
- priv->frame.width,
- priv->frame.height);
-
- CLOCK_INFO(client, EXT_CLOCK);
-
- return ret;
-}
-
-static int mt9t112_set_params(struct mt9t112_priv *priv,
- const struct v4l2_rect *rect,
- enum v4l2_mbus_pixelcode code)
-{
- int i;
-
- /*
- * get color format
- */
- for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
- if (mt9t112_cfmts[i].code == code)
- break;
-
- if (i == ARRAY_SIZE(mt9t112_cfmts))
- return -EINVAL;
-
- priv->frame = *rect;
-
- /*
- * frame size check
- */
- mt9t112_frame_check(&priv->frame.width, &priv->frame.height,
- &priv->frame.left, &priv->frame.top);
-
- priv->format = mt9t112_cfmts + i;
-
- return 0;
-}
-
-static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = MAX_WIDTH;
- a->bounds.height = MAX_HEIGHT;
- a->defrect.left = 0;
- a->defrect.top = 0;
- a->defrect.width = VGA_WIDTH;
- a->defrect.height = VGA_HEIGHT;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- a->c = priv->frame;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int mt9t112_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- struct v4l2_rect *rect = &a->c;
-
- return mt9t112_set_params(priv, rect, priv->format->code);
-}
-
-static int mt9t112_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- mf->width = priv->frame.width;
- mf->height = priv->frame.height;
- mf->colorspace = priv->format->colorspace;
- mf->code = priv->format->code;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9t112_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9t112_priv *priv = to_mt9t112(client);
- struct v4l2_rect rect = {
- .width = mf->width,
- .height = mf->height,
- .left = priv->frame.left,
- .top = priv->frame.top,
- };
- int ret;
-
- ret = mt9t112_set_params(priv, &rect, mf->code);
-
- if (!ret)
- mf->colorspace = priv->format->colorspace;
-
- return ret;
-}
-
-static int mt9t112_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- unsigned int top, left;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++)
- if (mt9t112_cfmts[i].code == mf->code)
- break;
-
- if (i == ARRAY_SIZE(mt9t112_cfmts)) {
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- } else {
- mf->colorspace = mt9t112_cfmts[i].colorspace;
- }
-
- mt9t112_frame_check(&mf->width, &mf->height, &left, &top);
-
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(mt9t112_cfmts))
- return -EINVAL;
-
- *code = mt9t112_cfmts[index].code;
-
- return 0;
-}
-
-static int mt9t112_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int mt9t112_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING)
- priv->flags |= PCLK_RISING;
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = {
- .s_stream = mt9t112_s_stream,
- .g_mbus_fmt = mt9t112_g_fmt,
- .s_mbus_fmt = mt9t112_s_fmt,
- .try_mbus_fmt = mt9t112_try_fmt,
- .cropcap = mt9t112_cropcap,
- .g_crop = mt9t112_g_crop,
- .s_crop = mt9t112_s_crop,
- .enum_mbus_fmt = mt9t112_enum_fmt,
- .g_mbus_config = mt9t112_g_mbus_config,
- .s_mbus_config = mt9t112_s_mbus_config,
-};
-
-/************************************************************************
- i2c driver
-************************************************************************/
-static struct v4l2_subdev_ops mt9t112_subdev_ops = {
- .core = &mt9t112_subdev_core_ops,
- .video = &mt9t112_subdev_video_ops,
-};
-
-static int mt9t112_camera_probe(struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
- const char *devname;
- int chipid;
-
- /*
- * check and show chip ID
- */
- mt9t112_reg_read(chipid, client, 0x0000);
-
- switch (chipid) {
- case 0x2680:
- devname = "mt9t111";
- priv->model = V4L2_IDENT_MT9T111;
- break;
- case 0x2682:
- devname = "mt9t112";
- priv->model = V4L2_IDENT_MT9T112;
- break;
- default:
- dev_err(&client->dev, "Product ID error %04x\n", chipid);
- return -ENODEV;
- }
-
- dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid);
-
- return 0;
-}
-
-static int mt9t112_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9t112_priv *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct v4l2_rect rect = {
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- .left = (MAX_WIDTH - VGA_WIDTH) / 2,
- .top = (MAX_HEIGHT - VGA_HEIGHT) / 2,
- };
- int ret;
-
- if (!icl || !icl->priv) {
- dev_err(&client->dev, "mt9t112: missing platform data!\n");
- return -EINVAL;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = icl->priv;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops);
-
- ret = mt9t112_camera_probe(client);
- if (ret) {
- kfree(priv);
- return ret;
- }
-
- /* Cannot fail: using the default supported pixel code */
- mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8);
-
- return ret;
-}
-
-static int mt9t112_remove(struct i2c_client *client)
-{
- struct mt9t112_priv *priv = to_mt9t112(client);
-
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id mt9t112_id[] = {
- { "mt9t112", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9t112_id);
-
-static struct i2c_driver mt9t112_i2c_driver = {
- .driver = {
- .name = "mt9t112",
- },
- .probe = mt9t112_probe,
- .remove = mt9t112_remove,
- .id_table = mt9t112_id,
-};
-
-module_i2c_driver(mt9t112_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
deleted file mode 100644
index 72479247522a..000000000000
--- a/drivers/media/video/mt9v022.c
+++ /dev/null
@@ -1,879 +0,0 @@
-/*
- * Driver for MT9V022 CMOS Image Sensor from Micron
- *
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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/videodev2.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/delay.h>
-#include <linux/log2.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-
-/*
- * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
- * The platform has to define struct i2c_board_info objects and link to them
- * from struct soc_camera_link
- */
-
-static char *sensor_type;
-module_param(sensor_type, charp, S_IRUGO);
-MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
-
-/* mt9v022 selected register addresses */
-#define MT9V022_CHIP_VERSION 0x00
-#define MT9V022_COLUMN_START 0x01
-#define MT9V022_ROW_START 0x02
-#define MT9V022_WINDOW_HEIGHT 0x03
-#define MT9V022_WINDOW_WIDTH 0x04
-#define MT9V022_HORIZONTAL_BLANKING 0x05
-#define MT9V022_VERTICAL_BLANKING 0x06
-#define MT9V022_CHIP_CONTROL 0x07
-#define MT9V022_SHUTTER_WIDTH1 0x08
-#define MT9V022_SHUTTER_WIDTH2 0x09
-#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a
-#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b
-#define MT9V022_RESET 0x0c
-#define MT9V022_READ_MODE 0x0d
-#define MT9V022_MONITOR_MODE 0x0e
-#define MT9V022_PIXEL_OPERATION_MODE 0x0f
-#define MT9V022_LED_OUT_CONTROL 0x1b
-#define MT9V022_ADC_MODE_CONTROL 0x1c
-#define MT9V022_ANALOG_GAIN 0x35
-#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
-#define MT9V022_PIXCLK_FV_LV 0x74
-#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
-#define MT9V022_AEC_AGC_ENABLE 0xAF
-#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD
-
-/* Progressive scan, master, defaults */
-#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
-
-#define MT9V022_MAX_WIDTH 752
-#define MT9V022_MAX_HEIGHT 480
-#define MT9V022_MIN_WIDTH 48
-#define MT9V022_MIN_HEIGHT 32
-#define MT9V022_COLUMN_SKIP 1
-#define MT9V022_ROW_SKIP 4
-
-/* MT9V022 has only one fixed colorspace per pixelcode */
-struct mt9v022_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-/* Find a data format by a pixel code in an array */
-static const struct mt9v022_datafmt *mt9v022_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt,
- int n)
-{
- int i;
- for (i = 0; i < n; i++)
- if (fmt[i].code == code)
- return fmt + i;
-
- return NULL;
-}
-
-static const struct mt9v022_datafmt mt9v022_colour_fmts[] = {
- /*
- * Order important: first natively supported,
- * second supported with a GPIO extender
- */
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
-};
-
-static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = {
- /* Order important - see above */
- {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG},
-};
-
-struct mt9v022 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct {
- /* exposure/auto-exposure cluster */
- struct v4l2_ctrl *autoexposure;
- struct v4l2_ctrl *exposure;
- };
- struct {
- /* gain/auto-gain cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *gain;
- };
- struct v4l2_rect rect; /* Sensor window */
- const struct mt9v022_datafmt *fmt;
- const struct mt9v022_datafmt *fmts;
- int num_fmts;
- int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
- u16 chip_control;
- unsigned short y_skip_top; /* Lines to skip at the top */
-};
-
-static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct mt9v022, subdev);
-}
-
-static int reg_read(struct i2c_client *client, const u8 reg)
-{
- return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int reg_write(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static int reg_set(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret | data);
-}
-
-static int reg_clear(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, ret & ~data);
-}
-
-static int mt9v022_init(struct i2c_client *client)
-{
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- int ret;
-
- /*
- * Almost the default mode: master, parallel, simultaneous, and an
- * undocumented bit 0x200, which is present in table 7, but not in 8,
- * plus snapshot mode to disable scan for now
- */
- mt9v022->chip_control |= 0x10;
- ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (!ret)
- ret = reg_write(client, MT9V022_READ_MODE, 0x300);
-
- /* All defaults */
- if (!ret)
- /* AEC, AGC on */
- ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3);
- if (!ret)
- ret = reg_write(client, MT9V022_ANALOG_GAIN, 16);
- if (!ret)
- ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480);
- if (!ret)
- ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480);
- if (!ret)
- /* default - auto */
- ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1);
- if (!ret)
- ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0);
- if (!ret)
- return v4l2_ctrl_handler_setup(&mt9v022->hdl);
-
- return ret;
-}
-
-static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- if (enable)
- /* Switch to master "normal" mode */
- mt9v022->chip_control &= ~0x10;
- else
- /* Switch to snapshot mode */
- mt9v022->chip_control |= 0x10;
-
- if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
- return -EIO;
- return 0;
-}
-
-static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct v4l2_rect rect = a->c;
- int ret;
-
- /* Bayer format - even size lengths */
- if (mt9v022->fmts == mt9v022_colour_fmts) {
- rect.width = ALIGN(rect.width, 2);
- rect.height = ALIGN(rect.height, 2);
- /* Let the user play with the starting pixel */
- }
-
- soc_camera_limit_side(&rect.left, &rect.width,
- MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH);
-
- soc_camera_limit_side(&rect.top, &rect.height,
- MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT);
-
- /* Like in example app. Contradicts the datasheet though */
- ret = reg_read(client, MT9V022_AEC_AGC_ENABLE);
- if (ret >= 0) {
- if (ret & 1) /* Autoexposure */
- ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
- rect.height + mt9v022->y_skip_top + 43);
- else
- ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- rect.height + mt9v022->y_skip_top + 43);
- }
- /* Setup frame format: defaults apart from width and height */
- if (!ret)
- ret = reg_write(client, MT9V022_COLUMN_START, rect.left);
- if (!ret)
- ret = reg_write(client, MT9V022_ROW_START, rect.top);
- if (!ret)
- /*
- * Default 94, Phytec driver says:
- * "width + horizontal blank >= 660"
- */
- ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
- rect.width > 660 - 43 ? 43 :
- 660 - rect.width);
- if (!ret)
- ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45);
- if (!ret)
- ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
- if (!ret)
- ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
- rect.height + mt9v022->y_skip_top);
-
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height);
-
- mt9v022->rect = rect;
-
- return 0;
-}
-
-static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- a->c = mt9v022->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = MT9V022_COLUMN_SKIP;
- a->bounds.top = MT9V022_ROW_SKIP;
- a->bounds.width = MT9V022_MAX_WIDTH;
- a->bounds.height = MT9V022_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int mt9v022_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- mf->width = mt9v022->rect.width;
- mf->height = mt9v022->rect.height;
- mf->code = mt9v022->fmt->code;
- mf->colorspace = mt9v022->fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int mt9v022_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct v4l2_crop a = {
- .c = {
- .left = mt9v022->rect.left,
- .top = mt9v022->rect.top,
- .width = mf->width,
- .height = mf->height,
- },
- };
- int ret;
-
- /*
- * The caller provides a supported format, as verified per call to
- * .try_mbus_fmt(), datawidth is from our supported format list
- */
- switch (mf->code) {
- case V4L2_MBUS_FMT_Y8_1X8:
- case V4L2_MBUS_FMT_Y10_1X10:
- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
- return -EINVAL;
- break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- /* No support for scaling on this camera, just crop. */
- ret = mt9v022_s_crop(sd, &a);
- if (!ret) {
- mf->width = mt9v022->rect.width;
- mf->height = mt9v022->rect.height;
- mt9v022->fmt = mt9v022_find_datafmt(mf->code,
- mt9v022->fmts, mt9v022->num_fmts);
- mf->colorspace = mt9v022->fmt->colorspace;
- }
-
- return ret;
-}
-
-static int mt9v022_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- const struct mt9v022_datafmt *fmt;
- int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_1X10;
-
- v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH,
- MT9V022_MAX_WIDTH, align,
- &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top,
- MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0);
-
- fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts,
- mt9v022->num_fmts);
- if (!fmt) {
- fmt = mt9v022->fmt;
- mf->code = fmt->code;
- }
-
- mf->colorspace = fmt->colorspace;
-
- return 0;
-}
-
-static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = mt9v022->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9v022_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- reg->size = 2;
- reg->val = reg_read(client, reg->reg);
-
- if (reg->val > 0xffff)
- return -EIO;
-
- return 0;
-}
-
-static int mt9v022_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- if (reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9v022 *mt9v022 = container_of(ctrl->handler,
- struct mt9v022, hdl);
- struct v4l2_subdev *sd = &mt9v022->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct v4l2_ctrl *gain = mt9v022->gain;
- struct v4l2_ctrl *exp = mt9v022->exposure;
- unsigned long range;
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- data = reg_read(client, MT9V022_ANALOG_GAIN);
- if (data < 0)
- return -EIO;
-
- range = gain->maximum - gain->minimum;
- gain->val = ((data - 16) * range + 24) / 48 + gain->minimum;
- return 0;
- case V4L2_CID_EXPOSURE_AUTO:
- data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
- if (data < 0)
- return -EIO;
-
- range = exp->maximum - exp->minimum;
- exp->val = ((data - 1) * range + 239) / 479 + exp->minimum;
- return 0;
- }
- return -EINVAL;
-}
-
-static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9v022 *mt9v022 = container_of(ctrl->handler,
- struct mt9v022, hdl);
- struct v4l2_subdev *sd = &mt9v022->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- data = reg_set(client, MT9V022_READ_MODE, 0x10);
- else
- data = reg_clear(client, MT9V022_READ_MODE, 0x10);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- data = reg_set(client, MT9V022_READ_MODE, 0x20);
- else
- data = reg_clear(client, MT9V022_READ_MODE, 0x20);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_AUTOGAIN:
- if (ctrl->val) {
- if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
- return -EIO;
- } else {
- struct v4l2_ctrl *gain = mt9v022->gain;
- /* mt9v022 has minimum == default */
- unsigned long range = gain->maximum - gain->minimum;
- /* Valid values 16 to 64, 32 to 64 must be even. */
- unsigned long gain_val = ((gain->val - gain->minimum) *
- 48 + range / 2) / range + 16;
-
- if (gain_val >= 32)
- gain_val &= ~1;
-
- /*
- * The user wants to set gain manually, hope, she
- * knows, what she's doing... Switch AGC off.
- */
- if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
- return -EIO;
-
- dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
- reg_read(client, MT9V022_ANALOG_GAIN), gain_val);
- if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0)
- return -EIO;
- }
- return 0;
- case V4L2_CID_EXPOSURE_AUTO:
- if (ctrl->val == V4L2_EXPOSURE_AUTO) {
- data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- } else {
- struct v4l2_ctrl *exp = mt9v022->exposure;
- unsigned long range = exp->maximum - exp->minimum;
- unsigned long shutter = ((exp->val - exp->minimum) *
- 479 + range / 2) / range + 1;
-
- /*
- * The user wants to set shutter width manually, hope,
- * she knows, what she's doing... Switch AEC off.
- */
- data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1);
- if (data < 0)
- return -EIO;
- dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
- reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
- shutter);
- if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- shutter) < 0)
- return -EIO;
- }
- return 0;
- }
- return -EINVAL;
-}
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int mt9v022_video_probe(struct i2c_client *client)
-{
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- s32 data;
- int ret;
- unsigned long flags;
-
- /* Read out the chip version register */
- data = reg_read(client, MT9V022_CHIP_VERSION);
-
- /* must be 0x1311 or 0x1313 */
- if (data != 0x1311 && data != 0x1313) {
- ret = -ENODEV;
- dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n",
- data);
- goto ei2c;
- }
-
- /* Soft reset */
- ret = reg_write(client, MT9V022_RESET, 1);
- if (ret < 0)
- goto ei2c;
- /* 15 clock cycles */
- udelay(200);
- if (reg_read(client, MT9V022_RESET)) {
- dev_err(&client->dev, "Resetting MT9V022 failed!\n");
- if (ret > 0)
- ret = -EIO;
- goto ei2c;
- }
-
- /* Set monochrome or colour sensor type */
- if (sensor_type && (!strcmp("colour", sensor_type) ||
- !strcmp("color", sensor_type))) {
- ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
- mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
- mt9v022->fmts = mt9v022_colour_fmts;
- } else {
- ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11);
- mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
- mt9v022->fmts = mt9v022_monochrome_fmts;
- }
-
- if (ret < 0)
- goto ei2c;
-
- mt9v022->num_fmts = 0;
-
- /*
- * This is a 10bit sensor, so by default we only allow 10bit.
- * The platform may support different bus widths due to
- * different routing of the data lines.
- */
- if (icl->query_bus_param)
- flags = icl->query_bus_param(icl);
- else
- flags = SOCAM_DATAWIDTH_10;
-
- if (flags & SOCAM_DATAWIDTH_10)
- mt9v022->num_fmts++;
- else
- mt9v022->fmts++;
-
- if (flags & SOCAM_DATAWIDTH_8)
- mt9v022->num_fmts++;
-
- mt9v022->fmt = &mt9v022->fmts[0];
-
- dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
- data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
- "monochrome" : "colour");
-
- ret = mt9v022_init(client);
- if (ret < 0)
- dev_err(&client->dev, "Failed to initialise the camera\n");
-
-ei2c:
- return ret;
-}
-
-static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- *lines = mt9v022->y_skip_top;
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = {
- .g_volatile_ctrl = mt9v022_g_volatile_ctrl,
- .s_ctrl = mt9v022_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
- .g_chip_ident = mt9v022_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = mt9v022_g_register,
- .s_register = mt9v022_s_register,
-#endif
-};
-
-static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
-
- if (index >= mt9v022->num_fmts)
- return -EINVAL;
-
- *code = mt9v022->fmts[index].code;
- return 0;
-}
-
-static int mt9v022_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE |
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int mt9v022_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
- unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample;
- int ret;
- u16 pixclk = 0;
-
- if (icl->set_bus_param) {
- ret = icl->set_bus_param(icl, 1 << (bps - 1));
- if (ret)
- return ret;
- } else if (bps != 10) {
- /*
- * Without board specific bus width settings we only support the
- * sensors native bus width
- */
- return -EINVAL;
- }
-
- if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- pixclk |= 0x10;
-
- if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH))
- pixclk |= 0x1;
-
- if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH))
- pixclk |= 0x2;
-
- ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk);
- if (ret < 0)
- return ret;
-
- if (!(flags & V4L2_MBUS_MASTER))
- mt9v022->chip_control &= ~0x8;
-
- ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control);
- if (ret < 0)
- return ret;
-
- dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
- pixclk, mt9v022->chip_control);
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
- .s_stream = mt9v022_s_stream,
- .s_mbus_fmt = mt9v022_s_fmt,
- .g_mbus_fmt = mt9v022_g_fmt,
- .try_mbus_fmt = mt9v022_try_fmt,
- .s_crop = mt9v022_s_crop,
- .g_crop = mt9v022_g_crop,
- .cropcap = mt9v022_cropcap,
- .enum_mbus_fmt = mt9v022_enum_fmt,
- .g_mbus_config = mt9v022_g_mbus_config,
- .s_mbus_config = mt9v022_s_mbus_config,
-};
-
-static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = {
- .g_skip_top_lines = mt9v022_g_skip_top_lines,
-};
-
-static struct v4l2_subdev_ops mt9v022_subdev_ops = {
- .core = &mt9v022_subdev_core_ops,
- .video = &mt9v022_subdev_video_ops,
- .sensor = &mt9v022_subdev_sensor_ops,
-};
-
-static int mt9v022_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9v022 *mt9v022;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "MT9V022 driver needs platform data\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL);
- if (!mt9v022)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
- v4l2_ctrl_handler_init(&mt9v022->hdl, 6);
- v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 64);
-
- /*
- * Simulated autoexposure. If enabled, we calculate shutter width
- * ourselves in the driver based on vertical blanking and frame width
- */
- mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl,
- &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
- V4L2_EXPOSURE_AUTO);
- mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops,
- V4L2_CID_EXPOSURE, 1, 255, 1, 255);
-
- mt9v022->subdev.ctrl_handler = &mt9v022->hdl;
- if (mt9v022->hdl.error) {
- int err = mt9v022->hdl.error;
-
- kfree(mt9v022);
- return err;
- }
- v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure,
- V4L2_EXPOSURE_MANUAL, true);
- v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true);
-
- mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
-
- /*
- * MT9V022 _really_ corrupts the first read out line.
- * TODO: verify on i.MX31
- */
- mt9v022->y_skip_top = 1;
- mt9v022->rect.left = MT9V022_COLUMN_SKIP;
- mt9v022->rect.top = MT9V022_ROW_SKIP;
- mt9v022->rect.width = MT9V022_MAX_WIDTH;
- mt9v022->rect.height = MT9V022_MAX_HEIGHT;
-
- ret = mt9v022_video_probe(client);
- if (ret) {
- v4l2_ctrl_handler_free(&mt9v022->hdl);
- kfree(mt9v022);
- }
-
- return ret;
-}
-
-static int mt9v022_remove(struct i2c_client *client)
-{
- struct mt9v022 *mt9v022 = to_mt9v022(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- v4l2_device_unregister_subdev(&mt9v022->subdev);
- if (icl->free_bus)
- icl->free_bus(icl);
- v4l2_ctrl_handler_free(&mt9v022->hdl);
- kfree(mt9v022);
-
- return 0;
-}
-static const struct i2c_device_id mt9v022_id[] = {
- { "mt9v022", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9v022_id);
-
-static struct i2c_driver mt9v022_i2c_driver = {
- .driver = {
- .name = "mt9v022",
- },
- .probe = mt9v022_probe,
- .remove = mt9v022_remove,
- .id_table = mt9v022_id,
-};
-
-module_i2c_driver(mt9v022_i2c_driver);
-
-MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
deleted file mode 100644
index 4ba4884c016e..000000000000
--- a/drivers/media/video/mt9v032.c
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Driver for MT9V032 CMOS Image Sensor from Micron
- *
- * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- *
- * Based on the MT9M001 driver,
- *
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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/delay.h>
-#include <linux/i2c.h>
-#include <linux/log2.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/module.h>
-
-#include <media/mt9v032.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#define MT9V032_PIXEL_ARRAY_HEIGHT 492
-#define MT9V032_PIXEL_ARRAY_WIDTH 782
-
-#define MT9V032_CHIP_VERSION 0x00
-#define MT9V032_CHIP_ID_REV1 0x1311
-#define MT9V032_CHIP_ID_REV3 0x1313
-#define MT9V032_COLUMN_START 0x01
-#define MT9V032_COLUMN_START_MIN 1
-#define MT9V032_COLUMN_START_DEF 1
-#define MT9V032_COLUMN_START_MAX 752
-#define MT9V032_ROW_START 0x02
-#define MT9V032_ROW_START_MIN 4
-#define MT9V032_ROW_START_DEF 5
-#define MT9V032_ROW_START_MAX 482
-#define MT9V032_WINDOW_HEIGHT 0x03
-#define MT9V032_WINDOW_HEIGHT_MIN 1
-#define MT9V032_WINDOW_HEIGHT_DEF 480
-#define MT9V032_WINDOW_HEIGHT_MAX 480
-#define MT9V032_WINDOW_WIDTH 0x04
-#define MT9V032_WINDOW_WIDTH_MIN 1
-#define MT9V032_WINDOW_WIDTH_DEF 752
-#define MT9V032_WINDOW_WIDTH_MAX 752
-#define MT9V032_HORIZONTAL_BLANKING 0x05
-#define MT9V032_HORIZONTAL_BLANKING_MIN 43
-#define MT9V032_HORIZONTAL_BLANKING_MAX 1023
-#define MT9V032_VERTICAL_BLANKING 0x06
-#define MT9V032_VERTICAL_BLANKING_MIN 4
-#define MT9V032_VERTICAL_BLANKING_MAX 3000
-#define MT9V032_CHIP_CONTROL 0x07
-#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3)
-#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7)
-#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8)
-#define MT9V032_SHUTTER_WIDTH1 0x08
-#define MT9V032_SHUTTER_WIDTH2 0x09
-#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a
-#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b
-#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1
-#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480
-#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767
-#define MT9V032_RESET 0x0c
-#define MT9V032_READ_MODE 0x0d
-#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0)
-#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0
-#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2)
-#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2
-#define MT9V032_READ_MODE_ROW_FLIP (1 << 4)
-#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5)
-#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6)
-#define MT9V032_READ_MODE_DARK_ROWS (1 << 7)
-#define MT9V032_PIXEL_OPERATION_MODE 0x0f
-#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2)
-#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6)
-#define MT9V032_ANALOG_GAIN 0x35
-#define MT9V032_ANALOG_GAIN_MIN 16
-#define MT9V032_ANALOG_GAIN_DEF 16
-#define MT9V032_ANALOG_GAIN_MAX 64
-#define MT9V032_MAX_ANALOG_GAIN 0x36
-#define MT9V032_MAX_ANALOG_GAIN_MAX 127
-#define MT9V032_FRAME_DARK_AVERAGE 0x42
-#define MT9V032_DARK_AVG_THRESH 0x46
-#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0)
-#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0
-#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8)
-#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8
-#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70
-#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5)
-#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7)
-#define MT9V032_PIXEL_CLOCK 0x74
-#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0)
-#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1)
-#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2)
-#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3)
-#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4)
-#define MT9V032_TEST_PATTERN 0x7f
-#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0)
-#define MT9V032_TEST_PATTERN_DATA_SHIFT 0
-#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10)
-#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11)
-#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11)
-#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11)
-#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11)
-#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11)
-#define MT9V032_TEST_PATTERN_ENABLE (1 << 13)
-#define MT9V032_TEST_PATTERN_FLIP (1 << 14)
-#define MT9V032_AEC_AGC_ENABLE 0xaf
-#define MT9V032_AEC_ENABLE (1 << 0)
-#define MT9V032_AGC_ENABLE (1 << 1)
-#define MT9V032_THERMAL_INFO 0xc1
-
-struct mt9v032 {
- struct v4l2_subdev subdev;
- struct media_pad pad;
-
- struct v4l2_mbus_framefmt format;
- struct v4l2_rect crop;
-
- struct v4l2_ctrl_handler ctrls;
-
- struct mutex power_lock;
- int power_count;
-
- struct mt9v032_platform_data *pdata;
- u16 chip_control;
- u16 aec_agc;
-};
-
-static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct mt9v032, subdev);
-}
-
-static int mt9v032_read(struct i2c_client *client, const u8 reg)
-{
- s32 data = i2c_smbus_read_word_swapped(client, reg);
- dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__,
- data, reg);
- return data;
-}
-
-static int mt9v032_write(struct i2c_client *client, const u8 reg,
- const u16 data)
-{
- dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__,
- data, reg);
- return i2c_smbus_write_word_swapped(client, reg, data);
-}
-
-static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
- u16 value = (mt9v032->chip_control & ~clear) | set;
- int ret;
-
- ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value);
- if (ret < 0)
- return ret;
-
- mt9v032->chip_control = value;
- return 0;
-}
-
-static int
-mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
- u16 value = mt9v032->aec_agc;
- int ret;
-
- if (enable)
- value |= which;
- else
- value &= ~which;
-
- ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value);
- if (ret < 0)
- return ret;
-
- mt9v032->aec_agc = value;
- return 0;
-}
-
-static int mt9v032_power_on(struct mt9v032 *mt9v032)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
- int ret;
-
- if (mt9v032->pdata->set_clock) {
- mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000);
- udelay(1);
- }
-
- /* Reset the chip and stop data read out */
- ret = mt9v032_write(client, MT9V032_RESET, 1);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_RESET, 0);
- if (ret < 0)
- return ret;
-
- return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0);
-}
-
-static void mt9v032_power_off(struct mt9v032 *mt9v032)
-{
- if (mt9v032->pdata->set_clock)
- mt9v032->pdata->set_clock(&mt9v032->subdev, 0);
-}
-
-static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
- int ret;
-
- if (!on) {
- mt9v032_power_off(mt9v032);
- return 0;
- }
-
- ret = mt9v032_power_on(mt9v032);
- if (ret < 0)
- return ret;
-
- /* Configure the pixel clock polarity */
- if (mt9v032->pdata && mt9v032->pdata->clk_pol) {
- ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK,
- MT9V032_PIXEL_CLOCK_INV_PXL_CLK);
- if (ret < 0)
- return ret;
- }
-
- /* Disable the noise correction algorithm and restore the controls. */
- ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0);
- if (ret < 0)
- return ret;
-
- return v4l2_ctrl_handler_setup(&mt9v032->ctrls);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev video operations
- */
-
-static struct v4l2_mbus_framefmt *
-__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_format(fh, pad);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &mt9v032->format;
- default:
- return NULL;
- }
-}
-
-static struct v4l2_rect *
-__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- switch (which) {
- case V4L2_SUBDEV_FORMAT_TRY:
- return v4l2_subdev_get_try_crop(fh, pad);
- case V4L2_SUBDEV_FORMAT_ACTIVE:
- return &mt9v032->crop;
- default:
- return NULL;
- }
-}
-
-static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable)
-{
- const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE
- | MT9V032_CHIP_CONTROL_DOUT_ENABLE
- | MT9V032_CHIP_CONTROL_SEQUENTIAL;
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- struct v4l2_mbus_framefmt *format = &mt9v032->format;
- struct v4l2_rect *crop = &mt9v032->crop;
- unsigned int hratio;
- unsigned int vratio;
- int ret;
-
- if (!enable)
- return mt9v032_set_chip_control(mt9v032, mode, 0);
-
- /* Configure the window size and row/column bin */
- hratio = DIV_ROUND_CLOSEST(crop->width, format->width);
- vratio = DIV_ROUND_CLOSEST(crop->height, format->height);
-
- ret = mt9v032_write(client, MT9V032_READ_MODE,
- (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT |
- (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_ROW_START, crop->top);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height);
- if (ret < 0)
- return ret;
-
- ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING,
- max(43, 660 - crop->width));
- if (ret < 0)
- return ret;
-
- /* Switch to master "normal" mode */
- return mt9v032_set_chip_control(mt9v032, 0, mode);
-}
-
-static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index > 0)
- return -EINVAL;
-
- code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
- return 0;
-}
-
-static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
- return -EINVAL;
-
- fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index;
- fse->max_width = fse->min_width;
- fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index;
- fse->max_height = fse->min_height;
-
- return 0;
-}
-
-static int mt9v032_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *format)
-{
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
-
- format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad,
- format->which);
- return 0;
-}
-
-static int mt9v032_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *format)
-{
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- struct v4l2_mbus_framefmt *__format;
- struct v4l2_rect *__crop;
- unsigned int width;
- unsigned int height;
- unsigned int hratio;
- unsigned int vratio;
-
- __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad,
- format->which);
-
- /* Clamp the width and height to avoid dividing by zero. */
- width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
- max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN),
- __crop->width);
- height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
- max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN),
- __crop->height);
-
- hratio = DIV_ROUND_CLOSEST(__crop->width, width);
- vratio = DIV_ROUND_CLOSEST(__crop->height, height);
-
- __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad,
- format->which);
- __format->width = __crop->width / hratio;
- __format->height = __crop->height / vratio;
-
- format->format = *__format;
-
- return 0;
-}
-
-static int mt9v032_get_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
-
- crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad,
- crop->which);
- return 0;
-}
-
-static int mt9v032_set_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- struct v4l2_mbus_framefmt *__format;
- struct v4l2_rect *__crop;
- struct v4l2_rect rect;
-
- /* Clamp the crop rectangle boundaries and align them to a non multiple
- * of 2 pixels to ensure a GRBG Bayer pattern.
- */
- rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1,
- MT9V032_COLUMN_START_MIN,
- MT9V032_COLUMN_START_MAX);
- rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1,
- MT9V032_ROW_START_MIN,
- MT9V032_ROW_START_MAX);
- rect.width = clamp(ALIGN(crop->rect.width, 2),
- MT9V032_WINDOW_WIDTH_MIN,
- MT9V032_WINDOW_WIDTH_MAX);
- rect.height = clamp(ALIGN(crop->rect.height, 2),
- MT9V032_WINDOW_HEIGHT_MIN,
- MT9V032_WINDOW_HEIGHT_MAX);
-
- rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left);
- rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top);
-
- __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which);
-
- if (rect.width != __crop->width || rect.height != __crop->height) {
- /* Reset the output image size if the crop rectangle size has
- * been modified.
- */
- __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad,
- crop->which);
- __format->width = rect.width;
- __format->height = rect.height;
- }
-
- *__crop = rect;
- crop->rect = rect;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev control operations
- */
-
-#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
-
-static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct mt9v032 *mt9v032 =
- container_of(ctrl->handler, struct mt9v032, ctrls);
- struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev);
- u16 data;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE,
- ctrl->val);
-
- case V4L2_CID_GAIN:
- return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val);
-
- case V4L2_CID_EXPOSURE_AUTO:
- return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE,
- !ctrl->val);
-
- case V4L2_CID_EXPOSURE:
- return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH,
- ctrl->val);
-
- case V4L2_CID_TEST_PATTERN:
- switch (ctrl->val) {
- case 0:
- data = 0;
- break;
- case 1:
- data = MT9V032_TEST_PATTERN_GRAY_VERTICAL
- | MT9V032_TEST_PATTERN_ENABLE;
- break;
- case 2:
- data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL
- | MT9V032_TEST_PATTERN_ENABLE;
- break;
- case 3:
- data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL
- | MT9V032_TEST_PATTERN_ENABLE;
- break;
- default:
- data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT)
- | MT9V032_TEST_PATTERN_USE_DATA
- | MT9V032_TEST_PATTERN_ENABLE
- | MT9V032_TEST_PATTERN_FLIP;
- break;
- }
-
- return mt9v032_write(client, MT9V032_TEST_PATTERN, data);
- }
-
- return 0;
-}
-
-static struct v4l2_ctrl_ops mt9v032_ctrl_ops = {
- .s_ctrl = mt9v032_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config mt9v032_ctrls[] = {
- {
- .ops = &mt9v032_ctrl_ops,
- .id = V4L2_CID_TEST_PATTERN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Test pattern",
- .min = 0,
- .max = 1023,
- .step = 1,
- .def = 0,
- .flags = 0,
- }
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-static int mt9v032_set_power(struct v4l2_subdev *subdev, int on)
-{
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- int ret = 0;
-
- mutex_lock(&mt9v032->power_lock);
-
- /* If the power count is modified from 0 to != 0 or from != 0 to 0,
- * update the power state.
- */
- if (mt9v032->power_count == !on) {
- ret = __mt9v032_set_power(mt9v032, !!on);
- if (ret < 0)
- goto done;
- }
-
- /* Update the power count. */
- mt9v032->power_count += on ? 1 : -1;
- WARN_ON(mt9v032->power_count < 0);
-
-done:
- mutex_unlock(&mt9v032->power_lock);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev internal operations
- */
-
-static int mt9v032_registered(struct v4l2_subdev *subdev)
-{
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
- s32 data;
- int ret;
-
- dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n",
- client->addr);
-
- ret = mt9v032_power_on(mt9v032);
- if (ret < 0) {
- dev_err(&client->dev, "MT9V032 power up failed\n");
- return ret;
- }
-
- /* Read and check the sensor version */
- data = mt9v032_read(client, MT9V032_CHIP_VERSION);
- if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) {
- dev_err(&client->dev, "MT9V032 not detected, wrong version "
- "0x%04x\n", data);
- return -ENODEV;
- }
-
- mt9v032_power_off(mt9v032);
-
- dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n",
- client->addr);
-
- return ret;
-}
-
-static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- crop = v4l2_subdev_get_try_crop(fh, 0);
- crop->left = MT9V032_COLUMN_START_DEF;
- crop->top = MT9V032_ROW_START_DEF;
- crop->width = MT9V032_WINDOW_WIDTH_DEF;
- crop->height = MT9V032_WINDOW_HEIGHT_DEF;
-
- format = v4l2_subdev_get_try_format(fh, 0);
- format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
- format->width = MT9V032_WINDOW_WIDTH_DEF;
- format->height = MT9V032_WINDOW_HEIGHT_DEF;
- format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
-
- return mt9v032_set_power(subdev, 1);
-}
-
-static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
-{
- return mt9v032_set_power(subdev, 0);
-}
-
-static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = {
- .s_power = mt9v032_set_power,
-};
-
-static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = {
- .s_stream = mt9v032_s_stream,
-};
-
-static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = {
- .enum_mbus_code = mt9v032_enum_mbus_code,
- .enum_frame_size = mt9v032_enum_frame_size,
- .get_fmt = mt9v032_get_format,
- .set_fmt = mt9v032_set_format,
- .get_crop = mt9v032_get_crop,
- .set_crop = mt9v032_set_crop,
-};
-
-static struct v4l2_subdev_ops mt9v032_subdev_ops = {
- .core = &mt9v032_subdev_core_ops,
- .video = &mt9v032_subdev_video_ops,
- .pad = &mt9v032_subdev_pad_ops,
-};
-
-static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = {
- .registered = mt9v032_registered,
- .open = mt9v032_open,
- .close = mt9v032_close,
-};
-
-/* -----------------------------------------------------------------------------
- * Driver initialization and probing
- */
-
-static int mt9v032_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct mt9v032 *mt9v032;
- unsigned int i;
- int ret;
-
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_WORD_DATA)) {
- dev_warn(&client->adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
- return -EIO;
- }
-
- mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL);
- if (!mt9v032)
- return -ENOMEM;
-
- mutex_init(&mt9v032->power_lock);
- mt9v032->pdata = client->dev.platform_data;
-
- v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4);
-
- v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN,
- MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF);
- v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0,
- V4L2_EXPOSURE_AUTO);
- v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops,
- V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN,
- MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1,
- MT9V032_TOTAL_SHUTTER_WIDTH_DEF);
-
- for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i)
- v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL);
-
- mt9v032->subdev.ctrl_handler = &mt9v032->ctrls;
-
- if (mt9v032->ctrls.error)
- printk(KERN_INFO "%s: control initialization error %d\n",
- __func__, mt9v032->ctrls.error);
-
- mt9v032->crop.left = MT9V032_COLUMN_START_DEF;
- mt9v032->crop.top = MT9V032_ROW_START_DEF;
- mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF;
- mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF;
-
- mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
- mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF;
- mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF;
- mt9v032->format.field = V4L2_FIELD_NONE;
- mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB;
-
- mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE;
-
- v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops);
- mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops;
- mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0);
- if (ret < 0)
- kfree(mt9v032);
-
- return ret;
-}
-
-static int mt9v032_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct mt9v032 *mt9v032 = to_mt9v032(subdev);
-
- v4l2_device_unregister_subdev(subdev);
- media_entity_cleanup(&subdev->entity);
- kfree(mt9v032);
- return 0;
-}
-
-static const struct i2c_device_id mt9v032_id[] = {
- { "mt9v032", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, mt9v032_id);
-
-static struct i2c_driver mt9v032_driver = {
- .driver = {
- .name = "mt9v032",
- },
- .probe = mt9v032_probe,
- .remove = mt9v032_remove,
- .id_table = mt9v032_id,
-};
-
-module_i2c_driver(mt9v032_driver);
-
-MODULE_DESCRIPTION("Aptina MT9V032 Camera driver");
-MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
deleted file mode 100644
index ac175406e582..000000000000
--- a/drivers/media/video/mx2_camera.c
+++ /dev/null
@@ -1,1884 +0,0 @@
-/*
- * V4L2 Driver for i.MX27/i.MX25 camera host
- *
- * Copyright (C) 2008, Sascha Hauer, Pengutronix
- * Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
- * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the 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/init.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/gcd.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/math64.h>
-#include <linux/mm.h>
-#include <linux/moduleparam.h>
-#include <linux/time.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <linux/clk.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-
-#include <linux/videodev2.h>
-
-#include <mach/mx2_cam.h>
-#include <mach/hardware.h>
-
-#include <asm/dma.h>
-
-#define MX2_CAM_DRV_NAME "mx2-camera"
-#define MX2_CAM_VERSION "0.0.6"
-#define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
-
-/* reset values */
-#define CSICR1_RESET_VAL 0x40000800
-#define CSICR2_RESET_VAL 0x0
-#define CSICR3_RESET_VAL 0x0
-
-/* csi control reg 1 */
-#define CSICR1_SWAP16_EN (1 << 31)
-#define CSICR1_EXT_VSYNC (1 << 30)
-#define CSICR1_EOF_INTEN (1 << 29)
-#define CSICR1_PRP_IF_EN (1 << 28)
-#define CSICR1_CCIR_MODE (1 << 27)
-#define CSICR1_COF_INTEN (1 << 26)
-#define CSICR1_SF_OR_INTEN (1 << 25)
-#define CSICR1_RF_OR_INTEN (1 << 24)
-#define CSICR1_STATFF_LEVEL (3 << 22)
-#define CSICR1_STATFF_INTEN (1 << 21)
-#define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19) /* MX27 */
-#define CSICR1_FB2_DMA_INTEN (1 << 20) /* MX25 */
-#define CSICR1_FB1_DMA_INTEN (1 << 19) /* MX25 */
-#define CSICR1_RXFF_INTEN (1 << 18)
-#define CSICR1_SOF_POL (1 << 17)
-#define CSICR1_SOF_INTEN (1 << 16)
-#define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
-#define CSICR1_HSYNC_POL (1 << 11)
-#define CSICR1_CCIR_EN (1 << 10)
-#define CSICR1_MCLKEN (1 << 9)
-#define CSICR1_FCC (1 << 8)
-#define CSICR1_PACK_DIR (1 << 7)
-#define CSICR1_CLR_STATFIFO (1 << 6)
-#define CSICR1_CLR_RXFIFO (1 << 5)
-#define CSICR1_GCLK_MODE (1 << 4)
-#define CSICR1_INV_DATA (1 << 3)
-#define CSICR1_INV_PCLK (1 << 2)
-#define CSICR1_REDGE (1 << 1)
-#define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
-
-#define SHIFT_STATFF_LEVEL 22
-#define SHIFT_RXFF_LEVEL 19
-#define SHIFT_MCLKDIV 12
-
-/* control reg 3 */
-#define CSICR3_FRMCNT (0xFFFF << 16)
-#define CSICR3_FRMCNT_RST (1 << 15)
-#define CSICR3_DMA_REFLASH_RFF (1 << 14)
-#define CSICR3_DMA_REFLASH_SFF (1 << 13)
-#define CSICR3_DMA_REQ_EN_RFF (1 << 12)
-#define CSICR3_DMA_REQ_EN_SFF (1 << 11)
-#define CSICR3_RXFF_LEVEL(l) (((l) & 7) << 4) /* MX25 */
-#define CSICR3_CSI_SUP (1 << 3)
-#define CSICR3_ZERO_PACK_EN (1 << 2)
-#define CSICR3_ECC_INT_EN (1 << 1)
-#define CSICR3_ECC_AUTO_EN (1 << 0)
-
-#define SHIFT_FRMCNT 16
-
-/* csi status reg */
-#define CSISR_SFF_OR_INT (1 << 25)
-#define CSISR_RFF_OR_INT (1 << 24)
-#define CSISR_STATFF_INT (1 << 21)
-#define CSISR_DMA_TSF_FB2_INT (1 << 20) /* MX25 */
-#define CSISR_DMA_TSF_FB1_INT (1 << 19) /* MX25 */
-#define CSISR_RXFF_INT (1 << 18)
-#define CSISR_EOF_INT (1 << 17)
-#define CSISR_SOF_INT (1 << 16)
-#define CSISR_F2_INT (1 << 15)
-#define CSISR_F1_INT (1 << 14)
-#define CSISR_COF_INT (1 << 13)
-#define CSISR_ECC_INT (1 << 1)
-#define CSISR_DRDY (1 << 0)
-
-#define CSICR1 0x00
-#define CSICR2 0x04
-#define CSISR (cpu_is_mx27() ? 0x08 : 0x18)
-#define CSISTATFIFO 0x0c
-#define CSIRFIFO 0x10
-#define CSIRXCNT 0x14
-#define CSICR3 (cpu_is_mx27() ? 0x1C : 0x08)
-#define CSIDMASA_STATFIFO 0x20
-#define CSIDMATA_STATFIFO 0x24
-#define CSIDMASA_FB1 0x28
-#define CSIDMASA_FB2 0x2c
-#define CSIFBUF_PARA 0x30
-#define CSIIMAG_PARA 0x34
-
-/* EMMA PrP */
-#define PRP_CNTL 0x00
-#define PRP_INTR_CNTL 0x04
-#define PRP_INTRSTATUS 0x08
-#define PRP_SOURCE_Y_PTR 0x0c
-#define PRP_SOURCE_CB_PTR 0x10
-#define PRP_SOURCE_CR_PTR 0x14
-#define PRP_DEST_RGB1_PTR 0x18
-#define PRP_DEST_RGB2_PTR 0x1c
-#define PRP_DEST_Y_PTR 0x20
-#define PRP_DEST_CB_PTR 0x24
-#define PRP_DEST_CR_PTR 0x28
-#define PRP_SRC_FRAME_SIZE 0x2c
-#define PRP_DEST_CH1_LINE_STRIDE 0x30
-#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
-#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
-#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
-#define PRP_CH2_OUT_IMAGE_SIZE 0x40
-#define PRP_SRC_LINE_STRIDE 0x44
-#define PRP_CSC_COEF_012 0x48
-#define PRP_CSC_COEF_345 0x4c
-#define PRP_CSC_COEF_678 0x50
-#define PRP_CH1_RZ_HORI_COEF1 0x54
-#define PRP_CH1_RZ_HORI_COEF2 0x58
-#define PRP_CH1_RZ_HORI_VALID 0x5c
-#define PRP_CH1_RZ_VERT_COEF1 0x60
-#define PRP_CH1_RZ_VERT_COEF2 0x64
-#define PRP_CH1_RZ_VERT_VALID 0x68
-#define PRP_CH2_RZ_HORI_COEF1 0x6c
-#define PRP_CH2_RZ_HORI_COEF2 0x70
-#define PRP_CH2_RZ_HORI_VALID 0x74
-#define PRP_CH2_RZ_VERT_COEF1 0x78
-#define PRP_CH2_RZ_VERT_COEF2 0x7c
-#define PRP_CH2_RZ_VERT_VALID 0x80
-
-#define PRP_CNTL_CH1EN (1 << 0)
-#define PRP_CNTL_CH2EN (1 << 1)
-#define PRP_CNTL_CSIEN (1 << 2)
-#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
-#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
-#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
-#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
-#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
-#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
-#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
-#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
-#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
-#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
-#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
-#define PRP_CNTL_CH1_LEN (1 << 9)
-#define PRP_CNTL_CH2_LEN (1 << 10)
-#define PRP_CNTL_SKIP_FRAME (1 << 11)
-#define PRP_CNTL_SWRST (1 << 12)
-#define PRP_CNTL_CLKEN (1 << 13)
-#define PRP_CNTL_WEN (1 << 14)
-#define PRP_CNTL_CH1BYP (1 << 15)
-#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
-#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
-#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
-#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
-#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
-#define PRP_CNTL_CH2B1EN (1 << 29)
-#define PRP_CNTL_CH2B2EN (1 << 30)
-#define PRP_CNTL_CH2FEN (1 << 31)
-
-/* IRQ Enable and status register */
-#define PRP_INTR_RDERR (1 << 0)
-#define PRP_INTR_CH1WERR (1 << 1)
-#define PRP_INTR_CH2WERR (1 << 2)
-#define PRP_INTR_CH1FC (1 << 3)
-#define PRP_INTR_CH2FC (1 << 5)
-#define PRP_INTR_LBOVF (1 << 7)
-#define PRP_INTR_CH2OVF (1 << 8)
-
-/* Resizing registers */
-#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
-#define PRP_RZ_VALID_BILINEAR (1 << 31)
-
-#define MAX_VIDEO_MEM 16
-
-#define RESIZE_NUM_MIN 1
-#define RESIZE_NUM_MAX 20
-#define BC_COEF 3
-#define SZ_COEF (1 << BC_COEF)
-
-#define RESIZE_DIR_H 0
-#define RESIZE_DIR_V 1
-
-#define RESIZE_ALGO_BILINEAR 0
-#define RESIZE_ALGO_AVERAGING 1
-
-struct mx2_prp_cfg {
- int channel;
- u32 in_fmt;
- u32 out_fmt;
- u32 src_pixel;
- u32 ch1_pixel;
- u32 irq_flags;
- u32 csicr1;
-};
-
-/* prp resizing parameters */
-struct emma_prp_resize {
- int algo; /* type of algorithm used */
- int len; /* number of coefficients */
- unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
-};
-
-/* prp configuration for a client-host fmt pair */
-struct mx2_fmt_cfg {
- enum v4l2_mbus_pixelcode in_fmt;
- u32 out_fmt;
- struct mx2_prp_cfg cfg;
-};
-
-enum mx2_buffer_state {
- MX2_STATE_QUEUED,
- MX2_STATE_ACTIVE,
- MX2_STATE_DONE,
-};
-
-struct mx2_buf_internal {
- struct list_head queue;
- int bufnum;
- bool discard;
-};
-
-/* buffer for one video frame */
-struct mx2_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_buffer vb;
- enum mx2_buffer_state state;
- struct mx2_buf_internal internal;
-};
-
-struct mx2_camera_dev {
- struct device *dev;
- struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
- struct clk *clk_csi, *clk_emma_ahb, *clk_emma_ipg;
-
- unsigned int irq_csi, irq_emma;
- void __iomem *base_csi, *base_emma;
- unsigned long base_dma;
-
- struct mx2_camera_platform_data *pdata;
- struct resource *res_csi, *res_emma;
- unsigned long platform_flags;
-
- struct list_head capture;
- struct list_head active_bufs;
- struct list_head discard;
-
- spinlock_t lock;
-
- int dma;
- struct mx2_buffer *active;
- struct mx2_buffer *fb1_active;
- struct mx2_buffer *fb2_active;
-
- u32 csicr1;
-
- struct mx2_buf_internal buf_discard[2];
- void *discard_buffer;
- dma_addr_t discard_buffer_dma;
- size_t discard_size;
- struct mx2_fmt_cfg *emma_prp;
- struct emma_prp_resize resizing[2];
- unsigned int s_width, s_height;
- u32 frame_count;
- struct vb2_alloc_ctx *alloc_ctx;
-};
-
-static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
-{
- return container_of(int_buf, struct mx2_buffer, internal);
-}
-
-static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
- /*
- * This is a generic configuration which is valid for most
- * prp input-output format combinations.
- * We set the incomming and outgoing pixelformat to a
- * 16 Bit wide format and adjust the bytesperline
- * accordingly. With this configuration the inputdata
- * will not be changed by the emma and could be any type
- * of 16 Bit Pixelformat.
- */
- {
- .in_fmt = 0,
- .out_fmt = 0,
- .cfg = {
- .channel = 1,
- .in_fmt = PRP_CNTL_DATA_IN_RGB16,
- .out_fmt = PRP_CNTL_CH1_OUT_RGB16,
- .src_pixel = 0x2ca00565, /* RGB565 */
- .ch1_pixel = 0x2ca00565, /* RGB565 */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
- PRP_INTR_CH1FC | PRP_INTR_LBOVF,
- .csicr1 = 0,
- }
- },
- {
- .in_fmt = V4L2_MBUS_FMT_YUYV8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUV420,
- .cfg = {
- .channel = 2,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
- PRP_INTR_CH2FC | PRP_INTR_LBOVF |
- PRP_INTR_CH2OVF,
- .csicr1 = CSICR1_PACK_DIR,
- }
- },
- {
- .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
- .out_fmt = V4L2_PIX_FMT_YUV420,
- .cfg = {
- .channel = 2,
- .in_fmt = PRP_CNTL_DATA_IN_YUV422,
- .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
- .src_pixel = 0x22000888, /* YUV422 (YUYV) */
- .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
- PRP_INTR_CH2FC | PRP_INTR_LBOVF |
- PRP_INTR_CH2OVF,
- .csicr1 = CSICR1_SWAP16_EN,
- }
- },
-};
-
-static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
- enum v4l2_mbus_pixelcode in_fmt,
- u32 out_fmt)
-{
- int i;
-
- for (i = 1; i < ARRAY_SIZE(mx27_emma_prp_table); i++)
- if ((mx27_emma_prp_table[i].in_fmt == in_fmt) &&
- (mx27_emma_prp_table[i].out_fmt == out_fmt)) {
- return &mx27_emma_prp_table[i];
- }
- /* If no match return the most generic configuration */
- return &mx27_emma_prp_table[0];
-};
-
-static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
- unsigned long phys, int bufnum)
-{
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-
- if (prp->cfg.channel == 1) {
- writel(phys, pcdev->base_emma +
- PRP_DEST_RGB1_PTR + 4 * bufnum);
- } else {
- writel(phys, pcdev->base_emma +
- PRP_DEST_Y_PTR - 0x14 * bufnum);
- if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
- u32 imgsize = pcdev->icd->user_height *
- pcdev->icd->user_width;
-
- writel(phys + imgsize, pcdev->base_emma +
- PRP_DEST_CB_PTR - 0x14 * bufnum);
- writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
- PRP_DEST_CR_PTR - 0x14 * bufnum);
- }
- }
-}
-
-static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
-{
- unsigned long flags;
-
- clk_disable_unprepare(pcdev->clk_csi);
- writel(0, pcdev->base_csi + CSICR1);
- if (cpu_is_mx27()) {
- writel(0, pcdev->base_emma + PRP_CNTL);
- } else if (cpu_is_mx25()) {
- spin_lock_irqsave(&pcdev->lock, flags);
- pcdev->fb1_active = NULL;
- pcdev->fb2_active = NULL;
- writel(0, pcdev->base_csi + CSIDMASA_FB1);
- writel(0, pcdev->base_csi + CSIDMASA_FB2);
- spin_unlock_irqrestore(&pcdev->lock, flags);
- }
-}
-
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on mx2 camera sensor interface
- */
-static int mx2_camera_add_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- int ret;
- u32 csicr1;
-
- if (pcdev->icd)
- return -EBUSY;
-
- ret = clk_prepare_enable(pcdev->clk_csi);
- if (ret < 0)
- return ret;
-
- csicr1 = CSICR1_MCLKEN;
-
- if (cpu_is_mx27()) {
- csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
- CSICR1_RXFF_LEVEL(0);
- } else if (cpu_is_mx27())
- csicr1 |= CSICR1_SOF_INTEN | CSICR1_RXFF_LEVEL(2);
-
- pcdev->csicr1 = csicr1;
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- pcdev->icd = icd;
- pcdev->frame_count = 0;
-
- dev_info(icd->parent, "Camera driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-static void mx2_camera_remove_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
-
- BUG_ON(icd != pcdev->icd);
-
- dev_info(icd->parent, "Camera driver detached from camera %d\n",
- icd->devnum);
-
- mx2_camera_deactivate(pcdev);
-
- pcdev->icd = NULL;
-}
-
-static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
- int state)
-{
- struct vb2_buffer *vb;
- struct mx2_buffer *buf;
- struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
- &pcdev->fb2_active;
- u32 fb_reg = fb == 1 ? CSIDMASA_FB1 : CSIDMASA_FB2;
- unsigned long flags;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (*fb_active == NULL)
- goto out;
-
- vb = &(*fb_active)->vb;
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- do_gettimeofday(&vb->v4l2_buf.timestamp);
- vb->v4l2_buf.sequence++;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
-
- if (list_empty(&pcdev->capture)) {
- buf = NULL;
- writel(0, pcdev->base_csi + fb_reg);
- } else {
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- vb = &buf->vb;
- list_del(&buf->internal.queue);
- buf->state = MX2_STATE_ACTIVE;
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + fb_reg);
- }
-
- *fb_active = buf;
-
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
- u32 status = readl(pcdev->base_csi + CSISR);
-
- if (status & CSISR_DMA_TSF_FB1_INT)
- mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
- else if (status & CSISR_DMA_TSF_FB2_INT)
- mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
-
- /* FIXME: handle CSISR_RFF_OR_INT */
-
- writel(status, pcdev->base_csi + CSISR);
-
- return IRQ_HANDLED;
-}
-
-/*
- * Videobuf operations
- */
-static int mx2_videobuf_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
-
- dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
-
- /* TODO: support for VIDIOC_CREATE_BUFS not ready */
- if (fmt != NULL)
- return -ENOTTY;
-
- alloc_ctxs[0] = pcdev->alloc_ctx;
-
- sizes[0] = icd->sizeimage;
-
- if (0 == *count)
- *count = 32;
- if (!*num_planes &&
- sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
-
- *num_planes = 1;
-
- return 0;
-}
-
-static int mx2_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- int ret = 0;
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
-#ifdef DEBUG
- /*
- * This can be useful if you want to see if we actually fill
- * the buffer with something
- */
- memset((void *)vb2_plane_vaddr(vb, 0),
- 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- vb2_set_plane_payload(vb, 0, icd->sizeimage);
- if (vb2_plane_vaddr(vb, 0) &&
- vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
- ret = -EINVAL;
- goto out;
- }
-
- return 0;
-
-out:
- return ret;
-}
-
-static void mx2_videobuf_queue(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
- unsigned long flags;
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- buf->state = MX2_STATE_QUEUED;
- list_add_tail(&buf->internal.queue, &pcdev->capture);
-
- if (cpu_is_mx25()) {
- u32 csicr3, dma_inten = 0;
-
- if (pcdev->fb1_active == NULL) {
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + CSIDMASA_FB1);
- pcdev->fb1_active = buf;
- dma_inten = CSICR1_FB1_DMA_INTEN;
- } else if (pcdev->fb2_active == NULL) {
- writel(vb2_dma_contig_plane_dma_addr(vb, 0),
- pcdev->base_csi + CSIDMASA_FB2);
- pcdev->fb2_active = buf;
- dma_inten = CSICR1_FB2_DMA_INTEN;
- }
-
- if (dma_inten) {
- list_del(&buf->internal.queue);
- buf->state = MX2_STATE_ACTIVE;
-
- csicr3 = readl(pcdev->base_csi + CSICR3);
-
- /* Reflash DMA */
- writel(csicr3 | CSICR3_DMA_REFLASH_RFF,
- pcdev->base_csi + CSICR3);
-
- /* clear & enable interrupts */
- writel(dma_inten, pcdev->base_csi + CSISR);
- pcdev->csicr1 |= dma_inten;
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- /* enable DMA */
- csicr3 |= CSICR3_DMA_REQ_EN_RFF | CSICR3_RXFF_LEVEL(1);
- writel(csicr3, pcdev->base_csi + CSICR3);
- }
- }
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static void mx2_videobuf_release(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
- unsigned long flags;
-
-#ifdef DEBUG
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
- switch (buf->state) {
- case MX2_STATE_ACTIVE:
- dev_info(icd->parent, "%s (active)\n", __func__);
- break;
- case MX2_STATE_QUEUED:
- dev_info(icd->parent, "%s (queued)\n", __func__);
- break;
- default:
- dev_info(icd->parent, "%s (unknown) %d\n", __func__,
- buf->state);
- break;
- }
-#endif
-
- /*
- * Terminate only queued but inactive buffers. Active buffers are
- * released when they become inactive after videobuf_waiton().
- *
- * FIXME: implement forced termination of active buffers for mx27 and
- * mx27 eMMA, so that the user won't get stuck in an uninterruptible
- * state. This requires a specific handling for each of the these DMA
- * types.
- */
-
- spin_lock_irqsave(&pcdev->lock, flags);
- if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
- if (pcdev->fb1_active == buf) {
- pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
- writel(0, pcdev->base_csi + CSIDMASA_FB1);
- pcdev->fb1_active = NULL;
- } else if (pcdev->fb2_active == buf) {
- pcdev->csicr1 &= ~CSICR1_FB2_DMA_INTEN;
- writel(0, pcdev->base_csi + CSIDMASA_FB2);
- pcdev->fb2_active = NULL;
- }
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
- }
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
- int bytesperline)
-{
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-
- writel((pcdev->s_width << 16) | pcdev->s_height,
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
- writel(prp->cfg.src_pixel,
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
- if (prp->cfg.channel == 1) {
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
- writel(bytesperline,
- pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
- writel(prp->cfg.ch1_pixel,
- pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
- } else { /* channel 2 */
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
- }
-
- /* Enable interrupts */
- writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
-}
-
-static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
-{
- int dir;
-
- for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
- unsigned char *s = pcdev->resizing[dir].s;
- int len = pcdev->resizing[dir].len;
- unsigned int coeff[2] = {0, 0};
- unsigned int valid = 0;
- int i;
-
- if (len == 0)
- continue;
-
- for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
- int j;
-
- j = i > 9 ? 1 : 0;
- coeff[j] = (coeff[j] << BC_COEF) |
- (s[i] & (SZ_COEF - 1));
-
- if (i == 5 || i == 15)
- coeff[j] <<= 1;
-
- valid = (valid << 1) | (s[i] >> BC_COEF);
- }
-
- valid |= PRP_RZ_VALID_TBL_LEN(len);
-
- if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
- valid |= PRP_RZ_VALID_BILINEAR;
-
- if (pcdev->emma_prp->cfg.channel == 1) {
- if (dir == RESIZE_DIR_H) {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH1_RZ_HORI_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH1_RZ_HORI_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH1_RZ_HORI_VALID);
- } else {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH1_RZ_VERT_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH1_RZ_VERT_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH1_RZ_VERT_VALID);
- }
- } else {
- if (dir == RESIZE_DIR_H) {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH2_RZ_HORI_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH2_RZ_HORI_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH2_RZ_HORI_VALID);
- } else {
- writel(coeff[0], pcdev->base_emma +
- PRP_CH2_RZ_VERT_COEF1);
- writel(coeff[1], pcdev->base_emma +
- PRP_CH2_RZ_VERT_COEF2);
- writel(valid, pcdev->base_emma +
- PRP_CH2_RZ_VERT_VALID);
- }
- }
- }
-}
-
-static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- struct vb2_buffer *vb;
- struct mx2_buffer *buf;
- unsigned long phys;
- int bytesperline;
-
- if (cpu_is_mx27()) {
- unsigned long flags;
- if (count < 2)
- return -EINVAL;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 0;
- vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
- buf->internal.bufnum = 1;
- vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- bytesperline = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytesperline < 0)
- return bytesperline;
-
- /*
- * I didn't manage to properly enable/disable the prp
- * on a per frame basis during running transfers,
- * thus we allocate a buffer here and use it to
- * discard frames when no buffer is available.
- * Feel free to work on this ;)
- */
- pcdev->discard_size = icd->user_height * bytesperline;
- pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, &pcdev->discard_buffer_dma,
- GFP_KERNEL);
- if (!pcdev->discard_buffer)
- return -ENOMEM;
-
- pcdev->buf_discard[0].discard = true;
- list_add_tail(&pcdev->buf_discard[0].queue,
- &pcdev->discard);
-
- pcdev->buf_discard[1].discard = true;
- list_add_tail(&pcdev->buf_discard[1].queue,
- &pcdev->discard);
-
- mx2_prp_resize_commit(pcdev);
-
- mx27_camera_emma_buf_init(icd, bytesperline);
-
- if (prp->cfg.channel == 1) {
- writel(PRP_CNTL_CH1EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH1_LEN |
- PRP_CNTL_CH1BYP |
- PRP_CNTL_CH1_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(PRP_CNTL_CH2EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH2_LEN |
- PRP_CNTL_CH2_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
- }
- spin_unlock_irqrestore(&pcdev->lock, flags);
- }
-
- return 0;
-}
-
-static int mx2_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici =
- to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- unsigned long flags;
- void *b;
- u32 cntl;
-
- if (cpu_is_mx27()) {
- spin_lock_irqsave(&pcdev->lock, flags);
-
- cntl = readl(pcdev->base_emma + PRP_CNTL);
- if (prp->cfg.channel == 1) {
- writel(cntl & ~PRP_CNTL_CH1EN,
- pcdev->base_emma + PRP_CNTL);
- } else {
- writel(cntl & ~PRP_CNTL_CH2EN,
- pcdev->base_emma + PRP_CNTL);
- }
- INIT_LIST_HEAD(&pcdev->capture);
- INIT_LIST_HEAD(&pcdev->active_bufs);
- INIT_LIST_HEAD(&pcdev->discard);
-
- b = pcdev->discard_buffer;
- pcdev->discard_buffer = NULL;
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
-
- dma_free_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, b, pcdev->discard_buffer_dma);
- }
-
- return 0;
-}
-
-static struct vb2_ops mx2_videobuf_ops = {
- .queue_setup = mx2_videobuf_setup,
- .buf_prepare = mx2_videobuf_prepare,
- .buf_queue = mx2_videobuf_queue,
- .buf_cleanup = mx2_videobuf_release,
- .start_streaming = mx2_start_streaming,
- .stop_streaming = mx2_stop_streaming,
-};
-
-static int mx2_camera_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->drv_priv = icd;
- q->ops = &mx2_videobuf_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct mx2_buffer);
-
- return vb2_queue_init(q);
-}
-
-#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_VSYNC_ACTIVE_LOW | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_HSYNC_ACTIVE_LOW | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | \
- V4L2_MBUS_PCLK_SAMPLE_FALLING | \
- V4L2_MBUS_DATA_ACTIVE_HIGH | \
- V4L2_MBUS_DATA_ACTIVE_LOW)
-
-static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
-{
- u32 cntl;
- int count = 0;
-
- cntl = readl(pcdev->base_emma + PRP_CNTL);
- writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
- while (count++ < 100) {
- if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
- return 0;
- barrier();
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long common_flags;
- int ret;
- int bytesperline;
- u32 csicr1 = pcdev->csicr1;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg, MX2_BUS_FLAGS);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%x\n",
- cfg.flags, MX2_BUS_FLAGS);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = MX2_BUS_FLAGS;
- }
-
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & MX2_CAMERA_HSYNC_HIGH)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- }
-
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (pcdev->platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(icd->parent, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- csicr1 = (csicr1 & ~CSICR1_FMT_MASK) | pcdev->emma_prp->cfg.csicr1;
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- csicr1 |= CSICR1_REDGE;
- if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
- csicr1 |= CSICR1_SOF_POL;
- if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
- csicr1 |= CSICR1_HSYNC_POL;
- if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
- csicr1 |= CSICR1_EXT_VSYNC;
- if (pcdev->platform_flags & MX2_CAMERA_CCIR)
- csicr1 |= CSICR1_CCIR_EN;
- if (pcdev->platform_flags & MX2_CAMERA_CCIR_INTERLACE)
- csicr1 |= CSICR1_CCIR_MODE;
- if (pcdev->platform_flags & MX2_CAMERA_GATED_CLOCK)
- csicr1 |= CSICR1_GCLK_MODE;
- if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
- csicr1 |= CSICR1_INV_DATA;
-
- pcdev->csicr1 = csicr1;
-
- bytesperline = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytesperline < 0)
- return bytesperline;
-
- if (cpu_is_mx27()) {
- ret = mx27_camera_emma_prp_reset(pcdev);
- if (ret)
- return ret;
- } else if (cpu_is_mx25()) {
- writel((bytesperline * icd->user_height) >> 2,
- pcdev->base_csi + CSIRXCNT);
- writel((bytesperline << 16) | icd->user_height,
- pcdev->base_csi + CSIIMAG_PARA);
- }
-
- writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
-
- return 0;
-}
-
-static int mx2_camera_set_crop(struct soc_camera_device *icd,
- struct v4l2_crop *a)
-{
- struct v4l2_rect *rect = &a->c;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
- soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
-
- ret = v4l2_subdev_call(sd, video, s_crop, a);
- if (ret < 0)
- return ret;
-
- /* The capture device might have changed its output */
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
- mf.width, mf.height);
-
- icd->user_width = mf.width;
- icd->user_height = mf.height;
-
- return ret;
-}
-
-static int mx2_camera_get_formats(struct soc_camera_device *icd,
- unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_mbus_pixelfmt *fmt;
- struct device *dev = icd->parent;
- enum v4l2_mbus_pixelcode code;
- int ret, formats = 0;
-
- ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
- if (ret < 0)
- /* no more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code);
- if (!fmt) {
- dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
- return 0;
- }
-
- if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
- code == V4L2_MBUS_FMT_UYVY8_2X8) {
- formats++;
- if (xlate) {
- /*
- * CH2 can output YUV420 which is a standard format in
- * soc_mediabus.c
- */
- xlate->host_fmt =
- soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_1_5X8);
- xlate->code = code;
- dev_dbg(dev, "Providing host format %s for sensor code %d\n",
- xlate->host_fmt->name, code);
- xlate++;
- }
- }
-
- /* Generic pass-trough */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code;
- xlate++;
- }
- return formats;
-}
-
-static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
- struct v4l2_mbus_framefmt *mf_in,
- struct v4l2_pix_format *pix_out, bool apply)
-{
- int num, den;
- unsigned long m;
- int i, dir;
-
- for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
- struct emma_prp_resize tmprsz;
- unsigned char *s = tmprsz.s;
- int len = 0;
- int in, out;
-
- if (dir == RESIZE_DIR_H) {
- in = mf_in->width;
- out = pix_out->width;
- } else {
- in = mf_in->height;
- out = pix_out->height;
- }
-
- if (in < out)
- return -EINVAL;
- else if (in == out)
- continue;
-
- /* Calculate ratio */
- m = gcd(in, out);
- num = in / m;
- den = out / m;
- if (num > RESIZE_NUM_MAX)
- return -EINVAL;
-
- if ((num >= 2 * den) && (den == 1) &&
- (num < 9) && (!(num & 0x01))) {
- int sum = 0;
- int j;
-
- /* Average scaling for >= 2:1 ratios */
- /* Support can be added for num >=9 and odd values */
-
- tmprsz.algo = RESIZE_ALGO_AVERAGING;
- len = num;
-
- for (i = 0; i < (len / 2); i++)
- s[i] = 8;
-
- do {
- for (i = 0; i < (len / 2); i++) {
- s[i] = s[i] >> 1;
- sum = 0;
- for (j = 0; j < (len / 2); j++)
- sum += s[j];
- if (sum == 4)
- break;
- }
- } while (sum != 4);
-
- for (i = (len / 2); i < len; i++)
- s[i] = s[len - i - 1];
-
- s[len - 1] |= SZ_COEF;
- } else {
- /* bilinear scaling for < 2:1 ratios */
- int v; /* overflow counter */
- int coeff, nxt; /* table output */
- int in_pos_inc = 2 * den;
- int out_pos = num;
- int out_pos_inc = 2 * num;
- int init_carry = num - den;
- int carry = init_carry;
-
- tmprsz.algo = RESIZE_ALGO_BILINEAR;
- v = den + in_pos_inc;
- do {
- coeff = v - out_pos;
- out_pos += out_pos_inc;
- carry += out_pos_inc;
- for (nxt = 0; v < out_pos; nxt++) {
- v += in_pos_inc;
- carry -= in_pos_inc;
- }
-
- if (len > RESIZE_NUM_MAX)
- return -EINVAL;
-
- coeff = ((coeff << BC_COEF) +
- (in_pos_inc >> 1)) / in_pos_inc;
-
- if (coeff >= (SZ_COEF - 1))
- coeff--;
-
- coeff |= SZ_COEF;
- s[len] = (unsigned char)coeff;
- len++;
-
- for (i = 1; i < nxt; i++) {
- if (len >= RESIZE_NUM_MAX)
- return -EINVAL;
- s[len] = 0;
- len++;
- }
- } while (carry != init_carry);
- }
- tmprsz.len = len;
- if (dir == RESIZE_DIR_H)
- mf_in->width = pix_out->width;
- else
- mf_in->height = pix_out->height;
-
- if (apply)
- memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
- }
- return 0;
-}
-
-static int mx2_camera_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* Store width and height returned by the sensor for resizing */
- pcdev->s_width = mf.width;
- pcdev->s_height = mf.height;
- dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
- __func__, pcdev->s_width, pcdev->s_height);
-
- pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
-
- memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
- if ((mf.width != pix->width || mf.height != pix->height) &&
- pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
- if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
- dev_dbg(icd->parent, "%s: can't resize\n", __func__);
- }
-
- if (mf.code != xlate->code)
- return -EINVAL;
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
- icd->current_fmt = xlate;
-
- dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- return 0;
-}
-
-static int mx2_camera_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- __u32 pixfmt = pix->pixelformat;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx2_camera_dev *pcdev = ici->priv;
- unsigned int width_limit;
- int ret;
-
- dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (pixfmt && !xlate) {
- dev_warn(icd->parent, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /* FIXME: implement MX27 limits */
-
- /* limit to MX25 hardware capabilities */
- if (cpu_is_mx25()) {
- if (xlate->host_fmt->bits_per_sample <= 8)
- width_limit = 0xffff * 4;
- else
- width_limit = 0xffff * 2;
- /* CSIIMAG_PARA limit */
- if (pix->width > width_limit)
- pix->width = width_limit;
- if (pix->height > 0xffff)
- pix->height = 0xffff;
-
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (pix->bytesperline < 0)
- return pix->bytesperline;
- pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
- pix->bytesperline, pix->height);
- /* Check against the CSIRXCNT limit */
- if (pix->sizeimage > 4 * 0x3ffff) {
- /* Adjust geometry, preserve aspect ratio */
- unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
- 4 * pix->height, pix->bytesperline));
- pix->width = new_height * pix->width / pix->height;
- pix->height = new_height;
- pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- BUG_ON(pix->bytesperline < 0);
- pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
- pix->bytesperline, pix->height);
- }
- }
-
- /* limit to sensor capabilities */
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
- __func__, pcdev->s_width, pcdev->s_height);
-
- /* If the sensor does not support image size try PrP resizing */
- pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
-
- memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
- if ((mf.width != pix->width || mf.height != pix->height) &&
- pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
- if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
- dev_dbg(icd->parent, "%s: can't resize\n", __func__);
- }
-
- if (mf.field == V4L2_FIELD_ANY)
- mf.field = V4L2_FIELD_NONE;
- /*
- * Driver supports interlaced images provided they have
- * both fields so that they can be processed as if they
- * were progressive.
- */
- if (mf.field != V4L2_FIELD_NONE && !V4L2_FIELD_HAS_BOTH(mf.field)) {
- dev_err(icd->parent, "Field type %d unsupported.\n",
- mf.field);
- return -EINVAL;
- }
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
-
- dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
- __func__, pix->width, pix->height);
-
- return 0;
-}
-
-static int mx2_camera_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the friendly caller:-> */
- strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = mx2_camera_add_device,
- .remove = mx2_camera_remove_device,
- .set_fmt = mx2_camera_set_fmt,
- .set_crop = mx2_camera_set_crop,
- .get_formats = mx2_camera_get_formats,
- .try_fmt = mx2_camera_try_fmt,
- .init_videobuf2 = mx2_camera_init_videobuf,
- .poll = mx2_camera_poll,
- .querycap = mx2_camera_querycap,
- .set_bus_param = mx2_camera_set_bus_param,
-};
-
-static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
- int bufnum, bool err)
-{
-#ifdef DEBUG
- struct mx2_fmt_cfg *prp = pcdev->emma_prp;
-#endif
- struct mx2_buf_internal *ibuf;
- struct mx2_buffer *buf;
- struct vb2_buffer *vb;
- unsigned long phys;
-
- ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
- queue);
-
- BUG_ON(ibuf->bufnum != bufnum);
-
- if (ibuf->discard) {
- /*
- * Discard buffer must not be returned to user space.
- * Just return it to the discard queue.
- */
- list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
- } else {
- buf = mx2_ibuf_to_buf(ibuf);
-
- vb = &buf->vb;
-#ifdef DEBUG
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- if (prp->cfg.channel == 1) {
- if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
- 4 * bufnum) != phys) {
- dev_err(pcdev->dev, "%lx != %x\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_RGB1_PTR + 4 * bufnum));
- }
- } else {
- if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
- 0x14 * bufnum) != phys) {
- dev_err(pcdev->dev, "%lx != %x\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_Y_PTR - 0x14 * bufnum));
- }
- }
-#endif
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
- vb2_plane_vaddr(vb, 0),
- vb2_get_plane_payload(vb, 0));
-
- list_del_init(&buf->internal.queue);
- do_gettimeofday(&vb->v4l2_buf.timestamp);
- vb->v4l2_buf.sequence = pcdev->frame_count;
- if (err)
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- else
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- }
-
- pcdev->frame_count++;
-
- if (list_empty(&pcdev->capture)) {
- if (list_empty(&pcdev->discard)) {
- dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
- __func__);
- return;
- }
-
- ibuf = list_first_entry(&pcdev->discard,
- struct mx2_buf_internal, queue);
- ibuf->bufnum = bufnum;
-
- list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
- mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
- return;
- }
-
- buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
- internal.queue);
-
- buf->internal.bufnum = bufnum;
-
- list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
-
- vb = &buf->vb;
- buf->state = MX2_STATE_ACTIVE;
-
- phys = vb2_dma_contig_plane_dma_addr(vb, 0);
- mx27_update_emma_buf(pcdev, phys, bufnum);
-}
-
-static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
- unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
- struct mx2_buf_internal *ibuf;
-
- spin_lock(&pcdev->lock);
-
- if (list_empty(&pcdev->active_bufs)) {
- dev_warn(pcdev->dev, "%s: called while active list is empty\n",
- __func__);
-
- if (!status) {
- spin_unlock(&pcdev->lock);
- return IRQ_NONE;
- }
- }
-
- if (status & (1 << 7)) { /* overflow */
- u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
- writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
- pcdev->base_emma + PRP_CNTL);
- writel(cntl, pcdev->base_emma + PRP_CNTL);
-
- ibuf = list_first_entry(&pcdev->active_bufs,
- struct mx2_buf_internal, queue);
- mx27_camera_frame_done_emma(pcdev,
- ibuf->bufnum, true);
-
- status &= ~(1 << 7);
- } else if (((status & (3 << 5)) == (3 << 5)) ||
- ((status & (3 << 3)) == (3 << 3))) {
- /*
- * Both buffers have triggered, process the one we're expecting
- * to first
- */
- ibuf = list_first_entry(&pcdev->active_bufs,
- struct mx2_buf_internal, queue);
- mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
- status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
- } else if ((status & (1 << 6)) || (status & (1 << 4))) {
- mx27_camera_frame_done_emma(pcdev, 0, false);
- } else if ((status & (1 << 5)) || (status & (1 << 3))) {
- mx27_camera_frame_done_emma(pcdev, 1, false);
- }
-
- spin_unlock(&pcdev->lock);
- writel(status, pcdev->base_emma + PRP_INTRSTATUS);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit mx27_camera_emma_init(struct mx2_camera_dev *pcdev)
-{
- struct resource *res_emma = pcdev->res_emma;
- int err = 0;
-
- if (!request_mem_region(res_emma->start, resource_size(res_emma),
- MX2_CAM_DRV_NAME)) {
- err = -EBUSY;
- goto out;
- }
-
- pcdev->base_emma = ioremap(res_emma->start, resource_size(res_emma));
- if (!pcdev->base_emma) {
- err = -ENOMEM;
- goto exit_release;
- }
-
- err = request_irq(pcdev->irq_emma, mx27_camera_emma_irq, 0,
- MX2_CAM_DRV_NAME, pcdev);
- if (err) {
- dev_err(pcdev->dev, "Camera EMMA interrupt register failed \n");
- goto exit_iounmap;
- }
-
- pcdev->clk_emma_ipg = clk_get(pcdev->dev, "emma-ipg");
- if (IS_ERR(pcdev->clk_emma_ipg)) {
- err = PTR_ERR(pcdev->clk_emma_ipg);
- goto exit_free_irq;
- }
-
- clk_prepare_enable(pcdev->clk_emma_ipg);
-
- pcdev->clk_emma_ahb = clk_get(pcdev->dev, "emma-ahb");
- if (IS_ERR(pcdev->clk_emma_ahb)) {
- err = PTR_ERR(pcdev->clk_emma_ahb);
- goto exit_clk_emma_ipg_put;
- }
-
- clk_prepare_enable(pcdev->clk_emma_ahb);
-
- err = mx27_camera_emma_prp_reset(pcdev);
- if (err)
- goto exit_clk_emma_ahb_put;
-
- return err;
-
-exit_clk_emma_ahb_put:
- clk_disable_unprepare(pcdev->clk_emma_ahb);
- clk_put(pcdev->clk_emma_ahb);
-exit_clk_emma_ipg_put:
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_put(pcdev->clk_emma_ipg);
-exit_free_irq:
- free_irq(pcdev->irq_emma, pcdev);
-exit_iounmap:
- iounmap(pcdev->base_emma);
-exit_release:
- release_mem_region(res_emma->start, resource_size(res_emma));
-out:
- return err;
-}
-
-static int __devinit mx2_camera_probe(struct platform_device *pdev)
-{
- struct mx2_camera_dev *pcdev;
- struct resource *res_csi, *res_emma;
- void __iomem *base_csi;
- int irq_csi, irq_emma;
- int err = 0;
-
- dev_dbg(&pdev->dev, "initialising\n");
-
- res_csi = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq_csi = platform_get_irq(pdev, 0);
- if (res_csi == NULL || irq_csi < 0) {
- dev_err(&pdev->dev, "Missing platform resources data\n");
- err = -ENODEV;
- goto exit;
- }
-
- pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit;
- }
-
- pcdev->clk_csi = clk_get(&pdev->dev, "ahb");
- if (IS_ERR(pcdev->clk_csi)) {
- dev_err(&pdev->dev, "Could not get csi clock\n");
- err = PTR_ERR(pcdev->clk_csi);
- goto exit_kfree;
- }
-
- pcdev->res_csi = res_csi;
- pcdev->pdata = pdev->dev.platform_data;
- if (pcdev->pdata) {
- long rate;
-
- pcdev->platform_flags = pcdev->pdata->flags;
-
- rate = clk_round_rate(pcdev->clk_csi, pcdev->pdata->clk * 2);
- if (rate <= 0) {
- err = -ENODEV;
- goto exit_dma_free;
- }
- err = clk_set_rate(pcdev->clk_csi, rate);
- if (err < 0)
- goto exit_dma_free;
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- INIT_LIST_HEAD(&pcdev->active_bufs);
- INIT_LIST_HEAD(&pcdev->discard);
- spin_lock_init(&pcdev->lock);
-
- /*
- * Request the regions.
- */
- if (!request_mem_region(res_csi->start, resource_size(res_csi),
- MX2_CAM_DRV_NAME)) {
- err = -EBUSY;
- goto exit_dma_free;
- }
-
- base_csi = ioremap(res_csi->start, resource_size(res_csi));
- if (!base_csi) {
- err = -ENOMEM;
- goto exit_release;
- }
- pcdev->irq_csi = irq_csi;
- pcdev->base_csi = base_csi;
- pcdev->base_dma = res_csi->start;
- pcdev->dev = &pdev->dev;
-
- if (cpu_is_mx25()) {
- err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0,
- MX2_CAM_DRV_NAME, pcdev);
- if (err) {
- dev_err(pcdev->dev, "Camera interrupt register failed \n");
- goto exit_iounmap;
- }
- }
-
- if (cpu_is_mx27()) {
- /* EMMA support */
- res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- irq_emma = platform_get_irq(pdev, 1);
-
- if (!res_emma || !irq_emma) {
- dev_err(&pdev->dev, "no EMMA resources\n");
- goto exit_free_irq;
- }
-
- pcdev->res_emma = res_emma;
- pcdev->irq_emma = irq_emma;
- if (mx27_camera_emma_init(pcdev))
- goto exit_free_irq;
- }
-
- pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
- pcdev->soc_host.ops = &mx2_soc_camera_host_ops,
- pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
- pcdev->soc_host.nr = pdev->id;
- if (cpu_is_mx25())
- pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
-
- pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(pcdev->alloc_ctx)) {
- err = PTR_ERR(pcdev->alloc_ctx);
- goto eallocctx;
- }
- err = soc_camera_host_register(&pcdev->soc_host);
- if (err)
- goto exit_free_emma;
-
- dev_info(&pdev->dev, "MX2 Camera (CSI) driver probed, clock frequency: %ld\n",
- clk_get_rate(pcdev->clk_csi));
-
- return 0;
-
-exit_free_emma:
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-eallocctx:
- if (cpu_is_mx27()) {
- free_irq(pcdev->irq_emma, pcdev);
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_put(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
- clk_put(pcdev->clk_emma_ahb);
- iounmap(pcdev->base_emma);
- release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma));
- }
-exit_free_irq:
- if (cpu_is_mx25())
- free_irq(pcdev->irq_csi, pcdev);
-exit_iounmap:
- iounmap(base_csi);
-exit_release:
- release_mem_region(res_csi->start, resource_size(res_csi));
-exit_dma_free:
- clk_put(pcdev->clk_csi);
-exit_kfree:
- kfree(pcdev);
-exit:
- return err;
-}
-
-static int __devexit mx2_camera_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct mx2_camera_dev *pcdev = container_of(soc_host,
- struct mx2_camera_dev, soc_host);
- struct resource *res;
-
- clk_put(pcdev->clk_csi);
- if (cpu_is_mx25())
- free_irq(pcdev->irq_csi, pcdev);
- if (cpu_is_mx27())
- free_irq(pcdev->irq_emma, pcdev);
-
- soc_camera_host_unregister(&pcdev->soc_host);
-
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-
- iounmap(pcdev->base_csi);
-
- if (cpu_is_mx27()) {
- clk_disable_unprepare(pcdev->clk_emma_ipg);
- clk_put(pcdev->clk_emma_ipg);
- clk_disable_unprepare(pcdev->clk_emma_ahb);
- clk_put(pcdev->clk_emma_ahb);
- iounmap(pcdev->base_emma);
- res = pcdev->res_emma;
- release_mem_region(res->start, resource_size(res));
- }
-
- res = pcdev->res_csi;
- release_mem_region(res->start, resource_size(res));
-
- kfree(pcdev);
-
- dev_info(&pdev->dev, "MX2 Camera driver unloaded\n");
-
- return 0;
-}
-
-static struct platform_driver mx2_camera_driver = {
- .driver = {
- .name = MX2_CAM_DRV_NAME,
- },
- .remove = __devexit_p(mx2_camera_remove),
-};
-
-
-static int __init mx2_camera_init(void)
-{
- return platform_driver_probe(&mx2_camera_driver, &mx2_camera_probe);
-}
-
-static void __exit mx2_camera_exit(void)
-{
- return platform_driver_unregister(&mx2_camera_driver);
-}
-
-module_init(mx2_camera_init);
-module_exit(mx2_camera_exit);
-
-MODULE_DESCRIPTION("i.MX27/i.MX25 SoC Camera Host driver");
-MODULE_AUTHOR("Sascha Hauer <sha@pengutronix.de>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(MX2_CAM_VERSION);
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
deleted file mode 100644
index 5f8a6f5b98f9..000000000000
--- a/drivers/media/video/mx2_emmaprp.c
+++ /dev/null
@@ -1,1016 +0,0 @@
-/*
- * Support eMMa-PrP through mem2mem framework.
- *
- * eMMa-PrP is a piece of HW that allows fetching buffers
- * from one memory location and do several operations on
- * them such as scaling or format conversion giving, as a result
- * a new processed buffer in another memory location.
- *
- * Based on mem2mem_testdev.c by Pawel Osciak.
- *
- * Copyright (c) 2011 Vista Silicon S.L.
- * Javier Martin <javier.martin@vista-silicon.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
- */
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-dma-contig.h>
-#include <asm/sizes.h>
-
-#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
-
-MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
-MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.1");
-
-static bool debug;
-module_param(debug, bool, 0644);
-
-#define MIN_W 32
-#define MIN_H 32
-#define MAX_W 2040
-#define MAX_H 2046
-
-#define S_ALIGN 1 /* multiple of 2 */
-#define W_ALIGN_YUV420 3 /* multiple of 8 */
-#define W_ALIGN_OTHERS 2 /* multiple of 4 */
-#define H_ALIGN 1 /* multiple of 2 */
-
-/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
-
-#define MEM2MEM_NAME "m2m-emmaprp"
-
-/* In bytes, per queue */
-#define MEM2MEM_VID_MEM_LIMIT SZ_16M
-
-#define dprintk(dev, fmt, arg...) \
- v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
-
-/* EMMA PrP */
-#define PRP_CNTL 0x00
-#define PRP_INTR_CNTL 0x04
-#define PRP_INTRSTATUS 0x08
-#define PRP_SOURCE_Y_PTR 0x0c
-#define PRP_SOURCE_CB_PTR 0x10
-#define PRP_SOURCE_CR_PTR 0x14
-#define PRP_DEST_RGB1_PTR 0x18
-#define PRP_DEST_RGB2_PTR 0x1c
-#define PRP_DEST_Y_PTR 0x20
-#define PRP_DEST_CB_PTR 0x24
-#define PRP_DEST_CR_PTR 0x28
-#define PRP_SRC_FRAME_SIZE 0x2c
-#define PRP_DEST_CH1_LINE_STRIDE 0x30
-#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
-#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
-#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
-#define PRP_CH2_OUT_IMAGE_SIZE 0x40
-#define PRP_SRC_LINE_STRIDE 0x44
-#define PRP_CSC_COEF_012 0x48
-#define PRP_CSC_COEF_345 0x4c
-#define PRP_CSC_COEF_678 0x50
-#define PRP_CH1_RZ_HORI_COEF1 0x54
-#define PRP_CH1_RZ_HORI_COEF2 0x58
-#define PRP_CH1_RZ_HORI_VALID 0x5c
-#define PRP_CH1_RZ_VERT_COEF1 0x60
-#define PRP_CH1_RZ_VERT_COEF2 0x64
-#define PRP_CH1_RZ_VERT_VALID 0x68
-#define PRP_CH2_RZ_HORI_COEF1 0x6c
-#define PRP_CH2_RZ_HORI_COEF2 0x70
-#define PRP_CH2_RZ_HORI_VALID 0x74
-#define PRP_CH2_RZ_VERT_COEF1 0x78
-#define PRP_CH2_RZ_VERT_COEF2 0x7c
-#define PRP_CH2_RZ_VERT_VALID 0x80
-
-#define PRP_CNTL_CH1EN (1 << 0)
-#define PRP_CNTL_CH2EN (1 << 1)
-#define PRP_CNTL_CSIEN (1 << 2)
-#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
-#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
-#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
-#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
-#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
-#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
-#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
-#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
-#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
-#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
-#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
-#define PRP_CNTL_CH1_LEN (1 << 9)
-#define PRP_CNTL_CH2_LEN (1 << 10)
-#define PRP_CNTL_SKIP_FRAME (1 << 11)
-#define PRP_CNTL_SWRST (1 << 12)
-#define PRP_CNTL_CLKEN (1 << 13)
-#define PRP_CNTL_WEN (1 << 14)
-#define PRP_CNTL_CH1BYP (1 << 15)
-#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
-#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
-#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
-#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
-#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
-#define PRP_CNTL_CH2B1EN (1 << 29)
-#define PRP_CNTL_CH2B2EN (1 << 30)
-#define PRP_CNTL_CH2FEN (1 << 31)
-
-#define PRP_SIZE_HEIGHT(x) (x)
-#define PRP_SIZE_WIDTH(x) ((x) << 16)
-
-/* IRQ Enable and status register */
-#define PRP_INTR_RDERR (1 << 0)
-#define PRP_INTR_CH1WERR (1 << 1)
-#define PRP_INTR_CH2WERR (1 << 2)
-#define PRP_INTR_CH1FC (1 << 3)
-#define PRP_INTR_CH2FC (1 << 5)
-#define PRP_INTR_LBOVF (1 << 7)
-#define PRP_INTR_CH2OVF (1 << 8)
-
-#define PRP_INTR_ST_RDERR (1 << 0)
-#define PRP_INTR_ST_CH1WERR (1 << 1)
-#define PRP_INTR_ST_CH2WERR (1 << 2)
-#define PRP_INTR_ST_CH2B2CI (1 << 3)
-#define PRP_INTR_ST_CH2B1CI (1 << 4)
-#define PRP_INTR_ST_CH1B2CI (1 << 5)
-#define PRP_INTR_ST_CH1B1CI (1 << 6)
-#define PRP_INTR_ST_LBOVF (1 << 7)
-#define PRP_INTR_ST_CH2OVF (1 << 8)
-
-struct emmaprp_fmt {
- char *name;
- u32 fourcc;
- /* Types the format can be used for */
- u32 types;
-};
-
-static struct emmaprp_fmt formats[] = {
- {
- .name = "YUV 4:2:0 Planar",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .types = MEM2MEM_CAPTURE,
- },
- {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .types = MEM2MEM_OUTPUT,
- },
-};
-
-/* Per-queue, driver-specific private data */
-struct emmaprp_q_data {
- unsigned int width;
- unsigned int height;
- unsigned int sizeimage;
- struct emmaprp_fmt *fmt;
-};
-
-enum {
- V4L2_M2M_SRC = 0,
- V4L2_M2M_DST = 1,
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-static struct emmaprp_fmt *find_format(struct v4l2_format *f)
-{
- struct emmaprp_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < NUM_FORMATS; k++) {
- fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
- break;
- }
-
- if (k == NUM_FORMATS)
- return NULL;
-
- return &formats[k];
-}
-
-struct emmaprp_dev {
- struct v4l2_device v4l2_dev;
- struct video_device *vfd;
-
- struct mutex dev_mutex;
- spinlock_t irqlock;
-
- int irq_emma;
- void __iomem *base_emma;
- struct clk *clk_emma;
- struct resource *res_emma;
-
- struct v4l2_m2m_dev *m2m_dev;
- struct vb2_alloc_ctx *alloc_ctx;
-};
-
-struct emmaprp_ctx {
- struct emmaprp_dev *dev;
- /* Abort requested by m2m */
- int aborting;
- struct emmaprp_q_data q_data[2];
- struct v4l2_m2m_ctx *m2m_ctx;
-};
-
-static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
- enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return &(ctx->q_data[V4L2_M2M_SRC]);
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &(ctx->q_data[V4L2_M2M_DST]);
- default:
- BUG();
- }
- return NULL;
-}
-
-/*
- * mem2mem callbacks
- */
-static void emmaprp_job_abort(void *priv)
-{
- struct emmaprp_ctx *ctx = priv;
- struct emmaprp_dev *pcdev = ctx->dev;
-
- ctx->aborting = 1;
-
- dprintk(pcdev, "Aborting task\n");
-
- v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
-}
-
-static void emmaprp_lock(void *priv)
-{
- struct emmaprp_ctx *ctx = priv;
- struct emmaprp_dev *pcdev = ctx->dev;
- mutex_lock(&pcdev->dev_mutex);
-}
-
-static void emmaprp_unlock(void *priv)
-{
- struct emmaprp_ctx *ctx = priv;
- struct emmaprp_dev *pcdev = ctx->dev;
- mutex_unlock(&pcdev->dev_mutex);
-}
-
-static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
-{
- dprintk(pcdev,
- "eMMa-PrP Registers:\n"
- " SOURCE_Y_PTR = 0x%08X\n"
- " SRC_FRAME_SIZE = 0x%08X\n"
- " DEST_Y_PTR = 0x%08X\n"
- " DEST_CR_PTR = 0x%08X\n"
- " DEST_CB_PTR = 0x%08X\n"
- " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
- " CNTL = 0x%08X\n",
- readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
- readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
- readl(pcdev->base_emma + PRP_DEST_Y_PTR),
- readl(pcdev->base_emma + PRP_DEST_CR_PTR),
- readl(pcdev->base_emma + PRP_DEST_CB_PTR),
- readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
- readl(pcdev->base_emma + PRP_CNTL));
-}
-
-static void emmaprp_device_run(void *priv)
-{
- struct emmaprp_ctx *ctx = priv;
- struct emmaprp_q_data *s_q_data, *d_q_data;
- struct vb2_buffer *src_buf, *dst_buf;
- struct emmaprp_dev *pcdev = ctx->dev;
- unsigned int s_width, s_height;
- unsigned int d_width, d_height;
- unsigned int d_size;
- dma_addr_t p_in, p_out;
- u32 tmp;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
-
- s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
- s_width = s_q_data->width;
- s_height = s_q_data->height;
-
- d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- d_width = d_q_data->width;
- d_height = d_q_data->height;
- d_size = d_width * d_height;
-
- p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
- if (!p_in || !p_out) {
- v4l2_err(&pcdev->v4l2_dev,
- "Acquiring kernel pointers to buffers failed\n");
- return;
- }
-
- /* Input frame parameters */
- writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
- writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
-
- /* Output frame parameters */
- writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
- writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
- writel(p_out + d_size + (d_size >> 2),
- pcdev->base_emma + PRP_DEST_CR_PTR);
- writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
- pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
-
- /* IRQ configuration */
- tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
- writel(tmp | PRP_INTR_RDERR |
- PRP_INTR_CH2WERR |
- PRP_INTR_CH2FC,
- pcdev->base_emma + PRP_INTR_CNTL);
-
- emmaprp_dump_regs(pcdev);
-
- /* Enable transfer */
- tmp = readl(pcdev->base_emma + PRP_CNTL);
- writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
- PRP_CNTL_DATA_IN_YUV422 |
- PRP_CNTL_CH2EN,
- pcdev->base_emma + PRP_CNTL);
-}
-
-static irqreturn_t emmaprp_irq(int irq_emma, void *data)
-{
- struct emmaprp_dev *pcdev = data;
- struct emmaprp_ctx *curr_ctx;
- struct vb2_buffer *src_vb, *dst_vb;
- unsigned long flags;
- u32 irqst;
-
- /* Check irq flags and clear irq */
- irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
- writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
- dprintk(pcdev, "irqst = 0x%08x\n", irqst);
-
- curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
- if (curr_ctx == NULL) {
- pr_err("Instance released before the end of transaction\n");
- return IRQ_HANDLED;
- }
-
- if (!curr_ctx->aborting) {
- if ((irqst & PRP_INTR_ST_RDERR) ||
- (irqst & PRP_INTR_ST_CH2WERR)) {
- pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
- writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
- } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
- src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
-
- spin_lock_irqsave(&pcdev->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(&pcdev->irqlock, flags);
- }
- }
-
- v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
- return IRQ_HANDLED;
-}
-
-/*
- * video ioctls
- */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
- strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
-{
- int i, num;
- struct emmaprp_fmt *fmt;
-
- num = 0;
-
- for (i = 0; i < NUM_FORMATS; ++i) {
- if (formats[i].types & type) {
- /* index-th format of type type found ? */
- if (num == f->index)
- break;
- /* Correct type but haven't reached our index yet,
- * just increment per-type index */
- ++num;
- }
- }
-
- if (i < NUM_FORMATS) {
- /* Format found */
- fmt = &formats[i];
- strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
- f->pixelformat = fmt->fourcc;
- return 0;
- }
-
- /* Format not found */
- return -EINVAL;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_CAPTURE);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- return enum_fmt(f, MEM2MEM_OUTPUT);
-}
-
-static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct emmaprp_q_data *q_data;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
-
- f->fmt.pix.width = q_data->width;
- f->fmt.pix.height = q_data->height;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = q_data->fmt->fourcc;
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
- f->fmt.pix.bytesperline = q_data->width * 3 / 2;
- else /* YUYV */
- f->fmt.pix.bytesperline = q_data->width * 2;
- f->fmt.pix.sizeimage = q_data->sizeimage;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(priv, f);
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- return vidioc_g_fmt(priv, f);
-}
-
-static int vidioc_try_fmt(struct v4l2_format *f)
-{
- enum v4l2_field field;
-
-
- if (!find_format(f))
- return -EINVAL;
-
- field = f->fmt.pix.field;
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_NONE;
- else if (V4L2_FIELD_NONE != field)
- return -EINVAL;
-
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
- f->fmt.pix.field = field;
-
- if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
- v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
- W_ALIGN_YUV420, &f->fmt.pix.height,
- MIN_H, MAX_H, H_ALIGN, S_ALIGN);
- f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
- } else {
- v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
- W_ALIGN_OTHERS, &f->fmt.pix.height,
- MIN_H, MAX_H, H_ALIGN, S_ALIGN);
- f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
- }
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct emmaprp_fmt *fmt;
- struct emmaprp_ctx *ctx = priv;
-
- fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- return vidioc_try_fmt(f);
-}
-
-static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct emmaprp_fmt *fmt;
- struct emmaprp_ctx *ctx = priv;
-
- fmt = find_format(f);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
- v4l2_err(&ctx->dev->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- return vidioc_try_fmt(f);
-}
-
-static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
-{
- struct emmaprp_q_data *q_data;
- struct vb2_queue *vq;
- int ret;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ctx, f->type);
- if (!q_data)
- return -EINVAL;
-
- if (vb2_is_busy(vq)) {
- v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- ret = vidioc_try_fmt(f);
- if (ret)
- return ret;
-
- q_data->fmt = find_format(f);
- q_data->width = f->fmt.pix.width;
- q_data->height = f->fmt.pix.height;
- if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
- q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
- else /* YUYV */
- q_data->sizeimage = q_data->width * q_data->height * 2;
-
- dprintk(ctx->dev,
- "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
- f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return vidioc_s_fmt(priv, f);
-}
-
-static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = vidioc_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- return vidioc_s_fmt(priv, f);
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct emmaprp_ctx *ctx = priv;
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
-
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
-
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-};
-
-
-/*
- * Queue operations
- */
-static int emmaprp_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
- struct emmaprp_q_data *q_data;
- unsigned int size, count = *nbuffers;
-
- q_data = get_q_data(ctx, vq->type);
-
- if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
- size = q_data->width * q_data->height * 3 / 2;
- else
- size = q_data->width * q_data->height * 2;
-
- while (size * count > MEM2MEM_VID_MEM_LIMIT)
- (count)--;
-
- *nplanes = 1;
- *nbuffers = count;
- sizes[0] = size;
-
- alloc_ctxs[0] = ctx->dev->alloc_ctx;
-
- dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
-
- return 0;
-}
-
-static int emmaprp_buf_prepare(struct vb2_buffer *vb)
-{
- struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct emmaprp_q_data *q_data;
-
- dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
-
- 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;
- }
-
- vb2_set_plane_payload(vb, 0, q_data->sizeimage);
-
- return 0;
-}
-
-static void emmaprp_buf_queue(struct vb2_buffer *vb)
-{
- struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static struct vb2_ops emmaprp_qops = {
- .queue_setup = emmaprp_queue_setup,
- .buf_prepare = emmaprp_buf_prepare,
- .buf_queue = emmaprp_buf_queue,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct emmaprp_ctx *ctx = priv;
- int ret;
-
- memset(src_vq, 0, sizeof(*src_vq));
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
- src_vq->drv_priv = ctx;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &emmaprp_qops;
- src_vq->mem_ops = &vb2_dma_contig_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 | VB2_USERPTR;
- dst_vq->drv_priv = ctx;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &emmaprp_qops;
- dst_vq->mem_ops = &vb2_dma_contig_memops;
-
- return vb2_queue_init(dst_vq);
-}
-
-/*
- * File operations
- */
-static int emmaprp_open(struct file *file)
-{
- struct emmaprp_dev *pcdev = video_drvdata(file);
- struct emmaprp_ctx *ctx;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- file->private_data = ctx;
- ctx->dev = pcdev;
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
-
- if (IS_ERR(ctx->m2m_ctx)) {
- int ret = PTR_ERR(ctx->m2m_ctx);
-
- kfree(ctx);
- return ret;
- }
-
- clk_enable(pcdev->clk_emma);
- ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
- ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
-
- dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
-
- return 0;
-}
-
-static int emmaprp_release(struct file *file)
-{
- struct emmaprp_dev *pcdev = video_drvdata(file);
- struct emmaprp_ctx *ctx = file->private_data;
-
- dprintk(pcdev, "Releasing instance %p\n", ctx);
-
- clk_disable(pcdev->clk_emma);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- kfree(ctx);
-
- return 0;
-}
-
-static unsigned int emmaprp_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct emmaprp_ctx *ctx = file->private_data;
-
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct emmaprp_ctx *ctx = file->private_data;
-
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-}
-
-static const struct v4l2_file_operations emmaprp_fops = {
- .owner = THIS_MODULE,
- .open = emmaprp_open,
- .release = emmaprp_release,
- .poll = emmaprp_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = emmaprp_mmap,
-};
-
-static struct video_device emmaprp_videodev = {
- .name = MEM2MEM_NAME,
- .fops = &emmaprp_fops,
- .ioctl_ops = &emmaprp_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
-};
-
-static struct v4l2_m2m_ops m2m_ops = {
- .device_run = emmaprp_device_run,
- .job_abort = emmaprp_job_abort,
- .lock = emmaprp_lock,
- .unlock = emmaprp_unlock,
-};
-
-static int emmaprp_probe(struct platform_device *pdev)
-{
- struct emmaprp_dev *pcdev;
- struct video_device *vfd;
- struct resource *res_emma;
- int irq_emma;
- int ret;
-
- pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
- if (!pcdev)
- return -ENOMEM;
-
- spin_lock_init(&pcdev->irqlock);
-
- pcdev->clk_emma = clk_get(&pdev->dev, NULL);
- if (IS_ERR(pcdev->clk_emma)) {
- ret = PTR_ERR(pcdev->clk_emma);
- goto free_dev;
- }
-
- irq_emma = platform_get_irq(pdev, 0);
- res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (irq_emma < 0 || res_emma == NULL) {
- dev_err(&pdev->dev, "Missing platform resources data\n");
- ret = -ENODEV;
- goto free_clk;
- }
-
- ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
- if (ret)
- goto free_clk;
-
- mutex_init(&pcdev->dev_mutex);
-
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto unreg_dev;
- }
-
- *vfd = emmaprp_videodev;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->lock = &pcdev->dev_mutex;
-
- video_set_drvdata(vfd, pcdev);
- snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
- pcdev->vfd = vfd;
- v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
- " Device registered as /dev/video%d\n", vfd->num);
-
- platform_set_drvdata(pdev, pcdev);
-
- if (devm_request_mem_region(&pdev->dev, res_emma->start,
- resource_size(res_emma), MEM2MEM_NAME) == NULL)
- goto rel_vdev;
-
- pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start,
- resource_size(res_emma));
- if (!pcdev->base_emma)
- goto rel_vdev;
-
- pcdev->irq_emma = irq_emma;
- pcdev->res_emma = res_emma;
-
- if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
- 0, MEM2MEM_NAME, pcdev) < 0)
- goto rel_vdev;
-
- pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(pcdev->alloc_ctx)) {
- v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
- ret = PTR_ERR(pcdev->alloc_ctx);
- goto rel_vdev;
- }
-
- pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(pcdev->m2m_dev)) {
- v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
- ret = PTR_ERR(pcdev->m2m_dev);
- goto rel_ctx;
- }
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
- goto rel_m2m;
- }
-
- return 0;
-
-
-rel_m2m:
- v4l2_m2m_release(pcdev->m2m_dev);
-rel_ctx:
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-rel_vdev:
- video_device_release(vfd);
-unreg_dev:
- v4l2_device_unregister(&pcdev->v4l2_dev);
-free_clk:
- clk_put(pcdev->clk_emma);
-free_dev:
- kfree(pcdev);
-
- return ret;
-}
-
-static int emmaprp_remove(struct platform_device *pdev)
-{
- struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
-
- v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
-
- video_unregister_device(pcdev->vfd);
- v4l2_m2m_release(pcdev->m2m_dev);
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
- v4l2_device_unregister(&pcdev->v4l2_dev);
- clk_put(pcdev->clk_emma);
- kfree(pcdev);
-
- return 0;
-}
-
-static struct platform_driver emmaprp_pdrv = {
- .probe = emmaprp_probe,
- .remove = emmaprp_remove,
- .driver = {
- .name = MEM2MEM_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static void __exit emmaprp_exit(void)
-{
- platform_driver_unregister(&emmaprp_pdrv);
-}
-
-static int __init emmaprp_init(void)
-{
- return platform_driver_register(&emmaprp_pdrv);
-}
-
-module_init(emmaprp_init);
-module_exit(emmaprp_exit);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
deleted file mode 100644
index af2297dd49c8..000000000000
--- a/drivers/media/video/mx3_camera.c
+++ /dev/null
@@ -1,1292 +0,0 @@
-/*
- * V4L2 Driver for i.MX3x camera host
- *
- * Copyright (C) 2008
- * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
- *
- * 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/videodev2.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/vmalloc.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-
-#include <mach/ipu.h>
-#include <mach/mx3_camera.h>
-#include <mach/dma.h>
-
-#define MX3_CAM_DRV_NAME "mx3-camera"
-
-/* CMOS Sensor Interface Registers */
-#define CSI_REG_START 0x60
-
-#define CSI_SENS_CONF (0x60 - CSI_REG_START)
-#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
-#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
-#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
-#define CSI_TST_CTRL (0x70 - CSI_REG_START)
-#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
-#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
-#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
-#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
-#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
-
-#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
-#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
-#define CSI_SENS_CONF_DATA_POL_SHIFT 2
-#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
-#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
-#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
-#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
-#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
-#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
-#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
-
-#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
-
-#define MAX_VIDEO_MEM 16
-
-struct mx3_camera_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_buffer vb;
- struct list_head queue;
-
- /* One descriptot per scatterlist (per frame) */
- struct dma_async_tx_descriptor *txd;
-
- /* We have to "build" a scatterlist ourselves - one element per frame */
- struct scatterlist sg;
-};
-
-/**
- * struct mx3_camera_dev - i.MX3x camera (CSI) object
- * @dev: camera device, to which the coherent buffer is attached
- * @icd: currently attached camera sensor
- * @clk: pointer to clock
- * @base: remapped register base address
- * @pdata: platform data
- * @platform_flags: platform flags
- * @mclk: master clock frequency in Hz
- * @capture: list of capture videobuffers
- * @lock: protects video buffer lists
- * @active: active video buffer
- * @idmac_channel: array of pointers to IPU DMAC DMA channels
- * @soc_host: embedded soc_host object
- */
-struct mx3_camera_dev {
- /*
- * i.MX3x is only supposed to handle one camera on its Camera Sensor
- * Interface. If anyone ever builds hardware to enable more than one
- * camera _simultaneously_, they will have to modify this driver too
- */
- struct soc_camera_device *icd;
- struct clk *clk;
-
- void __iomem *base;
-
- struct mx3_camera_pdata *pdata;
-
- unsigned long platform_flags;
- unsigned long mclk;
- u16 width_flags; /* max 15 bits */
-
- struct list_head capture;
- spinlock_t lock; /* Protects video buffer lists */
- struct mx3_camera_buffer *active;
- size_t buf_total;
- 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 */
-
- struct soc_camera_host soc_host;
-};
-
-struct dma_chan_request {
- struct mx3_camera_dev *mx3_cam;
- enum ipu_channel id;
-};
-
-static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
-{
- return __raw_readl(mx3->base + reg);
-}
-
-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)
-{
- struct idmac_tx_desc *desc = to_tx_desc(arg);
- struct dma_chan *chan = desc->txd.chan;
- struct idmac_channel *ichannel = to_idmac_chan(chan);
- struct mx3_camera_dev *mx3_cam = ichannel->client;
-
- 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) {
- 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)) {
- mx3_cam->active = NULL;
- spin_unlock(&mx3_cam->lock);
-
- /*
- * stop capture - without further buffers IPU_CHA_BUF0_RDY will
- * not get updated
- */
- return;
- }
-
- mx3_cam->active = list_entry(mx3_cam->capture.next,
- struct mx3_camera_buffer, queue);
- spin_unlock(&mx3_cam->lock);
-}
-
-/*
- * Videobuf operations
- */
-
-/*
- * Calculate the __buffer__ (not data) size and number of buffers.
- */
-static int mx3_videobuf_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- if (!mx3_cam->idmac_channel[0])
- return -EINVAL;
-
- if (fmt) {
- const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
- fmt->fmt.pix.pixelformat);
- unsigned int bytes_per_line;
- int ret;
-
- if (!xlate)
- return -EINVAL;
-
- ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
- fmt->fmt.pix.height);
- if (ret < 0)
- return ret;
-
- sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
- } else {
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- sizes[0] = icd->sizeimage;
- }
-
- alloc_ctxs[0] = mx3_cam->alloc_ctx;
-
- if (!vq->num_buffers)
- mx3_cam->sequence = 0;
-
- if (!*count)
- *count = 2;
-
- /* If *num_planes != 0, we have already verified *count. */
- if (!*num_planes &&
- sizes[0] * *count + mx3_cam->buf_total > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024 - mx3_cam->buf_total) /
- sizes[0];
-
- *num_planes = 1;
-
- 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_RGB24:
- return IPU_PIX_FMT_RGB24;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_RGB565:
- default:
- return IPU_PIX_FMT_GENERIC;
- }
-}
-
-static void mx3_videobuf_queue(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vb);
- struct scatterlist *sg = &buf->sg;
- struct dma_async_tx_descriptor *txd;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct idmac_video_param *video = &ichan->params.video;
- const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
- unsigned long flags;
- dma_cookie_t cookie;
- size_t new_size;
-
- new_size = icd->sizeimage;
-
- if (vb2_plane_size(vb, 0) < new_size) {
- dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
- vb->v4l2_buf.index, vb2_plane_size(vb, 0), new_size);
- goto error;
- }
-
- if (!buf->txd) {
- sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
- sg_dma_len(sg) = new_size;
-
- txd = dmaengine_prep_slave_sg(
- &ichan->dma_chan, sg, 1, DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
- if (!txd)
- goto error;
-
- txd->callback_param = txd;
- txd->callback = mx3_cam_dma_done;
-
- buf->txd = txd;
- } else {
- txd = buf->txd;
- }
-
- vb2_set_plane_payload(vb, 0, new_size);
-
- /* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
-
- if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
- /*
- * If the IPU DMA channel is configured to transfer generic
- * 8-bit data, we have to set up the geometry parameters
- * correctly, according to the current pixel format. The DMA
- * horizontal parameters in this case are expressed in bytes,
- * not in pixels.
- */
- video->out_width = icd->bytesperline;
- video->out_height = icd->user_height;
- video->out_stride = icd->bytesperline;
- } 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 */
- if (vb2_plane_vaddr(vb, 0))
- memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- spin_lock_irqsave(&mx3_cam->lock, flags);
- list_add_tail(&buf->queue, &mx3_cam->capture);
-
- if (!mx3_cam->active)
- mx3_cam->active = buf;
-
- spin_unlock_irq(&mx3_cam->lock);
-
- cookie = txd->tx_submit(txd);
- dev_dbg(icd->parent, "Submitted cookie %d DMA 0x%08x\n",
- cookie, sg_dma_address(&buf->sg));
-
- if (cookie >= 0)
- return;
-
- spin_lock_irq(&mx3_cam->lock);
-
- /* Submit error */
- list_del_init(&buf->queue);
-
- if (mx3_cam->active == buf)
- mx3_cam->active = NULL;
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
-error:
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
-}
-
-static void mx3_videobuf_release(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vb);
- struct dma_async_tx_descriptor *txd = buf->txd;
- unsigned long flags;
-
- dev_dbg(icd->parent,
- "Release%s DMA 0x%08x, queue %sempty\n",
- mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
- list_empty(&buf->queue) ? "" : "not ");
-
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
- if (mx3_cam->active == buf)
- mx3_cam->active = NULL;
-
- /* Doesn't hurt also if the list is empty */
- list_del_init(&buf->queue);
-
- if (txd) {
- buf->txd = NULL;
- if (mx3_cam->idmac_channel[0])
- async_tx_ack(txd);
- }
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
-
- mx3_cam->buf_total -= vb2_plane_size(vb, 0);
-}
-
-static int mx3_videobuf_init(struct vb2_buffer *vb)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct mx3_camera_buffer *buf = to_mx3_vb(vb);
-
- if (!buf->txd) {
- /* This is for locking debugging only */
- INIT_LIST_HEAD(&buf->queue);
- sg_init_table(&buf->sg, 1);
-
- mx3_cam->buf_total += vb2_plane_size(vb, 0);
- }
-
- return 0;
-}
-
-static int mx3_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = soc_camera_from_vb2q(q);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
- struct mx3_camera_buffer *buf, *tmp;
- unsigned long flags;
-
- if (ichan) {
- struct dma_chan *chan = &ichan->dma_chan;
- chan->device->device_control(chan, DMA_PAUSE, 0);
- }
-
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
- mx3_cam->active = NULL;
-
- list_for_each_entry_safe(buf, tmp, &mx3_cam->capture, queue) {
- list_del_init(&buf->queue);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
-
- return 0;
-}
-
-static struct vb2_ops mx3_videobuf_ops = {
- .queue_setup = mx3_videobuf_setup,
- .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,
- .stop_streaming = mx3_stop_streaming,
-};
-
-static int mx3_camera_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- 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() */
-static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
- struct soc_camera_device *icd)
-{
- u32 conf;
- long rate;
-
- /* Set default size: ipu_csi_set_window_size() */
- csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
- /* ...and position to 0:0: ipu_csi_set_window_pos() */
- conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
- csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
-
- /* We use only gated clock synchronisation mode so far */
- conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
-
- /* Set generic data, platform-biggest bus-width */
- conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
-
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
- conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
-
- if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
- conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
- conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
-
- /* ipu_csi_init_interface() */
- csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
-
- clk_prepare_enable(mx3_cam->clk);
- rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
- if (rate)
- clk_set_rate(mx3_cam->clk, rate);
-}
-
-/* Called with .video_lock held */
-static int mx3_camera_add_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
-
- if (mx3_cam->icd)
- return -EBUSY;
-
- mx3_camera_activate(mx3_cam, icd);
-
- mx3_cam->buf_total = 0;
- mx3_cam->icd = icd;
-
- dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n",
- icd->devnum);
-
- return 0;
-}
-
-/* Called with .video_lock held */
-static void mx3_camera_remove_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
-
- BUG_ON(icd != mx3_cam->icd);
-
- if (*ichan) {
- dma_release_channel(&(*ichan)->dma_chan);
- *ichan = NULL;
- }
-
- clk_disable_unprepare(mx3_cam->clk);
-
- mx3_cam->icd = NULL;
-
- dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n",
- icd->devnum);
-}
-
-static int test_platform_param(struct mx3_camera_dev *mx3_cam,
- unsigned char buswidth, unsigned long *flags)
-{
- /*
- * If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to shift bits
- */
- if (buswidth > fls(mx3_cam->width_flags))
- return -EINVAL;
-
- /*
- * Platform specified synchronization and pixel clock polarities are
- * only a recommendation and are only used during probing. MX3x
- * camera interface only works in master mode, i.e., uses HSYNC and
- * VSYNC signals from the sensor
- */
- *flags = V4L2_MBUS_MASTER |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH |
- V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_PCLK_SAMPLE_RISING |
- V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_LOW;
-
- return 0;
-}
-
-static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
- const unsigned int depth)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long bus_flags, common_flags;
- int ret = test_platform_param(mx3_cam, depth, &bus_flags);
-
- dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
-
- if (ret < 0)
- return ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- }
-
- return 0;
-}
-
-static bool chan_filter(struct dma_chan *chan, void *arg)
-{
- struct dma_chan_request *rq = arg;
- struct mx3_camera_pdata *pdata;
-
- if (!imx_dma_is_ipu(chan))
- return false;
-
- if (!rq)
- return false;
-
- pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
-
- return rq->id == chan->chan_id &&
- pdata->dma_dev == chan->device->dev;
-}
-
-static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .name = "Bayer BGGR (sRGB) 8 bit",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_NONE,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- }, {
- .fourcc = V4L2_PIX_FMT_GREY,
- .name = "Monochrome 8 bit",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_NONE,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-};
-
-/* This will be corrected as we get more formats */
-static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
-{
- return fmt->packing == SOC_MBUS_PACKING_NONE ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
- (fmt->bits_per_sample > 8 &&
- fmt->packing == SOC_MBUS_PACKING_EXTEND16);
-}
-
-static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- int formats = 0, ret;
- enum v4l2_mbus_pixelcode code;
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code);
- if (!fmt) {
- dev_warn(icd->parent,
- "Unsupported format code #%u: %d\n", idx, code);
- return 0;
- }
-
- /* This also checks support for the requested bits-per-sample */
- ret = mx3_camera_try_bus_param(icd, fmt->bits_per_sample);
- if (ret < 0)
- return 0;
-
- switch (code) {
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[0];
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- mx3_camera_formats[0].name, code);
- }
- break;
- case V4L2_MBUS_FMT_Y10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[1];
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- mx3_camera_formats[1].name, code);
- }
- break;
- default:
- if (!mx3_camera_packing_supported(fmt))
- return 0;
- }
-
- /* Generic pass-through */
- formats++;
- 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++;
- }
-
- return formats;
-}
-
-static void configure_geometry(struct mx3_camera_dev *mx3_cam,
- unsigned int width, unsigned int height,
- const struct soc_mbus_pixelfmt *fmt)
-{
- u32 ctrl, width_field, height_field;
-
- 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.
- */
- unsigned int num, den;
- int ret = soc_mbus_samples_per_pixel(fmt, &num, &den);
- BUG_ON(ret < 0);
- width = width * num / den;
- }
-
- /* Setup frame size - this cannot be changed on-the-fly... */
- width_field = width - 1;
- height_field = height - 1;
- csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
-
- csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
- csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
-
- csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
-
- /* ...and position */
- ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
- /* Sensor does the cropping */
- csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
-}
-
-static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
-{
- dma_cap_mask_t mask;
- struct dma_chan *chan;
- struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
- /* We have to use IDMAC_IC_7 for Bayer / generic data */
- struct dma_chan_request rq = {.mx3_cam = mx3_cam,
- .id = IDMAC_IC_7};
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
- dma_cap_set(DMA_PRIVATE, mask);
- chan = dma_request_channel(mask, chan_filter, &rq);
- if (!chan)
- return -EBUSY;
-
- *ichan = to_idmac_chan(chan);
- (*ichan)->client = mx3_cam;
-
- return 0;
-}
-
-/*
- * FIXME: learn to use stride != width, then we can keep stride properly aligned
- * and support arbitrary (even) widths.
- */
-static inline void stride_align(__u32 *width)
-{
- if (ALIGN(*width, 8) < 4096)
- *width = ALIGN(*width, 8);
- else
- *width = *width & ~7;
-}
-
-/*
- * As long as we don't implement host-side cropping and scaling, we can use
- * default g_crop and cropcap from soc_camera.c
- */
-static int mx3_camera_set_crop(struct soc_camera_device *icd,
- struct v4l2_crop *a)
-{
- struct v4l2_rect *rect = &a->c;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
- soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
-
- ret = v4l2_subdev_call(sd, video, s_crop, a);
- if (ret < 0)
- return ret;
-
- /* The capture device might have changed its output sizes */
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- if (mf.code != icd->current_fmt->code)
- return -EINVAL;
-
- if (mf.width & 7) {
- /* Ouch! We can only handle 8-byte aligned width... */
- stride_align(&mf.width);
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
- }
-
- if (mf.width != icd->user_width || mf.height != icd->user_height)
- configure_geometry(mx3_cam, mf.width, mf.height,
- icd->current_fmt->host_fmt);
-
- dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
- mf.width, mf.height);
-
- icd->user_width = mf.width;
- icd->user_height = mf.height;
-
- return ret;
-}
-
-static int mx3_camera_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- stride_align(&pix->width);
- dev_dbg(icd->parent, "Set format %dx%d\n", pix->width, pix->height);
-
- /*
- * 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, xlate->host_fmt);
-
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- 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;
-
- dev_dbg(icd->parent, "Sensor set %dx%d\n", pix->width, pix->height);
-
- return ret;
-}
-
-static int mx3_camera_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- __u32 pixfmt = pix->pixelformat;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (pixfmt && !xlate) {
- dev_warn(icd->parent, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /* limit to MX3 hardware capabilities */
- if (pix->height > 4096)
- pix->height = 4096;
- if (pix->width > 4096)
- pix->width = 4096;
-
- /* limit to sensor capabilities */
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->colorspace = mf.colorspace;
-
- switch (mf.field) {
- case V4L2_FIELD_ANY:
- pix->field = V4L2_FIELD_NONE;
- break;
- case V4L2_FIELD_NONE:
- break;
- default:
- dev_err(icd->parent, "Field type %d unsupported.\n",
- mf.field);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static int mx3_camera_reqbufs(struct soc_camera_device *icd,
- struct v4l2_requestbuffers *p)
-{
- return 0;
-}
-
-static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static int mx3_camera_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the firendly caller:-> */
- strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct mx3_camera_dev *mx3_cam = ici->priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
- unsigned long bus_flags, common_flags;
- u32 dw, sens_conf;
- const struct soc_mbus_pixelfmt *fmt;
- int buswidth;
- int ret;
- const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->parent;
-
- fmt = soc_mbus_get_fmtdesc(icd->current_fmt->code);
- if (!fmt)
- return -EINVAL;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- dev_warn(dev, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- buswidth = fmt->bits_per_sample;
- ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
-
- dev_dbg(dev, "requested bus width %d bit: %d\n", buswidth, ret);
-
- if (ret < 0)
- return ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- bus_flags);
- if (!common_flags) {
- dev_warn(icd->parent,
- "Flags incompatible: camera 0x%x, host 0x%lx\n",
- cfg.flags, bus_flags);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = bus_flags;
- }
-
- dev_dbg(dev, "Flags cam: 0x%x host: 0x%lx common: 0x%lx\n",
- cfg.flags, bus_flags, common_flags);
-
- /* Make choices, based on platform preferences */
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_DATA_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_DP)
- common_flags &= ~V4L2_MBUS_DATA_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_DATA_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- /*
- * So far only gated clock mode is supported. Add a line
- * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
- * below and select the required mode when supporting other
- * synchronisation protocols.
- */
- sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
- ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
- (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
- (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
- (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
- (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
- (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
-
- /* TODO: Support RGB and YUV formats */
-
- /* This has been set in mx3_camera_activate(), but we clear it above */
- sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
- if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
- if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
- if (common_flags & V4L2_MBUS_DATA_ACTIVE_LOW)
- sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
-
- /* Just do what we're asked to do */
- switch (xlate->host_fmt->bits_per_sample) {
- case 4:
- dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- case 8:
- dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- case 10:
- dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- break;
- default:
- /*
- * Actually it can only be 15 now, default is just to silence
- * compiler warnings
- */
- case 15:
- dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
- }
-
- csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
-
- dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
-
- return 0;
-}
-
-static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = mx3_camera_add_device,
- .remove = mx3_camera_remove_device,
- .set_crop = mx3_camera_set_crop,
- .set_fmt = mx3_camera_set_fmt,
- .try_fmt = mx3_camera_try_fmt,
- .get_formats = mx3_camera_get_formats,
- .init_videobuf2 = mx3_camera_init_videobuf,
- .reqbufs = mx3_camera_reqbufs,
- .poll = mx3_camera_poll,
- .querycap = mx3_camera_querycap,
- .set_bus_param = mx3_camera_set_bus_param,
-};
-
-static int __devinit mx3_camera_probe(struct platform_device *pdev)
-{
- struct mx3_camera_dev *mx3_cam;
- struct resource *res;
- void __iomem *base;
- int err = 0;
- struct soc_camera_host *soc_host;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto egetres;
- }
-
- mx3_cam = vzalloc(sizeof(*mx3_cam));
- if (!mx3_cam) {
- dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
- err = -ENOMEM;
- goto ealloc;
- }
-
- mx3_cam->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(mx3_cam->clk)) {
- err = PTR_ERR(mx3_cam->clk);
- goto eclkget;
- }
-
- mx3_cam->pdata = pdev->dev.platform_data;
- mx3_cam->platform_flags = mx3_cam->pdata->flags;
- if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 |
- MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 |
- MX3_CAMERA_DATAWIDTH_15))) {
- /*
- * Platform hasn't set available data widths. This is bad.
- * Warn and use a default.
- */
- dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
- "data widths, using default 8 bit\n");
- mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
- }
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)
- mx3_cam->width_flags = 1 << 3;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
- mx3_cam->width_flags |= 1 << 7;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
- mx3_cam->width_flags |= 1 << 9;
- if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
- mx3_cam->width_flags |= 1 << 14;
-
- mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
- if (!mx3_cam->mclk) {
- dev_warn(&pdev->dev,
- "mclk_10khz == 0! Please, fix your platform data. "
- "Using default 20MHz\n");
- mx3_cam->mclk = 20000000;
- }
-
- /* list of video-buffers */
- INIT_LIST_HEAD(&mx3_cam->capture);
- spin_lock_init(&mx3_cam->lock);
-
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
- err = -ENOMEM;
- goto eioremap;
- }
-
- mx3_cam->base = base;
-
- soc_host = &mx3_cam->soc_host;
- soc_host->drv_name = MX3_CAM_DRV_NAME;
- soc_host->ops = &mx3_soc_camera_host_ops;
- soc_host->priv = mx3_cam;
- 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;
-
- /* IDMAC interface */
- dmaengine_get();
-
- return 0;
-
-ecamhostreg:
- vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
-eallocctx:
- iounmap(base);
-eioremap:
- clk_put(mx3_cam->clk);
-eclkget:
- vfree(mx3_cam);
-ealloc:
-egetres:
- return err;
-}
-
-static int __devexit mx3_camera_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct mx3_camera_dev *mx3_cam = container_of(soc_host,
- struct mx3_camera_dev, soc_host);
-
- clk_put(mx3_cam->clk);
-
- soc_camera_host_unregister(soc_host);
-
- iounmap(mx3_cam->base);
-
- /*
- * The channel has either not been allocated,
- * or should have been released
- */
- 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();
-
- return 0;
-}
-
-static struct platform_driver mx3_camera_driver = {
- .driver = {
- .name = MX3_CAM_DRV_NAME,
- },
- .probe = mx3_camera_probe,
- .remove = __devexit_p(mx3_camera_remove),
-};
-
-module_platform_driver(mx3_camera_driver);
-
-MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION("0.2.3");
-MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
deleted file mode 100644
index b520a45cb3f3..000000000000
--- a/drivers/media/video/mxb.c
+++ /dev/null
@@ -1,886 +0,0 @@
-/*
- mxb - v4l2 driver for the Multimedia eXtension Board
-
- Copyright (C) 1998-2006 Michael Hunold <michael@mihu.de>
-
- Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html
- for further details about this card.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DEBUG_VARIABLE debug
-
-#include <media/saa7146_vv.h>
-#include <media/tuner.h>
-#include <media/v4l2-common.h>
-#include <media/saa7115.h>
-#include <linux/module.h>
-
-#include "tea6415c.h"
-#include "tea6420.h"
-
-#define MXB_AUDIOS 6
-
-#define I2C_SAA7111A 0x24
-#define I2C_TDA9840 0x42
-#define I2C_TEA6415C 0x43
-#define I2C_TEA6420_1 0x4c
-#define I2C_TEA6420_2 0x4d
-#define I2C_TUNER 0x60
-
-#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
-
-/* global variable */
-static int mxb_num;
-
-/* initial frequence the tuner will be tuned to.
- in verden (lower saxony, germany) 4148 is a
- channel called "phoenix" */
-static int freq = 4148;
-module_param(freq, int, 0644);
-MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup");
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
-
-#define MXB_INPUTS 4
-enum { TUNER, AUX1, AUX3, AUX3_YC };
-
-static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
- { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0,
- V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD },
- { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
- V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
- { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
- V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
- { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
- V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
-};
-
-/* this array holds the information, which port of the saa7146 each
- input actually uses. the mxb uses port 0 for every input */
-static struct {
- int hps_source;
- int hps_sync;
-} input_port_selection[MXB_INPUTS] = {
- { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
- { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
- { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
- { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A },
-};
-
-/* this array holds the information of the audio source (mxb_audios),
- which has to be switched corresponding to the video source (mxb_channels) */
-static int video_audio_connect[MXB_INPUTS] =
- { 0, 1, 3, 3 };
-
-struct mxb_routing {
- u32 input;
- u32 output;
-};
-
-/* these are the available audio sources, which can switched
- to the line- and cd-output individually */
-static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
- {
- .index = 0,
- .name = "Tuner",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 1,
- .name = "AUX1",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 2,
- .name = "AUX2",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 3,
- .name = "AUX3",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 4,
- .name = "Radio (X9)",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 5,
- .name = "CD-ROM (X10)",
- .capability = V4L2_AUDCAP_STEREO,
- }
-};
-
-/* These are the necessary input-output-pins for bringing one audio source
- (see above) to the CD-output. Note that gain is set to 0 in this table. */
-static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
- { { 1, 1 }, { 1, 1 } }, /* Tuner */
- { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
- { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
- { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
- { { 1, 1 }, { 3, 1 } }, /* Radio */
- { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
- { { 6, 1 }, { 6, 1 } } /* Mute */
-};
-
-/* These are the necessary input-output-pins for bringing one audio source
- (see above) to the line-output. Note that gain is set to 0 in this table. */
-static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
- { { 2, 3 }, { 1, 2 } },
- { { 5, 3 }, { 6, 2 } },
- { { 4, 3 }, { 6, 2 } },
- { { 3, 3 }, { 6, 2 } },
- { { 2, 3 }, { 3, 2 } },
- { { 2, 3 }, { 2, 2 } },
- { { 6, 3 }, { 6, 2 } } /* Mute */
-};
-
-struct mxb
-{
- struct video_device *video_dev;
- struct video_device *vbi_dev;
-
- struct i2c_adapter i2c_adapter;
-
- struct v4l2_subdev *saa7111a;
- struct v4l2_subdev *tda9840;
- struct v4l2_subdev *tea6415c;
- struct v4l2_subdev *tuner;
- struct v4l2_subdev *tea6420_1;
- struct v4l2_subdev *tea6420_2;
-
- int cur_mode; /* current audio mode (mono, stereo, ...) */
- int cur_input; /* current input */
- int cur_audinput; /* current audio input */
- int cur_mute; /* current mute status */
- struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
-};
-
-#define saa7111a_call(mxb, o, f, args...) \
- v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
-#define tda9840_call(mxb, o, f, args...) \
- v4l2_subdev_call(mxb->tda9840, o, f, ##args)
-#define tea6415c_call(mxb, o, f, args...) \
- v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
-#define tuner_call(mxb, o, f, args...) \
- v4l2_subdev_call(mxb->tuner, o, f, ##args)
-#define call_all(dev, o, f, args...) \
- v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
-
-static void mxb_update_audmode(struct mxb *mxb)
-{
- struct v4l2_tuner t = {
- .audmode = mxb->cur_mode,
- };
-
- tda9840_call(mxb, tuner, s_tuner, &t);
-}
-
-static inline void tea6420_route(struct mxb *mxb, int idx)
-{
- v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
- TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
- v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
- TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
- v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
- TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
- v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
- TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0);
-}
-
-static struct saa7146_extension extension;
-
-static int mxb_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct saa7146_dev *dev = container_of(ctrl->handler,
- struct saa7146_dev, ctrl_handler);
- struct mxb *mxb = dev->ext_priv;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- mxb->cur_mute = ctrl->val;
- /* switch the audio-source */
- tea6420_route(mxb, ctrl->val ? 6 :
- video_audio_connect[mxb->cur_input]);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops mxb_ctrl_ops = {
- .s_ctrl = mxb_s_ctrl,
-};
-
-static int mxb_probe(struct saa7146_dev *dev)
-{
- struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
- struct mxb *mxb = NULL;
-
- v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops,
- V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
- if (hdl->error)
- return hdl->error;
- mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
- if (mxb == NULL) {
- DEB_D("not enough kernel memory\n");
- return -ENOMEM;
- }
-
-
- snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
-
- saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
- if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
- DEB_S("cannot register i2c-device. skipping.\n");
- kfree(mxb);
- return -EFAULT;
- }
-
- mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "saa7111", I2C_SAA7111A, NULL);
- mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "tea6420", I2C_TEA6420_1, NULL);
- mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "tea6420", I2C_TEA6420_2, NULL);
- mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "tea6415c", I2C_TEA6415C, NULL);
- mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "tda9840", I2C_TDA9840, NULL);
- mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter,
- "tuner", I2C_TUNER, NULL);
-
- /* check if all devices are present */
- if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
- !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) {
- pr_err("did not find all i2c devices. aborting\n");
- i2c_del_adapter(&mxb->i2c_adapter);
- kfree(mxb);
- return -ENODEV;
- }
-
- /* all devices are present, probe was successful */
-
- /* we store the pointer in our private data field */
- dev->ext_priv = mxb;
-
- v4l2_ctrl_handler_setup(hdl);
-
- return 0;
-}
-
-/* some init data for the saa7740, the so-called 'sound arena module'.
- there are no specs available, so we simply use some init values */
-static struct {
- int length;
- char data[9];
-} mxb_saa7740_init[] = {
- { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } },
- { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } },
- { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } },
- { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } },
- { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } },
- { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } },
- { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } },
- { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } },
- { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } },
- { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } },
- { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } },
- { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } },
- { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } },
- { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } },
- { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } },
- { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } },
- { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } },
- { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } },
- { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } },
- { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } },
- { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } },
- { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } },
- { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } },
- { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } },
- { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } },
- { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } },
- { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } },
- { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } },
- { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } },
- { 3, { 0x48, 0x00, 0x01 } },
- { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
- { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } },
- { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
- { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } },
- { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
- { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } },
- { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
- { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } },
- { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
- { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } },
- { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
- { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } },
- { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
- { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } },
- { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
- { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } },
- { 3, { 0x80, 0xb3, 0x0a } },
- {-1, { 0 } }
-};
-
-/* bring hardware to a sane state. this has to be done, just in case someone
- wants to capture from this device before it has been properly initialized.
- the capture engine would badly fail, because no valid signal arrives on the
- saa7146, thus leading to timeouts and stuff. */
-static int mxb_init_done(struct saa7146_dev* dev)
-{
- struct mxb* mxb = (struct mxb*)dev->ext_priv;
- struct i2c_msg msg;
- struct tuner_setup tun_setup;
- v4l2_std_id std = V4L2_STD_PAL_BG;
-
- int i = 0, err = 0;
-
- /* mute audio on tea6420s */
- tea6420_route(mxb, 6);
-
- /* select video mode in saa7111a */
- saa7111a_call(mxb, core, s_std, std);
-
- /* select tuner-output on saa7111a */
- i = 0;
- saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0,
- SAA7111_FMT_CCIR, 0);
-
- /* select a tuner type */
- tun_setup.mode_mask = T_ANALOG_TV;
- tun_setup.addr = ADDR_UNSET;
- tun_setup.type = TUNER_PHILIPS_PAL;
- tuner_call(mxb, tuner, s_type_addr, &tun_setup);
- /* tune in some frequency on tuner */
- mxb->cur_freq.tuner = 0;
- mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
- mxb->cur_freq.frequency = freq;
- tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
-
- /* set a default video standard */
- /* These two gpio calls set the GPIO pins that control the tda9820 */
- saa7146_write(dev, GPIO_CTRL, 0x00404050);
- saa7111a_call(mxb, core, s_gpio, 1);
- saa7111a_call(mxb, core, s_std, std);
- tuner_call(mxb, core, s_std, std);
-
- /* switch to tuner-channel on tea6415c */
- tea6415c_call(mxb, video, s_routing, 3, 17, 0);
-
- /* select tuner-output on multicable on tea6415c */
- tea6415c_call(mxb, video, s_routing, 3, 13, 0);
-
- /* the rest for mxb */
- mxb->cur_input = 0;
- mxb->cur_audinput = video_audio_connect[mxb->cur_input];
- mxb->cur_mute = 1;
-
- mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
- mxb_update_audmode(mxb);
-
- /* check if the saa7740 (aka 'sound arena module') is present
- on the mxb. if so, we must initialize it. due to lack of
- informations about the saa7740, the values were reverse
- engineered. */
- msg.addr = 0x1b;
- msg.flags = 0;
- msg.len = mxb_saa7740_init[0].length;
- msg.buf = &mxb_saa7740_init[0].data[0];
-
- err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
- if (err == 1) {
- /* the sound arena module is a pos, that's probably the reason
- philips refuses to hand out a datasheet for the saa7740...
- it seems to screw up the i2c bus, so we disable fast irq
- based i2c transactions here and rely on the slow and safe
- polling method ... */
- extension.flags &= ~SAA7146_USE_I2C_IRQ;
- for (i = 1; ; i++) {
- if (-1 == mxb_saa7740_init[i].length)
- break;
-
- msg.len = mxb_saa7740_init[i].length;
- msg.buf = &mxb_saa7740_init[i].data[0];
- err = i2c_transfer(&mxb->i2c_adapter, &msg, 1);
- if (err != 1) {
- DEB_D("failed to initialize 'sound arena module'\n");
- goto err;
- }
- }
- pr_info("'sound arena module' detected\n");
- }
-err:
- /* the rest for saa7146: you should definitely set some basic values
- for the input-port handling of the saa7146. */
-
- /* ext->saa has been filled by the core driver */
-
- /* some stuff is done via variables */
- saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source,
- input_port_selection[mxb->cur_input].hps_sync);
-
- /* some stuff is done via direct write to the registers */
-
- /* this is ugly, but because of the fact that this is completely
- hardware dependend, it should be done directly... */
- saa7146_write(dev, DD1_STREAM_B, 0x00000000);
- saa7146_write(dev, DD1_INIT, 0x02000200);
- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
-
- return 0;
-}
-
-/* interrupt-handler. this gets called when irq_mask is != 0.
- it must clear the interrupt-bits in irq_mask it has handled */
-/*
-void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
-{
- struct mxb* mxb = (struct mxb*)dev->ext_priv;
-}
-*/
-
-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
-{
- DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
- if (i->index >= MXB_INPUTS)
- return -EINVAL;
- memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
- *i = mxb->cur_input;
-
- DEB_EE("VIDIOC_G_INPUT %d\n", *i);
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
- int err = 0;
- int i = 0;
-
- DEB_EE("VIDIOC_S_INPUT %d\n", input);
-
- if (input >= MXB_INPUTS)
- return -EINVAL;
-
- mxb->cur_input = input;
-
- saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
- input_port_selection[input].hps_sync);
-
- /* prepare switching of tea6415c and saa7111a;
- have a look at the 'background'-file for further informations */
- switch (input) {
- case TUNER:
- i = SAA7115_COMPOSITE0;
-
- err = tea6415c_call(mxb, video, s_routing, 3, 17, 0);
-
- /* connect tuner-output always to multicable */
- if (!err)
- err = tea6415c_call(mxb, video, s_routing, 3, 13, 0);
- break;
- case AUX3_YC:
- /* nothing to be done here. aux3_yc is
- directly connected to the saa711a */
- i = SAA7115_SVIDEO1;
- break;
- case AUX3:
- /* nothing to be done here. aux3 is
- directly connected to the saa711a */
- i = SAA7115_COMPOSITE1;
- break;
- case AUX1:
- i = SAA7115_COMPOSITE0;
- err = tea6415c_call(mxb, video, s_routing, 1, 17, 0);
- break;
- }
-
- if (err)
- return err;
-
- /* switch video in saa7111a */
- if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
- pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
-
- mxb->cur_audinput = video_audio_connect[input];
- /* switch the audio-source only if necessary */
- if (0 == mxb->cur_mute)
- tea6420_route(mxb, mxb->cur_audinput);
- if (mxb->cur_audinput == 0)
- mxb_update_audmode(mxb);
-
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- if (t->index) {
- DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n",
- t->index);
- return -EINVAL;
- }
-
- DEB_EE("VIDIOC_G_TUNER: %d\n", t->index);
-
- memset(t, 0, sizeof(*t));
- strlcpy(t->name, "TV Tuner", sizeof(t->name));
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- t->audmode = mxb->cur_mode;
- return call_all(dev, tuner, g_tuner, t);
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- if (t->index) {
- DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n",
- t->index);
- return -EINVAL;
- }
-
- mxb->cur_mode = t->audmode;
- return call_all(dev, tuner, s_tuner, t);
-}
-
-static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
-
- return call_all(dev, video, querystd, norm);
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- if (f->tuner)
- return -EINVAL;
- *f = mxb->cur_freq;
-
- DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
- struct saa7146_vv *vv = dev->vv_data;
-
- if (f->tuner)
- return -EINVAL;
-
- if (V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
-
- DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
-
- /* tune in desired frequency */
- tuner_call(mxb, tuner, s_frequency, f);
- /* let the tuner subdev clamp the frequency to the tuner range */
- tuner_call(mxb, tuner, g_frequency, f);
- mxb->cur_freq = *f;
- if (mxb->cur_audinput == 0)
- mxb_update_audmode(mxb);
-
- if (mxb->cur_input)
- return 0;
-
- /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
- spin_lock(&dev->slock);
- vv->vbi_fieldcount = 0;
- spin_unlock(&dev->slock);
-
- return 0;
-}
-
-static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- if (a->index >= MXB_AUDIOS)
- return -EINVAL;
- *a = mxb_audios[a->index];
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- DEB_EE("VIDIOC_G_AUDIO\n");
- *a = mxb_audios[mxb->cur_audinput];
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
- if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) {
- if (mxb->cur_audinput != a->index) {
- mxb->cur_audinput = a->index;
- tea6420_route(mxb, a->index);
- if (mxb->cur_audinput == 0)
- mxb_update_audmode(mxb);
- }
- return 0;
- }
- return -EINVAL;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (v4l2_chip_match_host(&reg->match)) {
- reg->val = saa7146_read(dev, reg->reg);
- reg->size = 4;
- return 0;
- }
- call_all(dev, core, g_register, reg);
- return 0;
-}
-
-static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (v4l2_chip_match_host(&reg->match)) {
- saa7146_write(dev, reg->reg, reg->val);
- reg->size = 4;
- return 0;
- }
- return call_all(dev, core, s_register, reg);
-}
-#endif
-
-static struct saa7146_ext_vv vv_data;
-
-/* this function only gets called when the probing was successful */
-static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
-{
- struct mxb *mxb;
-
- DEB_EE("dev:%p\n", dev);
-
- saa7146_vv_init(dev, &vv_data);
- if (mxb_probe(dev)) {
- saa7146_vv_release(dev);
- return -1;
- }
- mxb = (struct mxb *)dev->ext_priv;
-
- vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
- vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
- vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
- vv_data.vid_ops.vidioc_querystd = vidioc_querystd;
- vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner;
- vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner;
- vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency;
- vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency;
- vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio;
- vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio;
- vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio;
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- vv_data.vid_ops.vidioc_g_register = vidioc_g_register;
- vv_data.vid_ops.vidioc_s_register = vidioc_s_register;
-#endif
- if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
- ERR("cannot register capture v4l2 device. skipping.\n");
- saa7146_vv_release(dev);
- return -1;
- }
-
- /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
- if (MXB_BOARD_CAN_DO_VBI(dev)) {
- if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
- ERR("cannot register vbi v4l2 device. skipping.\n");
- }
- }
-
- pr_info("found Multimedia eXtension Board #%d\n", mxb_num);
-
- mxb_num++;
- mxb_init_done(dev);
- return 0;
-}
-
-static int mxb_detach(struct saa7146_dev *dev)
-{
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- DEB_EE("dev:%p\n", dev);
-
- /* mute audio on tea6420s */
- tea6420_route(mxb, 6);
-
- saa7146_unregister_device(&mxb->video_dev,dev);
- if (MXB_BOARD_CAN_DO_VBI(dev))
- saa7146_unregister_device(&mxb->vbi_dev, dev);
- saa7146_vv_release(dev);
-
- mxb_num--;
-
- i2c_del_adapter(&mxb->i2c_adapter);
- kfree(mxb);
-
- return 0;
-}
-
-static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
-{
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- if (V4L2_STD_PAL_I == standard->id) {
- v4l2_std_id std = V4L2_STD_PAL_I;
-
- DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
- /* These two gpio calls set the GPIO pins that control the tda9820 */
- saa7146_write(dev, GPIO_CTRL, 0x00404050);
- saa7111a_call(mxb, core, s_gpio, 0);
- saa7111a_call(mxb, core, s_std, std);
- if (mxb->cur_input == 0)
- tuner_call(mxb, core, s_std, std);
- } else {
- v4l2_std_id std = V4L2_STD_PAL_BG;
-
- if (mxb->cur_input)
- std = standard->id;
- DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
- /* These two gpio calls set the GPIO pins that control the tda9820 */
- saa7146_write(dev, GPIO_CTRL, 0x00404050);
- saa7111a_call(mxb, core, s_gpio, 1);
- saa7111a_call(mxb, core, s_std, std);
- if (mxb->cur_input == 0)
- tuner_call(mxb, core, s_std, std);
- }
- return 0;
-}
-
-static struct saa7146_standard standard[] = {
- {
- .name = "PAL-BG", .id = V4L2_STD_PAL_BG,
- .v_offset = 0x17, .v_field = 288,
- .h_offset = 0x14, .h_pixels = 680,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "PAL-I", .id = V4L2_STD_PAL_I,
- .v_offset = 0x17, .v_field = 288,
- .h_offset = 0x14, .h_pixels = 680,
- .v_max_out = 576, .h_max_out = 768,
- }, {
- .name = "NTSC", .id = V4L2_STD_NTSC,
- .v_offset = 0x16, .v_field = 240,
- .h_offset = 0x06, .h_pixels = 708,
- .v_max_out = 480, .h_max_out = 640,
- }, {
- .name = "SECAM", .id = V4L2_STD_SECAM,
- .v_offset = 0x14, .v_field = 288,
- .h_offset = 0x14, .h_pixels = 720,
- .v_max_out = 576, .h_max_out = 768,
- }
-};
-
-static struct saa7146_pci_extension_data mxb = {
- .ext_priv = "Multimedia eXtension Board",
- .ext = &extension,
-};
-
-static struct pci_device_id pci_tbl[] = {
- {
- .vendor = PCI_VENDOR_ID_PHILIPS,
- .device = PCI_DEVICE_ID_PHILIPS_SAA7146,
- .subvendor = 0x0000,
- .subdevice = 0x0000,
- .driver_data = (unsigned long)&mxb,
- }, {
- .vendor = 0,
- }
-};
-
-MODULE_DEVICE_TABLE(pci, pci_tbl);
-
-static struct saa7146_ext_vv vv_data = {
- .inputs = MXB_INPUTS,
- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO,
- .stds = &standard[0],
- .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
- .std_callback = &std_callback,
-};
-
-static struct saa7146_extension extension = {
- .name = "Multimedia eXtension Board",
- .flags = SAA7146_USE_I2C_IRQ,
-
- .pci_tbl = &pci_tbl[0],
- .module = THIS_MODULE,
-
- .attach = mxb_attach,
- .detach = mxb_detach,
-
- .irq_mask = 0,
- .irq_func = NULL,
-};
-
-static int __init mxb_init_module(void)
-{
- if (saa7146_register_extension(&extension)) {
- DEB_S("failed to register extension\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void __exit mxb_cleanup_module(void)
-{
- saa7146_unregister_extension(&extension);
-}
-
-module_init(mxb_init_module);
-module_exit(mxb_cleanup_module);
-
-MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'");
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile
deleted file mode 100644
index fc410b438f7d..000000000000
--- a/drivers/media/video/omap/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for the omap video device drivers.
-#
-
-# OMAP2/3 Display driver
-omap-vout-y := omap_vout.o omap_voutlib.o
-omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o
-obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
deleted file mode 100644
index 88cf9d952631..000000000000
--- a/drivers/media/video/omap/omap_vout.c
+++ /dev/null
@@ -1,2289 +0,0 @@
-/*
- * omap_vout.c
- *
- * Copyright (C) 2005-2010 Texas Instruments.
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
- * Leveraged code from the OMAP2 camera driver
- * Video-for-Linux (Version 2) camera capture driver for
- * the OMAP24xx camera controller.
- *
- * Author: Andy Lowe (source@mvista.com)
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2010 Texas Instruments.
- *
- * History:
- * 20-APR-2006 Khasim Modified VRFB based Rotation,
- * The image data is always read from 0 degree
- * view and written
- * to the virtual space of desired rotation angle
- * 4-DEC-2006 Jian Changed to support better memory management
- *
- * 17-Nov-2008 Hardik Changed driver to use video_ioctl2
- *
- * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/videodev2.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-
-#include <media/videobuf-dma-contig.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-
-#include <plat/dma.h>
-#include <plat/vrfb.h>
-#include <video/omapdss.h>
-
-#include "omap_voutlib.h"
-#include "omap_voutdef.h"
-#include "omap_vout_vrfb.h"
-
-MODULE_AUTHOR("Texas Instruments");
-MODULE_DESCRIPTION("OMAP Video for Linux Video out driver");
-MODULE_LICENSE("GPL");
-
-/* Driver Configuration macros */
-#define VOUT_NAME "omap_vout"
-
-enum omap_vout_channels {
- OMAP_VIDEO1,
- OMAP_VIDEO2,
-};
-
-static struct videobuf_queue_ops video_vbq_ops;
-/* Variables configurable through module params*/
-static u32 video1_numbuffers = 3;
-static u32 video2_numbuffers = 3;
-static u32 video1_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
-static u32 video2_bufsize = OMAP_VOUT_MAX_BUF_SIZE;
-static bool vid1_static_vrfb_alloc;
-static bool vid2_static_vrfb_alloc;
-static bool debug;
-
-/* Module parameters */
-module_param(video1_numbuffers, uint, S_IRUGO);
-MODULE_PARM_DESC(video1_numbuffers,
- "Number of buffers to be allocated at init time for Video1 device.");
-
-module_param(video2_numbuffers, uint, S_IRUGO);
-MODULE_PARM_DESC(video2_numbuffers,
- "Number of buffers to be allocated at init time for Video2 device.");
-
-module_param(video1_bufsize, uint, S_IRUGO);
-MODULE_PARM_DESC(video1_bufsize,
- "Size of the buffer to be allocated for video1 device");
-
-module_param(video2_bufsize, uint, S_IRUGO);
-MODULE_PARM_DESC(video2_bufsize,
- "Size of the buffer to be allocated for video2 device");
-
-module_param(vid1_static_vrfb_alloc, bool, S_IRUGO);
-MODULE_PARM_DESC(vid1_static_vrfb_alloc,
- "Static allocation of the VRFB buffer for video1 device");
-
-module_param(vid2_static_vrfb_alloc, bool, S_IRUGO);
-MODULE_PARM_DESC(vid2_static_vrfb_alloc,
- "Static allocation of the VRFB buffer for video2 device");
-
-module_param(debug, bool, S_IRUGO);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-/* list of image formats supported by OMAP2 video pipelines */
-static const struct v4l2_fmtdesc omap_formats[] = {
- {
- /* Note: V4L2 defines RGB565 as:
- *
- * Byte 0 Byte 1
- * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
- *
- * We interpret RGB565 as:
- *
- * Byte 0 Byte 1
- * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
- */
- .description = "RGB565, le",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- },
- {
- /* Note: V4L2 defines RGB32 as: RGB-8-8-8-8 we use
- * this for RGB24 unpack mode, the last 8 bits are ignored
- * */
- .description = "RGB32, le",
- .pixelformat = V4L2_PIX_FMT_RGB32,
- },
- {
- /* Note: V4L2 defines RGB24 as: RGB-8-8-8 we use
- * this for RGB24 packed mode
- *
- */
- .description = "RGB24, le",
- .pixelformat = V4L2_PIX_FMT_RGB24,
- },
- {
- .description = "YUYV (YUV 4:2:2), packed",
- .pixelformat = V4L2_PIX_FMT_YUYV,
- },
- {
- .description = "UYVY, packed",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- },
-};
-
-#define NUM_OUTPUT_FORMATS (ARRAY_SIZE(omap_formats))
-
-/*
- * Try format
- */
-static int omap_vout_try_format(struct v4l2_pix_format *pix)
-{
- int ifmt, bpp = 0;
-
- pix->height = clamp(pix->height, (u32)VID_MIN_HEIGHT,
- (u32)VID_MAX_HEIGHT);
- pix->width = clamp(pix->width, (u32)VID_MIN_WIDTH, (u32)VID_MAX_WIDTH);
-
- for (ifmt = 0; ifmt < NUM_OUTPUT_FORMATS; ifmt++) {
- if (pix->pixelformat == omap_formats[ifmt].pixelformat)
- break;
- }
-
- if (ifmt == NUM_OUTPUT_FORMATS)
- ifmt = 0;
-
- pix->pixelformat = omap_formats[ifmt].pixelformat;
- pix->field = V4L2_FIELD_ANY;
- pix->priv = 0;
-
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- default:
- pix->colorspace = V4L2_COLORSPACE_JPEG;
- bpp = YUYV_BPP;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- bpp = RGB565_BPP;
- break;
- case V4L2_PIX_FMT_RGB24:
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- bpp = RGB24_BPP;
- break;
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- bpp = RGB32_BPP;
- break;
- }
- pix->bytesperline = pix->width * bpp;
- pix->sizeimage = pix->bytesperline * pix->height;
-
- return bpp;
-}
-
-/*
- * omap_vout_uservirt_to_phys: This inline function is used to convert user
- * space virtual address to physical address.
- */
-static u32 omap_vout_uservirt_to_phys(u32 virtp)
-{
- unsigned long physp = 0;
- struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
-
- vma = find_vma(mm, virtp);
- /* For kernel direct-mapped memory, take the easy way */
- if (virtp >= PAGE_OFFSET) {
- physp = virt_to_phys((void *) virtp);
- } else if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
- /* this will catch, kernel-allocated, mmaped-to-usermode
- addresses */
- physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
- } else {
- /* otherwise, use get_user_pages() for general userland pages */
- int res, nr_pages = 1;
- struct page *pages;
- down_read(&current->mm->mmap_sem);
-
- res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
- 0, &pages, NULL);
- up_read(&current->mm->mmap_sem);
-
- if (res == nr_pages) {
- physp = __pa(page_address(&pages[0]) +
- (virtp & ~PAGE_MASK));
- } else {
- printk(KERN_WARNING VOUT_NAME
- "get_user_pages failed\n");
- return 0;
- }
- }
-
- return physp;
-}
-
-/*
- * Free the V4L2 buffers
- */
-void omap_vout_free_buffers(struct omap_vout_device *vout)
-{
- int i, numbuffers;
-
- /* Allocate memory for the buffers */
- numbuffers = (vout->vid) ? video2_numbuffers : video1_numbuffers;
- vout->buffer_size = (vout->vid) ? video2_bufsize : video1_bufsize;
-
- for (i = 0; i < numbuffers; i++) {
- omap_vout_free_buffer(vout->buf_virt_addr[i],
- vout->buffer_size);
- vout->buf_phy_addr[i] = 0;
- vout->buf_virt_addr[i] = 0;
- }
-}
-
-/*
- * Convert V4L2 rotation to DSS rotation
- * V4L2 understand 0, 90, 180, 270.
- * Convert to 0, 1, 2 and 3 respectively for DSS
- */
-static int v4l2_rot_to_dss_rot(int v4l2_rotation,
- enum dss_rotation *rotation, bool mirror)
-{
- int ret = 0;
-
- switch (v4l2_rotation) {
- case 90:
- *rotation = dss_rotation_90_degree;
- break;
- case 180:
- *rotation = dss_rotation_180_degree;
- break;
- case 270:
- *rotation = dss_rotation_270_degree;
- break;
- case 0:
- *rotation = dss_rotation_0_degree;
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int omap_vout_calculate_offset(struct omap_vout_device *vout)
-{
- struct omapvideo_info *ovid;
- struct v4l2_rect *crop = &vout->crop;
- struct v4l2_pix_format *pix = &vout->pix;
- int *cropped_offset = &vout->cropped_offset;
- int ps = 2, line_length = 0;
-
- ovid = &vout->vid_info;
-
- if (ovid->rotation_type == VOUT_ROT_VRFB) {
- omap_vout_calculate_vrfb_offset(vout);
- } else {
- vout->line_length = line_length = pix->width;
-
- if (V4L2_PIX_FMT_YUYV == pix->pixelformat ||
- V4L2_PIX_FMT_UYVY == pix->pixelformat)
- ps = 2;
- else if (V4L2_PIX_FMT_RGB32 == pix->pixelformat)
- ps = 4;
- else if (V4L2_PIX_FMT_RGB24 == pix->pixelformat)
- ps = 3;
-
- vout->ps = ps;
-
- *cropped_offset = (line_length * ps) *
- crop->top + crop->left * ps;
- }
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "%s Offset:%x\n",
- __func__, vout->cropped_offset);
-
- return 0;
-}
-
-/*
- * Convert V4L2 pixel format to DSS pixel format
- */
-static int video_mode_to_dss_mode(struct omap_vout_device *vout)
-{
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct v4l2_pix_format *pix = &vout->pix;
- enum omap_color_mode mode;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- switch (pix->pixelformat) {
- case 0:
- break;
- case V4L2_PIX_FMT_YUYV:
- mode = OMAP_DSS_COLOR_YUV2;
- break;
- case V4L2_PIX_FMT_UYVY:
- mode = OMAP_DSS_COLOR_UYVY;
- break;
- case V4L2_PIX_FMT_RGB565:
- mode = OMAP_DSS_COLOR_RGB16;
- break;
- case V4L2_PIX_FMT_RGB24:
- mode = OMAP_DSS_COLOR_RGB24P;
- break;
- case V4L2_PIX_FMT_RGB32:
- mode = (ovl->id == OMAP_DSS_VIDEO1) ?
- OMAP_DSS_COLOR_RGB24U : OMAP_DSS_COLOR_ARGB32;
- break;
- case V4L2_PIX_FMT_BGR32:
- mode = OMAP_DSS_COLOR_RGBX32;
- break;
- default:
- mode = -EINVAL;
- }
- return mode;
-}
-
-/*
- * Setup the overlay
- */
-static int omapvid_setup_overlay(struct omap_vout_device *vout,
- struct omap_overlay *ovl, int posx, int posy, int outw,
- int outh, u32 addr)
-{
- int ret = 0;
- struct omap_overlay_info info;
- int cropheight, cropwidth, pixheight, pixwidth;
-
- if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0 &&
- (outw != vout->pix.width || outh != vout->pix.height)) {
- ret = -EINVAL;
- goto setup_ovl_err;
- }
-
- vout->dss_mode = video_mode_to_dss_mode(vout);
- if (vout->dss_mode == -EINVAL) {
- ret = -EINVAL;
- goto setup_ovl_err;
- }
-
- /* Setup the input plane parameters according to
- * rotation value selected.
- */
- if (is_rotation_90_or_270(vout)) {
- cropheight = vout->crop.width;
- cropwidth = vout->crop.height;
- pixheight = vout->pix.width;
- pixwidth = vout->pix.height;
- } else {
- cropheight = vout->crop.height;
- cropwidth = vout->crop.width;
- pixheight = vout->pix.height;
- pixwidth = vout->pix.width;
- }
-
- ovl->get_overlay_info(ovl, &info);
- info.paddr = addr;
- info.width = cropwidth;
- info.height = cropheight;
- info.color_mode = vout->dss_mode;
- info.mirror = vout->mirror;
- info.pos_x = posx;
- info.pos_y = posy;
- info.out_width = outw;
- info.out_height = outh;
- info.global_alpha = vout->win.global_alpha;
- if (!is_rotation_enabled(vout)) {
- info.rotation = 0;
- info.rotation_type = OMAP_DSS_ROT_DMA;
- info.screen_width = pixwidth;
- } else {
- info.rotation = vout->rotation;
- info.rotation_type = OMAP_DSS_ROT_VRFB;
- info.screen_width = 2048;
- }
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- "%s enable=%d addr=%x width=%d\n height=%d color_mode=%d\n"
- "rotation=%d mirror=%d posx=%d posy=%d out_width = %d \n"
- "out_height=%d rotation_type=%d screen_width=%d\n",
- __func__, ovl->is_enabled(ovl), info.paddr, info.width, info.height,
- info.color_mode, info.rotation, info.mirror, info.pos_x,
- info.pos_y, info.out_width, info.out_height, info.rotation_type,
- info.screen_width);
-
- ret = ovl->set_overlay_info(ovl, &info);
- if (ret)
- goto setup_ovl_err;
-
- return 0;
-
-setup_ovl_err:
- v4l2_warn(&vout->vid_dev->v4l2_dev, "setup_overlay failed\n");
- return ret;
-}
-
-/*
- * Initialize the overlay structure
- */
-static int omapvid_init(struct omap_vout_device *vout, u32 addr)
-{
- int ret = 0, i;
- struct v4l2_window *win;
- struct omap_overlay *ovl;
- int posx, posy, outw, outh, temp;
- struct omap_video_timings *timing;
- struct omapvideo_info *ovid = &vout->vid_info;
-
- win = &vout->win;
- for (i = 0; i < ovid->num_overlays; i++) {
- ovl = ovid->overlays[i];
- if (!ovl->manager || !ovl->manager->device)
- return -EINVAL;
-
- timing = &ovl->manager->device->panel.timings;
-
- outw = win->w.width;
- outh = win->w.height;
- switch (vout->rotation) {
- case dss_rotation_90_degree:
- /* Invert the height and width for 90
- * and 270 degree rotation
- */
- temp = outw;
- outw = outh;
- outh = temp;
- posy = (timing->y_res - win->w.width) - win->w.left;
- posx = win->w.top;
- break;
-
- case dss_rotation_180_degree:
- posx = (timing->x_res - win->w.width) - win->w.left;
- posy = (timing->y_res - win->w.height) - win->w.top;
- break;
-
- case dss_rotation_270_degree:
- temp = outw;
- outw = outh;
- outh = temp;
- posy = win->w.left;
- posx = (timing->x_res - win->w.height) - win->w.top;
- break;
-
- default:
- posx = win->w.left;
- posy = win->w.top;
- break;
- }
-
- ret = omapvid_setup_overlay(vout, ovl, posx, posy,
- outw, outh, addr);
- if (ret)
- goto omapvid_init_err;
- }
- return 0;
-
-omapvid_init_err:
- v4l2_warn(&vout->vid_dev->v4l2_dev, "apply_changes failed\n");
- return ret;
-}
-
-/*
- * Apply the changes set the go bit of DSS
- */
-static int omapvid_apply_changes(struct omap_vout_device *vout)
-{
- int i;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid = &vout->vid_info;
-
- for (i = 0; i < ovid->num_overlays; i++) {
- ovl = ovid->overlays[i];
- if (!ovl->manager || !ovl->manager->device)
- return -EINVAL;
- ovl->manager->apply(ovl->manager);
- }
-
- return 0;
-}
-
-static int omapvid_handle_interlace_display(struct omap_vout_device *vout,
- unsigned int irqstatus, struct timeval timevalue)
-{
- u32 fid;
-
- if (vout->first_int) {
- vout->first_int = 0;
- goto err;
- }
-
- if (irqstatus & DISPC_IRQ_EVSYNC_ODD)
- fid = 1;
- else if (irqstatus & DISPC_IRQ_EVSYNC_EVEN)
- fid = 0;
- else
- goto err;
-
- vout->field_id ^= 1;
- if (fid != vout->field_id) {
- if (fid == 0)
- vout->field_id = fid;
- } else if (0 == fid) {
- if (vout->cur_frm == vout->next_frm)
- goto err;
-
- vout->cur_frm->ts = timevalue;
- vout->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&vout->cur_frm->done);
- vout->cur_frm = vout->next_frm;
- } else {
- if (list_empty(&vout->dma_queue) ||
- (vout->cur_frm != vout->next_frm))
- goto err;
- }
-
- return vout->field_id;
-err:
- return 0;
-}
-
-static void omap_vout_isr(void *arg, unsigned int irqstatus)
-{
- int ret, fid, mgr_id;
- u32 addr, irq;
- struct omap_overlay *ovl;
- struct timeval timevalue;
- struct omapvideo_info *ovid;
- struct omap_dss_device *cur_display;
- struct omap_vout_device *vout = (struct omap_vout_device *)arg;
-
- if (!vout->streaming)
- return;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
- /* get the display device attached to the overlay */
- if (!ovl->manager || !ovl->manager->device)
- return;
-
- mgr_id = ovl->manager->id;
- cur_display = ovl->manager->device;
-
- spin_lock(&vout->vbq_lock);
- do_gettimeofday(&timevalue);
-
- switch (cur_display->type) {
- case OMAP_DISPLAY_TYPE_DSI:
- case OMAP_DISPLAY_TYPE_DPI:
- if (mgr_id == OMAP_DSS_CHANNEL_LCD)
- irq = DISPC_IRQ_VSYNC;
- else if (mgr_id == OMAP_DSS_CHANNEL_LCD2)
- irq = DISPC_IRQ_VSYNC2;
- else
- goto vout_isr_err;
-
- if (!(irqstatus & irq))
- goto vout_isr_err;
- break;
- case OMAP_DISPLAY_TYPE_VENC:
- fid = omapvid_handle_interlace_display(vout, irqstatus,
- timevalue);
- if (!fid)
- goto vout_isr_err;
- break;
- case OMAP_DISPLAY_TYPE_HDMI:
- if (!(irqstatus & DISPC_IRQ_EVSYNC_EVEN))
- goto vout_isr_err;
- break;
- default:
- goto vout_isr_err;
- }
-
- if (!vout->first_int && (vout->cur_frm != vout->next_frm)) {
- vout->cur_frm->ts = timevalue;
- vout->cur_frm->state = VIDEOBUF_DONE;
- wake_up_interruptible(&vout->cur_frm->done);
- vout->cur_frm = vout->next_frm;
- }
-
- vout->first_int = 0;
- if (list_empty(&vout->dma_queue))
- goto vout_isr_err;
-
- vout->next_frm = list_entry(vout->dma_queue.next,
- struct videobuf_buffer, queue);
- list_del(&vout->next_frm->queue);
-
- vout->next_frm->state = VIDEOBUF_ACTIVE;
-
- addr = (unsigned long) vout->queued_buf_addr[vout->next_frm->i]
- + vout->cropped_offset;
-
- /* First save the configuration in ovelray structure */
- ret = omapvid_init(vout, addr);
- if (ret)
- printk(KERN_ERR VOUT_NAME
- "failed to set overlay info\n");
- /* Enable the pipeline and set the Go bit */
- ret = omapvid_apply_changes(vout);
- if (ret)
- printk(KERN_ERR VOUT_NAME "failed to change mode\n");
-
-vout_isr_err:
- spin_unlock(&vout->vbq_lock);
-}
-
-/* Video buffer call backs */
-
-/*
- * Buffer setup function is called by videobuf layer when REQBUF ioctl is
- * called. This is used to setup buffers and return size and count of
- * buffers allocated. After the call to this buffer, videobuf layer will
- * setup buffer queue depending on the size and count of buffers
- */
-static int omap_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
-{
- int startindex = 0, i, j;
- u32 phy_addr = 0, virt_addr = 0;
- struct omap_vout_device *vout = q->priv_data;
- struct omapvideo_info *ovid = &vout->vid_info;
- int vid_max_buf_size;
-
- if (!vout)
- return -EINVAL;
-
- vid_max_buf_size = vout->vid == OMAP_VIDEO1 ? video1_bufsize :
- video2_bufsize;
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type)
- return -EINVAL;
-
- startindex = (vout->vid == OMAP_VIDEO1) ?
- video1_numbuffers : video2_numbuffers;
- if (V4L2_MEMORY_MMAP == vout->memory && *count < startindex)
- *count = startindex;
-
- if (ovid->rotation_type == VOUT_ROT_VRFB) {
- if (omap_vout_vrfb_buffer_setup(vout, count, startindex))
- return -ENOMEM;
- }
-
- if (V4L2_MEMORY_MMAP != vout->memory)
- return 0;
-
- /* Now allocated the V4L2 buffers */
- *size = PAGE_ALIGN(vout->pix.width * vout->pix.height * vout->bpp);
- startindex = (vout->vid == OMAP_VIDEO1) ?
- video1_numbuffers : video2_numbuffers;
-
- /* Check the size of the buffer */
- if (*size > vid_max_buf_size) {
- v4l2_err(&vout->vid_dev->v4l2_dev,
- "buffer allocation mismatch [%u] [%u]\n",
- *size, vout->buffer_size);
- return -ENOMEM;
- }
-
- for (i = startindex; i < *count; i++) {
- vout->buffer_size = *size;
-
- virt_addr = omap_vout_alloc_buffer(vout->buffer_size,
- &phy_addr);
- if (!virt_addr) {
- if (ovid->rotation_type == VOUT_ROT_NONE) {
- break;
- } else {
- if (!is_rotation_enabled(vout))
- break;
- /* Free the VRFB buffers if no space for V4L2 buffers */
- for (j = i; j < *count; j++) {
- omap_vout_free_buffer(
- vout->smsshado_virt_addr[j],
- vout->smsshado_size);
- vout->smsshado_virt_addr[j] = 0;
- vout->smsshado_phy_addr[j] = 0;
- }
- }
- }
- vout->buf_virt_addr[i] = virt_addr;
- vout->buf_phy_addr[i] = phy_addr;
- }
- *count = vout->buffer_allocated = i;
-
- return 0;
-}
-
-/*
- * Free the V4L2 buffers additionally allocated than default
- * number of buffers
- */
-static void omap_vout_free_extra_buffers(struct omap_vout_device *vout)
-{
- int num_buffers = 0, i;
-
- num_buffers = (vout->vid == OMAP_VIDEO1) ?
- video1_numbuffers : video2_numbuffers;
-
- for (i = num_buffers; i < vout->buffer_allocated; i++) {
- if (vout->buf_virt_addr[i])
- omap_vout_free_buffer(vout->buf_virt_addr[i],
- vout->buffer_size);
-
- vout->buf_virt_addr[i] = 0;
- vout->buf_phy_addr[i] = 0;
- }
- vout->buffer_allocated = num_buffers;
-}
-
-/*
- * This function will be called when VIDIOC_QBUF ioctl is called.
- * It prepare buffers before give out for the display. This function
- * converts user space virtual address into physical address if userptr memory
- * exchange mechanism is used. If rotation is enabled, it copies entire
- * buffer into VRFB memory space before giving it to the DSS.
- */
-static int omap_vout_buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct omap_vout_device *vout = q->priv_data;
- struct omapvideo_info *ovid = &vout->vid_info;
-
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->width = vout->pix.width;
- vb->height = vout->pix.height;
- vb->size = vb->width * vb->height * vout->bpp;
- vb->field = field;
- }
- vb->state = VIDEOBUF_PREPARED;
- /* if user pointer memory mechanism is used, get the physical
- * address of the buffer
- */
- if (V4L2_MEMORY_USERPTR == vb->memory) {
- if (0 == vb->baddr)
- return -EINVAL;
- /* Physical address */
- vout->queued_buf_addr[vb->i] = (u8 *)
- omap_vout_uservirt_to_phys(vb->baddr);
- } else {
- u32 addr, dma_addr;
- unsigned long size;
-
- addr = (unsigned long) vout->buf_virt_addr[vb->i];
- size = (unsigned long) vb->size;
-
- dma_addr = dma_map_single(vout->vid_dev->v4l2_dev.dev, (void *) addr,
- size, DMA_TO_DEVICE);
- if (dma_mapping_error(vout->vid_dev->v4l2_dev.dev, dma_addr))
- v4l2_err(&vout->vid_dev->v4l2_dev, "dma_map_single failed\n");
-
- vout->queued_buf_addr[vb->i] = (u8 *)vout->buf_phy_addr[vb->i];
- }
-
- if (ovid->rotation_type == VOUT_ROT_VRFB)
- return omap_vout_prepare_vrfb(vout, vb);
- else
- return 0;
-}
-
-/*
- * Buffer queue function will be called from the videobuf layer when _QBUF
- * ioctl is called. It is used to enqueue buffer, which is ready to be
- * displayed.
- */
-static void omap_vout_buffer_queue(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct omap_vout_device *vout = q->priv_data;
-
- /* Driver is also maintainig a queue. So enqueue buffer in the driver
- * queue */
- list_add_tail(&vb->queue, &vout->dma_queue);
-
- vb->state = VIDEOBUF_QUEUED;
-}
-
-/*
- * Buffer release function is called from videobuf layer to release buffer
- * which are already allocated
- */
-static void omap_vout_buffer_release(struct videobuf_queue *q,
- struct videobuf_buffer *vb)
-{
- struct omap_vout_device *vout = q->priv_data;
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-
- if (V4L2_MEMORY_MMAP != vout->memory)
- return;
-}
-
-/*
- * File operations
- */
-static unsigned int omap_vout_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct omap_vout_device *vout = file->private_data;
- struct videobuf_queue *q = &vout->vbq;
-
- return videobuf_poll_stream(file, q, wait);
-}
-
-static void omap_vout_vm_open(struct vm_area_struct *vma)
-{
- struct omap_vout_device *vout = vma->vm_private_data;
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- "vm_open [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
- vout->mmap_count++;
-}
-
-static void omap_vout_vm_close(struct vm_area_struct *vma)
-{
- struct omap_vout_device *vout = vma->vm_private_data;
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- "vm_close [vma=%08lx-%08lx]\n", vma->vm_start, vma->vm_end);
- vout->mmap_count--;
-}
-
-static struct vm_operations_struct omap_vout_vm_ops = {
- .open = omap_vout_vm_open,
- .close = omap_vout_vm_close,
-};
-
-static int omap_vout_mmap(struct file *file, struct vm_area_struct *vma)
-{
- int i;
- void *pos;
- unsigned long start = vma->vm_start;
- unsigned long size = (vma->vm_end - vma->vm_start);
- struct omap_vout_device *vout = file->private_data;
- struct videobuf_queue *q = &vout->vbq;
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- " %s pgoff=0x%lx, start=0x%lx, end=0x%lx\n", __func__,
- vma->vm_pgoff, vma->vm_start, vma->vm_end);
-
- /* look for the buffer to map */
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == q->bufs[i])
- continue;
- if (V4L2_MEMORY_MMAP != q->bufs[i]->memory)
- continue;
- if (q->bufs[i]->boff == (vma->vm_pgoff << PAGE_SHIFT))
- break;
- }
-
- if (VIDEO_MAX_FRAME == i) {
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev,
- "offset invalid [offset=0x%lx]\n",
- (vma->vm_pgoff << PAGE_SHIFT));
- return -EINVAL;
- }
- /* Check the size of the buffer */
- if (size > vout->buffer_size) {
- v4l2_err(&vout->vid_dev->v4l2_dev,
- "insufficient memory [%lu] [%u]\n",
- size, vout->buffer_size);
- return -ENOMEM;
- }
-
- q->bufs[i]->baddr = vma->vm_start;
-
- vma->vm_flags |= VM_RESERVED;
- vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
- vma->vm_ops = &omap_vout_vm_ops;
- vma->vm_private_data = (void *) vout;
- pos = (void *)vout->buf_virt_addr[i];
- vma->vm_pgoff = virt_to_phys((void *)pos) >> PAGE_SHIFT;
- while (size > 0) {
- unsigned long pfn;
- pfn = virt_to_phys((void *) pos) >> PAGE_SHIFT;
- if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
- vout->mmap_count++;
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
-
- return 0;
-}
-
-static int omap_vout_release(struct file *file)
-{
- unsigned int ret, i;
- struct videobuf_queue *q;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout = file->private_data;
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
- ovid = &vout->vid_info;
-
- if (!vout)
- return 0;
-
- q = &vout->vbq;
- /* Disable all the overlay managers connected with this interface */
- for (i = 0; i < ovid->num_overlays; i++) {
- struct omap_overlay *ovl = ovid->overlays[i];
- if (ovl->manager && ovl->manager->device)
- ovl->disable(ovl);
- }
- /* Turn off the pipeline */
- ret = omapvid_apply_changes(vout);
- if (ret)
- v4l2_warn(&vout->vid_dev->v4l2_dev,
- "Unable to apply changes\n");
-
- /* Free all buffers */
- omap_vout_free_extra_buffers(vout);
-
- /* Free the VRFB buffers only if they are allocated
- * during reqbufs. Don't free if init time allocated
- */
- if (ovid->rotation_type == VOUT_ROT_VRFB) {
- if (!vout->vrfb_static_allocation)
- omap_vout_free_vrfb_buffers(vout);
- }
- videobuf_mmap_free(q);
-
- /* Even if apply changes fails we should continue
- freeing allocated memory */
- if (vout->streaming) {
- u32 mask = 0;
-
- mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN |
- DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_VSYNC2;
- omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
- vout->streaming = 0;
-
- videobuf_streamoff(q);
- videobuf_queue_cancel(q);
- }
-
- if (vout->mmap_count != 0)
- vout->mmap_count = 0;
-
- vout->opened -= 1;
- file->private_data = NULL;
-
- if (vout->buffer_allocated)
- videobuf_mmap_free(q);
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
- return ret;
-}
-
-static int omap_vout_open(struct file *file)
-{
- struct videobuf_queue *q;
- struct omap_vout_device *vout = NULL;
-
- vout = video_drvdata(file);
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Entering %s\n", __func__);
-
- if (vout == NULL)
- return -ENODEV;
-
- /* for now, we only support single open */
- if (vout->opened)
- return -EBUSY;
-
- vout->opened += 1;
-
- file->private_data = vout;
- vout->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-
- q = &vout->vbq;
- video_vbq_ops.buf_setup = omap_vout_buffer_setup;
- video_vbq_ops.buf_prepare = omap_vout_buffer_prepare;
- video_vbq_ops.buf_release = omap_vout_buffer_release;
- video_vbq_ops.buf_queue = omap_vout_buffer_queue;
- spin_lock_init(&vout->vbq_lock);
-
- videobuf_queue_dma_contig_init(q, &video_vbq_ops, q->dev,
- &vout->vbq_lock, vout->type, V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer), vout, NULL);
-
- v4l2_dbg(1, debug, &vout->vid_dev->v4l2_dev, "Exiting %s\n", __func__);
- return 0;
-}
-
-/*
- * V4L2 ioctls
- */
-static int vidioc_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct omap_vout_device *vout = fh;
-
- strlcpy(cap->driver, VOUT_NAME, sizeof(cap->driver));
- strlcpy(cap->card, vout->vfd->name, sizeof(cap->card));
- cap->bus_info[0] = '\0';
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- int index = fmt->index;
-
- if (index >= NUM_OUTPUT_FORMATS)
- return -EINVAL;
-
- fmt->flags = omap_formats[index].flags;
- strlcpy(fmt->description, omap_formats[index].description,
- sizeof(fmt->description));
- fmt->pixelformat = omap_formats[index].pixelformat;
-
- return 0;
-}
-
-static int vidioc_g_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap_vout_device *vout = fh;
-
- f->fmt.pix = vout->pix;
- return 0;
-
-}
-
-static int vidioc_try_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_video_timings *timing;
- struct omap_vout_device *vout = fh;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- if (!ovl->manager || !ovl->manager->device)
- return -EINVAL;
- /* get the display device attached to the overlay */
- timing = &ovl->manager->device->panel.timings;
-
- vout->fbuf.fmt.height = timing->y_res;
- vout->fbuf.fmt.width = timing->x_res;
-
- omap_vout_try_format(&f->fmt.pix);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_out(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- int ret, bpp;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_video_timings *timing;
- struct omap_vout_device *vout = fh;
-
- if (vout->streaming)
- return -EBUSY;
-
- mutex_lock(&vout->lock);
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- /* get the display device attached to the overlay */
- if (!ovl->manager || !ovl->manager->device) {
- ret = -EINVAL;
- goto s_fmt_vid_out_exit;
- }
- timing = &ovl->manager->device->panel.timings;
-
- /* We dont support RGB24-packed mode if vrfb rotation
- * is enabled*/
- if ((is_rotation_enabled(vout)) &&
- f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) {
- ret = -EINVAL;
- goto s_fmt_vid_out_exit;
- }
-
- /* get the framebuffer parameters */
-
- if (is_rotation_90_or_270(vout)) {
- vout->fbuf.fmt.height = timing->x_res;
- vout->fbuf.fmt.width = timing->y_res;
- } else {
- vout->fbuf.fmt.height = timing->y_res;
- vout->fbuf.fmt.width = timing->x_res;
- }
-
- /* change to samller size is OK */
-
- bpp = omap_vout_try_format(&f->fmt.pix);
- f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height * bpp;
-
- /* try & set the new output format */
- vout->bpp = bpp;
- vout->pix = f->fmt.pix;
- vout->vrfb_bpp = 1;
-
- /* If YUYV then vrfb bpp is 2, for others its 1 */
- if (V4L2_PIX_FMT_YUYV == vout->pix.pixelformat ||
- V4L2_PIX_FMT_UYVY == vout->pix.pixelformat)
- vout->vrfb_bpp = 2;
-
- /* set default crop and win */
- omap_vout_new_format(&vout->pix, &vout->fbuf, &vout->crop, &vout->win);
-
- /* Save the changes in the overlay strcuture */
- ret = omapvid_init(vout, 0);
- if (ret) {
- v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
- goto s_fmt_vid_out_exit;
- }
-
- ret = 0;
-
-s_fmt_vid_out_exit:
- mutex_unlock(&vout->lock);
- return ret;
-}
-
-static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- int ret = 0;
- struct omap_vout_device *vout = fh;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct v4l2_window *win = &f->fmt.win;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- ret = omap_vout_try_window(&vout->fbuf, win);
-
- if (!ret) {
- if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
- win->global_alpha = 255;
- else
- win->global_alpha = f->fmt.win.global_alpha;
- }
-
- return ret;
-}
-
-static int vidioc_s_fmt_vid_overlay(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- int ret = 0;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout = fh;
- struct v4l2_window *win = &f->fmt.win;
-
- mutex_lock(&vout->lock);
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- ret = omap_vout_new_window(&vout->crop, &vout->win, &vout->fbuf, win);
- if (!ret) {
- /* Video1 plane does not support global alpha on OMAP3 */
- if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
- vout->win.global_alpha = 255;
- else
- vout->win.global_alpha = f->fmt.win.global_alpha;
-
- vout->win.chromakey = f->fmt.win.chromakey;
- }
- mutex_unlock(&vout->lock);
- return ret;
-}
-
-static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh,
- struct v4l2_fmtdesc *fmt)
-{
- int index = fmt->index;
-
- if (index >= NUM_OUTPUT_FORMATS)
- return -EINVAL;
-
- fmt->flags = omap_formats[index].flags;
- strlcpy(fmt->description, omap_formats[index].description,
- sizeof(fmt->description));
- fmt->pixelformat = omap_formats[index].pixelformat;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- u32 key_value = 0;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout = fh;
- struct omap_overlay_manager_info info;
- struct v4l2_window *win = &f->fmt.win;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- win->w = vout->win.w;
- win->field = vout->win.field;
- win->global_alpha = vout->win.global_alpha;
-
- if (ovl->manager && ovl->manager->get_manager_info) {
- ovl->manager->get_manager_info(ovl->manager, &info);
- key_value = info.trans_key;
- }
- win->chromakey = key_value;
- return 0;
-}
-
-static int vidioc_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cropcap)
-{
- struct omap_vout_device *vout = fh;
- struct v4l2_pix_format *pix = &vout->pix;
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- /* Width and height are always even */
- cropcap->bounds.width = pix->width & ~1;
- cropcap->bounds.height = pix->height & ~1;
-
- omap_vout_default_crop(&vout->pix, &vout->fbuf, &cropcap->defrect);
- cropcap->pixelaspect.numerator = 1;
- cropcap->pixelaspect.denominator = 1;
- return 0;
-}
-
-static int vidioc_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct omap_vout_device *vout = fh;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
- crop->c = vout->crop;
- return 0;
-}
-
-static int vidioc_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- int ret = -EINVAL;
- struct omap_vout_device *vout = fh;
- struct omapvideo_info *ovid;
- struct omap_overlay *ovl;
- struct omap_video_timings *timing;
-
- if (vout->streaming)
- return -EBUSY;
-
- mutex_lock(&vout->lock);
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- if (!ovl->manager || !ovl->manager->device) {
- ret = -EINVAL;
- goto s_crop_err;
- }
- /* get the display device attached to the overlay */
- timing = &ovl->manager->device->panel.timings;
-
- if (is_rotation_90_or_270(vout)) {
- vout->fbuf.fmt.height = timing->x_res;
- vout->fbuf.fmt.width = timing->y_res;
- } else {
- vout->fbuf.fmt.height = timing->y_res;
- vout->fbuf.fmt.width = timing->x_res;
- }
-
- if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- ret = omap_vout_new_crop(&vout->pix, &vout->crop, &vout->win,
- &vout->fbuf, &crop->c);
-
-s_crop_err:
- mutex_unlock(&vout->lock);
- return ret;
-}
-
-static int vidioc_queryctrl(struct file *file, void *fh,
- struct v4l2_queryctrl *ctrl)
-{
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_ROTATE:
- ret = v4l2_ctrl_query_fill(ctrl, 0, 270, 90, 0);
- break;
- case V4L2_CID_BG_COLOR:
- ret = v4l2_ctrl_query_fill(ctrl, 0, 0xFFFFFF, 1, 0);
- break;
- case V4L2_CID_VFLIP:
- ret = v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0);
- break;
- default:
- ctrl->name[0] = '\0';
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
-{
- int ret = 0;
- struct omap_vout_device *vout = fh;
-
- switch (ctrl->id) {
- case V4L2_CID_ROTATE:
- ctrl->value = vout->control[0].value;
- break;
- case V4L2_CID_BG_COLOR:
- {
- struct omap_overlay_manager_info info;
- struct omap_overlay *ovl;
-
- ovl = vout->vid_info.overlays[0];
- if (!ovl->manager || !ovl->manager->get_manager_info) {
- ret = -EINVAL;
- break;
- }
-
- ovl->manager->get_manager_info(ovl->manager, &info);
- ctrl->value = info.default_color;
- break;
- }
- case V4L2_CID_VFLIP:
- ctrl->value = vout->control[2].value;
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
-{
- int ret = 0;
- struct omap_vout_device *vout = fh;
-
- switch (a->id) {
- case V4L2_CID_ROTATE:
- {
- struct omapvideo_info *ovid;
- int rotation = a->value;
-
- ovid = &vout->vid_info;
-
- mutex_lock(&vout->lock);
- if (rotation && ovid->rotation_type == VOUT_ROT_NONE) {
- mutex_unlock(&vout->lock);
- ret = -ERANGE;
- break;
- }
-
- if (rotation && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
- mutex_unlock(&vout->lock);
- ret = -EINVAL;
- break;
- }
-
- if (v4l2_rot_to_dss_rot(rotation, &vout->rotation,
- vout->mirror)) {
- mutex_unlock(&vout->lock);
- ret = -EINVAL;
- break;
- }
-
- vout->control[0].value = rotation;
- mutex_unlock(&vout->lock);
- break;
- }
- case V4L2_CID_BG_COLOR:
- {
- struct omap_overlay *ovl;
- unsigned int color = a->value;
- struct omap_overlay_manager_info info;
-
- ovl = vout->vid_info.overlays[0];
-
- mutex_lock(&vout->lock);
- if (!ovl->manager || !ovl->manager->get_manager_info) {
- mutex_unlock(&vout->lock);
- ret = -EINVAL;
- break;
- }
-
- ovl->manager->get_manager_info(ovl->manager, &info);
- info.default_color = color;
- if (ovl->manager->set_manager_info(ovl->manager, &info)) {
- mutex_unlock(&vout->lock);
- ret = -EINVAL;
- break;
- }
-
- vout->control[1].value = color;
- mutex_unlock(&vout->lock);
- break;
- }
- case V4L2_CID_VFLIP:
- {
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- unsigned int mirror = a->value;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- mutex_lock(&vout->lock);
- if (mirror && ovid->rotation_type == VOUT_ROT_NONE) {
- mutex_unlock(&vout->lock);
- ret = -ERANGE;
- break;
- }
-
- if (mirror && vout->pix.pixelformat == V4L2_PIX_FMT_RGB24) {
- mutex_unlock(&vout->lock);
- ret = -EINVAL;
- break;
- }
- vout->mirror = mirror;
- vout->control[2].value = mirror;
- mutex_unlock(&vout->lock);
- break;
- }
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *req)
-{
- int ret = 0;
- unsigned int i, num_buffers = 0;
- struct omap_vout_device *vout = fh;
- struct videobuf_queue *q = &vout->vbq;
-
- if ((req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) || (req->count < 0))
- return -EINVAL;
- /* if memory is not mmp or userptr
- return error */
- if ((V4L2_MEMORY_MMAP != req->memory) &&
- (V4L2_MEMORY_USERPTR != req->memory))
- return -EINVAL;
-
- mutex_lock(&vout->lock);
- /* Cannot be requested when streaming is on */
- if (vout->streaming) {
- ret = -EBUSY;
- goto reqbuf_err;
- }
-
- /* If buffers are already allocated free them */
- if (q->bufs[0] && (V4L2_MEMORY_MMAP == q->bufs[0]->memory)) {
- if (vout->mmap_count) {
- ret = -EBUSY;
- goto reqbuf_err;
- }
- num_buffers = (vout->vid == OMAP_VIDEO1) ?
- video1_numbuffers : video2_numbuffers;
- for (i = num_buffers; i < vout->buffer_allocated; i++) {
- omap_vout_free_buffer(vout->buf_virt_addr[i],
- vout->buffer_size);
- vout->buf_virt_addr[i] = 0;
- vout->buf_phy_addr[i] = 0;
- }
- vout->buffer_allocated = num_buffers;
- videobuf_mmap_free(q);
- } else if (q->bufs[0] && (V4L2_MEMORY_USERPTR == q->bufs[0]->memory)) {
- if (vout->buffer_allocated) {
- videobuf_mmap_free(q);
- for (i = 0; i < vout->buffer_allocated; i++) {
- kfree(q->bufs[i]);
- q->bufs[i] = NULL;
- }
- vout->buffer_allocated = 0;
- }
- }
-
- /*store the memory type in data structure */
- vout->memory = req->memory;
-
- INIT_LIST_HEAD(&vout->dma_queue);
-
- /* call videobuf_reqbufs api */
- ret = videobuf_reqbufs(q, req);
- if (ret < 0)
- goto reqbuf_err;
-
- vout->buffer_allocated = req->count;
-
-reqbuf_err:
- mutex_unlock(&vout->lock);
- return ret;
-}
-
-static int vidioc_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *b)
-{
- struct omap_vout_device *vout = fh;
-
- return videobuf_querybuf(&vout->vbq, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh,
- struct v4l2_buffer *buffer)
-{
- struct omap_vout_device *vout = fh;
- struct videobuf_queue *q = &vout->vbq;
-
- if ((V4L2_BUF_TYPE_VIDEO_OUTPUT != buffer->type) ||
- (buffer->index >= vout->buffer_allocated) ||
- (q->bufs[buffer->index]->memory != buffer->memory)) {
- return -EINVAL;
- }
- if (V4L2_MEMORY_USERPTR == buffer->memory) {
- if ((buffer->length < vout->pix.sizeimage) ||
- (0 == buffer->m.userptr)) {
- return -EINVAL;
- }
- }
-
- if ((is_rotation_enabled(vout)) &&
- vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) {
- v4l2_warn(&vout->vid_dev->v4l2_dev,
- "DMA Channel not allocated for Rotation\n");
- return -EINVAL;
- }
-
- return videobuf_qbuf(q, buffer);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct omap_vout_device *vout = fh;
- struct videobuf_queue *q = &vout->vbq;
-
- int ret;
- u32 addr;
- unsigned long size;
- struct videobuf_buffer *vb;
-
- vb = q->bufs[b->index];
-
- if (!vout->streaming)
- return -EINVAL;
-
- if (file->f_flags & O_NONBLOCK)
- /* Call videobuf_dqbuf for non blocking mode */
- ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 1);
- else
- /* Call videobuf_dqbuf for blocking mode */
- ret = videobuf_dqbuf(q, (struct v4l2_buffer *)b, 0);
-
- addr = (unsigned long) vout->buf_phy_addr[vb->i];
- size = (unsigned long) vb->size;
- dma_unmap_single(vout->vid_dev->v4l2_dev.dev, addr,
- size, DMA_TO_DEVICE);
- return ret;
-}
-
-static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- int ret = 0, j;
- u32 addr = 0, mask = 0;
- struct omap_vout_device *vout = fh;
- struct videobuf_queue *q = &vout->vbq;
- struct omapvideo_info *ovid = &vout->vid_info;
-
- mutex_lock(&vout->lock);
-
- if (vout->streaming) {
- ret = -EBUSY;
- goto streamon_err;
- }
-
- ret = videobuf_streamon(q);
- if (ret)
- goto streamon_err;
-
- if (list_empty(&vout->dma_queue)) {
- ret = -EIO;
- goto streamon_err1;
- }
-
- /* Get the next frame from the buffer queue */
- vout->next_frm = vout->cur_frm = list_entry(vout->dma_queue.next,
- struct videobuf_buffer, queue);
- /* Remove buffer from the buffer queue */
- list_del(&vout->cur_frm->queue);
- /* Mark state of the current frame to active */
- vout->cur_frm->state = VIDEOBUF_ACTIVE;
- /* Initialize field_id and started member */
- vout->field_id = 0;
-
- /* set flag here. Next QBUF will start DMA */
- vout->streaming = 1;
-
- vout->first_int = 1;
-
- if (omap_vout_calculate_offset(vout)) {
- ret = -EINVAL;
- goto streamon_err1;
- }
- addr = (unsigned long) vout->queued_buf_addr[vout->cur_frm->i]
- + vout->cropped_offset;
-
- mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
- | DISPC_IRQ_VSYNC2;
-
- omap_dispc_register_isr(omap_vout_isr, vout, mask);
-
- for (j = 0; j < ovid->num_overlays; j++) {
- struct omap_overlay *ovl = ovid->overlays[j];
-
- if (ovl->manager && ovl->manager->device) {
- struct omap_overlay_info info;
- ovl->get_overlay_info(ovl, &info);
- info.paddr = addr;
- if (ovl->set_overlay_info(ovl, &info)) {
- ret = -EINVAL;
- goto streamon_err1;
- }
- }
- }
-
- /* First save the configuration in ovelray structure */
- ret = omapvid_init(vout, addr);
- if (ret)
- v4l2_err(&vout->vid_dev->v4l2_dev,
- "failed to set overlay info\n");
- /* Enable the pipeline and set the Go bit */
- ret = omapvid_apply_changes(vout);
- if (ret)
- v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode\n");
-
- for (j = 0; j < ovid->num_overlays; j++) {
- struct omap_overlay *ovl = ovid->overlays[j];
-
- if (ovl->manager && ovl->manager->device) {
- ret = ovl->enable(ovl);
- if (ret)
- goto streamon_err1;
- }
- }
-
- ret = 0;
-
-streamon_err1:
- if (ret)
- ret = videobuf_streamoff(q);
-streamon_err:
- mutex_unlock(&vout->lock);
- return ret;
-}
-
-static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- u32 mask = 0;
- int ret = 0, j;
- struct omap_vout_device *vout = fh;
- struct omapvideo_info *ovid = &vout->vid_info;
-
- if (!vout->streaming)
- return -EINVAL;
-
- vout->streaming = 0;
- mask = DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD
- | DISPC_IRQ_VSYNC2;
-
- omap_dispc_unregister_isr(omap_vout_isr, vout, mask);
-
- for (j = 0; j < ovid->num_overlays; j++) {
- struct omap_overlay *ovl = ovid->overlays[j];
-
- if (ovl->manager && ovl->manager->device)
- ovl->disable(ovl);
- }
-
- /* Turn of the pipeline */
- ret = omapvid_apply_changes(vout);
- if (ret)
- v4l2_err(&vout->vid_dev->v4l2_dev, "failed to change mode in"
- " streamoff\n");
-
- INIT_LIST_HEAD(&vout->dma_queue);
- ret = videobuf_streamoff(&vout->vbq);
-
- return ret;
-}
-
-static int vidioc_s_fbuf(struct file *file, void *fh,
- struct v4l2_framebuffer *a)
-{
- int enable = 0;
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout = fh;
- struct omap_overlay_manager_info info;
- enum omap_dss_trans_key_type key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- /* OMAP DSS doesn't support Source and Destination color
- key together */
- if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY) &&
- (a->flags & V4L2_FBUF_FLAG_CHROMAKEY))
- return -EINVAL;
- /* OMAP DSS Doesn't support the Destination color key
- and alpha blending together */
- if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY) &&
- (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA))
- return -EINVAL;
-
- if ((a->flags & V4L2_FBUF_FLAG_SRC_CHROMAKEY)) {
- vout->fbuf.flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
- key_type = OMAP_DSS_COLOR_KEY_VID_SRC;
- } else
- vout->fbuf.flags &= ~V4L2_FBUF_FLAG_SRC_CHROMAKEY;
-
- if ((a->flags & V4L2_FBUF_FLAG_CHROMAKEY)) {
- vout->fbuf.flags |= V4L2_FBUF_FLAG_CHROMAKEY;
- key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
- } else
- vout->fbuf.flags &= ~V4L2_FBUF_FLAG_CHROMAKEY;
-
- if (a->flags & (V4L2_FBUF_FLAG_CHROMAKEY |
- V4L2_FBUF_FLAG_SRC_CHROMAKEY))
- enable = 1;
- else
- enable = 0;
- if (ovl->manager && ovl->manager->get_manager_info &&
- ovl->manager->set_manager_info) {
-
- ovl->manager->get_manager_info(ovl->manager, &info);
- info.trans_enabled = enable;
- info.trans_key_type = key_type;
- info.trans_key = vout->win.chromakey;
-
- if (ovl->manager->set_manager_info(ovl->manager, &info))
- return -EINVAL;
- }
- if (a->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) {
- vout->fbuf.flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
- enable = 1;
- } else {
- vout->fbuf.flags &= ~V4L2_FBUF_FLAG_LOCAL_ALPHA;
- enable = 0;
- }
- if (ovl->manager && ovl->manager->get_manager_info &&
- ovl->manager->set_manager_info) {
- ovl->manager->get_manager_info(ovl->manager, &info);
- /* enable this only if there is no zorder cap */
- if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
- info.partial_alpha_enabled = enable;
- if (ovl->manager->set_manager_info(ovl->manager, &info))
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vidioc_g_fbuf(struct file *file, void *fh,
- struct v4l2_framebuffer *a)
-{
- struct omap_overlay *ovl;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout = fh;
- struct omap_overlay_manager_info info;
-
- ovid = &vout->vid_info;
- ovl = ovid->overlays[0];
-
- /* The video overlay must stay within the framebuffer and can't be
- positioned independently. */
- a->flags = V4L2_FBUF_FLAG_OVERLAY;
- a->capability = V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_CHROMAKEY
- | V4L2_FBUF_CAP_SRC_CHROMAKEY;
-
- if (ovl->manager && ovl->manager->get_manager_info) {
- ovl->manager->get_manager_info(ovl->manager, &info);
- if (info.trans_key_type == OMAP_DSS_COLOR_KEY_VID_SRC)
- a->flags |= V4L2_FBUF_FLAG_SRC_CHROMAKEY;
- if (info.trans_key_type == OMAP_DSS_COLOR_KEY_GFX_DST)
- a->flags |= V4L2_FBUF_FLAG_CHROMAKEY;
- }
- if (ovl->manager && ovl->manager->get_manager_info) {
- ovl->manager->get_manager_info(ovl->manager, &info);
- if (info.partial_alpha_enabled)
- a->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA;
- }
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops vout_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_fbuf = vidioc_s_fbuf,
- .vidioc_g_fbuf = vidioc_g_fbuf,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
- .vidioc_cropcap = vidioc_cropcap,
- .vidioc_g_crop = vidioc_g_crop,
- .vidioc_s_crop = vidioc_s_crop,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-};
-
-static const struct v4l2_file_operations omap_vout_fops = {
- .owner = THIS_MODULE,
- .poll = omap_vout_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = omap_vout_mmap,
- .open = omap_vout_open,
- .release = omap_vout_release,
-};
-
-/* Init functions used during driver initialization */
-/* Initial setup of video_data */
-static int __init omap_vout_setup_video_data(struct omap_vout_device *vout)
-{
- struct video_device *vfd;
- struct v4l2_pix_format *pix;
- struct v4l2_control *control;
- struct omap_dss_device *display =
- vout->vid_info.overlays[0]->manager->device;
-
- /* set the default pix */
- pix = &vout->pix;
-
- /* Set the default picture of QVGA */
- pix->width = QQVGA_WIDTH;
- pix->height = QQVGA_HEIGHT;
-
- /* Default pixel format is RGB 5-6-5 */
- pix->pixelformat = V4L2_PIX_FMT_RGB565;
- pix->field = V4L2_FIELD_ANY;
- pix->bytesperline = pix->width * 2;
- pix->sizeimage = pix->bytesperline * pix->height;
- pix->priv = 0;
- pix->colorspace = V4L2_COLORSPACE_JPEG;
-
- vout->bpp = RGB565_BPP;
- vout->fbuf.fmt.width = display->panel.timings.x_res;
- vout->fbuf.fmt.height = display->panel.timings.y_res;
-
- /* Set the data structures for the overlay parameters*/
- vout->win.global_alpha = 255;
- vout->fbuf.flags = 0;
- vout->fbuf.capability = V4L2_FBUF_CAP_LOCAL_ALPHA |
- V4L2_FBUF_CAP_SRC_CHROMAKEY | V4L2_FBUF_CAP_CHROMAKEY;
- vout->win.chromakey = 0;
-
- omap_vout_new_format(pix, &vout->fbuf, &vout->crop, &vout->win);
-
- /*Initialize the control variables for
- rotation, flipping and background color. */
- control = vout->control;
- control[0].id = V4L2_CID_ROTATE;
- control[0].value = 0;
- vout->rotation = 0;
- vout->mirror = 0;
- vout->control[2].id = V4L2_CID_HFLIP;
- vout->control[2].value = 0;
- if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
- vout->vrfb_bpp = 2;
-
- control[1].id = V4L2_CID_BG_COLOR;
- control[1].value = 0;
-
- /* initialize the video_device struct */
- vfd = vout->vfd = video_device_alloc();
-
- if (!vfd) {
- printk(KERN_ERR VOUT_NAME ": could not allocate"
- " video device struct\n");
- return -ENOMEM;
- }
- vfd->release = video_device_release;
- vfd->ioctl_ops = &vout_ioctl_ops;
-
- strlcpy(vfd->name, VOUT_NAME, sizeof(vfd->name));
-
- vfd->fops = &omap_vout_fops;
- vfd->v4l2_dev = &vout->vid_dev->v4l2_dev;
- mutex_init(&vout->lock);
-
- vfd->minor = -1;
- return 0;
-
-}
-
-/* Setup video buffers */
-static int __init omap_vout_setup_video_bufs(struct platform_device *pdev,
- int vid_num)
-{
- u32 numbuffers;
- int ret = 0, i;
- struct omapvideo_info *ovid;
- struct omap_vout_device *vout;
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct omap2video_device *vid_dev =
- container_of(v4l2_dev, struct omap2video_device, v4l2_dev);
-
- vout = vid_dev->vouts[vid_num];
- ovid = &vout->vid_info;
-
- numbuffers = (vid_num == 0) ? video1_numbuffers : video2_numbuffers;
- vout->buffer_size = (vid_num == 0) ? video1_bufsize : video2_bufsize;
- dev_info(&pdev->dev, "Buffer Size = %d\n", vout->buffer_size);
-
- for (i = 0; i < numbuffers; i++) {
- vout->buf_virt_addr[i] =
- omap_vout_alloc_buffer(vout->buffer_size,
- (u32 *) &vout->buf_phy_addr[i]);
- if (!vout->buf_virt_addr[i]) {
- numbuffers = i;
- ret = -ENOMEM;
- goto free_buffers;
- }
- }
-
- vout->cropped_offset = 0;
-
- if (ovid->rotation_type == VOUT_ROT_VRFB) {
- int static_vrfb_allocation = (vid_num == 0) ?
- vid1_static_vrfb_alloc : vid2_static_vrfb_alloc;
- ret = omap_vout_setup_vrfb_bufs(pdev, vid_num,
- static_vrfb_allocation);
- }
-
- return ret;
-
-free_buffers:
- for (i = 0; i < numbuffers; i++) {
- omap_vout_free_buffer(vout->buf_virt_addr[i],
- vout->buffer_size);
- vout->buf_virt_addr[i] = 0;
- vout->buf_phy_addr[i] = 0;
- }
- return ret;
-
-}
-
-/* Create video out devices */
-static int __init omap_vout_create_video_devices(struct platform_device *pdev)
-{
- int ret = 0, k;
- struct omap_vout_device *vout;
- struct video_device *vfd = NULL;
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct omap2video_device *vid_dev = container_of(v4l2_dev,
- struct omap2video_device, v4l2_dev);
-
- for (k = 0; k < pdev->num_resources; k++) {
-
- vout = kzalloc(sizeof(struct omap_vout_device), GFP_KERNEL);
- if (!vout) {
- dev_err(&pdev->dev, ": could not allocate memory\n");
- return -ENOMEM;
- }
-
- vout->vid = k;
- vid_dev->vouts[k] = vout;
- vout->vid_dev = vid_dev;
- /* Select video2 if only 1 overlay is controlled by V4L2 */
- if (pdev->num_resources == 1)
- vout->vid_info.overlays[0] = vid_dev->overlays[k + 2];
- else
- /* Else select video1 and video2 one by one. */
- vout->vid_info.overlays[0] = vid_dev->overlays[k + 1];
- vout->vid_info.num_overlays = 1;
- vout->vid_info.id = k + 1;
-
- /* Set VRFB as rotation_type for omap2 and omap3 */
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
- vout->vid_info.rotation_type = VOUT_ROT_VRFB;
-
- /* Setup the default configuration for the video devices
- */
- if (omap_vout_setup_video_data(vout) != 0) {
- ret = -ENOMEM;
- goto error;
- }
-
- /* Allocate default number of buffers for the video streaming
- * and reserve the VRFB space for rotation
- */
- if (omap_vout_setup_video_bufs(pdev, k) != 0) {
- ret = -ENOMEM;
- goto error1;
- }
-
- /* Register the Video device with V4L2
- */
- vfd = vout->vfd;
- if (video_register_device(vfd, VFL_TYPE_GRABBER, -1) < 0) {
- dev_err(&pdev->dev, ": Could not register "
- "Video for Linux device\n");
- vfd->minor = -1;
- ret = -ENODEV;
- goto error2;
- }
- video_set_drvdata(vfd, vout);
-
- /* Configure the overlay structure */
- ret = omapvid_init(vid_dev->vouts[k], 0);
- if (!ret)
- goto success;
-
-error2:
- if (vout->vid_info.rotation_type == VOUT_ROT_VRFB)
- omap_vout_release_vrfb(vout);
- omap_vout_free_buffers(vout);
-error1:
- video_device_release(vfd);
-error:
- kfree(vout);
- return ret;
-
-success:
- dev_info(&pdev->dev, ": registered and initialized"
- " video device %d\n", vfd->minor);
- if (k == (pdev->num_resources - 1))
- return 0;
- }
-
- return -ENODEV;
-}
-/* Driver functions */
-static void omap_vout_cleanup_device(struct omap_vout_device *vout)
-{
- struct video_device *vfd;
- struct omapvideo_info *ovid;
-
- if (!vout)
- return;
-
- vfd = vout->vfd;
- ovid = &vout->vid_info;
- if (vfd) {
- if (!video_is_registered(vfd)) {
- /*
- * The device was never registered, so release the
- * video_device struct directly.
- */
- video_device_release(vfd);
- } else {
- /*
- * The unregister function will release the video_device
- * struct as well as unregistering it.
- */
- video_unregister_device(vfd);
- }
- }
- if (ovid->rotation_type == VOUT_ROT_VRFB) {
- omap_vout_release_vrfb(vout);
- /* Free the VRFB buffer if allocated
- * init time
- */
- if (vout->vrfb_static_allocation)
- omap_vout_free_vrfb_buffers(vout);
- }
- omap_vout_free_buffers(vout);
-
- kfree(vout);
-}
-
-static int omap_vout_remove(struct platform_device *pdev)
-{
- int k;
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct omap2video_device *vid_dev = container_of(v4l2_dev, struct
- omap2video_device, v4l2_dev);
-
- v4l2_device_unregister(v4l2_dev);
- for (k = 0; k < pdev->num_resources; k++)
- omap_vout_cleanup_device(vid_dev->vouts[k]);
-
- for (k = 0; k < vid_dev->num_displays; k++) {
- if (vid_dev->displays[k]->state != OMAP_DSS_DISPLAY_DISABLED)
- vid_dev->displays[k]->driver->disable(vid_dev->displays[k]);
-
- omap_dss_put_device(vid_dev->displays[k]);
- }
- kfree(vid_dev);
- return 0;
-}
-
-static int __init omap_vout_probe(struct platform_device *pdev)
-{
- int ret = 0, i;
- struct omap_overlay *ovl;
- struct omap_dss_device *dssdev = NULL;
- struct omap_dss_device *def_display;
- struct omap2video_device *vid_dev = NULL;
-
- if (pdev->num_resources == 0) {
- dev_err(&pdev->dev, "probed for an unknown device\n");
- return -ENODEV;
- }
-
- vid_dev = kzalloc(sizeof(struct omap2video_device), GFP_KERNEL);
- if (vid_dev == NULL)
- return -ENOMEM;
-
- vid_dev->num_displays = 0;
- for_each_dss_dev(dssdev) {
- omap_dss_get_device(dssdev);
-
- if (!dssdev->driver) {
- dev_warn(&pdev->dev, "no driver for display: %s\n",
- dssdev->name);
- omap_dss_put_device(dssdev);
- continue;
- }
-
- vid_dev->displays[vid_dev->num_displays++] = dssdev;
- }
-
- if (vid_dev->num_displays == 0) {
- dev_err(&pdev->dev, "no displays\n");
- ret = -EINVAL;
- goto probe_err0;
- }
-
- vid_dev->num_overlays = omap_dss_get_num_overlays();
- for (i = 0; i < vid_dev->num_overlays; i++)
- vid_dev->overlays[i] = omap_dss_get_overlay(i);
-
- vid_dev->num_managers = omap_dss_get_num_overlay_managers();
- for (i = 0; i < vid_dev->num_managers; i++)
- vid_dev->managers[i] = omap_dss_get_overlay_manager(i);
-
- /* Get the Video1 overlay and video2 overlay.
- * Setup the Display attached to that overlays
- */
- for (i = 1; i < vid_dev->num_overlays; i++) {
- ovl = omap_dss_get_overlay(i);
- if (ovl->manager && ovl->manager->device) {
- def_display = ovl->manager->device;
- } else {
- dev_warn(&pdev->dev, "cannot find display\n");
- def_display = NULL;
- }
- if (def_display) {
- struct omap_dss_driver *dssdrv = def_display->driver;
-
- ret = dssdrv->enable(def_display);
- if (ret) {
- /* Here we are not considering a error
- * as display may be enabled by frame
- * buffer driver
- */
- dev_warn(&pdev->dev,
- "'%s' Display already enabled\n",
- def_display->name);
- }
- }
- }
-
- if (v4l2_device_register(&pdev->dev, &vid_dev->v4l2_dev) < 0) {
- dev_err(&pdev->dev, "v4l2_device_register failed\n");
- ret = -ENODEV;
- goto probe_err1;
- }
-
- ret = omap_vout_create_video_devices(pdev);
- if (ret)
- goto probe_err2;
-
- for (i = 0; i < vid_dev->num_displays; i++) {
- struct omap_dss_device *display = vid_dev->displays[i];
-
- if (display->driver->update)
- display->driver->update(display, 0, 0,
- display->panel.timings.x_res,
- display->panel.timings.y_res);
- }
- return 0;
-
-probe_err2:
- v4l2_device_unregister(&vid_dev->v4l2_dev);
-probe_err1:
- for (i = 1; i < vid_dev->num_overlays; i++) {
- def_display = NULL;
- ovl = omap_dss_get_overlay(i);
- if (ovl->manager && ovl->manager->device)
- def_display = ovl->manager->device;
-
- if (def_display && def_display->driver)
- def_display->driver->disable(def_display);
- }
-probe_err0:
- kfree(vid_dev);
- return ret;
-}
-
-static struct platform_driver omap_vout_driver = {
- .driver = {
- .name = VOUT_NAME,
- },
- .remove = omap_vout_remove,
-};
-
-static int __init omap_vout_init(void)
-{
- if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
- printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static void omap_vout_cleanup(void)
-{
- platform_driver_unregister(&omap_vout_driver);
-}
-
-late_initcall(omap_vout_init);
-module_exit(omap_vout_cleanup);
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
deleted file mode 100644
index c7e41145041f..000000000000
--- a/drivers/media/video/omap1_camera.c
+++ /dev/null
@@ -1,1723 +0,0 @@
-/*
- * V4L2 SoC Camera driver for OMAP1 Camera Interface
- *
- * Copyright (C) 2010, Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * Based on V4L2 Driver for i.MXL/i.MXL camera (CSI) host
- * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
- * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com>
- *
- * Based on PXA SoC camera driver
- * Copyright (C) 2006, Sascha Hauer, Pengutronix
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * Hardware specific bits initialy based on former work by Matt Callow
- * drivers/media/video/omap/omap1510cam.c
- * Copyright (C) 2006 Matt Callow
- *
- * 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/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <media/omap1_camera.h>
-#include <media/soc_camera.h>
-#include <media/soc_mediabus.h>
-#include <media/videobuf-dma-contig.h>
-#include <media/videobuf-dma-sg.h>
-
-#include <plat/dma.h>
-
-
-#define DRIVER_NAME "omap1-camera"
-#define DRIVER_VERSION "0.0.2"
-
-
-/*
- * ---------------------------------------------------------------------------
- * OMAP1 Camera Interface registers
- * ---------------------------------------------------------------------------
- */
-
-#define REG_CTRLCLOCK 0x00
-#define REG_IT_STATUS 0x04
-#define REG_MODE 0x08
-#define REG_STATUS 0x0C
-#define REG_CAMDATA 0x10
-#define REG_GPIO 0x14
-#define REG_PEAK_COUNTER 0x18
-
-/* CTRLCLOCK bit shifts */
-#define LCLK_EN BIT(7)
-#define DPLL_EN BIT(6)
-#define MCLK_EN BIT(5)
-#define CAMEXCLK_EN BIT(4)
-#define POLCLK BIT(3)
-#define FOSCMOD_SHIFT 0
-#define FOSCMOD_MASK (0x7 << FOSCMOD_SHIFT)
-#define FOSCMOD_12MHz 0x0
-#define FOSCMOD_6MHz 0x2
-#define FOSCMOD_9_6MHz 0x4
-#define FOSCMOD_24MHz 0x5
-#define FOSCMOD_8MHz 0x6
-
-/* IT_STATUS bit shifts */
-#define DATA_TRANSFER BIT(5)
-#define FIFO_FULL BIT(4)
-#define H_DOWN BIT(3)
-#define H_UP BIT(2)
-#define V_DOWN BIT(1)
-#define V_UP BIT(0)
-
-/* MODE bit shifts */
-#define RAZ_FIFO BIT(18)
-#define EN_FIFO_FULL BIT(17)
-#define EN_NIRQ BIT(16)
-#define THRESHOLD_SHIFT 9
-#define THRESHOLD_MASK (0x7f << THRESHOLD_SHIFT)
-#define DMA BIT(8)
-#define EN_H_DOWN BIT(7)
-#define EN_H_UP BIT(6)
-#define EN_V_DOWN BIT(5)
-#define EN_V_UP BIT(4)
-#define ORDERCAMD BIT(3)
-
-#define IRQ_MASK (EN_V_UP | EN_V_DOWN | EN_H_UP | EN_H_DOWN | \
- EN_NIRQ | EN_FIFO_FULL)
-
-/* STATUS bit shifts */
-#define HSTATUS BIT(1)
-#define VSTATUS BIT(0)
-
-/* GPIO bit shifts */
-#define CAM_RST BIT(0)
-
-/* end of OMAP1 Camera Interface registers */
-
-
-#define SOCAM_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | \
- V4L2_MBUS_DATA_ACTIVE_HIGH)
-
-
-#define FIFO_SIZE ((THRESHOLD_MASK >> THRESHOLD_SHIFT) + 1)
-#define FIFO_SHIFT __fls(FIFO_SIZE)
-
-#define DMA_BURST_SHIFT (1 + OMAP_DMA_DATA_BURST_4)
-#define DMA_BURST_SIZE (1 << DMA_BURST_SHIFT)
-
-#define DMA_ELEMENT_SHIFT OMAP_DMA_DATA_TYPE_S32
-#define DMA_ELEMENT_SIZE (1 << DMA_ELEMENT_SHIFT)
-
-#define DMA_FRAME_SHIFT_CONTIG (FIFO_SHIFT - 1)
-#define DMA_FRAME_SHIFT_SG DMA_BURST_SHIFT
-
-#define DMA_FRAME_SHIFT(x) ((x) == OMAP1_CAM_DMA_CONTIG ? \
- DMA_FRAME_SHIFT_CONTIG : \
- DMA_FRAME_SHIFT_SG)
-#define DMA_FRAME_SIZE(x) (1 << DMA_FRAME_SHIFT(x))
-#define DMA_SYNC OMAP_DMA_SYNC_FRAME
-#define THRESHOLD_LEVEL DMA_FRAME_SIZE
-
-
-#define MAX_VIDEO_MEM 4 /* arbitrary video memory limit in MB */
-
-
-/*
- * Structures
- */
-
-/* buffer for one video frame */
-struct omap1_cam_buf {
- struct videobuf_buffer vb;
- enum v4l2_mbus_pixelcode code;
- int inwork;
- struct scatterlist *sgbuf;
- int sgcount;
- int bytes_left;
- enum videobuf_state result;
-};
-
-struct omap1_cam_dev {
- struct soc_camera_host soc_host;
- struct soc_camera_device *icd;
- struct clk *clk;
-
- unsigned int irq;
- void __iomem *base;
-
- int dma_ch;
-
- struct omap1_cam_platform_data *pdata;
- struct resource *res;
- unsigned long pflags;
- unsigned long camexclk;
-
- struct list_head capture;
-
- /* lock used to protect videobuf */
- spinlock_t lock;
-
- /* Pointers to DMA buffers */
- struct omap1_cam_buf *active;
- struct omap1_cam_buf *ready;
-
- enum omap1_cam_vb_mode vb_mode;
- int (*mmap_mapper)(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma);
-
- u32 reg_cache[0];
-};
-
-
-static void cam_write(struct omap1_cam_dev *pcdev, u16 reg, u32 val)
-{
- pcdev->reg_cache[reg / sizeof(u32)] = val;
- __raw_writel(val, pcdev->base + reg);
-}
-
-static u32 cam_read(struct omap1_cam_dev *pcdev, u16 reg, bool from_cache)
-{
- return !from_cache ? __raw_readl(pcdev->base + reg) :
- pcdev->reg_cache[reg / sizeof(u32)];
-}
-
-#define CAM_READ(pcdev, reg) \
- cam_read(pcdev, REG_##reg, false)
-#define CAM_WRITE(pcdev, reg, val) \
- cam_write(pcdev, REG_##reg, val)
-#define CAM_READ_CACHE(pcdev, reg) \
- cam_read(pcdev, REG_##reg, true)
-
-/*
- * Videobuf operations
- */
-static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- *size = icd->sizeimage;
-
- if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
- *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
-
- if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
-
- dev_dbg(icd->parent,
- "%s: count=%d, size=%d\n", __func__, *count, *size);
-
- return 0;
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct omap1_cam_buf *buf,
- enum omap1_cam_vb_mode vb_mode)
-{
- struct videobuf_buffer *vb = &buf->vb;
-
- BUG_ON(in_interrupt());
-
- videobuf_waiton(vq, vb, 0, 0);
-
- if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
- videobuf_dma_contig_free(vq, vb);
- } else {
- struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->parent;
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- videobuf_dma_unmap(dev, dma);
- videobuf_dma_free(dma);
- }
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int omap1_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- int ret;
-
- WARN_ON(!list_empty(&vb->queue));
-
- BUG_ON(NULL == icd->current_fmt);
-
- buf->inwork = 1;
-
- if (buf->code != icd->current_fmt->code || vb->field != field ||
- vb->width != icd->user_width ||
- vb->height != icd->user_height) {
- buf->code = icd->current_fmt->code;
- vb->width = icd->user_width;
- vb->height = icd->user_height;
- vb->field = field;
- vb->state = VIDEOBUF_NEEDS_INIT;
- }
-
- vb->size = icd->sizeimage;
-
- if (vb->baddr && vb->bsize < vb->size) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret)
- goto fail;
-
- vb->state = VIDEOBUF_PREPARED;
- }
- buf->inwork = 0;
-
- return 0;
-fail:
- free_buffer(vq, buf, pcdev->vb_mode);
-out:
- buf->inwork = 0;
- return ret;
-}
-
-static void set_dma_dest_params(int dma_ch, struct omap1_cam_buf *buf,
- enum omap1_cam_vb_mode vb_mode)
-{
- dma_addr_t dma_addr;
- unsigned int block_size;
-
- if (vb_mode == OMAP1_CAM_DMA_CONTIG) {
- dma_addr = videobuf_to_dma_contig(&buf->vb);
- block_size = buf->vb.size;
- } else {
- if (WARN_ON(!buf->sgbuf)) {
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- dma_addr = sg_dma_address(buf->sgbuf);
- if (WARN_ON(!dma_addr)) {
- buf->sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- block_size = sg_dma_len(buf->sgbuf);
- if (WARN_ON(!block_size)) {
- buf->sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- return;
- }
- if (unlikely(buf->bytes_left < block_size))
- block_size = buf->bytes_left;
- if (WARN_ON(dma_addr & (DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE - 1))) {
- dma_addr = ALIGN(dma_addr, DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE);
- block_size &= ~(DMA_FRAME_SIZE(vb_mode) *
- DMA_ELEMENT_SIZE - 1);
- }
- buf->bytes_left -= block_size;
- buf->sgcount++;
- }
-
- omap_set_dma_dest_params(dma_ch,
- OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, dma_addr, 0, 0);
- omap_set_dma_transfer_params(dma_ch,
- OMAP_DMA_DATA_TYPE_S32, DMA_FRAME_SIZE(vb_mode),
- block_size >> (DMA_FRAME_SHIFT(vb_mode) + DMA_ELEMENT_SHIFT),
- DMA_SYNC, 0, 0);
-}
-
-static struct omap1_cam_buf *prepare_next_vb(struct omap1_cam_dev *pcdev)
-{
- struct omap1_cam_buf *buf;
-
- /*
- * If there is already a buffer pointed out by the pcdev->ready,
- * (re)use it, otherwise try to fetch and configure a new one.
- */
- buf = pcdev->ready;
- if (!buf) {
- if (list_empty(&pcdev->capture))
- return buf;
- buf = list_entry(pcdev->capture.next,
- struct omap1_cam_buf, vb.queue);
- buf->vb.state = VIDEOBUF_ACTIVE;
- pcdev->ready = buf;
- list_del_init(&buf->vb.queue);
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, we can safely enter next buffer parameters
- * into the DMA programming register set after the DMA
- * has already been activated on the previous buffer
- */
- set_dma_dest_params(pcdev->dma_ch, buf, pcdev->vb_mode);
- } else {
- /*
- * In SG mode, the above is not safe since there are probably
- * a bunch of sgbufs from previous sglist still pending.
- * Instead, mark the sglist fresh for the upcoming
- * try_next_sgbuf().
- */
- buf->sgbuf = NULL;
- }
-
- return buf;
-}
-
-static struct scatterlist *try_next_sgbuf(int dma_ch, struct omap1_cam_buf *buf)
-{
- struct scatterlist *sgbuf;
-
- if (likely(buf->sgbuf)) {
- /* current sglist is active */
- if (unlikely(!buf->bytes_left)) {
- /* indicate sglist complete */
- sgbuf = NULL;
- } else {
- /* process next sgbuf */
- sgbuf = sg_next(buf->sgbuf);
- if (WARN_ON(!sgbuf)) {
- buf->result = VIDEOBUF_ERROR;
- } else if (WARN_ON(!sg_dma_len(sgbuf))) {
- sgbuf = NULL;
- buf->result = VIDEOBUF_ERROR;
- }
- }
- buf->sgbuf = sgbuf;
- } else {
- /* sglist is fresh, initialize it before using */
- struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
-
- sgbuf = dma->sglist;
- if (!(WARN_ON(!sgbuf))) {
- buf->sgbuf = sgbuf;
- buf->sgcount = 0;
- buf->bytes_left = buf->vb.size;
- buf->result = VIDEOBUF_DONE;
- }
- }
- if (sgbuf)
- /*
- * Put our next sgbuf parameters (address, size)
- * into the DMA programming register set.
- */
- set_dma_dest_params(dma_ch, buf, OMAP1_CAM_DMA_SG);
-
- return sgbuf;
-}
-
-static void start_capture(struct omap1_cam_dev *pcdev)
-{
- struct omap1_cam_buf *buf = pcdev->active;
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- u32 mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN;
-
- if (WARN_ON(!buf))
- return;
-
- /*
- * Enable start of frame interrupt, which we will use for activating
- * our end of frame watchdog when capture actually starts.
- */
- mode |= EN_V_UP;
-
- if (unlikely(ctrlclock & LCLK_EN))
- /* stop pixel clock before FIFO reset */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- /* reset FIFO */
- CAM_WRITE(pcdev, MODE, mode | RAZ_FIFO);
-
- omap_start_dma(pcdev->dma_ch);
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, it's a good moment for fetching next sgbuf
- * from the current sglist and, if available, already putting
- * its parameters into the DMA programming register set.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- /* (re)enable pixel clock */
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | LCLK_EN);
- /* release FIFO reset */
- CAM_WRITE(pcdev, MODE, mode);
-}
-
-static void suspend_capture(struct omap1_cam_dev *pcdev)
-{
- u32 ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
-
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
- omap_stop_dma(pcdev->dma_ch);
-}
-
-static void disable_capture(struct omap1_cam_dev *pcdev)
-{
- u32 mode = CAM_READ_CACHE(pcdev, MODE);
-
- CAM_WRITE(pcdev, MODE, mode & ~(IRQ_MASK | DMA));
-}
-
-static void omap1_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct omap1_cam_buf *buf;
- u32 mode;
-
- list_add_tail(&vb->queue, &pcdev->capture);
- vb->state = VIDEOBUF_QUEUED;
-
- if (pcdev->active) {
- /*
- * Capture in progress, so don't touch pcdev->ready even if
- * empty. Since the transfer of the DMA programming register set
- * content to the DMA working register set is done automatically
- * by the DMA hardware, this can pretty well happen while we
- * are keeping the lock here. Leave fetching it from the queue
- * to be done when a next DMA interrupt occures instead.
- */
- return;
- }
-
- WARN_ON(pcdev->ready);
-
- buf = prepare_next_vb(pcdev);
- if (WARN_ON(!buf))
- return;
-
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- dev_dbg(icd->parent,
- "%s: capture not active, setup FIFO, start DMA\n", __func__);
- mode = CAM_READ_CACHE(pcdev, MODE) & ~THRESHOLD_MASK;
- mode |= THRESHOLD_LEVEL(pcdev->vb_mode) << THRESHOLD_SHIFT;
- CAM_WRITE(pcdev, MODE, mode | EN_FIFO_FULL | DMA);
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, the above prepare_next_vb() didn't actually
- * put anything into the DMA programming register set,
- * so we have to do it now, before activating DMA.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- start_capture(pcdev);
-}
-
-static void omap1_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct omap1_cam_buf *buf =
- container_of(vb, struct omap1_cam_buf, vb);
- struct soc_camera_device *icd = vq->priv_data;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- switch (vb->state) {
- case VIDEOBUF_DONE:
- dev_dbg(dev, "%s (done)\n", __func__);
- break;
- case VIDEOBUF_ACTIVE:
- dev_dbg(dev, "%s (active)\n", __func__);
- break;
- case VIDEOBUF_QUEUED:
- dev_dbg(dev, "%s (queued)\n", __func__);
- break;
- case VIDEOBUF_PREPARED:
- dev_dbg(dev, "%s (prepared)\n", __func__);
- break;
- default:
- dev_dbg(dev, "%s (unknown %d)\n", __func__, vb->state);
- break;
- }
-
- free_buffer(vq, buf, pcdev->vb_mode);
-}
-
-static void videobuf_done(struct omap1_cam_dev *pcdev,
- enum videobuf_state result)
-{
- struct omap1_cam_buf *buf = pcdev->active;
- struct videobuf_buffer *vb;
- struct device *dev = pcdev->icd->parent;
-
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- return;
- }
-
- if (result == VIDEOBUF_ERROR)
- suspend_capture(pcdev);
-
- vb = &buf->vb;
- if (waitqueue_active(&vb->done)) {
- if (!pcdev->ready && result != VIDEOBUF_ERROR) {
- /*
- * No next buffer has been entered into the DMA
- * programming register set on time (could be done only
- * while the previous DMA interurpt was processed, not
- * later), so the last DMA block, be it a whole buffer
- * if in CONTIG or its last sgbuf if in SG mode, is
- * about to be reused by the just autoreinitialized DMA
- * engine, and overwritten with next frame data. Best we
- * can do is stopping the capture as soon as possible,
- * hopefully before the next frame start.
- */
- suspend_capture(pcdev);
- }
- vb->state = result;
- do_gettimeofday(&vb->ts);
- if (result != VIDEOBUF_ERROR)
- vb->field_count++;
- wake_up(&vb->done);
-
- /* shift in next buffer */
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
-
- if (!buf) {
- /*
- * No next buffer was ready on time (see above), so
- * indicate error condition to force capture restart or
- * stop, depending on next buffer already queued or not.
- */
- result = VIDEOBUF_ERROR;
- prepare_next_vb(pcdev);
-
- buf = pcdev->ready;
- pcdev->active = buf;
- pcdev->ready = NULL;
- }
- } else if (pcdev->ready) {
- /*
- * In both CONTIG and SG mode, the DMA engine has possibly
- * been already autoreinitialized with the preprogrammed
- * pcdev->ready buffer. We can either accept this fact
- * and just swap the buffers, or provoke an error condition
- * and restart capture. The former seems less intrusive.
- */
- dev_dbg(dev, "%s: nobody waiting on videobuf, swap with next\n",
- __func__);
- pcdev->active = pcdev->ready;
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_SG) {
- /*
- * In SG mode, we have to make sure that the buffer we
- * are putting back into the pcdev->ready is marked
- * fresh.
- */
- buf->sgbuf = NULL;
- }
- pcdev->ready = buf;
-
- buf = pcdev->active;
- } else {
- /*
- * No next buffer has been entered into
- * the DMA programming register set on time.
- */
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the DMA engine has already been
- * reinitialized with the current buffer. Best we can do
- * is not touching it.
- */
- dev_dbg(dev,
- "%s: nobody waiting on videobuf, reuse it\n",
- __func__);
- } else {
- /*
- * In SG mode, the DMA engine has just been
- * autoreinitialized with the last sgbuf from the
- * current list. Restart capture in order to transfer
- * next frame start into the first sgbuf, not the last
- * one.
- */
- if (result != VIDEOBUF_ERROR) {
- suspend_capture(pcdev);
- result = VIDEOBUF_ERROR;
- }
- }
- }
-
- if (!buf) {
- dev_dbg(dev, "%s: no more videobufs, stop capture\n", __func__);
- disable_capture(pcdev);
- return;
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the current buffer parameters had already
- * been entered into the DMA programming register set while the
- * buffer was fetched with prepare_next_vb(), they may have also
- * been transferred into the runtime set and already active if
- * the DMA still running.
- */
- } else {
- /* In SG mode, extra steps are required */
- if (result == VIDEOBUF_ERROR)
- /* make sure we (re)use sglist from start on error */
- buf->sgbuf = NULL;
-
- /*
- * In any case, enter the next sgbuf parameters into the DMA
- * programming register set. They will be used either during
- * nearest DMA autoreinitialization or, in case of an error,
- * on DMA startup below.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- }
-
- if (result == VIDEOBUF_ERROR) {
- dev_dbg(dev, "%s: videobuf error; reset FIFO, restart DMA\n",
- __func__);
- start_capture(pcdev);
- /*
- * In SG mode, the above also resulted in the next sgbuf
- * parameters being entered into the DMA programming register
- * set, making them ready for next DMA autoreinitialization.
- */
- }
-
- /*
- * Finally, try fetching next buffer.
- * In CONTIG mode, it will also enter it into the DMA programming
- * register set, making it ready for next DMA autoreinitialization.
- */
- prepare_next_vb(pcdev);
-}
-
-static void dma_isr(int channel, unsigned short status, void *data)
-{
- struct omap1_cam_dev *pcdev = data;
- struct omap1_cam_buf *buf = pcdev->active;
- unsigned long flags;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (WARN_ON(!buf)) {
- suspend_capture(pcdev);
- disable_capture(pcdev);
- goto out;
- }
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, assume we have just managed to collect the
- * whole frame, hopefully before our end of frame watchdog is
- * triggered. Then, all we have to do is disabling the watchdog
- * for this frame, and calling videobuf_done() with success
- * indicated.
- */
- CAM_WRITE(pcdev, MODE,
- CAM_READ_CACHE(pcdev, MODE) & ~EN_V_DOWN);
- videobuf_done(pcdev, VIDEOBUF_DONE);
- } else {
- /*
- * In SG mode, we have to process every sgbuf from the current
- * sglist, one after another.
- */
- if (buf->sgbuf) {
- /*
- * Current sglist not completed yet, try fetching next
- * sgbuf, hopefully putting it into the DMA programming
- * register set, making it ready for next DMA
- * autoreinitialization.
- */
- try_next_sgbuf(pcdev->dma_ch, buf);
- if (buf->sgbuf)
- goto out;
-
- /*
- * No more sgbufs left in the current sglist. This
- * doesn't mean that the whole videobuffer is already
- * complete, but only that the last sgbuf from the
- * current sglist is about to be filled. It will be
- * ready on next DMA interrupt, signalled with the
- * buf->sgbuf set back to NULL.
- */
- if (buf->result != VIDEOBUF_ERROR) {
- /*
- * Video frame collected without errors so far,
- * we can prepare for collecting a next one
- * as soon as DMA gets autoreinitialized
- * after the current (last) sgbuf is completed.
- */
- buf = prepare_next_vb(pcdev);
- if (!buf)
- goto out;
-
- try_next_sgbuf(pcdev->dma_ch, buf);
- goto out;
- }
- }
- /* end of videobuf */
- videobuf_done(pcdev, buf->result);
- }
-
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static irqreturn_t cam_isr(int irq, void *data)
-{
- struct omap1_cam_dev *pcdev = data;
- struct device *dev = pcdev->icd->parent;
- struct omap1_cam_buf *buf = pcdev->active;
- u32 it_status;
- unsigned long flags;
-
- it_status = CAM_READ(pcdev, IT_STATUS);
- if (!it_status)
- return IRQ_NONE;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (WARN_ON(!buf)) {
- dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
- __func__, it_status);
- suspend_capture(pcdev);
- disable_capture(pcdev);
- goto out;
- }
-
- if (unlikely(it_status & FIFO_FULL)) {
- dev_warn(dev, "%s: FIFO overflow\n", __func__);
-
- } else if (it_status & V_DOWN) {
- /* end of video frame watchdog */
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, the watchdog is disabled with
- * successful DMA end of block interrupt, and reenabled
- * on next frame start. If we get here, there is nothing
- * to check, we must be out of sync.
- */
- } else {
- if (buf->sgcount == 2) {
- /*
- * If exactly 2 sgbufs from the next sglist have
- * been programmed into the DMA engine (the
- * first one already transferred into the DMA
- * runtime register set, the second one still
- * in the programming set), then we are in sync.
- */
- goto out;
- }
- }
- dev_notice(dev, "%s: unexpected end of video frame\n",
- __func__);
-
- } else if (it_status & V_UP) {
- u32 mode;
-
- if (pcdev->vb_mode == OMAP1_CAM_DMA_CONTIG) {
- /*
- * In CONTIG mode, we need this interrupt every frame
- * in oredr to reenable our end of frame watchdog.
- */
- mode = CAM_READ_CACHE(pcdev, MODE);
- } else {
- /*
- * In SG mode, the below enabled end of frame watchdog
- * is kept on permanently, so we can turn this one shot
- * setup off.
- */
- mode = CAM_READ_CACHE(pcdev, MODE) & ~EN_V_UP;
- }
-
- if (!(mode & EN_V_DOWN)) {
- /* (re)enable end of frame watchdog interrupt */
- mode |= EN_V_DOWN;
- }
- CAM_WRITE(pcdev, MODE, mode);
- goto out;
-
- } else {
- dev_warn(dev, "%s: unhandled camera interrupt, status == %#x\n",
- __func__, it_status);
- goto out;
- }
-
- videobuf_done(pcdev, VIDEOBUF_ERROR);
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
- return IRQ_HANDLED;
-}
-
-static struct videobuf_queue_ops omap1_videobuf_ops = {
- .buf_setup = omap1_videobuf_setup,
- .buf_prepare = omap1_videobuf_prepare,
- .buf_queue = omap1_videobuf_queue,
- .buf_release = omap1_videobuf_release,
-};
-
-
-/*
- * SOC Camera host operations
- */
-
-static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset)
-{
- /* apply/release camera sensor reset if requested by platform data */
- if (pcdev->pflags & OMAP1_CAMERA_RST_HIGH)
- CAM_WRITE(pcdev, GPIO, reset);
- else if (pcdev->pflags & OMAP1_CAMERA_RST_LOW)
- CAM_WRITE(pcdev, GPIO, !reset);
-}
-
-/*
- * The following two functions absolutely depend on the fact, that
- * there can be only one camera on OMAP1 camera sensor interface
- */
-static int omap1_cam_add_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 ctrlclock;
-
- if (pcdev->icd)
- return -EBUSY;
-
- clk_enable(pcdev->clk);
-
- /* setup sensor clock */
- ctrlclock = CAM_READ(pcdev, CTRLCLOCK);
- ctrlclock &= ~(CAMEXCLK_EN | MCLK_EN | DPLL_EN);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- ctrlclock &= ~FOSCMOD_MASK;
- switch (pcdev->camexclk) {
- case 6000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_6MHz;
- break;
- case 8000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_8MHz | DPLL_EN;
- break;
- case 9600000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_9_6MHz | DPLL_EN;
- break;
- case 12000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_12MHz;
- break;
- case 24000000:
- ctrlclock |= CAMEXCLK_EN | FOSCMOD_24MHz | DPLL_EN;
- default:
- break;
- }
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~DPLL_EN);
-
- /* enable internal clock */
- ctrlclock |= MCLK_EN;
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- sensor_reset(pcdev, false);
-
- pcdev->icd = icd;
-
- dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n",
- icd->devnum);
- return 0;
-}
-
-static void omap1_cam_remove_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 ctrlclock;
-
- BUG_ON(icd != pcdev->icd);
-
- suspend_capture(pcdev);
- disable_capture(pcdev);
-
- sensor_reset(pcdev, true);
-
- /* disable and release system clocks */
- ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- ctrlclock &= ~(MCLK_EN | DPLL_EN | CAMEXCLK_EN);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- ctrlclock = (ctrlclock & ~FOSCMOD_MASK) | FOSCMOD_12MHz;
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock | MCLK_EN);
-
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN);
-
- clk_disable(pcdev->clk);
-
- pcdev->icd = NULL;
-
- dev_dbg(icd->parent,
- "OMAP1 Camera driver detached from camera %d\n", icd->devnum);
-}
-
-/* Duplicate standard formats based on host capability of byte swapping */
-static const struct soc_mbus_lookup omap1_cam_formats[] = {
-{
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .name = "YUYV",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .name = "YVYU",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .name = "UYVY",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .name = "VYUY",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB555,
- .name = "RGB555",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .name = "RGB555X",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .name = "RGB565",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .name = "RGB565X",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
- .layout = SOC_MBUS_LAYOUT_PACKED,
- },
-},
-};
-
-static int omap1_cam_get_formats(struct soc_camera_device *icd,
- unsigned int idx, struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- int formats = 0, ret;
- enum v4l2_mbus_pixelcode code;
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code);
- if (!fmt) {
- dev_warn(dev, "%s: unsupported format code #%d: %d\n", __func__,
- idx, code);
- return 0;
- }
-
- /* Check support for the requested bits-per-sample */
- if (fmt->bits_per_sample != 8)
- return 0;
-
- switch (code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE:
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- formats++;
- if (xlate) {
- xlate->host_fmt = soc_mbus_find_fmtdesc(code,
- omap1_cam_formats,
- ARRAY_SIZE(omap1_cam_formats));
- xlate->code = code;
- xlate++;
- dev_dbg(dev,
- "%s: providing format %s as byte swapped code #%d\n",
- __func__, xlate->host_fmt->name, code);
- }
- default:
- if (xlate)
- dev_dbg(dev,
- "%s: providing format %s in pass-through mode\n",
- __func__, fmt->name);
- }
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code;
- xlate++;
- }
-
- return formats;
-}
-
-static bool is_dma_aligned(s32 bytes_per_line, unsigned int height,
- enum omap1_cam_vb_mode vb_mode)
-{
- int size = bytes_per_line * height;
-
- return IS_ALIGNED(bytes_per_line, DMA_ELEMENT_SIZE) &&
- IS_ALIGNED(size, DMA_FRAME_SIZE(vb_mode) * DMA_ELEMENT_SIZE);
-}
-
-static int dma_align(int *width, int *height,
- const struct soc_mbus_pixelfmt *fmt,
- enum omap1_cam_vb_mode vb_mode, bool enlarge)
-{
- s32 bytes_per_line = soc_mbus_bytes_per_line(*width, fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- if (!is_dma_aligned(bytes_per_line, *height, vb_mode)) {
- unsigned int pxalign = __fls(bytes_per_line / *width);
- unsigned int salign = DMA_FRAME_SHIFT(vb_mode) +
- DMA_ELEMENT_SHIFT - pxalign;
- unsigned int incr = enlarge << salign;
-
- v4l_bound_align_image(width, 1, *width + incr, 0,
- height, 1, *height + incr, 0, salign);
- return 0;
- }
- 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; \
-})
-
-static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev,
- struct soc_camera_device *icd, struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf,
- const struct soc_camera_format_xlate *xlate)
-{
- s32 bytes_per_line;
- int ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_mbus_fmt, mf);
-
- if (ret < 0) {
- dev_err(dev, "%s: s_mbus_fmt failed\n", __func__);
- return ret;
- }
-
- if (mf->code != xlate->code) {
- dev_err(dev, "%s: unexpected pixel code change\n", __func__);
- return -EINVAL;
- }
-
- bytes_per_line = soc_mbus_bytes_per_line(mf->width, xlate->host_fmt);
- if (bytes_per_line < 0) {
- dev_err(dev, "%s: soc_mbus_bytes_per_line() failed\n",
- __func__);
- return bytes_per_line;
- }
-
- if (!is_dma_aligned(bytes_per_line, mf->height, pcdev->vb_mode)) {
- dev_err(dev, "%s: resulting geometry %ux%u not DMA aligned\n",
- __func__, mf->width, mf->height);
- return -EINVAL;
- }
- return 0;
-}
-
-static int omap1_cam_set_crop(struct soc_camera_device *icd,
- struct v4l2_crop *crop)
-{
- struct v4l2_rect *rect = &crop->c;
- const struct soc_camera_format_xlate *xlate = icd->current_fmt;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- ret = subdev_call_with_sense(pcdev, dev, icd, sd, s_crop, crop);
- if (ret < 0) {
- dev_warn(dev, "%s: failed to crop to %ux%u@%u:%u\n", __func__,
- rect->width, rect->height, rect->left, rect->top);
- return ret;
- }
-
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
- if (ret < 0) {
- dev_warn(dev, "%s: failed to fetch current format\n", __func__);
- return ret;
- }
-
- ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
- false);
- if (ret < 0) {
- dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
- __func__, mf.width, mf.height,
- xlate->host_fmt->name);
- return ret;
- }
-
- if (!ret) {
- /* sensor returned geometry not DMA aligned, trying to fix */
- ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
- if (ret < 0) {
- dev_err(dev, "%s: failed to set format\n", __func__);
- return ret;
- }
- }
-
- icd->user_width = mf.width;
- icd->user_height = mf.height;
-
- return 0;
-}
-
-static int omap1_cam_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(dev, "%s: format %#x not found\n", __func__,
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- ret = dma_align(&mf.width, &mf.height, xlate->host_fmt, pcdev->vb_mode,
- true);
- if (ret < 0) {
- dev_err(dev, "%s: failed to align %ux%u %s with DMA\n",
- __func__, pix->width, pix->height,
- xlate->host_fmt->name);
- return ret;
- }
-
- ret = set_mbus_format(pcdev, dev, icd, sd, &mf, xlate);
- if (ret < 0) {
- dev_err(dev, "%s: failed to set format\n", __func__);
- return ret;
- }
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
- icd->current_fmt = xlate;
-
- return 0;
-}
-
-static int omap1_cam_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- int ret;
- /* TODO: limit to mx1 hardware capabilities */
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate) {
- dev_warn(icd->parent, "Format %#x not found\n",
- pix->pixelformat);
- return -EINVAL;
- }
-
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- /* limit to sensor capabilities */
- ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
-
- return 0;
-}
-
-static bool sg_mode;
-
-/*
- * Local mmap_mapper wrapper,
- * used for detecting videobuf-dma-contig buffer allocation failures
- * and switching to videobuf-dma-sg automatically for future attempts.
- */
-static int omap1_cam_mmap_mapper(struct videobuf_queue *q,
- struct videobuf_buffer *buf,
- struct vm_area_struct *vma)
-{
- struct soc_camera_device *icd = q->priv_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
- int ret;
-
- ret = pcdev->mmap_mapper(q, buf, vma);
-
- if (ret == -ENOMEM)
- sg_mode = true;
-
- return ret;
-}
-
-static void omap1_cam_init_videobuf(struct videobuf_queue *q,
- struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct omap1_cam_dev *pcdev = ici->priv;
-
- if (!sg_mode)
- videobuf_queue_dma_contig_init(q, &omap1_videobuf_ops,
- icd->parent, &pcdev->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
- else
- videobuf_queue_sg_init(q, &omap1_videobuf_ops,
- icd->parent, &pcdev->lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE,
- sizeof(struct omap1_cam_buf), icd, &icd->video_lock);
-
- /* use videobuf mode (auto)selected with the module parameter */
- pcdev->vb_mode = sg_mode ? OMAP1_CAM_DMA_SG : OMAP1_CAM_DMA_CONTIG;
-
- /*
- * Ensure we substitute the videobuf-dma-contig version of the
- * mmap_mapper() callback with our own wrapper, used for switching
- * automatically to videobuf-dma-sg on buffer allocation failure.
- */
- if (!sg_mode && q->int_ops->mmap_mapper != omap1_cam_mmap_mapper) {
- pcdev->mmap_mapper = q->int_ops->mmap_mapper;
- q->int_ops->mmap_mapper = omap1_cam_mmap_mapper;
- }
-}
-
-static int omap1_cam_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 omap1_cam_buf *buf = container_of(icd->vb_vidq.bufs[i],
- struct omap1_cam_buf, vb);
- buf->inwork = 0;
- INIT_LIST_HEAD(&buf->vb.queue);
- }
-
- return 0;
-}
-
-static int omap1_cam_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- /* cap->name is set by the friendly caller:-> */
- strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static int omap1_cam_set_bus_param(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct omap1_cam_dev *pcdev = ici->priv;
- u32 pixfmt = icd->current_fmt->host_fmt->fourcc;
- const struct soc_camera_format_xlate *xlate;
- const struct soc_mbus_pixelfmt *fmt;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long common_flags;
- u32 ctrlclock, mode;
- int ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg, SOCAM_BUS_FLAGS);
- if (!common_flags) {
- dev_warn(dev,
- "Flags incompatible: camera 0x%x, host 0x%x\n",
- cfg.flags, SOCAM_BUS_FLAGS);
- return -EINVAL;
- }
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- } else {
- common_flags = SOCAM_BUS_FLAGS;
- }
-
- /* Make choices, possibly based on platform configuration */
- if ((common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) &&
- (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)) {
- if (!pcdev->pdata ||
- pcdev->pdata->flags & OMAP1_CAMERA_LCLK_RISING)
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_FALLING;
- else
- common_flags &= ~V4L2_MBUS_PCLK_SAMPLE_RISING;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- dev_dbg(dev, "camera s_mbus_config(0x%lx) returned %d\n",
- common_flags, ret);
- return ret;
- }
-
- ctrlclock = CAM_READ_CACHE(pcdev, CTRLCLOCK);
- if (ctrlclock & LCLK_EN)
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
-
- if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) {
- dev_dbg(dev, "CTRLCLOCK_REG |= POLCLK\n");
- ctrlclock |= POLCLK;
- } else {
- dev_dbg(dev, "CTRLCLOCK_REG &= ~POLCLK\n");
- ctrlclock &= ~POLCLK;
- }
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~LCLK_EN);
-
- if (ctrlclock & LCLK_EN)
- CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock);
-
- /* select bus endianess */
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- fmt = xlate->host_fmt;
-
- mode = CAM_READ(pcdev, MODE) & ~(RAZ_FIFO | IRQ_MASK | DMA);
- if (fmt->order == SOC_MBUS_ORDER_LE) {
- dev_dbg(dev, "MODE_REG &= ~ORDERCAMD\n");
- CAM_WRITE(pcdev, MODE, mode & ~ORDERCAMD);
- } else {
- dev_dbg(dev, "MODE_REG |= ORDERCAMD\n");
- CAM_WRITE(pcdev, MODE, mode | ORDERCAMD);
- }
-
- return 0;
-}
-
-static unsigned int omap1_cam_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
- struct omap1_cam_buf *buf;
-
- buf = list_entry(icd->vb_vidq.stream.next, struct omap1_cam_buf,
- 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;
-}
-
-static struct soc_camera_host_ops omap1_host_ops = {
- .owner = THIS_MODULE,
- .add = omap1_cam_add_device,
- .remove = omap1_cam_remove_device,
- .get_formats = omap1_cam_get_formats,
- .set_crop = omap1_cam_set_crop,
- .set_fmt = omap1_cam_set_fmt,
- .try_fmt = omap1_cam_try_fmt,
- .init_videobuf = omap1_cam_init_videobuf,
- .reqbufs = omap1_cam_reqbufs,
- .querycap = omap1_cam_querycap,
- .set_bus_param = omap1_cam_set_bus_param,
- .poll = omap1_cam_poll,
-};
-
-static int __init omap1_cam_probe(struct platform_device *pdev)
-{
- struct omap1_cam_dev *pcdev;
- struct resource *res;
- struct clk *clk;
- void __iomem *base;
- unsigned int irq;
- int err = 0;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0) {
- err = -ENODEV;
- goto exit;
- }
-
- clk = clk_get(&pdev->dev, "armper_ck");
- if (IS_ERR(clk)) {
- err = PTR_ERR(clk);
- goto exit;
- }
-
- pcdev = kzalloc(sizeof(*pcdev) + resource_size(res), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit_put_clk;
- }
-
- pcdev->res = res;
- pcdev->clk = clk;
-
- pcdev->pdata = pdev->dev.platform_data;
- if (pcdev->pdata) {
- pcdev->pflags = pcdev->pdata->flags;
- pcdev->camexclk = pcdev->pdata->camexclk_khz * 1000;
- }
-
- switch (pcdev->camexclk) {
- case 6000000:
- case 8000000:
- case 9600000:
- case 12000000:
- case 24000000:
- break;
- default:
- /* pcdev->camexclk != 0 => pcdev->pdata != NULL */
- dev_warn(&pdev->dev,
- "Incorrect sensor clock frequency %ld kHz, "
- "should be one of 0, 6, 8, 9.6, 12 or 24 MHz, "
- "please correct your platform data\n",
- pcdev->pdata->camexclk_khz);
- pcdev->camexclk = 0;
- case 0:
- dev_info(&pdev->dev, "Not providing sensor clock\n");
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- spin_lock_init(&pcdev->lock);
-
- /*
- * Request the region.
- */
- if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) {
- err = -EBUSY;
- goto exit_kfree;
- }
-
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- err = -ENOMEM;
- goto exit_release;
- }
- pcdev->irq = irq;
- pcdev->base = base;
-
- sensor_reset(pcdev, true);
-
- err = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, DRIVER_NAME,
- dma_isr, (void *)pcdev, &pcdev->dma_ch);
- if (err < 0) {
- dev_err(&pdev->dev, "Can't request DMA for OMAP1 Camera\n");
- err = -EBUSY;
- goto exit_iounmap;
- }
- dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_ch);
-
- /* preconfigure DMA */
- omap_set_dma_src_params(pcdev->dma_ch, OMAP_DMA_PORT_TIPB,
- OMAP_DMA_AMODE_CONSTANT, res->start + REG_CAMDATA,
- 0, 0);
- omap_set_dma_dest_burst_mode(pcdev->dma_ch, OMAP_DMA_DATA_BURST_4);
- /* setup DMA autoinitialization */
- omap_dma_link_lch(pcdev->dma_ch, pcdev->dma_ch);
-
- err = request_irq(pcdev->irq, cam_isr, 0, DRIVER_NAME, pcdev);
- if (err) {
- dev_err(&pdev->dev, "Camera interrupt register failed\n");
- goto exit_free_dma;
- }
-
- pcdev->soc_host.drv_name = DRIVER_NAME;
- pcdev->soc_host.ops = &omap1_host_ops;
- pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
- pcdev->soc_host.nr = pdev->id;
-
- err = soc_camera_host_register(&pcdev->soc_host);
- if (err)
- goto exit_free_irq;
-
- dev_info(&pdev->dev, "OMAP1 Camera Interface driver loaded\n");
-
- return 0;
-
-exit_free_irq:
- free_irq(pcdev->irq, pcdev);
-exit_free_dma:
- omap_free_dma(pcdev->dma_ch);
-exit_iounmap:
- iounmap(base);
-exit_release:
- release_mem_region(res->start, resource_size(res));
-exit_kfree:
- kfree(pcdev);
-exit_put_clk:
- clk_put(clk);
-exit:
- return err;
-}
-
-static int __exit omap1_cam_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct omap1_cam_dev *pcdev = container_of(soc_host,
- struct omap1_cam_dev, soc_host);
- struct resource *res;
-
- free_irq(pcdev->irq, pcdev);
-
- omap_free_dma(pcdev->dma_ch);
-
- soc_camera_host_unregister(soc_host);
-
- iounmap(pcdev->base);
-
- res = pcdev->res;
- release_mem_region(res->start, resource_size(res));
-
- clk_put(pcdev->clk);
-
- kfree(pcdev);
-
- dev_info(&pdev->dev, "OMAP1 Camera Interface driver unloaded\n");
-
- return 0;
-}
-
-static struct platform_driver omap1_cam_driver = {
- .driver = {
- .name = DRIVER_NAME,
- },
- .probe = omap1_cam_probe,
- .remove = __exit_p(omap1_cam_remove),
-};
-
-module_platform_driver(omap1_cam_driver);
-
-module_param(sg_mode, bool, 0644);
-MODULE_PARM_DESC(sg_mode, "videobuf mode, 0: dma-contig (default), 1: dma-sg");
-
-MODULE_DESCRIPTION("OMAP1 Camera Interface driver");
-MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c
deleted file mode 100644
index b5ae170de4a5..000000000000
--- a/drivers/media/video/omap24xxcam-dma.c
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
- * drivers/media/video/omap24xxcam-dma.c
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.com> and
- * David Cohen <david.cohen@indt.org.br>.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/scatterlist.h>
-
-#include "omap24xxcam.h"
-
-/*
- *
- * DMA hardware.
- *
- */
-
-/* Ack all interrupt on CSR and IRQSTATUS_L0 */
-static void omap24xxcam_dmahw_ack_all(void __iomem *base)
-{
- u32 csr;
- int i;
-
- for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
- csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
- /* ack interrupt in CSR */
- omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
- }
- omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
-}
-
-/* Ack dmach on CSR and IRQSTATUS_L0 */
-static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach)
-{
- u32 csr;
-
- csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
- /* ack interrupt in CSR */
- omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
- /* ack interrupt in IRQSTATUS */
- omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
-
- return csr;
-}
-
-static int omap24xxcam_dmahw_running(void __iomem *base, int dmach)
-{
- return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
-}
-
-static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach,
- dma_addr_t start, u32 len)
-{
- omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
- CAMDMA_CCR_SEL_SRC_DST_SYNC
- | CAMDMA_CCR_BS
- | CAMDMA_CCR_DST_AMODE_POST_INC
- | CAMDMA_CCR_SRC_AMODE_POST_INC
- | CAMDMA_CCR_FS
- | CAMDMA_CCR_WR_ACTIVE
- | CAMDMA_CCR_RD_ACTIVE
- | CAMDMA_CCR_SYNCHRO_CAMERA);
- omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
- omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
- omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
- CAMDMA_CSDP_WRITE_MODE_POSTED
- | CAMDMA_CSDP_DST_BURST_EN_32
- | CAMDMA_CSDP_DST_PACKED
- | CAMDMA_CSDP_SRC_BURST_EN_32
- | CAMDMA_CSDP_SRC_PACKED
- | CAMDMA_CSDP_DATA_TYPE_8BITS);
- omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
- omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
- omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
- omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
- CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR
- | CAMDMA_CSR_BLOCK
- | CAMDMA_CSR_DROP);
- omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
- CAMDMA_CICR_MISALIGNED_ERR_IE
- | CAMDMA_CICR_SECURE_ERR_IE
- | CAMDMA_CICR_TRANS_ERR_IE
- | CAMDMA_CICR_BLOCK_IE
- | CAMDMA_CICR_DROP_IE);
-}
-
-static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach)
-{
- omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
- CAMDMA_CCR_SEL_SRC_DST_SYNC
- | CAMDMA_CCR_BS
- | CAMDMA_CCR_DST_AMODE_POST_INC
- | CAMDMA_CCR_SRC_AMODE_POST_INC
- | CAMDMA_CCR_ENABLE
- | CAMDMA_CCR_FS
- | CAMDMA_CCR_SYNCHRO_CAMERA);
-}
-
-static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach,
- int free_dmach)
-{
- int prev_dmach, ch;
-
- if (dmach == 0)
- prev_dmach = NUM_CAMDMA_CHANNELS - 1;
- else
- prev_dmach = dmach - 1;
- omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
- CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
- /* Did we chain the DMA transfer before the previous one
- * finished?
- */
- ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
- while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
- & CAMDMA_CCR_ENABLE)) {
- if (ch == dmach) {
- /* The previous transfer has ended and this one
- * hasn't started, so we must not have chained
- * to the previous one in time. We'll have to
- * start it now.
- */
- omap24xxcam_dmahw_transfer_start(base, dmach);
- break;
- } else
- ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
- }
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. The DMA
- * controller may not be idle after this routine completes, because
- * the completion routines might start new transfers.
- */
-static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach)
-{
- /* mask all interrupts from this channel */
- omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
- /* unlink this channel */
- omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
- CAMDMA_CLNK_CTRL_ENABLE_LNK);
- /* disable this channel */
- omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
-}
-
-static void omap24xxcam_dmahw_init(void __iomem *base)
-{
- omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
- CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
- | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
- | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
-
- omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
- CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
-
- omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
-}
-
-/*
- *
- * Individual DMA channel handling.
- *
- */
-
-/* Start a DMA transfer from the camera to memory.
- * Returns zero if the transfer was successfully started, or non-zero if all
- * DMA channels are already in use or starting is currently inhibited.
- */
-static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
- u32 len, dma_callback_t callback, void *arg)
-{
- unsigned long flags;
- int dmach;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
- spin_unlock_irqrestore(&dma->lock, flags);
- return -EBUSY;
- }
-
- dmach = dma->next_dmach;
-
- dma->ch_state[dmach].callback = callback;
- dma->ch_state[dmach].arg = arg;
-
- omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
-
- /* We're ready to start the DMA transfer. */
-
- if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
- /* A transfer is already in progress, so try to chain to it. */
- omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
- dma->free_dmach);
- } else {
- /* No transfer is in progress, so we'll just start this one
- * now.
- */
- omap24xxcam_dmahw_transfer_start(dma->base, dmach);
- }
-
- dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
- dma->free_dmach--;
-
- spin_unlock_irqrestore(&dma->lock, flags);
-
- return 0;
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. The DMA
- * controller may not be idle after this routine completes, because
- * the completion routines might start new transfers.
- */
-static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
-{
- unsigned long flags;
- int dmach, i, free_dmach;
- dma_callback_t callback;
- void *arg;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- /* stop any DMA transfers in progress */
- dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
- for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
- omap24xxcam_dmahw_abort_ch(dma->base, dmach);
- dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
- }
-
- /* We have to be careful here because the callback routine
- * might start a new DMA transfer, and we only want to abort
- * transfers that were started before this routine was called.
- */
- free_dmach = dma->free_dmach;
- while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
- (free_dmach < NUM_CAMDMA_CHANNELS)) {
- dmach = (dma->next_dmach + dma->free_dmach)
- % NUM_CAMDMA_CHANNELS;
- callback = dma->ch_state[dmach].callback;
- arg = dma->ch_state[dmach].arg;
- dma->free_dmach++;
- free_dmach++;
- if (callback) {
- /* leave interrupts disabled during callback */
- spin_unlock(&dma->lock);
- (*callback) (dma, csr, arg);
- spin_lock(&dma->lock);
- }
- }
-
- spin_unlock_irqrestore(&dma->lock, flags);
-}
-
-/* Abort all chained DMA transfers. After all transfers have been
- * aborted and the DMA controller is idle, the completion routines for
- * any aborted transfers will be called in sequence. If the completion
- * routines attempt to start a new DMA transfer it will fail, so the
- * DMA controller will be idle after this routine completes.
- */
-static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
-{
- atomic_inc(&dma->dma_stop);
- omap24xxcam_dma_abort(dma, csr);
- atomic_dec(&dma->dma_stop);
-}
-
-/* Camera DMA interrupt service routine. */
-void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
-{
- int dmach;
- dma_callback_t callback;
- void *arg;
- u32 csr;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock(&dma->lock);
-
- if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
- /* A camera DMA interrupt occurred while all channels
- * are idle, so we'll acknowledge the interrupt in the
- * IRQSTATUS register and exit.
- */
- omap24xxcam_dmahw_ack_all(dma->base);
- spin_unlock(&dma->lock);
- return;
- }
-
- while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
- dmach = (dma->next_dmach + dma->free_dmach)
- % NUM_CAMDMA_CHANNELS;
- if (omap24xxcam_dmahw_running(dma->base, dmach)) {
- /* This buffer hasn't finished yet, so we're done. */
- break;
- }
- csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
- if (csr & csr_error) {
- /* A DMA error occurred, so stop all DMA
- * transfers in progress.
- */
- spin_unlock(&dma->lock);
- omap24xxcam_dma_stop(dma, csr);
- return;
- } else {
- callback = dma->ch_state[dmach].callback;
- arg = dma->ch_state[dmach].arg;
- dma->free_dmach++;
- if (callback) {
- spin_unlock(&dma->lock);
- (*callback) (dma, csr, arg);
- spin_lock(&dma->lock);
- }
- }
- }
-
- spin_unlock(&dma->lock);
-
- omap24xxcam_sgdma_process(
- container_of(dma, struct omap24xxcam_sgdma, dma));
-}
-
-void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dma->lock, flags);
-
- omap24xxcam_dmahw_init(dma->base);
-
- spin_unlock_irqrestore(&dma->lock, flags);
-}
-
-static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
- void __iomem *base)
-{
- int ch;
-
- /* group all channels on DMA IRQ0 and unmask irq */
- spin_lock_init(&dma->lock);
- dma->base = base;
- dma->free_dmach = NUM_CAMDMA_CHANNELS;
- dma->next_dmach = 0;
- for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
- dma->ch_state[ch].callback = NULL;
- dma->ch_state[ch].arg = NULL;
- }
-}
-
-/*
- *
- * Scatter-gather DMA.
- *
- * High-level DMA construct for transferring whole picture frames to
- * memory that is discontinuous.
- *
- */
-
-/* DMA completion routine for the scatter-gather DMA fragments. */
-static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
- void *arg)
-{
- struct omap24xxcam_sgdma *sgdma =
- container_of(dma, struct omap24xxcam_sgdma, dma);
- int sgslot = (int)arg;
- struct sgdma_state *sg_state;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock(&sgdma->lock);
-
- /* We got an interrupt, we can remove the timer */
- del_timer(&sgdma->reset_timer);
-
- sg_state = sgdma->sg_state + sgslot;
- if (!sg_state->queued_sglist) {
- spin_unlock(&sgdma->lock);
- printk(KERN_ERR "%s: sgdma completed when none queued!\n",
- __func__);
- return;
- }
-
- sg_state->csr |= csr;
- if (!--sg_state->queued_sglist) {
- /* Queue for this sglist is empty, so check to see if we're
- * done.
- */
- if ((sg_state->next_sglist == sg_state->sglen)
- || (sg_state->csr & csr_error)) {
- sgdma_callback_t callback = sg_state->callback;
- void *arg = sg_state->arg;
- u32 sg_csr = sg_state->csr;
- /* All done with this sglist */
- sgdma->free_sgdma++;
- if (callback) {
- spin_unlock(&sgdma->lock);
- (*callback) (sgdma, sg_csr, arg);
- return;
- }
- }
- }
-
- spin_unlock(&sgdma->lock);
-}
-
-/* Start queued scatter-gather DMA transfers. */
-void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
-{
- unsigned long flags;
- int queued_sgdma, sgslot;
- struct sgdma_state *sg_state;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
- sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
- while (queued_sgdma > 0) {
- sg_state = sgdma->sg_state + sgslot;
- while ((sg_state->next_sglist < sg_state->sglen) &&
- !(sg_state->csr & csr_error)) {
- const struct scatterlist *sglist;
- unsigned int len;
-
- sglist = sg_state->sglist + sg_state->next_sglist;
- /* try to start the next DMA transfer */
- if (sg_state->next_sglist + 1 == sg_state->sglen) {
- /*
- * On the last sg, we handle the case where
- * cam->img.pix.sizeimage % PAGE_ALIGN != 0
- */
- len = sg_state->len - sg_state->bytes_read;
- } else {
- len = sg_dma_len(sglist);
- }
-
- if (omap24xxcam_dma_start(&sgdma->dma,
- sg_dma_address(sglist),
- len,
- omap24xxcam_sgdma_callback,
- (void *)sgslot)) {
- /* DMA start failed */
- spin_unlock_irqrestore(&sgdma->lock, flags);
- return;
- } else {
- unsigned long expires;
- /* DMA start was successful */
- sg_state->next_sglist++;
- sg_state->bytes_read += len;
- sg_state->queued_sglist++;
-
- /* We start the reset timer */
- expires = jiffies + HZ;
- mod_timer(&sgdma->reset_timer, expires);
- }
- }
- queued_sgdma--;
- sgslot = (sgslot + 1) % NUM_SG_DMA;
- }
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-}
-
-/*
- * Queue a scatter-gather DMA transfer from the camera to memory.
- * Returns zero if the transfer was successfully queued, or non-zero
- * if all of the scatter-gather slots are already in use.
- */
-int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
- const struct scatterlist *sglist, int sglen,
- int len, sgdma_callback_t callback, void *arg)
-{
- unsigned long flags;
- struct sgdma_state *sg_state;
-
- if ((sglen < 0) || ((sglen > 0) && !sglist))
- return -EINVAL;
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- if (!sgdma->free_sgdma) {
- spin_unlock_irqrestore(&sgdma->lock, flags);
- return -EBUSY;
- }
-
- sg_state = sgdma->sg_state + sgdma->next_sgdma;
-
- sg_state->sglist = sglist;
- sg_state->sglen = sglen;
- sg_state->next_sglist = 0;
- sg_state->bytes_read = 0;
- sg_state->len = len;
- sg_state->queued_sglist = 0;
- sg_state->csr = 0;
- sg_state->callback = callback;
- sg_state->arg = arg;
-
- sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
- sgdma->free_sgdma--;
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-
- omap24xxcam_sgdma_process(sgdma);
-
- return 0;
-}
-
-/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
- * Any queued scatter-gather DMA transactions that have not yet been started
- * will remain queued. The DMA controller will be idle after this routine
- * completes. When the scatter-gather queue is restarted, the next
- * scatter-gather DMA transfer will begin at the start of a new transaction.
- */
-void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
-{
- unsigned long flags;
- int sgslot;
- struct sgdma_state *sg_state;
- u32 csr = CAMDMA_CSR_TRANS_ERR;
-
- /* stop any DMA transfers in progress */
- omap24xxcam_dma_stop(&sgdma->dma, csr);
-
- spin_lock_irqsave(&sgdma->lock, flags);
-
- if (sgdma->free_sgdma < NUM_SG_DMA) {
- sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
- sg_state = sgdma->sg_state + sgslot;
- if (sg_state->next_sglist != 0) {
- /* This DMA transfer was in progress, so abort it. */
- sgdma_callback_t callback = sg_state->callback;
- void *arg = sg_state->arg;
- sgdma->free_sgdma++;
- if (callback) {
- /* leave interrupts masked */
- spin_unlock(&sgdma->lock);
- (*callback) (sgdma, csr, arg);
- spin_lock(&sgdma->lock);
- }
- }
- }
-
- spin_unlock_irqrestore(&sgdma->lock, flags);
-}
-
-void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
- void __iomem *base,
- void (*reset_callback)(unsigned long data),
- unsigned long reset_callback_data)
-{
- int sg;
-
- spin_lock_init(&sgdma->lock);
- sgdma->free_sgdma = NUM_SG_DMA;
- sgdma->next_sgdma = 0;
- for (sg = 0; sg < NUM_SG_DMA; sg++) {
- sgdma->sg_state[sg].sglen = 0;
- sgdma->sg_state[sg].next_sglist = 0;
- sgdma->sg_state[sg].bytes_read = 0;
- sgdma->sg_state[sg].queued_sglist = 0;
- sgdma->sg_state[sg].csr = 0;
- sgdma->sg_state[sg].callback = NULL;
- sgdma->sg_state[sg].arg = NULL;
- }
-
- omap24xxcam_dma_init(&sgdma->dma, base);
- setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
-}
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
deleted file mode 100644
index e5015b0d5508..000000000000
--- a/drivers/media/video/omap24xxcam.c
+++ /dev/null
@@ -1,1881 +0,0 @@
-/*
- * drivers/media/video/omap24xxcam.c
- *
- * OMAP 2 camera block driver.
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007-2008 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/videodev2.h>
-#include <linux/pci.h> /* needed for videobufs */
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/module.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-#include "omap24xxcam.h"
-
-#define OMAP24XXCAM_VERSION "0.0.1"
-
-#define RESET_TIMEOUT_NS 10000
-
-static void omap24xxcam_reset(struct omap24xxcam_device *cam);
-static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
-static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
-static int omap24xxcam_remove(struct platform_device *pdev);
-
-/* module parameters */
-static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
-/*
- * Maximum amount of memory to use for capture buffers.
- * Default is 4800KB, enough to double-buffer SXGA.
- */
-static int capture_mem = 1280 * 960 * 2 * 2;
-
-static struct v4l2_int_device omap24xxcam;
-
-/*
- *
- * Clocks.
- *
- */
-
-static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
-{
- if (cam->ick != NULL && !IS_ERR(cam->ick))
- clk_put(cam->ick);
- if (cam->fck != NULL && !IS_ERR(cam->fck))
- clk_put(cam->fck);
-
- cam->ick = cam->fck = NULL;
-}
-
-static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
-{
- int rval = 0;
-
- cam->fck = clk_get(cam->dev, "fck");
- if (IS_ERR(cam->fck)) {
- dev_err(cam->dev, "can't get camera fck");
- rval = PTR_ERR(cam->fck);
- omap24xxcam_clock_put(cam);
- return rval;
- }
-
- cam->ick = clk_get(cam->dev, "ick");
- if (IS_ERR(cam->ick)) {
- dev_err(cam->dev, "can't get camera ick");
- rval = PTR_ERR(cam->ick);
- omap24xxcam_clock_put(cam);
- }
-
- return rval;
-}
-
-static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
-{
- clk_enable(cam->fck);
- clk_enable(cam->ick);
-}
-
-static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
-{
- clk_disable(cam->fck);
- clk_disable(cam->ick);
-}
-
-/*
- *
- * Camera core
- *
- */
-
-/*
- * Set xclk.
- *
- * To disable xclk, use value zero.
- */
-static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
- u32 xclk)
-{
- if (xclk) {
- u32 divisor = CAM_MCLK / xclk;
-
- if (divisor == 1)
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK,
- CC_CTRL_XCLK_DIV_BYPASS);
- else
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK, divisor);
- } else
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
- CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
-}
-
-static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
-{
- /*
- * Setting the camera core AUTOIDLE bit causes problems with frame
- * synchronization, so we will clear the AUTOIDLE bit instead.
- */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
- CC_SYSCONFIG_AUTOIDLE);
-
- /* program the camera interface DMA packet size */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
- CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
-
- /* enable camera core error interrupts */
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
- CC_IRQENABLE_FW_ERR_IRQ
- | CC_IRQENABLE_FSC_ERR_IRQ
- | CC_IRQENABLE_SSC_ERR_IRQ
- | CC_IRQENABLE_FIFO_OF_IRQ);
-}
-
-/*
- * Enable the camera core.
- *
- * Data transfer to the camera DMA starts from next starting frame.
- */
-static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
-{
-
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
- cam->cc_ctrl);
-}
-
-/*
- * Disable camera core.
- *
- * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
- * core internal state machines will be reset. Use
- * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
- * frame completely.
- */
-static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
-{
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
- CC_CTRL_CC_RST);
-}
-
-/* Interrupt service routine for camera core interrupts. */
-static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
-{
- u32 cc_irqstatus;
- const u32 cc_irqstatus_err =
- CC_IRQSTATUS_FW_ERR_IRQ
- | CC_IRQSTATUS_FSC_ERR_IRQ
- | CC_IRQSTATUS_SSC_ERR_IRQ
- | CC_IRQSTATUS_FIFO_UF_IRQ
- | CC_IRQSTATUS_FIFO_OF_IRQ;
-
- cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
- CC_IRQSTATUS);
- omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
- cc_irqstatus);
-
- if (cc_irqstatus & cc_irqstatus_err
- && !atomic_read(&cam->in_reset)) {
- dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
- cc_irqstatus);
- omap24xxcam_reset(cam);
- }
-}
-
-/*
- *
- * videobuf_buffer handling.
- *
- * Memory for mmapped videobuf_buffers is not allocated
- * conventionally, but by several kmalloc allocations and then
- * creating the scatterlist on our own. User-space buffers are handled
- * normally.
- *
- */
-
-/*
- * Free the memory-mapped buffer memory allocated for a
- * videobuf_buffer and the associated scatterlist.
- */
-static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
- size_t alloc_size;
- struct page *page;
- int i;
-
- if (dma->sglist == NULL)
- return;
-
- i = dma->sglen;
- while (i) {
- i--;
- alloc_size = sg_dma_len(&dma->sglist[i]);
- page = sg_page(&dma->sglist[i]);
- do {
- ClearPageReserved(page++);
- } while (alloc_size -= PAGE_SIZE);
- __free_pages(sg_page(&dma->sglist[i]),
- get_order(sg_dma_len(&dma->sglist[i])));
- }
-
- kfree(dma->sglist);
- dma->sglist = NULL;
-}
-
-/* Release all memory related to the videobuf_queue. */
-static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
-{
- int i;
-
- mutex_lock(&vbq->vb_lock);
-
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- if (NULL == vbq->bufs[i])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
- continue;
- vbq->ops->buf_release(vbq, vbq->bufs[i]);
- omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
- kfree(vbq->bufs[i]);
- vbq->bufs[i] = NULL;
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- videobuf_mmap_free(vbq);
-}
-
-/*
- * Allocate physically as contiguous as possible buffer for video
- * frame and allocate and build DMA scatter-gather list for it.
- */
-static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
-{
- unsigned int order;
- size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
- struct page *page;
- int max_pages, err = 0, i = 0;
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- /*
- * allocate maximum size scatter-gather list. Note this is
- * overhead. We may not use as many entries as we allocate
- */
- max_pages = vb->bsize >> PAGE_SHIFT;
- dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
- if (dma->sglist == NULL) {
- err = -ENOMEM;
- goto out;
- }
-
- while (size) {
- order = get_order(size);
- /*
- * do not over-allocate even if we would get larger
- * contiguous chunk that way
- */
- if ((PAGE_SIZE << order) > size)
- order--;
-
- /* try to allocate as many contiguous pages as possible */
- page = alloc_pages(GFP_KERNEL, order);
- /* if allocation fails, try to allocate smaller amount */
- while (page == NULL) {
- order--;
- page = alloc_pages(GFP_KERNEL, order);
- if (page == NULL && !order) {
- err = -ENOMEM;
- goto out;
- }
- }
- size -= (PAGE_SIZE << order);
-
- /* append allocated chunk of pages into scatter-gather list */
- sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
- dma->sglen++;
- i++;
-
- alloc_size = (PAGE_SIZE << order);
-
- /* clear pages before giving them to user space */
- memset(page_address(page), 0, alloc_size);
-
- /* mark allocated pages reserved */
- do {
- SetPageReserved(page++);
- } while (alloc_size -= PAGE_SIZE);
- }
- /*
- * REVISIT: not fully correct to assign nr_pages == sglen but
- * video-buf is passing nr_pages for e.g. unmap_sg calls
- */
- dma->nr_pages = dma->sglen;
- dma->direction = PCI_DMA_FROMDEVICE;
-
- return 0;
-
-out:
- omap24xxcam_vbq_free_mmap_buffer(vb);
- return err;
-}
-
-static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
- unsigned int count)
-{
- int i, err = 0;
- struct omap24xxcam_fh *fh =
- container_of(vbq, struct omap24xxcam_fh, vbq);
-
- mutex_lock(&vbq->vb_lock);
-
- for (i = 0; i < count; i++) {
- err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
- if (err)
- goto out;
- dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
- videobuf_to_dma(vbq->bufs[i])->sglen, i);
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- return 0;
-out:
- while (i) {
- i--;
- omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
- }
-
- mutex_unlock(&vbq->vb_lock);
-
- return err;
-}
-
-/*
- * This routine is called from interrupt context when a scatter-gather DMA
- * transfer of a videobuf_buffer completes.
- */
-static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
- u32 csr, void *arg)
-{
- struct omap24xxcam_device *cam =
- container_of(sgdma, struct omap24xxcam_device, sgdma);
- struct omap24xxcam_fh *fh = cam->streaming->private_data;
- struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
- const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
- | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
- | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
- if (--cam->sgdma_in_queue == 0)
- omap24xxcam_core_disable(cam);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-
- do_gettimeofday(&vb->ts);
- vb->field_count = atomic_add_return(2, &fh->field_count);
- if (csr & csr_error) {
- vb->state = VIDEOBUF_ERROR;
- if (!atomic_read(&fh->cam->in_reset)) {
- dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
- omap24xxcam_reset(cam);
- }
- } else
- vb->state = VIDEOBUF_DONE;
- wake_up(&vb->done);
-}
-
-static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb)
-{
- struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
- /* wait for buffer, especially to get out of the sgdma queue */
- videobuf_waiton(vbq, vb, 0, 0);
- if (vb->memory == V4L2_MEMORY_MMAP) {
- dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
- dma->direction);
- dma->direction = DMA_NONE;
- } else {
- videobuf_dma_unmap(vbq->dev, videobuf_to_dma(vb));
- videobuf_dma_free(videobuf_to_dma(vb));
- }
-
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-/*
- * Limit the number of available kernel image capture buffers based on the
- * number requested, the currently selected image size, and the maximum
- * amount of memory permitted for kernel capture buffers.
- */
-static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
- unsigned int *size)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
-
- if (*cnt <= 0)
- *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
-
- if (*cnt > VIDEO_MAX_FRAME)
- *cnt = VIDEO_MAX_FRAME;
-
- *size = fh->pix.sizeimage;
-
- /* accessing fh->cam->capture_mem is ok, it's constant */
- if (*size * *cnt > fh->cam->capture_mem)
- *cnt = fh->cam->capture_mem / *size;
-
- return 0;
-}
-
-static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
- struct videobuf_dmabuf *dma)
-{
- int err = 0;
-
- dma->direction = PCI_DMA_FROMDEVICE;
- if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
- kfree(dma->sglist);
- dma->sglist = NULL;
- dma->sglen = 0;
- err = -EIO;
- }
-
- return err;
-}
-
-static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
- int err = 0;
-
- /*
- * Accessing pix here is okay since it's constant while
- * streaming is on (and we only get called then).
- */
- if (vb->baddr) {
- /* This is a userspace buffer. */
- if (fh->pix.sizeimage > vb->bsize) {
- /* The buffer isn't big enough. */
- err = -EINVAL;
- } else
- vb->size = fh->pix.sizeimage;
- } else {
- if (vb->state != VIDEOBUF_NEEDS_INIT) {
- /*
- * We have a kernel bounce buffer that has
- * already been allocated.
- */
- if (fh->pix.sizeimage > vb->size) {
- /*
- * The image size has been changed to
- * a larger size since this buffer was
- * allocated, so we need to free and
- * reallocate it.
- */
- omap24xxcam_vbq_release(vbq, vb);
- vb->size = fh->pix.sizeimage;
- }
- } else {
- /* We need to allocate a new kernel bounce buffer. */
- vb->size = fh->pix.sizeimage;
- }
- }
-
- if (err)
- return err;
-
- vb->width = fh->pix.width;
- vb->height = fh->pix.height;
- vb->field = field;
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- if (vb->memory == V4L2_MEMORY_MMAP)
- /*
- * we have built the scatter-gather list by ourself so
- * do the scatter-gather mapping as well
- */
- err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
- else
- err = videobuf_iolock(vbq, vb, NULL);
- }
-
- if (!err)
- vb->state = VIDEOBUF_PREPARED;
- else
- omap24xxcam_vbq_release(vbq, vb);
-
- return err;
-}
-
-static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
- struct videobuf_buffer *vb)
-{
- struct omap24xxcam_fh *fh = vbq->priv_data;
- struct omap24xxcam_device *cam = fh->cam;
- enum videobuf_state state = vb->state;
- unsigned long flags;
- int err;
-
- /*
- * FIXME: We're marking the buffer active since we have no
- * pretty way of marking it active exactly when the
- * scatter-gather transfer starts.
- */
- vb->state = VIDEOBUF_ACTIVE;
-
- err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
- videobuf_to_dma(vb)->sglist,
- videobuf_to_dma(vb)->sglen, vb->size,
- omap24xxcam_vbq_complete, vb);
-
- if (!err) {
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
- if (++cam->sgdma_in_queue == 1
- && !atomic_read(&cam->in_reset))
- omap24xxcam_core_enable(cam);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
- } else {
- /*
- * Oops. We're not supposed to get any errors here.
- * The only way we could get an error is if we ran out
- * of scatter-gather DMA slots, but we are supposed to
- * have at least as many scatter-gather DMA slots as
- * video buffers so that can't happen.
- */
- dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
- dev_err(cam->dev, "likely a bug in the driver!\n");
- vb->state = state;
- }
-}
-
-static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
- .buf_setup = omap24xxcam_vbq_setup,
- .buf_prepare = omap24xxcam_vbq_prepare,
- .buf_queue = omap24xxcam_vbq_queue,
- .buf_release = omap24xxcam_vbq_release,
-};
-
-/*
- *
- * OMAP main camera system
- *
- */
-
-/*
- * Reset camera block to power-on state.
- */
-static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
-{
- int max_loop = RESET_TIMEOUT_NS;
-
- /* Reset whole camera subsystem */
- omap24xxcam_reg_out(cam->mmio_base,
- CAM_SYSCONFIG,
- CAM_SYSCONFIG_SOFTRESET);
-
- /* Wait till it's finished */
- while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
- & CAM_SYSSTATUS_RESETDONE)
- && --max_loop) {
- ndelay(1);
- }
-
- if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
- & CAM_SYSSTATUS_RESETDONE))
- dev_err(cam->dev, "camera soft reset timeout\n");
-}
-
-/*
- * (Re)initialise the camera block.
- */
-static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
-{
- omap24xxcam_poweron_reset(cam);
-
- /* set the camera subsystem autoidle bit */
- omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
- CAM_SYSCONFIG_AUTOIDLE);
-
- /* set the camera MMU autoidle bit */
- omap24xxcam_reg_out(cam->mmio_base,
- CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
- CAMMMU_SYSCONFIG_AUTOIDLE);
-
- omap24xxcam_core_hwinit(cam);
-
- omap24xxcam_dma_hwinit(&cam->sgdma.dma);
-}
-
-/*
- * Callback for dma transfer stalling.
- */
-static void omap24xxcam_stalled_dma_reset(unsigned long data)
-{
- struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
-
- if (!atomic_read(&cam->in_reset)) {
- dev_dbg(cam->dev, "dma stalled, resetting camera\n");
- omap24xxcam_reset(cam);
- }
-}
-
-/*
- * Stop capture. Mark we're doing a reset, stop DMA transfers and
- * core. (No new scatter-gather transfers will be queued whilst
- * in_reset is non-zero.)
- *
- * If omap24xxcam_capture_stop is called from several places at
- * once, only the first call will have an effect. Similarly, the last
- * call omap24xxcam_streaming_cont will have effect.
- *
- * Serialisation is ensured by using cam->core_enable_disable_lock.
- */
-static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
-
- if (atomic_inc_return(&cam->in_reset) != 1) {
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
- return;
- }
-
- omap24xxcam_core_disable(cam);
-
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-
- omap24xxcam_sgdma_sync(&cam->sgdma);
-}
-
-/*
- * Reset and continue streaming.
- *
- * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
- * register is supposed to be sufficient to recover from a camera
- * interface error, but it doesn't seem to be enough. If we only do
- * that then subsequent image captures are out of sync by either one
- * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
- * entire camera subsystem prevents the problem with frame
- * synchronization.
- */
-static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
-
- if (atomic_read(&cam->in_reset) != 1)
- goto out;
-
- omap24xxcam_hwinit(cam);
-
- omap24xxcam_sensor_if_enable(cam);
-
- omap24xxcam_sgdma_process(&cam->sgdma);
-
- if (cam->sgdma_in_queue)
- omap24xxcam_core_enable(cam);
-
-out:
- atomic_dec(&cam->in_reset);
- spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
-}
-
-static ssize_t
-omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct omap24xxcam_device *cam = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
-}
-static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
-
-/*
- * Stop capture and restart it. I.e. reset the camera during use.
- */
-static void omap24xxcam_reset(struct omap24xxcam_device *cam)
-{
- omap24xxcam_capture_stop(cam);
- omap24xxcam_capture_cont(cam);
-}
-
-/*
- * The main interrupt handler.
- */
-static irqreturn_t omap24xxcam_isr(int irq, void *arg)
-{
- struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
- u32 irqstatus;
- unsigned int irqhandled = 0;
-
- irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
-
- if (irqstatus &
- (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
- | CAM_IRQSTATUS_DMA_IRQ0)) {
- omap24xxcam_dma_isr(&cam->sgdma.dma);
- irqhandled = 1;
- }
- if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
- omap24xxcam_core_isr(cam);
- irqhandled = 1;
- }
- if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
- dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
-
- return IRQ_RETVAL(irqhandled);
-}
-
-/*
- *
- * Sensor handling.
- *
- */
-
-/*
- * Enable the external sensor interface. Try to negotiate interface
- * parameters with the sensor and start using the new ones. The calls
- * to sensor_if_enable and sensor_if_disable need not to be balanced.
- */
-static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
-{
- int rval;
- struct v4l2_ifparm p;
-
- rval = vidioc_int_g_ifparm(cam->sdev, &p);
- if (rval) {
- dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
- return rval;
- }
-
- cam->if_type = p.if_type;
-
- cam->cc_ctrl = CC_CTRL_CC_EN;
-
- switch (p.if_type) {
- case V4L2_IF_TYPE_BT656:
- if (p.u.bt656.frame_start_on_rising_vs)
- cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
- if (p.u.bt656.bt_sync_correct)
- cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
- if (p.u.bt656.swap)
- cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
- if (p.u.bt656.latch_clk_inv)
- cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
- if (p.u.bt656.nobt_hs_inv)
- cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
- if (p.u.bt656.nobt_vs_inv)
- cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
-
- switch (p.u.bt656.mode) {
- case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
- break;
- case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
- break;
- case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
- break;
- case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
- break;
- case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
- cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
- break;
- default:
- dev_err(cam->dev,
- "bt656 interface mode %d not supported\n",
- p.u.bt656.mode);
- return -EINVAL;
- }
- /*
- * The clock rate that the sensor wants has changed.
- * We have to adjust the xclk from OMAP 2 side to
- * match the sensor's wish as closely as possible.
- */
- if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
- u32 xclk = p.u.bt656.clock_curr;
- u32 divisor;
-
- if (xclk == 0)
- return -EINVAL;
-
- if (xclk > CAM_MCLK)
- xclk = CAM_MCLK;
-
- divisor = CAM_MCLK / xclk;
- if (divisor * xclk < CAM_MCLK)
- divisor++;
- if (CAM_MCLK / divisor < p.u.bt656.clock_min
- && divisor > 1)
- divisor--;
- if (divisor > 30)
- divisor = 30;
-
- xclk = CAM_MCLK / divisor;
-
- if (xclk < p.u.bt656.clock_min
- || xclk > p.u.bt656.clock_max)
- return -EINVAL;
-
- cam->if_u.bt656.xclk = xclk;
- }
- omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
- break;
- default:
- /* FIXME: how about other interfaces? */
- dev_err(cam->dev, "interface type %d not supported\n",
- p.if_type);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
-{
- switch (cam->if_type) {
- case V4L2_IF_TYPE_BT656:
- omap24xxcam_core_xclk_set(cam, 0);
- break;
- }
-}
-
-/*
- * Initialise the sensor hardware.
- */
-static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
-{
- int err = 0;
- struct v4l2_int_device *sdev = cam->sdev;
-
- omap24xxcam_clock_on(cam);
- err = omap24xxcam_sensor_if_enable(cam);
- if (err) {
- dev_err(cam->dev, "sensor interface could not be enabled at "
- "initialisation, %d\n", err);
- cam->sdev = NULL;
- goto out;
- }
-
- /* power up sensor during sensor initialization */
- vidioc_int_s_power(sdev, 1);
-
- err = vidioc_int_dev_init(sdev);
- if (err) {
- dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
- /* Sensor init failed --- it's nonexistent to us! */
- cam->sdev = NULL;
- goto out;
- }
-
- dev_info(cam->dev, "sensor is %s\n", sdev->name);
-
-out:
- omap24xxcam_sensor_if_disable(cam);
- omap24xxcam_clock_off(cam);
-
- vidioc_int_s_power(sdev, 0);
-
- return err;
-}
-
-static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
-{
- if (cam->sdev)
- vidioc_int_dev_exit(cam->sdev);
-}
-
-static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
-{
- omap24xxcam_sensor_if_disable(cam);
- omap24xxcam_clock_off(cam);
- vidioc_int_s_power(cam->sdev, 0);
-}
-
-/*
- * Power-up and configure camera sensor. It's ready for capturing now.
- */
-static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
-{
- int rval;
-
- omap24xxcam_clock_on(cam);
-
- omap24xxcam_sensor_if_enable(cam);
-
- rval = vidioc_int_s_power(cam->sdev, 1);
- if (rval)
- goto out;
-
- rval = vidioc_int_init(cam->sdev);
- if (rval)
- goto out;
-
- return 0;
-
-out:
- omap24xxcam_sensor_disable(cam);
-
- return rval;
-}
-
-static void omap24xxcam_sensor_reset_work(struct work_struct *work)
-{
- struct omap24xxcam_device *cam =
- container_of(work, struct omap24xxcam_device,
- sensor_reset_work);
-
- if (atomic_read(&cam->reset_disable))
- return;
-
- omap24xxcam_capture_stop(cam);
-
- if (vidioc_int_reset(cam->sdev) == 0) {
- vidioc_int_init(cam->sdev);
- } else {
- /* Can't reset it by vidioc_int_reset. */
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_sensor_enable(cam);
- }
-
- omap24xxcam_capture_cont(cam);
-}
-
-/*
- *
- * IOCTL interface.
- *
- */
-
-static int vidioc_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
-
- strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
- strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
-
- return rval;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_fmt_cap(cam->sdev, f);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- rval = vidioc_int_s_fmt_cap(cam->sdev, f);
-
-out:
- mutex_unlock(&cam->mutex);
-
- if (!rval) {
- mutex_lock(&ofh->vbq.vb_lock);
- ofh->pix = f->fmt.pix;
- mutex_unlock(&ofh->vbq.vb_lock);
- }
-
- memset(f, 0, sizeof(*f));
- vidioc_g_fmt_vid_cap(file, fh, f);
-
- return rval;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_try_fmt_cap(cam->sdev, f);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *b)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- mutex_unlock(&cam->mutex);
- return -EBUSY;
- }
-
- omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
- mutex_unlock(&cam->mutex);
-
- rval = videobuf_reqbufs(&ofh->vbq, b);
-
- /*
- * Either videobuf_reqbufs failed or the buffers are not
- * memory-mapped (which would need special attention).
- */
- if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
- goto out;
-
- rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
- if (rval)
- omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
-
-out:
- return rval;
-}
-
-static int vidioc_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
-
- return videobuf_querybuf(&ofh->vbq, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
-
- return videobuf_qbuf(&ofh->vbq, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct videobuf_buffer *vb;
- int rval;
-
-videobuf_dqbuf_again:
- rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
- if (rval)
- goto out;
-
- vb = ofh->vbq.bufs[b->index];
-
- mutex_lock(&cam->mutex);
- /* _needs_reset returns -EIO if reset is required. */
- rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
- mutex_unlock(&cam->mutex);
- if (rval == -EIO)
- schedule_work(&cam->sensor_reset_work);
- else
- rval = 0;
-
-out:
- /*
- * This is a hack. We don't want to show -EIO to the user
- * space. Requeue the buffer and try again if we're not doing
- * this in non-blocking mode.
- */
- if (rval == -EIO) {
- videobuf_qbuf(&ofh->vbq, b);
- if (!(file->f_flags & O_NONBLOCK))
- goto videobuf_dqbuf_again;
- /*
- * We don't have a videobuf_buffer now --- maybe next
- * time...
- */
- rval = -EAGAIN;
- }
-
- return rval;
-}
-
-static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- rval = omap24xxcam_sensor_if_enable(cam);
- if (rval) {
- dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
- goto out;
- }
-
- rval = videobuf_streamon(&ofh->vbq);
- if (!rval) {
- cam->streaming = file;
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- }
-
-out:
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct videobuf_queue *q = &ofh->vbq;
- int rval;
-
- atomic_inc(&cam->reset_disable);
-
- flush_work_sync(&cam->sensor_reset_work);
-
- rval = videobuf_streamoff(q);
- if (!rval) {
- mutex_lock(&cam->mutex);
- cam->streaming = NULL;
- mutex_unlock(&cam->mutex);
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- }
-
- atomic_dec(&cam->reset_disable);
-
- return rval;
-}
-
-static int vidioc_enum_input(struct file *file, void *fh,
- struct v4l2_input *inp)
-{
- if (inp->index > 0)
- return -EINVAL;
-
- strlcpy(inp->name, "camera", sizeof(inp->name));
- inp->type = V4L2_INPUT_TYPE_CAMERA;
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
- *i = 0;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *fh,
- struct v4l2_queryctrl *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- rval = vidioc_int_queryctrl(cam->sdev, a);
-
- return rval;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh,
- struct v4l2_control *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_ctrl(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh,
- struct v4l2_control *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_s_ctrl(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a) {
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- int rval;
-
- mutex_lock(&cam->mutex);
- rval = vidioc_int_g_parm(cam->sdev, a);
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-static int vidioc_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct omap24xxcam_fh *ofh = fh;
- struct omap24xxcam_device *cam = ofh->cam;
- struct v4l2_streamparm old_streamparm;
- int rval;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- rval = -EBUSY;
- goto out;
- }
-
- old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
- if (rval)
- goto out;
-
- rval = vidioc_int_s_parm(cam->sdev, a);
- if (rval)
- goto out;
-
- rval = omap24xxcam_sensor_if_enable(cam);
- /*
- * Revert to old streaming parameters if enabling sensor
- * interface with the new ones failed.
- */
- if (rval)
- vidioc_int_s_parm(cam->sdev, &old_streamparm);
-
-out:
- mutex_unlock(&cam->mutex);
-
- return rval;
-}
-
-/*
- *
- * File operations.
- *
- */
-
-static unsigned int omap24xxcam_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
- struct videobuf_buffer *vb;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming != file) {
- mutex_unlock(&cam->mutex);
- return POLLERR;
- }
- mutex_unlock(&cam->mutex);
-
- mutex_lock(&fh->vbq.vb_lock);
- if (list_empty(&fh->vbq.stream)) {
- mutex_unlock(&fh->vbq.vb_lock);
- return POLLERR;
- }
- vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
- mutex_unlock(&fh->vbq.vb_lock);
-
- poll_wait(file, &vb->done, wait);
-
- if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
- return POLLIN | POLLRDNORM;
-
- return 0;
-}
-
-static int omap24xxcam_mmap_buffers(struct file *file,
- struct vm_area_struct *vma)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
- struct videobuf_queue *vbq = &fh->vbq;
- unsigned int first, last, size, i, j;
- int err = 0;
-
- mutex_lock(&cam->mutex);
- if (cam->streaming) {
- mutex_unlock(&cam->mutex);
- return -EBUSY;
- }
- mutex_unlock(&cam->mutex);
- mutex_lock(&vbq->vb_lock);
-
- /* look for first buffer to map */
- for (first = 0; first < VIDEO_MAX_FRAME; first++) {
- if (NULL == vbq->bufs[first])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
- continue;
- if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
- break;
- }
-
- /* look for last buffer to map */
- for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
- if (NULL == vbq->bufs[last])
- continue;
- if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
- continue;
- size += vbq->bufs[last]->bsize;
- if (size == (vma->vm_end - vma->vm_start))
- break;
- }
-
- size = 0;
- for (i = first; i <= last && i < VIDEO_MAX_FRAME; i++) {
- struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
-
- for (j = 0; j < dma->sglen; j++) {
- err = remap_pfn_range(
- vma, vma->vm_start + size,
- page_to_pfn(sg_page(&dma->sglist[j])),
- sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
- if (err)
- goto out;
- size += sg_dma_len(&dma->sglist[j]);
- }
- }
-
-out:
- mutex_unlock(&vbq->vb_lock);
-
- return err;
-}
-
-static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- int rval;
-
- /* let the video-buf mapper check arguments and set-up structures */
- rval = videobuf_mmap_mapper(&fh->vbq, vma);
- if (rval)
- return rval;
-
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-
- /* do mapping to our allocated buffers */
- rval = omap24xxcam_mmap_buffers(file, vma);
- /*
- * In case of error, free vma->vm_private_data allocated by
- * videobuf_mmap_mapper.
- */
- if (rval)
- kfree(vma->vm_private_data);
-
- return rval;
-}
-
-static int omap24xxcam_open(struct file *file)
-{
- struct omap24xxcam_device *cam = omap24xxcam.priv;
- struct omap24xxcam_fh *fh;
- struct v4l2_format format;
-
- if (!cam || !cam->vfd)
- return -ENODEV;
-
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (fh == NULL)
- return -ENOMEM;
-
- mutex_lock(&cam->mutex);
- if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
- mutex_unlock(&cam->mutex);
- goto out_try_module_get;
- }
-
- if (atomic_inc_return(&cam->users) == 1) {
- omap24xxcam_hwinit(cam);
- if (omap24xxcam_sensor_enable(cam)) {
- mutex_unlock(&cam->mutex);
- goto out_omap24xxcam_sensor_enable;
- }
- }
- mutex_unlock(&cam->mutex);
-
- fh->cam = cam;
- mutex_lock(&cam->mutex);
- vidioc_int_g_fmt_cap(cam->sdev, &format);
- mutex_unlock(&cam->mutex);
- /* FIXME: how about fh->pix when there are more users? */
- fh->pix = format.fmt.pix;
-
- file->private_data = fh;
-
- spin_lock_init(&fh->vbq_lock);
-
- videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
- &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer), fh, NULL);
-
- return 0;
-
-out_omap24xxcam_sensor_enable:
- omap24xxcam_poweron_reset(cam);
- module_put(cam->sdev->module);
-
-out_try_module_get:
- kfree(fh);
-
- return -ENODEV;
-}
-
-static int omap24xxcam_release(struct file *file)
-{
- struct omap24xxcam_fh *fh = file->private_data;
- struct omap24xxcam_device *cam = fh->cam;
-
- atomic_inc(&cam->reset_disable);
-
- flush_work_sync(&cam->sensor_reset_work);
-
- /* stop streaming capture */
- videobuf_streamoff(&fh->vbq);
-
- mutex_lock(&cam->mutex);
- if (cam->streaming == file) {
- cam->streaming = NULL;
- mutex_unlock(&cam->mutex);
- sysfs_notify(&cam->dev->kobj, NULL, "streaming");
- } else {
- mutex_unlock(&cam->mutex);
- }
-
- atomic_dec(&cam->reset_disable);
-
- omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
-
- /*
- * Make sure the reset work we might have scheduled is not
- * pending! It may be run *only* if we have users. (And it may
- * not be scheduled anymore since streaming is already
- * disabled.)
- */
- flush_work_sync(&cam->sensor_reset_work);
-
- mutex_lock(&cam->mutex);
- if (atomic_dec_return(&cam->users) == 0) {
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_poweron_reset(cam);
- }
- mutex_unlock(&cam->mutex);
-
- file->private_data = NULL;
-
- module_put(cam->sdev->module);
- kfree(fh);
-
- return 0;
-}
-
-static struct v4l2_file_operations omap24xxcam_fops = {
- .ioctl = video_ioctl2,
- .poll = omap24xxcam_poll,
- .mmap = omap24xxcam_mmap,
- .open = omap24xxcam_open,
- .release = omap24xxcam_release,
-};
-
-/*
- *
- * Power management.
- *
- */
-
-#ifdef CONFIG_PM
-static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (atomic_read(&cam->users) == 0)
- return 0;
-
- if (!atomic_read(&cam->reset_disable))
- omap24xxcam_capture_stop(cam);
-
- omap24xxcam_sensor_disable(cam);
- omap24xxcam_poweron_reset(cam);
-
- return 0;
-}
-
-static int omap24xxcam_resume(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (atomic_read(&cam->users) == 0)
- return 0;
-
- omap24xxcam_hwinit(cam);
- omap24xxcam_sensor_enable(cam);
-
- if (!atomic_read(&cam->reset_disable))
- omap24xxcam_capture_cont(cam);
-
- return 0;
-}
-#endif /* CONFIG_PM */
-
-static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_s_parm = vidioc_s_parm,
-};
-
-/*
- *
- * Camera device (i.e. /dev/video).
- *
- */
-
-static int omap24xxcam_device_register(struct v4l2_int_device *s)
-{
- struct omap24xxcam_device *cam = s->u.slave->master->priv;
- struct video_device *vfd;
- int rval;
-
- /* We already have a slave. */
- if (cam->sdev)
- return -EBUSY;
-
- cam->sdev = s;
-
- if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
- dev_err(cam->dev, "could not register sysfs entry\n");
- rval = -EBUSY;
- goto err;
- }
-
- /* initialize the video_device struct */
- vfd = cam->vfd = video_device_alloc();
- if (!vfd) {
- dev_err(cam->dev, "could not allocate video device struct\n");
- rval = -ENOMEM;
- goto err;
- }
- vfd->release = video_device_release;
-
- vfd->parent = cam->dev;
-
- strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
- vfd->fops = &omap24xxcam_fops;
- vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
-
- omap24xxcam_hwinit(cam);
-
- rval = omap24xxcam_sensor_init(cam);
- if (rval)
- goto err;
-
- if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
- dev_err(cam->dev, "could not register V4L device\n");
- rval = -EBUSY;
- goto err;
- }
-
- omap24xxcam_poweron_reset(cam);
-
- dev_info(cam->dev, "registered device %s\n",
- video_device_node_name(vfd));
-
- return 0;
-
-err:
- omap24xxcam_device_unregister(s);
-
- return rval;
-}
-
-static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
-{
- struct omap24xxcam_device *cam = s->u.slave->master->priv;
-
- omap24xxcam_sensor_exit(cam);
-
- if (cam->vfd) {
- if (!video_is_registered(cam->vfd)) {
- /*
- * The device was never registered, so release the
- * video_device struct directly.
- */
- video_device_release(cam->vfd);
- } else {
- /*
- * The unregister function will release the
- * video_device struct as well as
- * unregistering it.
- */
- video_unregister_device(cam->vfd);
- }
- cam->vfd = NULL;
- }
-
- device_remove_file(cam->dev, &dev_attr_streaming);
-
- cam->sdev = NULL;
-}
-
-static struct v4l2_int_master omap24xxcam_master = {
- .attach = omap24xxcam_device_register,
- .detach = omap24xxcam_device_unregister,
-};
-
-static struct v4l2_int_device omap24xxcam = {
- .module = THIS_MODULE,
- .name = CAM_NAME,
- .type = v4l2_int_type_master,
- .u = {
- .master = &omap24xxcam_master
- },
-};
-
-/*
- *
- * Driver initialisation and deinitialisation.
- *
- */
-
-static int __devinit omap24xxcam_probe(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam;
- struct resource *mem;
- int irq;
-
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
- if (!cam) {
- dev_err(&pdev->dev, "could not allocate memory\n");
- goto err;
- }
-
- platform_set_drvdata(pdev, cam);
-
- cam->dev = &pdev->dev;
-
- /*
- * Impose a lower limit on the amount of memory allocated for
- * capture. We require at least enough memory to double-buffer
- * QVGA (300KB).
- */
- if (capture_mem < 320 * 240 * 2 * 2)
- capture_mem = 320 * 240 * 2 * 2;
- cam->capture_mem = capture_mem;
-
- /* request the mem region for the camera registers */
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(cam->dev, "no mem resource?\n");
- goto err;
- }
- if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
- dev_err(cam->dev,
- "cannot reserve camera register I/O region\n");
- goto err;
- }
- cam->mmio_base_phys = mem->start;
- cam->mmio_size = resource_size(mem);
-
- /* map the region */
- cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
- if (!cam->mmio_base) {
- dev_err(cam->dev, "cannot map camera register I/O region\n");
- goto err;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(cam->dev, "no irq for camera?\n");
- goto err;
- }
-
- /* install the interrupt service routine */
- if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
- dev_err(cam->dev,
- "could not install interrupt service routine\n");
- goto err;
- }
- cam->irq = irq;
-
- if (omap24xxcam_clock_get(cam))
- goto err;
-
- INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
-
- mutex_init(&cam->mutex);
- spin_lock_init(&cam->core_enable_disable_lock);
-
- omap24xxcam_sgdma_init(&cam->sgdma,
- cam->mmio_base + CAMDMA_REG_OFFSET,
- omap24xxcam_stalled_dma_reset,
- (unsigned long)cam);
-
- omap24xxcam.priv = cam;
-
- if (v4l2_int_device_register(&omap24xxcam))
- goto err;
-
- return 0;
-
-err:
- omap24xxcam_remove(pdev);
- return -ENODEV;
-}
-
-static int omap24xxcam_remove(struct platform_device *pdev)
-{
- struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
-
- if (!cam)
- return 0;
-
- if (omap24xxcam.priv != NULL)
- v4l2_int_device_unregister(&omap24xxcam);
- omap24xxcam.priv = NULL;
-
- omap24xxcam_clock_put(cam);
-
- if (cam->irq) {
- free_irq(cam->irq, cam);
- cam->irq = 0;
- }
-
- if (cam->mmio_base) {
- iounmap((void *)cam->mmio_base);
- cam->mmio_base = 0;
- }
-
- if (cam->mmio_base_phys) {
- release_mem_region(cam->mmio_base_phys, cam->mmio_size);
- cam->mmio_base_phys = 0;
- }
-
- kfree(cam);
-
- return 0;
-}
-
-static struct platform_driver omap24xxcam_driver = {
- .probe = omap24xxcam_probe,
- .remove = omap24xxcam_remove,
-#ifdef CONFIG_PM
- .suspend = omap24xxcam_suspend,
- .resume = omap24xxcam_resume,
-#endif
- .driver = {
- .name = CAM_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(omap24xxcam_driver);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
-MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(OMAP24XXCAM_VERSION);
-module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr,
- "Minor number for video device (-1 ==> auto assign)");
-module_param(capture_mem, int, 0);
-MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
- "buffers (default 4800kiB)");
diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h
deleted file mode 100644
index d59727afe894..000000000000
--- a/drivers/media/video/omap24xxcam.h
+++ /dev/null
@@ -1,593 +0,0 @@
-/*
- * drivers/media/video/omap24xxcam.h
- *
- * Copyright (C) 2004 MontaVista Software, Inc.
- * Copyright (C) 2004 Texas Instruments.
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from Andy Lowe <source@mvista.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP24XXCAM_H
-#define OMAP24XXCAM_H
-
-#include <media/videobuf-dma-sg.h>
-#include <media/v4l2-int-device.h>
-
-/*
- *
- * General driver related definitions.
- *
- */
-
-#define CAM_NAME "omap24xxcam"
-
-#define CAM_MCLK 96000000
-
-/* number of bytes transferred per DMA request */
-#define DMA_THRESHOLD 32
-
-/*
- * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
- * the camera DMA controller.
- */
-#define NUM_CAMDMA_CHANNELS 4
-
-/*
- * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
- * be queued. (We don't have any overlay sglists now.)
- */
-#define NUM_SG_DMA (VIDEO_MAX_FRAME)
-
-/*
- *
- * Register definitions.
- *
- */
-
-/* subsystem register block offsets */
-#define CC_REG_OFFSET 0x00000400
-#define CAMDMA_REG_OFFSET 0x00000800
-#define CAMMMU_REG_OFFSET 0x00000C00
-
-/* define camera subsystem register offsets */
-#define CAM_REVISION 0x000
-#define CAM_SYSCONFIG 0x010
-#define CAM_SYSSTATUS 0x014
-#define CAM_IRQSTATUS 0x018
-#define CAM_GPO 0x040
-#define CAM_GPI 0x050
-
-/* define camera core register offsets */
-#define CC_REVISION 0x000
-#define CC_SYSCONFIG 0x010
-#define CC_SYSSTATUS 0x014
-#define CC_IRQSTATUS 0x018
-#define CC_IRQENABLE 0x01C
-#define CC_CTRL 0x040
-#define CC_CTRL_DMA 0x044
-#define CC_CTRL_XCLK 0x048
-#define CC_FIFODATA 0x04C
-#define CC_TEST 0x050
-#define CC_GENPAR 0x054
-#define CC_CCPFSCR 0x058
-#define CC_CCPFECR 0x05C
-#define CC_CCPLSCR 0x060
-#define CC_CCPLECR 0x064
-#define CC_CCPDFR 0x068
-
-/* define camera dma register offsets */
-#define CAMDMA_REVISION 0x000
-#define CAMDMA_IRQSTATUS_L0 0x008
-#define CAMDMA_IRQSTATUS_L1 0x00C
-#define CAMDMA_IRQSTATUS_L2 0x010
-#define CAMDMA_IRQSTATUS_L3 0x014
-#define CAMDMA_IRQENABLE_L0 0x018
-#define CAMDMA_IRQENABLE_L1 0x01C
-#define CAMDMA_IRQENABLE_L2 0x020
-#define CAMDMA_IRQENABLE_L3 0x024
-#define CAMDMA_SYSSTATUS 0x028
-#define CAMDMA_OCP_SYSCONFIG 0x02C
-#define CAMDMA_CAPS_0 0x064
-#define CAMDMA_CAPS_2 0x06C
-#define CAMDMA_CAPS_3 0x070
-#define CAMDMA_CAPS_4 0x074
-#define CAMDMA_GCR 0x078
-#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
-#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
-#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
-#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
-#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
-#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
-#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
-#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
-#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
-#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
-#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
-#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
-#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
-#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
-#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
-#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
-#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
-#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
-
-/* define camera mmu register offsets */
-#define CAMMMU_REVISION 0x000
-#define CAMMMU_SYSCONFIG 0x010
-#define CAMMMU_SYSSTATUS 0x014
-#define CAMMMU_IRQSTATUS 0x018
-#define CAMMMU_IRQENABLE 0x01C
-#define CAMMMU_WALKING_ST 0x040
-#define CAMMMU_CNTL 0x044
-#define CAMMMU_FAULT_AD 0x048
-#define CAMMMU_TTB 0x04C
-#define CAMMMU_LOCK 0x050
-#define CAMMMU_LD_TLB 0x054
-#define CAMMMU_CAM 0x058
-#define CAMMMU_RAM 0x05C
-#define CAMMMU_GFLUSH 0x060
-#define CAMMMU_FLUSH_ENTRY 0x064
-#define CAMMMU_READ_CAM 0x068
-#define CAMMMU_READ_RAM 0x06C
-#define CAMMMU_EMU_FAULT_AD 0x070
-
-/* Define bit fields within selected registers */
-#define CAM_REVISION_MAJOR (15 << 4)
-#define CAM_REVISION_MAJOR_SHIFT 4
-#define CAM_REVISION_MINOR (15 << 0)
-#define CAM_REVISION_MINOR_SHIFT 0
-
-#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
-#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CAM_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
-#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
-#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
-#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
-#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
-
-#define CAM_GPO_CAM_S_P_EN (1 << 1)
-#define CAM_GPO_CAM_CCP_MODE (1 << 0)
-
-#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
-#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
-#define CAP_GPI_CAM_MSTANDBY (1 << 21)
-#define CAP_GPI_CAM_WAIT (1 << 20)
-#define CAP_GPI_CAM_S_DATA (1 << 17)
-#define CAP_GPI_CAM_S_CLK (1 << 16)
-#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
-#define CAP_GPI_CAM_P_DATA_SHIFT 3
-#define CAP_GPI_CAM_P_VS (1 << 2)
-#define CAP_GPI_CAM_P_HS (1 << 1)
-#define CAP_GPI_CAM_P_CLK (1 << 0)
-
-#define CC_REVISION_MAJOR (15 << 4)
-#define CC_REVISION_MAJOR_SHIFT 4
-#define CC_REVISION_MINOR (15 << 0)
-#define CC_REVISION_MINOR_SHIFT 0
-
-#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
-#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
-#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
-#define CC_SYSCONFIG_SOFTRESET (1 << 1)
-#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CC_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CC_IRQSTATUS_FS_IRQ (1 << 19)
-#define CC_IRQSTATUS_LE_IRQ (1 << 18)
-#define CC_IRQSTATUS_LS_IRQ (1 << 17)
-#define CC_IRQSTATUS_FE_IRQ (1 << 16)
-#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
-#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
-#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
-#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
-#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
-#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
-#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
-#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
-
-#define CC_IRQENABLE_FS_IRQ (1 << 19)
-#define CC_IRQENABLE_LE_IRQ (1 << 18)
-#define CC_IRQENABLE_LS_IRQ (1 << 17)
-#define CC_IRQENABLE_FE_IRQ (1 << 16)
-#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
-#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
-#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
-#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
-#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
-#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
-#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
-#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
-
-#define CC_CTRL_CC_ONE_SHOT (1 << 20)
-#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
-#define CC_CTRL_CC_RST (1 << 18)
-#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
-#define CC_CTRL_CC_EN (1 << 16)
-#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
-#define CC_CTRL_BT_CORRECT (1 << 12)
-#define CC_CTRL_PAR_ORDERCAM (1 << 11)
-#define CC_CTRL_PAR_CLK_POL (1 << 10)
-#define CC_CTRL_NOBT_HS_POL (1 << 9)
-#define CC_CTRL_NOBT_VS_POL (1 << 8)
-#define CC_CTRL_PAR_MODE (7 << 1)
-#define CC_CTRL_PAR_MODE_SHIFT 1
-#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
-#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
-#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
-#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
-#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
-#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
-#define CC_CTRL_CCP_MODE (1 << 0)
-
-#define CC_CTRL_DMA_EN (1 << 8)
-#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
-#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
-
-#define CC_CTRL_XCLK_DIV (0x1F << 0)
-#define CC_CTRL_XCLK_DIV_SHIFT 0
-#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
-#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
-#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
-
-#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
-#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
-#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
-#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
-#define CC_TEST_FIFO_LEVEL (0xFF << 8)
-#define CC_TEST_FIFO_LEVEL_SHIFT 8
-#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
-#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
-
-#define CC_GENPAR_FIFO_DEPTH (7 << 0)
-#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
-
-#define CC_CCPDFR_ALPHA (0xFF << 8)
-#define CC_CCPDFR_ALPHA_SHIFT 8
-#define CC_CCPDFR_DATAFORMAT (15 << 0)
-#define CC_CCPDFR_DATAFORMAT_SHIFT 0
-#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
-#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
-#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
-#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
-#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
-#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
-
-#define CAMDMA_REVISION_MAJOR (15 << 4)
-#define CAMDMA_REVISION_MAJOR_SHIFT 4
-#define CAMDMA_REVISION_MINOR (15 << 0)
-#define CAMDMA_REVISION_MINOR_SHIFT 0
-
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
-#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
-#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
-#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
-#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
-#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
-#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
-
-#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
-
-#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
-#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
-#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
-#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
-
-#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
-#define CAMDMA_CCR_PREFETCH (1 << 23)
-#define CAMDMA_CCR_SUPERVISOR (1 << 22)
-#define CAMDMA_CCR_SECURE (1 << 21)
-#define CAMDMA_CCR_BS (1 << 18)
-#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
-#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
-#define CAMDMA_CCR_DST_AMODE (3 << 14)
-#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
-#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
-#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
-#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
-#define CAMDMA_CCR_SRC_AMODE (3 << 12)
-#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
-#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
-#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
-#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
-#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
-#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
-#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
-#define CAMDMA_CCR_ENABLE (1 << 7)
-#define CAMDMA_CCR_PRIO (1 << 6)
-#define CAMDMA_CCR_FS (1 << 5)
-#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
-#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
-
-#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
-#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
-#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
-
-#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
-#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
-#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
-#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
-#define CAMDMA_CICR_PACKET_IE (1 << 7)
-#define CAMDMA_CICR_BLOCK_IE (1 << 5)
-#define CAMDMA_CICR_LAST_IE (1 << 4)
-#define CAMDMA_CICR_FRAME_IE (1 << 3)
-#define CAMDMA_CICR_HALF_IE (1 << 2)
-#define CAMDMA_CICR_DROP_IE (1 << 1)
-
-#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
-#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
-#define CAMDMA_CSR_SECURE_ERR (1 << 9)
-#define CAMDMA_CSR_TRANS_ERR (1 << 8)
-#define CAMDMA_CSR_PACKET (1 << 7)
-#define CAMDMA_CSR_SYNC (1 << 6)
-#define CAMDMA_CSR_BLOCK (1 << 5)
-#define CAMDMA_CSR_LAST (1 << 4)
-#define CAMDMA_CSR_FRAME (1 << 3)
-#define CAMDMA_CSR_HALF (1 << 2)
-#define CAMDMA_CSR_DROP (1 << 1)
-
-#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
-#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
-#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
-#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
-#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
-#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
-#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
-#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
-#define CAMDMA_CSDP_DST_PACKED (1 << 13)
-#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
-#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
-#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
-#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
-#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
-#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
-#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
-#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
-#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
-
-#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
-
-/*
- *
- * Declarations.
- *
- */
-
-/* forward declarations */
-struct omap24xxcam_sgdma;
-struct omap24xxcam_dma;
-
-typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
- u32 status, void *arg);
-typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
- u32 status, void *arg);
-
-struct channel_state {
- dma_callback_t callback;
- void *arg;
-};
-
-/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
-struct sgdma_state {
- const struct scatterlist *sglist;
- int sglen; /* number of sglist entries */
- int next_sglist; /* index of next sglist entry to process */
- unsigned int bytes_read; /* number of bytes read */
- unsigned int len; /* total length of sglist (excluding
- * bytes due to page alignment) */
- int queued_sglist; /* number of sglist entries queued for DMA */
- u32 csr; /* DMA return code */
- sgdma_callback_t callback;
- void *arg;
-};
-
-/* physical DMA channel management */
-struct omap24xxcam_dma {
- spinlock_t lock; /* Lock for the whole structure. */
-
- void __iomem *base; /* base address for dma controller */
-
- /* While dma_stop!=0, an attempt to start a new DMA transfer will
- * fail.
- */
- atomic_t dma_stop;
- int free_dmach; /* number of dma channels free */
- int next_dmach; /* index of next dma channel to use */
- struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
-};
-
-/* scatter-gather DMA (scatterlist stuff) management */
-struct omap24xxcam_sgdma {
- struct omap24xxcam_dma dma;
-
- spinlock_t lock; /* Lock for the fields below. */
- int free_sgdma; /* number of free sg dma slots */
- int next_sgdma; /* index of next sg dma slot to use */
- struct sgdma_state sg_state[NUM_SG_DMA];
-
- /* Reset timer data */
- struct timer_list reset_timer;
-};
-
-/* per-device data structure */
-struct omap24xxcam_device {
- /*** mutex ***/
- /*
- * mutex serialises access to this structure. Also camera
- * opening and releasing is synchronised by this.
- */
- struct mutex mutex;
-
- /*** general driver state information ***/
- atomic_t users;
- /*
- * Lock to serialise core enabling and disabling and access to
- * sgdma_in_queue.
- */
- spinlock_t core_enable_disable_lock;
- /*
- * Number or sgdma requests in scatter-gather queue, protected
- * by the lock above.
- */
- int sgdma_in_queue;
- /*
- * Sensor interface parameters: interface type, CC_CTRL
- * register value and interface specific data.
- */
- int if_type;
- union {
- struct parallel {
- u32 xclk;
- } bt656;
- } if_u;
- u32 cc_ctrl;
-
- /*** subsystem structures ***/
- struct omap24xxcam_sgdma sgdma;
-
- /*** hardware resources ***/
- unsigned int irq;
- void __iomem *mmio_base;
- unsigned long mmio_base_phys;
- unsigned long mmio_size;
-
- /*** interfaces and device ***/
- struct v4l2_int_device *sdev;
- struct device *dev;
- struct video_device *vfd;
-
- /*** camera and sensor reset related stuff ***/
- struct work_struct sensor_reset_work;
- /*
- * We're in the middle of a reset. Don't enable core if this
- * is non-zero! This exists to help decisionmaking in a case
- * where videobuf_qbuf is called while we are in the middle of
- * a reset.
- */
- atomic_t in_reset;
- /*
- * Non-zero if we don't want any resets for now. Used to
- * prevent reset work to run when we're about to stop
- * streaming.
- */
- atomic_t reset_disable;
-
- /*** video device parameters ***/
- int capture_mem;
-
- /*** camera module clocks ***/
- struct clk *fck;
- struct clk *ick;
-
- /*** capture data ***/
- /* file handle, if streaming is on */
- struct file *streaming;
-};
-
-/* Per-file handle data. */
-struct omap24xxcam_fh {
- spinlock_t vbq_lock; /* spinlock for the videobuf queue */
- struct videobuf_queue vbq;
- struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
- atomic_t field_count; /* field counter for videobuf_buffer */
- /* accessing cam here doesn't need serialisation: it's constant */
- struct omap24xxcam_device *cam;
-};
-
-/*
- *
- * Register I/O functions.
- *
- */
-
-static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset)
-{
- return readl(base + offset);
-}
-
-static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset,
- u32 val)
-{
- writel(val, base + offset);
- return val;
-}
-
-static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset,
- u32 val, u32 mask)
-{
- u32 __iomem *addr = base + offset;
- u32 new_val = (readl(addr) & ~mask) | (val & mask);
-
- writel(new_val, addr);
- return new_val;
-}
-
-/*
- *
- * Function prototypes.
- *
- */
-
-/* dma prototypes */
-
-void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
-void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
-
-/* sgdma prototypes */
-
-void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
-int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
- const struct scatterlist *sglist, int sglen,
- int len, sgdma_callback_t callback, void *arg);
-void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
-void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
- void __iomem *base,
- void (*reset_callback)(unsigned long data),
- unsigned long reset_callback_data);
-void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
-
-#endif
diff --git a/drivers/media/video/omap3isp/cfa_coef_table.h b/drivers/media/video/omap3isp/cfa_coef_table.h
deleted file mode 100644
index c60df0ed075a..000000000000
--- a/drivers/media/video/omap3isp/cfa_coef_table.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * cfa_coef_table.h
- *
- * TI OMAP3 ISP - CFA coefficients table
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
-247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
-244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
-247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
-244, 0, 247, 0, 12, 27, 36, 247, 250, 0, 27, 0, 4, 250, 12, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 12, 250, 4, 0, 27, 0, 250,
-247, 36, 27, 12, 0, 247, 0, 244, 0, 0, 40, 0, 0, 0, 0, 248,
- 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
- 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
- 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
- 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
- 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
- 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
- 0, 247, 0, 244, 247, 36, 27, 12, 0, 27, 0, 250, 244, 12, 250, 4,
- 0, 0, 0, 248, 0, 0, 40, 0, 4, 250, 12, 244, 250, 0, 27, 0,
- 12, 27, 36, 247, 244, 0, 247, 0, 0, 40, 0, 0, 248, 0, 0, 0,
- 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
- 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
- 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
- 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
- 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
- 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
- 4, 250, 12, 244, 250, 0, 27, 0, 12, 27, 36, 247, 244, 0, 247, 0,
- 0, 0, 0, 248, 0, 0, 40, 0, 0, 247, 0, 244, 247, 36, 27, 12,
- 0, 27, 0, 250, 244, 12, 250, 4, 0, 40, 0, 0, 248, 0, 0, 0,
-244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
-250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
-244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
-250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248,
-244, 12, 250, 4, 0, 27, 0, 250, 247, 36, 27, 12, 0, 247, 0, 244,
-248, 0, 0, 0, 0, 40, 0, 0, 244, 0, 247, 0, 12, 27, 36, 247,
-250, 0, 27, 0, 4, 250, 12, 244, 0, 0, 40, 0, 0, 0, 0, 248
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
deleted file mode 100644
index 1c347633e663..000000000000
--- a/drivers/media/video/omap3isp/isp.c
+++ /dev/null
@@ -1,2230 +0,0 @@
-/*
- * isp.c
- *
- * TI OMAP3 ISP - Core
- *
- * Copyright (C) 2006-2010 Nokia Corporation
- * Copyright (C) 2007-2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * Contributors:
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- * David Cohen <dacohen@gmail.com>
- * Stanimir Varbanov <svarbanov@mm-sol.com>
- * Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
- * Tuukka Toivonen <tuukkat76@gmail.com>
- * Sergio Aguirre <saaguirre@ti.com>
- * Antti Koskipaa <akoskipa@gmail.com>
- * Ivan T. Ivanov <iivanov@mm-sol.com>
- * RaniSuneela <r-m@ti.com>
- * Atanas Filipov <afilipov@mm-sol.com>
- * Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
- * Hiroshi DOYU <hiroshi.doyu@nokia.com>
- * Nayden Kanchev <nkanchev@mm-sol.com>
- * Phil Carmody <ext-phil.2.carmody@nokia.com>
- * Artem Bityutskiy <artem.bityutskiy@nokia.com>
- * Dominic Curran <dcurran@ti.com>
- * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
- * Pallavi Kulkarni <p-kulkarni@ti.com>
- * Vaibhav Hiremath <hvaibhav@ti.com>
- * Mohit Jalori <mjalori@ti.com>
- * Sameer Venkatraman <sameerv@ti.com>
- * Senthilvadivu Guruswamy <svadivu@ti.com>
- * Thara Gopinath <thara@ti.com>
- * Toni Leinonen <toni.leinonen@nokia.com>
- * Troy Laramy <t-laramy@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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/vmalloc.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "ispccdc.h"
-#include "isppreview.h"
-#include "ispresizer.h"
-#include "ispcsi2.h"
-#include "ispccp2.h"
-#include "isph3a.h"
-#include "isphist.h"
-
-static unsigned int autoidle;
-module_param(autoidle, int, 0444);
-MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
-
-static void isp_save_ctx(struct isp_device *isp);
-
-static void isp_restore_ctx(struct isp_device *isp);
-
-static const struct isp_res_mapping isp_res_maps[] = {
- {
- .isp_rev = ISP_REVISION_2_0,
- .map = 1 << OMAP3_ISP_IOMEM_MAIN |
- 1 << OMAP3_ISP_IOMEM_CCP2 |
- 1 << OMAP3_ISP_IOMEM_CCDC |
- 1 << OMAP3_ISP_IOMEM_HIST |
- 1 << OMAP3_ISP_IOMEM_H3A |
- 1 << OMAP3_ISP_IOMEM_PREV |
- 1 << OMAP3_ISP_IOMEM_RESZ |
- 1 << OMAP3_ISP_IOMEM_SBL |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY2,
- },
- {
- .isp_rev = ISP_REVISION_15_0,
- .map = 1 << OMAP3_ISP_IOMEM_MAIN |
- 1 << OMAP3_ISP_IOMEM_CCP2 |
- 1 << OMAP3_ISP_IOMEM_CCDC |
- 1 << OMAP3_ISP_IOMEM_HIST |
- 1 << OMAP3_ISP_IOMEM_H3A |
- 1 << OMAP3_ISP_IOMEM_PREV |
- 1 << OMAP3_ISP_IOMEM_RESZ |
- 1 << OMAP3_ISP_IOMEM_SBL |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY2 |
- 1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
- 1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
- 1 << OMAP3_ISP_IOMEM_CSIPHY1 |
- 1 << OMAP3_ISP_IOMEM_CSI2C_REGS2,
- },
-};
-
-/* Structure for saving/restoring ISP module registers */
-static struct isp_reg isp_reg_list[] = {
- {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
- {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
- {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
- {0, ISP_TOK_TERM, 0}
-};
-
-/*
- * omap3isp_flush - Post pending L3 bus writes by doing a register readback
- * @isp: OMAP3 ISP device
- *
- * In order to force posting of pending writes, we need to write and
- * readback the same register, in this case the revision register.
- *
- * See this link for reference:
- * http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
- */
-void omap3isp_flush(struct isp_device *isp)
-{
- isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
-}
-
-/*
- * isp_enable_interrupts - Enable ISP interrupts.
- * @isp: OMAP3 ISP device
- */
-static void isp_enable_interrupts(struct isp_device *isp)
-{
- static const u32 irq = IRQ0ENABLE_CSIA_IRQ
- | IRQ0ENABLE_CSIB_IRQ
- | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ
- | IRQ0ENABLE_CCDC_LSC_DONE_IRQ
- | IRQ0ENABLE_CCDC_VD0_IRQ
- | IRQ0ENABLE_CCDC_VD1_IRQ
- | IRQ0ENABLE_HS_VS_IRQ
- | IRQ0ENABLE_HIST_DONE_IRQ
- | IRQ0ENABLE_H3A_AWB_DONE_IRQ
- | IRQ0ENABLE_H3A_AF_DONE_IRQ
- | IRQ0ENABLE_PRV_DONE_IRQ
- | IRQ0ENABLE_RSZ_DONE_IRQ;
-
- isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
- isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
-}
-
-/*
- * isp_disable_interrupts - Disable ISP interrupts.
- * @isp: OMAP3 ISP device
- */
-static void isp_disable_interrupts(struct isp_device *isp)
-{
- isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE);
-}
-
-/**
- * isp_set_xclk - Configures the specified cam_xclk to the desired frequency.
- * @isp: OMAP3 ISP device
- * @xclk: Desired frequency of the clock in Hz. 0 = stable low, 1 is stable high
- * @xclksel: XCLK to configure (0 = A, 1 = B).
- *
- * Configures the specified MCLK divisor in the ISP timing control register
- * (TCTRL_CTRL) to generate the desired xclk clock value.
- *
- * Divisor = cam_mclk_hz / xclk
- *
- * Returns the final frequency that is actually being generated
- **/
-static u32 isp_set_xclk(struct isp_device *isp, u32 xclk, u8 xclksel)
-{
- u32 divisor;
- u32 currentxclk;
- unsigned long mclk_hz;
-
- if (!omap3isp_get(isp))
- return 0;
-
- mclk_hz = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
-
- if (xclk >= mclk_hz) {
- divisor = ISPTCTRL_CTRL_DIV_BYPASS;
- currentxclk = mclk_hz;
- } else if (xclk >= 2) {
- divisor = mclk_hz / xclk;
- if (divisor >= ISPTCTRL_CTRL_DIV_BYPASS)
- divisor = ISPTCTRL_CTRL_DIV_BYPASS - 1;
- currentxclk = mclk_hz / divisor;
- } else {
- divisor = xclk;
- currentxclk = 0;
- }
-
- switch (xclksel) {
- case ISP_XCLK_A:
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
- ISPTCTRL_CTRL_DIVA_MASK,
- divisor << ISPTCTRL_CTRL_DIVA_SHIFT);
- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclka set to %d Hz\n",
- currentxclk);
- break;
- case ISP_XCLK_B:
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
- ISPTCTRL_CTRL_DIVB_MASK,
- divisor << ISPTCTRL_CTRL_DIVB_SHIFT);
- dev_dbg(isp->dev, "isp_set_xclk(): cam_xclkb set to %d Hz\n",
- currentxclk);
- break;
- case ISP_XCLK_NONE:
- default:
- omap3isp_put(isp);
- dev_dbg(isp->dev, "ISP_ERR: isp_set_xclk(): Invalid requested "
- "xclk. Must be 0 (A) or 1 (B).\n");
- return -EINVAL;
- }
-
- /* Do we go from stable whatever to clock? */
- if (divisor >= 2 && isp->xclk_divisor[xclksel - 1] < 2)
- omap3isp_get(isp);
- /* Stopping the clock. */
- else if (divisor < 2 && isp->xclk_divisor[xclksel - 1] >= 2)
- omap3isp_put(isp);
-
- isp->xclk_divisor[xclksel - 1] = divisor;
-
- omap3isp_put(isp);
-
- return currentxclk;
-}
-
-/*
- * isp_power_settings - Sysconfig settings, for Power Management.
- * @isp: OMAP3 ISP device
- * @idle: Consider idle state.
- *
- * Sets the power settings for the ISP, and SBL bus.
- */
-static void isp_power_settings(struct isp_device *isp, int idle)
-{
- isp_reg_writel(isp,
- ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY :
- ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) <<
- ISP_SYSCONFIG_MIDLEMODE_SHIFT) |
- ((isp->revision == ISP_REVISION_15_0) ?
- ISP_SYSCONFIG_AUTOIDLE : 0),
- OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
-
- if (isp->autoidle)
- isp_reg_writel(isp, ISPCTRL_SBL_AUTOIDLE, OMAP3_ISP_IOMEM_MAIN,
- ISP_CTRL);
-}
-
-/*
- * Configure the bridge and lane shifter. Valid inputs are
- *
- * CCDC_INPUT_PARALLEL: Parallel interface
- * CCDC_INPUT_CSI2A: CSI2a receiver
- * CCDC_INPUT_CCP2B: CCP2b receiver
- * CCDC_INPUT_CSI2C: CSI2c receiver
- *
- * The bridge and lane shifter are configured according to the selected input
- * and the ISP platform data.
- */
-void omap3isp_configure_bridge(struct isp_device *isp,
- enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata,
- unsigned int shift)
-{
- u32 ispctrl_val;
-
- ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
- ispctrl_val &= ~ISPCTRL_SHIFT_MASK;
- ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV;
- ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK;
- ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK;
-
- switch (input) {
- case CCDC_INPUT_PARALLEL:
- ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL;
- ispctrl_val |= pdata->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT;
- ispctrl_val |= pdata->bridge << ISPCTRL_PAR_BRIDGE_SHIFT;
- shift += pdata->data_lane_shift * 2;
- break;
-
- case CCDC_INPUT_CSI2A:
- ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA;
- break;
-
- case CCDC_INPUT_CCP2B:
- ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB;
- break;
-
- case CCDC_INPUT_CSI2C:
- ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC;
- break;
-
- default:
- return;
- }
-
- ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK;
-
- ispctrl_val &= ~ISPCTRL_SYNC_DETECT_MASK;
- ispctrl_val |= ISPCTRL_SYNC_DETECT_VSRISE;
-
- isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
-}
-
-void omap3isp_hist_dma_done(struct isp_device *isp)
-{
- if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
- omap3isp_stat_pcr_busy(&isp->isp_hist)) {
- /* Histogram cannot be enabled in this frame anymore */
- atomic_set(&isp->isp_hist.buf_err, 1);
- dev_dbg(isp->dev, "hist: Out of synchronization with "
- "CCDC. Ignoring next buffer.\n");
- }
-}
-
-static inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus)
-{
- static const char *name[] = {
- "CSIA_IRQ",
- "res1",
- "res2",
- "CSIB_LCM_IRQ",
- "CSIB_IRQ",
- "res5",
- "res6",
- "res7",
- "CCDC_VD0_IRQ",
- "CCDC_VD1_IRQ",
- "CCDC_VD2_IRQ",
- "CCDC_ERR_IRQ",
- "H3A_AF_DONE_IRQ",
- "H3A_AWB_DONE_IRQ",
- "res14",
- "res15",
- "HIST_DONE_IRQ",
- "CCDC_LSC_DONE",
- "CCDC_LSC_PREFETCH_COMPLETED",
- "CCDC_LSC_PREFETCH_ERROR",
- "PRV_DONE_IRQ",
- "CBUFF_IRQ",
- "res22",
- "res23",
- "RSZ_DONE_IRQ",
- "OVF_IRQ",
- "res26",
- "res27",
- "MMU_ERR_IRQ",
- "OCP_ERR_IRQ",
- "SEC_ERR_IRQ",
- "HS_VS_IRQ",
- };
- int i;
-
- dev_dbg(isp->dev, "ISP IRQ: ");
-
- for (i = 0; i < ARRAY_SIZE(name); i++) {
- if ((1 << i) & irqstatus)
- printk(KERN_CONT "%s ", name[i]);
- }
- printk(KERN_CONT "\n");
-}
-
-static void isp_isr_sbl(struct isp_device *isp)
-{
- struct device *dev = isp->dev;
- struct isp_pipeline *pipe;
- u32 sbl_pcr;
-
- /*
- * Handle shared buffer logic overflows for video buffers.
- * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored.
- */
- sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
- isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR);
- sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF;
-
- if (sbl_pcr)
- dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr);
-
- if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) {
- pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity);
- if (pipe != NULL)
- pipe->error = true;
- }
-
- if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) {
- pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity);
- if (pipe != NULL)
- pipe->error = true;
- }
-
- if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) {
- pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity);
- if (pipe != NULL)
- pipe->error = true;
- }
-
- if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) {
- pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity);
- if (pipe != NULL)
- pipe->error = true;
- }
-
- if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF
- | ISPSBL_PCR_RSZ2_WBL_OVF
- | ISPSBL_PCR_RSZ3_WBL_OVF
- | ISPSBL_PCR_RSZ4_WBL_OVF)) {
- pipe = to_isp_pipeline(&isp->isp_res.subdev.entity);
- if (pipe != NULL)
- pipe->error = true;
- }
-
- if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF)
- omap3isp_stat_sbl_overflow(&isp->isp_af);
-
- if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF)
- omap3isp_stat_sbl_overflow(&isp->isp_aewb);
-}
-
-/*
- * isp_isr - Interrupt Service Routine for Camera ISP module.
- * @irq: Not used currently.
- * @_isp: Pointer to the OMAP3 ISP device
- *
- * Handles the corresponding callback if plugged in.
- *
- * Returns IRQ_HANDLED when IRQ was correctly handled, or IRQ_NONE when the
- * IRQ wasn't handled.
- */
-static irqreturn_t isp_isr(int irq, void *_isp)
-{
- static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ |
- IRQ0STATUS_CCDC_LSC_DONE_IRQ |
- IRQ0STATUS_CCDC_VD0_IRQ |
- IRQ0STATUS_CCDC_VD1_IRQ |
- IRQ0STATUS_HS_VS_IRQ;
- struct isp_device *isp = _isp;
- u32 irqstatus;
-
- irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
- isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
-
- isp_isr_sbl(isp);
-
- if (irqstatus & IRQ0STATUS_CSIA_IRQ)
- omap3isp_csi2_isr(&isp->isp_csi2a);
-
- if (irqstatus & IRQ0STATUS_CSIB_IRQ)
- omap3isp_ccp2_isr(&isp->isp_ccp2);
-
- if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) {
- if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW)
- omap3isp_preview_isr_frame_sync(&isp->isp_prev);
- if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER)
- omap3isp_resizer_isr_frame_sync(&isp->isp_res);
- omap3isp_stat_isr_frame_sync(&isp->isp_aewb);
- omap3isp_stat_isr_frame_sync(&isp->isp_af);
- omap3isp_stat_isr_frame_sync(&isp->isp_hist);
- }
-
- if (irqstatus & ccdc_events)
- omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events);
-
- if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) {
- if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER)
- omap3isp_resizer_isr_frame_sync(&isp->isp_res);
- omap3isp_preview_isr(&isp->isp_prev);
- }
-
- if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ)
- omap3isp_resizer_isr(&isp->isp_res);
-
- if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ)
- omap3isp_stat_isr(&isp->isp_aewb);
-
- if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ)
- omap3isp_stat_isr(&isp->isp_af);
-
- if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ)
- omap3isp_stat_isr(&isp->isp_hist);
-
- omap3isp_flush(isp);
-
-#if defined(DEBUG) && defined(ISP_ISR_DEBUG)
- isp_isr_dbg(isp, irqstatus);
-#endif
-
- return IRQ_HANDLED;
-}
-
-/* -----------------------------------------------------------------------------
- * Pipeline power management
- *
- * Entities must be powered up when part of a pipeline that contains at least
- * one open video device node.
- *
- * To achieve this use the entity use_count field to track the number of users.
- * For entities corresponding to video device nodes the use_count field stores
- * the users count of the node. For entities corresponding to subdevs the
- * use_count field stores the total number of users of all video device nodes
- * in the pipeline.
- *
- * The omap3isp_pipeline_pm_use() function must be called in the open() and
- * close() handlers of video device nodes. It increments or decrements the use
- * count of all subdev entities in the pipeline.
- *
- * To react to link management on powered pipelines, the link setup notification
- * callback updates the use count of all entities in the source and sink sides
- * of the link.
- */
-
-/*
- * isp_pipeline_pm_use_count - Count the number of users of a pipeline
- * @entity: The entity
- *
- * Return the total number of users of all video device nodes in the pipeline.
- */
-static int isp_pipeline_pm_use_count(struct media_entity *entity)
-{
- struct media_entity_graph graph;
- int use = 0;
-
- media_entity_graph_walk_start(&graph, entity);
-
- while ((entity = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE)
- use += entity->use_count;
- }
-
- return use;
-}
-
-/*
- * isp_pipeline_pm_power_one - Apply power change to an entity
- * @entity: The entity
- * @change: Use count change
- *
- * Change the entity use count by @change. If the entity is a subdev update its
- * power state by calling the core::s_power operation when the use count goes
- * from 0 to != 0 or from != 0 to 0.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int isp_pipeline_pm_power_one(struct media_entity *entity, int change)
-{
- struct v4l2_subdev *subdev;
- int ret;
-
- subdev = media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV
- ? media_entity_to_v4l2_subdev(entity) : NULL;
-
- if (entity->use_count == 0 && change > 0 && subdev != NULL) {
- ret = v4l2_subdev_call(subdev, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- }
-
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- if (entity->use_count == 0 && change < 0 && subdev != NULL)
- v4l2_subdev_call(subdev, core, s_power, 0);
-
- return 0;
-}
-
-/*
- * isp_pipeline_pm_power - Apply power change to all entities in a pipeline
- * @entity: The entity
- * @change: Use count change
- *
- * Walk the pipeline to update the use count and the power state of all non-node
- * entities.
- *
- * Return 0 on success or a negative error code on failure.
- */
-static int isp_pipeline_pm_power(struct media_entity *entity, int change)
-{
- struct media_entity_graph graph;
- struct media_entity *first = entity;
- int ret = 0;
-
- if (!change)
- return 0;
-
- media_entity_graph_walk_start(&graph, entity);
-
- while (!ret && (entity = media_entity_graph_walk_next(&graph)))
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
- ret = isp_pipeline_pm_power_one(entity, change);
-
- if (!ret)
- return 0;
-
- media_entity_graph_walk_start(&graph, first);
-
- while ((first = media_entity_graph_walk_next(&graph))
- && first != entity)
- if (media_entity_type(first) != MEDIA_ENT_T_DEVNODE)
- isp_pipeline_pm_power_one(first, -change);
-
- return ret;
-}
-
-/*
- * omap3isp_pipeline_pm_use - Update the use count of an entity
- * @entity: The entity
- * @use: Use (1) or stop using (0) the entity
- *
- * Update the use count of all entities in the pipeline and power entities on or
- * off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. No failure can occur when the use parameter is
- * set to 0.
- */
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use)
-{
- int change = use ? 1 : -1;
- int ret;
-
- mutex_lock(&entity->parent->graph_mutex);
-
- /* Apply use count to node. */
- entity->use_count += change;
- WARN_ON(entity->use_count < 0);
-
- /* Apply power change to connected non-nodes. */
- ret = isp_pipeline_pm_power(entity, change);
- if (ret < 0)
- entity->use_count -= change;
-
- mutex_unlock(&entity->parent->graph_mutex);
-
- return ret;
-}
-
-/*
- * isp_pipeline_link_notify - Link management notification callback
- * @source: Pad at the start of the link
- * @sink: Pad at the end of the link
- * @flags: New link flags that will be applied
- *
- * React to link management on powered pipelines by updating the use count of
- * all entities in the source and sink sides of the link. Entities are powered
- * on or off accordingly.
- *
- * Return 0 on success or a negative error code on failure. Powering entities
- * off is assumed to never fail. This function will not fail for disconnection
- * events.
- */
-static int isp_pipeline_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
-{
- int source_use = isp_pipeline_pm_use_count(source->entity);
- int sink_use = isp_pipeline_pm_use_count(sink->entity);
- int ret;
-
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- /* Powering off entities is assumed to never fail. */
- isp_pipeline_pm_power(source->entity, -sink_use);
- isp_pipeline_pm_power(sink->entity, -source_use);
- return 0;
- }
-
- ret = isp_pipeline_pm_power(source->entity, sink_use);
- if (ret < 0)
- return ret;
-
- ret = isp_pipeline_pm_power(sink->entity, source_use);
- if (ret < 0)
- isp_pipeline_pm_power(source->entity, -sink_use);
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Pipeline stream management
- */
-
-/*
- * isp_pipeline_enable - Enable streaming on a pipeline
- * @pipe: ISP pipeline
- * @mode: Stream mode (single shot or continuous)
- *
- * Walk the entities chain starting at the pipeline output video node and start
- * all modules in the chain in the given mode.
- *
- * Return 0 if successful, or the return value of the failed video::s_stream
- * operation otherwise.
- */
-static int isp_pipeline_enable(struct isp_pipeline *pipe,
- enum isp_pipeline_stream_state mode)
-{
- struct isp_device *isp = pipe->output->isp;
- struct media_entity *entity;
- struct media_pad *pad;
- struct v4l2_subdev *subdev;
- unsigned long flags;
- int ret;
-
- /* If the preview engine crashed it might not respond to read/write
- * operations on the L4 bus. This would result in a bus fault and a
- * kernel oops. Refuse to start streaming in that case. This check must
- * be performed before the loop below to avoid starting entities if the
- * pipeline won't start anyway (those entities would then likely fail to
- * stop, making the problem worse).
- */
- if ((pipe->entities & isp->crashed) &
- (1U << isp->isp_prev.subdev.entity.id))
- return -EIO;
-
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
- spin_unlock_irqrestore(&pipe->lock, flags);
-
- pipe->do_propagation = false;
-
- entity = &pipe->output->video.entity;
- while (1) {
- pad = &entity->pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- entity = pad->entity;
- subdev = media_entity_to_v4l2_subdev(entity);
-
- ret = v4l2_subdev_call(subdev, video, s_stream, mode);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- if (subdev == &isp->isp_ccdc.subdev) {
- v4l2_subdev_call(&isp->isp_aewb.subdev, video,
- s_stream, mode);
- v4l2_subdev_call(&isp->isp_af.subdev, video,
- s_stream, mode);
- v4l2_subdev_call(&isp->isp_hist.subdev, video,
- s_stream, mode);
- pipe->do_propagation = true;
- }
- }
-
- return 0;
-}
-
-static int isp_pipeline_wait_resizer(struct isp_device *isp)
-{
- return omap3isp_resizer_busy(&isp->isp_res);
-}
-
-static int isp_pipeline_wait_preview(struct isp_device *isp)
-{
- return omap3isp_preview_busy(&isp->isp_prev);
-}
-
-static int isp_pipeline_wait_ccdc(struct isp_device *isp)
-{
- return omap3isp_stat_busy(&isp->isp_af)
- || omap3isp_stat_busy(&isp->isp_aewb)
- || omap3isp_stat_busy(&isp->isp_hist)
- || omap3isp_ccdc_busy(&isp->isp_ccdc);
-}
-
-#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000)
-
-static int isp_pipeline_wait(struct isp_device *isp,
- int(*busy)(struct isp_device *isp))
-{
- unsigned long timeout = jiffies + ISP_STOP_TIMEOUT;
-
- while (!time_after(jiffies, timeout)) {
- if (!busy(isp))
- return 0;
- }
-
- return 1;
-}
-
-/*
- * isp_pipeline_disable - Disable streaming on a pipeline
- * @pipe: ISP pipeline
- *
- * Walk the entities chain starting at the pipeline output video node and stop
- * all modules in the chain. Wait synchronously for the modules to be stopped if
- * necessary.
- *
- * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module
- * can't be stopped (in which case a software reset of the ISP is probably
- * necessary).
- */
-static int isp_pipeline_disable(struct isp_pipeline *pipe)
-{
- struct isp_device *isp = pipe->output->isp;
- struct media_entity *entity;
- struct media_pad *pad;
- struct v4l2_subdev *subdev;
- int failure = 0;
- int ret;
-
- /*
- * We need to stop all the modules after CCDC first or they'll
- * never stop since they may not get a full frame from CCDC.
- */
- entity = &pipe->output->video.entity;
- while (1) {
- pad = &entity->pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- entity = pad->entity;
- subdev = media_entity_to_v4l2_subdev(entity);
-
- if (subdev == &isp->isp_ccdc.subdev) {
- v4l2_subdev_call(&isp->isp_aewb.subdev,
- video, s_stream, 0);
- v4l2_subdev_call(&isp->isp_af.subdev,
- video, s_stream, 0);
- v4l2_subdev_call(&isp->isp_hist.subdev,
- video, s_stream, 0);
- }
-
- v4l2_subdev_call(subdev, video, s_stream, 0);
-
- if (subdev == &isp->isp_res.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_resizer);
- else if (subdev == &isp->isp_prev.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_preview);
- else if (subdev == &isp->isp_ccdc.subdev)
- ret = isp_pipeline_wait(isp, isp_pipeline_wait_ccdc);
- else
- ret = 0;
-
- if (ret) {
- dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
- /* If the entity failed to stopped, assume it has
- * crashed. Mark it as such, the ISP will be reset when
- * applications will release it.
- */
- isp->crashed |= 1U << subdev->entity.id;
- failure = -ETIMEDOUT;
- }
- }
-
- return failure;
-}
-
-/*
- * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline
- * @pipe: ISP pipeline
- * @state: Stream state (stopped, single shot or continuous)
- *
- * Set the pipeline to the given stream state. Pipelines can be started in
- * single-shot or continuous mode.
- *
- * Return 0 if successful, or the return value of the failed video::s_stream
- * operation otherwise. The pipeline state is not updated when the operation
- * fails, except when stopping the pipeline.
- */
-int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
- enum isp_pipeline_stream_state state)
-{
- int ret;
-
- if (state == ISP_PIPELINE_STREAM_STOPPED)
- ret = isp_pipeline_disable(pipe);
- else
- ret = isp_pipeline_enable(pipe, state);
-
- if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED)
- pipe->stream_state = state;
-
- return ret;
-}
-
-/*
- * isp_pipeline_resume - Resume streaming on a pipeline
- * @pipe: ISP pipeline
- *
- * Resume video output and input and re-enable pipeline.
- */
-static void isp_pipeline_resume(struct isp_pipeline *pipe)
-{
- int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT;
-
- omap3isp_video_resume(pipe->output, !singleshot);
- if (singleshot)
- omap3isp_video_resume(pipe->input, 0);
- isp_pipeline_enable(pipe, pipe->stream_state);
-}
-
-/*
- * isp_pipeline_suspend - Suspend streaming on a pipeline
- * @pipe: ISP pipeline
- *
- * Suspend pipeline.
- */
-static void isp_pipeline_suspend(struct isp_pipeline *pipe)
-{
- isp_pipeline_disable(pipe);
-}
-
-/*
- * isp_pipeline_is_last - Verify if entity has an enabled link to the output
- * video node
- * @me: ISP module's media entity
- *
- * Returns 1 if the entity has an enabled link to the output video node or 0
- * otherwise. It's true only while pipeline can have no more than one output
- * node.
- */
-static int isp_pipeline_is_last(struct media_entity *me)
-{
- struct isp_pipeline *pipe;
- struct media_pad *pad;
-
- if (!me->pipe)
- return 0;
- pipe = to_isp_pipeline(me);
- if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
- pad = media_entity_remote_source(&pipe->output->pad);
- return pad->entity == me;
-}
-
-/*
- * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module
- * @me: ISP module's media entity
- *
- * Suspend the whole pipeline if module's entity has an enabled link to the
- * output video node. It works only while pipeline can have no more than one
- * output node.
- */
-static void isp_suspend_module_pipeline(struct media_entity *me)
-{
- if (isp_pipeline_is_last(me))
- isp_pipeline_suspend(to_isp_pipeline(me));
-}
-
-/*
- * isp_resume_module_pipeline - Resume pipeline to which belongs the module
- * @me: ISP module's media entity
- *
- * Resume the whole pipeline if module's entity has an enabled link to the
- * output video node. It works only while pipeline can have no more than one
- * output node.
- */
-static void isp_resume_module_pipeline(struct media_entity *me)
-{
- if (isp_pipeline_is_last(me))
- isp_pipeline_resume(to_isp_pipeline(me));
-}
-
-/*
- * isp_suspend_modules - Suspend ISP submodules.
- * @isp: OMAP3 ISP device
- *
- * Returns 0 if suspend left in idle state all the submodules properly,
- * or returns 1 if a general Reset is required to suspend the submodules.
- */
-static int isp_suspend_modules(struct isp_device *isp)
-{
- unsigned long timeout;
-
- omap3isp_stat_suspend(&isp->isp_aewb);
- omap3isp_stat_suspend(&isp->isp_af);
- omap3isp_stat_suspend(&isp->isp_hist);
- isp_suspend_module_pipeline(&isp->isp_res.subdev.entity);
- isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity);
- isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity);
- isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity);
- isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity);
-
- timeout = jiffies + ISP_STOP_TIMEOUT;
- while (omap3isp_stat_busy(&isp->isp_af)
- || omap3isp_stat_busy(&isp->isp_aewb)
- || omap3isp_stat_busy(&isp->isp_hist)
- || omap3isp_preview_busy(&isp->isp_prev)
- || omap3isp_resizer_busy(&isp->isp_res)
- || omap3isp_ccdc_busy(&isp->isp_ccdc)) {
- if (time_after(jiffies, timeout)) {
- dev_info(isp->dev, "can't stop modules.\n");
- return 1;
- }
- msleep(1);
- }
-
- return 0;
-}
-
-/*
- * isp_resume_modules - Resume ISP submodules.
- * @isp: OMAP3 ISP device
- */
-static void isp_resume_modules(struct isp_device *isp)
-{
- omap3isp_stat_resume(&isp->isp_aewb);
- omap3isp_stat_resume(&isp->isp_af);
- omap3isp_stat_resume(&isp->isp_hist);
- isp_resume_module_pipeline(&isp->isp_res.subdev.entity);
- isp_resume_module_pipeline(&isp->isp_prev.subdev.entity);
- isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity);
- isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity);
- isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity);
-}
-
-/*
- * isp_reset - Reset ISP with a timeout wait for idle.
- * @isp: OMAP3 ISP device
- */
-static int isp_reset(struct isp_device *isp)
-{
- unsigned long timeout = 0;
-
- isp_reg_writel(isp,
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG)
- | ISP_SYSCONFIG_SOFTRESET,
- OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG);
- while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN,
- ISP_SYSSTATUS) & 0x1)) {
- if (timeout++ > 10000) {
- dev_alert(isp->dev, "cannot reset ISP\n");
- return -ETIMEDOUT;
- }
- udelay(1);
- }
-
- isp->crashed = 0;
- return 0;
-}
-
-/*
- * isp_save_context - Saves the values of the ISP module registers.
- * @isp: OMAP3 ISP device
- * @reg_list: Structure containing pairs of register address and value to
- * modify on OMAP.
- */
-static void
-isp_save_context(struct isp_device *isp, struct isp_reg *reg_list)
-{
- struct isp_reg *next = reg_list;
-
- for (; next->reg != ISP_TOK_TERM; next++)
- next->val = isp_reg_readl(isp, next->mmio_range, next->reg);
-}
-
-/*
- * isp_restore_context - Restores the values of the ISP module registers.
- * @isp: OMAP3 ISP device
- * @reg_list: Structure containing pairs of register address and value to
- * modify on OMAP.
- */
-static void
-isp_restore_context(struct isp_device *isp, struct isp_reg *reg_list)
-{
- struct isp_reg *next = reg_list;
-
- for (; next->reg != ISP_TOK_TERM; next++)
- isp_reg_writel(isp, next->val, next->mmio_range, next->reg);
-}
-
-/*
- * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
- * @isp: OMAP3 ISP device
- *
- * Routine for saving the context of each module in the ISP.
- * CCDC, HIST, H3A, PREV, RESZ and MMU.
- */
-static void isp_save_ctx(struct isp_device *isp)
-{
- isp_save_context(isp, isp_reg_list);
- omap_iommu_save_ctx(isp->dev);
-}
-
-/*
- * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context.
- * @isp: OMAP3 ISP device
- *
- * Routine for restoring the context of each module in the ISP.
- * CCDC, HIST, H3A, PREV, RESZ and MMU.
- */
-static void isp_restore_ctx(struct isp_device *isp)
-{
- isp_restore_context(isp, isp_reg_list);
- omap_iommu_restore_ctx(isp->dev);
- omap3isp_ccdc_restore_context(isp);
- omap3isp_preview_restore_context(isp);
-}
-
-/* -----------------------------------------------------------------------------
- * SBL resources management
- */
-#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \
- OMAP3_ISP_SBL_CCDC_LSC_READ | \
- OMAP3_ISP_SBL_PREVIEW_READ | \
- OMAP3_ISP_SBL_RESIZER_READ)
-#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \
- OMAP3_ISP_SBL_CSI2A_WRITE | \
- OMAP3_ISP_SBL_CSI2C_WRITE | \
- OMAP3_ISP_SBL_CCDC_WRITE | \
- OMAP3_ISP_SBL_PREVIEW_WRITE)
-
-void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res)
-{
- u32 sbl = 0;
-
- isp->sbl_resources |= res;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)
- sbl |= ISPCTRL_SBL_SHARED_RPORTA;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)
- sbl |= ISPCTRL_SBL_SHARED_RPORTB;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)
- sbl |= ISPCTRL_SBL_SHARED_WPORTC;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)
- sbl |= ISPCTRL_SBL_WR0_RAM_EN;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE)
- sbl |= ISPCTRL_SBL_WR1_RAM_EN;
-
- if (isp->sbl_resources & OMAP3_ISP_SBL_READ)
- sbl |= ISPCTRL_SBL_RD_RAM_EN;
-
- isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
-}
-
-void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res)
-{
- u32 sbl = 0;
-
- isp->sbl_resources &= ~res;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ))
- sbl |= ISPCTRL_SBL_SHARED_RPORTA;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ))
- sbl |= ISPCTRL_SBL_SHARED_RPORTB;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE))
- sbl |= ISPCTRL_SBL_SHARED_WPORTC;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE))
- sbl |= ISPCTRL_SBL_WR0_RAM_EN;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE))
- sbl |= ISPCTRL_SBL_WR1_RAM_EN;
-
- if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ))
- sbl |= ISPCTRL_SBL_RD_RAM_EN;
-
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl);
-}
-
-/*
- * isp_module_sync_idle - Helper to sync module with its idle state
- * @me: ISP submodule's media entity
- * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
- * @stopping: flag which tells module wants to stop
- *
- * This function checks if ISP submodule needs to wait for next interrupt. If
- * yes, makes the caller to sleep while waiting for such event.
- */
-int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
- atomic_t *stopping)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(me);
-
- if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED ||
- (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT &&
- !isp_pipeline_ready(pipe)))
- return 0;
-
- /*
- * atomic_set() doesn't include memory barrier on ARM platform for SMP
- * scenario. We'll call it here to avoid race conditions.
- */
- atomic_set(stopping, 1);
- smp_mb();
-
- /*
- * If module is the last one, it's writing to memory. In this case,
- * it's necessary to check if the module is already paused due to
- * DMA queue underrun or if it has to wait for next interrupt to be
- * idle.
- * If it isn't the last one, the function won't sleep but *stopping
- * will still be set to warn next submodule caller's interrupt the
- * module wants to be idle.
- */
- if (isp_pipeline_is_last(me)) {
- struct isp_video *video = pipe->output;
- unsigned long flags;
- spin_lock_irqsave(&video->queue->irqlock, flags);
- if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
- atomic_set(stopping, 0);
- smp_mb();
- return 0;
- }
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
- if (!wait_event_timeout(*wait, !atomic_read(stopping),
- msecs_to_jiffies(1000))) {
- atomic_set(stopping, 0);
- smp_mb();
- return -ETIMEDOUT;
- }
- }
-
- return 0;
-}
-
-/*
- * omap3isp_module_sync_is_stopped - Helper to verify if module was stopping
- * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization
- * @stopping: flag which tells module wants to stop
- *
- * This function checks if ISP submodule was stopping. In case of yes, it
- * notices the caller by setting stopping to 0 and waking up the wait queue.
- * Returns 1 if it was stopping or 0 otherwise.
- */
-int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
- atomic_t *stopping)
-{
- if (atomic_cmpxchg(stopping, 1, 0)) {
- wake_up(wait);
- return 1;
- }
-
- return 0;
-}
-
-/* --------------------------------------------------------------------------
- * Clock management
- */
-
-#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \
- ISPCTRL_HIST_CLK_EN | \
- ISPCTRL_RSZ_CLK_EN | \
- (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \
- (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN))
-
-static void __isp_subclk_update(struct isp_device *isp)
-{
- u32 clk = 0;
-
- if (isp->subclk_resources & OMAP3_ISP_SUBCLK_H3A)
- clk |= ISPCTRL_H3A_CLK_EN;
-
- if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST)
- clk |= ISPCTRL_HIST_CLK_EN;
-
- if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER)
- clk |= ISPCTRL_RSZ_CLK_EN;
-
- /* NOTE: For CCDC & Preview submodules, we need to affect internal
- * RAM as well.
- */
- if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC)
- clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN;
-
- if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW)
- clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN;
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_CLKS_MASK, clk);
-}
-
-void omap3isp_subclk_enable(struct isp_device *isp,
- enum isp_subclk_resource res)
-{
- isp->subclk_resources |= res;
-
- __isp_subclk_update(isp);
-}
-
-void omap3isp_subclk_disable(struct isp_device *isp,
- enum isp_subclk_resource res)
-{
- isp->subclk_resources &= ~res;
-
- __isp_subclk_update(isp);
-}
-
-/*
- * isp_enable_clocks - Enable ISP clocks
- * @isp: OMAP3 ISP device
- *
- * Return 0 if successful, or clk_enable return value if any of tthem fails.
- */
-static int isp_enable_clocks(struct isp_device *isp)
-{
- int r;
- unsigned long rate;
- int divisor;
-
- /*
- * cam_mclk clock chain:
- * dpll4 -> dpll4_m5 -> dpll4_m5x2 -> cam_mclk
- *
- * In OMAP3630 dpll4_m5x2 != 2 x dpll4_m5 but both are
- * set to the same value. Hence the rate set for dpll4_m5
- * has to be twice of what is set on OMAP3430 to get
- * the required value for cam_mclk
- */
- if (cpu_is_omap3630())
- divisor = 1;
- else
- divisor = 2;
-
- r = clk_enable(isp->clock[ISP_CLK_CAM_ICK]);
- if (r) {
- dev_err(isp->dev, "clk_enable cam_ick failed\n");
- goto out_clk_enable_ick;
- }
- r = clk_set_rate(isp->clock[ISP_CLK_DPLL4_M5_CK],
- CM_CAM_MCLK_HZ/divisor);
- if (r) {
- dev_err(isp->dev, "clk_set_rate for dpll4_m5_ck failed\n");
- goto out_clk_enable_mclk;
- }
- r = clk_enable(isp->clock[ISP_CLK_CAM_MCLK]);
- if (r) {
- dev_err(isp->dev, "clk_enable cam_mclk failed\n");
- goto out_clk_enable_mclk;
- }
- rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]);
- if (rate != CM_CAM_MCLK_HZ)
- dev_warn(isp->dev, "unexpected cam_mclk rate:\n"
- " expected : %d\n"
- " actual : %ld\n", CM_CAM_MCLK_HZ, rate);
- r = clk_enable(isp->clock[ISP_CLK_CSI2_FCK]);
- if (r) {
- dev_err(isp->dev, "clk_enable csi2_fck failed\n");
- goto out_clk_enable_csi2_fclk;
- }
- return 0;
-
-out_clk_enable_csi2_fclk:
- clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
-out_clk_enable_mclk:
- clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
-out_clk_enable_ick:
- return r;
-}
-
-/*
- * isp_disable_clocks - Disable ISP clocks
- * @isp: OMAP3 ISP device
- */
-static void isp_disable_clocks(struct isp_device *isp)
-{
- clk_disable(isp->clock[ISP_CLK_CAM_ICK]);
- clk_disable(isp->clock[ISP_CLK_CAM_MCLK]);
- clk_disable(isp->clock[ISP_CLK_CSI2_FCK]);
-}
-
-static const char *isp_clocks[] = {
- "cam_ick",
- "cam_mclk",
- "dpll4_m5_ck",
- "csi2_96m_fck",
- "l3_ick",
-};
-
-static void isp_put_clocks(struct isp_device *isp)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
- if (isp->clock[i]) {
- clk_put(isp->clock[i]);
- isp->clock[i] = NULL;
- }
- }
-}
-
-static int isp_get_clocks(struct isp_device *isp)
-{
- struct clk *clk;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) {
- clk = clk_get(isp->dev, isp_clocks[i]);
- if (IS_ERR(clk)) {
- dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]);
- isp_put_clocks(isp);
- return PTR_ERR(clk);
- }
-
- isp->clock[i] = clk;
- }
-
- return 0;
-}
-
-/*
- * omap3isp_get - Acquire the ISP resource.
- *
- * Initializes the clocks for the first acquire.
- *
- * Increment the reference count on the ISP. If the first reference is taken,
- * enable clocks and power-up all submodules.
- *
- * Return a pointer to the ISP device structure, or NULL if an error occurred.
- */
-struct isp_device *omap3isp_get(struct isp_device *isp)
-{
- struct isp_device *__isp = isp;
-
- if (isp == NULL)
- return NULL;
-
- mutex_lock(&isp->isp_mutex);
- if (isp->ref_count > 0)
- goto out;
-
- if (isp_enable_clocks(isp) < 0) {
- __isp = NULL;
- goto out;
- }
-
- /* We don't want to restore context before saving it! */
- if (isp->has_context)
- isp_restore_ctx(isp);
- else
- isp->has_context = 1;
-
- isp_enable_interrupts(isp);
-
-out:
- if (__isp != NULL)
- isp->ref_count++;
- mutex_unlock(&isp->isp_mutex);
-
- return __isp;
-}
-
-/*
- * omap3isp_put - Release the ISP
- *
- * Decrement the reference count on the ISP. If the last reference is released,
- * power-down all submodules, disable clocks and free temporary buffers.
- */
-void omap3isp_put(struct isp_device *isp)
-{
- if (isp == NULL)
- return;
-
- mutex_lock(&isp->isp_mutex);
- BUG_ON(isp->ref_count == 0);
- if (--isp->ref_count == 0) {
- isp_disable_interrupts(isp);
- if (isp->domain)
- isp_save_ctx(isp);
- /* Reset the ISP if an entity has failed to stop. This is the
- * only way to recover from such conditions.
- */
- if (isp->crashed)
- isp_reset(isp);
- isp_disable_clocks(isp);
- }
- mutex_unlock(&isp->isp_mutex);
-}
-
-/* --------------------------------------------------------------------------
- * Platform device driver
- */
-
-/*
- * omap3isp_print_status - Prints the values of the ISP Control Module registers
- * @isp: OMAP3 ISP device
- */
-#define ISP_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
-#define SBL_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
-
-void omap3isp_print_status(struct isp_device *isp)
-{
- dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
-
- ISP_PRINT_REGISTER(isp, SYSCONFIG);
- ISP_PRINT_REGISTER(isp, SYSSTATUS);
- ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
- ISP_PRINT_REGISTER(isp, IRQ0STATUS);
- ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
- ISP_PRINT_REGISTER(isp, CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
-
- SBL_PRINT_REGISTER(isp, PCR);
- SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
-#ifdef CONFIG_PM
-
-/*
- * Power management support.
- *
- * As the ISP can't properly handle an input video stream interruption on a non
- * frame boundary, the ISP pipelines need to be stopped before sensors get
- * suspended. However, as suspending the sensors can require a running clock,
- * which can be provided by the ISP, the ISP can't be completely suspended
- * before the sensor.
- *
- * To solve this problem power management support is split into prepare/complete
- * and suspend/resume operations. The pipelines are stopped in prepare() and the
- * ISP clocks get disabled in suspend(). Similarly, the clocks are reenabled in
- * resume(), and the the pipelines are restarted in complete().
- *
- * TODO: PM dependencies between the ISP and sensors are not modeled explicitly
- * yet.
- */
-static int isp_pm_prepare(struct device *dev)
-{
- struct isp_device *isp = dev_get_drvdata(dev);
- int reset;
-
- WARN_ON(mutex_is_locked(&isp->isp_mutex));
-
- if (isp->ref_count == 0)
- return 0;
-
- reset = isp_suspend_modules(isp);
- isp_disable_interrupts(isp);
- isp_save_ctx(isp);
- if (reset)
- isp_reset(isp);
-
- return 0;
-}
-
-static int isp_pm_suspend(struct device *dev)
-{
- struct isp_device *isp = dev_get_drvdata(dev);
-
- WARN_ON(mutex_is_locked(&isp->isp_mutex));
-
- if (isp->ref_count)
- isp_disable_clocks(isp);
-
- return 0;
-}
-
-static int isp_pm_resume(struct device *dev)
-{
- struct isp_device *isp = dev_get_drvdata(dev);
-
- if (isp->ref_count == 0)
- return 0;
-
- return isp_enable_clocks(isp);
-}
-
-static void isp_pm_complete(struct device *dev)
-{
- struct isp_device *isp = dev_get_drvdata(dev);
-
- if (isp->ref_count == 0)
- return;
-
- isp_restore_ctx(isp);
- isp_enable_interrupts(isp);
- isp_resume_modules(isp);
-}
-
-#else
-
-#define isp_pm_prepare NULL
-#define isp_pm_suspend NULL
-#define isp_pm_resume NULL
-#define isp_pm_complete NULL
-
-#endif /* CONFIG_PM */
-
-static void isp_unregister_entities(struct isp_device *isp)
-{
- omap3isp_csi2_unregister_entities(&isp->isp_csi2a);
- omap3isp_ccp2_unregister_entities(&isp->isp_ccp2);
- omap3isp_ccdc_unregister_entities(&isp->isp_ccdc);
- omap3isp_preview_unregister_entities(&isp->isp_prev);
- omap3isp_resizer_unregister_entities(&isp->isp_res);
- omap3isp_stat_unregister_entities(&isp->isp_aewb);
- omap3isp_stat_unregister_entities(&isp->isp_af);
- omap3isp_stat_unregister_entities(&isp->isp_hist);
-
- v4l2_device_unregister(&isp->v4l2_dev);
- media_device_unregister(&isp->media_dev);
-}
-
-/*
- * isp_register_subdev_group - Register a group of subdevices
- * @isp: OMAP3 ISP device
- * @board_info: I2C subdevs board information array
- *
- * Register all I2C subdevices in the board_info array. The array must be
- * terminated by a NULL entry, and the first entry must be the sensor.
- *
- * Return a pointer to the sensor media entity if it has been successfully
- * registered, or NULL otherwise.
- */
-static struct v4l2_subdev *
-isp_register_subdev_group(struct isp_device *isp,
- struct isp_subdev_i2c_board_info *board_info)
-{
- struct v4l2_subdev *sensor = NULL;
- unsigned int first;
-
- if (board_info->board_info == NULL)
- return NULL;
-
- for (first = 1; board_info->board_info; ++board_info, first = 0) {
- struct v4l2_subdev *subdev;
- struct i2c_adapter *adapter;
-
- adapter = i2c_get_adapter(board_info->i2c_adapter_id);
- if (adapter == NULL) {
- printk(KERN_ERR "%s: Unable to get I2C adapter %d for "
- "device %s\n", __func__,
- board_info->i2c_adapter_id,
- board_info->board_info->type);
- continue;
- }
-
- subdev = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
- board_info->board_info, NULL);
- if (subdev == NULL) {
- printk(KERN_ERR "%s: Unable to register subdev %s\n",
- __func__, board_info->board_info->type);
- continue;
- }
-
- if (first)
- sensor = subdev;
- }
-
- return sensor;
-}
-
-static int isp_register_entities(struct isp_device *isp)
-{
- struct isp_platform_data *pdata = isp->pdata;
- struct isp_v4l2_subdevs_group *subdevs;
- int ret;
-
- isp->media_dev.dev = isp->dev;
- strlcpy(isp->media_dev.model, "TI OMAP3 ISP",
- sizeof(isp->media_dev.model));
- isp->media_dev.hw_revision = isp->revision;
- isp->media_dev.link_notify = isp_pipeline_link_notify;
- ret = media_device_register(&isp->media_dev);
- if (ret < 0) {
- printk(KERN_ERR "%s: Media device registration failed (%d)\n",
- __func__, ret);
- return ret;
- }
-
- isp->v4l2_dev.mdev = &isp->media_dev;
- ret = v4l2_device_register(isp->dev, &isp->v4l2_dev);
- if (ret < 0) {
- printk(KERN_ERR "%s: V4L2 device registration failed (%d)\n",
- __func__, ret);
- goto done;
- }
-
- /* Register internal entities */
- ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_preview_register_entities(&isp->isp_prev,
- &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev);
- if (ret < 0)
- goto done;
-
- /* Register external entities */
- for (subdevs = pdata->subdevs; subdevs && subdevs->subdevs; ++subdevs) {
- struct v4l2_subdev *sensor;
- struct media_entity *input;
- unsigned int flags;
- unsigned int pad;
-
- sensor = isp_register_subdev_group(isp, subdevs->subdevs);
- if (sensor == NULL)
- continue;
-
- sensor->host_priv = subdevs;
-
- /* Connect the sensor to the correct interface module. Parallel
- * sensors are connected directly to the CCDC, while serial
- * sensors are connected to the CSI2a, CCP2b or CSI2c receiver
- * through CSIPHY1 or CSIPHY2.
- */
- switch (subdevs->interface) {
- case ISP_INTERFACE_PARALLEL:
- input = &isp->isp_ccdc.subdev.entity;
- pad = CCDC_PAD_SINK;
- flags = 0;
- break;
-
- case ISP_INTERFACE_CSI2A_PHY2:
- input = &isp->isp_csi2a.subdev.entity;
- pad = CSI2_PAD_SINK;
- flags = MEDIA_LNK_FL_IMMUTABLE
- | MEDIA_LNK_FL_ENABLED;
- break;
-
- case ISP_INTERFACE_CCP2B_PHY1:
- case ISP_INTERFACE_CCP2B_PHY2:
- input = &isp->isp_ccp2.subdev.entity;
- pad = CCP2_PAD_SINK;
- flags = 0;
- break;
-
- case ISP_INTERFACE_CSI2C_PHY1:
- input = &isp->isp_csi2c.subdev.entity;
- pad = CSI2_PAD_SINK;
- flags = MEDIA_LNK_FL_IMMUTABLE
- | MEDIA_LNK_FL_ENABLED;
- break;
-
- default:
- printk(KERN_ERR "%s: invalid interface type %u\n",
- __func__, subdevs->interface);
- ret = -EINVAL;
- goto done;
- }
-
- ret = media_entity_create_link(&sensor->entity, 0, input, pad,
- flags);
- if (ret < 0)
- goto done;
- }
-
- ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
-
-done:
- if (ret < 0)
- isp_unregister_entities(isp);
-
- return ret;
-}
-
-static void isp_cleanup_modules(struct isp_device *isp)
-{
- omap3isp_h3a_aewb_cleanup(isp);
- omap3isp_h3a_af_cleanup(isp);
- omap3isp_hist_cleanup(isp);
- omap3isp_resizer_cleanup(isp);
- omap3isp_preview_cleanup(isp);
- omap3isp_ccdc_cleanup(isp);
- omap3isp_ccp2_cleanup(isp);
- omap3isp_csi2_cleanup(isp);
-}
-
-static int isp_initialize_modules(struct isp_device *isp)
-{
- int ret;
-
- ret = omap3isp_csiphy_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "CSI PHY initialization failed\n");
- goto error_csiphy;
- }
-
- ret = omap3isp_csi2_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "CSI2 initialization failed\n");
- goto error_csi2;
- }
-
- ret = omap3isp_ccp2_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "CCP2 initialization failed\n");
- goto error_ccp2;
- }
-
- ret = omap3isp_ccdc_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "CCDC initialization failed\n");
- goto error_ccdc;
- }
-
- ret = omap3isp_preview_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "Preview initialization failed\n");
- goto error_preview;
- }
-
- ret = omap3isp_resizer_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "Resizer initialization failed\n");
- goto error_resizer;
- }
-
- ret = omap3isp_hist_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "Histogram initialization failed\n");
- goto error_hist;
- }
-
- ret = omap3isp_h3a_aewb_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "H3A AEWB initialization failed\n");
- goto error_h3a_aewb;
- }
-
- ret = omap3isp_h3a_af_init(isp);
- if (ret < 0) {
- dev_err(isp->dev, "H3A AF initialization failed\n");
- goto error_h3a_af;
- }
-
- /* Connect the submodules. */
- ret = media_entity_create_link(
- &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE,
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE,
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF,
- &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE,
- &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_aewb.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_af.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(
- &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP,
- &isp->isp_hist.subdev.entity, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
- if (ret < 0)
- goto error_link;
-
- return 0;
-
-error_link:
- omap3isp_h3a_af_cleanup(isp);
-error_h3a_af:
- omap3isp_h3a_aewb_cleanup(isp);
-error_h3a_aewb:
- omap3isp_hist_cleanup(isp);
-error_hist:
- omap3isp_resizer_cleanup(isp);
-error_resizer:
- omap3isp_preview_cleanup(isp);
-error_preview:
- omap3isp_ccdc_cleanup(isp);
-error_ccdc:
- omap3isp_ccp2_cleanup(isp);
-error_ccp2:
- omap3isp_csi2_cleanup(isp);
-error_csi2:
-error_csiphy:
- return ret;
-}
-
-/*
- * isp_remove - Remove ISP platform device
- * @pdev: Pointer to ISP platform device
- *
- * Always returns 0.
- */
-static int __devexit isp_remove(struct platform_device *pdev)
-{
- struct isp_device *isp = platform_get_drvdata(pdev);
- int i;
-
- isp_unregister_entities(isp);
- isp_cleanup_modules(isp);
-
- omap3isp_get(isp);
- iommu_detach_device(isp->domain, &pdev->dev);
- iommu_domain_free(isp->domain);
- isp->domain = NULL;
- omap3isp_put(isp);
-
- free_irq(isp->irq_num, isp);
- isp_put_clocks(isp);
-
- for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp->mmio_base[i]) {
- iounmap(isp->mmio_base[i]);
- isp->mmio_base[i] = NULL;
- }
-
- if (isp->mmio_base_phys[i]) {
- release_mem_region(isp->mmio_base_phys[i],
- isp->mmio_size[i]);
- isp->mmio_base_phys[i] = 0;
- }
- }
-
- regulator_put(isp->isp_csiphy1.vdd);
- regulator_put(isp->isp_csiphy2.vdd);
- kfree(isp);
-
- return 0;
-}
-
-static int isp_map_mem_resource(struct platform_device *pdev,
- struct isp_device *isp,
- enum isp_mem_resources res)
-{
- struct resource *mem;
-
- /* request the mem region for the camera registers */
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, res);
- if (!mem) {
- dev_err(isp->dev, "no mem resource?\n");
- return -ENODEV;
- }
-
- if (!request_mem_region(mem->start, resource_size(mem), pdev->name)) {
- dev_err(isp->dev,
- "cannot reserve camera register I/O region\n");
- return -ENODEV;
- }
- isp->mmio_base_phys[res] = mem->start;
- isp->mmio_size[res] = resource_size(mem);
-
- /* map the region */
- isp->mmio_base[res] = ioremap_nocache(isp->mmio_base_phys[res],
- isp->mmio_size[res]);
- if (!isp->mmio_base[res]) {
- dev_err(isp->dev, "cannot map camera register I/O region\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-/*
- * isp_probe - Probe ISP platform device
- * @pdev: Pointer to ISP platform device
- *
- * Returns 0 if successful,
- * -ENOMEM if no memory available,
- * -ENODEV if no platform device resources found
- * or no space for remapping registers,
- * -EINVAL if couldn't install ISR,
- * or clk_get return error value.
- */
-static int __devinit isp_probe(struct platform_device *pdev)
-{
- struct isp_platform_data *pdata = pdev->dev.platform_data;
- struct isp_device *isp;
- int ret;
- int i, m;
-
- if (pdata == NULL)
- return -EINVAL;
-
- isp = kzalloc(sizeof(*isp), GFP_KERNEL);
- if (!isp) {
- dev_err(&pdev->dev, "could not allocate memory\n");
- return -ENOMEM;
- }
-
- isp->autoidle = autoidle;
- isp->platform_cb.set_xclk = isp_set_xclk;
-
- mutex_init(&isp->isp_mutex);
- spin_lock_init(&isp->stat_lock);
-
- isp->dev = &pdev->dev;
- isp->pdata = pdata;
- isp->ref_count = 0;
-
- isp->raw_dmamask = DMA_BIT_MASK(32);
- isp->dev->dma_mask = &isp->raw_dmamask;
- isp->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- platform_set_drvdata(pdev, isp);
-
- /* Regulators */
- isp->isp_csiphy1.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY1");
- isp->isp_csiphy2.vdd = regulator_get(&pdev->dev, "VDD_CSIPHY2");
-
- /* Clocks */
- ret = isp_map_mem_resource(pdev, isp, OMAP3_ISP_IOMEM_MAIN);
- if (ret < 0)
- goto error;
-
- ret = isp_get_clocks(isp);
- if (ret < 0)
- goto error;
-
- if (omap3isp_get(isp) == NULL)
- goto error;
-
- ret = isp_reset(isp);
- if (ret < 0)
- goto error_isp;
-
- /* Memory resources */
- isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
- dev_info(isp->dev, "Revision %d.%d found\n",
- (isp->revision & 0xf0) >> 4, isp->revision & 0x0f);
-
- for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++)
- if (isp->revision == isp_res_maps[m].isp_rev)
- break;
-
- if (m == ARRAY_SIZE(isp_res_maps)) {
- dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n",
- (isp->revision & 0xf0) >> 4, isp->revision & 0xf);
- ret = -ENODEV;
- goto error_isp;
- }
-
- for (i = 1; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp_res_maps[m].map & 1 << i) {
- ret = isp_map_mem_resource(pdev, isp, i);
- if (ret)
- goto error_isp;
- }
- }
-
- isp->domain = iommu_domain_alloc(pdev->dev.bus);
- if (!isp->domain) {
- dev_err(isp->dev, "can't alloc iommu domain\n");
- ret = -ENOMEM;
- goto error_isp;
- }
-
- ret = iommu_attach_device(isp->domain, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
- goto free_domain;
- }
-
- /* Interrupt */
- isp->irq_num = platform_get_irq(pdev, 0);
- if (isp->irq_num <= 0) {
- dev_err(isp->dev, "No IRQ resource\n");
- ret = -ENODEV;
- goto detach_dev;
- }
-
- if (request_irq(isp->irq_num, isp_isr, IRQF_SHARED, "OMAP3 ISP", isp)) {
- dev_err(isp->dev, "Unable to request IRQ\n");
- ret = -EINVAL;
- goto detach_dev;
- }
-
- /* Entities */
- ret = isp_initialize_modules(isp);
- if (ret < 0)
- goto error_irq;
-
- ret = isp_register_entities(isp);
- if (ret < 0)
- goto error_modules;
-
- isp_power_settings(isp, 1);
- omap3isp_put(isp);
-
- return 0;
-
-error_modules:
- isp_cleanup_modules(isp);
-error_irq:
- free_irq(isp->irq_num, isp);
-detach_dev:
- iommu_detach_device(isp->domain, &pdev->dev);
-free_domain:
- iommu_domain_free(isp->domain);
-error_isp:
- omap3isp_put(isp);
-error:
- isp_put_clocks(isp);
-
- for (i = 0; i < OMAP3_ISP_IOMEM_LAST; i++) {
- if (isp->mmio_base[i]) {
- iounmap(isp->mmio_base[i]);
- isp->mmio_base[i] = NULL;
- }
-
- if (isp->mmio_base_phys[i]) {
- release_mem_region(isp->mmio_base_phys[i],
- isp->mmio_size[i]);
- isp->mmio_base_phys[i] = 0;
- }
- }
- regulator_put(isp->isp_csiphy2.vdd);
- regulator_put(isp->isp_csiphy1.vdd);
- platform_set_drvdata(pdev, NULL);
-
- mutex_destroy(&isp->isp_mutex);
- kfree(isp);
-
- return ret;
-}
-
-static const struct dev_pm_ops omap3isp_pm_ops = {
- .prepare = isp_pm_prepare,
- .suspend = isp_pm_suspend,
- .resume = isp_pm_resume,
- .complete = isp_pm_complete,
-};
-
-static struct platform_device_id omap3isp_id_table[] = {
- { "omap3isp", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
-
-static struct platform_driver omap3isp_driver = {
- .probe = isp_probe,
- .remove = __devexit_p(isp_remove),
- .id_table = omap3isp_id_table,
- .driver = {
- .owner = THIS_MODULE,
- .name = "omap3isp",
- .pm = &omap3isp_pm_ops,
- },
-};
-
-module_platform_driver(omap3isp_driver);
-
-MODULE_AUTHOR("Nokia Corporation");
-MODULE_DESCRIPTION("TI OMAP3 ISP driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(ISP_VIDEO_DRIVER_VERSION);
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
deleted file mode 100644
index fc7af3e32efd..000000000000
--- a/drivers/media/video/omap3isp/isp.h
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * isp.h
- *
- * TI OMAP3 ISP - Core
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_CORE_H
-#define OMAP3_ISP_CORE_H
-
-#include <media/omap3isp.h>
-#include <media/v4l2-device.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/wait.h>
-#include <linux/iommu.h>
-#include <plat/iommu.h>
-#include <plat/iovmm.h>
-
-#include "ispstat.h"
-#include "ispccdc.h"
-#include "ispreg.h"
-#include "ispresizer.h"
-#include "isppreview.h"
-#include "ispcsiphy.h"
-#include "ispcsi2.h"
-#include "ispccp2.h"
-
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
-#define ISP_TOK_TERM 0xFFFFFFFF /*
- * terminating token for ISP
- * modules reg list
- */
-#define to_isp_device(ptr_module) \
- container_of(ptr_module, struct isp_device, isp_##ptr_module)
-#define to_device(ptr_module) \
- (to_isp_device(ptr_module)->dev)
-
-enum isp_mem_resources {
- OMAP3_ISP_IOMEM_MAIN,
- OMAP3_ISP_IOMEM_CCP2,
- OMAP3_ISP_IOMEM_CCDC,
- OMAP3_ISP_IOMEM_HIST,
- OMAP3_ISP_IOMEM_H3A,
- OMAP3_ISP_IOMEM_PREV,
- OMAP3_ISP_IOMEM_RESZ,
- OMAP3_ISP_IOMEM_SBL,
- OMAP3_ISP_IOMEM_CSI2A_REGS1,
- OMAP3_ISP_IOMEM_CSIPHY2,
- OMAP3_ISP_IOMEM_CSI2A_REGS2,
- OMAP3_ISP_IOMEM_CSI2C_REGS1,
- OMAP3_ISP_IOMEM_CSIPHY1,
- OMAP3_ISP_IOMEM_CSI2C_REGS2,
- OMAP3_ISP_IOMEM_LAST
-};
-
-enum isp_sbl_resource {
- OMAP3_ISP_SBL_CSI1_READ = 0x1,
- OMAP3_ISP_SBL_CSI1_WRITE = 0x2,
- OMAP3_ISP_SBL_CSI2A_WRITE = 0x4,
- OMAP3_ISP_SBL_CSI2C_WRITE = 0x8,
- OMAP3_ISP_SBL_CCDC_LSC_READ = 0x10,
- OMAP3_ISP_SBL_CCDC_WRITE = 0x20,
- OMAP3_ISP_SBL_PREVIEW_READ = 0x40,
- OMAP3_ISP_SBL_PREVIEW_WRITE = 0x80,
- OMAP3_ISP_SBL_RESIZER_READ = 0x100,
- OMAP3_ISP_SBL_RESIZER_WRITE = 0x200,
-};
-
-enum isp_subclk_resource {
- OMAP3_ISP_SUBCLK_CCDC = (1 << 0),
- OMAP3_ISP_SUBCLK_H3A = (1 << 1),
- OMAP3_ISP_SUBCLK_HIST = (1 << 2),
- OMAP3_ISP_SUBCLK_PREVIEW = (1 << 3),
- OMAP3_ISP_SUBCLK_RESIZER = (1 << 4),
-};
-
-/* ISP: OMAP 34xx ES 1.0 */
-#define ISP_REVISION_1_0 0x10
-/* ISP2: OMAP 34xx ES 2.0, 2.1 and 3.0 */
-#define ISP_REVISION_2_0 0x20
-/* ISP2P: OMAP 36xx */
-#define ISP_REVISION_15_0 0xF0
-
-/*
- * struct isp_res_mapping - Map ISP io resources to ISP revision.
- * @isp_rev: ISP_REVISION_x_x
- * @map: bitmap for enum isp_mem_resources
- */
-struct isp_res_mapping {
- u32 isp_rev;
- u32 map;
-};
-
-/*
- * struct isp_reg - Structure for ISP register values.
- * @reg: 32-bit Register address.
- * @val: 32-bit Register value.
- */
-struct isp_reg {
- enum isp_mem_resources mmio_range;
- u32 reg;
- u32 val;
-};
-
-struct isp_platform_callback {
- u32 (*set_xclk)(struct isp_device *isp, u32 xclk, u8 xclksel);
- int (*csiphy_config)(struct isp_csiphy *phy,
- struct isp_csiphy_dphy_cfg *dphy,
- struct isp_csiphy_lanes_cfg *lanes);
-};
-
-/*
- * struct isp_device - ISP device structure.
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @revision: Stores current ISP module revision.
- * @irq_num: Currently used IRQ number.
- * @mmio_base: Array with kernel base addresses for ioremapped ISP register
- * regions.
- * @mmio_base_phys: Array with physical L4 bus addresses for ISP register
- * regions.
- * @mmio_size: Array with ISP register regions size in bytes.
- * @raw_dmamask: Raw DMA mask
- * @stat_lock: Spinlock for handling statistics
- * @isp_mutex: Mutex for serializing requests to ISP.
- * @crashed: Bitmask of crashed entities (indexed by entity ID)
- * @has_context: Context has been saved at least once and can be restored.
- * @ref_count: Reference count for handling multiple ISP requests.
- * @cam_ick: Pointer to camera interface clock structure.
- * @cam_mclk: Pointer to camera functional clock structure.
- * @dpll4_m5_ck: Pointer to DPLL4 M5 clock structure.
- * @csi2_fck: Pointer to camera CSI2 complexIO clock structure.
- * @l3_ick: Pointer to OMAP3 L3 bus interface clock.
- * @irq: Currently attached ISP ISR callbacks information structure.
- * @isp_af: Pointer to current settings for ISP AutoFocus SCM.
- * @isp_hist: Pointer to current settings for ISP Histogram SCM.
- * @isp_h3a: Pointer to current settings for ISP Auto Exposure and
- * White Balance SCM.
- * @isp_res: Pointer to current settings for ISP Resizer.
- * @isp_prev: Pointer to current settings for ISP Preview.
- * @isp_ccdc: Pointer to current settings for ISP CCDC.
- * @iommu: Pointer to requested IOMMU instance for ISP.
- * @platform_cb: ISP driver callback function pointers for platform code
- *
- * This structure is used to store the OMAP ISP Information.
- */
-struct isp_device {
- struct v4l2_device v4l2_dev;
- struct media_device media_dev;
- struct device *dev;
- u32 revision;
-
- /* platform HW resources */
- struct isp_platform_data *pdata;
- unsigned int irq_num;
-
- void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
- unsigned long mmio_base_phys[OMAP3_ISP_IOMEM_LAST];
- resource_size_t mmio_size[OMAP3_ISP_IOMEM_LAST];
-
- u64 raw_dmamask;
-
- /* ISP Obj */
- spinlock_t stat_lock; /* common lock for statistic drivers */
- struct mutex isp_mutex; /* For handling ref_count field */
- u32 crashed;
- int has_context;
- int ref_count;
- unsigned int autoidle;
- u32 xclk_divisor[2]; /* Two clocks, a and b. */
-#define ISP_CLK_CAM_ICK 0
-#define ISP_CLK_CAM_MCLK 1
-#define ISP_CLK_DPLL4_M5_CK 2
-#define ISP_CLK_CSI2_FCK 3
-#define ISP_CLK_L3_ICK 4
- struct clk *clock[5];
-
- /* ISP modules */
- struct ispstat isp_af;
- struct ispstat isp_aewb;
- struct ispstat isp_hist;
- struct isp_res_device isp_res;
- struct isp_prev_device isp_prev;
- struct isp_ccdc_device isp_ccdc;
- struct isp_csi2_device isp_csi2a;
- struct isp_csi2_device isp_csi2c;
- struct isp_ccp2_device isp_ccp2;
- struct isp_csiphy isp_csiphy1;
- struct isp_csiphy isp_csiphy2;
-
- unsigned int sbl_resources;
- unsigned int subclk_resources;
-
- struct iommu_domain *domain;
-
- struct isp_platform_callback platform_cb;
-};
-
-#define v4l2_dev_to_isp_device(dev) \
- container_of(dev, struct isp_device, v4l2_dev)
-
-void omap3isp_hist_dma_done(struct isp_device *isp);
-
-void omap3isp_flush(struct isp_device *isp);
-
-int omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait,
- atomic_t *stopping);
-
-int omap3isp_module_sync_is_stopping(wait_queue_head_t *wait,
- atomic_t *stopping);
-
-int omap3isp_pipeline_set_stream(struct isp_pipeline *pipe,
- enum isp_pipeline_stream_state state);
-void omap3isp_configure_bridge(struct isp_device *isp,
- enum ccdc_input_entity input,
- const struct isp_parallel_platform_data *pdata,
- unsigned int shift);
-
-struct isp_device *omap3isp_get(struct isp_device *isp);
-void omap3isp_put(struct isp_device *isp);
-
-void omap3isp_print_status(struct isp_device *isp);
-
-void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res);
-void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res);
-
-void omap3isp_subclk_enable(struct isp_device *isp,
- enum isp_subclk_resource res);
-void omap3isp_subclk_disable(struct isp_device *isp,
- enum isp_subclk_resource res);
-
-int omap3isp_pipeline_pm_use(struct media_entity *entity, int use);
-
-int omap3isp_register_entities(struct platform_device *pdev,
- struct v4l2_device *v4l2_dev);
-void omap3isp_unregister_entities(struct platform_device *pdev);
-
-/*
- * isp_reg_readl - Read value of an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @isp_mmio_range: Range to which the register offset refers to.
- * @reg_offset: Register offset to read from.
- *
- * Returns an unsigned 32 bit value with the required register contents.
- */
-static inline
-u32 isp_reg_readl(struct isp_device *isp, enum isp_mem_resources isp_mmio_range,
- u32 reg_offset)
-{
- return __raw_readl(isp->mmio_base[isp_mmio_range] + reg_offset);
-}
-
-/*
- * isp_reg_writel - Write value to an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @reg_value: 32 bit value to write to the register.
- * @isp_mmio_range: Range to which the register offset refers to.
- * @reg_offset: Register offset to write into.
- */
-static inline
-void isp_reg_writel(struct isp_device *isp, u32 reg_value,
- enum isp_mem_resources isp_mmio_range, u32 reg_offset)
-{
- __raw_writel(reg_value, isp->mmio_base[isp_mmio_range] + reg_offset);
-}
-
-/*
- * isp_reg_and - Clear individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @mmio_range: Range to which the register offset refers to.
- * @reg: Register offset to work on.
- * @clr_bits: 32 bit value which would be cleared in the register.
- */
-static inline
-void isp_reg_clr(struct isp_device *isp, enum isp_mem_resources mmio_range,
- u32 reg, u32 clr_bits)
-{
- u32 v = isp_reg_readl(isp, mmio_range, reg);
-
- isp_reg_writel(isp, v & ~clr_bits, mmio_range, reg);
-}
-
-/*
- * isp_reg_set - Set individual bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @mmio_range: Range to which the register offset refers to.
- * @reg: Register offset to work on.
- * @set_bits: 32 bit value which would be set in the register.
- */
-static inline
-void isp_reg_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
- u32 reg, u32 set_bits)
-{
- u32 v = isp_reg_readl(isp, mmio_range, reg);
-
- isp_reg_writel(isp, v | set_bits, mmio_range, reg);
-}
-
-/*
- * isp_reg_clr_set - Clear and set invidial bits in an OMAP3 ISP register
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @mmio_range: Range to which the register offset refers to.
- * @reg: Register offset to work on.
- * @clr_bits: 32 bit value which would be cleared in the register.
- * @set_bits: 32 bit value which would be set in the register.
- *
- * The clear operation is done first, and then the set operation.
- */
-static inline
-void isp_reg_clr_set(struct isp_device *isp, enum isp_mem_resources mmio_range,
- u32 reg, u32 clr_bits, u32 set_bits)
-{
- u32 v = isp_reg_readl(isp, mmio_range, reg);
-
- isp_reg_writel(isp, (v & ~clr_bits) | set_bits, mmio_range, reg);
-}
-
-static inline enum v4l2_buf_type
-isp_pad_buffer_type(const struct v4l2_subdev *subdev, int pad)
-{
- if (pad >= subdev->entity.num_pads)
- return 0;
-
- if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SINK)
- return V4L2_BUF_TYPE_VIDEO_OUTPUT;
- else
- return V4L2_BUF_TYPE_VIDEO_CAPTURE;
-}
-
-#endif /* OMAP3_ISP_CORE_H */
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
deleted file mode 100644
index f1220d3d4970..000000000000
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ /dev/null
@@ -1,2535 +0,0 @@
-/*
- * ispccdc.c
- *
- * TI OMAP3 ISP - CCDC module
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/uaccess.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <media/v4l2-event.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "ispccdc.h"
-
-#define CCDC_MIN_WIDTH 32
-#define CCDC_MIN_HEIGHT 32
-
-static struct v4l2_mbus_framefmt *
-__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which);
-
-static const unsigned int ccdc_fmts[] = {
- V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y12_1X12,
- V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12,
- V4L2_MBUS_FMT_SRGGB12_1X12,
- V4L2_MBUS_FMT_SBGGR12_1X12,
- V4L2_MBUS_FMT_SGBRG12_1X12,
-};
-
-/*
- * ccdc_print_status - Print current CCDC Module register values.
- * @ccdc: Pointer to ISP CCDC device.
- *
- * Also prints other debug information stored in the CCDC module.
- */
-#define CCDC_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###CCDC " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_##name))
-
-static void ccdc_print_status(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- dev_dbg(isp->dev, "-------------CCDC Register dump-------------\n");
-
- CCDC_PRINT_REGISTER(isp, PCR);
- CCDC_PRINT_REGISTER(isp, SYN_MODE);
- CCDC_PRINT_REGISTER(isp, HD_VD_WID);
- CCDC_PRINT_REGISTER(isp, PIX_LINES);
- CCDC_PRINT_REGISTER(isp, HORZ_INFO);
- CCDC_PRINT_REGISTER(isp, VERT_START);
- CCDC_PRINT_REGISTER(isp, VERT_LINES);
- CCDC_PRINT_REGISTER(isp, CULLING);
- CCDC_PRINT_REGISTER(isp, HSIZE_OFF);
- CCDC_PRINT_REGISTER(isp, SDOFST);
- CCDC_PRINT_REGISTER(isp, SDR_ADDR);
- CCDC_PRINT_REGISTER(isp, CLAMP);
- CCDC_PRINT_REGISTER(isp, DCSUB);
- CCDC_PRINT_REGISTER(isp, COLPTN);
- CCDC_PRINT_REGISTER(isp, BLKCMP);
- CCDC_PRINT_REGISTER(isp, FPC);
- CCDC_PRINT_REGISTER(isp, FPC_ADDR);
- CCDC_PRINT_REGISTER(isp, VDINT);
- CCDC_PRINT_REGISTER(isp, ALAW);
- CCDC_PRINT_REGISTER(isp, REC656IF);
- CCDC_PRINT_REGISTER(isp, CFG);
- CCDC_PRINT_REGISTER(isp, FMTCFG);
- CCDC_PRINT_REGISTER(isp, FMT_HORZ);
- CCDC_PRINT_REGISTER(isp, FMT_VERT);
- CCDC_PRINT_REGISTER(isp, PRGEVEN0);
- CCDC_PRINT_REGISTER(isp, PRGEVEN1);
- CCDC_PRINT_REGISTER(isp, PRGODD0);
- CCDC_PRINT_REGISTER(isp, PRGODD1);
- CCDC_PRINT_REGISTER(isp, VP_OUT);
- CCDC_PRINT_REGISTER(isp, LSC_CONFIG);
- CCDC_PRINT_REGISTER(isp, LSC_INITIAL);
- CCDC_PRINT_REGISTER(isp, LSC_TABLE_BASE);
- CCDC_PRINT_REGISTER(isp, LSC_TABLE_OFFSET);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
-/*
- * omap3isp_ccdc_busy - Get busy state of the CCDC.
- * @ccdc: Pointer to ISP CCDC device.
- */
-int omap3isp_ccdc_busy(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR) &
- ISPCCDC_PCR_BUSY;
-}
-
-/* -----------------------------------------------------------------------------
- * Lens Shading Compensation
- */
-
-/*
- * ccdc_lsc_validate_config - Check that LSC configuration is valid.
- * @ccdc: Pointer to ISP CCDC device.
- * @lsc_cfg: the LSC configuration to check.
- *
- * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid.
- */
-static int ccdc_lsc_validate_config(struct isp_ccdc_device *ccdc,
- struct omap3isp_ccdc_lsc_config *lsc_cfg)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- struct v4l2_mbus_framefmt *format;
- unsigned int paxel_width, paxel_height;
- unsigned int paxel_shift_x, paxel_shift_y;
- unsigned int min_width, min_height, min_size;
- unsigned int input_width, input_height;
-
- paxel_shift_x = lsc_cfg->gain_mode_m;
- paxel_shift_y = lsc_cfg->gain_mode_n;
-
- if ((paxel_shift_x < 2) || (paxel_shift_x > 6) ||
- (paxel_shift_y < 2) || (paxel_shift_y > 6)) {
- dev_dbg(isp->dev, "CCDC: LSC: Invalid paxel size\n");
- return -EINVAL;
- }
-
- if (lsc_cfg->offset & 3) {
- dev_dbg(isp->dev, "CCDC: LSC: Offset must be a multiple of "
- "4\n");
- return -EINVAL;
- }
-
- if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) {
- dev_dbg(isp->dev, "CCDC: LSC: initial_x and y must be even\n");
- return -EINVAL;
- }
-
- format = __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
- V4L2_SUBDEV_FORMAT_ACTIVE);
- input_width = format->width;
- input_height = format->height;
-
- /* Calculate minimum bytesize for validation */
- paxel_width = 1 << paxel_shift_x;
- min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1)
- >> paxel_shift_x) + 1;
-
- paxel_height = 1 << paxel_shift_y;
- min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1)
- >> paxel_shift_y) + 1;
-
- min_size = 4 * min_width * min_height;
- if (min_size > lsc_cfg->size) {
- dev_dbg(isp->dev, "CCDC: LSC: too small table\n");
- return -EINVAL;
- }
- if (lsc_cfg->offset < (min_width * 4)) {
- dev_dbg(isp->dev, "CCDC: LSC: Offset is too small\n");
- return -EINVAL;
- }
- if ((lsc_cfg->size / lsc_cfg->offset) < min_height) {
- dev_dbg(isp->dev, "CCDC: LSC: Wrong size/offset combination\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/*
- * ccdc_lsc_program_table - Program Lens Shading Compensation table address.
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr)
-{
- isp_reg_writel(to_isp_device(ccdc), addr,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE);
-}
-
-/*
- * ccdc_lsc_setup_regs - Configures the lens shading compensation module
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_lsc_setup_regs(struct isp_ccdc_device *ccdc,
- struct omap3isp_ccdc_lsc_config *cfg)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- int reg;
-
- isp_reg_writel(isp, cfg->offset, OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_LSC_TABLE_OFFSET);
-
- reg = 0;
- reg |= cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT;
- reg |= cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT;
- reg |= cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT;
- isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG);
-
- reg = 0;
- reg &= ~ISPCCDC_LSC_INITIAL_X_MASK;
- reg |= cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT;
- reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK;
- reg |= cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT;
- isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_LSC_INITIAL);
-}
-
-static int ccdc_lsc_wait_prefetch(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- unsigned int wait;
-
- isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
- OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
-
- /* timeout 1 ms */
- for (wait = 0; wait < 1000; wait++) {
- if (isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS) &
- IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ) {
- isp_reg_writel(isp, IRQ0STATUS_CCDC_LSC_PREF_COMP_IRQ,
- OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS);
- return 0;
- }
-
- rmb();
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-/*
- * __ccdc_lsc_enable - Enables/Disables the Lens Shading Compensation module.
- * @ccdc: Pointer to ISP CCDC device.
- * @enable: 0 Disables LSC, 1 Enables LSC.
- */
-static int __ccdc_lsc_enable(struct isp_ccdc_device *ccdc, int enable)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- const struct v4l2_mbus_framefmt *format =
- __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK,
- V4L2_SUBDEV_FORMAT_ACTIVE);
-
- if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) &&
- (format->code != V4L2_MBUS_FMT_SGBRG10_1X10))
- return -EINVAL;
-
- if (enable)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_LSC_READ);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
- ISPCCDC_LSC_ENABLE, enable ? ISPCCDC_LSC_ENABLE : 0);
-
- if (enable) {
- if (ccdc_lsc_wait_prefetch(ccdc) < 0) {
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE);
- ccdc->lsc.state = LSC_STATE_STOPPED;
- dev_warn(to_device(ccdc), "LSC prefecth timeout\n");
- return -ETIMEDOUT;
- }
- ccdc->lsc.state = LSC_STATE_RUNNING;
- } else {
- ccdc->lsc.state = LSC_STATE_STOPPING;
- }
-
- return 0;
-}
-
-static int ccdc_lsc_busy(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- return isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG) &
- ISPCCDC_LSC_BUSY;
-}
-
-/* __ccdc_lsc_configure - Apply a new configuration to the LSC engine
- * @ccdc: Pointer to ISP CCDC device
- * @req: New configuration request
- *
- * context: in_interrupt()
- */
-static int __ccdc_lsc_configure(struct isp_ccdc_device *ccdc,
- struct ispccdc_lsc_config_req *req)
-{
- if (!req->enable)
- return -EINVAL;
-
- if (ccdc_lsc_validate_config(ccdc, &req->config) < 0) {
- dev_dbg(to_device(ccdc), "Discard LSC configuration\n");
- return -EINVAL;
- }
-
- if (ccdc_lsc_busy(ccdc))
- return -EBUSY;
-
- ccdc_lsc_setup_regs(ccdc, &req->config);
- ccdc_lsc_program_table(ccdc, req->table);
- return 0;
-}
-
-/*
- * ccdc_lsc_error_handler - Handle LSC prefetch error scenario.
- * @ccdc: Pointer to ISP CCDC device.
- *
- * Disables LSC, and defers enablement to shadow registers update time.
- */
-static void ccdc_lsc_error_handler(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- /*
- * From OMAP3 TRM: When this event is pending, the module
- * goes into transparent mode (output =input). Normal
- * operation can be resumed at the start of the next frame
- * after:
- * 1) Clearing this event
- * 2) Disabling the LSC module
- * 3) Enabling it
- */
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG,
- ISPCCDC_LSC_ENABLE);
- ccdc->lsc.state = LSC_STATE_STOPPED;
-}
-
-static void ccdc_lsc_free_request(struct isp_ccdc_device *ccdc,
- struct ispccdc_lsc_config_req *req)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- if (req == NULL)
- return;
-
- if (req->iovm)
- dma_unmap_sg(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
- if (req->table)
- omap_iommu_vfree(isp->domain, isp->dev, req->table);
- kfree(req);
-}
-
-static void ccdc_lsc_free_queue(struct isp_ccdc_device *ccdc,
- struct list_head *queue)
-{
- struct ispccdc_lsc_config_req *req, *n;
- unsigned long flags;
-
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- list_for_each_entry_safe(req, n, queue, list) {
- list_del(&req->list);
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
- ccdc_lsc_free_request(ccdc, req);
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- }
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-}
-
-static void ccdc_lsc_free_table_work(struct work_struct *work)
-{
- struct isp_ccdc_device *ccdc;
- struct ispccdc_lsc *lsc;
-
- lsc = container_of(work, struct ispccdc_lsc, table_work);
- ccdc = container_of(lsc, struct isp_ccdc_device, lsc);
-
- ccdc_lsc_free_queue(ccdc, &lsc->free_queue);
-}
-
-/*
- * ccdc_lsc_config - Configure the LSC module from a userspace request
- *
- * Store the request LSC configuration in the LSC engine request pointer. The
- * configuration will be applied to the hardware when the CCDC will be enabled,
- * or at the next LSC interrupt if the CCDC is already running.
- */
-static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
- struct omap3isp_ccdc_update_config *config)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- struct ispccdc_lsc_config_req *req;
- unsigned long flags;
- void *table;
- u16 update;
- int ret;
-
- update = config->update &
- (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC);
- if (!update)
- return 0;
-
- if (update != (OMAP3ISP_CCDC_CONFIG_LSC | OMAP3ISP_CCDC_TBL_LSC)) {
- dev_dbg(to_device(ccdc), "%s: Both LSC configuration and table "
- "need to be supplied\n", __func__);
- return -EINVAL;
- }
-
- req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (req == NULL)
- return -ENOMEM;
-
- if (config->flag & OMAP3ISP_CCDC_CONFIG_LSC) {
- if (copy_from_user(&req->config, config->lsc_cfg,
- sizeof(req->config))) {
- ret = -EFAULT;
- goto done;
- }
-
- req->enable = 1;
-
- req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
- req->config.size, IOMMU_FLAG);
- if (IS_ERR_VALUE(req->table)) {
- req->table = 0;
- ret = -ENOMEM;
- goto done;
- }
-
- req->iovm = omap_find_iovm_area(isp->dev, req->table);
- if (req->iovm == NULL) {
- ret = -ENOMEM;
- goto done;
- }
-
- if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE)) {
- ret = -ENOMEM;
- req->iovm = NULL;
- goto done;
- }
-
- dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
-
- table = omap_da_to_va(isp->dev, req->table);
- if (copy_from_user(table, config->lsc, req->config.size)) {
- ret = -EFAULT;
- goto done;
- }
-
- dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl,
- req->iovm->sgt->nents, DMA_TO_DEVICE);
- }
-
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- if (ccdc->lsc.request) {
- list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
- schedule_work(&ccdc->lsc.table_work);
- }
- ccdc->lsc.request = req;
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-
- ret = 0;
-
-done:
- if (ret < 0)
- ccdc_lsc_free_request(ccdc, req);
-
- return ret;
-}
-
-static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- if (ccdc->lsc.active) {
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
- return 1;
- }
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
- return 0;
-}
-
-static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc)
-{
- struct ispccdc_lsc *lsc = &ccdc->lsc;
-
- if (lsc->state != LSC_STATE_STOPPED)
- return -EINVAL;
-
- if (lsc->active) {
- list_add_tail(&lsc->active->list, &lsc->free_queue);
- lsc->active = NULL;
- }
-
- if (__ccdc_lsc_configure(ccdc, lsc->request) < 0) {
- omap3isp_sbl_disable(to_isp_device(ccdc),
- OMAP3_ISP_SBL_CCDC_LSC_READ);
- list_add_tail(&lsc->request->list, &lsc->free_queue);
- lsc->request = NULL;
- goto done;
- }
-
- lsc->active = lsc->request;
- lsc->request = NULL;
- __ccdc_lsc_enable(ccdc, 1);
-
-done:
- if (!list_empty(&lsc->free_queue))
- schedule_work(&lsc->table_work);
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * Parameters configuration
- */
-
-/*
- * ccdc_configure_clamp - Configure optical-black or digital clamping
- * @ccdc: Pointer to ISP CCDC device.
- *
- * The CCDC performs either optical-black or digital clamp. Configure and enable
- * the selected clamp method.
- */
-static void ccdc_configure_clamp(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- u32 clamp;
-
- if (ccdc->obclamp) {
- clamp = ccdc->clamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT;
- clamp |= ccdc->clamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT;
- clamp |= ccdc->clamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT;
- clamp |= ccdc->clamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT;
- isp_reg_writel(isp, clamp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP);
- } else {
- isp_reg_writel(isp, ccdc->clamp.dcsubval,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB);
- }
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP,
- ISPCCDC_CLAMP_CLAMPEN,
- ccdc->obclamp ? ISPCCDC_CLAMP_CLAMPEN : 0);
-}
-
-/*
- * ccdc_configure_fpc - Configure Faulty Pixel Correction
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_configure_fpc(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, ISPCCDC_FPC_FPCEN);
-
- if (!ccdc->fpc_en)
- return;
-
- isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_FPC_ADDR);
- /* The FPNUM field must be set before enabling FPC. */
- isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
- isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT) |
- ISPCCDC_FPC_FPCEN, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC);
-}
-
-/*
- * ccdc_configure_black_comp - Configure Black Level Compensation.
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_configure_black_comp(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- u32 blcomp;
-
- blcomp = ccdc->blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT;
- blcomp |= ccdc->blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT;
- blcomp |= ccdc->blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT;
- blcomp |= ccdc->blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT;
-
- isp_reg_writel(isp, blcomp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP);
-}
-
-/*
- * ccdc_configure_lpf - Configure Low-Pass Filter (LPF).
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_configure_lpf(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE,
- ISPCCDC_SYN_MODE_LPF,
- ccdc->lpf ? ISPCCDC_SYN_MODE_LPF : 0);
-}
-
-/*
- * ccdc_configure_alaw - Configure A-law compression.
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_configure_alaw(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- u32 alaw = 0;
-
- switch (ccdc->syncif.datsz) {
- case 8:
- return;
-
- case 10:
- alaw = ISPCCDC_ALAW_GWDI_9_0;
- break;
- case 11:
- alaw = ISPCCDC_ALAW_GWDI_10_1;
- break;
- case 12:
- alaw = ISPCCDC_ALAW_GWDI_11_2;
- break;
- case 13:
- alaw = ISPCCDC_ALAW_GWDI_12_3;
- break;
- }
-
- if (ccdc->alaw)
- alaw |= ISPCCDC_ALAW_CCDTBL;
-
- isp_reg_writel(isp, alaw, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW);
-}
-
-/*
- * ccdc_config_imgattr - Configure sensor image specific attributes.
- * @ccdc: Pointer to ISP CCDC device.
- * @colptn: Color pattern of the sensor.
- */
-static void ccdc_config_imgattr(struct isp_ccdc_device *ccdc, u32 colptn)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_writel(isp, colptn, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN);
-}
-
-/*
- * ccdc_config - Set CCDC configuration from userspace
- * @ccdc: Pointer to ISP CCDC device.
- * @userspace_add: Structure containing CCDC configuration sent from userspace.
- *
- * Returns 0 if successful, -EINVAL if the pointer to the configuration
- * structure is null, or the copy_from_user function fails to copy user space
- * memory to kernel space memory.
- */
-static int ccdc_config(struct isp_ccdc_device *ccdc,
- struct omap3isp_ccdc_update_config *ccdc_struct)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- unsigned long flags;
-
- spin_lock_irqsave(&ccdc->lock, flags);
- ccdc->shadow_update = 1;
- spin_unlock_irqrestore(&ccdc->lock, flags);
-
- if (OMAP3ISP_CCDC_ALAW & ccdc_struct->update) {
- ccdc->alaw = !!(OMAP3ISP_CCDC_ALAW & ccdc_struct->flag);
- ccdc->update |= OMAP3ISP_CCDC_ALAW;
- }
-
- if (OMAP3ISP_CCDC_LPF & ccdc_struct->update) {
- ccdc->lpf = !!(OMAP3ISP_CCDC_LPF & ccdc_struct->flag);
- ccdc->update |= OMAP3ISP_CCDC_LPF;
- }
-
- if (OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->update) {
- if (copy_from_user(&ccdc->clamp, ccdc_struct->bclamp,
- sizeof(ccdc->clamp))) {
- ccdc->shadow_update = 0;
- return -EFAULT;
- }
-
- ccdc->obclamp = !!(OMAP3ISP_CCDC_BLCLAMP & ccdc_struct->flag);
- ccdc->update |= OMAP3ISP_CCDC_BLCLAMP;
- }
-
- if (OMAP3ISP_CCDC_BCOMP & ccdc_struct->update) {
- if (copy_from_user(&ccdc->blcomp, ccdc_struct->blcomp,
- sizeof(ccdc->blcomp))) {
- ccdc->shadow_update = 0;
- return -EFAULT;
- }
-
- ccdc->update |= OMAP3ISP_CCDC_BCOMP;
- }
-
- ccdc->shadow_update = 0;
-
- if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) {
- u32 table_old = 0;
- u32 table_new;
- u32 size;
-
- if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
- return -EBUSY;
-
- ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag);
-
- if (ccdc->fpc_en) {
- if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc,
- sizeof(ccdc->fpc)))
- return -EFAULT;
-
- /*
- * table_new must be 64-bytes aligned, but it's
- * already done by omap_iommu_vmalloc().
- */
- size = ccdc->fpc.fpnum * 4;
- table_new = omap_iommu_vmalloc(isp->domain, isp->dev,
- 0, size, IOMMU_FLAG);
- if (IS_ERR_VALUE(table_new))
- return -ENOMEM;
-
- if (copy_from_user(omap_da_to_va(isp->dev, table_new),
- (__force void __user *)
- ccdc->fpc.fpcaddr, size)) {
- omap_iommu_vfree(isp->domain, isp->dev,
- table_new);
- return -EFAULT;
- }
-
- table_old = ccdc->fpc.fpcaddr;
- ccdc->fpc.fpcaddr = table_new;
- }
-
- ccdc_configure_fpc(ccdc);
- if (table_old != 0)
- omap_iommu_vfree(isp->domain, isp->dev, table_old);
- }
-
- return ccdc_lsc_config(ccdc, ccdc_struct);
-}
-
-static void ccdc_apply_controls(struct isp_ccdc_device *ccdc)
-{
- if (ccdc->update & OMAP3ISP_CCDC_ALAW) {
- ccdc_configure_alaw(ccdc);
- ccdc->update &= ~OMAP3ISP_CCDC_ALAW;
- }
-
- if (ccdc->update & OMAP3ISP_CCDC_LPF) {
- ccdc_configure_lpf(ccdc);
- ccdc->update &= ~OMAP3ISP_CCDC_LPF;
- }
-
- if (ccdc->update & OMAP3ISP_CCDC_BLCLAMP) {
- ccdc_configure_clamp(ccdc);
- ccdc->update &= ~OMAP3ISP_CCDC_BLCLAMP;
- }
-
- if (ccdc->update & OMAP3ISP_CCDC_BCOMP) {
- ccdc_configure_black_comp(ccdc);
- ccdc->update &= ~OMAP3ISP_CCDC_BCOMP;
- }
-}
-
-/*
- * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers
- * @dev: Pointer to ISP device
- */
-void omap3isp_ccdc_restore_context(struct isp_device *isp)
-{
- struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
-
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_VDLC);
-
- ccdc->update = OMAP3ISP_CCDC_ALAW | OMAP3ISP_CCDC_LPF
- | OMAP3ISP_CCDC_BLCLAMP | OMAP3ISP_CCDC_BCOMP;
- ccdc_apply_controls(ccdc);
- ccdc_configure_fpc(ccdc);
-}
-
-/* -----------------------------------------------------------------------------
- * Format- and pipeline-related configuration helpers
- */
-
-/*
- * ccdc_config_vp - Configure the Video Port.
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
- struct isp_device *isp = to_isp_device(ccdc);
- unsigned long l3_ick = pipe->l3_ick;
- unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8;
- unsigned int div = 0;
- u32 fmtcfg_vp;
-
- fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG)
- & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK);
-
- switch (ccdc->syncif.datsz) {
- case 8:
- case 10:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0;
- break;
- case 11:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1;
- break;
- case 12:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2;
- break;
- case 13:
- fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3;
- break;
- };
-
- if (pipe->input)
- div = DIV_ROUND_UP(l3_ick, pipe->max_rate);
- else if (pipe->external_rate)
- div = l3_ick / pipe->external_rate;
-
- div = clamp(div, 2U, max_div);
- fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
-
- isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG);
-}
-
-/*
- * ccdc_enable_vp - Enable Video Port.
- * @ccdc: Pointer to ISP CCDC device.
- * @enable: 0 Disables VP, 1 Enables VP
- *
- * This is needed for outputting image to Preview, H3A and HIST ISP submodules.
- */
-static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG,
- ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0);
-}
-
-/*
- * ccdc_config_outlineoffset - Configure memory saving output line offset
- * @ccdc: Pointer to ISP CCDC device.
- * @offset: Address offset to start a new line. Must be twice the
- * Output width and aligned on 32 byte boundary
- * @oddeven: Specifies the odd/even line pattern to be chosen to store the
- * output.
- * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines.
- *
- * - Configures the output line offset when stored in memory
- * - Sets the odd/even line pattern to store the output
- * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4))
- * - Configures the number of even and odd line fields in case of rearranging
- * the lines.
- */
-static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc,
- u32 offset, u8 oddeven, u8 numlines)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_writel(isp, offset & 0xffff,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF);
-
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- ISPCCDC_SDOFST_FINV);
-
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- ISPCCDC_SDOFST_FOFST_4L);
-
- switch (oddeven) {
- case EVENEVEN:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT);
- break;
- case ODDEVEN:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT);
- break;
- case EVENODD:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT);
- break;
- case ODDODD:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST,
- (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT);
- break;
- default:
- break;
- }
-}
-
-/*
- * ccdc_set_outaddr - Set memory address to save output image
- * @ccdc: Pointer to ISP CCDC device.
- * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
- *
- * Sets the memory address where the output will be saved.
- */
-static void ccdc_set_outaddr(struct isp_ccdc_device *ccdc, u32 addr)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR);
-}
-
-/*
- * omap3isp_ccdc_max_rate - Calculate maximum input data rate based on the input
- * @ccdc: Pointer to ISP CCDC device.
- * @max_rate: Maximum calculated data rate.
- *
- * Returns in *max_rate less value between calculated and passed
- */
-void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
- unsigned int *max_rate)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
- unsigned int rate;
-
- if (pipe == NULL)
- return;
-
- /*
- * TRM says that for parallel sensors the maximum data rate
- * should be 90% form L3/2 clock, otherwise just L3/2.
- */
- if (ccdc->input == CCDC_INPUT_PARALLEL)
- rate = pipe->l3_ick / 2 * 9 / 10;
- else
- rate = pipe->l3_ick / 2;
-
- *max_rate = min(*max_rate, rate);
-}
-
-/*
- * ccdc_config_sync_if - Set CCDC sync interface configuration
- * @ccdc: Pointer to ISP CCDC device.
- * @syncif: Structure containing the sync parameters like field state, CCDC in
- * master/slave mode, raw/yuv data, polarity of data, field, hs, vs
- * signals.
- */
-static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc,
- struct ispccdc_syncif *syncif)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- u32 syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_SYN_MODE);
-
- syn_mode |= ISPCCDC_SYN_MODE_VDHDEN;
-
- if (syncif->fldstat)
- syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT;
-
- syn_mode &= ~ISPCCDC_SYN_MODE_DATSIZ_MASK;
- switch (syncif->datsz) {
- case 8:
- syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8;
- break;
- case 10:
- syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10;
- break;
- case 11:
- syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11;
- break;
- case 12:
- syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12;
- break;
- };
-
- if (syncif->fldmode)
- syn_mode |= ISPCCDC_SYN_MODE_FLDMODE;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE;
-
- if (syncif->datapol)
- syn_mode |= ISPCCDC_SYN_MODE_DATAPOL;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL;
-
- if (syncif->fldpol)
- syn_mode |= ISPCCDC_SYN_MODE_FLDPOL;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL;
-
- if (syncif->hdpol)
- syn_mode |= ISPCCDC_SYN_MODE_HDPOL;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL;
-
- if (syncif->vdpol)
- syn_mode |= ISPCCDC_SYN_MODE_VDPOL;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL;
-
- if (syncif->ccdc_mastermode) {
- syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT;
- isp_reg_writel(isp,
- syncif->hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT
- | syncif->vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT,
- OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_HD_VD_WID);
-
- isp_reg_writel(isp,
- syncif->ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT
- | syncif->hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT,
- OMAP3_ISP_IOMEM_CCDC,
- ISPCCDC_PIX_LINES);
- } else
- syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT |
- ISPCCDC_SYN_MODE_VDHDOUT);
-
- isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
-
- if (!syncif->bt_r656_en)
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF,
- ISPCCDC_REC656IF_R656ON);
-}
-
-/* CCDC formats descriptions */
-static const u32 ccdc_sgrbg_pattern =
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
-
-static const u32 ccdc_srggb_pattern =
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
-
-static const u32 ccdc_sbggr_pattern =
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
-
-static const u32 ccdc_sgbrg_pattern =
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC0_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC1_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP0PLC2_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP0PLC3_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC0_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC1_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP1PLC2_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP1PLC3_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC0_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC1_SHIFT |
- ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP2PLC2_SHIFT |
- ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP2PLC3_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC0_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC1_SHIFT |
- ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP3PLC2_SHIFT |
- ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP3PLC3_SHIFT;
-
-static void ccdc_configure(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
- struct isp_parallel_platform_data *pdata = NULL;
- struct v4l2_subdev *sensor;
- struct v4l2_mbus_framefmt *format;
- const struct v4l2_rect *crop;
- const struct isp_format_info *fmt_info;
- struct v4l2_subdev_format fmt_src;
- unsigned int depth_out;
- unsigned int depth_in = 0;
- struct media_pad *pad;
- unsigned long flags;
- unsigned int shift;
- u32 syn_mode;
- u32 ccdc_pattern;
-
- pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]);
- sensor = media_entity_to_v4l2_subdev(pad->entity);
- if (ccdc->input == CCDC_INPUT_PARALLEL)
- pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv)
- ->bus.parallel;
-
- /* Compute shift value for lane shifter to configure the bridge. */
- fmt_src.pad = pad->index;
- fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- if (!v4l2_subdev_call(sensor, pad, get_fmt, NULL, &fmt_src)) {
- fmt_info = omap3isp_video_format_info(fmt_src.format.code);
- depth_in = fmt_info->bpp;
- }
-
- fmt_info = omap3isp_video_format_info
- (isp->isp_ccdc.formats[CCDC_PAD_SINK].code);
- depth_out = fmt_info->bpp;
-
- shift = depth_in - depth_out;
- omap3isp_configure_bridge(isp, ccdc->input, pdata, shift);
-
- ccdc->syncif.datsz = depth_out;
- ccdc->syncif.hdpol = pdata ? pdata->hs_pol : 0;
- ccdc->syncif.vdpol = pdata ? pdata->vs_pol : 0;
- ccdc_config_sync_if(ccdc, &ccdc->syncif);
-
- /* CCDC_PAD_SINK */
- format = &ccdc->formats[CCDC_PAD_SINK];
-
- syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
-
- /* Use the raw, unprocessed data when writing to memory. The H3A and
- * histogram modules are still fed with lens shading corrected data.
- */
- syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR;
-
- if (ccdc->output & CCDC_OUTPUT_MEMORY)
- syn_mode |= ISPCCDC_SYN_MODE_WEN;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_WEN;
-
- if (ccdc->output & CCDC_OUTPUT_RESIZER)
- syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ;
-
- /* Use PACK8 mode for 1byte per pixel formats. */
- if (omap3isp_video_format_info(format->code)->bpp <= 8)
- syn_mode |= ISPCCDC_SYN_MODE_PACK8;
- else
- syn_mode &= ~ISPCCDC_SYN_MODE_PACK8;
-
- isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE);
-
- /* Mosaic filter */
- switch (format->code) {
- case V4L2_MBUS_FMT_SRGGB10_1X10:
- case V4L2_MBUS_FMT_SRGGB12_1X12:
- ccdc_pattern = ccdc_srggb_pattern;
- break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- case V4L2_MBUS_FMT_SBGGR12_1X12:
- ccdc_pattern = ccdc_sbggr_pattern;
- break;
- case V4L2_MBUS_FMT_SGBRG10_1X10:
- case V4L2_MBUS_FMT_SGBRG12_1X12:
- ccdc_pattern = ccdc_sgbrg_pattern;
- break;
- default:
- /* Use GRBG */
- ccdc_pattern = ccdc_sgrbg_pattern;
- break;
- }
- ccdc_config_imgattr(ccdc, ccdc_pattern);
-
- /* Generate VD0 on the last line of the image and VD1 on the
- * 2/3 height line.
- */
- isp_reg_writel(isp, ((format->height - 2) << ISPCCDC_VDINT_0_SHIFT) |
- ((format->height * 2 / 3) << ISPCCDC_VDINT_1_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
-
- /* CCDC_PAD_SOURCE_OF */
- crop = &ccdc->crop;
-
- isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
- ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
- isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
- isp_reg_writel(isp, (crop->height - 1)
- << ISPCCDC_VERT_LINES_NLV_SHIFT,
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
-
- ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0);
-
- /* CCDC_PAD_SOURCE_VP */
- format = &ccdc->formats[CCDC_PAD_SOURCE_VP];
-
- isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) |
- (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ);
- isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) |
- ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT);
-
- isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) |
- (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT),
- OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT);
-
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
- if (ccdc->lsc.request == NULL)
- goto unlock;
-
- WARN_ON(ccdc->lsc.active);
-
- /* Get last good LSC configuration. If it is not supported for
- * the current active resolution discard it.
- */
- if (ccdc->lsc.active == NULL &&
- __ccdc_lsc_configure(ccdc, ccdc->lsc.request) == 0) {
- ccdc->lsc.active = ccdc->lsc.request;
- } else {
- list_add_tail(&ccdc->lsc.request->list, &ccdc->lsc.free_queue);
- schedule_work(&ccdc->lsc.table_work);
- }
-
- ccdc->lsc.request = NULL;
-
-unlock:
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-
- ccdc_apply_controls(ccdc);
-}
-
-static void __ccdc_enable(struct isp_ccdc_device *ccdc, int enable)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR,
- ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0);
-}
-
-static int ccdc_disable(struct isp_ccdc_device *ccdc)
-{
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&ccdc->lock, flags);
- if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS)
- ccdc->stopping = CCDC_STOP_REQUEST;
- spin_unlock_irqrestore(&ccdc->lock, flags);
-
- ret = wait_event_timeout(ccdc->wait,
- ccdc->stopping == CCDC_STOP_FINISHED,
- msecs_to_jiffies(2000));
- if (ret == 0) {
- ret = -ETIMEDOUT;
- dev_warn(to_device(ccdc), "CCDC stop timeout!\n");
- }
-
- omap3isp_sbl_disable(to_isp_device(ccdc), OMAP3_ISP_SBL_CCDC_LSC_READ);
-
- mutex_lock(&ccdc->ioctl_lock);
- ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
- ccdc->lsc.request = ccdc->lsc.active;
- ccdc->lsc.active = NULL;
- cancel_work_sync(&ccdc->lsc.table_work);
- ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
- mutex_unlock(&ccdc->ioctl_lock);
-
- ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
-
- return ret > 0 ? 0 : ret;
-}
-
-static void ccdc_enable(struct isp_ccdc_device *ccdc)
-{
- if (ccdc_lsc_is_configured(ccdc))
- __ccdc_lsc_enable(ccdc, 1);
- __ccdc_enable(ccdc, 1);
-}
-
-/* -----------------------------------------------------------------------------
- * Interrupt handling
- */
-
-/*
- * ccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits
- * @ccdc: Pointer to ISP CCDC device.
- *
- * Returns zero if the CCDC is idle and the image has been written to
- * memory, too.
- */
-static int ccdc_sbl_busy(struct isp_ccdc_device *ccdc)
-{
- struct isp_device *isp = to_isp_device(ccdc);
-
- return omap3isp_ccdc_busy(ccdc)
- | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_0) &
- ISPSBL_CCDC_WR_0_DATA_READY)
- | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_1) &
- ISPSBL_CCDC_WR_0_DATA_READY)
- | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_2) &
- ISPSBL_CCDC_WR_0_DATA_READY)
- | (isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_CCDC_WR_3) &
- ISPSBL_CCDC_WR_0_DATA_READY);
-}
-
-/*
- * ccdc_sbl_wait_idle - Wait until the CCDC and related SBL are idle
- * @ccdc: Pointer to ISP CCDC device.
- * @max_wait: Max retry count in us for wait for idle/busy transition.
- */
-static int ccdc_sbl_wait_idle(struct isp_ccdc_device *ccdc,
- unsigned int max_wait)
-{
- unsigned int wait = 0;
-
- if (max_wait == 0)
- max_wait = 10000; /* 10 ms */
-
- for (wait = 0; wait <= max_wait; wait++) {
- if (!ccdc_sbl_busy(ccdc))
- return 0;
-
- rmb();
- udelay(1);
- }
-
- return -EBUSY;
-}
-
-/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence
- * @ccdc: Pointer to ISP CCDC device.
- * @event: Pointing which event trigger handler
- *
- * Return 1 when the event and stopping request combination is satisfied,
- * zero otherwise.
- */
-static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event)
-{
- int rval = 0;
-
- switch ((ccdc->stopping & 3) | event) {
- case CCDC_STOP_REQUEST | CCDC_EVENT_VD1:
- if (ccdc->lsc.state != LSC_STATE_STOPPED)
- __ccdc_lsc_enable(ccdc, 0);
- __ccdc_enable(ccdc, 0);
- ccdc->stopping = CCDC_STOP_EXECUTED;
- return 1;
-
- case CCDC_STOP_EXECUTED | CCDC_EVENT_VD0:
- ccdc->stopping |= CCDC_STOP_CCDC_FINISHED;
- if (ccdc->lsc.state == LSC_STATE_STOPPED)
- ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
- rval = 1;
- break;
-
- case CCDC_STOP_EXECUTED | CCDC_EVENT_LSC_DONE:
- ccdc->stopping |= CCDC_STOP_LSC_FINISHED;
- rval = 1;
- break;
-
- case CCDC_STOP_EXECUTED | CCDC_EVENT_VD1:
- return 1;
- }
-
- if (ccdc->stopping == CCDC_STOP_FINISHED) {
- wake_up(&ccdc->wait);
- rval = 1;
- }
-
- return rval;
-}
-
-static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
- struct video_device *vdev = ccdc->subdev.devnode;
- struct v4l2_event event;
-
- /* Frame number propagation */
- atomic_inc(&pipe->frame_number);
-
- memset(&event, 0, sizeof(event));
- event.type = V4L2_EVENT_FRAME_SYNC;
- event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
-
- v4l2_event_queue(vdev, &event);
-}
-
-/*
- * ccdc_lsc_isr - Handle LSC events
- * @ccdc: Pointer to ISP CCDC device.
- * @events: LSC events
- */
-static void ccdc_lsc_isr(struct isp_ccdc_device *ccdc, u32 events)
-{
- unsigned long flags;
-
- if (events & IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ) {
- struct isp_pipeline *pipe =
- to_isp_pipeline(&ccdc->subdev.entity);
-
- ccdc_lsc_error_handler(ccdc);
- pipe->error = true;
- dev_dbg(to_device(ccdc), "lsc prefetch error\n");
- }
-
- if (!(events & IRQ0STATUS_CCDC_LSC_DONE_IRQ))
- return;
-
- /* LSC_DONE interrupt occur, there are two cases
- * 1. stopping for reconfiguration
- * 2. stopping because of STREAM OFF command
- */
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
-
- if (ccdc->lsc.state == LSC_STATE_STOPPING)
- ccdc->lsc.state = LSC_STATE_STOPPED;
-
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE))
- goto done;
-
- if (ccdc->lsc.state != LSC_STATE_RECONFIG)
- goto done;
-
- /* LSC is in STOPPING state, change to the new state */
- ccdc->lsc.state = LSC_STATE_STOPPED;
-
- /* This is an exception. Start of frame and LSC_DONE interrupt
- * have been received on the same time. Skip this event and wait
- * for better times.
- */
- if (events & IRQ0STATUS_HS_VS_IRQ)
- goto done;
-
- /* The LSC engine is stopped at this point. Enable it if there's a
- * pending request.
- */
- if (ccdc->lsc.request == NULL)
- goto done;
-
- ccdc_lsc_enable(ccdc);
-
-done:
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-}
-
-static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity);
- struct isp_device *isp = to_isp_device(ccdc);
- struct isp_buffer *buffer;
- int restart = 0;
-
- /* The CCDC generates VD0 interrupts even when disabled (the datasheet
- * doesn't explicitly state if that's supposed to happen or not, so it
- * can be considered as a hardware bug or as a feature, but we have to
- * deal with it anyway). Disabling the CCDC when no buffer is available
- * would thus not be enough, we need to handle the situation explicitly.
- */
- if (list_empty(&ccdc->video_out.dmaqueue))
- goto done;
-
- /* We're in continuous mode, and memory writes were disabled due to a
- * buffer underrun. Reenable them now that we have a buffer. The buffer
- * address has been set in ccdc_video_queue.
- */
- if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) {
- restart = 1;
- ccdc->underrun = 0;
- goto done;
- }
-
- if (ccdc_sbl_wait_idle(ccdc, 1000)) {
- dev_info(isp->dev, "CCDC won't become idle!\n");
- goto done;
- }
-
- buffer = omap3isp_video_buffer_next(&ccdc->video_out);
- if (buffer != NULL) {
- ccdc_set_outaddr(ccdc, buffer->isp_addr);
- restart = 1;
- }
-
- pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
-
- if (ccdc->state == ISP_PIPELINE_STREAM_SINGLESHOT &&
- isp_pipeline_ready(pipe))
- omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_SINGLESHOT);
-
-done:
- return restart;
-}
-
-/*
- * ccdc_vd0_isr - Handle VD0 event
- * @ccdc: Pointer to ISP CCDC device.
- *
- * Executes LSC deferred enablement before next frame starts.
- */
-static void ccdc_vd0_isr(struct isp_ccdc_device *ccdc)
-{
- unsigned long flags;
- int restart = 0;
-
- if (ccdc->output & CCDC_OUTPUT_MEMORY)
- restart = ccdc_isr_buffer(ccdc);
-
- spin_lock_irqsave(&ccdc->lock, flags);
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) {
- spin_unlock_irqrestore(&ccdc->lock, flags);
- return;
- }
-
- if (!ccdc->shadow_update)
- ccdc_apply_controls(ccdc);
- spin_unlock_irqrestore(&ccdc->lock, flags);
-
- if (restart)
- ccdc_enable(ccdc);
-}
-
-/*
- * ccdc_vd1_isr - Handle VD1 event
- * @ccdc: Pointer to ISP CCDC device.
- */
-static void ccdc_vd1_isr(struct isp_ccdc_device *ccdc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
-
- /*
- * Depending on the CCDC pipeline state, CCDC stopping should be
- * handled differently. In SINGLESHOT we emulate an internal CCDC
- * stopping because the CCDC hw works only in continuous mode.
- * When CONTINUOUS pipeline state is used and the CCDC writes it's
- * data to memory the CCDC and LSC are stopped immediately but
- * without change the CCDC stopping state machine. The CCDC
- * stopping state machine should be used only when user request
- * for stopping is received (SINGLESHOT is an exeption).
- */
- switch (ccdc->state) {
- case ISP_PIPELINE_STREAM_SINGLESHOT:
- ccdc->stopping = CCDC_STOP_REQUEST;
- break;
-
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- if (ccdc->output & CCDC_OUTPUT_MEMORY) {
- if (ccdc->lsc.state != LSC_STATE_STOPPED)
- __ccdc_lsc_enable(ccdc, 0);
- __ccdc_enable(ccdc, 0);
- }
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- break;
- }
-
- if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1))
- goto done;
-
- if (ccdc->lsc.request == NULL)
- goto done;
-
- /*
- * LSC need to be reconfigured. Stop it here and on next LSC_DONE IRQ
- * do the appropriate changes in registers
- */
- if (ccdc->lsc.state == LSC_STATE_RUNNING) {
- __ccdc_lsc_enable(ccdc, 0);
- ccdc->lsc.state = LSC_STATE_RECONFIG;
- goto done;
- }
-
- /* LSC has been in STOPPED state, enable it */
- if (ccdc->lsc.state == LSC_STATE_STOPPED)
- ccdc_lsc_enable(ccdc);
-
-done:
- spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags);
-}
-
-/*
- * omap3isp_ccdc_isr - Configure CCDC during interframe time.
- * @ccdc: Pointer to ISP CCDC device.
- * @events: CCDC events
- */
-int omap3isp_ccdc_isr(struct isp_ccdc_device *ccdc, u32 events)
-{
- if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
-
- if (events & IRQ0STATUS_CCDC_VD1_IRQ)
- ccdc_vd1_isr(ccdc);
-
- ccdc_lsc_isr(ccdc, events);
-
- if (events & IRQ0STATUS_CCDC_VD0_IRQ)
- ccdc_vd0_isr(ccdc);
-
- if (events & IRQ0STATUS_HS_VS_IRQ)
- ccdc_hs_vs_isr(ccdc);
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * ISP video operations
- */
-
-static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer)
-{
- struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc;
-
- if (!(ccdc->output & CCDC_OUTPUT_MEMORY))
- return -ENODEV;
-
- ccdc_set_outaddr(ccdc, buffer->isp_addr);
-
- /* We now have a buffer queued on the output, restart the pipeline
- * on the next CCDC interrupt if running in continuous mode (or when
- * starting the stream).
- */
- ccdc->underrun = 1;
-
- return 0;
-}
-
-static const struct isp_video_operations ccdc_video_ops = {
- .queue = ccdc_video_queue,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev operations
- */
-
-/*
- * ccdc_ioctl - CCDC module private ioctl's
- * @sd: ISP CCDC V4L2 subdevice
- * @cmd: ioctl command
- * @arg: ioctl argument
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- int ret;
-
- switch (cmd) {
- case VIDIOC_OMAP3ISP_CCDC_CFG:
- mutex_lock(&ccdc->ioctl_lock);
- ret = ccdc_config(ccdc, arg);
- mutex_unlock(&ccdc->ioctl_lock);
- break;
-
- default:
- return -ENOIOCTLCMD;
- }
-
- return ret;
-}
-
-static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- if (sub->type != V4L2_EVENT_FRAME_SYNC)
- return -EINVAL;
-
- /* line number is zero at frame start */
- if (sub->id != 0)
- return -EINVAL;
-
- return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL);
-}
-
-static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- return v4l2_event_unsubscribe(fh, sub);
-}
-
-/*
- * ccdc_set_stream - Enable/Disable streaming on the CCDC module
- * @sd: ISP CCDC V4L2 subdevice
- * @enable: Enable/disable stream
- *
- * When writing to memory, the CCDC hardware can't be enabled without a memory
- * buffer to write to. As the s_stream operation is called in response to a
- * STREAMON call without any buffer queued yet, just update the enabled field
- * and return immediately. The CCDC will be enabled in ccdc_isr_buffer().
- *
- * When not writing to memory enable the CCDC immediately.
- */
-static int ccdc_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct isp_device *isp = to_isp_device(ccdc);
- int ret = 0;
-
- if (ccdc->state == ISP_PIPELINE_STREAM_STOPPED) {
- if (enable == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
-
- omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_CCDC);
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG,
- ISPCCDC_CFG_VDLC);
-
- ccdc_configure(ccdc);
-
- /* TODO: Don't configure the video port if all of its output
- * links are inactive.
- */
- ccdc_config_vp(ccdc);
- ccdc_enable_vp(ccdc, 1);
- ccdc_print_status(ccdc);
- }
-
- switch (enable) {
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- if (ccdc->output & CCDC_OUTPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
-
- if (ccdc->underrun || !(ccdc->output & CCDC_OUTPUT_MEMORY))
- ccdc_enable(ccdc);
-
- ccdc->underrun = 0;
- break;
-
- case ISP_PIPELINE_STREAM_SINGLESHOT:
- if (ccdc->output & CCDC_OUTPUT_MEMORY &&
- ccdc->state != ISP_PIPELINE_STREAM_SINGLESHOT)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
-
- ccdc_enable(ccdc);
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- ret = ccdc_disable(ccdc);
- if (ccdc->output & CCDC_OUTPUT_MEMORY)
- omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CCDC_WRITE);
- omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_CCDC);
- ccdc->underrun = 0;
- break;
- }
-
- ccdc->state = enable;
- return ret;
-}
-
-static struct v4l2_mbus_framefmt *
-__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(fh, pad);
- else
- return &ccdc->formats[pad];
-}
-
-static struct v4l2_rect *
-__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF);
- else
- return &ccdc->crop;
-}
-
-/*
- * ccdc_try_format - Try video format on a pad
- * @ccdc: ISP CCDC device
- * @fh : V4L2 subdev file handle
- * @pad: Pad number
- * @fmt: Format
- */
-static void
-ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
- unsigned int pad, struct v4l2_mbus_framefmt *fmt,
- enum v4l2_subdev_format_whence which)
-{
- struct v4l2_mbus_framefmt *format;
- const struct isp_format_info *info;
- unsigned int width = fmt->width;
- unsigned int height = fmt->height;
- struct v4l2_rect *crop;
- unsigned int i;
-
- switch (pad) {
- case CCDC_PAD_SINK:
- /* TODO: If the CCDC output formatter pad is connected directly
- * to the resizer, only YUV formats can be used.
- */
- for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) {
- if (fmt->code == ccdc_fmts[i])
- break;
- }
-
- /* If not found, use SGRBG10 as default */
- if (i >= ARRAY_SIZE(ccdc_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
-
- /* Clamp the input size. */
- fmt->width = clamp_t(u32, width, 32, 4096);
- fmt->height = clamp_t(u32, height, 32, 4096);
- break;
-
- case CCDC_PAD_SOURCE_OF:
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
- memcpy(fmt, format, sizeof(*fmt));
-
- /* Hardcode the output size to the crop rectangle size. */
- crop = __ccdc_get_crop(ccdc, fh, which);
- fmt->width = crop->width;
- fmt->height = crop->height;
- break;
-
- case CCDC_PAD_SOURCE_VP:
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
- memcpy(fmt, format, sizeof(*fmt));
-
- /* The video port interface truncates the data to 10 bits. */
- info = omap3isp_video_format_info(fmt->code);
- fmt->code = info->truncated;
-
- /* The number of lines that can be clocked out from the video
- * port output must be at least one line less than the number
- * of input lines.
- */
- fmt->width = clamp_t(u32, width, 32, fmt->width);
- fmt->height = clamp_t(u32, height, 32, fmt->height - 1);
- break;
- }
-
- /* Data is written to memory unpacked, each 10-bit or 12-bit pixel is
- * stored on 2 bytes.
- */
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
-}
-
-/*
- * ccdc_try_crop - Validate a crop rectangle
- * @ccdc: ISP CCDC device
- * @sink: format on the sink pad
- * @crop: crop rectangle to be validated
- */
-static void ccdc_try_crop(struct isp_ccdc_device *ccdc,
- const struct v4l2_mbus_framefmt *sink,
- struct v4l2_rect *crop)
-{
- const struct isp_format_info *info;
- unsigned int max_width;
-
- /* For Bayer formats, restrict left/top and width/height to even values
- * to keep the Bayer pattern.
- */
- info = omap3isp_video_format_info(sink->code);
- if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
- crop->left &= ~1;
- crop->top &= ~1;
- }
-
- crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH);
- crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT);
-
- /* The data formatter truncates the number of horizontal output pixels
- * to a multiple of 16. To avoid clipping data, allow callers to request
- * an output size bigger than the input size up to the nearest multiple
- * of 16.
- */
- max_width = (sink->width - crop->left + 15) & ~15;
- crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width)
- & ~15;
- crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT,
- sink->height - crop->top);
-
- /* Odd width/height values don't make sense for Bayer formats. */
- if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
- crop->width &= ~1;
- crop->height &= ~1;
- }
-}
-
-/*
- * ccdc_enum_mbus_code - Handle pixel format enumeration
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @code : pointer to v4l2_subdev_mbus_code_enum structure
- * return -EINVAL or zero on success
- */
-static int ccdc_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- switch (code->pad) {
- case CCDC_PAD_SINK:
- if (code->index >= ARRAY_SIZE(ccdc_fmts))
- return -EINVAL;
-
- code->code = ccdc_fmts[code->index];
- break;
-
- case CCDC_PAD_SOURCE_OF:
- case CCDC_PAD_SOURCE_VP:
- /* No format conversion inside CCDC */
- if (code->index != 0)
- return -EINVAL;
-
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK,
- V4L2_SUBDEV_FORMAT_TRY);
-
- code->code = format->code;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt format;
-
- if (fse->index != 0)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = 1;
- format.height = 1;
- ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->min_width = format.width;
- fse->min_height = format.height;
-
- if (format.code != fse->code)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = -1;
- format.height = -1;
- ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->max_width = format.width;
- fse->max_height = format.height;
-
- return 0;
-}
-
-/*
- * ccdc_get_selection - Retrieve a selection rectangle on a pad
- * @sd: ISP CCDC V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangles are the crop rectangles on the output formatter
- * source pad.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- if (sel->pad != CCDC_PAD_SOURCE_OF)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = INT_MAX;
- sel->r.height = INT_MAX;
-
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
- ccdc_try_crop(ccdc, format, &sel->r);
- break;
-
- case V4L2_SEL_TGT_CROP:
- sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * ccdc_set_selection - Set a selection rectangle on a pad
- * @sd: ISP CCDC V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangle is the actual crop rectangle on the output
- * formatter source pad.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- if (sel->target != V4L2_SEL_TGT_CROP ||
- sel->pad != CCDC_PAD_SOURCE_OF)
- return -EINVAL;
-
- /* The crop rectangle can't be changed while streaming. */
- if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
- return -EBUSY;
-
- /* Modifying the crop rectangle always changes the format on the source
- * pad. If the KEEP_CONFIG flag is set, just return the current crop
- * rectangle.
- */
- if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
- sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
- return 0;
- }
-
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
- ccdc_try_crop(ccdc, format, &sel->r);
- *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r;
-
- /* Update the source format. */
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which);
- ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which);
-
- return 0;
-}
-
-/*
- * ccdc_get_format - Retrieve the video format on a pad
- * @sd : ISP CCDC V4L2 subdevice
- * @fh : V4L2 subdev file handle
- * @fmt: Format
- *
- * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
- * to the format type.
- */
-static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- fmt->format = *format;
- return 0;
-}
-
-/*
- * ccdc_set_format - Set the video format on a pad
- * @sd : ISP CCDC V4L2 subdevice
- * @fh : V4L2 subdev file handle
- * @fmt: Format
- *
- * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond
- * to the format type.
- */
-static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which);
- *format = fmt->format;
-
- /* Propagate the format from sink to source */
- if (fmt->pad == CCDC_PAD_SINK) {
- /* Reset the crop rectangle. */
- crop = __ccdc_get_crop(ccdc, fh, fmt->which);
- crop->left = 0;
- crop->top = 0;
- crop->width = fmt->format.width;
- crop->height = fmt->format.height;
-
- ccdc_try_crop(ccdc, &fmt->format, crop);
-
- /* Update the source formats. */
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
- fmt->which);
- *format = fmt->format;
- ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format,
- fmt->which);
-
- format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP,
- fmt->which);
- *format = fmt->format;
- ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format,
- fmt->which);
- }
-
- return 0;
-}
-
-/*
- * Decide whether desired output pixel code can be obtained with
- * the lane shifter by shifting the input pixel code.
- * @in: input pixelcode to shifter
- * @out: output pixelcode from shifter
- * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
- *
- * return true if the combination is possible
- * return false otherwise
- */
-static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in,
- enum v4l2_mbus_pixelcode out,
- unsigned int additional_shift)
-{
- const struct isp_format_info *in_info, *out_info;
-
- if (in == out)
- return true;
-
- in_info = omap3isp_video_format_info(in);
- out_info = omap3isp_video_format_info(out);
-
- if ((in_info->flavor == 0) || (out_info->flavor == 0))
- return false;
-
- if (in_info->flavor != out_info->flavor)
- return false;
-
- return in_info->bpp - out_info->bpp + additional_shift <= 6;
-}
-
-static int ccdc_link_validate(struct v4l2_subdev *sd,
- struct media_link *link,
- struct v4l2_subdev_format *source_fmt,
- struct v4l2_subdev_format *sink_fmt)
-{
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- unsigned long parallel_shift;
-
- /* Check if the two ends match */
- if (source_fmt->format.width != sink_fmt->format.width ||
- source_fmt->format.height != sink_fmt->format.height)
- return -EPIPE;
-
- /* We've got a parallel sensor here. */
- if (ccdc->input == CCDC_INPUT_PARALLEL) {
- struct isp_parallel_platform_data *pdata =
- &((struct isp_v4l2_subdevs_group *)
- media_entity_to_v4l2_subdev(link->source->entity)
- ->host_priv)->bus.parallel;
- parallel_shift = pdata->data_lane_shift * 2;
- } else {
- parallel_shift = 0;
- }
-
- /* Lane shifter may be used to drop bits on CCDC sink pad */
- if (!ccdc_is_shiftable(source_fmt->format.code,
- sink_fmt->format.code, parallel_shift))
- return -EPIPE;
-
- return 0;
-}
-
-/*
- * ccdc_init_formats - Initialize formats on all pads
- * @sd: ISP CCDC V4L2 subdevice
- * @fh: V4L2 subdev file handle
- *
- * Initialize all pad formats with default values. If fh is not NULL, try
- * formats are initialized on the file handle. Otherwise active formats are
- * initialized on the device.
- */
-static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_subdev_format format;
-
- memset(&format, 0, sizeof(format));
- format.pad = CCDC_PAD_SINK;
- format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
- format.format.width = 4096;
- format.format.height = 4096;
- ccdc_set_format(sd, fh, &format);
-
- return 0;
-}
-
-/* V4L2 subdev core operations */
-static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = {
- .ioctl = ccdc_ioctl,
- .subscribe_event = ccdc_subscribe_event,
- .unsubscribe_event = ccdc_unsubscribe_event,
-};
-
-/* V4L2 subdev video operations */
-static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = {
- .s_stream = ccdc_set_stream,
-};
-
-/* V4L2 subdev pad operations */
-static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
- .enum_mbus_code = ccdc_enum_mbus_code,
- .enum_frame_size = ccdc_enum_frame_size,
- .get_fmt = ccdc_get_format,
- .set_fmt = ccdc_set_format,
- .get_selection = ccdc_get_selection,
- .set_selection = ccdc_set_selection,
- .link_validate = ccdc_link_validate,
-};
-
-/* V4L2 subdev operations */
-static const struct v4l2_subdev_ops ccdc_v4l2_ops = {
- .core = &ccdc_v4l2_core_ops,
- .video = &ccdc_v4l2_video_ops,
- .pad = &ccdc_v4l2_pad_ops,
-};
-
-/* V4L2 subdev internal operations */
-static const struct v4l2_subdev_internal_ops ccdc_v4l2_internal_ops = {
- .open = ccdc_init_formats,
-};
-
-/* -----------------------------------------------------------------------------
- * Media entity operations
- */
-
-/*
- * ccdc_link_setup - Setup CCDC connections
- * @entity: CCDC media entity
- * @local: Pad at the local end of the link
- * @remote: Pad at the remote end of the link
- * @flags: Link flags
- *
- * return -EINVAL or zero on success
- */
-static int ccdc_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
- struct isp_device *isp = to_isp_device(ccdc);
-
- switch (local->index | media_entity_type(remote->entity)) {
- case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
- /* Read from the sensor (parallel interface), CCP2, CSI2a or
- * CSI2c.
- */
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- ccdc->input = CCDC_INPUT_NONE;
- break;
- }
-
- if (ccdc->input != CCDC_INPUT_NONE)
- return -EBUSY;
-
- if (remote->entity == &isp->isp_ccp2.subdev.entity)
- ccdc->input = CCDC_INPUT_CCP2B;
- else if (remote->entity == &isp->isp_csi2a.subdev.entity)
- ccdc->input = CCDC_INPUT_CSI2A;
- else if (remote->entity == &isp->isp_csi2c.subdev.entity)
- ccdc->input = CCDC_INPUT_CSI2C;
- else
- ccdc->input = CCDC_INPUT_PARALLEL;
-
- break;
-
- /*
- * The ISP core doesn't support pipelines with multiple video outputs.
- * Revisit this when it will be implemented, and return -EBUSY for now.
- */
-
- case CCDC_PAD_SOURCE_VP | MEDIA_ENT_T_V4L2_SUBDEV:
- /* Write to preview engine, histogram and H3A. When none of
- * those links are active, the video port can be disabled.
- */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (ccdc->output & ~CCDC_OUTPUT_PREVIEW)
- return -EBUSY;
- ccdc->output |= CCDC_OUTPUT_PREVIEW;
- } else {
- ccdc->output &= ~CCDC_OUTPUT_PREVIEW;
- }
- break;
-
- case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_DEVNODE:
- /* Write to memory */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (ccdc->output & ~CCDC_OUTPUT_MEMORY)
- return -EBUSY;
- ccdc->output |= CCDC_OUTPUT_MEMORY;
- } else {
- ccdc->output &= ~CCDC_OUTPUT_MEMORY;
- }
- break;
-
- case CCDC_PAD_SOURCE_OF | MEDIA_ENT_T_V4L2_SUBDEV:
- /* Write to resizer */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (ccdc->output & ~CCDC_OUTPUT_RESIZER)
- return -EBUSY;
- ccdc->output |= CCDC_OUTPUT_RESIZER;
- } else {
- ccdc->output &= ~CCDC_OUTPUT_RESIZER;
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* media operations */
-static const struct media_entity_operations ccdc_media_ops = {
- .link_setup = ccdc_link_setup,
- .link_validate = v4l2_subdev_link_validate,
-};
-
-void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
-{
- v4l2_device_unregister_subdev(&ccdc->subdev);
- omap3isp_video_unregister(&ccdc->video_out);
-}
-
-int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video node. */
- ret = v4l2_device_register_subdev(vdev, &ccdc->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&ccdc->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_ccdc_unregister_entities(ccdc);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * ISP CCDC initialisation and cleanup
- */
-
-/*
- * ccdc_init_entities - Initialize V4L2 subdev and media entity
- * @ccdc: ISP CCDC module
- *
- * Return 0 on success and a negative error code on failure.
- */
-static int ccdc_init_entities(struct isp_ccdc_device *ccdc)
-{
- struct v4l2_subdev *sd = &ccdc->subdev;
- struct media_pad *pads = ccdc->pads;
- struct media_entity *me = &sd->entity;
- int ret;
-
- ccdc->input = CCDC_INPUT_NONE;
-
- v4l2_subdev_init(sd, &ccdc_v4l2_ops);
- sd->internal_ops = &ccdc_v4l2_internal_ops;
- strlcpy(sd->name, "OMAP3 ISP CCDC", sizeof(sd->name));
- sd->grp_id = 1 << 16; /* group ID for isp subdevs */
- v4l2_set_subdevdata(sd, ccdc);
- sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE;
- pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE;
-
- me->ops = &ccdc_media_ops;
- ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0);
- if (ret < 0)
- return ret;
-
- ccdc_init_formats(sd, NULL);
-
- ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- ccdc->video_out.ops = &ccdc_video_ops;
- ccdc->video_out.isp = to_isp_device(ccdc);
- ccdc->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
- ccdc->video_out.bpl_alignment = 32;
-
- ret = omap3isp_video_init(&ccdc->video_out, "CCDC");
- if (ret < 0)
- goto error_video;
-
- /* Connect the CCDC subdev to the video node. */
- ret = media_entity_create_link(&ccdc->subdev.entity, CCDC_PAD_SOURCE_OF,
- &ccdc->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
- return 0;
-
-error_link:
- omap3isp_video_cleanup(&ccdc->video_out);
-error_video:
- media_entity_cleanup(me);
- return ret;
-}
-
-/*
- * omap3isp_ccdc_init - CCDC module initialization.
- * @dev: Device pointer specific to the OMAP3 ISP.
- *
- * TODO: Get the initialisation values from platform data.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-int omap3isp_ccdc_init(struct isp_device *isp)
-{
- struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
- int ret;
-
- spin_lock_init(&ccdc->lock);
- init_waitqueue_head(&ccdc->wait);
- mutex_init(&ccdc->ioctl_lock);
-
- ccdc->stopping = CCDC_STOP_NOT_REQUESTED;
-
- INIT_WORK(&ccdc->lsc.table_work, ccdc_lsc_free_table_work);
- ccdc->lsc.state = LSC_STATE_STOPPED;
- INIT_LIST_HEAD(&ccdc->lsc.free_queue);
- spin_lock_init(&ccdc->lsc.req_lock);
-
- ccdc->syncif.ccdc_mastermode = 0;
- ccdc->syncif.datapol = 0;
- ccdc->syncif.datsz = 0;
- ccdc->syncif.fldmode = 0;
- ccdc->syncif.fldout = 0;
- ccdc->syncif.fldpol = 0;
- ccdc->syncif.fldstat = 0;
-
- ccdc->clamp.oblen = 0;
- ccdc->clamp.dcsubval = 0;
-
- ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
- ccdc_apply_controls(ccdc);
-
- ret = ccdc_init_entities(ccdc);
- if (ret < 0) {
- mutex_destroy(&ccdc->ioctl_lock);
- return ret;
- }
-
- return 0;
-}
-
-/*
- * omap3isp_ccdc_cleanup - CCDC module cleanup.
- * @dev: Device pointer specific to the OMAP3 ISP.
- */
-void omap3isp_ccdc_cleanup(struct isp_device *isp)
-{
- struct isp_ccdc_device *ccdc = &isp->isp_ccdc;
-
- omap3isp_video_cleanup(&ccdc->video_out);
- media_entity_cleanup(&ccdc->subdev.entity);
-
- /* Free LSC requests. As the CCDC is stopped there's no active request,
- * so only the pending request and the free queue need to be handled.
- */
- ccdc_lsc_free_request(ccdc, ccdc->lsc.request);
- cancel_work_sync(&ccdc->lsc.table_work);
- ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue);
-
- if (ccdc->fpc.fpcaddr != 0)
- omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr);
-
- mutex_destroy(&ccdc->ioctl_lock);
-}
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h
deleted file mode 100644
index 890f6b3a68fd..000000000000
--- a/drivers/media/video/omap3isp/ispccdc.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * ispccdc.h
- *
- * TI OMAP3 ISP - CCDC module
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_CCDC_H
-#define OMAP3_ISP_CCDC_H
-
-#include <linux/omap3isp.h>
-#include <linux/workqueue.h>
-
-#include "ispvideo.h"
-
-enum ccdc_input_entity {
- CCDC_INPUT_NONE,
- CCDC_INPUT_PARALLEL,
- CCDC_INPUT_CSI2A,
- CCDC_INPUT_CCP2B,
- CCDC_INPUT_CSI2C
-};
-
-#define CCDC_OUTPUT_MEMORY (1 << 0)
-#define CCDC_OUTPUT_PREVIEW (1 << 1)
-#define CCDC_OUTPUT_RESIZER (1 << 2)
-
-#define OMAP3ISP_CCDC_NEVENTS 16
-
-/*
- * struct ispccdc_syncif - Structure for Sync Interface between sensor and CCDC
- * @ccdc_mastermode: Master mode. 1 - Master, 0 - Slave.
- * @fldstat: Field state. 0 - Odd Field, 1 - Even Field.
- * @datsz: Data size.
- * @fldmode: 0 - Progressive, 1 - Interlaced.
- * @datapol: 0 - Positive, 1 - Negative.
- * @fldpol: 0 - Positive, 1 - Negative.
- * @hdpol: 0 - Positive, 1 - Negative.
- * @vdpol: 0 - Positive, 1 - Negative.
- * @fldout: 0 - Input, 1 - Output.
- * @hs_width: Width of the Horizontal Sync pulse, used for HS/VS Output.
- * @vs_width: Width of the Vertical Sync pulse, used for HS/VS Output.
- * @ppln: Number of pixels per line, used for HS/VS Output.
- * @hlprf: Number of half lines per frame, used for HS/VS Output.
- * @bt_r656_en: 1 - Enable ITU-R BT656 mode, 0 - Sync mode.
- */
-struct ispccdc_syncif {
- u8 ccdc_mastermode;
- u8 fldstat;
- u8 datsz;
- u8 fldmode;
- u8 datapol;
- u8 fldpol;
- u8 hdpol;
- u8 vdpol;
- u8 fldout;
- u8 hs_width;
- u8 vs_width;
- u8 ppln;
- u8 hlprf;
- u8 bt_r656_en;
-};
-
-enum ispccdc_lsc_state {
- LSC_STATE_STOPPED = 0,
- LSC_STATE_STOPPING = 1,
- LSC_STATE_RUNNING = 2,
- LSC_STATE_RECONFIG = 3,
-};
-
-struct ispccdc_lsc_config_req {
- struct list_head list;
- struct omap3isp_ccdc_lsc_config config;
- unsigned char enable;
- u32 table;
- struct iovm_struct *iovm;
-};
-
-/*
- * ispccdc_lsc - CCDC LSC parameters
- * @update_config: Set when user changes config
- * @request_enable: Whether LSC is requested to be enabled
- * @config: LSC config set by user
- * @update_table: Set when user provides a new LSC table to table_new
- * @table_new: LSC table set by user, ISP address
- * @table_inuse: LSC table currently in use, ISP address
- */
-struct ispccdc_lsc {
- enum ispccdc_lsc_state state;
- struct work_struct table_work;
-
- /* LSC queue of configurations */
- spinlock_t req_lock;
- struct ispccdc_lsc_config_req *request; /* requested configuration */
- struct ispccdc_lsc_config_req *active; /* active configuration */
- struct list_head free_queue; /* configurations for freeing */
-};
-
-#define CCDC_STOP_NOT_REQUESTED 0x00
-#define CCDC_STOP_REQUEST 0x01
-#define CCDC_STOP_EXECUTED (0x02 | CCDC_STOP_REQUEST)
-#define CCDC_STOP_CCDC_FINISHED 0x04
-#define CCDC_STOP_LSC_FINISHED 0x08
-#define CCDC_STOP_FINISHED \
- (CCDC_STOP_EXECUTED | CCDC_STOP_CCDC_FINISHED | CCDC_STOP_LSC_FINISHED)
-
-#define CCDC_EVENT_VD1 0x10
-#define CCDC_EVENT_VD0 0x20
-#define CCDC_EVENT_LSC_DONE 0x40
-
-/* Sink and source CCDC pads */
-#define CCDC_PAD_SINK 0
-#define CCDC_PAD_SOURCE_OF 1
-#define CCDC_PAD_SOURCE_VP 2
-#define CCDC_PADS_NUM 3
-
-/*
- * struct isp_ccdc_device - Structure for the CCDC module to store its own
- * information
- * @subdev: V4L2 subdevice
- * @pads: Sink and source media entity pads
- * @formats: Active video formats
- * @crop: Active crop rectangle on the OF source pad
- * @input: Active input
- * @output: Active outputs
- * @video_out: Output video node
- * @alaw: A-law compression enabled (1) or disabled (0)
- * @lpf: Low pass filter enabled (1) or disabled (0)
- * @obclamp: Optical-black clamp enabled (1) or disabled (0)
- * @fpc_en: Faulty pixels correction enabled (1) or disabled (0)
- * @blcomp: Black level compensation configuration
- * @clamp: Optical-black or digital clamp configuration
- * @fpc: Faulty pixels correction configuration
- * @lsc: Lens shading compensation configuration
- * @update: Bitmask of controls to update during the next interrupt
- * @shadow_update: Controls update in progress by userspace
- * @syncif: Interface synchronization configuration
- * @underrun: A buffer underrun occurred and a new buffer has been queued
- * @state: Streaming state
- * @lock: Serializes shadow_update with interrupt handler
- * @wait: Wait queue used to stop the module
- * @stopping: Stopping state
- * @ioctl_lock: Serializes ioctl calls and LSC requests freeing
- */
-struct isp_ccdc_device {
- struct v4l2_subdev subdev;
- struct media_pad pads[CCDC_PADS_NUM];
- struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM];
- struct v4l2_rect crop;
-
- enum ccdc_input_entity input;
- unsigned int output;
- struct isp_video video_out;
-
- unsigned int alaw:1,
- lpf:1,
- obclamp:1,
- fpc_en:1;
- struct omap3isp_ccdc_blcomp blcomp;
- struct omap3isp_ccdc_bclamp clamp;
- struct omap3isp_ccdc_fpc fpc;
- struct ispccdc_lsc lsc;
- unsigned int update;
- unsigned int shadow_update;
-
- struct ispccdc_syncif syncif;
-
- unsigned int underrun:1;
- enum isp_pipeline_stream_state state;
- spinlock_t lock;
- wait_queue_head_t wait;
- unsigned int stopping;
- struct mutex ioctl_lock;
-};
-
-struct isp_device;
-
-int omap3isp_ccdc_init(struct isp_device *isp);
-void omap3isp_ccdc_cleanup(struct isp_device *isp);
-int omap3isp_ccdc_register_entities(struct isp_ccdc_device *ccdc,
- struct v4l2_device *vdev);
-void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc);
-
-int omap3isp_ccdc_busy(struct isp_ccdc_device *isp_ccdc);
-int omap3isp_ccdc_isr(struct isp_ccdc_device *isp_ccdc, u32 events);
-void omap3isp_ccdc_restore_context(struct isp_device *isp);
-void omap3isp_ccdc_max_rate(struct isp_ccdc_device *ccdc,
- unsigned int *max_rate);
-
-#endif /* OMAP3_ISP_CCDC_H */
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
deleted file mode 100644
index a1724362b6d5..000000000000
--- a/drivers/media/video/omap3isp/ispcsi2.c
+++ /dev/null
@@ -1,1305 +0,0 @@
-/*
- * ispcsi2.c
- *
- * TI OMAP3 ISP - CSI2 module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-#include <linux/delay.h>
-#include <media/v4l2-common.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/mm.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "ispcsi2.h"
-
-/*
- * csi2_if_enable - Enable CSI2 Receiver interface.
- * @enable: enable flag
- *
- */
-static void csi2_if_enable(struct isp_device *isp,
- struct isp_csi2_device *csi2, u8 enable)
-{
- struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl;
-
- isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN,
- enable ? ISPCSI2_CTRL_IF_EN : 0);
-
- currctrl->if_enable = enable;
-}
-
-/*
- * csi2_recv_config - CSI2 receiver module configuration.
- * @currctrl: isp_csi2_ctrl_cfg structure
- *
- */
-static void csi2_recv_config(struct isp_device *isp,
- struct isp_csi2_device *csi2,
- struct isp_csi2_ctrl_cfg *currctrl)
-{
- u32 reg;
-
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL);
-
- if (currctrl->frame_mode)
- reg |= ISPCSI2_CTRL_FRAME;
- else
- reg &= ~ISPCSI2_CTRL_FRAME;
-
- if (currctrl->vp_clk_enable)
- reg |= ISPCSI2_CTRL_VP_CLK_EN;
- else
- reg &= ~ISPCSI2_CTRL_VP_CLK_EN;
-
- if (currctrl->vp_only_enable)
- reg |= ISPCSI2_CTRL_VP_ONLY_EN;
- else
- reg &= ~ISPCSI2_CTRL_VP_ONLY_EN;
-
- reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK;
- reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT;
-
- if (currctrl->ecc_enable)
- reg |= ISPCSI2_CTRL_ECC_EN;
- else
- reg &= ~ISPCSI2_CTRL_ECC_EN;
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL);
-}
-
-static const unsigned int csi2_input_fmts[] = {
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
-};
-
-/* To set the format on the CSI2 requires a mapping function that takes
- * the following inputs:
- * - 2 different formats (at this time)
- * - 2 destinations (mem, vp+mem) (vp only handled separately)
- * - 2 decompression options (on, off)
- * - 2 isp revisions (certain format must be handled differently on OMAP3630)
- * Output should be CSI2 frame format code
- * Array indices as follows: [format][dest][decompr][is_3630]
- * Not all combinations are valid. 0 means invalid.
- */
-static const u16 __csi2_fmt_map[2][2][2][2] = {
- /* RAW10 formats */
- {
- /* Output to memory */
- {
- /* No DPCM decompression */
- { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 },
- /* DPCM decompression */
- { 0, 0 },
- },
- /* Output to both */
- {
- /* No DPCM decompression */
- { CSI2_PIX_FMT_RAW10_EXP16_VP,
- CSI2_PIX_FMT_RAW10_EXP16_VP },
- /* DPCM decompression */
- { 0, 0 },
- },
- },
- /* RAW10 DPCM8 formats */
- {
- /* Output to memory */
- {
- /* No DPCM decompression */
- { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 },
- /* DPCM decompression */
- { CSI2_PIX_FMT_RAW8_DPCM10_EXP16,
- CSI2_USERDEF_8BIT_DATA1_DPCM10 },
- },
- /* Output to both */
- {
- /* No DPCM decompression */
- { CSI2_PIX_FMT_RAW8_VP,
- CSI2_PIX_FMT_RAW8_VP },
- /* DPCM decompression */
- { CSI2_PIX_FMT_RAW8_DPCM10_VP,
- CSI2_USERDEF_8BIT_DATA1_DPCM10_VP },
- },
- },
-};
-
-/*
- * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID
- * @csi2: ISP CSI2 device
- *
- * Returns CSI2 physical format id
- */
-static u16 csi2_ctx_map_format(struct isp_csi2_device *csi2)
-{
- const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK];
- int fmtidx, destidx, is_3630;
-
- switch (fmt->code) {
- case V4L2_MBUS_FMT_SGRBG10_1X10:
- case V4L2_MBUS_FMT_SRGGB10_1X10:
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- case V4L2_MBUS_FMT_SGBRG10_1X10:
- fmtidx = 0;
- break;
- case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8:
- case V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8:
- fmtidx = 1;
- break;
- default:
- WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n",
- fmt->code);
- return 0;
- }
-
- if (!(csi2->output & CSI2_OUTPUT_CCDC) &&
- !(csi2->output & CSI2_OUTPUT_MEMORY)) {
- /* Neither output enabled is a valid combination */
- return CSI2_PIX_FMT_OTHERS;
- }
-
- /* If we need to skip frames at the beginning of the stream disable the
- * video port to avoid sending the skipped frames to the CCDC.
- */
- destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC);
- is_3630 = csi2->isp->revision == ISP_REVISION_15_0;
-
- return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630];
-}
-
-/*
- * csi2_set_outaddr - Set memory address to save output image
- * @csi2: Pointer to ISP CSI2a device.
- * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary.
- *
- * Sets the memory address where the output will be saved.
- *
- * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte
- * boundary.
- */
-static void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr)
-{
- struct isp_device *isp = csi2->isp;
- struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0];
-
- ctx->ping_addr = addr;
- ctx->pong_addr = addr;
- isp_reg_writel(isp, ctx->ping_addr,
- csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
- isp_reg_writel(isp, ctx->pong_addr,
- csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
-}
-
-/*
- * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should
- * be enabled by CSI2.
- * @format_id: mapped format id
- *
- */
-static inline int is_usr_def_mapping(u32 format_id)
-{
- return (format_id & 0x40) ? 1 : 0;
-}
-
-/*
- * csi2_ctx_enable - Enable specified CSI2 context
- * @ctxnum: Context number, valid between 0 and 7 values.
- * @enable: enable
- *
- */
-static void csi2_ctx_enable(struct isp_device *isp,
- struct isp_csi2_device *csi2, u8 ctxnum, u8 enable)
-{
- struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum];
- unsigned int skip = 0;
- u32 reg;
-
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
-
- if (enable) {
- if (csi2->frame_skip)
- skip = csi2->frame_skip;
- else if (csi2->output & CSI2_OUTPUT_MEMORY)
- skip = 1;
-
- reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK;
- reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK
- | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT)
- | ISPCSI2_CTX_CTRL1_CTX_EN;
- } else {
- reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN;
- }
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum));
- ctx->enabled = enable;
-}
-
-/*
- * csi2_ctx_config - CSI2 context configuration.
- * @ctx: context configuration
- *
- */
-static void csi2_ctx_config(struct isp_device *isp,
- struct isp_csi2_device *csi2,
- struct isp_csi2_ctx_cfg *ctx)
-{
- u32 reg;
-
- /* Set up CSI2_CTx_CTRL1 */
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
-
- if (ctx->eof_enabled)
- reg |= ISPCSI2_CTX_CTRL1_EOF_EN;
- else
- reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN;
-
- if (ctx->eol_enabled)
- reg |= ISPCSI2_CTX_CTRL1_EOL_EN;
- else
- reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN;
-
- if (ctx->checksum_enabled)
- reg |= ISPCSI2_CTX_CTRL1_CS_EN;
- else
- reg &= ~ISPCSI2_CTX_CTRL1_CS_EN;
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum));
-
- /* Set up CSI2_CTx_CTRL2 */
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
-
- reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK);
- reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT;
-
- reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK);
- reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT;
-
- if (ctx->dpcm_decompress) {
- if (ctx->dpcm_predictor)
- reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED;
- else
- reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED;
- }
-
- if (is_usr_def_mapping(ctx->format_id)) {
- reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK;
- reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT;
- }
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum));
-
- /* Set up CSI2_CTx_CTRL3 */
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
- reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK);
- reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT);
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum));
-
- /* Set up CSI2_CTx_DAT_OFST */
- reg = isp_reg_readl(isp, csi2->regs1,
- ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
- reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK;
- reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT;
- isp_reg_writel(isp, reg, csi2->regs1,
- ISPCSI2_CTX_DAT_OFST(ctx->ctxnum));
-
- isp_reg_writel(isp, ctx->ping_addr,
- csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum));
-
- isp_reg_writel(isp, ctx->pong_addr,
- csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum));
-}
-
-/*
- * csi2_timing_config - CSI2 timing configuration.
- * @timing: csi2_timing_cfg structure
- */
-static void csi2_timing_config(struct isp_device *isp,
- struct isp_csi2_device *csi2,
- struct isp_csi2_timing_cfg *timing)
-{
- u32 reg;
-
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING);
-
- if (timing->force_rx_mode)
- reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
- else
- reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum);
-
- if (timing->stop_state_16x)
- reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
- else
- reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum);
-
- if (timing->stop_state_4x)
- reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
- else
- reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum);
-
- reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum);
- reg |= timing->stop_state_counter <<
- ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum);
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING);
-}
-
-/*
- * csi2_irq_ctx_set - Enables CSI2 Context IRQs.
- * @enable: Enable/disable CSI2 Context interrupts
- */
-static void csi2_irq_ctx_set(struct isp_device *isp,
- struct isp_csi2_device *csi2, int enable)
-{
- int i;
-
- for (i = 0; i < 8; i++) {
- isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1,
- ISPCSI2_CTX_IRQSTATUS(i));
- if (enable)
- isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
- ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
- else
- isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
- ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
- }
-}
-
-/*
- * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs.
- * @enable: Enable/disable CSI2 ComplexIO #1 interrupts
- */
-static void csi2_irq_complexio1_set(struct isp_device *isp,
- struct isp_csi2_device *csi2, int enable)
-{
- u32 reg;
- reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT |
- ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER |
- ISPCSI2_PHY_IRQENABLE_STATEULPM5 |
- ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 |
- ISPCSI2_PHY_IRQENABLE_ERRESC5 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 |
- ISPCSI2_PHY_IRQENABLE_STATEULPM4 |
- ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 |
- ISPCSI2_PHY_IRQENABLE_ERRESC4 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 |
- ISPCSI2_PHY_IRQENABLE_STATEULPM3 |
- ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 |
- ISPCSI2_PHY_IRQENABLE_ERRESC3 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 |
- ISPCSI2_PHY_IRQENABLE_STATEULPM2 |
- ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 |
- ISPCSI2_PHY_IRQENABLE_ERRESC2 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 |
- ISPCSI2_PHY_IRQENABLE_STATEULPM1 |
- ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 |
- ISPCSI2_PHY_IRQENABLE_ERRESC1 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 |
- ISPCSI2_PHY_IRQENABLE_ERRSOTHS1;
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
- if (enable)
- reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
- else
- reg = 0;
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE);
-}
-
-/*
- * csi2_irq_status_set - Enables CSI2 Status IRQs.
- * @enable: Enable/disable CSI2 Status interrupts
- */
-static void csi2_irq_status_set(struct isp_device *isp,
- struct isp_csi2_device *csi2, int enable)
-{
- u32 reg;
- reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
- ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
- ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ |
- ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
- ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
- ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ |
- ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ |
- ISPCSI2_IRQSTATUS_CONTEXT(0);
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS);
- if (enable)
- reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE);
- else
- reg = 0;
-
- isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE);
-}
-
-/*
- * omap3isp_csi2_reset - Resets the CSI2 module.
- *
- * Must be called with the phy lock held.
- *
- * Returns 0 if successful, or -EBUSY if power command didn't respond.
- */
-int omap3isp_csi2_reset(struct isp_csi2_device *csi2)
-{
- struct isp_device *isp = csi2->isp;
- u8 soft_reset_retries = 0;
- u32 reg;
- int i;
-
- if (!csi2->available)
- return -ENODEV;
-
- if (csi2->phy->phy_in_use)
- return -EBUSY;
-
- isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
- ISPCSI2_SYSCONFIG_SOFT_RESET);
-
- do {
- reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) &
- ISPCSI2_SYSSTATUS_RESET_DONE;
- if (reg == ISPCSI2_SYSSTATUS_RESET_DONE)
- break;
- soft_reset_retries++;
- if (soft_reset_retries < 5)
- udelay(100);
- } while (soft_reset_retries < 5);
-
- if (soft_reset_retries == 5) {
- printk(KERN_ERR "CSI2: Soft reset try count exceeded!\n");
- return -EBUSY;
- }
-
- if (isp->revision == ISP_REVISION_15_0)
- isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG,
- ISPCSI2_PHY_CFG_RESET_CTRL);
-
- i = 100;
- do {
- reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1)
- & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK;
- if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK)
- break;
- udelay(100);
- } while (--i > 0);
-
- if (i == 0) {
- printk(KERN_ERR
- "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n");
- return -EBUSY;
- }
-
- if (isp->autoidle)
- isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
- ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
- ISPCSI2_SYSCONFIG_AUTO_IDLE,
- ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART |
- ((isp->revision == ISP_REVISION_15_0) ?
- ISPCSI2_SYSCONFIG_AUTO_IDLE : 0));
- else
- isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG,
- ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK |
- ISPCSI2_SYSCONFIG_AUTO_IDLE,
- ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO);
-
- return 0;
-}
-
-static int csi2_configure(struct isp_csi2_device *csi2)
-{
- const struct isp_v4l2_subdevs_group *pdata;
- struct isp_device *isp = csi2->isp;
- struct isp_csi2_timing_cfg *timing = &csi2->timing[0];
- struct v4l2_subdev *sensor;
- struct media_pad *pad;
-
- /*
- * CSI2 fields that can be updated while the context has
- * been enabled or the interface has been enabled are not
- * updated dynamically currently. So we do not allow to
- * reconfigure if either has been enabled
- */
- if (csi2->contexts[0].enabled || csi2->ctrl.if_enable)
- return -EBUSY;
-
- pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]);
- sensor = media_entity_to_v4l2_subdev(pad->entity);
- pdata = sensor->host_priv;
-
- csi2->frame_skip = 0;
- v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip);
-
- csi2->ctrl.vp_out_ctrl = pdata->bus.csi2.vpclk_div;
- csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE;
- csi2->ctrl.ecc_enable = pdata->bus.csi2.crc;
-
- timing->ionum = 1;
- timing->force_rx_mode = 1;
- timing->stop_state_16x = 1;
- timing->stop_state_4x = 1;
- timing->stop_state_counter = 0x1FF;
-
- /*
- * The CSI2 receiver can't do any format conversion except DPCM
- * decompression, so every set_format call configures both pads
- * and enables DPCM decompression as a special case:
- */
- if (csi2->formats[CSI2_PAD_SINK].code !=
- csi2->formats[CSI2_PAD_SOURCE].code)
- csi2->dpcm_decompress = true;
- else
- csi2->dpcm_decompress = false;
-
- csi2->contexts[0].format_id = csi2_ctx_map_format(csi2);
-
- if (csi2->video_out.bpl_padding == 0)
- csi2->contexts[0].data_offset = 0;
- else
- csi2->contexts[0].data_offset = csi2->video_out.bpl_value;
-
- /*
- * Enable end of frame and end of line signals generation for
- * context 0. These signals are generated from CSI2 receiver to
- * qualify the last pixel of a frame and the last pixel of a line.
- * Without enabling the signals CSI2 receiver writes data to memory
- * beyond buffer size and/or data line offset is not handled correctly.
- */
- csi2->contexts[0].eof_enabled = 1;
- csi2->contexts[0].eol_enabled = 1;
-
- csi2_irq_complexio1_set(isp, csi2, 1);
- csi2_irq_ctx_set(isp, csi2, 1);
- csi2_irq_status_set(isp, csi2, 1);
-
- /* Set configuration (timings, format and links) */
- csi2_timing_config(isp, csi2, timing);
- csi2_recv_config(isp, csi2, &csi2->ctrl);
- csi2_ctx_config(isp, csi2, &csi2->contexts[0]);
-
- return 0;
-}
-
-/*
- * csi2_print_status - Prints CSI2 debug information.
- */
-#define CSI2_PRINT_REGISTER(isp, regs, name)\
- dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \
- isp_reg_readl(isp, regs, ISPCSI2_##name))
-
-static void csi2_print_status(struct isp_csi2_device *csi2)
-{
- struct isp_device *isp = csi2->isp;
-
- if (!csi2->available)
- return;
-
- dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n");
-
- CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING);
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0));
- CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0));
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
-/* -----------------------------------------------------------------------------
- * Interrupt handling
- */
-
-/*
- * csi2_isr_buffer - Does buffer handling at end-of-frame
- * when writing to memory.
- */
-static void csi2_isr_buffer(struct isp_csi2_device *csi2)
-{
- struct isp_device *isp = csi2->isp;
- struct isp_buffer *buffer;
-
- csi2_ctx_enable(isp, csi2, 0, 0);
-
- buffer = omap3isp_video_buffer_next(&csi2->video_out);
-
- /*
- * Let video queue operation restart engine if there is an underrun
- * condition.
- */
- if (buffer == NULL)
- return;
-
- csi2_set_outaddr(csi2, buffer->isp_addr);
- csi2_ctx_enable(isp, csi2, 0, 1);
-}
-
-static void csi2_isr_ctx(struct isp_csi2_device *csi2,
- struct isp_csi2_ctx_cfg *ctx)
-{
- struct isp_device *isp = csi2->isp;
- unsigned int n = ctx->ctxnum;
- u32 status;
-
- status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
- isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
-
- if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ))
- return;
-
- /* Skip interrupts until we reach the frame skip count. The CSI2 will be
- * automatically disabled, as the frame skip count has been programmed
- * in the CSI2_CTx_CTRL1::COUNT field, so reenable it.
- *
- * It would have been nice to rely on the FRAME_NUMBER interrupt instead
- * but it turned out that the interrupt is only generated when the CSI2
- * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased
- * correctly and reaches 0 when data is forwarded to the video port only
- * but no interrupt arrives). Maybe a CSI2 hardware bug.
- */
- if (csi2->frame_skip) {
- csi2->frame_skip--;
- if (csi2->frame_skip == 0) {
- ctx->format_id = csi2_ctx_map_format(csi2);
- csi2_ctx_config(isp, csi2, ctx);
- csi2_ctx_enable(isp, csi2, n, 1);
- }
- return;
- }
-
- if (csi2->output & CSI2_OUTPUT_MEMORY)
- csi2_isr_buffer(csi2);
-}
-
-/*
- * omap3isp_csi2_isr - CSI2 interrupt handling.
- */
-void omap3isp_csi2_isr(struct isp_csi2_device *csi2)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
- u32 csi2_irqstatus, cpxio1_irqstatus;
- struct isp_device *isp = csi2->isp;
-
- if (!csi2->available)
- return;
-
- csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS);
- isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS);
-
- /* Failure Cases */
- if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) {
- cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1,
- ISPCSI2_PHY_IRQSTATUS);
- isp_reg_writel(isp, cpxio1_irqstatus,
- csi2->regs1, ISPCSI2_PHY_IRQSTATUS);
- dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ "
- "%x\n", cpxio1_irqstatus);
- pipe->error = true;
- }
-
- if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ |
- ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ |
- ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ |
- ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ |
- ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) {
- dev_dbg(isp->dev, "CSI2 Err:"
- " OCP:%d,"
- " Short_pack:%d,"
- " ECC:%d,"
- " CPXIO2:%d,"
- " FIFO_OVF:%d,"
- "\n",
- (csi2_irqstatus &
- ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0,
- (csi2_irqstatus &
- ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0,
- (csi2_irqstatus &
- ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0,
- (csi2_irqstatus &
- ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0,
- (csi2_irqstatus &
- ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0);
- pipe->error = true;
- }
-
- if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping))
- return;
-
- /* Successful cases */
- if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0))
- csi2_isr_ctx(csi2, &csi2->contexts[0]);
-
- if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ)
- dev_dbg(isp->dev, "CSI2: ECC correction done\n");
-}
-
-/* -----------------------------------------------------------------------------
- * ISP video operations
- */
-
-/*
- * csi2_queue - Queues the first buffer when using memory output
- * @video: The video node
- * @buffer: buffer to queue
- */
-static int csi2_queue(struct isp_video *video, struct isp_buffer *buffer)
-{
- struct isp_device *isp = video->isp;
- struct isp_csi2_device *csi2 = &isp->isp_csi2a;
-
- csi2_set_outaddr(csi2, buffer->isp_addr);
-
- /*
- * If streaming was enabled before there was a buffer queued
- * or underrun happened in the ISR, the hardware was not enabled
- * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set.
- * Enable it now.
- */
- if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) {
- /* Enable / disable context 0 and IRQs */
- csi2_if_enable(isp, csi2, 1);
- csi2_ctx_enable(isp, csi2, 0, 1);
- isp_video_dmaqueue_flags_clr(&csi2->video_out);
- }
-
- return 0;
-}
-
-static const struct isp_video_operations csi2_ispvideo_ops = {
- .queue = csi2_queue,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev operations
- */
-
-static struct v4l2_mbus_framefmt *
-__csi2_get_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(fh, pad);
- else
- return &csi2->formats[pad];
-}
-
-static void
-csi2_try_format(struct isp_csi2_device *csi2, struct v4l2_subdev_fh *fh,
- unsigned int pad, struct v4l2_mbus_framefmt *fmt,
- enum v4l2_subdev_format_whence which)
-{
- enum v4l2_mbus_pixelcode pixelcode;
- struct v4l2_mbus_framefmt *format;
- const struct isp_format_info *info;
- unsigned int i;
-
- switch (pad) {
- case CSI2_PAD_SINK:
- /* Clamp the width and height to valid range (1-8191). */
- for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) {
- if (fmt->code == csi2_input_fmts[i])
- break;
- }
-
- /* If not found, use SGRBG10 as default */
- if (i >= ARRAY_SIZE(csi2_input_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
-
- fmt->width = clamp_t(u32, fmt->width, 1, 8191);
- fmt->height = clamp_t(u32, fmt->height, 1, 8191);
- break;
-
- case CSI2_PAD_SOURCE:
- /* Source format same as sink format, except for DPCM
- * compression.
- */
- pixelcode = fmt->code;
- format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK, which);
- memcpy(fmt, format, sizeof(*fmt));
-
- /*
- * Only Allow DPCM decompression, and check that the
- * pattern is preserved
- */
- info = omap3isp_video_format_info(fmt->code);
- if (info->uncompressed == pixelcode)
- fmt->code = pixelcode;
- break;
- }
-
- /* RGB, non-interlaced */
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- fmt->field = V4L2_FIELD_NONE;
-}
-
-/*
- * csi2_enum_mbus_code - Handle pixel format enumeration
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @code : pointer to v4l2_subdev_mbus_code_enum structure
- * return -EINVAL or zero on success
- */
-static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
- const struct isp_format_info *info;
-
- if (code->pad == CSI2_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(csi2_input_fmts))
- return -EINVAL;
-
- code->code = csi2_input_fmts[code->index];
- } else {
- format = __csi2_get_format(csi2, fh, CSI2_PAD_SINK,
- V4L2_SUBDEV_FORMAT_TRY);
- switch (code->index) {
- case 0:
- /* Passthrough sink pad code */
- code->code = format->code;
- break;
- case 1:
- /* Uncompressed code */
- info = omap3isp_video_format_info(format->code);
- if (info->uncompressed == format->code)
- return -EINVAL;
-
- code->code = info->uncompressed;
- break;
- default:
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int csi2_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt format;
-
- if (fse->index != 0)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = 1;
- format.height = 1;
- csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->min_width = format.width;
- fse->min_height = format.height;
-
- if (format.code != fse->code)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = -1;
- format.height = -1;
- csi2_try_format(csi2, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->max_width = format.width;
- fse->max_height = format.height;
-
- return 0;
-}
-
-/*
- * csi2_get_format - Handle get format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt: pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int csi2_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- fmt->format = *format;
- return 0;
-}
-
-/*
- * csi2_set_format - Handle set format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt: pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int csi2_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __csi2_get_format(csi2, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- csi2_try_format(csi2, fh, fmt->pad, &fmt->format, fmt->which);
- *format = fmt->format;
-
- /* Propagate the format from sink to source */
- if (fmt->pad == CSI2_PAD_SINK) {
- format = __csi2_get_format(csi2, fh, CSI2_PAD_SOURCE,
- fmt->which);
- *format = fmt->format;
- csi2_try_format(csi2, fh, CSI2_PAD_SOURCE, format, fmt->which);
- }
-
- return 0;
-}
-
-/*
- * csi2_init_formats - Initialize formats on all pads
- * @sd: ISP CSI2 V4L2 subdevice
- * @fh: V4L2 subdev file handle
- *
- * Initialize all pad formats with default values. If fh is not NULL, try
- * formats are initialized on the file handle. Otherwise active formats are
- * initialized on the device.
- */
-static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_subdev_format format;
-
- memset(&format, 0, sizeof(format));
- format.pad = CSI2_PAD_SINK;
- format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
- format.format.width = 4096;
- format.format.height = 4096;
- csi2_set_format(sd, fh, &format);
-
- return 0;
-}
-
-/*
- * csi2_set_stream - Enable/Disable streaming on the CSI2 module
- * @sd: ISP CSI2 V4L2 subdevice
- * @enable: ISP pipeline stream state
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct isp_device *isp = csi2->isp;
- struct isp_video *video_out = &csi2->video_out;
-
- switch (enable) {
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- if (omap3isp_csiphy_acquire(csi2->phy) < 0)
- return -ENODEV;
- if (csi2->output & CSI2_OUTPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
- csi2_configure(csi2);
- csi2_print_status(csi2);
-
- /*
- * When outputting to memory with no buffer available, let the
- * buffer queue handler start the hardware. A DMA queue flag
- * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is
- * a buffer available.
- */
- if (csi2->output & CSI2_OUTPUT_MEMORY &&
- !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED))
- break;
- /* Enable context 0 and IRQs */
- atomic_set(&csi2->stopping, 0);
- csi2_ctx_enable(isp, csi2, 0, 1);
- csi2_if_enable(isp, csi2, 1);
- isp_video_dmaqueue_flags_clr(video_out);
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- if (csi2->state == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
- if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait,
- &csi2->stopping))
- dev_dbg(isp->dev, "%s: module stop timeout.\n",
- sd->name);
- csi2_ctx_enable(isp, csi2, 0, 0);
- csi2_if_enable(isp, csi2, 0);
- csi2_irq_ctx_set(isp, csi2, 0);
- omap3isp_csiphy_release(csi2->phy);
- isp_video_dmaqueue_flags_clr(video_out);
- omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
- break;
- }
-
- csi2->state = enable;
- return 0;
-}
-
-/* subdev video operations */
-static const struct v4l2_subdev_video_ops csi2_video_ops = {
- .s_stream = csi2_set_stream,
-};
-
-/* subdev pad operations */
-static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
- .enum_mbus_code = csi2_enum_mbus_code,
- .enum_frame_size = csi2_enum_frame_size,
- .get_fmt = csi2_get_format,
- .set_fmt = csi2_set_format,
-};
-
-/* subdev operations */
-static const struct v4l2_subdev_ops csi2_ops = {
- .video = &csi2_video_ops,
- .pad = &csi2_pad_ops,
-};
-
-/* subdev internal operations */
-static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
- .open = csi2_init_formats,
-};
-
-/* -----------------------------------------------------------------------------
- * Media entity operations
- */
-
-/*
- * csi2_link_setup - Setup CSI2 connections.
- * @entity : Pointer to media entity structure
- * @local : Pointer to local pad array
- * @remote : Pointer to remote pad array
- * @flags : Link flags
- * return -EINVAL or zero on success
- */
-static int csi2_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
- struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl;
-
- /*
- * The ISP core doesn't support pipelines with multiple video outputs.
- * Revisit this when it will be implemented, and return -EBUSY for now.
- */
-
- switch (local->index | media_entity_type(remote->entity)) {
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (csi2->output & ~CSI2_OUTPUT_MEMORY)
- return -EBUSY;
- csi2->output |= CSI2_OUTPUT_MEMORY;
- } else {
- csi2->output &= ~CSI2_OUTPUT_MEMORY;
- }
- break;
-
- case CSI2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (csi2->output & ~CSI2_OUTPUT_CCDC)
- return -EBUSY;
- csi2->output |= CSI2_OUTPUT_CCDC;
- } else {
- csi2->output &= ~CSI2_OUTPUT_CCDC;
- }
- break;
-
- default:
- /* Link from camera to CSI2 is fixed... */
- return -EINVAL;
- }
-
- ctrl->vp_only_enable =
- (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true;
- ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC);
-
- return 0;
-}
-
-/* media operations */
-static const struct media_entity_operations csi2_media_ops = {
- .link_setup = csi2_link_setup,
- .link_validate = v4l2_subdev_link_validate,
-};
-
-void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
-{
- v4l2_device_unregister_subdev(&csi2->subdev);
- omap3isp_video_unregister(&csi2->video_out);
-}
-
-int omap3isp_csi2_register_entities(struct isp_csi2_device *csi2,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&csi2->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_csi2_unregister_entities(csi2);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * ISP CSI2 initialisation and cleanup
- */
-
-/*
- * csi2_init_entities - Initialize subdev and media entity.
- * @csi2: Pointer to csi2 structure.
- * return -ENOMEM or zero on success
- */
-static int csi2_init_entities(struct isp_csi2_device *csi2)
-{
- struct v4l2_subdev *sd = &csi2->subdev;
- struct media_pad *pads = csi2->pads;
- struct media_entity *me = &sd->entity;
- int ret;
-
- v4l2_subdev_init(sd, &csi2_ops);
- sd->internal_ops = &csi2_internal_ops;
- strlcpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name));
-
- sd->grp_id = 1 << 16; /* group ID for isp subdevs */
- v4l2_set_subdevdata(sd, csi2);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
-
- me->ops = &csi2_media_ops;
- ret = media_entity_init(me, CSI2_PADS_NUM, pads, 0);
- if (ret < 0)
- return ret;
-
- csi2_init_formats(sd, NULL);
-
- /* Video device node */
- csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- csi2->video_out.ops = &csi2_ispvideo_ops;
- csi2->video_out.bpl_alignment = 32;
- csi2->video_out.bpl_zero_padding = 1;
- csi2->video_out.bpl_max = 0x1ffe0;
- csi2->video_out.isp = csi2->isp;
- csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3;
-
- ret = omap3isp_video_init(&csi2->video_out, "CSI2a");
- if (ret < 0)
- goto error_video;
-
- /* Connect the CSI2 subdev to the video node. */
- ret = media_entity_create_link(&csi2->subdev.entity, CSI2_PAD_SOURCE,
- &csi2->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
- return 0;
-
-error_link:
- omap3isp_video_cleanup(&csi2->video_out);
-error_video:
- media_entity_cleanup(&csi2->subdev.entity);
- return ret;
-}
-
-/*
- * omap3isp_csi2_init - Routine for module driver init
- */
-int omap3isp_csi2_init(struct isp_device *isp)
-{
- struct isp_csi2_device *csi2a = &isp->isp_csi2a;
- struct isp_csi2_device *csi2c = &isp->isp_csi2c;
- int ret;
-
- csi2a->isp = isp;
- csi2a->available = 1;
- csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1;
- csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2;
- csi2a->phy = &isp->isp_csiphy2;
- csi2a->state = ISP_PIPELINE_STREAM_STOPPED;
- init_waitqueue_head(&csi2a->wait);
-
- ret = csi2_init_entities(csi2a);
- if (ret < 0)
- return ret;
-
- if (isp->revision == ISP_REVISION_15_0) {
- csi2c->isp = isp;
- csi2c->available = 1;
- csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1;
- csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2;
- csi2c->phy = &isp->isp_csiphy1;
- csi2c->state = ISP_PIPELINE_STREAM_STOPPED;
- init_waitqueue_head(&csi2c->wait);
- }
-
- return 0;
-}
-
-/*
- * omap3isp_csi2_cleanup - Routine for module driver cleanup
- */
-void omap3isp_csi2_cleanup(struct isp_device *isp)
-{
- struct isp_csi2_device *csi2a = &isp->isp_csi2a;
-
- omap3isp_video_cleanup(&csi2a->video_out);
- media_entity_cleanup(&csi2a->subdev.entity);
-}
diff --git a/drivers/media/video/omap3isp/isph3a_aewb.c b/drivers/media/video/omap3isp/isph3a_aewb.c
deleted file mode 100644
index a3c76bf18175..000000000000
--- a/drivers/media/video/omap3isp/isph3a_aewb.c
+++ /dev/null
@@ -1,374 +0,0 @@
-/*
- * isph3a.c
- *
- * TI OMAP3 ISP - H3A module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: David Cohen <dacohen@gmail.com>
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include "isp.h"
-#include "isph3a.h"
-#include "ispstat.h"
-
-/*
- * h3a_aewb_update_regs - Helper function to update h3a registers.
- */
-static void h3a_aewb_setup_regs(struct ispstat *aewb, void *priv)
-{
- struct omap3isp_h3a_aewb_config *conf = priv;
- u32 pcr;
- u32 win1;
- u32 start;
- u32 blk;
- u32 subwin;
-
- if (aewb->state == ISPSTAT_DISABLED)
- return;
-
- isp_reg_writel(aewb->isp, aewb->active_buf->iommu_addr,
- OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWBUFST);
-
- if (!aewb->update)
- return;
-
- /* Converting config metadata into reg values */
- pcr = conf->saturation_limit << ISPH3A_PCR_AEW_AVE2LMT_SHIFT;
- pcr |= !!conf->alaw_enable << ISPH3A_PCR_AEW_ALAW_EN_SHIFT;
-
- win1 = ((conf->win_height >> 1) - 1) << ISPH3A_AEWWIN1_WINH_SHIFT;
- win1 |= ((conf->win_width >> 1) - 1) << ISPH3A_AEWWIN1_WINW_SHIFT;
- win1 |= (conf->ver_win_count - 1) << ISPH3A_AEWWIN1_WINVC_SHIFT;
- win1 |= (conf->hor_win_count - 1) << ISPH3A_AEWWIN1_WINHC_SHIFT;
-
- start = conf->hor_win_start << ISPH3A_AEWINSTART_WINSH_SHIFT;
- start |= conf->ver_win_start << ISPH3A_AEWINSTART_WINSV_SHIFT;
-
- blk = conf->blk_ver_win_start << ISPH3A_AEWINBLK_WINSV_SHIFT;
- blk |= ((conf->blk_win_height >> 1) - 1) << ISPH3A_AEWINBLK_WINH_SHIFT;
-
- subwin = ((conf->subsample_ver_inc >> 1) - 1) <<
- ISPH3A_AEWSUBWIN_AEWINCV_SHIFT;
- subwin |= ((conf->subsample_hor_inc >> 1) - 1) <<
- ISPH3A_AEWSUBWIN_AEWINCH_SHIFT;
-
- isp_reg_writel(aewb->isp, win1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWWIN1);
- isp_reg_writel(aewb->isp, start, OMAP3_ISP_IOMEM_H3A,
- ISPH3A_AEWINSTART);
- isp_reg_writel(aewb->isp, blk, OMAP3_ISP_IOMEM_H3A, ISPH3A_AEWINBLK);
- isp_reg_writel(aewb->isp, subwin, OMAP3_ISP_IOMEM_H3A,
- ISPH3A_AEWSUBWIN);
- isp_reg_clr_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- ISPH3A_PCR_AEW_MASK, pcr);
-
- aewb->update = 0;
- aewb->config_counter += aewb->inc_config;
- aewb->inc_config = 0;
- aewb->buf_size = conf->buf_size;
-}
-
-static void h3a_aewb_enable(struct ispstat *aewb, int enable)
-{
- if (enable) {
- isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- ISPH3A_PCR_AEW_EN);
- /* This bit is already set if AF is enabled */
- if (aewb->isp->isp_af.state != ISPSTAT_ENABLED)
- isp_reg_set(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_H3A_CLK_EN);
- } else {
- isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- ISPH3A_PCR_AEW_EN);
- /* This bit can't be cleared if AF is enabled */
- if (aewb->isp->isp_af.state != ISPSTAT_ENABLED)
- isp_reg_clr(aewb->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_H3A_CLK_EN);
- }
-}
-
-static int h3a_aewb_busy(struct ispstat *aewb)
-{
- return isp_reg_readl(aewb->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
- & ISPH3A_PCR_BUSYAEAWB;
-}
-
-static u32 h3a_aewb_get_buf_size(struct omap3isp_h3a_aewb_config *conf)
-{
- /* Number of configured windows + extra row for black data */
- u32 win_count = (conf->ver_win_count + 1) * conf->hor_win_count;
-
- /*
- * Unsaturated block counts for each 8 windows.
- * 1 extra for the last (win_count % 8) windows if win_count is not
- * divisible by 8.
- */
- win_count += (win_count + 7) / 8;
-
- return win_count * AEWB_PACKET_SIZE;
-}
-
-static int h3a_aewb_validate_params(struct ispstat *aewb, void *new_conf)
-{
- struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
- u32 buf_size;
-
- if (unlikely(user_cfg->saturation_limit >
- OMAP3ISP_AEWB_MAX_SATURATION_LIM))
- return -EINVAL;
-
- if (unlikely(user_cfg->win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
- user_cfg->win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
- user_cfg->win_height & 0x01))
- return -EINVAL;
-
- if (unlikely(user_cfg->win_width < OMAP3ISP_AEWB_MIN_WIN_W ||
- user_cfg->win_width > OMAP3ISP_AEWB_MAX_WIN_W ||
- user_cfg->win_width & 0x01))
- return -EINVAL;
-
- if (unlikely(user_cfg->ver_win_count < OMAP3ISP_AEWB_MIN_WINVC ||
- user_cfg->ver_win_count > OMAP3ISP_AEWB_MAX_WINVC))
- return -EINVAL;
-
- if (unlikely(user_cfg->hor_win_count < OMAP3ISP_AEWB_MIN_WINHC ||
- user_cfg->hor_win_count > OMAP3ISP_AEWB_MAX_WINHC))
- return -EINVAL;
-
- if (unlikely(user_cfg->ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
- return -EINVAL;
-
- if (unlikely(user_cfg->hor_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
- return -EINVAL;
-
- if (unlikely(user_cfg->blk_ver_win_start > OMAP3ISP_AEWB_MAX_WINSTART))
- return -EINVAL;
-
- if (unlikely(user_cfg->blk_win_height < OMAP3ISP_AEWB_MIN_WIN_H ||
- user_cfg->blk_win_height > OMAP3ISP_AEWB_MAX_WIN_H ||
- user_cfg->blk_win_height & 0x01))
- return -EINVAL;
-
- if (unlikely(user_cfg->subsample_ver_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
- user_cfg->subsample_ver_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
- user_cfg->subsample_ver_inc & 0x01))
- return -EINVAL;
-
- if (unlikely(user_cfg->subsample_hor_inc < OMAP3ISP_AEWB_MIN_SUB_INC ||
- user_cfg->subsample_hor_inc > OMAP3ISP_AEWB_MAX_SUB_INC ||
- user_cfg->subsample_hor_inc & 0x01))
- return -EINVAL;
-
- buf_size = h3a_aewb_get_buf_size(user_cfg);
- if (buf_size > user_cfg->buf_size)
- user_cfg->buf_size = buf_size;
- else if (user_cfg->buf_size > OMAP3ISP_AEWB_MAX_BUF_SIZE)
- user_cfg->buf_size = OMAP3ISP_AEWB_MAX_BUF_SIZE;
-
- return 0;
-}
-
-/*
- * h3a_aewb_set_params - Helper function to check & store user given params.
- * @new_conf: Pointer to AE and AWB parameters struct.
- *
- * As most of them are busy-lock registers, need to wait until AEW_BUSY = 0 to
- * program them during ISR.
- */
-static void h3a_aewb_set_params(struct ispstat *aewb, void *new_conf)
-{
- struct omap3isp_h3a_aewb_config *user_cfg = new_conf;
- struct omap3isp_h3a_aewb_config *cur_cfg = aewb->priv;
- int update = 0;
-
- if (cur_cfg->saturation_limit != user_cfg->saturation_limit) {
- cur_cfg->saturation_limit = user_cfg->saturation_limit;
- update = 1;
- }
- if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
- cur_cfg->alaw_enable = user_cfg->alaw_enable;
- update = 1;
- }
- if (cur_cfg->win_height != user_cfg->win_height) {
- cur_cfg->win_height = user_cfg->win_height;
- update = 1;
- }
- if (cur_cfg->win_width != user_cfg->win_width) {
- cur_cfg->win_width = user_cfg->win_width;
- update = 1;
- }
- if (cur_cfg->ver_win_count != user_cfg->ver_win_count) {
- cur_cfg->ver_win_count = user_cfg->ver_win_count;
- update = 1;
- }
- if (cur_cfg->hor_win_count != user_cfg->hor_win_count) {
- cur_cfg->hor_win_count = user_cfg->hor_win_count;
- update = 1;
- }
- if (cur_cfg->ver_win_start != user_cfg->ver_win_start) {
- cur_cfg->ver_win_start = user_cfg->ver_win_start;
- update = 1;
- }
- if (cur_cfg->hor_win_start != user_cfg->hor_win_start) {
- cur_cfg->hor_win_start = user_cfg->hor_win_start;
- update = 1;
- }
- if (cur_cfg->blk_ver_win_start != user_cfg->blk_ver_win_start) {
- cur_cfg->blk_ver_win_start = user_cfg->blk_ver_win_start;
- update = 1;
- }
- if (cur_cfg->blk_win_height != user_cfg->blk_win_height) {
- cur_cfg->blk_win_height = user_cfg->blk_win_height;
- update = 1;
- }
- if (cur_cfg->subsample_ver_inc != user_cfg->subsample_ver_inc) {
- cur_cfg->subsample_ver_inc = user_cfg->subsample_ver_inc;
- update = 1;
- }
- if (cur_cfg->subsample_hor_inc != user_cfg->subsample_hor_inc) {
- cur_cfg->subsample_hor_inc = user_cfg->subsample_hor_inc;
- update = 1;
- }
-
- if (update || !aewb->configured) {
- aewb->inc_config++;
- aewb->update = 1;
- cur_cfg->buf_size = h3a_aewb_get_buf_size(cur_cfg);
- }
-}
-
-static long h3a_aewb_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct ispstat *stat = v4l2_get_subdevdata(sd);
-
- switch (cmd) {
- case VIDIOC_OMAP3ISP_AEWB_CFG:
- return omap3isp_stat_config(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_REQ:
- return omap3isp_stat_request_statistics(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_EN: {
- unsigned long *en = arg;
- return omap3isp_stat_enable(stat, !!*en);
- }
- }
-
- return -ENOIOCTLCMD;
-}
-
-static const struct ispstat_ops h3a_aewb_ops = {
- .validate_params = h3a_aewb_validate_params,
- .set_params = h3a_aewb_set_params,
- .setup_regs = h3a_aewb_setup_regs,
- .enable = h3a_aewb_enable,
- .busy = h3a_aewb_busy,
-};
-
-static const struct v4l2_subdev_core_ops h3a_aewb_subdev_core_ops = {
- .ioctl = h3a_aewb_ioctl,
- .subscribe_event = omap3isp_stat_subscribe_event,
- .unsubscribe_event = omap3isp_stat_unsubscribe_event,
-};
-
-static const struct v4l2_subdev_video_ops h3a_aewb_subdev_video_ops = {
- .s_stream = omap3isp_stat_s_stream,
-};
-
-static const struct v4l2_subdev_ops h3a_aewb_subdev_ops = {
- .core = &h3a_aewb_subdev_core_ops,
- .video = &h3a_aewb_subdev_video_ops,
-};
-
-/*
- * omap3isp_h3a_aewb_init - Module Initialisation.
- */
-int omap3isp_h3a_aewb_init(struct isp_device *isp)
-{
- struct ispstat *aewb = &isp->isp_aewb;
- struct omap3isp_h3a_aewb_config *aewb_cfg;
- struct omap3isp_h3a_aewb_config *aewb_recover_cfg;
- int ret;
-
- aewb_cfg = kzalloc(sizeof(*aewb_cfg), GFP_KERNEL);
- if (!aewb_cfg)
- return -ENOMEM;
-
- memset(aewb, 0, sizeof(*aewb));
- aewb->ops = &h3a_aewb_ops;
- aewb->priv = aewb_cfg;
- aewb->dma_ch = -1;
- aewb->event_type = V4L2_EVENT_OMAP3ISP_AEWB;
- aewb->isp = isp;
-
- /* Set recover state configuration */
- aewb_recover_cfg = kzalloc(sizeof(*aewb_recover_cfg), GFP_KERNEL);
- if (!aewb_recover_cfg) {
- dev_err(aewb->isp->dev, "AEWB: cannot allocate memory for "
- "recover configuration.\n");
- ret = -ENOMEM;
- goto err_recover_alloc;
- }
-
- aewb_recover_cfg->saturation_limit = OMAP3ISP_AEWB_MAX_SATURATION_LIM;
- aewb_recover_cfg->win_height = OMAP3ISP_AEWB_MIN_WIN_H;
- aewb_recover_cfg->win_width = OMAP3ISP_AEWB_MIN_WIN_W;
- aewb_recover_cfg->ver_win_count = OMAP3ISP_AEWB_MIN_WINVC;
- aewb_recover_cfg->hor_win_count = OMAP3ISP_AEWB_MIN_WINHC;
- aewb_recover_cfg->blk_ver_win_start = aewb_recover_cfg->ver_win_start +
- aewb_recover_cfg->win_height * aewb_recover_cfg->ver_win_count;
- aewb_recover_cfg->blk_win_height = OMAP3ISP_AEWB_MIN_WIN_H;
- aewb_recover_cfg->subsample_ver_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
- aewb_recover_cfg->subsample_hor_inc = OMAP3ISP_AEWB_MIN_SUB_INC;
-
- if (h3a_aewb_validate_params(aewb, aewb_recover_cfg)) {
- dev_err(aewb->isp->dev, "AEWB: recover configuration is "
- "invalid.\n");
- ret = -EINVAL;
- goto err_conf;
- }
-
- aewb_recover_cfg->buf_size = h3a_aewb_get_buf_size(aewb_recover_cfg);
- aewb->recover_priv = aewb_recover_cfg;
-
- ret = omap3isp_stat_init(aewb, "AEWB", &h3a_aewb_subdev_ops);
- if (ret)
- goto err_conf;
-
- return 0;
-
-err_conf:
- kfree(aewb_recover_cfg);
-err_recover_alloc:
- kfree(aewb_cfg);
-
- return ret;
-}
-
-/*
- * omap3isp_h3a_aewb_cleanup - Module exit.
- */
-void omap3isp_h3a_aewb_cleanup(struct isp_device *isp)
-{
- kfree(isp->isp_aewb.priv);
- kfree(isp->isp_aewb.recover_priv);
- omap3isp_stat_cleanup(&isp->isp_aewb);
-}
diff --git a/drivers/media/video/omap3isp/isph3a_af.c b/drivers/media/video/omap3isp/isph3a_af.c
deleted file mode 100644
index 58e0bc414899..000000000000
--- a/drivers/media/video/omap3isp/isph3a_af.c
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- * isph3a_af.c
- *
- * TI OMAP3 ISP - H3A AF module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: David Cohen <dacohen@gmail.com>
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-/* Linux specific include files */
-#include <linux/device.h>
-#include <linux/slab.h>
-
-#include "isp.h"
-#include "isph3a.h"
-#include "ispstat.h"
-
-#define IS_OUT_OF_BOUNDS(value, min, max) \
- (((value) < (min)) || ((value) > (max)))
-
-static void h3a_af_setup_regs(struct ispstat *af, void *priv)
-{
- struct omap3isp_h3a_af_config *conf = priv;
- u32 pcr;
- u32 pax1;
- u32 pax2;
- u32 paxstart;
- u32 coef;
- u32 base_coef_set0;
- u32 base_coef_set1;
- int index;
-
- if (af->state == ISPSTAT_DISABLED)
- return;
-
- isp_reg_writel(af->isp, af->active_buf->iommu_addr, OMAP3_ISP_IOMEM_H3A,
- ISPH3A_AFBUFST);
-
- if (!af->update)
- return;
-
- /* Configure Hardware Registers */
- pax1 = ((conf->paxel.width >> 1) - 1) << AF_PAXW_SHIFT;
- /* Set height in AFPAX1 */
- pax1 |= (conf->paxel.height >> 1) - 1;
- isp_reg_writel(af->isp, pax1, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX1);
-
- /* Configure AFPAX2 Register */
- /* Set Line Increment in AFPAX2 Register */
- pax2 = ((conf->paxel.line_inc >> 1) - 1) << AF_LINE_INCR_SHIFT;
- /* Set Vertical Count */
- pax2 |= (conf->paxel.v_cnt - 1) << AF_VT_COUNT_SHIFT;
- /* Set Horizontal Count */
- pax2 |= (conf->paxel.h_cnt - 1);
- isp_reg_writel(af->isp, pax2, OMAP3_ISP_IOMEM_H3A, ISPH3A_AFPAX2);
-
- /* Configure PAXSTART Register */
- /*Configure Horizontal Start */
- paxstart = conf->paxel.h_start << AF_HZ_START_SHIFT;
- /* Configure Vertical Start */
- paxstart |= conf->paxel.v_start;
- isp_reg_writel(af->isp, paxstart, OMAP3_ISP_IOMEM_H3A,
- ISPH3A_AFPAXSTART);
-
- /*SetIIRSH Register */
- isp_reg_writel(af->isp, conf->iir.h_start,
- OMAP3_ISP_IOMEM_H3A, ISPH3A_AFIIRSH);
-
- base_coef_set0 = ISPH3A_AFCOEF010;
- base_coef_set1 = ISPH3A_AFCOEF110;
- for (index = 0; index <= 8; index += 2) {
- /*Set IIR Filter0 Coefficients */
- coef = 0;
- coef |= conf->iir.coeff_set0[index];
- coef |= conf->iir.coeff_set0[index + 1] <<
- AF_COEF_SHIFT;
- isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
- base_coef_set0);
- base_coef_set0 += AFCOEF_OFFSET;
-
- /*Set IIR Filter1 Coefficients */
- coef = 0;
- coef |= conf->iir.coeff_set1[index];
- coef |= conf->iir.coeff_set1[index + 1] <<
- AF_COEF_SHIFT;
- isp_reg_writel(af->isp, coef, OMAP3_ISP_IOMEM_H3A,
- base_coef_set1);
- base_coef_set1 += AFCOEF_OFFSET;
- }
- /* set AFCOEF0010 Register */
- isp_reg_writel(af->isp, conf->iir.coeff_set0[10],
- OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF0010);
- /* set AFCOEF1010 Register */
- isp_reg_writel(af->isp, conf->iir.coeff_set1[10],
- OMAP3_ISP_IOMEM_H3A, ISPH3A_AFCOEF1010);
-
- /* PCR Register */
- /* Set RGB Position */
- pcr = conf->rgb_pos << AF_RGBPOS_SHIFT;
- /* Set Accumulator Mode */
- if (conf->fvmode == OMAP3ISP_AF_MODE_PEAK)
- pcr |= AF_FVMODE;
- /* Set A-law */
- if (conf->alaw_enable)
- pcr |= AF_ALAW_EN;
- /* HMF Configurations */
- if (conf->hmf.enable) {
- /* Enable HMF */
- pcr |= AF_MED_EN;
- /* Set Median Threshold */
- pcr |= conf->hmf.threshold << AF_MED_TH_SHIFT;
- }
- /* Set PCR Register */
- isp_reg_clr_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- AF_PCR_MASK, pcr);
-
- af->update = 0;
- af->config_counter += af->inc_config;
- af->inc_config = 0;
- af->buf_size = conf->buf_size;
-}
-
-static void h3a_af_enable(struct ispstat *af, int enable)
-{
- if (enable) {
- isp_reg_set(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- ISPH3A_PCR_AF_EN);
- /* This bit is already set if AEWB is enabled */
- if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
- isp_reg_set(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_H3A_CLK_EN);
- } else {
- isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR,
- ISPH3A_PCR_AF_EN);
- /* This bit can't be cleared if AEWB is enabled */
- if (af->isp->isp_aewb.state != ISPSTAT_ENABLED)
- isp_reg_clr(af->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_H3A_CLK_EN);
- }
-}
-
-static int h3a_af_busy(struct ispstat *af)
-{
- return isp_reg_readl(af->isp, OMAP3_ISP_IOMEM_H3A, ISPH3A_PCR)
- & ISPH3A_PCR_BUSYAF;
-}
-
-static u32 h3a_af_get_buf_size(struct omap3isp_h3a_af_config *conf)
-{
- return conf->paxel.h_cnt * conf->paxel.v_cnt * OMAP3ISP_AF_PAXEL_SIZE;
-}
-
-/* Function to check paxel parameters */
-static int h3a_af_validate_params(struct ispstat *af, void *new_conf)
-{
- struct omap3isp_h3a_af_config *user_cfg = new_conf;
- struct omap3isp_h3a_af_paxel *paxel_cfg = &user_cfg->paxel;
- struct omap3isp_h3a_af_iir *iir_cfg = &user_cfg->iir;
- int index;
- u32 buf_size;
-
- /* Check horizontal Count */
- if (IS_OUT_OF_BOUNDS(paxel_cfg->h_cnt,
- OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN,
- OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MAX))
- return -EINVAL;
-
- /* Check Vertical Count */
- if (IS_OUT_OF_BOUNDS(paxel_cfg->v_cnt,
- OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN,
- OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MAX))
- return -EINVAL;
-
- if (IS_OUT_OF_BOUNDS(paxel_cfg->height, OMAP3ISP_AF_PAXEL_HEIGHT_MIN,
- OMAP3ISP_AF_PAXEL_HEIGHT_MAX) ||
- paxel_cfg->height % 2)
- return -EINVAL;
-
- /* Check width */
- if (IS_OUT_OF_BOUNDS(paxel_cfg->width, OMAP3ISP_AF_PAXEL_WIDTH_MIN,
- OMAP3ISP_AF_PAXEL_WIDTH_MAX) ||
- paxel_cfg->width % 2)
- return -EINVAL;
-
- /* Check Line Increment */
- if (IS_OUT_OF_BOUNDS(paxel_cfg->line_inc,
- OMAP3ISP_AF_PAXEL_INCREMENT_MIN,
- OMAP3ISP_AF_PAXEL_INCREMENT_MAX) ||
- paxel_cfg->line_inc % 2)
- return -EINVAL;
-
- /* Check Horizontal Start */
- if ((paxel_cfg->h_start < iir_cfg->h_start) ||
- IS_OUT_OF_BOUNDS(paxel_cfg->h_start,
- OMAP3ISP_AF_PAXEL_HZSTART_MIN,
- OMAP3ISP_AF_PAXEL_HZSTART_MAX))
- return -EINVAL;
-
- /* Check IIR */
- for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
- if ((iir_cfg->coeff_set0[index]) > OMAP3ISP_AF_COEF_MAX)
- return -EINVAL;
-
- if ((iir_cfg->coeff_set1[index]) > OMAP3ISP_AF_COEF_MAX)
- return -EINVAL;
- }
-
- if (IS_OUT_OF_BOUNDS(iir_cfg->h_start, OMAP3ISP_AF_IIRSH_MIN,
- OMAP3ISP_AF_IIRSH_MAX))
- return -EINVAL;
-
- /* Hack: If paxel size is 12, the 10th AF window may be corrupted */
- if ((paxel_cfg->h_cnt * paxel_cfg->v_cnt > 9) &&
- (paxel_cfg->width * paxel_cfg->height == 12))
- return -EINVAL;
-
- buf_size = h3a_af_get_buf_size(user_cfg);
- if (buf_size > user_cfg->buf_size)
- /* User buf_size request wasn't enough */
- user_cfg->buf_size = buf_size;
- else if (user_cfg->buf_size > OMAP3ISP_AF_MAX_BUF_SIZE)
- user_cfg->buf_size = OMAP3ISP_AF_MAX_BUF_SIZE;
-
- return 0;
-}
-
-/* Update local parameters */
-static void h3a_af_set_params(struct ispstat *af, void *new_conf)
-{
- struct omap3isp_h3a_af_config *user_cfg = new_conf;
- struct omap3isp_h3a_af_config *cur_cfg = af->priv;
- int update = 0;
- int index;
-
- /* alaw */
- if (cur_cfg->alaw_enable != user_cfg->alaw_enable) {
- update = 1;
- goto out;
- }
-
- /* hmf */
- if (cur_cfg->hmf.enable != user_cfg->hmf.enable) {
- update = 1;
- goto out;
- }
- if (cur_cfg->hmf.threshold != user_cfg->hmf.threshold) {
- update = 1;
- goto out;
- }
-
- /* rgbpos */
- if (cur_cfg->rgb_pos != user_cfg->rgb_pos) {
- update = 1;
- goto out;
- }
-
- /* iir */
- if (cur_cfg->iir.h_start != user_cfg->iir.h_start) {
- update = 1;
- goto out;
- }
- for (index = 0; index < OMAP3ISP_AF_NUM_COEF; index++) {
- if (cur_cfg->iir.coeff_set0[index] !=
- user_cfg->iir.coeff_set0[index]) {
- update = 1;
- goto out;
- }
- if (cur_cfg->iir.coeff_set1[index] !=
- user_cfg->iir.coeff_set1[index]) {
- update = 1;
- goto out;
- }
- }
-
- /* paxel */
- if ((cur_cfg->paxel.width != user_cfg->paxel.width) ||
- (cur_cfg->paxel.height != user_cfg->paxel.height) ||
- (cur_cfg->paxel.h_start != user_cfg->paxel.h_start) ||
- (cur_cfg->paxel.v_start != user_cfg->paxel.v_start) ||
- (cur_cfg->paxel.h_cnt != user_cfg->paxel.h_cnt) ||
- (cur_cfg->paxel.v_cnt != user_cfg->paxel.v_cnt) ||
- (cur_cfg->paxel.line_inc != user_cfg->paxel.line_inc)) {
- update = 1;
- goto out;
- }
-
- /* af_mode */
- if (cur_cfg->fvmode != user_cfg->fvmode)
- update = 1;
-
-out:
- if (update || !af->configured) {
- memcpy(cur_cfg, user_cfg, sizeof(*cur_cfg));
- af->inc_config++;
- af->update = 1;
- /*
- * User might be asked for a bigger buffer than necessary for
- * this configuration. In order to return the right amount of
- * data during buffer request, let's calculate the size here
- * instead of stick with user_cfg->buf_size.
- */
- cur_cfg->buf_size = h3a_af_get_buf_size(cur_cfg);
- }
-}
-
-static long h3a_af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct ispstat *stat = v4l2_get_subdevdata(sd);
-
- switch (cmd) {
- case VIDIOC_OMAP3ISP_AF_CFG:
- return omap3isp_stat_config(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_REQ:
- return omap3isp_stat_request_statistics(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_EN: {
- int *en = arg;
- return omap3isp_stat_enable(stat, !!*en);
- }
- }
-
- return -ENOIOCTLCMD;
-
-}
-
-static const struct ispstat_ops h3a_af_ops = {
- .validate_params = h3a_af_validate_params,
- .set_params = h3a_af_set_params,
- .setup_regs = h3a_af_setup_regs,
- .enable = h3a_af_enable,
- .busy = h3a_af_busy,
-};
-
-static const struct v4l2_subdev_core_ops h3a_af_subdev_core_ops = {
- .ioctl = h3a_af_ioctl,
- .subscribe_event = omap3isp_stat_subscribe_event,
- .unsubscribe_event = omap3isp_stat_unsubscribe_event,
-};
-
-static const struct v4l2_subdev_video_ops h3a_af_subdev_video_ops = {
- .s_stream = omap3isp_stat_s_stream,
-};
-
-static const struct v4l2_subdev_ops h3a_af_subdev_ops = {
- .core = &h3a_af_subdev_core_ops,
- .video = &h3a_af_subdev_video_ops,
-};
-
-/* Function to register the AF character device driver. */
-int omap3isp_h3a_af_init(struct isp_device *isp)
-{
- struct ispstat *af = &isp->isp_af;
- struct omap3isp_h3a_af_config *af_cfg;
- struct omap3isp_h3a_af_config *af_recover_cfg;
- int ret;
-
- af_cfg = kzalloc(sizeof(*af_cfg), GFP_KERNEL);
- if (af_cfg == NULL)
- return -ENOMEM;
-
- memset(af, 0, sizeof(*af));
- af->ops = &h3a_af_ops;
- af->priv = af_cfg;
- af->dma_ch = -1;
- af->event_type = V4L2_EVENT_OMAP3ISP_AF;
- af->isp = isp;
-
- /* Set recover state configuration */
- af_recover_cfg = kzalloc(sizeof(*af_recover_cfg), GFP_KERNEL);
- if (!af_recover_cfg) {
- dev_err(af->isp->dev, "AF: cannot allocate memory for recover "
- "configuration.\n");
- ret = -ENOMEM;
- goto err_recover_alloc;
- }
-
- af_recover_cfg->paxel.h_start = OMAP3ISP_AF_PAXEL_HZSTART_MIN;
- af_recover_cfg->paxel.width = OMAP3ISP_AF_PAXEL_WIDTH_MIN;
- af_recover_cfg->paxel.height = OMAP3ISP_AF_PAXEL_HEIGHT_MIN;
- af_recover_cfg->paxel.h_cnt = OMAP3ISP_AF_PAXEL_HORIZONTAL_COUNT_MIN;
- af_recover_cfg->paxel.v_cnt = OMAP3ISP_AF_PAXEL_VERTICAL_COUNT_MIN;
- af_recover_cfg->paxel.line_inc = OMAP3ISP_AF_PAXEL_INCREMENT_MIN;
- if (h3a_af_validate_params(af, af_recover_cfg)) {
- dev_err(af->isp->dev, "AF: recover configuration is "
- "invalid.\n");
- ret = -EINVAL;
- goto err_conf;
- }
-
- af_recover_cfg->buf_size = h3a_af_get_buf_size(af_recover_cfg);
- af->recover_priv = af_recover_cfg;
-
- ret = omap3isp_stat_init(af, "AF", &h3a_af_subdev_ops);
- if (ret)
- goto err_conf;
-
- return 0;
-
-err_conf:
- kfree(af_recover_cfg);
-err_recover_alloc:
- kfree(af_cfg);
-
- return ret;
-}
-
-void omap3isp_h3a_af_cleanup(struct isp_device *isp)
-{
- kfree(isp->isp_af.priv);
- kfree(isp->isp_af.recover_priv);
- omap3isp_stat_cleanup(&isp->isp_af);
-}
diff --git a/drivers/media/video/omap3isp/isphist.c b/drivers/media/video/omap3isp/isphist.c
deleted file mode 100644
index 1163907bcddc..000000000000
--- a/drivers/media/video/omap3isp/isphist.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * isphist.c
- *
- * TI OMAP3 ISP - Histogram module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: David Cohen <dacohen@gmail.com>
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/device.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "isphist.h"
-
-#define HIST_CONFIG_DMA 1
-
-#define HIST_USING_DMA(hist) ((hist)->dma_ch >= 0)
-
-/*
- * hist_reset_mem - clear Histogram memory before start stats engine.
- */
-static void hist_reset_mem(struct ispstat *hist)
-{
- struct isp_device *isp = hist->isp;
- struct omap3isp_hist_config *conf = hist->priv;
- unsigned int i;
-
- isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
-
- /*
- * By setting it, the histogram internal buffer is being cleared at the
- * same time it's being read. This bit must be cleared afterwards.
- */
- isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
-
- /*
- * We'll clear 4 words at each iteration for optimization. It avoids
- * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4.
- */
- for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) {
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- }
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
-
- hist->wait_acc_frames = conf->num_acc_frames;
-}
-
-static void hist_dma_config(struct ispstat *hist)
-{
- hist->dma_config.data_type = OMAP_DMA_DATA_TYPE_S32;
- hist->dma_config.sync_mode = OMAP_DMA_SYNC_ELEMENT;
- hist->dma_config.frame_count = 1;
- hist->dma_config.src_amode = OMAP_DMA_AMODE_CONSTANT;
- hist->dma_config.src_start = OMAP3ISP_HIST_REG_BASE + ISPHIST_DATA;
- hist->dma_config.dst_amode = OMAP_DMA_AMODE_POST_INC;
- hist->dma_config.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
-}
-
-/*
- * hist_setup_regs - Helper function to update Histogram registers.
- */
-static void hist_setup_regs(struct ispstat *hist, void *priv)
-{
- struct isp_device *isp = hist->isp;
- struct omap3isp_hist_config *conf = priv;
- int c;
- u32 cnt;
- u32 wb_gain;
- u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS];
- u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS];
-
- if (!hist->update || hist->state == ISPSTAT_DISABLED ||
- hist->state == ISPSTAT_DISABLING)
- return;
-
- cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT;
-
- wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT;
- wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT;
- wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT;
- if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER)
- wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT;
-
- /* Regions size and position */
- for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) {
- if (c < conf->num_regions) {
- reg_hor[c] = conf->region[c].h_start <<
- ISPHIST_REG_START_SHIFT;
- reg_hor[c] = conf->region[c].h_end <<
- ISPHIST_REG_END_SHIFT;
- reg_ver[c] = conf->region[c].v_start <<
- ISPHIST_REG_START_SHIFT;
- reg_ver[c] = conf->region[c].v_end <<
- ISPHIST_REG_END_SHIFT;
- } else {
- reg_hor[c] = 0;
- reg_ver[c] = 0;
- }
- }
-
- cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT;
- switch (conf->hist_bins) {
- case OMAP3ISP_HIST_BINS_256:
- cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) <<
- ISPHIST_CNT_SHIFT_SHIFT;
- break;
- case OMAP3ISP_HIST_BINS_128:
- cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) <<
- ISPHIST_CNT_SHIFT_SHIFT;
- break;
- case OMAP3ISP_HIST_BINS_64:
- cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) <<
- ISPHIST_CNT_SHIFT_SHIFT;
- break;
- default: /* OMAP3ISP_HIST_BINS_32 */
- cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) <<
- ISPHIST_CNT_SHIFT_SHIFT;
- break;
- }
-
- hist_reset_mem(hist);
-
- isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT);
- isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN);
- isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ);
- isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT);
- isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ);
- isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT);
- isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ);
- isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT);
- isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ);
- isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT);
-
- hist->update = 0;
- hist->config_counter += hist->inc_config;
- hist->inc_config = 0;
- hist->buf_size = conf->buf_size;
-}
-
-static void hist_enable(struct ispstat *hist, int enable)
-{
- if (enable) {
- isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
- ISPHIST_PCR_ENABLE);
- isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_HIST_CLK_EN);
- } else {
- isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR,
- ISPHIST_PCR_ENABLE);
- isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL,
- ISPCTRL_HIST_CLK_EN);
- }
-}
-
-static int hist_busy(struct ispstat *hist)
-{
- return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR)
- & ISPHIST_PCR_BUSY;
-}
-
-static void hist_dma_cb(int lch, u16 ch_status, void *data)
-{
- struct ispstat *hist = data;
-
- if (ch_status & ~OMAP_DMA_BLOCK_IRQ) {
- dev_dbg(hist->isp->dev, "hist: DMA error. status = 0x%04x\n",
- ch_status);
- omap_stop_dma(lch);
- hist_reset_mem(hist);
- atomic_set(&hist->buf_err, 1);
- }
- isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
- ISPHIST_CNT_CLEAR);
-
- omap3isp_stat_dma_isr(hist);
- if (hist->state != ISPSTAT_DISABLED)
- omap3isp_hist_dma_done(hist->isp);
-}
-
-static int hist_buf_dma(struct ispstat *hist)
-{
- dma_addr_t dma_addr = hist->active_buf->dma_addr;
-
- if (unlikely(!dma_addr)) {
- dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n");
- hist_reset_mem(hist);
- return STAT_NO_BUF;
- }
-
- isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
- isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
- ISPHIST_CNT_CLEAR);
- omap3isp_flush(hist->isp);
- hist->dma_config.dst_start = dma_addr;
- hist->dma_config.elem_count = hist->buf_size / sizeof(u32);
- omap_set_dma_params(hist->dma_ch, &hist->dma_config);
-
- omap_start_dma(hist->dma_ch);
-
- return STAT_BUF_WAITING_DMA;
-}
-
-static int hist_buf_pio(struct ispstat *hist)
-{
- struct isp_device *isp = hist->isp;
- u32 *buf = hist->active_buf->virt_addr;
- unsigned int i;
-
- if (!buf) {
- dev_dbg(isp->dev, "hist: invalid PIO buffer address\n");
- hist_reset_mem(hist);
- return STAT_NO_BUF;
- }
-
- isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR);
-
- /*
- * By setting it, the histogram internal buffer is being cleared at the
- * same time it's being read. This bit must be cleared just after all
- * data is acquired.
- */
- isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR);
-
- /*
- * We'll read 4 times a 4-bytes-word at each iteration for
- * optimization. It avoids 3/4 of the jumps. We also know buf_size is
- * divisible by 16.
- */
- for (i = hist->buf_size / 16; i > 0; i--) {
- *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA);
- }
- isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT,
- ISPHIST_CNT_CLEAR);
-
- return STAT_BUF_DONE;
-}
-
-/*
- * hist_buf_process - Callback from ISP driver for HIST interrupt.
- */
-static int hist_buf_process(struct ispstat *hist)
-{
- struct omap3isp_hist_config *user_cfg = hist->priv;
- int ret;
-
- if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) {
- hist_reset_mem(hist);
- return STAT_NO_BUF;
- }
-
- if (--(hist->wait_acc_frames))
- return STAT_NO_BUF;
-
- if (HIST_USING_DMA(hist))
- ret = hist_buf_dma(hist);
- else
- ret = hist_buf_pio(hist);
-
- hist->wait_acc_frames = user_cfg->num_acc_frames;
-
- return ret;
-}
-
-static u32 hist_get_buf_size(struct omap3isp_hist_config *conf)
-{
- return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions;
-}
-
-/*
- * hist_validate_params - Helper function to check user given params.
- * @user_cfg: Pointer to user configuration structure.
- *
- * Returns 0 on success configuration.
- */
-static int hist_validate_params(struct ispstat *hist, void *new_conf)
-{
- struct omap3isp_hist_config *user_cfg = new_conf;
- int c;
- u32 buf_size;
-
- if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3)
- return -EINVAL;
-
- /* Regions size and position */
-
- if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) ||
- (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS))
- return -EINVAL;
-
- /* Regions */
- for (c = 0; c < user_cfg->num_regions; c++) {
- if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK)
- return -EINVAL;
- if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK)
- return -EINVAL;
- if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK)
- return -EINVAL;
- if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK)
- return -EINVAL;
- if (user_cfg->region[c].h_start > user_cfg->region[c].h_end)
- return -EINVAL;
- if (user_cfg->region[c].v_start > user_cfg->region[c].v_end)
- return -EINVAL;
- }
-
- switch (user_cfg->num_regions) {
- case 1:
- if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256)
- return -EINVAL;
- break;
- case 2:
- if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128)
- return -EINVAL;
- break;
- default: /* 3 or 4 */
- if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64)
- return -EINVAL;
- break;
- }
-
- buf_size = hist_get_buf_size(user_cfg);
- if (buf_size > user_cfg->buf_size)
- /* User's buf_size request wasn't enoght */
- user_cfg->buf_size = buf_size;
- else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE)
- user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE;
-
- return 0;
-}
-
-static int hist_comp_params(struct ispstat *hist,
- struct omap3isp_hist_config *user_cfg)
-{
- struct omap3isp_hist_config *cur_cfg = hist->priv;
- int c;
-
- if (cur_cfg->cfa != user_cfg->cfa)
- return 1;
-
- if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames)
- return 1;
-
- if (cur_cfg->hist_bins != user_cfg->hist_bins)
- return 1;
-
- for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) {
- if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3)
- break;
- else if (cur_cfg->wg[c] != user_cfg->wg[c])
- return 1;
- }
-
- if (cur_cfg->num_regions != user_cfg->num_regions)
- return 1;
-
- /* Regions */
- for (c = 0; c < user_cfg->num_regions; c++) {
- if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start)
- return 1;
- if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end)
- return 1;
- if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start)
- return 1;
- if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * hist_update_params - Helper function to check and store user given params.
- * @new_conf: Pointer to user configuration structure.
- */
-static void hist_set_params(struct ispstat *hist, void *new_conf)
-{
- struct omap3isp_hist_config *user_cfg = new_conf;
- struct omap3isp_hist_config *cur_cfg = hist->priv;
-
- if (!hist->configured || hist_comp_params(hist, user_cfg)) {
- memcpy(cur_cfg, user_cfg, sizeof(*user_cfg));
- if (user_cfg->num_acc_frames == 0)
- user_cfg->num_acc_frames = 1;
- hist->inc_config++;
- hist->update = 1;
- /*
- * User might be asked for a bigger buffer than necessary for
- * this configuration. In order to return the right amount of
- * data during buffer request, let's calculate the size here
- * instead of stick with user_cfg->buf_size.
- */
- cur_cfg->buf_size = hist_get_buf_size(cur_cfg);
-
- }
-}
-
-static long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct ispstat *stat = v4l2_get_subdevdata(sd);
-
- switch (cmd) {
- case VIDIOC_OMAP3ISP_HIST_CFG:
- return omap3isp_stat_config(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_REQ:
- return omap3isp_stat_request_statistics(stat, arg);
- case VIDIOC_OMAP3ISP_STAT_EN: {
- int *en = arg;
- return omap3isp_stat_enable(stat, !!*en);
- }
- }
-
- return -ENOIOCTLCMD;
-
-}
-
-static const struct ispstat_ops hist_ops = {
- .validate_params = hist_validate_params,
- .set_params = hist_set_params,
- .setup_regs = hist_setup_regs,
- .enable = hist_enable,
- .busy = hist_busy,
- .buf_process = hist_buf_process,
-};
-
-static const struct v4l2_subdev_core_ops hist_subdev_core_ops = {
- .ioctl = hist_ioctl,
- .subscribe_event = omap3isp_stat_subscribe_event,
- .unsubscribe_event = omap3isp_stat_unsubscribe_event,
-};
-
-static const struct v4l2_subdev_video_ops hist_subdev_video_ops = {
- .s_stream = omap3isp_stat_s_stream,
-};
-
-static const struct v4l2_subdev_ops hist_subdev_ops = {
- .core = &hist_subdev_core_ops,
- .video = &hist_subdev_video_ops,
-};
-
-/*
- * omap3isp_hist_init - Module Initialization.
- */
-int omap3isp_hist_init(struct isp_device *isp)
-{
- struct ispstat *hist = &isp->isp_hist;
- struct omap3isp_hist_config *hist_cfg;
- int ret = -1;
-
- hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL);
- if (hist_cfg == NULL)
- return -ENOMEM;
-
- memset(hist, 0, sizeof(*hist));
- if (HIST_CONFIG_DMA)
- ret = omap_request_dma(OMAP24XX_DMA_NO_DEVICE, "DMA_ISP_HIST",
- hist_dma_cb, hist, &hist->dma_ch);
- if (ret) {
- if (HIST_CONFIG_DMA)
- dev_warn(isp->dev, "hist: DMA request channel failed. "
- "Using PIO only.\n");
- hist->dma_ch = -1;
- } else {
- dev_dbg(isp->dev, "hist: DMA channel = %d\n", hist->dma_ch);
- hist_dma_config(hist);
- omap_enable_dma_irq(hist->dma_ch, OMAP_DMA_BLOCK_IRQ);
- }
-
- hist->ops = &hist_ops;
- hist->priv = hist_cfg;
- hist->event_type = V4L2_EVENT_OMAP3ISP_HIST;
- hist->isp = isp;
-
- ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops);
- if (ret) {
- kfree(hist_cfg);
- if (HIST_USING_DMA(hist))
- omap_free_dma(hist->dma_ch);
- }
-
- return ret;
-}
-
-/*
- * omap3isp_hist_cleanup - Module cleanup.
- */
-void omap3isp_hist_cleanup(struct isp_device *isp)
-{
- if (HIST_USING_DMA(&isp->isp_hist))
- omap_free_dma(isp->isp_hist.dma_ch);
- kfree(isp->isp_hist.priv);
- omap3isp_stat_cleanup(&isp->isp_hist);
-}
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
deleted file mode 100644
index 53f5a703e31a..000000000000
--- a/drivers/media/video/omap3isp/isppreview.c
+++ /dev/null
@@ -1,2369 +0,0 @@
-/*
- * isppreview.c
- *
- * TI OMAP3 ISP driver - Preview module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/device.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/uaccess.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "isppreview.h"
-
-/* Default values in Office Fluorescent Light for RGBtoRGB Blending */
-static struct omap3isp_prev_rgbtorgb flr_rgb2rgb = {
- { /* RGB-RGB Matrix */
- {0x01E2, 0x0F30, 0x0FEE},
- {0x0F9B, 0x01AC, 0x0FB9},
- {0x0FE0, 0x0EC0, 0x0260}
- }, /* RGB Offset */
- {0x0000, 0x0000, 0x0000}
-};
-
-/* Default values in Office Fluorescent Light for RGB to YUV Conversion*/
-static struct omap3isp_prev_csc flr_prev_csc = {
- { /* CSC Coef Matrix */
- {66, 129, 25},
- {-38, -75, 112},
- {112, -94 , -18}
- }, /* CSC Offset */
- {0x0, 0x0, 0x0}
-};
-
-/* Default values in Office Fluorescent Light for CFA Gradient*/
-#define FLR_CFA_GRADTHRS_HORZ 0x28
-#define FLR_CFA_GRADTHRS_VERT 0x28
-
-/* Default values in Office Fluorescent Light for Chroma Suppression*/
-#define FLR_CSUP_GAIN 0x0D
-#define FLR_CSUP_THRES 0xEB
-
-/* Default values in Office Fluorescent Light for Noise Filter*/
-#define FLR_NF_STRGTH 0x03
-
-/* Default values for White Balance */
-#define FLR_WBAL_DGAIN 0x100
-#define FLR_WBAL_COEF 0x20
-
-/* Default values in Office Fluorescent Light for Black Adjustment*/
-#define FLR_BLKADJ_BLUE 0x0
-#define FLR_BLKADJ_GREEN 0x0
-#define FLR_BLKADJ_RED 0x0
-
-#define DEF_DETECT_CORRECT_VAL 0xe
-
-/*
- * Margins and image size limits.
- *
- * The preview engine crops several rows and columns internally depending on
- * which filters are enabled. To avoid format changes when the filters are
- * enabled or disabled (which would prevent them from being turned on or off
- * during streaming), the driver assumes all the filters are enabled when
- * computing sink crop and source format limits.
- *
- * If a filter is disabled, additional cropping is automatically added at the
- * preview engine input by the driver to avoid overflow at line and frame end.
- * This is completely transparent for applications.
- *
- * Median filter 4 pixels
- * Noise filter,
- * Faulty pixels correction 4 pixels, 4 lines
- * CFA filter 4 pixels, 4 lines in Bayer mode
- * 2 lines in other modes
- * Color suppression 2 pixels
- * or luma enhancement
- * -------------------------------------------------------------
- * Maximum total 14 pixels, 8 lines
- *
- * The color suppression and luma enhancement filters are applied after bayer to
- * YUV conversion. They thus can crop one pixel on the left and one pixel on the
- * right side of the image without changing the color pattern. When both those
- * filters are disabled, the driver must crop the two pixels on the same side of
- * the image to avoid changing the bayer pattern. The left margin is thus set to
- * 8 pixels and the right margin to 6 pixels.
- */
-
-#define PREV_MARGIN_LEFT 8
-#define PREV_MARGIN_RIGHT 6
-#define PREV_MARGIN_TOP 4
-#define PREV_MARGIN_BOTTOM 4
-
-#define PREV_MIN_IN_WIDTH 64
-#define PREV_MIN_IN_HEIGHT 8
-#define PREV_MAX_IN_HEIGHT 16384
-
-#define PREV_MIN_OUT_WIDTH 0
-#define PREV_MIN_OUT_HEIGHT 0
-#define PREV_MAX_OUT_WIDTH_REV_1 1280
-#define PREV_MAX_OUT_WIDTH_REV_2 3300
-#define PREV_MAX_OUT_WIDTH_REV_15 4096
-
-/*
- * Coeficient Tables for the submodules in Preview.
- * Array is initialised with the values from.the tables text file.
- */
-
-/*
- * CFA Filter Coefficient Table
- *
- */
-static u32 cfa_coef_table[] = {
-#include "cfa_coef_table.h"
-};
-
-/*
- * Default Gamma Correction Table - All components
- */
-static u32 gamma_table[] = {
-#include "gamma_table.h"
-};
-
-/*
- * Noise Filter Threshold table
- */
-static u32 noise_filter_table[] = {
-#include "noise_filter_table.h"
-};
-
-/*
- * Luminance Enhancement Table
- */
-static u32 luma_enhance_table[] = {
-#include "luma_enhance_table.h"
-};
-
-/*
- * preview_enable_invalaw - Enable/Disable Inverse A-Law module in Preview.
- * @enable: 1 - Reverse the A-Law done in CCDC.
- */
-static void
-preview_enable_invalaw(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_WIDTH | ISPPRV_PCR_INVALAW);
-}
-
-/*
- * preview_enable_drkframe_capture - Enable/Disable of the darkframe capture.
- * @prev -
- * @enable: 1 - Enable, 0 - Disable
- *
- * NOTE: PRV_WSDR_ADDR and PRV_WADD_OFFSET must be set also
- * The process is applied for each captured frame.
- */
-static void
-preview_enable_drkframe_capture(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DRKFCAP);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DRKFCAP);
-}
-
-/*
- * preview_enable_drkframe - Enable/Disable of the darkframe subtract.
- * @enable: 1 - Acquires memory bandwidth since the pixels in each frame is
- * subtracted with the pixels in the current frame.
- *
- * The process is applied for each captured frame.
- */
-static void
-preview_enable_drkframe(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DRKFEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DRKFEN);
-}
-
-/*
- * preview_config_drkf_shadcomp - Configures shift value in shading comp.
- * @scomp_shtval: 3bit value of shift used in shading compensation.
- */
-static void
-preview_config_drkf_shadcomp(struct isp_prev_device *prev,
- const void *scomp_shtval)
-{
- struct isp_device *isp = to_isp_device(prev);
- const u32 *shtval = scomp_shtval;
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SCOMP_SFT_MASK,
- *shtval << ISPPRV_PCR_SCOMP_SFT_SHIFT);
-}
-
-/*
- * preview_enable_hmed - Enables/Disables of the Horizontal Median Filter.
- * @enable: 1 - Enables Horizontal Median Filter.
- */
-static void
-preview_enable_hmed(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_HMEDEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_HMEDEN);
-}
-
-/*
- * preview_config_hmed - Configures the Horizontal Median Filter.
- * @prev_hmed: Structure containing the odd and even distance between the
- * pixels in the image along with the filter threshold.
- */
-static void
-preview_config_hmed(struct isp_prev_device *prev, const void *prev_hmed)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_hmed *hmed = prev_hmed;
-
- isp_reg_writel(isp, (hmed->odddist == 1 ? 0 : ISPPRV_HMED_ODDDIST) |
- (hmed->evendist == 1 ? 0 : ISPPRV_HMED_EVENDIST) |
- (hmed->thres << ISPPRV_HMED_THRESHOLD_SHIFT),
- OMAP3_ISP_IOMEM_PREV, ISPPRV_HMED);
-}
-
-/*
- * preview_config_noisefilter - Configures the Noise Filter.
- * @prev_nf: Structure containing the noisefilter table, strength to be used
- * for the noise filter and the defect correction enable flag.
- */
-static void
-preview_config_noisefilter(struct isp_prev_device *prev, const void *prev_nf)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_nf *nf = prev_nf;
- unsigned int i;
-
- isp_reg_writel(isp, nf->spread, OMAP3_ISP_IOMEM_PREV, ISPPRV_NF);
- isp_reg_writel(isp, ISPPRV_NF_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
- for (i = 0; i < OMAP3ISP_PREV_NF_TBL_SIZE; i++) {
- isp_reg_writel(isp, nf->table[i],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
- }
-}
-
-/*
- * preview_config_dcor - Configures the defect correction
- * @prev_dcor: Structure containing the defect correct thresholds
- */
-static void
-preview_config_dcor(struct isp_prev_device *prev, const void *prev_dcor)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_dcor *dcor = prev_dcor;
-
- isp_reg_writel(isp, dcor->detect_correct[0],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR0);
- isp_reg_writel(isp, dcor->detect_correct[1],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR1);
- isp_reg_writel(isp, dcor->detect_correct[2],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR2);
- isp_reg_writel(isp, dcor->detect_correct[3],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CDC_THR3);
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DCCOUP,
- dcor->couplet_mode_en ? ISPPRV_PCR_DCCOUP : 0);
-}
-
-/*
- * preview_config_cfa - Configures the CFA Interpolation parameters.
- * @prev_cfa: Structure containing the CFA interpolation table, CFA format
- * in the image, vertical and horizontal gradient threshold.
- */
-static void
-preview_config_cfa(struct isp_prev_device *prev, const void *prev_cfa)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_cfa *cfa = prev_cfa;
- unsigned int i;
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_CFAFMT_MASK,
- cfa->format << ISPPRV_PCR_CFAFMT_SHIFT);
-
- isp_reg_writel(isp,
- (cfa->gradthrs_vert << ISPPRV_CFA_GRADTH_VER_SHIFT) |
- (cfa->gradthrs_horz << ISPPRV_CFA_GRADTH_HOR_SHIFT),
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CFA);
-
- isp_reg_writel(isp, ISPPRV_CFA_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
-
- for (i = 0; i < OMAP3ISP_PREV_CFA_TBL_SIZE; i++) {
- isp_reg_writel(isp, cfa->table[i],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
- }
-}
-
-/*
- * preview_config_gammacorrn - Configures the Gamma Correction table values
- * @gtable: Structure containing the table for red, blue, green gamma table.
- */
-static void
-preview_config_gammacorrn(struct isp_prev_device *prev, const void *gtable)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_gtables *gt = gtable;
- unsigned int i;
-
- isp_reg_writel(isp, ISPPRV_REDGAMMA_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
- for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
- isp_reg_writel(isp, gt->red[i], OMAP3_ISP_IOMEM_PREV,
- ISPPRV_SET_TBL_DATA);
-
- isp_reg_writel(isp, ISPPRV_GREENGAMMA_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
- for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
- isp_reg_writel(isp, gt->green[i], OMAP3_ISP_IOMEM_PREV,
- ISPPRV_SET_TBL_DATA);
-
- isp_reg_writel(isp, ISPPRV_BLUEGAMMA_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
- for (i = 0; i < OMAP3ISP_PREV_GAMMA_TBL_SIZE; i++)
- isp_reg_writel(isp, gt->blue[i], OMAP3_ISP_IOMEM_PREV,
- ISPPRV_SET_TBL_DATA);
-}
-
-/*
- * preview_config_luma_enhancement - Sets the Luminance Enhancement table.
- * @ytable: Structure containing the table for Luminance Enhancement table.
- */
-static void
-preview_config_luma_enhancement(struct isp_prev_device *prev,
- const void *ytable)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_luma *yt = ytable;
- unsigned int i;
-
- isp_reg_writel(isp, ISPPRV_YENH_TABLE_ADDR,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_ADDR);
- for (i = 0; i < OMAP3ISP_PREV_YENH_TBL_SIZE; i++) {
- isp_reg_writel(isp, yt->table[i],
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SET_TBL_DATA);
- }
-}
-
-/*
- * preview_config_chroma_suppression - Configures the Chroma Suppression.
- * @csup: Structure containing the threshold value for suppression
- * and the hypass filter enable flag.
- */
-static void
-preview_config_chroma_suppression(struct isp_prev_device *prev,
- const void *csup)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_csup *cs = csup;
-
- isp_reg_writel(isp,
- cs->gain | (cs->thres << ISPPRV_CSUP_THRES_SHIFT) |
- (cs->hypf_en << ISPPRV_CSUP_HPYF_SHIFT),
- OMAP3_ISP_IOMEM_PREV, ISPPRV_CSUP);
-}
-
-/*
- * preview_enable_noisefilter - Enables/Disables the Noise Filter.
- * @enable: 1 - Enables the Noise Filter.
- */
-static void
-preview_enable_noisefilter(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_NFEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_NFEN);
-}
-
-/*
- * preview_enable_dcor - Enables/Disables the defect correction.
- * @enable: 1 - Enables the defect correction.
- */
-static void
-preview_enable_dcor(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DCOREN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_DCOREN);
-}
-
-/*
- * preview_enable_gammabypass - Enables/Disables the GammaByPass
- * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB.
- * 0 - Goes through Gamma Correction. input and output is 10bit.
- */
-static void
-preview_enable_gammabypass(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_GAMMA_BYPASS);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_GAMMA_BYPASS);
-}
-
-/*
- * preview_enable_luma_enhancement - Enables/Disables Luminance Enhancement
- * @enable: 1 - Enable the Luminance Enhancement.
- */
-static void
-preview_enable_luma_enhancement(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_YNENHEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_YNENHEN);
-}
-
-/*
- * preview_enable_chroma_suppression - Enables/Disables Chrominance Suppr.
- * @enable: 1 - Enable the Chrominance Suppression.
- */
-static void
-preview_enable_chroma_suppression(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SUPEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SUPEN);
-}
-
-/*
- * preview_config_whitebalance - Configures the White Balance parameters.
- * @prev_wbal: Structure containing the digital gain and white balance
- * coefficient.
- *
- * Coefficient matrix always with default values.
- */
-static void
-preview_config_whitebalance(struct isp_prev_device *prev, const void *prev_wbal)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_wbal *wbal = prev_wbal;
- u32 val;
-
- isp_reg_writel(isp, wbal->dgain, OMAP3_ISP_IOMEM_PREV, ISPPRV_WB_DGAIN);
-
- val = wbal->coef0 << ISPPRV_WBGAIN_COEF0_SHIFT;
- val |= wbal->coef1 << ISPPRV_WBGAIN_COEF1_SHIFT;
- val |= wbal->coef2 << ISPPRV_WBGAIN_COEF2_SHIFT;
- val |= wbal->coef3 << ISPPRV_WBGAIN_COEF3_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_WBGAIN);
-
- isp_reg_writel(isp,
- ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_0_SHIFT |
- ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_1_SHIFT |
- ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N0_2_SHIFT |
- ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N0_3_SHIFT |
- ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_0_SHIFT |
- ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_1_SHIFT |
- ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N1_2_SHIFT |
- ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N1_3_SHIFT |
- ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_0_SHIFT |
- ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_1_SHIFT |
- ISPPRV_WBSEL_COEF0 << ISPPRV_WBSEL_N2_2_SHIFT |
- ISPPRV_WBSEL_COEF1 << ISPPRV_WBSEL_N2_3_SHIFT |
- ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_0_SHIFT |
- ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_1_SHIFT |
- ISPPRV_WBSEL_COEF2 << ISPPRV_WBSEL_N3_2_SHIFT |
- ISPPRV_WBSEL_COEF3 << ISPPRV_WBSEL_N3_3_SHIFT,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_WBSEL);
-}
-
-/*
- * preview_config_blkadj - Configures the Black Adjustment parameters.
- * @prev_blkadj: Structure containing the black adjustment towards red, green,
- * blue.
- */
-static void
-preview_config_blkadj(struct isp_prev_device *prev, const void *prev_blkadj)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_blkadj *blkadj = prev_blkadj;
-
- isp_reg_writel(isp, (blkadj->blue << ISPPRV_BLKADJOFF_B_SHIFT) |
- (blkadj->green << ISPPRV_BLKADJOFF_G_SHIFT) |
- (blkadj->red << ISPPRV_BLKADJOFF_R_SHIFT),
- OMAP3_ISP_IOMEM_PREV, ISPPRV_BLKADJOFF);
-}
-
-/*
- * preview_config_rgb_blending - Configures the RGB-RGB Blending matrix.
- * @rgb2rgb: Structure containing the rgb to rgb blending matrix and the rgb
- * offset.
- */
-static void
-preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_rgbtorgb *rgbrgb = rgb2rgb;
- u32 val;
-
- val = (rgbrgb->matrix[0][0] & 0xfff) << ISPPRV_RGB_MAT1_MTX_RR_SHIFT;
- val |= (rgbrgb->matrix[0][1] & 0xfff) << ISPPRV_RGB_MAT1_MTX_GR_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT1);
-
- val = (rgbrgb->matrix[0][2] & 0xfff) << ISPPRV_RGB_MAT2_MTX_BR_SHIFT;
- val |= (rgbrgb->matrix[1][0] & 0xfff) << ISPPRV_RGB_MAT2_MTX_RG_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT2);
-
- val = (rgbrgb->matrix[1][1] & 0xfff) << ISPPRV_RGB_MAT3_MTX_GG_SHIFT;
- val |= (rgbrgb->matrix[1][2] & 0xfff) << ISPPRV_RGB_MAT3_MTX_BG_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT3);
-
- val = (rgbrgb->matrix[2][0] & 0xfff) << ISPPRV_RGB_MAT4_MTX_RB_SHIFT;
- val |= (rgbrgb->matrix[2][1] & 0xfff) << ISPPRV_RGB_MAT4_MTX_GB_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT4);
-
- val = (rgbrgb->matrix[2][2] & 0xfff) << ISPPRV_RGB_MAT5_MTX_BB_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_MAT5);
-
- val = (rgbrgb->offset[0] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFR_SHIFT;
- val |= (rgbrgb->offset[1] & 0x3ff) << ISPPRV_RGB_OFF1_MTX_OFFG_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF1);
-
- val = (rgbrgb->offset[2] & 0x3ff) << ISPPRV_RGB_OFF2_MTX_OFFB_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_RGB_OFF2);
-}
-
-/*
- * Configures the color space conversion (RGB toYCbYCr) matrix
- * @prev_csc: Structure containing the RGB to YCbYCr matrix and the
- * YCbCr offset.
- */
-static void
-preview_config_csc(struct isp_prev_device *prev, const void *prev_csc)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_csc *csc = prev_csc;
- u32 val;
-
- val = (csc->matrix[0][0] & 0x3ff) << ISPPRV_CSC0_RY_SHIFT;
- val |= (csc->matrix[0][1] & 0x3ff) << ISPPRV_CSC0_GY_SHIFT;
- val |= (csc->matrix[0][2] & 0x3ff) << ISPPRV_CSC0_BY_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC0);
-
- val = (csc->matrix[1][0] & 0x3ff) << ISPPRV_CSC1_RCB_SHIFT;
- val |= (csc->matrix[1][1] & 0x3ff) << ISPPRV_CSC1_GCB_SHIFT;
- val |= (csc->matrix[1][2] & 0x3ff) << ISPPRV_CSC1_BCB_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC1);
-
- val = (csc->matrix[2][0] & 0x3ff) << ISPPRV_CSC2_RCR_SHIFT;
- val |= (csc->matrix[2][1] & 0x3ff) << ISPPRV_CSC2_GCR_SHIFT;
- val |= (csc->matrix[2][2] & 0x3ff) << ISPPRV_CSC2_BCR_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC2);
-
- val = (csc->offset[0] & 0xff) << ISPPRV_CSC_OFFSET_Y_SHIFT;
- val |= (csc->offset[1] & 0xff) << ISPPRV_CSC_OFFSET_CB_SHIFT;
- val |= (csc->offset[2] & 0xff) << ISPPRV_CSC_OFFSET_CR_SHIFT;
- isp_reg_writel(isp, val, OMAP3_ISP_IOMEM_PREV, ISPPRV_CSC_OFFSET);
-}
-
-/*
- * preview_update_contrast - Updates the contrast.
- * @contrast: Pointer to hold the current programmed contrast value.
- *
- * Value should be programmed before enabling the module.
- */
-static void
-preview_update_contrast(struct isp_prev_device *prev, u8 contrast)
-{
- struct prev_params *params;
- unsigned long flags;
-
- spin_lock_irqsave(&prev->params.lock, flags);
- params = (prev->params.active & OMAP3ISP_PREV_CONTRAST)
- ? &prev->params.params[0] : &prev->params.params[1];
-
- if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) {
- params->contrast = contrast * ISPPRV_CONTRAST_UNITS;
- params->update |= OMAP3ISP_PREV_CONTRAST;
- }
- spin_unlock_irqrestore(&prev->params.lock, flags);
-}
-
-/*
- * preview_config_contrast - Configures the Contrast.
- * @params: Contrast value (u8 pointer, U8Q0 format).
- *
- * Value should be programmed before enabling the module.
- */
-static void
-preview_config_contrast(struct isp_prev_device *prev, const void *params)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
- 0xff << ISPPRV_CNT_BRT_CNT_SHIFT,
- *(u8 *)params << ISPPRV_CNT_BRT_CNT_SHIFT);
-}
-
-/*
- * preview_update_brightness - Updates the brightness in preview module.
- * @brightness: Pointer to hold the current programmed brightness value.
- *
- */
-static void
-preview_update_brightness(struct isp_prev_device *prev, u8 brightness)
-{
- struct prev_params *params;
- unsigned long flags;
-
- spin_lock_irqsave(&prev->params.lock, flags);
- params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS)
- ? &prev->params.params[0] : &prev->params.params[1];
-
- if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) {
- params->brightness = brightness * ISPPRV_BRIGHT_UNITS;
- params->update |= OMAP3ISP_PREV_BRIGHTNESS;
- }
- spin_unlock_irqrestore(&prev->params.lock, flags);
-}
-
-/*
- * preview_config_brightness - Configures the brightness.
- * @params: Brightness value (u8 pointer, U8Q0 format).
- */
-static void
-preview_config_brightness(struct isp_prev_device *prev, const void *params)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_CNT_BRT,
- 0xff << ISPPRV_CNT_BRT_BRT_SHIFT,
- *(u8 *)params << ISPPRV_CNT_BRT_BRT_SHIFT);
-}
-
-/*
- * preview_config_yc_range - Configures the max and min Y and C values.
- * @yclimit: Structure containing the range of Y and C values.
- */
-static void
-preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit)
-{
- struct isp_device *isp = to_isp_device(prev);
- const struct omap3isp_prev_yclimit *yc = yclimit;
-
- isp_reg_writel(isp,
- yc->maxC << ISPPRV_SETUP_YC_MAXC_SHIFT |
- yc->maxY << ISPPRV_SETUP_YC_MAXY_SHIFT |
- yc->minC << ISPPRV_SETUP_YC_MINC_SHIFT |
- yc->minY << ISPPRV_SETUP_YC_MINY_SHIFT,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC);
-}
-
-static u32
-preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow)
-{
- u32 active = prev->params.active;
-
- if (shadow) {
- /* Mark all shadow parameters we are going to touch as busy. */
- prev->params.params[0].busy |= ~active & update;
- prev->params.params[1].busy |= active & update;
- } else {
- /* Mark all active parameters we are going to touch as busy. */
- update = (prev->params.params[0].update & active)
- | (prev->params.params[1].update & ~active);
-
- prev->params.params[0].busy |= active & update;
- prev->params.params[1].busy |= ~active & update;
- }
-
- return update;
-}
-
-static void
-preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow)
-{
- u32 active = prev->params.active;
-
- if (shadow) {
- /* Set the update flag for shadow parameters that have been
- * updated and clear the busy flag for all shadow parameters.
- */
- prev->params.params[0].update |= (~active & update);
- prev->params.params[1].update |= (active & update);
- prev->params.params[0].busy &= active;
- prev->params.params[1].busy &= ~active;
- } else {
- /* Clear the update flag for active parameters that have been
- * applied and the busy flag for all active parameters.
- */
- prev->params.params[0].update &= ~(active & update);
- prev->params.params[1].update &= ~(~active & update);
- prev->params.params[0].busy &= ~active;
- prev->params.params[1].busy &= active;
- }
-}
-
-static void preview_params_switch(struct isp_prev_device *prev)
-{
- u32 to_switch;
-
- /* Switch active parameters with updated shadow parameters when the
- * shadow parameter has been updated and neither the active not the
- * shadow parameter is busy.
- */
- to_switch = (prev->params.params[0].update & ~prev->params.active)
- | (prev->params.params[1].update & prev->params.active);
- to_switch &= ~(prev->params.params[0].busy |
- prev->params.params[1].busy);
- if (to_switch == 0)
- return;
-
- prev->params.active ^= to_switch;
-
- /* Remove the update flag for the shadow copy of parameters we have
- * switched.
- */
- prev->params.params[0].update &= ~(~prev->params.active & to_switch);
- prev->params.params[1].update &= ~(prev->params.active & to_switch);
-}
-
-/* preview parameters update structure */
-struct preview_update {
- void (*config)(struct isp_prev_device *, const void *);
- void (*enable)(struct isp_prev_device *, u8);
- unsigned int param_offset;
- unsigned int param_size;
- unsigned int config_offset;
- bool skip;
-};
-
-/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */
-static const struct preview_update update_attrs[] = {
- /* OMAP3ISP_PREV_LUMAENH */ {
- preview_config_luma_enhancement,
- preview_enable_luma_enhancement,
- offsetof(struct prev_params, luma),
- FIELD_SIZEOF(struct prev_params, luma),
- offsetof(struct omap3isp_prev_update_config, luma),
- }, /* OMAP3ISP_PREV_INVALAW */ {
- NULL,
- preview_enable_invalaw,
- }, /* OMAP3ISP_PREV_HRZ_MED */ {
- preview_config_hmed,
- preview_enable_hmed,
- offsetof(struct prev_params, hmed),
- FIELD_SIZEOF(struct prev_params, hmed),
- offsetof(struct omap3isp_prev_update_config, hmed),
- }, /* OMAP3ISP_PREV_CFA */ {
- preview_config_cfa,
- NULL,
- offsetof(struct prev_params, cfa),
- FIELD_SIZEOF(struct prev_params, cfa),
- offsetof(struct omap3isp_prev_update_config, cfa),
- }, /* OMAP3ISP_PREV_CHROMA_SUPP */ {
- preview_config_chroma_suppression,
- preview_enable_chroma_suppression,
- offsetof(struct prev_params, csup),
- FIELD_SIZEOF(struct prev_params, csup),
- offsetof(struct omap3isp_prev_update_config, csup),
- }, /* OMAP3ISP_PREV_WB */ {
- preview_config_whitebalance,
- NULL,
- offsetof(struct prev_params, wbal),
- FIELD_SIZEOF(struct prev_params, wbal),
- offsetof(struct omap3isp_prev_update_config, wbal),
- }, /* OMAP3ISP_PREV_BLKADJ */ {
- preview_config_blkadj,
- NULL,
- offsetof(struct prev_params, blkadj),
- FIELD_SIZEOF(struct prev_params, blkadj),
- offsetof(struct omap3isp_prev_update_config, blkadj),
- }, /* OMAP3ISP_PREV_RGB2RGB */ {
- preview_config_rgb_blending,
- NULL,
- offsetof(struct prev_params, rgb2rgb),
- FIELD_SIZEOF(struct prev_params, rgb2rgb),
- offsetof(struct omap3isp_prev_update_config, rgb2rgb),
- }, /* OMAP3ISP_PREV_COLOR_CONV */ {
- preview_config_csc,
- NULL,
- offsetof(struct prev_params, csc),
- FIELD_SIZEOF(struct prev_params, csc),
- offsetof(struct omap3isp_prev_update_config, csc),
- }, /* OMAP3ISP_PREV_YC_LIMIT */ {
- preview_config_yc_range,
- NULL,
- offsetof(struct prev_params, yclimit),
- FIELD_SIZEOF(struct prev_params, yclimit),
- offsetof(struct omap3isp_prev_update_config, yclimit),
- }, /* OMAP3ISP_PREV_DEFECT_COR */ {
- preview_config_dcor,
- preview_enable_dcor,
- offsetof(struct prev_params, dcor),
- FIELD_SIZEOF(struct prev_params, dcor),
- offsetof(struct omap3isp_prev_update_config, dcor),
- }, /* OMAP3ISP_PREV_GAMMABYPASS */ {
- NULL,
- preview_enable_gammabypass,
- }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ {
- NULL,
- preview_enable_drkframe_capture,
- }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ {
- NULL,
- preview_enable_drkframe,
- }, /* OMAP3ISP_PREV_LENS_SHADING */ {
- preview_config_drkf_shadcomp,
- preview_enable_drkframe,
- }, /* OMAP3ISP_PREV_NF */ {
- preview_config_noisefilter,
- preview_enable_noisefilter,
- offsetof(struct prev_params, nf),
- FIELD_SIZEOF(struct prev_params, nf),
- offsetof(struct omap3isp_prev_update_config, nf),
- }, /* OMAP3ISP_PREV_GAMMA */ {
- preview_config_gammacorrn,
- NULL,
- offsetof(struct prev_params, gamma),
- FIELD_SIZEOF(struct prev_params, gamma),
- offsetof(struct omap3isp_prev_update_config, gamma),
- }, /* OMAP3ISP_PREV_CONTRAST */ {
- preview_config_contrast,
- NULL,
- offsetof(struct prev_params, contrast),
- 0, 0, true,
- }, /* OMAP3ISP_PREV_BRIGHTNESS */ {
- preview_config_brightness,
- NULL,
- offsetof(struct prev_params, brightness),
- 0, 0, true,
- },
-};
-
-/*
- * preview_config - Copy and update local structure with userspace preview
- * configuration.
- * @prev: ISP preview engine
- * @cfg: Configuration
- *
- * Return zero if success or -EFAULT if the configuration can't be copied from
- * userspace.
- */
-static int preview_config(struct isp_prev_device *prev,
- struct omap3isp_prev_update_config *cfg)
-{
- unsigned long flags;
- unsigned int i;
- int rval = 0;
- u32 update;
- u32 active;
-
- if (cfg->update == 0)
- return 0;
-
- /* Mark the shadow parameters we're going to update as busy. */
- spin_lock_irqsave(&prev->params.lock, flags);
- preview_params_lock(prev, cfg->update, true);
- active = prev->params.active;
- spin_unlock_irqrestore(&prev->params.lock, flags);
-
- update = 0;
-
- for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
- const struct preview_update *attr = &update_attrs[i];
- struct prev_params *params;
- unsigned int bit = 1 << i;
-
- if (attr->skip || !(cfg->update & bit))
- continue;
-
- params = &prev->params.params[!!(active & bit)];
-
- if (cfg->flag & bit) {
- void __user *from = *(void * __user *)
- ((void *)cfg + attr->config_offset);
- void *to = (void *)params + attr->param_offset;
- size_t size = attr->param_size;
-
- if (to && from && size) {
- if (copy_from_user(to, from, size)) {
- rval = -EFAULT;
- break;
- }
- }
- params->features |= bit;
- } else {
- params->features &= ~bit;
- }
-
- update |= bit;
- }
-
- spin_lock_irqsave(&prev->params.lock, flags);
- preview_params_unlock(prev, update, true);
- preview_params_switch(prev);
- spin_unlock_irqrestore(&prev->params.lock, flags);
-
- return rval;
-}
-
-/*
- * preview_setup_hw - Setup preview registers and/or internal memory
- * @prev: pointer to preview private structure
- * @update: Bitmask of parameters to setup
- * @active: Bitmask of parameters active in set 0
- * Note: can be called from interrupt context
- * Return none
- */
-static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
- u32 active)
-{
- unsigned int i;
- u32 features;
-
- if (update == 0)
- return;
-
- features = (prev->params.params[0].features & active)
- | (prev->params.params[1].features & ~active);
-
- for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
- const struct preview_update *attr = &update_attrs[i];
- struct prev_params *params;
- unsigned int bit = 1 << i;
- void *param_ptr;
-
- if (!(update & bit))
- continue;
-
- params = &prev->params.params[!(active & bit)];
-
- if (params->features & bit) {
- if (attr->config) {
- param_ptr = (void *)params + attr->param_offset;
- attr->config(prev, param_ptr);
- }
- if (attr->enable)
- attr->enable(prev, 1);
- } else {
- if (attr->enable)
- attr->enable(prev, 0);
- }
- }
-}
-
-/*
- * preview_config_ycpos - Configure byte layout of YUV image.
- * @mode: Indicates the required byte layout.
- */
-static void
-preview_config_ycpos(struct isp_prev_device *prev,
- enum v4l2_mbus_pixelcode pixelcode)
-{
- struct isp_device *isp = to_isp_device(prev);
- enum preview_ycpos_mode mode;
-
- switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
- mode = YCPOS_CrYCbY;
- break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
- mode = YCPOS_YCrYCb;
- break;
- default:
- return;
- }
-
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_YCPOS_CrYCbY,
- mode << ISPPRV_PCR_YCPOS_SHIFT);
-}
-
-/*
- * preview_config_averager - Enable / disable / configure averager
- * @average: Average value to be configured.
- */
-static void preview_config_averager(struct isp_prev_device *prev, u8 average)
-{
- struct isp_device *isp = to_isp_device(prev);
- struct prev_params *params;
- int reg = 0;
-
- params = (prev->params.active & OMAP3ISP_PREV_CFA)
- ? &prev->params.params[0] : &prev->params.params[1];
-
- if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER)
- reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT |
- ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT |
- average;
- else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
- reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT |
- ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT |
- average;
- isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE);
-}
-
-/*
- * preview_config_input_format - Configure the input format
- * @prev: The preview engine
- * @format: Format on the preview engine sink pad
- *
- * Enable CFA interpolation for Bayer formats and disable it for greyscale
- * formats.
- */
-static void preview_config_input_format(struct isp_prev_device *prev,
- const struct v4l2_mbus_framefmt *format)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (format->code != V4L2_MBUS_FMT_Y10_1X10)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_CFAEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_CFAEN);
-}
-
-/*
- * preview_config_input_size - Configure the input frame size
- *
- * The preview engine crops several rows and columns internally depending on
- * which processing blocks are enabled. The driver assumes all those blocks are
- * enabled when reporting source pad formats to userspace. If this assumption is
- * not true, rows and columns must be manually cropped at the preview engine
- * input to avoid overflows at the end of lines and frames.
- *
- * See the explanation at the PREV_MARGIN_* definitions for more details.
- */
-static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
-{
- const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
- struct isp_device *isp = to_isp_device(prev);
- unsigned int sph = prev->crop.left;
- unsigned int eph = prev->crop.left + prev->crop.width - 1;
- unsigned int slv = prev->crop.top;
- unsigned int elv = prev->crop.top + prev->crop.height - 1;
- u32 features;
-
- if (format->code != V4L2_MBUS_FMT_Y10_1X10) {
- sph -= 2;
- eph += 2;
- slv -= 2;
- elv += 2;
- }
-
- features = (prev->params.params[0].features & active)
- | (prev->params.params[1].features & ~active);
-
- if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) {
- sph -= 2;
- eph += 2;
- slv -= 2;
- elv += 2;
- }
- if (features & OMAP3ISP_PREV_HRZ_MED) {
- sph -= 2;
- eph += 2;
- }
- if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH))
- sph -= 2;
-
- isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_HORZ_INFO);
- isp_reg_writel(isp, (slv << ISPPRV_VERT_INFO_SLV_SHIFT) | elv,
- OMAP3_ISP_IOMEM_PREV, ISPPRV_VERT_INFO);
-}
-
-/*
- * preview_config_inlineoffset - Configures the Read address line offset.
- * @prev: Preview module
- * @offset: Line offset
- *
- * According to the TRM, the line offset must be aligned on a 32 bytes boundary.
- * However, a hardware bug requires the memory start address to be aligned on a
- * 64 bytes boundary, so the offset probably should be aligned on 64 bytes as
- * well.
- */
-static void
-preview_config_inlineoffset(struct isp_prev_device *prev, u32 offset)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
- ISPPRV_RADR_OFFSET);
-}
-
-/*
- * preview_set_inaddr - Sets memory address of input frame.
- * @addr: 32bit memory address aligned on 32byte boundary.
- *
- * Configures the memory address from which the input frame is to be read.
- */
-static void preview_set_inaddr(struct isp_prev_device *prev, u32 addr)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_RSDR_ADDR);
-}
-
-/*
- * preview_config_outlineoffset - Configures the Write address line offset.
- * @offset: Line Offset for the preview output.
- *
- * The offset must be a multiple of 32 bytes.
- */
-static void preview_config_outlineoffset(struct isp_prev_device *prev,
- u32 offset)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_writel(isp, offset & 0xffff, OMAP3_ISP_IOMEM_PREV,
- ISPPRV_WADD_OFFSET);
-}
-
-/*
- * preview_set_outaddr - Sets the memory address to store output frame
- * @addr: 32bit memory address aligned on 32byte boundary.
- *
- * Configures the memory address to which the output frame is written.
- */
-static void preview_set_outaddr(struct isp_prev_device *prev, u32 addr)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_PREV, ISPPRV_WSDR_ADDR);
-}
-
-static void preview_adjust_bandwidth(struct isp_prev_device *prev)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
- struct isp_device *isp = to_isp_device(prev);
- const struct v4l2_mbus_framefmt *ifmt = &prev->formats[PREV_PAD_SINK];
- unsigned long l3_ick = pipe->l3_ick;
- struct v4l2_fract *timeperframe;
- unsigned int cycles_per_frame;
- unsigned int requests_per_frame;
- unsigned int cycles_per_request;
- unsigned int minimum;
- unsigned int maximum;
- unsigned int value;
-
- if (prev->input != PREVIEW_INPUT_MEMORY) {
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
- ISPSBL_SDR_REQ_PRV_EXP_MASK);
- return;
- }
-
- /* Compute the minimum number of cycles per request, based on the
- * pipeline maximum data rate. This is an absolute lower bound if we
- * don't want SBL overflows, so round the value up.
- */
- cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
- pipe->max_rate);
- minimum = DIV_ROUND_UP(cycles_per_request, 32);
-
- /* Compute the maximum number of cycles per request, based on the
- * requested frame rate. This is a soft upper bound to achieve a frame
- * rate equal or higher than the requested value, so round the value
- * down.
- */
- timeperframe = &pipe->max_timeperframe;
-
- requests_per_frame = DIV_ROUND_UP(ifmt->width * 2, 256) * ifmt->height;
- cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
- timeperframe->denominator);
- cycles_per_request = cycles_per_frame / requests_per_frame;
-
- maximum = cycles_per_request / 32;
-
- value = max(minimum, maximum);
-
- dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
- ISPSBL_SDR_REQ_PRV_EXP_MASK,
- value << ISPSBL_SDR_REQ_PRV_EXP_SHIFT);
-}
-
-/*
- * omap3isp_preview_busy - Gets busy state of preview module.
- */
-int omap3isp_preview_busy(struct isp_prev_device *prev)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- return isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR)
- & ISPPRV_PCR_BUSY;
-}
-
-/*
- * omap3isp_preview_restore_context - Restores the values of preview registers
- */
-void omap3isp_preview_restore_context(struct isp_device *isp)
-{
- struct isp_prev_device *prev = &isp->isp_prev;
- const u32 update = OMAP3ISP_PREV_FEATURES_END - 1;
-
- prev->params.params[0].update = prev->params.active & update;
- prev->params.params[1].update = ~prev->params.active & update;
-
- preview_setup_hw(prev, update, prev->params.active);
-
- prev->params.params[0].update = 0;
- prev->params.params[1].update = 0;
-}
-
-/*
- * preview_print_status - Dump preview module registers to the kernel log
- */
-#define PREV_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###PRV " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_##name))
-
-static void preview_print_status(struct isp_prev_device *prev)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- dev_dbg(isp->dev, "-------------Preview Register dump----------\n");
-
- PREV_PRINT_REGISTER(isp, PCR);
- PREV_PRINT_REGISTER(isp, HORZ_INFO);
- PREV_PRINT_REGISTER(isp, VERT_INFO);
- PREV_PRINT_REGISTER(isp, RSDR_ADDR);
- PREV_PRINT_REGISTER(isp, RADR_OFFSET);
- PREV_PRINT_REGISTER(isp, DSDR_ADDR);
- PREV_PRINT_REGISTER(isp, DRKF_OFFSET);
- PREV_PRINT_REGISTER(isp, WSDR_ADDR);
- PREV_PRINT_REGISTER(isp, WADD_OFFSET);
- PREV_PRINT_REGISTER(isp, AVE);
- PREV_PRINT_REGISTER(isp, HMED);
- PREV_PRINT_REGISTER(isp, NF);
- PREV_PRINT_REGISTER(isp, WB_DGAIN);
- PREV_PRINT_REGISTER(isp, WBGAIN);
- PREV_PRINT_REGISTER(isp, WBSEL);
- PREV_PRINT_REGISTER(isp, CFA);
- PREV_PRINT_REGISTER(isp, BLKADJOFF);
- PREV_PRINT_REGISTER(isp, RGB_MAT1);
- PREV_PRINT_REGISTER(isp, RGB_MAT2);
- PREV_PRINT_REGISTER(isp, RGB_MAT3);
- PREV_PRINT_REGISTER(isp, RGB_MAT4);
- PREV_PRINT_REGISTER(isp, RGB_MAT5);
- PREV_PRINT_REGISTER(isp, RGB_OFF1);
- PREV_PRINT_REGISTER(isp, RGB_OFF2);
- PREV_PRINT_REGISTER(isp, CSC0);
- PREV_PRINT_REGISTER(isp, CSC1);
- PREV_PRINT_REGISTER(isp, CSC2);
- PREV_PRINT_REGISTER(isp, CSC_OFFSET);
- PREV_PRINT_REGISTER(isp, CNT_BRT);
- PREV_PRINT_REGISTER(isp, CSUP);
- PREV_PRINT_REGISTER(isp, SETUP_YC);
- PREV_PRINT_REGISTER(isp, SET_TBL_ADDR);
- PREV_PRINT_REGISTER(isp, CDC_THR0);
- PREV_PRINT_REGISTER(isp, CDC_THR1);
- PREV_PRINT_REGISTER(isp, CDC_THR2);
- PREV_PRINT_REGISTER(isp, CDC_THR3);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
-/*
- * preview_init_params - init image processing parameters.
- * @prev: pointer to previewer private structure
- */
-static void preview_init_params(struct isp_prev_device *prev)
-{
- struct prev_params *params;
- unsigned int i;
-
- spin_lock_init(&prev->params.lock);
-
- prev->params.active = ~0;
- prev->params.params[0].busy = 0;
- prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1;
- prev->params.params[1].busy = 0;
- prev->params.params[1].update = 0;
-
- params = &prev->params.params[0];
-
- /* Init values */
- params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
- params->brightness = ISPPRV_BRIGHT_DEF * ISPPRV_BRIGHT_UNITS;
- params->cfa.format = OMAP3ISP_CFAFMT_BAYER;
- memcpy(params->cfa.table, cfa_coef_table,
- sizeof(params->cfa.table));
- params->cfa.gradthrs_horz = FLR_CFA_GRADTHRS_HORZ;
- params->cfa.gradthrs_vert = FLR_CFA_GRADTHRS_VERT;
- params->csup.gain = FLR_CSUP_GAIN;
- params->csup.thres = FLR_CSUP_THRES;
- params->csup.hypf_en = 0;
- memcpy(params->luma.table, luma_enhance_table,
- sizeof(params->luma.table));
- params->nf.spread = FLR_NF_STRGTH;
- memcpy(params->nf.table, noise_filter_table, sizeof(params->nf.table));
- params->dcor.couplet_mode_en = 1;
- for (i = 0; i < OMAP3ISP_PREV_DETECT_CORRECT_CHANNELS; i++)
- params->dcor.detect_correct[i] = DEF_DETECT_CORRECT_VAL;
- memcpy(params->gamma.blue, gamma_table, sizeof(params->gamma.blue));
- memcpy(params->gamma.green, gamma_table, sizeof(params->gamma.green));
- memcpy(params->gamma.red, gamma_table, sizeof(params->gamma.red));
- params->wbal.dgain = FLR_WBAL_DGAIN;
- params->wbal.coef0 = FLR_WBAL_COEF;
- params->wbal.coef1 = FLR_WBAL_COEF;
- params->wbal.coef2 = FLR_WBAL_COEF;
- params->wbal.coef3 = FLR_WBAL_COEF;
- params->blkadj.red = FLR_BLKADJ_RED;
- params->blkadj.green = FLR_BLKADJ_GREEN;
- params->blkadj.blue = FLR_BLKADJ_BLUE;
- params->rgb2rgb = flr_rgb2rgb;
- params->csc = flr_prev_csc;
- params->yclimit.minC = ISPPRV_YC_MIN;
- params->yclimit.maxC = ISPPRV_YC_MAX;
- params->yclimit.minY = ISPPRV_YC_MIN;
- params->yclimit.maxY = ISPPRV_YC_MAX;
-
- params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR
- | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA
- | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT
- | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV
- | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS
- | OMAP3ISP_PREV_CONTRAST;
-}
-
-/*
- * preview_max_out_width - Handle previewer hardware ouput limitations
- * @isp_revision : ISP revision
- * returns maximum width output for current isp revision
- */
-static unsigned int preview_max_out_width(struct isp_prev_device *prev)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- switch (isp->revision) {
- case ISP_REVISION_1_0:
- return PREV_MAX_OUT_WIDTH_REV_1;
-
- case ISP_REVISION_2_0:
- default:
- return PREV_MAX_OUT_WIDTH_REV_2;
-
- case ISP_REVISION_15_0:
- return PREV_MAX_OUT_WIDTH_REV_15;
- }
-}
-
-static void preview_configure(struct isp_prev_device *prev)
-{
- struct isp_device *isp = to_isp_device(prev);
- struct v4l2_mbus_framefmt *format;
- unsigned long flags;
- u32 update;
- u32 active;
-
- spin_lock_irqsave(&prev->params.lock, flags);
- /* Mark all active parameters we are going to touch as busy. */
- update = preview_params_lock(prev, 0, false);
- active = prev->params.active;
- spin_unlock_irqrestore(&prev->params.lock, flags);
-
- preview_setup_hw(prev, update, active);
-
- if (prev->output & PREVIEW_OUTPUT_MEMORY)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SDRPORT);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SDRPORT);
-
- if (prev->output & PREVIEW_OUTPUT_RESIZER)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_RSZPORT);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_RSZPORT);
-
- /* PREV_PAD_SINK */
- format = &prev->formats[PREV_PAD_SINK];
-
- preview_adjust_bandwidth(prev);
-
- preview_config_input_format(prev, format);
- preview_config_input_size(prev, active);
-
- if (prev->input == PREVIEW_INPUT_CCDC)
- preview_config_inlineoffset(prev, 0);
- else
- preview_config_inlineoffset(prev,
- ALIGN(format->width, 0x20) * 2);
-
- /* PREV_PAD_SOURCE */
- format = &prev->formats[PREV_PAD_SOURCE];
-
- if (prev->output & PREVIEW_OUTPUT_MEMORY)
- preview_config_outlineoffset(prev,
- ALIGN(format->width, 0x10) * 2);
-
- preview_config_averager(prev, 0);
- preview_config_ycpos(prev, format->code);
-
- spin_lock_irqsave(&prev->params.lock, flags);
- preview_params_unlock(prev, update, false);
- spin_unlock_irqrestore(&prev->params.lock, flags);
-}
-
-/* -----------------------------------------------------------------------------
- * Interrupt handling
- */
-
-static void preview_enable_oneshot(struct isp_prev_device *prev)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- /* The PCR.SOURCE bit is automatically reset to 0 when the PCR.ENABLE
- * bit is set. As the preview engine is used in single-shot mode, we
- * need to set PCR.SOURCE before enabling the preview engine.
- */
- if (prev->input == PREVIEW_INPUT_MEMORY)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_SOURCE);
-
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_EN | ISPPRV_PCR_ONESHOT);
-}
-
-void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev)
-{
- /*
- * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
- * condition, the module was paused and now we have a buffer queued
- * on the output again. Restart the pipeline if running in continuous
- * mode.
- */
- if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
- prev->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
- preview_enable_oneshot(prev);
- isp_video_dmaqueue_flags_clr(&prev->video_out);
- }
-}
-
-static void preview_isr_buffer(struct isp_prev_device *prev)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&prev->subdev.entity);
- struct isp_buffer *buffer;
- int restart = 0;
-
- if (prev->input == PREVIEW_INPUT_MEMORY) {
- buffer = omap3isp_video_buffer_next(&prev->video_in);
- if (buffer != NULL)
- preview_set_inaddr(prev, buffer->isp_addr);
- pipe->state |= ISP_PIPELINE_IDLE_INPUT;
- }
-
- if (prev->output & PREVIEW_OUTPUT_MEMORY) {
- buffer = omap3isp_video_buffer_next(&prev->video_out);
- if (buffer != NULL) {
- preview_set_outaddr(prev, buffer->isp_addr);
- restart = 1;
- }
- pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
- }
-
- switch (prev->state) {
- case ISP_PIPELINE_STREAM_SINGLESHOT:
- if (isp_pipeline_ready(pipe))
- omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_SINGLESHOT);
- break;
-
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- /* If an underrun occurs, the video queue operation handler will
- * restart the preview engine. Otherwise restart it immediately.
- */
- if (restart)
- preview_enable_oneshot(prev);
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- default:
- return;
- }
-}
-
-/*
- * omap3isp_preview_isr - ISP preview engine interrupt handler
- *
- * Manage the preview engine video buffers and configure shadowed registers.
- */
-void omap3isp_preview_isr(struct isp_prev_device *prev)
-{
- unsigned long flags;
- u32 update;
- u32 active;
-
- if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping))
- return;
-
- spin_lock_irqsave(&prev->params.lock, flags);
- preview_params_switch(prev);
- update = preview_params_lock(prev, 0, false);
- active = prev->params.active;
- spin_unlock_irqrestore(&prev->params.lock, flags);
-
- preview_setup_hw(prev, update, active);
- preview_config_input_size(prev, active);
-
- if (prev->input == PREVIEW_INPUT_MEMORY ||
- prev->output & PREVIEW_OUTPUT_MEMORY)
- preview_isr_buffer(prev);
- else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS)
- preview_enable_oneshot(prev);
-
- spin_lock_irqsave(&prev->params.lock, flags);
- preview_params_unlock(prev, update, false);
- spin_unlock_irqrestore(&prev->params.lock, flags);
-}
-
-/* -----------------------------------------------------------------------------
- * ISP video operations
- */
-
-static int preview_video_queue(struct isp_video *video,
- struct isp_buffer *buffer)
-{
- struct isp_prev_device *prev = &video->isp->isp_prev;
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- preview_set_inaddr(prev, buffer->isp_addr);
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- preview_set_outaddr(prev, buffer->isp_addr);
-
- return 0;
-}
-
-static const struct isp_video_operations preview_video_ops = {
- .queue = preview_video_queue,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev operations
- */
-
-/*
- * preview_s_ctrl - Handle set control subdev method
- * @ctrl: pointer to v4l2 control structure
- */
-static int preview_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct isp_prev_device *prev =
- container_of(ctrl->handler, struct isp_prev_device, ctrls);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- preview_update_brightness(prev, ctrl->val);
- break;
- case V4L2_CID_CONTRAST:
- preview_update_contrast(prev, ctrl->val);
- break;
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops preview_ctrl_ops = {
- .s_ctrl = preview_s_ctrl,
-};
-
-/*
- * preview_ioctl - Handle preview module private ioctl's
- * @prev: pointer to preview context structure
- * @cmd: configuration command
- * @arg: configuration argument
- * return -EINVAL or zero on success
- */
-static long preview_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
-
- switch (cmd) {
- case VIDIOC_OMAP3ISP_PRV_CFG:
- return preview_config(prev, arg);
-
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-/*
- * preview_set_stream - Enable/Disable streaming on preview subdev
- * @sd : pointer to v4l2 subdev structure
- * @enable: 1 == Enable, 0 == Disable
- * return -EINVAL or zero on success
- */
-static int preview_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct isp_video *video_out = &prev->video_out;
- struct isp_device *isp = to_isp_device(prev);
- struct device *dev = to_device(prev);
-
- if (prev->state == ISP_PIPELINE_STREAM_STOPPED) {
- if (enable == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
-
- omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
- preview_configure(prev);
- atomic_set(&prev->stopping, 0);
- preview_print_status(prev);
- }
-
- switch (enable) {
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- if (prev->output & PREVIEW_OUTPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
-
- if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED ||
- !(prev->output & PREVIEW_OUTPUT_MEMORY))
- preview_enable_oneshot(prev);
-
- isp_video_dmaqueue_flags_clr(video_out);
- break;
-
- case ISP_PIPELINE_STREAM_SINGLESHOT:
- if (prev->input == PREVIEW_INPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
- if (prev->output & PREVIEW_OUTPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
-
- preview_enable_oneshot(prev);
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- if (omap3isp_module_sync_idle(&sd->entity, &prev->wait,
- &prev->stopping))
- dev_dbg(dev, "%s: stop timeout.\n", sd->name);
- omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
- omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
- omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
- isp_video_dmaqueue_flags_clr(video_out);
- break;
- }
-
- prev->state = enable;
- return 0;
-}
-
-static struct v4l2_mbus_framefmt *
-__preview_get_format(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(fh, pad);
- else
- return &prev->formats[pad];
-}
-
-static struct v4l2_rect *
-__preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(fh, PREV_PAD_SINK);
- else
- return &prev->crop;
-}
-
-/* previewer format descriptions */
-static const unsigned int preview_input_fmts[] = {
- V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10,
-};
-
-static const unsigned int preview_output_fmts[] = {
- V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16,
-};
-
-/*
- * preview_try_format - Validate a format
- * @prev: ISP preview engine
- * @fh: V4L2 subdev file handle
- * @pad: pad number
- * @fmt: format to be validated
- * @which: try/active format selector
- *
- * Validate and adjust the given format for the given pad based on the preview
- * engine limits and the format and crop rectangles on other pads.
- */
-static void preview_try_format(struct isp_prev_device *prev,
- struct v4l2_subdev_fh *fh, unsigned int pad,
- struct v4l2_mbus_framefmt *fmt,
- enum v4l2_subdev_format_whence which)
-{
- enum v4l2_mbus_pixelcode pixelcode;
- struct v4l2_rect *crop;
- unsigned int i;
-
- switch (pad) {
- case PREV_PAD_SINK:
- /* When reading data from the CCDC, the input size has already
- * been mangled by the CCDC output pad so it can be accepted
- * as-is.
- *
- * When reading data from memory, clamp the requested width and
- * height. The TRM doesn't specify a minimum input height, make
- * sure we got enough lines to enable the noise filter and color
- * filter array interpolation.
- */
- if (prev->input == PREVIEW_INPUT_MEMORY) {
- fmt->width = clamp_t(u32, fmt->width, PREV_MIN_IN_WIDTH,
- preview_max_out_width(prev));
- fmt->height = clamp_t(u32, fmt->height,
- PREV_MIN_IN_HEIGHT,
- PREV_MAX_IN_HEIGHT);
- }
-
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
-
- for (i = 0; i < ARRAY_SIZE(preview_input_fmts); i++) {
- if (fmt->code == preview_input_fmts[i])
- break;
- }
-
- /* If not found, use SGRBG10 as default */
- if (i >= ARRAY_SIZE(preview_input_fmts))
- fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10;
- break;
-
- case PREV_PAD_SOURCE:
- pixelcode = fmt->code;
- *fmt = *__preview_get_format(prev, fh, PREV_PAD_SINK, which);
-
- switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
- case V4L2_MBUS_FMT_UYVY8_1X16:
- fmt->code = pixelcode;
- break;
-
- default:
- fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
- break;
- }
-
- /* The preview module output size is configurable through the
- * averager (horizontal scaling by 1/1, 1/2, 1/4 or 1/8). This
- * is not supported yet, hardcode the output size to the crop
- * rectangle size.
- */
- crop = __preview_get_crop(prev, fh, which);
- fmt->width = crop->width;
- fmt->height = crop->height;
-
- fmt->colorspace = V4L2_COLORSPACE_JPEG;
- break;
- }
-
- fmt->field = V4L2_FIELD_NONE;
-}
-
-/*
- * preview_try_crop - Validate a crop rectangle
- * @prev: ISP preview engine
- * @sink: format on the sink pad
- * @crop: crop rectangle to be validated
- *
- * The preview engine crops lines and columns for its internal operation,
- * depending on which filters are enabled. Enforce minimum crop margins to
- * handle that transparently for userspace.
- *
- * See the explanation at the PREV_MARGIN_* definitions for more details.
- */
-static void preview_try_crop(struct isp_prev_device *prev,
- const struct v4l2_mbus_framefmt *sink,
- struct v4l2_rect *crop)
-{
- unsigned int left = PREV_MARGIN_LEFT;
- unsigned int right = sink->width - PREV_MARGIN_RIGHT;
- unsigned int top = PREV_MARGIN_TOP;
- unsigned int bottom = sink->height - PREV_MARGIN_BOTTOM;
-
- /* When processing data on-the-fly from the CCDC, at least 2 pixels must
- * be cropped from the left and right sides of the image. As we don't
- * know which filters will be enabled, increase the left and right
- * margins by two.
- */
- if (prev->input == PREVIEW_INPUT_CCDC) {
- left += 2;
- right -= 2;
- }
-
- /* Restrict left/top to even values to keep the Bayer pattern. */
- crop->left &= ~1;
- crop->top &= ~1;
-
- crop->left = clamp_t(u32, crop->left, left, right - PREV_MIN_OUT_WIDTH);
- crop->top = clamp_t(u32, crop->top, top, bottom - PREV_MIN_OUT_HEIGHT);
- crop->width = clamp_t(u32, crop->width, PREV_MIN_OUT_WIDTH,
- right - crop->left);
- crop->height = clamp_t(u32, crop->height, PREV_MIN_OUT_HEIGHT,
- bottom - crop->top);
-}
-
-/*
- * preview_enum_mbus_code - Handle pixel format enumeration
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @code : pointer to v4l2_subdev_mbus_code_enum structure
- * return -EINVAL or zero on success
- */
-static int preview_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- switch (code->pad) {
- case PREV_PAD_SINK:
- if (code->index >= ARRAY_SIZE(preview_input_fmts))
- return -EINVAL;
-
- code->code = preview_input_fmts[code->index];
- break;
- case PREV_PAD_SOURCE:
- if (code->index >= ARRAY_SIZE(preview_output_fmts))
- return -EINVAL;
-
- code->code = preview_output_fmts[code->index];
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int preview_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt format;
-
- if (fse->index != 0)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = 1;
- format.height = 1;
- preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->min_width = format.width;
- fse->min_height = format.height;
-
- if (format.code != fse->code)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = -1;
- format.height = -1;
- preview_try_format(prev, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->max_width = format.width;
- fse->max_height = format.height;
-
- return 0;
-}
-
-/*
- * preview_get_selection - Retrieve a selection rectangle on a pad
- * @sd: ISP preview V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangles are the crop rectangles on the sink pad.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int preview_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- if (sel->pad != PREV_PAD_SINK)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = INT_MAX;
- sel->r.height = INT_MAX;
-
- format = __preview_get_format(prev, fh, PREV_PAD_SINK,
- sel->which);
- preview_try_crop(prev, format, &sel->r);
- break;
-
- case V4L2_SEL_TGT_CROP:
- sel->r = *__preview_get_crop(prev, fh, sel->which);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * preview_set_selection - Set a selection rectangle on a pad
- * @sd: ISP preview V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangle is the actual crop rectangle on the sink pad.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int preview_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- if (sel->target != V4L2_SEL_TGT_CROP ||
- sel->pad != PREV_PAD_SINK)
- return -EINVAL;
-
- /* The crop rectangle can't be changed while streaming. */
- if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
- return -EBUSY;
-
- /* Modifying the crop rectangle always changes the format on the source
- * pad. If the KEEP_CONFIG flag is set, just return the current crop
- * rectangle.
- */
- if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) {
- sel->r = *__preview_get_crop(prev, fh, sel->which);
- return 0;
- }
-
- format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which);
- preview_try_crop(prev, format, &sel->r);
- *__preview_get_crop(prev, fh, sel->which) = sel->r;
-
- /* Update the source format. */
- format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which);
- preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which);
-
- return 0;
-}
-
-/*
- * preview_get_format - Handle get format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt: pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int preview_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- fmt->format = *format;
- return 0;
-}
-
-/*
- * preview_set_format - Handle set format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt: pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int preview_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- format = __preview_get_format(prev, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- preview_try_format(prev, fh, fmt->pad, &fmt->format, fmt->which);
- *format = fmt->format;
-
- /* Propagate the format from sink to source */
- if (fmt->pad == PREV_PAD_SINK) {
- /* Reset the crop rectangle. */
- crop = __preview_get_crop(prev, fh, fmt->which);
- crop->left = 0;
- crop->top = 0;
- crop->width = fmt->format.width;
- crop->height = fmt->format.height;
-
- preview_try_crop(prev, &fmt->format, crop);
-
- /* Update the source format. */
- format = __preview_get_format(prev, fh, PREV_PAD_SOURCE,
- fmt->which);
- preview_try_format(prev, fh, PREV_PAD_SOURCE, format,
- fmt->which);
- }
-
- return 0;
-}
-
-/*
- * preview_init_formats - Initialize formats on all pads
- * @sd: ISP preview V4L2 subdevice
- * @fh: V4L2 subdev file handle
- *
- * Initialize all pad formats with default values. If fh is not NULL, try
- * formats are initialized on the file handle. Otherwise active formats are
- * initialized on the device.
- */
-static int preview_init_formats(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh)
-{
- struct v4l2_subdev_format format;
-
- memset(&format, 0, sizeof(format));
- format.pad = PREV_PAD_SINK;
- format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
- format.format.width = 4096;
- format.format.height = 4096;
- preview_set_format(sd, fh, &format);
-
- return 0;
-}
-
-/* subdev core operations */
-static const struct v4l2_subdev_core_ops preview_v4l2_core_ops = {
- .ioctl = preview_ioctl,
-};
-
-/* subdev video operations */
-static const struct v4l2_subdev_video_ops preview_v4l2_video_ops = {
- .s_stream = preview_set_stream,
-};
-
-/* subdev pad operations */
-static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
- .enum_mbus_code = preview_enum_mbus_code,
- .enum_frame_size = preview_enum_frame_size,
- .get_fmt = preview_get_format,
- .set_fmt = preview_set_format,
- .get_selection = preview_get_selection,
- .set_selection = preview_set_selection,
-};
-
-/* subdev operations */
-static const struct v4l2_subdev_ops preview_v4l2_ops = {
- .core = &preview_v4l2_core_ops,
- .video = &preview_v4l2_video_ops,
- .pad = &preview_v4l2_pad_ops,
-};
-
-/* subdev internal operations */
-static const struct v4l2_subdev_internal_ops preview_v4l2_internal_ops = {
- .open = preview_init_formats,
-};
-
-/* -----------------------------------------------------------------------------
- * Media entity operations
- */
-
-/*
- * preview_link_setup - Setup previewer connections.
- * @entity : Pointer to media entity structure
- * @local : Pointer to local pad array
- * @remote : Pointer to remote pad array
- * @flags : Link flags
- * return -EINVAL or zero on success
- */
-static int preview_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
-
- switch (local->index | media_entity_type(remote->entity)) {
- case PREV_PAD_SINK | MEDIA_ENT_T_DEVNODE:
- /* read from memory */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (prev->input == PREVIEW_INPUT_CCDC)
- return -EBUSY;
- prev->input = PREVIEW_INPUT_MEMORY;
- } else {
- if (prev->input == PREVIEW_INPUT_MEMORY)
- prev->input = PREVIEW_INPUT_NONE;
- }
- break;
-
- case PREV_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
- /* read from ccdc */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (prev->input == PREVIEW_INPUT_MEMORY)
- return -EBUSY;
- prev->input = PREVIEW_INPUT_CCDC;
- } else {
- if (prev->input == PREVIEW_INPUT_CCDC)
- prev->input = PREVIEW_INPUT_NONE;
- }
- break;
-
- /*
- * The ISP core doesn't support pipelines with multiple video outputs.
- * Revisit this when it will be implemented, and return -EBUSY for now.
- */
-
- case PREV_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
- /* write to memory */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (prev->output & ~PREVIEW_OUTPUT_MEMORY)
- return -EBUSY;
- prev->output |= PREVIEW_OUTPUT_MEMORY;
- } else {
- prev->output &= ~PREVIEW_OUTPUT_MEMORY;
- }
- break;
-
- case PREV_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV:
- /* write to resizer */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (prev->output & ~PREVIEW_OUTPUT_RESIZER)
- return -EBUSY;
- prev->output |= PREVIEW_OUTPUT_RESIZER;
- } else {
- prev->output &= ~PREVIEW_OUTPUT_RESIZER;
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* media operations */
-static const struct media_entity_operations preview_media_ops = {
- .link_setup = preview_link_setup,
- .link_validate = v4l2_subdev_link_validate,
-};
-
-void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
-{
- v4l2_device_unregister_subdev(&prev->subdev);
- omap3isp_video_unregister(&prev->video_in);
- omap3isp_video_unregister(&prev->video_out);
-}
-
-int omap3isp_preview_register_entities(struct isp_prev_device *prev,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &prev->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&prev->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_preview_unregister_entities(prev);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * ISP previewer initialisation and cleanup
- */
-
-/*
- * preview_init_entities - Initialize subdev and media entity.
- * @prev : Pointer to preview structure
- * return -ENOMEM or zero on success
- */
-static int preview_init_entities(struct isp_prev_device *prev)
-{
- struct v4l2_subdev *sd = &prev->subdev;
- struct media_pad *pads = prev->pads;
- struct media_entity *me = &sd->entity;
- int ret;
-
- prev->input = PREVIEW_INPUT_NONE;
-
- v4l2_subdev_init(sd, &preview_v4l2_ops);
- sd->internal_ops = &preview_v4l2_internal_ops;
- strlcpy(sd->name, "OMAP3 ISP preview", sizeof(sd->name));
- sd->grp_id = 1 << 16; /* group ID for isp subdevs */
- v4l2_set_subdevdata(sd, prev);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- v4l2_ctrl_handler_init(&prev->ctrls, 2);
- v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_BRIGHTNESS,
- ISPPRV_BRIGHT_LOW, ISPPRV_BRIGHT_HIGH,
- ISPPRV_BRIGHT_STEP, ISPPRV_BRIGHT_DEF);
- v4l2_ctrl_new_std(&prev->ctrls, &preview_ctrl_ops, V4L2_CID_CONTRAST,
- ISPPRV_CONTRAST_LOW, ISPPRV_CONTRAST_HIGH,
- ISPPRV_CONTRAST_STEP, ISPPRV_CONTRAST_DEF);
- v4l2_ctrl_handler_setup(&prev->ctrls);
- sd->ctrl_handler = &prev->ctrls;
-
- pads[PREV_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- pads[PREV_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
-
- me->ops = &preview_media_ops;
- ret = media_entity_init(me, PREV_PADS_NUM, pads, 0);
- if (ret < 0)
- return ret;
-
- preview_init_formats(sd, NULL);
-
- /* According to the OMAP34xx TRM, video buffers need to be aligned on a
- * 32 bytes boundary. However, an undocumented hardware bug requires a
- * 64 bytes boundary at the preview engine input.
- */
- prev->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- prev->video_in.ops = &preview_video_ops;
- prev->video_in.isp = to_isp_device(prev);
- prev->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
- prev->video_in.bpl_alignment = 64;
- prev->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- prev->video_out.ops = &preview_video_ops;
- prev->video_out.isp = to_isp_device(prev);
- prev->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
- prev->video_out.bpl_alignment = 32;
-
- ret = omap3isp_video_init(&prev->video_in, "preview");
- if (ret < 0)
- goto error_video_in;
-
- ret = omap3isp_video_init(&prev->video_out, "preview");
- if (ret < 0)
- goto error_video_out;
-
- /* Connect the video nodes to the previewer subdev. */
- ret = media_entity_create_link(&prev->video_in.video.entity, 0,
- &prev->subdev.entity, PREV_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(&prev->subdev.entity, PREV_PAD_SOURCE,
- &prev->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
- return 0;
-
-error_link:
- omap3isp_video_cleanup(&prev->video_out);
-error_video_out:
- omap3isp_video_cleanup(&prev->video_in);
-error_video_in:
- media_entity_cleanup(&prev->subdev.entity);
- return ret;
-}
-
-/*
- * omap3isp_preview_init - Previewer initialization.
- * @dev : Pointer to ISP device
- * return -ENOMEM or zero on success
- */
-int omap3isp_preview_init(struct isp_device *isp)
-{
- struct isp_prev_device *prev = &isp->isp_prev;
-
- init_waitqueue_head(&prev->wait);
-
- preview_init_params(prev);
-
- return preview_init_entities(prev);
-}
-
-void omap3isp_preview_cleanup(struct isp_device *isp)
-{
- struct isp_prev_device *prev = &isp->isp_prev;
-
- v4l2_ctrl_handler_free(&prev->ctrls);
- omap3isp_video_cleanup(&prev->video_in);
- omap3isp_video_cleanup(&prev->video_out);
- media_entity_cleanup(&prev->subdev.entity);
-}
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
deleted file mode 100644
index 6663ab64e4b1..000000000000
--- a/drivers/media/video/omap3isp/isppreview.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * isppreview.h
- *
- * TI OMAP3 ISP - Preview module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc.
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_PREVIEW_H
-#define OMAP3_ISP_PREVIEW_H
-
-#include <linux/omap3isp.h>
-#include <linux/types.h>
-#include <media/v4l2-ctrls.h>
-
-#include "ispvideo.h"
-
-#define ISPPRV_BRIGHT_STEP 0x1
-#define ISPPRV_BRIGHT_DEF 0x0
-#define ISPPRV_BRIGHT_LOW 0x0
-#define ISPPRV_BRIGHT_HIGH 0xFF
-#define ISPPRV_BRIGHT_UNITS 0x1
-
-#define ISPPRV_CONTRAST_STEP 0x1
-#define ISPPRV_CONTRAST_DEF 0x10
-#define ISPPRV_CONTRAST_LOW 0x0
-#define ISPPRV_CONTRAST_HIGH 0xFF
-#define ISPPRV_CONTRAST_UNITS 0x1
-
-/* Additional features not listed in linux/omap3isp.h */
-#define OMAP3ISP_PREV_CONTRAST (1 << 17)
-#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18)
-#define OMAP3ISP_PREV_FEATURES_END (1 << 19)
-
-enum preview_input_entity {
- PREVIEW_INPUT_NONE,
- PREVIEW_INPUT_CCDC,
- PREVIEW_INPUT_MEMORY,
-};
-
-#define PREVIEW_OUTPUT_RESIZER (1 << 1)
-#define PREVIEW_OUTPUT_MEMORY (1 << 2)
-
-/* Configure byte layout of YUV image */
-enum preview_ycpos_mode {
- YCPOS_YCrYCb = 0,
- YCPOS_YCbYCr = 1,
- YCPOS_CbYCrY = 2,
- YCPOS_CrYCbY = 3
-};
-
-/*
- * struct prev_params - Structure for all configuration
- * @busy: Bitmask of busy parameters (being updated or used)
- * @update: Bitmask of the parameters to be updated
- * @features: Set of features enabled.
- * @cfa: CFA coefficients.
- * @csup: Chroma suppression coefficients.
- * @luma: Luma enhancement coefficients.
- * @nf: Noise filter coefficients.
- * @dcor: Noise filter coefficients.
- * @gamma: Gamma coefficients.
- * @wbal: White Balance parameters.
- * @blkadj: Black adjustment parameters.
- * @rgb2rgb: RGB blending parameters.
- * @csc: Color space conversion (RGB to YCbCr) parameters.
- * @hmed: Horizontal median filter.
- * @yclimit: YC limits parameters.
- * @contrast: Contrast.
- * @brightness: Brightness.
- */
-struct prev_params {
- u32 busy;
- u32 update;
- u32 features;
- struct omap3isp_prev_cfa cfa;
- struct omap3isp_prev_csup csup;
- struct omap3isp_prev_luma luma;
- struct omap3isp_prev_nf nf;
- struct omap3isp_prev_dcor dcor;
- struct omap3isp_prev_gtables gamma;
- struct omap3isp_prev_wbal wbal;
- struct omap3isp_prev_blkadj blkadj;
- struct omap3isp_prev_rgbtorgb rgb2rgb;
- struct omap3isp_prev_csc csc;
- struct omap3isp_prev_hmed hmed;
- struct omap3isp_prev_yclimit yclimit;
- u8 contrast;
- u8 brightness;
-};
-
-/* Sink and source previewer pads */
-#define PREV_PAD_SINK 0
-#define PREV_PAD_SOURCE 1
-#define PREV_PADS_NUM 2
-
-/*
- * struct isp_prev_device - Structure for storing ISP Preview module information
- * @subdev: V4L2 subdevice
- * @pads: Media entity pads
- * @formats: Active formats at the subdev pad
- * @crop: Active crop rectangle
- * @input: Module currently connected to the input pad
- * @output: Bitmask of the active output
- * @video_in: Input video entity
- * @video_out: Output video entity
- * @params.params : Active and shadow parameters sets
- * @params.active: Bitmask of parameters active in set 0
- * @params.lock: Parameters lock, protects params.active and params.shadow
- * @underrun: Whether the preview entity has queued buffers on the output
- * @state: Current preview pipeline state
- *
- * This structure is used to store the OMAP ISP Preview module Information.
- */
-struct isp_prev_device {
- struct v4l2_subdev subdev;
- struct media_pad pads[PREV_PADS_NUM];
- struct v4l2_mbus_framefmt formats[PREV_PADS_NUM];
- struct v4l2_rect crop;
-
- struct v4l2_ctrl_handler ctrls;
-
- enum preview_input_entity input;
- unsigned int output;
- struct isp_video video_in;
- struct isp_video video_out;
-
- struct {
- struct prev_params params[2];
- u32 active;
- spinlock_t lock;
- } params;
-
- enum isp_pipeline_stream_state state;
- wait_queue_head_t wait;
- atomic_t stopping;
-};
-
-struct isp_device;
-
-int omap3isp_preview_init(struct isp_device *isp);
-void omap3isp_preview_cleanup(struct isp_device *isp);
-
-int omap3isp_preview_register_entities(struct isp_prev_device *prv,
- struct v4l2_device *vdev);
-void omap3isp_preview_unregister_entities(struct isp_prev_device *prv);
-
-void omap3isp_preview_isr_frame_sync(struct isp_prev_device *prev);
-void omap3isp_preview_isr(struct isp_prev_device *prev);
-
-int omap3isp_preview_busy(struct isp_prev_device *isp_prev);
-
-void omap3isp_preview_restore_context(struct isp_device *isp);
-
-#endif /* OMAP3_ISP_PREVIEW_H */
diff --git a/drivers/media/video/omap3isp/ispqueue.c b/drivers/media/video/omap3isp/ispqueue.c
deleted file mode 100644
index 9bebb1e57aab..000000000000
--- a/drivers/media/video/omap3isp/ispqueue.c
+++ /dev/null
@@ -1,1157 +0,0 @@
-/*
- * ispqueue.c
- *
- * TI OMAP3 ISP - Video buffers queue handling
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/pagemap.h>
-#include <linux/poll.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include "ispqueue.h"
-
-/* -----------------------------------------------------------------------------
- * Video buffers management
- */
-
-/*
- * isp_video_buffer_cache_sync - Keep the buffers coherent between CPU and ISP
- *
- * The typical operation required here is Cache Invalidation across
- * the (user space) buffer address range. And this _must_ be done
- * at QBUF stage (and *only* at QBUF).
- *
- * We try to use optimal cache invalidation function:
- * - dmac_map_area:
- * - used when the number of pages are _low_.
- * - it becomes quite slow as the number of pages increase.
- * - for 648x492 viewfinder (150 pages) it takes 1.3 ms.
- * - for 5 Mpix buffer (2491 pages) it takes between 25-50 ms.
- *
- * - flush_cache_all:
- * - used when the number of pages are _high_.
- * - time taken in the range of 500-900 us.
- * - has a higher penalty but, as whole dcache + icache is invalidated
- */
-/*
- * FIXME: dmac_inv_range crashes randomly on the user space buffer
- * address. Fall back to flush_cache_all for now.
- */
-#define ISP_CACHE_FLUSH_PAGES_MAX 0
-
-static void isp_video_buffer_cache_sync(struct isp_video_buffer *buf)
-{
- if (buf->skip_cache)
- return;
-
- if (buf->vbuf.m.userptr == 0 || buf->npages == 0 ||
- buf->npages > ISP_CACHE_FLUSH_PAGES_MAX)
- flush_cache_all();
- else {
- dmac_map_area((void *)buf->vbuf.m.userptr, buf->vbuf.length,
- DMA_FROM_DEVICE);
- outer_inv_range(buf->vbuf.m.userptr,
- buf->vbuf.m.userptr + buf->vbuf.length);
- }
-}
-
-/*
- * isp_video_buffer_lock_vma - Prevent VMAs from being unmapped
- *
- * Lock the VMAs underlying the given buffer into memory. This avoids the
- * userspace buffer mapping from being swapped out, making VIPT cache handling
- * easier.
- *
- * Note that the pages will not be freed as the buffers have been locked to
- * memory using by a call to get_user_pages(), but the userspace mapping could
- * still disappear if the VMAs are not locked. This is caused by the memory
- * management code trying to be as lock-less as possible, which results in the
- * userspace mapping manager not finding out that the pages are locked under
- * some conditions.
- */
-static int isp_video_buffer_lock_vma(struct isp_video_buffer *buf, int lock)
-{
- struct vm_area_struct *vma;
- unsigned long start;
- unsigned long end;
- int ret = 0;
-
- if (buf->vbuf.memory == V4L2_MEMORY_MMAP)
- return 0;
-
- /* We can be called from workqueue context if the current task dies to
- * unlock the VMAs. In that case there's no current memory management
- * context so unlocking can't be performed, but the VMAs have been or
- * are getting destroyed anyway so it doesn't really matter.
- */
- if (!current || !current->mm)
- return lock ? -EINVAL : 0;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- down_write(&current->mm->mmap_sem);
- spin_lock(&current->mm->page_table_lock);
-
- do {
- vma = find_vma(current->mm, start);
- if (vma == NULL) {
- ret = -EFAULT;
- goto out;
- }
-
- if (lock)
- vma->vm_flags |= VM_LOCKED;
- else
- vma->vm_flags &= ~VM_LOCKED;
-
- start = vma->vm_end + 1;
- } while (vma->vm_end < end);
-
- if (lock)
- buf->vm_flags |= VM_LOCKED;
- else
- buf->vm_flags &= ~VM_LOCKED;
-
-out:
- spin_unlock(&current->mm->page_table_lock);
- up_write(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_sglist_kernel - Build a scatter list for a vmalloc'ed buffer
- *
- * Iterate over the vmalloc'ed area and create a scatter list entry for every
- * page.
- */
-static int isp_video_buffer_sglist_kernel(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int npages;
- unsigned int i;
- void *addr;
-
- addr = buf->vaddr;
- npages = PAGE_ALIGN(buf->vbuf.length) >> PAGE_SHIFT;
-
- sglist = vmalloc(npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, npages);
-
- for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
- struct page *page = vmalloc_to_page(addr);
-
- if (page == NULL || PageHighMem(page)) {
- vfree(sglist);
- return -EINVAL;
- }
-
- sg_set_page(&sglist[i], page, PAGE_SIZE, 0);
- }
-
- buf->sglen = npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_sglist_user - Build a scatter list for a userspace buffer
- *
- * Walk the buffer pages list and create a 1:1 mapping to a scatter list.
- */
-static int isp_video_buffer_sglist_user(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int offset = buf->offset;
- unsigned int i;
-
- sglist = vmalloc(buf->npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, buf->npages);
-
- for (i = 0; i < buf->npages; ++i) {
- if (PageHighMem(buf->pages[i])) {
- vfree(sglist);
- return -EINVAL;
- }
-
- sg_set_page(&sglist[i], buf->pages[i], PAGE_SIZE - offset,
- offset);
- offset = 0;
- }
-
- buf->sglen = buf->npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_sglist_pfnmap - Build a scatter list for a VM_PFNMAP buffer
- *
- * Create a scatter list of physically contiguous pages starting at the buffer
- * memory physical address.
- */
-static int isp_video_buffer_sglist_pfnmap(struct isp_video_buffer *buf)
-{
- struct scatterlist *sglist;
- unsigned int offset = buf->offset;
- unsigned long pfn = buf->paddr >> PAGE_SHIFT;
- unsigned int i;
-
- sglist = vmalloc(buf->npages * sizeof(*sglist));
- if (sglist == NULL)
- return -ENOMEM;
-
- sg_init_table(sglist, buf->npages);
-
- for (i = 0; i < buf->npages; ++i, ++pfn) {
- sg_set_page(&sglist[i], pfn_to_page(pfn), PAGE_SIZE - offset,
- offset);
- /* PFNMAP buffers will not get DMA-mapped, set the DMA address
- * manually.
- */
- sg_dma_address(&sglist[i]) = (pfn << PAGE_SHIFT) + offset;
- offset = 0;
- }
-
- buf->sglen = buf->npages;
- buf->sglist = sglist;
-
- return 0;
-}
-
-/*
- * isp_video_buffer_cleanup - Release pages for a userspace VMA.
- *
- * Release pages locked by a call isp_video_buffer_prepare_user and free the
- * pages table.
- */
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
- enum dma_data_direction direction;
- unsigned int i;
-
- if (buf->queue->ops->buffer_cleanup)
- buf->queue->ops->buffer_cleanup(buf);
-
- if (!(buf->vm_flags & VM_PFNMAP)) {
- direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- dma_unmap_sg(buf->queue->dev, buf->sglist, buf->sglen,
- direction);
- }
-
- vfree(buf->sglist);
- buf->sglist = NULL;
- buf->sglen = 0;
-
- if (buf->pages != NULL) {
- isp_video_buffer_lock_vma(buf, 0);
-
- for (i = 0; i < buf->npages; ++i)
- page_cache_release(buf->pages[i]);
-
- vfree(buf->pages);
- buf->pages = NULL;
- }
-
- buf->npages = 0;
- buf->skip_cache = false;
-}
-
-/*
- * isp_video_buffer_prepare_user - Pin userspace VMA pages to memory.
- *
- * This function creates a list of pages for a userspace VMA. The number of
- * pages is first computed based on the buffer size, and pages are then
- * retrieved by a call to get_user_pages.
- *
- * Pages are pinned to memory by get_user_pages, making them available for DMA
- * transfers. However, due to memory management optimization, it seems the
- * get_user_pages doesn't guarantee that the pinned pages will not be written
- * to swap and removed from the userspace mapping(s). When this happens, a page
- * fault can be generated when accessing those unmapped pages.
- *
- * If the fault is triggered by a page table walk caused by VIPT cache
- * management operations, the page fault handler might oops if the MM semaphore
- * is held, as it can't handle kernel page faults in that case. To fix that, a
- * fixup entry needs to be added to the cache management code, or the userspace
- * VMA must be locked to avoid removing pages from the userspace mapping in the
- * first place.
- *
- * If the number of pages retrieved is smaller than the number required by the
- * buffer size, the function returns -EFAULT.
- */
-static int isp_video_buffer_prepare_user(struct isp_video_buffer *buf)
-{
- unsigned long data;
- unsigned int first;
- unsigned int last;
- int ret;
-
- data = buf->vbuf.m.userptr;
- first = (data & PAGE_MASK) >> PAGE_SHIFT;
- last = ((data + buf->vbuf.length - 1) & PAGE_MASK) >> PAGE_SHIFT;
-
- buf->offset = data & ~PAGE_MASK;
- buf->npages = last - first + 1;
- buf->pages = vmalloc(buf->npages * sizeof(buf->pages[0]));
- if (buf->pages == NULL)
- return -ENOMEM;
-
- down_read(&current->mm->mmap_sem);
- ret = get_user_pages(current, current->mm, data & PAGE_MASK,
- buf->npages,
- buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE, 0,
- buf->pages, NULL);
- up_read(&current->mm->mmap_sem);
-
- if (ret != buf->npages) {
- buf->npages = ret < 0 ? 0 : ret;
- isp_video_buffer_cleanup(buf);
- return -EFAULT;
- }
-
- ret = isp_video_buffer_lock_vma(buf, 1);
- if (ret < 0)
- isp_video_buffer_cleanup(buf);
-
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare_pfnmap - Validate a VM_PFNMAP userspace buffer
- *
- * Userspace VM_PFNMAP buffers are supported only if they are contiguous in
- * memory and if they span a single VMA.
- *
- * Return 0 if the buffer is valid, or -EFAULT otherwise.
- */
-static int isp_video_buffer_prepare_pfnmap(struct isp_video_buffer *buf)
-{
- struct vm_area_struct *vma;
- unsigned long prev_pfn;
- unsigned long this_pfn;
- unsigned long start;
- unsigned long end;
- dma_addr_t pa;
- int ret = -EFAULT;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- buf->offset = start & ~PAGE_MASK;
- buf->npages = (end >> PAGE_SHIFT) - (start >> PAGE_SHIFT) + 1;
- buf->pages = NULL;
-
- down_read(&current->mm->mmap_sem);
- vma = find_vma(current->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;
- }
-
- buf->paddr = pa + buf->offset;
- ret = 0;
-
-done:
- up_read(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare_vm_flags - Get VMA flags for a userspace address
- *
- * This function locates the VMAs for the buffer's userspace address and checks
- * that their flags match. The only flag that we need to care for at the moment
- * is VM_PFNMAP.
- *
- * The buffer vm_flags field is set to the first VMA flags.
- *
- * Return -EFAULT if no VMA can be found for part of the buffer, or if the VMAs
- * have incompatible flags.
- */
-static int isp_video_buffer_prepare_vm_flags(struct isp_video_buffer *buf)
-{
- struct vm_area_struct *vma;
- pgprot_t vm_page_prot;
- unsigned long start;
- unsigned long end;
- int ret = -EFAULT;
-
- start = buf->vbuf.m.userptr;
- end = buf->vbuf.m.userptr + buf->vbuf.length - 1;
-
- down_read(&current->mm->mmap_sem);
-
- do {
- vma = find_vma(current->mm, start);
- if (vma == NULL)
- goto done;
-
- if (start == buf->vbuf.m.userptr) {
- buf->vm_flags = vma->vm_flags;
- vm_page_prot = vma->vm_page_prot;
- }
-
- if ((buf->vm_flags ^ vma->vm_flags) & VM_PFNMAP)
- goto done;
-
- if (vm_page_prot != vma->vm_page_prot)
- goto done;
-
- start = vma->vm_end + 1;
- } while (vma->vm_end < end);
-
- /* Skip cache management to enhance performances for non-cached or
- * write-combining buffers.
- */
- if (vm_page_prot == pgprot_noncached(vm_page_prot) ||
- vm_page_prot == pgprot_writecombine(vm_page_prot))
- buf->skip_cache = true;
-
- ret = 0;
-
-done:
- up_read(&current->mm->mmap_sem);
- return ret;
-}
-
-/*
- * isp_video_buffer_prepare - Make a buffer ready for operation
- *
- * Preparing a buffer involves:
- *
- * - validating VMAs (userspace buffers only)
- * - locking pages and VMAs into memory (userspace buffers only)
- * - building page and scatter-gather lists
- * - mapping buffers for DMA operation
- * - performing driver-specific preparation
- *
- * The function must be called in userspace context with a valid mm context
- * (this excludes cleanup paths such as sys_close when the userspace process
- * segfaults).
- */
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
-{
- enum dma_data_direction direction;
- int ret;
-
- switch (buf->vbuf.memory) {
- case V4L2_MEMORY_MMAP:
- ret = isp_video_buffer_sglist_kernel(buf);
- break;
-
- case V4L2_MEMORY_USERPTR:
- ret = isp_video_buffer_prepare_vm_flags(buf);
- if (ret < 0)
- return ret;
-
- if (buf->vm_flags & VM_PFNMAP) {
- ret = isp_video_buffer_prepare_pfnmap(buf);
- if (ret < 0)
- return ret;
-
- ret = isp_video_buffer_sglist_pfnmap(buf);
- } else {
- ret = isp_video_buffer_prepare_user(buf);
- if (ret < 0)
- return ret;
-
- ret = isp_video_buffer_sglist_user(buf);
- }
- break;
-
- default:
- return -EINVAL;
- }
-
- if (ret < 0)
- goto done;
-
- if (!(buf->vm_flags & VM_PFNMAP)) {
- direction = buf->vbuf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
- ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
- ret = dma_map_sg(buf->queue->dev, buf->sglist, buf->sglen,
- direction);
- if (ret != buf->sglen) {
- ret = -EFAULT;
- goto done;
- }
- }
-
- if (buf->queue->ops->buffer_prepare)
- ret = buf->queue->ops->buffer_prepare(buf);
-
-done:
- if (ret < 0) {
- isp_video_buffer_cleanup(buf);
- return ret;
- }
-
- return ret;
-}
-
-/*
- * isp_video_queue_query - Query the status of a given buffer
- *
- * Locking: must be called with the queue lock held.
- */
-static void isp_video_buffer_query(struct isp_video_buffer *buf,
- struct v4l2_buffer *vbuf)
-{
- memcpy(vbuf, &buf->vbuf, sizeof(*vbuf));
-
- if (buf->vma_use_count)
- vbuf->flags |= V4L2_BUF_FLAG_MAPPED;
-
- switch (buf->state) {
- case ISP_BUF_STATE_ERROR:
- vbuf->flags |= V4L2_BUF_FLAG_ERROR;
- case ISP_BUF_STATE_DONE:
- vbuf->flags |= V4L2_BUF_FLAG_DONE;
- case ISP_BUF_STATE_QUEUED:
- case ISP_BUF_STATE_ACTIVE:
- vbuf->flags |= V4L2_BUF_FLAG_QUEUED;
- break;
- case ISP_BUF_STATE_IDLE:
- default:
- break;
- }
-}
-
-/*
- * isp_video_buffer_wait - Wait for a buffer to be ready
- *
- * In non-blocking mode, return immediately with 0 if the buffer is ready or
- * -EAGAIN if the buffer is in the QUEUED or ACTIVE state.
- *
- * In blocking mode, wait (interruptibly but with no timeout) on the buffer wait
- * queue using the same condition.
- */
-static int isp_video_buffer_wait(struct isp_video_buffer *buf, int nonblocking)
-{
- if (nonblocking) {
- return (buf->state != ISP_BUF_STATE_QUEUED &&
- buf->state != ISP_BUF_STATE_ACTIVE)
- ? 0 : -EAGAIN;
- }
-
- return wait_event_interruptible(buf->wait,
- buf->state != ISP_BUF_STATE_QUEUED &&
- buf->state != ISP_BUF_STATE_ACTIVE);
-}
-
-/* -----------------------------------------------------------------------------
- * Queue management
- */
-
-/*
- * isp_video_queue_free - Free video buffers memory
- *
- * Buffers can only be freed if the queue isn't streaming and if no buffer is
- * mapped to userspace. Return -EBUSY if those conditions aren't statisfied.
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_free(struct isp_video_queue *queue)
-{
- unsigned int i;
-
- if (queue->streaming)
- return -EBUSY;
-
- for (i = 0; i < queue->count; ++i) {
- if (queue->buffers[i]->vma_use_count != 0)
- return -EBUSY;
- }
-
- for (i = 0; i < queue->count; ++i) {
- struct isp_video_buffer *buf = queue->buffers[i];
-
- isp_video_buffer_cleanup(buf);
-
- vfree(buf->vaddr);
- buf->vaddr = NULL;
-
- kfree(buf);
- queue->buffers[i] = NULL;
- }
-
- INIT_LIST_HEAD(&queue->queue);
- queue->count = 0;
- return 0;
-}
-
-/*
- * isp_video_queue_alloc - Allocate video buffers memory
- *
- * This function must be called with the queue lock held.
- */
-static int isp_video_queue_alloc(struct isp_video_queue *queue,
- unsigned int nbuffers,
- unsigned int size, enum v4l2_memory memory)
-{
- struct isp_video_buffer *buf;
- unsigned int i;
- void *mem;
- int ret;
-
- /* Start by freeing the buffers. */
- ret = isp_video_queue_free(queue);
- if (ret < 0)
- return ret;
-
- /* Bail out of no buffers should be allocated. */
- if (nbuffers == 0)
- return 0;
-
- /* Initialize the allocated buffers. */
- for (i = 0; i < nbuffers; ++i) {
- buf = kzalloc(queue->bufsize, GFP_KERNEL);
- if (buf == NULL)
- break;
-
- if (memory == V4L2_MEMORY_MMAP) {
- /* Allocate video buffers memory for mmap mode. Align
- * the size to the page size.
- */
- mem = vmalloc_32_user(PAGE_ALIGN(size));
- if (mem == NULL) {
- kfree(buf);
- break;
- }
-
- buf->vbuf.m.offset = i * PAGE_ALIGN(size);
- buf->vaddr = mem;
- }
-
- buf->vbuf.index = i;
- buf->vbuf.length = size;
- buf->vbuf.type = queue->type;
- buf->vbuf.field = V4L2_FIELD_NONE;
- buf->vbuf.memory = memory;
-
- buf->queue = queue;
- init_waitqueue_head(&buf->wait);
-
- queue->buffers[i] = buf;
- }
-
- if (i == 0)
- return -ENOMEM;
-
- queue->count = i;
- return nbuffers;
-}
-
-/**
- * omap3isp_video_queue_cleanup - Clean up the video buffers queue
- * @queue: Video buffers queue
- *
- * Free all allocated resources and clean up the video buffers queue. The queue
- * must not be busy (no ongoing video stream) and buffers must have been
- * unmapped.
- *
- * Return 0 on success or -EBUSY if the queue is busy or buffers haven't been
- * unmapped.
- */
-int omap3isp_video_queue_cleanup(struct isp_video_queue *queue)
-{
- return isp_video_queue_free(queue);
-}
-
-/**
- * omap3isp_video_queue_init - Initialize the video buffers queue
- * @queue: Video buffers queue
- * @type: V4L2 buffer type (capture or output)
- * @ops: Driver-specific queue operations
- * @dev: Device used for DMA operations
- * @bufsize: Size of the driver-specific buffer structure
- *
- * Initialize the video buffers queue with the supplied parameters.
- *
- * The queue type must be one of V4L2_BUF_TYPE_VIDEO_CAPTURE or
- * V4L2_BUF_TYPE_VIDEO_OUTPUT. Other buffer types are not supported yet.
- *
- * Buffer objects will be allocated using the given buffer size to allow room
- * for driver-specific fields. Driver-specific buffer structures must start
- * with a struct isp_video_buffer field. Drivers with no driver-specific buffer
- * structure must pass the size of the isp_video_buffer structure in the bufsize
- * parameter.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_init(struct isp_video_queue *queue,
- enum v4l2_buf_type type,
- const struct isp_video_queue_operations *ops,
- struct device *dev, unsigned int bufsize)
-{
- INIT_LIST_HEAD(&queue->queue);
- mutex_init(&queue->lock);
- spin_lock_init(&queue->irqlock);
-
- queue->type = type;
- queue->ops = ops;
- queue->dev = dev;
- queue->bufsize = bufsize;
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 operations
- */
-
-/**
- * omap3isp_video_queue_reqbufs - Allocate video buffers memory
- *
- * This function is intended to be used as a VIDIOC_REQBUFS ioctl handler. It
- * allocated video buffer objects and, for MMAP buffers, buffer memory.
- *
- * If the number of buffers is 0, all buffers are freed and the function returns
- * without performing any allocation.
- *
- * If the number of buffers is not 0, currently allocated buffers (if any) are
- * freed and the requested number of buffers are allocated. Depending on
- * driver-specific requirements and on memory availability, a number of buffer
- * smaller or bigger than requested can be allocated. This isn't considered as
- * an error.
- *
- * Return 0 on success or one of the following error codes:
- *
- * -EINVAL if the buffer type or index are invalid
- * -EBUSY if the queue is busy (streaming or buffers mapped)
- * -ENOMEM if the buffers can't be allocated due to an out-of-memory condition
- */
-int omap3isp_video_queue_reqbufs(struct isp_video_queue *queue,
- struct v4l2_requestbuffers *rb)
-{
- unsigned int nbuffers = rb->count;
- unsigned int size;
- int ret;
-
- if (rb->type != queue->type)
- return -EINVAL;
-
- queue->ops->queue_prepare(queue, &nbuffers, &size);
- if (size == 0)
- return -EINVAL;
-
- nbuffers = min_t(unsigned int, nbuffers, ISP_VIDEO_MAX_BUFFERS);
-
- mutex_lock(&queue->lock);
-
- ret = isp_video_queue_alloc(queue, nbuffers, size, rb->memory);
- if (ret < 0)
- goto done;
-
- rb->count = ret;
- ret = 0;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_querybuf - Query the status of a buffer in a queue
- *
- * This function is intended to be used as a VIDIOC_QUERYBUF ioctl handler. It
- * returns the status of a given video buffer.
- *
- * Return 0 on success or -EINVAL if the buffer type or index are invalid.
- */
-int omap3isp_video_queue_querybuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf)
-{
- struct isp_video_buffer *buf;
- int ret = 0;
-
- if (vbuf->type != queue->type)
- return -EINVAL;
-
- mutex_lock(&queue->lock);
-
- if (vbuf->index >= queue->count) {
- ret = -EINVAL;
- goto done;
- }
-
- buf = queue->buffers[vbuf->index];
- isp_video_buffer_query(buf, vbuf);
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_qbuf - Queue a buffer
- *
- * This function is intended to be used as a VIDIOC_QBUF ioctl handler.
- *
- * The v4l2_buffer structure passed from userspace is first sanity tested. If
- * sane, the buffer is then processed and added to the main queue and, if the
- * queue is streaming, to the IRQ queue.
- *
- * Before being enqueued, USERPTR buffers are checked for address changes. If
- * the buffer has a different userspace address, the old memory area is unlocked
- * and the new memory area is locked.
- */
-int omap3isp_video_queue_qbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
- int ret = -EINVAL;
-
- if (vbuf->type != queue->type)
- goto done;
-
- mutex_lock(&queue->lock);
-
- if (vbuf->index >= queue->count)
- goto done;
-
- buf = queue->buffers[vbuf->index];
-
- if (vbuf->memory != buf->vbuf.memory)
- goto done;
-
- if (buf->state != ISP_BUF_STATE_IDLE)
- goto done;
-
- if (vbuf->memory == V4L2_MEMORY_USERPTR &&
- vbuf->length < buf->vbuf.length)
- goto done;
-
- if (vbuf->memory == V4L2_MEMORY_USERPTR &&
- vbuf->m.userptr != buf->vbuf.m.userptr) {
- isp_video_buffer_cleanup(buf);
- buf->vbuf.m.userptr = vbuf->m.userptr;
- buf->prepared = 0;
- }
-
- if (!buf->prepared) {
- ret = isp_video_buffer_prepare(buf);
- if (ret < 0)
- goto done;
- buf->prepared = 1;
- }
-
- isp_video_buffer_cache_sync(buf);
-
- buf->state = ISP_BUF_STATE_QUEUED;
- list_add_tail(&buf->stream, &queue->queue);
-
- if (queue->streaming) {
- spin_lock_irqsave(&queue->irqlock, flags);
- queue->ops->buffer_queue(buf);
- spin_unlock_irqrestore(&queue->irqlock, flags);
- }
-
- ret = 0;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_dqbuf - Dequeue a buffer
- *
- * This function is intended to be used as a VIDIOC_DQBUF ioctl handler.
- *
- * The v4l2_buffer structure passed from userspace is first sanity tested. If
- * sane, the buffer is then processed and added to the main queue and, if the
- * queue is streaming, to the IRQ queue.
- *
- * Before being enqueued, USERPTR buffers are checked for address changes. If
- * the buffer has a different userspace address, the old memory area is unlocked
- * and the new memory area is locked.
- */
-int omap3isp_video_queue_dqbuf(struct isp_video_queue *queue,
- struct v4l2_buffer *vbuf, int nonblocking)
-{
- struct isp_video_buffer *buf;
- int ret;
-
- if (vbuf->type != queue->type)
- return -EINVAL;
-
- mutex_lock(&queue->lock);
-
- if (list_empty(&queue->queue)) {
- ret = -EINVAL;
- goto done;
- }
-
- buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
- ret = isp_video_buffer_wait(buf, nonblocking);
- if (ret < 0)
- goto done;
-
- list_del(&buf->stream);
-
- isp_video_buffer_query(buf, vbuf);
- buf->state = ISP_BUF_STATE_IDLE;
- vbuf->flags &= ~V4L2_BUF_FLAG_QUEUED;
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_streamon - Start streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMON ioctl handler. It
- * starts streaming on the queue and calls the buffer_queue operation for all
- * queued buffers.
- *
- * Return 0 on success.
- */
-int omap3isp_video_queue_streamon(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
-
- mutex_lock(&queue->lock);
-
- if (queue->streaming)
- goto done;
-
- queue->streaming = 1;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- list_for_each_entry(buf, &queue->queue, stream)
- queue->ops->buffer_queue(buf);
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
-done:
- mutex_unlock(&queue->lock);
- return 0;
-}
-
-/**
- * omap3isp_video_queue_streamoff - Stop streaming
- *
- * This function is intended to be used as a VIDIOC_STREAMOFF ioctl handler. It
- * stops streaming on the queue and wakes up all the buffers.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_streamoff(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned long flags;
- unsigned int i;
-
- mutex_lock(&queue->lock);
-
- if (!queue->streaming)
- goto done;
-
- queue->streaming = 0;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
-
- if (buf->state == ISP_BUF_STATE_ACTIVE)
- wake_up(&buf->wait);
-
- buf->state = ISP_BUF_STATE_IDLE;
- }
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
- INIT_LIST_HEAD(&queue->queue);
-
-done:
- mutex_unlock(&queue->lock);
-}
-
-/**
- * omap3isp_video_queue_discard_done - Discard all buffers marked as DONE
- *
- * This function is intended to be used with suspend/resume operations. It
- * discards all 'done' buffers as they would be too old to be requested after
- * resume.
- *
- * Drivers must stop the hardware and synchronize with interrupt handlers and/or
- * delayed works before calling this function to make sure no buffer will be
- * touched by the driver and/or hardware.
- */
-void omap3isp_video_queue_discard_done(struct isp_video_queue *queue)
-{
- struct isp_video_buffer *buf;
- unsigned int i;
-
- mutex_lock(&queue->lock);
-
- if (!queue->streaming)
- goto done;
-
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
-
- if (buf->state == ISP_BUF_STATE_DONE)
- buf->state = ISP_BUF_STATE_ERROR;
- }
-
-done:
- mutex_unlock(&queue->lock);
-}
-
-static void isp_video_queue_vm_open(struct vm_area_struct *vma)
-{
- struct isp_video_buffer *buf = vma->vm_private_data;
-
- buf->vma_use_count++;
-}
-
-static void isp_video_queue_vm_close(struct vm_area_struct *vma)
-{
- struct isp_video_buffer *buf = vma->vm_private_data;
-
- buf->vma_use_count--;
-}
-
-static const struct vm_operations_struct isp_video_queue_vm_ops = {
- .open = isp_video_queue_vm_open,
- .close = isp_video_queue_vm_close,
-};
-
-/**
- * omap3isp_video_queue_mmap - Map buffers to userspace
- *
- * This function is intended to be used as an mmap() file operation handler. It
- * maps a buffer to userspace based on the VMA offset.
- *
- * Only buffers of memory type MMAP are supported.
- */
-int omap3isp_video_queue_mmap(struct isp_video_queue *queue,
- struct vm_area_struct *vma)
-{
- struct isp_video_buffer *uninitialized_var(buf);
- unsigned long size;
- unsigned int i;
- int ret = 0;
-
- mutex_lock(&queue->lock);
-
- for (i = 0; i < queue->count; ++i) {
- buf = queue->buffers[i];
- if ((buf->vbuf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
-
- if (i == queue->count) {
- ret = -EINVAL;
- goto done;
- }
-
- size = vma->vm_end - vma->vm_start;
-
- if (buf->vbuf.memory != V4L2_MEMORY_MMAP ||
- size != PAGE_ALIGN(buf->vbuf.length)) {
- ret = -EINVAL;
- goto done;
- }
-
- ret = remap_vmalloc_range(vma, buf->vaddr, 0);
- if (ret < 0)
- goto done;
-
- vma->vm_ops = &isp_video_queue_vm_ops;
- vma->vm_private_data = buf;
- isp_video_queue_vm_open(vma);
-
-done:
- mutex_unlock(&queue->lock);
- return ret;
-}
-
-/**
- * omap3isp_video_queue_poll - Poll video queue state
- *
- * This function is intended to be used as a poll() file operation handler. It
- * polls the state of the video buffer at the front of the queue and returns an
- * events mask.
- *
- * If no buffer is present at the front of the queue, POLLERR is returned.
- */
-unsigned int omap3isp_video_queue_poll(struct isp_video_queue *queue,
- struct file *file, poll_table *wait)
-{
- struct isp_video_buffer *buf;
- unsigned int mask = 0;
-
- mutex_lock(&queue->lock);
- if (list_empty(&queue->queue)) {
- mask |= POLLERR;
- goto done;
- }
- buf = list_first_entry(&queue->queue, struct isp_video_buffer, stream);
-
- poll_wait(file, &buf->wait, wait);
- if (buf->state == ISP_BUF_STATE_DONE ||
- buf->state == ISP_BUF_STATE_ERROR) {
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- mask |= POLLIN | POLLRDNORM;
- else
- mask |= POLLOUT | POLLWRNORM;
- }
-
-done:
- mutex_unlock(&queue->lock);
- return mask;
-}
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
deleted file mode 100644
index ae17d917f77b..000000000000
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ /dev/null
@@ -1,1776 +0,0 @@
-/*
- * ispresizer.c
- *
- * TI OMAP3 ISP - Resizer module
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/device.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-
-#include "isp.h"
-#include "ispreg.h"
-#include "ispresizer.h"
-
-/*
- * Resizer Constants
- */
-#define MIN_RESIZE_VALUE 64
-#define MID_RESIZE_VALUE 512
-#define MAX_RESIZE_VALUE 1024
-
-#define MIN_IN_WIDTH 32
-#define MIN_IN_HEIGHT 32
-#define MAX_IN_WIDTH_MEMORY_MODE 4095
-#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES1 1280
-#define MAX_IN_WIDTH_ONTHEFLY_MODE_ES2 4095
-#define MAX_IN_HEIGHT 4095
-
-#define MIN_OUT_WIDTH 16
-#define MIN_OUT_HEIGHT 2
-#define MAX_OUT_HEIGHT 4095
-
-/*
- * Resizer Use Constraints
- * "TRM ES3.1, table 12-46"
- */
-#define MAX_4TAP_OUT_WIDTH_ES1 1280
-#define MAX_7TAP_OUT_WIDTH_ES1 640
-#define MAX_4TAP_OUT_WIDTH_ES2 3312
-#define MAX_7TAP_OUT_WIDTH_ES2 1650
-#define MAX_4TAP_OUT_WIDTH_3630 4096
-#define MAX_7TAP_OUT_WIDTH_3630 2048
-
-/*
- * Constants for ratio calculation
- */
-#define RESIZE_DIVISOR 256
-#define DEFAULT_PHASE 1
-
-/*
- * Default (and only) configuration of filter coefficients.
- * 7-tap mode is for scale factors 0.25x to 0.5x.
- * 4-tap mode is for scale factors 0.5x to 4.0x.
- * There shouldn't be any reason to recalculate these, EVER.
- */
-static const struct isprsz_coef filter_coefs = {
- /* For 8-phase 4-tap horizontal filter: */
- {
- 0x0000, 0x0100, 0x0000, 0x0000,
- 0x03FA, 0x00F6, 0x0010, 0x0000,
- 0x03F9, 0x00DB, 0x002C, 0x0000,
- 0x03FB, 0x00B3, 0x0053, 0x03FF,
- 0x03FD, 0x0082, 0x0084, 0x03FD,
- 0x03FF, 0x0053, 0x00B3, 0x03FB,
- 0x0000, 0x002C, 0x00DB, 0x03F9,
- 0x0000, 0x0010, 0x00F6, 0x03FA
- },
- /* For 8-phase 4-tap vertical filter: */
- {
- 0x0000, 0x0100, 0x0000, 0x0000,
- 0x03FA, 0x00F6, 0x0010, 0x0000,
- 0x03F9, 0x00DB, 0x002C, 0x0000,
- 0x03FB, 0x00B3, 0x0053, 0x03FF,
- 0x03FD, 0x0082, 0x0084, 0x03FD,
- 0x03FF, 0x0053, 0x00B3, 0x03FB,
- 0x0000, 0x002C, 0x00DB, 0x03F9,
- 0x0000, 0x0010, 0x00F6, 0x03FA
- },
- /* For 4-phase 7-tap horizontal filter: */
- #define DUMMY 0
- {
- 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
- 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
- 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
- 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
- },
- /* For 4-phase 7-tap vertical filter: */
- {
- 0x0004, 0x0023, 0x005A, 0x0058, 0x0023, 0x0004, 0x0000, DUMMY,
- 0x0002, 0x0018, 0x004d, 0x0060, 0x0031, 0x0008, 0x0000, DUMMY,
- 0x0001, 0x000f, 0x003f, 0x0062, 0x003f, 0x000f, 0x0001, DUMMY,
- 0x0000, 0x0008, 0x0031, 0x0060, 0x004d, 0x0018, 0x0002, DUMMY
- }
- /*
- * The dummy padding is required in 7-tap mode because of how the
- * registers are arranged physically.
- */
- #undef DUMMY
-};
-
-/*
- * __resizer_get_format - helper function for getting resizer format
- * @res : pointer to resizer private structure
- * @pad : pad number
- * @fh : V4L2 subdev file handle
- * @which : wanted subdev format
- * return zero
- */
-static struct v4l2_mbus_framefmt *
-__resizer_get_format(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_format(fh, pad);
- else
- return &res->formats[pad];
-}
-
-/*
- * __resizer_get_crop - helper function for getting resizer crop rectangle
- * @res : pointer to resizer private structure
- * @fh : V4L2 subdev file handle
- * @which : wanted subdev crop rectangle
- */
-static struct v4l2_rect *
-__resizer_get_crop(struct isp_res_device *res, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(fh, RESZ_PAD_SINK);
- else
- return &res->crop.request;
-}
-
-/*
- * resizer_set_filters - Set resizer filters
- * @res: Device context.
- * @h_coeff: horizontal coefficient
- * @v_coeff: vertical coefficient
- * Return none
- */
-static void resizer_set_filters(struct isp_res_device *res, const u16 *h_coeff,
- const u16 *v_coeff)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 startaddr_h, startaddr_v, tmp_h, tmp_v;
- int i;
-
- startaddr_h = ISPRSZ_HFILT10;
- startaddr_v = ISPRSZ_VFILT10;
-
- for (i = 0; i < COEFF_CNT; i += 2) {
- tmp_h = h_coeff[i] |
- (h_coeff[i + 1] << ISPRSZ_HFILT_COEF1_SHIFT);
- tmp_v = v_coeff[i] |
- (v_coeff[i + 1] << ISPRSZ_VFILT_COEF1_SHIFT);
- isp_reg_writel(isp, tmp_h, OMAP3_ISP_IOMEM_RESZ, startaddr_h);
- isp_reg_writel(isp, tmp_v, OMAP3_ISP_IOMEM_RESZ, startaddr_v);
- startaddr_h += 4;
- startaddr_v += 4;
- }
-}
-
-/*
- * resizer_set_bilinear - Chrominance horizontal algorithm select
- * @res: Device context.
- * @type: Filtering interpolation type.
- *
- * Filtering that is same as luminance processing is
- * intended only for downsampling, and bilinear interpolation
- * is intended only for upsampling.
- */
-static void resizer_set_bilinear(struct isp_res_device *res,
- enum resizer_chroma_algo type)
-{
- struct isp_device *isp = to_isp_device(res);
-
- if (type == RSZ_BILINEAR)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_CBILIN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_CBILIN);
-}
-
-/*
- * resizer_set_ycpos - Luminance and chrominance order
- * @res: Device context.
- * @order: order type.
- */
-static void resizer_set_ycpos(struct isp_res_device *res,
- enum v4l2_mbus_pixelcode pixelcode)
-{
- struct isp_device *isp = to_isp_device(res);
-
- switch (pixelcode) {
- case V4L2_MBUS_FMT_YUYV8_1X16:
- isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_YCPOS);
- break;
- case V4L2_MBUS_FMT_UYVY8_1X16:
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_YCPOS);
- break;
- default:
- return;
- }
-}
-
-/*
- * resizer_set_phase - Setup horizontal and vertical starting phase
- * @res: Device context.
- * @h_phase: horizontal phase parameters.
- * @v_phase: vertical phase parameters.
- *
- * Horizontal and vertical phase range is 0 to 7
- */
-static void resizer_set_phase(struct isp_res_device *res, u32 h_phase,
- u32 v_phase)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
- ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK);
- rgval |= (h_phase << ISPRSZ_CNT_HSTPH_SHIFT) & ISPRSZ_CNT_HSTPH_MASK;
- rgval |= (v_phase << ISPRSZ_CNT_VSTPH_SHIFT) & ISPRSZ_CNT_VSTPH_MASK;
-
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
-}
-
-/*
- * resizer_set_luma - Setup luminance enhancer parameters
- * @res: Device context.
- * @luma: Structure for luminance enhancer parameters.
- *
- * Algorithm select:
- * 0x0: Disable
- * 0x1: [-1 2 -1]/2 high-pass filter
- * 0x2: [-1 -2 6 -2 -1]/4 high-pass filter
- *
- * Maximum gain:
- * The data is coded in U4Q4 representation.
- *
- * Slope:
- * The data is coded in U4Q4 representation.
- *
- * Coring offset:
- * The data is coded in U8Q0 representation.
- *
- * The new luminance value is computed as:
- * Y += HPF(Y) x max(GAIN, (HPF(Y) - CORE) x SLOP + 8) >> 4.
- */
-static void resizer_set_luma(struct isp_res_device *res,
- struct resizer_luma_yenh *luma)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- rgval = (luma->algo << ISPRSZ_YENH_ALGO_SHIFT)
- & ISPRSZ_YENH_ALGO_MASK;
- rgval |= (luma->gain << ISPRSZ_YENH_GAIN_SHIFT)
- & ISPRSZ_YENH_GAIN_MASK;
- rgval |= (luma->slope << ISPRSZ_YENH_SLOP_SHIFT)
- & ISPRSZ_YENH_SLOP_MASK;
- rgval |= (luma->core << ISPRSZ_YENH_CORE_SHIFT)
- & ISPRSZ_YENH_CORE_MASK;
-
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH);
-}
-
-/*
- * resizer_set_source - Input source select
- * @res: Device context.
- * @source: Input source type
- *
- * If this field is set to RESIZER_INPUT_VP, the resizer input is fed from
- * Preview/CCDC engine, otherwise from memory.
- */
-static void resizer_set_source(struct isp_res_device *res,
- enum resizer_input_entity source)
-{
- struct isp_device *isp = to_isp_device(res);
-
- if (source == RESIZER_INPUT_MEMORY)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_INPSRC);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_INPSRC);
-}
-
-/*
- * resizer_set_ratio - Setup horizontal and vertical resizing value
- * @res: Device context.
- * @ratio: Structure for ratio parameters.
- *
- * Resizing range from 64 to 1024
- */
-static void resizer_set_ratio(struct isp_res_device *res,
- const struct resizer_ratio *ratio)
-{
- struct isp_device *isp = to_isp_device(res);
- const u16 *h_filter, *v_filter;
- u32 rgval = 0;
-
- rgval = isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) &
- ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK);
- rgval |= ((ratio->horz - 1) << ISPRSZ_CNT_HRSZ_SHIFT)
- & ISPRSZ_CNT_HRSZ_MASK;
- rgval |= ((ratio->vert - 1) << ISPRSZ_CNT_VRSZ_SHIFT)
- & ISPRSZ_CNT_VRSZ_MASK;
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT);
-
- /* prepare horizontal filter coefficients */
- if (ratio->horz > MID_RESIZE_VALUE)
- h_filter = &filter_coefs.h_filter_coef_7tap[0];
- else
- h_filter = &filter_coefs.h_filter_coef_4tap[0];
-
- /* prepare vertical filter coefficients */
- if (ratio->vert > MID_RESIZE_VALUE)
- v_filter = &filter_coefs.v_filter_coef_7tap[0];
- else
- v_filter = &filter_coefs.v_filter_coef_4tap[0];
-
- resizer_set_filters(res, h_filter, v_filter);
-}
-
-/*
- * resizer_set_dst_size - Setup the output height and width
- * @res: Device context.
- * @width: Output width.
- * @height: Output height.
- *
- * Width :
- * The value must be EVEN.
- *
- * Height:
- * The number of bytes written to SDRAM must be
- * a multiple of 16-bytes if the vertical resizing factor
- * is greater than 1x (upsizing)
- */
-static void resizer_set_output_size(struct isp_res_device *res,
- u32 width, u32 height)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- dev_dbg(isp->dev, "Output size[w/h]: %dx%d\n", width, height);
- rgval = (width << ISPRSZ_OUT_SIZE_HORZ_SHIFT)
- & ISPRSZ_OUT_SIZE_HORZ_MASK;
- rgval |= (height << ISPRSZ_OUT_SIZE_VERT_SHIFT)
- & ISPRSZ_OUT_SIZE_VERT_MASK;
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE);
-}
-
-/*
- * resizer_set_output_offset - Setup memory offset for the output lines.
- * @res: Device context.
- * @offset: Memory offset.
- *
- * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
- * boundary; the 5 LSBs are read-only. For optimal use of SDRAM bandwidth,
- * the SDRAM line offset must be set on a 256-byte boundary
- */
-static void resizer_set_output_offset(struct isp_res_device *res, u32 offset)
-{
- struct isp_device *isp = to_isp_device(res);
-
- isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF);
-}
-
-/*
- * resizer_set_start - Setup vertical and horizontal start position
- * @res: Device context.
- * @left: Horizontal start position.
- * @top: Vertical start position.
- *
- * Vertical start line:
- * This field makes sense only when the resizer obtains its input
- * from the preview engine/CCDC
- *
- * Horizontal start pixel:
- * Pixels are coded on 16 bits for YUV and 8 bits for color separate data.
- * When the resizer gets its input from SDRAM, this field must be set
- * to <= 15 for YUV 16-bit data and <= 31 for 8-bit color separate data
- */
-static void resizer_set_start(struct isp_res_device *res, u32 left, u32 top)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- rgval = (left << ISPRSZ_IN_START_HORZ_ST_SHIFT)
- & ISPRSZ_IN_START_HORZ_ST_MASK;
- rgval |= (top << ISPRSZ_IN_START_VERT_ST_SHIFT)
- & ISPRSZ_IN_START_VERT_ST_MASK;
-
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START);
-}
-
-/*
- * resizer_set_input_size - Setup the input size
- * @res: Device context.
- * @width: The range is 0 to 4095 pixels
- * @height: The range is 0 to 4095 lines
- */
-static void resizer_set_input_size(struct isp_res_device *res,
- u32 width, u32 height)
-{
- struct isp_device *isp = to_isp_device(res);
- u32 rgval = 0;
-
- dev_dbg(isp->dev, "Input size[w/h]: %dx%d\n", width, height);
-
- rgval = (width << ISPRSZ_IN_SIZE_HORZ_SHIFT)
- & ISPRSZ_IN_SIZE_HORZ_MASK;
- rgval |= (height << ISPRSZ_IN_SIZE_VERT_SHIFT)
- & ISPRSZ_IN_SIZE_VERT_MASK;
-
- isp_reg_writel(isp, rgval, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE);
-}
-
-/*
- * resizer_set_src_offs - Setup the memory offset for the input lines
- * @res: Device context.
- * @offset: Memory offset.
- *
- * The 5 LSBs are forced to be zeros by the hardware to align on a 32-byte
- * boundary; the 5 LSBs are read-only. This field must be programmed to be
- * 0x0 if the resizer input is from preview engine/CCDC.
- */
-static void resizer_set_input_offset(struct isp_res_device *res, u32 offset)
-{
- struct isp_device *isp = to_isp_device(res);
-
- isp_reg_writel(isp, offset, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF);
-}
-
-/*
- * resizer_set_intype - Input type select
- * @res: Device context.
- * @type: Pixel format type.
- */
-static void resizer_set_intype(struct isp_res_device *res,
- enum resizer_colors_type type)
-{
- struct isp_device *isp = to_isp_device(res);
-
- if (type == RSZ_COLOR8)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_INPTYP);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT,
- ISPRSZ_CNT_INPTYP);
-}
-
-/*
- * __resizer_set_inaddr - Helper function for set input address
- * @res : pointer to resizer private data structure
- * @addr: input address
- * return none
- */
-static void __resizer_set_inaddr(struct isp_res_device *res, u32 addr)
-{
- struct isp_device *isp = to_isp_device(res);
-
- isp_reg_writel(isp, addr, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD);
-}
-
-/*
- * The data rate at the horizontal resizer output must not exceed half the
- * functional clock or 100 MP/s, whichever is lower. According to the TRM
- * there's no similar requirement for the vertical resizer output. However
- * experience showed that vertical upscaling by 4 leads to SBL overflows (with
- * data rates at the resizer output exceeding 300 MP/s). Limiting the resizer
- * output data rate to the functional clock or 200 MP/s, whichever is lower,
- * seems to get rid of SBL overflows.
- *
- * The maximum data rate at the output of the horizontal resizer can thus be
- * computed with
- *
- * max intermediate rate <= L3 clock * input height / output height
- * max intermediate rate <= L3 clock / 2
- *
- * The maximum data rate at the resizer input is then
- *
- * max input rate <= max intermediate rate * input width / output width
- *
- * where the input width and height are the resizer input crop rectangle size.
- * The TRM doesn't clearly explain if that's a maximum instant data rate or a
- * maximum average data rate.
- */
-void omap3isp_resizer_max_rate(struct isp_res_device *res,
- unsigned int *max_rate)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
- const struct v4l2_mbus_framefmt *ofmt = &res->formats[RESZ_PAD_SOURCE];
- unsigned long limit = min(pipe->l3_ick, 200000000UL);
- unsigned long clock;
-
- clock = div_u64((u64)limit * res->crop.active.height, ofmt->height);
- clock = min(clock, limit / 2);
- *max_rate = div_u64((u64)clock * res->crop.active.width, ofmt->width);
-}
-
-/*
- * When the resizer processes images from memory, the driver must slow down read
- * requests on the input to at least comply with the internal data rate
- * requirements. If the application real-time requirements can cope with slower
- * processing, the resizer can be slowed down even more to put less pressure on
- * the overall system.
- *
- * When the resizer processes images on the fly (either from the CCDC or the
- * preview module), the same data rate requirements apply but they can't be
- * enforced at the resizer level. The image input module (sensor, CCP2 or
- * preview module) must not provide image data faster than the resizer can
- * process.
- *
- * For live image pipelines, the data rate is set by the frame format, size and
- * rate. The sensor output frame rate must not exceed the maximum resizer data
- * rate.
- *
- * The resizer slows down read requests by inserting wait cycles in the SBL
- * requests. The maximum number of 256-byte requests per second can be computed
- * as (the data rate is multiplied by 2 to convert from pixels per second to
- * bytes per second)
- *
- * request per second = data rate * 2 / 256
- * cycles per request = cycles per second / requests per second
- *
- * The number of cycles per second is controlled by the L3 clock, leading to
- *
- * cycles per request = L3 frequency / 2 * 256 / data rate
- */
-static void resizer_adjust_bandwidth(struct isp_res_device *res)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
- struct isp_device *isp = to_isp_device(res);
- unsigned long l3_ick = pipe->l3_ick;
- struct v4l2_fract *timeperframe;
- unsigned int cycles_per_frame;
- unsigned int requests_per_frame;
- unsigned int cycles_per_request;
- unsigned int granularity;
- unsigned int minimum;
- unsigned int maximum;
- unsigned int value;
-
- if (res->input != RESIZER_INPUT_MEMORY) {
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
- ISPSBL_SDR_REQ_RSZ_EXP_MASK);
- return;
- }
-
- switch (isp->revision) {
- case ISP_REVISION_1_0:
- case ISP_REVISION_2_0:
- default:
- granularity = 1024;
- break;
-
- case ISP_REVISION_15_0:
- granularity = 32;
- break;
- }
-
- /* Compute the minimum number of cycles per request, based on the
- * pipeline maximum data rate. This is an absolute lower bound if we
- * don't want SBL overflows, so round the value up.
- */
- cycles_per_request = div_u64((u64)l3_ick / 2 * 256 + pipe->max_rate - 1,
- pipe->max_rate);
- minimum = DIV_ROUND_UP(cycles_per_request, granularity);
-
- /* Compute the maximum number of cycles per request, based on the
- * requested frame rate. This is a soft upper bound to achieve a frame
- * rate equal or higher than the requested value, so round the value
- * down.
- */
- timeperframe = &pipe->max_timeperframe;
-
- requests_per_frame = DIV_ROUND_UP(res->crop.active.width * 2, 256)
- * res->crop.active.height;
- cycles_per_frame = div_u64((u64)l3_ick * timeperframe->numerator,
- timeperframe->denominator);
- cycles_per_request = cycles_per_frame / requests_per_frame;
-
- maximum = cycles_per_request / granularity;
-
- value = max(minimum, maximum);
-
- dev_dbg(isp->dev, "%s: cycles per request = %u\n", __func__, value);
- isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_SDR_REQ_EXP,
- ISPSBL_SDR_REQ_RSZ_EXP_MASK,
- value << ISPSBL_SDR_REQ_RSZ_EXP_SHIFT);
-}
-
-/*
- * omap3isp_resizer_busy - Checks if ISP resizer is busy.
- *
- * Returns busy field from ISPRSZ_PCR register.
- */
-int omap3isp_resizer_busy(struct isp_res_device *res)
-{
- struct isp_device *isp = to_isp_device(res);
-
- return isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) &
- ISPRSZ_PCR_BUSY;
-}
-
-/*
- * resizer_set_inaddr - Sets the memory address of the input frame.
- * @addr: 32bit memory address aligned on 32byte boundary.
- */
-static void resizer_set_inaddr(struct isp_res_device *res, u32 addr)
-{
- res->addr_base = addr;
-
- /* This will handle crop settings in stream off state */
- if (res->crop_offset)
- addr += res->crop_offset & ~0x1f;
-
- __resizer_set_inaddr(res, addr);
-}
-
-/*
- * Configures the memory address to which the output frame is written.
- * @addr: 32bit memory address aligned on 32byte boundary.
- * Note: For SBL efficiency reasons the address should be on a 256-byte
- * boundary.
- */
-static void resizer_set_outaddr(struct isp_res_device *res, u32 addr)
-{
- struct isp_device *isp = to_isp_device(res);
-
- /*
- * Set output address. This needs to be in its own function
- * because it changes often.
- */
- isp_reg_writel(isp, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT,
- OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD);
-}
-
-/*
- * resizer_print_status - Prints the values of the resizer module registers.
- */
-#define RSZ_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###RSZ " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_##name))
-
-static void resizer_print_status(struct isp_res_device *res)
-{
- struct isp_device *isp = to_isp_device(res);
-
- dev_dbg(isp->dev, "-------------Resizer Register dump----------\n");
-
- RSZ_PRINT_REGISTER(isp, PCR);
- RSZ_PRINT_REGISTER(isp, CNT);
- RSZ_PRINT_REGISTER(isp, OUT_SIZE);
- RSZ_PRINT_REGISTER(isp, IN_START);
- RSZ_PRINT_REGISTER(isp, IN_SIZE);
- RSZ_PRINT_REGISTER(isp, SDR_INADD);
- RSZ_PRINT_REGISTER(isp, SDR_INOFF);
- RSZ_PRINT_REGISTER(isp, SDR_OUTADD);
- RSZ_PRINT_REGISTER(isp, SDR_OUTOFF);
- RSZ_PRINT_REGISTER(isp, YENH);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
-/*
- * resizer_calc_ratios - Helper function for calculate resizer ratios
- * @res: pointer to resizer private data structure
- * @input: input frame size
- * @output: output frame size
- * @ratio : return calculated ratios
- * return none
- *
- * The resizer uses a polyphase sample rate converter. The upsampling filter
- * has a fixed number of phases that depend on the resizing ratio. As the ratio
- * computation depends on the number of phases, we need to compute a first
- * approximation and then refine it.
- *
- * The input/output/ratio relationship is given by the OMAP34xx TRM:
- *
- * - 8-phase, 4-tap mode (RSZ = 64 ~ 512)
- * iw = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7
- * ih = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4
- * - 4-phase, 7-tap mode (RSZ = 513 ~ 1024)
- * iw = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7
- * ih = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7
- *
- * iw and ih are the input width and height after cropping. Those equations need
- * to be satisfied exactly for the resizer to work correctly.
- *
- * The equations can't be easily reverted, as the >> 8 operation is not linear.
- * In addition, not all input sizes can be achieved for a given output size. To
- * get the highest input size lower than or equal to the requested input size,
- * we need to compute the highest resizing ratio that satisfies the following
- * inequality (taking the 4-tap mode width equation as an example)
- *
- * iw >= (32 * sph + (ow - 1) * hrsz + 16) >> 8 - 7
- *
- * (where iw is the requested input width) which can be rewritten as
- *
- * iw - 7 >= (32 * sph + (ow - 1) * hrsz + 16) >> 8
- * (iw - 7) << 8 >= 32 * sph + (ow - 1) * hrsz + 16 - b
- * ((iw - 7) << 8) + b >= 32 * sph + (ow - 1) * hrsz + 16
- *
- * where b is the value of the 8 least significant bits of the right hand side
- * expression of the last inequality. The highest resizing ratio value will be
- * achieved when b is equal to its maximum value of 255. That resizing ratio
- * value will still satisfy the original inequality, as b will disappear when
- * the expression will be shifted right by 8.
- *
- * The reverted the equations thus become
- *
- * - 8-phase, 4-tap mode
- * hrsz = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / (ow - 1)
- * vrsz = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / (oh - 1)
- * - 4-phase, 7-tap mode
- * hrsz = ((iw - 7) * 256 + 255 - 32 - 64 * sph) / (ow - 1)
- * vrsz = ((ih - 7) * 256 + 255 - 32 - 64 * spv) / (oh - 1)
- *
- * The ratios are integer values, and are rounded down to ensure that the
- * cropped input size is not bigger than the uncropped input size.
- *
- * As the number of phases/taps, used to select the correct equations to compute
- * the ratio, depends on the ratio, we start with the 4-tap mode equations to
- * compute an approximation of the ratio, and switch to the 7-tap mode equations
- * if the approximation is higher than the ratio threshold.
- *
- * As the 7-tap mode equations will return a ratio smaller than or equal to the
- * 4-tap mode equations, the resulting ratio could become lower than or equal to
- * the ratio threshold. This 'equations loop' isn't an issue as long as the
- * correct equations are used to compute the final input size. Starting with the
- * 4-tap mode equations ensure that, in case of values resulting in a 'ratio
- * loop', the smallest of the ratio values will be used, never exceeding the
- * requested input size.
- *
- * We first clamp the output size according to the hardware capabilitie to avoid
- * auto-cropping the input more than required to satisfy the TRM equations. The
- * minimum output size is achieved with a scaling factor of 1024. It is thus
- * computed using the 7-tap equations.
- *
- * min ow = ((iw - 7) * 256 - 32 - 64 * sph) / 1024 + 1
- * min oh = ((ih - 7) * 256 - 32 - 64 * spv) / 1024 + 1
- *
- * Similarly, the maximum output size is achieved with a scaling factor of 64
- * and computed using the 4-tap equations.
- *
- * max ow = ((iw - 7) * 256 + 255 - 16 - 32 * sph) / 64 + 1
- * max oh = ((ih - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1
- *
- * The additional +255 term compensates for the round down operation performed
- * by the TRM equations when shifting the value right by 8 bits.
- *
- * We then compute and clamp the ratios (x1/4 ~ x4). Clamping the output size to
- * the maximum value guarantees that the ratio value will never be smaller than
- * the minimum, but it could still slightly exceed the maximum. Clamping the
- * ratio will thus result in a resizing factor slightly larger than the
- * requested value.
- *
- * To accommodate that, and make sure the TRM equations are satisfied exactly, we
- * compute the input crop rectangle as the last step.
- *
- * As if the situation wasn't complex enough, the maximum output width depends
- * on the vertical resizing ratio. Fortunately, the output height doesn't
- * depend on the horizontal resizing ratio. We can then start by computing the
- * output height and the vertical ratio, and then move to computing the output
- * width and the horizontal ratio.
- */
-static void resizer_calc_ratios(struct isp_res_device *res,
- struct v4l2_rect *input,
- struct v4l2_mbus_framefmt *output,
- struct resizer_ratio *ratio)
-{
- struct isp_device *isp = to_isp_device(res);
- const unsigned int spv = DEFAULT_PHASE;
- const unsigned int sph = DEFAULT_PHASE;
- unsigned int upscaled_width;
- unsigned int upscaled_height;
- unsigned int min_width;
- unsigned int min_height;
- unsigned int max_width;
- unsigned int max_height;
- unsigned int width_alignment;
- unsigned int width;
- unsigned int height;
-
- /*
- * Clamp the output height based on the hardware capabilities and
- * compute the vertical resizing ratio.
- */
- min_height = ((input->height - 7) * 256 - 32 - 64 * spv) / 1024 + 1;
- min_height = max_t(unsigned int, min_height, MIN_OUT_HEIGHT);
- max_height = ((input->height - 4) * 256 + 255 - 16 - 32 * spv) / 64 + 1;
- max_height = min_t(unsigned int, max_height, MAX_OUT_HEIGHT);
- output->height = clamp(output->height, min_height, max_height);
-
- ratio->vert = ((input->height - 4) * 256 + 255 - 16 - 32 * spv)
- / (output->height - 1);
- if (ratio->vert > MID_RESIZE_VALUE)
- ratio->vert = ((input->height - 7) * 256 + 255 - 32 - 64 * spv)
- / (output->height - 1);
- ratio->vert = clamp_t(unsigned int, ratio->vert,
- MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
-
- if (ratio->vert <= MID_RESIZE_VALUE) {
- upscaled_height = (output->height - 1) * ratio->vert
- + 32 * spv + 16;
- height = (upscaled_height >> 8) + 4;
- } else {
- upscaled_height = (output->height - 1) * ratio->vert
- + 64 * spv + 32;
- height = (upscaled_height >> 8) + 7;
- }
-
- /*
- * Compute the minimum and maximum output widths based on the hardware
- * capabilities. The maximum depends on the vertical resizing ratio.
- */
- min_width = ((input->width - 7) * 256 - 32 - 64 * sph) / 1024 + 1;
- min_width = max_t(unsigned int, min_width, MIN_OUT_WIDTH);
-
- if (ratio->vert <= MID_RESIZE_VALUE) {
- switch (isp->revision) {
- case ISP_REVISION_1_0:
- max_width = MAX_4TAP_OUT_WIDTH_ES1;
- break;
-
- case ISP_REVISION_2_0:
- default:
- max_width = MAX_4TAP_OUT_WIDTH_ES2;
- break;
-
- case ISP_REVISION_15_0:
- max_width = MAX_4TAP_OUT_WIDTH_3630;
- break;
- }
- } else {
- switch (isp->revision) {
- case ISP_REVISION_1_0:
- max_width = MAX_7TAP_OUT_WIDTH_ES1;
- break;
-
- case ISP_REVISION_2_0:
- default:
- max_width = MAX_7TAP_OUT_WIDTH_ES2;
- break;
-
- case ISP_REVISION_15_0:
- max_width = MAX_7TAP_OUT_WIDTH_3630;
- break;
- }
- }
- max_width = min(((input->width - 7) * 256 + 255 - 16 - 32 * sph) / 64
- + 1, max_width);
-
- /*
- * The output width must be even, and must be a multiple of 16 bytes
- * when upscaling vertically. Clamp the output width to the valid range.
- * Take the alignment into account (the maximum width in 7-tap mode on
- * ES2 isn't a multiple of 8) and align the result up to make sure it
- * won't be smaller than the minimum.
- */
- width_alignment = ratio->vert < 256 ? 8 : 2;
- output->width = clamp(output->width, min_width,
- max_width & ~(width_alignment - 1));
- output->width = ALIGN(output->width, width_alignment);
-
- ratio->horz = ((input->width - 7) * 256 + 255 - 16 - 32 * sph)
- / (output->width - 1);
- if (ratio->horz > MID_RESIZE_VALUE)
- ratio->horz = ((input->width - 7) * 256 + 255 - 32 - 64 * sph)
- / (output->width - 1);
- ratio->horz = clamp_t(unsigned int, ratio->horz,
- MIN_RESIZE_VALUE, MAX_RESIZE_VALUE);
-
- if (ratio->horz <= MID_RESIZE_VALUE) {
- upscaled_width = (output->width - 1) * ratio->horz
- + 32 * sph + 16;
- width = (upscaled_width >> 8) + 7;
- } else {
- upscaled_width = (output->width - 1) * ratio->horz
- + 64 * sph + 32;
- width = (upscaled_width >> 8) + 7;
- }
-
- /* Center the new crop rectangle. */
- input->left += (input->width - width) / 2;
- input->top += (input->height - height) / 2;
- input->width = width;
- input->height = height;
-}
-
-/*
- * resizer_set_crop_params - Setup hardware with cropping parameters
- * @res : resizer private structure
- * @crop_rect : current crop rectangle
- * @ratio : resizer ratios
- * return none
- */
-static void resizer_set_crop_params(struct isp_res_device *res,
- const struct v4l2_mbus_framefmt *input,
- const struct v4l2_mbus_framefmt *output)
-{
- resizer_set_ratio(res, &res->ratio);
-
- /* Set chrominance horizontal algorithm */
- if (res->ratio.horz >= RESIZE_DIVISOR)
- resizer_set_bilinear(res, RSZ_THE_SAME);
- else
- resizer_set_bilinear(res, RSZ_BILINEAR);
-
- resizer_adjust_bandwidth(res);
-
- if (res->input == RESIZER_INPUT_MEMORY) {
- /* Calculate additional offset for crop */
- res->crop_offset = (res->crop.active.top * input->width +
- res->crop.active.left) * 2;
- /*
- * Write lowest 4 bits of horizontal pixel offset (in pixels),
- * vertical start must be 0.
- */
- resizer_set_start(res, (res->crop_offset / 2) & 0xf, 0);
-
- /*
- * Set start (read) address for cropping, in bytes.
- * Lowest 5 bits must be zero.
- */
- __resizer_set_inaddr(res,
- res->addr_base + (res->crop_offset & ~0x1f));
- } else {
- /*
- * Set vertical start line and horizontal starting pixel.
- * If the input is from CCDC/PREV, horizontal start field is
- * in bytes (twice number of pixels).
- */
- resizer_set_start(res, res->crop.active.left * 2,
- res->crop.active.top);
- /* Input address and offset must be 0 for preview/ccdc input */
- __resizer_set_inaddr(res, 0);
- resizer_set_input_offset(res, 0);
- }
-
- /* Set the input size */
- resizer_set_input_size(res, res->crop.active.width,
- res->crop.active.height);
-}
-
-static void resizer_configure(struct isp_res_device *res)
-{
- struct v4l2_mbus_framefmt *informat, *outformat;
- struct resizer_luma_yenh luma = {0, 0, 0, 0};
-
- resizer_set_source(res, res->input);
-
- informat = &res->formats[RESZ_PAD_SINK];
- outformat = &res->formats[RESZ_PAD_SOURCE];
-
- /* RESZ_PAD_SINK */
- if (res->input == RESIZER_INPUT_VP)
- resizer_set_input_offset(res, 0);
- else
- resizer_set_input_offset(res, ALIGN(informat->width, 0x10) * 2);
-
- /* YUV422 interleaved, default phase, no luma enhancement */
- resizer_set_intype(res, RSZ_YUV422);
- resizer_set_ycpos(res, informat->code);
- resizer_set_phase(res, DEFAULT_PHASE, DEFAULT_PHASE);
- resizer_set_luma(res, &luma);
-
- /* RESZ_PAD_SOURCE */
- resizer_set_output_offset(res, ALIGN(outformat->width * 2, 32));
- resizer_set_output_size(res, outformat->width, outformat->height);
-
- resizer_set_crop_params(res, informat, outformat);
-}
-
-/* -----------------------------------------------------------------------------
- * Interrupt handling
- */
-
-static void resizer_enable_oneshot(struct isp_res_device *res)
-{
- struct isp_device *isp = to_isp_device(res);
-
- isp_reg_set(isp, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR,
- ISPRSZ_PCR_ENABLE | ISPRSZ_PCR_ONESHOT);
-}
-
-void omap3isp_resizer_isr_frame_sync(struct isp_res_device *res)
-{
- /*
- * If ISP_VIDEO_DMAQUEUE_QUEUED is set, DMA queue had an underrun
- * condition, the module was paused and now we have a buffer queued
- * on the output again. Restart the pipeline if running in continuous
- * mode.
- */
- if (res->state == ISP_PIPELINE_STREAM_CONTINUOUS &&
- res->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
- resizer_enable_oneshot(res);
- isp_video_dmaqueue_flags_clr(&res->video_out);
- }
-}
-
-static void resizer_isr_buffer(struct isp_res_device *res)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&res->subdev.entity);
- struct isp_buffer *buffer;
- int restart = 0;
-
- if (res->state == ISP_PIPELINE_STREAM_STOPPED)
- return;
-
- /* Complete the output buffer and, if reading from memory, the input
- * buffer.
- */
- buffer = omap3isp_video_buffer_next(&res->video_out);
- if (buffer != NULL) {
- resizer_set_outaddr(res, buffer->isp_addr);
- restart = 1;
- }
-
- pipe->state |= ISP_PIPELINE_IDLE_OUTPUT;
-
- if (res->input == RESIZER_INPUT_MEMORY) {
- buffer = omap3isp_video_buffer_next(&res->video_in);
- if (buffer != NULL)
- resizer_set_inaddr(res, buffer->isp_addr);
- pipe->state |= ISP_PIPELINE_IDLE_INPUT;
- }
-
- if (res->state == ISP_PIPELINE_STREAM_SINGLESHOT) {
- if (isp_pipeline_ready(pipe))
- omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_SINGLESHOT);
- } else {
- /* If an underrun occurs, the video queue operation handler will
- * restart the resizer. Otherwise restart it immediately.
- */
- if (restart)
- resizer_enable_oneshot(res);
- }
-}
-
-/*
- * omap3isp_resizer_isr - ISP resizer interrupt handler
- *
- * Manage the resizer video buffers and configure shadowed and busy-locked
- * registers.
- */
-void omap3isp_resizer_isr(struct isp_res_device *res)
-{
- struct v4l2_mbus_framefmt *informat, *outformat;
-
- if (omap3isp_module_sync_is_stopping(&res->wait, &res->stopping))
- return;
-
- if (res->applycrop) {
- outformat = __resizer_get_format(res, NULL, RESZ_PAD_SOURCE,
- V4L2_SUBDEV_FORMAT_ACTIVE);
- informat = __resizer_get_format(res, NULL, RESZ_PAD_SINK,
- V4L2_SUBDEV_FORMAT_ACTIVE);
- resizer_set_crop_params(res, informat, outformat);
- res->applycrop = 0;
- }
-
- resizer_isr_buffer(res);
-}
-
-/* -----------------------------------------------------------------------------
- * ISP video operations
- */
-
-static int resizer_video_queue(struct isp_video *video,
- struct isp_buffer *buffer)
-{
- struct isp_res_device *res = &video->isp->isp_res;
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- resizer_set_inaddr(res, buffer->isp_addr);
-
- /*
- * We now have a buffer queued on the output. Despite what the
- * TRM says, the resizer can't be restarted immediately.
- * Enabling it in one shot mode in the middle of a frame (or at
- * least asynchronously to the frame) results in the output
- * being shifted randomly left/right and up/down, as if the
- * hardware didn't synchronize itself to the beginning of the
- * frame correctly.
- *
- * Restart the resizer on the next sync interrupt if running in
- * continuous mode or when starting the stream.
- */
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- resizer_set_outaddr(res, buffer->isp_addr);
-
- return 0;
-}
-
-static const struct isp_video_operations resizer_video_ops = {
- .queue = resizer_video_queue,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev operations
- */
-
-/*
- * resizer_set_stream - Enable/Disable streaming on resizer subdev
- * @sd: ISP resizer V4L2 subdev
- * @enable: 1 == Enable, 0 == Disable
- *
- * The resizer hardware can't be enabled without a memory buffer to write to.
- * As the s_stream operation is called in response to a STREAMON call without
- * any buffer queued yet, just update the state field and return immediately.
- * The resizer will be enabled in resizer_video_queue().
- */
-static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct isp_video *video_out = &res->video_out;
- struct isp_device *isp = to_isp_device(res);
- struct device *dev = to_device(res);
-
- if (res->state == ISP_PIPELINE_STREAM_STOPPED) {
- if (enable == ISP_PIPELINE_STREAM_STOPPED)
- return 0;
-
- omap3isp_subclk_enable(isp, OMAP3_ISP_SUBCLK_RESIZER);
- resizer_configure(res);
- resizer_print_status(res);
- }
-
- switch (enable) {
- case ISP_PIPELINE_STREAM_CONTINUOUS:
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
- if (video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED) {
- resizer_enable_oneshot(res);
- isp_video_dmaqueue_flags_clr(video_out);
- }
- break;
-
- case ISP_PIPELINE_STREAM_SINGLESHOT:
- if (res->input == RESIZER_INPUT_MEMORY)
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_READ);
- omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_RESIZER_WRITE);
-
- resizer_enable_oneshot(res);
- break;
-
- case ISP_PIPELINE_STREAM_STOPPED:
- if (omap3isp_module_sync_idle(&sd->entity, &res->wait,
- &res->stopping))
- dev_dbg(dev, "%s: module stop timeout.\n", sd->name);
- omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_RESIZER_READ |
- OMAP3_ISP_SBL_RESIZER_WRITE);
- omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_RESIZER);
- isp_video_dmaqueue_flags_clr(video_out);
- break;
- }
-
- res->state = enable;
- return 0;
-}
-
-/*
- * resizer_try_crop - mangles crop parameters.
- */
-static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
- const struct v4l2_mbus_framefmt *source,
- struct v4l2_rect *crop)
-{
- const unsigned int spv = DEFAULT_PHASE;
- const unsigned int sph = DEFAULT_PHASE;
-
- /* Crop rectangle is constrained by the output size so that zoom ratio
- * cannot exceed +/-4.0.
- */
- unsigned int min_width =
- ((32 * sph + (source->width - 1) * 64 + 16) >> 8) + 7;
- unsigned int min_height =
- ((32 * spv + (source->height - 1) * 64 + 16) >> 8) + 4;
- unsigned int max_width =
- ((64 * sph + (source->width - 1) * 1024 + 32) >> 8) + 7;
- unsigned int max_height =
- ((64 * spv + (source->height - 1) * 1024 + 32) >> 8) + 7;
-
- crop->width = clamp_t(u32, crop->width, min_width, max_width);
- crop->height = clamp_t(u32, crop->height, min_height, max_height);
-
- /* Crop can not go beyond of the input rectangle */
- crop->left = clamp_t(u32, crop->left, 0, sink->width - MIN_IN_WIDTH);
- crop->width = clamp_t(u32, crop->width, MIN_IN_WIDTH,
- sink->width - crop->left);
- crop->top = clamp_t(u32, crop->top, 0, sink->height - MIN_IN_HEIGHT);
- crop->height = clamp_t(u32, crop->height, MIN_IN_HEIGHT,
- sink->height - crop->top);
-}
-
-/*
- * resizer_get_selection - Retrieve a selection rectangle on a pad
- * @sd: ISP resizer V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangles are the crop rectangles on the sink pad.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int resizer_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format_source;
- struct v4l2_mbus_framefmt *format_sink;
- struct resizer_ratio ratio;
-
- if (sel->pad != RESZ_PAD_SINK)
- return -EINVAL;
-
- format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
- sel->which);
- format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
- sel->which);
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = INT_MAX;
- sel->r.height = INT_MAX;
-
- resizer_try_crop(format_sink, format_source, &sel->r);
- resizer_calc_ratios(res, &sel->r, format_source, &ratio);
- break;
-
- case V4L2_SEL_TGT_CROP:
- sel->r = *__resizer_get_crop(res, fh, sel->which);
- resizer_calc_ratios(res, &sel->r, format_source, &ratio);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/*
- * resizer_set_selection - Set a selection rectangle on a pad
- * @sd: ISP resizer V4L2 subdevice
- * @fh: V4L2 subdev file handle
- * @sel: Selection rectangle
- *
- * The only supported rectangle is the actual crop rectangle on the sink pad.
- *
- * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag
- * was always set.
- *
- * Return 0 on success or a negative error code otherwise.
- */
-static int resizer_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct isp_device *isp = to_isp_device(res);
- struct v4l2_mbus_framefmt *format_sink, *format_source;
- struct resizer_ratio ratio;
-
- if (sel->target != V4L2_SEL_TGT_CROP ||
- sel->pad != RESZ_PAD_SINK)
- return -EINVAL;
-
- format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
- sel->which);
- format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
- sel->which);
-
- dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
- sel->r.left, sel->r.top, sel->r.width, sel->r.height,
- sel->which);
-
- dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
- format_sink->width, format_sink->height,
- format_source->width, format_source->height);
-
- /* Clamp the crop rectangle to the bounds, and then mangle it further to
- * fulfill the TRM equations. Store the clamped but otherwise unmangled
- * rectangle to avoid cropping the input multiple times: when an
- * application sets the output format, the current crop rectangle is
- * mangled during crop rectangle computation, which would lead to a new,
- * smaller input crop rectangle every time the output size is set if we
- * stored the mangled rectangle.
- */
- resizer_try_crop(format_sink, format_source, &sel->r);
- *__resizer_get_crop(res, fh, sel->which) = sel->r;
- resizer_calc_ratios(res, &sel->r, format_source, &ratio);
-
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
- return 0;
-
- res->ratio = ratio;
- res->crop.active = sel->r;
-
- /*
- * set_selection can be called while streaming is on. In this case the
- * crop values will be set in the next IRQ.
- */
- if (res->state != ISP_PIPELINE_STREAM_STOPPED)
- res->applycrop = 1;
-
- return 0;
-}
-
-/* resizer pixel formats */
-static const unsigned int resizer_formats[] = {
- V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16,
-};
-
-static unsigned int resizer_max_in_width(struct isp_res_device *res)
-{
- struct isp_device *isp = to_isp_device(res);
-
- if (res->input == RESIZER_INPUT_MEMORY) {
- return MAX_IN_WIDTH_MEMORY_MODE;
- } else {
- if (isp->revision == ISP_REVISION_1_0)
- return MAX_IN_WIDTH_ONTHEFLY_MODE_ES1;
- else
- return MAX_IN_WIDTH_ONTHEFLY_MODE_ES2;
- }
-}
-
-/*
- * resizer_try_format - Handle try format by pad subdev method
- * @res : ISP resizer device
- * @fh : V4L2 subdev file handle
- * @pad : pad num
- * @fmt : pointer to v4l2 format structure
- * @which : wanted subdev format
- */
-static void resizer_try_format(struct isp_res_device *res,
- struct v4l2_subdev_fh *fh, unsigned int pad,
- struct v4l2_mbus_framefmt *fmt,
- enum v4l2_subdev_format_whence which)
-{
- struct v4l2_mbus_framefmt *format;
- struct resizer_ratio ratio;
- struct v4l2_rect crop;
-
- switch (pad) {
- case RESZ_PAD_SINK:
- if (fmt->code != V4L2_MBUS_FMT_YUYV8_1X16 &&
- fmt->code != V4L2_MBUS_FMT_UYVY8_1X16)
- fmt->code = V4L2_MBUS_FMT_YUYV8_1X16;
-
- fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH,
- resizer_max_in_width(res));
- fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT,
- MAX_IN_HEIGHT);
- break;
-
- case RESZ_PAD_SOURCE:
- format = __resizer_get_format(res, fh, RESZ_PAD_SINK, which);
- fmt->code = format->code;
-
- crop = *__resizer_get_crop(res, fh, which);
- resizer_calc_ratios(res, &crop, fmt, &ratio);
- break;
- }
-
- fmt->colorspace = V4L2_COLORSPACE_JPEG;
- fmt->field = V4L2_FIELD_NONE;
-}
-
-/*
- * resizer_enum_mbus_code - Handle pixel format enumeration
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @code : pointer to v4l2_subdev_mbus_code_enum structure
- * return -EINVAL or zero on success
- */
-static int resizer_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- if (code->pad == RESZ_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(resizer_formats))
- return -EINVAL;
-
- code->code = resizer_formats[code->index];
- } else {
- if (code->index != 0)
- return -EINVAL;
-
- format = __resizer_get_format(res, fh, RESZ_PAD_SINK,
- V4L2_SUBDEV_FORMAT_TRY);
- code->code = format->code;
- }
-
- return 0;
-}
-
-static int resizer_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt format;
-
- if (fse->index != 0)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = 1;
- format.height = 1;
- resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->min_width = format.width;
- fse->min_height = format.height;
-
- if (format.code != fse->code)
- return -EINVAL;
-
- format.code = fse->code;
- format.width = -1;
- format.height = -1;
- resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY);
- fse->max_width = format.width;
- fse->max_height = format.height;
-
- return 0;
-}
-
-/*
- * resizer_get_format - Handle get format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt : pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
-
- format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- fmt->format = *format;
- return 0;
-}
-
-/*
- * resizer_set_format - Handle set format by pads subdev method
- * @sd : pointer to v4l2 subdev structure
- * @fh : V4L2 subdev file handle
- * @fmt : pointer to v4l2 subdev format structure
- * return -EINVAL or zero on success
- */
-static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *crop;
-
- format = __resizer_get_format(res, fh, fmt->pad, fmt->which);
- if (format == NULL)
- return -EINVAL;
-
- resizer_try_format(res, fh, fmt->pad, &fmt->format, fmt->which);
- *format = fmt->format;
-
- if (fmt->pad == RESZ_PAD_SINK) {
- /* reset crop rectangle */
- crop = __resizer_get_crop(res, fh, fmt->which);
- crop->left = 0;
- crop->top = 0;
- crop->width = fmt->format.width;
- crop->height = fmt->format.height;
-
- /* Propagate the format from sink to source */
- format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
- fmt->which);
- *format = fmt->format;
- resizer_try_format(res, fh, RESZ_PAD_SOURCE, format,
- fmt->which);
- }
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- /* Compute and store the active crop rectangle and resizer
- * ratios. format already points to the source pad active
- * format.
- */
- res->crop.active = res->crop.request;
- resizer_calc_ratios(res, &res->crop.active, format,
- &res->ratio);
- }
-
- return 0;
-}
-
-/*
- * resizer_init_formats - Initialize formats on all pads
- * @sd: ISP resizer V4L2 subdevice
- * @fh: V4L2 subdev file handle
- *
- * Initialize all pad formats with default values. If fh is not NULL, try
- * formats are initialized on the file handle. Otherwise active formats are
- * initialized on the device.
- */
-static int resizer_init_formats(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh)
-{
- struct v4l2_subdev_format format;
-
- memset(&format, 0, sizeof(format));
- format.pad = RESZ_PAD_SINK;
- format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- format.format.code = V4L2_MBUS_FMT_YUYV8_1X16;
- format.format.width = 4096;
- format.format.height = 4096;
- resizer_set_format(sd, fh, &format);
-
- return 0;
-}
-
-/* subdev video operations */
-static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = {
- .s_stream = resizer_set_stream,
-};
-
-/* subdev pad operations */
-static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
- .enum_mbus_code = resizer_enum_mbus_code,
- .enum_frame_size = resizer_enum_frame_size,
- .get_fmt = resizer_get_format,
- .set_fmt = resizer_set_format,
- .get_selection = resizer_get_selection,
- .set_selection = resizer_set_selection,
-};
-
-/* subdev operations */
-static const struct v4l2_subdev_ops resizer_v4l2_ops = {
- .video = &resizer_v4l2_video_ops,
- .pad = &resizer_v4l2_pad_ops,
-};
-
-/* subdev internal operations */
-static const struct v4l2_subdev_internal_ops resizer_v4l2_internal_ops = {
- .open = resizer_init_formats,
-};
-
-/* -----------------------------------------------------------------------------
- * Media entity operations
- */
-
-/*
- * resizer_link_setup - Setup resizer connections.
- * @entity : Pointer to media entity structure
- * @local : Pointer to local pad array
- * @remote : Pointer to remote pad array
- * @flags : Link flags
- * return -EINVAL or zero on success
- */
-static int resizer_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
-
- switch (local->index | media_entity_type(remote->entity)) {
- case RESZ_PAD_SINK | MEDIA_ENT_T_DEVNODE:
- /* read from memory */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (res->input == RESIZER_INPUT_VP)
- return -EBUSY;
- res->input = RESIZER_INPUT_MEMORY;
- } else {
- if (res->input == RESIZER_INPUT_MEMORY)
- res->input = RESIZER_INPUT_NONE;
- }
- break;
-
- case RESZ_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV:
- /* read from ccdc or previewer */
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (res->input == RESIZER_INPUT_MEMORY)
- return -EBUSY;
- res->input = RESIZER_INPUT_VP;
- } else {
- if (res->input == RESIZER_INPUT_VP)
- res->input = RESIZER_INPUT_NONE;
- }
- break;
-
- case RESZ_PAD_SOURCE | MEDIA_ENT_T_DEVNODE:
- /* resizer always write to memory */
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* media operations */
-static const struct media_entity_operations resizer_media_ops = {
- .link_setup = resizer_link_setup,
- .link_validate = v4l2_subdev_link_validate,
-};
-
-void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
-{
- v4l2_device_unregister_subdev(&res->subdev);
- omap3isp_video_unregister(&res->video_in);
- omap3isp_video_unregister(&res->video_out);
-}
-
-int omap3isp_resizer_register_entities(struct isp_res_device *res,
- struct v4l2_device *vdev)
-{
- int ret;
-
- /* Register the subdev and video nodes. */
- ret = v4l2_device_register_subdev(vdev, &res->subdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_in, vdev);
- if (ret < 0)
- goto error;
-
- ret = omap3isp_video_register(&res->video_out, vdev);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- omap3isp_resizer_unregister_entities(res);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * ISP resizer initialization and cleanup
- */
-
-/*
- * resizer_init_entities - Initialize resizer subdev and media entity.
- * @res : Pointer to resizer device structure
- * return -ENOMEM or zero on success
- */
-static int resizer_init_entities(struct isp_res_device *res)
-{
- struct v4l2_subdev *sd = &res->subdev;
- struct media_pad *pads = res->pads;
- struct media_entity *me = &sd->entity;
- int ret;
-
- res->input = RESIZER_INPUT_NONE;
-
- v4l2_subdev_init(sd, &resizer_v4l2_ops);
- sd->internal_ops = &resizer_v4l2_internal_ops;
- strlcpy(sd->name, "OMAP3 ISP resizer", sizeof(sd->name));
- sd->grp_id = 1 << 16; /* group ID for isp subdevs */
- v4l2_set_subdevdata(sd, res);
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- pads[RESZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- pads[RESZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
-
- me->ops = &resizer_media_ops;
- ret = media_entity_init(me, RESZ_PADS_NUM, pads, 0);
- if (ret < 0)
- return ret;
-
- resizer_init_formats(sd, NULL);
-
- res->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- res->video_in.ops = &resizer_video_ops;
- res->video_in.isp = to_isp_device(res);
- res->video_in.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
- res->video_in.bpl_alignment = 32;
- res->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- res->video_out.ops = &resizer_video_ops;
- res->video_out.isp = to_isp_device(res);
- res->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 2 * 3;
- res->video_out.bpl_alignment = 32;
-
- ret = omap3isp_video_init(&res->video_in, "resizer");
- if (ret < 0)
- goto error_video_in;
-
- ret = omap3isp_video_init(&res->video_out, "resizer");
- if (ret < 0)
- goto error_video_out;
-
- /* Connect the video nodes to the resizer subdev. */
- ret = media_entity_create_link(&res->video_in.video.entity, 0,
- &res->subdev.entity, RESZ_PAD_SINK, 0);
- if (ret < 0)
- goto error_link;
-
- ret = media_entity_create_link(&res->subdev.entity, RESZ_PAD_SOURCE,
- &res->video_out.video.entity, 0, 0);
- if (ret < 0)
- goto error_link;
-
- return 0;
-
-error_link:
- omap3isp_video_cleanup(&res->video_out);
-error_video_out:
- omap3isp_video_cleanup(&res->video_in);
-error_video_in:
- media_entity_cleanup(&res->subdev.entity);
- return ret;
-}
-
-/*
- * isp_resizer_init - Resizer initialization.
- * @isp : Pointer to ISP device
- * return -ENOMEM or zero on success
- */
-int omap3isp_resizer_init(struct isp_device *isp)
-{
- struct isp_res_device *res = &isp->isp_res;
-
- init_waitqueue_head(&res->wait);
- atomic_set(&res->stopping, 0);
- return resizer_init_entities(res);
-}
-
-void omap3isp_resizer_cleanup(struct isp_device *isp)
-{
- struct isp_res_device *res = &isp->isp_res;
-
- omap3isp_video_cleanup(&res->video_in);
- omap3isp_video_cleanup(&res->video_out);
- media_entity_cleanup(&res->subdev.entity);
-}
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
deleted file mode 100644
index b8640be692f1..000000000000
--- a/drivers/media/video/omap3isp/ispstat.c
+++ /dev/null
@@ -1,1102 +0,0 @@
-/*
- * ispstat.c
- *
- * TI OMAP3 ISP - Statistics core
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc
- *
- * Contacts: David Cohen <dacohen@gmail.com>
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include "isp.h"
-
-#define IS_COHERENT_BUF(stat) ((stat)->dma_ch >= 0)
-
-/*
- * MAGIC_SIZE must always be the greatest common divisor of
- * AEWB_PACKET_SIZE and AF_PAXEL_SIZE.
- */
-#define MAGIC_SIZE 16
-#define MAGIC_NUM 0x55
-
-/* HACK: AF module seems to be writing one more paxel data than it should. */
-#define AF_EXTRA_DATA OMAP3ISP_AF_PAXEL_SIZE
-
-/*
- * HACK: H3A modules go to an invalid state after have a SBL overflow. It makes
- * the next buffer to start to be written in the same point where the overflow
- * occurred instead of the configured address. The only known way to make it to
- * go back to a valid state is having a valid buffer processing. Of course it
- * requires at least a doubled buffer size to avoid an access to invalid memory
- * region. But it does not fix everything. It may happen more than one
- * consecutive SBL overflows. In that case, it might be unpredictable how many
- * buffers the allocated memory should fit. For that case, a recover
- * configuration was created. It produces the minimum buffer size for each H3A
- * module and decrease the change for more SBL overflows. This recover state
- * will be enabled every time a SBL overflow occur. As the output buffer size
- * isn't big, it's possible to have an extra size able to fit many recover
- * buffers making it extreamily unlikely to have an access to invalid memory
- * region.
- */
-#define NUM_H3A_RECOVER_BUFS 10
-
-/*
- * HACK: Because of HW issues the generic layer sometimes need to have
- * different behaviour for different statistic modules.
- */
-#define IS_H3A_AF(stat) ((stat) == &(stat)->isp->isp_af)
-#define IS_H3A_AEWB(stat) ((stat) == &(stat)->isp->isp_aewb)
-#define IS_H3A(stat) (IS_H3A_AF(stat) || IS_H3A_AEWB(stat))
-
-static void __isp_stat_buf_sync_magic(struct ispstat *stat,
- struct ispstat_buffer *buf,
- u32 buf_size, enum dma_data_direction dir,
- void (*dma_sync)(struct device *,
- dma_addr_t, unsigned long, size_t,
- enum dma_data_direction))
-{
- struct device *dev = stat->isp->dev;
- struct page *pg;
- dma_addr_t dma_addr;
- u32 offset;
-
- /* Initial magic words */
- pg = vmalloc_to_page(buf->virt_addr);
- dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
- dma_sync(dev, dma_addr, 0, MAGIC_SIZE, dir);
-
- /* Final magic words */
- pg = vmalloc_to_page(buf->virt_addr + buf_size);
- dma_addr = pfn_to_dma(dev, page_to_pfn(pg));
- offset = ((u32)buf->virt_addr + buf_size) & ~PAGE_MASK;
- dma_sync(dev, dma_addr, offset, MAGIC_SIZE, dir);
-}
-
-static void isp_stat_buf_sync_magic_for_device(struct ispstat *stat,
- struct ispstat_buffer *buf,
- u32 buf_size,
- enum dma_data_direction dir)
-{
- if (IS_COHERENT_BUF(stat))
- return;
-
- __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
- dma_sync_single_range_for_device);
-}
-
-static void isp_stat_buf_sync_magic_for_cpu(struct ispstat *stat,
- struct ispstat_buffer *buf,
- u32 buf_size,
- enum dma_data_direction dir)
-{
- if (IS_COHERENT_BUF(stat))
- return;
-
- __isp_stat_buf_sync_magic(stat, buf, buf_size, dir,
- dma_sync_single_range_for_cpu);
-}
-
-static int isp_stat_buf_check_magic(struct ispstat *stat,
- struct ispstat_buffer *buf)
-{
- const u32 buf_size = IS_H3A_AF(stat) ?
- buf->buf_size + AF_EXTRA_DATA : buf->buf_size;
- u8 *w;
- u8 *end;
- int ret = -EINVAL;
-
- isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
-
- /* Checking initial magic numbers. They shouldn't be here anymore. */
- for (w = buf->virt_addr, end = w + MAGIC_SIZE; w < end; w++)
- if (likely(*w != MAGIC_NUM))
- ret = 0;
-
- if (ret) {
- dev_dbg(stat->isp->dev, "%s: beginning magic check does not "
- "match.\n", stat->subdev.name);
- return ret;
- }
-
- /* Checking magic numbers at the end. They must be still here. */
- for (w = buf->virt_addr + buf_size, end = w + MAGIC_SIZE;
- w < end; w++) {
- if (unlikely(*w != MAGIC_NUM)) {
- dev_dbg(stat->isp->dev, "%s: endding magic check does "
- "not match.\n", stat->subdev.name);
- return -EINVAL;
- }
- }
-
- isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
- DMA_FROM_DEVICE);
-
- return 0;
-}
-
-static void isp_stat_buf_insert_magic(struct ispstat *stat,
- struct ispstat_buffer *buf)
-{
- const u32 buf_size = IS_H3A_AF(stat) ?
- stat->buf_size + AF_EXTRA_DATA : stat->buf_size;
-
- isp_stat_buf_sync_magic_for_cpu(stat, buf, buf_size, DMA_FROM_DEVICE);
-
- /*
- * Inserting MAGIC_NUM at the beginning and end of the buffer.
- * buf->buf_size is set only after the buffer is queued. For now the
- * right buf_size for the current configuration is pointed by
- * stat->buf_size.
- */
- memset(buf->virt_addr, MAGIC_NUM, MAGIC_SIZE);
- memset(buf->virt_addr + buf_size, MAGIC_NUM, MAGIC_SIZE);
-
- isp_stat_buf_sync_magic_for_device(stat, buf, buf_size,
- DMA_BIDIRECTIONAL);
-}
-
-static void isp_stat_buf_sync_for_device(struct ispstat *stat,
- struct ispstat_buffer *buf)
-{
- if (IS_COHERENT_BUF(stat))
- return;
-
- dma_sync_sg_for_device(stat->isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents, DMA_FROM_DEVICE);
-}
-
-static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
- struct ispstat_buffer *buf)
-{
- if (IS_COHERENT_BUF(stat))
- return;
-
- dma_sync_sg_for_cpu(stat->isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents, DMA_FROM_DEVICE);
-}
-
-static void isp_stat_buf_clear(struct ispstat *stat)
-{
- int i;
-
- for (i = 0; i < STAT_MAX_BUFS; i++)
- stat->buf[i].empty = 1;
-}
-
-static struct ispstat_buffer *
-__isp_stat_buf_find(struct ispstat *stat, int look_empty)
-{
- struct ispstat_buffer *found = NULL;
- int i;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *curr = &stat->buf[i];
-
- /*
- * Don't select the buffer which is being copied to
- * userspace or used by the module.
- */
- if (curr == stat->locked_buf || curr == stat->active_buf)
- continue;
-
- /* Don't select uninitialised buffers if it's not required */
- if (!look_empty && curr->empty)
- continue;
-
- /* Pick uninitialised buffer over anything else if look_empty */
- if (curr->empty) {
- found = curr;
- break;
- }
-
- /* Choose the oldest buffer */
- if (!found ||
- (s32)curr->frame_number - (s32)found->frame_number < 0)
- found = curr;
- }
-
- return found;
-}
-
-static inline struct ispstat_buffer *
-isp_stat_buf_find_oldest(struct ispstat *stat)
-{
- return __isp_stat_buf_find(stat, 0);
-}
-
-static inline struct ispstat_buffer *
-isp_stat_buf_find_oldest_or_empty(struct ispstat *stat)
-{
- return __isp_stat_buf_find(stat, 1);
-}
-
-static int isp_stat_buf_queue(struct ispstat *stat)
-{
- if (!stat->active_buf)
- return STAT_NO_BUF;
-
- do_gettimeofday(&stat->active_buf->ts);
-
- stat->active_buf->buf_size = stat->buf_size;
- if (isp_stat_buf_check_magic(stat, stat->active_buf)) {
- dev_dbg(stat->isp->dev, "%s: data wasn't properly written.\n",
- stat->subdev.name);
- return STAT_NO_BUF;
- }
- stat->active_buf->config_counter = stat->config_counter;
- stat->active_buf->frame_number = stat->frame_number;
- stat->active_buf->empty = 0;
- stat->active_buf = NULL;
-
- return STAT_BUF_DONE;
-}
-
-/* Get next free buffer to write the statistics to and mark it active. */
-static void isp_stat_buf_next(struct ispstat *stat)
-{
- if (unlikely(stat->active_buf))
- /* Overwriting unused active buffer */
- dev_dbg(stat->isp->dev, "%s: new buffer requested without "
- "queuing active one.\n",
- stat->subdev.name);
- else
- stat->active_buf = isp_stat_buf_find_oldest_or_empty(stat);
-}
-
-static void isp_stat_buf_release(struct ispstat *stat)
-{
- unsigned long flags;
-
- isp_stat_buf_sync_for_device(stat, stat->locked_buf);
- spin_lock_irqsave(&stat->isp->stat_lock, flags);
- stat->locked_buf = NULL;
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
-}
-
-/* Get buffer to userspace. */
-static struct ispstat_buffer *isp_stat_buf_get(struct ispstat *stat,
- struct omap3isp_stat_data *data)
-{
- int rval = 0;
- unsigned long flags;
- struct ispstat_buffer *buf;
-
- spin_lock_irqsave(&stat->isp->stat_lock, flags);
-
- while (1) {
- buf = isp_stat_buf_find_oldest(stat);
- if (!buf) {
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
- dev_dbg(stat->isp->dev, "%s: cannot find a buffer.\n",
- stat->subdev.name);
- return ERR_PTR(-EBUSY);
- }
- if (isp_stat_buf_check_magic(stat, buf)) {
- dev_dbg(stat->isp->dev, "%s: current buffer has "
- "corrupted data\n.", stat->subdev.name);
- /* Mark empty because it doesn't have valid data. */
- buf->empty = 1;
- } else {
- /* Buffer isn't corrupted. */
- break;
- }
- }
-
- stat->locked_buf = buf;
-
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
-
- if (buf->buf_size > data->buf_size) {
- dev_warn(stat->isp->dev, "%s: userspace's buffer size is "
- "not enough.\n", stat->subdev.name);
- isp_stat_buf_release(stat);
- return ERR_PTR(-EINVAL);
- }
-
- isp_stat_buf_sync_for_cpu(stat, buf);
-
- rval = copy_to_user(data->buf,
- buf->virt_addr,
- buf->buf_size);
-
- if (rval) {
- dev_info(stat->isp->dev,
- "%s: failed copying %d bytes of stat data\n",
- stat->subdev.name, rval);
- buf = ERR_PTR(-EFAULT);
- isp_stat_buf_release(stat);
- }
-
- return buf;
-}
-
-static void isp_stat_bufs_free(struct ispstat *stat)
-{
- struct isp_device *isp = stat->isp;
- int i;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *buf = &stat->buf[i];
-
- if (!IS_COHERENT_BUF(stat)) {
- if (IS_ERR_OR_NULL((void *)buf->iommu_addr))
- continue;
- if (buf->iovm)
- dma_unmap_sg(isp->dev, buf->iovm->sgt->sgl,
- buf->iovm->sgt->nents,
- DMA_FROM_DEVICE);
- omap_iommu_vfree(isp->domain, isp->dev,
- buf->iommu_addr);
- } else {
- if (!buf->virt_addr)
- continue;
- dma_free_coherent(stat->isp->dev, stat->buf_alloc_size,
- buf->virt_addr, buf->dma_addr);
- }
- buf->iommu_addr = 0;
- buf->iovm = NULL;
- buf->dma_addr = 0;
- buf->virt_addr = NULL;
- buf->empty = 1;
- }
-
- dev_dbg(stat->isp->dev, "%s: all buffers were freed.\n",
- stat->subdev.name);
-
- stat->buf_alloc_size = 0;
- stat->active_buf = NULL;
-}
-
-static int isp_stat_bufs_alloc_iommu(struct ispstat *stat, unsigned int size)
-{
- struct isp_device *isp = stat->isp;
- int i;
-
- stat->buf_alloc_size = size;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *buf = &stat->buf[i];
- struct iovm_struct *iovm;
-
- WARN_ON(buf->dma_addr);
- buf->iommu_addr = omap_iommu_vmalloc(isp->domain, isp->dev, 0,
- size, IOMMU_FLAG);
- if (IS_ERR((void *)buf->iommu_addr)) {
- dev_err(stat->isp->dev,
- "%s: Can't acquire memory for "
- "buffer %d\n", stat->subdev.name, i);
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
-
- iovm = omap_find_iovm_area(isp->dev, buf->iommu_addr);
- if (!iovm ||
- !dma_map_sg(isp->dev, iovm->sgt->sgl, iovm->sgt->nents,
- DMA_FROM_DEVICE)) {
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
- buf->iovm = iovm;
-
- buf->virt_addr = omap_da_to_va(stat->isp->dev,
- (u32)buf->iommu_addr);
- buf->empty = 1;
- dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
- "iommu_addr=0x%08lx virt_addr=0x%08lx",
- stat->subdev.name, i, buf->iommu_addr,
- (unsigned long)buf->virt_addr);
- }
-
- return 0;
-}
-
-static int isp_stat_bufs_alloc_dma(struct ispstat *stat, unsigned int size)
-{
- int i;
-
- stat->buf_alloc_size = size;
-
- for (i = 0; i < STAT_MAX_BUFS; i++) {
- struct ispstat_buffer *buf = &stat->buf[i];
-
- WARN_ON(buf->iommu_addr);
- buf->virt_addr = dma_alloc_coherent(stat->isp->dev, size,
- &buf->dma_addr, GFP_KERNEL | GFP_DMA);
-
- if (!buf->virt_addr || !buf->dma_addr) {
- dev_info(stat->isp->dev,
- "%s: Can't acquire memory for "
- "DMA buffer %d\n", stat->subdev.name, i);
- isp_stat_bufs_free(stat);
- return -ENOMEM;
- }
- buf->empty = 1;
-
- dev_dbg(stat->isp->dev, "%s: buffer[%d] allocated."
- "dma_addr=0x%08lx virt_addr=0x%08lx\n",
- stat->subdev.name, i, (unsigned long)buf->dma_addr,
- (unsigned long)buf->virt_addr);
- }
-
- return 0;
-}
-
-static int isp_stat_bufs_alloc(struct ispstat *stat, u32 size)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&stat->isp->stat_lock, flags);
-
- BUG_ON(stat->locked_buf != NULL);
-
- /* Are the old buffers big enough? */
- if (stat->buf_alloc_size >= size) {
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
- return 0;
- }
-
- if (stat->state != ISPSTAT_DISABLED || stat->buf_processing) {
- dev_info(stat->isp->dev,
- "%s: trying to allocate memory when busy\n",
- stat->subdev.name);
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
- return -EBUSY;
- }
-
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
-
- isp_stat_bufs_free(stat);
-
- if (IS_COHERENT_BUF(stat))
- return isp_stat_bufs_alloc_dma(stat, size);
- else
- return isp_stat_bufs_alloc_iommu(stat, size);
-}
-
-static void isp_stat_queue_event(struct ispstat *stat, int err)
-{
- struct video_device *vdev = stat->subdev.devnode;
- struct v4l2_event event;
- struct omap3isp_stat_event_status *status = (void *)event.u.data;
-
- memset(&event, 0, sizeof(event));
- if (!err) {
- status->frame_number = stat->frame_number;
- status->config_counter = stat->config_counter;
- } else {
- status->buf_err = 1;
- }
- event.type = stat->event_type;
- v4l2_event_queue(vdev, &event);
-}
-
-
-/*
- * omap3isp_stat_request_statistics - Request statistics.
- * @data: Pointer to return statistics data.
- *
- * Returns 0 if successful.
- */
-int omap3isp_stat_request_statistics(struct ispstat *stat,
- struct omap3isp_stat_data *data)
-{
- struct ispstat_buffer *buf;
-
- if (stat->state != ISPSTAT_ENABLED) {
- dev_dbg(stat->isp->dev, "%s: engine not enabled.\n",
- stat->subdev.name);
- return -EINVAL;
- }
-
- mutex_lock(&stat->ioctl_lock);
- buf = isp_stat_buf_get(stat, data);
- if (IS_ERR(buf)) {
- mutex_unlock(&stat->ioctl_lock);
- return PTR_ERR(buf);
- }
-
- data->ts = buf->ts;
- data->config_counter = buf->config_counter;
- data->frame_number = buf->frame_number;
- data->buf_size = buf->buf_size;
-
- buf->empty = 1;
- isp_stat_buf_release(stat);
- mutex_unlock(&stat->ioctl_lock);
-
- return 0;
-}
-
-/*
- * omap3isp_stat_config - Receives new statistic engine configuration.
- * @new_conf: Pointer to config structure.
- *
- * Returns 0 if successful, -EINVAL if new_conf pointer is NULL, -ENOMEM if
- * was unable to allocate memory for the buffer, or other errors if parameters
- * are invalid.
- */
-int omap3isp_stat_config(struct ispstat *stat, void *new_conf)
-{
- int ret;
- unsigned long irqflags;
- struct ispstat_generic_config *user_cfg = new_conf;
- u32 buf_size = user_cfg->buf_size;
-
- if (!new_conf) {
- dev_dbg(stat->isp->dev, "%s: configuration is NULL\n",
- stat->subdev.name);
- return -EINVAL;
- }
-
- mutex_lock(&stat->ioctl_lock);
-
- dev_dbg(stat->isp->dev, "%s: configuring module with buffer "
- "size=0x%08lx\n", stat->subdev.name, (unsigned long)buf_size);
-
- ret = stat->ops->validate_params(stat, new_conf);
- if (ret) {
- mutex_unlock(&stat->ioctl_lock);
- dev_dbg(stat->isp->dev, "%s: configuration values are "
- "invalid.\n", stat->subdev.name);
- return ret;
- }
-
- if (buf_size != user_cfg->buf_size)
- dev_dbg(stat->isp->dev, "%s: driver has corrected buffer size "
- "request to 0x%08lx\n", stat->subdev.name,
- (unsigned long)user_cfg->buf_size);
-
- /*
- * Hack: H3A modules may need a doubled buffer size to avoid access
- * to a invalid memory address after a SBL overflow.
- * The buffer size is always PAGE_ALIGNED.
- * Hack 2: MAGIC_SIZE is added to buf_size so a magic word can be
- * inserted at the end to data integrity check purpose.
- * Hack 3: AF module writes one paxel data more than it should, so
- * the buffer allocation must consider it to avoid invalid memory
- * access.
- * Hack 4: H3A need to allocate extra space for the recover state.
- */
- if (IS_H3A(stat)) {
- buf_size = user_cfg->buf_size * 2 + MAGIC_SIZE;
- if (IS_H3A_AF(stat))
- /*
- * Adding one extra paxel data size for each recover
- * buffer + 2 regular ones.
- */
- buf_size += AF_EXTRA_DATA * (NUM_H3A_RECOVER_BUFS + 2);
- if (stat->recover_priv) {
- struct ispstat_generic_config *recover_cfg =
- stat->recover_priv;
- buf_size += recover_cfg->buf_size *
- NUM_H3A_RECOVER_BUFS;
- }
- buf_size = PAGE_ALIGN(buf_size);
- } else { /* Histogram */
- buf_size = PAGE_ALIGN(user_cfg->buf_size + MAGIC_SIZE);
- }
-
- ret = isp_stat_bufs_alloc(stat, buf_size);
- if (ret) {
- mutex_unlock(&stat->ioctl_lock);
- return ret;
- }
-
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
- stat->ops->set_params(stat, new_conf);
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
-
- /*
- * Returning the right future config_counter for this setup, so
- * userspace can *know* when it has been applied.
- */
- user_cfg->config_counter = stat->config_counter + stat->inc_config;
-
- /* Module has a valid configuration. */
- stat->configured = 1;
- dev_dbg(stat->isp->dev, "%s: module has been successfully "
- "configured.\n", stat->subdev.name);
-
- mutex_unlock(&stat->ioctl_lock);
-
- return 0;
-}
-
-/*
- * isp_stat_buf_process - Process statistic buffers.
- * @buf_state: points out if buffer is ready to be processed. It's necessary
- * because histogram needs to copy the data from internal memory
- * before be able to process the buffer.
- */
-static int isp_stat_buf_process(struct ispstat *stat, int buf_state)
-{
- int ret = STAT_NO_BUF;
-
- if (!atomic_add_unless(&stat->buf_err, -1, 0) &&
- buf_state == STAT_BUF_DONE && stat->state == ISPSTAT_ENABLED) {
- ret = isp_stat_buf_queue(stat);
- isp_stat_buf_next(stat);
- }
-
- return ret;
-}
-
-int omap3isp_stat_pcr_busy(struct ispstat *stat)
-{
- return stat->ops->busy(stat);
-}
-
-int omap3isp_stat_busy(struct ispstat *stat)
-{
- return omap3isp_stat_pcr_busy(stat) | stat->buf_processing |
- (stat->state != ISPSTAT_DISABLED);
-}
-
-/*
- * isp_stat_pcr_enable - Disables/Enables statistic engines.
- * @pcr_enable: 0/1 - Disables/Enables the engine.
- *
- * Must be called from ISP driver when the module is idle and synchronized
- * with CCDC.
- */
-static void isp_stat_pcr_enable(struct ispstat *stat, u8 pcr_enable)
-{
- if ((stat->state != ISPSTAT_ENABLING &&
- stat->state != ISPSTAT_ENABLED) && pcr_enable)
- /* Userspace has disabled the module. Aborting. */
- return;
-
- stat->ops->enable(stat, pcr_enable);
- if (stat->state == ISPSTAT_DISABLING && !pcr_enable)
- stat->state = ISPSTAT_DISABLED;
- else if (stat->state == ISPSTAT_ENABLING && pcr_enable)
- stat->state = ISPSTAT_ENABLED;
-}
-
-void omap3isp_stat_suspend(struct ispstat *stat)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&stat->isp->stat_lock, flags);
-
- if (stat->state != ISPSTAT_DISABLED)
- stat->ops->enable(stat, 0);
- if (stat->state == ISPSTAT_ENABLED)
- stat->state = ISPSTAT_SUSPENDED;
-
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
-}
-
-void omap3isp_stat_resume(struct ispstat *stat)
-{
- /* Module will be re-enabled with its pipeline */
- if (stat->state == ISPSTAT_SUSPENDED)
- stat->state = ISPSTAT_ENABLING;
-}
-
-static void isp_stat_try_enable(struct ispstat *stat)
-{
- unsigned long irqflags;
-
- if (stat->priv == NULL)
- /* driver wasn't initialised */
- return;
-
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
- if (stat->state == ISPSTAT_ENABLING && !stat->buf_processing &&
- stat->buf_alloc_size) {
- /*
- * Userspace's requested to enable the engine but it wasn't yet.
- * Let's do that now.
- */
- stat->update = 1;
- isp_stat_buf_next(stat);
- stat->ops->setup_regs(stat, stat->priv);
- isp_stat_buf_insert_magic(stat, stat->active_buf);
-
- /*
- * H3A module has some hw issues which forces the driver to
- * ignore next buffers even if it was disabled in the meantime.
- * On the other hand, Histogram shouldn't ignore buffers anymore
- * if it's being enabled.
- */
- if (!IS_H3A(stat))
- atomic_set(&stat->buf_err, 0);
-
- isp_stat_pcr_enable(stat, 1);
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- dev_dbg(stat->isp->dev, "%s: module is enabled.\n",
- stat->subdev.name);
- } else {
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- }
-}
-
-void omap3isp_stat_isr_frame_sync(struct ispstat *stat)
-{
- isp_stat_try_enable(stat);
-}
-
-void omap3isp_stat_sbl_overflow(struct ispstat *stat)
-{
- unsigned long irqflags;
-
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
- /*
- * Due to a H3A hw issue which prevents the next buffer to start from
- * the correct memory address, 2 buffers must be ignored.
- */
- atomic_set(&stat->buf_err, 2);
-
- /*
- * If more than one SBL overflow happen in a row, H3A module may access
- * invalid memory region.
- * stat->sbl_ovl_recover is set to tell to the driver to temporarily use
- * a soft configuration which helps to avoid consecutive overflows.
- */
- if (stat->recover_priv)
- stat->sbl_ovl_recover = 1;
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
-}
-
-/*
- * omap3isp_stat_enable - Disable/Enable statistic engine as soon as possible
- * @enable: 0/1 - Disables/Enables the engine.
- *
- * Client should configure all the module registers before this.
- * This function can be called from a userspace request.
- */
-int omap3isp_stat_enable(struct ispstat *stat, u8 enable)
-{
- unsigned long irqflags;
-
- dev_dbg(stat->isp->dev, "%s: user wants to %s module.\n",
- stat->subdev.name, enable ? "enable" : "disable");
-
- /* Prevent enabling while configuring */
- mutex_lock(&stat->ioctl_lock);
-
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
-
- if (!stat->configured && enable) {
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- mutex_unlock(&stat->ioctl_lock);
- dev_dbg(stat->isp->dev, "%s: cannot enable module as it's "
- "never been successfully configured so far.\n",
- stat->subdev.name);
- return -EINVAL;
- }
-
- if (enable) {
- if (stat->state == ISPSTAT_DISABLING)
- /* Previous disabling request wasn't done yet */
- stat->state = ISPSTAT_ENABLED;
- else if (stat->state == ISPSTAT_DISABLED)
- /* Module is now being enabled */
- stat->state = ISPSTAT_ENABLING;
- } else {
- if (stat->state == ISPSTAT_ENABLING) {
- /* Previous enabling request wasn't done yet */
- stat->state = ISPSTAT_DISABLED;
- } else if (stat->state == ISPSTAT_ENABLED) {
- /* Module is now being disabled */
- stat->state = ISPSTAT_DISABLING;
- isp_stat_buf_clear(stat);
- }
- }
-
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- mutex_unlock(&stat->ioctl_lock);
-
- return 0;
-}
-
-int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable)
-{
- struct ispstat *stat = v4l2_get_subdevdata(subdev);
-
- if (enable) {
- /*
- * Only set enable PCR bit if the module was previously
- * enabled through ioct.
- */
- isp_stat_try_enable(stat);
- } else {
- unsigned long flags;
- /* Disable PCR bit and config enable field */
- omap3isp_stat_enable(stat, 0);
- spin_lock_irqsave(&stat->isp->stat_lock, flags);
- stat->ops->enable(stat, 0);
- spin_unlock_irqrestore(&stat->isp->stat_lock, flags);
-
- /*
- * If module isn't busy, a new interrupt may come or not to
- * set the state to DISABLED. As Histogram needs to read its
- * internal memory to clear it, let interrupt handler
- * responsible of changing state to DISABLED. If the last
- * interrupt is coming, it's still safe as the handler will
- * ignore the second time when state is already set to DISABLED.
- * It's necessary to synchronize Histogram with streamoff, once
- * the module may be considered idle before last SDMA transfer
- * starts if we return here.
- */
- if (!omap3isp_stat_pcr_busy(stat))
- omap3isp_stat_isr(stat);
-
- dev_dbg(stat->isp->dev, "%s: module is being disabled\n",
- stat->subdev.name);
- }
-
- return 0;
-}
-
-/*
- * __stat_isr - Interrupt handler for statistic drivers
- */
-static void __stat_isr(struct ispstat *stat, int from_dma)
-{
- int ret = STAT_BUF_DONE;
- int buf_processing;
- unsigned long irqflags;
- struct isp_pipeline *pipe;
-
- /*
- * stat->buf_processing must be set before disable module. It's
- * necessary to not inform too early the buffers aren't busy in case
- * of SDMA is going to be used.
- */
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
- if (stat->state == ISPSTAT_DISABLED) {
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- return;
- }
- buf_processing = stat->buf_processing;
- stat->buf_processing = 1;
- stat->ops->enable(stat, 0);
-
- if (buf_processing && !from_dma) {
- if (stat->state == ISPSTAT_ENABLED) {
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- dev_err(stat->isp->dev,
- "%s: interrupt occurred when module was still "
- "processing a buffer.\n", stat->subdev.name);
- ret = STAT_NO_BUF;
- goto out;
- } else {
- /*
- * Interrupt handler was called from streamoff when
- * the module wasn't busy anymore to ensure it is being
- * disabled after process last buffer. If such buffer
- * processing has already started, no need to do
- * anything else.
- */
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- return;
- }
- }
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
-
- /* If it's busy we can't process this buffer anymore */
- if (!omap3isp_stat_pcr_busy(stat)) {
- if (!from_dma && stat->ops->buf_process)
- /* Module still need to copy data to buffer. */
- ret = stat->ops->buf_process(stat);
- if (ret == STAT_BUF_WAITING_DMA)
- /* Buffer is not ready yet */
- return;
-
- spin_lock_irqsave(&stat->isp->stat_lock, irqflags);
-
- /*
- * Histogram needs to read its internal memory to clear it
- * before be disabled. For that reason, common statistic layer
- * can return only after call stat's buf_process() operator.
- */
- if (stat->state == ISPSTAT_DISABLING) {
- stat->state = ISPSTAT_DISABLED;
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- stat->buf_processing = 0;
- return;
- }
- pipe = to_isp_pipeline(&stat->subdev.entity);
- stat->frame_number = atomic_read(&pipe->frame_number);
-
- /*
- * Before this point, 'ret' stores the buffer's status if it's
- * ready to be processed. Afterwards, it holds the status if
- * it was processed successfully.
- */
- ret = isp_stat_buf_process(stat, ret);
-
- if (likely(!stat->sbl_ovl_recover)) {
- stat->ops->setup_regs(stat, stat->priv);
- } else {
- /*
- * Using recover config to increase the chance to have
- * a good buffer processing and make the H3A module to
- * go back to a valid state.
- */
- stat->update = 1;
- stat->ops->setup_regs(stat, stat->recover_priv);
- stat->sbl_ovl_recover = 0;
-
- /*
- * Set 'update' in case of the module needs to use
- * regular configuration after next buffer.
- */
- stat->update = 1;
- }
-
- isp_stat_buf_insert_magic(stat, stat->active_buf);
-
- /*
- * Hack: H3A modules may access invalid memory address or send
- * corrupted data to userspace if more than 1 SBL overflow
- * happens in a row without re-writing its buffer's start memory
- * address in the meantime. Such situation is avoided if the
- * module is not immediately re-enabled when the ISR misses the
- * timing to process the buffer and to setup the registers.
- * Because of that, pcr_enable(1) was moved to inside this 'if'
- * block. But the next interruption will still happen as during
- * pcr_enable(0) the module was busy.
- */
- isp_stat_pcr_enable(stat, 1);
- spin_unlock_irqrestore(&stat->isp->stat_lock, irqflags);
- } else {
- /*
- * If a SBL overflow occurs and the H3A driver misses the timing
- * to process the buffer, stat->buf_err is set and won't be
- * cleared now. So the next buffer will be correctly ignored.
- * It's necessary due to a hw issue which makes the next H3A
- * buffer to start from the memory address where the previous
- * one stopped, instead of start where it was configured to.
- * Do not "stat->buf_err = 0" here.
- */
-
- if (stat->ops->buf_process)
- /*
- * Driver may need to erase current data prior to
- * process a new buffer. If it misses the timing, the
- * next buffer might be wrong. So should be ignored.
- * It happens only for Histogram.
- */
- atomic_set(&stat->buf_err, 1);
-
- ret = STAT_NO_BUF;
- dev_dbg(stat->isp->dev, "%s: cannot process buffer, "
- "device is busy.\n", stat->subdev.name);
- }
-
-out:
- stat->buf_processing = 0;
- isp_stat_queue_event(stat, ret != STAT_BUF_DONE);
-}
-
-void omap3isp_stat_isr(struct ispstat *stat)
-{
- __stat_isr(stat, 0);
-}
-
-void omap3isp_stat_dma_isr(struct ispstat *stat)
-{
- __stat_isr(stat, 1);
-}
-
-int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
- struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- struct ispstat *stat = v4l2_get_subdevdata(subdev);
-
- if (sub->type != stat->event_type)
- return -EINVAL;
-
- return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL);
-}
-
-int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
- struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- return v4l2_event_unsubscribe(fh, sub);
-}
-
-void omap3isp_stat_unregister_entities(struct ispstat *stat)
-{
- v4l2_device_unregister_subdev(&stat->subdev);
-}
-
-int omap3isp_stat_register_entities(struct ispstat *stat,
- struct v4l2_device *vdev)
-{
- return v4l2_device_register_subdev(vdev, &stat->subdev);
-}
-
-static int isp_stat_init_entities(struct ispstat *stat, const char *name,
- const struct v4l2_subdev_ops *sd_ops)
-{
- struct v4l2_subdev *subdev = &stat->subdev;
- struct media_entity *me = &subdev->entity;
-
- v4l2_subdev_init(subdev, sd_ops);
- snprintf(subdev->name, V4L2_SUBDEV_NAME_SIZE, "OMAP3 ISP %s", name);
- subdev->grp_id = 1 << 16; /* group ID for isp subdevs */
- subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
- v4l2_set_subdevdata(subdev, stat);
-
- stat->pad.flags = MEDIA_PAD_FL_SINK;
- me->ops = NULL;
-
- return media_entity_init(me, 1, &stat->pad, 0);
-}
-
-int omap3isp_stat_init(struct ispstat *stat, const char *name,
- const struct v4l2_subdev_ops *sd_ops)
-{
- int ret;
-
- stat->buf = kcalloc(STAT_MAX_BUFS, sizeof(*stat->buf), GFP_KERNEL);
- if (!stat->buf)
- return -ENOMEM;
-
- isp_stat_buf_clear(stat);
- mutex_init(&stat->ioctl_lock);
- atomic_set(&stat->buf_err, 0);
-
- ret = isp_stat_init_entities(stat, name, sd_ops);
- if (ret < 0) {
- mutex_destroy(&stat->ioctl_lock);
- kfree(stat->buf);
- }
-
- return ret;
-}
-
-void omap3isp_stat_cleanup(struct ispstat *stat)
-{
- media_entity_cleanup(&stat->subdev.entity);
- mutex_destroy(&stat->ioctl_lock);
- isp_stat_bufs_free(stat);
- kfree(stat->buf);
-}
diff --git a/drivers/media/video/omap3isp/ispstat.h b/drivers/media/video/omap3isp/ispstat.h
deleted file mode 100644
index 9b7c8654dc8a..000000000000
--- a/drivers/media/video/omap3isp/ispstat.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * ispstat.h
- *
- * TI OMAP3 ISP - Statistics core
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2009 Texas Instruments, Inc
- *
- * Contacts: David Cohen <dacohen@gmail.com>
- * Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_STAT_H
-#define OMAP3_ISP_STAT_H
-
-#include <linux/types.h>
-#include <linux/omap3isp.h>
-#include <plat/dma.h>
-#include <media/v4l2-event.h>
-
-#include "isp.h"
-#include "ispvideo.h"
-
-#define STAT_MAX_BUFS 5
-#define STAT_NEVENTS 8
-
-#define STAT_BUF_DONE 0 /* Buffer is ready */
-#define STAT_NO_BUF 1 /* An error has occurred */
-#define STAT_BUF_WAITING_DMA 2 /* Histogram only: DMA is running */
-
-struct ispstat;
-
-struct ispstat_buffer {
- unsigned long iommu_addr;
- struct iovm_struct *iovm;
- void *virt_addr;
- dma_addr_t dma_addr;
- struct timeval ts;
- u32 buf_size;
- u32 frame_number;
- u16 config_counter;
- u8 empty;
-};
-
-struct ispstat_ops {
- /*
- * Validate new params configuration.
- * new_conf->buf_size value must be changed to the exact buffer size
- * necessary for the new configuration if it's smaller.
- */
- int (*validate_params)(struct ispstat *stat, void *new_conf);
-
- /*
- * Save new params configuration.
- * stat->priv->buf_size value must be set to the exact buffer size for
- * the new configuration.
- * stat->update is set to 1 if new configuration is different than
- * current one.
- */
- void (*set_params)(struct ispstat *stat, void *new_conf);
-
- /* Apply stored configuration. */
- void (*setup_regs)(struct ispstat *stat, void *priv);
-
- /* Enable/Disable module. */
- void (*enable)(struct ispstat *stat, int enable);
-
- /* Verify is module is busy. */
- int (*busy)(struct ispstat *stat);
-
- /* Used for specific operations during generic buf process task. */
- int (*buf_process)(struct ispstat *stat);
-};
-
-enum ispstat_state_t {
- ISPSTAT_DISABLED = 0,
- ISPSTAT_DISABLING,
- ISPSTAT_ENABLED,
- ISPSTAT_ENABLING,
- ISPSTAT_SUSPENDED,
-};
-
-struct ispstat {
- struct v4l2_subdev subdev;
- struct media_pad pad; /* sink pad */
-
- /* Control */
- unsigned configured:1;
- unsigned update:1;
- unsigned buf_processing:1;
- unsigned sbl_ovl_recover:1;
- u8 inc_config;
- atomic_t buf_err;
- enum ispstat_state_t state; /* enabling/disabling state */
- struct omap_dma_channel_params dma_config;
- struct isp_device *isp;
- void *priv; /* pointer to priv config struct */
- void *recover_priv; /* pointer to recover priv configuration */
- struct mutex ioctl_lock; /* serialize private ioctl */
-
- const struct ispstat_ops *ops;
-
- /* Buffer */
- u8 wait_acc_frames;
- u16 config_counter;
- u32 frame_number;
- u32 buf_size;
- u32 buf_alloc_size;
- int dma_ch;
- unsigned long event_type;
- struct ispstat_buffer *buf;
- struct ispstat_buffer *active_buf;
- struct ispstat_buffer *locked_buf;
-};
-
-struct ispstat_generic_config {
- /*
- * Fields must be in the same order as in:
- * - omap3isp_h3a_aewb_config
- * - omap3isp_h3a_af_config
- * - omap3isp_hist_config
- */
- u32 buf_size;
- u16 config_counter;
-};
-
-int omap3isp_stat_config(struct ispstat *stat, void *new_conf);
-int omap3isp_stat_request_statistics(struct ispstat *stat,
- struct omap3isp_stat_data *data);
-int omap3isp_stat_init(struct ispstat *stat, const char *name,
- const struct v4l2_subdev_ops *sd_ops);
-void omap3isp_stat_cleanup(struct ispstat *stat);
-int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
- struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
-int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
- struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
-int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable);
-
-int omap3isp_stat_busy(struct ispstat *stat);
-int omap3isp_stat_pcr_busy(struct ispstat *stat);
-void omap3isp_stat_suspend(struct ispstat *stat);
-void omap3isp_stat_resume(struct ispstat *stat);
-int omap3isp_stat_enable(struct ispstat *stat, u8 enable);
-void omap3isp_stat_sbl_overflow(struct ispstat *stat);
-void omap3isp_stat_isr(struct ispstat *stat);
-void omap3isp_stat_isr_frame_sync(struct ispstat *stat);
-void omap3isp_stat_dma_isr(struct ispstat *stat);
-int omap3isp_stat_register_entities(struct ispstat *stat,
- struct v4l2_device *vdev);
-void omap3isp_stat_unregister_entities(struct ispstat *stat);
-
-#endif /* OMAP3_ISP_STAT_H */
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
deleted file mode 100644
index b37379d39cdd..000000000000
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ /dev/null
@@ -1,1392 +0,0 @@
-/*
- * ispvideo.c
- *
- * TI OMAP3 ISP - Generic video node
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <asm/cacheflush.h>
-#include <linux/clk.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
-#include <plat/iommu.h>
-#include <plat/iovmm.h>
-#include <plat/omap-pm.h>
-
-#include "ispvideo.h"
-#include "isp.h"
-
-
-/* -----------------------------------------------------------------------------
- * Helper functions
- */
-
-/*
- * NOTE: When adding new media bus codes, always remember to add
- * corresponding in-memory formats to the table below!!!
- */
-static struct isp_format_info formats[] = {
- { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_PIX_FMT_GREY, 8, },
- { V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_PIX_FMT_Y10, 10, },
- { V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y10_1X10,
- V4L2_MBUS_FMT_Y12_1X12, V4L2_MBUS_FMT_Y8_1X8,
- V4L2_PIX_FMT_Y12, 12, },
- { V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_PIX_FMT_SBGGR8, 8, },
- { V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_MBUS_FMT_SGBRG8_1X8, V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_PIX_FMT_SGBRG8, 8, },
- { V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_MBUS_FMT_SGRBG8_1X8, V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_PIX_FMT_SGRBG8, 8, },
- { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_PIX_FMT_SRGGB8, 8, },
- { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
- V4L2_MBUS_FMT_SBGGR10_1X10, 0,
- V4L2_PIX_FMT_SBGGR10DPCM8, 8, },
- { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGBRG10_1X10, 0,
- V4L2_PIX_FMT_SGBRG10DPCM8, 8, },
- { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
- V4L2_MBUS_FMT_SGRBG10_1X10, 0,
- V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
- { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
- V4L2_MBUS_FMT_SRGGB10_1X10, 0,
- V4L2_PIX_FMT_SRGGB10DPCM8, 8, },
- { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_PIX_FMT_SBGGR10, 10, },
- { V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG10_1X10, V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_PIX_FMT_SGBRG10, 10, },
- { V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_PIX_FMT_SGRBG10, 10, },
- { V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_PIX_FMT_SRGGB10, 10, },
- { V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR10_1X10,
- V4L2_MBUS_FMT_SBGGR12_1X12, V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_PIX_FMT_SBGGR12, 12, },
- { V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG10_1X10,
- V4L2_MBUS_FMT_SGBRG12_1X12, V4L2_MBUS_FMT_SGBRG8_1X8,
- V4L2_PIX_FMT_SGBRG12, 12, },
- { V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG10_1X10,
- V4L2_MBUS_FMT_SGRBG12_1X12, V4L2_MBUS_FMT_SGRBG8_1X8,
- V4L2_PIX_FMT_SGRBG12, 12, },
- { V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB10_1X10,
- V4L2_MBUS_FMT_SRGGB12_1X12, V4L2_MBUS_FMT_SRGGB8_1X8,
- V4L2_PIX_FMT_SRGGB12, 12, },
- { V4L2_MBUS_FMT_UYVY8_1X16, V4L2_MBUS_FMT_UYVY8_1X16,
- V4L2_MBUS_FMT_UYVY8_1X16, 0,
- V4L2_PIX_FMT_UYVY, 16, },
- { V4L2_MBUS_FMT_YUYV8_1X16, V4L2_MBUS_FMT_YUYV8_1X16,
- V4L2_MBUS_FMT_YUYV8_1X16, 0,
- V4L2_PIX_FMT_YUYV, 16, },
-};
-
-const struct isp_format_info *
-omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (formats[i].code == code)
- return &formats[i];
- }
-
- return NULL;
-}
-
-/*
- * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
- * @video: ISP video instance
- * @mbus: v4l2_mbus_framefmt format (input)
- * @pix: v4l2_pix_format format (output)
- *
- * Fill the output pix structure with information from the input mbus format.
- * The bytesperline and sizeimage fields are computed from the requested bytes
- * per line value in the pix format and information from the video instance.
- *
- * Return the number of padding bytes at end of line.
- */
-static unsigned int isp_video_mbus_to_pix(const struct isp_video *video,
- const struct v4l2_mbus_framefmt *mbus,
- struct v4l2_pix_format *pix)
-{
- unsigned int bpl = pix->bytesperline;
- unsigned int min_bpl;
- unsigned int i;
-
- memset(pix, 0, sizeof(*pix));
- pix->width = mbus->width;
- pix->height = mbus->height;
-
- for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (formats[i].code == mbus->code)
- break;
- }
-
- if (WARN_ON(i == ARRAY_SIZE(formats)))
- return 0;
-
- min_bpl = pix->width * ALIGN(formats[i].bpp, 8) / 8;
-
- /* Clamp the requested bytes per line value. If the maximum bytes per
- * line value is zero, the module doesn't support user configurable line
- * sizes. Override the requested value with the minimum in that case.
- */
- if (video->bpl_max)
- bpl = clamp(bpl, min_bpl, video->bpl_max);
- else
- bpl = min_bpl;
-
- if (!video->bpl_zero_padding || bpl != min_bpl)
- bpl = ALIGN(bpl, video->bpl_alignment);
-
- pix->pixelformat = formats[i].pixelformat;
- pix->bytesperline = bpl;
- pix->sizeimage = pix->bytesperline * pix->height;
- pix->colorspace = mbus->colorspace;
- pix->field = mbus->field;
-
- return bpl - min_bpl;
-}
-
-static void isp_video_pix_to_mbus(const struct v4l2_pix_format *pix,
- struct v4l2_mbus_framefmt *mbus)
-{
- unsigned int i;
-
- memset(mbus, 0, sizeof(*mbus));
- mbus->width = pix->width;
- mbus->height = pix->height;
-
- /* Skip the last format in the loop so that it will be selected if no
- * match is found.
- */
- for (i = 0; i < ARRAY_SIZE(formats) - 1; ++i) {
- if (formats[i].pixelformat == pix->pixelformat)
- break;
- }
-
- mbus->code = formats[i].code;
- mbus->colorspace = pix->colorspace;
- mbus->field = pix->field;
-}
-
-static struct v4l2_subdev *
-isp_video_remote_subdev(struct isp_video *video, u32 *pad)
-{
- struct media_pad *remote;
-
- remote = media_entity_remote_source(&video->pad);
-
- if (remote == NULL ||
- media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- return NULL;
-
- if (pad)
- *pad = remote->index;
-
- return media_entity_to_v4l2_subdev(remote->entity);
-}
-
-/* Return a pointer to the ISP video instance at the far end of the pipeline. */
-static int isp_video_get_graph_data(struct isp_video *video,
- struct isp_pipeline *pipe)
-{
- struct media_entity_graph graph;
- struct media_entity *entity = &video->video.entity;
- struct media_device *mdev = entity->parent;
- struct isp_video *far_end = NULL;
-
- mutex_lock(&mdev->graph_mutex);
- media_entity_graph_walk_start(&graph, entity);
-
- while ((entity = media_entity_graph_walk_next(&graph))) {
- struct isp_video *__video;
-
- pipe->entities |= 1 << entity->id;
-
- if (far_end != NULL)
- continue;
-
- if (entity == &video->video.entity)
- continue;
-
- if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
- continue;
-
- __video = to_isp_video(media_entity_to_video_device(entity));
- if (__video->type != video->type)
- far_end = __video;
- }
-
- mutex_unlock(&mdev->graph_mutex);
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- pipe->input = far_end;
- pipe->output = video;
- } else {
- if (far_end == NULL)
- return -EPIPE;
-
- pipe->input = video;
- pipe->output = far_end;
- }
-
- return 0;
-}
-
-/*
- * Validate a pipeline by checking both ends of all links for format
- * discrepancies.
- *
- * Compute the minimum time per frame value as the maximum of time per frame
- * limits reported by every block in the pipeline.
- *
- * Return 0 if all formats match, or -EPIPE if at least one link is found with
- * different formats on its two ends or if the pipeline doesn't start with a
- * video source (either a subdev with no input pad, or a non-subdev entity).
- */
-static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
-{
- struct isp_device *isp = pipe->output->isp;
- struct media_pad *pad;
- struct v4l2_subdev *subdev;
-
- subdev = isp_video_remote_subdev(pipe->output, NULL);
- if (subdev == NULL)
- return -EPIPE;
-
- while (1) {
- /* Retrieve the sink format */
- pad = &subdev->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- /* Update the maximum frame rate */
- if (subdev == &isp->isp_res.subdev)
- omap3isp_resizer_max_rate(&isp->isp_res,
- &pipe->max_rate);
-
- /* Retrieve the source format. Return an error if no source
- * entity can be found, and stop checking the pipeline if the
- * source entity isn't a subdev.
- */
- pad = media_entity_remote_source(pad);
- if (pad == NULL)
- return -EPIPE;
-
- if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- subdev = media_entity_to_v4l2_subdev(pad->entity);
- }
-
- return 0;
-}
-
-static int
-__isp_video_get_format(struct isp_video *video, struct v4l2_format *format)
-{
- struct v4l2_subdev_format fmt;
- struct v4l2_subdev *subdev;
- u32 pad;
- int ret;
-
- subdev = isp_video_remote_subdev(video, &pad);
- if (subdev == NULL)
- return -EINVAL;
-
- mutex_lock(&video->mutex);
-
- fmt.pad = pad;
- fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
- if (ret == -ENOIOCTLCMD)
- ret = -EINVAL;
-
- mutex_unlock(&video->mutex);
-
- if (ret)
- return ret;
-
- format->type = video->type;
- return isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
-}
-
-static int
-isp_video_check_format(struct isp_video *video, struct isp_video_fh *vfh)
-{
- struct v4l2_format format;
- int ret;
-
- memcpy(&format, &vfh->format, sizeof(format));
- ret = __isp_video_get_format(video, &format);
- if (ret < 0)
- return ret;
-
- if (vfh->format.fmt.pix.pixelformat != format.fmt.pix.pixelformat ||
- vfh->format.fmt.pix.height != format.fmt.pix.height ||
- vfh->format.fmt.pix.width != format.fmt.pix.width ||
- vfh->format.fmt.pix.bytesperline != format.fmt.pix.bytesperline ||
- vfh->format.fmt.pix.sizeimage != format.fmt.pix.sizeimage)
- return -EINVAL;
-
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * IOMMU management
- */
-
-#define IOMMU_FLAG (IOVMF_ENDIAN_LITTLE | IOVMF_ELSZ_8)
-
-/*
- * ispmmu_vmap - Wrapper for Virtual memory mapping of a scatter gather list
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @sglist: Pointer to source Scatter gather list to allocate.
- * @sglen: Number of elements of the scatter-gatter list.
- *
- * Returns a resulting mapped device address by the ISP MMU, or -ENOMEM if
- * we ran out of memory.
- */
-static dma_addr_t
-ispmmu_vmap(struct isp_device *isp, const struct scatterlist *sglist, int sglen)
-{
- struct sg_table *sgt;
- u32 da;
-
- sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
- if (sgt == NULL)
- return -ENOMEM;
-
- sgt->sgl = (struct scatterlist *)sglist;
- sgt->nents = sglen;
- sgt->orig_nents = sglen;
-
- da = omap_iommu_vmap(isp->domain, isp->dev, 0, sgt, IOMMU_FLAG);
- if (IS_ERR_VALUE(da))
- kfree(sgt);
-
- return da;
-}
-
-/*
- * ispmmu_vunmap - Unmap a device address from the ISP MMU
- * @dev: Device pointer specific to the OMAP3 ISP.
- * @da: Device address generated from a ispmmu_vmap call.
- */
-static void ispmmu_vunmap(struct isp_device *isp, dma_addr_t da)
-{
- struct sg_table *sgt;
-
- sgt = omap_iommu_vunmap(isp->domain, isp->dev, (u32)da);
- kfree(sgt);
-}
-
-/* -----------------------------------------------------------------------------
- * Video queue operations
- */
-
-static void isp_video_queue_prepare(struct isp_video_queue *queue,
- unsigned int *nbuffers, unsigned int *size)
-{
- struct isp_video_fh *vfh =
- container_of(queue, struct isp_video_fh, queue);
- struct isp_video *video = vfh->video;
-
- *size = vfh->format.fmt.pix.sizeimage;
- if (*size == 0)
- return;
-
- *nbuffers = min(*nbuffers, video->capture_mem / PAGE_ALIGN(*size));
-}
-
-static void isp_video_buffer_cleanup(struct isp_video_buffer *buf)
-{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
- struct isp_buffer *buffer = to_isp_buffer(buf);
- struct isp_video *video = vfh->video;
-
- if (buffer->isp_addr) {
- ispmmu_vunmap(video->isp, buffer->isp_addr);
- buffer->isp_addr = 0;
- }
-}
-
-static int isp_video_buffer_prepare(struct isp_video_buffer *buf)
-{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
- struct isp_buffer *buffer = to_isp_buffer(buf);
- struct isp_video *video = vfh->video;
- unsigned long addr;
-
- addr = ispmmu_vmap(video->isp, buf->sglist, buf->sglen);
- if (IS_ERR_VALUE(addr))
- return -EIO;
-
- if (!IS_ALIGNED(addr, 32)) {
- dev_dbg(video->isp->dev, "Buffer address must be "
- "aligned to 32 bytes boundary.\n");
- ispmmu_vunmap(video->isp, buffer->isp_addr);
- return -EINVAL;
- }
-
- buf->vbuf.bytesused = vfh->format.fmt.pix.sizeimage;
- buffer->isp_addr = addr;
- return 0;
-}
-
-/*
- * isp_video_buffer_queue - Add buffer to streaming queue
- * @buf: Video buffer
- *
- * In memory-to-memory mode, start streaming on the pipeline if buffers are
- * queued on both the input and the output, if the pipeline isn't already busy.
- * If the pipeline is busy, it will be restarted in the output module interrupt
- * handler.
- */
-static void isp_video_buffer_queue(struct isp_video_buffer *buf)
-{
- struct isp_video_fh *vfh = isp_video_queue_to_isp_video_fh(buf->queue);
- struct isp_buffer *buffer = to_isp_buffer(buf);
- struct isp_video *video = vfh->video;
- struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
- enum isp_pipeline_state state;
- unsigned long flags;
- unsigned int empty;
- unsigned int start;
-
- empty = list_empty(&video->dmaqueue);
- list_add_tail(&buffer->buffer.irqlist, &video->dmaqueue);
-
- if (empty) {
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- state = ISP_PIPELINE_QUEUE_OUTPUT;
- else
- state = ISP_PIPELINE_QUEUE_INPUT;
-
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state |= state;
- video->ops->queue(video, buffer);
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
-
- start = isp_pipeline_ready(pipe);
- if (start)
- pipe->state |= ISP_PIPELINE_STREAM;
- spin_unlock_irqrestore(&pipe->lock, flags);
-
- if (start)
- omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_SINGLESHOT);
- }
-}
-
-static const struct isp_video_queue_operations isp_video_queue_ops = {
- .queue_prepare = &isp_video_queue_prepare,
- .buffer_prepare = &isp_video_buffer_prepare,
- .buffer_queue = &isp_video_buffer_queue,
- .buffer_cleanup = &isp_video_buffer_cleanup,
-};
-
-/*
- * omap3isp_video_buffer_next - Complete the current buffer and return the next
- * @video: ISP video object
- *
- * Remove the current video buffer from the DMA queue and fill its timestamp,
- * field count and state fields before waking up its completion handler.
- *
- * For capture video nodes the buffer state is set to ISP_BUF_STATE_DONE if no
- * error has been flagged in the pipeline, or to ISP_BUF_STATE_ERROR otherwise.
- * For video output nodes the buffer state is always set to ISP_BUF_STATE_DONE.
- *
- * The DMA queue is expected to contain at least one buffer.
- *
- * Return a pointer to the next buffer in the DMA queue, or NULL if the queue is
- * empty.
- */
-struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video)
-{
- struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
- struct isp_video_queue *queue = video->queue;
- enum isp_pipeline_state state;
- struct isp_video_buffer *buf;
- unsigned long flags;
- struct timespec ts;
-
- spin_lock_irqsave(&queue->irqlock, flags);
- if (WARN_ON(list_empty(&video->dmaqueue))) {
- spin_unlock_irqrestore(&queue->irqlock, flags);
- return NULL;
- }
-
- buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
- irqlist);
- list_del(&buf->irqlist);
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
- ktime_get_ts(&ts);
- buf->vbuf.timestamp.tv_sec = ts.tv_sec;
- buf->vbuf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
-
- /* Do frame number propagation only if this is the output video node.
- * Frame number either comes from the CSI receivers or it gets
- * incremented here if H3A is not active.
- * Note: There is no guarantee that the output buffer will finish
- * first, so the input number might lag behind by 1 in some cases.
- */
- if (video == pipe->output && !pipe->do_propagation)
- buf->vbuf.sequence = atomic_inc_return(&pipe->frame_number);
- else
- buf->vbuf.sequence = atomic_read(&pipe->frame_number);
-
- /* Report pipeline errors to userspace on the capture device side. */
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->error) {
- buf->state = ISP_BUF_STATE_ERROR;
- pipe->error = false;
- } else {
- buf->state = ISP_BUF_STATE_DONE;
- }
-
- wake_up(&buf->wait);
-
- if (list_empty(&video->dmaqueue)) {
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- state = ISP_PIPELINE_QUEUE_OUTPUT
- | ISP_PIPELINE_STREAM;
- else
- state = ISP_PIPELINE_QUEUE_INPUT
- | ISP_PIPELINE_STREAM;
-
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state &= ~state;
- if (video->pipe.stream_state == ISP_PIPELINE_STREAM_CONTINUOUS)
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
- spin_unlock_irqrestore(&pipe->lock, flags);
- return NULL;
- }
-
- if (queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && pipe->input != NULL) {
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state &= ~ISP_PIPELINE_STREAM;
- spin_unlock_irqrestore(&pipe->lock, flags);
- }
-
- buf = list_first_entry(&video->dmaqueue, struct isp_video_buffer,
- irqlist);
- buf->state = ISP_BUF_STATE_ACTIVE;
- return to_isp_buffer(buf);
-}
-
-/*
- * omap3isp_video_resume - Perform resume operation on the buffers
- * @video: ISP video object
- * @continuous: Pipeline is in single shot mode if 0 or continuous mode otherwise
- *
- * This function is intended to be used on suspend/resume scenario. It
- * requests video queue layer to discard buffers marked as DONE if it's in
- * continuous mode and requests ISP modules to queue again the ACTIVE buffer
- * if there's any.
- */
-void omap3isp_video_resume(struct isp_video *video, int continuous)
-{
- struct isp_buffer *buf = NULL;
-
- if (continuous && video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- omap3isp_video_queue_discard_done(video->queue);
-
- if (!list_empty(&video->dmaqueue)) {
- buf = list_first_entry(&video->dmaqueue,
- struct isp_buffer, buffer.irqlist);
- video->ops->queue(video, buf);
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_QUEUED;
- } else {
- if (continuous)
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
- }
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 ioctls
- */
-
-static int
-isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
-{
- struct isp_video *video = video_drvdata(file);
-
- strlcpy(cap->driver, ISP_VIDEO_DRIVER_NAME, sizeof(cap->driver));
- strlcpy(cap->card, video->video.name, sizeof(cap->card));
- strlcpy(cap->bus_info, "media", sizeof(cap->bus_info));
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- else
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
-
- return 0;
-}
-
-static int
-isp_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
-
- if (format->type != video->type)
- return -EINVAL;
-
- mutex_lock(&video->mutex);
- *format = vfh->format;
- mutex_unlock(&video->mutex);
-
- return 0;
-}
-
-static int
-isp_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
- struct v4l2_mbus_framefmt fmt;
-
- if (format->type != video->type)
- return -EINVAL;
-
- mutex_lock(&video->mutex);
-
- /* Fill the bytesperline and sizeimage fields by converting to media bus
- * format and back to pixel format.
- */
- isp_video_pix_to_mbus(&format->fmt.pix, &fmt);
- isp_video_mbus_to_pix(video, &fmt, &format->fmt.pix);
-
- vfh->format = *format;
-
- mutex_unlock(&video->mutex);
- return 0;
-}
-
-static int
-isp_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_subdev_format fmt;
- struct v4l2_subdev *subdev;
- u32 pad;
- int ret;
-
- if (format->type != video->type)
- return -EINVAL;
-
- subdev = isp_video_remote_subdev(video, &pad);
- if (subdev == NULL)
- return -EINVAL;
-
- isp_video_pix_to_mbus(&format->fmt.pix, &fmt.format);
-
- fmt.pad = pad;
- fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
- if (ret)
- return ret == -ENOIOCTLCMD ? -EINVAL : ret;
-
- isp_video_mbus_to_pix(video, &fmt.format, &format->fmt.pix);
- return 0;
-}
-
-static int
-isp_video_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_subdev *subdev;
- int ret;
-
- subdev = isp_video_remote_subdev(video, NULL);
- if (subdev == NULL)
- return -EINVAL;
-
- mutex_lock(&video->mutex);
- ret = v4l2_subdev_call(subdev, video, cropcap, cropcap);
- mutex_unlock(&video->mutex);
-
- return ret == -ENOIOCTLCMD ? -EINVAL : ret;
-}
-
-static int
-isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_subdev_format format;
- struct v4l2_subdev *subdev;
- u32 pad;
- int ret;
-
- subdev = isp_video_remote_subdev(video, &pad);
- if (subdev == NULL)
- return -EINVAL;
-
- /* Try the get crop operation first and fallback to get format if not
- * implemented.
- */
- ret = v4l2_subdev_call(subdev, video, g_crop, crop);
- if (ret != -ENOIOCTLCMD)
- return ret;
-
- format.pad = pad;
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
- if (ret < 0)
- return ret == -ENOIOCTLCMD ? -EINVAL : ret;
-
- crop->c.left = 0;
- crop->c.top = 0;
- crop->c.width = format.format.width;
- crop->c.height = format.format.height;
-
- return 0;
-}
-
-static int
-isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_subdev *subdev;
- int ret;
-
- subdev = isp_video_remote_subdev(video, NULL);
- if (subdev == NULL)
- return -EINVAL;
-
- mutex_lock(&video->mutex);
- ret = v4l2_subdev_call(subdev, video, s_crop, crop);
- mutex_unlock(&video->mutex);
-
- return ret == -ENOIOCTLCMD ? -EINVAL : ret;
-}
-
-static int
-isp_video_get_param(struct file *file, void *fh, struct v4l2_streamparm *a)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
-
- if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- video->type != a->type)
- return -EINVAL;
-
- memset(a, 0, sizeof(*a));
- a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
- a->parm.output.timeperframe = vfh->timeperframe;
-
- return 0;
-}
-
-static int
-isp_video_set_param(struct file *file, void *fh, struct v4l2_streamparm *a)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
-
- if (video->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- video->type != a->type)
- return -EINVAL;
-
- if (a->parm.output.timeperframe.denominator == 0)
- a->parm.output.timeperframe.denominator = 1;
-
- vfh->timeperframe = a->parm.output.timeperframe;
-
- return 0;
-}
-
-static int
-isp_video_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
-
- return omap3isp_video_queue_reqbufs(&vfh->queue, rb);
-}
-
-static int
-isp_video_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
-
- return omap3isp_video_queue_querybuf(&vfh->queue, b);
-}
-
-static int
-isp_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
-
- return omap3isp_video_queue_qbuf(&vfh->queue, b);
-}
-
-static int
-isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
-
- return omap3isp_video_queue_dqbuf(&vfh->queue, b,
- file->f_flags & O_NONBLOCK);
-}
-
-static int isp_video_check_external_subdevs(struct isp_video *video,
- struct isp_pipeline *pipe)
-{
- struct isp_device *isp = video->isp;
- struct media_entity *ents[] = {
- &isp->isp_csi2a.subdev.entity,
- &isp->isp_csi2c.subdev.entity,
- &isp->isp_ccp2.subdev.entity,
- &isp->isp_ccdc.subdev.entity
- };
- struct media_pad *source_pad;
- struct media_entity *source = NULL;
- struct media_entity *sink;
- struct v4l2_subdev_format fmt;
- struct v4l2_ext_controls ctrls;
- struct v4l2_ext_control ctrl;
- unsigned int i;
- int ret = 0;
-
- for (i = 0; i < ARRAY_SIZE(ents); i++) {
- /* Is the entity part of the pipeline? */
- if (!(pipe->entities & (1 << ents[i]->id)))
- continue;
-
- /* ISP entities have always sink pad == 0. Find source. */
- source_pad = media_entity_remote_source(&ents[i]->pads[0]);
- if (source_pad == NULL)
- continue;
-
- source = source_pad->entity;
- sink = ents[i];
- break;
- }
-
- if (!source) {
- dev_warn(isp->dev, "can't find source, failing now\n");
- return ret;
- }
-
- if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
- return 0;
-
- pipe->external = media_entity_to_v4l2_subdev(source);
-
- fmt.pad = source_pad->index;
- fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink),
- pad, get_fmt, NULL, &fmt);
- if (unlikely(ret < 0)) {
- dev_warn(isp->dev, "get_fmt returned null!\n");
- return ret;
- }
-
- pipe->external_bpp = omap3isp_video_format_info(fmt.format.code)->bpp;
-
- memset(&ctrls, 0, sizeof(ctrls));
- memset(&ctrl, 0, sizeof(ctrl));
-
- ctrl.id = V4L2_CID_PIXEL_RATE;
-
- ctrls.count = 1;
- ctrls.controls = &ctrl;
-
- ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
- if (ret < 0) {
- dev_warn(isp->dev, "no pixel rate control in subdev %s\n",
- pipe->external->name);
- return ret;
- }
-
- pipe->external_rate = ctrl.value64;
-
- if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) {
- unsigned int rate = UINT_MAX;
- /*
- * Check that maximum allowed CCDC pixel rate isn't
- * exceeded by the pixel rate.
- */
- omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate);
- if (pipe->external_rate > rate)
- return -ENOSPC;
- }
-
- return 0;
-}
-
-/*
- * Stream management
- *
- * Every ISP pipeline has a single input and a single output. The input can be
- * either a sensor or a video node. The output is always a video node.
- *
- * As every pipeline has an output video node, the ISP video objects at the
- * pipeline output stores the pipeline state. It tracks the streaming state of
- * both the input and output, as well as the availability of buffers.
- *
- * In sensor-to-memory mode, frames are always available at the pipeline input.
- * Starting the sensor usually requires I2C transfers and must be done in
- * interruptible context. The pipeline is started and stopped synchronously
- * to the stream on/off commands. All modules in the pipeline will get their
- * subdev set stream handler called. The module at the end of the pipeline must
- * delay starting the hardware until buffers are available at its output.
- *
- * In memory-to-memory mode, starting/stopping the stream requires
- * synchronization between the input and output. ISP modules can't be stopped
- * in the middle of a frame, and at least some of the modules seem to become
- * busy as soon as they're started, even if they don't receive a frame start
- * event. For that reason frames need to be processed in single-shot mode. The
- * driver needs to wait until a frame is completely processed and written to
- * memory before restarting the pipeline for the next frame. Pipelined
- * processing might be possible but requires more testing.
- *
- * Stream start must be delayed until buffers are available at both the input
- * and output. The pipeline must be started in the videobuf queue callback with
- * the buffers queue spinlock held. The modules subdev set stream operation must
- * not sleep.
- */
-static int
-isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
- enum isp_pipeline_state state;
- struct isp_pipeline *pipe;
- unsigned long flags;
- int ret;
-
- if (type != video->type)
- return -EINVAL;
-
- mutex_lock(&video->stream_lock);
-
- if (video->streaming) {
- mutex_unlock(&video->stream_lock);
- return -EBUSY;
- }
-
- /* Start streaming on the pipeline. No link touching an entity in the
- * pipeline can be activated or deactivated once streaming is started.
- */
- pipe = video->video.entity.pipe
- ? to_isp_pipeline(&video->video.entity) : &video->pipe;
-
- pipe->entities = 0;
-
- if (video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, true);
- pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
- pipe->max_rate = pipe->l3_ick;
-
- ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
- if (ret < 0)
- goto err_pipeline_start;
-
- /* Verify that the currently configured format matches the output of
- * the connected subdev.
- */
- ret = isp_video_check_format(video, vfh);
- if (ret < 0)
- goto err_check_format;
-
- video->bpl_padding = ret;
- video->bpl_value = vfh->format.fmt.pix.bytesperline;
-
- ret = isp_video_get_graph_data(video, pipe);
- if (ret < 0)
- goto err_check_format;
-
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT;
- else
- state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT;
-
- ret = isp_video_check_external_subdevs(video, pipe);
- if (ret < 0)
- goto err_check_format;
-
- /* Validate the pipeline and update its state. */
- ret = isp_video_validate_pipeline(pipe);
- if (ret < 0)
- goto err_check_format;
-
- pipe->error = false;
-
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state &= ~ISP_PIPELINE_STREAM;
- pipe->state |= state;
- spin_unlock_irqrestore(&pipe->lock, flags);
-
- /* Set the maximum time per frame as the value requested by userspace.
- * This is a soft limit that can be overridden if the hardware doesn't
- * support the request limit.
- */
- if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- pipe->max_timeperframe = vfh->timeperframe;
-
- video->queue = &vfh->queue;
- INIT_LIST_HEAD(&video->dmaqueue);
- atomic_set(&pipe->frame_number, -1);
-
- ret = omap3isp_video_queue_streamon(&vfh->queue);
- if (ret < 0)
- goto err_check_format;
-
- /* In sensor-to-memory mode, the stream can be started synchronously
- * to the stream on command. In memory-to-memory mode, it will be
- * started when buffers are queued on both the input and output.
- */
- if (pipe->input == NULL) {
- ret = omap3isp_pipeline_set_stream(pipe,
- ISP_PIPELINE_STREAM_CONTINUOUS);
- if (ret < 0)
- goto err_set_stream;
- spin_lock_irqsave(&video->queue->irqlock, flags);
- if (list_empty(&video->dmaqueue))
- video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
- spin_unlock_irqrestore(&video->queue->irqlock, flags);
- }
-
- video->streaming = 1;
-
- mutex_unlock(&video->stream_lock);
- return 0;
-
-err_set_stream:
- omap3isp_video_queue_streamoff(&vfh->queue);
-err_check_format:
- media_entity_pipeline_stop(&video->video.entity);
-err_pipeline_start:
- if (video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, false);
- /* The DMA queue must be emptied here, otherwise CCDC interrupts that
- * will get triggered the next time the CCDC is powered up will try to
- * access buffers that might have been freed but still present in the
- * DMA queue. This can easily get triggered if the above
- * omap3isp_pipeline_set_stream() call fails on a system with a
- * free-running sensor.
- */
- INIT_LIST_HEAD(&video->dmaqueue);
- video->queue = NULL;
-
- mutex_unlock(&video->stream_lock);
- return ret;
-}
-
-static int
-isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(fh);
- struct isp_video *video = video_drvdata(file);
- struct isp_pipeline *pipe = to_isp_pipeline(&video->video.entity);
- enum isp_pipeline_state state;
- unsigned int streaming;
- unsigned long flags;
-
- if (type != video->type)
- return -EINVAL;
-
- mutex_lock(&video->stream_lock);
-
- /* Make sure we're not streaming yet. */
- mutex_lock(&vfh->queue.lock);
- streaming = vfh->queue.streaming;
- mutex_unlock(&vfh->queue.lock);
-
- if (!streaming)
- goto done;
-
- /* Update the pipeline state. */
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- state = ISP_PIPELINE_STREAM_OUTPUT
- | ISP_PIPELINE_QUEUE_OUTPUT;
- else
- state = ISP_PIPELINE_STREAM_INPUT
- | ISP_PIPELINE_QUEUE_INPUT;
-
- spin_lock_irqsave(&pipe->lock, flags);
- pipe->state &= ~state;
- spin_unlock_irqrestore(&pipe->lock, flags);
-
- /* Stop the stream. */
- omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_STOPPED);
- omap3isp_video_queue_streamoff(&vfh->queue);
- video->queue = NULL;
- video->streaming = 0;
-
- if (video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, false);
- media_entity_pipeline_stop(&video->video.entity);
-
-done:
- mutex_unlock(&video->stream_lock);
- return 0;
-}
-
-static int
-isp_video_enum_input(struct file *file, void *fh, struct v4l2_input *input)
-{
- if (input->index > 0)
- return -EINVAL;
-
- strlcpy(input->name, "camera", sizeof(input->name));
- input->type = V4L2_INPUT_TYPE_CAMERA;
-
- return 0;
-}
-
-static int
-isp_video_g_input(struct file *file, void *fh, unsigned int *input)
-{
- *input = 0;
-
- return 0;
-}
-
-static int
-isp_video_s_input(struct file *file, void *fh, unsigned int input)
-{
- return input == 0 ? 0 : -EINVAL;
-}
-
-static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
- .vidioc_querycap = isp_video_querycap,
- .vidioc_g_fmt_vid_cap = isp_video_get_format,
- .vidioc_s_fmt_vid_cap = isp_video_set_format,
- .vidioc_try_fmt_vid_cap = isp_video_try_format,
- .vidioc_g_fmt_vid_out = isp_video_get_format,
- .vidioc_s_fmt_vid_out = isp_video_set_format,
- .vidioc_try_fmt_vid_out = isp_video_try_format,
- .vidioc_cropcap = isp_video_cropcap,
- .vidioc_g_crop = isp_video_get_crop,
- .vidioc_s_crop = isp_video_set_crop,
- .vidioc_g_parm = isp_video_get_param,
- .vidioc_s_parm = isp_video_set_param,
- .vidioc_reqbufs = isp_video_reqbufs,
- .vidioc_querybuf = isp_video_querybuf,
- .vidioc_qbuf = isp_video_qbuf,
- .vidioc_dqbuf = isp_video_dqbuf,
- .vidioc_streamon = isp_video_streamon,
- .vidioc_streamoff = isp_video_streamoff,
- .vidioc_enum_input = isp_video_enum_input,
- .vidioc_g_input = isp_video_g_input,
- .vidioc_s_input = isp_video_s_input,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 file operations
- */
-
-static int isp_video_open(struct file *file)
-{
- struct isp_video *video = video_drvdata(file);
- struct isp_video_fh *handle;
- int ret = 0;
-
- handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (handle == NULL)
- return -ENOMEM;
-
- v4l2_fh_init(&handle->vfh, &video->video);
- v4l2_fh_add(&handle->vfh);
-
- /* If this is the first user, initialise the pipeline. */
- if (omap3isp_get(video->isp) == NULL) {
- ret = -EBUSY;
- goto done;
- }
-
- ret = omap3isp_pipeline_pm_use(&video->video.entity, 1);
- if (ret < 0) {
- omap3isp_put(video->isp);
- goto done;
- }
-
- omap3isp_video_queue_init(&handle->queue, video->type,
- &isp_video_queue_ops, video->isp->dev,
- sizeof(struct isp_buffer));
-
- memset(&handle->format, 0, sizeof(handle->format));
- handle->format.type = video->type;
- handle->timeperframe.denominator = 1;
-
- handle->video = video;
- file->private_data = &handle->vfh;
-
-done:
- if (ret < 0) {
- v4l2_fh_del(&handle->vfh);
- kfree(handle);
- }
-
- return ret;
-}
-
-static int isp_video_release(struct file *file)
-{
- struct isp_video *video = video_drvdata(file);
- struct v4l2_fh *vfh = file->private_data;
- struct isp_video_fh *handle = to_isp_video_fh(vfh);
-
- /* Disable streaming and free the buffers queue resources. */
- isp_video_streamoff(file, vfh, video->type);
-
- mutex_lock(&handle->queue.lock);
- omap3isp_video_queue_cleanup(&handle->queue);
- mutex_unlock(&handle->queue.lock);
-
- omap3isp_pipeline_pm_use(&video->video.entity, 0);
-
- /* Release the file handle. */
- v4l2_fh_del(vfh);
- kfree(handle);
- file->private_data = NULL;
-
- omap3isp_put(video->isp);
-
- return 0;
-}
-
-static unsigned int isp_video_poll(struct file *file, poll_table *wait)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
- struct isp_video_queue *queue = &vfh->queue;
-
- return omap3isp_video_queue_poll(queue, file, wait);
-}
-
-static int isp_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct isp_video_fh *vfh = to_isp_video_fh(file->private_data);
-
- return omap3isp_video_queue_mmap(&vfh->queue, vma);
-}
-
-static struct v4l2_file_operations isp_video_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
- .open = isp_video_open,
- .release = isp_video_release,
- .poll = isp_video_poll,
- .mmap = isp_video_mmap,
-};
-
-/* -----------------------------------------------------------------------------
- * ISP video core
- */
-
-static const struct isp_video_operations isp_video_dummy_ops = {
-};
-
-int omap3isp_video_init(struct isp_video *video, const char *name)
-{
- const char *direction;
- int ret;
-
- switch (video->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- direction = "output";
- video->pad.flags = MEDIA_PAD_FL_SINK;
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- direction = "input";
- video->pad.flags = MEDIA_PAD_FL_SOURCE;
- break;
-
- default:
- return -EINVAL;
- }
-
- ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
- if (ret < 0)
- return ret;
-
- mutex_init(&video->mutex);
- atomic_set(&video->active, 0);
-
- spin_lock_init(&video->pipe.lock);
- mutex_init(&video->stream_lock);
-
- /* Initialize the video device. */
- if (video->ops == NULL)
- video->ops = &isp_video_dummy_ops;
-
- video->video.fops = &isp_video_fops;
- snprintf(video->video.name, sizeof(video->video.name),
- "OMAP3 ISP %s %s", name, direction);
- video->video.vfl_type = VFL_TYPE_GRABBER;
- video->video.release = video_device_release_empty;
- video->video.ioctl_ops = &isp_video_ioctl_ops;
- video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;
-
- video_set_drvdata(&video->video, video);
-
- return 0;
-}
-
-void omap3isp_video_cleanup(struct isp_video *video)
-{
- media_entity_cleanup(&video->video.entity);
- mutex_destroy(&video->stream_lock);
- mutex_destroy(&video->mutex);
-}
-
-int omap3isp_video_register(struct isp_video *video, struct v4l2_device *vdev)
-{
- int ret;
-
- video->video.v4l2_dev = vdev;
-
- ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
- if (ret < 0)
- printk(KERN_ERR "%s: could not register video device (%d)\n",
- __func__, ret);
-
- return ret;
-}
-
-void omap3isp_video_unregister(struct isp_video *video)
-{
- if (video_is_registered(&video->video))
- video_unregister_device(&video->video);
-}
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
deleted file mode 100644
index 5acc909500ec..000000000000
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * ispvideo.h
- *
- * TI OMAP3 ISP - Generic video node
- *
- * Copyright (C) 2009-2010 Nokia Corporation
- *
- * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#ifndef OMAP3_ISP_VIDEO_H
-#define OMAP3_ISP_VIDEO_H
-
-#include <linux/v4l2-mediabus.h>
-#include <media/media-entity.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-
-#include "ispqueue.h"
-
-#define ISP_VIDEO_DRIVER_NAME "ispvideo"
-#define ISP_VIDEO_DRIVER_VERSION "0.0.2"
-
-struct isp_device;
-struct isp_video;
-struct v4l2_mbus_framefmt;
-struct v4l2_pix_format;
-
-/*
- * struct isp_format_info - ISP media bus format information
- * @code: V4L2 media bus format code
- * @truncated: V4L2 media bus format code for the same format truncated to 10
- * bits. Identical to @code if the format is 10 bits wide or less.
- * @uncompressed: V4L2 media bus format code for the corresponding uncompressed
- * format. Identical to @code if the format is not DPCM compressed.
- * @flavor: V4L2 media bus format code for the same pixel layout but
- * shifted to be 8 bits per pixel. =0 if format is not shiftable.
- * @pixelformat: V4L2 pixel format FCC identifier
- * @bpp: Bits per pixel
- */
-struct isp_format_info {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_mbus_pixelcode truncated;
- enum v4l2_mbus_pixelcode uncompressed;
- enum v4l2_mbus_pixelcode flavor;
- u32 pixelformat;
- unsigned int bpp;
-};
-
-enum isp_pipeline_stream_state {
- ISP_PIPELINE_STREAM_STOPPED = 0,
- ISP_PIPELINE_STREAM_CONTINUOUS = 1,
- ISP_PIPELINE_STREAM_SINGLESHOT = 2,
-};
-
-enum isp_pipeline_state {
- /* The stream has been started on the input video node. */
- ISP_PIPELINE_STREAM_INPUT = 1,
- /* The stream has been started on the output video node. */
- ISP_PIPELINE_STREAM_OUTPUT = 2,
- /* At least one buffer is queued on the input video node. */
- ISP_PIPELINE_QUEUE_INPUT = 4,
- /* At least one buffer is queued on the output video node. */
- ISP_PIPELINE_QUEUE_OUTPUT = 8,
- /* The input entity is idle, ready to be started. */
- ISP_PIPELINE_IDLE_INPUT = 16,
- /* The output entity is idle, ready to be started. */
- ISP_PIPELINE_IDLE_OUTPUT = 32,
- /* The pipeline is currently streaming. */
- ISP_PIPELINE_STREAM = 64,
-};
-
-/*
- * struct isp_pipeline - An ISP hardware pipeline
- * @error: A hardware error occurred during capture
- * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
- */
-struct isp_pipeline {
- struct media_pipeline pipe;
- spinlock_t lock; /* Pipeline state and queue flags */
- unsigned int state;
- enum isp_pipeline_stream_state stream_state;
- struct isp_video *input;
- struct isp_video *output;
- u32 entities;
- unsigned long l3_ick;
- unsigned int max_rate;
- atomic_t frame_number;
- bool do_propagation; /* of frame number */
- bool error;
- struct v4l2_fract max_timeperframe;
- struct v4l2_subdev *external;
- unsigned int external_rate;
- unsigned int external_bpp;
-};
-
-#define to_isp_pipeline(__e) \
- container_of((__e)->pipe, struct isp_pipeline, pipe)
-
-static inline int isp_pipeline_ready(struct isp_pipeline *pipe)
-{
- return pipe->state == (ISP_PIPELINE_STREAM_INPUT |
- ISP_PIPELINE_STREAM_OUTPUT |
- ISP_PIPELINE_QUEUE_INPUT |
- ISP_PIPELINE_QUEUE_OUTPUT |
- ISP_PIPELINE_IDLE_INPUT |
- ISP_PIPELINE_IDLE_OUTPUT);
-}
-
-/*
- * struct isp_buffer - ISP buffer
- * @buffer: ISP video buffer
- * @isp_addr: MMU mapped address (a.k.a. device address) of the buffer.
- */
-struct isp_buffer {
- struct isp_video_buffer buffer;
- dma_addr_t isp_addr;
-};
-
-#define to_isp_buffer(buf) container_of(buf, struct isp_buffer, buffer)
-
-enum isp_video_dmaqueue_flags {
- /* Set if DMA queue becomes empty when ISP_PIPELINE_STREAM_CONTINUOUS */
- ISP_VIDEO_DMAQUEUE_UNDERRUN = (1 << 0),
- /* Set when queuing buffer to an empty DMA queue */
- ISP_VIDEO_DMAQUEUE_QUEUED = (1 << 1),
-};
-
-#define isp_video_dmaqueue_flags_clr(video) \
- ({ (video)->dmaqueue_flags = 0; })
-
-/*
- * struct isp_video_operations - ISP video operations
- * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF
- * if there was no buffer previously queued.
- */
-struct isp_video_operations {
- int(*queue)(struct isp_video *video, struct isp_buffer *buffer);
-};
-
-struct isp_video {
- struct video_device video;
- enum v4l2_buf_type type;
- struct media_pad pad;
-
- struct mutex mutex; /* format and crop settings */
- atomic_t active;
-
- struct isp_device *isp;
-
- unsigned int capture_mem;
- unsigned int bpl_alignment; /* alignment value */
- unsigned int bpl_zero_padding; /* whether the alignment is optional */
- unsigned int bpl_max; /* maximum bytes per line value */
- unsigned int bpl_value; /* bytes per line value */
- unsigned int bpl_padding; /* padding at end of line */
-
- /* Entity video node streaming */
- unsigned int streaming:1;
-
- /* Pipeline state */
- struct isp_pipeline pipe;
- struct mutex stream_lock; /* pipeline and stream states */
-
- /* Video buffers queue */
- struct isp_video_queue *queue;
- struct list_head dmaqueue;
- enum isp_video_dmaqueue_flags dmaqueue_flags;
-
- const struct isp_video_operations *ops;
-};
-
-#define to_isp_video(vdev) container_of(vdev, struct isp_video, video)
-
-struct isp_video_fh {
- struct v4l2_fh vfh;
- struct isp_video *video;
- struct isp_video_queue queue;
- struct v4l2_format format;
- struct v4l2_fract timeperframe;
-};
-
-#define to_isp_video_fh(fh) container_of(fh, struct isp_video_fh, vfh)
-#define isp_video_queue_to_isp_video_fh(q) \
- container_of(q, struct isp_video_fh, queue)
-
-int omap3isp_video_init(struct isp_video *video, const char *name);
-void omap3isp_video_cleanup(struct isp_video *video);
-int omap3isp_video_register(struct isp_video *video,
- struct v4l2_device *vdev);
-void omap3isp_video_unregister(struct isp_video *video);
-struct isp_buffer *omap3isp_video_buffer_next(struct isp_video *video);
-void omap3isp_video_resume(struct isp_video *video, int continuous);
-struct media_pad *omap3isp_video_remote_pad(struct isp_video *video);
-
-const struct isp_format_info *
-omap3isp_video_format_info(enum v4l2_mbus_pixelcode code);
-
-#endif /* OMAP3_ISP_VIDEO_H */
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
deleted file mode 100644
index 7c44d1fe3c87..000000000000
--- a/drivers/media/video/ov2640.c
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * ov2640 Camera Driver
- *
- * Copyright (C) 2010 Alberto Panizzo <maramaopercheseimorto@gmail.com>
- *
- * Based on ov772x, ov9640 drivers and previous non merged implementations.
- *
- * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright (C) 2006, OmniVision
- *
- * 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 <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-ctrls.h>
-
-#define VAL_SET(x, mask, rshift, lshift) \
- ((((x) >> rshift) & mask) << lshift)
-/*
- * DSP registers
- * register offset for BANK_SEL == BANK_SEL_DSP
- */
-#define R_BYPASS 0x05 /* Bypass DSP */
-#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */
-#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */
-#define QS 0x44 /* Quantization Scale Factor */
-#define CTRLI 0x50
-#define CTRLI_LP_DP 0x80
-#define CTRLI_ROUND 0x40
-#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3)
-#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0)
-#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */
-#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
-#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */
-#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0)
-#define XOFFL 0x53 /* OFFSET_X[7:0] */
-#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
-#define YOFFL 0x54 /* OFFSET_Y[7:0] */
-#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0)
-#define VHYX 0x55 /* Offset and size completion */
-#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7)
-#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3)
-#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4)
-#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0)
-#define DPRP 0x56
-#define TEST 0x57 /* Horizontal size completion */
-#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7)
-#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */
-#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0)
-#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */
-#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0)
-#define ZMHH 0x5C /* Zoom: Speed and H&W completion */
-#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4)
-#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2)
-#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0)
-#define BPADDR 0x7C /* SDE Indirect Register Access: Address */
-#define BPDATA 0x7D /* SDE Indirect Register Access: Data */
-#define CTRL2 0x86 /* DSP Module enable 2 */
-#define CTRL2_DCW_EN 0x20
-#define CTRL2_SDE_EN 0x10
-#define CTRL2_UV_ADJ_EN 0x08
-#define CTRL2_UV_AVG_EN 0x04
-#define CTRL2_CMX_EN 0x01
-#define CTRL3 0x87 /* DSP Module enable 3 */
-#define CTRL3_BPC_EN 0x80
-#define CTRL3_WPC_EN 0x40
-#define SIZEL 0x8C /* Image Size Completion */
-#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6)
-#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3)
-#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0)
-#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */
-#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
-#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */
-#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0)
-#define CTRL0 0xC2 /* DSP Module enable 0 */
-#define CTRL0_AEC_EN 0x80
-#define CTRL0_AEC_SEL 0x40
-#define CTRL0_STAT_SEL 0x20
-#define CTRL0_VFIRST 0x10
-#define CTRL0_YUV422 0x08
-#define CTRL0_YUV_EN 0x04
-#define CTRL0_RGB_EN 0x02
-#define CTRL0_RAW_EN 0x01
-#define CTRL1 0xC3 /* DSP Module enable 1 */
-#define CTRL1_CIP 0x80
-#define CTRL1_DMY 0x40
-#define CTRL1_RAW_GMA 0x20
-#define CTRL1_DG 0x10
-#define CTRL1_AWB 0x08
-#define CTRL1_AWB_GAIN 0x04
-#define CTRL1_LENC 0x02
-#define CTRL1_PRE 0x01
-#define R_DVP_SP 0xD3 /* DVP output speed control */
-#define R_DVP_SP_AUTO_MODE 0x80
-#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0);
- * = sysclk (48)/(2*[6:0]) (RAW);*/
-#define IMAGE_MODE 0xDA /* Image Output Format Select */
-#define IMAGE_MODE_Y8_DVP_EN 0x40
-#define IMAGE_MODE_JPEG_EN 0x10
-#define IMAGE_MODE_YUV422 0x00
-#define IMAGE_MODE_RAW10 0x04 /* (DVP) */
-#define IMAGE_MODE_RGB565 0x08
-#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output
- * mode (0 for HREF is same as sensor) */
-#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP
- * 1: Low byte first UYVY (C2[4] =0)
- * VYUY (C2[4] =1)
- * 0: High byte first YUYV (C2[4]=0)
- * YVYU (C2[4] = 1) */
-#define RESET 0xE0 /* Reset */
-#define RESET_MICROC 0x40
-#define RESET_SCCB 0x20
-#define RESET_JPEG 0x10
-#define RESET_DVP 0x04
-#define RESET_IPU 0x02
-#define RESET_CIF 0x01
-#define REGED 0xED /* Register ED */
-#define REGED_CLK_OUT_DIS 0x10
-#define MS_SP 0xF0 /* SCCB Master Speed */
-#define SS_ID 0xF7 /* SCCB Slave ID */
-#define SS_CTRL 0xF8 /* SCCB Slave Control */
-#define SS_CTRL_ADD_AUTO_INC 0x20
-#define SS_CTRL_EN 0x08
-#define SS_CTRL_DELAY_CLK 0x04
-#define SS_CTRL_ACC_EN 0x02
-#define SS_CTRL_SEN_PASS_THR 0x01
-#define MC_BIST 0xF9 /* Microcontroller misc register */
-#define MC_BIST_RESET 0x80 /* Microcontroller Reset */
-#define MC_BIST_BOOT_ROM_SEL 0x40
-#define MC_BIST_12KB_SEL 0x20
-#define MC_BIST_12KB_MASK 0x30
-#define MC_BIST_512KB_SEL 0x08
-#define MC_BIST_512KB_MASK 0x0C
-#define MC_BIST_BUSY_BIT_R 0x02
-#define MC_BIST_MC_RES_ONE_SH_W 0x02
-#define MC_BIST_LAUNCH 0x01
-#define BANK_SEL 0xFF /* Register Bank Select */
-#define BANK_SEL_DSP 0x00
-#define BANK_SEL_SENS 0x01
-
-/*
- * Sensor registers
- * register offset for BANK_SEL == BANK_SEL_SENS
- */
-#define GAIN 0x00 /* AGC - Gain control gain setting */
-#define COM1 0x03 /* Common control 1 */
-#define COM1_1_DUMMY_FR 0x40
-#define COM1_3_DUMMY_FR 0x80
-#define COM1_7_DUMMY_FR 0xC0
-#define COM1_VWIN_LSB_UXGA 0x0F
-#define COM1_VWIN_LSB_SVGA 0x0A
-#define COM1_VWIN_LSB_CIF 0x06
-#define REG04 0x04 /* Register 04 */
-#define REG04_DEF 0x20 /* Always set */
-#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */
-#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */
-#define REG04_VREF_EN 0x10
-#define REG04_HREF_EN 0x08
-#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0)
-#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */
-#define COM2 0x09 /* Common control 2 */
-#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
- /* Output drive capability */
-#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */
-#define PID 0x0A /* Product ID Number MSB */
-#define VER 0x0B /* Product ID Number LSB */
-#define COM3 0x0C /* Common control 3 */
-#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */
-#define COM3_BAND_AUTO 0x02 /* Auto Banding */
-#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the
- * snapshot sequence*/
-#define AEC 0x10 /* AEC[9:2] Exposure Value */
-#define CLKRC 0x11 /* Internal clock */
-#define CLKRC_EN 0x80
-#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */
-#define COM7 0x12 /* Common control 7 */
-#define COM7_SRST 0x80 /* Initiates system reset. All registers are
- * set to factory default values after which
- * the chip resumes normal operation */
-#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */
-#define COM7_RES_SVGA 0x40 /* SVGA */
-#define COM7_RES_CIF 0x20 /* CIF */
-#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */
-#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */
-#define COM8 0x13 /* Common control 8 */
-#define COM8_DEF 0xC0 /* Banding filter ON/OFF */
-#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */
-#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
-#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
-#define COM9 0x14 /* Common control 9
- * Automatic gain ceiling - maximum AGC value [7:5]*/
-#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */
-#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */
-#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */
-#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */
-#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */
-#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */
-#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */
-#define COM10 0x15 /* Common control 10 */
-#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */
-#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of
- * PCLK (user can latch data at the next
- * falling edge of PCLK).
- * 0 otherwise. */
-#define COM10_HREF_INV 0x08 /* Invert HREF polarity:
- * HREF negative for valid data*/
-#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */
-#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */
-#define HEND 0x18 /* Horizontal Window end MSB 8 bit */
-#define VSTART 0x19 /* Vertical Window start MSB 8 bit */
-#define VEND 0x1A /* Vertical Window end MSB 8 bit */
-#define MIDH 0x1C /* Manufacturer ID byte - high */
-#define MIDL 0x1D /* Manufacturer ID byte - low */
-#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
-#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
-#define VV 0x26 /* AGC/AEC Fast mode operating region */
-#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4)
-#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0)
-#define REG2A 0x2A /* Dummy pixel insert MSB */
-#define FRARL 0x2B /* Dummy pixel insert LSB */
-#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
-#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */
-#define YAVG 0x2F /* Y/G Channel Average value */
-#define REG32 0x32 /* Common Control 32 */
-#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */
-#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */
-#define ARCOM2 0x34 /* Zoom: Horizontal start point */
-#define REG45 0x45 /* Register 45 */
-#define FLL 0x46 /* Frame Length Adjustment LSBs */
-#define FLH 0x47 /* Frame Length Adjustment MSBs */
-#define COM19 0x48 /* Zoom: Vertical start point */
-#define ZOOMS 0x49 /* Zoom: Vertical start point */
-#define COM22 0x4B /* Flash light control */
-#define COM25 0x4E /* For Banding operations */
-#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */
-#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */
-#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */
-#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */
-#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */
-#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */
-#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */
-#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */
-
-/*
- * ID
- */
-#define MANUFACTURER_ID 0x7FA2
-#define PID_OV2640 0x2642
-#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF))
-
-/*
- * Struct
- */
-struct regval_list {
- u8 reg_num;
- u8 value;
-};
-
-/* Supported resolutions */
-enum ov2640_width {
- W_QCIF = 176,
- W_QVGA = 320,
- W_CIF = 352,
- W_VGA = 640,
- W_SVGA = 800,
- W_XGA = 1024,
- W_SXGA = 1280,
- W_UXGA = 1600,
-};
-
-enum ov2640_height {
- H_QCIF = 144,
- H_QVGA = 240,
- H_CIF = 288,
- H_VGA = 480,
- H_SVGA = 600,
- H_XGA = 768,
- H_SXGA = 1024,
- H_UXGA = 1200,
-};
-
-struct ov2640_win_size {
- char *name;
- enum ov2640_width width;
- enum ov2640_height height;
- const struct regval_list *regs;
-};
-
-
-struct ov2640_priv {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- enum v4l2_mbus_pixelcode cfmt_code;
- const struct ov2640_win_size *win;
- int model;
-};
-
-/*
- * Registers settings
- */
-
-#define ENDMARKER { 0xff, 0xff }
-
-static const struct regval_list ov2640_init_regs[] = {
- { BANK_SEL, BANK_SEL_DSP },
- { 0x2c, 0xff },
- { 0x2e, 0xdf },
- { BANK_SEL, BANK_SEL_SENS },
- { 0x3c, 0x32 },
- { CLKRC, CLKRC_DIV_SET(1) },
- { COM2, COM2_OCAP_Nx_SET(3) },
- { REG04, REG04_DEF | REG04_HREF_EN },
- { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN },
- { COM9, COM9_AGC_GAIN_8x | 0x08},
- { 0x2c, 0x0c },
- { 0x33, 0x78 },
- { 0x3a, 0x33 },
- { 0x3b, 0xfb },
- { 0x3e, 0x00 },
- { 0x43, 0x11 },
- { 0x16, 0x10 },
- { 0x39, 0x02 },
- { 0x35, 0x88 },
- { 0x22, 0x0a },
- { 0x37, 0x40 },
- { 0x23, 0x00 },
- { ARCOM2, 0xa0 },
- { 0x06, 0x02 },
- { 0x06, 0x88 },
- { 0x07, 0xc0 },
- { 0x0d, 0xb7 },
- { 0x0e, 0x01 },
- { 0x4c, 0x00 },
- { 0x4a, 0x81 },
- { 0x21, 0x99 },
- { AEW, 0x40 },
- { AEB, 0x38 },
- { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) },
- { 0x5c, 0x00 },
- { 0x63, 0x00 },
- { FLL, 0x22 },
- { COM3, 0x38 | COM3_BAND_AUTO },
- { REG5D, 0x55 },
- { REG5E, 0x7d },
- { REG5F, 0x7d },
- { REG60, 0x55 },
- { HISTO_LOW, 0x70 },
- { HISTO_HIGH, 0x80 },
- { 0x7c, 0x05 },
- { 0x20, 0x80 },
- { 0x28, 0x30 },
- { 0x6c, 0x00 },
- { 0x6d, 0x80 },
- { 0x6e, 0x00 },
- { 0x70, 0x02 },
- { 0x71, 0x94 },
- { 0x73, 0xc1 },
- { 0x3d, 0x34 },
- { COM7, COM7_RES_UXGA | COM7_ZOOM_EN },
- { 0x5a, 0x57 },
- { BD50, 0xbb },
- { BD60, 0x9c },
- { BANK_SEL, BANK_SEL_DSP },
- { 0xe5, 0x7f },
- { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL },
- { 0x41, 0x24 },
- { RESET, RESET_JPEG | RESET_DVP },
- { 0x76, 0xff },
- { 0x33, 0xa0 },
- { 0x42, 0x20 },
- { 0x43, 0x18 },
- { 0x4c, 0x00 },
- { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 },
- { 0x88, 0x3f },
- { 0xd7, 0x03 },
- { 0xd9, 0x10 },
- { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 },
- { 0xc8, 0x08 },
- { 0xc9, 0x80 },
- { BPADDR, 0x00 },
- { BPDATA, 0x00 },
- { BPADDR, 0x03 },
- { BPDATA, 0x48 },
- { BPDATA, 0x48 },
- { BPADDR, 0x08 },
- { BPDATA, 0x20 },
- { BPDATA, 0x10 },
- { BPDATA, 0x0e },
- { 0x90, 0x00 },
- { 0x91, 0x0e },
- { 0x91, 0x1a },
- { 0x91, 0x31 },
- { 0x91, 0x5a },
- { 0x91, 0x69 },
- { 0x91, 0x75 },
- { 0x91, 0x7e },
- { 0x91, 0x88 },
- { 0x91, 0x8f },
- { 0x91, 0x96 },
- { 0x91, 0xa3 },
- { 0x91, 0xaf },
- { 0x91, 0xc4 },
- { 0x91, 0xd7 },
- { 0x91, 0xe8 },
- { 0x91, 0x20 },
- { 0x92, 0x00 },
- { 0x93, 0x06 },
- { 0x93, 0xe3 },
- { 0x93, 0x03 },
- { 0x93, 0x03 },
- { 0x93, 0x00 },
- { 0x93, 0x02 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x93, 0x00 },
- { 0x96, 0x00 },
- { 0x97, 0x08 },
- { 0x97, 0x19 },
- { 0x97, 0x02 },
- { 0x97, 0x0c },
- { 0x97, 0x24 },
- { 0x97, 0x30 },
- { 0x97, 0x28 },
- { 0x97, 0x26 },
- { 0x97, 0x02 },
- { 0x97, 0x98 },
- { 0x97, 0x80 },
- { 0x97, 0x00 },
- { 0x97, 0x00 },
- { 0xa4, 0x00 },
- { 0xa8, 0x00 },
- { 0xc5, 0x11 },
- { 0xc6, 0x51 },
- { 0xbf, 0x80 },
- { 0xc7, 0x10 },
- { 0xb6, 0x66 },
- { 0xb8, 0xA5 },
- { 0xb7, 0x64 },
- { 0xb9, 0x7C },
- { 0xb3, 0xaf },
- { 0xb4, 0x97 },
- { 0xb5, 0xFF },
- { 0xb0, 0xC5 },
- { 0xb1, 0x94 },
- { 0xb2, 0x0f },
- { 0xc4, 0x5c },
- { 0xa6, 0x00 },
- { 0xa7, 0x20 },
- { 0xa7, 0xd8 },
- { 0xa7, 0x1b },
- { 0xa7, 0x31 },
- { 0xa7, 0x00 },
- { 0xa7, 0x18 },
- { 0xa7, 0x20 },
- { 0xa7, 0xd8 },
- { 0xa7, 0x19 },
- { 0xa7, 0x31 },
- { 0xa7, 0x00 },
- { 0xa7, 0x18 },
- { 0xa7, 0x20 },
- { 0xa7, 0xd8 },
- { 0xa7, 0x19 },
- { 0xa7, 0x31 },
- { 0xa7, 0x00 },
- { 0xa7, 0x18 },
- { 0x7f, 0x00 },
- { 0xe5, 0x1f },
- { 0xe1, 0x77 },
- { 0xdd, 0x7f },
- { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN },
- ENDMARKER,
-};
-
-/*
- * Register settings for window size
- * The preamble, setup the internal DSP to input an UXGA (1600x1200) image.
- * Then the different zooming configurations will setup the output image size.
- */
-static const struct regval_list ov2640_size_change_preamble_regs[] = {
- { BANK_SEL, BANK_SEL_DSP },
- { RESET, RESET_DVP },
- { HSIZE8, HSIZE8_SET(W_UXGA) },
- { VSIZE8, VSIZE8_SET(H_UXGA) },
- { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN |
- CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN },
- { HSIZE, HSIZE_SET(W_UXGA) },
- { VSIZE, VSIZE_SET(H_UXGA) },
- { XOFFL, XOFFL_SET(0) },
- { YOFFL, YOFFL_SET(0) },
- { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) |
- VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)},
- { TEST, TEST_HSIZE_SET(W_UXGA) },
- ENDMARKER,
-};
-
-#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \
- { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \
- CTRLI_H_DIV_SET(h_div)}, \
- { ZMOW, ZMOW_OUTW_SET(x) }, \
- { ZMOH, ZMOH_OUTH_SET(y) }, \
- { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \
- { R_DVP_SP, pclk_div }, \
- { RESET, 0x00}
-
-static const struct regval_list ov2640_qcif_regs[] = {
- PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4),
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_qvga_regs[] = {
- PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4),
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_cif_regs[] = {
- PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8),
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_vga_regs[] = {
- PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2),
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_svga_regs[] = {
- PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2),
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_xga_regs[] = {
- PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2),
- { CTRLI, 0x00},
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_sxga_regs[] = {
- PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2),
- { CTRLI, 0x00},
- { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE },
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_uxga_regs[] = {
- PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0),
- { CTRLI, 0x00},
- { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE },
- ENDMARKER,
-};
-
-#define OV2640_SIZE(n, w, h, r) \
- {.name = n, .width = w , .height = h, .regs = r }
-
-static const struct ov2640_win_size ov2640_supported_win_sizes[] = {
- OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs),
- OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs),
- OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs),
- OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs),
- OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs),
- OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs),
- OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs),
- OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs),
-};
-
-/*
- * Register settings for pixel formats
- */
-static const struct regval_list ov2640_format_change_preamble_regs[] = {
- { BANK_SEL, BANK_SEL_DSP },
- { R_BYPASS, R_BYPASS_USE_DSP },
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_yuv422_regs[] = {
- { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 },
- { 0xD7, 0x01 },
- { 0x33, 0xa0 },
- { 0xe1, 0x67 },
- { RESET, 0x00 },
- { R_BYPASS, R_BYPASS_USE_DSP },
- ENDMARKER,
-};
-
-static const struct regval_list ov2640_rgb565_regs[] = {
- { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 },
- { 0xd7, 0x03 },
- { RESET, 0x00 },
- { R_BYPASS, R_BYPASS_USE_DSP },
- ENDMARKER,
-};
-
-static enum v4l2_mbus_pixelcode ov2640_codes[] = {
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_RGB565_2X8_LE,
-};
-
-/*
- * General functions
- */
-static struct ov2640_priv *to_ov2640(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov2640_priv,
- subdev);
-}
-
-static int ov2640_write_array(struct i2c_client *client,
- const struct regval_list *vals)
-{
- int ret;
-
- while ((vals->reg_num != 0xff) || (vals->value != 0xff)) {
- ret = i2c_smbus_write_byte_data(client,
- vals->reg_num, vals->value);
- dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x",
- vals->reg_num, vals->value);
-
- if (ret < 0)
- return ret;
- vals++;
- }
- return 0;
-}
-
-static int ov2640_mask_set(struct i2c_client *client,
- u8 reg, u8 mask, u8 set)
-{
- s32 val = i2c_smbus_read_byte_data(client, reg);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- dev_vdbg(&client->dev, "masks: 0x%02x, 0x%02x", reg, val);
-
- return i2c_smbus_write_byte_data(client, reg, val);
-}
-
-static int ov2640_reset(struct i2c_client *client)
-{
- int ret;
- const struct regval_list reset_seq[] = {
- {BANK_SEL, BANK_SEL_SENS},
- {COM7, COM7_SRST},
- ENDMARKER,
- };
-
- ret = ov2640_write_array(client, reset_seq);
- if (ret)
- goto err;
-
- msleep(5);
-err:
- dev_dbg(&client->dev, "%s: (ret %d)", __func__, ret);
- return ret;
-}
-
-/*
- * soc_camera_ops functions
- */
-static int ov2640_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
-static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd =
- &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 val;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- val = ctrl->val ? REG04_VFLIP_IMG : 0x00;
- return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val);
- case V4L2_CID_HFLIP:
- val = ctrl->val ? REG04_HFLIP_IMG : 0x00;
- return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val);
- }
-
- return -EINVAL;
-}
-
-static int ov2640_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov2640_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- reg->size = 1;
- if (reg->reg > 0xff)
- return -EINVAL;
-
- ret = i2c_smbus_read_byte_data(client, reg->reg);
- if (ret < 0)
- return ret;
-
- reg->val = ret;
-
- return 0;
-}
-
-static int ov2640_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg > 0xff ||
- reg->val > 0xff)
- return -EINVAL;
-
- return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
-}
-#endif
-
-/* Select the nearest higher resolution for capture */
-static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height)
-{
- int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1;
-
- for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) {
- if (ov2640_supported_win_sizes[i].width >= *width &&
- ov2640_supported_win_sizes[i].height >= *height) {
- *width = ov2640_supported_win_sizes[i].width;
- *height = ov2640_supported_win_sizes[i].height;
- return &ov2640_supported_win_sizes[i];
- }
- }
-
- *width = ov2640_supported_win_sizes[default_size].width;
- *height = ov2640_supported_win_sizes[default_size].height;
- return &ov2640_supported_win_sizes[default_size];
-}
-
-static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height,
- enum v4l2_mbus_pixelcode code)
-{
- struct ov2640_priv *priv = to_ov2640(client);
- const struct regval_list *selected_cfmt_regs;
- int ret;
-
- /* select win */
- priv->win = ov2640_select_win(width, height);
-
- /* select format */
- priv->cfmt_code = 0;
- switch (code) {
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- dev_dbg(&client->dev, "%s: Selected cfmt RGB565", __func__);
- selected_cfmt_regs = ov2640_rgb565_regs;
- break;
- default:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- dev_dbg(&client->dev, "%s: Selected cfmt YUV422", __func__);
- selected_cfmt_regs = ov2640_yuv422_regs;
- }
-
- /* reset hardware */
- ov2640_reset(client);
-
- /* initialize the sensor with default data */
- dev_dbg(&client->dev, "%s: Init default", __func__);
- ret = ov2640_write_array(client, ov2640_init_regs);
- if (ret < 0)
- goto err;
-
- /* select preamble */
- dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name);
- ret = ov2640_write_array(client, ov2640_size_change_preamble_regs);
- if (ret < 0)
- goto err;
-
- /* set size win */
- ret = ov2640_write_array(client, priv->win->regs);
- if (ret < 0)
- goto err;
-
- /* cfmt preamble */
- dev_dbg(&client->dev, "%s: Set cfmt", __func__);
- ret = ov2640_write_array(client, ov2640_format_change_preamble_regs);
- if (ret < 0)
- goto err;
-
- /* set cfmt */
- ret = ov2640_write_array(client, selected_cfmt_regs);
- if (ret < 0)
- goto err;
-
- priv->cfmt_code = code;
- *width = priv->win->width;
- *height = priv->win->height;
-
- return 0;
-
-err:
- dev_err(&client->dev, "%s: Error %d", __func__, ret);
- ov2640_reset(client);
- priv->win = NULL;
-
- return ret;
-}
-
-static int ov2640_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov2640_priv *priv = to_ov2640(client);
-
- if (!priv->win) {
- u32 width = W_SVGA, height = H_SVGA;
- priv->win = ov2640_select_win(&width, &height);
- priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8;
- }
-
- mf->width = priv->win->width;
- mf->height = priv->win->height;
- mf->code = priv->cfmt_code;
-
- switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- default:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- }
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov2640_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
-
- switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- }
-
- ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code);
-
- return ret;
-}
-
-static int ov2640_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- const struct ov2640_win_size *win;
-
- /*
- * select suitable win
- */
- win = ov2640_select_win(&mf->width, &mf->height);
-
- mf->field = V4L2_FIELD_NONE;
-
- switch (mf->code) {
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- }
-
- return 0;
-}
-
-static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(ov2640_codes))
- return -EINVAL;
-
- *code = ov2640_codes[index];
- return 0;
-}
-
-static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = W_UXGA;
- a->c.height = H_UXGA;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = W_UXGA;
- a->bounds.height = H_UXGA;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int ov2640_video_probe(struct i2c_client *client)
-{
- struct ov2640_priv *priv = to_ov2640(client);
- u8 pid, ver, midh, midl;
- const char *devname;
- int ret;
-
- /*
- * check and show product ID and manufacturer ID
- */
- i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS);
- pid = i2c_smbus_read_byte_data(client, PID);
- ver = i2c_smbus_read_byte_data(client, VER);
- midh = i2c_smbus_read_byte_data(client, MIDH);
- midl = i2c_smbus_read_byte_data(client, MIDL);
-
- switch (VERSION(pid, ver)) {
- case PID_OV2640:
- devname = "ov2640";
- priv->model = V4L2_IDENT_OV2640;
- break;
- default:
- dev_err(&client->dev,
- "Product ID error %x:%x\n", pid, ver);
- ret = -ENODEV;
- goto err;
- }
-
- dev_info(&client->dev,
- "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
- devname, pid, ver, midh, midl);
-
- return v4l2_ctrl_handler_setup(&priv->hdl);
-
-err:
- return ret;
-}
-
-static const struct v4l2_ctrl_ops ov2640_ctrl_ops = {
- .s_ctrl = ov2640_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = {
- .g_chip_ident = ov2640_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov2640_g_register,
- .s_register = ov2640_s_register,
-#endif
-};
-
-static int ov2640_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = {
- .s_stream = ov2640_s_stream,
- .g_mbus_fmt = ov2640_g_fmt,
- .s_mbus_fmt = ov2640_s_fmt,
- .try_mbus_fmt = ov2640_try_fmt,
- .cropcap = ov2640_cropcap,
- .g_crop = ov2640_g_crop,
- .enum_mbus_fmt = ov2640_enum_fmt,
- .g_mbus_config = ov2640_g_mbus_config,
-};
-
-static struct v4l2_subdev_ops ov2640_subdev_ops = {
- .core = &ov2640_subdev_core_ops,
- .video = &ov2640_subdev_video_ops,
-};
-
-/*
- * i2c_driver functions
- */
-static int ov2640_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov2640_priv *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- int ret;
-
- if (!icl) {
- dev_err(&adapter->dev,
- "OV2640: Missing platform_data for driver\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&adapter->dev,
- "OV2640: I2C-Adapter doesn't support SMBUS\n");
- return -EIO;
- }
-
- priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&adapter->dev,
- "Failed to allocate memory for private data!\n");
- return -ENOMEM;
- }
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops);
- v4l2_ctrl_handler_init(&priv->hdl, 2);
- v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error) {
- int err = priv->hdl.error;
-
- kfree(priv);
- return err;
- }
-
- ret = ov2640_video_probe(client);
- if (ret) {
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- } else {
- dev_info(&adapter->dev, "OV2640 Probed\n");
- }
-
- return ret;
-}
-
-static int ov2640_remove(struct i2c_client *client)
-{
- struct ov2640_priv *priv = to_ov2640(client);
-
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id ov2640_id[] = {
- { "ov2640", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov2640_id);
-
-static struct i2c_driver ov2640_i2c_driver = {
- .driver = {
- .name = "ov2640",
- },
- .probe = ov2640_probe,
- .remove = ov2640_remove,
- .id_table = ov2640_id,
-};
-
-module_i2c_driver(ov2640_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor");
-MODULE_AUTHOR("Alberto Panizzo");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
deleted file mode 100644
index 0bc93313d37a..000000000000
--- a/drivers/media/video/ov5642.c
+++ /dev/null
@@ -1,1073 +0,0 @@
-/*
- * Driver for OV5642 CMOS Image Sensor from Omnivision
- *
- * Copyright (C) 2011, Bastian Hecht <hechtb@gmail.com>
- *
- * Based on Sony IMX074 Camera Driver
- * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * Based on Omnivision OV7670 Camera Driver
- * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net>
- *
- * 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/bitops.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-#include <linux/v4l2-mediabus.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
-
-/* OV5642 registers */
-#define REG_CHIP_ID_HIGH 0x300a
-#define REG_CHIP_ID_LOW 0x300b
-
-#define REG_WINDOW_START_X_HIGH 0x3800
-#define REG_WINDOW_START_X_LOW 0x3801
-#define REG_WINDOW_START_Y_HIGH 0x3802
-#define REG_WINDOW_START_Y_LOW 0x3803
-#define REG_WINDOW_WIDTH_HIGH 0x3804
-#define REG_WINDOW_WIDTH_LOW 0x3805
-#define REG_WINDOW_HEIGHT_HIGH 0x3806
-#define REG_WINDOW_HEIGHT_LOW 0x3807
-#define REG_OUT_WIDTH_HIGH 0x3808
-#define REG_OUT_WIDTH_LOW 0x3809
-#define REG_OUT_HEIGHT_HIGH 0x380a
-#define REG_OUT_HEIGHT_LOW 0x380b
-#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c
-#define REG_OUT_TOTAL_WIDTH_LOW 0x380d
-#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e
-#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f
-#define REG_OUTPUT_FORMAT 0x4300
-#define REG_ISP_CTRL_01 0x5001
-#define REG_AVG_WINDOW_END_X_HIGH 0x5682
-#define REG_AVG_WINDOW_END_X_LOW 0x5683
-#define REG_AVG_WINDOW_END_Y_HIGH 0x5686
-#define REG_AVG_WINDOW_END_Y_LOW 0x5687
-
-/* active pixel array size */
-#define OV5642_SENSOR_SIZE_X 2592
-#define OV5642_SENSOR_SIZE_Y 1944
-
-/*
- * About OV5642 resolution, cropping and binning:
- * This sensor supports it all, at least in the feature description.
- * Unfortunately, no combination of appropriate registers settings could make
- * the chip work the intended way. As it works with predefined register lists,
- * some undocumented registers are presumably changed there to achieve their
- * goals.
- * This driver currently only works for resolutions up to 720 lines with a
- * 1:1 scale. Hopefully these restrictions will be removed in the future.
- */
-#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X
-#define OV5642_MAX_HEIGHT 720
-
-/* default sizes */
-#define OV5642_DEFAULT_WIDTH 1280
-#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT
-
-/* minimum extra blanking */
-#define BLANKING_EXTRA_WIDTH 500
-#define BLANKING_EXTRA_HEIGHT 20
-
-/*
- * the sensor's autoexposure is buggy when setting total_height low.
- * It tries to expose longer than 1 frame period without taking care of it
- * and this leads to weird output. So we set 1000 lines as minimum.
- */
-#define BLANKING_MIN_HEIGHT 1000
-
-struct regval_list {
- u16 reg_num;
- u8 value;
-};
-
-static struct regval_list ov5642_default_regs_init[] = {
- { 0x3103, 0x93 },
- { 0x3008, 0x82 },
- { 0x3017, 0x7f },
- { 0x3018, 0xfc },
- { 0x3810, 0xc2 },
- { 0x3615, 0xf0 },
- { 0x3000, 0x0 },
- { 0x3001, 0x0 },
- { 0x3002, 0x0 },
- { 0x3003, 0x0 },
- { 0x3004, 0xff },
- { 0x3030, 0x2b },
- { 0x3011, 0x8 },
- { 0x3010, 0x10 },
- { 0x3604, 0x60 },
- { 0x3622, 0x60 },
- { 0x3621, 0x9 },
- { 0x3709, 0x0 },
- { 0x4000, 0x21 },
- { 0x401d, 0x22 },
- { 0x3600, 0x54 },
- { 0x3605, 0x4 },
- { 0x3606, 0x3f },
- { 0x3c01, 0x80 },
- { 0x300d, 0x22 },
- { 0x3623, 0x22 },
- { 0x5000, 0x4f },
- { 0x5020, 0x4 },
- { 0x5181, 0x79 },
- { 0x5182, 0x0 },
- { 0x5185, 0x22 },
- { 0x5197, 0x1 },
- { 0x5500, 0xa },
- { 0x5504, 0x0 },
- { 0x5505, 0x7f },
- { 0x5080, 0x8 },
- { 0x300e, 0x18 },
- { 0x4610, 0x0 },
- { 0x471d, 0x5 },
- { 0x4708, 0x6 },
- { 0x370c, 0xa0 },
- { 0x5687, 0x94 },
- { 0x501f, 0x0 },
- { 0x5000, 0x4f },
- { 0x5001, 0xcf },
- { 0x4300, 0x30 },
- { 0x4300, 0x30 },
- { 0x460b, 0x35 },
- { 0x471d, 0x0 },
- { 0x3002, 0xc },
- { 0x3002, 0x0 },
- { 0x4713, 0x3 },
- { 0x471c, 0x50 },
- { 0x4721, 0x2 },
- { 0x4402, 0x90 },
- { 0x460c, 0x22 },
- { 0x3815, 0x44 },
- { 0x3503, 0x7 },
- { 0x3501, 0x73 },
- { 0x3502, 0x80 },
- { 0x350b, 0x0 },
- { 0x3818, 0xc8 },
- { 0x3824, 0x11 },
- { 0x3a00, 0x78 },
- { 0x3a1a, 0x4 },
- { 0x3a13, 0x30 },
- { 0x3a18, 0x0 },
- { 0x3a19, 0x7c },
- { 0x3a08, 0x12 },
- { 0x3a09, 0xc0 },
- { 0x3a0a, 0xf },
- { 0x3a0b, 0xa0 },
- { 0x350c, 0x7 },
- { 0x350d, 0xd0 },
- { 0x3a0d, 0x8 },
- { 0x3a0e, 0x6 },
- { 0x3500, 0x0 },
- { 0x3501, 0x0 },
- { 0x3502, 0x0 },
- { 0x350a, 0x0 },
- { 0x350b, 0x0 },
- { 0x3503, 0x0 },
- { 0x3a0f, 0x3c },
- { 0x3a10, 0x32 },
- { 0x3a1b, 0x3c },
- { 0x3a1e, 0x32 },
- { 0x3a11, 0x80 },
- { 0x3a1f, 0x20 },
- { 0x3030, 0x2b },
- { 0x3a02, 0x0 },
- { 0x3a03, 0x7d },
- { 0x3a04, 0x0 },
- { 0x3a14, 0x0 },
- { 0x3a15, 0x7d },
- { 0x3a16, 0x0 },
- { 0x3a00, 0x78 },
- { 0x3a08, 0x9 },
- { 0x3a09, 0x60 },
- { 0x3a0a, 0x7 },
- { 0x3a0b, 0xd0 },
- { 0x3a0d, 0x10 },
- { 0x3a0e, 0xd },
- { 0x4407, 0x4 },
- { 0x5193, 0x70 },
- { 0x589b, 0x0 },
- { 0x589a, 0xc0 },
- { 0x401e, 0x20 },
- { 0x4001, 0x42 },
- { 0x401c, 0x6 },
- { 0x3825, 0xac },
- { 0x3827, 0xc },
- { 0x528a, 0x1 },
- { 0x528b, 0x4 },
- { 0x528c, 0x8 },
- { 0x528d, 0x10 },
- { 0x528e, 0x20 },
- { 0x528f, 0x28 },
- { 0x5290, 0x30 },
- { 0x5292, 0x0 },
- { 0x5293, 0x1 },
- { 0x5294, 0x0 },
- { 0x5295, 0x4 },
- { 0x5296, 0x0 },
- { 0x5297, 0x8 },
- { 0x5298, 0x0 },
- { 0x5299, 0x10 },
- { 0x529a, 0x0 },
- { 0x529b, 0x20 },
- { 0x529c, 0x0 },
- { 0x529d, 0x28 },
- { 0x529e, 0x0 },
- { 0x529f, 0x30 },
- { 0x5282, 0x0 },
- { 0x5300, 0x0 },
- { 0x5301, 0x20 },
- { 0x5302, 0x0 },
- { 0x5303, 0x7c },
- { 0x530c, 0x0 },
- { 0x530d, 0xc },
- { 0x530e, 0x20 },
- { 0x530f, 0x80 },
- { 0x5310, 0x20 },
- { 0x5311, 0x80 },
- { 0x5308, 0x20 },
- { 0x5309, 0x40 },
- { 0x5304, 0x0 },
- { 0x5305, 0x30 },
- { 0x5306, 0x0 },
- { 0x5307, 0x80 },
- { 0x5314, 0x8 },
- { 0x5315, 0x20 },
- { 0x5319, 0x30 },
- { 0x5316, 0x10 },
- { 0x5317, 0x0 },
- { 0x5318, 0x2 },
- { 0x5380, 0x1 },
- { 0x5381, 0x0 },
- { 0x5382, 0x0 },
- { 0x5383, 0x4e },
- { 0x5384, 0x0 },
- { 0x5385, 0xf },
- { 0x5386, 0x0 },
- { 0x5387, 0x0 },
- { 0x5388, 0x1 },
- { 0x5389, 0x15 },
- { 0x538a, 0x0 },
- { 0x538b, 0x31 },
- { 0x538c, 0x0 },
- { 0x538d, 0x0 },
- { 0x538e, 0x0 },
- { 0x538f, 0xf },
- { 0x5390, 0x0 },
- { 0x5391, 0xab },
- { 0x5392, 0x0 },
- { 0x5393, 0xa2 },
- { 0x5394, 0x8 },
- { 0x5480, 0x14 },
- { 0x5481, 0x21 },
- { 0x5482, 0x36 },
- { 0x5483, 0x57 },
- { 0x5484, 0x65 },
- { 0x5485, 0x71 },
- { 0x5486, 0x7d },
- { 0x5487, 0x87 },
- { 0x5488, 0x91 },
- { 0x5489, 0x9a },
- { 0x548a, 0xaa },
- { 0x548b, 0xb8 },
- { 0x548c, 0xcd },
- { 0x548d, 0xdd },
- { 0x548e, 0xea },
- { 0x548f, 0x1d },
- { 0x5490, 0x5 },
- { 0x5491, 0x0 },
- { 0x5492, 0x4 },
- { 0x5493, 0x20 },
- { 0x5494, 0x3 },
- { 0x5495, 0x60 },
- { 0x5496, 0x2 },
- { 0x5497, 0xb8 },
- { 0x5498, 0x2 },
- { 0x5499, 0x86 },
- { 0x549a, 0x2 },
- { 0x549b, 0x5b },
- { 0x549c, 0x2 },
- { 0x549d, 0x3b },
- { 0x549e, 0x2 },
- { 0x549f, 0x1c },
- { 0x54a0, 0x2 },
- { 0x54a1, 0x4 },
- { 0x54a2, 0x1 },
- { 0x54a3, 0xed },
- { 0x54a4, 0x1 },
- { 0x54a5, 0xc5 },
- { 0x54a6, 0x1 },
- { 0x54a7, 0xa5 },
- { 0x54a8, 0x1 },
- { 0x54a9, 0x6c },
- { 0x54aa, 0x1 },
- { 0x54ab, 0x41 },
- { 0x54ac, 0x1 },
- { 0x54ad, 0x20 },
- { 0x54ae, 0x0 },
- { 0x54af, 0x16 },
- { 0x54b0, 0x1 },
- { 0x54b1, 0x20 },
- { 0x54b2, 0x0 },
- { 0x54b3, 0x10 },
- { 0x54b4, 0x0 },
- { 0x54b5, 0xf0 },
- { 0x54b6, 0x0 },
- { 0x54b7, 0xdf },
- { 0x5402, 0x3f },
- { 0x5403, 0x0 },
- { 0x3406, 0x0 },
- { 0x5180, 0xff },
- { 0x5181, 0x52 },
- { 0x5182, 0x11 },
- { 0x5183, 0x14 },
- { 0x5184, 0x25 },
- { 0x5185, 0x24 },
- { 0x5186, 0x6 },
- { 0x5187, 0x8 },
- { 0x5188, 0x8 },
- { 0x5189, 0x7c },
- { 0x518a, 0x60 },
- { 0x518b, 0xb2 },
- { 0x518c, 0xb2 },
- { 0x518d, 0x44 },
- { 0x518e, 0x3d },
- { 0x518f, 0x58 },
- { 0x5190, 0x46 },
- { 0x5191, 0xf8 },
- { 0x5192, 0x4 },
- { 0x5193, 0x70 },
- { 0x5194, 0xf0 },
- { 0x5195, 0xf0 },
- { 0x5196, 0x3 },
- { 0x5197, 0x1 },
- { 0x5198, 0x4 },
- { 0x5199, 0x12 },
- { 0x519a, 0x4 },
- { 0x519b, 0x0 },
- { 0x519c, 0x6 },
- { 0x519d, 0x82 },
- { 0x519e, 0x0 },
- { 0x5025, 0x80 },
- { 0x3a0f, 0x38 },
- { 0x3a10, 0x30 },
- { 0x3a1b, 0x3a },
- { 0x3a1e, 0x2e },
- { 0x3a11, 0x60 },
- { 0x3a1f, 0x10 },
- { 0x5688, 0xa6 },
- { 0x5689, 0x6a },
- { 0x568a, 0xea },
- { 0x568b, 0xae },
- { 0x568c, 0xa6 },
- { 0x568d, 0x6a },
- { 0x568e, 0x62 },
- { 0x568f, 0x26 },
- { 0x5583, 0x40 },
- { 0x5584, 0x40 },
- { 0x5580, 0x2 },
- { 0x5000, 0xcf },
- { 0x5800, 0x27 },
- { 0x5801, 0x19 },
- { 0x5802, 0x12 },
- { 0x5803, 0xf },
- { 0x5804, 0x10 },
- { 0x5805, 0x15 },
- { 0x5806, 0x1e },
- { 0x5807, 0x2f },
- { 0x5808, 0x15 },
- { 0x5809, 0xd },
- { 0x580a, 0xa },
- { 0x580b, 0x9 },
- { 0x580c, 0xa },
- { 0x580d, 0xc },
- { 0x580e, 0x12 },
- { 0x580f, 0x19 },
- { 0x5810, 0xb },
- { 0x5811, 0x7 },
- { 0x5812, 0x4 },
- { 0x5813, 0x3 },
- { 0x5814, 0x3 },
- { 0x5815, 0x6 },
- { 0x5816, 0xa },
- { 0x5817, 0xf },
- { 0x5818, 0xa },
- { 0x5819, 0x5 },
- { 0x581a, 0x1 },
- { 0x581b, 0x0 },
- { 0x581c, 0x0 },
- { 0x581d, 0x3 },
- { 0x581e, 0x8 },
- { 0x581f, 0xc },
- { 0x5820, 0xa },
- { 0x5821, 0x5 },
- { 0x5822, 0x1 },
- { 0x5823, 0x0 },
- { 0x5824, 0x0 },
- { 0x5825, 0x3 },
- { 0x5826, 0x8 },
- { 0x5827, 0xc },
- { 0x5828, 0xe },
- { 0x5829, 0x8 },
- { 0x582a, 0x6 },
- { 0x582b, 0x4 },
- { 0x582c, 0x5 },
- { 0x582d, 0x7 },
- { 0x582e, 0xb },
- { 0x582f, 0x12 },
- { 0x5830, 0x18 },
- { 0x5831, 0x10 },
- { 0x5832, 0xc },
- { 0x5833, 0xa },
- { 0x5834, 0xb },
- { 0x5835, 0xe },
- { 0x5836, 0x15 },
- { 0x5837, 0x19 },
- { 0x5838, 0x32 },
- { 0x5839, 0x1f },
- { 0x583a, 0x18 },
- { 0x583b, 0x16 },
- { 0x583c, 0x17 },
- { 0x583d, 0x1e },
- { 0x583e, 0x26 },
- { 0x583f, 0x53 },
- { 0x5840, 0x10 },
- { 0x5841, 0xf },
- { 0x5842, 0xd },
- { 0x5843, 0xc },
- { 0x5844, 0xe },
- { 0x5845, 0x9 },
- { 0x5846, 0x11 },
- { 0x5847, 0x10 },
- { 0x5848, 0x10 },
- { 0x5849, 0x10 },
- { 0x584a, 0x10 },
- { 0x584b, 0xe },
- { 0x584c, 0x10 },
- { 0x584d, 0x10 },
- { 0x584e, 0x11 },
- { 0x584f, 0x10 },
- { 0x5850, 0xf },
- { 0x5851, 0xc },
- { 0x5852, 0xf },
- { 0x5853, 0x10 },
- { 0x5854, 0x10 },
- { 0x5855, 0xf },
- { 0x5856, 0xe },
- { 0x5857, 0xb },
- { 0x5858, 0x10 },
- { 0x5859, 0xd },
- { 0x585a, 0xd },
- { 0x585b, 0xc },
- { 0x585c, 0xc },
- { 0x585d, 0xc },
- { 0x585e, 0xb },
- { 0x585f, 0xc },
- { 0x5860, 0xc },
- { 0x5861, 0xc },
- { 0x5862, 0xd },
- { 0x5863, 0x8 },
- { 0x5864, 0x11 },
- { 0x5865, 0x18 },
- { 0x5866, 0x18 },
- { 0x5867, 0x19 },
- { 0x5868, 0x17 },
- { 0x5869, 0x19 },
- { 0x586a, 0x16 },
- { 0x586b, 0x13 },
- { 0x586c, 0x13 },
- { 0x586d, 0x12 },
- { 0x586e, 0x13 },
- { 0x586f, 0x16 },
- { 0x5870, 0x14 },
- { 0x5871, 0x12 },
- { 0x5872, 0x10 },
- { 0x5873, 0x11 },
- { 0x5874, 0x11 },
- { 0x5875, 0x16 },
- { 0x5876, 0x14 },
- { 0x5877, 0x11 },
- { 0x5878, 0x10 },
- { 0x5879, 0xf },
- { 0x587a, 0x10 },
- { 0x587b, 0x14 },
- { 0x587c, 0x13 },
- { 0x587d, 0x12 },
- { 0x587e, 0x11 },
- { 0x587f, 0x11 },
- { 0x5880, 0x12 },
- { 0x5881, 0x15 },
- { 0x5882, 0x14 },
- { 0x5883, 0x15 },
- { 0x5884, 0x15 },
- { 0x5885, 0x15 },
- { 0x5886, 0x13 },
- { 0x5887, 0x17 },
- { 0x3710, 0x10 },
- { 0x3632, 0x51 },
- { 0x3702, 0x10 },
- { 0x3703, 0xb2 },
- { 0x3704, 0x18 },
- { 0x370b, 0x40 },
- { 0x370d, 0x3 },
- { 0x3631, 0x1 },
- { 0x3632, 0x52 },
- { 0x3606, 0x24 },
- { 0x3620, 0x96 },
- { 0x5785, 0x7 },
- { 0x3a13, 0x30 },
- { 0x3600, 0x52 },
- { 0x3604, 0x48 },
- { 0x3606, 0x1b },
- { 0x370d, 0xb },
- { 0x370f, 0xc0 },
- { 0x3709, 0x1 },
- { 0x3823, 0x0 },
- { 0x5007, 0x0 },
- { 0x5009, 0x0 },
- { 0x5011, 0x0 },
- { 0x5013, 0x0 },
- { 0x519e, 0x0 },
- { 0x5086, 0x0 },
- { 0x5087, 0x0 },
- { 0x5088, 0x0 },
- { 0x5089, 0x0 },
- { 0x302b, 0x0 },
- { 0x3503, 0x7 },
- { 0x3011, 0x8 },
- { 0x350c, 0x2 },
- { 0x350d, 0xe4 },
- { 0x3621, 0xc9 },
- { 0x370a, 0x81 },
- { 0xffff, 0xff },
-};
-
-static struct regval_list ov5642_default_regs_finalise[] = {
- { 0x3810, 0xc2 },
- { 0x3818, 0xc9 },
- { 0x381c, 0x10 },
- { 0x381d, 0xa0 },
- { 0x381e, 0x5 },
- { 0x381f, 0xb0 },
- { 0x3820, 0x0 },
- { 0x3821, 0x0 },
- { 0x3824, 0x11 },
- { 0x3a08, 0x1b },
- { 0x3a09, 0xc0 },
- { 0x3a0a, 0x17 },
- { 0x3a0b, 0x20 },
- { 0x3a0d, 0x2 },
- { 0x3a0e, 0x1 },
- { 0x401c, 0x4 },
- { 0x5682, 0x5 },
- { 0x5683, 0x0 },
- { 0x5686, 0x2 },
- { 0x5687, 0xcc },
- { 0x5001, 0x4f },
- { 0x589b, 0x6 },
- { 0x589a, 0xc5 },
- { 0x3503, 0x0 },
- { 0x460c, 0x20 },
- { 0x460b, 0x37 },
- { 0x471c, 0xd0 },
- { 0x471d, 0x5 },
- { 0x3815, 0x1 },
- { 0x3818, 0xc1 },
- { 0x501f, 0x0 },
- { 0x5002, 0xe0 },
- { 0x4300, 0x32 }, /* UYVY */
- { 0x3002, 0x1c },
- { 0x4800, 0x14 },
- { 0x4801, 0xf },
- { 0x3007, 0x3b },
- { 0x300e, 0x4 },
- { 0x4803, 0x50 },
- { 0x3815, 0x1 },
- { 0x4713, 0x2 },
- { 0x4842, 0x1 },
- { 0x300f, 0xe },
- { 0x3003, 0x3 },
- { 0x3003, 0x1 },
- { 0xffff, 0xff },
-};
-
-struct ov5642_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-struct ov5642 {
- struct v4l2_subdev subdev;
- const struct ov5642_datafmt *fmt;
- struct v4l2_rect crop_rect;
-
- /* blanking information */
- int total_width;
- int total_height;
-};
-
-static const struct ov5642_datafmt ov5642_colour_fmts[] = {
- {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG},
-};
-
-static struct ov5642 *to_ov5642(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov5642, subdev);
-}
-
-/* Find a data format by a pixel code in an array */
-static const struct ov5642_datafmt
- *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++)
- if (ov5642_colour_fmts[i].code == code)
- return ov5642_colour_fmts + i;
-
- return NULL;
-}
-
-static int reg_read(struct i2c_client *client, u16 reg, u8 *val)
-{
- int ret;
- /* We have 16-bit i2c addresses - care for endianess */
- unsigned char data[2] = { reg >> 8, reg & 0xff };
-
- ret = i2c_master_send(client, data, 2);
- if (ret < 2) {
- dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
- __func__, reg);
- return ret < 0 ? ret : -EIO;
- }
-
- ret = i2c_master_recv(client, val, 1);
- if (ret < 1) {
- dev_err(&client->dev, "%s: i2c read error, reg: %x\n",
- __func__, reg);
- return ret < 0 ? ret : -EIO;
- }
- return 0;
-}
-
-static int reg_write(struct i2c_client *client, u16 reg, u8 val)
-{
- int ret;
- unsigned char data[3] = { reg >> 8, reg & 0xff, val };
-
- ret = i2c_master_send(client, data, 3);
- if (ret < 3) {
- dev_err(&client->dev, "%s: i2c write error, reg: %x\n",
- __func__, reg);
- return ret < 0 ? ret : -EIO;
- }
-
- return 0;
-}
-
-/*
- * convenience function to write 16 bit register values that are split up
- * into two consecutive high and low parts
- */
-static int reg_write16(struct i2c_client *client, u16 reg, u16 val16)
-{
- int ret;
-
- ret = reg_write(client, reg, val16 >> 8);
- if (ret)
- return ret;
- return reg_write(client, reg + 1, val16 & 0x00ff);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov5642_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 = 1;
-
- ret = reg_read(client, reg->reg, &val);
- if (!ret)
- reg->val = (__u64)val;
-
- return ret;
-}
-
-static int ov5642_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 reg_write(client, reg->reg, reg->val);
-}
-#endif
-
-static int ov5642_write_array(struct i2c_client *client,
- struct regval_list *vals)
-{
- while (vals->reg_num != 0xffff || vals->value != 0xff) {
- int ret = reg_write(client, vals->reg_num, vals->value);
- if (ret < 0)
- return ret;
- vals++;
- }
- dev_dbg(&client->dev, "Register list loaded\n");
- return 0;
-}
-
-static int ov5642_set_resolution(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
- int width = priv->crop_rect.width;
- int height = priv->crop_rect.height;
- int total_width = priv->total_width;
- int total_height = priv->total_height;
- int start_x = (OV5642_SENSOR_SIZE_X - width) / 2;
- int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2;
- int ret;
-
- /*
- * This should set the starting point for cropping.
- * Doesn't work so far.
- */
- ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x);
- if (!ret)
- ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y);
- if (!ret) {
- priv->crop_rect.left = start_x;
- priv->crop_rect.top = start_y;
- }
-
- if (!ret)
- ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width);
- if (!ret)
- ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height);
- if (ret)
- return ret;
- priv->crop_rect.width = width;
- priv->crop_rect.height = height;
-
- /* Set the output window size. Only 1:1 scale is supported so far. */
- ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width);
- if (!ret)
- ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height);
-
- /* Total width = output size + blanking */
- if (!ret)
- ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width);
- if (!ret)
- ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height);
-
- /* Sets the window for AWB calculations */
- if (!ret)
- ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width);
- if (!ret)
- ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height);
-
- return ret;
-}
-
-static int ov5642_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
- const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code);
-
- mf->width = priv->crop_rect.width;
- mf->height = priv->crop_rect.height;
-
- if (!fmt) {
- mf->code = ov5642_colour_fmts[0].code;
- mf->colorspace = ov5642_colour_fmts[0].colorspace;
- }
-
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov5642_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
-
- /* MIPI CSI could have changed the format, double-check */
- if (!ov5642_find_datafmt(mf->code))
- return -EINVAL;
-
- ov5642_try_fmt(sd, mf);
- priv->fmt = ov5642_find_datafmt(mf->code);
-
- return 0;
-}
-
-static int ov5642_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
-
- const struct ov5642_datafmt *fmt = priv->fmt;
-
- mf->code = fmt->code;
- mf->colorspace = fmt->colorspace;
- mf->width = priv->crop_rect.width;
- mf->height = priv->crop_rect.height;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(ov5642_colour_fmts))
- return -EINVAL;
-
- *code = ov5642_colour_fmts[index].code;
- return 0;
-}
-
-static int ov5642_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_OV5642;
- id->revision = 0;
-
- return 0;
-}
-
-static int ov5642_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
- struct v4l2_rect *rect = &a->c;
- int ret;
-
- v4l_bound_align_image(&rect->width, 48, OV5642_MAX_WIDTH, 1,
- &rect->height, 32, OV5642_MAX_HEIGHT, 1, 0);
-
- priv->crop_rect.width = rect->width;
- priv->crop_rect.height = rect->height;
- priv->total_width = rect->width + BLANKING_EXTRA_WIDTH;
- priv->total_height = max_t(int, rect->height +
- BLANKING_EXTRA_HEIGHT,
- BLANKING_MIN_HEIGHT);
- priv->crop_rect.width = rect->width;
- priv->crop_rect.height = rect->height;
-
- ret = ov5642_write_array(client, ov5642_default_regs_init);
- if (!ret)
- ret = ov5642_set_resolution(sd);
- if (!ret)
- ret = ov5642_write_array(client, ov5642_default_regs_finalise);
-
- return ret;
-}
-
-static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov5642 *priv = to_ov5642(client);
- struct v4l2_rect *rect = &a->c;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- *rect = priv->crop_rect;
-
- return 0;
-}
-
-static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = OV5642_MAX_WIDTH;
- a->bounds.height = OV5642_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int ov5642_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- cfg->type = V4L2_MBUS_CSI2;
- cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |
- V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
-
- return 0;
-}
-
-static int ov5642_s_power(struct v4l2_subdev *sd, int on)
-{
- struct i2c_client *client;
- int ret;
-
- if (!on)
- return 0;
-
- client = v4l2_get_subdevdata(sd);
- ret = ov5642_write_array(client, ov5642_default_regs_init);
- if (!ret)
- ret = ov5642_set_resolution(sd);
- if (!ret)
- ret = ov5642_write_array(client, ov5642_default_regs_finalise);
-
- return ret;
-}
-
-static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = {
- .s_mbus_fmt = ov5642_s_fmt,
- .g_mbus_fmt = ov5642_g_fmt,
- .try_mbus_fmt = ov5642_try_fmt,
- .enum_mbus_fmt = ov5642_enum_fmt,
- .s_crop = ov5642_s_crop,
- .g_crop = ov5642_g_crop,
- .cropcap = ov5642_cropcap,
- .g_mbus_config = ov5642_g_mbus_config,
-};
-
-static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = {
- .s_power = ov5642_s_power,
- .g_chip_ident = ov5642_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov5642_get_register,
- .s_register = ov5642_set_register,
-#endif
-};
-
-static struct v4l2_subdev_ops ov5642_subdev_ops = {
- .core = &ov5642_subdev_core_ops,
- .video = &ov5642_subdev_video_ops,
-};
-
-static int ov5642_video_probe(struct i2c_client *client)
-{
- int ret;
- u8 id_high, id_low;
- u16 id;
-
- /* Read sensor Model ID */
- ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high);
- if (ret < 0)
- return ret;
-
- id = id_high << 8;
-
- ret = reg_read(client, REG_CHIP_ID_LOW, &id_low);
- if (ret < 0)
- return ret;
-
- id |= id_low;
-
- dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
-
- if (id != 0x5642)
- return -ENODEV;
-
- return 0;
-}
-
-static int ov5642_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov5642 *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "OV5642: missing platform data!\n");
- return -EINVAL;
- }
-
- priv = kzalloc(sizeof(struct ov5642), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops);
-
- priv->fmt = &ov5642_colour_fmts[0];
-
- priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
- priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
- priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
- priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
- priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
- priv->total_height = BLANKING_MIN_HEIGHT;
-
- ret = ov5642_video_probe(client);
- if (ret < 0)
- goto error;
-
- return 0;
-
-error:
- kfree(priv);
- return ret;
-}
-
-static int ov5642_remove(struct i2c_client *client)
-{
- struct ov5642 *priv = to_ov5642(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- if (icl->free_bus)
- icl->free_bus(icl);
- kfree(priv);
-
- return 0;
-}
-
-static const struct i2c_device_id ov5642_id[] = {
- { "ov5642", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov5642_id);
-
-static struct i2c_driver ov5642_i2c_driver = {
- .driver = {
- .name = "ov5642",
- },
- .probe = ov5642_probe,
- .remove = ov5642_remove,
- .id_table = ov5642_id,
-};
-
-module_i2c_driver(ov5642_i2c_driver);
-
-MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
-MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
deleted file mode 100644
index 3e028b1970dd..000000000000
--- a/drivers/media/video/ov6650.c
+++ /dev/null
@@ -1,1053 +0,0 @@
-/*
- * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor
- *
- * Copyright (C) 2010 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * Based on OmniVision OV96xx Camera Driver
- * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
- *
- * Based on ov772x camera driver:
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov7670 and soc_camera_platform driver,
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * Hardware specific bits initialy based on former work by Matt Callow
- * drivers/media/video/omap/sensor_ov6650.c
- * Copyright (C) 2006 Matt Callow
- *
- * 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/bitops.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/module.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-
-/* Register definitions */
-#define REG_GAIN 0x00 /* range 00 - 3F */
-#define REG_BLUE 0x01
-#define REG_RED 0x02
-#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */
-#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */
-
-#define REG_BRT 0x06
-
-#define REG_PIDH 0x0a
-#define REG_PIDL 0x0b
-
-#define REG_AECH 0x10
-#define REG_CLKRC 0x11 /* Data Format and Internal Clock */
- /* [7:6] Input system clock (MHz)*/
- /* 00=8, 01=12, 10=16, 11=24 */
- /* [5:0]: Internal Clock Pre-Scaler */
-#define REG_COMA 0x12 /* [7] Reset */
-#define REG_COMB 0x13
-#define REG_COMC 0x14
-#define REG_COMD 0x15
-#define REG_COML 0x16
-#define REG_HSTRT 0x17
-#define REG_HSTOP 0x18
-#define REG_VSTRT 0x19
-#define REG_VSTOP 0x1a
-#define REG_PSHFT 0x1b
-#define REG_MIDH 0x1c
-#define REG_MIDL 0x1d
-#define REG_HSYNS 0x1e
-#define REG_HSYNE 0x1f
-#define REG_COME 0x20
-#define REG_YOFF 0x21
-#define REG_UOFF 0x22
-#define REG_VOFF 0x23
-#define REG_AEW 0x24
-#define REG_AEB 0x25
-#define REG_COMF 0x26
-#define REG_COMG 0x27
-#define REG_COMH 0x28
-#define REG_COMI 0x29
-
-#define REG_FRARL 0x2b
-#define REG_COMJ 0x2c
-#define REG_COMK 0x2d
-#define REG_AVGY 0x2e
-#define REG_REF0 0x2f
-#define REG_REF1 0x30
-#define REG_REF2 0x31
-#define REG_FRAJH 0x32
-#define REG_FRAJL 0x33
-#define REG_FACT 0x34
-#define REG_L1AEC 0x35
-#define REG_AVGU 0x36
-#define REG_AVGV 0x37
-
-#define REG_SPCB 0x60
-#define REG_SPCC 0x61
-#define REG_GAM1 0x62
-#define REG_GAM2 0x63
-#define REG_GAM3 0x64
-#define REG_SPCD 0x65
-
-#define REG_SPCE 0x68
-#define REG_ADCL 0x69
-
-#define REG_RMCO 0x6c
-#define REG_GMCO 0x6d
-#define REG_BMCO 0x6e
-
-
-/* Register bits, values, etc. */
-#define OV6650_PIDH 0x66 /* high byte of product ID number */
-#define OV6650_PIDL 0x50 /* low byte of product ID number */
-#define OV6650_MIDH 0x7F /* high byte of mfg ID */
-#define OV6650_MIDL 0xA2 /* low byte of mfg ID */
-
-#define DEF_GAIN 0x00
-#define DEF_BLUE 0x80
-#define DEF_RED 0x80
-
-#define SAT_SHIFT 4
-#define SAT_MASK (0xf << SAT_SHIFT)
-#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK)
-
-#define HUE_EN BIT(5)
-#define HUE_MASK 0x1f
-#define DEF_HUE 0x10
-#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK))
-
-#define DEF_AECH 0x4D
-
-#define CLKRC_6MHz 0x00
-#define CLKRC_12MHz 0x40
-#define CLKRC_16MHz 0x80
-#define CLKRC_24MHz 0xc0
-#define CLKRC_DIV_MASK 0x3f
-#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1)
-
-#define COMA_RESET BIT(7)
-#define COMA_QCIF BIT(5)
-#define COMA_RAW_RGB BIT(4)
-#define COMA_RGB BIT(3)
-#define COMA_BW BIT(2)
-#define COMA_WORD_SWAP BIT(1)
-#define COMA_BYTE_SWAP BIT(0)
-#define DEF_COMA 0x00
-
-#define COMB_FLIP_V BIT(7)
-#define COMB_FLIP_H BIT(5)
-#define COMB_BAND_FILTER BIT(4)
-#define COMB_AWB BIT(2)
-#define COMB_AGC BIT(1)
-#define COMB_AEC BIT(0)
-#define DEF_COMB 0x5f
-
-#define COML_ONE_CHANNEL BIT(7)
-
-#define DEF_HSTRT 0x24
-#define DEF_HSTOP 0xd4
-#define DEF_VSTRT 0x04
-#define DEF_VSTOP 0x94
-
-#define COMF_HREF_LOW BIT(4)
-
-#define COMJ_PCLK_RISING BIT(4)
-#define COMJ_VSYNC_HIGH BIT(0)
-
-/* supported resolutions */
-#define W_QCIF (DEF_HSTOP - DEF_HSTRT)
-#define W_CIF (W_QCIF << 1)
-#define H_QCIF (DEF_VSTOP - DEF_VSTRT)
-#define H_CIF (H_QCIF << 1)
-
-#define FRAME_RATE_MAX 30
-
-
-struct ov6650_reg {
- u8 reg;
- u8 val;
-};
-
-struct ov6650 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct {
- /* exposure/autoexposure cluster */
- struct v4l2_ctrl *autoexposure;
- struct v4l2_ctrl *exposure;
- };
- struct {
- /* gain/autogain cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *gain;
- };
- struct {
- /* blue/red/autowhitebalance cluster */
- struct v4l2_ctrl *autowb;
- struct v4l2_ctrl *blue;
- struct v4l2_ctrl *red;
- };
- bool half_scale; /* scale down output by 2 */
- struct v4l2_rect rect; /* sensor cropping window */
- unsigned long pclk_limit; /* from host */
- unsigned long pclk_max; /* from resolution and format */
- struct v4l2_fract tpf; /* as requested with s_parm */
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-
-static enum v4l2_mbus_pixelcode ov6650_codes[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_YVYU8_2X8,
- V4L2_MBUS_FMT_VYUY8_2X8,
- V4L2_MBUS_FMT_SBGGR8_1X8,
- V4L2_MBUS_FMT_Y8_1X8,
-};
-
-/* read a register */
-static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
-{
- int ret;
- u8 data = reg;
- struct i2c_msg msg = {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = &data,
- };
-
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret < 0)
- goto err;
-
- msg.flags = I2C_M_RD;
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret < 0)
- goto err;
-
- *val = data;
- return 0;
-
-err:
- dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
- return ret;
-}
-
-/* write a register */
-static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val)
-{
- int ret;
- unsigned char data[2] = { reg, val };
- struct i2c_msg msg = {
- .addr = client->addr,
- .flags = 0,
- .len = 2,
- .buf = data,
- };
-
- ret = i2c_transfer(client->adapter, &msg, 1);
- udelay(100);
-
- if (ret < 0) {
- dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
- return ret;
- }
- return 0;
-}
-
-
-/* Read a register, alter its bits, write it back */
-static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask)
-{
- u8 val;
- int ret;
-
- ret = ov6650_reg_read(client, reg, &val);
- if (ret) {
- dev_err(&client->dev,
- "[Read]-Modify-Write of register 0x%02x failed!\n",
- reg);
- return ret;
- }
-
- val &= ~mask;
- val |= set;
-
- ret = ov6650_reg_write(client, reg, val);
- if (ret)
- dev_err(&client->dev,
- "Read-Modify-[Write] of register 0x%02x failed!\n",
- reg);
-
- return ret;
-}
-
-static struct ov6650 *to_ov6650(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov6650, subdev);
-}
-
-/* Start/Stop streaming from the device */
-static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
-/* Get status of additional camera capabilities */
-static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
- struct v4l2_subdev *sd = &priv->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- uint8_t reg, reg2;
- int ret;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- ret = ov6650_reg_read(client, REG_GAIN, &reg);
- if (!ret)
- priv->gain->val = reg;
- return ret;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = ov6650_reg_read(client, REG_BLUE, &reg);
- if (!ret)
- ret = ov6650_reg_read(client, REG_RED, &reg2);
- if (!ret) {
- priv->blue->val = reg;
- priv->red->val = reg2;
- }
- return ret;
- case V4L2_CID_EXPOSURE_AUTO:
- ret = ov6650_reg_read(client, REG_AECH, &reg);
- if (!ret)
- priv->exposure->val = reg;
- return ret;
- }
- return -EINVAL;
-}
-
-/* Set status of additional camera capabilities */
-static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
- struct v4l2_subdev *sd = &priv->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTOGAIN:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->val ? COMB_AGC : 0, COMB_AGC);
- if (!ret && !ctrl->val)
- ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
- return ret;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = ov6650_reg_rmw(client, REG_COMB,
- ctrl->val ? COMB_AWB : 0, COMB_AWB);
- if (!ret && !ctrl->val) {
- ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
- if (!ret)
- ret = ov6650_reg_write(client, REG_RED,
- priv->red->val);
- }
- return ret;
- case V4L2_CID_SATURATION:
- return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
- SAT_MASK);
- case V4L2_CID_HUE:
- return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
- HUE_MASK);
- case V4L2_CID_BRIGHTNESS:
- return ov6650_reg_write(client, REG_BRT, ctrl->val);
- case V4L2_CID_EXPOSURE_AUTO:
- ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val ==
- V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC);
- if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
- ret = ov6650_reg_write(client, REG_AECH,
- priv->exposure->val);
- return ret;
- case V4L2_CID_GAMMA:
- return ov6650_reg_write(client, REG_GAM1, ctrl->val);
- case V4L2_CID_VFLIP:
- return ov6650_reg_rmw(client, REG_COMB,
- ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
- case V4L2_CID_HFLIP:
- return ov6650_reg_rmw(client, REG_COMB,
- ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
- }
-
- return -EINVAL;
-}
-
-/* Get chip identification */
-static int ov6650_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- id->ident = V4L2_IDENT_OV6650;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov6650_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 & ~0xff)
- return -EINVAL;
-
- reg->size = 1;
-
- ret = ov6650_reg_read(client, reg->reg, &val);
- if (!ret)
- reg->val = (__u64)val;
-
- return ret;
-}
-
-static int ov6650_set_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg & ~0xff || reg->val & ~0xff)
- return -EINVAL;
-
- return ov6650_reg_write(client, reg->reg, reg->val);
-}
-#endif
-
-static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
-
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->c = priv->rect;
-
- return 0;
-}
-
-static int ov6650_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- struct v4l2_rect *rect = &a->c;
- int ret;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- rect->left = ALIGN(rect->left, 2);
- rect->width = ALIGN(rect->width, 2);
- rect->top = ALIGN(rect->top, 2);
- rect->height = ALIGN(rect->height, 2);
- soc_camera_limit_side(&rect->left, &rect->width,
- DEF_HSTRT << 1, 2, W_CIF);
- soc_camera_limit_side(&rect->top, &rect->height,
- DEF_VSTRT << 1, 2, H_CIF);
-
- ret = ov6650_reg_write(client, REG_HSTRT, rect->left >> 1);
- if (!ret) {
- priv->rect.left = rect->left;
- ret = ov6650_reg_write(client, REG_HSTOP,
- (rect->left + rect->width) >> 1);
- }
- if (!ret) {
- priv->rect.width = rect->width;
- ret = ov6650_reg_write(client, REG_VSTRT, rect->top >> 1);
- }
- if (!ret) {
- priv->rect.top = rect->top;
- ret = ov6650_reg_write(client, REG_VSTOP,
- (rect->top + rect->height) >> 1);
- }
- if (!ret)
- priv->rect.height = rect->height;
-
- return ret;
-}
-
-static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- a->bounds.left = DEF_HSTRT << 1;
- a->bounds.top = DEF_VSTRT << 1;
- a->bounds.width = W_CIF;
- a->bounds.height = H_CIF;
- a->defrect = a->bounds;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int ov6650_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
-
- mf->width = priv->rect.width >> priv->half_scale;
- mf->height = priv->rect.height >> priv->half_scale;
- mf->code = priv->code;
- mf->colorspace = priv->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect)
-{
- return width > rect->width >> 1 || height > rect->height >> 1;
-}
-
-static u8 to_clkrc(struct v4l2_fract *timeperframe,
- unsigned long pclk_limit, unsigned long pclk_max)
-{
- unsigned long pclk;
-
- if (timeperframe->numerator && timeperframe->denominator)
- pclk = pclk_max * timeperframe->denominator /
- (FRAME_RATE_MAX * timeperframe->numerator);
- else
- pclk = pclk_max;
-
- if (pclk_limit && pclk_limit < pclk)
- pclk = pclk_limit;
-
- return (pclk_max - 1) / pclk;
-}
-
-/* set the format we will capture in */
-static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd);
- struct soc_camera_sense *sense = icd->sense;
- struct ov6650 *priv = to_ov6650(client);
- bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect);
- struct v4l2_crop a = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .c = {
- .left = priv->rect.left + (priv->rect.width >> 1) -
- (mf->width >> (1 - half_scale)),
- .top = priv->rect.top + (priv->rect.height >> 1) -
- (mf->height >> (1 - half_scale)),
- .width = mf->width << half_scale,
- .height = mf->height << half_scale,
- },
- };
- enum v4l2_mbus_pixelcode code = mf->code;
- unsigned long mclk, pclk;
- u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc;
- int ret;
-
- /* select color matrix configuration for given color encoding */
- switch (code) {
- case V4L2_MBUS_FMT_Y8_1X8:
- dev_dbg(&client->dev, "pixel format GREY8_1X8\n");
- coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP;
- coma_set |= COMA_BW;
- break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n");
- coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP;
- coma_set |= COMA_WORD_SWAP;
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n");
- coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP |
- COMA_BYTE_SWAP;
- break;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n");
- if (half_scale) {
- coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
- coma_set |= COMA_BYTE_SWAP;
- } else {
- coma_mask |= COMA_RGB | COMA_BW;
- coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
- }
- break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
- dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n");
- if (half_scale) {
- coma_mask |= COMA_RGB | COMA_BW;
- coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP;
- } else {
- coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP;
- coma_set |= COMA_BYTE_SWAP;
- }
- break;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n");
- coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP;
- coma_set |= COMA_RAW_RGB | COMA_RGB;
- break;
- default:
- dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code);
- return -EINVAL;
- }
- priv->code = code;
-
- if (code == V4L2_MBUS_FMT_Y8_1X8 ||
- code == V4L2_MBUS_FMT_SBGGR8_1X8) {
- coml_mask = COML_ONE_CHANNEL;
- coml_set = 0;
- priv->pclk_max = 4000000;
- } else {
- coml_mask = 0;
- coml_set = COML_ONE_CHANNEL;
- priv->pclk_max = 8000000;
- }
-
- if (code == V4L2_MBUS_FMT_SBGGR8_1X8)
- priv->colorspace = V4L2_COLORSPACE_SRGB;
- else if (code != 0)
- priv->colorspace = V4L2_COLORSPACE_JPEG;
-
- if (half_scale) {
- dev_dbg(&client->dev, "max resolution: QCIF\n");
- coma_set |= COMA_QCIF;
- priv->pclk_max /= 2;
- } else {
- dev_dbg(&client->dev, "max resolution: CIF\n");
- coma_mask |= COMA_QCIF;
- }
- priv->half_scale = half_scale;
-
- if (sense) {
- if (sense->master_clock == 8000000) {
- dev_dbg(&client->dev, "8MHz input clock\n");
- clkrc = CLKRC_6MHz;
- } else if (sense->master_clock == 12000000) {
- dev_dbg(&client->dev, "12MHz input clock\n");
- clkrc = CLKRC_12MHz;
- } else if (sense->master_clock == 16000000) {
- dev_dbg(&client->dev, "16MHz input clock\n");
- clkrc = CLKRC_16MHz;
- } else if (sense->master_clock == 24000000) {
- dev_dbg(&client->dev, "24MHz input clock\n");
- clkrc = CLKRC_24MHz;
- } else {
- dev_err(&client->dev,
- "unsupported input clock, check platform data\n");
- return -EINVAL;
- }
- mclk = sense->master_clock;
- priv->pclk_limit = sense->pixel_clock_max;
- } else {
- clkrc = CLKRC_24MHz;
- mclk = 24000000;
- priv->pclk_limit = 0;
- dev_dbg(&client->dev, "using default 24MHz input clock\n");
- }
-
- clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
-
- pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc);
- dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n",
- mclk / pclk, 10 * mclk % pclk / pclk);
-
- ret = ov6650_s_crop(sd, &a);
- if (!ret)
- ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask);
- if (!ret)
- ret = ov6650_reg_write(client, REG_CLKRC, clkrc);
- if (!ret)
- ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask);
-
- if (!ret) {
- mf->colorspace = priv->colorspace;
- mf->width = priv->rect.width >> half_scale;
- mf->height = priv->rect.height >> half_scale;
- }
-
- return ret;
-}
-
-static int ov6650_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
-
- if (is_unscaled_ok(mf->width, mf->height, &priv->rect))
- v4l_bound_align_image(&mf->width, 2, W_CIF, 1,
- &mf->height, 2, H_CIF, 1, 0);
-
- mf->field = V4L2_FIELD_NONE;
-
- switch (mf->code) {
- case V4L2_MBUS_FMT_Y10_1X10:
- mf->code = V4L2_MBUS_FMT_Y8_1X8;
- case V4L2_MBUS_FMT_Y8_1X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- break;
- default:
- mf->code = V4L2_MBUS_FMT_SBGGR8_1X8;
- case V4L2_MBUS_FMT_SBGGR8_1X8:
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- }
-
- return 0;
-}
-
-static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(ov6650_codes))
- return -EINVAL;
-
- *code = ov6650_codes[index];
- return 0;
-}
-
-static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- struct v4l2_captureparm *cp = &parms->parm.capture;
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memset(cp, 0, sizeof(*cp));
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf,
- priv->pclk_limit, priv->pclk_max));
- cp->timeperframe.denominator = FRAME_RATE_MAX;
-
- dev_dbg(&client->dev, "Frame interval: %u/%u s\n",
- cp->timeperframe.numerator, cp->timeperframe.denominator);
-
- return 0;
-}
-
-static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov6650 *priv = to_ov6650(client);
- struct v4l2_captureparm *cp = &parms->parm.capture;
- struct v4l2_fract *tpf = &cp->timeperframe;
- int div, ret;
- u8 clkrc;
-
- if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (cp->extendedmode != 0)
- return -EINVAL;
-
- if (tpf->numerator == 0 || tpf->denominator == 0)
- div = 1; /* Reset to full rate */
- else
- div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator;
-
- if (div == 0)
- div = 1;
- else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK))
- div = GET_CLKRC_DIV(CLKRC_DIV_MASK);
-
- /*
- * Keep result to be used as tpf limit
- * for subseqent clock divider calculations
- */
- priv->tpf.numerator = div;
- priv->tpf.denominator = FRAME_RATE_MAX;
-
- clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max);
-
- ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK);
- if (!ret) {
- tpf->numerator = GET_CLKRC_DIV(clkrc);
- tpf->denominator = FRAME_RATE_MAX;
- }
-
- return ret;
-}
-
-/* Soft reset the camera. This has nothing to do with the RESET pin! */
-static int ov6650_reset(struct i2c_client *client)
-{
- int ret;
-
- dev_dbg(&client->dev, "reset\n");
-
- ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0);
- if (ret)
- dev_err(&client->dev,
- "An error occurred while entering soft reset!\n");
-
- return ret;
-}
-
-/* program default register values */
-static int ov6650_prog_dflt(struct i2c_client *client)
-{
- int ret;
-
- dev_dbg(&client->dev, "initializing\n");
-
- ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */
- if (!ret)
- ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER);
-
- return ret;
-}
-
-static int ov6650_video_probe(struct i2c_client *client)
-{
- u8 pidh, pidl, midh, midl;
- int ret = 0;
-
- /*
- * check and show product ID and manufacturer ID
- */
- ret = ov6650_reg_read(client, REG_PIDH, &pidh);
- if (!ret)
- ret = ov6650_reg_read(client, REG_PIDL, &pidl);
- if (!ret)
- ret = ov6650_reg_read(client, REG_MIDH, &midh);
- if (!ret)
- ret = ov6650_reg_read(client, REG_MIDL, &midl);
-
- if (ret)
- return ret;
-
- if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) {
- dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n",
- pidh, pidl);
- return -ENODEV;
- }
-
- dev_info(&client->dev,
- "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n",
- pidh, pidl, midh, midl);
-
- ret = ov6650_reset(client);
- if (!ret)
- ret = ov6650_prog_dflt(client);
-
- return ret;
-}
-
-static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
- .g_volatile_ctrl = ov6550_g_volatile_ctrl,
- .s_ctrl = ov6550_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops ov6650_core_ops = {
- .g_chip_ident = ov6650_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov6650_get_register,
- .s_register = ov6650_set_register,
-#endif
-};
-
-/* Request bus settings on camera side */
-static int ov6650_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_MASTER |
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-/* Alter bus settings on camera side */
-static int ov6650_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
- int ret;
-
- if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING);
- if (ret)
- return ret;
-
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW);
- if (ret)
- return ret;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
- ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0);
- else
- ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH);
-
- return ret;
-}
-
-static struct v4l2_subdev_video_ops ov6650_video_ops = {
- .s_stream = ov6650_s_stream,
- .g_mbus_fmt = ov6650_g_fmt,
- .s_mbus_fmt = ov6650_s_fmt,
- .try_mbus_fmt = ov6650_try_fmt,
- .enum_mbus_fmt = ov6650_enum_fmt,
- .cropcap = ov6650_cropcap,
- .g_crop = ov6650_g_crop,
- .s_crop = ov6650_s_crop,
- .g_parm = ov6650_g_parm,
- .s_parm = ov6650_s_parm,
- .g_mbus_config = ov6650_g_mbus_config,
- .s_mbus_config = ov6650_s_mbus_config,
-};
-
-static struct v4l2_subdev_ops ov6650_subdev_ops = {
- .core = &ov6650_core_ops,
- .video = &ov6650_video_ops,
-};
-
-/*
- * i2c_driver function
- */
-static int ov6650_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov6650 *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "Missing platform_data for driver\n");
- return -EINVAL;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&client->dev,
- "Failed to allocate memory for private data!\n");
- return -ENOMEM;
- }
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
- v4l2_ctrl_handler_init(&priv->hdl, 13);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
- priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
- priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
- priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
- priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
- &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
- priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
- v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
- V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
-
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error) {
- int err = priv->hdl.error;
-
- kfree(priv);
- return err;
- }
- v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
- v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
- v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
- V4L2_EXPOSURE_MANUAL, true);
-
- priv->rect.left = DEF_HSTRT << 1;
- priv->rect.top = DEF_VSTRT << 1;
- priv->rect.width = W_CIF;
- priv->rect.height = H_CIF;
- priv->half_scale = false;
- priv->code = V4L2_MBUS_FMT_YUYV8_2X8;
- priv->colorspace = V4L2_COLORSPACE_JPEG;
-
- ret = ov6650_video_probe(client);
- if (!ret)
- ret = v4l2_ctrl_handler_setup(&priv->hdl);
-
- if (ret) {
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- }
-
- return ret;
-}
-
-static int ov6650_remove(struct i2c_client *client)
-{
- struct ov6650 *priv = to_ov6650(client);
-
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id ov6650_id[] = {
- { "ov6650", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov6650_id);
-
-static struct i2c_driver ov6650_i2c_driver = {
- .driver = {
- .name = "ov6650",
- },
- .probe = ov6650_probe,
- .remove = ov6650_remove,
- .id_table = ov6650_id,
-};
-
-module_i2c_driver(ov6650_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
-MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
deleted file mode 100644
index 6d79b89b8603..000000000000
--- a/drivers/media/video/ov772x.c
+++ /dev/null
@@ -1,1126 +0,0 @@
-/*
- * ov772x Camera Driver
- *
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov7670 and soc_camera_platform driver,
- *
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/ov772x.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
-
-/*
- * register offset
- */
-#define GAIN 0x00 /* AGC - Gain control gain setting */
-#define BLUE 0x01 /* AWB - Blue channel gain setting */
-#define RED 0x02 /* AWB - Red channel gain setting */
-#define GREEN 0x03 /* AWB - Green channel gain setting */
-#define COM1 0x04 /* Common control 1 */
-#define BAVG 0x05 /* U/B Average Level */
-#define GAVG 0x06 /* Y/Gb Average Level */
-#define RAVG 0x07 /* V/R Average Level */
-#define AECH 0x08 /* Exposure Value - AEC MSBs */
-#define COM2 0x09 /* Common control 2 */
-#define PID 0x0A /* Product ID Number MSB */
-#define VER 0x0B /* Product ID Number LSB */
-#define COM3 0x0C /* Common control 3 */
-#define COM4 0x0D /* Common control 4 */
-#define COM5 0x0E /* Common control 5 */
-#define COM6 0x0F /* Common control 6 */
-#define AEC 0x10 /* Exposure Value */
-#define CLKRC 0x11 /* Internal clock */
-#define COM7 0x12 /* Common control 7 */
-#define COM8 0x13 /* Common control 8 */
-#define COM9 0x14 /* Common control 9 */
-#define COM10 0x15 /* Common control 10 */
-#define REG16 0x16 /* Register 16 */
-#define HSTART 0x17 /* Horizontal sensor size */
-#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */
-#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */
-#define VSIZE 0x1A /* Vertical sensor size */
-#define PSHFT 0x1B /* Data format - pixel delay select */
-#define MIDH 0x1C /* Manufacturer ID byte - high */
-#define MIDL 0x1D /* Manufacturer ID byte - low */
-#define LAEC 0x1F /* Fine AEC value */
-#define COM11 0x20 /* Common control 11 */
-#define BDBASE 0x22 /* Banding filter Minimum AEC value */
-#define DBSTEP 0x23 /* Banding filter Maximum Setp */
-#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */
-#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */
-#define VPT 0x26 /* AGC/AEC Fast mode operating region */
-#define REG28 0x28 /* Register 28 */
-#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */
-#define EXHCH 0x2A /* Dummy pixel insert MSB */
-#define EXHCL 0x2B /* Dummy pixel insert LSB */
-#define VOUTSIZE 0x2C /* Vertical data output size MSBs */
-#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */
-#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */
-#define YAVE 0x2F /* Y/G Channel Average value */
-#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */
-#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */
-#define HREF 0x32 /* Image start and size control */
-#define DM_LNL 0x33 /* Dummy line low 8 bits */
-#define DM_LNH 0x34 /* Dummy line high 8 bits */
-#define ADOFF_B 0x35 /* AD offset compensation value for B channel */
-#define ADOFF_R 0x36 /* AD offset compensation value for R channel */
-#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */
-#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */
-#define OFF_B 0x39 /* Analog process B channel offset value */
-#define OFF_R 0x3A /* Analog process R channel offset value */
-#define OFF_GB 0x3B /* Analog process Gb channel offset value */
-#define OFF_GR 0x3C /* Analog process Gr channel offset value */
-#define COM12 0x3D /* Common control 12 */
-#define COM13 0x3E /* Common control 13 */
-#define COM14 0x3F /* Common control 14 */
-#define COM15 0x40 /* Common control 15*/
-#define COM16 0x41 /* Common control 16 */
-#define TGT_B 0x42 /* BLC blue channel target value */
-#define TGT_R 0x43 /* BLC red channel target value */
-#define TGT_GB 0x44 /* BLC Gb channel target value */
-#define TGT_GR 0x45 /* BLC Gr channel target value */
-/* for ov7720 */
-#define LCC0 0x46 /* Lens correction control 0 */
-#define LCC1 0x47 /* Lens correction option 1 - X coordinate */
-#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */
-#define LCC3 0x49 /* Lens correction option 3 */
-#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */
-#define LCC5 0x4B /* Lens correction option 5 */
-#define LCC6 0x4C /* Lens correction option 6 */
-/* for ov7725 */
-#define LC_CTR 0x46 /* Lens correction control */
-#define LC_XC 0x47 /* X coordinate of lens correction center relative */
-#define LC_YC 0x48 /* Y coordinate of lens correction center relative */
-#define LC_COEF 0x49 /* Lens correction coefficient */
-#define LC_RADI 0x4A /* Lens correction radius */
-#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
-#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
-
-#define FIXGAIN 0x4D /* Analog fix gain amplifer */
-#define AREF0 0x4E /* Sensor reference control */
-#define AREF1 0x4F /* Sensor reference current control */
-#define AREF2 0x50 /* Analog reference control */
-#define AREF3 0x51 /* ADC reference control */
-#define AREF4 0x52 /* ADC reference control */
-#define AREF5 0x53 /* ADC reference control */
-#define AREF6 0x54 /* Analog reference control */
-#define AREF7 0x55 /* Analog reference control */
-#define UFIX 0x60 /* U channel fixed value output */
-#define VFIX 0x61 /* V channel fixed value output */
-#define AWBB_BLK 0x62 /* AWB option for advanced AWB */
-#define AWB_CTRL0 0x63 /* AWB control byte 0 */
-#define DSP_CTRL1 0x64 /* DSP control byte 1 */
-#define DSP_CTRL2 0x65 /* DSP control byte 2 */
-#define DSP_CTRL3 0x66 /* DSP control byte 3 */
-#define DSP_CTRL4 0x67 /* DSP control byte 4 */
-#define AWB_BIAS 0x68 /* AWB BLC level clip */
-#define AWB_CTRL1 0x69 /* AWB control 1 */
-#define AWB_CTRL2 0x6A /* AWB control 2 */
-#define AWB_CTRL3 0x6B /* AWB control 3 */
-#define AWB_CTRL4 0x6C /* AWB control 4 */
-#define AWB_CTRL5 0x6D /* AWB control 5 */
-#define AWB_CTRL6 0x6E /* AWB control 6 */
-#define AWB_CTRL7 0x6F /* AWB control 7 */
-#define AWB_CTRL8 0x70 /* AWB control 8 */
-#define AWB_CTRL9 0x71 /* AWB control 9 */
-#define AWB_CTRL10 0x72 /* AWB control 10 */
-#define AWB_CTRL11 0x73 /* AWB control 11 */
-#define AWB_CTRL12 0x74 /* AWB control 12 */
-#define AWB_CTRL13 0x75 /* AWB control 13 */
-#define AWB_CTRL14 0x76 /* AWB control 14 */
-#define AWB_CTRL15 0x77 /* AWB control 15 */
-#define AWB_CTRL16 0x78 /* AWB control 16 */
-#define AWB_CTRL17 0x79 /* AWB control 17 */
-#define AWB_CTRL18 0x7A /* AWB control 18 */
-#define AWB_CTRL19 0x7B /* AWB control 19 */
-#define AWB_CTRL20 0x7C /* AWB control 20 */
-#define AWB_CTRL21 0x7D /* AWB control 21 */
-#define GAM1 0x7E /* Gamma Curve 1st segment input end point */
-#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */
-#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */
-#define GAM4 0x81 /* Gamma Curve 4th segment input end point */
-#define GAM5 0x82 /* Gamma Curve 5th segment input end point */
-#define GAM6 0x83 /* Gamma Curve 6th segment input end point */
-#define GAM7 0x84 /* Gamma Curve 7th segment input end point */
-#define GAM8 0x85 /* Gamma Curve 8th segment input end point */
-#define GAM9 0x86 /* Gamma Curve 9th segment input end point */
-#define GAM10 0x87 /* Gamma Curve 10th segment input end point */
-#define GAM11 0x88 /* Gamma Curve 11th segment input end point */
-#define GAM12 0x89 /* Gamma Curve 12th segment input end point */
-#define GAM13 0x8A /* Gamma Curve 13th segment input end point */
-#define GAM14 0x8B /* Gamma Curve 14th segment input end point */
-#define GAM15 0x8C /* Gamma Curve 15th segment input end point */
-#define SLOP 0x8D /* Gamma curve highest segment slope */
-#define DNSTH 0x8E /* De-noise threshold */
-#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */
-#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */
-#define DNSOFF 0x91 /* Auto De-noise threshold control */
-#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */
-#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */
-#define MTX1 0x94 /* Matrix coefficient 1 */
-#define MTX2 0x95 /* Matrix coefficient 2 */
-#define MTX3 0x96 /* Matrix coefficient 3 */
-#define MTX4 0x97 /* Matrix coefficient 4 */
-#define MTX5 0x98 /* Matrix coefficient 5 */
-#define MTX6 0x99 /* Matrix coefficient 6 */
-#define MTX_CTRL 0x9A /* Matrix control */
-#define BRIGHT 0x9B /* Brightness control */
-#define CNTRST 0x9C /* Contrast contrast */
-#define CNTRST_CTRL 0x9D /* Contrast contrast center */
-#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */
-#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */
-#define SCAL0 0xA0 /* Scaling control 0 */
-#define SCAL1 0xA1 /* Scaling control 1 */
-#define SCAL2 0xA2 /* Scaling control 2 */
-#define FIFODLYM 0xA3 /* FIFO manual mode delay control */
-#define FIFODLYA 0xA4 /* FIFO auto mode delay control */
-#define SDE 0xA6 /* Special digital effect control */
-#define USAT 0xA7 /* U component saturation control */
-#define VSAT 0xA8 /* V component saturation control */
-/* for ov7720 */
-#define HUE0 0xA9 /* Hue control 0 */
-#define HUE1 0xAA /* Hue control 1 */
-/* for ov7725 */
-#define HUECOS 0xA9 /* Cosine value */
-#define HUESIN 0xAA /* Sine value */
-
-#define SIGN 0xAB /* Sign bit for Hue and contrast */
-#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */
-
-/*
- * register detail
- */
-
-/* COM2 */
-#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */
- /* Output drive capability */
-#define OCAP_1x 0x00 /* 1x */
-#define OCAP_2x 0x01 /* 2x */
-#define OCAP_3x 0x02 /* 3x */
-#define OCAP_4x 0x03 /* 4x */
-
-/* COM3 */
-#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
-#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
-
-#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
-#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
-#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
-#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
-#define SWAP_ML 0x08 /* Swap output MSB/LSB */
- /* Tri-state option for output clock */
-#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */
- /* 1: No tri-state at this period */
- /* Tri-state option for output data */
-#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */
- /* 1: No tri-state at this period */
-#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */
-
-/* COM4 */
- /* PLL frequency control */
-#define PLL_BYPASS 0x00 /* 00: Bypass PLL */
-#define PLL_4x 0x40 /* 01: PLL 4x */
-#define PLL_6x 0x80 /* 10: PLL 6x */
-#define PLL_8x 0xc0 /* 11: PLL 8x */
- /* AEC evaluate window */
-#define AEC_FULL 0x00 /* 00: Full window */
-#define AEC_1p2 0x10 /* 01: 1/2 window */
-#define AEC_1p4 0x20 /* 10: 1/4 window */
-#define AEC_2p3 0x30 /* 11: Low 2/3 window */
-
-/* COM5 */
-#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */
-#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */
- /* Auto frame rate max rate control */
-#define AFR_NO_RATE 0x00 /* No reduction of frame rate */
-#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */
-#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */
-#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */
- /* Auto frame rate active point control */
-#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */
-#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */
-#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
-#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
- /* AEC max step control */
-#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
- /* 1 : No limit to AEC increase step */
-
-/* COM7 */
- /* SCCB Register Reset */
-#define SCCB_RESET 0x80 /* 0 : No change */
- /* 1 : Resets all registers to default */
- /* Resolution selection */
-#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */
-#define SLCT_VGA 0x00 /* 0 : VGA */
-#define SLCT_QVGA 0x40 /* 1 : QVGA */
-#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
- /* RGB output format control */
-#define FMT_MASK 0x0c /* Mask of color format */
-#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
-#define FMT_RGB565 0x04 /* 01 : RGB 565 */
-#define FMT_RGB555 0x08 /* 10 : RGB 555 */
-#define FMT_RGB444 0x0c /* 11 : RGB 444 */
- /* Output format control */
-#define OFMT_MASK 0x03 /* Mask of output format */
-#define OFMT_YUV 0x00 /* 00 : YUV */
-#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
-#define OFMT_RGB 0x02 /* 10 : RGB */
-#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */
-
-/* COM8 */
-#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */
- /* AEC Setp size limit */
-#define UNLMT_STEP 0x40 /* 0 : Step size is limited */
- /* 1 : Unlimited step size */
-#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */
-#define AEC_BND 0x10 /* Enable AEC below banding value */
-#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */
-#define AGC_ON 0x04 /* AGC Enable */
-#define AWB_ON 0x02 /* AWB Enable */
-#define AEC_ON 0x01 /* AEC Enable */
-
-/* COM9 */
-#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */
- /* Automatic gain ceiling - maximum AGC value */
-#define GAIN_2x 0x00 /* 000 : 2x */
-#define GAIN_4x 0x10 /* 001 : 4x */
-#define GAIN_8x 0x20 /* 010 : 8x */
-#define GAIN_16x 0x30 /* 011 : 16x */
-#define GAIN_32x 0x40 /* 100 : 32x */
-#define GAIN_64x 0x50 /* 101 : 64x */
-#define GAIN_128x 0x60 /* 110 : 128x */
-#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
-#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
-
-/* COM11 */
-#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */
-#define SGLF_TRIG 0x01 /* Single frame transfer trigger */
-
-/* EXHCH */
-#define VSIZE_LSB 0x04 /* Vertical data output size LSB */
-
-/* DSP_CTRL1 */
-#define FIFO_ON 0x80 /* FIFO enable/disable selection */
-#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */
-#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */
-#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */
-#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */
-#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */
-#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */
-#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */
-
-/* DSP_CTRL3 */
-#define UV_MASK 0x80 /* UV output sequence option */
-#define UV_ON 0x80 /* ON */
-#define UV_OFF 0x00 /* OFF */
-#define CBAR_MASK 0x20 /* DSP Color bar mask */
-#define CBAR_ON 0x20 /* ON */
-#define CBAR_OFF 0x00 /* OFF */
-
-/* HSTART */
-#define HST_VGA 0x23
-#define HST_QVGA 0x3F
-
-/* HSIZE */
-#define HSZ_VGA 0xA0
-#define HSZ_QVGA 0x50
-
-/* VSTART */
-#define VST_VGA 0x07
-#define VST_QVGA 0x03
-
-/* VSIZE */
-#define VSZ_VGA 0xF0
-#define VSZ_QVGA 0x78
-
-/* HOUTSIZE */
-#define HOSZ_VGA 0xA0
-#define HOSZ_QVGA 0x50
-
-/* VOUTSIZE */
-#define VOSZ_VGA 0xF0
-#define VOSZ_QVGA 0x78
-
-/* DSPAUTO (DSP Auto Function ON/OFF Control) */
-#define AWB_ACTRL 0x80 /* AWB auto threshold control */
-#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */
-#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */
-#define UV_ACTRL 0x10 /* UV adjust auto slope control */
-#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */
-#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */
-
-/*
- * ID
- */
-#define OV7720 0x7720
-#define OV7725 0x7721
-#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF))
-
-/*
- * struct
- */
-struct regval_list {
- unsigned char reg_num;
- unsigned char value;
-};
-
-struct ov772x_color_format {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
- u8 dsp3;
- u8 com3;
- u8 com7;
-};
-
-struct ov772x_win_size {
- char *name;
- __u32 width;
- __u32 height;
- unsigned char com7_bit;
- const struct regval_list *regs;
-};
-
-struct ov772x_priv {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct ov772x_camera_info *info;
- const struct ov772x_color_format *cfmt;
- const struct ov772x_win_size *win;
- int model;
- unsigned short flag_vflip:1;
- unsigned short flag_hflip:1;
- /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
- unsigned short band_filter;
-};
-
-#define ENDMARKER { 0xff, 0xff }
-
-/*
- * register setting for window size
- */
-static const struct regval_list ov772x_qvga_regs[] = {
- { HSTART, HST_QVGA },
- { HSIZE, HSZ_QVGA },
- { VSTART, VST_QVGA },
- { VSIZE, VSZ_QVGA },
- { HOUTSIZE, HOSZ_QVGA },
- { VOUTSIZE, VOSZ_QVGA },
- ENDMARKER,
-};
-
-static const struct regval_list ov772x_vga_regs[] = {
- { HSTART, HST_VGA },
- { HSIZE, HSZ_VGA },
- { VSTART, VST_VGA },
- { VSIZE, VSZ_VGA },
- { HOUTSIZE, HOSZ_VGA },
- { VOUTSIZE, VOSZ_VGA },
- ENDMARKER,
-};
-
-/*
- * supported color format list
- */
-static const struct ov772x_color_format ov772x_cfmts[] = {
- {
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = 0x0,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
- },
- {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = UV_ON,
- .com3 = SWAP_YUV,
- .com7 = OFMT_YUV,
- },
- {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
- .colorspace = V4L2_COLORSPACE_JPEG,
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = OFMT_YUV,
- },
- {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB555 | OFMT_RGB,
- },
- {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = FMT_RGB555 | OFMT_RGB,
- },
- {
- .code = V4L2_MBUS_FMT_RGB565_2X8_LE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .com3 = SWAP_RGB,
- .com7 = FMT_RGB565 | OFMT_RGB,
- },
- {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .dsp3 = 0x0,
- .com3 = 0x0,
- .com7 = FMT_RGB565 | OFMT_RGB,
- },
-};
-
-
-/*
- * window size list
- */
-#define VGA_WIDTH 640
-#define VGA_HEIGHT 480
-#define QVGA_WIDTH 320
-#define QVGA_HEIGHT 240
-#define MAX_WIDTH VGA_WIDTH
-#define MAX_HEIGHT VGA_HEIGHT
-
-static const struct ov772x_win_size ov772x_win_vga = {
- .name = "VGA",
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- .com7_bit = SLCT_VGA,
- .regs = ov772x_vga_regs,
-};
-
-static const struct ov772x_win_size ov772x_win_qvga = {
- .name = "QVGA",
- .width = QVGA_WIDTH,
- .height = QVGA_HEIGHT,
- .com7_bit = SLCT_QVGA,
- .regs = ov772x_qvga_regs,
-};
-
-/*
- * general function
- */
-
-static struct ov772x_priv *to_ov772x(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct ov772x_priv,
- subdev);
-}
-
-static int ov772x_write_array(struct i2c_client *client,
- const struct regval_list *vals)
-{
- while (vals->reg_num != 0xff) {
- int ret = i2c_smbus_write_byte_data(client,
- vals->reg_num,
- vals->value);
- if (ret < 0)
- return ret;
- vals++;
- }
- return 0;
-}
-
-static int ov772x_mask_set(struct i2c_client *client,
- u8 command,
- u8 mask,
- u8 set)
-{
- s32 val = i2c_smbus_read_byte_data(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return i2c_smbus_write_byte_data(client, command, val);
-}
-
-static int ov772x_reset(struct i2c_client *client)
-{
- int ret = i2c_smbus_write_byte_data(client, COM7, SCCB_RESET);
- msleep(1);
- return ret;
-}
-
-/*
- * soc_camera_ops function
- */
-
-static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
- if (!enable) {
- ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
- return 0;
- }
-
- if (!priv->win || !priv->cfmt) {
- dev_err(&client->dev, "norm or win select error\n");
- return -EPERM;
- }
-
- ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
-
- dev_dbg(&client->dev, "format %d, win %s\n",
- priv->cfmt->code, priv->win->name);
-
- return 0;
-}
-
-static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov772x_priv *priv = container_of(ctrl->handler,
- struct ov772x_priv, hdl);
- struct v4l2_subdev *sd = &priv->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
- u8 val;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- val = ctrl->val ? VFLIP_IMG : 0x00;
- priv->flag_vflip = ctrl->val;
- if (priv->info->flags & OV772X_FLAG_VFLIP)
- val ^= VFLIP_IMG;
- return ov772x_mask_set(client, COM3, VFLIP_IMG, val);
- case V4L2_CID_HFLIP:
- val = ctrl->val ? HFLIP_IMG : 0x00;
- priv->flag_hflip = ctrl->val;
- if (priv->info->flags & OV772X_FLAG_HFLIP)
- val ^= HFLIP_IMG;
- return ov772x_mask_set(client, COM3, HFLIP_IMG, val);
- case V4L2_CID_BAND_STOP_FILTER:
- if (!ctrl->val) {
- /* Switch the filter off, it is on now */
- ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
- if (!ret)
- ret = ov772x_mask_set(client, COM8,
- BNDF_ON_OFF, 0);
- } else {
- /* Switch the filter on, set AEC low limit */
- val = 256 - ctrl->val;
- ret = ov772x_mask_set(client, COM8,
- BNDF_ON_OFF, BNDF_ON_OFF);
- if (!ret)
- ret = ov772x_mask_set(client, BDBASE,
- 0xff, val);
- }
- if (!ret)
- priv->band_filter = ctrl->val;
- return ret;
- }
-
- return -EINVAL;
-}
-
-static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
- id->ident = priv->model;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov772x_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- reg->size = 1;
- if (reg->reg > 0xff)
- return -EINVAL;
-
- ret = i2c_smbus_read_byte_data(client, reg->reg);
- if (ret < 0)
- return ret;
-
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int ov772x_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg > 0xff ||
- reg->val > 0xff)
- return -EINVAL;
-
- return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
-}
-#endif
-
-static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
-{
- __u32 diff;
- const struct ov772x_win_size *win;
-
- /* default is QVGA */
- diff = abs(width - ov772x_win_qvga.width) +
- abs(height - ov772x_win_qvga.height);
- win = &ov772x_win_qvga;
-
- /* VGA */
- if (diff >
- abs(width - ov772x_win_vga.width) +
- abs(height - ov772x_win_vga.height))
- win = &ov772x_win_vga;
-
- return win;
-}
-
-static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height,
- enum v4l2_mbus_pixelcode code)
-{
- struct ov772x_priv *priv = to_ov772x(client);
- int ret = -EINVAL;
- u8 val;
- int i;
-
- /*
- * select format
- */
- priv->cfmt = NULL;
- for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
- if (code == ov772x_cfmts[i].code) {
- priv->cfmt = ov772x_cfmts + i;
- break;
- }
- }
- if (!priv->cfmt)
- goto ov772x_set_fmt_error;
-
- /*
- * select win
- */
- priv->win = ov772x_select_win(*width, *height);
-
- /*
- * reset hardware
- */
- ov772x_reset(client);
-
- /*
- * Edge Ctrl
- */
- if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) {
-
- /*
- * Manual Edge Control Mode
- *
- * Edge auto strength bit is set by default.
- * Remove it when manual mode.
- */
-
- ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK,
- priv->info->edgectrl.threshold);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK,
- priv->info->edgectrl.strength);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) {
- /*
- * Auto Edge Control Mode
- *
- * set upper and lower limit
- */
- ret = ov772x_mask_set(client,
- EDGE_UPPER, OV772X_EDGE_UPPER_MASK,
- priv->info->edgectrl.upper);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- ret = ov772x_mask_set(client,
- EDGE_LOWER, OV772X_EDGE_LOWER_MASK,
- priv->info->edgectrl.lower);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- /*
- * set size format
- */
- ret = ov772x_write_array(client, priv->win->regs);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /*
- * set DSP_CTRL3
- */
- val = priv->cfmt->dsp3;
- if (val) {
- ret = ov772x_mask_set(client,
- DSP_CTRL3, UV_MASK, val);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- /*
- * set COM3
- */
- val = priv->cfmt->com3;
- if (priv->info->flags & OV772X_FLAG_VFLIP)
- val |= VFLIP_IMG;
- if (priv->info->flags & OV772X_FLAG_HFLIP)
- val |= HFLIP_IMG;
- if (priv->flag_vflip)
- val ^= VFLIP_IMG;
- if (priv->flag_hflip)
- val ^= HFLIP_IMG;
-
- ret = ov772x_mask_set(client,
- COM3, SWAP_MASK | IMG_MASK, val);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /*
- * set COM7
- */
- val = priv->win->com7_bit | priv->cfmt->com7;
- ret = ov772x_mask_set(client,
- COM7, SLCT_MASK | FMT_MASK | OFMT_MASK,
- val);
- if (ret < 0)
- goto ov772x_set_fmt_error;
-
- /*
- * set COM8
- */
- if (priv->band_filter) {
- ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
- if (!ret)
- ret = ov772x_mask_set(client, BDBASE,
- 0xff, 256 - priv->band_filter);
- if (ret < 0)
- goto ov772x_set_fmt_error;
- }
-
- *width = priv->win->width;
- *height = priv->win->height;
-
- return ret;
-
-ov772x_set_fmt_error:
-
- ov772x_reset(client);
- priv->win = NULL;
- priv->cfmt = NULL;
-
- return ret;
-}
-
-static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = VGA_WIDTH;
- a->c.height = VGA_HEIGHT;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = VGA_WIDTH;
- a->bounds.height = VGA_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int ov772x_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
-
- if (!priv->win || !priv->cfmt) {
- priv->cfmt = &ov772x_cfmts[0];
- priv->win = ov772x_select_win(VGA_WIDTH, VGA_HEIGHT);
- }
-
- mf->width = priv->win->width;
- mf->height = priv->win->height;
- mf->code = priv->cfmt->code;
- mf->colorspace = priv->cfmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static int ov772x_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
- int ret = ov772x_set_params(client, &mf->width, &mf->height,
- mf->code);
-
- if (!ret)
- mf->colorspace = priv->cfmt->colorspace;
-
- return ret;
-}
-
-static int ov772x_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev);
- const struct ov772x_win_size *win;
- int i;
-
- /*
- * select suitable win
- */
- win = ov772x_select_win(mf->width, mf->height);
-
- mf->width = win->width;
- mf->height = win->height;
- mf->field = V4L2_FIELD_NONE;
-
- for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++)
- if (mf->code == ov772x_cfmts[i].code)
- break;
-
- if (i == ARRAY_SIZE(ov772x_cfmts)) {
- /* Unsupported format requested. Propose either */
- if (priv->cfmt) {
- /* the current one or */
- mf->colorspace = priv->cfmt->colorspace;
- mf->code = priv->cfmt->code;
- } else {
- /* the default one */
- mf->colorspace = ov772x_cfmts[0].colorspace;
- mf->code = ov772x_cfmts[0].code;
- }
- } else {
- /* Also return the colorspace */
- mf->colorspace = ov772x_cfmts[i].colorspace;
- }
-
- return 0;
-}
-
-static int ov772x_video_probe(struct i2c_client *client)
-{
- struct ov772x_priv *priv = to_ov772x(client);
- u8 pid, ver;
- const char *devname;
-
- /*
- * check and show product ID and manufacturer ID
- */
- pid = i2c_smbus_read_byte_data(client, PID);
- ver = i2c_smbus_read_byte_data(client, VER);
-
- switch (VERSION(pid, ver)) {
- case OV7720:
- devname = "ov7720";
- priv->model = V4L2_IDENT_OV7720;
- break;
- case OV7725:
- devname = "ov7725";
- priv->model = V4L2_IDENT_OV7725;
- break;
- default:
- dev_err(&client->dev,
- "Product ID error %x:%x\n", pid, ver);
- return -ENODEV;
- }
-
- dev_info(&client->dev,
- "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
- devname,
- pid,
- ver,
- i2c_smbus_read_byte_data(client, MIDH),
- i2c_smbus_read_byte_data(client, MIDL));
- return v4l2_ctrl_handler_setup(&priv->hdl);
-}
-
-static const struct v4l2_ctrl_ops ov772x_ctrl_ops = {
- .s_ctrl = ov772x_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
- .g_chip_ident = ov772x_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov772x_g_register,
- .s_register = ov772x_s_register,
-#endif
-};
-
-static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(ov772x_cfmts))
- return -EINVAL;
-
- *code = ov772x_cfmts[index].code;
- return 0;
-}
-
-static int ov772x_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
- .s_stream = ov772x_s_stream,
- .g_mbus_fmt = ov772x_g_fmt,
- .s_mbus_fmt = ov772x_s_fmt,
- .try_mbus_fmt = ov772x_try_fmt,
- .cropcap = ov772x_cropcap,
- .g_crop = ov772x_g_crop,
- .enum_mbus_fmt = ov772x_enum_fmt,
- .g_mbus_config = ov772x_g_mbus_config,
-};
-
-static struct v4l2_subdev_ops ov772x_subdev_ops = {
- .core = &ov772x_subdev_core_ops,
- .video = &ov772x_subdev_video_ops,
-};
-
-/*
- * i2c_driver function
- */
-
-static int ov772x_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov772x_priv *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- int ret;
-
- if (!icl || !icl->priv) {
- dev_err(&client->dev, "OV772X: missing platform data!\n");
- return -EINVAL;
- }
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&adapter->dev,
- "I2C-Adapter doesn't support "
- "I2C_FUNC_SMBUS_BYTE_DATA\n");
- return -EIO;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = icl->priv;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
- v4l2_ctrl_handler_init(&priv->hdl, 3);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
- V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0);
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error) {
- int err = priv->hdl.error;
-
- kfree(priv);
- return err;
- }
-
- ret = ov772x_video_probe(client);
- if (ret) {
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- }
-
- return ret;
-}
-
-static int ov772x_remove(struct i2c_client *client)
-{
- struct ov772x_priv *priv = to_ov772x(client);
-
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id ov772x_id[] = {
- { "ov772x", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov772x_id);
-
-static struct i2c_driver ov772x_i2c_driver = {
- .driver = {
- .name = "ov772x",
- },
- .probe = ov772x_probe,
- .remove = ov772x_remove,
- .id_table = ov772x_id,
-};
-
-module_i2c_driver(ov772x_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for ov772x");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
deleted file mode 100644
index 9ed4ba4236c4..000000000000
--- a/drivers/media/video/ov9640.c
+++ /dev/null
@@ -1,746 +0,0 @@
-/*
- * OmniVision OV96xx Camera Driver
- *
- * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com>
- *
- * Based on ov772x camera driver:
- *
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov7670 and soc_camera_platform driver,
- *
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ctrls.h>
-
-#include "ov9640.h"
-
-#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev)
-
-/* default register setup */
-static const struct ov9640_reg ov9640_regs_dflt[] = {
- { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP },
- { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS |
- OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN },
- { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) },
- { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD },
- { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY },
- { OV9640_COM16, OV9640_COM16_RB_AVG },
-
- /* Gamma curve P */
- { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 },
- { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 },
- { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 },
- { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 },
-
- /* Gamma curve T */
- { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 },
- { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 },
- { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e },
- { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 },
-};
-
-/* Configurations
- * NOTE: for YUV, alter the following registers:
- * COM12 |= OV9640_COM12_YUV_AVG
- *
- * for RGB, alter the following registers:
- * COM7 |= OV9640_COM7_RGB
- * COM13 |= OV9640_COM13_RGB_AVG
- * COM15 |= proper RGB color encoding mode
- */
-static const struct ov9640_reg ov9640_regs_qqcif[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) },
- { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
- { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
- { OV9640_COM7, OV9640_COM7_QCIF },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_qqvga[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
- { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP },
- { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
- { OV9640_COM7, OV9640_COM7_QVGA },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_qcif[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) },
- { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
- { OV9640_COM7, OV9640_COM7_QCIF },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_qvga[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
- { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD },
- { OV9640_COM7, OV9640_COM7_QVGA },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_cif[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) },
- { OV9640_COM3, OV9640_COM3_VP },
- { OV9640_COM7, OV9640_COM7_CIF },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_vga[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
- { OV9640_COM3, OV9640_COM3_VP },
- { OV9640_COM7, OV9640_COM7_VGA },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_sxga[] = {
- { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) },
- { OV9640_COM3, OV9640_COM3_VP },
- { OV9640_COM7, 0 },
- { OV9640_COM12, OV9640_COM12_RSVD },
- { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN },
- { OV9640_COM15, OV9640_COM15_OR_10F0 },
-};
-
-static const struct ov9640_reg ov9640_regs_yuv[] = {
- { OV9640_MTX1, 0x58 },
- { OV9640_MTX2, 0x48 },
- { OV9640_MTX3, 0x10 },
- { OV9640_MTX4, 0x28 },
- { OV9640_MTX5, 0x48 },
- { OV9640_MTX6, 0x70 },
- { OV9640_MTX7, 0x40 },
- { OV9640_MTX8, 0x40 },
- { OV9640_MTX9, 0x40 },
- { OV9640_MTXS, 0x0f },
-};
-
-static const struct ov9640_reg ov9640_regs_rgb[] = {
- { OV9640_MTX1, 0x71 },
- { OV9640_MTX2, 0x3e },
- { OV9640_MTX3, 0x0c },
- { OV9640_MTX4, 0x33 },
- { OV9640_MTX5, 0x72 },
- { OV9640_MTX6, 0x00 },
- { OV9640_MTX7, 0x2b },
- { OV9640_MTX8, 0x66 },
- { OV9640_MTX9, 0xd2 },
- { OV9640_MTXS, 0x65 },
-};
-
-static enum v4l2_mbus_pixelcode ov9640_codes[] = {
- V4L2_MBUS_FMT_UYVY8_2X8,
- V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
- V4L2_MBUS_FMT_RGB565_2X8_LE,
-};
-
-/* read a register */
-static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val)
-{
- int ret;
- u8 data = reg;
- struct i2c_msg msg = {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = &data,
- };
-
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret < 0)
- goto err;
-
- msg.flags = I2C_M_RD;
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret < 0)
- goto err;
-
- *val = data;
- return 0;
-
-err:
- dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg);
- return ret;
-}
-
-/* write a register */
-static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val)
-{
- int ret;
- u8 _val;
- unsigned char data[2] = { reg, val };
- struct i2c_msg msg = {
- .addr = client->addr,
- .flags = 0,
- .len = 2,
- .buf = data,
- };
-
- ret = i2c_transfer(client->adapter, &msg, 1);
- if (ret < 0) {
- dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg);
- return ret;
- }
-
- /* we have to read the register back ... no idea why, maybe HW bug */
- ret = ov9640_reg_read(client, reg, &_val);
- if (ret)
- dev_err(&client->dev,
- "Failed reading back register 0x%02x!\n", reg);
-
- return 0;
-}
-
-
-/* Read a register, alter its bits, write it back */
-static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset)
-{
- u8 val;
- int ret;
-
- ret = ov9640_reg_read(client, reg, &val);
- if (ret) {
- dev_err(&client->dev,
- "[Read]-Modify-Write of register %02x failed!\n", reg);
- return val;
- }
-
- val |= set;
- val &= ~unset;
-
- ret = ov9640_reg_write(client, reg, val);
- if (ret)
- dev_err(&client->dev,
- "Read-Modify-[Write] of register %02x failed!\n", reg);
-
- return ret;
-}
-
-/* Soft reset the camera. This has nothing to do with the RESET pin! */
-static int ov9640_reset(struct i2c_client *client)
-{
- int ret;
-
- ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET);
- if (ret)
- dev_err(&client->dev,
- "An error occurred while entering soft reset!\n");
-
- return ret;
-}
-
-/* Start/Stop streaming from the device */
-static int ov9640_s_stream(struct v4l2_subdev *sd, int enable)
-{
- return 0;
-}
-
-/* Set status of additional camera capabilities */
-static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl);
- struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- return ov9640_reg_rmw(client, OV9640_MVFP,
- OV9640_MVFP_V, 0);
- return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V);
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- return ov9640_reg_rmw(client, OV9640_MVFP,
- OV9640_MVFP_H, 0);
- return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H);
- }
- return -EINVAL;
-}
-
-/* Get chip identification */
-static int ov9640_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- id->ident = priv->model;
- id->revision = priv->revision;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov9640_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 & ~0xff)
- return -EINVAL;
-
- reg->size = 1;
-
- ret = ov9640_reg_read(client, reg->reg, &val);
- if (ret)
- return ret;
-
- reg->val = (__u64)val;
-
- return 0;
-}
-
-static int ov9640_set_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg & ~0xff || reg->val & ~0xff)
- return -EINVAL;
-
- return ov9640_reg_write(client, reg->reg, reg->val);
-}
-#endif
-
-/* select nearest higher resolution for capture */
-static void ov9640_res_roundup(u32 *width, u32 *height)
-{
- int i;
- enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA };
- int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 };
- int res_y[] = { 72, 120, 144, 240, 288, 480, 960 };
-
- for (i = 0; i < ARRAY_SIZE(res_x); i++) {
- if (res_x[i] >= *width && res_y[i] >= *height) {
- *width = res_x[i];
- *height = res_y[i];
- return;
- }
- }
-
- *width = res_x[SXGA];
- *height = res_y[SXGA];
-}
-
-/* Prepare necessary register changes depending on color encoding */
-static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code,
- struct ov9640_reg_alt *alt)
-{
- switch (code) {
- default:
- case V4L2_MBUS_FMT_UYVY8_2X8:
- alt->com12 = OV9640_COM12_YUV_AVG;
- alt->com13 = OV9640_COM13_Y_DELAY_EN |
- OV9640_COM13_YUV_DLY(0x01);
- break;
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- alt->com7 = OV9640_COM7_RGB;
- alt->com13 = OV9640_COM13_RGB_AVG;
- alt->com15 = OV9640_COM15_RGB_555;
- break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- alt->com7 = OV9640_COM7_RGB;
- alt->com13 = OV9640_COM13_RGB_AVG;
- alt->com15 = OV9640_COM15_RGB_565;
- break;
- };
-}
-
-/* Setup registers according to resolution and color encoding */
-static int ov9640_write_regs(struct i2c_client *client, u32 width,
- enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts)
-{
- const struct ov9640_reg *ov9640_regs, *matrix_regs;
- int ov9640_regs_len, matrix_regs_len;
- int i, ret;
- u8 val;
-
- /* select register configuration for given resolution */
- switch (width) {
- case W_QQCIF:
- ov9640_regs = ov9640_regs_qqcif;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif);
- break;
- case W_QQVGA:
- ov9640_regs = ov9640_regs_qqvga;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga);
- break;
- case W_QCIF:
- ov9640_regs = ov9640_regs_qcif;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif);
- break;
- case W_QVGA:
- ov9640_regs = ov9640_regs_qvga;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga);
- break;
- case W_CIF:
- ov9640_regs = ov9640_regs_cif;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif);
- break;
- case W_VGA:
- ov9640_regs = ov9640_regs_vga;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga);
- break;
- case W_SXGA:
- ov9640_regs = ov9640_regs_sxga;
- ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga);
- break;
- default:
- dev_err(&client->dev, "Failed to select resolution!\n");
- return -EINVAL;
- }
-
- /* select color matrix configuration for given color encoding */
- if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
- matrix_regs = ov9640_regs_yuv;
- matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv);
- } else {
- matrix_regs = ov9640_regs_rgb;
- matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb);
- }
-
- /* write register settings into the module */
- for (i = 0; i < ov9640_regs_len; i++) {
- val = ov9640_regs[i].val;
-
- switch (ov9640_regs[i].reg) {
- case OV9640_COM7:
- val |= alts->com7;
- break;
- case OV9640_COM12:
- val |= alts->com12;
- break;
- case OV9640_COM13:
- val |= alts->com13;
- break;
- case OV9640_COM15:
- val |= alts->com15;
- break;
- }
-
- ret = ov9640_reg_write(client, ov9640_regs[i].reg, val);
- if (ret)
- return ret;
- }
-
- /* write color matrix configuration into the module */
- for (i = 0; i < matrix_regs_len; i++) {
- ret = ov9640_reg_write(client, matrix_regs[i].reg,
- matrix_regs[i].val);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-/* program default register values */
-static int ov9640_prog_dflt(struct i2c_client *client)
-{
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) {
- ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg,
- ov9640_regs_dflt[i].val);
- if (ret)
- return ret;
- }
-
- /* wait for the changes to actually happen, 140ms are not enough yet */
- mdelay(150);
-
- return 0;
-}
-
-/* set the format we will capture in */
-static int ov9640_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct ov9640_reg_alt alts = {0};
- enum v4l2_colorspace cspace;
- enum v4l2_mbus_pixelcode code = mf->code;
- int ret;
-
- ov9640_res_roundup(&mf->width, &mf->height);
- ov9640_alter_regs(mf->code, &alts);
-
- ov9640_reset(client);
-
- ret = ov9640_prog_dflt(client);
- if (ret)
- return ret;
-
- switch (code) {
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- cspace = V4L2_COLORSPACE_SRGB;
- break;
- default:
- code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- cspace = V4L2_COLORSPACE_JPEG;
- }
-
- ret = ov9640_write_regs(client, mf->width, code, &alts);
- if (!ret) {
- mf->code = code;
- mf->colorspace = cspace;
- }
-
- return ret;
-}
-
-static int ov9640_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- ov9640_res_roundup(&mf->width, &mf->height);
-
- mf->field = V4L2_FIELD_NONE;
-
- switch (mf->code) {
- case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE:
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- mf->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- default:
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- case V4L2_MBUS_FMT_UYVY8_2X8:
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- }
-
- return 0;
-}
-
-static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(ov9640_codes))
- return -EINVAL;
-
- *code = ov9640_codes[index];
- return 0;
-}
-
-static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = W_SXGA;
- a->c.height = H_SXGA;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = W_SXGA;
- a->bounds.height = H_SXGA;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int ov9640_video_probe(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
- u8 pid, ver, midh, midl;
- const char *devname;
- int ret = 0;
-
- /*
- * check and show product ID and manufacturer ID
- */
-
- ret = ov9640_reg_read(client, OV9640_PID, &pid);
- if (!ret)
- ret = ov9640_reg_read(client, OV9640_VER, &ver);
- if (!ret)
- ret = ov9640_reg_read(client, OV9640_MIDH, &midh);
- if (!ret)
- ret = ov9640_reg_read(client, OV9640_MIDL, &midl);
- if (ret)
- return ret;
-
- switch (VERSION(pid, ver)) {
- case OV9640_V2:
- devname = "ov9640";
- priv->model = V4L2_IDENT_OV9640;
- priv->revision = 2;
- break;
- case OV9640_V3:
- devname = "ov9640";
- priv->model = V4L2_IDENT_OV9640;
- priv->revision = 3;
- break;
- default:
- dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver);
- return -ENODEV;
- }
-
- dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
- devname, pid, ver, midh, midl);
-
- return v4l2_ctrl_handler_setup(&priv->hdl);
-}
-
-static const struct v4l2_ctrl_ops ov9640_ctrl_ops = {
- .s_ctrl = ov9640_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops ov9640_core_ops = {
- .g_chip_ident = ov9640_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov9640_get_register,
- .s_register = ov9640_set_register,
-#endif
-
-};
-
-/* Request bus settings on camera side */
-static int ov9640_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops ov9640_video_ops = {
- .s_stream = ov9640_s_stream,
- .s_mbus_fmt = ov9640_s_fmt,
- .try_mbus_fmt = ov9640_try_fmt,
- .enum_mbus_fmt = ov9640_enum_fmt,
- .cropcap = ov9640_cropcap,
- .g_crop = ov9640_g_crop,
- .g_mbus_config = ov9640_g_mbus_config,
-};
-
-static struct v4l2_subdev_ops ov9640_subdev_ops = {
- .core = &ov9640_core_ops,
- .video = &ov9640_video_ops,
-};
-
-/*
- * i2c_driver function
- */
-static int ov9640_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov9640_priv *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl) {
- dev_err(&client->dev, "Missing platform_data for driver\n");
- return -EINVAL;
- }
-
- priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL);
- if (!priv) {
- dev_err(&client->dev,
- "Failed to allocate memory for private data!\n");
- return -ENOMEM;
- }
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops);
-
- v4l2_ctrl_handler_init(&priv->hdl, 2);
- v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error) {
- int err = priv->hdl.error;
-
- kfree(priv);
- return err;
- }
-
- ret = ov9640_video_probe(client);
-
- if (ret) {
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- }
-
- return ret;
-}
-
-static int ov9640_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct ov9640_priv *priv = to_ov9640_sensor(sd);
-
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id ov9640_id[] = {
- { "ov9640", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, ov9640_id);
-
-static struct i2c_driver ov9640_i2c_driver = {
- .driver = {
- .name = "ov9640",
- },
- .probe = ov9640_probe,
- .remove = ov9640_remove,
- .id_table = ov9640_id,
-};
-
-module_i2c_driver(ov9640_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
-MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
deleted file mode 100644
index 3eb07c22516e..000000000000
--- a/drivers/media/video/ov9740.c
+++ /dev/null
@@ -1,1005 +0,0 @@
-/*
- * 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 <linux/v4l2-mediabus.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.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_CTRL15 0x3615
-#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
-
-#define OV9740_MAX_WIDTH 1280
-#define OV9740_MAX_HEIGHT 720
-
-/* Misc. structures */
-struct ov9740_reg {
- u16 reg;
- u8 val;
-};
-
-struct ov9740_priv {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
-
- int ident;
- u16 model;
- u8 revision;
- u8 manid;
- u8 smiaver;
-
- bool flag_vflip;
- bool flag_hflip;
-
- /* For suspend/resume. */
- struct v4l2_mbus_framefmt current_mf;
- bool current_enable;
-};
-
-static const struct ov9740_reg ov9740_defaults[] = {
- /* Software Reset */
- { OV9740_SOFTWARE_RESET, 0x01 },
-
- /* 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 },
- { OV9740_ANALOG_CTRL15, 0xf0 },
-
- /* 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 }, /* 0x64 for discontinuous clk */
- { 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 },
-
- /* YUYV order */
- { OV9740_ISP_CTRL19, 0x02 },
-};
-
-static enum v4l2_mbus_pixelcode ov9740_codes[] = {
- V4L2_MBUS_FMT_YUYV8_2X8,
-};
-
-/* 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 0x%04x 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 0x%04x 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);
- }
-
- priv->current_enable = enable;
-
- return ret;
-}
-
-/* select nearest higher resolution for capture */
-static void ov9740_res_roundup(u32 *width, u32 *height)
-{
- /* Width must be a multiple of 4 pixels. */
- *width = ALIGN(*width, 4);
-
- /* Max resolution is 1280x720 (720p). */
- if (*width > OV9740_MAX_WIDTH)
- *width = OV9740_MAX_WIDTH;
-
- if (*height > OV9740_MAX_HEIGHT)
- *height = OV9740_MAX_HEIGHT;
-}
-
-/* Setup registers according to resolution and color encoding */
-static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height)
-{
- u32 x_start;
- u32 y_start;
- u32 x_end;
- u32 y_end;
- bool scaling = 0;
- u32 scale_input_x;
- u32 scale_input_y;
- int ret;
-
- if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT))
- scaling = 1;
-
- /*
- * Try to use as much of the sensor area as possible when supporting
- * smaller resolutions. Depending on the aspect ratio of the
- * chosen resolution, we can either use the full width of the sensor,
- * or the full height of the sensor (or both if the aspect ratio is
- * the same as 1280x720.
- */
- if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) {
- scale_input_x = (OV9740_MAX_HEIGHT * width) / height;
- scale_input_y = OV9740_MAX_HEIGHT;
- } else {
- scale_input_x = OV9740_MAX_WIDTH;
- scale_input_y = (OV9740_MAX_WIDTH * height) / width;
- }
-
- /* These describe the area of the sensor to use. */
- x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2;
- y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2;
- x_end = x_start + scale_input_x - 1;
- y_end = y_start + scale_input_y - 1;
-
- ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff);
- if (ret)
- goto done;
-
- ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff);
- if (ret)
- goto done;
-
- ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff);
- if (ret)
- goto done;
-
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff);
- if (ret)
- goto done;
-
- ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI,
- (scale_input_x - width) >> 8);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO,
- (scale_input_x - width) & 0xff);
- if (ret)
- goto done;
-
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff);
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef |
- (scaling << 4));
- if (ret)
- goto done;
- ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff);
-
-done:
- 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);
- struct ov9740_priv *priv = to_ov9740(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, mf->height);
- if (ret < 0)
- return ret;
-
- mf->code = code;
- mf->colorspace = cspace;
-
- memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt));
-
- 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_MAX_WIDTH;
- a->bounds.height = OV9740_MAX_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_MAX_WIDTH;
- a->c.height = OV9740_MAX_HEIGHT;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-/* Set status of additional camera capabilities */
-static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct ov9740_priv *priv =
- container_of(ctrl->handler, struct ov9740_priv, hdl);
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- priv->flag_vflip = ctrl->val;
- break;
- case V4L2_CID_HFLIP:
- priv->flag_hflip = ctrl->val;
- 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;
-}
-
-static int ov9740_s_power(struct v4l2_subdev *sd, int on)
-{
- struct ov9740_priv *priv = to_ov9740(sd);
-
- if (!priv->current_enable)
- return 0;
-
- if (on) {
- ov9740_s_fmt(sd, &priv->current_mf);
- ov9740_s_stream(sd, priv->current_enable);
- } else {
- ov9740_s_stream(sd, 0);
- priv->current_enable = true;
- }
-
- 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
-
-static int ov9740_video_probe(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct ov9740_priv *priv = to_ov9740(sd);
- u8 modelhi, modello;
- int ret;
-
- /*
- * 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;
-}
-
-/* Request bus settings on camera side */
-static int ov9740_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-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,
- .g_mbus_config = ov9740_g_mbus_config,
-};
-
-static struct v4l2_subdev_core_ops ov9740_core_ops = {
- .g_chip_ident = ov9740_g_chip_ident,
- .s_power = ov9740_s_power,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = ov9740_get_register,
- .s_register = ov9740_set_register,
-#endif
-};
-
-static struct v4l2_subdev_ops ov9740_subdev_ops = {
- .core = &ov9740_core_ops,
- .video = &ov9740_video_ops,
-};
-
-static const struct v4l2_ctrl_ops ov9740_ctrl_ops = {
- .s_ctrl = ov9740_s_ctrl,
-};
-
-/*
- * i2c_driver function
- */
-static int ov9740_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct ov9740_priv *priv;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- 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);
- v4l2_ctrl_handler_init(&priv->hdl, 13);
- v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- priv->subdev.ctrl_handler = &priv->hdl;
- if (priv->hdl.error) {
- int err = priv->hdl.error;
-
- kfree(priv);
- return err;
- }
-
- ret = ov9740_video_probe(client);
- if (!ret)
- ret = v4l2_ctrl_handler_setup(&priv->hdl);
- if (ret < 0) {
- v4l2_ctrl_handler_free(&priv->hdl);
- kfree(priv);
- }
-
- return ret;
-}
-
-static int ov9740_remove(struct i2c_client *client)
-{
- struct ov9740_priv *priv = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(&priv->subdev);
- v4l2_ctrl_handler_free(&priv->hdl);
- 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,
-};
-
-module_i2c_driver(ov9740_i2c_driver);
-
-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/Kconfig b/drivers/media/video/pvrusb2/Kconfig
deleted file mode 100644
index 25e412ecad2c..000000000000
--- a/drivers/media/video/pvrusb2/Kconfig
+++ /dev/null
@@ -1,65 +0,0 @@
-config VIDEO_PVRUSB2
- tristate "Hauppauge WinTV-PVR USB2 support"
- depends on VIDEO_V4L2 && I2C
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEO_CX2341X
- select VIDEO_SAA711X
- select VIDEO_CX25840
- select VIDEO_MSP3400
- select VIDEO_WM8775
- select VIDEO_CS53L32A
- ---help---
- This is a video4linux driver for Conexant 23416 based
- usb2 personal video recorder devices.
-
- To compile this driver as a module, choose M here: the
- module will be called pvrusb2
-
-config VIDEO_PVRUSB2_SYSFS
- bool "pvrusb2 sysfs support (EXPERIMENTAL)"
- default y
- depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL
- ---help---
- This option enables the operation of a sysfs based
- interface for query and control of the pvrusb2 driver.
-
- This is not generally needed for v4l applications,
- although certain applications are optimized to take
- advantage of this feature.
-
- If you are in doubt, say Y.
-
- Note: This feature is experimental and subject to change.
-
-config VIDEO_PVRUSB2_DVB
- bool "pvrusb2 ATSC/DVB support (EXPERIMENTAL)"
- default y
- depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
- select DVB_LGDT330X if !DVB_FE_CUSTOMISE
- select DVB_S5H1409 if !DVB_FE_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select DVB_TDA10048 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- ---help---
-
- This option enables a DVB interface for the pvrusb2 driver.
- If your device does not support digital television, this
- feature will have no affect on the driver's operation.
-
- If you are in doubt, say Y.
-
-config VIDEO_PVRUSB2_DEBUGIFC
- bool "pvrusb2 debug interface"
- depends on VIDEO_PVRUSB2_SYSFS
- ---help---
- This option enables the inclusion of a debug interface
- in the pvrusb2 driver, hosted through sysfs.
-
- You do not need to select this option unless you plan
- on debugging the driver or performing a manual firmware
- extraction.
-
- If you are in doubt, say N.
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
deleted file mode 100644
index c17f37d964ad..000000000000
--- a/drivers/media/video/pvrusb2/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
-obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
-obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
-
-pvrusb2-objs := pvrusb2-i2c-core.o \
- pvrusb2-audio.o \
- pvrusb2-encoder.o pvrusb2-video-v4l.o \
- pvrusb2-eeprom.o \
- pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
- pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
- pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
- pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
- pvrusb2-cs53l32a.o \
- $(obj-pvrusb2-dvb-y) \
- $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
-
-obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
deleted file mode 100644
index d8c898278e8c..000000000000
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ /dev/null
@@ -1,569 +0,0 @@
-/*
- *
- *
- * Copyright (C) 2007 Mike Isely <isely@pobox.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-/*
-
-This source file should encompass ALL per-device type information for the
-driver. To define a new device, add elements to the pvr2_device_table and
-pvr2_device_desc structures.
-
-*/
-
-#include "pvrusb2-devattr.h"
-#include <linux/usb.h>
-#include <linux/module.h>
-/* This is needed in order to pull in tuner type ids... */
-#include <linux/i2c.h>
-#include <media/tuner.h>
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
-#include "pvrusb2-hdw-internal.h"
-#include "lgdt330x.h"
-#include "s5h1409.h"
-#include "s5h1411.h"
-#include "tda10048.h"
-#include "tda18271.h"
-#include "tda8290.h"
-#include "tuner-simple.h"
-#endif
-
-
-/*------------------------------------------------------------------------*/
-/* Hauppauge PVR-USB2 Model 29xxx */
-
-static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = {
- { .module_id = PVR2_CLIENT_ID_SAA7115 },
- { .module_id = PVR2_CLIENT_ID_MSP3400 },
- { .module_id = PVR2_CLIENT_ID_TUNER },
- { .module_id = PVR2_CLIENT_ID_DEMOD },
-};
-
-static const char *pvr2_fw1_names_29xxx[] = {
- "v4l-pvrusb2-29xxx-01.fw",
-};
-
-static const struct pvr2_device_desc pvr2_device_29xxx = {
- .description = "WinTV PVR USB2 Model 29xxx",
- .shortname = "29xxx",
- .client_table.lst = pvr2_cli_29xxx,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
- .fx2_firmware.lst = pvr2_fw1_names_29xxx,
- .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
- .flag_has_hauppauge_rom = !0,
- .flag_has_analogtuner = !0,
- .flag_has_fmradio = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
- .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
- .ir_scheme = PVR2_IR_SCHEME_29XXX,
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* Hauppauge PVR-USB2 Model 24xxx */
-
-static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = {
- { .module_id = PVR2_CLIENT_ID_CX25840 },
- { .module_id = PVR2_CLIENT_ID_TUNER },
- { .module_id = PVR2_CLIENT_ID_WM8775 },
- { .module_id = PVR2_CLIENT_ID_DEMOD },
-};
-
-static const char *pvr2_fw1_names_24xxx[] = {
- "v4l-pvrusb2-24xxx-01.fw",
-};
-
-static const struct pvr2_device_desc pvr2_device_24xxx = {
- .description = "WinTV PVR USB2 Model 24xxx",
- .shortname = "24xxx",
- .client_table.lst = pvr2_cli_24xxx,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
- .fx2_firmware.lst = pvr2_fw1_names_24xxx,
- .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
- .flag_has_cx25840 = !0,
- .flag_has_wm8775 = !0,
- .flag_has_hauppauge_rom = !0,
- .flag_has_analogtuner = !0,
- .flag_has_fmradio = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
- .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
- .ir_scheme = PVR2_IR_SCHEME_24XXX,
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* GOTVIEW USB2.0 DVD2 */
-
-static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = {
- { .module_id = PVR2_CLIENT_ID_CX25840 },
- { .module_id = PVR2_CLIENT_ID_TUNER },
- { .module_id = PVR2_CLIENT_ID_DEMOD },
-};
-
-static const struct pvr2_device_desc pvr2_device_gotview_2 = {
- .description = "Gotview USB 2.0 DVD 2",
- .shortname = "gv2",
- .client_table.lst = pvr2_cli_gotview_2,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
- .flag_has_cx25840 = !0,
- .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .flag_has_analogtuner = !0,
- .flag_has_fmradio = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* GOTVIEW USB2.0 DVD Deluxe */
-
-/* (same module list as gotview_2) */
-
-static const struct pvr2_device_desc pvr2_device_gotview_2d = {
- .description = "Gotview USB 2.0 DVD Deluxe",
- .shortname = "gv2d",
- .client_table.lst = pvr2_cli_gotview_2,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
- .flag_has_cx25840 = !0,
- .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* Terratec Grabster AV400 */
-
-static const struct pvr2_device_client_desc pvr2_cli_av400[] = {
- { .module_id = PVR2_CLIENT_ID_CX25840 },
-};
-
-static const struct pvr2_device_desc pvr2_device_av400 = {
- .description = "Terratec Grabster AV400",
- .shortname = "av400",
- .flag_is_experimental = 1,
- .client_table.lst = pvr2_cli_av400,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_av400),
- .flag_has_cx25840 = !0,
- .flag_has_analogtuner = 0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_AV400,
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* OnAir Creator */
-
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
-static struct lgdt330x_config pvr2_lgdt3303_config = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3303,
- .clock_polarity_flip = 1,
-};
-
-static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
-{
- adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
- &adap->channel.hdw->i2c_adap);
- if (adap->fe)
- return 0;
-
- return -EIO;
-}
-
-static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
-{
- dvb_attach(simple_tuner_attach, adap->fe,
- &adap->channel.hdw->i2c_adap, 0x61,
- TUNER_LG_TDVS_H06XF);
-
- return 0;
-}
-
-static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
- .frontend_attach = pvr2_lgdt3303_attach,
- .tuner_attach = pvr2_lgh06xf_attach,
-};
-#endif
-
-static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = {
- { .module_id = PVR2_CLIENT_ID_SAA7115 },
- { .module_id = PVR2_CLIENT_ID_CS53L32A },
- { .module_id = PVR2_CLIENT_ID_TUNER },
-};
-
-static const struct pvr2_device_desc pvr2_device_onair_creator = {
- .description = "OnAir Creator Hybrid USB tuner",
- .shortname = "oac",
- .client_table.lst = pvr2_cli_onair_creator,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator),
- .default_tuner_type = TUNER_LG_TDVS_H06XF,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .flag_digital_requires_cx23416 = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
- .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
- .default_std_mask = V4L2_STD_NTSC_M,
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
- .dvb_props = &pvr2_onair_creator_fe_props,
-#endif
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* OnAir USB 2.0 */
-
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
-static struct lgdt330x_config pvr2_lgdt3302_config = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3302,
-};
-
-static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
-{
- adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
- &adap->channel.hdw->i2c_adap);
- if (adap->fe)
- return 0;
-
- return -EIO;
-}
-
-static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
-{
- dvb_attach(simple_tuner_attach, adap->fe,
- &adap->channel.hdw->i2c_adap, 0x61,
- TUNER_PHILIPS_FCV1236D);
-
- return 0;
-}
-
-static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
- .frontend_attach = pvr2_lgdt3302_attach,
- .tuner_attach = pvr2_fcv1236d_attach,
-};
-#endif
-
-static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = {
- { .module_id = PVR2_CLIENT_ID_SAA7115 },
- { .module_id = PVR2_CLIENT_ID_CS53L32A },
- { .module_id = PVR2_CLIENT_ID_TUNER },
-};
-
-static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
- .description = "OnAir USB2 Hybrid USB tuner",
- .shortname = "oa2",
- .client_table.lst = pvr2_cli_onair_usb2,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2),
- .default_tuner_type = TUNER_PHILIPS_FCV1236D,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .flag_digital_requires_cx23416 = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
- .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
- .default_std_mask = V4L2_STD_NTSC_M,
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
- .dvb_props = &pvr2_onair_usb2_fe_props,
-#endif
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* Hauppauge PVR-USB2 Model 73xxx */
-
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
-static struct tda10048_config hauppauge_tda10048_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_PARALLEL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_50,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3800,
- .dtv8_if_freq_khz = TDA10048_IF_4300,
- .clk_freq_khz = TDA10048_CLK_16000,
- .disable_gate_access = 1,
-};
-
-static struct tda829x_config tda829x_no_probe = {
- .probe_tuner = TDA829X_DONT_PROBE,
-};
-
-static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = {
- .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
- .if_lvl = 1, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config hauppauge_tda18271_dvb_config = {
- .std_map = &hauppauge_tda18271_dvbt_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap)
-{
- adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config,
- &adap->channel.hdw->i2c_adap);
- if (adap->fe)
- return 0;
-
- return -EIO;
-}
-
-static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
-{
- dvb_attach(tda829x_attach, adap->fe,
- &adap->channel.hdw->i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, adap->fe, 0x60,
- &adap->channel.hdw->i2c_adap,
- &hauppauge_tda18271_dvb_config);
-
- return 0;
-}
-
-static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
- .frontend_attach = pvr2_tda10048_attach,
- .tuner_attach = pvr2_73xxx_tda18271_8295_attach,
-};
-#endif
-
-static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = {
- { .module_id = PVR2_CLIENT_ID_CX25840 },
- { .module_id = PVR2_CLIENT_ID_TUNER,
- .i2c_address_list = "\x42"},
-};
-
-static const char *pvr2_fw1_names_73xxx[] = {
- "v4l-pvrusb2-73xxx-01.fw",
-};
-
-static const struct pvr2_device_desc pvr2_device_73xxx = {
- .description = "WinTV HVR-1900 Model 73xxx",
- .shortname = "73xxx",
- .client_table.lst = pvr2_cli_73xxx,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
- .fx2_firmware.lst = pvr2_fw1_names_73xxx,
- .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
- .flag_has_cx25840 = !0,
- .flag_has_hauppauge_rom = !0,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .flag_fx2_16kb = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
- .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
- .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
- .ir_scheme = PVR2_IR_SCHEME_ZILOG,
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
- .dvb_props = &pvr2_73xxx_dvb_props,
-#endif
-};
-
-
-
-/*------------------------------------------------------------------------*/
-/* Hauppauge PVR-USB2 Model 75xxx */
-
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
-static struct s5h1409_config pvr2_s5h1409_config = {
- .demod_address = 0x32 >> 1,
- .output_mode = S5H1409_PARALLEL_OUTPUT,
- .gpio = S5H1409_GPIO_OFF,
- .qam_if = 4000,
- .inversion = S5H1409_INVERSION_ON,
- .status_mode = S5H1409_DEMODLOCKING,
-};
-
-static struct s5h1411_config pvr2_s5h1411_config = {
- .output_mode = S5H1411_PARALLEL_OUTPUT,
- .gpio = S5H1411_GPIO_OFF,
- .vsb_if = S5H1411_IF_44000,
- .qam_if = S5H1411_IF_4000,
- .inversion = S5H1411_INVERSION_ON,
- .status_mode = S5H1411_DEMODLOCKING,
-};
-
-static struct tda18271_std_map hauppauge_tda18271_std_map = {
- .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
- .if_lvl = 6, .rfagc_top = 0x37, },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
- .if_lvl = 6, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config hauppauge_tda18271_config = {
- .std_map = &hauppauge_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap)
-{
- adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config,
- &adap->channel.hdw->i2c_adap);
- if (adap->fe)
- return 0;
-
- return -EIO;
-}
-
-static int pvr2_s5h1411_attach(struct pvr2_dvb_adapter *adap)
-{
- adap->fe = dvb_attach(s5h1411_attach, &pvr2_s5h1411_config,
- &adap->channel.hdw->i2c_adap);
- if (adap->fe)
- return 0;
-
- return -EIO;
-}
-
-static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
-{
- dvb_attach(tda829x_attach, adap->fe,
- &adap->channel.hdw->i2c_adap, 0x42,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, adap->fe, 0x60,
- &adap->channel.hdw->i2c_adap,
- &hauppauge_tda18271_config);
-
- return 0;
-}
-
-static const struct pvr2_dvb_props pvr2_750xx_dvb_props = {
- .frontend_attach = pvr2_s5h1409_attach,
- .tuner_attach = pvr2_tda18271_8295_attach,
-};
-
-static const struct pvr2_dvb_props pvr2_751xx_dvb_props = {
- .frontend_attach = pvr2_s5h1411_attach,
- .tuner_attach = pvr2_tda18271_8295_attach,
-};
-#endif
-
-static const char *pvr2_fw1_names_75xxx[] = {
- "v4l-pvrusb2-73xxx-01.fw",
-};
-
-static const struct pvr2_device_desc pvr2_device_750xx = {
- .description = "WinTV HVR-1950 Model 750xx",
- .shortname = "750xx",
- .client_table.lst = pvr2_cli_73xxx,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
- .fx2_firmware.lst = pvr2_fw1_names_75xxx,
- .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
- .flag_has_cx25840 = !0,
- .flag_has_hauppauge_rom = !0,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .flag_fx2_16kb = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
- .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
- .default_std_mask = V4L2_STD_NTSC_M,
- .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
- .ir_scheme = PVR2_IR_SCHEME_ZILOG,
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
- .dvb_props = &pvr2_750xx_dvb_props,
-#endif
-};
-
-static const struct pvr2_device_desc pvr2_device_751xx = {
- .description = "WinTV HVR-1950 Model 751xx",
- .shortname = "751xx",
- .client_table.lst = pvr2_cli_73xxx,
- .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
- .fx2_firmware.lst = pvr2_fw1_names_75xxx,
- .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
- .flag_has_cx25840 = !0,
- .flag_has_hauppauge_rom = !0,
- .flag_has_analogtuner = !0,
- .flag_has_composite = !0,
- .flag_has_svideo = !0,
- .flag_fx2_16kb = !0,
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
- .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
- .default_std_mask = V4L2_STD_NTSC_M,
- .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
- .ir_scheme = PVR2_IR_SCHEME_ZILOG,
-#ifdef CONFIG_VIDEO_PVRUSB2_DVB
- .dvb_props = &pvr2_751xx_dvb_props,
-#endif
-};
-
-
-
-/*------------------------------------------------------------------------*/
-
-struct usb_device_id pvr2_device_table[] = {
- { USB_DEVICE(0x2040, 0x2900),
- .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
- { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */
- .driver_info = (kernel_ulong_t)&pvr2_device_29xxx},
- { USB_DEVICE(0x2040, 0x2400),
- .driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
- { USB_DEVICE(0x1164, 0x0622),
- .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
- { USB_DEVICE(0x1164, 0x0602),
- .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
- { USB_DEVICE(0x11ba, 0x1003),
- .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
- { USB_DEVICE(0x11ba, 0x1001),
- .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2},
- { USB_DEVICE(0x2040, 0x7300),
- .driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
- { USB_DEVICE(0x2040, 0x7500),
- .driver_info = (kernel_ulong_t)&pvr2_device_750xx},
- { USB_DEVICE(0x2040, 0x7501),
- .driver_info = (kernel_ulong_t)&pvr2_device_751xx},
- { USB_DEVICE(0x0ccd, 0x0039),
- .driver_info = (kernel_ulong_t)&pvr2_device_av400},
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, pvr2_device_table);
-
-
-/*
- Stuff for Emacs to see, in order to encourage consistent editing style:
- *** Local Variables: ***
- *** mode: c ***
- *** fill-column: 75 ***
- *** tab-width: 8 ***
- *** c-basic-offset: 8 ***
- *** End: ***
- */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
deleted file mode 100644
index f344aed32a93..000000000000
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ /dev/null
@@ -1,1413 +0,0 @@
-/*
- *
- *
- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
- * Copyright (C) 2004 Aurelien Alleaume <slts@free.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; either version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include "pvrusb2-context.h"
-#include "pvrusb2-hdw.h"
-#include "pvrusb2.h"
-#include "pvrusb2-debug.h"
-#include "pvrusb2-v4l2.h"
-#include "pvrusb2-ioread.h"
-#include <linux/videodev2.h>
-#include <linux/module.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-
-struct pvr2_v4l2_dev;
-struct pvr2_v4l2_fh;
-struct pvr2_v4l2;
-
-struct pvr2_v4l2_dev {
- struct video_device devbase; /* MUST be first! */
- struct pvr2_v4l2 *v4lp;
- struct pvr2_context_stream *stream;
- /* Information about this device: */
- enum pvr2_config config; /* Expected stream format */
- int v4l_type; /* V4L defined type for this device node */
- enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
-};
-
-struct pvr2_v4l2_fh {
- struct pvr2_channel channel;
- struct pvr2_v4l2_dev *pdi;
- enum v4l2_priority prio;
- struct pvr2_ioread *rhp;
- struct file *file;
- struct pvr2_v4l2 *vhead;
- struct pvr2_v4l2_fh *vnext;
- struct pvr2_v4l2_fh *vprev;
- wait_queue_head_t wait_data;
- int fw_mode_flag;
- /* Map contiguous ordinal value to input id */
- unsigned char *input_map;
- unsigned int input_cnt;
-};
-
-struct pvr2_v4l2 {
- struct pvr2_channel channel;
- struct pvr2_v4l2_fh *vfirst;
- struct pvr2_v4l2_fh *vlast;
-
- struct v4l2_prio_state prio;
-
- /* streams - Note that these must be separately, individually,
- * allocated pointers. This is because the v4l core is going to
- * manage their deletion - separately, individually... */
- struct pvr2_v4l2_dev *dev_video;
- struct pvr2_v4l2_dev *dev_radio;
-};
-
-static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
-module_param_array(video_nr, int, NULL, 0444);
-MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
-static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
-module_param_array(radio_nr, int, NULL, 0444);
-MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
-static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
-module_param_array(vbi_nr, int, NULL, 0444);
-MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
-
-static struct v4l2_capability pvr_capability ={
- .driver = "pvrusb2",
- .card = "Hauppauge WinTV pvr-usb2",
- .bus_info = "usb",
- .version = LINUX_VERSION_CODE,
- .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
- V4L2_CAP_READWRITE),
-};
-
-static struct v4l2_fmtdesc pvr_fmtdesc [] = {
- {
- .index = 0,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .flags = V4L2_FMT_FLAG_COMPRESSED,
- .description = "MPEG1/2",
- // This should really be V4L2_PIX_FMT_MPEG, but xawtv
- // breaks when I do that.
- .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
- }
-};
-
-#define PVR_FORMAT_PIX 0
-#define PVR_FORMAT_VBI 1
-
-static struct v4l2_format pvr_format [] = {
- [PVR_FORMAT_PIX] = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .fmt = {
- .pix = {
- .width = 720,
- .height = 576,
- // This should really be V4L2_PIX_FMT_MPEG,
- // but xawtv breaks when I do that.
- .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
- .field = V4L2_FIELD_INTERLACED,
- .bytesperline = 0, // doesn't make sense
- // here
- //FIXME : Don't know what to put here...
- .sizeimage = (32*1024),
- .colorspace = 0, // doesn't make sense here
- .priv = 0
- }
- }
- },
- [PVR_FORMAT_VBI] = {
- .type = V4L2_BUF_TYPE_VBI_CAPTURE,
- .fmt = {
- .vbi = {
- .sampling_rate = 27000000,
- .offset = 248,
- .samples_per_line = 1443,
- .sample_format = V4L2_PIX_FMT_GREY,
- .start = { 0, 0 },
- .count = { 0, 0 },
- .flags = 0,
- }
- }
- }
-};
-
-
-
-/*
- * This is part of Video 4 Linux API. These procedures handle ioctl() calls.
- */
-static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
- strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
- sizeof(cap->bus_info));
- strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
- return 0;
-}
-
-static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_v4l2 *vp = fh->vhead;
-
- *p = v4l2_prio_max(&vp->prio);
- return 0;
-}
-
-static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_v4l2 *vp = fh->vhead;
-
- return v4l2_prio_change(&vp->prio, &fh->prio, prio);
-}
-
-static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val = 0;
- int ret;
-
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val);
- *std = val;
- return ret;
-}
-
-int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- return pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std);
-}
-
-static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val = 0;
- int ret;
-
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val);
- *std = val;
- return ret;
-}
-
-static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct pvr2_ctrl *cptr;
- struct v4l2_input tmp;
- unsigned int cnt;
- int val;
-
- cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
-
- memset(&tmp, 0, sizeof(tmp));
- tmp.index = vi->index;
- if (vi->index >= fh->input_cnt)
- return -EINVAL;
- val = fh->input_map[vi->index];
- switch (val) {
- case PVR2_CVAL_INPUT_TV:
- case PVR2_CVAL_INPUT_DTV:
- case PVR2_CVAL_INPUT_RADIO:
- tmp.type = V4L2_INPUT_TYPE_TUNER;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- case PVR2_CVAL_INPUT_COMPOSITE:
- tmp.type = V4L2_INPUT_TYPE_CAMERA;
- break;
- default:
- return -EINVAL;
- }
-
- cnt = 0;
- pvr2_ctrl_get_valname(cptr, val,
- tmp.name, sizeof(tmp.name) - 1, &cnt);
- tmp.name[cnt] = 0;
-
- /* Don't bother with audioset, since this driver currently
- always switches the audio whenever the video is
- switched. */
-
- /* Handling std is a tougher problem. It doesn't make
- sense in cases where a device might be multi-standard.
- We could just copy out the current value for the
- standard, but it can change over time. For now just
- leave it zero. */
- *vi = tmp;
- return 0;
-}
-
-static int pvr2_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- unsigned int idx;
- struct pvr2_ctrl *cptr;
- int val;
- int ret;
-
- cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
- val = 0;
- ret = pvr2_ctrl_get_value(cptr, &val);
- *i = 0;
- for (idx = 0; idx < fh->input_cnt; idx++) {
- if (fh->input_map[idx] == val) {
- *i = idx;
- break;
- }
- }
- return ret;
-}
-
-static int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- if (inp >= fh->input_cnt)
- return -EINVAL;
- return pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
- fh->input_map[inp]);
-}
-
-static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
-{
- /* pkt: FIXME: We are returning one "fake" input here
- which could very well be called "whatever_we_like".
- This is for apps that want to see an audio input
- just to feel comfortable, as well as to test if
- it can do stereo or sth. There is actually no guarantee
- that the actual audio input cannot change behind the app's
- back, but most applications should not mind that either.
-
- Hopefully, mplayer people will work with us on this (this
- whole mess is to support mplayer pvr://), or Hans will come
- up with a more standard way to say "we have inputs but we
- don 't want you to change them independent of video" which
- will sort this mess.
- */
-
- if (vin->index > 0)
- return -EINVAL;
- strncpy(vin->name, "PVRUSB2 Audio", 14);
- vin->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
-{
- /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
- vin->index = 0;
- strncpy(vin->name, "PVRUSB2 Audio", 14);
- vin->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout)
-{
- if (vout->index)
- return -EINVAL;
- return 0;
-}
-
-static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- if (vt->index != 0)
- return -EINVAL; /* Only answer for the 1st tuner */
-
- pvr2_hdw_execute_tuner_poll(hdw);
- return pvr2_hdw_get_tuner_status(hdw, vt);
-}
-
-static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- if (vt->index != 0)
- return -EINVAL;
-
- return pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
- vt->audmode);
-}
-
-int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- unsigned long fv;
- struct v4l2_tuner vt;
- int cur_input;
- struct pvr2_ctrl *ctrlp;
- int ret;
-
- ret = pvr2_hdw_get_tuner_status(hdw, &vt);
- if (ret != 0)
- return ret;
- ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
- ret = pvr2_ctrl_get_value(ctrlp, &cur_input);
- if (ret != 0)
- return ret;
- if (vf->type == V4L2_TUNER_RADIO) {
- if (cur_input != PVR2_CVAL_INPUT_RADIO)
- pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO);
- } else {
- if (cur_input == PVR2_CVAL_INPUT_RADIO)
- pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV);
- }
- fv = vf->frequency;
- if (vt.capability & V4L2_TUNER_CAP_LOW)
- fv = (fv * 125) / 2;
- else
- fv = fv * 62500;
- return pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
-}
-
-static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val = 0;
- int cur_input;
- struct v4l2_tuner vt;
- int ret;
-
- ret = pvr2_hdw_get_tuner_status(hdw, &vt);
- if (ret != 0)
- return ret;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY),
- &val);
- if (ret != 0)
- return ret;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
- &cur_input);
- if (cur_input == PVR2_CVAL_INPUT_RADIO)
- vf->type = V4L2_TUNER_RADIO;
- else
- vf->type = V4L2_TUNER_ANALOG_TV;
- if (vt.capability & V4L2_TUNER_CAP_LOW)
- val = (val * 2) / 125;
- else
- val /= 62500;
- vf->frequency = val;
- return 0;
-}
-
-static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd)
-{
- /* Only one format is supported : mpeg.*/
- if (fd->index != 0)
- return -EINVAL;
-
- memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
- return 0;
-}
-
-static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val;
-
- memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format));
- val = 0;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES),
- &val);
- vf->fmt.pix.width = val;
- val = 0;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES),
- &val);
- vf->fmt.pix.height = val;
- return 0;
-}
-
-static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int lmin, lmax, ldef;
- struct pvr2_ctrl *hcp, *vcp;
- int h = vf->fmt.pix.height;
- int w = vf->fmt.pix.width;
-
- hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
- vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
-
- lmin = pvr2_ctrl_get_min(hcp);
- lmax = pvr2_ctrl_get_max(hcp);
- pvr2_ctrl_get_def(hcp, &ldef);
- if (w == -1)
- w = ldef;
- else if (w < lmin)
- w = lmin;
- else if (w > lmax)
- w = lmax;
- lmin = pvr2_ctrl_get_min(vcp);
- lmax = pvr2_ctrl_get_max(vcp);
- pvr2_ctrl_get_def(vcp, &ldef);
- if (h == -1)
- h = ldef;
- else if (h < lmin)
- h = lmin;
- else if (h > lmax)
- h = lmax;
-
- memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
- sizeof(struct v4l2_format));
- vf->fmt.pix.width = w;
- vf->fmt.pix.height = h;
- return 0;
-}
-
-static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct pvr2_ctrl *hcp, *vcp;
- int ret = pvr2_try_fmt_vid_cap(file, fh, vf);
-
- if (ret)
- return ret;
- hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
- vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
- pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
- pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
- return 0;
-}
-
-static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct pvr2_v4l2_dev *pdi = fh->pdi;
- int ret;
-
- if (!fh->pdi->stream) {
- /* No stream defined for this node. This means
- that we're not currently allowed to stream from
- this node. */
- return -EPERM;
- }
- ret = pvr2_hdw_set_stream_type(hdw, pdi->config);
- if (ret < 0)
- return ret;
- return pvr2_hdw_set_streaming(hdw, !0);
-}
-
-static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- if (!fh->pdi->stream) {
- /* No stream defined for this node. This means
- that we're not currently allowed to stream from
- this node. */
- return -EPERM;
- }
- return pvr2_hdw_set_streaming(hdw, 0);
-}
-
-static int pvr2_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *vc)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct pvr2_ctrl *cptr;
- int val;
-
- if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
- cptr = pvr2_hdw_get_ctrl_nextv4l(
- hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
- if (cptr)
- vc->id = pvr2_ctrl_get_v4lid(cptr);
- } else {
- cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id);
- }
- if (!cptr) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x not implemented here",
- vc->id);
- return -EINVAL;
- }
-
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x mapping name=%s (%s)",
- vc->id, pvr2_ctrl_get_name(cptr),
- pvr2_ctrl_get_desc(cptr));
- strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name));
- vc->flags = pvr2_ctrl_get_v4lflags(cptr);
- pvr2_ctrl_get_def(cptr, &val);
- vc->default_value = val;
- switch (pvr2_ctrl_get_type(cptr)) {
- case pvr2_ctl_enum:
- vc->type = V4L2_CTRL_TYPE_MENU;
- vc->minimum = 0;
- vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
- vc->step = 1;
- break;
- case pvr2_ctl_bool:
- vc->type = V4L2_CTRL_TYPE_BOOLEAN;
- vc->minimum = 0;
- vc->maximum = 1;
- vc->step = 1;
- break;
- case pvr2_ctl_int:
- vc->type = V4L2_CTRL_TYPE_INTEGER;
- vc->minimum = pvr2_ctrl_get_min(cptr);
- vc->maximum = pvr2_ctrl_get_max(cptr);
- vc->step = 1;
- break;
- default:
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x name=%s not mappable",
- vc->id, pvr2_ctrl_get_name(cptr));
- return -EINVAL;
- }
- return 0;
-}
-
-static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- unsigned int cnt = 0;
- int ret;
-
- ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id),
- vm->index,
- vm->name, sizeof(vm->name) - 1,
- &cnt);
- vm->name[cnt] = 0;
- return ret;
-}
-
-static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val = 0;
- int ret;
-
- ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
- &val);
- vc->value = val;
- return ret;
-}
-
-static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
- vc->value);
-}
-
-static int pvr2_g_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctls)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct v4l2_ext_control *ctrl;
- unsigned int idx;
- int val;
- int ret;
-
- ret = 0;
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val);
- if (ret) {
- ctls->error_idx = idx;
- return ret;
- }
- /* Ensure that if read as a 64 bit value, the user
- will still get a hopefully sane value */
- ctrl->value64 = 0;
- ctrl->value = val;
- }
- return 0;
-}
-
-static int pvr2_s_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctls)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct v4l2_ext_control *ctrl;
- unsigned int idx;
- int ret;
-
- ret = 0;
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id),
- ctrl->value);
- if (ret) {
- ctls->error_idx = idx;
- return ret;
- }
- }
- return 0;
-}
-
-static int pvr2_try_ext_ctrls(struct file *file, void *priv,
- struct v4l2_ext_controls *ctls)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- struct v4l2_ext_control *ctrl;
- struct pvr2_ctrl *pctl;
- unsigned int idx;
-
- /* For the moment just validate that the requested control
- actually exists. */
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
- if (!pctl) {
- ctls->error_idx = idx;
- return -EINVAL;
- }
- }
- return 0;
-}
-
-static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int ret;
-
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- ret = pvr2_hdw_get_cropcap(hdw, cap);
- cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
- return ret;
-}
-
-static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int val = 0;
- int ret;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
- if (ret != 0)
- return -EINVAL;
- crop->c.left = val;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
- if (ret != 0)
- return -EINVAL;
- crop->c.top = val;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
- if (ret != 0)
- return -EINVAL;
- crop->c.width = val;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
- if (ret != 0)
- return -EINVAL;
- crop->c.height = val;
- return 0;
-}
-
-static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- int ret;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
- crop->c.left);
- if (ret != 0)
- return -EINVAL;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
- crop->c.top);
- if (ret != 0)
- return -EINVAL;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
- crop->c.width);
- if (ret != 0)
- return -EINVAL;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
- crop->c.height);
- if (ret != 0)
- return -EINVAL;
- return 0;
-}
-
-static int pvr2_log_status(struct file *file, void *priv)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
-
- pvr2_hdw_trigger_module_log(hdw);
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- u64 val;
- int ret;
-
- ret = pvr2_hdw_register_access(
- hdw, &req->match, req->reg,
- 0, &val);
- req->val = val;
- return ret;
-}
-
-static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- u64 val;
- int ret;
-
- val = req->val;
- ret = pvr2_hdw_register_access(
- hdw, &req->match, req->reg,
- 1, &val);
- return ret;
-}
-#endif
-
-static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
- .vidioc_querycap = pvr2_querycap,
- .vidioc_g_priority = pvr2_g_priority,
- .vidioc_s_priority = pvr2_s_priority,
- .vidioc_s_audio = pvr2_s_audio,
- .vidioc_g_audio = pvr2_g_audio,
- .vidioc_enumaudio = pvr2_enumaudio,
- .vidioc_enum_input = pvr2_enum_input,
- .vidioc_cropcap = pvr2_cropcap,
- .vidioc_s_crop = pvr2_s_crop,
- .vidioc_g_crop = pvr2_g_crop,
- .vidioc_g_input = pvr2_g_input,
- .vidioc_s_input = pvr2_s_input,
- .vidioc_g_frequency = pvr2_g_frequency,
- .vidioc_s_frequency = pvr2_s_frequency,
- .vidioc_s_tuner = pvr2_s_tuner,
- .vidioc_g_tuner = pvr2_g_tuner,
- .vidioc_g_std = pvr2_g_std,
- .vidioc_s_std = pvr2_s_std,
- .vidioc_querystd = pvr2_querystd,
- .vidioc_log_status = pvr2_log_status,
- .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap,
- .vidioc_streamon = pvr2_streamon,
- .vidioc_streamoff = pvr2_streamoff,
- .vidioc_queryctrl = pvr2_queryctrl,
- .vidioc_querymenu = pvr2_querymenu,
- .vidioc_g_ctrl = pvr2_g_ctrl,
- .vidioc_s_ctrl = pvr2_s_ctrl,
- .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls,
- .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls,
- .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = pvr2_g_register,
- .vidioc_s_register = pvr2_s_register,
-#endif
-};
-
-static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
-{
- struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
- enum pvr2_config cfg = dip->config;
- char msg[80];
- unsigned int mcnt;
-
- /* Construct the unregistration message *before* we actually
- perform the unregistration step. By doing it this way we don't
- have to worry about potentially touching deleted resources. */
- mcnt = scnprintf(msg, sizeof(msg) - 1,
- "pvrusb2: unregistered device %s [%s]",
- video_device_node_name(&dip->devbase),
- pvr2_config_get_name(cfg));
- msg[mcnt] = 0;
-
- pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
-
- /* Paranoia */
- dip->v4lp = NULL;
- dip->stream = NULL;
-
- /* Actual deallocation happens later when all internal references
- are gone. */
- video_unregister_device(&dip->devbase);
-
- printk(KERN_INFO "%s\n", msg);
-
-}
-
-
-static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
-{
- if (!dip) return;
- if (!dip->devbase.parent) return;
- dip->devbase.parent = NULL;
- device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
-}
-
-
-static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
-{
- if (vp->dev_video) {
- pvr2_v4l2_dev_destroy(vp->dev_video);
- vp->dev_video = NULL;
- }
- if (vp->dev_radio) {
- pvr2_v4l2_dev_destroy(vp->dev_radio);
- vp->dev_radio = NULL;
- }
-
- pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
- pvr2_channel_done(&vp->channel);
- kfree(vp);
-}
-
-
-static void pvr2_video_device_release(struct video_device *vdev)
-{
- struct pvr2_v4l2_dev *dev;
- dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
- kfree(dev);
-}
-
-
-static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
-{
- struct pvr2_v4l2 *vp;
- vp = container_of(chp,struct pvr2_v4l2,channel);
- if (!vp->channel.mc_head->disconnect_flag) return;
- pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
- pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
- if (vp->vfirst) return;
- pvr2_v4l2_destroy_no_lock(vp);
-}
-
-
-static long pvr2_v4l2_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
-
- struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_v4l2 *vp = fh->vhead;
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- long ret = -EINVAL;
-
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
- v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
-
- if (!pvr2_hdw_dev_ok(hdw)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "ioctl failed - bad or no context");
- return -EFAULT;
- }
-
- /* check priority */
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- ret = v4l2_prio_check(&vp->prio, fh->prio);
- if (ret)
- return ret;
- }
-
- ret = video_ioctl2(file, cmd, arg);
-
- pvr2_hdw_commit_ctl(hdw);
-
- if (ret < 0) {
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
- } else {
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl failure, ret=%ld"
- " command was:", ret);
- v4l_printk_ioctl(pvr2_hdw_get_driver_name(hdw),
- cmd);
- }
- }
- } else {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
- ret, ret);
- }
- return ret;
-
-}
-
-
-static int pvr2_v4l2_release(struct file *file)
-{
- struct pvr2_v4l2_fh *fhp = file->private_data;
- struct pvr2_v4l2 *vp = fhp->vhead;
- struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
-
- pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
-
- if (fhp->rhp) {
- struct pvr2_stream *sp;
- pvr2_hdw_set_streaming(hdw,0);
- sp = pvr2_ioread_get_stream(fhp->rhp);
- if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
- pvr2_ioread_destroy(fhp->rhp);
- fhp->rhp = NULL;
- }
-
- v4l2_prio_close(&vp->prio, fhp->prio);
- file->private_data = NULL;
-
- if (fhp->vnext) {
- fhp->vnext->vprev = fhp->vprev;
- } else {
- vp->vlast = fhp->vprev;
- }
- if (fhp->vprev) {
- fhp->vprev->vnext = fhp->vnext;
- } else {
- vp->vfirst = fhp->vnext;
- }
- fhp->vnext = NULL;
- fhp->vprev = NULL;
- fhp->vhead = NULL;
- pvr2_channel_done(&fhp->channel);
- pvr2_trace(PVR2_TRACE_STRUCT,
- "Destroying pvr_v4l2_fh id=%p",fhp);
- if (fhp->input_map) {
- kfree(fhp->input_map);
- fhp->input_map = NULL;
- }
- kfree(fhp);
- if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
- pvr2_v4l2_destroy_no_lock(vp);
- }
- return 0;
-}
-
-
-static int pvr2_v4l2_open(struct file *file)
-{
- struct pvr2_v4l2_dev *dip; /* Our own context pointer */
- struct pvr2_v4l2_fh *fhp;
- struct pvr2_v4l2 *vp;
- struct pvr2_hdw *hdw;
- unsigned int input_mask = 0;
- unsigned int input_cnt,idx;
- int ret = 0;
-
- dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
-
- vp = dip->v4lp;
- hdw = vp->channel.hdw;
-
- pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
-
- if (!pvr2_hdw_dev_ok(hdw)) {
- pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
- "pvr2_v4l2_open: hardware not ready");
- return -EIO;
- }
-
- fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
- if (!fhp) {
- return -ENOMEM;
- }
-
- init_waitqueue_head(&fhp->wait_data);
- fhp->pdi = dip;
-
- pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
- pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
-
- if (dip->v4l_type == VFL_TYPE_RADIO) {
- /* Opening device as a radio, legal input selection subset
- is just the radio. */
- input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
- } else {
- /* Opening the main V4L device, legal input selection
- subset includes all analog inputs. */
- input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
- (1 << PVR2_CVAL_INPUT_TV) |
- (1 << PVR2_CVAL_INPUT_COMPOSITE) |
- (1 << PVR2_CVAL_INPUT_SVIDEO));
- }
- ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
- if (ret) {
- pvr2_channel_done(&fhp->channel);
- pvr2_trace(PVR2_TRACE_STRUCT,
- "Destroying pvr_v4l2_fh id=%p (input mask error)",
- fhp);
-
- kfree(fhp);
- return ret;
- }
-
- input_mask &= pvr2_hdw_get_input_available(hdw);
- input_cnt = 0;
- for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
- if (input_mask & (1 << idx)) input_cnt++;
- }
- fhp->input_cnt = input_cnt;
- fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
- if (!fhp->input_map) {
- pvr2_channel_done(&fhp->channel);
- pvr2_trace(PVR2_TRACE_STRUCT,
- "Destroying pvr_v4l2_fh id=%p (input map failure)",
- fhp);
- kfree(fhp);
- return -ENOMEM;
- }
- input_cnt = 0;
- for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
- if (!(input_mask & (1 << idx))) continue;
- fhp->input_map[input_cnt++] = idx;
- }
-
- fhp->vnext = NULL;
- fhp->vprev = vp->vlast;
- if (vp->vlast) {
- vp->vlast->vnext = fhp;
- } else {
- vp->vfirst = fhp;
- }
- vp->vlast = fhp;
- fhp->vhead = vp;
-
- fhp->file = file;
- file->private_data = fhp;
- v4l2_prio_open(&vp->prio, &fhp->prio);
-
- fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
-
- return 0;
-}
-
-
-static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
-{
- wake_up(&fhp->wait_data);
-}
-
-static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
-{
- int ret;
- struct pvr2_stream *sp;
- struct pvr2_hdw *hdw;
- if (fh->rhp) return 0;
-
- if (!fh->pdi->stream) {
- /* No stream defined for this node. This means that we're
- not currently allowed to stream from this node. */
- return -EPERM;
- }
-
- /* First read() attempt. Try to claim the stream and start
- it... */
- if ((ret = pvr2_channel_claim_stream(&fh->channel,
- fh->pdi->stream)) != 0) {
- /* Someone else must already have it */
- return ret;
- }
-
- fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
- if (!fh->rhp) {
- pvr2_channel_claim_stream(&fh->channel,NULL);
- return -ENOMEM;
- }
-
- hdw = fh->channel.mc_head->hdw;
- sp = fh->pdi->stream->stream;
- pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
- pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
- if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
- return pvr2_ioread_set_enabled(fh->rhp,!0);
-}
-
-
-static ssize_t pvr2_v4l2_read(struct file *file,
- char __user *buff, size_t count, loff_t *ppos)
-{
- struct pvr2_v4l2_fh *fh = file->private_data;
- int ret;
-
- if (fh->fw_mode_flag) {
- struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- char *tbuf;
- int c1,c2;
- int tcnt = 0;
- unsigned int offs = *ppos;
-
- tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
- if (!tbuf) return -ENOMEM;
-
- while (count) {
- c1 = count;
- if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
- c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
- if (c2 < 0) {
- tcnt = c2;
- break;
- }
- if (!c2) break;
- if (copy_to_user(buff,tbuf,c2)) {
- tcnt = -EFAULT;
- break;
- }
- offs += c2;
- tcnt += c2;
- buff += c2;
- count -= c2;
- *ppos += c2;
- }
- kfree(tbuf);
- return tcnt;
- }
-
- if (!fh->rhp) {
- ret = pvr2_v4l2_iosetup(fh);
- if (ret) {
- return ret;
- }
- }
-
- for (;;) {
- ret = pvr2_ioread_read(fh->rhp,buff,count);
- if (ret >= 0) break;
- if (ret != -EAGAIN) break;
- if (file->f_flags & O_NONBLOCK) break;
- /* Doing blocking I/O. Wait here. */
- ret = wait_event_interruptible(
- fh->wait_data,
- pvr2_ioread_avail(fh->rhp) >= 0);
- if (ret < 0) break;
- }
-
- return ret;
-}
-
-
-static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
-{
- unsigned int mask = 0;
- struct pvr2_v4l2_fh *fh = file->private_data;
- int ret;
-
- if (fh->fw_mode_flag) {
- mask |= POLLIN | POLLRDNORM;
- return mask;
- }
-
- if (!fh->rhp) {
- ret = pvr2_v4l2_iosetup(fh);
- if (ret) return POLLERR;
- }
-
- poll_wait(file,&fh->wait_data,wait);
-
- if (pvr2_ioread_avail(fh->rhp) >= 0) {
- mask |= POLLIN | POLLRDNORM;
- }
-
- return mask;
-}
-
-
-static const struct v4l2_file_operations vdev_fops = {
- .owner = THIS_MODULE,
- .open = pvr2_v4l2_open,
- .release = pvr2_v4l2_release,
- .read = pvr2_v4l2_read,
- .ioctl = pvr2_v4l2_ioctl,
- .poll = pvr2_v4l2_poll,
-};
-
-
-static struct video_device vdev_template = {
- .fops = &vdev_fops,
-};
-
-
-static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
- struct pvr2_v4l2 *vp,
- int v4l_type)
-{
- struct usb_device *usbdev;
- int mindevnum;
- int unit_number;
- struct pvr2_hdw *hdw;
- int *nr_ptr = NULL;
- dip->v4lp = vp;
-
- hdw = vp->channel.mc_head->hdw;
- usbdev = pvr2_hdw_get_dev(hdw);
- dip->v4l_type = v4l_type;
- switch (v4l_type) {
- case VFL_TYPE_GRABBER:
- dip->stream = &vp->channel.mc_head->video_stream;
- dip->config = pvr2_config_mpeg;
- dip->minor_type = pvr2_v4l_type_video;
- nr_ptr = video_nr;
- if (!dip->stream) {
- pr_err(KBUILD_MODNAME
- ": Failed to set up pvrusb2 v4l video dev"
- " due to missing stream instance\n");
- return;
- }
- break;
- case VFL_TYPE_VBI:
- dip->config = pvr2_config_vbi;
- dip->minor_type = pvr2_v4l_type_vbi;
- nr_ptr = vbi_nr;
- break;
- case VFL_TYPE_RADIO:
- dip->stream = &vp->channel.mc_head->video_stream;
- dip->config = pvr2_config_mpeg;
- dip->minor_type = pvr2_v4l_type_radio;
- nr_ptr = radio_nr;
- break;
- default:
- /* Bail out (this should be impossible) */
- pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
- " due to unrecognized config\n");
- return;
- }
-
- memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template));
- dip->devbase.release = pvr2_video_device_release;
- dip->devbase.ioctl_ops = &pvr2_ioctl_ops;
- {
- int val;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,
- PVR2_CID_STDAVAIL), &val);
- dip->devbase.tvnorms = (v4l2_std_id)val;
- }
-
- mindevnum = -1;
- unit_number = pvr2_hdw_get_unit_number(hdw);
- if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
- mindevnum = nr_ptr[unit_number];
- }
- dip->devbase.parent = &usbdev->dev;
- if ((video_register_device(&dip->devbase,
- dip->v4l_type, mindevnum) < 0) &&
- (video_register_device(&dip->devbase,
- dip->v4l_type, -1) < 0)) {
- pr_err(KBUILD_MODNAME
- ": Failed to register pvrusb2 v4l device\n");
- }
-
- printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
- video_device_node_name(&dip->devbase),
- pvr2_config_get_name(dip->config));
-
- pvr2_hdw_v4l_store_minor_number(hdw,
- dip->minor_type,dip->devbase.minor);
-}
-
-
-struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
-{
- struct pvr2_v4l2 *vp;
-
- vp = kzalloc(sizeof(*vp),GFP_KERNEL);
- if (!vp) return vp;
- pvr2_channel_init(&vp->channel,mnp);
- pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
-
- vp->channel.check_func = pvr2_v4l2_internal_check;
-
- /* register streams */
- vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
- if (!vp->dev_video) goto fail;
- pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
- if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
- (1 << PVR2_CVAL_INPUT_RADIO)) {
- vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
- if (!vp->dev_radio) goto fail;
- pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
- }
-
- return vp;
- fail:
- pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
- pvr2_v4l2_destroy_no_lock(vp);
- return NULL;
-}
-
-/*
- Stuff for Emacs to see, in order to encourage consistent editing style:
- *** Local Variables: ***
- *** mode: c ***
- *** fill-column: 75 ***
- *** tab-width: 8 ***
- *** c-basic-offset: 8 ***
- *** End: ***
- */
diff --git a/drivers/media/video/pwc/Makefile b/drivers/media/video/pwc/Makefile
deleted file mode 100644
index f5c8ec261e87..000000000000
--- a/drivers/media/video/pwc/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o
-pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o
-
-obj-$(CONFIG_USB_PWC) += pwc.o
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
deleted file mode 100644
index de7c7ba99ef4..000000000000
--- a/drivers/media/video/pwc/pwc-if.c
+++ /dev/null
@@ -1,1165 +0,0 @@
-/* Linux driver for Philips webcam
- USB and Video4Linux interface part.
- (C) 1999-2004 Nemosoft Unv.
- (C) 2004-2006 Luc Saillard (luc@saillard.org)
- (C) 2011 Hans de Goede <hdegoede@redhat.com>
-
- NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
- driver and thus may have bugs that are not present in the original version.
- Please send bug reports and support requests to <luc@saillard.org>.
- The decompression routines have been implemented by reverse-engineering the
- Nemosoft binary pwcx module. Caveat emptor.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-*/
-
-/*
- This code forms the interface between the USB layers and the Philips
- specific stuff. Some adanved stuff of the driver falls under an
- NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and
- is thus not distributed in source form. The binary pwcx.o module
- contains the code that falls under the NDA.
-
- In case you're wondering: 'pwc' stands for "Philips WebCam", but
- I really didn't want to type 'philips_web_cam' every time (I'm lazy as
- any Linux kernel hacker, but I don't like uncomprehensible abbreviations
- without explanation).
-
- Oh yes, convention: to disctinguish between all the various pointers to
- device-structures, I use these names for the pointer variables:
- udev: struct usb_device *
- vdev: struct video_device (member of pwc_dev)
- pdev: struct pwc_devive *
-*/
-
-/* Contributors:
- - Alvarado: adding whitebalance code
- - Alistar Moire: QuickCam 3000 Pro device/product ID
- - Tony Hoyle: Creative Labs Webcam 5 device/product ID
- - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
- - Jk Fang: Sotec Afina Eye ID
- - Xavier Roche: QuickCam Pro 4000 ID
- - Jens Knudsen: QuickCam Zoom ID
- - J. Debert: QuickCam for Notebooks ID
- - Pham Thanh Nam: webcam snapshot button as an event input device
-*/
-
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/poll.h>
-#include <linux/slab.h>
-#ifdef CONFIG_USB_PWC_INPUT_EVDEV
-#include <linux/usb/input.h>
-#endif
-#include <linux/vmalloc.h>
-#include <asm/io.h>
-#include <linux/kernel.h> /* simple_strtol() */
-
-#include "pwc.h"
-#include "pwc-kiara.h"
-#include "pwc-timon.h"
-#include "pwc-dec23.h"
-#include "pwc-dec1.h"
-
-/* Function prototypes and driver templates */
-
-/* hotplug device table support */
-static const struct usb_device_id pwc_device_table [] = {
- { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */
- { USB_DEVICE(0x0471, 0x0303) },
- { USB_DEVICE(0x0471, 0x0304) },
- { USB_DEVICE(0x0471, 0x0307) },
- { USB_DEVICE(0x0471, 0x0308) },
- { USB_DEVICE(0x0471, 0x030C) },
- { USB_DEVICE(0x0471, 0x0310) },
- { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */
- { USB_DEVICE(0x0471, 0x0312) },
- { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */
- { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */
- { USB_DEVICE(0x069A, 0x0001) }, /* Askey */
- { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */
- { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */
- { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */
- { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */
- { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */
- { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */
- { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */
- { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */
- { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */
- { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */
- { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */
- { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */
- { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */
- { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */
- { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */
- { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */
- { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */
- { USB_DEVICE(0x0d81, 0x1900) },
- { }
-};
-MODULE_DEVICE_TABLE(usb, pwc_device_table);
-
-static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);
-static void usb_pwc_disconnect(struct usb_interface *intf);
-static void pwc_isoc_cleanup(struct pwc_device *pdev);
-
-static struct usb_driver pwc_driver = {
- .name = "Philips webcam", /* name */
- .id_table = pwc_device_table,
- .probe = usb_pwc_probe, /* probe() */
- .disconnect = usb_pwc_disconnect, /* disconnect() */
-};
-
-#define MAX_DEV_HINTS 20
-#define MAX_ISOC_ERRORS 20
-
-#ifdef CONFIG_USB_PWC_DEBUG
- int pwc_trace = PWC_DEBUG_LEVEL;
-#endif
-static int power_save = -1;
-static int leds[2] = { 100, 0 };
-
-/***/
-
-static const struct v4l2_file_operations pwc_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .read = vb2_fop_read,
- .poll = vb2_fop_poll,
- .mmap = vb2_fop_mmap,
- .unlocked_ioctl = video_ioctl2,
-};
-static struct video_device pwc_template = {
- .name = "Philips Webcam", /* Filled in later */
- .release = video_device_release_empty,
- .fops = &pwc_fops,
- .ioctl_ops = &pwc_ioctl_ops,
-};
-
-/***************************************************************************/
-/* Private functions */
-
-struct pwc_frame_buf *pwc_get_next_fill_buf(struct pwc_device *pdev)
-{
- unsigned long flags = 0;
- struct pwc_frame_buf *buf = NULL;
-
- spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
- if (list_empty(&pdev->queued_bufs))
- goto leave;
-
- buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list);
- list_del(&buf->list);
-leave:
- spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
- return buf;
-}
-
-static void pwc_snapshot_button(struct pwc_device *pdev, int down)
-{
- if (down) {
- PWC_TRACE("Snapshot button pressed.\n");
- } else {
- PWC_TRACE("Snapshot button released.\n");
- }
-
-#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- if (pdev->button_dev) {
- input_report_key(pdev->button_dev, KEY_CAMERA, down);
- input_sync(pdev->button_dev);
- }
-#endif
-}
-
-static void pwc_frame_complete(struct pwc_device *pdev)
-{
- struct pwc_frame_buf *fbuf = pdev->fill_buf;
-
- /* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus
- frames on the USB wire after an exposure change. This conditition is
- however detected in the cam and a bit is set in the header.
- */
- if (pdev->type == 730) {
- unsigned char *ptr = (unsigned char *)fbuf->data;
-
- if (ptr[1] == 1 && ptr[0] & 0x10) {
- PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
- pdev->drop_frames += 2;
- }
- if ((ptr[0] ^ pdev->vmirror) & 0x01) {
- pwc_snapshot_button(pdev, ptr[0] & 0x01);
- }
- if ((ptr[0] ^ pdev->vmirror) & 0x02) {
- if (ptr[0] & 0x02)
- PWC_TRACE("Image is mirrored.\n");
- else
- PWC_TRACE("Image is normal.\n");
- }
- pdev->vmirror = ptr[0] & 0x03;
- /* Sometimes the trailer of the 730 is still sent as a 4 byte packet
- after a short frame; this condition is filtered out specifically. A 4 byte
- frame doesn't make sense anyway.
- So we get either this sequence:
- drop_bit set -> 4 byte frame -> short frame -> good frame
- Or this one:
- drop_bit set -> short frame -> good frame
- So we drop either 3 or 2 frames in all!
- */
- if (fbuf->filled == 4)
- pdev->drop_frames++;
- } else if (pdev->type == 740 || pdev->type == 720) {
- unsigned char *ptr = (unsigned char *)fbuf->data;
- if ((ptr[0] ^ pdev->vmirror) & 0x01) {
- pwc_snapshot_button(pdev, ptr[0] & 0x01);
- }
- pdev->vmirror = ptr[0] & 0x03;
- }
-
- /* In case we were instructed to drop the frame, do so silently. */
- if (pdev->drop_frames > 0) {
- pdev->drop_frames--;
- } else {
- /* Check for underflow first */
- if (fbuf->filled < pdev->frame_total_size) {
- PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes);"
- " discarded.\n", fbuf->filled);
- } else {
- fbuf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
- fbuf->vb.v4l2_buf.sequence = pdev->vframe_count;
- vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE);
- pdev->fill_buf = NULL;
- pdev->vsync = 0;
- }
- } /* !drop_frames */
- pdev->vframe_count++;
-}
-
-/* This gets called for the Isochronous pipe (video). This is done in
- * interrupt time, so it has to be fast, not crash, and not stall. Neat.
- */
-static void pwc_isoc_handler(struct urb *urb)
-{
- struct pwc_device *pdev = (struct pwc_device *)urb->context;
- int i, fst, flen;
- unsigned char *iso_buf = NULL;
-
- if (urb->status == -ENOENT || urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN) {
- PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a");
- return;
- }
-
- if (pdev->fill_buf == NULL)
- pdev->fill_buf = pwc_get_next_fill_buf(pdev);
-
- if (urb->status != 0) {
- const char *errmsg;
-
- errmsg = "Unknown";
- switch(urb->status) {
- case -ENOSR: errmsg = "Buffer error (overrun)"; break;
- case -EPIPE: errmsg = "Stalled (device not responding)"; break;
- case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
- case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
- case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
- case -ETIME: errmsg = "Device does not respond"; break;
- }
- PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
- urb->status, errmsg);
- /* Give up after a number of contiguous errors */
- if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
- {
- PWC_ERROR("Too many ISOC errors, bailing out.\n");
- if (pdev->fill_buf) {
- vb2_buffer_done(&pdev->fill_buf->vb,
- VB2_BUF_STATE_ERROR);
- pdev->fill_buf = NULL;
- }
- }
- pdev->vsync = 0; /* Drop the current frame */
- goto handler_end;
- }
-
- /* Reset ISOC error counter. We did get here, after all. */
- pdev->visoc_errors = 0;
-
- /* vsync: 0 = don't copy data
- 1 = sync-hunt
- 2 = synched
- */
- /* Compact data */
- for (i = 0; i < urb->number_of_packets; i++) {
- fst = urb->iso_frame_desc[i].status;
- flen = urb->iso_frame_desc[i].actual_length;
- iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- if (fst != 0) {
- PWC_ERROR("Iso frame %d has error %d\n", i, fst);
- continue;
- }
- if (flen > 0 && pdev->vsync) {
- struct pwc_frame_buf *fbuf = pdev->fill_buf;
-
- if (pdev->vsync == 1) {
- do_gettimeofday(&fbuf->vb.v4l2_buf.timestamp);
- pdev->vsync = 2;
- }
-
- if (flen + fbuf->filled > pdev->frame_total_size) {
- PWC_ERROR("Frame overflow (%d > %d)\n",
- flen + fbuf->filled,
- pdev->frame_total_size);
- pdev->vsync = 0; /* Let's wait for an EOF */
- } else {
- memcpy(fbuf->data + fbuf->filled, iso_buf,
- flen);
- fbuf->filled += flen;
- }
- }
- if (flen < pdev->vlast_packet_size) {
- /* Shorter packet... end of frame */
- if (pdev->vsync == 2)
- pwc_frame_complete(pdev);
- if (pdev->fill_buf == NULL)
- pdev->fill_buf = pwc_get_next_fill_buf(pdev);
- if (pdev->fill_buf) {
- pdev->fill_buf->filled = 0;
- pdev->vsync = 1;
- }
- }
- pdev->vlast_packet_size = flen;
- }
-
-handler_end:
- i = usb_submit_urb(urb, GFP_ATOMIC);
- if (i != 0)
- PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
-}
-
-/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
-static int pwc_isoc_init(struct pwc_device *pdev)
-{
- struct usb_device *udev;
- struct urb *urb;
- int i, j, ret;
- struct usb_interface *intf;
- struct usb_host_interface *idesc = NULL;
- int compression = 0; /* 0..3 = uncompressed..high */
-
- pdev->vsync = 0;
- pdev->vlast_packet_size = 0;
- pdev->fill_buf = NULL;
- pdev->vframe_count = 0;
- pdev->visoc_errors = 0;
- udev = pdev->udev;
-
-retry:
- /* We first try with low compression and then retry with a higher
- compression setting if there is not enough bandwidth. */
- ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
- pdev->vframes, &compression, 1);
-
- /* Get the current alternate interface, adjust packet size */
- intf = usb_ifnum_to_if(udev, 0);
- if (intf)
- idesc = usb_altnum_to_altsetting(intf, pdev->valternate);
- if (!idesc)
- return -EIO;
-
- /* Search video endpoint */
- pdev->vmax_packet_size = -1;
- for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
- if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
- pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize);
- break;
- }
- }
-
- if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
- PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n");
- return -ENFILE; /* Odd error, that should be noticeable */
- }
-
- /* Set alternate interface */
- PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
- ret = usb_set_interface(pdev->udev, 0, pdev->valternate);
- if (ret == -ENOSPC && compression < 3) {
- compression++;
- goto retry;
- }
- if (ret < 0)
- return ret;
-
- /* Allocate and init Isochronuous urbs */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
- if (urb == NULL) {
- PWC_ERROR("Failed to allocate urb %d\n", i);
- pwc_isoc_cleanup(pdev);
- return -ENOMEM;
- }
- pdev->urbs[i] = urb;
- PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
-
- urb->interval = 1; // devik
- urb->dev = udev;
- urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint);
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- urb->transfer_buffer = usb_alloc_coherent(udev,
- ISO_BUFFER_SIZE,
- GFP_KERNEL,
- &urb->transfer_dma);
- if (urb->transfer_buffer == NULL) {
- PWC_ERROR("Failed to allocate urb buffer %d\n", i);
- pwc_isoc_cleanup(pdev);
- return -ENOMEM;
- }
- urb->transfer_buffer_length = ISO_BUFFER_SIZE;
- urb->complete = pwc_isoc_handler;
- urb->context = pdev;
- urb->start_frame = 0;
- urb->number_of_packets = ISO_FRAMES_PER_DESC;
- for (j = 0; j < ISO_FRAMES_PER_DESC; j++) {
- urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE;
- urb->iso_frame_desc[j].length = pdev->vmax_packet_size;
- }
- }
-
- /* link */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
- if (ret == -ENOSPC && compression < 3) {
- compression++;
- pwc_isoc_cleanup(pdev);
- goto retry;
- }
- if (ret) {
- PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
- pwc_isoc_cleanup(pdev);
- return ret;
- }
- PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
- }
-
- /* All is done... */
- PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
- return 0;
-}
-
-static void pwc_iso_stop(struct pwc_device *pdev)
-{
- int i;
-
- /* Unlinking ISOC buffers one by one */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- if (pdev->urbs[i]) {
- PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
- usb_kill_urb(pdev->urbs[i]);
- }
- }
-}
-
-static void pwc_iso_free(struct pwc_device *pdev)
-{
- int i;
-
- /* Freeing ISOC buffers one by one */
- for (i = 0; i < MAX_ISO_BUFS; i++) {
- if (pdev->urbs[i]) {
- PWC_DEBUG_MEMORY("Freeing URB\n");
- if (pdev->urbs[i]->transfer_buffer) {
- usb_free_coherent(pdev->udev,
- pdev->urbs[i]->transfer_buffer_length,
- pdev->urbs[i]->transfer_buffer,
- pdev->urbs[i]->transfer_dma);
- }
- usb_free_urb(pdev->urbs[i]);
- pdev->urbs[i] = NULL;
- }
- }
-}
-
-/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
-static void pwc_isoc_cleanup(struct pwc_device *pdev)
-{
- PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
-
- pwc_iso_stop(pdev);
- pwc_iso_free(pdev);
- usb_set_interface(pdev->udev, 0, 0);
-
- PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
-}
-
-/* Must be called with vb_queue_lock hold */
-static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
-{
- unsigned long flags = 0;
-
- spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
- while (!list_empty(&pdev->queued_bufs)) {
- struct pwc_frame_buf *buf;
-
- buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf,
- list);
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
- spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
-}
-
-#ifdef CONFIG_USB_PWC_DEBUG
-static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
-{
- switch(sensor_type) {
- case 0x00:
- return "Hyundai CMOS sensor";
- case 0x20:
- return "Sony CCD sensor + TDA8787";
- case 0x2E:
- return "Sony CCD sensor + Exas 98L59";
- case 0x2F:
- return "Sony CCD sensor + ADI 9804";
- case 0x30:
- return "Sharp CCD sensor + TDA8787";
- case 0x3E:
- return "Sharp CCD sensor + Exas 98L59";
- case 0x3F:
- return "Sharp CCD sensor + ADI 9804";
- case 0x40:
- return "UPA 1021 sensor";
- case 0x100:
- return "VGA sensor";
- case 0x101:
- return "PAL MR sensor";
- default:
- return "unknown type of sensor";
- }
-}
-#endif
-
-/***************************************************************************/
-/* Video4Linux functions */
-
-static void pwc_video_release(struct v4l2_device *v)
-{
- struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
-
- v4l2_ctrl_handler_free(&pdev->ctrl_handler);
- v4l2_device_unregister(&pdev->v4l2_dev);
- kfree(pdev->ctrl_buf);
- kfree(pdev);
-}
-
-/***************************************************************************/
-/* Videobuf2 operations */
-
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- int size;
-
- if (*nbuffers < MIN_FRAMES)
- *nbuffers = MIN_FRAMES;
- else if (*nbuffers > MAX_FRAMES)
- *nbuffers = MAX_FRAMES;
-
- *nplanes = 1;
-
- size = pwc_get_size(pdev, MAX_WIDTH, MAX_HEIGHT);
- sizes[0] = PAGE_ALIGN(pwc_image_sizes[size][0] *
- pwc_image_sizes[size][1] * 3 / 2);
-
- return 0;
-}
-
-static int buffer_init(struct vb2_buffer *vb)
-{
- struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
-
- /* need vmalloc since frame buffer > 128K */
- buf->data = vzalloc(PWC_FRAME_SIZE);
- if (buf->data == NULL)
- return -ENOMEM;
-
- return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
-
- /* Don't allow queing new buffers after device disconnection */
- if (!pdev->udev)
- return -ENODEV;
-
- return 0;
-}
-
-static int buffer_finish(struct vb2_buffer *vb)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
- struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
-
- /*
- * Application has called dqbuf and is getting back a buffer we've
- * filled, take the pwc data we've stored in buf->data and decompress
- * it into a usable format, storing the result in the vb2_buffer
- */
- return pwc_decompress(pdev, buf);
-}
-
-static void buffer_cleanup(struct vb2_buffer *vb)
-{
- struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
-
- vfree(buf->data);
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vb->vb2_queue);
- struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
- unsigned long flags = 0;
-
- /* Check the device has not disconnected between prep and queuing */
- if (!pdev->udev) {
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- return;
- }
-
- spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
- list_add_tail(&buf->list, &pdev->queued_bufs);
- spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
-}
-
-static int start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
- int r;
-
- if (!pdev->udev)
- return -ENODEV;
-
- if (mutex_lock_interruptible(&pdev->v4l2_lock))
- return -ERESTARTSYS;
- /* Turn on camera and set LEDS on */
- pwc_camera_power(pdev, 1);
- pwc_set_leds(pdev, leds[0], leds[1]);
-
- r = pwc_isoc_init(pdev);
- if (r) {
- /* If we failed turn camera and LEDS back off */
- pwc_set_leds(pdev, 0, 0);
- pwc_camera_power(pdev, 0);
- /* And cleanup any queued bufs!! */
- pwc_cleanup_queued_bufs(pdev);
- }
- mutex_unlock(&pdev->v4l2_lock);
-
- return r;
-}
-
-static int stop_streaming(struct vb2_queue *vq)
-{
- struct pwc_device *pdev = vb2_get_drv_priv(vq);
-
- if (mutex_lock_interruptible(&pdev->v4l2_lock))
- return -ERESTARTSYS;
- if (pdev->udev) {
- pwc_set_leds(pdev, 0, 0);
- pwc_camera_power(pdev, 0);
- pwc_isoc_cleanup(pdev);
- }
-
- pwc_cleanup_queued_bufs(pdev);
- mutex_unlock(&pdev->v4l2_lock);
-
- return 0;
-}
-
-static struct vb2_ops pwc_vb_queue_ops = {
- .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 = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
-};
-
-/***************************************************************************/
-/* USB functions */
-
-/* This function gets called when a new device is plugged in or the usb core
- * is loaded.
- */
-
-static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct pwc_device *pdev = NULL;
- int vendor_id, product_id, type_id;
- int rc;
- int features = 0;
- int compression = 0;
- int my_power_save = power_save;
- char serial_number[30], *name;
-
- vendor_id = le16_to_cpu(udev->descriptor.idVendor);
- product_id = le16_to_cpu(udev->descriptor.idProduct);
-
- /* Check if we can handle this device */
- PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
- vendor_id, product_id,
- intf->altsetting->desc.bInterfaceNumber);
-
- /* the interfaces are probed one by one. We are only interested in the
- video interface (0) now.
- Interface 1 is the Audio Control, and interface 2 Audio itself.
- */
- if (intf->altsetting->desc.bInterfaceNumber > 0)
- return -ENODEV;
-
- if (vendor_id == 0x0471) {
- switch (product_id) {
- case 0x0302:
- PWC_INFO("Philips PCA645VC USB webcam detected.\n");
- name = "Philips 645 webcam";
- type_id = 645;
- break;
- case 0x0303:
- PWC_INFO("Philips PCA646VC USB webcam detected.\n");
- name = "Philips 646 webcam";
- type_id = 646;
- break;
- case 0x0304:
- PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
- name = "Askey VC010 webcam";
- type_id = 646;
- break;
- case 0x0307:
- PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
- name = "Philips 675 webcam";
- type_id = 675;
- break;
- case 0x0308:
- PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
- name = "Philips 680 webcam";
- type_id = 680;
- break;
- case 0x030C:
- PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
- name = "Philips 690 webcam";
- type_id = 690;
- break;
- case 0x0310:
- PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
- name = "Philips 730 webcam";
- type_id = 730;
- break;
- case 0x0311:
- PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
- name = "Philips 740 webcam";
- type_id = 740;
- break;
- case 0x0312:
- PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
- name = "Philips 750 webcam";
- type_id = 750;
- break;
- case 0x0313:
- PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
- name = "Philips 720K/40 webcam";
- type_id = 720;
- break;
- case 0x0329:
- PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
- name = "Philips SPC 900NC webcam";
- type_id = 740;
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x069A) {
- switch(product_id) {
- case 0x0001:
- PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
- name = "Askey VC010 webcam";
- type_id = 645;
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x046d) {
- switch(product_id) {
- case 0x08b0:
- PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
- name = "Logitech QuickCam Pro 3000";
- type_id = 740; /* CCD sensor */
- break;
- case 0x08b1:
- PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
- name = "Logitech QuickCam Notebook Pro";
- type_id = 740; /* CCD sensor */
- break;
- case 0x08b2:
- PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
- name = "Logitech QuickCam Pro 4000";
- type_id = 740; /* CCD sensor */
- if (my_power_save == -1)
- my_power_save = 1;
- break;
- case 0x08b3:
- PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
- name = "Logitech QuickCam Zoom";
- type_id = 740; /* CCD sensor */
- break;
- case 0x08B4:
- PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
- name = "Logitech QuickCam Zoom";
- type_id = 740; /* CCD sensor */
- if (my_power_save == -1)
- my_power_save = 1;
- break;
- case 0x08b5:
- PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
- name = "Logitech QuickCam Orbit";
- type_id = 740; /* CCD sensor */
- if (my_power_save == -1)
- my_power_save = 1;
- features |= FEATURE_MOTOR_PANTILT;
- break;
- case 0x08b6:
- PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
- name = "Cisco VT Camera";
- type_id = 740; /* CCD sensor */
- break;
- case 0x08b7:
- PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
- name = "Logitech ViewPort AV 100";
- type_id = 740; /* CCD sensor */
- break;
- case 0x08b8: /* Where this released? */
- PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
- name = "Logitech QuickCam (res.)";
- type_id = 730; /* Assuming CMOS */
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x055d) {
- /* I don't know the difference between the C10 and the C30;
- I suppose the difference is the sensor, but both cameras
- work equally well with a type_id of 675
- */
- switch(product_id) {
- case 0x9000:
- PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
- name = "Samsung MPC-C10";
- type_id = 675;
- break;
- case 0x9001:
- PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
- name = "Samsung MPC-C30";
- type_id = 675;
- break;
- case 0x9002:
- PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
- name = "Samsung MPC-C30";
- type_id = 740;
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x041e) {
- switch(product_id) {
- case 0x400c:
- PWC_INFO("Creative Labs Webcam 5 detected.\n");
- name = "Creative Labs Webcam 5";
- type_id = 730;
- if (my_power_save == -1)
- my_power_save = 1;
- break;
- case 0x4011:
- PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
- name = "Creative Labs Webcam Pro Ex";
- type_id = 740;
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x04cc) {
- switch(product_id) {
- case 0x8116:
- PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
- name = "Sotec Afina Eye";
- type_id = 730;
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else if (vendor_id == 0x06be) {
- switch(product_id) {
- case 0x8116:
- /* This is essentially the same cam as the Sotec Afina Eye */
- PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
- name = "AME Co. Afina Eye";
- type_id = 750;
- break;
- default:
- return -ENODEV;
- break;
- }
-
- }
- else if (vendor_id == 0x0d81) {
- switch(product_id) {
- case 0x1900:
- PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
- name = "Visionite VCS-UC300";
- type_id = 740; /* CCD sensor */
- break;
- case 0x1910:
- PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
- name = "Visionite VCS-UM100";
- type_id = 730; /* CMOS sensor */
- break;
- default:
- return -ENODEV;
- break;
- }
- }
- else
- return -ENODEV; /* Not any of the know types; but the list keeps growing. */
-
- if (my_power_save == -1)
- my_power_save = 0;
-
- memset(serial_number, 0, 30);
- usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
- PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
-
- if (udev->descriptor.bNumConfigurations > 1)
- PWC_WARNING("Warning: more than 1 configuration available.\n");
-
- /* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
- pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL);
- if (pdev == NULL) {
- PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
- return -ENOMEM;
- }
- pdev->type = type_id;
- pdev->features = features;
- pwc_construct(pdev); /* set min/max sizes correct */
-
- mutex_init(&pdev->v4l2_lock);
- mutex_init(&pdev->vb_queue_lock);
- spin_lock_init(&pdev->queued_bufs_lock);
- INIT_LIST_HEAD(&pdev->queued_bufs);
-
- pdev->udev = udev;
- pdev->power_save = my_power_save;
-
- /* Init videobuf2 queue structure */
- memset(&pdev->vb_queue, 0, sizeof(pdev->vb_queue));
- pdev->vb_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pdev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;
- pdev->vb_queue.drv_priv = pdev;
- pdev->vb_queue.buf_struct_size = sizeof(struct pwc_frame_buf);
- pdev->vb_queue.ops = &pwc_vb_queue_ops;
- pdev->vb_queue.mem_ops = &vb2_vmalloc_memops;
- vb2_queue_init(&pdev->vb_queue);
-
- /* Init video_device structure */
- memcpy(&pdev->vdev, &pwc_template, sizeof(pwc_template));
- strcpy(pdev->vdev.name, name);
- pdev->vdev.queue = &pdev->vb_queue;
- pdev->vdev.queue->lock = &pdev->vb_queue_lock;
- set_bit(V4L2_FL_USE_FH_PRIO, &pdev->vdev.flags);
- video_set_drvdata(&pdev->vdev, pdev);
-
- pdev->release = le16_to_cpu(udev->descriptor.bcdDevice);
- PWC_DEBUG_PROBE("Release: %04x\n", pdev->release);
-
- /* Allocate USB command buffers */
- pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL);
- if (!pdev->ctrl_buf) {
- PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
- rc = -ENOMEM;
- goto err_free_mem;
- }
-
-#ifdef CONFIG_USB_PWC_DEBUG
- /* Query sensor type */
- if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
- PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
- pdev->vdev.name,
- pwc_sensor_type_to_string(rc), rc);
- }
-#endif
-
- /* Set the leds off */
- pwc_set_leds(pdev, 0, 0);
-
- /* Setup intial videomode */
- rc = pwc_set_video_mode(pdev, MAX_WIDTH, MAX_HEIGHT,
- V4L2_PIX_FMT_YUV420, 30, &compression, 1);
- if (rc)
- goto err_free_mem;
-
- /* Register controls (and read default values from camera */
- rc = pwc_init_controls(pdev);
- if (rc) {
- PWC_ERROR("Failed to register v4l2 controls (%d).\n", rc);
- goto err_free_mem;
- }
-
- /* And powerdown the camera until streaming starts */
- pwc_camera_power(pdev, 0);
-
- /* Register the v4l2_device structure */
- pdev->v4l2_dev.release = pwc_video_release;
- rc = v4l2_device_register(&intf->dev, &pdev->v4l2_dev);
- if (rc) {
- PWC_ERROR("Failed to register v4l2-device (%d).\n", rc);
- goto err_free_controls;
- }
-
- pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
- pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
- pdev->vdev.lock = &pdev->v4l2_lock;
-
- rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
- if (rc < 0) {
- PWC_ERROR("Failed to register as video device (%d).\n", rc);
- goto err_unregister_v4l2_dev;
- }
- PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
-
-#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- /* register webcam snapshot button input device */
- pdev->button_dev = input_allocate_device();
- if (!pdev->button_dev) {
- PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
- rc = -ENOMEM;
- goto err_video_unreg;
- }
-
- usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
- strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
-
- pdev->button_dev->name = "PWC snapshot button";
- pdev->button_dev->phys = pdev->button_phys;
- usb_to_input_id(pdev->udev, &pdev->button_dev->id);
- pdev->button_dev->dev.parent = &pdev->udev->dev;
- pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
- pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
-
- rc = input_register_device(pdev->button_dev);
- if (rc) {
- input_free_device(pdev->button_dev);
- pdev->button_dev = NULL;
- goto err_video_unreg;
- }
-#endif
-
- return 0;
-
-err_video_unreg:
- video_unregister_device(&pdev->vdev);
-err_unregister_v4l2_dev:
- v4l2_device_unregister(&pdev->v4l2_dev);
-err_free_controls:
- v4l2_ctrl_handler_free(&pdev->ctrl_handler);
-err_free_mem:
- kfree(pdev->ctrl_buf);
- kfree(pdev);
- return rc;
-}
-
-/* The user yanked out the cable... */
-static void usb_pwc_disconnect(struct usb_interface *intf)
-{
- struct v4l2_device *v = usb_get_intfdata(intf);
- struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
-
- mutex_lock(&pdev->vb_queue_lock);
- mutex_lock(&pdev->v4l2_lock);
- /* No need to keep the urbs around after disconnection */
- if (pdev->vb_queue.streaming)
- pwc_isoc_cleanup(pdev);
- pdev->udev = NULL;
- pwc_cleanup_queued_bufs(pdev);
-
- v4l2_device_disconnect(&pdev->v4l2_dev);
- video_unregister_device(&pdev->vdev);
- mutex_unlock(&pdev->v4l2_lock);
- mutex_unlock(pdev->vb_queue.lock);
-
-#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- if (pdev->button_dev)
- input_unregister_device(pdev->button_dev);
-#endif
-
- v4l2_device_put(&pdev->v4l2_dev);
-}
-
-
-/*
- * Initialization code & module stuff
- */
-
-static unsigned int leds_nargs;
-
-#ifdef CONFIG_USB_PWC_DEBUG
-module_param_named(trace, pwc_trace, int, 0644);
-#endif
-module_param(power_save, int, 0644);
-module_param_array(leds, int, &leds_nargs, 0444);
-
-#ifdef CONFIG_USB_PWC_DEBUG
-MODULE_PARM_DESC(trace, "For debugging purposes");
-#endif
-MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
-MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
-
-MODULE_DESCRIPTION("Philips & OEM USB webcam driver");
-MODULE_AUTHOR("Luc Saillard <luc@saillard.org>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("pwcx");
-MODULE_VERSION( PWC_VERSION );
-
-module_usb_driver(pwc_driver);
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
deleted file mode 100644
index f6419b22c258..000000000000
--- a/drivers/media/video/rj54n1cb0c.c
+++ /dev/null
@@ -1,1414 +0,0 @@
-/*
- * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp
- *
- * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * 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/delay.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/rj54n1cb0c.h>
-#include <media/soc_camera.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-
-#define RJ54N1_DEV_CODE 0x0400
-#define RJ54N1_DEV_CODE2 0x0401
-#define RJ54N1_OUT_SEL 0x0403
-#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404
-#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405
-#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406
-#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407
-#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408
-#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409
-#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a
-#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b
-#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c
-#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d
-#define RJ54N1_RESIZE_N 0x040e
-#define RJ54N1_RESIZE_N_STEP 0x040f
-#define RJ54N1_RESIZE_STEP 0x0410
-#define RJ54N1_RESIZE_HOLD_H 0x0411
-#define RJ54N1_RESIZE_HOLD_L 0x0412
-#define RJ54N1_H_OBEN_OFS 0x0413
-#define RJ54N1_V_OBEN_OFS 0x0414
-#define RJ54N1_RESIZE_CONTROL 0x0415
-#define RJ54N1_STILL_CONTROL 0x0417
-#define RJ54N1_INC_USE_SEL_H 0x0425
-#define RJ54N1_INC_USE_SEL_L 0x0426
-#define RJ54N1_MIRROR_STILL_MODE 0x0427
-#define RJ54N1_INIT_START 0x0428
-#define RJ54N1_SCALE_1_2_LEV 0x0429
-#define RJ54N1_SCALE_4_LEV 0x042a
-#define RJ54N1_Y_GAIN 0x04d8
-#define RJ54N1_APT_GAIN_UP 0x04fa
-#define RJ54N1_RA_SEL_UL 0x0530
-#define RJ54N1_BYTE_SWAP 0x0531
-#define RJ54N1_OUT_SIGPO 0x053b
-#define RJ54N1_WB_SEL_WEIGHT_I 0x054e
-#define RJ54N1_BIT8_WB 0x0569
-#define RJ54N1_HCAPS_WB 0x056a
-#define RJ54N1_VCAPS_WB 0x056b
-#define RJ54N1_HCAPE_WB 0x056c
-#define RJ54N1_VCAPE_WB 0x056d
-#define RJ54N1_EXPOSURE_CONTROL 0x058c
-#define RJ54N1_FRAME_LENGTH_S_H 0x0595
-#define RJ54N1_FRAME_LENGTH_S_L 0x0596
-#define RJ54N1_FRAME_LENGTH_P_H 0x0597
-#define RJ54N1_FRAME_LENGTH_P_L 0x0598
-#define RJ54N1_PEAK_H 0x05b7
-#define RJ54N1_PEAK_50 0x05b8
-#define RJ54N1_PEAK_60 0x05b9
-#define RJ54N1_PEAK_DIFF 0x05ba
-#define RJ54N1_IOC 0x05ef
-#define RJ54N1_TG_BYPASS 0x0700
-#define RJ54N1_PLL_L 0x0701
-#define RJ54N1_PLL_N 0x0702
-#define RJ54N1_PLL_EN 0x0704
-#define RJ54N1_RATIO_TG 0x0706
-#define RJ54N1_RATIO_T 0x0707
-#define RJ54N1_RATIO_R 0x0708
-#define RJ54N1_RAMP_TGCLK_EN 0x0709
-#define RJ54N1_OCLK_DSP 0x0710
-#define RJ54N1_RATIO_OP 0x0711
-#define RJ54N1_RATIO_O 0x0712
-#define RJ54N1_OCLK_SEL_EN 0x0713
-#define RJ54N1_CLK_RST 0x0717
-#define RJ54N1_RESET_STANDBY 0x0718
-#define RJ54N1_FWFLG 0x07fe
-
-#define E_EXCLK (1 << 7)
-#define SOFT_STDBY (1 << 4)
-#define SEN_RSTX (1 << 2)
-#define TG_RSTX (1 << 1)
-#define DSP_RSTX (1 << 0)
-
-#define RESIZE_HOLD_SEL (1 << 2)
-#define RESIZE_GO (1 << 1)
-
-/*
- * When cropping, the camera automatically centers the cropped region, there
- * doesn't seem to be a way to specify an explicit location of the rectangle.
- */
-#define RJ54N1_COLUMN_SKIP 0
-#define RJ54N1_ROW_SKIP 0
-#define RJ54N1_MAX_WIDTH 1600
-#define RJ54N1_MAX_HEIGHT 1200
-
-#define PLL_L 2
-#define PLL_N 0x31
-
-/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */
-
-/* RJ54N1CB0C has only one fixed colorspace per pixelcode */
-struct rj54n1_datafmt {
- enum v4l2_mbus_pixelcode code;
- enum v4l2_colorspace colorspace;
-};
-
-/* Find a data format by a pixel code in an array */
-static const struct rj54n1_datafmt *rj54n1_find_datafmt(
- enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt,
- int n)
-{
- int i;
- for (i = 0; i < n; i++)
- if (fmt[i].code == code)
- return fmt + i;
-
- return NULL;
-}
-
-static const struct rj54n1_datafmt rj54n1_colour_fmts[] = {
- {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG},
- {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB},
- {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB},
-};
-
-struct rj54n1_clock_div {
- u8 ratio_tg; /* can be 0 or an odd number */
- u8 ratio_t;
- u8 ratio_r;
- u8 ratio_op;
- u8 ratio_o;
-};
-
-struct rj54n1 {
- struct v4l2_subdev subdev;
- struct v4l2_ctrl_handler hdl;
- struct rj54n1_clock_div clk_div;
- const struct rj54n1_datafmt *fmt;
- struct v4l2_rect rect; /* Sensor window */
- unsigned int tgclk_mhz;
- bool auto_wb;
- unsigned short width; /* Output window */
- unsigned short height;
- unsigned short resize; /* Sensor * 1024 / resize = Output */
- unsigned short scale;
- u8 bank;
-};
-
-struct rj54n1_reg_val {
- u16 reg;
- u8 val;
-};
-
-static const struct rj54n1_reg_val bank_4[] = {
- {0x417, 0},
- {0x42c, 0},
- {0x42d, 0xf0},
- {0x42e, 0},
- {0x42f, 0x50},
- {0x430, 0xf5},
- {0x431, 0x16},
- {0x432, 0x20},
- {0x433, 0},
- {0x434, 0xc8},
- {0x43c, 8},
- {0x43e, 0x90},
- {0x445, 0x83},
- {0x4ba, 0x58},
- {0x4bb, 4},
- {0x4bc, 0x20},
- {0x4db, 4},
- {0x4fe, 2},
-};
-
-static const struct rj54n1_reg_val bank_5[] = {
- {0x514, 0},
- {0x516, 0},
- {0x518, 0},
- {0x51a, 0},
- {0x51d, 0xff},
- {0x56f, 0x28},
- {0x575, 0x40},
- {0x5bc, 0x48},
- {0x5c1, 6},
- {0x5e5, 0x11},
- {0x5e6, 0x43},
- {0x5e7, 0x33},
- {0x5e8, 0x21},
- {0x5e9, 0x30},
- {0x5ea, 0x0},
- {0x5eb, 0xa5},
- {0x5ec, 0xff},
- {0x5fe, 2},
-};
-
-static const struct rj54n1_reg_val bank_7[] = {
- {0x70a, 0},
- {0x714, 0xff},
- {0x715, 0xff},
- {0x716, 0x1f},
- {0x7FE, 2},
-};
-
-static const struct rj54n1_reg_val bank_8[] = {
- {0x800, 0x00},
- {0x801, 0x01},
- {0x802, 0x61},
- {0x805, 0x00},
- {0x806, 0x00},
- {0x807, 0x00},
- {0x808, 0x00},
- {0x809, 0x01},
- {0x80A, 0x61},
- {0x80B, 0x00},
- {0x80C, 0x01},
- {0x80D, 0x00},
- {0x80E, 0x00},
- {0x80F, 0x00},
- {0x810, 0x00},
- {0x811, 0x01},
- {0x812, 0x61},
- {0x813, 0x00},
- {0x814, 0x11},
- {0x815, 0x00},
- {0x816, 0x41},
- {0x817, 0x00},
- {0x818, 0x51},
- {0x819, 0x01},
- {0x81A, 0x1F},
- {0x81B, 0x00},
- {0x81C, 0x01},
- {0x81D, 0x00},
- {0x81E, 0x11},
- {0x81F, 0x00},
- {0x820, 0x41},
- {0x821, 0x00},
- {0x822, 0x51},
- {0x823, 0x00},
- {0x824, 0x00},
- {0x825, 0x00},
- {0x826, 0x47},
- {0x827, 0x01},
- {0x828, 0x4F},
- {0x829, 0x00},
- {0x82A, 0x00},
- {0x82B, 0x00},
- {0x82C, 0x30},
- {0x82D, 0x00},
- {0x82E, 0x40},
- {0x82F, 0x00},
- {0x830, 0xB3},
- {0x831, 0x00},
- {0x832, 0xE3},
- {0x833, 0x00},
- {0x834, 0x00},
- {0x835, 0x00},
- {0x836, 0x00},
- {0x837, 0x00},
- {0x838, 0x00},
- {0x839, 0x01},
- {0x83A, 0x61},
- {0x83B, 0x00},
- {0x83C, 0x01},
- {0x83D, 0x00},
- {0x83E, 0x00},
- {0x83F, 0x00},
- {0x840, 0x00},
- {0x841, 0x01},
- {0x842, 0x61},
- {0x843, 0x00},
- {0x844, 0x1D},
- {0x845, 0x00},
- {0x846, 0x00},
- {0x847, 0x00},
- {0x848, 0x00},
- {0x849, 0x01},
- {0x84A, 0x1F},
- {0x84B, 0x00},
- {0x84C, 0x05},
- {0x84D, 0x00},
- {0x84E, 0x19},
- {0x84F, 0x01},
- {0x850, 0x21},
- {0x851, 0x01},
- {0x852, 0x5D},
- {0x853, 0x00},
- {0x854, 0x00},
- {0x855, 0x00},
- {0x856, 0x19},
- {0x857, 0x01},
- {0x858, 0x21},
- {0x859, 0x00},
- {0x85A, 0x00},
- {0x85B, 0x00},
- {0x85C, 0x00},
- {0x85D, 0x00},
- {0x85E, 0x00},
- {0x85F, 0x00},
- {0x860, 0xB3},
- {0x861, 0x00},
- {0x862, 0xE3},
- {0x863, 0x00},
- {0x864, 0x00},
- {0x865, 0x00},
- {0x866, 0x00},
- {0x867, 0x00},
- {0x868, 0x00},
- {0x869, 0xE2},
- {0x86A, 0x00},
- {0x86B, 0x01},
- {0x86C, 0x06},
- {0x86D, 0x00},
- {0x86E, 0x00},
- {0x86F, 0x00},
- {0x870, 0x60},
- {0x871, 0x8C},
- {0x872, 0x10},
- {0x873, 0x00},
- {0x874, 0xE0},
- {0x875, 0x00},
- {0x876, 0x27},
- {0x877, 0x01},
- {0x878, 0x00},
- {0x879, 0x00},
- {0x87A, 0x00},
- {0x87B, 0x03},
- {0x87C, 0x00},
- {0x87D, 0x00},
- {0x87E, 0x00},
- {0x87F, 0x00},
- {0x880, 0x00},
- {0x881, 0x00},
- {0x882, 0x00},
- {0x883, 0x00},
- {0x884, 0x00},
- {0x885, 0x00},
- {0x886, 0xF8},
- {0x887, 0x00},
- {0x888, 0x03},
- {0x889, 0x00},
- {0x88A, 0x64},
- {0x88B, 0x00},
- {0x88C, 0x03},
- {0x88D, 0x00},
- {0x88E, 0xB1},
- {0x88F, 0x00},
- {0x890, 0x03},
- {0x891, 0x01},
- {0x892, 0x1D},
- {0x893, 0x00},
- {0x894, 0x03},
- {0x895, 0x01},
- {0x896, 0x4B},
- {0x897, 0x00},
- {0x898, 0xE5},
- {0x899, 0x00},
- {0x89A, 0x01},
- {0x89B, 0x00},
- {0x89C, 0x01},
- {0x89D, 0x04},
- {0x89E, 0xC8},
- {0x89F, 0x00},
- {0x8A0, 0x01},
- {0x8A1, 0x01},
- {0x8A2, 0x61},
- {0x8A3, 0x00},
- {0x8A4, 0x01},
- {0x8A5, 0x00},
- {0x8A6, 0x00},
- {0x8A7, 0x00},
- {0x8A8, 0x00},
- {0x8A9, 0x00},
- {0x8AA, 0x7F},
- {0x8AB, 0x03},
- {0x8AC, 0x00},
- {0x8AD, 0x00},
- {0x8AE, 0x00},
- {0x8AF, 0x00},
- {0x8B0, 0x00},
- {0x8B1, 0x00},
- {0x8B6, 0x00},
- {0x8B7, 0x01},
- {0x8B8, 0x00},
- {0x8B9, 0x00},
- {0x8BA, 0x02},
- {0x8BB, 0x00},
- {0x8BC, 0xFF},
- {0x8BD, 0x00},
- {0x8FE, 2},
-};
-
-static const struct rj54n1_reg_val bank_10[] = {
- {0x10bf, 0x69}
-};
-
-/* Clock dividers - these are default register values, divider = register + 1 */
-static const struct rj54n1_clock_div clk_div = {
- .ratio_tg = 3 /* default: 5 */,
- .ratio_t = 4 /* default: 1 */,
- .ratio_r = 4 /* default: 0 */,
- .ratio_op = 1 /* default: 5 */,
- .ratio_o = 9 /* default: 0 */,
-};
-
-static struct rj54n1 *to_rj54n1(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct rj54n1, subdev);
-}
-
-static int reg_read(struct i2c_client *client, const u16 reg)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* set bank */
- if (rj54n1->bank != reg >> 8) {
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
- ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
- if (ret < 0)
- return ret;
- rj54n1->bank = reg >> 8;
- }
- return i2c_smbus_read_byte_data(client, reg & 0xff);
-}
-
-static int reg_write(struct i2c_client *client, const u16 reg,
- const u8 data)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* set bank */
- if (rj54n1->bank != reg >> 8) {
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8);
- ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8);
- if (ret < 0)
- return ret;
- rj54n1->bank = reg >> 8;
- }
- dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data);
- return i2c_smbus_write_byte_data(client, reg & 0xff, data);
-}
-
-static int reg_set(struct i2c_client *client, const u16 reg,
- const u8 data, const u8 mask)
-{
- int ret;
-
- ret = reg_read(client, reg);
- if (ret < 0)
- return ret;
- return reg_write(client, reg, (ret & ~mask) | (data & mask));
-}
-
-static int reg_write_multiple(struct i2c_client *client,
- const struct rj54n1_reg_val *rv, const int n)
-{
- int i, ret;
-
- for (i = 0; i < n; i++) {
- ret = reg_write(client, rv->reg, rv->val);
- if (ret < 0)
- return ret;
- rv++;
- }
-
- return 0;
-}
-
-static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index >= ARRAY_SIZE(rj54n1_colour_fmts))
- return -EINVAL;
-
- *code = rj54n1_colour_fmts[index].code;
- return 0;
-}
-
-static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* Switch between preview and still shot modes */
- return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80);
-}
-
-static int rj54n1_set_rect(struct i2c_client *client,
- u16 reg_x, u16 reg_y, u16 reg_xy,
- u32 width, u32 height)
-{
- int ret;
-
- ret = reg_write(client, reg_xy,
- ((width >> 4) & 0x70) |
- ((height >> 8) & 7));
-
- if (!ret)
- ret = reg_write(client, reg_x, width & 0xff);
- if (!ret)
- ret = reg_write(client, reg_y, height & 0xff);
-
- return ret;
-}
-
-/*
- * Some commands, specifically certain initialisation sequences, require
- * a commit operation.
- */
-static int rj54n1_commit(struct i2c_client *client)
-{
- int ret = reg_write(client, RJ54N1_INIT_START, 1);
- msleep(10);
- if (!ret)
- ret = reg_write(client, RJ54N1_INIT_START, 0);
- return ret;
-}
-
-static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
- s32 *out_w, s32 *out_h);
-
-static int rj54n1_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct v4l2_rect *rect = &a->c;
- int dummy = 0, output_w, output_h,
- input_w = rect->width, input_h = rect->height;
- int ret;
-
- /* arbitrary minimum width and height, edges unimportant */
- soc_camera_limit_side(&dummy, &input_w,
- RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH);
-
- soc_camera_limit_side(&dummy, &input_h,
- RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT);
-
- output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize;
- output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize;
-
- dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n",
- input_w, input_h, rj54n1->resize, output_w, output_h);
-
- ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
- if (ret < 0)
- return ret;
-
- rj54n1->width = output_w;
- rj54n1->height = output_h;
- rj54n1->resize = ret;
- rj54n1->rect.width = input_w;
- rj54n1->rect.height = input_h;
-
- return 0;
-}
-
-static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
-
- a->c = rj54n1->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- a->bounds.left = RJ54N1_COLUMN_SKIP;
- a->bounds.top = RJ54N1_ROW_SKIP;
- a->bounds.width = RJ54N1_MAX_WIDTH;
- a->bounds.height = RJ54N1_MAX_HEIGHT;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int rj54n1_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
-
- mf->code = rj54n1->fmt->code;
- mf->colorspace = rj54n1->fmt->colorspace;
- mf->field = V4L2_FIELD_NONE;
- mf->width = rj54n1->width;
- mf->height = rj54n1->height;
-
- return 0;
-}
-
-/*
- * The actual geometry configuration routine. It scales the input window into
- * the output one, updates the window sizes and returns an error or the resize
- * coefficient on success. Note: we only use the "Fixed Scaling" on this camera.
- */
-static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
- s32 *out_w, s32 *out_h)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- unsigned int skip, resize, input_w = *in_w, input_h = *in_h,
- output_w = *out_w, output_h = *out_h;
- u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom;
- unsigned int peak, peak_50, peak_60;
- int ret;
-
- /*
- * We have a problem with crops, where the window is larger than 512x384
- * and output window is larger than a half of the input one. In this
- * case we have to either reduce the input window to equal or below
- * 512x384 or the output window to equal or below 1/2 of the input.
- */
- if (output_w > max(512U, input_w / 2)) {
- if (2 * output_w > RJ54N1_MAX_WIDTH) {
- input_w = RJ54N1_MAX_WIDTH;
- output_w = RJ54N1_MAX_WIDTH / 2;
- } else {
- input_w = output_w * 2;
- }
-
- dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n",
- input_w, output_w);
- }
-
- if (output_h > max(384U, input_h / 2)) {
- if (2 * output_h > RJ54N1_MAX_HEIGHT) {
- input_h = RJ54N1_MAX_HEIGHT;
- output_h = RJ54N1_MAX_HEIGHT / 2;
- } else {
- input_h = output_h * 2;
- }
-
- dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n",
- input_h, output_h);
- }
-
- /* Idea: use the read mode for snapshots, handle separate geometries */
- ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L,
- RJ54N1_Y_OUTPUT_SIZE_S_L,
- RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h);
- if (!ret)
- ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L,
- RJ54N1_Y_OUTPUT_SIZE_P_L,
- RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h);
-
- if (ret < 0)
- return ret;
-
- if (output_w > input_w && output_h > input_h) {
- input_w = output_w;
- input_h = output_h;
-
- resize = 1024;
- } else {
- unsigned int resize_x, resize_y;
- resize_x = (input_w * 1024 + output_w / 2) / output_w;
- resize_y = (input_h * 1024 + output_h / 2) / output_h;
-
- /* We want max(resize_x, resize_y), check if it still fits */
- if (resize_x > resize_y &&
- (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT)
- resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) /
- output_h;
- else if (resize_y > resize_x &&
- (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH)
- resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) /
- output_w;
- else
- resize = max(resize_x, resize_y);
-
- /* Prohibited value ranges */
- switch (resize) {
- case 2040 ... 2047:
- resize = 2039;
- break;
- case 4080 ... 4095:
- resize = 4079;
- break;
- case 8160 ... 8191:
- resize = 8159;
- break;
- case 16320 ... 16384:
- resize = 16319;
- }
- }
-
- /* Set scaling */
- ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff);
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8);
-
- if (ret < 0)
- return ret;
-
- /*
- * Configure a skipping bitmask. The sensor will select a skipping value
- * among set bits automatically. This is very unclear in the datasheet
- * too. I was told, in this register one enables all skipping values,
- * that are required for a specific resize, and the camera selects
- * automatically, which ones to use. But it is unclear how to identify,
- * which cropping values are needed. Secondly, why don't we just set all
- * bits and let the camera choose? Would it increase processing time and
- * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to
- * improve the image quality or stability for larger frames (see comment
- * above), but I didn't check the framerate.
- */
- skip = min(resize / 1024, 15U);
-
- inc_sel = 1 << skip;
-
- if (inc_sel <= 2)
- inc_sel = 0xc;
- else if (resize & 1023 && skip < 15)
- inc_sel |= 1 << (skip + 1);
-
- ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc);
- if (!ret)
- ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8);
-
- if (!rj54n1->auto_wb) {
- /* Auto white balance window */
- wb_left = output_w / 16;
- wb_right = (3 * output_w / 4 - 3) / 4;
- wb_top = output_h / 16;
- wb_bottom = (3 * output_h / 4 - 3) / 4;
- wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) |
- ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1);
-
- if (!ret)
- ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8);
- if (!ret)
- ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left);
- if (!ret)
- ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top);
- if (!ret)
- ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right);
- if (!ret)
- ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom);
- }
-
- /* Antiflicker */
- peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz /
- 10000;
- peak_50 = peak / 6;
- peak_60 = peak / 5;
-
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_H,
- ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8));
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_50, peak_50);
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_60, peak_60);
- if (!ret)
- ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150);
-
- /* Start resizing */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
- RESIZE_HOLD_SEL | RESIZE_GO | 1);
-
- if (ret < 0)
- return ret;
-
- /* Constant taken from manufacturer's example */
- msleep(230);
-
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1);
- if (ret < 0)
- return ret;
-
- *in_w = (output_w * resize + 512) / 1024;
- *in_h = (output_h * resize + 512) / 1024;
- *out_w = output_w;
- *out_h = output_h;
-
- dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n",
- *in_w, *in_h, resize, output_w, output_h, skip);
-
- return resize;
-}
-
-static int rj54n1_set_clock(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret;
-
- /* Enable external clock */
- ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY);
- /* Leave stand-by. Note: use this when implementing suspend / resume */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK);
-
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_L, PLL_L);
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_N, PLL_N);
-
- /* TGCLK dividers */
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_TG,
- rj54n1->clk_div.ratio_tg);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_T,
- rj54n1->clk_div.ratio_t);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_R,
- rj54n1->clk_div.ratio_r);
-
- /* Enable TGCLK & RAMP */
- if (!ret)
- ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3);
-
- /* Disable clock output */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_DSP, 0);
-
- /* Set divisors */
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_OP,
- rj54n1->clk_div.ratio_op);
- if (!ret)
- ret = reg_write(client, RJ54N1_RATIO_O,
- rj54n1->clk_div.ratio_o);
-
- /* Enable OCLK */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
-
- /* Use PLL for Timing Generator, write 2 to reserved bits */
- if (!ret)
- ret = reg_write(client, RJ54N1_TG_BYPASS, 2);
-
- /* Take sensor out of reset */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | SEN_RSTX);
- /* Enable PLL */
- if (!ret)
- ret = reg_write(client, RJ54N1_PLL_EN, 1);
-
- /* Wait for PLL to stabilise */
- msleep(10);
-
- /* Enable clock to frequency divider */
- if (!ret)
- ret = reg_write(client, RJ54N1_CLK_RST, 1);
-
- if (!ret)
- ret = reg_read(client, RJ54N1_CLK_RST);
- if (ret != 1) {
- dev_err(&client->dev,
- "Resetting RJ54N1CB0C clock failed: %d!\n", ret);
- return -EIO;
- }
-
- /* Start the PLL */
- ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1);
-
- /* Enable OCLK */
- if (!ret)
- ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1);
-
- return ret;
-}
-
-static int rj54n1_reg_init(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- int ret = rj54n1_set_clock(client);
-
- if (!ret)
- ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7));
- if (!ret)
- ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10));
-
- /* Set binning divisors */
- if (!ret)
- ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4));
- if (!ret)
- ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf);
-
- /* Switch to fixed resize mode */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESIZE_CONTROL,
- RESIZE_HOLD_SEL | 1);
-
- /* Set gain */
- if (!ret)
- ret = reg_write(client, RJ54N1_Y_GAIN, 0x84);
-
- /*
- * Mirror the image back: default is upside down and left-to-right...
- * Set manual preview / still shot switching
- */
- if (!ret)
- ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27);
-
- if (!ret)
- ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4));
-
- /* Auto exposure area */
- if (!ret)
- ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80);
- /* Check current auto WB config */
- if (!ret)
- ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I);
- if (ret >= 0) {
- rj54n1->auto_wb = ret & 0x80;
- ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5));
- }
- if (!ret)
- ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8));
-
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | DSP_RSTX | SEN_RSTX);
-
- /* Commit init */
- if (!ret)
- ret = rj54n1_commit(client);
-
- /* Take DSP, TG, sensor out of reset */
- if (!ret)
- ret = reg_write(client, RJ54N1_RESET_STANDBY,
- E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX);
-
- /* Start register update? Same register as 0x?FE in many bank_* sets */
- if (!ret)
- ret = reg_write(client, RJ54N1_FWFLG, 2);
-
- /* Constant taken from manufacturer's example */
- msleep(700);
-
- return ret;
-}
-
-static int rj54n1_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct rj54n1_datafmt *fmt;
- int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE ||
- mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE;
-
- dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
- __func__, mf->code, mf->width, mf->height);
-
- fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
- ARRAY_SIZE(rj54n1_colour_fmts));
- if (!fmt) {
- fmt = rj54n1->fmt;
- mf->code = fmt->code;
- }
-
- mf->field = V4L2_FIELD_NONE;
- mf->colorspace = fmt->colorspace;
-
- v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align,
- &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0);
-
- return 0;
-}
-
-static int rj54n1_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- const struct rj54n1_datafmt *fmt;
- int output_w, output_h, max_w, max_h,
- input_w = rj54n1->rect.width, input_h = rj54n1->rect.height;
- int ret;
-
- /*
- * The host driver can call us without .try_fmt(), so, we have to take
- * care ourseleves
- */
- rj54n1_try_fmt(sd, mf);
-
- /*
- * Verify if the sensor has just been powered on. TODO: replace this
- * with proper PM, when a suitable API is available.
- */
- ret = reg_read(client, RJ54N1_RESET_STANDBY);
- if (ret < 0)
- return ret;
-
- if (!(ret & E_EXCLK)) {
- ret = rj54n1_reg_init(client);
- if (ret < 0)
- return ret;
- }
-
- dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n",
- __func__, mf->code, mf->width, mf->height);
-
- /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */
- switch (mf->code) {
- case V4L2_MBUS_FMT_YUYV8_2X8:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- break;
- case V4L2_MBUS_FMT_RGB565_2X8_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- break;
- case V4L2_MBUS_FMT_RGB565_2X8_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 0x11);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
- break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
- break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 0);
- break;
- case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE:
- ret = reg_write(client, RJ54N1_OUT_SEL, 4);
- if (!ret)
- ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8);
- if (!ret)
- ret = reg_write(client, RJ54N1_RA_SEL_UL, 8);
- break;
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- ret = reg_write(client, RJ54N1_OUT_SEL, 5);
- break;
- default:
- ret = -EINVAL;
- }
-
- /* Special case: a raw mode with 10 bits of data per clock tick */
- if (!ret)
- ret = reg_set(client, RJ54N1_OCLK_SEL_EN,
- (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2);
-
- if (ret < 0)
- return ret;
-
- /* Supported scales 1:1 >= scale > 1:16 */
- max_w = mf->width * (16 * 1024 - 1) / 1024;
- if (input_w > max_w)
- input_w = max_w;
- max_h = mf->height * (16 * 1024 - 1) / 1024;
- if (input_h > max_h)
- input_h = max_h;
-
- output_w = mf->width;
- output_h = mf->height;
-
- ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h);
- if (ret < 0)
- return ret;
-
- fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts,
- ARRAY_SIZE(rj54n1_colour_fmts));
-
- rj54n1->fmt = fmt;
- rj54n1->resize = ret;
- rj54n1->rect.width = input_w;
- rj54n1->rect.height = input_h;
- rj54n1->width = output_w;
- rj54n1->height = output_h;
-
- mf->width = output_w;
- mf->height = output_h;
- mf->field = V4L2_FIELD_NONE;
- mf->colorspace = fmt->colorspace;
-
- return 0;
-}
-
-static int rj54n1_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
- return -EINVAL;
-
- if (id->match.addr != client->addr)
- return -ENODEV;
-
- id->ident = V4L2_IDENT_RJ54N1CB0C;
- id->revision = 0;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int rj54n1_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
- reg->reg < 0x400 || reg->reg > 0x1fff)
- /* Registers > 0x0800 are only available from Sharp support */
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- reg->size = 1;
- reg->val = reg_read(client, reg->reg);
-
- if (reg->val > 0xff)
- return -EIO;
-
- return 0;
-}
-
-static int rj54n1_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR ||
- reg->reg < 0x400 || reg->reg > 0x1fff)
- /* Registers >= 0x0800 are only available from Sharp support */
- return -EINVAL;
-
- if (reg->match.addr != client->addr)
- return -ENODEV;
-
- if (reg_write(client, reg->reg, reg->val) < 0)
- return -EIO;
-
- return 0;
-}
-#endif
-
-static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl);
- struct v4l2_subdev *sd = &rj54n1->subdev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int data;
-
- switch (ctrl->id) {
- case V4L2_CID_VFLIP:
- if (ctrl->val)
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1);
- else
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_HFLIP:
- if (ctrl->val)
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2);
- else
- data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2);
- if (data < 0)
- return -EIO;
- return 0;
- case V4L2_CID_GAIN:
- if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0)
- return -EIO;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- /* Auto WB area - whole image */
- if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7,
- 0x80) < 0)
- return -EIO;
- rj54n1->auto_wb = ctrl->val;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = {
- .s_ctrl = rj54n1_s_ctrl,
-};
-
-static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = {
- .g_chip_ident = rj54n1_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = rj54n1_g_register,
- .s_register = rj54n1_s_register,
-#endif
-};
-
-static int rj54n1_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags =
- V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING |
- V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int rj54n1_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */
- if (soc_camera_apply_board_flags(icl, cfg) &
- V4L2_MBUS_PCLK_SAMPLE_RISING)
- return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4);
- else
- return reg_write(client, RJ54N1_OUT_SIGPO, 0);
-}
-
-static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = {
- .s_stream = rj54n1_s_stream,
- .s_mbus_fmt = rj54n1_s_fmt,
- .g_mbus_fmt = rj54n1_g_fmt,
- .try_mbus_fmt = rj54n1_try_fmt,
- .enum_mbus_fmt = rj54n1_enum_fmt,
- .g_crop = rj54n1_g_crop,
- .s_crop = rj54n1_s_crop,
- .cropcap = rj54n1_cropcap,
- .g_mbus_config = rj54n1_g_mbus_config,
- .s_mbus_config = rj54n1_s_mbus_config,
-};
-
-static struct v4l2_subdev_ops rj54n1_subdev_ops = {
- .core = &rj54n1_subdev_core_ops,
- .video = &rj54n1_subdev_video_ops,
-};
-
-/*
- * Interface active, can use i2c. If it fails, it can indeed mean, that
- * this wasn't our capture interface, so, we wait for the right one
- */
-static int rj54n1_video_probe(struct i2c_client *client,
- struct rj54n1_pdata *priv)
-{
- int data1, data2;
- int ret;
-
- /* Read out the chip version register */
- data1 = reg_read(client, RJ54N1_DEV_CODE);
- data2 = reg_read(client, RJ54N1_DEV_CODE2);
-
- if (data1 != 0x51 || data2 != 0x10) {
- ret = -ENODEV;
- dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n",
- data1, data2);
- goto ei2c;
- }
-
- /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */
- ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7);
- if (ret < 0)
- goto ei2c;
-
- dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n",
- data1, data2);
-
-ei2c:
- return ret;
-}
-
-static int rj54n1_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct rj54n1 *rj54n1;
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct rj54n1_pdata *rj54n1_priv;
- int ret;
-
- if (!icl || !icl->priv) {
- dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n");
- return -EINVAL;
- }
-
- rj54n1_priv = icl->priv;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_warn(&adapter->dev,
- "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
- return -EIO;
- }
-
- rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL);
- if (!rj54n1)
- return -ENOMEM;
-
- v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops);
- v4l2_ctrl_handler_init(&rj54n1->hdl, 4);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_GAIN, 0, 127, 1, 66);
- v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops,
- V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
- rj54n1->subdev.ctrl_handler = &rj54n1->hdl;
- if (rj54n1->hdl.error) {
- int err = rj54n1->hdl.error;
-
- kfree(rj54n1);
- return err;
- }
-
- rj54n1->clk_div = clk_div;
- rj54n1->rect.left = RJ54N1_COLUMN_SKIP;
- rj54n1->rect.top = RJ54N1_ROW_SKIP;
- rj54n1->rect.width = RJ54N1_MAX_WIDTH;
- rj54n1->rect.height = RJ54N1_MAX_HEIGHT;
- rj54n1->width = RJ54N1_MAX_WIDTH;
- rj54n1->height = RJ54N1_MAX_HEIGHT;
- rj54n1->fmt = &rj54n1_colour_fmts[0];
- rj54n1->resize = 1024;
- rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) /
- (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1);
-
- ret = rj54n1_video_probe(client, rj54n1_priv);
- if (ret < 0) {
- v4l2_ctrl_handler_free(&rj54n1->hdl);
- kfree(rj54n1);
- return ret;
- }
- return v4l2_ctrl_handler_setup(&rj54n1->hdl);
-}
-
-static int rj54n1_remove(struct i2c_client *client)
-{
- struct rj54n1 *rj54n1 = to_rj54n1(client);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- v4l2_device_unregister_subdev(&rj54n1->subdev);
- if (icl->free_bus)
- icl->free_bus(icl);
- v4l2_ctrl_handler_free(&rj54n1->hdl);
- kfree(rj54n1);
-
- return 0;
-}
-
-static const struct i2c_device_id rj54n1_id[] = {
- { "rj54n1cb0c", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, rj54n1_id);
-
-static struct i2c_driver rj54n1_i2c_driver = {
- .driver = {
- .name = "rj54n1cb0c",
- },
- .probe = rj54n1_probe,
- .remove = rj54n1_remove,
- .id_table = rj54n1_id,
-};
-
-module_i2c_driver(rj54n1_i2c_driver);
-
-MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
deleted file mode 100644
index 95007dda0c93..000000000000
--- a/drivers/media/video/s2255drv.c
+++ /dev/null
@@ -1,2689 +0,0 @@
-/*
- * s2255drv.c - a driver for the Sensoray 2255 USB video capture device
- *
- * Copyright (C) 2007-2010 by Sensoray Company Inc.
- * Dean Anderson
- *
- * Some video buffer code based on vivi driver:
- *
- * Sensoray 2255 device supports 4 simultaneous channels.
- * The channels are not "crossbar" inputs, they are physically
- * attached to separate video decoders.
- *
- * Because of USB2.0 bandwidth limitations. There is only a
- * certain amount of data which may be transferred at one time.
- *
- * Example maximum bandwidth utilization:
- *
- * -full size, color mode YUYV or YUV422P: 2 channels at once
- * -full or half size Grey scale: all 4 channels at once
- * -half size, color mode YUYV or YUV422P: all 4 channels at once
- * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels
- * at once.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/firmware.h>
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/mm.h>
-#include <media/videobuf-vmalloc.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/vmalloc.h>
-#include <linux/usb.h>
-
-#define S2255_VERSION "1.22.1"
-#define FIRMWARE_FILE_NAME "f2255usb.bin"
-
-/* default JPEG quality */
-#define S2255_DEF_JPEG_QUAL 50
-/* vendor request in */
-#define S2255_VR_IN 0
-/* vendor request out */
-#define S2255_VR_OUT 1
-/* firmware query */
-#define S2255_VR_FW 0x30
-/* USB endpoint number for configuring the device */
-#define S2255_CONFIG_EP 2
-/* maximum time for DSP to start responding after last FW word loaded(ms) */
-#define S2255_DSP_BOOTTIME 800
-/* maximum time to wait for firmware to load (ms) */
-#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME)
-#define S2255_DEF_BUFS 16
-#define S2255_SETMODE_TIMEOUT 500
-#define S2255_VIDSTATUS_TIMEOUT 350
-#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL)
-#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL)
-#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01)
-#define S2255_RESPONSE_FW cpu_to_le32(0x10)
-#define S2255_RESPONSE_STATUS cpu_to_le32(0x20)
-#define S2255_USB_XFER_SIZE (16 * 1024)
-#define MAX_CHANNELS 4
-#define SYS_FRAMES 4
-/* maximum size is PAL full size plus room for the marker header(s) */
-#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096)
-#define DEF_USB_BLOCK S2255_USB_XFER_SIZE
-#define LINE_SZ_4CIFS_NTSC 640
-#define LINE_SZ_2CIFS_NTSC 640
-#define LINE_SZ_1CIFS_NTSC 320
-#define LINE_SZ_4CIFS_PAL 704
-#define LINE_SZ_2CIFS_PAL 704
-#define LINE_SZ_1CIFS_PAL 352
-#define NUM_LINES_4CIFS_NTSC 240
-#define NUM_LINES_2CIFS_NTSC 240
-#define NUM_LINES_1CIFS_NTSC 240
-#define NUM_LINES_4CIFS_PAL 288
-#define NUM_LINES_2CIFS_PAL 288
-#define NUM_LINES_1CIFS_PAL 288
-#define LINE_SZ_DEF 640
-#define NUM_LINES_DEF 240
-
-
-/* predefined settings */
-#define FORMAT_NTSC 1
-#define FORMAT_PAL 2
-
-#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */
-#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */
-#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */
-/* SCALE_4CIFSI is the 2 fields interpolated into one */
-#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */
-
-#define COLOR_YUVPL 1 /* YUV planar */
-#define COLOR_YUVPK 2 /* YUV packed */
-#define COLOR_Y8 4 /* monochrome */
-#define COLOR_JPG 5 /* JPEG */
-
-#define MASK_COLOR 0x000000ff
-#define MASK_JPG_QUALITY 0x0000ff00
-#define MASK_INPUT_TYPE 0x000f0000
-/* frame decimation. */
-#define FDEC_1 1 /* capture every frame. default */
-#define FDEC_2 2 /* capture every 2nd frame */
-#define FDEC_3 3 /* capture every 3rd frame */
-#define FDEC_5 5 /* capture every 5th frame */
-
-/*-------------------------------------------------------
- * Default mode parameters.
- *-------------------------------------------------------*/
-#define DEF_SCALE SCALE_4CIFS
-#define DEF_COLOR COLOR_YUVPL
-#define DEF_FDEC FDEC_1
-#define DEF_BRIGHT 0
-#define DEF_CONTRAST 0x5c
-#define DEF_SATURATION 0x80
-#define DEF_HUE 0
-
-/* usb config commands */
-#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de)
-#define CMD_2255 0xc2255000
-#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10))
-#define CMD_START cpu_to_le32((CMD_2255 | 0x20))
-#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30))
-#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40))
-
-struct s2255_mode {
- u32 format; /* input video format (NTSC, PAL) */
- u32 scale; /* output video scale */
- u32 color; /* output video color format */
- u32 fdec; /* frame decimation */
- u32 bright; /* brightness */
- u32 contrast; /* contrast */
- u32 saturation; /* saturation */
- u32 hue; /* hue (NTSC only)*/
- u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/
- u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */
- u32 restart; /* if DSP requires restart */
-};
-
-
-#define S2255_READ_IDLE 0
-#define S2255_READ_FRAME 1
-
-/* frame structure */
-struct s2255_framei {
- unsigned long size;
- unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/
- void *lpvbits; /* image data */
- unsigned long cur_size; /* current data copied to it */
-};
-
-/* image buffer structure */
-struct s2255_bufferi {
- unsigned long dwFrames; /* number of frames in buffer */
- struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */
-};
-
-#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \
- DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \
- DEF_HUE, 0, DEF_USB_BLOCK, 0}
-
-struct s2255_dmaqueue {
- struct list_head active;
- struct s2255_dev *dev;
-};
-
-/* for firmware loading, fw_state */
-#define S2255_FW_NOTLOADED 0
-#define S2255_FW_LOADED_DSPWAIT 1
-#define S2255_FW_SUCCESS 2
-#define S2255_FW_FAILED 3
-#define S2255_FW_DISCONNECTING 4
-#define S2255_FW_MARKER cpu_to_le32(0x22552f2f)
-/* 2255 read states */
-#define S2255_READ_IDLE 0
-#define S2255_READ_FRAME 1
-struct s2255_fw {
- int fw_loaded;
- int fw_size;
- struct urb *fw_urb;
- atomic_t fw_state;
- void *pfw_data;
- wait_queue_head_t wait_fw;
- const struct firmware *fw;
-};
-
-struct s2255_pipeinfo {
- u32 max_transfer_size;
- u32 cur_transfer_size;
- u8 *transfer_buffer;
- u32 state;
- void *stream_urb;
- void *dev; /* back pointer to s2255_dev struct*/
- u32 err_count;
- u32 idx;
-};
-
-struct s2255_fmt; /*forward declaration */
-struct s2255_dev;
-
-struct s2255_channel {
- struct video_device vdev;
- int resources;
- struct s2255_dmaqueue vidq;
- struct s2255_bufferi buffer;
- struct s2255_mode mode;
- /* jpeg compression */
- struct v4l2_jpegcompression jc;
- /* capture parameters (for high quality mode full size) */
- struct v4l2_captureparm cap_parm;
- int cur_frame;
- int last_frame;
-
- int b_acquire;
- /* allocated image size */
- unsigned long req_image_size;
- /* received packet size */
- unsigned long pkt_size;
- int bad_payload;
- unsigned long frame_count;
- /* if JPEG image */
- int jpg_size;
- /* if channel configured to default state */
- int configured;
- wait_queue_head_t wait_setmode;
- int setmode_ready;
- /* video status items */
- int vidstatus;
- wait_queue_head_t wait_vidstatus;
- int vidstatus_ready;
- unsigned int width;
- unsigned int height;
- const struct s2255_fmt *fmt;
- int idx; /* channel number on device, 0-3 */
-};
-
-
-struct s2255_dev {
- struct s2255_channel channel[MAX_CHANNELS];
- struct v4l2_device v4l2_dev;
- atomic_t num_channels;
- int frames;
- struct mutex lock; /* channels[].vdev.lock */
- struct mutex open_lock;
- struct usb_device *udev;
- struct usb_interface *interface;
- u8 read_endpoint;
- struct timer_list timer;
- struct s2255_fw *fw_data;
- struct s2255_pipeinfo pipe;
- u32 cc; /* current channel */
- int frame_ready;
- int chn_ready;
- spinlock_t slock;
- /* dsp firmware version (f2255usb.bin) */
- int dsp_fw_ver;
- u16 pid; /* product id */
-};
-
-static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev)
-{
- return container_of(v4l2_dev, struct s2255_dev, v4l2_dev);
-}
-
-struct s2255_fmt {
- char *name;
- u32 fourcc;
- int depth;
-};
-
-/* buffer for one video frame */
-struct s2255_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
- const struct s2255_fmt *fmt;
-};
-
-struct s2255_fh {
- struct s2255_dev *dev;
- struct videobuf_queue vb_vidq;
- enum v4l2_buf_type type;
- struct s2255_channel *channel;
- int resources;
-};
-
-/* current cypress EEPROM firmware version */
-#define S2255_CUR_USB_FWVER ((3 << 8) | 12)
-/* current DSP FW version */
-#define S2255_CUR_DSP_FWVER 10104
-/* Need DSP version 5+ for video status feature */
-#define S2255_MIN_DSP_STATUS 5
-#define S2255_MIN_DSP_COLORFILTER 8
-#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC)
-
-/* private V4L2 controls */
-
-/*
- * The following chart displays how COLORFILTER should be set
- * =========================================================
- * = fourcc = COLORFILTER =
- * = ===============================
- * = = 0 = 1 =
- * =========================================================
- * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome=
- * = = s-video or = composite =
- * = = B/W camera = input =
- * =========================================================
- * = other = color, svideo = color, =
- * = = = composite =
- * =========================================================
- *
- * Notes:
- * channels 0-3 on 2255 are composite
- * channels 0-1 on 2257 are composite, 2-3 are s-video
- * If COLORFILTER is 0 with a composite color camera connected,
- * the output will appear monochrome but hatching
- * will occur.
- * COLORFILTER is different from "color killer" and "color effects"
- * for reasons above.
- */
-#define S2255_V4L2_YC_ON 1
-#define S2255_V4L2_YC_OFF 0
-#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0)
-
-/* frame prefix size (sent once every frame) */
-#define PREFIX_SIZE 512
-
-/* Channels on box are in reverse order */
-static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0};
-
-static int debug;
-static int *s2255_debug = &debug;
-
-static int s2255_start_readpipe(struct s2255_dev *dev);
-static void s2255_stop_readpipe(struct s2255_dev *dev);
-static int s2255_start_acquire(struct s2255_channel *channel);
-static int s2255_stop_acquire(struct s2255_channel *channel);
-static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf,
- int jpgsize);
-static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode);
-static int s2255_board_shutdown(struct s2255_dev *dev);
-static void s2255_fwload_start(struct s2255_dev *dev, int reset);
-static void s2255_destroy(struct s2255_dev *dev);
-static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
- u16 index, u16 value, void *buf,
- s32 buf_len, int bOut);
-
-/* dev_err macro with driver name */
-#define S2255_DRIVER_NAME "s2255"
-#define s2255_dev_err(dev, fmt, arg...) \
- dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
-
-#define dprintk(level, fmt, arg...) \
- do { \
- if (*s2255_debug >= (level)) { \
- printk(KERN_DEBUG S2255_DRIVER_NAME \
- ": " fmt, ##arg); \
- } \
- } while (0)
-
-static struct usb_driver s2255_driver;
-
-/* Declare static vars that will be used as parameters */
-static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
-
-/* start video number */
-static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
-
-/* Enable jpeg capture. */
-static int jpeg_enable = 1;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level(0-100) default 0");
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)");
-module_param(video_nr, int, 0644);
-MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)");
-module_param(jpeg_enable, int, 0644);
-MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1");
-
-/* USB device table */
-#define USB_SENSORAY_VID 0x1943
-static struct usb_device_id s2255_table[] = {
- {USB_DEVICE(USB_SENSORAY_VID, 0x2255)},
- {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, s2255_table);
-
-#define BUFFER_TIMEOUT msecs_to_jiffies(400)
-
-/* image formats. */
-/* JPEG formats must be defined last to support jpeg_enable parameter */
-static const struct s2255_fmt formats[] = {
- {
- .name = "4:2:2, planar, YUV422P",
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .depth = 16
-
- }, {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16
-
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16
- }, {
- .name = "8bpp GREY",
- .fourcc = V4L2_PIX_FMT_GREY,
- .depth = 8
- }, {
- .name = "JPG",
- .fourcc = V4L2_PIX_FMT_JPEG,
- .depth = 24
- }, {
- .name = "MJPG",
- .fourcc = V4L2_PIX_FMT_MJPEG,
- .depth = 24
- }
-};
-
-static int norm_maxw(struct video_device *vdev)
-{
- return (vdev->current_norm & V4L2_STD_NTSC) ?
- LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL;
-}
-
-static int norm_maxh(struct video_device *vdev)
-{
- return (vdev->current_norm & V4L2_STD_NTSC) ?
- (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2);
-}
-
-static int norm_minw(struct video_device *vdev)
-{
- return (vdev->current_norm & V4L2_STD_NTSC) ?
- LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL;
-}
-
-static int norm_minh(struct video_device *vdev)
-{
- return (vdev->current_norm & V4L2_STD_NTSC) ?
- (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL);
-}
-
-
-/*
- * TODO: fixme: move YUV reordering to hardware
- * converts 2255 planar format to yuyv or uyvy
- */
-static void planar422p_to_yuv_packed(const unsigned char *in,
- unsigned char *out,
- int width, int height,
- int fmt)
-{
- unsigned char *pY;
- unsigned char *pCb;
- unsigned char *pCr;
- unsigned long size = height * width;
- unsigned int i;
- pY = (unsigned char *)in;
- pCr = (unsigned char *)in + height * width;
- pCb = (unsigned char *)in + height * width + (height * width / 2);
- for (i = 0; i < size * 2; i += 4) {
- out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++;
- out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++;
- out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++;
- out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++;
- }
- return;
-}
-
-static void s2255_reset_dsppower(struct s2255_dev *dev)
-{
- s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1);
- msleep(10);
- s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1);
- msleep(600);
- s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1);
- return;
-}
-
-/* kickstarts the firmware loading. from probe
- */
-static void s2255_timer(unsigned long user_data)
-{
- struct s2255_fw *data = (struct s2255_fw *)user_data;
- dprintk(100, "%s\n", __func__);
- if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
- printk(KERN_ERR "s2255: can't submit urb\n");
- atomic_set(&data->fw_state, S2255_FW_FAILED);
- /* wake up anything waiting for the firmware */
- wake_up(&data->wait_fw);
- return;
- }
-}
-
-
-/* this loads the firmware asynchronously.
- Originally this was done synchroously in probe.
- But it is better to load it asynchronously here than block
- inside the probe function. Blocking inside probe affects boot time.
- FW loading is triggered by the timer in the probe function
-*/
-static void s2255_fwchunk_complete(struct urb *urb)
-{
- struct s2255_fw *data = urb->context;
- struct usb_device *udev = urb->dev;
- int len;
- dprintk(100, "%s: udev %p urb %p", __func__, udev, urb);
- if (urb->status) {
- dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
- atomic_set(&data->fw_state, S2255_FW_FAILED);
- /* wake up anything waiting for the firmware */
- wake_up(&data->wait_fw);
- return;
- }
- if (data->fw_urb == NULL) {
- s2255_dev_err(&udev->dev, "disconnected\n");
- atomic_set(&data->fw_state, S2255_FW_FAILED);
- /* wake up anything waiting for the firmware */
- wake_up(&data->wait_fw);
- return;
- }
-#define CHUNK_SIZE 512
- /* all USB transfers must be done with continuous kernel memory.
- can't allocate more than 128k in current linux kernel, so
- upload the firmware in chunks
- */
- if (data->fw_loaded < data->fw_size) {
- len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ?
- data->fw_size % CHUNK_SIZE : CHUNK_SIZE;
-
- if (len < CHUNK_SIZE)
- memset(data->pfw_data, 0, CHUNK_SIZE);
-
- dprintk(100, "completed len %d, loaded %d \n", len,
- data->fw_loaded);
-
- memcpy(data->pfw_data,
- (char *) data->fw->data + data->fw_loaded, len);
-
- usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2),
- data->pfw_data, CHUNK_SIZE,
- s2255_fwchunk_complete, data);
- if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) {
- dev_err(&udev->dev, "failed submit URB\n");
- atomic_set(&data->fw_state, S2255_FW_FAILED);
- /* wake up anything waiting for the firmware */
- wake_up(&data->wait_fw);
- return;
- }
- data->fw_loaded += len;
- } else {
- atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT);
- dprintk(100, "%s: firmware upload complete\n", __func__);
- }
- return;
-
-}
-
-static int s2255_got_frame(struct s2255_channel *channel, int jpgsize)
-{
- struct s2255_dmaqueue *dma_q = &channel->vidq;
- struct s2255_buffer *buf;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- unsigned long flags = 0;
- int rc = 0;
- spin_lock_irqsave(&dev->slock, flags);
- if (list_empty(&dma_q->active)) {
- dprintk(1, "No active queue to serve\n");
- rc = -1;
- goto unlock;
- }
- buf = list_entry(dma_q->active.next,
- struct s2255_buffer, vb.queue);
- list_del(&buf->vb.queue);
- do_gettimeofday(&buf->vb.ts);
- s2255_fillbuff(channel, buf, jpgsize);
- wake_up(&buf->vb.done);
- dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i);
-unlock:
- spin_unlock_irqrestore(&dev->slock, flags);
- return rc;
-}
-
-static const struct s2255_fmt *format_by_fourcc(int fourcc)
-{
- unsigned int i;
- for (i = 0; i < ARRAY_SIZE(formats); i++) {
- if (-1 == formats[i].fourcc)
- continue;
- if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) ||
- (formats[i].fourcc == V4L2_PIX_FMT_MJPEG)))
- continue;
- if (formats[i].fourcc == fourcc)
- return formats + i;
- }
- return NULL;
-}
-
-/* video buffer vmalloc implementation based partly on VIVI driver which is
- * Copyright (c) 2006 by
- * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
- * Ted Walther <ted--a.t--enumera.com>
- * John Sokol <sokol--a.t--videotechnology.com>
- * http://v4l.videotechnology.com/
- *
- */
-static void s2255_fillbuff(struct s2255_channel *channel,
- struct s2255_buffer *buf, int jpgsize)
-{
- int pos = 0;
- struct timeval ts;
- const char *tmpbuf;
- char *vbuf = videobuf_to_vmalloc(&buf->vb);
- unsigned long last_frame;
-
- if (!vbuf)
- return;
- last_frame = channel->last_frame;
- if (last_frame != -1) {
- tmpbuf =
- (const char *)channel->buffer.frame[last_frame].lpvbits;
- switch (buf->fmt->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- planar422p_to_yuv_packed((const unsigned char *)tmpbuf,
- vbuf, buf->vb.width,
- buf->vb.height,
- buf->fmt->fourcc);
- break;
- case V4L2_PIX_FMT_GREY:
- memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height);
- break;
- case V4L2_PIX_FMT_JPEG:
- case V4L2_PIX_FMT_MJPEG:
- buf->vb.size = jpgsize;
- memcpy(vbuf, tmpbuf, buf->vb.size);
- break;
- case V4L2_PIX_FMT_YUV422P:
- memcpy(vbuf, tmpbuf,
- buf->vb.width * buf->vb.height * 2);
- break;
- default:
- printk(KERN_DEBUG "s2255: unknown format?\n");
- }
- channel->last_frame = -1;
- } else {
- printk(KERN_ERR "s2255: =======no frame\n");
- return;
-
- }
- dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n",
- (unsigned long)vbuf, pos);
- /* tell v4l buffer was filled */
-
- buf->vb.field_count = channel->frame_count * 2;
- do_gettimeofday(&ts);
- buf->vb.ts = ts;
- buf->vb.state = VIDEOBUF_DONE;
-}
-
-
-/* ------------------------------------------------------------------
- Videobuf operations
- ------------------------------------------------------------------*/
-
-static int buffer_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
- *size = channel->width * channel->height * (channel->fmt->depth >> 3);
-
- if (0 == *count)
- *count = S2255_DEF_BUFS;
-
- if (*size * *count > vid_limit * 1024 * 1024)
- *count = (vid_limit * 1024 * 1024) / *size;
-
- return 0;
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf)
-{
- dprintk(4, "%s\n", __func__);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
- struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- int rc;
- int w = channel->width;
- int h = channel->height;
- dprintk(4, "%s, field=%d\n", __func__, field);
- if (channel->fmt == NULL)
- return -EINVAL;
-
- if ((w < norm_minw(&channel->vdev)) ||
- (w > norm_maxw(&channel->vdev)) ||
- (h < norm_minh(&channel->vdev)) ||
- (h > norm_maxh(&channel->vdev))) {
- dprintk(4, "invalid buffer prepare\n");
- return -EINVAL;
- }
- buf->vb.size = w * h * (channel->fmt->depth >> 3);
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) {
- dprintk(4, "invalid buffer prepare\n");
- return -EINVAL;
- }
-
- buf->fmt = channel->fmt;
- buf->vb.width = w;
- buf->vb.height = h;
- buf->vb.field = field;
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc < 0)
- goto fail;
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- struct s2255_fh *fh = vq->priv_data;
- struct s2255_channel *channel = fh->channel;
- struct s2255_dmaqueue *vidq = &channel->vidq;
- dprintk(1, "%s\n", __func__);
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-}
-
-static void buffer_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb);
- struct s2255_fh *fh = vq->priv_data;
- dprintk(4, "%s %d\n", __func__, fh->channel->idx);
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops s2255_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-
-static int res_get(struct s2255_fh *fh)
-{
- struct s2255_channel *channel = fh->channel;
- /* is it free? */
- if (channel->resources)
- return 0; /* no, someone else uses it */
- /* it's free, grab it */
- channel->resources = 1;
- fh->resources = 1;
- dprintk(1, "s2255: res: get\n");
- return 1;
-}
-
-static int res_locked(struct s2255_fh *fh)
-{
- return fh->channel->resources;
-}
-
-static int res_check(struct s2255_fh *fh)
-{
- return fh->resources;
-}
-
-
-static void res_free(struct s2255_fh *fh)
-{
- struct s2255_channel *channel = fh->channel;
- channel->resources = 0;
- fh->resources = 0;
- dprintk(1, "res: put\n");
-}
-
-static int vidioc_querymenu(struct file *file, void *priv,
- struct v4l2_querymenu *qmenu)
-{
- static const char *colorfilter[] = {
- "Off",
- "On",
- NULL
- };
- if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) {
- int i;
- const char **menu_items = colorfilter;
- for (i = 0; i < qmenu->index && menu_items[i]; i++)
- ; /* do nothing (from v4l2-common.c) */
- if (menu_items[i] == NULL || menu_items[i][0] == '\0')
- return -EINVAL;
- strlcpy(qmenu->name, menu_items[qmenu->index],
- sizeof(qmenu->name));
- return 0;
- }
- return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev = fh->dev;
- strlcpy(cap->driver, "s2255", sizeof(cap->driver));
- strlcpy(cap->card, "s2255", sizeof(cap->card));
- usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- int index = f->index;
-
- if (index >= ARRAY_SIZE(formats))
- return -EINVAL;
- if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
- (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
- return -EINVAL;
- dprintk(4, "name %s\n", formats[index].name);
- strlcpy(f->description, formats[index].name, sizeof(f->description));
- f->pixelformat = formats[index].fourcc;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
-
- f->fmt.pix.width = channel->width;
- f->fmt.pix.height = channel->height;
- f->fmt.pix.field = fh->vb_vidq.field;
- f->fmt.pix.pixelformat = channel->fmt->fourcc;
- f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3);
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- const struct s2255_fmt *fmt;
- enum v4l2_field field;
- int b_any_field = 0;
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- int is_ntsc;
- is_ntsc =
- (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
- if (fmt == NULL)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- if (field == V4L2_FIELD_ANY)
- b_any_field = 1;
-
- dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n",
- __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height);
- if (is_ntsc) {
- /* NTSC */
- if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) {
- f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2;
- if (b_any_field) {
- field = V4L2_FIELD_SEQ_TB;
- } else if (!((field == V4L2_FIELD_INTERLACED) ||
- (field == V4L2_FIELD_SEQ_TB) ||
- (field == V4L2_FIELD_INTERLACED_TB))) {
- dprintk(1, "unsupported field setting\n");
- return -EINVAL;
- }
- } else {
- f->fmt.pix.height = NUM_LINES_1CIFS_NTSC;
- if (b_any_field) {
- field = V4L2_FIELD_TOP;
- } else if (!((field == V4L2_FIELD_TOP) ||
- (field == V4L2_FIELD_BOTTOM))) {
- dprintk(1, "unsupported field setting\n");
- return -EINVAL;
- }
-
- }
- if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC)
- f->fmt.pix.width = LINE_SZ_4CIFS_NTSC;
- else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC)
- f->fmt.pix.width = LINE_SZ_2CIFS_NTSC;
- else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC)
- f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
- else
- f->fmt.pix.width = LINE_SZ_1CIFS_NTSC;
- } else {
- /* PAL */
- if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) {
- f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2;
- if (b_any_field) {
- field = V4L2_FIELD_SEQ_TB;
- } else if (!((field == V4L2_FIELD_INTERLACED) ||
- (field == V4L2_FIELD_SEQ_TB) ||
- (field == V4L2_FIELD_INTERLACED_TB))) {
- dprintk(1, "unsupported field setting\n");
- return -EINVAL;
- }
- } else {
- f->fmt.pix.height = NUM_LINES_1CIFS_PAL;
- if (b_any_field) {
- field = V4L2_FIELD_TOP;
- } else if (!((field == V4L2_FIELD_TOP) ||
- (field == V4L2_FIELD_BOTTOM))) {
- dprintk(1, "unsupported field setting\n");
- return -EINVAL;
- }
- }
- if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) {
- f->fmt.pix.width = LINE_SZ_4CIFS_PAL;
- field = V4L2_FIELD_SEQ_TB;
- } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) {
- f->fmt.pix.width = LINE_SZ_2CIFS_PAL;
- field = V4L2_FIELD_TOP;
- } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) {
- f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
- field = V4L2_FIELD_TOP;
- } else {
- f->fmt.pix.width = LINE_SZ_1CIFS_PAL;
- field = V4L2_FIELD_TOP;
- }
- }
- f->fmt.pix.field = field;
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- dprintk(50, "%s: set width %d height %d field %d\n", __func__,
- f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field);
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- const struct s2255_fmt *fmt;
- struct videobuf_queue *q = &fh->vb_vidq;
- struct s2255_mode mode;
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, fh, f);
-
- if (ret < 0)
- return ret;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
-
- if (fmt == NULL)
- return -EINVAL;
-
- mutex_lock(&q->vb_lock);
-
- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
- dprintk(1, "queue busy\n");
- ret = -EBUSY;
- goto out_s_fmt;
- }
-
- if (res_locked(fh)) {
- dprintk(1, "%s: channel busy\n", __func__);
- ret = -EBUSY;
- goto out_s_fmt;
- }
- mode = channel->mode;
- channel->fmt = fmt;
- channel->width = f->fmt.pix.width;
- channel->height = f->fmt.pix.height;
- fh->vb_vidq.field = f->fmt.pix.field;
- fh->type = f->type;
- if (channel->width > norm_minw(&channel->vdev)) {
- if (channel->height > norm_minh(&channel->vdev)) {
- if (channel->cap_parm.capturemode &
- V4L2_MODE_HIGHQUALITY)
- mode.scale = SCALE_4CIFSI;
- else
- mode.scale = SCALE_4CIFS;
- } else
- mode.scale = SCALE_2CIFS;
-
- } else {
- mode.scale = SCALE_1CIFS;
- }
- /* color mode */
- switch (channel->fmt->fourcc) {
- case V4L2_PIX_FMT_GREY:
- mode.color &= ~MASK_COLOR;
- mode.color |= COLOR_Y8;
- break;
- case V4L2_PIX_FMT_JPEG:
- case V4L2_PIX_FMT_MJPEG:
- mode.color &= ~MASK_COLOR;
- mode.color |= COLOR_JPG;
- mode.color |= (channel->jc.quality << 8);
- break;
- case V4L2_PIX_FMT_YUV422P:
- mode.color &= ~MASK_COLOR;
- mode.color |= COLOR_YUVPL;
- break;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- default:
- mode.color &= ~MASK_COLOR;
- mode.color |= COLOR_YUVPK;
- break;
- }
- if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR))
- mode.restart = 1;
- else if (mode.scale != channel->mode.scale)
- mode.restart = 1;
- else if (mode.format != channel->mode.format)
- mode.restart = 1;
- channel->mode = mode;
- (void) s2255_set_mode(channel, &mode);
- ret = 0;
-out_s_fmt:
- mutex_unlock(&q->vb_lock);
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_reqbufs(&fh->vb_vidq, p);
- return rc;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_querybuf(&fh->vb_vidq, p);
- return rc;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_qbuf(&fh->vb_vidq, p);
- return rc;
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- int rc;
- struct s2255_fh *fh = priv;
- rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK);
- return rc;
-}
-
-/* write to the configuration pipe, synchronously */
-static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf,
- int size)
-{
- int pipe;
- int done;
- long retval = -1;
- if (udev) {
- pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP);
- retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500);
- }
- return retval;
-}
-
-static u32 get_transfer_size(struct s2255_mode *mode)
-{
- int linesPerFrame = LINE_SZ_DEF;
- int pixelsPerLine = NUM_LINES_DEF;
- u32 outImageSize;
- u32 usbInSize;
- unsigned int mask_mult;
-
- if (mode == NULL)
- return 0;
-
- if (mode->format == FORMAT_NTSC) {
- switch (mode->scale) {
- case SCALE_4CIFS:
- case SCALE_4CIFSI:
- linesPerFrame = NUM_LINES_4CIFS_NTSC * 2;
- pixelsPerLine = LINE_SZ_4CIFS_NTSC;
- break;
- case SCALE_2CIFS:
- linesPerFrame = NUM_LINES_2CIFS_NTSC;
- pixelsPerLine = LINE_SZ_2CIFS_NTSC;
- break;
- case SCALE_1CIFS:
- linesPerFrame = NUM_LINES_1CIFS_NTSC;
- pixelsPerLine = LINE_SZ_1CIFS_NTSC;
- break;
- default:
- break;
- }
- } else if (mode->format == FORMAT_PAL) {
- switch (mode->scale) {
- case SCALE_4CIFS:
- case SCALE_4CIFSI:
- linesPerFrame = NUM_LINES_4CIFS_PAL * 2;
- pixelsPerLine = LINE_SZ_4CIFS_PAL;
- break;
- case SCALE_2CIFS:
- linesPerFrame = NUM_LINES_2CIFS_PAL;
- pixelsPerLine = LINE_SZ_2CIFS_PAL;
- break;
- case SCALE_1CIFS:
- linesPerFrame = NUM_LINES_1CIFS_PAL;
- pixelsPerLine = LINE_SZ_1CIFS_PAL;
- break;
- default:
- break;
- }
- }
- outImageSize = linesPerFrame * pixelsPerLine;
- if ((mode->color & MASK_COLOR) != COLOR_Y8) {
- /* 2 bytes/pixel if not monochrome */
- outImageSize *= 2;
- }
-
- /* total bytes to send including prefix and 4K padding;
- must be a multiple of USB_READ_SIZE */
- usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */
- mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1;
- /* if size not a multiple of USB_READ_SIZE */
- if (usbInSize & ~mask_mult)
- usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK);
- return usbInSize;
-}
-
-static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode)
-{
- struct device *dev = &sdev->udev->dev;
- dev_info(dev, "------------------------------------------------\n");
- dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale);
- dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color);
- dev_info(dev, "bright: 0x%x\n", mode->bright);
- dev_info(dev, "------------------------------------------------\n");
-}
-
-/*
- * set mode is the function which controls the DSP.
- * the restart parameter in struct s2255_mode should be set whenever
- * the image size could change via color format, video system or image
- * size.
- * When the restart parameter is set, we sleep for ONE frame to allow the
- * DSP time to get the new frame
- */
-static int s2255_set_mode(struct s2255_channel *channel,
- struct s2255_mode *mode)
-{
- int res;
- __le32 *buffer;
- unsigned long chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- dprintk(3, "%s channel: %d\n", __func__, channel->idx);
- /* if JPEG, set the quality */
- if ((mode->color & MASK_COLOR) == COLOR_JPG) {
- mode->color &= ~MASK_COLOR;
- mode->color |= COLOR_JPG;
- mode->color &= ~MASK_JPG_QUALITY;
- mode->color |= (channel->jc.quality << 8);
- }
- /* save the mode */
- channel->mode = *mode;
- channel->req_image_size = get_transfer_size(mode);
- dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size);
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
- /* set the mode */
- buffer[0] = IN_DATA_TOKEN;
- buffer[1] = (__le32) cpu_to_le32(chn_rev);
- buffer[2] = CMD_SET_MODE;
- memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode));
- channel->setmode_ready = 0;
- res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
- if (debug)
- s2255_print_cfg(dev, mode);
- kfree(buffer);
- /* wait at least 3 frames before continuing */
- if (mode->restart) {
- wait_event_timeout(channel->wait_setmode,
- (channel->setmode_ready != 0),
- msecs_to_jiffies(S2255_SETMODE_TIMEOUT));
- if (channel->setmode_ready != 1) {
- printk(KERN_DEBUG "s2255: no set mode response\n");
- res = -EFAULT;
- }
- }
- /* clear the restart flag */
- channel->mode.restart = 0;
- dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res);
- return res;
-}
-
-static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus)
-{
- int res;
- __le32 *buffer;
- u32 chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- dprintk(4, "%s chan %d\n", __func__, channel->idx);
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
- /* form the get vid status command */
- buffer[0] = IN_DATA_TOKEN;
- buffer[1] = (__le32) cpu_to_le32(chn_rev);
- buffer[2] = CMD_STATUS;
- *pstatus = 0;
- channel->vidstatus_ready = 0;
- res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
- kfree(buffer);
- wait_event_timeout(channel->wait_vidstatus,
- (channel->vidstatus_ready != 0),
- msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT));
- if (channel->vidstatus_ready != 1) {
- printk(KERN_DEBUG "s2255: no vidstatus response\n");
- res = -EFAULT;
- }
- *pstatus = channel->vidstatus;
- dprintk(4, "%s, vid status %d\n", __func__, *pstatus);
- return res;
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- int res;
- struct s2255_fh *fh = priv;
- struct s2255_dev *dev = fh->dev;
- struct s2255_channel *channel = fh->channel;
- int j;
- dprintk(4, "%s\n", __func__);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_err(&dev->udev->dev, "invalid fh type0\n");
- return -EINVAL;
- }
- if (i != fh->type) {
- dev_err(&dev->udev->dev, "invalid fh type1\n");
- return -EINVAL;
- }
-
- if (!res_get(fh)) {
- s2255_dev_err(&dev->udev->dev, "stream busy\n");
- return -EBUSY;
- }
- channel->last_frame = -1;
- channel->bad_payload = 0;
- channel->cur_frame = 0;
- channel->frame_count = 0;
- for (j = 0; j < SYS_FRAMES; j++) {
- channel->buffer.frame[j].ulState = S2255_READ_IDLE;
- channel->buffer.frame[j].cur_size = 0;
- }
- res = videobuf_streamon(&fh->vb_vidq);
- if (res == 0) {
- s2255_start_acquire(channel);
- channel->b_acquire = 1;
- } else
- res_free(fh);
-
- return res;
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct s2255_fh *fh = priv;
- dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx);
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- printk(KERN_ERR "invalid fh type0\n");
- return -EINVAL;
- }
- if (i != fh->type) {
- printk(KERN_ERR "invalid type i\n");
- return -EINVAL;
- }
- s2255_stop_acquire(fh->channel);
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
-{
- struct s2255_fh *fh = priv;
- struct s2255_mode mode;
- struct videobuf_queue *q = &fh->vb_vidq;
- int ret = 0;
- mutex_lock(&q->vb_lock);
- if (videobuf_queue_is_busy(q)) {
- dprintk(1, "queue busy\n");
- ret = -EBUSY;
- goto out_s_std;
- }
- if (res_locked(fh)) {
- dprintk(1, "can't change standard after started\n");
- ret = -EBUSY;
- goto out_s_std;
- }
- mode = fh->channel->mode;
- if (*i & V4L2_STD_NTSC) {
- dprintk(4, "%s NTSC\n", __func__);
- /* if changing format, reset frame decimation/intervals */
- if (mode.format != FORMAT_NTSC) {
- mode.restart = 1;
- mode.format = FORMAT_NTSC;
- mode.fdec = FDEC_1;
- }
- } else if (*i & V4L2_STD_PAL) {
- dprintk(4, "%s PAL\n", __func__);
- if (mode.format != FORMAT_PAL) {
- mode.restart = 1;
- mode.format = FORMAT_PAL;
- mode.fdec = FDEC_1;
- }
- } else {
- ret = -EINVAL;
- }
- if (mode.restart)
- s2255_set_mode(fh->channel, &mode);
-out_s_std:
- mutex_unlock(&q->vb_lock);
- return ret;
-}
-
-/* Sensoray 2255 is a multiple channel capture device.
- It does not have a "crossbar" of inputs.
- We use one V4L device per channel. The user must
- be aware that certain combinations are not allowed.
- For instance, you cannot do full FPS on more than 2 channels(2 videodevs)
- at once in color(you can do full fps on 4 channels with greyscale.
-*/
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- struct s2255_fh *fh = priv;
- struct s2255_dev *dev = fh->dev;
- struct s2255_channel *channel = fh->channel;
- u32 status = 0;
- if (inp->index != 0)
- return -EINVAL;
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = S2255_NORMS;
- inp->status = 0;
- if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) {
- int rc;
- rc = s2255_cmd_status(fh->channel, &status);
- dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status);
- if (rc == 0)
- inp->status = (status & 0x01) ? 0
- : V4L2_IN_ST_NO_SIGNAL;
- }
- switch (dev->pid) {
- case 0x2255:
- default:
- strlcpy(inp->name, "Composite", sizeof(inp->name));
- break;
- case 0x2257:
- strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video",
- sizeof(inp->name));
- break;
- }
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
- return 0;
-}
-
-/* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- struct s2255_dev *dev = fh->dev;
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT);
- break;
- case V4L2_CID_CONTRAST:
- v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST);
- break;
- case V4L2_CID_SATURATION:
- v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION);
- break;
- case V4L2_CID_HUE:
- v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE);
- break;
- case V4L2_CID_PRIVATE_COLORFILTER:
- if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
- return -EINVAL;
- if ((dev->pid == 0x2257) && (channel->idx > 1))
- return -EINVAL;
- strlcpy(qc->name, "Color Filter", sizeof(qc->name));
- qc->type = V4L2_CTRL_TYPE_MENU;
- qc->minimum = 0;
- qc->maximum = 1;
- qc->step = 1;
- qc->default_value = 1;
- qc->flags = 0;
- break;
- default:
- return -EINVAL;
- }
- dprintk(4, "%s, id %d\n", __func__, qc->id);
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct s2255_fh *fh = priv;
- struct s2255_dev *dev = fh->dev;
- struct s2255_channel *channel = fh->channel;
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = channel->mode.bright;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = channel->mode.contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = channel->mode.saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = channel->mode.hue;
- break;
- case V4L2_CID_PRIVATE_COLORFILTER:
- if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
- return -EINVAL;
- if ((dev->pid == 0x2257) && (channel->idx > 1))
- return -EINVAL;
- ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16);
- break;
- default:
- return -EINVAL;
- }
- dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value);
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- struct s2255_mode mode;
- mode = channel->mode;
- dprintk(4, "%s\n", __func__);
- /* update the mode to the corresponding value */
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- mode.bright = ctrl->value;
- break;
- case V4L2_CID_CONTRAST:
- mode.contrast = ctrl->value;
- break;
- case V4L2_CID_HUE:
- mode.hue = ctrl->value;
- break;
- case V4L2_CID_SATURATION:
- mode.saturation = ctrl->value;
- break;
- case V4L2_CID_PRIVATE_COLORFILTER:
- if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
- return -EINVAL;
- if ((dev->pid == 0x2257) && (channel->idx > 1))
- return -EINVAL;
- mode.color &= ~MASK_INPUT_TYPE;
- mode.color |= ((ctrl->value ? 0 : 1) << 16);
- break;
- default:
- return -EINVAL;
- }
- mode.restart = 0;
- /* set mode here. Note: stream does not need restarted.
- some V4L programs restart stream unnecessarily
- after a s_crtl.
- */
- s2255_set_mode(fh->channel, &mode);
- return 0;
-}
-
-static int vidioc_g_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *jc)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- *jc = channel->jc;
- dprintk(2, "%s: quality %d\n", __func__, jc->quality);
- return 0;
-}
-
-static int vidioc_s_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *jc)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- if (jc->quality < 0 || jc->quality > 100)
- return -EINVAL;
- channel->jc.quality = jc->quality;
- dprintk(2, "%s: quality %d\n", __func__, jc->quality);
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *sp)
-{
- struct s2255_fh *fh = priv;
- __u32 def_num, def_dem;
- struct s2255_channel *channel = fh->channel;
- if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- memset(sp, 0, sizeof(struct v4l2_streamparm));
- sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- sp->parm.capture.capturemode = channel->cap_parm.capturemode;
- def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000;
- def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000;
- sp->parm.capture.timeperframe.denominator = def_dem;
- switch (channel->mode.fdec) {
- default:
- case FDEC_1:
- sp->parm.capture.timeperframe.numerator = def_num;
- break;
- case FDEC_2:
- sp->parm.capture.timeperframe.numerator = def_num * 2;
- break;
- case FDEC_3:
- sp->parm.capture.timeperframe.numerator = def_num * 3;
- break;
- case FDEC_5:
- sp->parm.capture.timeperframe.numerator = def_num * 5;
- break;
- }
- dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__,
- sp->parm.capture.capturemode,
- sp->parm.capture.timeperframe.numerator,
- sp->parm.capture.timeperframe.denominator);
- return 0;
-}
-
-static int vidioc_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *sp)
-{
- struct s2255_fh *fh = priv;
- struct s2255_channel *channel = fh->channel;
- struct s2255_mode mode;
- int fdec = FDEC_1;
- __u32 def_num, def_dem;
- if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- mode = channel->mode;
- /* high quality capture mode requires a stream restart */
- if (channel->cap_parm.capturemode
- != sp->parm.capture.capturemode && res_locked(fh))
- return -EBUSY;
- def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000;
- def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000;
- if (def_dem != sp->parm.capture.timeperframe.denominator)
- sp->parm.capture.timeperframe.numerator = def_num;
- else if (sp->parm.capture.timeperframe.numerator <= def_num)
- sp->parm.capture.timeperframe.numerator = def_num;
- else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) {
- sp->parm.capture.timeperframe.numerator = def_num * 2;
- fdec = FDEC_2;
- } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) {
- sp->parm.capture.timeperframe.numerator = def_num * 3;
- fdec = FDEC_3;
- } else {
- sp->parm.capture.timeperframe.numerator = def_num * 5;
- fdec = FDEC_5;
- }
- mode.fdec = fdec;
- sp->parm.capture.timeperframe.denominator = def_dem;
- s2255_set_mode(channel, &mode);
- dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n",
- __func__,
- sp->parm.capture.capturemode,
- sp->parm.capture.timeperframe.numerator,
- sp->parm.capture.timeperframe.denominator, fdec);
- return 0;
-}
-
-static int vidioc_enum_frameintervals(struct file *file, void *priv,
- struct v4l2_frmivalenum *fe)
-{
- int is_ntsc = 0;
-#define NUM_FRAME_ENUMS 4
- int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5};
- if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS)
- return -EINVAL;
- switch (fe->width) {
- case 640:
- if (fe->height != 240 && fe->height != 480)
- return -EINVAL;
- is_ntsc = 1;
- break;
- case 320:
- if (fe->height != 240)
- return -EINVAL;
- is_ntsc = 1;
- break;
- case 704:
- if (fe->height != 288 && fe->height != 576)
- return -EINVAL;
- break;
- case 352:
- if (fe->height != 288)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- fe->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fe->discrete.denominator = is_ntsc ? 30000 : 25000;
- fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index];
- dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator,
- fe->discrete.denominator);
- return 0;
-}
-
-static int s2255_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct s2255_channel *channel = video_drvdata(file);
- struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
- struct s2255_fh *fh;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- int state;
- dprintk(1, "s2255: open called (dev=%s)\n",
- video_device_node_name(vdev));
- /*
- * open lock necessary to prevent multiple instances
- * of v4l-conf (or other programs) from simultaneously
- * reloading firmware.
- */
- mutex_lock(&dev->open_lock);
- state = atomic_read(&dev->fw_data->fw_state);
- switch (state) {
- case S2255_FW_DISCONNECTING:
- mutex_unlock(&dev->open_lock);
- return -ENODEV;
- case S2255_FW_FAILED:
- s2255_dev_err(&dev->udev->dev,
- "firmware load failed. retrying.\n");
- s2255_fwload_start(dev, 1);
- wait_event_timeout(dev->fw_data->wait_fw,
- ((atomic_read(&dev->fw_data->fw_state)
- == S2255_FW_SUCCESS) ||
- (atomic_read(&dev->fw_data->fw_state)
- == S2255_FW_DISCONNECTING)),
- msecs_to_jiffies(S2255_LOAD_TIMEOUT));
- /* state may have changed, re-read */
- state = atomic_read(&dev->fw_data->fw_state);
- break;
- case S2255_FW_NOTLOADED:
- case S2255_FW_LOADED_DSPWAIT:
- /* give S2255_LOAD_TIMEOUT time for firmware to load in case
- driver loaded and then device immediately opened */
- printk(KERN_INFO "%s waiting for firmware load\n", __func__);
- wait_event_timeout(dev->fw_data->wait_fw,
- ((atomic_read(&dev->fw_data->fw_state)
- == S2255_FW_SUCCESS) ||
- (atomic_read(&dev->fw_data->fw_state)
- == S2255_FW_DISCONNECTING)),
- msecs_to_jiffies(S2255_LOAD_TIMEOUT));
- /* state may have changed, re-read */
- state = atomic_read(&dev->fw_data->fw_state);
- break;
- case S2255_FW_SUCCESS:
- default:
- break;
- }
- /* state may have changed in above switch statement */
- switch (state) {
- case S2255_FW_SUCCESS:
- break;
- case S2255_FW_FAILED:
- printk(KERN_INFO "2255 firmware load failed.\n");
- mutex_unlock(&dev->open_lock);
- return -ENODEV;
- case S2255_FW_DISCONNECTING:
- printk(KERN_INFO "%s: disconnecting\n", __func__);
- mutex_unlock(&dev->open_lock);
- return -ENODEV;
- case S2255_FW_LOADED_DSPWAIT:
- case S2255_FW_NOTLOADED:
- printk(KERN_INFO "%s: firmware not loaded yet"
- "please try again later\n",
- __func__);
- /*
- * Timeout on firmware load means device unusable.
- * Set firmware failure state.
- * On next s2255_open the firmware will be reloaded.
- */
- atomic_set(&dev->fw_data->fw_state,
- S2255_FW_FAILED);
- mutex_unlock(&dev->open_lock);
- return -EAGAIN;
- default:
- printk(KERN_INFO "%s: unknown state\n", __func__);
- mutex_unlock(&dev->open_lock);
- return -EFAULT;
- }
- mutex_unlock(&dev->open_lock);
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
- file->private_data = fh;
- fh->dev = dev;
- fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fh->channel = channel;
- if (!channel->configured) {
- /* configure channel to default state */
- channel->fmt = &formats[0];
- s2255_set_mode(channel, &channel->mode);
- channel->configured = 1;
- }
- dprintk(1, "%s: dev=%s type=%s\n", __func__,
- video_device_node_name(vdev), v4l2_type_names[type]);
- dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__,
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&channel->vidq);
- dprintk(4, "%s: list_empty active=%d\n", __func__,
- list_empty(&channel->vidq.active));
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops,
- NULL, &dev->slock,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct s2255_buffer),
- fh, vdev->lock);
- return 0;
-}
-
-
-static unsigned int s2255_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct s2255_fh *fh = file->private_data;
- int rc;
- dprintk(100, "%s\n", __func__);
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
- return POLLERR;
- rc = videobuf_poll_stream(file, &fh->vb_vidq, wait);
- return rc;
-}
-
-static void s2255_destroy(struct s2255_dev *dev)
-{
- /* board shutdown stops the read pipe if it is running */
- s2255_board_shutdown(dev);
- /* make sure firmware still not trying to load */
- del_timer(&dev->timer); /* only started in .probe and .open */
- if (dev->fw_data->fw_urb) {
- usb_kill_urb(dev->fw_data->fw_urb);
- usb_free_urb(dev->fw_data->fw_urb);
- dev->fw_data->fw_urb = NULL;
- }
- release_firmware(dev->fw_data->fw);
- kfree(dev->fw_data->pfw_data);
- kfree(dev->fw_data);
- /* reset the DSP so firmware can be reloaded next time */
- s2255_reset_dsppower(dev);
- mutex_destroy(&dev->open_lock);
- mutex_destroy(&dev->lock);
- usb_put_dev(dev->udev);
- v4l2_device_unregister(&dev->v4l2_dev);
- dprintk(1, "%s", __func__);
- kfree(dev);
-}
-
-static int s2255_release(struct file *file)
-{
- struct s2255_fh *fh = file->private_data;
- struct s2255_dev *dev = fh->dev;
- struct video_device *vdev = video_devdata(file);
- struct s2255_channel *channel = fh->channel;
- if (!dev)
- return -ENODEV;
- /* turn off stream */
- if (res_check(fh)) {
- if (channel->b_acquire)
- s2255_stop_acquire(fh->channel);
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
- }
- videobuf_mmap_free(&fh->vb_vidq);
- dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev));
- kfree(fh);
- return 0;
-}
-
-static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma)
-{
- struct s2255_fh *fh = file->private_data;
- int ret;
-
- if (!fh)
- return -ENODEV;
- dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma);
- ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);
- dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__,
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret);
- return ret;
-}
-
-static const struct v4l2_file_operations s2255_fops_v4l = {
- .owner = THIS_MODULE,
- .open = s2255_open,
- .release = s2255_release,
- .poll = s2255_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = s2255_mmap_v4l,
-};
-
-static const struct v4l2_ioctl_ops s2255_ioctl_ops = {
- .vidioc_querymenu = vidioc_querymenu,
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_s_jpegcomp = vidioc_s_jpegcomp,
- .vidioc_g_jpegcomp = vidioc_g_jpegcomp,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_g_parm = vidioc_g_parm,
- .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
-};
-
-static void s2255_video_device_release(struct video_device *vdev)
-{
- struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev);
- dprintk(4, "%s, chnls: %d \n", __func__,
- atomic_read(&dev->num_channels));
- if (atomic_dec_and_test(&dev->num_channels))
- s2255_destroy(dev);
- return;
-}
-
-static struct video_device template = {
- .name = "s2255v",
- .fops = &s2255_fops_v4l,
- .ioctl_ops = &s2255_ioctl_ops,
- .release = s2255_video_device_release,
- .tvnorms = S2255_NORMS,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static int s2255_probe_v4l(struct s2255_dev *dev)
-{
- int ret;
- int i;
- int cur_nr = video_nr;
- struct s2255_channel *channel;
- ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev);
- if (ret)
- return ret;
- /* initialize all video 4 linux */
- /* register 4 video devices */
- for (i = 0; i < MAX_CHANNELS; i++) {
- channel = &dev->channel[i];
- INIT_LIST_HEAD(&channel->vidq.active);
- channel->vidq.dev = dev;
- /* register 4 video devices */
- channel->vdev = template;
- channel->vdev.lock = &dev->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &channel->vdev.flags);
- channel->vdev.v4l2_dev = &dev->v4l2_dev;
- video_set_drvdata(&channel->vdev, channel);
- if (video_nr == -1)
- ret = video_register_device(&channel->vdev,
- VFL_TYPE_GRABBER,
- video_nr);
- else
- ret = video_register_device(&channel->vdev,
- VFL_TYPE_GRABBER,
- cur_nr + i);
-
- if (ret) {
- dev_err(&dev->udev->dev,
- "failed to register video device!\n");
- break;
- }
- atomic_inc(&dev->num_channels);
- v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
- video_device_node_name(&channel->vdev));
-
- }
- printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n",
- S2255_VERSION);
- /* if no channels registered, return error and probe will fail*/
- if (atomic_read(&dev->num_channels) == 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- return ret;
- }
- if (atomic_read(&dev->num_channels) != MAX_CHANNELS)
- printk(KERN_WARNING "s2255: Not all channels available.\n");
- return 0;
-}
-
-/* this function moves the usb stream read pipe data
- * into the system buffers.
- * returns 0 on success, EAGAIN if more data to process( call this
- * function again).
- *
- * Received frame structure:
- * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME)
- * bytes 4-7: channel: 0-3
- * bytes 8-11: payload size: size of the frame
- * bytes 12-payloadsize+12: frame data
- */
-static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
-{
- char *pdest;
- u32 offset = 0;
- int bframe = 0;
- char *psrc;
- unsigned long copy_size;
- unsigned long size;
- s32 idx = -1;
- struct s2255_framei *frm;
- unsigned char *pdata;
- struct s2255_channel *channel;
- dprintk(100, "buffer to user\n");
- channel = &dev->channel[dev->cc];
- idx = channel->cur_frame;
- frm = &channel->buffer.frame[idx];
- if (frm->ulState == S2255_READ_IDLE) {
- int jj;
- unsigned int cc;
- __le32 *pdword; /*data from dsp is little endian */
- int payload;
- /* search for marker codes */
- pdata = (unsigned char *)pipe_info->transfer_buffer;
- pdword = (__le32 *)pdata;
- for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) {
- switch (*pdword) {
- case S2255_MARKER_FRAME:
- dprintk(4, "found frame marker at offset:"
- " %d [%x %x]\n", jj, pdata[0],
- pdata[1]);
- offset = jj + PREFIX_SIZE;
- bframe = 1;
- cc = le32_to_cpu(pdword[1]);
- if (cc >= MAX_CHANNELS) {
- printk(KERN_ERR
- "bad channel\n");
- return -EINVAL;
- }
- /* reverse it */
- dev->cc = G_chnmap[cc];
- channel = &dev->channel[dev->cc];
- payload = le32_to_cpu(pdword[3]);
- if (payload > channel->req_image_size) {
- channel->bad_payload++;
- /* discard the bad frame */
- return -EINVAL;
- }
- channel->pkt_size = payload;
- channel->jpg_size = le32_to_cpu(pdword[4]);
- break;
- case S2255_MARKER_RESPONSE:
-
- pdata += DEF_USB_BLOCK;
- jj += DEF_USB_BLOCK;
- if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
- break;
- cc = G_chnmap[le32_to_cpu(pdword[1])];
- if (cc >= MAX_CHANNELS)
- break;
- channel = &dev->channel[cc];
- switch (pdword[2]) {
- case S2255_RESPONSE_SETMODE:
- /* check if channel valid */
- /* set mode ready */
- channel->setmode_ready = 1;
- wake_up(&channel->wait_setmode);
- dprintk(5, "setmode ready %d\n", cc);
- break;
- case S2255_RESPONSE_FW:
- dev->chn_ready |= (1 << cc);
- if ((dev->chn_ready & 0x0f) != 0x0f)
- break;
- /* all channels ready */
- printk(KERN_INFO "s2255: fw loaded\n");
- atomic_set(&dev->fw_data->fw_state,
- S2255_FW_SUCCESS);
- wake_up(&dev->fw_data->wait_fw);
- break;
- case S2255_RESPONSE_STATUS:
- channel->vidstatus = le32_to_cpu(pdword[3]);
- channel->vidstatus_ready = 1;
- wake_up(&channel->wait_vidstatus);
- dprintk(5, "got vidstatus %x chan %d\n",
- le32_to_cpu(pdword[3]), cc);
- break;
- default:
- printk(KERN_INFO "s2255 unknown resp\n");
- }
- default:
- pdata++;
- break;
- }
- if (bframe)
- break;
- } /* for */
- if (!bframe)
- return -EINVAL;
- }
- channel = &dev->channel[dev->cc];
- idx = channel->cur_frame;
- frm = &channel->buffer.frame[idx];
- /* search done. now find out if should be acquiring on this channel */
- if (!channel->b_acquire) {
- /* we found a frame, but this channel is turned off */
- frm->ulState = S2255_READ_IDLE;
- return -EINVAL;
- }
-
- if (frm->ulState == S2255_READ_IDLE) {
- frm->ulState = S2255_READ_FRAME;
- frm->cur_size = 0;
- }
-
- /* skip the marker 512 bytes (and offset if out of sync) */
- psrc = (u8 *)pipe_info->transfer_buffer + offset;
-
-
- if (frm->lpvbits == NULL) {
- dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d",
- frm, dev, dev->cc, idx);
- return -ENOMEM;
- }
-
- pdest = frm->lpvbits + frm->cur_size;
-
- copy_size = (pipe_info->cur_transfer_size - offset);
-
- size = channel->pkt_size - PREFIX_SIZE;
-
- /* sanity check on pdest */
- if ((copy_size + frm->cur_size) < channel->req_image_size)
- memcpy(pdest, psrc, copy_size);
-
- frm->cur_size += copy_size;
- dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size);
-
- if (frm->cur_size >= size) {
- dprintk(2, "****************[%d]Buffer[%d]full*************\n",
- dev->cc, idx);
- channel->last_frame = channel->cur_frame;
- channel->cur_frame++;
- /* end of system frame ring buffer, start at zero */
- if ((channel->cur_frame == SYS_FRAMES) ||
- (channel->cur_frame == channel->buffer.dwFrames))
- channel->cur_frame = 0;
- /* frame ready */
- if (channel->b_acquire)
- s2255_got_frame(channel, channel->jpg_size);
- channel->frame_count++;
- frm->ulState = S2255_READ_IDLE;
- frm->cur_size = 0;
-
- }
- /* done successfully */
- return 0;
-}
-
-static void s2255_read_video_callback(struct s2255_dev *dev,
- struct s2255_pipeinfo *pipe_info)
-{
- int res;
- dprintk(50, "callback read video \n");
-
- if (dev->cc >= MAX_CHANNELS) {
- dev->cc = 0;
- dev_err(&dev->udev->dev, "invalid channel\n");
- return;
- }
- /* otherwise copy to the system buffers */
- res = save_frame(dev, pipe_info);
- if (res != 0)
- dprintk(4, "s2255: read callback failed\n");
-
- dprintk(50, "callback read video done\n");
- return;
-}
-
-static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request,
- u16 Index, u16 Value, void *TransferBuffer,
- s32 TransferBufferLength, int bOut)
-{
- int r;
- if (!bOut) {
- r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
- Request,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE |
- USB_DIR_IN,
- Value, Index, TransferBuffer,
- TransferBufferLength, HZ * 5);
- } else {
- r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
- Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- Value, Index, TransferBuffer,
- TransferBufferLength, HZ * 5);
- }
- return r;
-}
-
-/*
- * retrieve FX2 firmware version. future use.
- * @param dev pointer to device extension
- * @return -1 for fail, else returns firmware version as an int(16 bits)
- */
-static int s2255_get_fx2fw(struct s2255_dev *dev)
-{
- int fw;
- int ret;
- unsigned char transBuffer[64];
- ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2,
- S2255_VR_IN);
- if (ret < 0)
- dprintk(2, "get fw error: %x\n", ret);
- fw = transBuffer[0] + (transBuffer[1] << 8);
- dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]);
- return fw;
-}
-
-/*
- * Create the system ring buffer to copy frames into from the
- * usb read pipe.
- */
-static int s2255_create_sys_buffers(struct s2255_channel *channel)
-{
- unsigned long i;
- unsigned long reqsize;
- dprintk(1, "create sys buffers\n");
- channel->buffer.dwFrames = SYS_FRAMES;
- /* always allocate maximum size(PAL) for system buffers */
- reqsize = SYS_FRAMES_MAXSIZE;
-
- if (reqsize > SYS_FRAMES_MAXSIZE)
- reqsize = SYS_FRAMES_MAXSIZE;
-
- for (i = 0; i < SYS_FRAMES; i++) {
- /* allocate the frames */
- channel->buffer.frame[i].lpvbits = vmalloc(reqsize);
- dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n",
- &channel->buffer.frame[i], channel->idx, i,
- channel->buffer.frame[i].lpvbits);
- channel->buffer.frame[i].size = reqsize;
- if (channel->buffer.frame[i].lpvbits == NULL) {
- printk(KERN_INFO "out of memory. using less frames\n");
- channel->buffer.dwFrames = i;
- break;
- }
- }
-
- /* make sure internal states are set */
- for (i = 0; i < SYS_FRAMES; i++) {
- channel->buffer.frame[i].ulState = 0;
- channel->buffer.frame[i].cur_size = 0;
- }
-
- channel->cur_frame = 0;
- channel->last_frame = -1;
- return 0;
-}
-
-static int s2255_release_sys_buffers(struct s2255_channel *channel)
-{
- unsigned long i;
- dprintk(1, "release sys buffers\n");
- for (i = 0; i < SYS_FRAMES; i++) {
- if (channel->buffer.frame[i].lpvbits) {
- dprintk(1, "vfree %p\n",
- channel->buffer.frame[i].lpvbits);
- vfree(channel->buffer.frame[i].lpvbits);
- }
- channel->buffer.frame[i].lpvbits = NULL;
- }
- return 0;
-}
-
-static int s2255_board_init(struct s2255_dev *dev)
-{
- struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT;
- int fw_ver;
- int j;
- struct s2255_pipeinfo *pipe = &dev->pipe;
- dprintk(4, "board init: %p", dev);
- memset(pipe, 0, sizeof(*pipe));
- pipe->dev = dev;
- pipe->cur_transfer_size = S2255_USB_XFER_SIZE;
- pipe->max_transfer_size = S2255_USB_XFER_SIZE;
-
- pipe->transfer_buffer = kzalloc(pipe->max_transfer_size,
- GFP_KERNEL);
- if (pipe->transfer_buffer == NULL) {
- dprintk(1, "out of memory!\n");
- return -ENOMEM;
- }
- /* query the firmware */
- fw_ver = s2255_get_fx2fw(dev);
-
- printk(KERN_INFO "s2255: usb firmware version %d.%d\n",
- (fw_ver >> 8) & 0xff,
- fw_ver & 0xff);
-
- if (fw_ver < S2255_CUR_USB_FWVER)
- printk(KERN_INFO "s2255: newer USB firmware available\n");
-
- for (j = 0; j < MAX_CHANNELS; j++) {
- struct s2255_channel *channel = &dev->channel[j];
- channel->b_acquire = 0;
- channel->mode = mode_def;
- if (dev->pid == 0x2257 && j > 1)
- channel->mode.color |= (1 << 16);
- channel->jc.quality = S2255_DEF_JPEG_QUAL;
- channel->width = LINE_SZ_4CIFS_NTSC;
- channel->height = NUM_LINES_4CIFS_NTSC * 2;
- channel->fmt = &formats[0];
- channel->mode.restart = 1;
- channel->req_image_size = get_transfer_size(&mode_def);
- channel->frame_count = 0;
- /* create the system buffers */
- s2255_create_sys_buffers(channel);
- }
- /* start read pipe */
- s2255_start_readpipe(dev);
- dprintk(1, "%s: success\n", __func__);
- return 0;
-}
-
-static int s2255_board_shutdown(struct s2255_dev *dev)
-{
- u32 i;
- dprintk(1, "%s: dev: %p", __func__, dev);
-
- for (i = 0; i < MAX_CHANNELS; i++) {
- if (dev->channel[i].b_acquire)
- s2255_stop_acquire(&dev->channel[i]);
- }
- s2255_stop_readpipe(dev);
- for (i = 0; i < MAX_CHANNELS; i++)
- s2255_release_sys_buffers(&dev->channel[i]);
- /* release transfer buffer */
- kfree(dev->pipe.transfer_buffer);
- return 0;
-}
-
-static void read_pipe_completion(struct urb *purb)
-{
- struct s2255_pipeinfo *pipe_info;
- struct s2255_dev *dev;
- int status;
- int pipe;
- pipe_info = purb->context;
- dprintk(100, "%s: urb:%p, status %d\n", __func__, purb,
- purb->status);
- if (pipe_info == NULL) {
- dev_err(&purb->dev->dev, "no context!\n");
- return;
- }
-
- dev = pipe_info->dev;
- if (dev == NULL) {
- dev_err(&purb->dev->dev, "no context!\n");
- return;
- }
- status = purb->status;
- /* if shutting down, do not resubmit, exit immediately */
- if (status == -ESHUTDOWN) {
- dprintk(2, "%s: err shutdown\n", __func__);
- pipe_info->err_count++;
- return;
- }
-
- if (pipe_info->state == 0) {
- dprintk(2, "%s: exiting USB pipe", __func__);
- return;
- }
-
- if (status == 0)
- s2255_read_video_callback(dev, pipe_info);
- else {
- pipe_info->err_count++;
- dprintk(1, "%s: failed URB %d\n", __func__, status);
- }
-
- pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
- /* reuse urb */
- usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
- pipe,
- pipe_info->transfer_buffer,
- pipe_info->cur_transfer_size,
- read_pipe_completion, pipe_info);
-
- if (pipe_info->state != 0) {
- if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) {
- dev_err(&dev->udev->dev, "error submitting urb\n");
- }
- } else {
- dprintk(2, "%s :complete state 0\n", __func__);
- }
- return;
-}
-
-static int s2255_start_readpipe(struct s2255_dev *dev)
-{
- int pipe;
- int retval;
- struct s2255_pipeinfo *pipe_info = &dev->pipe;
- pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint);
- dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint);
- pipe_info->state = 1;
- pipe_info->err_count = 0;
- pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!pipe_info->stream_urb) {
- dev_err(&dev->udev->dev,
- "ReadStream: Unable to alloc URB\n");
- return -ENOMEM;
- }
- /* transfer buffer allocated in board_init */
- usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev,
- pipe,
- pipe_info->transfer_buffer,
- pipe_info->cur_transfer_size,
- read_pipe_completion, pipe_info);
- retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL);
- if (retval) {
- printk(KERN_ERR "s2255: start read pipe failed\n");
- return retval;
- }
- return 0;
-}
-
-/* starts acquisition process */
-static int s2255_start_acquire(struct s2255_channel *channel)
-{
- unsigned char *buffer;
- int res;
- unsigned long chn_rev;
- int j;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
-
- channel->last_frame = -1;
- channel->bad_payload = 0;
- channel->cur_frame = 0;
- for (j = 0; j < SYS_FRAMES; j++) {
- channel->buffer.frame[j].ulState = 0;
- channel->buffer.frame[j].cur_size = 0;
- }
-
- /* send the start command */
- *(__le32 *) buffer = IN_DATA_TOKEN;
- *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
- *((__le32 *) buffer + 2) = CMD_START;
- res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
- if (res != 0)
- dev_err(&dev->udev->dev, "CMD_START error\n");
-
- dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res);
- kfree(buffer);
- return 0;
-}
-
-static int s2255_stop_acquire(struct s2255_channel *channel)
-{
- unsigned char *buffer;
- int res;
- unsigned long chn_rev;
- struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev);
- chn_rev = G_chnmap[channel->idx];
- buffer = kzalloc(512, GFP_KERNEL);
- if (buffer == NULL) {
- dev_err(&dev->udev->dev, "out of mem\n");
- return -ENOMEM;
- }
- /* send the stop command */
- *(__le32 *) buffer = IN_DATA_TOKEN;
- *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev);
- *((__le32 *) buffer + 2) = CMD_STOP;
- res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512);
- if (res != 0)
- dev_err(&dev->udev->dev, "CMD_STOP error\n");
- kfree(buffer);
- channel->b_acquire = 0;
- dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res);
- return res;
-}
-
-static void s2255_stop_readpipe(struct s2255_dev *dev)
-{
- struct s2255_pipeinfo *pipe = &dev->pipe;
-
- pipe->state = 0;
- if (pipe->stream_urb) {
- /* cancel urb */
- usb_kill_urb(pipe->stream_urb);
- usb_free_urb(pipe->stream_urb);
- pipe->stream_urb = NULL;
- }
- dprintk(4, "%s", __func__);
- return;
-}
-
-static void s2255_fwload_start(struct s2255_dev *dev, int reset)
-{
- if (reset)
- s2255_reset_dsppower(dev);
- dev->fw_data->fw_size = dev->fw_data->fw->size;
- atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
- memcpy(dev->fw_data->pfw_data,
- dev->fw_data->fw->data, CHUNK_SIZE);
- dev->fw_data->fw_loaded = CHUNK_SIZE;
- usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev,
- usb_sndbulkpipe(dev->udev, 2),
- dev->fw_data->pfw_data,
- CHUNK_SIZE, s2255_fwchunk_complete,
- dev->fw_data);
- mod_timer(&dev->timer, jiffies + HZ);
-}
-
-/* standard usb probe function */
-static int s2255_probe(struct usb_interface *interface,
- const struct usb_device_id *id)
-{
- struct s2255_dev *dev = NULL;
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- int i;
- int retval = -ENOMEM;
- __le32 *pdata;
- int fw_size;
- dprintk(2, "%s\n", __func__);
- /* allocate memory for our device state and initialize it to zero */
- dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
- if (dev == NULL) {
- s2255_dev_err(&interface->dev, "out of memory\n");
- return -ENOMEM;
- }
- atomic_set(&dev->num_channels, 0);
- dev->pid = id->idProduct;
- dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL);
- if (!dev->fw_data)
- goto errorFWDATA1;
- mutex_init(&dev->lock);
- mutex_init(&dev->open_lock);
- /* grab usb_device and save it */
- dev->udev = usb_get_dev(interface_to_usbdev(interface));
- if (dev->udev == NULL) {
- dev_err(&interface->dev, "null usb device\n");
- retval = -ENODEV;
- goto errorUDEV;
- }
- dprintk(1, "dev: %p, udev %p interface %p\n", dev,
- dev->udev, interface);
- dev->interface = interface;
- /* set up the endpoint information */
- iface_desc = interface->cur_altsetting;
- dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints);
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
- /* we found the bulk in endpoint */
- dev->read_endpoint = endpoint->bEndpointAddress;
- }
- }
-
- if (!dev->read_endpoint) {
- dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
- goto errorEP;
- }
- init_timer(&dev->timer);
- dev->timer.function = s2255_timer;
- dev->timer.data = (unsigned long)dev->fw_data;
- init_waitqueue_head(&dev->fw_data->wait_fw);
- for (i = 0; i < MAX_CHANNELS; i++) {
- struct s2255_channel *channel = &dev->channel[i];
- dev->channel[i].idx = i;
- init_waitqueue_head(&channel->wait_setmode);
- init_waitqueue_head(&channel->wait_vidstatus);
- }
-
- dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->fw_data->fw_urb) {
- dev_err(&interface->dev, "out of memory!\n");
- goto errorFWURB;
- }
-
- dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL);
- if (!dev->fw_data->pfw_data) {
- dev_err(&interface->dev, "out of memory!\n");
- goto errorFWDATA2;
- }
- /* load the first chunk */
- if (request_firmware(&dev->fw_data->fw,
- FIRMWARE_FILE_NAME, &dev->udev->dev)) {
- printk(KERN_ERR "sensoray 2255 failed to get firmware\n");
- goto errorREQFW;
- }
- /* check the firmware is valid */
- fw_size = dev->fw_data->fw->size;
- pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8];
-
- if (*pdata != S2255_FW_MARKER) {
- printk(KERN_INFO "Firmware invalid.\n");
- retval = -ENODEV;
- goto errorFWMARKER;
- } else {
- /* make sure firmware is the latest */
- __le32 *pRel;
- pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
- printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
- dev->dsp_fw_ver = le32_to_cpu(*pRel);
- if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
- printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
- if (dev->pid == 0x2257 &&
- dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
- printk(KERN_WARNING "s2255: 2257 requires firmware %d"
- " or above.\n", S2255_MIN_DSP_COLORFILTER);
- }
- usb_reset_device(dev->udev);
- /* load 2255 board specific */
- retval = s2255_board_init(dev);
- if (retval)
- goto errorBOARDINIT;
- spin_lock_init(&dev->slock);
- s2255_fwload_start(dev, 0);
- /* loads v4l specific */
- retval = s2255_probe_v4l(dev);
- if (retval)
- goto errorBOARDINIT;
- dev_info(&interface->dev, "Sensoray 2255 detected\n");
- return 0;
-errorBOARDINIT:
- s2255_board_shutdown(dev);
-errorFWMARKER:
- release_firmware(dev->fw_data->fw);
-errorREQFW:
- kfree(dev->fw_data->pfw_data);
-errorFWDATA2:
- usb_free_urb(dev->fw_data->fw_urb);
-errorFWURB:
- del_timer(&dev->timer);
-errorEP:
- usb_put_dev(dev->udev);
-errorUDEV:
- kfree(dev->fw_data);
- mutex_destroy(&dev->open_lock);
- mutex_destroy(&dev->lock);
-errorFWDATA1:
- kfree(dev);
- printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval);
- return retval;
-}
-
-/* disconnect routine. when board is removed physically or with rmmod */
-static void s2255_disconnect(struct usb_interface *interface)
-{
- struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface));
- int i;
- int channels = atomic_read(&dev->num_channels);
- mutex_lock(&dev->lock);
- v4l2_device_disconnect(&dev->v4l2_dev);
- mutex_unlock(&dev->lock);
- /*see comments in the uvc_driver.c usb disconnect function */
- atomic_inc(&dev->num_channels);
- /* unregister each video device. */
- for (i = 0; i < channels; i++)
- video_unregister_device(&dev->channel[i].vdev);
- /* wake up any of our timers */
- atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING);
- wake_up(&dev->fw_data->wait_fw);
- for (i = 0; i < MAX_CHANNELS; i++) {
- dev->channel[i].setmode_ready = 1;
- wake_up(&dev->channel[i].wait_setmode);
- dev->channel[i].vidstatus_ready = 1;
- wake_up(&dev->channel[i].wait_vidstatus);
- }
- if (atomic_dec_and_test(&dev->num_channels))
- s2255_destroy(dev);
- dev_info(&interface->dev, "%s\n", __func__);
-}
-
-static struct usb_driver s2255_driver = {
- .name = S2255_DRIVER_NAME,
- .probe = s2255_probe,
- .disconnect = s2255_disconnect,
- .id_table = s2255_table,
-};
-
-module_usb_driver(s2255_driver);
-
-MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver");
-MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(S2255_VERSION);
-MODULE_FIRMWARE(FIRMWARE_FILE_NAME);
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
deleted file mode 100644
index 6625e46a4638..000000000000
--- a/drivers/media/video/s5k6aa.c
+++ /dev/null
@@ -1,1670 +0,0 @@
-/*
- * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor
- * with embedded SoC ISP.
- *
- * Copyright (C) 2011, Samsung Electronics Co., Ltd.
- * Sylwester Nawrocki <s.nawrocki@samsung.com>
- *
- * Based on a driver authored by Dongsoo Nathaniel Kim.
- * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.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 version.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/media.h>
-#include <linux/module.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-
-#include <media/media-entity.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-#include <media/v4l2-mediabus.h>
-#include <media/s5k6aa.h>
-
-static int debug;
-module_param(debug, int, 0644);
-
-#define DRIVER_NAME "S5K6AA"
-
-/* The token to indicate array termination */
-#define S5K6AA_TERM 0xffff
-#define S5K6AA_OUT_WIDTH_DEF 640
-#define S5K6AA_OUT_HEIGHT_DEF 480
-#define S5K6AA_WIN_WIDTH_MAX 1280
-#define S5K6AA_WIN_HEIGHT_MAX 1024
-#define S5K6AA_WIN_WIDTH_MIN 8
-#define S5K6AA_WIN_HEIGHT_MIN 8
-
-/*
- * H/W register Interface (0xD0000000 - 0xD0000FFF)
- */
-#define AHB_MSB_ADDR_PTR 0xfcfc
-#define GEN_REG_OFFSH 0xd000
-#define REG_CMDWR_ADDRH 0x0028
-#define REG_CMDWR_ADDRL 0x002a
-#define REG_CMDRD_ADDRH 0x002c
-#define REG_CMDRD_ADDRL 0x002e
-#define REG_CMDBUF0_ADDR 0x0f12
-#define REG_CMDBUF1_ADDR 0x0f10
-
-/*
- * Host S/W Register interface (0x70000000 - 0x70002000)
- * The value of the two most significant address bytes is 0x7000,
- * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs.
- */
-#define HOST_SWIF_OFFSH 0x7000
-
-/* Initialization parameters */
-/* Master clock frequency in KHz */
-#define REG_I_INCLK_FREQ_L 0x01b8
-#define REG_I_INCLK_FREQ_H 0x01ba
-#define MIN_MCLK_FREQ_KHZ 6000U
-#define MAX_MCLK_FREQ_KHZ 27000U
-#define REG_I_USE_NPVI_CLOCKS 0x01c6
-#define REG_I_USE_NMIPI_CLOCKS 0x01c8
-
-/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */
-#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc)
-#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce)
-#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0)
-#define SYS_PLL_OUT_FREQ (48000000 / 4000)
-#define PCLK_FREQ_MIN (24000000 / 4000)
-#define PCLK_FREQ_MAX (48000000 / 4000)
-#define REG_I_INIT_PARAMS_UPDATED 0x01e0
-#define REG_I_ERROR_INFO 0x01e2
-
-/* General purpose parameters */
-#define REG_USER_BRIGHTNESS 0x01e4
-#define REG_USER_CONTRAST 0x01e6
-#define REG_USER_SATURATION 0x01e8
-#define REG_USER_SHARPBLUR 0x01ea
-
-#define REG_G_SPEC_EFFECTS 0x01ee
-#define REG_G_ENABLE_PREV 0x01f0
-#define REG_G_ENABLE_PREV_CHG 0x01f2
-#define REG_G_NEW_CFG_SYNC 0x01f8
-#define REG_G_PREVZOOM_IN_WIDTH 0x020a
-#define REG_G_PREVZOOM_IN_HEIGHT 0x020c
-#define REG_G_PREVZOOM_IN_XOFFS 0x020e
-#define REG_G_PREVZOOM_IN_YOFFS 0x0210
-#define REG_G_INPUTS_CHANGE_REQ 0x021a
-#define REG_G_ACTIVE_PREV_CFG 0x021c
-#define REG_G_PREV_CFG_CHG 0x021e
-#define REG_G_PREV_OPEN_AFTER_CH 0x0220
-#define REG_G_PREV_CFG_ERROR 0x0222
-
-/* Preview control section. n = 0...4. */
-#define PREG(n, x) ((n) * 0x26 + x)
-#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242)
-#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244)
-#define REG_P_FMT(n) PREG(n, 0x0246)
-#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248)
-#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a)
-#define REG_P_PVI_MASK(n) PREG(n, 0x024c)
-#define REG_P_CLK_INDEX(n) PREG(n, 0x024e)
-#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250)
-#define FR_RATE_DYNAMIC 0
-#define FR_RATE_FIXED 1
-#define FR_RATE_FIXED_ACCURATE 2
-#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252)
-#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */
-#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */
-/* Frame period in 0.1 ms units */
-#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254)
-#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256)
-/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */
-#define US_TO_FR_TIME(__t) ((__t) / 100)
-#define S5K6AA_MIN_FR_TIME 33300 /* us */
-#define S5K6AA_MAX_FR_TIME 650000 /* us */
-#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */
-/* The below 5 registers are for "device correction" values */
-#define REG_P_COLORTEMP(n) PREG(n, 0x025e)
-#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262)
-
-/* Extended image property controls */
-/* Exposure time in 10 us units */
-#define REG_SF_USR_EXPOSURE_L 0x03c6
-#define REG_SF_USR_EXPOSURE_H 0x03c8
-#define REG_SF_USR_EXPOSURE_CHG 0x03ca
-#define REG_SF_USR_TOT_GAIN 0x03cc
-#define REG_SF_USR_TOT_GAIN_CHG 0x03ce
-#define REG_SF_RGAIN 0x03d0
-#define REG_SF_RGAIN_CHG 0x03d2
-#define REG_SF_GGAIN 0x03d4
-#define REG_SF_GGAIN_CHG 0x03d6
-#define REG_SF_BGAIN 0x03d8
-#define REG_SF_BGAIN_CHG 0x03da
-#define REG_SF_FLICKER_QUANT 0x03dc
-#define REG_SF_FLICKER_QUANT_CHG 0x03de
-
-/* Output interface (parallel/MIPI) setup */
-#define REG_OIF_EN_MIPI_LANES 0x03fa
-#define REG_OIF_EN_PACKETS 0x03fc
-#define REG_OIF_CFG_CHG 0x03fe
-
-/* Auto-algorithms enable mask */
-#define REG_DBG_AUTOALG_EN 0x0400
-#define AALG_ALL_EN_MASK (1 << 0)
-#define AALG_AE_EN_MASK (1 << 1)
-#define AALG_DIVLEI_EN_MASK (1 << 2)
-#define AALG_WB_EN_MASK (1 << 3)
-#define AALG_FLICKER_EN_MASK (1 << 5)
-#define AALG_FIT_EN_MASK (1 << 6)
-#define AALG_WRHW_EN_MASK (1 << 7)
-
-/* Firmware revision information */
-#define REG_FW_APIVER 0x012e
-#define S5K6AAFX_FW_APIVER 0x0001
-#define REG_FW_REVISION 0x0130
-
-/* For now we use only one user configuration register set */
-#define S5K6AA_MAX_PRESETS 1
-
-static const char * const s5k6aa_supply_names[] = {
- "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */
- "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */
- "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V)
- or 2.8V (2.6V to 3.0) */
- "vddio", /* I/O supply 1.8V (1.65V to 1.95V)
- or 2.8V (2.5V to 3.1V) */
-};
-#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names)
-
-enum s5k6aa_gpio_id {
- STBY,
- RST,
- GPIO_NUM,
-};
-
-struct s5k6aa_regval {
- u16 addr;
- u16 val;
-};
-
-struct s5k6aa_pixfmt {
- enum v4l2_mbus_pixelcode code;
- u32 colorspace;
- /* REG_P_FMT(x) register value */
- u16 reg_p_fmt;
-};
-
-struct s5k6aa_preset {
- /* output pixel format and resolution */
- struct v4l2_mbus_framefmt mbus_fmt;
- u8 clk_id;
- u8 index;
-};
-
-struct s5k6aa_ctrls {
- struct v4l2_ctrl_handler handler;
- /* Auto / manual white balance cluster */
- struct v4l2_ctrl *awb;
- struct v4l2_ctrl *gain_red;
- struct v4l2_ctrl *gain_blue;
- struct v4l2_ctrl *gain_green;
- /* Mirror cluster */
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- /* Auto exposure / manual exposure and gain cluster */
- struct v4l2_ctrl *auto_exp;
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *gain;
-};
-
-struct s5k6aa_interval {
- u16 reg_fr_time;
- struct v4l2_fract interval;
- /* Maximum rectangle for the interval */
- struct v4l2_frmsize_discrete size;
-};
-
-struct s5k6aa {
- struct v4l2_subdev sd;
- struct media_pad pad;
-
- enum v4l2_mbus_type bus_type;
- u8 mipi_lanes;
-
- int (*s_power)(int enable);
- struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
- struct s5k6aa_gpio gpio[GPIO_NUM];
-
- /* external master clock frequency */
- unsigned long mclk_frequency;
- /* ISP internal master clock frequency */
- u16 clk_fop;
- /* output pixel clock frequency range */
- u16 pclk_fmin;
- u16 pclk_fmax;
-
- unsigned int inv_hflip:1;
- unsigned int inv_vflip:1;
-
- /* protects the struct members below */
- struct mutex lock;
-
- /* sensor matrix scan window */
- struct v4l2_rect ccd_rect;
-
- struct s5k6aa_ctrls ctrls;
- struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS];
- struct s5k6aa_preset *preset;
- const struct s5k6aa_interval *fiv;
-
- unsigned int streaming:1;
- unsigned int apply_cfg:1;
- unsigned int apply_crop:1;
- unsigned int power;
-};
-
-static struct s5k6aa_regval s5k6aa_analog_config[] = {
- /* Analog settings */
- { 0x112a, 0x0000 }, { 0x1132, 0x0000 },
- { 0x113e, 0x0000 }, { 0x115c, 0x0000 },
- { 0x1164, 0x0000 }, { 0x1174, 0x0000 },
- { 0x1178, 0x0000 }, { 0x077a, 0x0000 },
- { 0x077c, 0x0000 }, { 0x077e, 0x0000 },
- { 0x0780, 0x0000 }, { 0x0782, 0x0000 },
- { 0x0784, 0x0000 }, { 0x0786, 0x0000 },
- { 0x0788, 0x0000 }, { 0x07a2, 0x0000 },
- { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 },
- { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 },
- { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 },
- { 0x07bc, 0x0004 }, { 0x07be, 0x0005 },
- { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 },
-};
-
-/* TODO: Add RGB888 and Bayer format */
-static const struct s5k6aa_pixfmt s5k6aa_formats[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 },
- /* range 16-240 */
- { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 },
- { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 },
-};
-
-static const struct s5k6aa_interval s5k6aa_intervals[] = {
- { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */
- { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */
- { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */
- { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */
- { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */
-};
-
-#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */
-
-static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl)
-{
- return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd;
-}
-
-static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct s5k6aa, sd);
-}
-
-/* Set initial values for all preview presets */
-static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa)
-{
- struct s5k6aa_preset *preset = &s5k6aa->presets[0];
- int i;
-
- for (i = 0; i < S5K6AA_MAX_PRESETS; i++) {
- preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF;
- preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF;
- preset->mbus_fmt.code = s5k6aa_formats[0].code;
- preset->index = i;
- preset->clk_id = 0;
- preset++;
- }
-
- s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX];
- s5k6aa->preset = &s5k6aa->presets[0];
-}
-
-static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val)
-{
- u8 wbuf[2] = {addr >> 8, addr & 0xFF};
- struct i2c_msg msg[2];
- u8 rbuf[2];
- int ret;
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 2;
- msg[0].buf = wbuf;
-
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 2;
- msg[1].buf = rbuf;
-
- ret = i2c_transfer(client->adapter, msg, 2);
- *val = be16_to_cpu(*((u16 *)rbuf));
-
- v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val);
-
- return ret == 2 ? 0 : ret;
-}
-
-static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val)
-{
- u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF};
-
- int ret = i2c_master_send(client, buf, 4);
- v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val);
-
- return ret == 4 ? 0 : ret;
-}
-
-/* The command register write, assumes Command_Wr_addH = 0x7000. */
-static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val)
-{
- int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr);
- if (ret)
- return ret;
- return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val);
-}
-
-/* The command register read, assumes Command_Rd_addH = 0x7000. */
-static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val)
-{
- int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr);
- if (ret)
- return ret;
- return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val);
-}
-
-static int s5k6aa_write_array(struct v4l2_subdev *sd,
- const struct s5k6aa_regval *msg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u16 addr_incr = 0;
- int ret = 0;
-
- while (msg->addr != S5K6AA_TERM) {
- if (addr_incr != 2)
- ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL,
- msg->addr);
- if (ret)
- break;
- ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val);
- if (ret)
- break;
- /* Assume that msg->addr is always less than 0xfffc */
- addr_incr = (msg + 1)->addr - msg->addr;
- msg++;
- }
-
- return ret;
-}
-
-/* Configure the AHB high address bytes for GTG registers access */
-static int s5k6aa_set_ahb_address(struct i2c_client *client)
-{
- int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH);
- if (ret)
- return ret;
- ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH);
- if (ret)
- return ret;
- return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH);
-}
-
-/**
- * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration
- *
- * Configure the internal ISP PLL for the required output frequency.
- * Locking: called with s5k6aa.lock mutex held.
- */
-static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa)
-{
- struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
- unsigned long fmclk = s5k6aa->mclk_frequency / 1000;
- u16 status;
- int ret;
-
- if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ,
- "Invalid clock frequency: %ld\n", fmclk))
- return -EINVAL;
-
- s5k6aa->pclk_fmin = PCLK_FREQ_MIN;
- s5k6aa->pclk_fmax = PCLK_FREQ_MAX;
- s5k6aa->clk_fop = SYS_PLL_OUT_FREQ;
-
- /* External input clock frequency in kHz */
- ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16);
- if (!ret)
- ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF);
- if (!ret)
- ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1);
- /* Internal PLL frequency */
- if (!ret)
- ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop);
- if (!ret)
- ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0),
- s5k6aa->pclk_fmin);
- if (!ret)
- ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0),
- s5k6aa->pclk_fmax);
- if (!ret)
- ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1);
- if (!ret)
- ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status);
-
- return ret ? ret : (status ? -EINVAL : 0);
-}
-
-/* Set horizontal and vertical image flipping */
-static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- int index = s5k6aa->preset->index;
-
- unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip;
- unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1);
-
- return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip);
-}
-
-/* Configure auto/manual white balance and R/G/B gains */
-static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb)
-{
- struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
- struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
- u16 reg;
-
- int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &reg);
-
- if (!ret && !awb) {
- ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val);
- if (!ret)
- ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1);
- if (ret)
- return ret;
-
- ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val);
- if (!ret)
- ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1);
- if (ret)
- return ret;
-
- ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val);
- if (!ret)
- ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1);
- }
- if (!ret) {
- reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK;
- ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg);
- }
-
- return ret;
-}
-
-/* Program FW with exposure time, 'exposure' in us units */
-static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure)
-{
- unsigned int time = exposure / 10;
-
- int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff);
- if (!ret)
- ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16);
- if (ret)
- return ret;
- return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1);
-}
-
-static int s5k6aa_set_user_gain(struct i2c_client *client, int gain)
-{
- int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain);
- if (ret)
- return ret;
- return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1);
-}
-
-/* Set auto/manual exposure and total gain */
-static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value)
-{
- struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
- unsigned int exp_time = s5k6aa->ctrls.exposure->val;
- u16 auto_alg;
-
- int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg);
- if (ret)
- return ret;
-
- v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n",
- exp_time, value, auto_alg);
-
- if (value == V4L2_EXPOSURE_AUTO) {
- auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK;
- } else {
- ret = s5k6aa_set_user_exposure(c, exp_time);
- if (ret)
- return ret;
- ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val);
- if (ret)
- return ret;
- auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK);
- }
-
- return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg);
-}
-
-static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- u16 auto_alg;
- int ret;
-
- ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg);
- if (ret)
- return ret;
-
- if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) {
- auto_alg |= AALG_FLICKER_EN_MASK;
- } else {
- auto_alg &= ~AALG_FLICKER_EN_MASK;
- /* The V4L2_CID_LINE_FREQUENCY control values match
- * the register values */
- ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value);
- if (ret)
- return ret;
- ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1);
- if (ret)
- return ret;
- }
-
- return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg);
-}
-
-static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- static const struct v4l2_control colorfx[] = {
- { V4L2_COLORFX_NONE, 0 },
- { V4L2_COLORFX_BW, 1 },
- { V4L2_COLORFX_NEGATIVE, 2 },
- { V4L2_COLORFX_SEPIA, 3 },
- { V4L2_COLORFX_SKY_BLUE, 4 },
- { V4L2_COLORFX_SKETCH, 5 },
- };
- int i;
-
- for (i = 0; i < ARRAY_SIZE(colorfx); i++) {
- if (colorfx[i].id == val)
- return s5k6aa_write(client, REG_G_SPEC_EFFECTS,
- colorfx[i].value);
- }
- return -EINVAL;
-}
-
-static int s5k6aa_preview_config_status(struct i2c_client *client)
-{
- u16 error = 0;
- int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error);
-
- v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret);
- return ret ? ret : (error ? -EINVAL : 0);
-}
-
-static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa,
- struct v4l2_mbus_framefmt *mf)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++)
- if (mf->colorspace == s5k6aa_formats[i].colorspace &&
- mf->code == s5k6aa_formats[i].code)
- return i;
- return 0;
-}
-
-static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa,
- struct s5k6aa_preset *preset)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt);
- int ret;
-
- ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index),
- preset->mbus_fmt.width);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index),
- preset->mbus_fmt.height);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_FMT(preset->index),
- s5k6aa_formats[fmt_index].reg_p_fmt);
- return ret;
-}
-
-static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa)
-{
- struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd);
- struct v4l2_rect *r = &s5k6aa->ccd_rect;
- int ret;
-
- ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width);
- if (!ret)
- ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height);
- if (!ret)
- ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left);
- if (!ret)
- ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top);
- if (!ret)
- ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1);
- if (!ret)
- s5k6aa->apply_crop = 0;
-
- return ret;
-}
-
-/**
- * s5k6aa_configure_video_bus - configure the video output interface
- * @bus_type: video bus type: parallel or MIPI-CSI
- * @nlanes: number of MIPI lanes to be used (MIPI-CSI only)
- *
- * Note: Only parallel bus operation has been tested.
- */
-static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa,
- enum v4l2_mbus_type bus_type, int nlanes)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- u16 cfg = 0;
- int ret;
-
- /*
- * TODO: The sensor is supposed to support BT.601 and BT.656
- * but there is nothing indicating how to switch between both
- * in the datasheet. For now default BT.601 interface is assumed.
- */
- if (bus_type == V4L2_MBUS_CSI2)
- cfg = nlanes;
- else if (bus_type != V4L2_MBUS_PARALLEL)
- return -EINVAL;
-
- ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg);
- if (ret)
- return ret;
- return s5k6aa_write(client, REG_OIF_CFG_CHG, 1);
-}
-
-/* This function should be called when switching to new user configuration set*/
-static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout,
- int cid)
-{
- unsigned long end = jiffies + msecs_to_jiffies(timeout);
- u16 reg = 1;
- int ret;
-
- ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid);
- if (!ret)
- ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
- if (!ret)
- ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1);
- if (timeout == 0)
- return ret;
-
- while (ret >= 0 && time_is_after_jiffies(end)) {
- ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, &reg);
- if (!reg)
- return 0;
- usleep_range(1000, 5000);
- }
- return ret ? ret : -ETIMEDOUT;
-}
-
-/**
- * s5k6aa_set_prev_config - write user preview register set
- *
- * Configure output resolution and color fromat, pixel clock
- * frequency range, device frame rate type and frame period range.
- */
-static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa,
- struct s5k6aa_preset *preset)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- int idx = preset->index;
- u16 frame_rate_q;
- int ret;
-
- if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME)
- frame_rate_q = FR_RATE_Q_BEST_FRRATE;
- else
- frame_rate_q = FR_RATE_Q_BEST_QUALITY;
-
- ret = s5k6aa_set_output_framefmt(s5k6aa, preset);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx),
- s5k6aa->pclk_fmax);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx),
- s5k6aa->pclk_fmin);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx),
- preset->clk_id);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx),
- FR_RATE_DYNAMIC);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx),
- frame_rate_q);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx),
- s5k6aa->fiv->reg_fr_time + 33);
- if (!ret)
- ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx),
- s5k6aa->fiv->reg_fr_time - 33);
- if (!ret)
- ret = s5k6aa_new_config_sync(client, 250, idx);
- if (!ret)
- ret = s5k6aa_preview_config_status(client);
- if (!ret)
- s5k6aa->apply_cfg = 0;
-
- v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n",
- s5k6aa->fiv->reg_fr_time, ret);
- return ret;
-}
-
-/**
- * s5k6aa_initialize_isp - basic ISP MCU initialization
- *
- * Configure AHB addresses for registers read/write; configure PLLs for
- * required output pixel clock. The ISP power supply needs to be already
- * enabled, with an optional H/W reset.
- * Locking: called with s5k6aa.lock mutex held.
- */
-static int s5k6aa_initialize_isp(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int ret;
-
- s5k6aa->apply_crop = 1;
- s5k6aa->apply_cfg = 1;
- msleep(100);
-
- ret = s5k6aa_set_ahb_address(client);
- if (ret)
- return ret;
- ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type,
- s5k6aa->mipi_lanes);
- if (ret)
- return ret;
- ret = s5k6aa_write_array(sd, s5k6aa_analog_config);
- if (ret)
- return ret;
- msleep(20);
-
- return s5k6aa_configure_pixel_clocks(s5k6aa);
-}
-
-static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val)
-{
- if (!gpio_is_valid(priv->gpio[id].gpio))
- return 0;
- gpio_set_value(priv->gpio[id].gpio, !!val);
- return 1;
-}
-
-static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id)
-{
- return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level);
-}
-
-static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id)
-{
- return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level);
-}
-
-static int __s5k6aa_power_on(struct s5k6aa *s5k6aa)
-{
- int ret;
-
- ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
- if (ret)
- return ret;
- if (s5k6aa_gpio_deassert(s5k6aa, STBY))
- usleep_range(150, 200);
-
- if (s5k6aa->s_power)
- ret = s5k6aa->s_power(1);
- usleep_range(4000, 4000);
-
- if (s5k6aa_gpio_deassert(s5k6aa, RST))
- msleep(20);
-
- return ret;
-}
-
-static int __s5k6aa_power_off(struct s5k6aa *s5k6aa)
-{
- int ret;
-
- if (s5k6aa_gpio_assert(s5k6aa, RST))
- usleep_range(100, 150);
-
- if (s5k6aa->s_power) {
- ret = s5k6aa->s_power(0);
- if (ret)
- return ret;
- }
- if (s5k6aa_gpio_assert(s5k6aa, STBY))
- usleep_range(50, 100);
- s5k6aa->streaming = 0;
-
- return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
-}
-
-/*
- * V4L2 subdev core and video operations
- */
-static int s5k6aa_set_power(struct v4l2_subdev *sd, int on)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int ret = 0;
-
- mutex_lock(&s5k6aa->lock);
-
- if (!on == s5k6aa->power) {
- if (on) {
- ret = __s5k6aa_power_on(s5k6aa);
- if (!ret)
- ret = s5k6aa_initialize_isp(sd);
- } else {
- ret = __s5k6aa_power_off(s5k6aa);
- }
-
- if (!ret)
- s5k6aa->power += on ? 1 : -1;
- }
-
- mutex_unlock(&s5k6aa->lock);
-
- if (!on || ret || s5k6aa->power != 1)
- return ret;
-
- return v4l2_ctrl_handler_setup(sd->ctrl_handler);
-}
-
-static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- int ret = 0;
-
- ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable);
- if (!ret)
- ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1);
- if (!ret)
- s5k6aa->streaming = enable;
-
- return ret;
-}
-
-static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int ret = 0;
-
- mutex_lock(&s5k6aa->lock);
-
- if (s5k6aa->streaming == !on) {
- if (!ret && s5k6aa->apply_cfg)
- ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset);
- if (s5k6aa->apply_crop)
- ret = s5k6aa_set_input_params(s5k6aa);
- if (!ret)
- ret = __s5k6aa_stream(s5k6aa, !!on);
- }
- mutex_unlock(&s5k6aa->lock);
-
- return ret;
-}
-
-static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
-
- mutex_lock(&s5k6aa->lock);
- fi->interval = s5k6aa->fiv->interval;
- mutex_unlock(&s5k6aa->lock);
-
- return 0;
-}
-
-static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa,
- struct v4l2_subdev_frame_interval *fi)
-{
- struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt;
- const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0];
- unsigned int err, min_err = UINT_MAX;
- unsigned int i, fr_time;
-
- if (fi->interval.denominator == 0)
- return -EINVAL;
-
- fr_time = fi->interval.numerator * 10000 / fi->interval.denominator;
-
- for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) {
- const struct s5k6aa_interval *iv = &s5k6aa_intervals[i];
-
- if (mbus_fmt->width > iv->size.width ||
- mbus_fmt->height > iv->size.height)
- continue;
-
- err = abs(iv->reg_fr_time - fr_time);
- if (err < min_err) {
- fiv = iv;
- min_err = err;
- }
- }
- s5k6aa->fiv = fiv;
-
- v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n",
- fiv->reg_fr_time * 100);
- return 0;
-}
-
-static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *fi)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int ret;
-
- v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n",
- fi->interval.numerator, fi->interval.denominator);
-
- mutex_lock(&s5k6aa->lock);
- ret = __s5k6aa_set_frame_interval(s5k6aa, fi);
- s5k6aa->apply_cfg = 1;
-
- mutex_unlock(&s5k6aa->lock);
- return ret;
-}
-
-/*
- * V4L2 subdev pad level and video operations
- */
-static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_interval_enum *fie)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- const struct s5k6aa_interval *fi;
- int ret = 0;
-
- if (fie->index > ARRAY_SIZE(s5k6aa_intervals))
- return -EINVAL;
-
- v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN,
- S5K6AA_WIN_WIDTH_MAX, 1,
- &fie->height, S5K6AA_WIN_HEIGHT_MIN,
- S5K6AA_WIN_HEIGHT_MAX, 1, 0);
-
- mutex_lock(&s5k6aa->lock);
- fi = &s5k6aa_intervals[fie->index];
- if (fie->width > fi->size.width || fie->height > fi->size.height)
- ret = -EINVAL;
- else
- fie->interval = fi->interval;
- mutex_unlock(&s5k6aa->lock);
-
- return ret;
-}
-
-static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index >= ARRAY_SIZE(s5k6aa_formats))
- return -EINVAL;
-
- code->code = s5k6aa_formats[code->index].code;
- return 0;
-}
-
-static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- int i = ARRAY_SIZE(s5k6aa_formats);
-
- if (fse->index > 0)
- return -EINVAL;
-
- while (--i)
- if (fse->code == s5k6aa_formats[i].code)
- break;
-
- fse->code = s5k6aa_formats[i].code;
- fse->min_width = S5K6AA_WIN_WIDTH_MIN;
- fse->max_width = S5K6AA_WIN_WIDTH_MAX;
- fse->max_height = S5K6AA_WIN_HEIGHT_MIN;
- fse->min_height = S5K6AA_WIN_HEIGHT_MAX;
-
- return 0;
-}
-
-static struct v4l2_rect *
-__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh,
- enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return &s5k6aa->ccd_rect;
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_get_try_crop(fh, 0);
-
- return NULL;
-}
-
-static void s5k6aa_try_format(struct s5k6aa *s5k6aa,
- struct v4l2_mbus_framefmt *mf)
-{
- unsigned int index;
-
- v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN,
- S5K6AA_WIN_WIDTH_MAX, 1,
- &mf->height, S5K6AA_WIN_HEIGHT_MIN,
- S5K6AA_WIN_HEIGHT_MAX, 1, 0);
-
- if (mf->colorspace != V4L2_COLORSPACE_JPEG &&
- mf->colorspace != V4L2_COLORSPACE_REC709)
- mf->colorspace = V4L2_COLORSPACE_JPEG;
-
- index = s5k6aa_get_pixfmt_index(s5k6aa, mf);
-
- mf->colorspace = s5k6aa_formats[index].colorspace;
- mf->code = s5k6aa_formats[index].code;
- mf->field = V4L2_FIELD_NONE;
-}
-
-static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- struct v4l2_mbus_framefmt *mf;
-
- memset(fmt->reserved, 0, sizeof(fmt->reserved));
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, 0);
- fmt->format = *mf;
- return 0;
- }
-
- mutex_lock(&s5k6aa->lock);
- fmt->format = s5k6aa->preset->mbus_fmt;
- mutex_unlock(&s5k6aa->lock);
-
- return 0;
-}
-
-static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- struct s5k6aa_preset *preset = s5k6aa->preset;
- struct v4l2_mbus_framefmt *mf;
- struct v4l2_rect *crop;
- int ret = 0;
-
- mutex_lock(&s5k6aa->lock);
- s5k6aa_try_format(s5k6aa, &fmt->format);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- crop = v4l2_subdev_get_try_crop(fh, 0);
- } else {
- if (s5k6aa->streaming) {
- ret = -EBUSY;
- } else {
- mf = &preset->mbus_fmt;
- crop = &s5k6aa->ccd_rect;
- s5k6aa->apply_cfg = 1;
- }
- }
-
- if (ret == 0) {
- struct v4l2_subdev_frame_interval fiv = {
- .interval = {0, 1}
- };
-
- *mf = fmt->format;
- /*
- * Make sure the crop window is valid, i.e. its size is
- * greater than the output window, as the ISP supports
- * only down-scaling.
- */
- crop->width = clamp_t(unsigned int, crop->width, mf->width,
- S5K6AA_WIN_WIDTH_MAX);
- crop->height = clamp_t(unsigned int, crop->height, mf->height,
- S5K6AA_WIN_HEIGHT_MAX);
- crop->left = clamp_t(unsigned int, crop->left, 0,
- S5K6AA_WIN_WIDTH_MAX - crop->width);
- crop->top = clamp_t(unsigned int, crop->top, 0,
- S5K6AA_WIN_HEIGHT_MAX - crop->height);
-
- /* Reset to minimum possible frame interval */
- ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv);
- }
- mutex_unlock(&s5k6aa->lock);
-
- return ret;
-}
-
-static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- struct v4l2_rect *rect;
-
- memset(crop->reserved, 0, sizeof(crop->reserved));
- mutex_lock(&s5k6aa->lock);
-
- rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
- if (rect)
- crop->rect = *rect;
-
- mutex_unlock(&s5k6aa->lock);
-
- v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n",
- rect->left, rect->top, rect->width, rect->height);
-
- return 0;
-}
-
-static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- struct v4l2_mbus_framefmt *mf;
- unsigned int max_x, max_y;
- struct v4l2_rect *crop_r;
-
- mutex_lock(&s5k6aa->lock);
- crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which);
-
- if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- mf = &s5k6aa->preset->mbus_fmt;
- s5k6aa->apply_crop = 1;
- } else {
- mf = v4l2_subdev_get_try_format(fh, 0);
- }
- v4l_bound_align_image(&crop->rect.width, mf->width,
- S5K6AA_WIN_WIDTH_MAX, 1,
- &crop->rect.height, mf->height,
- S5K6AA_WIN_HEIGHT_MAX, 1, 0);
-
- max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1;
- max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1;
-
- crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x);
- crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y);
-
- *crop_r = crop->rect;
-
- mutex_unlock(&s5k6aa->lock);
-
- v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n",
- crop_r->left, crop_r->top, crop_r->width, crop_r->height);
-
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = {
- .enum_mbus_code = s5k6aa_enum_mbus_code,
- .enum_frame_size = s5k6aa_enum_frame_size,
- .enum_frame_interval = s5k6aa_enum_frame_interval,
- .get_fmt = s5k6aa_get_fmt,
- .set_fmt = s5k6aa_set_fmt,
- .get_crop = s5k6aa_get_crop,
- .set_crop = s5k6aa_set_crop,
-};
-
-static const struct v4l2_subdev_video_ops s5k6aa_video_ops = {
- .g_frame_interval = s5k6aa_g_frame_interval,
- .s_frame_interval = s5k6aa_s_frame_interval,
- .s_stream = s5k6aa_s_stream,
-};
-
-/*
- * V4L2 subdev controls
- */
-
-static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int idx, err = 0;
-
- v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val);
-
- mutex_lock(&s5k6aa->lock);
- /*
- * If the device is not powered up by the host driver do
- * not apply any controls to H/W at this time. Instead
- * the controls will be restored right after power-up.
- */
- if (s5k6aa->power == 0)
- goto unlock;
- idx = s5k6aa->preset->index;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTO_WHITE_BALANCE:
- err = s5k6aa_set_awb(s5k6aa, ctrl->val);
- break;
-
- case V4L2_CID_BRIGHTNESS:
- err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val);
- break;
-
- case V4L2_CID_COLORFX:
- err = s5k6aa_set_colorfx(s5k6aa, ctrl->val);
- break;
-
- case V4L2_CID_CONTRAST:
- err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val);
- break;
-
- case V4L2_CID_EXPOSURE_AUTO:
- err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val);
- break;
-
- case V4L2_CID_HFLIP:
- err = s5k6aa_set_mirror(s5k6aa, ctrl->val);
- if (err)
- break;
- err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
- break;
-
- case V4L2_CID_POWER_LINE_FREQUENCY:
- err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val);
- break;
-
- case V4L2_CID_SATURATION:
- err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val);
- break;
-
- case V4L2_CID_SHARPNESS:
- err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val);
- break;
-
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
- err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val);
- if (err)
- break;
- err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1);
- break;
- }
-unlock:
- mutex_unlock(&s5k6aa->lock);
- return err;
-}
-
-static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = {
- .s_ctrl = s5k6aa_s_ctrl,
-};
-
-static int s5k6aa_log_status(struct v4l2_subdev *sd)
-{
- v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name);
- return 0;
-}
-
-#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001)
-#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002)
-#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003)
-
-static const struct v4l2_ctrl_config s5k6aa_ctrls[] = {
- {
- .ops = &s5k6aa_ctrl_ops,
- .id = V4L2_CID_RED_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain, Red",
- .min = 0,
- .max = 256,
- .def = 127,
- .step = 1,
- }, {
- .ops = &s5k6aa_ctrl_ops,
- .id = V4L2_CID_GREEN_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain, Green",
- .min = 0,
- .max = 256,
- .def = 127,
- .step = 1,
- }, {
- .ops = &s5k6aa_ctrl_ops,
- .id = V4L2_CID_BLUE_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain, Blue",
- .min = 0,
- .max = 256,
- .def = 127,
- .step = 1,
- },
-};
-
-static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa)
-{
- const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops;
- struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls;
- struct v4l2_ctrl_handler *hdl = &ctrls->handler;
-
- int ret = v4l2_ctrl_handler_init(hdl, 16);
- if (ret)
- return ret;
- /* Auto white balance cluster */
- ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE,
- 0, 1, 1, 1);
- ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL);
- ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL);
- ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL);
- v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false);
-
- ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
- ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
- v4l2_ctrl_cluster(2, &ctrls->hflip);
-
- ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops,
- V4L2_CID_EXPOSURE_AUTO,
- V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO);
- /* Exposure time: x 1 us */
- ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
- 0, 6000000U, 1, 100000U);
- /* Total gain: 256 <=> 1x */
- ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN,
- 0, 256, 1, 256);
- v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false);
-
- v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY,
- V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
- V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
-
- v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX,
- V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE);
-
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE,
- 0, 256, 1, 0);
-
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0);
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0);
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0);
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0);
-
- if (hdl->error) {
- ret = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- return ret;
- }
-
- s5k6aa->sd.ctrl_handler = hdl;
- return 0;
-}
-
-/*
- * V4L2 subdev internal operations
- */
-static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
- struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0);
-
- format->colorspace = s5k6aa_formats[0].colorspace;
- format->code = s5k6aa_formats[0].code;
- format->width = S5K6AA_OUT_WIDTH_DEF;
- format->height = S5K6AA_OUT_HEIGHT_DEF;
- format->field = V4L2_FIELD_NONE;
-
- crop->width = S5K6AA_WIN_WIDTH_MAX;
- crop->height = S5K6AA_WIN_HEIGHT_MAX;
- crop->left = 0;
- crop->top = 0;
-
- return 0;
-}
-
-int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd);
- u16 api_ver = 0, fw_rev = 0;
-
- int ret = s5k6aa_set_ahb_address(client);
-
- if (!ret)
- ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver);
- if (!ret)
- ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev);
- if (ret) {
- v4l2_err(&s5k6aa->sd, "FW revision check failed!\n");
- return ret;
- }
-
- v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n",
- api_ver, fw_rev);
-
- return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV;
-}
-
-static int s5k6aa_registered(struct v4l2_subdev *sd)
-{
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
- int ret;
-
- mutex_lock(&s5k6aa->lock);
- ret = __s5k6aa_power_on(s5k6aa);
- if (!ret) {
- msleep(100);
- ret = s5k6aa_check_fw_revision(s5k6aa);
- __s5k6aa_power_off(s5k6aa);
- }
- mutex_unlock(&s5k6aa->lock);
-
- return ret;
-}
-
-static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = {
- .registered = s5k6aa_registered,
- .open = s5k6aa_open,
-};
-
-static const struct v4l2_subdev_core_ops s5k6aa_core_ops = {
- .s_power = s5k6aa_set_power,
- .log_status = s5k6aa_log_status,
-};
-
-static const struct v4l2_subdev_ops s5k6aa_subdev_ops = {
- .core = &s5k6aa_core_ops,
- .pad = &s5k6aa_pad_ops,
- .video = &s5k6aa_video_ops,
-};
-
-/*
- * GPIO setup
- */
-static int s5k6aa_configure_gpio(int nr, int val, const char *name)
-{
- unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
- int ret;
-
- if (!gpio_is_valid(nr))
- return 0;
- ret = gpio_request_one(nr, flags, name);
- if (!ret)
- gpio_export(nr, 0);
- return ret;
-}
-
-static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) {
- if (!gpio_is_valid(s5k6aa->gpio[i].gpio))
- continue;
- gpio_free(s5k6aa->gpio[i].gpio);
- s5k6aa->gpio[i].gpio = -EINVAL;
- }
-}
-
-static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
- const struct s5k6aa_platform_data *pdata)
-{
- const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
- int ret;
-
- s5k6aa->gpio[STBY].gpio = -EINVAL;
- s5k6aa->gpio[RST].gpio = -EINVAL;
-
- ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
- if (ret) {
- s5k6aa_free_gpios(s5k6aa);
- return ret;
- }
- s5k6aa->gpio[STBY] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
-
- gpio = &pdata->gpio_reset;
- ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
- if (ret) {
- s5k6aa_free_gpios(s5k6aa);
- return ret;
- }
- s5k6aa->gpio[RST] = *gpio;
- if (gpio_is_valid(gpio->gpio))
- gpio_set_value(gpio->gpio, 0);
-
- return 0;
-}
-
-static int s5k6aa_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
- struct v4l2_subdev *sd;
- struct s5k6aa *s5k6aa;
- int i, ret;
-
- if (pdata == NULL) {
- dev_err(&client->dev, "Platform data not specified\n");
- return -EINVAL;
- }
-
- if (pdata->mclk_frequency == 0) {
- dev_err(&client->dev, "MCLK frequency not specified\n");
- return -EINVAL;
- }
-
- s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
- if (!s5k6aa)
- return -ENOMEM;
-
- mutex_init(&s5k6aa->lock);
-
- s5k6aa->mclk_frequency = pdata->mclk_frequency;
- s5k6aa->bus_type = pdata->bus_type;
- s5k6aa->mipi_lanes = pdata->nlanes;
- s5k6aa->s_power = pdata->set_power;
- s5k6aa->inv_hflip = pdata->horiz_flip;
- s5k6aa->inv_vflip = pdata->vert_flip;
-
- sd = &s5k6aa->sd;
- v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
- strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
-
- sd->internal_ops = &s5k6aa_subdev_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
- ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
- if (ret)
- goto out_err1;
-
- ret = s5k6aa_configure_gpios(s5k6aa, pdata);
- if (ret)
- goto out_err2;
-
- for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++)
- s5k6aa->supplies[i].supply = s5k6aa_supply_names[i];
-
- ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES,
- s5k6aa->supplies);
- if (ret) {
- dev_err(&client->dev, "Failed to get regulators\n");
- goto out_err3;
- }
-
- ret = s5k6aa_initialize_ctrls(s5k6aa);
- if (ret)
- goto out_err4;
-
- s5k6aa_presets_data_init(s5k6aa);
-
- s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX;
- s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX;
- s5k6aa->ccd_rect.left = 0;
- s5k6aa->ccd_rect.top = 0;
-
- return 0;
-
-out_err4:
- regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
-out_err3:
- s5k6aa_free_gpios(s5k6aa);
-out_err2:
- media_entity_cleanup(&s5k6aa->sd.entity);
-out_err1:
- kfree(s5k6aa);
- return ret;
-}
-
-static int s5k6aa_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct s5k6aa *s5k6aa = to_s5k6aa(sd);
-
- v4l2_device_unregister_subdev(sd);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
- media_entity_cleanup(&sd->entity);
- regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
- s5k6aa_free_gpios(s5k6aa);
- kfree(s5k6aa);
-
- return 0;
-}
-
-static const struct i2c_device_id s5k6aa_id[] = {
- { DRIVER_NAME, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
-
-
-static struct i2c_driver s5k6aa_i2c_driver = {
- .driver = {
- .name = DRIVER_NAME
- },
- .probe = s5k6aa_probe,
- .remove = s5k6aa_remove,
- .id_table = s5k6aa_id,
-};
-
-module_i2c_driver(s5k6aa_i2c_driver);
-
-MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
deleted file mode 100644
index 40923885977a..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ /dev/null
@@ -1,1731 +0,0 @@
-/*
- * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver
- *
- * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/bug.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/pm_runtime.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "fimc-mdevice.h"
-#include "fimc-core.h"
-#include "fimc-reg.h"
-
-static int fimc_capture_hw_init(struct fimc_dev *fimc)
-{
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_pipeline *p = &fimc->pipeline;
- struct fimc_sensor_info *sensor;
- unsigned long flags;
- int ret = 0;
-
- if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL)
- return -ENXIO;
- if (ctx->s_frame.fmt == NULL)
- return -EINVAL;
-
- sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]);
-
- spin_lock_irqsave(&fimc->slock, flags);
- fimc_prepare_dma_offset(ctx, &ctx->d_frame);
- fimc_set_yuv_order(ctx);
-
- fimc_hw_set_camera_polarity(fimc, sensor->pdata);
- fimc_hw_set_camera_type(fimc, sensor->pdata);
- fimc_hw_set_camera_source(fimc, sensor->pdata);
- fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
-
- ret = fimc_set_scaler_info(ctx);
- if (!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);
- if (fimc->variant->has_alpha)
- fimc_hw_set_rgb_alpha(ctx);
- clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- }
- spin_unlock_irqrestore(&fimc->slock, flags);
- return ret;
-}
-
-/*
- * Reinitialize the driver so it is ready to start the streaming again.
- * Set fimc->state to indicate stream off and the hardware shut down state.
- * If not suspending (@suspend is false), return any buffers to videobuf2.
- * Otherwise put any owned buffers onto the pending buffers queue, so they
- * can be re-spun when the device is being resumed. Also perform FIMC
- * software reset and disable streaming on the whole pipeline if required.
- */
-static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
-{
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- struct fimc_vid_buffer *buf;
- unsigned long flags;
- bool streaming;
-
- spin_lock_irqsave(&fimc->slock, flags);
- streaming = fimc->state & (1 << ST_CAPT_ISP_STREAM);
-
- fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
- 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
- if (suspend)
- fimc->state |= (1 << ST_CAPT_SUSPENDED);
- else
- fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
-
- /* Release unused buffers */
- while (!suspend && !list_empty(&cap->pending_buf_q)) {
- buf = fimc_pending_queue_pop(cap);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
- /* If suspending put unused buffers onto pending queue */
- while (!list_empty(&cap->active_buf_q)) {
- buf = fimc_active_queue_pop(cap);
- if (suspend)
- fimc_pending_queue_add(cap, buf);
- else
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
-
- fimc_hw_reset(fimc);
- cap->buf_index = 0;
-
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- if (streaming)
- return fimc_pipeline_s_stream(&fimc->pipeline, 0);
- else
- return 0;
-}
-
-static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
-{
- unsigned long flags;
-
- if (!fimc_capture_active(fimc))
- return 0;
-
- spin_lock_irqsave(&fimc->slock, flags);
- set_bit(ST_CAPT_SHUT, &fimc->state);
- fimc_deactivate_capture(fimc);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- wait_event_timeout(fimc->irq_queue,
- !test_bit(ST_CAPT_SHUT, &fimc->state),
- (2*HZ/10)); /* 200 ms */
-
- return fimc_capture_state_cleanup(fimc, suspend);
-}
-
-/**
- * fimc_capture_config_update - apply the camera interface configuration
- *
- * To be called from within the interrupt handler with fimc.slock
- * spinlock held. It updates the camera pixel crop, rotation and
- * image flip in H/W.
- */
-static int fimc_capture_config_update(struct fimc_ctx *ctx)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
-
- ret = fimc_set_scaler_info(ctx);
- if (ret)
- return ret;
-
- 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_prepare_dma_offset(ctx, &ctx->d_frame);
- fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
- fimc_hw_set_rgb_alpha(ctx);
-
- clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- return ret;
-}
-
-void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
-{
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- struct fimc_vid_buffer *v_buf;
- struct timeval *tv;
- struct timespec ts;
-
- if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- goto done;
- }
-
- if (!list_empty(&cap->active_buf_q) &&
- test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) {
- ktime_get_real_ts(&ts);
-
- v_buf = fimc_active_queue_pop(cap);
-
- tv = &v_buf->vb.v4l2_buf.timestamp;
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
- v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
-
- vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
- }
-
- if (!list_empty(&cap->pending_buf_q)) {
-
- v_buf = fimc_pending_queue_pop(cap);
- fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
- v_buf->index = cap->buf_index;
-
- /* Move the buffer to the capture active queue */
- fimc_active_queue_add(cap, v_buf);
-
- dbg("next frame: %d, done frame: %d",
- fimc_hw_get_frame_index(fimc), v_buf->index);
-
- if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
- cap->buf_index = 0;
- }
-
- if (cap->active_buf_cnt == 0) {
- if (deq_buf)
- clear_bit(ST_CAPT_RUN, &fimc->state);
-
- if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
- cap->buf_index = 0;
- } else {
- set_bit(ST_CAPT_RUN, &fimc->state);
- }
-
- if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
- fimc_capture_config_update(cap->ctx);
-done:
- if (cap->active_buf_cnt == 1) {
- fimc_deactivate_capture(fimc);
- clear_bit(ST_CAPT_STREAM, &fimc->state);
- }
-
- dbg("frame: %d, active_buf_cnt: %d",
- fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
-}
-
-
-static int start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct fimc_ctx *ctx = q->drv_priv;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- int min_bufs;
- int ret;
-
- vid_cap->frame_count = 0;
-
- ret = fimc_capture_hw_init(fimc);
- if (ret) {
- fimc_capture_state_cleanup(fimc, false);
- return ret;
- }
-
- set_bit(ST_CAPT_PEND, &fimc->state);
-
- min_bufs = fimc->vid_cap.reqbufs_count > 1 ? 2 : 1;
-
- if (vid_cap->active_buf_cnt >= min_bufs &&
- !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
- fimc_activate_capture(ctx);
-
- if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_s_stream(&fimc->pipeline, 1);
- }
-
- 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_capture_active(fimc))
- return -EINVAL;
-
- return fimc_stop_capture(fimc, false);
-}
-
-int fimc_capture_suspend(struct fimc_dev *fimc)
-{
- bool suspend = fimc_capture_busy(fimc);
-
- int ret = fimc_stop_capture(fimc, suspend);
- if (ret)
- return ret;
- return fimc_pipeline_shutdown(&fimc->pipeline);
-}
-
-static void buffer_queue(struct vb2_buffer *vb);
-
-int fimc_capture_resume(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct fimc_vid_buffer *buf;
- int i;
-
- if (!test_and_clear_bit(ST_CAPT_SUSPENDED, &fimc->state))
- return 0;
-
- INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
- vid_cap->buf_index = 0;
- fimc_pipeline_initialize(&fimc->pipeline, &vid_cap->vfd.entity,
- false);
- fimc_capture_hw_init(fimc);
-
- clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
-
- for (i = 0; i < vid_cap->reqbufs_count; i++) {
- if (list_empty(&vid_cap->pending_buf_q))
- break;
- buf = fimc_pending_queue_pop(vid_cap);
- buffer_queue(&buf->vb);
- }
- return 0;
-
-}
-
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
- unsigned int *num_buffers, unsigned int *num_planes,
- unsigned int sizes[], void *allocators[])
-{
- const struct v4l2_pix_format_mplane *pixm = NULL;
- struct fimc_ctx *ctx = vq->drv_priv;
- struct fimc_frame *frame = &ctx->d_frame;
- struct fimc_fmt *fmt = frame->fmt;
- unsigned long wh;
- int i;
-
- if (pfmt) {
- pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_find_format(&pixm->pixelformat, NULL,
- FMT_FLAGS_CAM | FMT_FLAGS_M2M, -1);
- wh = pixm->width * pixm->height;
- } else {
- wh = frame->f_width * frame->f_height;
- }
-
- if (fmt == NULL)
- return -EINVAL;
-
- *num_planes = fmt->memplanes;
-
- for (i = 0; i < fmt->memplanes; i++) {
- unsigned int size = (wh * fmt->depth[i]) / 8;
- if (pixm)
- sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
- else
- sizes[i] = max_t(u32, size, frame->payload[i]);
-
- allocators[i] = ctx->fimc_dev->alloc_ctx;
- }
-
- return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct fimc_ctx *ctx = vq->drv_priv;
- int i;
-
- if (ctx->d_frame.fmt == NULL)
- return -EINVAL;
-
- for (i = 0; i < ctx->d_frame.fmt->memplanes; i++) {
- unsigned long size = ctx->d_frame.payload[i];
-
- if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&ctx->fimc_dev->vid_cap.vfd,
- "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_vid_buffer *buf
- = container_of(vb, struct fimc_vid_buffer, vb);
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- unsigned long flags;
- int min_bufs;
-
- spin_lock_irqsave(&fimc->slock, flags);
- fimc_prepare_addr(ctx, &buf->vb, &ctx->d_frame, &buf->paddr);
-
- if (!test_bit(ST_CAPT_SUSPENDED, &fimc->state) &&
- !test_bit(ST_CAPT_STREAM, &fimc->state) &&
- vid_cap->active_buf_cnt < FIMC_MAX_OUT_BUFS) {
- /* Setup the buffer directly for processing. */
- int buf_id = (vid_cap->reqbufs_count == 1) ? -1 :
- vid_cap->buf_index;
-
- fimc_hw_set_output_addr(fimc, &buf->paddr, buf_id);
- buf->index = vid_cap->buf_index;
- fimc_active_queue_add(vid_cap, buf);
-
- if (++vid_cap->buf_index >= FIMC_MAX_OUT_BUFS)
- vid_cap->buf_index = 0;
- } else {
- fimc_pending_queue_add(vid_cap, buf);
- }
-
- min_bufs = vid_cap->reqbufs_count > 1 ? 2 : 1;
-
-
- if (vb2_is_streaming(&vid_cap->vbq) &&
- vid_cap->active_buf_cnt >= min_bufs &&
- !test_and_set_bit(ST_CAPT_STREAM, &fimc->state)) {
- fimc_activate_capture(ctx);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_s_stream(&fimc->pipeline, 1);
- return;
- }
- 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,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
- .start_streaming = start_streaming,
- .stop_streaming = stop_streaming,
-};
-
-/**
- * fimc_capture_ctrls_create - initialize the control handler
- * Initialize the capture video node control handler and fill it
- * with the FIMC controls. Inherit any sensor's controls if the
- * 'user_subdev_api' flag is false (default behaviour).
- * This function need to be called with the graph mutex held.
- */
-int fimc_capture_ctrls_create(struct fimc_dev *fimc)
-{
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- int ret;
-
- if (WARN_ON(vid_cap->ctx == NULL))
- return -ENXIO;
- if (vid_cap->ctx->ctrls.ready)
- return 0;
-
- ret = fimc_ctrls_create(vid_cap->ctx);
- if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready)
- return ret;
-
- return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler,
- fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler);
-}
-
-static int fimc_capture_set_default_format(struct fimc_dev *fimc);
-
-static int fimc_capture_open(struct file *file)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret = -EBUSY;
-
- dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- if (fimc_m2m_active(fimc))
- goto unlock;
-
- set_bit(ST_CAPT_BUSY, &fimc->state);
- ret = pm_runtime_get_sync(&fimc->pdev->dev);
- if (ret < 0)
- goto unlock;
-
- ret = v4l2_fh_open(file);
- if (ret) {
- pm_runtime_put(&fimc->pdev->dev);
- goto unlock;
- }
-
- if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_pipeline_initialize(&fimc->pipeline,
- &fimc->vid_cap.vfd.entity, true);
-
- if (!ret && !fimc->vid_cap.user_subdev_api)
- ret = fimc_capture_set_default_format(fimc);
-
- if (!ret)
- ret = fimc_capture_ctrls_create(fimc);
-
- if (ret < 0) {
- clear_bit(ST_CAPT_BUSY, &fimc->state);
- pm_runtime_put_sync(&fimc->pdev->dev);
- fimc->vid_cap.refcnt--;
- v4l2_fh_release(file);
- }
- }
-unlock:
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static int fimc_capture_close(struct file *file)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret;
-
- dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- if (--fimc->vid_cap.refcnt == 0) {
- clear_bit(ST_CAPT_BUSY, &fimc->state);
- fimc_stop_capture(fimc, false);
- fimc_pipeline_shutdown(&fimc->pipeline);
- clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
- }
-
- pm_runtime_put(&fimc->pdev->dev);
-
- if (fimc->vid_cap.refcnt == 0) {
- vb2_queue_release(&fimc->vid_cap.vbq);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- }
-
- ret = v4l2_fh_release(file);
-
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static unsigned int fimc_capture_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return POLL_ERR;
-
- ret = vb2_poll(&fimc->vid_cap.vbq, file, wait);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-static int fimc_capture_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = vb2_mmap(&fimc->vid_cap.vbq, vma);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-static const struct v4l2_file_operations fimc_capture_fops = {
- .owner = THIS_MODULE,
- .open = fimc_capture_open,
- .release = fimc_capture_close,
- .poll = fimc_capture_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = fimc_capture_mmap,
-};
-
-/*
- * Format and crop negotiation helpers
- */
-
-static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
- u32 *width, u32 *height,
- u32 *code, u32 *fourcc, int pad)
-{
- bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *var = fimc->variant;
- struct fimc_pix_limit *pl = var->pix_limit;
- struct fimc_frame *dst = &ctx->d_frame;
- u32 depth, min_w, max_w, min_h, align_h = 3;
- u32 mask = FMT_FLAGS_CAM;
- struct fimc_fmt *ffmt;
-
- /* Color conversion from/to JPEG is not supported */
- if (code && ctx->s_frame.fmt && pad == FIMC_SD_PAD_SOURCE &&
- fimc_fmt_is_jpeg(ctx->s_frame.fmt->color))
- *code = V4L2_MBUS_FMT_JPEG_1X8;
-
- if (fourcc && *fourcc != V4L2_PIX_FMT_JPEG && pad != FIMC_SD_PAD_SINK)
- mask |= FMT_FLAGS_M2M;
-
- ffmt = fimc_find_format(fourcc, code, mask, 0);
- if (WARN_ON(!ffmt))
- return NULL;
- if (code)
- *code = ffmt->mbus_code;
- if (fourcc)
- *fourcc = ffmt->fourcc;
-
- if (pad == FIMC_SD_PAD_SINK) {
- max_w = fimc_fmt_is_jpeg(ffmt->color) ?
- pl->scaler_dis_w : pl->scaler_en_w;
- /* Apply the camera input interface pixel constraints */
- v4l_bound_align_image(width, max_t(u32, *width, 32), max_w, 4,
- height, max_t(u32, *height, 32),
- FIMC_CAMIF_MAX_HEIGHT,
- fimc_fmt_is_jpeg(ffmt->color) ? 3 : 1,
- 0);
- return ffmt;
- }
- /* Can't scale or crop in transparent (JPEG) transfer mode */
- if (fimc_fmt_is_jpeg(ffmt->color)) {
- *width = ctx->s_frame.f_width;
- *height = ctx->s_frame.f_height;
- return ffmt;
- }
- /* Apply the scaler and the output DMA constraints */
- max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
- if (ctx->state & FIMC_COMPOSE) {
- min_w = dst->offs_h + dst->width;
- min_h = dst->offs_v + dst->height;
- } else {
- min_w = var->min_out_pixsize;
- min_h = var->min_out_pixsize;
- }
- if (var->min_vsize_align == 1 && !rotation)
- align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
-
- depth = fimc_get_format_depth(ffmt);
- v4l_bound_align_image(width, min_w, max_w,
- ffs(var->min_out_pixsize) - 1,
- height, min_h, FIMC_CAMIF_MAX_HEIGHT,
- align_h,
- 64/(ALIGN(depth, 8)));
-
- dbg("pad%d: code: 0x%x, %dx%d. dst fmt: %dx%d",
- pad, code ? *code : 0, *width, *height,
- dst->f_width, dst->f_height);
-
- return ffmt;
-}
-
-static void fimc_capture_try_selection(struct fimc_ctx *ctx,
- struct v4l2_rect *r,
- int target)
-{
- bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *var = fimc->variant;
- struct fimc_pix_limit *pl = var->pix_limit;
- struct fimc_frame *sink = &ctx->s_frame;
- u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
- u32 align_sz = 0, align_h = 4;
- u32 max_sc_h, max_sc_v;
-
- /* In JPEG transparent transfer mode cropping is not supported */
- if (fimc_fmt_is_jpeg(ctx->d_frame.fmt->color)) {
- r->width = sink->f_width;
- r->height = sink->f_height;
- r->left = r->top = 0;
- return;
- }
- if (target == V4L2_SEL_TGT_COMPOSE) {
- if (ctx->rotation != 90 && ctx->rotation != 270)
- align_h = 1;
- max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
- max_sc_v = min(SCALER_MAX_VRATIO, 1 << (ffs(sink->height) - 1));
- min_sz = var->min_out_pixsize;
- } else {
- u32 depth = fimc_get_format_depth(sink->fmt);
- align_sz = 64/ALIGN(depth, 8);
- min_sz = var->min_inp_pixsize;
- min_w = min_h = min_sz;
- max_sc_h = max_sc_v = 1;
- }
- /*
- * For the compose rectangle the following constraints must be met:
- * - it must fit in the sink pad format rectangle (f_width/f_height);
- * - maximum downscaling ratio is 64;
- * - maximum crop size depends if the rotator is used or not;
- * - the sink pad format width/height must be 4 multiple of the
- * prescaler ratios determined by sink pad size and source pad crop,
- * the prescaler ratio is returned by fimc_get_scaler_factor().
- */
- max_w = min_t(u32,
- rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
- rotate ? sink->f_height : sink->f_width);
- max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
-
- if (target == V4L2_SEL_TGT_COMPOSE) {
- min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
- min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
- if (rotate) {
- swap(max_sc_h, max_sc_v);
- swap(min_w, min_h);
- }
- }
- v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
- &r->height, min_h, max_h, align_h,
- align_sz);
- /* Adjust left/top if crop/compose rectangle is out of bounds */
- r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
- r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
- r->left = round_down(r->left, var->hor_offs_align);
-
- dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
- target, r->left, r->top, r->width, r->height,
- sink->f_width, sink->f_height);
-}
-
-/*
- * The video node ioctl operations
- */
-static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
-
- return 0;
-}
-
-static int fimc_cap_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct fimc_fmt *fmt;
-
- fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM | FMT_FLAGS_M2M,
- f->index);
- if (!fmt)
- return -EINVAL;
- strncpy(f->description, fmt->name, sizeof(f->description) - 1);
- f->pixelformat = fmt->fourcc;
- if (fmt->fourcc == V4L2_MBUS_FMT_JPEG_1X8)
- f->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
-}
-
-/**
- * fimc_pipeline_try_format - negotiate and/or set formats at pipeline
- * elements
- * @ctx: FIMC capture context
- * @tfmt: media bus format to try/set on subdevs
- * @fmt_id: fimc pixel format id corresponding to returned @tfmt (output)
- * @set: true to set format on subdevs, false to try only
- */
-static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
- struct v4l2_mbus_framefmt *tfmt,
- struct fimc_fmt **fmt_id,
- bool set)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
- struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
- struct v4l2_subdev_format sfmt;
- struct v4l2_mbus_framefmt *mf = &sfmt.format;
- struct fimc_fmt *ffmt = NULL;
- int ret, i = 0;
-
- if (WARN_ON(!sd || !tfmt))
- return -EINVAL;
-
- memset(&sfmt, 0, sizeof(sfmt));
- sfmt.format = *tfmt;
-
- sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
- while (1) {
- ffmt = fimc_find_format(NULL, mf->code != 0 ? &mf->code : NULL,
- FMT_FLAGS_CAM, i++);
- if (ffmt == NULL) {
- /*
- * Notify user-space if common pixel code for
- * host and sensor does not exist.
- */
- return -EINVAL;
- }
- mf->code = tfmt->code = ffmt->mbus_code;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
- if (ret)
- return ret;
- if (mf->code != tfmt->code) {
- mf->code = 0;
- continue;
- }
- if (mf->width != tfmt->width || mf->height != tfmt->height) {
- u32 fcc = ffmt->fourcc;
- tfmt->width = mf->width;
- tfmt->height = mf->height;
- ffmt = fimc_capture_try_format(ctx,
- &tfmt->width, &tfmt->height,
- NULL, &fcc, FIMC_SD_PAD_SOURCE);
- if (ffmt && ffmt->mbus_code)
- mf->code = ffmt->mbus_code;
- if (mf->width != tfmt->width ||
- mf->height != tfmt->height)
- continue;
- tfmt->code = mf->code;
- }
- if (csis)
- ret = v4l2_subdev_call(csis, pad, set_fmt, NULL, &sfmt);
-
- if (mf->code == tfmt->code &&
- mf->width == tfmt->width && mf->height == tfmt->height)
- break;
- }
-
- if (fmt_id && ffmt)
- *fmt_id = ffmt;
- *tfmt = *mf;
-
- dbg("code: 0x%x, %dx%d, %p", mf->code, mf->width, mf->height, ffmt);
- return 0;
-}
-
-static int fimc_cap_g_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
-
- return fimc_fill_format(&ctx->d_frame, f);
-}
-
-static int fimc_cap_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_mbus_framefmt mf;
- struct fimc_fmt *ffmt = NULL;
-
- if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
- fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
- }
- ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ffmt)
- return -EINVAL;
-
- if (!fimc->vid_cap.user_subdev_api) {
- mf.width = pix->width;
- mf.height = pix->height;
- mf.code = ffmt->mbus_code;
- fimc_md_graph_lock(fimc);
- fimc_pipeline_try_format(ctx, &mf, &ffmt, false);
- fimc_md_graph_unlock(fimc);
-
- pix->width = mf.width;
- pix->height = mf.height;
- if (ffmt)
- pix->pixelformat = ffmt->fourcc;
- }
-
- fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix);
- return 0;
-}
-
-static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, bool jpeg)
-{
- ctx->scaler.enabled = !jpeg;
- fimc_ctrls_activate(ctx, !jpeg);
-
- if (jpeg)
- set_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
- else
- clear_bit(ST_CAPT_JPEG, &ctx->fimc_dev->state);
-}
-
-static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.mf;
- struct fimc_frame *ff = &ctx->d_frame;
- struct fimc_fmt *s_fmt = NULL;
- int ret, i;
-
- if (vb2_is_busy(&fimc->vid_cap.vbq))
- return -EBUSY;
-
- /* Pre-configure format at camera interface input, for JPEG only */
- if (pix->pixelformat == V4L2_PIX_FMT_JPEG) {
- fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SINK);
- ctx->s_frame.f_width = pix->width;
- ctx->s_frame.f_height = pix->height;
- }
- /* Try the format at the scaler and the DMA output */
- ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height,
- NULL, &pix->pixelformat,
- FIMC_SD_PAD_SOURCE);
- if (!ff->fmt)
- return -EINVAL;
-
- /* Update RGB Alpha control state and value range */
- fimc_alpha_ctrl_update(ctx);
-
- /* Try to match format at the host and the sensor */
- if (!fimc->vid_cap.user_subdev_api) {
- mf->code = ff->fmt->mbus_code;
- mf->width = pix->width;
- mf->height = pix->height;
-
- fimc_md_graph_lock(fimc);
- ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true);
- fimc_md_graph_unlock(fimc);
- if (ret)
- return ret;
- pix->width = mf->width;
- pix->height = mf->height;
- }
-
- fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix);
- for (i = 0; i < ff->fmt->colplanes; i++)
- ff->payload[i] = pix->plane_fmt[i].sizeimage;
-
- set_frame_bounds(ff, pix->width, pix->height);
- /* Reset the composition rectangle if not yet configured */
- if (!(ctx->state & FIMC_COMPOSE))
- set_frame_crop(ff, 0, 0, pix->width, pix->height);
-
- fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
-
- /* Reset cropping and set format at the camera interface input */
- if (!fimc->vid_cap.user_subdev_api) {
- ctx->s_frame.fmt = s_fmt;
- set_frame_bounds(&ctx->s_frame, pix->width, pix->height);
- set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height);
- }
-
- return ret;
-}
-
-static int fimc_cap_s_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return fimc_capture_set_format(fimc, f);
-}
-
-static int fimc_cap_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
-
- if (i->index != 0)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- if (sd)
- strlcpy(i->name, sd->name, sizeof(i->name));
- return 0;
-}
-
-static int fimc_cap_s_input(struct file *file, void *priv, unsigned int i)
-{
- return i == 0 ? i : -EINVAL;
-}
-
-static int fimc_cap_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-/**
- * fimc_pipeline_validate - check for formats inconsistencies
- * between source and sink pad of each link
- *
- * Return 0 if all formats match or -EPIPE otherwise.
- */
-static int fimc_pipeline_validate(struct fimc_dev *fimc)
-{
- struct v4l2_subdev_format sink_fmt, src_fmt;
- struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
- struct v4l2_subdev *sd;
- struct media_pad *pad;
- int ret;
-
- /* Start with the video capture node pad */
- pad = media_entity_remote_source(&vid_cap->vd_pad);
- if (pad == NULL)
- return -EPIPE;
- /* FIMC.{N} subdevice */
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- while (1) {
- /* Retrieve format at the sink pad */
- pad = &sd->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
- /* Don't call FIMC subdev operation to avoid nested locking */
- if (sd == &fimc->vid_cap.subdev) {
- struct fimc_frame *ff = &vid_cap->ctx->s_frame;
- sink_fmt.format.width = ff->f_width;
- sink_fmt.format.height = ff->f_height;
- sink_fmt.format.code = ff->fmt ? ff->fmt->mbus_code : 0;
- } else {
- sink_fmt.pad = pad->index;
- sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
- }
- /* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
- src_fmt.pad = pad->index;
- src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
-
- if (src_fmt.format.width != sink_fmt.format.width ||
- src_fmt.format.height != sink_fmt.format.height ||
- src_fmt.format.code != sink_fmt.format.code)
- return -EPIPE;
- }
- return 0;
-}
-
-static int fimc_cap_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_pipeline *p = &fimc->pipeline;
- struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR];
- int ret;
-
- if (fimc_capture_active(fimc))
- return -EBUSY;
-
- ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline);
- if (ret < 0)
- return ret;
-
- if (fimc->vid_cap.user_subdev_api) {
- ret = fimc_pipeline_validate(fimc);
- if (ret < 0) {
- media_entity_pipeline_stop(&sd->entity);
- return ret;
- }
- }
- return vb2_streamon(&fimc->vid_cap.vbq, type);
-}
-
-static int fimc_cap_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
- int ret;
-
- ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
- if (ret == 0)
- media_entity_pipeline_stop(&sd->entity);
- return ret;
-}
-
-static int fimc_cap_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- int ret = vb2_reqbufs(&fimc->vid_cap.vbq, reqbufs);
-
- if (!ret)
- fimc->vid_cap.reqbufs_count = reqbufs->count;
- return ret;
-}
-
-static int fimc_cap_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_querybuf(&fimc->vid_cap.vbq, buf);
-}
-
-static int fimc_cap_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_qbuf(&fimc->vid_cap.vbq, buf);
-}
-
-static int fimc_cap_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_cap_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *create)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_create_bufs(&fimc->vid_cap.vbq, create);
-}
-
-static int fimc_cap_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct fimc_dev *fimc = video_drvdata(file);
-
- return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
-}
-
-static int fimc_cap_g_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *f = &ctx->s_frame;
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
-
- switch (s->target) {
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP_DEFAULT:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = f->o_width;
- s->r.height = f->o_height;
- return 0;
-
- case V4L2_SEL_TGT_COMPOSE:
- f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP:
- s->r.left = f->offs_h;
- s->r.top = f->offs_v;
- s->r.width = f->width;
- s->r.height = f->height;
- return 0;
- }
-
- return -EINVAL;
-}
-
-/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
-static int fimc_cap_s_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_rect rect = s->r;
- struct fimc_frame *f;
- unsigned long flags;
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
-
- if (s->target == V4L2_SEL_TGT_COMPOSE)
- f = &ctx->d_frame;
- else if (s->target == V4L2_SEL_TGT_CROP)
- f = &ctx->s_frame;
- else
- return -EINVAL;
-
- fimc_capture_try_selection(ctx, &rect, s->target);
-
- if (s->flags & V4L2_SEL_FLAG_LE &&
- !enclosed_rectangle(&rect, &s->r))
- return -ERANGE;
-
- if (s->flags & V4L2_SEL_FLAG_GE &&
- !enclosed_rectangle(&s->r, &rect))
- return -ERANGE;
-
- s->r = rect;
- spin_lock_irqsave(&fimc->slock, flags);
- set_frame_crop(f, s->r.left, s->r.top, s->r.width,
- s->r.height);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- return 0;
-}
-
-static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
- .vidioc_querycap = fimc_vidioc_querycap_capture,
-
- .vidioc_enum_fmt_vid_cap_mplane = fimc_cap_enum_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_cap_try_fmt_mplane,
- .vidioc_s_fmt_vid_cap_mplane = fimc_cap_s_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_cap_g_fmt_mplane,
-
- .vidioc_reqbufs = fimc_cap_reqbufs,
- .vidioc_querybuf = fimc_cap_querybuf,
-
- .vidioc_qbuf = fimc_cap_qbuf,
- .vidioc_dqbuf = fimc_cap_dqbuf,
-
- .vidioc_prepare_buf = fimc_cap_prepare_buf,
- .vidioc_create_bufs = fimc_cap_create_bufs,
-
- .vidioc_streamon = fimc_cap_streamon,
- .vidioc_streamoff = fimc_cap_streamoff,
-
- .vidioc_g_selection = fimc_cap_g_selection,
- .vidioc_s_selection = fimc_cap_s_selection,
-
- .vidioc_enum_input = fimc_cap_enum_input,
- .vidioc_s_input = fimc_cap_s_input,
- .vidioc_g_input = fimc_cap_g_input,
-};
-
-/* Capture subdev media entity operations */
-static int fimc_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
-
- if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- return -EINVAL;
-
- if (WARN_ON(fimc == NULL))
- return 0;
-
- dbg("%s --> %s, flags: 0x%x. input: 0x%x",
- local->entity->name, remote->entity->name, flags,
- fimc->vid_cap.input);
-
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (fimc->vid_cap.input != 0)
- return -EBUSY;
- fimc->vid_cap.input = sd->grp_id;
- return 0;
- }
-
- fimc->vid_cap.input = 0;
- return 0;
-}
-
-static const struct media_entity_operations fimc_sd_media_ops = {
- .link_setup = fimc_link_setup,
-};
-
-/**
- * fimc_sensor_notify - v4l2_device notification from a sensor subdev
- * @sd: pointer to a subdev generating the notification
- * @notification: the notification type, must be S5P_FIMC_TX_END_NOTIFY
- * @arg: pointer to an u32 type integer that stores the frame payload value
- *
- * The End Of Frame notification sent by sensor subdev in its still capture
- * mode. If there is only a single VSYNC generated by the sensor at the
- * beginning of a frame transmission, FIMC does not issue the LastIrq
- * (end of frame) interrupt. And this notification is used to complete the
- * frame capture and returning a buffer to user-space. Subdev drivers should
- * call this notification from their last 'End of frame capture' interrupt.
- */
-void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
- void *arg)
-{
- struct fimc_sensor_info *sensor;
- struct fimc_vid_buffer *buf;
- struct fimc_md *fmd;
- struct fimc_dev *fimc;
- unsigned long flags;
-
- if (sd == NULL)
- return;
-
- sensor = v4l2_get_subdev_hostdata(sd);
- fmd = entity_to_fimc_mdev(&sd->entity);
-
- spin_lock_irqsave(&fmd->slock, flags);
- fimc = sensor ? sensor->host : NULL;
-
- if (fimc && arg && notification == S5P_FIMC_TX_END_NOTIFY &&
- test_bit(ST_CAPT_PEND, &fimc->state)) {
- unsigned long irq_flags;
- spin_lock_irqsave(&fimc->slock, irq_flags);
- if (!list_empty(&fimc->vid_cap.active_buf_q)) {
- buf = list_entry(fimc->vid_cap.active_buf_q.next,
- struct fimc_vid_buffer, list);
- vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
- }
- fimc_capture_irq_handler(fimc, 1);
- fimc_deactivate_capture(fimc);
- spin_unlock_irqrestore(&fimc->slock, irq_flags);
- }
- spin_unlock_irqrestore(&fmd->slock, flags);
-}
-
-static int fimc_subdev_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct fimc_fmt *fmt;
-
- fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, code->index);
- if (!fmt)
- return -EINVAL;
- code->code = fmt->mbus_code;
- return 0;
-}
-
-static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_mbus_framefmt *mf;
- struct fimc_frame *ff;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- fmt->format = *mf;
- return 0;
- }
- mf = &fmt->format;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- ff = fmt->pad == FIMC_SD_PAD_SINK ? &ctx->s_frame : &ctx->d_frame;
-
- mutex_lock(&fimc->lock);
- /* The pixel code is same on both input and output pad */
- if (!WARN_ON(ctx->s_frame.fmt == NULL))
- mf->code = ctx->s_frame.fmt->mbus_code;
- mf->width = ff->f_width;
- mf->height = ff->f_height;
- mutex_unlock(&fimc->lock);
-
- return 0;
-}
-
-static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *ff;
- struct fimc_fmt *ffmt;
-
- dbg("pad%d: code: 0x%x, %dx%d",
- fmt->pad, mf->code, mf->width, mf->height);
-
- if (fmt->pad == FIMC_SD_PAD_SOURCE &&
- vb2_is_busy(&fimc->vid_cap.vbq))
- return -EBUSY;
-
- mutex_lock(&fimc->lock);
- ffmt = fimc_capture_try_format(ctx, &mf->width, &mf->height,
- &mf->code, NULL, fmt->pad);
- mutex_unlock(&fimc->lock);
- mf->colorspace = V4L2_COLORSPACE_JPEG;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- *mf = fmt->format;
- return 0;
- }
- /* Update RGB Alpha control state and value range */
- fimc_alpha_ctrl_update(ctx);
-
- fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ffmt->color));
-
- ff = fmt->pad == FIMC_SD_PAD_SINK ?
- &ctx->s_frame : &ctx->d_frame;
-
- mutex_lock(&fimc->lock);
- set_frame_bounds(ff, mf->width, mf->height);
- fimc->vid_cap.mf = *mf;
- ff->fmt = ffmt;
-
- /* Reset the crop rectangle if required. */
- if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
- set_frame_crop(ff, 0, 0, mf->width, mf->height);
-
- if (fmt->pad == FIMC_SD_PAD_SINK)
- ctx->state &= ~FIMC_COMPOSE;
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *f = &ctx->s_frame;
- struct v4l2_rect *r = &sel->r;
- struct v4l2_rect *try_sel;
-
- if (sel->pad != FIMC_SD_PAD_SINK)
- return -EINVAL;
-
- mutex_lock(&fimc->lock);
-
- switch (sel->target) {
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- r->width = f->o_width;
- r->height = f->o_height;
- r->left = 0;
- r->top = 0;
- mutex_unlock(&fimc->lock);
- return 0;
-
- case V4L2_SEL_TGT_CROP:
- try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
- break;
- case V4L2_SEL_TGT_COMPOSE:
- try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
- f = &ctx->d_frame;
- break;
- default:
- mutex_unlock(&fimc->lock);
- return -EINVAL;
- }
-
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
- sel->r = *try_sel;
- } else {
- r->left = f->offs_h;
- r->top = f->offs_v;
- r->width = f->width;
- r->height = f->height;
- }
-
- dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
- sel->pad, r->left, r->top, r->width, r->height,
- f->f_width, f->f_height);
-
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
- struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *f = &ctx->s_frame;
- struct v4l2_rect *r = &sel->r;
- struct v4l2_rect *try_sel;
- unsigned long flags;
-
- if (sel->pad != FIMC_SD_PAD_SINK)
- return -EINVAL;
-
- mutex_lock(&fimc->lock);
- fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP);
-
- switch (sel->target) {
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- f = &ctx->d_frame;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- r->width = f->o_width;
- r->height = f->o_height;
- r->left = 0;
- r->top = 0;
- mutex_unlock(&fimc->lock);
- return 0;
-
- case V4L2_SEL_TGT_CROP:
- try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
- break;
- case V4L2_SEL_TGT_COMPOSE:
- try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
- f = &ctx->d_frame;
- break;
- default:
- mutex_unlock(&fimc->lock);
- return -EINVAL;
- }
-
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
- *try_sel = sel->r;
- } else {
- spin_lock_irqsave(&fimc->slock, flags);
- set_frame_crop(f, r->left, r->top, r->width, r->height);
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
- if (sel->target == V4L2_SEL_TGT_COMPOSE)
- ctx->state |= FIMC_COMPOSE;
- }
-
- dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
- r->width, r->height);
-
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
- .enum_mbus_code = fimc_subdev_enum_mbus_code,
- .get_selection = fimc_subdev_get_selection,
- .set_selection = fimc_subdev_set_selection,
- .get_fmt = fimc_subdev_get_fmt,
- .set_fmt = fimc_subdev_set_fmt,
-};
-
-static struct v4l2_subdev_ops fimc_subdev_ops = {
- .pad = &fimc_subdev_pad_ops,
-};
-
-/* Set default format at the sensor and host interface */
-static int fimc_capture_set_default_format(struct fimc_dev *fimc)
-{
- struct v4l2_format fmt = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
- .fmt.pix_mp = {
- .width = 640,
- .height = 480,
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_JPEG,
- },
- };
-
- return fimc_capture_set_format(fimc, &fmt);
-}
-
-/* fimc->lock must be already initialized */
-static int fimc_register_capture_device(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev)
-{
- struct video_device *vfd = &fimc->vid_cap.vfd;
- struct fimc_vid_cap *vid_cap;
- struct fimc_ctx *ctx;
- struct vb2_queue *q;
- int ret = -ENOMEM;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- ctx->fimc_dev = fimc;
- ctx->in_path = FIMC_IO_CAMERA;
- ctx->out_path = FIMC_IO_DMA;
- ctx->state = FIMC_CTX_CAP;
- ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
- ctx->d_frame.fmt = ctx->s_frame.fmt;
-
- memset(vfd, 0, sizeof(*vfd));
- snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id);
-
- vfd->fops = &fimc_capture_fops;
- vfd->ioctl_ops = &fimc_capture_ioctl_ops;
- vfd->v4l2_dev = v4l2_dev;
- vfd->minor = -1;
- vfd->release = video_device_release_empty;
- vfd->lock = &fimc->lock;
-
- video_set_drvdata(vfd, fimc);
-
- vid_cap = &fimc->vid_cap;
- vid_cap->active_buf_cnt = 0;
- vid_cap->reqbufs_count = 0;
- vid_cap->refcnt = 0;
-
- INIT_LIST_HEAD(&vid_cap->pending_buf_q);
- INIT_LIST_HEAD(&vid_cap->active_buf_q);
- vid_cap->ctx = ctx;
-
- 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);
-
- vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
- if (ret)
- goto err_ent;
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret)
- goto err_vd;
-
- v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
- vfd->name, video_device_node_name(vfd));
-
- vfd->ctrl_handler = &ctx->ctrls.handler;
- return 0;
-
-err_vd:
- media_entity_cleanup(&vfd->entity);
-err_ent:
- kfree(ctx);
- return ret;
-}
-
-static int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
- int ret;
-
- if (fimc == NULL)
- return -ENXIO;
-
- ret = fimc_register_m2m_device(fimc, sd->v4l2_dev);
- if (ret)
- return ret;
-
- ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
- if (ret)
- fimc_unregister_m2m_device(fimc);
-
- return ret;
-}
-
-static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
-{
- struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
-
- if (fimc == NULL)
- return;
-
- fimc_unregister_m2m_device(fimc);
-
- if (video_is_registered(&fimc->vid_cap.vfd)) {
- video_unregister_device(&fimc->vid_cap.vfd);
- media_entity_cleanup(&fimc->vid_cap.vfd.entity);
- }
- kfree(fimc->vid_cap.ctx);
- fimc->vid_cap.ctx = NULL;
-}
-
-static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
- .registered = fimc_capture_subdev_registered,
- .unregistered = fimc_capture_subdev_unregistered,
-};
-
-int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
-{
- struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
- int ret;
-
- v4l2_subdev_init(sd, &fimc_subdev_ops);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
-
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
- fimc->vid_cap.sd_pads, 0);
- if (ret)
- return ret;
-
- sd->entity.ops = &fimc_sd_media_ops;
- sd->internal_ops = &fimc_capture_sd_internal_ops;
- v4l2_set_subdevdata(sd, fimc);
- return 0;
-}
-
-void fimc_unregister_capture_subdev(struct fimc_dev *fimc)
-{
- struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
-
- v4l2_device_unregister_subdev(sd);
- media_entity_cleanup(&sd->entity);
- v4l2_set_subdevdata(sd, NULL);
-}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
deleted file mode 100644
index d3a3a00321c3..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
- *
- * 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.
- */
-
-#ifndef FIMC_CORE_H_
-#define FIMC_CORE_H_
-
-/*#define DEBUG*/
-
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/videodev2.h>
-#include <linux/io.h>
-#include <asm/sizes.h>
-
-#include <media/media-entity.h>
-#include <media/videobuf2-core.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
-
-#define dbg(fmt, args...) \
- pr_debug("%s:%d: " fmt "\n", __func__, __LINE__, ##args)
-
-/* Time to wait for next frame VSYNC interrupt while stopping operation. */
-#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
-#define MAX_FIMC_CLOCKS 2
-#define FIMC_MODULE_NAME "s5p-fimc"
-#define FIMC_MAX_DEVS 4
-#define FIMC_MAX_OUT_BUFS 4
-#define SCALER_MAX_HRATIO 64
-#define SCALER_MAX_VRATIO 64
-#define DMA_MIN_SIZE 8
-#define FIMC_CAMIF_MAX_HEIGHT 0x2000
-
-/* indices to the clocks array */
-enum {
- CLK_BUS,
- CLK_GATE,
-};
-
-enum fimc_dev_flags {
- ST_LPM,
- /* m2m node */
- ST_M2M_RUN,
- ST_M2M_PEND,
- ST_M2M_SUSPENDING,
- ST_M2M_SUSPENDED,
- /* capture node */
- ST_CAPT_PEND,
- ST_CAPT_RUN,
- ST_CAPT_STREAM,
- ST_CAPT_ISP_STREAM,
- ST_CAPT_SUSPENDED,
- ST_CAPT_SHUT,
- ST_CAPT_BUSY,
- ST_CAPT_APPLY_CFG,
- ST_CAPT_JPEG,
-};
-
-#define fimc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state)
-#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)
-
-#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
-#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)
-#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
-
-enum fimc_datapath {
- FIMC_IO_NONE,
- FIMC_IO_CAMERA,
- FIMC_IO_DMA,
- FIMC_IO_LCDFIFO,
- FIMC_IO_WRITEBACK,
- FIMC_IO_ISP,
-};
-
-enum fimc_color_fmt {
- FIMC_FMT_RGB444 = 0x10,
- FIMC_FMT_RGB555,
- FIMC_FMT_RGB565,
- FIMC_FMT_RGB666,
- FIMC_FMT_RGB888,
- FIMC_FMT_RGB30_LOCAL,
- FIMC_FMT_YCBCR420 = 0x20,
- FIMC_FMT_YCBYCR422,
- FIMC_FMT_YCRYCB422,
- FIMC_FMT_CBYCRY422,
- FIMC_FMT_CRYCBY422,
- FIMC_FMT_YCBCR444_LOCAL,
- FIMC_FMT_JPEG = 0x40,
- FIMC_FMT_RAW8 = 0x80,
- FIMC_FMT_RAW10,
- FIMC_FMT_RAW12,
-};
-
-#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
-#define fimc_fmt_is_jpeg(x) (!!((x) & 0x40))
-
-#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
- __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
-
-/* The hardware context state. */
-#define FIMC_PARAMS (1 << 0)
-#define FIMC_SRC_FMT (1 << 3)
-#define FIMC_DST_FMT (1 << 4)
-#define FIMC_COMPOSE (1 << 5)
-#define FIMC_CTX_M2M (1 << 16)
-#define FIMC_CTX_CAP (1 << 17)
-#define FIMC_CTX_SHUT (1 << 18)
-
-/* Image conversion flags */
-#define FIMC_IN_DMA_ACCESS_TILED (1 << 0)
-#define FIMC_IN_DMA_ACCESS_LINEAR (0 << 0)
-#define FIMC_OUT_DMA_ACCESS_TILED (1 << 1)
-#define FIMC_OUT_DMA_ACCESS_LINEAR (0 << 1)
-#define FIMC_SCAN_MODE_PROGRESSIVE (0 << 2)
-#define FIMC_SCAN_MODE_INTERLACED (1 << 2)
-/*
- * YCbCr data dynamic range for RGB-YUV color conversion.
- * Y/Cb/Cr: (0 ~ 255) */
-#define FIMC_COLOR_RANGE_WIDE (0 << 3)
-/* Y (16 ~ 235), Cb/Cr (16 ~ 240) */
-#define FIMC_COLOR_RANGE_NARROW (1 << 3)
-
-/**
- * struct fimc_fmt - the driver's internal color format data
- * @mbus_code: Media Bus pixel code, -1 if not applicable
- * @name: format description
- * @fourcc: the fourcc code for this format, 0 if not applicable
- * @color: the corresponding fimc_color_fmt
- * @memplanes: number of physically non-contiguous data planes
- * @colplanes: number of physically contiguous data planes
- * @depth: per plane driver's private 'number of bits per pixel'
- * @flags: flags indicating which operation mode format applies to
- */
-struct fimc_fmt {
- enum v4l2_mbus_pixelcode mbus_code;
- char *name;
- u32 fourcc;
- u32 color;
- u16 memplanes;
- u16 colplanes;
- u8 depth[VIDEO_MAX_PLANES];
- u16 flags;
-#define FMT_FLAGS_CAM (1 << 0)
-#define FMT_FLAGS_M2M_IN (1 << 1)
-#define FMT_FLAGS_M2M_OUT (1 << 2)
-#define FMT_FLAGS_M2M (1 << 1 | 1 << 2)
-#define FMT_HAS_ALPHA (1 << 3)
-};
-
-/**
- * struct fimc_dma_offset - pixel offset information for DMA
- * @y_h: y value horizontal offset
- * @y_v: y value vertical offset
- * @cb_h: cb value horizontal offset
- * @cb_v: cb value vertical offset
- * @cr_h: cr value horizontal offset
- * @cr_v: cr value vertical offset
- */
-struct fimc_dma_offset {
- int y_h;
- int y_v;
- int cb_h;
- int cb_v;
- int cr_h;
- int cr_v;
-};
-
-/**
- * struct fimc_effect - color effect information
- * @type: effect type
- * @pat_cb: cr value when type is "arbitrary"
- * @pat_cr: cr value when type is "arbitrary"
- */
-struct fimc_effect {
- u32 type;
- u8 pat_cb;
- u8 pat_cr;
-};
-
-/**
- * struct fimc_scaler - the configuration data for FIMC inetrnal scaler
- * @scaleup_h: flag indicating scaling up horizontally
- * @scaleup_v: flag indicating scaling up vertically
- * @copy_mode: flag indicating transparent DMA transfer (no scaling
- * and color format conversion)
- * @enabled: flag indicating if the scaler is used
- * @hfactor: horizontal shift factor
- * @vfactor: vertical shift factor
- * @pre_hratio: horizontal ratio of the prescaler
- * @pre_vratio: vertical ratio of the prescaler
- * @pre_dst_width: the prescaler's destination width
- * @pre_dst_height: the prescaler's destination height
- * @main_hratio: the main scaler's horizontal ratio
- * @main_vratio: the main scaler's vertical ratio
- * @real_width: source pixel (width - offset)
- * @real_height: source pixel (height - offset)
- */
-struct fimc_scaler {
- unsigned int scaleup_h:1;
- unsigned int scaleup_v:1;
- unsigned int copy_mode:1;
- unsigned int enabled:1;
- u32 hfactor;
- u32 vfactor;
- u32 pre_hratio;
- u32 pre_vratio;
- u32 pre_dst_width;
- u32 pre_dst_height;
- u32 main_hratio;
- u32 main_vratio;
- u32 real_width;
- u32 real_height;
-};
-
-/**
- * struct fimc_addr - the FIMC physical address set for DMA
- * @y: luminance plane physical address
- * @cb: Cb plane physical address
- * @cr: Cr plane physical address
- */
-struct fimc_addr {
- u32 y;
- u32 cb;
- u32 cr;
-};
-
-/**
- * struct fimc_vid_buffer - the driver's video buffer
- * @vb: v4l videobuf buffer
- * @list: linked list structure for buffer queue
- * @paddr: precalculated physical address set
- * @index: buffer index for the output DMA engine
- */
-struct fimc_vid_buffer {
- struct vb2_buffer vb;
- struct list_head list;
- struct fimc_addr paddr;
- int index;
-};
-
-/**
- * struct fimc_frame - source/target frame properties
- * @f_width: image full width (virtual screen size)
- * @f_height: image full height (virtual screen size)
- * @o_width: original image width as set by S_FMT
- * @o_height: original image height as set by S_FMT
- * @offs_h: image horizontal pixel offset
- * @offs_v: image vertical pixel offset
- * @width: image pixel width
- * @height: image pixel weight
- * @payload: image size in bytes (w x h x bpp)
- * @paddr: image frame buffer physical addresses
- * @dma_offset: DMA offset in bytes
- * @fmt: fimc color format pointer
- */
-struct fimc_frame {
- u32 f_width;
- u32 f_height;
- u32 o_width;
- u32 o_height;
- u32 offs_h;
- u32 offs_v;
- u32 width;
- u32 height;
- unsigned long payload[VIDEO_MAX_PLANES];
- struct fimc_addr paddr;
- struct fimc_dma_offset dma_offset;
- struct fimc_fmt *fmt;
- u8 alpha;
-};
-
-/**
- * struct fimc_m2m_device - v4l2 memory-to-memory device data
- * @vfd: the video device node for v4l2 m2m mode
- * @m2m_dev: v4l2 memory-to-memory device data
- * @ctx: hardware context data
- * @refcnt: the reference counter
- */
-struct fimc_m2m_device {
- struct video_device vfd;
- struct v4l2_m2m_dev *m2m_dev;
- struct fimc_ctx *ctx;
- int refcnt;
-};
-
-#define FIMC_SD_PAD_SINK 0
-#define FIMC_SD_PAD_SOURCE 1
-#define FIMC_SD_PADS_NUM 2
-
-/**
- * struct fimc_vid_cap - camera capture device information
- * @ctx: hardware context data
- * @vfd: video device node for camera capture mode
- * @subdev: subdev exposing the FIMC processing block
- * @vd_pad: fimc video capture node pad
- * @sd_pads: fimc video processing block pads
- * @mf: media bus format at the FIMC camera input (and the scaler output) pad
- * @pending_buf_q: the pending buffer queue head
- * @active_buf_q: the queue head of buffers scheduled in hardware
- * @vbq: the capture am video buffer queue
- * @active_buf_cnt: number of video buffers scheduled in hardware
- * @buf_index: index for managing the output DMA buffers
- * @frame_count: the frame counter for statistics
- * @reqbufs_count: the number of buffers requested in REQBUFS ioctl
- * @input_index: input (camera sensor) index
- * @refcnt: driver's private reference counter
- * @input: capture input type, grp_id of the attached subdev
- * @user_subdev_api: true if subdevs are not configured by the host driver
- */
-struct fimc_vid_cap {
- struct fimc_ctx *ctx;
- struct vb2_alloc_ctx *alloc_ctx;
- struct video_device vfd;
- struct v4l2_subdev subdev;
- struct media_pad vd_pad;
- struct v4l2_mbus_framefmt mf;
- struct media_pad sd_pads[FIMC_SD_PADS_NUM];
- struct list_head pending_buf_q;
- struct list_head active_buf_q;
- struct vb2_queue vbq;
- int active_buf_cnt;
- int buf_index;
- unsigned int frame_count;
- unsigned int reqbufs_count;
- int input_index;
- int refcnt;
- u32 input;
- bool user_subdev_api;
-};
-
-/**
- * struct fimc_pix_limit - image pixel size limits in various IP configurations
- *
- * @scaler_en_w: max input pixel width when the scaler is enabled
- * @scaler_dis_w: max input pixel width when the scaler is disabled
- * @in_rot_en_h: max input width with the input rotator is on
- * @in_rot_dis_w: max input width with the input rotator is off
- * @out_rot_en_w: max output width with the output rotator on
- * @out_rot_dis_w: max output width with the output rotator off
- */
-struct fimc_pix_limit {
- u16 scaler_en_w;
- u16 scaler_dis_w;
- u16 in_rot_en_h;
- u16 in_rot_dis_w;
- u16 out_rot_en_w;
- u16 out_rot_dis_w;
-};
-
-/**
- * struct fimc_variant - FIMC device variant information
- * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
- * @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
- * @has_cam_if: set if this instance has a camera input interface
- * @pix_limit: pixel size constraints for the scaler
- * @min_inp_pixsize: minimum input pixel size
- * @min_out_pixsize: minimum output pixel size
- * @hor_offs_align: horizontal pixel offset aligment
- * @min_vsize_align: minimum vertical pixel size alignment
- * @out_buf_count: the number of buffers in output DMA sequence
- */
-struct fimc_variant {
- unsigned int pix_hoff:1;
- unsigned int has_inp_rot:1;
- unsigned int has_out_rot:1;
- unsigned int has_cistatus2:1;
- unsigned int has_mainscaler_ext:1;
- unsigned int has_cam_if:1;
- unsigned int has_alpha:1;
- struct fimc_pix_limit *pix_limit;
- u16 min_inp_pixsize;
- u16 min_out_pixsize;
- u16 hor_offs_align;
- u16 min_vsize_align;
- u16 out_buf_count;
-};
-
-/**
- * struct fimc_drvdata - per device type driver data
- * @variant: variant information for this device
- * @num_entities: number of fimc instances available in a SoC
- * @lclk_frequency: local bus clock frequency
- */
-struct fimc_drvdata {
- struct fimc_variant *variant[FIMC_MAX_DEVS];
- int num_entities;
- unsigned long lclk_frequency;
-};
-
-#define fimc_get_drvdata(_pdev) \
- ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data)
-
-struct fimc_ctx;
-
-/**
- * struct fimc_dev - abstraction for FIMC entity
- * @slock: the spinlock protecting this data structure
- * @lock: the mutex protecting this data structure
- * @pdev: pointer to the FIMC platform device
- * @pdata: pointer to the device platform data
- * @variant: the IP variant information
- * @id: FIMC device index (0..FIMC_MAX_DEVS)
- * @clock: clocks required for FIMC operation
- * @regs: the mapped hardware registers
- * @irq_queue: interrupt handler waitqueue
- * @v4l2_dev: root v4l2_device
- * @m2m: memory-to-memory V4L2 device information
- * @vid_cap: camera capture device information
- * @state: flags used to synchronize m2m and capture mode operation
- * @alloc_ctx: videobuf2 memory allocator context
- * @pipeline: fimc video capture pipeline data structure
- */
-struct fimc_dev {
- spinlock_t slock;
- struct mutex lock;
- struct platform_device *pdev;
- struct s5p_platform_fimc *pdata;
- struct fimc_variant *variant;
- u16 id;
- struct clk *clock[MAX_FIMC_CLOCKS];
- void __iomem *regs;
- wait_queue_head_t irq_queue;
- struct v4l2_device *v4l2_dev;
- struct fimc_m2m_device m2m;
- struct fimc_vid_cap vid_cap;
- unsigned long state;
- struct vb2_alloc_ctx *alloc_ctx;
- struct fimc_pipeline pipeline;
-};
-
-/**
- * struct fimc_ctrls - v4l2 controls structure
- * @handler: the control handler
- * @colorfx: image effect control
- * @colorfx_cbcr: Cb/Cr coefficients control
- * @rotate: image rotation control
- * @hflip: horizontal flip control
- * @vflip: vertical flip control
- * @alpha: RGB alpha control
- * @ready: true if @handler is initialized
- */
-struct fimc_ctrls {
- struct v4l2_ctrl_handler handler;
- struct {
- struct v4l2_ctrl *colorfx;
- struct v4l2_ctrl *colorfx_cbcr;
- };
- struct v4l2_ctrl *rotate;
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- struct v4l2_ctrl *alpha;
- bool ready;
-};
-
-/**
- * fimc_ctx - the device context data
- * @s_frame: source frame properties
- * @d_frame: destination frame properties
- * @out_order_1p: output 1-plane YCBCR order
- * @out_order_2p: output 2-plane YCBCR order
- * @in_order_1p input 1-plane YCBCR order
- * @in_order_2p: input 2-plane YCBCR order
- * @in_path: input mode (DMA or camera)
- * @out_path: output mode (DMA or FIFO)
- * @scaler: image scaler properties
- * @effect: image effect
- * @rotation: image clockwise rotation in degrees
- * @hflip: indicates image horizontal flip if set
- * @vflip: indicates image vertical flip if set
- * @flags: additional flags for image conversion
- * @state: flags to keep track of user configuration
- * @fimc_dev: the FIMC device this context applies to
- * @m2m_ctx: memory-to-memory device context
- * @fh: v4l2 file handle
- * @ctrls: v4l2 controls structure
- */
-struct fimc_ctx {
- struct fimc_frame s_frame;
- struct fimc_frame d_frame;
- u32 out_order_1p;
- u32 out_order_2p;
- u32 in_order_1p;
- u32 in_order_2p;
- enum fimc_datapath in_path;
- enum fimc_datapath out_path;
- struct fimc_scaler scaler;
- struct fimc_effect effect;
- int rotation;
- unsigned int hflip:1;
- unsigned int vflip:1;
- u32 flags;
- u32 state;
- struct fimc_dev *fimc_dev;
- struct v4l2_m2m_ctx *m2m_ctx;
- struct v4l2_fh fh;
- struct fimc_ctrls ctrls;
-};
-
-#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
-
-static inline void set_frame_bounds(struct fimc_frame *f, u32 width, u32 height)
-{
- f->o_width = width;
- f->o_height = height;
- f->f_width = width;
- f->f_height = height;
-}
-
-static inline void set_frame_crop(struct fimc_frame *f,
- u32 left, u32 top, u32 width, u32 height)
-{
- f->offs_h = left;
- f->offs_v = top;
- f->width = width;
- f->height = height;
-}
-
-static inline u32 fimc_get_format_depth(struct fimc_fmt *ff)
-{
- u32 i, depth = 0;
-
- if (ff != NULL)
- for (i = 0; i < ff->colplanes; i++)
- depth += ff->depth[i];
- return depth;
-}
-
-static inline bool fimc_capture_active(struct fimc_dev *fimc)
-{
- unsigned long flags;
- bool ret;
-
- spin_lock_irqsave(&fimc->slock, flags);
- ret = !!(fimc->state & (1 << ST_CAPT_RUN) ||
- fimc->state & (1 << ST_CAPT_PEND));
- spin_unlock_irqrestore(&fimc->slock, flags);
- return ret;
-}
-
-static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
- ctx->state |= state;
- spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
-}
-
-static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx)
-{
- unsigned long flags;
- bool ret;
-
- spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
- ret = (ctx->state & mask) == mask;
- spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
- return ret;
-}
-
-static inline int tiled_fmt(struct fimc_fmt *fmt)
-{
- return fmt->fourcc == V4L2_PIX_FMT_NV12MT;
-}
-
-/* Return the alpha component bit mask */
-static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt)
-{
- switch (fmt->color) {
- case FIMC_FMT_RGB444: return 0x0f;
- case FIMC_FMT_RGB555: return 0x01;
- case FIMC_FMT_RGB888: return 0xff;
- default: return 0;
- };
-}
-
-static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
- enum v4l2_buf_type type)
-{
- struct fimc_frame *frame;
-
- if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) {
- if (fimc_ctx_state_is_set(FIMC_CTX_M2M, ctx))
- frame = &ctx->s_frame;
- else
- return ERR_PTR(-EINVAL);
- } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) {
- frame = &ctx->d_frame;
- } else {
- v4l2_err(ctx->fimc_dev->v4l2_dev,
- "Wrong buffer/video queue type (%d)\n", type);
- return ERR_PTR(-EINVAL);
- }
-
- return frame;
-}
-
-/* -----------------------------------------------------*/
-/* fimc-core.c */
-int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f);
-int fimc_ctrls_create(struct fimc_ctx *ctx);
-void fimc_ctrls_delete(struct fimc_ctx *ctx);
-void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active);
-void fimc_alpha_ctrl_update(struct fimc_ctx *ctx);
-int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f);
-void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
- struct v4l2_pix_format_mplane *pix);
-struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
- unsigned int mask, int index);
-struct fimc_fmt *fimc_get_format(unsigned int index);
-
-int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
- int dw, int dh, int rotation);
-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 vb2_buffer *vb,
- struct fimc_frame *frame, struct fimc_addr *paddr);
-void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
-void fimc_set_yuv_order(struct fimc_ctx *ctx);
-void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
-void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf);
-
-int fimc_register_m2m_device(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev);
-void fimc_unregister_m2m_device(struct fimc_dev *fimc);
-int fimc_register_driver(void);
-void fimc_unregister_driver(void);
-
-/* -----------------------------------------------------*/
-/* fimc-m2m.c */
-void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state);
-
-/* -----------------------------------------------------*/
-/* fimc-capture.c */
-int fimc_initialize_capture_subdev(struct fimc_dev *fimc);
-void fimc_unregister_capture_subdev(struct fimc_dev *fimc);
-int fimc_capture_ctrls_create(struct fimc_dev *fimc);
-void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
- void *arg);
-int fimc_capture_suspend(struct fimc_dev *fimc);
-int fimc_capture_resume(struct fimc_dev *fimc);
-
-/*
- * Buffer list manipulation functions. Must be called with fimc.slock held.
- */
-
-/**
- * fimc_active_queue_add - add buffer to the capture active buffers queue
- * @buf: buffer to add to the active buffers list
- */
-static inline void fimc_active_queue_add(struct fimc_vid_cap *vid_cap,
- struct fimc_vid_buffer *buf)
-{
- list_add_tail(&buf->list, &vid_cap->active_buf_q);
- vid_cap->active_buf_cnt++;
-}
-
-/**
- * fimc_active_queue_pop - pop buffer from the capture active buffers queue
- *
- * The caller must assure the active_buf_q list is not empty.
- */
-static inline struct fimc_vid_buffer *fimc_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, list);
- list_del(&buf->list);
- vid_cap->active_buf_cnt--;
- return buf;
-}
-
-/**
- * fimc_pending_queue_add - add buffer to the capture pending buffers queue
- * @buf: buffer to add to the pending buffers list
- */
-static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
- struct fimc_vid_buffer *buf)
-{
- list_add_tail(&buf->list, &vid_cap->pending_buf_q);
-}
-
-/**
- * fimc_pending_queue_pop - pop buffer from the capture pending buffers queue
- *
- * The caller must assure the pending_buf_q list is not empty.
- */
-static inline struct fimc_vid_buffer *fimc_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, list);
- list_del(&buf->list);
- return buf;
-}
-
-#endif /* FIMC_CORE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c
deleted file mode 100644
index 09dc71e477df..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-lite-reg.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Register interface file for EXYNOS FIMC-LITE (camera interface) driver
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * 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 version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <media/s5p_fimc.h>
-
-#include "fimc-lite-reg.h"
-#include "fimc-lite.h"
-#include "fimc-core.h"
-
-#define FLITE_RESET_TIMEOUT 50 /* in ms */
-
-void flite_hw_reset(struct fimc_lite *dev)
-{
- unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT);
- u32 cfg;
-
- cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- cfg |= FLITE_REG_CIGCTRL_SWRST_REQ;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-
- while (time_is_after_jiffies(end)) {
- cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY)
- break;
- usleep_range(1000, 5000);
- }
-
- cfg |= FLITE_REG_CIGCTRL_SWRST;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-}
-
-void flite_hw_clear_pending_irq(struct fimc_lite *dev)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS);
- cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM;
- writel(cfg, dev->regs + FLITE_REG_CISTATUS);
-}
-
-u32 flite_hw_get_interrupt_source(struct fimc_lite *dev)
-{
- u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS);
- return intsrc & FLITE_REG_CISTATUS_IRQ_MASK;
-}
-
-void flite_hw_clear_last_capture_end(struct fimc_lite *dev)
-{
-
- u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2);
- cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND;
- writel(cfg, dev->regs + FLITE_REG_CISTATUS2);
-}
-
-void flite_hw_set_interrupt_mask(struct fimc_lite *dev)
-{
- u32 cfg, intsrc;
-
- /* Select interrupts to be enabled for each output mode */
- if (dev->out_path == FIMC_IO_DMA) {
- intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
- FLITE_REG_CIGCTRL_IRQ_LASTEN |
- FLITE_REG_CIGCTRL_IRQ_STARTEN;
- } else {
- /* An output to the FIMC-IS */
- intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
- FLITE_REG_CIGCTRL_IRQ_LASTEN;
- }
-
- cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK;
- cfg &= ~intsrc;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-}
-
-void flite_hw_capture_start(struct fimc_lite *dev)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
- cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN;
- writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
-}
-
-void flite_hw_capture_stop(struct fimc_lite *dev)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
- cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN;
- writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
-}
-
-/*
- * Test pattern (color bars) enable/disable. External sensor
- * pixel clock must be active for the test pattern to work.
- */
-void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- if (on)
- cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
- else
- cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-}
-
-static const u32 src_pixfmt_map[8][3] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR,
- FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB,
- FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY,
- FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY,
- FLITE_REG_CIGCTRL_YUV422_1P },
- { V4L2_PIX_FMT_SGRBG8, 0, FLITE_REG_CIGCTRL_RAW8 },
- { V4L2_PIX_FMT_SGRBG10, 0, FLITE_REG_CIGCTRL_RAW10 },
- { V4L2_PIX_FMT_SGRBG12, 0, FLITE_REG_CIGCTRL_RAW12 },
- { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) },
-};
-
-/* Set camera input pixel format and resolution */
-void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
-{
- enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code;
- unsigned int i = ARRAY_SIZE(src_pixfmt_map);
- u32 cfg;
-
- while (i-- >= 0) {
- if (src_pixfmt_map[i][0] == pixelcode)
- break;
- }
-
- if (i == 0 && src_pixfmt_map[i][0] != pixelcode) {
- v4l2_err(&dev->vfd,
- "Unsupported pixel code, falling back to %#08x\n",
- src_pixfmt_map[i][0]);
- }
-
- cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK;
- cfg |= src_pixfmt_map[i][2];
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-
- cfg = readl(dev->regs + FLITE_REG_CISRCSIZE);
- cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK |
- FLITE_REG_CISRCSIZE_SIZE_CAM_MASK);
- cfg |= (f->f_width << 16) | f->f_height;
- cfg |= src_pixfmt_map[i][1];
- writel(cfg, dev->regs + FLITE_REG_CISRCSIZE);
-}
-
-/* Set the camera host input window offsets (cropping) */
-void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f)
-{
- u32 hoff2, voff2;
- u32 cfg;
-
- cfg = readl(dev->regs + FLITE_REG_CIWDOFST);
- cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK;
- cfg |= (f->rect.left << 16) | f->rect.top;
- cfg |= FLITE_REG_CIWDOFST_WINOFSEN;
- writel(cfg, dev->regs + FLITE_REG_CIWDOFST);
-
- hoff2 = f->f_width - f->rect.width - f->rect.left;
- voff2 = f->f_height - f->rect.height - f->rect.top;
-
- cfg = (hoff2 << 16) | voff2;
- writel(cfg, dev->regs + FLITE_REG_CIWDOFST2);
-}
-
-/* Select camera port (A, B) */
-static void flite_hw_set_camera_port(struct fimc_lite *dev, int id)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL);
- if (id == 0)
- cfg &= ~FLITE_REG_CIGENERAL_CAM_B;
- else
- cfg |= FLITE_REG_CIGENERAL_CAM_B;
- writel(cfg, dev->regs + FLITE_REG_CIGENERAL);
-}
-
-/* Select serial or parallel bus, camera port (A,B) and set signals polarity */
-void flite_hw_set_camera_bus(struct fimc_lite *dev,
- struct s5p_fimc_isp_info *s_info)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
- unsigned int flags = s_info->flags;
-
- if (s_info->bus_type != FIMC_MIPI_CSI2) {
- cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI |
- FLITE_REG_CIGCTRL_INVPOLPCLK |
- FLITE_REG_CIGCTRL_INVPOLVSYNC |
- FLITE_REG_CIGCTRL_INVPOLHREF);
-
- if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC;
-
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- cfg |= FLITE_REG_CIGCTRL_INVPOLHREF;
- } else {
- cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI;
- }
-
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-
- flite_hw_set_camera_port(dev, s_info->mux_id);
-}
-
-static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
-{
- static const u32 pixcode[4][2] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
- { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB },
- { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY },
- { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
- };
- u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
- unsigned int i = ARRAY_SIZE(pixcode);
-
- while (i-- >= 0)
- if (pixcode[i][0] == dev->fmt->mbus_code)
- break;
- cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK;
- writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT);
-}
-
-void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f)
-{
- u32 cfg;
-
- /* Maximum output pixel size */
- cfg = readl(dev->regs + FLITE_REG_CIOCAN);
- cfg &= ~FLITE_REG_CIOCAN_MASK;
- cfg = (f->f_height << 16) | f->f_width;
- writel(cfg, dev->regs + FLITE_REG_CIOCAN);
-
- /* DMA offsets */
- cfg = readl(dev->regs + FLITE_REG_CIOOFF);
- cfg &= ~FLITE_REG_CIOOFF_MASK;
- cfg |= (f->rect.top << 16) | f->rect.left;
- writel(cfg, dev->regs + FLITE_REG_CIOOFF);
-}
-
-/* Enable/disable output DMA, set output pixel size and offsets (composition) */
-void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
- bool enable)
-{
- u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
-
- if (!enable) {
- cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
- return;
- }
-
- cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE;
- writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
-
- flite_hw_set_out_order(dev, f);
- flite_hw_set_dma_window(dev, f);
-}
-
-void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
-{
- struct {
- u32 offset;
- const char * const name;
- } registers[] = {
- { 0x00, "CISRCSIZE" },
- { 0x04, "CIGCTRL" },
- { 0x08, "CIIMGCPT" },
- { 0x0c, "CICPTSEQ" },
- { 0x10, "CIWDOFST" },
- { 0x14, "CIWDOFST2" },
- { 0x18, "CIODMAFMT" },
- { 0x20, "CIOCAN" },
- { 0x24, "CIOOFF" },
- { 0x30, "CIOSA" },
- { 0x40, "CISTATUS" },
- { 0x44, "CISTATUS2" },
- { 0xf0, "CITHOLD" },
- { 0xfc, "CIGENERAL" },
- };
- u32 i;
-
- pr_info("--- %s ---\n", label);
- for (i = 0; i < ARRAY_SIZE(registers); i++) {
- u32 cfg = readl(dev->regs + registers[i].offset);
- pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg);
- }
-}
diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c
deleted file mode 100644
index 928900842715..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-lite.c
+++ /dev/null
@@ -1,1596 +0,0 @@
-/*
- * Samsung EXYNOS FIMC-LITE (camera host interface) driver
-*
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
-
-#include <linux/bug.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "fimc-mdevice.h"
-#include "fimc-core.h"
-#include "fimc-lite-reg.h"
-
-static int debug;
-module_param(debug, int, 0644);
-
-static const struct fimc_fmt fimc_lite_formats[] = {
- {
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = { 16 },
- .color = FIMC_FMT_YCBYCR422,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
- }, {
- .name = "YUV 4:2:2 packed, CbYCrY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = { 16 },
- .color = FIMC_FMT_CBYCRY422,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
- }, {
- .name = "YUV 4:2:2 packed, CrYCbY",
- .fourcc = V4L2_PIX_FMT_VYUY,
- .depth = { 16 },
- .color = FIMC_FMT_CRYCBY422,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
- }, {
- .name = "YUV 4:2:2 packed, YCrYCb",
- .fourcc = V4L2_PIX_FMT_YVYU,
- .depth = { 16 },
- .color = FIMC_FMT_YCRYCB422,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
- }, {
- .name = "RAW8 (GRBG)",
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .depth = { 8 },
- .color = FIMC_FMT_RAW8,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
- }, {
- .name = "RAW10 (GRBG)",
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .depth = { 10 },
- .color = FIMC_FMT_RAW10,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
- }, {
- .name = "RAW12 (GRBG)",
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .depth = { 12 },
- .color = FIMC_FMT_RAW12,
- .memplanes = 1,
- .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
- },
-};
-
-/**
- * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code
- * @pixelformat: fourcc to match, ignored if null
- * @mbus_code: media bus code to match, ignored if null
- * @index: index to the fimc_lite_formats array, ignored if negative
- */
-static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
- const u32 *mbus_code, int index)
-{
- const struct fimc_fmt *fmt, *def_fmt = NULL;
- unsigned int i;
- int id = 0;
-
- if (index >= (int)ARRAY_SIZE(fimc_lite_formats))
- return NULL;
-
- for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) {
- fmt = &fimc_lite_formats[i];
- if (pixelformat && fmt->fourcc == *pixelformat)
- return fmt;
- if (mbus_code && fmt->mbus_code == *mbus_code)
- return fmt;
- if (index == id)
- def_fmt = fmt;
- id++;
- }
- return def_fmt;
-}
-
-static int fimc_lite_hw_init(struct fimc_lite *fimc)
-{
- struct fimc_pipeline *pipeline = &fimc->pipeline;
- struct fimc_sensor_info *sensor;
- unsigned long flags;
-
- if (pipeline->subdevs[IDX_SENSOR] == NULL)
- return -ENXIO;
-
- if (fimc->fmt == NULL)
- return -EINVAL;
-
- sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
- spin_lock_irqsave(&fimc->slock, flags);
-
- flite_hw_set_camera_bus(fimc, sensor->pdata);
- flite_hw_set_source_format(fimc, &fimc->inp_frame);
- flite_hw_set_window_offset(fimc, &fimc->inp_frame);
- flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
- flite_hw_set_interrupt_mask(fimc);
- flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
-
- if (debug > 0)
- flite_hw_dump_regs(fimc, __func__);
-
- spin_unlock_irqrestore(&fimc->slock, flags);
- return 0;
-}
-
-/*
- * Reinitialize the driver so it is ready to start the streaming again.
- * Set fimc->state to indicate stream off and the hardware shut down state.
- * If not suspending (@suspend is false), return any buffers to videobuf2.
- * Otherwise put any owned buffers onto the pending buffers queue, so they
- * can be re-spun when the device is being resumed. Also perform FIMC
- * software reset and disable streaming on the whole pipeline if required.
- */
-static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend)
-{
- struct flite_buffer *buf;
- unsigned long flags;
- bool streaming;
-
- spin_lock_irqsave(&fimc->slock, flags);
- streaming = fimc->state & (1 << ST_SENSOR_STREAM);
-
- fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF |
- 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM);
- if (suspend)
- fimc->state |= (1 << ST_FLITE_SUSPENDED);
- else
- fimc->state &= ~(1 << ST_FLITE_PENDING |
- 1 << ST_FLITE_SUSPENDED);
-
- /* Release unused buffers */
- while (!suspend && !list_empty(&fimc->pending_buf_q)) {
- buf = fimc_lite_pending_queue_pop(fimc);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
- /* If suspending put unused buffers onto pending queue */
- while (!list_empty(&fimc->active_buf_q)) {
- buf = fimc_lite_active_queue_pop(fimc);
- if (suspend)
- fimc_lite_pending_queue_add(fimc, buf);
- else
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
-
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- flite_hw_reset(fimc);
-
- if (!streaming)
- return 0;
-
- return fimc_pipeline_s_stream(&fimc->pipeline, 0);
-}
-
-static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend)
-{
- unsigned long flags;
-
- if (!fimc_lite_active(fimc))
- return 0;
-
- spin_lock_irqsave(&fimc->slock, flags);
- set_bit(ST_FLITE_OFF, &fimc->state);
- flite_hw_capture_stop(fimc);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- wait_event_timeout(fimc->irq_queue,
- !test_bit(ST_FLITE_OFF, &fimc->state),
- (2*HZ/10)); /* 200 ms */
-
- return fimc_lite_reinit(fimc, suspend);
-}
-
-/* Must be called with fimc.slock spinlock held. */
-static void fimc_lite_config_update(struct fimc_lite *fimc)
-{
- flite_hw_set_window_offset(fimc, &fimc->inp_frame);
- flite_hw_set_dma_window(fimc, &fimc->out_frame);
- flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
- clear_bit(ST_FLITE_CONFIG, &fimc->state);
-}
-
-static irqreturn_t flite_irq_handler(int irq, void *priv)
-{
- struct fimc_lite *fimc = priv;
- struct flite_buffer *vbuf;
- unsigned long flags;
- struct timeval *tv;
- struct timespec ts;
- u32 intsrc;
-
- spin_lock_irqsave(&fimc->slock, flags);
-
- intsrc = flite_hw_get_interrupt_source(fimc);
- flite_hw_clear_pending_irq(fimc);
-
- if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- goto done;
- }
-
- if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) {
- clear_bit(ST_FLITE_RUN, &fimc->state);
- fimc->events.data_overflow++;
- }
-
- if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) {
- flite_hw_clear_last_capture_end(fimc);
- clear_bit(ST_FLITE_STREAM, &fimc->state);
- wake_up(&fimc->irq_queue);
- }
-
- if (fimc->out_path != FIMC_IO_DMA)
- goto done;
-
- if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
- test_bit(ST_FLITE_RUN, &fimc->state) &&
- !list_empty(&fimc->active_buf_q) &&
- !list_empty(&fimc->pending_buf_q)) {
- vbuf = fimc_lite_active_queue_pop(fimc);
- ktime_get_ts(&ts);
- tv = &vbuf->vb.v4l2_buf.timestamp;
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
- vbuf->vb.v4l2_buf.sequence = fimc->frame_count++;
- vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
-
- vbuf = fimc_lite_pending_queue_pop(fimc);
- flite_hw_set_output_addr(fimc, vbuf->paddr);
- fimc_lite_active_queue_add(fimc, vbuf);
- }
-
- if (test_bit(ST_FLITE_CONFIG, &fimc->state))
- fimc_lite_config_update(fimc);
-
- if (list_empty(&fimc->pending_buf_q)) {
- flite_hw_capture_stop(fimc);
- clear_bit(ST_FLITE_STREAM, &fimc->state);
- }
-done:
- set_bit(ST_FLITE_RUN, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
- return IRQ_HANDLED;
-}
-
-static int start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct fimc_lite *fimc = q->drv_priv;
- int ret;
-
- fimc->frame_count = 0;
-
- ret = fimc_lite_hw_init(fimc);
- if (ret) {
- fimc_lite_reinit(fimc, false);
- return ret;
- }
-
- set_bit(ST_FLITE_PENDING, &fimc->state);
-
- if (!list_empty(&fimc->active_buf_q) &&
- !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
- flite_hw_capture_start(fimc);
-
- if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_s_stream(&fimc->pipeline, 1);
- }
- if (debug > 0)
- flite_hw_dump_regs(fimc, __func__);
-
- return 0;
-}
-
-static int stop_streaming(struct vb2_queue *q)
-{
- struct fimc_lite *fimc = q->drv_priv;
-
- if (!fimc_lite_active(fimc))
- return -EINVAL;
-
- return fimc_lite_stop_capture(fimc, false);
-}
-
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
- unsigned int *num_buffers, unsigned int *num_planes,
- unsigned int sizes[], void *allocators[])
-{
- const struct v4l2_pix_format_mplane *pixm = NULL;
- struct fimc_lite *fimc = vq->drv_priv;
- struct flite_frame *frame = &fimc->out_frame;
- const struct fimc_fmt *fmt = fimc->fmt;
- unsigned long wh;
- int i;
-
- if (pfmt) {
- pixm = &pfmt->fmt.pix_mp;
- fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1);
- wh = pixm->width * pixm->height;
- } else {
- wh = frame->f_width * frame->f_height;
- }
-
- if (fmt == NULL)
- return -EINVAL;
-
- *num_planes = fmt->memplanes;
-
- for (i = 0; i < fmt->memplanes; i++) {
- unsigned int size = (wh * fmt->depth[i]) / 8;
- if (pixm)
- sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
- else
- sizes[i] = size;
- allocators[i] = fimc->alloc_ctx;
- }
-
- return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct fimc_lite *fimc = vq->drv_priv;
- int i;
-
- if (fimc->fmt == NULL)
- return -EINVAL;
-
- for (i = 0; i < fimc->fmt->memplanes; i++) {
- unsigned long size = fimc->payload[i];
-
- if (vb2_plane_size(vb, i) < size) {
- v4l2_err(&fimc->vfd,
- "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 flite_buffer *buf
- = container_of(vb, struct flite_buffer, vb);
- struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long flags;
-
- spin_lock_irqsave(&fimc->slock, flags);
- buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
-
- if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) &&
- !test_bit(ST_FLITE_STREAM, &fimc->state) &&
- list_empty(&fimc->active_buf_q)) {
- flite_hw_set_output_addr(fimc, buf->paddr);
- fimc_lite_active_queue_add(fimc, buf);
- } else {
- fimc_lite_pending_queue_add(fimc, buf);
- }
-
- if (vb2_is_streaming(&fimc->vb_queue) &&
- !list_empty(&fimc->pending_buf_q) &&
- !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
- flite_hw_capture_start(fimc);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
- fimc_pipeline_s_stream(&fimc->pipeline, 1);
- return;
- }
- spin_unlock_irqrestore(&fimc->slock, flags);
-}
-
-static void fimc_lock(struct vb2_queue *vq)
-{
- struct fimc_lite *fimc = vb2_get_drv_priv(vq);
- mutex_lock(&fimc->lock);
-}
-
-static void fimc_unlock(struct vb2_queue *vq)
-{
- struct fimc_lite *fimc = vb2_get_drv_priv(vq);
- mutex_unlock(&fimc->lock);
-}
-
-static const struct vb2_ops fimc_lite_qops = {
- .queue_setup = queue_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
- .start_streaming = start_streaming,
- .stop_streaming = stop_streaming,
-};
-
-static void fimc_lite_clear_event_counters(struct fimc_lite *fimc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&fimc->slock, flags);
- memset(&fimc->events, 0, sizeof(fimc->events));
- spin_unlock_irqrestore(&fimc->slock, flags);
-}
-
-static int fimc_lite_open(struct file *file)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- set_bit(ST_FLITE_IN_USE, &fimc->state);
- ret = pm_runtime_get_sync(&fimc->pdev->dev);
- if (ret < 0)
- goto done;
-
- ret = v4l2_fh_open(file);
- if (ret < 0)
- goto done;
-
- if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) {
- ret = fimc_pipeline_initialize(&fimc->pipeline,
- &fimc->vfd.entity, true);
- if (ret < 0) {
- pm_runtime_put_sync(&fimc->pdev->dev);
- fimc->ref_count--;
- v4l2_fh_release(file);
- clear_bit(ST_FLITE_IN_USE, &fimc->state);
- }
-
- fimc_lite_clear_event_counters(fimc);
- }
-done:
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static int fimc_lite_close(struct file *file)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) {
- clear_bit(ST_FLITE_IN_USE, &fimc->state);
- fimc_lite_stop_capture(fimc, false);
- fimc_pipeline_shutdown(&fimc->pipeline);
- clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
- }
-
- pm_runtime_put(&fimc->pdev->dev);
-
- if (fimc->ref_count == 0)
- vb2_queue_release(&fimc->vb_queue);
-
- ret = v4l2_fh_release(file);
-
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static unsigned int fimc_lite_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return POLL_ERR;
-
- ret = vb2_poll(&fimc->vb_queue, file, wait);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = vb2_mmap(&fimc->vb_queue, vma);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-static const struct v4l2_file_operations fimc_lite_fops = {
- .owner = THIS_MODULE,
- .open = fimc_lite_open,
- .release = fimc_lite_close,
- .poll = fimc_lite_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = fimc_lite_mmap,
-};
-
-/*
- * Format and crop negotiation helpers
- */
-
-static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc,
- u32 *width, u32 *height,
- u32 *code, u32 *fourcc, int pad)
-{
- struct flite_variant *variant = fimc->variant;
- const struct fimc_fmt *fmt;
-
- fmt = fimc_lite_find_format(fourcc, code, 0);
- if (WARN_ON(!fmt))
- return NULL;
-
- if (code)
- *code = fmt->mbus_code;
- if (fourcc)
- *fourcc = fmt->fourcc;
-
- if (pad == FLITE_SD_PAD_SINK) {
- v4l_bound_align_image(width, 8, variant->max_width,
- ffs(variant->out_width_align) - 1,
- height, 0, variant->max_height, 0, 0);
- } else {
- v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width,
- ffs(variant->out_width_align) - 1,
- height, 0, fimc->inp_frame.rect.height,
- 0, 0);
- }
-
- v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n",
- code ? *code : 0, *width, *height);
-
- return fmt;
-}
-
-static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
-{
- struct flite_frame *frame = &fimc->inp_frame;
-
- v4l_bound_align_image(&r->width, 0, frame->f_width, 0,
- &r->height, 0, frame->f_height, 0, 0);
-
- /* Adjust left/top if cropping rectangle got out of bounds */
- r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
- r->left = round_down(r->left, fimc->variant->win_hor_offs_align);
- r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
-
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d",
- r->left, r->top, r->width, r->height,
- frame->f_width, frame->f_height);
-}
-
-static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
-{
- struct flite_frame *frame = &fimc->out_frame;
- struct v4l2_rect *crop_rect = &fimc->inp_frame.rect;
-
- /* Scaling is not supported so we enforce compose rectangle size
- same as size of the sink crop rectangle. */
- r->width = crop_rect->width;
- r->height = crop_rect->height;
-
- /* Adjust left/top if the composing rectangle got out of bounds */
- r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
- r->left = round_down(r->left, fimc->variant->out_hor_offs_align);
- r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
-
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d",
- r->left, r->top, r->width, r->height,
- frame->f_width, frame->f_height);
-}
-
-/*
- * Video node ioctl operations
- */
-static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver));
- cap->bus_info[0] = 0;
- cap->card[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- const struct fimc_fmt *fmt;
-
- if (f->index >= ARRAY_SIZE(fimc_lite_formats))
- return -EINVAL;
-
- fmt = &fimc_lite_formats[f->index];
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
-
- return 0;
-}
-
-static int fimc_lite_g_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
- struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
- struct flite_frame *frame = &fimc->out_frame;
- const struct fimc_fmt *fmt = fimc->fmt;
-
- plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8;
- plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height;
-
- pixm->num_planes = fmt->memplanes;
- pixm->pixelformat = fmt->fourcc;
- pixm->width = frame->f_width;
- pixm->height = frame->f_height;
- pixm->field = V4L2_FIELD_NONE;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
- return 0;
-}
-
-static int fimc_lite_try_fmt(struct fimc_lite *fimc,
- struct v4l2_pix_format_mplane *pixm,
- const struct fimc_fmt **ffmt)
-{
- struct flite_variant *variant = fimc->variant;
- u32 bpl = pixm->plane_fmt[0].bytesperline;
- const struct fimc_fmt *fmt;
-
- fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0);
- if (WARN_ON(fmt == NULL))
- return -EINVAL;
- if (ffmt)
- *ffmt = fmt;
- v4l_bound_align_image(&pixm->width, 8, variant->max_width,
- ffs(variant->out_width_align) - 1,
- &pixm->height, 0, variant->max_height, 0, 0);
-
- if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width))
- pixm->plane_fmt[0].bytesperline = (pixm->width *
- fmt->depth[0]) / 8;
-
- if (pixm->plane_fmt[0].sizeimage == 0)
- pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height *
- fmt->depth[0]) / 8;
- pixm->num_planes = fmt->memplanes;
- pixm->pixelformat = fmt->fourcc;
- pixm->colorspace = V4L2_COLORSPACE_JPEG;
- pixm->field = V4L2_FIELD_NONE;
- return 0;
-}
-
-static int fimc_lite_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL);
-}
-
-static int fimc_lite_s_fmt_mplane(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
- struct fimc_lite *fimc = video_drvdata(file);
- struct flite_frame *frame = &fimc->out_frame;
- const struct fimc_fmt *fmt = NULL;
- int ret;
-
- if (vb2_is_busy(&fimc->vb_queue))
- return -EBUSY;
-
- ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt);
- if (ret < 0)
- return ret;
-
- fimc->fmt = fmt;
- fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8,
- pixm->plane_fmt[0].sizeimage);
- frame->f_width = pixm->width;
- frame->f_height = pixm->height;
-
- return 0;
-}
-
-static int fimc_pipeline_validate(struct fimc_lite *fimc)
-{
- struct v4l2_subdev *sd = &fimc->subdev;
- struct v4l2_subdev_format sink_fmt, src_fmt;
- struct media_pad *pad;
- int ret;
-
- while (1) {
- /* Retrieve format at the sink pad */
- pad = &sd->entity.pads[0];
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
- /* Don't call FIMC subdev operation to avoid nested locking */
- if (sd == &fimc->subdev) {
- struct flite_frame *ff = &fimc->out_frame;
- sink_fmt.format.width = ff->f_width;
- sink_fmt.format.height = ff->f_height;
- sink_fmt.format.code = fimc->fmt->mbus_code;
- } else {
- sink_fmt.pad = pad->index;
- sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL,
- &sink_fmt);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
- }
- /* Retrieve format at the source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
- src_fmt.pad = pad->index;
- src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
-
- if (src_fmt.format.width != sink_fmt.format.width ||
- src_fmt.format.height != sink_fmt.format.height ||
- src_fmt.format.code != sink_fmt.format.code)
- return -EPIPE;
- }
- return 0;
-}
-
-static int fimc_lite_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR];
- struct fimc_pipeline *p = &fimc->pipeline;
- int ret;
-
- if (fimc_lite_active(fimc))
- return -EBUSY;
-
- ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
- if (ret < 0)
- return ret;
-
- ret = fimc_pipeline_validate(fimc);
- if (ret) {
- media_entity_pipeline_stop(&sensor->entity);
- return ret;
- }
-
- return vb2_streamon(&fimc->vb_queue, type);
-}
-
-static int fimc_lite_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
- int ret;
-
- ret = vb2_streamoff(&fimc->vb_queue, type);
- if (ret == 0)
- media_entity_pipeline_stop(&sd->entity);
- return ret;
-}
-
-static int fimc_lite_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- int ret;
-
- reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count);
- ret = vb2_reqbufs(&fimc->vb_queue, reqbufs);
- if (!ret < 0)
- fimc->reqbufs_count = reqbufs->count;
-
- return ret;
-}
-
-static int fimc_lite_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_querybuf(&fimc->vb_queue, buf);
-}
-
-static int fimc_lite_qbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_qbuf(&fimc->vb_queue, buf);
-}
-
-static int fimc_lite_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK);
-}
-
-static int fimc_lite_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *create)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_create_bufs(&fimc->vb_queue, create);
-}
-
-static int fimc_lite_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct fimc_lite *fimc = video_drvdata(file);
-
- return vb2_prepare_buf(&fimc->vb_queue, b);
-}
-
-/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
-static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left || a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
-
- return 1;
-}
-
-static int fimc_lite_g_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- struct flite_frame *f = &fimc->out_frame;
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
-
- switch (sel->target) {
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = f->f_width;
- sel->r.height = f->f_height;
- return 0;
-
- case V4L2_SEL_TGT_COMPOSE:
- sel->r = f->rect;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int fimc_lite_s_selection(struct file *file, void *fh,
- struct v4l2_selection *sel)
-{
- struct fimc_lite *fimc = video_drvdata(file);
- struct flite_frame *f = &fimc->out_frame;
- struct v4l2_rect rect = sel->r;
- unsigned long flags;
-
- if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
- sel->target != V4L2_SEL_TGT_COMPOSE)
- return -EINVAL;
-
- fimc_lite_try_compose(fimc, &rect);
-
- if ((sel->flags & V4L2_SEL_FLAG_LE) &&
- !enclosed_rectangle(&rect, &sel->r))
- return -ERANGE;
-
- if ((sel->flags & V4L2_SEL_FLAG_GE) &&
- !enclosed_rectangle(&sel->r, &rect))
- return -ERANGE;
-
- sel->r = rect;
- spin_lock_irqsave(&fimc->slock, flags);
- f->rect = rect;
- set_bit(ST_FLITE_CONFIG, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
- .vidioc_querycap = fimc_vidioc_querycap_capture,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane,
- .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane,
- .vidioc_g_selection = fimc_lite_g_selection,
- .vidioc_s_selection = fimc_lite_s_selection,
- .vidioc_reqbufs = fimc_lite_reqbufs,
- .vidioc_querybuf = fimc_lite_querybuf,
- .vidioc_prepare_buf = fimc_lite_prepare_buf,
- .vidioc_create_bufs = fimc_lite_create_bufs,
- .vidioc_qbuf = fimc_lite_qbuf,
- .vidioc_dqbuf = fimc_lite_dqbuf,
- .vidioc_streamon = fimc_lite_streamon,
- .vidioc_streamoff = fimc_lite_streamoff,
-};
-
-/* Capture subdev media entity operations */
-static int fimc_lite_link_setup(struct media_entity *entity,
- const struct media_pad *local,
- const struct media_pad *remote, u32 flags)
-{
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- unsigned int remote_ent_type = media_entity_type(remote->entity);
-
- if (WARN_ON(fimc == NULL))
- return 0;
-
- v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
- __func__, local->entity->name, remote->entity->name,
- flags, fimc->source_subdev_grp_id);
-
- switch (local->index) {
- case FIMC_SD_PAD_SINK:
- if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV)
- return -EINVAL;
-
- if (flags & MEDIA_LNK_FL_ENABLED) {
- if (fimc->source_subdev_grp_id != 0)
- return -EBUSY;
- fimc->source_subdev_grp_id = sd->grp_id;
- return 0;
- }
-
- fimc->source_subdev_grp_id = 0;
- break;
-
- case FIMC_SD_PAD_SOURCE:
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- fimc->out_path = FIMC_IO_NONE;
- return 0;
- }
- if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
- fimc->out_path = FIMC_IO_ISP;
- else
- fimc->out_path = FIMC_IO_DMA;
- break;
-
- default:
- v4l2_err(sd, "Invalid pad index\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct media_entity_operations fimc_lite_subdev_media_ops = {
- .link_setup = fimc_lite_link_setup,
-};
-
-static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- const struct fimc_fmt *fmt;
-
- fmt = fimc_lite_find_format(NULL, NULL, code->index);
- if (!fmt)
- return -EINVAL;
- code->code = fmt->mbus_code;
- return 0;
-}
-
-static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct flite_frame *f = &fimc->out_frame;
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- fmt->format = *mf;
- return 0;
- }
- mf->colorspace = V4L2_COLORSPACE_JPEG;
-
- mutex_lock(&fimc->lock);
- mf->code = fimc->fmt->mbus_code;
-
- if (fmt->pad == FLITE_SD_PAD_SINK) {
- /* full camera input frame size */
- mf->width = f->f_width;
- mf->height = f->f_height;
- } else {
- /* crop size */
- mf->width = f->rect.width;
- mf->height = f->rect.height;
- }
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf = &fmt->format;
- struct flite_frame *sink = &fimc->inp_frame;
- const struct fimc_fmt *ffmt;
-
- v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d",
- fmt->pad, mf->code, mf->width, mf->height);
-
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- mutex_lock(&fimc->lock);
-
- if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) ||
- (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) {
- mutex_unlock(&fimc->lock);
- return -EBUSY;
- }
-
- ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height,
- &mf->code, NULL, fmt->pad);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- mf = v4l2_subdev_get_try_format(fh, fmt->pad);
- *mf = fmt->format;
- mutex_unlock(&fimc->lock);
- return 0;
- }
-
- if (fmt->pad == FLITE_SD_PAD_SINK) {
- sink->f_width = mf->width;
- sink->f_height = mf->height;
- fimc->fmt = ffmt;
- /* Set sink crop rectangle */
- sink->rect.width = mf->width;
- sink->rect.height = mf->height;
- sink->rect.left = 0;
- sink->rect.top = 0;
- /* Reset source crop rectangle */
- fimc->out_frame.rect = sink->rect;
- } else {
- /* Allow changing format only on sink pad */
- mf->code = fimc->fmt->mbus_code;
- mf->width = sink->rect.width;
- mf->height = sink->rect.height;
- }
-
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- struct flite_frame *f = &fimc->inp_frame;
-
- if ((sel->target != V4L2_SEL_TGT_CROP &&
- sel->target != V4L2_SEL_TGT_CROP_BOUNDS) ||
- sel->pad != FLITE_SD_PAD_SINK)
- return -EINVAL;
-
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
- sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
- return 0;
- }
-
- mutex_lock(&fimc->lock);
- if (sel->target == V4L2_SEL_TGT_CROP) {
- sel->r = f->rect;
- } else {
- sel->r.left = 0;
- sel->r.top = 0;
- sel->r.width = f->f_width;
- sel->r.height = f->f_height;
- }
- mutex_unlock(&fimc->lock);
-
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
- __func__, f->rect.left, f->rect.top, f->rect.width,
- f->rect.height, f->f_width, f->f_height);
-
- return 0;
-}
-
-static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- struct flite_frame *f = &fimc->inp_frame;
- int ret = 0;
-
- if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != FLITE_SD_PAD_SINK)
- return -EINVAL;
-
- mutex_lock(&fimc->lock);
- fimc_lite_try_crop(fimc, &sel->r);
-
- if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
- *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
- } else {
- unsigned long flags;
- spin_lock_irqsave(&fimc->slock, flags);
- f->rect = sel->r;
- /* Same crop rectangle on the source pad */
- fimc->out_frame.rect = sel->r;
- set_bit(ST_FLITE_CONFIG, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
- }
- mutex_unlock(&fimc->lock);
-
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
- __func__, f->rect.left, f->rect.top, f->rect.width,
- f->rect.height, f->f_width, f->f_height);
-
- return ret;
-}
-
-static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
-
- if (fimc->out_path == FIMC_IO_DMA)
- return -ENOIOCTLCMD;
-
- /* TODO: */
-
- return 0;
-}
-
-static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
-
- if (fimc->out_path == FIMC_IO_DMA)
- return -ENOIOCTLCMD;
-
- /* TODO: */
-
- return 0;
-}
-
-static int fimc_lite_log_status(struct v4l2_subdev *sd)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
-
- flite_hw_dump_regs(fimc, __func__);
- return 0;
-}
-
-static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
- struct vb2_queue *q = &fimc->vb_queue;
- struct video_device *vfd = &fimc->vfd;
- int ret;
-
- memset(vfd, 0, sizeof(*vfd));
-
- fimc->fmt = &fimc_lite_formats[0];
- fimc->out_path = FIMC_IO_DMA;
-
- snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
- fimc->index);
-
- vfd->fops = &fimc_lite_fops;
- vfd->ioctl_ops = &fimc_lite_ioctl_ops;
- vfd->v4l2_dev = sd->v4l2_dev;
- vfd->minor = -1;
- vfd->release = video_device_release_empty;
- vfd->lock = &fimc->lock;
- fimc->ref_count = 0;
- fimc->reqbufs_count = 0;
-
- INIT_LIST_HEAD(&fimc->pending_buf_q);
- INIT_LIST_HEAD(&fimc->active_buf_q);
-
- memset(q, 0, sizeof(*q));
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->ops = &fimc_lite_qops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->buf_struct_size = sizeof(struct flite_buffer);
- q->drv_priv = fimc;
-
- vb2_queue_init(q);
-
- fimc->vd_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0);
- if (ret < 0)
- return ret;
-
- video_set_drvdata(vfd, fimc);
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- media_entity_cleanup(&vfd->entity);
- return ret;
- }
-
- v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n",
- vfd->name, video_device_node_name(vfd));
- return 0;
-}
-
-static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd)
-{
- struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
-
- if (fimc == NULL)
- return;
-
- if (video_is_registered(&fimc->vfd)) {
- video_unregister_device(&fimc->vfd);
- media_entity_cleanup(&fimc->vfd.entity);
- }
-}
-
-static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = {
- .registered = fimc_lite_subdev_registered,
- .unregistered = fimc_lite_subdev_unregistered,
-};
-
-static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = {
- .enum_mbus_code = fimc_lite_subdev_enum_mbus_code,
- .get_selection = fimc_lite_subdev_get_selection,
- .set_selection = fimc_lite_subdev_set_selection,
- .get_fmt = fimc_lite_subdev_get_fmt,
- .set_fmt = fimc_lite_subdev_set_fmt,
-};
-
-static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = {
- .s_stream = fimc_lite_subdev_s_stream,
-};
-
-static const struct v4l2_subdev_core_ops fimc_lite_core_ops = {
- .s_power = fimc_lite_subdev_s_power,
- .log_status = fimc_lite_log_status,
-};
-
-static struct v4l2_subdev_ops fimc_lite_subdev_ops = {
- .core = &fimc_lite_core_ops,
- .video = &fimc_lite_subdev_video_ops,
- .pad = &fimc_lite_subdev_pad_ops,
-};
-
-static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite,
- ctrl_handler);
- set_bit(ST_FLITE_CONFIG, &fimc->state);
- return 0;
-}
-
-static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = {
- .s_ctrl = fimc_lite_s_ctrl,
-};
-
-static const struct v4l2_ctrl_config fimc_lite_ctrl = {
- .ops = &fimc_lite_ctrl_ops,
- .id = V4L2_CTRL_CLASS_USER | 0x1001,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Test Pattern 640x480",
-};
-
-static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
-{
- struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler;
- struct v4l2_subdev *sd = &fimc->subdev;
- int ret;
-
- v4l2_subdev_init(sd, &fimc_lite_subdev_ops);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);
-
- fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
- fimc->subdev_pads, 0);
- if (ret)
- return ret;
-
- v4l2_ctrl_handler_init(handler, 1);
- fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl,
- NULL);
- if (handler->error) {
- media_entity_cleanup(&sd->entity);
- return handler->error;
- }
-
- sd->ctrl_handler = handler;
- sd->internal_ops = &fimc_lite_subdev_internal_ops;
- sd->entity.ops = &fimc_lite_subdev_media_ops;
- v4l2_set_subdevdata(sd, fimc);
-
- return 0;
-}
-
-static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc)
-{
- struct v4l2_subdev *sd = &fimc->subdev;
-
- v4l2_device_unregister_subdev(sd);
- media_entity_cleanup(&sd->entity);
- v4l2_ctrl_handler_free(&fimc->ctrl_handler);
- v4l2_set_subdevdata(sd, NULL);
-}
-
-static void fimc_lite_clk_put(struct fimc_lite *fimc)
-{
- if (IS_ERR_OR_NULL(fimc->clock))
- return;
-
- clk_unprepare(fimc->clock);
- clk_put(fimc->clock);
- fimc->clock = NULL;
-}
-
-static int fimc_lite_clk_get(struct fimc_lite *fimc)
-{
- int ret;
-
- fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME);
- if (IS_ERR(fimc->clock))
- return PTR_ERR(fimc->clock);
-
- ret = clk_prepare(fimc->clock);
- if (ret < 0) {
- clk_put(fimc->clock);
- fimc->clock = NULL;
- }
- return ret;
-}
-
-static int __devinit fimc_lite_probe(struct platform_device *pdev)
-{
- struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev);
- struct fimc_lite *fimc;
- struct resource *res;
- int ret;
-
- fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
- if (!fimc)
- return -ENOMEM;
-
- fimc->index = pdev->id;
- fimc->variant = drv_data->variant[fimc->index];
- fimc->pdev = pdev;
-
- init_waitqueue_head(&fimc->irq_queue);
- spin_lock_init(&fimc->slock);
- mutex_init(&fimc->lock);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (fimc->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "Failed to get IRQ resource\n");
- return -ENXIO;
- }
-
- ret = fimc_lite_clk_get(fimc);
- if (ret)
- return ret;
-
- ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler,
- 0, dev_name(&pdev->dev), fimc);
- if (ret) {
- dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
- goto err_clk;
- }
-
- /* The video node will be created within the subdev's registered() op */
- ret = fimc_lite_create_capture_subdev(fimc);
- if (ret)
- goto err_clk;
-
- platform_set_drvdata(pdev, fimc);
- pm_runtime_enable(&pdev->dev);
- ret = pm_runtime_get_sync(&pdev->dev);
- if (ret < 0)
- goto err_sd;
-
- fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(fimc->alloc_ctx)) {
- ret = PTR_ERR(fimc->alloc_ctx);
- goto err_pm;
- }
- pm_runtime_put(&pdev->dev);
-
- dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n",
- fimc->index);
- return 0;
-err_pm:
- pm_runtime_put(&pdev->dev);
-err_sd:
- fimc_lite_unregister_capture_subdev(fimc);
-err_clk:
- fimc_lite_clk_put(fimc);
- return ret;
-}
-
-static int fimc_lite_runtime_resume(struct device *dev)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
-
- clk_enable(fimc->clock);
- return 0;
-}
-
-static int fimc_lite_runtime_suspend(struct device *dev)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
-
- clk_disable(fimc->clock);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int fimc_lite_resume(struct device *dev)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
- struct flite_buffer *buf;
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&fimc->slock, flags);
- if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
- !test_bit(ST_FLITE_IN_USE, &fimc->state)) {
- spin_unlock_irqrestore(&fimc->slock, flags);
- return 0;
- }
- flite_hw_reset(fimc);
- spin_unlock_irqrestore(&fimc->slock, flags);
-
- if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state))
- return 0;
-
- INIT_LIST_HEAD(&fimc->active_buf_q);
- fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd.entity, false);
- fimc_lite_hw_init(fimc);
- clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
-
- for (i = 0; i < fimc->reqbufs_count; i++) {
- if (list_empty(&fimc->pending_buf_q))
- break;
- buf = fimc_lite_pending_queue_pop(fimc);
- buffer_queue(&buf->vb);
- }
- return 0;
-}
-
-static int fimc_lite_suspend(struct device *dev)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
- bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state);
- int ret;
-
- if (test_and_set_bit(ST_LPM, &fimc->state))
- return 0;
-
- ret = fimc_lite_stop_capture(fimc, suspend);
- if (ret < 0 || !fimc_lite_active(fimc))
- return ret;
-
- return fimc_pipeline_shutdown(&fimc->pipeline);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static int __devexit fimc_lite_remove(struct platform_device *pdev)
-{
- struct fimc_lite *fimc = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
-
- pm_runtime_disable(dev);
- pm_runtime_set_suspended(dev);
- fimc_lite_unregister_capture_subdev(fimc);
- vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
- fimc_lite_clk_put(fimc);
-
- dev_info(dev, "Driver unloaded\n");
- return 0;
-}
-
-static struct flite_variant fimc_lite0_variant_exynos4 = {
- .max_width = 8192,
- .max_height = 8192,
- .out_width_align = 8,
- .win_hor_offs_align = 2,
- .out_hor_offs_align = 8,
-};
-
-/* EXYNOS4212, EXYNOS4412 */
-static struct flite_drvdata fimc_lite_drvdata_exynos4 = {
- .variant = {
- [0] = &fimc_lite0_variant_exynos4,
- [1] = &fimc_lite0_variant_exynos4,
- },
-};
-
-static struct platform_device_id fimc_lite_driver_ids[] = {
- {
- .name = "exynos-fimc-lite",
- .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids);
-
-static const struct dev_pm_ops fimc_lite_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume)
- SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume,
- NULL)
-};
-
-static struct platform_driver fimc_lite_driver = {
- .probe = fimc_lite_probe,
- .remove = __devexit_p(fimc_lite_remove),
- .id_table = fimc_lite_driver_ids,
- .driver = {
- .name = FIMC_LITE_DRV_NAME,
- .owner = THIS_MODULE,
- .pm = &fimc_lite_pm_ops,
- }
-};
-module_platform_driver(fimc_lite_driver);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME);
diff --git a/drivers/media/video/s5p-fimc/fimc-lite.h b/drivers/media/video/s5p-fimc/fimc-lite.h
deleted file mode 100644
index 9944dd36ec7c..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-lite.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- *
- * 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.
- */
-
-#ifndef FIMC_LITE_H_
-#define FIMC_LITE_H_
-
-#include <asm/sizes.h>
-#include <linux/io.h>
-#include <linux/irqreturn.h>
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-#include <linux/videodev2.h>
-
-#include <media/media-entity.h>
-#include <media/videobuf2-core.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-mediabus.h>
-#include <media/s5p_fimc.h>
-
-#include "fimc-core.h"
-
-#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
-#define FLITE_CLK_NAME "flite"
-#define FIMC_LITE_MAX_DEVS 2
-#define FLITE_REQ_BUFS_MIN 2
-
-/* Bit index definitions for struct fimc_lite::state */
-enum {
- ST_FLITE_LPM,
- ST_FLITE_PENDING,
- ST_FLITE_RUN,
- ST_FLITE_STREAM,
- ST_FLITE_SUSPENDED,
- ST_FLITE_OFF,
- ST_FLITE_IN_USE,
- ST_FLITE_CONFIG,
- ST_SENSOR_STREAM,
-};
-
-#define FLITE_SD_PAD_SINK 0
-#define FLITE_SD_PAD_SOURCE 1
-#define FLITE_SD_PADS_NUM 2
-
-struct flite_variant {
- unsigned short max_width;
- unsigned short max_height;
- unsigned short out_width_align;
- unsigned short win_hor_offs_align;
- unsigned short out_hor_offs_align;
-};
-
-struct flite_drvdata {
- struct flite_variant *variant[FIMC_LITE_MAX_DEVS];
-};
-
-#define fimc_lite_get_drvdata(_pdev) \
- ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data)
-
-struct fimc_lite_events {
- unsigned int data_overflow;
-};
-
-#define FLITE_MAX_PLANES 1
-
-/**
- * struct flite_frame - source/target frame properties
- * @f_width: full pixel width
- * @f_height: full pixel height
- * @rect: crop/composition rectangle
- */
-struct flite_frame {
- u16 f_width;
- u16 f_height;
- struct v4l2_rect rect;
-};
-
-/**
- * struct flite_buffer - video buffer structure
- * @vb: vb2 buffer
- * @list: list head for the buffers queue
- * @paddr: precalculated physical address
- */
-struct flite_buffer {
- struct vb2_buffer vb;
- struct list_head list;
- dma_addr_t paddr;
-};
-
-/**
- * struct fimc_lite - fimc lite structure
- * @pdev: pointer to FIMC-LITE platform device
- * @variant: variant information for this IP
- * @v4l2_dev: pointer to top the level v4l2_device
- * @vfd: video device node
- * @fh: v4l2 file handle
- * @alloc_ctx: videobuf2 memory allocator context
- * @subdev: FIMC-LITE subdev
- * @vd_pad: media (sink) pad for the capture video node
- * @subdev_pads: the subdev media pads
- * @ctrl_handler: v4l2 control handler
- * @test_pattern: test pattern controls
- * @index: FIMC-LITE platform device index
- * @pipeline: video capture pipeline data structure
- * @slock: spinlock protecting this data structure and the hw registers
- * @lock: mutex serializing video device and the subdev operations
- * @clock: FIMC-LITE gate clock
- * @regs: memory mapped io registers
- * @irq_queue: interrupt handler waitqueue
- * @fmt: pointer to color format description structure
- * @payload: image size in bytes (w x h x bpp)
- * @inp_frame: camera input frame structure
- * @out_frame: DMA output frame structure
- * @out_path: output data path (DMA or FIFO)
- * @source_subdev_grp_id: source subdev group id
- * @state: driver state flags
- * @pending_buf_q: pending buffers queue head
- * @active_buf_q: the queue head of buffers scheduled in hardware
- * @vb_queue: vb2 buffers queue
- * @active_buf_count: number of video buffers scheduled in hardware
- * @frame_count: the captured frames counter
- * @reqbufs_count: the number of buffers requested with REQBUFS ioctl
- * @ref_count: driver's private reference counter
- */
-struct fimc_lite {
- struct platform_device *pdev;
- struct flite_variant *variant;
- struct v4l2_device *v4l2_dev;
- struct video_device vfd;
- struct v4l2_fh fh;
- struct vb2_alloc_ctx *alloc_ctx;
- struct v4l2_subdev subdev;
- struct media_pad vd_pad;
- struct media_pad subdev_pads[FLITE_SD_PADS_NUM];
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_ctrl *test_pattern;
- u32 index;
- struct fimc_pipeline pipeline;
-
- struct mutex lock;
- spinlock_t slock;
-
- struct clk *clock;
- void __iomem *regs;
- wait_queue_head_t irq_queue;
-
- const struct fimc_fmt *fmt;
- unsigned long payload[FLITE_MAX_PLANES];
- struct flite_frame inp_frame;
- struct flite_frame out_frame;
- enum fimc_datapath out_path;
- unsigned int source_subdev_grp_id;
-
- unsigned long state;
- struct list_head pending_buf_q;
- struct list_head active_buf_q;
- struct vb2_queue vb_queue;
- unsigned int frame_count;
- unsigned int reqbufs_count;
- int ref_count;
-
- struct fimc_lite_events events;
-};
-
-static inline bool fimc_lite_active(struct fimc_lite *fimc)
-{
- unsigned long flags;
- bool ret;
-
- spin_lock_irqsave(&fimc->slock, flags);
- ret = fimc->state & (1 << ST_FLITE_RUN) ||
- fimc->state & (1 << ST_FLITE_PENDING);
- spin_unlock_irqrestore(&fimc->slock, flags);
- return ret;
-}
-
-static inline void fimc_lite_active_queue_add(struct fimc_lite *dev,
- struct flite_buffer *buf)
-{
- list_add_tail(&buf->list, &dev->active_buf_q);
-}
-
-static inline struct flite_buffer *fimc_lite_active_queue_pop(
- struct fimc_lite *dev)
-{
- struct flite_buffer *buf = list_entry(dev->active_buf_q.next,
- struct flite_buffer, list);
- list_del(&buf->list);
- return buf;
-}
-
-static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev,
- struct flite_buffer *buf)
-{
- list_add_tail(&buf->list, &dev->pending_buf_q);
-}
-
-static inline struct flite_buffer *fimc_lite_pending_queue_pop(
- struct fimc_lite *dev)
-{
- struct flite_buffer *buf = list_entry(dev->pending_buf_q.next,
- struct flite_buffer, list);
- list_del(&buf->list);
- return buf;
-}
-
-#endif /* FIMC_LITE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c
deleted file mode 100644
index 293e602677f0..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-m2m.c
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/bug.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/list.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "fimc-core.h"
-#include "fimc-reg.h"
-#include "fimc-mdevice.h"
-
-
-static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
-{
- if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return FMT_FLAGS_M2M_IN;
- else
- return FMT_FLAGS_M2M_OUT;
-}
-
-void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
-{
- struct vb2_buffer *src_vb, *dst_vb;
-
- if (!ctx || !ctx->m2m_ctx)
- return;
-
- 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, vb_state);
- v4l2_m2m_buf_done(dst_vb, vb_state);
- v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
- ctx->m2m_ctx);
- }
-}
-
-/* Complete the transaction which has been scheduled for execution. */
-static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (!fimc_m2m_pending(fimc))
- return 0;
-
- fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
-
- ret = wait_event_timeout(fimc->irq_queue,
- !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
- FIMC_SHUTDOWN_TIMEOUT);
-
- return ret == 0 ? -ETIMEDOUT : ret;
-}
-
-static int start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct fimc_ctx *ctx = q->drv_priv;
- int ret;
-
- ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
- return ret > 0 ? 0 : ret;
-}
-
-static int stop_streaming(struct vb2_queue *q)
-{
- struct fimc_ctx *ctx = q->drv_priv;
- int ret;
-
- ret = fimc_m2m_shutdown(ctx);
- if (ret == -ETIMEDOUT)
- fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
-
- pm_runtime_put(&ctx->fimc_dev->pdev->dev);
- return 0;
-}
-
-static void fimc_device_run(void *priv)
-{
- struct vb2_buffer *vb = NULL;
- struct fimc_ctx *ctx = priv;
- struct fimc_frame *sf, *df;
- struct fimc_dev *fimc;
- unsigned long flags;
- u32 ret;
-
- if (WARN(!ctx, "Null context\n"))
- return;
-
- fimc = ctx->fimc_dev;
- spin_lock_irqsave(&fimc->slock, flags);
-
- set_bit(ST_M2M_PEND, &fimc->state);
- sf = &ctx->s_frame;
- df = &ctx->d_frame;
-
- if (ctx->state & FIMC_PARAMS) {
- /* Prepare the DMA offsets for scaler */
- fimc_prepare_dma_offset(ctx, sf);
- fimc_prepare_dma_offset(ctx, df);
- }
-
- vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr);
- if (ret)
- goto dma_unlock;
-
- vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, df, &df->paddr);
- if (ret)
- goto dma_unlock;
-
- /* Reconfigure hardware if the context has changed. */
- if (fimc->m2m.ctx != ctx) {
- ctx->state |= FIMC_PARAMS;
- fimc->m2m.ctx = ctx;
- }
-
- if (ctx->state & FIMC_PARAMS) {
- fimc_set_yuv_order(ctx);
- fimc_hw_set_input_path(ctx);
- fimc_hw_set_in_dma(ctx);
- ret = fimc_set_scaler_info(ctx);
- if (ret)
- goto dma_unlock;
- 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_out_dma(ctx);
- if (fimc->variant->has_alpha)
- fimc_hw_set_rgb_alpha(ctx);
- fimc_hw_set_output_path(ctx);
- }
- fimc_hw_set_input_addr(fimc, &sf->paddr);
- fimc_hw_set_output_addr(fimc, &df->paddr, -1);
-
- fimc_activate_capture(ctx);
- ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
- FIMC_SRC_FMT | FIMC_DST_FMT);
- fimc_hw_activate_input_dma(fimc, true);
-
-dma_unlock:
- spin_unlock_irqrestore(&fimc->slock, flags);
-}
-
-static void fimc_job_abort(void *priv)
-{
- fimc_m2m_shutdown(priv);
-}
-
-static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *num_buffers, unsigned int *num_planes,
- unsigned int sizes[], void *allocators[])
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- struct fimc_frame *f;
- int i;
-
- 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)
- return -EINVAL;
-
- *num_planes = f->fmt->memplanes;
- for (i = 0; i < f->fmt->memplanes; i++) {
- sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
- allocators[i] = ctx->fimc_dev->alloc_ctx;
- }
- return 0;
-}
-
-static int fimc_buf_prepare(struct vb2_buffer *vb)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct fimc_frame *frame;
- int i;
-
- frame = ctx_get_frame(ctx, vb->vb2_queue->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- 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 vb2_buffer *vb)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
-
- if (ctx->m2m_ctx)
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-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_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,
- .start_streaming = start_streaming,
-};
-
-/*
- * V4L2 ioctl handlers
- */
-static int fimc_m2m_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
-
- strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
- return 0;
-}
-
-static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct fimc_fmt *fmt;
-
- fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
- f->index);
- if (!fmt)
- return -EINVAL;
-
- strncpy(f->description, fmt->name, sizeof(f->description) - 1);
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
-
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- return fimc_fill_format(frame, f);
-}
-
-static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_variant *variant = fimc->variant;
- struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct fimc_fmt *fmt;
- u32 max_w, mod_x, mod_y;
-
- if (!IS_M2M(f->type))
- return -EINVAL;
-
- dbg("w: %d, h: %d", pix->width, pix->height);
-
- fmt = fimc_find_format(&pix->pixelformat, NULL,
- get_m2m_fmt_flags(f->type), 0);
- if (WARN(fmt == NULL, "Pixel format lookup failed"))
- return -EINVAL;
-
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_NONE;
- else if (pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- max_w = variant->pix_limit->scaler_dis_w;
- mod_x = ffs(variant->min_inp_pixsize) - 1;
- } else {
- max_w = variant->pix_limit->out_rot_dis_w;
- mod_x = ffs(variant->min_out_pixsize) - 1;
- }
-
- if (tiled_fmt(fmt)) {
- mod_x = 6; /* 64 x 32 pixels tile */
- mod_y = 5;
- } else {
- if (variant->min_vsize_align == 1)
- mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
- else
- mod_y = ffs(variant->min_vsize_align) - 1;
- }
-
- v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
- &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
-
- fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
- return 0;
-}
-
-static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return fimc_try_fmt_mplane(ctx, f);
-}
-
-static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct vb2_queue *vq;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pix;
- int i, ret = 0;
-
- ret = fimc_try_fmt_mplane(ctx, f);
- if (ret)
- return ret;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
-
- if (vb2_is_busy(vq)) {
- v4l2_err(&fimc->m2m.vfd, "queue (%d) busy\n", f->type);
- return -EBUSY;
- }
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- frame = &ctx->s_frame;
- else
- frame = &ctx->d_frame;
-
- pix = &f->fmt.pix_mp;
- frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
- get_m2m_fmt_flags(f->type), 0);
- if (!frame->fmt)
- return -EINVAL;
-
- /* Update RGB Alpha control state and value range */
- fimc_alpha_ctrl_update(ctx);
-
- for (i = 0; i < frame->fmt->colplanes; i++) {
- frame->payload[i] =
- (pix->width * pix->height * frame->fmt->depth[i]) / 8;
- }
-
- fimc_fill_frame(frame, f);
-
- ctx->scaler.enabled = 1;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
- else
- fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
-
- dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
-
- return 0;
-}
-
-static int fimc_m2m_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int fimc_m2m_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_qbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_dqbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- /* The source and target color format need to be set */
- if (V4L2_TYPE_IS_OUTPUT(type)) {
- if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
- return -EINVAL;
- } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
- return -EINVAL;
- }
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame;
-
- frame = ctx_get_frame(ctx, cr->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- cr->bounds.left = 0;
- cr->bounds.top = 0;
- cr->bounds.width = frame->o_width;
- cr->bounds.height = frame->o_height;
- cr->defrect = cr->bounds;
-
- return 0;
-}
-
-static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame;
-
- frame = ctx_get_frame(ctx, cr->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- cr->c.left = frame->offs_h;
- cr->c.top = frame->offs_v;
- cr->c.width = frame->width;
- cr->c.height = frame->height;
-
- return 0;
-}
-
-static int fimc_m2m_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, depth = 0;
- int i;
-
- if (cr->c.top < 0 || cr->c.left < 0) {
- v4l2_err(&fimc->m2m.vfd,
- "doesn't support negative values for top & left\n");
- return -EINVAL;
- }
- if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f = &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- f = &ctx->s_frame;
- else
- return -EINVAL;
-
- min_size = (f == &ctx->s_frame) ?
- fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
-
- /* Get pixel alignment constraints. */
- if (fimc->variant->min_vsize_align == 1)
- halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
- else
- halign = ffs(fimc->variant->min_vsize_align) - 1;
-
- 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(depth, 8)));
-
- /* adjust left/top if cropping rectangle is out of bounds */
- if (cr->c.left + cr->c.width > f->o_width)
- cr->c.left = f->o_width - cr->c.width;
- if (cr->c.top + cr->c.height > f->o_height)
- cr->c.top = f->o_height - cr->c.height;
-
- cr->c.left = round_down(cr->c.left, min_size);
- cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
-
- dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
- cr->c.left, cr->c.top, cr->c.width, cr->c.height,
- f->f_width, f->f_height);
-
- return 0;
-}
-
-static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_frame *f;
- int ret;
-
- ret = fimc_m2m_try_crop(ctx, cr);
- if (ret)
- return ret;
-
- f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
- &ctx->s_frame : &ctx->d_frame;
-
- /* Check to see if scaling ratio is within supported range */
- if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
- if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = fimc_check_scaler_ratio(ctx, cr->c.width,
- cr->c.height, ctx->d_frame.width,
- ctx->d_frame.height, ctx->rotation);
- } else {
- ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
- ctx->s_frame.height, cr->c.width,
- cr->c.height, ctx->rotation);
- }
- if (ret) {
- v4l2_err(&fimc->m2m.vfd, "Out of scaler range\n");
- return -EINVAL;
- }
- }
-
- f->offs_h = cr->c.left;
- f->offs_v = cr->c.top;
- f->width = cr->c.width;
- f->height = cr->c.height;
-
- fimc_ctx_state_set(FIMC_PARAMS, ctx);
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
- .vidioc_querycap = fimc_m2m_querycap,
- .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
- .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
- .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
- .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
- .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
- .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
- .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,
- .vidioc_qbuf = fimc_m2m_qbuf,
- .vidioc_dqbuf = fimc_m2m_dqbuf,
- .vidioc_streamon = fimc_m2m_streamon,
- .vidioc_streamoff = fimc_m2m_streamoff,
- .vidioc_g_crop = fimc_m2m_g_crop,
- .vidioc_s_crop = fimc_m2m_s_crop,
- .vidioc_cropcap = fimc_m2m_cropcap
-
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct fimc_ctx *ctx = priv;
- 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);
-
- 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;
- int ret = -EBUSY;
-
- dbg("pid: %d, state: 0x%lx, refcnt: %d",
- task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
- /*
- * Return if the corresponding video capture node
- * is already opened.
- */
- if (fimc->vid_cap.refcnt > 0)
- goto unlock;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx) {
- ret = -ENOMEM;
- goto unlock;
- }
- v4l2_fh_init(&ctx->fh, &fimc->m2m.vfd);
- ctx->fimc_dev = fimc;
-
- /* Default color format */
- ctx->s_frame.fmt = fimc_get_format(0);
- ctx->d_frame.fmt = fimc_get_format(0);
-
- ret = fimc_ctrls_create(ctx);
- if (ret)
- goto error_fh;
-
- /* Use separate control handler per file handle */
- ctx->fh.ctrl_handler = &ctx->ctrls.handler;
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
-
- /* Setup the device context for memory-to-memory mode */
- ctx->state = FIMC_CTX_M2M;
- ctx->flags = 0;
- ctx->in_path = FIMC_IO_DMA;
- ctx->out_path = FIMC_IO_DMA;
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
- goto error_c;
- }
-
- if (fimc->m2m.refcnt++ == 0)
- set_bit(ST_M2M_RUN, &fimc->state);
-
- mutex_unlock(&fimc->lock);
- return 0;
-
-error_c:
- fimc_ctrls_delete(ctx);
-error_fh:
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
-unlock:
- mutex_unlock(&fimc->lock);
- return ret;
-}
-
-static int fimc_m2m_release(struct file *file)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
-
- dbg("pid: %d, state: 0x%lx, refcnt= %d",
- task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- fimc_ctrls_delete(ctx);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
-
- if (--fimc->m2m.refcnt <= 0)
- clear_bit(ST_M2M_RUN, &fimc->state);
- kfree(ctx);
-
- mutex_unlock(&fimc->lock);
- return 0;
-}
-
-static unsigned int fimc_m2m_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-
-static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (mutex_lock_interruptible(&fimc->lock))
- return -ERESTARTSYS;
-
- ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
- mutex_unlock(&fimc->lock);
-
- return ret;
-}
-
-static const struct v4l2_file_operations fimc_m2m_fops = {
- .owner = THIS_MODULE,
- .open = fimc_m2m_open,
- .release = fimc_m2m_release,
- .poll = fimc_m2m_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = fimc_m2m_mmap,
-};
-
-static struct v4l2_m2m_ops m2m_ops = {
- .device_run = fimc_device_run,
- .job_abort = fimc_job_abort,
-};
-
-int fimc_register_m2m_device(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev)
-{
- struct video_device *vfd = &fimc->m2m.vfd;
- int ret;
-
- fimc->v4l2_dev = v4l2_dev;
-
- memset(vfd, 0, sizeof(*vfd));
- vfd->fops = &fimc_m2m_fops;
- vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
- vfd->v4l2_dev = v4l2_dev;
- vfd->minor = -1;
- vfd->release = video_device_release_empty;
- vfd->lock = &fimc->lock;
-
- snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
- video_set_drvdata(vfd, fimc);
-
- fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(fimc->m2m.m2m_dev)) {
- v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
- return PTR_ERR(fimc->m2m.m2m_dev);
- }
-
- ret = media_entity_init(&vfd->entity, 0, NULL, 0);
- if (ret)
- goto err_me;
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
- if (ret)
- goto err_vd;
-
- v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
- vfd->name, video_device_node_name(vfd));
- return 0;
-
-err_vd:
- media_entity_cleanup(&vfd->entity);
-err_me:
- v4l2_m2m_release(fimc->m2m.m2m_dev);
- return ret;
-}
-
-void fimc_unregister_m2m_device(struct fimc_dev *fimc)
-{
- if (!fimc)
- return;
-
- if (fimc->m2m.m2m_dev)
- v4l2_m2m_release(fimc->m2m.m2m_dev);
-
- if (video_is_registered(&fimc->m2m.vfd)) {
- video_unregister_device(&fimc->m2m.vfd);
- media_entity_cleanup(&fimc->m2m.vfd.entity);
- }
-}
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
deleted file mode 100644
index e2aa8d99d858..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.c
+++ /dev/null
@@ -1,1040 +0,0 @@
-/*
- * S5P/EXYNOS4 SoC series camera host interface media device driver
- *
- * Copyright (C) 2011 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.
- */
-
-#include <linux/bug.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <media/v4l2-ctrls.h>
-#include <media/media-device.h>
-
-#include "fimc-core.h"
-#include "fimc-lite.h"
-#include "fimc-mdevice.h"
-#include "mipi-csis.h"
-
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info,
- bool on);
-/**
- * fimc_pipeline_prepare - update pipeline information with subdevice pointers
- * @fimc: fimc device terminating the pipeline
- *
- * Caller holds the graph mutex.
- */
-void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me)
-{
- struct media_pad *pad = &me->pads[0];
- struct v4l2_subdev *sd;
- int i;
-
- for (i = 0; i < IDX_MAX; i++)
- p->subdevs[i] = NULL;
-
- while (1) {
- if (!(pad->flags & MEDIA_PAD_FL_SINK))
- break;
-
- /* source pad */
- pad = media_entity_remote_source(pad);
- if (pad == NULL ||
- media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- break;
-
- sd = media_entity_to_v4l2_subdev(pad->entity);
-
- switch (sd->grp_id) {
- case SENSOR_GROUP_ID:
- p->subdevs[IDX_SENSOR] = sd;
- break;
- case CSIS_GROUP_ID:
- p->subdevs[IDX_CSIS] = sd;
- break;
- case FLITE_GROUP_ID:
- p->subdevs[IDX_FLITE] = sd;
- break;
- case FIMC_GROUP_ID:
- /* No need to control FIMC subdev through subdev ops */
- break;
- default:
- pr_warn("%s: Unknown subdev grp_id: %#x\n",
- __func__, sd->grp_id);
- }
- /* sink pad */
- pad = &sd->entity.pads[0];
- }
-}
-
-/**
- * __subdev_set_power - change power state of a single subdev
- * @sd: subdevice to change power state for
- * @on: 1 to enable power or 0 to disable
- *
- * Return result of s_power subdev operation or -ENXIO if sd argument
- * is NULL. Return 0 if the subdevice does not implement s_power.
- */
-static int __subdev_set_power(struct v4l2_subdev *sd, int on)
-{
- int *use_count;
- int ret;
-
- if (sd == NULL)
- return -ENXIO;
-
- use_count = &sd->entity.use_count;
- if (on && (*use_count)++ > 0)
- return 0;
- else if (!on && (*use_count == 0 || --(*use_count) > 0))
- return 0;
- ret = v4l2_subdev_call(sd, core, s_power, on);
-
- return ret != -ENOIOCTLCMD ? ret : 0;
-}
-
-/**
- * fimc_pipeline_s_power - change power state of all pipeline subdevs
- * @fimc: fimc device terminating the pipeline
- * @state: true to power on, false to power off
- *
- * Needs to be called with the graph mutex held.
- */
-int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state)
-{
- unsigned int i;
- int ret;
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -ENXIO;
-
- for (i = 0; i < IDX_MAX; i++) {
- unsigned int idx = state ? (IDX_MAX - 1) - i : i;
-
- ret = __subdev_set_power(p->subdevs[idx], state);
- if (ret < 0 && ret != -ENXIO)
- return ret;
- }
-
- return 0;
-}
-
-/**
- * __fimc_pipeline_initialize - update the pipeline information, enable power
- * of all pipeline subdevs and the sensor clock
- * @me: media entity to start graph walk with
- * @prep: true to acquire sensor (and csis) subdevs
- *
- * This function must be called with the graph mutex held.
- */
-static int __fimc_pipeline_initialize(struct fimc_pipeline *p,
- struct media_entity *me, bool prep)
-{
- int ret;
-
- if (prep)
- fimc_pipeline_prepare(p, me);
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -EINVAL;
-
- ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true);
- if (ret)
- return ret;
-
- return fimc_pipeline_s_power(p, 1);
-}
-
-int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me,
- bool prep)
-{
- int ret;
-
- mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_initialize(p, me, prep);
- mutex_unlock(&me->parent->graph_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(fimc_pipeline_initialize);
-
-/**
- * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
- * @fimc: fimc device terminating the pipeline
- *
- * Disable power of all subdevs in the pipeline and turn off the external
- * sensor clock.
- * Called with the graph mutex held.
- */
-static int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
-{
- int ret = 0;
-
- if (p->subdevs[IDX_SENSOR]) {
- ret = fimc_pipeline_s_power(p, 0);
- fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false);
- }
- return ret == -ENXIO ? 0 : ret;
-}
-
-int fimc_pipeline_shutdown(struct fimc_pipeline *p)
-{
- struct media_entity *me;
- int ret;
-
- if (!p || !p->subdevs[IDX_SENSOR])
- return -EINVAL;
-
- me = &p->subdevs[IDX_SENSOR]->entity;
- mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_shutdown(p);
- mutex_unlock(&me->parent->graph_mutex);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(fimc_pipeline_shutdown);
-
-/**
- * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
- * @pipeline: video pipeline structure
- * @on: passed as the s_stream call argument
- */
-int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
-{
- int i, ret;
-
- if (p->subdevs[IDX_SENSOR] == NULL)
- return -ENODEV;
-
- for (i = 0; i < IDX_MAX; i++) {
- unsigned int idx = on ? (IDX_MAX - 1) - i : i;
-
- ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
-
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
- }
-
- return 0;
-
-}
-EXPORT_SYMBOL_GPL(fimc_pipeline_s_stream);
-
-/*
- * Sensor subdevice helper functions
- */
-static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info)
-{
- struct i2c_adapter *adapter;
- struct v4l2_subdev *sd = NULL;
-
- if (!s_info || !fmd)
- return NULL;
-
- adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
- if (!adapter) {
- v4l2_warn(&fmd->v4l2_dev,
- "Failed to get I2C adapter %d, deferring probe\n",
- s_info->pdata->i2c_bus_num);
- return ERR_PTR(-EPROBE_DEFER);
- }
- sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
- s_info->pdata->board_info, NULL);
- if (IS_ERR_OR_NULL(sd)) {
- i2c_put_adapter(adapter);
- v4l2_warn(&fmd->v4l2_dev,
- "Failed to acquire subdev %s, deferring probe\n",
- s_info->pdata->board_info->type);
- return ERR_PTR(-EPROBE_DEFER);
- }
- v4l2_set_subdev_hostdata(sd, s_info);
- sd->grp_id = SENSOR_GROUP_ID;
-
- v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice %s\n",
- s_info->pdata->board_info->type);
- return sd;
-}
-
-static void fimc_md_unregister_sensor(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct i2c_adapter *adapter;
-
- if (!client)
- return;
- v4l2_device_unregister_subdev(sd);
- adapter = client->adapter;
- i2c_unregister_device(client);
- if (adapter)
- i2c_put_adapter(adapter);
-}
-
-static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
-{
- struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
- struct fimc_dev *fd = NULL;
- int num_clients, ret, i;
-
- /*
- * Runtime resume one of the FIMC entities to make sure
- * the sclk_cam clocks are not globally disabled.
- */
- for (i = 0; !fd && i < ARRAY_SIZE(fmd->fimc); i++)
- if (fmd->fimc[i])
- fd = fmd->fimc[i];
- if (!fd)
- return -ENXIO;
- ret = pm_runtime_get_sync(&fd->pdev->dev);
- if (ret < 0)
- return ret;
-
- WARN_ON(pdata->num_clients > ARRAY_SIZE(fmd->sensor));
- num_clients = min_t(u32, pdata->num_clients, ARRAY_SIZE(fmd->sensor));
-
- fmd->num_sensors = num_clients;
- for (i = 0; i < num_clients; i++) {
- struct v4l2_subdev *sd;
-
- fmd->sensor[i].pdata = &pdata->isp_info[i];
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
- if (ret)
- break;
- sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
- ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
-
- if (!IS_ERR(sd)) {
- fmd->sensor[i].subdev = sd;
- } else {
- fmd->sensor[i].subdev = NULL;
- ret = PTR_ERR(sd);
- break;
- }
- if (ret)
- break;
- }
- pm_runtime_put(&fd->pdev->dev);
- return ret;
-}
-
-/*
- * MIPI CSIS and FIMC platform devices registration.
- */
-static int fimc_register_callback(struct device *dev, void *p)
-{
- struct fimc_dev *fimc = dev_get_drvdata(dev);
- struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
- struct fimc_md *fmd = p;
- int ret = 0;
-
- if (!fimc || !fimc->pdev)
- return 0;
-
- if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
- return 0;
-
- fmd->fimc[fimc->pdev->id] = fimc;
- sd->grp_id = FIMC_GROUP_ID;
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- if (ret) {
- v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
- fimc->id, ret);
- }
-
- return ret;
-}
-
-static int fimc_lite_register_callback(struct device *dev, void *p)
-{
- struct fimc_lite *fimc = dev_get_drvdata(dev);
- struct v4l2_subdev *sd = &fimc->subdev;
- struct fimc_md *fmd = p;
- int ret;
-
- if (fimc == NULL)
- return 0;
-
- if (fimc->index >= FIMC_LITE_MAX_DEVS)
- return 0;
-
- fmd->fimc_lite[fimc->index] = fimc;
- sd->grp_id = FLITE_GROUP_ID;
-
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- if (ret) {
- v4l2_err(&fmd->v4l2_dev,
- "Failed to register FIMC-LITE.%d (%d)\n",
- fimc->index, ret);
- }
- return ret;
-}
-
-static int csis_register_callback(struct device *dev, void *p)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct platform_device *pdev;
- struct fimc_md *fmd = p;
- int id, ret;
-
- if (!sd)
- return 0;
- pdev = v4l2_get_subdevdata(sd);
- if (!pdev || pdev->id < 0 || pdev->id >= CSIS_MAX_ENTITIES)
- return 0;
- v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name);
-
- id = pdev->id < 0 ? 0 : pdev->id;
- fmd->csis[id].sd = sd;
- sd->grp_id = CSIS_GROUP_ID;
- ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
- if (ret)
- v4l2_err(&fmd->v4l2_dev,
- "Failed to register CSIS subdevice: %d\n", ret);
- return ret;
-}
-
-/**
- * fimc_md_register_platform_entities - register FIMC and CSIS media entities
- */
-static int fimc_md_register_platform_entities(struct fimc_md *fmd)
-{
- struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
- struct device_driver *driver;
- int ret, i;
-
- driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
- if (!driver) {
- v4l2_warn(&fmd->v4l2_dev,
- "%s driver not found, deffering probe\n",
- FIMC_MODULE_NAME);
- return -EPROBE_DEFER;
- }
-
- ret = driver_for_each_device(driver, NULL, fmd,
- fimc_register_callback);
- if (ret)
- return ret;
-
- driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
- if (driver && try_module_get(driver->owner)) {
- ret = driver_for_each_device(driver, NULL, fmd,
- fimc_lite_register_callback);
- if (ret)
- return ret;
- module_put(driver->owner);
- }
- /*
- * Check if there is any sensor on the MIPI-CSI2 bus and
- * if not skip the s5p-csis module loading.
- */
- if (pdata == NULL)
- return 0;
- for (i = 0; i < pdata->num_clients; i++) {
- if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
- ret = 1;
- break;
- }
- }
- if (!ret)
- return 0;
-
- driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
- if (!driver || !try_module_get(driver->owner)) {
- v4l2_warn(&fmd->v4l2_dev,
- "%s driver not found, deffering probe\n",
- CSIS_DRIVER_NAME);
- return -EPROBE_DEFER;
- }
-
- return driver_for_each_device(driver, NULL, fmd,
- csis_register_callback);
-}
-
-static void fimc_md_unregister_entities(struct fimc_md *fmd)
-{
- int i;
-
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (fmd->fimc[i] == NULL)
- continue;
- v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
- fmd->fimc[i] = NULL;
- }
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- if (fmd->fimc_lite[i] == NULL)
- continue;
- v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
- fmd->fimc_lite[i] = NULL;
- }
- for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
- if (fmd->csis[i].sd == NULL)
- continue;
- v4l2_device_unregister_subdev(fmd->csis[i].sd);
- module_put(fmd->csis[i].sd->owner);
- fmd->csis[i].sd = NULL;
- }
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
- fimc_md_unregister_sensor(fmd->sensor[i].subdev);
- fmd->sensor[i].subdev = NULL;
- }
-}
-
-/**
- * __fimc_md_create_fimc_links - create links to all FIMC entities
- * @fmd: fimc media device
- * @source: the source entity to create links to all fimc entities from
- * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null
- * @pad: the source entity pad index
- * @link_mask: bitmask of the fimc devices for which link should be enabled
- */
-static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
- struct media_entity *source,
- struct v4l2_subdev *sensor,
- int pad, int link_mask)
-{
- struct fimc_sensor_info *s_info;
- struct media_entity *sink;
- unsigned int flags = 0;
- int ret, i;
-
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (!fmd->fimc[i])
- continue;
- /*
- * Some FIMC variants are not fitted with camera capture
- * interface. Skip creating a link from sensor for those.
- */
- if (!fmd->fimc[i]->variant->has_cam_if)
- continue;
-
- flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0;
-
- sink = &fmd->fimc[i]->vid_cap.subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
- FIMC_SD_PAD_SINK, flags);
- if (ret)
- return ret;
-
- /* Notify FIMC capture subdev entity */
- ret = media_entity_call(sink, link_setup, &sink->pads[0],
- &source->pads[pad], flags);
- if (ret)
- break;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
- source->name, flags ? '=' : '-', sink->name);
-
- if (flags == 0 || sensor == NULL)
- continue;
- s_info = v4l2_get_subdev_hostdata(sensor);
- if (!WARN_ON(s_info == NULL)) {
- unsigned long irq_flags;
- spin_lock_irqsave(&fmd->slock, irq_flags);
- s_info->host = fmd->fimc[i];
- spin_unlock_irqrestore(&fmd->slock, irq_flags);
- }
- }
-
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- if (!fmd->fimc_lite[i])
- continue;
-
- if (link_mask & (1 << (i + FIMC_MAX_DEVS)))
- flags = MEDIA_LNK_FL_ENABLED;
- else
- flags = 0;
-
- sink = &fmd->fimc_lite[i]->subdev.entity;
- ret = media_entity_create_link(source, pad, sink,
- FLITE_SD_PAD_SINK, flags);
- if (ret)
- return ret;
-
- /* Notify FIMC-LITE subdev entity */
- ret = media_entity_call(sink, link_setup, &sink->pads[0],
- &source->pads[pad], flags);
- if (ret)
- break;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
- source->name, flags ? '=' : '-', sink->name);
- }
- return 0;
-}
-
-/* Create links from FIMC-LITE source pads to other entities */
-static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
-{
- struct media_entity *source, *sink;
- unsigned int flags = MEDIA_LNK_FL_ENABLED;
- int i, ret;
-
- for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
- struct fimc_lite *fimc = fmd->fimc_lite[i];
- if (fimc == NULL)
- continue;
- source = &fimc->subdev.entity;
- sink = &fimc->vfd.entity;
- /* FIMC-LITE's subdev and video node */
- ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
- sink, 0, flags);
- if (ret)
- break;
- /* TODO: create links to other entities */
- }
-
- return ret;
-}
-
-/**
- * fimc_md_create_links - create default links between registered entities
- *
- * Parallel interface sensor entities are connected directly to FIMC capture
- * entities. The sensors using MIPI CSIS bus are connected through immutable
- * link with CSI receiver entity specified by mux_id. Any registered CSIS
- * entity has a link to each registered FIMC capture entity. Enabled links
- * are created by default between each subsequent registered sensor and
- * subsequent FIMC capture entity. The number of default active links is
- * determined by the number of available sensors or FIMC entities,
- * whichever is less.
- */
-static int fimc_md_create_links(struct fimc_md *fmd)
-{
- struct v4l2_subdev *csi_sensors[2] = { NULL };
- struct v4l2_subdev *sensor, *csis;
- struct s5p_fimc_isp_info *pdata;
- struct fimc_sensor_info *s_info;
- struct media_entity *source, *sink;
- int i, pad, fimc_id = 0, ret = 0;
- u32 flags, link_mask = 0;
-
- for (i = 0; i < fmd->num_sensors; i++) {
- if (fmd->sensor[i].subdev == NULL)
- continue;
-
- sensor = fmd->sensor[i].subdev;
- s_info = v4l2_get_subdev_hostdata(sensor);
- if (!s_info || !s_info->pdata)
- continue;
-
- source = NULL;
- pdata = s_info->pdata;
-
- switch (pdata->bus_type) {
- case FIMC_MIPI_CSI2:
- if (WARN(pdata->mux_id >= CSIS_MAX_ENTITIES,
- "Wrong CSI channel id: %d\n", pdata->mux_id))
- return -EINVAL;
-
- csis = fmd->csis[pdata->mux_id].sd;
- if (WARN(csis == NULL,
- "MIPI-CSI interface specified "
- "but s5p-csis module is not loaded!\n"))
- return -EINVAL;
-
- ret = media_entity_create_link(&sensor->entity, 0,
- &csis->entity, CSIS_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret)
- return ret;
-
- v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
- sensor->entity.name, csis->entity.name);
-
- source = NULL;
- csi_sensors[pdata->mux_id] = sensor;
- break;
-
- case FIMC_ITU_601...FIMC_ITU_656:
- source = &sensor->entity;
- pad = 0;
- break;
-
- default:
- v4l2_err(&fmd->v4l2_dev, "Wrong bus_type: %x\n",
- pdata->bus_type);
- return -EINVAL;
- }
- if (source == NULL)
- continue;
-
- link_mask = 1 << fimc_id++;
- ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
- pad, link_mask);
- }
-
- for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) {
- if (fmd->csis[i].sd == NULL)
- continue;
- source = &fmd->csis[i].sd->entity;
- pad = CSIS_PAD_SOURCE;
- sensor = csi_sensors[i];
-
- link_mask = 1 << fimc_id++;
- ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
- pad, link_mask);
- }
-
- /* Create immutable links between each FIMC's subdev and video node */
- flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
- for (i = 0; i < FIMC_MAX_DEVS; i++) {
- if (!fmd->fimc[i])
- continue;
- source = &fmd->fimc[i]->vid_cap.subdev.entity;
- sink = &fmd->fimc[i]->vid_cap.vfd.entity;
- ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
- sink, 0, flags);
- if (ret)
- break;
- }
-
- return __fimc_md_create_flite_source_links(fmd);
-}
-
-/*
- * The peripheral sensor clock management.
- */
-static int fimc_md_get_clocks(struct fimc_md *fmd)
-{
- char clk_name[32];
- struct clk *clock;
- int i;
-
- for (i = 0; i < FIMC_MAX_CAMCLKS; i++) {
- snprintf(clk_name, sizeof(clk_name), "sclk_cam%u", i);
- clock = clk_get(NULL, clk_name);
- if (IS_ERR_OR_NULL(clock)) {
- v4l2_err(&fmd->v4l2_dev, "Failed to get clock: %s",
- clk_name);
- return -ENXIO;
- }
- fmd->camclk[i].clock = clock;
- }
- return 0;
-}
-
-static void fimc_md_put_clocks(struct fimc_md *fmd)
-{
- int i = FIMC_MAX_CAMCLKS;
-
- while (--i >= 0) {
- if (IS_ERR_OR_NULL(fmd->camclk[i].clock))
- continue;
- clk_put(fmd->camclk[i].clock);
- fmd->camclk[i].clock = NULL;
- }
-}
-
-static int __fimc_md_set_camclk(struct fimc_md *fmd,
- struct fimc_sensor_info *s_info,
- bool on)
-{
- struct s5p_fimc_isp_info *pdata = s_info->pdata;
- struct fimc_camclk_info *camclk;
- int ret = 0;
-
- if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL)
- return -EINVAL;
-
- camclk = &fmd->camclk[pdata->clk_id];
-
- dbg("camclk %d, f: %lu, use_count: %d, on: %d",
- pdata->clk_id, pdata->clk_frequency, camclk->use_count, on);
-
- if (on) {
- if (camclk->use_count > 0 &&
- camclk->frequency != pdata->clk_frequency)
- return -EINVAL;
-
- if (camclk->use_count++ == 0) {
- clk_set_rate(camclk->clock, pdata->clk_frequency);
- camclk->frequency = pdata->clk_frequency;
- ret = clk_enable(camclk->clock);
- dbg("Enabled camclk %d: f: %lu", pdata->clk_id,
- clk_get_rate(camclk->clock));
- }
- return ret;
- }
-
- if (WARN_ON(camclk->use_count == 0))
- return 0;
-
- if (--camclk->use_count == 0) {
- clk_disable(camclk->clock);
- dbg("Disabled camclk %d", pdata->clk_id);
- }
- return ret;
-}
-
-/**
- * fimc_md_set_camclk - peripheral sensor clock setup
- * @sd: sensor subdev to configure sclk_cam clock for
- * @on: 1 to enable or 0 to disable the clock
- *
- * There are 2 separate clock outputs available in the SoC for external
- * image processors. These clocks are shared between all registered FIMC
- * devices to which sensors can be attached, either directly or through
- * the MIPI CSI receiver. The clock is allowed here to be used by
- * multiple sensors concurrently if they use same frequency.
- * This function should only be called when the graph mutex is held.
- */
-int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
-{
- struct fimc_sensor_info *s_info = v4l2_get_subdev_hostdata(sd);
- struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity);
-
- return __fimc_md_set_camclk(fmd, s_info, on);
-}
-
-static int fimc_md_link_notify(struct media_pad *source,
- struct media_pad *sink, u32 flags)
-{
- struct fimc_lite *fimc_lite = NULL;
- struct fimc_dev *fimc = NULL;
- struct fimc_pipeline *pipeline;
- struct v4l2_subdev *sd;
- int ret = 0;
-
- if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
- return 0;
-
- sd = media_entity_to_v4l2_subdev(sink->entity);
-
- switch (sd->grp_id) {
- case FLITE_GROUP_ID:
- fimc_lite = v4l2_get_subdevdata(sd);
- pipeline = &fimc_lite->pipeline;
- break;
- case FIMC_GROUP_ID:
- fimc = v4l2_get_subdevdata(sd);
- pipeline = &fimc->pipeline;
- break;
- default:
- return 0;
- }
-
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- ret = __fimc_pipeline_shutdown(pipeline);
- pipeline->subdevs[IDX_SENSOR] = NULL;
- pipeline->subdevs[IDX_CSIS] = NULL;
-
- if (fimc) {
- mutex_lock(&fimc->lock);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- mutex_unlock(&fimc->lock);
- }
- return ret;
- }
- /*
- * Link activation. Enable power of pipeline elements only if the
- * pipeline is already in use, i.e. its video node is opened.
- * Recreate the controls destroyed during the link deactivation.
- */
- if (fimc) {
- mutex_lock(&fimc->lock);
- if (fimc->vid_cap.refcnt > 0) {
- ret = __fimc_pipeline_initialize(pipeline,
- source->entity, true);
- if (!ret)
- ret = fimc_capture_ctrls_create(fimc);
- }
- mutex_unlock(&fimc->lock);
- } else {
- mutex_lock(&fimc_lite->lock);
- if (fimc_lite->ref_count > 0) {
- ret = __fimc_pipeline_initialize(pipeline,
- source->entity, true);
- }
- mutex_unlock(&fimc_lite->lock);
- }
- return ret ? -EPIPE : ret;
-}
-
-static ssize_t fimc_md_sysfs_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fimc_md *fmd = platform_get_drvdata(pdev);
-
- if (fmd->user_subdev_api)
- return strlcpy(buf, "Sub-device API (sub-dev)\n", PAGE_SIZE);
-
- return strlcpy(buf, "V4L2 video node only API (vid-dev)\n", PAGE_SIZE);
-}
-
-static ssize_t fimc_md_sysfs_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct fimc_md *fmd = platform_get_drvdata(pdev);
- bool subdev_api;
- int i;
-
- if (!strcmp(buf, "vid-dev\n"))
- subdev_api = false;
- else if (!strcmp(buf, "sub-dev\n"))
- subdev_api = true;
- else
- return count;
-
- fmd->user_subdev_api = subdev_api;
- for (i = 0; i < FIMC_MAX_DEVS; i++)
- if (fmd->fimc[i])
- fmd->fimc[i]->vid_cap.user_subdev_api = subdev_api;
- return count;
-}
-/*
- * This device attribute is to select video pipeline configuration method.
- * There are following valid values:
- * vid-dev - for V4L2 video node API only, subdevice will be configured
- * by the host driver.
- * sub-dev - for media controller API, subdevs must be configured in user
- * space before starting streaming.
- */
-static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
- fimc_md_sysfs_show, fimc_md_sysfs_store);
-
-static int fimc_md_probe(struct platform_device *pdev)
-{
- struct v4l2_device *v4l2_dev;
- struct fimc_md *fmd;
- int ret;
-
- fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
- if (!fmd)
- return -ENOMEM;
-
- spin_lock_init(&fmd->slock);
- fmd->pdev = pdev;
-
- strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC",
- sizeof(fmd->media_dev.model));
- fmd->media_dev.link_notify = fimc_md_link_notify;
- fmd->media_dev.dev = &pdev->dev;
-
- v4l2_dev = &fmd->v4l2_dev;
- v4l2_dev->mdev = &fmd->media_dev;
- v4l2_dev->notify = fimc_sensor_notify;
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
- dev_name(&pdev->dev));
-
- ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
- return ret;
- }
- ret = media_device_register(&fmd->media_dev);
- if (ret < 0) {
- v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err_md;
- }
- ret = fimc_md_get_clocks(fmd);
- if (ret)
- goto err_clk;
-
- fmd->user_subdev_api = false;
-
- /* Protect the media graph while we're registering entities */
- mutex_lock(&fmd->media_dev.graph_mutex);
-
- ret = fimc_md_register_platform_entities(fmd);
- if (ret)
- goto err_unlock;
-
- if (pdev->dev.platform_data) {
- ret = fimc_md_register_sensor_entities(fmd);
- if (ret)
- goto err_unlock;
- }
- ret = fimc_md_create_links(fmd);
- if (ret)
- goto err_unlock;
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
- if (ret)
- goto err_unlock;
-
- ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
- if (ret)
- goto err_unlock;
-
- platform_set_drvdata(pdev, fmd);
- mutex_unlock(&fmd->media_dev.graph_mutex);
- return 0;
-
-err_unlock:
- mutex_unlock(&fmd->media_dev.graph_mutex);
-err_clk:
- media_device_unregister(&fmd->media_dev);
- fimc_md_put_clocks(fmd);
- fimc_md_unregister_entities(fmd);
-err_md:
- v4l2_device_unregister(&fmd->v4l2_dev);
- return ret;
-}
-
-static int __devexit fimc_md_remove(struct platform_device *pdev)
-{
- struct fimc_md *fmd = platform_get_drvdata(pdev);
-
- if (!fmd)
- return 0;
- device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
- fimc_md_unregister_entities(fmd);
- media_device_unregister(&fmd->media_dev);
- fimc_md_put_clocks(fmd);
- return 0;
-}
-
-static struct platform_driver fimc_md_driver = {
- .probe = fimc_md_probe,
- .remove = __devexit_p(fimc_md_remove),
- .driver = {
- .name = "s5p-fimc-md",
- .owner = THIS_MODULE,
- }
-};
-
-static int __init fimc_md_init(void)
-{
- int ret;
-
- request_module("s5p-csis");
- ret = fimc_register_driver();
- if (ret)
- return ret;
-
- return platform_driver_register(&fimc_md_driver);
-}
-
-static void __exit fimc_md_exit(void)
-{
- platform_driver_unregister(&fimc_md_driver);
- fimc_unregister_driver();
-}
-
-module_init(fimc_md_init);
-module_exit(fimc_md_exit);
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("S5P FIMC camera host interface/video postprocessor driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("2.0.1");
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
deleted file mode 100644
index d310d9cc3e1a..000000000000
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- *
- * 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.
- */
-
-#ifndef FIMC_MDEVICE_H_
-#define FIMC_MDEVICE_H_
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-#include <media/media-device.h>
-#include <media/media-entity.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#include "fimc-core.h"
-#include "fimc-lite.h"
-#include "mipi-csis.h"
-
-/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */
-#define SENSOR_GROUP_ID (1 << 8)
-#define CSIS_GROUP_ID (1 << 9)
-#define WRITEBACK_GROUP_ID (1 << 10)
-#define FIMC_GROUP_ID (1 << 11)
-#define FLITE_GROUP_ID (1 << 12)
-
-#define FIMC_MAX_SENSORS 8
-#define FIMC_MAX_CAMCLKS 2
-
-struct fimc_csis_info {
- struct v4l2_subdev *sd;
- int id;
-};
-
-struct fimc_camclk_info {
- struct clk *clock;
- int use_count;
- unsigned long frequency;
-};
-
-/**
- * struct fimc_sensor_info - image data source subdev information
- * @pdata: sensor's atrributes passed as media device's platform data
- * @subdev: image sensor v4l2 subdev
- * @host: fimc device the sensor is currently linked to
- *
- * This data structure applies to image sensor and the writeback subdevs.
- */
-struct fimc_sensor_info {
- struct s5p_fimc_isp_info *pdata;
- struct v4l2_subdev *subdev;
- struct fimc_dev *host;
-};
-
-/**
- * struct fimc_md - fimc media device information
- * @csis: MIPI CSIS subdevs data
- * @sensor: array of registered sensor subdevs
- * @num_sensors: actual number of registered sensors
- * @camclk: external sensor clock information
- * @fimc: array of registered fimc devices
- * @media_dev: top level media device
- * @v4l2_dev: top level v4l2_device holding up the subdevs
- * @pdev: platform device this media device is hooked up into
- * @user_subdev_api: true if subdevs are not configured by the host driver
- * @slock: spinlock protecting @sensor array
- */
-struct fimc_md {
- struct fimc_csis_info csis[CSIS_MAX_ENTITIES];
- struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
- int num_sensors;
- struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
- struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
- struct fimc_dev *fimc[FIMC_MAX_DEVS];
- struct media_device media_dev;
- struct v4l2_device v4l2_dev;
- struct platform_device *pdev;
- bool user_subdev_api;
- spinlock_t slock;
-};
-
-#define is_subdev_pad(pad) (pad == NULL || \
- media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
-
-#define me_subtype(me) \
- ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK))
-
-#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)
-
-static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me)
-{
- return me->parent == NULL ? NULL :
- container_of(me->parent, struct fimc_md, media_dev);
-}
-
-static inline void fimc_md_graph_lock(struct fimc_dev *fimc)
-{
- mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
-}
-
-static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
-{
- mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex);
-}
-
-int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
-void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me);
-int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me,
- bool resume);
-int fimc_pipeline_shutdown(struct fimc_pipeline *p);
-int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state);
-int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool state);
-
-#endif
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
deleted file mode 100644
index 2f73d9e3d0b7..000000000000
--- a/drivers/media/video/s5p-fimc/mipi-csis.c
+++ /dev/null
@@ -1,722 +0,0 @@
-/*
- * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
- *
- * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/kernel.h>
-#include <linux/memory.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-#include <plat/mipi_csis.h>
-#include "mipi-csis.h"
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-/* Register map definition */
-
-/* CSIS global control */
-#define S5PCSIS_CTRL 0x00
-#define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31)
-#define S5PCSIS_CTRL_DPDN_SWAP (1 << 31)
-#define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20)
-#define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16)
-#define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8)
-#define S5PCSIS_CTRL_RESET (1 << 4)
-#define S5PCSIS_CTRL_ENABLE (1 << 0)
-
-/* D-PHY control */
-#define S5PCSIS_DPHYCTRL 0x04
-#define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27)
-#define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0)
-
-#define S5PCSIS_CONFIG 0x08
-#define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
-#define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2)
-#define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2)
-#define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2)
-/* User defined formats, x = 1...4 */
-#define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2)
-#define S5PCSIS_CFG_FMT_MASK (0x3f << 2)
-#define S5PCSIS_CFG_NR_LANE_MASK 3
-
-/* Interrupt mask. */
-#define S5PCSIS_INTMSK 0x10
-#define S5PCSIS_INTMSK_EN_ALL 0xf000003f
-#define S5PCSIS_INTSRC 0x14
-
-/* Pixel resolution */
-#define S5PCSIS_RESOL 0x2c
-#define CSIS_MAX_PIX_WIDTH 0xffff
-#define CSIS_MAX_PIX_HEIGHT 0xffff
-
-enum {
- CSIS_CLK_MUX,
- CSIS_CLK_GATE,
-};
-
-static char *csi_clock_name[] = {
- [CSIS_CLK_MUX] = "sclk_csis",
- [CSIS_CLK_GATE] = "csis",
-};
-#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
-
-static const char * const csis_supply_name[] = {
- "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */
- "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */
-};
-#define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
-
-enum {
- ST_POWERED = 1,
- ST_STREAMING = 2,
- ST_SUSPENDED = 4,
-};
-
-/**
- * struct csis_state - the driver's internal state data structure
- * @lock: mutex serializing the subdev and power management operations,
- * protecting @format and @flags members
- * @pads: CSIS pads array
- * @sd: v4l2_subdev associated with CSIS device instance
- * @pdev: CSIS platform device
- * @regs: mmaped I/O registers memory
- * @clock: CSIS clocks
- * @irq: requested s5p-mipi-csis irq number
- * @flags: the state variable for power and streaming control
- * @csis_fmt: current CSIS pixel format
- * @format: common media bus format for the source and sink pad
- */
-struct csis_state {
- struct mutex lock;
- struct media_pad pads[CSIS_PADS_NUM];
- struct v4l2_subdev sd;
- struct platform_device *pdev;
- void __iomem *regs;
- struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
- struct clk *clock[NUM_CSIS_CLOCKS];
- int irq;
- u32 flags;
- const struct csis_pix_format *csis_fmt;
- struct v4l2_mbus_framefmt format;
-};
-
-/**
- * struct csis_pix_format - CSIS pixel format description
- * @pix_width_alignment: horizontal pixel alignment, width will be
- * multiple of 2^pix_width_alignment
- * @code: corresponding media bus code
- * @fmt_reg: S5PCSIS_CONFIG register value
- * @data_alignment: MIPI-CSI data alignment in bits
- */
-struct csis_pix_format {
- unsigned int pix_width_alignment;
- enum v4l2_mbus_pixelcode code;
- u32 fmt_reg;
- u8 data_alignment;
-};
-
-static const struct csis_pix_format s5pcsis_formats[] = {
- {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
- .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
- .data_alignment = 32,
- }, {
- .code = V4L2_MBUS_FMT_JPEG_1X8,
- .fmt_reg = S5PCSIS_CFG_FMT_USER(1),
- .data_alignment = 32,
- },
-};
-
-#define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
-#define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
-
-static struct csis_state *sd_to_csis_state(struct v4l2_subdev *sdev)
-{
- return container_of(sdev, struct csis_state, sd);
-}
-
-static const struct csis_pix_format *find_csis_format(
- struct v4l2_mbus_framefmt *mf)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s5pcsis_formats); i++)
- if (mf->code == s5pcsis_formats[i].code)
- return &s5pcsis_formats[i];
- return NULL;
-}
-
-static void s5pcsis_enable_interrupts(struct csis_state *state, bool on)
-{
- u32 val = s5pcsis_read(state, S5PCSIS_INTMSK);
-
- val = on ? val | S5PCSIS_INTMSK_EN_ALL :
- val & ~S5PCSIS_INTMSK_EN_ALL;
- s5pcsis_write(state, S5PCSIS_INTMSK, val);
-}
-
-static void s5pcsis_reset(struct csis_state *state)
-{
- u32 val = s5pcsis_read(state, S5PCSIS_CTRL);
-
- s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_RESET);
- udelay(10);
-}
-
-static void s5pcsis_system_enable(struct csis_state *state, int on)
-{
- u32 val;
-
- val = s5pcsis_read(state, S5PCSIS_CTRL);
- if (on)
- val |= S5PCSIS_CTRL_ENABLE;
- else
- val &= ~S5PCSIS_CTRL_ENABLE;
- s5pcsis_write(state, S5PCSIS_CTRL, val);
-
- val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
- if (on)
- val |= S5PCSIS_DPHYCTRL_ENABLE;
- else
- val &= ~S5PCSIS_DPHYCTRL_ENABLE;
- s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
-}
-
-/* Called with the state.lock mutex held */
-static void __s5pcsis_set_format(struct csis_state *state)
-{
- struct v4l2_mbus_framefmt *mf = &state->format;
- u32 val;
-
- v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n",
- mf->code, mf->width, mf->height);
-
- /* Color format */
- val = s5pcsis_read(state, S5PCSIS_CONFIG);
- val = (val & ~S5PCSIS_CFG_FMT_MASK) | state->csis_fmt->fmt_reg;
- s5pcsis_write(state, S5PCSIS_CONFIG, val);
-
- /* Pixel resolution */
- val = (mf->width << 16) | mf->height;
- s5pcsis_write(state, S5PCSIS_RESOL, val);
-}
-
-static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
-{
- u32 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
-
- val = (val & ~S5PCSIS_DPHYCTRL_HSS_MASK) | (settle << 27);
- s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
-}
-
-static void s5pcsis_set_params(struct csis_state *state)
-{
- struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
- u32 val;
-
- val = s5pcsis_read(state, S5PCSIS_CONFIG);
- val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
- s5pcsis_write(state, S5PCSIS_CONFIG, val);
-
- __s5pcsis_set_format(state);
- s5pcsis_set_hsync_settle(state, pdata->hs_settle);
-
- val = s5pcsis_read(state, S5PCSIS_CTRL);
- if (state->csis_fmt->data_alignment == 32)
- val |= S5PCSIS_CTRL_ALIGN_32BIT;
- else /* 24-bits */
- val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
- /* Not using external clock. */
- val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
- s5pcsis_write(state, S5PCSIS_CTRL, val);
-
- /* Update the shadow register. */
- val = s5pcsis_read(state, S5PCSIS_CTRL);
- s5pcsis_write(state, S5PCSIS_CTRL, val | S5PCSIS_CTRL_UPDATE_SHADOW);
-}
-
-static void s5pcsis_clk_put(struct csis_state *state)
-{
- int i;
-
- for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
- if (IS_ERR_OR_NULL(state->clock[i]))
- continue;
- clk_unprepare(state->clock[i]);
- clk_put(state->clock[i]);
- state->clock[i] = NULL;
- }
-}
-
-static int s5pcsis_clk_get(struct csis_state *state)
-{
- struct device *dev = &state->pdev->dev;
- int i, ret;
-
- for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
- state->clock[i] = clk_get(dev, csi_clock_name[i]);
- if (IS_ERR(state->clock[i]))
- goto err;
- ret = clk_prepare(state->clock[i]);
- if (ret < 0) {
- clk_put(state->clock[i]);
- state->clock[i] = NULL;
- goto err;
- }
- }
- return 0;
-err:
- s5pcsis_clk_put(state);
- dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
- return -ENXIO;
-}
-
-static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
-{
- struct csis_state *state = sd_to_csis_state(sd);
- struct device *dev = &state->pdev->dev;
-
- if (on)
- return pm_runtime_get_sync(dev);
-
- return pm_runtime_put_sync(dev);
-}
-
-static void s5pcsis_start_stream(struct csis_state *state)
-{
- s5pcsis_reset(state);
- s5pcsis_set_params(state);
- s5pcsis_system_enable(state, true);
- s5pcsis_enable_interrupts(state, true);
-}
-
-static void s5pcsis_stop_stream(struct csis_state *state)
-{
- s5pcsis_enable_interrupts(state, false);
- s5pcsis_system_enable(state, false);
-}
-
-/* v4l2_subdev operations */
-static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct csis_state *state = sd_to_csis_state(sd);
- int ret = 0;
-
- v4l2_dbg(1, debug, sd, "%s: %d, state: 0x%x\n",
- __func__, enable, state->flags);
-
- if (enable) {
- ret = pm_runtime_get_sync(&state->pdev->dev);
- if (ret && ret != 1)
- return ret;
- }
- mutex_lock(&state->lock);
- if (enable) {
- if (state->flags & ST_SUSPENDED) {
- ret = -EBUSY;
- goto unlock;
- }
- s5pcsis_start_stream(state);
- state->flags |= ST_STREAMING;
- } else {
- s5pcsis_stop_stream(state);
- state->flags &= ~ST_STREAMING;
- }
-unlock:
- mutex_unlock(&state->lock);
- if (!enable)
- pm_runtime_put(&state->pdev->dev);
-
- return ret == 1 ? 0 : ret;
-}
-
-static int s5pcsis_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- if (code->index >= ARRAY_SIZE(s5pcsis_formats))
- return -EINVAL;
-
- code->code = s5pcsis_formats[code->index].code;
- return 0;
-}
-
-static struct csis_pix_format const *s5pcsis_try_format(
- struct v4l2_mbus_framefmt *mf)
-{
- struct csis_pix_format const *csis_fmt;
-
- csis_fmt = find_csis_format(mf);
- if (csis_fmt == NULL)
- csis_fmt = &s5pcsis_formats[0];
-
- mf->code = csis_fmt->code;
- v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
- csis_fmt->pix_width_alignment,
- &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
- 0);
- return csis_fmt;
-}
-
-static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
- struct csis_state *state, struct v4l2_subdev_fh *fh,
- u32 pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
-
- return &state->format;
-}
-
-static int s5pcsis_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct csis_state *state = sd_to_csis_state(sd);
- struct csis_pix_format const *csis_fmt;
- struct v4l2_mbus_framefmt *mf;
-
- if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
- return -EINVAL;
-
- mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
-
- if (fmt->pad == CSIS_PAD_SOURCE) {
- if (mf) {
- mutex_lock(&state->lock);
- fmt->format = *mf;
- mutex_unlock(&state->lock);
- }
- return 0;
- }
- csis_fmt = s5pcsis_try_format(&fmt->format);
- if (mf) {
- mutex_lock(&state->lock);
- *mf = fmt->format;
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- state->csis_fmt = csis_fmt;
- mutex_unlock(&state->lock);
- }
- return 0;
-}
-
-static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct csis_state *state = sd_to_csis_state(sd);
- struct v4l2_mbus_framefmt *mf;
-
- if (fmt->pad != CSIS_PAD_SOURCE && fmt->pad != CSIS_PAD_SINK)
- return -EINVAL;
-
- mf = __s5pcsis_get_format(state, fh, fmt->pad, fmt->which);
- if (!mf)
- return -EINVAL;
-
- mutex_lock(&state->lock);
- fmt->format = *mf;
- mutex_unlock(&state->lock);
- return 0;
-}
-
-static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0);
-
- format->colorspace = V4L2_COLORSPACE_JPEG;
- format->code = s5pcsis_formats[0].code;
- format->width = S5PCSIS_DEF_PIX_WIDTH;
- format->height = S5PCSIS_DEF_PIX_HEIGHT;
- format->field = V4L2_FIELD_NONE;
-
- return 0;
-}
-
-static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
- .open = s5pcsis_open,
-};
-
-static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
- .s_power = s5pcsis_s_power,
-};
-
-static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
- .enum_mbus_code = s5pcsis_enum_mbus_code,
- .get_fmt = s5pcsis_get_fmt,
- .set_fmt = s5pcsis_set_fmt,
-};
-
-static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
- .s_stream = s5pcsis_s_stream,
-};
-
-static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
- .core = &s5pcsis_core_ops,
- .pad = &s5pcsis_pad_ops,
- .video = &s5pcsis_video_ops,
-};
-
-static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
-{
- struct csis_state *state = dev_id;
- u32 val;
-
- /* Just clear the interrupt pending bits. */
- val = s5pcsis_read(state, S5PCSIS_INTSRC);
- s5pcsis_write(state, S5PCSIS_INTSRC, val);
-
- return IRQ_HANDLED;
-}
-
-static int __devinit s5pcsis_probe(struct platform_device *pdev)
-{
- struct s5p_platform_mipi_csis *pdata;
- struct resource *mem_res;
- struct csis_state *state;
- int ret = -ENOMEM;
- int i;
-
- state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- mutex_init(&state->lock);
- state->pdev = pdev;
-
- pdata = pdev->dev.platform_data;
- if (pdata == NULL || pdata->phy_enable == NULL) {
- dev_err(&pdev->dev, "Platform data not fully specified\n");
- return -EINVAL;
- }
-
- if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
- pdata->lanes > CSIS0_MAX_LANES) {
- dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
- pdata->lanes);
- return -EINVAL;
- }
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
- if (state->regs == NULL) {
- dev_err(&pdev->dev, "Failed to request and remap io memory\n");
- return -ENXIO;
- }
-
- state->irq = platform_get_irq(pdev, 0);
- if (state->irq < 0) {
- dev_err(&pdev->dev, "Failed to get irq\n");
- return state->irq;
- }
-
- for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
- state->supplies[i].supply = csis_supply_name[i];
-
- ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
- state->supplies);
- if (ret)
- return ret;
-
- ret = s5pcsis_clk_get(state);
- if (ret)
- goto e_clkput;
-
- clk_enable(state->clock[CSIS_CLK_MUX]);
- if (pdata->clk_rate)
- clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
- else
- dev_WARN(&pdev->dev, "No clock frequency specified!\n");
-
- ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
- 0, dev_name(&pdev->dev), state);
- if (ret) {
- dev_err(&pdev->dev, "Interrupt request failed\n");
- goto e_regput;
- }
-
- v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
- state->sd.owner = THIS_MODULE;
- strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
- state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- state->csis_fmt = &s5pcsis_formats[0];
-
- state->format.code = s5pcsis_formats[0].code;
- state->format.width = S5PCSIS_DEF_PIX_WIDTH;
- state->format.height = S5PCSIS_DEF_PIX_HEIGHT;
-
- state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&state->sd.entity,
- CSIS_PADS_NUM, state->pads, 0);
- if (ret < 0)
- goto e_clkput;
-
- /* This allows to retrieve the platform device id by the host driver */
- v4l2_set_subdevdata(&state->sd, pdev);
-
- /* .. and a pointer to the subdev. */
- platform_set_drvdata(pdev, &state->sd);
-
- pm_runtime_enable(&pdev->dev);
- return 0;
-
-e_regput:
- regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
-e_clkput:
- clk_disable(state->clock[CSIS_CLK_MUX]);
- s5pcsis_clk_put(state);
- return ret;
-}
-
-static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
-{
- struct s5p_platform_mipi_csis *pdata = dev->platform_data;
- struct platform_device *pdev = to_platform_device(dev);
- struct v4l2_subdev *sd = platform_get_drvdata(pdev);
- struct csis_state *state = sd_to_csis_state(sd);
- int ret = 0;
-
- v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
- __func__, state->flags);
-
- mutex_lock(&state->lock);
- if (state->flags & ST_POWERED) {
- s5pcsis_stop_stream(state);
- ret = pdata->phy_enable(state->pdev, false);
- if (ret)
- goto unlock;
- ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
- state->supplies);
- if (ret)
- goto unlock;
- clk_disable(state->clock[CSIS_CLK_GATE]);
- state->flags &= ~ST_POWERED;
- if (!runtime)
- state->flags |= ST_SUSPENDED;
- }
- unlock:
- mutex_unlock(&state->lock);
- return ret ? -EAGAIN : 0;
-}
-
-static int s5pcsis_pm_resume(struct device *dev, bool runtime)
-{
- struct s5p_platform_mipi_csis *pdata = dev->platform_data;
- struct platform_device *pdev = to_platform_device(dev);
- struct v4l2_subdev *sd = platform_get_drvdata(pdev);
- struct csis_state *state = sd_to_csis_state(sd);
- int ret = 0;
-
- v4l2_dbg(1, debug, sd, "%s: flags: 0x%x\n",
- __func__, state->flags);
-
- mutex_lock(&state->lock);
- if (!runtime && !(state->flags & ST_SUSPENDED))
- goto unlock;
-
- if (!(state->flags & ST_POWERED)) {
- ret = regulator_bulk_enable(CSIS_NUM_SUPPLIES,
- state->supplies);
- if (ret)
- goto unlock;
- ret = pdata->phy_enable(state->pdev, true);
- if (!ret) {
- state->flags |= ST_POWERED;
- } else {
- regulator_bulk_disable(CSIS_NUM_SUPPLIES,
- state->supplies);
- goto unlock;
- }
- clk_enable(state->clock[CSIS_CLK_GATE]);
- }
- if (state->flags & ST_STREAMING)
- s5pcsis_start_stream(state);
-
- state->flags &= ~ST_SUSPENDED;
- unlock:
- mutex_unlock(&state->lock);
- return ret ? -EAGAIN : 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int s5pcsis_suspend(struct device *dev)
-{
- return s5pcsis_pm_suspend(dev, false);
-}
-
-static int s5pcsis_resume(struct device *dev)
-{
- return s5pcsis_pm_resume(dev, false);
-}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int s5pcsis_runtime_suspend(struct device *dev)
-{
- return s5pcsis_pm_suspend(dev, true);
-}
-
-static int s5pcsis_runtime_resume(struct device *dev)
-{
- return s5pcsis_pm_resume(dev, true);
-}
-#endif
-
-static int __devexit s5pcsis_remove(struct platform_device *pdev)
-{
- struct v4l2_subdev *sd = platform_get_drvdata(pdev);
- struct csis_state *state = sd_to_csis_state(sd);
-
- pm_runtime_disable(&pdev->dev);
- s5pcsis_pm_suspend(&pdev->dev, false);
- clk_disable(state->clock[CSIS_CLK_MUX]);
- pm_runtime_set_suspended(&pdev->dev);
- s5pcsis_clk_put(state);
- regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
-
- media_entity_cleanup(&state->sd.entity);
-
- return 0;
-}
-
-static const struct dev_pm_ops s5pcsis_pm_ops = {
- SET_RUNTIME_PM_OPS(s5pcsis_runtime_suspend, s5pcsis_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
-};
-
-static struct platform_driver s5pcsis_driver = {
- .probe = s5pcsis_probe,
- .remove = __devexit_p(s5pcsis_remove),
- .driver = {
- .name = CSIS_DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = &s5pcsis_pm_ops,
- },
-};
-
-module_platform_driver(s5pcsis_driver);
-
-MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c
deleted file mode 100644
index 7c2200435206..000000000000
--- a/drivers/media/video/s5p-g2d/g2d.c
+++ /dev/null
@@ -1,832 +0,0 @@
-/*
- * Samsung S5P G2D - 2D Graphics Accelerator Driver
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Kamil Debski, <k.debski@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
- */
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/version.h>
-#include <linux/timer.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-
-#include <linux/platform_device.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "g2d.h"
-#include "g2d-regs.h"
-
-#define fh2ctx(__fh) container_of(__fh, struct g2d_ctx, fh)
-
-static struct g2d_fmt formats[] = {
- {
- .name = "XRGB_8888",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_8888),
- },
- {
- .name = "RGB_565",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .depth = 16,
- .hw = COLOR_MODE(ORDER_XRGB, MODE_RGB_565),
- },
- {
- .name = "XRGB_1555",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .depth = 16,
- .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_1555),
- },
- {
- .name = "XRGB_4444",
- .fourcc = V4L2_PIX_FMT_RGB444,
- .depth = 16,
- .hw = COLOR_MODE(ORDER_XRGB, MODE_XRGB_4444),
- },
- {
- .name = "PACKED_RGB_888",
- .fourcc = V4L2_PIX_FMT_RGB24,
- .depth = 24,
- .hw = COLOR_MODE(ORDER_XRGB, MODE_PACKED_RGB_888),
- },
-};
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-static struct g2d_frame def_frame = {
- .width = DEFAULT_WIDTH,
- .height = DEFAULT_HEIGHT,
- .c_width = DEFAULT_WIDTH,
- .c_height = DEFAULT_HEIGHT,
- .o_width = 0,
- .o_height = 0,
- .fmt = &formats[0],
- .right = DEFAULT_WIDTH,
- .bottom = DEFAULT_HEIGHT,
-};
-
-static struct g2d_fmt *find_fmt(struct v4l2_format *f)
-{
- unsigned int i;
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].fourcc == f->fmt.pix.pixelformat)
- return &formats[i];
- }
- return NULL;
-}
-
-
-static struct g2d_frame *get_frame(struct g2d_ctx *ctx,
- enum v4l2_buf_type type)
-{
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- return &ctx->in;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- return &ctx->out;
- default:
- return ERR_PTR(-EINVAL);
- }
-}
-
-static int g2d_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct g2d_ctx *ctx = vb2_get_drv_priv(vq);
- struct g2d_frame *f = get_frame(ctx, vq->type);
-
- if (IS_ERR(f))
- return PTR_ERR(f);
-
- sizes[0] = f->size;
- *nplanes = 1;
- alloc_ctxs[0] = ctx->dev->alloc_ctx;
-
- if (*nbuffers == 0)
- *nbuffers = 1;
-
- return 0;
-}
-
-static int g2d_buf_prepare(struct vb2_buffer *vb)
-{
- struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct g2d_frame *f = get_frame(ctx, vb->vb2_queue->type);
-
- if (IS_ERR(f))
- return PTR_ERR(f);
- vb2_set_plane_payload(vb, 0, f->size);
- return 0;
-}
-
-static void g2d_buf_queue(struct vb2_buffer *vb)
-{
- struct g2d_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-
-static struct vb2_ops g2d_qops = {
- .queue_setup = g2d_queue_setup,
- .buf_prepare = g2d_buf_prepare,
- .buf_queue = g2d_buf_queue,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct g2d_ctx *ctx = priv;
- int ret;
-
- memset(src_vq, 0, sizeof(*src_vq));
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
- src_vq->drv_priv = ctx;
- src_vq->ops = &g2d_qops;
- src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
-
- 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 | VB2_USERPTR;
- dst_vq->drv_priv = ctx;
- dst_vq->ops = &g2d_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 g2d_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
- ctrl_handler);
- unsigned long flags;
-
- spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
- switch (ctrl->id) {
- case V4L2_CID_COLORFX:
- if (ctrl->val == V4L2_COLORFX_NEGATIVE)
- ctx->rop = ROP4_INVERT;
- else
- ctx->rop = ROP4_COPY;
- break;
-
- case V4L2_CID_HFLIP:
- ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
- break;
-
- }
- spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
- return 0;
-}
-
-static const struct v4l2_ctrl_ops g2d_ctrl_ops = {
- .s_ctrl = g2d_s_ctrl,
-};
-
-static int g2d_setup_ctrls(struct g2d_ctx *ctx)
-{
- struct g2d_dev *dev = ctx->dev;
-
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
-
- ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
-
- ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-
- v4l2_ctrl_new_std_menu(
- &ctx->ctrl_handler,
- &g2d_ctrl_ops,
- V4L2_CID_COLORFX,
- V4L2_COLORFX_NEGATIVE,
- ~((1 << V4L2_COLORFX_NONE) | (1 << V4L2_COLORFX_NEGATIVE)),
- V4L2_COLORFX_NONE);
-
- if (ctx->ctrl_handler.error) {
- int err = ctx->ctrl_handler.error;
- v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- return err;
- }
-
- v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
-
- return 0;
-}
-
-static int g2d_open(struct file *file)
-{
- struct g2d_dev *dev = video_drvdata(file);
- struct g2d_ctx *ctx = NULL;
- int ret = 0;
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- ctx->dev = dev;
- /* Set default formats */
- ctx->in = def_frame;
- ctx->out = def_frame;
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
- kfree(ctx);
- return ret;
- }
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
-
- g2d_setup_ctrls(ctx);
-
- /* Write the default values to the ctx struct */
- v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
-
- ctx->fh.ctrl_handler = &ctx->ctrl_handler;
-
- v4l2_info(&dev->v4l2_dev, "instance opened\n");
- return 0;
-}
-
-static int g2d_release(struct file *file)
-{
- struct g2d_dev *dev = video_drvdata(file);
- struct g2d_ctx *ctx = fh2ctx(file->private_data);
-
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- v4l2_info(&dev->v4l2_dev, "instance closed\n");
- return 0;
-}
-
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strncpy(cap->driver, G2D_NAME, sizeof(cap->driver) - 1);
- strncpy(cap->card, G2D_NAME, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
- V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int vidioc_enum_fmt(struct file *file, void *prv, struct v4l2_fmtdesc *f)
-{
- struct g2d_fmt *fmt;
- if (f->index >= NUM_FORMATS)
- return -EINVAL;
- fmt = &formats[f->index];
- f->pixelformat = fmt->fourcc;
- strncpy(f->description, fmt->name, sizeof(f->description) - 1);
- return 0;
-}
-
-static int vidioc_g_fmt(struct file *file, void *prv, struct v4l2_format *f)
-{
- struct g2d_ctx *ctx = prv;
- struct vb2_queue *vq;
- struct g2d_frame *frm;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
- frm = get_frame(ctx, f->type);
- if (IS_ERR(frm))
- return PTR_ERR(frm);
-
- f->fmt.pix.width = frm->width;
- f->fmt.pix.height = frm->height;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- f->fmt.pix.pixelformat = frm->fmt->fourcc;
- f->fmt.pix.bytesperline = (frm->width * frm->fmt->depth) >> 3;
- f->fmt.pix.sizeimage = frm->size;
- return 0;
-}
-
-static int vidioc_try_fmt(struct file *file, void *prv, struct v4l2_format *f)
-{
- struct g2d_fmt *fmt;
- enum v4l2_field *field;
-
- fmt = find_fmt(f);
- if (!fmt)
- return -EINVAL;
-
- field = &f->fmt.pix.field;
- if (*field == V4L2_FIELD_ANY)
- *field = V4L2_FIELD_NONE;
- else if (*field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- if (f->fmt.pix.width > MAX_WIDTH)
- f->fmt.pix.width = MAX_WIDTH;
- if (f->fmt.pix.height > MAX_HEIGHT)
- f->fmt.pix.height = MAX_HEIGHT;
-
- if (f->fmt.pix.width < 1)
- f->fmt.pix.width = 1;
- if (f->fmt.pix.height < 1)
- f->fmt.pix.height = 1;
-
- f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- return 0;
-}
-
-static int vidioc_s_fmt(struct file *file, void *prv, struct v4l2_format *f)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- struct vb2_queue *vq;
- struct g2d_frame *frm;
- struct g2d_fmt *fmt;
- int ret = 0;
-
- /* Adjust all values accordingly to the hardware capabilities
- * and chosen format. */
- ret = vidioc_try_fmt(file, prv, f);
- if (ret)
- return ret;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (vb2_is_busy(vq)) {
- v4l2_err(&dev->v4l2_dev, "queue (%d) bust\n", f->type);
- return -EBUSY;
- }
- frm = get_frame(ctx, f->type);
- if (IS_ERR(frm))
- return PTR_ERR(frm);
- fmt = find_fmt(f);
- if (!fmt)
- return -EINVAL;
- frm->width = f->fmt.pix.width;
- frm->height = f->fmt.pix.height;
- frm->size = f->fmt.pix.sizeimage;
- /* Reset crop settings */
- frm->o_width = 0;
- frm->o_height = 0;
- frm->c_width = frm->width;
- frm->c_height = frm->height;
- frm->right = frm->width;
- frm->bottom = frm->height;
- frm->fmt = fmt;
- frm->stride = f->fmt.pix.bytesperline;
- return 0;
-}
-
-static unsigned int g2d_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct g2d_ctx *ctx = fh2ctx(file->private_data);
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-static int g2d_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct g2d_ctx *ctx = fh2ctx(file->private_data);
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct g2d_ctx *ctx = priv;
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static int vidioc_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cr)
-{
- struct g2d_ctx *ctx = priv;
- struct g2d_frame *f;
-
- f = get_frame(ctx, cr->type);
- if (IS_ERR(f))
- return PTR_ERR(f);
-
- cr->bounds.left = 0;
- cr->bounds.top = 0;
- cr->bounds.width = f->width;
- cr->bounds.height = f->height;
- cr->defrect = cr->bounds;
- return 0;
-}
-
-static int vidioc_g_crop(struct file *file, void *prv, struct v4l2_crop *cr)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_frame *f;
-
- f = get_frame(ctx, cr->type);
- if (IS_ERR(f))
- return PTR_ERR(f);
-
- cr->c.left = f->o_height;
- cr->c.top = f->o_width;
- cr->c.width = f->c_width;
- cr->c.height = f->c_height;
- return 0;
-}
-
-static int vidioc_try_crop(struct file *file, void *prv, struct v4l2_crop *cr)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- struct g2d_frame *f;
-
- f = get_frame(ctx, cr->type);
- if (IS_ERR(f))
- return PTR_ERR(f);
-
- if (cr->c.top < 0 || cr->c.left < 0) {
- v4l2_err(&dev->v4l2_dev,
- "doesn't support negative values for top & left\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vidioc_s_crop(struct file *file, void *prv, struct v4l2_crop *cr)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_frame *f;
- int ret;
-
- ret = vidioc_try_crop(file, prv, cr);
- if (ret)
- return ret;
- f = get_frame(ctx, cr->type);
- if (IS_ERR(f))
- return PTR_ERR(f);
-
- f->c_width = cr->c.width;
- f->c_height = cr->c.height;
- f->o_width = cr->c.left;
- f->o_height = cr->c.top;
- f->bottom = f->o_height + f->c_height;
- f->right = f->o_width + f->c_width;
- return 0;
-}
-
-static void g2d_lock(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- mutex_lock(&dev->mutex);
-}
-
-static void g2d_unlock(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- mutex_unlock(&dev->mutex);
-}
-
-static void job_abort(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- int ret;
-
- if (dev->curr == NULL) /* No job currently running */
- return;
-
- ret = wait_event_timeout(dev->irq_queue,
- dev->curr == NULL,
- msecs_to_jiffies(G2D_TIMEOUT));
-}
-
-static void device_run(void *prv)
-{
- struct g2d_ctx *ctx = prv;
- struct g2d_dev *dev = ctx->dev;
- struct vb2_buffer *src, *dst;
- unsigned long flags;
- u32 cmd = 0;
-
- dev->curr = ctx;
-
- src = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
-
- clk_enable(dev->gate);
- g2d_reset(dev);
-
- spin_lock_irqsave(&dev->ctrl_lock, flags);
-
- g2d_set_src_size(dev, &ctx->in);
- g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
-
- g2d_set_dst_size(dev, &ctx->out);
- g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
-
- g2d_set_rop4(dev, ctx->rop);
- g2d_set_flip(dev, ctx->flip);
-
- if (ctx->in.c_width != ctx->out.c_width ||
- ctx->in.c_height != ctx->out.c_height)
- cmd |= g2d_cmd_stretch(1);
- g2d_set_cmd(dev, cmd);
- g2d_start(dev);
-
- spin_unlock_irqrestore(&dev->ctrl_lock, flags);
-}
-
-static irqreturn_t g2d_isr(int irq, void *prv)
-{
- struct g2d_dev *dev = prv;
- struct g2d_ctx *ctx = dev->curr;
- struct vb2_buffer *src, *dst;
-
- g2d_clear_int(dev);
- clk_disable(dev->gate);
-
- BUG_ON(ctx == NULL);
-
- src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
-
- BUG_ON(src == NULL);
- BUG_ON(dst == NULL);
-
- v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
- v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
- v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
-
- dev->curr = NULL;
- wake_up(&dev->irq_queue);
- return IRQ_HANDLED;
-}
-
-static const struct v4l2_file_operations g2d_fops = {
- .owner = THIS_MODULE,
- .open = g2d_open,
- .release = g2d_release,
- .poll = g2d_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = g2d_mmap,
-};
-
-static const struct v4l2_ioctl_ops g2d_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
-
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt,
- .vidioc_g_fmt_vid_out = vidioc_g_fmt,
- .vidioc_try_fmt_vid_out = vidioc_try_fmt,
- .vidioc_s_fmt_vid_out = vidioc_s_fmt,
-
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
-
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-
- .vidioc_g_crop = vidioc_g_crop,
- .vidioc_s_crop = vidioc_s_crop,
- .vidioc_cropcap = vidioc_cropcap,
-};
-
-static struct video_device g2d_videodev = {
- .name = G2D_NAME,
- .fops = &g2d_fops,
- .ioctl_ops = &g2d_ioctl_ops,
- .minor = -1,
- .release = video_device_release,
-};
-
-static struct v4l2_m2m_ops g2d_m2m_ops = {
- .device_run = device_run,
- .job_abort = job_abort,
- .lock = g2d_lock,
- .unlock = g2d_unlock,
-};
-
-static int g2d_probe(struct platform_device *pdev)
-{
- struct g2d_dev *dev;
- struct video_device *vfd;
- struct resource *res;
- int ret = 0;
-
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- spin_lock_init(&dev->ctrl_lock);
- mutex_init(&dev->mutex);
- atomic_set(&dev->num_inst, 0);
- init_waitqueue_head(&dev->irq_queue);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- dev->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (dev->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
-
- dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
- if (IS_ERR_OR_NULL(dev->clk)) {
- dev_err(&pdev->dev, "failed to get g2d clock\n");
- return -ENXIO;
- }
-
- ret = clk_prepare(dev->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare g2d clock\n");
- goto put_clk;
- }
-
- dev->gate = clk_get(&pdev->dev, "fimg2d");
- if (IS_ERR_OR_NULL(dev->gate)) {
- dev_err(&pdev->dev, "failed to get g2d clock gate\n");
- ret = -ENXIO;
- goto unprep_clk;
- }
-
- ret = clk_prepare(dev->gate);
- if (ret) {
- dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
- goto put_clk_gate;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to find IRQ\n");
- ret = -ENXIO;
- goto unprep_clk_gate;
- }
-
- dev->irq = res->start;
-
- ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr,
- 0, pdev->name, dev);
- if (ret) {
- dev_err(&pdev->dev, "failed to install IRQ\n");
- goto put_clk_gate;
- }
-
- dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(dev->alloc_ctx)) {
- ret = PTR_ERR(dev->alloc_ctx);
- goto unprep_clk_gate;
- }
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- goto alloc_ctx_cleanup;
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto unreg_v4l2_dev;
- }
- *vfd = g2d_videodev;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->lock = &dev->mutex;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto rel_vdev;
- }
- video_set_drvdata(vfd, dev);
- snprintf(vfd->name, sizeof(vfd->name), "%s", g2d_videodev.name);
- dev->vfd = vfd;
- v4l2_info(&dev->v4l2_dev, "device registered as /dev/video%d\n",
- vfd->num);
- platform_set_drvdata(pdev, dev);
- dev->m2m_dev = v4l2_m2m_init(&g2d_m2m_ops);
- if (IS_ERR(dev->m2m_dev)) {
- v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
- ret = PTR_ERR(dev->m2m_dev);
- goto unreg_video_dev;
- }
-
- def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
-
- return 0;
-
-unreg_video_dev:
- video_unregister_device(dev->vfd);
-rel_vdev:
- video_device_release(vfd);
-unreg_v4l2_dev:
- v4l2_device_unregister(&dev->v4l2_dev);
-alloc_ctx_cleanup:
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
-unprep_clk_gate:
- clk_unprepare(dev->gate);
-put_clk_gate:
- clk_put(dev->gate);
-unprep_clk:
- clk_unprepare(dev->clk);
-put_clk:
- clk_put(dev->clk);
-
- return ret;
-}
-
-static int g2d_remove(struct platform_device *pdev)
-{
- struct g2d_dev *dev = (struct g2d_dev *)platform_get_drvdata(pdev);
-
- v4l2_info(&dev->v4l2_dev, "Removing " G2D_NAME);
- v4l2_m2m_release(dev->m2m_dev);
- video_unregister_device(dev->vfd);
- v4l2_device_unregister(&dev->v4l2_dev);
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
- clk_unprepare(dev->gate);
- clk_put(dev->gate);
- clk_unprepare(dev->clk);
- clk_put(dev->clk);
- return 0;
-}
-
-static struct platform_driver g2d_pdrv = {
- .probe = g2d_probe,
- .remove = g2d_remove,
- .driver = {
- .name = G2D_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(g2d_pdrv);
-
-MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
-MODULE_DESCRIPTION("S5P G2D 2d graphics accelerator driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c
deleted file mode 100644
index 813b801238d1..000000000000
--- a/drivers/media/video/s5p-jpeg/jpeg-core.c
+++ /dev/null
@@ -1,1515 +0,0 @@
-/* linux/drivers/media/video/s5p-jpeg/jpeg-core.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/string.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-core.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "jpeg-core.h"
-#include "jpeg-hw.h"
-
-static struct s5p_jpeg_fmt formats_enc[] = {
- {
- .name = "JPEG JFIF",
- .fourcc = V4L2_PIX_FMT_JPEG,
- .colplanes = 1,
- .types = MEM2MEM_CAPTURE,
- },
- {
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .colplanes = 1,
- .types = MEM2MEM_OUTPUT,
- },
- {
- .name = "RGB565",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- .colplanes = 1,
- .types = MEM2MEM_OUTPUT,
- },
-};
-#define NUM_FORMATS_ENC ARRAY_SIZE(formats_enc)
-
-static struct s5p_jpeg_fmt formats_dec[] = {
- {
- .name = "YUV 4:2:0 planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .colplanes = 3,
- .h_align = 4,
- .v_align = 4,
- .types = MEM2MEM_CAPTURE,
- },
- {
- .name = "YUV 4:2:2 packed, YCbYCr",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .colplanes = 1,
- .h_align = 4,
- .v_align = 3,
- .types = MEM2MEM_CAPTURE,
- },
- {
- .name = "JPEG JFIF",
- .fourcc = V4L2_PIX_FMT_JPEG,
- .colplanes = 1,
- .types = MEM2MEM_OUTPUT,
- },
-};
-#define NUM_FORMATS_DEC ARRAY_SIZE(formats_dec)
-
-static const unsigned char qtbl_luminance[4][64] = {
- {/* level 1 - high quality */
- 8, 6, 6, 8, 12, 14, 16, 17,
- 6, 6, 6, 8, 10, 13, 12, 15,
- 6, 6, 7, 8, 13, 14, 18, 24,
- 8, 8, 8, 14, 13, 19, 24, 35,
- 12, 10, 13, 13, 20, 26, 34, 39,
- 14, 13, 14, 19, 26, 34, 39, 39,
- 16, 12, 18, 24, 34, 39, 39, 39,
- 17, 15, 24, 35, 39, 39, 39, 39
- },
- {/* level 2 */
- 12, 8, 8, 12, 17, 21, 24, 23,
- 8, 9, 9, 11, 15, 19, 18, 23,
- 8, 9, 10, 12, 19, 20, 27, 36,
- 12, 11, 12, 21, 20, 28, 36, 53,
- 17, 15, 19, 20, 30, 39, 51, 59,
- 21, 19, 20, 28, 39, 51, 59, 59,
- 24, 18, 27, 36, 51, 59, 59, 59,
- 23, 23, 36, 53, 59, 59, 59, 59
- },
- {/* level 3 */
- 16, 11, 11, 16, 23, 27, 31, 30,
- 11, 12, 12, 15, 20, 23, 23, 30,
- 11, 12, 13, 16, 23, 26, 35, 47,
- 16, 15, 16, 23, 26, 37, 47, 64,
- 23, 20, 23, 26, 39, 51, 64, 64,
- 27, 23, 26, 37, 51, 64, 64, 64,
- 31, 23, 35, 47, 64, 64, 64, 64,
- 30, 30, 47, 64, 64, 64, 64, 64
- },
- {/*level 4 - low quality */
- 20, 16, 25, 39, 50, 46, 62, 68,
- 16, 18, 23, 38, 38, 53, 65, 68,
- 25, 23, 31, 38, 53, 65, 68, 68,
- 39, 38, 38, 53, 65, 68, 68, 68,
- 50, 38, 53, 65, 68, 68, 68, 68,
- 46, 53, 65, 68, 68, 68, 68, 68,
- 62, 65, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68
- }
-};
-
-static const unsigned char qtbl_chrominance[4][64] = {
- {/* level 1 - high quality */
- 9, 8, 9, 11, 14, 17, 19, 24,
- 8, 10, 9, 11, 14, 13, 17, 22,
- 9, 9, 13, 14, 13, 15, 23, 26,
- 11, 11, 14, 14, 15, 20, 26, 33,
- 14, 14, 13, 15, 20, 24, 33, 39,
- 17, 13, 15, 20, 24, 32, 39, 39,
- 19, 17, 23, 26, 33, 39, 39, 39,
- 24, 22, 26, 33, 39, 39, 39, 39
- },
- {/* level 2 */
- 13, 11, 13, 16, 20, 20, 29, 37,
- 11, 14, 14, 14, 16, 20, 26, 32,
- 13, 14, 15, 17, 20, 23, 35, 40,
- 16, 14, 17, 21, 23, 30, 40, 50,
- 20, 16, 20, 23, 30, 37, 50, 59,
- 20, 20, 23, 30, 37, 48, 59, 59,
- 29, 26, 35, 40, 50, 59, 59, 59,
- 37, 32, 40, 50, 59, 59, 59, 59
- },
- {/* level 3 */
- 17, 15, 17, 21, 20, 26, 38, 48,
- 15, 19, 18, 17, 20, 26, 35, 43,
- 17, 18, 20, 22, 26, 30, 46, 53,
- 21, 17, 22, 28, 30, 39, 53, 64,
- 20, 20, 26, 30, 39, 48, 64, 64,
- 26, 26, 30, 39, 48, 63, 64, 64,
- 38, 35, 46, 53, 64, 64, 64, 64,
- 48, 43, 53, 64, 64, 64, 64, 64
- },
- {/*level 4 - low quality */
- 21, 25, 32, 38, 54, 68, 68, 68,
- 25, 28, 24, 38, 54, 68, 68, 68,
- 32, 24, 32, 43, 66, 68, 68, 68,
- 38, 38, 43, 53, 68, 68, 68, 68,
- 54, 54, 66, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68,
- 68, 68, 68, 68, 68, 68, 68, 68
- }
-};
-
-static const unsigned char hdctbl0[16] = {
- 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
-};
-
-static const unsigned char hdctblg0[12] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb
-};
-static const unsigned char hactbl0[16] = {
- 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
-};
-static const unsigned char hactblg0[162] = {
- 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
- 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
- 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
- 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
- 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
- 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
- 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
- 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
- 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
- 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
- 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
- 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
- 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xf9, 0xfa
-};
-
-static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
-{
- return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
-}
-
-static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
-{
- return container_of(fh, struct s5p_jpeg_ctx, fh);
-}
-
-static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
- unsigned long tab, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- writel((unsigned int)qtbl[i], regs + tab + (i * 0x04));
-}
-
-static inline void jpeg_set_qtbl_lum(void __iomem *regs, int quality)
-{
- /* this driver fills quantisation table 0 with data for luma */
- jpeg_set_qtbl(regs, qtbl_luminance[quality], S5P_JPG_QTBL_CONTENT(0),
- ARRAY_SIZE(qtbl_luminance[quality]));
-}
-
-static inline void jpeg_set_qtbl_chr(void __iomem *regs, int quality)
-{
- /* this driver fills quantisation table 1 with data for chroma */
- jpeg_set_qtbl(regs, qtbl_chrominance[quality], S5P_JPG_QTBL_CONTENT(1),
- ARRAY_SIZE(qtbl_chrominance[quality]));
-}
-
-static inline void jpeg_set_htbl(void __iomem *regs, const unsigned char *htbl,
- unsigned long tab, int len)
-{
- int i;
-
- for (i = 0; i < len; i++)
- writel((unsigned int)htbl[i], regs + tab + (i * 0x04));
-}
-
-static inline void jpeg_set_hdctbl(void __iomem *regs)
-{
- /* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hdctbl0, S5P_JPG_HDCTBL(0), ARRAY_SIZE(hdctbl0));
-}
-
-static inline void jpeg_set_hdctblg(void __iomem *regs)
-{
- /* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hdctblg0, S5P_JPG_HDCTBLG(0), ARRAY_SIZE(hdctblg0));
-}
-
-static inline void jpeg_set_hactbl(void __iomem *regs)
-{
- /* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hactbl0, S5P_JPG_HACTBL(0), ARRAY_SIZE(hactbl0));
-}
-
-static inline void jpeg_set_hactblg(void __iomem *regs)
-{
- /* this driver fills table 0 for this component */
- jpeg_set_htbl(regs, hactblg0, S5P_JPG_HACTBLG(0), ARRAY_SIZE(hactblg0));
-}
-
-/*
- * ============================================================================
- * Device file operations
- * ============================================================================
- */
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq);
-static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
- __u32 pixelformat);
-static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
-
-static int s5p_jpeg_open(struct file *file)
-{
- struct s5p_jpeg *jpeg = video_drvdata(file);
- struct video_device *vfd = video_devdata(file);
- struct s5p_jpeg_ctx *ctx;
- struct s5p_jpeg_fmt *out_fmt;
- int ret = 0;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
-
- v4l2_fh_init(&ctx->fh, vfd);
- /* Use separate control handler per file handle */
- ctx->fh.ctrl_handler = &ctx->ctrl_handler;
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
-
- ctx->jpeg = jpeg;
- if (vfd == jpeg->vfd_encoder) {
- ctx->mode = S5P_JPEG_ENCODE;
- out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_RGB565);
- } else {
- ctx->mode = S5P_JPEG_DECODE;
- out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
- }
-
- ret = s5p_jpeg_controls_create(ctx);
- if (ret < 0)
- goto error;
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
- goto error;
- }
-
- ctx->out_q.fmt = out_fmt;
- ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
- return 0;
-
-error:
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- return ret;
-}
-
-static int s5p_jpeg_release(struct file *file)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
-
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
-
- return 0;
-}
-
-static unsigned int s5p_jpeg_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
-
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
-
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-}
-
-static const struct v4l2_file_operations s5p_jpeg_fops = {
- .owner = THIS_MODULE,
- .open = s5p_jpeg_open,
- .release = s5p_jpeg_release,
- .poll = s5p_jpeg_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = s5p_jpeg_mmap,
-};
-
-/*
- * ============================================================================
- * video ioctl operations
- * ============================================================================
- */
-
-static int get_byte(struct s5p_jpeg_buffer *buf)
-{
- if (buf->curr >= buf->size)
- return -1;
-
- return ((unsigned char *)buf->data)[buf->curr++];
-}
-
-static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word)
-{
- unsigned int temp;
- int byte;
-
- byte = get_byte(buf);
- if (byte == -1)
- return -1;
- temp = byte << 8;
- byte = get_byte(buf);
- if (byte == -1)
- return -1;
- *word = (unsigned int)byte | temp;
- return 0;
-}
-
-static void skip(struct s5p_jpeg_buffer *buf, long len)
-{
- if (len <= 0)
- return;
-
- while (len--)
- get_byte(buf);
-}
-
-static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
- unsigned long buffer, unsigned long size)
-{
- int c, components, notfound;
- unsigned int height, width, word;
- long length;
- struct s5p_jpeg_buffer jpeg_buffer;
-
- jpeg_buffer.size = size;
- jpeg_buffer.data = buffer;
- jpeg_buffer.curr = 0;
-
- notfound = 1;
- while (notfound) {
- c = get_byte(&jpeg_buffer);
- if (c == -1)
- break;
- if (c != 0xff)
- continue;
- do
- c = get_byte(&jpeg_buffer);
- while (c == 0xff);
- if (c == -1)
- break;
- if (c == 0)
- continue;
- length = 0;
- switch (c) {
- /* SOF0: baseline JPEG */
- case SOF0:
- if (get_word_be(&jpeg_buffer, &word))
- break;
- if (get_byte(&jpeg_buffer) == -1)
- break;
- if (get_word_be(&jpeg_buffer, &height))
- break;
- if (get_word_be(&jpeg_buffer, &width))
- break;
- components = get_byte(&jpeg_buffer);
- if (components == -1)
- break;
- notfound = 0;
-
- skip(&jpeg_buffer, components * 3);
- break;
-
- /* skip payload-less markers */
- case RST ... RST + 7:
- case SOI:
- case EOI:
- case TEM:
- break;
-
- /* skip uninteresting payload markers */
- default:
- if (get_word_be(&jpeg_buffer, &word))
- break;
- length = (long)word - 2;
- skip(&jpeg_buffer, length);
- break;
- }
- }
- result->w = width;
- result->h = height;
- result->size = components;
- return !notfound;
-}
-
-static int s5p_jpeg_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->mode == S5P_JPEG_ENCODE) {
- strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder",
- sizeof(cap->driver));
- strlcpy(cap->card, S5P_JPEG_M2M_NAME " encoder",
- sizeof(cap->card));
- } else {
- strlcpy(cap->driver, S5P_JPEG_M2M_NAME " decoder",
- sizeof(cap->driver));
- strlcpy(cap->card, S5P_JPEG_M2M_NAME " decoder",
- sizeof(cap->card));
- }
- cap->bus_info[0] = 0;
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
- V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT;
- return 0;
-}
-
-static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
- struct v4l2_fmtdesc *f, u32 type)
-{
- int i, num = 0;
-
- for (i = 0; i < n; ++i) {
- if (formats[i].types & type) {
- /* index-th format of type type found ? */
- if (num == f->index)
- break;
- /* Correct type but haven't reached our index yet,
- * just increment per-type index */
- ++num;
- }
- }
-
- /* Format not found */
- if (i >= n)
- return -EINVAL;
-
- strlcpy(f->description, formats[i].name, sizeof(f->description));
- f->pixelformat = formats[i].fourcc;
-
- return 0;
-}
-
-static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
- MEM2MEM_CAPTURE);
-
- return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_CAPTURE);
-}
-
-static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->mode == S5P_JPEG_ENCODE)
- return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
- MEM2MEM_OUTPUT);
-
- return enum_fmt(formats_dec, NUM_FORMATS_DEC, f, MEM2MEM_OUTPUT);
-}
-
-static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
- enum v4l2_buf_type type)
-{
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return &ctx->out_q;
- if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return &ctx->cap_q;
-
- return NULL;
-}
-
-static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct s5p_jpeg_q_data *q_data = NULL;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
-
- vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed)
- return -EINVAL;
- q_data = get_q_data(ct, f->type);
- BUG_ON(q_data == NULL);
-
- pix->width = q_data->w;
- pix->height = q_data->h;
- pix->field = V4L2_FIELD_NONE;
- pix->pixelformat = q_data->fmt->fourcc;
- pix->bytesperline = 0;
- if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG) {
- u32 bpl = q_data->w;
- if (q_data->fmt->colplanes == 1)
- bpl = (bpl * q_data->fmt->depth) >> 3;
- pix->bytesperline = bpl;
- }
- pix->sizeimage = q_data->size;
-
- return 0;
-}
-
-static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
- u32 pixelformat)
-{
- unsigned int k;
- struct s5p_jpeg_fmt *formats;
- int n;
-
- if (mode == S5P_JPEG_ENCODE) {
- formats = formats_enc;
- n = NUM_FORMATS_ENC;
- } else {
- formats = formats_dec;
- n = NUM_FORMATS_DEC;
- }
-
- for (k = 0; k < n; k++) {
- struct s5p_jpeg_fmt *fmt = &formats[k];
- if (fmt->fourcc == pixelformat)
- return fmt;
- }
-
- return NULL;
-
-}
-
-static void jpeg_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
- unsigned int walign,
- u32 *h, unsigned int hmin, unsigned int hmax,
- unsigned int halign)
-{
- int width, height, w_step, h_step;
-
- width = *w;
- height = *h;
-
- w_step = 1 << walign;
- h_step = 1 << halign;
- v4l_bound_align_image(w, wmin, wmax, walign, h, hmin, hmax, halign, 0);
-
- if (*w < width && (*w + w_step) < wmax)
- *w += w_step;
- if (*h < height && (*h + h_step) < hmax)
- *h += h_step;
-
-}
-
-static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
- struct s5p_jpeg_ctx *ctx, int q_type)
-{
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_NONE;
- else if (pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- /* V4L2 specification suggests the driver corrects the format struct
- * if any of the dimensions is unsupported */
- if (q_type == MEM2MEM_OUTPUT)
- jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
- S5P_JPEG_MAX_WIDTH, 0,
- &pix->height, S5P_JPEG_MIN_HEIGHT,
- S5P_JPEG_MAX_HEIGHT, 0);
- else
- jpeg_bound_align_image(&pix->width, S5P_JPEG_MIN_WIDTH,
- S5P_JPEG_MAX_WIDTH, fmt->h_align,
- &pix->height, S5P_JPEG_MIN_HEIGHT,
- S5P_JPEG_MAX_HEIGHT, fmt->v_align);
-
- if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
- if (pix->sizeimage <= 0)
- pix->sizeimage = PAGE_SIZE;
- pix->bytesperline = 0;
- } else {
- u32 bpl = pix->bytesperline;
-
- if (fmt->colplanes > 1 && bpl < pix->width)
- bpl = pix->width; /* planar */
-
- if (fmt->colplanes == 1 && /* packed */
- (bpl << 3) * fmt->depth < pix->width)
- bpl = (pix->width * fmt->depth) >> 3;
-
- pix->bytesperline = bpl;
- pix->sizeimage = (pix->width * pix->height * fmt->depth) >> 3;
- }
-
- return 0;
-}
-
-static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
- struct s5p_jpeg_fmt *fmt;
-
- fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
- if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
- v4l2_err(&ctx->jpeg->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_CAPTURE);
-}
-
-static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
- struct s5p_jpeg_fmt *fmt;
-
- fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
- if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
- v4l2_err(&ctx->jpeg->v4l2_dev,
- "Fourcc format (0x%08x) invalid.\n",
- f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- return vidioc_try_fmt(f, fmt, ctx, MEM2MEM_OUTPUT);
-}
-
-static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
-{
- struct vb2_queue *vq;
- struct s5p_jpeg_q_data *q_data = NULL;
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
- q_data = get_q_data(ct, f->type);
- BUG_ON(q_data == NULL);
-
- if (vb2_is_busy(vq)) {
- v4l2_err(&ct->jpeg->v4l2_dev, "%s queue busy\n", __func__);
- return -EBUSY;
- }
-
- q_data->fmt = s5p_jpeg_find_format(ct->mode, pix->pixelformat);
- q_data->w = pix->width;
- q_data->h = pix->height;
- if (q_data->fmt->fourcc != V4L2_PIX_FMT_JPEG)
- q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
- else
- q_data->size = pix->sizeimage;
-
- return 0;
-}
-
-static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = s5p_jpeg_try_fmt_vid_cap(file, priv, f);
- if (ret)
- return ret;
-
- return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
-}
-
-static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- int ret;
-
- ret = s5p_jpeg_try_fmt_vid_out(file, priv, f);
- if (ret)
- return ret;
-
- return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
-}
-
-static int s5p_jpeg_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int s5p_jpeg_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_dqbuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int s5p_jpeg_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int s5p_jpeg_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static int s5p_jpeg_g_selection(struct file *file, void *priv,
- struct v4l2_selection *s)
-{
- struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* For JPEG blob active == default == bounds */
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE:
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- s->r.width = ctx->out_q.w;
- s->r.height = ctx->out_q.h;
- break;
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- case V4L2_SEL_TGT_COMPOSE_PADDED:
- s->r.width = ctx->cap_q.w;
- s->r.height = ctx->cap_q.h;
- break;
- default:
- return -EINVAL;
- }
- s->r.left = 0;
- s->r.top = 0;
- return 0;
-}
-
-/*
- * V4L2 controls
- */
-
-static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
- struct s5p_jpeg *jpeg = ctx->jpeg;
- unsigned long flags;
-
- switch (ctrl->id) {
- case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
- spin_lock_irqsave(&jpeg->slock, flags);
-
- WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
- if (ctx->subsampling > 2)
- ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
- else
- ctrl->val = ctx->subsampling;
- spin_unlock_irqrestore(&jpeg->slock, flags);
- break;
- }
-
- return 0;
-}
-
-static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
- unsigned long flags;
-
- spin_lock_irqsave(&ctx->jpeg->slock, flags);
-
- switch (ctrl->id) {
- case V4L2_CID_JPEG_COMPRESSION_QUALITY:
- ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
- break;
- case V4L2_CID_JPEG_RESTART_INTERVAL:
- ctx->restart_interval = ctrl->val;
- break;
- case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
- ctx->subsampling = ctrl->val;
- break;
- }
-
- spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
- return 0;
-}
-
-static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
- .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
- .s_ctrl = s5p_jpeg_s_ctrl,
-};
-
-static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
-{
- unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
- struct v4l2_ctrl *ctrl;
-
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
-
- if (ctx->mode == S5P_JPEG_ENCODE) {
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
- V4L2_CID_JPEG_COMPRESSION_QUALITY,
- 0, 3, 1, 3);
-
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
- V4L2_CID_JPEG_RESTART_INTERVAL,
- 0, 3, 0xffff, 0);
- mask = ~0x06; /* 422, 420 */
- }
-
- ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
- V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
- V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
- V4L2_JPEG_CHROMA_SUBSAMPLING_422);
-
- if (ctx->ctrl_handler.error)
- return ctx->ctrl_handler.error;
-
- if (ctx->mode == S5P_JPEG_DECODE)
- ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
- V4L2_CTRL_FLAG_READ_ONLY;
- return 0;
-}
-
-static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
- .vidioc_querycap = s5p_jpeg_querycap,
-
- .vidioc_enum_fmt_vid_cap = s5p_jpeg_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_out = s5p_jpeg_enum_fmt_vid_out,
-
- .vidioc_g_fmt_vid_cap = s5p_jpeg_g_fmt,
- .vidioc_g_fmt_vid_out = s5p_jpeg_g_fmt,
-
- .vidioc_try_fmt_vid_cap = s5p_jpeg_try_fmt_vid_cap,
- .vidioc_try_fmt_vid_out = s5p_jpeg_try_fmt_vid_out,
-
- .vidioc_s_fmt_vid_cap = s5p_jpeg_s_fmt_vid_cap,
- .vidioc_s_fmt_vid_out = s5p_jpeg_s_fmt_vid_out,
-
- .vidioc_reqbufs = s5p_jpeg_reqbufs,
- .vidioc_querybuf = s5p_jpeg_querybuf,
-
- .vidioc_qbuf = s5p_jpeg_qbuf,
- .vidioc_dqbuf = s5p_jpeg_dqbuf,
-
- .vidioc_streamon = s5p_jpeg_streamon,
- .vidioc_streamoff = s5p_jpeg_streamoff,
-
- .vidioc_g_selection = s5p_jpeg_g_selection,
-};
-
-/*
- * ============================================================================
- * mem2mem callbacks
- * ============================================================================
- */
-
-static void s5p_jpeg_device_run(void *priv)
-{
- struct s5p_jpeg_ctx *ctx = priv;
- struct s5p_jpeg *jpeg = ctx->jpeg;
- struct vb2_buffer *src_buf, *dst_buf;
- unsigned long src_addr, dst_addr;
-
- src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
-
- jpeg_reset(jpeg->regs);
- jpeg_poweron(jpeg->regs);
- jpeg_proc_mode(jpeg->regs, ctx->mode);
- if (ctx->mode == S5P_JPEG_ENCODE) {
- if (ctx->out_q.fmt->fourcc == V4L2_PIX_FMT_RGB565)
- jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
- else
- jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
- jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
- jpeg_dri(jpeg->regs, ctx->restart_interval);
- jpeg_x(jpeg->regs, ctx->out_q.w);
- jpeg_y(jpeg->regs, ctx->out_q.h);
- jpeg_imgadr(jpeg->regs, src_addr);
- jpeg_jpgadr(jpeg->regs, dst_addr);
-
- /* ultimately comes from sizeimage from userspace */
- jpeg_enc_stream_int(jpeg->regs, ctx->cap_q.size);
-
- /* JPEG RGB to YCbCr conversion matrix */
- jpeg_coef(jpeg->regs, 1, 1, S5P_JPEG_COEF11);
- jpeg_coef(jpeg->regs, 1, 2, S5P_JPEG_COEF12);
- jpeg_coef(jpeg->regs, 1, 3, S5P_JPEG_COEF13);
- jpeg_coef(jpeg->regs, 2, 1, S5P_JPEG_COEF21);
- jpeg_coef(jpeg->regs, 2, 2, S5P_JPEG_COEF22);
- jpeg_coef(jpeg->regs, 2, 3, S5P_JPEG_COEF23);
- jpeg_coef(jpeg->regs, 3, 1, S5P_JPEG_COEF31);
- jpeg_coef(jpeg->regs, 3, 2, S5P_JPEG_COEF32);
- jpeg_coef(jpeg->regs, 3, 3, S5P_JPEG_COEF33);
-
- /*
- * JPEG IP allows storing 4 quantization tables
- * We fill table 0 for luma and table 1 for chroma
- */
- jpeg_set_qtbl_lum(jpeg->regs, ctx->compr_quality);
- jpeg_set_qtbl_chr(jpeg->regs, ctx->compr_quality);
- /* use table 0 for Y */
- jpeg_qtbl(jpeg->regs, 1, 0);
- /* use table 1 for Cb and Cr*/
- jpeg_qtbl(jpeg->regs, 2, 1);
- jpeg_qtbl(jpeg->regs, 3, 1);
-
- /* Y, Cb, Cr use Huffman table 0 */
- jpeg_htbl_ac(jpeg->regs, 1);
- jpeg_htbl_dc(jpeg->regs, 1);
- jpeg_htbl_ac(jpeg->regs, 2);
- jpeg_htbl_dc(jpeg->regs, 2);
- jpeg_htbl_ac(jpeg->regs, 3);
- jpeg_htbl_dc(jpeg->regs, 3);
- } else { /* S5P_JPEG_DECODE */
- jpeg_rst_int_enable(jpeg->regs, true);
- jpeg_data_num_int_enable(jpeg->regs, true);
- jpeg_final_mcu_num_int_enable(jpeg->regs, true);
- if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
- else
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
- jpeg_jpgadr(jpeg->regs, src_addr);
- jpeg_imgadr(jpeg->regs, dst_addr);
- }
-
- jpeg_start(jpeg->regs);
-}
-
-static int s5p_jpeg_job_ready(void *priv)
-{
- struct s5p_jpeg_ctx *ctx = priv;
-
- if (ctx->mode == S5P_JPEG_DECODE)
- return ctx->hdr_parsed;
- return 1;
-}
-
-static void s5p_jpeg_job_abort(void *priv)
-{
-}
-
-static struct v4l2_m2m_ops s5p_jpeg_m2m_ops = {
- .device_run = s5p_jpeg_device_run,
- .job_ready = s5p_jpeg_job_ready,
- .job_abort = s5p_jpeg_job_abort,
-};
-
-/*
- * ============================================================================
- * Queue operations
- * ============================================================================
- */
-
-static int s5p_jpeg_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
- struct s5p_jpeg_q_data *q_data = NULL;
- unsigned int size, count = *nbuffers;
-
- q_data = get_q_data(ctx, vq->type);
- BUG_ON(q_data == NULL);
-
- size = q_data->size;
-
- /*
- * header is parsed during decoding and parsed information stored
- * in the context so we do not allow another buffer to overwrite it
- */
- if (ctx->mode == S5P_JPEG_DECODE)
- count = 1;
-
- *nbuffers = count;
- *nplanes = 1;
- sizes[0] = size;
- alloc_ctxs[0] = ctx->jpeg->alloc_ctx;
-
- return 0;
-}
-
-static int s5p_jpeg_buf_prepare(struct vb2_buffer *vb)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct s5p_jpeg_q_data *q_data = NULL;
-
- q_data = get_q_data(ctx, vb->vb2_queue->type);
- BUG_ON(q_data == NULL);
-
- if (vb2_plane_size(vb, 0) < q_data->size) {
- pr_err("%s data will not fit into plane (%lu < %lu)\n",
- __func__, vb2_plane_size(vb, 0),
- (long)q_data->size);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(vb, 0, q_data->size);
-
- return 0;
-}
-
-static void s5p_jpeg_buf_queue(struct vb2_buffer *vb)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- if (ctx->mode == S5P_JPEG_DECODE &&
- vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- struct s5p_jpeg_q_data tmp, *q_data;
- ctx->hdr_parsed = s5p_jpeg_parse_hdr(&tmp,
- (unsigned long)vb2_plane_vaddr(vb, 0),
- min((unsigned long)ctx->out_q.size,
- vb2_get_plane_payload(vb, 0)));
- if (!ctx->hdr_parsed) {
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- return;
- }
-
- q_data = &ctx->out_q;
- q_data->w = tmp.w;
- q_data->h = tmp.h;
-
- q_data = &ctx->cap_q;
- q_data->w = tmp.w;
- q_data->h = tmp.h;
-
- jpeg_bound_align_image(&q_data->w, S5P_JPEG_MIN_WIDTH,
- S5P_JPEG_MAX_WIDTH, q_data->fmt->h_align,
- &q_data->h, S5P_JPEG_MIN_HEIGHT,
- S5P_JPEG_MAX_HEIGHT, q_data->fmt->v_align
- );
- q_data->size = q_data->w * q_data->h * q_data->fmt->depth >> 3;
- }
- if (ctx->m2m_ctx)
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void s5p_jpeg_wait_prepare(struct vb2_queue *vq)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
-
- mutex_unlock(&ctx->jpeg->lock);
-}
-
-static void s5p_jpeg_wait_finish(struct vb2_queue *vq)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(vq);
-
- mutex_lock(&ctx->jpeg->lock);
-}
-
-static int s5p_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
- int ret;
-
- ret = pm_runtime_get_sync(ctx->jpeg->dev);
-
- return ret > 0 ? 0 : ret;
-}
-
-static int s5p_jpeg_stop_streaming(struct vb2_queue *q)
-{
- struct s5p_jpeg_ctx *ctx = vb2_get_drv_priv(q);
-
- pm_runtime_put(ctx->jpeg->dev);
-
- return 0;
-}
-
-static struct vb2_ops s5p_jpeg_qops = {
- .queue_setup = s5p_jpeg_queue_setup,
- .buf_prepare = s5p_jpeg_buf_prepare,
- .buf_queue = s5p_jpeg_buf_queue,
- .wait_prepare = s5p_jpeg_wait_prepare,
- .wait_finish = s5p_jpeg_wait_finish,
- .start_streaming = s5p_jpeg_start_streaming,
- .stop_streaming = s5p_jpeg_stop_streaming,
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct s5p_jpeg_ctx *ctx = priv;
- int ret;
-
- memset(src_vq, 0, sizeof(*src_vq));
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
- src_vq->drv_priv = ctx;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- src_vq->ops = &s5p_jpeg_qops;
- src_vq->mem_ops = &vb2_dma_contig_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 | VB2_USERPTR;
- dst_vq->drv_priv = ctx;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
- dst_vq->ops = &s5p_jpeg_qops;
- dst_vq->mem_ops = &vb2_dma_contig_memops;
-
- return vb2_queue_init(dst_vq);
-}
-
-/*
- * ============================================================================
- * ISR
- * ============================================================================
- */
-
-static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
-{
- struct s5p_jpeg *jpeg = dev_id;
- struct s5p_jpeg_ctx *curr_ctx;
- struct vb2_buffer *src_buf, *dst_buf;
- unsigned long payload_size = 0;
- enum vb2_buffer_state state = VB2_BUF_STATE_DONE;
- bool enc_jpeg_too_large = false;
- bool timer_elapsed = false;
- bool op_completed = false;
-
- spin_lock(&jpeg->slock);
-
- curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
-
- src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
- dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
-
- if (curr_ctx->mode == S5P_JPEG_ENCODE)
- enc_jpeg_too_large = jpeg_enc_stream_stat(jpeg->regs);
- timer_elapsed = jpeg_timer_stat(jpeg->regs);
- op_completed = jpeg_result_stat_ok(jpeg->regs);
- if (curr_ctx->mode == S5P_JPEG_DECODE)
- op_completed = op_completed && jpeg_stream_stat_ok(jpeg->regs);
-
- if (enc_jpeg_too_large) {
- state = VB2_BUF_STATE_ERROR;
- jpeg_clear_enc_stream_stat(jpeg->regs);
- } else if (timer_elapsed) {
- state = VB2_BUF_STATE_ERROR;
- jpeg_clear_timer_stat(jpeg->regs);
- } else if (!op_completed) {
- state = VB2_BUF_STATE_ERROR;
- } else {
- payload_size = jpeg_compressed_size(jpeg->regs);
- }
-
- v4l2_m2m_buf_done(src_buf, state);
- if (curr_ctx->mode == S5P_JPEG_ENCODE)
- vb2_set_plane_payload(dst_buf, 0, payload_size);
- v4l2_m2m_buf_done(dst_buf, state);
- v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
-
- curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
- spin_unlock(&jpeg->slock);
-
- jpeg_clear_int(jpeg->regs);
-
- return IRQ_HANDLED;
-}
-
-/*
- * ============================================================================
- * Driver basic infrastructure
- * ============================================================================
- */
-
-static int s5p_jpeg_probe(struct platform_device *pdev)
-{
- struct s5p_jpeg *jpeg;
- struct resource *res;
- int ret;
-
- /* JPEG IP abstraction struct */
- jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL);
- if (!jpeg)
- return -ENOMEM;
-
- mutex_init(&jpeg->lock);
- spin_lock_init(&jpeg->slock);
- jpeg->dev = &pdev->dev;
-
- /* memory-mapped registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- jpeg->regs = devm_request_and_ioremap(&pdev->dev, res);
- if (jpeg->regs == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
-
- /* interrupt service routine registration */
- jpeg->irq = ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(&pdev->dev, "cannot find IRQ\n");
- return ret;
- }
-
- ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0,
- dev_name(&pdev->dev), jpeg);
- if (ret) {
- dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);
- return ret;
- }
-
- /* clocks */
- jpeg->clk = clk_get(&pdev->dev, "jpeg");
- if (IS_ERR(jpeg->clk)) {
- dev_err(&pdev->dev, "cannot get clock\n");
- ret = PTR_ERR(jpeg->clk);
- return ret;
- }
- dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk);
- clk_enable(jpeg->clk);
-
- /* v4l2 device */
- ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register v4l2 device\n");
- goto clk_get_rollback;
- }
-
- /* mem2mem device */
- jpeg->m2m_dev = v4l2_m2m_init(&s5p_jpeg_m2m_ops);
- if (IS_ERR(jpeg->m2m_dev)) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to init mem2mem device\n");
- ret = PTR_ERR(jpeg->m2m_dev);
- goto device_register_rollback;
- }
-
- jpeg->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(jpeg->alloc_ctx)) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to init memory allocator\n");
- ret = PTR_ERR(jpeg->alloc_ctx);
- goto m2m_init_rollback;
- }
-
- /* JPEG encoder /dev/videoX node */
- jpeg->vfd_encoder = video_device_alloc();
- if (!jpeg->vfd_encoder) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto vb2_allocator_rollback;
- }
- strlcpy(jpeg->vfd_encoder->name, S5P_JPEG_M2M_NAME,
- sizeof(jpeg->vfd_encoder->name));
- jpeg->vfd_encoder->fops = &s5p_jpeg_fops;
- jpeg->vfd_encoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
- jpeg->vfd_encoder->minor = -1;
- jpeg->vfd_encoder->release = video_device_release;
- jpeg->vfd_encoder->lock = &jpeg->lock;
- jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_encoder->flags);
-
- ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
- goto enc_vdev_alloc_rollback;
- }
-
- video_set_drvdata(jpeg->vfd_encoder, jpeg);
- v4l2_info(&jpeg->v4l2_dev,
- "encoder device registered as /dev/video%d\n",
- jpeg->vfd_encoder->num);
-
- /* JPEG decoder /dev/videoX node */
- jpeg->vfd_decoder = video_device_alloc();
- if (!jpeg->vfd_decoder) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto enc_vdev_register_rollback;
- }
- strlcpy(jpeg->vfd_decoder->name, S5P_JPEG_M2M_NAME,
- sizeof(jpeg->vfd_decoder->name));
- jpeg->vfd_decoder->fops = &s5p_jpeg_fops;
- jpeg->vfd_decoder->ioctl_ops = &s5p_jpeg_ioctl_ops;
- jpeg->vfd_decoder->minor = -1;
- jpeg->vfd_decoder->release = video_device_release;
- jpeg->vfd_decoder->lock = &jpeg->lock;
- jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev;
- /* Locking in file operations other than ioctl should be done by the driver,
- not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_decoder->flags);
-
- ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
- if (ret) {
- v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
- goto dec_vdev_alloc_rollback;
- }
-
- video_set_drvdata(jpeg->vfd_decoder, jpeg);
- v4l2_info(&jpeg->v4l2_dev,
- "decoder device registered as /dev/video%d\n",
- jpeg->vfd_decoder->num);
-
- /* final statements & power management */
- platform_set_drvdata(pdev, jpeg);
-
- pm_runtime_enable(&pdev->dev);
-
- v4l2_info(&jpeg->v4l2_dev, "Samsung S5P JPEG codec\n");
-
- return 0;
-
-dec_vdev_alloc_rollback:
- video_device_release(jpeg->vfd_decoder);
-
-enc_vdev_register_rollback:
- video_unregister_device(jpeg->vfd_encoder);
-
-enc_vdev_alloc_rollback:
- video_device_release(jpeg->vfd_encoder);
-
-vb2_allocator_rollback:
- vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
-
-m2m_init_rollback:
- v4l2_m2m_release(jpeg->m2m_dev);
-
-device_register_rollback:
- v4l2_device_unregister(&jpeg->v4l2_dev);
-
-clk_get_rollback:
- clk_disable(jpeg->clk);
- clk_put(jpeg->clk);
-
- return ret;
-}
-
-static int s5p_jpeg_remove(struct platform_device *pdev)
-{
- struct s5p_jpeg *jpeg = platform_get_drvdata(pdev);
-
- pm_runtime_disable(jpeg->dev);
-
- video_unregister_device(jpeg->vfd_decoder);
- video_device_release(jpeg->vfd_decoder);
- video_unregister_device(jpeg->vfd_encoder);
- video_device_release(jpeg->vfd_encoder);
- vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
- v4l2_m2m_release(jpeg->m2m_dev);
- v4l2_device_unregister(&jpeg->v4l2_dev);
-
- clk_disable(jpeg->clk);
- clk_put(jpeg->clk);
-
- return 0;
-}
-
-static int s5p_jpeg_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int s5p_jpeg_runtime_resume(struct device *dev)
-{
- struct s5p_jpeg *jpeg = dev_get_drvdata(dev);
- /*
- * JPEG IP allows storing two Huffman tables for each component
- * We fill table 0 for each component
- */
- jpeg_set_hdctbl(jpeg->regs);
- jpeg_set_hdctblg(jpeg->regs);
- jpeg_set_hactbl(jpeg->regs);
- jpeg_set_hactblg(jpeg->regs);
- return 0;
-}
-
-static const struct dev_pm_ops s5p_jpeg_pm_ops = {
- .runtime_suspend = s5p_jpeg_runtime_suspend,
- .runtime_resume = s5p_jpeg_runtime_resume,
-};
-
-static struct platform_driver s5p_jpeg_driver = {
- .probe = s5p_jpeg_probe,
- .remove = s5p_jpeg_remove,
- .driver = {
- .owner = THIS_MODULE,
- .name = S5P_JPEG_M2M_NAME,
- .pm = &s5p_jpeg_pm_ops,
- },
-};
-
-module_platform_driver(s5p_jpeg_driver);
-
-MODULE_AUTHOR("Andrzej Pietrasiewicz <andrzej.p@samsung.com>");
-MODULE_DESCRIPTION("Samsung JPEG codec driver");
-MODULE_LICENSE("GPL");
-
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h
deleted file mode 100644
index 9d0cd2b76f61..000000000000
--- a/drivers/media/video/s5p-jpeg/jpeg-core.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/* linux/drivers/media/video/s5p-jpeg/jpeg-core.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef JPEG_CORE_H_
-#define JPEG_CORE_H_
-
-#include <media/v4l2-device.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-ctrls.h>
-
-#define S5P_JPEG_M2M_NAME "s5p-jpeg"
-
-/* JPEG compression quality setting */
-#define S5P_JPEG_COMPR_QUAL_BEST 0
-#define S5P_JPEG_COMPR_QUAL_WORST 3
-
-/* JPEG RGB to YCbCr conversion matrix coefficients */
-#define S5P_JPEG_COEF11 0x4d
-#define S5P_JPEG_COEF12 0x97
-#define S5P_JPEG_COEF13 0x1e
-#define S5P_JPEG_COEF21 0x2c
-#define S5P_JPEG_COEF22 0x57
-#define S5P_JPEG_COEF23 0x83
-#define S5P_JPEG_COEF31 0x83
-#define S5P_JPEG_COEF32 0x6e
-#define S5P_JPEG_COEF33 0x13
-
-/* a selection of JPEG markers */
-#define TEM 0x01
-#define SOF0 0xc0
-#define RST 0xd0
-#define SOI 0xd8
-#define EOI 0xd9
-#define DHP 0xde
-
-/* Flags that indicate a format can be used for capture/output */
-#define MEM2MEM_CAPTURE (1 << 0)
-#define MEM2MEM_OUTPUT (1 << 1)
-
-/**
- * struct s5p_jpeg - JPEG IP abstraction
- * @lock: the mutex protecting this structure
- * @slock: spinlock protecting the device contexts
- * @v4l2_dev: v4l2 device for mem2mem mode
- * @vfd_encoder: video device node for encoder mem2mem mode
- * @vfd_decoder: video device node for decoder mem2mem mode
- * @m2m_dev: v4l2 mem2mem device data
- * @regs: JPEG IP registers mapping
- * @irq: JPEG IP irq
- * @clk: JPEG IP clock
- * @dev: JPEG IP struct device
- * @alloc_ctx: videobuf2 memory allocator's context
- */
-struct s5p_jpeg {
- struct mutex lock;
- struct spinlock slock;
-
- struct v4l2_device v4l2_dev;
- struct video_device *vfd_encoder;
- struct video_device *vfd_decoder;
- struct v4l2_m2m_dev *m2m_dev;
-
- void __iomem *regs;
- unsigned int irq;
- struct clk *clk;
- struct device *dev;
- void *alloc_ctx;
-};
-
-/**
- * struct jpeg_fmt - driver's internal color format data
- * @name: format descritpion
- * @fourcc: the fourcc code, 0 if not applicable
- * @depth: number of bits per pixel
- * @colplanes: number of color planes (1 for packed formats)
- * @h_align: horizontal alignment order (align to 2^h_align)
- * @v_align: vertical alignment order (align to 2^v_align)
- * @types: types of queue this format is applicable to
- */
-struct s5p_jpeg_fmt {
- char *name;
- u32 fourcc;
- int depth;
- int colplanes;
- int h_align;
- int v_align;
- u32 types;
-};
-
-/**
- * s5p_jpeg_q_data - parameters of one queue
- * @fmt: driver-specific format of this queue
- * @w: image width
- * @h: image height
- * @size: image buffer size in bytes
- */
-struct s5p_jpeg_q_data {
- struct s5p_jpeg_fmt *fmt;
- u32 w;
- u32 h;
- u32 size;
-};
-
-/**
- * s5p_jpeg_ctx - the device context data
- * @jpeg: JPEG IP device for this context
- * @mode: compression (encode) operation or decompression (decode)
- * @compr_quality: destination image quality in compression (encode) mode
- * @m2m_ctx: mem2mem device context
- * @out_q: source (output) queue information
- * @cap_fmt: destination (capture) queue queue information
- * @hdr_parsed: set if header has been parsed during decompression
- * @ctrl_handler: controls handler
- */
-struct s5p_jpeg_ctx {
- struct s5p_jpeg *jpeg;
- unsigned int mode;
- unsigned short compr_quality;
- unsigned short restart_interval;
- unsigned short subsampling;
- struct v4l2_m2m_ctx *m2m_ctx;
- struct s5p_jpeg_q_data out_q;
- struct s5p_jpeg_q_data cap_q;
- struct v4l2_fh fh;
- bool hdr_parsed;
- struct v4l2_ctrl_handler ctrl_handler;
-};
-
-/**
- * s5p_jpeg_buffer - description of memory containing input JPEG data
- * @size: buffer size
- * @curr: current position in the buffer
- * @data: pointer to the data
- */
-struct s5p_jpeg_buffer {
- unsigned long size;
- unsigned long curr;
- unsigned long data;
-};
-
-#endif /* JPEG_CORE_H */
diff --git a/drivers/media/video/s5p-jpeg/jpeg-hw.h b/drivers/media/video/s5p-jpeg/jpeg-hw.h
deleted file mode 100644
index f12f0fdbde7c..000000000000
--- a/drivers/media/video/s5p-jpeg/jpeg-hw.h
+++ /dev/null
@@ -1,357 +0,0 @@
-/* linux/drivers/media/video/s5p-jpeg/jpeg-hw.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef JPEG_HW_H_
-#define JPEG_HW_H_
-
-#include <linux/io.h>
-#include <linux/videodev2.h>
-
-#include "jpeg-hw.h"
-#include "jpeg-regs.h"
-
-#define S5P_JPEG_MIN_WIDTH 32
-#define S5P_JPEG_MIN_HEIGHT 32
-#define S5P_JPEG_MAX_WIDTH 8192
-#define S5P_JPEG_MAX_HEIGHT 8192
-#define S5P_JPEG_ENCODE 0
-#define S5P_JPEG_DECODE 1
-#define S5P_JPEG_RAW_IN_565 0
-#define S5P_JPEG_RAW_IN_422 1
-#define S5P_JPEG_RAW_OUT_422 0
-#define S5P_JPEG_RAW_OUT_420 1
-
-static inline void jpeg_reset(void __iomem *regs)
-{
- unsigned long reg;
-
- writel(1, regs + S5P_JPG_SW_RESET);
- reg = readl(regs + S5P_JPG_SW_RESET);
- /* no other way but polling for when JPEG IP becomes operational */
- while (reg != 0) {
- cpu_relax();
- reg = readl(regs + S5P_JPG_SW_RESET);
- }
-}
-
-static inline void jpeg_poweron(void __iomem *regs)
-{
- writel(S5P_POWER_ON, regs + S5P_JPGCLKCON);
-}
-
-static inline void jpeg_input_raw_mode(void __iomem *regs, unsigned long mode)
-{
- unsigned long reg, m;
-
- m = S5P_MOD_SEL_565;
- if (mode == S5P_JPEG_RAW_IN_565)
- m = S5P_MOD_SEL_565;
- else if (mode == S5P_JPEG_RAW_IN_422)
- m = S5P_MOD_SEL_422;
-
- reg = readl(regs + S5P_JPGCMOD);
- reg &= ~S5P_MOD_SEL_MASK;
- reg |= m;
- writel(reg, regs + S5P_JPGCMOD);
-}
-
-static inline void jpeg_input_raw_y16(void __iomem *regs, bool y16)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGCMOD);
- if (y16)
- reg |= S5P_MODE_Y16;
- else
- reg &= ~S5P_MODE_Y16_MASK;
- writel(reg, regs + S5P_JPGCMOD);
-}
-
-static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
-{
- unsigned long reg, m;
-
- m = S5P_PROC_MODE_DECOMPR;
- if (mode == S5P_JPEG_ENCODE)
- m = S5P_PROC_MODE_COMPR;
- else
- m = S5P_PROC_MODE_DECOMPR;
- reg = readl(regs + S5P_JPGMOD);
- reg &= ~S5P_PROC_MODE_MASK;
- reg |= m;
- writel(reg, regs + S5P_JPGMOD);
-}
-
-static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
-{
- unsigned long reg, m;
-
- if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
- m = S5P_SUBSAMPLING_MODE_420;
- else
- m = S5P_SUBSAMPLING_MODE_422;
-
- reg = readl(regs + S5P_JPGMOD);
- reg &= ~S5P_SUBSAMPLING_MODE_MASK;
- reg |= m;
- writel(reg, regs + S5P_JPGMOD);
-}
-
-static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
-{
- return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
-}
-
-static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGDRI_U);
- reg &= ~0xff;
- reg |= (dri >> 8) & 0xff;
- writel(reg, regs + S5P_JPGDRI_U);
-
- reg = readl(regs + S5P_JPGDRI_L);
- reg &= ~0xff;
- reg |= dri & 0xff;
- writel(reg, regs + S5P_JPGDRI_L);
-}
-
-static inline void jpeg_qtbl(void __iomem *regs, unsigned int t, unsigned int n)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_QTBL);
- reg &= ~S5P_QT_NUMt_MASK(t);
- reg |= (n << S5P_QT_NUMt_SHIFT(t)) & S5P_QT_NUMt_MASK(t);
- writel(reg, regs + S5P_JPG_QTBL);
-}
-
-static inline void jpeg_htbl_ac(void __iomem *regs, unsigned int t)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_HTBL);
- reg &= ~S5P_HT_NUMt_AC_MASK(t);
- /* this driver uses table 0 for all color components */
- reg |= (0 << S5P_HT_NUMt_AC_SHIFT(t)) & S5P_HT_NUMt_AC_MASK(t);
- writel(reg, regs + S5P_JPG_HTBL);
-}
-
-static inline void jpeg_htbl_dc(void __iomem *regs, unsigned int t)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_HTBL);
- reg &= ~S5P_HT_NUMt_DC_MASK(t);
- /* this driver uses table 0 for all color components */
- reg |= (0 << S5P_HT_NUMt_DC_SHIFT(t)) & S5P_HT_NUMt_DC_MASK(t);
- writel(reg, regs + S5P_JPG_HTBL);
-}
-
-static inline void jpeg_y(void __iomem *regs, unsigned int y)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGY_U);
- reg &= ~0xff;
- reg |= (y >> 8) & 0xff;
- writel(reg, regs + S5P_JPGY_U);
-
- reg = readl(regs + S5P_JPGY_L);
- reg &= ~0xff;
- reg |= y & 0xff;
- writel(reg, regs + S5P_JPGY_L);
-}
-
-static inline void jpeg_x(void __iomem *regs, unsigned int x)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGX_U);
- reg &= ~0xff;
- reg |= (x >> 8) & 0xff;
- writel(reg, regs + S5P_JPGX_U);
-
- reg = readl(regs + S5P_JPGX_L);
- reg &= ~0xff;
- reg |= x & 0xff;
- writel(reg, regs + S5P_JPGX_L);
-}
-
-static inline void jpeg_rst_int_enable(void __iomem *regs, bool enable)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGINTSE);
- reg &= ~S5P_RSTm_INT_EN_MASK;
- if (enable)
- reg |= S5P_RSTm_INT_EN;
- writel(reg, regs + S5P_JPGINTSE);
-}
-
-static inline void jpeg_data_num_int_enable(void __iomem *regs, bool enable)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGINTSE);
- reg &= ~S5P_DATA_NUM_INT_EN_MASK;
- if (enable)
- reg |= S5P_DATA_NUM_INT_EN;
- writel(reg, regs + S5P_JPGINTSE);
-}
-
-static inline void jpeg_final_mcu_num_int_enable(void __iomem *regs, bool enbl)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGINTSE);
- reg &= ~S5P_FINAL_MCU_NUM_INT_EN_MASK;
- if (enbl)
- reg |= S5P_FINAL_MCU_NUM_INT_EN;
- writel(reg, regs + S5P_JPGINTSE);
-}
-
-static inline void jpeg_timer_enable(void __iomem *regs, unsigned long val)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_TIMER_SE);
- reg |= S5P_TIMER_INT_EN;
- reg &= ~S5P_TIMER_INIT_MASK;
- reg |= val & S5P_TIMER_INIT_MASK;
- writel(reg, regs + S5P_JPG_TIMER_SE);
-}
-
-static inline void jpeg_timer_disable(void __iomem *regs)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_TIMER_SE);
- reg &= ~S5P_TIMER_INT_EN_MASK;
- writel(reg, regs + S5P_JPG_TIMER_SE);
-}
-
-static inline int jpeg_timer_stat(void __iomem *regs)
-{
- return (int)((readl(regs + S5P_JPG_TIMER_ST) & S5P_TIMER_INT_STAT_MASK)
- >> S5P_TIMER_INT_STAT_SHIFT);
-}
-
-static inline void jpeg_clear_timer_stat(void __iomem *regs)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_TIMER_SE);
- reg &= ~S5P_TIMER_INT_STAT_MASK;
- writel(reg, regs + S5P_JPG_TIMER_SE);
-}
-
-static inline void jpeg_enc_stream_int(void __iomem *regs, unsigned long size)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
- reg &= ~S5P_ENC_STREAM_BOUND_MASK;
- reg |= S5P_ENC_STREAM_INT_EN;
- reg |= size & S5P_ENC_STREAM_BOUND_MASK;
- writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
-}
-
-static inline int jpeg_enc_stream_stat(void __iomem *regs)
-{
- return (int)(readl(regs + S5P_JPG_ENC_STREAM_INTST) &
- S5P_ENC_STREAM_INT_STAT_MASK);
-}
-
-static inline void jpeg_clear_enc_stream_stat(void __iomem *regs)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_ENC_STREAM_INTSE);
- reg &= ~S5P_ENC_STREAM_INT_MASK;
- writel(reg, regs + S5P_JPG_ENC_STREAM_INTSE);
-}
-
-static inline void jpeg_outform_raw(void __iomem *regs, unsigned long format)
-{
- unsigned long reg, f;
-
- f = S5P_DEC_OUT_FORMAT_422;
- if (format == S5P_JPEG_RAW_OUT_422)
- f = S5P_DEC_OUT_FORMAT_422;
- else if (format == S5P_JPEG_RAW_OUT_420)
- f = S5P_DEC_OUT_FORMAT_420;
- reg = readl(regs + S5P_JPG_OUTFORM);
- reg &= ~S5P_DEC_OUT_FORMAT_MASK;
- reg |= f;
- writel(reg, regs + S5P_JPG_OUTFORM);
-}
-
-static inline void jpeg_jpgadr(void __iomem *regs, unsigned long addr)
-{
- writel(addr, regs + S5P_JPG_JPGADR);
-}
-
-static inline void jpeg_imgadr(void __iomem *regs, unsigned long addr)
-{
- writel(addr, regs + S5P_JPG_IMGADR);
-}
-
-static inline void jpeg_coef(void __iomem *regs, unsigned int i,
- unsigned int j, unsigned int coef)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPG_COEF(i));
- reg &= ~S5P_COEFn_MASK(j);
- reg |= (coef << S5P_COEFn_SHIFT(j)) & S5P_COEFn_MASK(j);
- writel(reg, regs + S5P_JPG_COEF(i));
-}
-
-static inline void jpeg_start(void __iomem *regs)
-{
- writel(1, regs + S5P_JSTART);
-}
-
-static inline int jpeg_result_stat_ok(void __iomem *regs)
-{
- return (int)((readl(regs + S5P_JPGINTST) & S5P_RESULT_STAT_MASK)
- >> S5P_RESULT_STAT_SHIFT);
-}
-
-static inline int jpeg_stream_stat_ok(void __iomem *regs)
-{
- return !(int)((readl(regs + S5P_JPGINTST) & S5P_STREAM_STAT_MASK)
- >> S5P_STREAM_STAT_SHIFT);
-}
-
-static inline void jpeg_clear_int(void __iomem *regs)
-{
- unsigned long reg;
-
- reg = readl(regs + S5P_JPGINTST);
- writel(S5P_INT_RELEASE, regs + S5P_JPGCOM);
- reg = readl(regs + S5P_JPGOPR);
-}
-
-static inline unsigned int jpeg_compressed_size(void __iomem *regs)
-{
- unsigned long jpeg_size = 0;
-
- jpeg_size |= (readl(regs + S5P_JPGCNT_U) & 0xff) << 16;
- jpeg_size |= (readl(regs + S5P_JPGCNT_M) & 0xff) << 8;
- jpeg_size |= (readl(regs + S5P_JPGCNT_L) & 0xff);
-
- return (unsigned int)jpeg_size;
-}
-
-#endif /* JPEG_HW_H_ */
diff --git a/drivers/media/video/s5p-jpeg/jpeg-regs.h b/drivers/media/video/s5p-jpeg/jpeg-regs.h
deleted file mode 100644
index 91f4dd5f86dd..000000000000
--- a/drivers/media/video/s5p-jpeg/jpeg-regs.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/* linux/drivers/media/video/s5p-jpeg/jpeg-regs.h
- *
- * Register definition file for Samsung JPEG codec driver
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * 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 version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef JPEG_REGS_H_
-#define JPEG_REGS_H_
-
-/* JPEG mode register */
-#define S5P_JPGMOD 0x00
-#define S5P_PROC_MODE_MASK (0x1 << 3)
-#define S5P_PROC_MODE_DECOMPR (0x1 << 3)
-#define S5P_PROC_MODE_COMPR (0x0 << 3)
-#define S5P_SUBSAMPLING_MODE_MASK 0x7
-#define S5P_SUBSAMPLING_MODE_444 (0x0 << 0)
-#define S5P_SUBSAMPLING_MODE_422 (0x1 << 0)
-#define S5P_SUBSAMPLING_MODE_420 (0x2 << 0)
-#define S5P_SUBSAMPLING_MODE_GRAY (0x3 << 0)
-
-/* JPEG operation status register */
-#define S5P_JPGOPR 0x04
-
-/* Quantization tables*/
-#define S5P_JPG_QTBL 0x08
-#define S5P_QT_NUMt_SHIFT(t) (((t) - 1) << 1)
-#define S5P_QT_NUMt_MASK(t) (0x3 << S5P_QT_NUMt_SHIFT(t))
-
-/* Huffman tables */
-#define S5P_JPG_HTBL 0x0c
-#define S5P_HT_NUMt_AC_SHIFT(t) (((t) << 1) - 1)
-#define S5P_HT_NUMt_AC_MASK(t) (0x1 << S5P_HT_NUMt_AC_SHIFT(t))
-
-#define S5P_HT_NUMt_DC_SHIFT(t) (((t) - 1) << 1)
-#define S5P_HT_NUMt_DC_MASK(t) (0x1 << S5P_HT_NUMt_DC_SHIFT(t))
-
-/* JPEG restart interval register upper byte */
-#define S5P_JPGDRI_U 0x10
-
-/* JPEG restart interval register lower byte */
-#define S5P_JPGDRI_L 0x14
-
-/* JPEG vertical resolution register upper byte */
-#define S5P_JPGY_U 0x18
-
-/* JPEG vertical resolution register lower byte */
-#define S5P_JPGY_L 0x1c
-
-/* JPEG horizontal resolution register upper byte */
-#define S5P_JPGX_U 0x20
-
-/* JPEG horizontal resolution register lower byte */
-#define S5P_JPGX_L 0x24
-
-/* JPEG byte count register upper byte */
-#define S5P_JPGCNT_U 0x28
-
-/* JPEG byte count register middle byte */
-#define S5P_JPGCNT_M 0x2c
-
-/* JPEG byte count register lower byte */
-#define S5P_JPGCNT_L 0x30
-
-/* JPEG interrupt setting register */
-#define S5P_JPGINTSE 0x34
-#define S5P_RSTm_INT_EN_MASK (0x1 << 7)
-#define S5P_RSTm_INT_EN (0x1 << 7)
-#define S5P_DATA_NUM_INT_EN_MASK (0x1 << 6)
-#define S5P_DATA_NUM_INT_EN (0x1 << 6)
-#define S5P_FINAL_MCU_NUM_INT_EN_MASK (0x1 << 5)
-#define S5P_FINAL_MCU_NUM_INT_EN (0x1 << 5)
-
-/* JPEG interrupt status register */
-#define S5P_JPGINTST 0x38
-#define S5P_RESULT_STAT_SHIFT 6
-#define S5P_RESULT_STAT_MASK (0x1 << S5P_RESULT_STAT_SHIFT)
-#define S5P_STREAM_STAT_SHIFT 5
-#define S5P_STREAM_STAT_MASK (0x1 << S5P_STREAM_STAT_SHIFT)
-
-/* JPEG command register */
-#define S5P_JPGCOM 0x4c
-#define S5P_INT_RELEASE (0x1 << 2)
-
-/* Raw image data r/w address register */
-#define S5P_JPG_IMGADR 0x50
-
-/* JPEG file r/w address register */
-#define S5P_JPG_JPGADR 0x58
-
-/* Coefficient for RGB-to-YCbCr converter register */
-#define S5P_JPG_COEF(n) (0x5c + (((n) - 1) << 2))
-#define S5P_COEFn_SHIFT(j) ((3 - (j)) << 3)
-#define S5P_COEFn_MASK(j) (0xff << S5P_COEFn_SHIFT(j))
-
-/* JPEG color mode register */
-#define S5P_JPGCMOD 0x68
-#define S5P_MOD_SEL_MASK (0x7 << 5)
-#define S5P_MOD_SEL_422 (0x1 << 5)
-#define S5P_MOD_SEL_565 (0x2 << 5)
-#define S5P_MODE_Y16_MASK (0x1 << 1)
-#define S5P_MODE_Y16 (0x1 << 1)
-
-/* JPEG clock control register */
-#define S5P_JPGCLKCON 0x6c
-#define S5P_CLK_DOWN_READY (0x1 << 1)
-#define S5P_POWER_ON (0x1 << 0)
-
-/* JPEG start register */
-#define S5P_JSTART 0x70
-
-/* JPEG SW reset register */
-#define S5P_JPG_SW_RESET 0x78
-
-/* JPEG timer setting register */
-#define S5P_JPG_TIMER_SE 0x7c
-#define S5P_TIMER_INT_EN_MASK (0x1 << 31)
-#define S5P_TIMER_INT_EN (0x1 << 31)
-#define S5P_TIMER_INIT_MASK 0x7fffffff
-
-/* JPEG timer status register */
-#define S5P_JPG_TIMER_ST 0x80
-#define S5P_TIMER_INT_STAT_SHIFT 31
-#define S5P_TIMER_INT_STAT_MASK (0x1 << S5P_TIMER_INT_STAT_SHIFT)
-#define S5P_TIMER_CNT_SHIFT 0
-#define S5P_TIMER_CNT_MASK 0x7fffffff
-
-/* JPEG decompression output format register */
-#define S5P_JPG_OUTFORM 0x88
-#define S5P_DEC_OUT_FORMAT_MASK (0x1 << 0)
-#define S5P_DEC_OUT_FORMAT_422 (0x0 << 0)
-#define S5P_DEC_OUT_FORMAT_420 (0x1 << 0)
-
-/* JPEG version register */
-#define S5P_JPG_VERSION 0x8c
-
-/* JPEG compressed stream size interrupt setting register */
-#define S5P_JPG_ENC_STREAM_INTSE 0x98
-#define S5P_ENC_STREAM_INT_MASK (0x1 << 24)
-#define S5P_ENC_STREAM_INT_EN (0x1 << 24)
-#define S5P_ENC_STREAM_BOUND_MASK 0xffffff
-
-/* JPEG compressed stream size interrupt status register */
-#define S5P_JPG_ENC_STREAM_INTST 0x9c
-#define S5P_ENC_STREAM_INT_STAT_MASK 0x1
-
-/* JPEG quantizer table register */
-#define S5P_JPG_QTBL_CONTENT(n) (0x400 + (n) * 0x100)
-
-/* JPEG DC Huffman table register */
-#define S5P_JPG_HDCTBL(n) (0x800 + (n) * 0x400)
-
-/* JPEG DC Huffman table register */
-#define S5P_JPG_HDCTBLG(n) (0x840 + (n) * 0x400)
-
-/* JPEG AC Huffman table register */
-#define S5P_JPG_HACTBL(n) (0x880 + (n) * 0x400)
-
-/* JPEG AC Huffman table register */
-#define S5P_JPG_HACTBLG(n) (0x8c0 + (n) * 0x400)
-
-#endif /* JPEG_REGS_H_ */
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
deleted file mode 100644
index 9bb68e7b5ae8..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc.c
+++ /dev/null
@@ -1,1223 +0,0 @@
-/*
- * Samsung S5P Multi Format Codec v 5.1
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Kamil Debski, <k.debski@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.
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/workqueue.h>
-#include <media/videobuf2-core.h>
-#include "regs-mfc.h"
-#include "s5p_mfc_ctrl.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_dec.h"
-#include "s5p_mfc_enc.h"
-#include "s5p_mfc_intr.h"
-#include "s5p_mfc_opr.h"
-#include "s5p_mfc_pm.h"
-#include "s5p_mfc_shm.h"
-
-#define S5P_MFC_NAME "s5p-mfc"
-#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
-#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
-
-int debug;
-module_param(debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
-
-/* Helper functions for interrupt processing */
-/* Remove from hw execution round robin */
-static void clear_work_bit(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- spin_lock(&dev->condlock);
- clear_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock(&dev->condlock);
-}
-
-/* Wake up context wait_queue */
-static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
- unsigned int err)
-{
- ctx->int_cond = 1;
- ctx->int_type = reason;
- ctx->int_err = err;
- wake_up(&ctx->queue);
-}
-
-/* Wake up device wait_queue */
-static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
- unsigned int err)
-{
- dev->int_cond = 1;
- dev->int_type = reason;
- dev->int_err = err;
- wake_up(&dev->queue);
-}
-
-static void s5p_mfc_watchdog(unsigned long arg)
-{
- struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
-
- if (test_bit(0, &dev->hw_lock))
- atomic_inc(&dev->watchdog_cnt);
- if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
- /* This means that hw is busy and no interrupts were
- * generated by hw for the Nth time of running this
- * watchdog timer. This usually means a serious hw
- * error. Now it is time to kill all instances and
- * reset the MFC. */
- mfc_err("Time out during waiting for HW\n");
- queue_work(dev->watchdog_workqueue, &dev->watchdog_work);
- }
- dev->watchdog_timer.expires = jiffies +
- msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
- add_timer(&dev->watchdog_timer);
-}
-
-static void s5p_mfc_watchdog_worker(struct work_struct *work)
-{
- struct s5p_mfc_dev *dev;
- struct s5p_mfc_ctx *ctx;
- unsigned long flags;
- int mutex_locked;
- int i, ret;
-
- dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
-
- mfc_err("Driver timeout error handling\n");
- /* Lock the mutex that protects open and release.
- * This is necessary as they may load and unload firmware. */
- mutex_locked = mutex_trylock(&dev->mfc_mutex);
- if (!mutex_locked)
- mfc_err("Error: some instance may be closing/opening\n");
- spin_lock_irqsave(&dev->irqlock, flags);
-
- s5p_mfc_clock_off();
-
- for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
- ctx = dev->ctx[i];
- if (!ctx)
- continue;
- ctx->state = MFCINST_ERROR;
- s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
- s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
- clear_work_bit(ctx);
- wake_up_ctx(ctx, S5P_FIMV_R2H_CMD_ERR_RET, 0);
- }
- clear_bit(0, &dev->hw_lock);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- /* Double check if there is at least one instance running.
- * If no instance is in memory than no firmware should be present */
- if (dev->num_inst > 0) {
- ret = s5p_mfc_reload_firmware(dev);
- if (ret) {
- mfc_err("Failed to reload FW\n");
- goto unlock;
- }
- s5p_mfc_clock_on();
- ret = s5p_mfc_init_hw(dev);
- if (ret)
- mfc_err("Failed to reinit FW\n");
- }
-unlock:
- if (mutex_locked)
- mutex_unlock(&dev->mfc_mutex);
-}
-
-static enum s5p_mfc_node_type s5p_mfc_get_node_type(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (!vdev) {
- mfc_err("failed to get video_device");
- return MFCNODE_INVALID;
- }
- if (vdev->index == 0)
- return MFCNODE_DECODER;
- else if (vdev->index == 1)
- return MFCNODE_ENCODER;
- return MFCNODE_INVALID;
-}
-
-static void s5p_mfc_clear_int_flags(struct s5p_mfc_dev *dev)
-{
- mfc_write(dev, 0, S5P_FIMV_RISC_HOST_INT);
- mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
- mfc_write(dev, 0xffff, S5P_FIMV_SI_RTN_CHID);
-}
-
-static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_buf *dst_buf;
-
- ctx->state = MFCINST_FINISHED;
- ctx->sequence++;
- while (!list_empty(&ctx->dst_queue)) {
- dst_buf = list_entry(ctx->dst_queue.next,
- struct s5p_mfc_buf, list);
- mfc_debug(2, "Cleaning up buffer: %d\n",
- dst_buf->b->v4l2_buf.index);
- vb2_set_plane_payload(dst_buf->b, 0, 0);
- vb2_set_plane_payload(dst_buf->b, 1, 0);
- list_del(&dst_buf->list);
- ctx->dst_queue_cnt--;
- dst_buf->b->v4l2_buf.sequence = (ctx->sequence++);
-
- if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
- s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
- dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
- else
- dst_buf->b->v4l2_buf.field = V4L2_FIELD_INTERLACED;
-
- ctx->dec_dst_flag &= ~(1 << dst_buf->b->v4l2_buf.index);
- vb2_buffer_done(dst_buf->b, VB2_BUF_STATE_DONE);
- }
-}
-
-static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *dst_buf, *src_buf;
- size_t dec_y_addr = s5p_mfc_get_dec_y_adr();
- unsigned int frame_type = s5p_mfc_get_frame_type();
-
- /* Copy timestamp / timecode from decoded src to dst and set
- appropraite flags */
- src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
- if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dec_y_addr) {
- memcpy(&dst_buf->b->v4l2_buf.timecode,
- &src_buf->b->v4l2_buf.timecode,
- sizeof(struct v4l2_timecode));
- memcpy(&dst_buf->b->v4l2_buf.timestamp,
- &src_buf->b->v4l2_buf.timestamp,
- sizeof(struct timeval));
- switch (frame_type) {
- case S5P_FIMV_DECODE_FRAME_I_FRAME:
- dst_buf->b->v4l2_buf.flags |=
- V4L2_BUF_FLAG_KEYFRAME;
- break;
- case S5P_FIMV_DECODE_FRAME_P_FRAME:
- dst_buf->b->v4l2_buf.flags |=
- V4L2_BUF_FLAG_PFRAME;
- break;
- case S5P_FIMV_DECODE_FRAME_B_FRAME:
- dst_buf->b->v4l2_buf.flags |=
- V4L2_BUF_FLAG_BFRAME;
- break;
- }
- break;
- }
- }
-}
-
-static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *dst_buf;
- size_t dspl_y_addr = s5p_mfc_get_dspl_y_adr();
- unsigned int frame_type = s5p_mfc_get_frame_type();
- unsigned int index;
-
- /* If frame is same as previous then skip and do not dequeue */
- if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
- if (!ctx->after_packed_pb)
- ctx->sequence++;
- ctx->after_packed_pb = 0;
- return;
- }
- ctx->sequence++;
- /* The MFC returns address of the buffer, now we have to
- * check which videobuf does it correspond to */
- list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
- /* Check if this is the buffer we're looking for */
- if (vb2_dma_contig_plane_dma_addr(dst_buf->b, 0) == dspl_y_addr) {
- list_del(&dst_buf->list);
- ctx->dst_queue_cnt--;
- dst_buf->b->v4l2_buf.sequence = ctx->sequence;
- if (s5p_mfc_read_shm(ctx, PIC_TIME_TOP) ==
- s5p_mfc_read_shm(ctx, PIC_TIME_BOT))
- dst_buf->b->v4l2_buf.field = V4L2_FIELD_NONE;
- else
- dst_buf->b->v4l2_buf.field =
- V4L2_FIELD_INTERLACED;
- vb2_set_plane_payload(dst_buf->b, 0, ctx->luma_size);
- vb2_set_plane_payload(dst_buf->b, 1, ctx->chroma_size);
- clear_bit(dst_buf->b->v4l2_buf.index,
- &ctx->dec_dst_flag);
-
- vb2_buffer_done(dst_buf->b,
- err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
-
- index = dst_buf->b->v4l2_buf.index;
- break;
- }
- }
-}
-
-/* Handle frame decoding interrupt */
-static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
- unsigned int reason, unsigned int err)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned int dst_frame_status;
- struct s5p_mfc_buf *src_buf;
- unsigned long flags;
- unsigned int res_change;
-
- unsigned int index;
-
- dst_frame_status = s5p_mfc_get_dspl_status()
- & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
- res_change = s5p_mfc_get_dspl_status()
- & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK;
- mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
- if (ctx->state == MFCINST_RES_CHANGE_INIT)
- ctx->state = MFCINST_RES_CHANGE_FLUSH;
- if (res_change) {
- ctx->state = MFCINST_RES_CHANGE_INIT;
- s5p_mfc_clear_int_flags(dev);
- wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- s5p_mfc_try_run(dev);
- return;
- }
- if (ctx->dpb_flush_flag)
- ctx->dpb_flush_flag = 0;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- /* All frames remaining in the buffer have been extracted */
- if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
- if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
- s5p_mfc_handle_frame_all_extracted(ctx);
- ctx->state = MFCINST_RES_CHANGE_END;
- goto leave_handle_frame;
- } else {
- s5p_mfc_handle_frame_all_extracted(ctx);
- }
- }
-
- if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY ||
- dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_ONLY)
- s5p_mfc_handle_frame_copy_time(ctx);
-
- /* A frame has been decoded and is in the buffer */
- if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
- dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
- s5p_mfc_handle_frame_new(ctx, err);
- } else {
- mfc_debug(2, "No frame decode\n");
- }
- /* Mark source buffer as complete */
- if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
- && !list_empty(&ctx->src_queue)) {
- src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
- list);
- ctx->consumed_stream += s5p_mfc_get_consumed_stream();
- if (ctx->codec_mode != S5P_FIMV_CODEC_H264_DEC &&
- s5p_mfc_get_frame_type() == S5P_FIMV_DECODE_FRAME_P_FRAME
- && ctx->consumed_stream + STUFF_BYTE <
- src_buf->b->v4l2_planes[0].bytesused) {
- /* Run MFC again on the same buffer */
- mfc_debug(2, "Running again the same buffer\n");
- ctx->after_packed_pb = 1;
- } else {
- index = src_buf->b->v4l2_buf.index;
- mfc_debug(2, "MFC needs next buffer\n");
- ctx->consumed_stream = 0;
- list_del(&src_buf->list);
- ctx->src_queue_cnt--;
- if (s5p_mfc_err_dec(err) > 0)
- vb2_buffer_done(src_buf->b, VB2_BUF_STATE_ERROR);
- else
- vb2_buffer_done(src_buf->b, VB2_BUF_STATE_DONE);
- }
- }
-leave_handle_frame:
- spin_unlock_irqrestore(&dev->irqlock, flags);
- if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
- || ctx->dst_queue_cnt < ctx->dpb_count)
- clear_work_bit(ctx);
- s5p_mfc_clear_int_flags(dev);
- wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- s5p_mfc_try_run(dev);
-}
-
-/* Error handling for interrupt */
-static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
- unsigned int reason, unsigned int err)
-{
- struct s5p_mfc_dev *dev;
- unsigned long flags;
-
- /* If no context is available then all necessary
- * processing has been done. */
- if (ctx == NULL)
- return;
-
- dev = ctx->dev;
- mfc_err("Interrupt Error: %08x\n", err);
- s5p_mfc_clear_int_flags(dev);
- wake_up_dev(dev, reason, err);
-
- /* Error recovery is dependent on the state of context */
- switch (ctx->state) {
- case MFCINST_INIT:
- /* This error had to happen while acquireing instance */
- case MFCINST_GOT_INST:
- /* This error had to happen while parsing the header */
- case MFCINST_HEAD_PARSED:
- /* This error had to happen while setting dst buffers */
- case MFCINST_RETURN_INST:
- /* This error had to happen while releasing instance */
- clear_work_bit(ctx);
- wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- ctx->state = MFCINST_ERROR;
- break;
- case MFCINST_FINISHING:
- case MFCINST_FINISHED:
- case MFCINST_RUNNING:
- /* It is higly probable that an error occured
- * while decoding a frame */
- clear_work_bit(ctx);
- ctx->state = MFCINST_ERROR;
- /* Mark all dst buffers as having an error */
- spin_lock_irqsave(&dev->irqlock, flags);
- s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
- /* Mark all src buffers as having an error */
- s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- break;
- default:
- mfc_err("Encountered an error interrupt which had not been handled\n");
- break;
- }
- return;
-}
-
-/* Header parsing interrupt handling */
-static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
- unsigned int reason, unsigned int err)
-{
- struct s5p_mfc_dev *dev;
- unsigned int guard_width, guard_height;
-
- if (ctx == NULL)
- return;
- dev = ctx->dev;
- if (ctx->c_ops->post_seq_start) {
- if (ctx->c_ops->post_seq_start(ctx))
- mfc_err("post_seq_start() failed\n");
- } else {
- ctx->img_width = s5p_mfc_get_img_width();
- ctx->img_height = s5p_mfc_get_img_height();
-
- ctx->buf_width = ALIGN(ctx->img_width,
- S5P_FIMV_NV12MT_HALIGN);
- ctx->buf_height = ALIGN(ctx->img_height,
- S5P_FIMV_NV12MT_VALIGN);
- mfc_debug(2, "SEQ Done: Movie dimensions %dx%d, "
- "buffer dimensions: %dx%d\n", ctx->img_width,
- ctx->img_height, ctx->buf_width,
- ctx->buf_height);
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
- ctx->luma_size = ALIGN(ctx->buf_width *
- ctx->buf_height, S5P_FIMV_DEC_BUF_ALIGN);
- ctx->chroma_size = ALIGN(ctx->buf_width *
- ALIGN((ctx->img_height >> 1),
- S5P_FIMV_NV12MT_VALIGN),
- S5P_FIMV_DEC_BUF_ALIGN);
- ctx->mv_size = ALIGN(ctx->buf_width *
- ALIGN((ctx->buf_height >> 2),
- S5P_FIMV_NV12MT_VALIGN),
- S5P_FIMV_DEC_BUF_ALIGN);
- } else {
- guard_width = ALIGN(ctx->img_width + 24,
- S5P_FIMV_NV12MT_HALIGN);
- guard_height = ALIGN(ctx->img_height + 16,
- S5P_FIMV_NV12MT_VALIGN);
- ctx->luma_size = ALIGN(guard_width *
- guard_height, S5P_FIMV_DEC_BUF_ALIGN);
- guard_width = ALIGN(ctx->img_width + 16,
- S5P_FIMV_NV12MT_HALIGN);
- guard_height = ALIGN((ctx->img_height >> 1) + 4,
- S5P_FIMV_NV12MT_VALIGN);
- ctx->chroma_size = ALIGN(guard_width *
- guard_height, S5P_FIMV_DEC_BUF_ALIGN);
- ctx->mv_size = 0;
- }
- ctx->dpb_count = s5p_mfc_get_dpb_count();
- if (ctx->img_width == 0 || ctx->img_height == 0)
- ctx->state = MFCINST_ERROR;
- else
- ctx->state = MFCINST_HEAD_PARSED;
- }
- s5p_mfc_clear_int_flags(dev);
- clear_work_bit(ctx);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- s5p_mfc_try_run(dev);
- wake_up_ctx(ctx, reason, err);
-}
-
-/* Header parsing interrupt handling */
-static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
- unsigned int reason, unsigned int err)
-{
- struct s5p_mfc_buf *src_buf;
- struct s5p_mfc_dev *dev;
- unsigned long flags;
-
- if (ctx == NULL)
- return;
- dev = ctx->dev;
- s5p_mfc_clear_int_flags(dev);
- ctx->int_type = reason;
- ctx->int_err = err;
- ctx->int_cond = 1;
- spin_lock(&dev->condlock);
- clear_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock(&dev->condlock);
- if (err == 0) {
- ctx->state = MFCINST_RUNNING;
- if (!ctx->dpb_flush_flag) {
- spin_lock_irqsave(&dev->irqlock, flags);
- if (!list_empty(&ctx->src_queue)) {
- src_buf = list_entry(ctx->src_queue.next,
- struct s5p_mfc_buf, list);
- list_del(&src_buf->list);
- ctx->src_queue_cnt--;
- vb2_buffer_done(src_buf->b,
- VB2_BUF_STATE_DONE);
- }
- spin_unlock_irqrestore(&dev->irqlock, flags);
- } else {
- ctx->dpb_flush_flag = 0;
- }
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
-
- s5p_mfc_clock_off();
-
- wake_up(&ctx->queue);
- s5p_mfc_try_run(dev);
- } else {
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
-
- s5p_mfc_clock_off();
-
- wake_up(&ctx->queue);
- }
-}
-
-/* Interrupt processing */
-static irqreturn_t s5p_mfc_irq(int irq, void *priv)
-{
- struct s5p_mfc_dev *dev = priv;
- struct s5p_mfc_ctx *ctx;
- unsigned int reason;
- unsigned int err;
-
- mfc_debug_enter();
- /* Reset the timeout watchdog */
- atomic_set(&dev->watchdog_cnt, 0);
- ctx = dev->ctx[dev->curr_ctx];
- /* Get the reason of interrupt and the error code */
- reason = s5p_mfc_get_int_reason();
- err = s5p_mfc_get_int_err();
- mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
- switch (reason) {
- case S5P_FIMV_R2H_CMD_ERR_RET:
- /* An error has occured */
- if (ctx->state == MFCINST_RUNNING &&
- s5p_mfc_err_dec(err) >= S5P_FIMV_ERR_WARNINGS_START)
- s5p_mfc_handle_frame(ctx, reason, err);
- else
- s5p_mfc_handle_error(ctx, reason, err);
- clear_bit(0, &dev->enter_suspend);
- break;
-
- case S5P_FIMV_R2H_CMD_SLICE_DONE_RET:
- case S5P_FIMV_R2H_CMD_FRAME_DONE_RET:
- if (ctx->c_ops->post_frame_start) {
- if (ctx->c_ops->post_frame_start(ctx))
- mfc_err("post_frame_start() failed\n");
- s5p_mfc_clear_int_flags(dev);
- wake_up_ctx(ctx, reason, err);
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- BUG();
- s5p_mfc_clock_off();
- s5p_mfc_try_run(dev);
- } else {
- s5p_mfc_handle_frame(ctx, reason, err);
- }
- break;
-
- case S5P_FIMV_R2H_CMD_SEQ_DONE_RET:
- s5p_mfc_handle_seq_done(ctx, reason, err);
- break;
-
- case S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET:
- ctx->inst_no = s5p_mfc_get_inst_no();
- ctx->state = MFCINST_GOT_INST;
- clear_work_bit(ctx);
- wake_up(&ctx->queue);
- goto irq_cleanup_hw;
-
- case S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET:
- clear_work_bit(ctx);
- ctx->state = MFCINST_FREE;
- wake_up(&ctx->queue);
- goto irq_cleanup_hw;
-
- case S5P_FIMV_R2H_CMD_SYS_INIT_RET:
- case S5P_FIMV_R2H_CMD_FW_STATUS_RET:
- case S5P_FIMV_R2H_CMD_SLEEP_RET:
- case S5P_FIMV_R2H_CMD_WAKEUP_RET:
- if (ctx)
- clear_work_bit(ctx);
- s5p_mfc_clear_int_flags(dev);
- wake_up_dev(dev, reason, err);
- clear_bit(0, &dev->hw_lock);
- clear_bit(0, &dev->enter_suspend);
- break;
-
- case S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET:
- s5p_mfc_handle_init_buffers(ctx, reason, err);
- break;
- default:
- mfc_debug(2, "Unknown int reason\n");
- s5p_mfc_clear_int_flags(dev);
- }
- mfc_debug_leave();
- return IRQ_HANDLED;
-irq_cleanup_hw:
- s5p_mfc_clear_int_flags(dev);
- ctx->int_type = reason;
- ctx->int_err = err;
- ctx->int_cond = 1;
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- mfc_err("Failed to unlock hw\n");
-
- s5p_mfc_clock_off();
-
- s5p_mfc_try_run(dev);
- mfc_debug(2, "Exit via irq_cleanup_hw\n");
- return IRQ_HANDLED;
-}
-
-/* Open an MFC node */
-static int s5p_mfc_open(struct file *file)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
- struct s5p_mfc_ctx *ctx = NULL;
- struct vb2_queue *q;
- unsigned long flags;
- int ret = 0;
-
- mfc_debug_enter();
- dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
- /* Allocate memory for context */
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx) {
- mfc_err("Not enough memory\n");
- ret = -ENOMEM;
- goto err_alloc;
- }
- v4l2_fh_init(&ctx->fh, video_devdata(file));
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
- ctx->dev = dev;
- INIT_LIST_HEAD(&ctx->src_queue);
- INIT_LIST_HEAD(&ctx->dst_queue);
- ctx->src_queue_cnt = 0;
- ctx->dst_queue_cnt = 0;
- /* Get context number */
- ctx->num = 0;
- while (dev->ctx[ctx->num]) {
- ctx->num++;
- if (ctx->num >= MFC_NUM_CONTEXTS) {
- mfc_err("Too many open contexts\n");
- ret = -EBUSY;
- goto err_no_ctx;
- }
- }
- /* Mark context as idle */
- spin_lock_irqsave(&dev->condlock, flags);
- clear_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- dev->ctx[ctx->num] = ctx;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
- ctx->type = MFCINST_DECODER;
- ctx->c_ops = get_dec_codec_ops();
- /* Setup ctrl handler */
- ret = s5p_mfc_dec_ctrls_setup(ctx);
- if (ret) {
- mfc_err("Failed to setup mfc controls\n");
- goto err_ctrls_setup;
- }
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
- ctx->type = MFCINST_ENCODER;
- ctx->c_ops = get_enc_codec_ops();
- /* only for encoder */
- INIT_LIST_HEAD(&ctx->ref_queue);
- ctx->ref_queue_cnt = 0;
- /* Setup ctrl handler */
- ret = s5p_mfc_enc_ctrls_setup(ctx);
- if (ret) {
- mfc_err("Failed to setup mfc controls\n");
- goto err_ctrls_setup;
- }
- } else {
- ret = -ENOENT;
- goto err_bad_node;
- }
- ctx->fh.ctrl_handler = &ctx->ctrl_handler;
- ctx->inst_no = -1;
- /* Load firmware if this is the first instance */
- if (dev->num_inst == 1) {
- dev->watchdog_timer.expires = jiffies +
- msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
- add_timer(&dev->watchdog_timer);
- ret = s5p_mfc_power_on();
- if (ret < 0) {
- mfc_err("power on failed\n");
- goto err_pwr_enable;
- }
- s5p_mfc_clock_on();
- ret = s5p_mfc_alloc_and_load_firmware(dev);
- if (ret)
- goto err_alloc_fw;
- /* Init the FW */
- ret = s5p_mfc_init_hw(dev);
- if (ret)
- goto err_init_hw;
- s5p_mfc_clock_off();
- }
- /* Init videobuf2 queue for CAPTURE */
- q = &ctx->vq_dst;
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- q->drv_priv = &ctx->fh;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
- q->io_modes = VB2_MMAP;
- q->ops = get_dec_queue_ops();
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->ops = get_enc_queue_ops();
- } else {
- ret = -ENOENT;
- goto err_queue_init;
- }
- q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
- ret = vb2_queue_init(q);
- if (ret) {
- mfc_err("Failed to initialize videobuf2 queue(capture)\n");
- goto err_queue_init;
- }
- /* Init videobuf2 queue for OUTPUT */
- q = &ctx->vq_src;
- q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- q->io_modes = VB2_MMAP;
- q->drv_priv = &ctx->fh;
- if (s5p_mfc_get_node_type(file) == MFCNODE_DECODER) {
- q->io_modes = VB2_MMAP;
- q->ops = get_dec_queue_ops();
- } else if (s5p_mfc_get_node_type(file) == MFCNODE_ENCODER) {
- q->io_modes = VB2_MMAP | VB2_USERPTR;
- q->ops = get_enc_queue_ops();
- } else {
- ret = -ENOENT;
- goto err_queue_init;
- }
- q->mem_ops = (struct vb2_mem_ops *)&vb2_dma_contig_memops;
- ret = vb2_queue_init(q);
- if (ret) {
- mfc_err("Failed to initialize videobuf2 queue(output)\n");
- goto err_queue_init;
- }
- init_waitqueue_head(&ctx->queue);
- mfc_debug_leave();
- return ret;
- /* Deinit when failure occured */
-err_queue_init:
-err_init_hw:
- s5p_mfc_release_firmware(dev);
-err_alloc_fw:
- dev->ctx[ctx->num] = NULL;
- del_timer_sync(&dev->watchdog_timer);
- s5p_mfc_clock_off();
-err_pwr_enable:
- if (dev->num_inst == 1) {
- if (s5p_mfc_power_off() < 0)
- mfc_err("power off failed\n");
- s5p_mfc_release_firmware(dev);
- }
-err_ctrls_setup:
- s5p_mfc_dec_ctrls_delete(ctx);
-err_bad_node:
-err_no_ctx:
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
-err_alloc:
- dev->num_inst--;
- mfc_debug_leave();
- return ret;
-}
-
-/* Release MFC context */
-static int s5p_mfc_release(struct file *file)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
-
- mfc_debug_enter();
- s5p_mfc_clock_on();
- vb2_queue_release(&ctx->vq_src);
- vb2_queue_release(&ctx->vq_dst);
- /* Mark context as idle */
- spin_lock_irqsave(&dev->condlock, flags);
- clear_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- /* If instance was initialised then
- * return instance and free reosurces */
- if (ctx->inst_no != MFC_NO_INSTANCE_SET) {
- mfc_debug(2, "Has to free instance\n");
- ctx->state = MFCINST_RETURN_INST;
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_try_run(dev);
- /* Wait until instance is returned or timeout occured */
- if (s5p_mfc_wait_for_done_ctx
- (ctx, S5P_FIMV_R2H_CMD_CLOSE_INSTANCE_RET, 0)) {
- s5p_mfc_clock_off();
- mfc_err("Err returning instance\n");
- }
- mfc_debug(2, "After free instance\n");
- /* Free resources */
- s5p_mfc_release_codec_buffers(ctx);
- s5p_mfc_release_instance_buffer(ctx);
- if (ctx->type == MFCINST_DECODER)
- s5p_mfc_release_dec_desc_buffer(ctx);
-
- ctx->inst_no = MFC_NO_INSTANCE_SET;
- }
- /* hardware locking scheme */
- if (dev->curr_ctx == ctx->num)
- clear_bit(0, &dev->hw_lock);
- dev->num_inst--;
- if (dev->num_inst == 0) {
- mfc_debug(2, "Last instance - release firmware\n");
- /* reset <-> F/W release */
- s5p_mfc_reset(dev);
- s5p_mfc_release_firmware(dev);
- del_timer_sync(&dev->watchdog_timer);
- if (s5p_mfc_power_off() < 0)
- mfc_err("Power off failed\n");
- }
- mfc_debug(2, "Shutting down clock\n");
- s5p_mfc_clock_off();
- dev->ctx[ctx->num] = NULL;
- s5p_mfc_dec_ctrls_delete(ctx);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- mfc_debug_leave();
- return 0;
-}
-
-/* Poll */
-static unsigned int s5p_mfc_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
- struct s5p_mfc_dev *dev = ctx->dev;
- 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 = &ctx->vq_src;
- dst_q = &ctx->vq_dst;
- /*
- * 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;
- }
- mutex_unlock(&dev->mfc_mutex);
- poll_wait(file, &src_q->done_wq, wait);
- poll_wait(file, &dst_q->done_wq, wait);
- mutex_lock(&dev->mfc_mutex);
- 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:
- return rc;
-}
-
-/* Mmap */
-static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- int ret;
- if (offset < DST_QUEUE_OFF_BASE) {
- mfc_debug(2, "mmaping source\n");
- ret = vb2_mmap(&ctx->vq_src, vma);
- } else { /* capture */
- mfc_debug(2, "mmaping destination\n");
- vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
- ret = vb2_mmap(&ctx->vq_dst, vma);
- }
- return ret;
-}
-
-/* v4l2 ops */
-static const struct v4l2_file_operations s5p_mfc_fops = {
- .owner = THIS_MODULE,
- .open = s5p_mfc_open,
- .release = s5p_mfc_release,
- .poll = s5p_mfc_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = s5p_mfc_mmap,
-};
-
-static int match_child(struct device *dev, void *data)
-{
- if (!dev_name(dev))
- return 0;
- return !strcmp(dev_name(dev), (char *)data);
-}
-
-/* MFC probe function */
-static int s5p_mfc_probe(struct platform_device *pdev)
-{
- struct s5p_mfc_dev *dev;
- struct video_device *vfd;
- struct resource *res;
- int ret;
-
- pr_debug("%s++\n", __func__);
- dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL);
- if (!dev) {
- dev_err(&pdev->dev, "Not enough memory for MFC device\n");
- return -ENOMEM;
- }
-
- spin_lock_init(&dev->irqlock);
- spin_lock_init(&dev->condlock);
- dev->plat_dev = pdev;
- if (!dev->plat_dev) {
- dev_err(&pdev->dev, "No platform data specified\n");
- return -ENODEV;
- }
-
- ret = s5p_mfc_init_pm(dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to get mfc clock source\n");
- return ret;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- dev->regs_base = devm_request_and_ioremap(&pdev->dev, res);
- if (dev->regs_base == NULL) {
- dev_err(&pdev->dev, "Failed to obtain io memory\n");
- return -ENOENT;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
- ret = -ENOENT;
- goto err_res;
- }
- dev->irq = res->start;
- ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
- IRQF_DISABLED, pdev->name, dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
- goto err_res;
- }
-
- dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
- match_child);
- if (!dev->mem_dev_l) {
- mfc_err("Mem child (L) device get failed\n");
- ret = -ENODEV;
- goto err_res;
- }
- dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
- match_child);
- if (!dev->mem_dev_r) {
- mfc_err("Mem child (R) device get failed\n");
- ret = -ENODEV;
- goto err_res;
- }
-
- dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
- if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
- ret = PTR_ERR(dev->alloc_ctx[0]);
- goto err_res;
- }
- dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
- if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
- ret = PTR_ERR(dev->alloc_ctx[1]);
- goto err_mem_init_ctx_1;
- }
-
- mutex_init(&dev->mfc_mutex);
-
- ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
- if (ret)
- goto err_v4l2_dev_reg;
- init_waitqueue_head(&dev->queue);
-
- /* decoder */
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto err_dec_alloc;
- }
- vfd->fops = &s5p_mfc_fops,
- vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
- vfd->release = video_device_release,
- vfd->lock = &dev->mfc_mutex;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->v4l2_dev = &dev->v4l2_dev;
- snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
- dev->vfd_dec = vfd;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- video_device_release(vfd);
- goto err_dec_reg;
- }
- v4l2_info(&dev->v4l2_dev,
- "decoder registered as /dev/video%d\n", vfd->num);
- video_set_drvdata(vfd, dev);
-
- /* encoder */
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
- ret = -ENOMEM;
- goto err_enc_alloc;
- }
- vfd->fops = &s5p_mfc_fops,
- vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
- vfd->release = video_device_release,
- vfd->lock = &dev->mfc_mutex;
- /* This should not be necessary */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
- vfd->v4l2_dev = &dev->v4l2_dev;
- snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
- dev->vfd_enc = vfd;
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
- if (ret) {
- v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- video_device_release(vfd);
- goto err_enc_reg;
- }
- v4l2_info(&dev->v4l2_dev,
- "encoder registered as /dev/video%d\n", vfd->num);
- video_set_drvdata(vfd, dev);
- platform_set_drvdata(pdev, dev);
-
- dev->hw_lock = 0;
- dev->watchdog_workqueue = create_singlethread_workqueue(S5P_MFC_NAME);
- INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
- atomic_set(&dev->watchdog_cnt, 0);
- init_timer(&dev->watchdog_timer);
- dev->watchdog_timer.data = (unsigned long)dev;
- dev->watchdog_timer.function = s5p_mfc_watchdog;
-
- pr_debug("%s--\n", __func__);
- return 0;
-
-/* Deinit MFC if probe had failed */
-err_enc_reg:
- video_device_release(dev->vfd_enc);
-err_enc_alloc:
- video_unregister_device(dev->vfd_dec);
-err_dec_reg:
- video_device_release(dev->vfd_dec);
-err_dec_alloc:
- v4l2_device_unregister(&dev->v4l2_dev);
-err_v4l2_dev_reg:
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
-err_mem_init_ctx_1:
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
-err_res:
- s5p_mfc_final_pm(dev);
-
- pr_debug("%s-- with error\n", __func__);
- return ret;
-
-}
-
-/* Remove the driver */
-static int __devexit s5p_mfc_remove(struct platform_device *pdev)
-{
- struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
-
- v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
-
- del_timer_sync(&dev->watchdog_timer);
- flush_workqueue(dev->watchdog_workqueue);
- destroy_workqueue(dev->watchdog_workqueue);
-
- video_unregister_device(dev->vfd_enc);
- video_unregister_device(dev->vfd_dec);
- v4l2_device_unregister(&dev->v4l2_dev);
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
- vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
-
- s5p_mfc_final_pm(dev);
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int s5p_mfc_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
- int ret;
-
- if (m_dev->num_inst == 0)
- return 0;
- return s5p_mfc_sleep(m_dev);
- if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
- mfc_err("Error: going to suspend for a second time\n");
- return -EIO;
- }
-
- /* Check if we're processing then wait if it necessary. */
- while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
- /* Try and lock the HW */
- /* Wait on the interrupt waitqueue */
- ret = wait_event_interruptible_timeout(m_dev->queue,
- m_dev->int_cond || m_dev->ctx[m_dev->curr_ctx]->int_cond,
- msecs_to_jiffies(MFC_INT_TIMEOUT));
-
- if (ret == 0) {
- mfc_err("Waiting for hardware to finish timed out\n");
- return -EIO;
- }
- }
- return 0;
-}
-
-static int s5p_mfc_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
-
- if (m_dev->num_inst == 0)
- return 0;
- return s5p_mfc_wakeup(m_dev);
-}
-#endif
-
-#ifdef CONFIG_PM_RUNTIME
-static int s5p_mfc_runtime_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
-
- atomic_set(&m_dev->pm.power, 0);
- return 0;
-}
-
-static int s5p_mfc_runtime_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
- int pre_power;
-
- if (!m_dev->alloc_ctx)
- return 0;
- pre_power = atomic_read(&m_dev->pm.power);
- atomic_set(&m_dev->pm.power, 1);
- return 0;
-}
-#endif
-
-/* Power management */
-static const struct dev_pm_ops s5p_mfc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
- SET_RUNTIME_PM_OPS(s5p_mfc_runtime_suspend, s5p_mfc_runtime_resume,
- NULL)
-};
-
-static struct platform_driver s5p_mfc_driver = {
- .probe = s5p_mfc_probe,
- .remove = __devexit_p(s5p_mfc_remove),
- .driver = {
- .name = S5P_MFC_NAME,
- .owner = THIS_MODULE,
- .pm = &s5p_mfc_pm_ops
- },
-};
-
-module_platform_driver(s5p_mfc_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
-MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
deleted file mode 100644
index f0665ed1a529..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.c
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.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.
- */
-
-#include "regs-mfc.h"
-#include "s5p_mfc_cmd.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-
-/* This function is used to send a command to the MFC */
-static int s5p_mfc_cmd_host2risc(struct s5p_mfc_dev *dev, int cmd,
- struct s5p_mfc_cmd_args *args)
-{
- int cur_cmd;
- unsigned long timeout;
-
- timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
- /* wait until host to risc command register becomes 'H2R_CMD_EMPTY' */
- do {
- if (time_after(jiffies, timeout)) {
- mfc_err("Timeout while waiting for hardware\n");
- return -EIO;
- }
- cur_cmd = mfc_read(dev, S5P_FIMV_HOST2RISC_CMD);
- } while (cur_cmd != S5P_FIMV_H2R_CMD_EMPTY);
- mfc_write(dev, args->arg[0], S5P_FIMV_HOST2RISC_ARG1);
- mfc_write(dev, args->arg[1], S5P_FIMV_HOST2RISC_ARG2);
- mfc_write(dev, args->arg[2], S5P_FIMV_HOST2RISC_ARG3);
- mfc_write(dev, args->arg[3], S5P_FIMV_HOST2RISC_ARG4);
- /* Issue the command */
- mfc_write(dev, cmd, S5P_FIMV_HOST2RISC_CMD);
- return 0;
-}
-
-/* Initialize the MFC */
-int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev)
-{
- struct s5p_mfc_cmd_args h2r_args;
-
- memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
- h2r_args.arg[0] = dev->fw_size;
- return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SYS_INIT, &h2r_args);
-}
-
-/* Suspend the MFC hardware */
-int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev)
-{
- struct s5p_mfc_cmd_args h2r_args;
-
- memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
- return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_SLEEP, &h2r_args);
-}
-
-/* Wake up the MFC hardware */
-int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev)
-{
- struct s5p_mfc_cmd_args h2r_args;
-
- memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
- return s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_WAKEUP, &h2r_args);
-}
-
-
-int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_cmd_args h2r_args;
- int ret;
-
- /* Preparing decoding - getting instance number */
- mfc_debug(2, "Getting instance number (codec: %d)\n", ctx->codec_mode);
- dev->curr_ctx = ctx->num;
- memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
- h2r_args.arg[0] = ctx->codec_mode;
- h2r_args.arg[1] = 0; /* no crc & no pixelcache */
- h2r_args.arg[2] = ctx->ctx_ofs;
- h2r_args.arg[3] = ctx->ctx_size;
- ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_OPEN_INSTANCE,
- &h2r_args);
- if (ret) {
- mfc_err("Failed to create a new instance\n");
- ctx->state = MFCINST_ERROR;
- }
- return ret;
-}
-
-int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_cmd_args h2r_args;
- int ret;
-
- if (ctx->state == MFCINST_FREE) {
- mfc_err("Instance already returned\n");
- ctx->state = MFCINST_ERROR;
- return -EINVAL;
- }
- /* Closing decoding instance */
- mfc_debug(2, "Returning instance number %d\n", ctx->inst_no);
- dev->curr_ctx = ctx->num;
- memset(&h2r_args, 0, sizeof(struct s5p_mfc_cmd_args));
- h2r_args.arg[0] = ctx->inst_no;
- ret = s5p_mfc_cmd_host2risc(dev, S5P_FIMV_H2R_CMD_CLOSE_INSTANCE,
- &h2r_args);
- if (ret) {
- mfc_err("Failed to return an instance\n");
- ctx->state = MFCINST_ERROR;
- return -EINVAL;
- }
- return 0;
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
deleted file mode 100644
index 5ceebfe6131a..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_cmd.h
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_CMD_H_
-#define S5P_MFC_CMD_H_
-
-#include "s5p_mfc_common.h"
-
-#define MAX_H2R_ARG 4
-
-struct s5p_mfc_cmd_args {
- unsigned int arg[MAX_H2R_ARG];
-};
-
-int s5p_mfc_sys_init_cmd(struct s5p_mfc_dev *dev);
-int s5p_mfc_sleep_cmd(struct s5p_mfc_dev *dev);
-int s5p_mfc_wakeup_cmd(struct s5p_mfc_dev *dev);
-int s5p_mfc_open_inst_cmd(struct s5p_mfc_ctx *ctx);
-int s5p_mfc_close_inst_cmd(struct s5p_mfc_ctx *ctx);
-
-#endif /* S5P_MFC_CMD_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
deleted file mode 100644
index bd5706a6bad1..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * Samsung S5P Multi Format Codec v 5.0
- *
- * This file contains definitions of enums and structs used by the codec
- * driver.
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Kamil Debski, <k.debski@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 S5P_MFC_COMMON_H_
-#define S5P_MFC_COMMON_H_
-
-#include "regs-mfc.h"
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/videobuf2-core.h>
-
-/* Definitions related to MFC memory */
-
-/* Offset base used to differentiate between CAPTURE and OUTPUT
-* while mmaping */
-#define DST_QUEUE_OFF_BASE (TASK_SIZE / 2)
-
-/* Offset used by the hardware to store addresses */
-#define MFC_OFFSET_SHIFT 11
-
-#define FIRMWARE_ALIGN 0x20000 /* 128KB */
-#define MFC_H264_CTX_BUF_SIZE 0x96000 /* 600KB per H264 instance */
-#define MFC_CTX_BUF_SIZE 0x2800 /* 10KB per instance */
-#define DESC_BUF_SIZE 0x20000 /* 128KB for DESC buffer */
-#define SHARED_BUF_SIZE 0x2000 /* 8KB for shared buffer */
-
-#define DEF_CPB_SIZE 0x40000 /* 512KB */
-
-#define MFC_BANK1_ALLOC_CTX 0
-#define MFC_BANK2_ALLOC_CTX 1
-
-#define MFC_BANK1_ALIGN_ORDER 13
-#define MFC_BANK2_ALIGN_ORDER 13
-#define MFC_BASE_ALIGN_ORDER 17
-
-#include <media/videobuf2-dma-contig.h>
-
-static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
-{
- /* Same functionality as the vb2_dma_contig_plane_paddr */
- dma_addr_t *paddr = vb2_dma_contig_memops.cookie(b);
-
- return *paddr;
-}
-
-/* MFC definitions */
-#define MFC_MAX_EXTRA_DPB 5
-#define MFC_MAX_BUFFERS 32
-#define MFC_NUM_CONTEXTS 4
-/* Interrupt timeout */
-#define MFC_INT_TIMEOUT 2000
-/* Busy wait timeout */
-#define MFC_BW_TIMEOUT 500
-/* Watchdog interval */
-#define MFC_WATCHDOG_INTERVAL 1000
-/* After how many executions watchdog should assume lock up */
-#define MFC_WATCHDOG_CNT 10
-#define MFC_NO_INSTANCE_SET -1
-#define MFC_ENC_CAP_PLANE_COUNT 1
-#define MFC_ENC_OUT_PLANE_COUNT 2
-#define STUFF_BYTE 4
-#define MFC_MAX_CTRLS 64
-
-#define mfc_read(dev, offset) readl(dev->regs_base + (offset))
-#define mfc_write(dev, data, offset) writel((data), dev->regs_base + \
- (offset))
-
-/**
- * enum s5p_mfc_fmt_type - type of the pixelformat
- */
-enum s5p_mfc_fmt_type {
- MFC_FMT_DEC,
- MFC_FMT_ENC,
- MFC_FMT_RAW,
-};
-
-/**
- * enum s5p_mfc_node_type - The type of an MFC device node.
- */
-enum s5p_mfc_node_type {
- MFCNODE_INVALID = -1,
- MFCNODE_DECODER = 0,
- MFCNODE_ENCODER = 1,
-};
-
-/**
- * enum s5p_mfc_inst_type - The type of an MFC instance.
- */
-enum s5p_mfc_inst_type {
- MFCINST_INVALID,
- MFCINST_DECODER,
- MFCINST_ENCODER,
-};
-
-/**
- * enum s5p_mfc_inst_state - The state of an MFC instance.
- */
-enum s5p_mfc_inst_state {
- MFCINST_FREE = 0,
- MFCINST_INIT = 100,
- MFCINST_GOT_INST,
- MFCINST_HEAD_PARSED,
- MFCINST_BUFS_SET,
- MFCINST_RUNNING,
- MFCINST_FINISHING,
- MFCINST_FINISHED,
- MFCINST_RETURN_INST,
- MFCINST_ERROR,
- MFCINST_ABORT,
- MFCINST_RES_CHANGE_INIT,
- MFCINST_RES_CHANGE_FLUSH,
- MFCINST_RES_CHANGE_END,
-};
-
-/**
- * enum s5p_mfc_queue_state - The state of buffer queue.
- */
-enum s5p_mfc_queue_state {
- QUEUE_FREE,
- QUEUE_BUFS_REQUESTED,
- QUEUE_BUFS_QUERIED,
- QUEUE_BUFS_MMAPED,
-};
-
-/**
- * enum s5p_mfc_decode_arg - type of frame decoding
- */
-enum s5p_mfc_decode_arg {
- MFC_DEC_FRAME,
- MFC_DEC_LAST_FRAME,
- MFC_DEC_RES_CHANGE,
-};
-
-struct s5p_mfc_ctx;
-
-/**
- * struct s5p_mfc_buf - MFC buffer
- */
-struct s5p_mfc_buf {
- struct list_head list;
- struct vb2_buffer *b;
- union {
- struct {
- size_t luma;
- size_t chroma;
- } raw;
- size_t stream;
- } cookie;
- int used;
-};
-
-/**
- * struct s5p_mfc_pm - power management data structure
- */
-struct s5p_mfc_pm {
- struct clk *clock;
- struct clk *clock_gate;
- atomic_t power;
- struct device *device;
-};
-
-/**
- * struct s5p_mfc_dev - The struct containing driver internal parameters.
- *
- * @v4l2_dev: v4l2_device
- * @vfd_dec: video device for decoding
- * @vfd_enc: video device for encoding
- * @plat_dev: platform device
- * @mem_dev_l: child device of the left memory bank (0)
- * @mem_dev_r: child device of the right memory bank (1)
- * @regs_base: base address of the MFC hw registers
- * @irq: irq resource
- * @dec_ctrl_handler: control framework handler for decoding
- * @enc_ctrl_handler: control framework handler for encoding
- * @pm: power management control
- * @num_inst: couter of active MFC instances
- * @irqlock: lock for operations on videobuf2 queues
- * @condlock: lock for changing/checking if a context is ready to be
- * processed
- * @mfc_mutex: lock for video_device
- * @int_cond: variable used by the waitqueue
- * @int_type: type of last interrupt
- * @int_err: error number for last interrupt
- * @queue: waitqueue for waiting for completion of device commands
- * @fw_size: size of firmware
- * @bank1: address of the beggining of bank 1 memory
- * @bank2: address of the beggining of bank 2 memory
- * @hw_lock: used for hardware locking
- * @ctx: array of driver contexts
- * @curr_ctx: number of the currently running context
- * @ctx_work_bits: used to mark which contexts are waiting for hardware
- * @watchdog_cnt: counter for the watchdog
- * @watchdog_workqueue: workqueue for the watchdog
- * @watchdog_work: worker for the watchdog
- * @alloc_ctx: videobuf2 allocator contexts for two memory banks
- * @enter_suspend: flag set when entering suspend
- *
- */
-struct s5p_mfc_dev {
- struct v4l2_device v4l2_dev;
- struct video_device *vfd_dec;
- struct video_device *vfd_enc;
- struct platform_device *plat_dev;
- struct device *mem_dev_l;
- struct device *mem_dev_r;
- void __iomem *regs_base;
- int irq;
- struct v4l2_ctrl_handler dec_ctrl_handler;
- struct v4l2_ctrl_handler enc_ctrl_handler;
- struct s5p_mfc_pm pm;
- int num_inst;
- spinlock_t irqlock; /* lock when operating on videobuf2 queues */
- spinlock_t condlock; /* lock when changing/checking if a context is
- ready to be processed */
- struct mutex mfc_mutex; /* video_device lock */
- int int_cond;
- int int_type;
- unsigned int int_err;
- wait_queue_head_t queue;
- size_t fw_size;
- size_t bank1;
- size_t bank2;
- unsigned long hw_lock;
- struct s5p_mfc_ctx *ctx[MFC_NUM_CONTEXTS];
- int curr_ctx;
- unsigned long ctx_work_bits;
- atomic_t watchdog_cnt;
- struct timer_list watchdog_timer;
- struct workqueue_struct *watchdog_workqueue;
- struct work_struct watchdog_work;
- void *alloc_ctx[2];
- unsigned long enter_suspend;
-};
-
-/**
- * struct s5p_mfc_h264_enc_params - encoding parameters for h264
- */
-struct s5p_mfc_h264_enc_params {
- enum v4l2_mpeg_video_h264_profile profile;
- enum v4l2_mpeg_video_h264_loop_filter_mode loop_filter_mode;
- s8 loop_filter_alpha;
- s8 loop_filter_beta;
- enum v4l2_mpeg_video_h264_entropy_mode entropy_mode;
- u8 max_ref_pic;
- u8 num_ref_pic_4p;
- int _8x8_transform;
- int rc_mb;
- int rc_mb_dark;
- int rc_mb_smooth;
- int rc_mb_static;
- int rc_mb_activity;
- int vui_sar;
- u8 vui_sar_idc;
- u16 vui_ext_sar_width;
- u16 vui_ext_sar_height;
- int open_gop;
- u16 open_gop_size;
- u8 rc_frame_qp;
- u8 rc_min_qp;
- u8 rc_max_qp;
- u8 rc_p_frame_qp;
- u8 rc_b_frame_qp;
- enum v4l2_mpeg_video_h264_level level_v4l2;
- int level;
- u16 cpb_size;
-};
-
-/**
- * struct s5p_mfc_mpeg4_enc_params - encoding parameters for h263 and mpeg4
- */
-struct s5p_mfc_mpeg4_enc_params {
- /* MPEG4 Only */
- enum v4l2_mpeg_video_mpeg4_profile profile;
- int quarter_pixel;
- /* Common for MPEG4, H263 */
- u16 vop_time_res;
- u16 vop_frm_delta;
- u8 rc_frame_qp;
- u8 rc_min_qp;
- u8 rc_max_qp;
- u8 rc_p_frame_qp;
- u8 rc_b_frame_qp;
- enum v4l2_mpeg_video_mpeg4_level level_v4l2;
- int level;
-};
-
-/**
- * struct s5p_mfc_enc_params - general encoding parameters
- */
-struct s5p_mfc_enc_params {
- u16 width;
- u16 height;
-
- u16 gop_size;
- enum v4l2_mpeg_video_multi_slice_mode slice_mode;
- u16 slice_mb;
- u32 slice_bit;
- u16 intra_refresh_mb;
- int pad;
- u8 pad_luma;
- u8 pad_cb;
- u8 pad_cr;
- int rc_frame;
- u32 rc_bitrate;
- u16 rc_reaction_coeff;
- u16 vbv_size;
-
- enum v4l2_mpeg_video_header_mode seq_hdr_mode;
- enum v4l2_mpeg_mfc51_video_frame_skip_mode frame_skip_mode;
- int fixed_target_bit;
-
- u8 num_b_frame;
- u32 rc_framerate_num;
- u32 rc_framerate_denom;
- int interlace;
-
- union {
- struct s5p_mfc_h264_enc_params h264;
- struct s5p_mfc_mpeg4_enc_params mpeg4;
- } codec;
-
-};
-
-/**
- * struct s5p_mfc_codec_ops - codec ops, used by encoding
- */
-struct s5p_mfc_codec_ops {
- /* initialization routines */
- int (*pre_seq_start) (struct s5p_mfc_ctx *ctx);
- int (*post_seq_start) (struct s5p_mfc_ctx *ctx);
- /* execution routines */
- int (*pre_frame_start) (struct s5p_mfc_ctx *ctx);
- int (*post_frame_start) (struct s5p_mfc_ctx *ctx);
-};
-
-#define call_cop(c, op, args...) \
- (((c)->c_ops->op) ? \
- ((c)->c_ops->op(args)) : 0)
-
-/**
- * struct s5p_mfc_ctx - This struct contains the instance context
- *
- * @dev: pointer to the s5p_mfc_dev of the device
- * @fh: struct v4l2_fh
- * @num: number of the context that this structure describes
- * @int_cond: variable used by the waitqueue
- * @int_type: type of the last interrupt
- * @int_err: error number received from MFC hw in the interrupt
- * @queue: waitqueue that can be used to wait for this context to
- * finish
- * @src_fmt: source pixelformat information
- * @dst_fmt: destination pixelformat information
- * @vq_src: vb2 queue for source buffers
- * @vq_dst: vb2 queue for destination buffers
- * @src_queue: driver internal queue for source buffers
- * @dst_queue: driver internal queue for destination buffers
- * @src_queue_cnt: number of buffers queued on the source internal queue
- * @dst_queue_cnt: number of buffers queued on the dest internal queue
- * @type: type of the instance - decoder or encoder
- * @state: state of the context
- * @inst_no: number of hw instance associated with the context
- * @img_width: width of the image that is decoded or encoded
- * @img_height: height of the image that is decoded or encoded
- * @buf_width: width of the buffer for processed image
- * @buf_height: height of the buffer for processed image
- * @luma_size: size of a luma plane
- * @chroma_size: size of a chroma plane
- * @mv_size: size of a motion vectors buffer
- * @consumed_stream: number of bytes that have been used so far from the
- * decoding buffer
- * @dpb_flush_flag: flag used to indicate that a DPB buffers are being
- * flushed
- * @bank1_buf: handle to memory allocated for temporary buffers from
- * memory bank 1
- * @bank1_phys: address of the temporary buffers from memory bank 1
- * @bank1_size: size of the memory allocated for temporary buffers from
- * memory bank 1
- * @bank2_buf: handle to memory allocated for temporary buffers from
- * memory bank 2
- * @bank2_phys: address of the temporary buffers from memory bank 2
- * @bank2_size: size of the memory allocated for temporary buffers from
- * memory bank 2
- * @capture_state: state of the capture buffers queue
- * @output_state: state of the output buffers queue
- * @src_bufs: information on allocated source buffers
- * @dst_bufs: information on allocated destination buffers
- * @sequence: counter for the sequence number for v4l2
- * @dec_dst_flag: flags for buffers queued in the hardware
- * @dec_src_buf_size: size of the buffer for source buffers in decoding
- * @codec_mode: number of codec mode used by MFC hw
- * @slice_interface: slice interface flag
- * @loop_filter_mpeg4: loop filter for MPEG4 flag
- * @display_delay: value of the display delay for H264
- * @display_delay_enable: display delay for H264 enable flag
- * @after_packed_pb: flag used to track buffer when stream is in
- * Packed PB format
- * @dpb_count: count of the DPB buffers required by MFC hw
- * @total_dpb_count: count of DPB buffers with additional buffers
- * requested by the application
- * @ctx_buf: handle to the memory associated with this context
- * @ctx_phys: address of the memory associated with this context
- * @ctx_size: size of the memory associated with this context
- * @desc_buf: description buffer for decoding handle
- * @desc_phys: description buffer for decoding address
- * @shm_alloc: handle for the shared memory buffer
- * @shm: virtual address for the shared memory buffer
- * @shm_ofs: address offset for shared memory
- * @enc_params: encoding parameters for MFC
- * @enc_dst_buf_size: size of the buffers for encoder output
- * @frame_type: used to force the type of the next encoded frame
- * @ref_queue: list of the reference buffers for encoding
- * @ref_queue_cnt: number of the buffers in the reference list
- * @c_ops: ops for encoding
- * @ctrls: array of controls, used when adding controls to the
- * v4l2 control framework
- * @ctrl_handler: handler for v4l2 framework
- */
-struct s5p_mfc_ctx {
- struct s5p_mfc_dev *dev;
- struct v4l2_fh fh;
-
- int num;
-
- int int_cond;
- int int_type;
- unsigned int int_err;
- wait_queue_head_t queue;
-
- struct s5p_mfc_fmt *src_fmt;
- struct s5p_mfc_fmt *dst_fmt;
-
- struct vb2_queue vq_src;
- struct vb2_queue vq_dst;
-
- struct list_head src_queue;
- struct list_head dst_queue;
-
- unsigned int src_queue_cnt;
- unsigned int dst_queue_cnt;
-
- enum s5p_mfc_inst_type type;
- enum s5p_mfc_inst_state state;
- int inst_no;
-
- /* Image parameters */
- int img_width;
- int img_height;
- int buf_width;
- int buf_height;
-
- int luma_size;
- int chroma_size;
- int mv_size;
-
- unsigned long consumed_stream;
-
- unsigned int dpb_flush_flag;
-
- /* Buffers */
- void *bank1_buf;
- size_t bank1_phys;
- size_t bank1_size;
-
- void *bank2_buf;
- size_t bank2_phys;
- size_t bank2_size;
-
- enum s5p_mfc_queue_state capture_state;
- enum s5p_mfc_queue_state output_state;
-
- struct s5p_mfc_buf src_bufs[MFC_MAX_BUFFERS];
- int src_bufs_cnt;
- struct s5p_mfc_buf dst_bufs[MFC_MAX_BUFFERS];
- int dst_bufs_cnt;
-
- unsigned int sequence;
- unsigned long dec_dst_flag;
- size_t dec_src_buf_size;
-
- /* Control values */
- int codec_mode;
- int slice_interface;
- int loop_filter_mpeg4;
- int display_delay;
- int display_delay_enable;
- int after_packed_pb;
-
- int dpb_count;
- int total_dpb_count;
-
- /* Buffers */
- void *ctx_buf;
- size_t ctx_phys;
- size_t ctx_ofs;
- size_t ctx_size;
-
- void *desc_buf;
- size_t desc_phys;
-
-
- void *shm_alloc;
- void *shm;
- size_t shm_ofs;
-
- struct s5p_mfc_enc_params enc_params;
-
- size_t enc_dst_buf_size;
-
- enum v4l2_mpeg_mfc51_video_force_frame_type force_frame_type;
-
- struct list_head ref_queue;
- unsigned int ref_queue_cnt;
-
- struct s5p_mfc_codec_ops *c_ops;
-
- struct v4l2_ctrl *ctrls[MFC_MAX_CTRLS];
- struct v4l2_ctrl_handler ctrl_handler;
-};
-
-/*
- * struct s5p_mfc_fmt - structure used to store information about pixelformats
- * used by the MFC
- */
-struct s5p_mfc_fmt {
- char *name;
- u32 fourcc;
- u32 codec_mode;
- enum s5p_mfc_fmt_type type;
- u32 num_planes;
-};
-
-/**
- * struct mfc_control - structure used to store information about MFC controls
- * it is used to initialize the control framework.
- */
-struct mfc_control {
- __u32 id;
- enum v4l2_ctrl_type type;
- __u8 name[32]; /* Whatever */
- __s32 minimum; /* Note signedness */
- __s32 maximum;
- __s32 step;
- __u32 menu_skip_mask;
- __s32 default_value;
- __u32 flags;
- __u32 reserved[2];
- __u8 is_volatile;
-};
-
-
-#define fh_to_ctx(__fh) container_of(__fh, struct s5p_mfc_ctx, fh)
-#define ctrl_to_ctx(__ctrl) \
- container_of((__ctrl)->handler, struct s5p_mfc_ctx, ctrl_handler)
-
-#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
deleted file mode 100644
index fd62402490ba..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.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.
- */
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/firmware.h>
-#include <linux/jiffies.h>
-#include <linux/sched.h>
-#include "regs-mfc.h"
-#include "s5p_mfc_cmd.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_intr.h"
-#include "s5p_mfc_pm.h"
-
-static void *s5p_mfc_bitproc_buf;
-static size_t s5p_mfc_bitproc_phys;
-static unsigned char *s5p_mfc_bitproc_virt;
-
-/* Allocate and load firmware */
-int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
-{
- struct firmware *fw_blob;
- size_t bank2_base_phys;
- void *b_base;
- int err;
-
- /* Firmare has to be present as a separate file or compiled
- * into kernel. */
- mfc_debug_enter();
- err = request_firmware((const struct firmware **)&fw_blob,
- "s5p-mfc.fw", dev->v4l2_dev.dev);
- if (err != 0) {
- mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
- return -EINVAL;
- }
- dev->fw_size = ALIGN(fw_blob->size, FIRMWARE_ALIGN);
- if (s5p_mfc_bitproc_buf) {
- mfc_err("Attempting to allocate firmware when it seems that it is already loaded\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
- if (IS_ERR(s5p_mfc_bitproc_buf)) {
- s5p_mfc_bitproc_buf = NULL;
- mfc_err("Allocating bitprocessor buffer failed\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- s5p_mfc_bitproc_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], s5p_mfc_bitproc_buf);
- if (s5p_mfc_bitproc_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
- mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- release_firmware(fw_blob);
- return -EIO;
- }
- s5p_mfc_bitproc_virt = vb2_dma_contig_memops.vaddr(s5p_mfc_bitproc_buf);
- if (!s5p_mfc_bitproc_virt) {
- mfc_err("Bitprocessor memory remap failed\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- release_firmware(fw_blob);
- return -EIO;
- }
- dev->bank1 = s5p_mfc_bitproc_phys;
- b_base = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], 1 << MFC_BASE_ALIGN_ORDER);
- if (IS_ERR(b_base)) {
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- mfc_err("Allocating bank2 base failed\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- bank2_base_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], b_base);
- vb2_dma_contig_memops.put(b_base);
- if (bank2_base_phys & ((1 << MFC_BASE_ALIGN_ORDER) - 1)) {
- mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- release_firmware(fw_blob);
- return -EIO;
- }
- dev->bank2 = bank2_base_phys;
- memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
- wmb();
- release_firmware(fw_blob);
- mfc_debug_leave();
- return 0;
-}
-
-/* Reload firmware to MFC */
-int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
-{
- struct firmware *fw_blob;
- int err;
-
- /* Firmare has to be present as a separate file or compiled
- * into kernel. */
- mfc_debug_enter();
- err = request_firmware((const struct firmware **)&fw_blob,
- "s5p-mfc.fw", dev->v4l2_dev.dev);
- if (err != 0) {
- mfc_err("Firmware is not present in the /lib/firmware directory nor compiled in kernel\n");
- return -EINVAL;
- }
- if (fw_blob->size > dev->fw_size) {
- mfc_err("MFC firmware is too big to be loaded\n");
- release_firmware(fw_blob);
- return -ENOMEM;
- }
- if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) {
- mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
- release_firmware(fw_blob);
- return -EINVAL;
- }
- memcpy(s5p_mfc_bitproc_virt, fw_blob->data, fw_blob->size);
- wmb();
- release_firmware(fw_blob);
- mfc_debug_leave();
- return 0;
-}
-
-/* Release firmware memory */
-int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
-{
- /* Before calling this function one has to make sure
- * that MFC is no longer processing */
- if (!s5p_mfc_bitproc_buf)
- return -EINVAL;
- vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_virt = NULL;
- s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = NULL;
- return 0;
-}
-
-/* Reset the device */
-int s5p_mfc_reset(struct s5p_mfc_dev *dev)
-{
- unsigned int mc_status;
- unsigned long timeout;
-
- mfc_debug_enter();
- /* Stop procedure */
- /* reset RISC */
- mfc_write(dev, 0x3f6, S5P_FIMV_SW_RESET);
- /* All reset except for MC */
- mfc_write(dev, 0x3e2, S5P_FIMV_SW_RESET);
- mdelay(10);
-
- timeout = jiffies + msecs_to_jiffies(MFC_BW_TIMEOUT);
- /* Check MC status */
- do {
- if (time_after(jiffies, timeout)) {
- mfc_err("Timeout while resetting MFC\n");
- return -EIO;
- }
-
- mc_status = mfc_read(dev, S5P_FIMV_MC_STATUS);
-
- } while (mc_status & 0x3);
-
- mfc_write(dev, 0x0, S5P_FIMV_SW_RESET);
- mfc_write(dev, 0x3fe, S5P_FIMV_SW_RESET);
- mfc_debug_leave();
- return 0;
-}
-
-static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
-{
- mfc_write(dev, dev->bank1, S5P_FIMV_MC_DRAMBASE_ADR_A);
- mfc_write(dev, dev->bank2, S5P_FIMV_MC_DRAMBASE_ADR_B);
- mfc_debug(2, "Bank1: %08x, Bank2: %08x\n", dev->bank1, dev->bank2);
-}
-
-static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
-{
- mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH0_INST_ID);
- mfc_write(dev, 0xffffffff, S5P_FIMV_SI_CH1_INST_ID);
- mfc_write(dev, 0, S5P_FIMV_RISC2HOST_CMD);
- mfc_write(dev, 0, S5P_FIMV_HOST2RISC_CMD);
-}
-
-/* Initialize hardware */
-int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
-{
- unsigned int ver;
- int ret;
-
- mfc_debug_enter();
- if (!s5p_mfc_bitproc_buf)
- return -EINVAL;
-
- /* 0. MFC reset */
- mfc_debug(2, "MFC reset..\n");
- s5p_mfc_clock_on();
- ret = s5p_mfc_reset(dev);
- if (ret) {
- mfc_err("Failed to reset MFC - timeout\n");
- return ret;
- }
- mfc_debug(2, "Done MFC reset..\n");
- /* 1. Set DRAM base Addr */
- s5p_mfc_init_memctrl(dev);
- /* 2. Initialize registers of channel I/F */
- s5p_mfc_clear_cmds(dev);
- /* 3. Release reset signal to the RISC */
- s5p_mfc_clean_dev_int_flags(dev);
- mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
- mfc_debug(2, "Will now wait for completion of firmware transfer\n");
- if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_FW_STATUS_RET)) {
- mfc_err("Failed to load firmware\n");
- s5p_mfc_reset(dev);
- s5p_mfc_clock_off();
- return -EIO;
- }
- s5p_mfc_clean_dev_int_flags(dev);
- /* 4. Initialize firmware */
- ret = s5p_mfc_sys_init_cmd(dev);
- if (ret) {
- mfc_err("Failed to send command to MFC - timeout\n");
- s5p_mfc_reset(dev);
- s5p_mfc_clock_off();
- return ret;
- }
- mfc_debug(2, "Ok, now will write a command to init the system\n");
- if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SYS_INIT_RET)) {
- mfc_err("Failed to load firmware\n");
- s5p_mfc_reset(dev);
- s5p_mfc_clock_off();
- return -EIO;
- }
- dev->int_cond = 0;
- if (dev->int_err != 0 || dev->int_type !=
- S5P_FIMV_R2H_CMD_SYS_INIT_RET) {
- /* Failure. */
- mfc_err("Failed to init firmware - error: %d int: %d\n",
- dev->int_err, dev->int_type);
- s5p_mfc_reset(dev);
- s5p_mfc_clock_off();
- return -EIO;
- }
- ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
- mfc_debug(2, "MFC F/W version : %02xyy, %02xmm, %02xdd\n",
- (ver >> 16) & 0xFF, (ver >> 8) & 0xFF, ver & 0xFF);
- s5p_mfc_clock_off();
- mfc_debug_leave();
- return 0;
-}
-
-
-int s5p_mfc_sleep(struct s5p_mfc_dev *dev)
-{
- int ret;
-
- mfc_debug_enter();
- s5p_mfc_clock_on();
- s5p_mfc_clean_dev_int_flags(dev);
- ret = s5p_mfc_sleep_cmd(dev);
- if (ret) {
- mfc_err("Failed to send command to MFC - timeout\n");
- return ret;
- }
- if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_SLEEP_RET)) {
- mfc_err("Failed to sleep\n");
- return -EIO;
- }
- s5p_mfc_clock_off();
- dev->int_cond = 0;
- if (dev->int_err != 0 || dev->int_type !=
- S5P_FIMV_R2H_CMD_SLEEP_RET) {
- /* Failure. */
- mfc_err("Failed to sleep - error: %d int: %d\n", dev->int_err,
- dev->int_type);
- return -EIO;
- }
- mfc_debug_leave();
- return ret;
-}
-
-int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
-{
- int ret;
-
- mfc_debug_enter();
- /* 0. MFC reset */
- mfc_debug(2, "MFC reset..\n");
- s5p_mfc_clock_on();
- ret = s5p_mfc_reset(dev);
- if (ret) {
- mfc_err("Failed to reset MFC - timeout\n");
- return ret;
- }
- mfc_debug(2, "Done MFC reset..\n");
- /* 1. Set DRAM base Addr */
- s5p_mfc_init_memctrl(dev);
- /* 2. Initialize registers of channel I/F */
- s5p_mfc_clear_cmds(dev);
- s5p_mfc_clean_dev_int_flags(dev);
- /* 3. Initialize firmware */
- ret = s5p_mfc_wakeup_cmd(dev);
- if (ret) {
- mfc_err("Failed to send command to MFC - timeout\n");
- return ret;
- }
- /* 4. Release reset signal to the RISC */
- mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
- mfc_debug(2, "Ok, now will write a command to wakeup the system\n");
- if (s5p_mfc_wait_for_done_dev(dev, S5P_FIMV_R2H_CMD_WAKEUP_RET)) {
- mfc_err("Failed to load firmware\n");
- return -EIO;
- }
- s5p_mfc_clock_off();
- dev->int_cond = 0;
- if (dev->int_err != 0 || dev->int_type !=
- S5P_FIMV_R2H_CMD_WAKEUP_RET) {
- /* Failure. */
- mfc_err("Failed to wakeup - error: %d int: %d\n", dev->int_err,
- dev->int_type);
- return -EIO;
- }
- mfc_debug_leave();
- return 0;
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
deleted file mode 100644
index 61dc23b7ee5a..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.h
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_CTRL_H
-#define S5P_MFC_CTRL_H
-
-#include "s5p_mfc_common.h"
-
-int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev);
-int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev);
-int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev);
-
-int s5p_mfc_init_hw(struct s5p_mfc_dev *dev);
-
-int s5p_mfc_sleep(struct s5p_mfc_dev *dev);
-int s5p_mfc_wakeup(struct s5p_mfc_dev *dev);
-
-int s5p_mfc_reset(struct s5p_mfc_dev *dev);
-
-#endif /* S5P_MFC_CTRL_H */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_debug.h b/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
deleted file mode 100644
index ecb8616a492a..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_debug.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * drivers/media/video/samsung/mfc5/s5p_mfc_debug.h
- *
- * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
- * This file contains debug macros
- *
- * Kamil Debski, Copyright (c) 2011 Samsung Electronics
- * http://www.samsung.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.
- */
-
-#ifndef S5P_MFC_DEBUG_H_
-#define S5P_MFC_DEBUG_H_
-
-#define DEBUG
-
-#ifdef DEBUG
-extern int debug;
-
-#define mfc_debug(level, fmt, args...) \
- do { \
- if (debug >= level) \
- printk(KERN_DEBUG "%s:%d: " fmt, \
- __func__, __LINE__, ##args); \
- } while (0)
-#else
-#define mfc_debug(level, fmt, args...)
-#endif
-
-#define mfc_debug_enter() mfc_debug(5, "enter")
-#define mfc_debug_leave() mfc_debug(5, "leave")
-
-#define mfc_err(fmt, args...) \
- do { \
- printk(KERN_ERR "%s:%d: " fmt, \
- __func__, __LINE__, ##args); \
- } while (0)
-
-#define mfc_info(fmt, args...) \
- do { \
- printk(KERN_INFO "%s:%d: " fmt, \
- __func__, __LINE__, ##args); \
- } while (0)
-
-#endif /* S5P_MFC_DEBUG_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
deleted file mode 100644
index c5d567f87d77..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.c
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- * Kamil Debski, <k.debski@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.
- */
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/version.h>
-#include <linux/videodev2.h>
-#include <linux/workqueue.h>
-#include <media/v4l2-ctrls.h>
-#include <media/videobuf2-core.h>
-#include "regs-mfc.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_dec.h"
-#include "s5p_mfc_intr.h"
-#include "s5p_mfc_opr.h"
-#include "s5p_mfc_pm.h"
-#include "s5p_mfc_shm.h"
-
-static struct s5p_mfc_fmt formats[] = {
- {
- .name = "4:2:0 2 Planes 64x32 Tiles",
- .fourcc = V4L2_PIX_FMT_NV12MT,
- .codec_mode = S5P_FIMV_CODEC_NONE,
- .type = MFC_FMT_RAW,
- .num_planes = 2,
- },
- {
- .name = "4:2:0 2 Planes",
- .fourcc = V4L2_PIX_FMT_NV12M,
- .codec_mode = S5P_FIMV_CODEC_NONE,
- .type = MFC_FMT_RAW,
- .num_planes = 2,
- },
- {
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- .codec_mode = S5P_FIMV_CODEC_H264_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "H263 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H263,
- .codec_mode = S5P_FIMV_CODEC_H263_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "MPEG1 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG1,
- .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "MPEG2 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG2,
- .codec_mode = S5P_FIMV_CODEC_MPEG2_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "XviD Encoded Stream",
- .fourcc = V4L2_PIX_FMT_XVID,
- .codec_mode = S5P_FIMV_CODEC_MPEG4_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "VC1 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
- .codec_mode = S5P_FIMV_CODEC_VC1_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
- {
- .name = "VC1 RCV Encoded Stream",
- .fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
- .codec_mode = S5P_FIMV_CODEC_VC1RCV_DEC,
- .type = MFC_FMT_DEC,
- .num_planes = 1,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-/* Find selected format description */
-static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
-{
- unsigned int i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
- formats[i].type == t)
- return &formats[i];
- }
- return NULL;
-}
-
-static struct mfc_control controls[] = {
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H264 Display Delay",
- .minimum = 0,
- .maximum = 16383,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "H264 Display Delay Enable",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mpeg4 Loop Filter Enable",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Slice Interface Enable",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Minimum number of cap bufs",
- .minimum = 1,
- .maximum = 32,
- .step = 1,
- .default_value = 1,
- .is_volatile = 1,
- },
-};
-
-#define NUM_CTRLS ARRAY_SIZE(controls)
-
-/* Check whether a context should be run on hardware */
-static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
-{
- /* Context is to parse header */
- if (ctx->src_queue_cnt >= 1 && ctx->state == MFCINST_GOT_INST)
- return 1;
- /* Context is to decode a frame */
- if (ctx->src_queue_cnt >= 1 &&
- ctx->state == MFCINST_RUNNING &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
- return 1;
- /* Context is to return last frame */
- if (ctx->state == MFCINST_FINISHING &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
- return 1;
- /* Context is to set buffers */
- if (ctx->src_queue_cnt >= 1 &&
- ctx->state == MFCINST_HEAD_PARSED &&
- ctx->capture_state == QUEUE_BUFS_MMAPED)
- return 1;
- /* Resolution change */
- if ((ctx->state == MFCINST_RES_CHANGE_INIT ||
- ctx->state == MFCINST_RES_CHANGE_FLUSH) &&
- ctx->dst_queue_cnt >= ctx->dpb_count)
- return 1;
- if (ctx->state == MFCINST_RES_CHANGE_END &&
- ctx->src_queue_cnt >= 1)
- return 1;
- mfc_debug(2, "ctx is not ready\n");
- return 0;
-}
-
-static struct s5p_mfc_codec_ops decoder_codec_ops = {
- .pre_seq_start = NULL,
- .post_seq_start = NULL,
- .pre_frame_start = NULL,
- .post_frame_start = NULL,
-};
-
-/* Query capabilities of the device */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
-
- strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_VIDEO_OUTPUT_MPLANE;
- return 0;
-}
-
-/* Enumerate format */
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
-{
- struct s5p_mfc_fmt *fmt;
- int i, j = 0;
-
- for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (mplane && formats[i].num_planes == 1)
- continue;
- else if (!mplane && formats[i].num_planes > 1)
- continue;
- if (out && formats[i].type != MFC_FMT_DEC)
- continue;
- else if (!out && formats[i].type != MFC_FMT_RAW)
- continue;
-
- if (j == f->index)
- break;
- ++j;
- }
- if (i == ARRAY_SIZE(formats))
- return -EINVAL;
- fmt = &formats[i];
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, false, false);
-}
-
-static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, false, true);
-}
-
-static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, true, true);
-}
-
-/* Get format */
-static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct v4l2_pix_format_mplane *pix_mp;
-
- mfc_debug_enter();
- pix_mp = &f->fmt.pix_mp;
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- (ctx->state == MFCINST_GOT_INST || ctx->state ==
- MFCINST_RES_CHANGE_END)) {
- /* If the MFC is parsing the header,
- * so wait until it is finished */
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_SEQ_DONE_RET,
- 0);
- }
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- ctx->state >= MFCINST_HEAD_PARSED &&
- ctx->state < MFCINST_ABORT) {
- /* This is run on CAPTURE (decode output) */
- /* Width and height are set to the dimensions
- of the movie, the buffer is bigger and
- further processing stages should crop to this
- rectangle. */
- pix_mp->width = ctx->buf_width;
- pix_mp->height = ctx->buf_height;
- pix_mp->field = V4L2_FIELD_NONE;
- pix_mp->num_planes = 2;
- /* Set pixelformat to the format in which MFC
- outputs the decoded frame */
- pix_mp->pixelformat = V4L2_PIX_FMT_NV12MT;
- pix_mp->plane_fmt[0].bytesperline = ctx->buf_width;
- pix_mp->plane_fmt[0].sizeimage = ctx->luma_size;
- pix_mp->plane_fmt[1].bytesperline = ctx->buf_width;
- pix_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- /* This is run on OUTPUT
- The buffer contains compressed image
- so width and height have no meaning */
- pix_mp->width = 0;
- pix_mp->height = 0;
- pix_mp->field = V4L2_FIELD_NONE;
- pix_mp->plane_fmt[0].bytesperline = ctx->dec_src_buf_size;
- pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size;
- pix_mp->pixelformat = ctx->src_fmt->fourcc;
- pix_mp->num_planes = ctx->src_fmt->num_planes;
- } else {
- mfc_err("Format could not be read\n");
- mfc_debug(2, "%s-- with error\n", __func__);
- return -EINVAL;
- }
- mfc_debug_leave();
- return 0;
-}
-
-/* Try format */
-static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_fmt *fmt;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- mfc_err("This node supports decoding only\n");
- return -EINVAL;
- }
- fmt = find_format(f, MFC_FMT_DEC);
- if (!fmt) {
- mfc_err("Unsupported format\n");
- return -EINVAL;
- }
- if (fmt->type != MFC_FMT_DEC) {
- mfc_err("\n");
- return -EINVAL;
- }
- return 0;
-}
-
-/* Set format */
-static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- int ret = 0;
- struct s5p_mfc_fmt *fmt;
- struct v4l2_pix_format_mplane *pix_mp;
-
- mfc_debug_enter();
- ret = vidioc_try_fmt(file, priv, f);
- pix_mp = &f->fmt.pix_mp;
- if (ret)
- return ret;
- if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
- v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
- ret = -EBUSY;
- goto out;
- }
- fmt = find_format(f, MFC_FMT_DEC);
- if (!fmt || fmt->codec_mode == S5P_FIMV_CODEC_NONE) {
- mfc_err("Unknown codec\n");
- ret = -EINVAL;
- goto out;
- }
- if (fmt->type != MFC_FMT_DEC) {
- mfc_err("Wrong format selected, you should choose "
- "format for decoding\n");
- ret = -EINVAL;
- goto out;
- }
- ctx->src_fmt = fmt;
- ctx->codec_mode = fmt->codec_mode;
- mfc_debug(2, "The codec number is: %d\n", ctx->codec_mode);
- pix_mp->height = 0;
- pix_mp->width = 0;
- if (pix_mp->plane_fmt[0].sizeimage)
- ctx->dec_src_buf_size = pix_mp->plane_fmt[0].sizeimage;
- else
- pix_mp->plane_fmt[0].sizeimage = ctx->dec_src_buf_size =
- DEF_CPB_SIZE;
- pix_mp->plane_fmt[0].bytesperline = 0;
- ctx->state = MFCINST_INIT;
-out:
- mfc_debug_leave();
- return ret;
-}
-
-/* Reqeust buffers */
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- int ret = 0;
- unsigned long flags;
-
- if (reqbufs->memory != V4L2_MEMORY_MMAP) {
- mfc_err("Only V4L2_MEMORY_MAP is supported\n");
- return -EINVAL;
- }
- if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- /* Can only request buffers after an instance has been opened.*/
- if (ctx->state == MFCINST_INIT) {
- ctx->src_bufs_cnt = 0;
- if (reqbufs->count == 0) {
- mfc_debug(2, "Freeing buffers\n");
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- s5p_mfc_clock_off();
- return ret;
- }
- /* Decoding */
- if (ctx->output_state != QUEUE_FREE) {
- mfc_err("Bufs have already been requested\n");
- return -EINVAL;
- }
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- s5p_mfc_clock_off();
- if (ret) {
- mfc_err("vb2_reqbufs on output failed\n");
- return ret;
- }
- mfc_debug(2, "vb2_reqbufs: %d\n", ret);
- ctx->output_state = QUEUE_BUFS_REQUESTED;
- }
- } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- ctx->dst_bufs_cnt = 0;
- if (reqbufs->count == 0) {
- mfc_debug(2, "Freeing buffers\n");
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return ret;
- }
- if (ctx->capture_state != QUEUE_FREE) {
- mfc_err("Bufs have already been requested\n");
- return -EINVAL;
- }
- ctx->capture_state = QUEUE_BUFS_REQUESTED;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- if (ret) {
- mfc_err("vb2_reqbufs on capture failed\n");
- return ret;
- }
- if (reqbufs->count < ctx->dpb_count) {
- mfc_err("Not enough buffers allocated\n");
- reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return -ENOMEM;
- }
- ctx->total_dpb_count = reqbufs->count;
- ret = s5p_mfc_alloc_codec_buffers(ctx);
- if (ret) {
- mfc_err("Failed to allocate decoding buffers\n");
- reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_clock_off();
- return -ENOMEM;
- }
- if (ctx->dst_bufs_cnt == ctx->total_dpb_count) {
- ctx->capture_state = QUEUE_BUFS_MMAPED;
- } else {
- mfc_err("Not all buffers passed to buf_init\n");
- reqbufs->count = 0;
- s5p_mfc_clock_on();
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- s5p_mfc_release_codec_buffers(ctx);
- s5p_mfc_clock_off();
- return -ENOMEM;
- }
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
- s5p_mfc_wait_for_done_ctx(ctx,
- S5P_FIMV_R2H_CMD_INIT_BUFFERS_RET, 0);
- }
- return ret;
-}
-
-/* Query buffer */
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- int ret;
- int i;
-
- if (buf->memory != V4L2_MEMORY_MMAP) {
- mfc_err("Only mmaped buffers can be used\n");
- return -EINVAL;
- }
- mfc_debug(2, "State: %d, buf->type: %d\n", ctx->state, buf->type);
- if (ctx->state == MFCINST_INIT &&
- buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = vb2_querybuf(&ctx->vq_src, buf);
- } else if (ctx->state == MFCINST_RUNNING &&
- buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- ret = vb2_querybuf(&ctx->vq_dst, buf);
- for (i = 0; i < buf->length; i++)
- buf->m.planes[i].m.mem_offset += DST_QUEUE_OFF_BASE;
- } else {
- mfc_err("vidioc_querybuf called in an inappropriate state\n");
- ret = -EINVAL;
- }
- mfc_debug_leave();
- return ret;
-}
-
-/* Queue a buffer */
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->state == MFCINST_ERROR) {
- mfc_err("Call on QBUF after unrecoverable error\n");
- return -EIO;
- }
- if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_qbuf(&ctx->vq_src, buf);
- else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_qbuf(&ctx->vq_dst, buf);
- return -EINVAL;
-}
-
-/* Dequeue a buffer */
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->state == MFCINST_ERROR) {
- mfc_err("Call on DQBUF after unrecoverable error\n");
- return -EIO;
- }
- if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
- else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
- return -EINVAL;
-}
-
-/* Stream on */
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- int ret = -EINVAL;
-
- mfc_debug_enter();
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
-
- if (ctx->state == MFCINST_INIT) {
- ctx->dst_bufs_cnt = 0;
- ctx->src_bufs_cnt = 0;
- ctx->capture_state = QUEUE_FREE;
- ctx->output_state = QUEUE_FREE;
- s5p_mfc_alloc_instance_buffer(ctx);
- s5p_mfc_alloc_dec_temp_buffers(ctx);
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_try_run(dev);
-
- if (s5p_mfc_wait_for_done_ctx(ctx,
- S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 0)) {
- /* Error or timeout */
- mfc_err("Error getting instance from hardware\n");
- s5p_mfc_release_instance_buffer(ctx);
- s5p_mfc_release_dec_desc_buffer(ctx);
- return -EIO;
- }
- mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
- }
- ret = vb2_streamon(&ctx->vq_src, type);
- }
- else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- ret = vb2_streamon(&ctx->vq_dst, type);
- mfc_debug_leave();
- return ret;
-}
-
-/* Stream off, which equals to a pause */
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_streamoff(&ctx->vq_src, type);
- else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_streamoff(&ctx->vq_dst, type);
- return -EINVAL;
-}
-
-/* Set controls - v4l2 control framework */
-static int s5p_mfc_dec_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY:
- ctx->display_delay = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE:
- ctx->display_delay_enable = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
- ctx->loop_filter_mpeg4 = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
- ctx->slice_interface = ctrl->val;
- break;
- default:
- mfc_err("Invalid control 0x%08x\n", ctrl->id);
- return -EINVAL;
- }
- return 0;
-}
-
-static int s5p_mfc_dec_g_v_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- switch (ctrl->id) {
- case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
- if (ctx->state >= MFCINST_HEAD_PARSED &&
- ctx->state < MFCINST_ABORT) {
- ctrl->val = ctx->dpb_count;
- break;
- } else if (ctx->state != MFCINST_INIT) {
- v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
- return -EINVAL;
- }
- /* Should wait for the header to be parsed */
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_wait_for_done_ctx(ctx,
- S5P_FIMV_R2H_CMD_SEQ_DONE_RET, 0);
- if (ctx->state >= MFCINST_HEAD_PARSED &&
- ctx->state < MFCINST_ABORT) {
- ctrl->val = ctx->dpb_count;
- } else {
- v4l2_err(&dev->v4l2_dev, "Decoding not initialised\n");
- return -EINVAL;
- }
- break;
- }
- return 0;
-}
-
-
-static const struct v4l2_ctrl_ops s5p_mfc_dec_ctrl_ops = {
- .s_ctrl = s5p_mfc_dec_s_ctrl,
- .g_volatile_ctrl = s5p_mfc_dec_g_v_ctrl,
-};
-
-/* Get cropping information */
-static int vidioc_g_crop(struct file *file, void *priv,
- struct v4l2_crop *cr)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- u32 left, right, top, bottom;
-
- if (ctx->state != MFCINST_HEAD_PARSED &&
- ctx->state != MFCINST_RUNNING && ctx->state != MFCINST_FINISHING
- && ctx->state != MFCINST_FINISHED) {
- mfc_err("Cannont set crop\n");
- return -EINVAL;
- }
- if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_H264) {
- left = s5p_mfc_read_shm(ctx, CROP_INFO_H);
- right = left >> S5P_FIMV_SHARED_CROP_RIGHT_SHIFT;
- left = left & S5P_FIMV_SHARED_CROP_LEFT_MASK;
- top = s5p_mfc_read_shm(ctx, CROP_INFO_V);
- bottom = top >> S5P_FIMV_SHARED_CROP_BOTTOM_SHIFT;
- top = top & S5P_FIMV_SHARED_CROP_TOP_MASK;
- cr->c.left = left;
- cr->c.top = top;
- cr->c.width = ctx->img_width - left - right;
- cr->c.height = ctx->img_height - top - bottom;
- mfc_debug(2, "Cropping info [h264]: l=%d t=%d "
- "w=%d h=%d (r=%d b=%d fw=%d fh=%d\n", left, top,
- cr->c.width, cr->c.height, right, bottom,
- ctx->buf_width, ctx->buf_height);
- } else {
- cr->c.left = 0;
- cr->c.top = 0;
- cr->c.width = ctx->img_width;
- cr->c.height = ctx->img_height;
- mfc_debug(2, "Cropping info: w=%d h=%d fw=%d "
- "fh=%d\n", cr->c.width, cr->c.height, ctx->buf_width,
- ctx->buf_height);
- }
- return 0;
-}
-
-/* v4l2_ioctl_ops */
-static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
- .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
- .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
- .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
- .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
- .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
- .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_crop = vidioc_g_crop,
-};
-
-static int s5p_mfc_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt, unsigned int *buf_count,
- unsigned int *plane_count, unsigned int psize[],
- void *allocators[])
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
-
- /* Video output for decoding (source)
- * this can be set after getting an instance */
- if (ctx->state == MFCINST_INIT &&
- vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- /* A single plane is required for input */
- *plane_count = 1;
- if (*buf_count < 1)
- *buf_count = 1;
- if (*buf_count > MFC_MAX_BUFFERS)
- *buf_count = MFC_MAX_BUFFERS;
- /* Video capture for decoding (destination)
- * this can be set after the header was parsed */
- } else if (ctx->state == MFCINST_HEAD_PARSED &&
- vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- /* Output plane count is 2 - one for Y and one for CbCr */
- *plane_count = 2;
- /* Setup buffer count */
- if (*buf_count < ctx->dpb_count)
- *buf_count = ctx->dpb_count;
- if (*buf_count > ctx->dpb_count + MFC_MAX_EXTRA_DPB)
- *buf_count = ctx->dpb_count + MFC_MAX_EXTRA_DPB;
- if (*buf_count > MFC_MAX_BUFFERS)
- *buf_count = MFC_MAX_BUFFERS;
- } else {
- mfc_err("State seems invalid. State = %d, vq->type = %d\n",
- ctx->state, vq->type);
- return -EINVAL;
- }
- mfc_debug(2, "Buffer count=%d, plane count=%d\n",
- *buf_count, *plane_count);
- if (ctx->state == MFCINST_HEAD_PARSED &&
- vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- psize[0] = ctx->luma_size;
- psize[1] = ctx->chroma_size;
- allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
- allocators[1] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
- ctx->state == MFCINST_INIT) {
- psize[0] = ctx->dec_src_buf_size;
- allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
- } else {
- mfc_err("This video node is dedicated to decoding. Decoding not initalised\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static void s5p_mfc_unlock(struct vb2_queue *q)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mutex_unlock(&dev->mfc_mutex);
-}
-
-static void s5p_mfc_lock(struct vb2_queue *q)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mutex_lock(&dev->mfc_mutex);
-}
-
-static int s5p_mfc_buf_init(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
- unsigned int i;
-
- if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- if (ctx->capture_state == QUEUE_BUFS_MMAPED)
- return 0;
- for (i = 0; i <= ctx->src_fmt->num_planes ; i++) {
- if (IS_ERR_OR_NULL(ERR_PTR(
- vb2_dma_contig_plane_dma_addr(vb, i)))) {
- mfc_err("Plane mem not allocated\n");
- return -EINVAL;
- }
- }
- if (vb2_plane_size(vb, 0) < ctx->luma_size ||
- vb2_plane_size(vb, 1) < ctx->chroma_size) {
- mfc_err("Plane buffer (CAPTURE) is too small\n");
- return -EINVAL;
- }
- i = vb->v4l2_buf.index;
- ctx->dst_bufs[i].b = vb;
- ctx->dst_bufs[i].cookie.raw.luma =
- vb2_dma_contig_plane_dma_addr(vb, 0);
- ctx->dst_bufs[i].cookie.raw.chroma =
- vb2_dma_contig_plane_dma_addr(vb, 1);
- ctx->dst_bufs_cnt++;
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (IS_ERR_OR_NULL(ERR_PTR(
- vb2_dma_contig_plane_dma_addr(vb, 0)))) {
- mfc_err("Plane memory not allocated\n");
- return -EINVAL;
- }
- if (vb2_plane_size(vb, 0) < ctx->dec_src_buf_size) {
- mfc_err("Plane buffer (OUTPUT) is too small\n");
- return -EINVAL;
- }
-
- i = vb->v4l2_buf.index;
- ctx->src_bufs[i].b = vb;
- ctx->src_bufs[i].cookie.stream =
- vb2_dma_contig_plane_dma_addr(vb, 0);
- ctx->src_bufs_cnt++;
- } else {
- mfc_err("s5p_mfc_buf_init: unknown queue type\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
-
- v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
- if (ctx->state == MFCINST_FINISHING ||
- ctx->state == MFCINST_FINISHED)
- ctx->state = MFCINST_RUNNING;
- /* If context is ready then dev = work->data;schedule it to run */
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
- return 0;
-}
-
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
-{
- unsigned long flags;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- int aborted = 0;
-
- if ((ctx->state == MFCINST_FINISHING ||
- ctx->state == MFCINST_RUNNING) &&
- dev->curr_ctx == ctx->num && dev->hw_lock) {
- ctx->state = MFCINST_ABORT;
- s5p_mfc_wait_for_done_ctx(ctx,
- S5P_FIMV_R2H_CMD_FRAME_DONE_RET, 0);
- aborted = 1;
- }
- spin_lock_irqsave(&dev->irqlock, flags);
- if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
- INIT_LIST_HEAD(&ctx->dst_queue);
- ctx->dst_queue_cnt = 0;
- ctx->dpb_flush_flag = 1;
- ctx->dec_dst_flag = 0;
- }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
- INIT_LIST_HEAD(&ctx->src_queue);
- ctx->src_queue_cnt = 0;
- }
- if (aborted)
- ctx->state = MFCINST_RUNNING;
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return 0;
-}
-
-
-static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *mfc_buf;
-
- if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
- mfc_buf->used = 0;
- spin_lock_irqsave(&dev->irqlock, flags);
- list_add_tail(&mfc_buf->list, &ctx->src_queue);
- ctx->src_queue_cnt++;
- spin_unlock_irqrestore(&dev->irqlock, flags);
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
- mfc_buf->used = 0;
- /* Mark destination as available for use by MFC */
- spin_lock_irqsave(&dev->irqlock, flags);
- set_bit(vb->v4l2_buf.index, &ctx->dec_dst_flag);
- list_add_tail(&mfc_buf->list, &ctx->dst_queue);
- ctx->dst_queue_cnt++;
- spin_unlock_irqrestore(&dev->irqlock, flags);
- } else {
- mfc_err("Unsupported buffer type (%d)\n", vq->type);
- }
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
-}
-
-static struct vb2_ops s5p_mfc_dec_qops = {
- .queue_setup = s5p_mfc_queue_setup,
- .wait_prepare = s5p_mfc_unlock,
- .wait_finish = s5p_mfc_lock,
- .buf_init = s5p_mfc_buf_init,
- .start_streaming = s5p_mfc_start_streaming,
- .stop_streaming = s5p_mfc_stop_streaming,
- .buf_queue = s5p_mfc_buf_queue,
-};
-
-struct s5p_mfc_codec_ops *get_dec_codec_ops(void)
-{
- return &decoder_codec_ops;
-}
-
-struct vb2_ops *get_dec_queue_ops(void)
-{
- return &s5p_mfc_dec_qops;
-}
-
-const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void)
-{
- return &s5p_mfc_dec_ioctl_ops;
-}
-
-#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
- && V4L2_CTRL_DRIVER_PRIV(x))
-
-int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx)
-{
- struct v4l2_ctrl_config cfg;
- int i;
-
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
- if (ctx->ctrl_handler.error) {
- mfc_err("v4l2_ctrl_handler_init failed\n");
- return ctx->ctrl_handler.error;
- }
-
- for (i = 0; i < NUM_CTRLS; i++) {
- if (IS_MFC51_PRIV(controls[i].id)) {
- memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
- cfg.ops = &s5p_mfc_dec_ctrl_ops;
- cfg.id = controls[i].id;
- cfg.min = controls[i].minimum;
- cfg.max = controls[i].maximum;
- cfg.def = controls[i].default_value;
- cfg.name = controls[i].name;
- cfg.type = controls[i].type;
-
- cfg.step = controls[i].step;
- cfg.menu_skip_mask = 0;
-
- ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
- &cfg, NULL);
- } else {
- ctx->ctrls[i] = v4l2_ctrl_new_std(&ctx->ctrl_handler,
- &s5p_mfc_dec_ctrl_ops,
- controls[i].id, controls[i].minimum,
- controls[i].maximum, controls[i].step,
- controls[i].default_value);
- }
- if (ctx->ctrl_handler.error) {
- mfc_err("Adding control (%d) failed\n", i);
- return ctx->ctrl_handler.error;
- }
- if (controls[i].is_volatile && ctx->ctrls[i])
- ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
- }
- return 0;
-}
-
-void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx)
-{
- int i;
-
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- for (i = 0; i < NUM_CTRLS; i++)
- ctx->ctrls[i] = NULL;
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.h b/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
deleted file mode 100644
index fb8b215db0e7..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_dec.h
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_DEC_H_
-#define S5P_MFC_DEC_H_
-
-struct s5p_mfc_codec_ops *get_dec_codec_ops(void);
-struct vb2_ops *get_dec_queue_ops(void);
-const struct v4l2_ioctl_ops *get_dec_v4l2_ioctl_ops(void);
-struct s5p_mfc_fmt *get_dec_def_fmt(bool src);
-int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_dec_ctrls_delete(struct s5p_mfc_ctx *ctx);
-
-#endif /* S5P_MFC_DEC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
deleted file mode 100644
index aa1c244cf66e..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+++ /dev/null
@@ -1,1834 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * Jeongtae Park <jtp.park@samsung.com>
- * Kamil Debski <k.debski@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.
- */
-
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/version.h>
-#include <linux/videodev2.h>
-#include <linux/workqueue.h>
-#include <media/v4l2-ctrls.h>
-#include <media/videobuf2-core.h>
-#include "regs-mfc.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_enc.h"
-#include "s5p_mfc_intr.h"
-#include "s5p_mfc_opr.h"
-
-static struct s5p_mfc_fmt formats[] = {
- {
- .name = "4:2:0 2 Planes 64x32 Tiles",
- .fourcc = V4L2_PIX_FMT_NV12MT,
- .codec_mode = S5P_FIMV_CODEC_NONE,
- .type = MFC_FMT_RAW,
- .num_planes = 2,
- },
- {
- .name = "4:2:0 2 Planes",
- .fourcc = V4L2_PIX_FMT_NV12M,
- .codec_mode = S5P_FIMV_CODEC_NONE,
- .type = MFC_FMT_RAW,
- .num_planes = 2,
- },
- {
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- .codec_mode = S5P_FIMV_CODEC_H264_ENC,
- .type = MFC_FMT_ENC,
- .num_planes = 1,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- .codec_mode = S5P_FIMV_CODEC_MPEG4_ENC,
- .type = MFC_FMT_ENC,
- .num_planes = 1,
- },
- {
- .name = "H263 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H263,
- .codec_mode = S5P_FIMV_CODEC_H263_ENC,
- .type = MFC_FMT_ENC,
- .num_planes = 1,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-static struct s5p_mfc_fmt *find_format(struct v4l2_format *f, unsigned int t)
-{
- unsigned int i;
-
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
- formats[i].type == t)
- return &formats[i];
- }
- return NULL;
-}
-
-static struct mfc_control controls[] = {
- {
- .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
- .maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
- .default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 1,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 1900,
- .maximum = (1 << 30) - 1,
- .step = 1,
- .default_value = 1900,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Padding Control Enable",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Padding Color YUV Value",
- .minimum = 0,
- .maximum = (1 << 25) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_BITRATE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 1,
- .maximum = (1 << 30) - 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Rate Control Reaction Coeff.",
- .minimum = 1,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Force frame type",
- .minimum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
- .maximum = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED,
- .default_value = V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_VBV_SIZE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
- .maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
- .default_value = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Frame Skip Enable",
- .minimum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
- .maximum = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
- .menu_skip_mask = 0,
- .default_value = V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Fixed Target Bit Enable",
- .minimum = 0,
- .maximum = 1,
- .default_value = 0,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 2,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
- .maximum = V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
- .default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
- .menu_skip_mask = ~(
- (1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
- (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
- (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)
- ),
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
- .maximum = V4L2_MPEG_VIDEO_H264_LEVEL_4_0,
- .default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
- .maximum = V4L2_MPEG_VIDEO_MPEG4_LEVEL_5,
- .default_value = V4L2_MPEG_VIDEO_MPEG4_LEVEL_0,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
- .maximum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY,
- .default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = -6,
- .maximum = 6,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = -6,
- .maximum = 6,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
- .maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
- .default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "The Number of Ref. Pic for P",
- .minimum = 1,
- .maximum = 2,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H263 I-Frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H263_MIN_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H263 Minimum QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H263_MAX_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H263 Maximum QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H263 P frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "H263 B frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "MPEG4 I-Frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "MPEG4 Minimum QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "MPEG4 Maximum QP value",
- .minimum = 0,
- .maximum = 51,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "MPEG4 P frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "MPEG4 B frame QP value",
- .minimum = 1,
- .maximum = 31,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "H264 Dark Reg Adaptive RC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "H264 Smooth Reg Adaptive RC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "H264 Static Reg Adaptive RC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "H264 Activity Reg Adaptive RC",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
- .maximum = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
- .default_value = V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .minimum = 0,
- .maximum = (1 << 16) - 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
- .type = V4L2_CTRL_TYPE_MENU,
- .minimum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
- .maximum = V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE,
- .default_value = V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE,
- .menu_skip_mask = 0,
- },
- {
- .id = V4L2_CID_MPEG_VIDEO_MPEG4_QPEL,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
-};
-
-#define NUM_CTRLS ARRAY_SIZE(controls)
-static const char * const *mfc51_get_menu(u32 id)
-{
- static const char * const mfc51_video_frame_skip[] = {
- "Disabled",
- "Level Limit",
- "VBV/CPB Limit",
- NULL,
- };
- static const char * const mfc51_video_force_frame[] = {
- "Disabled",
- "I Frame",
- "Not Coded",
- NULL,
- };
- switch (id) {
- case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
- return mfc51_video_frame_skip;
- case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
- return mfc51_video_force_frame;
- }
- return NULL;
-}
-
-static int s5p_mfc_ctx_ready(struct s5p_mfc_ctx *ctx)
-{
- mfc_debug(2, "src=%d, dst=%d, state=%d\n",
- ctx->src_queue_cnt, ctx->dst_queue_cnt, ctx->state);
- /* context is ready to make header */
- if (ctx->state == MFCINST_GOT_INST && ctx->dst_queue_cnt >= 1)
- return 1;
- /* context is ready to encode a frame */
- if (ctx->state == MFCINST_RUNNING &&
- ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
- return 1;
- /* context is ready to encode remain frames */
- if (ctx->state == MFCINST_FINISHING &&
- ctx->src_queue_cnt >= 1 && ctx->dst_queue_cnt >= 1)
- return 1;
- mfc_debug(2, "ctx is not ready\n");
- return 0;
-}
-
-static void cleanup_ref_queue(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_buf *mb_entry;
- unsigned long mb_y_addr, mb_c_addr;
-
- /* move buffers in ref queue to src queue */
- while (!list_empty(&ctx->ref_queue)) {
- mb_entry = list_entry((&ctx->ref_queue)->next,
- struct s5p_mfc_buf, list);
- mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
- list_del(&mb_entry->list);
- ctx->ref_queue_cnt--;
- list_add_tail(&mb_entry->list, &ctx->src_queue);
- ctx->src_queue_cnt++;
- }
- mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
- ctx->src_queue_cnt, ctx->ref_queue_cnt);
- INIT_LIST_HEAD(&ctx->ref_queue);
- ctx->ref_queue_cnt = 0;
-}
-
-static int enc_pre_seq_start(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *dst_mb;
- unsigned long dst_addr;
- unsigned int dst_size;
- unsigned long flags;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
- dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return 0;
-}
-
-static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- struct s5p_mfc_buf *dst_mb;
- unsigned long flags;
-
- if (p->seq_hdr_mode == V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) {
- spin_lock_irqsave(&dev->irqlock, flags);
- dst_mb = list_entry(ctx->dst_queue.next,
- struct s5p_mfc_buf, list);
- list_del(&dst_mb->list);
- ctx->dst_queue_cnt--;
- vb2_set_plane_payload(dst_mb->b, 0,
- s5p_mfc_get_enc_strm_size());
- vb2_buffer_done(dst_mb->b, VB2_BUF_STATE_DONE);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- }
- ctx->state = MFCINST_RUNNING;
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
- return 0;
-}
-
-static int enc_pre_frame_start(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *dst_mb;
- struct s5p_mfc_buf *src_mb;
- unsigned long flags;
- unsigned long src_y_addr, src_c_addr, dst_addr;
- unsigned int dst_size;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
- src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
- s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
- spin_lock_irqsave(&dev->irqlock, flags);
- dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
- dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
-
- return 0;
-}
-
-static int enc_post_frame_start(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *mb_entry;
- unsigned long enc_y_addr, enc_c_addr;
- unsigned long mb_y_addr, mb_c_addr;
- int slice_type;
- unsigned int strm_size;
- unsigned long flags;
-
- slice_type = s5p_mfc_get_enc_slice_type();
- strm_size = s5p_mfc_get_enc_strm_size();
- mfc_debug(2, "Encoded slice type: %d", slice_type);
- mfc_debug(2, "Encoded stream size: %d", strm_size);
- mfc_debug(2, "Display order: %d",
- mfc_read(dev, S5P_FIMV_ENC_SI_PIC_CNT));
- spin_lock_irqsave(&dev->irqlock, flags);
- if (slice_type >= 0) {
- s5p_mfc_get_enc_frame_buffer(ctx, &enc_y_addr, &enc_c_addr);
- list_for_each_entry(mb_entry, &ctx->src_queue, list) {
- mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
- if ((enc_y_addr == mb_y_addr) &&
- (enc_c_addr == mb_c_addr)) {
- list_del(&mb_entry->list);
- ctx->src_queue_cnt--;
- vb2_buffer_done(mb_entry->b,
- VB2_BUF_STATE_DONE);
- break;
- }
- }
- list_for_each_entry(mb_entry, &ctx->ref_queue, list) {
- mb_y_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 0);
- mb_c_addr = vb2_dma_contig_plane_dma_addr(mb_entry->b, 1);
- if ((enc_y_addr == mb_y_addr) &&
- (enc_c_addr == mb_c_addr)) {
- list_del(&mb_entry->list);
- ctx->ref_queue_cnt--;
- vb2_buffer_done(mb_entry->b,
- VB2_BUF_STATE_DONE);
- break;
- }
- }
- }
- if ((ctx->src_queue_cnt > 0) && (ctx->state == MFCINST_RUNNING)) {
- mb_entry = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
- list);
- if (mb_entry->used) {
- list_del(&mb_entry->list);
- ctx->src_queue_cnt--;
- list_add_tail(&mb_entry->list, &ctx->ref_queue);
- ctx->ref_queue_cnt++;
- }
- mfc_debug(2, "enc src count: %d, enc ref count: %d\n",
- ctx->src_queue_cnt, ctx->ref_queue_cnt);
- }
- if (strm_size > 0) {
- /* at least one more dest. buffers exist always */
- mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
- list);
- list_del(&mb_entry->list);
- ctx->dst_queue_cnt--;
- switch (slice_type) {
- case S5P_FIMV_ENC_SI_SLICE_TYPE_I:
- mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
- break;
- case S5P_FIMV_ENC_SI_SLICE_TYPE_P:
- mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
- break;
- case S5P_FIMV_ENC_SI_SLICE_TYPE_B:
- mb_entry->b->v4l2_buf.flags |= V4L2_BUF_FLAG_BFRAME;
- break;
- }
- vb2_set_plane_payload(mb_entry->b, 0, strm_size);
- vb2_buffer_done(mb_entry->b, VB2_BUF_STATE_DONE);
- }
- spin_unlock_irqrestore(&dev->irqlock, flags);
- if ((ctx->src_queue_cnt == 0) || (ctx->dst_queue_cnt == 0)) {
- spin_lock(&dev->condlock);
- clear_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock(&dev->condlock);
- }
- return 0;
-}
-
-static struct s5p_mfc_codec_ops encoder_codec_ops = {
- .pre_seq_start = enc_pre_seq_start,
- .post_seq_start = enc_post_seq_start,
- .pre_frame_start = enc_pre_frame_start,
- .post_frame_start = enc_post_frame_start,
-};
-
-/* Query capabilities of the device */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
-
- strncpy(cap->driver, dev->plat_dev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, dev->plat_dev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->version = KERNEL_VERSION(1, 0, 0);
- /*
- * This is only a mem-to-mem video device. The capture and output
- * device capability flags are left only for backward compatibility
- * and are scheduled for removal.
- */
- cap->capabilities = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_VIDEO_OUTPUT_MPLANE;
- return 0;
-}
-
-static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, bool mplane, bool out)
-{
- struct s5p_mfc_fmt *fmt;
- int i, j = 0;
-
- for (i = 0; i < ARRAY_SIZE(formats); ++i) {
- if (mplane && formats[i].num_planes == 1)
- continue;
- else if (!mplane && formats[i].num_planes > 1)
- continue;
- if (out && formats[i].type != MFC_FMT_RAW)
- continue;
- else if (!out && formats[i].type != MFC_FMT_ENC)
- continue;
- if (j == f->index) {
- fmt = &formats[i];
- strlcpy(f->description, fmt->name,
- sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- return 0;
- }
- ++j;
- }
- return -EINVAL;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, false, false);
-}
-
-static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *pirv,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, true, false);
-}
-
-static int vidioc_enum_fmt_vid_out(struct file *file, void *prov,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, false, true);
-}
-
-static int vidioc_enum_fmt_vid_out_mplane(struct file *file, void *prov,
- struct v4l2_fmtdesc *f)
-{
- return vidioc_enum_fmt(f, true, true);
-}
-
-static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
-
- mfc_debug(2, "f->type = %d ctx->state = %d\n", f->type, ctx->state);
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- /* This is run on output (encoder dest) */
- pix_fmt_mp->width = 0;
- pix_fmt_mp->height = 0;
- pix_fmt_mp->field = V4L2_FIELD_NONE;
- pix_fmt_mp->pixelformat = ctx->dst_fmt->fourcc;
- pix_fmt_mp->num_planes = ctx->dst_fmt->num_planes;
-
- pix_fmt_mp->plane_fmt[0].bytesperline = ctx->enc_dst_buf_size;
- pix_fmt_mp->plane_fmt[0].sizeimage = ctx->enc_dst_buf_size;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- /* This is run on capture (encoder src) */
- pix_fmt_mp->width = ctx->img_width;
- pix_fmt_mp->height = ctx->img_height;
-
- pix_fmt_mp->field = V4L2_FIELD_NONE;
- pix_fmt_mp->pixelformat = ctx->src_fmt->fourcc;
- pix_fmt_mp->num_planes = ctx->src_fmt->num_planes;
-
- pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
- pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
- pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
- pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
- } else {
- mfc_err("invalid buf type\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_fmt *fmt;
- struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fmt = find_format(f, MFC_FMT_ENC);
- if (!fmt) {
- mfc_err("failed to try output format\n");
- return -EINVAL;
- }
-
- if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
- mfc_err("must be set encoding output size\n");
- return -EINVAL;
- }
-
- pix_fmt_mp->plane_fmt[0].bytesperline =
- pix_fmt_mp->plane_fmt[0].sizeimage;
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- fmt = find_format(f, MFC_FMT_RAW);
- if (!fmt) {
- mfc_err("failed to try output format\n");
- return -EINVAL;
- }
-
- if (fmt->num_planes != pix_fmt_mp->num_planes) {
- mfc_err("failed to try output format\n");
- return -EINVAL;
- }
- v4l_bound_align_image(&pix_fmt_mp->width, 8, 1920, 1,
- &pix_fmt_mp->height, 4, 1080, 1, 0);
- } else {
- mfc_err("invalid buf type\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
-{
- struct s5p_mfc_dev *dev = video_drvdata(file);
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- struct s5p_mfc_fmt *fmt;
- struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp;
- unsigned long flags;
- int ret = 0;
-
- ret = vidioc_try_fmt(file, priv, f);
- if (ret)
- return ret;
- if (ctx->vq_src.streaming || ctx->vq_dst.streaming) {
- v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
- ret = -EBUSY;
- goto out;
- }
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fmt = find_format(f, MFC_FMT_ENC);
- if (!fmt) {
- mfc_err("failed to set capture format\n");
- return -EINVAL;
- }
- ctx->state = MFCINST_INIT;
- ctx->dst_fmt = fmt;
- ctx->codec_mode = ctx->dst_fmt->codec_mode;
- ctx->enc_dst_buf_size = pix_fmt_mp->plane_fmt[0].sizeimage;
- pix_fmt_mp->plane_fmt[0].bytesperline = 0;
- ctx->dst_bufs_cnt = 0;
- ctx->capture_state = QUEUE_FREE;
- s5p_mfc_alloc_instance_buffer(ctx);
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_try_run(dev);
- if (s5p_mfc_wait_for_done_ctx(ctx, \
- S5P_FIMV_R2H_CMD_OPEN_INSTANCE_RET, 1)) {
- /* Error or timeout */
- mfc_err("Error getting instance from hardware\n");
- s5p_mfc_release_instance_buffer(ctx);
- ret = -EIO;
- goto out;
- }
- mfc_debug(2, "Got instance number: %d\n", ctx->inst_no);
- } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- fmt = find_format(f, MFC_FMT_RAW);
- if (!fmt) {
- mfc_err("failed to set output format\n");
- return -EINVAL;
- }
- if (fmt->num_planes != pix_fmt_mp->num_planes) {
- mfc_err("failed to set output format\n");
- ret = -EINVAL;
- goto out;
- }
- ctx->src_fmt = fmt;
- ctx->img_width = pix_fmt_mp->width;
- ctx->img_height = pix_fmt_mp->height;
- mfc_debug(2, "codec number: %d\n", ctx->src_fmt->codec_mode);
- mfc_debug(2, "fmt - w: %d, h: %d, ctx - w: %d, h: %d\n",
- pix_fmt_mp->width, pix_fmt_mp->height,
- ctx->img_width, ctx->img_height);
- if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M) {
- ctx->buf_width = ALIGN(ctx->img_width,
- S5P_FIMV_NV12M_HALIGN);
- ctx->luma_size = ALIGN(ctx->img_width,
- S5P_FIMV_NV12M_HALIGN) * ALIGN(ctx->img_height,
- S5P_FIMV_NV12M_LVALIGN);
- ctx->chroma_size = ALIGN(ctx->img_width,
- S5P_FIMV_NV12M_HALIGN) * ALIGN((ctx->img_height
- >> 1), S5P_FIMV_NV12M_CVALIGN);
-
- ctx->luma_size = ALIGN(ctx->luma_size,
- S5P_FIMV_NV12M_SALIGN);
- ctx->chroma_size = ALIGN(ctx->chroma_size,
- S5P_FIMV_NV12M_SALIGN);
-
- pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
- pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
- pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
- pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
-
- } else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
- ctx->buf_width = ALIGN(ctx->img_width,
- S5P_FIMV_NV12MT_HALIGN);
- ctx->luma_size = ALIGN(ctx->img_width,
- S5P_FIMV_NV12MT_HALIGN) * ALIGN(ctx->img_height,
- S5P_FIMV_NV12MT_VALIGN);
- ctx->chroma_size = ALIGN(ctx->img_width,
- S5P_FIMV_NV12MT_HALIGN) * ALIGN((ctx->img_height
- >> 1), S5P_FIMV_NV12MT_VALIGN);
- ctx->luma_size = ALIGN(ctx->luma_size,
- S5P_FIMV_NV12MT_SALIGN);
- ctx->chroma_size = ALIGN(ctx->chroma_size,
- S5P_FIMV_NV12MT_SALIGN);
-
- pix_fmt_mp->plane_fmt[0].sizeimage = ctx->luma_size;
- pix_fmt_mp->plane_fmt[0].bytesperline = ctx->buf_width;
- pix_fmt_mp->plane_fmt[1].sizeimage = ctx->chroma_size;
- pix_fmt_mp->plane_fmt[1].bytesperline = ctx->buf_width;
- }
- ctx->src_bufs_cnt = 0;
- ctx->output_state = QUEUE_FREE;
- } else {
- mfc_err("invalid buf type\n");
- return -EINVAL;
- }
-out:
- mfc_debug_leave();
- return ret;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- int ret = 0;
-
- /* if memory is not mmp or userptr return error */
- if ((reqbufs->memory != V4L2_MEMORY_MMAP) &&
- (reqbufs->memory != V4L2_MEMORY_USERPTR))
- return -EINVAL;
- if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- if (ctx->capture_state != QUEUE_FREE) {
- mfc_err("invalid capture state: %d\n",
- ctx->capture_state);
- return -EINVAL;
- }
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- if (ret != 0) {
- mfc_err("error in vb2_reqbufs() for E(D)\n");
- return ret;
- }
- ctx->capture_state = QUEUE_BUFS_REQUESTED;
- ret = s5p_mfc_alloc_codec_buffers(ctx);
- if (ret) {
- mfc_err("Failed to allocate encoding buffers\n");
- reqbufs->count = 0;
- ret = vb2_reqbufs(&ctx->vq_dst, reqbufs);
- return -ENOMEM;
- }
- } else if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (ctx->output_state != QUEUE_FREE) {
- mfc_err("invalid output state: %d\n",
- ctx->output_state);
- return -EINVAL;
- }
- ret = vb2_reqbufs(&ctx->vq_src, reqbufs);
- if (ret != 0) {
- mfc_err("error in vb2_reqbufs() for E(S)\n");
- return ret;
- }
- ctx->output_state = QUEUE_BUFS_REQUESTED;
- } else {
- mfc_err("invalid buf type\n");
- return -EINVAL;
- }
- return ret;
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
- int ret = 0;
-
- /* if memory is not mmp or userptr return error */
- if ((buf->memory != V4L2_MEMORY_MMAP) &&
- (buf->memory != V4L2_MEMORY_USERPTR))
- return -EINVAL;
- if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- if (ctx->state != MFCINST_GOT_INST) {
- mfc_err("invalid context state: %d\n", ctx->state);
- return -EINVAL;
- }
- ret = vb2_querybuf(&ctx->vq_dst, buf);
- if (ret != 0) {
- mfc_err("error in vb2_querybuf() for E(D)\n");
- return ret;
- }
- buf->m.planes[0].m.mem_offset += DST_QUEUE_OFF_BASE;
- } else if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = vb2_querybuf(&ctx->vq_src, buf);
- if (ret != 0) {
- mfc_err("error in vb2_querybuf() for E(S)\n");
- return ret;
- }
- } else {
- mfc_err("invalid buf type\n");
- return -EINVAL;
- }
- return ret;
-}
-
-/* Queue a buffer */
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->state == MFCINST_ERROR) {
- mfc_err("Call on QBUF after unrecoverable error\n");
- return -EIO;
- }
- if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_qbuf(&ctx->vq_src, buf);
- else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_qbuf(&ctx->vq_dst, buf);
- return -EINVAL;
-}
-
-/* Dequeue a buffer */
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (ctx->state == MFCINST_ERROR) {
- mfc_err("Call on DQBUF after unrecoverable error\n");
- return -EIO;
- }
- if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_dqbuf(&ctx->vq_src, buf, file->f_flags & O_NONBLOCK);
- else if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_dqbuf(&ctx->vq_dst, buf, file->f_flags & O_NONBLOCK);
- return -EINVAL;
-}
-
-/* Stream on */
-static int vidioc_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_streamon(&ctx->vq_src, type);
- else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_streamon(&ctx->vq_dst, type);
- return -EINVAL;
-}
-
-/* Stream off, which equals to a pause */
-static int vidioc_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return vb2_streamoff(&ctx->vq_src, type);
- else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return vb2_streamoff(&ctx->vq_dst, type);
- return -EINVAL;
-}
-
-static inline int h264_level(enum v4l2_mpeg_video_h264_level lvl)
-{
- static unsigned int t[V4L2_MPEG_VIDEO_H264_LEVEL_4_0 + 1] = {
- /* V4L2_MPEG_VIDEO_H264_LEVEL_1_0 */ 10,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_1B */ 9,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_1_1 */ 11,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_1_2 */ 12,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_1_3 */ 13,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_2_0 */ 20,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_2_1 */ 21,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_2_2 */ 22,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_3_0 */ 30,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_3_1 */ 31,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_3_2 */ 32,
- /* V4L2_MPEG_VIDEO_H264_LEVEL_4_0 */ 40,
- };
- return t[lvl];
-}
-
-static inline int mpeg4_level(enum v4l2_mpeg_video_mpeg4_level lvl)
-{
- static unsigned int t[V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 + 1] = {
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 */ 0,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B */ 9,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 */ 1,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 */ 2,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 */ 3,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B */ 7,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 */ 4,
- /* V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 */ 5,
- };
- return t[lvl];
-}
-
-static inline int vui_sar_idc(enum v4l2_mpeg_video_h264_vui_sar_idc sar)
-{
- static unsigned int t[V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED + 1] = {
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED */ 0,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 */ 1,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 */ 2,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 */ 3,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 */ 4,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 */ 5,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 */ 6,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 */ 7,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 */ 8,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 */ 9,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 */ 10,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 */ 11,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 */ 12,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 */ 13,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 */ 14,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 */ 15,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 */ 16,
- /* V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED */ 255,
- };
- return t[sar];
-}
-
-static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct s5p_mfc_ctx *ctx = ctrl_to_ctx(ctrl);
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
- p->gop_size = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
- p->slice_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
- p->slice_mb = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
- p->slice_bit = ctrl->val * 8;
- break;
- case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB:
- p->intra_refresh_mb = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_PADDING:
- p->pad = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV:
- p->pad_luma = (ctrl->val >> 16) & 0xff;
- p->pad_cb = (ctrl->val >> 8) & 0xff;
- p->pad_cr = (ctrl->val >> 0) & 0xff;
- break;
- case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
- p->rc_frame = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_BITRATE:
- p->rc_bitrate = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF:
- p->rc_reaction_coeff = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE:
- ctx->force_frame_type = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
- p->vbv_size = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
- p->codec.h264.cpb_size = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
- p->seq_hdr_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE:
- p->frame_skip_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT:
- p->fixed_target_bit = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
- p->num_b_frame = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- switch (ctrl->val) {
- case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
- p->codec.h264.profile =
- S5P_FIMV_ENC_PROFILE_H264_MAIN;
- break;
- case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
- p->codec.h264.profile =
- S5P_FIMV_ENC_PROFILE_H264_HIGH;
- break;
- case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
- p->codec.h264.profile =
- S5P_FIMV_ENC_PROFILE_H264_BASELINE;
- break;
- default:
- ret = -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- p->codec.h264.level_v4l2 = ctrl->val;
- p->codec.h264.level = h264_level(ctrl->val);
- if (p->codec.h264.level < 0) {
- mfc_err("Level number is wrong\n");
- ret = p->codec.h264.level;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
- p->codec.mpeg4.level_v4l2 = ctrl->val;
- p->codec.mpeg4.level = mpeg4_level(ctrl->val);
- if (p->codec.mpeg4.level < 0) {
- mfc_err("Level number is wrong\n");
- ret = p->codec.mpeg4.level;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
- p->codec.h264.loop_filter_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
- p->codec.h264.loop_filter_alpha = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
- p->codec.h264.loop_filter_beta = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
- p->codec.h264.entropy_mode = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P:
- p->codec.h264.num_ref_pic_4p = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
- p->codec.h264._8x8_transform = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
- p->codec.h264.rc_mb = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
- p->codec.h264.rc_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_MIN_QP:
- p->codec.h264.rc_min_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_MAX_QP:
- p->codec.h264.rc_max_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
- p->codec.h264.rc_p_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
- p->codec.h264.rc_b_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP:
- case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP:
- p->codec.mpeg4.rc_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP:
- case V4L2_CID_MPEG_VIDEO_H263_MIN_QP:
- p->codec.mpeg4.rc_min_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP:
- case V4L2_CID_MPEG_VIDEO_H263_MAX_QP:
- p->codec.mpeg4.rc_max_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP:
- case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP:
- p->codec.mpeg4.rc_p_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP:
- case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP:
- p->codec.mpeg4.rc_b_frame_qp = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK:
- p->codec.h264.rc_mb_dark = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH:
- p->codec.h264.rc_mb_smooth = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC:
- p->codec.h264.rc_mb_static = ctrl->val;
- break;
- case V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY:
- p->codec.h264.rc_mb_activity = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
- p->codec.h264.vui_sar = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
- p->codec.h264.vui_sar_idc = vui_sar_idc(ctrl->val);
- break;
- case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
- p->codec.h264.vui_ext_sar_width = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
- p->codec.h264.vui_ext_sar_height = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- p->codec.h264.open_gop = !ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD:
- p->codec.h264.open_gop_size = ctrl->val;
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- switch (ctrl->val) {
- case V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE:
- p->codec.mpeg4.profile =
- S5P_FIMV_ENC_PROFILE_MPEG4_SIMPLE;
- break;
- case V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE:
- p->codec.mpeg4.profile =
- S5P_FIMV_ENC_PROFILE_MPEG4_ADVANCED_SIMPLE;
- break;
- default:
- ret = -EINVAL;
- }
- break;
- case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
- p->codec.mpeg4.quarter_pixel = ctrl->val;
- break;
- default:
- v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
- ctrl->id, ctrl->val);
- ret = -EINVAL;
- }
- return ret;
-}
-
-static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
- .s_ctrl = s5p_mfc_enc_s_ctrl,
-};
-
-static int vidioc_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *a)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ctx->enc_params.rc_framerate_num =
- a->parm.output.timeperframe.denominator;
- ctx->enc_params.rc_framerate_denom =
- a->parm.output.timeperframe.numerator;
- } else {
- mfc_err("Setting FPS is only possible for the output queue\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *a)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
-
- if (a->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- a->parm.output.timeperframe.denominator =
- ctx->enc_params.rc_framerate_num;
- a->parm.output.timeperframe.numerator =
- ctx->enc_params.rc_framerate_denom;
- } else {
- mfc_err("Setting FPS is only possible for the output queue\n");
- return -EINVAL;
- }
- return 0;
-}
-
-static const struct v4l2_ioctl_ops s5p_mfc_enc_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap_mplane = vidioc_enum_fmt_vid_cap_mplane,
- .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
- .vidioc_enum_fmt_vid_out_mplane = vidioc_enum_fmt_vid_out_mplane,
- .vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt,
- .vidioc_g_fmt_vid_out_mplane = vidioc_g_fmt,
- .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt,
- .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt,
- .vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt,
- .vidioc_s_fmt_vid_out_mplane = vidioc_s_fmt,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_s_parm = vidioc_s_parm,
- .vidioc_g_parm = vidioc_g_parm,
-};
-
-static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb)
-{
- int i;
-
- if (!fmt)
- return -EINVAL;
- if (fmt->num_planes != vb->num_planes) {
- mfc_err("invalid plane number for the format\n");
- return -EINVAL;
- }
- for (i = 0; i < fmt->num_planes; i++) {
- if (!vb2_dma_contig_plane_dma_addr(vb, i)) {
- mfc_err("failed to get plane cookie\n");
- return -EINVAL;
- }
- mfc_debug(2, "index: %d, plane[%d] cookie: 0x%08zx",
- vb->v4l2_buf.index, i,
- vb2_dma_contig_plane_dma_addr(vb, i));
- }
- return 0;
-}
-
-static int s5p_mfc_queue_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *buf_count, unsigned int *plane_count,
- unsigned int psize[], void *allocators[])
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
-
- if (ctx->state != MFCINST_GOT_INST) {
- mfc_err("inavlid state: %d\n", ctx->state);
- return -EINVAL;
- }
- if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- if (ctx->dst_fmt)
- *plane_count = ctx->dst_fmt->num_planes;
- else
- *plane_count = MFC_ENC_CAP_PLANE_COUNT;
- if (*buf_count < 1)
- *buf_count = 1;
- if (*buf_count > MFC_MAX_BUFFERS)
- *buf_count = MFC_MAX_BUFFERS;
- psize[0] = ctx->enc_dst_buf_size;
- allocators[0] = ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- if (ctx->src_fmt)
- *plane_count = ctx->src_fmt->num_planes;
- else
- *plane_count = MFC_ENC_OUT_PLANE_COUNT;
-
- if (*buf_count < 1)
- *buf_count = 1;
- if (*buf_count > MFC_MAX_BUFFERS)
- *buf_count = MFC_MAX_BUFFERS;
- psize[0] = ctx->luma_size;
- psize[1] = ctx->chroma_size;
- allocators[0] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
- allocators[1] = ctx->dev->alloc_ctx[MFC_BANK2_ALLOC_CTX];
- } else {
- mfc_err("inavlid queue type: %d\n", vq->type);
- return -EINVAL;
- }
- return 0;
-}
-
-static void s5p_mfc_unlock(struct vb2_queue *q)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mutex_unlock(&dev->mfc_mutex);
-}
-
-static void s5p_mfc_lock(struct vb2_queue *q)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mutex_lock(&dev->mfc_mutex);
-}
-
-static int s5p_mfc_buf_init(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
- unsigned int i;
- int ret;
-
- if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- ret = check_vb_with_fmt(ctx->dst_fmt, vb);
- if (ret < 0)
- return ret;
- i = vb->v4l2_buf.index;
- ctx->dst_bufs[i].b = vb;
- ctx->dst_bufs[i].cookie.stream =
- vb2_dma_contig_plane_dma_addr(vb, 0);
- ctx->dst_bufs_cnt++;
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = check_vb_with_fmt(ctx->src_fmt, vb);
- if (ret < 0)
- return ret;
- i = vb->v4l2_buf.index;
- ctx->src_bufs[i].b = vb;
- ctx->src_bufs[i].cookie.raw.luma =
- vb2_dma_contig_plane_dma_addr(vb, 0);
- ctx->src_bufs[i].cookie.raw.chroma =
- vb2_dma_contig_plane_dma_addr(vb, 1);
- ctx->src_bufs_cnt++;
- } else {
- mfc_err("inavlid queue type: %d\n", vq->type);
- return -EINVAL;
- }
- return 0;
-}
-
-static int s5p_mfc_buf_prepare(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
- int ret;
-
- if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- ret = check_vb_with_fmt(ctx->dst_fmt, vb);
- if (ret < 0)
- return ret;
- mfc_debug(2, "plane size: %ld, dst size: %d\n",
- vb2_plane_size(vb, 0), ctx->enc_dst_buf_size);
- if (vb2_plane_size(vb, 0) < ctx->enc_dst_buf_size) {
- mfc_err("plane size is too small for capture\n");
- return -EINVAL;
- }
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = check_vb_with_fmt(ctx->src_fmt, vb);
- if (ret < 0)
- return ret;
- mfc_debug(2, "plane size: %ld, luma size: %d\n",
- vb2_plane_size(vb, 0), ctx->luma_size);
- mfc_debug(2, "plane size: %ld, chroma size: %d\n",
- vb2_plane_size(vb, 1), ctx->chroma_size);
- if (vb2_plane_size(vb, 0) < ctx->luma_size ||
- vb2_plane_size(vb, 1) < ctx->chroma_size) {
- mfc_err("plane size is too small for output\n");
- return -EINVAL;
- }
- } else {
- mfc_err("inavlid queue type: %d\n", vq->type);
- return -EINVAL;
- }
- return 0;
-}
-
-static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
-
- v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
- /* If context is ready then dev = work->data;schedule it to run */
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
- return 0;
-}
-
-static int s5p_mfc_stop_streaming(struct vb2_queue *q)
-{
- unsigned long flags;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
-
- if ((ctx->state == MFCINST_FINISHING ||
- ctx->state == MFCINST_RUNNING) &&
- dev->curr_ctx == ctx->num && dev->hw_lock) {
- ctx->state = MFCINST_ABORT;
- s5p_mfc_wait_for_done_ctx(ctx, S5P_FIMV_R2H_CMD_FRAME_DONE_RET,
- 0);
- }
- ctx->state = MFCINST_FINISHED;
- spin_lock_irqsave(&dev->irqlock, flags);
- if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
- INIT_LIST_HEAD(&ctx->dst_queue);
- ctx->dst_queue_cnt = 0;
- }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- cleanup_ref_queue(ctx);
- s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
- INIT_LIST_HEAD(&ctx->src_queue);
- ctx->src_queue_cnt = 0;
- }
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return 0;
-}
-
-static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
-{
- struct vb2_queue *vq = vb->vb2_queue;
- struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *mfc_buf;
-
- if (ctx->state == MFCINST_ERROR) {
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
- cleanup_ref_queue(ctx);
- return;
- }
- if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- mfc_buf = &ctx->dst_bufs[vb->v4l2_buf.index];
- mfc_buf->used = 0;
- /* Mark destination as available for use by MFC */
- spin_lock_irqsave(&dev->irqlock, flags);
- list_add_tail(&mfc_buf->list, &ctx->dst_queue);
- ctx->dst_queue_cnt++;
- spin_unlock_irqrestore(&dev->irqlock, flags);
- } else if (vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- mfc_buf = &ctx->src_bufs[vb->v4l2_buf.index];
- mfc_buf->used = 0;
- spin_lock_irqsave(&dev->irqlock, flags);
- if (vb->v4l2_planes[0].bytesused == 0) {
- mfc_debug(1, "change state to FINISHING\n");
- ctx->state = MFCINST_FINISHING;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- cleanup_ref_queue(ctx);
- } else {
- list_add_tail(&mfc_buf->list, &ctx->src_queue);
- ctx->src_queue_cnt++;
- }
- spin_unlock_irqrestore(&dev->irqlock, flags);
- } else {
- mfc_err("unsupported buffer type (%d)\n", vq->type);
- }
- if (s5p_mfc_ctx_ready(ctx)) {
- spin_lock_irqsave(&dev->condlock, flags);
- set_bit(ctx->num, &dev->ctx_work_bits);
- spin_unlock_irqrestore(&dev->condlock, flags);
- }
- s5p_mfc_try_run(dev);
-}
-
-static struct vb2_ops s5p_mfc_enc_qops = {
- .queue_setup = s5p_mfc_queue_setup,
- .wait_prepare = s5p_mfc_unlock,
- .wait_finish = s5p_mfc_lock,
- .buf_init = s5p_mfc_buf_init,
- .buf_prepare = s5p_mfc_buf_prepare,
- .start_streaming = s5p_mfc_start_streaming,
- .stop_streaming = s5p_mfc_stop_streaming,
- .buf_queue = s5p_mfc_buf_queue,
-};
-
-struct s5p_mfc_codec_ops *get_enc_codec_ops(void)
-{
- return &encoder_codec_ops;
-}
-
-struct vb2_ops *get_enc_queue_ops(void)
-{
- return &s5p_mfc_enc_qops;
-}
-
-const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void)
-{
- return &s5p_mfc_enc_ioctl_ops;
-}
-
-#define IS_MFC51_PRIV(x) ((V4L2_CTRL_ID2CLASS(x) == V4L2_CTRL_CLASS_MPEG) \
- && V4L2_CTRL_DRIVER_PRIV(x))
-
-int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
-{
- struct v4l2_ctrl_config cfg;
- int i;
-
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, NUM_CTRLS);
- if (ctx->ctrl_handler.error) {
- mfc_err("v4l2_ctrl_handler_init failed\n");
- return ctx->ctrl_handler.error;
- }
- for (i = 0; i < NUM_CTRLS; i++) {
- if (IS_MFC51_PRIV(controls[i].id)) {
- memset(&cfg, 0, sizeof(struct v4l2_ctrl_config));
- cfg.ops = &s5p_mfc_enc_ctrl_ops;
- cfg.id = controls[i].id;
- cfg.min = controls[i].minimum;
- cfg.max = controls[i].maximum;
- cfg.def = controls[i].default_value;
- cfg.name = controls[i].name;
- cfg.type = controls[i].type;
- cfg.flags = 0;
-
- if (cfg.type == V4L2_CTRL_TYPE_MENU) {
- cfg.step = 0;
- cfg.menu_skip_mask = cfg.menu_skip_mask;
- cfg.qmenu = mfc51_get_menu(cfg.id);
- } else {
- cfg.step = controls[i].step;
- cfg.menu_skip_mask = 0;
- }
- ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
- &cfg, NULL);
- } else {
- if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
- ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
- &ctx->ctrl_handler,
- &s5p_mfc_enc_ctrl_ops, controls[i].id,
- controls[i].maximum, 0,
- controls[i].default_value);
- } else {
- ctx->ctrls[i] = v4l2_ctrl_new_std(
- &ctx->ctrl_handler,
- &s5p_mfc_enc_ctrl_ops, controls[i].id,
- controls[i].minimum,
- controls[i].maximum, controls[i].step,
- controls[i].default_value);
- }
- }
- if (ctx->ctrl_handler.error) {
- mfc_err("Adding control (%d) failed\n", i);
- return ctx->ctrl_handler.error;
- }
- if (controls[i].is_volatile && ctx->ctrls[i])
- ctx->ctrls[i]->flags |= V4L2_CTRL_FLAG_VOLATILE;
- }
- return 0;
-}
-
-void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx)
-{
- int i;
-
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- for (i = 0; i < NUM_CTRLS; i++)
- ctx->ctrls[i] = NULL;
-}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.h b/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
deleted file mode 100644
index 405bdd3ee083..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_enc.h
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_ENC_H_
-#define S5P_MFC_ENC_H_
-
-struct s5p_mfc_codec_ops *get_enc_codec_ops(void);
-struct vb2_ops *get_enc_queue_ops(void);
-const struct v4l2_ioctl_ops *get_enc_v4l2_ioctl_ops(void);
-struct s5p_mfc_fmt *get_enc_def_fmt(bool src);
-int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_enc_ctrls_delete(struct s5p_mfc_ctx *ctx);
-
-#endif /* S5P_MFC_ENC_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c b/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
deleted file mode 100644
index 8f2f8bf4da7f..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_intr.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * drivers/media/video/samsung/mfc5/s5p_mfc_intr.c
- *
- * C file for Samsung MFC (Multi Function Codec - FIMV) driver
- * This file contains functions used to wait for command completion.
- *
- * Kamil Debski, Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.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.
- */
-
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include "regs-mfc.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_intr.h"
-
-int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command)
-{
- int ret;
-
- ret = wait_event_interruptible_timeout(dev->queue,
- (dev->int_cond && (dev->int_type == command
- || dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
- msecs_to_jiffies(MFC_INT_TIMEOUT));
- if (ret == 0) {
- mfc_err("Interrupt (dev->int_type:%d, command:%d) timed out\n",
- dev->int_type, command);
- return 1;
- } else if (ret == -ERESTARTSYS) {
- mfc_err("Interrupted by a signal\n");
- return 1;
- }
- mfc_debug(1, "Finished waiting (dev->int_type:%d, command: %d)\n",
- dev->int_type, command);
- if (dev->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
- return 1;
- return 0;
-}
-
-void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev)
-{
- dev->int_cond = 0;
- dev->int_type = 0;
- dev->int_err = 0;
-}
-
-int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
- int command, int interrupt)
-{
- int ret;
-
- if (interrupt) {
- ret = wait_event_interruptible_timeout(ctx->queue,
- (ctx->int_cond && (ctx->int_type == command
- || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
- msecs_to_jiffies(MFC_INT_TIMEOUT));
- } else {
- ret = wait_event_timeout(ctx->queue,
- (ctx->int_cond && (ctx->int_type == command
- || ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)),
- msecs_to_jiffies(MFC_INT_TIMEOUT));
- }
- if (ret == 0) {
- mfc_err("Interrupt (ctx->int_type:%d, command:%d) timed out\n",
- ctx->int_type, command);
- return 1;
- } else if (ret == -ERESTARTSYS) {
- mfc_err("Interrupted by a signal\n");
- return 1;
- }
- mfc_debug(1, "Finished waiting (ctx->int_type:%d, command: %d)\n",
- ctx->int_type, command);
- if (ctx->int_type == S5P_FIMV_R2H_CMD_ERR_RET)
- return 1;
- return 0;
-}
-
-void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx)
-{
- ctx->int_cond = 0;
- ctx->int_type = 0;
- ctx->int_err = 0;
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h b/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
deleted file mode 100644
index 122d7732f745..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_intr.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * drivers/media/video/samsung/mfc5/s5p_mfc_intr.h
- *
- * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
- * It contains waiting functions declarations.
- *
- * Kamil Debski, Copyright (C) 2011 Samsung Electronics
- * http://www.samsung.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.
- */
-
-#ifndef S5P_MFC_INTR_H_
-#define S5P_MFC_INTR_H_
-
-#include "s5p_mfc_common.h"
-
-int s5p_mfc_wait_for_done_ctx(struct s5p_mfc_ctx *ctx,
- int command, int interrupt);
-int s5p_mfc_wait_for_done_dev(struct s5p_mfc_dev *dev, int command);
-void s5p_mfc_clean_ctx_int_flags(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_clean_dev_int_flags(struct s5p_mfc_dev *dev);
-
-#endif /* S5P_MFC_INTR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
deleted file mode 100644
index e6217cbfa4a3..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
+++ /dev/null
@@ -1,1397 +0,0 @@
-/*
- * drivers/media/video/samsung/mfc5/s5p_mfc_opr.c
- *
- * Samsung MFC (Multi Function Codec - FIMV) driver
- * This file contains hw related functions.
- *
- * Kamil Debski, Copyright (c) 2011 Samsung Electronics
- * http://www.samsung.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.
- */
-
-#include "regs-mfc.h"
-#include "s5p_mfc_cmd.h"
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_ctrl.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_intr.h"
-#include "s5p_mfc_opr.h"
-#include "s5p_mfc_pm.h"
-#include "s5p_mfc_shm.h"
-#include <asm/cacheflush.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/firmware.h>
-#include <linux/io.h>
-#include <linux/jiffies.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-
-#define OFFSETA(x) (((x) - dev->bank1) >> MFC_OFFSET_SHIFT)
-#define OFFSETB(x) (((x) - dev->bank2) >> MFC_OFFSET_SHIFT)
-
-/* Allocate temporary buffers for decoding */
-int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
-{
- void *desc_virt;
- struct s5p_mfc_dev *dev = ctx->dev;
-
- ctx->desc_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
- if (IS_ERR_VALUE((int)ctx->desc_buf)) {
- ctx->desc_buf = NULL;
- mfc_err("Allocating DESC buffer failed\n");
- return -ENOMEM;
- }
- ctx->desc_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->desc_buf);
- BUG_ON(ctx->desc_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- desc_virt = vb2_dma_contig_memops.vaddr(ctx->desc_buf);
- if (desc_virt == NULL) {
- vb2_dma_contig_memops.put(ctx->desc_buf);
- ctx->desc_phys = 0;
- ctx->desc_buf = NULL;
- mfc_err("Remapping DESC buffer failed\n");
- return -ENOMEM;
- }
- memset(desc_virt, 0, DESC_BUF_SIZE);
- wmb();
- return 0;
-}
-
-/* Release temporary buffers for decoding */
-void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
-{
- if (ctx->desc_phys) {
- vb2_dma_contig_memops.put(ctx->desc_buf);
- ctx->desc_phys = 0;
- ctx->desc_buf = NULL;
- }
-}
-
-/* Allocate codec buffers */
-int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned int enc_ref_y_size = 0;
- unsigned int enc_ref_c_size = 0;
- unsigned int guard_width, guard_height;
-
- if (ctx->type == MFCINST_DECODER) {
- mfc_debug(2, "Luma size:%d Chroma size:%d MV size:%d\n",
- ctx->luma_size, ctx->chroma_size, ctx->mv_size);
- mfc_debug(2, "Totals bufs: %d\n", ctx->total_dpb_count);
- } else if (ctx->type == MFCINST_ENCODER) {
- enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
- * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
- enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
-
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
- enc_ref_c_size = ALIGN(ctx->img_width,
- S5P_FIMV_NV12MT_HALIGN)
- * ALIGN(ctx->img_height >> 1,
- S5P_FIMV_NV12MT_VALIGN);
- enc_ref_c_size = ALIGN(enc_ref_c_size,
- S5P_FIMV_NV12MT_SALIGN);
- } else {
- guard_width = ALIGN(ctx->img_width + 16,
- S5P_FIMV_NV12MT_HALIGN);
- guard_height = ALIGN((ctx->img_height >> 1) + 4,
- S5P_FIMV_NV12MT_VALIGN);
- enc_ref_c_size = ALIGN(guard_width * guard_height,
- S5P_FIMV_NV12MT_SALIGN);
- }
- mfc_debug(2, "recon luma size: %d chroma size: %d\n",
- enc_ref_y_size, enc_ref_c_size);
- } else {
- return -EINVAL;
- }
- /* Codecs have different memory requirements */
- switch (ctx->codec_mode) {
- case S5P_FIMV_CODEC_H264_DEC:
- ctx->bank1_size =
- ALIGN(S5P_FIMV_DEC_NB_IP_SIZE +
- S5P_FIMV_DEC_VERT_NB_MV_SIZE,
- S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = ctx->total_dpb_count * ctx->mv_size;
- break;
- case S5P_FIMV_CODEC_MPEG4_DEC:
- ctx->bank1_size =
- ALIGN(S5P_FIMV_DEC_NB_DCAC_SIZE +
- S5P_FIMV_DEC_UPNB_MV_SIZE +
- S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
- S5P_FIMV_DEC_STX_PARSER_SIZE +
- S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE,
- S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
- break;
- case S5P_FIMV_CODEC_VC1RCV_DEC:
- case S5P_FIMV_CODEC_VC1_DEC:
- ctx->bank1_size =
- ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
- S5P_FIMV_DEC_UPNB_MV_SIZE +
- S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
- S5P_FIMV_DEC_NB_DCAC_SIZE +
- 3 * S5P_FIMV_DEC_VC1_BITPLANE_SIZE,
- S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
- break;
- case S5P_FIMV_CODEC_MPEG2_DEC:
- ctx->bank1_size = 0;
- ctx->bank2_size = 0;
- break;
- case S5P_FIMV_CODEC_H263_DEC:
- ctx->bank1_size =
- ALIGN(S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE +
- S5P_FIMV_DEC_UPNB_MV_SIZE +
- S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE +
- S5P_FIMV_DEC_NB_DCAC_SIZE,
- S5P_FIMV_DEC_BUF_ALIGN);
- ctx->bank2_size = 0;
- break;
- case S5P_FIMV_CODEC_H264_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
- S5P_FIMV_ENC_UPMV_SIZE +
- S5P_FIMV_ENC_COLFLG_SIZE +
- S5P_FIMV_ENC_INTRAMD_SIZE +
- S5P_FIMV_ENC_NBORINFO_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
- (enc_ref_c_size * 4) +
- S5P_FIMV_ENC_INTRAPRED_SIZE;
- break;
- case S5P_FIMV_CODEC_MPEG4_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
- S5P_FIMV_ENC_UPMV_SIZE +
- S5P_FIMV_ENC_COLFLG_SIZE +
- S5P_FIMV_ENC_ACDCCOEF_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
- (enc_ref_c_size * 4);
- break;
- case S5P_FIMV_CODEC_H263_ENC:
- ctx->bank1_size = (enc_ref_y_size * 2) +
- S5P_FIMV_ENC_UPMV_SIZE +
- S5P_FIMV_ENC_ACDCCOEF_SIZE;
- ctx->bank2_size = (enc_ref_y_size * 2) +
- (enc_ref_c_size * 4);
- break;
- default:
- break;
- }
- /* Allocate only if memory from bank 1 is necessary */
- if (ctx->bank1_size > 0) {
- ctx->bank1_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
- if (IS_ERR(ctx->bank1_buf)) {
- ctx->bank1_buf = NULL;
- printk(KERN_ERR
- "Buf alloc for decoding failed (port A)\n");
- return -ENOMEM;
- }
- ctx->bank1_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_buf);
- BUG_ON(ctx->bank1_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- }
- /* Allocate only if memory from bank 2 is necessary */
- if (ctx->bank2_size > 0) {
- ctx->bank2_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
- if (IS_ERR(ctx->bank2_buf)) {
- ctx->bank2_buf = NULL;
- mfc_err("Buf alloc for decoding failed (port B)\n");
- return -ENOMEM;
- }
- ctx->bank2_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_buf);
- BUG_ON(ctx->bank2_phys & ((1 << MFC_BANK2_ALIGN_ORDER) - 1));
- }
- return 0;
-}
-
-/* Release buffers allocated for codec */
-void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
-{
- if (ctx->bank1_buf) {
- vb2_dma_contig_memops.put(ctx->bank1_buf);
- ctx->bank1_buf = NULL;
- ctx->bank1_phys = 0;
- ctx->bank1_size = 0;
- }
- if (ctx->bank2_buf) {
- vb2_dma_contig_memops.put(ctx->bank2_buf);
- ctx->bank2_buf = NULL;
- ctx->bank2_phys = 0;
- ctx->bank2_size = 0;
- }
-}
-
-/* Allocate memory for instance data buffer */
-int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
-{
- void *context_virt;
- struct s5p_mfc_dev *dev = ctx->dev;
-
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC ||
- ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
- ctx->ctx_size = MFC_H264_CTX_BUF_SIZE;
- else
- ctx->ctx_size = MFC_CTX_BUF_SIZE;
- ctx->ctx_buf = vb2_dma_contig_memops.alloc(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_size);
- if (IS_ERR(ctx->ctx_buf)) {
- mfc_err("Allocating context buffer failed\n");
- ctx->ctx_phys = 0;
- ctx->ctx_buf = NULL;
- return -ENOMEM;
- }
- ctx->ctx_phys = s5p_mfc_mem_cookie(
- dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->ctx_buf);
- BUG_ON(ctx->ctx_phys & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- ctx->ctx_ofs = OFFSETA(ctx->ctx_phys);
- context_virt = vb2_dma_contig_memops.vaddr(ctx->ctx_buf);
- if (context_virt == NULL) {
- mfc_err("Remapping instance buffer failed\n");
- vb2_dma_contig_memops.put(ctx->ctx_buf);
- ctx->ctx_phys = 0;
- ctx->ctx_buf = NULL;
- return -ENOMEM;
- }
- /* Zero content of the allocated memory */
- memset(context_virt, 0, ctx->ctx_size);
- wmb();
- if (s5p_mfc_init_shm(ctx) < 0) {
- vb2_dma_contig_memops.put(ctx->ctx_buf);
- ctx->ctx_phys = 0;
- ctx->ctx_buf = NULL;
- return -ENOMEM;
- }
- return 0;
-}
-
-/* Release instance buffer */
-void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
-{
- if (ctx->ctx_buf) {
- vb2_dma_contig_memops.put(ctx->ctx_buf);
- ctx->ctx_phys = 0;
- ctx->ctx_buf = NULL;
- }
- if (ctx->shm_alloc) {
- vb2_dma_contig_memops.put(ctx->shm_alloc);
- ctx->shm_alloc = NULL;
- ctx->shm = NULL;
- }
-}
-
-/* Set registers for decoding temporary buffers */
-void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mfc_write(dev, OFFSETA(ctx->desc_phys), S5P_FIMV_SI_CH0_DESC_ADR);
- mfc_write(dev, DESC_BUF_SIZE, S5P_FIMV_SI_CH0_DESC_SIZE);
-}
-
-/* Set registers for shared buffer */
-static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
-}
-
-/* Set registers for decoding stream buffer */
-int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
- unsigned int start_num_byte, unsigned int buf_size)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mfc_write(dev, OFFSETA(buf_addr), S5P_FIMV_SI_CH0_SB_ST_ADR);
- mfc_write(dev, ctx->dec_src_buf_size, S5P_FIMV_SI_CH0_CPB_SIZE);
- mfc_write(dev, buf_size, S5P_FIMV_SI_CH0_SB_FRM_SIZE);
- s5p_mfc_write_shm(ctx, start_num_byte, START_BYTE_NUM);
- return 0;
-}
-
-/* Set decoding frame buffer */
-int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx)
-{
- unsigned int frame_size, i;
- unsigned int frame_size_ch, frame_size_mv;
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned int dpb;
- size_t buf_addr1, buf_addr2;
- int buf_size1, buf_size2;
-
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
- buf_addr2 = ctx->bank2_phys;
- buf_size2 = ctx->bank2_size;
- dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
- ~S5P_FIMV_DPB_COUNT_MASK;
- mfc_write(dev, ctx->total_dpb_count | dpb,
- S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
- s5p_mfc_set_shared_buffer(ctx);
- switch (ctx->codec_mode) {
- case S5P_FIMV_CODEC_H264_DEC:
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_H264_VERT_NB_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_VERT_NB_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_VERT_NB_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_NB_IP_ADR);
- buf_addr1 += S5P_FIMV_DEC_NB_IP_SIZE;
- buf_size1 -= S5P_FIMV_DEC_NB_IP_SIZE;
- break;
- case S5P_FIMV_CODEC_MPEG4_DEC:
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_NB_DCAC_ADR);
- buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
- buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_NB_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SA_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_SP_ADR);
- buf_addr1 += S5P_FIMV_DEC_STX_PARSER_SIZE;
- buf_size1 -= S5P_FIMV_DEC_STX_PARSER_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_OT_LINE_ADR);
- buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- break;
- case S5P_FIMV_CODEC_H263_DEC:
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_OT_LINE_ADR);
- buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_NB_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_SA_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_NB_DCAC_ADR);
- buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
- buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
- break;
- case S5P_FIMV_CODEC_VC1_DEC:
- case S5P_FIMV_CODEC_VC1RCV_DEC:
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_NB_DCAC_ADR);
- buf_addr1 += S5P_FIMV_DEC_NB_DCAC_SIZE;
- buf_size1 -= S5P_FIMV_DEC_NB_DCAC_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_OT_LINE_ADR);
- buf_addr1 += S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- buf_size1 -= S5P_FIMV_DEC_OVERLAP_TRANSFORM_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_UP_NB_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_UPNB_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_UPNB_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_SA_MV_ADR);
- buf_addr1 += S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- buf_size1 -= S5P_FIMV_DEC_SUB_ANCHOR_MV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE3_ADR);
- buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE2_ADR);
- buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_VC1_BITPLANE1_ADR);
- buf_addr1 += S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- buf_size1 -= S5P_FIMV_DEC_VC1_BITPLANE_SIZE;
- break;
- case S5P_FIMV_CODEC_MPEG2_DEC:
- break;
- default:
- mfc_err("Unknown codec for decoding (%x)\n",
- ctx->codec_mode);
- return -EINVAL;
- break;
- }
- frame_size = ctx->luma_size;
- frame_size_ch = ctx->chroma_size;
- frame_size_mv = ctx->mv_size;
- mfc_debug(2, "Frm size: %d ch: %d mv: %d\n", frame_size, frame_size_ch,
- frame_size_mv);
- for (i = 0; i < ctx->total_dpb_count; i++) {
- /* Bank2 */
- mfc_debug(2, "Luma %d: %x\n", i,
- ctx->dst_bufs[i].cookie.raw.luma);
- mfc_write(dev, OFFSETB(ctx->dst_bufs[i].cookie.raw.luma),
- S5P_FIMV_DEC_LUMA_ADR + i * 4);
- mfc_debug(2, "\tChroma %d: %x\n", i,
- ctx->dst_bufs[i].cookie.raw.chroma);
- mfc_write(dev, OFFSETA(ctx->dst_bufs[i].cookie.raw.chroma),
- S5P_FIMV_DEC_CHROMA_ADR + i * 4);
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC) {
- mfc_debug(2, "\tBuf2: %x, size: %d\n",
- buf_addr2, buf_size2);
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_H264_MV_ADR + i * 4);
- buf_addr2 += frame_size_mv;
- buf_size2 -= frame_size_mv;
- }
- }
- mfc_debug(2, "Buf1: %u, buf_size1: %d\n", buf_addr1, buf_size1);
- mfc_debug(2, "Buf 1/2 size after: %d/%d (frames %d)\n",
- buf_size1, buf_size2, ctx->total_dpb_count);
- if (buf_size1 < 0 || buf_size2 < 0) {
- mfc_debug(2, "Not enough memory has been allocated\n");
- return -ENOMEM;
- }
- s5p_mfc_write_shm(ctx, frame_size, ALLOC_LUMA_DPB_SIZE);
- s5p_mfc_write_shm(ctx, frame_size_ch, ALLOC_CHROMA_DPB_SIZE);
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_DEC)
- s5p_mfc_write_shm(ctx, frame_size_mv, ALLOC_MV_SIZE);
- mfc_write(dev, ((S5P_FIMV_CH_INIT_BUFS & S5P_FIMV_CH_MASK)
- << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
- S5P_FIMV_SI_CH0_INST_ID);
- return 0;
-}
-
-/* Set registers for encoding stream buffer */
-int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long addr, unsigned int size)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mfc_write(dev, OFFSETA(addr), S5P_FIMV_ENC_SI_CH0_SB_ADR);
- mfc_write(dev, size, S5P_FIMV_ENC_SI_CH0_SB_SIZE);
- return 0;
-}
-
-void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long y_addr, unsigned long c_addr)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mfc_write(dev, OFFSETB(y_addr), S5P_FIMV_ENC_SI_CH0_CUR_Y_ADR);
- mfc_write(dev, OFFSETB(c_addr), S5P_FIMV_ENC_SI_CH0_CUR_C_ADR);
-}
-
-void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long *y_addr, unsigned long *c_addr)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- *y_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_Y_ADDR)
- << MFC_OFFSET_SHIFT);
- *c_addr = dev->bank2 + (mfc_read(dev, S5P_FIMV_ENCODED_C_ADDR)
- << MFC_OFFSET_SHIFT);
-}
-
-/* Set encoding ref & codec buffer */
-int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- size_t buf_addr1, buf_addr2;
- size_t buf_size1, buf_size2;
- unsigned int enc_ref_y_size, enc_ref_c_size;
- unsigned int guard_width, guard_height;
- int i;
-
- buf_addr1 = ctx->bank1_phys;
- buf_size1 = ctx->bank1_size;
- buf_addr2 = ctx->bank2_phys;
- buf_size2 = ctx->bank2_size;
- enc_ref_y_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
- * ALIGN(ctx->img_height, S5P_FIMV_NV12MT_VALIGN);
- enc_ref_y_size = ALIGN(enc_ref_y_size, S5P_FIMV_NV12MT_SALIGN);
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC) {
- enc_ref_c_size = ALIGN(ctx->img_width, S5P_FIMV_NV12MT_HALIGN)
- * ALIGN((ctx->img_height >> 1), S5P_FIMV_NV12MT_VALIGN);
- enc_ref_c_size = ALIGN(enc_ref_c_size, S5P_FIMV_NV12MT_SALIGN);
- } else {
- guard_width = ALIGN(ctx->img_width + 16,
- S5P_FIMV_NV12MT_HALIGN);
- guard_height = ALIGN((ctx->img_height >> 1) + 4,
- S5P_FIMV_NV12MT_VALIGN);
- enc_ref_c_size = ALIGN(guard_width * guard_height,
- S5P_FIMV_NV12MT_SALIGN);
- }
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n", buf_size1, buf_size2);
- switch (ctx->codec_mode) {
- case S5P_FIMV_CODEC_H264_ENC:
- for (i = 0; i < 2; i++) {
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
- buf_addr1 += enc_ref_y_size;
- buf_size1 -= enc_ref_y_size;
-
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
- buf_addr2 += enc_ref_y_size;
- buf_size2 -= enc_ref_y_size;
- }
- for (i = 0; i < 4; i++) {
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
- buf_addr2 += enc_ref_c_size;
- buf_size2 -= enc_ref_c_size;
- }
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H264_UP_MV_ADR);
- buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
- buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_H264_COZERO_FLAG_ADR);
- buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
- buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_H264_UP_INTRA_MD_ADR);
- buf_addr1 += S5P_FIMV_ENC_INTRAMD_SIZE;
- buf_size1 -= S5P_FIMV_ENC_INTRAMD_SIZE;
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_H264_UP_INTRA_PRED_ADR);
- buf_addr2 += S5P_FIMV_ENC_INTRAPRED_SIZE;
- buf_size2 -= S5P_FIMV_ENC_INTRAPRED_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_H264_NBOR_INFO_ADR);
- buf_addr1 += S5P_FIMV_ENC_NBORINFO_SIZE;
- buf_size1 -= S5P_FIMV_ENC_NBORINFO_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
- buf_size1, buf_size2);
- break;
- case S5P_FIMV_CODEC_MPEG4_ENC:
- for (i = 0; i < 2; i++) {
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
- buf_addr1 += enc_ref_y_size;
- buf_size1 -= enc_ref_y_size;
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
- buf_addr2 += enc_ref_y_size;
- buf_size2 -= enc_ref_y_size;
- }
- for (i = 0; i < 4; i++) {
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
- buf_addr2 += enc_ref_c_size;
- buf_size2 -= enc_ref_c_size;
- }
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_MPEG4_UP_MV_ADR);
- buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
- buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_MPEG4_COZERO_FLAG_ADR);
- buf_addr1 += S5P_FIMV_ENC_COLFLG_SIZE;
- buf_size1 -= S5P_FIMV_ENC_COLFLG_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_MPEG4_ACDC_COEF_ADR);
- buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
- buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
- buf_size1, buf_size2);
- break;
- case S5P_FIMV_CODEC_H263_ENC:
- for (i = 0; i < 2; i++) {
- mfc_write(dev, OFFSETA(buf_addr1),
- S5P_FIMV_ENC_REF0_LUMA_ADR + (4 * i));
- buf_addr1 += enc_ref_y_size;
- buf_size1 -= enc_ref_y_size;
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF2_LUMA_ADR + (4 * i));
- buf_addr2 += enc_ref_y_size;
- buf_size2 -= enc_ref_y_size;
- }
- for (i = 0; i < 4; i++) {
- mfc_write(dev, OFFSETB(buf_addr2),
- S5P_FIMV_ENC_REF0_CHROMA_ADR + (4 * i));
- buf_addr2 += enc_ref_c_size;
- buf_size2 -= enc_ref_c_size;
- }
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_UP_MV_ADR);
- buf_addr1 += S5P_FIMV_ENC_UPMV_SIZE;
- buf_size1 -= S5P_FIMV_ENC_UPMV_SIZE;
- mfc_write(dev, OFFSETA(buf_addr1), S5P_FIMV_H263_ACDC_COEF_ADR);
- buf_addr1 += S5P_FIMV_ENC_ACDCCOEF_SIZE;
- buf_size1 -= S5P_FIMV_ENC_ACDCCOEF_SIZE;
- mfc_debug(2, "buf_size1: %d, buf_size2: %d\n",
- buf_size1, buf_size2);
- break;
- default:
- mfc_err("Unknown codec set for encoding: %d\n",
- ctx->codec_mode);
- return -EINVAL;
- }
- return 0;
-}
-
-static int s5p_mfc_set_enc_params(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- unsigned int reg;
- unsigned int shm;
-
- /* width */
- mfc_write(dev, ctx->img_width, S5P_FIMV_ENC_HSIZE_PX);
- /* height */
- mfc_write(dev, ctx->img_height, S5P_FIMV_ENC_VSIZE_PX);
- /* pictype : enable, IDR period */
- reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- reg |= (1 << 18);
- reg &= ~(0xFFFF);
- reg |= p->gop_size;
- mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- mfc_write(dev, 0, S5P_FIMV_ENC_B_RECON_WRITE_ON);
- /* multi-slice control */
- /* multi-slice MB number or bit size */
- mfc_write(dev, p->slice_mode, S5P_FIMV_ENC_MSLICE_CTRL);
- if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
- mfc_write(dev, p->slice_mb, S5P_FIMV_ENC_MSLICE_MB);
- } else if (p->slice_mode == V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES) {
- mfc_write(dev, p->slice_bit, S5P_FIMV_ENC_MSLICE_BIT);
- } else {
- mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_MB);
- mfc_write(dev, 0, S5P_FIMV_ENC_MSLICE_BIT);
- }
- /* cyclic intra refresh */
- mfc_write(dev, p->intra_refresh_mb, S5P_FIMV_ENC_CIR_CTRL);
- /* memory structure cur. frame */
- if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
- mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
- else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
- mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
- /* padding control & value */
- reg = mfc_read(dev, S5P_FIMV_ENC_PADDING_CTRL);
- if (p->pad) {
- /** enable */
- reg |= (1 << 31);
- /** cr value */
- reg &= ~(0xFF << 16);
- reg |= (p->pad_cr << 16);
- /** cb value */
- reg &= ~(0xFF << 8);
- reg |= (p->pad_cb << 8);
- /** y value */
- reg &= ~(0xFF);
- reg |= (p->pad_luma);
- } else {
- /** disable & all value clear */
- reg = 0;
- }
- mfc_write(dev, reg, S5P_FIMV_ENC_PADDING_CTRL);
- /* rate control config. */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
- /** frame-level rate control */
- reg &= ~(0x1 << 9);
- reg |= (p->rc_frame << 9);
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
- /* bit rate */
- if (p->rc_frame)
- mfc_write(dev, p->rc_bitrate,
- S5P_FIMV_ENC_RC_BIT_RATE);
- else
- mfc_write(dev, 0, S5P_FIMV_ENC_RC_BIT_RATE);
- /* reaction coefficient */
- if (p->rc_frame)
- mfc_write(dev, p->rc_reaction_coeff, S5P_FIMV_ENC_RC_RPARA);
- shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
- /* seq header ctrl */
- shm &= ~(0x1 << 3);
- shm |= (p->seq_hdr_mode << 3);
- /* frame skip mode */
- shm &= ~(0x3 << 1);
- shm |= (p->frame_skip_mode << 1);
- s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
- /* fixed target bit */
- s5p_mfc_write_shm(ctx, p->fixed_target_bit, RC_CONTROL_CONFIG);
- return 0;
-}
-
-static int s5p_mfc_set_enc_params_h264(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- struct s5p_mfc_h264_enc_params *p_264 = &p->codec.h264;
- unsigned int reg;
- unsigned int shm;
-
- s5p_mfc_set_enc_params(ctx);
- /* pictype : number of B */
- reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- /* num_b_frame - 0 ~ 2 */
- reg &= ~(0x3 << 16);
- reg |= (p->num_b_frame << 16);
- mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- /* profile & level */
- reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
- /* level */
- reg &= ~(0xFF << 8);
- reg |= (p_264->level << 8);
- /* profile - 0 ~ 2 */
- reg &= ~(0x3F);
- reg |= p_264->profile;
- mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
- /* interlace */
- mfc_write(dev, p->interlace, S5P_FIMV_ENC_PIC_STRUCT);
- /* height */
- if (p->interlace)
- mfc_write(dev, ctx->img_height >> 1, S5P_FIMV_ENC_VSIZE_PX);
- /* loopfilter ctrl */
- mfc_write(dev, p_264->loop_filter_mode, S5P_FIMV_ENC_LF_CTRL);
- /* loopfilter alpha offset */
- if (p_264->loop_filter_alpha < 0) {
- reg = 0x10;
- reg |= (0xFF - p_264->loop_filter_alpha) + 1;
- } else {
- reg = 0x00;
- reg |= (p_264->loop_filter_alpha & 0xF);
- }
- mfc_write(dev, reg, S5P_FIMV_ENC_ALPHA_OFF);
- /* loopfilter beta offset */
- if (p_264->loop_filter_beta < 0) {
- reg = 0x10;
- reg |= (0xFF - p_264->loop_filter_beta) + 1;
- } else {
- reg = 0x00;
- reg |= (p_264->loop_filter_beta & 0xF);
- }
- mfc_write(dev, reg, S5P_FIMV_ENC_BETA_OFF);
- /* entropy coding mode */
- if (p_264->entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
- mfc_write(dev, 1, S5P_FIMV_ENC_H264_ENTROPY_MODE);
- else
- mfc_write(dev, 0, S5P_FIMV_ENC_H264_ENTROPY_MODE);
- /* number of ref. picture */
- reg = mfc_read(dev, S5P_FIMV_ENC_H264_NUM_OF_REF);
- /* num of ref. pictures of P */
- reg &= ~(0x3 << 5);
- reg |= (p_264->num_ref_pic_4p << 5);
- /* max number of ref. pictures */
- reg &= ~(0x1F);
- reg |= p_264->max_ref_pic;
- mfc_write(dev, reg, S5P_FIMV_ENC_H264_NUM_OF_REF);
- /* 8x8 transform enable */
- mfc_write(dev, p_264->_8x8_transform, S5P_FIMV_ENC_H264_TRANS_FLAG);
- /* rate control config. */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
- /* macroblock level rate control */
- reg &= ~(0x1 << 8);
- reg |= (p_264->rc_mb << 8);
- /* frame QP */
- reg &= ~(0x3F);
- reg |= p_264->rc_frame_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
- /* frame rate */
- if (p->rc_frame && p->rc_framerate_denom)
- mfc_write(dev, p->rc_framerate_num * 1000
- / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
- else
- mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
- /* max & min value of QP */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
- /* max QP */
- reg &= ~(0x3F << 8);
- reg |= (p_264->rc_max_qp << 8);
- /* min QP */
- reg &= ~(0x3F);
- reg |= p_264->rc_min_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
- /* macroblock adaptive scaling features */
- if (p_264->rc_mb) {
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_MB_CTRL);
- /* dark region */
- reg &= ~(0x1 << 3);
- reg |= (p_264->rc_mb_dark << 3);
- /* smooth region */
- reg &= ~(0x1 << 2);
- reg |= (p_264->rc_mb_smooth << 2);
- /* static region */
- reg &= ~(0x1 << 1);
- reg |= (p_264->rc_mb_static << 1);
- /* high activity region */
- reg &= ~(0x1);
- reg |= p_264->rc_mb_activity;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_MB_CTRL);
- }
- if (!p->rc_frame &&
- !p_264->rc_mb) {
- shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
- shm &= ~(0xFFF);
- shm |= ((p_264->rc_b_frame_qp & 0x3F) << 6);
- shm |= (p_264->rc_p_frame_qp & 0x3F);
- s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
- }
- /* extended encoder ctrl */
- shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
- /* AR VUI control */
- shm &= ~(0x1 << 15);
- shm |= (p_264->vui_sar << 1);
- s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
- if (p_264->vui_sar) {
- /* aspect ration IDC */
- shm = s5p_mfc_read_shm(ctx, SAMPLE_ASPECT_RATIO_IDC);
- shm &= ~(0xFF);
- shm |= p_264->vui_sar_idc;
- s5p_mfc_write_shm(ctx, shm, SAMPLE_ASPECT_RATIO_IDC);
- if (p_264->vui_sar_idc == 0xFF) {
- /* sample AR info */
- shm = s5p_mfc_read_shm(ctx, EXTENDED_SAR);
- shm &= ~(0xFFFFFFFF);
- shm |= p_264->vui_ext_sar_width << 16;
- shm |= p_264->vui_ext_sar_height;
- s5p_mfc_write_shm(ctx, shm, EXTENDED_SAR);
- }
- }
- /* intra picture period for H.264 */
- shm = s5p_mfc_read_shm(ctx, H264_I_PERIOD);
- /* control */
- shm &= ~(0x1 << 16);
- shm |= (p_264->open_gop << 16);
- /* value */
- if (p_264->open_gop) {
- shm &= ~(0xFFFF);
- shm |= p_264->open_gop_size;
- }
- s5p_mfc_write_shm(ctx, shm, H264_I_PERIOD);
- /* extended encoder ctrl */
- shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
- /* vbv buffer size */
- if (p->frame_skip_mode ==
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- shm &= ~(0xFFFF << 16);
- shm |= (p_264->cpb_size << 16);
- }
- s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
- return 0;
-}
-
-static int s5p_mfc_set_enc_params_mpeg4(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- struct s5p_mfc_mpeg4_enc_params *p_mpeg4 = &p->codec.mpeg4;
- unsigned int reg;
- unsigned int shm;
- unsigned int framerate;
-
- s5p_mfc_set_enc_params(ctx);
- /* pictype : number of B */
- reg = mfc_read(dev, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- /* num_b_frame - 0 ~ 2 */
- reg &= ~(0x3 << 16);
- reg |= (p->num_b_frame << 16);
- mfc_write(dev, reg, S5P_FIMV_ENC_PIC_TYPE_CTRL);
- /* profile & level */
- reg = mfc_read(dev, S5P_FIMV_ENC_PROFILE);
- /* level */
- reg &= ~(0xFF << 8);
- reg |= (p_mpeg4->level << 8);
- /* profile - 0 ~ 2 */
- reg &= ~(0x3F);
- reg |= p_mpeg4->profile;
- mfc_write(dev, reg, S5P_FIMV_ENC_PROFILE);
- /* quarter_pixel */
- mfc_write(dev, p_mpeg4->quarter_pixel, S5P_FIMV_ENC_MPEG4_QUART_PXL);
- /* qp */
- if (!p->rc_frame) {
- shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
- shm &= ~(0xFFF);
- shm |= ((p_mpeg4->rc_b_frame_qp & 0x3F) << 6);
- shm |= (p_mpeg4->rc_p_frame_qp & 0x3F);
- s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
- }
- /* frame rate */
- if (p->rc_frame) {
- if (p->rc_framerate_denom > 0) {
- framerate = p->rc_framerate_num * 1000 /
- p->rc_framerate_denom;
- mfc_write(dev, framerate,
- S5P_FIMV_ENC_RC_FRAME_RATE);
- shm = s5p_mfc_read_shm(ctx, RC_VOP_TIMING);
- shm &= ~(0xFFFFFFFF);
- shm |= (1 << 31);
- shm |= ((p->rc_framerate_num & 0x7FFF) << 16);
- shm |= (p->rc_framerate_denom & 0xFFFF);
- s5p_mfc_write_shm(ctx, shm, RC_VOP_TIMING);
- }
- } else {
- mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
- }
- /* rate control config. */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
- /* frame QP */
- reg &= ~(0x3F);
- reg |= p_mpeg4->rc_frame_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
- /* max & min value of QP */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
- /* max QP */
- reg &= ~(0x3F << 8);
- reg |= (p_mpeg4->rc_max_qp << 8);
- /* min QP */
- reg &= ~(0x3F);
- reg |= p_mpeg4->rc_min_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
- /* extended encoder ctrl */
- shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
- /* vbv buffer size */
- if (p->frame_skip_mode ==
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- shm &= ~(0xFFFF << 16);
- shm |= (p->vbv_size << 16);
- }
- s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
- return 0;
-}
-
-static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_enc_params *p = &ctx->enc_params;
- struct s5p_mfc_mpeg4_enc_params *p_h263 = &p->codec.mpeg4;
- unsigned int reg;
- unsigned int shm;
-
- s5p_mfc_set_enc_params(ctx);
- /* qp */
- if (!p->rc_frame) {
- shm = s5p_mfc_read_shm(ctx, P_B_FRAME_QP);
- shm &= ~(0xFFF);
- shm |= (p_h263->rc_p_frame_qp & 0x3F);
- s5p_mfc_write_shm(ctx, shm, P_B_FRAME_QP);
- }
- /* frame rate */
- if (p->rc_frame && p->rc_framerate_denom)
- mfc_write(dev, p->rc_framerate_num * 1000
- / p->rc_framerate_denom, S5P_FIMV_ENC_RC_FRAME_RATE);
- else
- mfc_write(dev, 0, S5P_FIMV_ENC_RC_FRAME_RATE);
- /* rate control config. */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_CONFIG);
- /* frame QP */
- reg &= ~(0x3F);
- reg |= p_h263->rc_frame_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_CONFIG);
- /* max & min value of QP */
- reg = mfc_read(dev, S5P_FIMV_ENC_RC_QBOUND);
- /* max QP */
- reg &= ~(0x3F << 8);
- reg |= (p_h263->rc_max_qp << 8);
- /* min QP */
- reg &= ~(0x3F);
- reg |= p_h263->rc_min_qp;
- mfc_write(dev, reg, S5P_FIMV_ENC_RC_QBOUND);
- /* extended encoder ctrl */
- shm = s5p_mfc_read_shm(ctx, EXT_ENC_CONTROL);
- /* vbv buffer size */
- if (p->frame_skip_mode ==
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
- shm &= ~(0xFFFF << 16);
- shm |= (p->vbv_size << 16);
- }
- s5p_mfc_write_shm(ctx, shm, EXT_ENC_CONTROL);
- return 0;
-}
-
-/* Initialize decoding */
-int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- s5p_mfc_set_shared_buffer(ctx);
- /* Setup loop filter, for decoding this is only valid for MPEG4 */
- if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_DEC)
- mfc_write(dev, ctx->loop_filter_mpeg4, S5P_FIMV_ENC_LF_CTRL);
- else
- mfc_write(dev, 0, S5P_FIMV_ENC_LF_CTRL);
- mfc_write(dev, ((ctx->slice_interface & S5P_FIMV_SLICE_INT_MASK) <<
- S5P_FIMV_SLICE_INT_SHIFT) | (ctx->display_delay_enable <<
- S5P_FIMV_DDELAY_ENA_SHIFT) | ((ctx->display_delay &
- S5P_FIMV_DDELAY_VAL_MASK) << S5P_FIMV_DDELAY_VAL_SHIFT),
- S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
- mfc_write(dev,
- ((S5P_FIMV_CH_SEQ_HEADER & S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT)
- | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
- return 0;
-}
-
-static void s5p_mfc_set_flush(struct s5p_mfc_ctx *ctx, int flush)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned int dpb;
-
- if (flush)
- dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) | (
- S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
- else
- dpb = mfc_read(dev, S5P_FIMV_SI_CH0_DPB_CONF_CTRL) &
- ~(S5P_FIMV_DPB_FLUSH_MASK << S5P_FIMV_DPB_FLUSH_SHIFT);
- mfc_write(dev, dpb, S5P_FIMV_SI_CH0_DPB_CONF_CTRL);
-}
-
-/* Decode a single frame */
-int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
- enum s5p_mfc_decode_arg last_frame)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- mfc_write(dev, ctx->dec_dst_flag, S5P_FIMV_SI_CH0_RELEASE_BUF);
- s5p_mfc_set_shared_buffer(ctx);
- s5p_mfc_set_flush(ctx, ctx->dpb_flush_flag);
- /* Issue different commands to instance basing on whether it
- * is the last frame or not. */
- switch (last_frame) {
- case MFC_DEC_FRAME:
- mfc_write(dev, ((S5P_FIMV_CH_FRAME_START & S5P_FIMV_CH_MASK) <<
- S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
- break;
- case MFC_DEC_LAST_FRAME:
- mfc_write(dev, ((S5P_FIMV_CH_LAST_FRAME & S5P_FIMV_CH_MASK) <<
- S5P_FIMV_CH_SHIFT) | (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
- break;
- case MFC_DEC_RES_CHANGE:
- mfc_write(dev, ((S5P_FIMV_CH_FRAME_START_REALLOC &
- S5P_FIMV_CH_MASK) << S5P_FIMV_CH_SHIFT) | (ctx->inst_no),
- S5P_FIMV_SI_CH0_INST_ID);
- break;
- }
- mfc_debug(2, "Decoding a usual frame\n");
- return 0;
-}
-
-int s5p_mfc_init_encode(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- if (ctx->codec_mode == S5P_FIMV_CODEC_H264_ENC)
- s5p_mfc_set_enc_params_h264(ctx);
- else if (ctx->codec_mode == S5P_FIMV_CODEC_MPEG4_ENC)
- s5p_mfc_set_enc_params_mpeg4(ctx);
- else if (ctx->codec_mode == S5P_FIMV_CODEC_H263_ENC)
- s5p_mfc_set_enc_params_h263(ctx);
- else {
- mfc_err("Unknown codec for encoding (%x)\n",
- ctx->codec_mode);
- return -EINVAL;
- }
- s5p_mfc_set_shared_buffer(ctx);
- mfc_write(dev, ((S5P_FIMV_CH_SEQ_HEADER << 16) & 0x70000) |
- (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
- return 0;
-}
-
-/* Encode a single frame */
-int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- /* memory structure cur. frame */
- if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12M)
- mfc_write(dev, 0, S5P_FIMV_ENC_MAP_FOR_CUR);
- else if (ctx->src_fmt->fourcc == V4L2_PIX_FMT_NV12MT)
- mfc_write(dev, 3, S5P_FIMV_ENC_MAP_FOR_CUR);
- s5p_mfc_set_shared_buffer(ctx);
- mfc_write(dev, (S5P_FIMV_CH_FRAME_START << 16 & 0x70000) |
- (ctx->inst_no), S5P_FIMV_SI_CH0_INST_ID);
- return 0;
-}
-
-static int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
-{
- unsigned long flags;
- int new_ctx;
- int cnt;
-
- spin_lock_irqsave(&dev->condlock, flags);
- new_ctx = (dev->curr_ctx + 1) % MFC_NUM_CONTEXTS;
- cnt = 0;
- while (!test_bit(new_ctx, &dev->ctx_work_bits)) {
- new_ctx = (new_ctx + 1) % MFC_NUM_CONTEXTS;
- if (++cnt > MFC_NUM_CONTEXTS) {
- /* No contexts to run */
- spin_unlock_irqrestore(&dev->condlock, flags);
- return -EAGAIN;
- }
- }
- spin_unlock_irqrestore(&dev->condlock, flags);
- return new_ctx;
-}
-
-static void s5p_mfc_run_res_change(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
-
- s5p_mfc_set_dec_stream_buffer(ctx, 0, 0, 0);
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_decode_one_frame(ctx, MFC_DEC_RES_CHANGE);
-}
-
-static int s5p_mfc_run_dec_frame(struct s5p_mfc_ctx *ctx, int last_frame)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- struct s5p_mfc_buf *temp_vb;
- unsigned long flags;
- unsigned int index;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- /* Frames are being decoded */
- if (list_empty(&ctx->src_queue)) {
- mfc_debug(2, "No src buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return -EAGAIN;
- }
- /* Get the next source buffer */
- temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- temp_vb->used = 1;
- s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_dma_addr(temp_vb->b, 0), ctx->consumed_stream,
- temp_vb->b->v4l2_planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- index = temp_vb->b->v4l2_buf.index;
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- if (temp_vb->b->v4l2_planes[0].bytesused == 0) {
- last_frame = MFC_DEC_LAST_FRAME;
- mfc_debug(2, "Setting ctx->state to FINISHING\n");
- ctx->state = MFCINST_FINISHING;
- }
- s5p_mfc_decode_one_frame(ctx, last_frame);
- return 0;
-}
-
-static int s5p_mfc_run_enc_frame(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *dst_mb;
- struct s5p_mfc_buf *src_mb;
- unsigned long src_y_addr, src_c_addr, dst_addr;
- unsigned int dst_size;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- if (list_empty(&ctx->src_queue)) {
- mfc_debug(2, "no src buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return -EAGAIN;
- }
- if (list_empty(&ctx->dst_queue)) {
- mfc_debug(2, "no dst buffers\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return -EAGAIN;
- }
- src_mb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- src_mb->used = 1;
- src_y_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 0);
- src_c_addr = vb2_dma_contig_plane_dma_addr(src_mb->b, 1);
- s5p_mfc_set_enc_frame_buffer(ctx, src_y_addr, src_c_addr);
- dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_mb->used = 1;
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
- dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_encode_one_frame(ctx);
- return 0;
-}
-
-static void s5p_mfc_run_init_dec(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *temp_vb;
-
- /* Initializing decoding - parsing header */
- spin_lock_irqsave(&dev->irqlock, flags);
- mfc_debug(2, "Preparing to init decoding\n");
- temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- s5p_mfc_set_dec_desc_buffer(ctx);
- mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
- s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
- 0, temp_vb->b->v4l2_planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_init_decode(ctx);
-}
-
-static void s5p_mfc_run_init_enc(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *dst_mb;
- unsigned long dst_addr;
- unsigned int dst_size;
-
- s5p_mfc_set_enc_ref_buffer(ctx);
- spin_lock_irqsave(&dev->irqlock, flags);
- dst_mb = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, list);
- dst_addr = vb2_dma_contig_plane_dma_addr(dst_mb->b, 0);
- dst_size = vb2_plane_size(dst_mb->b, 0);
- s5p_mfc_set_enc_stream_buffer(ctx, dst_addr, dst_size);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- s5p_mfc_init_encode(ctx);
-}
-
-static int s5p_mfc_run_init_dec_buffers(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- unsigned long flags;
- struct s5p_mfc_buf *temp_vb;
- int ret;
-
- /*
- * Header was parsed now starting processing
- * First set the output frame buffers
- */
- if (ctx->capture_state != QUEUE_BUFS_MMAPED) {
- mfc_err("It seems that not all destionation buffers were "
- "mmaped\nMFC requires that all destination are mmaped "
- "before starting processing\n");
- return -EAGAIN;
- }
- spin_lock_irqsave(&dev->irqlock, flags);
- if (list_empty(&ctx->src_queue)) {
- mfc_err("Header has been deallocated in the middle of"
- " initialization\n");
- spin_unlock_irqrestore(&dev->irqlock, flags);
- return -EIO;
- }
- temp_vb = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
- mfc_debug(2, "Header size: %d\n", temp_vb->b->v4l2_planes[0].bytesused);
- s5p_mfc_set_dec_stream_buffer(ctx,
- vb2_dma_contig_plane_dma_addr(temp_vb->b, 0),
- 0, temp_vb->b->v4l2_planes[0].bytesused);
- spin_unlock_irqrestore(&dev->irqlock, flags);
- dev->curr_ctx = ctx->num;
- s5p_mfc_clean_ctx_int_flags(ctx);
- ret = s5p_mfc_set_dec_frame_buffer(ctx);
- if (ret) {
- mfc_err("Failed to alloc frame mem\n");
- ctx->state = MFCINST_ERROR;
- }
- return ret;
-}
-
-/* Try running an operation on hardware */
-void s5p_mfc_try_run(struct s5p_mfc_dev *dev)
-{
- struct s5p_mfc_ctx *ctx;
- int new_ctx;
- unsigned int ret = 0;
-
- if (test_bit(0, &dev->enter_suspend)) {
- mfc_debug(1, "Entering suspend so do not schedule any jobs\n");
- return;
- }
- /* Check whether hardware is not running */
- if (test_and_set_bit(0, &dev->hw_lock) != 0) {
- /* This is perfectly ok, the scheduled ctx should wait */
- mfc_debug(1, "Couldn't lock HW\n");
- return;
- }
- /* Choose the context to run */
- new_ctx = s5p_mfc_get_new_ctx(dev);
- if (new_ctx < 0) {
- /* No contexts to run */
- if (test_and_clear_bit(0, &dev->hw_lock) == 0) {
- mfc_err("Failed to unlock hardware\n");
- return;
- }
- mfc_debug(1, "No ctx is scheduled to be run\n");
- return;
- }
- ctx = dev->ctx[new_ctx];
- /* Got context to run in ctx */
- /*
- * Last frame has already been sent to MFC.
- * Now obtaining frames from MFC buffer
- */
- s5p_mfc_clock_on();
- if (ctx->type == MFCINST_DECODER) {
- s5p_mfc_set_dec_desc_buffer(ctx);
- switch (ctx->state) {
- case MFCINST_FINISHING:
- s5p_mfc_run_dec_frame(ctx, MFC_DEC_LAST_FRAME);
- break;
- case MFCINST_RUNNING:
- ret = s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
- break;
- case MFCINST_INIT:
- s5p_mfc_clean_ctx_int_flags(ctx);
- ret = s5p_mfc_open_inst_cmd(ctx);
- break;
- case MFCINST_RETURN_INST:
- s5p_mfc_clean_ctx_int_flags(ctx);
- ret = s5p_mfc_close_inst_cmd(ctx);
- break;
- case MFCINST_GOT_INST:
- s5p_mfc_run_init_dec(ctx);
- break;
- case MFCINST_HEAD_PARSED:
- ret = s5p_mfc_run_init_dec_buffers(ctx);
- mfc_debug(1, "head parsed\n");
- break;
- case MFCINST_RES_CHANGE_INIT:
- s5p_mfc_run_res_change(ctx);
- break;
- case MFCINST_RES_CHANGE_FLUSH:
- s5p_mfc_run_dec_frame(ctx, MFC_DEC_FRAME);
- break;
- case MFCINST_RES_CHANGE_END:
- mfc_debug(2, "Finished remaining frames after resolution change\n");
- ctx->capture_state = QUEUE_FREE;
- mfc_debug(2, "Will re-init the codec\n");
- s5p_mfc_run_init_dec(ctx);
- break;
- default:
- ret = -EAGAIN;
- }
- } else if (ctx->type == MFCINST_ENCODER) {
- switch (ctx->state) {
- case MFCINST_FINISHING:
- case MFCINST_RUNNING:
- ret = s5p_mfc_run_enc_frame(ctx);
- break;
- case MFCINST_INIT:
- s5p_mfc_clean_ctx_int_flags(ctx);
- ret = s5p_mfc_open_inst_cmd(ctx);
- break;
- case MFCINST_RETURN_INST:
- s5p_mfc_clean_ctx_int_flags(ctx);
- ret = s5p_mfc_close_inst_cmd(ctx);
- break;
- case MFCINST_GOT_INST:
- s5p_mfc_run_init_enc(ctx);
- break;
- default:
- ret = -EAGAIN;
- }
- } else {
- mfc_err("Invalid context type: %d\n", ctx->type);
- ret = -EAGAIN;
- }
-
- if (ret) {
- /* Free hardware lock */
- if (test_and_clear_bit(0, &dev->hw_lock) == 0)
- mfc_err("Failed to unlock hardware\n");
-
- /* This is in deed imporant, as no operation has been
- * scheduled, reduce the clock count as no one will
- * ever do this, because no interrupt related to this try_run
- * will ever come from hardware. */
- s5p_mfc_clock_off();
- }
-}
-
-
-void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
-{
- struct s5p_mfc_buf *b;
- int i;
-
- while (!list_empty(lh)) {
- b = list_entry(lh->next, struct s5p_mfc_buf, list);
- for (i = 0; i < b->b->num_planes; i++)
- vb2_set_plane_payload(b->b, i, 0);
- vb2_buffer_done(b->b, VB2_BUF_STATE_ERROR);
- list_del(&b->list);
- }
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h b/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
deleted file mode 100644
index 5932d1c782c5..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * drivers/media/video/samsung/mfc5/s5p_mfc_opr.h
- *
- * Header file for Samsung MFC (Multi Function Codec - FIMV) driver
- * Contains declarations of hw related functions.
- *
- * Kamil Debski, Copyright (C) 2011 Samsung Electronics
- * http://www.samsung.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.
- */
-
-#ifndef S5P_MFC_OPR_H_
-#define S5P_MFC_OPR_H_
-
-#include "s5p_mfc_common.h"
-
-int s5p_mfc_init_decode(struct s5p_mfc_ctx *ctx);
-int s5p_mfc_init_encode(struct s5p_mfc_ctx *mfc_ctx);
-
-/* Decoding functions */
-int s5p_mfc_set_dec_frame_buffer(struct s5p_mfc_ctx *ctx);
-int s5p_mfc_set_dec_stream_buffer(struct s5p_mfc_ctx *ctx, int buf_addr,
- unsigned int start_num_byte,
- unsigned int buf_size);
-
-/* Encoding functions */
-void s5p_mfc_set_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long y_addr, unsigned long c_addr);
-int s5p_mfc_set_enc_stream_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long addr, unsigned int size);
-void s5p_mfc_get_enc_frame_buffer(struct s5p_mfc_ctx *ctx,
- unsigned long *y_addr, unsigned long *c_addr);
-int s5p_mfc_set_enc_ref_buffer(struct s5p_mfc_ctx *mfc_ctx);
-
-int s5p_mfc_decode_one_frame(struct s5p_mfc_ctx *ctx,
- enum s5p_mfc_decode_arg last_frame);
-int s5p_mfc_encode_one_frame(struct s5p_mfc_ctx *mfc_ctx);
-
-/* Memory allocation */
-int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx);
-
-int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx);
-
-int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx);
-void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx);
-
-void s5p_mfc_try_run(struct s5p_mfc_dev *dev);
-void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq);
-
-#define s5p_mfc_get_dspl_y_adr() (readl(dev->regs_base + \
- S5P_FIMV_SI_DISPLAY_Y_ADR) << \
- MFC_OFFSET_SHIFT)
-#define s5p_mfc_get_dec_y_adr() (readl(dev->regs_base + \
- S5P_FIMV_SI_DECODE_Y_ADR) << \
- MFC_OFFSET_SHIFT)
-#define s5p_mfc_get_dspl_status() readl(dev->regs_base + \
- S5P_FIMV_SI_DISPLAY_STATUS)
-#define s5p_mfc_get_dec_status() readl(dev->regs_base + \
- S5P_FIMV_SI_DECODE_STATUS)
-#define s5p_mfc_get_frame_type() (readl(dev->regs_base + \
- S5P_FIMV_DECODE_FRAME_TYPE) \
- & S5P_FIMV_DECODE_FRAME_MASK)
-#define s5p_mfc_get_consumed_stream() readl(dev->regs_base + \
- S5P_FIMV_SI_CONSUMED_BYTES)
-#define s5p_mfc_get_int_reason() (readl(dev->regs_base + \
- S5P_FIMV_RISC2HOST_CMD) & \
- S5P_FIMV_RISC2HOST_CMD_MASK)
-#define s5p_mfc_get_int_err() readl(dev->regs_base + \
- S5P_FIMV_RISC2HOST_ARG2)
-#define s5p_mfc_err_dec(x) (((x) & S5P_FIMV_ERR_DEC_MASK) >> \
- S5P_FIMV_ERR_DEC_SHIFT)
-#define s5p_mfc_err_dspl(x) (((x) & S5P_FIMV_ERR_DSPL_MASK) >> \
- S5P_FIMV_ERR_DSPL_SHIFT)
-#define s5p_mfc_get_img_width() readl(dev->regs_base + \
- S5P_FIMV_SI_HRESOL)
-#define s5p_mfc_get_img_height() readl(dev->regs_base + \
- S5P_FIMV_SI_VRESOL)
-#define s5p_mfc_get_dpb_count() readl(dev->regs_base + \
- S5P_FIMV_SI_BUF_NUMBER)
-#define s5p_mfc_get_inst_no() readl(dev->regs_base + \
- S5P_FIMV_RISC2HOST_ARG1)
-#define s5p_mfc_get_enc_strm_size() readl(dev->regs_base + \
- S5P_FIMV_ENC_SI_STRM_SIZE)
-#define s5p_mfc_get_enc_slice_type() readl(dev->regs_base + \
- S5P_FIMV_ENC_SI_SLICE_TYPE)
-
-#endif /* S5P_MFC_OPR_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
deleted file mode 100644
index 738a607be43c..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.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.
- */
-
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/platform_device.h>
-#ifdef CONFIG_PM_RUNTIME
-#include <linux/pm_runtime.h>
-#endif
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-#include "s5p_mfc_pm.h"
-
-#define MFC_CLKNAME "sclk_mfc"
-#define MFC_GATE_CLK_NAME "mfc"
-
-#define CLK_DEBUG
-
-static struct s5p_mfc_pm *pm;
-static struct s5p_mfc_dev *p_dev;
-
-#ifdef CLK_DEBUG
-atomic_t clk_ref;
-#endif
-
-int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
-{
- int ret = 0;
-
- pm = &dev->pm;
- p_dev = dev;
- pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
- if (IS_ERR(pm->clock_gate)) {
- mfc_err("Failed to get clock-gating control\n");
- ret = PTR_ERR(pm->clock_gate);
- goto err_g_ip_clk;
- }
-
- ret = clk_prepare(pm->clock_gate);
- if (ret) {
- mfc_err("Failed to preapre clock-gating control\n");
- goto err_p_ip_clk;
- }
-
- pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
- if (IS_ERR(pm->clock)) {
- mfc_err("Failed to get MFC clock\n");
- ret = PTR_ERR(pm->clock);
- goto err_g_ip_clk_2;
- }
-
- ret = clk_prepare(pm->clock);
- if (ret) {
- mfc_err("Failed to prepare MFC clock\n");
- goto err_p_ip_clk_2;
- }
-
- atomic_set(&pm->power, 0);
-#ifdef CONFIG_PM_RUNTIME
- pm->device = &dev->plat_dev->dev;
- pm_runtime_enable(pm->device);
-#endif
-#ifdef CLK_DEBUG
- atomic_set(&clk_ref, 0);
-#endif
- return 0;
-err_p_ip_clk_2:
- clk_put(pm->clock);
-err_g_ip_clk_2:
- clk_unprepare(pm->clock_gate);
-err_p_ip_clk:
- clk_put(pm->clock_gate);
-err_g_ip_clk:
- return ret;
-}
-
-void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
-{
- clk_unprepare(pm->clock_gate);
- clk_put(pm->clock_gate);
- clk_unprepare(pm->clock);
- clk_put(pm->clock);
-#ifdef CONFIG_PM_RUNTIME
- pm_runtime_disable(pm->device);
-#endif
-}
-
-int s5p_mfc_clock_on(void)
-{
- int ret;
-#ifdef CLK_DEBUG
- atomic_inc(&clk_ref);
- mfc_debug(3, "+ %d", atomic_read(&clk_ref));
-#endif
- ret = clk_enable(pm->clock_gate);
- return ret;
-}
-
-void s5p_mfc_clock_off(void)
-{
-#ifdef CLK_DEBUG
- atomic_dec(&clk_ref);
- mfc_debug(3, "- %d", atomic_read(&clk_ref));
-#endif
- clk_disable(pm->clock_gate);
-}
-
-int s5p_mfc_power_on(void)
-{
-#ifdef CONFIG_PM_RUNTIME
- return pm_runtime_get_sync(pm->device);
-#else
- atomic_set(&pm->power, 1);
- return 0;
-#endif
-}
-
-int s5p_mfc_power_off(void)
-{
-#ifdef CONFIG_PM_RUNTIME
- return pm_runtime_put_sync(pm->device);
-#else
- atomic_set(&pm->power, 0);
- return 0;
-#endif
-}
-
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.h b/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
deleted file mode 100644
index 5107914f27e4..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_pm.h
- *
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_PM_H_
-#define S5P_MFC_PM_H_
-
-int s5p_mfc_init_pm(struct s5p_mfc_dev *dev);
-void s5p_mfc_final_pm(struct s5p_mfc_dev *dev);
-
-int s5p_mfc_clock_on(void);
-void s5p_mfc_clock_off(void);
-int s5p_mfc_power_on(void);
-int s5p_mfc_power_off(void);
-
-#endif /* S5P_MFC_PM_H_ */
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.c b/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
deleted file mode 100644
index 91fdbac8c37a..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.c
- *
- * Copyright (c) 2010 Samsung Electronics Co., Ltd.
- * http://www.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.
- */
-
-#ifdef CONFIG_ARCH_EXYNOS4
-#include <linux/dma-mapping.h>
-#endif
-#include <linux/io.h>
-#include "s5p_mfc_common.h"
-#include "s5p_mfc_debug.h"
-
-int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx)
-{
- struct s5p_mfc_dev *dev = ctx->dev;
- void *shm_alloc_ctx = dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
-
- ctx->shm_alloc = vb2_dma_contig_memops.alloc(shm_alloc_ctx,
- SHARED_BUF_SIZE);
- if (IS_ERR(ctx->shm_alloc)) {
- mfc_err("failed to allocate shared memory\n");
- return PTR_ERR(ctx->shm_alloc);
- }
- /* shm_ofs only keeps the offset from base (port a) */
- ctx->shm_ofs = s5p_mfc_mem_cookie(shm_alloc_ctx, ctx->shm_alloc)
- - dev->bank1;
- BUG_ON(ctx->shm_ofs & ((1 << MFC_BANK1_ALIGN_ORDER) - 1));
- ctx->shm = vb2_dma_contig_memops.vaddr(ctx->shm_alloc);
- if (!ctx->shm) {
- vb2_dma_contig_memops.put(ctx->shm_alloc);
- ctx->shm_ofs = 0;
- ctx->shm_alloc = NULL;
- mfc_err("failed to virt addr of shared memory\n");
- return -ENOMEM;
- }
- memset((void *)ctx->shm, 0, SHARED_BUF_SIZE);
- wmb();
- return 0;
-}
-
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_shm.h b/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
deleted file mode 100644
index cf962a466276..000000000000
--- a/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * linux/drivers/media/video/s5p-mfc/s5p_mfc_shm.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.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 S5P_MFC_SHM_H_
-#define S5P_MFC_SHM_H_
-
-enum MFC_SHM_OFS {
- EXTENEDED_DECODE_STATUS = 0x00, /* D */
- SET_FRAME_TAG = 0x04, /* D */
- GET_FRAME_TAG_TOP = 0x08, /* D */
- GET_FRAME_TAG_BOT = 0x0C, /* D */
- PIC_TIME_TOP = 0x10, /* D */
- PIC_TIME_BOT = 0x14, /* D */
- START_BYTE_NUM = 0x18, /* D */
-
- CROP_INFO_H = 0x20, /* D */
- CROP_INFO_V = 0x24, /* D */
- EXT_ENC_CONTROL = 0x28, /* E */
- ENC_PARAM_CHANGE = 0x2C, /* E */
- RC_VOP_TIMING = 0x30, /* E, MPEG4 */
- HEC_PERIOD = 0x34, /* E, MPEG4 */
- METADATA_ENABLE = 0x38, /* C */
- METADATA_STATUS = 0x3C, /* C */
- METADATA_DISPLAY_INDEX = 0x40, /* C */
- EXT_METADATA_START_ADDR = 0x44, /* C */
- PUT_EXTRADATA = 0x48, /* C */
- EXTRADATA_ADDR = 0x4C, /* C */
-
- ALLOC_LUMA_DPB_SIZE = 0x64, /* D */
- ALLOC_CHROMA_DPB_SIZE = 0x68, /* D */
- ALLOC_MV_SIZE = 0x6C, /* D */
- P_B_FRAME_QP = 0x70, /* E */
- SAMPLE_ASPECT_RATIO_IDC = 0x74, /* E, H.264, depend on
- ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
- EXTENDED_SAR = 0x78, /* E, H.264, depned on
- ASPECT_RATIO_VUI_ENABLE in EXT_ENC_CONTROL */
- DISP_PIC_PROFILE = 0x7C, /* D */
- FLUSH_CMD_TYPE = 0x80, /* C */
- FLUSH_CMD_INBUF1 = 0x84, /* C */
- FLUSH_CMD_INBUF2 = 0x88, /* C */
- FLUSH_CMD_OUTBUF = 0x8C, /* E */
- NEW_RC_BIT_RATE = 0x90, /* E, format as RC_BIT_RATE(0xC5A8)
- depend on RC_BIT_RATE_CHANGE in ENC_PARAM_CHANGE */
- NEW_RC_FRAME_RATE = 0x94, /* E, format as RC_FRAME_RATE(0xD0D0)
- depend on RC_FRAME_RATE_CHANGE in ENC_PARAM_CHANGE */
- NEW_I_PERIOD = 0x98, /* E, format as I_FRM_CTRL(0xC504)
- depend on I_PERIOD_CHANGE in ENC_PARAM_CHANGE */
- H264_I_PERIOD = 0x9C, /* E, H.264, open GOP */
- RC_CONTROL_CONFIG = 0xA0, /* E */
- BATCH_INPUT_ADDR = 0xA4, /* E */
- BATCH_OUTPUT_ADDR = 0xA8, /* E */
- BATCH_OUTPUT_SIZE = 0xAC, /* E */
- MIN_LUMA_DPB_SIZE = 0xB0, /* D */
- DEVICE_FORMAT_ID = 0xB4, /* C */
- H264_POC_TYPE = 0xB8, /* D */
- MIN_CHROMA_DPB_SIZE = 0xBC, /* D */
- DISP_PIC_FRAME_TYPE = 0xC0, /* D */
- FREE_LUMA_DPB = 0xC4, /* D, VC1 MPEG4 */
- ASPECT_RATIO_INFO = 0xC8, /* D, MPEG4 */
- EXTENDED_PAR = 0xCC, /* D, MPEG4 */
- DBG_HISTORY_INPUT0 = 0xD0, /* C */
- DBG_HISTORY_INPUT1 = 0xD4, /* C */
- DBG_HISTORY_OUTPUT = 0xD8, /* C */
- HIERARCHICAL_P_QP = 0xE0, /* E, H.264 */
-};
-
-int s5p_mfc_init_shm(struct s5p_mfc_ctx *ctx);
-
-#define s5p_mfc_write_shm(ctx, x, ofs) \
- do { \
- writel(x, (ctx->shm + ofs)); \
- wmb(); \
- } while (0)
-
-static inline u32 s5p_mfc_read_shm(struct s5p_mfc_ctx *ctx, unsigned int ofs)
-{
- rmb();
- return readl(ctx->shm + ofs);
-}
-
-#endif /* S5P_MFC_SHM_H_ */
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
deleted file mode 100644
index f248b2856720..000000000000
--- a/drivers/media/video/s5p-tv/Kconfig
+++ /dev/null
@@ -1,86 +0,0 @@
-# drivers/media/video/s5p-tv/Kconfig
-#
-# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
-# http://www.samsung.com/
-# Tomasz Stanislawski <t.stanislaws@samsung.com>
-#
-# Licensed under GPL
-
-config VIDEO_SAMSUNG_S5P_TV
- bool "Samsung TV driver for S5P platform (experimental)"
- depends on PLAT_S5P && PM_RUNTIME
- depends on EXPERIMENTAL
- default n
- ---help---
- Say Y here to enable selecting the TV output devices for
- Samsung S5P platform.
-
-if VIDEO_SAMSUNG_S5P_TV
-
-config VIDEO_SAMSUNG_S5P_HDMI
- tristate "Samsung HDMI Driver"
- depends on VIDEO_V4L2
- depends on VIDEO_SAMSUNG_S5P_TV
- select VIDEO_SAMSUNG_S5P_HDMIPHY
- help
- Say Y here if you want support for the HDMI output
- interface in S5P Samsung SoC. The driver can be compiled
- as module. It is an auxiliary driver, that exposes a V4L2
- subdev for use by other drivers. This driver requires
- hdmiphy driver to work correctly.
-
-config VIDEO_SAMSUNG_S5P_HDMI_DEBUG
- bool "Enable debug for HDMI Driver"
- depends on VIDEO_SAMSUNG_S5P_HDMI
- default n
- help
- Enables debugging for HDMI driver.
-
-config VIDEO_SAMSUNG_S5P_HDMIPHY
- tristate "Samsung HDMIPHY Driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && I2C
- depends on VIDEO_SAMSUNG_S5P_TV
- help
- Say Y here if you want support for the physical HDMI
- interface in S5P Samsung SoC. The driver can be compiled
- as module. It is an I2C driver, that exposes a V4L2
- subdev for use by other drivers.
-
-config VIDEO_SAMSUNG_S5P_SII9234
- tristate "Samsung SII9234 Driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && I2C
- depends on VIDEO_SAMSUNG_S5P_TV
- help
- Say Y here if you want support for the MHL interface
- in S5P Samsung SoC. The driver can be compiled
- as module. It is an I2C driver, that exposes a V4L2
- subdev for use by other drivers.
-
-config VIDEO_SAMSUNG_S5P_SDO
- tristate "Samsung Analog TV Driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on VIDEO_SAMSUNG_S5P_TV
- help
- Say Y here if you want support for the analog TV output
- interface in S5P Samsung SoC. The driver can be compiled
- as module. It is an auxiliary driver, that exposes a V4L2
- subdev for use by other drivers. This driver requires
- hdmiphy driver to work correctly.
-
-config VIDEO_SAMSUNG_S5P_MIXER
- tristate "Samsung Mixer and Video Processor Driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on VIDEO_SAMSUNG_S5P_TV
- select VIDEOBUF2_DMA_CONTIG
- help
- Say Y here if you want support for the Mixer in Samsung S5P SoCs.
- This device produce image data to one of output interfaces.
-
-config VIDEO_SAMSUNG_S5P_MIXER_DEBUG
- bool "Enable debug for Mixer Driver"
- depends on VIDEO_SAMSUNG_S5P_MIXER
- default n
- help
- Enables debugging for Mixer driver.
-
-endif # VIDEO_SAMSUNG_S5P_TV
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
deleted file mode 100644
index f49e756a2fde..000000000000
--- a/drivers/media/video/s5p-tv/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# drivers/media/video/samsung/tvout/Makefile
-#
-# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
-# http://www.samsung.com/
-# Tomasz Stanislawski <t.stanislaws@samsung.com>
-#
-# Licensed under GPL
-
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
-s5p-hdmiphy-y += hdmiphy_drv.o
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
-s5p-sii9234-y += sii9234_drv.o
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
-s5p-hdmi-y += hdmi_drv.o
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
-s5p-sdo-y += sdo_drv.o
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MIXER) += s5p-mixer.o
-s5p-mixer-y += mixer_drv.o mixer_video.o mixer_reg.o mixer_grp_layer.o mixer_vp_layer.o
-
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
deleted file mode 100644
index 20cb6eef2979..000000000000
--- a/drivers/media/video/s5p-tv/hdmi_drv.c
+++ /dev/null
@@ -1,1007 +0,0 @@
-/*
- * Samsung HDMI interface driver
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *
- * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
- * or (at your option) any later version
- */
-
-#ifdef CONFIG_VIDEO_SAMSUNG_S5P_HDMI_DEBUG
-#define DEBUG
-#endif
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <media/v4l2-subdev.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/delay.h>
-#include <linux/bug.h>
-#include <linux/pm_runtime.h>
-#include <linux/clk.h>
-#include <linux/regulator/consumer.h>
-
-#include <media/s5p_hdmi.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-device.h>
-
-#include "regs-hdmi.h"
-
-MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
-MODULE_DESCRIPTION("Samsung HDMI");
-MODULE_LICENSE("GPL");
-
-/* default preset configured on probe */
-#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94
-
-struct hdmi_pulse {
- u32 beg;
- u32 end;
-};
-
-struct hdmi_timings {
- struct hdmi_pulse hact;
- u32 hsyn_pol; /* 0 - high, 1 - low */
- struct hdmi_pulse hsyn;
- u32 interlaced;
- struct hdmi_pulse vact[2];
- u32 vsyn_pol; /* 0 - high, 1 - low */
- u32 vsyn_off;
- struct hdmi_pulse vsyn[2];
-};
-
-struct hdmi_resources {
- struct clk *hdmi;
- struct clk *sclk_hdmi;
- struct clk *sclk_pixel;
- struct clk *sclk_hdmiphy;
- struct clk *hdmiphy;
- struct regulator_bulk_data *regul_bulk;
- int regul_count;
-};
-
-struct hdmi_device {
- /** base address of HDMI registers */
- void __iomem *regs;
- /** HDMI interrupt */
- unsigned int irq;
- /** pointer to device parent */
- struct device *dev;
- /** subdev generated by HDMI device */
- struct v4l2_subdev sd;
- /** V4L2 device structure */
- struct v4l2_device v4l2_dev;
- /** subdev of HDMIPHY interface */
- struct v4l2_subdev *phy_sd;
- /** subdev of MHL interface */
- struct v4l2_subdev *mhl_sd;
- /** configuration of current graphic mode */
- const struct hdmi_timings *cur_conf;
- /** flag indicating that timings are dirty */
- int cur_conf_dirty;
- /** current preset */
- u32 cur_preset;
- /** other resources */
- struct hdmi_resources res;
-};
-
-static struct platform_device_id hdmi_driver_types[] = {
- {
- .name = "s5pv210-hdmi",
- }, {
- .name = "exynos4-hdmi",
- }, {
- /* end node */
- }
-};
-
-static const struct v4l2_subdev_ops hdmi_sd_ops;
-
-static struct hdmi_device *sd_to_hdmi_dev(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct hdmi_device, sd);
-}
-
-static inline
-void hdmi_write(struct hdmi_device *hdev, u32 reg_id, u32 value)
-{
- writel(value, hdev->regs + reg_id);
-}
-
-static inline
-void hdmi_write_mask(struct hdmi_device *hdev, u32 reg_id, u32 value, u32 mask)
-{
- u32 old = readl(hdev->regs + reg_id);
- value = (value & mask) | (old & ~mask);
- writel(value, hdev->regs + reg_id);
-}
-
-static inline
-void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
-{
- writeb(value, hdev->regs + reg_id);
-}
-
-static inline
-void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value)
-{
- switch (n) {
- default:
- writeb(value >> 24, hdev->regs + reg_id + 12);
- case 3:
- writeb(value >> 16, hdev->regs + reg_id + 8);
- case 2:
- writeb(value >> 8, hdev->regs + reg_id + 4);
- case 1:
- writeb(value >> 0, hdev->regs + reg_id + 0);
- }
-}
-
-static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
-{
- return readl(hdev->regs + reg_id);
-}
-
-static irqreturn_t hdmi_irq_handler(int irq, void *dev_data)
-{
- struct hdmi_device *hdev = dev_data;
- u32 intc_flag;
-
- (void)irq;
- intc_flag = hdmi_read(hdev, HDMI_INTC_FLAG);
- /* clearing flags for HPD plug/unplug */
- if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
- printk(KERN_INFO "unplugged\n");
- hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
- HDMI_INTC_FLAG_HPD_UNPLUG);
- }
- if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
- printk(KERN_INFO "plugged\n");
- hdmi_write_mask(hdev, HDMI_INTC_FLAG, ~0,
- HDMI_INTC_FLAG_HPD_PLUG);
- }
-
- return IRQ_HANDLED;
-}
-
-static void hdmi_reg_init(struct hdmi_device *hdev)
-{
- /* enable HPD interrupts */
- hdmi_write_mask(hdev, HDMI_INTC_CON, ~0, HDMI_INTC_EN_GLOBAL |
- HDMI_INTC_EN_HPD_PLUG | HDMI_INTC_EN_HPD_UNPLUG);
- /* choose DVI mode */
- hdmi_write_mask(hdev, HDMI_MODE_SEL,
- HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
- hdmi_write_mask(hdev, HDMI_CON_2, ~0,
- HDMI_DVI_PERAMBLE_EN | HDMI_DVI_BAND_EN);
- /* disable bluescreen */
- hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
- /* choose bluescreen (fecal) color */
- hdmi_writeb(hdev, HDMI_BLUE_SCREEN_0, 0x12);
- hdmi_writeb(hdev, HDMI_BLUE_SCREEN_1, 0x34);
- hdmi_writeb(hdev, HDMI_BLUE_SCREEN_2, 0x56);
-}
-
-static void hdmi_timing_apply(struct hdmi_device *hdev,
- const struct hdmi_timings *t)
-{
- /* setting core registers */
- hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg);
- hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3,
- (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg);
- hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol);
- hdmi_writebn(hdev, HDMI_V_BLANK_0, 3,
- (t->vact[0].beg << 11) | t->vact[0].end);
- hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3,
- (t->vsyn[0].beg << 12) | t->vsyn[0].end);
- if (t->interlaced) {
- u32 vsyn_trans = t->hsyn.beg + t->vsyn_off;
-
- hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1);
- hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
- (t->hact.end << 12) | t->vact[1].end);
- hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3,
- (t->vact[1].end << 11) | t->vact[1].beg);
- hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3,
- (t->vsyn[1].beg << 12) | t->vsyn[1].end);
- hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3,
- (vsyn_trans << 12) | vsyn_trans);
- } else {
- hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0);
- hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
- (t->hact.end << 12) | t->vact[0].end);
- }
-
- /* Timing generator registers */
- hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end);
- hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg);
- hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg);
- hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg);
- hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg);
- hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2,
- t->vact[0].end - t->vact[0].beg);
- hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg);
- hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg);
- if (t->interlaced) {
- hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN);
- hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end);
- hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg);
- hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end);
- hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg);
- hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg);
- hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg);
- } else {
- hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN);
- hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end);
- }
-}
-
-static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
-{
- struct device *dev = hdmi_dev->dev;
- const struct hdmi_timings *conf = hdmi_dev->cur_conf;
- struct v4l2_dv_preset preset;
- int ret;
-
- dev_dbg(dev, "%s\n", __func__);
-
- /* skip if conf is already synchronized with HW */
- if (!hdmi_dev->cur_conf_dirty)
- return 0;
-
- /* reset hdmiphy */
- hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
- mdelay(10);
- hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
- mdelay(10);
-
- /* configure presets */
- preset.preset = hdmi_dev->cur_preset;
- ret = v4l2_subdev_call(hdmi_dev->phy_sd, video, s_dv_preset, &preset);
- if (ret) {
- dev_err(dev, "failed to set preset (%u)\n", preset.preset);
- return ret;
- }
-
- /* resetting HDMI core */
- hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
- mdelay(10);
- hdmi_write_mask(hdmi_dev, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
- mdelay(10);
-
- hdmi_reg_init(hdmi_dev);
-
- /* setting core registers */
- hdmi_timing_apply(hdmi_dev, conf);
-
- hdmi_dev->cur_conf_dirty = 0;
-
- return 0;
-}
-
-static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
-{
-#define DUMPREG(reg_id) \
- dev_dbg(hdev->dev, "%s:" #reg_id " = %08x\n", prefix, \
- readl(hdev->regs + reg_id))
-
- dev_dbg(hdev->dev, "%s: ---- CONTROL REGISTERS ----\n", prefix);
- DUMPREG(HDMI_INTC_FLAG);
- DUMPREG(HDMI_INTC_CON);
- DUMPREG(HDMI_HPD_STATUS);
- DUMPREG(HDMI_PHY_RSTOUT);
- DUMPREG(HDMI_PHY_VPLL);
- DUMPREG(HDMI_PHY_CMU);
- DUMPREG(HDMI_CORE_RSTOUT);
-
- dev_dbg(hdev->dev, "%s: ---- CORE REGISTERS ----\n", prefix);
- DUMPREG(HDMI_CON_0);
- DUMPREG(HDMI_CON_1);
- DUMPREG(HDMI_CON_2);
- DUMPREG(HDMI_SYS_STATUS);
- DUMPREG(HDMI_PHY_STATUS);
- DUMPREG(HDMI_STATUS_EN);
- DUMPREG(HDMI_HPD);
- DUMPREG(HDMI_MODE_SEL);
- DUMPREG(HDMI_HPD_GEN);
- DUMPREG(HDMI_DC_CONTROL);
- DUMPREG(HDMI_VIDEO_PATTERN_GEN);
-
- dev_dbg(hdev->dev, "%s: ---- CORE SYNC REGISTERS ----\n", prefix);
- DUMPREG(HDMI_H_BLANK_0);
- DUMPREG(HDMI_H_BLANK_1);
- DUMPREG(HDMI_V_BLANK_0);
- DUMPREG(HDMI_V_BLANK_1);
- DUMPREG(HDMI_V_BLANK_2);
- DUMPREG(HDMI_H_V_LINE_0);
- DUMPREG(HDMI_H_V_LINE_1);
- DUMPREG(HDMI_H_V_LINE_2);
- DUMPREG(HDMI_VSYNC_POL);
- DUMPREG(HDMI_INT_PRO_MODE);
- DUMPREG(HDMI_V_BLANK_F_0);
- DUMPREG(HDMI_V_BLANK_F_1);
- DUMPREG(HDMI_V_BLANK_F_2);
- DUMPREG(HDMI_H_SYNC_GEN_0);
- DUMPREG(HDMI_H_SYNC_GEN_1);
- DUMPREG(HDMI_H_SYNC_GEN_2);
- DUMPREG(HDMI_V_SYNC_GEN_1_0);
- DUMPREG(HDMI_V_SYNC_GEN_1_1);
- DUMPREG(HDMI_V_SYNC_GEN_1_2);
- DUMPREG(HDMI_V_SYNC_GEN_2_0);
- DUMPREG(HDMI_V_SYNC_GEN_2_1);
- DUMPREG(HDMI_V_SYNC_GEN_2_2);
- DUMPREG(HDMI_V_SYNC_GEN_3_0);
- DUMPREG(HDMI_V_SYNC_GEN_3_1);
- DUMPREG(HDMI_V_SYNC_GEN_3_2);
-
- dev_dbg(hdev->dev, "%s: ---- TG REGISTERS ----\n", prefix);
- DUMPREG(HDMI_TG_CMD);
- DUMPREG(HDMI_TG_H_FSZ_L);
- DUMPREG(HDMI_TG_H_FSZ_H);
- DUMPREG(HDMI_TG_HACT_ST_L);
- DUMPREG(HDMI_TG_HACT_ST_H);
- DUMPREG(HDMI_TG_HACT_SZ_L);
- DUMPREG(HDMI_TG_HACT_SZ_H);
- DUMPREG(HDMI_TG_V_FSZ_L);
- DUMPREG(HDMI_TG_V_FSZ_H);
- DUMPREG(HDMI_TG_VSYNC_L);
- DUMPREG(HDMI_TG_VSYNC_H);
- DUMPREG(HDMI_TG_VSYNC2_L);
- DUMPREG(HDMI_TG_VSYNC2_H);
- DUMPREG(HDMI_TG_VACT_ST_L);
- DUMPREG(HDMI_TG_VACT_ST_H);
- DUMPREG(HDMI_TG_VACT_SZ_L);
- DUMPREG(HDMI_TG_VACT_SZ_H);
- DUMPREG(HDMI_TG_FIELD_CHG_L);
- DUMPREG(HDMI_TG_FIELD_CHG_H);
- DUMPREG(HDMI_TG_VACT_ST2_L);
- DUMPREG(HDMI_TG_VACT_ST2_H);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
- DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
- DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
-#undef DUMPREG
-}
-
-static const struct hdmi_timings hdmi_timings_480p = {
- .hact = { .beg = 138, .end = 858 },
- .hsyn_pol = 1,
- .hsyn = { .beg = 16, .end = 16 + 62 },
- .interlaced = 0,
- .vact[0] = { .beg = 42 + 3, .end = 522 + 3 },
- .vsyn_pol = 1,
- .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3},
-};
-
-static const struct hdmi_timings hdmi_timings_576p50 = {
- .hact = { .beg = 144, .end = 864 },
- .hsyn_pol = 1,
- .hsyn = { .beg = 12, .end = 12 + 64 },
- .interlaced = 0,
- .vact[0] = { .beg = 44 + 5, .end = 620 + 5 },
- .vsyn_pol = 1,
- .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
-};
-
-static const struct hdmi_timings hdmi_timings_720p60 = {
- .hact = { .beg = 370, .end = 1650 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 110, .end = 110 + 40 },
- .interlaced = 0,
- .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
- .vsyn_pol = 0,
- .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
-};
-
-static const struct hdmi_timings hdmi_timings_720p50 = {
- .hact = { .beg = 700, .end = 1980 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 440, .end = 440 + 40 },
- .interlaced = 0,
- .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
- .vsyn_pol = 0,
- .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
-};
-
-static const struct hdmi_timings hdmi_timings_1080p24 = {
- .hact = { .beg = 830, .end = 2750 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 638, .end = 638 + 44 },
- .interlaced = 0,
- .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
- .vsyn_pol = 0,
- .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
-};
-
-static const struct hdmi_timings hdmi_timings_1080p60 = {
- .hact = { .beg = 280, .end = 2200 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 88, .end = 88 + 44 },
- .interlaced = 0,
- .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
- .vsyn_pol = 0,
- .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
-};
-
-static const struct hdmi_timings hdmi_timings_1080i60 = {
- .hact = { .beg = 280, .end = 2200 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 88, .end = 88 + 44 },
- .interlaced = 1,
- .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
- .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
- .vsyn_pol = 0,
- .vsyn_off = 1100,
- .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
- .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
-};
-
-static const struct hdmi_timings hdmi_timings_1080i50 = {
- .hact = { .beg = 720, .end = 2640 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 528, .end = 528 + 44 },
- .interlaced = 1,
- .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
- .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
- .vsyn_pol = 0,
- .vsyn_off = 1320,
- .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
- .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
-};
-
-static const struct hdmi_timings hdmi_timings_1080p50 = {
- .hact = { .beg = 720, .end = 2640 },
- .hsyn_pol = 0,
- .hsyn = { .beg = 528, .end = 528 + 44 },
- .interlaced = 0,
- .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
- .vsyn_pol = 0,
- .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
-};
-
-static const struct {
- u32 preset;
- const struct hdmi_timings *timings;
-} hdmi_timings[] = {
- { V4L2_DV_480P59_94, &hdmi_timings_480p },
- { V4L2_DV_576P50, &hdmi_timings_576p50 },
- { V4L2_DV_720P50, &hdmi_timings_720p50 },
- { V4L2_DV_720P59_94, &hdmi_timings_720p60 },
- { V4L2_DV_720P60, &hdmi_timings_720p60 },
- { V4L2_DV_1080P24, &hdmi_timings_1080p24 },
- { V4L2_DV_1080P30, &hdmi_timings_1080p60 },
- { V4L2_DV_1080P50, &hdmi_timings_1080p50 },
- { V4L2_DV_1080I50, &hdmi_timings_1080i50 },
- { V4L2_DV_1080I60, &hdmi_timings_1080i60 },
- { V4L2_DV_1080P60, &hdmi_timings_1080p60 },
-};
-
-static const struct hdmi_timings *hdmi_preset2timings(u32 preset)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i)
- if (hdmi_timings[i].preset == preset)
- return hdmi_timings[i].timings;
- return NULL;
-}
-
-static int hdmi_streamon(struct hdmi_device *hdev)
-{
- struct device *dev = hdev->dev;
- struct hdmi_resources *res = &hdev->res;
- int ret, tries;
-
- dev_dbg(dev, "%s\n", __func__);
-
- ret = hdmi_conf_apply(hdev);
- if (ret)
- return ret;
-
- ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
- if (ret)
- return ret;
-
- /* waiting for HDMIPHY's PLL to get to steady state */
- for (tries = 100; tries; --tries) {
- u32 val = hdmi_read(hdev, HDMI_PHY_STATUS);
- if (val & HDMI_PHY_STATUS_READY)
- break;
- mdelay(1);
- }
- /* steady state not achieved */
- if (tries == 0) {
- dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
- v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
- hdmi_dumpregs(hdev, "hdmiphy - s_stream");
- return -EIO;
- }
-
- /* starting MHL */
- ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
- if (hdev->mhl_sd && ret) {
- v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
- hdmi_dumpregs(hdev, "mhl - s_stream");
- return -EIO;
- }
-
- /* hdmiphy clock is used for HDMI in streaming mode */
- clk_disable(res->sclk_hdmi);
- clk_set_parent(res->sclk_hdmi, res->sclk_hdmiphy);
- clk_enable(res->sclk_hdmi);
-
- /* enable HDMI and timing generator */
- hdmi_write_mask(hdev, HDMI_CON_0, ~0, HDMI_EN);
- hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_EN);
- hdmi_dumpregs(hdev, "streamon");
- return 0;
-}
-
-static int hdmi_streamoff(struct hdmi_device *hdev)
-{
- struct device *dev = hdev->dev;
- struct hdmi_resources *res = &hdev->res;
-
- dev_dbg(dev, "%s\n", __func__);
-
- hdmi_write_mask(hdev, HDMI_CON_0, 0, HDMI_EN);
- hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_EN);
-
- /* pixel(vpll) clock is used for HDMI in config mode */
- clk_disable(res->sclk_hdmi);
- clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
- clk_enable(res->sclk_hdmi);
-
- v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
- v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
-
- hdmi_dumpregs(hdev, "streamoff");
- return 0;
-}
-
-static int hdmi_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- struct device *dev = hdev->dev;
-
- dev_dbg(dev, "%s(%d)\n", __func__, enable);
- if (enable)
- return hdmi_streamon(hdev);
- return hdmi_streamoff(hdev);
-}
-
-static void hdmi_resource_poweron(struct hdmi_resources *res)
-{
- /* turn HDMI power on */
- regulator_bulk_enable(res->regul_count, res->regul_bulk);
- /* power-on hdmi physical interface */
- clk_enable(res->hdmiphy);
- /* use VPP as parent clock; HDMIPHY is not working yet */
- clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
- /* turn clocks on */
- clk_enable(res->sclk_hdmi);
-}
-
-static void hdmi_resource_poweroff(struct hdmi_resources *res)
-{
- /* turn clocks off */
- clk_disable(res->sclk_hdmi);
- /* power-off hdmiphy */
- clk_disable(res->hdmiphy);
- /* turn HDMI power off */
- regulator_bulk_disable(res->regul_count, res->regul_bulk);
-}
-
-static int hdmi_s_power(struct v4l2_subdev *sd, int on)
-{
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- int ret;
-
- if (on)
- ret = pm_runtime_get_sync(hdev->dev);
- else
- ret = pm_runtime_put_sync(hdev->dev);
- /* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
-}
-
-static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *preset)
-{
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- struct device *dev = hdev->dev;
- const struct hdmi_timings *conf;
-
- conf = hdmi_preset2timings(preset->preset);
- if (conf == NULL) {
- dev_err(dev, "preset (%u) not supported\n", preset->preset);
- return -EINVAL;
- }
- hdev->cur_conf = conf;
- hdev->cur_conf_dirty = 1;
- hdev->cur_preset = preset->preset;
- return 0;
-}
-
-static int hdmi_g_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *preset)
-{
- memset(preset, 0, sizeof(*preset));
- preset->preset = sd_to_hdmi_dev(sd)->cur_preset;
- return 0;
-}
-
-static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *fmt)
-{
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- const struct hdmi_timings *t = hdev->cur_conf;
-
- dev_dbg(hdev->dev, "%s\n", __func__);
- if (!hdev->cur_conf)
- return -EINVAL;
- memset(fmt, 0, sizeof *fmt);
- fmt->width = t->hact.end - t->hact.beg;
- fmt->height = t->vact[0].end - t->vact[0].beg;
- fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- if (t->interlaced) {
- fmt->field = V4L2_FIELD_INTERLACED;
- fmt->height *= 2;
- } else {
- fmt->field = V4L2_FIELD_NONE;
- }
- return 0;
-}
-
-static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
- struct v4l2_dv_enum_preset *preset)
-{
- if (preset->index >= ARRAY_SIZE(hdmi_timings))
- return -EINVAL;
- return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset,
- preset);
-}
-
-static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
- .s_power = hdmi_s_power,
-};
-
-static const struct v4l2_subdev_video_ops hdmi_sd_video_ops = {
- .s_dv_preset = hdmi_s_dv_preset,
- .g_dv_preset = hdmi_g_dv_preset,
- .enum_dv_presets = hdmi_enum_dv_presets,
- .g_mbus_fmt = hdmi_g_mbus_fmt,
- .s_stream = hdmi_s_stream,
-};
-
-static const struct v4l2_subdev_ops hdmi_sd_ops = {
- .core = &hdmi_sd_core_ops,
- .video = &hdmi_sd_video_ops,
-};
-
-static int hdmi_runtime_suspend(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
-
- dev_dbg(dev, "%s\n", __func__);
- v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
- hdmi_resource_poweroff(&hdev->res);
- /* flag that device context is lost */
- hdev->cur_conf_dirty = 1;
- return 0;
-}
-
-static int hdmi_runtime_resume(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- int ret = 0;
-
- dev_dbg(dev, "%s\n", __func__);
-
- hdmi_resource_poweron(&hdev->res);
-
- /* starting MHL */
- ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
- if (hdev->mhl_sd && ret)
- goto fail;
-
- dev_dbg(dev, "poweron succeed\n");
-
- return 0;
-
-fail:
- hdmi_resource_poweroff(&hdev->res);
- dev_err(dev, "poweron failed\n");
-
- return ret;
-}
-
-static const struct dev_pm_ops hdmi_pm_ops = {
- .runtime_suspend = hdmi_runtime_suspend,
- .runtime_resume = hdmi_runtime_resume,
-};
-
-static void hdmi_resources_cleanup(struct hdmi_device *hdev)
-{
- struct hdmi_resources *res = &hdev->res;
-
- dev_dbg(hdev->dev, "HDMI resource cleanup\n");
- /* put clocks, power */
- if (res->regul_count)
- regulator_bulk_free(res->regul_count, res->regul_bulk);
- /* kfree is NULL-safe */
- kfree(res->regul_bulk);
- if (!IS_ERR_OR_NULL(res->hdmiphy))
- clk_put(res->hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_hdmiphy))
- clk_put(res->sclk_hdmiphy);
- if (!IS_ERR_OR_NULL(res->sclk_pixel))
- clk_put(res->sclk_pixel);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
- clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->hdmi))
- clk_put(res->hdmi);
- memset(res, 0, sizeof *res);
-}
-
-static int hdmi_resources_init(struct hdmi_device *hdev)
-{
- struct device *dev = hdev->dev;
- struct hdmi_resources *res = &hdev->res;
- static char *supply[] = {
- "hdmi-en",
- "vdd",
- "vdd_osc",
- "vdd_pll",
- };
- int i, ret;
-
- dev_dbg(dev, "HDMI resource init\n");
-
- memset(res, 0, sizeof *res);
- /* get clocks, power */
-
- res->hdmi = clk_get(dev, "hdmi");
- if (IS_ERR_OR_NULL(res->hdmi)) {
- dev_err(dev, "failed to get clock 'hdmi'\n");
- goto fail;
- }
- res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
- if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
- dev_err(dev, "failed to get clock 'sclk_hdmi'\n");
- goto fail;
- }
- res->sclk_pixel = clk_get(dev, "sclk_pixel");
- if (IS_ERR_OR_NULL(res->sclk_pixel)) {
- dev_err(dev, "failed to get clock 'sclk_pixel'\n");
- goto fail;
- }
- res->sclk_hdmiphy = clk_get(dev, "sclk_hdmiphy");
- if (IS_ERR_OR_NULL(res->sclk_hdmiphy)) {
- dev_err(dev, "failed to get clock 'sclk_hdmiphy'\n");
- goto fail;
- }
- res->hdmiphy = clk_get(dev, "hdmiphy");
- if (IS_ERR_OR_NULL(res->hdmiphy)) {
- dev_err(dev, "failed to get clock 'hdmiphy'\n");
- goto fail;
- }
- res->regul_bulk = kcalloc(ARRAY_SIZE(supply),
- sizeof(res->regul_bulk[0]), GFP_KERNEL);
- if (!res->regul_bulk) {
- dev_err(dev, "failed to get memory for regulators\n");
- goto fail;
- }
- for (i = 0; i < ARRAY_SIZE(supply); ++i) {
- res->regul_bulk[i].supply = supply[i];
- res->regul_bulk[i].consumer = NULL;
- }
-
- ret = regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
- if (ret) {
- dev_err(dev, "failed to get regulators\n");
- goto fail;
- }
- res->regul_count = ARRAY_SIZE(supply);
-
- return 0;
-fail:
- dev_err(dev, "HDMI resource init - failed\n");
- hdmi_resources_cleanup(hdev);
- return -ENODEV;
-}
-
-static int __devinit hdmi_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct resource *res;
- struct i2c_adapter *adapter;
- struct v4l2_subdev *sd;
- struct hdmi_device *hdmi_dev = NULL;
- struct s5p_hdmi_platform_data *pdata = dev->platform_data;
- int ret;
-
- dev_dbg(dev, "probe start\n");
-
- if (!pdata) {
- dev_err(dev, "platform data is missing\n");
- ret = -ENODEV;
- goto fail;
- }
-
- hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
- if (!hdmi_dev) {
- dev_err(dev, "out of memory\n");
- ret = -ENOMEM;
- goto fail;
- }
-
- hdmi_dev->dev = dev;
-
- ret = hdmi_resources_init(hdmi_dev);
- if (ret)
- goto fail;
-
- /* mapping HDMI registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail_init;
- }
-
- hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (hdmi_dev->regs == NULL) {
- dev_err(dev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail_init;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(dev, "get interrupt resource failed.\n");
- ret = -ENXIO;
- goto fail_init;
- }
-
- ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
- "hdmi", hdmi_dev);
- if (ret) {
- dev_err(dev, "request interrupt failed.\n");
- goto fail_init;
- }
- hdmi_dev->irq = res->start;
-
- /* setting v4l2 name to prevent WARN_ON in v4l2_device_register */
- strlcpy(hdmi_dev->v4l2_dev.name, dev_name(dev),
- sizeof(hdmi_dev->v4l2_dev.name));
- /* passing NULL owner prevents driver from erasing drvdata */
- ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
- if (ret) {
- dev_err(dev, "could not register v4l2 device.\n");
- goto fail_init;
- }
-
- /* testing if hdmiphy info is present */
- if (!pdata->hdmiphy_info) {
- dev_err(dev, "hdmiphy info is missing in platform data\n");
- ret = -ENXIO;
- goto fail_vdev;
- }
-
- adapter = i2c_get_adapter(pdata->hdmiphy_bus);
- if (adapter == NULL) {
- dev_err(dev, "hdmiphy adapter request failed\n");
- ret = -ENXIO;
- goto fail_vdev;
- }
-
- hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
- adapter, pdata->hdmiphy_info, NULL);
- /* on failure or not adapter is no longer useful */
- i2c_put_adapter(adapter);
- if (hdmi_dev->phy_sd == NULL) {
- dev_err(dev, "missing subdev for hdmiphy\n");
- ret = -ENODEV;
- goto fail_vdev;
- }
-
- /* initialization of MHL interface if present */
- if (pdata->mhl_info) {
- adapter = i2c_get_adapter(pdata->mhl_bus);
- if (adapter == NULL) {
- dev_err(dev, "MHL adapter request failed\n");
- ret = -ENXIO;
- goto fail_vdev;
- }
-
- hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
- &hdmi_dev->v4l2_dev, adapter,
- pdata->mhl_info, NULL);
- /* on failure or not adapter is no longer useful */
- i2c_put_adapter(adapter);
- if (hdmi_dev->mhl_sd == NULL) {
- dev_err(dev, "missing subdev for MHL\n");
- ret = -ENODEV;
- goto fail_vdev;
- }
- }
-
- clk_enable(hdmi_dev->res.hdmi);
-
- pm_runtime_enable(dev);
-
- sd = &hdmi_dev->sd;
- v4l2_subdev_init(sd, &hdmi_sd_ops);
- sd->owner = THIS_MODULE;
-
- strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
- hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
- /* FIXME: missing fail preset is not supported */
- hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset);
- hdmi_dev->cur_conf_dirty = 1;
-
- /* storing subdev for call that have only access to struct device */
- dev_set_drvdata(dev, sd);
-
- dev_info(dev, "probe successful\n");
-
- return 0;
-
-fail_vdev:
- v4l2_device_unregister(&hdmi_dev->v4l2_dev);
-
-fail_init:
- hdmi_resources_cleanup(hdmi_dev);
-
-fail:
- dev_err(dev, "probe failed\n");
- return ret;
-}
-
-static int __devexit hdmi_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct hdmi_device *hdmi_dev = sd_to_hdmi_dev(sd);
-
- pm_runtime_disable(dev);
- clk_disable(hdmi_dev->res.hdmi);
- v4l2_device_unregister(&hdmi_dev->v4l2_dev);
- disable_irq(hdmi_dev->irq);
- hdmi_resources_cleanup(hdmi_dev);
- dev_info(dev, "remove successful\n");
-
- return 0;
-}
-
-static struct platform_driver hdmi_driver __refdata = {
- .probe = hdmi_probe,
- .remove = __devexit_p(hdmi_remove),
- .id_table = hdmi_driver_types,
- .driver = {
- .name = "s5p-hdmi",
- .owner = THIS_MODULE,
- .pm = &hdmi_pm_ops,
- }
-};
-
-module_platform_driver(hdmi_driver);
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
deleted file mode 100644
index edca06592883..000000000000
--- a/drivers/media/video/s5p-tv/mixer_drv.c
+++ /dev/null
@@ -1,487 +0,0 @@
-/*
- * Samsung TV Mixer driver
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *
- * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
- * or (at your option) any later version
- */
-
-#include "mixer.h"
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/fb.h>
-#include <linux/delay.h>
-#include <linux/pm_runtime.h>
-#include <linux/clk.h>
-
-MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
-MODULE_DESCRIPTION("Samsung MIXER");
-MODULE_LICENSE("GPL");
-
-/* --------- DRIVER PARAMETERS ---------- */
-
-static struct mxr_output_conf mxr_output_conf[] = {
- {
- .output_name = "S5P HDMI connector",
- .module_name = "s5p-hdmi",
- .cookie = 1,
- },
- {
- .output_name = "S5P SDO connector",
- .module_name = "s5p-sdo",
- .cookie = 0,
- },
-};
-
-void mxr_get_mbus_fmt(struct mxr_device *mdev,
- struct v4l2_mbus_framefmt *mbus_fmt)
-{
- struct v4l2_subdev *sd;
- int ret;
-
- mutex_lock(&mdev->mutex);
- sd = to_outsd(mdev);
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, mbus_fmt);
- WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
- mutex_unlock(&mdev->mutex);
-}
-
-void mxr_streamer_get(struct mxr_device *mdev)
-{
- mutex_lock(&mdev->mutex);
- ++mdev->n_streamer;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
- if (mdev->n_streamer == 1) {
- struct v4l2_subdev *sd = to_outsd(mdev);
- struct v4l2_mbus_framefmt mbus_fmt;
- struct mxr_resources *res = &mdev->res;
- int ret;
-
- if (to_output(mdev)->cookie == 0)
- clk_set_parent(res->sclk_mixer, res->sclk_dac);
- else
- clk_set_parent(res->sclk_mixer, res->sclk_hdmi);
- mxr_reg_s_output(mdev, to_output(mdev)->cookie);
-
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mbus_fmt);
- WARN(ret, "failed to get mbus_fmt for output %s\n", sd->name);
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
- WARN(ret, "starting stream failed for output %s\n", sd->name);
-
- mxr_reg_set_mbus_fmt(mdev, &mbus_fmt);
- mxr_reg_streamon(mdev);
- ret = mxr_reg_wait4vsync(mdev);
- WARN(ret, "failed to get vsync (%d) from output\n", ret);
- }
- mutex_unlock(&mdev->mutex);
- mxr_reg_dump(mdev);
- /* FIXME: what to do when streaming fails? */
-}
-
-void mxr_streamer_put(struct mxr_device *mdev)
-{
- mutex_lock(&mdev->mutex);
- --mdev->n_streamer;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_streamer);
- if (mdev->n_streamer == 0) {
- int ret;
- struct v4l2_subdev *sd = to_outsd(mdev);
-
- mxr_reg_streamoff(mdev);
- /* vsync applies Mixer setup */
- ret = mxr_reg_wait4vsync(mdev);
- WARN(ret, "failed to get vsync (%d) from output\n", ret);
- ret = v4l2_subdev_call(sd, video, s_stream, 0);
- WARN(ret, "stopping stream failed for output %s\n", sd->name);
- }
- WARN(mdev->n_streamer < 0, "negative number of streamers (%d)\n",
- mdev->n_streamer);
- mutex_unlock(&mdev->mutex);
- mxr_reg_dump(mdev);
-}
-
-void mxr_output_get(struct mxr_device *mdev)
-{
- mutex_lock(&mdev->mutex);
- ++mdev->n_output;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
- /* turn on auxiliary driver */
- if (mdev->n_output == 1)
- v4l2_subdev_call(to_outsd(mdev), core, s_power, 1);
- mutex_unlock(&mdev->mutex);
-}
-
-void mxr_output_put(struct mxr_device *mdev)
-{
- mutex_lock(&mdev->mutex);
- --mdev->n_output;
- mxr_dbg(mdev, "%s(%d)\n", __func__, mdev->n_output);
- /* turn on auxiliary driver */
- if (mdev->n_output == 0)
- v4l2_subdev_call(to_outsd(mdev), core, s_power, 0);
- WARN(mdev->n_output < 0, "negative number of output users (%d)\n",
- mdev->n_output);
- mutex_unlock(&mdev->mutex);
-}
-
-int mxr_power_get(struct mxr_device *mdev)
-{
- int ret = pm_runtime_get_sync(mdev->dev);
-
- /* returning 1 means that power is already enabled,
- * so zero success be returned */
- if (IS_ERR_VALUE(ret))
- return ret;
- return 0;
-}
-
-void mxr_power_put(struct mxr_device *mdev)
-{
- pm_runtime_put_sync(mdev->dev);
-}
-
-/* --------- RESOURCE MANAGEMENT -------------*/
-
-static int __devinit mxr_acquire_plat_resources(struct mxr_device *mdev,
- struct platform_device *pdev)
-{
- struct resource *res;
- int ret;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mxr");
- if (res == NULL) {
- mxr_err(mdev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail;
- }
-
- mdev->res.mxr_regs = ioremap(res->start, resource_size(res));
- if (mdev->res.mxr_regs == NULL) {
- mxr_err(mdev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vp");
- if (res == NULL) {
- mxr_err(mdev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail_mxr_regs;
- }
-
- mdev->res.vp_regs = ioremap(res->start, resource_size(res));
- if (mdev->res.vp_regs == NULL) {
- mxr_err(mdev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail_mxr_regs;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "irq");
- if (res == NULL) {
- mxr_err(mdev, "get interrupt resource failed.\n");
- ret = -ENXIO;
- goto fail_vp_regs;
- }
-
- ret = request_irq(res->start, mxr_irq_handler, 0, "s5p-mixer", mdev);
- if (ret) {
- mxr_err(mdev, "request interrupt failed.\n");
- goto fail_vp_regs;
- }
- mdev->res.irq = res->start;
-
- return 0;
-
-fail_vp_regs:
- iounmap(mdev->res.vp_regs);
-
-fail_mxr_regs:
- iounmap(mdev->res.mxr_regs);
-
-fail:
- return ret;
-}
-
-static void mxr_release_plat_resources(struct mxr_device *mdev)
-{
- free_irq(mdev->res.irq, mdev);
- iounmap(mdev->res.vp_regs);
- iounmap(mdev->res.mxr_regs);
-}
-
-static void mxr_release_clocks(struct mxr_device *mdev)
-{
- struct mxr_resources *res = &mdev->res;
-
- if (!IS_ERR_OR_NULL(res->sclk_dac))
- clk_put(res->sclk_dac);
- if (!IS_ERR_OR_NULL(res->sclk_hdmi))
- clk_put(res->sclk_hdmi);
- if (!IS_ERR_OR_NULL(res->sclk_mixer))
- clk_put(res->sclk_mixer);
- if (!IS_ERR_OR_NULL(res->vp))
- clk_put(res->vp);
- if (!IS_ERR_OR_NULL(res->mixer))
- clk_put(res->mixer);
-}
-
-static int mxr_acquire_clocks(struct mxr_device *mdev)
-{
- struct mxr_resources *res = &mdev->res;
- struct device *dev = mdev->dev;
-
- res->mixer = clk_get(dev, "mixer");
- if (IS_ERR_OR_NULL(res->mixer)) {
- mxr_err(mdev, "failed to get clock 'mixer'\n");
- goto fail;
- }
- res->vp = clk_get(dev, "vp");
- if (IS_ERR_OR_NULL(res->vp)) {
- mxr_err(mdev, "failed to get clock 'vp'\n");
- goto fail;
- }
- res->sclk_mixer = clk_get(dev, "sclk_mixer");
- if (IS_ERR_OR_NULL(res->sclk_mixer)) {
- mxr_err(mdev, "failed to get clock 'sclk_mixer'\n");
- goto fail;
- }
- res->sclk_hdmi = clk_get(dev, "sclk_hdmi");
- if (IS_ERR_OR_NULL(res->sclk_hdmi)) {
- mxr_err(mdev, "failed to get clock 'sclk_hdmi'\n");
- goto fail;
- }
- res->sclk_dac = clk_get(dev, "sclk_dac");
- if (IS_ERR_OR_NULL(res->sclk_dac)) {
- mxr_err(mdev, "failed to get clock 'sclk_dac'\n");
- goto fail;
- }
-
- return 0;
-fail:
- mxr_release_clocks(mdev);
- return -ENODEV;
-}
-
-static int __devinit mxr_acquire_resources(struct mxr_device *mdev,
- struct platform_device *pdev)
-{
- int ret;
- ret = mxr_acquire_plat_resources(mdev, pdev);
-
- if (ret)
- goto fail;
-
- ret = mxr_acquire_clocks(mdev);
- if (ret)
- goto fail_plat;
-
- mxr_info(mdev, "resources acquired\n");
- return 0;
-
-fail_plat:
- mxr_release_plat_resources(mdev);
-fail:
- mxr_err(mdev, "resources acquire failed\n");
- return ret;
-}
-
-static void mxr_release_resources(struct mxr_device *mdev)
-{
- mxr_release_clocks(mdev);
- mxr_release_plat_resources(mdev);
- memset(&mdev->res, 0, sizeof mdev->res);
-}
-
-static void mxr_release_layers(struct mxr_device *mdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mdev->layer); ++i)
- if (mdev->layer[i])
- mxr_layer_release(mdev->layer[i]);
-}
-
-static int __devinit mxr_acquire_layers(struct mxr_device *mdev,
- struct mxr_platform_data *pdata)
-{
- mdev->layer[0] = mxr_graph_layer_create(mdev, 0);
- mdev->layer[1] = mxr_graph_layer_create(mdev, 1);
- mdev->layer[2] = mxr_vp_layer_create(mdev, 0);
-
- if (!mdev->layer[0] || !mdev->layer[1] || !mdev->layer[2]) {
- mxr_err(mdev, "failed to acquire layers\n");
- goto fail;
- }
-
- return 0;
-
-fail:
- mxr_release_layers(mdev);
- return -ENODEV;
-}
-
-/* ---------- POWER MANAGEMENT ----------- */
-
-static int mxr_runtime_resume(struct device *dev)
-{
- struct mxr_device *mdev = to_mdev(dev);
- struct mxr_resources *res = &mdev->res;
-
- mxr_dbg(mdev, "resume - start\n");
- mutex_lock(&mdev->mutex);
- /* turn clocks on */
- clk_enable(res->mixer);
- clk_enable(res->vp);
- clk_enable(res->sclk_mixer);
- /* apply default configuration */
- mxr_reg_reset(mdev);
- mxr_dbg(mdev, "resume - finished\n");
-
- mutex_unlock(&mdev->mutex);
- return 0;
-}
-
-static int mxr_runtime_suspend(struct device *dev)
-{
- struct mxr_device *mdev = to_mdev(dev);
- struct mxr_resources *res = &mdev->res;
- mxr_dbg(mdev, "suspend - start\n");
- mutex_lock(&mdev->mutex);
- /* turn clocks off */
- clk_disable(res->sclk_mixer);
- clk_disable(res->vp);
- clk_disable(res->mixer);
- mutex_unlock(&mdev->mutex);
- mxr_dbg(mdev, "suspend - finished\n");
- return 0;
-}
-
-static const struct dev_pm_ops mxr_pm_ops = {
- .runtime_suspend = mxr_runtime_suspend,
- .runtime_resume = mxr_runtime_resume,
-};
-
-/* --------- DRIVER INITIALIZATION ---------- */
-
-static int __devinit mxr_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct mxr_platform_data *pdata = dev->platform_data;
- struct mxr_device *mdev;
- int ret;
-
- /* mdev does not exist yet so no mxr_dbg is used */
- dev_info(dev, "probe start\n");
-
- mdev = kzalloc(sizeof *mdev, GFP_KERNEL);
- if (!mdev) {
- mxr_err(mdev, "not enough memory.\n");
- ret = -ENOMEM;
- goto fail;
- }
-
- /* setup pointer to master device */
- mdev->dev = dev;
-
- mutex_init(&mdev->mutex);
- spin_lock_init(&mdev->reg_slock);
- init_waitqueue_head(&mdev->event_queue);
-
- /* acquire resources: regs, irqs, clocks, regulators */
- ret = mxr_acquire_resources(mdev, pdev);
- if (ret)
- goto fail_mem;
-
- /* configure resources for video output */
- ret = mxr_acquire_video(mdev, mxr_output_conf,
- ARRAY_SIZE(mxr_output_conf));
- if (ret)
- goto fail_resources;
-
- /* configure layers */
- ret = mxr_acquire_layers(mdev, pdata);
- if (ret)
- goto fail_video;
-
- pm_runtime_enable(dev);
-
- mxr_info(mdev, "probe successful\n");
- return 0;
-
-fail_video:
- mxr_release_video(mdev);
-
-fail_resources:
- mxr_release_resources(mdev);
-
-fail_mem:
- kfree(mdev);
-
-fail:
- dev_info(dev, "probe failed\n");
- return ret;
-}
-
-static int __devexit mxr_remove(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct mxr_device *mdev = to_mdev(dev);
-
- pm_runtime_disable(dev);
-
- mxr_release_layers(mdev);
- mxr_release_video(mdev);
- mxr_release_resources(mdev);
-
- kfree(mdev);
-
- dev_info(dev, "remove successful\n");
- return 0;
-}
-
-static struct platform_driver mxr_driver __refdata = {
- .probe = mxr_probe,
- .remove = __devexit_p(mxr_remove),
- .driver = {
- .name = MXR_DRIVER_NAME,
- .owner = THIS_MODULE,
- .pm = &mxr_pm_ops,
- }
-};
-
-static int __init mxr_init(void)
-{
- int i, ret;
- static const char banner[] __initconst = KERN_INFO
- "Samsung TV Mixer driver, "
- "(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
- printk(banner);
-
- /* Loading auxiliary modules */
- for (i = 0; i < ARRAY_SIZE(mxr_output_conf); ++i)
- request_module(mxr_output_conf[i].module_name);
-
- ret = platform_driver_register(&mxr_driver);
- if (ret != 0) {
- printk(KERN_ERR "registration of MIXER driver failed\n");
- return -ENXIO;
- }
-
- return 0;
-}
-module_init(mxr_init);
-
-static void __exit mxr_exit(void)
-{
- platform_driver_unregister(&mxr_driver);
-}
-module_exit(mxr_exit);
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
deleted file mode 100644
index 6c74b05d1f95..000000000000
--- a/drivers/media/video/s5p-tv/mixer_video.c
+++ /dev/null
@@ -1,1112 +0,0 @@
-/*
- * Samsung TV Mixer driver
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *
- * Tomasz Stanislawski, <t.stanislaws@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
- */
-
-#include "mixer.h"
-
-#include <media/v4l2-ioctl.h>
-#include <linux/videodev2.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/timer.h>
-#include <media/videobuf2-dma-contig.h>
-
-static int find_reg_callback(struct device *dev, void *p)
-{
- struct v4l2_subdev **sd = p;
-
- *sd = dev_get_drvdata(dev);
- /* non-zero value stops iteration */
- return 1;
-}
-
-static struct v4l2_subdev *find_and_register_subdev(
- struct mxr_device *mdev, char *module_name)
-{
- struct device_driver *drv;
- struct v4l2_subdev *sd = NULL;
- int ret;
-
- /* TODO: add waiting until probe is finished */
- drv = driver_find(module_name, &platform_bus_type);
- if (!drv) {
- mxr_warn(mdev, "module %s is missing\n", module_name);
- return NULL;
- }
- /* driver refcnt is increased, it is safe to iterate over devices */
- ret = driver_for_each_device(drv, NULL, &sd, find_reg_callback);
- /* ret == 0 means that find_reg_callback was never executed */
- if (sd == NULL) {
- mxr_warn(mdev, "module %s provides no subdev!\n", module_name);
- goto done;
- }
- /* v4l2_device_register_subdev detects if sd is NULL */
- ret = v4l2_device_register_subdev(&mdev->v4l2_dev, sd);
- if (ret) {
- mxr_warn(mdev, "failed to register subdev %s\n", sd->name);
- sd = NULL;
- }
-
-done:
- return sd;
-}
-
-int __devinit mxr_acquire_video(struct mxr_device *mdev,
- struct mxr_output_conf *output_conf, int output_count)
-{
- struct device *dev = mdev->dev;
- struct v4l2_device *v4l2_dev = &mdev->v4l2_dev;
- int i;
- int ret = 0;
- struct v4l2_subdev *sd;
-
- strlcpy(v4l2_dev->name, dev_name(mdev->dev), sizeof(v4l2_dev->name));
- /* prepare context for V4L2 device */
- ret = v4l2_device_register(dev, v4l2_dev);
- if (ret) {
- mxr_err(mdev, "could not register v4l2 device.\n");
- goto fail;
- }
-
- mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev);
- if (IS_ERR_OR_NULL(mdev->alloc_ctx)) {
- mxr_err(mdev, "could not acquire vb2 allocator\n");
- goto fail_v4l2_dev;
- }
-
- /* registering outputs */
- mdev->output_cnt = 0;
- for (i = 0; i < output_count; ++i) {
- struct mxr_output_conf *conf = &output_conf[i];
- struct mxr_output *out;
-
- sd = find_and_register_subdev(mdev, conf->module_name);
- /* trying to register next output */
- if (sd == NULL)
- continue;
- out = kzalloc(sizeof *out, GFP_KERNEL);
- if (out == NULL) {
- mxr_err(mdev, "no memory for '%s'\n",
- conf->output_name);
- ret = -ENOMEM;
- /* registered subdevs are removed in fail_v4l2_dev */
- goto fail_output;
- }
- strlcpy(out->name, conf->output_name, sizeof(out->name));
- out->sd = sd;
- out->cookie = conf->cookie;
- mdev->output[mdev->output_cnt++] = out;
- mxr_info(mdev, "added output '%s' from module '%s'\n",
- conf->output_name, conf->module_name);
- /* checking if maximal number of outputs is reached */
- if (mdev->output_cnt >= MXR_MAX_OUTPUTS)
- break;
- }
-
- if (mdev->output_cnt == 0) {
- mxr_err(mdev, "failed to register any output\n");
- ret = -ENODEV;
- /* skipping fail_output because there is nothing to free */
- goto fail_vb2_allocator;
- }
-
- return 0;
-
-fail_output:
- /* kfree is NULL-safe */
- for (i = 0; i < mdev->output_cnt; ++i)
- kfree(mdev->output[i]);
- memset(mdev->output, 0, sizeof mdev->output);
-
-fail_vb2_allocator:
- /* freeing allocator context */
- vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
-
-fail_v4l2_dev:
- /* NOTE: automatically unregister all subdevs */
- v4l2_device_unregister(v4l2_dev);
-
-fail:
- return ret;
-}
-
-void mxr_release_video(struct mxr_device *mdev)
-{
- int i;
-
- /* kfree is NULL-safe */
- for (i = 0; i < mdev->output_cnt; ++i)
- kfree(mdev->output[i]);
-
- vb2_dma_contig_cleanup_ctx(mdev->alloc_ctx);
- v4l2_device_unregister(&mdev->v4l2_dev);
-}
-
-static int mxr_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
-
- strlcpy(cap->driver, MXR_DRIVER_NAME, sizeof cap->driver);
- strlcpy(cap->card, layer->vfd.name, sizeof cap->card);
- sprintf(cap->bus_info, "%d", layer->idx);
- cap->version = KERNEL_VERSION(0, 1, 0);
- cap->capabilities = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
- return 0;
-}
-
-static void mxr_geometry_dump(struct mxr_device *mdev, struct mxr_geometry *geo)
-{
- mxr_dbg(mdev, "src.full_size = (%u, %u)\n",
- geo->src.full_width, geo->src.full_height);
- mxr_dbg(mdev, "src.size = (%u, %u)\n",
- geo->src.width, geo->src.height);
- mxr_dbg(mdev, "src.offset = (%u, %u)\n",
- geo->src.x_offset, geo->src.y_offset);
- mxr_dbg(mdev, "dst.full_size = (%u, %u)\n",
- geo->dst.full_width, geo->dst.full_height);
- mxr_dbg(mdev, "dst.size = (%u, %u)\n",
- geo->dst.width, geo->dst.height);
- mxr_dbg(mdev, "dst.offset = (%u, %u)\n",
- geo->dst.x_offset, geo->dst.y_offset);
- mxr_dbg(mdev, "ratio = (%u, %u)\n",
- geo->x_ratio, geo->y_ratio);
-}
-
-static void mxr_layer_default_geo(struct mxr_layer *layer)
-{
- struct mxr_device *mdev = layer->mdev;
- struct v4l2_mbus_framefmt mbus_fmt;
-
- memset(&layer->geo, 0, sizeof layer->geo);
-
- mxr_get_mbus_fmt(mdev, &mbus_fmt);
-
- layer->geo.dst.full_width = mbus_fmt.width;
- layer->geo.dst.full_height = mbus_fmt.height;
- layer->geo.dst.width = layer->geo.dst.full_width;
- layer->geo.dst.height = layer->geo.dst.full_height;
- layer->geo.dst.field = mbus_fmt.field;
-
- layer->geo.src.full_width = mbus_fmt.width;
- layer->geo.src.full_height = mbus_fmt.height;
- layer->geo.src.width = layer->geo.src.full_width;
- layer->geo.src.height = layer->geo.src.full_height;
-
- mxr_geometry_dump(mdev, &layer->geo);
- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
- mxr_geometry_dump(mdev, &layer->geo);
-}
-
-static void mxr_layer_update_output(struct mxr_layer *layer)
-{
- struct mxr_device *mdev = layer->mdev;
- struct v4l2_mbus_framefmt mbus_fmt;
-
- mxr_get_mbus_fmt(mdev, &mbus_fmt);
- /* checking if update is needed */
- if (layer->geo.dst.full_width == mbus_fmt.width &&
- layer->geo.dst.full_height == mbus_fmt.width)
- return;
-
- layer->geo.dst.full_width = mbus_fmt.width;
- layer->geo.dst.full_height = mbus_fmt.height;
- layer->geo.dst.field = mbus_fmt.field;
- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SINK, 0);
-
- mxr_geometry_dump(mdev, &layer->geo);
-}
-
-static const struct mxr_format *find_format_by_fourcc(
- struct mxr_layer *layer, unsigned long fourcc);
-static const struct mxr_format *find_format_by_index(
- struct mxr_layer *layer, unsigned long index);
-
-static int mxr_enum_fmt(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- const struct mxr_format *fmt;
-
- mxr_dbg(mdev, "%s\n", __func__);
- fmt = find_format_by_index(layer, f->index);
- if (fmt == NULL)
- return -EINVAL;
-
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
-
- return 0;
-}
-
-static unsigned int divup(unsigned int divident, unsigned int divisor)
-{
- return (divident + divisor - 1) / divisor;
-}
-
-unsigned long mxr_get_plane_size(const struct mxr_block *blk,
- unsigned int width, unsigned int height)
-{
- unsigned int bl_width = divup(width, blk->width);
- unsigned int bl_height = divup(height, blk->height);
-
- return bl_width * bl_height * blk->size;
-}
-
-static void mxr_mplane_fill(struct v4l2_plane_pix_format *planes,
- const struct mxr_format *fmt, u32 width, u32 height)
-{
- int i;
-
- /* checking if nothing to fill */
- if (!planes)
- return;
-
- memset(planes, 0, sizeof(*planes) * fmt->num_subframes);
- for (i = 0; i < fmt->num_planes; ++i) {
- struct v4l2_plane_pix_format *plane = planes
- + fmt->plane2subframe[i];
- const struct mxr_block *blk = &fmt->plane[i];
- u32 bl_width = divup(width, blk->width);
- u32 bl_height = divup(height, blk->height);
- u32 sizeimage = bl_width * bl_height * blk->size;
- u16 bytesperline = bl_width * blk->size / blk->height;
-
- plane->sizeimage += sizeimage;
- plane->bytesperline = max(plane->bytesperline, bytesperline);
- }
-}
-
-static int mxr_g_fmt(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
-
- pix->width = layer->geo.src.full_width;
- pix->height = layer->geo.src.full_height;
- pix->field = V4L2_FIELD_NONE;
- pix->pixelformat = layer->fmt->fourcc;
- pix->colorspace = layer->fmt->colorspace;
- mxr_mplane_fill(pix->plane_fmt, layer->fmt, pix->width, pix->height);
-
- return 0;
-}
-
-static int mxr_s_fmt(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct mxr_layer *layer = video_drvdata(file);
- const struct mxr_format *fmt;
- struct v4l2_pix_format_mplane *pix;
- struct mxr_device *mdev = layer->mdev;
- struct mxr_geometry *geo = &layer->geo;
-
- mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
-
- pix = &f->fmt.pix_mp;
- fmt = find_format_by_fourcc(layer, pix->pixelformat);
- if (fmt == NULL) {
- mxr_warn(mdev, "not recognized fourcc: %08x\n",
- pix->pixelformat);
- return -EINVAL;
- }
- layer->fmt = fmt;
- /* set source size to highest accepted value */
- geo->src.full_width = max(geo->dst.full_width, pix->width);
- geo->src.full_height = max(geo->dst.full_height, pix->height);
- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
- mxr_geometry_dump(mdev, &layer->geo);
- /* set cropping to total visible screen */
- geo->src.width = pix->width;
- geo->src.height = pix->height;
- geo->src.x_offset = 0;
- geo->src.y_offset = 0;
- /* assure consistency of geometry */
- layer->ops.fix_geometry(layer, MXR_GEOMETRY_CROP, MXR_NO_OFFSET);
- mxr_geometry_dump(mdev, &layer->geo);
- /* set full size to lowest possible value */
- geo->src.full_width = 0;
- geo->src.full_height = 0;
- layer->ops.fix_geometry(layer, MXR_GEOMETRY_SOURCE, 0);
- mxr_geometry_dump(mdev, &layer->geo);
-
- /* returning results */
- mxr_g_fmt(file, priv, f);
-
- return 0;
-}
-
-static int mxr_g_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_geometry *geo = &layer->geo;
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return -EINVAL;
-
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- s->r.left = geo->src.x_offset;
- s->r.top = geo->src.y_offset;
- s->r.width = geo->src.width;
- s->r.height = geo->src.height;
- break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = geo->src.full_width;
- s->r.height = geo->src.full_height;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- case V4L2_SEL_TGT_COMPOSE_PADDED:
- s->r.left = geo->dst.x_offset;
- s->r.top = geo->dst.y_offset;
- s->r.width = geo->dst.width;
- s->r.height = geo->dst.height;
- break;
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = geo->dst.full_width;
- s->r.height = geo->dst.full_height;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* returns 1 if rectangle 'a' is inside 'b' */
-static int mxr_is_rect_inside(struct v4l2_rect *a, struct v4l2_rect *b)
-{
- if (a->left < b->left)
- return 0;
- if (a->top < b->top)
- return 0;
- if (a->left + a->width > b->left + b->width)
- return 0;
- if (a->top + a->height > b->top + b->height)
- return 0;
- return 1;
-}
-
-static int mxr_s_selection(struct file *file, void *fh,
- struct v4l2_selection *s)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_geometry *geo = &layer->geo;
- struct mxr_crop *target = NULL;
- enum mxr_geometry_stage stage;
- struct mxr_geometry tmp;
- struct v4l2_rect res;
-
- memset(&res, 0, sizeof res);
-
- mxr_dbg(layer->mdev, "%s: rect: %dx%d@%d,%d\n", __func__,
- s->r.width, s->r.height, s->r.left, s->r.top);
-
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return -EINVAL;
-
- switch (s->target) {
- /* ignore read-only targets */
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- res.width = geo->src.full_width;
- res.height = geo->src.full_height;
- break;
-
- /* ignore read-only targets */
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- res.width = geo->dst.full_width;
- res.height = geo->dst.full_height;
- break;
-
- case V4L2_SEL_TGT_CROP:
- target = &geo->src;
- stage = MXR_GEOMETRY_CROP;
- break;
- case V4L2_SEL_TGT_COMPOSE:
- case V4L2_SEL_TGT_COMPOSE_PADDED:
- target = &geo->dst;
- stage = MXR_GEOMETRY_COMPOSE;
- break;
- default:
- return -EINVAL;
- }
- /* apply change and update geometry if needed */
- if (target) {
- /* backup current geometry if setup fails */
- memcpy(&tmp, geo, sizeof tmp);
-
- /* apply requested selection */
- target->x_offset = s->r.left;
- target->y_offset = s->r.top;
- target->width = s->r.width;
- target->height = s->r.height;
-
- layer->ops.fix_geometry(layer, stage, s->flags);
-
- /* retrieve update selection rectangle */
- res.left = target->x_offset;
- res.top = target->y_offset;
- res.width = target->width;
- res.height = target->height;
-
- mxr_geometry_dump(layer->mdev, &layer->geo);
- }
-
- /* checking if the rectangle satisfies constraints */
- if ((s->flags & V4L2_SEL_FLAG_LE) && !mxr_is_rect_inside(&res, &s->r))
- goto fail;
- if ((s->flags & V4L2_SEL_FLAG_GE) && !mxr_is_rect_inside(&s->r, &res))
- goto fail;
-
- /* return result rectangle */
- s->r = res;
-
- return 0;
-fail:
- /* restore old geometry, which is not touched if target is NULL */
- if (target)
- memcpy(geo, &tmp, sizeof tmp);
- return -ERANGE;
-}
-
-static int mxr_enum_dv_presets(struct file *file, void *fh,
- struct v4l2_dv_enum_preset *preset)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- /* lock protects from changing sd_out */
- mutex_lock(&mdev->mutex);
- ret = v4l2_subdev_call(to_outsd(mdev), video, enum_dv_presets, preset);
- mutex_unlock(&mdev->mutex);
-
- return ret ? -EINVAL : 0;
-}
-
-static int mxr_s_dv_preset(struct file *file, void *fh,
- struct v4l2_dv_preset *preset)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- /* lock protects from changing sd_out */
- mutex_lock(&mdev->mutex);
-
- /* preset change cannot be done while there is an entity
- * dependant on output configuration
- */
- if (mdev->n_output > 0) {
- mutex_unlock(&mdev->mutex);
- return -EBUSY;
- }
-
- ret = v4l2_subdev_call(to_outsd(mdev), video, s_dv_preset, preset);
-
- mutex_unlock(&mdev->mutex);
-
- mxr_layer_update_output(layer);
-
- /* any failure should return EINVAL according to V4L2 doc */
- return ret ? -EINVAL : 0;
-}
-
-static int mxr_g_dv_preset(struct file *file, void *fh,
- struct v4l2_dv_preset *preset)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- /* lock protects from changing sd_out */
- mutex_lock(&mdev->mutex);
- ret = v4l2_subdev_call(to_outsd(mdev), video, g_dv_preset, preset);
- mutex_unlock(&mdev->mutex);
-
- return ret ? -EINVAL : 0;
-}
-
-static int mxr_s_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- /* lock protects from changing sd_out */
- mutex_lock(&mdev->mutex);
-
- /* standard change cannot be done while there is an entity
- * dependant on output configuration
- */
- if (mdev->n_output > 0) {
- mutex_unlock(&mdev->mutex);
- return -EBUSY;
- }
-
- ret = v4l2_subdev_call(to_outsd(mdev), video, s_std_output, *norm);
-
- mutex_unlock(&mdev->mutex);
-
- mxr_layer_update_output(layer);
-
- return ret ? -EINVAL : 0;
-}
-
-static int mxr_g_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- /* lock protects from changing sd_out */
- mutex_lock(&mdev->mutex);
- ret = v4l2_subdev_call(to_outsd(mdev), video, g_std_output, norm);
- mutex_unlock(&mdev->mutex);
-
- return ret ? -EINVAL : 0;
-}
-
-static int mxr_enum_output(struct file *file, void *fh, struct v4l2_output *a)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- struct mxr_output *out;
- struct v4l2_subdev *sd;
-
- if (a->index >= mdev->output_cnt)
- return -EINVAL;
- out = mdev->output[a->index];
- BUG_ON(out == NULL);
- sd = out->sd;
- strlcpy(a->name, out->name, sizeof(a->name));
-
- /* try to obtain supported tv norms */
- v4l2_subdev_call(sd, video, g_tvnorms_output, &a->std);
- a->capabilities = 0;
- if (sd->ops->video && sd->ops->video->s_dv_preset)
- a->capabilities |= V4L2_OUT_CAP_PRESETS;
- if (sd->ops->video && sd->ops->video->s_std_output)
- a->capabilities |= V4L2_OUT_CAP_STD;
- a->type = V4L2_OUTPUT_TYPE_ANALOG;
-
- return 0;
-}
-
-static int mxr_s_output(struct file *file, void *fh, unsigned int i)
-{
- struct video_device *vfd = video_devdata(file);
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
-
- if (i >= mdev->output_cnt || mdev->output[i] == NULL)
- return -EINVAL;
-
- mutex_lock(&mdev->mutex);
- if (mdev->n_output > 0) {
- mutex_unlock(&mdev->mutex);
- return -EBUSY;
- }
- mdev->current_output = i;
- vfd->tvnorms = 0;
- v4l2_subdev_call(to_outsd(mdev), video, g_tvnorms_output,
- &vfd->tvnorms);
- mutex_unlock(&mdev->mutex);
-
- /* update layers geometry */
- mxr_layer_update_output(layer);
-
- mxr_dbg(mdev, "tvnorms = %08llx\n", vfd->tvnorms);
-
- return 0;
-}
-
-static int mxr_g_output(struct file *file, void *fh, unsigned int *p)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
-
- mutex_lock(&mdev->mutex);
- *p = mdev->current_output;
- mutex_unlock(&mdev->mutex);
-
- return 0;
-}
-
-static int mxr_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- return vb2_reqbufs(&layer->vb_queue, p);
-}
-
-static int mxr_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- return vb2_querybuf(&layer->vb_queue, p);
-}
-
-static int mxr_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d(%d)\n", __func__, __LINE__, p->index);
- return vb2_qbuf(&layer->vb_queue, p);
-}
-
-static int mxr_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- return vb2_dqbuf(&layer->vb_queue, p, file->f_flags & O_NONBLOCK);
-}
-
-static int mxr_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- return vb2_streamon(&layer->vb_queue, i);
-}
-
-static int mxr_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- return vb2_streamoff(&layer->vb_queue, i);
-}
-
-static const struct v4l2_ioctl_ops mxr_ioctl_ops = {
- .vidioc_querycap = mxr_querycap,
- /* format handling */
- .vidioc_enum_fmt_vid_out = mxr_enum_fmt,
- .vidioc_s_fmt_vid_out_mplane = mxr_s_fmt,
- .vidioc_g_fmt_vid_out_mplane = mxr_g_fmt,
- /* buffer control */
- .vidioc_reqbufs = mxr_reqbufs,
- .vidioc_querybuf = mxr_querybuf,
- .vidioc_qbuf = mxr_qbuf,
- .vidioc_dqbuf = mxr_dqbuf,
- /* Streaming control */
- .vidioc_streamon = mxr_streamon,
- .vidioc_streamoff = mxr_streamoff,
- /* Preset functions */
- .vidioc_enum_dv_presets = mxr_enum_dv_presets,
- .vidioc_s_dv_preset = mxr_s_dv_preset,
- .vidioc_g_dv_preset = mxr_g_dv_preset,
- /* analog TV standard functions */
- .vidioc_s_std = mxr_s_std,
- .vidioc_g_std = mxr_g_std,
- /* Output handling */
- .vidioc_enum_output = mxr_enum_output,
- .vidioc_s_output = mxr_s_output,
- .vidioc_g_output = mxr_g_output,
- /* selection ioctls */
- .vidioc_g_selection = mxr_g_selection,
- .vidioc_s_selection = mxr_s_selection,
-};
-
-static int mxr_video_open(struct file *file)
-{
- struct mxr_layer *layer = video_drvdata(file);
- struct mxr_device *mdev = layer->mdev;
- int ret = 0;
-
- mxr_dbg(mdev, "%s:%d\n", __func__, __LINE__);
- /* assure device probe is finished */
- wait_for_device_probe();
- /* creating context for file descriptor */
- ret = v4l2_fh_open(file);
- if (ret) {
- mxr_err(mdev, "v4l2_fh_open failed\n");
- return ret;
- }
-
- /* leaving if layer is already initialized */
- if (!v4l2_fh_is_singular_file(file))
- return 0;
-
- /* FIXME: should power be enabled on open? */
- ret = mxr_power_get(mdev);
- if (ret) {
- mxr_err(mdev, "power on failed\n");
- goto fail_fh_open;
- }
-
- ret = vb2_queue_init(&layer->vb_queue);
- if (ret != 0) {
- mxr_err(mdev, "failed to initialize vb2 queue\n");
- goto fail_power;
- }
- /* set default format, first on the list */
- layer->fmt = layer->fmt_array[0];
- /* setup default geometry */
- mxr_layer_default_geo(layer);
-
- return 0;
-
-fail_power:
- mxr_power_put(mdev);
-
-fail_fh_open:
- v4l2_fh_release(file);
-
- return ret;
-}
-
-static unsigned int
-mxr_video_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
-
- return vb2_poll(&layer->vb_queue, file, wait);
-}
-
-static int mxr_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
-
- return vb2_mmap(&layer->vb_queue, vma);
-}
-
-static int mxr_video_release(struct file *file)
-{
- struct mxr_layer *layer = video_drvdata(file);
-
- mxr_dbg(layer->mdev, "%s:%d\n", __func__, __LINE__);
- if (v4l2_fh_is_singular_file(file)) {
- vb2_queue_release(&layer->vb_queue);
- mxr_power_put(layer->mdev);
- }
- v4l2_fh_release(file);
- return 0;
-}
-
-static const struct v4l2_file_operations mxr_fops = {
- .owner = THIS_MODULE,
- .open = mxr_video_open,
- .poll = mxr_video_poll,
- .mmap = mxr_video_mmap,
- .release = mxr_video_release,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
- unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[],
- void *alloc_ctxs[])
-{
- struct mxr_layer *layer = vb2_get_drv_priv(vq);
- const struct mxr_format *fmt = layer->fmt;
- int i;
- struct mxr_device *mdev = layer->mdev;
- struct v4l2_plane_pix_format planes[3];
-
- mxr_dbg(mdev, "%s\n", __func__);
- /* checking if format was configured */
- if (fmt == NULL)
- return -EINVAL;
- mxr_dbg(mdev, "fmt = %s\n", fmt->name);
- mxr_mplane_fill(planes, fmt, layer->geo.src.full_width,
- layer->geo.src.full_height);
-
- *nplanes = fmt->num_subframes;
- for (i = 0; i < fmt->num_subframes; ++i) {
- alloc_ctxs[i] = layer->mdev->alloc_ctx;
- sizes[i] = planes[i].sizeimage;
- mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]);
- }
-
- if (*nbuffers == 0)
- *nbuffers = 1;
-
- return 0;
-}
-
-static void buf_queue(struct vb2_buffer *vb)
-{
- struct mxr_buffer *buffer = container_of(vb, struct mxr_buffer, vb);
- struct mxr_layer *layer = vb2_get_drv_priv(vb->vb2_queue);
- struct mxr_device *mdev = layer->mdev;
- unsigned long flags;
-
- spin_lock_irqsave(&layer->enq_slock, flags);
- list_add_tail(&buffer->list, &layer->enq_list);
- spin_unlock_irqrestore(&layer->enq_slock, flags);
-
- mxr_dbg(mdev, "queuing buffer\n");
-}
-
-static void wait_lock(struct vb2_queue *vq)
-{
- struct mxr_layer *layer = vb2_get_drv_priv(vq);
-
- mxr_dbg(layer->mdev, "%s\n", __func__);
- mutex_lock(&layer->mutex);
-}
-
-static void wait_unlock(struct vb2_queue *vq)
-{
- struct mxr_layer *layer = vb2_get_drv_priv(vq);
-
- mxr_dbg(layer->mdev, "%s\n", __func__);
- mutex_unlock(&layer->mutex);
-}
-
-static int start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct mxr_layer *layer = vb2_get_drv_priv(vq);
- struct mxr_device *mdev = layer->mdev;
- unsigned long flags;
-
- mxr_dbg(mdev, "%s\n", __func__);
-
- if (count == 0) {
- mxr_dbg(mdev, "no output buffers queued\n");
- return -EINVAL;
- }
-
- /* block any changes in output configuration */
- mxr_output_get(mdev);
-
- mxr_layer_update_output(layer);
- layer->ops.format_set(layer);
- /* enabling layer in hardware */
- spin_lock_irqsave(&layer->enq_slock, flags);
- layer->state = MXR_LAYER_STREAMING;
- spin_unlock_irqrestore(&layer->enq_slock, flags);
-
- layer->ops.stream_set(layer, MXR_ENABLE);
- mxr_streamer_get(mdev);
-
- return 0;
-}
-
-static void mxr_watchdog(unsigned long arg)
-{
- struct mxr_layer *layer = (struct mxr_layer *) arg;
- struct mxr_device *mdev = layer->mdev;
- unsigned long flags;
-
- mxr_err(mdev, "watchdog fired for layer %s\n", layer->vfd.name);
-
- spin_lock_irqsave(&layer->enq_slock, flags);
-
- if (layer->update_buf == layer->shadow_buf)
- layer->update_buf = NULL;
- if (layer->update_buf) {
- vb2_buffer_done(&layer->update_buf->vb, VB2_BUF_STATE_ERROR);
- layer->update_buf = NULL;
- }
- if (layer->shadow_buf) {
- vb2_buffer_done(&layer->shadow_buf->vb, VB2_BUF_STATE_ERROR);
- layer->shadow_buf = NULL;
- }
- spin_unlock_irqrestore(&layer->enq_slock, flags);
-}
-
-static int stop_streaming(struct vb2_queue *vq)
-{
- struct mxr_layer *layer = vb2_get_drv_priv(vq);
- struct mxr_device *mdev = layer->mdev;
- unsigned long flags;
- struct timer_list watchdog;
- struct mxr_buffer *buf, *buf_tmp;
-
- mxr_dbg(mdev, "%s\n", __func__);
-
- spin_lock_irqsave(&layer->enq_slock, flags);
-
- /* reset list */
- layer->state = MXR_LAYER_STREAMING_FINISH;
-
- /* set all buffer to be done */
- list_for_each_entry_safe(buf, buf_tmp, &layer->enq_list, list) {
- list_del(&buf->list);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
- }
-
- spin_unlock_irqrestore(&layer->enq_slock, flags);
-
- /* give 1 seconds to complete to complete last buffers */
- setup_timer_on_stack(&watchdog, mxr_watchdog,
- (unsigned long)layer);
- mod_timer(&watchdog, jiffies + msecs_to_jiffies(1000));
-
- /* wait until all buffers are goes to done state */
- vb2_wait_for_all_buffers(vq);
-
- /* stop timer if all synchronization is done */
- del_timer_sync(&watchdog);
- destroy_timer_on_stack(&watchdog);
-
- /* stopping hardware */
- spin_lock_irqsave(&layer->enq_slock, flags);
- layer->state = MXR_LAYER_IDLE;
- spin_unlock_irqrestore(&layer->enq_slock, flags);
-
- /* disabling layer in hardware */
- layer->ops.stream_set(layer, MXR_DISABLE);
- /* remove one streamer */
- mxr_streamer_put(mdev);
- /* allow changes in output configuration */
- mxr_output_put(mdev);
- return 0;
-}
-
-static struct vb2_ops mxr_video_qops = {
- .queue_setup = queue_setup,
- .buf_queue = buf_queue,
- .wait_prepare = wait_unlock,
- .wait_finish = wait_lock,
- .start_streaming = start_streaming,
- .stop_streaming = stop_streaming,
-};
-
-/* FIXME: try to put this functions to mxr_base_layer_create */
-int mxr_base_layer_register(struct mxr_layer *layer)
-{
- struct mxr_device *mdev = layer->mdev;
- int ret;
-
- ret = video_register_device(&layer->vfd, VFL_TYPE_GRABBER, -1);
- if (ret)
- mxr_err(mdev, "failed to register video device\n");
- else
- mxr_info(mdev, "registered layer %s as /dev/video%d\n",
- layer->vfd.name, layer->vfd.num);
- return ret;
-}
-
-void mxr_base_layer_unregister(struct mxr_layer *layer)
-{
- video_unregister_device(&layer->vfd);
-}
-
-void mxr_layer_release(struct mxr_layer *layer)
-{
- if (layer->ops.release)
- layer->ops.release(layer);
-}
-
-void mxr_base_layer_release(struct mxr_layer *layer)
-{
- kfree(layer);
-}
-
-static void mxr_vfd_release(struct video_device *vdev)
-{
- printk(KERN_INFO "video device release\n");
-}
-
-struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
- int idx, char *name, struct mxr_layer_ops *ops)
-{
- struct mxr_layer *layer;
-
- layer = kzalloc(sizeof *layer, GFP_KERNEL);
- if (layer == NULL) {
- mxr_err(mdev, "not enough memory for layer.\n");
- goto fail;
- }
-
- layer->mdev = mdev;
- layer->idx = idx;
- layer->ops = *ops;
-
- spin_lock_init(&layer->enq_slock);
- INIT_LIST_HEAD(&layer->enq_list);
- mutex_init(&layer->mutex);
-
- layer->vfd = (struct video_device) {
- .minor = -1,
- .release = mxr_vfd_release,
- .fops = &mxr_fops,
- .ioctl_ops = &mxr_ioctl_ops,
- };
- strlcpy(layer->vfd.name, name, sizeof(layer->vfd.name));
- /* let framework control PRIORITY */
- set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
-
- video_set_drvdata(&layer->vfd, layer);
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &layer->vfd.flags);
- layer->vfd.lock = &layer->mutex;
- layer->vfd.v4l2_dev = &mdev->v4l2_dev;
-
- layer->vb_queue = (struct vb2_queue) {
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- .io_modes = VB2_MMAP | VB2_USERPTR,
- .drv_priv = layer,
- .buf_struct_size = sizeof(struct mxr_buffer),
- .ops = &mxr_video_qops,
- .mem_ops = &vb2_dma_contig_memops,
- };
-
- return layer;
-
-fail:
- return NULL;
-}
-
-static const struct mxr_format *find_format_by_fourcc(
- struct mxr_layer *layer, unsigned long fourcc)
-{
- int i;
-
- for (i = 0; i < layer->fmt_array_size; ++i)
- if (layer->fmt_array[i]->fourcc == fourcc)
- return layer->fmt_array[i];
- return NULL;
-}
-
-static const struct mxr_format *find_format_by_index(
- struct mxr_layer *layer, unsigned long index)
-{
- if (index >= layer->fmt_array_size)
- return NULL;
- return layer->fmt_array[index];
-}
-
diff --git a/drivers/media/video/s5p-tv/regs-sdo.h b/drivers/media/video/s5p-tv/regs-sdo.h
deleted file mode 100644
index 7f7c2b8ac140..000000000000
--- a/drivers/media/video/s5p-tv/regs-sdo.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* drivers/media/video/s5p-tv/regs-sdo.h
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * SDO register description file
- *
- * 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.
- */
-
-#ifndef SAMSUNG_REGS_SDO_H
-#define SAMSUNG_REGS_SDO_H
-
-/*
- * Register part
- */
-
-#define SDO_CLKCON 0x0000
-#define SDO_CONFIG 0x0008
-#define SDO_VBI 0x0014
-#define SDO_DAC 0x003C
-#define SDO_CCCON 0x0180
-#define SDO_IRQ 0x0280
-#define SDO_IRQMASK 0x0284
-#define SDO_VERSION 0x03D8
-
-/*
- * Bit definition part
- */
-
-/* SDO Clock Control Register (SDO_CLKCON) */
-#define SDO_TVOUT_SW_RESET (1 << 4)
-#define SDO_TVOUT_CLOCK_READY (1 << 1)
-#define SDO_TVOUT_CLOCK_ON (1 << 0)
-
-/* SDO Video Standard Configuration Register (SDO_CONFIG) */
-#define SDO_PROGRESSIVE (1 << 4)
-#define SDO_NTSC_M 0
-#define SDO_PAL_M 1
-#define SDO_PAL_BGHID 2
-#define SDO_PAL_N 3
-#define SDO_PAL_NC 4
-#define SDO_NTSC_443 8
-#define SDO_PAL_60 9
-#define SDO_STANDARD_MASK 0xf
-
-/* SDO VBI Configuration Register (SDO_VBI) */
-#define SDO_CVBS_WSS_INS (1 << 14)
-#define SDO_CVBS_CLOSED_CAPTION_MASK (3 << 12)
-
-/* SDO DAC Configuration Register (SDO_DAC) */
-#define SDO_POWER_ON_DAC (1 << 0)
-
-/* SDO Color Compensation On/Off Control (SDO_CCCON) */
-#define SDO_COMPENSATION_BHS_ADJ_OFF (1 << 4)
-#define SDO_COMPENSATION_CVBS_COMP_OFF (1 << 0)
-
-/* SDO Interrupt Request Register (SDO_IRQ) */
-#define SDO_VSYNC_IRQ_PEND (1 << 0)
-
-#endif /* SAMSUNG_REGS_SDO_H */
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
deleted file mode 100644
index f6bca2c20e89..000000000000
--- a/drivers/media/video/s5p-tv/sdo_drv.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Samsung Standard Definition Output (SDO) driver
- *
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- *
- * Tomasz Stanislawski, <t.stanislaws@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 Foundiation. either version 2 of the License,
- * or (at your option) any later version
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-subdev.h>
-
-#include "regs-sdo.h"
-
-MODULE_AUTHOR("Tomasz Stanislawski, <t.stanislaws@samsung.com>");
-MODULE_DESCRIPTION("Samsung Standard Definition Output (SDO)");
-MODULE_LICENSE("GPL");
-
-#define SDO_DEFAULT_STD V4L2_STD_PAL
-
-struct sdo_format {
- v4l2_std_id id;
- /* all modes are 720 pixels wide */
- unsigned int height;
- unsigned int cookie;
-};
-
-struct sdo_device {
- /** pointer to device parent */
- struct device *dev;
- /** base address of SDO registers */
- void __iomem *regs;
- /** SDO interrupt */
- unsigned int irq;
- /** DAC source clock */
- struct clk *sclk_dac;
- /** DAC clock */
- struct clk *dac;
- /** DAC physical interface */
- struct clk *dacphy;
- /** clock for control of VPLL */
- struct clk *fout_vpll;
- /** regulator for SDO IP power */
- struct regulator *vdac;
- /** regulator for SDO plug detection */
- struct regulator *vdet;
- /** subdev used as device interface */
- struct v4l2_subdev sd;
- /** current format */
- const struct sdo_format *fmt;
-};
-
-static inline struct sdo_device *sd_to_sdev(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct sdo_device, sd);
-}
-
-static inline
-void sdo_write_mask(struct sdo_device *sdev, u32 reg_id, u32 value, u32 mask)
-{
- u32 old = readl(sdev->regs + reg_id);
- value = (value & mask) | (old & ~mask);
- writel(value, sdev->regs + reg_id);
-}
-
-static inline
-void sdo_write(struct sdo_device *sdev, u32 reg_id, u32 value)
-{
- writel(value, sdev->regs + reg_id);
-}
-
-static inline
-u32 sdo_read(struct sdo_device *sdev, u32 reg_id)
-{
- return readl(sdev->regs + reg_id);
-}
-
-static irqreturn_t sdo_irq_handler(int irq, void *dev_data)
-{
- struct sdo_device *sdev = dev_data;
-
- /* clear interrupt */
- sdo_write_mask(sdev, SDO_IRQ, ~0, SDO_VSYNC_IRQ_PEND);
- return IRQ_HANDLED;
-}
-
-static void sdo_reg_debug(struct sdo_device *sdev)
-{
-#define DBGREG(reg_id) \
- dev_info(sdev->dev, #reg_id " = %08x\n", \
- sdo_read(sdev, reg_id))
-
- DBGREG(SDO_CLKCON);
- DBGREG(SDO_CONFIG);
- DBGREG(SDO_VBI);
- DBGREG(SDO_DAC);
- DBGREG(SDO_IRQ);
- DBGREG(SDO_IRQMASK);
- DBGREG(SDO_VERSION);
-}
-
-static const struct sdo_format sdo_format[] = {
- { V4L2_STD_PAL_N, .height = 576, .cookie = SDO_PAL_N },
- { V4L2_STD_PAL_Nc, .height = 576, .cookie = SDO_PAL_NC },
- { V4L2_STD_PAL_M, .height = 480, .cookie = SDO_PAL_M },
- { V4L2_STD_PAL_60, .height = 480, .cookie = SDO_PAL_60 },
- { V4L2_STD_NTSC_443, .height = 480, .cookie = SDO_NTSC_443 },
- { V4L2_STD_PAL, .height = 576, .cookie = SDO_PAL_BGHID },
- { V4L2_STD_NTSC_M, .height = 480, .cookie = SDO_NTSC_M },
-};
-
-static const struct sdo_format *sdo_find_format(v4l2_std_id id)
-{
- int i;
- for (i = 0; i < ARRAY_SIZE(sdo_format); ++i)
- if (sdo_format[i].id & id)
- return &sdo_format[i];
- return NULL;
-}
-
-static int sdo_g_tvnorms_output(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- *std = V4L2_STD_NTSC_M | V4L2_STD_PAL_M | V4L2_STD_PAL |
- V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
- V4L2_STD_NTSC_443 | V4L2_STD_PAL_60;
- return 0;
-}
-
-static int sdo_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct sdo_device *sdev = sd_to_sdev(sd);
- const struct sdo_format *fmt;
- fmt = sdo_find_format(std);
- if (fmt == NULL)
- return -EINVAL;
- sdev->fmt = fmt;
- return 0;
-}
-
-static int sdo_g_std_output(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- *std = sd_to_sdev(sd)->fmt->id;
- return 0;
-}
-
-static int sdo_g_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *fmt)
-{
- struct sdo_device *sdev = sd_to_sdev(sd);
-
- if (!sdev->fmt)
- return -ENXIO;
- /* all modes are 720 pixels wide */
- fmt->width = 720;
- fmt->height = sdev->fmt->height;
- fmt->code = V4L2_MBUS_FMT_FIXED;
- fmt->field = V4L2_FIELD_INTERLACED;
- fmt->colorspace = V4L2_COLORSPACE_JPEG;
- return 0;
-}
-
-static int sdo_s_power(struct v4l2_subdev *sd, int on)
-{
- struct sdo_device *sdev = sd_to_sdev(sd);
- struct device *dev = sdev->dev;
- int ret;
-
- dev_info(dev, "sdo_s_power(%d)\n", on);
-
- if (on)
- ret = pm_runtime_get_sync(dev);
- else
- ret = pm_runtime_put_sync(dev);
-
- /* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
-}
-
-static int sdo_streamon(struct sdo_device *sdev)
-{
- /* set proper clock for Timing Generator */
- clk_set_rate(sdev->fout_vpll, 54000000);
- dev_info(sdev->dev, "fout_vpll.rate = %lu\n",
- clk_get_rate(sdev->fout_vpll));
- /* enable clock in SDO */
- sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_CLOCK_ON);
- clk_enable(sdev->dacphy);
- /* enable DAC */
- sdo_write_mask(sdev, SDO_DAC, ~0, SDO_POWER_ON_DAC);
- sdo_reg_debug(sdev);
- return 0;
-}
-
-static int sdo_streamoff(struct sdo_device *sdev)
-{
- int tries;
-
- sdo_write_mask(sdev, SDO_DAC, 0, SDO_POWER_ON_DAC);
- clk_disable(sdev->dacphy);
- sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_CLOCK_ON);
- for (tries = 100; tries; --tries) {
- if (sdo_read(sdev, SDO_CLKCON) & SDO_TVOUT_CLOCK_READY)
- break;
- mdelay(1);
- }
- if (tries == 0)
- dev_err(sdev->dev, "failed to stop streaming\n");
- return tries ? 0 : -EIO;
-}
-
-static int sdo_s_stream(struct v4l2_subdev *sd, int on)
-{
- struct sdo_device *sdev = sd_to_sdev(sd);
- return on ? sdo_streamon(sdev) : sdo_streamoff(sdev);
-}
-
-static const struct v4l2_subdev_core_ops sdo_sd_core_ops = {
- .s_power = sdo_s_power,
-};
-
-static const struct v4l2_subdev_video_ops sdo_sd_video_ops = {
- .s_std_output = sdo_s_std_output,
- .g_std_output = sdo_g_std_output,
- .g_tvnorms_output = sdo_g_tvnorms_output,
- .g_mbus_fmt = sdo_g_mbus_fmt,
- .s_stream = sdo_s_stream,
-};
-
-static const struct v4l2_subdev_ops sdo_sd_ops = {
- .core = &sdo_sd_core_ops,
- .video = &sdo_sd_video_ops,
-};
-
-static int sdo_runtime_suspend(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct sdo_device *sdev = sd_to_sdev(sd);
-
- dev_info(dev, "suspend\n");
- regulator_disable(sdev->vdet);
- regulator_disable(sdev->vdac);
- clk_disable(sdev->sclk_dac);
- return 0;
-}
-
-static int sdo_runtime_resume(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct sdo_device *sdev = sd_to_sdev(sd);
-
- dev_info(dev, "resume\n");
- clk_enable(sdev->sclk_dac);
- regulator_enable(sdev->vdac);
- regulator_enable(sdev->vdet);
-
- /* software reset */
- sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET);
- mdelay(10);
- sdo_write_mask(sdev, SDO_CLKCON, 0, SDO_TVOUT_SW_RESET);
-
- /* setting TV mode */
- sdo_write_mask(sdev, SDO_CONFIG, sdev->fmt->cookie, SDO_STANDARD_MASK);
- /* XXX: forcing interlaced mode using undocumented bit */
- sdo_write_mask(sdev, SDO_CONFIG, 0, SDO_PROGRESSIVE);
- /* turn all VBI off */
- sdo_write_mask(sdev, SDO_VBI, 0, SDO_CVBS_WSS_INS |
- SDO_CVBS_CLOSED_CAPTION_MASK);
- /* turn all post processing off */
- sdo_write_mask(sdev, SDO_CCCON, ~0, SDO_COMPENSATION_BHS_ADJ_OFF |
- SDO_COMPENSATION_CVBS_COMP_OFF);
- sdo_reg_debug(sdev);
- return 0;
-}
-
-static const struct dev_pm_ops sdo_pm_ops = {
- .runtime_suspend = sdo_runtime_suspend,
- .runtime_resume = sdo_runtime_resume,
-};
-
-static int __devinit sdo_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct sdo_device *sdev;
- struct resource *res;
- int ret = 0;
- struct clk *sclk_vpll;
-
- dev_info(dev, "probe start\n");
- sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
- if (!sdev) {
- dev_err(dev, "not enough memory.\n");
- ret = -ENOMEM;
- goto fail;
- }
- sdev->dev = dev;
-
- /* mapping registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(dev, "get memory resource failed.\n");
- ret = -ENXIO;
- goto fail;
- }
-
- sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
- if (sdev->regs == NULL) {
- dev_err(dev, "register mapping failed.\n");
- ret = -ENXIO;
- goto fail;
- }
-
- /* acquiring interrupt */
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(dev, "get interrupt resource failed.\n");
- ret = -ENXIO;
- goto fail;
- }
- ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
- "s5p-sdo", sdev);
- if (ret) {
- dev_err(dev, "request interrupt failed.\n");
- goto fail;
- }
- sdev->irq = res->start;
-
- /* acquire clocks */
- sdev->sclk_dac = clk_get(dev, "sclk_dac");
- if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
- dev_err(dev, "failed to get clock 'sclk_dac'\n");
- ret = -ENXIO;
- goto fail;
- }
- sdev->dac = clk_get(dev, "dac");
- if (IS_ERR_OR_NULL(sdev->dac)) {
- dev_err(dev, "failed to get clock 'dac'\n");
- ret = -ENXIO;
- goto fail_sclk_dac;
- }
- sdev->dacphy = clk_get(dev, "dacphy");
- if (IS_ERR_OR_NULL(sdev->dacphy)) {
- dev_err(dev, "failed to get clock 'dacphy'\n");
- ret = -ENXIO;
- goto fail_dac;
- }
- sclk_vpll = clk_get(dev, "sclk_vpll");
- if (IS_ERR_OR_NULL(sclk_vpll)) {
- dev_err(dev, "failed to get clock 'sclk_vpll'\n");
- ret = -ENXIO;
- goto fail_dacphy;
- }
- clk_set_parent(sdev->sclk_dac, sclk_vpll);
- clk_put(sclk_vpll);
- sdev->fout_vpll = clk_get(dev, "fout_vpll");
- if (IS_ERR_OR_NULL(sdev->fout_vpll)) {
- dev_err(dev, "failed to get clock 'fout_vpll'\n");
- goto fail_dacphy;
- }
- dev_info(dev, "fout_vpll.rate = %lu\n", clk_get_rate(sclk_vpll));
-
- /* acquire regulator */
- sdev->vdac = regulator_get(dev, "vdd33a_dac");
- if (IS_ERR_OR_NULL(sdev->vdac)) {
- dev_err(dev, "failed to get regulator 'vdac'\n");
- goto fail_fout_vpll;
- }
- sdev->vdet = regulator_get(dev, "vdet");
- if (IS_ERR_OR_NULL(sdev->vdet)) {
- dev_err(dev, "failed to get regulator 'vdet'\n");
- goto fail_vdac;
- }
-
- /* enable gate for dac clock, because mixer uses it */
- clk_enable(sdev->dac);
-
- /* configure power management */
- pm_runtime_enable(dev);
-
- /* configuration of interface subdevice */
- v4l2_subdev_init(&sdev->sd, &sdo_sd_ops);
- sdev->sd.owner = THIS_MODULE;
- strlcpy(sdev->sd.name, "s5p-sdo", sizeof sdev->sd.name);
-
- /* set default format */
- sdev->fmt = sdo_find_format(SDO_DEFAULT_STD);
- BUG_ON(sdev->fmt == NULL);
-
- /* keeping subdev in device's private for use by other drivers */
- dev_set_drvdata(dev, &sdev->sd);
-
- dev_info(dev, "probe succeeded\n");
- return 0;
-
-fail_vdac:
- regulator_put(sdev->vdac);
-fail_fout_vpll:
- clk_put(sdev->fout_vpll);
-fail_dacphy:
- clk_put(sdev->dacphy);
-fail_dac:
- clk_put(sdev->dac);
-fail_sclk_dac:
- clk_put(sdev->sclk_dac);
-fail:
- dev_info(dev, "probe failed\n");
- return ret;
-}
-
-static int __devexit sdo_remove(struct platform_device *pdev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(&pdev->dev);
- struct sdo_device *sdev = sd_to_sdev(sd);
-
- pm_runtime_disable(&pdev->dev);
- clk_disable(sdev->dac);
- regulator_put(sdev->vdet);
- regulator_put(sdev->vdac);
- clk_put(sdev->fout_vpll);
- clk_put(sdev->dacphy);
- clk_put(sdev->dac);
- clk_put(sdev->sclk_dac);
-
- dev_info(&pdev->dev, "remove successful\n");
- return 0;
-}
-
-static struct platform_driver sdo_driver __refdata = {
- .probe = sdo_probe,
- .remove = __devexit_p(sdo_remove),
- .driver = {
- .name = "s5p-sdo",
- .owner = THIS_MODULE,
- .pm = &sdo_pm_ops,
- }
-};
-
-module_platform_driver(sdo_driver);
diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c
deleted file mode 100644
index 6d348f90237a..000000000000
--- a/drivers/media/video/s5p-tv/sii9234_drv.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Samsung MHL interface driver
- *
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Author: Tomasz Stanislawski <t.stanislaws@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.
- */
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/freezer.h>
-#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/kthread.h>
-#include <linux/module.h>
-#include <linux/pm_runtime.h>
-#include <linux/regulator/machine.h>
-#include <linux/slab.h>
-
-#include <mach/gpio.h>
-#include <plat/gpio-cfg.h>
-
-#include <media/sii9234.h>
-#include <media/v4l2-subdev.h>
-
-MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
-MODULE_DESCRIPTION("Samsung MHL interface driver");
-MODULE_LICENSE("GPL");
-
-struct sii9234_context {
- struct i2c_client *client;
- struct regulator *power;
- int gpio_n_reset;
- struct v4l2_subdev sd;
-};
-
-static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct sii9234_context, sd);
-}
-
-static inline int sii9234_readb(struct i2c_client *client, int addr)
-{
- return i2c_smbus_read_byte_data(client, addr);
-}
-
-static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
-{
- return i2c_smbus_write_byte_data(client, addr, value);
-}
-
-static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
- int value, int mask)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, addr);
- if (ret < 0)
- return ret;
- ret = (ret & ~mask) | (value & mask);
- return i2c_smbus_write_byte_data(client, addr, ret);
-}
-
-static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
-{
- int ret;
- ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
- if (ret < 0)
- return ret;
- ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
- if (ret < 0)
- return ret;
- return i2c_smbus_read_byte_data(client, 0xbe);
-}
-
-static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
- int value)
-{
- int ret;
- ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
- if (ret < 0)
- return ret;
- ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
- if (ret < 0)
- return ret;
- ret = i2c_smbus_write_byte_data(client, 0xbe, value);
- return ret;
-}
-
-static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
- int value, int mask)
-{
- int ret;
-
- ret = sii9234_readb_idx(client, addr);
- if (ret < 0)
- return ret;
- ret = (ret & ~mask) | (value & mask);
- return sii9234_writeb_idx(client, addr, ret);
-}
-
-static int sii9234_reset(struct sii9234_context *ctx)
-{
- struct i2c_client *client = ctx->client;
- struct device *dev = &client->dev;
- int ret, tries;
-
- gpio_direction_output(ctx->gpio_n_reset, 1);
- mdelay(1);
- gpio_direction_output(ctx->gpio_n_reset, 0);
- mdelay(1);
- gpio_direction_output(ctx->gpio_n_reset, 1);
- mdelay(1);
-
- /* going to TTPI mode */
- ret = sii9234_writeb(client, 0xc7, 0);
- if (ret < 0) {
- dev_err(dev, "failed to set TTPI mode\n");
- return ret;
- }
- for (tries = 0; tries < 100 ; ++tries) {
- ret = sii9234_readb(client, 0x1b);
- if (ret > 0)
- break;
- if (ret < 0) {
- dev_err(dev, "failed to reset device\n");
- return -EIO;
- }
- mdelay(1);
- }
- if (tries == 100) {
- dev_err(dev, "maximal number of tries reached\n");
- return -EIO;
- }
-
- return 0;
-}
-
-static int sii9234_verify_version(struct i2c_client *client)
-{
- struct device *dev = &client->dev;
- int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
-
- family = sii9234_readb(client, 0x1b);
- rev = sii9234_readb(client, 0x1c) & 0x0f;
- tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
- dev_id = sii9234_readb_idx(client, 0x0103);
- sub_id = sii9234_readb_idx(client, 0x0102);
- hdcp = sii9234_readb(client, 0x30);
-
- if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
- sub_id < 0 || hdcp < 0) {
- dev_err(dev, "failed to read chip's version\n");
- return -EIO;
- }
-
- id = (dev_id << 8) | sub_id;
-
- dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
- id, family, rev);
- dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
- if (id != 0x9234) {
- dev_err(dev, "not supported chip\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static u8 data[][3] = {
-/* setup from driver created by doonsoo45.kim */
- { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
- { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
- { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
- { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
- { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
- { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
- { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
- { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
- { 0x01, 0x92, 0x46 }, /* Force MHD mode */
- { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
- { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
- { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
- { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
- { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
- { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
- { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
- { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
- { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
- { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
- { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
- { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
- { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
- { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
- { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
- { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
- { 0x03, 0x1a, 0x20 }, /* VCO Cal */
- { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
- { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
- { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
- { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
- { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
- { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
- { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
- { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
- { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
- { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
- { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
- { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
- { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
-};
-
-static int sii9234_set_internal(struct sii9234_context *ctx)
-{
- struct i2c_client *client = ctx->client;
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(data); ++i) {
- int addr = (data[i][0] << 8) | data[i][1];
- ret = sii9234_writeb_idx(client, addr, data[i][2]);
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-static int sii9234_runtime_suspend(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct sii9234_context *ctx = sd_to_context(sd);
- struct i2c_client *client = ctx->client;
-
- dev_info(dev, "suspend start\n");
-
- sii9234_writeb_mask(client, 0x1e, 3, 3);
- regulator_disable(ctx->power);
-
- return 0;
-}
-
-static int sii9234_runtime_resume(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct sii9234_context *ctx = sd_to_context(sd);
- struct i2c_client *client = ctx->client;
- int ret;
-
- dev_info(dev, "resume start\n");
- regulator_enable(ctx->power);
-
- ret = sii9234_reset(ctx);
- if (ret)
- goto fail;
-
- /* enable tpi */
- ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
- if (ret < 0)
- goto fail;
- ret = sii9234_set_internal(ctx);
- if (ret < 0)
- goto fail;
-
- return 0;
-
-fail:
- dev_err(dev, "failed to resume\n");
- regulator_disable(ctx->power);
-
- return ret;
-}
-
-static const struct dev_pm_ops sii9234_pm_ops = {
- .runtime_suspend = sii9234_runtime_suspend,
- .runtime_resume = sii9234_runtime_resume,
-};
-
-static int sii9234_s_power(struct v4l2_subdev *sd, int on)
-{
- struct sii9234_context *ctx = sd_to_context(sd);
- int ret;
-
- if (on)
- ret = pm_runtime_get_sync(&ctx->client->dev);
- else
- ret = pm_runtime_put(&ctx->client->dev);
- /* only values < 0 indicate errors */
- return IS_ERR_VALUE(ret) ? ret : 0;
-}
-
-static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct sii9234_context *ctx = sd_to_context(sd);
-
- /* (dis/en)able TDMS output */
- sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
- return 0;
-}
-
-static const struct v4l2_subdev_core_ops sii9234_core_ops = {
- .s_power = sii9234_s_power,
-};
-
-static const struct v4l2_subdev_video_ops sii9234_video_ops = {
- .s_stream = sii9234_s_stream,
-};
-
-static const struct v4l2_subdev_ops sii9234_ops = {
- .core = &sii9234_core_ops,
- .video = &sii9234_video_ops,
-};
-
-static int __devinit sii9234_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct device *dev = &client->dev;
- struct sii9234_platform_data *pdata = dev->platform_data;
- struct sii9234_context *ctx;
- int ret;
-
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- dev_err(dev, "out of memory\n");
- ret = -ENOMEM;
- goto fail;
- }
- ctx->client = client;
-
- ctx->power = regulator_get(dev, "hdmi-en");
- if (IS_ERR(ctx->power)) {
- dev_err(dev, "failed to acquire regulator hdmi-en\n");
- ret = PTR_ERR(ctx->power);
- goto fail_ctx;
- }
-
- ctx->gpio_n_reset = pdata->gpio_n_reset;
- ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
- if (ret) {
- dev_err(dev, "failed to acquire MHL_RST gpio\n");
- goto fail_power;
- }
-
- v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
-
- pm_runtime_enable(dev);
-
- /* enable device */
- ret = pm_runtime_get_sync(dev);
- if (ret)
- goto fail_pm;
-
- /* verify chip version */
- ret = sii9234_verify_version(client);
- if (ret)
- goto fail_pm_get;
-
- /* stop processing */
- pm_runtime_put(dev);
-
- dev_info(dev, "probe successful\n");
-
- return 0;
-
-fail_pm_get:
- pm_runtime_put_sync(dev);
-
-fail_pm:
- pm_runtime_disable(dev);
- gpio_free(ctx->gpio_n_reset);
-
-fail_power:
- regulator_put(ctx->power);
-
-fail_ctx:
- kfree(ctx);
-
-fail:
- dev_err(dev, "probe failed\n");
-
- return ret;
-}
-
-static int __devexit sii9234_remove(struct i2c_client *client)
-{
- struct device *dev = &client->dev;
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct sii9234_context *ctx = sd_to_context(sd);
-
- pm_runtime_disable(dev);
- gpio_free(ctx->gpio_n_reset);
- regulator_put(ctx->power);
- kfree(ctx);
-
- dev_info(dev, "remove successful\n");
-
- return 0;
-}
-
-
-static const struct i2c_device_id sii9234_id[] = {
- { "SII9234", 0 },
- { },
-};
-
-MODULE_DEVICE_TABLE(i2c, sii9234_id);
-static struct i2c_driver sii9234_driver = {
- .driver = {
- .name = "sii9234",
- .owner = THIS_MODULE,
- .pm = &sii9234_pm_ops,
- },
- .probe = sii9234_probe,
- .remove = __devexit_p(sii9234_remove),
- .id_table = sii9234_id,
-};
-
-module_i2c_driver(sii9234_driver);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
deleted file mode 100644
index 2107336cd836..000000000000
--- a/drivers/media/video/saa7115.c
+++ /dev/null
@@ -1,1727 +0,0 @@
-/* saa711x - Philips SAA711x video decoder driver
- * This driver can work with saa7111, saa7111a, saa7113, saa7114,
- * saa7115 and saa7118.
- *
- * Based on saa7114 driver by Maxim Yevtyushkin, which is based on
- * the saa7111 driver by Dave Perks.
- *
- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
- * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
- *
- * Slight changes for video timing and attachment output by
- * Wolfgang Scherr <scherr@net4you.net>
- *
- * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003)
- * by Ronald Bultje <rbultje@ronald.bitfreak.net>
- *
- * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com>
- * (2/17/2003)
- *
- * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
- * SAA7111, SAA7113 and SAA7118 support
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "saa711x_regs.h"
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/saa7115.h>
-#include <asm/div64.h>
-
-#define VRES_60HZ (480+16)
-
-MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver");
-MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
- "Hans Verkuil, Mauro Carvalho Chehab");
-MODULE_LICENSE("GPL");
-
-static bool debug;
-module_param(debug, bool, 0644);
-
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-
-struct saa711x_state {
- struct v4l2_subdev sd;
- struct v4l2_ctrl_handler hdl;
-
- struct {
- /* chroma gain control cluster */
- struct v4l2_ctrl *agc;
- struct v4l2_ctrl *gain;
- };
-
- v4l2_std_id std;
- int input;
- int output;
- int enable;
- int radio;
- int width;
- int height;
- u32 ident;
- u32 audclk_freq;
- u32 crystal_freq;
- u8 ucgc;
- u8 cgcdiv;
- u8 apll;
-};
-
-static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct saa711x_state, sd);
-}
-
-static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
-{
- return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return i2c_smbus_write_byte_data(client, reg, value);
-}
-
-/* Sanity routine to check if a register is present */
-static int saa711x_has_reg(const int id, const u8 reg)
-{
- if (id == V4L2_IDENT_SAA7111)
- return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
- (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
- if (id == V4L2_IDENT_SAA7111A)
- return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
- reg != 0x14 && reg != 0x18 && reg != 0x19 &&
- reg != 0x1d && reg != 0x1e;
-
- /* common for saa7113/4/5/8 */
- if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
- reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) ||
- reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) ||
- reg == 0x82 || (reg >= 0x89 && reg <= 0x8e)))
- return 0;
-
- switch (id) {
- case V4L2_IDENT_SAA7113:
- return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
- reg != 0x5d && reg < 0x63;
- case V4L2_IDENT_SAA7114:
- return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
- (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
- reg != 0x81 && reg < 0xf0;
- case V4L2_IDENT_SAA7115:
- return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
- case V4L2_IDENT_SAA7118:
- return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
- (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
- (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
- }
- return 1;
-}
-
-static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
-{
- struct saa711x_state *state = to_state(sd);
- unsigned char reg, data;
-
- while (*regs != 0x00) {
- reg = *(regs++);
- data = *(regs++);
-
- /* According with datasheets, reserved regs should be
- filled with 0 - seems better not to touch on they */
- if (saa711x_has_reg(state->ident, reg)) {
- if (saa711x_write(sd, reg, data) < 0)
- return -1;
- } else {
- v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
- }
- }
- return 0;
-}
-
-static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return i2c_smbus_read_byte_data(client, reg);
-}
-
-/* ----------------------------------------------------------------------- */
-
-/* SAA7111 initialization table */
-static const unsigned char saa7111_init[] = {
- R_01_INC_DELAY, 0x00, /* reserved */
-
- /*front end */
- R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */
- R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0,
- * GAFIX=0, GAI1=256, GAI2=256 */
- R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */
- R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */
-
- /* decoder */
- R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz)
- * pixels after end of last line */
- R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to
- * work with NTSC, too */
- R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0,
- * VTRC=1, HPLL=0, VNOI=0 */
- R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0,
- * VBLB=0, UPTCV=0, APER=1 */
- R_0A_LUMA_BRIGHT_CNTL, 0x80,
- R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */
- R_0C_CHROMA_SAT_CNTL, 0x40,
- R_0D_CHROMA_HUE_CNTL, 0x00,
- R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
- * FCTC=0, CHBW=1 */
- R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */
- R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
- R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
- * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
- R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */
- R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */
- R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
- R_15_VGATE_START_FID_CHG, 0x00,
- R_16_VGATE_STOP, 0x00,
- R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
-
- 0x00, 0x00
-};
-
-/* SAA7113 init codes */
-static const unsigned char saa7113_init[] = {
- R_01_INC_DELAY, 0x08,
- R_02_INPUT_CNTL_1, 0xc2,
- R_03_INPUT_CNTL_2, 0x30,
- R_04_INPUT_CNTL_3, 0x00,
- R_05_INPUT_CNTL_4, 0x00,
- R_06_H_SYNC_START, 0x89,
- R_07_H_SYNC_STOP, 0x0d,
- R_08_SYNC_CNTL, 0x88,
- R_09_LUMA_CNTL, 0x01,
- R_0A_LUMA_BRIGHT_CNTL, 0x80,
- R_0B_LUMA_CONTRAST_CNTL, 0x47,
- R_0C_CHROMA_SAT_CNTL, 0x40,
- R_0D_CHROMA_HUE_CNTL, 0x00,
- R_0E_CHROMA_CNTL_1, 0x01,
- R_0F_CHROMA_GAIN_CNTL, 0x2a,
- R_10_CHROMA_CNTL_2, 0x08,
- R_11_MODE_DELAY_CNTL, 0x0c,
- R_12_RT_SIGNAL_CNTL, 0x07,
- R_13_RT_X_PORT_OUT_CNTL, 0x00,
- R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
- R_15_VGATE_START_FID_CHG, 0x00,
- R_16_VGATE_STOP, 0x00,
- R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
-
- 0x00, 0x00
-};
-
-/* If a value differs from the Hauppauge driver values, then the comment starts with
- 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
- Hauppauge driver sets. */
-
-/* SAA7114 and SAA7115 initialization table */
-static const unsigned char saa7115_init_auto_input[] = {
- /* Front-End Part */
- R_01_INC_DELAY, 0x48, /* white peak control disabled */
- R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */
- R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */
- R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */
- /* Decoder Part */
- R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */
- R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */
- R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
- R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
- R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
- R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
- R_0D_CHROMA_HUE_CNTL, 0x00,
- R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */
- R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */
- R_11_MODE_DELAY_CNTL, 0x00,
- R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */
- R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */
- R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
- R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */
- R_19_RAW_DATA_OFF_CNTL, 0x80,
- R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */
- R_1B_MISC_TVVCRDET, 0x42, /* recommended value */
- R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */
- R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */
-
-
- R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */
-
- /* Power Device Control */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */
- 0x00, 0x00
-};
-
-/* Used to reset saa7113, saa7114 and saa7115 */
-static const unsigned char saa7115_cfg_reset_scaler[] = {
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */
- 0x00, 0x00
-};
-
-/* ============== SAA7715 VIDEO templates ============= */
-
-static const unsigned char saa7115_cfg_60hz_video[] = {
- R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
-
- R_15_VGATE_START_FID_CHG, 0x03,
- R_16_VGATE_STOP, 0x11,
- R_17_MISC_VGATE_CONF_AND_MSB, 0x9c,
-
- R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
- R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
-
- R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */
-
- /* Task A */
- R_90_A_TASK_HANDLING_CNTL, 0x80,
- R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
- R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
- R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
-
- /* hoffset low (input), 0x0002 is minimum */
- R_94_A_HORIZ_INPUT_WINDOW_START, 0x01,
- R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
-
- /* hsize low (input), 0x02d0 = 720 */
- R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
- R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
-
- R_98_A_VERT_INPUT_WINDOW_START, 0x05,
- R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
-
- R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c,
- R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
-
- R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
- R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,
-
- R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c,
- R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,
-
- /* Task B */
- R_C0_B_TASK_HANDLING_CNTL, 0x00,
- R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
- R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
- R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
-
- /* 0x0002 is minimum */
- R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02,
- R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
-
- /* 0x02d0 = 720 */
- R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
- R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
-
- /* vwindow start 0x12 = 18 */
- R_C8_B_VERT_INPUT_WINDOW_START, 0x12,
- R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
-
- /* vwindow length 0xf8 = 248 */
- R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1,
- R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9,
-
- /* hwindow 0x02d0 = 720 */
- R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
- R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
-
- R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
- R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */
- R_F5_PULSGEN_LINE_LENGTH, 0xad,
- R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
-
- 0x00, 0x00
-};
-
-static const unsigned char saa7115_cfg_50hz_video[] = {
- R_80_GLOBAL_CNTL_1, 0x00,
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
-
- R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */
- R_16_VGATE_STOP, 0x16,
- R_17_MISC_VGATE_CONF_AND_MSB, 0x99,
-
- R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
- R_0E_CHROMA_CNTL_1, 0x07,
-
- R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */
-
- /* Task A */
- R_90_A_TASK_HANDLING_CNTL, 0x81,
- R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
- R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
- R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
-
- /* This is weird: the datasheet says that you should use 2 as the minimum value, */
- /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
- /* hoffset low (input), 0x0002 is minimum */
- R_94_A_HORIZ_INPUT_WINDOW_START, 0x00,
- R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
-
- /* hsize low (input), 0x02d0 = 720 */
- R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
- R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
-
- R_98_A_VERT_INPUT_WINDOW_START, 0x03,
- R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
-
- /* vsize 0x12 = 18 */
- R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12,
- R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
-
- /* hsize 0x05a0 = 1440 */
- R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
- R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */
- R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */
- R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */
-
- /* Task B */
- R_C0_B_TASK_HANDLING_CNTL, 0x00,
- R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
- R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
- R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
-
- /* This is weird: the datasheet says that you should use 2 as the minimum value, */
- /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
- /* hoffset low (input), 0x0002 is minimum. See comment above. */
- R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00,
- R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
-
- /* hsize 0x02d0 = 720 */
- R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
- R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
-
- /* voffset 0x16 = 22 */
- R_C8_B_VERT_INPUT_WINDOW_START, 0x16,
- R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
-
- /* vsize 0x0120 = 288 */
- R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20,
- R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01,
-
- /* hsize 0x02d0 = 720 */
- R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
- R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
-
- R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
- R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */
- R_F5_PULSGEN_LINE_LENGTH, 0xb0,
- R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
-
- 0x00, 0x00
-};
-
-/* ============== SAA7715 VIDEO templates (end) ======= */
-
-static const unsigned char saa7115_cfg_vbi_on[] = {
- R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
- R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
-
- 0x00, 0x00
-};
-
-static const unsigned char saa7115_cfg_vbi_off[] = {
- R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
- R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
-
- 0x00, 0x00
-};
-
-
-static const unsigned char saa7115_init_misc[] = {
- R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01,
- R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01,
- R_84_I_PORT_SIGNAL_DEF, 0x20,
- R_85_I_PORT_SIGNAL_POLAR, 0x21,
- R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5,
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,
-
- /* Task A */
- R_A0_A_HORIZ_PRESCALING, 0x01,
- R_A1_A_ACCUMULATION_LENGTH, 0x00,
- R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
-
- /* Configure controls at nominal value*/
- R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80,
- R_A5_A_LUMA_CONTRAST_CNTL, 0x40,
- R_A6_A_CHROMA_SATURATION_CNTL, 0x40,
-
- /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
- R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00,
- R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02,
-
- R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00,
-
- /* must be horiz lum scaling / 2 */
- R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00,
- R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01,
-
- /* must be offset luma / 2 */
- R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00,
-
- R_B0_A_VERT_LUMA_SCALING_INC, 0x00,
- R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04,
-
- R_B2_A_VERT_CHROMA_SCALING_INC, 0x00,
- R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04,
-
- R_B4_A_VERT_SCALING_MODE_CNTL, 0x01,
-
- R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00,
- R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00,
- R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00,
- R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00,
-
- R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00,
- R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00,
- R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00,
- R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00,
-
- /* Task B */
- R_D0_B_HORIZ_PRESCALING, 0x01,
- R_D1_B_ACCUMULATION_LENGTH, 0x00,
- R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
-
- /* Configure controls at nominal value*/
- R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80,
- R_D5_B_LUMA_CONTRAST_CNTL, 0x40,
- R_D6_B_CHROMA_SATURATION_CNTL, 0x40,
-
- /* hor lum scaling 0x0400 = 1 */
- R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00,
- R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04,
-
- R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00,
-
- /* must be hor lum scaling / 2 */
- R_DC_B_HORIZ_CHROMA_SCALING, 0x00,
- R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02,
-
- /* must be offset luma / 2 */
- R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00,
-
- R_E0_B_VERT_LUMA_SCALING_INC, 0x00,
- R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04,
-
- R_E2_B_VERT_CHROMA_SCALING_INC, 0x00,
- R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04,
-
- R_E4_B_VERT_SCALING_MODE_CNTL, 0x01,
-
- R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00,
- R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00,
- R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00,
- R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00,
-
- R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00,
- R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00,
- R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00,
- R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00,
-
- R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
- R_F3_PLL_INCREMENT, 0x46,
- R_F4_PLL2_STATUS, 0x00,
- R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */
- R_F8_PULSE_B_POS, 0x00,
- R_F9_PULSE_B_POS_MSB, 0x4b,
- R_FA_PULSE_C_POS, 0x00,
- R_FB_PULSE_C_POS_MSB, 0x4b,
-
- /* PLL2 lock detection settings: 71 lines 50% phase error */
- R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88,
-
- /* Turn off VBI */
- R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */
- R_41_LCR_BASE, 0xff,
- R_41_LCR_BASE+1, 0xff,
- R_41_LCR_BASE+2, 0xff,
- R_41_LCR_BASE+3, 0xff,
- R_41_LCR_BASE+4, 0xff,
- R_41_LCR_BASE+5, 0xff,
- R_41_LCR_BASE+6, 0xff,
- R_41_LCR_BASE+7, 0xff,
- R_41_LCR_BASE+8, 0xff,
- R_41_LCR_BASE+9, 0xff,
- R_41_LCR_BASE+10, 0xff,
- R_41_LCR_BASE+11, 0xff,
- R_41_LCR_BASE+12, 0xff,
- R_41_LCR_BASE+13, 0xff,
- R_41_LCR_BASE+14, 0xff,
- R_41_LCR_BASE+15, 0xff,
- R_41_LCR_BASE+16, 0xff,
- R_41_LCR_BASE+17, 0xff,
- R_41_LCR_BASE+18, 0xff,
- R_41_LCR_BASE+19, 0xff,
- R_41_LCR_BASE+20, 0xff,
- R_41_LCR_BASE+21, 0xff,
- R_41_LCR_BASE+22, 0xff,
- R_58_PROGRAM_FRAMING_CODE, 0x40,
- R_59_H_OFF_FOR_SLICER, 0x47,
- R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83,
- R_5D_DID, 0xbd,
- R_5E_SDID, 0x35,
-
- R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */
-
- R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
- R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,
- 0x00, 0x00
-};
-
-static int saa711x_odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
-static int saa711x_decode_vps(u8 *dst, u8 *p)
-{
- static const u8 biphase_tbl[] = {
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
- 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
- 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
- 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
- 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
- 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
- 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
- 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
- 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
- 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
- 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
- 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
- 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
- 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
- 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
- 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
- };
- int i;
- u8 c, err = 0;
-
- for (i = 0; i < 2 * 13; i += 2) {
- err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
- c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4);
- dst[i / 2] = c;
- }
- return err & 0xf0;
-}
-
-static int saa711x_decode_wss(u8 *p)
-{
- static const int wss_bits[8] = {
- 0, 0, 0, 1, 0, 1, 1, 1
- };
- unsigned char parity;
- int wss = 0;
- int i;
-
- for (i = 0; i < 16; i++) {
- int b1 = wss_bits[p[i] & 7];
- int b2 = wss_bits[(p[i] >> 3) & 7];
-
- if (b1 == b2)
- return -1;
- wss |= b2 << i;
- }
- parity = wss & 15;
- parity ^= parity >> 2;
- parity ^= parity >> 1;
-
- if (!(parity & 1))
- return -1;
-
- return wss;
-}
-
-static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
-{
- struct saa711x_state *state = to_state(sd);
- u32 acpf;
- u32 acni;
- u32 hz;
- u64 f;
- u8 acc = 0; /* reg 0x3a, audio clock control */
-
- /* Checks for chips that don't have audio clock (saa7111, saa7113) */
- if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
- return 0;
-
- v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
-
- /* sanity check */
- if (freq < 32000 || freq > 48000)
- return -EINVAL;
-
- /* hz is the refresh rate times 100 */
- hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
- /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
- acpf = (25600 * freq) / hz;
- /* acni = (256 * freq * 2^23) / crystal_frequency =
- (freq * 2^(8+23)) / crystal_frequency =
- (freq << 31) / crystal_frequency */
- f = freq;
- f = f << 31;
- do_div(f, state->crystal_freq);
- acni = f;
- if (state->ucgc) {
- acpf = acpf * state->cgcdiv / 16;
- acni = acni * state->cgcdiv / 16;
- acc = 0x80;
- if (state->cgcdiv == 3)
- acc |= 0x40;
- }
- if (state->apll)
- acc |= 0x08;
-
- saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
- saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
- saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
-
- saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
- saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
- (acpf >> 8) & 0xff);
- saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
- (acpf >> 16) & 0x03);
-
- saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
- saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
- saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
- state->audclk_freq = freq;
- return 0;
-}
-
-static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = to_sd(ctrl);
- struct saa711x_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_CHROMA_AGC:
- /* chroma gain cluster */
- if (state->agc->val)
- state->gain->val =
- saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
- break;
- }
- return 0;
-}
-
-static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = to_sd(ctrl);
- struct saa711x_state *state = to_state(sd);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val);
- break;
-
- case V4L2_CID_CONTRAST:
- saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val);
- break;
-
- case V4L2_CID_SATURATION:
- saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val);
- break;
-
- case V4L2_CID_HUE:
- saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val);
- break;
-
- case V4L2_CID_CHROMA_AGC:
- /* chroma gain cluster */
- if (state->agc->val)
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
- else
- saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
-{
- struct saa711x_state *state = to_state(sd);
- int HPSC, HFSC;
- int VSCY;
- int res;
- int is_50hz = state->std & V4L2_STD_625_50;
- int Vsrc = is_50hz ? 576 : 480;
-
- v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
-
- /* FIXME need better bounds checking here */
- if ((width < 1) || (width > 1440))
- return -EINVAL;
- if ((height < 1) || (height > Vsrc))
- return -EINVAL;
-
- if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
- /* Decoder only supports 720 columns and 480 or 576 lines */
- if (width != 720)
- return -EINVAL;
- if (height != Vsrc)
- return -EINVAL;
- }
-
- state->width = width;
- state->height = height;
-
- if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH))
- return 0;
-
- /* probably have a valid size, let's set it */
- /* Set output width/height */
- /* width */
-
- saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
- (u8) (width & 0xff));
- saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
- (u8) ((width >> 8) & 0xff));
-
- /* Vertical Scaling uses height/2 */
- res = height / 2;
-
- /* On 60Hz, it is using a higher Vertical Output Size */
- if (!is_50hz)
- res += (VRES_60HZ - 480) >> 1;
-
- /* height */
- saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
- (u8) (res & 0xff));
- saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
- (u8) ((res >> 8) & 0xff));
-
- /* Scaling settings */
- /* Hprescaler is floor(inres/outres) */
- HPSC = (int)(720 / width);
- /* 0 is not allowed (div. by zero) */
- HPSC = HPSC ? HPSC : 1;
- HFSC = (int)((1024 * 720) / (HPSC * width));
- /* FIXME hardcodes to "Task B"
- * write H prescaler integer */
- saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
- (u8) (HPSC & 0x3f));
-
- v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
- /* write H fine-scaling (luminance) */
- saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
- (u8) (HFSC & 0xff));
- saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
- (u8) ((HFSC >> 8) & 0xff));
- /* write H fine-scaling (chrominance)
- * must be lum/2, so i'll just bitshift :) */
- saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
- (u8) ((HFSC >> 1) & 0xff));
- saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
- (u8) ((HFSC >> 9) & 0xff));
-
- VSCY = (int)((1024 * Vsrc) / height);
- v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
-
- /* Correct Contrast and Luminance */
- saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
- (u8) (64 * 1024 / VSCY));
- saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
- (u8) (64 * 1024 / VSCY));
-
- /* write V fine-scaling (luminance) */
- saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
- (u8) (VSCY & 0xff));
- saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
- (u8) ((VSCY >> 8) & 0xff));
- /* write V fine-scaling (chrominance) */
- saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
- (u8) (VSCY & 0xff));
- saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
- (u8) ((VSCY >> 8) & 0xff));
-
- saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
-
- /* Activates task "B" */
- saa711x_write(sd, R_80_GLOBAL_CNTL_1,
- saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
-
- return 0;
-}
-
-static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct saa711x_state *state = to_state(sd);
-
- /* Prevent unnecessary standard changes. During a standard
- change the I-Port is temporarily disabled. Any devices
- reading from that port can get confused.
- Note that s_std is also used to switch from
- radio to TV mode, so if a s_std is broadcast to
- all I2C devices then you do not want to have an unwanted
- side-effect here. */
- if (std == state->std)
- return;
-
- state->std = std;
-
- // This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
- if (std & V4L2_STD_525_60) {
- v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
- saa711x_writeregs(sd, saa7115_cfg_60hz_video);
- saa711x_set_size(sd, 720, 480);
- } else {
- v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
- saa711x_writeregs(sd, saa7115_cfg_50hz_video);
- saa711x_set_size(sd, 720, 576);
- }
-
- /* Register 0E - Bits D6-D4 on NO-AUTO mode
- (SAA7111 and SAA7113 doesn't have auto mode)
- 50 Hz / 625 lines 60 Hz / 525 lines
- 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz)
- 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz)
- 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz)
- 011 NTSC N (3.58MHz) PAL M (3.58MHz)
- 100 reserved NTSC-Japan (3.58MHz)
- */
- if (state->ident <= V4L2_IDENT_SAA7113) {
- u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
-
- if (std == V4L2_STD_PAL_M) {
- reg |= 0x30;
- } else if (std == V4L2_STD_PAL_Nc) {
- reg |= 0x20;
- } else if (std == V4L2_STD_PAL_60) {
- reg |= 0x10;
- } else if (std == V4L2_STD_NTSC_M_JP) {
- reg |= 0x40;
- } else if (std & V4L2_STD_SECAM) {
- reg |= 0x50;
- }
- saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
- } else {
- /* restart task B if needed */
- int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
-
- if (taskb && state->ident == V4L2_IDENT_SAA7114) {
- saa711x_writeregs(sd, saa7115_cfg_vbi_on);
- }
-
- /* switch audio mode too! */
- saa711x_s_clock_freq(sd, state->audclk_freq);
- }
-}
-
-/* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
-{
- struct saa711x_state *state = to_state(sd);
- int is_50hz = (state->std & V4L2_STD_625_50);
- u8 lcr[24];
- int i, x;
-
-#if 1
- /* saa7113/7114/7118 VBI support are experimental */
- if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
- return;
-
-#else
- /* SAA7113 and SAA7118 also should support VBI - Need testing */
- if (state->ident != V4L2_IDENT_SAA7115)
- return;
-#endif
-
- for (i = 0; i <= 23; i++)
- lcr[i] = 0xff;
-
- if (fmt == NULL) {
- /* raw VBI */
- if (is_50hz)
- for (i = 6; i <= 23; i++)
- lcr[i] = 0xdd;
- else
- for (i = 10; i <= 21; i++)
- lcr[i] = 0xdd;
- } else {
- /* sliced VBI */
- /* first clear lines that cannot be captured */
- if (is_50hz) {
- for (i = 0; i <= 5; i++)
- fmt->service_lines[0][i] =
- fmt->service_lines[1][i] = 0;
- }
- else {
- for (i = 0; i <= 9; i++)
- fmt->service_lines[0][i] =
- fmt->service_lines[1][i] = 0;
- for (i = 22; i <= 23; i++)
- fmt->service_lines[0][i] =
- fmt->service_lines[1][i] = 0;
- }
-
- /* Now set the lcr values according to the specified service */
- for (i = 6; i <= 23; i++) {
- lcr[i] = 0;
- for (x = 0; x <= 1; x++) {
- switch (fmt->service_lines[1-x][i]) {
- case 0:
- lcr[i] |= 0xf << (4 * x);
- break;
- case V4L2_SLICED_TELETEXT_B:
- lcr[i] |= 1 << (4 * x);
- break;
- case V4L2_SLICED_CAPTION_525:
- lcr[i] |= 4 << (4 * x);
- break;
- case V4L2_SLICED_WSS_625:
- lcr[i] |= 5 << (4 * x);
- break;
- case V4L2_SLICED_VPS:
- lcr[i] |= 7 << (4 * x);
- break;
- }
- }
- }
- }
-
- /* write the lcr registers */
- for (i = 2; i <= 23; i++) {
- saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
- }
-
- /* enable/disable raw VBI capturing */
- saa711x_writeregs(sd, fmt == NULL ?
- saa7115_cfg_vbi_on :
- saa7115_cfg_vbi_off);
-}
-
-static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced)
-{
- static u16 lcr2vbi[] = {
- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
- 0, V4L2_SLICED_CAPTION_525, /* 4 */
- V4L2_SLICED_WSS_625, 0, /* 5 */
- V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */
- 0, 0, 0, 0
- };
- int i;
-
- memset(sliced, 0, sizeof(*sliced));
- /* done if using raw VBI */
- if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
- return 0;
- for (i = 2; i <= 23; i++) {
- u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
-
- sliced->service_lines[0][i] = lcr2vbi[v >> 4];
- sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
- sliced->service_set |=
- sliced->service_lines[0][i] | sliced->service_lines[1][i];
- }
- return 0;
-}
-
-static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
-{
- saa711x_set_lcr(sd, NULL);
- return 0;
-}
-
-static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
-{
- saa711x_set_lcr(sd, fmt);
- return 0;
-}
-
-static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
-{
- if (fmt->code != V4L2_MBUS_FMT_FIXED)
- return -EINVAL;
- fmt->field = V4L2_FIELD_INTERLACED;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
- return saa711x_set_size(sd, fmt->width, fmt->height);
-}
-
-/* Decode the sliced VBI data stream as created by the saa7115.
- The format is described in the saa7115 datasheet in Tables 25 and 26
- and in Figure 33.
- The current implementation uses SAV/EAV codes and not the ancillary data
- headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
- code. */
-static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
-{
- struct saa711x_state *state = to_state(sd);
- static const char vbi_no_data_pattern[] = {
- 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
- };
- u8 *p = vbi->p;
- u32 wss;
- int id1, id2; /* the ID1 and ID2 bytes from the internal header */
-
- vbi->type = 0; /* mark result as a failure */
- id1 = p[2];
- id2 = p[3];
- /* Note: the field bit is inverted for 60 Hz video */
- if (state->std & V4L2_STD_525_60)
- id1 ^= 0x40;
-
- /* Skip internal header, p now points to the start of the payload */
- p += 4;
- vbi->p = p;
-
- /* calculate field and line number of the VBI packet (1-23) */
- vbi->is_second_field = ((id1 & 0x40) != 0);
- vbi->line = (id1 & 0x3f) << 3;
- vbi->line |= (id2 & 0x70) >> 4;
-
- /* Obtain data type */
- id2 &= 0xf;
-
- /* If the VBI slicer does not detect any signal it will fill up
- the payload buffer with 0xa0 bytes. */
- if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
- return 0;
-
- /* decode payloads */
- switch (id2) {
- case 1:
- vbi->type = V4L2_SLICED_TELETEXT_B;
- break;
- case 4:
- if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
- return 0;
- vbi->type = V4L2_SLICED_CAPTION_525;
- break;
- case 5:
- wss = saa711x_decode_wss(p);
- if (wss == -1)
- return 0;
- p[0] = wss & 0xff;
- p[1] = wss >> 8;
- vbi->type = V4L2_SLICED_WSS_625;
- break;
- case 7:
- if (saa711x_decode_vps(p, p) != 0)
- return 0;
- vbi->type = V4L2_SLICED_VPS;
- break;
- default:
- break;
- }
- return 0;
-}
-
-/* ============ SAA7115 AUDIO settings (end) ============= */
-
-static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- struct saa711x_state *state = to_state(sd);
- int status;
-
- if (state->radio)
- return 0;
- status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
-
- v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
- vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
- return 0;
-}
-
-static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct saa711x_state *state = to_state(sd);
-
- state->radio = 0;
- saa711x_set_v4lstd(sd, std);
- return 0;
-}
-
-static int saa711x_s_radio(struct v4l2_subdev *sd)
-{
- struct saa711x_state *state = to_state(sd);
-
- state->radio = 1;
- return 0;
-}
-
-static int saa711x_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct saa711x_state *state = to_state(sd);
- u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
-
- v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
- input, output);
-
- /* saa7111/3 does not have these inputs */
- if (state->ident <= V4L2_IDENT_SAA7113 &&
- (input == SAA7115_COMPOSITE4 ||
- input == SAA7115_COMPOSITE5)) {
- return -EINVAL;
- }
- if (input > SAA7115_SVIDEO3)
- return -EINVAL;
- if (state->input == input && state->output == output)
- return 0;
- v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
- (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
- (output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
- state->input = input;
-
- /* saa7111 has slightly different input numbering */
- if (state->ident <= V4L2_IDENT_SAA7111A) {
- if (input >= SAA7115_COMPOSITE4)
- input -= 2;
- /* saa7111 specific */
- saa711x_write(sd, R_10_CHROMA_CNTL_2,
- (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
- ((output & 0xc0) ^ 0x40));
- saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
- (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
- ((output & 2) ? 0x0a : 0));
- }
-
- /* select mode */
- saa711x_write(sd, R_02_INPUT_CNTL_1,
- (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
- input);
-
- /* bypass chrominance trap for S-Video modes */
- saa711x_write(sd, R_09_LUMA_CNTL,
- (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
- (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
-
- state->output = output;
- if (state->ident == V4L2_IDENT_SAA7114 ||
- state->ident == V4L2_IDENT_SAA7115) {
- saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
- (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
- (state->output & 0x01));
- }
- return 0;
-}
-
-static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
-{
- struct saa711x_state *state = to_state(sd);
-
- if (state->ident > V4L2_IDENT_SAA7111A)
- return -EINVAL;
- saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
- (val ? 0x80 : 0));
- return 0;
-}
-
-static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct saa711x_state *state = to_state(sd);
-
- v4l2_dbg(1, debug, sd, "%s output\n",
- enable ? "enable" : "disable");
-
- if (state->enable == enable)
- return 0;
- state->enable = enable;
- if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
- return 0;
- saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
- return 0;
-}
-
-static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags)
-{
- struct saa711x_state *state = to_state(sd);
-
- if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ)
- return -EINVAL;
- state->crystal_freq = freq;
- state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
- state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
- state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
- saa711x_s_clock_freq(sd, state->audclk_freq);
- return 0;
-}
-
-static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
-{
- v4l2_dbg(1, debug, sd, "decoder RESET\n");
- saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
- return 0;
-}
-
-static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
-{
- /* Note: the internal field ID is inverted for NTSC,
- so data->field 0 maps to the saa7115 even field,
- whereas for PAL it maps to the saa7115 odd field. */
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- if (saa711x_read(sd, 0x6b) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(sd, 0x6c);
- data->data[1] = saa711x_read(sd, 0x6d);
- return 0;
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0) {
- /* CC */
- if (saa711x_read(sd, 0x66) & 0x30)
- return -EIO;
- data->data[0] = saa711x_read(sd, 0x69);
- data->data[1] = saa711x_read(sd, 0x6a);
- return 0;
- }
- /* XDS */
- if (saa711x_read(sd, 0x66) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(sd, 0x67);
- data->data[1] = saa711x_read(sd, 0x68);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
-{
- struct saa711x_state *state = to_state(sd);
- int reg1f, reg1e;
-
- /*
- * The V4L2 core already initializes std with all supported
- * Standards. All driver needs to do is to mask it, to remove
- * standards that don't apply from the mask
- */
-
- reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
-
- /* horizontal/vertical not locked */
- if (reg1f & 0x40)
- goto ret;
-
- if (reg1f & 0x20)
- *std &= V4L2_STD_525_60;
- else
- *std &= V4L2_STD_625_50;
-
- if (state->ident != V4L2_IDENT_SAA7115)
- goto ret;
-
- reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
-
- switch (reg1e & 0x03) {
- case 1:
- *std &= V4L2_STD_NTSC;
- break;
- case 2:
- /*
- * V4L2_STD_PAL just cover the european PAL standards.
- * This is wrong, as the device could also be using an
- * other PAL standard.
- */
- *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc |
- V4L2_STD_PAL_M | V4L2_STD_PAL_60;
- break;
- case 3:
- *std &= V4L2_STD_SECAM;
- break;
- default:
- /* Can't detect anything */
- break;
- }
-
- v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
-
-ret:
- v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std);
-
- return 0;
-}
-
-static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
-{
- struct saa711x_state *state = to_state(sd);
- int reg1e = 0x80;
- int reg1f;
-
- *status = V4L2_IN_ST_NO_SIGNAL;
- if (state->ident == V4L2_IDENT_SAA7115)
- reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
- reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
- *status = 0;
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = saa711x_read(sd, reg->reg & 0xff);
- reg->size = 1;
- return 0;
-}
-
-static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
- return 0;
-}
-#endif
-
-static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct saa711x_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
-}
-
-static int saa711x_log_status(struct v4l2_subdev *sd)
-{
- struct saa711x_state *state = to_state(sd);
- int reg1e, reg1f;
- int signalOk;
- int vcr;
-
- v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
- if (state->ident != V4L2_IDENT_SAA7115) {
- /* status for the saa7114 */
- reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- signalOk = (reg1f & 0xc1) == 0x81;
- v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
- v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
- return 0;
- }
-
- /* status for the saa7115 */
- reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
- reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
-
- signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
- vcr = !(reg1f & 0x10);
-
- if (state->input >= 6)
- v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
- else
- v4l2_info(sd, "Input: Composite %d\n", state->input);
- v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
- v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-
- switch (reg1e & 0x03) {
- case 1:
- v4l2_info(sd, "Detected format: NTSC\n");
- break;
- case 2:
- v4l2_info(sd, "Detected format: PAL\n");
- break;
- case 3:
- v4l2_info(sd, "Detected format: SECAM\n");
- break;
- default:
- v4l2_info(sd, "Detected format: BW/No color\n");
- break;
- }
- v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
- v4l2_ctrl_handler_log_status(&state->hdl, sd->name);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
- .s_ctrl = saa711x_s_ctrl,
- .g_volatile_ctrl = saa711x_g_volatile_ctrl,
-};
-
-static const struct v4l2_subdev_core_ops saa711x_core_ops = {
- .log_status = saa711x_log_status,
- .g_chip_ident = saa711x_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,
- .s_std = saa711x_s_std,
- .reset = saa711x_reset,
- .s_gpio = saa711x_s_gpio,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = saa711x_g_register,
- .s_register = saa711x_s_register,
-#endif
-};
-
-static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
- .s_radio = saa711x_s_radio,
- .g_tuner = saa711x_g_tuner,
-};
-
-static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
- .s_clock_freq = saa711x_s_clock_freq,
-};
-
-static const struct v4l2_subdev_video_ops saa711x_video_ops = {
- .s_routing = saa711x_s_routing,
- .s_crystal_freq = saa711x_s_crystal_freq,
- .s_mbus_fmt = saa711x_s_mbus_fmt,
- .s_stream = saa711x_s_stream,
- .querystd = saa711x_querystd,
- .g_input_status = saa711x_g_input_status,
-};
-
-static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = {
- .g_vbi_data = saa711x_g_vbi_data,
- .decode_vbi_line = saa711x_decode_vbi_line,
- .g_sliced_fmt = saa711x_g_sliced_fmt,
- .s_sliced_fmt = saa711x_s_sliced_fmt,
- .s_raw_fmt = saa711x_s_raw_fmt,
-};
-
-static const struct v4l2_subdev_ops saa711x_ops = {
- .core = &saa711x_core_ops,
- .tuner = &saa711x_tuner_ops,
- .audio = &saa711x_audio_ops,
- .video = &saa711x_video_ops,
- .vbi = &saa711x_vbi_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static int saa711x_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct saa711x_state *state;
- struct v4l2_subdev *sd;
- struct v4l2_ctrl_handler *hdl;
- int i;
- char name[17];
- char chip_id;
- int autodetect = !id || id->driver_data == 1;
-
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -EIO;
-
- for (i = 0; i < 0x0f; i++) {
- i2c_smbus_write_byte_data(client, 0, i);
- name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
- if (name[i] > '9')
- name[i] += 'a' - '9' - 1;
- }
- name[i] = '\0';
-
- chip_id = name[5];
-
- /* Check whether this chip is part of the saa711x series */
- if (memcmp(name + 1, "f711", 4)) {
- v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
- client->addr << 1, name);
- return -ENODEV;
- }
-
- /* Safety check */
- if (!autodetect && id->name[6] != chip_id) {
- v4l_warn(client, "found saa711%c while %s was expected\n",
- chip_id, id->name);
- }
- snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
- v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
- client->addr << 1, client->adapter->name);
-
- state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
- if (state == NULL)
- return -ENOMEM;
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
-
- hdl = &state->hdl;
- v4l2_ctrl_handler_init(hdl, 6);
- /* add in ascending ID order */
- v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
- v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_CONTRAST, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_SATURATION, 0, 127, 1, 64);
- v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_HUE, -128, 127, 1, 0);
- state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
- state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops,
- V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40);
- sd->ctrl_handler = hdl;
- if (hdl->error) {
- int err = hdl->error;
-
- v4l2_ctrl_handler_free(hdl);
- kfree(state);
- return err;
- }
- v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
-
- state->input = -1;
- state->output = SAA7115_IPORT_ON;
- state->enable = 1;
- state->radio = 0;
- switch (chip_id) {
- case '1':
- state->ident = V4L2_IDENT_SAA7111;
- if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
- v4l_info(client, "saa7111a variant found\n");
- state->ident = V4L2_IDENT_SAA7111A;
- }
- break;
- case '3':
- state->ident = V4L2_IDENT_SAA7113;
- break;
- case '4':
- state->ident = V4L2_IDENT_SAA7114;
- break;
- case '5':
- state->ident = V4L2_IDENT_SAA7115;
- break;
- case '8':
- state->ident = V4L2_IDENT_SAA7118;
- break;
- default:
- state->ident = V4L2_IDENT_SAA7111;
- v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
- break;
- }
-
- state->audclk_freq = 48000;
-
- v4l2_dbg(1, debug, sd, "writing init values\n");
-
- /* init to 60hz/48khz */
- state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
- switch (state->ident) {
- case V4L2_IDENT_SAA7111:
- case V4L2_IDENT_SAA7111A:
- saa711x_writeregs(sd, saa7111_init);
- break;
- case V4L2_IDENT_SAA7113:
- saa711x_writeregs(sd, saa7113_init);
- break;
- default:
- state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
- saa711x_writeregs(sd, saa7115_init_auto_input);
- }
- if (state->ident > V4L2_IDENT_SAA7111A)
- saa711x_writeregs(sd, saa7115_init_misc);
- saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
- v4l2_ctrl_handler_setup(hdl);
-
- v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
- saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
- saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa711x_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- v4l2_ctrl_handler_free(sd->ctrl_handler);
- kfree(to_state(sd));
- return 0;
-}
-
-static const struct i2c_device_id saa711x_id[] = {
- { "saa7115_auto", 1 }, /* autodetect */
- { "saa7111", 0 },
- { "saa7113", 0 },
- { "saa7114", 0 },
- { "saa7115", 0 },
- { "saa7118", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, saa711x_id);
-
-static struct i2c_driver saa711x_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "saa7115",
- },
- .probe = saa711x_probe,
- .remove = saa711x_remove,
- .id_table = saa711x_id,
-};
-
-module_i2c_driver(saa711x_driver);
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
deleted file mode 100644
index 39c90b08eea8..000000000000
--- a/drivers/media/video/saa7127.c
+++ /dev/null
@@ -1,855 +0,0 @@
-/*
- * saa7127 - Philips SAA7127/SAA7129 video encoder driver
- *
- * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl>
- *
- * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter
- *
- * Copyright (C) 2000-2001 Gillem <htoa@gmx.net>
- * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de>
- *
- * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo
- *
- * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org>
- *
- * This driver is designed for the Hauppauge 250/350 Linux driver
- * from the ivtv Project
- *
- * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com>
- *
- * Dual output support:
- * Copyright (C) 2004 Eric Varsanyi
- *
- * NTSC Tuning and 7.5 IRE Setup
- * Copyright (C) 2004 Chris Kennedy <c@groovy.org>
- *
- * VBI additions & cleanup:
- * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: the saa7126 is identical to the saa7127, and the saa7128 is
- * identical to the saa7129, except that the saa7126 and saa7128 have
- * macrovision anti-taping support. This driver will almost certainly
- * work fine for those chips, except of course for the missing anti-taping
- * support.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/saa7127.h>
-
-static int debug;
-static int test_image;
-
-MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver");
-MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil");
-MODULE_LICENSE("GPL");
-module_param(debug, int, 0644);
-module_param(test_image, int, 0644);
-MODULE_PARM_DESC(debug, "debug level (0-2)");
-MODULE_PARM_DESC(test_image, "test_image (0-1)");
-
-
-/*
- * SAA7127 registers
- */
-
-#define SAA7127_REG_STATUS 0x00
-#define SAA7127_REG_WIDESCREEN_CONFIG 0x26
-#define SAA7127_REG_WIDESCREEN_ENABLE 0x27
-#define SAA7127_REG_BURST_START 0x28
-#define SAA7127_REG_BURST_END 0x29
-#define SAA7127_REG_COPYGEN_0 0x2a
-#define SAA7127_REG_COPYGEN_1 0x2b
-#define SAA7127_REG_COPYGEN_2 0x2c
-#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d
-#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38
-#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39
-#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a
-#define SAA7129_REG_FADE_KEY_COL2 0x4f
-#define SAA7127_REG_CHROMA_PHASE 0x5a
-#define SAA7127_REG_GAINU 0x5b
-#define SAA7127_REG_GAINV 0x5c
-#define SAA7127_REG_BLACK_LEVEL 0x5d
-#define SAA7127_REG_BLANKING_LEVEL 0x5e
-#define SAA7127_REG_VBI_BLANKING 0x5f
-#define SAA7127_REG_DAC_CONTROL 0x61
-#define SAA7127_REG_BURST_AMP 0x62
-#define SAA7127_REG_SUBC3 0x63
-#define SAA7127_REG_SUBC2 0x64
-#define SAA7127_REG_SUBC1 0x65
-#define SAA7127_REG_SUBC0 0x66
-#define SAA7127_REG_LINE_21_ODD_0 0x67
-#define SAA7127_REG_LINE_21_ODD_1 0x68
-#define SAA7127_REG_LINE_21_EVEN_0 0x69
-#define SAA7127_REG_LINE_21_EVEN_1 0x6a
-#define SAA7127_REG_RCV_PORT_CONTROL 0x6b
-#define SAA7127_REG_VTRIG 0x6c
-#define SAA7127_REG_HTRIG_HI 0x6d
-#define SAA7127_REG_MULTI 0x6e
-#define SAA7127_REG_CLOSED_CAPTION 0x6f
-#define SAA7127_REG_RCV2_OUTPUT_START 0x70
-#define SAA7127_REG_RCV2_OUTPUT_END 0x71
-#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72
-#define SAA7127_REG_TTX_REQUEST_H_START 0x73
-#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74
-#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75
-#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76
-#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77
-#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78
-#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79
-#define SAA7127_REG_FIRST_ACTIVE 0x7a
-#define SAA7127_REG_LAST_ACTIVE 0x7b
-#define SAA7127_REG_MSB_VERTICAL 0x7c
-#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e
-#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f
-
-/*
- **********************************************************************
- *
- * Arrays with configuration parameters for the SAA7127
- *
- **********************************************************************
- */
-
-struct i2c_reg_value {
- unsigned char reg;
- unsigned char value;
-};
-
-static const struct i2c_reg_value saa7129_init_config_extra[] = {
- { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 },
- { SAA7127_REG_VTRIG, 0xfa },
- { 0, 0 }
-};
-
-static const struct i2c_reg_value saa7127_init_config_common[] = {
- { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d },
- { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 },
- { SAA7127_REG_COPYGEN_0, 0x77 },
- { SAA7127_REG_COPYGEN_1, 0x41 },
- { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */
- { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf },
- { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 },
- { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 },
- { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */
- { SAA7127_REG_LINE_21_ODD_0, 0x77 },
- { SAA7127_REG_LINE_21_ODD_1, 0x41 },
- { SAA7127_REG_LINE_21_EVEN_0, 0x88 },
- { SAA7127_REG_LINE_21_EVEN_1, 0x41 },
- { SAA7127_REG_RCV_PORT_CONTROL, 0x12 },
- { SAA7127_REG_VTRIG, 0xf9 },
- { SAA7127_REG_HTRIG_HI, 0x00 },
- { SAA7127_REG_RCV2_OUTPUT_START, 0x41 },
- { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 },
- { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 },
- { SAA7127_REG_TTX_REQUEST_H_START, 0x3e },
- { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 },
- { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 },
- { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 },
- { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 },
- { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 },
- { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 },
- { SAA7127_REG_FIRST_ACTIVE, 0x1a },
- { SAA7127_REG_LAST_ACTIVE, 0x01 },
- { SAA7127_REG_MSB_VERTICAL, 0xc0 },
- { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 },
- { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 },
- { 0, 0 }
-};
-
-#define SAA7127_60HZ_DAC_CONTROL 0x15
-static const struct i2c_reg_value saa7127_init_config_60hz[] = {
- { SAA7127_REG_BURST_START, 0x19 },
- /* BURST_END is also used as a chip ID in saa7127_probe */
- { SAA7127_REG_BURST_END, 0x1d },
- { SAA7127_REG_CHROMA_PHASE, 0xa3 },
- { SAA7127_REG_GAINU, 0x98 },
- { SAA7127_REG_GAINV, 0xd3 },
- { SAA7127_REG_BLACK_LEVEL, 0x39 },
- { SAA7127_REG_BLANKING_LEVEL, 0x2e },
- { SAA7127_REG_VBI_BLANKING, 0x2e },
- { SAA7127_REG_DAC_CONTROL, 0x15 },
- { SAA7127_REG_BURST_AMP, 0x4d },
- { SAA7127_REG_SUBC3, 0x1f },
- { SAA7127_REG_SUBC2, 0x7c },
- { SAA7127_REG_SUBC1, 0xf0 },
- { SAA7127_REG_SUBC0, 0x21 },
- { SAA7127_REG_MULTI, 0x90 },
- { SAA7127_REG_CLOSED_CAPTION, 0x11 },
- { 0, 0 }
-};
-
-#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02
-static struct i2c_reg_value saa7127_init_config_50hz_pal[] = {
- { SAA7127_REG_BURST_START, 0x21 },
- /* BURST_END is also used as a chip ID in saa7127_probe */
- { SAA7127_REG_BURST_END, 0x1d },
- { SAA7127_REG_CHROMA_PHASE, 0x3f },
- { SAA7127_REG_GAINU, 0x7d },
- { SAA7127_REG_GAINV, 0xaf },
- { SAA7127_REG_BLACK_LEVEL, 0x33 },
- { SAA7127_REG_BLANKING_LEVEL, 0x35 },
- { SAA7127_REG_VBI_BLANKING, 0x35 },
- { SAA7127_REG_DAC_CONTROL, 0x02 },
- { SAA7127_REG_BURST_AMP, 0x2f },
- { SAA7127_REG_SUBC3, 0xcb },
- { SAA7127_REG_SUBC2, 0x8a },
- { SAA7127_REG_SUBC1, 0x09 },
- { SAA7127_REG_SUBC0, 0x2a },
- { SAA7127_REG_MULTI, 0xa0 },
- { SAA7127_REG_CLOSED_CAPTION, 0x00 },
- { 0, 0 }
-};
-
-#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08
-static struct i2c_reg_value saa7127_init_config_50hz_secam[] = {
- { SAA7127_REG_BURST_START, 0x21 },
- /* BURST_END is also used as a chip ID in saa7127_probe */
- { SAA7127_REG_BURST_END, 0x1d },
- { SAA7127_REG_CHROMA_PHASE, 0x3f },
- { SAA7127_REG_GAINU, 0x6a },
- { SAA7127_REG_GAINV, 0x81 },
- { SAA7127_REG_BLACK_LEVEL, 0x33 },
- { SAA7127_REG_BLANKING_LEVEL, 0x35 },
- { SAA7127_REG_VBI_BLANKING, 0x35 },
- { SAA7127_REG_DAC_CONTROL, 0x08 },
- { SAA7127_REG_BURST_AMP, 0x2f },
- { SAA7127_REG_SUBC3, 0xb2 },
- { SAA7127_REG_SUBC2, 0x3b },
- { SAA7127_REG_SUBC1, 0xa3 },
- { SAA7127_REG_SUBC0, 0x28 },
- { SAA7127_REG_MULTI, 0x90 },
- { SAA7127_REG_CLOSED_CAPTION, 0x00 },
- { 0, 0 }
-};
-
-/*
- **********************************************************************
- *
- * Encoder Struct, holds the configuration state of the encoder
- *
- **********************************************************************
- */
-
-struct saa7127_state {
- struct v4l2_subdev sd;
- v4l2_std_id std;
- u32 ident;
- enum saa7127_input_type input_type;
- enum saa7127_output_type output_type;
- int video_enable;
- int wss_enable;
- u16 wss_mode;
- int cc_enable;
- u16 cc_data;
- int xds_enable;
- u16 xds_data;
- int vps_enable;
- u8 vps_data[5];
- u8 reg_2d;
- u8 reg_3a;
- u8 reg_3a_cb; /* colorbar bit */
- u8 reg_61;
-};
-
-static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct saa7127_state, sd);
-}
-
-static const char * const output_strs[] =
-{
- "S-Video + Composite",
- "Composite",
- "S-Video",
- "RGB",
- "YUV C",
- "YUV V"
-};
-
-static const char * const wss_strs[] = {
- "invalid",
- "letterbox 14:9 center",
- "letterbox 14:9 top",
- "invalid",
- "letterbox 16:9 top",
- "invalid",
- "invalid",
- "16:9 full format anamorphic",
- "4:3 full format",
- "invalid",
- "invalid",
- "letterbox 16:9 center",
- "invalid",
- "letterbox >16:9 center",
- "14:9 full format center",
- "invalid",
-};
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_read(struct v4l2_subdev *sd, u8 reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return i2c_smbus_read_byte_data(client, reg);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int i;
-
- for (i = 0; i < 3; i++) {
- if (i2c_smbus_write_byte_data(client, reg, val) == 0)
- return 0;
- }
- v4l2_err(sd, "I2C Write Problem\n");
- return -1;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_write_inittab(struct v4l2_subdev *sd,
- const struct i2c_reg_value *regs)
-{
- while (regs->reg != 0) {
- saa7127_write(sd, regs->reg, regs->value);
- regs++;
- }
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
-{
- struct saa7127_state *state = to_state(sd);
- int enable = (data->line != 0);
-
- if (enable && (data->field != 0 || data->line != 16))
- return -EINVAL;
- if (state->vps_enable != enable) {
- v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
- saa7127_write(sd, 0x54, enable << 7);
- state->vps_enable = enable;
- }
- if (!enable)
- return 0;
-
- state->vps_data[0] = data->data[2];
- state->vps_data[1] = data->data[8];
- state->vps_data[2] = data->data[9];
- state->vps_data[3] = data->data[10];
- state->vps_data[4] = data->data[11];
- v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n",
- state->vps_data[0], state->vps_data[1],
- state->vps_data[2], state->vps_data[3],
- state->vps_data[4]);
- saa7127_write(sd, 0x55, state->vps_data[0]);
- saa7127_write(sd, 0x56, state->vps_data[1]);
- saa7127_write(sd, 0x57, state->vps_data[2]);
- saa7127_write(sd, 0x58, state->vps_data[3]);
- saa7127_write(sd, 0x59, state->vps_data[4]);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
-{
- struct saa7127_state *state = to_state(sd);
- u16 cc = data->data[1] << 8 | data->data[0];
- int enable = (data->line != 0);
-
- if (enable && (data->field != 0 || data->line != 21))
- return -EINVAL;
- if (state->cc_enable != enable) {
- v4l2_dbg(1, debug, sd,
- "Turn CC %s\n", enable ? "on" : "off");
- saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
- (state->xds_enable << 7) | (enable << 6) | 0x11);
- state->cc_enable = enable;
- }
- if (!enable)
- return 0;
-
- v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
- saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
- saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
- state->cc_data = cc;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
-{
- struct saa7127_state *state = to_state(sd);
- u16 xds = data->data[1] << 8 | data->data[0];
- int enable = (data->line != 0);
-
- if (enable && (data->field != 1 || data->line != 21))
- return -EINVAL;
- if (state->xds_enable != enable) {
- v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
- saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
- (enable << 7) | (state->cc_enable << 6) | 0x11);
- state->xds_enable = enable;
- }
- if (!enable)
- return 0;
-
- v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
- saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
- saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
- state->xds_data = xds;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
-{
- struct saa7127_state *state = to_state(sd);
- int enable = (data->line != 0);
-
- if (enable && (data->field != 0 || data->line != 23))
- return -EINVAL;
- if (state->wss_enable != enable) {
- v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
- saa7127_write(sd, 0x27, enable << 7);
- state->wss_enable = enable;
- }
- if (!enable)
- return 0;
-
- saa7127_write(sd, 0x26, data->data[0]);
- saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
- v4l2_dbg(1, debug, sd,
- "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
- state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
-{
- struct saa7127_state *state = to_state(sd);
-
- if (enable) {
- v4l2_dbg(1, debug, sd, "Enable Video Output\n");
- saa7127_write(sd, 0x2d, state->reg_2d);
- saa7127_write(sd, 0x61, state->reg_61);
- } else {
- v4l2_dbg(1, debug, sd, "Disable Video Output\n");
- saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
- saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
- }
- state->video_enable = enable;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct saa7127_state *state = to_state(sd);
- const struct i2c_reg_value *inittab;
-
- if (std & V4L2_STD_525_60) {
- v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
- inittab = saa7127_init_config_60hz;
- state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
-
- } else if (state->ident == V4L2_IDENT_SAA7129 &&
- (std & V4L2_STD_SECAM) &&
- !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) {
-
- /* If and only if SECAM, with a SAA712[89] */
- v4l2_dbg(1, debug, sd,
- "Selecting 50 Hz SECAM video Standard\n");
- inittab = saa7127_init_config_50hz_secam;
- state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL;
-
- } else {
- v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n");
- inittab = saa7127_init_config_50hz_pal;
- state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL;
- }
-
- /* Write Table */
- saa7127_write_inittab(sd, inittab);
- state->std = std;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
-{
- struct saa7127_state *state = to_state(sd);
-
- switch (output) {
- case SAA7127_OUTPUT_TYPE_RGB:
- state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */
- state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
- break;
-
- case SAA7127_OUTPUT_TYPE_COMPOSITE:
- if (state->ident == V4L2_IDENT_SAA7129)
- state->reg_2d = 0x20; /* CVBS only */
- else
- state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */
- state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
- break;
-
- case SAA7127_OUTPUT_TYPE_SVIDEO:
- if (state->ident == V4L2_IDENT_SAA7129)
- state->reg_2d = 0x18; /* Y + C */
- else
- state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */
- state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
- break;
-
- case SAA7127_OUTPUT_TYPE_YUV_V:
- state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */
- state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
- break;
-
- case SAA7127_OUTPUT_TYPE_YUV_C:
- state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */
- state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */
- break;
-
- case SAA7127_OUTPUT_TYPE_BOTH:
- if (state->ident == V4L2_IDENT_SAA7129)
- state->reg_2d = 0x38;
- else
- state->reg_2d = 0xbf;
- state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */
- break;
-
- default:
- return -EINVAL;
- }
- v4l2_dbg(1, debug, sd,
- "Selecting %s output type\n", output_strs[output]);
-
- /* Configure Encoder */
- saa7127_write(sd, 0x2d, state->reg_2d);
- saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
- state->output_type = output;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
-{
- struct saa7127_state *state = to_state(sd);
-
- switch (input) {
- case SAA7127_INPUT_TYPE_NORMAL: /* avia */
- v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
- state->reg_3a_cb = 0;
- break;
-
- case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
- v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
- state->reg_3a_cb = 0x80;
- break;
-
- default:
- return -EINVAL;
- }
- saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
- state->input_type = input;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct saa7127_state *state = to_state(sd);
-
- if (state->std == std)
- return 0;
- return saa7127_set_std(sd, std);
-}
-
-static int saa7127_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct saa7127_state *state = to_state(sd);
- int rc = 0;
-
- if (state->input_type != input)
- rc = saa7127_set_input_type(sd, input);
- if (rc == 0 && state->output_type != output)
- rc = saa7127_set_output_type(sd, output);
- return rc;
-}
-
-static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct saa7127_state *state = to_state(sd);
-
- if (state->video_enable == enable)
- return 0;
- return saa7127_set_video_enable(sd, enable);
-}
-
-static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
-{
- struct saa7127_state *state = to_state(sd);
-
- memset(fmt, 0, sizeof(*fmt));
- if (state->vps_enable)
- fmt->service_lines[0][16] = V4L2_SLICED_VPS;
- if (state->wss_enable)
- fmt->service_lines[0][23] = V4L2_SLICED_WSS_625;
- if (state->cc_enable) {
- fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- }
- fmt->service_set =
- (state->vps_enable ? V4L2_SLICED_VPS : 0) |
- (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
- (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
- return 0;
-}
-
-static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
-{
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- return saa7127_set_wss(sd, data);
- case V4L2_SLICED_VPS:
- return saa7127_set_vps(sd, data);
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0)
- return saa7127_set_cc(sd, data);
- return saa7127_set_xds(sd, data);
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = saa7127_read(sd, reg->reg & 0xff);
- reg->size = 1;
- return 0;
-}
-
-static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
- return 0;
-}
-#endif
-
-static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
-{
- struct saa7127_state *state = to_state(sd);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
-}
-
-static int saa7127_log_status(struct v4l2_subdev *sd)
-{
- struct saa7127_state *state = to_state(sd);
-
- v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
- v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
- v4l2_info(sd, "Output: %s\n", state->video_enable ?
- output_strs[state->output_type] : "disabled");
- v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
- wss_strs[state->wss_mode] : "disabled");
- v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
- v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops saa7127_core_ops = {
- .log_status = saa7127_log_status,
- .g_chip_ident = saa7127_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = saa7127_g_register,
- .s_register = saa7127_s_register,
-#endif
-};
-
-static const struct v4l2_subdev_video_ops saa7127_video_ops = {
- .s_std_output = saa7127_s_std_output,
- .s_routing = saa7127_s_routing,
- .s_stream = saa7127_s_stream,
-};
-
-static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = {
- .s_vbi_data = saa7127_s_vbi_data,
- .g_sliced_fmt = saa7127_g_sliced_fmt,
-};
-
-static const struct v4l2_subdev_ops saa7127_ops = {
- .core = &saa7127_core_ops,
- .video = &saa7127_video_ops,
- .vbi = &saa7127_vbi_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct saa7127_state *state;
- struct v4l2_subdev *sd;
- struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
-
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -EIO;
-
- v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
- client->addr << 1);
-
- state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
- if (state == NULL)
- return -ENOMEM;
-
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
-
- /* First test register 0: Bits 5-7 are a version ID (should be 0),
- and bit 2 should also be 0.
- This is rather general, so the second test is more specific and
- looks at the 'ending point of burst in clock cycles' which is
- 0x1d after a reset and not expected to ever change. */
- if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
- (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
- v4l2_dbg(1, debug, sd, "saa7127 not found\n");
- kfree(state);
- return -ENODEV;
- }
-
- if (id->driver_data) { /* Chip type is already known */
- state->ident = id->driver_data;
- } else { /* Needs detection */
- int read_result;
-
- /* Detect if it's an saa7129 */
- read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
- saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
- if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
- saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
- read_result);
- state->ident = V4L2_IDENT_SAA7129;
- strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
- } else {
- state->ident = V4L2_IDENT_SAA7127;
- strlcpy(client->name, "saa7127", I2C_NAME_SIZE);
- }
- }
-
- v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
- client->addr << 1, client->adapter->name);
-
- v4l2_dbg(1, debug, sd, "Configuring encoder\n");
- saa7127_write_inittab(sd, saa7127_init_config_common);
- saa7127_set_std(sd, V4L2_STD_NTSC);
- saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
- saa7127_set_vps(sd, &vbi);
- saa7127_set_wss(sd, &vbi);
- saa7127_set_cc(sd, &vbi);
- saa7127_set_xds(sd, &vbi);
- if (test_image == 1)
- /* The Encoder has an internal Colorbar generator */
- /* This can be used for debugging */
- saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
- else
- saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
- saa7127_set_video_enable(sd, 1);
-
- if (state->ident == V4L2_IDENT_SAA7129)
- saa7127_write_inittab(sd, saa7129_init_config_extra);
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static int saa7127_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- /* Turn off TV output */
- saa7127_set_video_enable(sd, 0);
- kfree(to_state(sd));
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static struct i2c_device_id saa7127_id[] = {
- { "saa7127_auto", 0 }, /* auto-detection */
- { "saa7126", V4L2_IDENT_SAA7127 },
- { "saa7127", V4L2_IDENT_SAA7127 },
- { "saa7128", V4L2_IDENT_SAA7129 },
- { "saa7129", V4L2_IDENT_SAA7129 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, saa7127_id);
-
-static struct i2c_driver saa7127_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "saa7127",
- },
- .probe = saa7127_probe,
- .remove = saa7127_remove,
- .id_table = saa7127_id,
-};
-
-module_i2c_driver(saa7127_driver);
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
deleted file mode 100644
index 39fc0187a747..000000000000
--- a/drivers/media/video/saa7134/Kconfig
+++ /dev/null
@@ -1,64 +0,0 @@
-config VIDEO_SAA7134
- tristate "Philips SAA7134 support"
- depends on VIDEO_DEV && PCI && I2C
- select VIDEOBUF_DMA_SG
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select CRC32
- select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- This is a video4linux driver for Philips SAA713x based
- TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7134.
-
-config VIDEO_SAA7134_ALSA
- tristate "Philips SAA7134 DMA audio support"
- depends on VIDEO_SAA7134 && SND
- select SND_PCM
- ---help---
- This is a video4linux driver for direct (DMA) audio in
- Philips SAA713x based TV cards using ALSA
-
- To compile this driver as a module, choose M here: the
- module will be called saa7134-alsa.
-
-config VIDEO_SAA7134_RC
- bool "Philips SAA7134 Remote Controller support"
- depends on RC_CORE
- depends on VIDEO_SAA7134
- depends on !(RC_CORE=m && VIDEO_SAA7134=y)
- default y
- ---help---
- Enables Remote Controller support on saa7134 driver.
-
-config VIDEO_SAA7134_DVB
- tristate "DVB/ATSC Support for saa7134 based TV cards"
- depends on VIDEO_SAA7134 && DVB_CORE
- select VIDEOBUF_DVB
- select DVB_PLL if !DVB_FE_CUSTOMISE
- select DVB_MT352 if !DVB_FE_CUSTOMISE
- select DVB_TDA1004X if !DVB_FE_CUSTOMISE
- select DVB_NXT200X if !DVB_FE_CUSTOMISE
- select DVB_TDA10086 if !DVB_FE_CUSTOMISE
- select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select DVB_ISL6405 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
- select DVB_ZL10036 if !DVB_FE_CUSTOMISE
- select DVB_MT312 if !DVB_FE_CUSTOMISE
- select DVB_LNBP21 if !DVB_FE_CUSTOMISE
- select DVB_ZL10353 if !DVB_FE_CUSTOMISE
- select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
- select DVB_TDA10048 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
- select DVB_ZL10039 if !DVB_FE_CUSTOMISE
- ---help---
- This adds support for DVB cards based on the
- Philips saa7134 chip.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7134-dvb.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
deleted file mode 100644
index da3899329f52..000000000000
--- a/drivers/media/video/saa7134/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-
-saa7134-y := saa7134-cards.o saa7134-core.o saa7134-i2c.o
-saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o
-saa7134-y += saa7134-video.o
-saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o
-
-obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o
-
-obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
-
-obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
-
-ccflags-y += -I$(srctree)/drivers/media/video
-ccflags-y += -I$(srctree)/drivers/media/common/tuners
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
deleted file mode 100644
index cc7f3d6ee966..000000000000
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ /dev/null
@@ -1,1936 +0,0 @@
-/*
- *
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * Extended 3 / 2005 by Hartmut Hackmann to support various
- * cards with the tda10046 DVB-T channel decoder
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/kthread.h>
-#include <linux/suspend.h>
-
-#include "saa7134-reg.h"
-#include "saa7134.h"
-#include <media/v4l2-common.h>
-#include "dvb-pll.h"
-#include <dvb_frontend.h>
-
-#include "mt352.h"
-#include "mt352_priv.h" /* FIXME */
-#include "tda1004x.h"
-#include "nxt200x.h"
-#include "tuner-xc2028.h"
-#include "xc5000.h"
-
-#include "tda10086.h"
-#include "tda826x.h"
-#include "tda827x.h"
-#include "isl6421.h"
-#include "isl6405.h"
-#include "lnbp21.h"
-#include "tuner-simple.h"
-#include "tda10048.h"
-#include "tda18271.h"
-#include "lgdt3305.h"
-#include "tda8290.h"
-#include "mb86a20s.h"
-#include "lgs8gxx.h"
-
-#include "zl10353.h"
-#include "qt1010.h"
-
-#include "zl10036.h"
-#include "zl10039.h"
-#include "mt312.h"
-#include "s5h1411.h"
-
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-
-static unsigned int antenna_pwr;
-
-module_param(antenna_pwr, int, 0444);
-MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
-
-static int use_frontend;
-module_param(use_frontend, int, 0644);
-MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)");
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off).");
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-#define dprintk(fmt, arg...) do { if (debug) \
- printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0)
-
-/* Print a warning */
-#define wprintk(fmt, arg...) \
- printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg)
-
-/* ------------------------------------------------------------------
- * mt352 based DVB-T cards
- */
-
-static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
-{
- u32 ok;
-
- if (!on) {
- saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
- saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
- return 0;
- }
-
- saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26));
- saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
- udelay(10);
-
- saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28));
- saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
- udelay(10);
- saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28));
- udelay(10);
- ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27);
- dprintk("%s %s\n", __func__, ok ? "on" : "off");
-
- if (!ok)
- saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26));
- return ok;
-}
-
-static int mt352_pinnacle_init(struct dvb_frontend* fe)
-{
- static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 };
- static u8 reset [] = { RESET, 0x80 };
- static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
- static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 };
- static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 };
- static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f };
- static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d };
- static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 };
- struct saa7134_dev *dev= fe->dvb->priv;
-
- dprintk("%s called\n", __func__);
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
-
- mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg));
- mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg));
- mt352_write(fe, irq_cfg, sizeof(irq_cfg));
-
- return 0;
-}
-
-static int mt352_aver777_init(struct dvb_frontend* fe)
-{
- static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
- static u8 reset [] = { RESET, 0x80 };
- static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
- static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 };
- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
-
- return 0;
-}
-
-static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe)
-{
- static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d };
- static u8 reset [] = { RESET, 0x80 };
- static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
- static u8 agc_cfg [] = { AGC_TARGET, 0xe };
- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 };
-
- mt352_write(fe, clock_config, sizeof(clock_config));
- udelay(200);
- mt352_write(fe, reset, sizeof(reset));
- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
- mt352_write(fe, agc_cfg, sizeof(agc_cfg));
- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
- return 0;
-}
-
-static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- u8 off[] = { 0x00, 0xf1};
- u8 on[] = { 0x00, 0x71};
- struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)};
-
- struct saa7134_dev *dev = fe->dvb->priv;
- struct v4l2_frequency f;
-
- /* set frequency (mt2050) */
- f.tuner = 0;
- f.type = V4L2_TUNER_DIGITAL_TV;
- f.frequency = c->frequency / 1000 * 16 / 1000;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
- saa_call_all(dev, tuner, s_frequency, &f);
- msg.buf = on;
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
-
- pinnacle_antenna_pwr(dev, antenna_pwr);
-
- /* mt352 setup */
- return mt352_pinnacle_init(fe);
-}
-
-static struct mt352_config pinnacle_300i = {
- .demod_address = 0x3c >> 1,
- .adc_clock = 20333,
- .if2 = 36150,
- .no_tuner = 1,
- .demod_init = mt352_pinnacle_init,
-};
-
-static struct mt352_config avermedia_777 = {
- .demod_address = 0xf,
- .demod_init = mt352_aver777_init,
-};
-
-static struct mt352_config avermedia_xc3028_mt352_dev = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .demod_init = mt352_avermedia_xc3028_init,
-};
-
-static struct tda18271_std_map mb86a20s_tda18271_std_map = {
- .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
- .if_lvl = 7, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config kworld_tda18271_config = {
- .std_map = &mb86a20s_tda18271_std_map,
- .gate = TDA18271_GATE_DIGITAL,
- .config = 3, /* Use tuner callback for AGC */
-
-};
-
-static const struct mb86a20s_config kworld_mb86a20s_config = {
- .demod_address = 0x10,
-};
-
-static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
-
- unsigned char initmsg[] = {0x45, 0x97};
- unsigned char msg_enable[] = {0x45, 0xc1};
- unsigned char msg_disable[] = {0x45, 0x81};
- struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2};
-
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
- wprintk("could not access the I2C gate\n");
- return -EIO;
- }
- if (enable)
- msg.buf = msg_enable;
- else
- msg.buf = msg_disable;
- if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) {
- wprintk("could not access the I2C gate\n");
- return -EIO;
- }
- msleep(20);
- return 0;
-}
-
-/* ==================================================================
- * tda1004x based DVB-T cards, helper functions
- */
-
-static int philips_tda1004x_request_firmware(struct dvb_frontend *fe,
- const struct firmware **fw, char *name)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- return request_firmware(fw, name, &dev->pci->dev);
-}
-
-/* ------------------------------------------------------------------
- * these tuners are tu1216, td1316(a)
- */
-
-static int philips_tda6651_pll_set(struct dvb_frontend *fe)
-{
- struct dtv_frontend_properties *c = &fe->dtv_property_cache;
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
- u8 addr = state->config->tuner_address;
- u8 tuner_buf[4];
- struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len =
- sizeof(tuner_buf) };
- int tuner_frequency = 0;
- u8 band, cp, filter;
-
- /* determine charge pump */
- tuner_frequency = c->frequency + 36166000;
- if (tuner_frequency < 87000000)
- return -EINVAL;
- else if (tuner_frequency < 130000000)
- cp = 3;
- else if (tuner_frequency < 160000000)
- cp = 5;
- else if (tuner_frequency < 200000000)
- cp = 6;
- else if (tuner_frequency < 290000000)
- cp = 3;
- else if (tuner_frequency < 420000000)
- cp = 5;
- else if (tuner_frequency < 480000000)
- cp = 6;
- else if (tuner_frequency < 620000000)
- cp = 3;
- else if (tuner_frequency < 830000000)
- cp = 5;
- else if (tuner_frequency < 895000000)
- cp = 7;
- else
- return -EINVAL;
-
- /* determine band */
- if (c->frequency < 49000000)
- return -EINVAL;
- else if (c->frequency < 161000000)
- band = 1;
- else if (c->frequency < 444000000)
- band = 2;
- else if (c->frequency < 861000000)
- band = 4;
- else
- return -EINVAL;
-
- /* setup PLL filter */
- switch (c->bandwidth_hz) {
- case 6000000:
- filter = 0;
- break;
-
- case 7000000:
- filter = 0;
- break;
-
- case 8000000:
- filter = 1;
- break;
-
- default:
- return -EINVAL;
- }
-
- /* calculate divisor
- * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
- */
- tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000;
-
- /* setup tuner buffer */
- tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
- tuner_buf[1] = tuner_frequency & 0xff;
- tuner_buf[2] = 0xca;
- tuner_buf[3] = (cp << 5) | (filter << 3) | band;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) {
- wprintk("could not write to tuner at addr: 0x%02x\n",
- addr << 1);
- return -EIO;
- }
- msleep(1);
- return 0;
-}
-
-static int philips_tu1216_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
- u8 addr = state->config->tuner_address;
- static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab };
- struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) };
-
- /* setup PLL configuration */
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1)
- return -EIO;
- msleep(1);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static struct tda1004x_config philips_tu1216_60_config = {
- .demod_address = 0x8,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_4M,
- .agc_config = TDA10046_AGC_DEFAULT,
- .if_freq = TDA10046_FREQ_3617,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config philips_tu1216_61_config = {
-
- .demod_address = 0x8,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_4M,
- .agc_config = TDA10046_AGC_DEFAULT,
- .if_freq = TDA10046_FREQ_3617,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-/* ------------------------------------------------------------------ */
-
-static int philips_td1316_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
- u8 addr = state->config->tuner_address;
- static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab };
- struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
-
- /* setup PLL configuration */
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
-static int philips_td1316_tuner_set_params(struct dvb_frontend *fe)
-{
- return philips_tda6651_pll_set(fe);
-}
-
-static int philips_td1316_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
- u8 addr = state->config->tuner_address;
- static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 };
- struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) };
-
- /* switch the tuner to analog mode */
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1)
- return -EIO;
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int philips_europa_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- static u8 msg[] = { 0x00, 0x40};
- struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
-
-
- if (philips_td1316_tuner_init(fe))
- return -EIO;
- msleep(1);
- if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1)
- return -EIO;
-
- return 0;
-}
-
-static int philips_europa_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
-
- static u8 msg[] = { 0x00, 0x14 };
- struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) };
-
- if (philips_td1316_tuner_sleep(fe))
- return -EIO;
-
- /* switch the board to analog mode */
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &analog_msg, 1);
- return 0;
-}
-
-static int philips_europa_demod_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
-
- if (dev->original_demod_sleep)
- dev->original_demod_sleep(fe);
- fe->ops.i2c_gate_ctrl(fe, 1);
- return 0;
-}
-
-static struct tda1004x_config philips_europa_config = {
-
- .demod_address = 0x8,
- .invert = 0,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_4M,
- .agc_config = TDA10046_AGC_IFO_AUTO_POS,
- .if_freq = TDA10046_FREQ_052,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config medion_cardbus = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_IFO_AUTO_NEG,
- .if_freq = TDA10046_FREQ_3613,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config technotrend_budget_t3000_config = {
- .demod_address = 0x8,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_4M,
- .agc_config = TDA10046_AGC_DEFAULT,
- .if_freq = TDA10046_FREQ_3617,
- .tuner_address = 0x63,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-/* ------------------------------------------------------------------
- * tda 1004x based cards with philips silicon tuner
- */
-
-static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable)
-{
- struct tda1004x_state *state = fe->demodulator_priv;
-
- u8 addr = state->config->i2c_gate;
- static u8 tda8290_close[] = { 0x21, 0xc0};
- static u8 tda8290_open[] = { 0x21, 0x80};
- struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2};
- if (enable) {
- tda8290_msg.buf = tda8290_close;
- } else {
- tda8290_msg.buf = tda8290_open;
- }
- if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) {
- struct saa7134_dev *dev = fe->dvb->priv;
- wprintk("could not access tda8290 I2C gate\n");
- return -EIO;
- }
- msleep(20);
- return 0;
-}
-
-static int philips_tda827x_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
-
- switch (state->config->antenna_switch) {
- case 0: break;
- case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
- saa7134_set_gpio(dev, 21, 0);
- break;
- case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
- saa7134_set_gpio(dev, 21, 1);
- break;
- }
- return 0;
-}
-
-static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- struct tda1004x_state *state = fe->demodulator_priv;
-
- switch (state->config->antenna_switch) {
- case 0: break;
- case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n");
- saa7134_set_gpio(dev, 21, 1);
- break;
- case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n");
- saa7134_set_gpio(dev, 21, 0);
- break;
- }
- return 0;
-}
-
-static int configure_tda827x_fe(struct saa7134_dev *dev,
- struct tda1004x_config *cdec_conf,
- struct tda827x_config *tuner_conf)
-{
- struct videobuf_dvb_frontend *fe0;
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
-
- fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (cdec_conf->i2c_gate)
- fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl;
- if (dvb_attach(tda827x_attach, fe0->dvb.frontend,
- cdec_conf->tuner_address,
- &dev->i2c_adap, tuner_conf))
- return 0;
-
- wprintk("no tda827x tuner found at addr: %02x\n",
- cdec_conf->tuner_address);
- }
- return -EINVAL;
-}
-
-/* ------------------------------------------------------------------ */
-
-static struct tda827x_config tda827x_cfg_0 = {
- .init = philips_tda827x_tuner_init,
- .sleep = philips_tda827x_tuner_sleep,
- .config = 0,
- .switch_addr = 0
-};
-
-static struct tda827x_config tda827x_cfg_1 = {
- .init = philips_tda827x_tuner_init,
- .sleep = philips_tda827x_tuner_sleep,
- .config = 1,
- .switch_addr = 0x4b
-};
-
-static struct tda827x_config tda827x_cfg_2 = {
- .init = philips_tda827x_tuner_init,
- .sleep = philips_tda827x_tuner_sleep,
- .config = 2,
- .switch_addr = 0x4b
-};
-
-static struct tda827x_config tda827x_cfg_2_sw42 = {
- .init = philips_tda827x_tuner_init,
- .sleep = philips_tda827x_tuner_sleep,
- .config = 2,
- .switch_addr = 0x42
-};
-
-/* ------------------------------------------------------------------ */
-
-static struct tda1004x_config tda827x_lifeview_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config philips_tiger_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config cinergy_ht_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config cinergy_ht_pci_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config philips_tiger_s_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config pinnacle_pctv_310i_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config hauppauge_hvr_1110_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config asus_p7131_dual_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 2,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config lifeview_trio_config = {
- .demod_address = 0x09,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP00_I,
- .if_freq = TDA10046_FREQ_045,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config tevion_dvbt220rf_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config md8800_dvbt_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x60,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config asus_p7131_4871_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 2,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config asus_p7131_hybrid_lna_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 2,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config kworld_dvb_t_210_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch= 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config avermedia_super_007_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x60,
- .antenna_switch= 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config twinhan_dtv_dvb_3056_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP01_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x42,
- .tuner_address = 0x61,
- .antenna_switch = 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config asus_tiger_3in1_config = {
- .demod_address = 0x0b,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch = 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct tda1004x_config asus_ps3_100_config = {
- .demod_address = 0x0b,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP11_I,
- .if_freq = TDA10046_FREQ_045,
- .i2c_gate = 0x4b,
- .tuner_address = 0x61,
- .antenna_switch = 1,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-/* ------------------------------------------------------------------
- * special case: this card uses saa713x GPIO22 for the mode switch
- */
-
-static int ads_duo_tuner_init(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- philips_tda827x_tuner_init(fe);
- /* route TDA8275a AGC input to the channel decoder */
- saa7134_set_gpio(dev, 22, 1);
- return 0;
-}
-
-static int ads_duo_tuner_sleep(struct dvb_frontend *fe)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- /* route TDA8275a AGC input to the analog IF chip*/
- saa7134_set_gpio(dev, 22, 0);
- philips_tda827x_tuner_sleep(fe);
- return 0;
-}
-
-static struct tda827x_config ads_duo_cfg = {
- .init = ads_duo_tuner_init,
- .sleep = ads_duo_tuner_sleep,
- .config = 0
-};
-
-static struct tda1004x_config ads_tech_duo_config = {
- .demod_address = 0x08,
- .invert = 1,
- .invert_oclk = 0,
- .xtal_freq = TDA10046_XTAL_16M,
- .agc_config = TDA10046_AGC_TDA827X,
- .gpio_config = TDA10046_GP00_I,
- .if_freq = TDA10046_FREQ_045,
- .tuner_address = 0x61,
- .request_firmware = philips_tda1004x_request_firmware
-};
-
-static struct zl10353_config behold_h6_config = {
- .demod_address = 0x1e>>1,
- .no_tuner = 1,
- .parallel_ts = 1,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static struct xc5000_config behold_x7_tunerconfig = {
- .i2c_address = 0xc2>>1,
- .if_khz = 4560,
- .radio_input = XC5000_RADIO_FM1,
-};
-
-static struct zl10353_config behold_x7_config = {
- .demod_address = 0x1e>>1,
- .if2 = 45600,
- .no_tuner = 1,
- .parallel_ts = 1,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static struct zl10353_config videomate_t750_zl10353_config = {
- .demod_address = 0x0f,
- .no_tuner = 1,
- .parallel_ts = 1,
- .disable_i2c_gate_ctrl = 1,
-};
-
-static struct qt1010_config videomate_t750_qt1010_config = {
- .i2c_address = 0x62
-};
-
-
-/* ==================================================================
- * tda10086 based DVB-S cards, helper functions
- */
-
-static struct tda10086_config flydvbs = {
- .demod_address = 0x0e,
- .invert = 0,
- .diseqc_tone = 0,
- .xtal_freq = TDA10086_XTAL_16M,
-};
-
-static struct tda10086_config sd1878_4m = {
- .demod_address = 0x0e,
- .invert = 0,
- .diseqc_tone = 0,
- .xtal_freq = TDA10086_XTAL_4M,
-};
-
-/* ------------------------------------------------------------------
- * special case: lnb supply is connected to the gated i2c
- */
-
-static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
-{
- int res = -EIO;
- struct saa7134_dev *dev = fe->dvb->priv;
- if (fe->ops.i2c_gate_ctrl) {
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (dev->original_set_voltage)
- res = dev->original_set_voltage(fe, voltage);
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- return res;
-};
-
-static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg)
-{
- int res = -EIO;
- struct saa7134_dev *dev = fe->dvb->priv;
- if (fe->ops.i2c_gate_ctrl) {
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (dev->original_set_high_voltage)
- res = dev->original_set_high_voltage(fe, arg);
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- return res;
-};
-
-static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- u8 wbuf[2] = { 0x1f, 00 };
- u8 rbuf;
- struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 },
- { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } };
-
- if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2)
- return -EIO;
- /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */
- if (voltage == SEC_VOLTAGE_18)
- wbuf[1] = rbuf | 0x10;
- else
- wbuf[1] = rbuf & 0xef;
- msg[0].len = 2;
- i2c_transfer(&dev->i2c_adap, msg, 1);
- return 0;
-}
-
-static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg)
-{
- struct saa7134_dev *dev = fe->dvb->priv;
- wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__);
- return -EIO;
-}
-
-/* ==================================================================
- * nxt200x based ATSC cards, helper functions
- */
-
-static struct nxt200x_config avertvhda180 = {
- .demod_address = 0x0a,
-};
-
-static struct nxt200x_config kworldatsc110 = {
- .demod_address = 0x0a,
-};
-
-/* ------------------------------------------------------------------ */
-
-static struct mt312_config avertv_a700_mt312 = {
- .demod_address = 0x0e,
- .voltage_inverted = 1,
-};
-
-static struct zl10036_config avertv_a700_tuner = {
- .tuner_address = 0x60,
-};
-
-static struct mt312_config zl10313_compro_s350_config = {
- .demod_address = 0x0e,
-};
-
-static struct lgdt3305_config hcw_lgdt3305_config = {
- .i2c_addr = 0x0e,
- .mpeg_mode = LGDT3305_MPEG_SERIAL,
- .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
- .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
- .deny_i2c_rptr = 1,
- .spectral_inversion = 1,
- .qam_if_khz = 4000,
- .vsb_if_khz = 3250,
-};
-
-static struct tda10048_config hcw_tda10048_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_SERIAL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_200,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3500,
- .dtv8_if_freq_khz = TDA10048_IF_4000,
- .clk_freq_khz = TDA10048_CLK_16000,
- .disable_gate_access = 1,
-};
-
-static struct tda18271_std_map hauppauge_tda18271_std_map = {
- .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
- .if_lvl = 1, .rfagc_top = 0x58, },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
- .if_lvl = 1, .rfagc_top = 0x58, },
-};
-
-static struct tda18271_config hcw_tda18271_config = {
- .std_map = &hauppauge_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .config = 3,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct tda829x_config tda829x_no_probe = {
- .probe_tuner = TDA829X_DONT_PROBE,
-};
-
-static struct tda10048_config zolid_tda10048_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_PARALLEL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_200,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3500,
- .dtv8_if_freq_khz = TDA10048_IF_4000,
- .clk_freq_khz = TDA10048_CLK_16000,
- .disable_gate_access = 1,
-};
-
-static struct tda18271_config zolid_tda18271_config = {
- .gate = TDA18271_GATE_ANALOG,
-};
-
-static struct tda10048_config dtv1000s_tda10048_config = {
- .demod_address = 0x10 >> 1,
- .output_mode = TDA10048_PARALLEL_OUTPUT,
- .fwbulkwritelen = TDA10048_BULKWRITE_200,
- .inversion = TDA10048_INVERSION_ON,
- .dtv6_if_freq_khz = TDA10048_IF_3300,
- .dtv7_if_freq_khz = TDA10048_IF_3800,
- .dtv8_if_freq_khz = TDA10048_IF_4300,
- .clk_freq_khz = TDA10048_CLK_16000,
- .disable_gate_access = 1,
-};
-
-static struct tda18271_std_map dtv1000s_tda18271_std_map = {
- .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
- .if_lvl = 1, .rfagc_top = 0x37, },
- .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
- .if_lvl = 1, .rfagc_top = 0x37, },
-};
-
-static struct tda18271_config dtv1000s_tda18271_config = {
- .std_map = &dtv1000s_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
-};
-
-static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = {
- .prod = LGS8GXX_PROD_LGS8G75,
- .demod_address = 0x1d,
- .serial_ts = 0,
- .ts_clk_pol = 1,
- .ts_clk_gated = 0,
- .if_clk_freq = 30400, /* 30.4 MHz */
- .if_freq = 4000, /* 4.00 MHz */
- .if_neg_center = 0,
- .ext_adc = 0,
- .adc_signed = 1,
- .adc_vpp = 3, /* 2.0 Vpp */
- .if_neg_edge = 1,
-};
-
-static struct tda18271_config prohdtv_pro2_tda18271_config = {
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
-};
-
-static struct tda18271_std_map kworld_tda18271_std_map = {
- .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
- .if_lvl = 6, .rfagc_top = 0x37 },
- .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
- .if_lvl = 6, .rfagc_top = 0x37 },
-};
-
-static struct tda18271_config kworld_pc150u_tda18271_config = {
- .std_map = &kworld_tda18271_std_map,
- .gate = TDA18271_GATE_ANALOG,
- .output_opt = TDA18271_OUTPUT_LT_OFF,
- .config = 3, /* Use tuner callback for AGC */
- .rf_cal_on_startup = 1
-};
-
-static struct s5h1411_config kworld_s5h1411_config = {
- .output_mode = S5H1411_PARALLEL_OUTPUT,
- .gpio = S5H1411_GPIO_OFF,
- .qam_if = S5H1411_IF_4000,
- .vsb_if = S5H1411_IF_3250,
- .inversion = S5H1411_INVERSION_ON,
- .status_mode = S5H1411_DEMODLOCKING,
- .mpeg_timing =
- S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
-};
-
-
-/* ==================================================================
- * Core code
- */
-
-static int dvb_init(struct saa7134_dev *dev)
-{
- int ret;
- int attach_xc3028 = 0;
- struct videobuf_dvb_frontend *fe0;
-
- /* FIXME: add support for multi-frontend */
- mutex_init(&dev->frontends.lock);
- INIT_LIST_HEAD(&dev->frontends.felist);
-
- printk(KERN_INFO "%s() allocating 1 frontend\n", __func__);
- fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1);
- if (!fe0) {
- printk(KERN_ERR "%s() failed to alloc\n", __func__);
- return -ENOMEM;
- }
-
- /* init struct videobuf_dvb */
- dev->ts.nr_bufs = 32;
- dev->ts.nr_packets = 32*4;
- fe0->dvb.name = dev->name;
- videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_ALTERNATE,
- sizeof(struct saa7134_buf),
- dev, NULL);
-
- switch (dev->board) {
- case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
- dprintk("pinnacle 300i dvb setup\n");
- fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params;
- }
- break;
- case SAA7134_BOARD_AVERMEDIA_777:
- case SAA7134_BOARD_AVERMEDIA_A16AR:
- dprintk("avertv 777 dvb setup\n");
- fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x61,
- TUNER_PHILIPS_TD1316);
- }
- break;
- case SAA7134_BOARD_AVERMEDIA_A16D:
- dprintk("AverMedia A16D dvb setup\n");
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &avermedia_xc3028_mt352_dev,
- &dev->i2c_adap);
- attach_xc3028 = 1;
- break;
- case SAA7134_BOARD_MD7134:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &medion_cardbus,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &dev->i2c_adap, medion_cardbus.tuner_address,
- TUNER_PHILIPS_FMD1216ME_MK3);
- }
- break;
- case SAA7134_BOARD_PHILIPS_TOUGH:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_tu1216_60_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
- fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
- }
- break;
- case SAA7134_BOARD_FLYDVBTDUO:
- case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
- if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_PHILIPS_EUROPA:
- case SAA7134_BOARD_VIDEOMATE_DVBT_300:
- case SAA7134_BOARD_ASUS_EUROPA_HYBRID:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_europa_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
- fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
- fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
- fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
- fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
- }
- break;
- case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &technotrend_budget_t3000_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
- fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
- fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
- fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
- fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
- }
- break;
- case SAA7134_BOARD_VIDEOMATE_DVBT_200:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_tu1216_61_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init;
- fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set;
- }
- break;
- case SAA7134_BOARD_KWORLD_DVBT_210:
- if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- fe0->dvb.frontend = dvb_attach(tda10048_attach,
- &hcw_tda10048_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &hcw_tda18271_config);
- }
- break;
- case SAA7134_BOARD_PHILIPS_TIGER:
- if (configure_tda827x_fe(dev, &philips_tiger_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_PINNACLE_PCTV_310i:
- if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config,
- &tda827x_cfg_1) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1110:
- if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config,
- &tda827x_cfg_1) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1150:
- fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
- &hcw_lgdt3305_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &hcw_tda18271_config);
- }
- break;
- case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
- if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_FLYDVBT_LR301:
- if (configure_tda827x_fe(dev, &tda827x_lifeview_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_FLYDVB_TRIO:
- if (!use_frontend) { /* terrestrial */
- if (configure_tda827x_fe(dev, &lifeview_trio_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- } else { /* satellite */
- fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63,
- &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__);
- goto detach_frontend;
- }
- if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap,
- 0x08, 0, 0) == NULL) {
- wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__);
- goto detach_frontend;
- }
- }
- }
- break;
- case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
- case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &ads_tech_duo_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda827x_attach,fe0->dvb.frontend,
- ads_tech_duo_config.tuner_address, &dev->i2c_adap,
- &ads_duo_cfg) == NULL) {
- wprintk("no tda827x tuner found at addr: %02x\n",
- ads_tech_duo_config.tuner_address);
- goto detach_frontend;
- }
- } else
- wprintk("failed to attach tda10046\n");
- break;
- case SAA7134_BOARD_TEVION_DVBT_220RF:
- if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_MEDION_MD8800_QUADRO:
- if (!use_frontend) { /* terrestrial */
- if (configure_tda827x_fe(dev, &md8800_dvbt_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- } else { /* satellite */
- fe0->dvb.frontend = dvb_attach(tda10086_attach,
- &flydvbs, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- struct dvb_frontend *fe = fe0->dvb.frontend;
- u8 dev_id = dev->eedata[2];
- u8 data = 0xc4;
- struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1};
-
- if (dvb_attach(tda826x_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: Medion Quadro, no tda826x "
- "found !\n", __func__);
- goto detach_frontend;
- }
- if (dev_id != 0x08) {
- /* we need to open the i2c gate (we know it exists) */
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (dvb_attach(isl6405_attach, fe,
- &dev->i2c_adap, 0x08, 0, 0) == NULL) {
- wprintk("%s: Medion Quadro, no ISL6405 "
- "found !\n", __func__);
- goto detach_frontend;
- }
- if (dev_id == 0x07) {
- /* fire up the 2nd section of the LNB supply since
- we can't do this from the other section */
- msg.buf = &data;
- i2c_transfer(&dev->i2c_adap, &msg, 1);
- }
- fe->ops.i2c_gate_ctrl(fe, 0);
- dev->original_set_voltage = fe->ops.set_voltage;
- fe->ops.set_voltage = md8800_set_voltage;
- dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
- fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
- } else {
- fe->ops.set_voltage = md8800_set_voltage2;
- fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2;
- }
- }
- }
- break;
- case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
- fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
- &dev->i2c_adap);
- if (fe0->dvb.frontend)
- dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61,
- NULL, DVB_PLL_TDHU2);
- break;
- case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
- case SAA7134_BOARD_KWORLD_ATSC110:
- fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
- &dev->i2c_adap);
- if (fe0->dvb.frontend)
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x61,
- TUNER_PHILIPS_TUV1236D);
- break;
- case SAA7134_BOARD_KWORLD_PC150U:
- saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
- saa7134_tuner_callback(dev, 0,
- TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
- fe0->dvb.frontend = dvb_attach(s5h1411_attach,
- &kworld_s5h1411_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &kworld_pc150u_tda18271_config);
- }
- break;
- case SAA7134_BOARD_FLYDVBS_LR300:
- fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
- &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: No tda826x found!\n", __func__);
- goto detach_frontend;
- }
- if (dvb_attach(isl6421_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x08, 0, 0) == NULL) {
- wprintk("%s: No ISL6421 found!\n", __func__);
- goto detach_frontend;
- }
- }
- break;
- case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &medion_cardbus,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep;
- fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
-
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &dev->i2c_adap, medion_cardbus.tuner_address,
- TUNER_PHILIPS_FMD1216ME_MK3);
- }
- break;
- case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
- fe0->dvb.frontend = dvb_attach(tda10046_attach,
- &philips_europa_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init;
- fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params;
- }
- break;
- case SAA7134_BOARD_CINERGY_HT_PCMCIA:
- if (configure_tda827x_fe(dev, &cinergy_ht_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_CINERGY_HT_PCI:
- if (configure_tda827x_fe(dev, &cinergy_ht_pci_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_PHILIPS_TIGER_S:
- if (configure_tda827x_fe(dev, &philips_tiger_s_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_ASUS_P7131_4871:
- if (configure_tda827x_fe(dev, &asus_p7131_4871_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
- if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_AVERMEDIA_SUPER_007:
- if (configure_tda827x_fe(dev, &avermedia_super_007_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_TWINHAN_DTV_DVB_3056:
- if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config,
- &tda827x_cfg_2_sw42) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_PHILIPS_SNAKE:
- fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60,
- &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: No tda826x found!\n", __func__);
- goto detach_frontend;
- }
- if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0, 0) == NULL) {
- wprintk("%s: No lnbp21 found!\n", __func__);
- goto detach_frontend;
- }
- }
- break;
- case SAA7134_BOARD_CREATIX_CTX953:
- if (configure_tda827x_fe(dev, &md8800_dvbt_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_MSI_TVANYWHERE_AD11:
- if (configure_tda827x_fe(dev, &philips_tiger_s_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
- dprintk("AverMedia E506R dvb setup\n");
- saa7134_set_gpio(dev, 25, 0);
- msleep(10);
- saa7134_set_gpio(dev, 25, 1);
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &avermedia_xc3028_mt352_dev,
- &dev->i2c_adap);
- attach_xc3028 = 1;
- break;
- case SAA7134_BOARD_MD7134_BRIDGE_2:
- fe0->dvb.frontend = dvb_attach(tda10086_attach,
- &sd1878_4m, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- struct dvb_frontend *fe;
- if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60,
- &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) {
- wprintk("%s: MD7134 DVB-S, no SD1878 "
- "found !\n", __func__);
- goto detach_frontend;
- }
- /* we need to open the i2c gate (we know it exists) */
- fe = fe0->dvb.frontend;
- fe->ops.i2c_gate_ctrl(fe, 1);
- if (dvb_attach(isl6405_attach, fe,
- &dev->i2c_adap, 0x08, 0, 0) == NULL) {
- wprintk("%s: MD7134 DVB-S, no ISL6405 "
- "found !\n", __func__);
- goto detach_frontend;
- }
- fe->ops.i2c_gate_ctrl(fe, 0);
- dev->original_set_voltage = fe->ops.set_voltage;
- fe->ops.set_voltage = md8800_set_voltage;
- dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage;
- fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage;
- }
- break;
- case SAA7134_BOARD_AVERMEDIA_M103:
- saa7134_set_gpio(dev, 25, 0);
- msleep(10);
- saa7134_set_gpio(dev, 25, 1);
- fe0->dvb.frontend = dvb_attach(mt352_attach,
- &avermedia_xc3028_mt352_dev,
- &dev->i2c_adap);
- attach_xc3028 = 1;
- break;
- case SAA7134_BOARD_ASUSTeK_TIGER_3IN1:
- if (!use_frontend) { /* terrestrial */
- if (configure_tda827x_fe(dev, &asus_tiger_3in1_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- } else { /* satellite */
- fe0->dvb.frontend = dvb_attach(tda10086_attach,
- &flydvbs, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda826x_attach,
- fe0->dvb.frontend, 0x60,
- &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: Asus Tiger 3in1, no "
- "tda826x found!\n", __func__);
- goto detach_frontend;
- }
- if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0, 0) == NULL) {
- wprintk("%s: Asus Tiger 3in1, no lnbp21"
- " found!\n", __func__);
- goto detach_frontend;
- }
- }
- }
- break;
- case SAA7134_BOARD_ASUSTeK_PS3_100:
- if (!use_frontend) { /* terrestrial */
- if (configure_tda827x_fe(dev, &asus_ps3_100_config,
- &tda827x_cfg_2) < 0)
- goto detach_frontend;
- } else { /* satellite */
- fe0->dvb.frontend = dvb_attach(tda10086_attach,
- &flydvbs, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(tda826x_attach,
- fe0->dvb.frontend, 0x60,
- &dev->i2c_adap, 0) == NULL) {
- wprintk("%s: Asus My Cinema PS3-100, no "
- "tda826x found!\n", __func__);
- goto detach_frontend;
- }
- if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0, 0) == NULL) {
- wprintk("%s: Asus My Cinema PS3-100, no lnbp21"
- " found!\n", __func__);
- goto detach_frontend;
- }
- }
- }
- break;
- case SAA7134_BOARD_ASUSTeK_TIGER:
- if (configure_tda827x_fe(dev, &philips_tiger_config,
- &tda827x_cfg_0) < 0)
- goto detach_frontend;
- break;
- case SAA7134_BOARD_BEHOLD_H6:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &behold_h6_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216MEX_MK3);
- }
- break;
- case SAA7134_BOARD_BEHOLD_X7:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &behold_x7_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &dev->i2c_adap, &behold_x7_tunerconfig);
- }
- break;
- case SAA7134_BOARD_BEHOLD_H7:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &behold_x7_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- dvb_attach(xc5000_attach, fe0->dvb.frontend,
- &dev->i2c_adap, &behold_x7_tunerconfig);
- }
- break;
- case SAA7134_BOARD_AVERMEDIA_A700_PRO:
- case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
- /* Zarlink ZL10313 */
- fe0->dvb.frontend = dvb_attach(mt312_attach,
- &avertv_a700_mt312, &dev->i2c_adap);
- if (fe0->dvb.frontend) {
- if (dvb_attach(zl10036_attach, fe0->dvb.frontend,
- &avertv_a700_tuner, &dev->i2c_adap) == NULL) {
- wprintk("%s: No zl10036 found!\n",
- __func__);
- }
- }
- break;
- case SAA7134_BOARD_VIDEOMATE_S350:
- fe0->dvb.frontend = dvb_attach(mt312_attach,
- &zl10313_compro_s350_config, &dev->i2c_adap);
- if (fe0->dvb.frontend)
- if (dvb_attach(zl10039_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap) == NULL)
- wprintk("%s: No zl10039 found!\n",
- __func__);
-
- break;
- case SAA7134_BOARD_VIDEOMATE_T750:
- fe0->dvb.frontend = dvb_attach(zl10353_attach,
- &videomate_t750_zl10353_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- if (dvb_attach(qt1010_attach,
- fe0->dvb.frontend,
- &dev->i2c_adap,
- &videomate_t750_qt1010_config) == NULL)
- wprintk("error attaching QT1010\n");
- }
- break;
- case SAA7134_BOARD_ZOLID_HYBRID_PCI:
- fe0->dvb.frontend = dvb_attach(tda10048_attach,
- &zolid_tda10048_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &zolid_tda18271_config);
- }
- break;
- case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
- fe0->dvb.frontend = dvb_attach(tda10048_attach,
- &dtv1000s_tda10048_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &dtv1000s_tda18271_config);
- }
- break;
- case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
- /* Switch to digital mode */
- saa7134_tuner_callback(dev, 0,
- TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
- fe0->dvb.frontend = dvb_attach(mb86a20s_attach,
- &kworld_mb86a20s_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &kworld_tda18271_config);
- fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl;
- }
-
- /* mb86a20s need to use the I2C gateway */
- break;
- case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
- fe0->dvb.frontend = dvb_attach(lgs8gxx_attach,
- &prohdtv_pro2_lgs8g75_config,
- &dev->i2c_adap);
- if (fe0->dvb.frontend != NULL) {
- dvb_attach(tda829x_attach, fe0->dvb.frontend,
- &dev->i2c_adap, 0x4b,
- &tda829x_no_probe);
- dvb_attach(tda18271_attach, fe0->dvb.frontend,
- 0x60, &dev->i2c_adap,
- &prohdtv_pro2_tda18271_config);
- }
- break;
- default:
- wprintk("Huh? unknown DVB card?\n");
- break;
- }
-
- if (attach_xc3028) {
- struct dvb_frontend *fe;
- struct xc2028_config cfg = {
- .i2c_adap = &dev->i2c_adap,
- .i2c_addr = 0x61,
- };
-
- if (!fe0->dvb.frontend)
- goto detach_frontend;
-
- fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg);
- if (!fe) {
- printk(KERN_ERR "%s/2: xc3028 attach failed\n",
- dev->name);
- goto detach_frontend;
- }
- }
-
- if (NULL == fe0->dvb.frontend) {
- printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name);
- goto detach_frontend;
- }
- /* define general-purpose callback pointer */
- fe0->dvb.frontend->callback = saa7134_tuner_callback;
-
- /* register everything else */
- ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
- &dev->pci->dev, adapter_nr, 0, NULL);
-
- /* this sequence is necessary to make the tda1004x load its firmware
- * and to enter analog mode of hybrid boards
- */
- if (!ret) {
- if (fe0->dvb.frontend->ops.init)
- fe0->dvb.frontend->ops.init(fe0->dvb.frontend);
- if (fe0->dvb.frontend->ops.sleep)
- fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend);
- if (fe0->dvb.frontend->ops.tuner_ops.sleep)
- fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend);
- }
- return ret;
-
-detach_frontend:
- videobuf_dvb_dealloc_frontends(&dev->frontends);
- return -EINVAL;
-}
-
-static int dvb_fini(struct saa7134_dev *dev)
-{
- struct videobuf_dvb_frontend *fe0;
-
- /* Get the first frontend */
- fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1);
- if (!fe0)
- return -EINVAL;
-
- /* FIXME: I suspect that this code is bogus, since the entry for
- Pinnacle 300I DVB-T PAL already defines the proper init to allow
- the detection of mt2032 (TDA9887_PORT2_INACTIVE)
- */
- if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) {
- struct v4l2_priv_tun_config tda9887_cfg;
- static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &on;
-
- /* otherwise we don't detect the tuner on next insmod */
- saa_call_all(dev, tuner, s_config, &tda9887_cfg);
- } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) {
- if ((dev->eedata[2] == 0x07) && use_frontend) {
- /* turn off the 2nd lnb supply */
- u8 data = 0x80;
- struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1};
- struct dvb_frontend *fe;
- fe = fe0->dvb.frontend;
- if (fe->ops.i2c_gate_ctrl) {
- fe->ops.i2c_gate_ctrl(fe, 1);
- i2c_transfer(&dev->i2c_adap, &msg, 1);
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- }
- }
- videobuf_dvb_unregister_bus(&dev->frontends);
- return 0;
-}
-
-static struct saa7134_mpeg_ops dvb_ops = {
- .type = SAA7134_MPEG_DVB,
- .init = dvb_init,
- .fini = dvb_fini,
-};
-
-static int __init dvb_register(void)
-{
- return saa7134_ts_register(&dvb_ops);
-}
-
-static void __exit dvb_unregister(void)
-{
- saa7134_ts_unregister(&dvb_ops);
-}
-
-module_init(dvb_register);
-module_exit(dvb_unregister);
-
-/* ------------------------------------------------------------------ */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
deleted file mode 100644
index 05c6e217d8a7..000000000000
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- *
- * handle saa7134 IR remotes via linux kernel input layer.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/slab.h>
-
-#include "saa7134-reg.h"
-#include "saa7134.h"
-
-#define MODULE_NAME "saa7134"
-
-static unsigned int disable_ir;
-module_param(disable_ir, int, 0444);
-MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
-
-static unsigned int ir_debug;
-module_param(ir_debug, int, 0644);
-MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
-
-static int pinnacle_remote;
-module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */
-MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)");
-
-#define dprintk(fmt, arg...) if (ir_debug) \
- printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg)
-#define i2cdprintk(fmt, arg...) if (ir_debug) \
- printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg)
-
-/* Helper function for raw decoding at GPIO16 or GPIO18 */
-static int saa7134_raw_decode_irq(struct saa7134_dev *dev);
-
-/* -------------------- GPIO generic keycode builder -------------------- */
-
-static int build_key(struct saa7134_dev *dev)
-{
- struct saa7134_card_ir *ir = dev->remote;
- u32 gpio, data;
-
- /* here comes the additional handshake steps for some cards */
- switch (dev->board) {
- case SAA7134_BOARD_GOTVIEW_7135:
- saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80);
- saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80);
- break;
- }
- /* rising SAA7134_GPIO_GPRESCAN reads the status */
- saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
-
- gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
- if (ir->polling) {
- if (ir->last_gpio == gpio)
- return 0;
- ir->last_gpio = gpio;
- }
-
- data = ir_extract_bits(gpio, ir->mask_keycode);
- dprintk("build_key gpio=0x%x mask=0x%x data=%d\n",
- gpio, ir->mask_keycode, data);
-
- switch (dev->board) {
- case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
- if (data == ir->mask_keycode)
- rc_keyup(ir->dev);
- else
- rc_keydown_notimeout(ir->dev, data, 0);
- return 0;
- }
-
- if (ir->polling) {
- if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
- (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
- rc_keydown_notimeout(ir->dev, data, 0);
- } else {
- rc_keyup(ir->dev);
- }
- }
- else { /* IRQ driven mode - handle key press and release in one go */
- if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
- (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
- rc_keydown_notimeout(ir->dev, data, 0);
- rc_keyup(ir->dev);
- }
- }
-
- return 0;
-}
-
-/* --------------------- Chip specific I2C key builders ----------------- */
-
-static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- int gpio;
- int attempt = 0;
- unsigned char b;
-
- /* We need this to access GPI Used by the saa_readl macro. */
- struct saa7134_dev *dev = ir->c->adapter->algo_data;
-
- if (dev == NULL) {
- i2cdprintk("get_key_flydvb_trio: "
- "ir->c->adapter->algo_data is NULL!\n");
- return -EIO;
- }
-
- /* rising SAA7134_GPIGPRESCAN reads the status */
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
-
- gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
-
- if (0x40000 & ~gpio)
- return 0; /* No button press */
-
- /* No button press - only before first key pressed */
- if (b == 0xFF)
- return 0;
-
- /* poll IR chip */
- /* weak up the IR chip */
- b = 0;
-
- while (1 != i2c_master_send(ir->c, &b, 1)) {
- if ((attempt++) < 10) {
- /*
- * wait a bit for next attempt -
- * I don't know how make it better
- */
- msleep(10);
- continue;
- }
- i2cdprintk("send wake up byte to pic16C505 (IR chip)"
- "failed %dx\n", attempt);
- return -EIO;
- }
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- *ir_key = b;
- *ir_raw = b;
- return 1;
-}
-
-static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
- u32 *ir_raw)
-{
- unsigned char b;
- int gpio;
-
- /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
- struct saa7134_dev *dev = ir->c->adapter->algo_data;
- if (dev == NULL) {
- i2cdprintk("get_key_msi_tvanywhere_plus: "
- "ir->c->adapter->algo_data is NULL!\n");
- return -EIO;
- }
-
- /* rising SAA7134_GPIO_GPRESCAN reads the status */
-
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
-
- gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
-
- /* GPIO&0x40 is pulsed low when a button is pressed. Don't do
- I2C receive if gpio&0x40 is not low. */
-
- if (gpio & 0x40)
- return 0; /* No button press */
-
- /* GPIO says there is a button press. Get it. */
-
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- /* No button press */
-
- if (b == 0xff)
- return 0;
-
- /* Button pressed */
-
- dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b);
- *ir_key = b;
- *ir_raw = b;
- return 1;
-}
-
-/* copied and modified from get_key_msi_tvanywhere_plus() */
-static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
- u32 *ir_raw)
-{
- unsigned char b;
- unsigned int gpio;
-
- /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
- struct saa7134_dev *dev = ir->c->adapter->algo_data;
- if (dev == NULL) {
- i2cdprintk("get_key_kworld_pc150u: "
- "ir->c->adapter->algo_data is NULL!\n");
- return -EIO;
- }
-
- /* rising SAA7134_GPIO_GPRESCAN reads the status */
-
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
-
- gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
-
- /* GPIO&0x100 is pulsed low when a button is pressed. Don't do
- I2C receive if gpio&0x100 is not low. */
-
- if (gpio & 0x100)
- return 0; /* No button press */
-
- /* GPIO says there is a button press. Get it. */
-
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- /* No button press */
-
- if (b == 0xff)
- return 0;
-
- /* Button pressed */
-
- dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
- *ir_key = b;
- *ir_raw = b;
- return 1;
-}
-
-static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- unsigned char b;
-
- /* poll IR chip */
- if (1 != i2c_master_recv(ir->c, &b, 1)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- /* no button press */
- if (b==0)
- return 0;
-
- /* repeating */
- if (b & 0x80)
- return 1;
-
- *ir_key = b;
- *ir_raw = b;
- return 1;
-}
-
-static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- unsigned char buf[5];
-
- /* poll IR chip */
- if (5 != i2c_master_recv(ir->c, buf, 5))
- return -EIO;
-
- /* Check if some key were pressed */
- if (!(buf[0] & 0x80))
- return 0;
-
- /*
- * buf[3] & 0x80 is always high.
- * buf[3] & 0x40 is a parity bit. A repeat event is marked
- * by preserving it into two separate readings
- * buf[4] bits 0 and 1, and buf[1] and buf[2] are always
- * zero.
- */
- *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2));
- *ir_raw = *ir_key;
- return 1;
-}
-
-
-static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- unsigned char data[12];
- u32 gpio;
-
- struct saa7134_dev *dev = ir->c->adapter->algo_data;
-
- /* rising SAA7134_GPIO_GPRESCAN reads the status */
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
-
- gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
-
- if (0x400000 & ~gpio)
- return 0; /* No button press */
-
- ir->c->addr = 0x5a >> 1;
-
- if (12 != i2c_master_recv(ir->c, data, 12)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- if (data[9] != (unsigned char)(~data[8]))
- return 0;
-
- *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0));
- *ir_key = *ir_raw;
-
- return 1;
-}
-
-/* Common (grey or coloured) pinnacle PCTV remote handling
- *
- */
-static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw,
- int parity_offset, int marker, int code_modulo)
-{
- unsigned char b[4];
- unsigned int start = 0,parity = 0,code = 0;
-
- /* poll IR chip */
- if (4 != i2c_master_recv(ir->c, b, 4)) {
- i2cdprintk("read error\n");
- return -EIO;
- }
-
- for (start = 0; start < ARRAY_SIZE(b); start++) {
- if (b[start] == marker) {
- code=b[(start+parity_offset + 1) % 4];
- parity=b[(start+parity_offset) % 4];
- }
- }
-
- /* Empty Request */
- if (parity == 0)
- return 0;
-
- /* Repeating... */
- if (ir->old == parity)
- return 0;
-
- ir->old = parity;
-
- /* drop special codes when a key is held down a long time for the grey controller
- In this case, the second bit of the code is asserted */
- if (marker == 0xfe && (code & 0x40))
- return 0;
-
- code %= code_modulo;
-
- *ir_raw = code;
- *ir_key = code;
-
- i2cdprintk("Pinnacle PCTV key %02x\n", code);
-
- return 1;
-}
-
-/* The grey pinnacle PCTV remote
- *
- * There are one issue with this remote:
- * - I2c packet does not change when the same key is pressed quickly. The workaround
- * is to hold down each key for about half a second, so that another code is generated
- * in the i2c packet, and the function can distinguish key presses.
- *
- * Sylvain Pasche <sylvain.pasche@gmail.com>
- */
-static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
-
- return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff);
-}
-
-
-/* The new pinnacle PCTV remote (with the colored buttons)
- *
- * Ricardo Cerqueira <v4l@cerqueira.org>
- */
-static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
-{
- /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE
- *
- * this is the only value that results in 42 unique
- * codes < 128
- */
-
- return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88);
-}
-
-void saa7134_input_irq(struct saa7134_dev *dev)
-{
- struct saa7134_card_ir *ir;
-
- if (!dev || !dev->remote)
- return;
-
- ir = dev->remote;
- if (!ir->running)
- return;
-
- if (!ir->polling && !ir->raw_decode) {
- build_key(dev);
- } else if (ir->raw_decode) {
- saa7134_raw_decode_irq(dev);
- }
-}
-
-static void saa7134_input_timer(unsigned long data)
-{
- struct saa7134_dev *dev = (struct saa7134_dev *)data;
- struct saa7134_card_ir *ir = dev->remote;
-
- build_key(dev);
- mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
-}
-
-static void ir_raw_decode_timer_end(unsigned long data)
-{
- struct saa7134_dev *dev = (struct saa7134_dev *)data;
- struct saa7134_card_ir *ir = dev->remote;
-
- ir_raw_event_handle(dev->remote->dev);
-
- ir->active = false;
-}
-
-static int __saa7134_ir_start(void *priv)
-{
- struct saa7134_dev *dev = priv;
- struct saa7134_card_ir *ir;
-
- if (!dev || !dev->remote)
- return -EINVAL;
-
- ir = dev->remote;
- if (ir->running)
- return 0;
-
- /* Moved here from saa7134_input_init1() because the latter
- * is not called on device resume */
- switch (dev->board) {
- case SAA7134_BOARD_MD2819:
- case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
- case SAA7134_BOARD_AVERMEDIA_305:
- case SAA7134_BOARD_AVERMEDIA_307:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
- case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
- case SAA7134_BOARD_AVERMEDIA_M102:
- case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
- /* Without this we won't receive key up events */
- saa_setb(SAA7134_GPIO_GPMODE0, 0x4);
- saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4);
- break;
- case SAA7134_BOARD_AVERMEDIA_777:
- case SAA7134_BOARD_AVERMEDIA_A16AR:
- /* Without this we won't receive key up events */
- saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
- saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
- break;
- case SAA7134_BOARD_AVERMEDIA_A16D:
- /* Without this we won't receive key up events */
- saa_setb(SAA7134_GPIO_GPMODE1, 0x1);
- saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1);
- break;
- case SAA7134_BOARD_GOTVIEW_7135:
- saa_setb(SAA7134_GPIO_GPMODE1, 0x80);
- break;
- }
-
- ir->running = true;
- ir->active = false;
-
- if (ir->polling) {
- setup_timer(&ir->timer, saa7134_input_timer,
- (unsigned long)dev);
- ir->timer.expires = jiffies + HZ;
- add_timer(&ir->timer);
- } else if (ir->raw_decode) {
- /* set timer_end for code completion */
- setup_timer(&ir->timer, ir_raw_decode_timer_end,
- (unsigned long)dev);
- }
-
- return 0;
-}
-
-static void __saa7134_ir_stop(void *priv)
-{
- struct saa7134_dev *dev = priv;
- struct saa7134_card_ir *ir;
-
- if (!dev || !dev->remote)
- return;
-
- ir = dev->remote;
- if (!ir->running)
- return;
-
- if (ir->polling || ir->raw_decode)
- del_timer_sync(&ir->timer);
-
- ir->active = false;
- ir->running = false;
-
- return;
-}
-
-int saa7134_ir_start(struct saa7134_dev *dev)
-{
- if (dev->remote->users)
- return __saa7134_ir_start(dev);
-
- return 0;
-}
-
-void saa7134_ir_stop(struct saa7134_dev *dev)
-{
- if (dev->remote->users)
- __saa7134_ir_stop(dev);
-}
-
-static int saa7134_ir_open(struct rc_dev *rc)
-{
- struct saa7134_dev *dev = rc->priv;
-
- dev->remote->users++;
- return __saa7134_ir_start(dev);
-}
-
-static void saa7134_ir_close(struct rc_dev *rc)
-{
- struct saa7134_dev *dev = rc->priv;
-
- dev->remote->users--;
- if (!dev->remote->users)
- __saa7134_ir_stop(dev);
-}
-
-int saa7134_input_init1(struct saa7134_dev *dev)
-{
- struct saa7134_card_ir *ir;
- struct rc_dev *rc;
- char *ir_codes = NULL;
- u32 mask_keycode = 0;
- u32 mask_keydown = 0;
- u32 mask_keyup = 0;
- unsigned polling = 0;
- bool raw_decode = false;
- int err;
-
- if (dev->has_remote != SAA7134_REMOTE_GPIO)
- return -ENODEV;
- if (disable_ir)
- return -ENODEV;
-
- /* detect & configure */
- switch (dev->board) {
- case SAA7134_BOARD_FLYVIDEO2000:
- case SAA7134_BOARD_FLYVIDEO3000:
- case SAA7134_BOARD_FLYTVPLATINUM_FM:
- case SAA7134_BOARD_FLYTVPLATINUM_MINI2:
- case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM:
- ir_codes = RC_MAP_FLYVIDEO;
- mask_keycode = 0xEC00000;
- mask_keydown = 0x0040000;
- break;
- case SAA7134_BOARD_CINERGY400:
- case SAA7134_BOARD_CINERGY600:
- case SAA7134_BOARD_CINERGY600_MK3:
- ir_codes = RC_MAP_CINERGY;
- mask_keycode = 0x00003f;
- mask_keyup = 0x040000;
- break;
- case SAA7134_BOARD_ECS_TVP3XP:
- case SAA7134_BOARD_ECS_TVP3XP_4CB5:
- ir_codes = RC_MAP_EZTV;
- mask_keycode = 0x00017c;
- mask_keyup = 0x000002;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_KWORLD_XPERT:
- case SAA7134_BOARD_AVACSSMARTTV:
- ir_codes = RC_MAP_PIXELVIEW;
- mask_keycode = 0x00001F;
- mask_keyup = 0x000020;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_MD2819:
- case SAA7134_BOARD_KWORLD_VSTREAM_XPERT:
- case SAA7134_BOARD_AVERMEDIA_305:
- case SAA7134_BOARD_AVERMEDIA_307:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_305:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_505:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_307:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_507:
- case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA:
- case SAA7134_BOARD_AVERMEDIA_GO_007_FM:
- case SAA7134_BOARD_AVERMEDIA_M102:
- case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS:
- ir_codes = RC_MAP_AVERMEDIA;
- mask_keycode = 0x0007C8;
- mask_keydown = 0x000010;
- polling = 50; // ms
- /* GPIO stuff moved to __saa7134_ir_start() */
- break;
- case SAA7134_BOARD_AVERMEDIA_M135A:
- ir_codes = RC_MAP_AVERMEDIA_M135A;
- mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- case SAA7134_BOARD_AVERMEDIA_M733A:
- ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6;
- mask_keydown = 0x0040000;
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- case SAA7134_BOARD_AVERMEDIA_777:
- case SAA7134_BOARD_AVERMEDIA_A16AR:
- ir_codes = RC_MAP_AVERMEDIA;
- mask_keycode = 0x02F200;
- mask_keydown = 0x000400;
- polling = 50; // ms
- /* GPIO stuff moved to __saa7134_ir_start() */
- break;
- case SAA7134_BOARD_AVERMEDIA_A16D:
- ir_codes = RC_MAP_AVERMEDIA_A16D;
- mask_keycode = 0x02F200;
- mask_keydown = 0x000400;
- polling = 50; /* ms */
- /* GPIO stuff moved to __saa7134_ir_start() */
- break;
- case SAA7134_BOARD_KWORLD_TERMINATOR:
- ir_codes = RC_MAP_PIXELVIEW;
- mask_keycode = 0x00001f;
- mask_keyup = 0x000060;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_MANLI_MTV001:
- case SAA7134_BOARD_MANLI_MTV002:
- ir_codes = RC_MAP_MANLI;
- mask_keycode = 0x001f00;
- mask_keyup = 0x004000;
- polling = 50; /* ms */
- break;
- case SAA7134_BOARD_BEHOLD_409FM:
- case SAA7134_BOARD_BEHOLD_401:
- case SAA7134_BOARD_BEHOLD_403:
- case SAA7134_BOARD_BEHOLD_403FM:
- case SAA7134_BOARD_BEHOLD_405:
- case SAA7134_BOARD_BEHOLD_405FM:
- case SAA7134_BOARD_BEHOLD_407:
- case SAA7134_BOARD_BEHOLD_407FM:
- case SAA7134_BOARD_BEHOLD_409:
- case SAA7134_BOARD_BEHOLD_505FM:
- case SAA7134_BOARD_BEHOLD_505RDS_MK5:
- case SAA7134_BOARD_BEHOLD_505RDS_MK3:
- case SAA7134_BOARD_BEHOLD_507_9FM:
- case SAA7134_BOARD_BEHOLD_507RDS_MK3:
- case SAA7134_BOARD_BEHOLD_507RDS_MK5:
- ir_codes = RC_MAP_MANLI;
- mask_keycode = 0x003f00;
- mask_keyup = 0x004000;
- polling = 50; /* ms */
- break;
- case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
- ir_codes = RC_MAP_BEHOLD_COLUMBUS;
- mask_keycode = 0x003f00;
- mask_keyup = 0x004000;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
- ir_codes = RC_MAP_PCTV_SEDNA;
- mask_keycode = 0x001f00;
- mask_keyup = 0x004000;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_GOTVIEW_7135:
- ir_codes = RC_MAP_GOTVIEW7135;
- mask_keycode = 0x0003CC;
- mask_keydown = 0x000010;
- polling = 5; /* ms */
- /* GPIO stuff moved to __saa7134_ir_start() */
- break;
- case SAA7134_BOARD_VIDEOMATE_TV_PVR:
- case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS:
- case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII:
- ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
- mask_keycode = 0x00003F;
- mask_keyup = 0x400000;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_PROTEUS_2309:
- ir_codes = RC_MAP_PROTEUS_2309;
- mask_keycode = 0x00007F;
- mask_keyup = 0x000080;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_VIDEOMATE_DVBT_300:
- case SAA7134_BOARD_VIDEOMATE_DVBT_200:
- ir_codes = RC_MAP_VIDEOMATE_TV_PVR;
- mask_keycode = 0x003F00;
- mask_keyup = 0x040000;
- break;
- case SAA7134_BOARD_FLYDVBS_LR300:
- case SAA7134_BOARD_FLYDVBT_LR301:
- case SAA7134_BOARD_FLYDVBTDUO:
- ir_codes = RC_MAP_FLYDVB;
- mask_keycode = 0x0001F00;
- mask_keydown = 0x0040000;
- break;
- case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
- case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
- case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
- ir_codes = RC_MAP_ASUS_PC39;
- mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- case SAA7134_BOARD_ASUSTeK_PS3_100:
- ir_codes = RC_MAP_ASUS_PS3_100;
- mask_keydown = 0x0040000;
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- case SAA7134_BOARD_ENCORE_ENLTV:
- case SAA7134_BOARD_ENCORE_ENLTV_FM:
- ir_codes = RC_MAP_ENCORE_ENLTV;
- mask_keycode = 0x00007f;
- mask_keyup = 0x040000;
- polling = 50; // ms
- break;
- case SAA7134_BOARD_ENCORE_ENLTV_FM53:
- case SAA7134_BOARD_ENCORE_ENLTV_FM3:
- ir_codes = RC_MAP_ENCORE_ENLTV_FM53;
- mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- case SAA7134_BOARD_10MOONSTVMASTER3:
- ir_codes = RC_MAP_ENCORE_ENLTV;
- mask_keycode = 0x5f80000;
- mask_keyup = 0x8000000;
- polling = 50; //ms
- break;
- case SAA7134_BOARD_GENIUS_TVGO_A11MCE:
- ir_codes = RC_MAP_GENIUS_TVGO_A11MCE;
- mask_keycode = 0xff;
- mask_keydown = 0xf00000;
- polling = 50; /* ms */
- break;
- case SAA7134_BOARD_REAL_ANGEL_220:
- ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS;
- mask_keycode = 0x3f00;
- mask_keyup = 0x4000;
- polling = 50; /* ms */
- break;
- case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
- ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG;
- mask_keycode = 0x7f;
- polling = 40; /* ms */
- break;
- case SAA7134_BOARD_VIDEOMATE_S350:
- ir_codes = RC_MAP_VIDEOMATE_S350;
- mask_keycode = 0x003f00;
- mask_keydown = 0x040000;
- break;
- case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S:
- ir_codes = RC_MAP_WINFAST;
- mask_keycode = 0x5f00;
- mask_keyup = 0x020000;
- polling = 50; /* ms */
- break;
- case SAA7134_BOARD_VIDEOMATE_M1F:
- ir_codes = RC_MAP_VIDEOMATE_K100;
- mask_keycode = 0x0ff00;
- mask_keyup = 0x040000;
- break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1150:
- case SAA7134_BOARD_HAUPPAUGE_HVR1120:
- ir_codes = RC_MAP_HAUPPAUGE;
- mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */
- mask_keyup = 0x0040000;
- mask_keycode = 0xffff;
- raw_decode = true;
- break;
- }
- if (NULL == ir_codes) {
- printk("%s: Oops: IR config error [card=%d]\n",
- dev->name, dev->board);
- return -ENODEV;
- }
-
- ir = kzalloc(sizeof(*ir), GFP_KERNEL);
- rc = rc_allocate_device();
- if (!ir || !rc) {
- err = -ENOMEM;
- goto err_out_free;
- }
-
- ir->dev = rc;
- dev->remote = ir;
-
- /* init hardware-specific stuff */
- ir->mask_keycode = mask_keycode;
- ir->mask_keydown = mask_keydown;
- ir->mask_keyup = mask_keyup;
- ir->polling = polling;
- ir->raw_decode = raw_decode;
-
- /* init input device */
- snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
- saa7134_boards[dev->board].name);
- snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
- pci_name(dev->pci));
-
- rc->priv = dev;
- rc->open = saa7134_ir_open;
- rc->close = saa7134_ir_close;
- if (raw_decode)
- rc->driver_type = RC_DRIVER_IR_RAW;
-
- rc->input_name = ir->name;
- rc->input_phys = ir->phys;
- rc->input_id.bustype = BUS_PCI;
- rc->input_id.version = 1;
- if (dev->pci->subsystem_vendor) {
- rc->input_id.vendor = dev->pci->subsystem_vendor;
- rc->input_id.product = dev->pci->subsystem_device;
- } else {
- rc->input_id.vendor = dev->pci->vendor;
- rc->input_id.product = dev->pci->device;
- }
- rc->dev.parent = &dev->pci->dev;
- rc->map_name = ir_codes;
- rc->driver_name = MODULE_NAME;
-
- err = rc_register_device(rc);
- if (err)
- goto err_out_free;
-
- return 0;
-
-err_out_free:
- rc_free_device(rc);
- dev->remote = NULL;
- kfree(ir);
- return err;
-}
-
-void saa7134_input_fini(struct saa7134_dev *dev)
-{
- if (NULL == dev->remote)
- return;
-
- saa7134_ir_stop(dev);
- rc_unregister_device(dev->remote->dev);
- kfree(dev->remote);
- dev->remote = NULL;
-}
-
-void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
-{
- struct i2c_board_info info;
- struct i2c_msg msg_msi = {
- .addr = 0x50,
- .flags = I2C_M_RD,
- .len = 0,
- .buf = NULL,
- };
- int rc;
-
- if (disable_ir) {
- dprintk("IR has been disabled, not probing for i2c remote\n");
- return;
- }
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- memset(&dev->init_data, 0, sizeof(dev->init_data));
- strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
-
- switch (dev->board) {
- case SAA7134_BOARD_PINNACLE_PCTV_110i:
- case SAA7134_BOARD_PINNACLE_PCTV_310i:
- dev->init_data.name = "Pinnacle PCTV";
- if (pinnacle_remote == 0) {
- dev->init_data.get_key = get_key_pinnacle_color;
- dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR;
- info.addr = 0x47;
- } else {
- dev->init_data.get_key = get_key_pinnacle_grey;
- dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
- info.addr = 0x47;
- }
- break;
- case SAA7134_BOARD_UPMOST_PURPLE_TV:
- dev->init_data.name = "Purple TV";
- dev->init_data.get_key = get_key_purpletv;
- dev->init_data.ir_codes = RC_MAP_PURPLETV;
- info.addr = 0x7a;
- break;
- case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS:
- dev->init_data.name = "MSI TV@nywhere Plus";
- dev->init_data.get_key = get_key_msi_tvanywhere_plus;
- dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS;
- /*
- * MSI TV@nyware Plus requires more frequent polling
- * otherwise it will miss some keypresses
- */
- dev->init_data.polling_interval = 50;
- info.addr = 0x30;
- /* MSI TV@nywhere Plus controller doesn't seem to
- respond to probes unless we read something from
- an existing device. Weird...
- REVISIT: might no longer be needed */
- rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
- dprintk("probe 0x%02x @ %s: %s\n",
- msg_msi.addr, dev->i2c_adap.name,
- (1 == rc) ? "yes" : "no");
- break;
- case SAA7134_BOARD_KWORLD_PC150U:
- /* copied and modified from MSI TV@nywhere Plus */
- dev->init_data.name = "Kworld PC150-U";
- dev->init_data.get_key = get_key_kworld_pc150u;
- dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
- info.addr = 0x30;
- /* MSI TV@nywhere Plus controller doesn't seem to
- respond to probes unless we read something from
- an existing device. Weird...
- REVISIT: might no longer be needed */
- rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
- dprintk("probe 0x%02x @ %s: %s\n",
- msg_msi.addr, dev->i2c_adap.name,
- (1 == rc) ? "yes" : "no");
- break;
- case SAA7134_BOARD_HAUPPAUGE_HVR1110:
- dev->init_data.name = "HVR 1110";
- dev->init_data.get_key = get_key_hvr1110;
- dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
- info.addr = 0x71;
- break;
- case SAA7134_BOARD_BEHOLD_607FM_MK3:
- case SAA7134_BOARD_BEHOLD_607FM_MK5:
- case SAA7134_BOARD_BEHOLD_609FM_MK3:
- case SAA7134_BOARD_BEHOLD_609FM_MK5:
- case SAA7134_BOARD_BEHOLD_607RDS_MK3:
- case SAA7134_BOARD_BEHOLD_607RDS_MK5:
- case SAA7134_BOARD_BEHOLD_609RDS_MK3:
- case SAA7134_BOARD_BEHOLD_609RDS_MK5:
- case SAA7134_BOARD_BEHOLD_M6:
- case SAA7134_BOARD_BEHOLD_M63:
- case SAA7134_BOARD_BEHOLD_M6_EXTRA:
- case SAA7134_BOARD_BEHOLD_H6:
- case SAA7134_BOARD_BEHOLD_X7:
- case SAA7134_BOARD_BEHOLD_H7:
- case SAA7134_BOARD_BEHOLD_A7:
- dev->init_data.name = "BeholdTV";
- dev->init_data.get_key = get_key_beholdm6xx;
- dev->init_data.ir_codes = RC_MAP_BEHOLD;
- dev->init_data.type = RC_TYPE_NEC;
- info.addr = 0x2d;
- break;
- case SAA7134_BOARD_AVERMEDIA_CARDBUS_501:
- case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
- info.addr = 0x40;
- break;
- case SAA7134_BOARD_FLYDVB_TRIO:
- dev->init_data.name = "FlyDVB Trio";
- dev->init_data.get_key = get_key_flydvb_trio;
- dev->init_data.ir_codes = RC_MAP_FLYDVB;
- info.addr = 0x0b;
- break;
- default:
- dprintk("No I2C IR support for board %x\n", dev->board);
- return;
- }
-
- if (dev->init_data.name)
- info.platform_data = &dev->init_data;
- i2c_new_device(&dev->i2c_adap, &info);
-}
-
-static int saa7134_raw_decode_irq(struct saa7134_dev *dev)
-{
- struct saa7134_card_ir *ir = dev->remote;
- unsigned long timeout;
- int space;
-
- /* Generate initial event */
- saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
- space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown;
- ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE);
-
- /*
- * Wait 15 ms from the start of the first IR event before processing
- * the event. This time is enough for NEC protocol. May need adjustments
- * to work with other protocols.
- */
- if (!ir->active) {
- timeout = jiffies + msecs_to_jiffies(15);
- mod_timer(&ir->timer, timeout);
- ir->active = true;
- }
-
- return 1;
-}
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
deleted file mode 100644
index 6de10b1e7251..000000000000
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ /dev/null
@@ -1,2661 +0,0 @@
-/*
- *
- * device driver for philips saa7134 based TV cards
- * video4linux video interface
- *
- * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/sort.h>
-
-#include "saa7134-reg.h"
-#include "saa7134.h"
-#include <media/v4l2-common.h>
-#include <media/saa6588.h>
-
-/* ------------------------------------------------------------------ */
-
-unsigned int video_debug;
-static unsigned int gbuffers = 8;
-static unsigned int noninterlaced; /* 0 */
-static unsigned int gbufsize = 720*576*4;
-static unsigned int gbufsize_max = 720*576*4;
-static char secam[] = "--";
-module_param(video_debug, int, 0644);
-MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
-module_param(gbuffers, int, 0444);
-MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32");
-module_param(noninterlaced, int, 0644);
-MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
-module_param_string(secam, secam, sizeof(secam), 0644);
-MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
-
-
-#define dprintk(fmt, arg...) if (video_debug&0x04) \
- printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
-
-/* ------------------------------------------------------------------ */
-/* Defines for Video Output Port Register at address 0x191 */
-
-/* Bit 0: VIP code T bit polarity */
-
-#define VP_T_CODE_P_NON_INVERTED 0x00
-#define VP_T_CODE_P_INVERTED 0x01
-
-/* ------------------------------------------------------------------ */
-/* Defines for Video Output Port Register at address 0x195 */
-
-/* Bit 2: Video output clock delay control */
-
-#define VP_CLK_CTRL2_NOT_DELAYED 0x00
-#define VP_CLK_CTRL2_DELAYED 0x04
-
-/* Bit 1: Video output clock invert control */
-
-#define VP_CLK_CTRL1_NON_INVERTED 0x00
-#define VP_CLK_CTRL1_INVERTED 0x02
-
-/* ------------------------------------------------------------------ */
-/* Defines for Video Output Port Register at address 0x196 */
-
-/* Bits 2 to 0: VSYNC pin video vertical sync type */
-
-#define VP_VS_TYPE_MASK 0x07
-
-#define VP_VS_TYPE_OFF 0x00
-#define VP_VS_TYPE_V123 0x01
-#define VP_VS_TYPE_V_ITU 0x02
-#define VP_VS_TYPE_VGATE_L 0x03
-#define VP_VS_TYPE_RESERVED1 0x04
-#define VP_VS_TYPE_RESERVED2 0x05
-#define VP_VS_TYPE_F_ITU 0x06
-#define VP_VS_TYPE_SC_FID 0x07
-
-/* ------------------------------------------------------------------ */
-/* data structs for video */
-
-static int video_out[][9] = {
- [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 },
-};
-
-static struct saa7134_format formats[] = {
- {
- .name = "8 bpp gray",
- .fourcc = V4L2_PIX_FMT_GREY,
- .depth = 8,
- .pm = 0x06,
- },{
- .name = "15 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .depth = 16,
- .pm = 0x13 | 0x80,
- },{
- .name = "15 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .depth = 16,
- .pm = 0x13 | 0x80,
- .bswap = 1,
- },{
- .name = "16 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .depth = 16,
- .pm = 0x10 | 0x80,
- },{
- .name = "16 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .depth = 16,
- .pm = 0x10 | 0x80,
- .bswap = 1,
- },{
- .name = "24 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .depth = 24,
- .pm = 0x11,
- },{
- .name = "24 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB24,
- .depth = 24,
- .pm = 0x11,
- .bswap = 1,
- },{
- .name = "32 bpp RGB, le",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .depth = 32,
- .pm = 0x12,
- },{
- .name = "32 bpp RGB, be",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .depth = 32,
- .pm = 0x12,
- .bswap = 1,
- .wswap = 1,
- },{
- .name = "4:2:2 packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- .pm = 0x00,
- .bswap = 1,
- .yuv = 1,
- },{
- .name = "4:2:2 packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- .pm = 0x00,
- .yuv = 1,
- },{
- .name = "4:2:2 planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .depth = 16,
- .pm = 0x09,
- .yuv = 1,
- .planar = 1,
- .hshift = 1,
- .vshift = 0,
- },{
- .name = "4:2:0 planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .pm = 0x0a,
- .yuv = 1,
- .planar = 1,
- .hshift = 1,
- .vshift = 1,
- },{
- .name = "4:2:0 planar, Y-Cb-Cr",
- .fourcc = V4L2_PIX_FMT_YVU420,
- .depth = 12,
- .pm = 0x0a,
- .yuv = 1,
- .planar = 1,
- .uvswap = 1,
- .hshift = 1,
- .vshift = 1,
- }
-};
-#define FORMATS ARRAY_SIZE(formats)
-
-#define NORM_625_50 \
- .h_start = 0, \
- .h_stop = 719, \
- .video_v_start = 24, \
- .video_v_stop = 311, \
- .vbi_v_start_0 = 7, \
- .vbi_v_stop_0 = 22, \
- .vbi_v_start_1 = 319, \
- .src_timing = 4
-
-#define NORM_525_60 \
- .h_start = 0, \
- .h_stop = 719, \
- .video_v_start = 23, \
- .video_v_stop = 262, \
- .vbi_v_start_0 = 10, \
- .vbi_v_stop_0 = 21, \
- .vbi_v_start_1 = 273, \
- .src_timing = 7
-
-static struct saa7134_tvnorm tvnorms[] = {
- {
- .name = "PAL", /* autodetect */
- .id = V4L2_STD_PAL,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x81,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
-
- },{
- .name = "PAL-BG",
- .id = V4L2_STD_PAL_BG,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x81,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
-
- },{
- .name = "PAL-I",
- .id = V4L2_STD_PAL_I,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x81,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
-
- },{
- .name = "PAL-DK",
- .id = V4L2_STD_PAL_DK,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x81,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
-
- },{
- .name = "NTSC",
- .id = V4L2_STD_NTSC,
- NORM_525_60,
-
- .sync_control = 0x59,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x89,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x0e,
- .vgate_misc = 0x18,
-
- },{
- .name = "SECAM",
- .id = V4L2_STD_SECAM,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x1b,
- .chroma_ctrl1 = 0xd1,
- .chroma_gain = 0x80,
- .chroma_ctrl2 = 0x00,
- .vgate_misc = 0x1c,
-
- },{
- .name = "SECAM-DK",
- .id = V4L2_STD_SECAM_DK,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x1b,
- .chroma_ctrl1 = 0xd1,
- .chroma_gain = 0x80,
- .chroma_ctrl2 = 0x00,
- .vgate_misc = 0x1c,
-
- },{
- .name = "SECAM-L",
- .id = V4L2_STD_SECAM_L,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x1b,
- .chroma_ctrl1 = 0xd1,
- .chroma_gain = 0x80,
- .chroma_ctrl2 = 0x00,
- .vgate_misc = 0x1c,
-
- },{
- .name = "SECAM-Lc",
- .id = V4L2_STD_SECAM_LC,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x1b,
- .chroma_ctrl1 = 0xd1,
- .chroma_gain = 0x80,
- .chroma_ctrl2 = 0x00,
- .vgate_misc = 0x1c,
-
- },{
- .name = "PAL-M",
- .id = V4L2_STD_PAL_M,
- NORM_525_60,
-
- .sync_control = 0x59,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0xb9,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x0e,
- .vgate_misc = 0x18,
-
- },{
- .name = "PAL-Nc",
- .id = V4L2_STD_PAL_Nc,
- NORM_625_50,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0xa1,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
-
- },{
- .name = "PAL-60",
- .id = V4L2_STD_PAL_60,
-
- .h_start = 0,
- .h_stop = 719,
- .video_v_start = 23,
- .video_v_stop = 262,
- .vbi_v_start_0 = 10,
- .vbi_v_stop_0 = 21,
- .vbi_v_start_1 = 273,
- .src_timing = 7,
-
- .sync_control = 0x18,
- .luma_control = 0x40,
- .chroma_ctrl1 = 0x81,
- .chroma_gain = 0x2a,
- .chroma_ctrl2 = 0x06,
- .vgate_misc = 0x1c,
- }
-};
-#define TVNORMS ARRAY_SIZE(tvnorms)
-
-#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0)
-#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1)
-#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2)
-#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3)
-#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4)
-
-static const struct v4l2_queryctrl no_ctrl = {
- .name = "42",
- .flags = V4L2_CTRL_FLAG_DISABLED,
-};
-static const struct v4l2_queryctrl video_ctrls[] = {
- /* --- video --- */
- {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 68,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_HUE,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_HFLIP,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
- /* --- audio --- */
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = -15,
- .maximum = 15,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },
- /* --- private --- */
- {
- .id = V4L2_CID_PRIVATE_INVERT,
- .name = "Invert",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_PRIVATE_Y_ODD,
- .name = "y offset odd field",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_Y_EVEN,
- .name = "y offset even field",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 0,
- .type = V4L2_CTRL_TYPE_INTEGER,
- },{
- .id = V4L2_CID_PRIVATE_AUTOMUTE,
- .name = "automute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- }
-};
-static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls);
-
-static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id)
-{
- unsigned int i;
-
- for (i = 0; i < CTRLS; i++)
- if (video_ctrls[i].id == id)
- return video_ctrls+i;
- return NULL;
-}
-
-static struct saa7134_format* format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < FORMATS; i++)
- if (formats[i].fourcc == fourcc)
- return formats+i;
- return NULL;
-}
-
-/* ----------------------------------------------------------------------- */
-/* resource management */
-
-static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit)
-{
- if (fh->resources & bit)
- /* have it already allocated */
- return 1;
-
- /* is it free? */
- mutex_lock(&dev->lock);
- if (dev->resources & bit) {
- /* no, someone else uses it */
- mutex_unlock(&dev->lock);
- return 0;
- }
- /* it's free, grab it */
- fh->resources |= bit;
- dev->resources |= bit;
- dprintk("res: get %d\n",bit);
- mutex_unlock(&dev->lock);
- return 1;
-}
-
-static int res_check(struct saa7134_fh *fh, unsigned int bit)
-{
- return (fh->resources & bit);
-}
-
-static int res_locked(struct saa7134_dev *dev, unsigned int bit)
-{
- return (dev->resources & bit);
-}
-
-static
-void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
-{
- BUG_ON((fh->resources & bits) != bits);
-
- mutex_lock(&dev->lock);
- fh->resources &= ~bits;
- dev->resources &= ~bits;
- dprintk("res: put %d\n",bits);
- mutex_unlock(&dev->lock);
-}
-
-/* ------------------------------------------------------------------ */
-
-static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
-{
- dprintk("set tv norm = %s\n",norm->name);
- dev->tvnorm = norm;
-
- /* setup cropping */
- dev->crop_bounds.left = norm->h_start;
- dev->crop_defrect.left = norm->h_start;
- dev->crop_bounds.width = norm->h_stop - norm->h_start +1;
- dev->crop_defrect.width = norm->h_stop - norm->h_start +1;
-
- dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2;
- dev->crop_defrect.top = norm->video_v_start*2;
- dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624)
- - dev->crop_bounds.top;
- dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2;
-
- dev->crop_current = dev->crop_defrect;
-
- saa7134_set_tvnorm_hw(dev);
-}
-
-static void video_mux(struct saa7134_dev *dev, int input)
-{
- dprintk("video input = %d [%s]\n", input, card_in(dev, input).name);
- dev->ctl_input = input;
- set_tvnorm(dev, dev->tvnorm);
- saa7134_tvaudio_setinput(dev, &card_in(dev, input));
-}
-
-
-static void saa7134_set_decoder(struct saa7134_dev *dev)
-{
- int luma_control, sync_control, mux;
-
- struct saa7134_tvnorm *norm = dev->tvnorm;
- mux = card_in(dev, dev->ctl_input).vmux;
-
- luma_control = norm->luma_control;
- sync_control = norm->sync_control;
-
- if (mux > 5)
- luma_control |= 0x80; /* svideo */
- if (noninterlaced || dev->nosignal)
- sync_control |= 0x20;
-
- /* setup video decoder */
- saa_writeb(SAA7134_INCR_DELAY, 0x08);
- saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux);
- saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00);
-
- saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90);
- saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90);
- saa_writeb(SAA7134_HSYNC_START, 0xeb);
- saa_writeb(SAA7134_HSYNC_STOP, 0xe0);
- saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing);
-
- saa_writeb(SAA7134_SYNC_CTRL, sync_control);
- saa_writeb(SAA7134_LUMA_CTRL, luma_control);
- saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
-
- saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
- dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
-
- saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
- dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
-
- saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
- saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1);
- saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain);
-
- saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2);
- saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00);
-
- saa_writeb(SAA7134_ANALOG_ADC, 0x01);
- saa_writeb(SAA7134_VGATE_START, 0x11);
- saa_writeb(SAA7134_VGATE_STOP, 0xfe);
- saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc);
- saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40);
- saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80);
-}
-
-void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
-{
- saa7134_set_decoder(dev);
-
- if (card_in(dev, dev->ctl_input).tv)
- saa_call_all(dev, core, s_std, dev->tvnorm->id);
- /* Set the correct norm for the saa6752hs. This function
- does nothing if there is no saa6752hs. */
- saa_call_empress(dev, core, s_std, dev->tvnorm->id);
-}
-
-static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
-{
- static const struct {
- int xpsc;
- int xacl;
- int xc2_1;
- int xdcg;
- int vpfy;
- } vals[] = {
- /* XPSC XACL XC2_1 XDCG VPFY */
- { 1, 0, 0, 0, 0 },
- { 2, 2, 1, 2, 2 },
- { 3, 4, 1, 3, 2 },
- { 4, 8, 1, 4, 2 },
- { 5, 8, 1, 4, 2 },
- { 6, 8, 1, 4, 3 },
- { 7, 8, 1, 4, 3 },
- { 8, 15, 0, 4, 3 },
- { 9, 15, 0, 4, 3 },
- { 10, 16, 1, 5, 3 },
- };
- static const int count = ARRAY_SIZE(vals);
- int i;
-
- for (i = 0; i < count; i++)
- if (vals[i].xpsc == prescale)
- break;
- if (i == count)
- return;
-
- saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc);
- saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl);
- saa_writeb(SAA7134_LEVEL_CTRL(task),
- (vals[i].xc2_1 << 3) | (vals[i].xdcg));
- saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f,
- (vals[i].vpfy << 2) | vals[i].vpfy);
-}
-
-static void set_v_scale(struct saa7134_dev *dev, int task, int yscale)
-{
- int val,mirror;
-
- saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff);
- saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8);
-
- mirror = (dev->ctl_mirror) ? 0x02 : 0x00;
- if (yscale < 2048) {
- /* LPI */
- dprintk("yscale LPI yscale=%d\n",yscale);
- saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror);
- saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40);
- saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40);
- } else {
- /* ACM */
- val = 0x40 * 1024 / yscale;
- dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val);
- saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror);
- saa_writeb(SAA7134_LUMA_CONTRAST(task), val);
- saa_writeb(SAA7134_CHROMA_SATURATION(task), val);
- }
- saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80);
-}
-
-static void set_size(struct saa7134_dev *dev, int task,
- int width, int height, int interlace)
-{
- int prescale,xscale,yscale,y_even,y_odd;
- int h_start, h_stop, v_start, v_stop;
- int div = interlace ? 2 : 1;
-
- /* setup video scaler */
- h_start = dev->crop_current.left;
- v_start = dev->crop_current.top/2;
- h_stop = (dev->crop_current.left + dev->crop_current.width -1);
- v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2;
-
- saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff);
- saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8);
- saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff);
- saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8);
- saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff);
- saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8);
- saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff);
- saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8);
-
- prescale = dev->crop_current.width / width;
- if (0 == prescale)
- prescale = 1;
- xscale = 1024 * dev->crop_current.width / prescale / width;
- yscale = 512 * div * dev->crop_current.height / height;
- dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale);
- set_h_prescale(dev,task,prescale);
- saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff);
- saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8);
- set_v_scale(dev,task,yscale);
-
- saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff);
- saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8);
- saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff);
- saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8);
-
- /* deinterlace y offsets */
- y_odd = dev->ctl_y_odd;
- y_even = dev->ctl_y_even;
- saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd);
- saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even);
- saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd);
- saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even);
-}
-
-/* ------------------------------------------------------------------ */
-
-struct cliplist {
- __u16 position;
- __u8 enable;
- __u8 disable;
-};
-
-static void set_cliplist(struct saa7134_dev *dev, int reg,
- struct cliplist *cl, int entries, char *name)
-{
- __u8 winbits = 0;
- int i;
-
- for (i = 0; i < entries; i++) {
- winbits |= cl[i].enable;
- winbits &= ~cl[i].disable;
- if (i < 15 && cl[i].position == cl[i+1].position)
- continue;
- saa_writeb(reg + 0, winbits);
- saa_writeb(reg + 2, cl[i].position & 0xff);
- saa_writeb(reg + 3, cl[i].position >> 8);
- dprintk("clip: %s winbits=%02x pos=%d\n",
- name,winbits,cl[i].position);
- reg += 8;
- }
- for (; reg < 0x400; reg += 8) {
- saa_writeb(reg+ 0, 0);
- saa_writeb(reg + 1, 0);
- saa_writeb(reg + 2, 0);
- saa_writeb(reg + 3, 0);
- }
-}
-
-static int clip_range(int val)
-{
- if (val < 0)
- val = 0;
- return val;
-}
-
-/* Sort into smallest position first order */
-static int cliplist_cmp(const void *a, const void *b)
-{
- const struct cliplist *cla = a;
- const struct cliplist *clb = b;
- if (cla->position < clb->position)
- return -1;
- if (cla->position > clb->position)
- return 1;
- return 0;
-}
-
-static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips,
- int nclips, int interlace)
-{
- struct cliplist col[16], row[16];
- int cols = 0, rows = 0, i;
- int div = interlace ? 2 : 1;
-
- memset(col, 0, sizeof(col));
- memset(row, 0, sizeof(row));
- for (i = 0; i < nclips && i < 8; i++) {
- col[cols].position = clip_range(clips[i].c.left);
- col[cols].enable = (1 << i);
- cols++;
- col[cols].position = clip_range(clips[i].c.left+clips[i].c.width);
- col[cols].disable = (1 << i);
- cols++;
- row[rows].position = clip_range(clips[i].c.top / div);
- row[rows].enable = (1 << i);
- rows++;
- row[rows].position = clip_range((clips[i].c.top + clips[i].c.height)
- / div);
- row[rows].disable = (1 << i);
- rows++;
- }
- sort(col, cols, sizeof col[0], cliplist_cmp, NULL);
- sort(row, rows, sizeof row[0], cliplist_cmp, NULL);
- set_cliplist(dev,0x380,col,cols,"cols");
- set_cliplist(dev,0x384,row,rows,"rows");
- return 0;
-}
-
-static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win)
-{
- enum v4l2_field field;
- int maxw, maxh;
-
- if (NULL == dev->ovbuf.base)
- return -EINVAL;
- if (NULL == dev->ovfmt)
- return -EINVAL;
- if (win->w.width < 48 || win->w.height < 32)
- return -EINVAL;
- if (win->clipcount > 2048)
- return -EINVAL;
-
- field = win->field;
- maxw = dev->crop_current.width;
- maxh = dev->crop_current.height;
-
- if (V4L2_FIELD_ANY == field) {
- field = (win->w.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_TOP;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- win->field = field;
- if (win->w.width > maxw)
- win->w.width = maxw;
- if (win->w.height > maxh)
- win->w.height = maxh;
- return 0;
-}
-
-static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
-{
- unsigned long base,control,bpl;
- int err;
-
- err = verify_preview(dev,&fh->win);
- if (0 != err)
- return err;
-
- dev->ovfield = fh->win.field;
- dprintk("start_preview %dx%d+%d+%d %s field=%s\n",
- fh->win.w.width,fh->win.w.height,
- fh->win.w.left,fh->win.w.top,
- dev->ovfmt->name,v4l2_field_names[dev->ovfield]);
-
- /* setup window + clipping */
- set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height,
- V4L2_FIELD_HAS_BOTH(dev->ovfield));
- setup_clipping(dev,fh->clips,fh->nclips,
- V4L2_FIELD_HAS_BOTH(dev->ovfield));
- if (dev->ovfmt->yuv)
- saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03);
- else
- saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01);
- saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20);
-
- /* dma: setup channel 1 (= Video Task B) */
- base = (unsigned long)dev->ovbuf.base;
- base += dev->ovbuf.fmt.bytesperline * fh->win.w.top;
- base += dev->ovfmt->depth/8 * fh->win.w.left;
- bpl = dev->ovbuf.fmt.bytesperline;
- control = SAA7134_RS_CONTROL_BURST_16;
- if (dev->ovfmt->bswap)
- control |= SAA7134_RS_CONTROL_BSWAP;
- if (dev->ovfmt->wswap)
- control |= SAA7134_RS_CONTROL_WSWAP;
- if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) {
- saa_writel(SAA7134_RS_BA1(1),base);
- saa_writel(SAA7134_RS_BA2(1),base+bpl);
- saa_writel(SAA7134_RS_PITCH(1),bpl*2);
- saa_writel(SAA7134_RS_CONTROL(1),control);
- } else {
- saa_writel(SAA7134_RS_BA1(1),base);
- saa_writel(SAA7134_RS_BA2(1),base);
- saa_writel(SAA7134_RS_PITCH(1),bpl);
- saa_writel(SAA7134_RS_CONTROL(1),control);
- }
-
- /* start dma */
- dev->ovenable = 1;
- saa7134_set_dmabits(dev);
-
- return 0;
-}
-
-static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh)
-{
- dev->ovenable = 0;
- saa7134_set_dmabits(dev);
- return 0;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int buffer_activate(struct saa7134_dev *dev,
- struct saa7134_buf *buf,
- struct saa7134_buf *next)
-{
- unsigned long base,control,bpl;
- unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */
-
- dprintk("buffer_activate buf=%p\n",buf);
- buf->vb.state = VIDEOBUF_ACTIVE;
- buf->top_seen = 0;
-
- set_size(dev,TASK_A,buf->vb.width,buf->vb.height,
- V4L2_FIELD_HAS_BOTH(buf->vb.field));
- if (buf->fmt->yuv)
- saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03);
- else
- saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01);
- saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm);
-
- /* DMA: setup channel 0 (= Video Task A0) */
- base = saa7134_buffer_base(buf);
- if (buf->fmt->planar)
- bpl = buf->vb.width;
- else
- bpl = (buf->vb.width * buf->fmt->depth) / 8;
- control = SAA7134_RS_CONTROL_BURST_16 |
- SAA7134_RS_CONTROL_ME |
- (buf->pt->dma >> 12);
- if (buf->fmt->bswap)
- control |= SAA7134_RS_CONTROL_BSWAP;
- if (buf->fmt->wswap)
- control |= SAA7134_RS_CONTROL_WSWAP;
- if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
- /* interlaced */
- saa_writel(SAA7134_RS_BA1(0),base);
- saa_writel(SAA7134_RS_BA2(0),base+bpl);
- saa_writel(SAA7134_RS_PITCH(0),bpl*2);
- } else {
- /* non-interlaced */
- saa_writel(SAA7134_RS_BA1(0),base);
- saa_writel(SAA7134_RS_BA2(0),base);
- saa_writel(SAA7134_RS_PITCH(0),bpl);
- }
- saa_writel(SAA7134_RS_CONTROL(0),control);
-
- if (buf->fmt->planar) {
- /* DMA: setup channel 4+5 (= planar task A) */
- bpl_uv = bpl >> buf->fmt->hshift;
- lines_uv = buf->vb.height >> buf->fmt->vshift;
- base2 = base + bpl * buf->vb.height;
- base3 = base2 + bpl_uv * lines_uv;
- if (buf->fmt->uvswap)
- tmp = base2, base2 = base3, base3 = tmp;
- dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n",
- bpl_uv,lines_uv,base2,base3);
- if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) {
- /* interlaced */
- saa_writel(SAA7134_RS_BA1(4),base2);
- saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv);
- saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2);
- saa_writel(SAA7134_RS_BA1(5),base3);
- saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv);
- saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2);
- } else {
- /* non-interlaced */
- saa_writel(SAA7134_RS_BA1(4),base2);
- saa_writel(SAA7134_RS_BA2(4),base2);
- saa_writel(SAA7134_RS_PITCH(4),bpl_uv);
- saa_writel(SAA7134_RS_BA1(5),base3);
- saa_writel(SAA7134_RS_BA2(5),base3);
- saa_writel(SAA7134_RS_PITCH(5),bpl_uv);
- }
- saa_writel(SAA7134_RS_CONTROL(4),control);
- saa_writel(SAA7134_RS_CONTROL(5),control);
- }
-
- /* start DMA */
- saa7134_set_dmabits(dev);
- mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT);
- return 0;
-}
-
-static int buffer_prepare(struct videobuf_queue *q,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_dev *dev = fh->dev;
- struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
- unsigned int size;
- int err;
-
- /* sanity checks */
- if (NULL == fh->fmt)
- return -EINVAL;
- if (fh->width < 48 ||
- fh->height < 32 ||
- fh->width/4 > dev->crop_current.width ||
- fh->height/4 > dev->crop_current.height ||
- fh->width > dev->crop_bounds.width ||
- fh->height > dev->crop_bounds.height)
- return -EINVAL;
- size = (fh->width * fh->height * fh->fmt->depth) >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < size)
- return -EINVAL;
-
- dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n",
- vb->i,fh->width,fh->height,size,v4l2_field_names[field],
- fh->fmt->name);
- if (buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
- buf->vb.size != size ||
- buf->vb.field != field ||
- buf->fmt != fh->fmt) {
- saa7134_dma_free(q,buf);
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
-
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.size = size;
- buf->vb.field = field;
- buf->fmt = fh->fmt;
- buf->pt = &fh->pt_cap;
- dev->video_q.curr = NULL;
-
- err = videobuf_iolock(q,&buf->vb,&dev->ovbuf);
- if (err)
- goto oops;
- err = saa7134_pgtable_build(dev->pci,buf->pt,
- dma->sglist,
- dma->sglen,
- saa7134_buffer_startpage(buf));
- if (err)
- goto oops;
- }
- buf->vb.state = VIDEOBUF_PREPARED;
- buf->activate = buffer_activate;
- return 0;
-
- oops:
- saa7134_dma_free(q,buf);
- return err;
-}
-
-static int
-buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
-{
- struct saa7134_fh *fh = q->priv_data;
-
- *size = fh->fmt->depth * fh->width * fh->height >> 3;
- if (0 == *count)
- *count = gbuffers;
- *count = saa7134_buffer_count(*size,*count);
- return 0;
-}
-
-static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct saa7134_fh *fh = q->priv_data;
- struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
-
- saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf);
-}
-
-static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb);
-
- saa7134_dma_free(q,buf);
-}
-
-static struct videobuf_queue_ops video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* ------------------------------------------------------------------ */
-
-int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
-{
- const struct v4l2_queryctrl* ctrl;
-
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl)
- return -EINVAL;
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- c->value = dev->ctl_bright;
- break;
- case V4L2_CID_HUE:
- c->value = dev->ctl_hue;
- break;
- case V4L2_CID_CONTRAST:
- c->value = dev->ctl_contrast;
- break;
- case V4L2_CID_SATURATION:
- c->value = dev->ctl_saturation;
- break;
- case V4L2_CID_AUDIO_MUTE:
- c->value = dev->ctl_mute;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- c->value = dev->ctl_volume;
- break;
- case V4L2_CID_PRIVATE_INVERT:
- c->value = dev->ctl_invert;
- break;
- case V4L2_CID_HFLIP:
- c->value = dev->ctl_mirror;
- break;
- case V4L2_CID_PRIVATE_Y_EVEN:
- c->value = dev->ctl_y_even;
- break;
- case V4L2_CID_PRIVATE_Y_ODD:
- c->value = dev->ctl_y_odd;
- break;
- case V4L2_CID_PRIVATE_AUTOMUTE:
- c->value = dev->ctl_automute;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal);
-
-static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c)
-{
- struct saa7134_fh *fh = priv;
-
- return saa7134_g_ctrl_internal(fh->dev, fh, c);
-}
-
-int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c)
-{
- const struct v4l2_queryctrl* ctrl;
- unsigned long flags;
- int restart_overlay = 0;
- int err;
-
- /* When called from the empress code fh == NULL.
- That needs to be fixed somehow, but for now this is
- good enough. */
- if (fh) {
- err = v4l2_prio_check(&dev->prio, fh->prio);
- if (0 != err)
- return err;
- }
- err = -EINVAL;
-
- mutex_lock(&dev->lock);
-
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl)
- goto error;
-
- dprintk("set_control name=%s val=%d\n",ctrl->name,c->value);
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER:
- if (c->value < ctrl->minimum)
- c->value = ctrl->minimum;
- if (c->value > ctrl->maximum)
- c->value = ctrl->maximum;
- break;
- default:
- /* nothing */;
- };
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS:
- dev->ctl_bright = c->value;
- saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright);
- break;
- case V4L2_CID_HUE:
- dev->ctl_hue = c->value;
- saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue);
- break;
- case V4L2_CID_CONTRAST:
- dev->ctl_contrast = c->value;
- saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
- dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
- break;
- case V4L2_CID_SATURATION:
- dev->ctl_saturation = c->value;
- saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
- dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
- break;
- case V4L2_CID_AUDIO_MUTE:
- dev->ctl_mute = c->value;
- saa7134_tvaudio_setmute(dev);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- dev->ctl_volume = c->value;
- saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
- break;
- case V4L2_CID_PRIVATE_INVERT:
- dev->ctl_invert = c->value;
- saa_writeb(SAA7134_DEC_LUMA_CONTRAST,
- dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast);
- saa_writeb(SAA7134_DEC_CHROMA_SATURATION,
- dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation);
- break;
- case V4L2_CID_HFLIP:
- dev->ctl_mirror = c->value;
- restart_overlay = 1;
- break;
- case V4L2_CID_PRIVATE_Y_EVEN:
- dev->ctl_y_even = c->value;
- restart_overlay = 1;
- break;
- case V4L2_CID_PRIVATE_Y_ODD:
- dev->ctl_y_odd = c->value;
- restart_overlay = 1;
- break;
- case V4L2_CID_PRIVATE_AUTOMUTE:
- {
- struct v4l2_priv_tun_config tda9887_cfg;
-
- tda9887_cfg.tuner = TUNER_TDA9887;
- tda9887_cfg.priv = &dev->tda9887_conf;
-
- dev->ctl_automute = c->value;
- if (dev->tda9887_conf) {
- if (dev->ctl_automute)
- dev->tda9887_conf |= TDA9887_AUTOMUTE;
- else
- dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
-
- saa_call_all(dev, tuner, s_config, &tda9887_cfg);
- }
- break;
- }
- default:
- goto error;
- }
- if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- start_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- }
- err = 0;
-
-error:
- mutex_unlock(&dev->lock);
- return err;
-}
-EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal);
-
-static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c)
-{
- struct saa7134_fh *fh = f;
-
- return saa7134_s_ctrl_internal(fh->dev, fh, c);
-}
-
-/* ------------------------------------------------------------------ */
-
-static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh)
-{
- struct videobuf_queue* q = NULL;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- q = &fh->cap;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- q = &fh->vbi;
- break;
- default:
- BUG();
- }
- return q;
-}
-
-static int saa7134_resource(struct saa7134_fh *fh)
-{
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return RESOURCE_VIDEO;
-
- if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
- return RESOURCE_VBI;
-
- BUG();
- return 0;
-}
-
-static int video_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct saa7134_dev *dev = video_drvdata(file);
- struct saa7134_fh *fh;
- enum v4l2_buf_type type = 0;
- int radio = 0;
-
- switch (vdev->vfl_type) {
- case VFL_TYPE_GRABBER:
- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- break;
- case VFL_TYPE_VBI:
- type = V4L2_BUF_TYPE_VBI_CAPTURE;
- break;
- case VFL_TYPE_RADIO:
- radio = 1;
- break;
- }
-
- dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev),
- radio, v4l2_type_names[type]);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh),GFP_KERNEL);
- if (NULL == fh)
- return -ENOMEM;
-
- file->private_data = fh;
- fh->dev = dev;
- fh->radio = radio;
- fh->type = type;
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
- fh->width = 720;
- fh->height = 576;
- v4l2_prio_open(&dev->prio, &fh->prio);
-
- videobuf_queue_sg_init(&fh->cap, &video_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct saa7134_buf),
- fh, NULL);
- videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops,
- &dev->pci->dev, &dev->slock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_SEQ_TB,
- sizeof(struct saa7134_buf),
- fh, NULL);
- saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
- saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
-
- if (fh->radio) {
- /* switch to radio mode */
- saa7134_tvaudio_setinput(dev,&card(dev).radio);
- saa_call_all(dev, tuner, s_radio);
- } else {
- /* switch to video/vbi mode */
- video_mux(dev,dev->ctl_input);
- }
- return 0;
-}
-
-static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
-{
- struct saa7134_fh *fh = file->private_data;
-
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- return -EBUSY;
- return videobuf_read_one(saa7134_queue(fh),
- data, count, ppos,
- file->f_flags & O_NONBLOCK);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (!res_get(fh->dev,fh,RESOURCE_VBI))
- return -EBUSY;
- return videobuf_read_stream(saa7134_queue(fh),
- data, count, ppos, 1,
- file->f_flags & O_NONBLOCK);
- break;
- default:
- BUG();
- return 0;
- }
-}
-
-static unsigned int
-video_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct saa7134_fh *fh = file->private_data;
- struct videobuf_buffer *buf = NULL;
- unsigned int rc = 0;
-
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
- return videobuf_poll_stream(file, &fh->vbi, wait);
-
- if (res_check(fh,RESOURCE_VIDEO)) {
- mutex_lock(&fh->cap.vb_lock);
- if (!list_empty(&fh->cap.stream))
- buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream);
- } else {
- mutex_lock(&fh->cap.vb_lock);
- if (UNSET == fh->cap.read_off) {
- /* need to capture a new frame */
- if (res_locked(fh->dev,RESOURCE_VIDEO))
- goto err;
- if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field))
- goto err;
- fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf);
- fh->cap.read_off = 0;
- }
- buf = fh->cap.read_buf;
- }
-
- if (!buf)
- goto err;
-
- poll_wait(file, &buf->done, wait);
- if (buf->state == VIDEOBUF_DONE ||
- buf->state == VIDEOBUF_ERROR)
- rc = POLLIN|POLLRDNORM;
- mutex_unlock(&fh->cap.vb_lock);
- return rc;
-
-err:
- mutex_unlock(&fh->cap.vb_lock);
- return POLLERR;
-}
-
-static int video_release(struct file *file)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
- struct saa6588_command cmd;
- unsigned long flags;
-
- saa7134_tvaudio_close(dev);
-
- /* turn off overlay */
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock,flags);
- stop_preview(dev,fh);
- spin_unlock_irqrestore(&dev->slock,flags);
- res_free(dev,fh,RESOURCE_OVERLAY);
- }
-
- /* stop video capture */
- if (res_check(fh, RESOURCE_VIDEO)) {
- videobuf_streamoff(&fh->cap);
- res_free(dev,fh,RESOURCE_VIDEO);
- }
- if (fh->cap.read_buf) {
- buffer_release(&fh->cap,fh->cap.read_buf);
- kfree(fh->cap.read_buf);
- }
-
- /* stop vbi capture */
- if (res_check(fh, RESOURCE_VBI)) {
- videobuf_stop(&fh->vbi);
- res_free(dev,fh,RESOURCE_VBI);
- }
-
- /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/
- saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0);
- saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0);
- saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
- saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
-
- saa_call_all(dev, core, s_power, 0);
- if (fh->radio)
- saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd);
-
- /* free stuff */
- videobuf_mmap_free(&fh->cap);
- videobuf_mmap_free(&fh->vbi);
- saa7134_pgtable_free(dev->pci,&fh->pt_cap);
- saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
-
- v4l2_prio_close(&dev->prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
- return 0;
-}
-
-static int video_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct saa7134_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(saa7134_queue(fh), vma);
-}
-
-static ssize_t radio_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
- struct saa6588_command cmd;
-
- cmd.block_count = count/3;
- cmd.buffer = data;
- cmd.instance = file;
- cmd.result = -ENODEV;
-
- saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd);
-
- return cmd.result;
-}
-
-static unsigned int radio_poll(struct file *file, poll_table *wait)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
- struct saa6588_command cmd;
-
- cmd.instance = file;
- cmd.event_list = wait;
- cmd.result = -ENODEV;
- saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd);
-
- return cmd.result;
-}
-
-/* ------------------------------------------------------------------ */
-
-static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- struct saa7134_tvnorm *norm = dev->tvnorm;
-
- f->fmt.vbi.sampling_rate = 6750000 * 4;
- f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */;
- f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
- f->fmt.vbi.offset = 64 * 4;
- f->fmt.vbi.start[0] = norm->vbi_v_start_0;
- f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1;
- f->fmt.vbi.start[1] = norm->vbi_v_start_1;
- f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
- f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */
-
- return 0;
-}
-
-static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
-
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->cap.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- return 0;
-}
-
-static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
-
- if (saa7134_no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- f->fmt.win = fh->win;
-
- return 0;
-}
-
-static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- struct saa7134_format *fmt;
- enum v4l2_field field;
- unsigned int maxw, maxh;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- field = f->fmt.pix.field;
- maxw = min(dev->crop_current.width*4, dev->crop_bounds.width);
- maxh = min(dev->crop_current.height*4, dev->crop_bounds.height);
-
- if (V4L2_FIELD_ANY == field) {
- field = (f->fmt.pix.height > maxh/2)
- ? V4L2_FIELD_INTERLACED
- : V4L2_FIELD_BOTTOM;
- }
- switch (field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- maxh = maxh / 2;
- break;
- case V4L2_FIELD_INTERLACED:
- break;
- default:
- return -EINVAL;
- }
-
- f->fmt.pix.field = field;
- if (f->fmt.pix.width < 48)
- f->fmt.pix.width = 48;
- if (f->fmt.pix.height < 32)
- f->fmt.pix.height = 32;
- if (f->fmt.pix.width > maxw)
- f->fmt.pix.width = maxw;
- if (f->fmt.pix.height > maxh)
- f->fmt.pix.height = maxh;
- f->fmt.pix.width &= ~0x03;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- if (saa7134_no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
-
- return verify_preview(dev, &f->fmt.win);
-}
-
-static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
- int err;
-
- err = saa7134_try_fmt_vid_cap(file, priv, f);
- if (0 != err)
- return err;
-
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->cap.field = f->fmt.pix.field;
- return 0;
-}
-
-static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int err;
- unsigned long flags;
-
- if (saa7134_no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
- err = verify_preview(dev, &f->fmt.win);
- if (0 != err)
- return err;
-
- mutex_lock(&dev->lock);
-
- fh->win = f->fmt.win;
- fh->nclips = f->fmt.win.clipcount;
-
- if (fh->nclips > 8)
- fh->nclips = 8;
-
- if (copy_from_user(fh->clips, f->fmt.win.clips,
- sizeof(struct v4l2_clip)*fh->nclips)) {
- mutex_unlock(&dev->lock);
- return -EFAULT;
- }
-
- if (res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
- start_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
- }
-
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c)
-{
- const struct v4l2_queryctrl *ctrl;
-
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
- ctrl = ctrl_by_id(c->id);
- *c = (NULL != ctrl) ? *ctrl : no_ctrl;
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_queryctrl);
-
-static int saa7134_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- unsigned int n;
-
- n = i->index;
- if (n >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev, i->index).name)
- return -EINVAL;
- i->index = n;
- i->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(i->name, card_in(dev, n).name);
- if (card_in(dev, n).tv)
- i->type = V4L2_INPUT_TYPE_TUNER;
- i->audioset = 1;
- if (n == dev->ctl_input) {
- int v1 = saa_readb(SAA7134_STATUS_VIDEO1);
- int v2 = saa_readb(SAA7134_STATUS_VIDEO2);
-
- if (0 != (v1 & 0x40))
- i->status |= V4L2_IN_ST_NO_H_LOCK;
- if (0 != (v2 & 0x40))
- i->status |= V4L2_IN_ST_NO_SYNC;
- if (0 != (v2 & 0x0e))
- i->status |= V4L2_IN_ST_MACROVISION;
- }
- i->std = SAA7134_NORMS;
- return 0;
-}
-
-static int saa7134_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- *i = dev->ctl_input;
- return 0;
-}
-
-static int saa7134_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int err;
-
- err = v4l2_prio_check(&dev->prio, fh->prio);
- if (0 != err)
- return err;
-
- if (i >= SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL == card_in(dev, i).name)
- return -EINVAL;
- mutex_lock(&dev->lock);
- video_mux(dev, i);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int saa7134_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- unsigned int tuner_type = dev->tuner_type;
-
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name,
- sizeof(cap->card));
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VBI_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_TUNER;
- if (dev->has_rds)
- cap->capabilities |= V4L2_CAP_RDS_CAPTURE;
- if (saa7134_no_overlay <= 0)
- cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY;
-
- if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET))
- cap->capabilities &= ~V4L2_CAP_TUNER;
- return 0;
-}
-
-int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id)
-{
- unsigned long flags;
- unsigned int i;
- v4l2_std_id fixup;
- int err;
-
- /* When called from the empress code fh == NULL.
- That needs to be fixed somehow, but for now this is
- good enough. */
- if (fh) {
- err = v4l2_prio_check(&dev->prio, fh->prio);
- if (0 != err)
- return err;
- } else if (res_locked(dev, RESOURCE_OVERLAY)) {
- /* Don't change the std from the mpeg device
- if overlay is active. */
- return -EBUSY;
- }
-
- for (i = 0; i < TVNORMS; i++)
- if (*id == tvnorms[i].id)
- break;
-
- if (i == TVNORMS)
- for (i = 0; i < TVNORMS; i++)
- if (*id & tvnorms[i].id)
- break;
- if (i == TVNORMS)
- return -EINVAL;
-
- if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) {
- if (secam[0] == 'L' || secam[0] == 'l') {
- if (secam[1] == 'C' || secam[1] == 'c')
- fixup = V4L2_STD_SECAM_LC;
- else
- fixup = V4L2_STD_SECAM_L;
- } else {
- if (secam[0] == 'D' || secam[0] == 'd')
- fixup = V4L2_STD_SECAM_DK;
- else
- fixup = V4L2_STD_SECAM;
- }
- for (i = 0; i < TVNORMS; i++) {
- if (fixup == tvnorms[i].id)
- break;
- }
- if (i == TVNORMS)
- return -EINVAL;
- }
-
- *id = tvnorms[i].id;
-
- mutex_lock(&dev->lock);
- if (fh && res_check(fh, RESOURCE_OVERLAY)) {
- spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- set_tvnorm(dev, &tvnorms[i]);
-
- spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
- } else
- set_tvnorm(dev, &tvnorms[i]);
-
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
-}
-EXPORT_SYMBOL_GPL(saa7134_s_std_internal);
-
-static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct saa7134_fh *fh = priv;
-
- return saa7134_s_std_internal(fh->dev, fh, id);
-}
-
-static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- *id = dev->tvnorm->id;
- return 0;
-}
-
-static int saa7134_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *cap)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- cap->bounds = dev->crop_bounds;
- cap->defrect = dev->crop_defrect;
- cap->pixelaspect.numerator = 1;
- cap->pixelaspect.denominator = 1;
- if (dev->tvnorm->id & V4L2_STD_525_60) {
- cap->pixelaspect.numerator = 11;
- cap->pixelaspect.denominator = 10;
- }
- if (dev->tvnorm->id & V4L2_STD_625_50) {
- cap->pixelaspect.numerator = 54;
- cap->pixelaspect.denominator = 59;
- }
- return 0;
-}
-
-static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- crop->c = dev->crop_current;
- return 0;
-}
-
-static int saa7134_s_crop(struct file *file, void *f, struct v4l2_crop *crop)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
- struct v4l2_rect *b = &dev->crop_bounds;
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
- return -EINVAL;
- if (crop->c.height < 0)
- return -EINVAL;
- if (crop->c.width < 0)
- return -EINVAL;
-
- if (res_locked(fh->dev, RESOURCE_OVERLAY))
- return -EBUSY;
- if (res_locked(fh->dev, RESOURCE_VIDEO))
- return -EBUSY;
-
- if (crop->c.top < b->top)
- crop->c.top = b->top;
- if (crop->c.top > b->top + b->height)
- crop->c.top = b->top + b->height;
- if (crop->c.height > b->top - crop->c.top + b->height)
- crop->c.height = b->top - crop->c.top + b->height;
-
- if (crop->c.left < b->left)
- crop->c.left = b->left;
- if (crop->c.left > b->left + b->width)
- crop->c.left = b->left + b->width;
- if (crop->c.width > b->left - crop->c.left + b->width)
- crop->c.width = b->left - crop->c.left + b->width;
-
- dev->crop_current = crop->c;
- return 0;
-}
-
-static int saa7134_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int n;
-
- if (0 != t->index)
- return -EINVAL;
- memset(t, 0, sizeof(*t));
- for (n = 0; n < SAA7134_INPUT_MAX; n++) {
- if (card_in(dev, n).tv)
- break;
- }
- if (n == SAA7134_INPUT_MAX)
- return -EINVAL;
- if (NULL != card_in(dev, n).name) {
- strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM |
- V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2;
- t->rangehigh = 0xffffffffUL;
- t->rxsubchans = saa7134_tvaudio_getstereo(dev);
- t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans);
- }
- if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03))
- t->signal = 0xffff;
- return 0;
-}
-
-static int saa7134_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int rx, mode, err;
-
- err = v4l2_prio_check(&dev->prio, fh->prio);
- if (0 != err)
- return err;
-
- mode = dev->thread.mode;
- if (UNSET == mode) {
- rx = saa7134_tvaudio_getstereo(dev);
- mode = saa7134_tvaudio_rx2mode(rx);
- }
- if (mode != t->audmode)
- dev->thread.mode = t->audmode;
-
- return 0;
-}
-
-static int saa7134_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- f->frequency = dev->ctl_freq;
-
- return 0;
-}
-
-static int saa7134_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int err;
-
- err = v4l2_prio_check(&dev->prio, fh->prio);
- if (0 != err)
- return err;
-
- if (0 != f->tuner)
- return -EINVAL;
- if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type)
- return -EINVAL;
- if (1 == fh->radio && V4L2_TUNER_RADIO != f->type)
- return -EINVAL;
- mutex_lock(&dev->lock);
- dev->ctl_freq = f->frequency;
-
- saa_call_all(dev, tuner, s_frequency, f);
-
- saa7134_tvaudio_do_scan(dev);
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- strcpy(a->name, "audio");
- return 0;
-}
-
-static int saa7134_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- return 0;
-}
-
-static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
-
- *p = v4l2_prio_max(&dev->prio);
- return 0;
-}
-
-static int saa7134_s_priority(struct file *file, void *f,
- enum v4l2_priority prio)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
-
- return v4l2_prio_change(&dev->prio, &fh->prio, prio);
-}
-
-static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (f->index >= FORMATS)
- return -EINVAL;
-
- strlcpy(f->description, formats[f->index].name,
- sizeof(f->description));
-
- f->pixelformat = formats[f->index].fourcc;
-
- return 0;
-}
-
-static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (saa7134_no_overlay > 0) {
- printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n");
- return -EINVAL;
- }
-
- if ((f->index >= FORMATS) || formats[f->index].planar)
- return -EINVAL;
-
- strlcpy(f->description, formats[f->index].name,
- sizeof(f->description));
-
- f->pixelformat = formats[f->index].fourcc;
-
- return 0;
-}
-
-static int saa7134_g_fbuf(struct file *file, void *f,
- struct v4l2_framebuffer *fb)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
-
- *fb = dev->ovbuf;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
-
- return 0;
-}
-
-static int saa7134_s_fbuf(struct file *file, void *f,
- struct v4l2_framebuffer *fb)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
- struct saa7134_format *fmt;
-
- if (!capable(CAP_SYS_ADMIN) &&
- !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* check args */
- fmt = format_by_fourcc(fb->fmt.pixelformat);
- if (NULL == fmt)
- return -EINVAL;
-
- /* ok, accept it */
- dev->ovbuf = *fb;
- dev->ovfmt = fmt;
- if (0 == dev->ovbuf.fmt.bytesperline)
- dev->ovbuf.fmt.bytesperline =
- dev->ovbuf.fmt.width*fmt->depth/8;
- return 0;
-}
-
-static int saa7134_overlay(struct file *file, void *f, unsigned int on)
-{
- struct saa7134_fh *fh = f;
- struct saa7134_dev *dev = fh->dev;
- unsigned long flags;
-
- if (on) {
- if (saa7134_no_overlay > 0) {
- dprintk("no_overlay\n");
- return -EINVAL;
- }
-
- if (!res_get(dev, fh, RESOURCE_OVERLAY))
- return -EBUSY;
- spin_lock_irqsave(&dev->slock, flags);
- start_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
- }
- if (!on) {
- if (!res_check(fh, RESOURCE_OVERLAY))
- return -EINVAL;
- spin_lock_irqsave(&dev->slock, flags);
- stop_preview(dev, fh);
- spin_unlock_irqrestore(&dev->slock, flags);
- res_free(dev, fh, RESOURCE_OVERLAY);
- }
- return 0;
-}
-
-static int saa7134_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct saa7134_fh *fh = priv;
- return videobuf_reqbufs(saa7134_queue(fh), p);
-}
-
-static int saa7134_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct saa7134_fh *fh = priv;
- return videobuf_querybuf(saa7134_queue(fh), b);
-}
-
-static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct saa7134_fh *fh = priv;
- return videobuf_qbuf(saa7134_queue(fh), b);
-}
-
-static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct saa7134_fh *fh = priv;
- return videobuf_dqbuf(saa7134_queue(fh), b,
- file->f_flags & O_NONBLOCK);
-}
-
-static int saa7134_streamon(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int res = saa7134_resource(fh);
-
- if (!res_get(dev, fh, res))
- return -EBUSY;
-
- return videobuf_streamon(saa7134_queue(fh));
-}
-
-static int saa7134_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type type)
-{
- int err;
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
- int res = saa7134_resource(fh);
-
- err = videobuf_streamoff(saa7134_queue(fh));
- if (err < 0)
- return err;
- res_free(dev, fh, res);
- return 0;
-}
-
-static int saa7134_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *parm)
-{
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register (struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- reg->val = saa_readb(reg->reg);
- reg->size = 1;
- return 0;
-}
-
-static int vidioc_s_register (struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- saa_writeb(reg->reg&0xffffff, reg->val);
- return 0;
-}
-#endif
-
-static int radio_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
-
- strcpy(cap->driver, "saa7134");
- strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card));
- sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->capabilities = V4L2_CAP_TUNER;
- return 0;
-}
-
-static int radio_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
-
- if (0 != t->index)
- return -EINVAL;
-
- memset(t, 0, sizeof(*t));
- strcpy(t->name, "Radio");
- t->type = V4L2_TUNER_RADIO;
-
- saa_call_all(dev, tuner, g_tuner, t);
- if (dev->input->amux == TV) {
- t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
- t->rxsubchans = (saa_readb(0x529) & 0x08) ?
- V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
- }
- return 0;
-}
-static int radio_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct saa7134_fh *fh = file->private_data;
- struct saa7134_dev *dev = fh->dev;
-
- if (0 != t->index)
- return -EINVAL;
-
- saa_call_all(dev, tuner, s_tuner, t);
- return 0;
-}
-
-static int radio_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- if (i->index != 0)
- return -EINVAL;
-
- strcpy(i->name, "Radio");
- i->type = V4L2_INPUT_TYPE_TUNER;
-
- return 0;
-}
-
-static int radio_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int radio_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- memset(a, 0, sizeof(*a));
- strcpy(a->name, "Radio");
- return 0;
-}
-
-static int radio_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return 0;
-}
-
-static int radio_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return 0;
-}
-
-static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- return 0;
-}
-
-static int radio_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *c)
-{
- const struct v4l2_queryctrl *ctrl;
-
- if (c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1)
- return -EINVAL;
- if (c->id == V4L2_CID_AUDIO_MUTE) {
- ctrl = ctrl_by_id(c->id);
- *c = *ctrl;
- } else
- *c = no_ctrl;
- return 0;
-}
-
-static const struct v4l2_file_operations video_fops =
-{
- .owner = THIS_MODULE,
- .open = video_open,
- .release = video_release,
- .read = video_read,
- .poll = video_poll,
- .mmap = video_mmap,
- .ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = saa7134_querycap,
- .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
- .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
- .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
- .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
- .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
- .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
- .vidioc_g_audio = saa7134_g_audio,
- .vidioc_s_audio = saa7134_s_audio,
- .vidioc_cropcap = saa7134_cropcap,
- .vidioc_reqbufs = saa7134_reqbufs,
- .vidioc_querybuf = saa7134_querybuf,
- .vidioc_qbuf = saa7134_qbuf,
- .vidioc_dqbuf = saa7134_dqbuf,
- .vidioc_s_std = saa7134_s_std,
- .vidioc_g_std = saa7134_g_std,
- .vidioc_enum_input = saa7134_enum_input,
- .vidioc_g_input = saa7134_g_input,
- .vidioc_s_input = saa7134_s_input,
- .vidioc_queryctrl = saa7134_queryctrl,
- .vidioc_g_ctrl = saa7134_g_ctrl,
- .vidioc_s_ctrl = saa7134_s_ctrl,
- .vidioc_streamon = saa7134_streamon,
- .vidioc_streamoff = saa7134_streamoff,
- .vidioc_g_tuner = saa7134_g_tuner,
- .vidioc_s_tuner = saa7134_s_tuner,
- .vidioc_g_crop = saa7134_g_crop,
- .vidioc_s_crop = saa7134_s_crop,
- .vidioc_g_fbuf = saa7134_g_fbuf,
- .vidioc_s_fbuf = saa7134_s_fbuf,
- .vidioc_overlay = saa7134_overlay,
- .vidioc_g_priority = saa7134_g_priority,
- .vidioc_s_priority = saa7134_s_priority,
- .vidioc_g_parm = saa7134_g_parm,
- .vidioc_g_frequency = saa7134_g_frequency,
- .vidioc_s_frequency = saa7134_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static const struct v4l2_file_operations radio_fops = {
- .owner = THIS_MODULE,
- .open = video_open,
- .read = radio_read,
- .release = video_release,
- .ioctl = video_ioctl2,
- .poll = radio_poll,
-};
-
-static const struct v4l2_ioctl_ops radio_ioctl_ops = {
- .vidioc_querycap = radio_querycap,
- .vidioc_g_tuner = radio_g_tuner,
- .vidioc_enum_input = radio_enum_input,
- .vidioc_g_audio = radio_g_audio,
- .vidioc_s_tuner = radio_s_tuner,
- .vidioc_s_audio = radio_s_audio,
- .vidioc_s_input = radio_s_input,
- .vidioc_s_std = radio_s_std,
- .vidioc_queryctrl = radio_queryctrl,
- .vidioc_g_input = radio_g_input,
- .vidioc_g_ctrl = saa7134_g_ctrl,
- .vidioc_s_ctrl = saa7134_s_ctrl,
- .vidioc_g_frequency = saa7134_g_frequency,
- .vidioc_s_frequency = saa7134_s_frequency,
-};
-
-/* ----------------------------------------------------------- */
-/* exported stuff */
-
-struct video_device saa7134_video_template = {
- .name = "saa7134-video",
- .fops = &video_fops,
- .ioctl_ops = &video_ioctl_ops,
- .tvnorms = SAA7134_NORMS,
- .current_norm = V4L2_STD_PAL,
-};
-
-struct video_device saa7134_radio_template = {
- .name = "saa7134-radio",
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-int saa7134_video_init1(struct saa7134_dev *dev)
-{
- /* sanitycheck insmod options */
- if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
- gbuffers = 2;
- if (gbufsize < 0 || gbufsize > gbufsize_max)
- gbufsize = gbufsize_max;
- gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
-
- /* put some sensible defaults into the data structures ... */
- dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value;
- dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value;
- dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value;
- dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value;
- dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value;
- dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value;
- dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value;
- dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value;
-
- if (dev->tda9887_conf && dev->ctl_automute)
- dev->tda9887_conf |= TDA9887_AUTOMUTE;
- dev->automute = 0;
-
- INIT_LIST_HEAD(&dev->video_q.queue);
- init_timer(&dev->video_q.timeout);
- dev->video_q.timeout.function = saa7134_buffer_timeout;
- dev->video_q.timeout.data = (unsigned long)(&dev->video_q);
- dev->video_q.dev = dev;
-
- if (saa7134_boards[dev->board].video_out)
- saa7134_videoport_init(dev);
-
- return 0;
-}
-
-int saa7134_videoport_init(struct saa7134_dev *dev)
-{
- /* enable video output */
- int vo = saa7134_boards[dev->board].video_out;
- int video_reg;
- unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
-
- /* Configure videoport */
- saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
- video_reg = video_out[vo][1];
- if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
- video_reg &= ~VP_T_CODE_P_INVERTED;
- saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
- saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
- saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
- video_reg = video_out[vo][5];
- if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
- video_reg &= ~VP_CLK_CTRL2_DELAYED;
- if (vid_port_opts & SET_CLOCK_INVERTED)
- video_reg |= VP_CLK_CTRL1_INVERTED;
- saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
- video_reg = video_out[vo][6];
- if (vid_port_opts & SET_VSYNC_OFF) {
- video_reg &= ~VP_VS_TYPE_MASK;
- video_reg |= VP_VS_TYPE_OFF;
- }
- saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
- saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
- saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
-
- /* Start videoport */
- saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
-
- return 0;
-}
-
-int saa7134_video_init2(struct saa7134_dev *dev)
-{
- /* init video hw */
- set_tvnorm(dev,&tvnorms[0]);
- video_mux(dev,0);
- saa7134_tvaudio_setmute(dev);
- saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
- return 0;
-}
-
-void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
-{
- static const char *st[] = {
- "(no signal)", "NTSC", "PAL", "SECAM" };
- u32 st1,st2;
-
- st1 = saa_readb(SAA7134_STATUS_VIDEO1);
- st2 = saa_readb(SAA7134_STATUS_VIDEO2);
- dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n",
- (st1 & 0x40) ? "not locked" : "locked",
- (st2 & 0x40) ? "no" : "yes",
- st[st1 & 0x03]);
- dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1);
-
- if (dev->nosignal) {
- /* no video signal -> mute audio */
- if (dev->ctl_automute)
- dev->automute = 1;
- saa7134_tvaudio_setmute(dev);
- } else {
- /* wake up tvaudio audio carrier scan thread */
- saa7134_tvaudio_do_scan(dev);
- }
-
- if ((st2 & 0x80) && !noninterlaced && !dev->nosignal)
- saa_clearb(SAA7134_SYNC_CTRL, 0x20);
- else
- saa_setb(SAA7134_SYNC_CTRL, 0x20);
-
- if (dev->mops && dev->mops->signal_change)
- dev->mops->signal_change(dev);
-}
-
-
-void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status)
-{
- enum v4l2_field field;
-
- spin_lock(&dev->slock);
- if (dev->video_q.curr) {
- dev->video_fieldcount++;
- field = dev->video_q.curr->vb.field;
- if (V4L2_FIELD_HAS_BOTH(field)) {
- /* make sure we have seen both fields */
- if ((status & 0x10) == 0x00) {
- dev->video_q.curr->top_seen = 1;
- goto done;
- }
- if (!dev->video_q.curr->top_seen)
- goto done;
- } else if (field == V4L2_FIELD_TOP) {
- if ((status & 0x10) != 0x10)
- goto done;
- } else if (field == V4L2_FIELD_BOTTOM) {
- if ((status & 0x10) != 0x00)
- goto done;
- }
- dev->video_q.curr->vb.field_count = dev->video_fieldcount;
- saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE);
- }
- saa7134_buffer_next(dev,&dev->video_q);
-
- done:
- spin_unlock(&dev->slock);
-}
-
-/* ----------------------------------------------------------- */
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
deleted file mode 100644
index 89c8333736a2..000000000000
--- a/drivers/media/video/saa7134/saa7134.h
+++ /dev/null
@@ -1,856 +0,0 @@
-/*
- *
- * v4l2 device driver for philips saa7134 based TV cards
- *
- * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#define SAA7134_VERSION "0, 2, 17"
-
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <linux/kdev_t.h>
-#include <linux/input.h>
-#include <linux/notifier.h>
-#include <linux/delay.h>
-#include <linux/mutex.h>
-
-#include <asm/io.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/tuner.h>
-#include <media/rc-core.h>
-#include <media/ir-kbd-i2c.h>
-#include <media/videobuf-dma-sg.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
-#include <media/videobuf-dvb.h>
-#endif
-
-#define UNSET (-1U)
-
-/* ----------------------------------------------------------- */
-/* enums */
-
-enum saa7134_tvaudio_mode {
- TVAUDIO_FM_MONO = 1,
- TVAUDIO_FM_BG_STEREO = 2,
- TVAUDIO_FM_SAT_STEREO = 3,
- TVAUDIO_FM_K_STEREO = 4,
- TVAUDIO_NICAM_AM = 5,
- TVAUDIO_NICAM_FM = 6,
-};
-
-enum saa7134_audio_in {
- TV = 1,
- LINE1 = 2,
- LINE2 = 3,
- LINE2_LEFT,
-};
-
-enum saa7134_video_out {
- CCIR656 = 1,
-};
-
-/* ----------------------------------------------------------- */
-/* static data */
-
-struct saa7134_tvnorm {
- char *name;
- v4l2_std_id id;
-
- /* video decoder */
- unsigned int sync_control;
- unsigned int luma_control;
- unsigned int chroma_ctrl1;
- unsigned int chroma_gain;
- unsigned int chroma_ctrl2;
- unsigned int vgate_misc;
-
- /* video scaler */
- unsigned int h_start;
- unsigned int h_stop;
- unsigned int video_v_start;
- unsigned int video_v_stop;
- unsigned int vbi_v_start_0;
- unsigned int vbi_v_stop_0;
- unsigned int src_timing;
- unsigned int vbi_v_start_1;
-};
-
-struct saa7134_tvaudio {
- char *name;
- v4l2_std_id std;
- enum saa7134_tvaudio_mode mode;
- int carr1;
- int carr2;
-};
-
-struct saa7134_format {
- char *name;
- unsigned int fourcc;
- unsigned int depth;
- unsigned int pm;
- unsigned int vshift; /* vertical downsampling (for planar yuv) */
- unsigned int hshift; /* horizontal downsampling (for planar yuv) */
- unsigned int bswap:1;
- unsigned int wswap:1;
- unsigned int yuv:1;
- unsigned int planar:1;
- unsigned int uvswap:1;
-};
-
-struct saa7134_card_ir {
- struct rc_dev *dev;
-
- char name[32];
- char phys[32];
- unsigned users;
-
- u32 polling;
- u32 last_gpio;
- u32 mask_keycode, mask_keydown, mask_keyup;
-
- bool running;
- bool active;
-
- struct timer_list timer;
-
- /* IR core raw decoding */
- u32 raw_decode;
-};
-
-/* ----------------------------------------------------------- */
-/* card configuration */
-
-#define SAA7134_BOARD_NOAUTO UNSET
-#define SAA7134_BOARD_UNKNOWN 0
-#define SAA7134_BOARD_PROTEUS_PRO 1
-#define SAA7134_BOARD_FLYVIDEO3000 2
-#define SAA7134_BOARD_FLYVIDEO2000 3
-#define SAA7134_BOARD_EMPRESS 4
-#define SAA7134_BOARD_MONSTERTV 5
-#define SAA7134_BOARD_MD9717 6
-#define SAA7134_BOARD_TVSTATION_RDS 7
-#define SAA7134_BOARD_CINERGY400 8
-#define SAA7134_BOARD_MD5044 9
-#define SAA7134_BOARD_KWORLD 10
-#define SAA7134_BOARD_CINERGY600 11
-#define SAA7134_BOARD_MD7134 12
-#define SAA7134_BOARD_TYPHOON_90031 13
-#define SAA7134_BOARD_ELSA 14
-#define SAA7134_BOARD_ELSA_500TV 15
-#define SAA7134_BOARD_ASUSTeK_TVFM7134 16
-#define SAA7134_BOARD_VA1000POWER 17
-#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18
-#define SAA7134_BOARD_VIDEOMATE_TV 19
-#define SAA7134_BOARD_CRONOS_PLUS 20
-#define SAA7134_BOARD_10MOONSTVMASTER 21
-#define SAA7134_BOARD_MD2819 22
-#define SAA7134_BOARD_BMK_MPEX_TUNER 23
-#define SAA7134_BOARD_TVSTATION_DVR 24
-#define SAA7134_BOARD_ASUSTEK_TVFM7133 25
-#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26
-#define SAA7134_BOARD_MANLI_MTV002 27
-#define SAA7134_BOARD_MANLI_MTV001 28
-#define SAA7134_BOARD_TG3000TV 29
-#define SAA7134_BOARD_ECS_TVP3XP 30
-#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31
-#define SAA7134_BOARD_AVACSSMARTTV 32
-#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
-#define SAA7134_BOARD_NOVAC_PRIMETV7133 34
-#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35
-#define SAA7134_BOARD_UPMOST_PURPLE_TV 36
-#define SAA7134_BOARD_ITEMS_MTV005 37
-#define SAA7134_BOARD_CINERGY200 38
-#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39
-#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40
-#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41
-#define SAA7134_BOARD_SABRENT_SBTTVFM 42
-#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43
-#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44
-#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45
-#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46
-#define SAA7134_BOARD_CINERGY400_CARDBUS 47
-#define SAA7134_BOARD_CINERGY600_MK3 48
-#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49
-#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50
-#define SAA7134_BOARD_PROVIDEO_PV952 51
-#define SAA7134_BOARD_AVERMEDIA_305 52
-#define SAA7134_BOARD_ASUSTeK_TVFM7135 53
-#define SAA7134_BOARD_FLYTVPLATINUM_FM 54
-#define SAA7134_BOARD_FLYDVBTDUO 55
-#define SAA7134_BOARD_AVERMEDIA_307 56
-#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57
-#define SAA7134_BOARD_ADS_INSTANT_TV 58
-#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59
-#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60
-#define SAA7134_BOARD_PHILIPS_TOUGH 61
-#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62
-#define SAA7134_BOARD_KWORLD_XPERT 63
-#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64
-#define SAA7134_BOARD_KWORLD_TERMINATOR 65
-#define SAA7134_BOARD_YUAN_TUN900 66
-#define SAA7134_BOARD_BEHOLD_409FM 67
-#define SAA7134_BOARD_GOTVIEW_7135 68
-#define SAA7134_BOARD_PHILIPS_EUROPA 69
-#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70
-#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71
-#define SAA7134_BOARD_RTD_VFG7350 72
-#define SAA7134_BOARD_RTD_VFG7330 73
-#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74
-#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75
-#define SAA7134_BOARD_MONSTERTV_MOBILE 76
-#define SAA7134_BOARD_PINNACLE_PCTV_110i 77
-#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78
-#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79
-#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80
-#define SAA7134_BOARD_PHILIPS_TIGER 81
-#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82
-#define SAA7134_BOARD_CINERGY250PCI 83
-#define SAA7134_BOARD_FLYDVB_TRIO 84
-#define SAA7134_BOARD_AVERMEDIA_777 85
-#define SAA7134_BOARD_FLYDVBT_LR301 86
-#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87
-#define SAA7134_BOARD_TEVION_DVBT_220RF 88
-#define SAA7134_BOARD_ELSA_700TV 89
-#define SAA7134_BOARD_KWORLD_ATSC110 90
-#define SAA7134_BOARD_AVERMEDIA_A169_B 91
-#define SAA7134_BOARD_AVERMEDIA_A169_B1 92
-#define SAA7134_BOARD_MD7134_BRIDGE_2 93
-#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
-#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95
-#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96
-#define SAA7134_BOARD_FLYDVBS_LR300 97
-#define SAA7134_BOARD_PROTEUS_2309 98
-#define SAA7134_BOARD_AVERMEDIA_A16AR 99
-#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100
-#define SAA7134_BOARD_PINNACLE_PCTV_310i 101
-#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102
-#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103
-#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104
-#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105
-#define SAA7134_BOARD_ENCORE_ENLTV 106
-#define SAA7134_BOARD_ENCORE_ENLTV_FM 107
-#define SAA7134_BOARD_CINERGY_HT_PCI 108
-#define SAA7134_BOARD_PHILIPS_TIGER_S 109
-#define SAA7134_BOARD_AVERMEDIA_M102 110
-#define SAA7134_BOARD_ASUS_P7131_4871 111
-#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112
-#define SAA7134_BOARD_ECS_TVP3XP_4CB6 113
-#define SAA7134_BOARD_KWORLD_DVBT_210 114
-#define SAA7134_BOARD_SABRENT_TV_PCB05 115
-#define SAA7134_BOARD_10MOONSTVMASTER3 116
-#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117
-#define SAA7134_BOARD_BEHOLD_401 118
-#define SAA7134_BOARD_BEHOLD_403 119
-#define SAA7134_BOARD_BEHOLD_403FM 120
-#define SAA7134_BOARD_BEHOLD_405 121
-#define SAA7134_BOARD_BEHOLD_405FM 122
-#define SAA7134_BOARD_BEHOLD_407 123
-#define SAA7134_BOARD_BEHOLD_407FM 124
-#define SAA7134_BOARD_BEHOLD_409 125
-#define SAA7134_BOARD_BEHOLD_505FM 126
-#define SAA7134_BOARD_BEHOLD_507_9FM 127
-#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128
-#define SAA7134_BOARD_BEHOLD_607FM_MK3 129
-#define SAA7134_BOARD_BEHOLD_M6 130
-#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131
-#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132
-#define SAA7134_BOARD_PHILIPS_SNAKE 133
-#define SAA7134_BOARD_CREATIX_CTX953 134
-#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135
-#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136
-#define SAA7134_BOARD_AVERMEDIA_A16D 137
-#define SAA7134_BOARD_AVERMEDIA_M115 138
-#define SAA7134_BOARD_VIDEOMATE_T750 139
-#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140
-#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
-#define SAA7134_BOARD_BEHOLD_H6 142
-#define SAA7134_BOARD_BEHOLD_M63 143
-#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
-#define SAA7134_BOARD_AVERMEDIA_M103 145
-#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146
-#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147
-#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148
-#define SAA7134_BOARD_AVERMEDIA_M135A 149
-#define SAA7134_BOARD_REAL_ANGEL_220 150
-#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151
-#define SAA7134_BOARD_ASUSTeK_TIGER 152
-#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
-#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
-#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155
-#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156
-#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157
-#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158
-#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159
-#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160
-#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161
-#define SAA7134_BOARD_BEHOLD_607FM_MK5 162
-#define SAA7134_BOARD_BEHOLD_609FM_MK3 163
-#define SAA7134_BOARD_BEHOLD_609FM_MK5 164
-#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165
-#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166
-#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167
-#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168
-#define SAA7134_BOARD_VIDEOMATE_S350 169
-#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170
-#define SAA7134_BOARD_BEHOLD_X7 171
-#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172
-#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173
-#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174
-#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175
-#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176
-#define SAA7134_BOARD_HAWELL_HW_404M7 177
-#define SAA7134_BOARD_BEHOLD_H7 178
-#define SAA7134_BOARD_BEHOLD_A7 179
-#define SAA7134_BOARD_AVERMEDIA_M733A 180
-#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181
-#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182
-#define SAA7134_BOARD_VIDEOMATE_M1F 183
-#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184
-#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185
-#define SAA7134_BOARD_BEHOLD_501 186
-#define SAA7134_BOARD_BEHOLD_503FM 187
-#define SAA7134_BOARD_SENSORAY811_911 188
-#define SAA7134_BOARD_KWORLD_PC150U 189
-#define SAA7134_BOARD_ASUSTeK_PS3_100 190
-
-#define SAA7134_MAXBOARDS 32
-#define SAA7134_INPUT_MAX 8
-
-/* ----------------------------------------------------------- */
-/* Since we support 2 remote types, lets tell them apart */
-
-#define SAA7134_REMOTE_GPIO 1
-#define SAA7134_REMOTE_I2C 2
-
-/* ----------------------------------------------------------- */
-/* Video Output Port Register Initialization Options */
-
-#define SET_T_CODE_POLARITY_NON_INVERTED (1 << 0)
-#define SET_CLOCK_NOT_DELAYED (1 << 1)
-#define SET_CLOCK_INVERTED (1 << 2)
-#define SET_VSYNC_OFF (1 << 3)
-
-struct saa7134_input {
- char *name;
- unsigned int vmux;
- enum saa7134_audio_in amux;
- unsigned int gpio;
- unsigned int tv:1;
-};
-
-enum saa7134_mpeg_type {
- SAA7134_MPEG_UNUSED,
- SAA7134_MPEG_EMPRESS,
- SAA7134_MPEG_DVB,
-};
-
-enum saa7134_mpeg_ts_type {
- SAA7134_MPEG_TS_PARALLEL = 0,
- SAA7134_MPEG_TS_SERIAL,
-};
-
-struct saa7134_board {
- char *name;
- unsigned int audio_clock;
-
- /* input switching */
- unsigned int gpiomask;
- struct saa7134_input inputs[SAA7134_INPUT_MAX];
- struct saa7134_input radio;
- struct saa7134_input mute;
-
- /* i2c chip info */
- unsigned int tuner_type;
- unsigned int radio_type;
- unsigned char tuner_addr;
- unsigned char radio_addr;
- unsigned char empress_addr;
- unsigned char rds_addr;
-
- unsigned int tda9887_conf;
- unsigned int tuner_config;
-
- /* peripheral I/O */
- enum saa7134_video_out video_out;
- enum saa7134_mpeg_type mpeg;
- enum saa7134_mpeg_ts_type ts_type;
- unsigned int vid_port_opts;
- unsigned int ts_force_val:1;
-};
-
-#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name)
-#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg)
-#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg)
-#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg)
-#define card(dev) (saa7134_boards[dev->board])
-#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n])
-
-/* ----------------------------------------------------------- */
-/* device / file handle status */
-
-#define RESOURCE_OVERLAY 1
-#define RESOURCE_VIDEO 2
-#define RESOURCE_VBI 4
-
-#define INTERLACE_AUTO 0
-#define INTERLACE_ON 1
-#define INTERLACE_OFF 2
-
-#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
-#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */
-
-struct saa7134_dev;
-struct saa7134_dma;
-
-/* saa7134 page table */
-struct saa7134_pgtable {
- unsigned int size;
- __le32 *cpu;
- dma_addr_t dma;
-};
-
-/* tvaudio thread status */
-struct saa7134_thread {
- struct task_struct *thread;
- unsigned int scan1;
- unsigned int scan2;
- unsigned int mode;
- unsigned int stopped;
-};
-
-/* buffer for one video/vbi/ts frame */
-struct saa7134_buf {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- /* saa7134 specific */
- struct saa7134_format *fmt;
- unsigned int top_seen;
- int (*activate)(struct saa7134_dev *dev,
- struct saa7134_buf *buf,
- struct saa7134_buf *next);
-
- /* page tables */
- struct saa7134_pgtable *pt;
-};
-
-struct saa7134_dmaqueue {
- struct saa7134_dev *dev;
- struct saa7134_buf *curr;
- struct list_head queue;
- struct timer_list timeout;
- unsigned int need_two;
-};
-
-/* video filehandle status */
-struct saa7134_fh {
- struct saa7134_dev *dev;
- unsigned int radio;
- enum v4l2_buf_type type;
- unsigned int resources;
- enum v4l2_priority prio;
-
- /* video overlay */
- struct v4l2_window win;
- struct v4l2_clip clips[8];
- unsigned int nclips;
-
- /* video capture */
- struct saa7134_format *fmt;
- unsigned int width,height;
- struct videobuf_queue cap;
- struct saa7134_pgtable pt_cap;
-
- /* vbi capture */
- struct videobuf_queue vbi;
- struct saa7134_pgtable pt_vbi;
-};
-
-/* dmasound dsp status */
-struct saa7134_dmasound {
- struct mutex lock;
- int minor_mixer;
- int minor_dsp;
- unsigned int users_dsp;
-
- /* mixer */
- enum saa7134_audio_in input;
- unsigned int count;
- unsigned int line1;
- unsigned int line2;
-
- /* dsp */
- unsigned int afmt;
- unsigned int rate;
- unsigned int channels;
- unsigned int recording_on;
- unsigned int dma_running;
- unsigned int blocks;
- unsigned int blksize;
- unsigned int bufsize;
- struct saa7134_pgtable pt;
- struct videobuf_dmabuf dma;
- unsigned int dma_blk;
- unsigned int read_offset;
- unsigned int read_count;
- void * priv_data;
- struct snd_pcm_substream *substream;
-};
-
-/* ts/mpeg status */
-struct saa7134_ts {
- /* TS capture */
- struct saa7134_pgtable pt_ts;
- int nr_packets;
- int nr_bufs;
-};
-
-/* ts/mpeg ops */
-struct saa7134_mpeg_ops {
- enum saa7134_mpeg_type type;
- struct list_head next;
- int (*init)(struct saa7134_dev *dev);
- int (*fini)(struct saa7134_dev *dev);
- void (*signal_change)(struct saa7134_dev *dev);
-};
-
-/* global device status */
-struct saa7134_dev {
- struct list_head devlist;
- struct mutex lock;
- spinlock_t slock;
- struct v4l2_prio_state prio;
- struct v4l2_device v4l2_dev;
- /* workstruct for loading modules */
- struct work_struct request_module_wk;
-
- /* insmod option/autodetected */
- int autodetected;
-
- /* various device info */
- unsigned int resources;
- struct video_device *video_dev;
- struct video_device *radio_dev;
- struct video_device *vbi_dev;
- struct saa7134_dmasound dmasound;
-
- /* infrared remote */
- int has_remote;
- struct saa7134_card_ir *remote;
-
- /* pci i/o */
- char name[32];
- int nr;
- struct pci_dev *pci;
- unsigned char pci_rev,pci_lat;
- __u32 __iomem *lmmio;
- __u8 __iomem *bmmio;
-
- /* config info */
- unsigned int board;
- unsigned int tuner_type;
- unsigned int radio_type;
- unsigned char tuner_addr;
- unsigned char radio_addr;
-
- unsigned int tda9887_conf;
- unsigned int gpio_value;
-
- /* i2c i/o */
- struct i2c_adapter i2c_adap;
- struct i2c_client i2c_client;
- unsigned char eedata[256];
- int has_rds;
-
- /* video overlay */
- struct v4l2_framebuffer ovbuf;
- struct saa7134_format *ovfmt;
- unsigned int ovenable;
- enum v4l2_field ovfield;
-
- /* video+ts+vbi capture */
- struct saa7134_dmaqueue video_q;
- struct saa7134_dmaqueue vbi_q;
- unsigned int video_fieldcount;
- unsigned int vbi_fieldcount;
-
- /* various v4l controls */
- struct saa7134_tvnorm *tvnorm; /* video */
- struct saa7134_tvaudio *tvaudio;
- unsigned int ctl_input;
- int ctl_bright;
- int ctl_contrast;
- int ctl_hue;
- int ctl_saturation;
- int ctl_freq;
- int ctl_mute; /* audio */
- int ctl_volume;
- int ctl_invert; /* private */
- int ctl_mirror;
- int ctl_y_odd;
- int ctl_y_even;
- int ctl_automute;
-
- /* crop */
- struct v4l2_rect crop_bounds;
- struct v4l2_rect crop_defrect;
- struct v4l2_rect crop_current;
-
- /* other global state info */
- unsigned int automute;
- struct saa7134_thread thread;
- struct saa7134_input *input;
- struct saa7134_input *hw_input;
- unsigned int hw_mute;
- int last_carrier;
- int nosignal;
- unsigned int insuspend;
-
- /* I2C keyboard data */
- struct IR_i2c_init_data init_data;
-
- /* SAA7134_MPEG_* */
- struct saa7134_ts ts;
- struct saa7134_dmaqueue ts_q;
- int ts_started;
- struct saa7134_mpeg_ops *mops;
-
- /* SAA7134_MPEG_EMPRESS only */
- struct video_device *empress_dev;
- struct videobuf_queue empress_tsq;
- atomic_t empress_users;
- struct work_struct empress_workqueue;
- int empress_started;
-
-#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE)
- /* SAA7134_MPEG_DVB only */
- struct videobuf_dvb_frontends frontends;
- int (*original_demod_sleep)(struct dvb_frontend *fe);
- int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
- int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
-#endif
- void (*gate_ctrl)(struct saa7134_dev *dev, int open);
-};
-
-/* ----------------------------------------------------------- */
-
-#define saa_readl(reg) readl(dev->lmmio + (reg))
-#define saa_writel(reg,value) writel((value), dev->lmmio + (reg));
-#define saa_andorl(reg,mask,value) \
- writel((readl(dev->lmmio+(reg)) & ~(mask)) |\
- ((value) & (mask)), dev->lmmio+(reg))
-#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit))
-#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0)
-
-#define saa_readb(reg) readb(dev->bmmio + (reg))
-#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg));
-#define saa_andorb(reg,mask,value) \
- writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\
- ((value) & (mask)), dev->bmmio+(reg))
-#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit))
-#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0)
-
-#define saa_wait(us) { udelay(us); }
-
-#define SAA7134_NORMS (\
- V4L2_STD_PAL | V4L2_STD_PAL_N | \
- V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \
- V4L2_STD_NTSC | V4L2_STD_PAL_M | \
- V4L2_STD_PAL_60)
-
-#define GRP_EMPRESS (1)
-#define saa_call_all(dev, o, f, args...) do { \
- if (dev->gate_ctrl) \
- dev->gate_ctrl(dev, 1); \
- v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \
- if (dev->gate_ctrl) \
- dev->gate_ctrl(dev, 0); \
-} while (0)
-
-#define saa_call_empress(dev, o, f, args...) ({ \
- long _rc; \
- if (dev->gate_ctrl) \
- dev->gate_ctrl(dev, 1); \
- _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \
- GRP_EMPRESS, o, f , ##args); \
- if (dev->gate_ctrl) \
- dev->gate_ctrl(dev, 0); \
- _rc; \
-})
-
-/* ----------------------------------------------------------- */
-/* saa7134-core.c */
-
-extern struct list_head saa7134_devlist;
-extern struct mutex saa7134_devlist_lock;
-extern int saa7134_no_overlay;
-
-void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
-void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value);
-
-#define SAA7134_PGTABLE_SIZE 4096
-
-int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt);
-int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt,
- struct scatterlist *list, unsigned int length,
- unsigned int startpage);
-void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt);
-
-int saa7134_buffer_count(unsigned int size, unsigned int count);
-int saa7134_buffer_startpage(struct saa7134_buf *buf);
-unsigned long saa7134_buffer_base(struct saa7134_buf *buf);
-
-int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
- struct saa7134_buf *buf);
-void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q,
- unsigned int state);
-void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
-void saa7134_buffer_timeout(unsigned long data);
-void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
-
-int saa7134_set_dmabits(struct saa7134_dev *dev);
-
-extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
-extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev);
-
-
-/* ----------------------------------------------------------- */
-/* saa7134-cards.c */
-
-extern struct saa7134_board saa7134_boards[];
-extern const unsigned int saa7134_bcount;
-extern struct pci_device_id __devinitdata saa7134_pci_tbl[];
-
-extern int saa7134_board_init1(struct saa7134_dev *dev);
-extern int saa7134_board_init2(struct saa7134_dev *dev);
-int saa7134_tuner_callback(void *priv, int component, int command, int arg);
-
-
-/* ----------------------------------------------------------- */
-/* saa7134-i2c.c */
-
-int saa7134_i2c_register(struct saa7134_dev *dev);
-int saa7134_i2c_unregister(struct saa7134_dev *dev);
-
-
-/* ----------------------------------------------------------- */
-/* saa7134-video.c */
-
-extern unsigned int video_debug;
-extern struct video_device saa7134_video_template;
-extern struct video_device saa7134_radio_template;
-
-int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
-int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c);
-int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c);
-int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id);
-
-int saa7134_videoport_init(struct saa7134_dev *dev);
-void saa7134_set_tvnorm_hw(struct saa7134_dev *dev);
-
-int saa7134_video_init1(struct saa7134_dev *dev);
-int saa7134_video_init2(struct saa7134_dev *dev);
-void saa7134_irq_video_signalchange(struct saa7134_dev *dev);
-void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status);
-
-
-/* ----------------------------------------------------------- */
-/* saa7134-ts.c */
-
-#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */
-
-extern struct videobuf_queue_ops saa7134_ts_qops;
-
-int saa7134_ts_init1(struct saa7134_dev *dev);
-int saa7134_ts_fini(struct saa7134_dev *dev);
-void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
-
-int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
-void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
-
-int saa7134_ts_init_hw(struct saa7134_dev *dev);
-
-int saa7134_ts_start(struct saa7134_dev *dev);
-int saa7134_ts_stop(struct saa7134_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* saa7134-vbi.c */
-
-extern struct videobuf_queue_ops saa7134_vbi_qops;
-extern struct video_device saa7134_vbi_template;
-
-int saa7134_vbi_init1(struct saa7134_dev *dev);
-int saa7134_vbi_fini(struct saa7134_dev *dev);
-void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status);
-
-
-/* ----------------------------------------------------------- */
-/* saa7134-tvaudio.c */
-
-int saa7134_tvaudio_rx2mode(u32 rx);
-
-void saa7134_tvaudio_setmute(struct saa7134_dev *dev);
-void saa7134_tvaudio_setinput(struct saa7134_dev *dev,
- struct saa7134_input *in);
-void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level);
-int saa7134_tvaudio_getstereo(struct saa7134_dev *dev);
-
-void saa7134_tvaudio_init(struct saa7134_dev *dev);
-int saa7134_tvaudio_init2(struct saa7134_dev *dev);
-int saa7134_tvaudio_fini(struct saa7134_dev *dev);
-int saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
-int saa7134_tvaudio_close(struct saa7134_dev *dev);
-
-int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
-
-void saa7134_enable_i2s(struct saa7134_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* saa7134-oss.c */
-
-extern const struct file_operations saa7134_dsp_fops;
-extern const struct file_operations saa7134_mixer_fops;
-
-int saa7134_oss_init1(struct saa7134_dev *dev);
-int saa7134_oss_fini(struct saa7134_dev *dev);
-void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status);
-
-/* ----------------------------------------------------------- */
-/* saa7134-input.c */
-
-#if defined(CONFIG_VIDEO_SAA7134_RC)
-int saa7134_input_init1(struct saa7134_dev *dev);
-void saa7134_input_fini(struct saa7134_dev *dev);
-void saa7134_input_irq(struct saa7134_dev *dev);
-void saa7134_probe_i2c_ir(struct saa7134_dev *dev);
-int saa7134_ir_start(struct saa7134_dev *dev);
-void saa7134_ir_stop(struct saa7134_dev *dev);
-#else
-#define saa7134_input_init1(dev) ((void)0)
-#define saa7134_input_fini(dev) ((void)0)
-#define saa7134_input_irq(dev) ((void)0)
-#define saa7134_probe_i2c_ir(dev) ((void)0)
-#define saa7134_ir_start(dev) ((void)0)
-#define saa7134_ir_stop(dev) ((void)0)
-#endif
diff --git a/drivers/media/video/saa7164/Kconfig b/drivers/media/video/saa7164/Kconfig
deleted file mode 100644
index 353263725172..000000000000
--- a/drivers/media/video/saa7164/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-config VIDEO_SAA7164
- tristate "NXP SAA7164 support"
- depends on DVB_CORE && PCI && I2C
- select I2C_ALGOBIT
- select FW_LOADER
- select VIDEO_TUNER
- select VIDEO_TVEEPROM
- select VIDEOBUF_DVB
- select DVB_TDA10048 if !DVB_FE_CUSTOMISE
- select DVB_S5H1411 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
- ---help---
- This is a video4linux driver for NXP SAA7164 based
- TV cards.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7164
-
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
deleted file mode 100644
index 068443af30c8..000000000000
--- a/drivers/media/video/saa7164/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
- saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \
- saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o
-
-obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
-
-ccflags-y += -I$(srctree)/drivers/media/video
-ccflags-y += -I$(srctree)/drivers/media/common/tuners
-ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
-ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
-
-ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c
deleted file mode 100644
index c8799fdaae67..000000000000
--- a/drivers/media/video/saa7164/saa7164-api.c
+++ /dev/null
@@ -1,1519 +0,0 @@
-/*
- * Driver for the NXP SAA7164 PCIe bridge
- *
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/wait.h>
-#include <linux/slab.h>
-
-#include "saa7164.h"
-
-int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i)
-{
- int ret;
-
- if (!(saa_debug & DBGLVL_CPU))
- return 0;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- i->deviceinst = 0;
- i->devicespec = 0;
- i->mode = 0;
- i->status = 0;
-
- ret = saa7164_cmd_send(dev, 0, GET_CUR,
- GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad);
-
- return ret;
-}
-
-int saa7164_api_collect_debug(struct saa7164_dev *dev)
-{
- struct tmComResDebugGetData d;
- u8 more = 255;
- int ret;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- while (more--) {
-
- memset(&d, 0, sizeof(d));
-
- ret = saa7164_cmd_send(dev, 0, GET_CUR,
- GET_DEBUG_DATA_CONTROL, sizeof(d), &d);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n",
- __func__, ret);
-
- if (d.dwResult != SAA_OK)
- break;
-
- printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr,
- d.ucDebugData);
- }
-
- return 0;
-}
-
-int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level)
-{
- struct tmComResDebugSetLevel lvl;
- int ret;
-
- dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level);
-
- /* Retrieve current state */
- ret = saa7164_cmd_send(dev, 0, GET_CUR,
- SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel);
-
- lvl.dwDebugLevel = level;
-
- /* set new state */
- ret = saa7164_cmd_send(dev, 0, SET_CUR,
- SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_set_vbi_format(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResProbeCommit fmt, rsp;
- int ret;
-
- dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__,
- port->nr, port->hwcfg.unitid);
-
- fmt.bmHint = 0;
- fmt.bFormatIndex = 1;
- fmt.bFrameIndex = 1;
-
- /* Probe, see if it can support this format */
- ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
- SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret);
-
- /* See of the format change was successful */
- ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
- GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret);
- } else {
- /* Compare requested vs received, should be same */
- if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) {
- dprintk(DBGLVL_API, "SET/PROBE Verified\n");
-
- /* Ask the device to select the negotiated format */
- ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
- SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() commit error, ret = 0x%x\n",
- __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid,
- GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n",
- __func__, ret);
-
- if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) {
- printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n",
- __func__, ret);
- } else
- dprintk(DBGLVL_API, "SET/COMMIT Verified\n");
-
- dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint);
- dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n",
- rsp.bFormatIndex);
- dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n",
- rsp.bFrameIndex);
- } else
- printk(KERN_ERR "%s() compare failed\n", __func__);
- }
-
- if (ret == SAA_OK)
- dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr);
-
- return ret;
-}
-
-int saa7164_api_set_gop_size(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResEncVideoGopStructure gs;
- int ret;
-
- dprintk(DBGLVL_ENC, "%s()\n", __func__);
-
- gs.ucRefFrameDist = port->encoder_params.refdist;
- gs.ucGOPSize = port->encoder_params.gop_size;
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_VIDEO_GOP_STRUCTURE_CONTROL,
- sizeof(gs), &gs);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_set_encoder(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResEncVideoBitRate vb;
- struct tmComResEncAudioBitRate ab;
- int ret;
-
- dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
- port->hwcfg.sourceid);
-
- if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
- port->encoder_profile = EU_PROFILE_PS_DVD;
- else
- port->encoder_profile = EU_PROFILE_TS_HQ;
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Resolution */
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Establish video bitrates */
- if (port->encoder_params.bitrate_mode ==
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
- vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT;
- else
- vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK;
- vb.dwVideoBitRate = port->encoder_params.bitrate;
- vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak;
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_VIDEO_BIT_RATE_CONTROL,
- sizeof(struct tmComResEncVideoBitRate),
- &vb);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Establish audio bitrates */
- ab.ucAudioBitRateMode = 0;
- ab.dwAudioBitRate = 384000;
- ab.dwAudioBitRatePeak = ab.dwAudioBitRate;
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_AUDIO_BIT_RATE_CONTROL,
- sizeof(struct tmComResEncAudioBitRate),
- &ab);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
- ret);
-
- saa7164_api_set_aspect_ratio(port);
- saa7164_api_set_gop_size(port);
-
- return ret;
-}
-
-int saa7164_api_get_encoder(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResEncVideoBitRate v;
- struct tmComResEncAudioBitRate a;
- struct tmComResEncVideoInputAspectRatio ar;
- int ret;
-
- dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__,
- port->hwcfg.sourceid);
-
- port->encoder_profile = 0;
- port->video_format = 0;
- port->video_resolution = 0;
- port->audio_format = 0;
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8),
- &port->video_resolution);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Aspect Ratio */
- ar.width = 0;
- ar.height = 0;
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR,
- EU_VIDEO_INPUT_ASPECT_CONTROL,
- sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile);
- dprintk(DBGLVL_ENC, "video_format = %d\n", port->video_format);
- dprintk(DBGLVL_ENC, "audio_format = %d\n", port->audio_format);
- dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution);
- dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n",
- v.ucVideoBitRateMode);
- dprintk(DBGLVL_ENC, "v.dwVideoBitRate = %d\n",
- v.dwVideoBitRate);
- dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n",
- v.dwVideoBitRatePeak);
- dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n",
- a.ucAudioBitRateMode);
- dprintk(DBGLVL_ENC, "a.dwVideoBitRate = %d\n",
- a.dwAudioBitRate);
- dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n",
- a.dwAudioBitRatePeak);
- dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n",
- ar.width, ar.height);
-
- return ret;
-}
-
-int saa7164_api_set_aspect_ratio(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResEncVideoInputAspectRatio ar;
- int ret;
-
- dprintk(DBGLVL_ENC, "%s(%d)\n", __func__,
- port->encoder_params.ctl_aspect);
-
- switch (port->encoder_params.ctl_aspect) {
- case V4L2_MPEG_VIDEO_ASPECT_1x1:
- ar.width = 1;
- ar.height = 1;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_4x3:
- ar.width = 4;
- ar.height = 3;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_16x9:
- ar.width = 16;
- ar.height = 9;
- break;
- case V4L2_MPEG_VIDEO_ASPECT_221x100:
- ar.width = 221;
- ar.height = 100;
- break;
- default:
- BUG();
- }
-
- dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__,
- port->encoder_params.ctl_aspect,
- ar.width, ar.height);
-
- /* Aspect Ratio */
- ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR,
- EU_VIDEO_INPUT_ASPECT_CONTROL,
- sizeof(struct tmComResEncVideoInputAspectRatio), &ar);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl)
-{
- struct saa7164_dev *dev = port->dev;
- int ret;
- u16 val;
-
- if (ctl == PU_BRIGHTNESS_CONTROL)
- val = port->ctl_brightness;
- else
- if (ctl == PU_CONTRAST_CONTROL)
- val = port->ctl_contrast;
- else
- if (ctl == PU_HUE_CONTROL)
- val = port->ctl_hue;
- else
- if (ctl == PU_SATURATION_CONTROL)
- val = port->ctl_saturation;
- else
- if (ctl == PU_SHARPNESS_CONTROL)
- val = port->ctl_sharpness;
- else
- return -EINVAL;
-
- dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n",
- __func__, port->encunit.vsourceid, ctl, val);
-
- ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR,
- ctl, sizeof(u16), &val);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl)
-{
- struct saa7164_dev *dev = port->dev;
- int ret;
- u16 val;
-
- ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR,
- ctl, sizeof(u16), &val);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
- return ret;
- }
-
- dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n",
- __func__, ctl, val);
-
- if (ctl == PU_BRIGHTNESS_CONTROL)
- port->ctl_brightness = val;
- else
- if (ctl == PU_CONTRAST_CONTROL)
- port->ctl_contrast = val;
- else
- if (ctl == PU_HUE_CONTROL)
- port->ctl_hue = val;
- else
- if (ctl == PU_SATURATION_CONTROL)
- port->ctl_saturation = val;
- else
- if (ctl == PU_SHARPNESS_CONTROL)
- port->ctl_sharpness = val;
-
- return ret;
-}
-
-int saa7164_api_set_videomux(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 };
- int ret;
-
- dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n",
- __func__, port->mux_input, inputs[port->mux_input - 1]);
-
- /* Audio Mute */
- ret = saa7164_api_audio_mute(port, 1);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Video Mux */
- ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR,
- SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Audio Mux */
- ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR,
- SU_INPUT_SELECT_CONTROL, sizeof(u8),
- &inputs[port->mux_input - 1]);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Audio UnMute */
- ret = saa7164_api_audio_mute(port, 0);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_audio_mute(struct saa7164_port *port, int mute)
-{
- struct saa7164_dev *dev = port->dev;
- u8 v = mute;
- int ret;
-
- dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute);
-
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
- MUTE_CONTROL, sizeof(u8), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-/* 0 = silence, 0xff = full */
-int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level)
-{
- struct saa7164_dev *dev = port->dev;
- s16 v, min, max;
- int ret;
-
- dprintk(DBGLVL_API, "%s(%d)\n", __func__, level);
-
- /* Obtain the min/max ranges */
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN,
- VOLUME_CONTROL, sizeof(u16), &min);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX,
- VOLUME_CONTROL, sizeof(u16), &max);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
- (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
- level, min, max, v);
-
- v = level;
- if (v < min)
- v = min;
- if (v > max)
- v = max;
-
- /* Left */
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
- (0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Right */
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
- (0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR,
- (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__,
- level, min, max, v);
-
- return ret;
-}
-
-int saa7164_api_set_audio_std(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResAudioDefaults lvl;
- struct tmComResTunerStandard tvaudio;
- int ret;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- /* Establish default levels */
- lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT;
- lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT;
- lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT;
- lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT;
- lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT;
- lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT;
- ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR,
- AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults),
- &lvl);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- /* Manually select the appropriate TV audio standard */
- if (port->encodernorm.id & V4L2_STD_NTSC) {
- tvaudio.std = TU_STANDARD_NTSC_M;
- tvaudio.country = 1;
- } else {
- tvaudio.std = TU_STANDARD_PAL_I;
- tvaudio.country = 44;
- }
-
- ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
- TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n",
- __func__, ret);
- return ret;
-}
-
-int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect)
-{
- struct saa7164_dev *dev = port->dev;
- struct tmComResTunerStandardAuto p;
- int ret;
-
- dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect);
-
- /* Disable TV Audio autodetect if not already set (buggy) */
- if (autodetect)
- p.mode = TU_STANDARD_AUTO;
- else
- p.mode = TU_STANDARD_MANUAL;
- ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR,
- TU_STANDARD_AUTO_CONTROL, sizeof(p), &p);
- if (ret != SAA_OK)
- printk(KERN_ERR
- "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n",
- __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_get_videomux(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- int ret;
-
- ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR,
- SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_ENC, "%s() v_mux=%d\n",
- __func__, port->mux_input);
-
- return ret;
-}
-
-int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val)
-{
- struct saa7164_dev *dev = port->dev;
-
- u16 len = 0;
- u8 buf[256];
- int ret;
- u8 mas;
-
- dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__,
- port->nr, port->type, val);
-
- if (port->nr == 0)
- mas = 0xd0;
- else
- mas = 0xe0;
-
- memset(buf, 0, sizeof(buf));
-
- buf[0x00] = 0x04;
- buf[0x01] = 0x00;
- buf[0x02] = 0x00;
- buf[0x03] = 0x00;
-
- buf[0x04] = 0x04;
- buf[0x05] = 0x00;
- buf[0x06] = 0x00;
- buf[0x07] = 0x00;
-
- buf[0x08] = reg;
- buf[0x09] = 0x26;
- buf[0x0a] = mas;
- buf[0x0b] = 0xb0;
-
- buf[0x0c] = val;
- buf[0x0d] = 0x00;
- buf[0x0e] = 0x00;
- buf[0x0f] = 0x00;
-
- ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN,
- EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
- return -EIO;
- }
-
- ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR,
- EXU_REGISTER_ACCESS_CONTROL, len, &buf);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
-#if 0
- saa7164_dumphex16(dev, buf, 16);
-#endif
- return ret == SAA_OK ? 0 : -EIO;
-}
-
-/* Disable the IF block AGC controls */
-int saa7164_api_configure_dif(struct saa7164_port *port, u32 std)
-{
- struct saa7164_dev *dev = port->dev;
- int ret = 0;
- u8 agc_disable;
-
- dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std);
-
- if (std & V4L2_STD_NTSC) {
- dprintk(DBGLVL_API, " NTSC\n");
- saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_I) {
- dprintk(DBGLVL_API, " PAL-I\n");
- saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_M) {
- dprintk(DBGLVL_API, " PAL-M\n");
- saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_N) {
- dprintk(DBGLVL_API, " PAL-N\n");
- saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_Nc) {
- dprintk(DBGLVL_API, " PAL-Nc\n");
- saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_B) {
- dprintk(DBGLVL_API, " PAL-B\n");
- saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_PAL_DK) {
- dprintk(DBGLVL_API, " PAL-DK\n");
- saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */
- agc_disable = 0;
- } else if (std & V4L2_STD_SECAM_L) {
- dprintk(DBGLVL_API, " SECAM-L\n");
- saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */
- agc_disable = 0;
- } else {
- /* Unknown standard, assume DTV */
- dprintk(DBGLVL_API, " Unknown (assuming DTV)\n");
- /* Undefinded Video Standard */
- saa7164_api_set_dif(port, 0x00, 0x80);
- agc_disable = 1;
- }
-
- saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */
- saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */
- saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */
- saa7164_api_set_dif(port, 0x04, 0x01); /* Active */
- msleep(100);
- saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */
- msleep(100);
-
- return ret;
-}
-
-/* Ensure the dif is in the correct state for the operating mode
- * (analog / dtv). We only configure the diff through the analog encoder
- * so when we're in digital mode we need to find the appropriate encoder
- * and use it to configure the DIF.
- */
-int saa7164_api_initialize_dif(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct saa7164_port *p = NULL;
- int ret = -EINVAL;
- u32 std = 0;
-
- dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__,
- port->nr, port->type);
-
- if (port->type == SAA7164_MPEG_ENCODER) {
- /* Pick any analog standard to init the diff.
- * we'll come back during encoder_init'
- * and set the correct standard if requried.
- */
- std = V4L2_STD_NTSC;
- } else
- if (port->type == SAA7164_MPEG_DVB) {
- if (port->nr == SAA7164_PORT_TS1)
- p = &dev->ports[SAA7164_PORT_ENC1];
- else
- p = &dev->ports[SAA7164_PORT_ENC2];
- } else
- if (port->type == SAA7164_MPEG_VBI) {
- std = V4L2_STD_NTSC;
- if (port->nr == SAA7164_PORT_VBI1)
- p = &dev->ports[SAA7164_PORT_ENC1];
- else
- p = &dev->ports[SAA7164_PORT_ENC2];
- } else
- BUG();
-
- if (p)
- ret = saa7164_api_configure_dif(p, std);
-
- return ret;
-}
-
-int saa7164_api_transition_port(struct saa7164_port *port, u8 mode)
-{
- struct saa7164_dev *dev = port->dev;
-
- int ret;
-
- dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n",
- __func__, port->nr, port->hwcfg.unitid, mode);
-
- ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR,
- SAA_STATE_CONTROL, sizeof(mode), &mode);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n",
- __func__, port->nr, port->hwcfg.unitid, ret);
-
- return ret;
-}
-
-int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version)
-{
- int ret;
-
- ret = saa7164_cmd_send(dev, 0, GET_CUR,
- GET_FW_VERSION_CONTROL, sizeof(u32), version);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen)
-{
- u8 reg[] = { 0x0f, 0x00 };
-
- if (buflen < 128)
- return -ENOMEM;
-
- /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */
- /* TODO: Pull the details from the boards struct */
- return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg),
- &reg[0], 128, buf);
-}
-
-int saa7164_api_configure_port_vbi(struct saa7164_dev *dev,
- struct saa7164_port *port)
-{
- struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc;
-
- dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
- dprintk(DBGLVL_API, " VideoStandard = 0x%x\n", fmt->VideoStandard);
- dprintk(DBGLVL_API, " StartLine = %d\n", fmt->StartLine);
- dprintk(DBGLVL_API, " EndLine = %d\n", fmt->EndLine);
- dprintk(DBGLVL_API, " FieldRate = %d\n", fmt->FieldRate);
- dprintk(DBGLVL_API, " bNumLines = %d\n", fmt->bNumLines);
-
- /* Cache the hardware configuration in the port */
-
- port->bufcounter = port->hwcfg.BARLocation;
- port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
- port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
- port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
- port->bufptr32l = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
- port->bufptr32h = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- port->bufptr64 = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
- port->hwcfg.BARLocation);
-
- dprintk(DBGLVL_API, " = VS_FORMAT_VBI (becomes dev->en[%d])\n",
- port->nr);
-
- return 0;
-}
-
-int saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev,
- struct saa7164_port *port,
- struct tmComResTSFormatDescrHeader *tsfmt)
-{
- dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex);
- dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset);
- dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength);
- dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength);
- dprintk(DBGLVL_API, " bguid = (....)\n");
-
- /* Cache the hardware configuration in the port */
-
- port->bufcounter = port->hwcfg.BARLocation;
- port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
- port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
- port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
- port->bufptr32l = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
- port->bufptr32h = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- port->bufptr64 = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
- port->hwcfg.BARLocation);
-
- dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n",
- port->nr);
-
- return 0;
-}
-
-int saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev,
- struct saa7164_port *port,
- struct tmComResPSFormatDescrHeader *fmt)
-{
- dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex);
- dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength);
- dprintk(DBGLVL_API, " wPackLength= 0x%x\n", fmt->wPackLength);
- dprintk(DBGLVL_API, " bPackDataType= 0x%x\n", fmt->bPackDataType);
-
- /* Cache the hardware configuration in the port */
- /* TODO: CHECK THIS in the port config */
- port->bufcounter = port->hwcfg.BARLocation;
- port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32));
- port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32));
- port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32));
- port->bufptr32l = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32);
- port->bufptr32h = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- port->bufptr64 = port->hwcfg.BARLocation +
- (4 * sizeof(u32)) +
- (sizeof(u32) * port->hwcfg.buffercount);
- dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n",
- port->hwcfg.BARLocation);
-
- dprintk(DBGLVL_API, " = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n",
- port->nr);
-
- return 0;
-}
-
-int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len)
-{
- struct saa7164_port *tsport = NULL;
- struct saa7164_port *encport = NULL;
- struct saa7164_port *vbiport = NULL;
- u32 idx, next_offset;
- int i;
- struct tmComResDescrHeader *hdr, *t;
- struct tmComResExtDevDescrHeader *exthdr;
- struct tmComResPathDescrHeader *pathhdr;
- struct tmComResAntTermDescrHeader *anttermhdr;
- struct tmComResTunerDescrHeader *tunerunithdr;
- struct tmComResDMATermDescrHeader *vcoutputtermhdr;
- struct tmComResTSFormatDescrHeader *tsfmt;
- struct tmComResPSFormatDescrHeader *psfmt;
- struct tmComResSelDescrHeader *psel;
- struct tmComResProcDescrHeader *pdh;
- struct tmComResAFeatureDescrHeader *afd;
- struct tmComResEncoderDescrHeader *edh;
- struct tmComResVBIFormatDescrHeader *vbifmt;
- u32 currpath = 0;
-
- dprintk(DBGLVL_API,
- "%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n",
- __func__, len, (u32)sizeof(struct tmComResDescrHeader));
-
- for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) {
-
- hdr = (struct tmComResDescrHeader *)(buf + idx);
-
- if (hdr->type != CS_INTERFACE)
- return SAA_ERR_NOT_SUPPORTED;
-
- dprintk(DBGLVL_API, "@ 0x%x =\n", idx);
- switch (hdr->subtype) {
- case GENERAL_REQUEST:
- dprintk(DBGLVL_API, " GENERAL_REQUEST\n");
- break;
- case VC_TUNER_PATH:
- dprintk(DBGLVL_API, " VC_TUNER_PATH\n");
- pathhdr = (struct tmComResPathDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " pathid = 0x%x\n",
- pathhdr->pathid);
- currpath = pathhdr->pathid;
- break;
- case VC_INPUT_TERMINAL:
- dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n");
- anttermhdr =
- (struct tmComResAntTermDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " terminalid = 0x%x\n",
- anttermhdr->terminalid);
- dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
- anttermhdr->terminaltype);
- switch (anttermhdr->terminaltype) {
- case ITT_ANTENNA:
- dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
- break;
- case LINE_CONNECTOR:
- dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
- break;
- case SPDIF_CONNECTOR:
- dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
- break;
- case COMPOSITE_CONNECTOR:
- dprintk(DBGLVL_API,
- " = COMPOSITE_CONNECTOR\n");
- break;
- case SVIDEO_CONNECTOR:
- dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
- break;
- case COMPONENT_CONNECTOR:
- dprintk(DBGLVL_API,
- " = COMPONENT_CONNECTOR\n");
- break;
- case STANDARD_DMA:
- dprintk(DBGLVL_API, " = STANDARD_DMA\n");
- break;
- default:
- dprintk(DBGLVL_API, " = undefined (0x%x)\n",
- anttermhdr->terminaltype);
- }
- dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
- anttermhdr->assocterminal);
- dprintk(DBGLVL_API, " iterminal = 0x%x\n",
- anttermhdr->iterminal);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- anttermhdr->controlsize);
- break;
- case VC_OUTPUT_TERMINAL:
- dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n");
- vcoutputtermhdr =
- (struct tmComResDMATermDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- vcoutputtermhdr->unitid);
- dprintk(DBGLVL_API, " terminaltype = 0x%x\n",
- vcoutputtermhdr->terminaltype);
- switch (vcoutputtermhdr->terminaltype) {
- case ITT_ANTENNA:
- dprintk(DBGLVL_API, " = ITT_ANTENNA\n");
- break;
- case LINE_CONNECTOR:
- dprintk(DBGLVL_API, " = LINE_CONNECTOR\n");
- break;
- case SPDIF_CONNECTOR:
- dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n");
- break;
- case COMPOSITE_CONNECTOR:
- dprintk(DBGLVL_API,
- " = COMPOSITE_CONNECTOR\n");
- break;
- case SVIDEO_CONNECTOR:
- dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n");
- break;
- case COMPONENT_CONNECTOR:
- dprintk(DBGLVL_API,
- " = COMPONENT_CONNECTOR\n");
- break;
- case STANDARD_DMA:
- dprintk(DBGLVL_API, " = STANDARD_DMA\n");
- break;
- default:
- dprintk(DBGLVL_API, " = undefined (0x%x)\n",
- vcoutputtermhdr->terminaltype);
- }
- dprintk(DBGLVL_API, " assocterminal= 0x%x\n",
- vcoutputtermhdr->assocterminal);
- dprintk(DBGLVL_API, " sourceid = 0x%x\n",
- vcoutputtermhdr->sourceid);
- dprintk(DBGLVL_API, " iterminal = 0x%x\n",
- vcoutputtermhdr->iterminal);
- dprintk(DBGLVL_API, " BARLocation = 0x%x\n",
- vcoutputtermhdr->BARLocation);
- dprintk(DBGLVL_API, " flags = 0x%x\n",
- vcoutputtermhdr->flags);
- dprintk(DBGLVL_API, " interruptid = 0x%x\n",
- vcoutputtermhdr->interruptid);
- dprintk(DBGLVL_API, " buffercount = 0x%x\n",
- vcoutputtermhdr->buffercount);
- dprintk(DBGLVL_API, " metadatasize = 0x%x\n",
- vcoutputtermhdr->metadatasize);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- vcoutputtermhdr->controlsize);
- dprintk(DBGLVL_API, " numformats = 0x%x\n",
- vcoutputtermhdr->numformats);
-
- t = (struct tmComResDescrHeader *)
- ((struct tmComResDMATermDescrHeader *)(buf + idx));
- next_offset = idx + (vcoutputtermhdr->len);
- for (i = 0; i < vcoutputtermhdr->numformats; i++) {
- t = (struct tmComResDescrHeader *)
- (buf + next_offset);
- switch (t->subtype) {
- case VS_FORMAT_MPEG2TS:
- tsfmt =
- (struct tmComResTSFormatDescrHeader *)t;
- if (currpath == 1)
- tsport = &dev->ports[SAA7164_PORT_TS1];
- else
- tsport = &dev->ports[SAA7164_PORT_TS2];
- memcpy(&tsport->hwcfg, vcoutputtermhdr,
- sizeof(*vcoutputtermhdr));
- saa7164_api_configure_port_mpeg2ts(dev,
- tsport, tsfmt);
- break;
- case VS_FORMAT_MPEG2PS:
- psfmt =
- (struct tmComResPSFormatDescrHeader *)t;
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->hwcfg, vcoutputtermhdr,
- sizeof(*vcoutputtermhdr));
- saa7164_api_configure_port_mpeg2ps(dev,
- encport, psfmt);
- break;
- case VS_FORMAT_VBI:
- vbifmt =
- (struct tmComResVBIFormatDescrHeader *)t;
- if (currpath == 1)
- vbiport = &dev->ports[SAA7164_PORT_VBI1];
- else
- vbiport = &dev->ports[SAA7164_PORT_VBI2];
- memcpy(&vbiport->hwcfg, vcoutputtermhdr,
- sizeof(*vcoutputtermhdr));
- memcpy(&vbiport->vbi_fmt_ntsc, vbifmt,
- sizeof(*vbifmt));
- saa7164_api_configure_port_vbi(dev,
- vbiport);
- break;
- case VS_FORMAT_RDS:
- dprintk(DBGLVL_API,
- " = VS_FORMAT_RDS\n");
- break;
- case VS_FORMAT_UNCOMPRESSED:
- dprintk(DBGLVL_API,
- " = VS_FORMAT_UNCOMPRESSED\n");
- break;
- case VS_FORMAT_TYPE:
- dprintk(DBGLVL_API,
- " = VS_FORMAT_TYPE\n");
- break;
- default:
- dprintk(DBGLVL_API,
- " = undefined (0x%x)\n",
- t->subtype);
- }
- next_offset += t->len;
- }
-
- break;
- case TUNER_UNIT:
- dprintk(DBGLVL_API, " TUNER_UNIT\n");
- tunerunithdr =
- (struct tmComResTunerDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- tunerunithdr->unitid);
- dprintk(DBGLVL_API, " sourceid = 0x%x\n",
- tunerunithdr->sourceid);
- dprintk(DBGLVL_API, " iunit = 0x%x\n",
- tunerunithdr->iunit);
- dprintk(DBGLVL_API, " tuningstandards = 0x%x\n",
- tunerunithdr->tuningstandards);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- tunerunithdr->controlsize);
- dprintk(DBGLVL_API, " controls = 0x%x\n",
- tunerunithdr->controls);
-
- if (tunerunithdr->unitid == tunerunithdr->iunit) {
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->tunerunit, tunerunithdr,
- sizeof(struct tmComResTunerDescrHeader));
- dprintk(DBGLVL_API,
- " (becomes dev->enc[%d] tuner)\n",
- encport->nr);
- }
- break;
- case VC_SELECTOR_UNIT:
- psel = (struct tmComResSelDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n");
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- psel->unitid);
- dprintk(DBGLVL_API, " nrinpins = 0x%x\n",
- psel->nrinpins);
- dprintk(DBGLVL_API, " sourceid = 0x%x\n",
- psel->sourceid);
- break;
- case VC_PROCESSING_UNIT:
- pdh = (struct tmComResProcDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n");
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- pdh->unitid);
- dprintk(DBGLVL_API, " sourceid = 0x%x\n",
- pdh->sourceid);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- pdh->controlsize);
- if (pdh->controlsize == 0x04) {
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->vidproc, pdh,
- sizeof(struct tmComResProcDescrHeader));
- dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
- encport->nr);
- }
- break;
- case FEATURE_UNIT:
- afd = (struct tmComResAFeatureDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " FEATURE_UNIT\n");
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- afd->unitid);
- dprintk(DBGLVL_API, " sourceid = 0x%x\n",
- afd->sourceid);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- afd->controlsize);
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->audfeat, afd,
- sizeof(struct tmComResAFeatureDescrHeader));
- dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n",
- encport->nr);
- break;
- case ENCODER_UNIT:
- edh = (struct tmComResEncoderDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " ENCODER_UNIT\n");
- dprintk(DBGLVL_API, " subtype = 0x%x\n", edh->subtype);
- dprintk(DBGLVL_API, " unitid = 0x%x\n", edh->unitid);
- dprintk(DBGLVL_API, " vsourceid = 0x%x\n",
- edh->vsourceid);
- dprintk(DBGLVL_API, " asourceid = 0x%x\n",
- edh->asourceid);
- dprintk(DBGLVL_API, " iunit = 0x%x\n", edh->iunit);
- if (edh->iunit == edh->unitid) {
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->encunit, edh,
- sizeof(struct tmComResEncoderDescrHeader));
- dprintk(DBGLVL_API,
- " (becomes dev->enc[%d])\n",
- encport->nr);
- }
- break;
- case EXTENSION_UNIT:
- dprintk(DBGLVL_API, " EXTENSION_UNIT\n");
- exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx);
- dprintk(DBGLVL_API, " unitid = 0x%x\n",
- exthdr->unitid);
- dprintk(DBGLVL_API, " deviceid = 0x%x\n",
- exthdr->deviceid);
- dprintk(DBGLVL_API, " devicetype = 0x%x\n",
- exthdr->devicetype);
- if (exthdr->devicetype & 0x1)
- dprintk(DBGLVL_API, " = Decoder Device\n");
- if (exthdr->devicetype & 0x2)
- dprintk(DBGLVL_API, " = GPIO Source\n");
- if (exthdr->devicetype & 0x4)
- dprintk(DBGLVL_API, " = Video Decoder\n");
- if (exthdr->devicetype & 0x8)
- dprintk(DBGLVL_API, " = Audio Decoder\n");
- if (exthdr->devicetype & 0x20)
- dprintk(DBGLVL_API, " = Crossbar\n");
- if (exthdr->devicetype & 0x40)
- dprintk(DBGLVL_API, " = Tuner\n");
- if (exthdr->devicetype & 0x80)
- dprintk(DBGLVL_API, " = IF PLL\n");
- if (exthdr->devicetype & 0x100)
- dprintk(DBGLVL_API, " = Demodulator\n");
- if (exthdr->devicetype & 0x200)
- dprintk(DBGLVL_API, " = RDS Decoder\n");
- if (exthdr->devicetype & 0x400)
- dprintk(DBGLVL_API, " = Encoder\n");
- if (exthdr->devicetype & 0x800)
- dprintk(DBGLVL_API, " = IR Decoder\n");
- if (exthdr->devicetype & 0x1000)
- dprintk(DBGLVL_API, " = EEPROM\n");
- if (exthdr->devicetype & 0x2000)
- dprintk(DBGLVL_API,
- " = VBI Decoder\n");
- if (exthdr->devicetype & 0x10000)
- dprintk(DBGLVL_API,
- " = Streaming Device\n");
- if (exthdr->devicetype & 0x20000)
- dprintk(DBGLVL_API,
- " = DRM Device\n");
- if (exthdr->devicetype & 0x40000000)
- dprintk(DBGLVL_API,
- " = Generic Device\n");
- if (exthdr->devicetype & 0x80000000)
- dprintk(DBGLVL_API,
- " = Config Space Device\n");
- dprintk(DBGLVL_API, " numgpiopins = 0x%x\n",
- exthdr->numgpiopins);
- dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n",
- exthdr->numgpiogroups);
- dprintk(DBGLVL_API, " controlsize = 0x%x\n",
- exthdr->controlsize);
- if (exthdr->devicetype & 0x80) {
- if (currpath == 1)
- encport = &dev->ports[SAA7164_PORT_ENC1];
- else
- encport = &dev->ports[SAA7164_PORT_ENC2];
- memcpy(&encport->ifunit, exthdr,
- sizeof(struct tmComResExtDevDescrHeader));
- dprintk(DBGLVL_API,
- " (becomes dev->enc[%d])\n",
- encport->nr);
- }
- break;
- case PVC_INFRARED_UNIT:
- dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n");
- break;
- case DRM_UNIT:
- dprintk(DBGLVL_API, " DRM_UNIT\n");
- break;
- default:
- dprintk(DBGLVL_API, "default %d\n", hdr->subtype);
- }
-
- dprintk(DBGLVL_API, " 1.%x\n", hdr->len);
- dprintk(DBGLVL_API, " 2.%x\n", hdr->type);
- dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype);
- dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid);
-
- idx += hdr->len;
- }
-
- return 0;
-}
-
-int saa7164_api_enum_subdevs(struct saa7164_dev *dev)
-{
- int ret;
- u32 buflen = 0;
- u8 *buf;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- /* Get the total descriptor length */
- ret = saa7164_cmd_send(dev, 0, GET_LEN,
- GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
-
- dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n",
- __func__, buflen);
-
- /* Allocate enough storage for all of the descs */
- buf = kzalloc(buflen, GFP_KERNEL);
- if (!buf)
- return SAA_ERR_NO_RESOURCES;
-
- /* Retrieve them */
- ret = saa7164_cmd_send(dev, 0, GET_CUR,
- GET_DESCRIPTORS_CONTROL, buflen, buf);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret);
- goto out;
- }
-
- if (saa_debug & DBGLVL_API)
- saa7164_dumphex16(dev, buf, (buflen/16)*16);
-
- saa7164_api_dump_subdevs(dev, buf, buflen);
-
-out:
- kfree(buf);
- return ret;
-}
-
-int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
- u32 datalen, u8 *data)
-{
- struct saa7164_dev *dev = bus->dev;
- u16 len = 0;
- int unitid;
- u8 buf[256];
- int ret;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- if (reglen > 4)
- return -EIO;
-
- /* Prepare the send buffer */
- /* Bytes 00-03 source register length
- * 04-07 source bytes to read
- * 08... register address
- */
- memset(buf, 0, sizeof(buf));
- memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen);
- *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
- *((u32 *)(buf + 1 * sizeof(u32))) = datalen;
-
- unitid = saa7164_i2caddr_to_unitid(bus, addr);
- if (unitid < 0) {
- printk(KERN_ERR
- "%s() error, cannot translate regaddr 0x%x to unitid\n",
- __func__, addr);
- return -EIO;
- }
-
- ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
- EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
- return -EIO;
- }
-
- dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
-
- if (saa_debug & DBGLVL_I2C)
- saa7164_dumphex16(dev, buf, 2 * 16);
-
- ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR,
- EXU_REGISTER_ACCESS_CONTROL, len, &buf);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
- else {
- if (saa_debug & DBGLVL_I2C)
- saa7164_dumphex16(dev, buf, sizeof(buf));
- memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen);
- }
-
- return ret == SAA_OK ? 0 : -EIO;
-}
-
-/* For a given 8 bit i2c address device, write the buffer */
-int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen,
- u8 *data)
-{
- struct saa7164_dev *dev = bus->dev;
- u16 len = 0;
- int unitid;
- int reglen;
- u8 buf[256];
- int ret;
-
- dprintk(DBGLVL_API, "%s()\n", __func__);
-
- if ((datalen == 0) || (datalen > 232))
- return -EIO;
-
- memset(buf, 0, sizeof(buf));
-
- unitid = saa7164_i2caddr_to_unitid(bus, addr);
- if (unitid < 0) {
- printk(KERN_ERR
- "%s() error, cannot translate regaddr 0x%x to unitid\n",
- __func__, addr);
- return -EIO;
- }
-
- reglen = saa7164_i2caddr_to_reglen(bus, addr);
- if (reglen < 0) {
- printk(KERN_ERR
- "%s() error, cannot translate regaddr to reglen\n",
- __func__);
- return -EIO;
- }
-
- ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN,
- EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len);
- if (ret != SAA_OK) {
- printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret);
- return -EIO;
- }
-
- dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len);
-
- /* Prepare the send buffer */
- /* Bytes 00-03 dest register length
- * 04-07 dest bytes to write
- * 08... register address
- */
- *((u32 *)(buf + 0 * sizeof(u32))) = reglen;
- *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen;
- memcpy((buf + 2 * sizeof(u32)), data, datalen);
-
- if (saa_debug & DBGLVL_I2C)
- saa7164_dumphex16(dev, buf, sizeof(buf));
-
- ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR,
- EXU_REGISTER_ACCESS_CONTROL, len, &buf);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret);
-
- return ret == SAA_OK ? 0 : -EIO;
-}
-
-int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid,
- u8 pin, u8 state)
-{
- int ret;
- struct tmComResGPIO t;
-
- dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n",
- __func__, unitid, pin, state);
-
- if ((pin > 7) || (state > 2))
- return SAA_ERR_BAD_PARAMETER;
-
- t.pin = pin;
- t.state = state;
-
- ret = saa7164_cmd_send(dev, unitid, SET_CUR,
- EXU_GPIO_CONTROL, sizeof(t), &t);
- if (ret != SAA_OK)
- printk(KERN_ERR "%s() error, ret = 0x%x\n",
- __func__, ret);
-
- return ret;
-}
-
-int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid,
- u8 pin)
-{
- return saa7164_api_modify_gpio(dev, unitid, pin, 1);
-}
-
-int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid,
- u8 pin)
-{
- return saa7164_api_modify_gpio(dev, unitid, pin, 0);
-}
-
diff --git a/drivers/media/video/saa7164/saa7164-core.c b/drivers/media/video/saa7164/saa7164-core.c
deleted file mode 100644
index 3b7d7b4e3034..000000000000
--- a/drivers/media/video/saa7164/saa7164-core.c
+++ /dev/null
@@ -1,1526 +0,0 @@
-/*
- * Driver for the NXP SAA7164 PCIe bridge
- *
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kmod.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <asm/div64.h>
-
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
-#include "saa7164.h"
-
-MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards");
-MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>");
-MODULE_LICENSE("GPL");
-
-/*
- * 1 Basic
- * 2
- * 4 i2c
- * 8 api
- * 16 cmd
- * 32 bus
- */
-
-unsigned int saa_debug;
-module_param_named(debug, saa_debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable debug messages");
-
-unsigned int fw_debug;
-module_param(fw_debug, int, 0644);
-MODULE_PARM_DESC(fw_debug, "Firware debug level def:2");
-
-unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS;
-module_param(encoder_buffers, int, 0644);
-MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64");
-
-unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS;
-module_param(vbi_buffers, int, 0644);
-MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64");
-
-unsigned int waitsecs = 10;
-module_param(waitsecs, int, 0644);
-MODULE_PARM_DESC(waitsecs, "timeout on firmware messages");
-
-static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET };
-module_param_array(card, int, NULL, 0444);
-MODULE_PARM_DESC(card, "card type");
-
-unsigned int print_histogram = 64;
-module_param(print_histogram, int, 0644);
-MODULE_PARM_DESC(print_histogram, "print histogram values once");
-
-unsigned int crc_checking = 1;
-module_param(crc_checking, int, 0644);
-MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers");
-
-unsigned int guard_checking = 1;
-module_param(guard_checking, int, 0644);
-MODULE_PARM_DESC(guard_checking,
- "enable dma sanity checking for buffer overruns");
-
-static unsigned int saa7164_devcount;
-
-static DEFINE_MUTEX(devlist);
-LIST_HEAD(saa7164_devlist);
-
-#define INT_SIZE 16
-
-void saa7164_dumphex16FF(struct saa7164_dev *dev, u8 *buf, int len)
-{
- int i;
- u8 tmp[16];
- memset(&tmp[0], 0xff, sizeof(tmp));
-
- printk(KERN_INFO "--------------------> "
- "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
-
- for (i = 0; i < len; i += 16) {
- if (memcmp(&tmp, buf + i, sizeof(tmp)) != 0) {
- printk(KERN_INFO " [0x%08x] "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
- *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
- *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
- *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
- *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
- }
- }
-}
-
-static void saa7164_pack_verifier(struct saa7164_buffer *buf)
-{
- u8 *p = (u8 *)buf->cpu;
- int i;
-
- for (i = 0; i < buf->actual_size; i += 2048) {
-
- if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) ||
- (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) {
- printk(KERN_ERR "No pack at 0x%x\n", i);
-#if 0
- saa7164_dumphex16FF(buf->port->dev, (p + i), 32);
-#endif
- }
- }
-}
-
-#define FIXED_VIDEO_PID 0xf1
-#define FIXED_AUDIO_PID 0xf2
-
-static void saa7164_ts_verifier(struct saa7164_buffer *buf)
-{
- struct saa7164_port *port = buf->port;
- u32 i;
- u8 cc, a;
- u16 pid;
- u8 __iomem *bufcpu = (u8 *)buf->cpu;
-
- port->sync_errors = 0;
- port->v_cc_errors = 0;
- port->a_cc_errors = 0;
-
- for (i = 0; i < buf->actual_size; i += 188) {
- if (*(bufcpu + i) != 0x47)
- port->sync_errors++;
-
- /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */
- pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2);
- cc = *(bufcpu + i + 3) & 0x0f;
-
- if (pid == FIXED_VIDEO_PID) {
- a = ((port->last_v_cc + 1) & 0x0f);
- if (a != cc) {
- printk(KERN_ERR "video cc last = %x current = %x i = %d\n",
- port->last_v_cc, cc, i);
- port->v_cc_errors++;
- }
-
- port->last_v_cc = cc;
- } else
- if (pid == FIXED_AUDIO_PID) {
- a = ((port->last_a_cc + 1) & 0x0f);
- if (a != cc) {
- printk(KERN_ERR "audio cc last = %x current = %x i = %d\n",
- port->last_a_cc, cc, i);
- port->a_cc_errors++;
- }
-
- port->last_a_cc = cc;
- }
-
- }
-
- /* Only report errors if we've been through this function atleast
- * once already and the cached cc values are primed. First time through
- * always generates errors.
- */
- if (port->v_cc_errors && (port->done_first_interrupt > 1))
- printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors);
-
- if (port->a_cc_errors && (port->done_first_interrupt > 1))
- printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors);
-
- if (port->sync_errors && (port->done_first_interrupt > 1))
- printk(KERN_ERR "sync_errors = %d\n", port->sync_errors);
-
- if (port->done_first_interrupt == 1)
- port->done_first_interrupt++;
-}
-
-static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name)
-{
- int i;
-
- memset(hg, 0, sizeof(struct saa7164_histogram));
- strcpy(hg->name, name);
-
- /* First 30ms x 1ms */
- for (i = 0; i < 30; i++)
- hg->counter1[0 + i].val = i;
-
- /* 30 - 200ms x 10ms */
- for (i = 0; i < 18; i++)
- hg->counter1[30 + i].val = 30 + (i * 10);
-
- /* 200 - 2000ms x 100ms */
- for (i = 0; i < 15; i++)
- hg->counter1[48 + i].val = 200 + (i * 200);
-
- /* Catch all massive value (2secs) */
- hg->counter1[55].val = 2000;
-
- /* Catch all massive value (4secs) */
- hg->counter1[56].val = 4000;
-
- /* Catch all massive value (8secs) */
- hg->counter1[57].val = 8000;
-
- /* Catch all massive value (15secs) */
- hg->counter1[58].val = 15000;
-
- /* Catch all massive value (30secs) */
- hg->counter1[59].val = 30000;
-
- /* Catch all massive value (60secs) */
- hg->counter1[60].val = 60000;
-
- /* Catch all massive value (5mins) */
- hg->counter1[61].val = 300000;
-
- /* Catch all massive value (15mins) */
- hg->counter1[62].val = 900000;
-
- /* Catch all massive values (1hr) */
- hg->counter1[63].val = 3600000;
-}
-
-void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val)
-{
- int i;
- for (i = 0; i < 64; i++) {
- if (val <= hg->counter1[i].val) {
- hg->counter1[i].count++;
- hg->counter1[i].update_time = jiffies;
- break;
- }
- }
-}
-
-static void saa7164_histogram_print(struct saa7164_port *port,
- struct saa7164_histogram *hg)
-{
- u32 entries = 0;
- int i;
-
- printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name);
- for (i = 0; i < 64; i++) {
- if (hg->counter1[i].count == 0)
- continue;
-
- printk(KERN_ERR " %4d %12d %Ld\n",
- hg->counter1[i].val,
- hg->counter1[i].count,
- hg->counter1[i].update_time);
-
- entries++;
- }
- printk(KERN_ERR "Total: %d\n", entries);
-}
-
-static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr)
-{
- struct saa7164_dev *dev = port->dev;
- struct saa7164_buffer *buf = NULL;
- struct saa7164_user_buffer *ubuf = NULL;
- struct list_head *c, *n;
- int i = 0;
- u8 __iomem *p;
-
- mutex_lock(&port->dmaqueue_lock);
- list_for_each_safe(c, n, &port->dmaqueue.list) {
-
- buf = list_entry(c, struct saa7164_buffer, list);
- if (i++ > port->hwcfg.buffercount) {
- printk(KERN_ERR "%s() illegal i count %d\n",
- __func__, i);
- break;
- }
-
- if (buf->idx == bufnr) {
-
- /* Found the buffer, deal with it */
- dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr);
-
- if (crc_checking) {
- /* Throw a new checksum on the dma buffer */
- buf->crc = crc32(0, buf->cpu, buf->actual_size);
- }
-
- if (guard_checking) {
- p = (u8 *)buf->cpu;
- if ((*(p + buf->actual_size + 0) != 0xff) ||
- (*(p + buf->actual_size + 1) != 0xff) ||
- (*(p + buf->actual_size + 2) != 0xff) ||
- (*(p + buf->actual_size + 3) != 0xff) ||
- (*(p + buf->actual_size + 0x10) != 0xff) ||
- (*(p + buf->actual_size + 0x11) != 0xff) ||
- (*(p + buf->actual_size + 0x12) != 0xff) ||
- (*(p + buf->actual_size + 0x13) != 0xff)) {
- printk(KERN_ERR "%s() buf %p guard buffer breach\n",
- __func__, buf);
-#if 0
- saa7164_dumphex16FF(dev, (p + buf->actual_size) - 32 , 64);
-#endif
- }
- }
-
- if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) {
- /* Validate the incoming buffer content */
- if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
- saa7164_ts_verifier(buf);
- else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS)
- saa7164_pack_verifier(buf);
- }
-
- /* find a free user buffer and clone to it */
- if (!list_empty(&port->list_buf_free.list)) {
-
- /* Pull the first buffer from the used list */
- ubuf = list_first_entry(&port->list_buf_free.list,
- struct saa7164_user_buffer, list);
-
- if (buf->actual_size <= ubuf->actual_size) {
-
- memcpy_fromio(ubuf->data, buf->cpu,
- ubuf->actual_size);
-
- if (crc_checking) {
- /* Throw a new checksum on the read buffer */
- ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size);
- }
-
- /* Requeue the buffer on the free list */
- ubuf->pos = 0;
-
- list_move_tail(&ubuf->list,
- &port->list_buf_used.list);
-
- /* Flag any userland waiters */
- wake_up_interruptible(&port->wait_read);
-
- } else {
- printk(KERN_ERR "buf %p bufsize fails match\n", buf);
- }
-
- } else
- printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n");
-
- /* Ensure offset into buffer remains 0, fill buffer
- * with known bad data. We check for this data at a later point
- * in time. */
- saa7164_buffer_zero_offsets(port, bufnr);
- memset_io(buf->cpu, 0xff, buf->pci_size);
- if (crc_checking) {
- /* Throw yet aanother new checksum on the dma buffer */
- buf->crc = crc32(0, buf->cpu, buf->actual_size);
- }
-
- break;
- }
- }
- mutex_unlock(&port->dmaqueue_lock);
-}
-
-static void saa7164_work_enchandler(struct work_struct *w)
-{
- struct saa7164_port *port =
- container_of(w, struct saa7164_port, workenc);
- struct saa7164_dev *dev = port->dev;
-
- u32 wp, mcb, rp, cnt = 0;
-
- port->last_svc_msecs_diff = port->last_svc_msecs;
- port->last_svc_msecs = jiffies_to_msecs(jiffies);
-
- port->last_svc_msecs_diff = port->last_svc_msecs -
- port->last_svc_msecs_diff;
-
- saa7164_histogram_update(&port->svc_interval,
- port->last_svc_msecs_diff);
-
- port->last_irq_svc_msecs_diff = port->last_svc_msecs -
- port->last_irq_msecs;
-
- saa7164_histogram_update(&port->irq_svc_interval,
- port->last_irq_svc_msecs_diff);
-
- dprintk(DBGLVL_IRQ,
- "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
- __func__,
- port->last_svc_msecs_diff,
- port->last_irq_svc_msecs_diff,
- port->last_svc_wp,
- port->last_svc_rp
- );
-
- /* Current write position */
- wp = saa7164_readl(port->bufcounter);
- if (wp > (port->hwcfg.buffercount - 1)) {
- printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
- return;
- }
-
- /* Most current complete buffer */
- if (wp == 0)
- mcb = (port->hwcfg.buffercount - 1);
- else
- mcb = wp - 1;
-
- while (1) {
- if (port->done_first_interrupt == 0) {
- port->done_first_interrupt++;
- rp = mcb;
- } else
- rp = (port->last_svc_rp + 1) % 8;
-
- if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
- printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
- break;
- }
-
- saa7164_work_enchandler_helper(port, rp);
- port->last_svc_rp = rp;
- cnt++;
-
- if (rp == mcb)
- break;
- }
-
- /* TODO: Convert this into a /proc/saa7164 style readable file */
- if (print_histogram == port->nr) {
- saa7164_histogram_print(port, &port->irq_interval);
- saa7164_histogram_print(port, &port->svc_interval);
- saa7164_histogram_print(port, &port->irq_svc_interval);
- saa7164_histogram_print(port, &port->read_interval);
- saa7164_histogram_print(port, &port->poll_interval);
- /* TODO: fix this to preserve any previous state */
- print_histogram = 64 + port->nr;
- }
-}
-
-static void saa7164_work_vbihandler(struct work_struct *w)
-{
- struct saa7164_port *port =
- container_of(w, struct saa7164_port, workenc);
- struct saa7164_dev *dev = port->dev;
-
- u32 wp, mcb, rp, cnt = 0;
-
- port->last_svc_msecs_diff = port->last_svc_msecs;
- port->last_svc_msecs = jiffies_to_msecs(jiffies);
- port->last_svc_msecs_diff = port->last_svc_msecs -
- port->last_svc_msecs_diff;
-
- saa7164_histogram_update(&port->svc_interval,
- port->last_svc_msecs_diff);
-
- port->last_irq_svc_msecs_diff = port->last_svc_msecs -
- port->last_irq_msecs;
-
- saa7164_histogram_update(&port->irq_svc_interval,
- port->last_irq_svc_msecs_diff);
-
- dprintk(DBGLVL_IRQ,
- "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n",
- __func__,
- port->last_svc_msecs_diff,
- port->last_irq_svc_msecs_diff,
- port->last_svc_wp,
- port->last_svc_rp
- );
-
- /* Current write position */
- wp = saa7164_readl(port->bufcounter);
- if (wp > (port->hwcfg.buffercount - 1)) {
- printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp);
- return;
- }
-
- /* Most current complete buffer */
- if (wp == 0)
- mcb = (port->hwcfg.buffercount - 1);
- else
- mcb = wp - 1;
-
- while (1) {
- if (port->done_first_interrupt == 0) {
- port->done_first_interrupt++;
- rp = mcb;
- } else
- rp = (port->last_svc_rp + 1) % 8;
-
- if ((rp < 0) || (rp > (port->hwcfg.buffercount - 1))) {
- printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp);
- break;
- }
-
- saa7164_work_enchandler_helper(port, rp);
- port->last_svc_rp = rp;
- cnt++;
-
- if (rp == mcb)
- break;
- }
-
- /* TODO: Convert this into a /proc/saa7164 style readable file */
- if (print_histogram == port->nr) {
- saa7164_histogram_print(port, &port->irq_interval);
- saa7164_histogram_print(port, &port->svc_interval);
- saa7164_histogram_print(port, &port->irq_svc_interval);
- saa7164_histogram_print(port, &port->read_interval);
- saa7164_histogram_print(port, &port->poll_interval);
- /* TODO: fix this to preserve any previous state */
- print_histogram = 64 + port->nr;
- }
-}
-
-static void saa7164_work_cmdhandler(struct work_struct *w)
-{
- struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd);
-
- /* Wake up any complete commands */
- saa7164_irq_dequeue(dev);
-}
-
-static void saa7164_buffer_deliver(struct saa7164_buffer *buf)
-{
- struct saa7164_port *port = buf->port;
-
- /* Feed the transport payload into the kernel demux */
- dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu,
- SAA7164_TS_NUMBER_OF_LINES);
-
-}
-
-static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
-
- /* Store old time */
- port->last_irq_msecs_diff = port->last_irq_msecs;
-
- /* Collect new stats */
- port->last_irq_msecs = jiffies_to_msecs(jiffies);
-
- /* Calculate stats */
- port->last_irq_msecs_diff = port->last_irq_msecs -
- port->last_irq_msecs_diff;
-
- saa7164_histogram_update(&port->irq_interval,
- port->last_irq_msecs_diff);
-
- dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
- port->last_irq_msecs_diff);
-
- /* Tis calls the vbi irq handler */
- schedule_work(&port->workenc);
- return 0;
-}
-
-static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
-
- /* Store old time */
- port->last_irq_msecs_diff = port->last_irq_msecs;
-
- /* Collect new stats */
- port->last_irq_msecs = jiffies_to_msecs(jiffies);
-
- /* Calculate stats */
- port->last_irq_msecs_diff = port->last_irq_msecs -
- port->last_irq_msecs_diff;
-
- saa7164_histogram_update(&port->irq_interval,
- port->last_irq_msecs_diff);
-
- dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__,
- port->last_irq_msecs_diff);
-
- schedule_work(&port->workenc);
- return 0;
-}
-
-static irqreturn_t saa7164_irq_ts(struct saa7164_port *port)
-{
- struct saa7164_dev *dev = port->dev;
- struct saa7164_buffer *buf;
- struct list_head *c, *n;
- int wp, i = 0, rp;
-
- /* Find the current write point from the hardware */
- wp = saa7164_readl(port->bufcounter);
- if (wp > (port->hwcfg.buffercount - 1))
- BUG();
-
- /* Find the previous buffer to the current write point */
- if (wp == 0)
- rp = (port->hwcfg.buffercount - 1);
- else
- rp = wp - 1;
-
- /* Lookup the WP in the buffer list */
- /* TODO: turn this into a worker thread */
- list_for_each_safe(c, n, &port->dmaqueue.list) {
- buf = list_entry(c, struct saa7164_buffer, list);
- if (i++ > port->hwcfg.buffercount)
- BUG();
-
- if (buf->idx == rp) {
- /* Found the buffer, deal with it */
- dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n",
- __func__, wp, rp);
- saa7164_buffer_deliver(buf);
- break;
- }
-
- }
- return 0;
-}
-
-/* Primary IRQ handler and dispatch mechanism */
-static irqreturn_t saa7164_irq(int irq, void *dev_id)
-{
- struct saa7164_dev *dev = dev_id;
- struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1];
- struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2];
- struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1];
- struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2];
- struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1];
- struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2];
-
- u32 intid, intstat[INT_SIZE/4];
- int i, handled = 0, bit;
-
- if (dev == NULL) {
- printk(KERN_ERR "%s() No device specified\n", __func__);
- handled = 0;
- goto out;
- }
-
- /* Check that the hardware is accessible. If the status bytes are
- * 0xFF then the device is not accessible, the the IRQ belongs
- * to another driver.
- * 4 x u32 interrupt registers.
- */
- for (i = 0; i < INT_SIZE/4; i++) {
-
- /* TODO: Convert into saa7164_readl() */
- /* Read the 4 hardware interrupt registers */
- intstat[i] = saa7164_readl(dev->int_status + (i * 4));
-
- if (intstat[i])
- handled = 1;
- }
- if (handled == 0)
- goto out;
-
- /* For each of the HW interrupt registers */
- for (i = 0; i < INT_SIZE/4; i++) {
-
- if (intstat[i]) {
- /* Each function of the board has it's own interruptid.
- * Find the function that triggered then call
- * it's handler.
- */
- for (bit = 0; bit < 32; bit++) {
-
- if (((intstat[i] >> bit) & 0x00000001) == 0)
- continue;
-
- /* Calculate the interrupt id (0x00 to 0x7f) */
-
- intid = (i * 32) + bit;
- if (intid == dev->intfdesc.bInterruptId) {
- /* A response to an cmd/api call */
- schedule_work(&dev->workcmd);
- } else if (intid == porta->hwcfg.interruptid) {
-
- /* Transport path 1 */
- saa7164_irq_ts(porta);
-
- } else if (intid == portb->hwcfg.interruptid) {
-
- /* Transport path 2 */
- saa7164_irq_ts(portb);
-
- } else if (intid == portc->hwcfg.interruptid) {
-
- /* Encoder path 1 */
- saa7164_irq_encoder(portc);
-
- } else if (intid == portd->hwcfg.interruptid) {
-
- /* Encoder path 2 */
- saa7164_irq_encoder(portd);
-
- } else if (intid == porte->hwcfg.interruptid) {
-
- /* VBI path 1 */
- saa7164_irq_vbi(porte);
-
- } else if (intid == portf->hwcfg.interruptid) {
-
- /* VBI path 2 */
- saa7164_irq_vbi(portf);
-
- } else {
- /* Find the function */
- dprintk(DBGLVL_IRQ,
- "%s() unhandled interrupt "
- "reg 0x%x bit 0x%x "
- "intid = 0x%x\n",
- __func__, i, bit, intid);
- }
- }
-
- /* Ack it */
- saa7164_writel(dev->int_ack + (i * 4), intstat[i]);
-
- }
- }
-out:
- return IRQ_RETVAL(handled);
-}
-
-void saa7164_getfirmwarestatus(struct saa7164_dev *dev)
-{
- struct saa7164_fw_status *s = &dev->fw_status;
-
- dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS);
- dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE);
- dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC);
- dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST);
- dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD);
- dev->fw_status.remainheap =
- saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP);
-
- dprintk(1, "Firmware status:\n");
- dprintk(1, " .status = 0x%08x\n", s->status);
- dprintk(1, " .mode = 0x%08x\n", s->mode);
- dprintk(1, " .spec = 0x%08x\n", s->spec);
- dprintk(1, " .inst = 0x%08x\n", s->inst);
- dprintk(1, " .cpuload = 0x%08x\n", s->cpuload);
- dprintk(1, " .remainheap = 0x%08x\n", s->remainheap);
-}
-
-u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev)
-{
- u32 reg;
-
- reg = saa7164_readl(SAA_DEVICE_VERSION);
- dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n",
- (reg & 0x0000fc00) >> 10,
- (reg & 0x000003e0) >> 5,
- (reg & 0x0000001f),
- (reg & 0xffff0000) >> 16,
- reg);
-
- return reg;
-}
-
-/* TODO: Debugging func, remove */
-void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len)
-{
- int i;
-
- printk(KERN_INFO "--------------------> "
- "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
-
- for (i = 0; i < len; i += 16)
- printk(KERN_INFO " [0x%08x] "
- "%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n", i,
- *(buf+i+0), *(buf+i+1), *(buf+i+2), *(buf+i+3),
- *(buf+i+4), *(buf+i+5), *(buf+i+6), *(buf+i+7),
- *(buf+i+8), *(buf+i+9), *(buf+i+10), *(buf+i+11),
- *(buf+i+12), *(buf+i+13), *(buf+i+14), *(buf+i+15));
-}
-
-/* TODO: Debugging func, remove */
-void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr)
-{
- int i;
-
- dprintk(1, "--------------------> "
- "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
-
- for (i = 0; i < 0x100; i += 16)
- dprintk(1, "region0[0x%08x] = "
- "%02x %02x %02x %02x %02x %02x %02x %02x"
- " %02x %02x %02x %02x %02x %02x %02x %02x\n", i,
- (u8)saa7164_readb(addr + i + 0),
- (u8)saa7164_readb(addr + i + 1),
- (u8)saa7164_readb(addr + i + 2),
- (u8)saa7164_readb(addr + i + 3),
- (u8)saa7164_readb(addr + i + 4),
- (u8)saa7164_readb(addr + i + 5),
- (u8)saa7164_readb(addr + i + 6),
- (u8)saa7164_readb(addr + i + 7),
- (u8)saa7164_readb(addr + i + 8),
- (u8)saa7164_readb(addr + i + 9),
- (u8)saa7164_readb(addr + i + 10),
- (u8)saa7164_readb(addr + i + 11),
- (u8)saa7164_readb(addr + i + 12),
- (u8)saa7164_readb(addr + i + 13),
- (u8)saa7164_readb(addr + i + 14),
- (u8)saa7164_readb(addr + i + 15)
- );
-}
-
-static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
-{
- dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
- &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr));
-
- dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength);
- dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType);
- dprintk(1, " .bDescriptorSubtype = 0x%x\n",
- dev->hwdesc.bDescriptorSubtype);
-
- dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion);
- dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency);
- dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes);
- dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities);
- dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n",
- dev->hwdesc.dwDeviceRegistersLocation);
-
- dprintk(1, " .dwHostMemoryRegion = 0x%x\n",
- dev->hwdesc.dwHostMemoryRegion);
-
- dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n",
- dev->hwdesc.dwHostMemoryRegionSize);
-
- dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n",
- dev->hwdesc.dwHostHibernatMemRegion);
-
- dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n",
- dev->hwdesc.dwHostHibernatMemRegionSize);
-}
-
-static void saa7164_dump_intfdesc(struct saa7164_dev *dev)
-{
- dprintk(1, "@0x%p intfdesc "
- "sizeof(struct tmComResInterfaceDescr) = %d bytes\n",
- &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr));
-
- dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength);
- dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType);
- dprintk(1, " .bDescriptorSubtype = 0x%x\n",
- dev->intfdesc.bDescriptorSubtype);
-
- dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags);
- dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType);
- dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId);
- dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface);
- dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId);
- dprintk(1, " .bDebugInterruptId = 0x%x\n",
- dev->intfdesc.bDebugInterruptId);
-
- dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation);
-}
-
-static void saa7164_dump_busdesc(struct saa7164_dev *dev)
-{
- dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n",
- &dev->busdesc, (u32)sizeof(struct tmComResBusDescr));
-
- dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing);
- dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing);
- dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite);
- dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead);
- dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite);
- dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead);
-}
-
-/* Much of the hardware configuration and PCI registers are configured
- * dynamically depending on firmware. We have to cache some initial
- * structures then use these to locate other important structures
- * from PCI space.
- */
-static void saa7164_get_descriptors(struct saa7164_dev *dev)
-{
- memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr));
- memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr),
- sizeof(struct tmComResInterfaceDescr));
- memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation,
- sizeof(struct tmComResBusDescr));
-
- if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) {
- printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n");
- printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength,
- (u32)sizeof(struct tmComResHWDescr));
- } else
- saa7164_dump_hwdesc(dev);
-
- if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
- printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
- printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
- (u32)sizeof(struct tmComResInterfaceDescr));
- } else
- saa7164_dump_intfdesc(dev);
-
- saa7164_dump_busdesc(dev);
-}
-
-static int saa7164_pci_quirks(struct saa7164_dev *dev)
-{
- return 0;
-}
-
-static int get_resources(struct saa7164_dev *dev)
-{
- if (request_mem_region(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0), dev->name)) {
-
- if (request_mem_region(pci_resource_start(dev->pci, 2),
- pci_resource_len(dev->pci, 2), dev->name))
- return 0;
- }
-
- printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n",
- dev->name,
- (u64)pci_resource_start(dev->pci, 0),
- (u64)pci_resource_start(dev->pci, 2));
-
- return -EBUSY;
-}
-
-static int saa7164_port_init(struct saa7164_dev *dev, int portnr)
-{
- struct saa7164_port *port = NULL;
-
- if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS))
- BUG();
-
- port = &dev->ports[portnr];
-
- port->dev = dev;
- port->nr = portnr;
-
- if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2))
- port->type = SAA7164_MPEG_DVB;
- else
- if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) {
- port->type = SAA7164_MPEG_ENCODER;
-
- /* We need a deferred interrupt handler for cmd handling */
- INIT_WORK(&port->workenc, saa7164_work_enchandler);
- } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) {
- port->type = SAA7164_MPEG_VBI;
-
- /* We need a deferred interrupt handler for cmd handling */
- INIT_WORK(&port->workenc, saa7164_work_vbihandler);
- } else
- BUG();
-
- /* Init all the critical resources */
- mutex_init(&port->dvb.lock);
- INIT_LIST_HEAD(&port->dmaqueue.list);
- mutex_init(&port->dmaqueue_lock);
-
- INIT_LIST_HEAD(&port->list_buf_used.list);
- INIT_LIST_HEAD(&port->list_buf_free.list);
- init_waitqueue_head(&port->wait_read);
-
-
- saa7164_histogram_reset(&port->irq_interval, "irq intervals");
- saa7164_histogram_reset(&port->svc_interval, "deferred intervals");
- saa7164_histogram_reset(&port->irq_svc_interval,
- "irq to deferred intervals");
- saa7164_histogram_reset(&port->read_interval,
- "encoder/vbi read() intervals");
- saa7164_histogram_reset(&port->poll_interval,
- "encoder/vbi poll() intervals");
-
- return 0;
-}
-
-static int saa7164_dev_setup(struct saa7164_dev *dev)
-{
- int i;
-
- mutex_init(&dev->lock);
- atomic_inc(&dev->refcount);
- dev->nr = saa7164_devcount++;
-
- snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr);
-
- mutex_lock(&devlist);
- list_add_tail(&dev->devlist, &saa7164_devlist);
- mutex_unlock(&devlist);
-
- /* board config */
- dev->board = UNSET;
- if (card[dev->nr] < saa7164_bcount)
- dev->board = card[dev->nr];
-
- for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++)
- if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor &&
- dev->pci->subsystem_device ==
- saa7164_subids[i].subdevice)
- dev->board = saa7164_subids[i].card;
-
- if (UNSET == dev->board) {
- dev->board = SAA7164_BOARD_UNKNOWN;
- saa7164_card_list(dev);
- }
-
- dev->pci_bus = dev->pci->bus->number;
- dev->pci_slot = PCI_SLOT(dev->pci->devfn);
-
- /* I2C Defaults / setup */
- dev->i2c_bus[0].dev = dev;
- dev->i2c_bus[0].nr = 0;
- dev->i2c_bus[1].dev = dev;
- dev->i2c_bus[1].nr = 1;
- dev->i2c_bus[2].dev = dev;
- dev->i2c_bus[2].nr = 2;
-
- /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */
- saa7164_port_init(dev, SAA7164_PORT_TS1);
- saa7164_port_init(dev, SAA7164_PORT_TS2);
- saa7164_port_init(dev, SAA7164_PORT_ENC1);
- saa7164_port_init(dev, SAA7164_PORT_ENC2);
- saa7164_port_init(dev, SAA7164_PORT_VBI1);
- saa7164_port_init(dev, SAA7164_PORT_VBI2);
-
- if (get_resources(dev) < 0) {
- printk(KERN_ERR "CORE %s No more PCIe resources for "
- "subsystem: %04x:%04x\n",
- dev->name, dev->pci->subsystem_vendor,
- dev->pci->subsystem_device);
-
- saa7164_devcount--;
- return -ENODEV;
- }
-
- /* PCI/e allocations */
- dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
-
- dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2),
- pci_resource_len(dev->pci, 2));
-
- dev->bmmio = (u8 __iomem *)dev->lmmio;
- dev->bmmio2 = (u8 __iomem *)dev->lmmio2;
-
- /* Inerrupt and ack register locations offset of bmmio */
- dev->int_status = 0x183000 + 0xf80;
- dev->int_ack = 0x183000 + 0xf90;
-
- printk(KERN_INFO
- "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
- dev->name, dev->pci->subsystem_vendor,
- dev->pci->subsystem_device, saa7164_boards[dev->board].name,
- dev->board, card[dev->nr] == dev->board ?
- "insmod option" : "autodetected");
-
- saa7164_pci_quirks(dev);
-
- return 0;
-}
-
-static void saa7164_dev_unregister(struct saa7164_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-
- release_mem_region(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
-
- release_mem_region(pci_resource_start(dev->pci, 2),
- pci_resource_len(dev->pci, 2));
-
- if (!atomic_dec_and_test(&dev->refcount))
- return;
-
- iounmap(dev->lmmio);
- iounmap(dev->lmmio2);
-
- return;
-}
-
-#ifdef CONFIG_PROC_FS
-static int saa7164_proc_show(struct seq_file *m, void *v)
-{
- struct saa7164_dev *dev;
- struct tmComResBusInfo *b;
- struct list_head *list;
- int i, c;
-
- if (saa7164_devcount == 0)
- return 0;
-
- list_for_each(list, &saa7164_devlist) {
- dev = list_entry(list, struct saa7164_dev, devlist);
- seq_printf(m, "%s = %p\n", dev->name, dev);
-
- /* Lock the bus from any other access */
- b = &dev->bus;
- mutex_lock(&b->lock);
-
- seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n",
- b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos));
-
- seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n",
- b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos));
-
- seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n",
- b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos));
-
- seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n",
- b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos));
- c = 0;
- seq_printf(m, "\n Set Ring:\n");
- seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
- for (i = 0; i < b->m_dwSizeSetRing; i++) {
- if (c == 0)
- seq_printf(m, " %04x:", i);
-
- seq_printf(m, " %02x", *(b->m_pdwSetRing + i));
-
- if (++c == 16) {
- seq_printf(m, "\n");
- c = 0;
- }
- }
-
- c = 0;
- seq_printf(m, "\n Get Ring:\n");
- seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
- for (i = 0; i < b->m_dwSizeGetRing; i++) {
- if (c == 0)
- seq_printf(m, " %04x:", i);
-
- seq_printf(m, " %02x", *(b->m_pdwGetRing + i));
-
- if (++c == 16) {
- seq_printf(m, "\n");
- c = 0;
- }
- }
-
- mutex_unlock(&b->lock);
-
- }
-
- return 0;
-}
-
-static int saa7164_proc_open(struct inode *inode, struct file *filp)
-{
- return single_open(filp, saa7164_proc_show, NULL);
-}
-
-static const struct file_operations saa7164_proc_fops = {
- .open = saa7164_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int saa7164_proc_create(void)
-{
- struct proc_dir_entry *pe;
-
- pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops);
- if (!pe)
- return -ENOMEM;
-
- return 0;
-}
-#endif
-
-static int saa7164_thread_function(void *data)
-{
- struct saa7164_dev *dev = data;
- struct tmFwInfoStruct fwinfo;
- u64 last_poll_time = 0;
-
- dprintk(DBGLVL_THR, "thread started\n");
-
- set_freezable();
-
- while (1) {
- msleep_interruptible(100);
- if (kthread_should_stop())
- break;
- try_to_freeze();
-
- dprintk(DBGLVL_THR, "thread running\n");
-
- /* Dump the firmware debug message to console */
- /* Polling this costs us 1-2% of the arm CPU */
- /* convert this into a respnde to interrupt 0x7a */
- saa7164_api_collect_debug(dev);
-
- /* Monitor CPU load every 1 second */
- if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) {
- saa7164_api_get_load_info(dev, &fwinfo);
- last_poll_time = jiffies_to_msecs(jiffies);
- }
-
- }
-
- dprintk(DBGLVL_THR, "thread exiting\n");
- return 0;
-}
-
-static int __devinit saa7164_initdev(struct pci_dev *pci_dev,
- const struct pci_device_id *pci_id)
-{
- struct saa7164_dev *dev;
- int err, i;
- u32 version;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (NULL == dev)
- return -ENOMEM;
-
- /* pci init */
- dev->pci = pci_dev;
- if (pci_enable_device(pci_dev)) {
- err = -EIO;
- goto fail_free;
- }
-
- if (saa7164_dev_setup(dev) < 0) {
- err = -EINVAL;
- goto fail_free;
- }
-
- /* print pci info */
- dev->pci_rev = pci_dev->revision;
- pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
- printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%llx\n", dev->name,
- pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
- dev->pci_lat,
- (unsigned long long)pci_resource_start(pci_dev, 0));
-
- pci_set_master(pci_dev);
- /* TODO */
- if (!pci_dma_supported(pci_dev, 0xffffffff)) {
- printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
- err = -EIO;
- goto fail_irq;
- }
-
- err = request_irq(pci_dev->irq, saa7164_irq,
- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
- if (err < 0) {
- printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name,
- pci_dev->irq);
- err = -EIO;
- goto fail_irq;
- }
-
- pci_set_drvdata(pci_dev, dev);
-
- /* Init the internal command list */
- for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
- dev->cmds[i].seqno = i;
- dev->cmds[i].inuse = 0;
- mutex_init(&dev->cmds[i].lock);
- init_waitqueue_head(&dev->cmds[i].wait);
- }
-
- /* We need a deferred interrupt handler for cmd handling */
- INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler);
-
- /* Only load the firmware if we know the board */
- if (dev->board != SAA7164_BOARD_UNKNOWN) {
-
- err = saa7164_downloadfirmware(dev);
- if (err < 0) {
- printk(KERN_ERR
- "Failed to boot firmware, no features "
- "registered\n");
- goto fail_fw;
- }
-
- saa7164_get_descriptors(dev);
- saa7164_dumpregs(dev, 0);
- saa7164_getcurrentfirmwareversion(dev);
- saa7164_getfirmwarestatus(dev);
- err = saa7164_bus_setup(dev);
- if (err < 0)
- printk(KERN_ERR
- "Failed to setup the bus, will continue\n");
- saa7164_bus_dump(dev);
-
- /* Ping the running firmware via the command bus and get the
- * firmware version, this checks the bus is running OK.
- */
- version = 0;
- if (saa7164_api_get_fw_version(dev, &version) == SAA_OK)
- dprintk(1, "Bus is operating correctly using "
- "version %d.%d.%d.%d (0x%x)\n",
- (version & 0x0000fc00) >> 10,
- (version & 0x000003e0) >> 5,
- (version & 0x0000001f),
- (version & 0xffff0000) >> 16,
- version);
- else
- printk(KERN_ERR
- "Failed to communicate with the firmware\n");
-
- /* Bring up the I2C buses */
- saa7164_i2c_register(&dev->i2c_bus[0]);
- saa7164_i2c_register(&dev->i2c_bus[1]);
- saa7164_i2c_register(&dev->i2c_bus[2]);
- saa7164_gpio_setup(dev);
- saa7164_card_setup(dev);
-
- /* Parse the dynamic device configuration, find various
- * media endpoints (MPEG, WMV, PS, TS) and cache their
- * configuration details into the driver, so we can
- * reference them later during simething_register() func,
- * interrupt handlers, deferred work handlers etc.
- */
- saa7164_api_enum_subdevs(dev);
-
- /* Begin to create the video sub-systems and register funcs */
- if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) {
- if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) {
- printk(KERN_ERR "%s() Failed to register "
- "dvb adapters on porta\n",
- __func__);
- }
- }
-
- if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) {
- if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) {
- printk(KERN_ERR"%s() Failed to register "
- "dvb adapters on portb\n",
- __func__);
- }
- }
-
- if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) {
- if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) {
- printk(KERN_ERR"%s() Failed to register "
- "mpeg encoder\n", __func__);
- }
- }
-
- if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) {
- if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) {
- printk(KERN_ERR"%s() Failed to register "
- "mpeg encoder\n", __func__);
- }
- }
-
- if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) {
- if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) {
- printk(KERN_ERR"%s() Failed to register "
- "vbi device\n", __func__);
- }
- }
-
- if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) {
- if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) {
- printk(KERN_ERR"%s() Failed to register "
- "vbi device\n", __func__);
- }
- }
- saa7164_api_set_debug(dev, fw_debug);
-
- if (fw_debug) {
- dev->kthread = kthread_run(saa7164_thread_function, dev,
- "saa7164 debug");
- if (!dev->kthread)
- printk(KERN_ERR "%s() Failed to create "
- "debug kernel thread\n", __func__);
- }
-
- } /* != BOARD_UNKNOWN */
- else
- printk(KERN_ERR "%s() Unsupported board detected, "
- "registering without firmware\n", __func__);
-
- dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug);
- dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs);
-
-fail_fw:
- return 0;
-
-fail_irq:
- saa7164_dev_unregister(dev);
-fail_free:
- kfree(dev);
- return err;
-}
-
-static void saa7164_shutdown(struct saa7164_dev *dev)
-{
- dprintk(1, "%s()\n", __func__);
-}
-
-static void __devexit saa7164_finidev(struct pci_dev *pci_dev)
-{
- struct saa7164_dev *dev = pci_get_drvdata(pci_dev);
-
- if (dev->board != SAA7164_BOARD_UNKNOWN) {
- if (fw_debug && dev->kthread) {
- kthread_stop(dev->kthread);
- dev->kthread = NULL;
- }
- if (dev->firmwareloaded)
- saa7164_api_set_debug(dev, 0x00);
- }
-
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
- &dev->ports[SAA7164_PORT_ENC1].irq_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
- &dev->ports[SAA7164_PORT_ENC1].svc_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
- &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
- &dev->ports[SAA7164_PORT_ENC1].read_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1],
- &dev->ports[SAA7164_PORT_ENC1].poll_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1],
- &dev->ports[SAA7164_PORT_VBI1].read_interval);
- saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2],
- &dev->ports[SAA7164_PORT_VBI2].poll_interval);
-
- saa7164_shutdown(dev);
-
- if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB)
- saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]);
-
- if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB)
- saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]);
-
- if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER)
- saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]);
-
- if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER)
- saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]);
-
- if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI)
- saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]);
-
- if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI)
- saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]);
-
- saa7164_i2c_unregister(&dev->i2c_bus[0]);
- saa7164_i2c_unregister(&dev->i2c_bus[1]);
- saa7164_i2c_unregister(&dev->i2c_bus[2]);
-
- pci_disable_device(pci_dev);
-
- /* unregister stuff */
- free_irq(pci_dev->irq, dev);
- pci_set_drvdata(pci_dev, NULL);
-
- mutex_lock(&devlist);
- list_del(&dev->devlist);
- mutex_unlock(&devlist);
-
- saa7164_dev_unregister(dev);
- kfree(dev);
-}
-
-static struct pci_device_id saa7164_pci_tbl[] = {
- {
- /* SAA7164 */
- .vendor = 0x1131,
- .device = 0x7164,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
- }, {
- /* --- end of list --- */
- }
-};
-MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl);
-
-static struct pci_driver saa7164_pci_driver = {
- .name = "saa7164",
- .id_table = saa7164_pci_tbl,
- .probe = saa7164_initdev,
- .remove = __devexit_p(saa7164_finidev),
- /* TODO */
- .suspend = NULL,
- .resume = NULL,
-};
-
-static int __init saa7164_init(void)
-{
- printk(KERN_INFO "saa7164 driver loaded\n");
-
-#ifdef CONFIG_PROC_FS
- saa7164_proc_create();
-#endif
- return pci_register_driver(&saa7164_pci_driver);
-}
-
-static void __exit saa7164_fini(void)
-{
-#ifdef CONFIG_PROC_FS
- remove_proc_entry("saa7164", NULL);
-#endif
- pci_unregister_driver(&saa7164_pci_driver);
-}
-
-module_init(saa7164_init);
-module_exit(saa7164_fini);
-
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
deleted file mode 100644
index 35219b9b0fbc..000000000000
--- a/drivers/media/video/saa7164/saa7164.h
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * Driver for the NXP SAA7164 PCIe bridge
- *
- * Copyright (c) 2010 Steven Toth <stoth@kernellabs.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- *
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- Driver architecture
- *******************
-
- saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c
- | : Standard Linux driver framework for creating
- | : exposing and managing interfaces to the rest
- | : of the kernel or userland. Also uses _fw.c to load
- | : firmware direct into the PCIe bus, bypassing layers.
- V
- saa7164_api..() : Translate kernel specific functions/features
- | : into command buffers.
- V
- saa7164_cmd..() : Manages the flow of command packets on/off,
- | : the bus. Deal with bus errors, timeouts etc.
- V
- saa7164_bus..() : Manage a read/write memory ring buffer in the
- | : PCIe Address space.
- |
- | saa7164_fw...() : Load any frimware
- | | : direct into the device
- V V
- <- ----------------- PCIe address space -------------------- ->
-*/
-
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/kdev_t.h>
-#include <linux/mutex.h>
-#include <linux/crc32.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-
-#include <media/tuner.h>
-#include <media/tveeprom.h>
-#include <media/videobuf-dma-sg.h>
-#include <media/videobuf-dvb.h>
-#include <dvb_demux.h>
-#include <dvb_frontend.h>
-#include <dvb_net.h>
-#include <dvbdev.h>
-#include <dmxdev.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-chip-ident.h>
-
-#include "saa7164-reg.h"
-#include "saa7164-types.h"
-
-#define SAA7164_MAXBOARDS 8
-
-#define UNSET (-1U)
-#define SAA7164_BOARD_NOAUTO UNSET
-#define SAA7164_BOARD_UNKNOWN 0
-#define SAA7164_BOARD_UNKNOWN_REV2 1
-#define SAA7164_BOARD_UNKNOWN_REV3 2
-#define SAA7164_BOARD_HAUPPAUGE_HVR2250 3
-#define SAA7164_BOARD_HAUPPAUGE_HVR2200 4
-#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5
-#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6
-#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7
-#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8
-#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9
-#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10
-
-#define SAA7164_MAX_UNITS 8
-#define SAA7164_TS_NUMBER_OF_LINES 312
-#define SAA7164_PS_NUMBER_OF_LINES 256
-#define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */
-#define SAA7164_MAX_ENCODER_BUFFERS 64 /* max 5secs of latency at 6Mbps */
-#define SAA7164_MAX_VBI_BUFFERS 64
-
-/* Port related defines */
-#define SAA7164_PORT_TS1 (0)
-#define SAA7164_PORT_TS2 (SAA7164_PORT_TS1 + 1)
-#define SAA7164_PORT_ENC1 (SAA7164_PORT_TS2 + 1)
-#define SAA7164_PORT_ENC2 (SAA7164_PORT_ENC1 + 1)
-#define SAA7164_PORT_VBI1 (SAA7164_PORT_ENC2 + 1)
-#define SAA7164_PORT_VBI2 (SAA7164_PORT_VBI1 + 1)
-#define SAA7164_MAX_PORTS (SAA7164_PORT_VBI2 + 1)
-
-#define DBGLVL_FW 4
-#define DBGLVL_DVB 8
-#define DBGLVL_I2C 16
-#define DBGLVL_API 32
-#define DBGLVL_CMD 64
-#define DBGLVL_BUS 128
-#define DBGLVL_IRQ 256
-#define DBGLVL_BUF 512
-#define DBGLVL_ENC 1024
-#define DBGLVL_VBI 2048
-#define DBGLVL_THR 4096
-#define DBGLVL_CPU 8192
-
-#define SAA7164_NORMS \
- (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443)
-
-enum port_t {
- SAA7164_MPEG_UNDEFINED = 0,
- SAA7164_MPEG_DVB,
- SAA7164_MPEG_ENCODER,
- SAA7164_MPEG_VBI,
-};
-
-enum saa7164_i2c_bus_nr {
- SAA7164_I2C_BUS_0 = 0,
- SAA7164_I2C_BUS_1,
- SAA7164_I2C_BUS_2,
-};
-
-enum saa7164_buffer_flags {
- SAA7164_BUFFER_UNDEFINED = 0,
- SAA7164_BUFFER_FREE,
- SAA7164_BUFFER_BUSY,
- SAA7164_BUFFER_FULL
-};
-
-enum saa7164_unit_type {
- SAA7164_UNIT_UNDEFINED = 0,
- SAA7164_UNIT_DIGITAL_DEMODULATOR,
- SAA7164_UNIT_ANALOG_DEMODULATOR,
- SAA7164_UNIT_TUNER,
- SAA7164_UNIT_EEPROM,
- SAA7164_UNIT_ZILOG_IRBLASTER,
- SAA7164_UNIT_ENCODER,
-};
-
-/* The PCIe bridge doesn't grant direct access to i2c.
- * Instead, you address i2c devices using a uniqely
- * allocated 'unitid' value via a messaging API. This
- * is a problem. The kernel and existing demod/tuner
- * drivers expect to talk 'i2c', so we have to maintain
- * a translation layer, and a series of functions to
- * convert i2c bus + device address into a unit id.
- */
-struct saa7164_unit {
- enum saa7164_unit_type type;
- u8 id;
- char *name;
- enum saa7164_i2c_bus_nr i2c_bus_nr;
- u8 i2c_bus_addr;
- u8 i2c_reg_len;
-};
-
-struct saa7164_board {
- char *name;
- enum port_t porta, portb, portc,
- portd, porte, portf;
- enum {
- SAA7164_CHIP_UNDEFINED = 0,
- SAA7164_CHIP_REV2,
- SAA7164_CHIP_REV3,
- } chiprev;
- struct saa7164_unit unit[SAA7164_MAX_UNITS];
-};
-
-struct saa7164_subid {
- u16 subvendor;
- u16 subdevice;
- u32 card;
-};
-
-struct saa7164_encoder_fh {
- struct saa7164_port *port;
- atomic_t v4l_reading;
-};
-
-struct saa7164_vbi_fh {
- struct saa7164_port *port;
- atomic_t v4l_reading;
-};
-
-struct saa7164_histogram_bucket {
- u32 val;
- u32 count;
- u64 update_time;
-};
-
-struct saa7164_histogram {
- char name[32];
- struct saa7164_histogram_bucket counter1[64];
-};
-
-struct saa7164_user_buffer {
- struct list_head list;
-
- /* Attributes */
- u8 *data;
- u32 pos;
- u32 actual_size;
-
- u32 crc;
-};
-
-struct saa7164_fw_status {
-
- /* RISC Core details */
- u32 status;
- u32 mode;
- u32 spec;
- u32 inst;
- u32 cpuload;
- u32 remainheap;
-
- /* Firmware version */
- u32 version;
- u32 major;
- u32 sub;
- u32 rel;
- u32 buildnr;
-};
-
-struct saa7164_dvb {
- struct mutex lock;
- struct dvb_adapter adapter;
- struct dvb_frontend *frontend;
- struct dvb_demux demux;
- struct dmxdev dmxdev;
- struct dmx_frontend fe_hw;
- struct dmx_frontend fe_mem;
- struct dvb_net net;
- int feeding;
-};
-
-struct saa7164_i2c {
- struct saa7164_dev *dev;
-
- enum saa7164_i2c_bus_nr nr;
-
- /* I2C I/O */
- struct i2c_adapter i2c_adap;
- struct i2c_client i2c_client;
- u32 i2c_rc;
-};
-
-struct saa7164_ctrl {
- struct v4l2_queryctrl v;
-};
-
-struct saa7164_tvnorm {
- char *name;
- v4l2_std_id id;
-};
-
-struct saa7164_encoder_params {
- struct saa7164_tvnorm encodernorm;
- u32 height;
- u32 width;
- u32 is_50hz;
- u32 bitrate; /* bps */
- u32 bitrate_peak; /* bps */
- u32 bitrate_mode;
- u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
-
- u32 audio_sampling_freq;
- u32 ctl_mute;
- u32 ctl_aspect;
- u32 refdist;
- u32 gop_size;
-};
-
-struct saa7164_vbi_params {
- struct saa7164_tvnorm encodernorm;
- u32 height;
- u32 width;
- u32 is_50hz;
- u32 bitrate; /* bps */
- u32 bitrate_peak; /* bps */
- u32 bitrate_mode;
- u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */
-
- u32 audio_sampling_freq;
- u32 ctl_mute;
- u32 ctl_aspect;
- u32 refdist;
- u32 gop_size;
-};
-
-struct saa7164_port;
-
-struct saa7164_buffer {
- struct list_head list;
-
- /* Note of which h/w buffer list index position we occupy */
- int idx;
-
- struct saa7164_port *port;
-
- /* Hardware Specific */
- /* PCI Memory allocations */
- enum saa7164_buffer_flags flags; /* Free, Busy, Full */
-
- /* A block of page align PCI memory */
- u32 pci_size; /* PCI allocation size in bytes */
- u64 __iomem *cpu; /* Virtual address */
- dma_addr_t dma; /* Physical address */
- u32 crc; /* Checksum for the entire buffer data */
-
- /* A page table that splits the block into a number of entries */
- u32 pt_size; /* PCI allocation size in bytes */
- u64 __iomem *pt_cpu; /* Virtual address */
- dma_addr_t pt_dma; /* Physical address */
-
- /* Encoder fops */
- u32 pos;
- u32 actual_size;
-};
-
-struct saa7164_port {
-
- struct saa7164_dev *dev;
- enum port_t type;
- int nr;
-
- /* --- Generic port attributes --- */
-
- /* HW stream parameters */
- struct tmHWStreamParameters hw_streamingparams;
-
- /* DMA configuration values, is seeded during initialization */
- struct tmComResDMATermDescrHeader hwcfg;
-
- /* hardware specific registers */
- u32 bufcounter;
- u32 pitch;
- u32 bufsize;
- u32 bufoffset;
- u32 bufptr32l;
- u32 bufptr32h;
- u64 bufptr64;
-
- u32 numpte; /* Number of entries in array, only valid in head */
-
- struct mutex dmaqueue_lock;
- struct saa7164_buffer dmaqueue;
-
- u64 last_irq_msecs, last_svc_msecs;
- u64 last_irq_msecs_diff, last_svc_msecs_diff;
- u32 last_svc_wp;
- u32 last_svc_rp;
- u64 last_irq_svc_msecs_diff;
- u64 last_read_msecs, last_read_msecs_diff;
- u64 last_poll_msecs, last_poll_msecs_diff;
-
- struct saa7164_histogram irq_interval;
- struct saa7164_histogram svc_interval;
- struct saa7164_histogram irq_svc_interval;
- struct saa7164_histogram read_interval;
- struct saa7164_histogram poll_interval;
-
- /* --- DVB Transport Specific --- */
- struct saa7164_dvb dvb;
-
- /* --- Encoder/V4L related attributes --- */
- /* Encoder */
- /* Defaults established in saa7164-encoder.c */
- struct saa7164_tvnorm encodernorm;
- u32 height;
- u32 width;
- u32 freq;
- u32 ts_packet_size;
- u32 ts_packet_count;
- u8 mux_input;
- u8 encoder_profile;
- u8 video_format;
- u8 audio_format;
- u8 video_resolution;
- u16 ctl_brightness;
- u16 ctl_contrast;
- u16 ctl_hue;
- u16 ctl_saturation;
- u16 ctl_sharpness;
- s8 ctl_volume;
-
- struct tmComResAFeatureDescrHeader audfeat;
- struct tmComResEncoderDescrHeader encunit;
- struct tmComResProcDescrHeader vidproc;
- struct tmComResExtDevDescrHeader ifunit;
- struct tmComResTunerDescrHeader tunerunit;
-
- struct work_struct workenc;
-
- /* V4L Encoder Video */
- struct saa7164_encoder_params encoder_params;
- struct video_device *v4l_device;
- atomic_t v4l_reader_count;
-
- struct saa7164_buffer list_buf_used;
- struct saa7164_buffer list_buf_free;
- wait_queue_head_t wait_read;
-
- /* V4L VBI */
- struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc;
- struct saa7164_vbi_params vbi_params;
-
- /* Debug */
- u32 sync_errors;
- u32 v_cc_errors;
- u32 a_cc_errors;
- u8 last_v_cc;
- u8 last_a_cc;
- u32 done_first_interrupt;
-};
-
-struct saa7164_dev {
- struct list_head devlist;
- atomic_t refcount;
-
- /* pci stuff */
- struct pci_dev *pci;
- unsigned char pci_rev, pci_lat;
- int pci_bus, pci_slot;
- u32 __iomem *lmmio;
- u8 __iomem *bmmio;
- u32 __iomem *lmmio2;
- u8 __iomem *bmmio2;
- int pci_irqmask;
-
- /* board details */
- int nr;
- int hwrevision;
- u32 board;
- char name[16];
-
- /* firmware status */
- struct saa7164_fw_status fw_status;
- u32 firmwareloaded;
-
- struct tmComResHWDescr hwdesc;
- struct tmComResInterfaceDescr intfdesc;
- struct tmComResBusDescr busdesc;
-
- struct tmComResBusInfo bus;
-
- /* Interrupt status and ack registers */
- u32 int_status;
- u32 int_ack;
-
- struct cmd cmds[SAA_CMD_MAX_MSG_UNITS];
- struct mutex lock;
-
- /* I2c related */
- struct saa7164_i2c i2c_bus[3];
-
- /* Transport related */
- struct saa7164_port ports[SAA7164_MAX_PORTS];
-
- /* Deferred command/api interrupts handling */
- struct work_struct workcmd;
-
- /* A kernel thread to monitor the firmware log, used
- * only in debug mode.
- */
- struct task_struct *kthread;
-
-};
-
-extern struct list_head saa7164_devlist;
-extern unsigned int waitsecs;
-extern unsigned int encoder_buffers;
-extern unsigned int vbi_buffers;
-
-/* ----------------------------------------------------------- */
-/* saa7164-core.c */
-void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr);
-void saa7164_dumphex16(struct saa7164_dev *dev, u8 *buf, int len);
-void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
-u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
-void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
-
-/* ----------------------------------------------------------- */
-/* saa7164-fw.c */
-int saa7164_downloadfirmware(struct saa7164_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* saa7164-i2c.c */
-extern int saa7164_i2c_register(struct saa7164_i2c *bus);
-extern int saa7164_i2c_unregister(struct saa7164_i2c *bus);
-extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus,
- unsigned int cmd, void *arg);
-
-/* ----------------------------------------------------------- */
-/* saa7164-bus.c */
-int saa7164_bus_setup(struct saa7164_dev *dev);
-void saa7164_bus_dump(struct saa7164_dev *dev);
-int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg,
- void *buf);
-int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
- void *buf, int peekonly);
-
-/* ----------------------------------------------------------- */
-/* saa7164-cmd.c */
-int saa7164_cmd_send(struct saa7164_dev *dev,
- u8 id, enum tmComResCmd command, u16 controlselector,
- u16 size, void *buf);
-void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
-int saa7164_irq_dequeue(struct saa7164_dev *dev);
-
-/* ----------------------------------------------------------- */
-/* saa7164-api.c */
-int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version);
-int saa7164_api_enum_subdevs(struct saa7164_dev *dev);
-int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg,
- u32 datalen, u8 *data);
-int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr,
- u32 datalen, u8 *data);
-int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr,
- u32 datalen, u8 *data);
-int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen);
-int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
-int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin);
-int saa7164_api_transition_port(struct saa7164_port *port, u8 mode);
-int saa7164_api_initialize_dif(struct saa7164_port *port);
-int saa7164_api_configure_dif(struct saa7164_port *port, u32 std);
-int saa7164_api_set_encoder(struct saa7164_port *port);
-int saa7164_api_get_encoder(struct saa7164_port *port);
-int saa7164_api_set_aspect_ratio(struct saa7164_port *port);
-int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl);
-int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl);
-int saa7164_api_set_videomux(struct saa7164_port *port);
-int saa7164_api_audio_mute(struct saa7164_port *port, int mute);
-int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level);
-int saa7164_api_set_audio_std(struct saa7164_port *port);
-int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect);
-int saa7164_api_get_videomux(struct saa7164_port *port);
-int saa7164_api_set_vbi_format(struct saa7164_port *port);
-int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level);
-int saa7164_api_collect_debug(struct saa7164_dev *dev);
-int saa7164_api_get_load_info(struct saa7164_dev *dev,
- struct tmFwInfoStruct *i);
-
-/* ----------------------------------------------------------- */
-/* saa7164-cards.c */
-extern struct saa7164_board saa7164_boards[];
-extern const unsigned int saa7164_bcount;
-
-extern struct saa7164_subid saa7164_subids[];
-extern const unsigned int saa7164_idcount;
-
-extern void saa7164_card_list(struct saa7164_dev *dev);
-extern void saa7164_gpio_setup(struct saa7164_dev *dev);
-extern void saa7164_card_setup(struct saa7164_dev *dev);
-
-extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr);
-extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr);
-extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid);
-
-/* ----------------------------------------------------------- */
-/* saa7164-dvb.c */
-extern int saa7164_dvb_register(struct saa7164_port *port);
-extern int saa7164_dvb_unregister(struct saa7164_port *port);
-
-/* ----------------------------------------------------------- */
-/* saa7164-buffer.c */
-extern struct saa7164_buffer *saa7164_buffer_alloc(
- struct saa7164_port *port, u32 len);
-extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
-extern void saa7164_buffer_display(struct saa7164_buffer *buf);
-extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
-extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
-extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
- struct saa7164_dev *dev, u32 len);
-extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf);
-extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i);
-
-/* ----------------------------------------------------------- */
-/* saa7164-encoder.c */
-int saa7164_encoder_register(struct saa7164_port *port);
-void saa7164_encoder_unregister(struct saa7164_port *port);
-
-/* ----------------------------------------------------------- */
-/* saa7164-vbi.c */
-int saa7164_vbi_register(struct saa7164_port *port);
-void saa7164_vbi_unregister(struct saa7164_port *port);
-
-/* ----------------------------------------------------------- */
-
-extern unsigned int crc_checking;
-
-extern unsigned int saa_debug;
-#define dprintk(level, fmt, arg...)\
- do { if (saa_debug & level)\
- printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\
- } while (0)
-
-#define log_warn(fmt, arg...)\
- do { \
- printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\
- } while (0)
-
-#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
-#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
-
-#define saa7164_readb(reg) readl(dev->bmmio + (reg))
-#define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg))
-
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
deleted file mode 100644
index 0baaf94db7e0..000000000000
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ /dev/null
@@ -1,2331 +0,0 @@
-/*
- * V4L2 Driver for SuperH Mobile CEU interface
- *
- * Copyright (C) 2008 Magnus Damm
- *
- * Based on V4L2 Driver for PXA camera host - "pxa_camera.c",
- *
- * Copyright (C) 2006, Sascha Hauer, Pengutronix
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.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.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/moduleparam.h>
-#include <linux/time.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <linux/pm_runtime.h>
-#include <linux/sched.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
-#include <media/soc_camera.h>
-#include <media/sh_mobile_ceu.h>
-#include <media/sh_mobile_csi2.h>
-#include <media/videobuf2-dma-contig.h>
-#include <media/v4l2-mediabus.h>
-#include <media/soc_mediabus.h>
-
-/* register offsets for sh7722 / sh7723 */
-
-#define CAPSR 0x00 /* Capture start register */
-#define CAPCR 0x04 /* Capture control register */
-#define CAMCR 0x08 /* Capture interface control register */
-#define CMCYR 0x0c /* Capture interface cycle register */
-#define CAMOR 0x10 /* Capture interface offset register */
-#define CAPWR 0x14 /* Capture interface width register */
-#define CAIFR 0x18 /* Capture interface input format register */
-#define CSTCR 0x20 /* Camera strobe control register (<= sh7722) */
-#define CSECR 0x24 /* Camera strobe emission count register (<= sh7722) */
-#define CRCNTR 0x28 /* CEU register control register */
-#define CRCMPR 0x2c /* CEU register forcible control register */
-#define CFLCR 0x30 /* Capture filter control register */
-#define CFSZR 0x34 /* Capture filter size clip register */
-#define CDWDR 0x38 /* Capture destination width register */
-#define CDAYR 0x3c /* Capture data address Y register */
-#define CDACR 0x40 /* Capture data address C register */
-#define CDBYR 0x44 /* Capture data bottom-field address Y register */
-#define CDBCR 0x48 /* Capture data bottom-field address C register */
-#define CBDSR 0x4c /* Capture bundle destination size register */
-#define CFWCR 0x5c /* Firewall operation control register */
-#define CLFCR 0x60 /* Capture low-pass filter control register */
-#define CDOCR 0x64 /* Capture data output control register */
-#define CDDCR 0x68 /* Capture data complexity level register */
-#define CDDAR 0x6c /* Capture data complexity level address register */
-#define CEIER 0x70 /* Capture event interrupt enable register */
-#define CETCR 0x74 /* Capture event flag clear register */
-#define CSTSR 0x7c /* Capture status register */
-#define CSRTR 0x80 /* Capture software reset register */
-#define CDSSR 0x84 /* Capture data size register */
-#define CDAYR2 0x90 /* Capture data address Y register 2 */
-#define CDACR2 0x94 /* Capture data address C register 2 */
-#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
-#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
-
-#undef DEBUG_GEOMETRY
-#ifdef DEBUG_GEOMETRY
-#define dev_geo dev_info
-#else
-#define dev_geo dev_dbg
-#endif
-
-/* per video frame buffer */
-struct sh_mobile_ceu_buffer {
- struct vb2_buffer vb; /* v4l buffer must be first */
- struct list_head queue;
-};
-
-struct sh_mobile_ceu_dev {
- struct soc_camera_host ici;
- struct soc_camera_device *icd;
- struct platform_device *csi2_pdev;
-
- unsigned int irq;
- void __iomem *base;
- size_t video_limit;
- size_t buf_total;
-
- spinlock_t lock; /* Protects video buffer lists */
- struct list_head capture;
- struct vb2_buffer *active;
- struct vb2_alloc_ctx *alloc_ctx;
-
- struct sh_mobile_ceu_info *pdata;
- struct completion complete;
-
- u32 cflcr;
-
- /* static max sizes either from platform data or default */
- int max_width;
- int max_height;
-
- enum v4l2_field field;
- int sequence;
-
- unsigned int image_mode:1;
- unsigned int is_16bit:1;
- unsigned int frozen:1;
-};
-
-struct sh_mobile_ceu_cam {
- /* CEU offsets within the camera output, before the CEU scaler */
- unsigned int ceu_left;
- unsigned int ceu_top;
- /* Client output, as seen by the CEU */
- unsigned int width;
- unsigned int height;
- /*
- * User window from S_CROP / G_CROP, produced by client cropping and
- * scaling, CEU scaling and CEU cropping, mapped back onto the client
- * input window
- */
- struct v4l2_rect subrect;
- /* Camera cropping rectangle */
- struct v4l2_rect rect;
- const struct soc_mbus_pixelfmt *extra_fmt;
- 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 void ceu_write(struct sh_mobile_ceu_dev *priv,
- unsigned long reg_offs, u32 data)
-{
- iowrite32(data, priv->base + reg_offs);
-}
-
-static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs)
-{
- return ioread32(priv->base + reg_offs);
-}
-
-static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev)
-{
- int i, success = 0;
- struct soc_camera_device *icd = pcdev->icd;
-
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
-
- /* wait CSTSR.CPTON bit */
- for (i = 0; i < 1000; i++) {
- if (!(ceu_read(pcdev, CSTSR) & 1)) {
- success++;
- break;
- }
- udelay(1);
- }
-
- /* wait CAPSR.CPKIL bit */
- for (i = 0; i < 1000; i++) {
- if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) {
- success++;
- break;
- }
- udelay(1);
- }
-
-
- if (2 != success) {
- dev_warn(icd->pdev, "soft reset time out\n");
- return -EIO;
- }
-
- return 0;
-}
-
-/*
- * Videobuf operations
- */
-
-/*
- * .queue_setup() is called to check, whether the driver can accept the
- * requested number of buffers and to fill in plane sizes
- * for the current frame format if required
- */
-static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
- const struct v4l2_format *fmt,
- unsigned int *count, unsigned int *num_planes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- if (fmt) {
- const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
- fmt->fmt.pix.pixelformat);
- unsigned int bytes_per_line;
- int ret;
-
- if (!xlate)
- return -EINVAL;
-
- ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
- fmt->fmt.pix.height);
- if (ret < 0)
- return ret;
-
- sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
- } else {
- /* Called from VIDIOC_REQBUFS or in compatibility mode */
- sizes[0] = icd->sizeimage;
- }
-
- alloc_ctxs[0] = pcdev->alloc_ctx;
-
- if (!vq->num_buffers)
- pcdev->sequence = 0;
-
- if (!*count)
- *count = 2;
-
- /* If *num_planes != 0, we have already verified *count. */
- if (pcdev->video_limit && !*num_planes) {
- size_t size = PAGE_ALIGN(sizes[0]) * *count;
-
- if (size + pcdev->buf_total > pcdev->video_limit)
- *count = (pcdev->video_limit - pcdev->buf_total) /
- PAGE_ALIGN(sizes[0]);
- }
-
- *num_planes = 1;
-
- dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
-
- return 0;
-}
-
-#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 */
-#define CEU_CEIER_VBP (1 << 20) /* vbp error */
-#define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */
-#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP)
-
-
-/*
- * return value doesn't reflex the success/failure to queue the new buffer,
- * but rather the status of the previous buffer.
- */
-static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
-{
- struct soc_camera_device *icd = pcdev->icd;
- dma_addr_t phys_addr_top, phys_addr_bottom;
- unsigned long top1, top2;
- unsigned long bottom1, bottom2;
- u32 status;
- bool planar;
- int ret = 0;
-
- /*
- * The hardware is _very_ picky about this sequence. Especially
- * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge
- * several not-so-well documented interrupt sources in CETCR.
- */
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK);
- status = ceu_read(pcdev, CETCR);
- ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC);
- if (!pcdev->frozen)
- ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK);
- ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP);
- ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW);
-
- /*
- * When a VBP interrupt occurs, a capture end interrupt does not occur
- * and the image of that frame is not captured correctly. So, soft reset
- * is needed here.
- */
- if (status & CEU_CEIER_VBP) {
- sh_mobile_ceu_soft_reset(pcdev);
- ret = -EIO;
- }
-
- if (pcdev->frozen) {
- complete(&pcdev->complete);
- return ret;
- }
-
- if (!pcdev->active)
- return ret;
-
- if (V4L2_FIELD_INTERLACED_BT == pcdev->field) {
- top1 = CDBYR;
- top2 = CDBCR;
- bottom1 = CDAYR;
- bottom2 = CDACR;
- } else {
- top1 = CDAYR;
- top2 = CDACR;
- bottom1 = CDBYR;
- bottom2 = CDBCR;
- }
-
- phys_addr_top = vb2_dma_contig_plane_dma_addr(pcdev->active, 0);
-
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- planar = true;
- break;
- default:
- planar = false;
- }
-
- ceu_write(pcdev, top1, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->bytesperline;
- ceu_write(pcdev, bottom1, phys_addr_bottom);
- }
-
- if (planar) {
- phys_addr_top += icd->bytesperline * icd->user_height;
- ceu_write(pcdev, top2, phys_addr_top);
- if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->bytesperline;
- ceu_write(pcdev, bottom2, phys_addr_bottom);
- }
- }
-
- ceu_write(pcdev, CAPSR, 0x1); /* start capture */
-
- return ret;
-}
-
-static int sh_mobile_ceu_videobuf_prepare(struct vb2_buffer *vb)
-{
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
-
- /* Added list head initialization on alloc */
- WARN(!list_empty(&buf->queue), "Buffer %p on queue!\n", vb);
-
- return 0;
-}
-
-static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
-{
- 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->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
- unsigned long size;
-
- size = icd->sizeimage;
-
- if (vb2_plane_size(vb, 0) < size) {
- dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
- vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
- goto error;
- }
-
- vb2_set_plane_payload(vb, 0, size);
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
- vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
-
-#ifdef DEBUG
- /*
- * This can be useful if you want to see if we actually fill
- * the buffer with something
- */
- if (vb2_plane_vaddr(vb, 0))
- memset(vb2_plane_vaddr(vb, 0), 0xaa, vb2_get_plane_payload(vb, 0));
-#endif
-
- spin_lock_irq(&pcdev->lock);
- list_add_tail(&buf->queue, &pcdev->capture);
-
- if (!pcdev->active) {
- /*
- * Because there were no active buffer at this moment,
- * we are not interested in the return value of
- * sh_mobile_ceu_capture here.
- */
- pcdev->active = vb;
- sh_mobile_ceu_capture(pcdev);
- }
- spin_unlock_irq(&pcdev->lock);
-
- return;
-
-error:
- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
-}
-
-static void sh_mobile_ceu_videobuf_release(struct vb2_buffer *vb)
-{
- 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->parent);
- struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- spin_lock_irq(&pcdev->lock);
-
- if (pcdev->active == vb) {
- /* disable capture (release DMA buffer), reset */
- ceu_write(pcdev, CAPSR, 1 << 16);
- pcdev->active = NULL;
- }
-
- /*
- * Doesn't hurt also if the list is empty, but it hurts, if queuing the
- * buffer failed, and .buf_init() hasn't been called
- */
- if (buf->queue.next)
- list_del_init(&buf->queue);
-
- pcdev->buf_total -= PAGE_ALIGN(vb2_plane_size(vb, 0));
- dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
- pcdev->buf_total);
-
- spin_unlock_irq(&pcdev->lock);
-}
-
-static int sh_mobile_ceu_videobuf_init(struct vb2_buffer *vb)
-{
- 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->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- pcdev->buf_total += PAGE_ALIGN(vb2_plane_size(vb, 0));
- dev_dbg(icd->parent, "%s() %zu bytes buffers\n", __func__,
- pcdev->buf_total);
-
- /* This is for locking debugging only */
- INIT_LIST_HEAD(&to_ceu_vb(vb)->queue);
- return 0;
-}
-
-static int sh_mobile_ceu_stop_streaming(struct vb2_queue *q)
-{
- struct soc_camera_device *icd = container_of(q, struct soc_camera_device, vb2_vidq);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct list_head *buf_head, *tmp;
-
- spin_lock_irq(&pcdev->lock);
-
- pcdev->active = NULL;
-
- list_for_each_safe(buf_head, tmp, &pcdev->capture)
- list_del_init(buf_head);
-
- spin_unlock_irq(&pcdev->lock);
-
- return sh_mobile_ceu_soft_reset(pcdev);
-}
-
-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,
- .stop_streaming = sh_mobile_ceu_stop_streaming,
-};
-
-static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
-{
- struct sh_mobile_ceu_dev *pcdev = data;
- struct vb2_buffer *vb;
- int ret;
-
- spin_lock(&pcdev->lock);
-
- vb = pcdev->active;
- if (!vb)
- /* Stale interrupt from a released buffer */
- goto out;
-
- list_del_init(&to_ceu_vb(vb)->queue);
-
- if (!list_empty(&pcdev->capture))
- pcdev->active = &list_entry(pcdev->capture.next,
- struct sh_mobile_ceu_buffer, queue)->vb;
- else
- pcdev->active = NULL;
-
- 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(&pcdev->lock);
-
- return IRQ_HANDLED;
-}
-
-static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev)
-{
- struct v4l2_subdev *sd;
-
- if (!pcdev->csi2_pdev)
- return NULL;
-
- v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev)
- if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd))
- return sd;
-
- return NULL;
-}
-
-/* Called with .video_lock held */
-static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *csi2_sd;
- int ret;
-
- if (pcdev->icd)
- return -EBUSY;
-
- dev_info(icd->parent,
- "SuperH Mobile CEU driver attached to camera %d\n",
- icd->devnum);
-
- pm_runtime_get_sync(ici->v4l2_dev.dev);
-
- pcdev->buf_total = 0;
-
- ret = sh_mobile_ceu_soft_reset(pcdev);
-
- csi2_sd = find_csi2(pcdev);
- if (csi2_sd) {
- csi2_sd->grp_id = soc_camera_grp_id(icd);
- v4l2_set_subdev_hostdata(csi2_sd, icd);
- }
-
- ret = v4l2_subdev_call(csi2_sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) {
- pm_runtime_put_sync(ici->v4l2_dev.dev);
- return ret;
- }
-
- /*
- * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver
- * has not found this soc-camera device among its clients
- */
- if (ret == -ENODEV && csi2_sd)
- csi2_sd->grp_id = 0;
- pcdev->icd = icd;
-
- return 0;
-}
-
-/* Called with .video_lock held */
-static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
-
- BUG_ON(icd != pcdev->icd);
-
- v4l2_subdev_call(csi2_sd, core, s_power, 0);
- if (csi2_sd)
- csi2_sd->grp_id = 0;
- /* disable capture, disable interrupts */
- ceu_write(pcdev, CEIER, 0);
- sh_mobile_ceu_soft_reset(pcdev);
-
- /* make sure active buffer is canceled */
- spin_lock_irq(&pcdev->lock);
- if (pcdev->active) {
- list_del_init(&to_ceu_vb(pcdev->active)->queue);
- vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR);
- pcdev->active = NULL;
- }
- spin_unlock_irq(&pcdev->lock);
-
- pm_runtime_put_sync(ici->v4l2_dev.dev);
-
- dev_info(icd->parent,
- "SuperH Mobile CEU driver detached from camera %d\n",
- icd->devnum);
-
- pcdev->icd = NULL;
-}
-
-/*
- * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)"
- * in SH7722 Hardware Manual
- */
-static unsigned int size_dst(unsigned int src, unsigned int scale)
-{
- unsigned int mant_pre = scale >> 12;
- if (!src || !scale)
- return src;
- return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) *
- mant_pre * 4096 / scale + 1;
-}
-
-static u16 calc_scale(unsigned int src, unsigned int *dst)
-{
- u16 scale;
-
- if (src == *dst)
- return 0;
-
- scale = (src * 4096 / *dst) & ~7;
-
- while (scale > 4096 && size_dst(src, scale) < *dst)
- scale -= 8;
-
- *dst = size_dst(src, scale);
-
- return scale;
-}
-
-/* rect is guaranteed to not exceed the scaled camera rectangle */
-static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned int height, width, cdwdr_width, in_width, in_height;
- unsigned int left_offset, top_offset;
- u32 camor;
-
- dev_geo(icd->parent, "Crop %ux%u@%u:%u\n",
- icd->user_width, icd->user_height, cam->ceu_left, cam->ceu_top);
-
- left_offset = cam->ceu_left;
- top_offset = cam->ceu_top;
-
- WARN_ON(icd->user_width & 3 || icd->user_height & 3);
-
- width = icd->user_width;
-
- if (pcdev->image_mode) {
- in_width = cam->width;
- if (!pcdev->is_16bit) {
- in_width *= 2;
- left_offset *= 2;
- }
- } else {
- unsigned int w_factor;
-
- switch (icd->current_fmt->host_fmt->packing) {
- case SOC_MBUS_PACKING_2X8_PADHI:
- w_factor = 2;
- break;
- default:
- w_factor = 1;
- }
-
- in_width = cam->width * w_factor;
- left_offset *= w_factor;
- }
-
- cdwdr_width = icd->bytesperline;
-
- height = icd->user_height;
- in_height = cam->height;
- if (V4L2_FIELD_NONE != pcdev->field) {
- height = (height / 2) & ~3;
- in_height /= 2;
- top_offset /= 2;
- cdwdr_width *= 2;
- }
-
- /* CSI2 special configuration */
- if (pcdev->pdata->csi2) {
- in_width = ((in_width - 2) * 2);
- left_offset *= 2;
- }
-
- /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
- camor = left_offset | (top_offset << 16);
-
- dev_geo(icd->parent,
- "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
- (in_height << 16) | in_width, (height << 16) | width,
- cdwdr_width);
-
- ceu_write(pcdev, CAMOR, camor);
- ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
- /* CFSZR clipping is applied _after_ the scaling filter (CFLCR) */
- ceu_write(pcdev, CFSZR, (height << 16) | width);
- ceu_write(pcdev, CDWDR, cdwdr_width);
-}
-
-static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev)
-{
- u32 capsr = ceu_read(pcdev, CAPSR);
- ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */
- return capsr;
-}
-
-static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
-{
- unsigned long timeout = jiffies + 10 * HZ;
-
- /*
- * Wait until the end of the current frame. It can take a long time,
- * but if it has been aborted by a CAPSR reset, it shoule exit sooner.
- */
- while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout))
- msleep(1);
-
- if (time_after(jiffies, timeout)) {
- dev_err(pcdev->ici.v4l2_dev.dev,
- "Timeout waiting for frame end! Interface problem?\n");
- return;
- }
-
- /* Wait until reset clears, this shall not hang... */
- while (ceu_read(pcdev, CAPSR) & (1 << 16))
- udelay(10);
-
- /* Anything to restore? */
- if (capsr & ~(1 << 16))
- ceu_write(pcdev, CAPSR, capsr);
-}
-
-/* Find the bus subdevice driver, e.g., CSI2 */
-static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
- struct soc_camera_device *icd)
-{
- if (pcdev->csi2_pdev) {
- struct v4l2_subdev *csi2_sd = find_csi2(pcdev);
- if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd))
- return csi2_sd;
- }
-
- return soc_camera_to_subdev(icd);
-}
-
-#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_HSYNC_ACTIVE_LOW | \
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_VSYNC_ACTIVE_LOW | \
- V4L2_MBUS_DATA_ACTIVE_HIGH)
-
-/* Capture is not running, no interrupts, no locking needed */
-static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- unsigned long value, common_flags = CEU_BUS_FLAGS;
- u32 capsr = capture_save_reset(pcdev);
- unsigned int yuv_lineskip;
- int ret;
-
- /*
- * If the client doesn't implement g_mbus_config, we just use our
- * platform data
- */
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret) {
- common_flags = soc_mbus_config_compatible(&cfg,
- common_flags);
- if (!common_flags)
- return -EINVAL;
- } else if (ret != -ENOIOCTLCMD) {
- return ret;
- }
-
- /* Make choises, based on platform preferences */
- if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
- (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
- if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
-
- cfg.flags = common_flags;
- ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- if (icd->current_fmt->host_fmt->bits_per_sample > 8)
- pcdev->is_16bit = 1;
- else
- pcdev->is_16bit = 0;
-
- ceu_write(pcdev, CRCNTR, 0);
- ceu_write(pcdev, CRCMPR, 0);
-
- value = 0x00000010; /* data fetch by default */
- yuv_lineskip = 0x10;
-
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- /* convert 4:2:2 -> 4:2:0 */
- yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
- /* fall-through */
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- switch (cam->code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
- value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
- break;
- case V4L2_MBUS_FMT_VYUY8_2X8:
- value = 0x00000100; /* Cr0, Y0, Cb0, Y1 */
- break;
- case V4L2_MBUS_FMT_YUYV8_2X8:
- value = 0x00000200; /* Y0, Cb0, Y1, Cr0 */
- break;
- case V4L2_MBUS_FMT_YVYU8_2X8:
- value = 0x00000300; /* Y0, Cr0, Y1, Cb0 */
- break;
- default:
- BUG();
- }
- }
-
- if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV21 ||
- icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
- value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
-
- value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
- value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
-
- if (pcdev->pdata->csi2) /* CSI2 mode */
- value |= 3 << 12;
- else if (pcdev->is_16bit)
- value |= 1 << 12;
- else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
- value |= 2 << 12;
-
- ceu_write(pcdev, CAMCR, value);
-
- ceu_write(pcdev, CAPCR, 0x00300000);
-
- switch (pcdev->field) {
- case V4L2_FIELD_INTERLACED_TB:
- value = 0x101;
- break;
- case V4L2_FIELD_INTERLACED_BT:
- value = 0x102;
- break;
- default:
- value = 0;
- break;
- }
- ceu_write(pcdev, CAIFR, value);
-
- sh_mobile_ceu_set_rect(icd);
- mdelay(1);
-
- dev_geo(icd->parent, "CFLCR 0x%x\n", pcdev->cflcr);
- ceu_write(pcdev, CFLCR, pcdev->cflcr);
-
- /*
- * A few words about byte order (observed in Big Endian mode)
- *
- * In data fetch mode bytes are received in chunks of 8 bytes.
- * D0, D1, D2, D3, D4, D5, D6, D7 (D0 received first)
- *
- * The data is however by default written to memory in reverse order:
- * D7, D6, D5, D4, D3, D2, D1, D0 (D7 written to lowest byte)
- *
- * The lowest three bits of CDOCR allows us to do swapping,
- * using 7 we swap the data bytes to match the incoming order:
- * D0, D1, D2, D3, D4, D5, D6, D7
- */
- value = 0x00000007 | yuv_lineskip;
-
- ceu_write(pcdev, CDOCR, value);
- ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
-
- capture_restore(pcdev, capsr);
-
- /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
- return 0;
-}
-
-static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
- unsigned char buswidth)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
- unsigned long common_flags = CEU_BUS_FLAGS;
- struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
- int ret;
-
- ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
- if (!ret)
- common_flags = soc_mbus_config_compatible(&cfg,
- common_flags);
- else if (ret != -ENOIOCTLCMD)
- return ret;
-
- if (!common_flags || buswidth > 16)
- return -EINVAL;
-
- return 0;
-}
-
-static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_NV12,
- .name = "NV12",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV21,
- .name = "NV21",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV16,
- .name = "NV16",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
- }, {
- .fourcc = V4L2_PIX_FMT_NV61,
- .name = "NV61",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
- .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
- },
-};
-
-/* This will be corrected as we get more formats */
-static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
-{
- return fmt->packing == SOC_MBUS_PACKING_NONE ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
- (fmt->bits_per_sample > 8 &&
- fmt->packing == SOC_MBUS_PACKING_EXTEND16);
-}
-
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect);
-
-static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl)
-{
- return container_of(ctrl->handler, struct soc_camera_device,
- ctrl_handler);
-}
-
-static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct soc_camera_device *icd = ctrl_to_icd(ctrl);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
-
- switch (ctrl->id) {
- case V4L2_CID_SHARPNESS:
- switch (icd->current_fmt->host_fmt->fourcc) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- ceu_write(pcdev, CLFCR, !ctrl->val);
- return 0;
- }
- break;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = {
- .s_ctrl = sh_mobile_ceu_s_ctrl,
-};
-
-static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx,
- struct soc_camera_format_xlate *xlate)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret, k, n;
- int formats = 0;
- struct sh_mobile_ceu_cam *cam;
- enum v4l2_mbus_pixelcode code;
- const struct soc_mbus_pixelfmt *fmt;
-
- ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
- if (ret < 0)
- /* No more formats */
- return 0;
-
- fmt = soc_mbus_get_fmtdesc(code);
- if (!fmt) {
- dev_warn(dev, "unsupported format code #%u: %d\n", idx, code);
- return 0;
- }
-
- if (!pcdev->pdata->csi2) {
- /* Are there any restrictions in the CSI-2 case? */
- ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
- if (ret < 0)
- return 0;
- }
-
- if (!icd->host_priv) {
- struct v4l2_mbus_framefmt mf;
- struct v4l2_rect rect;
- int shift = 0;
-
- /* Add our control */
- v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops,
- V4L2_CID_SHARPNESS, 0, 1, 1, 0);
- if (icd->ctrl_handler.error)
- return icd->ctrl_handler.error;
-
- /* FIXME: subwindow is lost between close / open */
-
- /* Cache current client geometry */
- ret = client_g_rect(sd, &rect);
- if (ret < 0)
- return ret;
-
- /* First time */
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- /*
- * All currently existing CEU implementations support 2560x1920
- * or larger frames. If the sensor is proposing too big a frame,
- * don't bother with possibly supportred by the CEU larger
- * sizes, just try VGA multiples. If needed, this can be
- * adjusted in the future.
- */
- while ((mf.width > pcdev->max_width ||
- mf.height > pcdev->max_height) && shift < 4) {
- /* Try 2560x1920, 1280x960, 640x480, 320x240 */
- mf.width = 2560 >> shift;
- mf.height = 1920 >> shift;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
- shift++;
- }
-
- if (shift == 4) {
- dev_err(dev, "Failed to configure the client below %ux%x\n",
- mf.width, mf.height);
- return -EIO;
- }
-
- dev_geo(dev, "camera fmt %ux%u\n", mf.width, mf.height);
-
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
- if (!cam)
- return -ENOMEM;
-
- /* We are called with current camera crop, initialise subrect with it */
- cam->rect = rect;
- cam->subrect = rect;
-
- cam->width = mf.width;
- cam->height = mf.height;
-
- icd->host_priv = cam;
- } else {
- cam = icd->host_priv;
- }
-
- /* Beginning of a pass */
- if (!idx)
- cam->extra_fmt = NULL;
-
- switch (code) {
- case V4L2_MBUS_FMT_UYVY8_2X8:
- case V4L2_MBUS_FMT_VYUY8_2X8:
- case V4L2_MBUS_FMT_YUYV8_2X8:
- case V4L2_MBUS_FMT_YVYU8_2X8:
- if (cam->extra_fmt)
- break;
-
- /*
- * Our case is simple so far: for any of the above four camera
- * formats we add all our four synthesized NV* formats, so,
- * just marking the device with a single flag suffices. If
- * the format generation rules are more complex, you would have
- * to actually hang your already added / counted formats onto
- * the host_priv pointer and check whether the format you're
- * going to add now is already there.
- */
- cam->extra_fmt = sh_mobile_ceu_formats;
-
- n = ARRAY_SIZE(sh_mobile_ceu_formats);
- formats += n;
- for (k = 0; xlate && k < n; k++) {
- xlate->host_fmt = &sh_mobile_ceu_formats[k];
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- sh_mobile_ceu_formats[k].name, code);
- }
- break;
- default:
- if (!sh_mobile_ceu_packing_supported(fmt))
- return 0;
- }
-
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s in pass-through mode\n",
- fmt->name);
- }
-
- return formats;
-}
-
-static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
-{
- kfree(icd->host_priv);
- icd->host_priv = NULL;
-}
-
-/* Check if any dimension of r1 is smaller than respective one of r2 */
-static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2)
-{
- return r1->width < r2->width || r1->height < r2->height;
-}
-
-/* Check if r1 fails to cover r2 */
-static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2)
-{
- return r1->left > r2->left || r1->top > r2->top ||
- r1->left + r1->width < r2->left + r2->width ||
- r1->top + r1->height < r2->top + r2->height;
-}
-
-static unsigned int scale_down(unsigned int size, unsigned int scale)
-{
- return (size * 4096 + scale / 2) / scale;
-}
-
-static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
-{
- return (input * 4096 + output / 2) / output;
-}
-
-/* Get and store current client crop */
-static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
-{
- struct v4l2_crop crop;
- struct v4l2_cropcap cap;
- int ret;
-
- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, g_crop, &crop);
- if (!ret) {
- *rect = crop.c;
- return ret;
- }
-
- /* Camera driver doesn't support .g_crop(), assume default rectangle */
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (!ret)
- *rect = cap.defrect;
-
- return ret;
-}
-
-/* Client crop has changed, update our sub-rectangle to remain within the area */
-static void update_subrect(struct sh_mobile_ceu_cam *cam)
-{
- struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect;
-
- if (rect->width < subrect->width)
- subrect->width = rect->width;
-
- if (rect->height < subrect->height)
- subrect->height = rect->height;
-
- if (rect->left > subrect->left)
- subrect->left = rect->left;
- else if (rect->left + rect->width >
- subrect->left + subrect->width)
- subrect->left = rect->left + rect->width -
- subrect->width;
-
- if (rect->top > subrect->top)
- subrect->top = rect->top;
- else if (rect->top + rect->height >
- subrect->top + subrect->height)
- subrect->top = rect->top + rect->height -
- subrect->height;
-}
-
-/*
- * The common for both scaling and cropping iterative approach is:
- * 1. try if the client can produce exactly what requested by the user
- * 2. if (1) failed, try to double the client image until we get one big enough
- * 3. if (2) failed, try to request the maximum image
- */
-static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
- struct v4l2_crop *cam_crop)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
- struct device *dev = sd->v4l2_dev->dev;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_cropcap cap;
- int ret;
- unsigned int width, height;
-
- v4l2_subdev_call(sd, video, s_crop, crop);
- ret = client_g_rect(sd, cam_rect);
- if (ret < 0)
- return ret;
-
- /*
- * Now cam_crop contains the current camera input rectangle, and it must
- * be within camera cropcap bounds
- */
- if (!memcmp(rect, cam_rect, sizeof(*rect))) {
- /* Even if camera S_CROP failed, but camera rectangle matches */
- dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n",
- rect->width, rect->height, rect->left, rect->top);
- cam->rect = *cam_rect;
- return 0;
- }
-
- /* Try to fix cropping, that camera hasn't managed to set */
- dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top,
- rect->width, rect->height, rect->left, rect->top);
-
- /* We need sensor maximum rectangle */
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- /* Put user requested rectangle within sensor bounds */
- soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
- cap.bounds.width);
- soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
- cap.bounds.height);
-
- /*
- * Popular special case - some cameras can only handle fixed sizes like
- * QVGA, VGA,... Take care to avoid infinite loop.
- */
- width = max(cam_rect->width, 2);
- height = max(cam_rect->height, 2);
-
- /*
- * Loop as long as sensor is not covering the requested rectangle and
- * is still within its bounds
- */
- while (!ret && (is_smaller(cam_rect, rect) ||
- is_inside(cam_rect, rect)) &&
- (cap.bounds.width > width || cap.bounds.height > height)) {
-
- width *= 2;
- height *= 2;
-
- cam_rect->width = width;
- cam_rect->height = height;
-
- /*
- * We do not know what capabilities the camera has to set up
- * left and top borders. We could try to be smarter in iterating
- * them, e.g., if camera current left is to the right of the
- * target left, set it to the middle point between the current
- * left and minimum left. But that would add too much
- * complexity: we would have to iterate each border separately.
- * Instead we just drop to the left and top bounds.
- */
- if (cam_rect->left > rect->left)
- cam_rect->left = cap.bounds.left;
-
- if (cam_rect->left + cam_rect->width < rect->left + rect->width)
- cam_rect->width = rect->left + rect->width -
- cam_rect->left;
-
- if (cam_rect->top > rect->top)
- cam_rect->top = cap.bounds.top;
-
- if (cam_rect->top + cam_rect->height < rect->top + rect->height)
- cam_rect->height = rect->top + rect->height -
- cam_rect->top;
-
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- /* S_CROP must not modify the rectangle */
- if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
- /*
- * The camera failed to configure a suitable cropping,
- * we cannot use the current rectangle, set to max
- */
- *cam_rect = cap.bounds;
- v4l2_subdev_call(sd, video, s_crop, cam_crop);
- ret = client_g_rect(sd, cam_rect);
- dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret,
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
- }
-
- if (!ret) {
- cam->rect = *cam_rect;
- update_subrect(cam);
- }
-
- return ret;
-}
-
-/* Iterative s_mbus_fmt, also updates cached client crop on success */
-static int client_s_fmt(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct device *dev = icd->parent;
- unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h;
- unsigned int max_width, max_height;
- struct v4l2_cropcap cap;
- bool ceu_1to1;
- int ret;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height);
-
- if (width == mf->width && height == mf->height) {
- /* Perfect! The client has done it all. */
- ceu_1to1 = true;
- goto update_cache;
- }
-
- ceu_1to1 = false;
- if (!ceu_can_scale)
- goto update_cache;
-
- cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- ret = v4l2_subdev_call(sd, video, cropcap, &cap);
- if (ret < 0)
- return ret;
-
- max_width = min(cap.bounds.width, pcdev->max_width);
- max_height = min(cap.bounds.height, pcdev->max_height);
-
- /* Camera set a format, but geometry is not precise, try to improve */
- tmp_w = mf->width;
- tmp_h = mf->height;
-
- /* width <= max_width && height <= max_height - guaranteed by try_fmt */
- while ((width > tmp_w || height > tmp_h) &&
- tmp_w < max_width && tmp_h < max_height) {
- tmp_w = min(2 * tmp_w, max_width);
- tmp_h = min(2 * tmp_h, max_height);
- mf->width = tmp_w;
- mf->height = tmp_h;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, mf);
- dev_geo(dev, "Camera scaled to %ux%u\n",
- mf->width, mf->height);
- if (ret < 0) {
- /* This shouldn't happen */
- dev_err(dev, "Client failed to set format: %d\n", ret);
- return ret;
- }
- }
-
-update_cache:
- /* Update cache */
- ret = client_g_rect(sd, &cam->rect);
- if (ret < 0)
- return ret;
-
- if (ceu_1to1)
- cam->subrect = cam->rect;
- else
- update_subrect(cam);
-
- return 0;
-}
-
-/**
- * @width - on output: user width, mapped back to input
- * @height - on output: user height, mapped back to input
- * @mf - in- / output camera output window
- */
-static int client_scale(struct soc_camera_device *icd,
- struct v4l2_mbus_framefmt *mf,
- unsigned int *width, unsigned int *height,
- bool ceu_can_scale)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_mbus_framefmt mf_tmp = *mf;
- unsigned int scale_h, scale_v;
- int ret;
-
- /*
- * 5. Apply iterative camera S_FMT for camera user window (also updates
- * client crop cache and the imaginary sub-rectangle).
- */
- ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "5: camera scaled to %ux%u\n",
- mf_tmp.width, mf_tmp.height);
-
- /* 6. Retrieve camera output window (g_fmt) */
-
- /* unneeded - it is already in "mf_tmp" */
-
- /* 7. Calculate new client scales. */
- scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width);
- scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height);
-
- mf->width = mf_tmp.width;
- mf->height = mf_tmp.height;
- mf->colorspace = mf_tmp.colorspace;
-
- /*
- * 8. Calculate new CEU crop - apply camera scales to previously
- * updated "effective" crop.
- */
- *width = scale_down(cam->subrect.width, scale_h);
- *height = scale_down(cam->subrect.height, scale_v);
-
- dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height);
-
- return 0;
-}
-
-/*
- * CEU can scale and crop, but we don't want to waste bandwidth and kill the
- * framerate by always requesting the maximum image from the client. See
- * Documentation/video4linux/sh_mobile_ceu_camera.txt for a description of
- * scaling and cropping algorithms and for the meaning of referenced here steps.
- */
-static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
- struct v4l2_crop *a)
-{
- struct v4l2_rect *rect = &a->c;
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct v4l2_crop cam_crop;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_rect *cam_rect = &cam_crop.c;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_framefmt mf;
- unsigned int scale_cam_h, scale_cam_v, scale_ceu_h, scale_ceu_v,
- out_width, out_height;
- int interm_width, interm_height;
- u32 capsr, cflcr;
- int ret;
-
- dev_geo(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height,
- rect->left, rect->top);
-
- /* During camera cropping its output window can change too, stop CEU */
- capsr = capture_save_reset(pcdev);
- dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
-
- /*
- * 1. - 2. Apply iterative camera S_CROP for new input window, read back
- * actual camera rectangle.
- */
- ret = client_s_crop(icd, a, &cam_crop);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "1-2: camera cropped to %ux%u@%u:%u\n",
- cam_rect->width, cam_rect->height,
- cam_rect->left, cam_rect->top);
-
- /* On success cam_crop contains current camera crop */
-
- /* 3. Retrieve camera output window */
- ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- if (mf.width > pcdev->max_width || mf.height > pcdev->max_height)
- return -EINVAL;
-
- /* 4. Calculate camera scales */
- scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
- scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
-
- /* Calculate intermediate window */
- interm_width = scale_down(rect->width, scale_cam_h);
- interm_height = scale_down(rect->height, scale_cam_v);
-
- if (interm_width < icd->user_width) {
- u32 new_scale_h;
-
- new_scale_h = calc_generic_scale(rect->width, icd->user_width);
-
- mf.width = scale_down(cam_rect->width, new_scale_h);
- }
-
- if (interm_height < icd->user_height) {
- u32 new_scale_v;
-
- new_scale_v = calc_generic_scale(rect->height, icd->user_height);
-
- mf.height = scale_down(cam_rect->height, new_scale_v);
- }
-
- if (interm_width < icd->user_width || interm_height < icd->user_height) {
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- s_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- dev_geo(dev, "New camera output %ux%u\n", mf.width, mf.height);
- scale_cam_h = calc_generic_scale(cam_rect->width, mf.width);
- scale_cam_v = calc_generic_scale(cam_rect->height, mf.height);
- interm_width = scale_down(rect->width, scale_cam_h);
- interm_height = scale_down(rect->height, scale_cam_v);
- }
-
- /* Cache camera output window */
- cam->width = mf.width;
- cam->height = mf.height;
-
- if (pcdev->image_mode) {
- out_width = min(interm_width, icd->user_width);
- out_height = min(interm_height, icd->user_height);
- } else {
- out_width = interm_width;
- out_height = interm_height;
- }
-
- /*
- * 5. Calculate CEU scales from camera scales from results of (5) and
- * the user window
- */
- scale_ceu_h = calc_scale(interm_width, &out_width);
- scale_ceu_v = calc_scale(interm_height, &out_height);
-
- dev_geo(dev, "5: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
-
- /* Apply CEU scales. */
- cflcr = scale_ceu_h | (scale_ceu_v << 16);
- if (cflcr != pcdev->cflcr) {
- pcdev->cflcr = cflcr;
- ceu_write(pcdev, CFLCR, cflcr);
- }
-
- icd->user_width = out_width & ~3;
- icd->user_height = out_height & ~3;
- /* Offsets are applied at the CEU scaling filter input */
- 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);
-
- cam->subrect = *rect;
-
- dev_geo(dev, "6: CEU cropped to %ux%u@%u:%u\n",
- icd->user_width, icd->user_height,
- cam->ceu_left, cam->ceu_top);
-
- /* Restore capture. The CE bit can be cleared by the hardware */
- if (pcdev->active)
- capsr |= 1;
- capture_restore(pcdev, capsr);
-
- /* Even if only camera cropping succeeded */
- return ret;
-}
-
-static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd,
- struct v4l2_crop *a)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
-
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->c = cam->subrect;
-
- return 0;
-}
-
-/*
- * Calculate real client output window by applying new scales to the current
- * client crop. New scales are calculated from the requested output format and
- * CEU crop, mapped backed onto the client input (subrect).
- */
-static void calculate_client_output(struct soc_camera_device *icd,
- const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf)
-{
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct device *dev = icd->parent;
- struct v4l2_rect *cam_subrect = &cam->subrect;
- unsigned int scale_v, scale_h;
-
- if (cam_subrect->width == cam->rect.width &&
- cam_subrect->height == cam->rect.height) {
- /* No sub-cropping */
- mf->width = pix->width;
- mf->height = pix->height;
- return;
- }
-
- /* 1.-2. Current camera scales and subwin - cached. */
-
- dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
- cam_subrect->width, cam_subrect->height,
- cam_subrect->left, cam_subrect->top);
-
- /*
- * 3. Calculate new combined scales from input sub-window to requested
- * user window.
- */
-
- /*
- * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF
- * (128x96) or larger than VGA
- */
- scale_h = calc_generic_scale(cam_subrect->width, pix->width);
- scale_v = calc_generic_scale(cam_subrect->height, pix->height);
-
- dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
-
- /*
- * 4. Calculate desired client output window by applying combined scales
- * to client (real) input window.
- */
- mf->width = scale_down(cam->rect.width, scale_h);
- mf->height = scale_down(cam->rect.height, scale_v);
-}
-
-/* Similar to set_crop multistage iterative algorithm */
-static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct device *dev = icd->parent;
- struct soc_camera_host *ici = to_soc_camera_host(dev);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- struct sh_mobile_ceu_cam *cam = icd->host_priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_mbus_framefmt mf;
- __u32 pixfmt = pix->pixelformat;
- const struct soc_camera_format_xlate *xlate;
- /* Keep Compiler Happy */
- unsigned int ceu_sub_width = 0, ceu_sub_height = 0;
- u16 scale_v, scale_h;
- int ret;
- bool image_mode;
- enum v4l2_field field;
-
- switch (pix->field) {
- default:
- pix->field = V4L2_FIELD_NONE;
- /* fall-through */
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_NONE:
- field = pix->field;
- break;
- case V4L2_FIELD_INTERLACED:
- field = V4L2_FIELD_INTERLACED_TB;
- break;
- }
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- dev_warn(dev, "Format %x not found\n", pixfmt);
- return -EINVAL;
- }
-
- /* 1.-4. Calculate desired client output geometry */
- calculate_client_output(icd, pix, &mf);
- mf.field = pix->field;
- mf.colorspace = pix->colorspace;
- mf.code = xlate->code;
-
- switch (pixfmt) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- image_mode = true;
- break;
- default:
- image_mode = false;
- }
-
- dev_geo(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
- pix->width, pix->height);
-
- dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
-
- /* 5. - 9. */
- ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height,
- image_mode && V4L2_FIELD_NONE == field);
-
- dev_geo(dev, "5-9: client scale return %d\n", ret);
-
- /* Done with the camera. Now see if we can improve the result */
-
- dev_geo(dev, "fmt %ux%u, requested %ux%u\n",
- mf.width, mf.height, pix->width, pix->height);
- if (ret < 0)
- return ret;
-
- if (mf.code != xlate->code)
- return -EINVAL;
-
- /* 9. Prepare CEU crop */
- cam->width = mf.width;
- cam->height = mf.height;
-
- /* 10. Use CEU scaling to scale to the requested user window. */
-
- /* We cannot scale up */
- if (pix->width > ceu_sub_width)
- ceu_sub_width = pix->width;
-
- if (pix->height > ceu_sub_height)
- ceu_sub_height = pix->height;
-
- pix->colorspace = mf.colorspace;
-
- if (image_mode) {
- /* Scale pix->{width x height} down to width x height */
- scale_h = calc_scale(ceu_sub_width, &pix->width);
- scale_v = calc_scale(ceu_sub_height, &pix->height);
- } else {
- pix->width = ceu_sub_width;
- pix->height = ceu_sub_height;
- scale_h = 0;
- scale_v = 0;
- }
-
- pcdev->cflcr = scale_h | (scale_v << 16);
-
- /*
- * We have calculated CFLCR, the actual configuration will be performed
- * in sh_mobile_ceu_set_bus_param()
- */
-
- dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
- ceu_sub_width, scale_h, pix->width,
- ceu_sub_height, scale_v, pix->height);
-
- cam->code = xlate->code;
- icd->current_fmt = xlate;
-
- pcdev->field = field;
- pcdev->image_mode = image_mode;
-
- /* CFSZR requirement */
- pix->width &= ~3;
- pix->height &= ~3;
-
- return 0;
-}
-
-#define CEU_CHDW_MAX 8188U /* Maximum line stride */
-
-static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct v4l2_mbus_framefmt mf;
- __u32 pixfmt = pix->pixelformat;
- int width, height;
- int ret;
-
- dev_geo(icd->parent, "TRY_FMT(pix=0x%x, %ux%u)\n",
- pixfmt, pix->width, pix->height);
-
- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
- if (!xlate) {
- xlate = icd->current_fmt;
- dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
- pixfmt, xlate->host_fmt->fourcc);
- pixfmt = xlate->host_fmt->fourcc;
- pix->pixelformat = pixfmt;
- pix->colorspace = icd->colorspace;
- }
-
- /* FIXME: calculate using depth and bus width */
-
- /* CFSZR requires height and width to be 4-pixel aligned */
- v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
- &pix->height, 4, pcdev->max_height, 2, 0);
-
- width = pix->width;
- height = pix->height;
-
- /* limit to sensor capabilities */
- mf.width = pix->width;
- mf.height = pix->height;
- mf.field = pix->field;
- mf.code = xlate->code;
- mf.colorspace = pix->colorspace;
-
- ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
- video, try_mbus_fmt, &mf);
- if (ret < 0)
- return ret;
-
- pix->width = mf.width;
- pix->height = mf.height;
- pix->field = mf.field;
- pix->colorspace = mf.colorspace;
-
- switch (pixfmt) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- /* FIXME: check against rect_max after converting soc-camera */
- /* We can scale precisely, need a bigger image from camera */
- if (pix->width < width || pix->height < height) {
- /*
- * We presume, the sensor behaves sanely, i.e., if
- * requested a bigger rectangle, it will not return a
- * smaller one.
- */
- mf.width = pcdev->max_width;
- mf.height = pcdev->max_height;
- ret = v4l2_device_call_until_err(sd->v4l2_dev,
- soc_camera_grp_id(icd), video,
- try_mbus_fmt, &mf);
- if (ret < 0) {
- /* Shouldn't actually happen... */
- dev_err(icd->parent,
- "FIXME: client try_fmt() = %d\n", ret);
- return ret;
- }
- }
- /* We will scale exactly */
- if (mf.width > width)
- pix->width = width;
- if (mf.height > height)
- pix->height = height;
-
- pix->bytesperline = max(pix->bytesperline, pix->width);
- pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX);
- pix->bytesperline &= ~3;
- break;
-
- default:
- /* Configurable stride isn't supported in pass-through mode. */
- pix->bytesperline = 0;
- }
-
- pix->width &= ~3;
- pix->height &= ~3;
- pix->sizeimage = 0;
-
- dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
- __func__, ret, pix->pixelformat, pix->width, pix->height);
-
- return ret;
-}
-
-static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd,
- struct v4l2_crop *a)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
- u32 out_width = icd->user_width, out_height = icd->user_height;
- int ret;
-
- /* Freeze queue */
- pcdev->frozen = 1;
- /* Wait for frame */
- ret = wait_for_completion_interruptible(&pcdev->complete);
- /* Stop the client */
- ret = v4l2_subdev_call(sd, video, s_stream, 0);
- if (ret < 0)
- dev_warn(icd->parent,
- "Client failed to stop the stream: %d\n", ret);
- else
- /* Do the crop, if it fails, there's nothing more we can do */
- sh_mobile_ceu_set_crop(icd, a);
-
- dev_geo(icd->parent, "Output after crop: %ux%u\n", icd->user_width, icd->user_height);
-
- if (icd->user_width != out_width || icd->user_height != out_height) {
- struct v4l2_format f = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .fmt.pix = {
- .width = out_width,
- .height = out_height,
- .pixelformat = icd->current_fmt->host_fmt->fourcc,
- .field = pcdev->field,
- .colorspace = icd->colorspace,
- },
- };
- ret = sh_mobile_ceu_set_fmt(icd, &f);
- if (!ret && (out_width != f.fmt.pix.width ||
- out_height != f.fmt.pix.height))
- ret = -EINVAL;
- if (!ret) {
- icd->user_width = out_width & ~3;
- icd->user_height = out_height & ~3;
- ret = sh_mobile_ceu_set_bus_param(icd);
- }
- }
-
- /* Thaw the queue */
- pcdev->frozen = 0;
- spin_lock_irq(&pcdev->lock);
- sh_mobile_ceu_capture(pcdev);
- spin_unlock_irq(&pcdev->lock);
- /* Start the client */
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
- return ret;
-}
-
-static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
-
- return vb2_poll(&icd->vb2_vidq, file, pt);
-}
-
-static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
- struct v4l2_capability *cap)
-{
- strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
- return 0;
-}
-
-static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
-{
- 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 struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
- .owner = THIS_MODULE,
- .add = sh_mobile_ceu_add_device,
- .remove = sh_mobile_ceu_remove_device,
- .get_formats = sh_mobile_ceu_get_formats,
- .put_formats = sh_mobile_ceu_put_formats,
- .get_crop = sh_mobile_ceu_get_crop,
- .set_crop = sh_mobile_ceu_set_crop,
- .set_livecrop = sh_mobile_ceu_set_livecrop,
- .set_fmt = sh_mobile_ceu_set_fmt,
- .try_fmt = sh_mobile_ceu_try_fmt,
- .poll = sh_mobile_ceu_poll,
- .querycap = sh_mobile_ceu_querycap,
- .set_bus_param = sh_mobile_ceu_set_bus_param,
- .init_videobuf2 = sh_mobile_ceu_init_videobuf,
-};
-
-struct bus_wait {
- struct notifier_block notifier;
- struct completion completion;
- struct device *dev;
-};
-
-static int bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct bus_wait *wait = container_of(nb, struct bus_wait, notifier);
-
- if (wait->dev != dev)
- return NOTIFY_DONE;
-
- switch (action) {
- case BUS_NOTIFY_UNBOUND_DRIVER:
- /* Protect from module unloading */
- wait_for_completion(&wait->completion);
- return NOTIFY_OK;
- }
- return NOTIFY_DONE;
-}
-
-static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
-{
- struct sh_mobile_ceu_dev *pcdev;
- struct resource *res;
- void __iomem *base;
- unsigned int irq;
- int err = 0;
- struct bus_wait wait = {
- .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion),
- .notifier.notifier_call = bus_notify,
- };
- struct sh_mobile_ceu_companion *csi2;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
- if (!res || (int)irq <= 0) {
- dev_err(&pdev->dev, "Not enough CEU platform resources.\n");
- err = -ENODEV;
- goto exit;
- }
-
- pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL);
- if (!pcdev) {
- dev_err(&pdev->dev, "Could not allocate pcdev\n");
- err = -ENOMEM;
- goto exit;
- }
-
- INIT_LIST_HEAD(&pcdev->capture);
- spin_lock_init(&pcdev->lock);
- init_completion(&pcdev->complete);
-
- pcdev->pdata = pdev->dev.platform_data;
- if (!pcdev->pdata) {
- err = -EINVAL;
- dev_err(&pdev->dev, "CEU platform data not set.\n");
- goto exit_kfree;
- }
-
- pcdev->max_width = pcdev->pdata->max_width ? : 2560;
- pcdev->max_height = pcdev->pdata->max_height ? : 1920;
-
- base = ioremap_nocache(res->start, resource_size(res));
- if (!base) {
- err = -ENXIO;
- dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n");
- goto exit_kfree;
- }
-
- pcdev->irq = irq;
- pcdev->base = base;
- pcdev->video_limit = 0; /* only enabled if second resource exists */
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- err = dma_declare_coherent_memory(&pdev->dev, res->start,
- res->start,
- resource_size(res),
- DMA_MEMORY_MAP |
- DMA_MEMORY_EXCLUSIVE);
- if (!err) {
- dev_err(&pdev->dev, "Unable to declare CEU memory.\n");
- err = -ENXIO;
- goto exit_iounmap;
- }
-
- pcdev->video_limit = resource_size(res);
- }
-
- /* request irq */
- err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), pcdev);
- if (err) {
- dev_err(&pdev->dev, "Unable to register CEU interrupt.\n");
- goto exit_release_mem;
- }
-
- pm_suspend_ignore_children(&pdev->dev, true);
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
-
- pcdev->ici.priv = pcdev;
- pcdev->ici.v4l2_dev.dev = &pdev->dev;
- pcdev->ici.nr = pdev->id;
- pcdev->ici.drv_name = dev_name(&pdev->dev);
- pcdev->ici.ops = &sh_mobile_ceu_host_ops;
- pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE;
-
- pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(pcdev->alloc_ctx)) {
- err = PTR_ERR(pcdev->alloc_ctx);
- goto exit_free_clk;
- }
-
- err = soc_camera_host_register(&pcdev->ici);
- if (err)
- goto exit_free_ctx;
-
- /* CSI2 interfacing */
- csi2 = pcdev->pdata->csi2;
- if (csi2) {
- struct platform_device *csi2_pdev =
- platform_device_alloc("sh-mobile-csi2", csi2->id);
- struct sh_csi2_pdata *csi2_pdata = csi2->platform_data;
-
- if (!csi2_pdev) {
- err = -ENOMEM;
- goto exit_host_unregister;
- }
-
- pcdev->csi2_pdev = csi2_pdev;
-
- err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata));
- if (err < 0)
- goto exit_pdev_put;
-
- csi2_pdata = csi2_pdev->dev.platform_data;
- csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev;
-
- csi2_pdev->resource = csi2->resource;
- csi2_pdev->num_resources = csi2->num_resources;
-
- err = platform_device_add(csi2_pdev);
- if (err < 0)
- goto exit_pdev_put;
-
- wait.dev = &csi2_pdev->dev;
-
- err = bus_register_notifier(&platform_bus_type, &wait.notifier);
- if (err < 0)
- goto exit_pdev_unregister;
-
- /*
- * From this point the driver module will not unload, until
- * we complete the completion.
- */
-
- if (!csi2_pdev->dev.driver) {
- complete(&wait.completion);
- /* Either too late, or probing failed */
- bus_unregister_notifier(&platform_bus_type, &wait.notifier);
- err = -ENXIO;
- goto exit_pdev_unregister;
- }
-
- /*
- * The module is still loaded, in the worst case it is hanging
- * in device release on our completion. So, _now_ dereferencing
- * the "owner" is safe!
- */
-
- err = try_module_get(csi2_pdev->dev.driver->owner);
-
- /* Let notifier complete, if it has been locked */
- complete(&wait.completion);
- bus_unregister_notifier(&platform_bus_type, &wait.notifier);
- if (!err) {
- err = -ENODEV;
- goto exit_pdev_unregister;
- }
- }
-
- return 0;
-
-exit_pdev_unregister:
- platform_device_del(pcdev->csi2_pdev);
-exit_pdev_put:
- pcdev->csi2_pdev->resource = NULL;
- platform_device_put(pcdev->csi2_pdev);
-exit_host_unregister:
- soc_camera_host_unregister(&pcdev->ici);
-exit_free_ctx:
- vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
-exit_free_clk:
- pm_runtime_disable(&pdev->dev);
- free_irq(pcdev->irq, pcdev);
-exit_release_mem:
- if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
- dma_release_declared_memory(&pdev->dev);
-exit_iounmap:
- iounmap(base);
-exit_kfree:
- kfree(pcdev);
-exit:
- return err;
-}
-
-static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
-{
- struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
- struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
- struct sh_mobile_ceu_dev, ici);
- struct platform_device *csi2_pdev = pcdev->csi2_pdev;
-
- soc_camera_host_unregister(soc_host);
- pm_runtime_disable(&pdev->dev);
- free_irq(pcdev->irq, pcdev);
- 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_pdev && csi2_pdev->dev.driver) {
- struct module *csi2_drv = csi2_pdev->dev.driver->owner;
- platform_device_del(csi2_pdev);
- csi2_pdev->resource = NULL;
- platform_device_put(csi2_pdev);
- module_put(csi2_drv);
- }
- kfree(pcdev);
-
- return 0;
-}
-
-static int sh_mobile_ceu_runtime_nop(struct device *dev)
-{
- /* Runtime PM callback shared between ->runtime_suspend()
- * and ->runtime_resume(). Simply returns success.
- *
- * This driver re-initializes all registers after
- * pm_runtime_get_sync() anyway so there is no need
- * to save and restore registers here.
- */
- return 0;
-}
-
-static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = {
- .runtime_suspend = sh_mobile_ceu_runtime_nop,
- .runtime_resume = sh_mobile_ceu_runtime_nop,
-};
-
-static struct platform_driver sh_mobile_ceu_driver = {
- .driver = {
- .name = "sh_mobile_ceu",
- .pm = &sh_mobile_ceu_dev_pm_ops,
- },
- .probe = sh_mobile_ceu_probe,
- .remove = __devexit_p(sh_mobile_ceu_remove),
-};
-
-static int __init sh_mobile_ceu_init(void)
-{
- /* Whatever return code */
- request_module("sh_mobile_csi2");
- return platform_driver_register(&sh_mobile_ceu_driver);
-}
-
-static void __exit sh_mobile_ceu_exit(void)
-{
- platform_driver_unregister(&sh_mobile_ceu_driver);
-}
-
-module_init(sh_mobile_ceu_init);
-module_exit(sh_mobile_ceu_exit);
-
-MODULE_DESCRIPTION("SuperH Mobile CEU driver");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.6");
-MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
deleted file mode 100644
index 8fd1874382c6..000000000000
--- a/drivers/media/video/sh_vou.c
+++ /dev/null
@@ -1,1497 +0,0 @@
-/*
- * SuperH Video Output Unit (VOU) driver
- *
- * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
- *
- * 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/dma-mapping.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/sh_vou.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-mediabus.h>
-#include <media/videobuf-dma-contig.h>
-
-/* Mirror addresses are not available for all registers */
-#define VOUER 0
-#define VOUCR 4
-#define VOUSTR 8
-#define VOUVCR 0xc
-#define VOUISR 0x10
-#define VOUBCR 0x14
-#define VOUDPR 0x18
-#define VOUDSR 0x1c
-#define VOUVPR 0x20
-#define VOUIR 0x24
-#define VOUSRR 0x28
-#define VOUMSR 0x2c
-#define VOUHIR 0x30
-#define VOUDFR 0x34
-#define VOUAD1R 0x38
-#define VOUAD2R 0x3c
-#define VOUAIR 0x40
-#define VOUSWR 0x44
-#define VOURCR 0x48
-#define VOURPR 0x50
-
-enum sh_vou_status {
- SH_VOU_IDLE,
- SH_VOU_INITIALISING,
- SH_VOU_RUNNING,
-};
-
-#define VOU_MAX_IMAGE_WIDTH 720
-#define VOU_MAX_IMAGE_HEIGHT 576
-
-struct sh_vou_device {
- struct v4l2_device v4l2_dev;
- struct video_device *vdev;
- atomic_t use_count;
- struct sh_vou_pdata *pdata;
- spinlock_t lock;
- void __iomem *base;
- /* State information */
- struct v4l2_pix_format pix;
- struct v4l2_rect rect;
- struct list_head queue;
- v4l2_std_id std;
- int pix_idx;
- struct videobuf_buffer *active;
- enum sh_vou_status status;
- struct mutex fop_lock;
-};
-
-struct sh_vou_file {
- struct videobuf_queue vbq;
-};
-
-/* Register access routines for sides A, B and mirror addresses */
-static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value)
-{
- __raw_writel(value, vou_dev->base + reg);
-}
-
-static void sh_vou_reg_ab_write(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value)
-{
- __raw_writel(value, vou_dev->base + reg);
- __raw_writel(value, vou_dev->base + reg + 0x1000);
-}
-
-static void sh_vou_reg_m_write(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value)
-{
- __raw_writel(value, vou_dev->base + reg + 0x2000);
-}
-
-static u32 sh_vou_reg_a_read(struct sh_vou_device *vou_dev, unsigned int reg)
-{
- return __raw_readl(vou_dev->base + reg);
-}
-
-static void sh_vou_reg_a_set(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value, u32 mask)
-{
- u32 old = __raw_readl(vou_dev->base + reg);
-
- value = (value & mask) | (old & ~mask);
- __raw_writel(value, vou_dev->base + reg);
-}
-
-static void sh_vou_reg_b_set(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value, u32 mask)
-{
- sh_vou_reg_a_set(vou_dev, reg + 0x1000, value, mask);
-}
-
-static void sh_vou_reg_ab_set(struct sh_vou_device *vou_dev, unsigned int reg,
- u32 value, u32 mask)
-{
- sh_vou_reg_a_set(vou_dev, reg, value, mask);
- sh_vou_reg_b_set(vou_dev, reg, value, mask);
-}
-
-struct sh_vou_fmt {
- u32 pfmt;
- char *desc;
- unsigned char bpp;
- unsigned char rgb;
- unsigned char yf;
- unsigned char pkf;
-};
-
-/* Further pixel formats can be added */
-static struct sh_vou_fmt vou_fmt[] = {
- {
- .pfmt = V4L2_PIX_FMT_NV12,
- .bpp = 12,
- .desc = "YVU420 planar",
- .yf = 0,
- .rgb = 0,
- },
- {
- .pfmt = V4L2_PIX_FMT_NV16,
- .bpp = 16,
- .desc = "YVYU planar",
- .yf = 1,
- .rgb = 0,
- },
- {
- .pfmt = V4L2_PIX_FMT_RGB24,
- .bpp = 24,
- .desc = "RGB24",
- .pkf = 2,
- .rgb = 1,
- },
- {
- .pfmt = V4L2_PIX_FMT_RGB565,
- .bpp = 16,
- .desc = "RGB565",
- .pkf = 3,
- .rgb = 1,
- },
- {
- .pfmt = V4L2_PIX_FMT_RGB565X,
- .bpp = 16,
- .desc = "RGB565 byteswapped",
- .pkf = 3,
- .rgb = 1,
- },
-};
-
-static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
- struct videobuf_buffer *vb)
-{
- dma_addr_t addr1, addr2;
-
- addr1 = videobuf_to_dma_contig(vb);
- switch (vou_dev->pix.pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV16:
- addr2 = addr1 + vou_dev->pix.width * vou_dev->pix.height;
- break;
- default:
- addr2 = 0;
- }
-
- sh_vou_reg_m_write(vou_dev, VOUAD1R, addr1);
- sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
-}
-
-static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
- struct videobuf_buffer *vb)
-{
- unsigned int row_coeff;
-#ifdef __LITTLE_ENDIAN
- u32 dataswap = 7;
-#else
- u32 dataswap = 0;
-#endif
-
- switch (vou_dev->pix.pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV16:
- row_coeff = 1;
- break;
- case V4L2_PIX_FMT_RGB565:
- dataswap ^= 1;
- case V4L2_PIX_FMT_RGB565X:
- row_coeff = 2;
- break;
- case V4L2_PIX_FMT_RGB24:
- row_coeff = 3;
- break;
- }
-
- sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
- sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
- sh_vou_schedule_next(vou_dev, vb);
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- BUG_ON(in_interrupt());
-
- /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
- videobuf_waiton(vq, vb, 0, 0);
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-/* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
-{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
- vou_dev->pix.height / 8;
-
- if (*count < 2)
- *count = 2;
-
- /* Taking into account maximum frame size, *count will stay >= 2 */
- if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
- *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
-
- dev_dbg(vq->dev, "%s(): count=%d, size=%d\n", __func__, *count, *size);
-
- return 0;
-}
-
-/* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct v4l2_pix_format *pix = &vou_dev->pix;
- int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
- int ret;
-
- dev_dbg(vq->dev, "%s()\n", __func__);
-
- if (vb->width != pix->width ||
- vb->height != pix->height ||
- vb->field != pix->field) {
- vb->width = pix->width;
- vb->height = pix->height;
- vb->field = field;
- if (vb->state != VIDEOBUF_NEEDS_INIT)
- free_buffer(vq, vb);
- }
-
- vb->size = vb->height * bytes_per_line;
- if (vb->baddr && vb->bsize < vb->size) {
- /* User buffer too small */
- dev_warn(vq->dev, "User buffer too small: [%u] @ %lx\n",
- vb->bsize, vb->baddr);
- return -EINVAL;
- }
-
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret < 0) {
- dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
- vb->memory, ret);
- return ret;
- }
- vb->state = VIDEOBUF_PREPARED;
- }
-
- dev_dbg(vq->dev,
- "%s(): fmt #%d, %u bytes per line, phys 0x%x, type %d, state %d\n",
- __func__, vou_dev->pix_idx, bytes_per_line,
- videobuf_to_dma_contig(vb), vb->memory, vb->state);
-
- return 0;
-}
-
-/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
-static void sh_vou_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- dev_dbg(vq->dev, "%s()\n", __func__);
-
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &vou_dev->queue);
-
- if (vou_dev->status == SH_VOU_RUNNING) {
- return;
- } else if (!vou_dev->active) {
- vou_dev->active = vb;
- /* Start from side A: we use mirror addresses, so, set B */
- sh_vou_reg_a_write(vou_dev, VOURPR, 1);
- dev_dbg(vq->dev, "%s: first buffer status 0x%x\n", __func__,
- sh_vou_reg_a_read(vou_dev, VOUSTR));
- sh_vou_schedule_next(vou_dev, vb);
- /* Only activate VOU after the second buffer */
- } else if (vou_dev->active->queue.next == &vb->queue) {
- /* Second buffer - initialise register side B */
- sh_vou_reg_a_write(vou_dev, VOURPR, 0);
- sh_vou_stream_start(vou_dev, vb);
-
- /* Register side switching with frame VSYNC */
- sh_vou_reg_a_write(vou_dev, VOURCR, 5);
- dev_dbg(vq->dev, "%s: second buffer status 0x%x\n", __func__,
- sh_vou_reg_a_read(vou_dev, VOUSTR));
-
- /* Enable End-of-Frame (VSYNC) interrupts */
- sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
- /* Two buffers on the queue - activate the hardware */
-
- vou_dev->status = SH_VOU_RUNNING;
- sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
- }
-}
-
-static void sh_vou_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- unsigned long flags;
-
- dev_dbg(vq->dev, "%s()\n", __func__);
-
- spin_lock_irqsave(&vou_dev->lock, flags);
-
- if (vou_dev->active == vb) {
- /* disable output */
- sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
- /* ...but the current frame will complete */
- sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
- vou_dev->active = NULL;
- }
-
- if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
- vb->state = VIDEOBUF_ERROR;
- list_del(&vb->queue);
- }
-
- spin_unlock_irqrestore(&vou_dev->lock, flags);
-
- free_buffer(vq, vb);
-}
-
-static struct videobuf_queue_ops sh_vou_video_qops = {
- .buf_setup = sh_vou_buf_setup,
- .buf_prepare = sh_vou_buf_prepare,
- .buf_queue = sh_vou_buf_queue,
- .buf_release = sh_vou_buf_release,
-};
-
-/* Video IOCTLs */
-static int sh_vou_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
- return 0;
-}
-
-/* Enumerate formats, that the device can accept from the user */
-static int sh_vou_enum_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_fmtdesc *fmt)
-{
- struct sh_vou_file *vou_file = priv;
-
- if (fmt->index >= ARRAY_SIZE(vou_fmt))
- return -EINVAL;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- strlcpy(fmt->description, vou_fmt[fmt->index].desc,
- sizeof(fmt->description));
- fmt->pixelformat = vou_fmt[fmt->index].pfmt;
-
- return 0;
-}
-
-static int sh_vou_g_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- fmt->fmt.pix = vou_dev->pix;
-
- return 0;
-}
-
-static const unsigned char vou_scale_h_num[] = {1, 9, 2, 9, 4};
-static const unsigned char vou_scale_h_den[] = {1, 8, 1, 4, 1};
-static const unsigned char vou_scale_h_fld[] = {0, 2, 1, 3};
-static const unsigned char vou_scale_v_num[] = {1, 2, 4};
-static const unsigned char vou_scale_v_den[] = {1, 1, 1};
-static const unsigned char vou_scale_v_fld[] = {0, 1};
-
-static void sh_vou_configure_geometry(struct sh_vou_device *vou_dev,
- int pix_idx, int w_idx, int h_idx)
-{
- struct sh_vou_fmt *fmt = vou_fmt + pix_idx;
- unsigned int black_left, black_top, width_max, height_max,
- frame_in_height, frame_out_height, frame_out_top;
- struct v4l2_rect *rect = &vou_dev->rect;
- struct v4l2_pix_format *pix = &vou_dev->pix;
- u32 vouvcr = 0, dsr_h, dsr_v;
-
- if (vou_dev->std & V4L2_STD_525_60) {
- width_max = 858;
- height_max = 262;
- } else {
- width_max = 864;
- height_max = 312;
- }
-
- frame_in_height = pix->height / 2;
- frame_out_height = rect->height / 2;
- frame_out_top = rect->top / 2;
-
- /*
- * Cropping scheme: max useful image is 720x480, and the total video
- * area is 858x525 (NTSC) or 864x625 (PAL). AK8813 / 8814 starts
- * sampling data beginning with fixed 276th (NTSC) / 288th (PAL) clock,
- * of which the first 33 / 25 clocks HSYNC must be held active. This
- * has to be configured in CR[HW]. 1 pixel equals 2 clock periods.
- * This gives CR[HW] = 16 / 12, VPR[HVP] = 138 / 144, which gives
- * exactly 858 - 138 = 864 - 144 = 720! We call the out-of-display area,
- * beyond DSR, specified on the left and top by the VPR register "black
- * pixels" and out-of-image area (DPR) "background pixels." We fix VPR
- * at 138 / 144 : 20, because that's the HSYNC timing, that our first
- * client requires, and that's exactly what leaves us 720 pixels for the
- * image; we leave VPR[VVP] at default 20 for now, because the client
- * doesn't seem to have any special requirements for it. Otherwise we
- * could also set it to max - 240 = 22 / 72. Thus VPR depends only on
- * the selected standard, and DPR and DSR are selected according to
- * cropping. Q: how does the client detect the first valid line? Does
- * HSYNC stay inactive during invalid (black) lines?
- */
- black_left = width_max - VOU_MAX_IMAGE_WIDTH;
- black_top = 20;
-
- dsr_h = rect->width + rect->left;
- dsr_v = frame_out_height + frame_out_top;
-
- dev_dbg(vou_dev->v4l2_dev.dev,
- "image %ux%u, black %u:%u, offset %u:%u, display %ux%u\n",
- pix->width, frame_in_height, black_left, black_top,
- rect->left, frame_out_top, dsr_h, dsr_v);
-
- /* VOUISR height - half of a frame height in frame mode */
- sh_vou_reg_ab_write(vou_dev, VOUISR, (pix->width << 16) | frame_in_height);
- sh_vou_reg_ab_write(vou_dev, VOUVPR, (black_left << 16) | black_top);
- sh_vou_reg_ab_write(vou_dev, VOUDPR, (rect->left << 16) | frame_out_top);
- sh_vou_reg_ab_write(vou_dev, VOUDSR, (dsr_h << 16) | dsr_v);
-
- /*
- * if necessary, we could set VOUHIR to
- * max(black_left + dsr_h, width_max) here
- */
-
- if (w_idx)
- vouvcr |= (1 << 15) | (vou_scale_h_fld[w_idx - 1] << 4);
- if (h_idx)
- vouvcr |= (1 << 14) | vou_scale_v_fld[h_idx - 1];
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s: scaling 0x%x\n", fmt->desc, vouvcr);
-
- /* To produce a colour bar for testing set bit 23 of VOUVCR */
- sh_vou_reg_ab_write(vou_dev, VOUVCR, vouvcr);
- sh_vou_reg_ab_write(vou_dev, VOUDFR,
- fmt->pkf | (fmt->yf << 8) | (fmt->rgb << 16));
-}
-
-struct sh_vou_geometry {
- struct v4l2_rect output;
- unsigned int in_width;
- unsigned int in_height;
- int scale_idx_h;
- int scale_idx_v;
-};
-
-/*
- * Find input geometry, that we can use to produce output, closest to the
- * requested rectangle, using VOU scaling
- */
-static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
-{
- /* The compiler cannot know, that best and idx will indeed be set */
- unsigned int best_err = UINT_MAX, best = 0, img_height_max;
- int i, idx = 0;
-
- if (std & V4L2_STD_525_60)
- img_height_max = 480;
- else
- img_height_max = 576;
-
- /* Image width must be a multiple of 4 */
- v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
- &geo->in_height, 0, img_height_max, 1, 0);
-
- /* Select scales to come as close as possible to the output image */
- for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
- unsigned int err;
- unsigned int found = geo->output.width * vou_scale_h_den[i] /
- vou_scale_h_num[i];
-
- if (found > VOU_MAX_IMAGE_WIDTH)
- /* scales increase */
- break;
-
- err = abs(found - geo->in_width);
- if (err < best_err) {
- best_err = err;
- idx = i;
- best = found;
- }
- if (!err)
- break;
- }
-
- geo->in_width = best;
- geo->scale_idx_h = idx;
-
- best_err = UINT_MAX;
-
- /* This loop can be replaced with one division */
- for (i = ARRAY_SIZE(vou_scale_v_num) - 1; i >= 0; i--) {
- unsigned int err;
- unsigned int found = geo->output.height * vou_scale_v_den[i] /
- vou_scale_v_num[i];
-
- if (found > img_height_max)
- /* scales increase */
- break;
-
- err = abs(found - geo->in_height);
- if (err < best_err) {
- best_err = err;
- idx = i;
- best = found;
- }
- if (!err)
- break;
- }
-
- geo->in_height = best;
- geo->scale_idx_v = idx;
-}
-
-/*
- * Find output geometry, that we can produce, using VOU scaling, closest to
- * the requested rectangle
- */
-static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
-{
- unsigned int best_err = UINT_MAX, best, width_max, height_max,
- img_height_max;
- int i, idx;
-
- if (std & V4L2_STD_525_60) {
- width_max = 858;
- height_max = 262 * 2;
- img_height_max = 480;
- } else {
- width_max = 864;
- height_max = 312 * 2;
- img_height_max = 576;
- }
-
- /* Select scales to come as close as possible to the output image */
- for (i = 0; i < ARRAY_SIZE(vou_scale_h_num); i++) {
- unsigned int err;
- unsigned int found = geo->in_width * vou_scale_h_num[i] /
- vou_scale_h_den[i];
-
- if (found > VOU_MAX_IMAGE_WIDTH)
- /* scales increase */
- break;
-
- err = abs(found - geo->output.width);
- if (err < best_err) {
- best_err = err;
- idx = i;
- best = found;
- }
- if (!err)
- break;
- }
-
- geo->output.width = best;
- geo->scale_idx_h = idx;
- if (geo->output.left + best > width_max)
- geo->output.left = width_max - best;
-
- pr_debug("%s(): W %u * %u/%u = %u\n", __func__, geo->in_width,
- vou_scale_h_num[idx], vou_scale_h_den[idx], best);
-
- best_err = UINT_MAX;
-
- /* This loop can be replaced with one division */
- for (i = 0; i < ARRAY_SIZE(vou_scale_v_num); i++) {
- unsigned int err;
- unsigned int found = geo->in_height * vou_scale_v_num[i] /
- vou_scale_v_den[i];
-
- if (found > img_height_max)
- /* scales increase */
- break;
-
- err = abs(found - geo->output.height);
- if (err < best_err) {
- best_err = err;
- idx = i;
- best = found;
- }
- if (!err)
- break;
- }
-
- geo->output.height = best;
- geo->scale_idx_v = idx;
- if (geo->output.top + best > height_max)
- geo->output.top = height_max - best;
-
- pr_debug("%s(): H %u * %u/%u = %u\n", __func__, geo->in_height,
- vou_scale_v_num[idx], vou_scale_v_den[idx], best);
-}
-
-static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- unsigned int img_height_max;
- int pix_idx;
- struct sh_vou_geometry geo;
- struct v4l2_mbus_framefmt mbfmt = {
- /* Revisit: is this the correct code? */
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
- .field = V4L2_FIELD_INTERLACED,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- };
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
- vou_dev->rect.width, vou_dev->rect.height,
- pix->width, pix->height);
-
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_NONE;
-
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
- if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
- break;
-
- if (pix_idx == ARRAY_SIZE(vou_fmt))
- return -EINVAL;
-
- if (vou_dev->std & V4L2_STD_525_60)
- img_height_max = 480;
- else
- img_height_max = 576;
-
- /* Image width must be a multiple of 4 */
- v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
- &pix->height, 0, img_height_max, 1, 0);
-
- geo.in_width = pix->width;
- geo.in_height = pix->height;
- geo.output = vou_dev->rect;
-
- vou_adjust_output(&geo, vou_dev->std);
-
- mbfmt.width = geo.output.width;
- mbfmt.height = geo.output.height;
- ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
- s_mbus_fmt, &mbfmt);
- /* Must be implemented, so, don't check for -ENOIOCTLCMD */
- if (ret < 0)
- return ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
- geo.output.width, geo.output.height, mbfmt.width, mbfmt.height);
-
- /* Sanity checks */
- if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
- (unsigned)mbfmt.height > img_height_max ||
- mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
- return -EIO;
-
- if (mbfmt.width != geo.output.width ||
- mbfmt.height != geo.output.height) {
- geo.output.width = mbfmt.width;
- geo.output.height = mbfmt.height;
-
- vou_adjust_input(&geo, vou_dev->std);
- }
-
- /* We tried to preserve output rectangle, but it could have changed */
- vou_dev->rect = geo.output;
- pix->width = geo.in_width;
- pix->height = geo.in_height;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u\n", __func__,
- pix->width, pix->height);
-
- vou_dev->pix_idx = pix_idx;
-
- vou_dev->pix = *pix;
-
- sh_vou_configure_geometry(vou_dev, pix_idx,
- geo.scale_idx_h, geo.scale_idx_v);
-
- return 0;
-}
-
-static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct sh_vou_file *vou_file = priv;
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int i;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- pix->field = V4L2_FIELD_NONE;
-
- v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
- &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
-
- for (i = 0; ARRAY_SIZE(vou_fmt); i++)
- if (vou_fmt[i].pfmt == pix->pixelformat)
- return 0;
-
- pix->pixelformat = vou_fmt[0].pfmt;
-
- return 0;
-}
-
-static int sh_vou_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- return videobuf_reqbufs(&vou_file->vbq, req);
-}
-
-static int sh_vou_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- return videobuf_querybuf(&vou_file->vbq, b);
-}
-
-static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- return videobuf_qbuf(&vou_file->vbq, b);
-}
-
-static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
-}
-
-static int sh_vou_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct sh_vou_file *vou_file = priv;
- int ret;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
- video, s_stream, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* This calls our .buf_queue() (== sh_vou_buf_queue) */
- return videobuf_streamon(&vou_file->vbq);
-}
-
-static int sh_vou_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- /*
- * This calls buf_release from host driver's videobuf_queue_ops for all
- * remaining buffers. When the last buffer is freed, stop streaming
- */
- videobuf_streamoff(&vou_file->vbq);
- v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
-
- return 0;
-}
-
-static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
-{
- switch (bus_fmt) {
- default:
- pr_warning("%s(): Invalid bus-format code %d, using default 8-bit\n",
- __func__, bus_fmt);
- case SH_VOU_BUS_8BIT:
- return 1;
- case SH_VOU_BUS_16BIT:
- return 0;
- case SH_VOU_BUS_BT656:
- return 3;
- }
-}
-
-static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id *std_id)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, *std_id);
-
- if (*std_id & ~vdev->tvnorms)
- return -EINVAL;
-
- ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
- s_std_output, *std_id);
- /* Shall we continue, if the subdev doesn't support .s_std_output()? */
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- if (*std_id & V4L2_STD_525_60)
- sh_vou_reg_ab_set(vou_dev, VOUCR,
- sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
- else
- sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
-
- vou_dev->std = *std_id;
-
- return 0;
-}
-
-static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- *std = vou_dev->std;
-
- return 0;
-}
-
-static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- a->c = vou_dev->rect;
-
- return 0;
-}
-
-/* Assume a dull encoder, do all the work ourselves. */
-static int sh_vou_s_crop(struct file *file, void *fh, struct v4l2_crop *a)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct v4l2_rect *rect = &a->c;
- struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
- struct v4l2_pix_format *pix = &vou_dev->pix;
- struct sh_vou_geometry geo;
- struct v4l2_mbus_framefmt mbfmt = {
- /* Revisit: is this the correct code? */
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
- .field = V4L2_FIELD_INTERLACED,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- };
- unsigned int img_height_max;
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
- rect->width, rect->height, rect->left, rect->top);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
- return -EINVAL;
-
- if (vou_dev->std & V4L2_STD_525_60)
- img_height_max = 480;
- else
- img_height_max = 576;
-
- v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
- &rect->height, 0, img_height_max, 1, 0);
-
- if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
- rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
-
- if (rect->height + rect->top > img_height_max)
- rect->top = img_height_max - rect->height;
-
- geo.output = *rect;
- geo.in_width = pix->width;
- geo.in_height = pix->height;
-
- /* Configure the encoder one-to-one, position at 0, ignore errors */
- sd_crop.c.width = geo.output.width;
- sd_crop.c.height = geo.output.height;
- /*
- * We first issue a S_CROP, so that the subsequent S_FMT delivers the
- * final encoder configuration.
- */
- v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
- s_crop, &sd_crop);
- mbfmt.width = geo.output.width;
- mbfmt.height = geo.output.height;
- ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
- s_mbus_fmt, &mbfmt);
- /* Must be implemented, so, don't check for -ENOIOCTLCMD */
- if (ret < 0)
- return ret;
-
- /* Sanity checks */
- if ((unsigned)mbfmt.width > VOU_MAX_IMAGE_WIDTH ||
- (unsigned)mbfmt.height > img_height_max ||
- mbfmt.code != V4L2_MBUS_FMT_YUYV8_2X8)
- return -EIO;
-
- geo.output.width = mbfmt.width;
- geo.output.height = mbfmt.height;
-
- /*
- * No down-scaling. According to the API, current call has precedence:
- * http://v4l2spec.bytesex.org/spec/x1904.htm#AEN1954 paragraph two.
- */
- vou_adjust_input(&geo, vou_dev->std);
-
- /* We tried to preserve output rectangle, but it could have changed */
- vou_dev->rect = geo.output;
- pix->width = geo.in_width;
- pix->height = geo.in_height;
-
- sh_vou_configure_geometry(vou_dev, vou_dev->pix_idx,
- geo.scale_idx_h, geo.scale_idx_v);
-
- return 0;
-}
-
-/*
- * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
- * is the initial register values, height takes the interlaced format into
- * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
- * actually only meaningfully contain values <= 720 and <= 240 respectively, and
- * not <= 864 and <= 312.
- */
-static int sh_vou_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *a)
-{
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = VOU_MAX_IMAGE_WIDTH;
- a->bounds.height = VOU_MAX_IMAGE_HEIGHT;
- /* Default = max, set VOUDPR = 0, which is not hardware default */
- a->defrect.left = 0;
- a->defrect.top = 0;
- a->defrect.width = VOU_MAX_IMAGE_WIDTH;
- a->defrect.height = VOU_MAX_IMAGE_HEIGHT;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static irqreturn_t sh_vou_isr(int irq, void *dev_id)
-{
- struct sh_vou_device *vou_dev = dev_id;
- static unsigned long j;
- struct videobuf_buffer *vb;
- static int cnt;
- static int side;
- u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
- u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
-
- if (!(irq_status & 0x300)) {
- if (printk_timed_ratelimit(&j, 500))
- dev_warn(vou_dev->v4l2_dev.dev, "IRQ status 0x%x!\n",
- irq_status);
- return IRQ_NONE;
- }
-
- spin_lock(&vou_dev->lock);
- if (!vou_dev->active || list_empty(&vou_dev->queue)) {
- if (printk_timed_ratelimit(&j, 500))
- dev_warn(vou_dev->v4l2_dev.dev,
- "IRQ without active buffer: %x!\n", irq_status);
- /* Just ack: buf_release will disable further interrupts */
- sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x300);
- spin_unlock(&vou_dev->lock);
- return IRQ_HANDLED;
- }
-
- masked = ~(0x300 & irq_status) & irq_status & 0x30304;
- dev_dbg(vou_dev->v4l2_dev.dev,
- "IRQ status 0x%x -> 0x%x, VOU status 0x%x, cnt %d\n",
- irq_status, masked, vou_status, cnt);
-
- cnt++;
- side = vou_status & 0x10000;
-
- /* Clear only set interrupts */
- sh_vou_reg_a_write(vou_dev, VOUIR, masked);
-
- vb = vou_dev->active;
- list_del(&vb->queue);
-
- vb->state = VIDEOBUF_DONE;
- do_gettimeofday(&vb->ts);
- vb->field_count++;
- wake_up(&vb->done);
-
- if (list_empty(&vou_dev->queue)) {
- /* Stop VOU */
- dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
- __func__, cnt);
- sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
- vou_dev->active = NULL;
- vou_dev->status = SH_VOU_INITIALISING;
- /* Disable End-of-Frame (VSYNC) interrupts */
- sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
- spin_unlock(&vou_dev->lock);
- return IRQ_HANDLED;
- }
-
- vou_dev->active = list_entry(vou_dev->queue.next,
- struct videobuf_buffer, queue);
-
- if (vou_dev->active->queue.next != &vou_dev->queue) {
- struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
- struct videobuf_buffer, queue);
- sh_vou_schedule_next(vou_dev, new);
- }
-
- spin_unlock(&vou_dev->lock);
-
- return IRQ_HANDLED;
-}
-
-static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
-{
- struct sh_vou_pdata *pdata = vou_dev->pdata;
- u32 voucr = sh_vou_ntsc_mode(pdata->bus_fmt) << 29;
- int i = 100;
-
- /* Disable all IRQs */
- sh_vou_reg_a_write(vou_dev, VOUIR, 0);
-
- /* Reset VOU interfaces - registers unaffected */
- sh_vou_reg_a_write(vou_dev, VOUSRR, 0x101);
- while (--i && (sh_vou_reg_a_read(vou_dev, VOUSRR) & 0x101))
- udelay(1);
-
- if (!i)
- return -ETIMEDOUT;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "Reset took %dus\n", 100 - i);
-
- if (pdata->flags & SH_VOU_PCLK_FALLING)
- voucr |= 1 << 28;
- if (pdata->flags & SH_VOU_HSYNC_LOW)
- voucr |= 1 << 27;
- if (pdata->flags & SH_VOU_VSYNC_LOW)
- voucr |= 1 << 26;
- sh_vou_reg_ab_set(vou_dev, VOUCR, voucr, 0xfc000000);
-
- /* Manual register side switching at first */
- sh_vou_reg_a_write(vou_dev, VOURCR, 4);
- /* Default - fixed HSYNC length, can be made configurable is required */
- sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
-
- return 0;
-}
-
-/* File operations */
-static int sh_vou_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
- GFP_KERNEL);
-
- if (!vou_file)
- return -ENOMEM;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- file->private_data = vou_file;
-
- if (atomic_inc_return(&vou_dev->use_count) == 1) {
- int ret;
- /* First open */
- vou_dev->status = SH_VOU_INITIALISING;
- pm_runtime_get_sync(vdev->v4l2_dev->dev);
- ret = sh_vou_hw_init(vou_dev);
- if (ret < 0) {
- atomic_dec(&vou_dev->use_count);
- pm_runtime_put(vdev->v4l2_dev->dev);
- vou_dev->status = SH_VOU_IDLE;
- return ret;
- }
- }
-
- videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
- vou_dev->v4l2_dev.dev, &vou_dev->lock,
- V4L2_BUF_TYPE_VIDEO_OUTPUT,
- V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer), vdev,
- &vou_dev->fop_lock);
-
- return 0;
-}
-
-static int sh_vou_release(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
- struct sh_vou_file *vou_file = file->private_data;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- if (!atomic_dec_return(&vou_dev->use_count)) {
- /* Last close */
- vou_dev->status = SH_VOU_IDLE;
- sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
- pm_runtime_put(vdev->v4l2_dev->dev);
- }
-
- file->private_data = NULL;
- kfree(vou_file);
-
- return 0;
-}
-
-static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct sh_vou_file *vou_file = file->private_data;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- return videobuf_mmap_mapper(&vou_file->vbq, vma);
-}
-
-static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
-{
- struct sh_vou_file *vou_file = file->private_data;
-
- dev_dbg(vou_file->vbq.dev, "%s()\n", __func__);
-
- return videobuf_poll_stream(file, &vou_file->vbq, wait);
-}
-
-static int sh_vou_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int sh_vou_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg);
-}
-
-static int sh_vou_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct video_device *vdev = video_devdata(file);
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg);
-}
-#endif
-
-/* sh_vou display ioctl operations */
-static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
- .vidioc_querycap = sh_vou_querycap,
- .vidioc_enum_fmt_vid_out = sh_vou_enum_fmt_vid_out,
- .vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,
- .vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,
- .vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,
- .vidioc_reqbufs = sh_vou_reqbufs,
- .vidioc_querybuf = sh_vou_querybuf,
- .vidioc_qbuf = sh_vou_qbuf,
- .vidioc_dqbuf = sh_vou_dqbuf,
- .vidioc_streamon = sh_vou_streamon,
- .vidioc_streamoff = sh_vou_streamoff,
- .vidioc_s_std = sh_vou_s_std,
- .vidioc_g_std = sh_vou_g_std,
- .vidioc_cropcap = sh_vou_cropcap,
- .vidioc_g_crop = sh_vou_g_crop,
- .vidioc_s_crop = sh_vou_s_crop,
- .vidioc_g_chip_ident = sh_vou_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = sh_vou_g_register,
- .vidioc_s_register = sh_vou_s_register,
-#endif
-};
-
-static const struct v4l2_file_operations sh_vou_fops = {
- .owner = THIS_MODULE,
- .open = sh_vou_open,
- .release = sh_vou_release,
- .unlocked_ioctl = video_ioctl2,
- .mmap = sh_vou_mmap,
- .poll = sh_vou_poll,
-};
-
-static const struct video_device sh_vou_video_template = {
- .name = "sh_vou",
- .fops = &sh_vou_fops,
- .ioctl_ops = &sh_vou_ioctl_ops,
- .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static int __devinit sh_vou_probe(struct platform_device *pdev)
-{
- struct sh_vou_pdata *vou_pdata = pdev->dev.platform_data;
- struct v4l2_rect *rect;
- struct v4l2_pix_format *pix;
- struct i2c_adapter *i2c_adap;
- struct video_device *vdev;
- struct sh_vou_device *vou_dev;
- struct resource *reg_res, *region;
- struct v4l2_subdev *subdev;
- int irq, ret;
-
- reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(pdev, 0);
-
- if (!vou_pdata || !reg_res || irq <= 0) {
- dev_err(&pdev->dev, "Insufficient VOU platform information.\n");
- return -ENODEV;
- }
-
- vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
- if (!vou_dev)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&vou_dev->queue);
- spin_lock_init(&vou_dev->lock);
- mutex_init(&vou_dev->fop_lock);
- atomic_set(&vou_dev->use_count, 0);
- vou_dev->pdata = vou_pdata;
- vou_dev->status = SH_VOU_IDLE;
-
- rect = &vou_dev->rect;
- pix = &vou_dev->pix;
-
- /* Fill in defaults */
- vou_dev->std = sh_vou_video_template.current_norm;
- rect->left = 0;
- rect->top = 0;
- rect->width = VOU_MAX_IMAGE_WIDTH;
- rect->height = 480;
- pix->width = VOU_MAX_IMAGE_WIDTH;
- pix->height = 480;
- pix->pixelformat = V4L2_PIX_FMT_YVYU;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2;
- pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480;
- pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- region = request_mem_region(reg_res->start, resource_size(reg_res),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "VOU region already claimed\n");
- ret = -EBUSY;
- goto ereqmemreg;
- }
-
- vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
- if (!vou_dev->base) {
- ret = -ENOMEM;
- goto emap;
- }
-
- ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
- if (ret < 0)
- goto ereqirq;
-
- ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "Error registering v4l2 device\n");
- goto ev4l2devreg;
- }
-
- /* Allocate memory for video device */
- vdev = video_device_alloc();
- if (vdev == NULL) {
- ret = -ENOMEM;
- goto evdevalloc;
- }
-
- *vdev = sh_vou_video_template;
- if (vou_pdata->bus_fmt == SH_VOU_BUS_8BIT)
- vdev->tvnorms |= V4L2_STD_PAL;
- vdev->v4l2_dev = &vou_dev->v4l2_dev;
- vdev->release = video_device_release;
- vdev->lock = &vou_dev->fop_lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
-
- vou_dev->vdev = vdev;
- video_set_drvdata(vdev, vou_dev);
-
- pm_runtime_enable(&pdev->dev);
- pm_runtime_resume(&pdev->dev);
-
- i2c_adap = i2c_get_adapter(vou_pdata->i2c_adap);
- if (!i2c_adap) {
- ret = -ENODEV;
- goto ei2cgadap;
- }
-
- ret = sh_vou_hw_init(vou_dev);
- if (ret < 0)
- goto ereset;
-
- subdev = v4l2_i2c_new_subdev_board(&vou_dev->v4l2_dev, i2c_adap,
- vou_pdata->board_info, NULL);
- if (!subdev) {
- ret = -ENOMEM;
- goto ei2cnd;
- }
-
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0)
- goto evregdev;
-
- return 0;
-
-evregdev:
-ei2cnd:
-ereset:
- i2c_put_adapter(i2c_adap);
-ei2cgadap:
- video_device_release(vdev);
- pm_runtime_disable(&pdev->dev);
-evdevalloc:
- v4l2_device_unregister(&vou_dev->v4l2_dev);
-ev4l2devreg:
- free_irq(irq, vou_dev);
-ereqirq:
- iounmap(vou_dev->base);
-emap:
- release_mem_region(reg_res->start, resource_size(reg_res));
-ereqmemreg:
- kfree(vou_dev);
- return ret;
-}
-
-static int __devexit sh_vou_remove(struct platform_device *pdev)
-{
- int irq = platform_get_irq(pdev, 0);
- struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
- struct sh_vou_device *vou_dev = container_of(v4l2_dev,
- struct sh_vou_device, v4l2_dev);
- struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
- struct v4l2_subdev, list);
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct resource *reg_res;
-
- if (irq > 0)
- free_irq(irq, vou_dev);
- pm_runtime_disable(&pdev->dev);
- video_unregister_device(vou_dev->vdev);
- i2c_put_adapter(client->adapter);
- v4l2_device_unregister(&vou_dev->v4l2_dev);
- iounmap(vou_dev->base);
- reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (reg_res)
- release_mem_region(reg_res->start, resource_size(reg_res));
- kfree(vou_dev);
- return 0;
-}
-
-static struct platform_driver __refdata sh_vou = {
- .remove = __devexit_p(sh_vou_remove),
- .driver = {
- .name = "sh-vou",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init sh_vou_init(void)
-{
- return platform_driver_probe(&sh_vou, sh_vou_probe);
-}
-
-static void __exit sh_vou_exit(void)
-{
- platform_driver_unregister(&sh_vou);
-}
-
-module_init(sh_vou_init);
-module_exit(sh_vou_exit);
-
-MODULE_DESCRIPTION("SuperH VOU driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
-MODULE_LICENSE("GPL v2");
-MODULE_VERSION("0.1.0");
-MODULE_ALIAS("platform:sh-vou");
diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c
deleted file mode 100644
index a2e41a21dc65..000000000000
--- a/drivers/media/video/smiapp-pll.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * drivers/media/video/smiapp-pll.c
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/gcd.h>
-#include <linux/lcm.h>
-#include <linux/module.h>
-
-#include "smiapp-pll.h"
-
-/* Return an even number or one. */
-static inline uint32_t clk_div_even(uint32_t a)
-{
- return max_t(uint32_t, 1, a & ~1);
-}
-
-/* Return an even number or one. */
-static inline uint32_t clk_div_even_up(uint32_t a)
-{
- if (a == 1)
- return 1;
- return (a + 1) & ~1;
-}
-
-static inline uint32_t is_one_or_even(uint32_t a)
-{
- if (a == 1)
- return 1;
- if (a & 1)
- return 0;
-
- return 1;
-}
-
-static int bounds_check(struct device *dev, uint32_t val,
- uint32_t min, uint32_t max, char *str)
-{
- if (val >= min && val <= max)
- return 0;
-
- dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
-
- return -EINVAL;
-}
-
-static void print_pll(struct device *dev, struct smiapp_pll *pll)
-{
- dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div);
- dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier);
- if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
- dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div);
- dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div);
- }
- dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div);
- dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div);
-
- dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz);
- dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz);
- dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz);
- if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
- dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n",
- pll->op_sys_clk_freq_hz);
- dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n",
- pll->op_pix_clk_freq_hz);
- }
- dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz);
- dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
-}
-
-int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
- struct smiapp_pll *pll)
-{
- uint32_t sys_div;
- uint32_t best_pix_div = INT_MAX >> 1;
- uint32_t vt_op_binning_div;
- uint32_t lane_op_clock_ratio;
- uint32_t mul, div;
- uint32_t more_mul_min, more_mul_max;
- uint32_t more_mul_factor;
- uint32_t min_vt_div, max_vt_div, vt_div;
- uint32_t min_sys_div, max_sys_div;
- unsigned int i;
- int rval;
-
- if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
- lane_op_clock_ratio = pll->lanes;
- else
- lane_op_clock_ratio = 1;
- dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
-
- dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
- pll->binning_vertical);
-
- /* CSI transfers 2 bits per clock per lane; thus times 2 */
- pll->pll_op_clk_freq_hz = pll->link_freq * 2
- * (pll->lanes / lane_op_clock_ratio);
-
- /* Figure out limits for pre-pll divider based on extclk */
- dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
- limits->max_pre_pll_clk_div =
- min_t(uint16_t, limits->max_pre_pll_clk_div,
- clk_div_even(pll->ext_clk_freq_hz /
- limits->min_pll_ip_freq_hz));
- limits->min_pre_pll_clk_div =
- max_t(uint16_t, limits->min_pre_pll_clk_div,
- clk_div_even_up(
- DIV_ROUND_UP(pll->ext_clk_freq_hz,
- limits->max_pll_ip_freq_hz)));
- dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
-
- i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
- mul = div_u64(pll->pll_op_clk_freq_hz, i);
- div = pll->ext_clk_freq_hz / i;
- dev_dbg(dev, "mul %d / div %d\n", mul, div);
-
- limits->min_pre_pll_clk_div =
- max_t(uint16_t, limits->min_pre_pll_clk_div,
- clk_div_even_up(
- DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
- limits->max_pll_op_freq_hz)));
- dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
- limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
-
- if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) {
- dev_err(dev, "unable to compute pre_pll divisor\n");
- return -EINVAL;
- }
-
- pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;
-
- /*
- * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
- * too high.
- */
- dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div);
-
- /* Don't go above max pll multiplier. */
- more_mul_max = limits->max_pll_multiplier / mul;
- dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n",
- more_mul_max);
- /* Don't go above max pll op frequency. */
- more_mul_max =
- min_t(int,
- more_mul_max,
- limits->max_pll_op_freq_hz
- / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
- dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n",
- more_mul_max);
- /* Don't go above the division capability of op sys clock divider. */
- more_mul_max = min(more_mul_max,
- limits->max_op_sys_clk_div * pll->pre_pll_clk_div
- / div);
- dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n",
- more_mul_max);
- /* Ensure we won't go above min_pll_multiplier. */
- more_mul_max = min(more_mul_max,
- DIV_ROUND_UP(limits->max_pll_multiplier, mul));
- dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n",
- more_mul_max);
-
- /* Ensure we won't go below min_pll_op_freq_hz. */
- more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
- pll->ext_clk_freq_hz / pll->pre_pll_clk_div
- * mul);
- dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n",
- more_mul_min);
- /* Ensure we won't go below min_pll_multiplier. */
- more_mul_min = max(more_mul_min,
- DIV_ROUND_UP(limits->min_pll_multiplier, mul));
- dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n",
- more_mul_min);
-
- if (more_mul_min > more_mul_max) {
- dev_warn(dev,
- "unable to compute more_mul_min and more_mul_max");
- return -EINVAL;
- }
-
- more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
- dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor);
- more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div);
- dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
- more_mul_factor);
- i = roundup(more_mul_min, more_mul_factor);
- if (!is_one_or_even(i))
- i <<= 1;
-
- dev_dbg(dev, "final more_mul: %d\n", i);
- if (i > more_mul_max) {
- dev_warn(dev, "final more_mul is bad, max %d", more_mul_max);
- return -EINVAL;
- }
-
- pll->pll_multiplier = mul * i;
- pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div;
- dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div);
-
- pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
- / pll->pre_pll_clk_div;
-
- pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
- * pll->pll_multiplier;
-
- /* Derive pll_op_clk_freq_hz. */
- pll->op_sys_clk_freq_hz =
- pll->pll_op_clk_freq_hz / pll->op_sys_clk_div;
-
- pll->op_pix_clk_div = pll->bits_per_pixel;
- dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div);
-
- pll->op_pix_clk_freq_hz =
- pll->op_sys_clk_freq_hz / pll->op_pix_clk_div;
-
- /*
- * Some sensors perform analogue binning and some do this
- * digitally. The ones doing this digitally can be roughly be
- * found out using this formula. The ones doing this digitally
- * should run at higher clock rate, so smaller divisor is used
- * on video timing side.
- */
- if (limits->min_line_length_pck_bin > limits->min_line_length_pck
- / pll->binning_horizontal)
- vt_op_binning_div = pll->binning_horizontal;
- else
- vt_op_binning_div = 1;
- dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div);
-
- /*
- * Profile 2 supports vt_pix_clk_div E [4, 10]
- *
- * Horizontal binning can be used as a base for difference in
- * divisors. One must make sure that horizontal blanking is
- * enough to accommodate the CSI-2 sync codes.
- *
- * Take scaling factor into account as well.
- *
- * Find absolute limits for the factor of vt divider.
- */
- dev_dbg(dev, "scale_m: %d\n", pll->scale_m);
- min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div
- * pll->scale_n,
- lane_op_clock_ratio * vt_op_binning_div
- * pll->scale_m);
-
- /* Find smallest and biggest allowed vt divisor. */
- dev_dbg(dev, "min_vt_div: %d\n", min_vt_div);
- min_vt_div = max(min_vt_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->max_vt_pix_clk_freq_hz));
- dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n",
- min_vt_div);
- min_vt_div = max_t(uint32_t, min_vt_div,
- limits->min_vt_pix_clk_div
- * limits->min_vt_sys_clk_div);
- dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div);
-
- max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div;
- dev_dbg(dev, "max_vt_div: %d\n", max_vt_div);
- max_vt_div = min(max_vt_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz));
- dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n",
- max_vt_div);
-
- /*
- * Find limitsits for sys_clk_div. Not all values are possible
- * with all values of pix_clk_div.
- */
- min_sys_div = limits->min_vt_sys_clk_div;
- dev_dbg(dev, "min_sys_div: %d\n", min_sys_div);
- min_sys_div = max(min_sys_div,
- DIV_ROUND_UP(min_vt_div,
- limits->max_vt_pix_clk_div));
- dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div);
- min_sys_div = max(min_sys_div,
- pll->pll_op_clk_freq_hz
- / limits->max_vt_sys_clk_freq_hz);
- dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div);
- min_sys_div = clk_div_even_up(min_sys_div);
- dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div);
-
- max_sys_div = limits->max_vt_sys_clk_div;
- dev_dbg(dev, "max_sys_div: %d\n", max_sys_div);
- max_sys_div = min(max_sys_div,
- DIV_ROUND_UP(max_vt_div,
- limits->min_vt_pix_clk_div));
- dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div);
- max_sys_div = min(max_sys_div,
- DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz));
- dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div);
-
- /*
- * Find pix_div such that a legal pix_div * sys_div results
- * into a value which is not smaller than div, the desired
- * divisor.
- */
- for (vt_div = min_vt_div; vt_div <= max_vt_div;
- vt_div += 2 - (vt_div & 1)) {
- for (sys_div = min_sys_div;
- sys_div <= max_sys_div;
- sys_div += 2 - (sys_div & 1)) {
- int pix_div = DIV_ROUND_UP(vt_div, sys_div);
-
- if (pix_div < limits->min_vt_pix_clk_div
- || pix_div > limits->max_vt_pix_clk_div) {
- dev_dbg(dev,
- "pix_div %d too small or too big (%d--%d)\n",
- pix_div,
- limits->min_vt_pix_clk_div,
- limits->max_vt_pix_clk_div);
- continue;
- }
-
- /* Check if this one is better. */
- if (pix_div * sys_div
- <= roundup(min_vt_div, best_pix_div))
- best_pix_div = pix_div;
- }
- if (best_pix_div < INT_MAX >> 1)
- break;
- }
-
- pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
- pll->vt_pix_clk_div = best_pix_div;
-
- pll->vt_sys_clk_freq_hz =
- pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div;
- pll->vt_pix_clk_freq_hz =
- pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div;
-
- pll->pixel_rate_csi =
- pll->op_pix_clk_freq_hz * lane_op_clock_ratio;
-
- print_pll(dev, pll);
-
- rval = bounds_check(dev, pll->pre_pll_clk_div,
- limits->min_pre_pll_clk_div,
- limits->max_pre_pll_clk_div, "pre_pll_clk_div");
- if (!rval)
- rval = bounds_check(
- dev, pll->pll_ip_clk_freq_hz,
- limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz,
- "pll_ip_clk_freq_hz");
- if (!rval)
- rval = bounds_check(
- dev, pll->pll_multiplier,
- limits->min_pll_multiplier, limits->max_pll_multiplier,
- "pll_multiplier");
- if (!rval)
- rval = bounds_check(
- dev, pll->pll_op_clk_freq_hz,
- limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz,
- "pll_op_clk_freq_hz");
- if (!rval)
- rval = bounds_check(
- dev, pll->op_sys_clk_div,
- limits->min_op_sys_clk_div, limits->max_op_sys_clk_div,
- "op_sys_clk_div");
- if (!rval)
- rval = bounds_check(
- dev, pll->op_pix_clk_div,
- limits->min_op_pix_clk_div, limits->max_op_pix_clk_div,
- "op_pix_clk_div");
- if (!rval)
- rval = bounds_check(
- dev, pll->op_sys_clk_freq_hz,
- limits->min_op_sys_clk_freq_hz,
- limits->max_op_sys_clk_freq_hz,
- "op_sys_clk_freq_hz");
- if (!rval)
- rval = bounds_check(
- dev, pll->op_pix_clk_freq_hz,
- limits->min_op_pix_clk_freq_hz,
- limits->max_op_pix_clk_freq_hz,
- "op_pix_clk_freq_hz");
- if (!rval)
- rval = bounds_check(
- dev, pll->vt_sys_clk_freq_hz,
- limits->min_vt_sys_clk_freq_hz,
- limits->max_vt_sys_clk_freq_hz,
- "vt_sys_clk_freq_hz");
- if (!rval)
- rval = bounds_check(
- dev, pll->vt_pix_clk_freq_hz,
- limits->min_vt_pix_clk_freq_hz,
- limits->max_vt_pix_clk_freq_hz,
- "vt_pix_clk_freq_hz");
-
- return rval;
-}
-EXPORT_SYMBOL_GPL(smiapp_pll_calculate);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
-MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h
deleted file mode 100644
index 9eab63f23afb..000000000000
--- a/drivers/media/video/smiapp-pll.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * drivers/media/video/smiapp-pll.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef SMIAPP_PLL_H
-#define SMIAPP_PLL_H
-
-#include <linux/device.h>
-
-struct smiapp_pll {
- uint8_t lanes;
- uint8_t binning_horizontal;
- uint8_t binning_vertical;
- uint8_t scale_m;
- uint8_t scale_n;
- uint8_t bits_per_pixel;
- uint16_t flags;
- uint32_t link_freq;
-
- uint16_t pre_pll_clk_div;
- uint16_t pll_multiplier;
- uint16_t op_sys_clk_div;
- uint16_t op_pix_clk_div;
- uint16_t vt_sys_clk_div;
- uint16_t vt_pix_clk_div;
-
- uint32_t ext_clk_freq_hz;
- uint32_t pll_ip_clk_freq_hz;
- uint32_t pll_op_clk_freq_hz;
- uint32_t op_sys_clk_freq_hz;
- uint32_t op_pix_clk_freq_hz;
- uint32_t vt_sys_clk_freq_hz;
- uint32_t vt_pix_clk_freq_hz;
-
- uint32_t pixel_rate_csi;
-};
-
-struct smiapp_pll_limits {
- /* Strict PLL limits */
- uint32_t min_ext_clk_freq_hz;
- uint32_t max_ext_clk_freq_hz;
- uint16_t min_pre_pll_clk_div;
- uint16_t max_pre_pll_clk_div;
- uint32_t min_pll_ip_freq_hz;
- uint32_t max_pll_ip_freq_hz;
- uint16_t min_pll_multiplier;
- uint16_t max_pll_multiplier;
- uint32_t min_pll_op_freq_hz;
- uint32_t max_pll_op_freq_hz;
-
- uint16_t min_vt_sys_clk_div;
- uint16_t max_vt_sys_clk_div;
- uint32_t min_vt_sys_clk_freq_hz;
- uint32_t max_vt_sys_clk_freq_hz;
- uint16_t min_vt_pix_clk_div;
- uint16_t max_vt_pix_clk_div;
- uint32_t min_vt_pix_clk_freq_hz;
- uint32_t max_vt_pix_clk_freq_hz;
-
- uint16_t min_op_sys_clk_div;
- uint16_t max_op_sys_clk_div;
- uint32_t min_op_sys_clk_freq_hz;
- uint32_t max_op_sys_clk_freq_hz;
- uint16_t min_op_pix_clk_div;
- uint16_t max_op_pix_clk_div;
- uint32_t min_op_pix_clk_freq_hz;
- uint32_t max_op_pix_clk_freq_hz;
-
- /* Other relevant limits */
- uint32_t min_line_length_pck_bin;
- uint32_t min_line_length_pck;
-};
-
-/* op pix clock is for all lanes in total normally */
-#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
-#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
-
-struct device;
-
-int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
- struct smiapp_pll *pll);
-
-#endif /* SMIAPP_PLL_H */
diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile
deleted file mode 100644
index 36b0cfa2c541..000000000000
--- a/drivers/media/video/smiapp/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-smiapp-objs += smiapp-core.o smiapp-regs.o \
- smiapp-quirk.o smiapp-limits.o
-obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o
-
-ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c
deleted file mode 100644
index bfd47c106134..000000000000
--- a/drivers/media/video/smiapp/smiapp-core.c
+++ /dev/null
@@ -1,2895 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-core.c
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
- *
- * Based on smiapp driver by Vimarsh Zutshi
- * Based on jt8ev1.c by Vimarsh Zutshi
- * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/regulator/consumer.h>
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-device.h>
-
-#include "smiapp.h"
-
-#define SMIAPP_ALIGN_DIM(dim, flags) \
- ((flags) & V4L2_SEL_FLAG_GE \
- ? ALIGN((dim), 2) \
- : (dim) & ~1)
-
-/*
- * smiapp_module_idents - supported camera modules
- */
-static const struct smiapp_module_ident smiapp_module_idents[] = {
- SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"),
- SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"),
- SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"),
- SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"),
- SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"),
- SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk),
- SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"),
- SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"),
- SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk),
- SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk),
- SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk),
-};
-
-/*
- *
- * Dynamic Capability Identification
- *
- */
-
-static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc;
- unsigned int i;
- int rval;
- int line_count = 0;
- int embedded_start = -1, embedded_end = -1;
- int image_start = 0;
-
- rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE,
- &fmt_model_type);
- if (rval)
- return rval;
-
- rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE,
- &fmt_model_subtype);
- if (rval)
- return rval;
-
- ncol_desc = (fmt_model_subtype
- & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK)
- >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT;
- nrow_desc = fmt_model_subtype
- & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK;
-
- dev_dbg(&client->dev, "format_model_type %s\n",
- fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE
- ? "2 byte" :
- fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE
- ? "4 byte" : "is simply bad");
-
- for (i = 0; i < ncol_desc + nrow_desc; i++) {
- u32 desc;
- u32 pixelcode;
- u32 pixels;
- char *which;
- char *what;
-
- if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) {
- rval = smiapp_read(
- sensor,
- SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i),
- &desc);
- if (rval)
- return rval;
-
- pixelcode =
- (desc
- & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK)
- >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT;
- pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK;
- } else if (fmt_model_type
- == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) {
- rval = smiapp_read(
- sensor,
- SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i),
- &desc);
- if (rval)
- return rval;
-
- pixelcode =
- (desc
- & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK)
- >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT;
- pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK;
- } else {
- dev_dbg(&client->dev,
- "invalid frame format model type %d\n",
- fmt_model_type);
- return -EINVAL;
- }
-
- if (i < ncol_desc)
- which = "columns";
- else
- which = "rows";
-
- switch (pixelcode) {
- case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED:
- what = "embedded";
- break;
- case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY:
- what = "dummy";
- break;
- case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK:
- what = "black";
- break;
- case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK:
- what = "dark";
- break;
- case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE:
- what = "visible";
- break;
- default:
- what = "invalid";
- dev_dbg(&client->dev, "pixelcode %d\n", pixelcode);
- break;
- }
-
- dev_dbg(&client->dev, "%s pixels: %d %s\n",
- what, pixels, which);
-
- if (i < ncol_desc)
- continue;
-
- /* Handle row descriptors */
- if (pixelcode
- == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) {
- embedded_start = line_count;
- } else {
- if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE
- || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2)
- image_start = line_count;
- if (embedded_start != -1 && embedded_end == -1)
- embedded_end = line_count;
- }
- line_count += pixels;
- }
-
- if (embedded_start == -1 || embedded_end == -1) {
- embedded_start = 0;
- embedded_end = 0;
- }
-
- dev_dbg(&client->dev, "embedded data from lines %d to %d\n",
- embedded_start, embedded_end);
- dev_dbg(&client->dev, "image data starts at line %d\n", image_start);
-
- return 0;
-}
-
-static int smiapp_pll_configure(struct smiapp_sensor *sensor)
-{
- struct smiapp_pll *pll = &sensor->pll;
- int rval;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div);
- if (rval < 0)
- return rval;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div);
- if (rval < 0)
- return rval;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div);
- if (rval < 0)
- return rval;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier);
- if (rval < 0)
- return rval;
-
- /* Lane op clock ratio does not apply here. */
- rval = smiapp_write(
- sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS,
- DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256));
- if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
- return rval;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div);
- if (rval < 0)
- return rval;
-
- return smiapp_write(
- sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div);
-}
-
-static int smiapp_pll_update(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- struct smiapp_pll_limits lim = {
- .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV],
- .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV],
- .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ],
- .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ],
- .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER],
- .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER],
- .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ],
- .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ],
-
- .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV],
- .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV],
- .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV],
- .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV],
- .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ],
- .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ],
- .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ],
- .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ],
-
- .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV],
- .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV],
- .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV],
- .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV],
- .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ],
- .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ],
- .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ],
- .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ],
-
- .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
- .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
- };
- struct smiapp_pll *pll = &sensor->pll;
- int rval;
-
- memset(&sensor->pll, 0, sizeof(sensor->pll));
-
- pll->lanes = sensor->platform_data->lanes;
- pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
-
- if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
- /*
- * Fill in operational clock divisors limits from the
- * video timing ones. On profile 0 sensors the
- * requirements regarding them are essentially the
- * same as on VT ones.
- */
- lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div;
- lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div;
- lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div;
- lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div;
- lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz;
- lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz;
- lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz;
- lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz;
- /* Profile 0 sensors have no separate OP clock branch. */
- pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
- }
-
- if (smiapp_needs_quirk(sensor,
- SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
- pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
-
- pll->binning_horizontal = sensor->binning_horizontal;
- pll->binning_vertical = sensor->binning_vertical;
- pll->link_freq =
- sensor->link_freq->qmenu_int[sensor->link_freq->val];
- pll->scale_m = sensor->scale_m;
- pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
- pll->bits_per_pixel = sensor->csi_format->compressed;
-
- rval = smiapp_pll_calculate(&client->dev, &lim, pll);
- if (rval < 0)
- return rval;
-
- sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz;
- sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi;
-
- return 0;
-}
-
-
-/*
- *
- * V4L2 Controls handling
- *
- */
-
-static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor)
-{
- struct v4l2_ctrl *ctrl = sensor->exposure;
- int max;
-
- max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
- + sensor->vblank->val
- - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN];
-
- ctrl->maximum = max;
- if (ctrl->default_value > max)
- ctrl->default_value = max;
- if (ctrl->val > max)
- ctrl->val = max;
- if (ctrl->cur.val > max)
- ctrl->cur.val = max;
-}
-
-/*
- * Order matters.
- *
- * 1. Bits-per-pixel, descending.
- * 2. Bits-per-pixel compressed, descending.
- * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel
- * orders must be defined.
- */
-static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = {
- { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
- { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
- { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
- { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
- { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
-};
-
-const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
-
-#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \
- - (unsigned long)smiapp_csi_data_formats) \
- / sizeof(*smiapp_csi_data_formats))
-
-static u32 smiapp_pixel_order(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int flip = 0;
-
- if (sensor->hflip) {
- if (sensor->hflip->val)
- flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
-
- if (sensor->vflip->val)
- flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
- }
-
- flip ^= sensor->hvflip_inv_mask;
-
- dev_dbg(&client->dev, "flip %d\n", flip);
- return sensor->default_pixel_order ^ flip;
-}
-
-static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int csi_format_idx =
- to_csi_format_idx(sensor->csi_format) & ~3;
- unsigned int internal_csi_format_idx =
- to_csi_format_idx(sensor->internal_csi_format) & ~3;
- unsigned int pixel_order = smiapp_pixel_order(sensor);
-
- sensor->mbus_frame_fmts =
- sensor->default_mbus_frame_fmts << pixel_order;
- sensor->csi_format =
- &smiapp_csi_data_formats[csi_format_idx + pixel_order];
- sensor->internal_csi_format =
- &smiapp_csi_data_formats[internal_csi_format_idx
- + pixel_order];
-
- BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order
- >= ARRAY_SIZE(smiapp_csi_data_formats));
- BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0);
-
- dev_dbg(&client->dev, "new pixel order %s\n",
- pixel_order_str[pixel_order]);
-}
-
-static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct smiapp_sensor *sensor =
- container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler)
- ->sensor;
- u32 orient = 0;
- int exposure;
- int rval;
-
- switch (ctrl->id) {
- case V4L2_CID_ANALOGUE_GAIN:
- return smiapp_write(
- sensor,
- SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val);
-
- case V4L2_CID_EXPOSURE:
- return smiapp_write(
- sensor,
- SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val);
-
- case V4L2_CID_HFLIP:
- case V4L2_CID_VFLIP:
- if (sensor->streaming)
- return -EBUSY;
-
- if (sensor->hflip->val)
- orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
-
- if (sensor->vflip->val)
- orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
-
- orient ^= sensor->hvflip_inv_mask;
- rval = smiapp_write(sensor,
- SMIAPP_REG_U8_IMAGE_ORIENTATION,
- orient);
- if (rval < 0)
- return rval;
-
- smiapp_update_mbus_formats(sensor);
-
- return 0;
-
- case V4L2_CID_VBLANK:
- exposure = sensor->exposure->val;
-
- __smiapp_update_exposure_limits(sensor);
-
- if (exposure > sensor->exposure->maximum) {
- sensor->exposure->val =
- sensor->exposure->maximum;
- rval = smiapp_set_ctrl(
- sensor->exposure);
- if (rval < 0)
- return rval;
- }
-
- return smiapp_write(
- sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
- + ctrl->val);
-
- case V4L2_CID_HBLANK:
- return smiapp_write(
- sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
- + ctrl->val);
-
- case V4L2_CID_LINK_FREQ:
- if (sensor->streaming)
- return -EBUSY;
-
- return smiapp_pll_update(sensor);
-
- default:
- return -EINVAL;
- }
-}
-
-static const struct v4l2_ctrl_ops smiapp_ctrl_ops = {
- .s_ctrl = smiapp_set_ctrl,
-};
-
-static int smiapp_init_controls(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int max;
- int rval;
-
- rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7);
- if (rval)
- return rval;
- sensor->pixel_array->ctrl_handler.lock = &sensor->mutex;
-
- sensor->analog_gain = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_ANALOGUE_GAIN,
- sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN],
- sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX],
- max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U),
- sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]);
-
- /* Exposure limits will be updated soon, use just something here. */
- sensor->exposure = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_EXPOSURE, 0, 0, 1, 0);
-
- sensor->hflip = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
- sensor->vflip = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
-
- sensor->vblank = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_VBLANK, 0, 1, 1, 0);
-
- if (sensor->vblank)
- sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE;
-
- sensor->hblank = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_HBLANK, 0, 1, 1, 0);
-
- if (sensor->hblank)
- sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE;
-
- sensor->pixel_rate_parray = v4l2_ctrl_new_std(
- &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
-
- if (sensor->pixel_array->ctrl_handler.error) {
- dev_err(&client->dev,
- "pixel array controls initialization failed (%d)\n",
- sensor->pixel_array->ctrl_handler.error);
- rval = sensor->pixel_array->ctrl_handler.error;
- goto error;
- }
-
- sensor->pixel_array->sd.ctrl_handler =
- &sensor->pixel_array->ctrl_handler;
-
- v4l2_ctrl_cluster(2, &sensor->hflip);
-
- rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0);
- if (rval)
- goto error;
- sensor->src->ctrl_handler.lock = &sensor->mutex;
-
- for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++);
-
- sensor->link_freq = v4l2_ctrl_new_int_menu(
- &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_LINK_FREQ, max, 0,
- sensor->platform_data->op_sys_clock);
-
- sensor->pixel_rate_csi = v4l2_ctrl_new_std(
- &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
- V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
-
- if (sensor->src->ctrl_handler.error) {
- dev_err(&client->dev,
- "src controls initialization failed (%d)\n",
- sensor->src->ctrl_handler.error);
- rval = sensor->src->ctrl_handler.error;
- goto error;
- }
-
- sensor->src->sd.ctrl_handler =
- &sensor->src->ctrl_handler;
-
- return 0;
-
-error:
- v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler);
- v4l2_ctrl_handler_free(&sensor->src->ctrl_handler);
-
- return rval;
-}
-
-static void smiapp_free_controls(struct smiapp_sensor *sensor)
-{
- unsigned int i;
-
- for (i = 0; i < sensor->ssds_used; i++)
- v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler);
-}
-
-static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,
- unsigned int n)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int i;
- u32 val;
- int rval;
-
- for (i = 0; i < n; i++) {
- rval = smiapp_read(
- sensor, smiapp_reg_limits[limit[i]].addr, &val);
- if (rval)
- return rval;
- sensor->limits[limit[i]] = val;
- dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n",
- smiapp_reg_limits[limit[i]].addr,
- smiapp_reg_limits[limit[i]].what, val, val);
- }
-
- return 0;
-}
-
-static int smiapp_get_all_limits(struct smiapp_sensor *sensor)
-{
- unsigned int i;
- int rval;
-
- for (i = 0; i < SMIAPP_LIMIT_LAST; i++) {
- rval = smiapp_get_limits(sensor, &i, 1);
- if (rval < 0)
- return rval;
- }
-
- if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0)
- smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16);
-
- return 0;
-}
-
-static int smiapp_get_limits_binning(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- static u32 const limits[] = {
- SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN,
- SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN,
- SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN,
- SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN,
- SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN,
- SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN,
- SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN,
- };
- static u32 const limits_replace[] = {
- SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES,
- SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES,
- SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK,
- SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK,
- SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK,
- SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN,
- SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN,
- };
- unsigned int i;
- int rval;
-
- if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] ==
- SMIAPP_BINNING_CAPABILITY_NO) {
- for (i = 0; i < ARRAY_SIZE(limits); i++)
- sensor->limits[limits[i]] =
- sensor->limits[limits_replace[i]];
-
- return 0;
- }
-
- rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits));
- if (rval < 0)
- return rval;
-
- /*
- * Sanity check whether the binning limits are valid. If not,
- * use the non-binning ones.
- */
- if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]
- && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]
- && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN])
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(limits); i++) {
- dev_dbg(&client->dev,
- "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n",
- smiapp_reg_limits[limits[i]].addr,
- smiapp_reg_limits[limits[i]].what,
- sensor->limits[limits_replace[i]],
- sensor->limits[limits_replace[i]]);
- sensor->limits[limits[i]] =
- sensor->limits[limits_replace[i]];
- }
-
- return 0;
-}
-
-static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int type, n;
- unsigned int i, pixel_order;
- int rval;
-
- rval = smiapp_read(
- sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type);
- if (rval)
- return rval;
-
- dev_dbg(&client->dev, "data_format_model_type %d\n", type);
-
- rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER,
- &pixel_order);
- if (rval)
- return rval;
-
- if (pixel_order >= ARRAY_SIZE(pixel_order_str)) {
- dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order);
- return -EINVAL;
- }
-
- dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order,
- pixel_order_str[pixel_order]);
-
- switch (type) {
- case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL:
- n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N;
- break;
- case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED:
- n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N;
- break;
- default:
- return -EINVAL;
- }
-
- sensor->default_pixel_order = pixel_order;
- sensor->mbus_frame_fmts = 0;
-
- for (i = 0; i < n; i++) {
- unsigned int fmt, j;
-
- rval = smiapp_read(
- sensor,
- SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt);
- if (rval)
- return rval;
-
- dev_dbg(&client->dev, "bpp %d, compressed %d\n",
- fmt >> 8, (u8)fmt);
-
- for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {
- const struct smiapp_csi_data_format *f =
- &smiapp_csi_data_formats[j];
-
- if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG)
- continue;
-
- if (f->width != fmt >> 8 || f->compressed != (u8)fmt)
- continue;
-
- dev_dbg(&client->dev, "jolly good! %d\n", j);
-
- sensor->default_mbus_frame_fmts |= 1 << j;
- if (!sensor->csi_format) {
- sensor->csi_format = f;
- sensor->internal_csi_format = f;
- }
- }
- }
-
- if (!sensor->csi_format) {
- dev_err(&client->dev, "no supported mbus code found\n");
- return -EINVAL;
- }
-
- smiapp_update_mbus_formats(sensor);
-
- return 0;
-}
-
-static void smiapp_update_blanking(struct smiapp_sensor *sensor)
-{
- struct v4l2_ctrl *vblank = sensor->vblank;
- struct v4l2_ctrl *hblank = sensor->hblank;
-
- vblank->minimum =
- max_t(int,
- sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES],
- sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] -
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height);
- vblank->maximum =
- sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] -
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height;
-
- vblank->val = clamp_t(int, vblank->val,
- vblank->minimum, vblank->maximum);
- vblank->default_value = vblank->minimum;
- vblank->val = vblank->val;
- vblank->cur.val = vblank->val;
-
- hblank->minimum =
- max_t(int,
- sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] -
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width,
- sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]);
- hblank->maximum =
- sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] -
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width;
-
- hblank->val = clamp_t(int, hblank->val,
- hblank->minimum, hblank->maximum);
- hblank->default_value = hblank->minimum;
- hblank->val = hblank->val;
- hblank->cur.val = hblank->val;
-
- __smiapp_update_exposure_limits(sensor);
-}
-
-static int smiapp_update_mode(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int binning_mode;
- int rval;
-
- dev_dbg(&client->dev, "frame size: %dx%d\n",
- sensor->src->crop[SMIAPP_PAD_SRC].width,
- sensor->src->crop[SMIAPP_PAD_SRC].height);
- dev_dbg(&client->dev, "csi format width: %d\n",
- sensor->csi_format->width);
-
- /* Binning has to be set up here; it affects limits */
- if (sensor->binning_horizontal == 1 &&
- sensor->binning_vertical == 1) {
- binning_mode = 0;
- } else {
- u8 binning_type =
- (sensor->binning_horizontal << 4)
- | sensor->binning_vertical;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type);
- if (rval < 0)
- return rval;
-
- binning_mode = 1;
- }
- rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode);
- if (rval < 0)
- return rval;
-
- /* Get updated limits due to binning */
- rval = smiapp_get_limits_binning(sensor);
- if (rval < 0)
- return rval;
-
- rval = smiapp_pll_update(sensor);
- if (rval < 0)
- return rval;
-
- /* Output from pixel array, including blanking */
- smiapp_update_blanking(sensor);
-
- dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val);
- dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
-
- dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
- sensor->pll.vt_pix_clk_freq_hz /
- ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
- + sensor->hblank->val) *
- (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
- + sensor->vblank->val) / 100));
-
- return 0;
-}
-
-/*
- *
- * SMIA++ NVM handling
- *
- */
-static int smiapp_read_nvm(struct smiapp_sensor *sensor,
- unsigned char *nvm)
-{
- u32 i, s, p, np, v;
- int rval = 0, rval2;
-
- np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE;
- for (p = 0; p < np; p++) {
- rval = smiapp_write(
- sensor,
- SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p);
- if (rval)
- goto out;
-
- rval = smiapp_write(sensor,
- SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL,
- SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN |
- SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN);
- if (rval)
- goto out;
-
- for (i = 0; i < 1000; i++) {
- rval = smiapp_read(
- sensor,
- SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s);
-
- if (rval)
- goto out;
-
- if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY)
- break;
-
- if (--i == 0) {
- rval = -ETIMEDOUT;
- goto out;
- }
-
- }
-
- for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) {
- rval = smiapp_read(
- sensor,
- SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i,
- &v);
- if (rval)
- goto out;
-
- *nvm++ = v;
- }
- }
-
-out:
- rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0);
- if (rval < 0)
- return rval;
- else
- return rval2;
-}
-
-/*
- *
- * SMIA++ CCI address control
- *
- */
-static int smiapp_change_cci_addr(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
- u32 val;
-
- client->addr = sensor->platform_data->i2c_addr_dfl;
-
- rval = smiapp_write(sensor,
- SMIAPP_REG_U8_CCI_ADDRESS_CONTROL,
- sensor->platform_data->i2c_addr_alt << 1);
- if (rval)
- return rval;
-
- client->addr = sensor->platform_data->i2c_addr_alt;
-
- /* verify addr change went ok */
- rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val);
- if (rval)
- return rval;
-
- if (val != sensor->platform_data->i2c_addr_alt << 1)
- return -ENODEV;
-
- return 0;
-}
-
-/*
- *
- * SMIA++ Mode Control
- *
- */
-static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor)
-{
- struct smiapp_flash_strobe_parms *strobe_setup;
- unsigned int ext_freq = sensor->platform_data->ext_clk;
- u32 tmp;
- u32 strobe_adjustment;
- u32 strobe_width_high_rs;
- int rval;
-
- strobe_setup = sensor->platform_data->strobe_setup;
-
- /*
- * How to calculate registers related to strobe length. Please
- * do not change, or if you do at least know what you're
- * doing. :-)
- *
- * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> 2010-10-25
- *
- * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl
- * / EXTCLK freq [Hz]) * flash_strobe_adjustment
- *
- * tFlash_strobe_width_ctrl E N, [1 - 0xffff]
- * flash_strobe_adjustment E N, [1 - 0xff]
- *
- * The formula above is written as below to keep it on one
- * line:
- *
- * l / 10^6 = w / e * a
- *
- * Let's mark w * a by x:
- *
- * x = w * a
- *
- * Thus, we get:
- *
- * x = l * e / 10^6
- *
- * The strobe width must be at least as long as requested,
- * thus rounding upwards is needed.
- *
- * x = (l * e + 10^6 - 1) / 10^6
- * -----------------------------
- *
- * Maximum possible accuracy is wanted at all times. Thus keep
- * a as small as possible.
- *
- * Calculate a, assuming maximum w, with rounding upwards:
- *
- * a = (x + (2^16 - 1) - 1) / (2^16 - 1)
- * -------------------------------------
- *
- * Thus, we also get w, with that a, with rounding upwards:
- *
- * w = (x + a - 1) / a
- * -------------------
- *
- * To get limits:
- *
- * x E [1, (2^16 - 1) * (2^8 - 1)]
- *
- * Substituting maximum x to the original formula (with rounding),
- * the maximum l is thus
- *
- * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1
- *
- * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e
- * --------------------------------------------------
- *
- * flash_strobe_length must be clamped between 1 and
- * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq.
- *
- * Then,
- *
- * flash_strobe_adjustment = ((flash_strobe_length *
- * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1)
- *
- * tFlash_strobe_width_ctrl = ((flash_strobe_length *
- * EXTCLK freq + 10^6 - 1) / 10^6 +
- * flash_strobe_adjustment - 1) / flash_strobe_adjustment
- */
- tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) -
- 1000000 + 1, ext_freq);
- strobe_setup->strobe_width_high_us =
- clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp);
-
- tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq +
- 1000000 - 1), 1000000ULL);
- strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1);
- strobe_width_high_rs = (tmp + strobe_adjustment - 1) /
- strobe_adjustment;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS,
- strobe_setup->mode);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT,
- strobe_adjustment);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL,
- strobe_width_high_rs);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL,
- strobe_setup->strobe_delay);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT,
- strobe_setup->stobe_start_point);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS,
- strobe_setup->trigger);
-
-out:
- sensor->platform_data->strobe_setup->trigger = 0;
-
- return rval;
-}
-
-/* -----------------------------------------------------------------------------
- * Power management
- */
-
-static int smiapp_power_on(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int sleep;
- int rval;
-
- rval = regulator_enable(sensor->vana);
- if (rval) {
- dev_err(&client->dev, "failed to enable vana regulator\n");
- return rval;
- }
- usleep_range(1000, 1000);
-
- if (sensor->platform_data->set_xclk)
- rval = sensor->platform_data->set_xclk(
- &sensor->src->sd, sensor->platform_data->ext_clk);
- else
- rval = clk_enable(sensor->ext_clk);
- if (rval < 0) {
- dev_dbg(&client->dev, "failed to set xclk\n");
- goto out_xclk_fail;
- }
- usleep_range(1000, 1000);
-
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_set_value(sensor->platform_data->xshutdown, 1);
-
- sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk);
- usleep_range(sleep, sleep);
-
- /*
- * Failures to respond to the address change command have been noticed.
- * Those failures seem to be caused by the sensor requiring a longer
- * boot time than advertised. An additional 10ms delay seems to work
- * around the issue, but the SMIA++ I2C write retry hack makes the delay
- * unnecessary. The failures need to be investigated to find a proper
- * fix, and a delay will likely need to be added here if the I2C write
- * retry hack is reverted before the root cause of the boot time issue
- * is found.
- */
-
- if (sensor->platform_data->i2c_addr_alt) {
- rval = smiapp_change_cci_addr(sensor);
- if (rval) {
- dev_err(&client->dev, "cci address change error\n");
- goto out_cci_addr_fail;
- }
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET,
- SMIAPP_SOFTWARE_RESET);
- if (rval < 0) {
- dev_err(&client->dev, "software reset failed\n");
- goto out_cci_addr_fail;
- }
-
- if (sensor->platform_data->i2c_addr_alt) {
- rval = smiapp_change_cci_addr(sensor);
- if (rval) {
- dev_err(&client->dev, "cci address change error\n");
- goto out_cci_addr_fail;
- }
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE,
- SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR);
- if (rval) {
- dev_err(&client->dev, "compression mode set failed\n");
- goto out_cci_addr_fail;
- }
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ,
- sensor->platform_data->ext_clk / (1000000 / (1 << 8)));
- if (rval) {
- dev_err(&client->dev, "extclk frequency set failed\n");
- goto out_cci_addr_fail;
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE,
- sensor->platform_data->lanes - 1);
- if (rval) {
- dev_err(&client->dev, "csi lane mode set failed\n");
- goto out_cci_addr_fail;
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL,
- SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE);
- if (rval) {
- dev_err(&client->dev, "fast standby set failed\n");
- goto out_cci_addr_fail;
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE,
- sensor->platform_data->csi_signalling_mode);
- if (rval) {
- dev_err(&client->dev, "csi signalling mode set failed\n");
- goto out_cci_addr_fail;
- }
-
- /* DPHY control done by sensor based on requested link rate */
- rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL,
- SMIAPP_DPHY_CTRL_UI);
- if (rval < 0)
- return rval;
-
- rval = smiapp_call_quirk(sensor, post_poweron);
- if (rval) {
- dev_err(&client->dev, "post_poweron quirks failed\n");
- goto out_cci_addr_fail;
- }
-
- /* Are we still initialising...? If yes, return here. */
- if (!sensor->pixel_array)
- return 0;
-
- rval = v4l2_ctrl_handler_setup(
- &sensor->pixel_array->ctrl_handler);
- if (rval)
- goto out_cci_addr_fail;
-
- rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
- if (rval)
- goto out_cci_addr_fail;
-
- mutex_lock(&sensor->mutex);
- rval = smiapp_update_mode(sensor);
- mutex_unlock(&sensor->mutex);
- if (rval < 0)
- goto out_cci_addr_fail;
-
- return 0;
-
-out_cci_addr_fail:
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_set_value(sensor->platform_data->xshutdown, 0);
- if (sensor->platform_data->set_xclk)
- sensor->platform_data->set_xclk(&sensor->src->sd, 0);
- else
- clk_disable(sensor->ext_clk);
-
-out_xclk_fail:
- regulator_disable(sensor->vana);
- return rval;
-}
-
-static void smiapp_power_off(struct smiapp_sensor *sensor)
-{
- /*
- * Currently power/clock to lens are enable/disabled separately
- * but they are essentially the same signals. So if the sensor is
- * powered off while the lens is powered on the sensor does not
- * really see a power off and next time the cci address change
- * will fail. So do a soft reset explicitly here.
- */
- if (sensor->platform_data->i2c_addr_alt)
- smiapp_write(sensor,
- SMIAPP_REG_U8_SOFTWARE_RESET,
- SMIAPP_SOFTWARE_RESET);
-
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_set_value(sensor->platform_data->xshutdown, 0);
- if (sensor->platform_data->set_xclk)
- sensor->platform_data->set_xclk(&sensor->src->sd, 0);
- else
- clk_disable(sensor->ext_clk);
- usleep_range(5000, 5000);
- regulator_disable(sensor->vana);
- sensor->streaming = 0;
-}
-
-static int smiapp_set_power(struct v4l2_subdev *subdev, int on)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int ret = 0;
-
- mutex_lock(&sensor->power_mutex);
-
- /*
- * If the power count is modified from 0 to != 0 or from != 0
- * to 0, update the power state.
- */
- if (!sensor->power_count == !on)
- goto out;
-
- if (on) {
- /* Power on and perform initialisation. */
- ret = smiapp_power_on(sensor);
- if (ret < 0)
- goto out;
- } else {
- smiapp_power_off(sensor);
- }
-
- /* Update the power count. */
- sensor->power_count += on ? 1 : -1;
- WARN_ON(sensor->power_count < 0);
-
-out:
- mutex_unlock(&sensor->power_mutex);
- return ret;
-}
-
-/* -----------------------------------------------------------------------------
- * Video stream management
- */
-
-static int smiapp_start_streaming(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- mutex_lock(&sensor->mutex);
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT,
- (sensor->csi_format->width << 8) |
- sensor->csi_format->compressed);
- if (rval)
- goto out;
-
- rval = smiapp_pll_configure(sensor);
- if (rval)
- goto out;
-
- /* Analog crop start coordinates */
- rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top);
- if (rval < 0)
- goto out;
-
- /* Analog crop end coordinates */
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_X_ADDR_END,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left
- + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_Y_ADDR_END,
- sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top
- + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1);
- if (rval < 0)
- goto out;
-
- /*
- * Output from pixel array, including blanking, is set using
- * controls below. No need to set here.
- */
-
- /* Digital crop */
- if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
- == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET,
- sensor->scaler->crop[SMIAPP_PAD_SINK].left);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET,
- sensor->scaler->crop[SMIAPP_PAD_SINK].top);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH,
- sensor->scaler->crop[SMIAPP_PAD_SINK].width);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(
- sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT,
- sensor->scaler->crop[SMIAPP_PAD_SINK].height);
- if (rval < 0)
- goto out;
- }
-
- /* Scaling */
- if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
- != SMIAPP_SCALING_CAPABILITY_NONE) {
- rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE,
- sensor->scaling_mode);
- if (rval < 0)
- goto out;
-
- rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M,
- sensor->scale_m);
- if (rval < 0)
- goto out;
- }
-
- /* Output size from sensor */
- rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE,
- sensor->src->crop[SMIAPP_PAD_SRC].width);
- if (rval < 0)
- goto out;
- rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE,
- sensor->src->crop[SMIAPP_PAD_SRC].height);
- if (rval < 0)
- goto out;
-
- if ((sensor->flash_capability &
- (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
- SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) &&
- sensor->platform_data->strobe_setup != NULL &&
- sensor->platform_data->strobe_setup->trigger != 0) {
- rval = smiapp_setup_flash_strobe(sensor);
- if (rval)
- goto out;
- }
-
- rval = smiapp_call_quirk(sensor, pre_streamon);
- if (rval) {
- dev_err(&client->dev, "pre_streamon quirks failed\n");
- goto out;
- }
-
- rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
- SMIAPP_MODE_SELECT_STREAMING);
-
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static int smiapp_stop_streaming(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
- SMIAPP_MODE_SELECT_SOFTWARE_STANDBY);
- if (rval)
- goto out;
-
- rval = smiapp_call_quirk(sensor, post_streamoff);
- if (rval)
- dev_err(&client->dev, "post_streamoff quirks failed\n");
-
-out:
- mutex_unlock(&sensor->mutex);
- return rval;
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev video operations
- */
-
-static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int rval;
-
- if (sensor->streaming == enable)
- return 0;
-
- if (enable) {
- sensor->streaming = 1;
- rval = smiapp_start_streaming(sensor);
- if (rval < 0)
- sensor->streaming = 0;
- } else {
- rval = smiapp_stop_streaming(sensor);
- sensor->streaming = 0;
- }
-
- return rval;
-}
-
-static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- unsigned int i;
- int idx = -1;
- int rval = -EINVAL;
-
- mutex_lock(&sensor->mutex);
-
- dev_err(&client->dev, "subdev %s, pad %d, index %d\n",
- subdev->name, code->pad, code->index);
-
- if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) {
- if (code->index)
- goto out;
-
- code->code = sensor->internal_csi_format->code;
- rval = 0;
- goto out;
- }
-
- for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i))
- idx++;
-
- if (idx == code->index) {
- code->code = smiapp_csi_data_formats[i].code;
- dev_err(&client->dev, "found index %d, i %d, code %x\n",
- code->index, i, code->code);
- rval = 0;
- break;
- }
- }
-
-out:
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev,
- unsigned int pad)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
-
- if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC)
- return sensor->csi_format->code;
- else
- return sensor->internal_csi_format->code;
-}
-
-static int __smiapp_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
- } else {
- struct v4l2_rect *r;
-
- if (fmt->pad == ssd->source_pad)
- r = &ssd->crop[ssd->source_pad];
- else
- r = &ssd->sink_fmt;
-
- fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
- fmt->format.width = r->width;
- fmt->format.height = r->height;
- }
-
- return 0;
-}
-
-static int smiapp_get_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = __smiapp_get_format(subdev, fh, fmt);
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-
-static void smiapp_get_crop_compose(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_rect **crops,
- struct v4l2_rect **comps, int which)
-{
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- unsigned int i;
-
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (crops)
- for (i = 0; i < subdev->entity.num_pads; i++)
- crops[i] = &ssd->crop[i];
- if (comps)
- *comps = &ssd->compose;
- } else {
- if (crops) {
- for (i = 0; i < subdev->entity.num_pads; i++) {
- crops[i] = v4l2_subdev_get_try_crop(fh, i);
- BUG_ON(!crops[i]);
- }
- }
- if (comps) {
- *comps = v4l2_subdev_get_try_compose(fh,
- SMIAPP_PAD_SINK);
- BUG_ON(!*comps);
- }
- }
-}
-
-/* Changes require propagation only on sink pad. */
-static void smiapp_propagate(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh, int which,
- int target)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- struct v4l2_rect *comp, *crops[SMIAPP_PADS];
-
- smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
-
- switch (target) {
- case V4L2_SEL_TGT_CROP:
- comp->width = crops[SMIAPP_PAD_SINK]->width;
- comp->height = crops[SMIAPP_PAD_SINK]->height;
- if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (ssd == sensor->scaler) {
- sensor->scale_m =
- sensor->limits[
- SMIAPP_LIMIT_SCALER_N_MIN];
- sensor->scaling_mode =
- SMIAPP_SCALING_MODE_NONE;
- } else if (ssd == sensor->binner) {
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
- }
- }
- /* Fall through */
- case V4L2_SEL_TGT_COMPOSE:
- *crops[SMIAPP_PAD_SRC] = *comp;
- break;
- default:
- BUG();
- }
-}
-
-static const struct smiapp_csi_data_format
-*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code)
-{
- const struct smiapp_csi_data_format *csi_format = sensor->csi_format;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
- if (sensor->mbus_frame_fmts & (1 << i)
- && smiapp_csi_data_formats[i].code == code)
- return &smiapp_csi_data_formats[i];
- }
-
- return csi_format;
-}
-
-static int smiapp_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_format *fmt)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- struct v4l2_rect *crops[SMIAPP_PADS];
-
- mutex_lock(&sensor->mutex);
-
- /*
- * Media bus code is changeable on src subdev's source pad. On
- * other source pads we just get format here.
- */
- if (fmt->pad == ssd->source_pad) {
- u32 code = fmt->format.code;
- int rval = __smiapp_get_format(subdev, fh, fmt);
-
- if (!rval && subdev == &sensor->src->sd) {
- const struct smiapp_csi_data_format *csi_format =
- smiapp_validate_csi_data_format(sensor, code);
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- sensor->csi_format = csi_format;
- fmt->format.code = csi_format->code;
- }
-
- mutex_unlock(&sensor->mutex);
- return rval;
- }
-
- /* Sink pad. Width and height are changeable here. */
- fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
- fmt->format.width &= ~1;
- fmt->format.height &= ~1;
-
- fmt->format.width =
- clamp(fmt->format.width,
- sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
- sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]);
- fmt->format.height =
- clamp(fmt->format.height,
- sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
- sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]);
-
- smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which);
-
- crops[ssd->sink_pad]->left = 0;
- crops[ssd->sink_pad]->top = 0;
- crops[ssd->sink_pad]->width = fmt->format.width;
- crops[ssd->sink_pad]->height = fmt->format.height;
- if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- ssd->sink_fmt = *crops[ssd->sink_pad];
- smiapp_propagate(subdev, fh, fmt->which,
- V4L2_SEL_TGT_CROP);
-
- mutex_unlock(&sensor->mutex);
-
- return 0;
-}
-
-/*
- * Calculate goodness of scaled image size compared to expected image
- * size and flags provided.
- */
-#define SCALING_GOODNESS 100000
-#define SCALING_GOODNESS_EXTREME 100000000
-static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
- int h, int ask_h, u32 flags)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- int val = 0;
-
- w &= ~1;
- ask_w &= ~1;
- h &= ~1;
- ask_h &= ~1;
-
- if (flags & V4L2_SEL_FLAG_GE) {
- if (w < ask_w)
- val -= SCALING_GOODNESS;
- if (h < ask_h)
- val -= SCALING_GOODNESS;
- }
-
- if (flags & V4L2_SEL_FLAG_LE) {
- if (w > ask_w)
- val -= SCALING_GOODNESS;
- if (h > ask_h)
- val -= SCALING_GOODNESS;
- }
-
- val -= abs(w - ask_w);
- val -= abs(h - ask_h);
-
- if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE])
- val -= SCALING_GOODNESS_EXTREME;
-
- dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n",
- w, ask_h, h, ask_h, val);
-
- return val;
-}
-
-static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- unsigned int i;
- unsigned int binh = 1, binv = 1;
- unsigned int best = scaling_goodness(
- subdev,
- crops[SMIAPP_PAD_SINK]->width, sel->r.width,
- crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags);
-
- for (i = 0; i < sensor->nbinning_subtypes; i++) {
- int this = scaling_goodness(
- subdev,
- crops[SMIAPP_PAD_SINK]->width
- / sensor->binning_subtypes[i].horizontal,
- sel->r.width,
- crops[SMIAPP_PAD_SINK]->height
- / sensor->binning_subtypes[i].vertical,
- sel->r.height, sel->flags);
-
- if (this > best) {
- binh = sensor->binning_subtypes[i].horizontal;
- binv = sensor->binning_subtypes[i].vertical;
- best = this;
- }
- }
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->binning_vertical = binv;
- sensor->binning_horizontal = binh;
- }
-
- sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1;
- sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1;
-}
-
-/*
- * Calculate best scaling ratio and mode for given output resolution.
- *
- * Try all of these: horizontal ratio, vertical ratio and smallest
- * size possible (horizontally).
- *
- * Also try whether horizontal scaler or full scaler gives a better
- * result.
- */
-static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel,
- struct v4l2_rect **crops,
- struct v4l2_rect *comp)
-{
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- u32 min, max, a, b, max_m;
- u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
- int mode = SMIAPP_SCALING_MODE_HORIZONTAL;
- u32 try[4];
- u32 ntry = 0;
- unsigned int i;
- int best = INT_MIN;
-
- sel->r.width = min_t(unsigned int, sel->r.width,
- crops[SMIAPP_PAD_SINK]->width);
- sel->r.height = min_t(unsigned int, sel->r.height,
- crops[SMIAPP_PAD_SINK]->height);
-
- a = crops[SMIAPP_PAD_SINK]->width
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width;
- b = crops[SMIAPP_PAD_SINK]->height
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height;
- max_m = crops[SMIAPP_PAD_SINK]->width
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]
- / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE];
-
- a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
- max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
- b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
- max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
- max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
- max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
-
- dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m);
-
- min = min(max_m, min(a, b));
- max = min(max_m, max(a, b));
-
- try[ntry] = min;
- ntry++;
- if (min != max) {
- try[ntry] = max;
- ntry++;
- }
- if (max != max_m) {
- try[ntry] = min + 1;
- ntry++;
- if (min != max) {
- try[ntry] = max + 1;
- ntry++;
- }
- }
-
- for (i = 0; i < ntry; i++) {
- int this = scaling_goodness(
- subdev,
- crops[SMIAPP_PAD_SINK]->width
- / try[i]
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
- sel->r.width,
- crops[SMIAPP_PAD_SINK]->height,
- sel->r.height,
- sel->flags);
-
- dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i);
-
- if (this > best) {
- scale_m = try[i];
- mode = SMIAPP_SCALING_MODE_HORIZONTAL;
- best = this;
- }
-
- if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
- == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
- continue;
-
- this = scaling_goodness(
- subdev, crops[SMIAPP_PAD_SINK]->width
- / try[i]
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
- sel->r.width,
- crops[SMIAPP_PAD_SINK]->height
- / try[i]
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
- sel->r.height,
- sel->flags);
-
- if (this > best) {
- scale_m = try[i];
- mode = SMIAPP_SCALING_MODE_BOTH;
- best = this;
- }
- }
-
- sel->r.width =
- (crops[SMIAPP_PAD_SINK]->width
- / scale_m
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1;
- if (mode == SMIAPP_SCALING_MODE_BOTH)
- sel->r.height =
- (crops[SMIAPP_PAD_SINK]->height
- / scale_m
- * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN])
- & ~1;
- else
- sel->r.height = crops[SMIAPP_PAD_SINK]->height;
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sensor->scale_m = scale_m;
- sensor->scaling_mode = mode;
- }
-}
-/* We're only called on source pads. This function sets scaling. */
-static int smiapp_set_compose(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- struct v4l2_rect *comp, *crops[SMIAPP_PADS];
-
- smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
-
- sel->r.top = 0;
- sel->r.left = 0;
-
- if (ssd == sensor->binner)
- smiapp_set_compose_binner(subdev, fh, sel, crops, comp);
- else
- smiapp_set_compose_scaler(subdev, fh, sel, crops, comp);
-
- *comp = sel->r;
- smiapp_propagate(subdev, fh, sel->which,
- V4L2_SEL_TGT_COMPOSE);
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return smiapp_update_mode(sensor);
-
- return 0;
-}
-
-static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
-
- /* We only implement crop in three places. */
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- if (ssd == sensor->pixel_array
- && sel->pad == SMIAPP_PA_PAD_SRC)
- return 0;
- if (ssd == sensor->src
- && sel->pad == SMIAPP_PAD_SRC)
- return 0;
- if (ssd == sensor->scaler
- && sel->pad == SMIAPP_PAD_SINK
- && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
- == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
- return 0;
- return -EINVAL;
- case V4L2_SEL_TGT_COMPOSE:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- if (sel->pad == ssd->source_pad)
- return -EINVAL;
- if (ssd == sensor->binner)
- return 0;
- if (ssd == sensor->scaler
- && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
- != SMIAPP_SCALING_CAPABILITY_NONE)
- return 0;
- /* Fall through */
- default:
- return -EINVAL;
- }
-}
-
-static int smiapp_set_crop(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- struct v4l2_rect *src_size, *crops[SMIAPP_PADS];
- struct v4l2_rect _r;
-
- smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which);
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- if (sel->pad == ssd->sink_pad)
- src_size = &ssd->sink_fmt;
- else
- src_size = &ssd->compose;
- } else {
- if (sel->pad == ssd->sink_pad) {
- _r.left = 0;
- _r.top = 0;
- _r.width = v4l2_subdev_get_try_format(fh, sel->pad)
- ->width;
- _r.height = v4l2_subdev_get_try_format(fh, sel->pad)
- ->height;
- src_size = &_r;
- } else {
- src_size =
- v4l2_subdev_get_try_compose(
- fh, ssd->sink_pad);
- }
- }
-
- if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) {
- sel->r.left = 0;
- sel->r.top = 0;
- }
-
- sel->r.width = min(sel->r.width, src_size->width);
- sel->r.height = min(sel->r.height, src_size->height);
-
- sel->r.left = min(sel->r.left, src_size->width - sel->r.width);
- sel->r.top = min(sel->r.top, src_size->height - sel->r.height);
-
- *crops[sel->pad] = sel->r;
-
- if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
- smiapp_propagate(subdev, fh, sel->which,
- V4L2_SEL_TGT_CROP);
-
- return 0;
-}
-
-static int __smiapp_get_selection(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
- struct v4l2_rect *comp, *crops[SMIAPP_PADS];
- struct v4l2_rect sink_fmt;
- int ret;
-
- ret = __smiapp_sel_supported(subdev, sel);
- if (ret)
- return ret;
-
- smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
-
- if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- sink_fmt = ssd->sink_fmt;
- } else {
- struct v4l2_mbus_framefmt *fmt =
- v4l2_subdev_get_try_format(fh, ssd->sink_pad);
-
- sink_fmt.left = 0;
- sink_fmt.top = 0;
- sink_fmt.width = fmt->width;
- sink_fmt.height = fmt->height;
- }
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP_BOUNDS:
- if (ssd == sensor->pixel_array) {
- sel->r.width =
- sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
- sel->r.height =
- sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
- } else if (sel->pad == ssd->sink_pad) {
- sel->r = sink_fmt;
- } else {
- sel->r = *comp;
- }
- break;
- case V4L2_SEL_TGT_CROP:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- sel->r = *crops[sel->pad];
- break;
- case V4L2_SEL_TGT_COMPOSE:
- sel->r = *comp;
- break;
- }
-
- return 0;
-}
-
-static int smiapp_get_selection(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int rval;
-
- mutex_lock(&sensor->mutex);
- rval = __smiapp_get_selection(subdev, fh, sel);
- mutex_unlock(&sensor->mutex);
-
- return rval;
-}
-static int smiapp_set_selection(struct v4l2_subdev *subdev,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_selection *sel)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int ret;
-
- ret = __smiapp_sel_supported(subdev, sel);
- if (ret)
- return ret;
-
- mutex_lock(&sensor->mutex);
-
- sel->r.left = max(0, sel->r.left & ~1);
- sel->r.top = max(0, sel->r.top & ~1);
- sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags));
- sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags));
-
- sel->r.width = max_t(unsigned int,
- sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
- sel->r.width);
- sel->r.height = max_t(unsigned int,
- sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
- sel->r.height);
-
- switch (sel->target) {
- case V4L2_SEL_TGT_CROP:
- ret = smiapp_set_crop(subdev, fh, sel);
- break;
- case V4L2_SEL_TGT_COMPOSE:
- ret = smiapp_set_compose(subdev, fh, sel);
- break;
- default:
- BUG();
- }
-
- mutex_unlock(&sensor->mutex);
- return ret;
-}
-
-static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
-
- *frames = sensor->frame_skip;
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
- * sysfs attributes
- */
-
-static ssize_t
-smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- unsigned int nbytes;
-
- if (!sensor->dev_init_done)
- return -EBUSY;
-
- if (!sensor->nvm_size) {
- /* NVM not read yet - read it now */
- sensor->nvm_size = sensor->platform_data->nvm_size;
- if (smiapp_set_power(subdev, 1) < 0)
- return -ENODEV;
- if (smiapp_read_nvm(sensor, sensor->nvm)) {
- dev_err(&client->dev, "nvm read failed\n");
- return -ENODEV;
- }
- smiapp_set_power(subdev, 0);
- }
- /*
- * NVM is still way below a PAGE_SIZE, so we can safely
- * assume this for now.
- */
- nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE);
- memcpy(buf, sensor->nvm, nbytes);
-
- return nbytes;
-}
-static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL);
-
-/* -----------------------------------------------------------------------------
- * V4L2 subdev core operations
- */
-
-static int smiapp_identify_module(struct v4l2_subdev *subdev)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct smiapp_module_info *minfo = &sensor->minfo;
- unsigned int i;
- int rval = 0;
-
- minfo->name = SMIAPP_NAME;
-
- /* Module info */
- rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID,
- &minfo->manufacturer_id);
- if (!rval)
- rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID,
- &minfo->model_id);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_REVISION_NUMBER_MAJOR,
- &minfo->revision_number_major);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_REVISION_NUMBER_MINOR,
- &minfo->revision_number_minor);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_MODULE_DATE_YEAR,
- &minfo->module_year);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_MODULE_DATE_MONTH,
- &minfo->module_month);
- if (!rval)
- rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY,
- &minfo->module_day);
-
- /* Sensor info */
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID,
- &minfo->sensor_manufacturer_id);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U16_SENSOR_MODEL_ID,
- &minfo->sensor_model_id);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_SENSOR_REVISION_NUMBER,
- &minfo->sensor_revision_number);
- if (!rval)
- rval = smiapp_read_8only(sensor,
- SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION,
- &minfo->sensor_firmware_version);
-
- /* SMIA */
- if (!rval)
- rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION,
- &minfo->smia_version);
- if (!rval)
- rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION,
- &minfo->smiapp_version);
-
- if (rval) {
- dev_err(&client->dev, "sensor detection failed\n");
- return -ENODEV;
- }
-
- dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n",
- minfo->manufacturer_id, minfo->model_id);
-
- dev_dbg(&client->dev,
- "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n",
- minfo->revision_number_major, minfo->revision_number_minor,
- minfo->module_year, minfo->module_month, minfo->module_day);
-
- dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n",
- minfo->sensor_manufacturer_id, minfo->sensor_model_id);
-
- dev_dbg(&client->dev,
- "sensor revision 0x%2.2x firmware version 0x%2.2x\n",
- minfo->sensor_revision_number, minfo->sensor_firmware_version);
-
- dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n",
- minfo->smia_version, minfo->smiapp_version);
-
- /*
- * Some modules have bad data in the lvalues below. Hope the
- * rvalues have better stuff. The lvalues are module
- * parameters whereas the rvalues are sensor parameters.
- */
- if (!minfo->manufacturer_id && !minfo->model_id) {
- minfo->manufacturer_id = minfo->sensor_manufacturer_id;
- minfo->model_id = minfo->sensor_model_id;
- minfo->revision_number_major = minfo->sensor_revision_number;
- }
-
- for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) {
- if (smiapp_module_idents[i].manufacturer_id
- != minfo->manufacturer_id)
- continue;
- if (smiapp_module_idents[i].model_id != minfo->model_id)
- continue;
- if (smiapp_module_idents[i].flags
- & SMIAPP_MODULE_IDENT_FLAG_REV_LE) {
- if (smiapp_module_idents[i].revision_number_major
- < minfo->revision_number_major)
- continue;
- } else {
- if (smiapp_module_idents[i].revision_number_major
- != minfo->revision_number_major)
- continue;
- }
-
- minfo->name = smiapp_module_idents[i].name;
- minfo->quirk = smiapp_module_idents[i].quirk;
- break;
- }
-
- if (i >= ARRAY_SIZE(smiapp_module_idents))
- dev_warn(&client->dev,
- "no quirks for this module; let's hope it's fully compliant\n");
-
- dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n",
- minfo->name, minfo->manufacturer_id, minfo->model_id,
- minfo->revision_number_major);
-
- strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name));
-
- return 0;
-}
-
-static const struct v4l2_subdev_ops smiapp_ops;
-static const struct v4l2_subdev_internal_ops smiapp_internal_ops;
-static const struct media_entity_operations smiapp_entity_ops;
-
-static int smiapp_registered(struct v4l2_subdev *subdev)
-{
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- struct i2c_client *client = v4l2_get_subdevdata(subdev);
- struct smiapp_subdev *last = NULL;
- u32 tmp;
- unsigned int i;
- int rval;
-
- sensor->vana = regulator_get(&client->dev, "VANA");
- if (IS_ERR(sensor->vana)) {
- dev_err(&client->dev, "could not get regulator for vana\n");
- return -ENODEV;
- }
-
- if (!sensor->platform_data->set_xclk) {
- sensor->ext_clk = clk_get(&client->dev,
- sensor->platform_data->ext_clk_name);
- if (IS_ERR(sensor->ext_clk)) {
- dev_err(&client->dev, "could not get clock %s\n",
- sensor->platform_data->ext_clk_name);
- rval = -ENODEV;
- goto out_clk_get;
- }
-
- rval = clk_set_rate(sensor->ext_clk,
- sensor->platform_data->ext_clk);
- if (rval < 0) {
- dev_err(&client->dev,
- "unable to set clock %s freq to %u\n",
- sensor->platform_data->ext_clk_name,
- sensor->platform_data->ext_clk);
- rval = -ENODEV;
- goto out_clk_set_rate;
- }
- }
-
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
- if (gpio_request_one(sensor->platform_data->xshutdown, 0,
- "SMIA++ xshutdown") != 0) {
- dev_err(&client->dev,
- "unable to acquire reset gpio %d\n",
- sensor->platform_data->xshutdown);
- rval = -ENODEV;
- goto out_clk_set_rate;
- }
- }
-
- rval = smiapp_power_on(sensor);
- if (rval) {
- rval = -ENODEV;
- goto out_smiapp_power_on;
- }
-
- rval = smiapp_identify_module(subdev);
- if (rval) {
- rval = -ENODEV;
- goto out_power_off;
- }
-
- rval = smiapp_get_all_limits(sensor);
- if (rval) {
- rval = -ENODEV;
- goto out_power_off;
- }
-
- /*
- * Handle Sensor Module orientation on the board.
- *
- * The application of H-FLIP and V-FLIP on the sensor is modified by
- * the sensor orientation on the board.
- *
- * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set
- * both H-FLIP and V-FLIP for normal operation which also implies
- * that a set/unset operation for user space HFLIP and VFLIP v4l2
- * controls will need to be internally inverted.
- *
- * Rotation also changes the bayer pattern.
- */
- if (sensor->platform_data->module_board_orient ==
- SMIAPP_MODULE_BOARD_ORIENT_180)
- sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
- SMIAPP_IMAGE_ORIENTATION_VFLIP;
-
- rval = smiapp_get_mbus_formats(sensor);
- if (rval) {
- rval = -ENODEV;
- goto out_power_off;
- }
-
- if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
- u32 val;
-
- rval = smiapp_read(sensor,
- SMIAPP_REG_U8_BINNING_SUBTYPES, &val);
- if (rval < 0) {
- rval = -ENODEV;
- goto out_power_off;
- }
- sensor->nbinning_subtypes = min_t(u8, val,
- SMIAPP_BINNING_SUBTYPES);
-
- for (i = 0; i < sensor->nbinning_subtypes; i++) {
- rval = smiapp_read(
- sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val);
- if (rval < 0) {
- rval = -ENODEV;
- goto out_power_off;
- }
- sensor->binning_subtypes[i] =
- *(struct smiapp_binning_subtype *)&val;
-
- dev_dbg(&client->dev, "binning %xx%x\n",
- sensor->binning_subtypes[i].horizontal,
- sensor->binning_subtypes[i].vertical);
- }
- }
- sensor->binning_horizontal = 1;
- sensor->binning_vertical = 1;
-
- /* SMIA++ NVM initialization - it will be read from the sensor
- * when it is first requested by userspace.
- */
- if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) {
- sensor->nvm = kzalloc(sensor->platform_data->nvm_size,
- GFP_KERNEL);
- if (sensor->nvm == NULL) {
- dev_err(&client->dev, "nvm buf allocation failed\n");
- rval = -ENOMEM;
- goto out_power_off;
- }
-
- if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
- dev_err(&client->dev, "sysfs nvm entry failed\n");
- rval = -EBUSY;
- goto out_power_off;
- }
- }
-
- rval = smiapp_call_quirk(sensor, limits);
- if (rval) {
- dev_err(&client->dev, "limits quirks failed\n");
- goto out_nvm_release;
- }
-
- /* We consider this as profile 0 sensor if any of these are zero. */
- if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
- !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
- !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] ||
- !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) {
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0;
- } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
- != SMIAPP_SCALING_CAPABILITY_NONE) {
- if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
- == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1;
- else
- sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2;
- sensor->scaler = &sensor->ssds[sensor->ssds_used];
- sensor->ssds_used++;
- } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
- == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
- sensor->scaler = &sensor->ssds[sensor->ssds_used];
- sensor->ssds_used++;
- }
- sensor->binner = &sensor->ssds[sensor->ssds_used];
- sensor->ssds_used++;
- sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
- sensor->ssds_used++;
-
- sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
-
- for (i = 0; i < SMIAPP_SUBDEVS; i++) {
- struct {
- struct smiapp_subdev *ssd;
- char *name;
- } const __this[] = {
- { sensor->scaler, "scaler", },
- { sensor->binner, "binner", },
- { sensor->pixel_array, "pixel array", },
- }, *_this = &__this[i];
- struct smiapp_subdev *this = _this->ssd;
-
- if (!this)
- continue;
-
- if (this != sensor->src)
- v4l2_subdev_init(&this->sd, &smiapp_ops);
-
- this->sensor = sensor;
-
- if (this == sensor->pixel_array) {
- this->npads = 1;
- } else {
- this->npads = 2;
- this->source_pad = 1;
- }
-
- snprintf(this->sd.name,
- sizeof(this->sd.name), "%s %s",
- sensor->minfo.name, _this->name);
-
- this->sink_fmt.width =
- sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
- this->sink_fmt.height =
- sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
- this->compose.width = this->sink_fmt.width;
- this->compose.height = this->sink_fmt.height;
- this->crop[this->source_pad] = this->compose;
- this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE;
- if (this != sensor->pixel_array) {
- this->crop[this->sink_pad] = this->compose;
- this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK;
- }
-
- this->sd.entity.ops = &smiapp_entity_ops;
-
- if (last == NULL) {
- last = this;
- continue;
- }
-
- this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- this->sd.internal_ops = &smiapp_internal_ops;
- this->sd.owner = NULL;
- v4l2_set_subdevdata(&this->sd, client);
-
- rval = media_entity_init(&this->sd.entity,
- this->npads, this->pads, 0);
- if (rval) {
- dev_err(&client->dev,
- "media_entity_init failed\n");
- goto out_nvm_release;
- }
-
- rval = media_entity_create_link(&this->sd.entity,
- this->source_pad,
- &last->sd.entity,
- last->sink_pad,
- MEDIA_LNK_FL_ENABLED |
- MEDIA_LNK_FL_IMMUTABLE);
- if (rval) {
- dev_err(&client->dev,
- "media_entity_create_link failed\n");
- goto out_nvm_release;
- }
-
- rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
- &this->sd);
- if (rval) {
- dev_err(&client->dev,
- "v4l2_device_register_subdev failed\n");
- goto out_nvm_release;
- }
-
- last = this;
- }
-
- dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
-
- sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
-
- /* final steps */
- smiapp_read_frame_fmt(sensor);
- rval = smiapp_init_controls(sensor);
- if (rval < 0)
- goto out_nvm_release;
-
- rval = smiapp_update_mode(sensor);
- if (rval) {
- dev_err(&client->dev, "update mode failed\n");
- goto out_nvm_release;
- }
-
- sensor->streaming = false;
- sensor->dev_init_done = true;
-
- /* check flash capability */
- rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp);
- sensor->flash_capability = tmp;
- if (rval)
- goto out_nvm_release;
-
- smiapp_power_off(sensor);
-
- return 0;
-
-out_nvm_release:
- device_remove_file(&client->dev, &dev_attr_nvm);
-
-out_power_off:
- kfree(sensor->nvm);
- sensor->nvm = NULL;
- smiapp_power_off(sensor);
-
-out_smiapp_power_on:
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_free(sensor->platform_data->xshutdown);
-
-out_clk_set_rate:
- clk_put(sensor->ext_clk);
- sensor->ext_clk = NULL;
-
-out_clk_get:
- regulator_put(sensor->vana);
- sensor->vana = NULL;
- return rval;
-}
-
-static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- struct smiapp_subdev *ssd = to_smiapp_subdev(sd);
- struct smiapp_sensor *sensor = ssd->sensor;
- u32 mbus_code =
- smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code;
- unsigned int i;
-
- mutex_lock(&sensor->mutex);
-
- for (i = 0; i < ssd->npads; i++) {
- struct v4l2_mbus_framefmt *try_fmt =
- v4l2_subdev_get_try_format(fh, i);
- struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i);
- struct v4l2_rect *try_comp;
-
- try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
- try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
- try_fmt->code = mbus_code;
-
- try_crop->top = 0;
- try_crop->left = 0;
- try_crop->width = try_fmt->width;
- try_crop->height = try_fmt->height;
-
- if (ssd != sensor->pixel_array)
- continue;
-
- try_comp = v4l2_subdev_get_try_compose(fh, i);
- *try_comp = *try_crop;
- }
-
- mutex_unlock(&sensor->mutex);
-
- return smiapp_set_power(sd, 1);
-}
-
-static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
-{
- return smiapp_set_power(sd, 0);
-}
-
-static const struct v4l2_subdev_video_ops smiapp_video_ops = {
- .s_stream = smiapp_set_stream,
-};
-
-static const struct v4l2_subdev_core_ops smiapp_core_ops = {
- .s_power = smiapp_set_power,
-};
-
-static const struct v4l2_subdev_pad_ops smiapp_pad_ops = {
- .enum_mbus_code = smiapp_enum_mbus_code,
- .get_fmt = smiapp_get_format,
- .set_fmt = smiapp_set_format,
- .get_selection = smiapp_get_selection,
- .set_selection = smiapp_set_selection,
-};
-
-static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = {
- .g_skip_frames = smiapp_get_skip_frames,
-};
-
-static const struct v4l2_subdev_ops smiapp_ops = {
- .core = &smiapp_core_ops,
- .video = &smiapp_video_ops,
- .pad = &smiapp_pad_ops,
- .sensor = &smiapp_sensor_ops,
-};
-
-static const struct media_entity_operations smiapp_entity_ops = {
- .link_validate = v4l2_subdev_link_validate,
-};
-
-static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = {
- .registered = smiapp_registered,
- .open = smiapp_open,
- .close = smiapp_close,
-};
-
-static const struct v4l2_subdev_internal_ops smiapp_internal_ops = {
- .open = smiapp_open,
- .close = smiapp_close,
-};
-
-/* -----------------------------------------------------------------------------
- * I2C Driver
- */
-
-#ifdef CONFIG_PM
-
-static int smiapp_suspend(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- bool streaming;
-
- BUG_ON(mutex_is_locked(&sensor->mutex));
-
- if (sensor->power_count == 0)
- return 0;
-
- if (sensor->streaming)
- smiapp_stop_streaming(sensor);
-
- streaming = sensor->streaming;
-
- smiapp_power_off(sensor);
-
- /* save state for resume */
- sensor->streaming = streaming;
-
- return 0;
-}
-
-static int smiapp_resume(struct device *dev)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- int rval;
-
- if (sensor->power_count == 0)
- return 0;
-
- rval = smiapp_power_on(sensor);
- if (rval)
- return rval;
-
- if (sensor->streaming)
- rval = smiapp_start_streaming(sensor);
-
- return rval;
-}
-
-#else
-
-#define smiapp_suspend NULL
-#define smiapp_resume NULL
-
-#endif /* CONFIG_PM */
-
-static int smiapp_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
-{
- struct smiapp_sensor *sensor;
- int rval;
-
- if (client->dev.platform_data == NULL)
- return -ENODEV;
-
- sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
- if (sensor == NULL)
- return -ENOMEM;
-
- sensor->platform_data = client->dev.platform_data;
- mutex_init(&sensor->mutex);
- mutex_init(&sensor->power_mutex);
- sensor->src = &sensor->ssds[sensor->ssds_used];
-
- v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops);
- sensor->src->sd.internal_ops = &smiapp_internal_src_ops;
- sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- sensor->src->sensor = sensor;
-
- sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
- rval = media_entity_init(&sensor->src->sd.entity, 2,
- sensor->src->pads, 0);
- if (rval < 0)
- kfree(sensor);
-
- return rval;
-}
-
-static int __exit smiapp_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *subdev = i2c_get_clientdata(client);
- struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
- unsigned int i;
-
- if (sensor->power_count) {
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_set_value(sensor->platform_data->xshutdown, 0);
- if (sensor->platform_data->set_xclk)
- sensor->platform_data->set_xclk(&sensor->src->sd, 0);
- else
- clk_disable(sensor->ext_clk);
- sensor->power_count = 0;
- }
-
- if (sensor->nvm) {
- device_remove_file(&client->dev, &dev_attr_nvm);
- kfree(sensor->nvm);
- }
-
- for (i = 0; i < sensor->ssds_used; i++) {
- media_entity_cleanup(&sensor->ssds[i].sd.entity);
- v4l2_device_unregister_subdev(&sensor->ssds[i].sd);
- }
- smiapp_free_controls(sensor);
- if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
- gpio_free(sensor->platform_data->xshutdown);
- if (sensor->ext_clk)
- clk_put(sensor->ext_clk);
- if (sensor->vana)
- regulator_put(sensor->vana);
-
- kfree(sensor);
-
- return 0;
-}
-
-static const struct i2c_device_id smiapp_id_table[] = {
- { SMIAPP_NAME, 0 },
- { },
-};
-MODULE_DEVICE_TABLE(i2c, smiapp_id_table);
-
-static const struct dev_pm_ops smiapp_pm_ops = {
- .suspend = smiapp_suspend,
- .resume = smiapp_resume,
-};
-
-static struct i2c_driver smiapp_i2c_driver = {
- .driver = {
- .name = SMIAPP_NAME,
- .pm = &smiapp_pm_ops,
- },
- .probe = smiapp_probe,
- .remove = __exit_p(smiapp_remove),
- .id_table = smiapp_id_table,
-};
-
-module_i2c_driver(smiapp_i2c_driver);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
-MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c
deleted file mode 100644
index 0800e095724e..000000000000
--- a/drivers/media/video/smiapp/smiapp-limits.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-limits.c
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include "smiapp.h"
-
-struct smiapp_reg_limits smiapp_reg_limits[] = {
- { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */
- { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" },
- { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" },
- { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" },
- { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" },
- { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */
- { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" },
- { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" },
- { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" },
- { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" },
- { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */
- { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" },
- { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" },
- { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" },
- { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" },
- { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */
- { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" },
- { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" },
- { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" },
- { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" },
- { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */
- { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" },
- { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" },
- { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" },
- { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" },
- { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */
- { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" },
- { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" },
- { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" },
- { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" },
- { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */
- { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" },
- { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" },
- { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" },
- { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" },
- { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */
- { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" },
- { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" },
- { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" },
- { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" },
- { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */
- { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" },
- { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" },
- { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" },
- { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" },
- { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */
- { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" },
- { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" },
- { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" },
- { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" },
- { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */
- { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" },
- { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" },
- { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" },
- { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" },
- { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */
- { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" },
- { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" },
- { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" },
- { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" },
- { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */
- { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" },
- { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" },
- { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" },
- { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" },
- { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */
- { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" },
- { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" },
- { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" },
- { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" },
- { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */
- { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" },
- { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" },
- { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" },
- { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" },
- { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */
- { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" },
- { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" },
- { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" },
- { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" },
- { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */
- { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" },
- { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" },
- { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" },
- { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" },
- { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */
- { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" },
- { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" },
- { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" },
- { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" },
- { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */
- { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" },
- { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" },
- { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" },
- { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" },
- { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */
- { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" },
- { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" },
- { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" },
- { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" },
- { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */
- { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" },
- { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" },
- { 0, NULL },
-};
diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h
deleted file mode 100644
index 7f4836bb78db..000000000000
--- a/drivers/media/video/smiapp/smiapp-limits.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-limits.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0
-#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1
-#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2
-#define SMIAPP_LIMIT_THS_ZERO_MIN 3
-#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4
-#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5
-#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6
-#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7
-#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8
-#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9
-#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10
-#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11
-#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12
-#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13
-#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14
-#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15
-#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16
-#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17
-#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18
-#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19
-#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20
-#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21
-#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22
-#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23
-#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24
-#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25
-#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26
-#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27
-#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28
-#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29
-#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30
-#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31
-#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32
-#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33
-#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34
-#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35
-#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36
-#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37
-#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38
-#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39
-#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40
-#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41
-#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42
-#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43
-#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44
-#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45
-#define SMIAPP_LIMIT_X_ADDR_MIN 46
-#define SMIAPP_LIMIT_Y_ADDR_MIN 47
-#define SMIAPP_LIMIT_X_ADDR_MAX 48
-#define SMIAPP_LIMIT_Y_ADDR_MAX 49
-#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50
-#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51
-#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52
-#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53
-#define SMIAPP_LIMIT_MIN_EVEN_INC 54
-#define SMIAPP_LIMIT_MAX_EVEN_INC 55
-#define SMIAPP_LIMIT_MIN_ODD_INC 56
-#define SMIAPP_LIMIT_MAX_ODD_INC 57
-#define SMIAPP_LIMIT_SCALING_CAPABILITY 58
-#define SMIAPP_LIMIT_SCALER_M_MIN 59
-#define SMIAPP_LIMIT_SCALER_M_MAX 60
-#define SMIAPP_LIMIT_SCALER_N_MIN 61
-#define SMIAPP_LIMIT_SCALER_N_MAX 62
-#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63
-#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64
-#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65
-#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66
-#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67
-#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68
-#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69
-#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70
-#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71
-#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72
-#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73
-#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74
-#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75
-#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76
-#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77
-#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78
-#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79
-#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80
-#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81
-#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82
-#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83
-#define SMIAPP_LIMIT_BINNING_CAPABILITY 84
-#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85
-#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86
-#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87
-#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88
-#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89
-#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90
-#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91
-#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92
-#define SMIAPP_LIMIT_EDOF_CAPABILITY 93
-#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94
-#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95
-#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96
-#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97
-#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98
-#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99
-#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100
-#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101
-#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102
-#define SMIAPP_LIMIT_LAST 103
diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c
deleted file mode 100644
index 55e87950dcea..000000000000
--- a/drivers/media/video/smiapp/smiapp-quirk.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-quirk.c
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/delay.h>
-
-#include "smiapp.h"
-
-static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)
-{
- return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val);
-}
-
-static int smiapp_write_8s(struct smiapp_sensor *sensor,
- struct smiapp_reg_8 *regs, int len)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- for (; len > 0; len--, regs++) {
- rval = smiapp_write_8(sensor, regs->reg, regs->val);
- if (rval < 0) {
- dev_err(&client->dev,
- "error %d writing reg 0x%4.4x, val 0x%2.2x",
- rval, regs->reg, regs->val);
- return rval;
- }
- }
-
- return 0;
-}
-
-void smiapp_replace_limit(struct smiapp_sensor *sensor,
- u32 limit, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
-
- dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n",
- smiapp_reg_limits[limit].addr,
- smiapp_reg_limits[limit].what, val, val);
- sensor->limits[limit] = val;
-}
-
-int smiapp_replace_limit_at(struct smiapp_sensor *sensor,
- u32 reg, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int i;
-
- for (i = 0; smiapp_reg_limits[i].addr; i++) {
- if ((smiapp_reg_limits[i].addr & 0xffff) != reg)
- continue;
-
- smiapp_replace_limit(sensor, i, val);
-
- return 0;
- }
-
- dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg);
-
- return -EINVAL;
-}
-
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
- u32 reg, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- const struct smia_reg *sreg;
-
- if (!sensor->minfo.quirk)
- return false;
-
- sreg = sensor->minfo.quirk->regs;
-
- if (!sreg)
- return false;
-
- while (sreg->type) {
- u16 type = reg >> 16;
- u16 reg16 = reg;
-
- if (sreg->type != type || sreg->reg != reg16) {
- sreg++;
- continue;
- }
-
- switch ((u8)type) {
- case SMIA_REG_8BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n",
- reg, sreg->val);
- break;
- case SMIA_REG_16BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n",
- reg, sreg->val);
- break;
- case SMIA_REG_32BIT:
- dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n",
- reg, sreg->val);
- break;
- }
-
- *val = sreg->val;
-
- return true;
- }
-
- return false;
-}
-
-static int jt8ew9_limits(struct smiapp_sensor *sensor)
-{
- if (sensor->minfo.revision_number_major < 0x03)
- sensor->frame_skip = 1;
-
- /* Below 24 gain doesn't have effect at all, */
- /* but ~59 is needed for full dynamic range */
- smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59);
- smiapp_replace_limit(
- sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000);
-
- return 0;
-}
-
-static int jt8ew9_post_poweron(struct smiapp_sensor *sensor)
-{
- struct smiapp_reg_8 regs[] = {
- { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */
- { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
- { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
- { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */
- { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
- { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
- { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
- { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
- { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */
- { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
- { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
- { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
- { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
- { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */
- { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */
- { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */
- { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
- { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
- /* Taken from v03. No idea what the rest are. */
- { 0x32e0, 0x05 },
- { 0x32e1, 0x05 },
- { 0x32e2, 0x04 },
- { 0x32e5, 0x04 },
- { 0x32e6, 0x04 },
-
- };
-
- return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
-}
-
-const struct smiapp_quirk smiapp_jt8ew9_quirk = {
- .limits = jt8ew9_limits,
- .post_poweron = jt8ew9_post_poweron,
-};
-
-static int imx125es_post_poweron(struct smiapp_sensor *sensor)
-{
- /* Taken from v02. No idea what the other two are. */
- struct smiapp_reg_8 regs[] = {
- /*
- * 0x3302: clk during frame blanking:
- * 0x00 - HS mode, 0x01 - LP11
- */
- { 0x3302, 0x01 },
- { 0x302d, 0x00 },
- { 0x3b08, 0x8c },
- };
-
- return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
-}
-
-const struct smiapp_quirk smiapp_imx125es_quirk = {
- .post_poweron = imx125es_post_poweron,
-};
-
-static int jt8ev1_limits(struct smiapp_sensor *sensor)
-{
- smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271);
- smiapp_replace_limit(sensor,
- SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184);
-
- return 0;
-}
-
-static int jt8ev1_post_poweron(struct smiapp_sensor *sensor)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- int rval;
-
- struct smiapp_reg_8 regs[] = {
- { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */
- { 0x30a3, 0xd0 }, /* FLASH STROBE enable */
- { 0x3237, 0x00 }, /* For control of pulse timing for ADC */
- { 0x3238, 0x43 },
- { 0x3301, 0x06 }, /* For analog bias for sensor */
- { 0x3302, 0x06 },
- { 0x3304, 0x00 },
- { 0x3305, 0x88 },
- { 0x332a, 0x14 },
- { 0x332c, 0x6b },
- { 0x3336, 0x01 },
- { 0x333f, 0x1f },
- { 0x3355, 0x00 },
- { 0x3356, 0x20 },
- { 0x33bf, 0x20 }, /* Adjust the FBC speed */
- { 0x33c9, 0x20 },
- { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */
- { 0x33cf, 0xec }, /* For Black sun */
- { 0x3328, 0x80 }, /* Ugh. No idea what's this. */
- };
-
- struct smiapp_reg_8 regs_96[] = {
- { 0x30ae, 0x00 }, /* For control of ADC clock */
- { 0x30af, 0xd0 },
- { 0x30b0, 0x01 },
- };
-
- rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
- if (rval < 0)
- return rval;
-
- switch (sensor->platform_data->ext_clk) {
- case 9600000:
- return smiapp_write_8s(sensor, regs_96,
- ARRAY_SIZE(regs_96));
- default:
- dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n",
- sensor->platform_data->ext_clk);
- return 0;
- }
-}
-
-static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor)
-{
- return smiapp_write_8(sensor, 0x3328, 0x00);
-}
-
-static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)
-{
- int rval;
-
- /* Workaround: allows fast standby to work properly */
- rval = smiapp_write_8(sensor, 0x3205, 0x04);
- if (rval < 0)
- return rval;
-
- /* Wait for 1 ms + one line => 2 ms is likely enough */
- usleep_range(2000, 2000);
-
- /* Restore it */
- rval = smiapp_write_8(sensor, 0x3205, 0x00);
- if (rval < 0)
- return rval;
-
- return smiapp_write_8(sensor, 0x3328, 0x80);
-}
-
-const struct smiapp_quirk smiapp_jt8ev1_quirk = {
- .limits = jt8ev1_limits,
- .post_poweron = jt8ev1_post_poweron,
- .pre_streamon = jt8ev1_pre_streamon,
- .post_streamoff = jt8ev1_post_streamoff,
- .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE,
-};
-
-static int tcm8500md_limits(struct smiapp_sensor *sensor)
-{
- smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000);
-
- return 0;
-}
-
-const struct smiapp_quirk smiapp_tcm8500md_quirk = {
- .limits = tcm8500md_limits,
-};
diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h
deleted file mode 100644
index f4dcaabaefe7..000000000000
--- a/drivers/media/video/smiapp/smiapp-quirk.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-quirk.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __SMIAPP_QUIRK__
-#define __SMIAPP_QUIRK__
-
-struct smiapp_sensor;
-
-/**
- * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard
- *
- * @limits: Replace sensor->limits with values which can't be read from
- * sensor registers. Called the first time the sensor is powered up.
- * @post_poweron: Called always after the sensor has been fully powered on.
- * @pre_streamon: Called just before streaming is enabled.
- * @post_streamon: Called right after stopping streaming.
- */
-struct smiapp_quirk {
- int (*limits)(struct smiapp_sensor *sensor);
- int (*post_poweron)(struct smiapp_sensor *sensor);
- int (*pre_streamon)(struct smiapp_sensor *sensor);
- int (*post_streamoff)(struct smiapp_sensor *sensor);
- const struct smia_reg *regs;
- unsigned long flags;
-};
-
-/* op pix clock is for all lanes in total normally */
-#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
-#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1)
-
-struct smiapp_reg_8 {
- u16 reg;
- u8 val;
-};
-
-void smiapp_replace_limit(struct smiapp_sensor *sensor,
- u32 limit, u32 val);
-bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
- u32 reg, u32 *val);
-
-#define SMIAPP_MK_QUIRK_REG(_reg, _val) \
- { \
- .type = (_reg >> 16), \
- .reg = (u16)_reg, \
- .val = _val, \
- }
-
-#define smiapp_call_quirk(_sensor, _quirk, ...) \
- (_sensor->minfo.quirk && \
- _sensor->minfo.quirk->_quirk ? \
- _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0)
-
-#define smiapp_needs_quirk(_sensor, _quirk) \
- (_sensor->minfo.quirk ? \
- _sensor->minfo.quirk->flags & _quirk : 0)
-
-extern const struct smiapp_quirk smiapp_jt8ev1_quirk;
-extern const struct smiapp_quirk smiapp_imx125es_quirk;
-extern const struct smiapp_quirk smiapp_jt8ew9_quirk;
-extern const struct smiapp_quirk smiapp_tcm8500md_quirk;
-
-#endif /* __SMIAPP_QUIRK__ */
diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h
deleted file mode 100644
index a089eb8161e1..000000000000
--- a/drivers/media/video/smiapp/smiapp-reg-defs.h
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-reg-defs.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r))
-#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r))
-#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r))
-
-#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r))
-
-#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000)
-#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002)
-#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003)
-#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004)
-#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005)
-#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006)
-#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008)
-#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c)
-#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010)
-#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011)
-#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012)
-#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013)
-#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014)
-#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015)
-#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016)
-#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018)
-#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019)
-#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a)
-#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c)
-#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040)
-#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041)
-#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */
-#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092)
-#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0)
-#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1)
-#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1))
-#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100)
-#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101)
-#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103)
-#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104)
-#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105)
-#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106)
-#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107)
-#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108)
-#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109)
-#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110)
-#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111)
-#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112)
-#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114)
-#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115)
-#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116)
-#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117)
-#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118)
-#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119)
-#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a)
-#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b)
-#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c)
-#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d)
-#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e)
-#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120)
-#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130)
-#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132)
-#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134)
-#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136)
-#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138)
-#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139)
-#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a)
-#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200)
-#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a)
-#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214)
-#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300)
-#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302)
-#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304)
-#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306)
-#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308)
-#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a)
-#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340)
-#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342)
-#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344)
-#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346)
-#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348)
-#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a)
-#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c)
-#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e)
-#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380)
-#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382)
-#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384)
-#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386)
-#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400)
-#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402)
-#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404)
-#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406)
-#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408)
-#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a)
-#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c)
-#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e)
-#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500)
-#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600)
-#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602)
-#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604)
-#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606)
-#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608)
-#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a)
-#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c)
-#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e)
-#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610)
-#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700)
-#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800)
-#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801)
-#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802)
-#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803)
-#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804)
-#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805)
-#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806)
-#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807)
-#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808)
-#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820)
-#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900)
-#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901)
-#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86)
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87)
-#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00)
-#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01)
-#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02)
-#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03)
-#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04)
-#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05)
-#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06)
-#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07)
-#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08)
-#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09)
-#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a)
-#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b)
-#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c)
-#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d)
-#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e)
-#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f)
-#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10)
-#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11)
-#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12)
-#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13)
-#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14)
-#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15)
-#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16)
-#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80)
-#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83)
-#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84)
-#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85)
-#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86)
-#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88)
-#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a)
-#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c)
-#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e)
-#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90)
-#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92)
-#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94)
-#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0)
-#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2)
-#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4)
-#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6)
-#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8)
-#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca)
-#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00)
-#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01)
-#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02)
-#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03)
-#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04)
-#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06)
-#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08)
-#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a)
-#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e)
-#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10)
-#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12)
-#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14)
-#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18)
-#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a)
-#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b)
-#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c)
-#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d)
-#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e)
-#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20)
-#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22)
-#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24)
-#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28)
-#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a)
-#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c)
-#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e)
-#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80)
-#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82)
-#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84)
-#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86)
-#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88)
-#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a)
-#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c)
-#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e)
-#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00)
-#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01)
-#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02)
-#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03)
-#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80)
-#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82)
-#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84)
-#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86)
-#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88)
-#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89)
-#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a)
-#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00)
-#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01)
-#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02)
-#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10)
-#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff)
-#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000)
-#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004)
-#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006)
-#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008)
-#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086)
-#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088)
-#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100)
-#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104)
-#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108)
-#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a)
-#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c)
-#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110)
-#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114)
-#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116)
-#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118)
-#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c)
-#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120)
-#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122)
-#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124)
-#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128)
-#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c)
-#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130)
-#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134)
-#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136)
-#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140)
-#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142)
-#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144)
-#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146)
-#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148)
-#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a)
-#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c)
-#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160)
-#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162)
-#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164)
-#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168)
-#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c)
-#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e)
-#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170)
-#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174)
-#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180)
-#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182)
-#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184)
-#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186)
-#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188)
-#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a)
-#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c)
-#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e)
-#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0)
-#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2)
-#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4)
-#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6)
-#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200)
-#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204)
-#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206)
-#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208)
-#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a)
-#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c)
-#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e)
-#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e)
-#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410)
-#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500)
-#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502)
-#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600)
-#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601)
-#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602)
-#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603)
-#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604)
-#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608)
-#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c)
-#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610)
-#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614)
-#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618)
-#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700)
-#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702)
-#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704)
-#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706)
-#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708)
-#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a)
-#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c)
-#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710)
-#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711)
-#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712)
-#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */
-#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800)
-#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900)
-#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901)
-#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902)
-#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903)
-#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904)
-#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906)
-#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980)
-#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981)
-#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982)
-#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983)
-#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984)
-#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985)
-#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986)
-#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987)
-#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988)
-#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0)
-#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1)
-#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2)
-#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4)
-#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00)
-#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02)
-#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02)
-#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04)
-#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40)
-#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42)
-#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44)
-#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00)
-#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01)
-#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02)
diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h
deleted file mode 100644
index d0167aa17534..000000000000
--- a/drivers/media/video/smiapp/smiapp-reg.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-reg.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __SMIAPP_REG_H_
-#define __SMIAPP_REG_H_
-
-#include "smiapp-reg-defs.h"
-
-/* Bits for above register */
-#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0)
-#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1)
-
-#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0)
-#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1)
-#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1)
-#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2)
-#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0)
-#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1)
-#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2)
-#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3)
-
-#define SMIAPP_SOFTWARE_RESET (1 << 0)
-
-#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0)
-#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1)
-
-#define SMIAPP_DPHY_CTRL_AUTOMATIC 0
-/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */
-#define SMIAPP_DPHY_CTRL_UI 1
-#define SMIAPP_DPHY_CTRL_REGISTER 2
-
-#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1
-#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2
-
-#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0
-#define SMIAPP_MODE_SELECT_STREAMING 1
-
-#define SMIAPP_SCALING_MODE_NONE 0
-#define SMIAPP_SCALING_MODE_HORIZONTAL 1
-#define SMIAPP_SCALING_MODE_BOTH 2
-
-#define SMIAPP_SCALING_CAPABILITY_NONE 0
-#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1
-#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */
-
-/* digital crop right before scaler */
-#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0
-#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1
-
-#define SMIAPP_BINNING_CAPABILITY_NO 0
-#define SMIAPP_BINNING_CAPABILITY_YES 1
-
-/* Maximum number of binning subtypes */
-#define SMIAPP_BINNING_SUBTYPES 253
-
-#define SMIAPP_PIXEL_ORDER_GRBG 0
-#define SMIAPP_PIXEL_ORDER_RGGB 1
-#define SMIAPP_PIXEL_ORDER_BGGR 2
-#define SMIAPP_PIXEL_ORDER_GBRG 3
-
-#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1
-#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2
-#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8
-#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16
-
-#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01
-#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02
-#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f
-#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0
-#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4
-
-#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000
-#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12
-#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff
-
-#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000
-#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28
-#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff
-
-#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1
-#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2
-#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3
-#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4
-#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5
-
-#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0
-#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1
-
-/* Scaling N factor */
-#define SMIAPP_SCALE_N 16
-
-/* Image statistics registers */
-/* Registers 0x2000 to 0x2fff are reserved for future
- * use for statistics features.
- */
-
-/* Manufacturer Specific Registers: 0x3000 to 0x3fff
- * The manufacturer specifies these as a black box.
- */
-
-#endif /* __SMIAPP_REG_H_ */
diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c
deleted file mode 100644
index b1812b17a407..000000000000
--- a/drivers/media/video/smiapp/smiapp-regs.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp-regs.c
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2011--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/delay.h>
-#include <linux/i2c.h>
-
-#include "smiapp.h"
-#include "smiapp-regs.h"
-
-static uint32_t float_to_u32_mul_1000000(struct i2c_client *client,
- uint32_t phloat)
-{
- int32_t exp;
- uint64_t man;
-
- if (phloat >= 0x80000000) {
- dev_err(&client->dev, "this is a negative number\n");
- return 0;
- }
-
- if (phloat == 0x7f800000)
- return ~0; /* Inf. */
-
- if ((phloat & 0x7f800000) == 0x7f800000) {
- dev_err(&client->dev, "NaN or other special number\n");
- return 0;
- }
-
- /* Valid cases begin here */
- if (phloat == 0)
- return 0; /* Valid zero */
-
- if (phloat > 0x4f800000)
- return ~0; /* larger than 4294967295 */
-
- /*
- * Unbias exponent (note how phloat is now guaranteed to
- * have 0 in the high bit)
- */
- exp = ((int32_t)phloat >> 23) - 127;
-
- /* Extract mantissa, add missing '1' bit and it's in MHz */
- man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL;
-
- if (exp < 0)
- man >>= -exp;
- else
- man <<= exp;
-
- man >>= 23; /* Remove mantissa bias */
-
- return man & 0xffffffff;
-}
-
-
-/*
- * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,
- u16 len, u32 *val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- struct i2c_msg msg;
- unsigned char data[4];
- u16 offset = reg;
- int r;
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.len = 2;
- msg.buf = data;
-
- /* high byte goes out first */
- data[0] = (u8) (offset >> 8);
- data[1] = (u8) offset;
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r != 1) {
- if (r >= 0)
- r = -EBUSY;
- goto err;
- }
-
- msg.len = len;
- msg.flags = I2C_M_RD;
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r != 1) {
- if (r >= 0)
- r = -EBUSY;
- goto err;
- }
-
- *val = 0;
- /* high byte comes first */
- switch (len) {
- case SMIA_REG_32BIT:
- *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +
- data[3];
- break;
- case SMIA_REG_16BIT:
- *val = (data[0] << 8) + data[1];
- break;
- case SMIA_REG_8BIT:
- *val = data[0];
- break;
- default:
- BUG();
- }
-
- return 0;
-
-err:
- dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r);
-
- return r;
-}
-
-/* Read a register using 8-bit access only. */
-static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg,
- u16 len, u32 *val)
-{
- unsigned int i;
- int rval;
-
- *val = 0;
-
- for (i = 0; i < len; i++) {
- u32 val8;
-
- rval = ____smiapp_read(sensor, reg + i, 1, &val8);
- if (rval < 0)
- return rval;
- *val |= val8 << ((len - i - 1) << 3);
- }
-
- return 0;
-}
-
-/*
- * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,
- bool only8)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- unsigned int len = (u8)(reg >> 16);
- int rval;
-
- if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT
- && len != SMIA_REG_32BIT)
- return -EINVAL;
-
- if (smiapp_quirk_reg(sensor, reg, val))
- goto found_quirk;
-
- if (len == SMIA_REG_8BIT && !only8)
- rval = ____smiapp_read(sensor, (u16)reg, len, val);
- else
- rval = ____smiapp_read_8only(sensor, (u16)reg, len, val);
- if (rval < 0)
- return rval;
-
-found_quirk:
- if (reg & SMIA_REG_FLAG_FLOAT)
- *val = float_to_u32_mul_1000000(client, *val);
-
- return 0;
-}
-
-int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
-{
- return __smiapp_read(
- sensor, reg, val,
- smiapp_needs_quirk(sensor,
- SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));
-}
-
-int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)
-{
- return __smiapp_read(sensor, reg, val, true);
-}
-
-/*
- * Write to a 8/16-bit register.
- * Returns zero if successful, or non-zero otherwise.
- */
-int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
- struct i2c_msg msg;
- unsigned char data[6];
- unsigned int retries;
- unsigned int flags = reg >> 24;
- unsigned int len = (u8)(reg >> 16);
- u16 offset = reg;
- int r;
-
- if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT &&
- len != SMIA_REG_32BIT) || flags)
- return -EINVAL;
-
- msg.addr = client->addr;
- msg.flags = 0; /* Write */
- msg.len = 2 + len;
- msg.buf = data;
-
- /* high byte goes out first */
- data[0] = (u8) (reg >> 8);
- data[1] = (u8) (reg & 0xff);
-
- switch (len) {
- case SMIA_REG_8BIT:
- data[2] = val;
- break;
- case SMIA_REG_16BIT:
- data[2] = val >> 8;
- data[3] = val;
- break;
- case SMIA_REG_32BIT:
- data[2] = val >> 24;
- data[3] = val >> 16;
- data[4] = val >> 8;
- data[5] = val;
- break;
- default:
- BUG();
- }
-
- for (retries = 0; retries < 5; retries++) {
- /*
- * Due to unknown reason sensor stops responding. This
- * loop is a temporaty solution until the root cause
- * is found.
- */
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r == 1) {
- if (retries)
- dev_err(&client->dev,
- "sensor i2c stall encountered. "
- "retries: %d\n", retries);
- return 0;
- }
-
- usleep_range(2000, 2000);
- }
-
- dev_err(&client->dev,
- "wrote 0x%x to offset 0x%x error %d\n", val, offset, r);
-
- return r;
-}
diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h
deleted file mode 100644
index 587f7f11238d..000000000000
--- a/drivers/media/video/smiapp/smiapp.h
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * drivers/media/video/smiapp/smiapp.h
- *
- * Generic driver for SMIA/SMIA++ compliant camera modules
- *
- * Copyright (C) 2010--2012 Nokia Corporation
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __SMIAPP_PRIV_H_
-#define __SMIAPP_PRIV_H_
-
-#include <linux/mutex.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-subdev.h>
-#include <media/smiapp.h>
-
-#include "smiapp-pll.h"
-#include "smiapp-reg.h"
-#include "smiapp-regs.h"
-#include "smiapp-quirk.h"
-
-/*
- * Standard SMIA++ constants
- */
-#define SMIA_VERSION_1 10
-#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */
-#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */
-#define SMIAPP_VERSION_1 10
-
-#define SMIAPP_PROFILE_0 0
-#define SMIAPP_PROFILE_1 1
-#define SMIAPP_PROFILE_2 2
-
-#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */
-
-#define SMIAPP_RESET_DELAY_CLOCKS 2400
-#define SMIAPP_RESET_DELAY(clk) \
- (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \
- + (clk) / 1000 - 1) / ((clk) / 1000))
-
-#include "smiapp-limits.h"
-
-struct smiapp_quirk;
-
-#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0)
-
-struct smiapp_module_ident {
- u8 manufacturer_id;
- u16 model_id;
- u8 revision_number_major;
-
- u8 flags;
-
- char *name;
- const struct smiapp_quirk *quirk;
-};
-
-struct smiapp_module_info {
- u32 manufacturer_id;
- u32 model_id;
- u32 revision_number_major;
- u32 revision_number_minor;
-
- u32 module_year;
- u32 module_month;
- u32 module_day;
-
- u32 sensor_manufacturer_id;
- u32 sensor_model_id;
- u32 sensor_revision_number;
- u32 sensor_firmware_version;
-
- u32 smia_version;
- u32 smiapp_version;
-
- u32 smiapp_profile;
-
- char *name;
- const struct smiapp_quirk *quirk;
-};
-
-#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \
- { .manufacturer_id = manufacturer, \
- .model_id = model, \
- .revision_number_major = rev, \
- .flags = fl, \
- .name = _name, \
- .quirk = _quirk, }
-
-#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \
- { .manufacturer_id = manufacturer, \
- .model_id = model, \
- .revision_number_major = rev, \
- .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
- .name = _name, \
- .quirk = _quirk, }
-
-#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \
- { .manufacturer_id = manufacturer, \
- .model_id = model, \
- .revision_number_major = rev, \
- .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
- .name = _name, }
-
-#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \
- { .manufacturer_id = manufacturer, \
- .model_id = model, \
- .revision_number_major = rev, \
- .flags = 0, \
- .name = _name, \
- .quirk = _quirk, }
-
-#define SMIAPP_IDENT(manufacturer, model, rev, _name) \
- { .manufacturer_id = manufacturer, \
- .model_id = model, \
- .revision_number_major = rev, \
- .flags = 0, \
- .name = _name, }
-
-struct smiapp_reg_limits {
- u32 addr;
- char *what;
-};
-
-extern struct smiapp_reg_limits smiapp_reg_limits[];
-
-struct smiapp_csi_data_format {
- u32 code;
- u8 width;
- u8 compressed;
- u8 pixel_order;
-};
-
-#define SMIAPP_SUBDEVS 3
-
-#define SMIAPP_PA_PAD_SRC 0
-#define SMIAPP_PAD_SINK 0
-#define SMIAPP_PAD_SRC 1
-#define SMIAPP_PADS 2
-
-struct smiapp_binning_subtype {
- u8 horizontal:4;
- u8 vertical:4;
-} __packed;
-
-struct smiapp_subdev {
- struct v4l2_subdev sd;
- struct media_pad pads[2];
- struct v4l2_rect sink_fmt;
- struct v4l2_rect crop[2];
- struct v4l2_rect compose; /* compose on sink */
- unsigned short sink_pad;
- unsigned short source_pad;
- int npads;
- struct smiapp_sensor *sensor;
- struct v4l2_ctrl_handler ctrl_handler;
-};
-
-/*
- * struct smiapp_sensor - Main device structure
- */
-struct smiapp_sensor {
- /*
- * "mutex" is used to serialise access to all fields here
- * except v4l2_ctrls at the end of the struct. "mutex" is also
- * used to serialise access to file handle specific
- * information. The exception to this rule is the power_mutex
- * below.
- */
- struct mutex mutex;
- /*
- * power_mutex is used to serialise power management related
- * activities. Acquiring "mutex" at that time isn't necessary
- * since there are no other users anyway.
- */
- struct mutex power_mutex;
- struct smiapp_subdev ssds[SMIAPP_SUBDEVS];
- u32 ssds_used;
- struct smiapp_subdev *src;
- struct smiapp_subdev *binner;
- struct smiapp_subdev *scaler;
- struct smiapp_subdev *pixel_array;
- struct smiapp_platform_data *platform_data;
- struct regulator *vana;
- struct clk *ext_clk;
- u32 limits[SMIAPP_LIMIT_LAST];
- u8 nbinning_subtypes;
- struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES];
- u32 mbus_frame_fmts;
- const struct smiapp_csi_data_format *csi_format;
- const struct smiapp_csi_data_format *internal_csi_format;
- u32 default_mbus_frame_fmts;
- int default_pixel_order;
-
- u8 binning_horizontal;
- u8 binning_vertical;
-
- u8 scale_m;
- u8 scaling_mode;
-
- u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */
- u8 flash_capability;
- u8 frame_skip;
-
- int power_count;
-
- bool streaming;
- bool dev_init_done;
-
- u8 *nvm; /* nvm memory buffer */
- unsigned int nvm_size; /* bytes */
-
- struct smiapp_module_info minfo;
-
- struct smiapp_pll pll;
-
- /* Pixel array controls */
- struct v4l2_ctrl *analog_gain;
- struct v4l2_ctrl *exposure;
- struct v4l2_ctrl *hflip;
- struct v4l2_ctrl *vflip;
- struct v4l2_ctrl *vblank;
- struct v4l2_ctrl *hblank;
- struct v4l2_ctrl *pixel_rate_parray;
- /* src controls */
- struct v4l2_ctrl *link_freq;
- struct v4l2_ctrl *pixel_rate_csi;
-};
-
-#define to_smiapp_subdev(_sd) \
- container_of(_sd, struct smiapp_subdev, sd)
-
-#define to_smiapp_sensor(_sd) \
- (to_smiapp_subdev(_sd)->sensor)
-
-#endif /* __SMIAPP_PRIV_H_ */
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
deleted file mode 100644
index 1bde255e45df..000000000000
--- a/drivers/media/video/soc_camera.c
+++ /dev/null
@@ -1,1546 +0,0 @@
-/*
- * camera image capture (abstract) bus driver
- *
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * This driver provides an interface between platform-specific camera
- * busses and camera devices. It should be used if the camera is
- * connected not over a "proper" bus like PCI or USB, but over a
- * special bus, like, for example, the Quick Capture interface on PXA270
- * SoCs. Later it should also be used for i.MX31 SoCs from Freescale.
- * It can handle multiple cameras and / or multiple busses, which can
- * be used, e.g., in stereo-vision applications.
- *
- * 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/device.h>
-#include <linux/err.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-#include <linux/vmalloc.h>
-
-#include <media/soc_camera.h>
-#include <media/v4l2-common.h>
-#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 */
-#define DEFAULT_WIDTH 640
-#define DEFAULT_HEIGHT 480
-
-#define is_streaming(ici, icd) \
- (((ici)->ops->init_videobuf) ? \
- (icd)->vb_vidq.streaming : \
- vb2_is_streaming(&(icd)->vb2_vidq))
-
-static LIST_HEAD(hosts);
-static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
-
-static int soc_camera_power_on(struct soc_camera_device *icd,
- struct soc_camera_link *icl)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret = regulator_bulk_enable(icl->num_regulators,
- icl->regulators);
- if (ret < 0) {
- dev_err(icd->pdev, "Cannot enable regulators\n");
- return ret;
- }
-
- if (icl->power) {
- ret = icl->power(icd->control, 1);
- if (ret < 0) {
- dev_err(icd->pdev,
- "Platform failed to power-on the camera.\n");
- goto elinkpwr;
- }
- }
-
- ret = v4l2_subdev_call(sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- goto esdpwr;
-
- return 0;
-
-esdpwr:
- if (icl->power)
- icl->power(icd->control, 0);
-elinkpwr:
- regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
- return ret;
-}
-
-static int soc_camera_power_off(struct soc_camera_device *icd,
- struct soc_camera_link *icl)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret = v4l2_subdev_call(sd, core, s_power, 0);
-
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- if (icl->power) {
- ret = icl->power(icd->control, 0);
- if (ret < 0) {
- dev_err(icd->pdev,
- "Platform failed to power-off the camera.\n");
- return ret;
- }
- }
-
- ret = regulator_bulk_disable(icl->num_regulators,
- icl->regulators);
- if (ret < 0)
- dev_err(icd->pdev, "Cannot disable regulators\n");
-
- return ret;
-}
-
-const struct soc_camera_format_xlate *soc_camera_xlate_by_fourcc(
- struct soc_camera_device *icd, unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < icd->num_user_formats; i++)
- if (icd->user_formats[i].host_fmt->fourcc == fourcc)
- return icd->user_formats + i;
- return NULL;
-}
-EXPORT_SYMBOL(soc_camera_xlate_by_fourcc);
-
-/**
- * soc_camera_apply_board_flags() - apply platform SOCAM_SENSOR_INVERT_* flags
- * @icl: camera platform parameters
- * @cfg: media bus configuration
- * @return: resulting flags
- */
-unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
- const struct v4l2_mbus_config *cfg)
-{
- unsigned long f, flags = cfg->flags;
-
- /* If only one of the two polarities is supported, switch to the opposite */
- if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) {
- f = flags & (V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW);
- if (f == V4L2_MBUS_HSYNC_ACTIVE_HIGH || f == V4L2_MBUS_HSYNC_ACTIVE_LOW)
- flags ^= V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW;
- }
-
- if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) {
- f = flags & (V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW);
- if (f == V4L2_MBUS_VSYNC_ACTIVE_HIGH || f == V4L2_MBUS_VSYNC_ACTIVE_LOW)
- flags ^= V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW;
- }
-
- if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) {
- f = flags & (V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING);
- if (f == V4L2_MBUS_PCLK_SAMPLE_RISING || f == V4L2_MBUS_PCLK_SAMPLE_FALLING)
- flags ^= V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING;
- }
-
- return flags;
-}
-EXPORT_SYMBOL(soc_camera_apply_board_flags);
-
-#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
- ((x) >> 24) & 0xff
-
-static int soc_camera_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- const struct soc_camera_format_xlate *xlate;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int ret;
-
- dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
- pixfmtstr(pix->pixelformat), pix->width, pix->height);
-
- if (pix->pixelformat != V4L2_PIX_FMT_JPEG &&
- !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) {
- pix->bytesperline = 0;
- pix->sizeimage = 0;
- }
-
- ret = ici->ops->try_fmt(icd, f);
- if (ret < 0)
- return ret;
-
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate)
- return -EINVAL;
-
- ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt);
- if (ret < 0)
- return ret;
-
- pix->bytesperline = max_t(u32, pix->bytesperline, ret);
-
- ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline,
- pix->height);
- if (ret < 0)
- return ret;
-
- pix->sizeimage = max_t(u32, pix->sizeimage, ret);
-
- return 0;
-}
-
-static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct soc_camera_device *icd = file->private_data;
-
- WARN_ON(priv != file->private_data);
-
- /* Only single-plane capture is supported so far */
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* limit format to hardware capabilities */
- return soc_camera_try_fmt(icd, f);
-}
-
-static int soc_camera_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index != 0)
- return -EINVAL;
-
- /* default is camera */
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_UNKNOWN;
- strcpy(inp->name, "Camera");
-
- return 0;
-}
-
-static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
-
- return 0;
-}
-
-static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, s_std, *a);
-}
-
-static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, g_std, a);
-}
-
-static int soc_camera_enum_framesizes(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->parent);
-
- return ici->ops->enum_framesizes(icd, fsize);
-}
-
-static int soc_camera_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- int ret;
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- WARN_ON(priv != file->private_data);
-
- if (icd->streamer && icd->streamer != file)
- return -EBUSY;
-
- 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);
- }
-
- if (!ret && !icd->streamer)
- icd->streamer = file;
-
- return ret;
-}
-
-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->parent);
-
- WARN_ON(priv != file->private_data);
-
- 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->parent);
-
- WARN_ON(priv != file->private_data);
-
- if (icd->streamer != file)
- return -EBUSY;
-
- 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->parent);
-
- WARN_ON(priv != file->private_data);
-
- if (icd->streamer != file)
- return -EBUSY;
-
- 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);
-}
-
-static int soc_camera_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *create)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- /* videobuf2 only */
- if (ici->ops->init_videobuf)
- return -EINVAL;
- else
- return vb2_create_bufs(&icd->vb2_vidq, create);
-}
-
-static int soc_camera_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- /* videobuf2 only */
- if (ici->ops->init_videobuf)
- return -EINVAL;
- else
- return vb2_prepare_buf(&icd->vb2_vidq, b);
-}
-
-/* Always entered with .video_lock held */
-static int soc_camera_init_user_formats(struct soc_camera_device *icd)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- unsigned int i, fmts = 0, raw_fmts = 0;
- int ret;
- enum v4l2_mbus_pixelcode code;
-
- while (!v4l2_subdev_call(sd, video, enum_mbus_fmt, raw_fmts, &code))
- raw_fmts++;
-
- if (!ici->ops->get_formats)
- /*
- * Fallback mode - the host will have to serve all
- * sensor-provided formats one-to-one to the user
- */
- fmts = raw_fmts;
- else
- /*
- * First pass - only count formats this host-sensor
- * configuration can provide
- */
- for (i = 0; i < raw_fmts; i++) {
- ret = ici->ops->get_formats(icd, i, NULL);
- if (ret < 0)
- return ret;
- fmts += ret;
- }
-
- if (!fmts)
- return -ENXIO;
-
- icd->user_formats =
- vmalloc(fmts * sizeof(struct soc_camera_format_xlate));
- if (!icd->user_formats)
- return -ENOMEM;
-
- dev_dbg(icd->pdev, "Found %d supported formats.\n", fmts);
-
- /* Second pass - actually fill data formats */
- fmts = 0;
- for (i = 0; i < raw_fmts; i++)
- if (!ici->ops->get_formats) {
- v4l2_subdev_call(sd, video, enum_mbus_fmt, i, &code);
- icd->user_formats[fmts].host_fmt =
- soc_mbus_get_fmtdesc(code);
- if (icd->user_formats[fmts].host_fmt)
- icd->user_formats[fmts++].code = code;
- } else {
- ret = ici->ops->get_formats(icd, i,
- &icd->user_formats[fmts]);
- if (ret < 0)
- goto egfmt;
- fmts += ret;
- }
-
- icd->num_user_formats = fmts;
- icd->current_fmt = &icd->user_formats[0];
-
- return 0;
-
-egfmt:
- vfree(icd->user_formats);
- return ret;
-}
-
-/* Always entered with .video_lock held */
-static void soc_camera_free_user_formats(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- if (ici->ops->put_formats)
- ici->ops->put_formats(icd);
- icd->current_fmt = NULL;
- icd->num_user_formats = 0;
- vfree(icd->user_formats);
- icd->user_formats = NULL;
-}
-
-/* Called with .vb_lock held, or from the first open(2), see comment there */
-static int soc_camera_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int ret;
-
- dev_dbg(icd->pdev, "S_FMT(%c%c%c%c, %ux%u)\n",
- pixfmtstr(pix->pixelformat), pix->width, pix->height);
-
- /* We always call try_fmt() before set_fmt() or set_crop() */
- ret = soc_camera_try_fmt(icd, f);
- if (ret < 0)
- return ret;
-
- ret = ici->ops->set_fmt(icd, f);
- if (ret < 0) {
- return ret;
- } else if (!icd->current_fmt ||
- icd->current_fmt->host_fmt->fourcc != pix->pixelformat) {
- dev_err(icd->pdev,
- "Host driver hasn't set up current format correctly!\n");
- return -EINVAL;
- }
-
- icd->user_width = pix->width;
- icd->user_height = pix->height;
- icd->bytesperline = pix->bytesperline;
- icd->sizeimage = pix->sizeimage;
- icd->colorspace = pix->colorspace;
- icd->field = pix->field;
- if (ici->ops->init_videobuf)
- icd->vb_vidq.field = pix->field;
-
- dev_dbg(icd->pdev, "set width: %d height: %d\n",
- icd->user_width, icd->user_height);
-
- /* set physical bus parameters */
- return ici->ops->set_bus_param(icd);
-}
-
-static int soc_camera_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct soc_camera_device *icd = dev_get_drvdata(vdev->parent);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- struct soc_camera_host *ici;
- int ret;
-
- if (!to_soc_camera_control(icd))
- /* No device driver attached */
- return -ENODEV;
-
- ici = to_soc_camera_host(icd->parent);
-
- if (!try_module_get(ici->ops->owner)) {
- dev_err(icd->pdev, "Couldn't lock capture bus driver.\n");
- return -EINVAL;
- }
-
- icd->use_count++;
-
- /* Now we really have to activate the camera */
- if (icd->use_count == 1) {
- /* Restore parameters before the last close() per V4L2 API */
- struct v4l2_format f = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .fmt.pix = {
- .width = icd->user_width,
- .height = icd->user_height,
- .field = icd->field,
- .colorspace = icd->colorspace,
- .pixelformat =
- icd->current_fmt->host_fmt->fourcc,
- },
- };
-
- /* The camera could have been already on, try to reset */
- if (icl->reset)
- icl->reset(icd->pdev);
-
- /* Don't mess with the host during probe */
- mutex_lock(&ici->host_lock);
- ret = ici->ops->add(icd);
- mutex_unlock(&ici->host_lock);
- if (ret < 0) {
- dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret);
- goto eiciadd;
- }
-
- ret = soc_camera_power_on(icd, icl);
- if (ret < 0)
- goto epower;
-
- pm_runtime_enable(&icd->vdev->dev);
- ret = pm_runtime_resume(&icd->vdev->dev);
- if (ret < 0 && ret != -ENOSYS)
- goto eresume;
-
- /*
- * Try to configure with default parameters. Notice: this is the
- * very first open, so, we cannot race against other calls,
- * apart from someone else calling open() simultaneously, but
- * .video_lock is protecting us against it.
- */
- ret = soc_camera_set_fmt(icd, &f);
- if (ret < 0)
- goto esfmt;
-
- 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;
- }
- v4l2_ctrl_handler_setup(&icd->ctrl_handler);
- }
-
- file->private_data = icd;
- dev_dbg(icd->pdev, "camera device open\n");
-
- return 0;
-
- /*
- * First four errors are entered with the .video_lock held
- * and use_count == 1
- */
-einitvb:
-esfmt:
- pm_runtime_disable(&icd->vdev->dev);
-eresume:
- soc_camera_power_off(icd, icl);
-epower:
- ici->ops->remove(icd);
-eiciadd:
- icd->use_count--;
- module_put(ici->ops->owner);
-
- return ret;
-}
-
-static int soc_camera_close(struct file *file)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- icd->use_count--;
- if (!icd->use_count) {
- struct soc_camera_link *icl = to_soc_camera_link(icd);
-
- pm_runtime_suspend(&icd->vdev->dev);
- pm_runtime_disable(&icd->vdev->dev);
-
- if (ici->ops->init_videobuf2)
- vb2_queue_release(&icd->vb2_vidq);
- ici->ops->remove(icd);
-
- soc_camera_power_off(icd, icl);
- }
-
- if (icd->streamer == file)
- icd->streamer = NULL;
-
- module_put(ici->ops->owner);
-
- dev_dbg(icd->pdev, "camera device close\n");
-
- return 0;
-}
-
-static ssize_t soc_camera_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct soc_camera_device *icd = file->private_data;
- int err = -EINVAL;
-
- dev_err(icd->pdev, "camera device read not implemented\n");
-
- return err;
-}
-
-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->parent);
- int err;
-
- dev_dbg(icd->pdev, "mmap called, vma=0x%08lx\n", (unsigned long)vma);
-
- if (icd->streamer != file)
- return -EBUSY;
-
- if (ici->ops->init_videobuf)
- err = videobuf_mmap_mapper(&icd->vb_vidq, vma);
- else
- err = vb2_mmap(&icd->vb2_vidq, vma);
-
- dev_dbg(icd->pdev, "vma start=0x%08lx, size=%ld, ret=%d\n",
- (unsigned long)vma->vm_start,
- (unsigned long)vma->vm_end - (unsigned long)vma->vm_start,
- err);
-
- return err;
-}
-
-static unsigned int soc_camera_poll(struct file *file, poll_table *pt)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- if (icd->streamer != file)
- return -EBUSY;
-
- if (ici->ops->init_videobuf && list_empty(&icd->vb_vidq.stream)) {
- dev_err(icd->pdev, "Trying to poll with no queued buffers!\n");
- return POLLERR;
- }
-
- 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,
- .release = soc_camera_close,
- .unlocked_ioctl = video_ioctl2,
- .read = soc_camera_read,
- .mmap = soc_camera_mmap,
- .poll = soc_camera_poll,
-};
-
-static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct soc_camera_device *icd = file->private_data;
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dev_warn(icd->pdev, "Wrong buf-type %d\n", f->type);
- return -EINVAL;
- }
-
- if (icd->streamer && icd->streamer != file)
- return -EBUSY;
-
- if (is_streaming(to_soc_camera_host(icd->parent), icd)) {
- dev_err(icd->pdev, "S_FMT denied: queue initialised\n");
- return -EBUSY;
- }
-
- ret = soc_camera_set_fmt(icd, f);
-
- if (!ret && !icd->streamer)
- icd->streamer = file;
-
- return ret;
-}
-
-static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct soc_camera_device *icd = file->private_data;
- const struct soc_mbus_pixelfmt *format;
-
- WARN_ON(priv != file->private_data);
-
- if (f->index >= icd->num_user_formats)
- return -EINVAL;
-
- format = icd->user_formats[f->index].host_fmt;
-
- if (format->name)
- strlcpy(f->description, format->name, sizeof(f->description));
- f->pixelformat = format->fourcc;
- return 0;
-}
-
-static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- WARN_ON(priv != file->private_data);
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- pix->width = icd->user_width;
- pix->height = icd->user_height;
- pix->bytesperline = icd->bytesperline;
- pix->sizeimage = icd->sizeimage;
- pix->field = icd->field;
- pix->pixelformat = icd->current_fmt->host_fmt->fourcc;
- pix->colorspace = icd->colorspace;
- dev_dbg(icd->pdev, "current_fmt->fourcc: 0x%08x\n",
- icd->current_fmt->host_fmt->fourcc);
- return 0;
-}
-
-static int soc_camera_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- WARN_ON(priv != file->private_data);
-
- strlcpy(cap->driver, ici->drv_name, sizeof(cap->driver));
- return ici->ops->querycap(ici, cap);
-}
-
-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->parent);
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- int ret;
-
- WARN_ON(priv != file->private_data);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (icd->streamer != file)
- return -EBUSY;
-
- /* This calls buf_queue from host driver's videobuf_queue_ops */
- 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;
-}
-
-static int soc_camera_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type i)
-{
- 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->parent);
-
- WARN_ON(priv != file->private_data);
-
- if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (icd->streamer != file)
- return -EBUSY;
-
- /*
- * This calls buf_release from host driver's videobuf_queue_ops for all
- * remaining buffers. When the last buffer is freed, stop capture
- */
- 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);
-
- return 0;
-}
-
-static int soc_camera_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- return ici->ops->cropcap(icd, a);
-}
-
-static int soc_camera_g_crop(struct file *file, void *fh,
- struct v4l2_crop *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- int ret;
-
- ret = ici->ops->get_crop(icd, a);
-
- return ret;
-}
-
-/*
- * According to the V4L2 API, drivers shall not update the struct v4l2_crop
- * argument with the actual geometry, instead, the user shall use G_CROP to
- * retrieve it.
- */
-static int soc_camera_s_crop(struct file *file, void *fh,
- struct v4l2_crop *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct v4l2_rect *rect = &a->c;
- struct v4l2_crop current_crop;
- int ret;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- dev_dbg(icd->pdev, "S_CROP(%ux%u@%u:%u)\n",
- rect->width, rect->height, rect->left, rect->top);
-
- /* If get_crop fails, we'll let host and / or client drivers decide */
- ret = ici->ops->get_crop(icd, &current_crop);
-
- /* Prohibit window size change with initialised buffers */
- if (ret < 0) {
- dev_err(icd->pdev,
- "S_CROP denied: getting current crop failed\n");
- } else if ((a->c.width == current_crop.c.width &&
- a->c.height == current_crop.c.height) ||
- !is_streaming(ici, icd)) {
- /* same size or not streaming - use .set_crop() */
- ret = ici->ops->set_crop(icd, a);
- } else if (ici->ops->set_livecrop) {
- ret = ici->ops->set_livecrop(icd, a);
- } else {
- dev_err(icd->pdev,
- "S_CROP denied: queue initialised and sizes differ\n");
- ret = -EBUSY;
- }
-
- return ret;
-}
-
-static int soc_camera_g_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- if (ici->ops->get_parm)
- return ici->ops->get_parm(icd, a);
-
- return -ENOIOCTLCMD;
-}
-
-static int soc_camera_s_parm(struct file *file, void *fh,
- struct v4l2_streamparm *a)
-{
- struct soc_camera_device *icd = file->private_data;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
-
- if (ici->ops->set_parm)
- return ici->ops->set_parm(icd, a);
-
- return -ENOIOCTLCMD;
-}
-
-static int soc_camera_g_chip_ident(struct file *file, void *fh,
- struct v4l2_dbg_chip_ident *id)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, g_chip_ident, id);
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int soc_camera_g_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, g_register, reg);
-}
-
-static int soc_camera_s_register(struct file *file, void *fh,
- struct v4l2_dbg_register *reg)
-{
- struct soc_camera_device *icd = file->private_data;
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
-
- return v4l2_subdev_call(sd, core, s_register, reg);
-}
-#endif
-
-static int soc_camera_probe(struct soc_camera_device *icd);
-
-/* So far this function cannot fail */
-static void scan_add_host(struct soc_camera_host *ici)
-{
- struct soc_camera_device *icd;
-
- mutex_lock(&ici->host_lock);
-
- list_for_each_entry(icd, &devices, list) {
- if (icd->iface == ici->nr) {
- int ret;
-
- icd->parent = ici->v4l2_dev.dev;
- ret = soc_camera_probe(icd);
- }
- }
-
- mutex_unlock(&ici->host_lock);
-}
-
-#ifdef CONFIG_I2C_BOARDINFO
-static int soc_camera_init_i2c(struct soc_camera_device *icd,
- struct soc_camera_link *icl)
-{
- struct i2c_client *client;
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
- struct v4l2_subdev *subdev;
-
- if (!adap) {
- dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n",
- icl->i2c_adapter_id);
- goto ei2cga;
- }
-
- icl->board_info->platform_data = icl;
-
- subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
- icl->board_info, NULL);
- if (!subdev)
- goto ei2cnd;
-
- client = v4l2_get_subdevdata(subdev);
-
- /* Use to_i2c_client(dev) to recover the i2c client */
- icd->control = &client->dev;
-
- return 0;
-ei2cnd:
- i2c_put_adapter(adap);
-ei2cga:
- return -ENODEV;
-}
-
-static void soc_camera_free_i2c(struct soc_camera_device *icd)
-{
- struct i2c_client *client =
- to_i2c_client(to_soc_camera_control(icd));
- struct i2c_adapter *adap = client->adapter;
-
- icd->control = NULL;
- v4l2_device_unregister_subdev(i2c_get_clientdata(client));
- i2c_unregister_device(client);
- i2c_put_adapter(adap);
-}
-#else
-#define soc_camera_init_i2c(icd, icl) (-ENODEV)
-#define soc_camera_free_i2c(icd) do {} while (0)
-#endif
-
-static int soc_camera_video_start(struct soc_camera_device *icd);
-static int video_dev_create(struct soc_camera_device *icd);
-/* Called during host-driver probe */
-static int soc_camera_probe(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- struct device *control = NULL;
- struct v4l2_subdev *sd;
- struct v4l2_mbus_framefmt mf;
- int ret;
-
- dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev));
-
- /*
- * Currently the subdev with the largest number of controls (13) is
- * ov6550. So let's pick 16 as a hint for the control handler. Note
- * that this is a hint only: too large and you waste some memory, too
- * small and there is a (very) small performance hit when looking up
- * controls in the internal hash.
- */
- ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16);
- if (ret < 0)
- return ret;
-
- ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
- icl->regulators);
- if (ret < 0)
- goto ereg;
-
- /* The camera could have been already on, try to reset */
- if (icl->reset)
- icl->reset(icd->pdev);
-
- ret = ici->ops->add(icd);
- if (ret < 0)
- goto eadd;
-
- /*
- * This will not yet call v4l2_subdev_core_ops::s_power(1), because the
- * subdevice has not been initialised yet. We'll have to call it once
- * again after initialisation, even though it shouldn't be needed, we
- * don't do any IO here.
- */
- ret = soc_camera_power_on(icd, icl);
- if (ret < 0)
- goto epower;
-
- /* Must have icd->vdev before registering the device */
- ret = video_dev_create(icd);
- if (ret < 0)
- goto evdc;
-
- /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
- if (icl->board_info) {
- ret = soc_camera_init_i2c(icd, icl);
- if (ret < 0)
- goto eadddev;
- } else if (!icl->add_device || !icl->del_device) {
- ret = -EINVAL;
- goto eadddev;
- } else {
- if (icl->module_name)
- ret = request_module(icl->module_name);
-
- ret = icl->add_device(icd);
- if (ret < 0)
- goto eadddev;
-
- /*
- * FIXME: this is racy, have to use driver-binding notification,
- * when it is available
- */
- control = to_soc_camera_control(icd);
- if (!control || !control->driver || !dev_get_drvdata(control) ||
- !try_module_get(control->driver->owner)) {
- icl->del_device(icd);
- ret = -ENODEV;
- goto enodrv;
- }
- }
-
- sd = soc_camera_to_subdev(icd);
- sd->grp_id = soc_camera_grp_id(icd);
- v4l2_set_subdev_hostdata(sd, icd);
-
- if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler))
- goto ectrl;
-
- /* At this point client .probe() should have run already */
- ret = soc_camera_init_user_formats(icd);
- if (ret < 0)
- goto eiufmt;
-
- icd->field = V4L2_FIELD_ANY;
-
- /*
- * ..._video_start() will create a device node, video_register_device()
- * itself is protected against concurrent open() calls, but we also have
- * to protect our data.
- */
- mutex_lock(&icd->video_lock);
-
- ret = soc_camera_video_start(icd);
- if (ret < 0)
- goto evidstart;
-
- ret = v4l2_subdev_call(sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- goto esdpwr;
-
- /* Try to improve our guess of a reasonable window format */
- if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
- icd->user_width = mf.width;
- icd->user_height = mf.height;
- icd->colorspace = mf.colorspace;
- icd->field = mf.field;
- }
-
- ici->ops->remove(icd);
-
- soc_camera_power_off(icd, icl);
-
- mutex_unlock(&icd->video_lock);
-
- return 0;
-
-esdpwr:
- video_unregister_device(icd->vdev);
-evidstart:
- mutex_unlock(&icd->video_lock);
- soc_camera_free_user_formats(icd);
-eiufmt:
-ectrl:
- if (icl->board_info) {
- soc_camera_free_i2c(icd);
- } else {
- icl->del_device(icd);
- module_put(control->driver->owner);
- }
-enodrv:
-eadddev:
- video_device_release(icd->vdev);
- icd->vdev = NULL;
-evdc:
- soc_camera_power_off(icd, icl);
-epower:
- ici->ops->remove(icd);
-eadd:
- regulator_bulk_free(icl->num_regulators, icl->regulators);
-ereg:
- v4l2_ctrl_handler_free(&icd->ctrl_handler);
- return ret;
-}
-
-/*
- * This is called on device_unregister, which only means we have to disconnect
- * from the host, but not remove ourselves from the device list
- */
-static int soc_camera_remove(struct soc_camera_device *icd)
-{
- struct soc_camera_link *icl = to_soc_camera_link(icd);
- struct video_device *vdev = icd->vdev;
-
- BUG_ON(!icd->parent);
-
- v4l2_ctrl_handler_free(&icd->ctrl_handler);
- if (vdev) {
- video_unregister_device(vdev);
- icd->vdev = NULL;
- }
-
- if (icl->board_info) {
- soc_camera_free_i2c(icd);
- } else {
- struct device_driver *drv = to_soc_camera_control(icd)->driver;
- if (drv) {
- icl->del_device(icd);
- module_put(drv->owner);
- }
- }
- soc_camera_free_user_formats(icd);
-
- regulator_bulk_free(icl->num_regulators, icl->regulators);
-
- return 0;
-}
-
-static int default_cropcap(struct soc_camera_device *icd,
- struct v4l2_cropcap *a)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, cropcap, a);
-}
-
-static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, g_crop, a);
-}
-
-static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, s_crop, a);
-}
-
-static int default_g_parm(struct soc_camera_device *icd,
- struct v4l2_streamparm *parm)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, g_parm, parm);
-}
-
-static int default_s_parm(struct soc_camera_device *icd,
- struct v4l2_streamparm *parm)
-{
- struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return v4l2_subdev_call(sd, video, s_parm, parm);
-}
-
-static int default_enum_framesizes(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_framesizes, &fsize_mbus);
- if (ret < 0)
- return ret;
-
- *fsize = fsize_mbus;
- fsize->pixel_format = pixfmt;
-
- return 0;
-}
-
-int soc_camera_host_register(struct soc_camera_host *ici)
-{
- struct soc_camera_host *ix;
- int ret;
-
- if (!ici || !ici->ops ||
- !ici->ops->try_fmt ||
- !ici->ops->set_fmt ||
- !ici->ops->set_bus_param ||
- !ici->ops->querycap ||
- ((!ici->ops->init_videobuf ||
- !ici->ops->reqbufs) &&
- !ici->ops->init_videobuf2) ||
- !ici->ops->add ||
- !ici->ops->remove ||
- !ici->ops->poll ||
- !ici->v4l2_dev.dev)
- return -EINVAL;
-
- if (!ici->ops->set_crop)
- ici->ops->set_crop = default_s_crop;
- if (!ici->ops->get_crop)
- ici->ops->get_crop = default_g_crop;
- if (!ici->ops->cropcap)
- ici->ops->cropcap = default_cropcap;
- if (!ici->ops->set_parm)
- ici->ops->set_parm = default_s_parm;
- if (!ici->ops->get_parm)
- ici->ops->get_parm = default_g_parm;
- if (!ici->ops->enum_framesizes)
- ici->ops->enum_framesizes = default_enum_framesizes;
-
- mutex_lock(&list_lock);
- list_for_each_entry(ix, &hosts, list) {
- if (ix->nr == ici->nr) {
- ret = -EBUSY;
- goto edevreg;
- }
- }
-
- ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
- if (ret < 0)
- goto edevreg;
-
- list_add_tail(&ici->list, &hosts);
- mutex_unlock(&list_lock);
-
- mutex_init(&ici->host_lock);
- scan_add_host(ici);
-
- return 0;
-
-edevreg:
- mutex_unlock(&list_lock);
- return ret;
-}
-EXPORT_SYMBOL(soc_camera_host_register);
-
-/* Unregister all clients! */
-void soc_camera_host_unregister(struct soc_camera_host *ici)
-{
- struct soc_camera_device *icd;
-
- mutex_lock(&list_lock);
-
- list_del(&ici->list);
- list_for_each_entry(icd, &devices, list)
- if (icd->iface == ici->nr && to_soc_camera_control(icd))
- soc_camera_remove(icd);
-
- mutex_unlock(&list_lock);
-
- v4l2_device_unregister(&ici->v4l2_dev);
-}
-EXPORT_SYMBOL(soc_camera_host_unregister);
-
-/* Image capture device */
-static int soc_camera_device_register(struct soc_camera_device *icd)
-{
- struct soc_camera_device *ix;
- int num = -1, i;
-
- for (i = 0; i < 256 && num < 0; i++) {
- num = i;
- /* Check if this index is available on this interface */
- list_for_each_entry(ix, &devices, list) {
- if (ix->iface == icd->iface && ix->devnum == i) {
- num = -1;
- break;
- }
- }
- }
-
- if (num < 0)
- /*
- * ok, we have 256 cameras on this host...
- * man, stay reasonable...
- */
- return -ENOMEM;
-
- icd->devnum = num;
- icd->use_count = 0;
- icd->host_priv = NULL;
- mutex_init(&icd->video_lock);
-
- list_add_tail(&icd->list, &devices);
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
- .vidioc_querycap = soc_camera_querycap,
- .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
- .vidioc_enum_input = soc_camera_enum_input,
- .vidioc_g_input = soc_camera_g_input,
- .vidioc_s_input = soc_camera_s_input,
- .vidioc_s_std = soc_camera_s_std,
- .vidioc_g_std = soc_camera_g_std,
- .vidioc_enum_framesizes = soc_camera_enum_framesizes,
- .vidioc_reqbufs = soc_camera_reqbufs,
- .vidioc_querybuf = soc_camera_querybuf,
- .vidioc_qbuf = soc_camera_qbuf,
- .vidioc_dqbuf = soc_camera_dqbuf,
- .vidioc_create_bufs = soc_camera_create_bufs,
- .vidioc_prepare_buf = soc_camera_prepare_buf,
- .vidioc_streamon = soc_camera_streamon,
- .vidioc_streamoff = soc_camera_streamoff,
- .vidioc_cropcap = soc_camera_cropcap,
- .vidioc_g_crop = soc_camera_g_crop,
- .vidioc_s_crop = soc_camera_s_crop,
- .vidioc_g_parm = soc_camera_g_parm,
- .vidioc_s_parm = soc_camera_s_parm,
- .vidioc_g_chip_ident = soc_camera_g_chip_ident,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = soc_camera_g_register,
- .vidioc_s_register = soc_camera_s_register,
-#endif
-};
-
-static int video_dev_create(struct soc_camera_device *icd)
-{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- struct video_device *vdev = video_device_alloc();
-
- if (!vdev)
- return -ENOMEM;
-
- strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
-
- vdev->parent = icd->pdev;
- vdev->current_norm = V4L2_STD_UNKNOWN;
- vdev->fops = &soc_camera_fops;
- vdev->ioctl_ops = &soc_camera_ioctl_ops;
- vdev->release = video_device_release;
- vdev->tvnorms = V4L2_STD_UNKNOWN;
- vdev->ctrl_handler = &icd->ctrl_handler;
- vdev->lock = &icd->video_lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
-
- icd->vdev = vdev;
-
- return 0;
-}
-
-/*
- * Called from soc_camera_probe() above (with .video_lock held???)
- */
-static int soc_camera_video_start(struct soc_camera_device *icd)
-{
- const struct device_type *type = icd->vdev->dev.type;
- int ret;
-
- if (!icd->parent)
- return -ENODEV;
-
- ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- dev_err(icd->pdev, "video_register_device failed: %d\n", ret);
- return ret;
- }
-
- /* Restore device type, possibly set by the subdevice driver */
- icd->vdev->dev.type = type;
-
- return 0;
-}
-
-static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
-{
- struct soc_camera_link *icl = pdev->dev.platform_data;
- struct soc_camera_device *icd;
- int ret;
-
- if (!icl)
- return -EINVAL;
-
- icd = kzalloc(sizeof(*icd), GFP_KERNEL);
- if (!icd)
- return -ENOMEM;
-
- icd->iface = icl->bus_id;
- icd->link = icl;
- icd->pdev = &pdev->dev;
- platform_set_drvdata(pdev, icd);
-
- ret = soc_camera_device_register(icd);
- if (ret < 0)
- goto escdevreg;
-
- icd->user_width = DEFAULT_WIDTH;
- icd->user_height = DEFAULT_HEIGHT;
-
- return 0;
-
-escdevreg:
- kfree(icd);
-
- return ret;
-}
-
-/*
- * Only called on rmmod for each platform device, since they are not
- * hot-pluggable. Now we know, that all our users - hosts and devices have
- * been unloaded already
- */
-static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
-{
- struct soc_camera_device *icd = platform_get_drvdata(pdev);
-
- if (!icd)
- return -EINVAL;
-
- list_del(&icd->list);
-
- kfree(icd);
-
- return 0;
-}
-
-static struct platform_driver __refdata soc_camera_pdrv = {
- .probe = soc_camera_pdrv_probe,
- .remove = __devexit_p(soc_camera_pdrv_remove),
- .driver = {
- .name = "soc-camera-pdrv",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init soc_camera_init(void)
-{
- return platform_driver_register(&soc_camera_pdrv);
-}
-
-static void __exit soc_camera_exit(void)
-{
- platform_driver_unregister(&soc_camera_pdrv);
-}
-
-module_init(soc_camera_init);
-module_exit(soc_camera_exit);
-
-MODULE_DESCRIPTION("Image capture bus driver");
-MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:soc-camera-pdrv");
diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
deleted file mode 100644
index f59ccade07c8..000000000000
--- a/drivers/media/video/soc_camera_platform.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Generic Platform Camera Driver
- *
- * Copyright (C) 2008 Magnus Damm
- * Based on mt9m001 driver,
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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/slab.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-subdev.h>
-#include <media/soc_camera.h>
-#include <media/soc_camera_platform.h>
-
-struct soc_camera_platform_priv {
- struct v4l2_subdev subdev;
-};
-
-static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
-{
- struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
- return container_of(subdev, struct soc_camera_platform_priv, subdev);
-}
-
-static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
- return p->set_capture(p, enable);
-}
-
-static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- mf->width = p->format.width;
- mf->height = p->format.height;
- mf->code = p->format.code;
- mf->colorspace = p->format.colorspace;
- mf->field = p->format.field;
-
- return 0;
-}
-
-static struct v4l2_subdev_core_ops platform_subdev_core_ops;
-
-static int soc_camera_platform_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- if (index)
- return -EINVAL;
-
- *code = p->format.code;
- return 0;
-}
-
-static int soc_camera_platform_g_crop(struct v4l2_subdev *sd,
- struct v4l2_crop *a)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- a->c.left = 0;
- a->c.top = 0;
- a->c.width = p->format.width;
- a->c.height = p->format.height;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int soc_camera_platform_cropcap(struct v4l2_subdev *sd,
- struct v4l2_cropcap *a)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = p->format.width;
- a->bounds.height = p->format.height;
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
-
- cfg->flags = p->mbus_param;
- cfg->type = p->mbus_type;
-
- return 0;
-}
-
-static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
- .s_stream = soc_camera_platform_s_stream,
- .enum_mbus_fmt = soc_camera_platform_enum_fmt,
- .cropcap = soc_camera_platform_cropcap,
- .g_crop = soc_camera_platform_g_crop,
- .try_mbus_fmt = soc_camera_platform_fill_fmt,
- .g_mbus_fmt = soc_camera_platform_fill_fmt,
- .s_mbus_fmt = soc_camera_platform_fill_fmt,
- .g_mbus_config = soc_camera_platform_g_mbus_config,
-};
-
-static struct v4l2_subdev_ops platform_subdev_ops = {
- .core = &platform_subdev_core_ops,
- .video = &platform_subdev_video_ops,
-};
-
-static int soc_camera_platform_probe(struct platform_device *pdev)
-{
- struct soc_camera_host *ici;
- struct soc_camera_platform_priv *priv;
- struct soc_camera_platform_info *p = pdev->dev.platform_data;
- struct soc_camera_device *icd;
- int ret;
-
- if (!p)
- return -EINVAL;
-
- if (!p->icd) {
- dev_err(&pdev->dev,
- "Platform has not set soc_camera_device pointer!\n");
- return -EINVAL;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- icd = p->icd;
-
- /* soc-camera convention: control's drvdata points to the subdev */
- platform_set_drvdata(pdev, &priv->subdev);
- /* Set the control device reference */
- icd->control = &pdev->dev;
-
- ici = to_soc_camera_host(icd->parent);
-
- v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
- v4l2_set_subdevdata(&priv->subdev, p);
- strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
-
- ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
- if (ret)
- goto evdrs;
-
- return ret;
-
-evdrs:
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
- return ret;
-}
-
-static int soc_camera_platform_remove(struct platform_device *pdev)
-{
- struct soc_camera_platform_priv *priv = get_priv(pdev);
- struct soc_camera_platform_info *p = v4l2_get_subdevdata(&priv->subdev);
-
- p->icd->control = NULL;
- v4l2_device_unregister_subdev(&priv->subdev);
- platform_set_drvdata(pdev, NULL);
- kfree(priv);
- return 0;
-}
-
-static struct platform_driver soc_camera_platform_driver = {
- .driver = {
- .name = "soc_camera_platform",
- .owner = THIS_MODULE,
- },
- .probe = soc_camera_platform_probe,
- .remove = soc_camera_platform_remove,
-};
-
-module_platform_driver(soc_camera_platform_driver);
-
-MODULE_DESCRIPTION("SoC Camera Platform driver");
-MODULE_AUTHOR("Magnus Damm");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:soc_camera_platform");
diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c
deleted file mode 100644
index 462caa44ae00..000000000000
--- a/drivers/media/video/tcm825x.c
+++ /dev/null
@@ -1,937 +0,0 @@
-/*
- * drivers/media/video/tcm825x.c
- *
- * TCM825X camera sensor driver.
- *
- * Copyright (C) 2007 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@nokia.com>
- *
- * Based on code from David Cohen <david.cohen@indt.org.br>
- *
- * This driver was based on ov9640 sensor driver from MontaVista
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <media/v4l2-int-device.h>
-
-#include "tcm825x.h"
-
-/*
- * The sensor has two fps modes: the lower one just gives half the fps
- * at the same xclk than the high one.
- */
-#define MAX_FPS 30
-#define MIN_FPS 8
-#define MAX_HALF_FPS (MAX_FPS / 2)
-#define HIGH_FPS_MODE_LOWER_LIMIT 14
-#define DEFAULT_FPS MAX_HALF_FPS
-
-struct tcm825x_sensor {
- const struct tcm825x_platform_data *platform_data;
- struct v4l2_int_device *v4l2_int_device;
- struct i2c_client *i2c_client;
- struct v4l2_pix_format pix;
- struct v4l2_fract timeperframe;
-};
-
-/* list of image formats supported by TCM825X sensor */
-static const struct v4l2_fmtdesc tcm825x_formats[] = {
- {
- .description = "YUYV (YUV 4:2:2), packed",
- .pixelformat = V4L2_PIX_FMT_UYVY,
- }, {
- /* Note: V4L2 defines RGB565 as:
- *
- * Byte 0 Byte 1
- * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3
- *
- * We interpret RGB565 as:
- *
- * Byte 0 Byte 1
- * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3
- */
- .description = "RGB565, le",
- .pixelformat = V4L2_PIX_FMT_RGB565,
- },
-};
-
-#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats)
-
-/*
- * TCM825X register configuration for all combinations of pixel format and
- * image size
- */
-static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ };
-static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ };
-static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ };
-static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ };
-static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ };
-static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ };
-
-static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT };
-static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT };
-
-/* Our own specific controls */
-#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE
-#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1
-#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2
-#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3
-#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4
-#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME
-
-/* Video controls */
-static struct vcontrol {
- struct v4l2_queryctrl qc;
- u16 reg;
- u16 start_bit;
-} video_control[] = {
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- },
- .reg = TCM825X_AG,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- },
- .reg = TCM825X_MRG,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- },
- .reg = TCM825X_MBG,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 0,
- },
- .reg = TCM825X_AWBSW,
- .start_bit = 7,
- },
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure Time",
- .minimum = 0,
- .maximum = 0x1fff,
- .step = 1,
- },
- .reg = TCM825X_ESRSPD_U,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror Image",
- .minimum = 0,
- .maximum = 1,
- .step = 0,
- },
- .reg = TCM825X_H_INV,
- .start_bit = 6,
- },
- {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vertical Flip",
- .minimum = 0,
- .maximum = 1,
- .step = 0,
- },
- .reg = TCM825X_V_INV,
- .start_bit = 7,
- },
- /* Private controls */
- {
- {
- .id = V4L2_CID_ALC,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Luminance Control",
- .minimum = 0,
- .maximum = 1,
- .step = 0,
- },
- .reg = TCM825X_ALCSW,
- .start_bit = 7,
- },
- {
- {
- .id = V4L2_CID_H_EDGE_EN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Horizontal Edge Enhancement",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- },
- .reg = TCM825X_HDTG,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_V_EDGE_EN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Vertical Edge Enhancement",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- },
- .reg = TCM825X_VDTG,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_LENS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Lens Shading Compensation",
- .minimum = 0,
- .maximum = 0x3f,
- .step = 1,
- },
- .reg = TCM825X_LENS,
- .start_bit = 0,
- },
- {
- {
- .id = V4L2_CID_MAX_EXPOSURE_TIME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Maximum Exposure Time",
- .minimum = 0,
- .maximum = 0x3,
- .step = 1,
- },
- .reg = TCM825X_ESRLIM,
- .start_bit = 5,
- },
-};
-
-
-static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] =
-{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga };
-
-static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] =
-{ &yuv422, &rgb565 };
-
-/*
- * Read a value from a register in an TCM825X sensor device. The value is
- * returned in 'val'.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tcm825x_read_reg(struct i2c_client *client, int reg)
-{
- int err;
- struct i2c_msg msg[2];
- u8 reg_buf, data_buf = 0;
-
- if (!client->adapter)
- return -ENODEV;
-
- msg[0].addr = client->addr;
- msg[0].flags = 0;
- msg[0].len = 1;
- msg[0].buf = &reg_buf;
- msg[1].addr = client->addr;
- msg[1].flags = I2C_M_RD;
- msg[1].len = 1;
- msg[1].buf = &data_buf;
-
- reg_buf = reg;
-
- err = i2c_transfer(client->adapter, msg, 2);
- if (err < 0)
- return err;
- return data_buf;
-}
-
-/*
- * Write a value to a register in an TCM825X sensor device.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val)
-{
- int err;
- struct i2c_msg msg[1];
- unsigned char data[2];
-
- if (!client->adapter)
- return -ENODEV;
-
- msg->addr = client->addr;
- msg->flags = 0;
- msg->len = 2;
- msg->buf = data;
- data[0] = reg;
- data[1] = val;
- err = i2c_transfer(client->adapter, msg, 1);
- if (err >= 0)
- return 0;
- return err;
-}
-
-static int __tcm825x_write_reg_mask(struct i2c_client *client,
- u8 reg, u8 val, u8 mask)
-{
- int rc;
-
- /* need to do read - modify - write */
- rc = tcm825x_read_reg(client, reg);
- if (rc < 0)
- return rc;
-
- rc &= (~mask); /* Clear the masked bits */
- val &= mask; /* Enforce mask on value */
- val |= rc;
-
- /* write the new value to the register */
- rc = tcm825x_write_reg(client, reg, val);
- if (rc)
- return rc;
-
- return 0;
-}
-
-#define tcm825x_write_reg_mask(client, regmask, val) \
- __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \
- TCM825X_MASK((regmask)))
-
-
-/*
- * Initialize a list of TCM825X registers.
- * The list of registers is terminated by the pair of values
- * { TCM825X_REG_TERM, TCM825X_VAL_TERM }.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tcm825x_write_default_regs(struct i2c_client *client,
- const struct tcm825x_reg *reglist)
-{
- int err;
- const struct tcm825x_reg *next = reglist;
-
- while (!((next->reg == TCM825X_REG_TERM)
- && (next->val == TCM825X_VAL_TERM))) {
- err = tcm825x_write_reg(client, next->reg, next->val);
- if (err) {
- dev_err(&client->dev, "register writing failed\n");
- return err;
- }
- next++;
- }
-
- return 0;
-}
-
-static struct vcontrol *find_vctrl(int id)
-{
- int i;
-
- if (id < V4L2_CID_BASE)
- return NULL;
-
- for (i = 0; i < ARRAY_SIZE(video_control); i++)
- if (video_control[i].qc.id == id)
- return &video_control[i];
-
- return NULL;
-}
-
-/*
- * Find the best match for a requested image capture size. The best match
- * is chosen as the nearest match that has the same number or fewer pixels
- * as the requested size, or the smallest image size if the requested size
- * has fewer pixels than the smallest image.
- */
-static enum image_size tcm825x_find_size(struct v4l2_int_device *s,
- unsigned int width,
- unsigned int height)
-{
- enum image_size isize;
- unsigned long pixels = width * height;
- struct tcm825x_sensor *sensor = s->priv;
-
- for (isize = subQCIF; isize < VGA; isize++) {
- if (tcm825x_sizes[isize + 1].height
- * tcm825x_sizes[isize + 1].width > pixels) {
- dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize);
-
- return isize;
- }
- }
-
- dev_dbg(&sensor->i2c_client->dev, "format default VGA\n");
-
- return VGA;
-}
-
-/*
- * Configure the TCM825X for current image size, pixel format, and
- * frame period. fper is the frame period (in seconds) expressed as a
- * fraction. Returns zero if successful, or non-zero otherwise. The
- * actual frame period is returned in fper.
- */
-static int tcm825x_configure(struct v4l2_int_device *s)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct v4l2_pix_format *pix = &sensor->pix;
- enum image_size isize = tcm825x_find_size(s, pix->width, pix->height);
- struct v4l2_fract *fper = &sensor->timeperframe;
- enum pixel_format pfmt;
- int err;
- u32 tgt_fps;
- u8 val;
-
- /* common register initialization */
- err = tcm825x_write_default_regs(
- sensor->i2c_client, sensor->platform_data->default_regs());
- if (err)
- return err;
-
- /* configure image size */
- val = tcm825x_siz_reg[isize]->val;
- dev_dbg(&sensor->i2c_client->dev,
- "configuring image size %d\n", isize);
- err = tcm825x_write_reg_mask(sensor->i2c_client,
- tcm825x_siz_reg[isize]->reg, val);
- if (err)
- return err;
-
- /* configure pixel format */
- switch (pix->pixelformat) {
- default:
- case V4L2_PIX_FMT_RGB565:
- pfmt = RGB565;
- break;
- case V4L2_PIX_FMT_UYVY:
- pfmt = YUV422;
- break;
- }
-
- dev_dbg(&sensor->i2c_client->dev,
- "configuring pixel format %d\n", pfmt);
- val = tcm825x_fmt_reg[pfmt]->val;
-
- err = tcm825x_write_reg_mask(sensor->i2c_client,
- tcm825x_fmt_reg[pfmt]->reg, val);
- if (err)
- return err;
-
- /*
- * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be
- * set. Frame rate will be halved from the normal.
- */
- tgt_fps = fper->denominator / fper->numerator;
- if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) {
- val = tcm825x_read_reg(sensor->i2c_client, 0x02);
- val |= 0x80;
- tcm825x_write_reg(sensor->i2c_client, 0x02, val);
- }
-
- return 0;
-}
-
-static int ioctl_queryctrl(struct v4l2_int_device *s,
- struct v4l2_queryctrl *qc)
-{
- struct vcontrol *control;
-
- control = find_vctrl(qc->id);
-
- if (control == NULL)
- return -EINVAL;
-
- *qc = control->qc;
-
- return 0;
-}
-
-static int ioctl_g_ctrl(struct v4l2_int_device *s,
- struct v4l2_control *vc)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct i2c_client *client = sensor->i2c_client;
- int val, r;
- struct vcontrol *lvc;
-
- /* exposure time is special, spread across 2 registers */
- if (vc->id == V4L2_CID_EXPOSURE) {
- int val_lower, val_upper;
-
- val_upper = tcm825x_read_reg(client,
- TCM825X_ADDR(TCM825X_ESRSPD_U));
- if (val_upper < 0)
- return val_upper;
- val_lower = tcm825x_read_reg(client,
- TCM825X_ADDR(TCM825X_ESRSPD_L));
- if (val_lower < 0)
- return val_lower;
-
- vc->value = ((val_upper & 0x1f) << 8) | (val_lower);
- return 0;
- }
-
- lvc = find_vctrl(vc->id);
- if (lvc == NULL)
- return -EINVAL;
-
- r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg));
- if (r < 0)
- return r;
- val = r & TCM825X_MASK(lvc->reg);
- val >>= lvc->start_bit;
-
- if (val < 0)
- return val;
-
- if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
- val ^= sensor->platform_data->is_upside_down();
-
- vc->value = val;
- return 0;
-}
-
-static int ioctl_s_ctrl(struct v4l2_int_device *s,
- struct v4l2_control *vc)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct i2c_client *client = sensor->i2c_client;
- struct vcontrol *lvc;
- int val = vc->value;
-
- /* exposure time is special, spread across 2 registers */
- if (vc->id == V4L2_CID_EXPOSURE) {
- int val_lower, val_upper;
- val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L);
- val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U);
-
- if (tcm825x_write_reg_mask(client,
- TCM825X_ESRSPD_U, val_upper))
- return -EIO;
-
- if (tcm825x_write_reg_mask(client,
- TCM825X_ESRSPD_L, val_lower))
- return -EIO;
-
- return 0;
- }
-
- lvc = find_vctrl(vc->id);
- if (lvc == NULL)
- return -EINVAL;
-
- if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
- val ^= sensor->platform_data->is_upside_down();
-
- val = val << lvc->start_bit;
- if (tcm825x_write_reg_mask(client, lvc->reg, val))
- return -EIO;
-
- return 0;
-}
-
-static int ioctl_enum_fmt_cap(struct v4l2_int_device *s,
- struct v4l2_fmtdesc *fmt)
-{
- int index = fmt->index;
-
- switch (fmt->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (index >= TCM825X_NUM_CAPTURE_FORMATS)
- return -EINVAL;
- break;
-
- default:
- return -EINVAL;
- }
-
- fmt->flags = tcm825x_formats[index].flags;
- strlcpy(fmt->description, tcm825x_formats[index].description,
- sizeof(fmt->description));
- fmt->pixelformat = tcm825x_formats[index].pixelformat;
-
- return 0;
-}
-
-static int ioctl_try_fmt_cap(struct v4l2_int_device *s,
- struct v4l2_format *f)
-{
- struct tcm825x_sensor *sensor = s->priv;
- enum image_size isize;
- int ifmt;
- struct v4l2_pix_format *pix = &f->fmt.pix;
-
- isize = tcm825x_find_size(s, pix->width, pix->height);
- dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n",
- isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS);
-
- pix->width = tcm825x_sizes[isize].width;
- pix->height = tcm825x_sizes[isize].height;
-
- for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++)
- if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat)
- break;
-
- if (ifmt == TCM825X_NUM_CAPTURE_FORMATS)
- ifmt = 0; /* Default = YUV 4:2:2 */
-
- pix->pixelformat = tcm825x_formats[ifmt].pixelformat;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL;
- pix->sizeimage = pix->bytesperline * pix->height;
- pix->priv = 0;
- dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n",
- pix->pixelformat);
-
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_UYVY:
- default:
- pix->colorspace = V4L2_COLORSPACE_JPEG;
- break;
- case V4L2_PIX_FMT_RGB565:
- pix->colorspace = V4L2_COLORSPACE_SRGB;
- break;
- }
-
- return 0;
-}
-
-static int ioctl_s_fmt_cap(struct v4l2_int_device *s,
- struct v4l2_format *f)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct v4l2_pix_format *pix = &f->fmt.pix;
- int rval;
-
- rval = ioctl_try_fmt_cap(s, f);
- if (rval)
- return rval;
-
- rval = tcm825x_configure(s);
-
- sensor->pix = *pix;
-
- return rval;
-}
-
-static int ioctl_g_fmt_cap(struct v4l2_int_device *s,
- struct v4l2_format *f)
-{
- struct tcm825x_sensor *sensor = s->priv;
-
- f->fmt.pix = sensor->pix;
-
- return 0;
-}
-
-static int ioctl_g_parm(struct v4l2_int_device *s,
- struct v4l2_streamparm *a)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct v4l2_captureparm *cparm = &a->parm.capture;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memset(a, 0, sizeof(*a));
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- cparm->capability = V4L2_CAP_TIMEPERFRAME;
- cparm->timeperframe = sensor->timeperframe;
-
- return 0;
-}
-
-static int ioctl_s_parm(struct v4l2_int_device *s,
- struct v4l2_streamparm *a)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe;
- u32 tgt_fps; /* target frames per secound */
- int rval;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if ((timeperframe->numerator == 0)
- || (timeperframe->denominator == 0)) {
- timeperframe->denominator = DEFAULT_FPS;
- timeperframe->numerator = 1;
- }
-
- tgt_fps = timeperframe->denominator / timeperframe->numerator;
-
- if (tgt_fps > MAX_FPS) {
- timeperframe->denominator = MAX_FPS;
- timeperframe->numerator = 1;
- } else if (tgt_fps < MIN_FPS) {
- timeperframe->denominator = MIN_FPS;
- timeperframe->numerator = 1;
- }
-
- sensor->timeperframe = *timeperframe;
-
- rval = tcm825x_configure(s);
-
- return rval;
-}
-
-static int ioctl_s_power(struct v4l2_int_device *s, int on)
-{
- struct tcm825x_sensor *sensor = s->priv;
-
- return sensor->platform_data->power_set(on);
-}
-
-/*
- * Given the image capture format in pix, the nominal frame period in
- * timeperframe, calculate the required xclk frequency.
- *
- * TCM825X input frequency characteristics are:
- * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz
- */
-
-static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
-{
- struct tcm825x_sensor *sensor = s->priv;
- struct v4l2_fract *timeperframe = &sensor->timeperframe;
- u32 tgt_xclk; /* target xclk */
- u32 tgt_fps; /* target frames per secound */
- int rval;
-
- rval = sensor->platform_data->ifparm(p);
- if (rval)
- return rval;
-
- tgt_fps = timeperframe->denominator / timeperframe->numerator;
-
- tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ?
- (2457 * tgt_fps) / MAX_HALF_FPS :
- (2457 * tgt_fps) / MAX_FPS;
- tgt_xclk *= 10000;
-
- tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX);
- tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN);
-
- p->u.bt656.clock_curr = tgt_xclk;
-
- return 0;
-}
-
-static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf)
-{
- struct tcm825x_sensor *sensor = s->priv;
-
- return sensor->platform_data->needs_reset(s, buf, &sensor->pix);
-}
-
-static int ioctl_reset(struct v4l2_int_device *s)
-{
- return -EBUSY;
-}
-
-static int ioctl_init(struct v4l2_int_device *s)
-{
- return tcm825x_configure(s);
-}
-
-static int ioctl_dev_exit(struct v4l2_int_device *s)
-{
- return 0;
-}
-
-static int ioctl_dev_init(struct v4l2_int_device *s)
-{
- struct tcm825x_sensor *sensor = s->priv;
- int r;
-
- r = tcm825x_read_reg(sensor->i2c_client, 0x01);
- if (r < 0)
- return r;
- if (r == 0) {
- dev_err(&sensor->i2c_client->dev, "device not detected\n");
- return -EIO;
- }
- return 0;
-}
-
-static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = {
- { vidioc_int_dev_init_num,
- (v4l2_int_ioctl_func *)ioctl_dev_init },
- { vidioc_int_dev_exit_num,
- (v4l2_int_ioctl_func *)ioctl_dev_exit },
- { vidioc_int_s_power_num,
- (v4l2_int_ioctl_func *)ioctl_s_power },
- { vidioc_int_g_ifparm_num,
- (v4l2_int_ioctl_func *)ioctl_g_ifparm },
- { vidioc_int_g_needs_reset_num,
- (v4l2_int_ioctl_func *)ioctl_g_needs_reset },
- { vidioc_int_reset_num,
- (v4l2_int_ioctl_func *)ioctl_reset },
- { vidioc_int_init_num,
- (v4l2_int_ioctl_func *)ioctl_init },
- { vidioc_int_enum_fmt_cap_num,
- (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap },
- { vidioc_int_try_fmt_cap_num,
- (v4l2_int_ioctl_func *)ioctl_try_fmt_cap },
- { vidioc_int_g_fmt_cap_num,
- (v4l2_int_ioctl_func *)ioctl_g_fmt_cap },
- { vidioc_int_s_fmt_cap_num,
- (v4l2_int_ioctl_func *)ioctl_s_fmt_cap },
- { vidioc_int_g_parm_num,
- (v4l2_int_ioctl_func *)ioctl_g_parm },
- { vidioc_int_s_parm_num,
- (v4l2_int_ioctl_func *)ioctl_s_parm },
- { vidioc_int_queryctrl_num,
- (v4l2_int_ioctl_func *)ioctl_queryctrl },
- { vidioc_int_g_ctrl_num,
- (v4l2_int_ioctl_func *)ioctl_g_ctrl },
- { vidioc_int_s_ctrl_num,
- (v4l2_int_ioctl_func *)ioctl_s_ctrl },
-};
-
-static struct v4l2_int_slave tcm825x_slave = {
- .ioctls = tcm825x_ioctl_desc,
- .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc),
-};
-
-static struct tcm825x_sensor tcm825x;
-
-static struct v4l2_int_device tcm825x_int_device = {
- .module = THIS_MODULE,
- .name = TCM825X_NAME,
- .priv = &tcm825x,
- .type = v4l2_int_type_slave,
- .u = {
- .slave = &tcm825x_slave,
- },
-};
-
-static int tcm825x_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-{
- struct tcm825x_sensor *sensor = &tcm825x;
-
- if (i2c_get_clientdata(client))
- return -EBUSY;
-
- sensor->platform_data = client->dev.platform_data;
-
- if (sensor->platform_data == NULL
- || !sensor->platform_data->is_okay())
- return -ENODEV;
-
- sensor->v4l2_int_device = &tcm825x_int_device;
-
- sensor->i2c_client = client;
- i2c_set_clientdata(client, sensor);
-
- /* Make the default capture format QVGA RGB565 */
- sensor->pix.width = tcm825x_sizes[QVGA].width;
- sensor->pix.height = tcm825x_sizes[QVGA].height;
- sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565;
-
- return v4l2_int_device_register(sensor->v4l2_int_device);
-}
-
-static int tcm825x_remove(struct i2c_client *client)
-{
- struct tcm825x_sensor *sensor = i2c_get_clientdata(client);
-
- if (!client->adapter)
- return -ENODEV; /* our client isn't attached */
-
- v4l2_int_device_unregister(sensor->v4l2_int_device);
-
- return 0;
-}
-
-static const struct i2c_device_id tcm825x_id[] = {
- { "tcm825x", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tcm825x_id);
-
-static struct i2c_driver tcm825x_i2c_driver = {
- .driver = {
- .name = TCM825X_NAME,
- },
- .probe = tcm825x_probe,
- .remove = tcm825x_remove,
- .id_table = tcm825x_id,
-};
-
-static struct tcm825x_sensor tcm825x = {
- .timeperframe = {
- .numerator = 1,
- .denominator = DEFAULT_FPS,
- },
-};
-
-static int __init tcm825x_init(void)
-{
- int rval;
-
- rval = i2c_add_driver(&tcm825x_i2c_driver);
- if (rval)
- printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n",
- __func__);
-
- return rval;
-}
-
-static void __exit tcm825x_exit(void)
-{
- i2c_del_driver(&tcm825x_i2c_driver);
-}
-
-/*
- * FIXME: Menelaus isn't ready (?) at module_init stage, so use
- * late_initcall for now.
- */
-late_initcall(tcm825x_init);
-module_exit(tcm825x_exit);
-
-MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
-MODULE_DESCRIPTION("TCM825x camera sensor driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h
deleted file mode 100644
index 5b7e69682368..000000000000
--- a/drivers/media/video/tcm825x.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * drivers/media/video/tcm825x.h
- *
- * Register definitions for the TCM825X CameraChip.
- *
- * Author: David Cohen (david.cohen@indt.org.br)
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- *
- * This file was based on ov9640.h from MontaVista
- */
-
-#ifndef TCM825X_H
-#define TCM825X_H
-
-#include <linux/videodev2.h>
-
-#include <media/v4l2-int-device.h>
-
-#define TCM825X_NAME "tcm825x"
-
-#define TCM825X_MASK(x) x & 0x00ff
-#define TCM825X_ADDR(x) (x & 0xff00) >> 8
-
-/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */
-#define TCM825X_I2C_ADDR 0x3d
-
-/*
- * define register offsets for the TCM825X sensor chip
- * OFFSET(8 bits) + MASK(8 bits)
- * MASK bit 4 and 3 are used when the register uses more than one address
- */
-#define TCM825X_FPS 0x0280
-#define TCM825X_ACF 0x0240
-#define TCM825X_DOUTBUF 0x020C
-#define TCM825X_DCLKP 0x0202
-#define TCM825X_ACFDET 0x0201
-#define TCM825X_DOUTSW 0x0380
-#define TCM825X_DATAHZ 0x0340
-#define TCM825X_PICSIZ 0x033c
-#define TCM825X_PICFMT 0x0302
-#define TCM825X_V_INV 0x0480
-#define TCM825X_H_INV 0x0440
-#define TCM825X_ESRLSW 0x0430
-#define TCM825X_V_LENGTH 0x040F
-#define TCM825X_ALCSW 0x0580
-#define TCM825X_ESRLIM 0x0560
-#define TCM825X_ESRSPD_U 0x051F
-#define TCM825X_ESRSPD_L 0x06FF
-#define TCM825X_AG 0x07FF
-#define TCM825X_ESRSPD2 0x06FF
-#define TCM825X_ALCMODE 0x0830
-#define TCM825X_ALCH 0x080F
-#define TCM825X_ALCL 0x09FF
-#define TCM825X_AWBSW 0x0A80
-#define TCM825X_MRG 0x0BFF
-#define TCM825X_MBG 0x0CFF
-#define TCM825X_GAMSW 0x0D80
-#define TCM825X_HDTG 0x0EFF
-#define TCM825X_VDTG 0x0FFF
-#define TCM825X_HDTCORE 0x10F0
-#define TCM825X_VDTCORE 0x100F
-#define TCM825X_CONT 0x11FF
-#define TCM825X_BRIGHT 0x12FF
-#define TCM825X_VHUE 0x137F
-#define TCM825X_UHUE 0x147F
-#define TCM825X_VGAIN 0x153F
-#define TCM825X_UGAIN 0x163F
-#define TCM825X_UVCORE 0x170F
-#define TCM825X_SATU 0x187F
-#define TCM825X_MHMODE 0x1980
-#define TCM825X_MHLPFSEL 0x1940
-#define TCM825X_YMODE 0x1930
-#define TCM825X_MIXHG 0x1907
-#define TCM825X_LENS 0x1A3F
-#define TCM825X_AGLIM 0x1BE0
-#define TCM825X_LENSRPOL 0x1B10
-#define TCM825X_LENSRGAIN 0x1B0F
-#define TCM825X_ES100S 0x1CFF
-#define TCM825X_ES120S 0x1DFF
-#define TCM825X_DMASK 0x1EC0
-#define TCM825X_CODESW 0x1E20
-#define TCM825X_CODESEL 0x1E10
-#define TCM825X_TESPIC 0x1E04
-#define TCM825X_PICSEL 0x1E03
-#define TCM825X_HNUM 0x20FF
-#define TCM825X_VOUTPH 0x287F
-#define TCM825X_ESROUT 0x327F
-#define TCM825X_ESROUT2 0x33FF
-#define TCM825X_AGOUT 0x34FF
-#define TCM825X_DGOUT 0x353F
-#define TCM825X_AGSLOW1 0x39C0
-#define TCM825X_FLLSMODE 0x3930
-#define TCM825X_FLLSLIM 0x390F
-#define TCM825X_DETSEL 0x3AF0
-#define TCM825X_ACDETNC 0x3A0F
-#define TCM825X_AGSLOW2 0x3BC0
-#define TCM825X_DG 0x3B3F
-#define TCM825X_REJHLEV 0x3CFF
-#define TCM825X_ALCLOCK 0x3D80
-#define TCM825X_FPSLNKSW 0x3D40
-#define TCM825X_ALCSPD 0x3D30
-#define TCM825X_REJH 0x3D03
-#define TCM825X_SHESRSW 0x3E80
-#define TCM825X_ESLIMSEL 0x3E40
-#define TCM825X_SHESRSPD 0x3E30
-#define TCM825X_ELSTEP 0x3E0C
-#define TCM825X_ELSTART 0x3E03
-#define TCM825X_AGMIN 0x3FFF
-#define TCM825X_PREGRG 0x423F
-#define TCM825X_PREGBG 0x433F
-#define TCM825X_PRERG 0x443F
-#define TCM825X_PREBG 0x453F
-#define TCM825X_MSKBR 0x477F
-#define TCM825X_MSKGR 0x487F
-#define TCM825X_MSKRB 0x497F
-#define TCM825X_MSKGB 0x4A7F
-#define TCM825X_MSKRG 0x4B7F
-#define TCM825X_MSKBG 0x4C7F
-#define TCM825X_HDTCSW 0x4D80
-#define TCM825X_VDTCSW 0x4D40
-#define TCM825X_DTCYL 0x4D3F
-#define TCM825X_HDTPSW 0x4E80
-#define TCM825X_VDTPSW 0x4E40
-#define TCM825X_DTCGAIN 0x4E3F
-#define TCM825X_DTLLIMSW 0x4F10
-#define TCM825X_DTLYLIM 0x4F0F
-#define TCM825X_YLCUTLMSK 0x5080
-#define TCM825X_YLCUTL 0x503F
-#define TCM825X_YLCUTHMSK 0x5180
-#define TCM825X_YLCUTH 0x513F
-#define TCM825X_UVSKNC 0x527F
-#define TCM825X_UVLJ 0x537F
-#define TCM825X_WBGMIN 0x54FF
-#define TCM825X_WBGMAX 0x55FF
-#define TCM825X_WBSPDUP 0x5603
-#define TCM825X_ALLAREA 0x5820
-#define TCM825X_WBLOCK 0x5810
-#define TCM825X_WB2SP 0x580F
-#define TCM825X_KIZUSW 0x5920
-#define TCM825X_PBRSW 0x5910
-#define TCM825X_ABCSW 0x5903
-#define TCM825X_PBDLV 0x5AFF
-#define TCM825X_PBC1LV 0x5BFF
-
-#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1)
-
-#define TCM825X_BYTES_PER_PIXEL 2
-
-#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */
-#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */
-
-/* define a structure for tcm825x register initialization values */
-struct tcm825x_reg {
- u8 val;
- u16 reg;
-};
-
-enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA };
-enum pixel_format { YUV422 = 0, RGB565 };
-#define NUM_IMAGE_SIZES 6
-#define NUM_PIXEL_FORMATS 2
-
-#define TCM825X_XCLK_MIN 11900000
-#define TCM825X_XCLK_MAX 25000000
-
-struct capture_size {
- unsigned long width;
- unsigned long height;
-};
-
-struct tcm825x_platform_data {
- /* Is the sensor usable? Doesn't yet mean it's there, but you
- * can try! */
- int (*is_okay)(void);
- /* Set power state, zero is off, non-zero is on. */
- int (*power_set)(int power);
- /* Default registers written after power-on or reset. */
- const struct tcm825x_reg *(*default_regs)(void);
- int (*needs_reset)(struct v4l2_int_device *s, void *buf,
- struct v4l2_pix_format *fmt);
- int (*ifparm)(struct v4l2_ifparm *p);
- int (*is_upside_down)(void);
-};
-
-/* Array of image sizes supported by TCM825X. These must be ordered from
- * smallest image size to largest.
- */
-static const struct capture_size tcm825x_sizes[] = {
- { 128, 96 }, /* subQCIF */
- { 160, 120 }, /* QQVGA */
- { 176, 144 }, /* QCIF */
- { 320, 240 }, /* QVGA */
- { 352, 288 }, /* CIF */
- { 640, 480 }, /* VGA */
-};
-
-#endif /* ifndef TCM825X_H */
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
deleted file mode 100644
index d1d6ea1dd273..000000000000
--- a/drivers/media/video/tea6415c.c
+++ /dev/null
@@ -1,187 +0,0 @@
- /*
- tea6415c - i2c-driver for the tea6415c by SGS Thomson
-
- Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
- Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
-
- The tea6415c is a bus controlled video-matrix-switch
- with 8 inputs and 6 outputs.
- It is cascadable, i.e. it can be found at the addresses
- 0x86 and 0x06 on the i2c-bus.
-
- For detailed informations download the specifications directly
- from SGS Thomson at http://www.st.com
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License vs published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <linux/module.h>
-#include <linux/ioctl.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include "tea6415c.h"
-
-MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
-MODULE_DESCRIPTION("tea6415c driver");
-MODULE_LICENSE("GPL");
-
-static int debug;
-module_param(debug, int, 0644);
-
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-
-/* makes a connection between the input-pin 'i' and the output-pin 'o' */
-static int tea6415c_s_routing(struct v4l2_subdev *sd,
- u32 i, u32 o, u32 config)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- u8 byte = 0;
- int ret;
-
- v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
-
- /* check if the pins are valid */
- if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
- && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
- return -EINVAL;
-
- /* to understand this, have a look at the tea6415c-specs (p.5) */
- switch (o) {
- case 18:
- byte = 0x00;
- break;
- case 14:
- byte = 0x20;
- break;
- case 16:
- byte = 0x10;
- break;
- case 17:
- byte = 0x08;
- break;
- case 15:
- byte = 0x18;
- break;
- case 13:
- byte = 0x28;
- break;
- };
-
- switch (i) {
- case 5:
- byte |= 0x00;
- break;
- case 8:
- byte |= 0x04;
- break;
- case 3:
- byte |= 0x02;
- break;
- case 20:
- byte |= 0x06;
- break;
- case 6:
- byte |= 0x01;
- break;
- case 10:
- byte |= 0x05;
- break;
- case 1:
- byte |= 0x03;
- break;
- case 11:
- byte |= 0x07;
- break;
- };
-
- ret = i2c_smbus_write_byte(client, byte);
- if (ret) {
- v4l2_dbg(1, debug, sd,
- "i2c_smbus_write_byte() failed, ret:%d\n", ret);
- return -EIO;
- }
- return ret;
-}
-
-static int tea6415c_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_TEA6415C, 0);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
- .g_chip_ident = tea6415c_g_chip_ident,
-};
-
-static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
- .s_routing = tea6415c_s_routing,
-};
-
-static const struct v4l2_subdev_ops tea6415c_ops = {
- .core = &tea6415c_core_ops,
- .video = &tea6415c_video_ops,
-};
-
-static int tea6415c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct v4l2_subdev *sd;
-
- /* let's see whether this adapter can support what we need */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE))
- return -EIO;
-
- 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)
- return -ENOMEM;
- v4l2_i2c_subdev_init(sd, client, &tea6415c_ops);
- return 0;
-}
-
-static int tea6415c_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
-
- v4l2_device_unregister_subdev(sd);
- kfree(sd);
- return 0;
-}
-
-static const struct i2c_device_id tea6415c_id[] = {
- { "tea6415c", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tea6415c_id);
-
-static struct i2c_driver tea6415c_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "tea6415c",
- },
- .probe = tea6415c_probe,
- .remove = tea6415c_remove,
- .id_table = tea6415c_id,
-};
-
-module_i2c_driver(tea6415c_driver);
diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile
deleted file mode 100644
index ea09b9af2d30..000000000000
--- a/drivers/media/video/tlg2300/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o
-
-obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-
diff --git a/drivers/media/video/tlg2300/pd-alsa.c b/drivers/media/video/tlg2300/pd-alsa.c
deleted file mode 100644
index 9f8b7da56b67..000000000000
--- a/drivers/media/video/tlg2300/pd-alsa.c
+++ /dev/null
@@ -1,332 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/usb.h>
-#include <linux/init.h>
-#include <linux/sound.h>
-#include <linux/spinlock.h>
-#include <linux/soundcard.h>
-#include <linux/vmalloc.h>
-#include <linux/proc_fs.h>
-#include <linux/module.h>
-#include <linux/gfp.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <media/v4l2-common.h>
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static void complete_handler_audio(struct urb *urb);
-#define AUDIO_EP (0x83)
-#define AUDIO_BUF_SIZE (512)
-#define PERIOD_SIZE (1024 * 8)
-#define PERIOD_MIN (4)
-#define PERIOD_MAX PERIOD_MIN
-
-static struct snd_pcm_hardware snd_pd_hw_capture = {
- .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID,
-
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_48000,
-
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN,
- .period_bytes_min = PERIOD_SIZE,
- .period_bytes_max = PERIOD_SIZE,
- .periods_min = PERIOD_MIN,
- .periods_max = PERIOD_MAX,
- /*
- .buffer_bytes_max = 62720 * 8,
- .period_bytes_min = 64,
- .period_bytes_max = 12544,
- .periods_min = 2,
- .periods_max = 98
- */
-};
-
-static int snd_pd_capture_open(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
- struct snd_pcm_runtime *runtime = substream->runtime;
-
- if (!p)
- return -ENODEV;
- pa->users++;
- pa->card_close = 0;
- pa->capture_pcm_substream = substream;
- runtime->private_data = p;
-
- runtime->hw = snd_pd_hw_capture;
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
- usb_autopm_get_interface(p->interface);
- kref_get(&p->kref);
- return 0;
-}
-
-static int snd_pd_pcm_close(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
-
- pa->users--;
- pa->card_close = 1;
- usb_autopm_put_interface(p->interface);
- kref_put(&p->kref, poseidon_delete);
- return 0;
-}
-
-static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned int size;
-
- size = params_buffer_bytes(hw_params);
- if (runtime->dma_area) {
- if (runtime->dma_bytes > size)
- return 0;
- vfree(runtime->dma_area);
- }
- runtime->dma_area = vmalloc(size);
- if (!runtime->dma_area)
- return -ENOMEM;
- else
- runtime->dma_bytes = size;
- return 0;
-}
-
-static int audio_buf_free(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
- int i;
-
- for (i = 0; i < AUDIO_BUFS; i++)
- if (pa->urb_array[i])
- usb_kill_urb(pa->urb_array[i]);
- free_all_urb_generic(pa->urb_array, AUDIO_BUFS);
- logpm();
- return 0;
-}
-
-static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
-
- logpm();
- audio_buf_free(p);
- return 0;
-}
-
-static int snd_pd_prepare(struct snd_pcm_substream *substream)
-{
- return 0;
-}
-
-#define AUDIO_TRAILER_SIZE (16)
-static inline void handle_audio_data(struct urb *urb, int *period_elapsed)
-{
- struct poseidon_audio *pa = urb->context;
- struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime;
-
- int stride = runtime->frame_bits >> 3;
- int len = urb->actual_length / stride;
- unsigned char *cp = urb->transfer_buffer;
- unsigned int oldptr = pa->rcv_position;
-
- if (urb->actual_length == AUDIO_BUF_SIZE - 4)
- len -= (AUDIO_TRAILER_SIZE / stride);
-
- /* do the copy */
- if (oldptr + len >= runtime->buffer_size) {
- unsigned int cnt = runtime->buffer_size - oldptr;
-
- memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride);
- memcpy(runtime->dma_area, (cp + cnt * stride),
- (len * stride - cnt * stride));
- } else
- memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
-
- /* update the statas */
- snd_pcm_stream_lock(pa->capture_pcm_substream);
- pa->rcv_position += len;
- if (pa->rcv_position >= runtime->buffer_size)
- pa->rcv_position -= runtime->buffer_size;
-
- pa->copied_position += (len);
- if (pa->copied_position >= runtime->period_size) {
- pa->copied_position -= runtime->period_size;
- *period_elapsed = 1;
- }
- snd_pcm_stream_unlock(pa->capture_pcm_substream);
-}
-
-static void complete_handler_audio(struct urb *urb)
-{
- struct poseidon_audio *pa = urb->context;
- struct snd_pcm_substream *substream = pa->capture_pcm_substream;
- int period_elapsed = 0;
- int ret;
-
- if (1 == pa->card_close || pa->capture_stream != STREAM_ON)
- return;
-
- if (urb->status != 0) {
- /*if (urb->status == -ESHUTDOWN)*/
- return;
- }
-
- if (substream) {
- if (urb->actual_length) {
- handle_audio_data(urb, &period_elapsed);
- if (period_elapsed)
- snd_pcm_period_elapsed(substream);
- }
- }
-
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret < 0)
- log("audio urb failed (errcod = %i)", ret);
- return;
-}
-
-static int fire_audio_urb(struct poseidon *p)
-{
- int i, ret = 0;
- struct poseidon_audio *pa = &p->audio;
-
- alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS,
- p->udev, AUDIO_EP,
- AUDIO_BUF_SIZE, GFP_ATOMIC,
- complete_handler_audio, pa);
-
- for (i = 0; i < AUDIO_BUFS; i++) {
- ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL);
- if (ret)
- log("urb err : %d", ret);
- }
- log();
- return ret;
-}
-
-static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
-
- if (debug_mode)
- log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_START:
- if (pa->capture_stream == STREAM_ON)
- return 0;
-
- pa->rcv_position = pa->copied_position = 0;
- pa->capture_stream = STREAM_ON;
-
- if (in_hibernation(p))
- return 0;
- fire_audio_urb(p);
- return 0;
-
- case SNDRV_PCM_TRIGGER_SUSPEND:
- pa->capture_stream = STREAM_SUSPEND;
- return 0;
- case SNDRV_PCM_TRIGGER_STOP:
- pa->capture_stream = STREAM_OFF;
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static snd_pcm_uframes_t
-snd_pd_capture_pointer(struct snd_pcm_substream *substream)
-{
- struct poseidon *p = snd_pcm_substream_chip(substream);
- struct poseidon_audio *pa = &p->audio;
- return pa->rcv_position;
-}
-
-static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs,
- unsigned long offset)
-{
- void *pageptr = subs->runtime->dma_area + offset;
- return vmalloc_to_page(pageptr);
-}
-
-static struct snd_pcm_ops pcm_capture_ops = {
- .open = snd_pd_capture_open,
- .close = snd_pd_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_pd_hw_capture_params,
- .hw_free = snd_pd_hw_capture_free,
- .prepare = snd_pd_prepare,
- .trigger = snd_pd_capture_trigger,
- .pointer = snd_pd_capture_pointer,
- .page = snd_pcm_pd_get_page,
-};
-
-#ifdef CONFIG_PM
-int pm_alsa_suspend(struct poseidon *p)
-{
- logpm(p);
- audio_buf_free(p);
- return 0;
-}
-
-int pm_alsa_resume(struct poseidon *p)
-{
- logpm(p);
- fire_audio_urb(p);
- return 0;
-}
-#endif
-
-int poseidon_audio_init(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
- struct snd_card *card;
- struct snd_pcm *pcm;
- int ret;
-
- ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card);
- if (ret != 0)
- return ret;
-
- ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm);
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
- pcm->info_flags = 0;
- pcm->private_data = p;
- strcpy(pcm->name, "poseidon audio capture");
-
- strcpy(card->driver, "ALSA driver");
- strcpy(card->shortname, "poseidon Audio");
- strcpy(card->longname, "poseidon ALSA Audio");
-
- if (snd_card_register(card)) {
- snd_card_free(card);
- return -ENOMEM;
- }
- pa->card = card;
- return 0;
-}
-
-int poseidon_audio_free(struct poseidon *p)
-{
- struct poseidon_audio *pa = &p->audio;
-
- if (pa->card)
- snd_card_free(pa->card);
- return 0;
-}
diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c
deleted file mode 100644
index 4fad1dfb92cf..000000000000
--- a/drivers/media/video/tlg2300/pd-radio.c
+++ /dev/null
@@ -1,421 +0,0 @@
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitmap.h>
-#include <linux/usb.h>
-#include <linux/i2c.h>
-#include <media/v4l2-dev.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/sched.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-static int set_frequency(struct poseidon *p, __u32 frequency);
-static int poseidon_fm_close(struct file *filp);
-static int poseidon_fm_open(struct file *filp);
-
-#define TUNER_FREQ_MIN_FM 76000000
-#define TUNER_FREQ_MAX_FM 108000000
-
-#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1)
-static int preemphasis[MAX_PREEMPHASIS] = {
- TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */
- TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */
- TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */
-};
-
-static int poseidon_check_mode_radio(struct poseidon *p)
-{
- int ret;
- u32 status;
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
- ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE);
- if (ret < 0)
- goto out;
-
- ret = set_tuner_mode(p, TLG_MODE_FM_RADIO);
- if (ret != 0)
- goto out;
-
- ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status);
- ret = send_set_req(p, TUNER_AUD_ANA_STD,
- p->radio_data.pre_emphasis, &status);
- ret |= send_set_req(p, TUNER_AUD_MODE,
- TLG_TUNE_TVAUDIO_MODE_STEREO, &status);
- ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL,
- ATV_AUDIO_RATE_48K, &status);
- ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status);
-out:
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_fm_suspend(struct poseidon *p)
-{
- logpm(p);
- pm_alsa_suspend(p);
- usb_set_interface(p->udev, 0, 0);
- msleep(300);
- return 0;
-}
-
-static int pm_fm_resume(struct poseidon *p)
-{
- logpm(p);
- poseidon_check_mode_radio(p);
- set_frequency(p, p->radio_data.fm_freq);
- pm_alsa_resume(p);
- return 0;
-}
-#endif
-
-static int poseidon_fm_open(struct file *filp)
-{
- struct video_device *vfd = video_devdata(filp);
- struct poseidon *p = video_get_drvdata(vfd);
- int ret = 0;
-
- if (!p)
- return -1;
-
- mutex_lock(&p->lock);
- if (p->state & POSEIDON_STATE_DISCONNECT) {
- ret = -ENODEV;
- goto out;
- }
-
- if (p->state && !(p->state & POSEIDON_STATE_FM)) {
- ret = -EBUSY;
- goto out;
- }
-
- usb_autopm_get_interface(p->interface);
- if (0 == p->state) {
- /* default pre-emphasis */
- if (p->radio_data.pre_emphasis == 0)
- p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR;
- set_debug_mode(vfd, debug_mode);
-
- ret = poseidon_check_mode_radio(p);
- if (ret < 0) {
- usb_autopm_put_interface(p->interface);
- goto out;
- }
- p->state |= POSEIDON_STATE_FM;
- }
- p->radio_data.users++;
- kref_get(&p->kref);
- filp->private_data = p;
-out:
- mutex_unlock(&p->lock);
- return ret;
-}
-
-static int poseidon_fm_close(struct file *filp)
-{
- struct poseidon *p = filp->private_data;
- struct radio_data *fm = &p->radio_data;
- uint32_t status;
-
- mutex_lock(&p->lock);
- fm->users--;
- if (0 == fm->users)
- p->state &= ~POSEIDON_STATE_FM;
-
- if (fm->is_radio_streaming && filp == p->file_for_stream) {
- fm->is_radio_streaming = 0;
- send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status);
- }
- usb_autopm_put_interface(p->interface);
- mutex_unlock(&p->lock);
-
- kref_put(&p->kref, poseidon_delete);
- filp->private_data = NULL;
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct poseidon *p = file->private_data;
-
- strlcpy(v->driver, "tele-radio", sizeof(v->driver));
- strlcpy(v->card, "Telegent Poseidon", sizeof(v->card));
- usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static const struct v4l2_file_operations poseidon_fm_fops = {
- .owner = THIS_MODULE,
- .open = poseidon_fm_open,
- .release = poseidon_fm_close,
- .ioctl = video_ioctl2,
-};
-
-static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *vt)
-{
- struct tuner_fm_sig_stat_s fm_stat = {};
- int ret, status, count = 5;
- struct poseidon *p = file->private_data;
-
- if (vt->index != 0)
- return -EINVAL;
-
- vt->type = V4L2_TUNER_RADIO;
- vt->capability = V4L2_TUNER_CAP_STEREO;
- vt->rangelow = TUNER_FREQ_MIN_FM / 62500;
- vt->rangehigh = TUNER_FREQ_MAX_FM / 62500;
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- vt->audmode = V4L2_TUNER_MODE_STEREO;
- vt->signal = 0;
- vt->afc = 0;
-
- mutex_lock(&p->lock);
- ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
- &fm_stat, &status, sizeof(fm_stat));
-
- while (fm_stat.sig_lock_busy && count-- && !ret) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
-
- ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO,
- &fm_stat, &status, sizeof(fm_stat));
- }
- mutex_unlock(&p->lock);
-
- if (ret || status) {
- vt->signal = 0;
- } else if ((fm_stat.sig_present || fm_stat.sig_locked)
- && fm_stat.sig_strength == 0) {
- vt->signal = 0xffff;
- } else
- vt->signal = (fm_stat.sig_strength * 255 / 10) << 8;
-
- return 0;
-}
-
-static int fm_get_freq(struct file *file, void *priv,
- struct v4l2_frequency *argp)
-{
- struct poseidon *p = file->private_data;
-
- argp->frequency = p->radio_data.fm_freq;
- return 0;
-}
-
-static int set_frequency(struct poseidon *p, __u32 frequency)
-{
- __u32 freq ;
- int ret, status;
-
- mutex_lock(&p->lock);
-
- ret = send_set_req(p, TUNER_AUD_ANA_STD,
- p->radio_data.pre_emphasis, &status);
-
- freq = (frequency * 125) * 500 / 1000;/* kHZ */
- if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) {
- ret = -EINVAL;
- goto error;
- }
-
- ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status);
- if (ret < 0)
- goto error ;
- ret = send_set_req(p, TAKE_REQUEST, 0, &status);
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- if (!p->radio_data.is_radio_streaming) {
- ret = send_set_req(p, TAKE_REQUEST, 0, &status);
- ret = send_set_req(p, PLAY_SERVICE,
- TLG_TUNE_PLAY_SVC_START, &status);
- p->radio_data.is_radio_streaming = 1;
- }
- p->radio_data.fm_freq = frequency;
-error:
- mutex_unlock(&p->lock);
- return ret;
-}
-
-static int fm_set_freq(struct file *file, void *priv,
- struct v4l2_frequency *argp)
-{
- struct poseidon *p = file->private_data;
-
- p->file_for_stream = file;
-#ifdef CONFIG_PM
- p->pm_suspend = pm_fm_suspend;
- p->pm_resume = pm_fm_resume;
-#endif
- return set_frequency(p, argp->frequency);
-}
-
-static int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *arg)
-{
- return 0;
-}
-
-static int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh,
- struct v4l2_ext_controls *ctrls)
-{
- struct poseidon *p = file->private_data;
- int i;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
- return -EINVAL;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = ctrls->controls + i;
-
- if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
- continue;
-
- if (i < MAX_PREEMPHASIS)
- ctrl->value = p->radio_data.pre_emphasis;
- }
- return 0;
-}
-
-static int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh,
- struct v4l2_ext_controls *ctrls)
-{
- int i;
-
- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX)
- return -EINVAL;
-
- for (i = 0; i < ctrls->count; i++) {
- struct v4l2_ext_control *ctrl = ctrls->controls + i;
-
- if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS)
- continue;
-
- if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) {
- struct poseidon *p = file->private_data;
- int pre_emphasis = preemphasis[ctrl->value];
- u32 status;
-
- send_set_req(p, TUNER_AUD_ANA_STD,
- pre_emphasis, &status);
- p->radio_data.pre_emphasis = pre_emphasis;
- }
- }
- return 0;
-}
-
-static int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- return 0;
-}
-
-static int tlg_fm_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *ctrl)
-{
- if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL))
- return -EINVAL;
-
- ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
- if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) {
- /* return the next supported control */
- ctrl->id = V4L2_CID_TUNE_PREEMPHASIS;
- v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED,
- V4L2_PREEMPHASIS_75_uS, 1,
- V4L2_PREEMPHASIS_50_uS);
- ctrl->flags = V4L2_CTRL_FLAG_UPDATE;
- return 0;
- }
- return -EINVAL;
-}
-
-static int tlg_fm_vidioc_querymenu(struct file *file, void *fh,
- struct v4l2_querymenu *qmenu)
-{
- return v4l2_ctrl_query_menu(qmenu, NULL, NULL);
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
-{
- return vt->index > 0 ? -EINVAL : 0;
-}
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va)
-{
- return (va->index != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- a->index = 0;
- a->mode = 0;
- a->capability = V4L2_AUDCAP_STEREO;
- strcpy(a->name, "Radio");
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, u32 i)
-{
- return (i != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, u32 *i)
-{
- return (*i != 0) ? -EINVAL : 0;
-}
-
-static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = tlg_fm_vidioc_queryctrl,
- .vidioc_querymenu = tlg_fm_vidioc_querymenu,
- .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl,
- .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl,
- .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl,
- .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_tuner = tlg_fm_vidioc_g_tuner,
- .vidioc_g_frequency = fm_get_freq,
- .vidioc_s_frequency = fm_set_freq,
-};
-
-static struct video_device poseidon_fm_template = {
- .name = "Telegent-Radio",
- .fops = &poseidon_fm_fops,
- .minor = -1,
- .release = video_device_release,
- .ioctl_ops = &poseidon_fm_ioctl_ops,
-};
-
-int poseidon_fm_init(struct poseidon *p)
-{
- struct video_device *fm_dev;
-
- fm_dev = vdev_init(p, &poseidon_fm_template);
- if (fm_dev == NULL)
- return -1;
-
- if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) {
- video_device_release(fm_dev);
- return -1;
- }
- p->radio_data.fm_dev = fm_dev;
- return 0;
-}
-
-int poseidon_fm_exit(struct poseidon *p)
-{
- destroy_video_device(&p->radio_data.fm_dev);
- return 0;
-}
diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c
deleted file mode 100644
index bfbf9e56b0a4..000000000000
--- a/drivers/media/video/tlg2300/pd-video.c
+++ /dev/null
@@ -1,1668 +0,0 @@
-#include <linux/fs.h>
-#include <linux/vmalloc.h>
-#include <linux/videodev2.h>
-#include <linux/usb.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-dev.h>
-
-#include "pd-common.h"
-#include "vendorcmds.h"
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd);
-static int pm_video_resume(struct poseidon *pd);
-#endif
-static void iso_bubble_handler(struct work_struct *w);
-
-static int usb_transfer_mode;
-module_param(usb_transfer_mode, int, 0644);
-MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous");
-
-static const struct poseidon_format poseidon_formats[] = {
- { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0},
- { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0},
-};
-
-static const struct poseidon_tvnorm poseidon_tvnorms[] = {
- { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D },
- { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B },
- { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G },
- { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H },
- { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I },
- { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M },
- { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO },
- { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO },
- { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M },
- { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J },
- { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B },
- { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D },
- { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G },
- { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H },
- { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K },
- { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 },
- { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L },
- { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 },
-};
-static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms);
-
-struct pd_audio_mode {
- u32 tlg_audio_mode;
- u32 v4l2_audio_sub;
- u32 v4l2_audio_mode;
-};
-
-static const struct pd_audio_mode pd_audio_modes[] = {
- { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO,
- V4L2_TUNER_MODE_MONO },
- { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO,
- V4L2_TUNER_MODE_STEREO },
- { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1,
- V4L2_TUNER_MODE_LANG1 },
- { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2,
- V4L2_TUNER_MODE_LANG2 },
- { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1,
- V4L2_TUNER_MODE_LANG1_LANG2 }
-};
-static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes);
-
-struct pd_input {
- char *name;
- uint32_t tlg_src;
-};
-
-static const struct pd_input pd_inputs[] = {
- { "TV Antenna", TLG_SIG_SRC_ANTENNA },
- { "TV Cable", TLG_SIG_SRC_CABLE },
- { "TV SVideo", TLG_SIG_SRC_SVIDEO },
- { "TV Composite", TLG_SIG_SRC_COMPOSITE }
-};
-static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs);
-
-struct poseidon_control {
- struct v4l2_queryctrl v4l2_ctrl;
- enum cmd_custom_param_id vc_id;
-};
-
-static struct poseidon_control controls[] = {
- {
- { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
- "brightness", 0, 10000, 1, 100, 0, },
- CUST_PARM_ID_BRIGHTNESS_CTRL
- }, {
- { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
- "contrast", 0, 10000, 1, 100, 0, },
- CUST_PARM_ID_CONTRAST_CTRL,
- }, {
- { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
- "hue", 0, 10000, 1, 100, 0, },
- CUST_PARM_ID_HUE_CTRL,
- }, {
- { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
- "saturation", 0, 10000, 1, 100, 0, },
- CUST_PARM_ID_SATURATION_CTRL,
- },
-};
-
-struct video_std_to_audio_std {
- v4l2_std_id video_std;
- int audio_std;
-};
-
-static const struct video_std_to_audio_std video_to_audio_map[] = {
- /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64,
- 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */
- { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D |
- V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM },
-
- /* country : { 1, 52, 54, 55, 886 } */
- {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC},
-
- /* country : { 81 } */
- { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ },
-
- /* other country : TLG_TUNE_ASTD_A2 */
-};
-static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map);
-
-static int get_audio_std(v4l2_std_id v4l2_std)
-{
- int i = 0;
-
- for (; i < map_size; i++) {
- if (v4l2_std & video_to_audio_map[i].video_std)
- return video_to_audio_map[i].audio_std;
- }
- return TLG_TUNE_ASTD_A2;
-}
-
-static int vidioc_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct front_face *front = fh;
- struct poseidon *p = front->pd;
-
- logs(front);
-
- strcpy(cap->driver, "tele-video");
- strcpy(cap->card, "Telegent Poseidon");
- usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
- V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
- return 0;
-}
-
-/*====================================================================*/
-static void init_copy(struct video_data *video, bool index)
-{
- struct front_face *front = video->front;
-
- video->field_count = index;
- video->lines_copied = 0;
- video->prev_left = 0 ;
- video->dst = (char *)videobuf_to_vmalloc(front->curr_frame)
- + index * video->lines_size;
- video->vbi->copied = 0; /* set it here */
-}
-
-static bool get_frame(struct front_face *front, int *need_init)
-{
- struct videobuf_buffer *vb = front->curr_frame;
-
- if (vb)
- return true;
-
- spin_lock(&front->queue_lock);
- if (!list_empty(&front->active)) {
- vb = list_entry(front->active.next,
- struct videobuf_buffer, queue);
- if (need_init)
- *need_init = 1;
- front->curr_frame = vb;
- list_del_init(&vb->queue);
- }
- spin_unlock(&front->queue_lock);
-
- return !!vb;
-}
-
-/* check if the video's buffer is ready */
-static bool get_video_frame(struct front_face *front, struct video_data *video)
-{
- int need_init = 0;
- bool ret = true;
-
- ret = get_frame(front, &need_init);
- if (ret && need_init)
- init_copy(video, 0);
- return ret;
-}
-
-static void submit_frame(struct front_face *front)
-{
- struct videobuf_buffer *vb = front->curr_frame;
-
- if (vb == NULL)
- return;
-
- front->curr_frame = NULL;
- vb->state = VIDEOBUF_DONE;
- vb->field_count++;
- do_gettimeofday(&vb->ts);
-
- wake_up(&vb->done);
-}
-
-/*
- * A frame is composed of two fields. If we receive all the two fields,
- * call the submit_frame() to submit the whole frame to applications.
- */
-static void end_field(struct video_data *video)
-{
- /* logs(video->front); */
- if (1 == video->field_count)
- submit_frame(video->front);
- else
- init_copy(video, 1);
-}
-
-static void copy_video_data(struct video_data *video, char *src,
- unsigned int count)
-{
-#define copy_data(len) \
- do { \
- if (++video->lines_copied > video->lines_per_field) \
- goto overflow; \
- memcpy(video->dst, src, len);\
- video->dst += len + video->lines_size; \
- src += len; \
- count -= len; \
- } while (0)
-
- while (count && count >= video->lines_size) {
- if (video->prev_left) {
- copy_data(video->prev_left);
- video->prev_left = 0;
- continue;
- }
- copy_data(video->lines_size);
- }
- if (count && count < video->lines_size) {
- memcpy(video->dst, src, count);
-
- video->prev_left = video->lines_size - count;
- video->dst += count;
- }
- return;
-
-overflow:
- end_field(video);
-}
-
-static void check_trailer(struct video_data *video, char *src, int count)
-{
- struct vbi_data *vbi = video->vbi;
- int offset; /* trailer's offset */
- char *buf;
-
- offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2)
- - (vbi->copied + video->lines_size * video->lines_copied);
- if (video->prev_left)
- offset -= (video->lines_size - video->prev_left);
-
- if (offset > count || offset <= 0)
- goto short_package;
-
- buf = src + offset;
-
- /* trailer : (VFHS) + U32 + U32 + field_num */
- if (!strncmp(buf, "VFHS", 4)) {
- int field_num = *((u32 *)(buf + 12));
-
- if ((field_num & 1) ^ video->field_count) {
- init_copy(video, video->field_count);
- return;
- }
- copy_video_data(video, src, offset);
- }
-short_package:
- end_field(video);
-}
-
-/* ========== Check this more carefully! =========== */
-static inline void copy_vbi_data(struct vbi_data *vbi,
- char *src, unsigned int count)
-{
- struct front_face *front = vbi->front;
-
- if (front && get_frame(front, NULL)) {
- char *buf = videobuf_to_vmalloc(front->curr_frame);
-
- if (vbi->video->field_count)
- buf += (vbi->vbi_size / 2);
- memcpy(buf + vbi->copied, src, count);
- }
- vbi->copied += count;
-}
-
-/*
- * Copy the normal data (VBI or VIDEO) without the trailer.
- * VBI is not interlaced, while VIDEO is interlaced.
- */
-static inline void copy_vbi_video_data(struct video_data *video,
- char *src, unsigned int count)
-{
- struct vbi_data *vbi = video->vbi;
- unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied;
-
- if (vbi_delta >= count) {
- copy_vbi_data(vbi, src, count);
- } else {
- if (vbi_delta) {
- copy_vbi_data(vbi, src, vbi_delta);
-
- /* we receive the two fields of the VBI*/
- if (vbi->front && video->field_count)
- submit_frame(vbi->front);
- }
- copy_video_data(video, src + vbi_delta, count - vbi_delta);
- }
-}
-
-static void urb_complete_bulk(struct urb *urb)
-{
- struct front_face *front = urb->context;
- struct video_data *video = &front->pd->video_data;
- char *src = (char *)urb->transfer_buffer;
- int count = urb->actual_length;
- int ret = 0;
-
- if (!video->is_streaming || urb->status) {
- if (urb->status == -EPROTO)
- goto resend_it;
- return;
- }
- if (!get_video_frame(front, video))
- goto resend_it;
-
- if (count == urb->transfer_buffer_length)
- copy_vbi_video_data(video, src, count);
- else
- check_trailer(video, src, count);
-
-resend_it:
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- log(" submit failed: error %d", ret);
-}
-
-/************************* for ISO *********************/
-#define GET_SUCCESS (0)
-#define GET_TRAILER (1)
-#define GET_TOO_MUCH_BUBBLE (2)
-#define GET_NONE (3)
-static int get_chunk(int start, struct urb *urb,
- int *head, int *tail, int *bubble_err)
-{
- struct usb_iso_packet_descriptor *pkt = NULL;
- int ret = GET_SUCCESS;
-
- for (*head = *tail = -1; start < urb->number_of_packets; start++) {
- pkt = &urb->iso_frame_desc[start];
-
- /* handle the bubble of the Hub */
- if (-EOVERFLOW == pkt->status) {
- if (++*bubble_err > urb->number_of_packets / 3)
- return GET_TOO_MUCH_BUBBLE;
- continue;
- }
-
- /* This is the gap */
- if (pkt->status || pkt->actual_length <= 0
- || pkt->actual_length > ISO_PKT_SIZE) {
- if (*head != -1)
- break;
- continue;
- }
-
- /* a good isochronous packet */
- if (pkt->actual_length == ISO_PKT_SIZE) {
- if (*head == -1)
- *head = start;
- *tail = start;
- continue;
- }
-
- /* trailer is here */
- if (pkt->actual_length < ISO_PKT_SIZE) {
- if (*head == -1) {
- *head = start;
- *tail = start;
- return GET_TRAILER;
- }
- break;
- }
- }
-
- if (*head == -1 && *tail == -1)
- ret = GET_NONE;
- return ret;
-}
-
-/*
- * |__|------|___|-----|_______|
- * ^ ^
- * | |
- * gap gap
- */
-static void urb_complete_iso(struct urb *urb)
-{
- struct front_face *front = urb->context;
- struct video_data *video = &front->pd->video_data;
- int bubble_err = 0, head = 0, tail = 0;
- char *src = (char *)urb->transfer_buffer;
- int ret = 0;
-
- if (!video->is_streaming)
- return;
-
- do {
- if (!get_video_frame(front, video))
- goto out;
-
- switch (get_chunk(head, urb, &head, &tail, &bubble_err)) {
- case GET_SUCCESS:
- copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE),
- (tail - head + 1) * ISO_PKT_SIZE);
- break;
- case GET_TRAILER:
- check_trailer(video, src + (head * ISO_PKT_SIZE),
- ISO_PKT_SIZE);
- break;
- case GET_NONE:
- goto out;
- case GET_TOO_MUCH_BUBBLE:
- log("\t We got too much bubble");
- schedule_work(&video->bubble_work);
- return;
- }
- } while (head = tail + 1, head < urb->number_of_packets);
-
-out:
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret)
- log("usb_submit_urb err : %d", ret);
-}
-/*============================= [ end ] =====================*/
-
-static int prepare_iso_urb(struct video_data *video)
-{
- struct usb_device *udev = video->pd->udev;
- int i;
-
- if (video->urb_array[0])
- return 0;
-
- for (i = 0; i < SBUF_NUM; i++) {
- struct urb *urb;
- void *mem;
- int j;
-
- urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL);
- if (urb == NULL)
- goto out;
-
- video->urb_array[i] = urb;
- mem = usb_alloc_coherent(udev,
- ISO_PKT_SIZE * PK_PER_URB,
- GFP_KERNEL,
- &urb->transfer_dma);
-
- urb->complete = urb_complete_iso; /* handler */
- urb->dev = udev;
- urb->context = video->front;
- urb->pipe = usb_rcvisocpipe(udev,
- video->endpoint_addr);
- urb->interval = 1;
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- urb->number_of_packets = PK_PER_URB;
- urb->transfer_buffer = mem;
- urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE;
-
- for (j = 0; j < PK_PER_URB; j++) {
- urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j;
- urb->iso_frame_desc[j].length = ISO_PKT_SIZE;
- }
- }
- return 0;
-out:
- for (; i > 0; i--)
- ;
- return -ENOMEM;
-}
-
-/* return the succeeded number of the allocation */
-int alloc_bulk_urbs_generic(struct urb **urb_array, int num,
- struct usb_device *udev, u8 ep_addr,
- int buf_size, gfp_t gfp_flags,
- usb_complete_t complete_fn, void *context)
-{
- int i = 0;
-
- for (; i < num; i++) {
- void *mem;
- struct urb *urb = usb_alloc_urb(0, gfp_flags);
- if (urb == NULL)
- return i;
-
- mem = usb_alloc_coherent(udev, buf_size, gfp_flags,
- &urb->transfer_dma);
- if (mem == NULL) {
- usb_free_urb(urb);
- return i;
- }
-
- usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr),
- mem, buf_size, complete_fn, context);
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- urb_array[i] = urb;
- }
- return i;
-}
-
-void free_all_urb_generic(struct urb **urb_array, int num)
-{
- int i;
- struct urb *urb;
-
- for (i = 0; i < num; i++) {
- urb = urb_array[i];
- if (urb) {
- usb_free_coherent(urb->dev,
- urb->transfer_buffer_length,
- urb->transfer_buffer,
- urb->transfer_dma);
- usb_free_urb(urb);
- urb_array[i] = NULL;
- }
- }
-}
-
-static int prepare_bulk_urb(struct video_data *video)
-{
- if (video->urb_array[0])
- return 0;
-
- alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM,
- video->pd->udev, video->endpoint_addr,
- 0x2000, GFP_KERNEL,
- urb_complete_bulk, video->front);
- return 0;
-}
-
-/* free the URBs */
-static void free_all_urb(struct video_data *video)
-{
- free_all_urb_generic(video->urb_array, SBUF_NUM);
-}
-
-static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- videobuf_vmalloc_free(vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
-}
-
-static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
-{
- struct front_face *front = q->priv_data;
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &front->active);
-}
-
-static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct front_face *front = q->priv_data;
- int rc;
-
- switch (front->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- struct v4l2_pix_format *pix;
-
- pix = &front->pd->video_data.context.pix;
- vb->size = pix->sizeimage; /* real frame size */
- vb->width = pix->width;
- vb->height = pix->height;
- rc = videobuf_iolock(q, vb, NULL);
- if (rc < 0)
- return rc;
- }
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (VIDEOBUF_NEEDS_INIT == vb->state) {
- vb->size = front->pd->vbi_data.vbi_size;
- rc = videobuf_iolock(q, vb, NULL);
- if (rc < 0)
- return rc;
- }
- break;
- default:
- return -EINVAL;
- }
- vb->field = field;
- vb->state = VIDEOBUF_PREPARED;
- return 0;
-}
-
-static int fire_all_urb(struct video_data *video)
-{
- int i, ret;
-
- video->is_streaming = 1;
-
- for (i = 0; i < SBUF_NUM; i++) {
- ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL);
- if (ret)
- log("(%d) failed: error %d", i, ret);
- }
- return ret;
-}
-
-static int start_video_stream(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- s32 cmd_status;
-
- send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status);
-
- if (pd->cur_transfer_mode) {
- prepare_iso_urb(video);
- INIT_WORK(&video->bubble_work, iso_bubble_handler);
- } else {
- /* The bulk mode does not need a bubble handler */
- prepare_bulk_urb(video);
- }
- fire_all_urb(video);
- return 0;
-}
-
-static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count,
- unsigned int *size)
-{
- struct front_face *front = q->priv_data;
- struct poseidon *pd = front->pd;
-
- switch (front->type) {
- default:
- return -EINVAL;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
- struct video_data *video = &pd->video_data;
- struct v4l2_pix_format *pix = &video->context.pix;
-
- *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */
- if (*count < 4)
- *count = 4;
- if (1) {
- /* same in different altersetting */
- video->endpoint_addr = 0x82;
- video->vbi = &pd->vbi_data;
- video->vbi->video = video;
- video->pd = pd;
- video->lines_per_field = pix->height / 2;
- video->lines_size = pix->width * 2;
- video->front = front;
- }
- return start_video_stream(pd);
- }
-
- case V4L2_BUF_TYPE_VBI_CAPTURE: {
- struct vbi_data *vbi = &pd->vbi_data;
-
- *size = PAGE_ALIGN(vbi->vbi_size);
- log("size : %d", *size);
- if (*count == 0)
- *count = 4;
- }
- break;
- }
- return 0;
-}
-
-static struct videobuf_queue_ops pd_video_qops = {
- .buf_setup = pd_buf_setup,
- .buf_prepare = pd_buf_prepare,
- .buf_queue = pd_buf_queue,
- .buf_release = pd_buf_release,
-};
-
-static int vidioc_enum_fmt(struct file *file, void *fh,
- struct v4l2_fmtdesc *f)
-{
- if (ARRAY_SIZE(poseidon_formats) <= f->index)
- return -EINVAL;
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->flags = 0;
- f->pixelformat = poseidon_formats[f->index].fourcc;
- strcpy(f->description, poseidon_formats[f->index].name);
- return 0;
-}
-
-static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
-
- logs(front);
- f->fmt.pix = pd->video_data.context.pix;
- return 0;
-}
-
-static int vidioc_try_fmt(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- return 0;
-}
-
-/*
- * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while
- * Mplayer calls them in the reverse order.
- */
-static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix)
-{
- struct video_data *video = &pd->video_data;
- struct running_context *context = &video->context;
- struct v4l2_pix_format *pix_def = &context->pix;
- s32 ret = 0, cmd_status = 0, vid_resol;
-
- /* set the pixel format to firmware */
- if (pix->pixelformat == V4L2_PIX_FMT_RGB565) {
- vid_resol = TLG_TUNER_VID_FORMAT_RGB_565;
- } else {
- pix->pixelformat = V4L2_PIX_FMT_YUYV;
- vid_resol = TLG_TUNER_VID_FORMAT_YUV;
- }
- ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL,
- vid_resol, &cmd_status);
-
- /* set the resolution to firmware */
- vid_resol = TLG_TUNE_VID_RES_720;
- switch (pix->width) {
- case 704:
- vid_resol = TLG_TUNE_VID_RES_704;
- break;
- default:
- pix->width = 720;
- case 720:
- break;
- }
- ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
- vid_resol, &cmd_status);
- if (ret || cmd_status)
- return -EBUSY;
-
- pix_def->pixelformat = pix->pixelformat; /* save it */
- pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576;
-
- /* Compare with the default setting */
- if ((pix_def->width != pix->width)
- || (pix_def->height != pix->height)) {
- pix_def->width = pix->width;
- pix_def->height = pix->height;
- pix_def->bytesperline = pix->width * 2;
- pix_def->sizeimage = pix->width * pix->height * 2;
- }
- *pix = *pix_def;
-
- return 0;
-}
-
-static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
-
- logs(front);
- /* stop VBI here */
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- if (pd->file_for_stream == NULL)
- pd->file_for_stream = file;
- else if (file != pd->file_for_stream) {
- mutex_unlock(&pd->lock);
- return -EINVAL;
- }
-
- pd_vidioc_s_fmt(pd, &f->fmt.pix);
- mutex_unlock(&pd->lock);
- return 0;
-}
-
-static int vidioc_g_fmt_vbi(struct file *file, void *fh,
- struct v4l2_format *v4l2_f)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi;
-
- vbi_fmt->samples_per_line = 720 * 2;
- vbi_fmt->sampling_rate = 6750000 * 4;
- vbi_fmt->sample_format = V4L2_PIX_FMT_GREY;
- vbi_fmt->offset = 64 * 4; /*FIXME: why offset */
- if (pd->video_data.context.tvnormid & V4L2_STD_525_60) {
- vbi_fmt->start[0] = 10;
- vbi_fmt->start[1] = 264;
- vbi_fmt->count[0] = V4L_NTSC_VBI_LINES;
- vbi_fmt->count[1] = V4L_NTSC_VBI_LINES;
- } else {
- vbi_fmt->start[0] = 6;
- vbi_fmt->start[1] = 314;
- vbi_fmt->count[0] = V4L_PAL_VBI_LINES;
- vbi_fmt->count[1] = V4L_PAL_VBI_LINES;
- }
- vbi_fmt->flags = V4L2_VBI_UNSYNC;
- logs(front);
- return 0;
-}
-
-static int set_std(struct poseidon *pd, v4l2_std_id *norm)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
- struct running_context *context;
- struct v4l2_pix_format *pix;
- s32 i, ret = 0, cmd_status, param;
- int height;
-
- for (i = 0; i < POSEIDON_TVNORMS; i++) {
- if (*norm & poseidon_tvnorms[i].v4l2_id) {
- param = poseidon_tvnorms[i].tlg_tvnorm;
- log("name : %s", poseidon_tvnorms[i].name);
- goto found;
- }
- }
- return -EINVAL;
-found:
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status);
- if (ret || cmd_status)
- goto out;
-
- /* Set vbi size and check the height of the frame */
- context = &video->context;
- context->tvnormid = poseidon_tvnorms[i].v4l2_id;
- if (context->tvnormid & V4L2_STD_525_60) {
- vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE;
- height = 480;
- } else {
- vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE;
- height = 576;
- }
-
- pix = &context->pix;
- if (pix->height != height) {
- pix->height = height;
- pix->sizeimage = pix->width * pix->height * 2;
- }
-
-out:
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm)
-{
- struct front_face *front = fh;
- logs(front);
- return set_std(front->pd, norm);
-}
-
-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in)
-{
- struct front_face *front = fh;
-
- if (in->index < 0 || in->index >= POSEIDON_INPUTS)
- return -EINVAL;
- strcpy(in->name, pd_inputs[in->index].name);
- in->type = V4L2_INPUT_TYPE_TUNER;
-
- /*
- * the audio input index mixed with this video input,
- * Poseidon only have one audio/video, set to "0"
- */
- in->audioset = 0;
- in->tuner = 0;
- in->std = V4L2_STD_ALL;
- in->status = 0;
- logs(front);
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct running_context *context = &pd->video_data.context;
-
- logs(front);
- *i = context->sig_index;
- return 0;
-}
-
-/* We can support several inputs */
-static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- s32 ret, cmd_status;
-
- if (i < 0 || i >= POSEIDON_INPUTS)
- return -EINVAL;
- ret = send_set_req(pd, SGNL_SRC_SEL,
- pd_inputs[i].tlg_src, &cmd_status);
- if (ret)
- return ret;
-
- pd->video_data.context.sig_index = i;
- return 0;
-}
-
-static struct poseidon_control *check_control_id(__u32 id)
-{
- struct poseidon_control *control = &controls[0];
- int array_size = ARRAY_SIZE(controls);
-
- for (; control < &controls[array_size]; control++)
- if (control->v4l2_ctrl.id == id)
- return control;
- return NULL;
-}
-
-static int vidioc_queryctrl(struct file *file, void *fh,
- struct v4l2_queryctrl *a)
-{
- struct poseidon_control *control = NULL;
-
- control = check_control_id(a->id);
- if (!control)
- return -EINVAL;
-
- *a = control->v4l2_ctrl;
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct poseidon_control *control = NULL;
- struct tuner_custom_parameter_s tuner_param;
- s32 ret = 0, cmd_status;
-
- control = check_control_id(ctrl->id);
- if (!control)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id,
- &tuner_param, &cmd_status, sizeof(tuner_param));
- mutex_unlock(&pd->lock);
-
- if (ret || cmd_status)
- return -1;
-
- ctrl->value = tuner_param.param_value;
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a)
-{
- struct tuner_custom_parameter_s param = {0};
- struct poseidon_control *control = NULL;
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- s32 ret = 0, cmd_status, params;
-
- control = check_control_id(a->id);
- if (!control)
- return -EINVAL;
-
- param.param_value = a->value;
- param.param_id = control->vc_id;
- params = *(s32 *)&param; /* temp code */
-
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status);
- ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
- mutex_unlock(&pd->lock);
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- return ret;
-}
-
-/* Audio ioctls */
-static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- if (0 != a->index)
- return -EINVAL;
- a->capability = V4L2_AUDCAP_STEREO;
- strcpy(a->name, "USB audio in");
- /*Poseidon have no AVL function.*/
- a->mode = 0;
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- a->index = 0;
- a->capability = V4L2_AUDCAP_STEREO;
- strcpy(a->name, "USB audio in");
- a->mode = 0;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
-{
- return (0 == a->index) ? 0 : -EINVAL;
-}
-
-/* Tuner ioctls */
-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct tuner_atv_sig_stat_s atv_stat;
- s32 count = 5, ret, cmd_status;
- int index;
-
- if (0 != tuner->index)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
- &atv_stat, &cmd_status, sizeof(atv_stat));
-
- while (atv_stat.sig_lock_busy && count-- && !ret) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
-
- ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV,
- &atv_stat, &cmd_status, sizeof(atv_stat));
- }
- mutex_unlock(&pd->lock);
-
- if (debug_mode)
- log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength);
-
- if (ret || cmd_status)
- tuner->signal = 0;
- else if (atv_stat.sig_present && !atv_stat.sig_strength)
- tuner->signal = 0xFFFF;
- else
- tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8;
-
- strcpy(tuner->name, "Telegent Systems");
- tuner->type = V4L2_TUNER_ANALOG_TV;
- tuner->rangelow = TUNER_FREQ_MIN / 62500;
- tuner->rangehigh = TUNER_FREQ_MAX / 62500;
- tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- index = pd->video_data.context.audio_idx;
- tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub;
- tuner->audmode = pd_audio_modes[index].v4l2_audio_mode;
- tuner->afc = 0;
- logs(front);
- return 0;
-}
-
-static int pd_vidioc_s_tuner(struct poseidon *pd, int index)
-{
- s32 ret = 0, cmd_status, param, audiomode;
-
- mutex_lock(&pd->lock);
- param = pd_audio_modes[index].tlg_audio_mode;
- ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status);
- audiomode = get_audio_std(pd->video_data.context.tvnormid);
- ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode,
- &cmd_status);
- if (!ret)
- pd->video_data.context.audio_idx = index;
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- int index;
-
- if (0 != a->index)
- return -EINVAL;
- logs(front);
- for (index = 0; index < POSEIDON_AUDIOMODS; index++)
- if (a->audmode == pd_audio_modes[index].v4l2_audio_mode)
- return pd_vidioc_s_tuner(pd, index);
- return -EINVAL;
-}
-
-static int vidioc_g_frequency(struct file *file, void *fh,
- struct v4l2_frequency *freq)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
- struct running_context *context = &pd->video_data.context;
-
- if (0 != freq->tuner)
- return -EINVAL;
- freq->frequency = context->freq;
- freq->type = V4L2_TUNER_ANALOG_TV;
- return 0;
-}
-
-static int set_frequency(struct poseidon *pd, __u32 frequency)
-{
- s32 ret = 0, param, cmd_status;
- struct running_context *context = &pd->video_data.context;
-
- param = frequency * 62500 / 1000;
- if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000)
- return -EINVAL;
-
- mutex_lock(&pd->lock);
- ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status);
- ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status);
-
- msleep(250); /* wait for a while until the hardware is ready. */
- context->freq = frequency;
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int vidioc_s_frequency(struct file *file, void *fh,
- struct v4l2_frequency *freq)
-{
- struct front_face *front = fh;
- struct poseidon *pd = front->pd;
-
- logs(front);
-#ifdef CONFIG_PM
- pd->pm_suspend = pm_video_suspend;
- pd->pm_resume = pm_video_resume;
-#endif
- return set_frequency(pd, freq->frequency);
-}
-
-static int vidioc_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *b)
-{
- struct front_face *front = file->private_data;
- logs(front);
- return videobuf_reqbufs(&front->q, b);
-}
-
-static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- logs(front);
- return videobuf_querybuf(&front->q, b);
-}
-
-static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_qbuf(&front->q, b);
-}
-
-static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
-{
- struct front_face *front = file->private_data;
- return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK);
-}
-
-/* Just stop the URBs, do not free the URBs */
-static int usb_transfer_stop(struct video_data *video)
-{
- if (video->is_streaming) {
- int i;
- s32 cmd_status;
- struct poseidon *pd = video->pd;
-
- video->is_streaming = 0;
- for (i = 0; i < SBUF_NUM; ++i) {
- if (video->urb_array[i])
- usb_kill_urb(video->urb_array[i]);
- }
-
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
- &cmd_status);
- }
- return 0;
-}
-
-int stop_all_video_stream(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
-
- mutex_lock(&pd->lock);
- if (video->is_streaming) {
- struct front_face *front = video->front;
-
- /* stop the URBs */
- usb_transfer_stop(video);
- free_all_urb(video);
-
- /* stop the host side of VIDEO */
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
-
- /* stop the host side of VBI */
- front = vbi->front;
- if (front) {
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
- }
- }
- mutex_unlock(&pd->lock);
- return 0;
-}
-
-/*
- * The bubbles can seriously damage the video's quality,
- * though it occurs in very rare situation.
- */
-static void iso_bubble_handler(struct work_struct *w)
-{
- struct video_data *video;
- struct poseidon *pd;
-
- video = container_of(w, struct video_data, bubble_work);
- pd = video->pd;
-
- mutex_lock(&pd->lock);
- usb_transfer_stop(video);
- msleep(500);
- start_video_stream(pd);
- mutex_unlock(&pd->lock);
-}
-
-
-static int vidioc_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct front_face *front = fh;
-
- logs(front);
- if (unlikely(type != front->type))
- return -EINVAL;
- return videobuf_streamon(&front->q);
-}
-
-static int vidioc_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct front_face *front = file->private_data;
-
- logs(front);
- if (unlikely(type != front->type))
- return -EINVAL;
- return videobuf_streamoff(&front->q);
-}
-
-/* Set the firmware's default values : need altersetting */
-static int pd_video_checkmode(struct poseidon *pd)
-{
- s32 ret = 0, cmd_status, audiomode;
-
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/2);
-
- /* choose the altersetting */
- ret = usb_set_interface(pd->udev, 0,
- (pd->cur_transfer_mode ?
- ISO_3K_BULK_ALTERNATE_IFACE :
- BULK_ALTERNATE_IFACE));
- if (ret < 0)
- goto error;
-
- /* set default parameters for PAL-D , with the VBI enabled*/
- ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV);
- ret |= send_set_req(pd, SGNL_SRC_SEL,
- TLG_SIG_SRC_ANTENNA, &cmd_status);
- ret |= send_set_req(pd, VIDEO_STD_SEL,
- TLG_TUNE_VSTD_PAL_D, &cmd_status);
- ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL,
- TLG_TUNER_VID_FORMAT_YUV, &cmd_status);
- ret |= send_set_req(pd, VIDEO_ROSOLU_SEL,
- TLG_TUNE_VID_RES_720, &cmd_status);
- ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status);
- ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */
-
- /* set the audio */
- audiomode = get_audio_std(pd->video_data.context.tvnormid);
- ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status);
- ret |= send_set_req(pd, TUNER_AUD_MODE,
- TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status);
- ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL,
- ATV_AUDIO_RATE_48K, &cmd_status);
-error:
- return ret;
-}
-
-#ifdef CONFIG_PM
-static int pm_video_suspend(struct poseidon *pd)
-{
- /* stop audio */
- pm_alsa_suspend(pd);
-
- /* stop and free all the URBs */
- usb_transfer_stop(&pd->video_data);
- free_all_urb(&pd->video_data);
-
- /* reset the interface */
- usb_set_interface(pd->udev, 0, 0);
- msleep(300);
- return 0;
-}
-
-static int restore_v4l2_context(struct poseidon *pd,
- struct running_context *context)
-{
- struct front_face *front = pd->video_data.front;
-
- pd_video_checkmode(pd);
-
- set_std(pd, &context->tvnormid);
- vidioc_s_input(NULL, front, context->sig_index);
- pd_vidioc_s_tuner(pd, context->audio_idx);
- pd_vidioc_s_fmt(pd, &context->pix);
- set_frequency(pd, context->freq);
- return 0;
-}
-
-static int pm_video_resume(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
-
- /* resume the video */
- /* [1] restore the origin V4L2 parameters */
- restore_v4l2_context(pd, &video->context);
-
- /* [2] initiate video copy variables */
- if (video->front->curr_frame)
- init_copy(video, 0);
-
- /* [3] fire urbs */
- start_video_stream(pd);
-
- /* resume the audio */
- pm_alsa_resume(pd);
- return 0;
-}
-#endif
-
-void set_debug_mode(struct video_device *vfd, int debug_mode)
-{
- vfd->debug = 0;
- if (debug_mode & 0x1)
- vfd->debug = V4L2_DEBUG_IOCTL;
- if (debug_mode & 0x2)
- vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
-}
-
-static void init_video_context(struct running_context *context)
-{
- context->sig_index = 0;
- context->audio_idx = 1; /* stereo */
- context->tvnormid = V4L2_STD_PAL_D;
- context->pix = (struct v4l2_pix_format) {
- .width = 720,
- .height = 576,
- .pixelformat = V4L2_PIX_FMT_YUYV,
- .field = V4L2_FIELD_INTERLACED,
- .bytesperline = 720 * 2,
- .sizeimage = 720 * 576 * 2,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .priv = 0
- };
-}
-
-static int pd_video_open(struct file *file)
-{
- struct video_device *vfd = video_devdata(file);
- struct poseidon *pd = video_get_drvdata(vfd);
- struct front_face *front = NULL;
- int ret = -ENOMEM;
-
- mutex_lock(&pd->lock);
- usb_autopm_get_interface(pd->interface);
-
- if (vfd->vfl_type == VFL_TYPE_GRABBER
- && !(pd->state & POSEIDON_STATE_ANALOG)) {
- front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
- if (!front)
- goto out;
-
- pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */
- init_video_context(&pd->video_data.context);
-
- ret = pd_video_checkmode(pd);
- if (ret < 0) {
- kfree(front);
- ret = -1;
- goto out;
- }
-
- pd->state |= POSEIDON_STATE_ANALOG;
- front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- pd->video_data.users++;
- set_debug_mode(vfd, debug_mode);
-
- videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
- NULL, &front->queue_lock,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,/* video is interlacd */
- sizeof(struct videobuf_buffer),/*it's enough*/
- front, NULL);
- } else if (vfd->vfl_type == VFL_TYPE_VBI
- && !(pd->state & POSEIDON_STATE_VBI)) {
- front = kzalloc(sizeof(struct front_face), GFP_KERNEL);
- if (!front)
- goto out;
-
- pd->state |= POSEIDON_STATE_VBI;
- front->type = V4L2_BUF_TYPE_VBI_CAPTURE;
- pd->vbi_data.front = front;
- pd->vbi_data.users++;
-
- videobuf_queue_vmalloc_init(&front->q, &pd_video_qops,
- NULL, &front->queue_lock,
- V4L2_BUF_TYPE_VBI_CAPTURE,
- V4L2_FIELD_NONE, /* vbi is NONE mode */
- sizeof(struct videobuf_buffer),
- front, NULL);
- } else {
- /* maybe add FM support here */
- log("other ");
- ret = -EINVAL;
- goto out;
- }
-
- front->pd = pd;
- front->curr_frame = NULL;
- INIT_LIST_HEAD(&front->active);
- spin_lock_init(&front->queue_lock);
-
- file->private_data = front;
- kref_get(&pd->kref);
-
- mutex_unlock(&pd->lock);
- return 0;
-out:
- usb_autopm_put_interface(pd->interface);
- mutex_unlock(&pd->lock);
- return ret;
-}
-
-static int pd_video_release(struct file *file)
-{
- struct front_face *front = file->private_data;
- struct poseidon *pd = front->pd;
- s32 cmd_status = 0;
-
- logs(front);
- mutex_lock(&pd->lock);
-
- if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- pd->state &= ~POSEIDON_STATE_ANALOG;
-
- /* stop the device, and free the URBs */
- usb_transfer_stop(&pd->video_data);
- free_all_urb(&pd->video_data);
-
- /* stop the firmware */
- send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP,
- &cmd_status);
-
- pd->file_for_stream = NULL;
- pd->video_data.users--;
- } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- pd->state &= ~POSEIDON_STATE_VBI;
- pd->vbi_data.front = NULL;
- pd->vbi_data.users--;
- }
- videobuf_stop(&front->q);
- videobuf_mmap_free(&front->q);
-
- usb_autopm_put_interface(pd->interface);
- mutex_unlock(&pd->lock);
-
- kfree(front);
- file->private_data = NULL;
- kref_put(&pd->kref, poseidon_delete);
- return 0;
-}
-
-static int pd_video_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct front_face *front = file->private_data;
- return videobuf_mmap_mapper(&front->q, vma);
-}
-
-static unsigned int pd_video_poll(struct file *file, poll_table *table)
-{
- struct front_face *front = file->private_data;
- return videobuf_poll_stream(file, &front->q, table);
-}
-
-static ssize_t pd_video_read(struct file *file, char __user *buffer,
- size_t count, loff_t *ppos)
-{
- struct front_face *front = file->private_data;
- return videobuf_read_stream(&front->q, buffer, count, ppos,
- 0, file->f_flags & O_NONBLOCK);
-}
-
-/* This struct works for both VIDEO and VBI */
-static const struct v4l2_file_operations pd_video_fops = {
- .owner = THIS_MODULE,
- .open = pd_video_open,
- .release = pd_video_release,
- .read = pd_video_read,
- .poll = pd_video_poll,
- .mmap = pd_video_mmap,
- .ioctl = video_ioctl2, /* maybe changed in future */
-};
-
-static const struct v4l2_ioctl_ops pd_video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
-
- /* Video format */
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt,
-
- /* Input */
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_enum_input = vidioc_enum_input,
-
- /* Audio ioctls */
- .vidioc_enumaudio = vidioc_enumaudio,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
-
- /* Tuner ioctls */
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-
- /* Buffer handlers */
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-
- /* Stream on/off */
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
-
- /* Control handling */
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static struct video_device pd_video_template = {
- .name = "Telegent-Video",
- .fops = &pd_video_fops,
- .minor = -1,
- .release = video_device_release,
- .tvnorms = V4L2_STD_ALL,
- .ioctl_ops = &pd_video_ioctl_ops,
-};
-
-struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (vfd == NULL)
- return NULL;
- *vfd = *tmp;
- vfd->minor = -1;
- vfd->v4l2_dev = &pd->v4l2_dev;
- /*vfd->parent = &(pd->udev->dev); */
- vfd->release = video_device_release;
- video_set_drvdata(vfd, pd);
- return vfd;
-}
-
-void destroy_video_device(struct video_device **v_dev)
-{
- struct video_device *dev = *v_dev;
-
- if (dev == NULL)
- return;
-
- if (video_is_registered(dev))
- video_unregister_device(dev);
- else
- video_device_release(dev);
- *v_dev = NULL;
-}
-
-void pd_video_exit(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
-
- destroy_video_device(&video->v_dev);
- destroy_video_device(&vbi->v_dev);
- log();
-}
-
-int pd_video_init(struct poseidon *pd)
-{
- struct video_data *video = &pd->video_data;
- struct vbi_data *vbi = &pd->vbi_data;
- int ret = -ENOMEM;
-
- video->v_dev = vdev_init(pd, &pd_video_template);
- if (video->v_dev == NULL)
- goto out;
-
- ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1);
- if (ret != 0)
- goto out;
-
- /* VBI uses the same template as video */
- vbi->v_dev = vdev_init(pd, &pd_video_template);
- if (vbi->v_dev == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1);
- if (ret != 0)
- goto out;
- log("register VIDEO/VBI devices");
- return 0;
-out:
- log("VIDEO/VBI devices register failed, : %d", ret);
- pd_video_exit(pd);
- return ret;
-}
-
diff --git a/drivers/media/video/tm6000/Makefile b/drivers/media/video/tm6000/Makefile
deleted file mode 100644
index 395515b4a888..000000000000
--- a/drivers/media/video/tm6000/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-tm6000-y := tm6000-cards.o \
- tm6000-core.o \
- tm6000-i2c.o \
- tm6000-video.o \
- tm6000-stds.o \
- tm6000-input.o
-
-obj-$(CONFIG_VIDEO_TM6000) += tm6000.o
-obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o
-obj-$(CONFIG_VIDEO_TM6000_DVB) += tm6000-dvb.o
-
-ccflags-y := -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c
deleted file mode 100644
index bd07ec707956..000000000000
--- a/drivers/media/video/tm6000/tm6000-alsa.c
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- *
- * Support for audio capture for tm5600/6000/6010
- * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * Based on cx88-alsa.c
- *
- * 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/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/usb.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-
-#include <linux/delay.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/control.h>
-#include <sound/initval.h>
-
-
-#include "tm6000.h"
-#include "tm6000-regs.h"
-
-#undef dprintk
-
-#define dprintk(level, fmt, arg...) do { \
- if (debug >= level) \
- printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \
- } while (0)
-
-/****************************************************************************
- Module global static vars
- ****************************************************************************/
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
-
-static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
-
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s).");
-
-
-/****************************************************************************
- Module macros
- ****************************************************************************/
-
-MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000/tm6010 based TV cards");
-MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Trident,tm5600},"
- "{{Trident,tm6000},"
- "{{Trident,tm6010}");
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "enable debug messages");
-
-/****************************************************************************
- Module specific funtions
- ****************************************************************************/
-
-/*
- * BOARD Specific: Sets audio DMA
- */
-
-static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip)
-{
- struct tm6000_core *core = chip->core;
-
- dprintk(1, "Starting audio DMA\n");
-
- /* Enables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x40, 0x40);
-
- tm6000_set_audio_bitrate(core, 48000);
-
- return 0;
-}
-
-/*
- * BOARD Specific: Resets audio DMA
- */
-static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip)
-{
- struct tm6000_core *core = chip->core;
-
- dprintk(1, "Stopping audio DMA\n");
-
- /* Disables audio */
- tm6000_set_reg_mask(core, TM6010_REQ07_RCC_ACTIVE_IF, 0x00, 0x40);
-
- return 0;
-}
-
-static void dsp_buffer_free(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
-
- dprintk(2, "Freeing buffer\n");
-
- vfree(substream->runtime->dma_area);
- substream->runtime->dma_area = NULL;
- substream->runtime->dma_bytes = 0;
-}
-
-static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
-
- dprintk(2, "Allocating buffer\n");
-
- if (substream->runtime->dma_area) {
- if (substream->runtime->dma_bytes > size)
- return 0;
-
- dsp_buffer_free(substream);
- }
-
- substream->runtime->dma_area = vmalloc(size);
- if (!substream->runtime->dma_area)
- return -ENOMEM;
-
- substream->runtime->dma_bytes = size;
-
- return 0;
-}
-
-
-/****************************************************************************
- ALSA PCM Interface
- ****************************************************************************/
-
-/*
- * Digital hardware definition
- */
-#define DEFAULT_FIFO_SIZE 4096
-
-static struct snd_pcm_hardware snd_tm6000_digital_hw = {
- .info = SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
-
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
- .rate_min = 48000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .period_bytes_min = 64,
- .period_bytes_max = 12544,
- .periods_min = 2,
- .periods_max = 98,
- .buffer_bytes_max = 62720 * 8,
-};
-
-/*
- * audio pcm capture open callback
- */
-static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
- int err;
-
- err = snd_pcm_hw_constraint_pow2(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (err < 0)
- goto _error;
-
- chip->substream = substream;
-
- runtime->hw = snd_tm6000_digital_hw;
- snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-
- return 0;
-_error:
- dprintk(1, "Error opening PCM!\n");
- return err;
-}
-
-/*
- * audio close callback
- */
-static int snd_tm6000_close(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
- struct tm6000_core *core = chip->core;
-
- if (atomic_read(&core->stream_started) > 0) {
- atomic_set(&core->stream_started, 0);
- schedule_work(&core->wq_trigger);
- }
-
- return 0;
-}
-
-static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size)
-{
- struct snd_tm6000_card *chip = core->adev;
- struct snd_pcm_substream *substream = chip->substream;
- struct snd_pcm_runtime *runtime;
- int period_elapsed = 0;
- unsigned int stride, buf_pos;
- int length;
-
- if (atomic_read(&core->stream_started) == 0)
- return 0;
-
- if (!size || !substream) {
- dprintk(1, "substream was NULL\n");
- return -EINVAL;
- }
-
- runtime = substream->runtime;
- if (!runtime || !runtime->dma_area) {
- dprintk(1, "runtime was NULL\n");
- return -EINVAL;
- }
-
- buf_pos = chip->buf_pos;
- stride = runtime->frame_bits >> 3;
-
- if (stride == 0) {
- dprintk(1, "stride is zero\n");
- return -EINVAL;
- }
-
- length = size / stride;
- if (length == 0) {
- dprintk(1, "%s: length was zero\n", __func__);
- return -EINVAL;
- }
-
- dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size,
- runtime->dma_area, buf_pos,
- (unsigned int)runtime->buffer_size, stride);
-
- if (buf_pos + length >= runtime->buffer_size) {
- unsigned int cnt = runtime->buffer_size - buf_pos;
- memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride);
- memcpy(runtime->dma_area, buf + cnt * stride,
- length * stride - cnt * stride);
- } else
- memcpy(runtime->dma_area + buf_pos * stride, buf,
- length * stride);
-
- snd_pcm_stream_lock(substream);
-
- chip->buf_pos += length;
- if (chip->buf_pos >= runtime->buffer_size)
- chip->buf_pos -= runtime->buffer_size;
-
- chip->period_pos += length;
- if (chip->period_pos >= runtime->period_size) {
- chip->period_pos -= runtime->period_size;
- period_elapsed = 1;
- }
-
- snd_pcm_stream_unlock(substream);
-
- if (period_elapsed)
- snd_pcm_period_elapsed(substream);
-
- return 0;
-}
-
-/*
- * hw_params callback
- */
-static int snd_tm6000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int size, rc;
-
- size = params_period_bytes(hw_params) * params_periods(hw_params);
-
- rc = dsp_buffer_alloc(substream, size);
- if (rc < 0)
- return rc;
-
- return 0;
-}
-
-/*
- * hw free callback
- */
-static int snd_tm6000_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
- struct tm6000_core *core = chip->core;
-
- if (atomic_read(&core->stream_started) > 0) {
- atomic_set(&core->stream_started, 0);
- schedule_work(&core->wq_trigger);
- }
-
- dsp_buffer_free(substream);
- return 0;
-}
-
-/*
- * prepare callback
- */
-static int snd_tm6000_prepare(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
-
- chip->buf_pos = 0;
- chip->period_pos = 0;
-
- return 0;
-}
-
-
-/*
- * trigger callback
- */
-static void audio_trigger(struct work_struct *work)
-{
- struct tm6000_core *core = container_of(work, struct tm6000_core,
- wq_trigger);
- struct snd_tm6000_card *chip = core->adev;
-
- if (atomic_read(&core->stream_started)) {
- dprintk(1, "starting capture");
- _tm6000_start_audio_dma(chip);
- } else {
- dprintk(1, "stopping capture");
- _tm6000_stop_audio_dma(chip);
- }
-}
-
-static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
- struct tm6000_core *core = chip->core;
- int err = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
- case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
- case SNDRV_PCM_TRIGGER_START:
- atomic_set(&core->stream_started, 1);
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
- case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
- case SNDRV_PCM_TRIGGER_STOP:
- atomic_set(&core->stream_started, 0);
- break;
- default:
- err = -EINVAL;
- break;
- }
- schedule_work(&core->wq_trigger);
-
- return err;
-}
-/*
- * pointer callback
- */
-static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream);
-
- return chip->buf_pos;
-}
-
-static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
- unsigned long offset)
-{
- void *pageptr = subs->runtime->dma_area + offset;
-
- return vmalloc_to_page(pageptr);
-}
-
-/*
- * operators
- */
-static struct snd_pcm_ops snd_tm6000_pcm_ops = {
- .open = snd_tm6000_pcm_open,
- .close = snd_tm6000_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_tm6000_hw_params,
- .hw_free = snd_tm6000_hw_free,
- .prepare = snd_tm6000_prepare,
- .trigger = snd_tm6000_card_trigger,
- .pointer = snd_tm6000_pointer,
- .page = snd_pcm_get_vmalloc_page,
-};
-
-/*
- * create a PCM device
- */
-
-/* FIXME: Control interface - How to control volume/mute? */
-
-/****************************************************************************
- Basic Flow for Sound Devices
- ****************************************************************************/
-
-/*
- * Alsa Constructor - Component probe
- */
-static int tm6000_audio_init(struct tm6000_core *dev)
-{
- struct snd_card *card;
- struct snd_tm6000_card *chip;
- int rc;
- static int devnr;
- char component[14];
- struct snd_pcm *pcm;
-
- if (!dev)
- return 0;
-
- if (devnr >= SNDRV_CARDS)
- return -ENODEV;
-
- if (!enable[devnr])
- return -ENOENT;
-
- rc = snd_card_create(index[devnr], "tm6000", THIS_MODULE, 0, &card);
- if (rc < 0) {
- snd_printk(KERN_ERR "cannot create card instance %d\n", devnr);
- return rc;
- }
- strcpy(card->driver, "tm6000-alsa");
- strcpy(card->shortname, "TM5600/60x0");
- sprintf(card->longname, "TM5600/60x0 Audio at bus %d device %d",
- dev->udev->bus->busnum, dev->udev->devnum);
-
- sprintf(component, "USB%04x:%04x",
- le16_to_cpu(dev->udev->descriptor.idVendor),
- le16_to_cpu(dev->udev->descriptor.idProduct));
- snd_component_add(card, component);
- snd_card_set_dev(card, &dev->udev->dev);
-
- chip = kzalloc(sizeof(struct snd_tm6000_card), GFP_KERNEL);
- if (!chip) {
- rc = -ENOMEM;
- goto error;
- }
-
- chip->core = dev;
- chip->card = card;
- dev->adev = chip;
- spin_lock_init(&chip->reg_lock);
-
- rc = snd_pcm_new(card, "TM6000 Audio", 0, 0, 1, &pcm);
- if (rc < 0)
- goto error_chip;
-
- pcm->info_flags = 0;
- pcm->private_data = chip;
- strcpy(pcm->name, "Trident TM5600/60x0");
-
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops);
-
- INIT_WORK(&dev->wq_trigger, audio_trigger);
- rc = snd_card_register(card);
- if (rc < 0)
- goto error_chip;
-
- dprintk(1, "Registered audio driver for %s\n", card->longname);
-
- return 0;
-
-error_chip:
- kfree(chip);
- dev->adev = NULL;
-error:
- snd_card_free(card);
- return rc;
-}
-
-static int tm6000_audio_fini(struct tm6000_core *dev)
-{
- struct snd_tm6000_card *chip = dev->adev;
-
- if (!dev)
- return 0;
-
- if (!chip)
- return 0;
-
- if (!chip->card)
- return 0;
-
- snd_card_free(chip->card);
- chip->card = NULL;
- kfree(chip);
- dev->adev = NULL;
-
- return 0;
-}
-
-static struct tm6000_ops audio_ops = {
- .type = TM6000_AUDIO,
- .name = "TM6000 Audio Extension",
- .init = tm6000_audio_init,
- .fini = tm6000_audio_fini,
- .fillbuf = tm6000_fillbuf,
-};
-
-static int __init tm6000_alsa_register(void)
-{
- return tm6000_register_extension(&audio_ops);
-}
-
-static void __exit tm6000_alsa_unregister(void)
-{
- tm6000_unregister_extension(&audio_ops);
-}
-
-module_init(tm6000_alsa_register);
-module_exit(tm6000_alsa_unregister);
diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
deleted file mode 100644
index e80b7e190471..000000000000
--- a/drivers/media/video/tm6000/tm6000-input.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * tm6000-input.c - driver for TM5600/TM6000/TM6010 USB video capture devices
- *
- * Copyright (C) 2010 Stefan Ringel <stefan.ringel@arcor.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-
-#include <linux/input.h>
-#include <linux/usb.h>
-
-#include <media/rc-core.h>
-
-#include "tm6000.h"
-#include "tm6000-regs.h"
-
-static unsigned int ir_debug;
-module_param(ir_debug, int, 0644);
-MODULE_PARM_DESC(ir_debug, "debug message level");
-
-static unsigned int enable_ir = 1;
-module_param(enable_ir, int, 0644);
-MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
-
-static unsigned int ir_clock_mhz = 12;
-module_param(ir_clock_mhz, int, 0644);
-MODULE_PARM_DESC(enable_ir, "ir clock, in MHz");
-
-#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */
-#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */
-
-#undef dprintk
-
-#define dprintk(level, fmt, arg...) do {\
- if (ir_debug >= level) \
- printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
- } while (0)
-
-struct tm6000_ir_poll_result {
- u16 rc_data;
-};
-
-struct tm6000_IR {
- struct tm6000_core *dev;
- struct rc_dev *rc;
- char name[32];
- char phys[32];
-
- /* poll expernal decoder */
- int polling;
- struct delayed_work work;
- u8 wait:1;
- u8 pwled:2;
- u8 submit_urb:1;
- u16 key_addr;
- struct urb *int_urb;
-
- /* IR device properties */
- u64 rc_type;
-};
-
-void tm6000_ir_wait(struct tm6000_core *dev, u8 state)
-{
- struct tm6000_IR *ir = dev->ir;
-
- if (!dev->ir)
- return;
-
- dprintk(2, "%s: %i\n",__func__, ir->wait);
-
- if (state)
- ir->wait = 1;
- else
- ir->wait = 0;
-}
-
-static int tm6000_ir_config(struct tm6000_IR *ir)
-{
- struct tm6000_core *dev = ir->dev;
- u32 pulse = 0, leader = 0;
-
- dprintk(2, "%s\n",__func__);
-
- /*
- * The IR decoder supports RC-5 or NEC, with a configurable timing.
- * The timing configuration there is not that accurate, as it uses
- * approximate values. The NEC spec mentions a 562.5 unit period,
- * and RC-5 uses a 888.8 period.
- * Currently, driver assumes a clock provided by a 12 MHz XTAL, but
- * a modprobe parameter can adjust it.
- * Adjustments are required for other timings.
- * It seems that the 900ms timing for NEC is used to detect a RC-5
- * IR, in order to discard such decoding
- */
-
- switch (ir->rc_type) {
- case RC_TYPE_NEC:
- leader = 900; /* ms */
- pulse = 700; /* ms - the actual value would be 562 */
- break;
- default:
- case RC_TYPE_RC5:
- leader = 900; /* ms - from the NEC decoding */
- pulse = 1780; /* ms - The actual value would be 1776 */
- break;
- }
-
- pulse = ir_clock_mhz * pulse;
- leader = ir_clock_mhz * leader;
- if (ir->rc_type == RC_TYPE_NEC)
- leader = leader | 0x8000;
-
- dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n",
- __func__,
- (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5",
- ir_clock_mhz, leader, pulse);
-
- /* Remote WAKEUP = enable, normal mode, from IR decoder output */
- tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
-
- /* Enable IR reception on non-busrt mode */
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f);
-
- /* IR_WKUP_SEL = Low byte in decoded IR data */
- tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff);
- /* IR_WKU_ADD code */
- tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff);
-
- tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8);
- tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader);
-
- tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8);
- tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse);
-
- if (!ir->polling)
- tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
- else
- tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1);
- msleep(10);
-
- /* Shows that IR is working via the LED */
- tm6000_flash_led(dev, 0);
- msleep(100);
- tm6000_flash_led(dev, 1);
- ir->pwled = 1;
-
- return 0;
-}
-
-static void tm6000_ir_urb_received(struct urb *urb)
-{
- struct tm6000_core *dev = urb->context;
- struct tm6000_IR *ir = dev->ir;
- struct tm6000_ir_poll_result poll_result;
- char *buf;
-
- dprintk(2, "%s\n",__func__);
- if (urb->status < 0 || urb->actual_length <= 0) {
- printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n",
- urb->status, urb->actual_length);
- ir->submit_urb = 1;
- schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
- return;
- }
- buf = urb->transfer_buffer;
-
- if (ir_debug)
- print_hex_dump(KERN_DEBUG, "tm6000: IR data: ",
- DUMP_PREFIX_OFFSET,16, 1,
- buf, urb->actual_length, false);
-
- poll_result.rc_data = buf[0];
- if (urb->actual_length > 1)
- poll_result.rc_data |= buf[1] << 8;
-
- dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
- rc_keydown(ir->rc, poll_result.rc_data, 0);
-
- usb_submit_urb(urb, GFP_ATOMIC);
- /*
- * Flash the led. We can't do it here, as it is running on IRQ context.
- * So, use the scheduler to do it, in a few ms.
- */
- ir->pwled = 2;
- schedule_delayed_work(&ir->work, msecs_to_jiffies(10));
-}
-
-static void tm6000_ir_handle_key(struct work_struct *work)
-{
- struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
- struct tm6000_core *dev = ir->dev;
- struct tm6000_ir_poll_result poll_result;
- int rc;
- u8 buf[2];
-
- if (ir->wait)
- return;
-
- dprintk(3, "%s\n",__func__);
-
- rc = tm6000_read_write_usb(dev, USB_DIR_IN |
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- REQ_02_GET_IR_CODE, 0, 0, buf, 2);
- if (rc < 0)
- return;
-
- if (rc > 1)
- poll_result.rc_data = buf[0] | buf[1] << 8;
- else
- poll_result.rc_data = buf[0];
-
- /* Check if something was read */
- if ((poll_result.rc_data & 0xff) == 0xff) {
- if (!ir->pwled) {
- tm6000_flash_led(dev, 1);
- ir->pwled = 1;
- }
- return;
- }
-
- dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
- rc_keydown(ir->rc, poll_result.rc_data, 0);
- tm6000_flash_led(dev, 0);
- ir->pwled = 0;
-
- /* Re-schedule polling */
- schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
-}
-
-static void tm6000_ir_int_work(struct work_struct *work)
-{
- struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
- struct tm6000_core *dev = ir->dev;
- int rc;
-
- dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb,
- ir->pwled);
-
- if (ir->submit_urb) {
- dprintk(3, "Resubmit urb\n");
- tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
-
- rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC);
- if (rc < 0) {
- printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n",
- rc);
- /* Retry in 100 ms */
- schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
- return;
- }
- ir->submit_urb = 0;
- }
-
- /* Led is enabled only if USB submit doesn't fail */
- if (ir->pwled == 2) {
- tm6000_flash_led(dev, 0);
- ir->pwled = 0;
- schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY));
- } else if (!ir->pwled) {
- tm6000_flash_led(dev, 1);
- ir->pwled = 1;
- }
-}
-
-static int tm6000_ir_start(struct rc_dev *rc)
-{
- struct tm6000_IR *ir = rc->priv;
-
- dprintk(2, "%s\n",__func__);
-
- schedule_delayed_work(&ir->work, 0);
-
- return 0;
-}
-
-static void tm6000_ir_stop(struct rc_dev *rc)
-{
- struct tm6000_IR *ir = rc->priv;
-
- dprintk(2, "%s\n",__func__);
-
- cancel_delayed_work_sync(&ir->work);
-}
-
-static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
-{
- struct tm6000_IR *ir = rc->priv;
-
- if (!ir)
- return 0;
-
- dprintk(2, "%s\n",__func__);
-
- if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
- ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
-
- ir->rc_type = rc_type;
-
- tm6000_ir_config(ir);
- /* TODO */
- return 0;
-}
-
-static int __tm6000_ir_int_start(struct rc_dev *rc)
-{
- struct tm6000_IR *ir = rc->priv;
- struct tm6000_core *dev = ir->dev;
- int pipe, size;
- int err = -ENOMEM;
-
- if (!ir)
- return -ENODEV;
-
- dprintk(2, "%s\n",__func__);
-
- ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!ir->int_urb)
- return -ENOMEM;
-
- pipe = usb_rcvintpipe(dev->udev,
- dev->int_in.endp->desc.bEndpointAddress
- & USB_ENDPOINT_NUMBER_MASK);
-
- size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
- dprintk(1, "IR max size: %d\n", size);
-
- ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC);
- if (ir->int_urb->transfer_buffer == NULL) {
- usb_free_urb(ir->int_urb);
- return err;
- }
- dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval);
-
- usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
- ir->int_urb->transfer_buffer, size,
- tm6000_ir_urb_received, dev,
- dev->int_in.endp->desc.bInterval);
-
- ir->submit_urb = 1;
- schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
-
- return 0;
-}
-
-static void __tm6000_ir_int_stop(struct rc_dev *rc)
-{
- struct tm6000_IR *ir = rc->priv;
-
- if (!ir || !ir->int_urb)
- return;
-
- dprintk(2, "%s\n",__func__);
-
- usb_kill_urb(ir->int_urb);
- kfree(ir->int_urb->transfer_buffer);
- usb_free_urb(ir->int_urb);
- ir->int_urb = NULL;
-}
-
-int tm6000_ir_int_start(struct tm6000_core *dev)
-{
- struct tm6000_IR *ir = dev->ir;
-
- if (!ir)
- return 0;
-
- return __tm6000_ir_int_start(ir->rc);
-}
-
-void tm6000_ir_int_stop(struct tm6000_core *dev)
-{
- struct tm6000_IR *ir = dev->ir;
-
- if (!ir || !ir->rc)
- return;
-
- __tm6000_ir_int_stop(ir->rc);
-}
-
-int tm6000_ir_init(struct tm6000_core *dev)
-{
- struct tm6000_IR *ir;
- struct rc_dev *rc;
- int err = -ENOMEM;
-
- if (!enable_ir)
- return -ENODEV;
-
- if (!dev->caps.has_remote)
- return 0;
-
- if (!dev->ir_codes)
- return 0;
-
- ir = kzalloc(sizeof(*ir), GFP_ATOMIC);
- rc = rc_allocate_device();
- if (!ir || !rc)
- goto out;
-
- dprintk(2, "%s\n", __func__);
-
- /* record handles to ourself */
- ir->dev = dev;
- dev->ir = ir;
- ir->rc = rc;
-
- /* input setup */
- rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
- /* Neded, in order to support NEC remotes with 24 or 32 bits */
- rc->scanmask = 0xffff;
- rc->priv = ir;
- rc->change_protocol = tm6000_ir_change_protocol;
- if (dev->int_in.endp) {
- rc->open = __tm6000_ir_int_start;
- rc->close = __tm6000_ir_int_stop;
- INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work);
- } else {
- rc->open = tm6000_ir_start;
- rc->close = tm6000_ir_stop;
- ir->polling = 50;
- INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key);
- }
- rc->driver_type = RC_DRIVER_SCANCODE;
-
- snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)",
- dev->name);
-
- usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
- strlcat(ir->phys, "/input0", sizeof(ir->phys));
-
- tm6000_ir_change_protocol(rc, RC_TYPE_UNKNOWN);
-
- rc->input_name = ir->name;
- rc->input_phys = ir->phys;
- rc->input_id.bustype = BUS_USB;
- rc->input_id.version = 1;
- rc->input_id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
- rc->input_id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
- rc->map_name = dev->ir_codes;
- rc->driver_name = "tm6000";
- rc->dev.parent = &dev->udev->dev;
-
- /* ir register */
- err = rc_register_device(rc);
- if (err)
- goto out;
-
- return 0;
-
-out:
- dev->ir = NULL;
- rc_free_device(rc);
- kfree(ir);
- return err;
-}
-
-int tm6000_ir_fini(struct tm6000_core *dev)
-{
- struct tm6000_IR *ir = dev->ir;
-
- /* skip detach on non attached board */
-
- if (!ir)
- return 0;
-
- dprintk(2, "%s\n",__func__);
-
- if (!ir->polling)
- __tm6000_ir_int_stop(ir->rc);
-
- tm6000_ir_stop(ir->rc);
-
- /* Turn off the led */
- tm6000_flash_led(dev, 0);
- ir->pwled = 0;
-
- rc_unregister_device(ir->rc);
-
- kfree(ir);
- dev->ir = NULL;
-
- return 0;
-}
diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
deleted file mode 100644
index f7034df94e0a..000000000000
--- a/drivers/media/video/tm6000/tm6000-video.c
+++ /dev/null
@@ -1,1818 +0,0 @@
-/*
- * tm6000-video.c - driver for TM5600/TM6000/TM6010 USB video capture devices
- *
- * Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org>
- *
- * Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com>
- * - Fixed module load/unload
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/random.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-ioctl.h>
-#include <media/tuner.h>
-#include <linux/interrupt.h>
-#include <linux/kthread.h>
-#include <linux/highmem.h>
-#include <linux/freezer.h>
-
-#include "tm6000-regs.h"
-#include "tm6000.h"
-
-#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */
-
-/* Limits minimum and default number of buffers */
-#define TM6000_MIN_BUF 4
-#define TM6000_DEF_BUF 8
-
-#define TM6000_MAX_ISO_PACKETS 46 /* Max number of ISO packets */
-
-/* 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[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 54,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 119,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 112,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .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[] = {
- {
- .name = "4:2:2, packed, YVY2",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- }, {
- .name = "A/V + VBI mux packet",
- .fourcc = V4L2_PIX_FMT_TM6000,
- .depth = 16,
- }
-};
-
-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
- * ------------------------------------------------------------------
- */
-
-#define norm_maxw(a) 720
-#define norm_maxh(a) 576
-
-#define norm_minw(a) norm_maxw(a)
-#define norm_minh(a) norm_maxh(a)
-
-/*
- * video-buf generic routine to get the next available buffer
- */
-static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
- struct tm6000_buffer **buf)
-{
- struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
-
- if (list_empty(&dma_q->active)) {
- dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
- *buf = NULL;
- return;
- }
-
- *buf = list_entry(dma_q->active.next,
- struct tm6000_buffer, vb.queue);
-}
-
-/*
- * Announces that a buffer were filled and request the next
- */
-static inline void buffer_filled(struct tm6000_core *dev,
- struct tm6000_dmaqueue *dma_q,
- struct tm6000_buffer *buf)
-{
- /* Advice that buffer was filled */
- dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i);
- buf->vb.state = VIDEOBUF_DONE;
- buf->vb.field_count++;
- do_gettimeofday(&buf->vb.ts);
-
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
-}
-
-/*
- * Identify the tm5600/6000 buffer header type and properly handles
- */
-static int copy_streams(u8 *data, unsigned long len,
- struct urb *urb)
-{
- struct tm6000_dmaqueue *dma_q = urb->context;
- struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- u8 *ptr = data, *endp = data+len;
- unsigned long header = 0;
- int rc = 0;
- unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
- struct tm6000_buffer *vbuf = NULL;
- char *voutp = NULL;
- unsigned int linewidth;
-
- 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) {
- /* Header */
- if (dev->isoc_ctl.tmp_buf_len > 0) {
- /* from last urb or packet */
- header = dev->isoc_ctl.tmp_buf;
- if (4 - dev->isoc_ctl.tmp_buf_len > 0) {
- memcpy((u8 *)&header +
- dev->isoc_ctl.tmp_buf_len,
- ptr,
- 4 - dev->isoc_ctl.tmp_buf_len);
- ptr += 4 - dev->isoc_ctl.tmp_buf_len;
- }
- dev->isoc_ctl.tmp_buf_len = 0;
- } else {
- if (ptr + 3 >= endp) {
- /* have incomplete header */
- dev->isoc_ctl.tmp_buf_len = endp - ptr;
- memcpy(&dev->isoc_ctl.tmp_buf, ptr,
- dev->isoc_ctl.tmp_buf_len);
- return rc;
- }
- /* Seek for sync */
- for (; ptr < endp - 3; ptr++) {
- if (*(ptr + 3) == 0x47)
- break;
- }
- /* Get message header */
- header = *(unsigned long *)ptr;
- ptr += 4;
- }
-
- /* split the header fields */
- size = ((header & 0x7e) << 1);
- if (size > 0)
- size -= 4;
- block = (header >> 7) & 0xf;
- field = (header >> 11) & 0x1;
- line = (header >> 12) & 0x1ff;
- cmd = (header >> 21) & 0x7;
- /* Validates haeder fields */
- if (size > TM6000_URB_MSG_LEN)
- size = TM6000_URB_MSG_LEN;
- pktsize = TM6000_URB_MSG_LEN;
- /*
- * calculate position in buffer and change the buffer
- */
- switch (cmd) {
- case TM6000_URB_MSG_VIDEO:
- 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,
- "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;
- }
- break;
- case TM6000_URB_MSG_VBI:
- break;
- case TM6000_URB_MSG_AUDIO:
- case TM6000_URB_MSG_PTS:
- size = pktsize; /* Size is always 180 bytes */
- break;
- }
- } else {
- /* Continue the last copy */
- cmd = dev->isoc_ctl.cmd;
- size = dev->isoc_ctl.size;
- pos = dev->isoc_ctl.pos;
- pktsize = dev->isoc_ctl.pktsize;
- field = dev->isoc_ctl.field;
- }
- cpysize = (endp - ptr > size) ? size : endp - ptr;
- if (cpysize) {
- /* copy data in different buffers */
- switch (cmd) {
- case TM6000_URB_MSG_VIDEO:
- /* Fills video buffer */
- if (vbuf)
- memcpy(&voutp[pos], ptr, cpysize);
- break;
- case TM6000_URB_MSG_AUDIO: {
- int i;
- for (i = 0; i < cpysize; i += 2)
- swab16s((u16 *)(ptr + i));
-
- tm6000_call_fillbuf(dev, TM6000_AUDIO, ptr, cpysize);
- break;
- }
- case TM6000_URB_MSG_VBI:
- /* Need some code to copy vbi buffer */
- break;
- case TM6000_URB_MSG_PTS: {
- /* Need some code to copy pts */
- u32 pts;
- pts = *(u32 *)ptr;
- dprintk(dev, V4L2_DEBUG_ISOC, "field %d, PTS %x",
- field, pts);
- break;
- }
- }
- }
- if (ptr + pktsize > endp) {
- /*
- * End of URB packet, but cmd processing is not
- * complete. Preserve the state for a next packet
- */
- dev->isoc_ctl.pos = pos + cpysize;
- dev->isoc_ctl.size = size - cpysize;
- dev->isoc_ctl.cmd = cmd;
- dev->isoc_ctl.field = field;
- dev->isoc_ctl.pktsize = pktsize - (endp - ptr);
- ptr += endp - ptr;
- } else {
- dev->isoc_ctl.cmd = 0;
- ptr += pktsize;
- }
- }
- return 0;
-}
-
-/*
- * Identify the tm5600/6000 buffer header type and properly handles
- */
-static int copy_multiplexed(u8 *ptr, unsigned long len,
- struct urb *urb)
-{
- struct tm6000_dmaqueue *dma_q = urb->context;
- struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- unsigned int pos = dev->isoc_ctl.pos, cpysize;
- int rc = 1;
- struct tm6000_buffer *buf;
- char *outp = NULL;
-
- get_next_buf(dma_q, &buf);
- if (buf)
- outp = videobuf_to_vmalloc(&buf->vb);
-
- if (!outp)
- return 0;
-
- while (len > 0) {
- cpysize = min(len, buf->vb.size-pos);
- memcpy(&outp[pos], ptr, cpysize);
- pos += cpysize;
- ptr += cpysize;
- len -= cpysize;
- if (pos >= buf->vb.size) {
- pos = 0;
- /* Announces that a new buffer were filled */
- buffer_filled(dev, dma_q, buf);
- dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n");
- get_next_buf(dma_q, &buf);
- if (!buf)
- break;
- outp = videobuf_to_vmalloc(&(buf->vb));
- if (!outp)
- return rc;
- pos = 0;
- }
- }
-
- dev->isoc_ctl.pos = pos;
- return rc;
-}
-
-static inline void print_err_status(struct tm6000_core *dev,
- int packet, int status)
-{
- char *errmsg = "Unknown";
-
- switch (status) {
- case -ENOENT:
- errmsg = "unlinked synchronuously";
- break;
- case -ECONNRESET:
- errmsg = "unlinked asynchronuously";
- break;
- case -ENOSR:
- errmsg = "Buffer error (overrun)";
- break;
- case -EPIPE:
- errmsg = "Stalled (device not responding)";
- break;
- case -EOVERFLOW:
- errmsg = "Babble (bad cable?)";
- break;
- case -EPROTO:
- errmsg = "Bit-stuff error (bad cable?)";
- break;
- case -EILSEQ:
- errmsg = "CRC/Timeout (could be anything)";
- break;
- case -ETIME:
- errmsg = "Device does not respond";
- break;
- }
- if (packet < 0) {
- dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n",
- status, errmsg);
- } else {
- dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n",
- packet, status, errmsg);
- }
-}
-
-
-/*
- * Controls the isoc copy of each urb packet
- */
-static inline int tm6000_isoc_copy(struct urb *urb)
-{
- struct tm6000_dmaqueue *dma_q = urb->context;
- struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- int i, len = 0, rc = 1, status;
- char *p;
-
- if (urb->status < 0) {
- print_err_status(dev, -1, urb->status);
- return 0;
- }
-
- for (i = 0; i < urb->number_of_packets; i++) {
- status = urb->iso_frame_desc[i].status;
-
- if (status < 0) {
- print_err_status(dev, i, status);
- continue;
- }
-
- len = urb->iso_frame_desc[i].actual_length;
-
- if (len > 0) {
- p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- if (!urb->iso_frame_desc[i].status) {
- if ((dev->fourcc) == V4L2_PIX_FMT_TM6000) {
- rc = copy_multiplexed(p, len, urb);
- if (rc <= 0)
- return rc;
- } else {
- copy_streams(p, len, urb);
- }
- }
- }
- }
- return rc;
-}
-
-/* ------------------------------------------------------------------
- * URB control
- * ------------------------------------------------------------------
- */
-
-/*
- * IRQ callback, called by URB callback
- */
-static void tm6000_irq_callback(struct urb *urb)
-{
- struct tm6000_dmaqueue *dma_q = urb->context;
- struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- int i;
-
- switch (urb->status) {
- case 0:
- case -ETIMEDOUT:
- break;
-
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- return;
-
- default:
- tm6000_err("urb completion error %d.\n", urb->status);
- break;
- }
-
- spin_lock(&dev->slock);
- tm6000_isoc_copy(urb);
- spin_unlock(&dev->slock);
-
- /* Reset urb buffers */
- for (i = 0; i < urb->number_of_packets; i++) {
- urb->iso_frame_desc[i].status = 0;
- urb->iso_frame_desc[i].actual_length = 0;
- }
-
- urb->status = usb_submit_urb(urb, GFP_ATOMIC);
- if (urb->status)
- tm6000_err("urb resubmit failed (error=%i)\n",
- urb->status);
-}
-
-/*
- * Stop and Deallocate URBs
- */
-static void tm6000_uninit_isoc(struct tm6000_core *dev)
-{
- struct urb *urb;
- int i;
-
- dev->isoc_ctl.buf = NULL;
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = dev->isoc_ctl.urb[i];
- if (urb) {
- usb_kill_urb(urb);
- usb_unlink_urb(urb);
- if (dev->isoc_ctl.transfer_buffer[i]) {
- usb_free_coherent(dev->udev,
- urb->transfer_buffer_length,
- dev->isoc_ctl.transfer_buffer[i],
- urb->transfer_dma);
- }
- usb_free_urb(urb);
- dev->isoc_ctl.urb[i] = NULL;
- }
- dev->isoc_ctl.transfer_buffer[i] = NULL;
- }
-
- kfree(dev->isoc_ctl.urb);
- kfree(dev->isoc_ctl.transfer_buffer);
-
- dev->isoc_ctl.urb = NULL;
- dev->isoc_ctl.transfer_buffer = NULL;
- dev->isoc_ctl.num_bufs = 0;
-}
-
-/*
- * Allocate URBs and start IRQ
- */
-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;
- struct urb *urb;
-
- /* De-allocates all pending stuff */
- tm6000_uninit_isoc(dev);
- /* Stop interrupt USB pipe */
- tm6000_ir_int_stop(dev);
-
- usb_set_interface(dev->udev,
- dev->isoc_in.bInterfaceNumber,
- dev->isoc_in.bAlternateSetting);
-
- /* Start interrupt USB pipe */
- tm6000_ir_int_start(dev);
-
- pipe = usb_rcvisocpipe(dev->udev,
- dev->isoc_in.endp->desc.bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK);
-
- size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
-
- if (size > dev->isoc_in.maxsize)
- size = dev->isoc_in.maxsize;
-
- dev->isoc_ctl.max_pkt_size = size;
-
- max_packets = TM6000_MAX_ISO_PACKETS;
- sb_size = max_packets * size;
-
- dev->isoc_ctl.num_bufs = num_bufs;
-
- dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
- if (!dev->isoc_ctl.urb) {
- tm6000_err("cannot alloc memory for usb buffers\n");
- return -ENOMEM;
- }
-
- dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
- if (!dev->isoc_ctl.transfer_buffer) {
- tm6000_err("cannot allocate memory for usbtransfer\n");
- kfree(dev->isoc_ctl.urb);
- return -ENOMEM;
- }
-
- dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets"
- " (%d bytes) of %d bytes each to handle %u size\n",
- max_packets, num_bufs, sb_size,
- dev->isoc_in.maxsize, size);
-
- /* allocate urbs and transfer buffers */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = usb_alloc_urb(max_packets, GFP_KERNEL);
- if (!urb) {
- tm6000_err("cannot alloc isoc_ctl.urb %i\n", i);
- tm6000_uninit_isoc(dev);
- usb_free_urb(urb);
- return -ENOMEM;
- }
- dev->isoc_ctl.urb[i] = urb;
-
- dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
- sb_size, GFP_KERNEL, &urb->transfer_dma);
- if (!dev->isoc_ctl.transfer_buffer[i]) {
- tm6000_err("unable to allocate %i bytes for transfer"
- " buffer %i%s\n",
- sb_size, i,
- in_interrupt() ? " while in int" : "");
- tm6000_uninit_isoc(dev);
- return -ENOMEM;
- }
- memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
-
- usb_fill_bulk_urb(urb, dev->udev, pipe,
- dev->isoc_ctl.transfer_buffer[i], sb_size,
- tm6000_irq_callback, dma_q);
- urb->interval = dev->isoc_in.endp->desc.bInterval;
- urb->number_of_packets = max_packets;
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-
- for (j = 0; j < max_packets; j++) {
- urb->iso_frame_desc[j].offset = size * j;
- urb->iso_frame_desc[j].length = size;
- }
- }
-
- return 0;
-}
-
-static int tm6000_start_thread(struct tm6000_core *dev)
-{
- struct tm6000_dmaqueue *dma_q = &dev->vidq;
- int i;
-
- dma_q->frame = 0;
- dma_q->ini_jiffies = jiffies;
-
- init_waitqueue_head(&dma_q->wq);
-
- /* submit urbs and enables IRQ */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
- if (rc) {
- tm6000_err("submit of urb %i failed (error=%i)\n", i,
- rc);
- tm6000_uninit_isoc(dev);
- return rc;
- }
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------
- * Videobuf operations
- * ------------------------------------------------------------------
- */
-
-static int
-buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
-{
- struct tm6000_fh *fh = vq->priv_data;
-
- *size = fh->fmt->depth * fh->width * fh->height >> 3;
- if (0 == *count)
- *count = TM6000_DEF_BUF;
-
- if (*count < TM6000_MIN_BUF)
- *count = TM6000_MIN_BUF;
-
- while (*size * *count > vid_limit * 1024 * 1024)
- (*count)--;
-
- return 0;
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf)
-{
- struct tm6000_fh *fh = vq->priv_data;
- struct tm6000_core *dev = fh->dev;
- unsigned long flags;
-
- if (in_interrupt())
- BUG();
-
- /* We used to wait for the buffer to finish here, but this didn't work
- because, as we were keeping the state as VIDEOBUF_QUEUED,
- videobuf_queue_cancel marked it as finished for us.
- (Also, it could wedge forever if the hardware was misconfigured.)
-
- This should be safe; by the time we get here, the buffer isn't
- queued anymore. If we ever start marking the buffers as
- VIDEOBUF_ACTIVE, it won't be, though.
- */
- spin_lock_irqsave(&dev->slock, flags);
- if (dev->isoc_ctl.buf == buf)
- dev->isoc_ctl.buf = NULL;
- spin_unlock_irqrestore(&dev->slock, flags);
-
- videobuf_vmalloc_free(&buf->vb);
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
-}
-
-static int
-buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct tm6000_fh *fh = vq->priv_data;
- struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
- struct tm6000_core *dev = fh->dev;
- int rc = 0;
-
- BUG_ON(NULL == fh->fmt);
-
-
- /* FIXME: It assumes depth=2 */
- /* The only currently supported format is 16 bits/pixel */
- buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3;
- if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
- return -EINVAL;
-
- if (buf->fmt != fh->fmt ||
- buf->vb.width != fh->width ||
- buf->vb.height != fh->height ||
- buf->vb.field != field) {
- buf->fmt = fh->fmt;
- buf->vb.width = fh->width;
- buf->vb.height = fh->height;
- buf->vb.field = field;
- buf->vb.state = VIDEOBUF_NEEDS_INIT;
- }
-
- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
- rc = videobuf_iolock(vq, &buf->vb, NULL);
- if (rc != 0)
- goto fail;
- }
-
- if (!dev->isoc_ctl.num_bufs) {
- rc = tm6000_prepare_isoc(dev);
- if (rc < 0)
- goto fail;
-
- rc = tm6000_start_thread(dev);
- if (rc < 0)
- goto fail;
-
- }
-
- buf->vb.state = VIDEOBUF_PREPARED;
- return 0;
-
-fail:
- free_buffer(vq, buf);
- return rc;
-}
-
-static void
-buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
- struct tm6000_fh *fh = vq->priv_data;
- struct tm6000_core *dev = fh->dev;
- struct tm6000_dmaqueue *vidq = &dev->vidq;
-
- buf->vb.state = VIDEOBUF_QUEUED;
- list_add_tail(&buf->vb.queue, &vidq->active);
-}
-
-static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- struct tm6000_buffer *buf = container_of(vb, struct tm6000_buffer, vb);
-
- free_buffer(vq, buf);
-}
-
-static struct videobuf_queue_ops tm6000_video_qops = {
- .buf_setup = buffer_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .buf_release = buffer_release,
-};
-
-/* ------------------------------------------------------------------
- * IOCTL handling
- * ------------------------------------------------------------------
- */
-
-static bool is_res_read(struct tm6000_core *dev, struct tm6000_fh *fh)
-{
- /* Is the current fh handling it? if so, that's OK */
- if (dev->resources == fh && dev->is_res_read)
- return true;
-
- return false;
-}
-
-static bool is_res_streaming(struct tm6000_core *dev, struct tm6000_fh *fh)
-{
- /* Is the current fh handling it? if so, that's OK */
- if (dev->resources == fh)
- return true;
-
- return false;
-}
-
-static bool res_get(struct tm6000_core *dev, struct tm6000_fh *fh,
- bool is_res_read)
-{
- /* Is the current fh handling it? if so, that's OK */
- if (dev->resources == fh && dev->is_res_read == is_res_read)
- return true;
-
- /* is it free? */
- if (dev->resources)
- return false;
-
- /* grab it */
- dev->resources = fh;
- dev->is_res_read = is_res_read;
- dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n");
- return true;
-}
-
-static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh)
-{
- /* Is the current fh handling it? if so, that's OK */
- if (dev->resources != fh)
- return;
-
- dev->resources = NULL;
- dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n");
-}
-
-/* ------------------------------------------------------------------
- * IOCTL vidioc handling
- * ------------------------------------------------------------------
- */
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
-
- strlcpy(cap->driver, "tm6000", sizeof(cap->driver));
- strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE;
-
- if (dev->tuner_type != TUNER_ABSENT)
- cap->capabilities |= V4L2_CAP_TUNER;
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- if (unlikely(f->index >= ARRAY_SIZE(format)))
- return -EINVAL;
-
- strlcpy(f->description, format[f->index].name, sizeof(f->description));
- f->pixelformat = format[f->index].fourcc;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct tm6000_fh *fh = priv;
-
- f->fmt.pix.width = fh->width;
- f->fmt.pix.height = fh->height;
- f->fmt.pix.field = fh->vb_vidq.field;
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-static struct tm6000_fmt *format_by_fourcc(unsigned int fourcc)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(format); i++)
- if (format[i].fourcc == fourcc)
- return format+i;
- return NULL;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev;
- struct tm6000_fmt *fmt;
- enum v4l2_field field;
-
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (NULL == fmt) {
- dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)"
- " invalid.\n", f->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY)
- field = V4L2_FIELD_SEQ_TB;
- else if (V4L2_FIELD_INTERLACED != field) {
- dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n");
- return -EINVAL;
- }
-
- tm6000_get_std_res(dev);
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
-
- f->fmt.pix.width &= ~0x01;
-
- f->fmt.pix.field = field;
-
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
-
- return 0;
-}
-
-/*FIXME: This seems to be generic enough to be at videodev2 */
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
- int ret = vidioc_try_fmt_vid_cap(file, fh, f);
- if (ret < 0)
- return ret;
-
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- fh->width = f->fmt.pix.width;
- fh->height = f->fmt.pix.height;
- fh->vb_vidq.field = f->fmt.pix.field;
- fh->type = f->type;
-
- dev->fourcc = f->fmt.pix.pixelformat;
-
- tm6000_set_fourcc_format(dev);
-
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct tm6000_fh *fh = priv;
-
- return videobuf_reqbufs(&fh->vb_vidq, p);
-}
-
-static int vidioc_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct tm6000_fh *fh = priv;
-
- return videobuf_querybuf(&fh->vb_vidq, p);
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct tm6000_fh *fh = priv;
-
- return videobuf_qbuf(&fh->vb_vidq, p);
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct tm6000_fh *fh = priv;
-
- return videobuf_dqbuf(&fh->vb_vidq, p,
- file->f_flags & O_NONBLOCK);
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (i != fh->type)
- return -EINVAL;
-
- if (!res_get(dev, fh, false))
- return -EBUSY;
- return videobuf_streamon(&fh->vb_vidq);
-}
-
-static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (i != fh->type)
- return -EINVAL;
-
- videobuf_streamoff(&fh->vb_vidq);
- res_free(dev, fh);
-
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
-{
- int rc = 0;
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- dev->norm = *norm;
- rc = tm6000_init_analog_mode(dev);
-
- fh->width = dev->width;
- fh->height = dev->height;
-
- if (rc < 0)
- return rc;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
-
- return 0;
-}
-
-static const char *iname[] = {
- [TM6000_INPUT_TV] = "Television",
- [TM6000_INPUT_COMPOSITE1] = "Composite 1",
- [TM6000_INPUT_COMPOSITE2] = "Composite 2",
- [TM6000_INPUT_SVIDEO] = "S-Video",
-};
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
- unsigned int n;
-
- n = i->index;
- if (n >= 3)
- return -EINVAL;
-
- if (!dev->vinput[n].type)
- return -EINVAL;
-
- i->index = n;
-
- if (dev->vinput[n].type == TM6000_INPUT_TV)
- i->type = V4L2_INPUT_TYPE_TUNER;
- else
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- strcpy(i->name, iname[dev->vinput[n].type]);
-
- i->std = TM6000_STD;
-
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- *i = dev->input;
-
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
- int rc = 0;
-
- if (i >= 3)
- return -EINVAL;
- if (!dev->vinput[i].type)
- return -EINVAL;
-
- dev->input = i;
-
- rc = vidioc_s_std(file, priv, &dev->vfd->current_norm);
-
- return rc;
-}
-
-/* --- controls ---------------------------------------------- */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
- if (qc->id && qc->id == tm6000_qctrl[i].id) {
- memcpy(qc, &(tm6000_qctrl[i]),
- sizeof(*qc));
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
- int val;
-
- /* FIXME: Probably, those won't work! Maybe we need shadow regs */
- switch (ctrl->id) {
- case V4L2_CID_CONTRAST:
- val = tm6000_get_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, 0);
- break;
- case V4L2_CID_BRIGHTNESS:
- val = tm6000_get_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, 0);
- return 0;
- case V4L2_CID_SATURATION:
- val = tm6000_get_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, 0);
- return 0;
- 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;
- }
-
- if (val < 0)
- return val;
-
- ctrl->value = val;
-
- return 0;
-}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
- u8 val = ctrl->value;
-
- switch (ctrl->id) {
- case V4L2_CID_CONTRAST:
- tm6000_set_reg(dev, TM6010_REQ07_R08_LUMA_CONTRAST_ADJ, val);
- return 0;
- case V4L2_CID_BRIGHTNESS:
- tm6000_set_reg(dev, TM6010_REQ07_R09_LUMA_BRIGHTNESS_ADJ, val);
- return 0;
- case V4L2_CID_SATURATION:
- tm6000_set_reg(dev, TM6010_REQ07_R0A_CHROMA_SATURATION_ADJ, val);
- return 0;
- 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;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- strcpy(t->name, "Television");
- t->type = V4L2_TUNER_ANALOG_TV;
- t->capability = V4L2_TUNER_CAP_NORM;
- t->rangehigh = 0xffffffffUL;
- t->rxsubchans = V4L2_TUNER_SUB_STEREO;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
-
- t->audmode = dev->amode;
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *t)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (UNSET == dev->tuner_type)
- return -EINVAL;
- if (0 != t->index)
- return -EINVAL;
-
- dev->amode = t->audmode;
- dprintk(dev, 3, "audio mode: %x\n", t->audmode);
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (unlikely(UNSET == dev->tuner_type))
- return -EINVAL;
-
- 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);
-
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- 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);
-
- 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 |
- V4L2_CAP_AUDIO |
- V4L2_CAP_RADIO |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING;
-
- 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;
- t->rxsubchans = V4L2_TUNER_SUB_STEREO;
-
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
-
- 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)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (i->index != 0)
- return -EINVAL;
-
- if (!dev->rinput.type)
- 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)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (dev->input != 5)
- return -EINVAL;
-
- *i = dev->input - 5;
-
- 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)
-{
- struct tm6000_fh *fh = priv;
- struct tm6000_core *dev = fh->dev;
-
- if (i)
- return -EINVAL;
-
- if (!dev->rinput.type)
- return -EINVAL;
-
- dev->input = i + 5;
-
- 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
- ------------------------------------------------------------------*/
-
-static int tm6000_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct tm6000_core *dev = video_drvdata(file);
- struct tm6000_fh *fh;
- enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- int i, rc;
- int radio = 0;
-
- 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++;
-
- dprintk(dev, V4L2_DEBUG_OPEN, "open dev=%s type=%s users=%d\n",
- video_device_node_name(vdev), v4l2_type_names[type],
- dev->users);
-
- /* allocate + initialize per filehandle data */
- fh = kzalloc(sizeof(*fh), GFP_KERNEL);
- if (NULL == fh) {
- dev->users--;
- return -ENOMEM;
- }
-
- file->private_data = fh;
- fh->dev = dev;
- fh->radio = radio;
- dev->radio = radio;
- fh->type = type;
- dev->fourcc = format[0].fourcc;
-
- fh->fmt = format_by_fourcc(dev->fourcc);
-
- tm6000_get_std_res(dev);
-
- fh->width = dev->width;
- fh->height = dev->height;
-
- dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, "
- "dev->vidq=0x%08lx\n",
- (unsigned long)fh, (unsigned long)dev,
- (unsigned long)&dev->vidq);
- dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
- "queued=%d\n", list_empty(&dev->vidq.queued));
- dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty "
- "active=%d\n", list_empty(&dev->vidq.active));
-
- /* initialize hardware on analog mode */
- rc = tm6000_init_analog_mode(dev);
- if (rc < 0)
- return rc;
-
- if (dev->mode != TM6000_MODE_ANALOG) {
- /* Put all controls at a sane state */
- for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++)
- qctl_regs[i] = tm6000_qctrl[i].default_value;
-
- dev->mode = TM6000_MODE_ANALOG;
- }
-
- if (!fh->radio) {
- videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
- NULL, &dev->slock,
- fh->type,
- V4L2_FIELD_INTERLACED,
- sizeof(struct tm6000_buffer), fh, &dev->lock);
- } else {
- dprintk(dev, V4L2_DEBUG_OPEN, "video_open: setting radio device\n");
- dev->input = 5;
- tm6000_set_audio_rinput(dev);
- v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio);
- tm6000_prepare_isoc(dev);
- tm6000_start_thread(dev);
- }
-
- return 0;
-}
-
-static ssize_t
-tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos)
-{
- struct tm6000_fh *fh = file->private_data;
-
- if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (!res_get(fh->dev, fh, true))
- return -EBUSY;
-
- return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0,
- file->f_flags & O_NONBLOCK);
- }
- return 0;
-}
-
-static unsigned int
-tm6000_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct tm6000_fh *fh = file->private_data;
- struct tm6000_buffer *buf;
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
- return POLLERR;
-
- if (!!is_res_streaming(fh->dev, fh))
- return POLLERR;
-
- if (!is_res_read(fh->dev, fh)) {
- /* streaming capture */
- if (list_empty(&fh->vb_vidq.stream))
- return POLLERR;
- buf = list_entry(fh->vb_vidq.stream.next, struct tm6000_buffer, vb.stream);
- } else {
- /* read() capture */
- return videobuf_poll_stream(file, &fh->vb_vidq, wait);
- }
- poll_wait(file, &buf->vb.done, wait);
- if (buf->vb.state == VIDEOBUF_DONE ||
- buf->vb.state == VIDEOBUF_ERROR)
- return POLLIN | POLLRDNORM;
- return 0;
-}
-
-static int tm6000_release(struct file *file)
-{
- struct tm6000_fh *fh = file->private_data;
- struct tm6000_core *dev = fh->dev;
- struct video_device *vdev = video_devdata(file);
-
- dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (dev=%s, users=%d)\n",
- video_device_node_name(vdev), dev->users);
-
- dev->users--;
-
- res_free(dev, fh);
-
- if (!dev->users) {
- tm6000_uninit_isoc(dev);
-
- /* Stop interrupt USB pipe */
- tm6000_ir_int_stop(dev);
-
- usb_reset_configuration(dev->udev);
-
- if (dev->int_in.endp)
- usb_set_interface(dev->udev,
- dev->isoc_in.bInterfaceNumber, 2);
- else
- usb_set_interface(dev->udev,
- dev->isoc_in.bInterfaceNumber, 0);
-
- /* Start interrupt USB pipe */
- tm6000_ir_int_start(dev);
-
- if (!fh->radio)
- videobuf_mmap_free(&fh->vb_vidq);
- }
-
- kfree(fh);
-
- return 0;
-}
-
-static int tm6000_mmap(struct file *file, struct vm_area_struct * vma)
-{
- struct tm6000_fh *fh = file->private_data;
-
- return videobuf_mmap_mapper(&fh->vb_vidq, vma);
-}
-
-static struct v4l2_file_operations tm6000_fops = {
- .owner = THIS_MODULE,
- .open = tm6000_open,
- .release = tm6000_release,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .read = tm6000_read,
- .poll = tm6000_poll,
- .mmap = tm6000_mmap,
-};
-
-static const struct v4l2_ioctl_ops video_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
-};
-
-static struct video_device tm6000_template = {
- .name = "tm6000",
- .fops = &tm6000_fops,
- .ioctl_ops = &video_ioctl_ops,
- .release = video_device_release,
- .tvnorms = TM6000_STD,
- .current_norm = V4L2_STD_NTSC_M,
-};
-
-static const struct v4l2_file_operations radio_fops = {
- .owner = THIS_MODULE,
- .open = tm6000_open,
- .release = tm6000_release,
- .unlocked_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,
-};
-
-static struct video_device tm6000_radio_template = {
- .name = "tm6000",
- .fops = &radio_fops,
- .ioctl_ops = &radio_ioctl_ops,
-};
-
-/* -----------------------------------------------------------------
- * Initialization and module stuff
- * ------------------------------------------------------------------
- */
-
-static struct video_device *vdev_init(struct tm6000_core *dev,
- const struct video_device
- *template, const char *type_name)
-{
- struct video_device *vfd;
-
- vfd = video_device_alloc();
- if (NULL == vfd)
- return NULL;
-
- *vfd = *template;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->release = video_device_release;
- vfd->debug = tm6000_debug;
- vfd->lock = &dev->lock;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
-
- snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
-
- video_set_drvdata(vfd, dev);
- return vfd;
-}
-
-int tm6000_v4l2_register(struct tm6000_core *dev)
-{
- int ret = -1;
-
- dev->vfd = vdev_init(dev, &tm6000_template, "video");
-
- if (!dev->vfd) {
- printk(KERN_INFO "%s: can't register video device\n",
- dev->name);
- return -ENOMEM;
- }
-
- /* init video dma queues */
- INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
-
- ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr);
-
- if (ret < 0) {
- printk(KERN_INFO "%s: can't register video device\n",
- dev->name);
- return ret;
- }
-
- printk(KERN_INFO "%s: registered device %s\n",
- dev->name, video_device_node_name(dev->vfd));
-
- if (dev->caps.has_radio) {
- 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;
-}
-
-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;
-}
-
-int tm6000_v4l2_exit(void)
-{
- return 0;
-}
-
-module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr, "Allow changing video device number");
-
-module_param_named(debug, tm6000_debug, int, 0444);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-module_param(vid_limit, int, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
deleted file mode 100644
index 321b3153df87..000000000000
--- a/drivers/media/video/tvaudio.c
+++ /dev/null
@@ -1,2118 +0,0 @@
-/*
- * Driver for simple i2c audio chips.
- *
- * Copyright (c) 2000 Gerd Knorr
- * based on code by:
- * Eric Sandeen (eric_sandeen@bigfoot.com)
- * Steve VanDeBogart (vandebo@uclink.berkeley.edu)
- * Greg Alexander (galexand@acm.org)
- *
- * Copyright(c) 2005-2008 Mauro Carvalho Chehab
- * - Some cleanups, code fixes, etc
- * - Convert it to V4L2 API
- *
- * This code is placed under the terms of the GNU General Public License
- *
- * OPTIONS:
- * debug - set to 1 if you'd like to see debug messages
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-
-#include <media/tvaudio.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-
-#include <media/i2c-addr.h>
-
-/* ---------------------------------------------------------------------- */
-/* insmod args */
-
-static int debug; /* insmod parameter */
-module_param(debug, int, 0644);
-
-MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips");
-MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr");
-MODULE_LICENSE("GPL");
-
-#define UNSET (-1U)
-
-/* ---------------------------------------------------------------------- */
-/* our structs */
-
-#define MAXREGS 256
-
-struct CHIPSTATE;
-typedef int (*getvalue)(int);
-typedef int (*checkit)(struct CHIPSTATE*);
-typedef int (*initialize)(struct CHIPSTATE*);
-typedef int (*getrxsubchans)(struct CHIPSTATE *);
-typedef void (*setaudmode)(struct CHIPSTATE*, int mode);
-
-/* i2c command */
-typedef struct AUDIOCMD {
- int count; /* # of bytes to send */
- unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */
-} audiocmd;
-
-/* chip description */
-struct CHIPDESC {
- char *name; /* chip name */
- int addr_lo, addr_hi; /* i2c address range */
- int registers; /* # of registers */
-
- int *insmodopt;
- checkit checkit;
- initialize initialize;
- int flags;
-#define CHIP_HAS_VOLUME 1
-#define CHIP_HAS_BASSTREBLE 2
-#define CHIP_HAS_INPUTSEL 4
-#define CHIP_NEED_CHECKMODE 8
-
- /* various i2c command sequences */
- audiocmd init;
-
- /* which register has which value */
- int leftreg,rightreg,treblereg,bassreg;
-
- /* initialize with (defaults to 65535/65535/32768/32768 */
- int leftinit,rightinit,trebleinit,bassinit;
-
- /* functions to convert the values (v4l -> chip) */
- getvalue volfunc,treblefunc,bassfunc;
-
- /* get/set mode */
- getrxsubchans getrxsubchans;
- setaudmode setaudmode;
-
- /* input switch register + values for v4l inputs */
- int inputreg;
- int inputmap[4];
- int inputmute;
- int inputmask;
-};
-
-/* current state of the chip */
-struct CHIPSTATE {
- struct v4l2_subdev sd;
-
- /* chip-specific description - should point to
- an entry at CHIPDESC table */
- struct CHIPDESC *desc;
-
- /* shadow register set */
- audiocmd shadow;
-
- /* current settings */
- __u16 left, right, treble, bass, muted;
- int prevmode;
- int radio;
- int input;
-
- /* thread */
- struct task_struct *thread;
- struct timer_list wt;
- int audmode;
-};
-
-static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd)
-{
- return container_of(sd, struct CHIPSTATE, sd);
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* i2c I/O functions */
-
-static int chip_write(struct CHIPSTATE *chip, int subaddr, int val)
-{
- struct v4l2_subdev *sd = &chip->sd;
- struct i2c_client *c = v4l2_get_subdevdata(sd);
- unsigned char buffer[2];
-
- if (subaddr < 0) {
- v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val);
- chip->shadow.bytes[1] = val;
- buffer[0] = val;
- if (1 != i2c_master_send(c, buffer, 1)) {
- v4l2_warn(sd, "I/O error (write 0x%x)\n", val);
- return -1;
- }
- } else {
- if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l2_info(sd,
- "Tried to access a non-existent register: %d\n",
- subaddr);
- return -EINVAL;
- }
-
- v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n",
- subaddr, val);
- chip->shadow.bytes[subaddr+1] = val;
- buffer[0] = subaddr;
- buffer[1] = val;
- if (2 != i2c_master_send(c, buffer, 2)) {
- v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n",
- subaddr, val);
- return -1;
- }
- }
- return 0;
-}
-
-static int chip_write_masked(struct CHIPSTATE *chip,
- int subaddr, int val, int mask)
-{
- struct v4l2_subdev *sd = &chip->sd;
-
- if (mask != 0) {
- if (subaddr < 0) {
- val = (chip->shadow.bytes[1] & ~mask) | (val & mask);
- } else {
- if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l2_info(sd,
- "Tried to access a non-existent register: %d\n",
- subaddr);
- return -EINVAL;
- }
-
- val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask);
- }
- }
- return chip_write(chip, subaddr, val);
-}
-
-static int chip_read(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- struct i2c_client *c = v4l2_get_subdevdata(sd);
- unsigned char buffer;
-
- if (1 != i2c_master_recv(c, &buffer, 1)) {
- v4l2_warn(sd, "I/O error (read)\n");
- return -1;
- }
- v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer);
- return buffer;
-}
-
-static int chip_read2(struct CHIPSTATE *chip, int subaddr)
-{
- struct v4l2_subdev *sd = &chip->sd;
- struct i2c_client *c = v4l2_get_subdevdata(sd);
- unsigned char write[1];
- unsigned char read[1];
- struct i2c_msg msgs[2] = {
- { c->addr, 0, 1, write },
- { c->addr, I2C_M_RD, 1, read }
- };
-
- write[0] = subaddr;
-
- if (2 != i2c_transfer(c->adapter, msgs, 2)) {
- v4l2_warn(sd, "I/O error (read2)\n");
- return -1;
- }
- v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n",
- subaddr, read[0]);
- return read[0];
-}
-
-static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
-{
- struct v4l2_subdev *sd = &chip->sd;
- struct i2c_client *c = v4l2_get_subdevdata(sd);
- int i;
-
- if (0 == cmd->count)
- return 0;
-
- if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) {
- v4l2_info(sd,
- "Tried to access a non-existent register range: %d to %d\n",
- cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1);
- return -EINVAL;
- }
-
- /* FIXME: it seems that the shadow bytes are wrong bellow !*/
-
- /* update our shadow register set; print bytes if (debug > 0) */
- v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:",
- name, cmd->bytes[0]);
- for (i = 1; i < cmd->count; i++) {
- if (debug)
- printk(KERN_CONT " 0x%x", cmd->bytes[i]);
- chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i];
- }
- if (debug)
- printk(KERN_CONT "\n");
-
- /* send data to the chip */
- if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) {
- v4l2_warn(sd, "I/O error (%s)\n", name);
- return -1;
- }
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* kernel thread for doing i2c stuff asyncronly
- * right now it is used only to check the audio mode (mono/stereo/whatever)
- * some time after switching to another TV channel, then turn on stereo
- * if available, ...
- */
-
-static void chip_thread_wake(unsigned long data)
-{
- struct CHIPSTATE *chip = (struct CHIPSTATE*)data;
- wake_up_process(chip->thread);
-}
-
-static int chip_thread(void *data)
-{
- struct CHIPSTATE *chip = data;
- struct CHIPDESC *desc = chip->desc;
- struct v4l2_subdev *sd = &chip->sd;
- int mode, selected;
-
- v4l2_dbg(1, debug, sd, "thread started\n");
- set_freezable();
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (!kthread_should_stop())
- schedule();
- set_current_state(TASK_RUNNING);
- try_to_freeze();
- if (kthread_should_stop())
- break;
- v4l2_dbg(1, debug, sd, "thread wakeup\n");
-
- /* don't do anything for radio */
- if (chip->radio)
- continue;
-
- /* have a look what's going on */
- mode = desc->getrxsubchans(chip);
- if (mode == chip->prevmode)
- continue;
-
- /* chip detected a new audio mode - set it */
- v4l2_dbg(1, debug, sd, "thread checkmode\n");
-
- chip->prevmode = mode;
-
- selected = V4L2_TUNER_MODE_MONO;
- switch (chip->audmode) {
- case V4L2_TUNER_MODE_MONO:
- if (mode & V4L2_TUNER_SUB_LANG1)
- selected = V4L2_TUNER_MODE_LANG1;
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- if (mode & V4L2_TUNER_SUB_LANG1)
- selected = V4L2_TUNER_MODE_LANG1;
- else if (mode & V4L2_TUNER_SUB_STEREO)
- selected = V4L2_TUNER_MODE_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG2:
- if (mode & V4L2_TUNER_SUB_LANG2)
- selected = V4L2_TUNER_MODE_LANG2;
- else if (mode & V4L2_TUNER_SUB_STEREO)
- selected = V4L2_TUNER_MODE_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- if (mode & V4L2_TUNER_SUB_LANG2)
- selected = V4L2_TUNER_MODE_LANG1_LANG2;
- else if (mode & V4L2_TUNER_SUB_STEREO)
- selected = V4L2_TUNER_MODE_STEREO;
- }
- desc->setaudmode(chip, selected);
-
- /* schedule next check */
- mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
- }
-
- v4l2_dbg(1, debug, sd, "thread exiting\n");
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for tda9840 */
-
-#define TDA9840_SW 0x00
-#define TDA9840_LVADJ 0x02
-#define TDA9840_STADJ 0x03
-#define TDA9840_TEST 0x04
-
-#define TDA9840_MONO 0x10
-#define TDA9840_STEREO 0x2a
-#define TDA9840_DUALA 0x12
-#define TDA9840_DUALB 0x1e
-#define TDA9840_DUALAB 0x1a
-#define TDA9840_DUALBA 0x16
-#define TDA9840_EXTERNAL 0x7a
-
-#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */
-#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */
-#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */
-
-#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */
-#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */
-
-static int tda9840_getrxsubchans(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int val, mode;
-
- val = chip_read(chip);
- mode = V4L2_TUNER_SUB_MONO;
- if (val & TDA9840_DS_DUAL)
- mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- if (val & TDA9840_ST_STEREO)
- mode = V4L2_TUNER_SUB_STEREO;
-
- v4l2_dbg(1, debug, sd,
- "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n",
- val, mode);
- return mode;
-}
-
-static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- int update = 1;
- int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e;
-
- switch (mode) {
- case V4L2_TUNER_MODE_MONO:
- t |= TDA9840_MONO;
- break;
- case V4L2_TUNER_MODE_STEREO:
- t |= TDA9840_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1:
- t |= TDA9840_DUALA;
- break;
- case V4L2_TUNER_MODE_LANG2:
- t |= TDA9840_DUALB;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- t |= TDA9840_DUALAB;
- break;
- default:
- update = 0;
- }
-
- if (update)
- chip_write(chip, TDA9840_SW, t);
-}
-
-static int tda9840_checkit(struct CHIPSTATE *chip)
-{
- int rc;
- rc = chip_read(chip);
- /* lower 5 bits should be 0 */
- return ((rc & 0x1f) == 0) ? 1 : 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for tda985x */
-
-/* subaddresses for TDA9855 */
-#define TDA9855_VR 0x00 /* Volume, right */
-#define TDA9855_VL 0x01 /* Volume, left */
-#define TDA9855_BA 0x02 /* Bass */
-#define TDA9855_TR 0x03 /* Treble */
-#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */
-
-/* subaddresses for TDA9850 */
-#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */
-
-/* subaddesses for both chips */
-#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */
-#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */
-#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */
-#define TDA985x_A1 0x08 /* Alignment 1 for both chips */
-#define TDA985x_A2 0x09 /* Alignment 2 for both chips */
-#define TDA985x_A3 0x0a /* Alignment 3 for both chips */
-
-/* Masks for bits in TDA9855 subaddresses */
-/* 0x00 - VR in TDA9855 */
-/* 0x01 - VL in TDA9855 */
-/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f)
- * in 1dB steps - mute is 0x27 */
-
-
-/* 0x02 - BA in TDA9855 */
-/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19)
- * in .5dB steps - 0 is 0x0E */
-
-
-/* 0x03 - TR in TDA9855 */
-/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb)
- * in 3dB steps - 0 is 0x7 */
-
-/* Masks for bits in both chips' subaddresses */
-/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */
-/* Unique to TDA9855: */
-/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf)
- * in 3dB steps - mute is 0x0 */
-
-/* Unique to TDA9850: */
-/* lower 4 bits control stereo noise threshold, over which stereo turns off
- * set to values of 0x00 through 0x0f for Ster1 through Ster16 */
-
-
-/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/
-/* Unique to TDA9855: */
-#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */
-#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */
-#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */
-#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */
- /* Bits 0 to 3 select various combinations
- * of line in and line out, only the
- * interesting ones are defined */
-#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */
-#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */
-
-/* Unique to TDA9850: */
-/* lower 4 bits contol SAP noise threshold, over which SAP turns off
- * set to values of 0x00 through 0x0f for SAP1 through SAP16 */
-
-
-/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */
-/* Common to TDA9855 and TDA9850: */
-#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */
-#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */
-#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */
-#define TDA985x_MONO 0 /* Forces Mono output */
-#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */
-
-/* Unique to TDA9855: */
-#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */
-#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/
-#define TDA9855_LINEAR 0 /* Linear Stereo */
-#define TDA9855_PSEUDO 1 /* Pseudo Stereo */
-#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */
-#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */
-#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/
-
-/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF)
- * in .5dB steps - 0dB is 0x7 */
-
-/* 0x08, 0x09 - A1 and A2 (read/write) */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 5 bites are wideband and spectral expander alignment
- * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */
-#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */
-#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */
-#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/
-
-/* 0x0a - A3 */
-/* Common to both TDA9855 and TDA9850: */
-/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1),
- * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */
-#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */
-
-static int tda9855_volume(int val) { return val/0x2e8+0x27; }
-static int tda9855_bass(int val) { return val/0xccc+0x06; }
-static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; }
-
-static int tda985x_getrxsubchans(struct CHIPSTATE *chip)
-{
- int mode, val;
-
- /* Add mono mode regardless of SAP and stereo */
- /* Allows forced mono */
- mode = V4L2_TUNER_SUB_MONO;
- val = chip_read(chip);
- if (val & TDA985x_STP)
- mode = V4L2_TUNER_SUB_STEREO;
- if (val & TDA985x_SAPP)
- mode |= V4L2_TUNER_SUB_SAP;
- return mode;
-}
-
-static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- int update = 1;
- int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f;
-
- switch (mode) {
- case V4L2_TUNER_MODE_MONO:
- c6 |= TDA985x_MONO;
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- c6 |= TDA985x_STEREO;
- break;
- case V4L2_TUNER_MODE_SAP:
- c6 |= TDA985x_SAP;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- c6 |= TDA985x_MONOSAP;
- break;
- default:
- update = 0;
- }
- if (update)
- chip_write(chip,TDA985x_C6,c6);
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for tda9873h */
-
-/* Subaddresses for TDA9873H */
-
-#define TDA9873_SW 0x00 /* Switching */
-#define TDA9873_AD 0x01 /* Adjust */
-#define TDA9873_PT 0x02 /* Port */
-
-/* Subaddress 0x00: Switching Data
- * B7..B0:
- *
- * B1, B0: Input source selection
- * 0, 0 internal
- * 1, 0 external stereo
- * 0, 1 external mono
- */
-#define TDA9873_INP_MASK 3
-#define TDA9873_INTERNAL 0
-#define TDA9873_EXT_STEREO 2
-#define TDA9873_EXT_MONO 1
-
-/* B3, B2: output signal select
- * B4 : transmission mode
- * 0, 0, 1 Mono
- * 1, 0, 0 Stereo
- * 1, 1, 1 Stereo (reversed channel)
- * 0, 0, 0 Dual AB
- * 0, 0, 1 Dual AA
- * 0, 1, 0 Dual BB
- * 0, 1, 1 Dual BA
- */
-
-#define TDA9873_TR_MASK (7 << 2)
-#define TDA9873_TR_MONO 4
-#define TDA9873_TR_STEREO 1 << 4
-#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2))
-#define TDA9873_TR_DUALA 1 << 2
-#define TDA9873_TR_DUALB 1 << 3
-#define TDA9873_TR_DUALAB 0
-
-/* output level controls
- * B5: output level switch (0 = reduced gain, 1 = normal gain)
- * B6: mute (1 = muted)
- * B7: auto-mute (1 = auto-mute enabled)
- */
-
-#define TDA9873_GAIN_NORMAL 1 << 5
-#define TDA9873_MUTE 1 << 6
-#define TDA9873_AUTOMUTE 1 << 7
-
-/* Subaddress 0x01: Adjust/standard */
-
-/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB)
- * Recommended value is +0 dB
- */
-
-#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */
-
-/* Bits C6..C4 control FM stantard
- * C6, C5, C4
- * 0, 0, 0 B/G (PAL FM)
- * 0, 0, 1 M
- * 0, 1, 0 D/K(1)
- * 0, 1, 1 D/K(2)
- * 1, 0, 0 D/K(3)
- * 1, 0, 1 I
- */
-#define TDA9873_BG 0
-#define TDA9873_M 1
-#define TDA9873_DK1 2
-#define TDA9873_DK2 3
-#define TDA9873_DK3 4
-#define TDA9873_I 5
-
-/* C7 controls identification response time (1=fast/0=normal)
- */
-#define TDA9873_IDR_NORM 0
-#define TDA9873_IDR_FAST 1 << 7
-
-
-/* Subaddress 0x02: Port data */
-
-/* E1, E0 free programmable ports P1/P2
- 0, 0 both ports low
- 0, 1 P1 high
- 1, 0 P2 high
- 1, 1 both ports high
-*/
-
-#define TDA9873_PORTS 3
-
-/* E2: test port */
-#define TDA9873_TST_PORT 1 << 2
-
-/* E5..E3 control mono output channel (together with transmission mode bit B4)
- *
- * E5 E4 E3 B4 OUTM
- * 0 0 0 0 mono
- * 0 0 1 0 DUAL B
- * 0 1 0 1 mono (from stereo decoder)
- */
-#define TDA9873_MOUT_MONO 0
-#define TDA9873_MOUT_FMONO 0
-#define TDA9873_MOUT_DUALA 0
-#define TDA9873_MOUT_DUALB 1 << 3
-#define TDA9873_MOUT_ST 1 << 4
-#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3))
-#define TDA9873_MOUT_EXTL 1 << 5
-#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3))
-#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4))
-#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3))
-
-/* Status bits: (chip read) */
-#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */
-#define TDA9873_STEREO 2 /* Stereo sound is identified */
-#define TDA9873_DUAL 4 /* Dual sound is identified */
-
-static int tda9873_getrxsubchans(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int val,mode;
-
- val = chip_read(chip);
- mode = V4L2_TUNER_SUB_MONO;
- if (val & TDA9873_STEREO)
- mode = V4L2_TUNER_SUB_STEREO;
- if (val & TDA9873_DUAL)
- mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- v4l2_dbg(1, debug, sd,
- "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n",
- val, mode);
- return mode;
-}
-
-static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK;
- /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */
-
- if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) {
- v4l2_dbg(1, debug, sd,
- "tda9873_setaudmode(): external input\n");
- return;
- }
-
- v4l2_dbg(1, debug, sd,
- "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n",
- TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]);
- v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n",
- sw_data);
-
- switch (mode) {
- case V4L2_TUNER_MODE_MONO:
- sw_data |= TDA9873_TR_MONO;
- break;
- case V4L2_TUNER_MODE_STEREO:
- sw_data |= TDA9873_TR_STEREO;
- break;
- case V4L2_TUNER_MODE_LANG1:
- sw_data |= TDA9873_TR_DUALA;
- break;
- case V4L2_TUNER_MODE_LANG2:
- sw_data |= TDA9873_TR_DUALB;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- sw_data |= TDA9873_TR_DUALAB;
- break;
- default:
- return;
- }
-
- chip_write(chip, TDA9873_SW, sw_data);
- v4l2_dbg(1, debug, sd,
- "tda9873_setaudmode(): req. mode %d; chip_write: %d\n",
- mode, sw_data);
-}
-
-static int tda9873_checkit(struct CHIPSTATE *chip)
-{
- int rc;
-
- if (-1 == (rc = chip_read2(chip,254)))
- return 0;
- return (rc & ~0x1f) == 0x80;
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* audio chip description - defines+functions for tda9874h and tda9874a */
-/* Dariusz Kowalewski <darekk@automex.pl> */
-
-/* Subaddresses for TDA9874H and TDA9874A (slave rx) */
-#define TDA9874A_AGCGR 0x00 /* AGC gain */
-#define TDA9874A_GCONR 0x01 /* general config */
-#define TDA9874A_MSR 0x02 /* monitor select */
-#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */
-#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */
-#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */
-#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */
-#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */
-#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */
-#define TDA9874A_DCR 0x09 /* demodulator config */
-#define TDA9874A_FMER 0x0a /* FM de-emphasis */
-#define TDA9874A_FMMR 0x0b /* FM dematrix */
-#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */
-#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */
-#define TDA9874A_NCONR 0x0e /* NICAM config */
-#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */
-#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */
-#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */
-#define TDA9874A_AMCONR 0x12 /* audio mute control */
-#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */
-#define TDA9874A_AOSR 0x14 /* analog output select */
-#define TDA9874A_DAICONR 0x15 /* digital audio interface config */
-#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */
-#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */
-#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */
-#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */
-
-/* Subaddresses for TDA9874H and TDA9874A (slave tx) */
-#define TDA9874A_DSR 0x00 /* device status */
-#define TDA9874A_NSR 0x01 /* NICAM status */
-#define TDA9874A_NECR 0x02 /* NICAM error count */
-#define TDA9874A_DR1 0x03 /* add. data LSB */
-#define TDA9874A_DR2 0x04 /* add. data MSB */
-#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */
-#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */
-#define TDA9874A_SIFLR 0x07 /* SIF level */
-#define TDA9874A_TR2 252 /* test reg. 2 */
-#define TDA9874A_TR1 253 /* test reg. 1 */
-#define TDA9874A_DIC 254 /* device id. code */
-#define TDA9874A_SIC 255 /* software id. code */
-
-
-static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */
-static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */
-static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */
-static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */
-static int tda9874a_dic = -1; /* device id. code */
-
-/* insmod options for tda9874a */
-static unsigned int tda9874a_SIF = UNSET;
-static unsigned int tda9874a_AMSEL = UNSET;
-static unsigned int tda9874a_STD = UNSET;
-module_param(tda9874a_SIF, int, 0444);
-module_param(tda9874a_AMSEL, int, 0444);
-module_param(tda9874a_STD, int, 0444);
-
-/*
- * initialization table for tda9874 decoder:
- * - carrier 1 freq. registers (3 bytes)
- * - carrier 2 freq. registers (3 bytes)
- * - demudulator config register
- * - FM de-emphasis register (slow identification mode)
- * Note: frequency registers must be written in single i2c transfer.
- */
-static struct tda9874a_MODES {
- char *name;
- audiocmd cmd;
-} tda9874a_modelist[9] = {
- { "A2, B/G", /* default */
- { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} },
- { "A2, M (Korea)",
- { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} },
- { "A2, D/K (1)",
- { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} },
- { "A2, D/K (2)",
- { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} },
- { "A2, D/K (3)",
- { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} },
- { "NICAM, I",
- { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} },
- { "NICAM, B/G",
- { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} },
- { "NICAM, D/K",
- { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} },
- { "NICAM, L",
- { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} }
-};
-
-static int tda9874a_setup(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
-
- chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */
- chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR);
- chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02);
- if(tda9874a_dic == 0x11) {
- chip_write(chip, TDA9874A_FMMR, 0x80);
- } else { /* dic == 0x07 */
- chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd);
- chip_write(chip, TDA9874A_FMMR, 0x00);
- }
- chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */
- chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */
- chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
- chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */
- /* Note: If signal quality is poor you may want to change NICAM */
- /* error limit registers (NLELR and NUELR) to some greater values. */
- /* Then the sound would remain stereo, but won't be so clear. */
- chip_write(chip, TDA9874A_NLELR, 0x14); /* default */
- chip_write(chip, TDA9874A_NUELR, 0x50); /* default */
-
- if(tda9874a_dic == 0x11) {
- chip_write(chip, TDA9874A_AMCONR, 0xf9);
- chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
- chip_write(chip, TDA9874A_AOSR, 0x80);
- chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80);
- chip_write(chip, TDA9874A_ESP, tda9874a_ESP);
- } else { /* dic == 0x07 */
- chip_write(chip, TDA9874A_AMCONR, 0xfb);
- chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80);
- chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */
- }
- v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n",
- tda9874a_modelist[tda9874a_STD].name,tda9874a_STD);
- return 1;
-}
-
-static int tda9874a_getrxsubchans(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int dsr,nsr,mode;
- int necr; /* just for debugging */
-
- mode = V4L2_TUNER_SUB_MONO;
-
- if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR)))
- return mode;
- if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR)))
- return mode;
- if(-1 == (necr = chip_read2(chip,TDA9874A_NECR)))
- return mode;
-
- /* need to store dsr/nsr somewhere */
- chip->shadow.bytes[MAXREGS-2] = dsr;
- chip->shadow.bytes[MAXREGS-1] = nsr;
-
- if(tda9874a_mode) {
- /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked.
- * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates
- * that sound has (temporarily) switched from NICAM to
- * mono FM (or AM) on 1st sound carrier due to high NICAM bit
- * error count. So in fact there is no stereo in this case :-(
- * But changing the mode to V4L2_TUNER_MODE_MONO would switch
- * external 4052 multiplexer in audio_hook().
- */
- if(nsr & 0x02) /* NSR.S/MB=1 */
- mode = V4L2_TUNER_SUB_STEREO;
- if(nsr & 0x01) /* NSR.D/SB=1 */
- mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- } else {
- if(dsr & 0x02) /* DSR.IDSTE=1 */
- mode = V4L2_TUNER_SUB_STEREO;
- if(dsr & 0x04) /* DSR.IDDUA=1 */
- mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- }
-
- v4l2_dbg(1, debug, sd,
- "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n",
- dsr, nsr, necr, mode);
- return mode;
-}
-
-static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- struct v4l2_subdev *sd = &chip->sd;
-
- /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */
- /* If auto-muting is disabled, we can hear a signal of degrading quality. */
- if (tda9874a_mode) {
- if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */
- tda9874a_NCONR &= 0xfe; /* enable */
- else
- tda9874a_NCONR |= 0x01; /* disable */
- chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR);
- }
-
- /* Note: TDA9874A supports automatic FM dematrixing (FMMR register)
- * and has auto-select function for audio output (AOSR register).
- * Old TDA9874H doesn't support these features.
- * TDA9874A also has additional mono output pin (OUTM), which
- * on same (all?) tv-cards is not used, anyway (as well as MONOIN).
- */
- if(tda9874a_dic == 0x11) {
- int aosr = 0x80;
- int mdacosr = (tda9874a_mode) ? 0x82:0x80;
-
- switch(mode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_STEREO:
- break;
- case V4L2_TUNER_MODE_LANG1:
- aosr = 0x80; /* auto-select, dual A/A */
- mdacosr = (tda9874a_mode) ? 0x82:0x80;
- break;
- case V4L2_TUNER_MODE_LANG2:
- aosr = 0xa0; /* auto-select, dual B/B */
- mdacosr = (tda9874a_mode) ? 0x83:0x81;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- aosr = 0x00; /* always route L to L and R to R */
- mdacosr = (tda9874a_mode) ? 0x82:0x80;
- break;
- default:
- return;
- }
- chip_write(chip, TDA9874A_AOSR, aosr);
- chip_write(chip, TDA9874A_MDACOSR, mdacosr);
-
- v4l2_dbg(1, debug, sd,
- "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n",
- mode, aosr, mdacosr);
-
- } else { /* dic == 0x07 */
- int fmmr,aosr;
-
- switch(mode) {
- case V4L2_TUNER_MODE_MONO:
- fmmr = 0x00; /* mono */
- aosr = 0x10; /* A/A */
- break;
- case V4L2_TUNER_MODE_STEREO:
- if(tda9874a_mode) {
- fmmr = 0x00;
- aosr = 0x00; /* handled by NICAM auto-mute */
- } else {
- fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */
- aosr = 0x00;
- }
- break;
- case V4L2_TUNER_MODE_LANG1:
- fmmr = 0x02; /* dual */
- aosr = 0x10; /* dual A/A */
- break;
- case V4L2_TUNER_MODE_LANG2:
- fmmr = 0x02; /* dual */
- aosr = 0x20; /* dual B/B */
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- fmmr = 0x02; /* dual */
- aosr = 0x00; /* dual A/B */
- break;
- default:
- return;
- }
- chip_write(chip, TDA9874A_FMMR, fmmr);
- chip_write(chip, TDA9874A_AOSR, aosr);
-
- v4l2_dbg(1, debug, sd,
- "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n",
- mode, fmmr, aosr);
- }
-}
-
-static int tda9874a_checkit(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int dic,sic; /* device id. and software id. codes */
-
- if(-1 == (dic = chip_read2(chip,TDA9874A_DIC)))
- return 0;
- if(-1 == (sic = chip_read2(chip,TDA9874A_SIC)))
- return 0;
-
- v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic);
-
- if((dic == 0x11)||(dic == 0x07)) {
- v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h");
- tda9874a_dic = dic; /* remember device id. */
- return 1;
- }
- return 0; /* not found */
-}
-
-static int tda9874a_initialize(struct CHIPSTATE *chip)
-{
- if (tda9874a_SIF > 2)
- tda9874a_SIF = 1;
- if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist))
- tda9874a_STD = 0;
- if(tda9874a_AMSEL > 1)
- tda9874a_AMSEL = 0;
-
- if(tda9874a_SIF == 1)
- tda9874a_GCONR = 0xc0; /* sound IF input 1 */
- else
- tda9874a_GCONR = 0xc1; /* sound IF input 2 */
-
- tda9874a_ESP = tda9874a_STD;
- tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1;
-
- if(tda9874a_AMSEL == 0)
- tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */
- else
- tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */
-
- tda9874a_setup(chip);
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* audio chip description - defines+functions for tda9875 */
-/* The TDA9875 is made by Philips Semiconductor
- * http://www.semiconductors.philips.com
- * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator
- *
- */
-
-/* subaddresses for TDA9875 */
-#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/
-#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */
-#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/
-#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/
-
-#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/
-#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/
-#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/
-#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/
-
-#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/
-#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/
-#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/
-#define TDA9875_MVL 0x1a /* Main volume gauche */
-#define TDA9875_MVR 0x1b /* Main volume droite */
-#define TDA9875_MBA 0x1d /* Main Basse */
-#define TDA9875_MTR 0x1e /* Main treble */
-#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/
-#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */
-#define TDA9875_AVR 0x21 /* Auxiliary volume droite */
-#define TDA9875_ABA 0x22 /* Auxiliary Basse */
-#define TDA9875_ATR 0x23 /* Auxiliary treble */
-
-#define TDA9875_MSR 0x02 /* Monitor select register */
-#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */
-#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */
-#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */
-#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */
-#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */
-#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */
-#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/
-#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/
-#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/
-
-/* values */
-#define TDA9875_MUTE_ON 0xff /* general mute */
-#define TDA9875_MUTE_OFF 0xcc /* general no mute */
-
-static int tda9875_initialize(struct CHIPSTATE *chip)
-{
- chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
- chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
- chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
- chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
- chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
- chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
- chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
- chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
- chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/
- chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
- chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
- chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
- chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
-
- chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
- chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
- chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
- chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
- chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
- chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
- chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
- chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
- chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
- chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
- chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
- chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
- chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
- chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
- chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
- chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
-
- chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */
- return 0;
-}
-
-static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); }
-static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); }
-static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); }
-
-/* ----------------------------------------------------------------------- */
-
-
-/* *********************** *
- * i2c interface functions *
- * *********************** */
-
-static int tda9875_checkit(struct CHIPSTATE *chip)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int dic, rev;
-
- dic = chip_read2(chip, 254);
- rev = chip_read2(chip, 255);
-
- if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
- v4l2_info(sd, "found tda9875%s rev. %d.\n",
- dic == 0 ? "" : "A", rev);
- return 1;
- }
- return 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for tea6420 */
-
-#define TEA6300_VL 0x00 /* volume left */
-#define TEA6300_VR 0x01 /* volume right */
-#define TEA6300_BA 0x02 /* bass */
-#define TEA6300_TR 0x03 /* treble */
-#define TEA6300_FA 0x04 /* fader control */
-#define TEA6300_S 0x05 /* switch register */
- /* values for those registers: */
-#define TEA6300_S_SA 0x01 /* stereo A input */
-#define TEA6300_S_SB 0x02 /* stereo B */
-#define TEA6300_S_SC 0x04 /* stereo C */
-#define TEA6300_S_GMU 0x80 /* general mute */
-
-#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */
-#define TEA6320_FFR 0x01 /* fader front right (0-5) */
-#define TEA6320_FFL 0x02 /* fader front left (0-5) */
-#define TEA6320_FRR 0x03 /* fader rear right (0-5) */
-#define TEA6320_FRL 0x04 /* fader rear left (0-5) */
-#define TEA6320_BA 0x05 /* bass (0-4) */
-#define TEA6320_TR 0x06 /* treble (0-4) */
-#define TEA6320_S 0x07 /* switch register */
- /* values for those registers: */
-#define TEA6320_S_SA 0x07 /* stereo A input */
-#define TEA6320_S_SB 0x06 /* stereo B */
-#define TEA6320_S_SC 0x05 /* stereo C */
-#define TEA6320_S_SD 0x04 /* stereo D */
-#define TEA6320_S_GMU 0x80 /* general mute */
-
-#define TEA6420_S_SA 0x00 /* stereo A input */
-#define TEA6420_S_SB 0x01 /* stereo B */
-#define TEA6420_S_SC 0x02 /* stereo C */
-#define TEA6420_S_SD 0x03 /* stereo D */
-#define TEA6420_S_SE 0x04 /* stereo E */
-#define TEA6420_S_GMU 0x05 /* general mute */
-
-static int tea6300_shift10(int val) { return val >> 10; }
-static int tea6300_shift12(int val) { return val >> 12; }
-
-/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */
-/* 0x0c mirror those immediately higher) */
-static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; }
-static int tea6320_shift11(int val) { return val >> 11; }
-static int tea6320_initialize(struct CHIPSTATE * chip)
-{
- chip_write(chip, TEA6320_FFR, 0x3f);
- chip_write(chip, TEA6320_FFL, 0x3f);
- chip_write(chip, TEA6320_FRR, 0x3f);
- chip_write(chip, TEA6320_FRL, 0x3f);
-
- return 0;
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for tda8425 */
-
-#define TDA8425_VL 0x00 /* volume left */
-#define TDA8425_VR 0x01 /* volume right */
-#define TDA8425_BA 0x02 /* bass */
-#define TDA8425_TR 0x03 /* treble */
-#define TDA8425_S1 0x08 /* switch functions */
- /* values for those registers: */
-#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */
-#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */
-#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */
-#define TDA8425_S1_MU 0x20 /* mute bit */
-#define TDA8425_S1_STEREO 0x18 /* stereo bits */
-#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */
-#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */
-#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */
-#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */
-#define TDA8425_S1_ML 0x06 /* language selector */
-#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */
-#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */
-#define TDA8425_S1_ML_STEREO 0x06 /* stereo */
-#define TDA8425_S1_IS 0x01 /* channel selector */
-
-
-static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; }
-static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; }
-
-static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1;
-
- switch (mode) {
- case V4L2_TUNER_MODE_LANG1:
- s1 |= TDA8425_S1_ML_SOUND_A;
- s1 |= TDA8425_S1_STEREO_PSEUDO;
- break;
- case V4L2_TUNER_MODE_LANG2:
- s1 |= TDA8425_S1_ML_SOUND_B;
- s1 |= TDA8425_S1_STEREO_PSEUDO;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- s1 |= TDA8425_S1_ML_STEREO;
- s1 |= TDA8425_S1_STEREO_LINEAR;
- break;
- case V4L2_TUNER_MODE_MONO:
- s1 |= TDA8425_S1_ML_STEREO;
- s1 |= TDA8425_S1_STEREO_MONO;
- break;
- case V4L2_TUNER_MODE_STEREO:
- s1 |= TDA8425_S1_ML_STEREO;
- s1 |= TDA8425_S1_STEREO_SPATIAL;
- break;
- default:
- return;
- }
- chip_write(chip,TDA8425_S1,s1);
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for pic16c54 (PV951) */
-
-/* the registers of 16C54, I2C sub address. */
-#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */
-#define PIC16C54_REG_MISC 0x02
-
-/* bit definition of the RESET register, I2C data. */
-#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */
- /* code of remote controller */
-#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */
-#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */
-#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */
-#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */
-#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */
-#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */
-#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - defines+functions for TA8874Z */
-
-/* write 1st byte */
-#define TA8874Z_LED_STE 0x80
-#define TA8874Z_LED_BIL 0x40
-#define TA8874Z_LED_EXT 0x20
-#define TA8874Z_MONO_SET 0x10
-#define TA8874Z_MUTE 0x08
-#define TA8874Z_F_MONO 0x04
-#define TA8874Z_MODE_SUB 0x02
-#define TA8874Z_MODE_MAIN 0x01
-
-/* write 2nd byte */
-/*#define TA8874Z_TI 0x80 */ /* test mode */
-#define TA8874Z_SEPARATION 0x3f
-#define TA8874Z_SEPARATION_DEFAULT 0x10
-
-/* read */
-#define TA8874Z_B1 0x80
-#define TA8874Z_B0 0x40
-#define TA8874Z_CHAG_FLAG 0x20
-
-/*
- * B1 B0
- * mono L H
- * stereo L L
- * BIL H L
- */
-static int ta8874z_getrxsubchans(struct CHIPSTATE *chip)
-{
- int val, mode;
-
- val = chip_read(chip);
- mode = V4L2_TUNER_SUB_MONO;
- if (val & TA8874Z_B1){
- mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
- }else if (!(val & TA8874Z_B0)){
- mode = V4L2_TUNER_SUB_STEREO;
- }
- /* v4l2_dbg(1, debug, &chip->sd,
- "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n",
- val, mode); */
- return mode;
-}
-
-static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}};
-static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}};
-static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}};
-static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
-static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}};
-
-static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode)
-{
- struct v4l2_subdev *sd = &chip->sd;
- int update = 1;
- audiocmd *t = NULL;
-
- v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode);
-
- switch(mode){
- case V4L2_TUNER_MODE_MONO:
- t = &ta8874z_mono;
- break;
- case V4L2_TUNER_MODE_STEREO:
- t = &ta8874z_stereo;
- break;
- case V4L2_TUNER_MODE_LANG1:
- t = &ta8874z_main;
- break;
- case V4L2_TUNER_MODE_LANG2:
- t = &ta8874z_sub;
- break;
- case V4L2_TUNER_MODE_LANG1_LANG2:
- t = &ta8874z_both;
- break;
- default:
- update = 0;
- }
-
- if(update)
- chip_cmd(chip, "TA8874Z", t);
-}
-
-static int ta8874z_checkit(struct CHIPSTATE *chip)
-{
- int rc;
- rc = chip_read(chip);
- return ((rc & 0x1f) == 0x1f) ? 1 : 0;
-}
-
-/* ---------------------------------------------------------------------- */
-/* audio chip descriptions - struct CHIPDESC */
-
-/* insmod options to enable/disable individual audio chips */
-static int tda8425 = 1;
-static int tda9840 = 1;
-static int tda9850 = 1;
-static int tda9855 = 1;
-static int tda9873 = 1;
-static int tda9874a = 1;
-static int tda9875 = 1;
-static int tea6300; /* default 0 - address clash with msp34xx */
-static int tea6320; /* default 0 - address clash with msp34xx */
-static int tea6420 = 1;
-static int pic16c54 = 1;
-static int ta8874z; /* default 0 - address clash with tda9840 */
-
-module_param(tda8425, int, 0444);
-module_param(tda9840, int, 0444);
-module_param(tda9850, int, 0444);
-module_param(tda9855, int, 0444);
-module_param(tda9873, int, 0444);
-module_param(tda9874a, int, 0444);
-module_param(tda9875, int, 0444);
-module_param(tea6300, int, 0444);
-module_param(tea6320, int, 0444);
-module_param(tea6420, int, 0444);
-module_param(pic16c54, int, 0444);
-module_param(ta8874z, int, 0444);
-
-static struct CHIPDESC chiplist[] = {
- {
- .name = "tda9840",
- .insmodopt = &tda9840,
- .addr_lo = I2C_ADDR_TDA9840 >> 1,
- .addr_hi = I2C_ADDR_TDA9840 >> 1,
- .registers = 5,
- .flags = CHIP_NEED_CHECKMODE,
-
- /* callbacks */
- .checkit = tda9840_checkit,
- .getrxsubchans = tda9840_getrxsubchans,
- .setaudmode = tda9840_setaudmode,
-
- .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN
- /* ,TDA9840_SW, TDA9840_MONO */} }
- },
- {
- .name = "tda9873h",
- .insmodopt = &tda9873,
- .addr_lo = I2C_ADDR_TDA985x_L >> 1,
- .addr_hi = I2C_ADDR_TDA985x_H >> 1,
- .registers = 3,
- .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE,
-
- /* callbacks */
- .checkit = tda9873_checkit,
- .getrxsubchans = tda9873_getrxsubchans,
- .setaudmode = tda9873_setaudmode,
-
- .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } },
- .inputreg = TDA9873_SW,
- .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE,
- .inputmap = {0xa0, 0xa2, 0xa0, 0xa0},
- .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE,
-
- },
- {
- .name = "tda9874h/a",
- .insmodopt = &tda9874a,
- .addr_lo = I2C_ADDR_TDA9874 >> 1,
- .addr_hi = I2C_ADDR_TDA9874 >> 1,
- .flags = CHIP_NEED_CHECKMODE,
-
- /* callbacks */
- .initialize = tda9874a_initialize,
- .checkit = tda9874a_checkit,
- .getrxsubchans = tda9874a_getrxsubchans,
- .setaudmode = tda9874a_setaudmode,
- },
- {
- .name = "tda9875",
- .insmodopt = &tda9875,
- .addr_lo = I2C_ADDR_TDA9875 >> 1,
- .addr_hi = I2C_ADDR_TDA9875 >> 1,
- .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
-
- /* callbacks */
- .initialize = tda9875_initialize,
- .checkit = tda9875_checkit,
- .volfunc = tda9875_volume,
- .bassfunc = tda9875_bass,
- .treblefunc = tda9875_treble,
- .leftreg = TDA9875_MVL,
- .rightreg = TDA9875_MVR,
- .bassreg = TDA9875_MBA,
- .treblereg = TDA9875_MTR,
- .leftinit = 58880,
- .rightinit = 58880,
- },
- {
- .name = "tda9850",
- .insmodopt = &tda9850,
- .addr_lo = I2C_ADDR_TDA985x_L >> 1,
- .addr_hi = I2C_ADDR_TDA985x_H >> 1,
- .registers = 11,
-
- .getrxsubchans = tda985x_getrxsubchans,
- .setaudmode = tda985x_setaudmode,
-
- .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } }
- },
- {
- .name = "tda9855",
- .insmodopt = &tda9855,
- .addr_lo = I2C_ADDR_TDA985x_L >> 1,
- .addr_hi = I2C_ADDR_TDA985x_H >> 1,
- .registers = 11,
- .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
-
- .leftreg = TDA9855_VL,
- .rightreg = TDA9855_VR,
- .bassreg = TDA9855_BA,
- .treblereg = TDA9855_TR,
-
- /* callbacks */
- .volfunc = tda9855_volume,
- .bassfunc = tda9855_bass,
- .treblefunc = tda9855_treble,
- .getrxsubchans = tda985x_getrxsubchans,
- .setaudmode = tda985x_setaudmode,
-
- .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2,
- TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT,
- TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM,
- 0x07, 0x10, 0x10, 0x03 }}
- },
- {
- .name = "tea6300",
- .insmodopt = &tea6300,
- .addr_lo = I2C_ADDR_TEA6300 >> 1,
- .addr_hi = I2C_ADDR_TEA6300 >> 1,
- .registers = 6,
- .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
-
- .leftreg = TEA6300_VR,
- .rightreg = TEA6300_VL,
- .bassreg = TEA6300_BA,
- .treblereg = TEA6300_TR,
-
- /* callbacks */
- .volfunc = tea6300_shift10,
- .bassfunc = tea6300_shift12,
- .treblefunc = tea6300_shift12,
-
- .inputreg = TEA6300_S,
- .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC },
- .inputmute = TEA6300_S_GMU,
- },
- {
- .name = "tea6320",
- .insmodopt = &tea6320,
- .addr_lo = I2C_ADDR_TEA6300 >> 1,
- .addr_hi = I2C_ADDR_TEA6300 >> 1,
- .registers = 8,
- .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
-
- .leftreg = TEA6320_V,
- .rightreg = TEA6320_V,
- .bassreg = TEA6320_BA,
- .treblereg = TEA6320_TR,
-
- /* callbacks */
- .initialize = tea6320_initialize,
- .volfunc = tea6320_volume,
- .bassfunc = tea6320_shift11,
- .treblefunc = tea6320_shift11,
-
- .inputreg = TEA6320_S,
- .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD },
- .inputmute = TEA6300_S_GMU,
- },
- {
- .name = "tea6420",
- .insmodopt = &tea6420,
- .addr_lo = I2C_ADDR_TEA6420 >> 1,
- .addr_hi = I2C_ADDR_TEA6420 >> 1,
- .registers = 1,
- .flags = CHIP_HAS_INPUTSEL,
-
- .inputreg = -1,
- .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC },
- .inputmute = TEA6300_S_GMU,
- },
- {
- .name = "tda8425",
- .insmodopt = &tda8425,
- .addr_lo = I2C_ADDR_TDA8425 >> 1,
- .addr_hi = I2C_ADDR_TDA8425 >> 1,
- .registers = 9,
- .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL,
-
- .leftreg = TDA8425_VL,
- .rightreg = TDA8425_VR,
- .bassreg = TDA8425_BA,
- .treblereg = TDA8425_TR,
-
- /* callbacks */
- .volfunc = tda8425_shift10,
- .bassfunc = tda8425_shift12,
- .treblefunc = tda8425_shift12,
- .setaudmode = tda8425_setaudmode,
-
- .inputreg = TDA8425_S1,
- .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 },
- .inputmute = TDA8425_S1_OFF,
-
- },
- {
- .name = "pic16c54 (PV951)",
- .insmodopt = &pic16c54,
- .addr_lo = I2C_ADDR_PIC16C54 >> 1,
- .addr_hi = I2C_ADDR_PIC16C54>> 1,
- .registers = 2,
- .flags = CHIP_HAS_INPUTSEL,
-
- .inputreg = PIC16C54_REG_MISC,
- .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER,
- PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
- PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE,
- PIC16C54_MISC_SND_MUTE},
- .inputmute = PIC16C54_MISC_SND_MUTE,
- },
- {
- .name = "ta8874z",
- .checkit = ta8874z_checkit,
- .insmodopt = &ta8874z,
- .addr_lo = I2C_ADDR_TDA9840 >> 1,
- .addr_hi = I2C_ADDR_TDA9840 >> 1,
- .registers = 2,
-
- /* callbacks */
- .getrxsubchans = ta8874z_getrxsubchans,
- .setaudmode = ta8874z_setaudmode,
-
- .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}},
- },
- { .name = NULL } /* EOF */
-};
-
-
-/* ---------------------------------------------------------------------- */
-
-static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
- struct v4l2_control *ctrl)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (!(desc->flags & CHIP_HAS_INPUTSEL))
- break;
- ctrl->value=chip->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- if (!(desc->flags & CHIP_HAS_VOLUME))
- break;
- ctrl->value = max(chip->left,chip->right);
- return 0;
- case V4L2_CID_AUDIO_BALANCE:
- {
- int volume;
- if (!(desc->flags & CHIP_HAS_VOLUME))
- break;
- volume = max(chip->left,chip->right);
- if (volume)
- ctrl->value=(32768*min(chip->left,chip->right))/volume;
- else
- ctrl->value=32768;
- return 0;
- }
- case V4L2_CID_AUDIO_BASS:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- break;
- ctrl->value = chip->bass;
- return 0;
- case V4L2_CID_AUDIO_TREBLE:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- break;
- ctrl->value = chip->treble;
- return 0;
- }
- return -EINVAL;
-}
-
-static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
- struct v4l2_control *ctrl)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (!(desc->flags & CHIP_HAS_INPUTSEL))
- break;
-
- if (ctrl->value < 0 || ctrl->value >= 2)
- return -ERANGE;
- chip->muted = ctrl->value;
- if (chip->muted)
- chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask);
- else
- chip_write_masked(chip,desc->inputreg,
- desc->inputmap[chip->input],desc->inputmask);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- {
- int volume,balance;
-
- if (!(desc->flags & CHIP_HAS_VOLUME))
- break;
-
- volume = max(chip->left,chip->right);
- if (volume)
- balance=(32768*min(chip->left,chip->right))/volume;
- else
- balance=32768;
-
- volume=ctrl->value;
- chip->left = (min(65536 - balance,32768) * volume) / 32768;
- chip->right = (min(balance,volume *(__u16)32768)) / 32768;
-
- chip_write(chip,desc->leftreg,desc->volfunc(chip->left));
- chip_write(chip,desc->rightreg,desc->volfunc(chip->right));
-
- return 0;
- }
- case V4L2_CID_AUDIO_BALANCE:
- {
- int volume, balance;
-
- if (!(desc->flags & CHIP_HAS_VOLUME))
- break;
-
- volume = max(chip->left, chip->right);
- balance = ctrl->value;
- chip->left = (min(65536 - balance, 32768) * volume) / 32768;
- chip->right = (min(balance, volume * (__u16)32768)) / 32768;
-
- chip_write(chip, desc->leftreg, desc->volfunc(chip->left));
- chip_write(chip, desc->rightreg, desc->volfunc(chip->right));
-
- return 0;
- }
- case V4L2_CID_AUDIO_BASS:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- break;
- chip->bass = ctrl->value;
- chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass));
-
- return 0;
- case V4L2_CID_AUDIO_TREBLE:
- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
- break;
- chip->treble = ctrl->value;
- chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
-
- return 0;
- }
- return -EINVAL;
-}
-
-
-/* ---------------------------------------------------------------------- */
-/* video4linux interface */
-
-static int tvaudio_s_radio(struct v4l2_subdev *sd)
-{
- struct CHIPSTATE *chip = to_state(sd);
-
- chip->radio = 1;
- /* del_timer(&chip->wt); */
- return 0;
-}
-
-static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (desc->flags & CHIP_HAS_INPUTSEL)
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- break;
- case V4L2_CID_AUDIO_VOLUME:
- if (desc->flags & CHIP_HAS_VOLUME)
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
- break;
- case V4L2_CID_AUDIO_BALANCE:
- if (desc->flags & CHIP_HAS_VOLUME)
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- break;
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- if (desc->flags & CHIP_HAS_BASSTREBLE)
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
- break;
- default:
- break;
- }
- return -EINVAL;
-}
-
-static int tvaudio_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- if (!(desc->flags & CHIP_HAS_INPUTSEL))
- return 0;
- if (input >= 4)
- return -EINVAL;
- /* There are four inputs: tuner, radio, extern and intern. */
- chip->input = input;
- if (chip->muted)
- return 0;
- chip_write_masked(chip, desc->inputreg,
- desc->inputmap[chip->input], desc->inputmask);
- return 0;
-}
-
-static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- if (!desc->setaudmode)
- return 0;
- if (chip->radio)
- return 0;
-
- switch (vt->audmode) {
- case V4L2_TUNER_MODE_MONO:
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1:
- case V4L2_TUNER_MODE_LANG2:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- break;
- default:
- return -EINVAL;
- }
- chip->audmode = vt->audmode;
-
- if (chip->thread)
- wake_up_process(chip->thread);
- else
- desc->setaudmode(chip, vt->audmode);
-
- return 0;
-}
-
-static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- if (!desc->getrxsubchans)
- return 0;
- if (chip->radio)
- return 0;
-
- vt->audmode = chip->audmode;
- vt->rxsubchans = desc->getrxsubchans(chip);
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
-
- return 0;
-}
-
-static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct CHIPSTATE *chip = to_state(sd);
-
- chip->radio = 0;
- return 0;
-}
-
-static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
-{
- struct CHIPSTATE *chip = to_state(sd);
- struct CHIPDESC *desc = chip->desc;
-
- /* For chips that provide getrxsubchans and setaudmode, and doesn't
- automatically follows the stereo carrier, a kthread is
- created to set the audio standard. In this case, when then
- the video channel is changed, tvaudio starts on MONO mode.
- After waiting for 2 seconds, the kernel thread is called,
- to follow whatever audio standard is pointed by the
- audio carrier.
- */
- if (chip->thread) {
- desc->setaudmode(chip, V4L2_TUNER_MODE_MONO);
- chip->prevmode = -1; /* reset previous mode */
- mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000));
- }
- return 0;
-}
-
-static int tvaudio_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_TVAUDIO, 0);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct v4l2_subdev_core_ops tvaudio_core_ops = {
- .g_chip_ident = tvaudio_g_chip_ident,
- .queryctrl = tvaudio_queryctrl,
- .g_ctrl = tvaudio_g_ctrl,
- .s_ctrl = tvaudio_s_ctrl,
- .s_std = tvaudio_s_std,
-};
-
-static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = {
- .s_radio = tvaudio_s_radio,
- .s_frequency = tvaudio_s_frequency,
- .s_tuner = tvaudio_s_tuner,
- .g_tuner = tvaudio_g_tuner,
-};
-
-static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = {
- .s_routing = tvaudio_s_routing,
-};
-
-static const struct v4l2_subdev_ops tvaudio_ops = {
- .core = &tvaudio_core_ops,
- .tuner = &tvaudio_tuner_ops,
- .audio = &tvaudio_audio_ops,
-};
-
-/* ----------------------------------------------------------------------- */
-
-
-/* i2c registration */
-
-static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct CHIPSTATE *chip;
- struct CHIPDESC *desc;
- struct v4l2_subdev *sd;
-
- if (debug) {
- printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n");
- printk(KERN_INFO "tvaudio: known chips: ");
- for (desc = chiplist; desc->name != NULL; desc++)
- printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name);
- printk("\n");
- }
-
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- sd = &chip->sd;
- v4l2_i2c_subdev_init(sd, client, &tvaudio_ops);
-
- /* find description for the chip */
- v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1);
- for (desc = chiplist; desc->name != NULL; desc++) {
- if (0 == *(desc->insmodopt))
- continue;
- if (client->addr < desc->addr_lo ||
- client->addr > desc->addr_hi)
- continue;
- if (desc->checkit && !desc->checkit(chip))
- continue;
- break;
- }
- if (desc->name == NULL) {
- v4l2_dbg(1, debug, sd, "no matching chip description found\n");
- kfree(chip);
- return -EIO;
- }
- v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name);
- if (desc->flags) {
- v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n",
- (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "",
- (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "",
- (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : "");
- }
-
- /* fill required data structures */
- if (!id)
- strlcpy(client->name, desc->name, I2C_NAME_SIZE);
- chip->desc = desc;
- chip->shadow.count = desc->registers+1;
- chip->prevmode = -1;
- chip->audmode = V4L2_TUNER_MODE_LANG1;
-
- /* initialization */
- if (desc->initialize != NULL)
- desc->initialize(chip);
- else
- chip_cmd(chip, "init", &desc->init);
-
- if (desc->flags & CHIP_HAS_VOLUME) {
- if (!desc->volfunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without volume controls
- */
- v4l2_info(sd, "volume callback undefined!\n");
- desc->flags &= ~CHIP_HAS_VOLUME;
- } else {
- chip->left = desc->leftinit ? desc->leftinit : 65535;
- chip->right = desc->rightinit ? desc->rightinit : 65535;
- chip_write(chip, desc->leftreg,
- desc->volfunc(chip->left));
- chip_write(chip, desc->rightreg,
- desc->volfunc(chip->right));
- }
- }
- if (desc->flags & CHIP_HAS_BASSTREBLE) {
- if (!desc->bassfunc || !desc->treblefunc) {
- /* This shouldn't be happen. Warn user, but keep working
- without bass/treble controls
- */
- v4l2_info(sd, "bass/treble callbacks undefined!\n");
- desc->flags &= ~CHIP_HAS_BASSTREBLE;
- } else {
- chip->treble = desc->trebleinit ?
- desc->trebleinit : 32768;
- chip->bass = desc->bassinit ?
- desc->bassinit : 32768;
- chip_write(chip, desc->bassreg,
- desc->bassfunc(chip->bass));
- chip_write(chip, desc->treblereg,
- desc->treblefunc(chip->treble));
- }
- }
-
- chip->thread = NULL;
- init_timer(&chip->wt);
- if (desc->flags & CHIP_NEED_CHECKMODE) {
- if (!desc->getrxsubchans || !desc->setaudmode) {
- /* This shouldn't be happen. Warn user, but keep working
- without kthread
- */
- v4l2_info(sd, "set/get mode callbacks undefined!\n");
- return 0;
- }
- /* start async thread */
- chip->wt.function = chip_thread_wake;
- chip->wt.data = (unsigned long)chip;
- chip->thread = kthread_run(chip_thread, chip, client->name);
- if (IS_ERR(chip->thread)) {
- v4l2_warn(sd, "failed to create kthread\n");
- chip->thread = NULL;
- }
- }
- return 0;
-}
-
-static int tvaudio_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct CHIPSTATE *chip = to_state(sd);
-
- del_timer_sync(&chip->wt);
- if (chip->thread) {
- /* shutdown async thread */
- kthread_stop(chip->thread);
- chip->thread = NULL;
- }
-
- v4l2_device_unregister_subdev(sd);
- kfree(chip);
- 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 tvaudio_id[] = {
- { "tvaudio", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tvaudio_id);
-
-static struct i2c_driver tvaudio_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "tvaudio",
- },
- .probe = tvaudio_probe,
- .remove = tvaudio_remove,
- .id_table = tvaudio_id,
-};
-
-module_i2c_driver(tvaudio_driver);
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
deleted file mode 100644
index cd615c1d6011..000000000000
--- a/drivers/media/video/tvp514x.c
+++ /dev/null
@@ -1,1166 +0,0 @@
-/*
- * drivers/media/video/tvp514x.c
- *
- * TI TVP5146/47 decoder driver
- *
- * Copyright (C) 2008 Texas Instruments Inc
- * Author: Vaibhav Hiremath <hvaibhav@ti.com>
- *
- * Contributors:
- * Sivaraj R <sivaraj@ti.com>
- * Brijesh R Jadav <brijesh.j@ti.com>
- * Hardik Shah <hardik.shah@ti.com>
- * Manjunath Hadli <mrh@ti.com>
- * Karicheri Muralidharan <m-karicheri2@ti.com>
- *
- * This package 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/videodev2.h>
-#include <linux/module.h>
-
-#include <media/v4l2-device.h>
-#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"
-
-/* Module Name */
-#define TVP514X_MODULE_NAME "tvp514x"
-
-/* Private macros for TVP */
-#define I2C_RETRY_COUNT (5)
-#define LOCK_RETRY_COUNT (5)
-#define LOCK_RETRY_DELAY (200)
-
-/* Debug functions */
-static bool debug;
-module_param(debug, bool, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
-MODULE_AUTHOR("Texas Instruments");
-MODULE_DESCRIPTION("TVP514X linux decoder driver");
-MODULE_LICENSE("GPL");
-
-/* enum tvp514x_std - enum for supported standards */
-enum tvp514x_std {
- STD_NTSC_MJ = 0,
- STD_PAL_BDGHIN,
- STD_INVALID
-};
-
-/**
- * struct tvp514x_std_info - Structure to store standard informations
- * @width: Line width in pixels
- * @height:Number of active lines
- * @video_std: Value to write in REG_VIDEO_STD register
- * @standard: v4l2 standard structure information
- */
-struct tvp514x_std_info {
- unsigned long width;
- unsigned long height;
- u8 video_std;
- struct v4l2_standard standard;
-};
-
-static struct tvp514x_reg tvp514x_reg_list_default[0x40];
-
-static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable);
-/**
- * struct tvp514x_decoder - TVP5146/47 decoder object
- * @sd: Subdevice Slave handle
- * @tvp514x_regs: copy of hw's regs with preset values.
- * @pdata: Board specific
- * @ver: Chip version
- * @streaming: TVP5146/47 decoder streaming - enabled or disabled.
- * @current_std: Current standard
- * @num_stds: Number of standards
- * @std_list: Standards list
- * @input: Input routing at chip level
- * @output: Output routing at chip level
- */
-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;
-
- int ver;
- int streaming;
-
- enum tvp514x_std current_std;
- int num_stds;
- const struct tvp514x_std_info *std_list;
- /* Input and Output Routing parameters */
- u32 input;
- u32 output;
-};
-
-/* TVP514x default register values */
-static struct tvp514x_reg tvp514x_reg_list_default[] = {
- /* Composite selected */
- {TOK_WRITE, REG_INPUT_SEL, 0x05},
- {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
- /* Auto mode */
- {TOK_WRITE, REG_VIDEO_STD, 0x00},
- {TOK_WRITE, REG_OPERATION_MODE, 0x00},
- {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
- {TOK_WRITE, REG_COLOR_KILLER, 0x10},
- {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
- {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
- {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
- {TOK_WRITE, REG_BRIGHTNESS, 0x80},
- {TOK_WRITE, REG_CONTRAST, 0x80},
- {TOK_WRITE, REG_SATURATION, 0x80},
- {TOK_WRITE, REG_HUE, 0x00},
- {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
- {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
- /* Reserved */
- {TOK_SKIP, 0x0F, 0x00},
- {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
- {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
- {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
- /* Reserved */
- {TOK_SKIP, 0x13, 0x00},
- {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
- /* Reserved */
- {TOK_SKIP, 0x15, 0x00},
- /* NTSC timing */
- {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55},
- {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
- {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
- {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
- /* NTSC timing */
- {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00},
- {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
- {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
- {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
- /* NTSC timing */
- {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04},
- {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
- {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
- {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
- /* NTSC timing */
- {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01},
- {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
- {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
- {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x26, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x27, 0x00},
- {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
- /* Reserved */
- {TOK_SKIP, 0x29, 0x00},
- {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x2B, 0x00},
- {TOK_SKIP, REG_SCART_DELAY, 0x00},
- {TOK_SKIP, REG_CTI_DELAY, 0x00},
- {TOK_SKIP, REG_CTI_CONTROL, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x2F, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x30, 0x00},
- /* Reserved */
- {TOK_SKIP, 0x31, 0x00},
- /* HS, VS active high */
- {TOK_WRITE, REG_SYNC_CONTROL, 0x00},
- /* 10-bit BT.656 */
- {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00},
- /* Enable clk & data */
- {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11},
- /* Enable AVID & FLD */
- {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE},
- /* Enable VS & HS */
- {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF},
- {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
- {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
- /* Clear status */
- {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01},
- {TOK_TERM, 0, 0},
-};
-
-/**
- * Supported standards -
- *
- * Currently supports two standards only, need to add support for rest of the
- * modes, like SECAM, etc...
- */
-static const struct tvp514x_std_info tvp514x_std_list[] = {
- /* Standard: STD_NTSC_MJ */
- [STD_NTSC_MJ] = {
- .width = NTSC_NUM_ACTIVE_PIXELS,
- .height = NTSC_NUM_ACTIVE_LINES,
- .video_std = VIDEO_STD_NTSC_MJ_BIT,
- .standard = {
- .index = 0,
- .id = V4L2_STD_NTSC,
- .name = "NTSC",
- .frameperiod = {1001, 30000},
- .framelines = 525
- },
- /* Standard: STD_PAL_BDGHIN */
- },
- [STD_PAL_BDGHIN] = {
- .width = PAL_NUM_ACTIVE_PIXELS,
- .height = PAL_NUM_ACTIVE_LINES,
- .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
- .standard = {
- .index = 1,
- .id = V4L2_STD_PAL,
- .name = "PAL",
- .frameperiod = {1, 25},
- .framelines = 625
- },
- },
- /* Standard: need to add for additional standard */
-};
-
-
-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.
- * @sd: ptr to v4l2_subdev struct
- * @reg: TVP5146/47 register address
- *
- * Returns value read if successful, or non-zero (-1) otherwise.
- */
-static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg)
-{
- int err, retry = 0;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-read_again:
-
- err = i2c_smbus_read_byte_data(client, reg);
- if (err < 0) {
- if (retry <= I2C_RETRY_COUNT) {
- v4l2_warn(sd, "Read: retry ... %d\n", retry);
- retry++;
- msleep_interruptible(10);
- goto read_again;
- }
- }
-
- return err;
-}
-
-/**
- * dump_reg() - dump the register content of TVP5146/47.
- * @sd: ptr to v4l2_subdev struct
- * @reg: TVP5146/47 register address
- */
-static void dump_reg(struct v4l2_subdev *sd, u8 reg)
-{
- u32 val;
-
- val = tvp514x_read_reg(sd, reg);
- v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val);
-}
-
-/**
- * tvp514x_write_reg() - Write a value to a register in TVP5146/47
- * @sd: ptr to v4l2_subdev struct
- * @reg: TVP5146/47 register address
- * @val: value to be written to the register
- *
- * Write a value to a register in an TVP5146/47 decoder device.
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val)
-{
- int err, retry = 0;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
-write_again:
-
- err = i2c_smbus_write_byte_data(client, reg, val);
- if (err) {
- if (retry <= I2C_RETRY_COUNT) {
- v4l2_warn(sd, "Write: retry ... %d\n", retry);
- retry++;
- msleep_interruptible(10);
- goto write_again;
- }
- }
-
- return err;
-}
-
-/**
- * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers
- * @sd: ptr to v4l2_subdev struct
- * @reglist: list of TVP5146/47 registers and values
- *
- * Initializes a list of TVP5146/47 registers:-
- * if token is TOK_TERM, then entire write operation terminates
- * if token is TOK_DELAY, then a delay of 'val' msec is introduced
- * if token is TOK_SKIP, then the register write is skipped
- * if token is TOK_WRITE, then the register write is performed
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tvp514x_write_regs(struct v4l2_subdev *sd,
- const struct tvp514x_reg reglist[])
-{
- int err;
- const struct tvp514x_reg *next = reglist;
-
- for (; next->token != TOK_TERM; next++) {
- if (next->token == TOK_DELAY) {
- msleep(next->val);
- continue;
- }
-
- if (next->token == TOK_SKIP)
- continue;
-
- err = tvp514x_write_reg(sd, next->reg, (u8) next->val);
- if (err) {
- v4l2_err(sd, "Write failed. Err[%d]\n", err);
- return err;
- }
- }
- return 0;
-}
-
-/**
- * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47
- * @sd: ptr to v4l2_subdev struct
- *
- * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no
- * standard detected.
- */
-static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd)
-{
- u8 std, std_status;
-
- std = tvp514x_read_reg(sd, REG_VIDEO_STD);
- if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT)
- /* use the standard status register */
- std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS);
- else
- /* use the standard register itself */
- std_status = std;
-
- switch (std_status & VIDEO_STD_MASK) {
- case VIDEO_STD_NTSC_MJ_BIT:
- return STD_NTSC_MJ;
-
- case VIDEO_STD_PAL_BDGHIN_BIT:
- return STD_PAL_BDGHIN;
-
- default:
- return STD_INVALID;
- }
-
- return STD_INVALID;
-}
-
-/* TVP5146/47 register dump function */
-static void tvp514x_reg_dump(struct v4l2_subdev *sd)
-{
- dump_reg(sd, REG_INPUT_SEL);
- dump_reg(sd, REG_AFE_GAIN_CTRL);
- dump_reg(sd, REG_VIDEO_STD);
- dump_reg(sd, REG_OPERATION_MODE);
- dump_reg(sd, REG_COLOR_KILLER);
- dump_reg(sd, REG_LUMA_CONTROL1);
- dump_reg(sd, REG_LUMA_CONTROL2);
- dump_reg(sd, REG_LUMA_CONTROL3);
- dump_reg(sd, REG_BRIGHTNESS);
- dump_reg(sd, REG_CONTRAST);
- dump_reg(sd, REG_SATURATION);
- dump_reg(sd, REG_HUE);
- dump_reg(sd, REG_CHROMA_CONTROL1);
- dump_reg(sd, REG_CHROMA_CONTROL2);
- dump_reg(sd, REG_COMP_PR_SATURATION);
- dump_reg(sd, REG_COMP_Y_CONTRAST);
- dump_reg(sd, REG_COMP_PB_SATURATION);
- dump_reg(sd, REG_COMP_Y_BRIGHTNESS);
- dump_reg(sd, REG_AVID_START_PIXEL_LSB);
- dump_reg(sd, REG_AVID_START_PIXEL_MSB);
- dump_reg(sd, REG_AVID_STOP_PIXEL_LSB);
- dump_reg(sd, REG_AVID_STOP_PIXEL_MSB);
- dump_reg(sd, REG_HSYNC_START_PIXEL_LSB);
- dump_reg(sd, REG_HSYNC_START_PIXEL_MSB);
- dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB);
- dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB);
- dump_reg(sd, REG_VSYNC_START_LINE_LSB);
- dump_reg(sd, REG_VSYNC_START_LINE_MSB);
- dump_reg(sd, REG_VSYNC_STOP_LINE_LSB);
- dump_reg(sd, REG_VSYNC_STOP_LINE_MSB);
- dump_reg(sd, REG_VBLK_START_LINE_LSB);
- dump_reg(sd, REG_VBLK_START_LINE_MSB);
- dump_reg(sd, REG_VBLK_STOP_LINE_LSB);
- dump_reg(sd, REG_VBLK_STOP_LINE_MSB);
- dump_reg(sd, REG_SYNC_CONTROL);
- dump_reg(sd, REG_OUTPUT_FORMATTER1);
- dump_reg(sd, REG_OUTPUT_FORMATTER2);
- dump_reg(sd, REG_OUTPUT_FORMATTER3);
- dump_reg(sd, REG_OUTPUT_FORMATTER4);
- dump_reg(sd, REG_OUTPUT_FORMATTER5);
- dump_reg(sd, REG_OUTPUT_FORMATTER6);
- dump_reg(sd, REG_CLEAR_LOST_LOCK);
-}
-
-/**
- * tvp514x_configure() - Configure the TVP5146/47 registers
- * @sd: ptr to v4l2_subdev struct
- * @decoder: ptr to tvp514x_decoder structure
- *
- * Returns zero if successful, or non-zero otherwise.
- */
-static int tvp514x_configure(struct v4l2_subdev *sd,
- struct tvp514x_decoder *decoder)
-{
- int err;
-
- /* common register initialization */
- err =
- tvp514x_write_regs(sd, decoder->tvp514x_regs);
- if (err)
- return err;
-
- if (debug)
- tvp514x_reg_dump(sd);
-
- return 0;
-}
-
-/**
- * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision.
- * @sd: pointer to standard V4L2 sub-device structure
- * @decoder: pointer to tvp514x_decoder structure
- *
- * A device is considered to be detected if the chip ID (LSB and MSB)
- * registers match the expected values.
- * Any value of the rom version register is accepted.
- * Returns ENODEV error number if no device is detected, or zero
- * if a device is detected.
- */
-static int tvp514x_detect(struct v4l2_subdev *sd,
- struct tvp514x_decoder *decoder)
-{
- u8 chip_id_msb, chip_id_lsb, rom_ver;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB);
- chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB);
- rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION);
-
- v4l2_dbg(1, debug, sd,
- "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
- chip_id_msb, chip_id_lsb, rom_ver);
- if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
- || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
- && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
- /* We didn't read the values we expected, so this must not be
- * an TVP5146/47.
- */
- v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n",
- chip_id_msb, chip_id_lsb);
- return -ENODEV;
- }
-
- decoder->ver = rom_ver;
-
- v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n",
- client->name, decoder->ver,
- client->addr << 1, client->adapter->name);
- return 0;
-}
-
-/**
- * tvp514x_querystd() - V4L2 decoder interface handler for querystd
- * @sd: pointer to standard V4L2 sub-device structure
- * @std_id: standard V4L2 std_id ioctl enum
- *
- * Returns the current standard detected by TVP5146/47. If no active input is
- * detected then *std_id is set to 0 and the function returns 0.
- */
-static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- enum tvp514x_std current_std;
- enum tvp514x_input input_sel;
- u8 sync_lock_status, lock_mask;
-
- if (std_id == NULL)
- return -EINVAL;
-
- *std_id = V4L2_STD_UNKNOWN;
-
- /* query the current standard */
- current_std = tvp514x_query_current_std(sd);
- if (current_std == STD_INVALID)
- return 0;
-
- input_sel = decoder->input;
-
- switch (input_sel) {
- case INPUT_CVBS_VI1A:
- case INPUT_CVBS_VI1B:
- case INPUT_CVBS_VI1C:
- case INPUT_CVBS_VI2A:
- case INPUT_CVBS_VI2B:
- case INPUT_CVBS_VI2C:
- case INPUT_CVBS_VI3A:
- case INPUT_CVBS_VI3B:
- case INPUT_CVBS_VI3C:
- case INPUT_CVBS_VI4A:
- lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
- STATUS_HORZ_SYNC_LOCK_BIT |
- STATUS_VIRT_SYNC_LOCK_BIT;
- break;
-
- case INPUT_SVIDEO_VI2A_VI1A:
- case INPUT_SVIDEO_VI2B_VI1B:
- case INPUT_SVIDEO_VI2C_VI1C:
- case INPUT_SVIDEO_VI2A_VI3A:
- case INPUT_SVIDEO_VI2B_VI3B:
- case INPUT_SVIDEO_VI2C_VI3C:
- case INPUT_SVIDEO_VI4A_VI1A:
- case INPUT_SVIDEO_VI4A_VI1B:
- case INPUT_SVIDEO_VI4A_VI1C:
- case INPUT_SVIDEO_VI4A_VI3A:
- case INPUT_SVIDEO_VI4A_VI3B:
- case INPUT_SVIDEO_VI4A_VI3C:
- lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
- STATUS_VIRT_SYNC_LOCK_BIT;
- break;
- /*Need to add other interfaces*/
- default:
- return -EINVAL;
- }
- /* check whether signal is locked */
- sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1);
- if (lock_mask != (sync_lock_status & lock_mask))
- return 0; /* No input detected */
-
- *std_id = decoder->std_list[current_std].standard.id;
-
- v4l2_dbg(1, debug, sd, "Current STD: %s\n",
- decoder->std_list[current_std].standard.name);
- return 0;
-}
-
-/**
- * tvp514x_s_std() - V4L2 decoder interface handler for s_std
- * @sd: pointer to standard V4L2 sub-device structure
- * @std_id: standard V4L2 v4l2_std_id ioctl enum
- *
- * If std_id is supported, sets the requested standard. Otherwise, returns
- * -EINVAL
- */
-static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- int err, i;
-
- for (i = 0; i < decoder->num_stds; i++)
- if (std_id & decoder->std_list[i].standard.id)
- break;
-
- if ((i == decoder->num_stds) || (i == STD_INVALID))
- return -EINVAL;
-
- err = tvp514x_write_reg(sd, REG_VIDEO_STD,
- decoder->std_list[i].video_std);
- if (err)
- return err;
-
- decoder->current_std = i;
- decoder->tvp514x_regs[REG_VIDEO_STD].val =
- decoder->std_list[i].video_std;
-
- v4l2_dbg(1, debug, sd, "Standard set to: %s\n",
- decoder->std_list[i].standard.name);
- return 0;
-}
-
-/**
- * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing
- * @sd: pointer to standard V4L2 sub-device structure
- * @input: input selector for routing the signal
- * @output: output selector for routing the signal
- * @config: config value. Not used
- *
- * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
- * the input is not supported or there is no active signal present in the
- * selected input.
- */
-static int tvp514x_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- int err;
- enum tvp514x_input input_sel;
- enum tvp514x_output output_sel;
- u8 sync_lock_status, lock_mask;
- int try_count = LOCK_RETRY_COUNT;
-
- if ((input >= INPUT_INVALID) ||
- (output >= OUTPUT_INVALID))
- /* Index out of bound */
- return -EINVAL;
-
- /*
- * For the sequence streamon -> streamoff and again s_input
- * it fails to lock the signal, since streamoff puts TVP514x
- * into power off state which leads to failure in sub-sequent s_input.
- *
- * So power up the TVP514x device here, since it is important to lock
- * the signal at this stage.
- */
- if (!decoder->streaming)
- tvp514x_s_stream(sd, 1);
-
- input_sel = input;
- output_sel = output;
-
- err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel);
- if (err)
- return err;
-
- output_sel |= tvp514x_read_reg(sd,
- REG_OUTPUT_FORMATTER1) & 0x7;
- err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1,
- output_sel);
- if (err)
- return err;
-
- decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel;
- decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel;
-
- /* Clear status */
- msleep(LOCK_RETRY_DELAY);
- err =
- tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01);
- if (err)
- return err;
-
- switch (input_sel) {
- case INPUT_CVBS_VI1A:
- case INPUT_CVBS_VI1B:
- case INPUT_CVBS_VI1C:
- case INPUT_CVBS_VI2A:
- case INPUT_CVBS_VI2B:
- case INPUT_CVBS_VI2C:
- case INPUT_CVBS_VI3A:
- case INPUT_CVBS_VI3B:
- case INPUT_CVBS_VI3C:
- case INPUT_CVBS_VI4A:
- lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
- STATUS_HORZ_SYNC_LOCK_BIT |
- STATUS_VIRT_SYNC_LOCK_BIT;
- break;
-
- case INPUT_SVIDEO_VI2A_VI1A:
- case INPUT_SVIDEO_VI2B_VI1B:
- case INPUT_SVIDEO_VI2C_VI1C:
- case INPUT_SVIDEO_VI2A_VI3A:
- case INPUT_SVIDEO_VI2B_VI3B:
- case INPUT_SVIDEO_VI2C_VI3C:
- case INPUT_SVIDEO_VI4A_VI1A:
- case INPUT_SVIDEO_VI4A_VI1B:
- case INPUT_SVIDEO_VI4A_VI1C:
- case INPUT_SVIDEO_VI4A_VI3A:
- case INPUT_SVIDEO_VI4A_VI3B:
- case INPUT_SVIDEO_VI4A_VI3C:
- lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
- STATUS_VIRT_SYNC_LOCK_BIT;
- break;
- /* Need to add other interfaces*/
- default:
- return -EINVAL;
- }
-
- while (try_count-- > 0) {
- /* Allow decoder to sync up with new input */
- msleep(LOCK_RETRY_DELAY);
-
- sync_lock_status = tvp514x_read_reg(sd,
- REG_STATUS1);
- if (lock_mask == (sync_lock_status & lock_mask))
- /* Input detected */
- break;
- }
-
- if (try_count < 0)
- return -EINVAL;
-
- decoder->input = input;
- decoder->output = output;
-
- v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel);
-
- return 0;
-}
-
-/**
- * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl
- * @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_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = to_sd(ctrl);
- struct tvp514x_decoder *decoder = to_decoder(sd);
- int err = -EINVAL, value;
-
- value = ctrl->val;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value);
- if (!err)
- decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
- break;
- case V4L2_CID_CONTRAST:
- err = tvp514x_write_reg(sd, REG_CONTRAST, value);
- if (!err)
- decoder->tvp514x_regs[REG_CONTRAST].val = value;
- break;
- case V4L2_CID_SATURATION:
- err = tvp514x_write_reg(sd, REG_SATURATION, 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;
- err = tvp514x_write_reg(sd, REG_HUE, value);
- if (!err)
- decoder->tvp514x_regs[REG_HUE].val = value;
- break;
- case V4L2_CID_AUTOGAIN:
- err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c);
- if (!err)
- decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
- break;
- }
-
- v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n",
- ctrl->id, ctrl->val);
- return err;
-}
-
-/**
- * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt
- * @sd: pointer to standard V4L2 sub-device structure
- * @index: index of pixelcode to retrieve
- * @code: receives the pixelcode
- *
- * Enumerates supported mediabus formats
- */
-static int
-tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index)
- return -EINVAL;
-
- *code = V4L2_MBUS_FMT_YUYV10_2X10;
- return 0;
-}
-
-/**
- * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt
- * @sd: pointer to standard V4L2 sub-device structure
- * @f: pointer to the mediabus format structure
- *
- * Negotiates the image capture size and mediabus format.
- */
-static int
-tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- enum tvp514x_std current_std;
-
- if (f == NULL)
- return -EINVAL;
-
- /* Calculate height and width based on current standard */
- current_std = decoder->current_std;
-
- f->code = V4L2_MBUS_FMT_YUYV10_2X10;
- f->width = decoder->std_list[current_std].width;
- f->height = decoder->std_list[current_std].height;
- f->field = V4L2_FIELD_INTERLACED;
- f->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n",
- f->width, f->height);
- return 0;
-}
-
-/**
- * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm
- * @sd: pointer to standard V4L2 sub-device structure
- * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
- *
- * Returns the decoder's video CAPTURE parameters.
- */
-static int
-tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- struct v4l2_captureparm *cparm;
- enum tvp514x_std current_std;
-
- if (a == NULL)
- return -EINVAL;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- /* only capture is supported */
- return -EINVAL;
-
- /* get the current standard */
- current_std = decoder->current_std;
-
- cparm = &a->parm.capture;
- cparm->capability = V4L2_CAP_TIMEPERFRAME;
- cparm->timeperframe =
- decoder->std_list[current_std].standard.frameperiod;
-
- return 0;
-}
-
-/**
- * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm
- * @sd: pointer to standard V4L2 sub-device structure
- * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
- *
- * Configures the decoder to use the input parameters, if possible. If
- * not possible, returns the appropriate error code.
- */
-static int
-tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
-{
- struct tvp514x_decoder *decoder = to_decoder(sd);
- struct v4l2_fract *timeperframe;
- enum tvp514x_std current_std;
-
- if (a == NULL)
- return -EINVAL;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- /* only capture is supported */
- return -EINVAL;
-
- timeperframe = &a->parm.capture.timeperframe;
-
- /* get the current standard */
- current_std = decoder->current_std;
-
- *timeperframe =
- decoder->std_list[current_std].standard.frameperiod;
-
- return 0;
-}
-
-/**
- * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream
- * @sd: pointer to standard V4L2 sub-device structure
- * @enable: streaming enable or disable
- *
- * Sets streaming to enable or disable, if possible.
- */
-static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
-{
- int err = 0;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tvp514x_decoder *decoder = to_decoder(sd);
-
- if (decoder->streaming == enable)
- return 0;
-
- switch (enable) {
- case 0:
- {
- /* Power Down Sequence */
- err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01);
- if (err) {
- v4l2_err(sd, "Unable to turn off decoder\n");
- return err;
- }
- decoder->streaming = enable;
- break;
- }
- case 1:
- {
- struct tvp514x_reg *int_seq = (struct tvp514x_reg *)
- client->driver->id_table->driver_data;
-
- /* Power Up Sequence */
- err = tvp514x_write_regs(sd, int_seq);
- if (err) {
- v4l2_err(sd, "Unable to turn on decoder\n");
- return err;
- }
- /* Detect if not already detected */
- err = tvp514x_detect(sd, decoder);
- if (err) {
- v4l2_err(sd, "Unable to detect decoder\n");
- return err;
- }
- err = tvp514x_configure(sd, decoder);
- if (err) {
- v4l2_err(sd, "Unable to configure decoder\n");
- return err;
- }
- decoder->streaming = enable;
- break;
- }
- default:
- err = -ENODEV;
- break;
- }
-
- return err;
-}
-
-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,
-};
-
-static const struct v4l2_subdev_video_ops tvp514x_video_ops = {
- .s_routing = tvp514x_s_routing,
- .querystd = tvp514x_querystd,
- .enum_mbus_fmt = tvp514x_enum_mbus_fmt,
- .g_mbus_fmt = tvp514x_mbus_fmt,
- .try_mbus_fmt = tvp514x_mbus_fmt,
- .s_mbus_fmt = tvp514x_mbus_fmt,
- .g_parm = tvp514x_g_parm,
- .s_parm = tvp514x_s_parm,
- .s_stream = tvp514x_s_stream,
-};
-
-static const struct v4l2_subdev_ops tvp514x_ops = {
- .core = &tvp514x_core_ops,
- .video = &tvp514x_video_ops,
-};
-
-static struct tvp514x_decoder tvp514x_dev = {
- .streaming = 0,
- .current_std = STD_NTSC_MJ,
- .std_list = tvp514x_std_list,
- .num_stds = ARRAY_SIZE(tvp514x_std_list),
-
-};
-
-/**
- * tvp514x_probe() - decoder driver i2c probe handler
- * @client: i2c driver client device structure
- * @id: i2c driver id table
- *
- * Register decoder as an i2c client device and V4L2
- * device.
- */
-static int
-tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
-{
- struct tvp514x_decoder *decoder;
- struct v4l2_subdev *sd;
-
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -EIO;
-
- if (!client->dev.platform_data) {
- v4l2_err(client, "No platform data!!\n");
- return -ENODEV;
- }
-
- decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
- if (!decoder)
- return -ENOMEM;
-
- /* Initialize the tvp514x_decoder with default configuration */
- *decoder = tvp514x_dev;
- /* Copy default register configuration */
- memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default,
- sizeof(tvp514x_reg_list_default));
-
- /* Copy board specific information here */
- decoder->pdata = client->dev.platform_data;
-
- /**
- * Fetch platform specific data, and configure the
- * tvp514x_reg_list[] accordingly. Since this is one
- * time configuration, no need to preserve.
- */
- decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |=
- (decoder->pdata->clk_polarity << 1);
- decoder->tvp514x_regs[REG_SYNC_CONTROL].val |=
- ((decoder->pdata->hs_polarity << 2) |
- (decoder->pdata->vs_polarity << 3));
- /* Set default standard to auto */
- decoder->tvp514x_regs[REG_VIDEO_STD].val =
- VIDEO_STD_AUTO_SWITCH_BIT;
-
- /* Register with V4L2 layer as slave device */
- 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;
-
-}
-
-/**
- * tvp514x_remove() - decoder driver i2c remove handler
- * @client: i2c driver client device structure
- *
- * Unregister decoder as an i2c client device and V4L2
- * device. Complement of tvp514x_probe().
- */
-static int tvp514x_remove(struct i2c_client *client)
-{
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct tvp514x_decoder *decoder = to_decoder(sd);
-
- v4l2_device_unregister_subdev(sd);
- v4l2_ctrl_handler_free(&decoder->hdl);
- kfree(decoder);
- return 0;
-}
-/* TVP5146 Init/Power on Sequence */
-static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
- {TOK_WRITE, REG_OPERATION_MODE, 0x01},
- {TOK_WRITE, REG_OPERATION_MODE, 0x00},
- {TOK_TERM, 0, 0},
-};
-
-/* TVP5147 Init/Power on Sequence */
-static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
- {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
- {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
- {TOK_WRITE, REG_OPERATION_MODE, 0x01},
- {TOK_WRITE, REG_OPERATION_MODE, 0x00},
- {TOK_TERM, 0, 0},
-};
-
-/* TVP5146M2/TVP5147M1 Init/Power on Sequence */
-static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
- {TOK_WRITE, REG_OPERATION_MODE, 0x01},
- {TOK_WRITE, REG_OPERATION_MODE, 0x00},
- {TOK_TERM, 0, 0},
-};
-
-/**
- * I2C Device Table -
- *
- * name - Name of the actual device/chip.
- * driver_data - Driver data
- */
-static const struct i2c_device_id tvp514x_id[] = {
- {"tvp5146", (unsigned long)tvp5146_init_reg_seq},
- {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq},
- {"tvp5147", (unsigned long)tvp5147_init_reg_seq},
- {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq},
- {},
-};
-
-MODULE_DEVICE_TABLE(i2c, tvp514x_id);
-
-static struct i2c_driver tvp514x_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = TVP514X_MODULE_NAME,
- },
- .probe = tvp514x_probe,
- .remove = tvp514x_remove,
- .id_table = tvp514x_id,
-};
-
-module_i2c_driver(tvp514x_driver);
diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h
deleted file mode 100644
index 18f29ad0dfe2..000000000000
--- a/drivers/media/video/tvp514x_regs.h
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * drivers/media/video/tvp514x_regs.h
- *
- * Copyright (C) 2008 Texas Instruments Inc
- * Author: Vaibhav Hiremath <hvaibhav@ti.com>
- *
- * Contributors:
- * Sivaraj R <sivaraj@ti.com>
- * Brijesh R Jadav <brijesh.j@ti.com>
- * Hardik Shah <hardik.shah@ti.com>
- * Manjunath Hadli <mrh@ti.com>
- * Karicheri Muralidharan <m-karicheri2@ti.com>
- *
- * This package 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef _TVP514X_REGS_H
-#define _TVP514X_REGS_H
-
-/*
- * TVP5146/47 registers
- */
-#define REG_INPUT_SEL (0x00)
-#define REG_AFE_GAIN_CTRL (0x01)
-#define REG_VIDEO_STD (0x02)
-#define REG_OPERATION_MODE (0x03)
-#define REG_AUTOSWITCH_MASK (0x04)
-
-#define REG_COLOR_KILLER (0x05)
-#define REG_LUMA_CONTROL1 (0x06)
-#define REG_LUMA_CONTROL2 (0x07)
-#define REG_LUMA_CONTROL3 (0x08)
-
-#define REG_BRIGHTNESS (0x09)
-#define REG_CONTRAST (0x0A)
-#define REG_SATURATION (0x0B)
-#define REG_HUE (0x0C)
-
-#define REG_CHROMA_CONTROL1 (0x0D)
-#define REG_CHROMA_CONTROL2 (0x0E)
-
-/* 0x0F Reserved */
-
-#define REG_COMP_PR_SATURATION (0x10)
-#define REG_COMP_Y_CONTRAST (0x11)
-#define REG_COMP_PB_SATURATION (0x12)
-
-/* 0x13 Reserved */
-
-#define REG_COMP_Y_BRIGHTNESS (0x14)
-
-/* 0x15 Reserved */
-
-#define REG_AVID_START_PIXEL_LSB (0x16)
-#define REG_AVID_START_PIXEL_MSB (0x17)
-#define REG_AVID_STOP_PIXEL_LSB (0x18)
-#define REG_AVID_STOP_PIXEL_MSB (0x19)
-
-#define REG_HSYNC_START_PIXEL_LSB (0x1A)
-#define REG_HSYNC_START_PIXEL_MSB (0x1B)
-#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
-#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
-
-#define REG_VSYNC_START_LINE_LSB (0x1E)
-#define REG_VSYNC_START_LINE_MSB (0x1F)
-#define REG_VSYNC_STOP_LINE_LSB (0x20)
-#define REG_VSYNC_STOP_LINE_MSB (0x21)
-
-#define REG_VBLK_START_LINE_LSB (0x22)
-#define REG_VBLK_START_LINE_MSB (0x23)
-#define REG_VBLK_STOP_LINE_LSB (0x24)
-#define REG_VBLK_STOP_LINE_MSB (0x25)
-
-/* 0x26 - 0x27 Reserved */
-
-#define REG_FAST_SWTICH_CONTROL (0x28)
-
-/* 0x29 Reserved */
-
-#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
-
-/* 0x2B Reserved */
-
-#define REG_SCART_DELAY (0x2C)
-#define REG_CTI_DELAY (0x2D)
-#define REG_CTI_CONTROL (0x2E)
-
-/* 0x2F - 0x31 Reserved */
-
-#define REG_SYNC_CONTROL (0x32)
-#define REG_OUTPUT_FORMATTER1 (0x33)
-#define REG_OUTPUT_FORMATTER2 (0x34)
-#define REG_OUTPUT_FORMATTER3 (0x35)
-#define REG_OUTPUT_FORMATTER4 (0x36)
-#define REG_OUTPUT_FORMATTER5 (0x37)
-#define REG_OUTPUT_FORMATTER6 (0x38)
-#define REG_CLEAR_LOST_LOCK (0x39)
-
-#define REG_STATUS1 (0x3A)
-#define REG_STATUS2 (0x3B)
-
-#define REG_AGC_GAIN_STATUS_LSB (0x3C)
-#define REG_AGC_GAIN_STATUS_MSB (0x3D)
-
-/* 0x3E Reserved */
-
-#define REG_VIDEO_STD_STATUS (0x3F)
-#define REG_GPIO_INPUT1 (0x40)
-#define REG_GPIO_INPUT2 (0x41)
-
-/* 0x42 - 0x45 Reserved */
-
-#define REG_AFE_COARSE_GAIN_CH1 (0x46)
-#define REG_AFE_COARSE_GAIN_CH2 (0x47)
-#define REG_AFE_COARSE_GAIN_CH3 (0x48)
-#define REG_AFE_COARSE_GAIN_CH4 (0x49)
-
-#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
-#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
-#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
-#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
-#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
-#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
-#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
-#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
-
-/* 0x52 - 0x68 Reserved */
-
-#define REG_FBIT_VBIT_CONTROL1 (0x69)
-
-/* 0x6A - 0x6B Reserved */
-
-#define REG_BACKEND_AGC_CONTROL (0x6C)
-
-/* 0x6D - 0x6E Reserved */
-
-#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
-#define REG_ROM_VERSION (0x70)
-
-/* 0x71 - 0x73 Reserved */
-
-#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
-#define REG_FBIT_VBIT_CONTROL2 (0x75)
-#define REG_VCR_TRICK_MODE_CONTROL (0x76)
-#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
-#define REG_AGC_INCREMENT_SPEED (0x78)
-#define REG_AGC_INCREMENT_DELAY (0x79)
-
-/* 0x7A - 0x7F Reserved */
-
-#define REG_CHIP_ID_MSB (0x80)
-#define REG_CHIP_ID_LSB (0x81)
-
-/* 0x82 Reserved */
-
-#define REG_CPLL_SPEED_CONTROL (0x83)
-
-/* 0x84 - 0x96 Reserved */
-
-#define REG_STATUS_REQUEST (0x97)
-
-/* 0x98 - 0x99 Reserved */
-
-#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
-#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
-
-/* 0x9C - 0x9D Reserved */
-
-#define REG_AGC_DECREMENT_DELAY (0x9E)
-
-/* 0x9F - 0xB0 Reserved */
-
-#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
-#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
-#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
-#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
-#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
-#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
-#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
-#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
-#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
-#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
-#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
-#define REG_VDP_FIFO_WORD_COUNT (0xBC)
-#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
-
-/* 0xBE Reserved */
-
-#define REG_VDP_FIFO_RESET (0xBF)
-#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
-#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
-#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
-#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
-
-/* 0xC4 - 0xD5 Reserved */
-
-#define REG_VDP_LINE_START (0xD6)
-#define REG_VDP_LINE_STOP (0xD7)
-#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
-#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
-#define REG_VDP_FULL_FIELD_MODE (0xDA)
-
-/* 0xDB - 0xDF Reserved */
-
-#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
-#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
-#define REG_FIFO_READ_DATA (0xE2)
-
-/* 0xE3 - 0xE7 Reserved */
-
-#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
-#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
-#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
-
-/* 0xEB - 0xEF Reserved */
-
-#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
-#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
-#define REG_INTERRUPT_STATUS0 (0xF2)
-#define REG_INTERRUPT_STATUS1 (0xF3)
-#define REG_INTERRUPT_MASK0 (0xF4)
-#define REG_INTERRUPT_MASK1 (0xF5)
-#define REG_INTERRUPT_CLEAR0 (0xF6)
-#define REG_INTERRUPT_CLEAR1 (0xF7)
-
-/* 0xF8 - 0xFF Reserved */
-
-/*
- * Mask and bit definitions of TVP5146/47 registers
- */
-/* The ID values we are looking for */
-#define TVP514X_CHIP_ID_MSB (0x51)
-#define TVP5146_CHIP_ID_LSB (0x46)
-#define TVP5147_CHIP_ID_LSB (0x47)
-
-#define VIDEO_STD_MASK (0x07)
-#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
-#define VIDEO_STD_NTSC_MJ_BIT (0x01)
-#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
-#define VIDEO_STD_PAL_M_BIT (0x03)
-#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
-#define VIDEO_STD_NTSC_4_43_BIT (0x05)
-#define VIDEO_STD_SECAM_BIT (0x06)
-#define VIDEO_STD_PAL_60_BIT (0x07)
-
-/*
- * Status bit
- */
-#define STATUS_TV_VCR_BIT (1<<0)
-#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
-#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
-#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
-#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
-#define STATUS_FEILD_RATE_BIT (1<<5)
-#define STATUS_LINE_ALTERNATING_BIT (1<<6)
-#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
-
-/* Tokens for register write */
-#define TOK_WRITE (0) /* token for write operation */
-#define TOK_TERM (1) /* terminating token */
-#define TOK_DELAY (2) /* delay token for reg list */
-#define TOK_SKIP (3) /* token to skip a register */
-/**
- * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
- * @token - Token: TOK_WRITE, TOK_TERM etc..
- * @reg - Register offset
- * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
- */
-struct tvp514x_reg {
- u8 token;
- u8 reg;
- u32 val;
-};
-
-#endif /* ifndef _TVP514X_REGS_H */
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
deleted file mode 100644
index a751b6c146fd..000000000000
--- a/drivers/media/video/tvp5150.c
+++ /dev/null
@@ -1,1274 +0,0 @@
-/*
- * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver
- *
- * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org)
- * This code is placed under the terms of the GNU General Public License v2
- */
-
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <linux/videodev2.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <media/v4l2-device.h>
-#include <media/tvp5150.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-ctrls.h>
-
-#include "tvp5150_reg.h"
-
-#define TVP5150_H_MAX 720
-#define TVP5150_V_MAX_525_60 480
-#define TVP5150_V_MAX_OTHERS 576
-#define TVP5150_MAX_CROP_LEFT 511
-#define TVP5150_MAX_CROP_TOP 127
-#define TVP5150_CROP_SHIFT 2
-
-MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
-MODULE_AUTHOR("Mauro Carvalho Chehab");
-MODULE_LICENSE("GPL");
-
-
-static int debug;
-module_param(debug, int, 0);
-MODULE_PARM_DESC(debug, "Debug level (0-2)");
-
-struct tvp5150 {
- struct v4l2_subdev sd;
- struct v4l2_ctrl_handler hdl;
- struct v4l2_rect rect;
-
- v4l2_std_id norm; /* Current set standard */
- u32 input;
- u32 output;
- int enable;
-};
-
-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);
- unsigned char buffer[1];
- int rc;
-
- buffer[0] = addr;
-
- rc = i2c_master_send(c, buffer, 1);
- if (rc < 0) {
- v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- return rc;
- }
-
- msleep(10);
-
- rc = i2c_master_recv(c, buffer, 1);
- if (rc < 0) {
- v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc);
- return rc;
- }
-
- v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]);
-
- return (buffer[0]);
-}
-
-static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr,
- unsigned char value)
-{
- struct i2c_client *c = v4l2_get_subdevdata(sd);
- unsigned char buffer[2];
- int rc;
-
- buffer[0] = addr;
- buffer[1] = value;
- v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]);
- if (2 != (rc = i2c_master_send(c, buffer, 2)))
- v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc);
-}
-
-static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init,
- const u8 end, int max_line)
-{
- int i = 0;
-
- while (init != (u8)(end + 1)) {
- if ((i % max_line) == 0) {
- if (i > 0)
- printk("\n");
- printk("tvp5150: %s reg 0x%02x = ", s, init);
- }
- printk("%02x ", tvp5150_read(sd, init));
-
- init++;
- i++;
- }
- printk("\n");
-}
-
-static int tvp5150_log_status(struct v4l2_subdev *sd)
-{
- printk("tvp5150: Video input source selection #1 = 0x%02x\n",
- tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1));
- printk("tvp5150: Analog channel controls = 0x%02x\n",
- tvp5150_read(sd, TVP5150_ANAL_CHL_CTL));
- printk("tvp5150: Operation mode controls = 0x%02x\n",
- tvp5150_read(sd, TVP5150_OP_MODE_CTL));
- printk("tvp5150: Miscellaneous controls = 0x%02x\n",
- tvp5150_read(sd, TVP5150_MISC_CTL));
- printk("tvp5150: Autoswitch mask= 0x%02x\n",
- tvp5150_read(sd, TVP5150_AUTOSW_MSK));
- printk("tvp5150: Color killer threshold control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL));
- printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n",
- tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1),
- tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2),
- tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3));
- printk("tvp5150: Brightness control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_BRIGHT_CTL));
- printk("tvp5150: Color saturation control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_SATURATION_CTL));
- printk("tvp5150: Hue control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_HUE_CTL));
- printk("tvp5150: Contrast control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_CONTRAST_CTL));
- printk("tvp5150: Outputs and data rates select = 0x%02x\n",
- tvp5150_read(sd, TVP5150_DATA_RATE_SEL));
- printk("tvp5150: Configuration shared pins = 0x%02x\n",
- tvp5150_read(sd, TVP5150_CONF_SHARED_PIN));
- printk("tvp5150: Active video cropping start = 0x%02x%02x\n",
- tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB),
- tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB));
- printk("tvp5150: Active video cropping stop = 0x%02x%02x\n",
- tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB),
- tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB));
- printk("tvp5150: Genlock/RTC = 0x%02x\n",
- tvp5150_read(sd, TVP5150_GENLOCK));
- printk("tvp5150: Horizontal sync start = 0x%02x\n",
- tvp5150_read(sd, TVP5150_HORIZ_SYNC_START));
- printk("tvp5150: Vertical blanking start = 0x%02x\n",
- tvp5150_read(sd, TVP5150_VERT_BLANKING_START));
- printk("tvp5150: Vertical blanking stop = 0x%02x\n",
- tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP));
- printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n",
- tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1),
- tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2));
- printk("tvp5150: Interrupt reset register B = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_RESET_REG_B));
- printk("tvp5150: Interrupt enable register B = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B));
- printk("tvp5150: Interrupt configuration register B = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B));
- printk("tvp5150: Video standard = 0x%02x\n",
- tvp5150_read(sd, TVP5150_VIDEO_STD));
- printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n",
- tvp5150_read(sd, TVP5150_CB_GAIN_FACT),
- tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR));
- printk("tvp5150: Macrovision on counter = 0x%02x\n",
- tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR));
- printk("tvp5150: Macrovision off counter = 0x%02x\n",
- tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR));
- printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n",
- (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4);
- printk("tvp5150: Device ID = %02x%02x\n",
- tvp5150_read(sd, TVP5150_MSB_DEV_ID),
- tvp5150_read(sd, TVP5150_LSB_DEV_ID));
- printk("tvp5150: ROM version = (hex) %02x.%02x\n",
- tvp5150_read(sd, TVP5150_ROM_MAJOR_VER),
- tvp5150_read(sd, TVP5150_ROM_MINOR_VER));
- printk("tvp5150: Vertical line count = 0x%02x%02x\n",
- tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB),
- tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB));
- printk("tvp5150: Interrupt status register B = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_STATUS_REG_B));
- printk("tvp5150: Interrupt active register B = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B));
- printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n",
- tvp5150_read(sd, TVP5150_STATUS_REG_1),
- tvp5150_read(sd, TVP5150_STATUS_REG_2),
- tvp5150_read(sd, TVP5150_STATUS_REG_3),
- tvp5150_read(sd, TVP5150_STATUS_REG_4),
- tvp5150_read(sd, TVP5150_STATUS_REG_5));
-
- dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI,
- TVP5150_TELETEXT_FIL1_END, 8);
- dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI,
- TVP5150_TELETEXT_FIL2_END, 8);
-
- printk("tvp5150: Teletext filter enable = 0x%02x\n",
- tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA));
- printk("tvp5150: Interrupt status register A = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_STATUS_REG_A));
- printk("tvp5150: Interrupt enable register A = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A));
- printk("tvp5150: Interrupt configuration = 0x%02x\n",
- tvp5150_read(sd, TVP5150_INT_CONF));
- printk("tvp5150: VDP status register = 0x%02x\n",
- tvp5150_read(sd, TVP5150_VDP_STATUS_REG));
- printk("tvp5150: FIFO word count = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT));
- printk("tvp5150: FIFO interrupt threshold = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD));
- printk("tvp5150: FIFO reset = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FIFO_RESET));
- printk("tvp5150: Line number interrupt = 0x%02x\n",
- tvp5150_read(sd, TVP5150_LINE_NUMBER_INT));
- printk("tvp5150: Pixel alignment register = 0x%02x%02x\n",
- tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH),
- tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW));
- printk("tvp5150: FIFO output control = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL));
- printk("tvp5150: Full field enable = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FULL_FIELD_ENA));
- printk("tvp5150: Full field mode register = 0x%02x\n",
- tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG));
-
- dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI,
- TVP5150_CC_DATA_END, 8);
-
- dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI,
- TVP5150_WSS_DATA_END, 8);
-
- dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI,
- TVP5150_VPS_DATA_END, 8);
-
- dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI,
- TVP5150_VITC_DATA_END, 10);
-
- dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI,
- TVP5150_LINE_MODE_END, 8);
- return 0;
-}
-
-/****************************************************************************
- Basic functions
- ****************************************************************************/
-
-static inline void tvp5150_selmux(struct v4l2_subdev *sd)
-{
- int opmode = 0;
- struct tvp5150 *decoder = to_tvp5150(sd);
- int input = 0;
- int val;
-
- if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable)
- input = 8;
-
- switch (decoder->input) {
- case TVP5150_COMPOSITE1:
- input |= 2;
- /* fall through */
- case TVP5150_COMPOSITE0:
- break;
- case TVP5150_SVIDEO:
- default:
- input |= 1;
- break;
- }
-
- v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i "
- "=> tvp5150 input=%i, opmode=%i\n",
- decoder->input, decoder->output,
- input, opmode);
-
- tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode);
- tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input);
-
- /* Svideo should enable YCrCb output and disable GPCL output
- * For Composite and TV, it should be the reverse
- */
- val = tvp5150_read(sd, TVP5150_MISC_CTL);
- if (val < 0) {
- v4l2_err(sd, "%s: failed with error = %d\n", __func__, val);
- return;
- }
-
- if (decoder->input == TVP5150_SVIDEO)
- val = (val & ~0x40) | 0x10;
- else
- val = (val & ~0x10) | 0x40;
- tvp5150_write(sd, TVP5150_MISC_CTL, val);
-};
-
-struct i2c_reg_value {
- unsigned char reg;
- unsigned char value;
-};
-
-/* Default values as sugested at TVP5150AM1 datasheet */
-static const struct i2c_reg_value tvp5150_init_default[] = {
- { /* 0x00 */
- TVP5150_VD_IN_SRC_SEL_1,0x00
- },
- { /* 0x01 */
- TVP5150_ANAL_CHL_CTL,0x15
- },
- { /* 0x02 */
- TVP5150_OP_MODE_CTL,0x00
- },
- { /* 0x03 */
- TVP5150_MISC_CTL,0x01
- },
- { /* 0x06 */
- TVP5150_COLOR_KIL_THSH_CTL,0x10
- },
- { /* 0x07 */
- TVP5150_LUMA_PROC_CTL_1,0x60
- },
- { /* 0x08 */
- TVP5150_LUMA_PROC_CTL_2,0x00
- },
- { /* 0x09 */
- TVP5150_BRIGHT_CTL,0x80
- },
- { /* 0x0a */
- TVP5150_SATURATION_CTL,0x80
- },
- { /* 0x0b */
- TVP5150_HUE_CTL,0x00
- },
- { /* 0x0c */
- TVP5150_CONTRAST_CTL,0x80
- },
- { /* 0x0d */
- TVP5150_DATA_RATE_SEL,0x47
- },
- { /* 0x0e */
- TVP5150_LUMA_PROC_CTL_3,0x00
- },
- { /* 0x0f */
- TVP5150_CONF_SHARED_PIN,0x08
- },
- { /* 0x11 */
- TVP5150_ACT_VD_CROP_ST_MSB,0x00
- },
- { /* 0x12 */
- TVP5150_ACT_VD_CROP_ST_LSB,0x00
- },
- { /* 0x13 */
- TVP5150_ACT_VD_CROP_STP_MSB,0x00
- },
- { /* 0x14 */
- TVP5150_ACT_VD_CROP_STP_LSB,0x00
- },
- { /* 0x15 */
- TVP5150_GENLOCK,0x01
- },
- { /* 0x16 */
- TVP5150_HORIZ_SYNC_START,0x80
- },
- { /* 0x18 */
- TVP5150_VERT_BLANKING_START,0x00
- },
- { /* 0x19 */
- TVP5150_VERT_BLANKING_STOP,0x00
- },
- { /* 0x1a */
- TVP5150_CHROMA_PROC_CTL_1,0x0c
- },
- { /* 0x1b */
- TVP5150_CHROMA_PROC_CTL_2,0x14
- },
- { /* 0x1c */
- TVP5150_INT_RESET_REG_B,0x00
- },
- { /* 0x1d */
- TVP5150_INT_ENABLE_REG_B,0x00
- },
- { /* 0x1e */
- TVP5150_INTT_CONFIG_REG_B,0x00
- },
- { /* 0x28 */
- TVP5150_VIDEO_STD,0x00
- },
- { /* 0x2e */
- TVP5150_MACROVISION_ON_CTR,0x0f
- },
- { /* 0x2f */
- TVP5150_MACROVISION_OFF_CTR,0x01
- },
- { /* 0xbb */
- TVP5150_TELETEXT_FIL_ENA,0x00
- },
- { /* 0xc0 */
- TVP5150_INT_STATUS_REG_A,0x00
- },
- { /* 0xc1 */
- TVP5150_INT_ENABLE_REG_A,0x00
- },
- { /* 0xc2 */
- TVP5150_INT_CONF,0x04
- },
- { /* 0xc8 */
- TVP5150_FIFO_INT_THRESHOLD,0x80
- },
- { /* 0xc9 */
- TVP5150_FIFO_RESET,0x00
- },
- { /* 0xca */
- TVP5150_LINE_NUMBER_INT,0x00
- },
- { /* 0xcb */
- TVP5150_PIX_ALIGN_REG_LOW,0x4e
- },
- { /* 0xcc */
- TVP5150_PIX_ALIGN_REG_HIGH,0x00
- },
- { /* 0xcd */
- TVP5150_FIFO_OUT_CTRL,0x01
- },
- { /* 0xcf */
- TVP5150_FULL_FIELD_ENA,0x00
- },
- { /* 0xd0 */
- TVP5150_LINE_MODE_INI,0x00
- },
- { /* 0xfc */
- TVP5150_FULL_FIELD_MODE_REG,0x7f
- },
- { /* end of data */
- 0xff,0xff
- }
-};
-
-/* Default values as sugested at TVP5150AM1 datasheet */
-static const struct i2c_reg_value tvp5150_init_enable[] = {
- {
- TVP5150_CONF_SHARED_PIN, 2
- },{ /* Automatic offset and AGC enabled */
- TVP5150_ANAL_CHL_CTL, 0x15
- },{ /* Activate YCrCb output 0x9 or 0xd ? */
- TVP5150_MISC_CTL, 0x6f
- },{ /* Activates video std autodetection for all standards */
- TVP5150_AUTOSW_MSK, 0x0
- },{ /* Default format: 0x47. For 4:2:2: 0x40 */
- TVP5150_DATA_RATE_SEL, 0x47
- },{
- TVP5150_CHROMA_PROC_CTL_1, 0x0c
- },{
- TVP5150_CHROMA_PROC_CTL_2, 0x54
- },{ /* Non documented, but initialized on WinTV USB2 */
- 0x27, 0x20
- },{
- 0xff,0xff
- }
-};
-
-struct tvp5150_vbi_type {
- unsigned int vbi_type;
- unsigned int ini_line;
- unsigned int end_line;
- unsigned int by_field :1;
-};
-
-struct i2c_vbi_ram_value {
- u16 reg;
- struct tvp5150_vbi_type type;
- unsigned char values[16];
-};
-
-/* This struct have the values for each supported VBI Standard
- * by
- tvp5150_vbi_types should follow the same order as vbi_ram_default
- * value 0 means rom position 0x10, value 1 means rom position 0x30
- * and so on. There are 16 possible locations from 0 to 15.
- */
-
-static struct i2c_vbi_ram_value vbi_ram_default[] =
-{
- /* FIXME: Current api doesn't handle all VBI types, those not
- yet supported are placed under #if 0 */
-#if 0
- {0x010, /* Teletext, SECAM, WST System A */
- {V4L2_SLICED_TELETEXT_SECAM,6,23,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26,
- 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 }
- },
-#endif
- {0x030, /* Teletext, PAL, WST System B */
- {V4L2_SLICED_TELETEXT_B,6,22,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b,
- 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 }
- },
-#if 0
- {0x050, /* Teletext, PAL, WST System C */
- {V4L2_SLICED_TELETEXT_PAL_C,6,22,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
- 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
- },
- {0x070, /* Teletext, NTSC, WST System B */
- {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23,
- 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
- },
- {0x090, /* Tetetext, NTSC NABTS System C */
- {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22,
- 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 }
- },
- {0x0b0, /* Teletext, NTSC-J, NABTS System D */
- {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1},
- { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23,
- 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 }
- },
- {0x0d0, /* Closed Caption, PAL/SECAM */
- {V4L2_SLICED_CAPTION_625,22,22,1},
- { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
- 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
- },
-#endif
- {0x0f0, /* Closed Caption, NTSC */
- {V4L2_SLICED_CAPTION_525,21,21,1},
- { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02,
- 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 }
- },
- {0x110, /* Wide Screen Signal, PAL/SECAM */
- {V4L2_SLICED_WSS_625,23,23,1},
- { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42,
- 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 }
- },
-#if 0
- {0x130, /* Wide Screen Signal, NTSC C */
- {V4L2_SLICED_WSS_525,20,20,1},
- { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43,
- 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 }
- },
- {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */
- {V4l2_SLICED_VITC_625,6,22,0},
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
- 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
- },
- {0x170, /* Vertical Interval Timecode (VITC), NTSC */
- {V4l2_SLICED_VITC_525,10,20,0},
- { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49,
- 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 }
- },
-#endif
- {0x190, /* Video Program System (VPS), PAL */
- {V4L2_SLICED_VPS,16,16,0},
- { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d,
- 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 }
- },
- /* 0x1d0 User programmable */
-
- /* End of struct */
- { (u16)-1 }
-};
-
-static int tvp5150_write_inittab(struct v4l2_subdev *sd,
- const struct i2c_reg_value *regs)
-{
- while (regs->reg != 0xff) {
- tvp5150_write(sd, regs->reg, regs->value);
- regs++;
- }
- return 0;
-}
-
-static int tvp5150_vdp_init(struct v4l2_subdev *sd,
- const struct i2c_vbi_ram_value *regs)
-{
- unsigned int i;
-
- /* Disable Full Field */
- tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
-
- /* Before programming, Line mode should be at 0xff */
- for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
- tvp5150_write(sd, i, 0xff);
-
- /* Load Ram Table */
- while (regs->reg != (u16)-1) {
- tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8);
- tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg);
-
- for (i = 0; i < 16; i++)
- tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]);
-
- regs++;
- }
- return 0;
-}
-
-/* Fills VBI capabilities based on i2c_vbi_ram_value struct */
-static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
- struct v4l2_sliced_vbi_cap *cap)
-{
- const struct i2c_vbi_ram_value *regs = vbi_ram_default;
- int line;
-
- v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n");
- memset(cap, 0, sizeof *cap);
-
- while (regs->reg != (u16)-1 ) {
- for (line=regs->type.ini_line;line<=regs->type.end_line;line++) {
- cap->service_lines[0][line] |= regs->type.vbi_type;
- }
- cap->service_set |= regs->type.vbi_type;
-
- regs++;
- }
- return 0;
-}
-
-/* Set vbi processing
- * type - one of tvp5150_vbi_types
- * line - line to gather data
- * fields: bit 0 field1, bit 1, field2
- * flags (default=0xf0) is a bitmask, were set means:
- * bit 7: enable filtering null bytes on CC
- * bit 6: send data also to FIFO
- * bit 5: don't allow data with errors on FIFO
- * bit 4: enable ECC when possible
- * pix_align = pix alignment:
- * LSB = field1
- * MSB = field2
- */
-static int tvp5150_set_vbi(struct v4l2_subdev *sd,
- const struct i2c_vbi_ram_value *regs,
- unsigned int type,u8 flags, int line,
- const int fields)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
- v4l2_std_id std = decoder->norm;
- u8 reg;
- int pos=0;
-
- if (std == V4L2_STD_ALL) {
- v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
- return 0;
- } else if (std & V4L2_STD_625_50) {
- /* Don't follow NTSC Line number convension */
- line += 3;
- }
-
- if (line<6||line>27)
- return 0;
-
- while (regs->reg != (u16)-1 ) {
- if ((type & regs->type.vbi_type) &&
- (line>=regs->type.ini_line) &&
- (line<=regs->type.end_line)) {
- type=regs->type.vbi_type;
- break;
- }
-
- regs++;
- pos++;
- }
- if (regs->reg == (u16)-1)
- return 0;
-
- type=pos | (flags & 0xf0);
- reg=((line-6)<<1)+TVP5150_LINE_MODE_INI;
-
- if (fields&1) {
- tvp5150_write(sd, reg, type);
- }
-
- if (fields&2) {
- tvp5150_write(sd, reg+1, type);
- }
-
- return type;
-}
-
-static int tvp5150_get_vbi(struct v4l2_subdev *sd,
- const struct i2c_vbi_ram_value *regs, int line)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
- v4l2_std_id std = decoder->norm;
- u8 reg;
- int pos, type = 0;
- int i, ret = 0;
-
- if (std == V4L2_STD_ALL) {
- v4l2_err(sd, "VBI can't be configured without knowing number of lines\n");
- return 0;
- } else if (std & V4L2_STD_625_50) {
- /* Don't follow NTSC Line number convension */
- line += 3;
- }
-
- if (line < 6 || line > 27)
- return 0;
-
- reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI;
-
- for (i = 0; i <= 1; i++) {
- ret = tvp5150_read(sd, reg + i);
- if (ret < 0) {
- v4l2_err(sd, "%s: failed with error = %d\n",
- __func__, ret);
- return 0;
- }
- pos = ret & 0x0f;
- if (pos < 0x0f)
- type |= regs[pos].type.vbi_type;
- }
-
- return type;
-}
-
-static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
- int fmt = 0;
-
- decoder->norm = std;
-
- /* First tests should be against specific std */
-
- if (std == V4L2_STD_ALL) {
- fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */
- } else if (std & V4L2_STD_NTSC_443) {
- fmt = VIDEO_STD_NTSC_4_43_BIT;
- } else if (std & V4L2_STD_PAL_M) {
- fmt = VIDEO_STD_PAL_M_BIT;
- } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
- fmt = VIDEO_STD_PAL_COMBINATION_N_BIT;
- } else {
- /* Then, test against generic ones */
- if (std & V4L2_STD_NTSC)
- fmt = VIDEO_STD_NTSC_MJ_BIT;
- else if (std & V4L2_STD_PAL)
- fmt = VIDEO_STD_PAL_BDGHIN_BIT;
- else if (std & V4L2_STD_SECAM)
- fmt = VIDEO_STD_SECAM_BIT;
- }
-
- v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt);
- tvp5150_write(sd, TVP5150_VIDEO_STD, fmt);
- return 0;
-}
-
-static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
-
- if (decoder->norm == std)
- return 0;
-
- /* Change cropping height limits */
- if (std & V4L2_STD_525_60)
- decoder->rect.height = TVP5150_V_MAX_525_60;
- else
- decoder->rect.height = TVP5150_V_MAX_OTHERS;
-
-
- return tvp5150_set_std(sd, std);
-}
-
-static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
-
- /* Initializes TVP5150 to its default values */
- tvp5150_write_inittab(sd, tvp5150_init_default);
-
- /* Initializes VDP registers */
- tvp5150_vdp_init(sd, vbi_ram_default);
-
- /* Selects decoder input */
- tvp5150_selmux(sd);
-
- /* Initializes TVP5150 to stream enabled values */
- tvp5150_write_inittab(sd, tvp5150_init_enable);
-
- /* Initialize image preferences */
- v4l2_ctrl_handler_setup(&decoder->hdl);
-
- tvp5150_set_std(sd, decoder->norm);
- return 0;
-};
-
-static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = to_sd(ctrl);
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val);
- return 0;
- case V4L2_CID_CONTRAST:
- tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val);
- return 0;
- case V4L2_CID_SATURATION:
- tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val);
- return 0;
- case V4L2_CID_HUE:
- tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val);
- return 0;
- }
- return -EINVAL;
-}
-
-static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd)
-{
- int val = tvp5150_read(sd, TVP5150_STATUS_REG_5);
-
- switch (val & 0x0F) {
- case 0x01:
- return V4L2_STD_NTSC;
- case 0x03:
- return V4L2_STD_PAL;
- case 0x05:
- return V4L2_STD_PAL_M;
- case 0x07:
- return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc;
- case 0x09:
- return V4L2_STD_NTSC_443;
- case 0xb:
- return V4L2_STD_SECAM;
- default:
- return V4L2_STD_UNKNOWN;
- }
-}
-
-static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index)
- return -EINVAL;
-
- *code = V4L2_MBUS_FMT_UYVY8_2X8;
- return 0;
-}
-
-static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *f)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
-
- if (f == NULL)
- return -EINVAL;
-
- tvp5150_reset(sd, 0);
-
- f->width = decoder->rect.width;
- f->height = decoder->rect.height;
-
- f->code = V4L2_MBUS_FMT_UYVY8_2X8;
- f->field = V4L2_FIELD_SEQ_TB;
- f->colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width,
- f->height);
- return 0;
-}
-
-static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct v4l2_rect rect = a->c;
- struct tvp5150 *decoder = to_tvp5150(sd);
- v4l2_std_id std;
- int hmax;
-
- v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
- __func__, rect.left, rect.top, rect.width, rect.height);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* tvp5150 has some special limits */
- rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
- rect.width = clamp(rect.width,
- TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
- TVP5150_H_MAX - rect.left);
- rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
-
- /* Calculate height based on current standard */
- if (decoder->norm == V4L2_STD_ALL)
- std = tvp5150_read_std(sd);
- else
- std = decoder->norm;
-
- if (std & V4L2_STD_525_60)
- hmax = TVP5150_V_MAX_525_60;
- else
- hmax = TVP5150_V_MAX_OTHERS;
-
- rect.height = clamp(rect.height,
- hmax - TVP5150_MAX_CROP_TOP - rect.top,
- hmax - rect.top);
-
- tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
- tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
- rect.top + rect.height - hmax);
- tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
- rect.left >> TVP5150_CROP_SHIFT);
- tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
- rect.left | (1 << TVP5150_CROP_SHIFT));
- tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
- (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
- TVP5150_CROP_SHIFT);
- tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
- rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
-
- decoder->rect = rect;
-
- return 0;
-}
-
-static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
-
- a->c = decoder->rect;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
- v4l2_std_id std;
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = TVP5150_H_MAX;
-
- /* Calculate height based on current standard */
- if (decoder->norm == V4L2_STD_ALL)
- std = tvp5150_read_std(sd);
- else
- std = decoder->norm;
-
- if (std & V4L2_STD_525_60)
- a->bounds.height = TVP5150_V_MAX_525_60;
- else
- a->bounds.height = TVP5150_V_MAX_OTHERS;
-
- a->defrect = a->bounds;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-/****************************************************************************
- I2C Command
- ****************************************************************************/
-
-static int tvp5150_s_routing(struct v4l2_subdev *sd,
- u32 input, u32 output, u32 config)
-{
- struct tvp5150 *decoder = to_tvp5150(sd);
-
- decoder->input = input;
- decoder->output = output;
- tvp5150_selmux(sd);
- return 0;
-}
-
-static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt)
-{
- /* this is for capturing 36 raw vbi lines
- if there's a way to cut off the beginning 2 vbi lines
- with the tvp5150 then the vbi line count could be lowered
- to 17 lines/field again, although I couldn't find a register
- which could do that cropping */
- if (fmt->sample_format == V4L2_PIX_FMT_GREY)
- tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70);
- if (fmt->count[0] == 18 && fmt->count[1] == 18) {
- tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00);
- tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01);
- }
- return 0;
-}
-
-static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- int i;
-
- if (svbi->service_set != 0) {
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[1][i] = 0;
- svbi->service_lines[0][i] =
- tvp5150_set_vbi(sd, vbi_ram_default,
- svbi->service_lines[0][i], 0xf0, i, 3);
- }
- /* Enables FIFO */
- tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1);
- } else {
- /* Disables FIFO*/
- tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0);
-
- /* Disable Full Field */
- tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0);
-
- /* Disable Line modes */
- for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++)
- tvp5150_write(sd, i, 0xff);
- }
- return 0;
-}
-
-static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi)
-{
- int i, mask = 0;
-
- memset(svbi, 0, sizeof(*svbi));
-
- for (i = 0; i <= 23; i++) {
- svbi->service_lines[0][i] =
- tvp5150_get_vbi(sd, vbi_ram_default, i);
- mask |= svbi->service_lines[0][i];
- }
- svbi->service_set = mask;
- return 0;
-}
-
-static int tvp5150_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
-{
- int rev;
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 |
- tvp5150_read(sd, TVP5150_ROM_MINOR_VER);
-
- return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150,
- rev);
-}
-
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- int res;
-
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- res = tvp5150_read(sd, reg->reg & 0xff);
- if (res < 0) {
- v4l2_err(sd, "%s: failed with error = %d\n", __func__, res);
- return res;
- }
-
- reg->val = res;
- reg->size = 1;
- return 0;
-}
-
-static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (!v4l2_chip_match_i2c_client(client, &reg->match))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff);
- return 0;
-}
-#endif
-
-static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
-{
- int status = tvp5150_read(sd, 0x88);
-
- vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0;
- return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-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_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,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = tvp5150_g_register,
- .s_register = tvp5150_s_register,
-#endif
-};
-
-static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = {
- .g_tuner = tvp5150_g_tuner,
-};
-
-static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
- .s_routing = tvp5150_s_routing,
- .enum_mbus_fmt = tvp5150_enum_mbus_fmt,
- .s_mbus_fmt = tvp5150_mbus_fmt,
- .try_mbus_fmt = tvp5150_mbus_fmt,
- .g_mbus_fmt = tvp5150_mbus_fmt,
- .s_crop = tvp5150_s_crop,
- .g_crop = tvp5150_g_crop,
- .cropcap = tvp5150_cropcap,
-};
-
-static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
- .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap,
- .g_sliced_fmt = tvp5150_g_sliced_fmt,
- .s_sliced_fmt = tvp5150_s_sliced_fmt,
- .s_raw_fmt = tvp5150_s_raw_fmt,
-};
-
-static const struct v4l2_subdev_ops tvp5150_ops = {
- .core = &tvp5150_core_ops,
- .tuner = &tvp5150_tuner_ops,
- .video = &tvp5150_video_ops,
- .vbi = &tvp5150_vbi_ops,
-};
-
-
-/****************************************************************************
- I2C Client & Driver
- ****************************************************************************/
-
-static int tvp5150_probe(struct i2c_client *c,
- const struct i2c_device_id *id)
-{
- struct tvp5150 *core;
- struct v4l2_subdev *sd;
- int tvp5150_id[4];
- int i, res;
-
- /* Check if the adapter supports the needed features */
- if (!i2c_check_functionality(c->adapter,
- I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
- return -EIO;
-
- core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL);
- if (!core) {
- return -ENOMEM;
- }
- sd = &core->sd;
- v4l2_i2c_subdev_init(sd, c, &tvp5150_ops);
-
- /*
- * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID,
- * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER
- */
- for (i = 0; i < 4; i++) {
- res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i);
- if (res < 0)
- goto free_core;
- tvp5150_id[i] = res;
- }
-
- v4l_info(c, "chip found @ 0x%02x (%s)\n",
- c->addr << 1, c->adapter->name);
-
- if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */
- v4l2_info(sd, "tvp%02x%02xam1 detected.\n",
- tvp5150_id[0], tvp5150_id[1]);
-
- /* ITU-T BT.656.4 timing */
- tvp5150_write(sd, TVP5150_REV_SELECT, 0);
- } else {
- /* Is TVP5150A */
- if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) {
- v4l2_info(sd, "tvp%02x%02xa detected.\n",
- tvp5150_id[2], tvp5150_id[3]);
- } else {
- v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n",
- tvp5150_id[2], tvp5150_id[3]);
- v4l2_info(sd, "*** Rom ver is %d.%d\n",
- tvp5150_id[2], tvp5150_id[3]);
- }
- }
-
- core->norm = V4L2_STD_ALL; /* Default is autodetect */
- core->input = TVP5150_COMPOSITE1;
- core->enable = 1;
-
- 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) {
- res = core->hdl.error;
- v4l2_ctrl_handler_free(&core->hdl);
- goto free_core;
- }
- v4l2_ctrl_handler_setup(&core->hdl);
-
- /* Default is no cropping */
- core->rect.top = 0;
- if (tvp5150_read_std(sd) & V4L2_STD_525_60)
- core->rect.height = TVP5150_V_MAX_525_60;
- else
- core->rect.height = TVP5150_V_MAX_OTHERS;
- core->rect.left = 0;
- core->rect.width = TVP5150_H_MAX;
-
- if (debug > 1)
- tvp5150_log_status(sd);
- return 0;
-
-free_core:
- kfree(core);
- return res;
-}
-
-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;
-}
-
-/* ----------------------------------------------------------------------- */
-
-static const struct i2c_device_id tvp5150_id[] = {
- { "tvp5150", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tvp5150_id);
-
-static struct i2c_driver tvp5150_driver = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "tvp5150",
- },
- .probe = tvp5150_probe,
- .remove = tvp5150_remove,
- .id_table = tvp5150_id,
-};
-
-module_i2c_driver(tvp5150_driver);
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
deleted file mode 100644
index 9f53eacb66e3..000000000000
--- a/drivers/media/video/tw9910.c
+++ /dev/null
@@ -1,956 +0,0 @@
-/*
- * tw9910 Video Driver
- *
- * Copyright (C) 2008 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on ov772x driver,
- *
- * Copyright (C) 2008 Kuninori Morimoto <morimoto.kuninori@renesas.com>
- * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net>
- * Copyright (C) 2008 Magnus Damm
- * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
- *
- * 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 <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/v4l2-mediabus.h>
-#include <linux/videodev2.h>
-
-#include <media/soc_camera.h>
-#include <media/tw9910.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-subdev.h>
-
-#define GET_ID(val) ((val & 0xF8) >> 3)
-#define GET_REV(val) (val & 0x07)
-
-/*
- * register offset
- */
-#define ID 0x00 /* Product ID Code Register */
-#define STATUS1 0x01 /* Chip Status Register I */
-#define INFORM 0x02 /* Input Format */
-#define OPFORM 0x03 /* Output Format Control Register */
-#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */
-#define OUTCTR1 0x05 /* Output Control I */
-#define ACNTL1 0x06 /* Analog Control Register 1 */
-#define CROP_HI 0x07 /* Cropping Register, High */
-#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */
-#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */
-#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */
-#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */
-#define CNTRL1 0x0C /* Control Register I */
-#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */
-#define SCALE_HI 0x0E /* Scaling Register, High */
-#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */
-#define BRIGHT 0x10 /* BRIGHTNESS Control Register */
-#define CONTRAST 0x11 /* CONTRAST Control Register */
-#define SHARPNESS 0x12 /* SHARPNESS Control Register I */
-#define SAT_U 0x13 /* Chroma (U) Gain Register */
-#define SAT_V 0x14 /* Chroma (V) Gain Register */
-#define HUE 0x15 /* Hue Control Register */
-#define CORING1 0x17
-#define CORING2 0x18 /* Coring and IF compensation */
-#define VBICNTL 0x19 /* VBI Control Register */
-#define ACNTL2 0x1A /* Analog Control 2 */
-#define OUTCTR2 0x1B /* Output Control 2 */
-#define SDT 0x1C /* Standard Selection */
-#define SDTR 0x1D /* Standard Recognition */
-#define TEST 0x1F /* Test Control Register */
-#define CLMPG 0x20 /* Clamping Gain */
-#define IAGC 0x21 /* Individual AGC Gain */
-#define AGCGAIN 0x22 /* AGC Gain */
-#define PEAKWT 0x23 /* White Peak Threshold */
-#define CLMPL 0x24 /* Clamp level */
-#define SYNCT 0x25 /* Sync Amplitude */
-#define MISSCNT 0x26 /* Sync Miss Count Register */
-#define PCLAMP 0x27 /* Clamp Position Register */
-#define VCNTL1 0x28 /* Vertical Control I */
-#define VCNTL2 0x29 /* Vertical Control II */
-#define CKILL 0x2A /* Color Killer Level Control */
-#define COMB 0x2B /* Comb Filter Control */
-#define LDLY 0x2C /* Luma Delay and H Filter Control */
-#define MISC1 0x2D /* Miscellaneous Control I */
-#define LOOP 0x2E /* LOOP Control Register */
-#define MISC2 0x2F /* Miscellaneous Control II */
-#define MVSN 0x30 /* Macrovision Detection */
-#define STATUS2 0x31 /* Chip STATUS II */
-#define HFREF 0x32 /* H monitor */
-#define CLMD 0x33 /* CLAMP MODE */
-#define IDCNTL 0x34 /* ID Detection Control */
-#define CLCNTL1 0x35 /* Clamp Control I */
-#define ANAPLLCTL 0x4C
-#define VBIMIN 0x4D
-#define HSLOWCTL 0x4E
-#define WSS3 0x4F
-#define FILLDATA 0x50
-#define SDID 0x51
-#define DID 0x52
-#define WSS1 0x53
-#define WSS2 0x54
-#define VVBI 0x55
-#define LCTL6 0x56
-#define LCTL7 0x57
-#define LCTL8 0x58
-#define LCTL9 0x59
-#define LCTL10 0x5A
-#define LCTL11 0x5B
-#define LCTL12 0x5C
-#define LCTL13 0x5D
-#define LCTL14 0x5E
-#define LCTL15 0x5F
-#define LCTL16 0x60
-#define LCTL17 0x61
-#define LCTL18 0x62
-#define LCTL19 0x63
-#define LCTL20 0x64
-#define LCTL21 0x65
-#define LCTL22 0x66
-#define LCTL23 0x67
-#define LCTL24 0x68
-#define LCTL25 0x69
-#define LCTL26 0x6A
-#define HSBEGIN 0x6B
-#define HSEND 0x6C
-#define OVSDLY 0x6D
-#define OVSEND 0x6E
-#define VBIDELAY 0x6F
-
-/*
- * register detail
- */
-
-/* INFORM */
-#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */
-#define FC27_FF 0x00 /* 0 : Square pixel mode. */
- /* Must use 24.54MHz for 60Hz field rate */
- /* source or 29.5MHz for 50Hz field rate */
-#define IFSEL_S 0x10 /* 01 : S-video decoding */
-#define IFSEL_C 0x00 /* 00 : Composite video decoding */
- /* Y input video selection */
-#define YSEL_M0 0x00 /* 00 : Mux0 selected */
-#define YSEL_M1 0x04 /* 01 : Mux1 selected */
-#define YSEL_M2 0x08 /* 10 : Mux2 selected */
-#define YSEL_M3 0x10 /* 11 : Mux3 selected */
-
-/* OPFORM */
-#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */
- /* 1 : ITU-R-656 compatible data sequence format */
-#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */
- /* 1 : 16-bit YCrCb 4:2:2 output format.*/
-#define LLCMODE 0x20 /* 1 : LLC output mode. */
- /* 0 : free-run output mode */
-#define AINC 0x10 /* Serial interface auto-indexing control */
- /* 0 : auto-increment */
- /* 1 : non-auto */
-#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */
- /* 0 : Vertical out ctrl by HACTIVE and DVALID */
-#define OEN_TRI_SEL_MASK 0x07
-#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */
-#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */
-#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */
-
-/* OUTCTR1 */
-#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */
-#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */
- /* VS pin output control */
-#define VSSL_VSYNC 0x00 /* 0 : VSYNC */
-#define VSSL_VACT 0x10 /* 1 : VACT */
-#define VSSL_FIELD 0x20 /* 2 : FIELD */
-#define VSSL_VVALID 0x30 /* 3 : VVALID */
-#define VSSL_ZERO 0x70 /* 7 : 0 */
-#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */
-#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/
- /* HS pin output control */
-#define HSSL_HACT 0x00 /* 0 : HACT */
-#define HSSL_HSYNC 0x01 /* 1 : HSYNC */
-#define HSSL_DVALID 0x02 /* 2 : DVALID */
-#define HSSL_HLOCK 0x03 /* 3 : HLOCK */
-#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */
-#define HSSL_ZERO 0x07 /* 7 : 0 */
-
-/* ACNTL1 */
-#define SRESET 0x80 /* resets the device to its default state
- * but all register content remain unchanged.
- * This bit is self-resetting.
- */
-#define ACNTL1_PDN_MASK 0x0e
-#define CLK_PDN 0x08 /* system clock power down */
-#define Y_PDN 0x04 /* Luma ADC power down */
-#define C_PDN 0x02 /* Chroma ADC power down */
-
-/* ACNTL2 */
-#define ACNTL2_PDN_MASK 0x40
-#define PLL_PDN 0x40 /* PLL power down */
-
-/* VBICNTL */
-
-/* RTSEL : control the real time signal output from the MPOUT pin */
-#define RTSEL_MASK 0x07
-#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */
-#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */
-#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */
-#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */
-#define RTSEL_MONO 0x04 /* 0100 = MONO */
-#define RTSEL_DET50 0x05 /* 0101 = DET50 */
-#define RTSEL_FIELD 0x06 /* 0110 = FIELD */
-#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */
-
-/* HSYNC start and end are constant for now */
-#define HSYNC_START 0x0260
-#define HSYNC_END 0x0300
-
-/*
- * structure
- */
-
-struct regval_list {
- unsigned char reg_num;
- unsigned char value;
-};
-
-struct tw9910_scale_ctrl {
- char *name;
- unsigned short width;
- unsigned short height;
- u16 hscale;
- u16 vscale;
-};
-
-struct tw9910_priv {
- struct v4l2_subdev subdev;
- struct tw9910_video_info *info;
- const struct tw9910_scale_ctrl *scale;
- v4l2_std_id norm;
- u32 revision;
-};
-
-static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = {
- {
- .name = "NTSC SQ",
- .width = 640,
- .height = 480,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "NTSC CCIR601",
- .width = 720,
- .height = 480,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "NTSC SQ (CIF)",
- .width = 320,
- .height = 240,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "NTSC CCIR601 (CIF)",
- .width = 360,
- .height = 240,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "NTSC SQ (QCIF)",
- .width = 160,
- .height = 120,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
- {
- .name = "NTSC CCIR601 (QCIF)",
- .width = 180,
- .height = 120,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
-};
-
-static const struct tw9910_scale_ctrl tw9910_pal_scales[] = {
- {
- .name = "PAL SQ",
- .width = 768,
- .height = 576,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "PAL CCIR601",
- .width = 720,
- .height = 576,
- .hscale = 0x0100,
- .vscale = 0x0100,
- },
- {
- .name = "PAL SQ (CIF)",
- .width = 384,
- .height = 288,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "PAL CCIR601 (CIF)",
- .width = 360,
- .height = 288,
- .hscale = 0x0200,
- .vscale = 0x0200,
- },
- {
- .name = "PAL SQ (QCIF)",
- .width = 192,
- .height = 144,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
- {
- .name = "PAL CCIR601 (QCIF)",
- .width = 180,
- .height = 144,
- .hscale = 0x0400,
- .vscale = 0x0400,
- },
-};
-
-/*
- * general function
- */
-static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
-{
- return container_of(i2c_get_clientdata(client), struct tw9910_priv,
- subdev);
-}
-
-static int tw9910_mask_set(struct i2c_client *client, u8 command,
- u8 mask, u8 set)
-{
- s32 val = i2c_smbus_read_byte_data(client, command);
- if (val < 0)
- return val;
-
- val &= ~mask;
- val |= set & mask;
-
- return i2c_smbus_write_byte_data(client, command, val);
-}
-
-static int tw9910_set_scale(struct i2c_client *client,
- const struct tw9910_scale_ctrl *scale)
-{
- int ret;
-
- ret = i2c_smbus_write_byte_data(client, SCALE_HI,
- (scale->vscale & 0x0F00) >> 4 |
- (scale->hscale & 0x0F00) >> 8);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, HSCALE_LO,
- scale->hscale & 0x00FF);
- if (ret < 0)
- return ret;
-
- ret = i2c_smbus_write_byte_data(client, VSCALE_LO,
- scale->vscale & 0x00FF);
-
- return ret;
-}
-
-static int tw9910_set_hsync(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
- int ret;
-
- /* bit 10 - 3 */
- ret = i2c_smbus_write_byte_data(client, HSBEGIN,
- (HSYNC_START & 0x07F8) >> 3);
- if (ret < 0)
- return ret;
-
- /* bit 10 - 3 */
- ret = i2c_smbus_write_byte_data(client, HSEND,
- (HSYNC_END & 0x07F8) >> 3);
- if (ret < 0)
- return ret;
-
- /* So far only revisions 0 and 1 have been seen */
- /* bit 2 - 0 */
- if (1 == priv->revision)
- ret = tw9910_mask_set(client, HSLOWCTL, 0x77,
- (HSYNC_START & 0x0007) << 4 |
- (HSYNC_END & 0x0007));
-
- return ret;
-}
-
-static void tw9910_reset(struct i2c_client *client)
-{
- tw9910_mask_set(client, ACNTL1, SRESET, SRESET);
- msleep(1);
-}
-
-static int tw9910_power(struct i2c_client *client, int enable)
-{
- int ret;
- u8 acntl1;
- u8 acntl2;
-
- if (enable) {
- acntl1 = 0;
- acntl2 = 0;
- } else {
- acntl1 = CLK_PDN | Y_PDN | C_PDN;
- acntl2 = PLL_PDN;
- }
-
- ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1);
- if (ret < 0)
- return ret;
-
- return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2);
-}
-
-static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm,
- u32 width, u32 height)
-{
- const struct tw9910_scale_ctrl *scale;
- const struct tw9910_scale_ctrl *ret = NULL;
- __u32 diff = 0xffffffff, tmp;
- int size, i;
-
- if (norm & V4L2_STD_NTSC) {
- scale = tw9910_ntsc_scales;
- size = ARRAY_SIZE(tw9910_ntsc_scales);
- } else if (norm & V4L2_STD_PAL) {
- scale = tw9910_pal_scales;
- size = ARRAY_SIZE(tw9910_pal_scales);
- } else {
- return NULL;
- }
-
- for (i = 0; i < size; i++) {
- tmp = abs(width - scale[i].width) +
- abs(height - scale[i].height);
- if (tmp < diff) {
- diff = tmp;
- ret = scale + i;
- }
- }
-
- return ret;
-}
-
-/*
- * subdevice operations
- */
-static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- u8 val;
- int ret;
-
- if (!enable) {
- switch (priv->revision) {
- case 0:
- val = OEN_TRI_SEL_ALL_OFF_r0;
- break;
- case 1:
- val = OEN_TRI_SEL_ALL_OFF_r1;
- break;
- default:
- dev_err(&client->dev, "un-supported revision\n");
- return -EINVAL;
- }
- } else {
- val = OEN_TRI_SEL_ALL_ON;
-
- if (!priv->scale) {
- dev_err(&client->dev, "norm select error\n");
- return -EPERM;
- }
-
- dev_dbg(&client->dev, "%s %dx%d\n",
- priv->scale->name,
- priv->scale->width,
- priv->scale->height);
- }
-
- ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val);
- if (ret < 0)
- return ret;
-
- return tw9910_power(client, enable);
-}
-
-static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- *norm = priv->norm;
-
- return 0;
-}
-
-static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL)))
- return -EINVAL;
-
- priv->norm = norm;
-
- return 0;
-}
-
-static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *id)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- id->ident = V4L2_IDENT_TW9910;
- id->revision = priv->revision;
-
- return 0;
-}
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int tw9910_g_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret;
-
- if (reg->reg > 0xff)
- return -EINVAL;
-
- ret = i2c_smbus_read_byte_data(client, reg->reg);
- if (ret < 0)
- return ret;
-
- /*
- * ret = int
- * reg->val = __u64
- */
- reg->val = (__u64)ret;
-
- return 0;
-}
-
-static int tw9910_s_register(struct v4l2_subdev *sd,
- struct v4l2_dbg_register *reg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- if (reg->reg > 0xff ||
- reg->val > 0xff)
- return -EINVAL;
-
- return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
-}
-#endif
-
-static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- int ret = -EINVAL;
- u8 val;
-
- /*
- * select suitable norm
- */
- priv->scale = tw9910_select_norm(priv->norm, *width, *height);
- if (!priv->scale)
- goto tw9910_set_fmt_error;
-
- /*
- * reset hardware
- */
- tw9910_reset(client);
-
- /*
- * set bus width
- */
- val = 0x00;
- if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
- val = LEN;
-
- ret = tw9910_mask_set(client, OPFORM, LEN, val);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * select MPOUT behavior
- */
- switch (priv->info->mpout) {
- case TW9910_MPO_VLOSS:
- val = RTSEL_VLOSS; break;
- case TW9910_MPO_HLOCK:
- val = RTSEL_HLOCK; break;
- case TW9910_MPO_SLOCK:
- val = RTSEL_SLOCK; break;
- case TW9910_MPO_VLOCK:
- val = RTSEL_VLOCK; break;
- case TW9910_MPO_MONO:
- val = RTSEL_MONO; break;
- case TW9910_MPO_DET50:
- val = RTSEL_DET50; break;
- case TW9910_MPO_FIELD:
- val = RTSEL_FIELD; break;
- case TW9910_MPO_RTCO:
- val = RTSEL_RTCO; break;
- default:
- val = 0;
- }
-
- ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * set scale
- */
- ret = tw9910_set_scale(client, priv->scale);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- /*
- * set hsync
- */
- ret = tw9910_set_hsync(client);
- if (ret < 0)
- goto tw9910_set_fmt_error;
-
- *width = priv->scale->width;
- *height = priv->scale->height;
-
- return ret;
-
-tw9910_set_fmt_error:
-
- tw9910_reset(client);
- priv->scale = NULL;
-
- return ret;
-}
-
-static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- a->c.left = 0;
- a->c.top = 0;
- if (priv->norm & V4L2_STD_NTSC) {
- a->c.width = 640;
- a->c.height = 480;
- } else {
- a->c.width = 768;
- a->c.height = 576;
- }
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- return 0;
-}
-
-static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- a->bounds.left = 0;
- a->bounds.top = 0;
- if (priv->norm & V4L2_STD_NTSC) {
- a->bounds.width = 640;
- a->bounds.height = 480;
- } else {
- a->bounds.width = 768;
- a->bounds.height = 576;
- }
- a->defrect = a->bounds;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
-static int tw9910_g_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
-
- if (!priv->scale) {
- priv->scale = tw9910_select_norm(priv->norm, 640, 480);
- if (!priv->scale)
- return -EINVAL;
- }
-
- mf->width = priv->scale->width;
- mf->height = priv->scale->height;
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
- mf->field = V4L2_FIELD_INTERLACED_BT;
-
- return 0;
-}
-
-static int tw9910_s_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- u32 width = mf->width, height = mf->height;
- int ret;
-
- WARN_ON(mf->field != V4L2_FIELD_ANY &&
- mf->field != V4L2_FIELD_INTERLACED_BT);
-
- /*
- * check color format
- */
- if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8)
- return -EINVAL;
-
- mf->colorspace = V4L2_COLORSPACE_JPEG;
-
- ret = tw9910_set_frame(sd, &width, &height);
- if (!ret) {
- mf->width = width;
- mf->height = height;
- }
- return ret;
-}
-
-static int tw9910_try_fmt(struct v4l2_subdev *sd,
- struct v4l2_mbus_framefmt *mf)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct tw9910_priv *priv = to_tw9910(client);
- const struct tw9910_scale_ctrl *scale;
-
- if (V4L2_FIELD_ANY == mf->field) {
- mf->field = V4L2_FIELD_INTERLACED_BT;
- } else if (V4L2_FIELD_INTERLACED_BT != mf->field) {
- dev_err(&client->dev, "Field type %d invalid.\n", mf->field);
- return -EINVAL;
- }
-
- mf->code = V4L2_MBUS_FMT_UYVY8_2X8;
- mf->colorspace = V4L2_COLORSPACE_JPEG;
-
- /*
- * select suitable norm
- */
- scale = tw9910_select_norm(priv->norm, mf->width, mf->height);
- if (!scale)
- return -EINVAL;
-
- mf->width = scale->width;
- mf->height = scale->height;
-
- return 0;
-}
-
-static int tw9910_video_probe(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
- s32 id;
-
- /*
- * tw9910 only use 8 or 16 bit bus width
- */
- if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
- SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
- dev_err(&client->dev, "bus width error\n");
- return -ENODEV;
- }
-
- /*
- * check and show Product ID
- * So far only revisions 0 and 1 have been seen
- */
- id = i2c_smbus_read_byte_data(client, ID);
- priv->revision = GET_REV(id);
- id = GET_ID(id);
-
- if (0x0B != id ||
- 0x01 < priv->revision) {
- dev_err(&client->dev,
- "Product ID error %x:%x\n",
- id, priv->revision);
- return -ENODEV;
- }
-
- dev_info(&client->dev,
- "tw9910 Product ID %0x:%0x\n", id, priv->revision);
-
- priv->norm = V4L2_STD_NTSC;
-
- return 0;
-}
-
-static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
- .g_chip_ident = tw9910_g_chip_ident,
- .s_std = tw9910_s_std,
- .g_std = tw9910_g_std,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .g_register = tw9910_g_register,
- .s_register = tw9910_s_register,
-#endif
-};
-
-static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
- enum v4l2_mbus_pixelcode *code)
-{
- if (index)
- return -EINVAL;
-
- *code = V4L2_MBUS_FMT_UYVY8_2X8;
- return 0;
-}
-
-static int tw9910_g_mbus_config(struct v4l2_subdev *sd,
- struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
-
- cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW |
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW |
- V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg->type = V4L2_MBUS_PARALLEL;
- cfg->flags = soc_camera_apply_board_flags(icl, cfg);
-
- return 0;
-}
-
-static int tw9910_s_mbus_config(struct v4l2_subdev *sd,
- const struct v4l2_mbus_config *cfg)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- u8 val = VSSL_VVALID | HSSL_DVALID;
- unsigned long flags = soc_camera_apply_board_flags(icl, cfg);
-
- /*
- * set OUTCTR1
- *
- * We use VVALID and DVALID signals to control VSYNC and HSYNC
- * outputs, in this mode their polarity is inverted.
- */
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- val |= HSP_HI;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- val |= VSP_HI;
-
- return i2c_smbus_write_byte_data(client, OUTCTR1, val);
-}
-
-static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
- .s_stream = tw9910_s_stream,
- .g_mbus_fmt = tw9910_g_fmt,
- .s_mbus_fmt = tw9910_s_fmt,
- .try_mbus_fmt = tw9910_try_fmt,
- .cropcap = tw9910_cropcap,
- .g_crop = tw9910_g_crop,
- .enum_mbus_fmt = tw9910_enum_fmt,
- .g_mbus_config = tw9910_g_mbus_config,
- .s_mbus_config = tw9910_s_mbus_config,
-};
-
-static struct v4l2_subdev_ops tw9910_subdev_ops = {
- .core = &tw9910_subdev_core_ops,
- .video = &tw9910_subdev_video_ops,
-};
-
-/*
- * i2c_driver function
- */
-
-static int tw9910_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
-
-{
- struct tw9910_priv *priv;
- struct tw9910_video_info *info;
- struct i2c_adapter *adapter =
- to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = soc_camera_i2c_to_link(client);
- int ret;
-
- if (!icl || !icl->priv) {
- dev_err(&client->dev, "TW9910: missing platform data!\n");
- return -EINVAL;
- }
-
- info = icl->priv;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
- dev_err(&client->dev,
- "I2C-Adapter doesn't support "
- "I2C_FUNC_SMBUS_BYTE_DATA\n");
- return -EIO;
- }
-
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- priv->info = info;
-
- v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
-
- ret = tw9910_video_probe(client);
- if (ret)
- kfree(priv);
-
- return ret;
-}
-
-static int tw9910_remove(struct i2c_client *client)
-{
- struct tw9910_priv *priv = to_tw9910(client);
-
- kfree(priv);
- return 0;
-}
-
-static const struct i2c_device_id tw9910_id[] = {
- { "tw9910", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tw9910_id);
-
-static struct i2c_driver tw9910_i2c_driver = {
- .driver = {
- .name = "tw9910",
- },
- .probe = tw9910_probe,
- .remove = tw9910_remove,
- .id_table = tw9910_id,
-};
-
-module_i2c_driver(tw9910_i2c_driver);
-
-MODULE_DESCRIPTION("SoC Camera driver for tw9910");
-MODULE_AUTHOR("Kuninori Morimoto");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig
deleted file mode 100644
index fc24ef05b3f3..000000000000
--- a/drivers/media/video/usbvision/Kconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-config VIDEO_USBVISION
- tristate "USB video devices based on Nogatech NT1003/1004/1005"
- depends on I2C && VIDEO_V4L2
- select VIDEO_TUNER
- select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
- ---help---
- There are more than 50 different USB video devices based on
- NT1003/1004/1005 USB Bridges. This driver enables using those
- devices.
-
- To compile this driver as a module, choose M here: the
- module will be called usbvision.
diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile
deleted file mode 100644
index aea1e3b5f06b..000000000000
--- a/drivers/media/video/usbvision/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o
-
-obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o
-
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
deleted file mode 100644
index 9bd8f084f348..000000000000
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ /dev/null
@@ -1,1690 +0,0 @@
-/*
- * USB USBVISION Video device driver 0.9.10
- *
- *
- *
- * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de>
- *
- * This module is part of usbvision driver project.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You 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.
- *
- * Let's call the version 0.... until compression decoding is completely
- * implemented.
- *
- * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach.
- * It was based on USB CPiA driver written by Peter Pregler,
- * Scott J. Bertin and Johannes Erdfelt
- * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler &
- * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink
- * Updates to driver completed by Dwaine P. Garden
- *
- *
- * TODO:
- * - use submit_urb for all setup packets
- * - Fix memory settings for nt1004. It is 4 times as big as the
- * nt1003 memory.
- * - Add audio on endpoint 3 for nt1004 chip.
- * Seems impossible, needs a codec interface. Which one?
- * - Clean up the driver.
- * - optimization for performance.
- * - Add Videotext capability (VBI). Working on it.....
- * - Check audio for other devices
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/timer.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/highmem.h>
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/videodev2.h>
-#include <linux/i2c.h>
-
-#include <media/saa7115.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/tuner.h>
-
-#include <linux/workqueue.h>
-
-#include "usbvision.h"
-#include "usbvision-cards.h"
-
-#define DRIVER_AUTHOR \
- "Joerg Heckenbach <joerg@heckenbach-aw.de>, " \
- "Dwaine Garden <DwaineGarden@rogers.com>"
-#define DRIVER_NAME "usbvision"
-#define DRIVER_ALIAS "USBVision"
-#define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
-#define DRIVER_LICENSE "GPL"
-#define USBVISION_VERSION_STRING "0.9.11"
-
-#define ENABLE_HEXDUMP 0 /* Enable if you need it */
-
-
-#ifdef USBVISION_DEBUG
- #define PDEBUG(level, fmt, args...) { \
- if (video_debug & (level)) \
- printk(KERN_INFO KBUILD_MODNAME ":[%s:%d] " fmt, \
- __func__, __LINE__ , ## args); \
- }
-#else
- #define PDEBUG(level, fmt, args...) do {} while (0)
-#endif
-
-#define DBG_IO (1 << 1)
-#define DBG_PROBE (1 << 2)
-#define DBG_MMAP (1 << 3)
-
-/* String operations */
-#define rmspace(str) while (*str == ' ') str++;
-#define goto2next(str) while (*str != ' ') str++; while (*str == ' ') str++;
-
-
-/* sequential number of usbvision device */
-static int usbvision_nr;
-
-static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = {
- { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" },
- { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" },
- { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" },
- { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" },
- { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" },
- { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" },
- { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, /* 1.5 ! */
- { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" }
-};
-
-/* Function prototypes */
-static void usbvision_release(struct usb_usbvision *usbvision);
-
-/* Default initialization of device driver parameters */
-/* Set the default format for ISOC endpoint */
-static int isoc_mode = ISOC_MODE_COMPRESS;
-/* Set the default Debug Mode of the device driver */
-static int video_debug;
-/* Set the default device to power on at startup */
-static int power_on_at_open = 1;
-/* Sequential Number of Video Device */
-static int video_nr = -1;
-/* Sequential Number of Radio Device */
-static int radio_nr = -1;
-
-/* Grab parameters for the device driver */
-
-/* Showing parameters under SYSFS */
-module_param(isoc_mode, int, 0444);
-module_param(video_debug, int, 0444);
-module_param(power_on_at_open, int, 0444);
-module_param(video_nr, int, 0444);
-module_param(radio_nr, int, 0444);
-
-MODULE_PARM_DESC(isoc_mode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)");
-MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)");
-MODULE_PARM_DESC(power_on_at_open, " Set the default device to power on when device is opened. Default: 1 (On)");
-MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)");
-MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
-
-
-/* Misc stuff */
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-MODULE_VERSION(USBVISION_VERSION_STRING);
-MODULE_ALIAS(DRIVER_ALIAS);
-
-
-/*****************************************************************************/
-/* SYSFS Code - Copied from the stv680.c usb module. */
-/* Device information is located at /sys/class/video4linux/video0 */
-/* Device parameters information is located at /sys/module/usbvision */
-/* Device USB Information is located at */
-/* /sys/bus/usb/drivers/USBVision Video Grabber */
-/*****************************************************************************/
-
-#define YES_NO(x) ((x) ? "Yes" : "No")
-
-static inline struct usb_usbvision *cd_to_usbvision(struct device *cd)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- return video_get_drvdata(vdev);
-}
-
-static ssize_t show_version(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%s\n", USBVISION_VERSION_STRING);
-}
-static DEVICE_ATTR(version, S_IRUGO, show_version, NULL);
-
-static ssize_t show_model(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- return sprintf(buf, "%s\n",
- usbvision_device_data[usbvision->dev_model].model_string);
-}
-static DEVICE_ATTR(model, S_IRUGO, show_model, NULL);
-
-static ssize_t show_hue(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- struct v4l2_control ctrl;
- ctrl.id = V4L2_CID_HUE;
- ctrl.value = 0;
- if (usbvision->user)
- call_all(usbvision, core, g_ctrl, &ctrl);
- return sprintf(buf, "%d\n", ctrl.value);
-}
-static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
-
-static ssize_t show_contrast(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- struct v4l2_control ctrl;
- ctrl.id = V4L2_CID_CONTRAST;
- ctrl.value = 0;
- if (usbvision->user)
- call_all(usbvision, core, g_ctrl, &ctrl);
- return sprintf(buf, "%d\n", ctrl.value);
-}
-static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
-
-static ssize_t show_brightness(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- struct v4l2_control ctrl;
- ctrl.id = V4L2_CID_BRIGHTNESS;
- ctrl.value = 0;
- if (usbvision->user)
- call_all(usbvision, core, g_ctrl, &ctrl);
- return sprintf(buf, "%d\n", ctrl.value);
-}
-static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
-
-static ssize_t show_saturation(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- struct v4l2_control ctrl;
- ctrl.id = V4L2_CID_SATURATION;
- ctrl.value = 0;
- if (usbvision->user)
- call_all(usbvision, core, g_ctrl, &ctrl);
- return sprintf(buf, "%d\n", ctrl.value);
-}
-static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
-
-static ssize_t show_streaming(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- return sprintf(buf, "%s\n",
- YES_NO(usbvision->streaming == stream_on ? 1 : 0));
-}
-static DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL);
-
-static ssize_t show_compression(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- return sprintf(buf, "%s\n",
- YES_NO(usbvision->isoc_mode == ISOC_MODE_COMPRESS));
-}
-static DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL);
-
-static ssize_t show_device_bridge(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev =
- container_of(cd, struct video_device, dev);
- struct usb_usbvision *usbvision = video_get_drvdata(vdev);
- return sprintf(buf, "%d\n", usbvision->bridge_type);
-}
-static DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL);
-
-static void usbvision_create_sysfs(struct video_device *vdev)
-{
- int res;
-
- if (!vdev)
- return;
- do {
- res = device_create_file(&vdev->dev, &dev_attr_version);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_model);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_hue);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_contrast);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_brightness);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_saturation);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_streaming);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_compression);
- if (res < 0)
- break;
- res = device_create_file(&vdev->dev, &dev_attr_bridge);
- if (res >= 0)
- return;
- } while (0);
-
- dev_err(&vdev->dev, "%s error: %d\n", __func__, res);
-}
-
-static void usbvision_remove_sysfs(struct video_device *vdev)
-{
- if (vdev) {
- device_remove_file(&vdev->dev, &dev_attr_version);
- device_remove_file(&vdev->dev, &dev_attr_model);
- device_remove_file(&vdev->dev, &dev_attr_hue);
- device_remove_file(&vdev->dev, &dev_attr_contrast);
- device_remove_file(&vdev->dev, &dev_attr_brightness);
- device_remove_file(&vdev->dev, &dev_attr_saturation);
- device_remove_file(&vdev->dev, &dev_attr_streaming);
- device_remove_file(&vdev->dev, &dev_attr_compression);
- device_remove_file(&vdev->dev, &dev_attr_bridge);
- }
-}
-
-/*
- * usbvision_open()
- *
- * This is part of Video 4 Linux API. The driver can be opened by one
- * client only (checks internal counter 'usbvision->user'). The procedure
- * then allocates buffers needed for video processing.
- *
- */
-static int usbvision_v4l2_open(struct file *file)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int err_code = 0;
-
- PDEBUG(DBG_IO, "open");
-
- usbvision_reset_power_off_timer(usbvision);
-
- if (usbvision->user)
- err_code = -EBUSY;
- else {
- /* Allocate memory for the scratch ring buffer */
- err_code = usbvision_scratch_alloc(usbvision);
- if (isoc_mode == ISOC_MODE_COMPRESS) {
- /* Allocate intermediate decompression buffers
- only if needed */
- err_code = usbvision_decompress_alloc(usbvision);
- }
- if (err_code) {
- /* Deallocate all buffers if trouble */
- usbvision_scratch_free(usbvision);
- usbvision_decompress_free(usbvision);
- }
- }
-
- /* If so far no errors then we shall start the camera */
- if (!err_code) {
- if (usbvision->power == 0) {
- usbvision_power_on(usbvision);
- usbvision_i2c_register(usbvision);
- }
-
- /* Send init sequence only once, it's large! */
- if (!usbvision->initialized) {
- int setup_ok = 0;
- setup_ok = usbvision_setup(usbvision, isoc_mode);
- if (setup_ok)
- usbvision->initialized = 1;
- else
- err_code = -EBUSY;
- }
-
- if (!err_code) {
- usbvision_begin_streaming(usbvision);
- err_code = usbvision_init_isoc(usbvision);
- /* device must be initialized before isoc transfer */
- usbvision_muxsel(usbvision, 0);
- usbvision->user++;
- } else {
- if (power_on_at_open) {
- usbvision_i2c_unregister(usbvision);
- usbvision_power_off(usbvision);
- usbvision->initialized = 0;
- }
- }
- }
-
- /* prepare queues */
- usbvision_empty_framequeues(usbvision);
-
- PDEBUG(DBG_IO, "success");
- return err_code;
-}
-
-/*
- * usbvision_v4l2_close()
- *
- * This is part of Video 4 Linux API. The procedure
- * stops streaming and deallocates all buffers that were earlier
- * allocated in usbvision_v4l2_open().
- *
- */
-static int usbvision_v4l2_close(struct file *file)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- PDEBUG(DBG_IO, "close");
-
- usbvision_audio_off(usbvision);
- usbvision_restart_isoc(usbvision);
- usbvision_stop_isoc(usbvision);
-
- usbvision_decompress_free(usbvision);
- usbvision_frames_free(usbvision);
- usbvision_empty_framequeues(usbvision);
- usbvision_scratch_free(usbvision);
-
- usbvision->user--;
-
- if (power_on_at_open) {
- /* power off in a little while
- to avoid off/on every close/open short sequences */
- usbvision_set_power_off_timer(usbvision);
- usbvision->initialized = 0;
- }
-
- if (usbvision->remove_pending) {
- printk(KERN_INFO "%s: Final disconnect\n", __func__);
- usbvision_release(usbvision);
- }
-
- PDEBUG(DBG_IO, "success");
- return 0;
-}
-
-
-/*
- * usbvision_ioctl()
- *
- * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
- *
- */
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int vidioc_g_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int err_code;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- /* NT100x has a 8-bit register space */
- err_code = usbvision_read_reg(usbvision, reg->reg&0xff);
- if (err_code < 0) {
- dev_err(&usbvision->vdev->dev,
- "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n",
- __func__, err_code);
- return err_code;
- }
- reg->val = err_code;
- reg->size = 1;
- return 0;
-}
-
-static int vidioc_s_register(struct file *file, void *priv,
- struct v4l2_dbg_register *reg)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int err_code;
-
- if (!v4l2_chip_match_host(&reg->match))
- return -EINVAL;
- /* NT100x has a 8-bit register space */
- err_code = usbvision_write_reg(usbvision, reg->reg & 0xff, reg->val);
- if (err_code < 0) {
- dev_err(&usbvision->vdev->dev,
- "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n",
- __func__, err_code);
- return err_code;
- }
- return 0;
-}
-#endif
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *vc)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- strlcpy(vc->driver, "USBVision", sizeof(vc->driver));
- strlcpy(vc->card,
- usbvision_device_data[usbvision->dev_model].model_string,
- sizeof(vc->card));
- usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
- vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING |
- (usbvision->have_tuner ? V4L2_CAP_TUNER : 0);
- return 0;
-}
-
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *vi)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int chan;
-
- if (vi->index >= usbvision->video_inputs)
- return -EINVAL;
- if (usbvision->have_tuner)
- chan = vi->index;
- else
- chan = vi->index + 1; /* skip Television string*/
-
- /* Determine the requested input characteristics
- specific for each usbvision card model */
- switch (chan) {
- case 0:
- if (usbvision_device_data[usbvision->dev_model].video_channels == 4) {
- strcpy(vi->name, "White Video Input");
- } else {
- strcpy(vi->name, "Television");
- vi->type = V4L2_INPUT_TYPE_TUNER;
- vi->audioset = 1;
- vi->tuner = chan;
- vi->std = USBVISION_NORMS;
- }
- break;
- case 1:
- vi->type = V4L2_INPUT_TYPE_CAMERA;
- if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
- strcpy(vi->name, "Green Video Input");
- else
- strcpy(vi->name, "Composite Video Input");
- vi->std = V4L2_STD_PAL;
- break;
- case 2:
- vi->type = V4L2_INPUT_TYPE_CAMERA;
- if (usbvision_device_data[usbvision->dev_model].video_channels == 4)
- strcpy(vi->name, "Yellow Video Input");
- else
- strcpy(vi->name, "S-Video Input");
- vi->std = V4L2_STD_PAL;
- break;
- case 3:
- vi->type = V4L2_INPUT_TYPE_CAMERA;
- strcpy(vi->name, "Red Video Input");
- vi->std = V4L2_STD_PAL;
- break;
- }
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *input)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- *input = usbvision->ctl_input;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int input)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- if (input >= usbvision->video_inputs)
- return -EINVAL;
-
- usbvision_muxsel(usbvision, input);
- usbvision_set_input(usbvision);
- usbvision_set_output(usbvision,
- usbvision->curwidth,
- usbvision->curheight);
- return 0;
-}
-
-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- usbvision->tvnorm_id = *id;
-
- call_all(usbvision, core, s_std, usbvision->tvnorm_id);
- /* propagate the change to the decoder */
- usbvision_muxsel(usbvision, usbvision->ctl_input);
-
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *vt)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- if (!usbvision->have_tuner || vt->index) /* Only tuner 0 */
- return -EINVAL;
- if (usbvision->radio) {
- strcpy(vt->name, "Radio");
- vt->type = V4L2_TUNER_RADIO;
- } else {
- strcpy(vt->name, "Television");
- }
- /* Let clients fill in the remainder of this struct */
- call_all(usbvision, tuner, g_tuner, vt);
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *vt)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- /* Only no or one tuner for now */
- if (!usbvision->have_tuner || vt->index)
- return -EINVAL;
- /* let clients handle this */
- call_all(usbvision, tuner, s_tuner, vt);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- freq->tuner = 0; /* Only one tuner */
- if (usbvision->radio)
- freq->type = V4L2_TUNER_RADIO;
- else
- freq->type = V4L2_TUNER_ANALOG_TV;
- freq->frequency = usbvision->freq;
-
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *freq)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- /* Only no or one tuner for now */
- if (!usbvision->have_tuner || freq->tuner)
- return -EINVAL;
-
- usbvision->freq = freq->frequency;
- call_all(usbvision, tuner, s_frequency, freq);
-
- return 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- if (usbvision->radio)
- strcpy(a->name, "Radio");
- else
- strcpy(a->name, "TV");
-
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *fh,
- struct v4l2_audio *a)
-{
- if (a->index)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *ctrl)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- call_all(usbvision, core, queryctrl, ctrl);
-
- if (!ctrl->type)
- return -EINVAL;
-
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- call_all(usbvision, core, g_ctrl, ctrl);
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- call_all(usbvision, core, s_ctrl, ctrl);
- return 0;
-}
-
-static int vidioc_reqbufs(struct file *file,
- void *priv, struct v4l2_requestbuffers *vr)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int ret;
-
- RESTRICT_TO_RANGE(vr->count, 1, USBVISION_NUMFRAMES);
-
- /* Check input validity:
- the user must do a VIDEO CAPTURE and MMAP method. */
- if (vr->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- if (usbvision->streaming == stream_on) {
- ret = usbvision_stream_interrupt(usbvision);
- if (ret)
- return ret;
- }
-
- usbvision_frames_free(usbvision);
- usbvision_empty_framequeues(usbvision);
- vr->count = usbvision_frames_alloc(usbvision, vr->count);
-
- usbvision->cur_frame = NULL;
-
- return 0;
-}
-
-static int vidioc_querybuf(struct file *file,
- void *priv, struct v4l2_buffer *vb)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- struct usbvision_frame *frame;
-
- /* FIXME : must control
- that buffers are mapped (VIDIOC_REQBUFS has been called) */
- if (vb->index >= usbvision->num_frames)
- return -EINVAL;
- /* Updating the corresponding frame state */
- vb->flags = 0;
- frame = &usbvision->frame[vb->index];
- if (frame->grabstate >= frame_state_ready)
- vb->flags |= V4L2_BUF_FLAG_QUEUED;
- if (frame->grabstate >= frame_state_done)
- vb->flags |= V4L2_BUF_FLAG_DONE;
- if (frame->grabstate == frame_state_unused)
- vb->flags |= V4L2_BUF_FLAG_MAPPED;
- vb->memory = V4L2_MEMORY_MMAP;
-
- vb->m.offset = vb->index * PAGE_ALIGN(usbvision->max_frame_size);
-
- vb->memory = V4L2_MEMORY_MMAP;
- vb->field = V4L2_FIELD_NONE;
- vb->length = usbvision->curwidth *
- usbvision->curheight *
- usbvision->palette.bytes_per_pixel;
- vb->timestamp = usbvision->frame[vb->index].timestamp;
- vb->sequence = usbvision->frame[vb->index].sequence;
- return 0;
-}
-
-static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- struct usbvision_frame *frame;
- unsigned long lock_flags;
-
- /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */
- if (vb->index >= usbvision->num_frames)
- return -EINVAL;
-
- frame = &usbvision->frame[vb->index];
-
- if (frame->grabstate != frame_state_unused)
- return -EAGAIN;
-
- /* Mark it as ready and enqueue frame */
- frame->grabstate = frame_state_ready;
- frame->scanstate = scan_state_scanning;
- frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */
-
- vb->flags &= ~V4L2_BUF_FLAG_DONE;
-
- /* set v4l2_format index */
- frame->v4l2_format = usbvision->palette;
-
- spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
- list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue);
- spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
-
- return 0;
-}
-
-static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *vb)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int ret;
- struct usbvision_frame *f;
- unsigned long lock_flags;
-
- if (list_empty(&(usbvision->outqueue))) {
- if (usbvision->streaming == stream_idle)
- return -EINVAL;
- ret = wait_event_interruptible
- (usbvision->wait_frame,
- !list_empty(&(usbvision->outqueue)));
- if (ret)
- return ret;
- }
-
- spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
- f = list_entry(usbvision->outqueue.next,
- struct usbvision_frame, frame);
- list_del(usbvision->outqueue.next);
- spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
-
- f->grabstate = frame_state_unused;
-
- vb->memory = V4L2_MEMORY_MMAP;
- vb->flags = V4L2_BUF_FLAG_MAPPED |
- V4L2_BUF_FLAG_QUEUED |
- V4L2_BUF_FLAG_DONE;
- vb->index = f->index;
- vb->sequence = f->sequence;
- vb->timestamp = f->timestamp;
- vb->field = V4L2_FIELD_NONE;
- vb->bytesused = f->scanlength;
-
- return 0;
-}
-
-static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- usbvision->streaming = stream_on;
- call_all(usbvision, video, s_stream, 1);
-
- return 0;
-}
-
-static int vidioc_streamoff(struct file *file,
- void *priv, enum v4l2_buf_type type)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (usbvision->streaming == stream_on) {
- usbvision_stream_interrupt(usbvision);
- /* Stop all video streamings */
- call_all(usbvision, video, s_stream, 0);
- }
- usbvision_empty_framequeues(usbvision);
-
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *vfd)
-{
- if (vfd->index >= USBVISION_SUPPORTED_PALETTES - 1)
- return -EINVAL;
- strcpy(vfd->description, usbvision_v4l2_format[vfd->index].desc);
- vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *vf)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- vf->fmt.pix.width = usbvision->curwidth;
- vf->fmt.pix.height = usbvision->curheight;
- vf->fmt.pix.pixelformat = usbvision->palette.format;
- vf->fmt.pix.bytesperline =
- usbvision->curwidth * usbvision->palette.bytes_per_pixel;
- vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline * usbvision->curheight;
- vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */
-
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *vf)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int format_idx;
-
- /* Find requested format in available ones */
- for (format_idx = 0; format_idx < USBVISION_SUPPORTED_PALETTES; format_idx++) {
- if (vf->fmt.pix.pixelformat ==
- usbvision_v4l2_format[format_idx].format) {
- usbvision->palette = usbvision_v4l2_format[format_idx];
- break;
- }
- }
- /* robustness */
- if (format_idx == USBVISION_SUPPORTED_PALETTES)
- return -EINVAL;
- RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
- RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
-
- vf->fmt.pix.bytesperline = vf->fmt.pix.width*
- usbvision->palette.bytes_per_pixel;
- vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height;
-
- return 0;
-}
-
-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *vf)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int ret;
-
- ret = vidioc_try_fmt_vid_cap(file, priv, vf);
- if (ret)
- return ret;
-
- /* stop io in case it is already in progress */
- if (usbvision->streaming == stream_on) {
- ret = usbvision_stream_interrupt(usbvision);
- if (ret)
- return ret;
- }
- usbvision_frames_free(usbvision);
- usbvision_empty_framequeues(usbvision);
-
- usbvision->cur_frame = NULL;
-
- /* by now we are committed to the new data... */
- usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height);
-
- return 0;
-}
-
-static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int noblock = file->f_flags & O_NONBLOCK;
- unsigned long lock_flags;
- int ret, i;
- struct usbvision_frame *frame;
-
- PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__,
- (unsigned long)count, noblock);
-
- if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL))
- return -EFAULT;
-
- /* This entry point is compatible with the mmap routines
- so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF
- to get frames or call read on the device. */
- if (!usbvision->num_frames) {
- /* First, allocate some frames to work with
- if this has not been done with VIDIOC_REQBUF */
- usbvision_frames_free(usbvision);
- usbvision_empty_framequeues(usbvision);
- usbvision_frames_alloc(usbvision, USBVISION_NUMFRAMES);
- }
-
- if (usbvision->streaming != stream_on) {
- /* no stream is running, make it running ! */
- usbvision->streaming = stream_on;
- call_all(usbvision, video, s_stream, 1);
- }
-
- /* Then, enqueue as many frames as possible
- (like a user of VIDIOC_QBUF would do) */
- for (i = 0; i < usbvision->num_frames; i++) {
- frame = &usbvision->frame[i];
- if (frame->grabstate == frame_state_unused) {
- /* Mark it as ready and enqueue frame */
- frame->grabstate = frame_state_ready;
- frame->scanstate = scan_state_scanning;
- /* Accumulated in usbvision_parse_data() */
- frame->scanlength = 0;
-
- /* set v4l2_format index */
- frame->v4l2_format = usbvision->palette;
-
- spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
- list_add_tail(&frame->frame, &usbvision->inqueue);
- spin_unlock_irqrestore(&usbvision->queue_lock,
- lock_flags);
- }
- }
-
- /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */
- if (list_empty(&(usbvision->outqueue))) {
- if (noblock)
- return -EAGAIN;
-
- ret = wait_event_interruptible
- (usbvision->wait_frame,
- !list_empty(&(usbvision->outqueue)));
- if (ret)
- return ret;
- }
-
- spin_lock_irqsave(&usbvision->queue_lock, lock_flags);
- frame = list_entry(usbvision->outqueue.next,
- struct usbvision_frame, frame);
- list_del(usbvision->outqueue.next);
- spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags);
-
- /* An error returns an empty frame */
- if (frame->grabstate == frame_state_error) {
- frame->bytes_read = 0;
- return 0;
- }
-
- PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld",
- __func__,
- frame->index, frame->bytes_read, frame->scanlength);
-
- /* copy bytes to user space; we allow for partials reads */
- if ((count + frame->bytes_read) > (unsigned long)frame->scanlength)
- count = frame->scanlength - frame->bytes_read;
-
- if (copy_to_user(buf, frame->data + frame->bytes_read, count))
- return -EFAULT;
-
- frame->bytes_read += count;
- PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld",
- __func__,
- (unsigned long)count, frame->bytes_read);
-
- /* For now, forget the frame if it has not been read in one shot. */
-/* if (frame->bytes_read >= frame->scanlength) {*/ /* All data has been read */
- frame->bytes_read = 0;
-
- /* Mark it as available to be used again. */
- frame->grabstate = frame_state_unused;
-/* } */
-
- return count;
-}
-
-static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- unsigned long size = vma->vm_end - vma->vm_start,
- start = vma->vm_start;
- void *pos;
- u32 i;
- struct usb_usbvision *usbvision = video_drvdata(file);
-
- PDEBUG(DBG_MMAP, "mmap");
-
- if (!USBVISION_IS_OPERATIONAL(usbvision))
- return -EFAULT;
-
- if (!(vma->vm_flags & VM_WRITE) ||
- size != PAGE_ALIGN(usbvision->max_frame_size)) {
- return -EINVAL;
- }
-
- for (i = 0; i < usbvision->num_frames; i++) {
- if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) ==
- vma->vm_pgoff)
- break;
- }
- if (i == usbvision->num_frames) {
- PDEBUG(DBG_MMAP,
- "mmap: user supplied mapping address is out of range");
- return -EINVAL;
- }
-
- /* VM_IO is eventually going to replace PageReserved altogether */
- vma->vm_flags |= VM_IO;
- vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
-
- pos = usbvision->frame[i].data;
- while (size > 0) {
- if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
- PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed");
- return -EAGAIN;
- }
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- return 0;
-}
-
-
-/*
- * Here comes the stuff for radio on usbvision based devices
- *
- */
-static int usbvision_radio_open(struct file *file)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int err_code = 0;
-
- PDEBUG(DBG_IO, "%s:", __func__);
-
- if (usbvision->user) {
- dev_err(&usbvision->rdev->dev,
- "%s: Someone tried to open an already opened USBVision Radio!\n",
- __func__);
- err_code = -EBUSY;
- } else {
- if (power_on_at_open) {
- usbvision_reset_power_off_timer(usbvision);
- if (usbvision->power == 0) {
- usbvision_power_on(usbvision);
- usbvision_i2c_register(usbvision);
- }
- }
-
- /* Alternate interface 1 is is the biggest frame size */
- err_code = usbvision_set_alternate(usbvision);
- if (err_code < 0) {
- usbvision->last_error = err_code;
- err_code = -EBUSY;
- goto out;
- }
-
- /* If so far no errors then we shall start the radio */
- usbvision->radio = 1;
- call_all(usbvision, tuner, s_radio);
- usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
- usbvision->user++;
- }
-
- if (err_code) {
- if (power_on_at_open) {
- usbvision_i2c_unregister(usbvision);
- usbvision_power_off(usbvision);
- usbvision->initialized = 0;
- }
- }
-out:
- return err_code;
-}
-
-
-static int usbvision_radio_close(struct file *file)
-{
- struct usb_usbvision *usbvision = video_drvdata(file);
- int err_code = 0;
-
- PDEBUG(DBG_IO, "");
-
- /* Set packet size to 0 */
- usbvision->iface_alt = 0;
- err_code = usb_set_interface(usbvision->dev, usbvision->iface,
- usbvision->iface_alt);
-
- usbvision_audio_off(usbvision);
- usbvision->radio = 0;
- usbvision->user--;
-
- if (power_on_at_open) {
- usbvision_set_power_off_timer(usbvision);
- usbvision->initialized = 0;
- }
-
- if (usbvision->remove_pending) {
- printk(KERN_INFO "%s: Final disconnect\n", __func__);
- usbvision_release(usbvision);
- }
-
- PDEBUG(DBG_IO, "success");
- return err_code;
-}
-
-/* Video registration stuff */
-
-/* Video template */
-static const struct v4l2_file_operations usbvision_fops = {
- .owner = THIS_MODULE,
- .open = usbvision_v4l2_open,
- .release = usbvision_v4l2_close,
- .read = usbvision_v4l2_read,
- .mmap = usbvision_v4l2_mmap,
- .unlocked_ioctl = video_ioctl2,
-/* .poll = video_poll, */
-};
-
-static const struct v4l2_ioctl_ops usbvision_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vidioc_reqbufs,
- .vidioc_querybuf = vidioc_querybuf,
- .vidioc_qbuf = vidioc_qbuf,
- .vidioc_dqbuf = vidioc_dqbuf,
- .vidioc_s_std = vidioc_s_std,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_streamon = vidioc_streamon,
- .vidioc_streamoff = vidioc_streamoff,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = vidioc_g_register,
- .vidioc_s_register = vidioc_s_register,
-#endif
-};
-
-static struct video_device usbvision_video_template = {
- .fops = &usbvision_fops,
- .ioctl_ops = &usbvision_ioctl_ops,
- .name = "usbvision-video",
- .release = video_device_release,
- .tvnorms = USBVISION_NORMS,
- .current_norm = V4L2_STD_PAL
-};
-
-
-/* Radio template */
-static const struct v4l2_file_operations usbvision_radio_fops = {
- .owner = THIS_MODULE,
- .open = usbvision_radio_open,
- .release = usbvision_radio_close,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops usbvision_radio_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
-};
-
-static struct video_device usbvision_radio_template = {
- .fops = &usbvision_radio_fops,
- .name = "usbvision-radio",
- .release = video_device_release,
- .ioctl_ops = &usbvision_radio_ioctl_ops,
-
- .tvnorms = USBVISION_NORMS,
- .current_norm = V4L2_STD_PAL
-};
-
-
-static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
- struct video_device *vdev_template,
- char *name)
-{
- struct usb_device *usb_dev = usbvision->dev;
- struct video_device *vdev;
-
- if (usb_dev == NULL) {
- dev_err(&usbvision->dev->dev,
- "%s: usbvision->dev is not set\n", __func__);
- return NULL;
- }
-
- vdev = video_device_alloc();
- if (NULL == vdev)
- return NULL;
- *vdev = *vdev_template;
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
- vdev->lock = &usbvision->v4l2_lock;
- vdev->v4l2_dev = &usbvision->v4l2_dev;
- snprintf(vdev->name, sizeof(vdev->name), "%s", name);
- video_set_drvdata(vdev, usbvision);
- return vdev;
-}
-
-/* unregister video4linux devices */
-static void usbvision_unregister_video(struct usb_usbvision *usbvision)
-{
- /* Radio Device: */
- if (usbvision->rdev) {
- PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
- video_device_node_name(usbvision->rdev));
- if (video_is_registered(usbvision->rdev))
- video_unregister_device(usbvision->rdev);
- else
- video_device_release(usbvision->rdev);
- usbvision->rdev = NULL;
- }
-
- /* Video Device: */
- if (usbvision->vdev) {
- PDEBUG(DBG_PROBE, "unregister %s [v4l2]",
- video_device_node_name(usbvision->vdev));
- if (video_is_registered(usbvision->vdev))
- video_unregister_device(usbvision->vdev);
- else
- video_device_release(usbvision->vdev);
- usbvision->vdev = NULL;
- }
-}
-
-/* register video4linux devices */
-static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
-{
- /* Video Device: */
- usbvision->vdev = usbvision_vdev_init(usbvision,
- &usbvision_video_template,
- "USBVision Video");
- if (usbvision->vdev == NULL)
- goto err_exit;
- if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr) < 0)
- goto err_exit;
- printk(KERN_INFO "USBVision[%d]: registered USBVision Video device %s [v4l2]\n",
- usbvision->nr, video_device_node_name(usbvision->vdev));
-
- /* Radio Device: */
- if (usbvision_device_data[usbvision->dev_model].radio) {
- /* usbvision has radio */
- usbvision->rdev = usbvision_vdev_init(usbvision,
- &usbvision_radio_template,
- "USBVision Radio");
- if (usbvision->rdev == NULL)
- goto err_exit;
- if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr) < 0)
- goto err_exit;
- printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device %s [v4l2]\n",
- usbvision->nr, video_device_node_name(usbvision->rdev));
- }
- /* all done */
- return 0;
-
- err_exit:
- dev_err(&usbvision->dev->dev,
- "USBVision[%d]: video_register_device() failed\n",
- usbvision->nr);
- usbvision_unregister_video(usbvision);
- return -1;
-}
-
-/*
- * usbvision_alloc()
- *
- * This code allocates the struct usb_usbvision.
- * It is filled with default values.
- *
- * Returns NULL on error, a pointer to usb_usbvision else.
- *
- */
-static struct usb_usbvision *usbvision_alloc(struct usb_device *dev,
- struct usb_interface *intf)
-{
- struct usb_usbvision *usbvision;
-
- usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL);
- if (usbvision == NULL)
- return NULL;
-
- usbvision->dev = dev;
- if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev))
- goto err_free;
-
- mutex_init(&usbvision->v4l2_lock);
-
- /* prepare control urb for control messages during interrupts */
- usbvision->ctrl_urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
- if (usbvision->ctrl_urb == NULL)
- goto err_unreg;
- init_waitqueue_head(&usbvision->ctrl_urb_wq);
-
- usbvision_init_power_off_timer(usbvision);
-
- return usbvision;
-
-err_unreg:
- v4l2_device_unregister(&usbvision->v4l2_dev);
-err_free:
- kfree(usbvision);
- return NULL;
-}
-
-/*
- * usbvision_release()
- *
- * This code does final release of struct usb_usbvision. This happens
- * after the device is disconnected -and- all clients closed their files.
- *
- */
-static void usbvision_release(struct usb_usbvision *usbvision)
-{
- PDEBUG(DBG_PROBE, "");
-
- usbvision_reset_power_off_timer(usbvision);
-
- usbvision->initialized = 0;
-
- usbvision_remove_sysfs(usbvision->vdev);
- usbvision_unregister_video(usbvision);
-
- usb_free_urb(usbvision->ctrl_urb);
-
- v4l2_device_unregister(&usbvision->v4l2_dev);
- kfree(usbvision);
-
- PDEBUG(DBG_PROBE, "success");
-}
-
-
-/*********************** usb interface **********************************/
-
-static void usbvision_configure_video(struct usb_usbvision *usbvision)
-{
- int model;
-
- if (usbvision == NULL)
- return;
-
- model = usbvision->dev_model;
- usbvision->palette = usbvision_v4l2_format[2]; /* V4L2_PIX_FMT_RGB24; */
-
- if (usbvision_device_data[usbvision->dev_model].vin_reg2_override) {
- usbvision->vin_reg2_preset =
- usbvision_device_data[usbvision->dev_model].vin_reg2;
- } else {
- usbvision->vin_reg2_preset = 0;
- }
-
- usbvision->tvnorm_id = usbvision_device_data[model].video_norm;
-
- usbvision->video_inputs = usbvision_device_data[model].video_channels;
- usbvision->ctl_input = 0;
-
- /* This should be here to make i2c clients to be able to register */
- /* first switch off audio */
- if (usbvision_device_data[model].audio_channels > 0)
- usbvision_audio_off(usbvision);
- if (!power_on_at_open) {
- /* and then power up the noisy tuner */
- usbvision_power_on(usbvision);
- usbvision_i2c_register(usbvision);
- }
-}
-
-/*
- * usbvision_probe()
- *
- * This procedure queries device descriptor and accepts the interface
- * if it looks like USBVISION video device
- *
- */
-static int __devinit usbvision_probe(struct usb_interface *intf,
- const struct usb_device_id *devid)
-{
- struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf));
- struct usb_interface *uif;
- __u8 ifnum = intf->altsetting->desc.bInterfaceNumber;
- const struct usb_host_interface *interface;
- struct usb_usbvision *usbvision = NULL;
- const struct usb_endpoint_descriptor *endpoint;
- int model, i;
-
- PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u",
- dev->descriptor.idVendor,
- dev->descriptor.idProduct, ifnum);
-
- model = devid->driver_info;
- if (model < 0 || model >= usbvision_device_data_size) {
- PDEBUG(DBG_PROBE, "model out of bounds %d", model);
- return -ENODEV;
- }
- printk(KERN_INFO "%s: %s found\n", __func__,
- usbvision_device_data[model].model_string);
-
- if (usbvision_device_data[model].interface >= 0)
- interface = &dev->actconfig->interface[usbvision_device_data[model].interface]->altsetting[0];
- else
- interface = &dev->actconfig->interface[ifnum]->altsetting[0];
- endpoint = &interface->endpoint[1].desc;
- if (!usb_endpoint_xfer_isoc(endpoint)) {
- dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
- __func__, ifnum);
- dev_err(&intf->dev, "%s: Endpoint attributes %d",
- __func__, endpoint->bmAttributes);
- return -ENODEV;
- }
- if (usb_endpoint_dir_out(endpoint)) {
- dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n",
- __func__, ifnum);
- return -ENODEV;
- }
-
- usbvision = usbvision_alloc(dev, intf);
- if (usbvision == NULL) {
- dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__);
- return -ENOMEM;
- }
-
- if (dev->descriptor.bNumConfigurations > 1)
- usbvision->bridge_type = BRIDGE_NT1004;
- else if (model == DAZZLE_DVC_90_REV_1_SECAM)
- usbvision->bridge_type = BRIDGE_NT1005;
- else
- usbvision->bridge_type = BRIDGE_NT1003;
- PDEBUG(DBG_PROBE, "bridge_type %d", usbvision->bridge_type);
-
- /* compute alternate max packet sizes */
- uif = dev->actconfig->interface[0];
-
- usbvision->num_alt = uif->num_altsetting;
- PDEBUG(DBG_PROBE, "Alternate settings: %i", usbvision->num_alt);
- usbvision->alt_max_pkt_size = kmalloc(32 * usbvision->num_alt, GFP_KERNEL);
- if (usbvision->alt_max_pkt_size == NULL) {
- dev_err(&intf->dev, "usbvision: out of memory!\n");
- return -ENOMEM;
- }
-
- for (i = 0; i < usbvision->num_alt; i++) {
- u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc.
- wMaxPacketSize);
- usbvision->alt_max_pkt_size[i] =
- (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
- PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i", i,
- usbvision->alt_max_pkt_size[i]);
- }
-
-
- usbvision->nr = usbvision_nr++;
-
- usbvision->have_tuner = usbvision_device_data[model].tuner;
- if (usbvision->have_tuner)
- usbvision->tuner_type = usbvision_device_data[model].tuner_type;
-
- usbvision->dev_model = model;
- usbvision->remove_pending = 0;
- usbvision->iface = ifnum;
- usbvision->iface_alt = 0;
- usbvision->video_endp = endpoint->bEndpointAddress;
- usbvision->isoc_packet_size = 0;
- usbvision->usb_bandwidth = 0;
- usbvision->user = 0;
- usbvision->streaming = stream_off;
- usbvision_configure_video(usbvision);
- usbvision_register_video(usbvision);
-
- usbvision_create_sysfs(usbvision->vdev);
-
- PDEBUG(DBG_PROBE, "success");
- return 0;
-}
-
-
-/*
- * usbvision_disconnect()
- *
- * This procedure stops all driver activity, deallocates interface-private
- * structure (pointed by 'ptr') and after that driver should be removable
- * with no ill consequences.
- *
- */
-static void __devexit usbvision_disconnect(struct usb_interface *intf)
-{
- struct usb_usbvision *usbvision = to_usbvision(usb_get_intfdata(intf));
-
- PDEBUG(DBG_PROBE, "");
-
- if (usbvision == NULL) {
- pr_err("%s: usb_get_intfdata() failed\n", __func__);
- return;
- }
-
- mutex_lock(&usbvision->v4l2_lock);
-
- /* At this time we ask to cancel outstanding URBs */
- usbvision_stop_isoc(usbvision);
-
- v4l2_device_disconnect(&usbvision->v4l2_dev);
-
- if (usbvision->power) {
- usbvision_i2c_unregister(usbvision);
- usbvision_power_off(usbvision);
- }
- usbvision->remove_pending = 1; /* Now all ISO data will be ignored */
-
- usb_put_dev(usbvision->dev);
- usbvision->dev = NULL; /* USB device is no more */
-
- mutex_unlock(&usbvision->v4l2_lock);
-
- if (usbvision->user) {
- printk(KERN_INFO "%s: In use, disconnect pending\n",
- __func__);
- wake_up_interruptible(&usbvision->wait_frame);
- wake_up_interruptible(&usbvision->wait_stream);
- } else {
- usbvision_release(usbvision);
- }
-
- PDEBUG(DBG_PROBE, "success");
-}
-
-static struct usb_driver usbvision_driver = {
- .name = "usbvision",
- .id_table = usbvision_table,
- .probe = usbvision_probe,
- .disconnect = __devexit_p(usbvision_disconnect),
-};
-
-/*
- * usbvision_init()
- *
- * This code is run to initialize the driver.
- *
- */
-static int __init usbvision_init(void)
-{
- int err_code;
-
- PDEBUG(DBG_PROBE, "");
-
- PDEBUG(DBG_IO, "IO debugging is enabled [video]");
- PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]");
- PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]");
-
- /* disable planar mode support unless compression enabled */
- if (isoc_mode != ISOC_MODE_COMPRESS) {
- /* FIXME : not the right way to set supported flag */
- usbvision_v4l2_format[6].supported = 0; /* V4L2_PIX_FMT_YVU420 */
- usbvision_v4l2_format[7].supported = 0; /* V4L2_PIX_FMT_YUV422P */
- }
-
- err_code = usb_register(&usbvision_driver);
-
- if (err_code == 0) {
- printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n");
- PDEBUG(DBG_PROBE, "success");
- }
- return err_code;
-}
-
-static void __exit usbvision_exit(void)
-{
- PDEBUG(DBG_PROBE, "");
-
- usb_deregister(&usbvision_driver);
- PDEBUG(DBG_PROBE, "success");
-}
-
-module_init(usbvision_init);
-module_exit(usbvision_exit);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
deleted file mode 100644
index 1d131720b6d7..000000000000
--- a/drivers/media/video/uvc/uvc_driver.c
+++ /dev/null
@@ -1,2448 +0,0 @@
-/*
- * uvc_driver.c -- USB Video Class driver
- *
- * Copyright (C) 2005-2010
- * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- */
-
-/*
- * This driver aims to support video input and ouput devices compliant with the
- * 'USB Video Class' specification.
- *
- * The driver doesn't support the deprecated v4l1 interface. It implements the
- * mmap capture method only, and doesn't do any image format conversion in
- * software. If your user-space application doesn't support YUYV or MJPEG, fix
- * it :-). Please note that the MJPEG data have been stripped from their
- * Huffman tables (DHT marker), you will need to add it back if your JPEG
- * codec can't handle MJPEG data.
- */
-
-#include <linux/atomic.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-#include <linux/version.h>
-#include <asm/unaligned.h>
-
-#include <media/v4l2-common.h>
-
-#include "uvcvideo.h"
-
-#define DRIVER_AUTHOR "Laurent Pinchart " \
- "<laurent.pinchart@ideasonboard.com>"
-#define DRIVER_DESC "USB Video Class driver"
-
-unsigned int uvc_clock_param = CLOCK_MONOTONIC;
-unsigned int uvc_no_drop_param;
-static unsigned int uvc_quirks_param = -1;
-unsigned int uvc_trace_param;
-unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
-
-/* ------------------------------------------------------------------------
- * Video formats
- */
-
-static struct uvc_format_desc uvc_fmts[] = {
- {
- .name = "YUV 4:2:2 (YUYV)",
- .guid = UVC_GUID_FORMAT_YUY2,
- .fcc = V4L2_PIX_FMT_YUYV,
- },
- {
- .name = "YUV 4:2:2 (YUYV)",
- .guid = UVC_GUID_FORMAT_YUY2_ISIGHT,
- .fcc = V4L2_PIX_FMT_YUYV,
- },
- {
- .name = "YUV 4:2:0 (NV12)",
- .guid = UVC_GUID_FORMAT_NV12,
- .fcc = V4L2_PIX_FMT_NV12,
- },
- {
- .name = "MJPEG",
- .guid = UVC_GUID_FORMAT_MJPEG,
- .fcc = V4L2_PIX_FMT_MJPEG,
- },
- {
- .name = "YVU 4:2:0 (YV12)",
- .guid = UVC_GUID_FORMAT_YV12,
- .fcc = V4L2_PIX_FMT_YVU420,
- },
- {
- .name = "YUV 4:2:0 (I420)",
- .guid = UVC_GUID_FORMAT_I420,
- .fcc = V4L2_PIX_FMT_YUV420,
- },
- {
- .name = "YUV 4:2:0 (M420)",
- .guid = UVC_GUID_FORMAT_M420,
- .fcc = V4L2_PIX_FMT_M420,
- },
- {
- .name = "YUV 4:2:2 (UYVY)",
- .guid = UVC_GUID_FORMAT_UYVY,
- .fcc = V4L2_PIX_FMT_UYVY,
- },
- {
- .name = "Greyscale (8-bit)",
- .guid = UVC_GUID_FORMAT_Y800,
- .fcc = V4L2_PIX_FMT_GREY,
- },
- {
- .name = "Greyscale (16-bit)",
- .guid = UVC_GUID_FORMAT_Y16,
- .fcc = V4L2_PIX_FMT_Y16,
- },
- {
- .name = "RGB Bayer",
- .guid = UVC_GUID_FORMAT_BY8,
- .fcc = V4L2_PIX_FMT_SBGGR8,
- },
- {
- .name = "RGB565",
- .guid = UVC_GUID_FORMAT_RGBP,
- .fcc = V4L2_PIX_FMT_RGB565,
- },
- {
- .name = "H.264",
- .guid = UVC_GUID_FORMAT_H264,
- .fcc = V4L2_PIX_FMT_H264,
- },
-};
-
-/* ------------------------------------------------------------------------
- * Utility functions
- */
-
-struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
- __u8 epaddr)
-{
- struct usb_host_endpoint *ep;
- unsigned int i;
-
- for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
- ep = &alts->endpoint[i];
- if (ep->desc.bEndpointAddress == epaddr)
- return ep;
- }
-
- return NULL;
-}
-
-static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
-{
- unsigned int len = ARRAY_SIZE(uvc_fmts);
- unsigned int i;
-
- for (i = 0; i < len; ++i) {
- if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
- return &uvc_fmts[i];
- }
-
- return NULL;
-}
-
-static __u32 uvc_colorspace(const __u8 primaries)
-{
- static const __u8 colorprimaries[] = {
- 0,
- V4L2_COLORSPACE_SRGB,
- V4L2_COLORSPACE_470_SYSTEM_M,
- V4L2_COLORSPACE_470_SYSTEM_BG,
- V4L2_COLORSPACE_SMPTE170M,
- V4L2_COLORSPACE_SMPTE240M,
- };
-
- if (primaries < ARRAY_SIZE(colorprimaries))
- return colorprimaries[primaries];
-
- return 0;
-}
-
-/* Simplify a fraction using a simple continued fraction decomposition. The
- * idea here is to convert fractions such as 333333/10000000 to 1/30 using
- * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
- * arbitrary parameters to remove non-significative terms from the simple
- * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
- * respectively seems to give nice results.
- */
-void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
- unsigned int n_terms, unsigned int threshold)
-{
- uint32_t *an;
- uint32_t x, y, r;
- unsigned int i, n;
-
- an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
- if (an == NULL)
- return;
-
- /* Convert the fraction to a simple continued fraction. See
- * http://mathforum.org/dr.math/faq/faq.fractions.html
- * Stop if the current term is bigger than or equal to the given
- * threshold.
- */
- x = *numerator;
- y = *denominator;
-
- for (n = 0; n < n_terms && y != 0; ++n) {
- an[n] = x / y;
- if (an[n] >= threshold) {
- if (n < 2)
- n++;
- break;
- }
-
- r = x - an[n] * y;
- x = y;
- y = r;
- }
-
- /* Expand the simple continued fraction back to an integer fraction. */
- x = 0;
- y = 1;
-
- for (i = n; i > 0; --i) {
- r = y;
- y = an[i-1] * y + x;
- x = r;
- }
-
- *numerator = y;
- *denominator = x;
- kfree(an);
-}
-
-/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
- * to compute numerator / denominator * 10000000 using 32 bit fixed point
- * arithmetic only.
- */
-uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
-{
- uint32_t multiplier;
-
- /* Saturate the result if the operation would overflow. */
- if (denominator == 0 ||
- numerator/denominator >= ((uint32_t)-1)/10000000)
- return (uint32_t)-1;
-
- /* Divide both the denominator and the multiplier by two until
- * numerator * multiplier doesn't overflow. If anyone knows a better
- * algorithm please let me know.
- */
- multiplier = 10000000;
- while (numerator > ((uint32_t)-1)/multiplier) {
- multiplier /= 2;
- denominator /= 2;
- }
-
- return denominator ? numerator * multiplier / denominator : 0;
-}
-
-/* ------------------------------------------------------------------------
- * Terminal and unit management
- */
-
-struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
-{
- struct uvc_entity *entity;
-
- list_for_each_entry(entity, &dev->entities, list) {
- if (entity->id == id)
- return entity;
- }
-
- return NULL;
-}
-
-static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
- int id, struct uvc_entity *entity)
-{
- unsigned int i;
-
- if (entity == NULL)
- entity = list_entry(&dev->entities, struct uvc_entity, list);
-
- list_for_each_entry_continue(entity, &dev->entities, list) {
- for (i = 0; i < entity->bNrInPins; ++i)
- if (entity->baSourceID[i] == id)
- return entity;
- }
-
- return NULL;
-}
-
-static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
-{
- struct uvc_streaming *stream;
-
- list_for_each_entry(stream, &dev->streams, list) {
- if (stream->header.bTerminalLink == id)
- return stream;
- }
-
- return NULL;
-}
-
-/* ------------------------------------------------------------------------
- * Descriptors parsing
- */
-
-static int uvc_parse_format(struct uvc_device *dev,
- struct uvc_streaming *streaming, struct uvc_format *format,
- __u32 **intervals, unsigned char *buffer, int buflen)
-{
- struct usb_interface *intf = streaming->intf;
- struct usb_host_interface *alts = intf->cur_altsetting;
- struct uvc_format_desc *fmtdesc;
- struct uvc_frame *frame;
- const unsigned char *start = buffer;
- unsigned int interval;
- unsigned int i, n;
- __u8 ftype;
-
- format->type = buffer[2];
- format->index = buffer[3];
-
- switch (buffer[2]) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- case UVC_VS_FORMAT_FRAME_BASED:
- n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
- if (buflen < n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- /* Find the format descriptor from its GUID. */
- fmtdesc = uvc_format_by_guid(&buffer[5]);
-
- if (fmtdesc != NULL) {
- strlcpy(format->name, fmtdesc->name,
- sizeof format->name);
- format->fcc = fmtdesc->fcc;
- } else {
- uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
- &buffer[5]);
- snprintf(format->name, sizeof(format->name), "%pUl\n",
- &buffer[5]);
- format->fcc = 0;
- }
-
- format->bpp = buffer[21];
- if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
- ftype = UVC_VS_FRAME_UNCOMPRESSED;
- } else {
- ftype = UVC_VS_FRAME_FRAME_BASED;
- if (buffer[27])
- format->flags = UVC_FMT_FLAG_COMPRESSED;
- }
- break;
-
- case UVC_VS_FORMAT_MJPEG:
- if (buflen < 11) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- strlcpy(format->name, "MJPEG", sizeof format->name);
- format->fcc = V4L2_PIX_FMT_MJPEG;
- format->flags = UVC_FMT_FLAG_COMPRESSED;
- format->bpp = 0;
- ftype = UVC_VS_FRAME_MJPEG;
- break;
-
- case UVC_VS_FORMAT_DV:
- if (buflen < 9) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- switch (buffer[8] & 0x7f) {
- case 0:
- strlcpy(format->name, "SD-DV", sizeof format->name);
- break;
- case 1:
- strlcpy(format->name, "SDL-DV", sizeof format->name);
- break;
- case 2:
- strlcpy(format->name, "HD-DV", sizeof format->name);
- break;
- default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d: unknown DV format %u\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber, buffer[8]);
- return -EINVAL;
- }
-
- strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
- sizeof format->name);
-
- format->fcc = V4L2_PIX_FMT_DV;
- format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
- format->bpp = 0;
- ftype = 0;
-
- /* Create a dummy frame descriptor. */
- frame = &format->frame[0];
- memset(&format->frame[0], 0, sizeof format->frame[0]);
- frame->bFrameIntervalType = 1;
- frame->dwDefaultFrameInterval = 1;
- frame->dwFrameInterval = *intervals;
- *(*intervals)++ = 1;
- format->nframes = 1;
- break;
-
- case UVC_VS_FORMAT_MPEG2TS:
- case UVC_VS_FORMAT_STREAM_BASED:
- /* Not supported yet. */
- default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d unsupported format %u\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber,
- buffer[2]);
- return -EINVAL;
- }
-
- uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
-
- buflen -= buffer[0];
- buffer += buffer[0];
-
- /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
- * based formats have frame descriptors.
- */
- while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
- buffer[2] == ftype) {
- frame = &format->frame[format->nframes];
- if (ftype != UVC_VS_FRAME_FRAME_BASED)
- n = buflen > 25 ? buffer[25] : 0;
- else
- n = buflen > 21 ? buffer[21] : 0;
-
- n = n ? n : 3;
-
- if (buflen < 26 + 4*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FRAME error\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- frame->bFrameIndex = buffer[3];
- frame->bmCapabilities = buffer[4];
- frame->wWidth = get_unaligned_le16(&buffer[5]);
- frame->wHeight = get_unaligned_le16(&buffer[7]);
- frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
- frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
- if (ftype != UVC_VS_FRAME_FRAME_BASED) {
- frame->dwMaxVideoFrameBufferSize =
- get_unaligned_le32(&buffer[17]);
- frame->dwDefaultFrameInterval =
- get_unaligned_le32(&buffer[21]);
- frame->bFrameIntervalType = buffer[25];
- } else {
- frame->dwMaxVideoFrameBufferSize = 0;
- frame->dwDefaultFrameInterval =
- get_unaligned_le32(&buffer[17]);
- frame->bFrameIntervalType = buffer[21];
- }
- frame->dwFrameInterval = *intervals;
-
- /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
- * completely. Observed behaviours range from setting the
- * value to 1.1x the actual frame size to hardwiring the
- * 16 low bits to 0. This results in a higher than necessary
- * memory usage as well as a wrong image size information. For
- * uncompressed formats this can be fixed by computing the
- * value from the frame size.
- */
- if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
- frame->dwMaxVideoFrameBufferSize = format->bpp
- * frame->wWidth * frame->wHeight / 8;
-
- /* Some bogus devices report dwMinFrameInterval equal to
- * dwMaxFrameInterval and have dwFrameIntervalStep set to
- * zero. Setting all null intervals to 1 fixes the problem and
- * some other divisions by zero that could happen.
- */
- for (i = 0; i < n; ++i) {
- interval = get_unaligned_le32(&buffer[26+4*i]);
- *(*intervals)++ = interval ? interval : 1;
- }
-
- /* Make sure that the default frame interval stays between
- * the boundaries.
- */
- n -= frame->bFrameIntervalType ? 1 : 2;
- frame->dwDefaultFrameInterval =
- min(frame->dwFrameInterval[n],
- max(frame->dwFrameInterval[0],
- frame->dwDefaultFrameInterval));
-
- if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
- frame->bFrameIntervalType = 1;
- frame->dwFrameInterval[0] =
- frame->dwDefaultFrameInterval;
- }
-
- uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
- frame->wWidth, frame->wHeight,
- 10000000/frame->dwDefaultFrameInterval,
- (100000000/frame->dwDefaultFrameInterval)%10);
-
- format->nframes++;
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
- buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
- buffer[2] == UVC_VS_COLORFORMAT) {
- if (buflen < 6) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d COLORFORMAT error\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- format->colorspace = uvc_colorspace(buffer[3]);
-
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- return buffer - start;
-}
-
-static int uvc_parse_streaming(struct uvc_device *dev,
- struct usb_interface *intf)
-{
- struct uvc_streaming *streaming = NULL;
- struct uvc_format *format;
- struct uvc_frame *frame;
- struct usb_host_interface *alts = &intf->altsetting[0];
- unsigned char *_buffer, *buffer = alts->extra;
- int _buflen, buflen = alts->extralen;
- unsigned int nformats = 0, nframes = 0, nintervals = 0;
- unsigned int size, i, n, p;
- __u32 *interval;
- __u16 psize;
- int ret = -EINVAL;
-
- if (intf->cur_altsetting->desc.bInterfaceSubClass
- != UVC_SC_VIDEOSTREAMING) {
- uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
- "video streaming interface\n", dev->udev->devnum,
- intf->altsetting[0].desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
- uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
- "claimed\n", dev->udev->devnum,
- intf->altsetting[0].desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
- if (streaming == NULL) {
- usb_driver_release_interface(&uvc_driver.driver, intf);
- return -EINVAL;
- }
-
- mutex_init(&streaming->mutex);
- streaming->dev = dev;
- streaming->intf = usb_get_intf(intf);
- streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
-
- /* The Pico iMage webcam has its class-specific interface descriptors
- * after the endpoint descriptors.
- */
- if (buflen == 0) {
- for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
- struct usb_host_endpoint *ep = &alts->endpoint[i];
-
- if (ep->extralen == 0)
- continue;
-
- if (ep->extralen > 2 &&
- ep->extra[1] == USB_DT_CS_INTERFACE) {
- uvc_trace(UVC_TRACE_DESCR, "trying extra data "
- "from endpoint %u.\n", i);
- buffer = alts->endpoint[i].extra;
- buflen = alts->endpoint[i].extralen;
- break;
- }
- }
- }
-
- /* Skip the standard interface descriptors. */
- while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- if (buflen <= 2) {
- uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
- "interface descriptors found.\n");
- goto error;
- }
-
- /* Parse the header descriptor. */
- switch (buffer[2]) {
- case UVC_VS_OUTPUT_HEADER:
- streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- size = 9;
- break;
-
- case UVC_VS_INPUT_HEADER:
- streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- size = 13;
- break;
-
- default:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d HEADER descriptor not found.\n", dev->udev->devnum,
- alts->desc.bInterfaceNumber);
- goto error;
- }
-
- p = buflen >= 4 ? buffer[3] : 0;
- n = buflen >= size ? buffer[size-1] : 0;
-
- if (buflen < size + p*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d HEADER descriptor is invalid.\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber);
- goto error;
- }
-
- streaming->header.bNumFormats = p;
- streaming->header.bEndpointAddress = buffer[6];
- if (buffer[2] == UVC_VS_INPUT_HEADER) {
- streaming->header.bmInfo = buffer[7];
- streaming->header.bTerminalLink = buffer[8];
- streaming->header.bStillCaptureMethod = buffer[9];
- streaming->header.bTriggerSupport = buffer[10];
- streaming->header.bTriggerUsage = buffer[11];
- } else {
- streaming->header.bTerminalLink = buffer[7];
- }
- streaming->header.bControlSize = n;
-
- streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
- GFP_KERNEL);
- if (streaming->header.bmaControls == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- buflen -= buffer[0];
- buffer += buffer[0];
-
- _buffer = buffer;
- _buflen = buflen;
-
- /* Count the format and frame descriptors. */
- while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
- switch (_buffer[2]) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- case UVC_VS_FORMAT_MJPEG:
- case UVC_VS_FORMAT_FRAME_BASED:
- nformats++;
- break;
-
- case UVC_VS_FORMAT_DV:
- /* DV format has no frame descriptor. We will create a
- * dummy frame descriptor with a dummy frame interval.
- */
- nformats++;
- nframes++;
- nintervals++;
- break;
-
- case UVC_VS_FORMAT_MPEG2TS:
- case UVC_VS_FORMAT_STREAM_BASED:
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
- "interface %d FORMAT %u is not supported.\n",
- dev->udev->devnum,
- alts->desc.bInterfaceNumber, _buffer[2]);
- break;
-
- case UVC_VS_FRAME_UNCOMPRESSED:
- case UVC_VS_FRAME_MJPEG:
- nframes++;
- if (_buflen > 25)
- nintervals += _buffer[25] ? _buffer[25] : 3;
- break;
-
- case UVC_VS_FRAME_FRAME_BASED:
- nframes++;
- if (_buflen > 21)
- nintervals += _buffer[21] ? _buffer[21] : 3;
- break;
- }
-
- _buflen -= _buffer[0];
- _buffer += _buffer[0];
- }
-
- if (nformats == 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d has no supported formats defined.\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber);
- goto error;
- }
-
- size = nformats * sizeof *format + nframes * sizeof *frame
- + nintervals * sizeof *interval;
- format = kzalloc(size, GFP_KERNEL);
- if (format == NULL) {
- ret = -ENOMEM;
- goto error;
- }
-
- frame = (struct uvc_frame *)&format[nformats];
- interval = (__u32 *)&frame[nframes];
-
- streaming->format = format;
- streaming->nformats = nformats;
-
- /* Parse the format descriptors. */
- while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
- switch (buffer[2]) {
- case UVC_VS_FORMAT_UNCOMPRESSED:
- case UVC_VS_FORMAT_MJPEG:
- case UVC_VS_FORMAT_DV:
- case UVC_VS_FORMAT_FRAME_BASED:
- format->frame = frame;
- ret = uvc_parse_format(dev, streaming, format,
- &interval, buffer, buflen);
- if (ret < 0)
- goto error;
-
- frame += format->nframes;
- format++;
-
- buflen -= ret;
- buffer += ret;
- continue;
-
- default:
- break;
- }
-
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- if (buflen)
- uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
- "%d has %u bytes of trailing descriptor garbage.\n",
- dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
-
- /* Parse the alternate settings to find the maximum bandwidth. */
- for (i = 0; i < intf->num_altsetting; ++i) {
- struct usb_host_endpoint *ep;
- alts = &intf->altsetting[i];
- ep = uvc_find_endpoint(alts,
- streaming->header.bEndpointAddress);
- if (ep == NULL)
- continue;
-
- psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- if (psize > streaming->maxpsize)
- streaming->maxpsize = psize;
- }
-
- list_add_tail(&streaming->list, &dev->streams);
- return 0;
-
-error:
- usb_driver_release_interface(&uvc_driver.driver, intf);
- usb_put_intf(intf);
- kfree(streaming->format);
- kfree(streaming->header.bmaControls);
- kfree(streaming);
- return ret;
-}
-
-static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
- unsigned int num_pads, unsigned int extra_size)
-{
- struct uvc_entity *entity;
- unsigned int num_inputs;
- unsigned int size;
- unsigned int i;
-
- extra_size = ALIGN(extra_size, sizeof(*entity->pads));
- num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
- size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
- + num_inputs;
- entity = kzalloc(size, GFP_KERNEL);
- if (entity == NULL)
- return NULL;
-
- entity->id = id;
- entity->type = type;
-
- entity->num_links = 0;
- entity->num_pads = num_pads;
- entity->pads = ((void *)(entity + 1)) + extra_size;
-
- for (i = 0; i < num_inputs; ++i)
- entity->pads[i].flags = MEDIA_PAD_FL_SINK;
- if (!UVC_ENTITY_IS_OTERM(entity))
- entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
-
- entity->bNrInPins = num_inputs;
- entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
-
- return entity;
-}
-
-/* Parse vendor-specific extensions. */
-static int uvc_parse_vendor_control(struct uvc_device *dev,
- const unsigned char *buffer, int buflen)
-{
- struct usb_device *udev = dev->udev;
- struct usb_host_interface *alts = dev->intf->cur_altsetting;
- struct uvc_entity *unit;
- unsigned int n, p;
- int handled = 0;
-
- switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
- case 0x046d: /* Logitech */
- if (buffer[1] != 0x41 || buffer[2] != 0x01)
- break;
-
- /* Logitech implements several vendor specific functions
- * through vendor specific extension units (LXU).
- *
- * The LXU descriptors are similar to XU descriptors
- * (see "USB Device Video Class for Video Devices", section
- * 3.7.2.6 "Extension Unit Descriptor") with the following
- * differences:
- *
- * ----------------------------------------------------------
- * 0 bLength 1 Number
- * Size of this descriptor, in bytes: 24+p+n*2
- * ----------------------------------------------------------
- * 23+p+n bmControlsType N Bitmap
- * Individual bits in the set are defined:
- * 0: Absolute
- * 1: Relative
- *
- * This bitset is mapped exactly the same as bmControls.
- * ----------------------------------------------------------
- * 23+p+n*2 bReserved 1 Boolean
- * ----------------------------------------------------------
- * 24+p+n*2 iExtension 1 Index
- * Index of a string descriptor that describes this
- * extension unit.
- * ----------------------------------------------------------
- */
- p = buflen >= 22 ? buffer[21] : 0;
- n = buflen >= 25 + p ? buffer[22+p] : 0;
-
- if (buflen < 25 + p + 2*n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d EXTENSION_UNIT error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- break;
- }
-
- unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
- p + 1, 2*n);
- if (unit == NULL)
- return -ENOMEM;
-
- memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
- unit->extension.bNumControls = buffer[20];
- memcpy(unit->baSourceID, &buffer[22], p);
- unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
- unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
- + n;
- memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
-
- if (buffer[24+p+2*n] != 0)
- usb_string(udev, buffer[24+p+2*n], unit->name,
- sizeof unit->name);
- else
- sprintf(unit->name, "Extension %u", buffer[3]);
-
- list_add_tail(&unit->list, &dev->entities);
- handled = 1;
- break;
- }
-
- return handled;
-}
-
-static int uvc_parse_standard_control(struct uvc_device *dev,
- const unsigned char *buffer, int buflen)
-{
- struct usb_device *udev = dev->udev;
- struct uvc_entity *unit, *term;
- struct usb_interface *intf;
- struct usb_host_interface *alts = dev->intf->cur_altsetting;
- unsigned int i, n, p, len;
- __u16 type;
-
- switch (buffer[2]) {
- case UVC_VC_HEADER:
- n = buflen >= 12 ? buffer[11] : 0;
-
- if (buflen < 12 || buflen < 12 + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d HEADER error\n", udev->devnum,
- alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- dev->uvc_version = get_unaligned_le16(&buffer[3]);
- dev->clock_frequency = get_unaligned_le32(&buffer[7]);
-
- /* Parse all USB Video Streaming interfaces. */
- for (i = 0; i < n; ++i) {
- intf = usb_ifnum_to_if(udev, buffer[12+i]);
- if (intf == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "device %d "
- "interface %d doesn't exists\n",
- udev->devnum, i);
- continue;
- }
-
- uvc_parse_streaming(dev, intf);
- }
- break;
-
- case UVC_VC_INPUT_TERMINAL:
- if (buflen < 8) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- /* Make sure the terminal type MSB is not null, otherwise it
- * could be confused with a unit.
- */
- type = get_unaligned_le16(&buffer[4]);
- if ((type & 0xff00) == 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL %d has invalid "
- "type 0x%04x, skipping\n", udev->devnum,
- alts->desc.bInterfaceNumber,
- buffer[3], type);
- return 0;
- }
-
- n = 0;
- p = 0;
- len = 8;
-
- if (type == UVC_ITT_CAMERA) {
- n = buflen >= 15 ? buffer[14] : 0;
- len = 15;
-
- } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
- n = buflen >= 9 ? buffer[8] : 0;
- p = buflen >= 10 + n ? buffer[9+n] : 0;
- len = 10;
- }
-
- if (buflen < len + n + p) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d INPUT_TERMINAL error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
- 1, n + p);
- if (term == NULL)
- return -ENOMEM;
-
- if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
- term->camera.bControlSize = n;
- term->camera.bmControls = (__u8 *)term + sizeof *term;
- term->camera.wObjectiveFocalLengthMin =
- get_unaligned_le16(&buffer[8]);
- term->camera.wObjectiveFocalLengthMax =
- get_unaligned_le16(&buffer[10]);
- term->camera.wOcularFocalLength =
- get_unaligned_le16(&buffer[12]);
- memcpy(term->camera.bmControls, &buffer[15], n);
- } else if (UVC_ENTITY_TYPE(term) ==
- UVC_ITT_MEDIA_TRANSPORT_INPUT) {
- term->media.bControlSize = n;
- term->media.bmControls = (__u8 *)term + sizeof *term;
- term->media.bTransportModeSize = p;
- term->media.bmTransportModes = (__u8 *)term
- + sizeof *term + n;
- memcpy(term->media.bmControls, &buffer[9], n);
- memcpy(term->media.bmTransportModes, &buffer[10+n], p);
- }
-
- if (buffer[7] != 0)
- usb_string(udev, buffer[7], term->name,
- sizeof term->name);
- else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
- sprintf(term->name, "Camera %u", buffer[3]);
- else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
- sprintf(term->name, "Media %u", buffer[3]);
- else
- sprintf(term->name, "Input %u", buffer[3]);
-
- list_add_tail(&term->list, &dev->entities);
- break;
-
- case UVC_VC_OUTPUT_TERMINAL:
- if (buflen < 9) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d OUTPUT_TERMINAL error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- /* Make sure the terminal type MSB is not null, otherwise it
- * could be confused with a unit.
- */
- type = get_unaligned_le16(&buffer[4]);
- if ((type & 0xff00) == 0) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d OUTPUT_TERMINAL %d has invalid "
- "type 0x%04x, skipping\n", udev->devnum,
- alts->desc.bInterfaceNumber, buffer[3], type);
- return 0;
- }
-
- term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
- 1, 0);
- if (term == NULL)
- return -ENOMEM;
-
- memcpy(term->baSourceID, &buffer[7], 1);
-
- if (buffer[8] != 0)
- usb_string(udev, buffer[8], term->name,
- sizeof term->name);
- else
- sprintf(term->name, "Output %u", buffer[3]);
-
- list_add_tail(&term->list, &dev->entities);
- break;
-
- case UVC_VC_SELECTOR_UNIT:
- p = buflen >= 5 ? buffer[4] : 0;
-
- if (buflen < 5 || buflen < 6 + p) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d SELECTOR_UNIT error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
- if (unit == NULL)
- return -ENOMEM;
-
- memcpy(unit->baSourceID, &buffer[5], p);
-
- if (buffer[5+p] != 0)
- usb_string(udev, buffer[5+p], unit->name,
- sizeof unit->name);
- else
- sprintf(unit->name, "Selector %u", buffer[3]);
-
- list_add_tail(&unit->list, &dev->entities);
- break;
-
- case UVC_VC_PROCESSING_UNIT:
- n = buflen >= 8 ? buffer[7] : 0;
- p = dev->uvc_version >= 0x0110 ? 10 : 9;
-
- if (buflen < p + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d PROCESSING_UNIT error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
- if (unit == NULL)
- return -ENOMEM;
-
- memcpy(unit->baSourceID, &buffer[4], 1);
- unit->processing.wMaxMultiplier =
- get_unaligned_le16(&buffer[5]);
- unit->processing.bControlSize = buffer[7];
- unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
- memcpy(unit->processing.bmControls, &buffer[8], n);
- if (dev->uvc_version >= 0x0110)
- unit->processing.bmVideoStandards = buffer[9+n];
-
- if (buffer[8+n] != 0)
- usb_string(udev, buffer[8+n], unit->name,
- sizeof unit->name);
- else
- sprintf(unit->name, "Processing %u", buffer[3]);
-
- list_add_tail(&unit->list, &dev->entities);
- break;
-
- case UVC_VC_EXTENSION_UNIT:
- p = buflen >= 22 ? buffer[21] : 0;
- n = buflen >= 24 + p ? buffer[22+p] : 0;
-
- if (buflen < 24 + p + n) {
- uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
- "interface %d EXTENSION_UNIT error\n",
- udev->devnum, alts->desc.bInterfaceNumber);
- return -EINVAL;
- }
-
- unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
- if (unit == NULL)
- return -ENOMEM;
-
- memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
- unit->extension.bNumControls = buffer[20];
- memcpy(unit->baSourceID, &buffer[22], p);
- unit->extension.bControlSize = buffer[22+p];
- unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
- memcpy(unit->extension.bmControls, &buffer[23+p], n);
-
- if (buffer[23+p+n] != 0)
- usb_string(udev, buffer[23+p+n], unit->name,
- sizeof unit->name);
- else
- sprintf(unit->name, "Extension %u", buffer[3]);
-
- list_add_tail(&unit->list, &dev->entities);
- break;
-
- default:
- uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
- "descriptor (%u)\n", buffer[2]);
- break;
- }
-
- return 0;
-}
-
-static int uvc_parse_control(struct uvc_device *dev)
-{
- struct usb_host_interface *alts = dev->intf->cur_altsetting;
- unsigned char *buffer = alts->extra;
- int buflen = alts->extralen;
- int ret;
-
- /* Parse the default alternate setting only, as the UVC specification
- * defines a single alternate setting, the default alternate setting
- * zero.
- */
-
- while (buflen > 2) {
- if (uvc_parse_vendor_control(dev, buffer, buflen) ||
- buffer[1] != USB_DT_CS_INTERFACE)
- goto next_descriptor;
-
- if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
- return ret;
-
-next_descriptor:
- buflen -= buffer[0];
- buffer += buffer[0];
- }
-
- /* Check if the optional status endpoint is present. Built-in iSight
- * webcams have an interrupt endpoint but spit proprietary data that
- * don't conform to the UVC status endpoint messages. Don't try to
- * handle the interrupt endpoint for those cameras.
- */
- if (alts->desc.bNumEndpoints == 1 &&
- !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
- struct usb_host_endpoint *ep = &alts->endpoint[0];
- struct usb_endpoint_descriptor *desc = &ep->desc;
-
- if (usb_endpoint_is_int_in(desc) &&
- le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
- desc->bInterval != 0) {
- uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
- "(addr %02x).\n", desc->bEndpointAddress);
- dev->int_ep = ep;
- }
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------------
- * UVC device scan
- */
-
-/*
- * Scan the UVC descriptors to locate a chain starting at an Output Terminal
- * and containing the following units:
- *
- * - one or more Output Terminals (USB Streaming or Display)
- * - zero or one Processing Unit
- * - zero, one or more single-input Selector Units
- * - zero or one multiple-input Selector Units, provided all inputs are
- * connected to input terminals
- * - zero, one or mode single-input Extension Units
- * - one or more Input Terminals (Camera, External or USB Streaming)
- *
- * The terminal and units must match on of the following structures:
- *
- * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
- * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
- * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
- *
- * +---------+ +---------+ -> OTT_*(0)
- * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
- * +---------+ +---------+ -> OTT_*(n)
- *
- * The Processing Unit and Extension Units can be in any order. Additional
- * Extension Units connected to the main chain as single-unit branches are
- * also supported. Single-input Selector Units are ignored.
- */
-static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
- struct uvc_entity *entity)
-{
- switch (UVC_ENTITY_TYPE(entity)) {
- case UVC_VC_EXTENSION_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- XU %d", entity->id);
-
- if (entity->bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
- "than 1 input pin.\n", entity->id);
- return -1;
- }
-
- break;
-
- case UVC_VC_PROCESSING_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- PU %d", entity->id);
-
- if (chain->processing != NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found multiple "
- "Processing Units in chain.\n");
- return -1;
- }
-
- chain->processing = entity;
- break;
-
- case UVC_VC_SELECTOR_UNIT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- SU %d", entity->id);
-
- /* Single-input selector units are ignored. */
- if (entity->bNrInPins == 1)
- break;
-
- if (chain->selector != NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
- "Units in chain.\n");
- return -1;
- }
-
- chain->selector = entity;
- break;
-
- case UVC_ITT_VENDOR_SPECIFIC:
- case UVC_ITT_CAMERA:
- case UVC_ITT_MEDIA_TRANSPORT_INPUT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- IT %d\n", entity->id);
-
- break;
-
- case UVC_OTT_VENDOR_SPECIFIC:
- case UVC_OTT_DISPLAY:
- case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" OT %d", entity->id);
-
- break;
-
- case UVC_TT_STREAMING:
- if (UVC_ENTITY_IS_ITERM(entity)) {
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- IT %d\n", entity->id);
- } else {
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" OT %d", entity->id);
- }
-
- break;
-
- default:
- uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
- "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
- return -1;
- }
-
- list_add_tail(&entity->chain, &chain->entities);
- return 0;
-}
-
-static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
- struct uvc_entity *entity, struct uvc_entity *prev)
-{
- struct uvc_entity *forward;
- int found;
-
- /* Forward scan */
- forward = NULL;
- found = 0;
-
- while (1) {
- forward = uvc_entity_by_reference(chain->dev, entity->id,
- forward);
- if (forward == NULL)
- break;
- if (forward == prev)
- continue;
-
- switch (UVC_ENTITY_TYPE(forward)) {
- case UVC_VC_EXTENSION_UNIT:
- if (forward->bNrInPins != 1) {
- uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
- "has more than 1 input pin.\n",
- entity->id);
- return -EINVAL;
- }
-
- list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(" (->");
-
- printk(" XU %d", forward->id);
- found = 1;
- }
- break;
-
- case UVC_OTT_VENDOR_SPECIFIC:
- case UVC_OTT_DISPLAY:
- case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
- case UVC_TT_STREAMING:
- if (UVC_ENTITY_IS_ITERM(forward)) {
- uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
- "terminal %u.\n", forward->id);
- return -EINVAL;
- }
-
- list_add_tail(&forward->chain, &chain->entities);
- if (uvc_trace_param & UVC_TRACE_PROBE) {
- if (!found)
- printk(" (->");
-
- printk(" OT %d", forward->id);
- found = 1;
- }
- break;
- }
- }
- if (found)
- printk(")");
-
- return 0;
-}
-
-static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
- struct uvc_entity **_entity)
-{
- struct uvc_entity *entity = *_entity;
- struct uvc_entity *term;
- int id = -EINVAL, i;
-
- switch (UVC_ENTITY_TYPE(entity)) {
- case UVC_VC_EXTENSION_UNIT:
- case UVC_VC_PROCESSING_UNIT:
- id = entity->baSourceID[0];
- break;
-
- case UVC_VC_SELECTOR_UNIT:
- /* Single-input selector units are ignored. */
- if (entity->bNrInPins == 1) {
- id = entity->baSourceID[0];
- break;
- }
-
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" <- IT");
-
- chain->selector = entity;
- for (i = 0; i < entity->bNrInPins; ++i) {
- id = entity->baSourceID[i];
- term = uvc_entity_by_id(chain->dev, id);
- if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
- uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
- "input %d isn't connected to an "
- "input terminal\n", entity->id, i);
- return -1;
- }
-
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk(" %d", term->id);
-
- list_add_tail(&term->chain, &chain->entities);
- uvc_scan_chain_forward(chain, term, entity);
- }
-
- if (uvc_trace_param & UVC_TRACE_PROBE)
- printk("\n");
-
- id = 0;
- break;
-
- case UVC_ITT_VENDOR_SPECIFIC:
- case UVC_ITT_CAMERA:
- case UVC_ITT_MEDIA_TRANSPORT_INPUT:
- case UVC_OTT_VENDOR_SPECIFIC:
- case UVC_OTT_DISPLAY:
- case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
- case UVC_TT_STREAMING:
- id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
- break;
- }
-
- if (id <= 0) {
- *_entity = NULL;
- return id;
- }
-
- entity = uvc_entity_by_id(chain->dev, id);
- if (entity == NULL) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "unknown entity %d.\n", id);
- return -EINVAL;
- }
-
- *_entity = entity;
- return 0;
-}
-
-static int uvc_scan_chain(struct uvc_video_chain *chain,
- struct uvc_entity *term)
-{
- struct uvc_entity *entity, *prev;
-
- uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
-
- entity = term;
- prev = NULL;
-
- while (entity != NULL) {
- /* Entity must not be part of an existing chain */
- if (entity->chain.next || entity->chain.prev) {
- uvc_trace(UVC_TRACE_DESCR, "Found reference to "
- "entity %d already in chain.\n", entity->id);
- return -EINVAL;
- }
-
- /* Process entity */
- if (uvc_scan_chain_entity(chain, entity) < 0)
- return -EINVAL;
-
- /* Forward scan */
- if (uvc_scan_chain_forward(chain, entity, prev) < 0)
- return -EINVAL;
-
- /* Backward scan */
- prev = entity;
- if (uvc_scan_chain_backward(chain, &entity) < 0)
- return -EINVAL;
- }
-
- return 0;
-}
-
-static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
- char *buffer)
-{
- struct uvc_entity *term;
- unsigned int nterms = 0;
- char *p = buffer;
-
- list_for_each_entry(term, terms, chain) {
- if (!UVC_ENTITY_IS_TERM(term) ||
- UVC_TERM_DIRECTION(term) != dir)
- continue;
-
- if (nterms)
- p += sprintf(p, ",");
- if (++nterms >= 4) {
- p += sprintf(p, "...");
- break;
- }
- p += sprintf(p, "%u", term->id);
- }
-
- return p - buffer;
-}
-
-static const char *uvc_print_chain(struct uvc_video_chain *chain)
-{
- static char buffer[43];
- char *p = buffer;
-
- p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
- p += sprintf(p, " -> ");
- uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
-
- return buffer;
-}
-
-/*
- * Scan the device for video chains and register video devices.
- *
- * Chains are scanned starting at their output terminals and walked backwards.
- */
-static int uvc_scan_device(struct uvc_device *dev)
-{
- struct uvc_video_chain *chain;
- struct uvc_entity *term;
-
- list_for_each_entry(term, &dev->entities, list) {
- if (!UVC_ENTITY_IS_OTERM(term))
- continue;
-
- /* If the terminal is already included in a chain, skip it.
- * This can happen for chains that have multiple output
- * terminals, where all output terminals beside the first one
- * will be inserted in the chain in forward scans.
- */
- if (term->chain.next || term->chain.prev)
- continue;
-
- chain = kzalloc(sizeof(*chain), GFP_KERNEL);
- if (chain == NULL)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&chain->entities);
- mutex_init(&chain->ctrl_mutex);
- chain->dev = dev;
-
- if (uvc_scan_chain(chain, term) < 0) {
- kfree(chain);
- continue;
- }
-
- uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
- uvc_print_chain(chain));
-
- list_add_tail(&chain->list, &dev->chains);
- }
-
- if (list_empty(&dev->chains)) {
- uvc_printk(KERN_INFO, "No valid video chain found.\n");
- return -1;
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------------
- * Video device registration and unregistration
- */
-
-/*
- * Delete the UVC device.
- *
- * Called by the kernel when the last reference to the uvc_device structure
- * is released.
- *
- * As this function is called after or during disconnect(), all URBs have
- * already been canceled by the USB core. There is no need to kill the
- * interrupt URB manually.
- */
-static void uvc_delete(struct uvc_device *dev)
-{
- struct list_head *p, *n;
-
- usb_put_intf(dev->intf);
- usb_put_dev(dev->udev);
-
- uvc_status_cleanup(dev);
- uvc_ctrl_cleanup_device(dev);
-
- if (dev->vdev.dev)
- v4l2_device_unregister(&dev->vdev);
-#ifdef CONFIG_MEDIA_CONTROLLER
- if (media_devnode_is_registered(&dev->mdev.devnode))
- media_device_unregister(&dev->mdev);
-#endif
-
- list_for_each_safe(p, n, &dev->chains) {
- struct uvc_video_chain *chain;
- chain = list_entry(p, struct uvc_video_chain, list);
- kfree(chain);
- }
-
- list_for_each_safe(p, n, &dev->entities) {
- struct uvc_entity *entity;
- entity = list_entry(p, struct uvc_entity, list);
-#ifdef CONFIG_MEDIA_CONTROLLER
- uvc_mc_cleanup_entity(entity);
-#endif
- if (entity->vdev) {
- video_device_release(entity->vdev);
- entity->vdev = NULL;
- }
- kfree(entity);
- }
-
- list_for_each_safe(p, n, &dev->streams) {
- struct uvc_streaming *streaming;
- streaming = list_entry(p, struct uvc_streaming, list);
- usb_driver_release_interface(&uvc_driver.driver,
- streaming->intf);
- usb_put_intf(streaming->intf);
- kfree(streaming->format);
- kfree(streaming->header.bmaControls);
- kfree(streaming);
- }
-
- kfree(dev);
-}
-
-static void uvc_release(struct video_device *vdev)
-{
- struct uvc_streaming *stream = video_get_drvdata(vdev);
- struct uvc_device *dev = stream->dev;
-
- /* Decrement the registered streams count and delete the device when it
- * reaches zero.
- */
- if (atomic_dec_and_test(&dev->nstreams))
- uvc_delete(dev);
-}
-
-/*
- * Unregister the video devices.
- */
-static void uvc_unregister_video(struct uvc_device *dev)
-{
- struct uvc_streaming *stream;
-
- /* Unregistering all video devices might result in uvc_delete() being
- * called from inside the loop if there's no open file handle. To avoid
- * that, increment the stream count before iterating over the streams
- * and decrement it when done.
- */
- atomic_inc(&dev->nstreams);
-
- list_for_each_entry(stream, &dev->streams, list) {
- if (stream->vdev == NULL)
- continue;
-
- video_unregister_device(stream->vdev);
- stream->vdev = NULL;
-
- uvc_debugfs_cleanup_stream(stream);
- }
-
- /* Decrement the stream count and call uvc_delete explicitly if there
- * are no stream left.
- */
- if (atomic_dec_and_test(&dev->nstreams))
- uvc_delete(dev);
-}
-
-static int uvc_register_video(struct uvc_device *dev,
- struct uvc_streaming *stream)
-{
- struct video_device *vdev;
- int ret;
-
- /* Initialize the streaming interface with default streaming
- * parameters.
- */
- ret = uvc_video_init(stream);
- if (ret < 0) {
- uvc_printk(KERN_ERR, "Failed to initialize the device "
- "(%d).\n", ret);
- return ret;
- }
-
- uvc_debugfs_init_stream(stream);
-
- /* Register the device with V4L. */
- vdev = video_device_alloc();
- if (vdev == NULL) {
- uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
- ret);
- return -ENOMEM;
- }
-
- /* We already hold a reference to dev->udev. The video device will be
- * unregistered before the reference is released, so we don't need to
- * get another one.
- */
- vdev->v4l2_dev = &dev->vdev;
- vdev->fops = &uvc_fops;
- vdev->release = uvc_release;
- strlcpy(vdev->name, dev->name, sizeof vdev->name);
-
- /* Set the driver data before calling video_register_device, otherwise
- * uvc_v4l2_open might race us.
- */
- stream->vdev = vdev;
- video_set_drvdata(vdev, stream);
-
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
- ret);
- stream->vdev = NULL;
- video_device_release(vdev);
- return ret;
- }
-
- atomic_inc(&dev->nstreams);
- return 0;
-}
-
-/*
- * Register all video devices in all chains.
- */
-static int uvc_register_terms(struct uvc_device *dev,
- struct uvc_video_chain *chain)
-{
- struct uvc_streaming *stream;
- struct uvc_entity *term;
- int ret;
-
- list_for_each_entry(term, &chain->entities, chain) {
- if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
- continue;
-
- stream = uvc_stream_by_id(dev, term->id);
- if (stream == NULL) {
- uvc_printk(KERN_INFO, "No streaming interface found "
- "for terminal %u.", term->id);
- continue;
- }
-
- stream->chain = chain;
- ret = uvc_register_video(dev, stream);
- if (ret < 0)
- return ret;
-
- term->vdev = stream->vdev;
- }
-
- return 0;
-}
-
-static int uvc_register_chains(struct uvc_device *dev)
-{
- struct uvc_video_chain *chain;
- int ret;
-
- list_for_each_entry(chain, &dev->chains, list) {
- ret = uvc_register_terms(dev, chain);
- if (ret < 0)
- return ret;
-
-#ifdef CONFIG_MEDIA_CONTROLLER
- ret = uvc_mc_register_entities(chain);
- if (ret < 0) {
- uvc_printk(KERN_INFO, "Failed to register entites "
- "(%d).\n", ret);
- }
-#endif
- }
-
- return 0;
-}
-
-/* ------------------------------------------------------------------------
- * USB probe, disconnect, suspend and resume
- */
-
-static int uvc_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct uvc_device *dev;
- int ret;
-
- if (id->idVendor && id->idProduct)
- uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
- "(%04x:%04x)\n", udev->devpath, id->idVendor,
- id->idProduct);
- else
- uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
- udev->devpath);
-
- /* Allocate memory for the device and initialize it. */
- if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
- return -ENOMEM;
-
- INIT_LIST_HEAD(&dev->entities);
- INIT_LIST_HEAD(&dev->chains);
- INIT_LIST_HEAD(&dev->streams);
- atomic_set(&dev->nstreams, 0);
- atomic_set(&dev->users, 0);
- atomic_set(&dev->nmappings, 0);
-
- dev->udev = usb_get_dev(udev);
- dev->intf = usb_get_intf(intf);
- dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
- dev->quirks = (uvc_quirks_param == -1)
- ? id->driver_info : uvc_quirks_param;
-
- if (udev->product != NULL)
- strlcpy(dev->name, udev->product, sizeof dev->name);
- else
- snprintf(dev->name, sizeof dev->name,
- "UVC Camera (%04x:%04x)",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
-
- /* Parse the Video Class control descriptor. */
- if (uvc_parse_control(dev) < 0) {
- uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
- "descriptors.\n");
- goto error;
- }
-
- uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
- dev->uvc_version >> 8, dev->uvc_version & 0xff,
- udev->product ? udev->product : "<unnamed>",
- le16_to_cpu(udev->descriptor.idVendor),
- le16_to_cpu(udev->descriptor.idProduct));
-
- if (dev->quirks != id->driver_info) {
- uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
- "parameter for testing purpose.\n", dev->quirks);
- uvc_printk(KERN_INFO, "Please report required quirks to the "
- "linux-uvc-devel mailing list.\n");
- }
-
- /* Register the media and V4L2 devices. */
-#ifdef CONFIG_MEDIA_CONTROLLER
- dev->mdev.dev = &intf->dev;
- strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
- if (udev->serial)
- strlcpy(dev->mdev.serial, udev->serial,
- sizeof(dev->mdev.serial));
- strcpy(dev->mdev.bus_info, udev->devpath);
- dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
- dev->mdev.driver_version = LINUX_VERSION_CODE;
- if (media_device_register(&dev->mdev) < 0)
- goto error;
-
- dev->vdev.mdev = &dev->mdev;
-#endif
- if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
- goto error;
-
- /* Initialize controls. */
- if (uvc_ctrl_init_device(dev) < 0)
- goto error;
-
- /* Scan the device for video chains. */
- if (uvc_scan_device(dev) < 0)
- goto error;
-
- /* Register video device nodes. */
- if (uvc_register_chains(dev) < 0)
- goto error;
-
- /* Save our data pointer in the interface data. */
- usb_set_intfdata(intf, dev);
-
- /* Initialize the interrupt URB. */
- if ((ret = uvc_status_init(dev)) < 0) {
- uvc_printk(KERN_INFO, "Unable to initialize the status "
- "endpoint (%d), status interrupt will not be "
- "supported.\n", ret);
- }
-
- uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
- usb_enable_autosuspend(udev);
- return 0;
-
-error:
- uvc_unregister_video(dev);
- return -ENODEV;
-}
-
-static void uvc_disconnect(struct usb_interface *intf)
-{
- struct uvc_device *dev = usb_get_intfdata(intf);
-
- /* Set the USB interface data to NULL. This can be done outside the
- * lock, as there's no other reader.
- */
- usb_set_intfdata(intf, NULL);
-
- if (intf->cur_altsetting->desc.bInterfaceSubClass ==
- UVC_SC_VIDEOSTREAMING)
- return;
-
- dev->state |= UVC_DEV_DISCONNECTED;
-
- uvc_unregister_video(dev);
-}
-
-static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct uvc_device *dev = usb_get_intfdata(intf);
- struct uvc_streaming *stream;
-
- uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- /* Controls are cached on the fly so they don't need to be saved. */
- if (intf->cur_altsetting->desc.bInterfaceSubClass ==
- UVC_SC_VIDEOCONTROL)
- return uvc_status_suspend(dev);
-
- list_for_each_entry(stream, &dev->streams, list) {
- if (stream->intf == intf)
- return uvc_video_suspend(stream);
- }
-
- uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
- "mismatch.\n");
- return -EINVAL;
-}
-
-static int __uvc_resume(struct usb_interface *intf, int reset)
-{
- struct uvc_device *dev = usb_get_intfdata(intf);
- struct uvc_streaming *stream;
-
- uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- if (intf->cur_altsetting->desc.bInterfaceSubClass ==
- UVC_SC_VIDEOCONTROL) {
- if (reset) {
- int ret = uvc_ctrl_resume_device(dev);
-
- if (ret < 0)
- return ret;
- }
-
- return uvc_status_resume(dev);
- }
-
- list_for_each_entry(stream, &dev->streams, list) {
- if (stream->intf == intf)
- return uvc_video_resume(stream, reset);
- }
-
- uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
- "mismatch.\n");
- return -EINVAL;
-}
-
-static int uvc_resume(struct usb_interface *intf)
-{
- return __uvc_resume(intf, 0);
-}
-
-static int uvc_reset_resume(struct usb_interface *intf)
-{
- return __uvc_resume(intf, 1);
-}
-
-/* ------------------------------------------------------------------------
- * Module parameters
- */
-
-static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
-{
- if (uvc_clock_param == CLOCK_MONOTONIC)
- return sprintf(buffer, "CLOCK_MONOTONIC");
- else
- return sprintf(buffer, "CLOCK_REALTIME");
-}
-
-static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
-{
- if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
- val += strlen("clock_");
-
- if (strcasecmp(val, "monotonic") == 0)
- uvc_clock_param = CLOCK_MONOTONIC;
- else if (strcasecmp(val, "realtime") == 0)
- uvc_clock_param = CLOCK_REALTIME;
- else
- return -EINVAL;
-
- return 0;
-}
-
-module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
- &uvc_clock_param, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
-module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
-module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(quirks, "Forced device quirks");
-module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(trace, "Trace level bitmask");
-module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
-
-/* ------------------------------------------------------------------------
- * Driver initialization and cleanup
- */
-
-/*
- * The Logitech cameras listed below have their interface class set to
- * VENDOR_SPEC because they don't announce themselves as UVC devices, even
- * though they are compliant.
- */
-static struct usb_device_id uvc_ids[] = {
- /* LogiLink Wireless Webcam */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0416,
- .idProduct = 0xa91a,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Genius eFace 2025 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0458,
- .idProduct = 0x706e,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Microsoft Lifecam NX-6000 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x045e,
- .idProduct = 0x00f8,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Microsoft Lifecam VX-7000 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x045e,
- .idProduct = 0x0723,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Logitech Quickcam Fusion */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c1,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Logitech Quickcam Orbit MP */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Logitech Quickcam Pro for Notebook */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c3,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Logitech Quickcam Pro 5000 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c5,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Logitech Quickcam OEM Dell Notebook */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c6,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Logitech Quickcam OEM Cisco VT Camera II */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x046d,
- .idProduct = 0x08c7,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Chicony CNF7129 (Asus EEE 100HE) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x04f2,
- .idProduct = 0xb071,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE },
- /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x058f,
- .idProduct = 0x3820,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Dell XPS m1530 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x05a9,
- .idProduct = 0x2640,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_DEF },
- /* Apple Built-In iSight */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x05ac,
- .idProduct = 0x8501,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_BUILTIN_ISIGHT },
- /* Foxlink ("HP Webcam" on HP Mini 5103) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x05c8,
- .idProduct = 0x0403,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
- /* Genesys Logic USB 2.0 PC Camera */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x05e3,
- .idProduct = 0x0505,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Hercules Classic Silver */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x06f8,
- .idProduct = 0x300c,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
- /* ViMicro Vega */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0ac8,
- .idProduct = 0x332d,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
- /* ViMicro - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0ac8,
- .idProduct = 0x3410,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
- /* ViMicro Venus - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0ac8,
- .idProduct = 0x3420,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
- /* MT6227 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x0e8d,
- .idProduct = 0x0004,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_PROBE_DEF },
- /* IMC Networks (Medion Akoya) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x13d3,
- .idProduct = 0x5103,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* JMicron USB2.0 XGA WebCam */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x152d,
- .idProduct = 0x0310,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Syntek (HP Spartan) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x5212,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Syntek (Samsung Q310) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x5931,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Syntek (Packard Bell EasyNote MX52 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x8a12,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Syntek (Asus F9SG) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x8a31,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Syntek (Asus U3S) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x8a33,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Syntek (JAOtech Smart Terminal) */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x174f,
- .idProduct = 0x8a34,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Miricle 307K */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x17dc,
- .idProduct = 0x0202,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Lenovo Thinkpad SL400/SL500 */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x17ef,
- .idProduct = 0x480b,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STREAM_NO_FID },
- /* Aveo Technology USB 2.0 Camera */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x1871,
- .idProduct = 0x0306,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_PROBE_EXTRAFIELDS },
- /* Ecamm Pico iMage */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x18cd,
- .idProduct = 0xcafe,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
- /* Manta MM-353 Plako */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x18ec,
- .idProduct = 0x3188,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* FSC WebCam V30S */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x18ec,
- .idProduct = 0x3288,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* Arkmicro unbranded */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x18ec,
- .idProduct = 0x3290,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_DEF },
- /* The Imaging Source USB CCD cameras */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x199e,
- .idProduct = 0x8102,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0 },
- /* Bodelin ProScopeHR */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_DEV_HI
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19ab,
- .idProduct = 0x1000,
- .bcdDevice_hi = 0x0126,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_STATUS_INTERVAL },
- /* MSI StarCam 370i */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x1b3b,
- .idProduct = 0x2951,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX },
- /* SiGma Micro USB Web Camera */
- { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
- | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x1c4f,
- .idProduct = 0x3000,
- .bInterfaceClass = USB_CLASS_VIDEO,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 0,
- .driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_IGNORE_SELECTOR_UNIT },
- /* Generic USB Video Class */
- { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
- {}
-};
-
-MODULE_DEVICE_TABLE(usb, uvc_ids);
-
-struct uvc_driver uvc_driver = {
- .driver = {
- .name = "uvcvideo",
- .probe = uvc_probe,
- .disconnect = uvc_disconnect,
- .suspend = uvc_suspend,
- .resume = uvc_resume,
- .reset_resume = uvc_reset_resume,
- .id_table = uvc_ids,
- .supports_autosuspend = 1,
- },
-};
-
-static int __init uvc_init(void)
-{
- int ret;
-
- uvc_debugfs_init();
-
- ret = usb_register(&uvc_driver.driver);
- if (ret < 0) {
- uvc_debugfs_cleanup();
- return ret;
- }
-
- printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
- return 0;
-}
-
-static void __exit uvc_cleanup(void)
-{
- usb_deregister(&uvc_driver.driver);
- uvc_debugfs_cleanup();
-}
-
-module_init(uvc_init);
-module_exit(uvc_cleanup);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
-
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
deleted file mode 100644
index 7ac4347ca09e..000000000000
--- a/drivers/media/video/uvc/uvc_video.c
+++ /dev/null
@@ -1,1861 +0,0 @@
-/*
- * uvc_video.c -- USB Video Class driver - Video handling
- *
- * Copyright (C) 2005-2010
- * Laurent Pinchart (laurent.pinchart@ideasonboard.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.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-#include <linux/atomic.h>
-#include <asm/unaligned.h>
-
-#include <media/v4l2-common.h>
-
-#include "uvcvideo.h"
-
-/* ------------------------------------------------------------------------
- * UVC Controls
- */
-
-static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size,
- int timeout)
-{
- __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- unsigned int pipe;
-
- pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
- : usb_sndctrlpipe(dev->udev, 0);
- type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
-
- return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
- unit << 8 | intfnum, data, size, timeout);
-}
-
-static const char *uvc_query_name(__u8 query)
-{
- switch (query) {
- case UVC_SET_CUR:
- return "SET_CUR";
- case UVC_GET_CUR:
- return "GET_CUR";
- case UVC_GET_MIN:
- return "GET_MIN";
- case UVC_GET_MAX:
- return "GET_MAX";
- case UVC_GET_RES:
- return "GET_RES";
- case UVC_GET_LEN:
- return "GET_LEN";
- case UVC_GET_INFO:
- return "GET_INFO";
- case UVC_GET_DEF:
- return "GET_DEF";
- default:
- return "<invalid>";
- }
-}
-
-int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size)
-{
- int ret;
-
- ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
- UVC_CTRL_CONTROL_TIMEOUT);
- if (ret != size) {
- uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on "
- "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs,
- unit, ret, size);
- return -EIO;
- }
-
- return 0;
-}
-
-static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
- struct uvc_streaming_control *ctrl)
-{
- struct uvc_format *format = NULL;
- struct uvc_frame *frame = NULL;
- unsigned int i;
-
- for (i = 0; i < stream->nformats; ++i) {
- if (stream->format[i].index == ctrl->bFormatIndex) {
- format = &stream->format[i];
- break;
- }
- }
-
- if (format == NULL)
- return;
-
- for (i = 0; i < format->nframes; ++i) {
- if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) {
- frame = &format->frame[i];
- break;
- }
- }
-
- if (frame == NULL)
- return;
-
- if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
- (ctrl->dwMaxVideoFrameSize == 0 &&
- stream->dev->uvc_version < 0x0110))
- ctrl->dwMaxVideoFrameSize =
- frame->dwMaxVideoFrameBufferSize;
-
- if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
- stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
- stream->intf->num_altsetting > 1) {
- u32 interval;
- u32 bandwidth;
-
- interval = (ctrl->dwFrameInterval > 100000)
- ? ctrl->dwFrameInterval
- : frame->dwFrameInterval[0];
-
- /* Compute a bandwidth estimation by multiplying the frame
- * size by the number of video frames per second, divide the
- * result by the number of USB frames (or micro-frames for
- * high-speed devices) per second and add the UVC header size
- * (assumed to be 12 bytes long).
- */
- bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
- bandwidth *= 10000000 / interval + 1;
- bandwidth /= 1000;
- if (stream->dev->udev->speed == USB_SPEED_HIGH)
- bandwidth /= 8;
- bandwidth += 12;
-
- /* The bandwidth estimate is too low for many cameras. Don't use
- * maximum packet sizes lower than 1024 bytes to try and work
- * around the problem. According to measurements done on two
- * different camera models, the value is high enough to get most
- * resolutions working while not preventing two simultaneous
- * VGA streams at 15 fps.
- */
- bandwidth = max_t(u32, bandwidth, 1024);
-
- ctrl->dwMaxPayloadTransferSize = bandwidth;
- }
-}
-
-static int uvc_get_video_ctrl(struct uvc_streaming *stream,
- struct uvc_streaming_control *ctrl, int probe, __u8 query)
-{
- __u8 *data;
- __u16 size;
- int ret;
-
- size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
- if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) &&
- query == UVC_GET_DEF)
- return -EIO;
-
- data = kmalloc(size, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum,
- probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
- size, uvc_timeout_param);
-
- if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) {
- /* Some cameras, mostly based on Bison Electronics chipsets,
- * answer a GET_MIN or GET_MAX request with the wCompQuality
- * field only.
- */
- uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non "
- "compliance - GET_MIN/MAX(PROBE) incorrectly "
- "supported. Enabling workaround.\n");
- memset(ctrl, 0, sizeof *ctrl);
- ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
- ret = 0;
- goto out;
- } else if (query == UVC_GET_DEF && probe == 1 && ret != size) {
- /* Many cameras don't support the GET_DEF request on their
- * video probe control. Warn once and return, the caller will
- * fall back to GET_CUR.
- */
- uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non "
- "compliance - GET_DEF(PROBE) not supported. "
- "Enabling workaround.\n");
- ret = -EIO;
- goto out;
- } else if (ret != size) {
- uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
- "%d (exp. %u).\n", query, probe ? "probe" : "commit",
- ret, size);
- ret = -EIO;
- goto out;
- }
-
- ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
- ctrl->bFormatIndex = data[2];
- ctrl->bFrameIndex = data[3];
- ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
- ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
- ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
- ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
- ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
- ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
- ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]);
- ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]);
-
- if (size == 34) {
- ctrl->dwClockFrequency = get_unaligned_le32(&data[26]);
- ctrl->bmFramingInfo = data[30];
- ctrl->bPreferedVersion = data[31];
- ctrl->bMinVersion = data[32];
- ctrl->bMaxVersion = data[33];
- } else {
- ctrl->dwClockFrequency = stream->dev->clock_frequency;
- ctrl->bmFramingInfo = 0;
- ctrl->bPreferedVersion = 0;
- ctrl->bMinVersion = 0;
- ctrl->bMaxVersion = 0;
- }
-
- /* Some broken devices return null or wrong dwMaxVideoFrameSize and
- * dwMaxPayloadTransferSize fields. Try to get the value from the
- * format and frame descriptors.
- */
- uvc_fixup_video_ctrl(stream, ctrl);
- ret = 0;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int uvc_set_video_ctrl(struct uvc_streaming *stream,
- struct uvc_streaming_control *ctrl, int probe)
-{
- __u8 *data;
- __u16 size;
- int ret;
-
- size = stream->dev->uvc_version >= 0x0110 ? 34 : 26;
- data = kzalloc(size, GFP_KERNEL);
- if (data == NULL)
- return -ENOMEM;
-
- *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
- data[2] = ctrl->bFormatIndex;
- data[3] = ctrl->bFrameIndex;
- *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
- *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
- *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
- *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
- *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
- *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
- put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]);
- put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]);
-
- if (size == 34) {
- put_unaligned_le32(ctrl->dwClockFrequency, &data[26]);
- data[30] = ctrl->bmFramingInfo;
- data[31] = ctrl->bPreferedVersion;
- data[32] = ctrl->bMinVersion;
- data[33] = ctrl->bMaxVersion;
- }
-
- ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum,
- probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data,
- size, uvc_timeout_param);
- if (ret != size) {
- uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
- "%d (exp. %u).\n", probe ? "probe" : "commit",
- ret, size);
- ret = -EIO;
- }
-
- kfree(data);
- return ret;
-}
-
-int uvc_probe_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *probe)
-{
- struct uvc_streaming_control probe_min, probe_max;
- __u16 bandwidth;
- unsigned int i;
- int ret;
-
- /* Perform probing. The device should adjust the requested values
- * according to its capabilities. However, some devices, namely the
- * first generation UVC Logitech webcams, don't implement the Video
- * Probe control properly, and just return the needed bandwidth. For
- * that reason, if the needed bandwidth exceeds the maximum available
- * bandwidth, try to lower the quality.
- */
- ret = uvc_set_video_ctrl(stream, probe, 1);
- if (ret < 0)
- goto done;
-
- /* Get the minimum and maximum values for compression settings. */
- if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
- ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN);
- if (ret < 0)
- goto done;
- ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX);
- if (ret < 0)
- goto done;
-
- probe->wCompQuality = probe_max.wCompQuality;
- }
-
- for (i = 0; i < 2; ++i) {
- ret = uvc_set_video_ctrl(stream, probe, 1);
- if (ret < 0)
- goto done;
- ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
- if (ret < 0)
- goto done;
-
- if (stream->intf->num_altsetting == 1)
- break;
-
- bandwidth = probe->dwMaxPayloadTransferSize;
- if (bandwidth <= stream->maxpsize)
- break;
-
- if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
- ret = -ENOSPC;
- goto done;
- }
-
- /* TODO: negotiate compression parameters */
- probe->wKeyFrameRate = probe_min.wKeyFrameRate;
- probe->wPFrameRate = probe_min.wPFrameRate;
- probe->wCompQuality = probe_max.wCompQuality;
- probe->wCompWindowSize = probe_min.wCompWindowSize;
- }
-
-done:
- return ret;
-}
-
-static int uvc_commit_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *probe)
-{
- return uvc_set_video_ctrl(stream, probe, 0);
-}
-
-/* -----------------------------------------------------------------------------
- * Clocks and timestamps
- */
-
-static void
-uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
- const __u8 *data, int len)
-{
- struct uvc_clock_sample *sample;
- unsigned int header_size;
- bool has_pts = false;
- bool has_scr = false;
- unsigned long flags;
- struct timespec ts;
- u16 host_sof;
- u16 dev_sof;
-
- switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
- case UVC_STREAM_PTS | UVC_STREAM_SCR:
- header_size = 12;
- has_pts = true;
- has_scr = true;
- break;
- case UVC_STREAM_PTS:
- header_size = 6;
- has_pts = true;
- break;
- case UVC_STREAM_SCR:
- header_size = 8;
- has_scr = true;
- break;
- default:
- header_size = 2;
- break;
- }
-
- /* Check for invalid headers. */
- if (len < header_size)
- return;
-
- /* Extract the timestamps:
- *
- * - store the frame PTS in the buffer structure
- * - if the SCR field is present, retrieve the host SOF counter and
- * kernel timestamps and store them with the SCR STC and SOF fields
- * in the ring buffer
- */
- if (has_pts && buf != NULL)
- buf->pts = get_unaligned_le32(&data[2]);
-
- if (!has_scr)
- return;
-
- /* To limit the amount of data, drop SCRs with an SOF identical to the
- * previous one.
- */
- dev_sof = get_unaligned_le16(&data[header_size - 2]);
- if (dev_sof == stream->clock.last_sof)
- return;
-
- stream->clock.last_sof = dev_sof;
-
- host_sof = usb_get_current_frame_number(stream->dev->udev);
- ktime_get_ts(&ts);
-
- /* The UVC specification allows device implementations that can't obtain
- * the USB frame number to keep their own frame counters as long as they
- * match the size and frequency of the frame number associated with USB
- * SOF tokens. The SOF values sent by such devices differ from the USB
- * SOF tokens by a fixed offset that needs to be estimated and accounted
- * for to make timestamp recovery as accurate as possible.
- *
- * The offset is estimated the first time a device SOF value is received
- * as the difference between the host and device SOF values. As the two
- * SOF values can differ slightly due to transmission delays, consider
- * that the offset is null if the difference is not higher than 10 ms
- * (negative differences can not happen and are thus considered as an
- * offset). The video commit control wDelay field should be used to
- * compute a dynamic threshold instead of using a fixed 10 ms value, but
- * devices don't report reliable wDelay values.
- *
- * See uvc_video_clock_host_sof() for an explanation regarding why only
- * the 8 LSBs of the delta are kept.
- */
- if (stream->clock.sof_offset == (u16)-1) {
- u16 delta_sof = (host_sof - dev_sof) & 255;
- if (delta_sof >= 10)
- stream->clock.sof_offset = delta_sof;
- else
- stream->clock.sof_offset = 0;
- }
-
- dev_sof = (dev_sof + stream->clock.sof_offset) & 2047;
-
- spin_lock_irqsave(&stream->clock.lock, flags);
-
- sample = &stream->clock.samples[stream->clock.head];
- sample->dev_stc = get_unaligned_le32(&data[header_size - 6]);
- sample->dev_sof = dev_sof;
- sample->host_sof = host_sof;
- sample->host_ts = ts;
-
- /* Update the sliding window head and count. */
- stream->clock.head = (stream->clock.head + 1) % stream->clock.size;
-
- if (stream->clock.count < stream->clock.size)
- stream->clock.count++;
-
- spin_unlock_irqrestore(&stream->clock.lock, flags);
-}
-
-static void uvc_video_clock_reset(struct uvc_streaming *stream)
-{
- struct uvc_clock *clock = &stream->clock;
-
- clock->head = 0;
- clock->count = 0;
- clock->last_sof = -1;
- clock->sof_offset = -1;
-}
-
-static int uvc_video_clock_init(struct uvc_streaming *stream)
-{
- struct uvc_clock *clock = &stream->clock;
-
- spin_lock_init(&clock->lock);
- clock->size = 32;
-
- clock->samples = kmalloc(clock->size * sizeof(*clock->samples),
- GFP_KERNEL);
- if (clock->samples == NULL)
- return -ENOMEM;
-
- uvc_video_clock_reset(stream);
-
- return 0;
-}
-
-static void uvc_video_clock_cleanup(struct uvc_streaming *stream)
-{
- kfree(stream->clock.samples);
- stream->clock.samples = NULL;
-}
-
-/*
- * uvc_video_clock_host_sof - Return the host SOF value for a clock sample
- *
- * Host SOF counters reported by usb_get_current_frame_number() usually don't
- * cover the whole 11-bits SOF range (0-2047) but are limited to the HCI frame
- * schedule window. They can be limited to 8, 9 or 10 bits depending on the host
- * controller and its configuration.
- *
- * We thus need to recover the SOF value corresponding to the host frame number.
- * As the device and host frame numbers are sampled in a short interval, the
- * difference between their values should be equal to a small delta plus an
- * integer multiple of 256 caused by the host frame number limited precision.
- *
- * To obtain the recovered host SOF value, compute the small delta by masking
- * the high bits of the host frame counter and device SOF difference and add it
- * to the device SOF value.
- */
-static u16 uvc_video_clock_host_sof(const struct uvc_clock_sample *sample)
-{
- /* The delta value can be negative. */
- s8 delta_sof;
-
- delta_sof = (sample->host_sof - sample->dev_sof) & 255;
-
- return (sample->dev_sof + delta_sof) & 2047;
-}
-
-/*
- * uvc_video_clock_update - Update the buffer timestamp
- *
- * This function converts the buffer PTS timestamp to the host clock domain by
- * going through the USB SOF clock domain and stores the result in the V4L2
- * buffer timestamp field.
- *
- * The relationship between the device clock and the host clock isn't known.
- * However, the device and the host share the common USB SOF clock which can be
- * used to recover that relationship.
- *
- * The relationship between the device clock and the USB SOF clock is considered
- * to be linear over the clock samples sliding window and is given by
- *
- * SOF = m * PTS + p
- *
- * Several methods to compute the slope (m) and intercept (p) can be used. As
- * the clock drift should be small compared to the sliding window size, we
- * assume that the line that goes through the points at both ends of the window
- * is a good approximation. Naming those points P1 and P2, we get
- *
- * SOF = (SOF2 - SOF1) / (STC2 - STC1) * PTS
- * + (SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1)
- *
- * or
- *
- * SOF = ((SOF2 - SOF1) * PTS + SOF1 * STC2 - SOF2 * STC1) / (STC2 - STC1) (1)
- *
- * to avoid loosing precision in the division. Similarly, the host timestamp is
- * computed with
- *
- * TS = ((TS2 - TS1) * PTS + TS1 * SOF2 - TS2 * SOF1) / (SOF2 - SOF1) (2)
- *
- * SOF values are coded on 11 bits by USB. We extend their precision with 16
- * decimal bits, leading to a 11.16 coding.
- *
- * TODO: To avoid surprises with device clock values, PTS/STC timestamps should
- * be normalized using the nominal device clock frequency reported through the
- * UVC descriptors.
- *
- * Both the PTS/STC and SOF counters roll over, after a fixed but device
- * specific amount of time for PTS/STC and after 2048ms for SOF. As long as the
- * sliding window size is smaller than the rollover period, differences computed
- * on unsigned integers will produce the correct result. However, the p term in
- * the linear relations will be miscomputed.
- *
- * To fix the issue, we subtract a constant from the PTS and STC values to bring
- * PTS to half the 32 bit STC range. The sliding window STC values then fit into
- * the 32 bit range without any rollover.
- *
- * Similarly, we add 2048 to the device SOF values to make sure that the SOF
- * computed by (1) will never be smaller than 0. This offset is then compensated
- * by adding 2048 to the SOF values used in (2). However, this doesn't prevent
- * rollovers between (1) and (2): the SOF value computed by (1) can be slightly
- * lower than 4096, and the host SOF counters can have rolled over to 2048. This
- * case is handled by subtracting 2048 from the SOF value if it exceeds the host
- * SOF value at the end of the sliding window.
- *
- * Finally we subtract a constant from the host timestamps to bring the first
- * timestamp of the sliding window to 1s.
- */
-void uvc_video_clock_update(struct uvc_streaming *stream,
- struct v4l2_buffer *v4l2_buf,
- struct uvc_buffer *buf)
-{
- struct uvc_clock *clock = &stream->clock;
- struct uvc_clock_sample *first;
- struct uvc_clock_sample *last;
- unsigned long flags;
- struct timespec ts;
- u32 delta_stc;
- u32 y1, y2;
- u32 x1, x2;
- u32 mean;
- u32 sof;
- u32 div;
- u32 rem;
- u64 y;
-
- spin_lock_irqsave(&clock->lock, flags);
-
- if (clock->count < clock->size)
- goto done;
-
- first = &clock->samples[clock->head];
- last = &clock->samples[(clock->head - 1) % clock->size];
-
- /* First step, PTS to SOF conversion. */
- delta_stc = buf->pts - (1UL << 31);
- x1 = first->dev_stc - delta_stc;
- x2 = last->dev_stc - delta_stc;
- if (x1 == x2)
- goto done;
-
- y1 = (first->dev_sof + 2048) << 16;
- y2 = (last->dev_sof + 2048) << 16;
- if (y2 < y1)
- y2 += 2048 << 16;
-
- y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
- - (u64)y2 * (u64)x1;
- y = div_u64(y, x2 - x1);
-
- sof = y;
-
- uvc_trace(UVC_TRACE_CLOCK, "%s: PTS %u y %llu.%06llu SOF %u.%06llu "
- "(x1 %u x2 %u y1 %u y2 %u SOF offset %u)\n",
- stream->dev->name, buf->pts,
- y >> 16, div_u64((y & 0xffff) * 1000000, 65536),
- sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
- x1, x2, y1, y2, clock->sof_offset);
-
- /* Second step, SOF to host clock conversion. */
- x1 = (uvc_video_clock_host_sof(first) + 2048) << 16;
- x2 = (uvc_video_clock_host_sof(last) + 2048) << 16;
- if (x2 < x1)
- x2 += 2048 << 16;
- if (x1 == x2)
- goto done;
-
- ts = timespec_sub(last->host_ts, first->host_ts);
- y1 = NSEC_PER_SEC;
- y2 = (ts.tv_sec + 1) * NSEC_PER_SEC + ts.tv_nsec;
-
- /* Interpolated and host SOF timestamps can wrap around at slightly
- * different times. Handle this by adding or removing 2048 to or from
- * the computed SOF value to keep it close to the SOF samples mean
- * value.
- */
- mean = (x1 + x2) / 2;
- if (mean - (1024 << 16) > sof)
- sof += 2048 << 16;
- else if (sof > mean + (1024 << 16))
- sof -= 2048 << 16;
-
- y = (u64)(y2 - y1) * (u64)sof + (u64)y1 * (u64)x2
- - (u64)y2 * (u64)x1;
- y = div_u64(y, x2 - x1);
-
- div = div_u64_rem(y, NSEC_PER_SEC, &rem);
- ts.tv_sec = first->host_ts.tv_sec - 1 + div;
- ts.tv_nsec = first->host_ts.tv_nsec + rem;
- if (ts.tv_nsec >= NSEC_PER_SEC) {
- ts.tv_sec++;
- ts.tv_nsec -= NSEC_PER_SEC;
- }
-
- uvc_trace(UVC_TRACE_CLOCK, "%s: SOF %u.%06llu y %llu ts %lu.%06lu "
- "buf ts %lu.%06lu (x1 %u/%u/%u x2 %u/%u/%u y1 %u y2 %u)\n",
- stream->dev->name,
- sof >> 16, div_u64(((u64)sof & 0xffff) * 1000000LLU, 65536),
- y, ts.tv_sec, ts.tv_nsec / NSEC_PER_USEC,
- v4l2_buf->timestamp.tv_sec, v4l2_buf->timestamp.tv_usec,
- x1, first->host_sof, first->dev_sof,
- x2, last->host_sof, last->dev_sof, y1, y2);
-
- /* Update the V4L2 buffer. */
- v4l2_buf->timestamp.tv_sec = ts.tv_sec;
- v4l2_buf->timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
-
-done:
- spin_unlock_irqrestore(&stream->clock.lock, flags);
-}
-
-/* ------------------------------------------------------------------------
- * Stream statistics
- */
-
-static void uvc_video_stats_decode(struct uvc_streaming *stream,
- const __u8 *data, int len)
-{
- unsigned int header_size;
- bool has_pts = false;
- bool has_scr = false;
- u16 uninitialized_var(scr_sof);
- u32 uninitialized_var(scr_stc);
- u32 uninitialized_var(pts);
-
- if (stream->stats.stream.nb_frames == 0 &&
- stream->stats.frame.nb_packets == 0)
- ktime_get_ts(&stream->stats.stream.start_ts);
-
- switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) {
- case UVC_STREAM_PTS | UVC_STREAM_SCR:
- header_size = 12;
- has_pts = true;
- has_scr = true;
- break;
- case UVC_STREAM_PTS:
- header_size = 6;
- has_pts = true;
- break;
- case UVC_STREAM_SCR:
- header_size = 8;
- has_scr = true;
- break;
- default:
- header_size = 2;
- break;
- }
-
- /* Check for invalid headers. */
- if (len < header_size || data[0] < header_size) {
- stream->stats.frame.nb_invalid++;
- return;
- }
-
- /* Extract the timestamps. */
- if (has_pts)
- pts = get_unaligned_le32(&data[2]);
-
- if (has_scr) {
- scr_stc = get_unaligned_le32(&data[header_size - 6]);
- scr_sof = get_unaligned_le16(&data[header_size - 2]);
- }
-
- /* Is PTS constant through the whole frame ? */
- if (has_pts && stream->stats.frame.nb_pts) {
- if (stream->stats.frame.pts != pts) {
- stream->stats.frame.nb_pts_diffs++;
- stream->stats.frame.last_pts_diff =
- stream->stats.frame.nb_packets;
- }
- }
-
- if (has_pts) {
- stream->stats.frame.nb_pts++;
- stream->stats.frame.pts = pts;
- }
-
- /* Do all frames have a PTS in their first non-empty packet, or before
- * their first empty packet ?
- */
- if (stream->stats.frame.size == 0) {
- if (len > header_size)
- stream->stats.frame.has_initial_pts = has_pts;
- if (len == header_size && has_pts)
- stream->stats.frame.has_early_pts = true;
- }
-
- /* Do the SCR.STC and SCR.SOF fields vary through the frame ? */
- if (has_scr && stream->stats.frame.nb_scr) {
- if (stream->stats.frame.scr_stc != scr_stc)
- stream->stats.frame.nb_scr_diffs++;
- }
-
- if (has_scr) {
- /* Expand the SOF counter to 32 bits and store its value. */
- if (stream->stats.stream.nb_frames > 0 ||
- stream->stats.frame.nb_scr > 0)
- stream->stats.stream.scr_sof_count +=
- (scr_sof - stream->stats.stream.scr_sof) % 2048;
- stream->stats.stream.scr_sof = scr_sof;
-
- stream->stats.frame.nb_scr++;
- stream->stats.frame.scr_stc = scr_stc;
- stream->stats.frame.scr_sof = scr_sof;
-
- if (scr_sof < stream->stats.stream.min_sof)
- stream->stats.stream.min_sof = scr_sof;
- if (scr_sof > stream->stats.stream.max_sof)
- stream->stats.stream.max_sof = scr_sof;
- }
-
- /* Record the first non-empty packet number. */
- if (stream->stats.frame.size == 0 && len > header_size)
- stream->stats.frame.first_data = stream->stats.frame.nb_packets;
-
- /* Update the frame size. */
- stream->stats.frame.size += len - header_size;
-
- /* Update the packets counters. */
- stream->stats.frame.nb_packets++;
- if (len > header_size)
- stream->stats.frame.nb_empty++;
-
- if (data[1] & UVC_STREAM_ERR)
- stream->stats.frame.nb_errors++;
-}
-
-static void uvc_video_stats_update(struct uvc_streaming *stream)
-{
- struct uvc_stats_frame *frame = &stream->stats.frame;
-
- uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets, "
- "%u/%u/%u pts (%searly %sinitial), %u/%u scr, "
- "last pts/stc/sof %u/%u/%u\n",
- stream->sequence, frame->first_data,
- frame->nb_packets - frame->nb_empty, frame->nb_packets,
- frame->nb_pts_diffs, frame->last_pts_diff, frame->nb_pts,
- frame->has_early_pts ? "" : "!",
- frame->has_initial_pts ? "" : "!",
- frame->nb_scr_diffs, frame->nb_scr,
- frame->pts, frame->scr_stc, frame->scr_sof);
-
- stream->stats.stream.nb_frames++;
- stream->stats.stream.nb_packets += stream->stats.frame.nb_packets;
- stream->stats.stream.nb_empty += stream->stats.frame.nb_empty;
- stream->stats.stream.nb_errors += stream->stats.frame.nb_errors;
- stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid;
-
- if (frame->has_early_pts)
- stream->stats.stream.nb_pts_early++;
- if (frame->has_initial_pts)
- stream->stats.stream.nb_pts_initial++;
- if (frame->last_pts_diff <= frame->first_data)
- stream->stats.stream.nb_pts_constant++;
- if (frame->nb_scr >= frame->nb_packets - frame->nb_empty)
- stream->stats.stream.nb_scr_count_ok++;
- if (frame->nb_scr_diffs + 1 == frame->nb_scr)
- stream->stats.stream.nb_scr_diffs_ok++;
-
- memset(&stream->stats.frame, 0, sizeof(stream->stats.frame));
-}
-
-size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
- size_t size)
-{
- unsigned int scr_sof_freq;
- unsigned int duration;
- struct timespec ts;
- size_t count = 0;
-
- ts.tv_sec = stream->stats.stream.stop_ts.tv_sec
- - stream->stats.stream.start_ts.tv_sec;
- ts.tv_nsec = stream->stats.stream.stop_ts.tv_nsec
- - stream->stats.stream.start_ts.tv_nsec;
- if (ts.tv_nsec < 0) {
- ts.tv_sec--;
- ts.tv_nsec += 1000000000;
- }
-
- /* Compute the SCR.SOF frequency estimate. At the nominal 1kHz SOF
- * frequency this will not overflow before more than 1h.
- */
- duration = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
- if (duration != 0)
- scr_sof_freq = stream->stats.stream.scr_sof_count * 1000
- / duration;
- else
- scr_sof_freq = 0;
-
- count += scnprintf(buf + count, size - count,
- "frames: %u\npackets: %u\nempty: %u\n"
- "errors: %u\ninvalid: %u\n",
- stream->stats.stream.nb_frames,
- stream->stats.stream.nb_packets,
- stream->stats.stream.nb_empty,
- stream->stats.stream.nb_errors,
- stream->stats.stream.nb_invalid);
- count += scnprintf(buf + count, size - count,
- "pts: %u early, %u initial, %u ok\n",
- stream->stats.stream.nb_pts_early,
- stream->stats.stream.nb_pts_initial,
- stream->stats.stream.nb_pts_constant);
- count += scnprintf(buf + count, size - count,
- "scr: %u count ok, %u diff ok\n",
- stream->stats.stream.nb_scr_count_ok,
- stream->stats.stream.nb_scr_diffs_ok);
- count += scnprintf(buf + count, size - count,
- "sof: %u <= sof <= %u, freq %u.%03u kHz\n",
- stream->stats.stream.min_sof,
- stream->stats.stream.max_sof,
- scr_sof_freq / 1000, scr_sof_freq % 1000);
-
- return count;
-}
-
-static void uvc_video_stats_start(struct uvc_streaming *stream)
-{
- memset(&stream->stats, 0, sizeof(stream->stats));
- stream->stats.stream.min_sof = 2048;
-}
-
-static void uvc_video_stats_stop(struct uvc_streaming *stream)
-{
- ktime_get_ts(&stream->stats.stream.stop_ts);
-}
-
-/* ------------------------------------------------------------------------
- * Video codecs
- */
-
-/* Video payload decoding is handled by uvc_video_decode_start(),
- * uvc_video_decode_data() and uvc_video_decode_end().
- *
- * uvc_video_decode_start is called with URB data at the start of a bulk or
- * isochronous payload. It processes header data and returns the header size
- * in bytes if successful. If an error occurs, it returns a negative error
- * code. The following error codes have special meanings.
- *
- * - EAGAIN informs the caller that the current video buffer should be marked
- * as done, and that the function should be called again with the same data
- * and a new video buffer. This is used when end of frame conditions can be
- * reliably detected at the beginning of the next frame only.
- *
- * If an error other than -EAGAIN is returned, the caller will drop the current
- * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be
- * made until the next payload. -ENODATA can be used to drop the current
- * payload if no other error code is appropriate.
- *
- * uvc_video_decode_data is called for every URB with URB data. It copies the
- * data to the video buffer.
- *
- * uvc_video_decode_end is called with header data at the end of a bulk or
- * isochronous payload. It performs any additional header data processing and
- * returns 0 or a negative error code if an error occurred. As header data have
- * already been processed by uvc_video_decode_start, this functions isn't
- * required to perform sanity checks a second time.
- *
- * For isochronous transfers where a payload is always transferred in a single
- * URB, the three functions will be called in a row.
- *
- * To let the decoder process header data and update its internal state even
- * when no video buffer is available, uvc_video_decode_start must be prepared
- * to be called with a NULL buf parameter. uvc_video_decode_data and
- * uvc_video_decode_end will never be called with a NULL buffer.
- */
-static int uvc_video_decode_start(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
-{
- __u8 fid;
-
- /* Sanity checks:
- * - packet must be at least 2 bytes long
- * - bHeaderLength value must be at least 2 bytes (see above)
- * - bHeaderLength value can't be larger than the packet size.
- */
- if (len < 2 || data[0] < 2 || data[0] > len) {
- stream->stats.frame.nb_invalid++;
- return -EINVAL;
- }
-
- fid = data[1] & UVC_STREAM_FID;
-
- /* Increase the sequence number regardless of any buffer states, so
- * that discontinuous sequence numbers always indicate lost frames.
- */
- if (stream->last_fid != fid) {
- stream->sequence++;
- if (stream->sequence)
- uvc_video_stats_update(stream);
- }
-
- uvc_video_clock_decode(stream, buf, data, len);
- uvc_video_stats_decode(stream, data, len);
-
- /* Store the payload FID bit and return immediately when the buffer is
- * NULL.
- */
- if (buf == NULL) {
- stream->last_fid = fid;
- return -ENODATA;
- }
-
- /* Mark the buffer as bad if the error bit is set. */
- if (data[1] & UVC_STREAM_ERR) {
- uvc_trace(UVC_TRACE_FRAME, "Marking buffer as bad (error bit "
- "set).\n");
- buf->error = 1;
- }
-
- /* Synchronize to the input stream by waiting for the FID bit to be
- * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
- * stream->last_fid is initialized to -1, so the first isochronous
- * frame will always be in sync.
- *
- * If the device doesn't toggle the FID bit, invert stream->last_fid
- * when the EOF bit is set to force synchronisation on the next packet.
- */
- if (buf->state != UVC_BUF_STATE_ACTIVE) {
- struct timespec ts;
-
- if (fid == stream->last_fid) {
- uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
- "sync).\n");
- if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
- (data[1] & UVC_STREAM_EOF))
- stream->last_fid ^= UVC_STREAM_FID;
- return -ENODATA;
- }
-
- if (uvc_clock_param == CLOCK_MONOTONIC)
- ktime_get_ts(&ts);
- else
- ktime_get_real_ts(&ts);
-
- buf->buf.v4l2_buf.sequence = stream->sequence;
- buf->buf.v4l2_buf.timestamp.tv_sec = ts.tv_sec;
- buf->buf.v4l2_buf.timestamp.tv_usec =
- ts.tv_nsec / NSEC_PER_USEC;
-
- /* TODO: Handle PTS and SCR. */
- buf->state = UVC_BUF_STATE_ACTIVE;
- }
-
- /* Mark the buffer as done if we're at the beginning of a new frame.
- * End of frame detection is better implemented by checking the EOF
- * bit (FID bit toggling is delayed by one frame compared to the EOF
- * bit), but some devices don't set the bit at end of frame (and the
- * last payload can be lost anyway). We thus must check if the FID has
- * been toggled.
- *
- * stream->last_fid is initialized to -1, so the first isochronous
- * frame will never trigger an end of frame detection.
- *
- * Empty buffers (bytesused == 0) don't trigger end of frame detection
- * as it doesn't make sense to return an empty buffer. This also
- * avoids detecting end of frame conditions at FID toggling if the
- * previous payload had the EOF bit set.
- */
- if (fid != stream->last_fid && buf->bytesused != 0) {
- uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
- "toggled).\n");
- buf->state = UVC_BUF_STATE_READY;
- return -EAGAIN;
- }
-
- stream->last_fid = fid;
-
- return data[0];
-}
-
-static void uvc_video_decode_data(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
-{
- unsigned int maxlen, nbytes;
- void *mem;
-
- if (len <= 0)
- return;
-
- /* Copy the video data to the buffer. */
- maxlen = buf->length - buf->bytesused;
- mem = buf->mem + buf->bytesused;
- nbytes = min((unsigned int)len, maxlen);
- memcpy(mem, data, nbytes);
- buf->bytesused += nbytes;
-
- /* Complete the current frame if the buffer size was exceeded. */
- if (len > maxlen) {
- uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
- buf->state = UVC_BUF_STATE_READY;
- }
-}
-
-static void uvc_video_decode_end(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const __u8 *data, int len)
-{
- /* Mark the buffer as done if the EOF marker is set. */
- if (data[1] & UVC_STREAM_EOF && buf->bytesused != 0) {
- uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
- if (data[0] == len)
- uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
- buf->state = UVC_BUF_STATE_READY;
- if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
- stream->last_fid ^= UVC_STREAM_FID;
- }
-}
-
-/* Video payload encoding is handled by uvc_video_encode_header() and
- * uvc_video_encode_data(). Only bulk transfers are currently supported.
- *
- * uvc_video_encode_header is called at the start of a payload. It adds header
- * data to the transfer buffer and returns the header size. As the only known
- * UVC output device transfers a whole frame in a single payload, the EOF bit
- * is always set in the header.
- *
- * uvc_video_encode_data is called for every URB and copies the data from the
- * video buffer to the transfer buffer.
- */
-static int uvc_video_encode_header(struct uvc_streaming *stream,
- struct uvc_buffer *buf, __u8 *data, int len)
-{
- data[0] = 2; /* Header length */
- data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
- | (stream->last_fid & UVC_STREAM_FID);
- return 2;
-}
-
-static int uvc_video_encode_data(struct uvc_streaming *stream,
- struct uvc_buffer *buf, __u8 *data, int len)
-{
- struct uvc_video_queue *queue = &stream->queue;
- unsigned int nbytes;
- void *mem;
-
- /* Copy video data to the URB buffer. */
- mem = buf->mem + queue->buf_used;
- nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
- nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size,
- nbytes);
- memcpy(data, mem, nbytes);
-
- queue->buf_used += nbytes;
-
- return nbytes;
-}
-
-/* ------------------------------------------------------------------------
- * URB handling
- */
-
-/*
- * Completion handler for video URBs.
- */
-static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream,
- struct uvc_buffer *buf)
-{
- u8 *mem;
- int ret, i;
-
- for (i = 0; i < urb->number_of_packets; ++i) {
- if (urb->iso_frame_desc[i].status < 0) {
- uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
- "lost (%d).\n", urb->iso_frame_desc[i].status);
- /* Mark the buffer as faulty. */
- if (buf != NULL)
- buf->error = 1;
- continue;
- }
-
- /* Decode the payload header. */
- mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
- do {
- ret = uvc_video_decode_start(stream, buf, mem,
- urb->iso_frame_desc[i].actual_length);
- if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
- } while (ret == -EAGAIN);
-
- if (ret < 0)
- continue;
-
- /* Decode the payload data. */
- uvc_video_decode_data(stream, buf, mem + ret,
- urb->iso_frame_desc[i].actual_length - ret);
-
- /* Process the header again. */
- uvc_video_decode_end(stream, buf, mem,
- urb->iso_frame_desc[i].actual_length);
-
- if (buf->state == UVC_BUF_STATE_READY) {
- if (buf->length != buf->bytesused &&
- !(stream->cur_format->flags &
- UVC_FMT_FLAG_COMPRESSED))
- buf->error = 1;
-
- buf = uvc_queue_next_buffer(&stream->queue, buf);
- }
- }
-}
-
-static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream,
- struct uvc_buffer *buf)
-{
- u8 *mem;
- int len, ret;
-
- /*
- * Ignore ZLPs if they're not part of a frame, otherwise process them
- * to trigger the end of payload detection.
- */
- if (urb->actual_length == 0 && stream->bulk.header_size == 0)
- return;
-
- mem = urb->transfer_buffer;
- len = urb->actual_length;
- stream->bulk.payload_size += len;
-
- /* If the URB is the first of its payload, decode and save the
- * header.
- */
- if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
- do {
- ret = uvc_video_decode_start(stream, buf, mem, len);
- if (ret == -EAGAIN)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
- } while (ret == -EAGAIN);
-
- /* If an error occurred skip the rest of the payload. */
- if (ret < 0 || buf == NULL) {
- stream->bulk.skip_payload = 1;
- } else {
- memcpy(stream->bulk.header, mem, ret);
- stream->bulk.header_size = ret;
-
- mem += ret;
- len -= ret;
- }
- }
-
- /* The buffer queue might have been cancelled while a bulk transfer
- * was in progress, so we can reach here with buf equal to NULL. Make
- * sure buf is never dereferenced if NULL.
- */
-
- /* Process video data. */
- if (!stream->bulk.skip_payload && buf != NULL)
- uvc_video_decode_data(stream, buf, mem, len);
-
- /* Detect the payload end by a URB smaller than the maximum size (or
- * a payload size equal to the maximum) and process the header again.
- */
- if (urb->actual_length < urb->transfer_buffer_length ||
- stream->bulk.payload_size >= stream->bulk.max_payload_size) {
- if (!stream->bulk.skip_payload && buf != NULL) {
- uvc_video_decode_end(stream, buf, stream->bulk.header,
- stream->bulk.payload_size);
- if (buf->state == UVC_BUF_STATE_READY)
- buf = uvc_queue_next_buffer(&stream->queue,
- buf);
- }
-
- stream->bulk.header_size = 0;
- stream->bulk.skip_payload = 0;
- stream->bulk.payload_size = 0;
- }
-}
-
-static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream,
- struct uvc_buffer *buf)
-{
- u8 *mem = urb->transfer_buffer;
- int len = stream->urb_size, ret;
-
- if (buf == NULL) {
- urb->transfer_buffer_length = 0;
- return;
- }
-
- /* If the URB is the first of its payload, add the header. */
- if (stream->bulk.header_size == 0) {
- ret = uvc_video_encode_header(stream, buf, mem, len);
- stream->bulk.header_size = ret;
- stream->bulk.payload_size += ret;
- mem += ret;
- len -= ret;
- }
-
- /* Process video data. */
- ret = uvc_video_encode_data(stream, buf, mem, len);
-
- stream->bulk.payload_size += ret;
- len -= ret;
-
- if (buf->bytesused == stream->queue.buf_used ||
- stream->bulk.payload_size == stream->bulk.max_payload_size) {
- if (buf->bytesused == stream->queue.buf_used) {
- stream->queue.buf_used = 0;
- buf->state = UVC_BUF_STATE_READY;
- buf->buf.v4l2_buf.sequence = ++stream->sequence;
- uvc_queue_next_buffer(&stream->queue, buf);
- stream->last_fid ^= UVC_STREAM_FID;
- }
-
- stream->bulk.header_size = 0;
- stream->bulk.payload_size = 0;
- }
-
- urb->transfer_buffer_length = stream->urb_size - len;
-}
-
-static void uvc_video_complete(struct urb *urb)
-{
- struct uvc_streaming *stream = urb->context;
- struct uvc_video_queue *queue = &stream->queue;
- struct uvc_buffer *buf = NULL;
- unsigned long flags;
- int ret;
-
- switch (urb->status) {
- case 0:
- break;
-
- default:
- uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
- "completion handler.\n", urb->status);
-
- case -ENOENT: /* usb_kill_urb() called. */
- if (stream->frozen)
- return;
-
- case -ECONNRESET: /* usb_unlink_urb() called. */
- case -ESHUTDOWN: /* The endpoint is being disabled. */
- uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
- return;
- }
-
- spin_lock_irqsave(&queue->irqlock, flags);
- if (!list_empty(&queue->irqqueue))
- buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
- queue);
- spin_unlock_irqrestore(&queue->irqlock, flags);
-
- stream->decode(urb, stream, buf);
-
- if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
- ret);
- }
-}
-
-/*
- * Free transfer buffers.
- */
-static void uvc_free_urb_buffers(struct uvc_streaming *stream)
-{
- unsigned int i;
-
- for (i = 0; i < UVC_URBS; ++i) {
- if (stream->urb_buffer[i]) {
-#ifndef CONFIG_DMA_NONCOHERENT
- usb_free_coherent(stream->dev->udev, stream->urb_size,
- stream->urb_buffer[i], stream->urb_dma[i]);
-#else
- kfree(stream->urb_buffer[i]);
-#endif
- stream->urb_buffer[i] = NULL;
- }
- }
-
- stream->urb_size = 0;
-}
-
-/*
- * Allocate transfer buffers. This function can be called with buffers
- * already allocated when resuming from suspend, in which case it will
- * return without touching the buffers.
- *
- * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the
- * system is too low on memory try successively smaller numbers of packets
- * until allocation succeeds.
- *
- * Return the number of allocated packets on success or 0 when out of memory.
- */
-static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
- unsigned int size, unsigned int psize, gfp_t gfp_flags)
-{
- unsigned int npackets;
- unsigned int i;
-
- /* Buffers are already allocated, bail out. */
- if (stream->urb_size)
- return stream->urb_size / psize;
-
- /* Compute the number of packets. Bulk endpoints might transfer UVC
- * payloads across multiple URBs.
- */
- npackets = DIV_ROUND_UP(size, psize);
- if (npackets > UVC_MAX_PACKETS)
- npackets = UVC_MAX_PACKETS;
-
- /* Retry allocations until one succeed. */
- for (; npackets > 1; npackets /= 2) {
- for (i = 0; i < UVC_URBS; ++i) {
- stream->urb_size = psize * npackets;
-#ifndef CONFIG_DMA_NONCOHERENT
- stream->urb_buffer[i] = usb_alloc_coherent(
- stream->dev->udev, stream->urb_size,
- gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]);
-#else
- stream->urb_buffer[i] =
- kmalloc(stream->urb_size, gfp_flags | __GFP_NOWARN);
-#endif
- if (!stream->urb_buffer[i]) {
- uvc_free_urb_buffers(stream);
- break;
- }
- }
-
- if (i == UVC_URBS) {
- uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
- "of %ux%u bytes each.\n", UVC_URBS, npackets,
- psize);
- return npackets;
- }
- }
-
- uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes "
- "per packet).\n", psize);
- return 0;
-}
-
-/*
- * Uninitialize isochronous/bulk URBs and free transfer buffers.
- */
-static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
-{
- struct urb *urb;
- unsigned int i;
-
- uvc_video_stats_stop(stream);
-
- for (i = 0; i < UVC_URBS; ++i) {
- urb = stream->urb[i];
- if (urb == NULL)
- continue;
-
- usb_kill_urb(urb);
- usb_free_urb(urb);
- stream->urb[i] = NULL;
- }
-
- if (free_buffers)
- uvc_free_urb_buffers(stream);
-}
-
-/*
- * Initialize isochronous URBs and allocate transfer buffers. The packet size
- * is given by the endpoint.
- */
-static int uvc_init_video_isoc(struct uvc_streaming *stream,
- struct usb_host_endpoint *ep, gfp_t gfp_flags)
-{
- struct urb *urb;
- unsigned int npackets, i, j;
- u16 psize;
- u32 size;
-
- psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- size = stream->ctrl.dwMaxVideoFrameSize;
-
- npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
- if (npackets == 0)
- return -ENOMEM;
-
- size = npackets * psize;
-
- for (i = 0; i < UVC_URBS; ++i) {
- urb = usb_alloc_urb(npackets, gfp_flags);
- if (urb == NULL) {
- uvc_uninit_video(stream, 1);
- return -ENOMEM;
- }
-
- urb->dev = stream->dev->udev;
- urb->context = stream;
- urb->pipe = usb_rcvisocpipe(stream->dev->udev,
- ep->desc.bEndpointAddress);
-#ifndef CONFIG_DMA_NONCOHERENT
- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
- urb->transfer_dma = stream->urb_dma[i];
-#else
- urb->transfer_flags = URB_ISO_ASAP;
-#endif
- urb->interval = ep->desc.bInterval;
- urb->transfer_buffer = stream->urb_buffer[i];
- urb->complete = uvc_video_complete;
- urb->number_of_packets = npackets;
- urb->transfer_buffer_length = size;
-
- for (j = 0; j < npackets; ++j) {
- urb->iso_frame_desc[j].offset = j * psize;
- urb->iso_frame_desc[j].length = psize;
- }
-
- stream->urb[i] = urb;
- }
-
- return 0;
-}
-
-/*
- * Initialize bulk URBs and allocate transfer buffers. The packet size is
- * given by the endpoint.
- */
-static int uvc_init_video_bulk(struct uvc_streaming *stream,
- struct usb_host_endpoint *ep, gfp_t gfp_flags)
-{
- struct urb *urb;
- unsigned int npackets, pipe, i;
- u16 psize;
- u32 size;
-
- psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
- size = stream->ctrl.dwMaxPayloadTransferSize;
- stream->bulk.max_payload_size = size;
-
- npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags);
- if (npackets == 0)
- return -ENOMEM;
-
- size = npackets * psize;
-
- if (usb_endpoint_dir_in(&ep->desc))
- pipe = usb_rcvbulkpipe(stream->dev->udev,
- ep->desc.bEndpointAddress);
- else
- pipe = usb_sndbulkpipe(stream->dev->udev,
- ep->desc.bEndpointAddress);
-
- if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
- size = 0;
-
- for (i = 0; i < UVC_URBS; ++i) {
- urb = usb_alloc_urb(0, gfp_flags);
- if (urb == NULL) {
- uvc_uninit_video(stream, 1);
- return -ENOMEM;
- }
-
- usb_fill_bulk_urb(urb, stream->dev->udev, pipe,
- stream->urb_buffer[i], size, uvc_video_complete,
- stream);
-#ifndef CONFIG_DMA_NONCOHERENT
- urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
- urb->transfer_dma = stream->urb_dma[i];
-#endif
-
- stream->urb[i] = urb;
- }
-
- return 0;
-}
-
-/*
- * Initialize isochronous/bulk URBs and allocate transfer buffers.
- */
-static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
-{
- struct usb_interface *intf = stream->intf;
- struct usb_host_endpoint *ep;
- unsigned int i;
- int ret;
-
- stream->sequence = -1;
- stream->last_fid = -1;
- stream->bulk.header_size = 0;
- stream->bulk.skip_payload = 0;
- stream->bulk.payload_size = 0;
-
- uvc_video_stats_start(stream);
-
- if (intf->num_altsetting > 1) {
- struct usb_host_endpoint *best_ep = NULL;
- unsigned int best_psize = 3 * 1024;
- unsigned int bandwidth;
- unsigned int uninitialized_var(altsetting);
- int intfnum = stream->intfnum;
-
- /* Isochronous endpoint, select the alternate setting. */
- bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
-
- if (bandwidth == 0) {
- uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
- "bandwidth, defaulting to lowest.\n");
- bandwidth = 1;
- } else {
- uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
- "B/frame bandwidth.\n", bandwidth);
- }
-
- for (i = 0; i < intf->num_altsetting; ++i) {
- struct usb_host_interface *alts;
- unsigned int psize;
-
- alts = &intf->altsetting[i];
- ep = uvc_find_endpoint(alts,
- stream->header.bEndpointAddress);
- if (ep == NULL)
- continue;
-
- /* Check if the bandwidth is high enough. */
- psize = le16_to_cpu(ep->desc.wMaxPacketSize);
- psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
- if (psize >= bandwidth && psize <= best_psize) {
- altsetting = alts->desc.bAlternateSetting;
- best_psize = psize;
- best_ep = ep;
- }
- }
-
- if (best_ep == NULL) {
- uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
- "for requested bandwidth.\n");
- return -EIO;
- }
-
- uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
- "(%u B/frame bandwidth).\n", altsetting, best_psize);
-
- ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
- if (ret < 0)
- return ret;
-
- ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
- } else {
- /* Bulk endpoint, proceed to URB initialization. */
- ep = uvc_find_endpoint(&intf->altsetting[0],
- stream->header.bEndpointAddress);
- if (ep == NULL)
- return -EIO;
-
- ret = uvc_init_video_bulk(stream, ep, gfp_flags);
- }
-
- if (ret < 0)
- return ret;
-
- /* Submit the URBs. */
- for (i = 0; i < UVC_URBS; ++i) {
- ret = usb_submit_urb(stream->urb[i], gfp_flags);
- if (ret < 0) {
- uvc_printk(KERN_ERR, "Failed to submit URB %u "
- "(%d).\n", i, ret);
- uvc_uninit_video(stream, 1);
- return ret;
- }
- }
-
- return 0;
-}
-
-/* --------------------------------------------------------------------------
- * Suspend/resume
- */
-
-/*
- * Stop streaming without disabling the video queue.
- *
- * To let userspace applications resume without trouble, we must not touch the
- * video buffers in any way. We mark the device as frozen to make sure the URB
- * completion handler won't try to cancel the queue when we kill the URBs.
- */
-int uvc_video_suspend(struct uvc_streaming *stream)
-{
- if (!uvc_queue_streaming(&stream->queue))
- return 0;
-
- stream->frozen = 1;
- uvc_uninit_video(stream, 0);
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
- return 0;
-}
-
-/*
- * Reconfigure the video interface and restart streaming if it was enabled
- * before suspend.
- *
- * If an error occurs, disable the video queue. This will wake all pending
- * buffers, making sure userspace applications are notified of the problem
- * instead of waiting forever.
- */
-int uvc_video_resume(struct uvc_streaming *stream, int reset)
-{
- int ret;
-
- /* If the bus has been reset on resume, set the alternate setting to 0.
- * This should be the default value, but some devices crash or otherwise
- * misbehave if they don't receive a SET_INTERFACE request before any
- * other video control request.
- */
- if (reset)
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
-
- stream->frozen = 0;
-
- uvc_video_clock_reset(stream);
-
- ret = uvc_commit_video(stream, &stream->ctrl);
- if (ret < 0) {
- uvc_queue_enable(&stream->queue, 0);
- return ret;
- }
-
- if (!uvc_queue_streaming(&stream->queue))
- return 0;
-
- ret = uvc_init_video(stream, GFP_NOIO);
- if (ret < 0)
- uvc_queue_enable(&stream->queue, 0);
-
- return ret;
-}
-
-/* ------------------------------------------------------------------------
- * Video device
- */
-
-/*
- * Initialize the UVC video device by switching to alternate setting 0 and
- * retrieve the default format.
- *
- * Some cameras (namely the Fuji Finepix) set the format and frame
- * indexes to zero. The UVC standard doesn't clearly make this a spec
- * violation, so try to silently fix the values if possible.
- *
- * This function is called before registering the device with V4L.
- */
-int uvc_video_init(struct uvc_streaming *stream)
-{
- struct uvc_streaming_control *probe = &stream->ctrl;
- struct uvc_format *format = NULL;
- struct uvc_frame *frame = NULL;
- unsigned int i;
- int ret;
-
- if (stream->nformats == 0) {
- uvc_printk(KERN_INFO, "No supported video formats found.\n");
- return -EINVAL;
- }
-
- atomic_set(&stream->active, 0);
-
- /* Initialize the video buffers queue. */
- uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param);
-
- /* Alternate setting 0 should be the default, yet the XBox Live Vision
- * Cam (and possibly other devices) crash or otherwise misbehave if
- * they don't receive a SET_INTERFACE request before any other video
- * control request.
- */
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
-
- /* Set the streaming probe control with default streaming parameters
- * retrieved from the device. Webcams that don't suport GET_DEF
- * requests on the probe control will just keep their current streaming
- * parameters.
- */
- if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
- uvc_set_video_ctrl(stream, probe, 1);
-
- /* Initialize the streaming parameters with the probe control current
- * value. This makes sure SET_CUR requests on the streaming commit
- * control will always use values retrieved from a successful GET_CUR
- * request on the probe control, as required by the UVC specification.
- */
- ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
- if (ret < 0)
- return ret;
-
- /* Check if the default format descriptor exists. Use the first
- * available format otherwise.
- */
- for (i = stream->nformats; i > 0; --i) {
- format = &stream->format[i-1];
- if (format->index == probe->bFormatIndex)
- break;
- }
-
- if (format->nframes == 0) {
- uvc_printk(KERN_INFO, "No frame descriptor found for the "
- "default format.\n");
- return -EINVAL;
- }
-
- /* Zero bFrameIndex might be correct. Stream-based formats (including
- * MPEG-2 TS and DV) do not support frames but have a dummy frame
- * descriptor with bFrameIndex set to zero. If the default frame
- * descriptor is not found, use the first available frame.
- */
- for (i = format->nframes; i > 0; --i) {
- frame = &format->frame[i-1];
- if (frame->bFrameIndex == probe->bFrameIndex)
- break;
- }
-
- probe->bFormatIndex = format->index;
- probe->bFrameIndex = frame->bFrameIndex;
-
- stream->cur_format = format;
- stream->cur_frame = frame;
-
- /* Select the video decoding function */
- if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
- stream->decode = uvc_video_decode_isight;
- else if (stream->intf->num_altsetting > 1)
- stream->decode = uvc_video_decode_isoc;
- else
- stream->decode = uvc_video_decode_bulk;
- } else {
- if (stream->intf->num_altsetting == 1)
- stream->decode = uvc_video_encode_bulk;
- else {
- uvc_printk(KERN_INFO, "Isochronous endpoints are not "
- "supported for video output devices.\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/*
- * Enable or disable the video stream.
- */
-int uvc_video_enable(struct uvc_streaming *stream, int enable)
-{
- int ret;
-
- if (!enable) {
- uvc_uninit_video(stream, 1);
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
- uvc_queue_enable(&stream->queue, 0);
- uvc_video_clock_cleanup(stream);
- return 0;
- }
-
- ret = uvc_video_clock_init(stream);
- if (ret < 0)
- return ret;
-
- ret = uvc_queue_enable(&stream->queue, 1);
- if (ret < 0)
- goto error_queue;
-
- /* Commit the streaming parameters. */
- ret = uvc_commit_video(stream, &stream->ctrl);
- if (ret < 0)
- goto error_commit;
-
- ret = uvc_init_video(stream, GFP_KERNEL);
- if (ret < 0)
- goto error_video;
-
- return 0;
-
-error_video:
- usb_set_interface(stream->dev->udev, stream->intfnum, 0);
-error_commit:
- uvc_queue_enable(&stream->queue, 0);
-error_queue:
- uvc_video_clock_cleanup(stream);
-
- return ret;
-}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
deleted file mode 100644
index 7c3d082505b7..000000000000
--- a/drivers/media/video/uvc/uvcvideo.h
+++ /dev/null
@@ -1,709 +0,0 @@
-#ifndef _USB_VIDEO_H_
-#define _USB_VIDEO_H_
-
-#ifndef __KERNEL__
-#error "The uvcvideo.h header is deprecated, use linux/uvcvideo.h instead."
-#endif /* __KERNEL__ */
-
-#include <linux/kernel.h>
-#include <linux/poll.h>
-#include <linux/usb.h>
-#include <linux/usb/video.h>
-#include <linux/uvcvideo.h>
-#include <linux/videodev2.h>
-#include <media/media-device.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-fh.h>
-#include <media/videobuf2-core.h>
-
-/* --------------------------------------------------------------------------
- * UVC constants
- */
-
-#define UVC_TERM_INPUT 0x0000
-#define UVC_TERM_OUTPUT 0x8000
-#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000)
-
-#define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff)
-#define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0)
-#define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0)
-#define UVC_ENTITY_IS_ITERM(entity) \
- (UVC_ENTITY_IS_TERM(entity) && \
- ((entity)->type & 0x8000) == UVC_TERM_INPUT)
-#define UVC_ENTITY_IS_OTERM(entity) \
- (UVC_ENTITY_IS_TERM(entity) && \
- ((entity)->type & 0x8000) == UVC_TERM_OUTPUT)
-
-
-/* ------------------------------------------------------------------------
- * GUIDs
- */
-#define UVC_GUID_UVC_CAMERA \
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
-#define UVC_GUID_UVC_OUTPUT \
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}
-#define UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT \
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03}
-#define UVC_GUID_UVC_PROCESSING \
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01}
-#define UVC_GUID_UVC_SELECTOR \
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}
-
-#define UVC_GUID_FORMAT_MJPEG \
- { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_YUY2 \
- { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_YUY2_ISIGHT \
- { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_NV12 \
- { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_YV12 \
- { 'Y', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_I420 \
- { 'I', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_UYVY \
- { 'U', 'Y', 'V', 'Y', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_Y800 \
- { 'Y', '8', '0', '0', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_Y16 \
- { 'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_BY8 \
- { 'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_RGBP \
- { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-#define UVC_GUID_FORMAT_M420 \
- { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-
-#define UVC_GUID_FORMAT_H264 \
- { 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, \
- 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
-
-/* ------------------------------------------------------------------------
- * Driver specific constants.
- */
-
-#define DRIVER_VERSION "1.1.1"
-
-/* Number of isochronous URBs. */
-#define UVC_URBS 5
-/* Maximum number of packets per URB. */
-#define UVC_MAX_PACKETS 32
-/* Maximum number of video buffers. */
-#define UVC_MAX_VIDEO_BUFFERS 32
-/* Maximum status buffer size in bytes of interrupt URB. */
-#define UVC_MAX_STATUS_SIZE 16
-
-#define UVC_CTRL_CONTROL_TIMEOUT 300
-#define UVC_CTRL_STREAMING_TIMEOUT 5000
-
-/* Maximum allowed number of control mappings per device */
-#define UVC_MAX_CONTROL_MAPPINGS 1024
-#define UVC_MAX_CONTROL_MENU_ENTRIES 32
-
-/* Devices quirks */
-#define UVC_QUIRK_STATUS_INTERVAL 0x00000001
-#define UVC_QUIRK_PROBE_MINMAX 0x00000002
-#define UVC_QUIRK_PROBE_EXTRAFIELDS 0x00000004
-#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
-#define UVC_QUIRK_STREAM_NO_FID 0x00000010
-#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
-#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080
-#define UVC_QUIRK_PROBE_DEF 0x00000100
-#define UVC_QUIRK_RESTRICT_FRAME_RATE 0x00000200
-
-/* Format flags */
-#define UVC_FMT_FLAG_COMPRESSED 0x00000001
-#define UVC_FMT_FLAG_STREAM 0x00000002
-
-/* ------------------------------------------------------------------------
- * Structures.
- */
-
-struct uvc_device;
-
-/* TODO: Put the most frequently accessed fields at the beginning of
- * structures to maximize cache efficiency.
- */
-struct uvc_control_info {
- struct list_head mappings;
-
- __u8 entity[16];
- __u8 index; /* Bit index in bmControls */
- __u8 selector;
-
- __u16 size;
- __u32 flags;
-};
-
-struct uvc_control_mapping {
- struct list_head list;
- struct list_head ev_subs;
-
- __u32 id;
- __u8 name[32];
- __u8 entity[16];
- __u8 selector;
-
- __u8 size;
- __u8 offset;
- enum v4l2_ctrl_type v4l2_type;
- __u32 data_type;
-
- struct uvc_menu_info *menu_info;
- __u32 menu_count;
-
- __u32 master_id;
- __s32 master_manual;
- __u32 slave_ids[2];
-
- __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
- const __u8 *data);
- void (*set) (struct uvc_control_mapping *mapping, __s32 value,
- __u8 *data);
-};
-
-struct uvc_control {
- struct uvc_entity *entity;
- struct uvc_control_info info;
-
- __u8 index; /* Used to match the uvc_control entry with a
- uvc_control_info. */
- __u8 dirty:1,
- loaded:1,
- modified:1,
- cached:1,
- initialized:1;
-
- __u8 *uvc_data;
-};
-
-struct uvc_format_desc {
- char *name;
- __u8 guid[16];
- __u32 fcc;
-};
-
-/* The term 'entity' refers to both UVC units and UVC terminals.
- *
- * The type field is either the terminal type (wTerminalType in the terminal
- * descriptor), or the unit type (bDescriptorSubtype in the unit descriptor).
- * As the bDescriptorSubtype field is one byte long, the type value will
- * always have a null MSB for units. All terminal types defined by the UVC
- * specification have a non-null MSB, so it is safe to use the MSB to
- * differentiate between units and terminals as long as the descriptor parsing
- * code makes sure terminal types have a non-null MSB.
- *
- * For terminals, the type's most significant bit stores the terminal
- * direction (either UVC_TERM_INPUT or UVC_TERM_OUTPUT). The type field should
- * always be accessed with the UVC_ENTITY_* macros and never directly.
- */
-
-struct uvc_entity {
- struct list_head list; /* Entity as part of a UVC device. */
- struct list_head chain; /* Entity as part of a video device
- * chain. */
- __u8 id;
- __u16 type;
- char name[64];
-
- /* Media controller-related fields. */
- struct video_device *vdev;
- struct v4l2_subdev subdev;
- unsigned int num_pads;
- unsigned int num_links;
- struct media_pad *pads;
-
- union {
- struct {
- __u16 wObjectiveFocalLengthMin;
- __u16 wObjectiveFocalLengthMax;
- __u16 wOcularFocalLength;
- __u8 bControlSize;
- __u8 *bmControls;
- } camera;
-
- struct {
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 bTransportModeSize;
- __u8 *bmTransportModes;
- } media;
-
- struct {
- } output;
-
- struct {
- __u16 wMaxMultiplier;
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 bmVideoStandards;
- } processing;
-
- struct {
- } selector;
-
- struct {
- __u8 guidExtensionCode[16];
- __u8 bNumControls;
- __u8 bControlSize;
- __u8 *bmControls;
- __u8 *bmControlsType;
- } extension;
- };
-
- __u8 bNrInPins;
- __u8 *baSourceID;
-
- unsigned int ncontrols;
- struct uvc_control *controls;
-};
-
-struct uvc_frame {
- __u8 bFrameIndex;
- __u8 bmCapabilities;
- __u16 wWidth;
- __u16 wHeight;
- __u32 dwMinBitRate;
- __u32 dwMaxBitRate;
- __u32 dwMaxVideoFrameBufferSize;
- __u8 bFrameIntervalType;
- __u32 dwDefaultFrameInterval;
- __u32 *dwFrameInterval;
-};
-
-struct uvc_format {
- __u8 type;
- __u8 index;
- __u8 bpp;
- __u8 colorspace;
- __u32 fcc;
- __u32 flags;
-
- char name[32];
-
- unsigned int nframes;
- struct uvc_frame *frame;
-};
-
-struct uvc_streaming_header {
- __u8 bNumFormats;
- __u8 bEndpointAddress;
- __u8 bTerminalLink;
- __u8 bControlSize;
- __u8 *bmaControls;
- /* The following fields are used by input headers only. */
- __u8 bmInfo;
- __u8 bStillCaptureMethod;
- __u8 bTriggerSupport;
- __u8 bTriggerUsage;
-};
-
-enum uvc_buffer_state {
- UVC_BUF_STATE_IDLE = 0,
- UVC_BUF_STATE_QUEUED = 1,
- UVC_BUF_STATE_ACTIVE = 2,
- UVC_BUF_STATE_READY = 3,
- UVC_BUF_STATE_DONE = 4,
- UVC_BUF_STATE_ERROR = 5,
-};
-
-struct uvc_buffer {
- struct vb2_buffer buf;
- struct list_head queue;
-
- enum uvc_buffer_state state;
- unsigned int error;
-
- void *mem;
- unsigned int length;
- unsigned int bytesused;
-
- u32 pts;
-};
-
-#define UVC_QUEUE_DISCONNECTED (1 << 0)
-#define UVC_QUEUE_DROP_CORRUPTED (1 << 1)
-
-struct uvc_video_queue {
- struct vb2_queue queue;
- struct mutex mutex; /* Protects queue */
-
- unsigned int flags;
- unsigned int buf_used;
-
- spinlock_t irqlock; /* Protects irqqueue */
- struct list_head irqqueue;
-};
-
-struct uvc_video_chain {
- struct uvc_device *dev;
- struct list_head list;
-
- struct list_head entities; /* All entities */
- struct uvc_entity *processing; /* Processing unit */
- struct uvc_entity *selector; /* Selector unit */
-
- struct mutex ctrl_mutex; /* Protects ctrl.info */
-};
-
-struct uvc_stats_frame {
- unsigned int size; /* Number of bytes captured */
- unsigned int first_data; /* Index of the first non-empty packet */
-
- unsigned int nb_packets; /* Number of packets */
- unsigned int nb_empty; /* Number of empty packets */
- unsigned int nb_invalid; /* Number of packets with an invalid header */
- unsigned int nb_errors; /* Number of packets with the error bit set */
-
- unsigned int nb_pts; /* Number of packets with a PTS timestamp */
- unsigned int nb_pts_diffs; /* Number of PTS differences inside a frame */
- unsigned int last_pts_diff; /* Index of the last PTS difference */
- bool has_initial_pts; /* Whether the first non-empty packet has a PTS */
- bool has_early_pts; /* Whether a PTS is present before the first non-empty packet */
- u32 pts; /* PTS of the last packet */
-
- unsigned int nb_scr; /* Number of packets with a SCR timestamp */
- unsigned int nb_scr_diffs; /* Number of SCR.STC differences inside a frame */
- u16 scr_sof; /* SCR.SOF of the last packet */
- u32 scr_stc; /* SCR.STC of the last packet */
-};
-
-struct uvc_stats_stream {
- struct timespec start_ts; /* Stream start timestamp */
- struct timespec stop_ts; /* Stream stop timestamp */
-
- unsigned int nb_frames; /* Number of frames */
-
- unsigned int nb_packets; /* Number of packets */
- unsigned int nb_empty; /* Number of empty packets */
- unsigned int nb_invalid; /* Number of packets with an invalid header */
- unsigned int nb_errors; /* Number of packets with the error bit set */
-
- unsigned int nb_pts_constant; /* Number of frames with constant PTS */
- unsigned int nb_pts_early; /* Number of frames with early PTS */
- unsigned int nb_pts_initial; /* Number of frames with initial PTS */
-
- unsigned int nb_scr_count_ok; /* Number of frames with at least one SCR per non empty packet */
- unsigned int nb_scr_diffs_ok; /* Number of frames with varying SCR.STC */
- unsigned int scr_sof_count; /* STC.SOF counter accumulated since stream start */
- unsigned int scr_sof; /* STC.SOF of the last packet */
- unsigned int min_sof; /* Minimum STC.SOF value */
- unsigned int max_sof; /* Maximum STC.SOF value */
-};
-
-struct uvc_streaming {
- struct list_head list;
- struct uvc_device *dev;
- struct video_device *vdev;
- struct uvc_video_chain *chain;
- atomic_t active;
-
- struct usb_interface *intf;
- int intfnum;
- __u16 maxpsize;
-
- struct uvc_streaming_header header;
- enum v4l2_buf_type type;
-
- unsigned int nformats;
- struct uvc_format *format;
-
- struct uvc_streaming_control ctrl;
- struct uvc_format *cur_format;
- struct uvc_frame *cur_frame;
- /* Protect access to ctrl, cur_format, cur_frame and hardware video
- * probe control.
- */
- struct mutex mutex;
-
- /* Buffers queue. */
- unsigned int frozen : 1;
- struct uvc_video_queue queue;
- void (*decode) (struct urb *urb, struct uvc_streaming *video,
- struct uvc_buffer *buf);
-
- /* Context data used by the bulk completion handler. */
- struct {
- __u8 header[256];
- unsigned int header_size;
- int skip_payload;
- __u32 payload_size;
- __u32 max_payload_size;
- } bulk;
-
- struct urb *urb[UVC_URBS];
- char *urb_buffer[UVC_URBS];
- dma_addr_t urb_dma[UVC_URBS];
- unsigned int urb_size;
-
- __u32 sequence;
- __u8 last_fid;
-
- /* debugfs */
- struct dentry *debugfs_dir;
- struct {
- struct uvc_stats_frame frame;
- struct uvc_stats_stream stream;
- } stats;
-
- /* Timestamps support. */
- struct uvc_clock {
- struct uvc_clock_sample {
- u32 dev_stc;
- u16 dev_sof;
- struct timespec host_ts;
- u16 host_sof;
- } *samples;
-
- unsigned int head;
- unsigned int count;
- unsigned int size;
-
- u16 last_sof;
- u16 sof_offset;
-
- spinlock_t lock;
- } clock;
-};
-
-enum uvc_device_state {
- UVC_DEV_DISCONNECTED = 1,
-};
-
-struct uvc_device {
- struct usb_device *udev;
- struct usb_interface *intf;
- unsigned long warnings;
- __u32 quirks;
- int intfnum;
- char name[32];
-
- enum uvc_device_state state;
- atomic_t users;
- atomic_t nmappings;
-
- /* Video control interface */
-#ifdef CONFIG_MEDIA_CONTROLLER
- struct media_device mdev;
-#endif
- struct v4l2_device vdev;
- __u16 uvc_version;
- __u32 clock_frequency;
-
- struct list_head entities;
- struct list_head chains;
-
- /* Video Streaming interfaces */
- struct list_head streams;
- atomic_t nstreams;
-
- /* Status Interrupt Endpoint */
- struct usb_host_endpoint *int_ep;
- struct urb *int_urb;
- __u8 *status;
- struct input_dev *input;
- char input_phys[64];
-};
-
-enum uvc_handle_state {
- UVC_HANDLE_PASSIVE = 0,
- UVC_HANDLE_ACTIVE = 1,
-};
-
-struct uvc_fh {
- struct v4l2_fh vfh;
- struct uvc_video_chain *chain;
- struct uvc_streaming *stream;
- enum uvc_handle_state state;
-};
-
-struct uvc_driver {
- struct usb_driver driver;
-};
-
-/* ------------------------------------------------------------------------
- * Debugging, printing and logging
- */
-
-#define UVC_TRACE_PROBE (1 << 0)
-#define UVC_TRACE_DESCR (1 << 1)
-#define UVC_TRACE_CONTROL (1 << 2)
-#define UVC_TRACE_FORMAT (1 << 3)
-#define UVC_TRACE_CAPTURE (1 << 4)
-#define UVC_TRACE_CALLS (1 << 5)
-#define UVC_TRACE_IOCTL (1 << 6)
-#define UVC_TRACE_FRAME (1 << 7)
-#define UVC_TRACE_SUSPEND (1 << 8)
-#define UVC_TRACE_STATUS (1 << 9)
-#define UVC_TRACE_VIDEO (1 << 10)
-#define UVC_TRACE_STATS (1 << 11)
-#define UVC_TRACE_CLOCK (1 << 12)
-
-#define UVC_WARN_MINMAX 0
-#define UVC_WARN_PROBE_DEF 1
-#define UVC_WARN_XU_GET_RES 2
-
-extern unsigned int uvc_clock_param;
-extern unsigned int uvc_no_drop_param;
-extern unsigned int uvc_trace_param;
-extern unsigned int uvc_timeout_param;
-
-#define uvc_trace(flag, msg...) \
- do { \
- if (uvc_trace_param & flag) \
- printk(KERN_DEBUG "uvcvideo: " msg); \
- } while (0)
-
-#define uvc_warn_once(dev, warn, msg...) \
- do { \
- if (!test_and_set_bit(warn, &dev->warnings)) \
- printk(KERN_INFO "uvcvideo: " msg); \
- } while (0)
-
-#define uvc_printk(level, msg...) \
- printk(level "uvcvideo: " msg)
-
-/* --------------------------------------------------------------------------
- * Internal functions.
- */
-
-/* Core driver */
-extern struct uvc_driver uvc_driver;
-
-extern struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
-
-/* Video buffers queue management. */
-extern void uvc_queue_init(struct uvc_video_queue *queue,
- enum v4l2_buf_type type, int drop_corrupted);
-extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-extern void uvc_free_buffers(struct uvc_video_queue *queue);
-extern int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-extern int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-extern int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking);
-extern int uvc_queue_enable(struct uvc_video_queue *queue, int enable);
-extern void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
-extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
- struct uvc_buffer *buf);
-extern int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma);
-extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
- struct file *file, poll_table *wait);
-#ifndef CONFIG_MMU
-extern unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff);
-#endif
-extern int uvc_queue_allocated(struct uvc_video_queue *queue);
-static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
-{
- return vb2_is_streaming(&queue->queue);
-}
-
-/* V4L2 interface */
-extern const struct v4l2_file_operations uvc_fops;
-
-/* Media controller */
-extern int uvc_mc_register_entities(struct uvc_video_chain *chain);
-extern void uvc_mc_cleanup_entity(struct uvc_entity *entity);
-
-/* Video */
-extern int uvc_video_init(struct uvc_streaming *stream);
-extern int uvc_video_suspend(struct uvc_streaming *stream);
-extern int uvc_video_resume(struct uvc_streaming *stream, int reset);
-extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
-extern int uvc_probe_video(struct uvc_streaming *stream,
- struct uvc_streaming_control *probe);
-extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
- __u8 intfnum, __u8 cs, void *data, __u16 size);
-void uvc_video_clock_update(struct uvc_streaming *stream,
- struct v4l2_buffer *v4l2_buf,
- struct uvc_buffer *buf);
-
-/* Status */
-extern int uvc_status_init(struct uvc_device *dev);
-extern void uvc_status_cleanup(struct uvc_device *dev);
-extern int uvc_status_start(struct uvc_device *dev);
-extern void uvc_status_stop(struct uvc_device *dev);
-extern int uvc_status_suspend(struct uvc_device *dev);
-extern int uvc_status_resume(struct uvc_device *dev);
-
-/* Controls */
-extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
-
-extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
- struct v4l2_queryctrl *v4l2_ctrl);
-extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
- struct v4l2_querymenu *query_menu);
-
-extern int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
- const struct uvc_control_mapping *mapping);
-extern int uvc_ctrl_init_device(struct uvc_device *dev);
-extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
-extern int uvc_ctrl_resume_device(struct uvc_device *dev);
-
-extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
-extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
- const struct v4l2_ext_control *xctrls,
- unsigned int xctrls_count);
-static inline int uvc_ctrl_commit(struct uvc_fh *handle,
- const struct v4l2_ext_control *xctrls,
- unsigned int xctrls_count)
-{
- return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count);
-}
-static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
-{
- return __uvc_ctrl_commit(handle, 1, NULL, 0);
-}
-
-extern int uvc_ctrl_get(struct uvc_video_chain *chain,
- struct v4l2_ext_control *xctrl);
-extern int uvc_ctrl_set(struct uvc_video_chain *chain,
- struct v4l2_ext_control *xctrl);
-
-extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
- struct uvc_xu_control_query *xqry);
-
-/* Utility functions */
-extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
- unsigned int n_terms, unsigned int threshold);
-extern uint32_t uvc_fraction_to_interval(uint32_t numerator,
- uint32_t denominator);
-extern struct usb_host_endpoint *uvc_find_endpoint(
- struct usb_host_interface *alts, __u8 epaddr);
-
-/* Quirks support */
-void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
- struct uvc_buffer *buf);
-
-/* debugfs and statistics */
-int uvc_debugfs_init(void);
-void uvc_debugfs_cleanup(void);
-int uvc_debugfs_init_stream(struct uvc_streaming *stream);
-void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream);
-
-size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf,
- size_t size);
-
-#endif
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
deleted file mode 100644
index 1baec8393306..000000000000
--- a/drivers/media/video/v4l2-common.c
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Video for Linux Two
- *
- * A generic video device interface for the LINUX operating system
- * using a set of device structures/vectors for low level operations.
- *
- * This file replaces the videodev.c file that comes with the
- * regular kernel distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Author: Bill Dirks <bill@thedirks.org>
- * based on code by Alan Cox, <alan@cymru.net>
- *
- */
-
-/*
- * Video capture interface for Linux
- *
- * A generic video device interface for the LINUX operating system
- * using a set of device structures/vectors for low level operations.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk>
- *
- * Fixes:
- */
-
-/*
- * Video4linux 1/2 integration by Justin Schoeman
- * <justin@suntiger.ee.up.ac.za>
- * 2.4 PROCFS support ported from 2.4 kernels by
- * Iñaki García Etxebarria <garetxe@euskalnet.net>
- * Makefile fix by "W. Michael Petullo" <mike@flyn.org>
- * 2.4 devfs support ported from 2.4 kernels by
- * Dan Merillat <dan@merillat.org>
- * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman)
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/i2c.h>
-#if defined(CONFIG_SPI)
-#include <linux/spi/spi.h>
-#endif
-#include <asm/uaccess.h>
-#include <asm/pgtable.h>
-#include <asm/io.h>
-#include <asm/div64.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-chip-ident.h>
-
-#include <linux/videodev2.h>
-
-MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr");
-MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers");
-MODULE_LICENSE("GPL");
-
-/*
- *
- * V 4 L 2 D R I V E R H E L P E R A P I
- *
- */
-
-/*
- * Video Standard Operations (contributed by Michael Schimek)
- */
-
-/* Helper functions for control handling */
-
-/* Check for correctness of the ctrl's value based on the data from
- struct v4l2_queryctrl and the available menu items. Note that
- menu_items may be NULL, in that case it is ignored. */
-int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
- const char * const *menu_items)
-{
- if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED)
- return -EINVAL;
- if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED)
- return -EBUSY;
- if (qctrl->type == V4L2_CTRL_TYPE_STRING)
- return 0;
- if (qctrl->type == V4L2_CTRL_TYPE_BUTTON ||
- qctrl->type == V4L2_CTRL_TYPE_INTEGER64 ||
- qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
- return 0;
- if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum)
- return -ERANGE;
- if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) {
- if (menu_items[ctrl->value] == NULL ||
- menu_items[ctrl->value][0] == '\0')
- return -EINVAL;
- }
- if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
- (ctrl->value & ~qctrl->maximum))
- return -ERANGE;
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_check);
-
-/* Fill in a struct v4l2_queryctrl */
-int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def)
-{
- const char *name;
-
- v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type,
- &min, &max, &step, &def, &qctrl->flags);
-
- if (name == NULL)
- return -EINVAL;
-
- qctrl->minimum = min;
- qctrl->maximum = max;
- qctrl->step = step;
- qctrl->default_value = def;
- qctrl->reserved[0] = qctrl->reserved[1] = 0;
- strlcpy(qctrl->name, name, sizeof(qctrl->name));
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_fill);
-
-/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
- the menu. The qctrl pointer may be NULL, in which case it is ignored.
- If menu_items is NULL, then the menu items are retrieved using
- v4l2_ctrl_get_menu. */
-int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl,
- const char * const *menu_items)
-{
- int i;
-
- qmenu->reserved = 0;
- if (menu_items == NULL)
- menu_items = v4l2_ctrl_get_menu(qmenu->id);
- if (menu_items == NULL ||
- (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum)))
- return -EINVAL;
- for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
- if (menu_items[i] == NULL || menu_items[i][0] == '\0')
- return -EINVAL;
- strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu);
-
-/* Fill in a struct v4l2_querymenu based on the specified array of valid
- menu items (terminated by V4L2_CTRL_MENU_IDS_END).
- Use this if there are 'holes' in the list of valid menu items. */
-int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids)
-{
- const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id);
-
- qmenu->reserved = 0;
- if (menu_items == NULL || ids == NULL)
- return -EINVAL;
- while (*ids != V4L2_CTRL_MENU_IDS_END) {
- if (*ids++ == qmenu->index) {
- strlcpy(qmenu->name, menu_items[qmenu->index],
- sizeof(qmenu->name));
- return 0;
- }
- }
- return -EINVAL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
-
-/* ctrl_classes points to an array of u32 pointers, the last element is
- a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
- Each array must be sorted low to high and belong to the same control
- class. The array of u32 pointers must also be sorted, from low class IDs
- to high class IDs.
-
- This function returns the first ID that follows after the given ID.
- When no more controls are available 0 is returned. */
-u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id)
-{
- u32 ctrl_class = V4L2_CTRL_ID2CLASS(id);
- const u32 *pctrl;
-
- if (ctrl_classes == NULL)
- return 0;
-
- /* if no query is desired, then check if the ID is part of ctrl_classes */
- if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) {
- /* find class */
- while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class)
- ctrl_classes++;
- if (*ctrl_classes == NULL)
- return 0;
- pctrl = *ctrl_classes;
- /* find control ID */
- while (*pctrl && *pctrl != id) pctrl++;
- return *pctrl ? id : 0;
- }
- id &= V4L2_CTRL_ID_MASK;
- id++; /* select next control */
- /* find first class that matches (or is greater than) the class of
- the ID */
- while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class)
- ctrl_classes++;
- /* no more classes */
- if (*ctrl_classes == NULL)
- return 0;
- pctrl = *ctrl_classes;
- /* find first ctrl within the class that is >= ID */
- while (*pctrl && *pctrl < id) pctrl++;
- if (*pctrl)
- return *pctrl;
- /* we are at the end of the controls of the current class. */
- /* continue with next class if available */
- ctrl_classes++;
- if (*ctrl_classes == NULL)
- return 0;
- return **ctrl_classes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_next);
-
-int v4l2_chip_match_host(const struct v4l2_dbg_match *match)
-{
- switch (match->type) {
- case V4L2_CHIP_MATCH_HOST:
- return match->addr == 0;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_host);
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match)
-{
- int len;
-
- if (c == NULL || match == NULL)
- return 0;
-
- switch (match->type) {
- case V4L2_CHIP_MATCH_I2C_DRIVER:
- if (c->driver == NULL || c->driver->driver.name == NULL)
- return 0;
- len = strlen(c->driver->driver.name);
- /* legacy drivers have a ' suffix, don't try to match that */
- if (len && c->driver->driver.name[len - 1] == '\'')
- len--;
- return len && !strncmp(c->driver->driver.name, match->name, len);
- case V4L2_CHIP_MATCH_I2C_ADDR:
- return c->addr == match->addr;
- default:
- return 0;
- }
-}
-EXPORT_SYMBOL(v4l2_chip_match_i2c_client);
-
-int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip,
- u32 ident, u32 revision)
-{
- if (!v4l2_chip_match_i2c_client(c, &chip->match))
- return 0;
- if (chip->ident == V4L2_IDENT_NONE) {
- chip->ident = ident;
- chip->revision = revision;
- }
- else {
- chip->ident = V4L2_IDENT_AMBIGUOUS;
- chip->revision = 0;
- }
- return 0;
-}
-EXPORT_SYMBOL(v4l2_chip_ident_i2c_client);
-
-/* ----------------------------------------------------------------- */
-
-/* I2C Helper functions */
-
-
-void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
- const struct v4l2_subdev_ops *ops)
-{
- v4l2_subdev_init(sd, ops);
- sd->flags |= V4L2_SUBDEV_FL_IS_I2C;
- /* the owner is the same as the i2c_client's driver owner */
- sd->owner = client->driver->driver.owner;
- /* i2c_client and v4l2_subdev point to one another */
- v4l2_set_subdevdata(sd, client);
- i2c_set_clientdata(client, sd);
- /* initialize name */
- snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
- client->driver->driver.name, i2c_adapter_id(client->adapter),
- client->addr);
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
-
-
-
-/* Load an i2c sub-device. */
-struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter, struct i2c_board_info *info,
- const unsigned short *probe_addrs)
-{
- struct v4l2_subdev *sd = NULL;
- struct i2c_client *client;
-
- BUG_ON(!v4l2_dev);
-
- request_module(I2C_MODULE_PREFIX "%s", info->type);
-
- /* Create the i2c client */
- if (info->addr == 0 && probe_addrs)
- client = i2c_new_probed_device(adapter, info, probe_addrs,
- NULL);
- else
- client = i2c_new_device(adapter, info);
-
- /* Note: by loading the module first we are certain that c->driver
- will be set if the driver was found. If the module was not loaded
- first, then the i2c core tries to delay-load the module for us,
- and then c->driver is still NULL until the module is finally
- loaded. This delay-load mechanism doesn't work if other drivers
- want to use the i2c device, so explicitly loading the module
- is the best alternative. */
- if (client == NULL || client->driver == NULL)
- goto error;
-
- /* Lock the module so we can safely get the v4l2_subdev pointer */
- if (!try_module_get(client->driver->driver.owner))
- goto error;
- sd = i2c_get_clientdata(client);
-
- /* Register with the v4l2_device which increases the module's
- use count as well. */
- if (v4l2_device_register_subdev(v4l2_dev, sd))
- sd = NULL;
- /* Decrease the module use count to match the first try_module_get. */
- module_put(client->driver->driver.owner);
-
-error:
- /* If we have a client but no subdev, then something went wrong and
- we must unregister the client. */
- if (client && sd == NULL)
- i2c_unregister_device(client);
- return sd;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board);
-
-struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev,
- struct i2c_adapter *adapter, const char *client_type,
- u8 addr, const unsigned short *probe_addrs)
-{
- struct i2c_board_info info;
-
- /* Setup the i2c board info with the device type and
- the device address. */
- memset(&info, 0, sizeof(info));
- strlcpy(info.type, client_type, sizeof(info.type));
- info.addr = addr;
-
- return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs);
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
-
-/* Return i2c client address of v4l2_subdev. */
-unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
-{
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- return client ? client->addr : I2C_CLIENT_END;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
-
-/* Return a list of I2C tuner addresses to probe. Use only if the tuner
- addresses are unknown. */
-const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
-{
- static const unsigned short radio_addrs[] = {
-#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
- 0x10,
-#endif
- 0x60,
- I2C_CLIENT_END
- };
- static const unsigned short demod_addrs[] = {
- 0x42, 0x43, 0x4a, 0x4b,
- I2C_CLIENT_END
- };
- static const unsigned short tv_addrs[] = {
- 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
- 0x60, 0x61, 0x62, 0x63, 0x64,
- I2C_CLIENT_END
- };
-
- switch (type) {
- case ADDRS_RADIO:
- return radio_addrs;
- case ADDRS_DEMOD:
- return demod_addrs;
- case ADDRS_TV:
- return tv_addrs;
- case ADDRS_TV_WITH_DEMOD:
- return tv_addrs + 4;
- }
- return NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
-
-#endif /* defined(CONFIG_I2C) */
-
-#if defined(CONFIG_SPI)
-
-/* Load a spi sub-device. */
-
-void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
- const struct v4l2_subdev_ops *ops)
-{
- v4l2_subdev_init(sd, ops);
- sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
- /* the owner is the same as the spi_device's driver owner */
- sd->owner = spi->dev.driver->owner;
- /* spi_device and v4l2_subdev point to one another */
- v4l2_set_subdevdata(sd, spi);
- spi_set_drvdata(spi, sd);
- /* initialize name */
- strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
-}
-EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);
-
-struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
- struct spi_master *master, struct spi_board_info *info)
-{
- struct v4l2_subdev *sd = NULL;
- struct spi_device *spi = NULL;
-
- BUG_ON(!v4l2_dev);
-
- if (info->modalias)
- request_module(info->modalias);
-
- spi = spi_new_device(master, info);
-
- if (spi == NULL || spi->dev.driver == NULL)
- goto error;
-
- if (!try_module_get(spi->dev.driver->owner))
- goto error;
-
- sd = spi_get_drvdata(spi);
-
- /* Register with the v4l2_device which increases the module's
- use count as well. */
- if (v4l2_device_register_subdev(v4l2_dev, sd))
- sd = NULL;
-
- /* Decrease the module use count to match the first try_module_get. */
- module_put(spi->dev.driver->owner);
-
-error:
- /* If we have a client but no subdev, then something went wrong and
- we must unregister the client. */
- if (spi && sd == NULL)
- spi_unregister_device(spi);
-
- return sd;
-}
-EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);
-
-#endif /* defined(CONFIG_SPI) */
-
-/* Clamp x to be between min and max, aligned to a multiple of 2^align. min
- * and max don't have to be aligned, but there must be at least one valid
- * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples
- * of 16 between 17 and 31. */
-static unsigned int clamp_align(unsigned int x, unsigned int min,
- unsigned int max, unsigned int align)
-{
- /* Bits that must be zero to be aligned */
- unsigned int mask = ~((1 << align) - 1);
-
- /* Round to nearest aligned value */
- if (align)
- x = (x + (1 << (align - 1))) & mask;
-
- /* Clamp to aligned value of min and max */
- if (x < min)
- x = (min + ~mask) & mask;
- else if (x > max)
- x = max & mask;
-
- return x;
-}
-
-/* Bound an image to have a width between wmin and wmax, and height between
- * hmin and hmax, inclusive. Additionally, the width will be a multiple of
- * 2^walign, the height will be a multiple of 2^halign, and the overall size
- * (width*height) will be a multiple of 2^salign. The image may be shrunk
- * or enlarged to fit the alignment constraints.
- *
- * The width or height maximum must not be smaller than the corresponding
- * minimum. The alignments must not be so high there are no possible image
- * sizes within the allowed bounds. wmin and hmin must be at least 1
- * (don't use 0). If you don't care about a certain alignment, specify 0,
- * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If
- * you only want to adjust downward, specify a maximum that's the same as
- * the initial value.
- */
-void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax,
- unsigned int walign,
- u32 *h, unsigned int hmin, unsigned int hmax,
- unsigned int halign, unsigned int salign)
-{
- *w = clamp_align(*w, wmin, wmax, walign);
- *h = clamp_align(*h, hmin, hmax, halign);
-
- /* Usually we don't need to align the size and are done now. */
- if (!salign)
- return;
-
- /* How much alignment do we have? */
- walign = __ffs(*w);
- halign = __ffs(*h);
- /* Enough to satisfy the image alignment? */
- if (walign + halign < salign) {
- /* Max walign where there is still a valid width */
- unsigned int wmaxa = __fls(wmax ^ (wmin - 1));
- /* Max halign where there is still a valid height */
- unsigned int hmaxa = __fls(hmax ^ (hmin - 1));
-
- /* up the smaller alignment until we have enough */
- do {
- if (halign >= hmaxa ||
- (walign <= halign && walign < wmaxa)) {
- *w = clamp_align(*w, wmin, wmax, walign + 1);
- walign = __ffs(*w);
- } else {
- *h = clamp_align(*h, hmin, hmax, halign + 1);
- halign = __ffs(*h);
- }
- } while (halign + walign < salign);
- }
-}
-EXPORT_SYMBOL_GPL(v4l_bound_align_image);
-
-/**
- * v4l_fill_dv_preset_info - fill description of a digital video preset
- * @preset - preset value
- * @info - pointer to struct v4l2_dv_enum_preset
- *
- * drivers can use this helper function to fill description of dv preset
- * in info.
- */
-int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info)
-{
- static const struct v4l2_dv_preset_info {
- u16 width;
- u16 height;
- const char *name;
- } dv_presets[] = {
- { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */
- { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */
- { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */
- { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */
- { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */
- { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */
- { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */
- { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */
- { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */
- { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */
- { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */
- { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */
- { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */
- { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */
- { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */
- { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */
- { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */
- { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */
- { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */
- };
-
- if (info == NULL || preset >= ARRAY_SIZE(dv_presets))
- return -EINVAL;
-
- info->preset = preset;
- info->width = dv_presets[preset].width;
- info->height = dv_presets[preset].height;
- strlcpy(info->name, dv_presets[preset].name, sizeof(info->name));
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info);
-
-const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
- const struct v4l2_discrete_probe *probe,
- s32 width, s32 height)
-{
- int i;
- u32 error, min_error = UINT_MAX;
- const struct v4l2_frmsize_discrete *size, *best = NULL;
-
- if (!probe)
- return best;
-
- for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) {
- error = abs(size->width - width) + abs(size->height - height);
- if (error < min_error) {
- min_error = error;
- best = size;
- }
- if (!error)
- break;
- }
-
- return best;
-}
-EXPORT_SYMBOL_GPL(v4l2_find_nearest_format);
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
deleted file mode 100644
index 9ebd5c540d10..000000000000
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ /dev/null
@@ -1,1045 +0,0 @@
-/*
- * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
- * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de>
- *
- * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com)
- * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs
- * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz)
- * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be)
- * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * These routines maintain argument size conversion between 32bit and 64bit
- * ioctls.
- */
-
-#include <linux/compat.h>
-#include <linux/module.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-ioctl.h>
-
-static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- long ret = -ENOIOCTLCMD;
-
- if (file->f_op->unlocked_ioctl)
- ret = file->f_op->unlocked_ioctl(file, cmd, arg);
-
- return ret;
-}
-
-
-struct v4l2_clip32 {
- struct v4l2_rect c;
- compat_caddr_t next;
-};
-
-struct v4l2_window32 {
- struct v4l2_rect w;
- __u32 field; /* enum v4l2_field */
- __u32 chromakey;
- compat_caddr_t clips; /* actually struct v4l2_clip32 * */
- __u32 clipcount;
- compat_caddr_t bitmap;
-};
-
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
- copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
- get_user(kp->field, &up->field) ||
- get_user(kp->chromakey, &up->chromakey) ||
- get_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
- if (kp->clipcount > 2048)
- return -EINVAL;
- if (kp->clipcount) {
- struct v4l2_clip32 __user *uclips;
- struct v4l2_clip __user *kclips;
- int n = kp->clipcount;
- compat_caddr_t p;
-
- if (get_user(p, &up->clips))
- return -EFAULT;
- uclips = compat_ptr(p);
- kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
- kp->clips = kclips;
- while (--n >= 0) {
- if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
- return -EFAULT;
- if (put_user(n ? kclips + 1 : NULL, &kclips->next))
- return -EFAULT;
- uclips += 1;
- kclips += 1;
- }
- } else
- kp->clips = NULL;
- return 0;
-}
-
-static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
-{
- if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->chromakey, &up->chromakey) ||
- put_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
- return -EFAULT;
- 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)))
- return -EFAULT;
- 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)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
- return -EFAULT;
- return 0;
-}
-
-static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_format32 {
- __u32 type; /* enum v4l2_buf_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;
- __u8 raw_data[200]; /* user-defined */
- } fmt;
-};
-
-/**
- * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument
- * @index: on return, index of the first created buffer
- * @count: entry: number of requested buffers,
- * return: number of created buffers
- * @memory: buffer memory type
- * @format: frame format, for which buffers are requested
- * @reserved: future extensions
- */
-struct v4l2_create_buffers32 {
- __u32 index;
- __u32 count;
- __u32 memory; /* enum v4l2_memory */
- struct v4l2_format32 format;
- __u32 reserved[8];
-};
-
-static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- switch (kp->type) {
- 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);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
- case V4L2_BUF_TYPE_PRIVATE:
- if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data)))
- return -EFAULT;
- return 0;
- default:
- printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
- kp->type);
- return -EINVAL;
- }
-}
-
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) ||
- get_user(kp->type, &up->type))
- return -EFAULT;
- return __get_v4l2_format32(kp, up);
-}
-
-static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
-{
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt)))
- return -EFAULT;
- return __get_v4l2_format32(&kp->format, &up->format);
-}
-
-static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- switch (kp->type) {
- 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);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
- case V4L2_BUF_TYPE_PRIVATE:
- if (copy_to_user(up, kp, sizeof(up->fmt.raw_data)))
- return -EFAULT;
- return 0;
- default:
- printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
- kp->type);
- return -EINVAL;
- }
-}
-
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
-{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
- put_user(kp->type, &up->type))
- return -EFAULT;
- return __put_v4l2_format32(kp, up);
-}
-
-static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
-{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
- return -EFAULT;
- return __put_v4l2_format32(&kp->format, &up->format);
-}
-
-struct v4l2_standard32 {
- __u32 index;
- __u32 id[2]; /* __u64 would get the alignment wrong */
- __u8 name[24];
- struct v4l2_fract frameperiod; /* Frames, not fields */
- __u32 framelines;
- __u32 reserved[4];
-};
-
-static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
- /* other fields are not set by the user, nor used by the driver */
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
- get_user(kp->index, &up->index))
- return -EFAULT;
- return 0;
-}
-
-static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
-{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
- put_user(kp->index, &up->index) ||
- copy_to_user(up->id, &kp->id, sizeof(__u64)) ||
- copy_to_user(up->name, kp->name, 24) ||
- copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
- put_user(kp->framelines, &up->framelines) ||
- copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
- return -EFAULT;
- 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;
- __u32 type; /* enum v4l2_buf_type */
- __u32 bytesused;
- __u32 flags;
- __u32 field; /* enum v4l2_field */
- struct compat_timeval timestamp;
- struct v4l2_timecode timecode;
- __u32 sequence;
-
- /* memory location */
- __u32 memory; /* enum v4l2_memory */
- union {
- __u32 offset;
- compat_long_t userptr;
- compat_caddr_t planes;
- } m;
- __u32 length;
- __u32 reserved2;
- __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) ||
- get_user(kp->type, &up->type) ||
- get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory))
- return -EFAULT;
-
- 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;
-
- if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
- if (get_user(kp->length, &up->length))
- return -EFAULT;
-
- 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;
- }
-
- if (get_user(p, &up->m.planes))
- return -EFAULT;
-
- 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) ||
- put_user(kp->flags, &up->flags) ||
- put_user(kp->memory, &up->memory))
- return -EFAULT;
-
- if (put_user(kp->bytesused, &up->bytesused) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
- put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
- copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
- put_user(kp->sequence, &up->sequence) ||
- put_user(kp->reserved2, &up->reserved2) ||
- 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;
-}
-
-struct v4l2_framebuffer32 {
- __u32 capability;
- __u32 flags;
- compat_caddr_t base;
- struct v4l2_pix_format fmt;
-};
-
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
- u32 tmp;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
- get_user(tmp, &up->base) ||
- get_user(kp->capability, &up->capability) ||
- get_user(kp->flags, &up->flags))
- return -EFAULT;
- kp->base = compat_ptr(tmp);
- get_v4l2_pix_format(&kp->fmt, &up->fmt);
- return 0;
-}
-
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
-{
- u32 tmp = (u32)((unsigned long)kp->base);
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
- put_user(tmp, &up->base) ||
- put_user(kp->capability, &up->capability) ||
- put_user(kp->flags, &up->flags))
- return -EFAULT;
- put_v4l2_pix_format(&kp->fmt, &up->fmt);
- return 0;
-}
-
-struct v4l2_input32 {
- __u32 index; /* Which input */
- __u8 name[32]; /* Label */
- __u32 type; /* Type of input */
- __u32 audioset; /* Associated audios (bitfield) */
- __u32 tuner; /* Associated tuner */
- v4l2_std_id std;
- __u32 status;
- __u32 reserved[4];
-} __attribute__ ((packed));
-
-/* The 64-bit v4l2_input struct has extra padding at the end of the struct.
- Otherwise it is identical to the 32-bit version. */
-static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
-{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
- return -EFAULT;
- return 0;
-}
-
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
-{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
- return -EFAULT;
- return 0;
-}
-
-struct v4l2_ext_controls32 {
- __u32 ctrl_class;
- __u32 count;
- __u32 error_idx;
- __u32 reserved[2];
- compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */
-};
-
-struct v4l2_ext_control32 {
- __u32 id;
- __u32 size;
- __u32 reserved2[1];
- union {
- __s32 value;
- __s64 value64;
- compat_caddr_t string; /* actually char * */
- };
-} __attribute__ ((packed));
-
-/* The following function really belong in v4l2-common, but that causes
- a circular dependency between modules. We need to think about this, but
- for now this will do. */
-
-/* Return non-zero if this control is a pointer type. Currently only
- type STRING is a pointer type. */
-static inline int ctrl_is_pointer(u32 id)
-{
- switch (id) {
- case V4L2_CID_RDS_TX_PS_NAME:
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- return 1;
- default:
- return 0;
- }
-}
-
-static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
-{
- struct v4l2_ext_control32 __user *ucontrols;
- struct v4l2_ext_control __user *kcontrols;
- int n;
- compat_caddr_t p;
-
- if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
- get_user(kp->ctrl_class, &up->ctrl_class) ||
- get_user(kp->count, &up->count) ||
- get_user(kp->error_idx, &up->error_idx) ||
- copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
- return -EFAULT;
- n = kp->count;
- if (n == 0) {
- kp->controls = NULL;
- return 0;
- }
- if (get_user(p, &up->controls))
- return -EFAULT;
- ucontrols = compat_ptr(p);
- 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(*ucontrols)))
- return -EFAULT;
- if (ctrl_is_pointer(kcontrols->id)) {
- void __user *s;
-
- if (get_user(p, &ucontrols->string))
- return -EFAULT;
- s = compat_ptr(p);
- if (put_user(s, &kcontrols->string))
- return -EFAULT;
- }
- ucontrols++;
- kcontrols++;
- }
- return 0;
-}
-
-static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
-{
- struct v4l2_ext_control32 __user *ucontrols;
- struct v4l2_ext_control __user *kcontrols = kp->controls;
- int n = kp->count;
- compat_caddr_t p;
-
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
- put_user(kp->ctrl_class, &up->ctrl_class) ||
- put_user(kp->count, &up->count) ||
- put_user(kp->error_idx, &up->error_idx) ||
- copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
- return -EFAULT;
- if (!kp->count)
- return 0;
-
- if (get_user(p, &up->controls))
- return -EFAULT;
- ucontrols = compat_ptr(p);
- if (!access_ok(VERIFY_WRITE, ucontrols,
- n * sizeof(struct v4l2_ext_control32)))
- return -EFAULT;
-
- while (--n >= 0) {
- unsigned size = sizeof(*ucontrols);
-
- /* Do not modify the pointer when copying a pointer control.
- The contents of the pointer was changed, not the pointer
- itself. */
- if (ctrl_is_pointer(kcontrols->id))
- size -= sizeof(ucontrols->value64);
- if (copy_in_user(ucontrols, kcontrols, size))
- return -EFAULT;
- ucontrols++;
- kcontrols++;
- }
- return 0;
-}
-
-struct v4l2_event32 {
- __u32 type;
- union {
- __u8 data[64];
- } u;
- __u32 pending;
- __u32 sequence;
- struct compat_timespec timestamp;
- __u32 id;
- __u32 reserved[8];
-};
-
-static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
-{
- if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
- put_user(kp->type, &up->type) ||
- copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
- put_user(kp->pending, &up->pending) ||
- put_user(kp->sequence, &up->sequence) ||
- put_compat_timespec(&kp->timestamp, &up->timestamp) ||
- put_user(kp->id, &up->id) ||
- copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
- return -EFAULT;
- return 0;
-}
-
-#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32)
-#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32)
-#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32)
-#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32)
-#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32)
-#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32)
-#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32)
-#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32)
-#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32)
-#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32)
-#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32)
-#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32)
-#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32)
-#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32)
-#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32)
-#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32)
-
-#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32)
-#define VIDIOC_STREAMON32 _IOW ('V', 18, s32)
-#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32)
-#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32)
-#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32)
-#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
-#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
-
-static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- union {
- struct v4l2_format v2f;
- struct v4l2_buffer v2b;
- struct v4l2_framebuffer v2fb;
- struct v4l2_input v2i;
- struct v4l2_standard v2s;
- struct v4l2_ext_controls v2ecs;
- struct v4l2_event v2ev;
- struct v4l2_create_buffers v2crt;
- unsigned long vx;
- int vi;
- } karg;
- void __user *up = compat_ptr(arg);
- int compatible_arg = 1;
- long err = 0;
-
- /* First, convert the command. */
- switch (cmd) {
- case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
- case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
- case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
- case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
- case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
- case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
- case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
- case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break;
- case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break;
- case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
- case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break;
- case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break;
- case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break;
- case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break;
- case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
- case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
- case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
- case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
- case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
- case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break;
- case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break;
- case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break;
- case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break;
- }
-
- switch (cmd) {
- case VIDIOC_OVERLAY:
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_OUTPUT:
- err = get_user(karg.vi, (s32 __user *)up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_INPUT:
- case VIDIOC_G_OUTPUT:
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT:
- err = get_v4l2_format32(&karg.v2f, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_CREATE_BUFS:
- err = get_v4l2_create32(&karg.v2crt, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_PREPARE_BUF:
- case VIDIOC_QUERYBUF:
- case VIDIOC_QBUF:
- case VIDIOC_DQBUF:
- err = get_v4l2_buffer32(&karg.v2b, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_S_FBUF:
- err = get_v4l2_framebuffer32(&karg.v2fb, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_FBUF:
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMSTD:
- err = get_v4l2_standard32(&karg.v2s, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_ENUMINPUT:
- err = get_v4l2_input32(&karg.v2i, up);
- compatible_arg = 0;
- break;
-
- case VIDIOC_G_EXT_CTRLS:
- case VIDIOC_S_EXT_CTRLS:
- case VIDIOC_TRY_EXT_CTRLS:
- err = get_v4l2_ext_controls32(&karg.v2ecs, up);
- compatible_arg = 0;
- break;
- case VIDIOC_DQEVENT:
- compatible_arg = 0;
- break;
- }
- if (err)
- return err;
-
- if (compatible_arg)
- err = native_ioctl(file, cmd, (unsigned long)up);
- else {
- mm_segment_t old_fs = get_fs();
-
- set_fs(KERNEL_DS);
- err = native_ioctl(file, cmd, (unsigned long)&karg);
- set_fs(old_fs);
- }
-
- /* Special case: even after an error we need to put the
- results back for these ioctls since the error_idx will
- contain information on which control failed. */
- switch (cmd) {
- case VIDIOC_G_EXT_CTRLS:
- case VIDIOC_S_EXT_CTRLS:
- case VIDIOC_TRY_EXT_CTRLS:
- if (put_v4l2_ext_controls32(&karg.v2ecs, up))
- err = -EFAULT;
- break;
- }
- if (err)
- return err;
-
- switch (cmd) {
- case VIDIOC_S_INPUT:
- case VIDIOC_S_OUTPUT:
- case VIDIOC_G_INPUT:
- case VIDIOC_G_OUTPUT:
- err = put_user(((s32)karg.vi), (s32 __user *)up);
- break;
-
- case VIDIOC_G_FBUF:
- err = put_v4l2_framebuffer32(&karg.v2fb, up);
- break;
-
- case VIDIOC_DQEVENT:
- err = put_v4l2_event32(&karg.v2ev, up);
- break;
-
- case VIDIOC_G_FMT:
- case VIDIOC_S_FMT:
- case VIDIOC_TRY_FMT:
- err = put_v4l2_format32(&karg.v2f, up);
- break;
-
- case VIDIOC_CREATE_BUFS:
- err = put_v4l2_create32(&karg.v2crt, up);
- break;
-
- case VIDIOC_QUERYBUF:
- case VIDIOC_QBUF:
- case VIDIOC_DQBUF:
- err = put_v4l2_buffer32(&karg.v2b, up);
- break;
-
- case VIDIOC_ENUMSTD:
- err = put_v4l2_standard32(&karg.v2s, up);
- break;
-
- case VIDIOC_ENUMINPUT:
- err = put_v4l2_input32(&karg.v2i, up);
- break;
- }
- return err;
-}
-
-long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct video_device *vdev = video_devdata(file);
- long ret = -ENOIOCTLCMD;
-
- if (!file->f_op->unlocked_ioctl)
- return ret;
-
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- case VIDIOC_RESERVED:
- case VIDIOC_ENUM_FMT:
- case VIDIOC_G_FMT32:
- case VIDIOC_S_FMT32:
- case VIDIOC_REQBUFS:
- case VIDIOC_QUERYBUF32:
- case VIDIOC_G_FBUF32:
- case VIDIOC_S_FBUF32:
- case VIDIOC_OVERLAY32:
- case VIDIOC_QBUF32:
- case VIDIOC_DQBUF32:
- case VIDIOC_STREAMON32:
- case VIDIOC_STREAMOFF32:
- case VIDIOC_G_PARM:
- case VIDIOC_S_PARM:
- case VIDIOC_G_STD:
- case VIDIOC_S_STD:
- case VIDIOC_ENUMSTD32:
- case VIDIOC_ENUMINPUT32:
- case VIDIOC_G_CTRL:
- case VIDIOC_S_CTRL:
- case VIDIOC_G_TUNER:
- case VIDIOC_S_TUNER:
- case VIDIOC_G_AUDIO:
- case VIDIOC_S_AUDIO:
- case VIDIOC_QUERYCTRL:
- case VIDIOC_QUERYMENU:
- case VIDIOC_G_INPUT32:
- case VIDIOC_S_INPUT32:
- case VIDIOC_G_OUTPUT32:
- case VIDIOC_S_OUTPUT32:
- case VIDIOC_ENUMOUTPUT:
- case VIDIOC_G_AUDOUT:
- case VIDIOC_S_AUDOUT:
- case VIDIOC_G_MODULATOR:
- case VIDIOC_S_MODULATOR:
- case VIDIOC_S_FREQUENCY:
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_CROPCAP:
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP:
- case VIDIOC_G_SELECTION:
- case VIDIOC_S_SELECTION:
- case VIDIOC_G_JPEGCOMP:
- case VIDIOC_S_JPEGCOMP:
- case VIDIOC_QUERYSTD:
- case VIDIOC_TRY_FMT32:
- case VIDIOC_ENUMAUDIO:
- case VIDIOC_ENUMAUDOUT:
- case VIDIOC_G_PRIORITY:
- case VIDIOC_S_PRIORITY:
- case VIDIOC_G_SLICED_VBI_CAP:
- case VIDIOC_LOG_STATUS:
- case VIDIOC_G_EXT_CTRLS32:
- case VIDIOC_S_EXT_CTRLS32:
- case VIDIOC_TRY_EXT_CTRLS32:
- case VIDIOC_ENUM_FRAMESIZES:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- case VIDIOC_G_ENC_INDEX:
- case VIDIOC_ENCODER_CMD:
- case VIDIOC_TRY_ENCODER_CMD:
- case VIDIOC_DECODER_CMD:
- case VIDIOC_TRY_DECODER_CMD:
- case VIDIOC_DBG_S_REGISTER:
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_G_CHIP_IDENT:
- case VIDIOC_S_HW_FREQ_SEEK:
- case VIDIOC_ENUM_DV_PRESETS:
- case VIDIOC_S_DV_PRESET:
- case VIDIOC_G_DV_PRESET:
- case VIDIOC_QUERY_DV_PRESET:
- case VIDIOC_S_DV_TIMINGS:
- case VIDIOC_G_DV_TIMINGS:
- case VIDIOC_DQEVENT:
- case VIDIOC_DQEVENT32:
- case VIDIOC_SUBSCRIBE_EVENT:
- case VIDIOC_UNSUBSCRIBE_EVENT:
- case VIDIOC_CREATE_BUFS32:
- case VIDIOC_PREPARE_BUF32:
- case VIDIOC_ENUM_DV_TIMINGS:
- case VIDIOC_QUERY_DV_TIMINGS:
- case VIDIOC_DV_TIMINGS_CAP:
- case VIDIOC_ENUM_FREQ_BANDS:
- ret = do_video_ioctl(file, cmd, arg);
- break;
-
- default:
- if (vdev->fops->compat_ioctl32)
- ret = vdev->fops->compat_ioctl32(file, cmd, arg);
-
- if (ret == -ENOIOCTLCMD)
- printk(KERN_WARNING "compat_ioctl32: "
- "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
- _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
- cmd);
- break;
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
deleted file mode 100644
index b6a2ee71e5c3..000000000000
--- a/drivers/media/video/v4l2-ctrls.c
+++ /dev/null
@@ -1,2651 +0,0 @@
-/*
- V4L2 controls framework implementation.
-
- Copyright (C) 2010 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/ctype.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-dev.h>
-
-#define has_op(master, op) \
- (master->ops && master->ops->op)
-#define call_op(master, op) \
- (has_op(master, op) ? master->ops->op(master) : 0)
-
-/* Internal temporary helper struct, one for each v4l2_ext_control */
-struct v4l2_ctrl_helper {
- /* Pointer to the control reference of the master control */
- struct v4l2_ctrl_ref *mref;
- /* The control corresponding to the v4l2_ext_control ID field. */
- struct v4l2_ctrl *ctrl;
- /* v4l2_ext_control index of the next control belonging to the
- same cluster, or 0 if there isn't any. */
- u32 next;
-};
-
-/* Small helper function to determine if the autocluster is set to manual
- mode. */
-static bool is_cur_manual(const struct v4l2_ctrl *master)
-{
- return master->is_auto && master->cur.val == master->manual_mode_value;
-}
-
-/* Same as above, but this checks the against the new value instead of the
- current value. */
-static bool is_new_manual(const struct v4l2_ctrl *master)
-{
- return master->is_auto && master->val == master->manual_mode_value;
-}
-
-/* Returns NULL or a character pointer array containing the menu for
- the given control ID. The pointer array ends with a NULL pointer.
- An empty string signifies a menu entry that is invalid. This allows
- drivers to disable certain options if it is not supported. */
-const char * const *v4l2_ctrl_get_menu(u32 id)
-{
- static const char * const mpeg_audio_sampling_freq[] = {
- "44.1 kHz",
- "48 kHz",
- "32 kHz",
- NULL
- };
- static const char * const mpeg_audio_encoding[] = {
- "MPEG-1/2 Layer I",
- "MPEG-1/2 Layer II",
- "MPEG-1/2 Layer III",
- "MPEG-2/4 AAC",
- "AC-3",
- NULL
- };
- static const char * const mpeg_audio_l1_bitrate[] = {
- "32 kbps",
- "64 kbps",
- "96 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "288 kbps",
- "320 kbps",
- "352 kbps",
- "384 kbps",
- "416 kbps",
- "448 kbps",
- NULL
- };
- static const char * const mpeg_audio_l2_bitrate[] = {
- "32 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- NULL
- };
- static const char * const mpeg_audio_l3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- NULL
- };
- static const char * const mpeg_audio_ac3_bitrate[] = {
- "32 kbps",
- "40 kbps",
- "48 kbps",
- "56 kbps",
- "64 kbps",
- "80 kbps",
- "96 kbps",
- "112 kbps",
- "128 kbps",
- "160 kbps",
- "192 kbps",
- "224 kbps",
- "256 kbps",
- "320 kbps",
- "384 kbps",
- "448 kbps",
- "512 kbps",
- "576 kbps",
- "640 kbps",
- NULL
- };
- static const char * const mpeg_audio_mode[] = {
- "Stereo",
- "Joint Stereo",
- "Dual",
- "Mono",
- NULL
- };
- static const char * const mpeg_audio_mode_extension[] = {
- "Bound 4",
- "Bound 8",
- "Bound 12",
- "Bound 16",
- NULL
- };
- static const char * const mpeg_audio_emphasis[] = {
- "No Emphasis",
- "50/15 us",
- "CCITT J17",
- NULL
- };
- static const char * const mpeg_audio_crc[] = {
- "No CRC",
- "16-bit CRC",
- NULL
- };
- static const char * const mpeg_audio_dec_playback[] = {
- "Auto",
- "Stereo",
- "Left",
- "Right",
- "Mono",
- "Swapped Stereo",
- NULL
- };
- static const char * const mpeg_video_encoding[] = {
- "MPEG-1",
- "MPEG-2",
- "MPEG-4 AVC",
- NULL
- };
- static const char * const mpeg_video_aspect[] = {
- "1x1",
- "4x3",
- "16x9",
- "2.21x1",
- NULL
- };
- static const char * const mpeg_video_bitrate_mode[] = {
- "Variable Bitrate",
- "Constant Bitrate",
- NULL
- };
- static const char * const mpeg_stream_type[] = {
- "MPEG-2 Program Stream",
- "MPEG-2 Transport Stream",
- "MPEG-1 System Stream",
- "MPEG-2 DVD-compatible Stream",
- "MPEG-1 VCD-compatible Stream",
- "MPEG-2 SVCD-compatible Stream",
- NULL
- };
- static const char * const mpeg_stream_vbi_fmt[] = {
- "No VBI",
- "Private Packet, IVTV Format",
- NULL
- };
- static const char * const camera_power_line_frequency[] = {
- "Disabled",
- "50 Hz",
- "60 Hz",
- "Auto",
- NULL
- };
- static const char * const camera_exposure_auto[] = {
- "Auto Mode",
- "Manual Mode",
- "Shutter Priority Mode",
- "Aperture Priority Mode",
- NULL
- };
- static const char * const camera_exposure_metering[] = {
- "Average",
- "Center Weighted",
- "Spot",
- NULL
- };
- static const char * const camera_auto_focus_range[] = {
- "Auto",
- "Normal",
- "Macro",
- "Infinity",
- NULL
- };
- static const char * const colorfx[] = {
- "None",
- "Black & White",
- "Sepia",
- "Negative",
- "Emboss",
- "Sketch",
- "Sky Blue",
- "Grass Green",
- "Skin Whiten",
- "Vivid",
- "Aqua",
- "Art Freeze",
- "Silhouette",
- "Solarization",
- "Antique",
- "Set Cb/Cr",
- NULL
- };
- static const char * const auto_n_preset_white_balance[] = {
- "Manual",
- "Auto",
- "Incandescent",
- "Fluorescent",
- "Fluorescent H",
- "Horizon",
- "Daylight",
- "Flash",
- "Cloudy",
- "Shade",
- NULL,
- };
- static const char * const camera_iso_sensitivity_auto[] = {
- "Manual",
- "Auto",
- NULL
- };
- static const char * const scene_mode[] = {
- "None",
- "Backlight",
- "Beach/Snow",
- "Candle Light",
- "Dusk/Dawn",
- "Fall Colors",
- "Fireworks",
- "Landscape",
- "Night",
- "Party/Indoor",
- "Portrait",
- "Sports",
- "Sunset",
- "Text",
- NULL
- };
- static const char * const tune_preemphasis[] = {
- "No Preemphasis",
- "50 Microseconds",
- "75 Microseconds",
- NULL,
- };
- static const char * const header_mode[] = {
- "Separate Buffer",
- "Joined With 1st Frame",
- NULL,
- };
- static const char * const multi_slice[] = {
- "Single",
- "Max Macroblocks",
- "Max Bytes",
- NULL,
- };
- static const char * const entropy_mode[] = {
- "CAVLC",
- "CABAC",
- NULL,
- };
- static const char * const mpeg_h264_level[] = {
- "1",
- "1b",
- "1.1",
- "1.2",
- "1.3",
- "2",
- "2.1",
- "2.2",
- "3",
- "3.1",
- "3.2",
- "4",
- "4.1",
- "4.2",
- "5",
- "5.1",
- NULL,
- };
- static const char * const h264_loop_filter[] = {
- "Enabled",
- "Disabled",
- "Disabled at Slice Boundary",
- NULL,
- };
- static const char * const h264_profile[] = {
- "Baseline",
- "Constrained Baseline",
- "Main",
- "Extended",
- "High",
- "High 10",
- "High 422",
- "High 444 Predictive",
- "High 10 Intra",
- "High 422 Intra",
- "High 444 Intra",
- "CAVLC 444 Intra",
- "Scalable Baseline",
- "Scalable High",
- "Scalable High Intra",
- "Multiview High",
- NULL,
- };
- static const char * const vui_sar_idc[] = {
- "Unspecified",
- "1:1",
- "12:11",
- "10:11",
- "16:11",
- "40:33",
- "24:11",
- "20:11",
- "32:11",
- "80:33",
- "18:11",
- "15:11",
- "64:33",
- "160:99",
- "4:3",
- "3:2",
- "2:1",
- "Extended SAR",
- NULL,
- };
- static const char * const mpeg_mpeg4_level[] = {
- "0",
- "0b",
- "1",
- "2",
- "3",
- "3b",
- "4",
- "5",
- NULL,
- };
- static const char * const mpeg4_profile[] = {
- "Simple",
- "Advanced Simple",
- "Core",
- "Simple Scalable",
- "Advanced Coding Efficency",
- NULL,
- };
-
- static const char * const flash_led_mode[] = {
- "Off",
- "Flash",
- "Torch",
- NULL,
- };
- static const char * const flash_strobe_source[] = {
- "Software",
- "External",
- NULL,
- };
-
- static const char * const jpeg_chroma_subsampling[] = {
- "4:4:4",
- "4:2:2",
- "4:2:0",
- "4:1:1",
- "4:1:0",
- "Gray",
- NULL,
- };
-
- switch (id) {
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- return mpeg_audio_sampling_freq;
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- return mpeg_audio_encoding;
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- return mpeg_audio_l1_bitrate;
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- return mpeg_audio_l2_bitrate;
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- return mpeg_audio_l3_bitrate;
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- return mpeg_audio_ac3_bitrate;
- case V4L2_CID_MPEG_AUDIO_MODE:
- return mpeg_audio_mode;
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- return mpeg_audio_mode_extension;
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- return mpeg_audio_emphasis;
- case V4L2_CID_MPEG_AUDIO_CRC:
- return mpeg_audio_crc;
- case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
- case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
- return mpeg_audio_dec_playback;
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- return mpeg_video_encoding;
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- return mpeg_video_aspect;
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- return mpeg_video_bitrate_mode;
- case V4L2_CID_MPEG_STREAM_TYPE:
- return mpeg_stream_type;
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- return mpeg_stream_vbi_fmt;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- return camera_power_line_frequency;
- case V4L2_CID_EXPOSURE_AUTO:
- return camera_exposure_auto;
- case V4L2_CID_EXPOSURE_METERING:
- return camera_exposure_metering;
- case V4L2_CID_AUTO_FOCUS_RANGE:
- return camera_auto_focus_range;
- case V4L2_CID_COLORFX:
- return colorfx;
- case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
- return auto_n_preset_white_balance;
- case V4L2_CID_ISO_SENSITIVITY_AUTO:
- return camera_iso_sensitivity_auto;
- case V4L2_CID_SCENE_MODE:
- return scene_mode;
- case V4L2_CID_TUNE_PREEMPHASIS:
- return tune_preemphasis;
- case V4L2_CID_FLASH_LED_MODE:
- return flash_led_mode;
- case V4L2_CID_FLASH_STROBE_SOURCE:
- return flash_strobe_source;
- case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
- return header_mode;
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
- return multi_slice;
- case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
- return entropy_mode;
- case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- return mpeg_h264_level;
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
- return h264_loop_filter;
- case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- return h264_profile;
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
- return vui_sar_idc;
- case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
- return mpeg_mpeg4_level;
- case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- return mpeg4_profile;
- case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
- return jpeg_chroma_subsampling;
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_menu);
-
-/* Return the control name. */
-const char *v4l2_ctrl_get_name(u32 id)
-{
- switch (id) {
- /* USER controls */
- /* Keep the order of the 'case's the same as in videodev2.h! */
- case V4L2_CID_USER_CLASS: return "User Controls";
- case V4L2_CID_BRIGHTNESS: return "Brightness";
- case V4L2_CID_CONTRAST: return "Contrast";
- case V4L2_CID_SATURATION: return "Saturation";
- case V4L2_CID_HUE: return "Hue";
- case V4L2_CID_AUDIO_VOLUME: return "Volume";
- case V4L2_CID_AUDIO_BALANCE: return "Balance";
- case V4L2_CID_AUDIO_BASS: return "Bass";
- case V4L2_CID_AUDIO_TREBLE: return "Treble";
- case V4L2_CID_AUDIO_MUTE: return "Mute";
- case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
- case V4L2_CID_BLACK_LEVEL: return "Black Level";
- case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
- case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
- case V4L2_CID_RED_BALANCE: return "Red Balance";
- case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
- case V4L2_CID_GAMMA: return "Gamma";
- case V4L2_CID_EXPOSURE: return "Exposure";
- case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
- case V4L2_CID_GAIN: return "Gain";
- case V4L2_CID_HFLIP: return "Horizontal Flip";
- case V4L2_CID_VFLIP: return "Vertical Flip";
- case V4L2_CID_HCENTER: return "Horizontal Center";
- case V4L2_CID_VCENTER: return "Vertical Center";
- case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
- case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
- case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
- case V4L2_CID_SHARPNESS: return "Sharpness";
- case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
- case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
- case V4L2_CID_COLOR_KILLER: return "Color Killer";
- case V4L2_CID_COLORFX: return "Color Effects";
- case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic";
- case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter";
- case V4L2_CID_ROTATE: return "Rotate";
- case V4L2_CID_BG_COLOR: return "Background Color";
- case V4L2_CID_CHROMA_GAIN: return "Chroma Gain";
- case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1";
- case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2";
- case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers";
- case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers";
- case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component";
- case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr";
-
- /* MPEG controls */
- /* Keep the order of the 'case's the same as in videodev2.h! */
- case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
- case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type";
- case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID";
- case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID";
- case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID";
- case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID";
- case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
- case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency";
- case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding";
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate";
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate";
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate";
- case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode";
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension";
- case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis";
- case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC";
- case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
- case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
- case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback";
- case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
- case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
- case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
- case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
- case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size";
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure";
- case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown";
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode";
- case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate";
- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate";
- case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation";
- case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute";
- case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV";
- case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface";
- case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable";
- case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs";
- case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable";
- case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control";
- case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode";
- case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics";
- case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value";
- case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value";
- case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable";
- case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size";
- case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entropy Mode";
- case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I-Frame Period";
- case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level";
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset";
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset";
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: return "H264 Loop Filter Mode";
- case V4L2_CID_MPEG_VIDEO_H264_PROFILE: return "H264 Profile";
- case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: return "Vertical Size of SAR";
- case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: return "Horizontal Size of SAR";
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable";
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC";
- case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value";
- case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value";
- case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value";
- case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level";
- case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile";
- case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable";
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "Maximum Bytes in a Slice";
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice";
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method";
- case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
- case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
- case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
-
- /* CAMERA controls */
- /* Keep the order of the 'case's the same as in videodev2.h! */
- case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
- case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
- case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
- case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
- case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
- case V4L2_CID_PAN_RESET: return "Pan, Reset";
- case V4L2_CID_TILT_RESET: return "Tilt, Reset";
- case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
- case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
- case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
- case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
- case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous";
- case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
- case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
- case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
- case V4L2_CID_PRIVACY: return "Privacy";
- case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
- case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
- case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias";
- case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset";
- case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range";
- case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization";
- case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity";
- case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto";
- case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode";
- case V4L2_CID_SCENE_MODE: return "Scene Mode";
- case V4L2_CID_3A_LOCK: return "3A Lock";
- case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start";
- case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop";
- case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status";
- case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range";
-
- /* FM Radio Modulator control */
- /* Keep the order of the 'case's the same as in videodev2.h! */
- case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls";
- case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation";
- case V4L2_CID_RDS_TX_PI: return "RDS Program ID";
- case V4L2_CID_RDS_TX_PTY: return "RDS Program Type";
- case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name";
- case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text";
- case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled";
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time";
- case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation";
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled";
- case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain";
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold";
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time";
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time";
- case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled";
- case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation";
- case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency";
- case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-Emphasis";
- case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level";
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor";
-
- /* Flash controls */
- case V4L2_CID_FLASH_CLASS: return "Flash Controls";
- case V4L2_CID_FLASH_LED_MODE: return "LED Mode";
- case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source";
- case V4L2_CID_FLASH_STROBE: return "Strobe";
- case V4L2_CID_FLASH_STROBE_STOP: return "Stop Strobe";
- case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe Status";
- case V4L2_CID_FLASH_TIMEOUT: return "Strobe Timeout";
- case V4L2_CID_FLASH_INTENSITY: return "Intensity, Flash Mode";
- case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, Torch Mode";
- case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator";
- case V4L2_CID_FLASH_FAULT: return "Faults";
- case V4L2_CID_FLASH_CHARGE: return "Charge";
- case V4L2_CID_FLASH_READY: return "Ready to Strobe";
-
- /* JPEG encoder controls */
- /* Keep the order of the 'case's the same as in videodev2.h! */
- case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls";
- case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling";
- case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval";
- case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
- case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
-
- /* Image source controls */
- case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls";
- case V4L2_CID_VBLANK: return "Vertical Blanking";
- case V4L2_CID_HBLANK: return "Horizontal Blanking";
- case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain";
-
- /* Image processing controls */
- case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls";
- case V4L2_CID_LINK_FREQ: return "Link Frequency";
- case V4L2_CID_PIXEL_RATE: return "Pixel Rate";
-
- default:
- return NULL;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_get_name);
-
-void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
- s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags)
-{
- *name = v4l2_ctrl_get_name(id);
- *flags = 0;
-
- switch (id) {
- case V4L2_CID_AUDIO_MUTE:
- case V4L2_CID_AUDIO_LOUDNESS:
- case V4L2_CID_AUTO_WHITE_BALANCE:
- case V4L2_CID_AUTOGAIN:
- case V4L2_CID_HFLIP:
- case V4L2_CID_VFLIP:
- case V4L2_CID_HUE_AUTO:
- case V4L2_CID_CHROMA_AGC:
- case V4L2_CID_COLOR_KILLER:
- case V4L2_CID_AUTOBRIGHTNESS:
- case V4L2_CID_MPEG_AUDIO_MUTE:
- case V4L2_CID_MPEG_VIDEO_MUTE:
- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
- case V4L2_CID_MPEG_VIDEO_PULLDOWN:
- case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
- case V4L2_CID_FOCUS_AUTO:
- case V4L2_CID_PRIVACY:
- case V4L2_CID_AUDIO_LIMITER_ENABLED:
- case V4L2_CID_AUDIO_COMPRESSION_ENABLED:
- case V4L2_CID_PILOT_TONE_ENABLED:
- case V4L2_CID_ILLUMINATORS_1:
- case V4L2_CID_ILLUMINATORS_2:
- case V4L2_CID_FLASH_STROBE_STATUS:
- case V4L2_CID_FLASH_CHARGE:
- case V4L2_CID_FLASH_READY:
- case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
- case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE:
- case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
- case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE:
- case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
- case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
- case V4L2_CID_WIDE_DYNAMIC_RANGE:
- case V4L2_CID_IMAGE_STABILIZATION:
- *type = V4L2_CTRL_TYPE_BOOLEAN;
- *min = 0;
- *max = *step = 1;
- break;
- case V4L2_CID_PAN_RESET:
- case V4L2_CID_TILT_RESET:
- case V4L2_CID_FLASH_STROBE:
- case V4L2_CID_FLASH_STROBE_STOP:
- case V4L2_CID_AUTO_FOCUS_START:
- case V4L2_CID_AUTO_FOCUS_STOP:
- *type = V4L2_CTRL_TYPE_BUTTON;
- *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- *min = *max = *step = *def = 0;
- break;
- case V4L2_CID_POWER_LINE_FREQUENCY:
- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
- case V4L2_CID_MPEG_AUDIO_CRC:
- case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
- case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
- case V4L2_CID_MPEG_VIDEO_ENCODING:
- case V4L2_CID_MPEG_VIDEO_ASPECT:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_STREAM_TYPE:
- case V4L2_CID_MPEG_STREAM_VBI_FMT:
- case V4L2_CID_EXPOSURE_AUTO:
- case V4L2_CID_AUTO_FOCUS_RANGE:
- case V4L2_CID_COLORFX:
- case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
- case V4L2_CID_TUNE_PREEMPHASIS:
- case V4L2_CID_FLASH_LED_MODE:
- case V4L2_CID_FLASH_STROBE_SOURCE:
- case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
- case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE:
- case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
- case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
- case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
- case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
- case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
- case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
- case V4L2_CID_ISO_SENSITIVITY_AUTO:
- case V4L2_CID_EXPOSURE_METERING:
- case V4L2_CID_SCENE_MODE:
- *type = V4L2_CTRL_TYPE_MENU;
- break;
- case V4L2_CID_LINK_FREQ:
- *type = V4L2_CTRL_TYPE_INTEGER_MENU;
- break;
- case V4L2_CID_RDS_TX_PS_NAME:
- case V4L2_CID_RDS_TX_RADIO_TEXT:
- *type = V4L2_CTRL_TYPE_STRING;
- break;
- case V4L2_CID_ISO_SENSITIVITY:
- case V4L2_CID_AUTO_EXPOSURE_BIAS:
- *type = V4L2_CTRL_TYPE_INTEGER_MENU;
- break;
- case V4L2_CID_USER_CLASS:
- case V4L2_CID_CAMERA_CLASS:
- case V4L2_CID_MPEG_CLASS:
- case V4L2_CID_FM_TX_CLASS:
- case V4L2_CID_FLASH_CLASS:
- case V4L2_CID_JPEG_CLASS:
- case V4L2_CID_IMAGE_SOURCE_CLASS:
- case V4L2_CID_IMAGE_PROC_CLASS:
- *type = V4L2_CTRL_TYPE_CTRL_CLASS;
- /* You can neither read not write these */
- *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
- *min = *max = *step = *def = 0;
- break;
- case V4L2_CID_BG_COLOR:
- *type = V4L2_CTRL_TYPE_INTEGER;
- *step = 1;
- *min = 0;
- /* Max is calculated as RGB888 that is 2^24 */
- *max = 0xFFFFFF;
- break;
- case V4L2_CID_FLASH_FAULT:
- case V4L2_CID_JPEG_ACTIVE_MARKER:
- case V4L2_CID_3A_LOCK:
- case V4L2_CID_AUTO_FOCUS_STATUS:
- *type = V4L2_CTRL_TYPE_BITMASK;
- break;
- case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
- case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
- *type = V4L2_CTRL_TYPE_INTEGER;
- *flags |= V4L2_CTRL_FLAG_READ_ONLY;
- break;
- case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
- case V4L2_CID_MPEG_VIDEO_DEC_PTS:
- *flags |= V4L2_CTRL_FLAG_VOLATILE;
- /* Fall through */
- case V4L2_CID_PIXEL_RATE:
- *type = V4L2_CTRL_TYPE_INTEGER64;
- *flags |= V4L2_CTRL_FLAG_READ_ONLY;
- *min = *max = *step = *def = 0;
- break;
- default:
- *type = V4L2_CTRL_TYPE_INTEGER;
- break;
- }
- switch (id) {
- case V4L2_CID_MPEG_AUDIO_ENCODING:
- case V4L2_CID_MPEG_AUDIO_MODE:
- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
- case V4L2_CID_MPEG_STREAM_TYPE:
- *flags |= V4L2_CTRL_FLAG_UPDATE;
- break;
- case V4L2_CID_AUDIO_VOLUME:
- case V4L2_CID_AUDIO_BALANCE:
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- case V4L2_CID_BRIGHTNESS:
- case V4L2_CID_CONTRAST:
- case V4L2_CID_SATURATION:
- case V4L2_CID_HUE:
- case V4L2_CID_RED_BALANCE:
- case V4L2_CID_BLUE_BALANCE:
- case V4L2_CID_GAMMA:
- case V4L2_CID_SHARPNESS:
- case V4L2_CID_CHROMA_GAIN:
- case V4L2_CID_RDS_TX_DEVIATION:
- case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME:
- case V4L2_CID_AUDIO_LIMITER_DEVIATION:
- case V4L2_CID_AUDIO_COMPRESSION_GAIN:
- case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD:
- case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME:
- case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME:
- case V4L2_CID_PILOT_TONE_DEVIATION:
- case V4L2_CID_PILOT_TONE_FREQUENCY:
- case V4L2_CID_TUNE_POWER_LEVEL:
- case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
- *flags |= V4L2_CTRL_FLAG_SLIDER;
- break;
- case V4L2_CID_PAN_RELATIVE:
- case V4L2_CID_TILT_RELATIVE:
- case V4L2_CID_FOCUS_RELATIVE:
- case V4L2_CID_IRIS_RELATIVE:
- case V4L2_CID_ZOOM_RELATIVE:
- *flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- break;
- case V4L2_CID_FLASH_STROBE_STATUS:
- case V4L2_CID_AUTO_FOCUS_STATUS:
- case V4L2_CID_FLASH_READY:
- *flags |= V4L2_CTRL_FLAG_READ_ONLY;
- break;
- }
-}
-EXPORT_SYMBOL(v4l2_ctrl_fill);
-
-/* Helper function to determine whether the control type is compatible with
- VIDIOC_G/S_CTRL. */
-static bool type_is_int(const struct v4l2_ctrl *ctrl)
-{
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_INTEGER64:
- case V4L2_CTRL_TYPE_STRING:
- /* Nope, these need v4l2_ext_control */
- return false;
- default:
- return true;
- }
-}
-
-static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes)
-{
- memset(ev->reserved, 0, sizeof(ev->reserved));
- ev->type = V4L2_EVENT_CTRL;
- ev->id = ctrl->id;
- ev->u.ctrl.changes = changes;
- ev->u.ctrl.type = ctrl->type;
- ev->u.ctrl.flags = ctrl->flags;
- if (ctrl->type == V4L2_CTRL_TYPE_STRING)
- ev->u.ctrl.value64 = 0;
- else
- ev->u.ctrl.value64 = ctrl->cur.val64;
- ev->u.ctrl.minimum = ctrl->minimum;
- ev->u.ctrl.maximum = ctrl->maximum;
- if (ctrl->type == V4L2_CTRL_TYPE_MENU
- || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
- ev->u.ctrl.step = 1;
- else
- ev->u.ctrl.step = ctrl->step;
- ev->u.ctrl.default_value = ctrl->default_value;
-}
-
-static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
-{
- struct v4l2_event ev;
- struct v4l2_subscribed_event *sev;
-
- if (list_empty(&ctrl->ev_subs))
- return;
- fill_event(&ev, ctrl, changes);
-
- list_for_each_entry(sev, &ctrl->ev_subs, node)
- if (sev->fh != fh ||
- (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK))
- v4l2_event_queue_fh(sev->fh, &ev);
-}
-
-/* Helper function: copy the current control value back to the caller */
-static int cur_to_user(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl)
-{
- u32 len;
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_STRING:
- len = strlen(ctrl->cur.string);
- if (c->size < len + 1) {
- c->size = len + 1;
- return -ENOSPC;
- }
- return copy_to_user(c->string, ctrl->cur.string,
- len + 1) ? -EFAULT : 0;
- case V4L2_CTRL_TYPE_INTEGER64:
- c->value64 = ctrl->cur.val64;
- break;
- default:
- c->value = ctrl->cur.val;
- break;
- }
- return 0;
-}
-
-/* Helper function: copy the caller-provider value as the new control value */
-static int user_to_new(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl)
-{
- int ret;
- u32 size;
-
- ctrl->is_new = 1;
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_INTEGER64:
- ctrl->val64 = c->value64;
- break;
- case V4L2_CTRL_TYPE_STRING:
- size = c->size;
- if (size == 0)
- return -ERANGE;
- if (size > ctrl->maximum + 1)
- size = ctrl->maximum + 1;
- ret = copy_from_user(ctrl->string, c->string, size);
- if (!ret) {
- char last = ctrl->string[size - 1];
-
- ctrl->string[size - 1] = 0;
- /* If the string was longer than ctrl->maximum,
- then return an error. */
- if (strlen(ctrl->string) == ctrl->maximum && last)
- return -ERANGE;
- }
- return ret ? -EFAULT : 0;
- default:
- ctrl->val = c->value;
- break;
- }
- return 0;
-}
-
-/* Helper function: copy the new control value back to the caller */
-static int new_to_user(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl)
-{
- u32 len;
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_STRING:
- len = strlen(ctrl->string);
- if (c->size < len + 1) {
- c->size = ctrl->maximum + 1;
- return -ENOSPC;
- }
- return copy_to_user(c->string, ctrl->string,
- len + 1) ? -EFAULT : 0;
- case V4L2_CTRL_TYPE_INTEGER64:
- c->value64 = ctrl->val64;
- break;
- default:
- c->value = ctrl->val;
- break;
- }
- return 0;
-}
-
-/* Copy the new value to the current value. */
-static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl,
- bool update_inactive)
-{
- bool changed = false;
-
- if (ctrl == NULL)
- return;
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BUTTON:
- changed = true;
- break;
- case V4L2_CTRL_TYPE_STRING:
- /* strings are always 0-terminated */
- changed = strcmp(ctrl->string, ctrl->cur.string);
- strcpy(ctrl->cur.string, ctrl->string);
- break;
- case V4L2_CTRL_TYPE_INTEGER64:
- changed = ctrl->val64 != ctrl->cur.val64;
- ctrl->cur.val64 = ctrl->val64;
- break;
- default:
- changed = ctrl->val != ctrl->cur.val;
- ctrl->cur.val = ctrl->val;
- break;
- }
- if (update_inactive) {
- /* Note: update_inactive can only be true for auto clusters. */
- ctrl->flags &=
- ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE);
- if (!is_cur_manual(ctrl->cluster[0])) {
- ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
- if (ctrl->cluster[0]->has_volatiles)
- ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
- }
- fh = NULL;
- }
- if (changed || update_inactive) {
- /* If a control was changed that was not one of the controls
- modified by the application, then send the event to all. */
- if (!ctrl->is_new)
- fh = NULL;
- send_event(fh, ctrl,
- (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) |
- (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0));
- }
-}
-
-/* Copy the current value to the new value */
-static void cur_to_new(struct v4l2_ctrl *ctrl)
-{
- if (ctrl == NULL)
- return;
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_STRING:
- /* strings are always 0-terminated */
- strcpy(ctrl->string, ctrl->cur.string);
- break;
- case V4L2_CTRL_TYPE_INTEGER64:
- ctrl->val64 = ctrl->cur.val64;
- break;
- default:
- ctrl->val = ctrl->cur.val;
- break;
- }
-}
-
-/* Return non-zero if one or more of the controls in the cluster has a new
- value that differs from the current value. */
-static int cluster_changed(struct v4l2_ctrl *master)
-{
- int diff = 0;
- int i;
-
- for (i = 0; !diff && i < master->ncontrols; i++) {
- struct v4l2_ctrl *ctrl = master->cluster[i];
-
- if (ctrl == NULL)
- continue;
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BUTTON:
- /* Button controls are always 'different' */
- return 1;
- case V4L2_CTRL_TYPE_STRING:
- /* strings are always 0-terminated */
- diff = strcmp(ctrl->string, ctrl->cur.string);
- break;
- case V4L2_CTRL_TYPE_INTEGER64:
- diff = ctrl->val64 != ctrl->cur.val64;
- break;
- default:
- diff = ctrl->val != ctrl->cur.val;
- break;
- }
- }
- return diff;
-}
-
-/* Validate integer-type control */
-static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
-{
- s32 val = *pval;
- u32 offset;
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_INTEGER:
- /* Round towards the closest legal value */
- val += ctrl->step / 2;
- if (val < ctrl->minimum)
- val = ctrl->minimum;
- if (val > ctrl->maximum)
- val = ctrl->maximum;
- offset = val - ctrl->minimum;
- offset = ctrl->step * (offset / ctrl->step);
- val = ctrl->minimum + offset;
- *pval = val;
- return 0;
-
- case V4L2_CTRL_TYPE_BOOLEAN:
- *pval = !!val;
- return 0;
-
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER_MENU:
- if (val < ctrl->minimum || val > ctrl->maximum)
- return -ERANGE;
- if (ctrl->menu_skip_mask & (1 << val))
- return -EINVAL;
- if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
- ctrl->qmenu[val][0] == '\0')
- return -EINVAL;
- return 0;
-
- case V4L2_CTRL_TYPE_BITMASK:
- *pval &= ctrl->maximum;
- return 0;
-
- case V4L2_CTRL_TYPE_BUTTON:
- case V4L2_CTRL_TYPE_CTRL_CLASS:
- *pval = 0;
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-/* Validate a new control */
-static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c)
-{
- char *s = c->string;
- size_t len;
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_INTEGER:
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER_MENU:
- case V4L2_CTRL_TYPE_BITMASK:
- case V4L2_CTRL_TYPE_BUTTON:
- case V4L2_CTRL_TYPE_CTRL_CLASS:
- return validate_new_int(ctrl, &c->value);
-
- case V4L2_CTRL_TYPE_INTEGER64:
- return 0;
-
- case V4L2_CTRL_TYPE_STRING:
- len = strlen(s);
- if (len < ctrl->minimum)
- return -ERANGE;
- if ((len - ctrl->minimum) % ctrl->step)
- return -ERANGE;
- return 0;
-
- default:
- return -EINVAL;
- }
-}
-
-static inline u32 node2id(struct list_head *node)
-{
- return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id;
-}
-
-/* Set the handler's error code if it wasn't set earlier already */
-static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
-{
- if (hdl->error == 0)
- hdl->error = err;
- return err;
-}
-
-/* Initialize the handler */
-int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
- unsigned nr_of_controls_hint)
-{
- hdl->lock = &hdl->_lock;
- mutex_init(hdl->lock);
- INIT_LIST_HEAD(&hdl->ctrls);
- INIT_LIST_HEAD(&hdl->ctrl_refs);
- hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
- hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]),
- GFP_KERNEL);
- hdl->error = hdl->buckets ? 0 : -ENOMEM;
- return hdl->error;
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_init);
-
-/* Free all controls and control refs */
-void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
-{
- struct v4l2_ctrl_ref *ref, *next_ref;
- struct v4l2_ctrl *ctrl, *next_ctrl;
- struct v4l2_subscribed_event *sev, *next_sev;
-
- if (hdl == NULL || hdl->buckets == NULL)
- return;
-
- mutex_lock(hdl->lock);
- /* Free all nodes */
- list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
- list_del(&ref->node);
- kfree(ref);
- }
- /* Free all controls owned by the handler */
- list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
- list_del(&ctrl->node);
- list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
- list_del(&sev->node);
- kfree(ctrl);
- }
- kfree(hdl->buckets);
- hdl->buckets = NULL;
- hdl->cached = NULL;
- hdl->error = 0;
- mutex_unlock(hdl->lock);
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_free);
-
-/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer
- be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing
- with applications that do not use the NEXT_CTRL flag.
-
- We just find the n-th private user control. It's O(N), but that should not
- be an issue in this particular case. */
-static struct v4l2_ctrl_ref *find_private_ref(
- struct v4l2_ctrl_handler *hdl, u32 id)
-{
- struct v4l2_ctrl_ref *ref;
-
- id -= V4L2_CID_PRIVATE_BASE;
- list_for_each_entry(ref, &hdl->ctrl_refs, node) {
- /* Search for private user controls that are compatible with
- VIDIOC_G/S_CTRL. */
- if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER &&
- V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) {
- if (!type_is_int(ref->ctrl))
- continue;
- if (id == 0)
- return ref;
- id--;
- }
- }
- return NULL;
-}
-
-/* Find a control with the given ID. */
-static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id)
-{
- struct v4l2_ctrl_ref *ref;
- int bucket;
-
- id &= V4L2_CTRL_ID_MASK;
-
- /* Old-style private controls need special handling */
- if (id >= V4L2_CID_PRIVATE_BASE)
- return find_private_ref(hdl, id);
- bucket = id % hdl->nr_of_buckets;
-
- /* Simple optimization: cache the last control found */
- if (hdl->cached && hdl->cached->ctrl->id == id)
- return hdl->cached;
-
- /* Not in cache, search the hash */
- ref = hdl->buckets ? hdl->buckets[bucket] : NULL;
- while (ref && ref->ctrl->id != id)
- ref = ref->next;
-
- if (ref)
- hdl->cached = ref; /* cache it! */
- return ref;
-}
-
-/* Find a control with the given ID. Take the handler's lock first. */
-static struct v4l2_ctrl_ref *find_ref_lock(
- struct v4l2_ctrl_handler *hdl, u32 id)
-{
- struct v4l2_ctrl_ref *ref = NULL;
-
- if (hdl) {
- mutex_lock(hdl->lock);
- ref = find_ref(hdl, id);
- mutex_unlock(hdl->lock);
- }
- return ref;
-}
-
-/* Find a control with the given ID. */
-struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id)
-{
- struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id);
-
- return ref ? ref->ctrl : NULL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_find);
-
-/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */
-static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl *ctrl)
-{
- struct v4l2_ctrl_ref *ref;
- struct v4l2_ctrl_ref *new_ref;
- u32 id = ctrl->id;
- u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1;
- int bucket = id % hdl->nr_of_buckets; /* which bucket to use */
-
- /* Automatically add the control class if it is not yet present. */
- if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL)
- if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0))
- return hdl->error;
-
- if (hdl->error)
- return hdl->error;
-
- new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL);
- if (!new_ref)
- return handler_set_err(hdl, -ENOMEM);
- new_ref->ctrl = ctrl;
- if (ctrl->handler == hdl) {
- /* By default each control starts in a cluster of its own.
- new_ref->ctrl is basically a cluster array with one
- element, so that's perfect to use as the cluster pointer.
- But only do this for the handler that owns the control. */
- ctrl->cluster = &new_ref->ctrl;
- ctrl->ncontrols = 1;
- }
-
- INIT_LIST_HEAD(&new_ref->node);
-
- mutex_lock(hdl->lock);
-
- /* Add immediately at the end of the list if the list is empty, or if
- the last element in the list has a lower ID.
- This ensures that when elements are added in ascending order the
- insertion is an O(1) operation. */
- if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) {
- list_add_tail(&new_ref->node, &hdl->ctrl_refs);
- goto insert_in_hash;
- }
-
- /* Find insert position in sorted list */
- list_for_each_entry(ref, &hdl->ctrl_refs, node) {
- if (ref->ctrl->id < id)
- continue;
- /* Don't add duplicates */
- if (ref->ctrl->id == id) {
- kfree(new_ref);
- goto unlock;
- }
- list_add(&new_ref->node, ref->node.prev);
- break;
- }
-
-insert_in_hash:
- /* Insert the control node in the hash */
- new_ref->next = hdl->buckets[bucket];
- hdl->buckets[bucket] = new_ref;
-
-unlock:
- mutex_unlock(hdl->lock);
- return 0;
-}
-
-/* Add a new control */
-static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
- const struct v4l2_ctrl_ops *ops,
- u32 id, const char *name, enum v4l2_ctrl_type type,
- s32 min, s32 max, u32 step, s32 def,
- u32 flags, const char * const *qmenu,
- const s64 *qmenu_int, void *priv)
-{
- struct v4l2_ctrl *ctrl;
- unsigned sz_extra = 0;
-
- if (hdl->error)
- return NULL;
-
- /* Sanity checks */
- if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
- (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
- (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
- (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
- (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) ||
- (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
- handler_set_err(hdl, -ERANGE);
- return NULL;
- }
- if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
- handler_set_err(hdl, -ERANGE);
- return NULL;
- }
- if ((type == V4L2_CTRL_TYPE_INTEGER ||
- type == V4L2_CTRL_TYPE_MENU ||
- type == V4L2_CTRL_TYPE_INTEGER_MENU ||
- type == V4L2_CTRL_TYPE_BOOLEAN) &&
- (def < min || def > max)) {
- handler_set_err(hdl, -ERANGE);
- return NULL;
- }
- if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
- handler_set_err(hdl, -ERANGE);
- return NULL;
- }
-
- if (type == V4L2_CTRL_TYPE_BUTTON)
- flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
- else if (type == V4L2_CTRL_TYPE_CTRL_CLASS)
- flags |= V4L2_CTRL_FLAG_READ_ONLY;
- else if (type == V4L2_CTRL_TYPE_STRING)
- sz_extra += 2 * (max + 1);
-
- ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL);
- if (ctrl == NULL) {
- handler_set_err(hdl, -ENOMEM);
- return NULL;
- }
-
- INIT_LIST_HEAD(&ctrl->node);
- INIT_LIST_HEAD(&ctrl->ev_subs);
- ctrl->handler = hdl;
- ctrl->ops = ops;
- ctrl->id = id;
- ctrl->name = name;
- ctrl->type = type;
- ctrl->flags = flags;
- ctrl->minimum = min;
- ctrl->maximum = max;
- ctrl->step = step;
- if (type == V4L2_CTRL_TYPE_MENU)
- ctrl->qmenu = qmenu;
- else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
- ctrl->qmenu_int = qmenu_int;
- ctrl->priv = priv;
- ctrl->cur.val = ctrl->val = ctrl->default_value = def;
-
- if (ctrl->type == V4L2_CTRL_TYPE_STRING) {
- ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1);
- ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1);
- if (ctrl->minimum)
- memset(ctrl->cur.string, ' ', ctrl->minimum);
- }
- if (handler_new_ref(hdl, ctrl)) {
- kfree(ctrl);
- return NULL;
- }
- mutex_lock(hdl->lock);
- list_add_tail(&ctrl->node, &hdl->ctrls);
- mutex_unlock(hdl->lock);
- return ctrl;
-}
-
-struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
- const struct v4l2_ctrl_config *cfg, void *priv)
-{
- bool is_menu;
- struct v4l2_ctrl *ctrl;
- const char *name = cfg->name;
- const char * const *qmenu = cfg->qmenu;
- const s64 *qmenu_int = cfg->qmenu_int;
- enum v4l2_ctrl_type type = cfg->type;
- u32 flags = cfg->flags;
- s32 min = cfg->min;
- s32 max = cfg->max;
- u32 step = cfg->step;
- s32 def = cfg->def;
-
- if (name == NULL)
- v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
- &def, &flags);
-
- is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU ||
- cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU);
- if (is_menu)
- WARN_ON(step);
- else
- WARN_ON(cfg->menu_skip_mask);
- if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)
- qmenu = v4l2_ctrl_get_menu(cfg->id);
- else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU &&
- qmenu_int == NULL) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
-
- ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
- type, min, max,
- is_menu ? cfg->menu_skip_mask : step,
- def, flags, qmenu, qmenu_int, priv);
- if (ctrl)
- ctrl->is_private = cfg->is_private;
- return ctrl;
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_custom);
-
-/* Helper function for standard non-menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
- const struct v4l2_ctrl_ops *ops,
- u32 id, s32 min, s32 max, u32 step, s32 def)
-{
- const char *name;
- enum v4l2_ctrl_type type;
- u32 flags;
-
- v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
- if (type == V4L2_CTRL_TYPE_MENU
- || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
- return v4l2_ctrl_new(hdl, ops, id, name, type,
- min, max, step, def, flags, NULL, NULL, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_std);
-
-/* Helper function for standard menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
- const struct v4l2_ctrl_ops *ops,
- u32 id, s32 max, s32 mask, s32 def)
-{
- const char * const *qmenu = v4l2_ctrl_get_menu(id);
- const char *name;
- enum v4l2_ctrl_type type;
- s32 min;
- s32 step;
- u32 flags;
-
- v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
- if (type != V4L2_CTRL_TYPE_MENU) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
- return v4l2_ctrl_new(hdl, ops, id, name, type,
- 0, max, mask, def, flags, qmenu, NULL, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
-
-/* Helper function for standard integer menu controls */
-struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
- const struct v4l2_ctrl_ops *ops,
- u32 id, s32 max, s32 def, const s64 *qmenu_int)
-{
- const char *name;
- enum v4l2_ctrl_type type;
- s32 min;
- s32 step;
- u32 flags;
-
- v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
- if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
- return v4l2_ctrl_new(hdl, ops, id, name, type,
- 0, max, 0, def, flags, NULL, qmenu_int, NULL);
-}
-EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
-
-/* Add a control from another handler to this handler */
-struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl *ctrl)
-{
- if (hdl == NULL || hdl->error)
- return NULL;
- if (ctrl == NULL) {
- handler_set_err(hdl, -EINVAL);
- return NULL;
- }
- if (ctrl->handler == hdl)
- return ctrl;
- return handler_new_ref(hdl, ctrl) ? NULL : ctrl;
-}
-EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
-
-/* Add the controls from another handler to our own. */
-int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl_handler *add)
-{
- struct v4l2_ctrl_ref *ref;
- int ret = 0;
-
- /* Do nothing if either handler is NULL or if they are the same */
- if (!hdl || !add || hdl == add)
- return 0;
- if (hdl->error)
- return hdl->error;
- mutex_lock(add->lock);
- list_for_each_entry(ref, &add->ctrl_refs, node) {
- struct v4l2_ctrl *ctrl = ref->ctrl;
-
- /* Skip handler-private controls. */
- if (ctrl->is_private)
- continue;
- /* And control classes */
- if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
- continue;
- ret = handler_new_ref(hdl, ctrl);
- if (ret)
- break;
- }
- mutex_unlock(add->lock);
- return ret;
-}
-EXPORT_SYMBOL(v4l2_ctrl_add_handler);
-
-/* Cluster controls */
-void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
-{
- bool has_volatiles = false;
- int i;
-
- /* The first control is the master control and it must not be NULL */
- BUG_ON(ncontrols == 0 || controls[0] == NULL);
-
- for (i = 0; i < ncontrols; i++) {
- if (controls[i]) {
- controls[i]->cluster = controls;
- controls[i]->ncontrols = ncontrols;
- if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE)
- has_volatiles = true;
- }
- }
- controls[0]->has_volatiles = has_volatiles;
-}
-EXPORT_SYMBOL(v4l2_ctrl_cluster);
-
-void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
- u8 manual_val, bool set_volatile)
-{
- struct v4l2_ctrl *master = controls[0];
- u32 flag = 0;
- int i;
-
- v4l2_ctrl_cluster(ncontrols, controls);
- WARN_ON(ncontrols <= 1);
- WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
- WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
- master->is_auto = true;
- master->has_volatiles = set_volatile;
- master->manual_mode_value = manual_val;
- master->flags |= V4L2_CTRL_FLAG_UPDATE;
-
- if (!is_cur_manual(master))
- flag = V4L2_CTRL_FLAG_INACTIVE |
- (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0);
-
- for (i = 1; i < ncontrols; i++)
- if (controls[i])
- controls[i]->flags |= flag;
-}
-EXPORT_SYMBOL(v4l2_ctrl_auto_cluster);
-
-/* Activate/deactivate a control. */
-void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
-{
- /* invert since the actual flag is called 'inactive' */
- bool inactive = !active;
- bool old;
-
- if (ctrl == NULL)
- return;
-
- if (inactive)
- /* set V4L2_CTRL_FLAG_INACTIVE */
- old = test_and_set_bit(4, &ctrl->flags);
- else
- /* clear V4L2_CTRL_FLAG_INACTIVE */
- old = test_and_clear_bit(4, &ctrl->flags);
- if (old != inactive)
- send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
-}
-EXPORT_SYMBOL(v4l2_ctrl_activate);
-
-/* Grab/ungrab a control.
- Typically used when streaming starts and you want to grab controls,
- preventing the user from changing them.
-
- Just call this and the framework will block any attempts to change
- these controls. */
-void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
-{
- bool old;
-
- if (ctrl == NULL)
- return;
-
- v4l2_ctrl_lock(ctrl);
- if (grabbed)
- /* set V4L2_CTRL_FLAG_GRABBED */
- old = test_and_set_bit(1, &ctrl->flags);
- else
- /* clear V4L2_CTRL_FLAG_GRABBED */
- old = test_and_clear_bit(1, &ctrl->flags);
- if (old != grabbed)
- send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS);
- v4l2_ctrl_unlock(ctrl);
-}
-EXPORT_SYMBOL(v4l2_ctrl_grab);
-
-/* Log the control name and value */
-static void log_ctrl(const struct v4l2_ctrl *ctrl,
- const char *prefix, const char *colon)
-{
- if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY))
- return;
- if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS)
- return;
-
- printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name);
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_INTEGER:
- printk(KERN_CONT "%d", ctrl->cur.val);
- break;
- case V4L2_CTRL_TYPE_BOOLEAN:
- printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false");
- break;
- case V4L2_CTRL_TYPE_MENU:
- printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
- break;
- case V4L2_CTRL_TYPE_INTEGER_MENU:
- printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
- break;
- case V4L2_CTRL_TYPE_BITMASK:
- printk(KERN_CONT "0x%08x", ctrl->cur.val);
- break;
- case V4L2_CTRL_TYPE_INTEGER64:
- printk(KERN_CONT "%lld", ctrl->cur.val64);
- break;
- case V4L2_CTRL_TYPE_STRING:
- printk(KERN_CONT "%s", ctrl->cur.string);
- break;
- default:
- printk(KERN_CONT "unknown type %d", ctrl->type);
- break;
- }
- if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE |
- V4L2_CTRL_FLAG_GRABBED |
- V4L2_CTRL_FLAG_VOLATILE)) {
- if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
- printk(KERN_CONT " inactive");
- if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)
- printk(KERN_CONT " grabbed");
- if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE)
- printk(KERN_CONT " volatile");
- }
- printk(KERN_CONT "\n");
-}
-
-/* Log all controls owned by the handler */
-void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
- const char *prefix)
-{
- struct v4l2_ctrl *ctrl;
- const char *colon = "";
- int len;
-
- if (hdl == NULL)
- return;
- if (prefix == NULL)
- prefix = "";
- len = strlen(prefix);
- if (len && prefix[len - 1] != ' ')
- colon = ": ";
- mutex_lock(hdl->lock);
- list_for_each_entry(ctrl, &hdl->ctrls, node)
- if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
- log_ctrl(ctrl, prefix, colon);
- mutex_unlock(hdl->lock);
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
-
-/* Call s_ctrl for all controls owned by the handler */
-int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
-{
- struct v4l2_ctrl *ctrl;
- int ret = 0;
-
- if (hdl == NULL)
- return 0;
- mutex_lock(hdl->lock);
- list_for_each_entry(ctrl, &hdl->ctrls, node)
- ctrl->done = false;
-
- list_for_each_entry(ctrl, &hdl->ctrls, node) {
- struct v4l2_ctrl *master = ctrl->cluster[0];
- int i;
-
- /* Skip if this control was already handled by a cluster. */
- /* Skip button controls and read-only controls. */
- if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON ||
- (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY))
- continue;
-
- for (i = 0; i < master->ncontrols; i++) {
- if (master->cluster[i]) {
- cur_to_new(master->cluster[i]);
- master->cluster[i]->is_new = 1;
- master->cluster[i]->done = true;
- }
- }
- ret = call_op(master, s_ctrl);
- if (ret)
- break;
- }
- mutex_unlock(hdl->lock);
- return ret;
-}
-EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
-
-/* Implement VIDIOC_QUERYCTRL */
-int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
-{
- u32 id = qc->id & V4L2_CTRL_ID_MASK;
- struct v4l2_ctrl_ref *ref;
- struct v4l2_ctrl *ctrl;
-
- if (hdl == NULL)
- return -EINVAL;
-
- mutex_lock(hdl->lock);
-
- /* Try to find it */
- ref = find_ref(hdl, id);
-
- if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) {
- /* Find the next control with ID > qc->id */
-
- /* Did we reach the end of the control list? */
- if (id >= node2id(hdl->ctrl_refs.prev)) {
- ref = NULL; /* Yes, so there is no next control */
- } else if (ref) {
- /* We found a control with the given ID, so just get
- the next one in the list. */
- ref = list_entry(ref->node.next, typeof(*ref), node);
- } else {
- /* No control with the given ID exists, so start
- searching for the next largest ID. We know there
- is one, otherwise the first 'if' above would have
- been true. */
- list_for_each_entry(ref, &hdl->ctrl_refs, node)
- if (id < ref->ctrl->id)
- break;
- }
- }
- mutex_unlock(hdl->lock);
- if (!ref)
- return -EINVAL;
-
- ctrl = ref->ctrl;
- memset(qc, 0, sizeof(*qc));
- if (id >= V4L2_CID_PRIVATE_BASE)
- qc->id = id;
- else
- qc->id = ctrl->id;
- strlcpy(qc->name, ctrl->name, sizeof(qc->name));
- qc->minimum = ctrl->minimum;
- qc->maximum = ctrl->maximum;
- qc->default_value = ctrl->default_value;
- if (ctrl->type == V4L2_CTRL_TYPE_MENU
- || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
- qc->step = 1;
- else
- qc->step = ctrl->step;
- qc->flags = ctrl->flags;
- qc->type = ctrl->type;
- return 0;
-}
-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);
-
-/* Implement VIDIOC_QUERYMENU */
-int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
-{
- struct v4l2_ctrl *ctrl;
- u32 i = qm->index;
-
- ctrl = v4l2_ctrl_find(hdl, qm->id);
- if (!ctrl)
- return -EINVAL;
-
- qm->reserved = 0;
- /* Sanity checks */
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_MENU:
- if (ctrl->qmenu == NULL)
- return -EINVAL;
- break;
- case V4L2_CTRL_TYPE_INTEGER_MENU:
- if (ctrl->qmenu_int == NULL)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- if (i < ctrl->minimum || i > ctrl->maximum)
- return -EINVAL;
-
- /* Use mask to see if this menu item should be skipped */
- if (ctrl->menu_skip_mask & (1 << i))
- return -EINVAL;
- /* Empty menu items should also be skipped */
- if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
- if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
- return -EINVAL;
- strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
- } else {
- qm->value = ctrl->qmenu_int[i];
- }
- return 0;
-}
-EXPORT_SYMBOL(v4l2_querymenu);
-
-int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm)
-{
- return v4l2_querymenu(sd->ctrl_handler, qm);
-}
-EXPORT_SYMBOL(v4l2_subdev_querymenu);
-
-
-
-/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS:
-
- It is not a fully atomic operation, just best-effort only. After all, if
- multiple controls have to be set through multiple i2c writes (for example)
- then some initial writes may succeed while others fail. Thus leaving the
- system in an inconsistent state. The question is how much effort you are
- willing to spend on trying to make something atomic that really isn't.
-
- From the point of view of an application the main requirement is that
- when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an
- error should be returned without actually affecting any controls.
-
- If all the values are correct, then it is acceptable to just give up
- in case of low-level errors.
-
- It is important though that the application can tell when only a partial
- configuration was done. The way we do that is through the error_idx field
- of struct v4l2_ext_controls: if that is equal to the count field then no
- controls were affected. Otherwise all controls before that index were
- successful in performing their 'get' or 'set' operation, the control at
- the given index failed, and you don't know what happened with the controls
- after the failed one. Since if they were part of a control cluster they
- could have been successfully processed (if a cluster member was encountered
- at index < error_idx), they could have failed (if a cluster member was at
- error_idx), or they may not have been processed yet (if the first cluster
- member appeared after error_idx).
-
- It is all fairly theoretical, though. In practice all you can do is to
- bail out. If error_idx == count, then it is an application bug. If
- error_idx < count then it is only an application bug if the error code was
- EBUSY. That usually means that something started streaming just when you
- tried to set the controls. In all other cases it is a driver/hardware
- problem and all you can do is to retry or bail out.
-
- Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that
- never modifies controls the error_idx is just set to whatever control
- has an invalid value.
- */
-
-/* Prepare for the extended g/s/try functions.
- Find the controls in the control array and do some basic checks. */
-static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ext_controls *cs,
- struct v4l2_ctrl_helper *helpers)
-{
- struct v4l2_ctrl_helper *h;
- bool have_clusters = false;
- u32 i;
-
- for (i = 0, h = helpers; i < cs->count; i++, h++) {
- struct v4l2_ext_control *c = &cs->controls[i];
- struct v4l2_ctrl_ref *ref;
- struct v4l2_ctrl *ctrl;
- u32 id = c->id & V4L2_CTRL_ID_MASK;
-
- cs->error_idx = i;
-
- if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class)
- return -EINVAL;
-
- /* Old-style private controls are not allowed for
- extended controls */
- if (id >= V4L2_CID_PRIVATE_BASE)
- return -EINVAL;
- ref = find_ref_lock(hdl, id);
- if (ref == NULL)
- return -EINVAL;
- ctrl = ref->ctrl;
- if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
- return -EINVAL;
-
- if (ctrl->cluster[0]->ncontrols > 1)
- have_clusters = true;
- if (ctrl->cluster[0] != ctrl)
- ref = find_ref_lock(hdl, ctrl->cluster[0]->id);
- /* Store the ref to the master control of the cluster */
- h->mref = ref;
- h->ctrl = ctrl;
- /* Initially set next to 0, meaning that there is no other
- control in this helper array belonging to the same
- cluster */
- h->next = 0;
- }
-
- /* We are done if there were no controls that belong to a multi-
- control cluster. */
- if (!have_clusters)
- return 0;
-
- /* The code below figures out in O(n) time which controls in the list
- belong to the same cluster. */
-
- /* This has to be done with the handler lock taken. */
- mutex_lock(hdl->lock);
-
- /* First zero the helper field in the master control references */
- for (i = 0; i < cs->count; i++)
- helpers[i].mref->helper = NULL;
- for (i = 0, h = helpers; i < cs->count; i++, h++) {
- struct v4l2_ctrl_ref *mref = h->mref;
-
- /* If the mref->helper is set, then it points to an earlier
- helper that belongs to the same cluster. */
- if (mref->helper) {
- /* Set the next field of mref->helper to the current
- index: this means that that earlier helper now
- points to the next helper in the same cluster. */
- mref->helper->next = i;
- /* mref should be set only for the first helper in the
- cluster, clear the others. */
- h->mref = NULL;
- }
- /* Point the mref helper to the current helper struct. */
- mref->helper = h;
- }
- mutex_unlock(hdl->lock);
- return 0;
-}
-
-/* Handles the corner case where cs->count == 0. It checks whether the
- specified control class exists. If that class ID is 0, then it checks
- whether there are any controls at all. */
-static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class)
-{
- if (ctrl_class == 0)
- return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0;
- return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL;
-}
-
-
-
-/* Get extended controls. Allocates the helpers array if needed. */
-int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
-{
- struct v4l2_ctrl_helper helper[4];
- struct v4l2_ctrl_helper *helpers = helper;
- int ret;
- int i, j;
-
- cs->error_idx = cs->count;
- cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
-
- if (hdl == NULL)
- return -EINVAL;
-
- if (cs->count == 0)
- return class_check(hdl, cs->ctrl_class);
-
- if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc_array(cs->count, sizeof(helper[0]),
- GFP_KERNEL);
- if (helpers == NULL)
- return -ENOMEM;
- }
-
- ret = prepare_ext_ctrls(hdl, cs, helpers);
- cs->error_idx = cs->count;
-
- for (i = 0; !ret && i < cs->count; i++)
- if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
- ret = -EACCES;
-
- for (i = 0; !ret && i < cs->count; i++) {
- int (*ctrl_to_user)(struct v4l2_ext_control *c,
- struct v4l2_ctrl *ctrl) = cur_to_user;
- struct v4l2_ctrl *master;
-
- if (helpers[i].mref == NULL)
- continue;
-
- master = helpers[i].mref->ctrl;
- cs->error_idx = i;
-
- v4l2_ctrl_lock(master);
-
- /* g_volatile_ctrl will update the new control values */
- if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) ||
- (master->has_volatiles && !is_cur_manual(master))) {
- for (j = 0; j < master->ncontrols; j++)
- cur_to_new(master->cluster[j]);
- ret = call_op(master, g_volatile_ctrl);
- ctrl_to_user = new_to_user;
- }
- /* If OK, then copy the current (for non-volatile controls)
- or the new (for volatile controls) control values to the
- caller */
- if (!ret) {
- u32 idx = i;
-
- do {
- ret = ctrl_to_user(cs->controls + idx,
- helpers[idx].ctrl);
- idx = helpers[idx].next;
- } while (!ret && idx);
- }
- v4l2_ctrl_unlock(master);
- }
-
- if (cs->count > ARRAY_SIZE(helper))
- kfree(helpers);
- return ret;
-}
-EXPORT_SYMBOL(v4l2_g_ext_ctrls);
-
-int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
- return v4l2_g_ext_ctrls(sd->ctrl_handler, cs);
-}
-EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls);
-
-/* Helper function to get a single control */
-static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
-{
- struct v4l2_ctrl *master = ctrl->cluster[0];
- int ret = 0;
- int i;
-
- if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
- return -EACCES;
-
- v4l2_ctrl_lock(master);
- /* g_volatile_ctrl will update the current control values */
- if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) {
- for (i = 0; i < master->ncontrols; i++)
- cur_to_new(master->cluster[i]);
- ret = call_op(master, g_volatile_ctrl);
- *val = ctrl->val;
- } else {
- *val = ctrl->cur.val;
- }
- v4l2_ctrl_unlock(master);
- return ret;
-}
-
-int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control)
-{
- struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
-
- if (ctrl == NULL || !type_is_int(ctrl))
- return -EINVAL;
- return get_ctrl(ctrl, &control->value);
-}
-EXPORT_SYMBOL(v4l2_g_ctrl);
-
-int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
-{
- return v4l2_g_ctrl(sd->ctrl_handler, control);
-}
-EXPORT_SYMBOL(v4l2_subdev_g_ctrl);
-
-s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl)
-{
- s32 val = 0;
-
- /* It's a driver bug if this happens. */
- WARN_ON(!type_is_int(ctrl));
- get_ctrl(ctrl, &val);
- return val;
-}
-EXPORT_SYMBOL(v4l2_ctrl_g_ctrl);
-
-
-/* Core function that calls try/s_ctrl and ensures that the new value is
- copied to the current value on a set.
- Must be called with ctrl->handler->lock held. */
-static int try_or_set_cluster(struct v4l2_fh *fh,
- struct v4l2_ctrl *master, bool set)
-{
- bool update_flag;
- int ret;
- int i;
-
- /* Go through the cluster and either validate the new value or
- (if no new value was set), copy the current value to the new
- value, ensuring a consistent view for the control ops when
- called. */
- for (i = 0; i < master->ncontrols; i++) {
- struct v4l2_ctrl *ctrl = master->cluster[i];
-
- if (ctrl == NULL)
- continue;
-
- if (!ctrl->is_new) {
- cur_to_new(ctrl);
- continue;
- }
- /* Check again: it may have changed since the
- previous check in try_or_set_ext_ctrls(). */
- if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
- return -EBUSY;
- }
-
- ret = call_op(master, try_ctrl);
-
- /* Don't set if there is no change */
- if (ret || !set || !cluster_changed(master))
- return ret;
- ret = call_op(master, s_ctrl);
- if (ret)
- return ret;
-
- /* If OK, then make the new values permanent. */
- update_flag = is_cur_manual(master) != is_new_manual(master);
- for (i = 0; i < master->ncontrols; i++)
- new_to_cur(fh, master->cluster[i], update_flag && i > 0);
- return 0;
-}
-
-/* Validate controls. */
-static int validate_ctrls(struct v4l2_ext_controls *cs,
- struct v4l2_ctrl_helper *helpers, bool set)
-{
- unsigned i;
- int ret = 0;
-
- cs->error_idx = cs->count;
- for (i = 0; i < cs->count; i++) {
- struct v4l2_ctrl *ctrl = helpers[i].ctrl;
-
- cs->error_idx = i;
-
- if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
- return -EACCES;
- /* This test is also done in try_set_control_cluster() which
- is called in atomic context, so that has the final say,
- but it makes sense to do an up-front check as well. Once
- an error occurs in try_set_control_cluster() some other
- controls may have been set already and we want to do a
- best-effort to avoid that. */
- if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
- return -EBUSY;
- ret = validate_new(ctrl, &cs->controls[i]);
- if (ret)
- return ret;
- }
- return 0;
-}
-
-/* Obtain the current volatile values of an autocluster and mark them
- as new. */
-static void update_from_auto_cluster(struct v4l2_ctrl *master)
-{
- int i;
-
- for (i = 0; i < master->ncontrols; i++)
- cur_to_new(master->cluster[i]);
- if (!call_op(master, g_volatile_ctrl))
- for (i = 1; i < master->ncontrols; i++)
- if (master->cluster[i])
- master->cluster[i]->is_new = 1;
-}
-
-/* Try or try-and-set controls */
-static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
- struct v4l2_ext_controls *cs,
- bool set)
-{
- struct v4l2_ctrl_helper helper[4];
- struct v4l2_ctrl_helper *helpers = helper;
- unsigned i, j;
- int ret;
-
- cs->error_idx = cs->count;
- cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
-
- if (hdl == NULL)
- return -EINVAL;
-
- if (cs->count == 0)
- return class_check(hdl, cs->ctrl_class);
-
- if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc_array(cs->count, sizeof(helper[0]),
- GFP_KERNEL);
- if (!helpers)
- return -ENOMEM;
- }
- ret = prepare_ext_ctrls(hdl, cs, helpers);
- if (!ret)
- ret = validate_ctrls(cs, helpers, set);
- if (ret && set)
- cs->error_idx = cs->count;
- for (i = 0; !ret && i < cs->count; i++) {
- struct v4l2_ctrl *master;
- u32 idx = i;
-
- if (helpers[i].mref == NULL)
- continue;
-
- cs->error_idx = i;
- master = helpers[i].mref->ctrl;
- v4l2_ctrl_lock(master);
-
- /* Reset the 'is_new' flags of the cluster */
- for (j = 0; j < master->ncontrols; j++)
- if (master->cluster[j])
- master->cluster[j]->is_new = 0;
-
- /* For volatile autoclusters that are currently in auto mode
- we need to discover if it will be set to manual mode.
- If so, then we have to copy the current volatile values
- first since those will become the new manual values (which
- may be overwritten by explicit new values from this set
- of controls). */
- if (master->is_auto && master->has_volatiles &&
- !is_cur_manual(master)) {
- /* Pick an initial non-manual value */
- s32 new_auto_val = master->manual_mode_value + 1;
- u32 tmp_idx = idx;
-
- do {
- /* Check if the auto control is part of the
- list, and remember the new value. */
- if (helpers[tmp_idx].ctrl == master)
- new_auto_val = cs->controls[tmp_idx].value;
- tmp_idx = helpers[tmp_idx].next;
- } while (tmp_idx);
- /* If the new value == the manual value, then copy
- the current volatile values. */
- if (new_auto_val == master->manual_mode_value)
- update_from_auto_cluster(master);
- }
-
- /* Copy the new caller-supplied control values.
- user_to_new() sets 'is_new' to 1. */
- do {
- ret = user_to_new(cs->controls + idx, helpers[idx].ctrl);
- idx = helpers[idx].next;
- } while (!ret && idx);
-
- if (!ret)
- ret = try_or_set_cluster(fh, master, set);
-
- /* Copy the new values back to userspace. */
- if (!ret) {
- idx = i;
- do {
- ret = new_to_user(cs->controls + idx,
- helpers[idx].ctrl);
- idx = helpers[idx].next;
- } while (!ret && idx);
- }
- v4l2_ctrl_unlock(master);
- }
-
- if (cs->count > ARRAY_SIZE(helper))
- kfree(helpers);
- return ret;
-}
-
-int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs)
-{
- return try_set_ext_ctrls(NULL, hdl, cs, false);
-}
-EXPORT_SYMBOL(v4l2_try_ext_ctrls);
-
-int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
- struct v4l2_ext_controls *cs)
-{
- return try_set_ext_ctrls(fh, hdl, cs, true);
-}
-EXPORT_SYMBOL(v4l2_s_ext_ctrls);
-
-int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
- return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false);
-}
-EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls);
-
-int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs)
-{
- return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true);
-}
-EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls);
-
-/* Helper function for VIDIOC_S_CTRL compatibility */
-static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val)
-{
- struct v4l2_ctrl *master = ctrl->cluster[0];
- int ret;
- int i;
-
- ret = validate_new_int(ctrl, val);
- if (ret)
- return ret;
-
- v4l2_ctrl_lock(ctrl);
-
- /* Reset the 'is_new' flags of the cluster */
- for (i = 0; i < master->ncontrols; i++)
- if (master->cluster[i])
- master->cluster[i]->is_new = 0;
-
- /* For autoclusters with volatiles that are switched from auto to
- manual mode we have to update the current volatile values since
- those will become the initial manual values after such a switch. */
- if (master->is_auto && master->has_volatiles && ctrl == master &&
- !is_cur_manual(master) && *val == master->manual_mode_value)
- update_from_auto_cluster(master);
- ctrl->val = *val;
- ctrl->is_new = 1;
- ret = try_or_set_cluster(fh, master, true);
- *val = ctrl->cur.val;
- v4l2_ctrl_unlock(ctrl);
- return ret;
-}
-
-int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
- struct v4l2_control *control)
-{
- struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id);
-
- if (ctrl == NULL || !type_is_int(ctrl))
- return -EINVAL;
-
- if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
- return -EACCES;
-
- return set_ctrl(fh, ctrl, &control->value);
-}
-EXPORT_SYMBOL(v4l2_s_ctrl);
-
-int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control)
-{
- return v4l2_s_ctrl(NULL, sd->ctrl_handler, control);
-}
-EXPORT_SYMBOL(v4l2_subdev_s_ctrl);
-
-int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
-{
- /* It's a driver bug if this happens. */
- WARN_ON(!type_is_int(ctrl));
- return set_ctrl(NULL, ctrl, &val);
-}
-EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
-
-static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
-{
- struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
-
- if (ctrl == NULL)
- return -EINVAL;
-
- v4l2_ctrl_lock(ctrl);
- list_add_tail(&sev->node, &ctrl->ev_subs);
- if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
- (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
- struct v4l2_event ev;
- u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
-
- if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
- changes |= V4L2_EVENT_CTRL_CH_VALUE;
- fill_event(&ev, ctrl, changes);
- /* Mark the queue as active, allowing this initial
- event to be accepted. */
- sev->elems = elems;
- v4l2_event_queue_fh(sev->fh, &ev);
- }
- v4l2_ctrl_unlock(ctrl);
- return 0;
-}
-
-static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev)
-{
- struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
-
- v4l2_ctrl_lock(ctrl);
- list_del(&sev->node);
- v4l2_ctrl_unlock(ctrl);
-}
-
-void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
-{
- u32 old_changes = old->u.ctrl.changes;
-
- old->u.ctrl = new->u.ctrl;
- old->u.ctrl.changes |= old_changes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_replace);
-
-void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
-{
- new->u.ctrl.changes |= old->u.ctrl.changes;
-}
-EXPORT_SYMBOL(v4l2_ctrl_merge);
-
-const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = {
- .add = v4l2_ctrl_add_event,
- .del = v4l2_ctrl_del_event,
- .replace = v4l2_ctrl_replace,
- .merge = v4l2_ctrl_merge,
-};
-EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops);
-
-int v4l2_ctrl_log_status(struct file *file, void *fh)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_fh *vfh = file->private_data;
-
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
- v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
- vfd->v4l2_dev->name);
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_log_status);
-
-int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- if (sub->type == V4L2_EVENT_CTRL)
- return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
- return -EINVAL;
-}
-EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
-
-unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
-{
- struct v4l2_fh *fh = file->private_data;
-
- if (v4l2_event_pending(fh))
- return POLLPRI;
- poll_wait(file, &fh->wait, wait);
- return 0;
-}
-EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
deleted file mode 100644
index 07aeafca9eaa..000000000000
--- a/drivers/media/video/v4l2-dev.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * Video capture interface for Linux version 2
- *
- * A generic video device interface for the LINUX operating system
- * using a set of device structures/vectors for low level operations.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
- * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
- *
- * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com>
- * - Added procfs support
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/kmod.h>
-#include <linux/slab.h>
-#include <asm/uaccess.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-
-#define VIDEO_NUM_DEVICES 256
-#define VIDEO_NAME "video4linux"
-
-/*
- * sysfs stuff
- */
-
-static ssize_t show_index(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev = to_video_device(cd);
-
- return sprintf(buf, "%i\n", vdev->index);
-}
-
-static ssize_t show_debug(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev = to_video_device(cd);
-
- return sprintf(buf, "%i\n", vdev->debug);
-}
-
-static ssize_t set_debug(struct device *cd, struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct video_device *vdev = to_video_device(cd);
- int res = 0;
- u16 value;
-
- res = kstrtou16(buf, 0, &value);
- if (res)
- return res;
-
- vdev->debug = value;
- return len;
-}
-
-static ssize_t show_name(struct device *cd,
- struct device_attribute *attr, char *buf)
-{
- struct video_device *vdev = to_video_device(cd);
-
- return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
-}
-
-static struct device_attribute video_device_attrs[] = {
- __ATTR(name, S_IRUGO, show_name, NULL),
- __ATTR(debug, 0644, show_debug, set_debug),
- __ATTR(index, S_IRUGO, show_index, NULL),
- __ATTR_NULL
-};
-
-/*
- * Active devices
- */
-static struct video_device *video_device[VIDEO_NUM_DEVICES];
-static DEFINE_MUTEX(videodev_lock);
-static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES);
-
-/* Device node utility functions */
-
-/* Note: these utility functions all assume that vfl_type is in the range
- [0, VFL_TYPE_MAX-1]. */
-
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
-/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
-{
- /* Any types not assigned to fixed minor ranges must be mapped to
- one single bitmap for the purposes of finding a free node number
- since all those unassigned types use the same minor range. */
- int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type;
-
- return devnode_nums[idx];
-}
-#else
-/* Return the bitmap corresponding to vfl_type. */
-static inline unsigned long *devnode_bits(int vfl_type)
-{
- return devnode_nums[vfl_type];
-}
-#endif
-
-/* Mark device node number vdev->num as used */
-static inline void devnode_set(struct video_device *vdev)
-{
- set_bit(vdev->num, devnode_bits(vdev->vfl_type));
-}
-
-/* Mark device node number vdev->num as unused */
-static inline void devnode_clear(struct video_device *vdev)
-{
- clear_bit(vdev->num, devnode_bits(vdev->vfl_type));
-}
-
-/* Try to find a free device node number in the range [from, to> */
-static inline int devnode_find(struct video_device *vdev, int from, int to)
-{
- return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from);
-}
-
-struct video_device *video_device_alloc(void)
-{
- return kzalloc(sizeof(struct video_device), GFP_KERNEL);
-}
-EXPORT_SYMBOL(video_device_alloc);
-
-void video_device_release(struct video_device *vdev)
-{
- kfree(vdev);
-}
-EXPORT_SYMBOL(video_device_release);
-
-void video_device_release_empty(struct video_device *vdev)
-{
- /* Do nothing */
- /* Only valid when the video_device struct is a static. */
-}
-EXPORT_SYMBOL(video_device_release_empty);
-
-static inline void video_get(struct video_device *vdev)
-{
- get_device(&vdev->dev);
-}
-
-static inline void video_put(struct video_device *vdev)
-{
- put_device(&vdev->dev);
-}
-
-/* Called when the last user of the video device exits. */
-static void v4l2_device_release(struct device *cd)
-{
- struct video_device *vdev = to_video_device(cd);
- struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
-
- mutex_lock(&videodev_lock);
- if (WARN_ON(video_device[vdev->minor] != vdev)) {
- /* should not happen */
- mutex_unlock(&videodev_lock);
- return;
- }
-
- /* Free up this device for reuse */
- video_device[vdev->minor] = NULL;
-
- /* Delete the cdev on this minor as well */
- cdev_del(vdev->cdev);
- /* Just in case some driver tries to access this from
- the release() callback. */
- vdev->cdev = NULL;
-
- /* Mark device node number as free */
- devnode_clear(vdev);
-
- mutex_unlock(&videodev_lock);
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev && v4l2_dev->mdev &&
- vdev->vfl_type != VFL_TYPE_SUBDEV)
- media_device_unregister_entity(&vdev->entity);
-#endif
-
- /* Do not call v4l2_device_put if there is no release callback set.
- * Drivers that have no v4l2_device release callback might free the
- * v4l2_dev instance in the video_device release callback below, so we
- * must perform this check here.
- *
- * TODO: In the long run all drivers that use v4l2_device should use the
- * v4l2_device release callback. This check will then be unnecessary.
- */
- if (v4l2_dev && v4l2_dev->release == NULL)
- v4l2_dev = NULL;
-
- /* Release video_device and perform other
- cleanups as needed. */
- vdev->release(vdev);
-
- /* Decrease v4l2_device refcount */
- if (v4l2_dev)
- v4l2_device_put(v4l2_dev);
-}
-
-static struct class video_class = {
- .name = VIDEO_NAME,
- .dev_attrs = video_device_attrs,
-};
-
-struct video_device *video_devdata(struct file *file)
-{
- return video_device[iminor(file->f_path.dentry->d_inode)];
-}
-EXPORT_SYMBOL(video_devdata);
-
-
-/* Priority handling */
-
-static inline bool prio_is_valid(enum v4l2_priority prio)
-{
- return prio == V4L2_PRIORITY_BACKGROUND ||
- prio == V4L2_PRIORITY_INTERACTIVE ||
- prio == V4L2_PRIORITY_RECORD;
-}
-
-void v4l2_prio_init(struct v4l2_prio_state *global)
-{
- memset(global, 0, sizeof(*global));
-}
-EXPORT_SYMBOL(v4l2_prio_init);
-
-int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local,
- enum v4l2_priority new)
-{
- if (!prio_is_valid(new))
- return -EINVAL;
- if (*local == new)
- return 0;
-
- atomic_inc(&global->prios[new]);
- if (prio_is_valid(*local))
- atomic_dec(&global->prios[*local]);
- *local = new;
- return 0;
-}
-EXPORT_SYMBOL(v4l2_prio_change);
-
-void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local)
-{
- v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT);
-}
-EXPORT_SYMBOL(v4l2_prio_open);
-
-void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local)
-{
- if (prio_is_valid(local))
- atomic_dec(&global->prios[local]);
-}
-EXPORT_SYMBOL(v4l2_prio_close);
-
-enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global)
-{
- if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0)
- return V4L2_PRIORITY_RECORD;
- if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0)
- return V4L2_PRIORITY_INTERACTIVE;
- if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0)
- return V4L2_PRIORITY_BACKGROUND;
- return V4L2_PRIORITY_UNSET;
-}
-EXPORT_SYMBOL(v4l2_prio_max);
-
-int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local)
-{
- return (local < v4l2_prio_max(global)) ? -EBUSY : 0;
-}
-EXPORT_SYMBOL(v4l2_prio_check);
-
-
-static ssize_t v4l2_read(struct file *filp, char __user *buf,
- size_t sz, loff_t *off)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = -ENODEV;
-
- if (!vdev->fops->read)
- return -EINVAL;
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
- mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->read(filp, buf, sz, off);
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: read: %zd (%d)\n",
- video_device_node_name(vdev), sz, ret);
- return ret;
-}
-
-static ssize_t v4l2_write(struct file *filp, const char __user *buf,
- size_t sz, loff_t *off)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = -ENODEV;
-
- if (!vdev->fops->write)
- return -EINVAL;
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
- mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->write(filp, buf, sz, off);
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: write: %zd (%d)\n",
- video_device_node_name(vdev), sz, ret);
- return ret;
-}
-
-static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = POLLERR | POLLHUP;
-
- if (!vdev->fops->poll)
- return DEFAULT_POLLMASK;
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_lock(vdev->lock);
- if (video_is_registered(vdev))
- ret = vdev->fops->poll(filp, poll);
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: poll: %08x\n",
- video_device_node_name(vdev), ret);
- return ret;
-}
-
-static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = -ENODEV;
-
- if (vdev->fops->unlocked_ioctl) {
- struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd);
-
- if (lock && mutex_lock_interruptible(lock))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (lock)
- mutex_unlock(lock);
- } else if (vdev->fops->ioctl) {
- /* This code path is a replacement for the BKL. It is a major
- * hack but it will have to do for those drivers that are not
- * yet converted to use unlocked_ioctl.
- *
- * There are two options: if the driver implements struct
- * v4l2_device, then the lock defined there is used to
- * serialize the ioctls. Otherwise the v4l2 core lock defined
- * below is used. This lock is really bad since it serializes
- * completely independent devices.
- *
- * Both variants suffer from the same problem: if the driver
- * sleeps, then it blocks all ioctls since the lock is still
- * held. This is very common for VIDIOC_DQBUF since that
- * normally waits for a frame to arrive. As a result any other
- * ioctl calls will proceed very, very slowly since each call
- * will have to wait for the VIDIOC_QBUF to finish. Things that
- * should take 0.01s may now take 10-20 seconds.
- *
- * The workaround is to *not* take the lock for VIDIOC_DQBUF.
- * This actually works OK for videobuf-based drivers, since
- * videobuf will take its own internal lock.
- */
- static DEFINE_MUTEX(v4l2_ioctl_mutex);
- struct mutex *m = vdev->v4l2_dev ?
- &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex;
-
- if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->ioctl(filp, cmd, arg);
- if (cmd != VIDIOC_DQBUF)
- mutex_unlock(m);
- } else
- ret = -ENOTTY;
-
- return ret;
-}
-
-#ifdef CONFIG_MMU
-#define v4l2_get_unmapped_area NULL
-#else
-static unsigned long v4l2_get_unmapped_area(struct file *filp,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret;
-
- if (!vdev->fops->get_unmapped_area)
- return -ENOSYS;
- if (!video_is_registered(vdev))
- return -ENODEV;
- ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n",
- video_device_node_name(vdev), ret);
- return ret;
-}
-#endif
-
-static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = -ENODEV;
-
- if (!vdev->fops->mmap)
- return ret;
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
- mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->mmap(filp, vm);
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- if (vdev->debug)
- printk(KERN_DEBUG "%s: mmap (%d)\n",
- video_device_node_name(vdev), ret);
- return ret;
-}
-
-/* Override for the open function */
-static int v4l2_open(struct inode *inode, struct file *filp)
-{
- struct video_device *vdev;
- int ret = 0;
-
- /* Check if the video device is available */
- mutex_lock(&videodev_lock);
- vdev = video_devdata(filp);
- /* return ENODEV if the video device has already been removed. */
- if (vdev == NULL || !video_is_registered(vdev)) {
- mutex_unlock(&videodev_lock);
- return -ENODEV;
- }
- /* and increase the device refcount */
- video_get(vdev);
- mutex_unlock(&videodev_lock);
- if (vdev->fops->open) {
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
- mutex_lock_interruptible(vdev->lock)) {
- ret = -ERESTARTSYS;
- goto err;
- }
- if (video_is_registered(vdev))
- ret = vdev->fops->open(filp);
- else
- ret = -ENODEV;
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- }
-
-err:
- if (vdev->debug)
- printk(KERN_DEBUG "%s: open (%d)\n",
- video_device_node_name(vdev), ret);
- /* decrease the refcount in case of an error */
- if (ret)
- video_put(vdev);
- return ret;
-}
-
-/* Override for the release function */
-static int v4l2_release(struct inode *inode, struct file *filp)
-{
- struct video_device *vdev = video_devdata(filp);
- int ret = 0;
-
- if (vdev->fops->release) {
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_lock(vdev->lock);
- vdev->fops->release(filp);
- if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
- mutex_unlock(vdev->lock);
- }
- if (vdev->debug)
- printk(KERN_DEBUG "%s: release\n",
- video_device_node_name(vdev));
- /* decrease the refcount unconditionally since the release()
- return value is ignored. */
- video_put(vdev);
- return ret;
-}
-
-static const struct file_operations v4l2_fops = {
- .owner = THIS_MODULE,
- .read = v4l2_read,
- .write = v4l2_write,
- .open = v4l2_open,
- .get_unmapped_area = v4l2_get_unmapped_area,
- .mmap = v4l2_mmap,
- .unlocked_ioctl = v4l2_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = v4l2_compat_ioctl32,
-#endif
- .release = v4l2_release,
- .poll = v4l2_poll,
- .llseek = no_llseek,
-};
-
-/**
- * get_index - assign stream index number based on parent device
- * @vdev: video_device to assign index number to, vdev->parent should be assigned
- *
- * Note that when this is called the new device has not yet been registered
- * in the video_device array, but it was able to obtain a minor number.
- *
- * This means that we can always obtain a free stream index number since
- * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in
- * use of the video_device array.
- *
- * Returns a free index number.
- */
-static int get_index(struct video_device *vdev)
-{
- /* This can be static since this function is called with the global
- videodev_lock held. */
- static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
- int i;
-
- /* Some drivers do not set the parent. In that case always return 0. */
- if (vdev->parent == NULL)
- return 0;
-
- bitmap_zero(used, VIDEO_NUM_DEVICES);
-
- for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
- if (video_device[i] != NULL &&
- video_device[i]->parent == vdev->parent) {
- set_bit(video_device[i]->index, used);
- }
- }
-
- return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
-}
-
-#define SET_VALID_IOCTL(ops, cmd, op) \
- if (ops->op) \
- set_bit(_IOC_NR(cmd), valid_ioctls)
-
-/* This determines which ioctls are actually implemented in the driver.
- It's a one-time thing which simplifies video_ioctl2 as it can just do
- a bit test.
-
- Note that drivers can override this by setting bits to 1 in
- vdev->valid_ioctls. If an ioctl is marked as 1 when this function is
- called, then that ioctl will actually be marked as unimplemented.
-
- It does that by first setting up the local valid_ioctls bitmap, and
- at the end do a:
-
- vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls)
- */
-static void determine_valid_ioctls(struct video_device *vdev)
-{
- DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
- const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
-
- bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
-
- SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
- if (ops->vidioc_g_priority ||
- test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
- set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
- if (ops->vidioc_s_priority ||
- test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
- set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
- if (ops->vidioc_enum_fmt_vid_cap ||
- ops->vidioc_enum_fmt_vid_out ||
- ops->vidioc_enum_fmt_vid_cap_mplane ||
- ops->vidioc_enum_fmt_vid_out_mplane ||
- ops->vidioc_enum_fmt_vid_overlay ||
- ops->vidioc_enum_fmt_type_private)
- set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
- if (ops->vidioc_g_fmt_vid_cap ||
- ops->vidioc_g_fmt_vid_out ||
- ops->vidioc_g_fmt_vid_cap_mplane ||
- ops->vidioc_g_fmt_vid_out_mplane ||
- ops->vidioc_g_fmt_vid_overlay ||
- ops->vidioc_g_fmt_vbi_cap ||
- ops->vidioc_g_fmt_vid_out_overlay ||
- ops->vidioc_g_fmt_vbi_out ||
- ops->vidioc_g_fmt_sliced_vbi_cap ||
- ops->vidioc_g_fmt_sliced_vbi_out ||
- ops->vidioc_g_fmt_type_private)
- set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
- if (ops->vidioc_s_fmt_vid_cap ||
- ops->vidioc_s_fmt_vid_out ||
- ops->vidioc_s_fmt_vid_cap_mplane ||
- ops->vidioc_s_fmt_vid_out_mplane ||
- ops->vidioc_s_fmt_vid_overlay ||
- ops->vidioc_s_fmt_vbi_cap ||
- ops->vidioc_s_fmt_vid_out_overlay ||
- ops->vidioc_s_fmt_vbi_out ||
- ops->vidioc_s_fmt_sliced_vbi_cap ||
- ops->vidioc_s_fmt_sliced_vbi_out ||
- ops->vidioc_s_fmt_type_private)
- set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
- if (ops->vidioc_try_fmt_vid_cap ||
- ops->vidioc_try_fmt_vid_out ||
- ops->vidioc_try_fmt_vid_cap_mplane ||
- ops->vidioc_try_fmt_vid_out_mplane ||
- ops->vidioc_try_fmt_vid_overlay ||
- ops->vidioc_try_fmt_vbi_cap ||
- ops->vidioc_try_fmt_vid_out_overlay ||
- ops->vidioc_try_fmt_vbi_out ||
- ops->vidioc_try_fmt_sliced_vbi_cap ||
- ops->vidioc_try_fmt_sliced_vbi_out ||
- ops->vidioc_try_fmt_type_private)
- set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
- SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
- SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
- SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
- SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
- SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
- SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
- SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
- SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
- if (vdev->tvnorms)
- set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
- if (ops->vidioc_g_std || vdev->current_norm)
- set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
- SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
- SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
- SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
- SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
- SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
- SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
- SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
- /* Note: the control handler can also be passed through the filehandle,
- and that can't be tested here. If the bit for these control ioctls
- is set, then the ioctl is valid. But if it is 0, then it can still
- be valid if the filehandle passed the control handler. */
- if (vdev->ctrl_handler || ops->vidioc_queryctrl)
- set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
- set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
- if (vdev->ctrl_handler || ops->vidioc_querymenu)
- set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
- SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
- SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
- SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
- SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
- SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
- SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
- SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
- if (ops->vidioc_g_crop || ops->vidioc_g_selection)
- set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
- if (ops->vidioc_s_crop || ops->vidioc_s_selection)
- set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
- SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
- if (ops->vidioc_cropcap || ops->vidioc_g_selection)
- set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp);
- SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp);
- SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index);
- SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd);
- SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
- SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
- SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
- if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER &&
- (ops->vidioc_g_std || vdev->tvnorms)))
- set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
- SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
- SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
- SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
- SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
- SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
- SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
- SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register);
- SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register);
-#endif
- SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
- SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
- SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
- SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
- SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets);
- SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset);
- SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset);
- SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset);
- SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
- SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
- SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings);
- SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings);
- SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap);
- /* yes, really vidioc_subscribe_event */
- SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
- SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
- SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
- SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
- SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
- if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
- set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
- bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
- BASE_VIDIOC_PRIVATE);
-}
-
-/**
- * __video_register_device - register video4linux devices
- * @vdev: video device structure we want to register
- * @type: type of device to register
- * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ...
- * -1 == first free)
- * @warn_if_nr_in_use: warn if the desired device node number
- * was already in use and another number was chosen instead.
- * @owner: module that owns the video device node
- *
- * The registration code assigns minor numbers and device node numbers
- * based on the requested type and registers the new device node with
- * the kernel.
- *
- * This function assumes that struct video_device was zeroed when it
- * was allocated and does not contain any stale date.
- *
- * An error is returned if no free minor or device node number could be
- * found, or if the registration of the device node failed.
- *
- * Zero is returned on success.
- *
- * Valid types are
- *
- * %VFL_TYPE_GRABBER - A frame grabber
- *
- * %VFL_TYPE_VBI - Vertical blank data (undecoded)
- *
- * %VFL_TYPE_RADIO - A radio card
- *
- * %VFL_TYPE_SUBDEV - A subdevice
- */
-int __video_register_device(struct video_device *vdev, int type, int nr,
- int warn_if_nr_in_use, struct module *owner)
-{
- int i = 0;
- int ret;
- int minor_offset = 0;
- int minor_cnt = VIDEO_NUM_DEVICES;
- const char *name_base;
-
- /* A minor value of -1 marks this video device as never
- having been registered */
- vdev->minor = -1;
-
- /* the release callback MUST be present */
- if (WARN_ON(!vdev->release))
- return -EINVAL;
-
- /* v4l2_fh support */
- spin_lock_init(&vdev->fh_lock);
- INIT_LIST_HEAD(&vdev->fh_list);
-
- /* Part 1: check device type */
- switch (type) {
- case VFL_TYPE_GRABBER:
- name_base = "video";
- break;
- case VFL_TYPE_VBI:
- name_base = "vbi";
- break;
- case VFL_TYPE_RADIO:
- name_base = "radio";
- break;
- case VFL_TYPE_SUBDEV:
- name_base = "v4l-subdev";
- break;
- default:
- printk(KERN_ERR "%s called with unknown type: %d\n",
- __func__, type);
- return -EINVAL;
- }
-
- vdev->vfl_type = type;
- vdev->cdev = NULL;
- if (vdev->v4l2_dev) {
- if (vdev->v4l2_dev->dev)
- vdev->parent = vdev->v4l2_dev->dev;
- if (vdev->ctrl_handler == NULL)
- vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
- /* If the prio state pointer is NULL, then use the v4l2_device
- prio state. */
- if (vdev->prio == NULL)
- vdev->prio = &vdev->v4l2_dev->prio;
- }
-
- /* Part 2: find a free minor, device node number and device index. */
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
- /* Keep the ranges for the first four types for historical
- * reasons.
- * Newer devices (not yet in place) should use the range
- * of 128-191 and just pick the first free minor there
- * (new style). */
- switch (type) {
- case VFL_TYPE_GRABBER:
- minor_offset = 0;
- minor_cnt = 64;
- break;
- case VFL_TYPE_RADIO:
- minor_offset = 64;
- minor_cnt = 64;
- break;
- case VFL_TYPE_VBI:
- minor_offset = 224;
- minor_cnt = 32;
- break;
- default:
- minor_offset = 128;
- minor_cnt = 64;
- break;
- }
-#endif
-
- /* Pick a device node number */
- mutex_lock(&videodev_lock);
- nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
- if (nr == minor_cnt)
- nr = devnode_find(vdev, 0, minor_cnt);
- if (nr == minor_cnt) {
- printk(KERN_ERR "could not get a free device node number\n");
- mutex_unlock(&videodev_lock);
- return -ENFILE;
- }
-#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
- /* 1-on-1 mapping of device node number to minor number */
- i = nr;
-#else
- /* The device node number and minor numbers are independent, so
- we just find the first free minor number. */
- for (i = 0; i < VIDEO_NUM_DEVICES; i++)
- if (video_device[i] == NULL)
- break;
- if (i == VIDEO_NUM_DEVICES) {
- mutex_unlock(&videodev_lock);
- printk(KERN_ERR "could not get a free minor\n");
- return -ENFILE;
- }
-#endif
- vdev->minor = i + minor_offset;
- vdev->num = nr;
- devnode_set(vdev);
-
- /* Should not happen since we thought this minor was free */
- WARN_ON(video_device[vdev->minor] != NULL);
- vdev->index = get_index(vdev);
- mutex_unlock(&videodev_lock);
- /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is
- clear and warn if it wasn't. */
- if (vdev->lock == NULL)
- WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags));
-
- if (vdev->ioctl_ops)
- determine_valid_ioctls(vdev);
-
- /* Part 3: Initialize the character device */
- vdev->cdev = cdev_alloc();
- if (vdev->cdev == NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
- vdev->cdev->ops = &v4l2_fops;
- vdev->cdev->owner = owner;
- ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
- if (ret < 0) {
- printk(KERN_ERR "%s: cdev_add failed\n", __func__);
- kfree(vdev->cdev);
- vdev->cdev = NULL;
- goto cleanup;
- }
-
- /* Part 4: register the device with sysfs */
- vdev->dev.class = &video_class;
- vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
- if (vdev->parent)
- vdev->dev.parent = vdev->parent;
- dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
- ret = device_register(&vdev->dev);
- if (ret < 0) {
- printk(KERN_ERR "%s: device_register failed\n", __func__);
- goto cleanup;
- }
- /* Register the release callback that will be called when the last
- reference to the device goes away. */
- vdev->dev.release = v4l2_device_release;
-
- if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
- printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
- name_base, nr, video_device_node_name(vdev));
-
- /* Increase v4l2_device refcount */
- if (vdev->v4l2_dev)
- v4l2_device_get(vdev->v4l2_dev);
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
- /* Part 5: Register the entity. */
- if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
- vdev->vfl_type != VFL_TYPE_SUBDEV) {
- vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
- vdev->entity.name = vdev->name;
- vdev->entity.info.v4l.major = VIDEO_MAJOR;
- vdev->entity.info.v4l.minor = vdev->minor;
- ret = media_device_register_entity(vdev->v4l2_dev->mdev,
- &vdev->entity);
- if (ret < 0)
- printk(KERN_WARNING
- "%s: media_device_register_entity failed\n",
- __func__);
- }
-#endif
- /* Part 6: Activate this minor. The char device can now be used. */
- set_bit(V4L2_FL_REGISTERED, &vdev->flags);
- mutex_lock(&videodev_lock);
- video_device[vdev->minor] = vdev;
- mutex_unlock(&videodev_lock);
-
- return 0;
-
-cleanup:
- mutex_lock(&videodev_lock);
- if (vdev->cdev)
- cdev_del(vdev->cdev);
- devnode_clear(vdev);
- mutex_unlock(&videodev_lock);
- /* Mark this video device as never having been registered. */
- vdev->minor = -1;
- return ret;
-}
-EXPORT_SYMBOL(__video_register_device);
-
-/**
- * video_unregister_device - unregister a video4linux device
- * @vdev: the device to unregister
- *
- * This unregisters the passed device. Future open calls will
- * be met with errors.
- */
-void video_unregister_device(struct video_device *vdev)
-{
- /* Check if vdev was ever registered at all */
- if (!vdev || !video_is_registered(vdev))
- return;
-
- mutex_lock(&videodev_lock);
- /* This must be in a critical section to prevent a race with v4l2_open.
- * Once this bit has been cleared video_get may never be called again.
- */
- clear_bit(V4L2_FL_REGISTERED, &vdev->flags);
- mutex_unlock(&videodev_lock);
- device_unregister(&vdev->dev);
-}
-EXPORT_SYMBOL(video_unregister_device);
-
-/*
- * Initialise video for linux
- */
-static int __init videodev_init(void)
-{
- dev_t dev = MKDEV(VIDEO_MAJOR, 0);
- int ret;
-
- printk(KERN_INFO "Linux video capture interface: v2.00\n");
- ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);
- if (ret < 0) {
- printk(KERN_WARNING "videodev: unable to get major %d\n",
- VIDEO_MAJOR);
- return ret;
- }
-
- ret = class_register(&video_class);
- if (ret < 0) {
- unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
- printk(KERN_WARNING "video_dev: class_register failed\n");
- return -EIO;
- }
-
- return 0;
-}
-
-static void __exit videodev_exit(void)
-{
- dev_t dev = MKDEV(VIDEO_MAJOR, 0);
-
- class_unregister(&video_class);
- unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
-}
-
-subsys_initcall(videodev_init);
-module_exit(videodev_exit)
-
-MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
-MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
-
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
deleted file mode 100644
index 1f203b85a637..000000000000
--- a/drivers/media/video/v4l2-device.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- V4L2 device support.
-
- Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You 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/types.h>
-#include <linux/ioctl.h>
-#include <linux/module.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#if defined(CONFIG_SPI)
-#include <linux/spi/spi.h>
-#endif
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ctrls.h>
-
-int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
-{
- if (v4l2_dev == NULL)
- return -EINVAL;
-
- INIT_LIST_HEAD(&v4l2_dev->subdevs);
- spin_lock_init(&v4l2_dev->lock);
- mutex_init(&v4l2_dev->ioctl_lock);
- v4l2_prio_init(&v4l2_dev->prio);
- kref_init(&v4l2_dev->ref);
- get_device(dev);
- v4l2_dev->dev = dev;
- if (dev == NULL) {
- /* If dev == NULL, then name must be filled in by the caller */
- WARN_ON(!v4l2_dev->name[0]);
- return 0;
- }
-
- /* Set name to driver name + device name if it is empty. */
- if (!v4l2_dev->name[0])
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
- dev->driver->name, dev_name(dev));
- if (!dev_get_drvdata(dev))
- dev_set_drvdata(dev, v4l2_dev);
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_device_register);
-
-static void v4l2_device_release(struct kref *ref)
-{
- struct v4l2_device *v4l2_dev =
- container_of(ref, struct v4l2_device, ref);
-
- if (v4l2_dev->release)
- v4l2_dev->release(v4l2_dev);
-}
-
-int v4l2_device_put(struct v4l2_device *v4l2_dev)
-{
- return kref_put(&v4l2_dev->ref, v4l2_device_release);
-}
-EXPORT_SYMBOL_GPL(v4l2_device_put);
-
-int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename,
- atomic_t *instance)
-{
- int num = atomic_inc_return(instance) - 1;
- int len = strlen(basename);
-
- if (basename[len - 1] >= '0' && basename[len - 1] <= '9')
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s-%d", basename, num);
- else
- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name),
- "%s%d", basename, num);
- return num;
-}
-EXPORT_SYMBOL_GPL(v4l2_device_set_name);
-
-void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
-{
- if (v4l2_dev->dev == NULL)
- return;
-
- if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev)
- dev_set_drvdata(v4l2_dev->dev, NULL);
- put_device(v4l2_dev->dev);
- v4l2_dev->dev = NULL;
-}
-EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
-
-void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
-{
- struct v4l2_subdev *sd, *next;
-
- if (v4l2_dev == NULL)
- return;
- v4l2_device_disconnect(v4l2_dev);
-
- /* Unregister subdevs */
- list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) {
- v4l2_device_unregister_subdev(sd);
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
- if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) {
- struct i2c_client *client = v4l2_get_subdevdata(sd);
-
- /* We need to unregister the i2c client explicitly.
- We cannot rely on i2c_del_adapter to always
- unregister clients for us, since if the i2c bus
- is a platform bus, then it is never deleted. */
- if (client)
- i2c_unregister_device(client);
- continue;
- }
-#endif
-#if defined(CONFIG_SPI)
- if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) {
- struct spi_device *spi = v4l2_get_subdevdata(sd);
-
- if (spi)
- spi_unregister_device(spi);
- continue;
- }
-#endif
- }
-}
-EXPORT_SYMBOL_GPL(v4l2_device_unregister);
-
-int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
- struct v4l2_subdev *sd)
-{
-#if defined(CONFIG_MEDIA_CONTROLLER)
- struct media_entity *entity = &sd->entity;
-#endif
- int err;
-
- /* Check for valid input */
- if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
- return -EINVAL;
-
- /* Warn if we apparently re-register a subdev */
- WARN_ON(sd->v4l2_dev != NULL);
-
- if (!try_module_get(sd->owner))
- return -ENODEV;
-
- sd->v4l2_dev = v4l2_dev;
- if (sd->internal_ops && sd->internal_ops->registered) {
- err = sd->internal_ops->registered(sd);
- if (err) {
- module_put(sd->owner);
- return err;
- }
- }
-
- /* This just returns 0 if either of the two args is NULL */
- err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);
- if (err) {
- if (sd->internal_ops && sd->internal_ops->unregistered)
- sd->internal_ops->unregistered(sd);
- module_put(sd->owner);
- return err;
- }
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
- /* Register the entity. */
- if (v4l2_dev->mdev) {
- err = media_device_register_entity(v4l2_dev->mdev, entity);
- if (err < 0) {
- if (sd->internal_ops && sd->internal_ops->unregistered)
- sd->internal_ops->unregistered(sd);
- module_put(sd->owner);
- return err;
- }
- }
-#endif
-
- spin_lock(&v4l2_dev->lock);
- list_add_tail(&sd->list, &v4l2_dev->subdevs);
- spin_unlock(&v4l2_dev->lock);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
-
-static void v4l2_device_release_subdev_node(struct video_device *vdev)
-{
- struct v4l2_subdev *sd = video_get_drvdata(vdev);
- sd->devnode = NULL;
- kfree(vdev);
-}
-
-int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
-{
- struct video_device *vdev;
- struct v4l2_subdev *sd;
- int err;
-
- /* Register a device node for every subdev marked with the
- * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
- */
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- continue;
-
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev) {
- err = -ENOMEM;
- goto clean_up;
- }
-
- video_set_drvdata(vdev, sd);
- strlcpy(vdev->name, sd->name, sizeof(vdev->name));
- vdev->v4l2_dev = v4l2_dev;
- vdev->fops = &v4l2_subdev_fops;
- vdev->release = v4l2_device_release_subdev_node;
- vdev->ctrl_handler = sd->ctrl_handler;
- err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
- sd->owner);
- if (err < 0) {
- kfree(vdev);
- goto clean_up;
- }
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.info.v4l.major = VIDEO_MAJOR;
- sd->entity.info.v4l.minor = vdev->minor;
-#endif
- sd->devnode = vdev;
- }
- return 0;
-
-clean_up:
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!sd->devnode)
- break;
- video_unregister_device(sd->devnode);
- }
-
- return err;
-}
-EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes);
-
-void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
-{
- struct v4l2_device *v4l2_dev;
-
- /* return if it isn't registered */
- if (sd == NULL || sd->v4l2_dev == NULL)
- return;
-
- v4l2_dev = sd->v4l2_dev;
-
- spin_lock(&v4l2_dev->lock);
- list_del(&sd->list);
- spin_unlock(&v4l2_dev->lock);
-
- if (sd->internal_ops && sd->internal_ops->unregistered)
- sd->internal_ops->unregistered(sd);
- sd->v4l2_dev = NULL;
-
-#if defined(CONFIG_MEDIA_CONTROLLER)
- if (v4l2_dev->mdev)
- media_device_unregister_entity(&sd->entity);
-#endif
- video_unregister_device(sd->devnode);
- module_put(sd->owner);
-}
-EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
deleted file mode 100644
index ef2a33c94045..000000000000
--- a/drivers/media/video/v4l2-event.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * v4l2-event.c
- *
- * V4L2 events.
- *
- * Copyright (C) 2009--2010 Nokia Corporation.
- *
- * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
-
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-
-static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
-{
- idx += sev->first;
- return idx >= sev->elems ? idx - sev->elems : idx;
-}
-
-static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
-{
- struct v4l2_kevent *kev;
- unsigned long flags;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
- if (list_empty(&fh->available)) {
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- return -ENOENT;
- }
-
- WARN_ON(fh->navailable == 0);
-
- kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
- list_del(&kev->list);
- fh->navailable--;
-
- kev->event.pending = fh->navailable;
- *event = kev->event;
- kev->sev->first = sev_pos(kev->sev, 1);
- kev->sev->in_use--;
-
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
- return 0;
-}
-
-int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
- int nonblocking)
-{
- int ret;
-
- if (nonblocking)
- return __v4l2_event_dequeue(fh, event);
-
- /* Release the vdev lock while waiting */
- if (fh->vdev->lock)
- mutex_unlock(fh->vdev->lock);
-
- do {
- ret = wait_event_interruptible(fh->wait,
- fh->navailable != 0);
- if (ret < 0)
- break;
-
- ret = __v4l2_event_dequeue(fh, event);
- } while (ret == -ENOENT);
-
- if (fh->vdev->lock)
- mutex_lock(fh->vdev->lock);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
-
-/* Caller must hold fh->vdev->fh_lock! */
-static struct v4l2_subscribed_event *v4l2_event_subscribed(
- struct v4l2_fh *fh, u32 type, u32 id)
-{
- struct v4l2_subscribed_event *sev;
-
- assert_spin_locked(&fh->vdev->fh_lock);
-
- list_for_each_entry(sev, &fh->subscribed, list)
- if (sev->type == type && sev->id == id)
- return sev;
-
- return NULL;
-}
-
-static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
- const struct timespec *ts)
-{
- struct v4l2_subscribed_event *sev;
- struct v4l2_kevent *kev;
- bool copy_payload = true;
-
- /* Are we subscribed? */
- sev = v4l2_event_subscribed(fh, ev->type, ev->id);
- if (sev == NULL)
- return;
-
- /*
- * If the event has been added to the fh->subscribed list, but its
- * add op has not completed yet elems will be 0, treat this as
- * not being subscribed.
- */
- if (!sev->elems)
- return;
-
- /* Increase event sequence number on fh. */
- fh->sequence++;
-
- /* Do we have any free events? */
- if (sev->in_use == sev->elems) {
- /* no, remove the oldest one */
- kev = sev->events + sev_pos(sev, 0);
- list_del(&kev->list);
- sev->in_use--;
- sev->first = sev_pos(sev, 1);
- fh->navailable--;
- if (sev->elems == 1) {
- if (sev->ops && sev->ops->replace) {
- sev->ops->replace(&kev->event, ev);
- copy_payload = false;
- }
- } else if (sev->ops && sev->ops->merge) {
- struct v4l2_kevent *second_oldest =
- sev->events + sev_pos(sev, 0);
- sev->ops->merge(&kev->event, &second_oldest->event);
- }
- }
-
- /* Take one and fill it. */
- kev = sev->events + sev_pos(sev, sev->in_use);
- kev->event.type = ev->type;
- if (copy_payload)
- kev->event.u = ev->u;
- kev->event.id = ev->id;
- kev->event.timestamp = *ts;
- kev->event.sequence = fh->sequence;
- sev->in_use++;
- list_add_tail(&kev->list, &fh->available);
-
- fh->navailable++;
-
- wake_up_all(&fh->wait);
-}
-
-void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
-{
- struct v4l2_fh *fh;
- unsigned long flags;
- struct timespec timestamp;
-
- ktime_get_ts(&timestamp);
-
- spin_lock_irqsave(&vdev->fh_lock, flags);
-
- list_for_each_entry(fh, &vdev->fh_list, list)
- __v4l2_event_queue_fh(fh, ev, &timestamp);
-
- spin_unlock_irqrestore(&vdev->fh_lock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_queue);
-
-void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
-{
- unsigned long flags;
- struct timespec timestamp;
-
- ktime_get_ts(&timestamp);
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- __v4l2_event_queue_fh(fh, ev, &timestamp);
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
-
-int v4l2_event_pending(struct v4l2_fh *fh)
-{
- return fh->navailable;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_pending);
-
-int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub, unsigned elems,
- const struct v4l2_subscribed_event_ops *ops)
-{
- struct v4l2_subscribed_event *sev, *found_ev;
- unsigned long flags;
- unsigned i;
-
- if (sub->type == V4L2_EVENT_ALL)
- return -EINVAL;
-
- if (elems < 1)
- elems = 1;
-
- sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
- if (!sev)
- return -ENOMEM;
- for (i = 0; i < elems; i++)
- sev->events[i].sev = sev;
- sev->type = sub->type;
- sev->id = sub->id;
- sev->flags = sub->flags;
- sev->fh = fh;
- sev->ops = ops;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
- if (!found_ev)
- list_add(&sev->list, &fh->subscribed);
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
- if (found_ev) {
- kfree(sev);
- return 0; /* Already listening */
- }
-
- if (sev->ops && sev->ops->add) {
- int ret = sev->ops->add(sev, elems);
- if (ret) {
- sev->ops = NULL;
- v4l2_event_unsubscribe(fh, sub);
- return ret;
- }
- }
-
- /* Mark as ready for use */
- sev->elems = elems;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
-
-void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
-{
- struct v4l2_event_subscription sub;
- struct v4l2_subscribed_event *sev;
- unsigned long flags;
-
- do {
- sev = NULL;
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
- if (!list_empty(&fh->subscribed)) {
- sev = list_first_entry(&fh->subscribed,
- struct v4l2_subscribed_event, list);
- sub.type = sev->type;
- sub.id = sev->id;
- }
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- if (sev)
- v4l2_event_unsubscribe(fh, &sub);
- } while (sev);
-}
-EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
-
-int v4l2_event_unsubscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- struct v4l2_subscribed_event *sev;
- unsigned long flags;
- int i;
-
- if (sub->type == V4L2_EVENT_ALL) {
- v4l2_event_unsubscribe_all(fh);
- return 0;
- }
-
- spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
- sev = v4l2_event_subscribed(fh, sub->type, sub->id);
- if (sev != NULL) {
- /* Remove any pending events for this subscription */
- for (i = 0; i < sev->in_use; i++) {
- list_del(&sev->events[sev_pos(sev, i)].list);
- fh->navailable--;
- }
- list_del(&sev->list);
- }
-
- spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-
- if (sev && sev->ops && sev->ops->del)
- sev->ops->del(sev);
-
- kfree(sev);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
deleted file mode 100644
index f5847af559b4..000000000000
--- a/drivers/media/video/v4l2-ioctl.c
+++ /dev/null
@@ -1,2336 +0,0 @@
-/*
- * Video capture interface for Linux version 2
- *
- * A generic framework to process V4L2 ioctl commands.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
- * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/version.h>
-
-#include <linux/videodev2.h>
-
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-chip-ident.h>
-#include <media/videobuf2-core.h>
-
-/* Zero out the end of the struct pointed to by p. Everything after, but
- * not including, the specified field is cleared. */
-#define CLEAR_AFTER_FIELD(p, field) \
- memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
- 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
-
-struct std_descr {
- v4l2_std_id std;
- const char *descr;
-};
-
-static const struct std_descr standards[] = {
- { V4L2_STD_NTSC, "NTSC" },
- { V4L2_STD_NTSC_M, "NTSC-M" },
- { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" },
- { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" },
- { V4L2_STD_NTSC_443, "NTSC-443" },
- { V4L2_STD_PAL, "PAL" },
- { V4L2_STD_PAL_BG, "PAL-BG" },
- { V4L2_STD_PAL_B, "PAL-B" },
- { V4L2_STD_PAL_B1, "PAL-B1" },
- { V4L2_STD_PAL_G, "PAL-G" },
- { V4L2_STD_PAL_H, "PAL-H" },
- { V4L2_STD_PAL_I, "PAL-I" },
- { V4L2_STD_PAL_DK, "PAL-DK" },
- { V4L2_STD_PAL_D, "PAL-D" },
- { V4L2_STD_PAL_D1, "PAL-D1" },
- { V4L2_STD_PAL_K, "PAL-K" },
- { V4L2_STD_PAL_M, "PAL-M" },
- { V4L2_STD_PAL_N, "PAL-N" },
- { V4L2_STD_PAL_Nc, "PAL-Nc" },
- { V4L2_STD_PAL_60, "PAL-60" },
- { V4L2_STD_SECAM, "SECAM" },
- { V4L2_STD_SECAM_B, "SECAM-B" },
- { V4L2_STD_SECAM_G, "SECAM-G" },
- { V4L2_STD_SECAM_H, "SECAM-H" },
- { V4L2_STD_SECAM_DK, "SECAM-DK" },
- { V4L2_STD_SECAM_D, "SECAM-D" },
- { V4L2_STD_SECAM_K, "SECAM-K" },
- { V4L2_STD_SECAM_K1, "SECAM-K1" },
- { V4L2_STD_SECAM_L, "SECAM-L" },
- { V4L2_STD_SECAM_LC, "SECAM-Lc" },
- { 0, "Unknown" }
-};
-
-/* video4linux standard ID conversion to standard name
- */
-const char *v4l2_norm_to_name(v4l2_std_id id)
-{
- u32 myid = id;
- int i;
-
- /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
- 64 bit comparations. So, on that architecture, with some gcc
- variants, compilation fails. Currently, the max value is 30bit wide.
- */
- BUG_ON(myid != id);
-
- for (i = 0; standards[i].std; i++)
- if (myid == standards[i].std)
- break;
- return standards[i].descr;
-}
-EXPORT_SYMBOL(v4l2_norm_to_name);
-
-/* Returns frame period for the given standard */
-void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
-{
- if (id & V4L2_STD_525_60) {
- frameperiod->numerator = 1001;
- frameperiod->denominator = 30000;
- } else {
- frameperiod->numerator = 1;
- frameperiod->denominator = 25;
- }
-}
-EXPORT_SYMBOL(v4l2_video_std_frame_period);
-
-/* Fill in the fields of a v4l2_standard structure according to the
- 'id' and 'transmission' parameters. Returns negative on error. */
-int v4l2_video_std_construct(struct v4l2_standard *vs,
- int id, const char *name)
-{
- vs->id = id;
- v4l2_video_std_frame_period(id, &vs->frameperiod);
- vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
- strlcpy(vs->name, name, sizeof(vs->name));
- return 0;
-}
-EXPORT_SYMBOL(v4l2_video_std_construct);
-
-/* ----------------------------------------------------------------- */
-/* some arrays for pretty-printing debug messages of enum types */
-
-const char *v4l2_field_names[] = {
- [V4L2_FIELD_ANY] = "any",
- [V4L2_FIELD_NONE] = "none",
- [V4L2_FIELD_TOP] = "top",
- [V4L2_FIELD_BOTTOM] = "bottom",
- [V4L2_FIELD_INTERLACED] = "interlaced",
- [V4L2_FIELD_SEQ_TB] = "seq-tb",
- [V4L2_FIELD_SEQ_BT] = "seq-bt",
- [V4L2_FIELD_ALTERNATE] = "alternate",
- [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
- [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
-};
-EXPORT_SYMBOL(v4l2_field_names);
-
-const char *v4l2_type_names[] = {
- [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap",
- [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay",
- [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out",
- [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap",
- [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out",
- [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);
-
-static const char *v4l2_memory_names[] = {
- [V4L2_MEMORY_MMAP] = "mmap",
- [V4L2_MEMORY_USERPTR] = "userptr",
- [V4L2_MEMORY_OVERLAY] = "overlay",
-};
-
-#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
- arr[a] : "unknown")
-
-/* ------------------------------------------------------------------ */
-/* debug help functions */
-
-static void v4l_print_querycap(const void *arg, bool write_only)
-{
- const struct v4l2_capability *p = arg;
-
- pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
- "capabilities=0x%08x, device_caps=0x%08x\n",
- p->driver, p->card, p->bus_info,
- p->version, p->capabilities, p->device_caps);
-}
-
-static void v4l_print_enuminput(const void *arg, bool write_only)
-{
- const struct v4l2_input *p = arg;
-
- pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
- "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
- p->index, p->name, p->type, p->audioset, p->tuner,
- (unsigned long long)p->std, p->status, p->capabilities);
-}
-
-static void v4l_print_enumoutput(const void *arg, bool write_only)
-{
- const struct v4l2_output *p = arg;
-
- pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
- "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
- p->index, p->name, p->type, p->audioset, p->modulator,
- (unsigned long long)p->std, p->capabilities);
-}
-
-static void v4l_print_audio(const void *arg, bool write_only)
-{
- const struct v4l2_audio *p = arg;
-
- if (write_only)
- pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
- else
- pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
- p->index, p->name, p->capability, p->mode);
-}
-
-static void v4l_print_audioout(const void *arg, bool write_only)
-{
- const struct v4l2_audioout *p = arg;
-
- if (write_only)
- pr_cont("index=%u\n", p->index);
- else
- pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
- p->index, p->name, p->capability, p->mode);
-}
-
-static void v4l_print_fmtdesc(const void *arg, bool write_only)
-{
- const struct v4l2_fmtdesc *p = arg;
-
- pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
- p->index, prt_names(p->type, v4l2_type_names),
- p->flags, (p->pixelformat & 0xff),
- (p->pixelformat >> 8) & 0xff,
- (p->pixelformat >> 16) & 0xff,
- (p->pixelformat >> 24) & 0xff,
- p->description);
-}
-
-static void v4l_print_format(const void *arg, bool write_only)
-{
- const struct v4l2_format *p = arg;
- const struct v4l2_pix_format *pix;
- const struct v4l2_pix_format_mplane *mp;
- const struct v4l2_vbi_format *vbi;
- const struct v4l2_sliced_vbi_format *sliced;
- const struct v4l2_window *win;
- const struct v4l2_clip *clip;
- unsigned i;
-
- pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- pix = &p->fmt.pix;
- pr_cont(", width=%u, height=%u, "
- "pixelformat=%c%c%c%c, field=%s, "
- "bytesperline=%u sizeimage=%u, colorspace=%d\n",
- pix->width, pix->height,
- (pix->pixelformat & 0xff),
- (pix->pixelformat >> 8) & 0xff,
- (pix->pixelformat >> 16) & 0xff,
- (pix->pixelformat >> 24) & 0xff,
- prt_names(pix->field, v4l2_field_names),
- pix->bytesperline, pix->sizeimage,
- pix->colorspace);
- break;
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- mp = &p->fmt.pix_mp;
- pr_cont(", width=%u, height=%u, "
- "format=%c%c%c%c, field=%s, "
- "colorspace=%d, num_planes=%u\n",
- mp->width, mp->height,
- (mp->pixelformat & 0xff),
- (mp->pixelformat >> 8) & 0xff,
- (mp->pixelformat >> 16) & 0xff,
- (mp->pixelformat >> 24) & 0xff,
- prt_names(mp->field, v4l2_field_names),
- mp->colorspace, mp->num_planes);
- for (i = 0; i < mp->num_planes; i++)
- printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
- mp->plane_fmt[i].bytesperline,
- mp->plane_fmt[i].sizeimage);
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- win = &p->fmt.win;
- pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
- "chromakey=0x%08x, bitmap=%p, "
- "global_alpha=0x%02x\n",
- win->w.width, win->w.height,
- win->w.left, win->w.top,
- prt_names(win->field, v4l2_field_names),
- win->chromakey, win->bitmap, win->global_alpha);
- clip = win->clips;
- for (i = 0; i < win->clipcount; i++) {
- printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
- i, clip->c.width, clip->c.height,
- clip->c.left, clip->c.top);
- clip = clip->next;
- }
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- vbi = &p->fmt.vbi;
- pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
- "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
- vbi->sampling_rate, vbi->offset,
- vbi->samples_per_line,
- (vbi->sample_format & 0xff),
- (vbi->sample_format >> 8) & 0xff,
- (vbi->sample_format >> 16) & 0xff,
- (vbi->sample_format >> 24) & 0xff,
- vbi->start[0], vbi->start[1],
- vbi->count[0], vbi->count[1]);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- sliced = &p->fmt.sliced;
- pr_cont(", service_set=0x%08x, io_size=%d\n",
- sliced->service_set, sliced->io_size);
- for (i = 0; i < 24; i++)
- printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
- sliced->service_lines[0][i],
- sliced->service_lines[1][i]);
- break;
- case V4L2_BUF_TYPE_PRIVATE:
- pr_cont("\n");
- break;
- }
-}
-
-static void v4l_print_framebuffer(const void *arg, bool write_only)
-{
- const struct v4l2_framebuffer *p = arg;
-
- pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
- "height=%u, pixelformat=%c%c%c%c, "
- "bytesperline=%u sizeimage=%u, colorspace=%d\n",
- p->capability, p->flags, p->base,
- p->fmt.width, p->fmt.height,
- (p->fmt.pixelformat & 0xff),
- (p->fmt.pixelformat >> 8) & 0xff,
- (p->fmt.pixelformat >> 16) & 0xff,
- (p->fmt.pixelformat >> 24) & 0xff,
- p->fmt.bytesperline, p->fmt.sizeimage,
- p->fmt.colorspace);
-}
-
-static void v4l_print_buftype(const void *arg, bool write_only)
-{
- pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
-}
-
-static void v4l_print_modulator(const void *arg, bool write_only)
-{
- const struct v4l2_modulator *p = arg;
-
- if (write_only)
- pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
- else
- pr_cont("index=%u, name=%s, capability=0x%x, "
- "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
- p->index, p->name, p->capability,
- p->rangelow, p->rangehigh, p->txsubchans);
-}
-
-static void v4l_print_tuner(const void *arg, bool write_only)
-{
- const struct v4l2_tuner *p = arg;
-
- if (write_only)
- pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
- else
- pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
- "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
- "rxsubchans=0x%x, audmode=%u\n",
- p->index, p->name, p->type,
- p->capability, p->rangelow,
- p->rangehigh, p->signal, p->afc,
- p->rxsubchans, p->audmode);
-}
-
-static void v4l_print_frequency(const void *arg, bool write_only)
-{
- const struct v4l2_frequency *p = arg;
-
- pr_cont("tuner=%u, type=%u, frequency=%u\n",
- p->tuner, p->type, p->frequency);
-}
-
-static void v4l_print_standard(const void *arg, bool write_only)
-{
- const struct v4l2_standard *p = arg;
-
- pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
- "framelines=%u\n", p->index,
- (unsigned long long)p->id, p->name,
- p->frameperiod.numerator,
- p->frameperiod.denominator,
- p->framelines);
-}
-
-static void v4l_print_std(const void *arg, bool write_only)
-{
- pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
-}
-
-static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
-{
- const struct v4l2_hw_freq_seek *p = arg;
-
- pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u, "
- "rangelow=%u, rangehigh=%u\n",
- p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing,
- p->rangelow, p->rangehigh);
-}
-
-static void v4l_print_requestbuffers(const void *arg, bool write_only)
-{
- const struct v4l2_requestbuffers *p = arg;
-
- pr_cont("count=%d, type=%s, memory=%s\n",
- p->count,
- prt_names(p->type, v4l2_type_names),
- prt_names(p->memory, v4l2_memory_names));
-}
-
-static void v4l_print_buffer(const void *arg, bool write_only)
-{
- const struct v4l2_buffer *p = arg;
- const struct v4l2_timecode *tc = &p->timecode;
- const struct v4l2_plane *plane;
- int i;
-
- pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
- "flags=0x%08x, field=%s, sequence=%d, memory=%s",
- 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->flags, prt_names(p->field, v4l2_field_names),
- p->sequence, prt_names(p->memory, v4l2_memory_names));
-
- if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
- pr_cont("\n");
- for (i = 0; i < p->length; ++i) {
- plane = &p->m.planes[i];
- printk(KERN_DEBUG
- "plane %d: bytesused=%d, data_offset=0x%08x "
- "offset/userptr=0x%lx, length=%d\n",
- i, plane->bytesused, plane->data_offset,
- plane->m.userptr, plane->length);
- }
- } else {
- pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
- p->bytesused, p->m.userptr, p->length);
- }
-
- printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
- "flags=0x%08x, frames=%d, userbits=0x%08x\n",
- tc->hours, tc->minutes, tc->seconds,
- tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
-}
-
-static void v4l_print_create_buffers(const void *arg, bool write_only)
-{
- const struct v4l2_create_buffers *p = arg;
-
- pr_cont("index=%d, count=%d, memory=%s, ",
- p->index, p->count,
- prt_names(p->memory, v4l2_memory_names));
- v4l_print_format(&p->format, write_only);
-}
-
-static void v4l_print_streamparm(const void *arg, bool write_only)
-{
- const struct v4l2_streamparm *p = arg;
-
- pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
-
- if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- const struct v4l2_captureparm *c = &p->parm.capture;
-
- pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
- "extendedmode=%d, readbuffers=%d\n",
- c->capability, c->capturemode,
- c->timeperframe.numerator, c->timeperframe.denominator,
- c->extendedmode, c->readbuffers);
- } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- const struct v4l2_outputparm *c = &p->parm.output;
-
- pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
- "extendedmode=%d, writebuffers=%d\n",
- c->capability, c->outputmode,
- c->timeperframe.numerator, c->timeperframe.denominator,
- c->extendedmode, c->writebuffers);
- }
-}
-
-static void v4l_print_queryctrl(const void *arg, bool write_only)
-{
- const struct v4l2_queryctrl *p = arg;
-
- pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
- "step=%d, default=%d, flags=0x%08x\n",
- p->id, p->type, p->name,
- p->minimum, p->maximum,
- p->step, p->default_value, p->flags);
-}
-
-static void v4l_print_querymenu(const void *arg, bool write_only)
-{
- const struct v4l2_querymenu *p = arg;
-
- pr_cont("id=0x%x, index=%d\n", p->id, p->index);
-}
-
-static void v4l_print_control(const void *arg, bool write_only)
-{
- const struct v4l2_control *p = arg;
-
- pr_cont("id=0x%x, value=%d\n", p->id, p->value);
-}
-
-static void v4l_print_ext_controls(const void *arg, bool write_only)
-{
- const struct v4l2_ext_controls *p = arg;
- int i;
-
- pr_cont("class=0x%x, count=%d, error_idx=%d",
- p->ctrl_class, p->count, p->error_idx);
- for (i = 0; i < p->count; i++) {
- if (p->controls[i].size)
- pr_cont(", id/val=0x%x/0x%x",
- p->controls[i].id, p->controls[i].value);
- else
- pr_cont(", id/size=0x%x/%u",
- p->controls[i].id, p->controls[i].size);
- }
- pr_cont("\n");
-}
-
-static void v4l_print_cropcap(const void *arg, bool write_only)
-{
- const struct v4l2_cropcap *p = arg;
-
- pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
- "defrect wxh=%dx%d, x,y=%d,%d\n, "
- "pixelaspect %d/%d\n",
- prt_names(p->type, v4l2_type_names),
- p->bounds.width, p->bounds.height,
- p->bounds.left, p->bounds.top,
- p->defrect.width, p->defrect.height,
- p->defrect.left, p->defrect.top,
- p->pixelaspect.numerator, p->pixelaspect.denominator);
-}
-
-static void v4l_print_crop(const void *arg, bool write_only)
-{
- const struct v4l2_crop *p = arg;
-
- pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
- prt_names(p->type, v4l2_type_names),
- p->c.width, p->c.height,
- p->c.left, p->c.top);
-}
-
-static void v4l_print_selection(const void *arg, bool write_only)
-{
- const struct v4l2_selection *p = arg;
-
- pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
- prt_names(p->type, v4l2_type_names),
- p->target, p->flags,
- p->r.width, p->r.height, p->r.left, p->r.top);
-}
-
-static void v4l_print_jpegcompression(const void *arg, bool write_only)
-{
- const struct v4l2_jpegcompression *p = arg;
-
- pr_cont("quality=%d, APPn=%d, APP_len=%d, "
- "COM_len=%d, jpeg_markers=0x%x\n",
- p->quality, p->APPn, p->APP_len,
- p->COM_len, p->jpeg_markers);
-}
-
-static void v4l_print_enc_idx(const void *arg, bool write_only)
-{
- const struct v4l2_enc_idx *p = arg;
-
- pr_cont("entries=%d, entries_cap=%d\n",
- p->entries, p->entries_cap);
-}
-
-static void v4l_print_encoder_cmd(const void *arg, bool write_only)
-{
- const struct v4l2_encoder_cmd *p = arg;
-
- pr_cont("cmd=%d, flags=0x%x\n",
- p->cmd, p->flags);
-}
-
-static void v4l_print_decoder_cmd(const void *arg, bool write_only)
-{
- const struct v4l2_decoder_cmd *p = arg;
-
- pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
-
- if (p->cmd == V4L2_DEC_CMD_START)
- pr_info("speed=%d, format=%u\n",
- p->start.speed, p->start.format);
- else if (p->cmd == V4L2_DEC_CMD_STOP)
- pr_info("pts=%llu\n", p->stop.pts);
-}
-
-static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
-{
- const struct v4l2_dbg_chip_ident *p = arg;
-
- pr_cont("type=%u, ", p->match.type);
- if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%s, ", p->match.name);
- else
- pr_cont("addr=%u, ", p->match.addr);
- pr_cont("chip_ident=%u, revision=0x%x\n",
- p->ident, p->revision);
-}
-
-static void v4l_print_dbg_register(const void *arg, bool write_only)
-{
- const struct v4l2_dbg_register *p = arg;
-
- pr_cont("type=%u, ", p->match.type);
- if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
- pr_cont("name=%s, ", p->match.name);
- else
- pr_cont("addr=%u, ", p->match.addr);
- pr_cont("reg=0x%llx, val=0x%llx\n",
- p->reg, p->val);
-}
-
-static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
-{
- const struct v4l2_dv_enum_preset *p = arg;
-
- pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
- p->index, p->preset, p->name, p->width, p->height);
-}
-
-static void v4l_print_dv_preset(const void *arg, bool write_only)
-{
- const struct v4l2_dv_preset *p = arg;
-
- pr_cont("preset=%u\n", p->preset);
-}
-
-static void v4l_print_dv_timings(const void *arg, bool write_only)
-{
- const struct v4l2_dv_timings *p = arg;
-
- switch (p->type) {
- case V4L2_DV_BT_656_1120:
- pr_cont("type=bt-656/1120, interlaced=%u, "
- "pixelclock=%llu, "
- "width=%u, height=%u, polarities=0x%x, "
- "hfrontporch=%u, hsync=%u, "
- "hbackporch=%u, vfrontporch=%u, "
- "vsync=%u, vbackporch=%u, "
- "il_vfrontporch=%u, il_vsync=%u, "
- "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
- p->bt.interlaced, p->bt.pixelclock,
- p->bt.width, p->bt.height,
- p->bt.polarities, p->bt.hfrontporch,
- p->bt.hsync, p->bt.hbackporch,
- p->bt.vfrontporch, p->bt.vsync,
- p->bt.vbackporch, p->bt.il_vfrontporch,
- p->bt.il_vsync, p->bt.il_vbackporch,
- p->bt.standards, p->bt.flags);
- break;
- default:
- pr_cont("type=%d\n", p->type);
- break;
- }
-}
-
-static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
-{
- const struct v4l2_enum_dv_timings *p = arg;
-
- pr_cont("index=%u, ", p->index);
- v4l_print_dv_timings(&p->timings, write_only);
-}
-
-static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
-{
- const struct v4l2_dv_timings_cap *p = arg;
-
- switch (p->type) {
- case V4L2_DV_BT_656_1120:
- pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
- "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
- p->bt.min_width, p->bt.max_width,
- p->bt.min_height, p->bt.max_height,
- p->bt.min_pixelclock, p->bt.max_pixelclock,
- p->bt.standards, p->bt.capabilities);
- break;
- default:
- pr_cont("type=%u\n", p->type);
- break;
- }
-}
-
-static void v4l_print_frmsizeenum(const void *arg, bool write_only)
-{
- const struct v4l2_frmsizeenum *p = arg;
-
- pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
- p->index,
- (p->pixel_format & 0xff),
- (p->pixel_format >> 8) & 0xff,
- (p->pixel_format >> 16) & 0xff,
- (p->pixel_format >> 24) & 0xff,
- p->type);
- switch (p->type) {
- case V4L2_FRMSIZE_TYPE_DISCRETE:
- pr_cont(" wxh=%ux%u\n",
- p->discrete.width, p->discrete.height);
- break;
- case V4L2_FRMSIZE_TYPE_STEPWISE:
- pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
- p->stepwise.min_width, p->stepwise.min_height,
- p->stepwise.step_width, p->stepwise.step_height,
- p->stepwise.max_width, p->stepwise.max_height);
- break;
- case V4L2_FRMSIZE_TYPE_CONTINUOUS:
- /* fall through */
- default:
- pr_cont("\n");
- break;
- }
-}
-
-static void v4l_print_frmivalenum(const void *arg, bool write_only)
-{
- const struct v4l2_frmivalenum *p = arg;
-
- pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
- p->index,
- (p->pixel_format & 0xff),
- (p->pixel_format >> 8) & 0xff,
- (p->pixel_format >> 16) & 0xff,
- (p->pixel_format >> 24) & 0xff,
- p->width, p->height, p->type);
- switch (p->type) {
- case V4L2_FRMIVAL_TYPE_DISCRETE:
- pr_cont(" fps=%d/%d\n",
- p->discrete.numerator,
- p->discrete.denominator);
- break;
- case V4L2_FRMIVAL_TYPE_STEPWISE:
- pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
- p->stepwise.min.numerator,
- p->stepwise.min.denominator,
- p->stepwise.max.numerator,
- p->stepwise.max.denominator,
- p->stepwise.step.numerator,
- p->stepwise.step.denominator);
- break;
- case V4L2_FRMIVAL_TYPE_CONTINUOUS:
- /* fall through */
- default:
- pr_cont("\n");
- break;
- }
-}
-
-static void v4l_print_event(const void *arg, bool write_only)
-{
- const struct v4l2_event *p = arg;
- const struct v4l2_event_ctrl *c;
-
- pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, "
- "timestamp=%lu.%9.9lu\n",
- p->type, p->pending, p->sequence, p->id,
- p->timestamp.tv_sec, p->timestamp.tv_nsec);
- switch (p->type) {
- case V4L2_EVENT_VSYNC:
- printk(KERN_DEBUG "field=%s\n",
- prt_names(p->u.vsync.field, v4l2_field_names));
- break;
- case V4L2_EVENT_CTRL:
- c = &p->u.ctrl;
- printk(KERN_DEBUG "changes=0x%x, type=%u, ",
- c->changes, c->type);
- if (c->type == V4L2_CTRL_TYPE_INTEGER64)
- pr_cont("value64=%lld, ", c->value64);
- else
- pr_cont("value=%d, ", c->value);
- pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
- " default_value=%d\n",
- c->flags, c->minimum, c->maximum,
- c->step, c->default_value);
- break;
- case V4L2_EVENT_FRAME_SYNC:
- pr_cont("frame_sequence=%u\n",
- p->u.frame_sync.frame_sequence);
- break;
- }
-}
-
-static void v4l_print_event_subscription(const void *arg, bool write_only)
-{
- const struct v4l2_event_subscription *p = arg;
-
- pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
- p->type, p->id, p->flags);
-}
-
-static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
-{
- const struct v4l2_sliced_vbi_cap *p = arg;
- int i;
-
- pr_cont("type=%s, service_set=0x%08x\n",
- prt_names(p->type, v4l2_type_names), p->service_set);
- for (i = 0; i < 24; i++)
- printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
- p->service_lines[0][i],
- p->service_lines[1][i]);
-}
-
-static void v4l_print_freq_band(const void *arg, bool write_only)
-{
- const struct v4l2_frequency_band *p = arg;
-
- pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
- "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
- p->tuner, p->type, p->index,
- p->capability, p->rangelow,
- p->rangehigh, p->modulation);
-}
-
-static void v4l_print_u32(const void *arg, bool write_only)
-{
- pr_cont("value=%u\n", *(const u32 *)arg);
-}
-
-static void v4l_print_newline(const void *arg, bool write_only)
-{
- pr_cont("\n");
-}
-
-static void v4l_print_default(const void *arg, bool write_only)
-{
- pr_cont("driver-specific ioctl\n");
-}
-
-static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
-{
- __u32 i;
-
- /* zero the reserved fields */
- c->reserved[0] = c->reserved[1] = 0;
- for (i = 0; i < c->count; i++)
- c->controls[i].reserved2[0] = 0;
-
- /* V4L2_CID_PRIVATE_BASE cannot be used as control class
- when using extended controls.
- Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
- is it allowed for backwards compatibility.
- */
- if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
- return 0;
- /* Check that all controls are from the same control class. */
- for (i = 0; i < c->count; i++) {
- if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
- c->error_idx = i;
- return 0;
- }
- }
- return 1;
-}
-
-static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
-{
- if (ops == NULL)
- return -EINVAL;
-
- switch (type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- 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:
- if (ops->vidioc_g_fmt_vid_overlay)
- return 0;
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- 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:
- if (ops->vidioc_g_fmt_vid_out_overlay)
- return 0;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_vbi_cap)
- return 0;
- break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_vbi_out)
- return 0;
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (ops->vidioc_g_fmt_sliced_vbi_cap)
- return 0;
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (ops->vidioc_g_fmt_sliced_vbi_out)
- return 0;
- break;
- case V4L2_BUF_TYPE_PRIVATE:
- if (ops->vidioc_g_fmt_type_private)
- return 0;
- break;
- }
- return -EINVAL;
-}
-
-static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_capability *cap = (struct v4l2_capability *)arg;
-
- cap->version = LINUX_VERSION_CODE;
- return ops->vidioc_querycap(file, fh, cap);
-}
-
-static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd;
- u32 *p = arg;
-
- if (ops->vidioc_g_priority)
- return ops->vidioc_g_priority(file, fh, arg);
- vfd = video_devdata(file);
- *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
- return 0;
-}
-
-static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd;
- struct v4l2_fh *vfh;
- u32 *p = arg;
-
- if (ops->vidioc_s_priority)
- return ops->vidioc_s_priority(file, fh, *p);
- vfd = video_devdata(file);
- vfh = file->private_data;
- return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
-}
-
-static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_input *p = arg;
-
- /*
- * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
- * CAP_STD here based on ioctl handler provided by the
- * driver. If the driver doesn't support these
- * for a specific input, it must override these flags.
- */
- if (ops->vidioc_s_std)
- p->capabilities |= V4L2_IN_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_IN_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
-
- return ops->vidioc_enum_input(file, fh, p);
-}
-
-static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_output *p = arg;
-
- /*
- * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
- * CAP_STD here based on ioctl handler provided by the
- * driver. If the driver doesn't support these
- * for a specific output, it must override these flags.
- */
- if (ops->vidioc_s_std)
- p->capabilities |= V4L2_OUT_CAP_STD;
- if (ops->vidioc_s_dv_preset)
- p->capabilities |= V4L2_OUT_CAP_PRESETS;
- if (ops->vidioc_s_dv_timings)
- p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
-
- return ops->vidioc_enum_output(file, fh, p);
-}
-
-static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_fmtdesc *p = arg;
-
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
- break;
- return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
- break;
- return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
- break;
- return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (unlikely(!ops->vidioc_enum_fmt_vid_out))
- break;
- return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
- break;
- return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_PRIVATE:
- if (unlikely(!ops->vidioc_enum_fmt_type_private))
- break;
- return ops->vidioc_enum_fmt_type_private(file, fh, arg);
- }
- return -EINVAL;
-}
-
-static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_format *p = arg;
-
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (unlikely(!ops->vidioc_g_fmt_vid_cap))
- break;
- return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
- break;
- return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
- break;
- return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (unlikely(!ops->vidioc_g_fmt_vid_out))
- break;
- return ops->vidioc_g_fmt_vid_out(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
- break;
- return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
- break;
- return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
- break;
- return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_g_fmt_vbi_out))
- break;
- return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
- break;
- return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
- break;
- return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_PRIVATE:
- if (unlikely(!ops->vidioc_g_fmt_type_private))
- break;
- return ops->vidioc_g_fmt_type_private(file, fh, arg);
- }
- return -EINVAL;
-}
-
-static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_format *p = arg;
-
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (unlikely(!ops->vidioc_s_fmt_vid_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp);
- return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
- break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (unlikely(!ops->vidioc_s_fmt_vid_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- return ops->vidioc_s_fmt_vid_out(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp);
- return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
- break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.vbi);
- return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_s_fmt_vbi_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.vbi);
- return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.sliced);
- return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.sliced);
- return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_PRIVATE:
- if (unlikely(!ops->vidioc_s_fmt_type_private))
- break;
- return ops->vidioc_s_fmt_type_private(file, fh, arg);
- }
- return -EINVAL;
-}
-
-static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_format *p = arg;
-
- switch (p->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (unlikely(!ops->vidioc_try_fmt_vid_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp);
- return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
- break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (unlikely(!ops->vidioc_try_fmt_vid_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix);
- return ops->vidioc_try_fmt_vid_out(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
- break;
- CLEAR_AFTER_FIELD(p, fmt.pix_mp);
- return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
- break;
- CLEAR_AFTER_FIELD(p, fmt.win);
- return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.vbi);
- return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_try_fmt_vbi_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.vbi);
- return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
- break;
- CLEAR_AFTER_FIELD(p, fmt.sliced);
- return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
- break;
- CLEAR_AFTER_FIELD(p, fmt.sliced);
- return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
- case V4L2_BUF_TYPE_PRIVATE:
- if (unlikely(!ops->vidioc_try_fmt_type_private))
- break;
- return ops->vidioc_try_fmt_type_private(file, fh, arg);
- }
- return -EINVAL;
-}
-
-static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_tuner *p = arg;
- int err;
-
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- err = ops->vidioc_g_tuner(file, fh, p);
- if (!err)
- p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
- return err;
-}
-
-static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_tuner *p = arg;
-
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- return ops->vidioc_s_tuner(file, fh, p);
-}
-
-static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_modulator *p = arg;
- int err;
-
- err = ops->vidioc_g_modulator(file, fh, p);
- if (!err)
- p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
- return err;
-}
-
-static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_frequency *p = arg;
-
- p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- return ops->vidioc_g_frequency(file, fh, p);
-}
-
-static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_frequency *p = arg;
- enum v4l2_tuner_type type;
-
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- if (p->type != type)
- return -EINVAL;
- return ops->vidioc_s_frequency(file, fh, p);
-}
-
-static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_standard *p = arg;
- v4l2_std_id id = vfd->tvnorms, curr_id = 0;
- unsigned int index = p->index, i, j = 0;
- const char *descr = "";
-
- /* Return norm array in a canonical way */
- for (i = 0; i <= index && id; i++) {
- /* last std value in the standards array is 0, so this
- while always ends there since (id & 0) == 0. */
- while ((id & standards[j].std) != standards[j].std)
- j++;
- curr_id = standards[j].std;
- descr = standards[j].descr;
- j++;
- if (curr_id == 0)
- break;
- if (curr_id != V4L2_STD_PAL &&
- curr_id != V4L2_STD_SECAM &&
- curr_id != V4L2_STD_NTSC)
- id &= ~curr_id;
- }
- if (i <= index)
- return -EINVAL;
-
- v4l2_video_std_construct(p, curr_id, descr);
- return 0;
-}
-
-static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- v4l2_std_id *id = arg;
-
- /* Calls the specific handler */
- if (ops->vidioc_g_std)
- return ops->vidioc_g_std(file, fh, arg);
- if (vfd->current_norm) {
- *id = vfd->current_norm;
- return 0;
- }
- return -ENOTTY;
-}
-
-static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- v4l2_std_id *id = arg, norm;
- int ret;
-
- norm = (*id) & vfd->tvnorms;
- if (vfd->tvnorms && !norm) /* Check if std is supported */
- return -EINVAL;
-
- /* Calls the specific handler */
- ret = ops->vidioc_s_std(file, fh, &norm);
-
- /* Updates standard information */
- if (ret >= 0)
- vfd->current_norm = norm;
- return ret;
-}
-
-static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- v4l2_std_id *p = arg;
-
- /*
- * If nothing detected, it should return all supported
- * standard.
- * Drivers just need to mask the std argument, in order
- * to remove the standards that don't apply from the mask.
- * This means that tuners, audio and video decoders can join
- * their efforts to improve the standards detection.
- */
- *p = vfd->tvnorms;
- return ops->vidioc_querystd(file, fh, arg);
-}
-
-static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_hw_freq_seek *p = arg;
- enum v4l2_tuner_type type;
-
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
- if (p->type != type)
- return -EINVAL;
- return ops->vidioc_s_hw_freq_seek(file, fh, p);
-}
-
-static int v4l_overlay(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_overlay(file, fh, *(unsigned int *)arg);
-}
-
-static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_requestbuffers *p = arg;
- int ret = check_fmt(ops, p->type);
-
- if (ret)
- return ret;
-
- if (p->type < V4L2_BUF_TYPE_PRIVATE)
- CLEAR_AFTER_FIELD(p, memory);
-
- return ops->vidioc_reqbufs(file, fh, p);
-}
-
-static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_buffer *p = arg;
- int ret = check_fmt(ops, p->type);
-
- return ret ? ret : ops->vidioc_querybuf(file, fh, p);
-}
-
-static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_buffer *p = arg;
- int ret = check_fmt(ops, p->type);
-
- return ret ? ret : ops->vidioc_qbuf(file, fh, p);
-}
-
-static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_buffer *p = arg;
- int ret = check_fmt(ops, p->type);
-
- return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
-}
-
-static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_create_buffers *create = arg;
- int ret = check_fmt(ops, create->format.type);
-
- return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
-}
-
-static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_buffer *b = arg;
- int ret = check_fmt(ops, b->type);
-
- return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
-}
-
-static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_streamparm *p = arg;
- v4l2_std_id std;
- int ret = check_fmt(ops, p->type);
-
- if (ret)
- return ret;
- if (ops->vidioc_g_parm)
- return ops->vidioc_g_parm(file, fh, p);
- std = vfd->current_norm;
- if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
- p->parm.capture.readbuffers = 2;
- if (ops->vidioc_g_std)
- ret = ops->vidioc_g_std(file, fh, &std);
- if (ret == 0)
- v4l2_video_std_frame_period(std,
- &p->parm.capture.timeperframe);
- return ret;
-}
-
-static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_streamparm *p = arg;
- int ret = check_fmt(ops, p->type);
-
- return ret ? ret : ops->vidioc_s_parm(file, fh, p);
-}
-
-static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_queryctrl *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
- if (vfh && vfh->ctrl_handler)
- return v4l2_queryctrl(vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_queryctrl(vfd->ctrl_handler, p);
- if (ops->vidioc_queryctrl)
- return ops->vidioc_queryctrl(file, fh, p);
- return -ENOTTY;
-}
-
-static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_querymenu *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
- if (vfh && vfh->ctrl_handler)
- return v4l2_querymenu(vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_querymenu(vfd->ctrl_handler, p);
- if (ops->vidioc_querymenu)
- return ops->vidioc_querymenu(file, fh, p);
- return -ENOTTY;
-}
-
-static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_control *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
- struct v4l2_ext_controls ctrls;
- struct v4l2_ext_control ctrl;
-
- if (vfh && vfh->ctrl_handler)
- return v4l2_g_ctrl(vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_g_ctrl(vfd->ctrl_handler, p);
- if (ops->vidioc_g_ctrl)
- return ops->vidioc_g_ctrl(file, fh, p);
- if (ops->vidioc_g_ext_ctrls == NULL)
- return -ENOTTY;
-
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
- ctrls.count = 1;
- ctrls.controls = &ctrl;
- ctrl.id = p->id;
- ctrl.value = p->value;
- if (check_ext_ctrls(&ctrls, 1)) {
- int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
-
- if (ret == 0)
- p->value = ctrl.value;
- return ret;
- }
- return -EINVAL;
-}
-
-static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_control *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
- struct v4l2_ext_controls ctrls;
- struct v4l2_ext_control ctrl;
-
- if (vfh && vfh->ctrl_handler)
- return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
- if (ops->vidioc_s_ctrl)
- return ops->vidioc_s_ctrl(file, fh, p);
- if (ops->vidioc_s_ext_ctrls == NULL)
- return -ENOTTY;
-
- ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
- ctrls.count = 1;
- ctrls.controls = &ctrl;
- ctrl.id = p->id;
- ctrl.value = p->value;
- if (check_ext_ctrls(&ctrls, 1))
- return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
- return -EINVAL;
-}
-
-static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
- p->error_idx = p->count;
- if (vfh && vfh->ctrl_handler)
- return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
- if (ops->vidioc_g_ext_ctrls == NULL)
- return -ENOTTY;
- return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
- -EINVAL;
-}
-
-static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
- p->error_idx = p->count;
- if (vfh && vfh->ctrl_handler)
- return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
- if (ops->vidioc_s_ext_ctrls == NULL)
- return -ENOTTY;
- return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
- -EINVAL;
-}
-
-static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_ext_controls *p = arg;
- struct v4l2_fh *vfh =
- test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
-
- p->error_idx = p->count;
- if (vfh && vfh->ctrl_handler)
- return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
- if (vfd->ctrl_handler)
- return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
- if (ops->vidioc_try_ext_ctrls == NULL)
- return -ENOTTY;
- return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
- -EINVAL;
-}
-
-static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_crop *p = arg;
- struct v4l2_selection s = {
- .type = p->type,
- };
- int ret;
-
- if (ops->vidioc_g_crop)
- return ops->vidioc_g_crop(file, fh, p);
- /* simulate capture crop using selection api */
-
- /* crop means compose for output devices */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
- else
- s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
- ret = ops->vidioc_g_selection(file, fh, &s);
-
- /* copying results to old structure on success */
- if (!ret)
- p->c = s.r;
- return ret;
-}
-
-static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_crop *p = arg;
- struct v4l2_selection s = {
- .type = p->type,
- .r = p->c,
- };
-
- if (ops->vidioc_s_crop)
- return ops->vidioc_s_crop(file, fh, p);
- /* simulate capture crop using selection api */
-
- /* crop means compose for output devices */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
- else
- s.target = V4L2_SEL_TGT_CROP_ACTIVE;
-
- return ops->vidioc_s_selection(file, fh, &s);
-}
-
-static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_cropcap *p = arg;
- struct v4l2_selection s = { .type = p->type };
- int ret;
-
- if (ops->vidioc_cropcap)
- return ops->vidioc_cropcap(file, fh, p);
-
- /* obtaining bounds */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
- else
- s.target = V4L2_SEL_TGT_CROP_BOUNDS;
-
- ret = ops->vidioc_g_selection(file, fh, &s);
- if (ret)
- return ret;
- p->bounds = s.r;
-
- /* obtaining defrect */
- if (V4L2_TYPE_IS_OUTPUT(p->type))
- s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
- else
- s.target = V4L2_SEL_TGT_CROP_DEFAULT;
-
- ret = ops->vidioc_g_selection(file, fh, &s);
- if (ret)
- return ret;
- p->defrect = s.r;
-
- /* setting trivial pixelaspect */
- p->pixelaspect.numerator = 1;
- p->pixelaspect.denominator = 1;
- return 0;
-}
-
-static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- int ret;
-
- if (vfd->v4l2_dev)
- pr_info("%s: ================= START STATUS =================\n",
- vfd->v4l2_dev->name);
- ret = ops->vidioc_log_status(file, fh);
- if (vfd->v4l2_dev)
- pr_info("%s: ================== END STATUS ==================\n",
- vfd->v4l2_dev->name);
- return ret;
-}
-
-static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register *p = arg;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return ops->vidioc_g_register(file, fh, p);
-#else
- return -ENOTTY;
-#endif
-}
-
-static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- struct v4l2_dbg_register *p = arg;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return ops->vidioc_s_register(file, fh, p);
-#else
- return -ENOTTY;
-#endif
-}
-
-static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_dbg_chip_ident *p = arg;
-
- p->ident = V4L2_IDENT_NONE;
- p->revision = 0;
- return ops->vidioc_g_chip_ident(file, fh, p);
-}
-
-static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
-}
-
-static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_subscribe_event(fh, arg);
-}
-
-static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- return ops->vidioc_unsubscribe_event(fh, arg);
-}
-
-static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct v4l2_sliced_vbi_cap *p = arg;
-
- /* Clear up to type, everything after type is zeroed already */
- memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
-
- return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
-}
-
-static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *arg)
-{
- struct video_device *vfd = video_devdata(file);
- struct v4l2_frequency_band *p = arg;
- enum v4l2_tuner_type type;
- int err;
-
- type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
- V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
-
- if (type != p->type)
- return -EINVAL;
- if (ops->vidioc_enum_freq_bands)
- return ops->vidioc_enum_freq_bands(file, fh, p);
- if (ops->vidioc_g_tuner) {
- struct v4l2_tuner t = {
- .index = p->tuner,
- .type = type,
- };
-
- if (p->index)
- return -EINVAL;
- err = ops->vidioc_g_tuner(file, fh, &t);
- if (err)
- return err;
- p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
- p->rangelow = t.rangelow;
- p->rangehigh = t.rangehigh;
- p->modulation = (type == V4L2_TUNER_RADIO) ?
- V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
- return 0;
- }
- if (ops->vidioc_g_modulator) {
- struct v4l2_modulator m = {
- .index = p->tuner,
- };
-
- if (type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (p->index)
- return -EINVAL;
- err = ops->vidioc_g_modulator(file, fh, &m);
- if (err)
- return err;
- p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
- p->rangelow = m.rangelow;
- p->rangehigh = m.rangehigh;
- p->modulation = (type == V4L2_TUNER_RADIO) ?
- V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
- return 0;
- }
- return -ENOTTY;
-}
-
-struct v4l2_ioctl_info {
- unsigned int ioctl;
- u32 flags;
- const char * const name;
- union {
- u32 offset;
- int (*func)(const struct v4l2_ioctl_ops *ops,
- struct file *file, void *fh, void *p);
- } u;
- void (*debug)(const void *arg, bool write_only);
-};
-
-/* This control needs a priority check */
-#define INFO_FL_PRIO (1 << 0)
-/* This control can be valid if the filehandle passes a control handler. */
-#define INFO_FL_CTRL (1 << 1)
-/* This is a standard ioctl, no need for special code */
-#define INFO_FL_STD (1 << 2)
-/* This is ioctl has its own function */
-#define INFO_FL_FUNC (1 << 3)
-/* Queuing ioctl */
-#define INFO_FL_QUEUE (1 << 4)
-/* Zero struct from after the field to the end */
-#define INFO_FL_CLEAR(v4l2_struct, field) \
- ((offsetof(struct v4l2_struct, field) + \
- sizeof(((struct v4l2_struct *)0)->field)) << 16)
-#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
-
-#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_STD, \
- .name = #_ioctl, \
- .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \
- .debug = _debug, \
- }
-
-#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \
- [_IOC_NR(_ioctl)] = { \
- .ioctl = _ioctl, \
- .flags = _flags | INFO_FL_FUNC, \
- .name = #_ioctl, \
- .u.func = _func, \
- .debug = _debug, \
- }
-
-static struct v4l2_ioctl_info v4l2_ioctls[] = {
- IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
- IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
- IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
- IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
- IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
- IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_OVERLAY, v4l_overlay, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
- IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
- IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
- IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
- IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
- IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
- IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
- IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
- IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
- IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
- IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
- IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
- IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
- IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
- IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
- IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
- IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
- IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
- IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
- IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
- IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
- IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
- IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
- IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
- IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
- IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
- IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
- IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
- IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
- IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
- IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
- IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
- IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
- IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
- IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
- IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
- IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
- IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
- IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
- IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
- IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
- IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
- IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
- IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
- IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
- IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
- IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
- IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
-};
-#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
-
-bool v4l2_is_known_ioctl(unsigned int cmd)
-{
- if (_IOC_NR(cmd) >= V4L2_IOCTLS)
- return false;
- return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
-}
-
-struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
-{
- if (_IOC_NR(cmd) >= V4L2_IOCTLS)
- return vdev->lock;
- if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
- return NULL;
- if (vdev->queue && vdev->queue->lock &&
- (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
- return vdev->queue->lock;
- return vdev->lock;
-}
-
-/* Common ioctl debug function. This function can be used by
- external ioctl messages as well as internal V4L ioctl */
-void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
-{
- const char *dir, *type;
-
- if (prefix)
- printk(KERN_DEBUG "%s: ", prefix);
-
- switch (_IOC_TYPE(cmd)) {
- case 'd':
- type = "v4l2_int";
- break;
- case 'V':
- if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
- type = "v4l2";
- break;
- }
- pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
- return;
- default:
- type = "unknown";
- break;
- }
-
- switch (_IOC_DIR(cmd)) {
- case _IOC_NONE: dir = "--"; break;
- case _IOC_READ: dir = "r-"; break;
- case _IOC_WRITE: dir = "-w"; break;
- case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
- default: dir = "*ERR*"; break;
- }
- pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
- type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
-}
-EXPORT_SYMBOL(v4l_printk_ioctl);
-
-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;
- bool write_only = false;
- struct v4l2_ioctl_info default_info;
- const struct v4l2_ioctl_info *info;
- void *fh = file->private_data;
- struct v4l2_fh *vfh = NULL;
- int use_fh_prio = 0;
- int debug = vfd->debug;
- long ret = -ENOTTY;
-
- if (ops == NULL) {
- pr_warn("%s: has no ioctl_ops.\n",
- video_device_node_name(vfd));
- return ret;
- }
-
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
- vfh = file->private_data;
- use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
- }
-
- if (v4l2_is_known_ioctl(cmd)) {
- info = &v4l2_ioctls[_IOC_NR(cmd)];
-
- if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
- !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
- goto done;
-
- if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
- ret = v4l2_prio_check(vfd->prio, vfh->prio);
- if (ret)
- goto done;
- }
- } else {
- default_info.ioctl = cmd;
- default_info.flags = 0;
- default_info.debug = v4l_print_default;
- info = &default_info;
- }
-
- write_only = _IOC_DIR(cmd) == _IOC_WRITE;
- if (write_only && debug > V4L2_DEBUG_IOCTL) {
- v4l_printk_ioctl(video_device_node_name(vfd), cmd);
- pr_cont(": ");
- info->debug(arg, write_only);
- }
- if (info->flags & INFO_FL_STD) {
- typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
- const void *p = vfd->ioctl_ops;
- const vidioc_op *vidioc = p + info->u.offset;
-
- ret = (*vidioc)(file, fh, arg);
- } else if (info->flags & INFO_FL_FUNC) {
- ret = info->u.func(ops, file, fh, arg);
- } else if (!ops->vidioc_default) {
- ret = -ENOTTY;
- } else {
- ret = ops->vidioc_default(file, fh,
- use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
- cmd, arg);
- }
-
-done:
- if (debug) {
- if (write_only && debug > V4L2_DEBUG_IOCTL) {
- if (ret < 0)
- printk(KERN_DEBUG "%s: error %ld\n",
- video_device_node_name(vfd), ret);
- return ret;
- }
- v4l_printk_ioctl(video_device_node_name(vfd), cmd);
- if (ret < 0)
- pr_cont(": error %ld\n", ret);
- else if (debug == V4L2_DEBUG_IOCTL)
- pr_cont("\n");
- else if (_IOC_DIR(cmd) == _IOC_NONE)
- info->debug(arg, write_only);
- else {
- pr_cont(": ");
- info->debug(arg, write_only);
- }
- }
-
- return ret;
-}
-
-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) {
- if (ctrls->count > V4L2_CID_MAX_CTRLS) {
- ret = -EINVAL;
- break;
- }
- *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_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
- v4l2_kioctl func)
-{
- char sbuf[128];
- void *mbuf = NULL;
- void *parg = (void *)arg;
- long err = -EINVAL;
- bool has_array_args;
- size_t array_size = 0;
- void __user *user_ptr = NULL;
- void **kernel_ptr = NULL;
-
- /* Copy arguments into temp kernel buffer */
- if (_IOC_DIR(cmd) != _IOC_NONE) {
- if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
- parg = sbuf;
- } else {
- /* too big to allocate from stack */
- mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
- if (NULL == mbuf)
- return -ENOMEM;
- parg = mbuf;
- }
-
- err = -EFAULT;
- if (_IOC_DIR(cmd) & _IOC_WRITE) {
- unsigned int n = _IOC_SIZE(cmd);
-
- /*
- * In some cases, only a few fields are used as input,
- * i.e. when the app sets "index" and then the driver
- * fills in the rest of the structure for the thing
- * with that index. We only need to copy up the first
- * non-input field.
- */
- if (v4l2_is_known_ioctl(cmd)) {
- u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;
- if (flags & INFO_FL_CLEAR_MASK)
- n = (flags & INFO_FL_CLEAR_MASK) >> 16;
- }
-
- if (copy_from_user(parg, (void __user *)arg, n))
- goto out;
-
- /* zero out anything we don't copy from userspace */
- if (n < _IOC_SIZE(cmd))
- memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
- } else {
- /* read-only ioctl */
- memset(parg, 0, _IOC_SIZE(cmd));
- }
- }
-
- err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
- if (err < 0)
- goto out;
- has_array_args = err;
-
- 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 = func(file, cmd, parg);
- if (err == -ENOIOCTLCMD)
- err = -ENOTTY;
-
- if (has_array_args) {
- *kernel_ptr = user_ptr;
- if (copy_to_user(user_ptr, mbuf, array_size))
- err = -EFAULT;
- goto out_array_args;
- }
- /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
- results that must be returned. */
- if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
- goto out;
-
-out_array_args:
- /* Copy results into user buffer */
- switch (_IOC_DIR(cmd)) {
- case _IOC_READ:
- case (_IOC_WRITE | _IOC_READ):
- if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
- err = -EFAULT;
- break;
- }
-
-out:
- kfree(mbuf);
- return err;
-}
-EXPORT_SYMBOL(video_usercopy);
-
-long video_ioctl2(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(file, cmd, arg, __video_do_ioctl);
-}
-EXPORT_SYMBOL(video_ioctl2);
diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c
deleted file mode 100644
index 97b48318aee1..000000000000
--- a/drivers/media/video/v4l2-mem2mem.c
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Memory-to-memory device framework for Video for Linux 2 and videobuf.
- *
- * Helper functions for devices that use videobuf buffers for both their
- * source and destination.
- *
- * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
- * Pawel Osciak, <pawel@osciak.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; either version 2 of the License, or (at your
- * option) any later version.
- */
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-#include <media/videobuf2-core.h>
-#include <media/v4l2-mem2mem.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-MODULE_DESCRIPTION("Mem to mem device framework for videobuf");
-MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
-
-static bool debug;
-module_param(debug, bool, 0644);
-
-#define dprintk(fmt, arg...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\
- } while (0)
-
-
-/* Instance is already queued on the job_queue */
-#define TRANS_QUEUED (1 << 0)
-/* Instance is currently running in hardware */
-#define TRANS_RUNNING (1 << 1)
-
-
-/* Offset base for buffers on the destination queue - used to distinguish
- * between source and destination buffers when mmapping - they receive the same
- * offsets but for different queues */
-#define DST_QUEUE_OFF_BASE (1 << 30)
-
-
-/**
- * struct v4l2_m2m_dev - per-device context
- * @curr_ctx: currently running instance
- * @job_queue: instances queued to run
- * @job_spinlock: protects job_queue
- * @m2m_ops: driver callbacks
- */
-struct v4l2_m2m_dev {
- struct v4l2_m2m_ctx *curr_ctx;
-
- struct list_head job_queue;
- spinlock_t job_spinlock;
-
- struct v4l2_m2m_ops *m2m_ops;
-};
-
-static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
- enum v4l2_buf_type type)
-{
- if (V4L2_TYPE_IS_OUTPUT(type))
- return &m2m_ctx->out_q_ctx;
- else
- return &m2m_ctx->cap_q_ctx;
-}
-
-/**
- * v4l2_m2m_get_vq() - return vb2_queue for the given type
- */
-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;
-
- q_ctx = get_queue_ctx(m2m_ctx, type);
- if (!q_ctx)
- return NULL;
-
- return &q_ctx->q;
-}
-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_queue_ctx *q_ctx)
-{
- struct v4l2_m2m_buffer *b = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
-
- if (list_empty(&q_ctx->rdy_queue)) {
- spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
- return NULL;
- }
-
- b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list);
- spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
- return &b->vb;
-}
-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_queue_ctx *q_ctx)
-{
- struct v4l2_m2m_buffer *b = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- if (list_empty(&q_ctx->rdy_queue)) {
- spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
- return NULL;
- }
- 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->rdy_spinlock, flags);
-
- return &b->vb;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove);
-
-/*
- * Scheduling handlers
- */
-
-/**
- * v4l2_m2m_get_curr_priv() - return driver private data for the currently
- * running instance or NULL if no instance is running
- */
-void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev)
-{
- unsigned long flags;
- void *ret = NULL;
-
- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
- if (m2m_dev->curr_ctx)
- ret = m2m_dev->curr_ctx->priv;
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(v4l2_m2m_get_curr_priv);
-
-/**
- * v4l2_m2m_try_run() - select next job to perform and run it if possible
- *
- * Get next transaction (if present) from the waiting jobs list and run it.
- */
-static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
- if (NULL != m2m_dev->curr_ctx) {
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
- dprintk("Another instance is running, won't run now\n");
- return;
- }
-
- if (list_empty(&m2m_dev->job_queue)) {
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
- dprintk("No job pending\n");
- return;
- }
-
- m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next,
- struct v4l2_m2m_ctx, queue);
- m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING;
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
-
- m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv);
-}
-
-/**
- * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to
- * the pending job queue and add it if so.
- * @m2m_ctx: m2m context assigned to the instance to be checked
- *
- * There are three basic requirements an instance has to meet to be able to run:
- * 1) at least one source buffer has to be queued,
- * 2) at least one destination buffer has to be queued,
- * 3) streaming has to be on.
- *
- * There may also be additional, custom requirements. In such case the driver
- * should supply a custom callback (job_ready in v4l2_m2m_ops) that should
- * return 1 if the instance is ready.
- * An example of the above could be an instance that requires more than one
- * src/dst buffer per transaction.
- */
-static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx)
-{
- struct v4l2_m2m_dev *m2m_dev;
- unsigned long flags_job, flags;
-
- m2m_dev = m2m_ctx->m2m_dev;
- dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx);
-
- if (!m2m_ctx->out_q_ctx.q.streaming
- || !m2m_ctx->cap_q_ctx.q.streaming) {
- dprintk("Streaming needs to be on for both queues\n");
- return;
- }
-
- spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job);
- if (m2m_ctx->job_flags & TRANS_QUEUED) {
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
- dprintk("On job queue already\n");
- return;
- }
-
- 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.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.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.rdy_spinlock, flags);
-
- if (m2m_dev->m2m_ops->job_ready
- && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) {
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
- dprintk("Driver not ready\n");
- return;
- }
-
- list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue);
- m2m_ctx->job_flags |= TRANS_QUEUED;
-
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job);
-
- v4l2_m2m_try_run(m2m_dev);
-}
-
-/**
- * v4l2_m2m_job_finish() - inform the framework that a job has been finished
- * and have it clean up
- *
- * Called by a driver to yield back the device after it has finished with it.
- * Should be called as soon as possible after reaching a state which allows
- * other instances to take control of the device.
- *
- * This function has to be called only after device_run() callback has been
- * called on the driver. To prevent recursion, it should not be called directly
- * from the device_run() callback though.
- */
-void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev,
- struct v4l2_m2m_ctx *m2m_ctx)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
- if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) {
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
- dprintk("Called by an instance not currently running\n");
- return;
- }
-
- 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);
-
- /* This instance might have more buffers ready, but since we do not
- * allow more than one job on the job_queue per instance, each has
- * to be scheduled separately after the previous one finishes. */
- v4l2_m2m_try_schedule(m2m_ctx);
- v4l2_m2m_try_run(m2m_dev);
-}
-EXPORT_SYMBOL(v4l2_m2m_job_finish);
-
-/**
- * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer
- */
-int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct vb2_queue *vq;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type);
- return vb2_reqbufs(vq, reqbufs);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs);
-
-/**
- * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer
- *
- * See v4l2_m2m_mmap() documentation for details.
- */
-int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct v4l2_buffer *buf)
-{
- struct vb2_queue *vq;
- int ret = 0;
- unsigned int i;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- 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;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf);
-
-/**
- * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on
- * the type
- */
-int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct v4l2_buffer *buf)
-{
- struct vb2_queue *vq;
- int ret;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- ret = vb2_qbuf(vq, buf);
- if (!ret)
- v4l2_m2m_try_schedule(m2m_ctx);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf);
-
-/**
- * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on
- * the type
- */
-int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct v4l2_buffer *buf)
-{
- struct vb2_queue *vq;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, buf->type);
- return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf);
-
-/**
- * v4l2_m2m_streamon() - turn on streaming for a video queue
- */
-int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- enum v4l2_buf_type type)
-{
- struct vb2_queue *vq;
- int ret;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, type);
- ret = vb2_streamon(vq, type);
- if (!ret)
- v4l2_m2m_try_schedule(m2m_ctx);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_streamon);
-
-/**
- * v4l2_m2m_streamoff() - turn off streaming for a video queue
- */
-int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- enum v4l2_buf_type type)
-{
- struct vb2_queue *vq;
-
- vq = v4l2_m2m_get_vq(m2m_ctx, type);
- return vb2_streamoff(vq, type);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff);
-
-/**
- * v4l2_m2m_poll() - poll replacement, for destination buffers only
- *
- * Call from the driver's poll() function. Will poll both queues. If a buffer
- * is available to dequeue (with dqbuf) from the source queue, this will
- * indicate that a non-blocking write can be performed, while read will be
- * returned in case of the destination queue.
- */
-unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx,
- struct poll_table_struct *wait)
-{
- struct video_device *vfd = video_devdata(file);
- unsigned long req_events = poll_requested_events(wait);
- struct vb2_queue *src_q, *dst_q;
- struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
- unsigned int rc = 0;
- unsigned long flags;
-
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
- struct v4l2_fh *fh = file->private_data;
-
- if (v4l2_event_pending(fh))
- rc = POLLPRI;
- else if (req_events & POLLPRI)
- poll_wait(file, &fh->wait, wait);
- if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM)))
- return rc;
- }
-
- src_q = v4l2_m2m_get_src_vq(m2m_ctx);
- dst_q = v4l2_m2m_get_dst_vq(m2m_ctx);
-
- /*
- * 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 (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:
- return rc;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_poll);
-
-/**
- * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer
- *
- * Call from driver's mmap() function. Will handle mmap() for both queues
- * seamlessly for videobuffer, which will receive normal per-queue offsets and
- * proper videobuf queue pointers. The differentiation is made outside videobuf
- * by adding a predefined offset to buffers from one of the queues and
- * subtracting it before passing it back to videobuf. Only drivers (and
- * thus applications) receive modified offsets.
- */
-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 vb2_queue *vq;
-
- if (offset < DST_QUEUE_OFF_BASE) {
- vq = v4l2_m2m_get_src_vq(m2m_ctx);
- } else {
- vq = v4l2_m2m_get_dst_vq(m2m_ctx);
- vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
- }
-
- return vb2_mmap(vq, vma);
-}
-EXPORT_SYMBOL(v4l2_m2m_mmap);
-
-/**
- * v4l2_m2m_init() - initialize per-driver m2m data
- *
- * Usually called from driver's probe() function.
- */
-struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops)
-{
- struct v4l2_m2m_dev *m2m_dev;
-
- if (!m2m_ops)
- return ERR_PTR(-EINVAL);
-
- BUG_ON(!m2m_ops->device_run);
- BUG_ON(!m2m_ops->job_abort);
-
- m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL);
- if (!m2m_dev)
- return ERR_PTR(-ENOMEM);
-
- m2m_dev->curr_ctx = NULL;
- m2m_dev->m2m_ops = m2m_ops;
- INIT_LIST_HEAD(&m2m_dev->job_queue);
- spin_lock_init(&m2m_dev->job_spinlock);
-
- return m2m_dev;
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_init);
-
-/**
- * v4l2_m2m_release() - cleans up and frees a m2m_dev structure
- *
- * Usually called from driver's remove() function.
- */
-void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev)
-{
- kfree(m2m_dev);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_release);
-
-/**
- * v4l2_m2m_ctx_init() - allocate and initialize a m2m context
- * @priv - driver's instance private data
- * @m2m_dev - a previously initialized m2m_dev struct
- * @vq_init - a callback for queue type-specific initialization function to be
- * used for initializing videobuf_queues
- *
- * Usually called from driver's open() function.
- */
-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;
- int ret;
-
- m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL);
- if (!m2m_ctx)
- return ERR_PTR(-ENOMEM);
-
- m2m_ctx->priv = drv_priv;
- m2m_ctx->m2m_dev = m2m_dev;
- init_waitqueue_head(&m2m_ctx->finished);
-
- 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);
-
- 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);
-
-/**
- * v4l2_m2m_ctx_release() - release m2m context
- *
- * Usually called from driver's release() function.
- */
-void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx)
-{
- struct v4l2_m2m_dev *m2m_dev;
- unsigned long flags;
-
- m2m_dev = m2m_ctx->m2m_dev;
-
- spin_lock_irqsave(&m2m_dev->job_spinlock, flags);
- if (m2m_ctx->job_flags & TRANS_RUNNING) {
- 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);
- 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);
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
- dprintk("m2m_ctx: %p had been on queue and was removed\n",
- m2m_ctx);
- } else {
- /* Do nothing, was not on queue/running */
- spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags);
- }
-
- vb2_queue_release(&m2m_ctx->cap_q_ctx.q);
- vb2_queue_release(&m2m_ctx->out_q_ctx.q);
-
- kfree(m2m_ctx);
-}
-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.
- */
-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, vb->vb2_queue->type);
- if (!q_ctx)
- return;
-
- spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
- list_add_tail(&b->list, &q_ctx->rdy_queue);
- q_ctx->num_rdy++;
- spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags);
-}
-EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
-
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
deleted file mode 100644
index 9182f81deb5b..000000000000
--- a/drivers/media/video/v4l2-subdev.c
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * V4L2 sub-device
- *
- * Copyright (C) 2010 Nokia Corporation
- *
- * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
- * Sakari Ailus <sakari.ailus@iki.fi>
- *
- * 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/ioctl.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/videodev2.h>
-#include <linux/export.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-
-static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
-{
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL);
- if (fh->pad == NULL)
- return -ENOMEM;
-#endif
- return 0;
-}
-
-static void subdev_fh_free(struct v4l2_subdev_fh *fh)
-{
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- kfree(fh->pad);
- fh->pad = NULL;
-#endif
-}
-
-static int subdev_open(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_subdev_fh *subdev_fh;
-#if defined(CONFIG_MEDIA_CONTROLLER)
- struct media_entity *entity = NULL;
-#endif
- int ret;
-
- subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL);
- if (subdev_fh == NULL)
- return -ENOMEM;
-
- ret = subdev_fh_init(subdev_fh, sd);
- if (ret) {
- kfree(subdev_fh);
- return ret;
- }
-
- v4l2_fh_init(&subdev_fh->vfh, vdev);
- v4l2_fh_add(&subdev_fh->vfh);
- file->private_data = &subdev_fh->vfh;
-#if defined(CONFIG_MEDIA_CONTROLLER)
- if (sd->v4l2_dev->mdev) {
- entity = media_entity_get(&sd->entity);
- if (!entity) {
- ret = -EBUSY;
- goto err;
- }
- }
-#endif
-
- if (sd->internal_ops && sd->internal_ops->open) {
- ret = sd->internal_ops->open(sd, subdev_fh);
- if (ret < 0)
- goto err;
- }
-
- return 0;
-
-err:
-#if defined(CONFIG_MEDIA_CONTROLLER)
- if (entity)
- media_entity_put(entity);
-#endif
- v4l2_fh_del(&subdev_fh->vfh);
- v4l2_fh_exit(&subdev_fh->vfh);
- subdev_fh_free(subdev_fh);
- kfree(subdev_fh);
-
- return ret;
-}
-
-static int subdev_close(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
- struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *vfh = file->private_data;
- struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
-
- if (sd->internal_ops && sd->internal_ops->close)
- sd->internal_ops->close(sd, subdev_fh);
-#if defined(CONFIG_MEDIA_CONTROLLER)
- if (sd->v4l2_dev->mdev)
- media_entity_put(&sd->entity);
-#endif
- v4l2_fh_del(vfh);
- v4l2_fh_exit(vfh);
- subdev_fh_free(subdev_fh);
- kfree(subdev_fh);
- file->private_data = NULL;
-
- return 0;
-}
-
-static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
-{
- struct video_device *vdev = video_devdata(file);
- struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *vfh = file->private_data;
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
-#endif
-
- switch (cmd) {
- case VIDIOC_QUERYCTRL:
- return v4l2_queryctrl(vfh->ctrl_handler, arg);
-
- case VIDIOC_QUERYMENU:
- return v4l2_querymenu(vfh->ctrl_handler, arg);
-
- case VIDIOC_G_CTRL:
- return v4l2_g_ctrl(vfh->ctrl_handler, arg);
-
- case VIDIOC_S_CTRL:
- return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg);
-
- case VIDIOC_G_EXT_CTRLS:
- return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg);
-
- case VIDIOC_S_EXT_CTRLS:
- return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg);
-
- case VIDIOC_TRY_EXT_CTRLS:
- return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg);
-
- case VIDIOC_DQEVENT:
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
- return -ENOIOCTLCMD;
-
- return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK);
-
- case VIDIOC_SUBSCRIBE_EVENT:
- return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg);
-
- case VIDIOC_UNSUBSCRIBE_EVENT:
- return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg);
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- {
- struct v4l2_dbg_register *p = arg;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return v4l2_subdev_call(sd, core, g_register, p);
- }
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_dbg_register *p = arg;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return v4l2_subdev_call(sd, core, s_register, p);
- }
-#endif
-
- case VIDIOC_LOG_STATUS: {
- int ret;
-
- pr_info("%s: ================= START STATUS =================\n",
- sd->name);
- ret = v4l2_subdev_call(sd, core, log_status);
- pr_info("%s: ================== END STATUS ==================\n",
- sd->name);
- return ret;
- }
-
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- case VIDIOC_SUBDEV_G_FMT: {
- struct v4l2_subdev_format *format = arg;
-
- if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
- format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (format->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format);
- }
-
- case VIDIOC_SUBDEV_S_FMT: {
- struct v4l2_subdev_format *format = arg;
-
- if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
- format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (format->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format);
- }
-
- case VIDIOC_SUBDEV_G_CROP: {
- struct v4l2_subdev_crop *crop = arg;
- struct v4l2_subdev_selection sel;
- int rval;
-
- if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
- crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (crop->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
- if (rval != -ENOIOCTLCMD)
- return rval;
-
- memset(&sel, 0, sizeof(sel));
- sel.which = crop->which;
- sel.pad = crop->pad;
- sel.target = V4L2_SEL_TGT_CROP;
-
- rval = v4l2_subdev_call(
- sd, pad, get_selection, subdev_fh, &sel);
-
- crop->rect = sel.r;
-
- return rval;
- }
-
- case VIDIOC_SUBDEV_S_CROP: {
- struct v4l2_subdev_crop *crop = arg;
- struct v4l2_subdev_selection sel;
- int rval;
-
- if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
- crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (crop->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
- if (rval != -ENOIOCTLCMD)
- return rval;
-
- memset(&sel, 0, sizeof(sel));
- sel.which = crop->which;
- sel.pad = crop->pad;
- sel.target = V4L2_SEL_TGT_CROP;
- sel.r = crop->rect;
-
- rval = v4l2_subdev_call(
- sd, pad, set_selection, subdev_fh, &sel);
-
- crop->rect = sel.r;
-
- return rval;
- }
-
- case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
- struct v4l2_subdev_mbus_code_enum *code = arg;
-
- if (code->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh,
- code);
- }
-
- case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
- struct v4l2_subdev_frame_size_enum *fse = arg;
-
- if (fse->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh,
- fse);
- }
-
- case VIDIOC_SUBDEV_G_FRAME_INTERVAL:
- return v4l2_subdev_call(sd, video, g_frame_interval, arg);
-
- case VIDIOC_SUBDEV_S_FRAME_INTERVAL:
- return v4l2_subdev_call(sd, video, s_frame_interval, arg);
-
- case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
- struct v4l2_subdev_frame_interval_enum *fie = arg;
-
- if (fie->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
- fie);
- }
-
- case VIDIOC_SUBDEV_G_SELECTION: {
- struct v4l2_subdev_selection *sel = arg;
-
- if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
- sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (sel->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(
- sd, pad, get_selection, subdev_fh, sel);
- }
-
- case VIDIOC_SUBDEV_S_SELECTION: {
- struct v4l2_subdev_selection *sel = arg;
-
- if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
- sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (sel->pad >= sd->entity.num_pads)
- return -EINVAL;
-
- return v4l2_subdev_call(
- sd, pad, set_selection, subdev_fh, sel);
- }
-#endif
- default:
- return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
- }
-
- return 0;
-}
-
-static long subdev_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- return video_usercopy(file, cmd, arg, subdev_do_ioctl);
-}
-
-static unsigned int subdev_poll(struct file *file, poll_table *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
- struct v4l2_fh *fh = file->private_data;
-
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS))
- return POLLERR;
-
- poll_wait(file, &fh->wait, wait);
-
- if (v4l2_event_pending(fh))
- return POLLPRI;
-
- return 0;
-}
-
-const struct v4l2_file_operations v4l2_subdev_fops = {
- .owner = THIS_MODULE,
- .open = subdev_open,
- .unlocked_ioctl = subdev_ioctl,
- .release = subdev_close,
- .poll = subdev_poll,
-};
-
-#ifdef CONFIG_MEDIA_CONTROLLER
-int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
- struct media_link *link,
- struct v4l2_subdev_format *source_fmt,
- struct v4l2_subdev_format *sink_fmt)
-{
- if (source_fmt->format.width != sink_fmt->format.width
- || source_fmt->format.height != sink_fmt->format.height
- || source_fmt->format.code != sink_fmt->format.code)
- return -EINVAL;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
-
-static int
-v4l2_subdev_link_validate_get_format(struct media_pad *pad,
- struct v4l2_subdev_format *fmt)
-{
- switch (media_entity_type(pad->entity)) {
- case MEDIA_ENT_T_V4L2_SUBDEV:
- fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
- fmt->pad = pad->index;
- return v4l2_subdev_call(media_entity_to_v4l2_subdev(
- pad->entity),
- pad, get_fmt, NULL, fmt);
- default:
- WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
- media_entity_type(pad->entity), pad->entity->name);
- /* Fall through */
- case MEDIA_ENT_T_DEVNODE_V4L:
- return -EINVAL;
- }
-}
-
-int v4l2_subdev_link_validate(struct media_link *link)
-{
- struct v4l2_subdev *sink;
- struct v4l2_subdev_format sink_fmt, source_fmt;
- int rval;
-
- rval = v4l2_subdev_link_validate_get_format(
- link->source, &source_fmt);
- if (rval < 0)
- return 0;
-
- rval = v4l2_subdev_link_validate_get_format(
- link->sink, &sink_fmt);
- if (rval < 0)
- return 0;
-
- sink = media_entity_to_v4l2_subdev(link->sink->entity);
-
- rval = v4l2_subdev_call(sink, pad, link_validate, link,
- &source_fmt, &sink_fmt);
- if (rval != -ENOIOCTLCMD)
- return rval;
-
- return v4l2_subdev_link_validate_default(
- sink, link, &source_fmt, &sink_fmt);
-}
-EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
-#endif /* CONFIG_MEDIA_CONTROLLER */
-
-void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
-{
- INIT_LIST_HEAD(&sd->list);
- BUG_ON(!ops);
- sd->ops = ops;
- sd->v4l2_dev = NULL;
- sd->flags = 0;
- sd->name[0] = '\0';
- sd->grp_id = 0;
- sd->dev_priv = NULL;
- sd->host_priv = NULL;
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.name = sd->name;
- sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
-#endif
-}
-EXPORT_SYMBOL(v4l2_subdev_init);
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
deleted file mode 100644
index 94d83a41381b..000000000000
--- a/drivers/media/video/videobuf-dvb.c
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- *
- * some helper function for simple DVB cards which simply DMA the
- * complete transport stream and let the computer sort everything else
- * (i.e. we are using the software demux, ...). Also uses the
- * video-buf to manage DMA buffers.
- *
- * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/kthread.h>
-#include <linux/file.h>
-#include <linux/slab.h>
-
-#include <linux/freezer.h>
-
-#include <media/videobuf-core.h>
-#include <media/videobuf-dvb.h>
-
-/* ------------------------------------------------------------------ */
-
-MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
-MODULE_LICENSE("GPL");
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug,"enable debug messages");
-
-#define dprintk(fmt, arg...) if (debug) \
- printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg)
-
-/* ------------------------------------------------------------------ */
-
-static int videobuf_dvb_thread(void *data)
-{
- struct videobuf_dvb *dvb = data;
- struct videobuf_buffer *buf;
- unsigned long flags;
- void *outp;
-
- dprintk("dvb thread started\n");
- set_freezable();
- videobuf_read_start(&dvb->dvbq);
-
- for (;;) {
- /* fetch next buffer */
- buf = list_entry(dvb->dvbq.stream.next,
- struct videobuf_buffer, stream);
- list_del(&buf->stream);
- videobuf_waiton(&dvb->dvbq, buf, 0, 1);
-
- /* no more feeds left or stop_feed() asked us to quit */
- if (0 == dvb->nfeeds)
- break;
- if (kthread_should_stop())
- break;
- try_to_freeze();
-
- /* feed buffer data to demux */
- outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf);
-
- if (buf->state == VIDEOBUF_DONE)
- dvb_dmx_swfilter(&dvb->demux, outp,
- buf->size);
-
- /* requeue buffer */
- list_add_tail(&buf->stream,&dvb->dvbq.stream);
- spin_lock_irqsave(dvb->dvbq.irqlock,flags);
- dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf);
- spin_unlock_irqrestore(dvb->dvbq.irqlock,flags);
- }
-
- videobuf_read_stop(&dvb->dvbq);
- dprintk("dvb thread stopped\n");
-
- /* Hmm, linux becomes *very* unhappy without this ... */
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- }
- return 0;
-}
-
-static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct videobuf_dvb *dvb = demux->priv;
- int rc;
-
- if (!demux->dmx.frontend)
- return -EINVAL;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds++;
- rc = dvb->nfeeds;
-
- if (NULL != dvb->thread)
- goto out;
- dvb->thread = kthread_run(videobuf_dvb_thread,
- dvb, "%s dvb", dvb->name);
- if (IS_ERR(dvb->thread)) {
- rc = PTR_ERR(dvb->thread);
- dvb->thread = NULL;
- }
-
-out:
- mutex_unlock(&dvb->lock);
- return rc;
-}
-
-static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed)
-{
- struct dvb_demux *demux = feed->demux;
- struct videobuf_dvb *dvb = demux->priv;
- int err = 0;
-
- mutex_lock(&dvb->lock);
- dvb->nfeeds--;
- if (0 == dvb->nfeeds && NULL != dvb->thread) {
- err = kthread_stop(dvb->thread);
- dvb->thread = NULL;
- }
- mutex_unlock(&dvb->lock);
- return err;
-}
-
-static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe,
- struct module *module,
- void *adapter_priv,
- struct device *device,
- char *adapter_name,
- short *adapter_nr,
- int mfe_shared,
- int (*fe_ioctl_override)(struct dvb_frontend *,
- unsigned int, void *, unsigned int))
-{
- int result;
-
- mutex_init(&fe->lock);
-
- /* register adapter */
- result = dvb_register_adapter(&fe->adapter, adapter_name, module,
- device, adapter_nr);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
- adapter_name, result);
- }
- fe->adapter.priv = adapter_priv;
- fe->adapter.mfe_shared = mfe_shared;
- fe->adapter.fe_ioctl_override = fe_ioctl_override;
-
- return result;
-}
-
-static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter,
- struct videobuf_dvb *dvb)
-{
- int result;
-
- /* register frontend */
- result = dvb_register_frontend(adapter, dvb->frontend);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n",
- dvb->name, result);
- goto fail_frontend;
- }
-
- /* register demux stuff */
- dvb->demux.dmx.capabilities =
- DMX_TS_FILTERING | DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
- dvb->demux.priv = dvb;
- dvb->demux.filternum = 256;
- dvb->demux.feednum = 256;
- dvb->demux.start_feed = videobuf_dvb_start_feed;
- dvb->demux.stop_feed = videobuf_dvb_stop_feed;
- result = dvb_dmx_init(&dvb->demux);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_dmx;
- }
-
- dvb->dmxdev.filternum = 256;
- dvb->dmxdev.demux = &dvb->demux.dmx;
- dvb->dmxdev.capabilities = 0;
- result = dvb_dmxdev_init(&dvb->dmxdev, adapter);
-
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_dmxdev;
- }
-
- dvb->fe_hw.source = DMX_FRONTEND_0;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
- dvb->name, result);
- goto fail_fe_hw;
- }
-
- dvb->fe_mem.source = DMX_MEMORY_FE;
- result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
- if (result < 0) {
- printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
- dvb->name, result);
- goto fail_fe_mem;
- }
-
- result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
- if (result < 0) {
- printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
- dvb->name, result);
- goto fail_fe_conn;
- }
-
- /* register network adapter */
- result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx);
- if (result < 0) {
- printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n",
- dvb->name, result);
- goto fail_fe_conn;
- }
- return 0;
-
-fail_fe_conn:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
-fail_fe_mem:
- dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
-fail_fe_hw:
- dvb_dmxdev_release(&dvb->dmxdev);
-fail_dmxdev:
- dvb_dmx_release(&dvb->demux);
-fail_dmx:
- dvb_unregister_frontend(dvb->frontend);
-fail_frontend:
- dvb_frontend_detach(dvb->frontend);
- dvb->frontend = NULL;
-
- return result;
-}
-
-/* ------------------------------------------------------------------ */
-/* Register a single adapter and one or more frontends */
-int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
- struct module *module,
- void *adapter_priv,
- struct device *device,
- short *adapter_nr,
- int mfe_shared,
- int (*fe_ioctl_override)(struct dvb_frontend *,
- unsigned int, void *, unsigned int))
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe;
- int res;
-
- fe = videobuf_dvb_get_frontend(f, 1);
- if (!fe) {
- printk(KERN_WARNING "Unable to register the adapter which has no frontends\n");
- return -EINVAL;
- }
-
- /* Bring up the adapter */
- res = videobuf_dvb_register_adapter(f, module, adapter_priv, device,
- fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override);
- if (res < 0) {
- printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res);
- return res;
- }
-
- /* Attach all of the frontends to the adapter */
- mutex_lock(&f->lock);
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb);
- if (res < 0) {
- printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n",
- fe->dvb.name, res);
- goto err;
- }
- }
- mutex_unlock(&f->lock);
- return 0;
-
-err:
- mutex_unlock(&f->lock);
- videobuf_dvb_unregister_bus(f);
- return res;
-}
-EXPORT_SYMBOL(videobuf_dvb_register_bus);
-
-void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f)
-{
- videobuf_dvb_dealloc_frontends(f);
-
- dvb_unregister_adapter(&f->adapter);
-}
-EXPORT_SYMBOL(videobuf_dvb_unregister_bus);
-
-struct videobuf_dvb_frontend *videobuf_dvb_get_frontend(
- struct videobuf_dvb_frontends *f, int id)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe, *ret = NULL;
-
- mutex_lock(&f->lock);
-
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->id == id) {
- ret = fe;
- break;
- }
- }
-
- mutex_unlock(&f->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_get_frontend);
-
-int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f,
- struct dvb_frontend *p)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe = NULL;
- int ret = 0;
-
- mutex_lock(&f->lock);
-
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->dvb.frontend == p) {
- ret = fe->id;
- break;
- }
- }
-
- mutex_unlock(&f->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(videobuf_dvb_find_frontend);
-
-struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend(
- struct videobuf_dvb_frontends *f, int id)
-{
- struct videobuf_dvb_frontend *fe;
-
- fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL);
- if (fe == NULL)
- goto fail_alloc;
-
- fe->id = id;
- mutex_init(&fe->dvb.lock);
-
- mutex_lock(&f->lock);
- list_add_tail(&fe->felist, &f->felist);
- mutex_unlock(&f->lock);
-
-fail_alloc:
- return fe;
-}
-EXPORT_SYMBOL(videobuf_dvb_alloc_frontend);
-
-void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f)
-{
- struct list_head *list, *q;
- struct videobuf_dvb_frontend *fe;
-
- mutex_lock(&f->lock);
- list_for_each_safe(list, q, &f->felist) {
- fe = list_entry(list, struct videobuf_dvb_frontend, felist);
- if (fe->dvb.net.dvbdev) {
- dvb_net_release(&fe->dvb.net);
- fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
- &fe->dvb.fe_mem);
- fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx,
- &fe->dvb.fe_hw);
- dvb_dmxdev_release(&fe->dvb.dmxdev);
- dvb_dmx_release(&fe->dvb.demux);
- dvb_unregister_frontend(fe->dvb.frontend);
- }
- if (fe->dvb.frontend)
- /* always allocated, may have been reset */
- dvb_frontend_detach(fe->dvb.frontend);
- list_del(list); /* remove list entry */
- kfree(fe); /* free frontend allocation */
- }
- mutex_unlock(&f->lock);
-}
-EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends);
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
deleted file mode 100644
index 268c7dd4f823..000000000000
--- a/drivers/media/video/videobuf2-core.c
+++ /dev/null
@@ -1,2387 +0,0 @@
-/*
- * videobuf2-core.c - V4L2 driver helper framework
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.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/v4l2-dev.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.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, 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)
-
-#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \
- V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \
- V4L2_BUF_FLAG_PREPARED)
-
-/**
- * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
- */
-static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
-{
- 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, alloc, q->alloc_ctx[plane],
- q->plane_sizes[plane]);
- if (IS_ERR_OR_NULL(mem_priv))
- goto free;
-
- /* Associate allocator private data with this plane */
- vb->planes[plane].mem_priv = mem_priv;
- vb->v4l2_planes[plane].length = q->plane_sizes[plane];
- }
-
- return 0;
-free:
- /* Free already allocated memory if one of the allocations failed */
- for (; plane > 0; --plane) {
- call_memop(q, put, vb->planes[plane - 1].mem_priv);
- vb->planes[plane - 1].mem_priv = NULL;
- }
-
- 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, 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) {
- if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].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 n)
-{
- unsigned int buffer, plane;
- struct vb2_buffer *vb;
- unsigned long off;
-
- if (q->num_buffers) {
- struct v4l2_plane *p;
- vb = q->bufs[q->num_buffers - 1];
- p = &vb->v4l2_planes[vb->num_planes - 1];
- off = PAGE_ALIGN(p->m.mem_offset + p->length);
- } else {
- off = 0;
- }
-
- for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
- vb = q->bufs[buffer];
- if (!vb)
- continue;
-
- for (plane = 0; plane < vb->num_planes; ++plane) {
- vb->v4l2_planes[plane].length = q->plane_sizes[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 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 = q->num_buffers + 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);
- 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[q->num_buffers + buffer] = vb;
- }
-
- __setup_offsets(q, buffer);
-
- dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
- buffer, 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 buffers)
-{
- unsigned int buffer;
- struct vb2_buffer *vb;
-
- for (buffer = q->num_buffers - buffers; 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 buffers at the end of the queue - video memory and
- * related information, if no buffers are left return the queue to an
- * uninitialized state. Might be called even if the queue has already been freed.
- */
-static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
-{
- unsigned int buffer;
-
- /* Call driver-provided cleanup function for each buffer, if provided */
- if (q->ops->buf_cleanup) {
- for (buffer = q->num_buffers - buffers; 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, buffers);
-
- /* Free videobuf buffers */
- for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
- ++buffer) {
- kfree(q->bufs[buffer]);
- q->bufs[buffer] = NULL;
- }
-
- q->num_buffers -= buffers;
- if (!q->num_buffers)
- q->memory = 0;
- INIT_LIST_HEAD(&q->queued_list);
-}
-
-/**
- * __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, const 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;
-}
-
-/**
- * __buffer_in_use() - return true if the buffer is in use and
- * the queue cannot be freed (by the means of REQBUFS(0)) call
- */
-static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
-{
- unsigned int plane;
- for (plane = 0; plane < vb->num_planes; ++plane) {
- void *mem_priv = vb->planes[plane].mem_priv;
- /*
- * 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 (mem_priv && call_memop(q, num_users, mem_priv) > 1)
- return true;
- }
- return false;
-}
-
-/**
- * __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;
- for (buffer = 0; buffer < q->num_buffers; ++buffer) {
- if (__buffer_in_use(q, q->bufs[buffer]))
- return true;
- }
- return false;
-}
-
-/**
- * __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;
-
- /* Copy back data such as timestamp, flags, etc. */
- memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
- b->reserved2 = vb->v4l2_buf.reserved2;
- 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;
- }
-
- /*
- * Clear any buffer state related flags.
- */
- b->flags &= ~V4L2_BUFFER_STATE_FLAGS;
-
- 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_PREPARED:
- b->flags |= V4L2_BUF_FLAG_PREPARED;
- break;
- case VB2_BUF_STATE_DEQUEUED:
- /* nothing */
- break;
- }
-
- if (__buffer_in_use(q, vb))
- b->flags |= V4L2_BUF_FLAG_MAPPED;
-
- return 0;
-}
-
-/**
- * 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;
-}
-
-/**
- * __verify_memory_type() - Check whether the memory type and buffer type
- * passed to a buffer operation are compatible with the queue.
- */
-static int __verify_memory_type(struct vb2_queue *q,
- enum v4l2_memory memory, enum v4l2_buf_type type)
-{
- if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) {
- dprintk(1, "reqbufs: unsupported memory type\n");
- return -EINVAL;
- }
-
- if (type != q->type) {
- dprintk(1, "reqbufs: requested type is incorrect\n");
- return -EINVAL;
- }
-
- /*
- * Make sure all the required memory ops for given memory type
- * are available.
- */
- if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
- dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
- return -EINVAL;
- }
-
- if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
- dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
- return -EINVAL;
- }
-
- /*
- * Place the busy tests at the end: -EBUSY can be ignored when
- * create_bufs is called with count == 0, but count == 0 should still
- * do the memory and type validation.
- */
- if (q->fileio) {
- dprintk(1, "reqbufs: file io in progress\n");
- return -EBUSY;
- }
- return 0;
-}
-
-/**
- * __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.
- */
-static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
-{
- unsigned int num_buffers, allocated_buffers, num_planes = 0;
- int ret;
-
- if (q->streaming) {
- dprintk(1, "reqbufs: streaming active\n");
- return -EBUSY;
- }
-
- if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
- /*
- * 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;
- }
-
- __vb2_queue_free(q, q->num_buffers);
-
- /*
- * In case of REQBUFS(0) return immediately without calling
- * driver's queue_setup() callback and allocating resources.
- */
- if (req->count == 0)
- return 0;
- }
-
- /*
- * Make sure the requested values and current defaults are sane.
- */
- num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
- memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
- memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
- q->memory = req->memory;
-
- /*
- * 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, NULL, &num_buffers, &num_planes,
- q->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);
- if (ret == 0) {
- dprintk(1, "Memory allocation failed\n");
- return -ENOMEM;
- }
-
- allocated_buffers = ret;
-
- /*
- * Check if driver can handle the allocated number of buffers.
- */
- if (allocated_buffers < num_buffers) {
- num_buffers = allocated_buffers;
-
- ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
-
- if (!ret && allocated_buffers < num_buffers)
- ret = -ENOMEM;
-
- /*
- * Either the driver has accepted a smaller number of buffers,
- * or .queue_setup() returned an error
- */
- }
-
- q->num_buffers = allocated_buffers;
-
- if (ret < 0) {
- __vb2_queue_free(q, allocated_buffers);
- return ret;
- }
-
- /*
- * Return the number of successfully allocated buffers
- * to the userspace.
- */
- req->count = allocated_buffers;
-
- return 0;
-}
-
-/**
- * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and
- * type values.
- * @q: videobuf2 queue
- * @req: struct passed from userspace to vidioc_reqbufs handler in driver
- */
-int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
-{
- int ret = __verify_memory_type(q, req->memory, req->type);
-
- return ret ? ret : __reqbufs(q, req);
-}
-EXPORT_SYMBOL_GPL(vb2_reqbufs);
-
-/**
- * __create_bufs() - Allocate buffers and any required auxiliary structs
- * @q: videobuf2 queue
- * @create: creation parameters, passed from userspace to vidioc_create_bufs
- * handler in driver
- *
- * Should be called from vidioc_create_bufs ioctl handler of a driver.
- * This function:
- * 1) verifies parameter sanity
- * 2) calls the .queue_setup() queue operation
- * 3) performs any necessary memory allocations
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_create_bufs handler in driver.
- */
-static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
-{
- unsigned int num_planes = 0, num_buffers, allocated_buffers;
- int ret;
-
- if (q->num_buffers == VIDEO_MAX_FRAME) {
- dprintk(1, "%s(): maximum number of buffers already allocated\n",
- __func__);
- return -ENOBUFS;
- }
-
- if (!q->num_buffers) {
- memset(q->plane_sizes, 0, sizeof(q->plane_sizes));
- memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
- q->memory = create->memory;
- }
-
- num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers);
-
- /*
- * Ask the driver, whether the requested number of buffers, planes per
- * buffer and their sizes are acceptable
- */
- ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
- if (ret)
- return ret;
-
- /* Finally, allocate buffers and video memory */
- ret = __vb2_queue_alloc(q, create->memory, num_buffers,
- num_planes);
- if (ret == 0) {
- dprintk(1, "Memory allocation failed\n");
- return -ENOMEM;
- }
-
- allocated_buffers = ret;
-
- /*
- * Check if driver can handle the so far allocated number of buffers.
- */
- if (ret < num_buffers) {
- num_buffers = ret;
-
- /*
- * q->num_buffers contains the total number of buffers, that the
- * queue driver has set up
- */
- ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
- &num_planes, q->plane_sizes, q->alloc_ctx);
-
- if (!ret && allocated_buffers < num_buffers)
- ret = -ENOMEM;
-
- /*
- * Either the driver has accepted a smaller number of buffers,
- * or .queue_setup() returned an error
- */
- }
-
- q->num_buffers += allocated_buffers;
-
- if (ret < 0) {
- __vb2_queue_free(q, allocated_buffers);
- return -ENOMEM;
- }
-
- /*
- * Return the number of successfully allocated buffers
- * to the userspace.
- */
- create->count = allocated_buffers;
-
- return 0;
-}
-
-/**
- * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the
- * memory and type values.
- * @q: videobuf2 queue
- * @create: creation parameters, passed from userspace to vidioc_create_bufs
- * handler in driver
- */
-int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
-{
- int ret = __verify_memory_type(q, create->memory, create->format.type);
-
- create->index = q->num_buffers;
- if (create->count == 0)
- return ret != -EBUSY ? ret : 0;
- return ret ? ret : __create_bufs(q, create);
-}
-EXPORT_SYMBOL_GPL(vb2_create_bufs);
-
-/**
- * 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 || !vb->planes[plane_no].mem_priv)
- return NULL;
-
- return call_memop(q, 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 || !vb->planes[plane_no].mem_priv)
- return NULL;
-
- return call_memop(q, 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, const 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;
- vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS;
-
- return 0;
-}
-
-/**
- * __qbuf_userptr() - handle qbuf of a USERPTR buffer
- */
-static int __qbuf_userptr(struct vb2_buffer *vb, const 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 &&
- 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);
-
- /* Check if the provided plane buffer is large enough */
- if (planes[plane].length < q->plane_sizes[plane]) {
- ret = -EINVAL;
- goto err;
- }
-
- /* Release previously acquired memory if present */
- if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].mem_priv);
-
- vb->planes[plane].mem_priv = NULL;
- vb->v4l2_planes[plane].m.userptr = 0;
- vb->v4l2_planes[plane].length = 0;
-
- /* Acquire each plane's memory */
- mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane],
- planes[plane].m.userptr,
- planes[plane].length, write);
- if (IS_ERR_OR_NULL(mem_priv)) {
- dprintk(1, "qbuf: failed acquiring userspace "
- "memory for plane %d\n", plane);
- ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL;
- 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 < vb->num_planes; ++plane) {
- if (vb->planes[plane].mem_priv)
- call_memop(q, put_userptr, vb->planes[plane].mem_priv);
- vb->planes[plane].mem_priv = NULL;
- vb->v4l2_planes[plane].m.userptr = 0;
- vb->v4l2_planes[plane].length = 0;
- }
-
- return ret;
-}
-
-/**
- * __qbuf_mmap() - handle qbuf of an MMAP buffer
- */
-static int __qbuf_mmap(struct vb2_buffer *vb, const 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);
-}
-
-static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
-{
- struct vb2_queue *q = vb->vb2_queue;
- int ret;
-
- switch (q->memory) {
- case V4L2_MEMORY_MMAP:
- ret = __qbuf_mmap(vb, b);
- break;
- case V4L2_MEMORY_USERPTR:
- ret = __qbuf_userptr(vb, b);
- break;
- default:
- WARN(1, "Invalid queue type\n");
- ret = -EINVAL;
- }
-
- if (!ret)
- ret = call_qop(q, buf_prepare, vb);
- if (ret)
- dprintk(1, "qbuf: buffer preparation failed: %d\n", ret);
- else
- vb->state = VB2_BUF_STATE_PREPARED;
-
- return ret;
-}
-
-/**
- * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel
- * @q: videobuf2 queue
- * @b: buffer structure passed from userspace to vidioc_prepare_buf
- * handler in driver
- *
- * Should be called from vidioc_prepare_buf 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,
- *
- * The return values from this function are intended to be directly returned
- * from vidioc_prepare_buf handler in driver.
- */
-int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b)
-{
- struct vb2_buffer *vb;
- int ret;
-
- if (q->fileio) {
- dprintk(1, "%s(): file io in progress\n", __func__);
- return -EBUSY;
- }
-
- if (b->type != q->type) {
- dprintk(1, "%s(): invalid buffer type\n", __func__);
- return -EINVAL;
- }
-
- if (b->index >= q->num_buffers) {
- dprintk(1, "%s(): buffer index out of range\n", __func__);
- return -EINVAL;
- }
-
- vb = q->bufs[b->index];
- if (NULL == vb) {
- /* Should never happen */
- dprintk(1, "%s(): buffer is NULL\n", __func__);
- return -EINVAL;
- }
-
- if (b->memory != q->memory) {
- dprintk(1, "%s(): invalid memory type\n", __func__);
- return -EINVAL;
- }
-
- if (vb->state != VB2_BUF_STATE_DEQUEUED) {
- dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state);
- return -EINVAL;
- }
-
- ret = __buf_prepare(vb, b);
- if (ret < 0)
- return ret;
-
- __fill_v4l2_buffer(vb, b);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_prepare_buf);
-
-/**
- * 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) if necessary, 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 rw_semaphore *mmap_sem = NULL;
- struct vb2_buffer *vb;
- int ret = 0;
-
- /*
- * In case of user pointer buffers vb2 allocator needs to get direct
- * access to userspace pages. This requires getting read access on
- * mmap semaphore in the current process structure. The same
- * semaphore is taken before calling mmap operation, while both mmap
- * and qbuf are called by the driver or v4l2 core with driver's lock
- * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in
- * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core
- * release driver's lock, takes mmap_sem and then takes again driver's
- * lock.
- *
- * To avoid race with other vb2 calls, which might be called after
- * releasing driver's lock, this operation is performed at the
- * beggining of qbuf processing. This way the queue status is
- * consistent after getting driver's lock back.
- */
- if (q->memory == V4L2_MEMORY_USERPTR) {
- mmap_sem = &current->mm->mmap_sem;
- call_qop(q, wait_prepare, q);
- down_read(mmap_sem);
- call_qop(q, wait_finish, q);
- }
-
- if (q->fileio) {
- dprintk(1, "qbuf: file io in progress\n");
- ret = -EBUSY;
- goto unlock;
- }
-
- if (b->type != q->type) {
- dprintk(1, "qbuf: invalid buffer type\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- if (b->index >= q->num_buffers) {
- dprintk(1, "qbuf: buffer index out of range\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- vb = q->bufs[b->index];
- if (NULL == vb) {
- /* Should never happen */
- dprintk(1, "qbuf: buffer is NULL\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- if (b->memory != q->memory) {
- dprintk(1, "qbuf: invalid memory type\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- switch (vb->state) {
- case VB2_BUF_STATE_DEQUEUED:
- ret = __buf_prepare(vb, b);
- if (ret)
- goto unlock;
- case VB2_BUF_STATE_PREPARED:
- break;
- default:
- dprintk(1, "qbuf: buffer already in use\n");
- ret = -EINVAL;
- goto unlock;
- }
-
- /*
- * 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);
-
- /* Fill buffer information for the userspace */
- __fill_v4l2_buffer(vb, b);
-
- dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
-unlock:
- if (mmap_sem)
- up_read(mmap_sem);
- return ret;
-}
-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_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);
- atomic_set(&q->queued_count, 0);
- 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_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) passes any previously queued buffers to the driver and starts streaming
- *
- * 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;
- int ret;
-
- 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;
- }
-
- /*
- * 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);
-
- /*
- * Let driver notice that streaming state has been enabled.
- */
- ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count));
- if (ret) {
- dprintk(1, "streamon: driver refused to start streaming\n");
- __vb2_queue_cancel(q);
- return ret;
- }
-
- q->streaming = 1;
-
- dprintk(3, "Streamon successful\n");
- return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_streamon);
-
-
-/**
- * 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_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];
-
- ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma);
- if (ret)
- return ret;
-
- dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane);
- return 0;
-}
-EXPORT_SYMBOL_GPL(vb2_mmap);
-
-#ifndef CONFIG_MMU
-unsigned long vb2_get_unmapped_area(struct vb2_queue *q,
- unsigned long addr,
- unsigned long len,
- unsigned long pgoff,
- unsigned long flags)
-{
- unsigned long off = pgoff << PAGE_SHIFT;
- 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;
- }
-
- /*
- * 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];
-
- return (unsigned long)vb2_plane_vaddr(vb, plane);
-}
-EXPORT_SYMBOL_GPL(vb2_get_unmapped_area);
-#endif
-
-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.
- *
- * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any
- * pending events.
- *
- * 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)
-{
- struct video_device *vfd = video_devdata(file);
- unsigned long req_events = poll_requested_events(wait);
- struct vb2_buffer *vb = NULL;
- unsigned int res = 0;
- unsigned long flags;
-
- if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
- struct v4l2_fh *fh = file->private_data;
-
- if (v4l2_event_pending(fh))
- res = POLLPRI;
- else if (req_events & POLLPRI)
- poll_wait(file, &fh->wait, wait);
- }
-
- /*
- * Start file I/O emulator only 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) &&
- (req_events & (POLLIN | POLLRDNORM))) {
- if (__vb2_init_fileio(q, 1))
- return res | POLLERR;
- }
- if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
- (req_events & (POLLOUT | POLLWRNORM))) {
- if (__vb2_init_fileio(q, 0))
- return res | POLLERR;
- /*
- * Write to OUTPUT queue can be done immediately.
- */
- return res | POLLOUT | POLLWRNORM;
- }
- }
-
- /*
- * There is nothing to wait for if no buffers have already been queued.
- */
- if (list_empty(&q->queued_list))
- return res | 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)) ?
- res | POLLOUT | POLLWRNORM :
- res | POLLIN | POLLRDNORM;
- }
- return res;
-}
-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, q->num_buffers);
-}
-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) {
- 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:
- fileio->req.count = 0;
- 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);
-
-
-/*
- * The following functions are not part of the vb2 core API, but are helper
- * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations
- * and struct vb2_ops.
- * They contain boilerplate code that most if not all drivers have to do
- * and so they simplify the driver code.
- */
-
-/* The queue is busy if there is a owner and you are not that owner. */
-static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file)
-{
- return vdev->queue->owner && vdev->queue->owner != file->private_data;
-}
-
-/* vb2 ioctl helpers */
-
-int vb2_ioctl_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *p)
-{
- struct video_device *vdev = video_devdata(file);
- int res = __verify_memory_type(vdev->queue, p->memory, p->type);
-
- if (res)
- return res;
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- res = __reqbufs(vdev->queue, p);
- /* If count == 0, then the owner has released all buffers and he
- is no longer owner of the queue. Otherwise we have a new owner. */
- if (res == 0)
- vdev->queue->owner = p->count ? file->private_data : NULL;
- return res;
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);
-
-int vb2_ioctl_create_bufs(struct file *file, void *priv,
- struct v4l2_create_buffers *p)
-{
- struct video_device *vdev = video_devdata(file);
- int res = __verify_memory_type(vdev->queue, p->memory, p->format.type);
-
- p->index = vdev->queue->num_buffers;
- /* If count == 0, then just check if memory and type are valid.
- Any -EBUSY result from __verify_memory_type can be mapped to 0. */
- if (p->count == 0)
- return res != -EBUSY ? res : 0;
- if (res)
- return res;
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- res = __create_bufs(vdev->queue, p);
- if (res == 0)
- vdev->queue->owner = file->private_data;
- return res;
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs);
-
-int vb2_ioctl_prepare_buf(struct file *file, void *priv,
- struct v4l2_buffer *p)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- return vb2_prepare_buf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf);
-
-int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct video_device *vdev = video_devdata(file);
-
- /* No need to call vb2_queue_is_busy(), anyone can query buffers. */
- return vb2_querybuf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf);
-
-int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- return vb2_qbuf(vdev->queue, p);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf);
-
-int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf);
-
-int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- return vb2_streamon(vdev->queue, i);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_streamon);
-
-int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (vb2_queue_is_busy(vdev, file))
- return -EBUSY;
- return vb2_streamoff(vdev->queue, i);
-}
-EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff);
-
-/* v4l2_file_operations helpers */
-
-int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct video_device *vdev = video_devdata(file);
-
- return vb2_mmap(vdev->queue, vma);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_mmap);
-
-int vb2_fop_release(struct file *file)
-{
- struct video_device *vdev = video_devdata(file);
-
- if (file->private_data == vdev->queue->owner) {
- vb2_queue_release(vdev->queue);
- vdev->queue->owner = NULL;
- }
- return v4l2_fh_release(file);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_release);
-
-ssize_t vb2_fop_write(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
- bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && lock;
- int err = -EBUSY;
-
- if (must_lock && mutex_lock_interruptible(lock))
- return -ERESTARTSYS;
- if (vb2_queue_is_busy(vdev, file))
- goto exit;
- err = vb2_write(vdev->queue, buf, count, ppos,
- file->f_flags & O_NONBLOCK);
- if (err >= 0)
- vdev->queue->owner = file->private_data;
-exit:
- if (must_lock)
- mutex_unlock(lock);
- return err;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_write);
-
-ssize_t vb2_fop_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct video_device *vdev = video_devdata(file);
- struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock;
- bool must_lock = !test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && vdev->lock;
- int err = -EBUSY;
-
- if (must_lock && mutex_lock_interruptible(lock))
- return -ERESTARTSYS;
- if (vb2_queue_is_busy(vdev, file))
- goto exit;
- err = vb2_read(vdev->queue, buf, count, ppos,
- file->f_flags & O_NONBLOCK);
- if (err >= 0)
- vdev->queue->owner = file->private_data;
-exit:
- if (must_lock)
- mutex_unlock(lock);
- return err;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_read);
-
-unsigned int vb2_fop_poll(struct file *file, poll_table *wait)
-{
- struct video_device *vdev = video_devdata(file);
- struct vb2_queue *q = vdev->queue;
- struct mutex *lock = q->lock ? q->lock : vdev->lock;
- unsigned long req_events = poll_requested_events(wait);
- unsigned res;
- void *fileio;
- /* Yuck. We really need to get rid of this flag asap. If it is
- set, then the core took the serialization lock before calling
- poll(). This is being phased out, but for now we have to handle
- this case. */
- bool locked = test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
- bool must_lock = false;
-
- /* Try to be smart: only lock if polling might start fileio,
- otherwise locking will only introduce unwanted delays. */
- if (q->num_buffers == 0 && q->fileio == NULL) {
- if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
- (req_events & (POLLIN | POLLRDNORM)))
- must_lock = true;
- else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
- (req_events & (POLLOUT | POLLWRNORM)))
- must_lock = true;
- }
-
- /* If locking is needed, but this helper doesn't know how, then you
- shouldn't be using this helper but you should write your own. */
- WARN_ON(must_lock && !locked && !lock);
-
- if (must_lock && !locked && lock && mutex_lock_interruptible(lock))
- return POLLERR;
-
- fileio = q->fileio;
-
- res = vb2_poll(vdev->queue, file, wait);
-
- /* If fileio was started, then we have a new queue owner. */
- if (must_lock && !fileio && q->fileio)
- q->owner = file->private_data;
- if (must_lock && !locked && lock)
- mutex_unlock(lock);
- return res;
-}
-EXPORT_SYMBOL_GPL(vb2_fop_poll);
-
-#ifndef CONFIG_MMU
-unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags)
-{
- struct video_device *vdev = video_devdata(file);
-
- return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags);
-}
-EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area);
-#endif
-
-/* vb2_ops helpers. Only use if vq->lock is non-NULL. */
-
-void vb2_ops_wait_prepare(struct vb2_queue *vq)
-{
- mutex_unlock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare);
-
-void vb2_ops_wait_finish(struct vb2_queue *vq)
-{
- mutex_lock(vq->lock);
-}
-EXPORT_SYMBOL_GPL(vb2_ops_wait_finish);
-
-MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
-MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
deleted file mode 100644
index 6b5ca6c70a46..000000000000
--- a/drivers/media/video/videobuf2-vmalloc.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
- *
- * Copyright (C) 2010 Samsung Electronics
- *
- * Author: Pawel Osciak <pawel@osciak.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/io.h>
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/sched.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;
- struct page **pages;
- struct vm_area_struct *vma;
- int write;
- unsigned long size;
- unsigned int n_pages;
- 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) {
- pr_debug("vmalloc of size %ld failed\n", buf->size);
- kfree(buf);
- return NULL;
- }
-
- atomic_inc(&buf->refcount);
- return buf;
-}
-
-static void vb2_vmalloc_put(void *buf_priv)
-{
- struct vb2_vmalloc_buf *buf = buf_priv;
-
- if (atomic_dec_and_test(&buf->refcount)) {
- vfree(buf->vaddr);
- kfree(buf);
- }
-}
-
-static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
- unsigned long size, int write)
-{
- struct vb2_vmalloc_buf *buf;
- unsigned long first, last;
- int n_pages, offset;
- struct vm_area_struct *vma;
- dma_addr_t physp;
-
- buf = kzalloc(sizeof(*buf), GFP_KERNEL);
- if (!buf)
- return NULL;
-
- buf->write = write;
- offset = vaddr & ~PAGE_MASK;
- buf->size = size;
-
-
- vma = find_vma(current->mm, vaddr);
- if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
- if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
- goto fail_pages_array_alloc;
- buf->vma = vma;
- buf->vaddr = ioremap_nocache(physp, size);
- if (!buf->vaddr)
- goto fail_pages_array_alloc;
- } else {
- first = vaddr >> PAGE_SHIFT;
- last = (vaddr + size - 1) >> PAGE_SHIFT;
- buf->n_pages = last - first + 1;
- buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
- GFP_KERNEL);
- if (!buf->pages)
- goto fail_pages_array_alloc;
-
- /* current->mm->mmap_sem is taken by videobuf2 core */
- n_pages = get_user_pages(current, current->mm,
- vaddr & PAGE_MASK, buf->n_pages,
- write, 1, /* force */
- buf->pages, NULL);
- if (n_pages != buf->n_pages)
- goto fail_get_user_pages;
-
- buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
- PAGE_KERNEL);
- if (!buf->vaddr)
- goto fail_get_user_pages;
- }
-
- buf->vaddr += offset;
- return buf;
-
-fail_get_user_pages:
- pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
- buf->n_pages);
- while (--n_pages >= 0)
- put_page(buf->pages[n_pages]);
- kfree(buf->pages);
-
-fail_pages_array_alloc:
- kfree(buf);
-
- return NULL;
-}
-
-static void vb2_vmalloc_put_userptr(void *buf_priv)
-{
- struct vb2_vmalloc_buf *buf = buf_priv;
- unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
- unsigned int i;
-
- if (buf->pages) {
- if (vaddr)
- vm_unmap_ram((void *)vaddr, buf->n_pages);
- for (i = 0; i < buf->n_pages; ++i) {
- if (buf->write)
- set_page_dirty_lock(buf->pages[i]);
- put_page(buf->pages[i]);
- }
- kfree(buf->pages);
- } else {
- if (buf->vma)
- vb2_put_vma(buf->vma);
- iounmap(buf->vaddr);
- }
- kfree(buf);
-}
-
-static void *vb2_vmalloc_vaddr(void *buf_priv)
-{
- struct vb2_vmalloc_buf *buf = buf_priv;
-
- if (!buf->vaddr) {
- pr_err("Address of an unallocated plane requested "
- "or cannot map user pointer\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) {
- pr_err("No memory to map\n");
- return -EINVAL;
- }
-
- ret = remap_vmalloc_range(vma, buf->vaddr, 0);
- if (ret) {
- pr_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,
- .get_userptr = vb2_vmalloc_get_userptr,
- .put_userptr = vb2_vmalloc_put_userptr,
- .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 <pawel@osciak.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
deleted file mode 100644
index aae1720b2f2d..000000000000
--- a/drivers/media/video/vino.c
+++ /dev/null
@@ -1,4349 +0,0 @@
-/*
- * Driver for the VINO (Video In No Out) system found in SGI Indys.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi>
- *
- * Based on the previous version of the driver for 2.4 kernels by:
- * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
- *
- * v4l2_device/v4l2_subdev conversion by:
- * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
- *
- * Note: this conversion is untested! Please contact the linux-media
- * mailinglist if you can test this, together with the test results.
- */
-
-/*
- * TODO:
- * - remove "mark pages reserved-hacks" from memory allocation code
- * and implement fault()
- * - check decimation, calculating and reporting image size when
- * using decimation
- * - implement read(), user mode buffers and overlay (?)
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/time.h>
-#include <linux/kmod.h>
-
-#include <linux/i2c.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <linux/mutex.h>
-
-#include <asm/paccess.h>
-#include <asm/io.h>
-#include <asm/sgi/ip22.h>
-#include <asm/sgi/mc.h>
-
-#include "vino.h"
-#include "saa7191.h"
-#include "indycam.h"
-
-/* Uncomment the following line to get lots and lots of (mostly useless)
- * debug info.
- * Note that the debug output also slows down the driver significantly */
-// #define VINO_DEBUG
-// #define VINO_DEBUG_INT
-
-#define VINO_MODULE_VERSION "0.0.7"
-
-MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver");
-MODULE_VERSION(VINO_MODULE_VERSION);
-MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
-MODULE_LICENSE("GPL");
-
-#ifdef VINO_DEBUG
-#define dprintk(x...) printk("VINO: " x);
-#else
-#define dprintk(x...)
-#endif
-
-#define VINO_NO_CHANNEL 0
-#define VINO_CHANNEL_A 1
-#define VINO_CHANNEL_B 2
-
-#define VINO_PAL_WIDTH 768
-#define VINO_PAL_HEIGHT 576
-#define VINO_NTSC_WIDTH 640
-#define VINO_NTSC_HEIGHT 480
-
-#define VINO_MIN_WIDTH 32
-#define VINO_MIN_HEIGHT 32
-
-#define VINO_CLIPPING_START_ODD_D1 1
-#define VINO_CLIPPING_START_ODD_PAL 15
-#define VINO_CLIPPING_START_ODD_NTSC 12
-
-#define VINO_CLIPPING_START_EVEN_D1 2
-#define VINO_CLIPPING_START_EVEN_PAL 15
-#define VINO_CLIPPING_START_EVEN_NTSC 12
-
-#define VINO_INPUT_CHANNEL_COUNT 3
-
-/* the number is the index for vino_inputs */
-#define VINO_INPUT_NONE -1
-#define VINO_INPUT_COMPOSITE 0
-#define VINO_INPUT_SVIDEO 1
-#define VINO_INPUT_D1 2
-
-#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE)
-
-#define VINO_FIFO_THRESHOLD_DEFAULT 16
-
-#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \
- * VINO_PAL_HEIGHT * 4 \
- + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1))
-
-#define VINO_FRAMEBUFFER_COUNT_MAX 8
-
-#define VINO_FRAMEBUFFER_UNUSED 0
-#define VINO_FRAMEBUFFER_IN_USE 1
-#define VINO_FRAMEBUFFER_READY 2
-
-#define VINO_QUEUE_ERROR -1
-#define VINO_QUEUE_MAGIC 0x20050125
-
-#define VINO_MEMORY_NONE 0
-#define VINO_MEMORY_MMAP 1
-#define VINO_MEMORY_USERPTR 2
-
-#define VINO_DUMMY_DESC_COUNT 4
-#define VINO_DESC_FETCH_DELAY 5 /* microseconds */
-
-#define VINO_MAX_FRAME_SKIP_COUNT 128
-
-/* the number is the index for vino_data_formats */
-#define VINO_DATA_FMT_NONE -1
-#define VINO_DATA_FMT_GREY 0
-#define VINO_DATA_FMT_RGB332 1
-#define VINO_DATA_FMT_RGB32 2
-#define VINO_DATA_FMT_YUV 3
-
-#define VINO_DATA_FMT_COUNT 4
-
-/* the number is the index for vino_data_norms */
-#define VINO_DATA_NORM_NONE -1
-#define VINO_DATA_NORM_NTSC 0
-#define VINO_DATA_NORM_PAL 1
-#define VINO_DATA_NORM_SECAM 2
-#define VINO_DATA_NORM_D1 3
-
-#define VINO_DATA_NORM_COUNT 4
-
-/* I2C controller flags */
-#define SGI_I2C_FORCE_IDLE (0 << 0)
-#define SGI_I2C_NOT_IDLE (1 << 0)
-#define SGI_I2C_WRITE (0 << 1)
-#define SGI_I2C_READ (1 << 1)
-#define SGI_I2C_RELEASE_BUS (0 << 2)
-#define SGI_I2C_HOLD_BUS (1 << 2)
-#define SGI_I2C_XFER_DONE (0 << 4)
-#define SGI_I2C_XFER_BUSY (1 << 4)
-#define SGI_I2C_ACK (0 << 5)
-#define SGI_I2C_NACK (1 << 5)
-#define SGI_I2C_BUS_OK (0 << 7)
-#define SGI_I2C_BUS_ERR (1 << 7)
-
-/* Internal data structure definitions */
-
-struct vino_input {
- char *name;
- v4l2_std_id std;
-};
-
-struct vino_clipping {
- unsigned int left, right, top, bottom;
-};
-
-struct vino_data_format {
- /* the description */
- char *description;
- /* bytes per pixel */
- unsigned int bpp;
- /* V4L2 fourcc code */
- __u32 pixelformat;
- /* V4L2 colorspace (duh!) */
- enum v4l2_colorspace colorspace;
-};
-
-struct vino_data_norm {
- char *description;
- unsigned int width, height;
- struct vino_clipping odd;
- struct vino_clipping even;
-
- v4l2_std_id std;
- unsigned int fps_min, fps_max;
- __u32 framelines;
-};
-
-struct vino_descriptor_table {
- /* the number of PAGE_SIZE sized pages in the buffer */
- unsigned int page_count;
- /* virtual (kmalloc'd) pointers to the actual data
- * (in PAGE_SIZE chunks, used with mmap streaming) */
- unsigned long *virtual;
-
- /* cpu address for the VINO descriptor table
- * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
- unsigned long *dma_cpu;
- /* dma address for the VINO descriptor table
- * (contains DMA addresses, VINO_PAGE_SIZE chunks) */
- dma_addr_t dma;
-};
-
-struct vino_framebuffer {
- /* identifier nubmer */
- unsigned int id;
- /* the length of the whole buffer */
- unsigned int size;
- /* the length of actual data in buffer */
- unsigned int data_size;
- /* the data format */
- unsigned int data_format;
- /* the state of buffer data */
- unsigned int state;
- /* is the buffer mapped in user space? */
- unsigned int map_count;
- /* memory offset for mmap() */
- unsigned int offset;
- /* frame counter */
- unsigned int frame_counter;
- /* timestamp (written when image capture finishes) */
- struct timeval timestamp;
-
- struct vino_descriptor_table desc_table;
-
- spinlock_t state_lock;
-};
-
-struct vino_framebuffer_fifo {
- unsigned int length;
-
- unsigned int used;
- unsigned int head;
- unsigned int tail;
-
- unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX];
-};
-
-struct vino_framebuffer_queue {
- unsigned int magic;
-
- /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */
- unsigned int type;
- unsigned int length;
-
- /* data field of in and out contain index numbers for buffer */
- struct vino_framebuffer_fifo in;
- struct vino_framebuffer_fifo out;
-
- struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX];
-
- spinlock_t queue_lock;
- struct mutex queue_mutex;
- wait_queue_head_t frame_wait_queue;
-};
-
-struct vino_interrupt_data {
- struct timeval timestamp;
- unsigned int frame_counter;
- unsigned int skip_count;
- unsigned int skip;
-};
-
-struct vino_channel_settings {
- unsigned int channel;
-
- int input;
- unsigned int data_format;
- unsigned int data_norm;
- struct vino_clipping clipping;
- unsigned int decimation;
- unsigned int line_size;
- unsigned int alpha;
- unsigned int fps;
- unsigned int framert_reg;
-
- unsigned int fifo_threshold;
-
- struct vino_framebuffer_queue fb_queue;
-
- /* number of the current field */
- unsigned int field;
-
- /* read in progress */
- int reading;
- /* streaming is active */
- int streaming;
- /* the driver is currently processing the queue */
- int capturing;
-
- struct mutex mutex;
- spinlock_t capture_lock;
-
- unsigned int users;
-
- struct vino_interrupt_data int_data;
-
- /* V4L support */
- struct video_device *vdev;
-};
-
-struct vino_settings {
- struct v4l2_device v4l2_dev;
- struct vino_channel_settings a;
- struct vino_channel_settings b;
-
- /* the channel which owns this client:
- * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
- unsigned int decoder_owner;
- struct v4l2_subdev *decoder;
- unsigned int camera_owner;
- struct v4l2_subdev *camera;
-
- /* a lock for vino register access */
- spinlock_t vino_lock;
- /* a lock for channel input changes */
- spinlock_t input_lock;
-
- unsigned long dummy_page;
- struct vino_descriptor_table dummy_desc_table;
-};
-
-/* Module parameters */
-
-/*
- * Using vino_pixel_conversion the ABGR32-format pixels supplied
- * by the VINO chip can be converted to more common formats
- * like RGBA32 (or probably RGB24 in the future). This way we
- * can give out data that can be specified correctly with
- * the V4L2-definitions.
- *
- * The pixel format is specified as RGBA32 when no conversion
- * is used.
- *
- * Note that this only affects the 32-bit bit depth.
- *
- * Use non-zero value to enable conversion.
- */
-static int vino_pixel_conversion;
-
-module_param_named(pixelconv, vino_pixel_conversion, int, 0);
-
-MODULE_PARM_DESC(pixelconv,
- "enable pixel conversion (non-zero value enables)");
-
-/* Internal data structures */
-
-static struct sgi_vino *vino;
-
-static struct vino_settings *vino_drvdata;
-
-#define camera_call(o, f, args...) \
- v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
-#define decoder_call(o, f, args...) \
- v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
-
-static const char *vino_driver_name = "vino";
-static const char *vino_driver_description = "SGI VINO";
-static const char *vino_bus_name = "GIO64 bus";
-static const char *vino_vdev_name_a = "SGI VINO Channel A";
-static const char *vino_vdev_name_b = "SGI VINO Channel B";
-
-static void vino_capture_tasklet(unsigned long channel);
-
-DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A);
-DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B);
-
-static const struct vino_input vino_inputs[] = {
- {
- .name = "Composite",
- .std = V4L2_STD_NTSC | V4L2_STD_PAL
- | V4L2_STD_SECAM,
- }, {
- .name = "S-Video",
- .std = V4L2_STD_NTSC | V4L2_STD_PAL
- | V4L2_STD_SECAM,
- }, {
- .name = "D1/IndyCam",
- .std = V4L2_STD_NTSC,
- }
-};
-
-static const struct vino_data_format vino_data_formats[] = {
- {
- .description = "8-bit greyscale",
- .bpp = 1,
- .pixelformat = V4L2_PIX_FMT_GREY,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- }, {
- .description = "8-bit dithered RGB 3-3-2",
- .bpp = 1,
- .pixelformat = V4L2_PIX_FMT_RGB332,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .description = "32-bit RGB",
- .bpp = 4,
- .pixelformat = V4L2_PIX_FMT_RGB32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- }, {
- .description = "YUV 4:2:2",
- .bpp = 2,
- .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- }
-};
-
-static const struct vino_data_norm vino_data_norms[] = {
- {
- .description = "NTSC",
- .std = V4L2_STD_NTSC,
- .fps_min = 6,
- .fps_max = 30,
- .framelines = 525,
- .width = VINO_NTSC_WIDTH,
- .height = VINO_NTSC_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_NTSC,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_NTSC
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_NTSC,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_NTSC
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- }, {
- .description = "PAL",
- .std = V4L2_STD_PAL,
- .fps_min = 5,
- .fps_max = 25,
- .framelines = 625,
- .width = VINO_PAL_WIDTH,
- .height = VINO_PAL_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- }, {
- .description = "SECAM",
- .std = V4L2_STD_SECAM,
- .fps_min = 5,
- .fps_max = 25,
- .framelines = 625,
- .width = VINO_PAL_WIDTH,
- .height = VINO_PAL_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_PAL,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_PAL
- + VINO_PAL_HEIGHT / 2 - 1,
- .right = VINO_PAL_WIDTH,
- },
- }, {
- .description = "NTSC/D1",
- .std = V4L2_STD_NTSC,
- .fps_min = 6,
- .fps_max = 30,
- .framelines = 525,
- .width = VINO_NTSC_WIDTH,
- .height = VINO_NTSC_HEIGHT,
- .odd = {
- .top = VINO_CLIPPING_START_ODD_D1,
- .left = 0,
- .bottom = VINO_CLIPPING_START_ODD_D1
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- .even = {
- .top = VINO_CLIPPING_START_EVEN_D1,
- .left = 0,
- .bottom = VINO_CLIPPING_START_EVEN_D1
- + VINO_NTSC_HEIGHT / 2 - 1,
- .right = VINO_NTSC_WIDTH,
- },
- }
-};
-
-#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9
-
-struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic Gain Control",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = INDYCAM_AGC_DEFAULT,
- }, {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Automatic White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = INDYCAM_AWB_DEFAULT,
- }, {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = INDYCAM_GAIN_MIN,
- .maximum = INDYCAM_GAIN_MAX,
- .step = 1,
- .default_value = INDYCAM_GAIN_DEFAULT,
- }, {
- .id = INDYCAM_CONTROL_RED_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Saturation",
- .minimum = INDYCAM_RED_SATURATION_MIN,
- .maximum = INDYCAM_RED_SATURATION_MAX,
- .step = 1,
- .default_value = INDYCAM_RED_SATURATION_DEFAULT,
- }, {
- .id = INDYCAM_CONTROL_BLUE_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Saturation",
- .minimum = INDYCAM_BLUE_SATURATION_MIN,
- .maximum = INDYCAM_BLUE_SATURATION_MAX,
- .step = 1,
- .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
- }, {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = INDYCAM_RED_BALANCE_MIN,
- .maximum = INDYCAM_RED_BALANCE_MAX,
- .step = 1,
- .default_value = INDYCAM_RED_BALANCE_DEFAULT,
- }, {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = INDYCAM_BLUE_BALANCE_MIN,
- .maximum = INDYCAM_BLUE_BALANCE_MAX,
- .step = 1,
- .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
- }, {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Shutter Control",
- .minimum = INDYCAM_SHUTTER_MIN,
- .maximum = INDYCAM_SHUTTER_MAX,
- .step = 1,
- .default_value = INDYCAM_SHUTTER_DEFAULT,
- }, {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = INDYCAM_GAMMA_MIN,
- .maximum = INDYCAM_GAMMA_MAX,
- .step = 1,
- .default_value = INDYCAM_GAMMA_DEFAULT,
- }
-};
-
-#define VINO_SAA7191_V4L2_CONTROL_COUNT 9
-
-struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = SAA7191_HUE_MIN,
- .maximum = SAA7191_HUE_MAX,
- .step = 1,
- .default_value = SAA7191_HUE_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_BANDPASS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Bandpass",
- .minimum = SAA7191_BANDPASS_MIN,
- .maximum = SAA7191_BANDPASS_MAX,
- .step = 1,
- .default_value = SAA7191_BANDPASS_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Bandpass Weight",
- .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
- .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
- .step = 1,
- .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_CORING,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "HF Luminance Coring",
- .minimum = SAA7191_CORING_MIN,
- .maximum = SAA7191_CORING_MAX,
- .step = 1,
- .default_value = SAA7191_CORING_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_FORCE_COLOUR,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Force Colour",
- .minimum = SAA7191_FORCE_COLOUR_MIN,
- .maximum = SAA7191_FORCE_COLOUR_MAX,
- .step = 1,
- .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_CHROMA_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Chrominance Gain Control",
- .minimum = SAA7191_CHROMA_GAIN_MIN,
- .maximum = SAA7191_CHROMA_GAIN_MAX,
- .step = 1,
- .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_VTRC,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "VTR Time Constant",
- .minimum = SAA7191_VTRC_MIN,
- .maximum = SAA7191_VTRC_MAX,
- .step = 1,
- .default_value = SAA7191_VTRC_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_LUMA_DELAY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Luminance Delay Compensation",
- .minimum = SAA7191_LUMA_DELAY_MIN,
- .maximum = SAA7191_LUMA_DELAY_MAX,
- .step = 1,
- .default_value = SAA7191_LUMA_DELAY_DEFAULT,
- }, {
- .id = SAA7191_CONTROL_VNR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Vertical Noise Reduction",
- .minimum = SAA7191_VNR_MIN,
- .maximum = SAA7191_VNR_MAX,
- .step = 1,
- .default_value = SAA7191_VNR_DEFAULT,
- }
-};
-
-/* VINO framebuffer/DMA descriptor management */
-
-static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
- unsigned int count)
-{
- unsigned int i;
-
- dprintk("vino_free_buffer_with_count(): count = %d\n", count);
-
- for (i = 0; i < count; i++) {
- ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- dma_unmap_single(NULL,
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
- PAGE_SIZE, DMA_FROM_DEVICE);
- free_page(fb->desc_table.virtual[i]);
- }
-
- dma_free_coherent(NULL,
- VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) *
- sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu,
- fb->desc_table.dma);
- kfree(fb->desc_table.virtual);
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-}
-
-static void vino_free_buffer(struct vino_framebuffer *fb)
-{
- vino_free_buffer_with_count(fb, fb->desc_table.page_count);
-}
-
-static int vino_allocate_buffer(struct vino_framebuffer *fb,
- unsigned int size)
-{
- unsigned int count, i, j;
- int ret = 0;
-
- dprintk("vino_allocate_buffer():\n");
-
- if (size < 1)
- return -EINVAL;
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-
- count = ((size / PAGE_SIZE) + 4) & ~3;
-
- dprintk("vino_allocate_buffer(): size = %d, count = %d\n",
- size, count);
-
- /* allocate memory for table with virtual (page) addresses */
- fb->desc_table.virtual =
- kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
- if (!fb->desc_table.virtual)
- return -ENOMEM;
-
- /* allocate memory for table with dma addresses
- * (has space for four extra descriptors) */
- fb->desc_table.dma_cpu =
- dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
- sizeof(dma_addr_t), &fb->desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.dma_cpu) {
- ret = -ENOMEM;
- goto out_free_virtual;
- }
-
- /* allocate pages for the buffer and acquire the according
- * dma addresses */
- for (i = 0; i < count; i++) {
- dma_addr_t dma_data_addr;
-
- fb->desc_table.virtual[i] =
- get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.virtual[i]) {
- ret = -ENOBUFS;
- break;
- }
-
- dma_data_addr =
- dma_map_single(NULL,
- (void *)fb->desc_table.virtual[i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-
- for (j = 0; j < VINO_PAGE_RATIO; j++) {
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
- dma_data_addr + VINO_PAGE_SIZE * j;
- }
-
- SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- }
-
- /* page_count needs to be set anyway, because the descriptor table has
- * been allocated according to this number */
- fb->desc_table.page_count = count;
-
- if (ret) {
- /* the descriptor with index i doesn't contain
- * a valid address yet */
- vino_free_buffer_with_count(fb, i);
- return ret;
- }
-
- //fb->size = size;
- fb->size = count * PAGE_SIZE;
- fb->data_format = VINO_DATA_FMT_NONE;
-
- /* set the dma stop-bit for the last (count+1)th descriptor */
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
- return 0;
-
- out_free_virtual:
- kfree(fb->desc_table.virtual);
- return ret;
-}
-
-#if 0
-/* user buffers not fully implemented yet */
-static int vino_prepare_user_buffer(struct vino_framebuffer *fb,
- void *user,
- unsigned int size)
-{
- unsigned int count, i, j;
- int ret = 0;
-
- dprintk("vino_prepare_user_buffer():\n");
-
- if (size < 1)
- return -EINVAL;
-
- memset(fb, 0, sizeof(struct vino_framebuffer));
-
- count = ((size / PAGE_SIZE)) & ~3;
-
- dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n",
- size, count);
-
- /* allocate memory for table with virtual (page) addresses */
- fb->desc_table.virtual = (unsigned long *)
- kmalloc(count * sizeof(unsigned long), GFP_KERNEL);
- if (!fb->desc_table.virtual)
- return -ENOMEM;
-
- /* allocate memory for table with dma addresses
- * (has space for four extra descriptors) */
- fb->desc_table.dma_cpu =
- dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) *
- sizeof(dma_addr_t), &fb->desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.dma_cpu) {
- ret = -ENOMEM;
- goto out_free_virtual;
- }
-
- /* allocate pages for the buffer and acquire the according
- * dma addresses */
- for (i = 0; i < count; i++) {
- dma_addr_t dma_data_addr;
-
- fb->desc_table.virtual[i] =
- get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!fb->desc_table.virtual[i]) {
- ret = -ENOBUFS;
- break;
- }
-
- dma_data_addr =
- dma_map_single(NULL,
- (void *)fb->desc_table.virtual[i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-
- for (j = 0; j < VINO_PAGE_RATIO; j++) {
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] =
- dma_data_addr + VINO_PAGE_SIZE * j;
- }
-
- SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i]));
- }
-
- /* page_count needs to be set anyway, because the descriptor table has
- * been allocated according to this number */
- fb->desc_table.page_count = count;
-
- if (ret) {
- /* the descriptor with index i doesn't contain
- * a valid address yet */
- vino_free_buffer_with_count(fb, i);
- return ret;
- }
-
- //fb->size = size;
- fb->size = count * PAGE_SIZE;
-
- /* set the dma stop-bit for the last (count+1)th descriptor */
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP;
- return 0;
-
- out_free_virtual:
- kfree(fb->desc_table.virtual);
- return ret;
-}
-#endif
-
-static void vino_sync_buffer(struct vino_framebuffer *fb)
-{
- int i;
-
- dprintk("vino_sync_buffer():\n");
-
- for (i = 0; i < fb->desc_table.page_count; i++)
- dma_sync_single_for_cpu(NULL,
- fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i],
- PAGE_SIZE, DMA_FROM_DEVICE);
-}
-
-/* Framebuffer fifo functions (need to be locked externally) */
-
-static inline void vino_fifo_init(struct vino_framebuffer_fifo *f,
- unsigned int length)
-{
- f->length = 0;
- f->used = 0;
- f->head = 0;
- f->tail = 0;
-
- if (length > VINO_FRAMEBUFFER_COUNT_MAX)
- length = VINO_FRAMEBUFFER_COUNT_MAX;
-
- f->length = length;
-}
-
-/* returns true/false */
-static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f,
- unsigned int id)
-{
- unsigned int i;
-
- for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) {
- if (f->data[i] == id)
- return 1;
- }
-
- return 0;
-}
-
-#if 0
-/* returns true/false */
-static inline int vino_fifo_full(struct vino_framebuffer_fifo *f)
-{
- return (f->used == f->length);
-}
-#endif
-
-static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f)
-{
- return f->used;
-}
-
-static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id)
-{
- if (id >= f->length) {
- return VINO_QUEUE_ERROR;
- }
-
- if (vino_fifo_has_id(f, id)) {
- return VINO_QUEUE_ERROR;
- }
-
- if (f->used < f->length) {
- f->data[f->tail] = id;
- f->tail = (f->tail + 1) % f->length;
- f->used++;
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
- if (f->used > 0) {
- *id = f->data[f->head];
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id)
-{
- if (f->used > 0) {
- *id = f->data[f->head];
- f->head = (f->head + 1) % f->length;
- f->used--;
- } else {
- return VINO_QUEUE_ERROR;
- }
-
- return 0;
-}
-
-/* Framebuffer queue functions */
-
-/* execute with queue_lock locked */
-static void vino_queue_free_with_count(struct vino_framebuffer_queue *q,
- unsigned int length)
-{
- unsigned int i;
-
- q->length = 0;
- memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo));
- memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo));
- for (i = 0; i < length; i++) {
- dprintk("vino_queue_free_with_count(): freeing buffer %d\n",
- i);
- vino_free_buffer(q->buffer[i]);
- kfree(q->buffer[i]);
- }
-
- q->type = VINO_MEMORY_NONE;
- q->magic = 0;
-}
-
-static void vino_queue_free(struct vino_framebuffer_queue *q)
-{
- dprintk("vino_queue_free():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC)
- return;
- if (q->type != VINO_MEMORY_MMAP)
- return;
-
- mutex_lock(&q->queue_mutex);
-
- vino_queue_free_with_count(q, q->length);
-
- mutex_unlock(&q->queue_mutex);
-}
-
-static int vino_queue_init(struct vino_framebuffer_queue *q,
- unsigned int *length)
-{
- unsigned int i;
- int ret = 0;
-
- dprintk("vino_queue_init(): length = %d\n", *length);
-
- if (q->magic == VINO_QUEUE_MAGIC) {
- dprintk("vino_queue_init(): queue already initialized!\n");
- return -EINVAL;
- }
-
- if (q->type != VINO_MEMORY_NONE) {
- dprintk("vino_queue_init(): queue already initialized!\n");
- return -EINVAL;
- }
-
- if (*length < 1)
- return -EINVAL;
-
- mutex_lock(&q->queue_mutex);
-
- if (*length > VINO_FRAMEBUFFER_COUNT_MAX)
- *length = VINO_FRAMEBUFFER_COUNT_MAX;
-
- q->length = 0;
-
- for (i = 0; i < *length; i++) {
- dprintk("vino_queue_init(): allocating buffer %d\n", i);
- q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer),
- GFP_KERNEL);
- if (!q->buffer[i]) {
- dprintk("vino_queue_init(): kmalloc() failed\n");
- ret = -ENOMEM;
- break;
- }
-
- ret = vino_allocate_buffer(q->buffer[i],
- VINO_FRAMEBUFFER_SIZE);
- if (ret) {
- kfree(q->buffer[i]);
- dprintk("vino_queue_init(): "
- "vino_allocate_buffer() failed\n");
- break;
- }
-
- q->buffer[i]->id = i;
- if (i > 0) {
- q->buffer[i]->offset = q->buffer[i - 1]->offset +
- q->buffer[i - 1]->size;
- } else {
- q->buffer[i]->offset = 0;
- }
-
- spin_lock_init(&q->buffer[i]->state_lock);
-
- dprintk("vino_queue_init(): buffer = %d, offset = %d, "
- "size = %d\n", i, q->buffer[i]->offset,
- q->buffer[i]->size);
- }
-
- if (ret) {
- vino_queue_free_with_count(q, i);
- *length = 0;
- } else {
- q->length = *length;
- vino_fifo_init(&q->in, q->length);
- vino_fifo_init(&q->out, q->length);
- q->type = VINO_MEMORY_MMAP;
- q->magic = VINO_QUEUE_MAGIC;
- }
-
- mutex_unlock(&q->queue_mutex);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_add(struct
- vino_framebuffer_queue *q,
- unsigned int id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned int total;
- unsigned long flags;
-
- dprintk("vino_queue_add(): id = %d\n", id);
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (id >= q->length)
- goto out;
-
- /* not needed?: if (vino_fifo_full(&q->out)) {
- goto out;
- }*/
- /* check that outgoing queue isn't already full
- * (or that it won't become full) */
- total = vino_fifo_get_used(&q->in) +
- vino_fifo_get_used(&q->out);
- if (total >= q->length)
- goto out;
-
- if (vino_fifo_enqueue(&q->in, id))
- goto out;
-
- ret = q->buffer[id];
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_transfer(struct
- vino_framebuffer_queue *q)
-{
- struct vino_framebuffer *ret = NULL;
- struct vino_framebuffer *fb;
- int id;
- unsigned long flags;
-
- dprintk("vino_queue_transfer():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- // now this actually removes an entry from the incoming queue
- if (vino_fifo_dequeue(&q->in, &id)) {
- goto out;
- }
-
- dprintk("vino_queue_transfer(): id = %d\n", id);
- fb = q->buffer[id];
-
- // we have already checked that the outgoing queue is not full, but...
- if (vino_fifo_enqueue(&q->out, id)) {
- printk(KERN_ERR "vino_queue_transfer(): "
- "outgoing queue is full, this shouldn't happen!\n");
- goto out;
- }
-
- ret = fb;
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* returns true/false */
-static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- ret = vino_fifo_has_id(&q->in, id);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* returns true/false */
-static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- ret = vino_fifo_has_id(&q->out, id);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static int vino_queue_get_incoming(struct vino_framebuffer_queue *q,
- unsigned int *used)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *used = vino_fifo_get_used(&q->in);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q,
- unsigned int *used)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *used = vino_fifo_get_used(&q->out);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-#if 0
-static int vino_queue_get_total(struct vino_framebuffer_queue *q,
- unsigned int *total)
-{
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return VINO_QUEUE_ERROR;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0) {
- ret = VINO_QUEUE_ERROR;
- goto out;
- }
-
- *total = vino_fifo_get_used(&q->in) +
- vino_fifo_get_used(&q->out);
-
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-#endif
-
-static struct vino_framebuffer *vino_queue_peek(struct
- vino_framebuffer_queue *q,
- unsigned int *id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (vino_fifo_peek(&q->in, id)) {
- goto out;
- }
-
- ret = q->buffer[*id];
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct vino_framebuffer *vino_queue_remove(struct
- vino_framebuffer_queue *q,
- unsigned int *id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
- dprintk("vino_queue_remove():\n");
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (vino_fifo_dequeue(&q->out, id)) {
- goto out;
- }
-
- dprintk("vino_queue_remove(): id = %d\n", *id);
- ret = q->buffer[*id];
-out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static struct
-vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q,
- unsigned int id)
-{
- struct vino_framebuffer *ret = NULL;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
-
- if (q->length == 0)
- goto out;
-
- if (id >= q->length)
- goto out;
-
- ret = q->buffer[id];
- out:
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q)
-{
- unsigned int length = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return length;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
- length = q->length;
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return length;
-}
-
-static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q)
-{
- unsigned int i;
- int ret = 0;
- unsigned long flags;
-
- if (q->magic != VINO_QUEUE_MAGIC) {
- return ret;
- }
-
- spin_lock_irqsave(&q->queue_lock, flags);
- for (i = 0; i < q->length; i++) {
- if (q->buffer[i]->map_count > 0) {
- ret = 1;
- break;
- }
- }
- spin_unlock_irqrestore(&q->queue_lock, flags);
-
- return ret;
-}
-
-/* VINO functions */
-
-/* execute with input_lock locked */
-static void vino_update_line_size(struct vino_channel_settings *vcs)
-{
- unsigned int w = vcs->clipping.right - vcs->clipping.left;
- unsigned int d = vcs->decimation;
- unsigned int bpp = vino_data_formats[vcs->data_format].bpp;
- unsigned int lsize;
-
- dprintk("update_line_size(): before: w = %d, d = %d, "
- "line_size = %d\n", w, d, vcs->line_size);
-
- /* line size must be multiple of 8 bytes */
- lsize = (bpp * (w / d)) & ~7;
- w = (lsize / bpp) * d;
-
- vcs->clipping.right = vcs->clipping.left + w;
- vcs->line_size = lsize;
-
- dprintk("update_line_size(): after: w = %d, d = %d, "
- "line_size = %d\n", w, d, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static void vino_set_clipping(struct vino_channel_settings *vcs,
- unsigned int x, unsigned int y,
- unsigned int w, unsigned int h)
-{
- unsigned int maxwidth, maxheight;
- unsigned int d;
-
- maxwidth = vino_data_norms[vcs->data_norm].width;
- maxheight = vino_data_norms[vcs->data_norm].height;
- d = vcs->decimation;
-
- y &= ~1; /* odd/even fields */
-
- if (x > maxwidth) {
- x = 0;
- }
- if (y > maxheight) {
- y = 0;
- }
-
- if (((w / d) < VINO_MIN_WIDTH)
- || ((h / d) < VINO_MIN_HEIGHT)) {
- w = VINO_MIN_WIDTH * d;
- h = VINO_MIN_HEIGHT * d;
- }
-
- if ((x + w) > maxwidth) {
- w = maxwidth - x;
- if ((w / d) < VINO_MIN_WIDTH)
- x = maxwidth - VINO_MIN_WIDTH * d;
- }
- if ((y + h) > maxheight) {
- h = maxheight - y;
- if ((h / d) < VINO_MIN_HEIGHT)
- y = maxheight - VINO_MIN_HEIGHT * d;
- }
-
- vcs->clipping.left = x;
- vcs->clipping.top = y;
- vcs->clipping.right = x + w;
- vcs->clipping.bottom = y + h;
-
- vino_update_line_size(vcs);
-
- dprintk("clipping %d, %d, %d, %d / %d - %d\n",
- vcs->clipping.left, vcs->clipping.top, vcs->clipping.right,
- vcs->clipping.bottom, vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_clipping(struct vino_channel_settings *vcs)
-{
- vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width,
- vino_data_norms[vcs->data_norm].height);
-}
-
-/* execute with input_lock locked */
-static void vino_set_scaling(struct vino_channel_settings *vcs,
- unsigned int w, unsigned int h)
-{
- unsigned int x, y, curw, curh, d;
-
- x = vcs->clipping.left;
- y = vcs->clipping.top;
- curw = vcs->clipping.right - vcs->clipping.left;
- curh = vcs->clipping.bottom - vcs->clipping.top;
-
- d = max(curw / w, curh / h);
-
- dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n",
- w, h, curw, curh, d);
-
- if (d < 1) {
- d = 1;
- } else if (d > 8) {
- d = 8;
- }
-
- vcs->decimation = d;
- vino_set_clipping(vcs, x, y, w * d, h * d);
-
- dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left,
- vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom,
- vcs->decimation, vcs->line_size);
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_scaling(struct vino_channel_settings *vcs)
-{
- vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left,
- vcs->clipping.bottom - vcs->clipping.top);
-}
-
-/* execute with input_lock locked */
-static void vino_set_framerate(struct vino_channel_settings *vcs,
- unsigned int fps)
-{
- unsigned int mask;
-
- switch (vcs->data_norm) {
- case VINO_DATA_NORM_NTSC:
- case VINO_DATA_NORM_D1:
- fps = (unsigned int)(fps / 6) * 6; // FIXME: round!
-
- if (fps < vino_data_norms[vcs->data_norm].fps_min)
- fps = vino_data_norms[vcs->data_norm].fps_min;
- if (fps > vino_data_norms[vcs->data_norm].fps_max)
- fps = vino_data_norms[vcs->data_norm].fps_max;
-
- switch (fps) {
- case 6:
- mask = 0x003;
- break;
- case 12:
- mask = 0x0c3;
- break;
- case 18:
- mask = 0x333;
- break;
- case 24:
- mask = 0x3ff;
- break;
- case 30:
- mask = 0xfff;
- break;
- default:
- mask = VINO_FRAMERT_FULL;
- }
- vcs->framert_reg = VINO_FRAMERT_RT(mask);
- break;
- case VINO_DATA_NORM_PAL:
- case VINO_DATA_NORM_SECAM:
- fps = (unsigned int)(fps / 5) * 5; // FIXME: round!
-
- if (fps < vino_data_norms[vcs->data_norm].fps_min)
- fps = vino_data_norms[vcs->data_norm].fps_min;
- if (fps > vino_data_norms[vcs->data_norm].fps_max)
- fps = vino_data_norms[vcs->data_norm].fps_max;
-
- switch (fps) {
- case 5:
- mask = 0x003;
- break;
- case 10:
- mask = 0x0c3;
- break;
- case 15:
- mask = 0x333;
- break;
- case 20:
- mask = 0x0ff;
- break;
- case 25:
- mask = 0x3ff;
- break;
- default:
- mask = VINO_FRAMERT_FULL;
- }
- vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL;
- break;
- }
-
- vcs->fps = fps;
-}
-
-/* execute with input_lock locked */
-static inline void vino_set_default_framerate(struct
- vino_channel_settings *vcs)
-{
- vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
-}
-
-/* VINO I2C bus functions */
-
-struct i2c_algo_sgi_data {
- void *data; /* private data for lowlevel routines */
- unsigned (*getctrl)(void *data);
- void (*setctrl)(void *data, unsigned val);
- unsigned (*rdata)(void *data);
- void (*wdata)(void *data, unsigned val);
-
- int xfer_timeout;
- int ack_timeout;
-};
-
-static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- for (i = 0; i < adap->xfer_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int wait_ack(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- if (wait_xfer_done(adap))
- return -ETIMEDOUT;
- for (i = 0; i < adap->ack_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
- return 0;
- udelay(1);
- }
-
- return -ETIMEDOUT;
-}
-
-static int force_idle(struct i2c_algo_sgi_data *adap)
-{
- int i;
-
- adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
- for (i = 0; i < adap->xfer_timeout; i++) {
- if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
- goto out;
- udelay(1);
- }
- return -ETIMEDOUT;
-out:
- if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
- return -EIO;
- return 0;
-}
-
-static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
- int rd)
-{
- if (rd)
- adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
- /* Check if bus is idle, eventually force it to do so */
- if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
- if (force_idle(adap))
- return -EIO;
- /* Write out the i2c chip address and specify operation */
- adap->setctrl(adap->data,
- SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
- if (rd)
- addr |= 1;
- adap->wdata(adap->data, addr);
- if (wait_ack(adap))
- return -EIO;
- return 0;
-}
-
-static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
- unsigned int len)
-{
- int i;
-
- adap->setctrl(adap->data,
- SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
- for (i = 0; i < len; i++) {
- if (wait_xfer_done(adap))
- return -EIO;
- buf[i] = adap->rdata(adap->data);
- }
- adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
-
- return 0;
-
-}
-
-static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
- unsigned int len)
-{
- int i;
-
- /* We are already in write state */
- for (i = 0; i < len; i++) {
- adap->wdata(adap->data, buf[i]);
- if (wait_ack(adap))
- return -EIO;
- }
- return 0;
-}
-
-static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
- int num)
-{
- struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
- struct i2c_msg *p;
- int i, err = 0;
-
- for (i = 0; !err && i < num; i++) {
- p = &msgs[i];
- err = do_address(adap, p->addr, p->flags & I2C_M_RD);
- if (err || !p->len)
- continue;
- if (p->flags & I2C_M_RD)
- err = i2c_read(adap, p->buf, p->len);
- else
- err = i2c_write(adap, p->buf, p->len);
- }
-
- return (err < 0) ? err : i;
-}
-
-static u32 sgi_func(struct i2c_adapter *adap)
-{
- return I2C_FUNC_SMBUS_EMUL;
-}
-
-static const struct i2c_algorithm sgi_algo = {
- .master_xfer = sgi_xfer,
- .functionality = sgi_func,
-};
-
-static unsigned i2c_vino_getctrl(void *data)
-{
- return vino->i2c_control;
-}
-
-static void i2c_vino_setctrl(void *data, unsigned val)
-{
- vino->i2c_control = val;
-}
-
-static unsigned i2c_vino_rdata(void *data)
-{
- return vino->i2c_data;
-}
-
-static void i2c_vino_wdata(void *data, unsigned val)
-{
- vino->i2c_data = val;
-}
-
-static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
- .getctrl = &i2c_vino_getctrl,
- .setctrl = &i2c_vino_setctrl,
- .rdata = &i2c_vino_rdata,
- .wdata = &i2c_vino_wdata,
- .xfer_timeout = 200,
- .ack_timeout = 1000,
-};
-
-static struct i2c_adapter vino_i2c_adapter = {
- .name = "VINO I2C bus",
- .algo = &sgi_algo,
- .algo_data = &i2c_sgi_vino_data,
- .owner = THIS_MODULE,
-};
-
-/*
- * Prepare VINO for DMA transfer...
- * (execute only with vino_lock and input_lock locked)
- */
-static int vino_dma_setup(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- u32 ctrl, intr;
- struct sgi_vino_channel *ch;
- const struct vino_data_norm *norm;
-
- dprintk("vino_dma_setup():\n");
-
- vcs->field = 0;
- fb->frame_counter = 0;
-
- ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
- norm = &vino_data_norms[vcs->data_norm];
-
- ch->page_index = 0;
- ch->line_count = 0;
-
- /* VINO line size register is set 8 bytes less than actual */
- ch->line_size = vcs->line_size - 8;
-
- /* let VINO know where to transfer data */
- ch->start_desc_tbl = fb->desc_table.dma;
- ch->next_4_desc = fb->desc_table.dma;
-
- /* give vino time to fetch the first four descriptors, 5 usec
- * should be more than enough time */
- udelay(VINO_DESC_FETCH_DELAY);
-
- dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n",
- ch->start_desc_tbl, ch->next_4_desc);
-
- /* set the alpha register */
- ch->alpha = vcs->alpha;
-
- /* set clipping registers */
- ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) |
- VINO_CLIP_EVEN(norm->even.top +
- vcs->clipping.top / 2) |
- VINO_CLIP_X(vcs->clipping.left);
- ch->clip_end = VINO_CLIP_ODD(norm->odd.top +
- vcs->clipping.bottom / 2 - 1) |
- VINO_CLIP_EVEN(norm->even.top +
- vcs->clipping.bottom / 2 - 1) |
- VINO_CLIP_X(vcs->clipping.right);
-
- /* set the size of actual content in the buffer (DECIMATION !) */
- fb->data_size = ((vcs->clipping.right - vcs->clipping.left) /
- vcs->decimation) *
- ((vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation) *
- vino_data_formats[vcs->data_format].bpp;
-
- ch->frame_rate = vcs->framert_reg;
-
- ctrl = vino->control;
- intr = vino->intr_status;
-
- if (vcs->channel == VINO_CHANNEL_A) {
- /* All interrupt conditions for this channel was cleared
- * so clear the interrupt status register and enable
- * interrupts */
- intr &= ~VINO_INTSTAT_A;
- ctrl |= VINO_CTRL_A_INT;
-
- /* enable synchronization */
- ctrl |= VINO_CTRL_A_SYNC_ENBL;
-
- /* enable frame assembly */
- ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL;
-
- /* set decimation used */
- if (vcs->decimation < 2)
- ctrl &= ~VINO_CTRL_A_DEC_ENBL;
- else {
- ctrl |= VINO_CTRL_A_DEC_ENBL;
- ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK;
- ctrl |= (vcs->decimation - 1) <<
- VINO_CTRL_A_DEC_SCALE_SHIFT;
- }
-
- /* select input interface */
- if (vcs->input == VINO_INPUT_D1)
- ctrl |= VINO_CTRL_A_SELECT;
- else
- ctrl &= ~VINO_CTRL_A_SELECT;
-
- /* palette */
- ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB |
- VINO_CTRL_A_DITHER);
- } else {
- intr &= ~VINO_INTSTAT_B;
- ctrl |= VINO_CTRL_B_INT;
-
- ctrl |= VINO_CTRL_B_SYNC_ENBL;
- ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL;
-
- if (vcs->decimation < 2)
- ctrl &= ~VINO_CTRL_B_DEC_ENBL;
- else {
- ctrl |= VINO_CTRL_B_DEC_ENBL;
- ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK;
- ctrl |= (vcs->decimation - 1) <<
- VINO_CTRL_B_DEC_SCALE_SHIFT;
-
- }
- if (vcs->input == VINO_INPUT_D1)
- ctrl |= VINO_CTRL_B_SELECT;
- else
- ctrl &= ~VINO_CTRL_B_SELECT;
-
- ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB |
- VINO_CTRL_B_DITHER);
- }
-
- /* set palette */
- fb->data_format = vcs->data_format;
-
- switch (vcs->data_format) {
- case VINO_DATA_FMT_GREY:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY;
- break;
- case VINO_DATA_FMT_RGB32:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_RGB : VINO_CTRL_B_RGB;
- break;
- case VINO_DATA_FMT_YUV:
- /* nothing needs to be done */
- break;
- case VINO_DATA_FMT_RGB332:
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER :
- VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER;
- break;
- }
-
- vino->intr_status = intr;
- vino->control = ctrl;
-
- return 0;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_start(struct vino_channel_settings *vcs)
-{
- u32 ctrl = vino->control;
-
- dprintk("vino_dma_start():\n");
- ctrl |= (vcs->channel == VINO_CHANNEL_A) ?
- VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL;
- vino->control = ctrl;
-}
-
-/* (execute only with vino_lock locked) */
-static inline void vino_dma_stop(struct vino_channel_settings *vcs)
-{
- u32 ctrl = vino->control;
-
- ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
- ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL;
- ctrl &= (vcs->channel == VINO_CHANNEL_A) ?
- ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT;
- vino->control = ctrl;
- dprintk("vino_dma_stop():\n");
-}
-
-/*
- * Load dummy page to descriptor registers. This prevents generating of
- * spurious interrupts. (execute only with vino_lock locked)
- */
-static void vino_clear_interrupt(struct vino_channel_settings *vcs)
-{
- struct sgi_vino_channel *ch;
-
- ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b;
-
- ch->page_index = 0;
- ch->line_count = 0;
-
- ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma;
- ch->next_4_desc = vino_drvdata->dummy_desc_table.dma;
-
- udelay(VINO_DESC_FETCH_DELAY);
- dprintk("channel %c clear interrupt condition\n",
- (vcs->channel == VINO_CHANNEL_A) ? 'A':'B');
-}
-
-static int vino_capture(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- int err = 0;
- unsigned long flags, flags2;
-
- spin_lock_irqsave(&fb->state_lock, flags);
-
- if (fb->state == VINO_FRAMEBUFFER_IN_USE)
- err = -EBUSY;
- fb->state = VINO_FRAMEBUFFER_IN_USE;
-
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- if (err)
- return err;
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
- spin_lock_irqsave(&vino_drvdata->input_lock, flags2);
-
- vino_dma_setup(vcs, fb);
- vino_dma_start(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2);
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
- return err;
-}
-
-static
-struct vino_framebuffer *vino_capture_enqueue(struct
- vino_channel_settings *vcs,
- unsigned int index)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
-
- dprintk("vino_capture_enqueue():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- fb = vino_queue_add(&vcs->fb_queue, index);
- if (fb == NULL) {
- dprintk("vino_capture_enqueue(): vino_queue_add() failed, "
- "queue full?\n");
- goto out;
- }
-out:
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return fb;
-}
-
-static int vino_capture_next(struct vino_channel_settings *vcs, int start)
-{
- struct vino_framebuffer *fb;
- unsigned int incoming, id;
- int err = 0;
- unsigned long flags;
-
- dprintk("vino_capture_next():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- if (start) {
- /* start capture only if capture isn't in progress already */
- if (vcs->capturing) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- return 0;
- }
-
- } else {
- /* capture next frame:
- * stop capture if capturing is not set */
- if (!vcs->capturing) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- return 0;
- }
- }
-
- err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (err) {
- dprintk("vino_capture_next(): vino_queue_get_incoming() "
- "failed\n");
- err = -EINVAL;
- goto out;
- }
- if (incoming == 0) {
- dprintk("vino_capture_next(): no buffers available\n");
- goto out;
- }
-
- fb = vino_queue_peek(&vcs->fb_queue, &id);
- if (fb == NULL) {
- dprintk("vino_capture_next(): vino_queue_peek() failed\n");
- err = -EINVAL;
- goto out;
- }
-
- if (start) {
- vcs->capturing = 1;
- }
-
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- err = vino_capture(vcs, fb);
-
- return err;
-
-out:
- vcs->capturing = 0;
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return err;
-}
-
-static inline int vino_is_capturing(struct vino_channel_settings *vcs)
-{
- int ret;
- unsigned long flags;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- ret = vcs->capturing;
-
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- return ret;
-}
-
-/* waits until a frame is captured */
-static int vino_wait_for_frame(struct vino_channel_settings *vcs)
-{
- wait_queue_t wait;
- int err = 0;
-
- dprintk("vino_wait_for_frame():\n");
-
- init_waitqueue_entry(&wait, current);
- /* add ourselves into wait queue */
- add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
- /* to ensure that schedule_timeout will return immediately
- * if VINO interrupt was triggered meanwhile */
- schedule_timeout_interruptible(msecs_to_jiffies(100));
-
- if (signal_pending(current))
- err = -EINTR;
-
- remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait);
-
- dprintk("vino_wait_for_frame(): waiting for frame %s\n",
- err ? "failed" : "ok");
-
- return err;
-}
-
-/* the function assumes that PAGE_SIZE % 4 == 0 */
-static void vino_convert_to_rgba(struct vino_framebuffer *fb) {
- unsigned char *pageptr;
- unsigned int page, i;
- unsigned char a;
-
- for (page = 0; page < fb->desc_table.page_count; page++) {
- pageptr = (unsigned char *)fb->desc_table.virtual[page];
-
- for (i = 0; i < PAGE_SIZE; i += 4) {
- a = pageptr[0];
- pageptr[0] = pageptr[3];
- pageptr[1] = pageptr[2];
- pageptr[2] = pageptr[1];
- pageptr[3] = a;
- pageptr += 4;
- }
- }
-}
-
-/* checks if the buffer is in correct state and syncs data */
-static int vino_check_buffer(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb)
-{
- int err = 0;
- unsigned long flags;
-
- dprintk("vino_check_buffer():\n");
-
- spin_lock_irqsave(&fb->state_lock, flags);
- switch (fb->state) {
- case VINO_FRAMEBUFFER_IN_USE:
- err = -EIO;
- break;
- case VINO_FRAMEBUFFER_READY:
- vino_sync_buffer(fb);
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- break;
- default:
- err = -EINVAL;
- }
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- if (!err) {
- if (vino_pixel_conversion
- && (fb->data_format == VINO_DATA_FMT_RGB32)) {
- vino_convert_to_rgba(fb);
- }
- } else if (err && (err != -EINVAL)) {
- dprintk("vino_check_buffer(): buffer not ready\n");
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
- }
-
- return err;
-}
-
-/* forcefully terminates capture */
-static void vino_capture_stop(struct vino_channel_settings *vcs)
-{
- unsigned int incoming = 0, outgoing = 0, id;
- unsigned long flags, flags2;
-
- dprintk("vino_capture_stop():\n");
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
-
- /* unset capturing to stop queue processing */
- vcs->capturing = 0;
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags2);
-
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2);
-
- /* remove all items from the queue */
- if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_incoming() failed\n");
- goto out;
- }
- while (incoming > 0) {
- vino_queue_transfer(&vcs->fb_queue);
-
- if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_incoming() failed\n");
- goto out;
- }
- }
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_outgoing() failed\n");
- goto out;
- }
- while (outgoing > 0) {
- vino_queue_remove(&vcs->fb_queue, &id);
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("vino_capture_stop(): "
- "vino_queue_get_outgoing() failed\n");
- goto out;
- }
- }
-
-out:
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-}
-
-#if 0
-static int vino_capture_failed(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
- unsigned int i;
- int ret;
-
- dprintk("vino_capture_failed():\n");
-
- spin_lock_irqsave(&vino_drvdata->vino_lock, flags);
-
- vino_dma_stop(vcs);
- vino_clear_interrupt(vcs);
-
- spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags);
-
- ret = vino_queue_get_incoming(&vcs->fb_queue, &i);
- if (ret == VINO_QUEUE_ERROR) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
- if (i == 0) {
- /* no buffers to process */
- return 0;
- }
-
- fb = vino_queue_peek(&vcs->fb_queue, &i);
- if (fb == NULL) {
- dprintk("vino_queue_peek() failed\n");
- return -EINVAL;
- }
-
- spin_lock_irqsave(&fb->state_lock, flags);
- if (fb->state == VINO_FRAMEBUFFER_IN_USE) {
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- vino_queue_transfer(&vcs->fb_queue);
- vino_queue_remove(&vcs->fb_queue, &i);
- /* we should actually discard the newest frame,
- * but who cares ... */
- }
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- return 0;
-}
-#endif
-
-static void vino_skip_frame(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
- unsigned int id;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
- fb = vino_queue_peek(&vcs->fb_queue, &id);
- if (!fb) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- dprintk("vino_skip_frame(): vino_queue_peek() failed!\n");
- return;
- }
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- spin_lock_irqsave(&fb->state_lock, flags);
- fb->state = VINO_FRAMEBUFFER_UNUSED;
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- vino_capture_next(vcs, 0);
-}
-
-static void vino_frame_done(struct vino_channel_settings *vcs)
-{
- struct vino_framebuffer *fb;
- unsigned long flags;
-
- spin_lock_irqsave(&vcs->capture_lock, flags);
- fb = vino_queue_transfer(&vcs->fb_queue);
- if (!fb) {
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
- dprintk("vino_frame_done(): vino_queue_transfer() failed!\n");
- return;
- }
- spin_unlock_irqrestore(&vcs->capture_lock, flags);
-
- fb->frame_counter = vcs->int_data.frame_counter;
- memcpy(&fb->timestamp, &vcs->int_data.timestamp,
- sizeof(struct timeval));
-
- spin_lock_irqsave(&fb->state_lock, flags);
- if (fb->state == VINO_FRAMEBUFFER_IN_USE)
- fb->state = VINO_FRAMEBUFFER_READY;
- spin_unlock_irqrestore(&fb->state_lock, flags);
-
- wake_up(&vcs->fb_queue.frame_wait_queue);
-
- vino_capture_next(vcs, 0);
-}
-
-static void vino_capture_tasklet(unsigned long channel) {
- struct vino_channel_settings *vcs;
-
- vcs = (channel == VINO_CHANNEL_A)
- ? &vino_drvdata->a : &vino_drvdata->b;
-
- if (vcs->int_data.skip)
- vcs->int_data.skip_count++;
-
- if (vcs->int_data.skip && (vcs->int_data.skip_count
- <= VINO_MAX_FRAME_SKIP_COUNT)) {
- vino_skip_frame(vcs);
- } else {
- vcs->int_data.skip_count = 0;
- vino_frame_done(vcs);
- }
-}
-
-static irqreturn_t vino_interrupt(int irq, void *dev_id)
-{
- u32 ctrl, intr;
- unsigned int fc_a, fc_b;
- int handled_a = 0, skip_a = 0, done_a = 0;
- int handled_b = 0, skip_b = 0, done_b = 0;
-
-#ifdef VINO_DEBUG_INT
- int loop = 0;
- unsigned int line_count = vino->a.line_count,
- page_index = vino->a.page_index,
- field_counter = vino->a.field_counter,
- start_desc_tbl = vino->a.start_desc_tbl,
- next_4_desc = vino->a.next_4_desc;
- unsigned int line_count_2,
- page_index_2,
- field_counter_2,
- start_desc_tbl_2,
- next_4_desc_2;
-#endif
-
- spin_lock(&vino_drvdata->vino_lock);
-
- while ((intr = vino->intr_status)) {
- fc_a = vino->a.field_counter >> 1;
- fc_b = vino->b.field_counter >> 1;
-
- /* handle error-interrupts in some special way ?
- * --> skips frames */
- if (intr & VINO_INTSTAT_A) {
- if (intr & VINO_INTSTAT_A_EOF) {
- vino_drvdata->a.field++;
- if (vino_drvdata->a.field > 1) {
- vino_dma_stop(&vino_drvdata->a);
- vino_clear_interrupt(&vino_drvdata->a);
- vino_drvdata->a.field = 0;
- done_a = 1;
- } else {
- if (vino->a.page_index
- != vino_drvdata->a.line_size) {
- vino->a.line_count = 0;
- vino->a.page_index =
- vino_drvdata->
- a.line_size;
- vino->a.next_4_desc =
- vino->a.start_desc_tbl;
- }
- }
- dprintk("channel A end-of-field "
- "interrupt: %04x\n", intr);
- } else {
- vino_dma_stop(&vino_drvdata->a);
- vino_clear_interrupt(&vino_drvdata->a);
- vino_drvdata->a.field = 0;
- skip_a = 1;
- dprintk("channel A error interrupt: %04x\n",
- intr);
- }
-
-#ifdef VINO_DEBUG_INT
- line_count_2 = vino->a.line_count;
- page_index_2 = vino->a.page_index;
- field_counter_2 = vino->a.field_counter;
- start_desc_tbl_2 = vino->a.start_desc_tbl;
- next_4_desc_2 = vino->a.next_4_desc;
-
- printk("intr = %04x, loop = %d, field = %d\n",
- intr, loop, vino_drvdata->a.field);
- printk("1- line count = %04d, page index = %04d, "
- "start = %08x, next = %08x\n"
- " fieldc = %d, framec = %d\n",
- line_count, page_index, start_desc_tbl,
- next_4_desc, field_counter, fc_a);
- printk("12-line count = %04d, page index = %04d, "
- " start = %08x, next = %08x\n",
- line_count_2, page_index_2, start_desc_tbl_2,
- next_4_desc_2);
-
- if (done_a)
- printk("\n");
-#endif
- }
-
- if (intr & VINO_INTSTAT_B) {
- if (intr & VINO_INTSTAT_B_EOF) {
- vino_drvdata->b.field++;
- if (vino_drvdata->b.field > 1) {
- vino_dma_stop(&vino_drvdata->b);
- vino_clear_interrupt(&vino_drvdata->b);
- vino_drvdata->b.field = 0;
- done_b = 1;
- }
- dprintk("channel B end-of-field "
- "interrupt: %04x\n", intr);
- } else {
- vino_dma_stop(&vino_drvdata->b);
- vino_clear_interrupt(&vino_drvdata->b);
- vino_drvdata->b.field = 0;
- skip_b = 1;
- dprintk("channel B error interrupt: %04x\n",
- intr);
- }
- }
-
- /* Always remember to clear interrupt status.
- * Disable VINO interrupts while we do this. */
- ctrl = vino->control;
- vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT);
- vino->intr_status = ~intr;
- vino->control = ctrl;
-
- spin_unlock(&vino_drvdata->vino_lock);
-
- if ((!handled_a) && (done_a || skip_a)) {
- if (!skip_a) {
- do_gettimeofday(&vino_drvdata->
- a.int_data.timestamp);
- vino_drvdata->a.int_data.frame_counter = fc_a;
- }
- vino_drvdata->a.int_data.skip = skip_a;
-
- dprintk("channel A %s, interrupt: %d\n",
- skip_a ? "skipping frame" : "frame done",
- intr);
- tasklet_hi_schedule(&vino_tasklet_a);
- handled_a = 1;
- }
-
- if ((!handled_b) && (done_b || skip_b)) {
- if (!skip_b) {
- do_gettimeofday(&vino_drvdata->
- b.int_data.timestamp);
- vino_drvdata->b.int_data.frame_counter = fc_b;
- }
- vino_drvdata->b.int_data.skip = skip_b;
-
- dprintk("channel B %s, interrupt: %d\n",
- skip_b ? "skipping frame" : "frame done",
- intr);
- tasklet_hi_schedule(&vino_tasklet_b);
- handled_b = 1;
- }
-
-#ifdef VINO_DEBUG_INT
- loop++;
-#endif
- spin_lock(&vino_drvdata->vino_lock);
- }
-
- spin_unlock(&vino_drvdata->vino_lock);
-
- return IRQ_HANDLED;
-}
-
-/* VINO video input management */
-
-static int vino_get_saa7191_input(int input)
-{
- switch (input) {
- case VINO_INPUT_COMPOSITE:
- return SAA7191_INPUT_COMPOSITE;
- case VINO_INPUT_SVIDEO:
- return SAA7191_INPUT_SVIDEO;
- default:
- printk(KERN_ERR "VINO: vino_get_saa7191_input(): "
- "invalid input!\n");
- return -1;
- }
-}
-
-/* execute with input_lock locked */
-static int vino_is_input_owner(struct vino_channel_settings *vcs)
-{
- switch(vcs->input) {
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- return vino_drvdata->decoder_owner == vcs->channel;
- case VINO_INPUT_D1:
- return vino_drvdata->camera_owner == vcs->channel;
- default:
- return 0;
- }
-}
-
-static int vino_acquire_input(struct vino_channel_settings *vcs)
-{
- unsigned long flags;
- int ret = 0;
-
- dprintk("vino_acquire_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- /* First try D1 and then SAA7191 */
- if (vino_drvdata->camera
- && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
- vino_drvdata->camera_owner = vcs->channel;
- vcs->input = VINO_INPUT_D1;
- vcs->data_norm = VINO_DATA_NORM_D1;
- } else if (vino_drvdata->decoder
- && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
- int input;
- int data_norm = 0;
- v4l2_std_id norm;
-
- input = VINO_INPUT_COMPOSITE;
-
- ret = decoder_call(video, s_routing,
- vino_get_saa7191_input(input), 0, 0);
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* Don't hold spinlocks while auto-detecting norm
- * as it may take a while... */
-
- ret = decoder_call(video, querystd, &norm);
- if (!ret) {
- for (data_norm = 0; data_norm < 3; data_norm++) {
- if (vino_data_norms[data_norm].std & norm)
- break;
- }
- if (data_norm == 3)
- data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(core, s_std, norm);
- }
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (ret) {
- ret = -EINVAL;
- goto out;
- }
-
- vino_drvdata->decoder_owner = vcs->channel;
-
- vcs->input = input;
- vcs->data_norm = data_norm;
- } else {
- vcs->input = (vcs->channel == VINO_CHANNEL_A) ?
- vino_drvdata->b.input : vino_drvdata->a.input;
- vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ?
- vino_drvdata->b.data_norm : vino_drvdata->a.data_norm;
- }
-
- if (vcs->input == VINO_INPUT_NONE) {
- ret = -ENODEV;
- goto out;
- }
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
-
- dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static int vino_set_input(struct vino_channel_settings *vcs, int input)
-{
- struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
- &vino_drvdata->b : &vino_drvdata->a;
- unsigned long flags;
- int ret = 0;
-
- dprintk("vino_set_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (vcs->input == input)
- goto out;
-
- switch (input) {
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- if (!vino_drvdata->decoder) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
- vino_drvdata->decoder_owner = vcs->channel;
- }
-
- if (vino_drvdata->decoder_owner == vcs->channel) {
- int data_norm = 0;
- v4l2_std_id norm;
-
- ret = decoder_call(video, s_routing,
- vino_get_saa7191_input(input), 0, 0);
- if (ret) {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- ret = -EINVAL;
- goto out;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* Don't hold spinlocks while auto-detecting norm
- * as it may take a while... */
-
- ret = decoder_call(video, querystd, &norm);
- if (!ret) {
- for (data_norm = 0; data_norm < 3; data_norm++) {
- if (vino_data_norms[data_norm].std & norm)
- break;
- }
- if (data_norm == 3)
- data_norm = VINO_DATA_NORM_PAL;
- ret = decoder_call(core, s_std, norm);
- }
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (ret) {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- ret = -EINVAL;
- goto out;
- }
-
- vcs->input = input;
- vcs->data_norm = data_norm;
- } else {
- if (input != vcs2->input) {
- ret = -EBUSY;
- goto out;
- }
-
- vcs->input = input;
- vcs->data_norm = vcs2->data_norm;
- }
-
- if (vino_drvdata->camera_owner == vcs->channel) {
- /* Transfer the ownership or release the input */
- if (vcs2->input == VINO_INPUT_D1) {
- vino_drvdata->camera_owner = vcs2->channel;
- } else {
- vino_drvdata->camera_owner = VINO_NO_CHANNEL;
- }
- }
- break;
- case VINO_INPUT_D1:
- if (!vino_drvdata->camera) {
- ret = -EINVAL;
- goto out;
- }
-
- if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
- vino_drvdata->camera_owner = vcs->channel;
-
- if (vino_drvdata->decoder_owner == vcs->channel) {
- /* Transfer the ownership or release the input */
- if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
- (vcs2->input == VINO_INPUT_SVIDEO)) {
- vino_drvdata->decoder_owner = vcs2->channel;
- } else {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- }
- }
-
- vcs->input = input;
- vcs->data_norm = VINO_DATA_NORM_D1;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
-
- dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name);
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static void vino_release_input(struct vino_channel_settings *vcs)
-{
- struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ?
- &vino_drvdata->b : &vino_drvdata->a;
- unsigned long flags;
-
- dprintk("vino_release_input():\n");
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- /* Release ownership of the channel
- * and if the other channel takes input from
- * the same source, transfer the ownership */
- if (vino_drvdata->camera_owner == vcs->channel) {
- if (vcs2->input == VINO_INPUT_D1) {
- vino_drvdata->camera_owner = vcs2->channel;
- } else {
- vino_drvdata->camera_owner = VINO_NO_CHANNEL;
- }
- } else if (vino_drvdata->decoder_owner == vcs->channel) {
- if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
- (vcs2->input == VINO_INPUT_SVIDEO)) {
- vino_drvdata->decoder_owner = vcs2->channel;
- } else {
- vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
- }
- }
- vcs->input = VINO_INPUT_NONE;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-}
-
-/* execute with input_lock locked */
-static int vino_set_data_norm(struct vino_channel_settings *vcs,
- unsigned int data_norm,
- unsigned long *flags)
-{
- int err = 0;
-
- if (data_norm == vcs->data_norm)
- return 0;
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- /* only one "norm" supported */
- if (data_norm != VINO_DATA_NORM_D1)
- return -EINVAL;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- v4l2_std_id norm;
-
- if ((data_norm != VINO_DATA_NORM_PAL)
- && (data_norm != VINO_DATA_NORM_NTSC)
- && (data_norm != VINO_DATA_NORM_SECAM))
- return -EINVAL;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
-
- /* Don't hold spinlocks while setting norm
- * as it may take a while... */
-
- norm = vino_data_norms[data_norm].std;
- err = decoder_call(core, s_std, norm);
-
- spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
-
- if (err)
- goto out;
-
- vcs->data_norm = data_norm;
-
- vino_set_default_clipping(vcs);
- vino_set_default_scaling(vcs);
- vino_set_default_framerate(vcs);
- break;
- }
- default:
- return -EINVAL;
- }
-
-out:
- return err;
-}
-
-/* V4L2 helper functions */
-
-static int vino_find_data_format(__u32 pixelformat)
-{
- int i;
-
- for (i = 0; i < VINO_DATA_FMT_COUNT; i++) {
- if (vino_data_formats[i].pixelformat == pixelformat)
- return i;
- }
-
- return VINO_DATA_FMT_NONE;
-}
-
-static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
-{
- int input = VINO_INPUT_NONE;
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- if (vino_drvdata->decoder && vino_drvdata->camera) {
- switch (index) {
- case 0:
- input = VINO_INPUT_COMPOSITE;
- break;
- case 1:
- input = VINO_INPUT_SVIDEO;
- break;
- case 2:
- input = VINO_INPUT_D1;
- break;
- }
- } else if (vino_drvdata->decoder) {
- switch (index) {
- case 0:
- input = VINO_INPUT_COMPOSITE;
- break;
- case 1:
- input = VINO_INPUT_SVIDEO;
- break;
- }
- } else if (vino_drvdata->camera) {
- switch (index) {
- case 0:
- input = VINO_INPUT_D1;
- break;
- }
- }
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return input;
-}
-
-/* execute with input_lock locked */
-static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
-{
- __u32 index = 0;
- // FIXME: detect when no inputs available
-
- if (vino_drvdata->decoder && vino_drvdata->camera) {
- switch (vcs->input) {
- case VINO_INPUT_COMPOSITE:
- index = 0;
- break;
- case VINO_INPUT_SVIDEO:
- index = 1;
- break;
- case VINO_INPUT_D1:
- index = 2;
- break;
- }
- } else if (vino_drvdata->decoder) {
- switch (vcs->input) {
- case VINO_INPUT_COMPOSITE:
- index = 0;
- break;
- case VINO_INPUT_SVIDEO:
- index = 1;
- break;
- }
- } else if (vino_drvdata->camera) {
- switch (vcs->input) {
- case VINO_INPUT_D1:
- index = 0;
- break;
- }
- }
-
- return index;
-}
-
-/* V4L2 ioctls */
-
-static int vino_querycap(struct file *file, void *__fh,
- struct v4l2_capability *cap)
-{
- memset(cap, 0, sizeof(struct v4l2_capability));
-
- strcpy(cap->driver, vino_driver_name);
- strcpy(cap->card, vino_driver_description);
- strcpy(cap->bus_info, vino_bus_name);
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING;
- // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
- return 0;
-}
-
-static int vino_enum_input(struct file *file, void *__fh,
- struct v4l2_input *i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- __u32 index = i->index;
- int input;
- dprintk("requested index = %d\n", index);
-
- input = vino_int_enum_input(vcs, index);
- if (input == VINO_INPUT_NONE)
- return -EINVAL;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
- i->std = vino_inputs[input].std;
- strcpy(i->name, vino_inputs[input].name);
-
- if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
- decoder_call(video, g_input_status, &i->status);
- return 0;
-}
-
-static int vino_g_input(struct file *file, void *__fh,
- unsigned int *i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- __u32 index;
- int input;
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- input = vcs->input;
- index = vino_find_input_index(vcs);
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- dprintk("input = %d\n", input);
-
- if (input == VINO_INPUT_NONE) {
- return -EINVAL;
- }
-
- *i = index;
-
- return 0;
-}
-
-static int vino_s_input(struct file *file, void *__fh,
- unsigned int i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int input;
- dprintk("requested input = %d\n", i);
-
- input = vino_int_enum_input(vcs, i);
- if (input == VINO_INPUT_NONE)
- return -EINVAL;
-
- return vino_set_input(vcs, input);
-}
-
-static int vino_querystd(struct file *file, void *__fh,
- v4l2_std_id *std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- *std = vino_inputs[vcs->input].std;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- decoder_call(video, querystd, std);
- break;
- }
- default:
- err = -EINVAL;
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_g_std(struct file *file, void *__fh,
- v4l2_std_id *std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- *std = vino_data_norms[vcs->data_norm].std;
- dprintk("current standard = %d\n", vcs->data_norm);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return 0;
-}
-
-static int vino_s_std(struct file *file, void *__fh,
- v4l2_std_id *std)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (!vino_is_input_owner(vcs)) {
- ret = -EBUSY;
- goto out;
- }
-
- /* check if the standard is valid for the current input */
- if ((*std) & vino_inputs[vcs->input].std) {
- dprintk("standard accepted\n");
-
- /* change the video norm for SAA7191
- * and accept NTSC for D1 (do nothing) */
-
- if (vcs->input == VINO_INPUT_D1)
- goto out;
-
- if ((*std) & V4L2_STD_PAL) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
- &flags);
- } else if ((*std) & V4L2_STD_NTSC) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC,
- &flags);
- } else if ((*std) & V4L2_STD_SECAM) {
- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM,
- &flags);
- } else {
- ret = -EINVAL;
- }
-
- if (ret) {
- ret = -EINVAL;
- }
- } else {
- ret = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return ret;
-}
-
-static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_fmtdesc *fd)
-{
- dprintk("format index = %d\n", fd->index);
-
- if (fd->index >= VINO_DATA_FMT_COUNT)
- return -EINVAL;
- dprintk("format name = %s\n", vino_data_formats[fd->index].description);
-
- fd->pixelformat = vino_data_formats[fd->index].pixelformat;
- strcpy(fd->description, vino_data_formats[fd->index].description);
- return 0;
-}
-
-static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_channel_settings tempvcs;
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- dprintk("requested: w = %d, h = %d\n",
- pf->width, pf->height);
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
- memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- tempvcs.data_format = vino_find_data_format(pf->pixelformat);
- if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
- tempvcs.data_format = VINO_DATA_FMT_GREY;
- pf->pixelformat =
- vino_data_formats[tempvcs.data_format].
- pixelformat;
- }
-
- /* data format must be set before clipping/scaling */
- vino_set_scaling(&tempvcs, pf->width, pf->height);
-
- dprintk("data format = %s\n",
- vino_data_formats[tempvcs.data_format].description);
-
- pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
- tempvcs.decimation;
- pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
- tempvcs.decimation;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = tempvcs.line_size;
- pf->sizeimage = tempvcs.line_size *
- (tempvcs.clipping.bottom - tempvcs.clipping.top) /
- tempvcs.decimation;
- pf->colorspace =
- vino_data_formats[tempvcs.data_format].colorspace;
-
- pf->priv = 0;
- return 0;
-}
-
-static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- pf->width = (vcs->clipping.right - vcs->clipping.left) /
- vcs->decimation;
- pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->pixelformat =
- vino_data_formats[vcs->data_format].pixelformat;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = vcs->line_size;
- pf->sizeimage = vcs->line_size *
- (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->colorspace =
- vino_data_formats[vcs->data_format].colorspace;
-
- pf->priv = 0;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- return 0;
-}
-
-static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *f)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int data_format;
- unsigned long flags;
- struct v4l2_pix_format *pf = &f->fmt.pix;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- data_format = vino_find_data_format(pf->pixelformat);
-
- if (data_format == VINO_DATA_FMT_NONE) {
- vcs->data_format = VINO_DATA_FMT_GREY;
- pf->pixelformat =
- vino_data_formats[vcs->data_format].
- pixelformat;
- } else {
- vcs->data_format = data_format;
- }
-
- /* data format must be set before clipping/scaling */
- vino_set_scaling(vcs, pf->width, pf->height);
-
- dprintk("data format = %s\n",
- vino_data_formats[vcs->data_format].description);
-
- pf->width = vcs->clipping.right - vcs->clipping.left;
- pf->height = vcs->clipping.bottom - vcs->clipping.top;
-
- pf->field = V4L2_FIELD_INTERLACED;
- pf->bytesperline = vcs->line_size;
- pf->sizeimage = vcs->line_size *
- (vcs->clipping.bottom - vcs->clipping.top) /
- vcs->decimation;
- pf->colorspace =
- vino_data_formats[vcs->data_format].colorspace;
-
- pf->priv = 0;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- return 0;
-}
-
-static int vino_cropcap(struct file *file, void *__fh,
- struct v4l2_cropcap *ccap)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- const struct vino_data_norm *norm;
- unsigned long flags;
-
- switch (ccap->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- norm = &vino_data_norms[vcs->data_norm];
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- ccap->bounds.left = 0;
- ccap->bounds.top = 0;
- ccap->bounds.width = norm->width;
- ccap->bounds.height = norm->height;
- memcpy(&ccap->defrect, &ccap->bounds,
- sizeof(struct v4l2_rect));
-
- ccap->pixelaspect.numerator = 1;
- ccap->pixelaspect.denominator = 1;
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_g_crop(struct file *file, void *__fh,
- struct v4l2_crop *c)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- switch (c->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- c->c.left = vcs->clipping.left;
- c->c.top = vcs->clipping.top;
- c->c.width = vcs->clipping.right - vcs->clipping.left;
- c->c.height = vcs->clipping.bottom - vcs->clipping.top;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_s_crop(struct file *file, void *__fh,
- struct v4l2_crop *c)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
-
- switch (c->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- vino_set_clipping(vcs, c->c.left, c->c.top,
- c->c.width, c->c.height);
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
- break;
- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int vino_g_parm(struct file *file, void *__fh,
- struct v4l2_streamparm *sp)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- cp->capability = V4L2_CAP_TIMEPERFRAME;
- cp->timeperframe.numerator = 1;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- cp->timeperframe.denominator = vcs->fps;
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- /* TODO: cp->readbuffers = xxx; */
-
- return 0;
-}
-
-static int vino_s_parm(struct file *file, void *__fh,
- struct v4l2_streamparm *sp)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- struct v4l2_captureparm *cp = &sp->parm.capture;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if ((cp->timeperframe.numerator == 0) ||
- (cp->timeperframe.denominator == 0)) {
- /* reset framerate */
- vino_set_default_framerate(vcs);
- } else {
- vino_set_framerate(vcs, cp->timeperframe.denominator /
- cp->timeperframe.numerator);
- }
-
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return 0;
-}
-
-static int vino_reqbufs(struct file *file, void *__fh,
- struct v4l2_requestbuffers *rb)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (rb->memory != V4L2_MEMORY_MMAP) {
- dprintk("type not mmap\n");
- return -EINVAL;
- }
-
- dprintk("count = %d\n", rb->count);
- if (rb->count > 0) {
- if (vino_is_capturing(vcs)) {
- dprintk("busy, capturing\n");
- return -EBUSY;
- }
-
- if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
- dprintk("busy, buffers still mapped\n");
- return -EBUSY;
- } else {
- vcs->streaming = 0;
- vino_queue_free(&vcs->fb_queue);
- vino_queue_init(&vcs->fb_queue, &rb->count);
- }
- } else {
- vcs->streaming = 0;
- vino_capture_stop(vcs);
- vino_queue_free(&vcs->fb_queue);
- }
-
- return 0;
-}
-
-static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
- struct vino_framebuffer *fb,
- struct v4l2_buffer *b)
-{
- if (vino_queue_outgoing_contains(&vcs->fb_queue,
- fb->id)) {
- b->flags &= ~V4L2_BUF_FLAG_QUEUED;
- b->flags |= V4L2_BUF_FLAG_DONE;
- } else if (vino_queue_incoming_contains(&vcs->fb_queue,
- fb->id)) {
- b->flags &= ~V4L2_BUF_FLAG_DONE;
- b->flags |= V4L2_BUF_FLAG_QUEUED;
- } else {
- b->flags &= ~(V4L2_BUF_FLAG_DONE |
- V4L2_BUF_FLAG_QUEUED);
- }
-
- b->flags &= ~(V4L2_BUF_FLAG_TIMECODE);
-
- if (fb->map_count > 0)
- b->flags |= V4L2_BUF_FLAG_MAPPED;
-
- b->index = fb->id;
- b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ?
- V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR;
- b->m.offset = fb->offset;
- b->bytesused = fb->data_size;
- b->length = fb->size;
- b->field = V4L2_FIELD_INTERLACED;
- b->sequence = fb->frame_counter;
- memcpy(&b->timestamp, &fb->timestamp,
- sizeof(struct timeval));
- // b->input ?
-
- dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n",
- fb->id, fb->size, fb->data_size, fb->offset);
-}
-
-static int vino_querybuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_framebuffer *fb;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
- dprintk("invalid index = %d\n",
- b->index);
- return -EINVAL;
- }
-
- fb = vino_queue_get_buffer(&vcs->fb_queue,
- b->index);
- if (fb == NULL) {
- dprintk("vino_queue_get_buffer() failed");
- return -EINVAL;
- }
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- return 0;
-}
-
-static int vino_qbuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- struct vino_framebuffer *fb;
- int ret;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
- if (b->memory != V4L2_MEMORY_MMAP) {
- dprintk("type not mmap\n");
- return -EINVAL;
- }
-
- fb = vino_capture_enqueue(vcs, b->index);
- if (fb == NULL)
- return -EINVAL;
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- if (vcs->streaming) {
- ret = vino_capture_next(vcs, 1);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int vino_dqbuf(struct file *file, void *__fh,
- struct v4l2_buffer *b)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int nonblocking = file->f_flags & O_NONBLOCK;
- struct vino_framebuffer *fb;
- unsigned int incoming, outgoing;
- int err;
-
- if (vcs->reading)
- return -EBUSY;
-
- /* TODO: check queue type */
-
- err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (err) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
- err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
- if (err) {
- dprintk("vino_queue_get_outgoing() failed\n");
- return -EINVAL;
- }
-
- dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
-
- if (outgoing == 0) {
- if (incoming == 0) {
- dprintk("no incoming or outgoing buffers\n");
- return -EINVAL;
- }
- if (nonblocking) {
- dprintk("non-blocking I/O was selected and "
- "there are no buffers to dequeue\n");
- return -EAGAIN;
- }
-
- err = vino_wait_for_frame(vcs);
- if (err) {
- err = vino_wait_for_frame(vcs);
- if (err) {
- /* interrupted or no frames captured because of
- * frame skipping */
- /* vino_capture_failed(vcs); */
- return -EIO;
- }
- }
- }
-
- fb = vino_queue_remove(&vcs->fb_queue, &b->index);
- if (fb == NULL) {
- dprintk("vino_queue_remove() failed\n");
- return -EINVAL;
- }
-
- err = vino_check_buffer(vcs, fb);
-
- vino_v4l2_get_buffer_status(vcs, fb, b);
-
- if (err)
- return -EIO;
-
- return 0;
-}
-
-static int vino_streamon(struct file *file, void *__fh,
- enum v4l2_buf_type i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int incoming;
- int ret;
- if (vcs->reading)
- return -EBUSY;
-
- if (vcs->streaming)
- return 0;
-
- // TODO: check queue type
-
- if (vino_queue_get_length(&vcs->fb_queue) < 1) {
- dprintk("no buffers allocated\n");
- return -EINVAL;
- }
-
- ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
- if (ret) {
- dprintk("vino_queue_get_incoming() failed\n");
- return -EINVAL;
- }
-
- vcs->streaming = 1;
-
- if (incoming > 0) {
- ret = vino_capture_next(vcs, 1);
- if (ret) {
- vcs->streaming = 0;
-
- dprintk("couldn't start capture\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static int vino_streamoff(struct file *file, void *__fh,
- enum v4l2_buf_type i)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- if (vcs->reading)
- return -EBUSY;
-
- if (!vcs->streaming)
- return 0;
-
- vcs->streaming = 0;
- vino_capture_stop(vcs);
-
- return 0;
-}
-
-static int vino_queryctrl(struct file *file, void *__fh,
- struct v4l2_queryctrl *queryctrl)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1:
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id ==
- queryctrl->id) {
- memcpy(queryctrl,
- &vino_indycam_v4l2_controls[i],
- sizeof(struct v4l2_queryctrl));
- queryctrl->reserved[0] = 0;
- goto found;
- }
- }
-
- err = -EINVAL;
- break;
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO:
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id ==
- queryctrl->id) {
- memcpy(queryctrl,
- &vino_saa7191_v4l2_controls[i],
- sizeof(struct v4l2_queryctrl));
- queryctrl->reserved[0] = 0;
- goto found;
- }
- }
-
- err = -EINVAL;
- break;
- default:
- err = -EINVAL;
- }
-
- found:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_g_ctrl(struct file *file, void *__fh,
- struct v4l2_control *control)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- switch (vcs->input) {
- case VINO_INPUT_D1: {
- err = -EINVAL;
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
-
- if (err)
- goto out;
-
- err = camera_call(core, g_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- err = -EINVAL;
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
-
- if (err)
- goto out;
-
- err = decoder_call(core, g_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- default:
- err = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-static int vino_s_ctrl(struct file *file, void *__fh,
- struct v4l2_control *control)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned long flags;
- int i;
- int err = 0;
-
- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
-
- if (!vino_is_input_owner(vcs)) {
- err = -EBUSY;
- goto out;
- }
-
- switch (vcs->input) {
- case VINO_INPUT_D1: {
- err = -EINVAL;
- for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
- if (vino_indycam_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
- if (err)
- goto out;
- if (control->value < vino_indycam_v4l2_controls[i].minimum ||
- control->value > vino_indycam_v4l2_controls[i].maximum) {
- err = -ERANGE;
- goto out;
- }
- err = camera_call(core, s_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- case VINO_INPUT_COMPOSITE:
- case VINO_INPUT_SVIDEO: {
- err = -EINVAL;
- for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
- if (vino_saa7191_v4l2_controls[i].id == control->id) {
- err = 0;
- break;
- }
- }
- if (err)
- goto out;
- if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
- control->value > vino_saa7191_v4l2_controls[i].maximum) {
- err = -ERANGE;
- goto out;
- }
-
- err = decoder_call(core, s_ctrl, control);
- if (err)
- err = -EINVAL;
- break;
- }
- default:
- err = -EINVAL;
- }
-
-out:
- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
-
- return err;
-}
-
-/* File operations */
-
-static int vino_open(struct file *file)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- int ret = 0;
- dprintk("open(): channel = %c\n",
- (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B');
-
- mutex_lock(&vcs->mutex);
-
- if (vcs->users) {
- dprintk("open(): driver busy\n");
- ret = -EBUSY;
- goto out;
- }
-
- ret = vino_acquire_input(vcs);
- if (ret) {
- dprintk("open(): vino_acquire_input() failed\n");
- goto out;
- }
-
- vcs->users++;
-
- out:
- mutex_unlock(&vcs->mutex);
-
- dprintk("open(): %s!\n", ret ? "failed" : "complete");
-
- return ret;
-}
-
-static int vino_close(struct file *file)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- dprintk("close():\n");
-
- mutex_lock(&vcs->mutex);
-
- vcs->users--;
-
- if (!vcs->users) {
- vino_release_input(vcs);
-
- /* stop DMA and free buffers */
- vino_capture_stop(vcs);
- vino_queue_free(&vcs->fb_queue);
- }
-
- mutex_unlock(&vcs->mutex);
-
- return 0;
-}
-
-static void vino_vm_open(struct vm_area_struct *vma)
-{
- struct vino_framebuffer *fb = vma->vm_private_data;
-
- fb->map_count++;
- dprintk("vino_vm_open(): count = %d\n", fb->map_count);
-}
-
-static void vino_vm_close(struct vm_area_struct *vma)
-{
- struct vino_framebuffer *fb = vma->vm_private_data;
-
- fb->map_count--;
- dprintk("vino_vm_close(): count = %d\n", fb->map_count);
-}
-
-static const struct vm_operations_struct vino_vm_ops = {
- .open = vino_vm_open,
- .close = vino_vm_close,
-};
-
-static int vino_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
-
- unsigned long start = vma->vm_start;
- unsigned long size = vma->vm_end - vma->vm_start;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-
- struct vino_framebuffer *fb = NULL;
- unsigned int i, length;
- int ret = 0;
-
- dprintk("mmap():\n");
-
- // TODO: reject mmap if already mapped
-
- if (mutex_lock_interruptible(&vcs->mutex))
- return -EINTR;
-
- if (vcs->reading) {
- ret = -EBUSY;
- goto out;
- }
-
- // TODO: check queue type
-
- if (!(vma->vm_flags & VM_WRITE)) {
- dprintk("mmap(): app bug: PROT_WRITE please\n");
- ret = -EINVAL;
- goto out;
- }
- if (!(vma->vm_flags & VM_SHARED)) {
- dprintk("mmap(): app bug: MAP_SHARED please\n");
- ret = -EINVAL;
- goto out;
- }
-
- /* find the correct buffer using offset */
- length = vino_queue_get_length(&vcs->fb_queue);
- if (length == 0) {
- dprintk("mmap(): queue not initialized\n");
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < length; i++) {
- fb = vino_queue_get_buffer(&vcs->fb_queue, i);
- if (fb == NULL) {
- dprintk("mmap(): vino_queue_get_buffer() failed\n");
- ret = -EINVAL;
- goto out;
- }
-
- if (fb->offset == offset)
- goto found;
- }
-
- dprintk("mmap(): invalid offset = %lu\n", offset);
- ret = -EINVAL;
- goto out;
-
-found:
- dprintk("mmap(): buffer = %d\n", i);
-
- if (size > (fb->desc_table.page_count * PAGE_SIZE)) {
- dprintk("mmap(): failed: size = %lu > %lu\n",
- size, fb->desc_table.page_count * PAGE_SIZE);
- ret = -EINVAL;
- goto out;
- }
-
- for (i = 0; i < fb->desc_table.page_count; i++) {
- unsigned long pfn =
- virt_to_phys((void *)fb->desc_table.virtual[i]) >>
- PAGE_SHIFT;
-
- if (size < PAGE_SIZE)
- break;
-
- // protection was: PAGE_READONLY
- if (remap_pfn_range(vma, start, pfn, PAGE_SIZE,
- vma->vm_page_prot)) {
- dprintk("mmap(): remap_pfn_range() failed\n");
- ret = -EAGAIN;
- goto out;
- }
-
- start += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- fb->map_count = 1;
-
- vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
- vma->vm_flags &= ~VM_IO;
- vma->vm_private_data = fb;
- vma->vm_file = file;
- vma->vm_ops = &vino_vm_ops;
-
-out:
- mutex_unlock(&vcs->mutex);
-
- return ret;
-}
-
-static unsigned int vino_poll(struct file *file, poll_table *pt)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- unsigned int outgoing;
- unsigned int ret = 0;
-
- // lock mutex (?)
- // TODO: this has to be corrected for different read modes
-
- dprintk("poll():\n");
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("poll(): vino_queue_get_outgoing() failed\n");
- ret = POLLERR;
- goto error;
- }
- if (outgoing > 0)
- goto over;
-
- poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt);
-
- if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) {
- dprintk("poll(): vino_queue_get_outgoing() failed\n");
- ret = POLLERR;
- goto error;
- }
-
-over:
- dprintk("poll(): data %savailable\n",
- (outgoing > 0) ? "" : "not ");
-
- if (outgoing > 0)
- ret = POLLIN | POLLRDNORM;
-
-error:
- return ret;
-}
-
-static long vino_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct vino_channel_settings *vcs = video_drvdata(file);
- long ret;
-
- if (mutex_lock_interruptible(&vcs->mutex))
- return -EINTR;
-
- ret = video_ioctl2(file, cmd, arg);
-
- mutex_unlock(&vcs->mutex);
-
- return ret;
-}
-
-/* Initialization and cleanup */
-
-/* __initdata */
-static int vino_init_stage;
-
-const struct v4l2_ioctl_ops vino_ioctl_ops = {
- .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap,
- .vidioc_querycap = vino_querycap,
- .vidioc_enum_input = vino_enum_input,
- .vidioc_g_input = vino_g_input,
- .vidioc_s_input = vino_s_input,
- .vidioc_g_std = vino_g_std,
- .vidioc_s_std = vino_s_std,
- .vidioc_querystd = vino_querystd,
- .vidioc_cropcap = vino_cropcap,
- .vidioc_s_crop = vino_s_crop,
- .vidioc_g_crop = vino_g_crop,
- .vidioc_s_parm = vino_s_parm,
- .vidioc_g_parm = vino_g_parm,
- .vidioc_reqbufs = vino_reqbufs,
- .vidioc_querybuf = vino_querybuf,
- .vidioc_qbuf = vino_qbuf,
- .vidioc_dqbuf = vino_dqbuf,
- .vidioc_streamon = vino_streamon,
- .vidioc_streamoff = vino_streamoff,
- .vidioc_queryctrl = vino_queryctrl,
- .vidioc_g_ctrl = vino_g_ctrl,
- .vidioc_s_ctrl = vino_s_ctrl,
-};
-
-static const struct v4l2_file_operations vino_fops = {
- .owner = THIS_MODULE,
- .open = vino_open,
- .release = vino_close,
- .unlocked_ioctl = vino_ioctl,
- .mmap = vino_mmap,
- .poll = vino_poll,
-};
-
-static struct video_device vdev_template = {
- .name = "NOT SET",
- .fops = &vino_fops,
- .ioctl_ops = &vino_ioctl_ops,
- .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
-};
-
-static void vino_module_cleanup(int stage)
-{
- switch(stage) {
- case 11:
- video_unregister_device(vino_drvdata->b.vdev);
- vino_drvdata->b.vdev = NULL;
- case 10:
- video_unregister_device(vino_drvdata->a.vdev);
- vino_drvdata->a.vdev = NULL;
- case 9:
- i2c_del_adapter(&vino_i2c_adapter);
- case 8:
- free_irq(SGI_VINO_IRQ, NULL);
- case 7:
- if (vino_drvdata->b.vdev) {
- video_device_release(vino_drvdata->b.vdev);
- vino_drvdata->b.vdev = NULL;
- }
- case 6:
- if (vino_drvdata->a.vdev) {
- video_device_release(vino_drvdata->a.vdev);
- vino_drvdata->a.vdev = NULL;
- }
- case 5:
- /* all entries in dma_cpu dummy table have the same address */
- dma_unmap_single(NULL,
- vino_drvdata->dummy_desc_table.dma_cpu[0],
- PAGE_SIZE, DMA_FROM_DEVICE);
- dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT
- * sizeof(dma_addr_t),
- (void *)vino_drvdata->
- dummy_desc_table.dma_cpu,
- vino_drvdata->dummy_desc_table.dma);
- case 4:
- free_page(vino_drvdata->dummy_page);
- case 3:
- v4l2_device_unregister(&vino_drvdata->v4l2_dev);
- case 2:
- kfree(vino_drvdata);
- case 1:
- iounmap(vino);
- case 0:
- break;
- default:
- dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n",
- stage);
- }
-}
-
-static int vino_probe(void)
-{
- unsigned long rev_id;
-
- if (ip22_is_fullhouse()) {
- printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n");
- return -ENODEV;
- }
-
- if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
- printk(KERN_ERR "VINO is not found (EISA BUS not present)\n");
- return -ENODEV;
- }
-
- vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino));
- if (!vino) {
- printk(KERN_ERR "VINO: ioremap() failed\n");
- return -EIO;
- }
- vino_init_stage++;
-
- if (get_dbe(rev_id, &(vino->rev_id))) {
- printk(KERN_ERR "Failed to read VINO revision register\n");
- vino_module_cleanup(vino_init_stage);
- return -ENODEV;
- }
-
- if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) {
- printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n",
- rev_id);
- vino_module_cleanup(vino_init_stage);
- return -ENODEV;
- }
-
- printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id));
-
- return 0;
-}
-
-static int vino_init(void)
-{
- dma_addr_t dma_dummy_address;
- int err;
- int i;
-
- vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
- if (!vino_drvdata) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
- strlcpy(vino_drvdata->v4l2_dev.name, "vino",
- sizeof(vino_drvdata->v4l2_dev.name));
- err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
- if (err)
- return err;
- vino_init_stage++;
-
- /* create a dummy dma descriptor */
- vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!vino_drvdata->dummy_page) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- // TODO: use page_count in dummy_desc_table
-
- vino_drvdata->dummy_desc_table.dma_cpu =
- dma_alloc_coherent(NULL,
- VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t),
- &vino_drvdata->dummy_desc_table.dma,
- GFP_KERNEL | GFP_DMA);
- if (!vino_drvdata->dummy_desc_table.dma_cpu) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- dma_dummy_address = dma_map_single(NULL,
- (void *)vino_drvdata->dummy_page,
- PAGE_SIZE, DMA_FROM_DEVICE);
- for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) {
- vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address;
- }
-
- /* initialize VINO */
-
- vino->control = 0;
- vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma;
- vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma;
- udelay(VINO_DESC_FETCH_DELAY);
-
- vino->intr_status = 0;
-
- vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
- vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT;
-
- return 0;
-}
-
-static int vino_init_channel_settings(struct vino_channel_settings *vcs,
- unsigned int channel, const char *name)
-{
- vcs->channel = channel;
- vcs->input = VINO_INPUT_NONE;
- vcs->alpha = 0;
- vcs->users = 0;
- vcs->data_format = VINO_DATA_FMT_GREY;
- vcs->data_norm = VINO_DATA_NORM_NTSC;
- vcs->decimation = 1;
- vino_set_default_clipping(vcs);
- vino_set_default_framerate(vcs);
-
- vcs->capturing = 0;
-
- mutex_init(&vcs->mutex);
- spin_lock_init(&vcs->capture_lock);
-
- mutex_init(&vcs->fb_queue.queue_mutex);
- spin_lock_init(&vcs->fb_queue.queue_lock);
- init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
-
- vcs->vdev = video_device_alloc();
- if (!vcs->vdev) {
- vino_module_cleanup(vino_init_stage);
- return -ENOMEM;
- }
- vino_init_stage++;
-
- memcpy(vcs->vdev, &vdev_template,
- sizeof(struct video_device));
- strcpy(vcs->vdev->name, name);
- vcs->vdev->release = video_device_release;
- vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
-
- video_set_drvdata(vcs->vdev, vcs);
-
- return 0;
-}
-
-static int __init vino_module_init(void)
-{
- int ret;
-
- printk(KERN_INFO "SGI VINO driver version %s\n",
- VINO_MODULE_VERSION);
-
- ret = vino_probe();
- if (ret)
- return ret;
-
- ret = vino_init();
- if (ret)
- return ret;
-
- /* initialize data structures */
-
- spin_lock_init(&vino_drvdata->vino_lock);
- spin_lock_init(&vino_drvdata->input_lock);
-
- ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
- vino_vdev_name_a);
- if (ret)
- return ret;
-
- ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
- vino_vdev_name_b);
- if (ret)
- return ret;
-
- /* initialize hardware and register V4L devices */
-
- ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0,
- vino_driver_description, NULL);
- if (ret) {
- printk(KERN_ERR "VINO: requesting IRQ %02d failed\n",
- SGI_VINO_IRQ);
- vino_module_cleanup(vino_init_stage);
- return -EAGAIN;
- }
- vino_init_stage++;
-
- ret = i2c_add_adapter(&vino_i2c_adapter);
- if (ret) {
- printk(KERN_ERR "VINO I2C bus registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return ret;
- }
- i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
- vino_init_stage++;
-
- ret = video_register_device(vino_drvdata->a.vdev,
- VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- printk(KERN_ERR "VINO channel A Video4Linux-device "
- "registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return -EINVAL;
- }
- vino_init_stage++;
-
- ret = video_register_device(vino_drvdata->b.vdev,
- VFL_TYPE_GRABBER, -1);
- if (ret < 0) {
- printk(KERN_ERR "VINO channel B Video4Linux-device "
- "registration failed\n");
- vino_module_cleanup(vino_init_stage);
- return -EINVAL;
- }
- vino_init_stage++;
-
- vino_drvdata->decoder =
- v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
- "saa7191", 0, I2C_ADDRS(0x45));
- vino_drvdata->camera =
- v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter,
- "indycam", 0, I2C_ADDRS(0x2b));
-
- dprintk("init complete!\n");
-
- return 0;
-}
-
-static void __exit vino_module_exit(void)
-{
- dprintk("exiting, stage = %d ...\n", vino_init_stage);
- vino_module_cleanup(vino_init_stage);
- dprintk("cleanup complete, exit!\n");
-}
-
-module_init(vino_module_init);
-module_exit(vino_module_exit);
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
deleted file mode 100644
index a05494b71b20..000000000000
--- a/drivers/media/video/vivi.c
+++ /dev/null
@@ -1,1385 +0,0 @@
-/*
- * Virtual Video driver - This code emulates a real video device with v4l2 api
- *
- * Copyright (c) 2006 by:
- * Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
- * Ted Walther <ted--a.t--enumera.com>
- * 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
- * License, or (at your option) any later version
- */
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/font.h>
-#include <linux/mutex.h>
-#include <linux/videodev2.h>
-#include <linux/kthread.h>
-#include <linux/freezer.h>
-#include <media/videobuf2-vmalloc.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-fh.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-common.h>
-
-#define VIVI_MODULE_NAME "vivi"
-
-/* Wake up at about 30 fps */
-#define WAKE_NUMERATOR 30
-#define WAKE_DENOMINATOR 1001
-#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
-
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1200
-
-#define VIVI_VERSION "0.8.1"
-
-MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
-MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_VERSION(VIVI_VERSION);
-
-static unsigned video_nr = -1;
-module_param(video_nr, uint, 0644);
-MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
-
-static unsigned n_devs = 1;
-module_param(n_devs, uint, 0644);
-MODULE_PARM_DESC(n_devs, "number of video devices to create");
-
-static unsigned debug;
-module_param(debug, uint, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-static unsigned int vid_limit = 16;
-module_param(vid_limit, uint, 0644);
-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
-
-/* Global font descriptor */
-static const u8 *font8x16;
-
-#define dprintk(dev, level, fmt, arg...) \
- v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
-
-/* ------------------------------------------------------------------
- Basic structures
- ------------------------------------------------------------------*/
-
-struct vivi_fmt {
- char *name;
- u32 fourcc; /* v4l2 format id */
- int depth;
-};
-
-static struct vivi_fmt formats[] = {
- {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .depth = 16,
- },
- {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .depth = 16,
- },
- {
- .name = "4:2:2, packed, YVYU",
- .fourcc = V4L2_PIX_FMT_YVYU,
- .depth = 16,
- },
- {
- .name = "4:2:2, packed, VYUY",
- .fourcc = V4L2_PIX_FMT_VYUY,
- .depth = 16,
- },
- {
- .name = "RGB565 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
- .depth = 16,
- },
- {
- .name = "RGB565 (BE)",
- .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
- .depth = 16,
- },
- {
- .name = "RGB555 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
- .depth = 16,
- },
- {
- .name = "RGB555 (BE)",
- .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
- .depth = 16,
- },
- {
- .name = "RGB24 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
- .depth = 24,
- },
- {
- .name = "RGB24 (BE)",
- .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
- .depth = 24,
- },
- {
- .name = "RGB32 (LE)",
- .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
- .depth = 32,
- },
- {
- .name = "RGB32 (BE)",
- .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */
- .depth = 32,
- },
-};
-
-static struct vivi_fmt *get_format(struct v4l2_format *f)
-{
- struct vivi_fmt *fmt;
- unsigned int k;
-
- for (k = 0; k < ARRAY_SIZE(formats); k++) {
- fmt = &formats[k];
- if (fmt->fourcc == f->fmt.pix.pixelformat)
- break;
- }
-
- if (k == ARRAY_SIZE(formats))
- return NULL;
-
- return &formats[k];
-}
-
-/* buffer for one video frame */
-struct vivi_buffer {
- /* common v4l buffer stuff -- must be first */
- struct vb2_buffer vb;
- struct list_head list;
- struct vivi_fmt *fmt;
-};
-
-struct vivi_dmaqueue {
- struct list_head active;
-
- /* thread for generating video stream*/
- struct task_struct *kthread;
- wait_queue_head_t wq;
- /* Counters to control fps rate */
- int frame;
- int ini_jiffies;
-};
-
-static LIST_HEAD(vivi_devlist);
-
-struct vivi_dev {
- struct list_head vivi_devlist;
- struct v4l2_device v4l2_dev;
- struct v4l2_ctrl_handler ctrl_handler;
- struct video_device vdev;
-
- /* controls */
- struct v4l2_ctrl *brightness;
- struct v4l2_ctrl *contrast;
- struct v4l2_ctrl *saturation;
- struct v4l2_ctrl *hue;
- struct {
- /* autogain/gain cluster */
- struct v4l2_ctrl *autogain;
- struct v4l2_ctrl *gain;
- };
- struct v4l2_ctrl *volume;
- struct v4l2_ctrl *alpha;
- struct v4l2_ctrl *button;
- struct v4l2_ctrl *boolean;
- struct v4l2_ctrl *int32;
- struct v4l2_ctrl *int64;
- struct v4l2_ctrl *menu;
- struct v4l2_ctrl *string;
- struct v4l2_ctrl *bitmask;
- struct v4l2_ctrl *int_menu;
-
- spinlock_t slock;
- struct mutex mutex;
-
- struct vivi_dmaqueue vidq;
-
- /* Several counters */
- unsigned ms;
- unsigned long jiffies;
- unsigned button_pressed;
-
- int mv_count; /* Controls bars movement */
-
- /* Input Number */
- int input;
-
- /* video capture */
- struct vivi_fmt *fmt;
- unsigned int width, height;
- struct vb2_queue vb_vidq;
- unsigned int field_count;
-
- u8 bars[9][3];
- u8 line[MAX_WIDTH * 8];
- unsigned int pixelsize;
- u8 alpha_component;
-};
-
-/* ------------------------------------------------------------------
- DMA and thread functions
- ------------------------------------------------------------------*/
-
-/* Bars and Colors should match positions */
-
-enum colors {
- WHITE,
- AMBER,
- CYAN,
- GREEN,
- MAGENTA,
- RED,
- BLUE,
- BLACK,
- TEXT_BLACK,
-};
-
-/* R G B */
-#define COLOR_WHITE {204, 204, 204}
-#define COLOR_AMBER {208, 208, 0}
-#define COLOR_CYAN { 0, 206, 206}
-#define COLOR_GREEN { 0, 239, 0}
-#define COLOR_MAGENTA {239, 0, 239}
-#define COLOR_RED {205, 0, 0}
-#define COLOR_BLUE { 0, 0, 255}
-#define COLOR_BLACK { 0, 0, 0}
-
-struct bar_std {
- u8 bar[9][3];
-};
-
-/* Maximum number of bars are 10 - otherwise, the input print code
- should be modified */
-static struct bar_std bars[] = {
- { /* Standard ITU-R color bar sequence */
- { COLOR_WHITE, COLOR_AMBER, COLOR_CYAN, COLOR_GREEN,
- COLOR_MAGENTA, COLOR_RED, COLOR_BLUE, COLOR_BLACK, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_AMBER, COLOR_BLACK, COLOR_WHITE,
- COLOR_AMBER, COLOR_BLACK, COLOR_WHITE, COLOR_AMBER, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_CYAN, COLOR_BLACK, COLOR_WHITE,
- COLOR_CYAN, COLOR_BLACK, COLOR_WHITE, COLOR_CYAN, COLOR_BLACK }
- }, {
- { COLOR_WHITE, COLOR_GREEN, COLOR_BLACK, COLOR_WHITE,
- COLOR_GREEN, COLOR_BLACK, COLOR_WHITE, COLOR_GREEN, COLOR_BLACK }
- },
-};
-
-#define NUM_INPUTS ARRAY_SIZE(bars)
-
-#define TO_Y(r, g, b) \
- (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
-/* RGB to V(Cr) Color transform */
-#define TO_V(r, g, b) \
- (((28784 * r - 24103 * g - 4681 * b + 32768) >> 16) + 128)
-/* RGB to U(Cb) Color transform */
-#define TO_U(r, g, b) \
- (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
-
-/* precalculate color bar values to speed up rendering */
-static void precalculate_bars(struct vivi_dev *dev)
-{
- u8 r, g, b;
- int k, is_yuv;
-
- for (k = 0; k < 9; k++) {
- r = bars[dev->input].bar[k][0];
- g = bars[dev->input].bar[k][1];
- b = bars[dev->input].bar[k][2];
- is_yuv = 0;
-
- switch (dev->fmt->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_VYUY:
- is_yuv = 1;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- r >>= 3;
- g >>= 2;
- b >>= 3;
- break;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB555X:
- r >>= 3;
- g >>= 3;
- b >>= 3;
- break;
- case V4L2_PIX_FMT_RGB24:
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_RGB32:
- case V4L2_PIX_FMT_BGR32:
- break;
- }
-
- if (is_yuv) {
- dev->bars[k][0] = TO_Y(r, g, b); /* Luma */
- dev->bars[k][1] = TO_U(r, g, b); /* Cb */
- dev->bars[k][2] = TO_V(r, g, b); /* Cr */
- } else {
- dev->bars[k][0] = r;
- dev->bars[k][1] = g;
- dev->bars[k][2] = b;
- }
- }
-}
-
-#define TSTAMP_MIN_Y 24
-#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
-#define TSTAMP_INPUT_X 10
-#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
-
-/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
-static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
-{
- u8 r_y, g_u, b_v;
- u8 alpha = dev->alpha_component;
- int color;
- u8 *p;
-
- r_y = dev->bars[colorpos][0]; /* R or precalculated Y */
- g_u = dev->bars[colorpos][1]; /* G or precalculated U */
- b_v = dev->bars[colorpos][2]; /* B or precalculated V */
-
- for (color = 0; color < dev->pixelsize; color++) {
- p = buf + color;
-
- switch (dev->fmt->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = odd ? b_v : g_u;
- break;
- }
- break;
- case V4L2_PIX_FMT_UYVY:
- switch (color) {
- case 0:
- *p = odd ? b_v : g_u;
- break;
- case 1:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_YVYU:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = odd ? g_u : b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_VYUY:
- switch (color) {
- case 0:
- *p = odd ? g_u : b_v;
- break;
- case 1:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565:
- switch (color) {
- case 0:
- *p = (g_u << 5) | b_v;
- break;
- case 1:
- *p = (r_y << 3) | (g_u >> 3);
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB565X:
- switch (color) {
- case 0:
- *p = (r_y << 3) | (g_u >> 3);
- break;
- case 1:
- *p = (g_u << 5) | b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555:
- switch (color) {
- case 0:
- *p = (g_u << 5) | b_v;
- break;
- case 1:
- *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB555X:
- switch (color) {
- case 0:
- *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
- break;
- case 1:
- *p = (g_u << 5) | b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB24:
- switch (color) {
- case 0:
- *p = r_y;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_BGR24:
- switch (color) {
- case 0:
- *p = b_v;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = r_y;
- break;
- }
- break;
- case V4L2_PIX_FMT_RGB32:
- switch (color) {
- case 0:
- *p = alpha;
- break;
- case 1:
- *p = r_y;
- break;
- case 2:
- *p = g_u;
- break;
- case 3:
- *p = b_v;
- break;
- }
- break;
- case V4L2_PIX_FMT_BGR32:
- switch (color) {
- case 0:
- *p = b_v;
- break;
- case 1:
- *p = g_u;
- break;
- case 2:
- *p = r_y;
- break;
- case 3:
- *p = alpha;
- break;
- }
- break;
- }
- }
-}
-
-static void precalculate_line(struct vivi_dev *dev)
-{
- int w;
-
- for (w = 0; w < dev->width * 2; w++) {
- int colorpos = w / (dev->width / 8) % 8;
-
- gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1);
- }
-}
-
-static void gen_text(struct vivi_dev *dev, char *basep,
- int y, int x, char *text)
-{
- int line;
-
- /* Checks if it is possible to show string */
- if (y + 16 >= dev->height || x + strlen(text) * 8 >= dev->width)
- return;
-
- /* Print stream time */
- for (line = y; line < y + 16; line++) {
- int j = 0;
- char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize;
- char *s;
-
- for (s = text; *s; s++) {
- u8 chr = font8x16[*s * 16 + line - y];
- int i;
-
- for (i = 0; i < 7; i++, j++) {
- /* Draw white font on black background */
- if (chr & (1 << (7 - i)))
- gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1);
- else
- gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1);
- }
- }
- }
-}
-
-static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
-{
- int wmax = dev->width;
- int hmax = dev->height;
- struct timeval ts;
- void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
- unsigned ms;
- char str[100];
- int h, line = 1;
- s32 gain;
-
- if (!vbuf)
- return;
-
- for (h = 0; h < hmax; h++)
- memcpy(vbuf + h * wmax * dev->pixelsize,
- dev->line + (dev->mv_count % wmax) * dev->pixelsize,
- wmax * dev->pixelsize);
-
- /* Updates stream time */
-
- dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
- dev->jiffies = jiffies;
- ms = dev->ms;
- snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
- (ms / (60 * 60 * 1000)) % 24,
- (ms / (60 * 1000)) % 60,
- (ms / 1000) % 60,
- ms % 1000);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " %dx%d, input %d ",
- dev->width, dev->height, dev->input);
- gen_text(dev, vbuf, line++ * 16, 16, str);
-
- gain = v4l2_ctrl_g_ctrl(dev->gain);
- mutex_lock(dev->ctrl_handler.lock);
- snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
- 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), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ",
- dev->autogain->cur.val, gain, dev->volume->cur.val,
- dev->alpha->cur.val);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
- dev->int32->cur.val,
- dev->int64->cur.val64,
- dev->bitmask->cur.val);
- 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);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
- dev->int_menu->qmenu_int[dev->int_menu->cur.val],
- dev->int_menu->cur.val);
- gen_text(dev, vbuf, line++ * 16, 16, str);
- mutex_unlock(dev->ctrl_handler.lock);
- 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;
-
- buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
- dev->field_count++;
- buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
- do_gettimeofday(&ts);
- buf->vb.v4l2_buf.timestamp = ts;
-}
-
-static void vivi_thread_tick(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
- struct vivi_buffer *buf;
- unsigned long flags = 0;
-
- dprintk(dev, 1, "Thread tick\n");
-
- spin_lock_irqsave(&dev->slock, flags);
- if (list_empty(&dma_q->active)) {
- dprintk(dev, 1, "No active queue to serve\n");
- spin_unlock_irqrestore(&dev->slock, flags);
- return;
- }
-
- buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
- list_del(&buf->list);
- spin_unlock_irqrestore(&dev->slock, flags);
-
- do_gettimeofday(&buf->vb.v4l2_buf.timestamp);
-
- /* Fill buffer */
- vivi_fillbuff(dev, buf);
- dprintk(dev, 1, "filled buffer %p\n", buf);
-
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
- dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
-}
-
-#define frames_to_ms(frames) \
- ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)
-
-static void vivi_sleep(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
- int timeout;
- DECLARE_WAITQUEUE(wait, current);
-
- dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
- (unsigned long)dma_q);
-
- add_wait_queue(&dma_q->wq, &wait);
- if (kthread_should_stop())
- goto stop_task;
-
- /* Calculate time to wake up */
- timeout = msecs_to_jiffies(frames_to_ms(1));
-
- vivi_thread_tick(dev);
-
- schedule_timeout_interruptible(timeout);
-
-stop_task:
- remove_wait_queue(&dma_q->wq, &wait);
- try_to_freeze();
-}
-
-static int vivi_thread(void *data)
-{
- struct vivi_dev *dev = data;
-
- dprintk(dev, 1, "thread started\n");
-
- set_freezable();
-
- for (;;) {
- vivi_sleep(dev);
-
- if (kthread_should_stop())
- break;
- }
- dprintk(dev, 1, "thread: exit\n");
- return 0;
-}
-
-static int vivi_start_generating(struct vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- /* Resets frame counters */
- dev->ms = 0;
- dev->mv_count = 0;
- dev->jiffies = jiffies;
-
- dma_q->frame = 0;
- dma_q->ini_jiffies = jiffies;
- dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);
-
- if (IS_ERR(dma_q->kthread)) {
- v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
- 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 vivi_dev *dev)
-{
- struct vivi_dmaqueue *dma_q = &dev->vidq;
-
- dprintk(dev, 1, "%s\n", __func__);
-
- /* shutdown control thread */
- if (dma_q->kthread) {
- kthread_stop(dma_q->kthread);
- dma_q->kthread = NULL;
- }
-
- /*
- * 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 queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], void *alloc_ctxs[])
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- unsigned long size;
-
- if (fmt)
- size = fmt->fmt.pix.sizeimage;
- else
- size = dev->width * dev->height * dev->pixelsize;
-
- if (size == 0)
- return -EINVAL;
-
- if (0 == *nbuffers)
- *nbuffers = 32;
-
- while (size * *nbuffers > vid_limit * 1024 * 1024)
- (*nbuffers)--;
-
- *nplanes = 1;
-
- sizes[0] = size;
-
- /*
- * videobuf2-vmalloc allocator is context-less so no need to set
- * alloc_ctxs array.
- */
-
- dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
- *nbuffers, size);
-
- return 0;
-}
-
-static int buffer_prepare(struct vb2_buffer *vb)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
- struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
- unsigned long size;
-
- 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;
-
- size = dev->width * dev->height * dev->pixelsize;
- 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);
-
- buf->fmt = dev->fmt;
-
- precalculate_bars(dev);
- precalculate_line(dev);
-
- return 0;
-}
-
-static void buffer_queue(struct vb2_buffer *vb)
-{
- 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__);
-
- spin_lock_irqsave(&dev->slock, flags);
- list_add_tail(&buf->list, &vidq->active);
- spin_unlock_irqrestore(&dev->slock, flags);
-}
-
-static int start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- 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);
-}
-
-static void vivi_unlock(struct vb2_queue *vq)
-{
- struct vivi_dev *dev = vb2_get_drv_priv(vq);
- mutex_unlock(&dev->mutex);
-}
-
-
-static struct vb2_ops vivi_video_qops = {
- .queue_setup = queue_setup,
- .buf_prepare = buffer_prepare,
- .buf_queue = buffer_queue,
- .start_streaming = start_streaming,
- .stop_streaming = stop_streaming,
- .wait_prepare = vivi_unlock,
- .wait_finish = vivi_lock,
-};
-
-/* ------------------------------------------------------------------
- IOCTL vidioc handling
- ------------------------------------------------------------------*/
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- strcpy(cap->driver, "vivi");
- strcpy(cap->card, "vivi");
- strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
- cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
- return 0;
-}
-
-static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct vivi_fmt *fmt;
-
- if (f->index >= ARRAY_SIZE(formats))
- return -EINVAL;
-
- fmt = &formats[f->index];
-
- strlcpy(f->description, fmt->name, sizeof(f->description));
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
-static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- f->fmt.pix.width = dev->width;
- f->fmt.pix.height = dev->height;
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- f->fmt.pix.pixelformat = dev->fmt->fourcc;
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * dev->fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- if (dev->fmt->fourcc == V4L2_PIX_FMT_YUYV ||
- dev->fmt->fourcc == V4L2_PIX_FMT_UYVY)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- else
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct vivi_dev *dev = video_drvdata(file);
- struct vivi_fmt *fmt;
-
- fmt = get_format(f);
- if (!fmt) {
- dprintk(dev, 1, "Fourcc format (0x%08x) unknown.\n",
- f->fmt.pix.pixelformat);
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- fmt = get_format(f);
- }
-
- f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 2,
- &f->fmt.pix.height, 32, MAX_HEIGHT, 0, 0);
- f->fmt.pix.bytesperline =
- (f->fmt.pix.width * fmt->depth) >> 3;
- f->fmt.pix.sizeimage =
- f->fmt.pix.height * f->fmt.pix.bytesperline;
- if (fmt->fourcc == V4L2_PIX_FMT_YUYV ||
- fmt->fourcc == V4L2_PIX_FMT_UYVY)
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
- else
- f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
- return 0;
-}
-
-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 (vb2_is_busy(q)) {
- dprintk(dev, 1, "%s device busy\n", __func__);
- return -EBUSY;
- }
-
- dev->fmt = get_format(f);
- dev->pixelsize = dev->fmt->depth / 8;
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
-
- return 0;
-}
-
-/* only one input in this sample driver */
-static int vidioc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index >= NUM_INPUTS)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- sprintf(inp->name, "Camera %u", inp->index);
- return 0;
-}
-
-static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- *i = dev->input;
- return 0;
-}
-
-static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- if (i >= NUM_INPUTS)
- return -EINVAL;
-
- if (i == dev->input)
- return 0;
-
- dev->input = i;
- precalculate_bars(dev);
- precalculate_line(dev);
- return 0;
-}
-
-/* --- controls ---------------------------------------------- */
-
-static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
- if (ctrl == dev->autogain)
- dev->gain->val = jiffies & 0xff;
- return 0;
-}
-
-static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
-
- switch (ctrl->id) {
- case V4L2_CID_ALPHA_COMPONENT:
- dev->alpha_component = ctrl->val;
- break;
- default:
- if (ctrl == dev->button)
- dev->button_pressed = 30;
- break;
- }
- return 0;
-}
-
-/* ------------------------------------------------------------------
- File operations for the device
- ------------------------------------------------------------------*/
-
-static const struct v4l2_ctrl_ops vivi_ctrl_ops = {
- .g_volatile_ctrl = vivi_g_volatile_ctrl,
- .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_ctrl_config vivi_ctrl_bitmask = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 6,
- .name = "Bitmask",
- .type = V4L2_CTRL_TYPE_BITMASK,
- .def = 0x80002000,
- .min = 0,
- .max = 0x80402010,
- .step = 0,
-};
-
-static const s64 vivi_ctrl_int_menu_values[] = {
- 1, 1, 2, 3, 5, 8, 13, 21, 42,
-};
-
-static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
- .ops = &vivi_ctrl_ops,
- .id = VIVI_CID_CUSTOM_BASE + 7,
- .name = "Integer menu",
- .type = V4L2_CTRL_TYPE_INTEGER_MENU,
- .min = 1,
- .max = 8,
- .def = 4,
- .menu_skip_mask = 0x02,
- .qmenu_int = vivi_ctrl_int_menu_values,
-};
-
-static const struct v4l2_file_operations vivi_fops = {
- .owner = THIS_MODULE,
- .open = v4l2_fh_open,
- .release = vb2_fop_release,
- .read = vb2_fop_read,
- .poll = vb2_fop_poll,
- .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
- .mmap = vb2_fop_mmap,
-};
-
-static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_enum_input = vidioc_enum_input,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static struct video_device vivi_template = {
- .name = "vivi",
- .fops = &vivi_fops,
- .ioctl_ops = &vivi_ioctl_ops,
- .release = video_device_release_empty,
-};
-
-/* -----------------------------------------------------------------
- Initialization and module stuff
- ------------------------------------------------------------------*/
-
-static int vivi_release(void)
-{
- struct vivi_dev *dev;
- struct list_head *list;
-
- while (!list_empty(&vivi_devlist)) {
- list = vivi_devlist.next;
- list_del(list);
- dev = list_entry(list, struct vivi_dev, vivi_devlist);
-
- v4l2_info(&dev->v4l2_dev, "unregistering %s\n",
- video_device_node_name(&dev->vdev));
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- v4l2_ctrl_handler_free(&dev->ctrl_handler);
- kfree(dev);
- }
-
- return 0;
-}
-
-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);
- if (!dev)
- return -ENOMEM;
-
- snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
- "%s-%03d", VIVI_MODULE_NAME, inst);
- ret = v4l2_device_register(NULL, &dev->v4l2_dev);
- if (ret)
- goto free_dev;
-
- dev->fmt = &formats[0];
- dev->width = 640;
- dev->height = 480;
- dev->pixelsize = dev->fmt->depth / 8;
- 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->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
- dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_GAIN, 0, 255, 1, 100);
- dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
- V4L2_CID_ALPHA_COMPONENT, 0, 255, 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);
- dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
- dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL);
- if (hdl->error) {
- ret = hdl->error;
- goto unreg_dev;
- }
- v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);
- dev->v4l2_dev.ctrl_handler = hdl;
-
- /* initialize locks */
- spin_lock_init(&dev->slock);
-
- /* 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);
- init_waitqueue_head(&dev->vidq.wq);
-
- vfd = &dev->vdev;
- *vfd = vivi_template;
- vfd->debug = debug;
- vfd->v4l2_dev = &dev->v4l2_dev;
- vfd->queue = q;
- set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
-
- /*
- * Provide a mutex to v4l2 core. It will be used to protect
- * all fops and v4l2 ioctls.
- */
- vfd->lock = &dev->mutex;
- video_set_drvdata(vfd, dev);
-
- ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
- if (ret < 0)
- goto unreg_dev;
-
- /* Now that everything is fine, let's add it to device list */
- list_add_tail(&dev->vivi_devlist, &vivi_devlist);
-
- v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",
- video_device_node_name(vfd));
- return 0;
-
-unreg_dev:
- v4l2_ctrl_handler_free(hdl);
- v4l2_device_unregister(&dev->v4l2_dev);
-free_dev:
- kfree(dev);
- return ret;
-}
-
-/* This routine allocates from 1 to n_devs virtual drivers.
-
- The real maximum number of virtual drivers will depend on how many drivers
- will succeed. This is limited to the maximum number of devices that
- videodev supports, which is equal to VIDEO_NUM_DEVICES.
- */
-static int __init vivi_init(void)
-{
- const struct font_desc *font = find_font("VGA8x16");
- int ret = 0, i;
-
- if (font == NULL) {
- printk(KERN_ERR "vivi: could not find font\n");
- return -ENODEV;
- }
- font8x16 = font->data;
-
- if (n_devs <= 0)
- n_devs = 1;
-
- for (i = 0; i < n_devs; i++) {
- ret = vivi_create_instance(i);
- if (ret) {
- /* If some instantiations succeeded, keep driver */
- if (i)
- ret = 0;
- break;
- }
- }
-
- if (ret < 0) {
- printk(KERN_ERR "vivi: error %d while loading driver\n", ret);
- return ret;
- }
-
- printk(KERN_INFO "Video Technology Magazine Virtual Video "
- "Capture Board ver %s successfully loaded.\n",
- VIVI_VERSION);
-
- /* n_devs will reflect the actual number of allocated devices */
- n_devs = i;
-
- return ret;
-}
-
-static void __exit vivi_exit(void)
-{
- vivi_release();
-}
-
-module_init(vivi_init);
-module_exit(vivi_exit);
diff --git a/drivers/media/video/zoran/Kconfig b/drivers/media/video/zoran/Kconfig
deleted file mode 100644
index fd4120e4c104..000000000000
--- a/drivers/media/video/zoran/Kconfig
+++ /dev/null
@@ -1,74 +0,0 @@
-config VIDEO_ZORAN
- tristate "Zoran ZR36057/36067 Video For Linux"
- depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
- help
- Say Y for support for MJPEG capture cards based on the Zoran
- 36057/36067 PCI controller chipset. This includes the Iomega
- Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is
- a driver homepage at <http://mjpeg.sf.net/driver-zoran/>. For
- more information, check <file:Documentation/video4linux/Zoran>.
-
- To compile this driver as a module, choose M here: the
- module will be called zr36067.
-
-config VIDEO_ZORAN_DC30
- tristate "Pinnacle/Miro DC30(+) support"
- depends on VIDEO_ZORAN
- select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
- card. This also supports really old DC10 cards based on the
- zr36050 MJPEG codec and zr36016 VFE.
-
-config VIDEO_ZORAN_ZR36060
- tristate "Zoran ZR36060"
- depends on VIDEO_ZORAN
- help
- Say Y to support Zoran boards based on 36060 chips.
- This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33
- and 33 R10 and AverMedia 6 boards.
-
-config VIDEO_ZORAN_BUZ
- tristate "Iomega Buz support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Iomega Buz MJPEG capture/playback card.
-
-config VIDEO_ZORAN_DC10
- tristate "Pinnacle/Miro DC10(+) support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33
- tristate "Linux Media Labs LML33 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the Linux Media Labs LML33 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_LML33R10
- tristate "Linux Media Labs LML33R10 support"
- depends on VIDEO_ZORAN_ZR36060
- select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
- help
- support for the Linux Media Labs LML33R10 MJPEG capture/playback
- card.
-
-config VIDEO_ZORAN_AVS6EYES
- tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
- depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL
- select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO
- select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
- help
- Support for the AverMedia 6 Eyes video surveillance card.
diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
deleted file mode 100644
index c3602d6cd48e..000000000000
--- a/drivers/media/video/zoran/zoran_card.c
+++ /dev/null
@@ -1,1524 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * This part handles card-specific data and detection
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Currently maintained by:
- * Ronald Bultje <rbultje@ronald.bitfreak.net>
- * Laurent Pinchart <laurent.pinchart@skynet.be>
- * Mailinglist <mjpeg-users@lists.sf.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/delay.h>
-
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
-
-#include <linux/proc_fs.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <linux/videodev2.h>
-#include <linux/spinlock.h>
-#include <linux/sem.h>
-#include <linux/kmod.h>
-#include <linux/wait.h>
-
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-#include <media/v4l2-common.h>
-#include <media/bt819.h>
-
-#include "videocodec.h"
-#include "zoran.h"
-#include "zoran_card.h"
-#include "zoran_device.h"
-#include "zoran_procfs.h"
-
-extern const struct zoran_format zoran_formats[];
-
-static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
-module_param_array(card, int, NULL, 0444);
-MODULE_PARM_DESC(card, "Card type");
-
-/*
- The video mem address of the video card.
- The driver has a little database for some videocards
- to determine it from there. If your video card is not in there
- you have either to give it to the driver as a parameter
- or set in in a VIDIOCSFBUF ioctl
- */
-
-static unsigned long vidmem; /* default = 0 - Video memory base address */
-module_param(vidmem, ulong, 0444);
-MODULE_PARM_DESC(vidmem, "Default video memory base address");
-
-/*
- Default input and video norm at startup of the driver.
-*/
-
-static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */
-module_param(default_input, uint, 0444);
-MODULE_PARM_DESC(default_input,
- "Default input (0=Composite, 1=S-Video, 2=Internal)");
-
-static int default_mux = 1; /* 6 Eyes input selection */
-module_param(default_mux, int, 0644);
-MODULE_PARM_DESC(default_mux,
- "Default 6 Eyes mux setting (Input selection)");
-
-static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */
-module_param(default_norm, int, 0444);
-MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)");
-
-/* /dev/videoN, -1 for autodetect */
-static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
-module_param_array(video_nr, int, NULL, 0444);
-MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
-
-int v4l_nbufs = 4;
-int v4l_bufsize = 864; /* Everybody should be able to work with this setting */
-module_param(v4l_nbufs, int, 0644);
-MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
-module_param(v4l_bufsize, int, 0644);
-MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)");
-
-int jpg_nbufs = 32;
-int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */
-module_param(jpg_nbufs, int, 0644);
-MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use");
-module_param(jpg_bufsize, int, 0644);
-MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)");
-
-int pass_through = 0; /* 1=Pass through TV signal when device is not used */
- /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
-module_param(pass_through, int, 0644);
-MODULE_PARM_DESC(pass_through,
- "Pass TV signal through to TV-out when idling");
-
-int zr36067_debug = 1;
-module_param_named(debug, zr36067_debug, int, 0644);
-MODULE_PARM_DESC(debug, "Debug level (0-5)");
-
-#define ZORAN_VERSION "0.10.1"
-
-MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver");
-MODULE_AUTHOR("Serguei Miridonov");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(ZORAN_VERSION);
-
-#define ZR_DEVICE(subven, subdev, data) { \
- .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \
- .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) }
-
-static struct pci_device_id zr36067_pci_tbl[] = {
- ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus),
- ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus),
- ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10),
- ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ),
- ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS),
- {0}
-};
-MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);
-
-static unsigned int zoran_num; /* number of cards found */
-
-/* videocodec bus functions ZR36060 */
-static u32
-zr36060_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 0, 1, reg >> 8)
- || post_office_write(zr, 0, 2, reg & 0xff)) {
- return -1;
- }
-
- data = post_office_read(zr, 0, 3) & 0xff;
- return data;
-}
-
-static void
-zr36060_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 0, 1, reg >> 8)
- || post_office_write(zr, 0, 2, reg & 0xff)) {
- return;
- }
-
- post_office_write(zr, 0, 3, val & 0xff);
-}
-
-/* videocodec bus functions ZR36050 */
-static u32
-zr36050_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
- return -1;
- }
-
- data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read
- return data;
-}
-
-static void
-zr36050_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)
- || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES
- return;
- }
-
- post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data
-}
-
-/* videocodec bus functions ZR36016 */
-static u32
-zr36016_read (struct videocodec *codec,
- u16 reg)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
- __u32 data;
-
- if (post_office_wait(zr)) {
- return -1;
- }
-
- data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read
- return data;
-}
-
-/* hack for in zoran_device.c */
-void
-zr36016_write (struct videocodec *codec,
- u16 reg,
- u32 val)
-{
- struct zoran *zr = (struct zoran *) codec->master_data->data;
-
- if (post_office_wait(zr)) {
- return;
- }
-
- post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data
-}
-
-/*
- * Board specific information
- */
-
-static void
-dc10_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- /* Pixel clock selection */
- GPIO(zr, 4, 0);
- GPIO(zr, 5, 1);
- /* Enable the video bus sync signals */
- GPIO(zr, 7, 0);
-}
-
-static void
-dc10plus_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-}
-
-static void
-buz_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- /* some stuff from Iomega */
- pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
- pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020);
- pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000);
-}
-
-static void
-lml33_init (struct zoran *zr)
-{
- dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- GPIO(zr, 2, 1); // Set Composite input/output
-}
-
-static void
-avs6eyes_init (struct zoran *zr)
-{
- // AverMedia 6-Eyes original driver by Christer Weinigel
-
- // Lifted straight from Christer's old driver and
- // modified slightly by Martin Samuelsson.
-
- int mux = default_mux; /* 1 = BT866, 7 = VID1 */
-
- GPIO(zr, 4, 1); /* Bt866 SLEEP on */
- udelay(2);
-
- GPIO(zr, 0, 1); /* ZR36060 /RESET on */
- GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */
- GPIO(zr, 2, mux & 1); /* MUX S0 */
- GPIO(zr, 3, 0); /* /FRAME on */
- GPIO(zr, 4, 0); /* Bt866 SLEEP off */
- GPIO(zr, 5, mux & 2); /* MUX S1 */
- GPIO(zr, 6, 0); /* ? */
- GPIO(zr, 7, mux & 4); /* MUX S2 */
-
-}
-
-static char *
-codecid_to_modulename (u16 codecid)
-{
- char *name = NULL;
-
- switch (codecid) {
- case CODEC_TYPE_ZR36060:
- name = "zr36060";
- break;
- case CODEC_TYPE_ZR36050:
- name = "zr36050";
- break;
- case CODEC_TYPE_ZR36016:
- name = "zr36016";
- break;
- }
-
- return name;
-}
-
-// struct tvnorm {
-// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
-// };
-
-static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
-static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
-static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
-
-static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 };
-
-/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */
-static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 };
-static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 };
-
-/* FIXME: I cannot swap U and V in saa7114, so i do one
- * pixel left shift in zoran (75 -> 74)
- * (Maxim Yevtyushkin <max@linuxmedialabs.com>) */
-static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 };
-
-/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I
- * copy Maxim's left shift hack for the 6 Eyes.
- *
- * Christer's driver used the unshifted norms, though...
- * /Sam */
-static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
-static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
-
-static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
-static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
-static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
-static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
-static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
-static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
-static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
-static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
-static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
-static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
-
-static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
- {
- .type = DC10_old,
- .name = "DC10(old)",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = DC10_new,
- .name = "DC10(new)",
- .i2c_decoder = "saa7110",
- .addrs_decoder = saa7110_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 3,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" },
- { 5, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel,
- &f60sqpixel,
- &f50sqpixel},
- .jpeg_int = ZR36057_ISR_GIRQ0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { -1, 1},
- .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10plus_init,
- }, {
- .type = DC10plus,
- .name = "DC10plus",
- .i2c_decoder = "saa7110",
- .addrs_decoder = saa7110_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 3,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" },
- { 5, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel,
- &f60sqpixel,
- &f50sqpixel
- },
- .jpeg_int = ZR36057_ISR_GIRQ0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { -1, 1 },
- .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10plus_init,
- }, {
- .type = DC30,
- .name = "DC30",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = DC30plus,
- .name = "DC30plus",
- .i2c_decoder = "vpx3220a",
- .addrs_decoder = vpx3220_addrs,
- .i2c_encoder = "adv7175",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36050,
- .video_vfe = CODEC_TYPE_ZR36016,
-
- .inputs = 3,
- .input = {
- { 1, "Composite" },
- { 2, "S-Video" },
- { 0, "Internal/comp" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50sqpixel_dc10,
- &f60sqpixel_dc10,
- &f50sqpixel_dc10
- },
- .jpeg_int = 0,
- .vsync_int = ZR36057_ISR_GIRQ1,
- .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 },
- .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 },
- .gpcs = { -1, 0 },
- .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gws_not_connected = 0,
- .input_mux = 0,
- .init = &dc10_init,
- }, {
- .type = LML33,
- .name = "LML33",
- .i2c_decoder = "bt819a",
- .addrs_decoder = bt819_addrs,
- .i2c_encoder = "bt856",
- .addrs_encoder = bt856_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_lml33,
- &f60ccir601_lml33,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &lml33_init,
- }, {
- .type = LML33R10,
- .name = "LML33R10",
- .i2c_decoder = "saa7114",
- .addrs_decoder = saa7114_addrs,
- .i2c_encoder = "adv7170",
- .addrs_encoder = adv717x_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 0, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_lm33r10,
- &f60ccir601_lm33r10,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &lml33_init,
- }, {
- .type = BUZ,
- .name = "Buz",
- .i2c_decoder = "saa7111",
- .addrs_decoder = saa7111_addrs,
- .i2c_encoder = "saa7185",
- .addrs_encoder = saa7185_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 2,
- .input = {
- { 3, "Composite" },
- { 7, "S-Video" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
- .tvn = {
- &f50ccir601,
- &f60ccir601,
- &f50ccir601
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 },
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 },
- .gpcs = { 3, 1 },
- .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 },
- .gws_not_connected = 1,
- .input_mux = 0,
- .init = &buz_init,
- }, {
- .type = AVS6EYES,
- .name = "6-Eyes",
- /* AverMedia chose not to brand the 6-Eyes. Thus it
- can't be autodetected, and requires card=x. */
- .i2c_decoder = "ks0127",
- .addrs_decoder = ks0127_addrs,
- .i2c_encoder = "bt866",
- .addrs_encoder = bt866_addrs,
- .video_codec = CODEC_TYPE_ZR36060,
-
- .inputs = 10,
- .input = {
- { 0, "Composite 1" },
- { 1, "Composite 2" },
- { 2, "Composite 3" },
- { 4, "Composite 4" },
- { 5, "Composite 5" },
- { 6, "Composite 6" },
- { 8, "S-Video 1" },
- { 9, "S-Video 2" },
- {10, "S-Video 3" },
- {15, "YCbCr" }
- },
- .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
- .tvn = {
- &f50ccir601_avs6eyes,
- &f60ccir601_avs6eyes,
- NULL
- },
- .jpeg_int = ZR36057_ISR_GIRQ1,
- .vsync_int = ZR36057_ISR_GIRQ0,
- .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam
- .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam
- .gpcs = { 3, 1 }, // Validity unknown /Sam
- .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam
- .gws_not_connected = 1,
- .input_mux = 1,
- .init = &avs6eyes_init,
- }
-
-};
-
-/*
- * I2C functions
- */
-/* software I2C functions */
-static int
-zoran_i2c_getsda (void *data)
-{
- struct zoran *zr = (struct zoran *) data;
-
- return (btread(ZR36057_I2CBR) >> 1) & 1;
-}
-
-static int
-zoran_i2c_getscl (void *data)
-{
- struct zoran *zr = (struct zoran *) data;
-
- return btread(ZR36057_I2CBR) & 1;
-}
-
-static void
-zoran_i2c_setsda (void *data,
- int state)
-{
- struct zoran *zr = (struct zoran *) data;
-
- if (state)
- zr->i2cbr |= 2;
- else
- zr->i2cbr &= ~2;
- btwrite(zr->i2cbr, ZR36057_I2CBR);
-}
-
-static void
-zoran_i2c_setscl (void *data,
- int state)
-{
- struct zoran *zr = (struct zoran *) data;
-
- if (state)
- zr->i2cbr |= 1;
- else
- zr->i2cbr &= ~1;
- btwrite(zr->i2cbr, ZR36057_I2CBR);
-}
-
-static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
- .setsda = zoran_i2c_setsda,
- .setscl = zoran_i2c_setscl,
- .getsda = zoran_i2c_getsda,
- .getscl = zoran_i2c_getscl,
- .udelay = 10,
- .timeout = 100,
-};
-
-static int
-zoran_register_i2c (struct zoran *zr)
-{
- memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
- sizeof(struct i2c_algo_bit_data));
- zr->i2c_algo.data = zr;
- strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
- sizeof(zr->i2c_adapter.name));
- i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
- zr->i2c_adapter.algo_data = &zr->i2c_algo;
- zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
- return i2c_bit_add_bus(&zr->i2c_adapter);
-}
-
-static void
-zoran_unregister_i2c (struct zoran *zr)
-{
- i2c_del_adapter(&zr->i2c_adapter);
-}
-
-/* Check a zoran_params struct for correctness, insert default params */
-
-int
-zoran_check_jpg_settings (struct zoran *zr,
- struct zoran_jpg_settings *settings,
- int try)
-{
- int err = 0, err0 = 0;
-
- dprintk(4,
- KERN_DEBUG
- "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
- ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
- settings->VerDcm, settings->TmpDcm);
- dprintk(4,
- KERN_DEBUG
- "%s: %s - x: %d, y: %d, w: %d, y: %d\n",
- ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
- settings->img_width, settings->img_height);
- /* Check decimation, set default values for decimation = 1, 2, 4 */
- switch (settings->decimation) {
- case 1:
-
- settings->HorDcm = 1;
- settings->VerDcm = 1;
- settings->TmpDcm = 1;
- settings->field_per_buff = 2;
- settings->img_x = 0;
- settings->img_y = 0;
- settings->img_width = BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 2:
-
- settings->HorDcm = 2;
- settings->VerDcm = 1;
- settings->TmpDcm = 2;
- settings->field_per_buff = 1;
- settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings->img_y = 0;
- settings->img_width =
- (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 4:
-
- if (zr->card.type == DC10_new) {
- dprintk(1,
- KERN_DEBUG
- "%s: %s - HDec by 4 is not supported on the DC10\n",
- ZR_DEVNAME(zr), __func__);
- err0++;
- break;
- }
-
- settings->HorDcm = 4;
- settings->VerDcm = 2;
- settings->TmpDcm = 2;
- settings->field_per_buff = 1;
- settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings->img_y = 0;
- settings->img_width =
- (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- settings->img_height = BUZ_MAX_HEIGHT / 2;
- break;
- case 0:
-
- /* We have to check the data the user has set */
-
- if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
- (zr->card.type == DC10_new || settings->HorDcm != 4)) {
- settings->HorDcm = clamp(settings->HorDcm, 1, 2);
- err0++;
- }
- if (settings->VerDcm != 1 && settings->VerDcm != 2) {
- settings->VerDcm = clamp(settings->VerDcm, 1, 2);
- err0++;
- }
- if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
- settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
- err0++;
- }
- if (settings->field_per_buff != 1 &&
- settings->field_per_buff != 2) {
- settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
- err0++;
- }
- if (settings->img_x < 0) {
- settings->img_x = 0;
- err0++;
- }
- if (settings->img_y < 0) {
- settings->img_y = 0;
- err0++;
- }
- if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
- settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
- err0++;
- }
- if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
- settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
- err0++;
- }
- if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
- settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
- err0++;
- }
- if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
- settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
- err0++;
- }
- if (settings->img_width % (16 * settings->HorDcm) != 0) {
- settings->img_width -= settings->img_width % (16 * settings->HorDcm);
- if (settings->img_width == 0)
- settings->img_width = 16 * settings->HorDcm;
- err0++;
- }
- if (settings->img_height % (8 * settings->VerDcm) != 0) {
- settings->img_height -= settings->img_height % (8 * settings->VerDcm);
- if (settings->img_height == 0)
- settings->img_height = 8 * settings->VerDcm;
- err0++;
- }
-
- if (!try && err0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - error in params for decimation = 0\n",
- ZR_DEVNAME(zr), __func__);
- err++;
- }
- break;
- default:
- dprintk(1,
- KERN_ERR
- "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
- ZR_DEVNAME(zr), __func__, settings->decimation);
- err++;
- break;
- }
-
- if (settings->jpg_comp.quality > 100)
- settings->jpg_comp.quality = 100;
- if (settings->jpg_comp.quality < 5)
- settings->jpg_comp.quality = 5;
- if (settings->jpg_comp.APPn < 0)
- settings->jpg_comp.APPn = 0;
- if (settings->jpg_comp.APPn > 15)
- settings->jpg_comp.APPn = 15;
- if (settings->jpg_comp.APP_len < 0)
- settings->jpg_comp.APP_len = 0;
- if (settings->jpg_comp.APP_len > 60)
- settings->jpg_comp.APP_len = 60;
- if (settings->jpg_comp.COM_len < 0)
- settings->jpg_comp.COM_len = 0;
- if (settings->jpg_comp.COM_len > 60)
- settings->jpg_comp.COM_len = 60;
- if (err)
- return -EINVAL;
- return 0;
-}
-
-void
-zoran_open_init_params (struct zoran *zr)
-{
- int i;
-
- /* User must explicitly set a window */
- zr->overlay_settings.is_set = 0;
- zr->overlay_mask = NULL;
- zr->overlay_active = ZORAN_FREE;
-
- zr->v4l_memgrab_active = 0;
- zr->v4l_overlay_active = 0;
- zr->v4l_grab_frame = NO_GRAB_ACTIVE;
- zr->v4l_grab_seq = 0;
- zr->v4l_settings.width = 192;
- zr->v4l_settings.height = 144;
- zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */
- zr->v4l_settings.bytesperline =
- zr->v4l_settings.width *
- ((zr->v4l_settings.format->depth + 7) / 8);
-
- /* DMA ring stuff for V4L */
- zr->v4l_pend_tail = 0;
- zr->v4l_pend_head = 0;
- zr->v4l_sync_tail = 0;
- zr->v4l_buffers.active = ZORAN_FREE;
- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
- zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- }
- zr->v4l_buffers.allocated = 0;
-
- for (i = 0; i < BUZ_MAX_FRAME; i++) {
- zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- }
- zr->jpg_buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- /* Set necessary params and call zoran_check_jpg_settings to set the defaults */
- zr->jpg_settings.decimation = 1;
- zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */
- if (zr->card.type != BUZ)
- zr->jpg_settings.odd_even = 1;
- else
- zr->jpg_settings.odd_even = 0;
- zr->jpg_settings.jpg_comp.APPn = 0;
- zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */
- memset(zr->jpg_settings.jpg_comp.APP_data, 0,
- sizeof(zr->jpg_settings.jpg_comp.APP_data));
- zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */
- memset(zr->jpg_settings.jpg_comp.COM_data, 0,
- sizeof(zr->jpg_settings.jpg_comp.COM_data));
- zr->jpg_settings.jpg_comp.jpeg_markers =
- V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT;
- i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
- if (i)
- dprintk(1, KERN_ERR "%s: %s internal error\n",
- ZR_DEVNAME(zr), __func__);
-
- clear_interrupt_counters(zr);
- zr->testing = 0;
-}
-
-static void __devinit
-test_interrupts (struct zoran *zr)
-{
- DEFINE_WAIT(wait);
- int timeout, icr;
-
- clear_interrupt_counters(zr);
-
- zr->testing = 1;
- icr = btread(ZR36057_ICR);
- btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
- prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE);
- timeout = schedule_timeout(HZ);
- finish_wait(&zr->test_q, &wait);
- btwrite(0, ZR36057_ICR);
- btwrite(0x78000000, ZR36057_ISR);
- zr->testing = 0;
- dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr));
- if (timeout) {
- dprintk(1, ": time spent: %d\n", 1 * HZ - timeout);
- }
- if (zr36067_debug > 1)
- print_interrupts(zr);
- btwrite(icr, ZR36057_ICR);
-}
-
-static int __devinit
-zr36057_init (struct zoran *zr)
-{
- int j, err;
-
- dprintk(1,
- KERN_INFO
- "%s: %s - initializing card[%d], zr=%p\n",
- ZR_DEVNAME(zr), __func__, zr->id, zr);
-
- /* default setup of all parameters which will persist between opens */
- zr->user = 0;
-
- init_waitqueue_head(&zr->v4l_capq);
- init_waitqueue_head(&zr->jpg_capq);
- init_waitqueue_head(&zr->test_q);
- zr->jpg_buffers.allocated = 0;
- zr->v4l_buffers.allocated = 0;
-
- zr->vbuf_base = (void *) vidmem;
- zr->vbuf_width = 0;
- zr->vbuf_height = 0;
- zr->vbuf_depth = 0;
- zr->vbuf_bytesperline = 0;
-
- /* Avoid nonsense settings from user for default input/norm */
- if (default_norm < 0 || default_norm > 2)
- default_norm = 0;
- if (default_norm == 0) {
- zr->norm = V4L2_STD_PAL;
- zr->timing = zr->card.tvn[0];
- } else if (default_norm == 1) {
- zr->norm = V4L2_STD_NTSC;
- zr->timing = zr->card.tvn[1];
- } else {
- zr->norm = V4L2_STD_SECAM;
- zr->timing = zr->card.tvn[2];
- }
- if (zr->timing == NULL) {
- dprintk(1,
- KERN_WARNING
- "%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
- ZR_DEVNAME(zr), __func__);
- zr->norm = V4L2_STD_PAL;
- zr->timing = zr->card.tvn[0];
- }
-
- if (default_input > zr->card.inputs-1) {
- dprintk(1,
- KERN_WARNING
- "%s: default_input value %d out of range (0-%d)\n",
- ZR_DEVNAME(zr), default_input, zr->card.inputs-1);
- default_input = 0;
- }
- zr->input = default_input;
-
- /* default setup (will be repeated at every open) */
- zoran_open_init_params(zr);
-
- /* allocate memory *before* doing anything to the hardware
- * in case allocation fails */
- zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
- zr->video_dev = video_device_alloc();
- if (!zr->stat_com || !zr->video_dev) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc (STAT_COM) failed\n",
- ZR_DEVNAME(zr), __func__);
- err = -ENOMEM;
- goto exit_free;
- }
- for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
- zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */
- }
-
- /*
- * Now add the template and register the device unit.
- */
- memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
- zr->video_dev->parent = &zr->pci_dev->dev;
- strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
- err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
- if (err < 0)
- goto exit_free;
- video_set_drvdata(zr->video_dev, zr);
-
- zoran_init_hardware(zr);
- if (zr36067_debug > 2)
- detect_guest_activity(zr);
- test_interrupts(zr);
- if (!pass_through) {
- decoder_call(zr, video, s_stream, 0);
- encoder_call(zr, video, s_routing, 2, 0, 0);
- }
-
- zr->zoran_proc = NULL;
- zr->initialized = 1;
- return 0;
-
-exit_free:
- kfree(zr->stat_com);
- kfree(zr->video_dev);
- return err;
-}
-
-static void __devexit zoran_remove(struct pci_dev *pdev)
-{
- struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
- struct zoran *zr = to_zoran(v4l2_dev);
-
- if (!zr->initialized)
- goto exit_free;
-
- /* unregister videocodec bus */
- if (zr->codec) {
- struct videocodec_master *master = zr->codec->master_data;
-
- videocodec_detach(zr->codec);
- kfree(master);
- }
- if (zr->vfe) {
- struct videocodec_master *master = zr->vfe->master_data;
-
- videocodec_detach(zr->vfe);
- kfree(master);
- }
-
- /* unregister i2c bus */
- zoran_unregister_i2c(zr);
- /* disable PCI bus-mastering */
- zoran_set_pci_master(zr, 0);
- /* put chip into reset */
- btwrite(0, ZR36057_SPGPPCR);
- free_irq(zr->pci_dev->irq, zr);
- /* unmap and free memory */
- kfree(zr->stat_com);
- zoran_proc_cleanup(zr);
- iounmap(zr->zr36057_mem);
- pci_disable_device(zr->pci_dev);
- video_unregister_device(zr->video_dev);
-exit_free:
- v4l2_device_unregister(&zr->v4l2_dev);
- kfree(zr);
-}
-
-void
-zoran_vdev_release (struct video_device *vdev)
-{
- kfree(vdev);
-}
-
-static struct videocodec_master * __devinit
-zoran_setup_videocodec (struct zoran *zr,
- int type)
-{
- struct videocodec_master *m = NULL;
-
- m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
- if (!m) {
- dprintk(1, KERN_ERR "%s: %s - no memory\n",
- ZR_DEVNAME(zr), __func__);
- return m;
- }
-
- /* magic and type are unused for master struct. Makes sense only at
- codec structs.
- In the past, .type were initialized to the old V4L1 .hardware
- value, as VID_HARDWARE_ZR36067
- */
- m->magic = 0L;
- m->type = 0;
-
- m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
- strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
- m->data = zr;
-
- switch (type)
- {
- case CODEC_TYPE_ZR36060:
- m->readreg = zr36060_read;
- m->writereg = zr36060_write;
- m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE;
- break;
- case CODEC_TYPE_ZR36050:
- m->readreg = zr36050_read;
- m->writereg = zr36050_write;
- m->flags |= CODEC_FLAG_JPEG;
- break;
- case CODEC_TYPE_ZR36016:
- m->readreg = zr36016_read;
- m->writereg = zr36016_write;
- m->flags |= CODEC_FLAG_VFE;
- break;
- }
-
- return m;
-}
-
-static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
-{
- struct zoran *zr = to_zoran(sd->v4l2_dev);
-
- /* Bt819 needs to reset its FIFO buffer using #FRST pin and
- LML33 card uses GPIO(7) for that. */
- if (cmd == BT819_FIFO_RESET_LOW)
- GPIO(zr, 7, 0);
- else if (cmd == BT819_FIFO_RESET_HIGH)
- GPIO(zr, 7, 1);
-}
-
-/*
- * Scan for a Buz card (actually for the PCI controller ZR36057),
- * request the irq and map the io memory
- */
-static int __devinit zoran_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- unsigned char latency, need_latency;
- struct zoran *zr;
- int result;
- struct videocodec_master *master_vfe = NULL;
- struct videocodec_master *master_codec = NULL;
- int card_num;
- char *codec_name, *vfe_name;
- unsigned int nr;
-
-
- nr = zoran_num++;
- if (nr >= BUZ_MAX) {
- dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
- ZORAN_NAME, BUZ_MAX);
- return -ENOENT;
- }
-
- zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
- if (!zr) {
- dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
- ZORAN_NAME, __func__);
- return -ENOMEM;
- }
- zr->v4l2_dev.notify = zoran_subdev_notify;
- if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
- goto zr_free_mem;
- zr->pci_dev = pdev;
- zr->id = nr;
- snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
- spin_lock_init(&zr->spinlock);
- mutex_init(&zr->resource_lock);
- mutex_init(&zr->other_lock);
- if (pci_enable_device(pdev))
- goto zr_unreg;
- zr->revision = zr->pci_dev->revision;
-
- dprintk(1,
- KERN_INFO
- "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n",
- ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision,
- zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0));
- if (zr->revision >= 2) {
- dprintk(1,
- KERN_INFO
- "%s: Subsystem vendor=0x%04x id=0x%04x\n",
- ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor,
- zr->pci_dev->subsystem_device);
- }
-
- /* Use auto-detected card type? */
- if (card[nr] == -1) {
- if (zr->revision < 2) {
- dprintk(1,
- KERN_ERR
- "%s: No card type specified, please use the card=X module parameter\n",
- ZR_DEVNAME(zr));
- dprintk(1,
- KERN_ERR
- "%s: It is not possible to auto-detect ZR36057 based cards\n",
- ZR_DEVNAME(zr));
- goto zr_unreg;
- }
-
- card_num = ent->driver_data;
- if (card_num >= NUM_CARDS) {
- dprintk(1,
- KERN_ERR
- "%s: Unknown card, try specifying card=X module parameter\n",
- ZR_DEVNAME(zr));
- goto zr_unreg;
- }
- dprintk(3,
- KERN_DEBUG
- "%s: %s() - card %s detected\n",
- ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name);
- } else {
- card_num = card[nr];
- if (card_num >= NUM_CARDS || card_num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: User specified card type %d out of range (0 .. %d)\n",
- ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
- goto zr_unreg;
- }
- }
-
- /* even though we make this a non pointer and thus
- * theoretically allow for making changes to this struct
- * on a per-individual card basis at runtime, this is
- * strongly discouraged. This structure is intended to
- * keep general card information, no settings or anything */
- zr->card = zoran_cards[card_num];
- snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)),
- "%s[%u]", zr->card.name, zr->id);
-
- zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
- if (!zr->zr36057_mem) {
- dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_unreg;
- }
-
- result = request_irq(zr->pci_dev->irq, zoran_irq,
- IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr);
- if (result < 0) {
- if (result == -EINVAL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - bad irq number or handler\n",
- ZR_DEVNAME(zr), __func__);
- } else if (result == -EBUSY) {
- dprintk(1,
- KERN_ERR
- "%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
- ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - can't assign irq, error code %d\n",
- ZR_DEVNAME(zr), __func__, result);
- }
- goto zr_unmap;
- }
-
- /* set PCI latency timer */
- pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
- &latency);
- need_latency = zr->revision > 1 ? 32 : 48;
- if (latency != need_latency) {
- dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
- ZR_DEVNAME(zr), latency, need_latency);
- pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
- need_latency);
- }
-
- zr36057_restart(zr);
- /* i2c */
- dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
- ZR_DEVNAME(zr));
-
- if (zoran_register_i2c(zr) < 0) {
- dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_irq;
- }
-
- zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
- &zr->i2c_adapter, zr->card.i2c_decoder,
- 0, zr->card.addrs_decoder);
-
- if (zr->card.i2c_encoder)
- zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev,
- &zr->i2c_adapter, zr->card.i2c_encoder,
- 0, zr->card.addrs_encoder);
-
- dprintk(2,
- KERN_INFO "%s: Initializing videocodec bus...\n",
- ZR_DEVNAME(zr));
-
- if (zr->card.video_codec) {
- codec_name = codecid_to_modulename(zr->card.video_codec);
- if (codec_name) {
- result = request_module(codec_name);
- if (result) {
- dprintk(1,
- KERN_ERR
- "%s: failed to load modules %s: %d\n",
- ZR_DEVNAME(zr), codec_name, result);
- }
- }
- }
- if (zr->card.video_vfe) {
- vfe_name = codecid_to_modulename(zr->card.video_vfe);
- if (vfe_name) {
- result = request_module(vfe_name);
- if (result < 0) {
- dprintk(1,
- KERN_ERR
- "%s: failed to load modules %s: %d\n",
- ZR_DEVNAME(zr), vfe_name, result);
- }
- }
- }
-
- /* reset JPEG codec */
- jpeg_codec_sleep(zr, 1);
- jpeg_codec_reset(zr);
- /* video bus enabled */
- /* display codec revision */
- if (zr->card.video_codec != 0) {
- master_codec = zoran_setup_videocodec(zr, zr->card.video_codec);
- if (!master_codec)
- goto zr_unreg_i2c;
- zr->codec = videocodec_attach(master_codec);
- if (!zr->codec) {
- dprintk(1, KERN_ERR "%s: %s - no codec found\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_codec;
- }
- if (zr->codec->type != zr->card.video_codec) {
- dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_detach_codec;
- }
- }
- if (zr->card.video_vfe != 0) {
- master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe);
- if (!master_vfe)
- goto zr_detach_codec;
- zr->vfe = videocodec_attach(master_vfe);
- if (!zr->vfe) {
- dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_free_vfe;
- }
- if (zr->vfe->type != zr->card.video_vfe) {
- dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
- ZR_DEVNAME(zr), __func__);
- goto zr_detach_vfe;
- }
- }
-
- /* take care of Natoma chipset and a revision 1 zr36057 */
- if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
- zr->jpg_buffers.need_contiguous = 1;
- dprintk(1, KERN_INFO
- "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
- ZR_DEVNAME(zr));
- }
-
- if (zr36057_init(zr) < 0)
- goto zr_detach_vfe;
-
- zoran_proc_init(zr);
-
- return 0;
-
-zr_detach_vfe:
- videocodec_detach(zr->vfe);
-zr_free_vfe:
- kfree(master_vfe);
-zr_detach_codec:
- videocodec_detach(zr->codec);
-zr_free_codec:
- kfree(master_codec);
-zr_unreg_i2c:
- zoran_unregister_i2c(zr);
-zr_free_irq:
- btwrite(0, ZR36057_SPGPPCR);
- free_irq(zr->pci_dev->irq, zr);
-zr_unmap:
- iounmap(zr->zr36057_mem);
-zr_unreg:
- v4l2_device_unregister(&zr->v4l2_dev);
-zr_free_mem:
- kfree(zr);
-
- return -ENODEV;
-}
-
-static struct pci_driver zoran_driver = {
- .name = "zr36067",
- .id_table = zr36067_pci_tbl,
- .probe = zoran_probe,
- .remove = __devexit_p(zoran_remove),
-};
-
-static int __init zoran_init(void)
-{
- int res;
-
- printk(KERN_INFO "Zoran MJPEG board driver version %s\n",
- ZORAN_VERSION);
-
- /* check the parameters we have been given, adjust if necessary */
- if (v4l_nbufs < 2)
- v4l_nbufs = 2;
- if (v4l_nbufs > VIDEO_MAX_FRAME)
- v4l_nbufs = VIDEO_MAX_FRAME;
- /* The user specfies the in KB, we want them in byte
- * (and page aligned) */
- v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
- if (v4l_bufsize < 32768)
- v4l_bufsize = 32768;
- /* 2 MB is arbitrary but sufficient for the maximum possible images */
- if (v4l_bufsize > 2048 * 1024)
- v4l_bufsize = 2048 * 1024;
- if (jpg_nbufs < 4)
- jpg_nbufs = 4;
- if (jpg_nbufs > BUZ_MAX_FRAME)
- jpg_nbufs = BUZ_MAX_FRAME;
- jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024);
- if (jpg_bufsize < 8192)
- jpg_bufsize = 8192;
- if (jpg_bufsize > (512 * 1024))
- jpg_bufsize = 512 * 1024;
- /* Use parameter for vidmem or try to find a video card */
- if (vidmem) {
- dprintk(1,
- KERN_INFO
- "%s: Using supplied video memory base address @ 0x%lx\n",
- ZORAN_NAME, vidmem);
- }
-
- /* some mainboards might not do PCI-PCI data transfer well */
- if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
- dprintk(1,
- KERN_WARNING
- "%s: chipset does not support reliable PCI-PCI DMA\n",
- ZORAN_NAME);
- }
-
- res = pci_register_driver(&zoran_driver);
- if (res) {
- dprintk(1,
- KERN_ERR
- "%s: Unable to register ZR36057 driver\n",
- ZORAN_NAME);
- return res;
- }
-
- return 0;
-}
-
-static void __exit zoran_exit(void)
-{
- pci_unregister_driver(&zoran_driver);
-}
-
-module_init(zoran_init);
-module_exit(zoran_exit);
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
deleted file mode 100644
index c6ccdeb6d8d6..000000000000
--- a/drivers/media/video/zoran/zoran_driver.c
+++ /dev/null
@@ -1,3090 +0,0 @@
-/*
- * Zoran zr36057/zr36067 PCI controller driver, for the
- * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux
- * Media Labs LML33/LML33R10.
- *
- * Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
- *
- * Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
- *
- * Changes for DC10/DC30 by Laurent Pinchart <laurent.pinchart@skynet.be>
- *
- * Changes for LML33R10 by Maxim Yevtyushkin <max@linuxmedialabs.com>
- *
- * Changes for videodev2/v4l2 by Ronald Bultje <rbultje@ronald.bitfreak.net>
- *
- * Based on
- *
- * Miro DC10 driver
- * Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
- *
- * Iomega Buz driver version 1.0
- * Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
- *
- * buz.0.0.3
- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
- *
- * bttv - Bt848 frame grabber driver
- * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
- * & Marcus Metzler (mocm@thp.uni-koeln.de)
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-#include <linux/wait.h>
-
-#include <linux/interrupt.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-
-#include <linux/spinlock.h>
-
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include "videocodec.h"
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/proc_fs.h>
-
-#include <linux/mutex.h>
-#include "zoran.h"
-#include "zoran_device.h"
-#include "zoran_card.h"
-
-
-const struct zoran_format zoran_formats[] = {
- {
- .name = "15-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_RGB555,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 15,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif|
- ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "15-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 15,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
- }, {
- .name = "16-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_RGB565,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif|
- ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "16-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
- }, {
- .name = "24-bit RGB",
- .fourcc = V4L2_PIX_FMT_BGR24,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 24,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
- }, {
- .name = "32-bit RGB LE",
- .fourcc = V4L2_PIX_FMT_BGR32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 32,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "32-bit RGB BE",
- .fourcc = V4L2_PIX_FMT_RGB32,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .depth = 32,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_RGB888,
- }, {
- .name = "4:2:2, packed, YUYV",
- .fourcc = V4L2_PIX_FMT_YUYV,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_YUV422,
- }, {
- .name = "4:2:2, packed, UYVY",
- .fourcc = V4L2_PIX_FMT_UYVY,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 16,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_OVERLAY,
- .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
- }, {
- .name = "Hardware-encoded Motion-JPEG",
- .fourcc = V4L2_PIX_FMT_MJPEG,
- .colorspace = V4L2_COLORSPACE_SMPTE170M,
- .depth = 0,
- .flags = ZORAN_FORMAT_CAPTURE |
- ZORAN_FORMAT_PLAYBACK |
- ZORAN_FORMAT_COMPRESSED,
- }
-};
-#define NUM_FORMATS ARRAY_SIZE(zoran_formats)
-
- /* small helper function for calculating buffersizes for v4l2
- * we calculate the nearest higher power-of-two, which
- * will be the recommended buffersize */
-static __u32
-zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
-{
- __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm;
- __u32 num = (1024 * 512) / (div);
- __u32 result = 2;
-
- num--;
- while (num) {
- num >>= 1;
- result <<= 1;
- }
-
- if (result > jpg_bufsize)
- return jpg_bufsize;
- if (result < 8192)
- return 8192;
- return result;
-}
-
-/* forward references */
-static void v4l_fbuffer_free(struct zoran_fh *fh);
-static void jpg_fbuffer_free(struct zoran_fh *fh);
-
-/* Set mapping mode */
-static void map_mode_raw(struct zoran_fh *fh)
-{
- fh->map_mode = ZORAN_MAP_MODE_RAW;
- fh->buffers.buffer_size = v4l_bufsize;
- fh->buffers.num_buffers = v4l_nbufs;
-}
-static void map_mode_jpg(struct zoran_fh *fh, int play)
-{
- fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
- fh->buffers.buffer_size = jpg_bufsize;
- fh->buffers.num_buffers = jpg_nbufs;
-}
-static inline const char *mode_name(enum zoran_map_mode mode)
-{
- return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
-}
-
-/*
- * Allocate the V4L grab buffers
- *
- * These have to be pysically contiguous.
- */
-
-static int v4l_fbuffer_alloc(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, off;
- unsigned char *mem;
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].v4l.fbuffer)
- dprintk(2,
- KERN_WARNING
- "%s: %s - buffer %d already allocated!?\n",
- ZR_DEVNAME(zr), __func__, i);
-
- //udelay(20);
- mem = kmalloc(fh->buffers.buffer_size,
- GFP_KERNEL | __GFP_NOWARN);
- if (!mem) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc for V4L buf %d failed\n",
- ZR_DEVNAME(zr), __func__, i);
- v4l_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].v4l.fbuffer = mem;
- fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
- fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
- for (off = 0; off < fh->buffers.buffer_size;
- off += PAGE_SIZE)
- SetPageReserved(virt_to_page(mem + off));
- dprintk(4,
- KERN_INFO
- "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
- ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
- (unsigned long long)virt_to_bus(mem));
- }
-
- fh->buffers.allocated = 1;
-
- return 0;
-}
-
-/* free the V4L grab buffers */
-static void v4l_fbuffer_free(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, off;
- unsigned char *mem;
-
- dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (!fh->buffers.buffer[i].v4l.fbuffer)
- continue;
-
- mem = fh->buffers.buffer[i].v4l.fbuffer;
- for (off = 0; off < fh->buffers.buffer_size;
- off += PAGE_SIZE)
- ClearPageReserved(virt_to_page(mem + off));
- kfree(fh->buffers.buffer[i].v4l.fbuffer);
- fh->buffers.buffer[i].v4l.fbuffer = NULL;
- }
-
- fh->buffers.allocated = 0;
-}
-
-/*
- * Allocate the MJPEG grab buffers.
- *
- * If a Natoma chipset is present and this is a revision 1 zr36057,
- * each MJPEG buffer needs to be physically contiguous.
- * (RJ: This statement is from Dave Perks' original driver,
- * I could never check it because I have a zr36067)
- *
- * RJ: The contents grab buffers needs never be accessed in the driver.
- * Therefore there is no need to allocate them with vmalloc in order
- * to get a contiguous virtual memory space.
- * I don't understand why many other drivers first allocate them with
- * vmalloc (which uses internally also get_zeroed_page, but delivers you
- * virtual addresses) and then again have to make a lot of efforts
- * to get the physical address.
- *
- * Ben Capper:
- * On big-endian architectures (such as ppc) some extra steps
- * are needed. When reading and writing to the stat_com array
- * and fragment buffers, the device expects to see little-
- * endian values. The use of cpu_to_le32() and le32_to_cpu()
- * in this function (and one or two others in zoran_device.c)
- * ensure that these values are always stored in little-endian
- * form, regardless of architecture. The zr36057 does Very Bad
- * Things on big endian architectures if the stat_com array
- * and fragment buffers are not little-endian.
- */
-
-static int jpg_fbuffer_alloc(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, j, off;
- u8 *mem;
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].jpg.frag_tab)
- dprintk(2,
- KERN_WARNING
- "%s: %s - buffer %d already allocated!?\n",
- ZR_DEVNAME(zr), __func__, i);
-
- /* Allocate fragment table for this buffer */
-
- mem = (void *)get_zeroed_page(GFP_KERNEL);
- if (!mem) {
- dprintk(1,
- KERN_ERR
- "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
- fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
-
- if (fh->buffers.need_contiguous) {
- mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
- if (mem == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - kmalloc failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
- fh->buffers.buffer[i].jpg.frag_tab[0] =
- cpu_to_le32(virt_to_bus(mem));
- fh->buffers.buffer[i].jpg.frag_tab[1] =
- cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
- for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
- SetPageReserved(virt_to_page(mem + off));
- } else {
- /* jpg_bufsize is already page aligned */
- for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
- mem = (void *)get_zeroed_page(GFP_KERNEL);
- if (mem == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - get_zeroed_page failed for buffer %d\n",
- ZR_DEVNAME(zr), __func__, i);
- jpg_fbuffer_free(fh);
- return -ENOBUFS;
- }
-
- fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
- cpu_to_le32(virt_to_bus(mem));
- fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
- cpu_to_le32((PAGE_SIZE >> 2) << 1);
- SetPageReserved(virt_to_page(mem));
- }
-
- fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
- }
- }
-
- dprintk(4,
- KERN_DEBUG "%s: %s - %d KB allocated\n",
- ZR_DEVNAME(zr), __func__,
- (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
-
- fh->buffers.allocated = 1;
-
- return 0;
-}
-
-/* free the MJPEG grab buffers */
-static void jpg_fbuffer_free(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
- int i, j, off;
- unsigned char *mem;
- __le32 frag_tab;
- struct zoran_buffer *buffer;
-
- dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
-
- for (i = 0, buffer = &fh->buffers.buffer[0];
- i < fh->buffers.num_buffers; i++, buffer++) {
- if (!buffer->jpg.frag_tab)
- continue;
-
- if (fh->buffers.need_contiguous) {
- frag_tab = buffer->jpg.frag_tab[0];
-
- if (frag_tab) {
- mem = bus_to_virt(le32_to_cpu(frag_tab));
- for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
- ClearPageReserved(virt_to_page(mem + off));
- kfree(mem);
- buffer->jpg.frag_tab[0] = 0;
- buffer->jpg.frag_tab[1] = 0;
- }
- } else {
- for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
- frag_tab = buffer->jpg.frag_tab[2 * j];
-
- if (!frag_tab)
- break;
- ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
- free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
- buffer->jpg.frag_tab[2 * j] = 0;
- buffer->jpg.frag_tab[2 * j + 1] = 0;
- }
- }
-
- free_page((unsigned long)buffer->jpg.frag_tab);
- buffer->jpg.frag_tab = NULL;
- }
-
- fh->buffers.allocated = 0;
-}
-
-/*
- * V4L Buffer grabbing
- */
-
-static int
-zoran_v4l_set_format (struct zoran_fh *fh,
- int width,
- int height,
- const struct zoran_format *format)
-{
- struct zoran *zr = fh->zr;
- int bpp;
-
- /* Check size and format of the grab wanted */
-
- if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH ||
- height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong frame size (%dx%d)\n",
- ZR_DEVNAME(zr), __func__, width, height);
- return -EINVAL;
- }
-
- bpp = (format->depth + 7) / 8;
-
- /* Check against available buffer size */
- if (height * width * bpp > fh->buffers.buffer_size) {
- dprintk(1,
- KERN_ERR
- "%s: %s - video buffer size (%d kB) is too small\n",
- ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
- return -EINVAL;
- }
-
- /* The video front end needs 4-byte alinged line sizes */
-
- if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong frame alignment\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- fh->v4l_settings.width = width;
- fh->v4l_settings.height = height;
- fh->v4l_settings.format = format;
- fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width;
-
- return 0;
-}
-
-static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int res = 0;
-
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- }
-
- /* No grabbing outside the buffer range! */
- if (num >= fh->buffers.num_buffers || num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer %d is out of range\n",
- ZR_DEVNAME(zr), __func__, num);
- res = -EINVAL;
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (fh->buffers.active == ZORAN_FREE) {
- if (zr->v4l_buffers.active == ZORAN_FREE) {
- zr->v4l_buffers = fh->buffers;
- fh->buffers.active = ZORAN_ACTIVE;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - another session is already capturing\n",
- ZR_DEVNAME(zr), __func__);
- res = -EBUSY;
- }
- }
-
- /* make sure a grab isn't going on currently with this buffer */
- if (!res) {
- switch (zr->v4l_buffers.buffer[num].state) {
- default:
- case BUZ_STATE_PEND:
- if (zr->v4l_buffers.active == ZORAN_FREE) {
- fh->buffers.active = ZORAN_FREE;
- zr->v4l_buffers.allocated = 0;
- }
- res = -EBUSY; /* what are you doing? */
- break;
- case BUZ_STATE_DONE:
- dprintk(2,
- KERN_WARNING
- "%s: %s - queueing buffer %d in state DONE!?\n",
- ZR_DEVNAME(zr), __func__, num);
- case BUZ_STATE_USER:
- /* since there is at least one unused buffer there's room for at least
- * one more pend[] entry */
- zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
- zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
- zr->v4l_buffers.buffer[num].bs.length =
- fh->v4l_settings.bytesperline *
- zr->v4l_settings.height;
- fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
- break;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- if (!res && zr->v4l_buffers.active == ZORAN_FREE)
- zr->v4l_buffers.active = fh->buffers.active;
-
- return res;
-}
-
-/*
- * Sync on a V4L buffer
- */
-
-static int v4l_sync(struct zoran_fh *fh, int frame)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
-
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no grab active for this session\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- /* check passed-in frame number */
- if (frame >= fh->buffers.num_buffers || frame < 0) {
- dprintk(1,
- KERN_ERR "%s: %s - frame %d is invalid\n",
- ZR_DEVNAME(zr), __func__, frame);
- return -EINVAL;
- }
-
- /* Check if is buffer was queued at all */
- if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
- dprintk(1,
- KERN_ERR
- "%s: %s - attempt to sync on a buffer which was not queued?\n",
- ZR_DEVNAME(zr), __func__);
- return -EPROTO;
- }
-
- /* wait on this buffer to get ready */
- if (!wait_event_interruptible_timeout(zr->v4l_capq,
- (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ))
- return -ETIME;
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- /* buffer should now be in BUZ_STATE_DONE */
- if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
- dprintk(2,
- KERN_ERR "%s: %s - internal state error\n",
- ZR_DEVNAME(zr), __func__);
-
- zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
- fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- /* Check if streaming capture has finished */
- if (zr->v4l_pend_tail == zr->v4l_pend_head) {
- zr36057_set_memgrab(zr, 0);
- if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
- fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
- zr->v4l_buffers.allocated = 0;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return 0;
-}
-
-/*
- * Queue a MJPEG buffer for capture/playback
- */
-
-static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
- enum zoran_codec_mode mode)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int res = 0;
-
- /* Check if buffers are allocated */
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -ENOMEM;
- }
-
- /* No grabbing outside the buffer range! */
- if (num >= fh->buffers.num_buffers || num < 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer %d out of range\n",
- ZR_DEVNAME(zr), __func__, num);
- return -EINVAL;
- }
-
- /* what is the codec mode right now? */
- if (zr->codec_mode == BUZ_MODE_IDLE) {
- zr->jpg_settings = fh->jpg_settings;
- } else if (zr->codec_mode != mode) {
- /* wrong codec mode active - invalid */
- dprintk(1,
- KERN_ERR
- "%s: %s - codec in wrong mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (fh->buffers.active == ZORAN_FREE) {
- if (zr->jpg_buffers.active == ZORAN_FREE) {
- zr->jpg_buffers = fh->buffers;
- fh->buffers.active = ZORAN_ACTIVE;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - another session is already capturing\n",
- ZR_DEVNAME(zr), __func__);
- res = -EBUSY;
- }
- }
-
- if (!res && zr->codec_mode == BUZ_MODE_IDLE) {
- /* Ok load up the jpeg codec */
- zr36057_enable_jpg(zr, mode);
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (!res) {
- switch (zr->jpg_buffers.buffer[num].state) {
- case BUZ_STATE_DONE:
- dprintk(2,
- KERN_WARNING
- "%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
- ZR_DEVNAME(zr), __func__);
- case BUZ_STATE_USER:
- /* since there is at least one unused buffer there's room for at
- *least one more pend[] entry */
- zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
- zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
- fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
- zoran_feed_stat_com(zr);
- break;
- default:
- case BUZ_STATE_DMA:
- case BUZ_STATE_PEND:
- if (zr->jpg_buffers.active == ZORAN_FREE) {
- fh->buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- }
- res = -EBUSY; /* what are you doing? */
- break;
- }
- }
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- if (!res && zr->jpg_buffers.active == ZORAN_FREE)
- zr->jpg_buffers.active = fh->buffers.active;
-
- return res;
-}
-
-static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
-{
- struct zoran *zr = fh->zr;
- int res = 0;
-
- /* Does the user want to stop streaming? */
- if (frame < 0) {
- if (zr->codec_mode == mode) {
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s(-1) - session not active\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
- zr->jpg_buffers.allocated = 0;
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- return 0;
- } else {
- dprintk(1,
- KERN_ERR
- "%s: %s - stop streaming but not in streaming mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- }
-
- if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
- return res;
-
- /* Start the jpeg codec when the first frame is queued */
- if (!res && zr->jpg_que_head == 1)
- jpeg_start(zr);
-
- return res;
-}
-
-/*
- * Sync on a MJPEG buffer
- */
-
-static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
- int frame;
-
- if (fh->buffers.active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - capture is not currently active\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
- zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
- dprintk(1,
- KERN_ERR
- "%s: %s - codec not in streaming mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (!wait_event_interruptible_timeout(zr->jpg_capq,
- (zr->jpg_que_tail != zr->jpg_dma_tail ||
- zr->jpg_dma_tail == zr->jpg_dma_head),
- 10*HZ)) {
- int isr;
-
- btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
- udelay(1);
- zr->codec->control(zr->codec, CODEC_G_STATUS,
- sizeof(isr), &isr);
- dprintk(1,
- KERN_ERR
- "%s: %s - timeout: codec isr=0x%02x\n",
- ZR_DEVNAME(zr), __func__, isr);
-
- return -ETIME;
-
- }
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- spin_lock_irqsave(&zr->spinlock, flags);
-
- if (zr->jpg_dma_tail != zr->jpg_dma_head)
- frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
- else
- frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- /* buffer should now be in BUZ_STATE_DONE */
- if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
- dprintk(2,
- KERN_ERR "%s: %s - internal state error\n",
- ZR_DEVNAME(zr), __func__);
-
- *bs = zr->jpg_buffers.buffer[frame].bs;
- bs->frame = frame;
- zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
- fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- return 0;
-}
-
-static void zoran_open_init_session(struct zoran_fh *fh)
-{
- int i;
- struct zoran *zr = fh->zr;
-
- /* Per default, map the V4L Buffers */
- map_mode_raw(fh);
-
- /* take over the card's current settings */
- fh->overlay_settings = zr->overlay_settings;
- fh->overlay_settings.is_set = 0;
- fh->overlay_settings.format = zr->overlay_settings.format;
- fh->overlay_active = ZORAN_FREE;
-
- /* v4l settings */
- fh->v4l_settings = zr->v4l_settings;
- /* jpg settings */
- fh->jpg_settings = zr->jpg_settings;
-
- /* buffers */
- memset(&fh->buffers, 0, sizeof(fh->buffers));
- for (i = 0; i < MAX_FRAME; i++) {
- fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
- fh->buffers.buffer[i].bs.frame = i;
- }
- fh->buffers.allocated = 0;
- fh->buffers.active = ZORAN_FREE;
-}
-
-static void zoran_close_end_session(struct zoran_fh *fh)
-{
- struct zoran *zr = fh->zr;
-
- /* overlay */
- if (fh->overlay_active != ZORAN_FREE) {
- fh->overlay_active = zr->overlay_active = ZORAN_FREE;
- zr->v4l_overlay_active = 0;
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
- }
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- /* v4l capture */
- if (fh->buffers.active != ZORAN_FREE) {
- unsigned long flags;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
- spin_unlock_irqrestore(&zr->spinlock, flags);
- }
-
- /* v4l buffers */
- if (fh->buffers.allocated)
- v4l_fbuffer_free(fh);
- } else {
- /* jpg capture */
- if (fh->buffers.active != ZORAN_FREE) {
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
- }
-
- /* jpg buffers */
- if (fh->buffers.allocated)
- jpg_fbuffer_free(fh);
- }
-}
-
-/*
- * Open a zoran card. Right now the flags stuff is just playing
- */
-
-static int zoran_open(struct file *file)
-{
- struct zoran *zr = video_drvdata(file);
- struct zoran_fh *fh;
- int res, first_open = 0;
-
- dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
- ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
-
- mutex_lock(&zr->other_lock);
-
- if (zr->user >= 2048) {
- dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
- ZR_DEVNAME(zr), zr->user);
- res = -EBUSY;
- goto fail_unlock;
- }
-
- /* now, create the open()-specific file_ops struct */
- fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
- if (!fh) {
- dprintk(1,
- KERN_ERR
- "%s: %s - allocation of zoran_fh failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- goto fail_unlock;
- }
- /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
- * on norm-change! */
- fh->overlay_mask =
- kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
- if (!fh->overlay_mask) {
- dprintk(1,
- KERN_ERR
- "%s: %s - allocation of overlay_mask failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -ENOMEM;
- goto fail_fh;
- }
-
- if (zr->user++ == 0)
- first_open = 1;
-
- /*mutex_unlock(&zr->resource_lock);*/
-
- /* default setup - TODO: look at flags */
- if (first_open) { /* First device open */
- zr36057_restart(zr);
- zoran_open_init_params(zr);
- zoran_init_hardware(zr);
-
- btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
- }
-
- /* set file_ops stuff */
- file->private_data = fh;
- fh->zr = zr;
- zoran_open_init_session(fh);
- mutex_unlock(&zr->other_lock);
-
- return 0;
-
-fail_fh:
- kfree(fh);
-fail_unlock:
- mutex_unlock(&zr->other_lock);
-
- dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n",
- ZR_DEVNAME(zr), res, zr->user);
-
- return res;
-}
-
-static int
-zoran_close(struct file *file)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
-
- dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
- ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
-
- /* kernel locks (fs/device.c), so don't do that ourselves
- * (prevents deadlocks) */
- mutex_lock(&zr->other_lock);
-
- zoran_close_end_session(fh);
-
- if (zr->user-- == 1) { /* Last process */
- /* Clean up JPEG process */
- wake_up_interruptible(&zr->jpg_capq);
- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = ZORAN_FREE;
-
- /* disable interrupts */
- btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
-
- if (zr36067_debug > 1)
- print_interrupts(zr);
-
- /* Overlay off */
- zr->v4l_overlay_active = 0;
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
-
- /* capture off */
- wake_up_interruptible(&zr->v4l_capq);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = ZORAN_FREE;
- zoran_set_pci_master(zr, 0);
-
- if (!pass_through) { /* Switch to color bar */
- decoder_call(zr, video, s_stream, 0);
- encoder_call(zr, video, s_routing, 2, 0, 0);
- }
- }
- mutex_unlock(&zr->other_lock);
-
- file->private_data = NULL;
- kfree(fh->overlay_mask);
- kfree(fh);
-
- dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
-
- return 0;
-}
-
-
-static ssize_t
-zoran_read (struct file *file,
- char __user *data,
- size_t count,
- loff_t *ppos)
-{
- /* we simply don't support read() (yet)... */
-
- return -EINVAL;
-}
-
-static ssize_t
-zoran_write (struct file *file,
- const char __user *data,
- size_t count,
- loff_t *ppos)
-{
- /* ...and the same goes for write() */
-
- return -EINVAL;
-}
-
-static int setup_fbuffer(struct zoran_fh *fh,
- void *base,
- const struct zoran_format *fmt,
- int width,
- int height,
- int bytesperline)
-{
- struct zoran *zr = fh->zr;
-
- /* (Ronald) v4l/v4l2 guidelines */
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
- ALi Magik (that needs very low latency while the card needs a
- higher value always) */
-
- if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
- return -ENXIO;
-
- /* we need a bytesperline value, even if not given */
- if (!bytesperline)
- bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
-
-#if 0
- if (zr->overlay_active) {
- /* dzjee... stupid users... don't even bother to turn off
- * overlay before changing the memory location...
- * normally, we would return errors here. However, one of
- * the tools that does this is... xawtv! and since xawtv
- * is used by +/- 99% of the users, we'd rather be user-
- * friendly and silently do as if nothing went wrong */
- dprintk(3,
- KERN_ERR
- "%s: %s - forced overlay turnoff because framebuffer changed\n",
- ZR_DEVNAME(zr), __func__);
- zr36057_overlay(zr, 0);
- }
-#endif
-
- if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no valid overlay format given\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (height <= 0 || width <= 0 || bytesperline <= 0) {
- dprintk(1,
- KERN_ERR
- "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
- ZR_DEVNAME(zr), __func__, width, height, bytesperline);
- return -EINVAL;
- }
- if (bytesperline & 3) {
- dprintk(1,
- KERN_ERR
- "%s: %s - bytesperline (%d) must be 4-byte aligned\n",
- ZR_DEVNAME(zr), __func__, bytesperline);
- return -EINVAL;
- }
-
- zr->vbuf_base = (void *) ((unsigned long) base & ~3);
- zr->vbuf_height = height;
- zr->vbuf_width = width;
- zr->vbuf_depth = fmt->depth;
- zr->overlay_settings.format = fmt;
- zr->vbuf_bytesperline = bytesperline;
-
- /* The user should set new window parameters */
- zr->overlay_settings.is_set = 0;
-
- return 0;
-}
-
-
-static int setup_window(struct zoran_fh *fh,
- int x,
- int y,
- int width,
- int height,
- struct v4l2_clip __user *clips,
- unsigned int clipcount,
- void __user *bitmap)
-{
- struct zoran *zr = fh->zr;
- struct v4l2_clip *vcp = NULL;
- int on, end;
-
-
- if (!zr->vbuf_base) {
- dprintk(1,
- KERN_ERR
- "%s: %s - frame buffer has to be set first\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (!fh->overlay_settings.format) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no overlay format set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- if (clipcount > 2048) {
- dprintk(1,
- KERN_ERR
- "%s: %s - invalid clipcount\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- /*
- * The video front end needs 4-byte alinged line sizes, we correct that
- * silently here if necessary
- */
- if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
- end = (x + width) & ~1; /* round down */
- x = (x + 1) & ~1; /* round up */
- width = end - x;
- }
-
- if (zr->vbuf_depth == 24) {
- end = (x + width) & ~3; /* round down */
- x = (x + 3) & ~3; /* round up */
- width = end - x;
- }
-
- if (width > BUZ_MAX_WIDTH)
- width = BUZ_MAX_WIDTH;
- if (height > BUZ_MAX_HEIGHT)
- height = BUZ_MAX_HEIGHT;
-
- /* Check for invalid parameters */
- if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT ||
- width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
- dprintk(1,
- KERN_ERR
- "%s: %s - width = %d or height = %d invalid\n",
- ZR_DEVNAME(zr), __func__, width, height);
- return -EINVAL;
- }
-
- fh->overlay_settings.x = x;
- fh->overlay_settings.y = y;
- fh->overlay_settings.width = width;
- fh->overlay_settings.height = height;
- fh->overlay_settings.clipcount = clipcount;
-
- /*
- * If an overlay is running, we have to switch it off
- * and switch it on again in order to get the new settings in effect.
- *
- * We also want to avoid that the overlay mask is written
- * when an overlay is running.
- */
-
- on = zr->v4l_overlay_active && !zr->v4l_memgrab_active &&
- zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active != ZORAN_FREE;
- if (on)
- zr36057_overlay(zr, 0);
-
- /*
- * Write the overlay mask if clips are wanted.
- * We prefer a bitmap.
- */
- if (bitmap) {
- /* fake value - it just means we want clips */
- fh->overlay_settings.clipcount = 1;
-
- if (copy_from_user(fh->overlay_mask, bitmap,
- (width * height + 7) / 8)) {
- return -EFAULT;
- }
- } else if (clipcount) {
- /* write our own bitmap from the clips */
- vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
- if (vcp == NULL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - Alloc of clip mask failed\n",
- ZR_DEVNAME(zr), __func__);
- return -ENOMEM;
- }
- if (copy_from_user
- (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
- vfree(vcp);
- return -EFAULT;
- }
- write_overlay_mask(fh, vcp, clipcount);
- vfree(vcp);
- }
-
- fh->overlay_settings.is_set = 1;
- if (fh->overlay_active != ZORAN_FREE &&
- zr->overlay_active != ZORAN_FREE)
- zr->overlay_settings = fh->overlay_settings;
-
- if (on)
- zr36057_overlay(zr, 1);
-
- /* Make sure the changes come into effect */
- return wait_grab_pending(zr);
-}
-
-static int setup_overlay(struct zoran_fh *fh, int on)
-{
- struct zoran *zr = fh->zr;
-
- /* If there is nothing to do, return immediately */
- if ((on && fh->overlay_active != ZORAN_FREE) ||
- (!on && fh->overlay_active == ZORAN_FREE))
- return 0;
-
- /* check whether we're touching someone else's overlay */
- if (on && zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - overlay is already active for another session\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
- if (!on && zr->overlay_active != ZORAN_FREE &&
- fh->overlay_active == ZORAN_FREE) {
- dprintk(1,
- KERN_ERR
- "%s: %s - you cannot cancel someone else's session\n",
- ZR_DEVNAME(zr), __func__);
- return -EPERM;
- }
-
- if (on == 0) {
- zr->overlay_active = fh->overlay_active = ZORAN_FREE;
- zr->v4l_overlay_active = 0;
- /* When a grab is running, the video simply
- * won't be switched on any more */
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 0);
- zr->overlay_mask = NULL;
- } else {
- if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
- dprintk(1,
- KERN_ERR
- "%s: %s - buffer or window not set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- if (!fh->overlay_settings.format) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no overlay format set\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
- zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
- zr->v4l_overlay_active = 1;
- zr->overlay_mask = fh->overlay_mask;
- zr->overlay_settings = fh->overlay_settings;
- if (!zr->v4l_memgrab_active)
- zr36057_overlay(zr, 1);
- /* When a grab is running, the video will be
- * switched on when grab is finished */
- }
-
- /* Make sure the changes come into effect */
- return wait_grab_pending(zr);
-}
-
-/* get the status of a buffer in the clients buffer queue */
-static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
- struct v4l2_buffer *buf, int num)
-{
- struct zoran *zr = fh->zr;
- unsigned long flags;
-
- buf->flags = V4L2_BUF_FLAG_MAPPED;
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- /* check range */
- if (num < 0 || num >= fh->buffers.num_buffers ||
- !fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong number or buffers not allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], num,
- "UPMD"[zr->v4l_buffers.buffer[num].state],
- fh->buffers.buffer[num].map ? 'Y' : 'N');
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->length = fh->buffers.buffer_size;
-
- /* get buffer */
- buf->bytesused = fh->buffers.buffer[num].bs.length;
- if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
- fh->buffers.buffer[num].state == BUZ_STATE_USER) {
- buf->sequence = fh->buffers.buffer[num].bs.seq;
- buf->flags |= V4L2_BUF_FLAG_DONE;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
- } else {
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- }
-
- if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2)
- buf->field = V4L2_FIELD_TOP;
- else
- buf->field = V4L2_FIELD_INTERLACED;
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
-
- /* check range */
- if (num < 0 || num >= fh->buffers.num_buffers ||
- !fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s - wrong number or buffers not allocated\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
- V4L2_BUF_TYPE_VIDEO_CAPTURE :
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- buf->length = fh->buffers.buffer_size;
-
- /* these variables are only written after frame has been captured */
- if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
- fh->buffers.buffer[num].state == BUZ_STATE_USER) {
- buf->sequence = fh->buffers.buffer[num].bs.seq;
- buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
- buf->bytesused = fh->buffers.buffer[num].bs.length;
- buf->flags |= V4L2_BUF_FLAG_DONE;
- } else {
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- }
-
- /* which fields are these? */
- if (fh->jpg_settings.TmpDcm != 1)
- buf->field = fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
- else
- buf->field = fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
-
- break;
-
- default:
-
- dprintk(5,
- KERN_ERR
- "%s: %s - invalid buffer type|map_mode (%d|%d)\n",
- ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
- return -EINVAL;
- }
-
- buf->memory = V4L2_MEMORY_MMAP;
- buf->index = num;
- buf->m.offset = buf->length * num;
-
- return 0;
-}
-
-static int
-zoran_set_norm (struct zoran *zr,
- v4l2_std_id norm)
-{
- int on;
-
- if (zr->v4l_buffers.active != ZORAN_FREE ||
- zr->jpg_buffers.active != ZORAN_FREE) {
- dprintk(1,
- KERN_WARNING
- "%s: %s called while in playback/capture mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
-
- if (!(norm & zr->card.norms)) {
- dprintk(1,
- KERN_ERR "%s: %s - unsupported norm %llx\n",
- ZR_DEVNAME(zr), __func__, norm);
- return -EINVAL;
- }
-
- if (norm == V4L2_STD_ALL) {
- unsigned int status = 0;
- v4l2_std_id std = 0;
-
- decoder_call(zr, video, querystd, &std);
- decoder_call(zr, core, s_std, std);
-
- /* let changes come into effect */
- ssleep(2);
-
- decoder_call(zr, video, g_input_status, &status);
- if (status & V4L2_IN_ST_NO_SIGNAL) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no norm detected\n",
- ZR_DEVNAME(zr), __func__);
- /* reset norm */
- decoder_call(zr, core, s_std, zr->norm);
- return -EIO;
- }
-
- norm = std;
- }
- if (norm & V4L2_STD_SECAM)
- zr->timing = zr->card.tvn[2];
- else if (norm & V4L2_STD_NTSC)
- zr->timing = zr->card.tvn[1];
- else
- zr->timing = zr->card.tvn[0];
-
- /* We switch overlay off and on since a change in the
- * norm needs different VFE settings */
- on = zr->overlay_active && !zr->v4l_memgrab_active;
- if (on)
- zr36057_overlay(zr, 0);
-
- decoder_call(zr, core, s_std, norm);
- encoder_call(zr, video, s_std_output, norm);
-
- if (on)
- zr36057_overlay(zr, 1);
-
- /* Make sure the changes come into effect */
- zr->norm = norm;
-
- return 0;
-}
-
-static int
-zoran_set_input (struct zoran *zr,
- int input)
-{
- if (input == zr->input) {
- return 0;
- }
-
- if (zr->v4l_buffers.active != ZORAN_FREE ||
- zr->jpg_buffers.active != ZORAN_FREE) {
- dprintk(1,
- KERN_WARNING
- "%s: %s called while in playback/capture mode\n",
- ZR_DEVNAME(zr), __func__);
- return -EBUSY;
- }
-
- if (input < 0 || input >= zr->card.inputs) {
- dprintk(1,
- KERN_ERR
- "%s: %s - unnsupported input %d\n",
- ZR_DEVNAME(zr), __func__, input);
- return -EINVAL;
- }
-
- zr->input = input;
-
- decoder_call(zr, video, s_routing,
- zr->card.input[input].muxsel, 0, 0);
-
- return 0;
-}
-
-/*
- * ioctl routine
- */
-
-static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- memset(cap, 0, sizeof(*cap));
- strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
- strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
- pci_name(zr->pci_dev));
- cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
- return 0;
-}
-
-static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
-{
- unsigned int num, i;
-
- for (num = i = 0; i < NUM_FORMATS; i++) {
- if (zoran_formats[i].flags & flag && num++ == fmt->index) {
- strncpy(fmt->description, zoran_formats[i].name,
- sizeof(fmt->description) - 1);
- /* fmt struct pre-zeroed, so adding '\0' not needed */
- fmt->pixelformat = zoran_formats[i].fourcc;
- if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
- fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
-}
-
-static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
-}
-
-static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_fmtdesc *f)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
-}
-
-static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- mutex_lock(&zr->resource_lock);
-
- fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
- fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
- (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
- fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
- fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
- if (fh->jpg_settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (fh->map_mode != ZORAN_MAP_MODE_RAW)
- return zoran_g_fmt_vid_out(file, fh, fmt);
-
- mutex_lock(&zr->resource_lock);
- fmt->fmt.pix.width = fh->v4l_settings.width;
- fmt->fmt.pix.height = fh->v4l_settings.height;
- fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
- fh->v4l_settings.height;
- fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
- fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
- fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
- if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
- fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.pix.field = V4L2_FIELD_TOP;
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- mutex_lock(&zr->resource_lock);
-
- fmt->fmt.win.w.left = fh->overlay_settings.x;
- fmt->fmt.win.w.top = fh->overlay_settings.y;
- fmt->fmt.win.w.width = fh->overlay_settings.width;
- fmt->fmt.win.w.height = fh->overlay_settings.height;
- if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
- fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.win.field = V4L2_FIELD_TOP;
-
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- mutex_lock(&zr->resource_lock);
-
- if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
- fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
- if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
- fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
- if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
- fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
- if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
- fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
-
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- struct zoran_jpg_settings settings;
- int res = 0;
-
- if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
-
- mutex_lock(&zr->resource_lock);
- settings = fh->jpg_settings;
-
- /* we actually need to set 'real' parameters now */
- if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
- settings.TmpDcm = 1;
- else
- settings.TmpDcm = 2;
- settings.decimation = 0;
- if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
- settings.VerDcm = 2;
- else
- settings.VerDcm = 1;
- if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
- settings.HorDcm = 4;
- else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
- settings.HorDcm = 2;
- else
- settings.HorDcm = 1;
- if (settings.TmpDcm == 1)
- settings.field_per_buff = 2;
- else
- settings.field_per_buff = 1;
-
- if (settings.HorDcm > 1) {
- settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- } else {
- settings.img_x = 0;
- settings.img_width = BUZ_MAX_WIDTH;
- }
-
- /* check */
- res = zoran_check_jpg_settings(zr, &settings, 1);
- if (res)
- goto tryfmt_unlock_and_return;
-
- /* tell the user what we actually did */
- fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
- fmt->fmt.pix.height = settings.img_height * 2 /
- (settings.TmpDcm * settings.VerDcm);
- if (settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
-
- fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-tryfmt_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int bpp;
- int i;
-
- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
- return zoran_try_fmt_vid_out(file, fh, fmt);
-
- mutex_lock(&zr->resource_lock);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
- break;
-
- if (i == NUM_FORMATS) {
- mutex_unlock(&zr->resource_lock);
- return -EINVAL;
- }
-
- bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8);
- v4l_bound_align_image(
- &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2,
- &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0);
- mutex_unlock(&zr->resource_lock);
-
- return 0;
-}
-
-static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res;
-
- dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
- fmt->fmt.win.w.left, fmt->fmt.win.w.top,
- fmt->fmt.win.w.width,
- fmt->fmt.win.w.height,
- fmt->fmt.win.clipcount,
- fmt->fmt.win.bitmap);
- mutex_lock(&zr->resource_lock);
- res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
- fmt->fmt.win.w.width, fmt->fmt.win.w.height,
- (struct v4l2_clip __user *)fmt->fmt.win.clips,
- fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
- struct zoran_jpg_settings settings;
- int res = 0;
-
- dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
- fmt->fmt.pix.width, fmt->fmt.pix.height,
- fmt->fmt.pix.pixelformat,
- (char *) &printformat);
- if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
- return -EINVAL;
-
- mutex_lock(&zr->resource_lock);
-
- if (fh->buffers.allocated) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- goto sfmtjpg_unlock_and_return;
- }
-
- settings = fh->jpg_settings;
-
- /* we actually need to set 'real' parameters now */
- if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
- settings.TmpDcm = 1;
- else
- settings.TmpDcm = 2;
- settings.decimation = 0;
- if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
- settings.VerDcm = 2;
- else
- settings.VerDcm = 1;
- if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
- settings.HorDcm = 4;
- else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
- settings.HorDcm = 2;
- else
- settings.HorDcm = 1;
- if (settings.TmpDcm == 1)
- settings.field_per_buff = 2;
- else
- settings.field_per_buff = 1;
-
- if (settings.HorDcm > 1) {
- settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
- settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
- } else {
- settings.img_x = 0;
- settings.img_width = BUZ_MAX_WIDTH;
- }
-
- /* check */
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- goto sfmtjpg_unlock_and_return;
-
- /* it's ok, so set them */
- fh->jpg_settings = settings;
-
- map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
- fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
-
- /* tell the user what we actually did */
- fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
- fmt->fmt.pix.height = settings.img_height * 2 /
- (settings.TmpDcm * settings.VerDcm);
- if (settings.TmpDcm == 1)
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
- else
- fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
- V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
- fmt->fmt.pix.bytesperline = 0;
- fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
- fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
-
-sfmtjpg_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
- struct v4l2_format *fmt)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i;
- int res = 0;
-
- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
- return zoran_s_fmt_vid_out(file, fh, fmt);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
- break;
- if (i == NUM_FORMATS) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
- ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
- return -EINVAL;
- }
-
- mutex_lock(&zr->resource_lock);
-
- if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
- fh->buffers.active != ZORAN_FREE) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- goto sfmtv4l_unlock_and_return;
- }
- if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
- fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
- if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
- fmt->fmt.pix.width = BUZ_MAX_WIDTH;
-
- map_mode_raw(fh);
-
- res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
- &zoran_formats[i]);
- if (res)
- goto sfmtv4l_unlock_and_return;
-
- /* tell the user the results/missing stuff */
- fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
- fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
- fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
- if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
- fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
- else
- fmt->fmt.pix.field = V4L2_FIELD_TOP;
-
-sfmtv4l_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_g_fbuf(struct file *file, void *__fh,
- struct v4l2_framebuffer *fb)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- memset(fb, 0, sizeof(*fb));
- mutex_lock(&zr->resource_lock);
- fb->base = zr->vbuf_base;
- fb->fmt.width = zr->vbuf_width;
- fb->fmt.height = zr->vbuf_height;
- if (zr->overlay_settings.format)
- fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
- fb->fmt.bytesperline = zr->vbuf_bytesperline;
- mutex_unlock(&zr->resource_lock);
- fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
- fb->fmt.field = V4L2_FIELD_INTERLACED;
- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
-
- return 0;
-}
-
-static int zoran_s_fbuf(struct file *file, void *__fh,
- struct v4l2_framebuffer *fb)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i, res = 0;
- __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
-
- for (i = 0; i < NUM_FORMATS; i++)
- if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
- break;
- if (i == NUM_FORMATS) {
- dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
- ZR_DEVNAME(zr), fb->fmt.pixelformat,
- (char *)&printformat);
- return -EINVAL;
- }
-
- mutex_lock(&zr->resource_lock);
- res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
- fb->fmt.height, fb->fmt.bytesperline);
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res;
-
- mutex_lock(&zr->resource_lock);
- res = setup_overlay(fh, on);
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
-
-static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- if (req->memory != V4L2_MEMORY_MMAP) {
- dprintk(2,
- KERN_ERR
- "%s: only MEMORY_MMAP capture is supported, not %d\n",
- ZR_DEVNAME(zr), req->memory);
- return -EINVAL;
- }
-
- if (req->count == 0)
- return zoran_streamoff(file, fh, req->type);
-
- mutex_lock(&zr->resource_lock);
- if (fh->buffers.allocated) {
- dprintk(2,
- KERN_ERR
- "%s: VIDIOC_REQBUFS - buffers already allocated\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- goto v4l2reqbuf_unlock_and_return;
- }
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
- req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- /* control user input */
- if (req->count < 2)
- req->count = 2;
- if (req->count > v4l_nbufs)
- req->count = v4l_nbufs;
-
- /* The next mmap will map the V4L buffers */
- map_mode_raw(fh);
- fh->buffers.num_buffers = req->count;
-
- if (v4l_fbuffer_alloc(fh)) {
- res = -ENOMEM;
- goto v4l2reqbuf_unlock_and_return;
- }
- } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
- fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
- /* we need to calculate size ourselves now */
- if (req->count < 4)
- req->count = 4;
- if (req->count > jpg_nbufs)
- req->count = jpg_nbufs;
-
- /* The next mmap will map the MJPEG buffers */
- map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
- fh->buffers.num_buffers = req->count;
- fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
-
- if (jpg_fbuffer_alloc(fh)) {
- res = -ENOMEM;
- goto v4l2reqbuf_unlock_and_return;
- }
- } else {
- dprintk(1,
- KERN_ERR
- "%s: VIDIOC_REQBUFS - unknown type %d\n",
- ZR_DEVNAME(zr), req->type);
- res = -EINVAL;
- goto v4l2reqbuf_unlock_and_return;
- }
-v4l2reqbuf_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res;
-
- mutex_lock(&zr->resource_lock);
- res = zoran_v4l2_buffer_status(fh, buf, buf->index);
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0, codec_mode, buf_type;
-
- mutex_lock(&zr->resource_lock);
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- goto qbuf_unlock_and_return;
- }
-
- res = zoran_v4l_queue_frame(fh, buf->index);
- if (res)
- goto qbuf_unlock_and_return;
- if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
- zr36057_set_memgrab(zr, 1);
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
- } else {
- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- codec_mode = BUZ_MODE_MOTION_COMPRESS;
- }
-
- if (buf->type != buf_type) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- goto qbuf_unlock_and_return;
- }
-
- res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
- if (res != 0)
- goto qbuf_unlock_and_return;
- if (zr->codec_mode == BUZ_MODE_IDLE &&
- fh->buffers.active == ZORAN_LOCKED)
- zr36057_enable_jpg(zr, codec_mode);
-
- break;
-
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - unsupported type %d\n",
- ZR_DEVNAME(zr), buf->type);
- res = -EINVAL;
- break;
- }
-qbuf_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0, buf_type, num = -1; /* compiler borks here (?) */
-
- mutex_lock(&zr->resource_lock);
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- goto dqbuf_unlock_and_return;
- }
-
- num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
- if (file->f_flags & O_NONBLOCK &&
- zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
- res = -EAGAIN;
- goto dqbuf_unlock_and_return;
- }
- res = v4l_sync(fh, num);
- if (res)
- goto dqbuf_unlock_and_return;
- zr->v4l_sync_tail++;
- res = zoran_v4l2_buffer_status(fh, buf, num);
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- {
- struct zoran_sync bs;
-
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- else
- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- if (buf->type != buf_type) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
- ZR_DEVNAME(zr), buf->type, fh->map_mode);
- res = -EINVAL;
- goto dqbuf_unlock_and_return;
- }
-
- num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- if (file->f_flags & O_NONBLOCK &&
- zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
- res = -EAGAIN;
- goto dqbuf_unlock_and_return;
- }
- bs.frame = 0; /* suppress compiler warning */
- res = jpg_sync(fh, &bs);
- if (res)
- goto dqbuf_unlock_and_return;
- res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
- break;
- }
-
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_DQBUF - unsupported type %d\n",
- ZR_DEVNAME(zr), buf->type);
- res = -EINVAL;
- break;
- }
-dqbuf_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- mutex_lock(&zr->resource_lock);
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW: /* raw capture */
- if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
- fh->buffers.active != ZORAN_ACTIVE) {
- res = -EBUSY;
- goto strmon_unlock_and_return;
- }
-
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
- zr->v4l_settings = fh->v4l_settings;
-
- zr->v4l_sync_tail = zr->v4l_pend_tail;
- if (!zr->v4l_memgrab_active &&
- zr->v4l_pend_head != zr->v4l_pend_tail) {
- zr36057_set_memgrab(zr, 1);
- }
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- /* what is the codec mode right now? */
- if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
- fh->buffers.active != ZORAN_ACTIVE) {
- res = -EBUSY;
- goto strmon_unlock_and_return;
- }
-
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
-
- if (zr->jpg_que_head != zr->jpg_que_tail) {
- /* Start the jpeg codec when the first frame is queued */
- jpeg_start(zr);
- }
- break;
-
- default:
- dprintk(1,
- KERN_ERR
- "%s: VIDIOC_STREAMON - invalid map mode %d\n",
- ZR_DEVNAME(zr), fh->map_mode);
- res = -EINVAL;
- break;
- }
-strmon_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int i, res = 0;
- unsigned long flags;
-
- mutex_lock(&zr->resource_lock);
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW: /* raw capture */
- if (fh->buffers.active == ZORAN_FREE &&
- zr->v4l_buffers.active != ZORAN_FREE) {
- res = -EPERM; /* stay off other's settings! */
- goto strmoff_unlock_and_return;
- }
- if (zr->v4l_buffers.active == ZORAN_FREE)
- goto strmoff_unlock_and_return;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- /* unload capture */
- if (zr->v4l_memgrab_active) {
-
- zr36057_set_memgrab(zr, 0);
- }
-
- for (i = 0; i < fh->buffers.num_buffers; i++)
- zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
- fh->buffers = zr->v4l_buffers;
-
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
-
- zr->v4l_grab_seq = 0;
- zr->v4l_pend_head = zr->v4l_pend_tail = 0;
- zr->v4l_sync_tail = 0;
-
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- if (fh->buffers.active == ZORAN_FREE &&
- zr->jpg_buffers.active != ZORAN_FREE) {
- res = -EPERM; /* stay off other's settings! */
- goto strmoff_unlock_and_return;
- }
- if (zr->jpg_buffers.active == ZORAN_FREE)
- goto strmoff_unlock_and_return;
-
- res = jpg_qbuf(fh, -1,
- (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
- BUZ_MODE_MOTION_COMPRESS :
- BUZ_MODE_MOTION_DECOMPRESS);
- if (res)
- goto strmoff_unlock_and_return;
- break;
- default:
- dprintk(1, KERN_ERR
- "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
- ZR_DEVNAME(zr), fh->map_mode);
- res = -EINVAL;
- break;
- }
-strmoff_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_queryctrl(struct file *file, void *__fh,
- struct v4l2_queryctrl *ctrl)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- /* we only support hue/saturation/contrast/brightness */
- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
- ctrl->id > V4L2_CID_HUE)
- return -EINVAL;
-
- decoder_call(zr, core, queryctrl, ctrl);
-
- return 0;
-}
-
-static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- /* we only support hue/saturation/contrast/brightness */
- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
- ctrl->id > V4L2_CID_HUE)
- return -EINVAL;
-
- mutex_lock(&zr->resource_lock);
- decoder_call(zr, core, g_ctrl, ctrl);
- mutex_unlock(&zr->resource_lock);
-
- return 0;
-}
-
-static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- /* we only support hue/saturation/contrast/brightness */
- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
- ctrl->id > V4L2_CID_HUE)
- return -EINVAL;
-
- mutex_lock(&zr->resource_lock);
- decoder_call(zr, core, s_ctrl, ctrl);
- mutex_unlock(&zr->resource_lock);
-
- return 0;
-}
-
-static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- mutex_lock(&zr->resource_lock);
- *std = zr->norm;
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
-
- mutex_lock(&zr->resource_lock);
- res = zoran_set_norm(zr, *std);
- if (res)
- goto sstd_unlock_and_return;
-
- res = wait_grab_pending(zr);
-sstd_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_enum_input(struct file *file, void *__fh,
- struct v4l2_input *inp)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- if (inp->index >= zr->card.inputs)
- return -EINVAL;
-
- strncpy(inp->name, zr->card.input[inp->index].name,
- sizeof(inp->name) - 1);
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = V4L2_STD_ALL;
-
- /* Get status of video decoder */
- mutex_lock(&zr->resource_lock);
- decoder_call(zr, video, g_input_status, &inp->status);
- mutex_unlock(&zr->resource_lock);
- return 0;
-}
-
-static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
-
- mutex_lock(&zr->resource_lock);
- *input = zr->input;
- mutex_unlock(&zr->resource_lock);
-
- return 0;
-}
-
-static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res;
-
- mutex_lock(&zr->resource_lock);
- res = zoran_set_input(zr, input);
- if (res)
- goto sinput_unlock_and_return;
-
- /* Make sure the changes come into effect */
- res = wait_grab_pending(zr);
-sinput_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_enum_output(struct file *file, void *__fh,
- struct v4l2_output *outp)
-{
- if (outp->index != 0)
- return -EINVAL;
-
- outp->index = 0;
- outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
- strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
-
- return 0;
-}
-
-static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
-{
- *output = 0;
-
- return 0;
-}
-
-static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
-{
- if (output != 0)
- return -EINVAL;
-
- return 0;
-}
-
-/* cropping (sub-frame capture) */
-static int zoran_cropcap(struct file *file, void *__fh,
- struct v4l2_cropcap *cropcap)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int type = cropcap->type, res = 0;
-
- memset(cropcap, 0, sizeof(*cropcap));
- cropcap->type = type;
-
- mutex_lock(&zr->resource_lock);
-
- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
- ZR_DEVNAME(zr));
- res = -EINVAL;
- goto cropcap_unlock_and_return;
- }
-
- cropcap->bounds.top = cropcap->bounds.left = 0;
- cropcap->bounds.width = BUZ_MAX_WIDTH;
- cropcap->bounds.height = BUZ_MAX_HEIGHT;
- cropcap->defrect.top = cropcap->defrect.left = 0;
- cropcap->defrect.width = BUZ_MIN_WIDTH;
- cropcap->defrect.height = BUZ_MIN_HEIGHT;
-cropcap_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int type = crop->type, res = 0;
-
- memset(crop, 0, sizeof(*crop));
- crop->type = type;
-
- mutex_lock(&zr->resource_lock);
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
- dprintk(1,
- KERN_ERR
- "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
- ZR_DEVNAME(zr));
- res = -EINVAL;
- goto gcrop_unlock_and_return;
- }
-
- crop->c.top = fh->jpg_settings.img_y;
- crop->c.left = fh->jpg_settings.img_x;
- crop->c.width = fh->jpg_settings.img_width;
- crop->c.height = fh->jpg_settings.img_height;
-
-gcrop_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
- struct zoran_jpg_settings settings;
-
- settings = fh->jpg_settings;
-
- mutex_lock(&zr->resource_lock);
-
- if (fh->buffers.allocated) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_S_CROP - cannot change settings while active\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- goto scrop_unlock_and_return;
- }
-
- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
- (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
- dprintk(1, KERN_ERR
- "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
- ZR_DEVNAME(zr));
- res = -EINVAL;
- goto scrop_unlock_and_return;
- }
-
- /* move into a form that we understand */
- settings.img_x = crop->c.left;
- settings.img_y = crop->c.top;
- settings.img_width = crop->c.width;
- settings.img_height = crop->c.height;
-
- /* check validity */
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- goto scrop_unlock_and_return;
-
- /* accept */
- fh->jpg_settings = settings;
-
-scrop_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
- return res;
-}
-
-static int zoran_g_jpegcomp(struct file *file, void *__fh,
- struct v4l2_jpegcompression *params)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- memset(params, 0, sizeof(*params));
-
- mutex_lock(&zr->resource_lock);
-
- params->quality = fh->jpg_settings.jpg_comp.quality;
- params->APPn = fh->jpg_settings.jpg_comp.APPn;
- memcpy(params->APP_data,
- fh->jpg_settings.jpg_comp.APP_data,
- fh->jpg_settings.jpg_comp.APP_len);
- params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
- memcpy(params->COM_data,
- fh->jpg_settings.jpg_comp.COM_data,
- fh->jpg_settings.jpg_comp.COM_len);
- params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
- params->jpeg_markers =
- fh->jpg_settings.jpg_comp.jpeg_markers;
-
- mutex_unlock(&zr->resource_lock);
-
- return 0;
-}
-
-static int zoran_s_jpegcomp(struct file *file, void *__fh,
- struct v4l2_jpegcompression *params)
-{
- struct zoran_fh *fh = __fh;
- struct zoran *zr = fh->zr;
- int res = 0;
- struct zoran_jpg_settings settings;
-
- settings = fh->jpg_settings;
-
- settings.jpg_comp = *params;
-
- mutex_lock(&zr->resource_lock);
-
- if (fh->buffers.active != ZORAN_FREE) {
- dprintk(1, KERN_WARNING
- "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
- ZR_DEVNAME(zr));
- res = -EBUSY;
- goto sjpegc_unlock_and_return;
- }
-
- res = zoran_check_jpg_settings(zr, &settings, 0);
- if (res)
- goto sjpegc_unlock_and_return;
- if (!fh->buffers.allocated)
- fh->buffers.buffer_size =
- zoran_v4l2_calc_bufsize(&fh->jpg_settings);
- fh->jpg_settings.jpg_comp = *params = settings.jpg_comp;
-sjpegc_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static unsigned int
-zoran_poll (struct file *file,
- poll_table *wait)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
- int res = 0, frame;
- unsigned long flags;
-
- /* we should check whether buffers are ready to be synced on
- * (w/o waits - O_NONBLOCK) here
- * if ready for read (sync), return POLLIN|POLLRDNORM,
- * if ready for write (sync), return POLLOUT|POLLWRNORM,
- * if error, return POLLERR,
- * if no buffers queued or so, return POLLNVAL
- */
-
- mutex_lock(&zr->resource_lock);
-
- switch (fh->map_mode) {
- case ZORAN_MAP_MODE_RAW:
- poll_wait(file, &zr->v4l_capq, wait);
- frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], zr->v4l_sync_tail,
- "UPMD"[zr->v4l_buffers.buffer[frame].state],
- zr->v4l_pend_tail, zr->v4l_pend_head);
- /* Process is the one capturing? */
- if (fh->buffers.active != ZORAN_FREE &&
- /* Buffer ready to DQBUF? */
- zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
- res = POLLIN | POLLRDNORM;
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- case ZORAN_MAP_MODE_JPG_REC:
- case ZORAN_MAP_MODE_JPG_PLAY:
- poll_wait(file, &zr->jpg_capq, wait);
- frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
-
- spin_lock_irqsave(&zr->spinlock, flags);
- dprintk(3,
- KERN_DEBUG
- "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
- ZR_DEVNAME(zr), __func__,
- "FAL"[fh->buffers.active], zr->jpg_que_tail,
- "UPMD"[zr->jpg_buffers.buffer[frame].state],
- zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
- if (fh->buffers.active != ZORAN_FREE &&
- zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
- if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
- res = POLLIN | POLLRDNORM;
- else
- res = POLLOUT | POLLWRNORM;
- }
- spin_unlock_irqrestore(&zr->spinlock, flags);
-
- break;
-
- default:
- dprintk(1,
- KERN_ERR
- "%s: %s - internal error, unknown map_mode=%d\n",
- ZR_DEVNAME(zr), __func__, fh->map_mode);
- res = POLLNVAL;
- }
-
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-
-/*
- * This maps the buffers to user space.
- *
- * Depending on the state of fh->map_mode
- * the V4L or the MJPEG buffers are mapped
- * per buffer or all together
- *
- * Note that we need to connect to some
- * unmap signal event to unmap the de-allocate
- * the buffer accordingly (zoran_vm_close())
- */
-
-static void
-zoran_vm_open (struct vm_area_struct *vma)
-{
- struct zoran_mapping *map = vma->vm_private_data;
-
- map->count++;
-}
-
-static void
-zoran_vm_close (struct vm_area_struct *vma)
-{
- struct zoran_mapping *map = vma->vm_private_data;
- struct zoran_fh *fh = map->fh;
- struct zoran *zr = fh->zr;
- int i;
-
- if (--map->count > 0)
- return;
-
- dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
- __func__, mode_name(fh->map_mode));
-
- for (i = 0; i < fh->buffers.num_buffers; i++) {
- if (fh->buffers.buffer[i].map == map)
- fh->buffers.buffer[i].map = NULL;
- }
- kfree(map);
-
- /* Any buffers still mapped? */
- for (i = 0; i < fh->buffers.num_buffers; i++)
- if (fh->buffers.buffer[i].map)
- return;
-
- dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
- __func__, mode_name(fh->map_mode));
-
- mutex_lock(&zr->resource_lock);
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- if (fh->buffers.active != ZORAN_FREE) {
- unsigned long flags;
-
- spin_lock_irqsave(&zr->spinlock, flags);
- zr36057_set_memgrab(zr, 0);
- zr->v4l_buffers.allocated = 0;
- zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
- spin_unlock_irqrestore(&zr->spinlock, flags);
- }
- v4l_fbuffer_free(fh);
- } else {
- if (fh->buffers.active != ZORAN_FREE) {
- jpg_qbuf(fh, -1, zr->codec_mode);
- zr->jpg_buffers.allocated = 0;
- zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
- }
- jpg_fbuffer_free(fh);
- }
-
- mutex_unlock(&zr->resource_lock);
-}
-
-static const struct vm_operations_struct zoran_vm_ops = {
- .open = zoran_vm_open,
- .close = zoran_vm_close,
-};
-
-static int
-zoran_mmap (struct file *file,
- struct vm_area_struct *vma)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
- unsigned long size = (vma->vm_end - vma->vm_start);
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- int i, j;
- unsigned long page, start = vma->vm_start, todo, pos, fraglen;
- int first, last;
- struct zoran_mapping *map;
- int res = 0;
-
- dprintk(3,
- KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
- ZR_DEVNAME(zr), __func__,
- mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
-
- if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
- !(vma->vm_flags & VM_WRITE)) {
- dprintk(1,
- KERN_ERR
- "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
- ZR_DEVNAME(zr), __func__);
- return -EINVAL;
- }
-
- mutex_lock(&zr->resource_lock);
-
- if (!fh->buffers.allocated) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - buffers not yet allocated\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
- res = -ENOMEM;
- goto mmap_unlock_and_return;
- }
-
- first = offset / fh->buffers.buffer_size;
- last = first - 1 + size / fh->buffers.buffer_size;
- if (offset % fh->buffers.buffer_size != 0 ||
- size % fh->buffers.buffer_size != 0 || first < 0 ||
- last < 0 || first >= fh->buffers.num_buffers ||
- last >= fh->buffers.buffer_size) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
- fh->buffers.buffer_size,
- fh->buffers.num_buffers);
- res = -EINVAL;
- goto mmap_unlock_and_return;
- }
-
- /* Check if any buffers are already mapped */
- for (i = first; i <= last; i++) {
- if (fh->buffers.buffer[i].map) {
- dprintk(1,
- KERN_ERR
- "%s: %s(%s) - buffer %d already mapped\n",
- ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
- res = -EBUSY;
- goto mmap_unlock_and_return;
- }
- }
-
- /* map these buffers */
- map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
- if (!map) {
- res = -ENOMEM;
- goto mmap_unlock_and_return;
- }
- map->fh = fh;
- map->count = 1;
-
- vma->vm_ops = &zoran_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_private_data = map;
-
- if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
- for (i = first; i <= last; i++) {
- todo = size;
- if (todo > fh->buffers.buffer_size)
- todo = fh->buffers.buffer_size;
- page = fh->buffers.buffer[i].v4l.fbuffer_phys;
- if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
- todo, PAGE_SHARED)) {
- dprintk(1,
- KERN_ERR
- "%s: %s(V4L) - remap_pfn_range failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -EAGAIN;
- goto mmap_unlock_and_return;
- }
- size -= todo;
- start += todo;
- fh->buffers.buffer[i].map = map;
- if (size == 0)
- break;
- }
- } else {
- for (i = first; i <= last; i++) {
- for (j = 0;
- j < fh->buffers.buffer_size / PAGE_SIZE;
- j++) {
- fraglen =
- (le32_to_cpu(fh->buffers.buffer[i].jpg.
- frag_tab[2 * j + 1]) & ~1) << 1;
- todo = size;
- if (todo > fraglen)
- todo = fraglen;
- pos =
- le32_to_cpu(fh->buffers.
- buffer[i].jpg.frag_tab[2 * j]);
- /* should just be pos on i386 */
- page = virt_to_phys(bus_to_virt(pos))
- >> PAGE_SHIFT;
- if (remap_pfn_range(vma, start, page,
- todo, PAGE_SHARED)) {
- dprintk(1,
- KERN_ERR
- "%s: %s(V4L) - remap_pfn_range failed\n",
- ZR_DEVNAME(zr), __func__);
- res = -EAGAIN;
- goto mmap_unlock_and_return;
- }
- size -= todo;
- start += todo;
- if (size == 0)
- break;
- if (le32_to_cpu(fh->buffers.buffer[i].jpg.
- frag_tab[2 * j + 1]) & 1)
- break; /* was last fragment */
- }
- fh->buffers.buffer[i].map = map;
- if (size == 0)
- break;
-
- }
- }
-
-mmap_unlock_and_return:
- mutex_unlock(&zr->resource_lock);
-
- return res;
-}
-
-static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
- .vidioc_querycap = zoran_querycap,
- .vidioc_cropcap = zoran_cropcap,
- .vidioc_s_crop = zoran_s_crop,
- .vidioc_g_crop = zoran_g_crop,
- .vidioc_enum_input = zoran_enum_input,
- .vidioc_g_input = zoran_g_input,
- .vidioc_s_input = zoran_s_input,
- .vidioc_enum_output = zoran_enum_output,
- .vidioc_g_output = zoran_g_output,
- .vidioc_s_output = zoran_s_output,
- .vidioc_g_fbuf = zoran_g_fbuf,
- .vidioc_s_fbuf = zoran_s_fbuf,
- .vidioc_g_std = zoran_g_std,
- .vidioc_s_std = zoran_s_std,
- .vidioc_g_jpegcomp = zoran_g_jpegcomp,
- .vidioc_s_jpegcomp = zoran_s_jpegcomp,
- .vidioc_overlay = zoran_overlay,
- .vidioc_reqbufs = zoran_reqbufs,
- .vidioc_querybuf = zoran_querybuf,
- .vidioc_qbuf = zoran_qbuf,
- .vidioc_dqbuf = zoran_dqbuf,
- .vidioc_streamon = zoran_streamon,
- .vidioc_streamoff = zoran_streamoff,
- .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap,
- .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,
- .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay,
- .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap,
- .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,
- .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay,
- .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap,
- .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,
- .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay,
- .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap,
- .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,
- .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay,
- .vidioc_queryctrl = zoran_queryctrl,
- .vidioc_s_ctrl = zoran_s_ctrl,
- .vidioc_g_ctrl = zoran_g_ctrl,
-};
-
-/* please use zr->resource_lock consistently and kill this wrapper */
-static long zoran_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- struct zoran_fh *fh = file->private_data;
- struct zoran *zr = fh->zr;
- int ret;
-
- mutex_lock(&zr->other_lock);
- ret = video_ioctl2(file, cmd, arg);
- mutex_unlock(&zr->other_lock);
-
- return ret;
-}
-
-static const struct v4l2_file_operations zoran_fops = {
- .owner = THIS_MODULE,
- .open = zoran_open,
- .release = zoran_close,
- .unlocked_ioctl = zoran_ioctl,
- .read = zoran_read,
- .write = zoran_write,
- .mmap = zoran_mmap,
- .poll = zoran_poll,
-};
-
-struct video_device zoran_template __devinitdata = {
- .name = ZORAN_NAME,
- .fops = &zoran_fops,
- .ioctl_ops = &zoran_ioctl_ops,
- .release = &zoran_vdev_release,
- .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
-};
-
diff --git a/drivers/mfd/88pm800.c b/drivers/mfd/88pm800.c
index b67a3018b136..ce229ea933d1 100644
--- a/drivers/mfd/88pm800.c
+++ b/drivers/mfd/88pm800.c
@@ -470,7 +470,8 @@ static int __devinit device_800_init(struct pm80x_chip *chip,
ret =
mfd_add_devices(chip->dev, 0, &onkey_devs[0],
- ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0);
+ ARRAY_SIZE(onkey_devs), &onkey_resources[0], 0,
+ NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
@@ -481,7 +482,7 @@ static int __devinit device_800_init(struct pm80x_chip *chip,
rtc_devs[0].platform_data = pdata->rtc;
rtc_devs[0].pdata_size = sizeof(struct pm80x_rtc_pdata);
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
- ARRAY_SIZE(rtc_devs), NULL, 0);
+ ARRAY_SIZE(rtc_devs), NULL, 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out_dev;
diff --git a/drivers/mfd/88pm805.c b/drivers/mfd/88pm805.c
index 6146583589f6..c20a31136f04 100644
--- a/drivers/mfd/88pm805.c
+++ b/drivers/mfd/88pm805.c
@@ -216,7 +216,8 @@ static int __devinit device_805_init(struct pm80x_chip *chip)
}
ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
- ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+ ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
+ NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add codec subdev\n");
goto out_codec;
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c
index d09918cf1b15..b73f033b2c60 100644
--- a/drivers/mfd/88pm860x-core.c
+++ b/drivers/mfd/88pm860x-core.c
@@ -637,7 +637,7 @@ static void __devinit device_bk_init(struct pm860x_chip *chip,
bk_devs[i].resources = &bk_resources[j];
ret = mfd_add_devices(chip->dev, 0,
&bk_devs[i], 1,
- &bk_resources[j], 0);
+ &bk_resources[j], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add "
"backlight subdev\n");
@@ -672,7 +672,7 @@ static void __devinit device_led_init(struct pm860x_chip *chip,
led_devs[i].resources = &led_resources[j],
ret = mfd_add_devices(chip->dev, 0,
&led_devs[i], 1,
- &led_resources[j], 0);
+ &led_resources[j], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add "
"led subdev\n");
@@ -709,7 +709,7 @@ static void __devinit device_regulator_init(struct pm860x_chip *chip,
regulator_devs[i].resources = &regulator_resources[seq];
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[i], 1,
- &regulator_resources[seq], 0);
+ &regulator_resources[seq], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regulator subdev\n");
goto out;
@@ -733,7 +733,7 @@ static void __devinit device_rtc_init(struct pm860x_chip *chip,
rtc_devs[0].resources = &rtc_resources[0];
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs), &rtc_resources[0],
- chip->irq_base);
+ chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add rtc subdev\n");
}
@@ -752,7 +752,7 @@ static void __devinit device_touch_init(struct pm860x_chip *chip,
touch_devs[0].resources = &touch_resources[0];
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs), &touch_resources[0],
- chip->irq_base);
+ chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add touch subdev\n");
}
@@ -770,7 +770,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
power_devs[0].num_resources = ARRAY_SIZE(battery_resources);
power_devs[0].resources = &battery_resources[0],
ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1,
- &battery_resources[0], chip->irq_base);
+ &battery_resources[0], chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add battery subdev\n");
@@ -779,7 +779,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
power_devs[1].num_resources = ARRAY_SIZE(charger_resources);
power_devs[1].resources = &charger_resources[0],
ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1,
- &charger_resources[0], chip->irq_base);
+ &charger_resources[0], chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add charger subdev\n");
@@ -788,7 +788,7 @@ static void __devinit device_power_init(struct pm860x_chip *chip,
power_devs[2].num_resources = ARRAY_SIZE(preg_resources);
power_devs[2].resources = &preg_resources[0],
ret = mfd_add_devices(chip->dev, 0, &power_devs[2], 1,
- &preg_resources[0], chip->irq_base);
+ &preg_resources[0], chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add preg subdev\n");
}
@@ -802,7 +802,7 @@ static void __devinit device_onkey_init(struct pm860x_chip *chip,
onkey_devs[0].resources = &onkey_resources[0],
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs), &onkey_resources[0],
- chip->irq_base);
+ chip->irq_base, NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add onkey subdev\n");
}
@@ -815,7 +815,8 @@ static void __devinit device_codec_init(struct pm860x_chip *chip,
codec_devs[0].num_resources = ARRAY_SIZE(codec_resources);
codec_devs[0].resources = &codec_resources[0],
ret = mfd_add_devices(chip->dev, 0, &codec_devs[0],
- ARRAY_SIZE(codec_devs), &codec_resources[0], 0);
+ ARRAY_SIZE(codec_devs), &codec_resources[0], 0,
+ NULL);
if (ret < 0)
dev_err(chip->dev, "Failed to add codec subdev\n");
}
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d1facef28a60..b1a146205c08 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -395,7 +395,8 @@ config MFD_TC6387XB
config MFD_TC6393XB
bool "Support Toshiba TC6393XB"
- depends on GPIOLIB && ARM && HAVE_CLK
+ depends on ARM && HAVE_CLK
+ select GPIOLIB
select MFD_CORE
select MFD_TMIO
help
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c
index 44a3fdbadef4..f1beb4971f87 100644
--- a/drivers/mfd/aat2870-core.c
+++ b/drivers/mfd/aat2870-core.c
@@ -424,7 +424,7 @@ static int aat2870_i2c_probe(struct i2c_client *client,
}
ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs,
- ARRAY_SIZE(aat2870_devs), NULL, 0);
+ ARRAY_SIZE(aat2870_devs), NULL, 0, NULL);
if (ret != 0) {
dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret);
goto out_disable;
diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c
index 4276aab4f196..01781ae5d0d7 100644
--- a/drivers/mfd/ab3100-core.c
+++ b/drivers/mfd/ab3100-core.c
@@ -409,8 +409,6 @@ static irqreturn_t ab3100_irq_handler(int irq, void *data)
u32 fatevent;
int err;
- add_interrupt_randomness(irq);
-
err = ab3100_get_register_page_interruptible(ab3100, AB3100_EVENTA1,
event_regs, 3);
if (err)
@@ -936,8 +934,6 @@ static int __devinit ab3100_probe(struct i2c_client *client,
IRQF_ONESHOT, "ab3100-core", ab3100);
if (err)
goto exit_no_irq;
- /* This real unpredictable IRQ is of course sampled for entropy */
- rand_initialize_irq(client->irq);
err = abx500_register_ops(&client->dev, &ab3100_ops);
if (err)
@@ -950,7 +946,7 @@ static int __devinit ab3100_probe(struct i2c_client *client,
}
err = mfd_add_devices(&client->dev, 0, ab3100_devs,
- ARRAY_SIZE(ab3100_devs), NULL, 0);
+ ARRAY_SIZE(ab3100_devs), NULL, 0, NULL);
ab3100_setup_debugfs(ab3100);
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 626b4ecaf647..47adf800024e 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -1418,25 +1418,25 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs,
ARRAY_SIZE(abx500_common_devs), NULL,
- ab8500->irq_base);
+ ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
if (is_ab9540(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs,
ARRAY_SIZE(ab9540_devs), NULL,
- ab8500->irq_base);
+ ab8500->irq_base, ab8500->domain);
else
ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs,
ARRAY_SIZE(ab8500_devs), NULL,
- ab8500->irq_base);
+ ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
if (is_ab9540(ab8500) || is_ab8505(ab8500))
ret = mfd_add_devices(ab8500->dev, 0, ab9540_ab8505_devs,
ARRAY_SIZE(ab9540_ab8505_devs), NULL,
- ab8500->irq_base);
+ ab8500->irq_base, ab8500->domain);
if (ret)
goto out_freeirq;
@@ -1444,7 +1444,7 @@ static int __devinit ab8500_probe(struct platform_device *pdev)
/* Add battery management devices */
ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
ARRAY_SIZE(ab8500_bm_devs), NULL,
- ab8500->irq_base);
+ ab8500->irq_base, ab8500->domain);
if (ret)
dev_err(ab8500->dev, "error adding bm devices\n");
}
diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c
index c7983e862549..1b48f2094806 100644
--- a/drivers/mfd/arizona-core.c
+++ b/drivers/mfd/arizona-core.c
@@ -316,7 +316,7 @@ int __devinit arizona_dev_init(struct arizona *arizona)
}
ret = mfd_add_devices(arizona->dev, -1, early_devs,
- ARRAY_SIZE(early_devs), NULL, 0);
+ ARRAY_SIZE(early_devs), NULL, 0, NULL);
if (ret != 0) {
dev_err(dev, "Failed to add early children: %d\n", ret);
return ret;
@@ -516,11 +516,11 @@ int __devinit arizona_dev_init(struct arizona *arizona)
switch (arizona->type) {
case WM5102:
ret = mfd_add_devices(arizona->dev, -1, wm5102_devs,
- ARRAY_SIZE(wm5102_devs), NULL, 0);
+ ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
break;
case WM5110:
ret = mfd_add_devices(arizona->dev, -1, wm5110_devs,
- ARRAY_SIZE(wm5102_devs), NULL, 0);
+ ARRAY_SIZE(wm5102_devs), NULL, 0, NULL);
break;
}
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 383421bf5760..62f0883a7630 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -913,18 +913,19 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
if (pdata->clock_rate) {
ds1wm_pdata.clock_rate = pdata->clock_rate;
ret = mfd_add_devices(&pdev->dev, pdev->id,
- &asic3_cell_ds1wm, 1, mem, asic->irq_base);
+ &asic3_cell_ds1wm, 1, mem, asic->irq_base, NULL);
if (ret < 0)
goto out;
}
if (mem_sdio && (irq >= 0)) {
ret = mfd_add_devices(&pdev->dev, pdev->id,
- &asic3_cell_mmc, 1, mem_sdio, irq);
+ &asic3_cell_mmc, 1, mem_sdio, irq, NULL);
if (ret < 0)
goto out;
}
+ ret = 0;
if (pdata->leds) {
int i;
@@ -933,7 +934,7 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]);
}
ret = mfd_add_devices(&pdev->dev, 0,
- asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0);
+ asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0, NULL);
}
out:
diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c
index 3419e726de47..2b282133c725 100644
--- a/drivers/mfd/cs5535-mfd.c
+++ b/drivers/mfd/cs5535-mfd.c
@@ -149,7 +149,7 @@ static int __devinit cs5535_mfd_probe(struct pci_dev *pdev,
}
err = mfd_add_devices(&pdev->dev, -1, cs5535_mfd_cells,
- ARRAY_SIZE(cs5535_mfd_cells), NULL, 0);
+ ARRAY_SIZE(cs5535_mfd_cells), NULL, 0, NULL);
if (err) {
dev_err(&pdev->dev, "MFD add devices failed: %d\n", err);
goto err_disable;
diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c
index 2544910e1fd6..a0a62b24621b 100644
--- a/drivers/mfd/da9052-core.c
+++ b/drivers/mfd/da9052-core.c
@@ -803,7 +803,7 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)
dev_err(da9052->dev, "DA9052 ADC IRQ failed ret=%d\n", ret);
ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info,
- ARRAY_SIZE(da9052_subdev_info), NULL, 0);
+ ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL);
if (ret)
goto err;
diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c
index 4e2af2cb2d26..45e83a68641b 100644
--- a/drivers/mfd/davinci_voicecodec.c
+++ b/drivers/mfd/davinci_voicecodec.c
@@ -129,7 +129,7 @@ static int __init davinci_vc_probe(struct platform_device *pdev)
cell->pdata_size = sizeof(*davinci_vc);
ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
- DAVINCI_VC_CELLS, NULL, 0);
+ DAVINCI_VC_CELLS, NULL, 0, NULL);
if (ret != 0) {
dev_err(&pdev->dev, "fail to register client devices\n");
goto fail4;
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 7040a0081130..0e63cdd9b52a 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -3010,7 +3010,7 @@ static int __devinit db8500_prcmu_probe(struct platform_device *pdev)
prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
- ARRAY_SIZE(db8500_prcmu_devs), NULL, 0);
+ ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, NULL);
if (err) {
pr_err("prcmu: Failed to add subdevices\n");
return err;
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index 43a76c41cfcc..db662e2dcfa5 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -202,7 +202,7 @@ static void pcap_isr_work(struct work_struct *work)
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
- } while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
+ } while (gpio_get_value(pdata->gpio));
}
static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc)
diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c
index 04c7093d6499..9e5453d21a68 100644
--- a/drivers/mfd/htc-pasic3.c
+++ b/drivers/mfd/htc-pasic3.c
@@ -168,7 +168,7 @@ static int __init pasic3_probe(struct platform_device *pdev)
/* the first 5 PASIC3 registers control the DS1WM */
ds1wm_resources[0].end = (5 << asic->bus_shift) - 1;
ret = mfd_add_devices(&pdev->dev, pdev->id,
- &ds1wm_cell, 1, r, irq);
+ &ds1wm_cell, 1, r, irq, NULL);
if (ret < 0)
dev_warn(dev, "failed to register DS1WM\n");
}
@@ -176,7 +176,8 @@ static int __init pasic3_probe(struct platform_device *pdev)
if (pdata && pdata->led_pdata) {
led_cell.platform_data = pdata->led_pdata;
led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo);
- ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0);
+ ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r,
+ 0, NULL);
if (ret < 0)
dev_warn(dev, "failed to register LED device\n");
}
diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c
index 59df5584cb58..266bdc5bd96d 100644
--- a/drivers/mfd/intel_msic.c
+++ b/drivers/mfd/intel_msic.c
@@ -344,13 +344,13 @@ static int __devinit intel_msic_init_devices(struct intel_msic *msic)
continue;
ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL,
- pdata->irq[i]);
+ pdata->irq[i], NULL);
if (ret)
goto fail;
}
ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs,
- ARRAY_SIZE(msic_other_devs), NULL, 0);
+ ARRAY_SIZE(msic_other_devs), NULL, 0, NULL);
if (ret)
goto fail;
diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c
index 2ea99989551a..965c4801df8a 100644
--- a/drivers/mfd/janz-cmodio.c
+++ b/drivers/mfd/janz-cmodio.c
@@ -147,7 +147,7 @@ static int __devinit cmodio_probe_submodules(struct cmodio_device *priv)
}
return mfd_add_devices(&pdev->dev, 0, priv->cells,
- num_probed, NULL, pdev->irq);
+ num_probed, NULL, pdev->irq, NULL);
}
/*
diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c
index 87662a17dec6..c6b6d7dda517 100644
--- a/drivers/mfd/jz4740-adc.c
+++ b/drivers/mfd/jz4740-adc.c
@@ -287,7 +287,8 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev)
writeb(0xff, adc->base + JZ_REG_ADC_CTRL);
ret = mfd_add_devices(&pdev->dev, 0, jz4740_adc_cells,
- ARRAY_SIZE(jz4740_adc_cells), mem_base, irq_base);
+ ARRAY_SIZE(jz4740_adc_cells), mem_base,
+ irq_base, NULL);
if (ret < 0)
goto err_clk_put;
diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c
index 0b2879b87fd9..24212f45b201 100644
--- a/drivers/mfd/lm3533-core.c
+++ b/drivers/mfd/lm3533-core.c
@@ -393,7 +393,8 @@ static int __devinit lm3533_device_als_init(struct lm3533 *lm3533)
lm3533_als_devs[0].platform_data = pdata->als;
lm3533_als_devs[0].pdata_size = sizeof(*pdata->als);
- ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL, 0);
+ ret = mfd_add_devices(lm3533->dev, 0, lm3533_als_devs, 1, NULL,
+ 0, NULL);
if (ret) {
dev_err(lm3533->dev, "failed to add ALS device\n");
return ret;
@@ -422,7 +423,7 @@ static int __devinit lm3533_device_bl_init(struct lm3533 *lm3533)
}
ret = mfd_add_devices(lm3533->dev, 0, lm3533_bl_devs,
- pdata->num_backlights, NULL, 0);
+ pdata->num_backlights, NULL, 0, NULL);
if (ret) {
dev_err(lm3533->dev, "failed to add backlight devices\n");
return ret;
@@ -451,7 +452,7 @@ static int __devinit lm3533_device_led_init(struct lm3533 *lm3533)
}
ret = mfd_add_devices(lm3533->dev, 0, lm3533_led_devs,
- pdata->num_leds, NULL, 0);
+ pdata->num_leds, NULL, 0, NULL);
if (ret) {
dev_err(lm3533->dev, "failed to add LED devices\n");
return ret;
diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c
index 027cc8f86132..092ad4b44b6d 100644
--- a/drivers/mfd/lpc_ich.c
+++ b/drivers/mfd/lpc_ich.c
@@ -750,7 +750,7 @@ gpe0_done:
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
- 1, NULL, 0);
+ 1, NULL, 0, NULL);
gpio_done:
if (acpi_conflict)
@@ -765,7 +765,6 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
u32 base_addr_cfg;
u32 base_addr;
int ret;
- bool acpi_conflict = false;
struct resource *res;
/* Setup power management base register */
@@ -780,20 +779,11 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
res = wdt_io_res(ICH_RES_IO_TCO);
res->start = base_addr + ACPIBASE_TCO_OFF;
res->end = base_addr + ACPIBASE_TCO_END;
- ret = acpi_check_resource_conflict(res);
- if (ret) {
- acpi_conflict = true;
- goto wdt_done;
- }
res = wdt_io_res(ICH_RES_IO_SMI);
res->start = base_addr + ACPIBASE_SMI_OFF;
res->end = base_addr + ACPIBASE_SMI_END;
- ret = acpi_check_resource_conflict(res);
- if (ret) {
- acpi_conflict = true;
- goto wdt_done;
- }
+
lpc_ich_enable_acpi_space(dev);
/*
@@ -813,21 +803,13 @@ static int __devinit lpc_ich_init_wdt(struct pci_dev *dev,
res = wdt_mem_res(ICH_RES_MEM_GCS);
res->start = base_addr + ACPIBASE_GCS_OFF;
res->end = base_addr + ACPIBASE_GCS_END;
- ret = acpi_check_resource_conflict(res);
- if (ret) {
- acpi_conflict = true;
- goto wdt_done;
- }
}
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
- 1, NULL, 0);
+ 1, NULL, 0, NULL);
wdt_done:
- if (acpi_conflict)
- pr_warn("Resource conflict(s) found affecting %s\n",
- lpc_ich_cells[LPC_WDT].name);
return ret;
}
diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c
index 9f20abc5e393..f6b9c5c96b24 100644
--- a/drivers/mfd/lpc_sch.c
+++ b/drivers/mfd/lpc_sch.c
@@ -127,7 +127,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
lpc_sch_cells[i].id = id->device;
ret = mfd_add_devices(&dev->dev, 0,
- lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+ lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
+ 0, NULL);
if (ret)
goto out_dev;
@@ -153,7 +154,8 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev,
tunnelcreek_cells[i].id = id->device;
ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
- ARRAY_SIZE(tunnelcreek_cells), NULL, 0);
+ ARRAY_SIZE(tunnelcreek_cells), NULL,
+ 0, NULL);
}
return ret;
diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c
index c03e12b51924..d9e24c849a00 100644
--- a/drivers/mfd/max77686.c
+++ b/drivers/mfd/max77686.c
@@ -126,7 +126,7 @@ static int max77686_i2c_probe(struct i2c_client *i2c,
max77686_irq_init(max77686);
ret = mfd_add_devices(max77686->dev, -1, max77686_devs,
- ARRAY_SIZE(max77686_devs), NULL, 0);
+ ARRAY_SIZE(max77686_devs), NULL, 0, NULL);
if (ret < 0)
goto err_mfd;
diff --git a/drivers/mfd/max77693-irq.c b/drivers/mfd/max77693-irq.c
index 2b403569e0a6..1029d018c739 100644
--- a/drivers/mfd/max77693-irq.c
+++ b/drivers/mfd/max77693-irq.c
@@ -137,6 +137,9 @@ static void max77693_irq_mask(struct irq_data *data)
const struct max77693_irq_data *irq_data =
irq_to_max77693_irq(max77693, data->irq);
+ if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+ return;
+
if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
max77693->irq_masks_cur[irq_data->group] &= ~irq_data->mask;
else
@@ -149,6 +152,9 @@ static void max77693_irq_unmask(struct irq_data *data)
const struct max77693_irq_data *irq_data =
irq_to_max77693_irq(max77693, data->irq);
+ if (irq_data->group >= MAX77693_IRQ_GROUP_NR)
+ return;
+
if (irq_data->group >= MUIC_INT1 && irq_data->group <= MUIC_INT3)
max77693->irq_masks_cur[irq_data->group] |= irq_data->mask;
else
@@ -200,7 +206,7 @@ static irqreturn_t max77693_irq_thread(int irq, void *data)
if (irq_src & MAX77693_IRQSRC_MUIC)
/* MUIC INT1 ~ INT3 */
- max77693_bulk_read(max77693->regmap, MAX77693_MUIC_REG_INT1,
+ max77693_bulk_read(max77693->regmap_muic, MAX77693_MUIC_REG_INT1,
MAX77693_NUM_IRQ_MUIC_REGS, &irq_reg[MUIC_INT1]);
/* Apply masking */
@@ -255,7 +261,8 @@ int max77693_irq_init(struct max77693_dev *max77693)
{
struct irq_domain *domain;
int i;
- int ret;
+ int ret = 0;
+ u8 intsrc_mask;
mutex_init(&max77693->irqlock);
@@ -287,19 +294,38 @@ int max77693_irq_init(struct max77693_dev *max77693)
&max77693_irq_domain_ops, max77693);
if (!domain) {
dev_err(max77693->dev, "could not create irq domain\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_irq;
}
max77693->irq_domain = domain;
+ /* Unmask max77693 interrupt */
+ ret = max77693_read_reg(max77693->regmap,
+ MAX77693_PMIC_REG_INTSRC_MASK, &intsrc_mask);
+ if (ret < 0) {
+ dev_err(max77693->dev, "fail to read PMIC register\n");
+ goto err_irq;
+ }
+
+ intsrc_mask &= ~(MAX77693_IRQSRC_CHG);
+ intsrc_mask &= ~(MAX77693_IRQSRC_FLASH);
+ intsrc_mask &= ~(MAX77693_IRQSRC_MUIC);
+ ret = max77693_write_reg(max77693->regmap,
+ MAX77693_PMIC_REG_INTSRC_MASK, intsrc_mask);
+ if (ret < 0) {
+ dev_err(max77693->dev, "fail to write PMIC register\n");
+ goto err_irq;
+ }
+
ret = request_threaded_irq(max77693->irq, NULL, max77693_irq_thread,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max77693-irq", max77693);
-
if (ret)
dev_err(max77693->dev, "Failed to request IRQ %d: %d\n",
max77693->irq, ret);
- return 0;
+err_irq:
+ return ret;
}
void max77693_irq_exit(struct max77693_dev *max77693)
diff --git a/drivers/mfd/max77693.c b/drivers/mfd/max77693.c
index a1811cb50ec7..cc5155e20494 100644
--- a/drivers/mfd/max77693.c
+++ b/drivers/mfd/max77693.c
@@ -152,6 +152,20 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
max77693->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
i2c_set_clientdata(max77693->haptic, max77693);
+ /*
+ * Initialize register map for MUIC device because use regmap-muic
+ * instance of MUIC device when irq of max77693 is initialized
+ * before call max77693-muic probe() function.
+ */
+ max77693->regmap_muic = devm_regmap_init_i2c(max77693->muic,
+ &max77693_regmap_config);
+ if (IS_ERR(max77693->regmap_muic)) {
+ ret = PTR_ERR(max77693->regmap_muic);
+ dev_err(max77693->dev,
+ "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
ret = max77693_irq_init(max77693);
if (ret < 0)
goto err_irq;
@@ -159,7 +173,7 @@ static int max77693_i2c_probe(struct i2c_client *i2c,
pm_runtime_set_active(max77693->dev);
ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
- ARRAY_SIZE(max77693_devs), NULL, 0);
+ ARRAY_SIZE(max77693_devs), NULL, 0, NULL);
if (ret < 0)
goto err_mfd;
diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c
index 825a7f06d9ba..ee53757beca7 100644
--- a/drivers/mfd/max8925-core.c
+++ b/drivers/mfd/max8925-core.c
@@ -598,7 +598,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &rtc_devs[0],
ARRAY_SIZE(rtc_devs),
- &rtc_resources[0], chip->irq_base);
+ &rtc_resources[0], chip->irq_base, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add rtc subdev\n");
goto out;
@@ -606,7 +606,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0],
ARRAY_SIZE(onkey_devs),
- &onkey_resources[0], 0);
+ &onkey_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add onkey subdev\n");
goto out_dev;
@@ -615,7 +615,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
if (pdata) {
ret = mfd_add_devices(chip->dev, 0, &regulator_devs[0],
ARRAY_SIZE(regulator_devs),
- &regulator_resources[0], 0);
+ &regulator_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regulator subdev\n");
goto out_dev;
@@ -625,7 +625,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
if (pdata && pdata->backlight) {
ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0],
ARRAY_SIZE(backlight_devs),
- &backlight_resources[0], 0);
+ &backlight_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add backlight subdev\n");
goto out_dev;
@@ -635,7 +635,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
if (pdata && pdata->power) {
ret = mfd_add_devices(chip->dev, 0, &power_devs[0],
ARRAY_SIZE(power_devs),
- &power_supply_resources[0], 0);
+ &power_supply_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add power supply "
"subdev\n");
@@ -646,7 +646,7 @@ int __devinit max8925_device_init(struct max8925_chip *chip,
if (pdata && pdata->touch) {
ret = mfd_add_devices(chip->dev, 0, &touch_devs[0],
ARRAY_SIZE(touch_devs),
- &touch_resources[0], 0);
+ &touch_resources[0], 0, NULL);
if (ret < 0) {
dev_err(chip->dev, "Failed to add touch subdev\n");
goto out_dev;
diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c
index 10b629c245b6..f123517065ec 100644
--- a/drivers/mfd/max8997.c
+++ b/drivers/mfd/max8997.c
@@ -160,7 +160,7 @@ static int max8997_i2c_probe(struct i2c_client *i2c,
mfd_add_devices(max8997->dev, -1, max8997_devs,
ARRAY_SIZE(max8997_devs),
- NULL, 0);
+ NULL, 0, NULL);
/*
* TODO: enable others (flash, muic, rtc, battery, ...) and
diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c
index 6ef56d28c056..d7218cc90945 100644
--- a/drivers/mfd/max8998.c
+++ b/drivers/mfd/max8998.c
@@ -161,13 +161,13 @@ static int max8998_i2c_probe(struct i2c_client *i2c,
switch (id->driver_data) {
case TYPE_LP3974:
ret = mfd_add_devices(max8998->dev, -1,
- lp3974_devs, ARRAY_SIZE(lp3974_devs),
- NULL, 0);
+ lp3974_devs, ARRAY_SIZE(lp3974_devs),
+ NULL, 0, NULL);
break;
case TYPE_MAX8998:
ret = mfd_add_devices(max8998->dev, -1,
- max8998_devs, ARRAY_SIZE(max8998_devs),
- NULL, 0);
+ max8998_devs, ARRAY_SIZE(max8998_devs),
+ NULL, 0, NULL);
break;
default:
ret = -EINVAL;
diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c
index b801dc72f041..1ec79b54bd2f 100644
--- a/drivers/mfd/mc13xxx-core.c
+++ b/drivers/mfd/mc13xxx-core.c
@@ -612,7 +612,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx,
if (!cell.name)
return -ENOMEM;
- return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0);
+ return mfd_add_devices(mc13xxx->dev, -1, &cell, 1, NULL, 0, NULL);
}
static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format)
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index 0c3a01cde2f7..f8b77711ad2d 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -74,12 +74,11 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell,
struct resource *mem_base,
- int irq_base)
+ int irq_base, struct irq_domain *domain)
{
struct resource *res;
struct platform_device *pdev;
struct device_node *np = NULL;
- struct irq_domain *domain = NULL;
int ret = -ENOMEM;
int r;
@@ -97,7 +96,6 @@ static int mfd_add_device(struct device *parent, int id,
for_each_child_of_node(parent->of_node, np) {
if (of_device_is_compatible(np, cell->of_compatible)) {
pdev->dev.of_node = np;
- domain = irq_find_host(parent->of_node);
break;
}
}
@@ -177,7 +175,7 @@ fail_alloc:
int mfd_add_devices(struct device *parent, int id,
struct mfd_cell *cells, int n_devs,
struct resource *mem_base,
- int irq_base)
+ int irq_base, struct irq_domain *domain)
{
int i;
int ret = 0;
@@ -191,7 +189,8 @@ int mfd_add_devices(struct device *parent, int id,
for (i = 0; i < n_devs; i++) {
atomic_set(&cnts[i], 0);
cells[i].usage_count = &cnts[i];
- ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
+ ret = mfd_add_device(parent, id, cells + i, mem_base,
+ irq_base, domain);
if (ret)
break;
}
@@ -247,7 +246,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
for (i = 0; i < n_clones; i++) {
cell_entry.name = clones[i];
/* don't give up if a single call fails; just report error */
- if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0))
+ if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0,
+ NULL))
dev_err(dev, "failed to create platform device '%s'\n",
clones[i]);
}
diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c
index c4a69f193a1d..a345f9bb7b47 100644
--- a/drivers/mfd/palmas.c
+++ b/drivers/mfd/palmas.c
@@ -453,7 +453,8 @@ static int __devinit palmas_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(palmas->dev, -1,
children, ARRAY_SIZE(palmas_children),
- NULL, regmap_irq_chip_get_base(palmas->irq_data));
+ NULL, regmap_irq_chip_get_base(palmas->irq_data),
+ NULL);
kfree(children);
if (ret < 0)
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index cdc1df7fa0e9..3a8fa88567b1 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -289,7 +289,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
}
ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs,
- ARRAY_SIZE(rc5t583_subdevs), NULL, 0);
+ ARRAY_SIZE(rc5t583_subdevs), NULL, 0, NULL);
if (ret) {
dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret);
goto err_add_devs;
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index 685d61e431ad..0f70dce61160 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -87,7 +87,8 @@ static int __devinit rdc321x_sb_probe(struct pci_dev *pdev,
rdc321x_wdt_pdata.sb_pdev = pdev;
return mfd_add_devices(&pdev->dev, -1,
- rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells), NULL, 0);
+ rdc321x_sb_cells, ARRAY_SIZE(rdc321x_sb_cells),
+ NULL, 0, NULL);
}
static void __devexit rdc321x_sb_remove(struct pci_dev *pdev)
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c
index 2988efde11eb..49d361a618d0 100644
--- a/drivers/mfd/sec-core.c
+++ b/drivers/mfd/sec-core.c
@@ -141,19 +141,19 @@ static int sec_pmic_probe(struct i2c_client *i2c,
switch (sec_pmic->device_type) {
case S5M8751X:
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8751_devs,
- ARRAY_SIZE(s5m8751_devs), NULL, 0);
+ ARRAY_SIZE(s5m8751_devs), NULL, 0, NULL);
break;
case S5M8763X:
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8763_devs,
- ARRAY_SIZE(s5m8763_devs), NULL, 0);
+ ARRAY_SIZE(s5m8763_devs), NULL, 0, NULL);
break;
case S5M8767X:
ret = mfd_add_devices(sec_pmic->dev, -1, s5m8767_devs,
- ARRAY_SIZE(s5m8767_devs), NULL, 0);
+ ARRAY_SIZE(s5m8767_devs), NULL, 0, NULL);
break;
case S2MPS11X:
ret = mfd_add_devices(sec_pmic->dev, -1, s2mps11_devs,
- ARRAY_SIZE(s2mps11_devs), NULL, 0);
+ ARRAY_SIZE(s2mps11_devs), NULL, 0, NULL);
break;
default:
/* If this happens the probe function is problem */
diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c
index d31fed07aefb..d35da6820bea 100644
--- a/drivers/mfd/sta2x11-mfd.c
+++ b/drivers/mfd/sta2x11-mfd.c
@@ -407,7 +407,7 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
sta2x11_mfd_bar0,
ARRAY_SIZE(sta2x11_mfd_bar0),
&pdev->resource[0],
- 0);
+ 0, NULL);
if (err) {
dev_err(&pdev->dev, "mfd_add_devices[0] failed: %d\n", err);
goto err_disable;
@@ -417,7 +417,7 @@ static int __devinit sta2x11_mfd_probe(struct pci_dev *pdev,
sta2x11_mfd_bar1,
ARRAY_SIZE(sta2x11_mfd_bar1),
&pdev->resource[1],
- 0);
+ 0, NULL);
if (err) {
dev_err(&pdev->dev, "mfd_add_devices[1] failed: %d\n", err);
goto err_disable;
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 2dd8d49cb30b..c94f521f392c 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -962,7 +962,7 @@ static int __devinit stmpe_add_device(struct stmpe *stmpe,
struct mfd_cell *cell, int irq)
{
return mfd_add_devices(stmpe->dev, stmpe->pdata->id, cell, 1,
- NULL, stmpe->irq_base + irq);
+ NULL, stmpe->irq_base + irq, NULL);
}
static int __devinit stmpe_devices_init(struct stmpe *stmpe)
diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
index 2d9e8799e733..b32940ec9034 100644
--- a/drivers/mfd/t7l66xb.c
+++ b/drivers/mfd/t7l66xb.c
@@ -388,7 +388,7 @@ static int t7l66xb_probe(struct platform_device *dev)
ret = mfd_add_devices(&dev->dev, dev->id,
t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
- iomem, t7l66xb->irq_base);
+ iomem, t7l66xb->irq_base, NULL);
if (!ret)
return 0;
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index 048bf0532a09..b56ba6b43294 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -262,8 +262,8 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
if (blocks & TC3589x_BLOCK_GPIO) {
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio,
- ARRAY_SIZE(tc3589x_dev_gpio), NULL,
- tc3589x->irq_base);
+ ARRAY_SIZE(tc3589x_dev_gpio), NULL,
+ tc3589x->irq_base, NULL);
if (ret) {
dev_err(tc3589x->dev, "failed to add gpio child\n");
return ret;
@@ -273,8 +273,8 @@ static int __devinit tc3589x_device_init(struct tc3589x *tc3589x)
if (blocks & TC3589x_BLOCK_KEYPAD) {
ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad,
- ARRAY_SIZE(tc3589x_dev_keypad), NULL,
- tc3589x->irq_base);
+ ARRAY_SIZE(tc3589x_dev_keypad), NULL,
+ tc3589x->irq_base, NULL);
if (ret) {
dev_err(tc3589x->dev, "failed to keypad child\n");
return ret;
diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c
index d20a284ad4ba..413c891102f8 100644
--- a/drivers/mfd/tc6387xb.c
+++ b/drivers/mfd/tc6387xb.c
@@ -192,7 +192,7 @@ static int __devinit tc6387xb_probe(struct platform_device *dev)
printk(KERN_INFO "Toshiba tc6387xb initialised\n");
ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells,
- ARRAY_SIZE(tc6387xb_cells), iomem, irq);
+ ARRAY_SIZE(tc6387xb_cells), iomem, irq, NULL);
if (!ret)
return 0;
diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c
index 9612264f0e6d..dcab026fcbb2 100644
--- a/drivers/mfd/tc6393xb.c
+++ b/drivers/mfd/tc6393xb.c
@@ -700,8 +700,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev)
tc6393xb_cells[TC6393XB_CELL_FB].pdata_size = sizeof(*tcpd->fb_data);
ret = mfd_add_devices(&dev->dev, dev->id,
- tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
- iomem, tcpd->irq_base);
+ tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells),
+ iomem, tcpd->irq_base, NULL);
if (!ret)
return 0;
diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c
index 4fb0e6c8e8fe..7c3675a74f93 100644
--- a/drivers/mfd/ti-ssp.c
+++ b/drivers/mfd/ti-ssp.c
@@ -412,7 +412,7 @@ static int __devinit ti_ssp_probe(struct platform_device *pdev)
cells[id].data_size = data->pdata_size;
}
- error = mfd_add_devices(dev, 0, cells, 2, NULL, 0);
+ error = mfd_add_devices(dev, 0, cells, 2, NULL, 0, NULL);
if (error < 0) {
dev_err(dev, "cannot add mfd cells\n");
goto error_enable;
diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c
index a447f4ec11fb..cccc626c83c8 100644
--- a/drivers/mfd/timberdale.c
+++ b/drivers/mfd/timberdale.c
@@ -757,25 +757,25 @@ static int __devinit timb_probe(struct pci_dev *dev,
err = mfd_add_devices(&dev->dev, -1,
timberdale_cells_bar0_cfg0,
ARRAY_SIZE(timberdale_cells_bar0_cfg0),
- &dev->resource[0], msix_entries[0].vector);
+ &dev->resource[0], msix_entries[0].vector, NULL);
break;
case TIMB_HW_VER1:
err = mfd_add_devices(&dev->dev, -1,
timberdale_cells_bar0_cfg1,
ARRAY_SIZE(timberdale_cells_bar0_cfg1),
- &dev->resource[0], msix_entries[0].vector);
+ &dev->resource[0], msix_entries[0].vector, NULL);
break;
case TIMB_HW_VER2:
err = mfd_add_devices(&dev->dev, -1,
timberdale_cells_bar0_cfg2,
ARRAY_SIZE(timberdale_cells_bar0_cfg2),
- &dev->resource[0], msix_entries[0].vector);
+ &dev->resource[0], msix_entries[0].vector, NULL);
break;
case TIMB_HW_VER3:
err = mfd_add_devices(&dev->dev, -1,
timberdale_cells_bar0_cfg3,
ARRAY_SIZE(timberdale_cells_bar0_cfg3),
- &dev->resource[0], msix_entries[0].vector);
+ &dev->resource[0], msix_entries[0].vector, NULL);
break;
default:
dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
@@ -792,7 +792,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
err = mfd_add_devices(&dev->dev, 0,
timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
- &dev->resource[1], msix_entries[0].vector);
+ &dev->resource[1], msix_entries[0].vector, NULL);
if (err) {
dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
goto err_mfd2;
@@ -803,7 +803,7 @@ static int __devinit timb_probe(struct pci_dev *dev,
((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
ARRAY_SIZE(timberdale_cells_bar2),
- &dev->resource[2], msix_entries[0].vector);
+ &dev->resource[2], msix_entries[0].vector, NULL);
if (err) {
dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
goto err_mfd2;
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index a293b978e27c..14051bdc714b 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -188,7 +188,7 @@ static int __devinit tps6105x_probe(struct i2c_client *client,
}
ret = mfd_add_devices(&client->dev, 0, tps6105x_cells,
- ARRAY_SIZE(tps6105x_cells), NULL, 0);
+ ARRAY_SIZE(tps6105x_cells), NULL, 0, NULL);
if (ret)
goto fail;
diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c
index 93d5fdf020c7..da2691f22e11 100644
--- a/drivers/mfd/tps65010.c
+++ b/drivers/mfd/tps65010.c
@@ -563,8 +563,7 @@ static int tps65010_probe(struct i2c_client *client,
*/
if (client->irq > 0) {
status = request_irq(client->irq, tps65010_irq,
- IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
- DRIVER_NAME, tps);
+ IRQF_TRIGGER_FALLING, DRIVER_NAME, tps);
if (status < 0) {
dev_dbg(&client->dev, "can't get IRQ %d, err %d\n",
client->irq, status);
diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c
index 33ba7723c967..1b203499c744 100644
--- a/drivers/mfd/tps6507x.c
+++ b/drivers/mfd/tps6507x.c
@@ -100,7 +100,7 @@ static int tps6507x_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(tps6507x->dev, -1,
tps6507x_devs, ARRAY_SIZE(tps6507x_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (ret < 0)
goto err;
diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c
index 80e24f4b47bf..50fd87c87a1c 100644
--- a/drivers/mfd/tps65090.c
+++ b/drivers/mfd/tps65090.c
@@ -292,7 +292,7 @@ static int __devinit tps65090_i2c_probe(struct i2c_client *client,
}
ret = mfd_add_devices(tps65090->dev, -1, tps65090s,
- ARRAY_SIZE(tps65090s), NULL, 0);
+ ARRAY_SIZE(tps65090s), NULL, 0, NULL);
if (ret) {
dev_err(&client->dev, "add mfd devices failed with err: %d\n",
ret);
diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c
index 61c097a98f5d..a95e9421b735 100644
--- a/drivers/mfd/tps65217.c
+++ b/drivers/mfd/tps65217.c
@@ -24,11 +24,18 @@
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/err.h>
-#include <linux/regulator/of_regulator.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/mfd/core.h>
#include <linux/mfd/tps65217.h>
+static struct mfd_cell tps65217s[] = {
+ {
+ .name = "tps65217-pmic",
+ },
+};
+
/**
* tps65217_reg_read: Read a single tps65217 register.
*
@@ -133,83 +140,48 @@ int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg,
}
EXPORT_SYMBOL_GPL(tps65217_clear_bits);
-#ifdef CONFIG_OF
-static struct of_regulator_match reg_matches[] = {
- { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 },
- { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 },
- { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 },
- { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 },
- { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 },
- { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 },
- { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 },
-};
-
-static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client)
-{
- struct device_node *node = client->dev.of_node;
- struct tps65217_board *pdata;
- struct device_node *regs;
- int count = ARRAY_SIZE(reg_matches);
- int ret, i;
-
- regs = of_find_node_by_name(node, "regulators");
- if (!regs)
- return NULL;
-
- ret = of_regulator_match(&client->dev, regs, reg_matches, count);
- of_node_put(regs);
- if ((ret < 0) || (ret > count))
- return NULL;
-
- count = ret;
- pdata = devm_kzalloc(&client->dev, count * sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return NULL;
-
- for (i = 0; i < count; i++) {
- if (!reg_matches[i].init_data || !reg_matches[i].of_node)
- continue;
-
- pdata->tps65217_init_data[i] = reg_matches[i].init_data;
- pdata->of_node[i] = reg_matches[i].of_node;
- }
-
- return pdata;
-}
-
-static struct of_device_id tps65217_of_match[] = {
- { .compatible = "ti,tps65217", },
- { },
-};
-#else
-static struct tps65217_board *tps65217_parse_dt(struct i2c_client *client)
-{
- return NULL;
-}
-#endif
-
static struct regmap_config tps65217_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
+static const struct of_device_id tps65217_of_match[] = {
+ { .compatible = "ti,tps65217", .data = (void *)TPS65217 },
+ { /* sentinel */ },
+};
+
static int __devinit tps65217_probe(struct i2c_client *client,
const struct i2c_device_id *ids)
{
struct tps65217 *tps;
- struct regulator_init_data *reg_data;
- struct tps65217_board *pdata = client->dev.platform_data;
- int i, ret;
unsigned int version;
+ unsigned int chip_id = ids->driver_data;
+ const struct of_device_id *match;
+ int ret;
- if (!pdata && client->dev.of_node)
- pdata = tps65217_parse_dt(client);
+ if (client->dev.of_node) {
+ match = of_match_device(tps65217_of_match, &client->dev);
+ if (!match) {
+ dev_err(&client->dev,
+ "Failed to find matching dt id\n");
+ return -EINVAL;
+ }
+ chip_id = (unsigned int)match->data;
+ }
+
+ if (!chip_id) {
+ dev_err(&client->dev, "id is null.\n");
+ return -ENODEV;
+ }
tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
if (!tps)
return -ENOMEM;
- tps->pdata = pdata;
+ i2c_set_clientdata(client, tps);
+ tps->dev = &client->dev;
+ tps->id = chip_id;
+
tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config);
if (IS_ERR(tps->regmap)) {
ret = PTR_ERR(tps->regmap);
@@ -218,8 +190,12 @@ static int __devinit tps65217_probe(struct i2c_client *client,
return ret;
}
- i2c_set_clientdata(client, tps);
- tps->dev = &client->dev;
+ ret = mfd_add_devices(tps->dev, -1, tps65217s,
+ ARRAY_SIZE(tps65217s), NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(tps->dev, "mfd_add_devices failed: %d\n", ret);
+ return ret;
+ }
ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version);
if (ret < 0) {
@@ -232,41 +208,21 @@ static int __devinit tps65217_probe(struct i2c_client *client,
(version & TPS65217_CHIPID_CHIP_MASK) >> 4,
version & TPS65217_CHIPID_REV_MASK);
- for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
- struct platform_device *pdev;
-
- pdev = platform_device_alloc("tps65217-pmic", i);
- if (!pdev) {
- dev_err(tps->dev, "Cannot create regulator %d\n", i);
- continue;
- }
-
- pdev->dev.parent = tps->dev;
- pdev->dev.of_node = pdata->of_node[i];
- reg_data = pdata->tps65217_init_data[i];
- platform_device_add_data(pdev, reg_data, sizeof(*reg_data));
- tps->regulator_pdev[i] = pdev;
-
- platform_device_add(pdev);
- }
-
return 0;
}
static int __devexit tps65217_remove(struct i2c_client *client)
{
struct tps65217 *tps = i2c_get_clientdata(client);
- int i;
- for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
- platform_device_unregister(tps->regulator_pdev[i]);
+ mfd_remove_devices(tps->dev);
return 0;
}
static const struct i2c_device_id tps65217_id_table[] = {
- {"tps65217", 0xF0},
- {/* end of list */}
+ {"tps65217", TPS65217},
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tps65217_id_table);
diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c
index 353c34812120..5f58370ccf55 100644
--- a/drivers/mfd/tps6586x.c
+++ b/drivers/mfd/tps6586x.c
@@ -493,7 +493,8 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client,
}
ret = mfd_add_devices(tps6586x->dev, -1,
- tps6586x_cell, ARRAY_SIZE(tps6586x_cell), NULL, 0);
+ tps6586x_cell, ARRAY_SIZE(tps6586x_cell),
+ NULL, 0, NULL);
if (ret < 0) {
dev_err(&client->dev, "mfd_add_devices failed: %d\n", ret);
goto err_mfd_add;
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 1c563792c777..d3ce4d569deb 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -254,7 +254,7 @@ static __devinit int tps65910_i2c_probe(struct i2c_client *i2c,
ret = mfd_add_devices(tps65910->dev, -1,
tps65910s, ARRAY_SIZE(tps65910s),
- NULL, 0);
+ NULL, 0, NULL);
if (ret < 0) {
dev_err(&i2c->dev, "mfd_add_devices failed: %d\n", ret);
return ret;
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index 74fd8cb5f372..4658b5bdcd84 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -146,7 +146,7 @@ int tps65912_device_init(struct tps65912 *tps65912)
ret = mfd_add_devices(tps65912->dev, -1,
tps65912s, ARRAY_SIZE(tps65912s),
- NULL, 0);
+ NULL, 0, NULL);
if (ret < 0)
goto err;
diff --git a/drivers/mfd/twl4030-audio.c b/drivers/mfd/twl4030-audio.c
index 838ce4eb444e..77c9acb14583 100644
--- a/drivers/mfd/twl4030-audio.c
+++ b/drivers/mfd/twl4030-audio.c
@@ -223,7 +223,7 @@ static int __devinit twl4030_audio_probe(struct platform_device *pdev)
if (childs)
ret = mfd_add_devices(&pdev->dev, pdev->id, audio->cells,
- childs, NULL, 0);
+ childs, NULL, 0, NULL);
else {
dev_err(&pdev->dev, "No platform data found for childs\n");
ret = -ENODEV;
diff --git a/drivers/mfd/twl6040-core.c b/drivers/mfd/twl6040-core.c
index b0fad0ffca56..3dca5c195a20 100644
--- a/drivers/mfd/twl6040-core.c
+++ b/drivers/mfd/twl6040-core.c
@@ -632,7 +632,7 @@ static int __devinit twl6040_probe(struct i2c_client *client,
}
ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children,
- NULL, 0);
+ NULL, 0, NULL);
if (ret)
goto mfd_err;
diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c
index 872aff21e4be..b9a636d44c7f 100644
--- a/drivers/mfd/vx855.c
+++ b/drivers/mfd/vx855.c
@@ -102,7 +102,7 @@ static __devinit int vx855_probe(struct pci_dev *pdev,
vx855_gpio_resources[1].end = vx855_gpio_resources[1].start + 3;
ret = mfd_add_devices(&pdev->dev, -1, vx855_cells, ARRAY_SIZE(vx855_cells),
- NULL, 0);
+ NULL, 0, NULL);
/* we always return -ENODEV here in order to enable other
* drivers like old, not-yet-platform_device ported i2c-viapro */
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
index f39b756df561..86e0e4309fc2 100644
--- a/drivers/mfd/wl1273-core.c
+++ b/drivers/mfd/wl1273-core.c
@@ -241,7 +241,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client,
__func__, children);
r = mfd_add_devices(&client->dev, -1, core->cells,
- children, NULL, 0);
+ children, NULL, 0, NULL);
if (r)
goto err;
diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c
index 946698fd2dc6..301731035940 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -1813,27 +1813,27 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8310:
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8310_devs, ARRAY_SIZE(wm8310_devs),
- NULL, 0);
+ NULL, 0, NULL);
break;
case WM8311:
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8311_devs, ARRAY_SIZE(wm8311_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (!pdata || !pdata->disable_touch)
mfd_add_devices(wm831x->dev, wm831x_num,
touch_devs, ARRAY_SIZE(touch_devs),
- NULL, 0);
+ NULL, 0, NULL);
break;
case WM8312:
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8312_devs, ARRAY_SIZE(wm8312_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (!pdata || !pdata->disable_touch)
mfd_add_devices(wm831x->dev, wm831x_num,
touch_devs, ARRAY_SIZE(touch_devs),
- NULL, 0);
+ NULL, 0, NULL);
break;
case WM8320:
@@ -1842,7 +1842,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
case WM8326:
ret = mfd_add_devices(wm831x->dev, wm831x_num,
wm8320_devs, ARRAY_SIZE(wm8320_devs),
- NULL, 0);
+ NULL, 0, NULL);
break;
default:
@@ -1867,7 +1867,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
if (ret & WM831X_XTAL_ENA) {
ret = mfd_add_devices(wm831x->dev, wm831x_num,
rtc_devs, ARRAY_SIZE(rtc_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to add RTC: %d\n", ret);
goto err_irq;
@@ -1880,7 +1880,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
/* Treat errors as non-critical */
ret = mfd_add_devices(wm831x->dev, wm831x_num, backlight_devs,
ARRAY_SIZE(backlight_devs), NULL,
- 0);
+ 0, NULL);
if (ret < 0)
dev_err(wm831x->dev, "Failed to add backlight: %d\n",
ret);
diff --git a/drivers/mfd/wm831x-otp.c b/drivers/mfd/wm831x-otp.c
index f742745ff354..b90f3e06b6c9 100644
--- a/drivers/mfd/wm831x-otp.c
+++ b/drivers/mfd/wm831x-otp.c
@@ -18,6 +18,7 @@
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/mfd/core.h>
+#include <linux/random.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/otp.h>
@@ -66,6 +67,7 @@ static DEVICE_ATTR(unique_id, 0444, wm831x_unique_id_show, NULL);
int wm831x_otp_init(struct wm831x *wm831x)
{
+ char uuid[WM831X_UNIQUE_ID_LEN];
int ret;
ret = device_create_file(wm831x->dev, &dev_attr_unique_id);
@@ -73,6 +75,12 @@ int wm831x_otp_init(struct wm831x *wm831x)
dev_err(wm831x->dev, "Unique ID attribute not created: %d\n",
ret);
+ ret = wm831x_unique_id_read(wm831x, uuid);
+ if (ret == 0)
+ add_device_randomness(uuid, sizeof(uuid));
+ else
+ dev_err(wm831x->dev, "Failed to read UUID: %d\n", ret);
+
return ret;
}
diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c
index 4b7d378551d5..639ca359242f 100644
--- a/drivers/mfd/wm8400-core.c
+++ b/drivers/mfd/wm8400-core.c
@@ -70,7 +70,7 @@ static int wm8400_register_codec(struct wm8400 *wm8400)
.pdata_size = sizeof(*wm8400),
};
- return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0);
+ return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0, NULL);
}
/*
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index eec74aa55fdf..2febf88cfce8 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -414,7 +414,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
ret = mfd_add_devices(wm8994->dev, -1,
wm8994_regulator_devs,
ARRAY_SIZE(wm8994_regulator_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
goto err;
@@ -648,7 +648,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
ret = mfd_add_devices(wm8994->dev, -1,
wm8994_devs, ARRAY_SIZE(wm8994_devs),
- NULL, 0);
+ NULL, 0, NULL);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
goto err_irq;
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index c6ffbbe5a6c0..d78c05e693f7 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -1253,7 +1253,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
if (dev->wd_timeout)
*slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
else
- *slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
+ *slots -= mei_data2slots(MEI_WD_PARAMS_SIZE);
}
}
if (dev->stop)
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 092330208869..7422c7652845 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -925,6 +925,27 @@ static struct miscdevice mei_misc_device = {
};
/**
+ * mei_quirk_probe - probe for devices that doesn't valid ME interface
+ * @pdev: PCI device structure
+ * @ent: entry into pci_device_table
+ *
+ * returns true if ME Interface is valid, false otherwise
+ */
+static bool __devinit mei_quirk_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ u32 reg;
+ if (ent->device == MEI_DEV_ID_PBG_1) {
+ pci_read_config_dword(pdev, 0x48, &reg);
+ /* make sure that bit 9 is up and bit 10 is down */
+ if ((reg & 0x600) == 0x200) {
+ dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
+ return false;
+ }
+ }
+ return true;
+}
+/**
* mei_probe - Device Initialization Routine
*
* @pdev: PCI device structure
@@ -939,6 +960,12 @@ static int __devinit mei_probe(struct pci_dev *pdev,
int err;
mutex_lock(&mei_mutex);
+
+ if (!mei_quirk_probe(pdev, ent)) {
+ err = -ENODEV;
+ goto end;
+ }
+
if (mei_device) {
err = -EEXIST;
goto end;
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index 87b251ab6ec5..b9e2000969f0 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -18,6 +18,8 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <asm/uv/uv_hub.h>
@@ -59,6 +61,8 @@ static struct xpc_heartbeat_uv *xpc_heartbeat_uv;
XPC_NOTIFY_MSG_SIZE_UV)
#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
+static int xpc_mq_node = -1;
+
static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
@@ -109,11 +113,8 @@ xpc_get_gru_mq_irq_uv(struct xpc_gru_mq_uv *mq, int cpu, char *irq_name)
#if defined CONFIG_X86_64
mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
UV_AFFINITY_CPU);
- if (mq->irq < 0) {
- dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
- -mq->irq);
+ if (mq->irq < 0)
return mq->irq;
- }
mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset);
@@ -238,8 +239,9 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
mq->mmr_blade = uv_cpu_to_blade_id(cpu);
nid = cpu_to_node(cpu);
- page = alloc_pages_exact_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
- pg_order);
+ page = alloc_pages_exact_node(nid,
+ GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
+ pg_order);
if (page == NULL) {
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
"bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
@@ -1731,9 +1733,50 @@ static struct xpc_arch_operations xpc_arch_ops_uv = {
.notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv,
};
+static int
+xpc_init_mq_node(int nid)
+{
+ int cpu;
+
+ get_online_cpus();
+
+ for_each_cpu(cpu, cpumask_of_node(nid)) {
+ xpc_activate_mq_uv =
+ xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid,
+ XPC_ACTIVATE_IRQ_NAME,
+ xpc_handle_activate_IRQ_uv);
+ if (!IS_ERR(xpc_activate_mq_uv))
+ break;
+ }
+ if (IS_ERR(xpc_activate_mq_uv)) {
+ put_online_cpus();
+ return PTR_ERR(xpc_activate_mq_uv);
+ }
+
+ for_each_cpu(cpu, cpumask_of_node(nid)) {
+ xpc_notify_mq_uv =
+ xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid,
+ XPC_NOTIFY_IRQ_NAME,
+ xpc_handle_notify_IRQ_uv);
+ if (!IS_ERR(xpc_notify_mq_uv))
+ break;
+ }
+ if (IS_ERR(xpc_notify_mq_uv)) {
+ xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
+ put_online_cpus();
+ return PTR_ERR(xpc_notify_mq_uv);
+ }
+
+ put_online_cpus();
+ return 0;
+}
+
int
xpc_init_uv(void)
{
+ int nid;
+ int ret = 0;
+
xpc_arch_ops = xpc_arch_ops_uv;
if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) {
@@ -1742,21 +1785,21 @@ xpc_init_uv(void)
return -E2BIG;
}
- xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0,
- XPC_ACTIVATE_IRQ_NAME,
- xpc_handle_activate_IRQ_uv);
- if (IS_ERR(xpc_activate_mq_uv))
- return PTR_ERR(xpc_activate_mq_uv);
+ if (xpc_mq_node < 0)
+ for_each_online_node(nid) {
+ ret = xpc_init_mq_node(nid);
- xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0,
- XPC_NOTIFY_IRQ_NAME,
- xpc_handle_notify_IRQ_uv);
- if (IS_ERR(xpc_notify_mq_uv)) {
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
- return PTR_ERR(xpc_notify_mq_uv);
- }
+ if (!ret)
+ break;
+ }
+ else
+ ret = xpc_init_mq_node(xpc_mq_node);
- return 0;
+ if (ret < 0)
+ dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n",
+ -ret);
+
+ return ret;
}
void
@@ -1765,3 +1808,6 @@ xpc_exit_uv(void)
xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
}
+
+module_param(xpc_mq_node, int, 0);
+MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues.");
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 1ff460a8e9c7..93b4d67cc4a3 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -87,7 +87,7 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data)
/* communicate to platform about chip wakeup */
kim_data = st_data->kim_data;
pdata = kim_data->kim_pdev->dev.platform_data;
- if (pdata->chip_asleep)
+ if (pdata->chip_awake)
pdata->chip_awake(NULL);
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index f1c84decb192..172a768036d8 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1411,7 +1411,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
- if (req->cmd_flags & REQ_SECURE)
+ if (req->cmd_flags & REQ_SECURE &&
+ !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN))
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
@@ -1716,6 +1717,7 @@ force_ro_fail:
#define CID_MANFID_SANDISK 0x2
#define CID_MANFID_TOSHIBA 0x11
#define CID_MANFID_MICRON 0x13
+#define CID_MANFID_SAMSUNG 0x15
static const struct mmc_fixup blk_fixups[] =
{
@@ -1752,6 +1754,28 @@ static const struct mmc_fixup blk_fixups[] =
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME),
+ /*
+ * On these Samsung MoviNAND parts, performing secure erase or
+ * secure trim can result in unrecoverable corruption due to a
+ * firmware bug.
+ */
+ MMC_FIXUP("M8G2FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MAG4FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MBG8FA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("MCGAFA", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VAL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("KYL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+
END_FIXUP
};
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 322412cec4ee..a53c7c478e05 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -81,6 +81,7 @@ struct atmel_mci_caps {
bool has_bad_data_ordering;
bool need_reset_after_xfer;
bool need_blksz_mul_4;
+ bool need_notbusy_for_read_ops;
};
struct atmel_mci_dma {
@@ -1625,7 +1626,8 @@ static void atmci_tasklet_func(unsigned long priv)
__func__);
atmci_set_completed(host, EVENT_XFER_COMPLETE);
- if (host->data->flags & MMC_DATA_WRITE) {
+ if (host->caps.need_notbusy_for_read_ops ||
+ (host->data->flags & MMC_DATA_WRITE)) {
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
} else if (host->mrq->stop) {
@@ -2218,6 +2220,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
host->caps.has_bad_data_ordering = 1;
host->caps.need_reset_after_xfer = 1;
host->caps.need_blksz_mul_4 = 1;
+ host->caps.need_notbusy_for_read_ops = 0;
/* keep only major version number */
switch (version & 0xf00) {
@@ -2238,6 +2241,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
case 0x200:
host->caps.has_rwproof = 1;
host->caps.need_blksz_mul_4 = 0;
+ host->caps.need_notbusy_for_read_ops = 1;
case 0x100:
host->caps.has_bad_data_ordering = 0;
host->caps.need_reset_after_xfer = 0;
diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c
index 03666174ca48..a17dd7363ceb 100644
--- a/drivers/mmc/host/bfin_sdh.c
+++ b/drivers/mmc/host/bfin_sdh.c
@@ -49,13 +49,6 @@
#define bfin_write_SDH_CFG bfin_write_RSI_CFG
#endif
-struct dma_desc_array {
- unsigned long start_addr;
- unsigned short cfg;
- unsigned short x_count;
- short x_modify;
-} __packed;
-
struct sdh_host {
struct mmc_host *mmc;
spinlock_t lock;
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 72dc3cde646d..af40d227bece 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -627,6 +627,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
{
struct dw_mci *host = slot->host;
u32 div;
+ u32 clk_en_a;
if (slot->clock != host->current_speed) {
div = host->bus_hz / slot->clock;
@@ -659,9 +660,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
- /* enable clock */
- mci_writel(host, CLKENA, ((SDMMC_CLKEN_ENABLE |
- SDMMC_CLKEN_LOW_PWR) << slot->id));
+ /* enable clock; only low power if no SDIO */
+ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
+ if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id)))
+ clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id;
+ mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */
mci_send_cmd(slot,
@@ -862,6 +865,30 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
return present;
}
+/*
+ * Disable lower power mode.
+ *
+ * Low power mode will stop the card clock when idle. According to the
+ * description of the CLKENA register we should disable low power mode
+ * for SDIO cards if we need SDIO interrupts to work.
+ *
+ * This function is fast if low power mode is already disabled.
+ */
+static void dw_mci_disable_low_power(struct dw_mci_slot *slot)
+{
+ struct dw_mci *host = slot->host;
+ u32 clk_en_a;
+ const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id;
+
+ clk_en_a = mci_readl(host, CLKENA);
+
+ if (clk_en_a & clken_low_pwr) {
+ mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr);
+ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
+ SDMMC_CMD_PRV_DAT_WAIT, 0);
+ }
+}
+
static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
@@ -871,6 +898,14 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
/* Enable/disable Slot Specific SDIO interrupt */
int_mask = mci_readl(host, INTMASK);
if (enb) {
+ /*
+ * Turn off low power mode if it was enabled. This is a bit of
+ * a heavy operation and we disable / enable IRQs a lot, so
+ * we'll leave low power mode disabled and it will get
+ * re-enabled again in dw_mci_setup_bus().
+ */
+ dw_mci_disable_low_power(slot);
+
mci_writel(host, INTMASK,
(int_mask | SDMMC_INT_SDIO(slot->id)));
} else {
@@ -1429,22 +1464,10 @@ static void dw_mci_read_data_pio(struct dw_mci *host)
nbytes += len;
remain -= len;
} while (remain);
- sg_miter->consumed = offset;
+ sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_RXDR);
- if (status & DW_MCI_DATA_ERROR_FLAGS) {
- host->data_status = status;
- data->bytes_xfered += nbytes;
- sg_miter_stop(sg_miter);
- host->sg = NULL;
- smp_wmb();
-
- set_bit(EVENT_DATA_ERROR, &host->pending_events);
-
- tasklet_schedule(&host->tasklet);
- return;
- }
} while (status & SDMMC_INT_RXDR); /*if the RXDR is ready read again*/
data->bytes_xfered += nbytes;
@@ -1497,23 +1520,10 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
nbytes += len;
remain -= len;
} while (remain);
- sg_miter->consumed = offset;
+ sg_miter->consumed = offset;
status = mci_readl(host, MINTSTS);
mci_writel(host, RINTSTS, SDMMC_INT_TXDR);
- if (status & DW_MCI_DATA_ERROR_FLAGS) {
- host->data_status = status;
- data->bytes_xfered += nbytes;
- sg_miter_stop(sg_miter);
- host->sg = NULL;
-
- smp_wmb();
-
- set_bit(EVENT_DATA_ERROR, &host->pending_events);
-
- tasklet_schedule(&host->tasklet);
- return;
- }
} while (status & SDMMC_INT_TXDR); /* if TXDR write again */
data->bytes_xfered += nbytes;
@@ -1547,12 +1557,11 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
{
struct dw_mci *host = dev_id;
- u32 status, pending;
+ u32 pending;
unsigned int pass_count = 0;
int i;
do {
- status = mci_readl(host, RINTSTS);
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
/*
@@ -1570,7 +1579,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
- host->cmd_status = status;
+ host->cmd_status = pending;
smp_wmb();
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
}
@@ -1578,18 +1587,16 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & DW_MCI_DATA_ERROR_FLAGS) {
/* if there is an error report DATA_ERROR */
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
- host->data_status = status;
+ host->data_status = pending;
smp_wmb();
set_bit(EVENT_DATA_ERROR, &host->pending_events);
- if (!(pending & (SDMMC_INT_DTO | SDMMC_INT_DCRC |
- SDMMC_INT_SBE | SDMMC_INT_EBE)))
- tasklet_schedule(&host->tasklet);
+ tasklet_schedule(&host->tasklet);
}
if (pending & SDMMC_INT_DATA_OVER) {
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
if (!host->data_status)
- host->data_status = status;
+ host->data_status = pending;
smp_wmb();
if (host->dir_status == DW_MCI_RECV_STATUS) {
if (host->sg != NULL)
@@ -1613,7 +1620,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
if (pending & SDMMC_INT_CMD_DONE) {
mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE);
- dw_mci_cmd_interrupt(host, status);
+ dw_mci_cmd_interrupt(host, pending);
}
if (pending & SDMMC_INT_CD) {
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index a51f9309ffbb..ad3fcea1269e 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -285,11 +285,11 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
writel(stat & MXS_MMC_IRQ_BITS,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_CLR);
+ spin_unlock(&host->lock);
+
if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc);
- spin_unlock(&host->lock);
-
if (stat & BM_SSP_CTRL1_RESP_TIMEOUT_IRQ)
cmd->error = -ETIMEDOUT;
else if (stat & BM_SSP_CTRL1_RESP_ERR_IRQ)
@@ -644,11 +644,6 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET);
writel(BM_SSP_CTRL1_SDIO_IRQ_EN,
host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET);
-
- if (readl(host->base + HW_SSP_STATUS(host)) &
- BM_SSP_STATUS_SDIO_IRQ)
- mmc_signal_sdio_irq(host->mmc);
-
} else {
writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK,
host->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR);
@@ -657,6 +652,11 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
}
spin_unlock_irqrestore(&host->lock, flags);
+
+ if (enable && readl(host->base + HW_SSP_STATUS(host)) &
+ BM_SSP_STATUS_SDIO_IRQ)
+ mmc_signal_sdio_irq(host->mmc);
+
}
static const struct mmc_host_ops mxs_mmc_ops = {
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 3e8dcf8d2e05..a5999a74496a 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -17,10 +17,12 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
+#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/clk.h>
@@ -128,6 +130,10 @@ struct mmc_omap_host {
unsigned char id; /* 16xx chips have 2 MMC blocks */
struct clk * iclk;
struct clk * fclk;
+ struct dma_chan *dma_rx;
+ u32 dma_rx_burst;
+ struct dma_chan *dma_tx;
+ u32 dma_tx_burst;
struct resource *mem_res;
void __iomem *virt_base;
unsigned int phys_base;
@@ -153,12 +159,8 @@ struct mmc_omap_host {
unsigned use_dma:1;
unsigned brs_received:1, dma_done:1;
- unsigned dma_is_read:1;
unsigned dma_in_use:1;
- int dma_ch;
spinlock_t dma_lock;
- struct timer_list dma_timer;
- unsigned dma_len;
struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS];
struct mmc_omap_slot *current_slot;
@@ -406,18 +408,25 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
int abort)
{
enum dma_data_direction dma_data_dir;
+ struct device *dev = mmc_dev(host->mmc);
+ struct dma_chan *c;
- BUG_ON(host->dma_ch < 0);
- if (data->error)
- omap_stop_dma(host->dma_ch);
- /* Release DMA channel lazily */
- mod_timer(&host->dma_timer, jiffies + HZ);
- if (data->flags & MMC_DATA_WRITE)
+ if (data->flags & MMC_DATA_WRITE) {
dma_data_dir = DMA_TO_DEVICE;
- else
+ c = host->dma_tx;
+ } else {
dma_data_dir = DMA_FROM_DEVICE;
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
- dma_data_dir);
+ c = host->dma_rx;
+ }
+ if (c) {
+ if (data->error) {
+ dmaengine_terminate_all(c);
+ /* Claim nothing transferred on error... */
+ data->bytes_xfered = 0;
+ }
+ dev = c->device->dev;
+ }
+ dma_unmap_sg(dev, data->sg, host->sg_len, dma_data_dir);
}
static void mmc_omap_send_stop_work(struct work_struct *work)
@@ -525,16 +534,6 @@ mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
}
static void
-mmc_omap_dma_timer(unsigned long data)
-{
- struct mmc_omap_host *host = (struct mmc_omap_host *) data;
-
- BUG_ON(host->dma_ch < 0);
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
-}
-
-static void
mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
{
unsigned long flags;
@@ -669,7 +668,7 @@ mmc_omap_clk_timer(unsigned long data)
static void
mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
{
- int n;
+ int n, nwords;
if (host->buffer_bytes_left == 0) {
host->sg_idx++;
@@ -679,15 +678,23 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
n = 64;
if (n > host->buffer_bytes_left)
n = host->buffer_bytes_left;
+
+ nwords = n / 2;
+ nwords += n & 1; /* handle odd number of bytes to transfer */
+
host->buffer_bytes_left -= n;
host->total_bytes_left -= n;
host->data->bytes_xfered += n;
if (write) {
- __raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
+ __raw_writesw(host->virt_base + OMAP_MMC_REG(host, DATA),
+ host->buffer, nwords);
} else {
- __raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA), host->buffer, n);
+ __raw_readsw(host->virt_base + OMAP_MMC_REG(host, DATA),
+ host->buffer, nwords);
}
+
+ host->buffer += nwords;
}
static inline void mmc_omap_report_irq(u16 status)
@@ -891,159 +898,15 @@ static void mmc_omap_cover_handler(unsigned long param)
jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
}
-/* Prepare to transfer the next segment of a scatterlist */
-static void
-mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
-{
- int dma_ch = host->dma_ch;
- unsigned long data_addr;
- u16 buf, frame;
- u32 count;
- struct scatterlist *sg = &data->sg[host->sg_idx];
- int src_port = 0;
- int dst_port = 0;
- int sync_dev = 0;
-
- data_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
- frame = data->blksz;
- count = sg_dma_len(sg);
-
- if ((data->blocks == 1) && (count > data->blksz))
- count = frame;
-
- host->dma_len = count;
-
- /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
- * Use 16 or 32 word frames when the blocksize is at least that large.
- * Blocksize is usually 512 bytes; but not for some SD reads.
- */
- if (cpu_is_omap15xx() && frame > 32)
- frame = 32;
- else if (frame > 64)
- frame = 64;
- count /= frame;
- frame >>= 1;
-
- if (!(data->flags & MMC_DATA_WRITE)) {
- buf = 0x800f | ((frame - 1) << 8);
-
- if (cpu_class_is_omap1()) {
- src_port = OMAP_DMA_PORT_TIPB;
- dst_port = OMAP_DMA_PORT_EMIFF;
- }
- if (cpu_is_omap24xx())
- sync_dev = OMAP24XX_DMA_MMC1_RX;
-
- omap_set_dma_src_params(dma_ch, src_port,
- OMAP_DMA_AMODE_CONSTANT,
- data_addr, 0, 0);
- omap_set_dma_dest_params(dma_ch, dst_port,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sg), 0, 0);
- omap_set_dma_dest_data_pack(dma_ch, 1);
- omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
- } else {
- buf = 0x0f80 | ((frame - 1) << 0);
-
- if (cpu_class_is_omap1()) {
- src_port = OMAP_DMA_PORT_EMIFF;
- dst_port = OMAP_DMA_PORT_TIPB;
- }
- if (cpu_is_omap24xx())
- sync_dev = OMAP24XX_DMA_MMC1_TX;
-
- omap_set_dma_dest_params(dma_ch, dst_port,
- OMAP_DMA_AMODE_CONSTANT,
- data_addr, 0, 0);
- omap_set_dma_src_params(dma_ch, src_port,
- OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sg), 0, 0);
- omap_set_dma_src_data_pack(dma_ch, 1);
- omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
- }
-
- /* Max limit for DMA frame count is 0xffff */
- BUG_ON(count > 0xffff);
-
- OMAP_MMC_WRITE(host, BUF, buf);
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
- frame, count, OMAP_DMA_SYNC_FRAME,
- sync_dev, 0);
-}
-
-/* A scatterlist segment completed */
-static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+static void mmc_omap_dma_callback(void *priv)
{
- struct mmc_omap_host *host = (struct mmc_omap_host *) data;
- struct mmc_data *mmcdat = host->data;
+ struct mmc_omap_host *host = priv;
+ struct mmc_data *data = host->data;
- if (unlikely(host->dma_ch < 0)) {
- dev_err(mmc_dev(host->mmc),
- "DMA callback while DMA not enabled\n");
- return;
- }
- /* FIXME: We really should do something to _handle_ the errors */
- if (ch_status & OMAP1_DMA_TOUT_IRQ) {
- dev_err(mmc_dev(host->mmc),"DMA timeout\n");
- return;
- }
- if (ch_status & OMAP_DMA_DROP_IRQ) {
- dev_err(mmc_dev(host->mmc), "DMA sync error\n");
- return;
- }
- if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
- return;
- }
- mmcdat->bytes_xfered += host->dma_len;
- host->sg_idx++;
- if (host->sg_idx < host->sg_len) {
- mmc_omap_prepare_dma(host, host->data);
- omap_start_dma(host->dma_ch);
- } else
- mmc_omap_dma_done(host, host->data);
-}
-
-static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
-{
- const char *dma_dev_name;
- int sync_dev, dma_ch, is_read, r;
-
- is_read = !(data->flags & MMC_DATA_WRITE);
- del_timer_sync(&host->dma_timer);
- if (host->dma_ch >= 0) {
- if (is_read == host->dma_is_read)
- return 0;
- omap_free_dma(host->dma_ch);
- host->dma_ch = -1;
- }
-
- if (is_read) {
- if (host->id == 0) {
- sync_dev = OMAP_DMA_MMC_RX;
- dma_dev_name = "MMC1 read";
- } else {
- sync_dev = OMAP_DMA_MMC2_RX;
- dma_dev_name = "MMC2 read";
- }
- } else {
- if (host->id == 0) {
- sync_dev = OMAP_DMA_MMC_TX;
- dma_dev_name = "MMC1 write";
- } else {
- sync_dev = OMAP_DMA_MMC2_TX;
- dma_dev_name = "MMC2 write";
- }
- }
- r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb,
- host, &dma_ch);
- if (r != 0) {
- dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
- return r;
- }
- host->dma_ch = dma_ch;
- host->dma_is_read = is_read;
+ /* If we got to the end of DMA, assume everything went well */
+ data->bytes_xfered += data->blocks * data->blksz;
- return 0;
+ mmc_omap_dma_done(host, data);
}
static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
@@ -1118,33 +981,85 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
host->sg_idx = 0;
if (use_dma) {
- if (mmc_omap_get_dma_channel(host, data) == 0) {
- enum dma_data_direction dma_data_dir;
-
- if (data->flags & MMC_DATA_WRITE)
- dma_data_dir = DMA_TO_DEVICE;
- else
- dma_data_dir = DMA_FROM_DEVICE;
-
- host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- sg_len, dma_data_dir);
- host->total_bytes_left = 0;
- mmc_omap_prepare_dma(host, req->data);
- host->brs_received = 0;
- host->dma_done = 0;
- host->dma_in_use = 1;
- } else
- use_dma = 0;
+ enum dma_data_direction dma_data_dir;
+ struct dma_async_tx_descriptor *tx;
+ struct dma_chan *c;
+ u32 burst, *bp;
+ u16 buf;
+
+ /*
+ * FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx
+ * and 24xx. Use 16 or 32 word frames when the
+ * blocksize is at least that large. Blocksize is
+ * usually 512 bytes; but not for some SD reads.
+ */
+ burst = cpu_is_omap15xx() ? 32 : 64;
+ if (burst > data->blksz)
+ burst = data->blksz;
+
+ burst >>= 1;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ c = host->dma_tx;
+ bp = &host->dma_tx_burst;
+ buf = 0x0f80 | (burst - 1) << 0;
+ dma_data_dir = DMA_TO_DEVICE;
+ } else {
+ c = host->dma_rx;
+ bp = &host->dma_rx_burst;
+ buf = 0x800f | (burst - 1) << 8;
+ dma_data_dir = DMA_FROM_DEVICE;
+ }
+
+ if (!c)
+ goto use_pio;
+
+ /* Only reconfigure if we have a different burst size */
+ if (*bp != burst) {
+ struct dma_slave_config cfg;
+
+ cfg.src_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+ cfg.dst_addr = host->phys_base + OMAP_MMC_REG(host, DATA);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ cfg.src_maxburst = burst;
+ cfg.dst_maxburst = burst;
+
+ if (dmaengine_slave_config(c, &cfg))
+ goto use_pio;
+
+ *bp = burst;
+ }
+
+ host->sg_len = dma_map_sg(c->device->dev, data->sg, sg_len,
+ dma_data_dir);
+ if (host->sg_len == 0)
+ goto use_pio;
+
+ tx = dmaengine_prep_slave_sg(c, data->sg, host->sg_len,
+ data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx)
+ goto use_pio;
+
+ OMAP_MMC_WRITE(host, BUF, buf);
+
+ tx->callback = mmc_omap_dma_callback;
+ tx->callback_param = host;
+ dmaengine_submit(tx);
+ host->brs_received = 0;
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+ return;
}
+ use_pio:
/* Revert to PIO? */
- if (!use_dma) {
- OMAP_MMC_WRITE(host, BUF, 0x1f1f);
- host->total_bytes_left = data->blocks * block_size;
- host->sg_len = sg_len;
- mmc_omap_sg_to_buf(host);
- host->dma_in_use = 0;
- }
+ OMAP_MMC_WRITE(host, BUF, 0x1f1f);
+ host->total_bytes_left = data->blocks * block_size;
+ host->sg_len = sg_len;
+ mmc_omap_sg_to_buf(host);
+ host->dma_in_use = 0;
}
static void mmc_omap_start_request(struct mmc_omap_host *host,
@@ -1157,8 +1072,12 @@ static void mmc_omap_start_request(struct mmc_omap_host *host,
/* only touch fifo AFTER the controller readies it */
mmc_omap_prepare_data(host, req);
mmc_omap_start_command(host, req->cmd);
- if (host->dma_in_use)
- omap_start_dma(host->dma_ch);
+ if (host->dma_in_use) {
+ struct dma_chan *c = host->data->flags & MMC_DATA_WRITE ?
+ host->dma_tx : host->dma_rx;
+
+ dma_async_issue_pending(c);
+ }
}
static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
@@ -1400,6 +1319,8 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_omap_host *host = NULL;
struct resource *res;
+ dma_cap_mask_t mask;
+ unsigned sig;
int i, ret = 0;
int irq;
@@ -1439,7 +1360,6 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
spin_lock_init(&host->dma_lock);
- setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
spin_lock_init(&host->slot_lock);
init_waitqueue_head(&host->slot_wq);
@@ -1450,11 +1370,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
host->id = pdev->id;
host->mem_res = res;
host->irq = irq;
-
host->use_dma = 1;
- host->dev->dma_mask = &pdata->dma_mask;
- host->dma_ch = -1;
-
host->irq = irq;
host->phys_base = host->mem_res->start;
host->virt_base = ioremap(res->start, resource_size(res));
@@ -1474,9 +1390,48 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev)
goto err_free_iclk;
}
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ host->dma_tx_burst = -1;
+ host->dma_rx_burst = -1;
+
+ if (cpu_is_omap24xx())
+ sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX;
+ else
+ sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
+ host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+ if (!host->dma_tx) {
+ dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
+ sig);
+ goto err_dma;
+ }
+#else
+ if (!host->dma_tx)
+ dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
+ sig);
+#endif
+ if (cpu_is_omap24xx())
+ sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
+ else
+ sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
+ host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+#if 0
+ if (!host->dma_rx) {
+ dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
+ sig);
+ goto err_dma;
+ }
+#else
+ if (!host->dma_rx)
+ dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
+ sig);
+#endif
+
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
if (ret)
- goto err_free_fclk;
+ goto err_free_dma;
if (pdata->init != NULL) {
ret = pdata->init(&pdev->dev);
@@ -1510,7 +1465,11 @@ err_plat_cleanup:
pdata->cleanup(&pdev->dev);
err_free_irq:
free_irq(host->irq, host);
-err_free_fclk:
+err_free_dma:
+ if (host->dma_tx)
+ dma_release_channel(host->dma_tx);
+ if (host->dma_rx)
+ dma_release_channel(host->dma_rx);
clk_put(host->fclk);
err_free_iclk:
clk_disable(host->iclk);
@@ -1545,6 +1504,11 @@ static int __devexit mmc_omap_remove(struct platform_device *pdev)
clk_disable(host->iclk);
clk_put(host->iclk);
+ if (host->dma_tx)
+ dma_release_channel(host->dma_tx);
+ if (host->dma_rx)
+ dma_release_channel(host->dma_rx);
+
iounmap(host->virt_base);
release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index bc28627af66b..3a09f93cc3b6 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
+#include <linux/dmaengine.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
@@ -29,6 +30,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
@@ -37,7 +39,6 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
-#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
#include <plat/mmc.h>
@@ -166,7 +167,8 @@ struct omap_hsmmc_host {
int suspended;
int irq;
int use_dma, dma_ch;
- int dma_line_tx, dma_line_rx;
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
int slot_id;
int response_busy;
int context_loss;
@@ -797,6 +799,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
return DMA_FROM_DEVICE;
}
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+ struct mmc_data *data)
+{
+ return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
{
int dma_ch;
@@ -889,10 +897,13 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
spin_unlock_irqrestore(&host->irq_lock, flags);
if (host->use_dma && dma_ch != -1) {
- dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
- host->data->sg_len,
+ struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
+
+ dmaengine_terminate_all(chan);
+ dma_unmap_sg(chan->device->dev,
+ host->data->sg, host->data->sg_len,
omap_hsmmc_get_dma_dir(host, host->data));
- omap_free_dma(dma_ch);
+
host->data->host_cookie = 0;
}
host->data = NULL;
@@ -1190,90 +1201,29 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
- struct mmc_data *data)
-{
- int sync_dev;
-
- if (data->flags & MMC_DATA_WRITE)
- sync_dev = host->dma_line_tx;
- else
- sync_dev = host->dma_line_rx;
- return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
- struct mmc_data *data,
- struct scatterlist *sgl)
-{
- int blksz, nblk, dma_ch;
-
- dma_ch = host->dma_ch;
- if (data->flags & MMC_DATA_WRITE) {
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- } else {
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- }
-
- blksz = host->data->blksz;
- nblk = sg_dma_len(sgl) / blksz;
-
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
- blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
- omap_hsmmc_get_dma_sync_dev(host, data),
- !(data->flags & MMC_DATA_WRITE));
-
- omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
+static void omap_hsmmc_dma_callback(void *param)
{
- struct omap_hsmmc_host *host = cb_data;
+ struct omap_hsmmc_host *host = param;
+ struct dma_chan *chan;
struct mmc_data *data;
- int dma_ch, req_in_progress;
- unsigned long flags;
-
- if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
- dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
- ch_status);
- return;
- }
+ int req_in_progress;
- spin_lock_irqsave(&host->irq_lock, flags);
+ spin_lock_irq(&host->irq_lock);
if (host->dma_ch < 0) {
- spin_unlock_irqrestore(&host->irq_lock, flags);
+ spin_unlock_irq(&host->irq_lock);
return;
}
data = host->mrq->data;
- host->dma_sg_idx++;
- if (host->dma_sg_idx < host->dma_len) {
- /* Fire up the next transfer. */
- omap_hsmmc_config_dma_params(host, data,
- data->sg + host->dma_sg_idx);
- spin_unlock_irqrestore(&host->irq_lock, flags);
- return;
- }
-
+ chan = omap_hsmmc_get_dma_chan(host, data);
if (!data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ dma_unmap_sg(chan->device->dev,
+ data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
req_in_progress = host->req_in_progress;
- dma_ch = host->dma_ch;
host->dma_ch = -1;
- spin_unlock_irqrestore(&host->irq_lock, flags);
-
- omap_free_dma(dma_ch);
+ spin_unlock_irq(&host->irq_lock);
/* If DMA has finished after TC, complete the request */
if (!req_in_progress) {
@@ -1286,7 +1236,8 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_data *data,
- struct omap_hsmmc_next *next)
+ struct omap_hsmmc_next *next,
+ struct dma_chan *chan)
{
int dma_len;
@@ -1301,8 +1252,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
/* Check if next job is already prepared */
if (next ||
(!next && data->host_cookie != host->next_data.cookie)) {
- dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
+ dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
} else {
@@ -1329,8 +1279,11 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
- int dma_ch = 0, ret = 0, i;
+ struct dma_slave_config cfg;
+ struct dma_async_tx_descriptor *tx;
+ int ret = 0, i;
struct mmc_data *data = req->data;
+ struct dma_chan *chan;
/* Sanity check: all the SG entries must be aligned by block size. */
for (i = 0; i < data->sg_len; i++) {
@@ -1348,22 +1301,41 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
BUG_ON(host->dma_ch != -1);
- ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
- "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
- if (ret != 0) {
- dev_err(mmc_dev(host->mmc),
- "%s: omap_request_dma() failed with %d\n",
- mmc_hostname(host->mmc), ret);
+ chan = omap_hsmmc_get_dma_chan(host, data);
+
+ cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = data->blksz / 4;
+ cfg.dst_maxburst = data->blksz / 4;
+
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret)
return ret;
- }
- ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
+
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
if (ret)
return ret;
- host->dma_ch = dma_ch;
- host->dma_sg_idx = 0;
+ tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+ data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx) {
+ dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+ /* FIXME: cleanup */
+ return -1;
+ }
+
+ tx->callback = omap_hsmmc_dma_callback;
+ tx->callback_param = host;
- omap_hsmmc_config_dma_params(host, data, data->sg);
+ /* Does not fail */
+ dmaengine_submit(tx);
+
+ host->dma_ch = 1;
+
+ dma_async_issue_pending(chan);
return 0;
}
@@ -1445,11 +1417,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- if (host->use_dma) {
- if (data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
- omap_hsmmc_get_dma_dir(host, data));
+ if (host->use_dma && data->host_cookie) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+
+ dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
data->host_cookie = 0;
}
}
@@ -1464,10 +1436,13 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
return ;
}
- if (host->use_dma)
+ if (host->use_dma) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+
if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
- &host->next_data))
+ &host->next_data, c))
mrq->data->host_cookie = 0;
+ }
}
/*
@@ -1800,6 +1775,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
struct resource *res;
int ret, irq;
const struct of_device_id *match;
+ dma_cap_mask_t mask;
+ unsigned tx_req, rx_req;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -1844,7 +1821,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
host->pdata = pdata;
host->dev = &pdev->dev;
host->use_dma = 1;
- host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
host->slot_id = 0;
@@ -1934,7 +1910,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = -ENXIO;
goto err_irq;
}
- host->dma_line_tx = res->start;
+ tx_req = res->start;
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
if (!res) {
@@ -1942,7 +1918,24 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
ret = -ENXIO;
goto err_irq;
}
- host->dma_line_rx = res->start;
+ rx_req = res->start;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req);
+ if (!host->rx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+ ret = -ENXIO;
+ goto err_irq;
+ }
+
+ host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+ if (!host->tx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+ ret = -ENXIO;
+ goto err_irq;
+ }
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
@@ -2021,6 +2014,10 @@ err_reg:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
@@ -2056,6 +2053,11 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
+
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index b97b2f5dafdb..d25f9ab9a54d 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -48,14 +48,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
int div = 1;
u32 temp;
+ if (clock == 0)
+ goto out;
+
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
- if (clock == 0)
- goto out;
-
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index cfff454f628b..c3bb304eca07 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -19,14 +19,13 @@
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
+#include <asm/sections.h>
/****************************************************************************/
-extern char _ebss;
-
struct map_info uclinux_ram_map = {
.name = "RAM",
- .phys = (unsigned long)&_ebss,
+ .phys = (unsigned long)__bss_stop,
.size = 0,
};
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index f2f482bec573..a6e74514e662 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1123,6 +1123,33 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
}
#endif
+static inline unsigned long get_vm_size(struct vm_area_struct *vma)
+{
+ return vma->vm_end - vma->vm_start;
+}
+
+static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
+{
+ return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
+}
+
+/*
+ * Set a new vm offset.
+ *
+ * Verify that the incoming offset really works as a page offset,
+ * and that the offset and size fit in a resource_size_t.
+ */
+static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
+{
+ pgoff_t pgoff = off >> PAGE_SHIFT;
+ if (off != (resource_size_t) pgoff << PAGE_SHIFT)
+ return -EINVAL;
+ if (off + get_vm_size(vma) - 1 < off)
+ return -EINVAL;
+ vma->vm_pgoff = pgoff;
+ return 0;
+}
+
/*
* set up a mapping for shared memory segments
*/
@@ -1132,20 +1159,29 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
- unsigned long start;
- unsigned long off;
- u32 len;
+ resource_size_t start, off;
+ unsigned long len, vma_len;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
- off = vma->vm_pgoff << PAGE_SHIFT;
+ off = get_vm_offset(vma);
start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
+ vma_len = get_vm_size(vma);
+
+ /* Overflow in off+len? */
+ if (vma_len + off < off)
+ return -EINVAL;
+ /* Does it fit in the mapping? */
+ if (vma_len + off > len)
return -EINVAL;
off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
+ /* Did that overflow? */
+ if (off < start)
+ return -EINVAL;
+ if (set_vm_offset(vma, off) < 0)
+ return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED;
#ifdef pgprot_noncached
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 31bb7e5b504a..8ca417614c57 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -480,7 +480,7 @@ config MTD_NAND_NANDSIM
config MTD_NAND_GPMI_NAND
bool "GPMI NAND Flash Controller driver"
- depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q)
+ depends on MTD_NAND && MXS_DMA
help
Enables NAND Flash support for IMX23 or IMX28.
The GPMI controller is very powerful, with the help of BCH
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index d7f681d0c9b9..ac4fd756eda3 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -9,6 +9,7 @@
*/
#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/module.h>
@@ -18,6 +19,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/omap-dma.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -123,7 +125,7 @@ struct omap_nand_info {
int gpmc_cs;
unsigned long phys_base;
struct completion comp;
- int dma_ch;
+ struct dma_chan *dma;
int gpmc_irq;
enum {
OMAP_NAND_IO_READ = 0, /* read */
@@ -336,12 +338,10 @@ static void omap_write_buf_pref(struct mtd_info *mtd,
}
/*
- * omap_nand_dma_cb: callback on the completion of dma transfer
- * @lch: logical channel
- * @ch_satuts: channel status
+ * omap_nand_dma_callback: callback on the completion of dma transfer
* @data: pointer to completion data structure
*/
-static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_nand_dma_callback(void *data)
{
complete((struct completion *) data);
}
@@ -358,17 +358,13 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
{
struct omap_nand_info *info = container_of(mtd,
struct omap_nand_info, mtd);
+ struct dma_async_tx_descriptor *tx;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
- dma_addr_t dma_addr;
- int ret;
+ struct scatterlist sg;
unsigned long tim, limit;
-
- /* The fifo depth is 64 bytes max.
- * But configure the FIFO-threahold to 32 to get a sync at each frame
- * and frame length is 32 bytes.
- */
- int buf_len = len >> 6;
+ unsigned n;
+ int ret;
if (addr >= high_memory) {
struct page *p1;
@@ -382,40 +378,33 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
}
- dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
- if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
+ sg_init_one(&sg, addr, len);
+ n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
+ if (n == 0) {
dev_err(&info->pdev->dev,
"Couldn't DMA map a %d byte buffer\n", len);
goto out_copy;
}
- if (is_write) {
- omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- info->phys_base, 0, 0);
- omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dma_addr, 0, 0);
- omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
- OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
- } else {
- omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- info->phys_base, 0, 0);
- omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- dma_addr, 0, 0);
- omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
- 0x10, buf_len, OMAP_DMA_SYNC_FRAME,
- OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
- }
- /* configure and start prefetch transfer */
+ tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
+ is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx)
+ goto out_copy_unmap;
+
+ tx->callback = omap_nand_dma_callback;
+ tx->callback_param = &info->comp;
+ dmaengine_submit(tx);
+
+ /* configure and start prefetch transfer */
ret = gpmc_prefetch_enable(info->gpmc_cs,
- PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
+ PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
if (ret)
/* PFPW engine is busy, use cpu copy method */
goto out_copy_unmap;
init_completion(&info->comp);
-
- omap_start_dma(info->dma_ch);
+ dma_async_issue_pending(info->dma);
/* setup and start DMA using dma_addr */
wait_for_completion(&info->comp);
@@ -427,11 +416,11 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
/* disable and stop the PFPW engine */
gpmc_prefetch_reset(info->gpmc_cs);
- dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
return 0;
out_copy_unmap:
- dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+ dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
out_copy:
if (info->nand.options & NAND_BUSWIDTH_16)
is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
@@ -1164,6 +1153,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
struct omap_nand_platform_data *pdata;
int err;
int i, offset;
+ dma_cap_mask_t mask;
+ unsigned sig;
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
@@ -1244,18 +1235,30 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
break;
case NAND_OMAP_PREFETCH_DMA:
- err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
- omap_nand_dma_cb, &info->comp, &info->dma_ch);
- if (err < 0) {
- info->dma_ch = -1;
- dev_err(&pdev->dev, "DMA request failed!\n");
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ sig = OMAP24XX_DMA_GPMC;
+ info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!info->dma) {
+ dev_err(&pdev->dev, "DMA engine request failed\n");
+ err = -ENXIO;
goto out_release_mem_region;
} else {
- omap_set_dma_dest_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
- omap_set_dma_src_burst_mode(info->dma_ch,
- OMAP_DMA_DATA_BURST_16);
-
+ struct dma_slave_config cfg;
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = info->phys_base;
+ cfg.dst_addr = info->phys_base;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = 16;
+ cfg.dst_maxburst = 16;
+ err = dmaengine_slave_config(info->dma, &cfg);
+ if (err) {
+ dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
+ err);
+ goto out_release_mem_region;
+ }
info->nand.read_buf = omap_read_buf_dma_pref;
info->nand.write_buf = omap_write_buf_dma_pref;
}
@@ -1358,6 +1361,8 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
return 0;
out_release_mem_region:
+ if (info->dma)
+ dma_release_channel(info->dma);
release_mem_region(info->phys_base, NAND_IO_SIZE);
out_free_info:
kfree(info);
@@ -1373,8 +1378,8 @@ static int omap_nand_remove(struct platform_device *pdev)
omap3_free_bch(&info->mtd);
platform_set_drvdata(pdev, NULL);
- if (info->dma_ch != -1)
- omap_free_dma(info->dma_ch);
+ if (info->dma)
+ dma_release_channel(info->dma);
if (info->gpmc_irq)
free_irq(info->gpmc_irq, info);
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index 437bc193e170..568307cc7caf 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -340,7 +340,7 @@ retry:
* of this LEB as it will be deleted and freed in 'ubi_add_to_av()'.
*/
err = ubi_add_to_av(ubi, ai, new_aeb->pnum, new_aeb->ec, vid_hdr, 0);
- kfree(new_aeb);
+ kmem_cache_free(ai->aeb_slab_cache, new_aeb);
ubi_free_vid_hdr(ubi, vid_hdr);
return err;
@@ -353,7 +353,7 @@ write_error:
list_add(&new_aeb->u.list, &ai->erase);
goto retry;
}
- kfree(new_aeb);
+ kmem_cache_free(ai->aeb_slab_cache, new_aeb);
out_free:
ubi_free_vid_hdr(ubi, vid_hdr);
return err;
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 545c09ed9079..cff6f023c03a 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -996,9 +996,7 @@ static int __init cops_module_init(void)
printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
cardname);
cops_dev = cops_probe(-1);
- if (IS_ERR(cops_dev))
- return PTR_ERR(cops_dev);
- return 0;
+ return PTR_RET(cops_dev);
}
static void __exit cops_module_exit(void)
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 0910dce3996d..b5782cdf0bca 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -1243,9 +1243,7 @@ static int __init ltpc_module_init(void)
"ltpc: Autoprobing is not recommended for modules\n");
dev_ltpc = ltpc_probe();
- if (IS_ERR(dev_ltpc))
- return PTR_ERR(dev_ltpc);
- return 0;
+ return PTR_RET(dev_ltpc);
}
module_init(ltpc_module_init);
#endif
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 6fae5f3ec7f6..d688a8af432c 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -398,7 +398,7 @@ int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping;
- if (unlikely(netpoll_tx_running(slave_dev)))
+ if (unlikely(netpoll_tx_running(bond->dev)))
bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb);
else
dev_queue_xmit(skb);
@@ -1235,12 +1235,12 @@ static inline int slave_enable_netpoll(struct slave *slave)
struct netpoll *np;
int err = 0;
- np = kzalloc(sizeof(*np), GFP_KERNEL);
+ np = kzalloc(sizeof(*np), GFP_ATOMIC);
err = -ENOMEM;
if (!np)
goto out;
- err = __netpoll_setup(np, slave->dev);
+ err = __netpoll_setup(np, slave->dev, GFP_ATOMIC);
if (err) {
kfree(np);
goto out;
@@ -1257,9 +1257,7 @@ static inline void slave_disable_netpoll(struct slave *slave)
return;
slave->np = NULL;
- synchronize_rcu_bh();
- __netpoll_cleanup(np);
- kfree(np);
+ __netpoll_free_rcu(np);
}
static inline bool slave_dev_support_netpoll(struct net_device *slave_dev)
{
@@ -1292,7 +1290,7 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev)
read_unlock(&bond->lock);
}
-static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
+static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp)
{
struct bonding *bond = netdev_priv(dev);
struct slave *slave;
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 98ee43819911..7edadee487ba 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1391,7 +1391,6 @@ static irqreturn_t ican3_irq(int irq, void *dev_id)
*/
static int ican3_reset_module(struct ican3_dev *mod)
{
- u8 val = 1 << mod->num;
unsigned long start;
u8 runold, runnew;
@@ -1405,8 +1404,7 @@ static int ican3_reset_module(struct ican3_dev *mod)
runold = ioread8(mod->dpm + TARGET_RUNNING);
/* reset the module */
- iowrite8(val, &mod->ctrl->reset_assert);
- iowrite8(val, &mod->ctrl->reset_deassert);
+ iowrite8(0x00, &mod->dpmctrl->hwreset);
/* wait until the module has finished resetting and is running */
start = jiffies;
diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c
index a580db29e503..26e7129332ab 100644
--- a/drivers/net/can/mcp251x.c
+++ b/drivers/net/can/mcp251x.c
@@ -83,6 +83,11 @@
#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n))
#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94)
#define INSTRUCTION_RESET 0xC0
+#define RTS_TXB0 0x01
+#define RTS_TXB1 0x02
+#define RTS_TXB2 0x04
+#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07))
+
/* MPC251x registers */
#define CANSTAT 0x0e
@@ -397,6 +402,7 @@ static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf,
static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
int tx_buf_idx)
{
+ struct mcp251x_priv *priv = dev_get_drvdata(&spi->dev);
u32 sid, eid, exide, rtr;
u8 buf[SPI_TRANSFER_BUF_LEN];
@@ -418,7 +424,10 @@ static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame,
buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->can_dlc;
memcpy(buf + TXBDAT_OFF, frame->data, frame->can_dlc);
mcp251x_hw_tx_frame(spi, buf, frame->can_dlc, tx_buf_idx);
- mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx), TXBCTRL_TXREQ);
+
+ /* use INSTRUCTION_RTS, to avoid "repeated frame problem" */
+ priv->spi_tx_buf[0] = INSTRUCTION_RTS(1 << tx_buf_idx);
+ mcp251x_spi_trans(priv->spi, 1);
}
static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf,
diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c
index 4f50145f6483..662c5f7eb0c5 100644
--- a/drivers/net/can/sja1000/sja1000_platform.c
+++ b/drivers/net/can/sja1000/sja1000_platform.c
@@ -109,7 +109,9 @@ static int sp_probe(struct platform_device *pdev)
priv = netdev_priv(dev);
dev->irq = res_irq->start;
- priv->irq_flags = res_irq->flags & (IRQF_TRIGGER_MASK | IRQF_SHARED);
+ priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
+ if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
+ priv->irq_flags |= IRQF_SHARED;
priv->reg_base = addr;
/* The CAN clock frequency is half the oscillator clock frequency */
priv->can.clock.freq = pdata->osc_freq / 2;
diff --git a/drivers/net/can/softing/softing_fw.c b/drivers/net/can/softing/softing_fw.c
index 310596175676..b595d3422b9f 100644
--- a/drivers/net/can/softing/softing_fw.c
+++ b/drivers/net/can/softing/softing_fw.c
@@ -150,7 +150,7 @@ int softing_load_fw(const char *file, struct softing *card,
const uint8_t *mem, *end, *dat;
uint16_t type, len;
uint32_t addr;
- uint8_t *buf = NULL;
+ uint8_t *buf = NULL, *new_buf;
int buflen = 0;
int8_t type_end = 0;
@@ -199,11 +199,12 @@ int softing_load_fw(const char *file, struct softing *card,
if (len > buflen) {
/* align buflen */
buflen = (len + (1024-1)) & ~(1024-1);
- buf = krealloc(buf, buflen, GFP_KERNEL);
- if (!buf) {
+ new_buf = krealloc(buf, buflen, GFP_KERNEL);
+ if (!new_buf) {
ret = -ENOMEM;
goto failed;
}
+ buf = new_buf;
}
/* verify record data */
memcpy_fromio(buf, &dpram[addr + offset], len);
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 527dbcf95335..9ded21e79db5 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -984,12 +984,12 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(ndev);
+ unregister_candev(ndev);
clk_disable(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
release_mem_region(res->start, resource_size(res));
- unregister_candev(ndev);
free_candev(ndev);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index f0c8bd54ce29..021d69c5d9bc 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -1712,7 +1712,7 @@ e100_set_network_leds(int active)
static void
e100_netpoll(struct net_device* netdev)
{
- e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev, NULL);
+ e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev);
}
#endif
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 79cebd8525ce..e48312f2305d 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -8564,7 +8564,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
error:
- iounmap(bp->regview);
+ pci_iounmap(pdev, bp->regview);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 77bcd4cb4ffb..6d1a24acb77e 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -1278,7 +1278,7 @@ struct bnx2x {
#define BNX2X_FW_RX_ALIGN_START (1UL << BNX2X_RX_ALIGN_SHIFT)
#define BNX2X_FW_RX_ALIGN_END \
- max(1UL << BNX2X_RX_ALIGN_SHIFT, \
+ max_t(u64, 1UL << BNX2X_RX_ALIGN_SHIFT, \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define BNX2X_PXP_DRAM_ALIGN (BNX2X_RX_ALIGN_SHIFT - 5)
@@ -1708,9 +1708,6 @@ struct bnx2x_func_init_params {
continue; \
else
-#define for_each_napi_rx_queue(bp, var) \
- for ((var) = 0; (var) < bp->num_napi_queues; (var)++)
-
/* Skip OOO FP */
#define for_each_tx_queue(bp, var) \
for ((var) = 0; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index e879e19eb0d6..e8e97a7d1d06 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -662,14 +662,16 @@ void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe,
struct bnx2x_fastpath *fp,
struct bnx2x_eth_q_stats *qstats)
{
- /* Do nothing if no IP/L4 csum validation was done */
-
+ /* Do nothing if no L4 csum validation was done.
+ * We do not check whether IP csum was validated. For IPv4 we assume
+ * that if the card got as far as validating the L4 csum, it also
+ * validated the IP csum. IPv6 has no IP csum.
+ */
if (cqe->fast_path_cqe.status_flags &
- (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG |
- ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))
+ ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)
return;
- /* If both IP/L4 validation were done, check if an error was found. */
+ /* If L4 validation was done, check if an error was found. */
if (cqe->fast_path_cqe.type_error_flags &
(ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG |
@@ -2046,6 +2048,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
*/
bnx2x_setup_tc(bp->dev, bp->max_cos);
+ /* Add all NAPI objects */
+ bnx2x_add_all_napi(bp);
bnx2x_napi_enable(bp);
/* set pf load just before approaching the MCP */
@@ -2408,6 +2412,8 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode)
/* Disable HW interrupts, NAPI */
bnx2x_netif_stop(bp, 1);
+ /* Delete all NAPI objects */
+ bnx2x_del_all_napi(bp);
/* Release IRQs */
bnx2x_free_irq(bp);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
index dfa757e74296..dfd86a55f1dc 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h
@@ -710,17 +710,15 @@ static inline u16 bnx2x_tx_avail(struct bnx2x *bp,
prod = txdata->tx_bd_prod;
cons = txdata->tx_bd_cons;
- /* NUM_TX_RINGS = number of "next-page" entries
- It will be used as a threshold */
- used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS;
+ used = SUB_S16(prod, cons);
#ifdef BNX2X_STOP_ON_ERROR
WARN_ON(used < 0);
- WARN_ON(used > bp->tx_ring_size);
- WARN_ON((bp->tx_ring_size - used) > MAX_TX_AVAIL);
+ WARN_ON(used > txdata->tx_ring_size);
+ WARN_ON((txdata->tx_ring_size - used) > MAX_TX_AVAIL);
#endif
- return (s16)(bp->tx_ring_size) - used;
+ return (s16)(txdata->tx_ring_size) - used;
}
static inline int bnx2x_tx_queue_has_work(struct bnx2x_fp_txdata *txdata)
@@ -792,7 +790,7 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp)
bp->num_napi_queues = bp->num_queues;
/* Add NAPI objects */
- for_each_napi_rx_queue(bp, i)
+ for_each_rx_queue(bp, i)
netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi),
bnx2x_poll, BNX2X_NAPI_WEIGHT);
}
@@ -801,7 +799,7 @@ static inline void bnx2x_del_all_napi(struct bnx2x *bp)
{
int i;
- for_each_napi_rx_queue(bp, i)
+ for_each_rx_queue(bp, i)
netif_napi_del(&bnx2x_fp(bp, i, napi));
}
@@ -1088,6 +1086,7 @@ static inline void bnx2x_init_txdata(struct bnx2x *bp,
txdata->txq_index = txq_index;
txdata->tx_cons_sb = tx_cons_sb;
txdata->parent_fp = fp;
+ txdata->tx_ring_size = IS_FCOE_FP(fp) ? MAX_TX_AVAIL : bp->tx_ring_size;
DP(NETIF_MSG_IFUP, "created tx data cid %d, txq %d\n",
txdata->cid, txdata->txq_index);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
index 3e4cff9b1ebe..b926f58e983b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dump.h
@@ -401,11 +401,11 @@ static const struct reg_addr reg_addrs[] = {
{ 0x70000, 8, RI_ALL_ONLINE },
{ 0x70020, 8184, RI_ALL_OFFLINE },
{ 0x78000, 8192, RI_E3E3B0_OFFLINE },
- { 0x85000, 3, RI_ALL_ONLINE },
- { 0x8501c, 7, RI_ALL_ONLINE },
- { 0x85048, 1, RI_ALL_ONLINE },
- { 0x85200, 32, RI_ALL_ONLINE },
- { 0xb0000, 16384, RI_E1H_ONLINE },
+ { 0x85000, 3, RI_ALL_OFFLINE },
+ { 0x8501c, 7, RI_ALL_OFFLINE },
+ { 0x85048, 1, RI_ALL_OFFLINE },
+ { 0x85200, 32, RI_ALL_OFFLINE },
+ { 0xb0000, 16384, RI_E1H_OFFLINE },
{ 0xc1000, 7, RI_ALL_ONLINE },
{ 0xc103c, 2, RI_E2E3E3B0_ONLINE },
{ 0xc1800, 2, RI_ALL_ONLINE },
@@ -581,17 +581,12 @@ static const struct reg_addr reg_addrs[] = {
{ 0x140188, 3, RI_E1E1HE2E3_ONLINE },
{ 0x140194, 13, RI_ALL_ONLINE },
{ 0x140200, 6, RI_E1E1HE2E3_ONLINE },
- { 0x140220, 4, RI_E2E3_ONLINE },
- { 0x140240, 4, RI_E2E3_ONLINE },
{ 0x140260, 4, RI_E2E3_ONLINE },
{ 0x140280, 4, RI_E2E3_ONLINE },
- { 0x1402a0, 4, RI_E2E3_ONLINE },
- { 0x1402c0, 4, RI_E2E3_ONLINE },
{ 0x1402e0, 2, RI_E2E3_ONLINE },
{ 0x1402e8, 2, RI_E2E3E3B0_ONLINE },
{ 0x1402f0, 9, RI_E2E3_ONLINE },
{ 0x140314, 44, RI_E3B0_ONLINE },
- { 0x1403d0, 70, RI_E3B0_ONLINE },
{ 0x144000, 4, RI_E1E1H_ONLINE },
{ 0x148000, 4, RI_E1E1H_ONLINE },
{ 0x14c000, 4, RI_E1E1H_ONLINE },
@@ -704,7 +699,6 @@ static const struct reg_addr reg_addrs[] = {
{ 0x180398, 1, RI_E2E3E3B0_ONLINE },
{ 0x1803a0, 5, RI_E2E3E3B0_ONLINE },
{ 0x1803b4, 2, RI_E3E3B0_ONLINE },
- { 0x180400, 1, RI_ALL_ONLINE },
{ 0x180404, 255, RI_E1E1H_OFFLINE },
{ 0x181000, 4, RI_ALL_ONLINE },
{ 0x181010, 1020, RI_ALL_OFFLINE },
@@ -800,9 +794,9 @@ static const struct reg_addr reg_addrs[] = {
{ 0x1b905c, 1, RI_E3E3B0_ONLINE },
{ 0x1b9064, 1, RI_E3B0_ONLINE },
{ 0x1b9080, 10, RI_E3B0_ONLINE },
- { 0x1b9400, 14, RI_E2E3E3B0_ONLINE },
- { 0x1b943c, 19, RI_E2E3E3B0_ONLINE },
- { 0x1b9490, 10, RI_E2E3E3B0_ONLINE },
+ { 0x1b9400, 14, RI_E2E3E3B0_OFFLINE },
+ { 0x1b943c, 19, RI_E2E3E3B0_OFFLINE },
+ { 0x1b9490, 10, RI_E2E3E3B0_OFFLINE },
{ 0x1c0000, 2, RI_ALL_ONLINE },
{ 0x200000, 65, RI_ALL_ONLINE },
{ 0x20014c, 2, RI_E1HE2E3E3B0_ONLINE },
@@ -814,7 +808,6 @@ static const struct reg_addr reg_addrs[] = {
{ 0x200398, 1, RI_E2E3E3B0_ONLINE },
{ 0x2003a0, 1, RI_E2E3E3B0_ONLINE },
{ 0x2003a8, 2, RI_E2E3E3B0_ONLINE },
- { 0x200400, 1, RI_ALL_ONLINE },
{ 0x200404, 255, RI_E1E1H_OFFLINE },
{ 0x202000, 4, RI_ALL_ONLINE },
{ 0x202010, 2044, RI_ALL_OFFLINE },
@@ -921,7 +914,6 @@ static const struct reg_addr reg_addrs[] = {
{ 0x280398, 1, RI_E2E3E3B0_ONLINE },
{ 0x2803a0, 1, RI_E2E3E3B0_ONLINE },
{ 0x2803a8, 2, RI_E2E3E3B0_ONLINE },
- { 0x280400, 1, RI_ALL_ONLINE },
{ 0x280404, 255, RI_E1E1H_OFFLINE },
{ 0x282000, 4, RI_ALL_ONLINE },
{ 0x282010, 2044, RI_ALL_OFFLINE },
@@ -1031,7 +1023,6 @@ static const struct reg_addr reg_addrs[] = {
{ 0x300398, 1, RI_E2E3E3B0_ONLINE },
{ 0x3003a0, 1, RI_E2E3E3B0_ONLINE },
{ 0x3003a8, 2, RI_E2E3E3B0_ONLINE },
- { 0x300400, 1, RI_ALL_ONLINE },
{ 0x300404, 255, RI_E1E1H_OFFLINE },
{ 0x302000, 4, RI_ALL_ONLINE },
{ 0x302010, 2044, RI_ALL_OFFLINE },
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index fc4e0e3885b0..ebf40cd7aa10 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -775,7 +775,7 @@ static void bnx2x_get_regs(struct net_device *dev,
struct bnx2x *bp = netdev_priv(dev);
struct dump_hdr dump_hdr = {0};
- regs->version = 0;
+ regs->version = 1;
memset(p, 0, regs->len);
if (!netif_running(bp->dev))
@@ -1587,6 +1587,12 @@ static int bnx2x_set_pauseparam(struct net_device *dev,
bp->link_params.req_flow_ctrl[cfg_idx] =
BNX2X_FLOW_CTRL_AUTO;
}
+ bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_NONE;
+ if (epause->rx_pause)
+ bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_RX;
+
+ if (epause->tx_pause)
+ bp->link_params.req_fc_auto_adv |= BNX2X_FLOW_CTRL_TX;
}
DP(BNX2X_MSG_ETHTOOL,
@@ -2888,11 +2894,9 @@ static void bnx2x_get_channels(struct net_device *dev,
*/
static void bnx2x_change_num_queues(struct bnx2x *bp, int num_rss)
{
- bnx2x_del_all_napi(bp);
bnx2x_disable_msi(bp);
BNX2X_NUM_QUEUES(bp) = num_rss + NON_ETH_CONTEXT_USE;
bnx2x_set_int_mode(bp);
- bnx2x_add_all_napi(bp);
}
/**
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
index f4beb46c4709..b046beb435b2 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c
@@ -2667,9 +2667,11 @@ int bnx2x_update_pfc(struct link_params *params,
return bnx2x_status;
DP(NETIF_MSG_LINK, "About to update PFC in BMAC\n");
- if (CHIP_IS_E3(bp))
- bnx2x_update_pfc_xmac(params, vars, 0);
- else {
+
+ if (CHIP_IS_E3(bp)) {
+ if (vars->mac_type == MAC_TYPE_XMAC)
+ bnx2x_update_pfc_xmac(params, vars, 0);
+ } else {
val = REG_RD(bp, MISC_REG_RESET_REG_2);
if ((val &
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
@@ -5432,7 +5434,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
switch (speed_mask) {
case GP_STATUS_10M:
vars->line_speed = SPEED_10;
- if (vars->duplex == DUPLEX_FULL)
+ if (is_duplex == DUPLEX_FULL)
vars->link_status |= LINK_10TFD;
else
vars->link_status |= LINK_10THD;
@@ -5440,7 +5442,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
case GP_STATUS_100M:
vars->line_speed = SPEED_100;
- if (vars->duplex == DUPLEX_FULL)
+ if (is_duplex == DUPLEX_FULL)
vars->link_status |= LINK_100TXFD;
else
vars->link_status |= LINK_100TXHD;
@@ -5449,7 +5451,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
case GP_STATUS_1G:
case GP_STATUS_1G_KX:
vars->line_speed = SPEED_1000;
- if (vars->duplex == DUPLEX_FULL)
+ if (is_duplex == DUPLEX_FULL)
vars->link_status |= LINK_1000TFD;
else
vars->link_status |= LINK_1000THD;
@@ -5457,7 +5459,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy,
case GP_STATUS_2_5G:
vars->line_speed = SPEED_2500;
- if (vars->duplex == DUPLEX_FULL)
+ if (is_duplex == DUPLEX_FULL)
vars->link_status |= LINK_2500TFD;
else
vars->link_status |= LINK_2500THD;
@@ -5531,6 +5533,7 @@ static int bnx2x_link_settings_status(struct bnx2x_phy *phy,
if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) {
if (SINGLE_MEDIA_DIRECT(params)) {
+ vars->duplex = duplex;
bnx2x_flow_ctrl_resolve(phy, params, vars, gp_status);
if (phy->req_line_speed == SPEED_AUTO_NEG)
bnx2x_xgxs_an_resolve(phy, params, vars,
@@ -5625,6 +5628,7 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy,
LINK_STATUS_PARALLEL_DETECTION_USED;
}
bnx2x_ext_phy_resolve_fc(phy, params, vars);
+ vars->duplex = duplex;
}
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index dd451c3dd83d..0875ecfe3372 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -4041,20 +4041,6 @@ static bool bnx2x_get_load_status(struct bnx2x *bp, int engine)
return val != 0;
}
-/*
- * Reset the load status for the current engine.
- */
-static void bnx2x_clear_load_status(struct bnx2x *bp)
-{
- u32 val;
- u32 mask = (BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_MASK :
- BNX2X_PATH0_LOAD_CNT_MASK);
- bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RECOVERY_REG);
- val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
- REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val & (~mask));
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RECOVERY_REG);
-}
-
static void _print_next_block(int idx, const char *blk)
{
pr_cont("%s%s", idx ? ", " : "", blk);
@@ -7575,8 +7561,14 @@ int bnx2x_set_mac_one(struct bnx2x *bp, u8 *mac,
}
rc = bnx2x_config_vlan_mac(bp, &ramrod_param);
- if (rc < 0)
+
+ if (rc == -EEXIST) {
+ DP(BNX2X_MSG_SP, "Failed to schedule ADD operations: %d\n", rc);
+ /* do not treat adding same MAC as error */
+ rc = 0;
+ } else if (rc < 0)
BNX2X_ERR("%s MAC failed\n", (set ? "Set" : "Del"));
+
return rc;
}
@@ -8441,6 +8433,8 @@ unload_error:
/* Disable HW interrupts, NAPI */
bnx2x_netif_stop(bp, 1);
+ /* Delete all NAPI objects */
+ bnx2x_del_all_napi(bp);
/* Release IRQs */
bnx2x_free_irq(bp);
@@ -9384,32 +9378,24 @@ static int __devinit bnx2x_prev_mark_path(struct bnx2x *bp)
return rc;
}
-static bool __devinit bnx2x_can_flr(struct bnx2x *bp)
-{
- int pos;
- u32 cap;
- struct pci_dev *dev = bp->pdev;
-
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
- if (!(cap & PCI_EXP_DEVCAP_FLR))
- return false;
-
- return true;
-}
-
static int __devinit bnx2x_do_flr(struct bnx2x *bp)
{
int i, pos;
u16 status;
struct pci_dev *dev = bp->pdev;
- /* probe the capability first */
- if (bnx2x_can_flr(bp))
- return -ENOTTY;
+
+ if (CHIP_IS_E1x(bp)) {
+ BNX2X_DEV_INFO("FLR not supported in E1/E1H\n");
+ return -EINVAL;
+ }
+
+ /* only bootcode REQ_BC_VER_4_INITIATE_FLR and onwards support flr */
+ if (bp->common.bc_ver < REQ_BC_VER_4_INITIATE_FLR) {
+ BNX2X_ERR("FLR not supported by BC_VER: 0x%x\n",
+ bp->common.bc_ver);
+ return -EINVAL;
+ }
pos = pci_pcie_cap(dev);
if (!pos)
@@ -9429,12 +9415,8 @@ static int __devinit bnx2x_do_flr(struct bnx2x *bp)
"transaction is not cleared; proceeding with reset anyway\n");
clear:
- if (bp->common.bc_ver < REQ_BC_VER_4_INITIATE_FLR) {
- BNX2X_ERR("FLR not supported by BC_VER: 0x%x\n",
- bp->common.bc_ver);
- return -EINVAL;
- }
+ BNX2X_DEV_INFO("Initiating FLR\n");
bnx2x_fw_command(bp, DRV_MSG_CODE_INITIATE_FLR, 0);
return 0;
@@ -9454,8 +9436,21 @@ static int __devinit bnx2x_prev_unload_uncommon(struct bnx2x *bp)
* the one required, then FLR will be sufficient to clean any residue
* left by previous driver
*/
- if (bnx2x_test_firmware_version(bp, false) && bnx2x_can_flr(bp))
- return bnx2x_do_flr(bp);
+ rc = bnx2x_test_firmware_version(bp, false);
+
+ if (!rc) {
+ /* fw version is good */
+ BNX2X_DEV_INFO("FW version matches our own. Attempting FLR\n");
+ rc = bnx2x_do_flr(bp);
+ }
+
+ if (!rc) {
+ /* FLR was performed */
+ BNX2X_DEV_INFO("FLR successful\n");
+ return 0;
+ }
+
+ BNX2X_DEV_INFO("Could not FLR\n");
/* Close the MCP request, return failure*/
rc = bnx2x_prev_mcp_done(bp);
@@ -9836,12 +9831,13 @@ static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
}
#ifdef CONFIG_PCI_MSI
- /*
- * It's expected that number of CAM entries for this functions is equal
- * to the number evaluated based on the MSI-X table size. We want a
- * harsh warning if these values are different!
+ /* Due to new PF resource allocation by MFW T7.4 and above, it's
+ * optional that number of CAM entries will not be equal to the value
+ * advertised in PCI.
+ * Driver should use the minimal value of both as the actual status
+ * block count
*/
- WARN_ON(bp->igu_sb_cnt != igu_sb_cnt);
+ bp->igu_sb_cnt = min_t(int, bp->igu_sb_cnt, igu_sb_cnt);
#endif
if (igu_sb_cnt == 0)
@@ -10305,13 +10301,11 @@ static void __devinit bnx2x_get_fcoe_info(struct bnx2x *bp)
dev_info.port_hw_config[port].
fcoe_wwn_node_name_lower);
} else if (!IS_MF_SD(bp)) {
- u32 cfg = MF_CFG_RD(bp, func_ext_config[func].func_cfg);
-
/*
* Read the WWN info only if the FCoE feature is enabled for
* this function.
*/
- if (cfg & MACP_FUNC_CFG_FLAGS_FCOE_OFFLOAD)
+ if (BNX2X_MF_EXT_PROTOCOL_FCOE(bp) && !CHIP_IS_E1x(bp))
bnx2x_get_ext_wwn_info(bp, func);
} else if (IS_MF_FCOE_SD(bp))
@@ -11084,7 +11078,14 @@ static int bnx2x_set_uc_list(struct bnx2x *bp)
netdev_for_each_uc_addr(ha, dev) {
rc = bnx2x_set_mac_one(bp, bnx2x_uc_addr(ha), mac_obj, true,
BNX2X_UC_LIST_MAC, &ramrod_flags);
- if (rc < 0) {
+ if (rc == -EEXIST) {
+ DP(BNX2X_MSG_SP,
+ "Failed to schedule ADD operations: %d\n", rc);
+ /* do not treat adding same MAC as error */
+ rc = 0;
+
+ } else if (rc < 0) {
+
BNX2X_ERR("Failed to schedule ADD operations: %d\n",
rc);
return rc;
@@ -11242,10 +11243,12 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static void poll_bnx2x(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
+ int i;
- disable_irq(bp->pdev->irq);
- bnx2x_interrupt(bp->pdev->irq, dev);
- enable_irq(bp->pdev->irq);
+ for_each_eth_queue(bp, i) {
+ struct bnx2x_fastpath *fp = &bp->fp[i];
+ napi_schedule(&bnx2x_fp(bp, fp->index, napi));
+ }
}
#endif
@@ -11427,9 +11430,6 @@ static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
if (!chip_is_e1x)
REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
- /* Reset the load counter */
- bnx2x_clear_load_status(bp);
-
dev->watchdog_timeo = TX_TIMEOUT;
dev->netdev_ops = &bnx2x_netdev_ops;
@@ -11915,9 +11915,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev,
*/
bnx2x_set_int_mode(bp);
- /* Add all NAPI objects */
- bnx2x_add_all_napi(bp);
-
rc = register_netdev(dev);
if (rc) {
dev_err(&pdev->dev, "Cannot register net device\n");
@@ -11992,9 +11989,6 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev)
unregister_netdev(dev);
- /* Delete all NAPI objects */
- bnx2x_del_all_napi(bp);
-
/* Power on: we can't let PCI layer write to us while we are in D3 */
bnx2x_set_power_state(bp, PCI_D0);
@@ -12041,6 +12035,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp)
bnx2x_tx_disable(bp);
bnx2x_netif_stop(bp, 0);
+ /* Delete all NAPI objects */
+ bnx2x_del_all_napi(bp);
del_timer_sync(&bp->timer);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 734fd87cd990..62f754bd0dfe 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -2485,6 +2485,7 @@ static int bnx2x_mcast_enqueue_cmd(struct bnx2x *bp,
break;
default:
+ kfree(new_cmd);
BNX2X_ERR("Unknown command: %d\n", cmd);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
index 332db64dd5be..a1d0446b39b3 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c
@@ -101,6 +101,11 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
if (CHIP_REV_IS_SLOW(bp))
return;
+ /* Update MCP's statistics if possible */
+ if (bp->func_stx)
+ memcpy(bnx2x_sp(bp, func_stats), &bp->func_stats,
+ sizeof(bp->func_stats));
+
/* loader */
if (bp->executer_idx) {
int loader_idx = PMF_DMAE_C(bp);
@@ -128,8 +133,6 @@ static void bnx2x_hw_stats_post(struct bnx2x *bp)
} else if (bp->func_stx) {
*stats_comp = 0;
- memcpy(bnx2x_sp(bp, func_stats), &bp->func_stats,
- sizeof(bp->func_stats));
bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
}
}
@@ -1151,9 +1154,11 @@ static void bnx2x_stats_update(struct bnx2x *bp)
if (bp->port.pmf)
bnx2x_hw_stats_update(bp);
- if (bnx2x_storm_stats_update(bp) && (bp->stats_pending++ == 3)) {
- BNX2X_ERR("storm stats were not updated for 3 times\n");
- bnx2x_panic();
+ if (bnx2x_storm_stats_update(bp)) {
+ if (bp->stats_pending++ == 3) {
+ BNX2X_ERR("storm stats were not updated for 3 times\n");
+ bnx2x_panic();
+ }
return;
}
diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index 77884191a8c6..4e980a7886fb 100644
--- a/drivers/net/ethernet/cadence/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -1086,7 +1086,7 @@ static int __init at91ether_probe(struct platform_device *pdev)
/* Clock */
lp->ether_clk = clk_get(&pdev->dev, "ether_clk");
if (IS_ERR(lp->ether_clk)) {
- res = -ENODEV;
+ res = PTR_ERR(lp->ether_clk);
goto err_ioumap;
}
clk_enable(lp->ether_clk);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index 8596acaa402b..d49933ed551f 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -528,7 +528,7 @@ static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n,
#endif
while (n--) {
- pg = alloc_page(gfp);
+ pg = __skb_alloc_page(gfp, NULL);
if (unlikely(!pg)) {
q->alloc_failed++;
break;
diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
index f2d1ecdcaf98..8877fbfefb63 100644
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c
@@ -653,7 +653,7 @@ static unsigned int refill_fl(struct adapter *adapter, struct sge_fl *fl,
alloc_small_pages:
while (n--) {
- page = alloc_page(gfp | __GFP_NOWARN | __GFP_COLD);
+ page = __skb_alloc_page(gfp | __GFP_NOWARN, NULL);
if (unlikely(!page)) {
fl->alloc_failed++;
break;
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index 845b2020f291..138446957786 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -1243,6 +1243,7 @@ static void set_multicast_list(struct net_device *dev)
{
struct net_local *lp = netdev_priv(dev);
unsigned long flags;
+ u16 cfg;
spin_lock_irqsave(&lp->lock, flags);
if (dev->flags & IFF_PROMISC)
@@ -1260,11 +1261,10 @@ static void set_multicast_list(struct net_device *dev)
/* in promiscuous mode, we accept errored packets,
* so we have to enable interrupts on them also
*/
- writereg(dev, PP_RxCFG,
- (lp->curr_rx_cfg |
- (lp->rx_mode == RX_ALL_ACCEPT)
- ? (RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL)
- : 0));
+ cfg = lp->curr_rx_cfg;
+ if (lp->rx_mode == RX_ALL_ACCEPT)
+ cfg |= RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL;
+ writereg(dev, PP_RxCFG, cfg);
spin_unlock_irqrestore(&lp->lock, flags);
}
diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c
index 7fac97b4bb59..8c63d06ab12b 100644
--- a/drivers/net/ethernet/emulex/benet/be_cmds.c
+++ b/drivers/net/ethernet/emulex/benet/be_cmds.c
@@ -259,7 +259,7 @@ int be_process_mcc(struct be_adapter *adapter)
int num = 0, status = 0;
struct be_mcc_obj *mcc_obj = &adapter->mcc_obj;
- spin_lock_bh(&adapter->mcc_cq_lock);
+ spin_lock(&adapter->mcc_cq_lock);
while ((compl = be_mcc_compl_get(adapter))) {
if (compl->flags & CQE_FLAGS_ASYNC_MASK) {
/* Interpret flags as an async trailer */
@@ -280,7 +280,7 @@ int be_process_mcc(struct be_adapter *adapter)
if (num)
be_cq_notify(adapter, mcc_obj->cq.id, mcc_obj->rearm_cq, num);
- spin_unlock_bh(&adapter->mcc_cq_lock);
+ spin_unlock(&adapter->mcc_cq_lock);
return status;
}
@@ -295,7 +295,9 @@ static int be_mcc_wait_compl(struct be_adapter *adapter)
if (be_error(adapter))
return -EIO;
+ local_bh_disable();
status = be_process_mcc(adapter);
+ local_bh_enable();
if (atomic_read(&mcc_obj->q.used) == 0)
break;
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index c60de89b6669..78b8aa8069f0 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -1948,7 +1948,7 @@ static int be_rx_cqs_create(struct be_adapter *adapter)
if (adapter->num_rx_qs != MAX_RX_QS)
dev_info(&adapter->pdev->dev,
- "Created only %d receive queues", adapter->num_rx_qs);
+ "Created only %d receive queues\n", adapter->num_rx_qs);
return 0;
}
@@ -3763,7 +3763,9 @@ static void be_worker(struct work_struct *work)
/* when interrupts are not yet enabled, just reap any pending
* mcc completions */
if (!netif_running(adapter->netdev)) {
+ local_bh_disable();
be_process_mcc(adapter);
+ local_bh_enable();
goto reschedule;
}
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
index 0f2d1a710909..151453309401 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
@@ -174,8 +174,10 @@ static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
new_bus->phy_mask = ~0;
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq)
+ if (!new_bus->irq) {
+ ret = -ENOMEM;
goto out_unmap_regs;
+ }
new_bus->parent = &ofdev->dev;
dev_set_drvdata(&ofdev->dev, new_bus);
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 55bb867258e6..cdf702a59485 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -137,8 +137,10 @@ static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start);
fec->fecp = ioremap(res.start, resource_size(&res));
- if (!fec->fecp)
+ if (!fec->fecp) {
+ ret = -ENOMEM;
goto out_fec;
+ }
if (get_bus_freq) {
clock = get_bus_freq(ofdev->dev.of_node);
@@ -172,8 +174,10 @@ static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev)
new_bus->phy_mask = ~0;
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq)
+ if (!new_bus->irq) {
+ ret = -ENOMEM;
goto out_unmap_regs;
+ }
new_bus->parent = &ofdev->dev;
dev_set_drvdata(&ofdev->dev, new_bus);
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 4605f7246687..d3233f59a82e 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -1041,7 +1041,7 @@ static int gfar_probe(struct platform_device *ofdev)
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+ dev->features |= NETIF_F_HW_VLAN_RX;
}
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_EXTENDED_HASH) {
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 8971921cc1c8..ab6762caa957 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -1773,6 +1773,7 @@ static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
}
int gfar_phc_index = -1;
+EXPORT_SYMBOL(gfar_phc_index);
static int gfar_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index c08e5d40fecb..0daa66b8eca0 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -515,7 +515,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
err = PTR_ERR(etsects->clock);
goto no_clock;
}
- gfar_phc_clock = ptp_clock_index(etsects->clock);
+ gfar_phc_index = ptp_clock_index(etsects->clock);
dev_set_drvdata(&dev->dev, etsects);
@@ -539,7 +539,7 @@ static int gianfar_ptp_remove(struct platform_device *dev)
gfar_write(&etsects->regs->tmr_temask, 0);
gfar_write(&etsects->regs->tmr_ctrl, 0);
- gfar_phc_clock = -1;
+ gfar_phc_index = -1;
ptp_clock_unregister(etsects->clock);
iounmap(etsects->regs);
release_resource(etsects->rsrc);
diff --git a/drivers/net/ethernet/i825xx/znet.c b/drivers/net/ethernet/i825xx/znet.c
index bd1f1ef91e19..ba4e0cea3506 100644
--- a/drivers/net/ethernet/i825xx/znet.c
+++ b/drivers/net/ethernet/i825xx/znet.c
@@ -139,8 +139,11 @@ struct znet_private {
/* Only one can be built-in;-> */
static struct net_device *znet_dev;
+#define NETIDBLK_MAGIC "NETIDBLK"
+#define NETIDBLK_MAGIC_SIZE 8
+
struct netidblk {
- char magic[8]; /* The magic number (string) "NETIDBLK" */
+ char magic[NETIDBLK_MAGIC_SIZE]; /* The magic number (string) "NETIDBLK" */
unsigned char netid[8]; /* The physical station address */
char nettype, globalopt;
char vendor[8]; /* The machine vendor and product name. */
@@ -373,14 +376,16 @@ static int __init znet_probe (void)
struct znet_private *znet;
struct net_device *dev;
char *p;
+ char *plast = phys_to_virt(0x100000 - NETIDBLK_MAGIC_SIZE);
int err = -ENOMEM;
/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
- for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
- if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ for(p = (char *)phys_to_virt(0xf0000); p <= plast; p++)
+ if (*p == 'N' &&
+ strncmp(p, NETIDBLK_MAGIC, NETIDBLK_MAGIC_SIZE) == 0)
break;
- if (p >= (char *)phys_to_virt(0x100000)) {
+ if (p > plast) {
if (znet_debug > 1)
printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
return -ENODEV;
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index 9010cea68bc3..b68d28a130e6 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -472,14 +472,9 @@ static void ibmveth_cleanup(struct ibmveth_adapter *adapter)
}
if (adapter->rx_queue.queue_addr != NULL) {
- if (!dma_mapping_error(dev, adapter->rx_queue.queue_dma)) {
- dma_unmap_single(dev,
- adapter->rx_queue.queue_dma,
- adapter->rx_queue.queue_len,
- DMA_BIDIRECTIONAL);
- adapter->rx_queue.queue_dma = DMA_ERROR_CODE;
- }
- kfree(adapter->rx_queue.queue_addr);
+ dma_free_coherent(dev, adapter->rx_queue.queue_len,
+ adapter->rx_queue.queue_addr,
+ adapter->rx_queue.queue_dma);
adapter->rx_queue.queue_addr = NULL;
}
@@ -556,10 +551,13 @@ static int ibmveth_open(struct net_device *netdev)
goto err_out;
}
+ dev = &adapter->vdev->dev;
+
adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) *
rxq_entries;
- adapter->rx_queue.queue_addr = kmalloc(adapter->rx_queue.queue_len,
- GFP_KERNEL);
+ adapter->rx_queue.queue_addr =
+ dma_alloc_coherent(dev, adapter->rx_queue.queue_len,
+ &adapter->rx_queue.queue_dma, GFP_KERNEL);
if (!adapter->rx_queue.queue_addr) {
netdev_err(netdev, "unable to allocate rx queue pages\n");
@@ -567,19 +565,13 @@ static int ibmveth_open(struct net_device *netdev)
goto err_out;
}
- dev = &adapter->vdev->dev;
-
adapter->buffer_list_dma = dma_map_single(dev,
adapter->buffer_list_addr, 4096, DMA_BIDIRECTIONAL);
adapter->filter_list_dma = dma_map_single(dev,
adapter->filter_list_addr, 4096, DMA_BIDIRECTIONAL);
- adapter->rx_queue.queue_dma = dma_map_single(dev,
- adapter->rx_queue.queue_addr,
- adapter->rx_queue.queue_len, DMA_BIDIRECTIONAL);
if ((dma_mapping_error(dev, adapter->buffer_list_dma)) ||
- (dma_mapping_error(dev, adapter->filter_list_dma)) ||
- (dma_mapping_error(dev, adapter->rx_queue.queue_dma))) {
+ (dma_mapping_error(dev, adapter->filter_list_dma))) {
netdev_err(netdev, "unable to map filter or buffer list "
"pages\n");
rc = -ENOMEM;
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 3bfbb8df8989..bde337ee1a34 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -3149,6 +3149,17 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+ /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
+ * packets may get corrupted during padding by HW.
+ * To WA this issue, pad all small packets manually.
+ */
+ if (skb->len < ETH_ZLEN) {
+ if (skb_pad(skb, ETH_ZLEN - skb->len))
+ return NETDEV_TX_OK;
+ skb->len = ETH_ZLEN;
+ skb_set_tail_pointer(skb, ETH_ZLEN);
+ }
+
mss = skb_shinfo(skb)->gso_size;
/* The controller does a simple calculation to
* make sure there is enough room in the FIFO before
diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c
index 0b3bade957fd..080c89093feb 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.c
+++ b/drivers/net/ethernet/intel/e1000e/82571.c
@@ -999,7 +999,7 @@ static s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, bool active)
**/
static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
{
- u32 ctrl, ctrl_ext, eecd;
+ u32 ctrl, ctrl_ext, eecd, tctl;
s32 ret_val;
/*
@@ -1014,7 +1014,9 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
ew32(IMC, 0xffffffff);
ew32(RCTL, 0);
- ew32(TCTL, E1000_TCTL_PSP);
+ tctl = er32(TCTL);
+ tctl &= ~E1000_TCTL_EN;
+ ew32(TCTL, tctl);
e1e_flush();
usleep_range(10000, 20000);
@@ -1601,10 +1603,8 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw)
* auto-negotiation in the TXCW register and disable
* forced link in the Device Control register in an
* attempt to auto-negotiate with our link partner.
- * If the partner code word is null, stop forcing
- * and restart auto negotiation.
*/
- if ((rxcw & E1000_RXCW_C) || !(rxcw & E1000_RXCW_CW)) {
+ if (rxcw & E1000_RXCW_C) {
/* Enable autoneg, and unforce link up */
ew32(TXCW, mac->txcw);
ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index cd153326c3cf..cb3356c9af80 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -310,6 +310,7 @@ struct e1000_adapter {
*/
struct e1000_ring *tx_ring /* One per active queue */
____cacheline_aligned_in_smp;
+ u32 tx_fifo_limit;
struct napi_struct napi;
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 95b245310f17..d01a099475a1 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -178,6 +178,24 @@ static void e1000_regdump(struct e1000_hw *hw, struct e1000_reg_info *reginfo)
pr_info("%-15s %08x %08x\n", rname, regs[0], regs[1]);
}
+static void e1000e_dump_ps_pages(struct e1000_adapter *adapter,
+ struct e1000_buffer *bi)
+{
+ int i;
+ struct e1000_ps_page *ps_page;
+
+ for (i = 0; i < adapter->rx_ps_pages; i++) {
+ ps_page = &bi->ps_pages[i];
+
+ if (ps_page->page) {
+ pr_info("packet dump for ps_page %d:\n", i);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS,
+ 16, 1, page_address(ps_page->page),
+ PAGE_SIZE, true);
+ }
+ }
+}
+
/*
* e1000e_dump - Print registers, Tx-ring and Rx-ring
*/
@@ -299,10 +317,10 @@ static void e1000e_dump(struct e1000_adapter *adapter)
(unsigned long long)buffer_info->time_stamp,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter) && buffer_info->dma != 0)
+ if (netif_msg_pktdata(adapter) && buffer_info->skb)
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS,
- 16, 1, phys_to_virt(buffer_info->dma),
- buffer_info->length, true);
+ 16, 1, buffer_info->skb->data,
+ buffer_info->skb->len, true);
}
/* Print Rx Ring Summary */
@@ -381,10 +399,8 @@ rx_ring_summary:
buffer_info->skb, next_desc);
if (netif_msg_pktdata(adapter))
- print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS, 16, 1,
- phys_to_virt(buffer_info->dma),
- adapter->rx_ps_bsize0, true);
+ e1000e_dump_ps_pages(adapter,
+ buffer_info);
}
}
break;
@@ -444,12 +460,12 @@ rx_ring_summary:
(unsigned long long)buffer_info->dma,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter))
+ if (netif_msg_pktdata(adapter) &&
+ buffer_info->skb)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS, 16,
1,
- phys_to_virt
- (buffer_info->dma),
+ buffer_info->skb->data,
adapter->rx_buffer_len,
true);
}
@@ -3501,6 +3517,15 @@ void e1000e_reset(struct e1000_adapter *adapter)
}
/*
+ * Alignment of Tx data is on an arbitrary byte boundary with the
+ * maximum size per Tx descriptor limited only to the transmit
+ * allocation of the packet buffer minus 96 bytes with an upper
+ * limit of 24KB due to receive synchronization limitations.
+ */
+ adapter->tx_fifo_limit = min_t(u32, ((er32(PBA) >> 16) << 10) - 96,
+ 24 << 10);
+
+ /*
* Disable Adaptive Interrupt Moderation if 2 full packets cannot
* fit in receive buffer.
*/
@@ -4769,12 +4794,9 @@ static bool e1000_tx_csum(struct e1000_ring *tx_ring, struct sk_buff *skb)
return 1;
}
-#define E1000_MAX_PER_TXD 8192
-#define E1000_MAX_TXD_PWR 12
-
static int e1000_tx_map(struct e1000_ring *tx_ring, struct sk_buff *skb,
unsigned int first, unsigned int max_per_txd,
- unsigned int nr_frags, unsigned int mss)
+ unsigned int nr_frags)
{
struct e1000_adapter *adapter = tx_ring->adapter;
struct pci_dev *pdev = adapter->pdev;
@@ -5007,20 +5029,19 @@ static int __e1000_maybe_stop_tx(struct e1000_ring *tx_ring, int size)
static int e1000_maybe_stop_tx(struct e1000_ring *tx_ring, int size)
{
+ BUG_ON(size > tx_ring->count);
+
if (e1000_desc_unused(tx_ring) >= size)
return 0;
return __e1000_maybe_stop_tx(tx_ring, size);
}
-#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1)
static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
struct net_device *netdev)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_ring *tx_ring = adapter->tx_ring;
unsigned int first;
- unsigned int max_per_txd = E1000_MAX_PER_TXD;
- unsigned int max_txd_pwr = E1000_MAX_TXD_PWR;
unsigned int tx_flags = 0;
unsigned int len = skb_headlen(skb);
unsigned int nr_frags;
@@ -5040,18 +5061,8 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
}
mss = skb_shinfo(skb)->gso_size;
- /*
- * The controller does a simple calculation to
- * make sure there is enough room in the FIFO before
- * initiating the DMA for each buffer. The calc is:
- * 4 = ceil(buffer len/mss). To make sure we don't
- * overrun the FIFO, adjust the max buffer len if mss
- * drops.
- */
if (mss) {
u8 hdr_len;
- max_per_txd = min(mss << 2, max_per_txd);
- max_txd_pwr = fls(max_per_txd) - 1;
/*
* TSO Workaround for 82571/2/3 Controllers -- if skb->data
@@ -5081,12 +5092,12 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
count++;
count++;
- count += TXD_USE_COUNT(len, max_txd_pwr);
+ count += DIV_ROUND_UP(len, adapter->tx_fifo_limit);
nr_frags = skb_shinfo(skb)->nr_frags;
for (f = 0; f < nr_frags; f++)
- count += TXD_USE_COUNT(skb_frag_size(&skb_shinfo(skb)->frags[f]),
- max_txd_pwr);
+ count += DIV_ROUND_UP(skb_frag_size(&skb_shinfo(skb)->frags[f]),
+ adapter->tx_fifo_limit);
if (adapter->hw.mac.tx_pkt_filtering)
e1000_transfer_dhcp_info(adapter, skb);
@@ -5128,15 +5139,18 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
tx_flags |= E1000_TX_FLAGS_NO_FCS;
/* if count is 0 then mapping error has occurred */
- count = e1000_tx_map(tx_ring, skb, first, max_per_txd, nr_frags, mss);
+ count = e1000_tx_map(tx_ring, skb, first, adapter->tx_fifo_limit,
+ nr_frags);
if (count) {
skb_tx_timestamp(skb);
netdev_sent_queue(netdev, skb->len);
e1000_tx_queue(tx_ring, tx_flags, count);
/* Make sure there is space in the ring for the next send. */
- e1000_maybe_stop_tx(tx_ring, MAX_SKB_FRAGS + 2);
-
+ e1000_maybe_stop_tx(tx_ring,
+ (MAX_SKB_FRAGS *
+ DIV_ROUND_UP(PAGE_SIZE,
+ adapter->tx_fifo_limit) + 2));
} else {
dev_kfree_skb_any(skb);
tx_ring->buffer_info[first].time_stamp = 0;
@@ -6311,8 +6325,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
adapter->hw.phy.autoneg_advertised = 0x2f;
/* ring size defaults */
- adapter->rx_ring->count = 256;
- adapter->tx_ring->count = 256;
+ adapter->rx_ring->count = E1000_DEFAULT_RXD;
+ adapter->tx_ring->count = E1000_DEFAULT_TXD;
/*
* Initial Wake on LAN setting - If APM wake is enabled in
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 5e84eaac48c1..ba994fb4cec6 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -254,6 +254,14 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
*/
size += NVM_WORD_SIZE_BASE_SHIFT;
+ /*
+ * Check for invalid size
+ */
+ if ((hw->mac.type == e1000_82576) && (size > 15)) {
+ pr_notice("The NVM size is not valid, defaulting to 32K\n");
+ size = 15;
+ }
+
nvm->word_size = 1 << size;
if (hw->mac.type < e1000_i210) {
nvm->opcode_bits = 8;
@@ -281,14 +289,6 @@ static s32 igb_get_invariants_82575(struct e1000_hw *hw)
} else
nvm->type = e1000_nvm_flash_hw;
- /*
- * Check for invalid size
- */
- if ((hw->mac.type == e1000_82576) && (size > 15)) {
- pr_notice("The NVM size is not valid, defaulting to 32K\n");
- size = 15;
- }
-
/* NVM Function Pointers */
switch (hw->mac.type) {
case e1000_82580:
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 10efcd88dca0..28394bea5253 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -156,8 +156,12 @@
: (0x0E018 + ((_n) * 0x40)))
#define E1000_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) \
: (0x0E028 + ((_n) * 0x40)))
-#define E1000_DCA_TXCTRL(_n) (0x03814 + (_n << 8))
-#define E1000_DCA_RXCTRL(_n) (0x02814 + (_n << 8))
+#define E1000_RXCTL(_n) ((_n) < 4 ? (0x02814 + ((_n) * 0x100)) : \
+ (0x0C014 + ((_n) * 0x40)))
+#define E1000_DCA_RXCTRL(_n) E1000_RXCTL(_n)
+#define E1000_TXCTL(_n) ((_n) < 4 ? (0x03814 + ((_n) * 0x100)) : \
+ (0x0E014 + ((_n) * 0x40)))
+#define E1000_DCA_TXCTRL(_n) E1000_TXCTL(_n)
#define E1000_TDWBAL(_n) ((_n) < 4 ? (0x03838 + ((_n) * 0x100)) \
: (0x0E038 + ((_n) * 0x40)))
#define E1000_TDWBAH(_n) ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) \
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index a19c84cad0e9..70591117051b 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -209,8 +209,8 @@ static int igb_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
/* When SoL/IDER sessions are active, autoneg/speed/duplex
* cannot be changed */
if (igb_check_reset_block(hw)) {
- dev_err(&adapter->pdev->dev, "Cannot change link "
- "characteristics when SoL/IDER is active.\n");
+ dev_err(&adapter->pdev->dev,
+ "Cannot change link characteristics when SoL/IDER is active.\n");
return -EINVAL;
}
@@ -1089,8 +1089,8 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data,
wr32(reg, (_test[pat] & write));
val = rd32(reg) & mask;
if (val != (_test[pat] & write & mask)) {
- dev_err(&adapter->pdev->dev, "pattern test reg %04X "
- "failed: got 0x%08X expected 0x%08X\n",
+ dev_err(&adapter->pdev->dev,
+ "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
reg, val, (_test[pat] & write & mask));
*data = reg;
return 1;
@@ -1108,8 +1108,8 @@ static bool reg_set_and_check(struct igb_adapter *adapter, u64 *data,
wr32(reg, write & mask);
val = rd32(reg);
if ((write & mask) != (val & mask)) {
- dev_err(&adapter->pdev->dev, "set/check reg %04X test failed:"
- " got 0x%08X expected 0x%08X\n", reg,
+ dev_err(&adapter->pdev->dev,
+ "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", reg,
(val & mask), (write & mask));
*data = reg;
return 1;
@@ -1171,8 +1171,9 @@ static int igb_reg_test(struct igb_adapter *adapter, u64 *data)
wr32(E1000_STATUS, toggle);
after = rd32(E1000_STATUS) & toggle;
if (value != after) {
- dev_err(&adapter->pdev->dev, "failed STATUS register test "
- "got: 0x%08X expected: 0x%08X\n", after, value);
+ dev_err(&adapter->pdev->dev,
+ "failed STATUS register test got: 0x%08X expected: 0x%08X\n",
+ after, value);
*data = 1;
return 1;
}
@@ -1497,6 +1498,9 @@ static int igb_integrated_phy_loopback(struct igb_adapter *adapter)
break;
}
+ /* add small delay to avoid loopback test failure */
+ msleep(50);
+
/* force 1000, set loopback */
igb_write_phy_reg(hw, PHY_CONTROL, 0x4140);
@@ -1777,16 +1781,14 @@ static int igb_loopback_test(struct igb_adapter *adapter, u64 *data)
* sessions are active */
if (igb_check_reset_block(&adapter->hw)) {
dev_err(&adapter->pdev->dev,
- "Cannot do PHY loopback test "
- "when SoL/IDER is active.\n");
+ "Cannot do PHY loopback test when SoL/IDER is active.\n");
*data = 0;
goto out;
}
if ((adapter->hw.mac.type == e1000_i210)
- || (adapter->hw.mac.type == e1000_i210)) {
+ || (adapter->hw.mac.type == e1000_i211)) {
dev_err(&adapter->pdev->dev,
- "Loopback test not supported "
- "on this part at this time.\n");
+ "Loopback test not supported on this part at this time.\n");
*data = 0;
goto out;
}
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 1050411e7ca3..48cc4fb1a307 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -462,10 +462,10 @@ static void igb_dump(struct igb_adapter *adapter)
(u64)buffer_info->time_stamp,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter) && buffer_info->dma != 0)
+ if (netif_msg_pktdata(adapter) && buffer_info->skb)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS,
- 16, 1, phys_to_virt(buffer_info->dma),
+ 16, 1, buffer_info->skb->data,
buffer_info->length, true);
}
}
@@ -547,18 +547,17 @@ rx_ring_summary:
(u64)buffer_info->dma,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter)) {
+ if (netif_msg_pktdata(adapter) &&
+ buffer_info->dma && buffer_info->skb) {
print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS,
- 16, 1,
- phys_to_virt(buffer_info->dma),
- IGB_RX_HDR_LEN, true);
+ DUMP_PREFIX_ADDRESS,
+ 16, 1, buffer_info->skb->data,
+ IGB_RX_HDR_LEN, true);
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS,
16, 1,
- phys_to_virt(
- buffer_info->page_dma +
- buffer_info->page_offset),
+ page_address(buffer_info->page) +
+ buffer_info->page_offset,
PAGE_SIZE/2, true);
}
}
@@ -6235,7 +6234,7 @@ static bool igb_alloc_mapped_page(struct igb_ring *rx_ring,
return true;
if (!page) {
- page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+ page = __skb_alloc_page(GFP_ATOMIC, bi->skb);
bi->page = page;
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_failed++;
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index 50fc137501da..18bf08c9d7a4 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -804,12 +804,13 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw,
link_mode == IXGBE_AUTOC_LMS_KX4_KX_KR_SGMII) {
/* Set KX4/KX/KR support according to speed requested */
autoc &= ~(IXGBE_AUTOC_KX4_KX_SUPP_MASK | IXGBE_AUTOC_KR_SUPP);
- if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+ if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
if (orig_autoc & IXGBE_AUTOC_KX4_SUPP)
autoc |= IXGBE_AUTOC_KX4_SUPP;
if ((orig_autoc & IXGBE_AUTOC_KR_SUPP) &&
(hw->phy.smart_speed_active == false))
autoc |= IXGBE_AUTOC_KR_SUPP;
+ }
if (speed & IXGBE_LINK_SPEED_1GB_FULL)
autoc |= IXGBE_AUTOC_KX_SUPP;
} else if ((pma_pmd_1g == IXGBE_AUTOC_1G_SFI) &&
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index c709eae58c63..4326f74f7137 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -1141,8 +1141,8 @@ static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring,
/* alloc new page for storage */
if (likely(!page)) {
- page = alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
- ixgbe_rx_pg_order(rx_ring));
+ page = __skb_alloc_pages(GFP_ATOMIC | __GFP_COLD | __GFP_COMP,
+ bi->skb, ixgbe_rx_pg_order(rx_ring));
if (unlikely(!page)) {
rx_ring->rx_stats.alloc_rx_page_failed++;
return false;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 3f9841d619ad..60ef64587412 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -352,7 +352,6 @@ static void ixgbevf_alloc_rx_buffers(struct ixgbevf_adapter *adapter,
adapter->alloc_rx_buff_failed++;
goto no_buffers;
}
-
bi->skb = skb;
}
if (!bi->dma) {
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index f32e70300770..5aba5ecdf1e2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -614,8 +614,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
/* If source MAC is equal to our own MAC and not performing
* the selftest or flb disabled - drop the packet */
if (s_mac == priv->mac &&
- (!(dev->features & NETIF_F_LOOPBACK) ||
- !priv->validate_loopback))
+ !((dev->features & NETIF_F_LOOPBACK) ||
+ priv->validate_loopback))
goto next;
/*
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 019d856b1334..10bba09c44ea 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -164,7 +164,6 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv,
ring->cons = 0xffffffff;
ring->last_nr_txbb = 1;
ring->poll_cnt = 0;
- ring->blocked = 0;
memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info));
memset(ring->buf, 0, ring->buf_size);
@@ -365,14 +364,13 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq)
ring->cons += txbbs_skipped;
netdev_tx_completed_queue(ring->tx_queue, packets, bytes);
- /* Wakeup Tx queue if this ring stopped it */
- if (unlikely(ring->blocked)) {
- if ((u32) (ring->prod - ring->cons) <=
- ring->size - HEADROOM - MAX_DESC_TXBBS) {
- ring->blocked = 0;
- netif_tx_wake_queue(ring->tx_queue);
- priv->port_stats.wake_queue++;
- }
+ /*
+ * Wakeup Tx queue if this stopped, and at least 1 packet
+ * was completed
+ */
+ if (netif_tx_queue_stopped(ring->tx_queue) && txbbs_skipped > 0) {
+ netif_tx_wake_queue(ring->tx_queue);
+ priv->port_stats.wake_queue++;
}
}
@@ -592,7 +590,6 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev)
ring->size - HEADROOM - MAX_DESC_TXBBS)) {
/* every full Tx ring stops queue */
netif_tx_stop_queue(ring->tx_queue);
- ring->blocked = 1;
priv->port_stats.queue_stopped++;
return NETDEV_TX_BUSY;
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index 88b7b3e75ab1..31d02649be41 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -227,9 +227,10 @@ int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev)
MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
}
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
+ u32 i = (obj & (table->num_obj - 1)) /
+ (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
int ret = 0;
mutex_lock(&table->mutex);
@@ -262,16 +263,18 @@ out:
return ret;
}
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i;
+ u32 i;
+ u64 offset;
i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
mutex_lock(&table->mutex);
if (--table->icm[i]->refcount == 0) {
- mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE,
+ offset = (u64) i * MLX4_TABLE_CHUNK_SIZE;
+ mlx4_UNMAP_ICM(dev, table->virt + offset,
MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
mlx4_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
@@ -280,9 +283,11 @@ void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
mutex_unlock(&table->mutex);
}
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle)
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj,
+ dma_addr_t *dma_handle)
{
- int idx, offset, dma_offset, i;
+ int offset, dma_offset, i;
+ u64 idx;
struct mlx4_icm_chunk *chunk;
struct mlx4_icm *icm;
struct page *page = NULL;
@@ -292,7 +297,7 @@ void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_han
mutex_lock(&table->mutex);
- idx = (obj & (table->num_obj - 1)) * table->obj_size;
+ idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size;
icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE];
dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE;
@@ -326,10 +331,11 @@ out:
}
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size;
- int i, err;
+ int err;
+ u32 i;
for (i = start; i <= end; i += inc) {
err = mlx4_table_get(dev, table, i);
@@ -349,22 +355,23 @@ fail:
}
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
- int i;
+ u32 i;
for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size)
mlx4_table_put(dev, table, i);
}
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- u64 virt, int obj_size, int nobj, int reserved,
+ u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent)
{
int obj_per_chunk;
int num_icm;
unsigned chunk_size;
int i;
+ u64 size;
obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size;
num_icm = (nobj + obj_per_chunk - 1) / obj_per_chunk;
@@ -380,10 +387,12 @@ int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
table->coherent = use_coherent;
mutex_init(&table->mutex);
+ size = (u64) nobj * obj_size;
for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) {
chunk_size = MLX4_TABLE_CHUNK_SIZE;
- if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > nobj * obj_size)
- chunk_size = PAGE_ALIGN(nobj * obj_size - i * MLX4_TABLE_CHUNK_SIZE);
+ if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > size)
+ chunk_size = PAGE_ALIGN(size -
+ i * MLX4_TABLE_CHUNK_SIZE);
table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT,
(use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h
index 19e4efc0b342..dee67fa39107 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.h
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.h
@@ -71,17 +71,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
gfp_t gfp_mask, int coherent);
void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent);
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- u64 virt, int obj_size, int nobj, int reserved,
+ u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent);
void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table);
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle);
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, dma_addr_t *dma_handle);
static inline void mlx4_icm_first(struct mlx4_icm *icm,
struct mlx4_icm_iter *iter)
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 48d0e90194cb..2f816c6aed72 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -157,9 +157,6 @@ int mlx4_check_port_params(struct mlx4_dev *dev,
"on this HCA, aborting.\n");
return -EINVAL;
}
- if (port_type[i] == MLX4_PORT_TYPE_ETH &&
- port_type[i + 1] == MLX4_PORT_TYPE_IB)
- return -EINVAL;
}
}
@@ -1237,13 +1234,13 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
mlx4_info(dev, "non-primary physical function, skipping.\n");
else
mlx4_err(dev, "QUERY_FW command failed, aborting.\n");
- goto unmap_bf;
+ return err;
}
err = mlx4_load_fw(dev);
if (err) {
mlx4_err(dev, "Failed to start FW, aborting.\n");
- goto unmap_bf;
+ return err;
}
mlx4_cfg.log_pg_sz_m = 1;
@@ -1307,7 +1304,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
err = mlx4_init_slave(dev);
if (err) {
mlx4_err(dev, "Failed to initialize slave\n");
- goto unmap_bf;
+ return err;
}
err = mlx4_slave_cap(dev);
@@ -1327,7 +1324,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
err = mlx4_QUERY_ADAPTER(dev, &adapter);
if (err) {
mlx4_err(dev, "QUERY_ADAPTER command failed, aborting.\n");
- goto err_close;
+ goto unmap_bf;
}
priv->eq_table.inta_pin = adapter.inta_pin;
@@ -1335,6 +1332,9 @@ static int mlx4_init_hca(struct mlx4_dev *dev)
return 0;
+unmap_bf:
+ unmap_bf_area(dev);
+
err_close:
mlx4_close_hca(dev);
@@ -1347,8 +1347,6 @@ err_stop_fw:
mlx4_UNMAP_FA(dev);
mlx4_free_icm(dev, priv->fw.fw_icm, 0);
}
-unmap_bf:
- unmap_bf_area(dev);
return err;
}
@@ -1999,7 +1997,8 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
}
slave_start:
- if (mlx4_cmd_init(dev)) {
+ err = mlx4_cmd_init(dev);
+ if (err) {
mlx4_err(dev, "Failed to init command interface, aborting.\n");
goto err_sriov;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index 4ec3835e1bc2..e151c21baf2b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -137,11 +137,11 @@ static int mlx4_GID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
return err;
}
-static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 pf_num,
+static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 port,
enum mlx4_steer_type steer,
u32 qpn)
{
- struct mlx4_steer *s_steer = &mlx4_priv(dev)->steer[pf_num];
+ struct mlx4_steer *s_steer = &mlx4_priv(dev)->steer[port - 1];
struct mlx4_promisc_qp *pqp;
list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) {
@@ -182,7 +182,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port,
/* If the given qpn is also a promisc qp,
* it should be inserted to duplicates list
*/
- pqp = get_promisc_qp(dev, 0, steer, qpn);
+ pqp = get_promisc_qp(dev, port, steer, qpn);
if (pqp) {
dqp = kmalloc(sizeof *dqp, GFP_KERNEL);
if (!dqp) {
@@ -256,7 +256,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port,
s_steer = &mlx4_priv(dev)->steer[port - 1];
- pqp = get_promisc_qp(dev, 0, steer, qpn);
+ pqp = get_promisc_qp(dev, port, steer, qpn);
if (!pqp)
return 0; /* nothing to do */
@@ -302,7 +302,7 @@ static bool check_duplicate_entry(struct mlx4_dev *dev, u8 port,
s_steer = &mlx4_priv(dev)->steer[port - 1];
/* if qp is not promisc, it cannot be duplicated */
- if (!get_promisc_qp(dev, 0, steer, qpn))
+ if (!get_promisc_qp(dev, port, steer, qpn))
return false;
/* The qp is promisc qp so it is a duplicate on this index
@@ -352,7 +352,7 @@ static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 port,
members_count = be32_to_cpu(mgm->members_count) & 0xffffff;
for (i = 0; i < members_count; i++) {
qpn = be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK;
- if (!get_promisc_qp(dev, 0, steer, qpn) && qpn != tqpn) {
+ if (!get_promisc_qp(dev, port, steer, qpn) && qpn != tqpn) {
/* the qp is not promisc, the entry can't be removed */
goto out;
}
@@ -398,7 +398,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port,
mutex_lock(&priv->mcg_table.mutex);
- if (get_promisc_qp(dev, 0, steer, qpn)) {
+ if (get_promisc_qp(dev, port, steer, qpn)) {
err = 0; /* Noting to do, already exists */
goto out_mutex;
}
@@ -432,8 +432,10 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port,
if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qpn) {
/* Entry already exists, add to duplicates */
dqp = kmalloc(sizeof *dqp, GFP_KERNEL);
- if (!dqp)
+ if (!dqp) {
+ err = -ENOMEM;
goto out_mailbox;
+ }
dqp->qpn = qpn;
list_add_tail(&dqp->list, &entry->duplicates);
found = true;
@@ -501,7 +503,7 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port,
s_steer = &mlx4_priv(dev)->steer[port - 1];
mutex_lock(&priv->mcg_table.mutex);
- pqp = get_promisc_qp(dev, 0, steer, qpn);
+ pqp = get_promisc_qp(dev, port, steer, qpn);
if (unlikely(!pqp)) {
mlx4_warn(dev, "QP %x is not promiscuous QP\n", qpn);
/* nothing to do */
@@ -648,13 +650,6 @@ static int find_entry(struct mlx4_dev *dev, u8 port,
return err;
}
-struct mlx4_net_trans_rule_hw_ctrl {
- __be32 ctrl;
- __be32 vf_vep_port;
- __be32 qpn;
- __be32 reserved;
-};
-
static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
struct mlx4_net_trans_rule_hw_ctrl *hw)
{
@@ -678,87 +673,18 @@ static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl,
hw->qpn = cpu_to_be32(ctrl->qpn);
}
-struct mlx4_net_trans_rule_hw_ib {
- u8 size;
- u8 rsvd1;
- __be16 id;
- u32 rsvd2;
- __be32 qpn;
- __be32 qpn_mask;
- u8 dst_gid[16];
- u8 dst_gid_msk[16];
-} __packed;
-
-struct mlx4_net_trans_rule_hw_eth {
- u8 size;
- u8 rsvd;
- __be16 id;
- u8 rsvd1[6];
- u8 dst_mac[6];
- u16 rsvd2;
- u8 dst_mac_msk[6];
- u16 rsvd3;
- u8 src_mac[6];
- u16 rsvd4;
- u8 src_mac_msk[6];
- u8 rsvd5;
- u8 ether_type_enable;
- __be16 ether_type;
- __be16 vlan_id_msk;
- __be16 vlan_id;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_tcp_udp {
- u8 size;
- u8 rsvd;
- __be16 id;
- __be16 rsvd1[3];
- __be16 dst_port;
- __be16 rsvd2;
- __be16 dst_port_msk;
- __be16 rsvd3;
- __be16 src_port;
- __be16 rsvd4;
- __be16 src_port_msk;
-} __packed;
-
-struct mlx4_net_trans_rule_hw_ipv4 {
- u8 size;
- u8 rsvd;
- __be16 id;
- __be32 rsvd1;
- __be32 dst_ip;
- __be32 dst_ip_msk;
- __be32 src_ip;
- __be32 src_ip_msk;
-} __packed;
-
-struct _rule_hw {
- union {
- struct {
- u8 size;
- u8 rsvd;
- __be16 id;
- };
- struct mlx4_net_trans_rule_hw_eth eth;
- struct mlx4_net_trans_rule_hw_ib ib;
- struct mlx4_net_trans_rule_hw_ipv4 ipv4;
- struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
- };
+const u16 __sw_id_hw[] = {
+ [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001,
+ [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005,
+ [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003,
+ [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002,
+ [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004,
+ [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006
};
static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec,
struct _rule_hw *rule_hw)
{
- static const u16 __sw_id_hw[] = {
- [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001,
- [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005,
- [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003,
- [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002,
- [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004,
- [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006
- };
-
static const size_t __rule_hw_sz[] = {
[MLX4_NET_TRANS_RULE_ID_ETH] =
sizeof(struct mlx4_net_trans_rule_hw_eth),
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index 59ebc0339638..dba69d98734a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -249,7 +249,7 @@ struct mlx4_bitmap {
struct mlx4_buddy {
unsigned long **bits;
unsigned int *num_free;
- int max_order;
+ u32 max_order;
spinlock_t lock;
};
@@ -258,7 +258,7 @@ struct mlx4_icm;
struct mlx4_icm_table {
u64 virt;
int num_icm;
- int num_obj;
+ u32 num_obj;
int obj_size;
int lowmem;
int coherent;
@@ -690,6 +690,82 @@ struct mlx4_steer {
struct list_head steer_entries[MLX4_NUM_STEERS];
};
+struct mlx4_net_trans_rule_hw_ctrl {
+ __be32 ctrl;
+ __be32 vf_vep_port;
+ __be32 qpn;
+ __be32 reserved;
+};
+
+struct mlx4_net_trans_rule_hw_ib {
+ u8 size;
+ u8 rsvd1;
+ __be16 id;
+ u32 rsvd2;
+ __be32 qpn;
+ __be32 qpn_mask;
+ u8 dst_gid[16];
+ u8 dst_gid_msk[16];
+} __packed;
+
+struct mlx4_net_trans_rule_hw_eth {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ u8 rsvd1[6];
+ u8 dst_mac[6];
+ u16 rsvd2;
+ u8 dst_mac_msk[6];
+ u16 rsvd3;
+ u8 src_mac[6];
+ u16 rsvd4;
+ u8 src_mac_msk[6];
+ u8 rsvd5;
+ u8 ether_type_enable;
+ __be16 ether_type;
+ __be16 vlan_id_msk;
+ __be16 vlan_id;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_tcp_udp {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ __be16 rsvd1[3];
+ __be16 dst_port;
+ __be16 rsvd2;
+ __be16 dst_port_msk;
+ __be16 rsvd3;
+ __be16 src_port;
+ __be16 rsvd4;
+ __be16 src_port_msk;
+} __packed;
+
+struct mlx4_net_trans_rule_hw_ipv4 {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ __be32 rsvd1;
+ __be32 dst_ip;
+ __be32 dst_ip_msk;
+ __be32 src_ip;
+ __be32 src_ip_msk;
+} __packed;
+
+struct _rule_hw {
+ union {
+ struct {
+ u8 size;
+ u8 rsvd;
+ __be16 id;
+ };
+ struct mlx4_net_trans_rule_hw_eth eth;
+ struct mlx4_net_trans_rule_hw_ib ib;
+ struct mlx4_net_trans_rule_hw_ipv4 ipv4;
+ struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp;
+ };
+};
+
struct mlx4_priv {
struct mlx4_dev dev;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 5f1ab105debc..9d27e42264e2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -248,7 +248,6 @@ struct mlx4_en_tx_ring {
u32 doorbell_qpn;
void *buf;
u16 poll_cnt;
- int blocked;
struct mlx4_en_tx_info *tx_info;
u8 *bounce_buf;
u32 last_nr_txbb;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c
index af55b7ce5341..c202d3ad2a0e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mr.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mr.c
@@ -37,6 +37,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/vmalloc.h>
#include <linux/mlx4/cmd.h>
@@ -120,7 +121,7 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
buddy->max_order = max_order;
spin_lock_init(&buddy->lock);
- buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
+ buddy->bits = kcalloc(buddy->max_order + 1, sizeof (long *),
GFP_KERNEL);
buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
@@ -129,10 +130,12 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
for (i = 0; i <= buddy->max_order; ++i) {
s = BITS_TO_LONGS(1 << (buddy->max_order - i));
- buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL);
- if (!buddy->bits[i])
- goto err_out_free;
- bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i));
+ buddy->bits[i] = kcalloc(s, sizeof (long), GFP_KERNEL | __GFP_NOWARN);
+ if (!buddy->bits[i]) {
+ buddy->bits[i] = vzalloc(s * sizeof(long));
+ if (!buddy->bits[i])
+ goto err_out_free;
+ }
}
set_bit(0, buddy->bits[buddy->max_order]);
@@ -142,7 +145,10 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order)
err_out_free:
for (i = 0; i <= buddy->max_order; ++i)
- kfree(buddy->bits[i]);
+ if (buddy->bits[i] && is_vmalloc_addr(buddy->bits[i]))
+ vfree(buddy->bits[i]);
+ else
+ kfree(buddy->bits[i]);
err_out:
kfree(buddy->bits);
@@ -156,7 +162,10 @@ static void mlx4_buddy_cleanup(struct mlx4_buddy *buddy)
int i;
for (i = 0; i <= buddy->max_order; ++i)
- kfree(buddy->bits[i]);
+ if (is_vmalloc_addr(buddy->bits[i]))
+ vfree(buddy->bits[i]);
+ else
+ kfree(buddy->bits[i]);
kfree(buddy->bits);
kfree(buddy->num_free);
@@ -668,7 +677,7 @@ int mlx4_init_mr_table(struct mlx4_dev *dev)
return err;
err = mlx4_buddy_init(&mr_table->mtt_buddy,
- ilog2(dev->caps.num_mtts /
+ ilog2((u32)dev->caps.num_mtts /
(1 << log_mtts_per_seg)));
if (err)
goto err_buddy;
@@ -678,7 +687,7 @@ int mlx4_init_mr_table(struct mlx4_dev *dev)
mlx4_alloc_mtt_range(dev,
fls(dev->caps.reserved_mtts - 1));
if (priv->reserved_mtts < 0) {
- mlx4_warn(dev, "MTT table of order %d is too small.\n",
+ mlx4_warn(dev, "MTT table of order %u is too small.\n",
mr_table->mtt_buddy.max_order);
err = -ENOMEM;
goto err_reserve_mtts;
diff --git a/drivers/net/ethernet/mellanox/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c
index 9ee4725363d5..8e0c3cc2a1ec 100644
--- a/drivers/net/ethernet/mellanox/mlx4/profile.c
+++ b/drivers/net/ethernet/mellanox/mlx4/profile.c
@@ -76,7 +76,7 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
u64 size;
u64 start;
int type;
- int num;
+ u32 num;
int log_num;
};
@@ -105,7 +105,7 @@ u64 mlx4_make_profile(struct mlx4_dev *dev,
si_meminfo(&si);
request->num_mtt =
roundup_pow_of_two(max_t(unsigned, request->num_mtt,
- min(1UL << 31,
+ min(1UL << (31 - log_mtts_per_seg),
si.totalram >> (log_mtts_per_seg - 1))));
profile[MLX4_RES_QP].size = dev_cap->qpc_entry_sz;
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index 94ceddd17ab2..293c9e820c49 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -42,6 +42,7 @@
#include <linux/mlx4/cmd.h>
#include <linux/mlx4/qp.h>
#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
#include "mlx4.h"
#include "fw.h"
@@ -2776,18 +2777,133 @@ ex_put:
return err;
}
+/*
+ * MAC validation for Flow Steering rules.
+ * VF can attach rules only with a mac address which is assigned to it.
+ */
+static int validate_eth_header_mac(int slave, struct _rule_hw *eth_header,
+ struct list_head *rlist)
+{
+ struct mac_res *res, *tmp;
+ __be64 be_mac;
+
+ /* make sure it isn't multicast or broadcast mac*/
+ if (!is_multicast_ether_addr(eth_header->eth.dst_mac) &&
+ !is_broadcast_ether_addr(eth_header->eth.dst_mac)) {
+ list_for_each_entry_safe(res, tmp, rlist, list) {
+ be_mac = cpu_to_be64(res->mac << 16);
+ if (!memcmp(&be_mac, eth_header->eth.dst_mac, ETH_ALEN))
+ return 0;
+ }
+ pr_err("MAC %pM doesn't belong to VF %d, Steering rule rejected\n",
+ eth_header->eth.dst_mac, slave);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * In case of missing eth header, append eth header with a MAC address
+ * assigned to the VF.
+ */
+static int add_eth_header(struct mlx4_dev *dev, int slave,
+ struct mlx4_cmd_mailbox *inbox,
+ struct list_head *rlist, int header_id)
+{
+ struct mac_res *res, *tmp;
+ u8 port;
+ struct mlx4_net_trans_rule_hw_ctrl *ctrl;
+ struct mlx4_net_trans_rule_hw_eth *eth_header;
+ struct mlx4_net_trans_rule_hw_ipv4 *ip_header;
+ struct mlx4_net_trans_rule_hw_tcp_udp *l4_header;
+ __be64 be_mac = 0;
+ __be64 mac_msk = cpu_to_be64(MLX4_MAC_MASK << 16);
+
+ ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
+ port = be32_to_cpu(ctrl->vf_vep_port) & 0xff;
+ eth_header = (struct mlx4_net_trans_rule_hw_eth *)(ctrl + 1);
+
+ /* Clear a space in the inbox for eth header */
+ switch (header_id) {
+ case MLX4_NET_TRANS_RULE_ID_IPV4:
+ ip_header =
+ (struct mlx4_net_trans_rule_hw_ipv4 *)(eth_header + 1);
+ memmove(ip_header, eth_header,
+ sizeof(*ip_header) + sizeof(*l4_header));
+ break;
+ case MLX4_NET_TRANS_RULE_ID_TCP:
+ case MLX4_NET_TRANS_RULE_ID_UDP:
+ l4_header = (struct mlx4_net_trans_rule_hw_tcp_udp *)
+ (eth_header + 1);
+ memmove(l4_header, eth_header, sizeof(*l4_header));
+ break;
+ default:
+ return -EINVAL;
+ }
+ list_for_each_entry_safe(res, tmp, rlist, list) {
+ if (port == res->port) {
+ be_mac = cpu_to_be64(res->mac << 16);
+ break;
+ }
+ }
+ if (!be_mac) {
+ pr_err("Failed adding eth header to FS rule, Can't find matching MAC for port %d .\n",
+ port);
+ return -EINVAL;
+ }
+
+ memset(eth_header, 0, sizeof(*eth_header));
+ eth_header->size = sizeof(*eth_header) >> 2;
+ eth_header->id = cpu_to_be16(__sw_id_hw[MLX4_NET_TRANS_RULE_ID_ETH]);
+ memcpy(eth_header->dst_mac, &be_mac, ETH_ALEN);
+ memcpy(eth_header->dst_mac_msk, &mac_msk, ETH_ALEN);
+
+ return 0;
+
+}
+
int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,
struct mlx4_vhcr *vhcr,
struct mlx4_cmd_mailbox *inbox,
struct mlx4_cmd_mailbox *outbox,
struct mlx4_cmd_info *cmd)
{
+
+ struct mlx4_priv *priv = mlx4_priv(dev);
+ struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;
+ struct list_head *rlist = &tracker->slave_list[slave].res_list[RES_MAC];
int err;
+ struct mlx4_net_trans_rule_hw_ctrl *ctrl;
+ struct _rule_hw *rule_header;
+ int header_id;
if (dev->caps.steering_mode !=
MLX4_STEERING_MODE_DEVICE_MANAGED)
return -EOPNOTSUPP;
+ ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf;
+ rule_header = (struct _rule_hw *)(ctrl + 1);
+ header_id = map_hw_to_sw_id(be16_to_cpu(rule_header->id));
+
+ switch (header_id) {
+ case MLX4_NET_TRANS_RULE_ID_ETH:
+ if (validate_eth_header_mac(slave, rule_header, rlist))
+ return -EINVAL;
+ break;
+ case MLX4_NET_TRANS_RULE_ID_IPV4:
+ case MLX4_NET_TRANS_RULE_ID_TCP:
+ case MLX4_NET_TRANS_RULE_ID_UDP:
+ pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n");
+ if (add_eth_header(dev, slave, inbox, rlist, header_id))
+ return -EINVAL;
+ vhcr->in_modifier +=
+ sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;
+ break;
+ default:
+ pr_err("Corrupted mailbox.\n");
+ return -EINVAL;
+ }
+
err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param,
vhcr->in_modifier, 0,
MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,
diff --git a/drivers/net/ethernet/mellanox/mlx4/sense.c b/drivers/net/ethernet/mellanox/mlx4/sense.c
index 802498293528..34ee09bae36e 100644
--- a/drivers/net/ethernet/mellanox/mlx4/sense.c
+++ b/drivers/net/ethernet/mellanox/mlx4/sense.c
@@ -81,20 +81,6 @@ void mlx4_do_sense_ports(struct mlx4_dev *dev,
}
/*
- * Adjust port configuration:
- * If port 1 sensed nothing and port 2 is IB, set both as IB
- * If port 2 sensed nothing and port 1 is Eth, set both as Eth
- */
- if (stype[0] == MLX4_PORT_TYPE_ETH) {
- for (i = 1; i < dev->caps.num_ports; i++)
- stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_ETH;
- }
- if (stype[dev->caps.num_ports - 1] == MLX4_PORT_TYPE_IB) {
- for (i = 0; i < dev->caps.num_ports - 1; i++)
- stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_IB;
- }
-
- /*
* If sensed nothing, remain in current configuration.
*/
for (i = 0; i < dev->caps.num_ports; i++)
diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
index 4069edab229e..53743f7a2ca9 100644
--- a/drivers/net/ethernet/nxp/lpc_eth.c
+++ b/drivers/net/ethernet/nxp/lpc_eth.c
@@ -346,28 +346,15 @@ static phy_interface_t lpc_phy_interface_mode(struct device *dev)
"phy-mode", NULL);
if (mode && !strcmp(mode, "mii"))
return PHY_INTERFACE_MODE_MII;
- return PHY_INTERFACE_MODE_RMII;
}
-
- /* non-DT */
-#ifdef CONFIG_ARCH_LPC32XX_MII_SUPPORT
- return PHY_INTERFACE_MODE_MII;
-#else
return PHY_INTERFACE_MODE_RMII;
-#endif
}
static bool use_iram_for_net(struct device *dev)
{
if (dev && dev->of_node)
return of_property_read_bool(dev->of_node, "use-iram");
-
- /* non-DT */
-#ifdef CONFIG_ARCH_LPC32XX_IRAM_FOR_NET
- return true;
-#else
return false;
-#endif
}
/* Receive Status information word */
diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index c42bbb16cdae..a688a2ddcfd6 100644
--- a/drivers/net/ethernet/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -722,10 +722,8 @@ static int octeon_mgmt_init_phy(struct net_device *netdev)
octeon_mgmt_adjust_link, 0,
PHY_INTERFACE_MODE_MII);
- if (IS_ERR(p->phydev)) {
- p->phydev = NULL;
+ if (!p->phydev)
return -1;
- }
phy_start_aneg(p->phydev);
diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c
index e559dfa06d6a..6fa74d530e44 100644
--- a/drivers/net/ethernet/pasemi/pasemi_mac.c
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.c
@@ -1101,9 +1101,9 @@ static int pasemi_mac_phy_init(struct net_device *dev)
phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0,
PHY_INTERFACE_MODE_SGMII);
- if (IS_ERR(phydev)) {
+ if (!phydev) {
printk(KERN_ERR "%s: Could not attach to phy\n", dev->name);
- return PTR_ERR(phydev);
+ return -ENODEV;
}
mac->phydev = phydev;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index 342b3a79bd0f..a77c558d8f40 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -1378,6 +1378,10 @@ static void netxen_mask_aer_correctable(struct netxen_adapter *adapter)
struct pci_dev *root = pdev->bus->self;
u32 aer_pos;
+ /* root bus? */
+ if (!root)
+ return;
+
if (adapter->ahw.board_type != NETXEN_BRDTYPE_P3_4_GB_MM &&
adapter->ahw.board_type != NETXEN_BRDTYPE_P3_10G_TP)
return;
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index b8ead696141e..2a179d087207 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -15,7 +15,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
do {
/* give atleast 1ms for firmware to respond */
- msleep(1);
+ mdelay(1);
if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
return QLCNIC_CDRP_RSP_TIMEOUT;
@@ -601,7 +601,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
qlcnic_fw_cmd_destroy_tx_ctx(adapter);
/* Allow dma queues to drain after context reset */
- msleep(20);
+ mdelay(20);
}
}
diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig
index 46df3a04030c..24c2305d7948 100644
--- a/drivers/net/ethernet/renesas/Kconfig
+++ b/drivers/net/ethernet/renesas/Kconfig
@@ -8,7 +8,7 @@ config SH_ETH
(CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \
CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \
CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \
- CPU_SUBTYPE_SH7757 || ARCH_R8A7740)
+ CPU_SUBTYPE_SH7757 || ARCH_R8A7740 || ARCH_R8A7779)
select CRC32
select NET_CORE
select MII
@@ -18,4 +18,4 @@ config SH_ETH
Renesas SuperH Ethernet device driver.
This driver supporting CPUs are:
- SH7619, SH7710, SH7712, SH7724, SH7734, SH7763, SH7757,
- and R8A7740.
+ R8A7740 and R8A7779.
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index af0b867a6cf6..bad8f2eec9b4 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -78,7 +78,7 @@ static void sh_eth_select_mii(struct net_device *ndev)
#endif
/* There is CPU dependent code */
-#if defined(CONFIG_CPU_SUBTYPE_SH7724)
+#if defined(CONFIG_CPU_SUBTYPE_SH7724) || defined(CONFIG_ARCH_R8A7779)
#define SH_ETH_RESET_DEFAULT 1
static void sh_eth_set_duplex(struct net_device *ndev)
{
@@ -93,13 +93,18 @@ static void sh_eth_set_duplex(struct net_device *ndev)
static void sh_eth_set_rate(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
+ unsigned int bits = ECMR_RTM;
+
+#if defined(CONFIG_ARCH_R8A7779)
+ bits |= ECMR_ELB;
+#endif
switch (mdp->speed) {
case 10: /* 10BASE */
- sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_RTM, ECMR);
+ sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~bits, ECMR);
break;
case 100:/* 100BASE */
- sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_RTM, ECMR);
+ sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | bits, ECMR);
break;
default:
break;
diff --git a/drivers/net/ethernet/seeq/sgiseeq.c b/drivers/net/ethernet/seeq/sgiseeq.c
index bb8c8222122b..4d15bf413bdc 100644
--- a/drivers/net/ethernet/seeq/sgiseeq.c
+++ b/drivers/net/ethernet/seeq/sgiseeq.c
@@ -751,6 +751,7 @@ static int __devinit sgiseeq_probe(struct platform_device *pdev)
sp->srings = sr;
sp->rx_desc = sp->srings->rxvector;
sp->tx_desc = sp->srings->txvector;
+ spin_lock_init(&sp->tx_lock);
/* A couple calculations now, saves many cycles later. */
setup_rx_ring(dev, sp->rx_desc, SEEQ_RX_BUFFERS);
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 70554a1b2b02..65a8d49106a4 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1503,6 +1503,11 @@ static int efx_probe_all(struct efx_nic *efx)
goto fail2;
}
+ BUILD_BUG_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_RXQ_MIN_ENT);
+ if (WARN_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_TXQ_MIN_ENT(efx))) {
+ rc = -EINVAL;
+ goto fail3;
+ }
efx->rxq_entries = efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE;
rc = efx_probe_filters(efx);
@@ -2070,6 +2075,7 @@ static int efx_register_netdev(struct efx_nic *efx)
net_dev->irq = efx->pci_dev->irq;
net_dev->netdev_ops = &efx_netdev_ops;
SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+ net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
rtnl_lock();
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index be8f9158a714..70755c97251a 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -30,6 +30,7 @@ extern netdev_tx_t
efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
+extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
/* RX */
extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
@@ -52,10 +53,15 @@ extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue);
#define EFX_MAX_EVQ_SIZE 16384UL
#define EFX_MIN_EVQ_SIZE 512UL
-/* The smallest [rt]xq_entries that the driver supports. Callers of
- * efx_wake_queue() assume that they can subsequently send at least one
- * skb. Falcon/A1 may require up to three descriptors per skb_frag. */
-#define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS))
+/* Maximum number of TCP segments we support for soft-TSO */
+#define EFX_TSO_MAX_SEGS 100
+
+/* The smallest [rt]xq_entries that the driver supports. RX minimum
+ * is a bit arbitrary. For TX, we must have space for at least 2
+ * TSO skbs.
+ */
+#define EFX_RXQ_MIN_ENT 128U
+#define EFX_TXQ_MIN_ENT(efx) (2 * efx_tx_max_skb_descs(efx))
/* Filters */
extern int efx_probe_filters(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index 10536f93b561..5faedd855b77 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -680,21 +680,27 @@ static int efx_ethtool_set_ringparam(struct net_device *net_dev,
struct ethtool_ringparam *ring)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ u32 txq_entries;
if (ring->rx_mini_pending || ring->rx_jumbo_pending ||
ring->rx_pending > EFX_MAX_DMAQ_SIZE ||
ring->tx_pending > EFX_MAX_DMAQ_SIZE)
return -EINVAL;
- if (ring->rx_pending < EFX_MIN_RING_SIZE ||
- ring->tx_pending < EFX_MIN_RING_SIZE) {
+ if (ring->rx_pending < EFX_RXQ_MIN_ENT) {
netif_err(efx, drv, efx->net_dev,
- "TX and RX queues cannot be smaller than %ld\n",
- EFX_MIN_RING_SIZE);
+ "RX queues cannot be smaller than %u\n",
+ EFX_RXQ_MIN_ENT);
return -EINVAL;
}
- return efx_realloc_channels(efx, ring->rx_pending, ring->tx_pending);
+ txq_entries = max(ring->tx_pending, EFX_TXQ_MIN_ENT(efx));
+ if (txq_entries != ring->tx_pending)
+ netif_warn(efx, drv, efx->net_dev,
+ "increasing TX queue size to minimum of %u\n",
+ txq_entries);
+
+ return efx_realloc_channels(efx, ring->rx_pending, txq_entries);
}
static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
@@ -857,8 +863,8 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx,
&ip_entry->ip4dst, &ip_entry->pdst);
if (rc != 0) {
rc = efx_filter_get_ipv4_full(
- &spec, &proto, &ip_entry->ip4src, &ip_entry->psrc,
- &ip_entry->ip4dst, &ip_entry->pdst);
+ &spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst,
+ &ip_entry->ip4src, &ip_entry->psrc);
EFX_WARN_ON_PARANOID(rc);
ip_mask->ip4src = ~0;
ip_mask->psrc = ~0;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 9b225a7769f7..18713436b443 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -119,6 +119,25 @@ efx_max_tx_len(struct efx_nic *efx, dma_addr_t dma_addr)
return len;
}
+unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
+{
+ /* Header and payload descriptor for each output segment, plus
+ * one for every input fragment boundary within a segment
+ */
+ unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
+
+ /* Possibly one more per segment for the alignment workaround */
+ if (EFX_WORKAROUND_5391(efx))
+ max_descs += EFX_TSO_MAX_SEGS;
+
+ /* Possibly more for PCIe page boundaries within input fragments */
+ if (PAGE_SIZE > EFX_PAGE_SIZE)
+ max_descs += max_t(unsigned int, MAX_SKB_FRAGS,
+ DIV_ROUND_UP(GSO_MAX_SIZE, EFX_PAGE_SIZE));
+
+ return max_descs;
+}
+
/*
* Add a socket buffer to a TX queue
*
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index e2d083228f3a..719be3912aa9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -22,6 +22,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/phy.h>
@@ -366,3 +369,5 @@ extern void stmmac_set_mac(void __iomem *ioaddr, bool enable);
extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr);
extern const struct stmmac_ring_mode_ops ring_mode_ops;
+
+#endif /* __COMMON_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs.h b/drivers/net/ethernet/stmicro/stmmac/descs.h
index 9820ec842cc0..223adf95fd03 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs.h
@@ -20,6 +20,10 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+
+#ifndef __DESCS_H__
+#define __DESCS_H__
+
struct dma_desc {
/* Receive descriptor */
union {
@@ -166,3 +170,5 @@ enum tdes_csum_insertion {
* is not calculated */
cic_full = 3, /* IP header and pseudoheader */
};
+
+#endif /* __DESCS_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/descs_com.h b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
index dd8d6e19dff6..7ee9499a6e38 100644
--- a/drivers/net/ethernet/stmicro/stmmac/descs_com.h
+++ b/drivers/net/ethernet/stmicro/stmmac/descs_com.h
@@ -27,6 +27,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __DESC_COM_H__
+#define __DESC_COM_H__
+
#if defined(CONFIG_STMMAC_RING)
static inline void ehn_desc_rx_set_on_ring_chain(struct dma_desc *p, int end)
{
@@ -124,3 +127,5 @@ static inline void norm_set_tx_desc_len(struct dma_desc *p, int len)
p->des01.tx.buffer1_size = len;
}
#endif
+
+#endif /* __DESC_COM_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
index 7c6d857a9cc7..2ec6aeae349e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100.h
@@ -22,6 +22,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __DWMAC100_H__
+#define __DWMAC100_H__
+
#include <linux/phy.h>
#include "common.h"
@@ -119,3 +122,5 @@ enum ttc_control {
#define DMA_MISSED_FRAME_M_CNTR 0x0000ffff /* Missed Frame Couinter */
extern const struct stmmac_dma_ops dwmac100_dma_ops;
+
+#endif /* __DWMAC100_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index f90fcb5f9573..0e4cacedc1f0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -19,6 +19,8 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __DWMAC1000_H__
+#define __DWMAC1000_H__
#include <linux/phy.h>
#include "common.h"
@@ -229,6 +231,7 @@ enum rtc_control {
#define GMAC_MMC_RX_CSUM_OFFLOAD 0x208
/* Synopsys Core versions */
-#define DWMAC_CORE_3_40 34
+#define DWMAC_CORE_3_40 0x34
extern const struct stmmac_dma_ops dwmac1000_dma_ops;
+#endif /* __DWMAC1000_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index e678ce39d014..e49c9a0fd6ff 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -22,6 +22,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __DWMAC_DMA_H__
+#define __DWMAC_DMA_H__
+
/* DMA CRS Control and Status Register Mapping */
#define DMA_BUS_MODE 0x00001000 /* Bus Mode */
#define DMA_XMT_POLL_DEMAND 0x00001004 /* Transmit Poll Demand */
@@ -109,3 +112,5 @@ extern void dwmac_dma_start_rx(void __iomem *ioaddr);
extern void dwmac_dma_stop_rx(void __iomem *ioaddr);
extern int dwmac_dma_interrupt(void __iomem *ioaddr,
struct stmmac_extra_stats *x);
+
+#endif /* __DWMAC_DMA_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc.h b/drivers/net/ethernet/stmicro/stmmac/mmc.h
index a38352024cb8..67995ef25251 100644
--- a/drivers/net/ethernet/stmicro/stmmac/mmc.h
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc.h
@@ -22,6 +22,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __MMC_H__
+#define __MMC_H__
+
/* MMC control register */
/* When set, all counter are reset */
#define MMC_CNTRL_COUNTER_RESET 0x1
@@ -129,3 +132,5 @@ struct stmmac_counters {
extern void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode);
extern void dwmac_mmc_intr_all_mask(void __iomem *ioaddr);
extern void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc);
+
+#endif /* __MMC_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
index c07cfe989f6e..0c74a702d461 100644
--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
@@ -33,7 +33,7 @@
#define MMC_TX_INTR 0x00000108 /* MMC TX Interrupt */
#define MMC_RX_INTR_MASK 0x0000010c /* MMC Interrupt Mask */
#define MMC_TX_INTR_MASK 0x00000110 /* MMC Interrupt Mask */
-#define MMC_DEFAUL_MASK 0xffffffff
+#define MMC_DEFAULT_MASK 0xffffffff
/* MMC TX counter registers */
@@ -147,8 +147,8 @@ void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode)
/* To mask all all interrupts.*/
void dwmac_mmc_intr_all_mask(void __iomem *ioaddr)
{
- writel(MMC_DEFAUL_MASK, ioaddr + MMC_RX_INTR_MASK);
- writel(MMC_DEFAUL_MASK, ioaddr + MMC_TX_INTR_MASK);
+ writel(MMC_DEFAULT_MASK, ioaddr + MMC_RX_INTR_MASK);
+ writel(MMC_DEFAULT_MASK, ioaddr + MMC_TX_INTR_MASK);
}
/* This reads the MAC core counters (if actaully supported).
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index f2d3665430ad..e872e1da3137 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -20,6 +20,9 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __STMMAC_H__
+#define __STMMAC_H__
+
#define STMMAC_RESOURCE_NAME "stmmaceth"
#define DRV_MODULE_VERSION "March_2012"
@@ -166,3 +169,5 @@ static inline void stmmac_unregister_pci(void)
{
}
#endif /* CONFIG_STMMAC_PCI */
+
+#endif /* __STMMAC_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index fd8882f9602a..3be88331d17a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1066,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
} else
priv->tm->enable = 1;
#endif
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
stmmac_check_ether_addr(priv);
@@ -1188,7 +1188,7 @@ open_error:
if (priv->phydev)
phy_disconnect(priv->phydev);
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return ret;
}
@@ -1246,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
#ifdef CONFIG_STMMAC_DEBUG_FS
stmmac_exit_fs();
#endif
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return 0;
}
@@ -2077,7 +2077,7 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device,
goto error_netdev_register;
}
- priv->stmmac_clk = clk_get(priv->device, NULL);
+ priv->stmmac_clk = clk_get(priv->device, STMMAC_RESOURCE_NAME);
if (IS_ERR(priv->stmmac_clk)) {
pr_warning("%s: warning: cannot get CSR clock\n", __func__);
goto error_clk_get;
@@ -2178,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
else {
stmmac_set_mac(priv->ioaddr, false);
/* Disable clock in case of PWM is off */
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
@@ -2203,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
priv->hw->mac->pmt(priv->ioaddr, 0);
else
/* enable the clk prevously disabled */
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
netif_device_attach(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index cd01ee7ecef1..b93245c11995 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -74,7 +74,7 @@ static int __devinit stmmac_probe_config_dt(struct platform_device *pdev,
* the necessary resources and invokes the main to init
* the net device, register the mdio bus etc.
*/
-static int stmmac_pltfr_probe(struct platform_device *pdev)
+static int __devinit stmmac_pltfr_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
index 2a0e1abde7e7..4ccd4e2977b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
@@ -97,19 +97,19 @@ static struct clk *timer_clock;
static void stmmac_tmu_start(unsigned int new_freq)
{
clk_set_rate(timer_clock, new_freq);
- clk_enable(timer_clock);
+ clk_prepare_enable(timer_clock);
}
static void stmmac_tmu_stop(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
}
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
{
timer_clock = clk_get(NULL, TMU_CHANNEL);
- if (timer_clock == NULL)
+ if (IS_ERR(timer_clock))
return -1;
if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) {
@@ -126,7 +126,7 @@ int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
int stmmac_close_ext_timer(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
tmu2_unregister_user();
clk_put(timer_clock);
return 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h
index 6863590d184b..aea9b14cdfbe 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h
@@ -21,6 +21,8 @@
Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
*******************************************************************************/
+#ifndef __STMMAC_TIMER_H__
+#define __STMMAC_TIMER_H__
struct stmmac_timer {
void (*timer_start) (unsigned int new_freq);
@@ -40,3 +42,5 @@ void stmmac_schedule(struct net_device *dev);
extern int tmu2_register_user(void *fnt, void *data);
extern void tmu2_unregister_user(void);
#endif
+
+#endif /* __STMMAC_TIMER_H__ */
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 3b5c4571b55e..d15c888e9df8 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -538,11 +538,12 @@ EXPORT_SYMBOL_GPL(cpdma_chan_create);
int cpdma_chan_destroy(struct cpdma_chan *chan)
{
- struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_ctlr *ctlr;
unsigned long flags;
if (!chan)
return -EINVAL;
+ ctlr = chan->ctlr;
spin_lock_irqsave(&ctlr->lock, flags);
if (chan->state != CPDMA_STATE_IDLE)
diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index cd7ee204e94a..a9ca4a03d31b 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -394,8 +394,10 @@ static int __devexit davinci_mdio_remove(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct davinci_mdio_data *data = dev_get_drvdata(dev);
- if (data->bus)
+ if (data->bus) {
+ mdiobus_unregister(data->bus);
mdiobus_free(data->bus);
+ }
if (data->clk)
clk_put(data->clk);
diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
index 482648fcf0b6..98934bdf6acf 100644
--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
+++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
@@ -1003,6 +1003,7 @@ static int ixp4xx_nway_reset(struct net_device *dev)
}
int ixp46x_phc_index = -1;
+EXPORT_SYMBOL_GPL(ixp46x_phc_index);
static int ixp4xx_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
diff --git a/drivers/net/fddi/skfp/pmf.c b/drivers/net/fddi/skfp/pmf.c
index 24d8566cfd8b..441b4dc79450 100644
--- a/drivers/net/fddi/skfp/pmf.c
+++ b/drivers/net/fddi/skfp/pmf.c
@@ -673,7 +673,7 @@ void smt_add_para(struct s_smc *smc, struct s_pcon *pcon, u_short para,
sm_pm_get_ls(smc,port_to_mib(smc,port))) ;
break ;
case SMT_P_REASON :
- * (u_long *) to = 0 ;
+ *(u32 *)to = 0 ;
sp_len = 4 ;
goto sp_done ;
case SMT_P1033 : /* time stamp */
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 6cee2917eb02..4a1a5f58fa73 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -383,13 +383,6 @@ int netvsc_device_remove(struct hv_device *device)
unsigned long flags;
net_device = hv_get_drvdata(device);
- spin_lock_irqsave(&device->channel->inbound_lock, flags);
- net_device->destroy = true;
- spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
-
- /* Wait for all send completions */
- wait_event(net_device->wait_drain,
- atomic_read(&net_device->num_outstanding_sends) == 0);
netvsc_disconnect_vsp(net_device);
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index e5d6146937fa..1e88a1095934 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -718,6 +718,9 @@ static void rndis_filter_halt_device(struct rndis_device *dev)
{
struct rndis_request *request;
struct rndis_halt_request *halt;
+ struct netvsc_device *nvdev = dev->net_dev;
+ struct hv_device *hdev = nvdev->dev;
+ ulong flags;
/* Attempt to do a rndis device halt */
request = get_rndis_request(dev, RNDIS_MSG_HALT,
@@ -735,6 +738,14 @@ static void rndis_filter_halt_device(struct rndis_device *dev)
dev->state = RNDIS_DEV_UNINITIALIZED;
cleanup:
+ spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
+ nvdev->destroy = true;
+ spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
+
+ /* Wait for all send completions */
+ wait_event(nvdev->wait_drain,
+ atomic_read(&nvdev->num_outstanding_sends) == 0);
+
if (request)
put_rndis_request(dev, request);
return;
diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c
index a561ae44a9ac..c6a0299aa9f9 100644
--- a/drivers/net/irda/bfin_sir.c
+++ b/drivers/net/irda/bfin_sir.c
@@ -158,7 +158,7 @@ static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed)
/* If not add the 'RPOLC', we can't catch the receive interrupt.
* It's related with the HW layout and the IR transiver.
*/
- val |= IREN | RPOLC;
+ val |= UMOD_IRDA | RPOLC;
UART_PUT_GCTL(port, val);
return ret;
}
@@ -432,7 +432,7 @@ static void bfin_sir_shutdown(struct bfin_sir_port *port, struct net_device *dev
bfin_sir_stop_rx(port);
val = UART_GET_GCTL(port);
- val &= ~(UCEN | IREN | RPOLC);
+ val &= ~(UCEN | UMOD_MASK | RPOLC);
UART_PUT_GCTL(port, val);
#ifdef CONFIG_SIR_BFIN_DMA
@@ -518,10 +518,10 @@ static void bfin_sir_send_work(struct work_struct *work)
* reset all the UART.
*/
val = UART_GET_GCTL(port);
- val &= ~(IREN | RPOLC);
+ val &= ~(UMOD_MASK | RPOLC);
UART_PUT_GCTL(port, val);
SSYNC();
- val |= IREN | RPOLC;
+ val |= UMOD_IRDA | RPOLC;
UART_PUT_GCTL(port, val);
SSYNC();
/* bfin_sir_set_speed(port, self->speed); */
diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c
index 824e2a93fe8a..5f3aeac3f86d 100644
--- a/drivers/net/irda/ks959-sir.c
+++ b/drivers/net/irda/ks959-sir.c
@@ -542,6 +542,7 @@ static int ks959_net_open(struct net_device *netdev)
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
if (!kingsun->irlap) {
+ err = -ENOMEM;
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
goto free_mem;
}
diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c
index 5a278ab83c2f..2d4b6a1ab202 100644
--- a/drivers/net/irda/ksdazzle-sir.c
+++ b/drivers/net/irda/ksdazzle-sir.c
@@ -436,6 +436,7 @@ static int ksdazzle_net_open(struct net_device *netdev)
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
if (!kingsun->irlap) {
+ err = -ENOMEM;
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
goto free_mem;
}
diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c
index 256eddf1f75a..795109425568 100644
--- a/drivers/net/irda/sh_sir.c
+++ b/drivers/net/irda/sh_sir.c
@@ -280,7 +280,7 @@ static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
}
clk = clk_get(NULL, "irda_clk");
- if (!clk) {
+ if (IS_ERR(clk)) {
dev_err(dev, "can not get irda_clk\n");
return -EIO;
}
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 0737bd4d1669..0f0f9ce3a776 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -94,7 +94,8 @@ static int get_slot(struct macvlan_dev *vlan, struct macvtap_queue *q)
int i;
for (i = 0; i < MAX_MACVTAP_QUEUES; i++) {
- if (rcu_dereference(vlan->taps[i]) == q)
+ if (rcu_dereference_protected(vlan->taps[i],
+ lockdep_is_held(&macvtap_lock)) == q)
return i;
}
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index f9347ea3d381..b3321129a83c 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -640,15 +640,9 @@ static int netconsole_netdev_event(struct notifier_block *this,
* rtnl_lock already held
*/
if (nt->np.dev) {
- spin_unlock_irqrestore(
- &target_list_lock,
- flags);
__netpoll_cleanup(&nt->np);
- spin_lock_irqsave(&target_list_lock,
- flags);
dev_put(nt->np.dev);
nt->np.dev = NULL;
- netconsole_target_put(nt);
}
nt->enabled = 0;
stopped = true;
diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c
index 2346b38b9837..799789518e87 100644
--- a/drivers/net/phy/bcm87xx.c
+++ b/drivers/net/phy/bcm87xx.c
@@ -229,3 +229,5 @@ static void __exit bcm87xx_exit(void)
ARRAY_SIZE(bcm87xx_driver));
}
module_exit(bcm87xx_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index e0cc4ef33dee..eefe49e8713c 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -101,7 +101,6 @@ err:
n--;
gpio_free(s->gpio[n]);
}
- devm_kfree(&pdev->dev, s);
return r;
}
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 5c120189ec86..4d4d25efc1e1 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -132,7 +132,7 @@ int mdio_mux_init(struct device *dev,
pb->mii_bus = parent_bus;
ret_val = -ENODEV;
- for_each_child_of_node(dev->of_node, child_bus_node) {
+ for_each_available_child_of_node(dev->of_node, child_bus_node) {
u32 v;
r = of_property_read_u32(child_bus_node, "reg", &v);
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index cf287e0eb408..2165d5fdb8c0 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -21,6 +21,12 @@
#include <linux/phy.h>
#include <linux/micrel_phy.h>
+/* Operation Mode Strap Override */
+#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
+#define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1)
+#define KSZPHY_OMSO_MII_OVERRIDE (1 << 0)
+
/* general Interrupt control/status reg in vendor specific block. */
#define MII_KSZPHY_INTCS 0x1B
#define KSZPHY_INTCS_JABBER (1 << 15)
@@ -101,6 +107,13 @@ static int kszphy_config_init(struct phy_device *phydev)
return 0;
}
+static int ksz8021_config_init(struct phy_device *phydev)
+{
+ const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
+ phy_write(phydev, MII_KSZPHY_OMSO, val);
+ return 0;
+}
+
static int ks8051_config_init(struct phy_device *phydev)
{
int regval;
@@ -128,9 +141,22 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = ks8737_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8041,
+ .phy_id = PHY_ID_KSZ8021,
+ .phy_id_mask = 0x00ffffff,
+ .name = "Micrel KSZ8021",
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = ksz8021_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = kszphy_ack_interrupt,
+ .config_intr = kszphy_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+}, {
+ .phy_id = PHY_ID_KSZ8041,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8041",
+ .name = "Micrel KSZ8041",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -141,9 +167,9 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8051,
+ .phy_id = PHY_ID_KSZ8051,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8051",
+ .name = "Micrel KSZ8051",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -154,8 +180,8 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8001,
- .name = "Micrel KS8001 or KS8721",
+ .phy_id = PHY_ID_KSZ8001,
+ .name = "Micrel KSZ8001 or KS8721",
.phy_id_mask = 0x00ffffff,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -201,10 +227,11 @@ MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused micrel_tbl[] = {
{ PHY_ID_KSZ9021, 0x000ffffe },
- { PHY_ID_KS8001, 0x00ffffff },
+ { PHY_ID_KSZ8001, 0x00ffffff },
{ PHY_ID_KS8737, 0x00fffff0 },
- { PHY_ID_KS8041, 0x00fffff0 },
- { PHY_ID_KS8051, 0x00fffff0 },
+ { PHY_ID_KSZ8021, 0x00ffffff },
+ { PHY_ID_KSZ8041, 0x00fffff0 },
+ { PHY_ID_KSZ8051, 0x00fffff0 },
{ }
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 6d6192316b30..88e3991464e7 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -56,6 +56,32 @@ static int smsc_phy_config_init(struct phy_device *phydev)
return smsc_phy_ack_interrupt (phydev);
}
+static int lan87xx_config_init(struct phy_device *phydev)
+{
+ /*
+ * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on
+ * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due
+ * to a bug on the chip.
+ *
+ * When the system is powered on with the network cable being
+ * disconnected all the way until after ifconfig ethX up is
+ * issued for the LAN port with this PHY, connecting the cable
+ * afterwards does not cause LINK change detection, while the
+ * expected behavior is the Link UP being detected.
+ */
+ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
+ if (rc < 0)
+ return rc;
+
+ rc &= ~MII_LAN83C185_EDPWRDOWN;
+
+ rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc);
+ if (rc < 0)
+ return rc;
+
+ return smsc_phy_ack_interrupt(phydev);
+}
+
static int lan911x_config_init(struct phy_device *phydev)
{
return smsc_phy_ack_interrupt(phydev);
@@ -162,7 +188,7 @@ static struct phy_driver smsc_phy_driver[] = {
/* basic functions */
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
- .config_init = smsc_phy_config_init,
+ .config_init = lan87xx_config_init,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index cbf7047decc0..20f31d0d1536 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -570,7 +570,7 @@ static int pppoe_release(struct socket *sock)
po = pppox_sk(sk);
- if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+ if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
dev_put(po->pppoe_dev);
po->pppoe_dev = NULL;
}
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 1c98321b56cc..162464fe86bf 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -189,7 +189,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
if (sk_pppox(po)->sk_state & PPPOX_DEAD)
goto tx_error;
- rt = ip_route_output_ports(&init_net, &fl4, NULL,
+ rt = ip_route_output_ports(sock_net(sk), &fl4, NULL,
opt->dst_addr.sin_addr.s_addr,
opt->src_addr.sin_addr.s_addr,
0, 0, IPPROTO_GRE,
@@ -468,7 +468,7 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
po->chan.private = sk;
po->chan.ops = &pptp_chan_ops;
- rt = ip_route_output_ports(&init_net, &fl4, sk,
+ rt = ip_route_output_ports(sock_net(sk), &fl4, sk,
opt->dst_addr.sin_addr.s_addr,
opt->src_addr.sin_addr.s_addr,
0, 0,
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 87707ab39430..f8cd61f449a4 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -795,16 +795,17 @@ static void team_port_leave(struct team *team, struct team_port *port)
}
#ifdef CONFIG_NET_POLL_CONTROLLER
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port,
+ gfp_t gfp)
{
struct netpoll *np;
int err;
- np = kzalloc(sizeof(*np), GFP_KERNEL);
+ np = kzalloc(sizeof(*np), gfp);
if (!np)
return -ENOMEM;
- err = __netpoll_setup(np, port->dev);
+ err = __netpoll_setup(np, port->dev, gfp);
if (err) {
kfree(np);
return err;
@@ -833,7 +834,8 @@ static struct netpoll_info *team_netpoll_info(struct team *team)
}
#else
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port,
+ gfp_t gfp)
{
return 0;
}
@@ -846,7 +848,7 @@ static struct netpoll_info *team_netpoll_info(struct team *team)
}
#endif
-static void __team_port_change_check(struct team_port *port, bool linkup);
+static void __team_port_change_port_added(struct team_port *port, bool linkup);
static int team_port_add(struct team *team, struct net_device *port_dev)
{
@@ -913,7 +915,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
}
if (team_netpoll_info(team)) {
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(team, port, GFP_KERNEL);
if (err) {
netdev_err(dev, "Failed to enable netpoll on device %s\n",
portname);
@@ -946,7 +948,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
team_port_enable(team, port);
list_add_tail_rcu(&port->list, &team->port_list);
__team_compute_features(team);
- __team_port_change_check(port, !!netif_carrier_ok(port_dev));
+ __team_port_change_port_added(port, !!netif_carrier_ok(port_dev));
__team_options_change_check(team);
netdev_info(dev, "Port device %s added\n", portname);
@@ -981,6 +983,8 @@ err_set_mtu:
return err;
}
+static void __team_port_change_port_removed(struct team_port *port);
+
static int team_port_del(struct team *team, struct net_device *port_dev)
{
struct net_device *dev = team->dev;
@@ -997,8 +1001,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
__team_option_inst_mark_removed_port(team, port);
__team_options_change_check(team);
__team_option_inst_del_port(team, port);
- port->removed = true;
- __team_port_change_check(port, false);
+ __team_port_change_port_removed(port);
team_port_disable(team, port);
list_del_rcu(&port->list);
netdev_rx_handler_unregister(port_dev);
@@ -1443,7 +1446,7 @@ static void team_netpoll_cleanup(struct net_device *dev)
}
static int team_netpoll_setup(struct net_device *dev,
- struct netpoll_info *npifo)
+ struct netpoll_info *npifo, gfp_t gfp)
{
struct team *team = netdev_priv(dev);
struct team_port *port;
@@ -1451,7 +1454,7 @@ static int team_netpoll_setup(struct net_device *dev,
mutex_lock(&team->lock);
list_for_each_entry(port, &team->port_list, list) {
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(team, port, gfp);
if (err) {
__team_netpoll_cleanup(team);
break;
@@ -1650,8 +1653,8 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&team_nl_family, 0, TEAM_CMD_NOOP);
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -EMSGSIZE;
goto err_msg_put;
}
@@ -1845,8 +1848,8 @@ start_again:
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI,
TEAM_CMD_OPTIONS_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2065,8 +2068,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
TEAM_CMD_PORT_LIST_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2249,13 +2252,11 @@ static void __team_options_change_check(struct team *team)
}
/* rtnl lock is held */
-static void __team_port_change_check(struct team_port *port, bool linkup)
+
+static void __team_port_change_send(struct team_port *port, bool linkup)
{
int err;
- if (!port->removed && port->state.linkup == linkup)
- return;
-
port->changed = true;
port->state.linkup = linkup;
team_refresh_port_linkup(port);
@@ -2280,6 +2281,23 @@ send_event:
}
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+ if (port->state.linkup != linkup)
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup)
+{
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_removed(struct team_port *port)
+{
+ port->removed = true;
+ __team_port_change_send(port, false);
+}
+
static void team_port_change_check(struct team_port *port, bool linkup)
{
struct team *team = port->team;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 926d4db5cb38..3a16d4fdaa05 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -187,7 +187,6 @@ static void __tun_detach(struct tun_struct *tun)
netif_tx_lock_bh(tun->dev);
netif_carrier_off(tun->dev);
tun->tfile = NULL;
- tun->socket.file = NULL;
netif_tx_unlock_bh(tun->dev);
/* Drop read queue */
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 4fd48df6b989..32e31c5c5dc6 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -962,6 +962,10 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x2001, 0x3c05),
.driver_info = (unsigned long) &ax88772_info,
}, {
+ // DLink DUB-E100 H/W Ver C1
+ USB_DEVICE (0x2001, 0x1a02),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
// Linksys USB1000
USB_DEVICE (0x1737, 0x0039),
.driver_info = (unsigned long) &ax88178_info,
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 187c144c5e5b..7d78669000d7 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -130,7 +130,7 @@ static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)
struct page *page;
int err;
- page = alloc_page(gfp_flags);
+ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
@@ -232,6 +232,7 @@ static int usbpn_open(struct net_device *dev)
struct urb *req = usb_alloc_urb(0, GFP_KERNEL);
if (!req || rx_submit(pnd, req, GFP_KERNEL | __GFP_COLD)) {
+ usb_free_urb(req);
usbpn_close(dev);
return -ENOMEM;
}
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index f4ce5957df32..4cd582a4f625 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1225,6 +1225,26 @@ static const struct usb_device_id cdc_devs[] = {
.driver_info = (unsigned long) &wwan_info,
},
+ /* Dell branded MBM devices like DW5550 */
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_VENDOR,
+ .idVendor = 0x413c,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .driver_info = (unsigned long) &wwan_info,
+ },
+
+ /* Toshiba branded MBM devices */
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_VENDOR,
+ .idVendor = 0x0930,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .driver_info = (unsigned long) &wwan_info,
+ },
+
/* Generic CDC-NCM devices */
{ USB_INTERFACE_INFO(USB_CLASS_COMM,
USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 2ea126a16d79..3543c9e57824 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -247,30 +247,12 @@ err:
*/
static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
{
- int rv;
struct qmi_wwan_state *info = (void *)&dev->data;
- /* ZTE makes devices where the interface descriptors and endpoint
- * configurations of two or more interfaces are identical, even
- * though the functions are completely different. If set, then
- * driver_info->data is a bitmap of acceptable interface numbers
- * allowing us to bind to one such interface without binding to
- * all of them
- */
- if (dev->driver_info->data &&
- !test_bit(intf->cur_altsetting->desc.bInterfaceNumber, &dev->driver_info->data)) {
- dev_info(&intf->dev, "not on our whitelist - ignored");
- rv = -ENODEV;
- goto err;
- }
-
/* control and data is shared */
info->control = intf;
info->data = intf;
- rv = qmi_wwan_register_subdriver(dev);
-
-err:
- return rv;
+ return qmi_wwan_register_subdriver(dev);
}
static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -315,7 +297,7 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
if (ret < 0)
goto err;
- if (info->subdriver && info->subdriver->suspend)
+ if (intf == info->control && info->subdriver && info->subdriver->suspend)
ret = info->subdriver->suspend(intf, message);
if (ret < 0)
usbnet_resume(intf);
@@ -328,13 +310,14 @@ static int qmi_wwan_resume(struct usb_interface *intf)
struct usbnet *dev = usb_get_intfdata(intf);
struct qmi_wwan_state *info = (void *)&dev->data;
int ret = 0;
+ bool callsub = (intf == info->control && info->subdriver && info->subdriver->resume);
- if (info->subdriver && info->subdriver->resume)
+ if (callsub)
ret = info->subdriver->resume(intf);
if (ret < 0)
goto err;
ret = usbnet_resume(intf);
- if (ret < 0 && info->subdriver && info->subdriver->resume && info->subdriver->suspend)
+ if (ret < 0 && callsub && info->subdriver->suspend)
info->subdriver->suspend(intf, PMSG_SUSPEND);
err:
return ret;
@@ -356,217 +339,71 @@ static const struct driver_info qmi_wwan_shared = {
.manage_power = qmi_wwan_manage_power,
};
-static const struct driver_info qmi_wwan_force_int0 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(0), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int1 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(1), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int2 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(2), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int3 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(3), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int4 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(4), /* interface whitelist bitmap */
-};
-
-/* Sierra Wireless provide equally useless interface descriptors
- * Devices in QMI mode can be switched between two different
- * configurations:
- * a) USB interface #8 is QMI/wwan
- * b) USB interfaces #8, #19 and #20 are QMI/wwan
- *
- * Both configurations provide a number of other interfaces (serial++),
- * some of which have the same endpoint configuration as we expect, so
- * a whitelist or blacklist is necessary.
- *
- * FIXME: The below whitelist should include BIT(20). It does not
- * because I cannot get it to work...
- */
-static const struct driver_info qmi_wwan_sierra = {
- .description = "Sierra Wireless wwan/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(8) | BIT(19), /* interface whitelist bitmap */
-};
-
#define HUAWEI_VENDOR_ID 0x12D1
+/* map QMI/wwan function by a fixed interface number */
+#define QMI_FIXED_INTF(vend, prod, num) \
+ USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
+ .driver_info = (unsigned long)&qmi_wwan_shared
+
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
#define QMI_GOBI1K_DEVICE(vend, prod) \
- USB_DEVICE(vend, prod), \
- .driver_info = (unsigned long)&qmi_wwan_force_int3
+ QMI_FIXED_INTF(vend, prod, 3)
-/* Gobi 2000 and Gobi 3000 QMI/wwan interface number is 0 according to qcserial */
+/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */
#define QMI_GOBI_DEVICE(vend, prod) \
- USB_DEVICE(vend, prod), \
- .driver_info = (unsigned long)&qmi_wwan_force_int0
+ QMI_FIXED_INTF(vend, prod, 0)
static const struct usb_device_id products[] = {
+ /* 1. CDC ECM like devices match on the control interface */
{ /* Huawei E392, E398 and possibly others sharing both device id and more... */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 9, /* CDC Ethernet *control* interface */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 9),
.driver_info = (unsigned long)&qmi_wwan_info,
},
{ /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 57, /* CDC Ethernet *control* interface */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 57),
.driver_info = (unsigned long)&qmi_wwan_info,
},
- { /* Huawei E392, E398 and possibly others in "Windows mode"
- * using a combined control and data interface without any CDC
- * functional descriptors
- */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 17,
- .driver_info = (unsigned long)&qmi_wwan_shared,
+
+ /* 2. Combined interface devices matching on class+protocol */
+ { /* Huawei E367 and possibly others in "Windows mode" */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7),
+ .driver_info = (unsigned long)&qmi_wwan_info,
},
- { /* Pantech UML290 */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x106c,
- .idProduct = 0x3718,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xf0,
- .bInterfaceProtocol = 0xff,
+ { /* Huawei E392, E398 and possibly others in "Windows mode" */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
- { /* ZTE MF820D */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0167,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE MF821D */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0326,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3520-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0055,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int1,
- },
- { /* ZTE (Vodafone) K3565-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0063,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3570-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1008,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3571-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1010,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3765-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x2002,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K4505-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0104,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE MF60 */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1402,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int2,
+ { /* Pantech UML290, P4200 and more */
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
+ .driver_info = (unsigned long)&qmi_wwan_shared,
},
- { /* Sierra Wireless MC77xx in QMI mode */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x1199,
- .idProduct = 0x68a2,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_sierra,
+ { /* Pantech UML290 - newer firmware */
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
+ .driver_info = (unsigned long)&qmi_wwan_shared,
},
- /* Gobi 1000 devices */
+ /* 3. Combined interface devices matching on interface number */
+ {QMI_FIXED_INTF(0x19d2, 0x0055, 1)}, /* ZTE (Vodafone) K3520-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0063, 4)}, /* ZTE (Vodafone) K3565-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0104, 4)}, /* ZTE (Vodafone) K4505-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0157, 5)}, /* ZTE MF683 */
+ {QMI_FIXED_INTF(0x19d2, 0x0167, 4)}, /* ZTE MF820D */
+ {QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */
+ {QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1010, 4)}, /* ZTE (Vodafone) K3571-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1018, 3)}, /* ZTE (Vodafone) K5006-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1402, 2)}, /* ZTE MF60 */
+ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
+ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
+ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
+ {QMI_FIXED_INTF(0x1199, 0x68a2, 19)}, /* Sierra Wireless MC7710 in QMI mode */
+ {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */
+
+ /* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{QMI_GOBI1K_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
- {QMI_GOBI1K_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
{QMI_GOBI1K_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */
{QMI_GOBI1K_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */
{QMI_GOBI1K_DEVICE(0x1410, 0xa001)}, /* Novatel Gobi Modem device */
@@ -579,9 +416,11 @@ static const struct usb_device_id products[] = {
{QMI_GOBI1K_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
- /* Gobi 2000 and 3000 devices */
+ /* 5. Gobi 2000 and 3000 devices */
{QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
+ {QMI_GOBI_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */
{QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
+ {QMI_GOBI_DEVICE(0x05c6, 0x920d)}, /* Gobi 3000 Composite */
{QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
{QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
{QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
@@ -589,6 +428,8 @@ static const struct usb_device_id products[] = {
{QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
{QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
{QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
+ {QMI_GOBI_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
+ {QMI_GOBI_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */
{QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
@@ -600,11 +441,17 @@ static const struct usb_device_id products[] = {
{QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
+ {QMI_FIXED_INTF(0x1199, 0x9011, 5)}, /* alternate interface number!? */
{QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
{QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
+ {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
{QMI_GOBI_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */
{QMI_GOBI_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */
+ {QMI_GOBI_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */
+ {QMI_GOBI_DEVICE(0x12d1, 0x14f1)}, /* Sony Gobi 3000 Composite */
+ {QMI_GOBI_DEVICE(0x1410, 0xa021)}, /* Foxconn Gobi 3000 Modem device (Novatel E396) */
+
{ } /* END */
};
MODULE_DEVICE_TABLE(usb, products);
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index d75d1f56becf..8e22417fa6c1 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -68,15 +68,8 @@ static atomic_t iface_counter = ATOMIC_INIT(0);
*/
#define SIERRA_NET_USBCTL_BUF_LEN 1024
-/* list of interface numbers - used for constructing interface lists */
-struct sierra_net_iface_info {
- const u32 infolen; /* number of interface numbers on list */
- const u8 *ifaceinfo; /* pointer to the array holding the numbers */
-};
-
struct sierra_net_info_data {
u16 rx_urb_size;
- struct sierra_net_iface_info whitelist;
};
/* Private data structure */
@@ -637,21 +630,6 @@ static int sierra_net_change_mtu(struct net_device *net, int new_mtu)
return usbnet_change_mtu(net, new_mtu);
}
-static int is_whitelisted(const u8 ifnum,
- const struct sierra_net_iface_info *whitelist)
-{
- if (whitelist) {
- const u8 *list = whitelist->ifaceinfo;
- int i;
-
- for (i = 0; i < whitelist->infolen; i++) {
- if (list[i] == ifnum)
- return 1;
- }
- }
- return 0;
-}
-
static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)
{
int result = 0;
@@ -678,7 +656,7 @@ static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)
return -EIO;
}
- *datap = *attrdata;
+ *datap = le16_to_cpu(*attrdata);
kfree(attrdata);
return result;
@@ -706,11 +684,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf)
dev_dbg(&dev->udev->dev, "%s", __func__);
ifacenum = intf->cur_altsetting->desc.bInterfaceNumber;
- /* We only accept certain interfaces */
- if (!is_whitelisted(ifacenum, &data->whitelist)) {
- dev_dbg(&dev->udev->dev, "Ignoring interface: %d", ifacenum);
- return -ENODEV;
- }
numendpoints = intf->cur_altsetting->desc.bNumEndpoints;
/* We have three endpoints, bulk in and out, and a status */
if (numendpoints != 3) {
@@ -945,13 +918,8 @@ struct sk_buff *sierra_net_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
return NULL;
}
-static const u8 sierra_net_ifnum_list[] = { 7, 10, 11 };
static const struct sierra_net_info_data sierra_net_info_data_direct_ip = {
.rx_urb_size = 8 * 1024,
- .whitelist = {
- .infolen = ARRAY_SIZE(sierra_net_ifnum_list),
- .ifaceinfo = sierra_net_ifnum_list
- }
};
static const struct driver_info sierra_net_info_direct_ip = {
@@ -965,15 +933,19 @@ static const struct driver_info sierra_net_info_direct_ip = {
.data = (unsigned long)&sierra_net_info_data_direct_ip,
};
+#define DIRECT_IP_DEVICE(vend, prod) \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 7), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}, \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 10), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}, \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 11), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}
+
static const struct usb_device_id products[] = {
- {USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x0F3D, 0x68A3), /* AT&T Direct IP modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x1199, 0x68AA), /* Sierra Wireless Direct IP LTE modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x0F3D, 0x68AA), /* AT&T Direct IP LTE modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
+ DIRECT_IP_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */
+ DIRECT_IP_DEVICE(0x0F3D, 0x68A3), /* AT&T Direct IP modem */
+ DIRECT_IP_DEVICE(0x1199, 0x68AA), /* Sierra Wireless Direct IP LTE modem */
+ DIRECT_IP_DEVICE(0x0F3D, 0x68AA), /* AT&T Direct IP LTE modem */
{}, /* last item */
};
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index f5ab6e613ec8..376143e8a1aa 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -1253,6 +1253,7 @@ static struct usb_driver smsc75xx_driver = {
.probe = usbnet_probe,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
.disconnect = usbnet_disconnect,
.disable_hub_initiated_lpm = 1,
};
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 8531c1caac28..fc9f578a1e25 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1201,19 +1201,26 @@ deferred:
}
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
-static void rx_alloc_submit(struct usbnet *dev, gfp_t flags)
+static int rx_alloc_submit(struct usbnet *dev, gfp_t flags)
{
struct urb *urb;
int i;
+ int ret = 0;
/* don't refill the queue all at once */
for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) {
urb = usb_alloc_urb(0, flags);
if (urb != NULL) {
- if (rx_submit(dev, urb, flags) == -ENOLINK)
- return;
+ ret = rx_submit(dev, urb, flags);
+ if (ret)
+ goto err;
+ } else {
+ ret = -ENOMEM;
+ goto err;
}
}
+err:
+ return ret;
}
/*-------------------------------------------------------------------------*/
@@ -1257,7 +1264,8 @@ static void usbnet_bh (unsigned long param)
int temp = dev->rxq.qlen;
if (temp < RX_QLEN(dev)) {
- rx_alloc_submit(dev, GFP_ATOMIC);
+ if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK)
+ return;
if (temp != dev->rxq.qlen)
netif_dbg(dev, link, dev->net,
"rxqlen %d --> %d\n",
@@ -1573,7 +1581,7 @@ int usbnet_resume (struct usb_interface *intf)
netif_device_present(dev->net) &&
!timer_pending(&dev->delay) &&
!test_bit(EVENT_RX_HALT, &dev->flags))
- rx_alloc_submit(dev, GFP_KERNEL);
+ rx_alloc_submit(dev, GFP_NOIO);
if (!(dev->txq.qlen >= TX_QLEN(dev)))
netif_tx_wake_all_queues(dev->net);
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 93e0cfb739b8..ce9d4f2c9776 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -3019,6 +3019,7 @@ vmxnet3_probe_device(struct pci_dev *pdev,
netdev->watchdog_timeo = 5 * HZ;
INIT_WORK(&adapter->work, vmxnet3_reset_work);
+ set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
if (adapter->intr.type == VMXNET3_IT_MSIX) {
int i;
@@ -3043,7 +3044,6 @@ vmxnet3_probe_device(struct pci_dev *pdev,
goto err_register;
}
- set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
vmxnet3_check_link(adapter, false);
atomic_inc(&devices_found);
return 0;
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 9eb6479306d6..ef36cafd44b7 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -774,14 +774,15 @@ static int __devinit dscc4_init_one(struct pci_dev *pdev,
}
/* Global interrupt queue */
writel((u32)(((IRQ_RING_SIZE >> 5) - 1) << 20), ioaddr + IQLENR1);
+
+ rc = -ENOMEM;
+
priv->iqcfg = (__le32 *) pci_alloc_consistent(pdev,
IRQ_RING_SIZE*sizeof(__le32), &priv->iqcfg_dma);
if (!priv->iqcfg)
goto err_free_irq_5;
writel(priv->iqcfg_dma, ioaddr + IQCFG);
- rc = -ENOMEM;
-
/*
* SCC 0-3 private rx/tx irq structures
* IQRX/TXi needs to be set soon. Learned it the hard way...
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c
index aaaca9aa2293..3f575afd8cfc 100644
--- a/drivers/net/wan/ixp4xx_hss.c
+++ b/drivers/net/wan/ixp4xx_hss.c
@@ -10,6 +10,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 283237f6f074..def12b38cbf7 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -326,8 +326,10 @@ int i2400m_barker_db_init(const char *_options)
unsigned barker;
options_orig = kstrdup(_options, GFP_KERNEL);
- if (options_orig == NULL)
+ if (options_orig == NULL) {
+ result = -ENOMEM;
goto error_parse;
+ }
options = options_orig;
while ((token = strsep(&options, ",")) != NULL) {
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index efc162e0b511..88b8d64c90f1 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -342,7 +342,7 @@ static int at76_dfu_get_status(struct usb_device *udev,
return ret;
}
-static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
+static int at76_dfu_get_state(struct usb_device *udev, u8 *state)
{
int ret;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 8c4c040a47b8..2aab20ee9f38 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2056,9 +2056,7 @@ ath5k_beacon_update_timers(struct ath5k_hw *ah, u64 bc_tsf)
void
ath5k_beacon_config(struct ath5k_hw *ah)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ah->bmisscount = 0;
ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
@@ -2085,7 +2083,7 @@ ath5k_beacon_config(struct ath5k_hw *ah)
ath5k_hw_set_imr(ah, ah->imask);
mmiowb();
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
static void ath5k_tasklet_beacon(unsigned long data)
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c
index 4026c906cc7b..b7e0258887e7 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath/ath5k/eeprom.c
@@ -1482,7 +1482,7 @@ ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
case AR5K_EEPROM_MODE_11A:
offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version);
rate_pcal_info = ee->ee_rate_tpwr_a;
- ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_CHAN;
+ ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_RATE_CHAN;
break;
case AR5K_EEPROM_MODE_11B:
offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version);
diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h
index dc2bcfeadeb4..94a9bbea6874 100644
--- a/drivers/net/wireless/ath/ath5k/eeprom.h
+++ b/drivers/net/wireless/ath/ath5k/eeprom.h
@@ -182,6 +182,7 @@
#define AR5K_EEPROM_EEP_DELTA 10
#define AR5K_EEPROM_N_MODES 3
#define AR5K_EEPROM_N_5GHZ_CHAN 10
+#define AR5K_EEPROM_N_5GHZ_RATE_CHAN 8
#define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 260e7dc7f751..d56453e43d7e 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -254,7 +254,6 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ath5k_vif *avf = (void *)vif->drv_priv;
struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
- unsigned long flags;
mutex_lock(&ah->lock);
@@ -300,9 +299,9 @@ ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
}
if (changes & BSS_CHANGED_BEACON) {
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ath5k_beacon_update(hw, vif);
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
if (changes & BSS_CHANGED_BEACON_ENABLED)
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 2588848f4a82..d066f2516e47 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -2982,6 +2982,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
case EEP_RX_MASK:
return pBase->txrxMask & 0xf;
case EEP_PAPRD:
+ if (AR_SREV_9462(ah))
+ return false;
+ if (!ah->config.enable_paprd);
+ return false;
return !!(pBase->featureEnable & BIT(5));
case EEP_CHAIN_MASK_REDUCE:
return (pBase->miscConfiguration >> 0x3) & 0x1;
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
index 2c9f7d7ed4cc..0ed3846f9cbb 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c
@@ -142,6 +142,7 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
};
int training_power;
int i, val;
+ u32 am2pm_mask = ah->paprd_ratemask;
if (IS_CHAN_2GHZ(ah->curchan))
training_power = ar9003_get_training_power_2g(ah);
@@ -158,10 +159,13 @@ static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
}
ah->paprd_training_power = training_power;
+ if (AR_SREV_9330(ah))
+ am2pm_mask = 0;
+
REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK,
ah->paprd_ratemask);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK,
- ah->paprd_ratemask);
+ am2pm_mask);
REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK,
ah->paprd_ratemask_ht40);
@@ -782,6 +786,102 @@ int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
}
EXPORT_SYMBOL(ar9003_paprd_setup_gain_table);
+static bool ar9003_paprd_retrain_pa_in(struct ath_hw *ah,
+ struct ath9k_hw_cal_data *caldata,
+ int chain)
+{
+ u32 *pa_in = caldata->pa_table[chain];
+ int capdiv_offset, quick_drop_offset;
+ int capdiv2g, quick_drop;
+ int count = 0;
+ int i;
+
+ if (!AR_SREV_9485(ah) && !AR_SREV_9330(ah))
+ return false;
+
+ capdiv2g = REG_READ_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+ AR_PHY_65NM_CH0_TXRF3_CAPDIV2G);
+
+ quick_drop = REG_READ_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+ AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP);
+
+ if (quick_drop)
+ quick_drop -= 0x40;
+
+ for (i = 0; i < NUM_BIN + 1; i++) {
+ if (pa_in[i] == 1400)
+ count++;
+ }
+
+ if (AR_SREV_9485(ah)) {
+ if (pa_in[23] < 800) {
+ capdiv_offset = (int)((1000 - pa_in[23] + 75) / 150);
+ capdiv2g += capdiv_offset;
+ if (capdiv2g > 7) {
+ capdiv2g = 7;
+ if (pa_in[23] < 600) {
+ quick_drop++;
+ if (quick_drop > 0)
+ quick_drop = 0;
+ }
+ }
+ } else if (pa_in[23] == 1400) {
+ quick_drop_offset = min_t(int, count / 3, 2);
+ quick_drop += quick_drop_offset;
+ capdiv2g += quick_drop_offset / 2;
+
+ if (capdiv2g > 7)
+ capdiv2g = 7;
+
+ if (quick_drop > 0) {
+ quick_drop = 0;
+ capdiv2g -= quick_drop_offset;
+ if (capdiv2g < 0)
+ capdiv2g = 0;
+ }
+ } else {
+ return false;
+ }
+ } else if (AR_SREV_9330(ah)) {
+ if (pa_in[23] < 1000) {
+ capdiv_offset = (1000 - pa_in[23]) / 100;
+ capdiv2g += capdiv_offset;
+ if (capdiv_offset > 3) {
+ capdiv_offset = 1;
+ quick_drop--;
+ }
+
+ capdiv2g += capdiv_offset;
+ if (capdiv2g > 6)
+ capdiv2g = 6;
+ if (quick_drop < -4)
+ quick_drop = -4;
+ } else if (pa_in[23] == 1400) {
+ if (count > 3) {
+ quick_drop++;
+ capdiv2g -= count / 4;
+ if (quick_drop > -2)
+ quick_drop = -2;
+ } else {
+ capdiv2g--;
+ }
+
+ if (capdiv2g < 0)
+ capdiv2g = 0;
+ } else {
+ return false;
+ }
+ }
+
+ REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_TXRF3,
+ AR_PHY_65NM_CH0_TXRF3_CAPDIV2G, capdiv2g);
+ REG_RMW_FIELD(ah, AR_PHY_PAPRD_TRAINER_CNTL3,
+ AR_PHY_PAPRD_TRAINER_CNTL3_CF_PAPRD_QUICK_DROP,
+ quick_drop);
+
+ return true;
+}
+
int ar9003_paprd_create_curve(struct ath_hw *ah,
struct ath9k_hw_cal_data *caldata, int chain)
{
@@ -817,6 +917,9 @@ int ar9003_paprd_create_curve(struct ath_hw *ah,
if (!create_pa_curve(data_L, data_U, pa_table, small_signal_gain))
status = -2;
+ if (ar9003_paprd_retrain_pa_in(ah, caldata, chain))
+ status = -EINPROGRESS;
+
REG_CLR_BIT(ah, AR_PHY_PAPRD_TRAINER_STAT1,
AR_PHY_PAPRD_TRAINER_STAT1_PAPRD_TRAIN_DONE);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
index 7bfbaf065a43..84d3d4956861 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h
@@ -625,6 +625,10 @@
#define AR_PHY_AIC_CTRL_4_B0 (AR_SM_BASE + 0x4c0)
#define AR_PHY_AIC_STAT_2_B0 (AR_SM_BASE + 0x4cc)
+#define AR_PHY_65NM_CH0_TXRF3 0x16048
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G 0x0000001e
+#define AR_PHY_65NM_CH0_TXRF3_CAPDIV2G_S 1
+
#define AR_PHY_65NM_CH0_SYNTH4 0x1608c
#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT (AR_SREV_9462(ah) ? 0x00000001 : 0x00000002)
#define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S (AR_SREV_9462(ah) ? 0 : 1)
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 68b643c8943c..c8ef30127adb 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1577,6 +1577,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_disable_ani);
+ debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ &sc->sc_ah->config.enable_paprd);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_regidx);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c
index bacdb8fb4ef4..9f83f71742a5 100644
--- a/drivers/net/wireless/ath/ath9k/gpio.c
+++ b/drivers/net/wireless/ath/ath9k/gpio.c
@@ -341,7 +341,8 @@ void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
{
struct ath_btcoex *btcoex = &sc->btcoex;
- ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
+ if (btcoex->hw_timer_enabled)
+ ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
}
u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cfa91ab7acf8..4faf0a395876 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -463,9 +463,6 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
ah->config.spurchans[i][1] = AR_NO_SPUR;
}
- /* PAPRD needs some more work to be enabled */
- ah->config.paprd_disable = 1;
-
ah->config.rx_intr_mitigation = true;
ah->config.pcieSerDesWrite = true;
@@ -730,6 +727,7 @@ int ath9k_hw_init(struct ath_hw *ah)
case AR9300_DEVID_QCA955X:
case AR9300_DEVID_AR9580:
case AR9300_DEVID_AR9462:
+ case AR9485_DEVID_AR1111:
break;
default:
if (common->bus_ops->ath_bus_type == ATH_USB)
@@ -977,9 +975,6 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah,
else
imr_reg |= AR_IMR_TXOK;
- if (opmode == NL80211_IFTYPE_AP)
- imr_reg |= AR_IMR_MIB;
-
ENABLE_REGWRITE_BUFFER(ah);
REG_WRITE(ah, AR_IMR, imr_reg);
@@ -1777,6 +1772,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
/* Operating channel changed, reset channel calibration data */
memset(caldata, 0, sizeof(*caldata));
ath9k_init_nfcal_hist_buffer(ah, chan);
+ } else if (caldata) {
+ caldata->paprd_packet_sent = false;
}
ah->noise = ath9k_hw_getchan_noise(ah, chan);
@@ -2500,9 +2497,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->rx_status_len = sizeof(struct ar9003_rxs);
pCap->tx_desc_len = sizeof(struct ar9003_txc);
pCap->txs_len = sizeof(struct ar9003_txs);
- if (!ah->config.paprd_disable &&
- ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
- pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
} else {
pCap->tx_desc_len = sizeof(struct ath_desc);
if (AR_SREV_9280_20(ah))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index dd0c146d81dc..de6968fc64f4 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -49,6 +49,7 @@
#define AR9300_DEVID_AR9462 0x0034
#define AR9300_DEVID_AR9330 0x0035
#define AR9300_DEVID_QCA955X 0x0038
+#define AR9485_DEVID_AR1111 0x0037
#define AR5416_AR9100_DEVID 0x000b
@@ -235,7 +236,6 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_LDPC = BIT(6),
ATH9K_HW_CAP_FASTCLOCK = BIT(7),
ATH9K_HW_CAP_SGI_20 = BIT(8),
- ATH9K_HW_CAP_PAPRD = BIT(9),
ATH9K_HW_CAP_ANT_DIV_COMB = BIT(10),
ATH9K_HW_CAP_2GHZ = BIT(11),
ATH9K_HW_CAP_5GHZ = BIT(12),
@@ -286,12 +286,12 @@ struct ath9k_ops_config {
u8 pcie_clock_req;
u32 pcie_waen;
u8 analog_shiftreg;
- u8 paprd_disable;
u32 ofdm_trig_low;
u32 ofdm_trig_high;
u32 cck_trig_high;
u32 cck_trig_low;
u32 enable_ani;
+ u32 enable_paprd;
int serialize_regmode;
bool rx_intr_mitigation;
bool tx_intr_mitigation;
@@ -404,6 +404,7 @@ struct ath9k_hw_cal_data {
int8_t iCoff;
int8_t qCoff;
bool rtt_done;
+ bool paprd_packet_sent;
bool paprd_done;
bool nfcal_pending;
bool nfcal_interference;
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index d4549e9aac5c..7b88b9c39ccd 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -254,8 +254,9 @@ void ath_paprd_calibrate(struct work_struct *work)
int chain_ok = 0;
int chain;
int len = 1800;
+ int ret;
- if (!caldata)
+ if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done)
return;
ath9k_ps_wakeup(sc);
@@ -282,13 +283,6 @@ void ath_paprd_calibrate(struct work_struct *work)
continue;
chain_ok = 0;
-
- ath_dbg(common, CALIBRATE,
- "Sending PAPRD frame for thermal measurement on chain %d\n",
- chain);
- if (!ath_paprd_send_frame(sc, skb, chain))
- goto fail_paprd;
-
ar9003_paprd_setup_gain_table(ah, chain);
ath_dbg(common, CALIBRATE,
@@ -302,7 +296,13 @@ void ath_paprd_calibrate(struct work_struct *work)
break;
}
- if (ar9003_paprd_create_curve(ah, caldata, chain)) {
+ ret = ar9003_paprd_create_curve(ah, caldata, chain);
+ if (ret == -EINPROGRESS) {
+ ath_dbg(common, CALIBRATE,
+ "PAPRD curve on chain %d needs to be re-trained\n",
+ chain);
+ break;
+ } else if (ret) {
ath_dbg(common, CALIBRATE,
"PAPRD create curve failed on chain %d\n",
chain);
@@ -423,7 +423,7 @@ set_timer:
cal_interval = min(cal_interval, (u32)short_cal_interval);
mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+ if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) {
if (!ah->caldata->paprd_done)
ieee80211_queue_work(sc->hw, &sc->paprd_work);
else if (!ah->paprd_table_write_done)
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 7990cd55599c..b42be910a83d 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -773,15 +773,10 @@ bool ath9k_hw_intrpend(struct ath_hw *ah)
}
EXPORT_SYMBOL(ath9k_hw_intrpend);
-void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+void ath9k_hw_kill_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- if (!(ah->imask & ATH9K_INT_GLOBAL))
- atomic_set(&ah->intr_ref_cnt, -1);
- else
- atomic_dec(&ah->intr_ref_cnt);
-
ath_dbg(common, INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
@@ -793,6 +788,17 @@ void ath9k_hw_disable_interrupts(struct ath_hw *ah)
(void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
}
}
+EXPORT_SYMBOL(ath9k_hw_kill_interrupts);
+
+void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+{
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ atomic_set(&ah->intr_ref_cnt, -1);
+ else
+ atomic_dec(&ah->intr_ref_cnt);
+
+ ath9k_hw_kill_interrupts(ah);
+}
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
void ath9k_hw_enable_interrupts(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 0eba36dca6f8..4a745e68dd94 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -738,6 +738,7 @@ bool ath9k_hw_intrpend(struct ath_hw *ah);
void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
+void ath9k_hw_kill_interrupts(struct ath_hw *ah);
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 6049d8b82855..a22df749b8db 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -462,8 +462,10 @@ irqreturn_t ath_isr(int irq, void *dev)
if (!ath9k_hw_intrpend(ah))
return IRQ_NONE;
- if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
+ ath9k_hw_kill_interrupts(ah);
return IRQ_HANDLED;
+ }
/*
* Figure out the reason(s) for the interrupt. Note
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 87b89d55e637..a978984d78a5 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -37,6 +37,7 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
{ PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */
+ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */
{ 0 }
};
@@ -320,6 +321,7 @@ static int ath_pci_suspend(struct device *device)
* Otherwise the chip never moved to full sleep,
* when no interface is up.
*/
+ ath9k_stop_btcoex(sc);
ath9k_hw_disable(sc->sc_ah);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 12aca02228c2..4480c0cc655f 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1044,7 +1044,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_hdr *hdr;
int retval;
- bool decrypt_error = false;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
@@ -1066,6 +1065,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
tsf_lower = tsf & 0xffffffff;
do {
+ bool decrypt_error = false;
/* If handling rx interrupt and flush is in progress => exit */
if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
break;
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 2c9da6b2ecb1..0d4155aec48d 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -2018,6 +2018,9 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb);
+ if (sc->sc_ah->caldata)
+ sc->sc_ah->caldata->paprd_packet_sent = true;
+
if (!(tx_flags & ATH_TX_ERROR))
/* Frame was ACKed */
tx_info->flags |= IEEE80211_TX_STAT_ACK;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index b80352b308d5..a140165dfee0 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2719,32 +2719,37 @@ static int b43_gpio_init(struct b43_wldev *dev)
if (dev->dev->chip_id == 0x4301) {
mask |= 0x0060;
set |= 0x0060;
+ } else if (dev->dev->chip_id == 0x5354) {
+ /* Don't allow overtaking buttons GPIOs */
+ set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
}
- if (dev->dev->chip_id == 0x5354)
- set &= 0xff02;
+
if (0 /* FIXME: conditional unknown */ ) {
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0100);
- mask |= 0x0180;
- set |= 0x0180;
+ /* BT Coexistance Input */
+ mask |= 0x0080;
+ set |= 0x0080;
+ /* BT Coexistance Out */
+ mask |= 0x0100;
+ set |= 0x0100;
}
if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
+ /* PA is controlled by gpio 9, let ucode handle it */
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0200);
mask |= 0x0200;
set |= 0x0200;
}
- if (dev->dev->core_rev >= 2)
- mask |= 0x0010; /* FIXME: This is redundant. */
switch (dev->dev->bus_type) {
#ifdef CONFIG_B43_BCMA
case B43_BUS_BCMA:
bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
(bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
- BCMA_CC_GPIOCTL) & mask) | set);
+ BCMA_CC_GPIOCTL) & ~mask) | set);
break;
#endif
#ifdef CONFIG_B43_SSB
@@ -2753,7 +2758,7 @@ static int b43_gpio_init(struct b43_wldev *dev)
if (gpiodev)
ssb_write32(gpiodev, B43_GPIO_CONTROL,
(ssb_read32(gpiodev, B43_GPIO_CONTROL)
- & mask) | set);
+ & ~mask) | set);
break;
#endif
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 49765d34b4e0..7c4ee72f9d56 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -638,6 +638,8 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
GFP_KERNEL);
+ if (!oobirq_entry)
+ return -ENOMEM;
oobirq_entry->irq = res->start;
oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
list_add_tail(&oobirq_entry->list, &oobirq_lh);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index 2621dd3d7dcd..6f70953f0bad 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -764,8 +764,11 @@ static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
{
char iovbuf[32];
int retcode;
+ __le32 arp_mode_le;
- brcmf_c_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+ arp_mode_le = cpu_to_le32(arp_mode);
+ brcmf_c_mkiovar("arp_ol", (char *)&arp_mode_le, 4, iovbuf,
+ sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
retcode = retcode >= 0 ? 0 : retcode;
@@ -781,8 +784,11 @@ static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
{
char iovbuf[32];
int retcode;
+ __le32 arp_enable_le;
- brcmf_c_mkiovar("arpoe", (char *)&arp_enable, 4,
+ arp_enable_le = cpu_to_le32(arp_enable);
+
+ brcmf_c_mkiovar("arpoe", (char *)&arp_enable_le, 4,
iovbuf, sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
@@ -800,10 +806,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for
"event_msgs" + '\0' + bitvec */
char buf[128], *ptr;
- u32 roaming = 1;
- uint bcn_timeout = 3;
- int scan_assoc_time = 40;
- int scan_unassoc_time = 40;
+ __le32 roaming_le = cpu_to_le32(1);
+ __le32 bcn_timeout_le = cpu_to_le32(3);
+ __le32 scan_assoc_time_le = cpu_to_le32(40);
+ __le32 scan_unassoc_time_le = cpu_to_le32(40);
int i;
struct brcmf_bus_dcmd *cmdlst;
struct list_head *cur, *q;
@@ -829,14 +835,14 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
/* Setup timeout if Beacons are lost and roam is off to report
link down */
- brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout_le, 4, iovbuf,
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
/* Enable/Disable build-in roaming to allowed ext supplicant to take
of romaing */
- brcmf_c_mkiovar("roam_off", (char *)&roaming, 4,
+ brcmf_c_mkiovar("roam_off", (char *)&roaming_le, 4,
iovbuf, sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
@@ -848,9 +854,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+ (char *)&scan_assoc_time_le, sizeof(scan_assoc_time_le));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+ (char *)&scan_unassoc_time_le, sizeof(scan_unassoc_time_le));
/* Set and enable ARP offload feature */
brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 57bf1d7ee80f..9ab24528f9b9 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -1188,7 +1188,7 @@ exit:
kfree(buf);
/* close file before return */
if (fp)
- filp_close(fp, current->files);
+ filp_close(fp, NULL);
/* restore previous address limit */
set_fs(old_fs);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index a299d42da8e7..58f89fa9c9f8 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -519,7 +519,7 @@ static void brcmf_usb_tx_complete(struct urb *urb)
else
devinfo->bus_pub.bus->dstats.tx_errors++;
- dev_kfree_skb(req->skb);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
@@ -540,7 +540,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
devinfo->bus_pub.bus->dstats.rx_packets++;
} else {
devinfo->bus_pub.bus->dstats.rx_errors++;
- dev_kfree_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
return;
}
@@ -550,13 +550,15 @@ static void brcmf_usb_rx_complete(struct urb *urb)
if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
brcmf_dbg(ERROR, "rx protocol error\n");
brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
devinfo->bus_pub.bus->dstats.rx_errors++;
} else {
brcmf_rx_packet(devinfo->dev, ifidx, skb);
brcmf_usb_rx_refill(devinfo, req);
}
} else {
- dev_kfree_skb(skb);
+ brcmu_pkt_buf_free_skb(skb);
+ brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
}
return;
@@ -581,14 +583,13 @@ static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo,
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->rx_pipe,
skb->data, skb_tailroom(skb), brcmf_usb_rx_complete,
req);
- req->urb->transfer_flags |= URB_ZERO_PACKET;
req->devinfo = devinfo;
+ brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (ret == 0) {
- brcmf_usb_enq(devinfo, &devinfo->rx_postq, req);
- } else {
- dev_kfree_skb(req->skb);
+ if (ret) {
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req);
}
@@ -683,23 +684,22 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq);
if (!req) {
+ brcmu_pkt_buf_free_skb(skb);
brcmf_dbg(ERROR, "no req to send\n");
return -ENOMEM;
}
- if (!req->urb) {
- brcmf_dbg(ERROR, "no urb for req %p\n", req);
- return -ENOBUFS;
- }
req->skb = skb;
req->devinfo = devinfo;
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
skb->data, skb->len, brcmf_usb_tx_complete, req);
req->urb->transfer_flags |= URB_ZERO_PACKET;
+ brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
ret = usb_submit_urb(req->urb, GFP_ATOMIC);
- if (!ret) {
- brcmf_usb_enq(devinfo, &devinfo->tx_postq, req);
- } else {
+ if (ret) {
+ brcmf_dbg(ERROR, "brcmf_usb_tx usb_submit_urb FAILED\n");
+ brcmf_usb_del_fromq(devinfo, req);
+ brcmu_pkt_buf_free_skb(req->skb);
req->skb = NULL;
brcmf_usb_enq(devinfo, &devinfo->tx_freeq, req);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index 28c5fbb4af26..50b5553b6964 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -500,8 +500,10 @@ static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
params_le->active_time = cpu_to_le32(-1);
params_le->passive_time = cpu_to_le32(-1);
params_le->home_time = cpu_to_le32(-1);
- if (ssid && ssid->SSID_len)
- memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
+ if (ssid && ssid->SSID_len) {
+ params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
+ memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
+ }
}
static s32
@@ -1876,16 +1878,17 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
}
if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
- scb_val.val = cpu_to_le32(0);
+ memset(&scb_val, 0, sizeof(scb_val));
err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
sizeof(struct brcmf_scb_val_le));
- if (err)
+ if (err) {
WL_ERR("Could not get rssi (%d)\n", err);
-
- rssi = le32_to_cpu(scb_val.val);
- sinfo->filled |= STATION_INFO_SIGNAL;
- sinfo->signal = rssi;
- WL_CONN("RSSI %d dBm\n", rssi);
+ } else {
+ rssi = le32_to_cpu(scb_val.val);
+ sinfo->filled |= STATION_INFO_SIGNAL;
+ sinfo->signal = rssi;
+ WL_CONN("RSSI %d dBm\n", rssi);
+ }
}
done:
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index 9a4c63f927cb..64a48f06d68b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -77,7 +77,7 @@
NL80211_RRF_NO_IBSS)
static const struct ieee80211_regdomain brcms_regdom_x2 = {
- .n_reg_rules = 7,
+ .n_reg_rules = 6,
.alpha2 = "X2",
.reg_rules = {
BRCM_2GHZ_2412_2462,
@@ -382,9 +382,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
{
struct brcms_c_info *wlc = wlc_cm->wlc;
struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel;
- const struct ieee80211_reg_rule *reg_rule;
struct txpwr_limits txpwr;
- int ret;
brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
@@ -393,8 +391,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec,
);
/* set or restore gmode as required by regulatory */
- ret = freq_reg_info(wlc->wiphy, ch->center_freq, 0, &reg_rule);
- if (!ret && (reg_rule->flags & NL80211_RRF_NO_OFDM))
+ if (ch->flags & IEEE80211_CHAN_NO_OFDM)
brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
else
brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 9e79d47e077f..a5edebeb0b4f 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -121,7 +121,8 @@ static struct ieee80211_channel brcms_2ghz_chantable[] = {
IEEE80211_CHAN_NO_HT40PLUS),
CHAN2GHZ(14, 2484,
IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+ IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
+ IEEE80211_CHAN_NO_OFDM)
};
static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
@@ -1232,6 +1233,9 @@ uint brcms_reset(struct brcms_info *wl)
/* dpc will not be rescheduled */
wl->resched = false;
+ /* inform publicly that interface is down */
+ wl->pub->up = false;
+
return 0;
}
diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c
index 95aa8e1683ec..83324b321652 100644
--- a/drivers/net/wireless/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/ipw2x00/ipw2100.c
@@ -2042,7 +2042,8 @@ static void isr_indicate_associated(struct ipw2100_priv *priv, u32 status)
return;
}
len = ETH_ALEN;
- ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, &bssid, &len);
+ ret = ipw2100_get_ordinal(priv, IPW_ORD_STAT_ASSN_AP_BSSID, bssid,
+ &len);
if (ret) {
IPW_DEBUG_INFO("failed querying ordinals at line %d\n",
__LINE__);
diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
index 46782f1102ac..a47b306b522c 100644
--- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c
@@ -124,6 +124,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file,
const struct fw_img *img;
size_t bufsz;
+ if (!iwl_is_ready_rf(priv))
+ return -EAGAIN;
+
/* default is to dump the entire data segment */
if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) {
priv->dbgfs_sram_offset = 0x800000;
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 6fddd2785e6e..a82f46c10f5e 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -707,11 +707,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags,
*/
static bool rs_use_green(struct ieee80211_sta *sta)
{
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
- !(ctx->ht.non_gf_sta_present);
+ /*
+ * There's a bug somewhere in this code that causes the
+ * scaling to get stuck because GF+SGI can't be combined
+ * in SISO rates. Until we find that bug, disable GF, it
+ * has only limited benefit and we still interoperate with
+ * GF APs since we can always receive GF transmissions.
+ */
+ return false;
}
/**
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index d9694c58208c..4ffc18dc3a57 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -350,7 +350,7 @@ int iwl_queue_space(const struct iwl_queue *q);
/*****************************************************
* Error handling
******************************************************/
-int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display);
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
void iwl_dump_csr(struct iwl_trans *trans);
/*****************************************************
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 39a6ca1f009c..d1a61ba6247a 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -555,7 +555,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans)
}
iwl_dump_csr(trans);
- iwl_dump_fh(trans, NULL, false);
+ iwl_dump_fh(trans, NULL);
iwl_op_mode_nic_error(trans->op_mode);
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 939c2f78df58..dbeebef562d5 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -1442,6 +1442,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
return err;
err_free_irq:
+ trans_pcie->irq_requested = false;
free_irq(trans_pcie->irq, trans);
error:
iwl_free_isr_ict(trans);
@@ -1649,13 +1650,9 @@ static const char *get_fh_string(int cmd)
#undef IWL_CMD
}
-int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display)
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
{
int i;
-#ifdef CONFIG_IWLWIFI_DEBUG
- int pos = 0;
- size_t bufsz = 0;
-#endif
static const u32 fh_tbl[] = {
FH_RSCSR_CHNL0_STTS_WPTR_REG,
FH_RSCSR_CHNL0_RBDCB_BASE_REG,
@@ -1667,29 +1664,35 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display)
FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_ERROR_REG
};
-#ifdef CONFIG_IWLWIFI_DEBUG
- if (display) {
- bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (buf) {
+ int pos = 0;
+ size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
*buf = kmalloc(bufsz, GFP_KERNEL);
if (!*buf)
return -ENOMEM;
+
pos += scnprintf(*buf + pos, bufsz - pos,
"FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
pos += scnprintf(*buf + pos, bufsz - pos,
" %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
- }
+
return pos;
}
#endif
+
IWL_ERR(trans, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) {
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
IWL_ERR(trans, " %34s: 0X%08x\n",
get_fh_string(fh_tbl[i]),
iwl_read_direct32(trans, fh_tbl[i]));
- }
+
return 0;
}
@@ -1982,11 +1985,11 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file,
size_t count, loff_t *ppos)
{
struct iwl_trans *trans = file->private_data;
- char *buf;
+ char *buf = NULL;
int pos = 0;
ssize_t ret = -EFAULT;
- ret = pos = iwl_dump_fh(trans, &buf, true);
+ ret = pos = iwl_dump_fh(trans, &buf);
if (buf) {
ret = simple_read_from_buffer(user_buf,
count, ppos, buf, pos);
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index eb5de800ed90..1c10b542ab23 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -1254,6 +1254,7 @@ static int lbs_associate(struct lbs_private *priv,
netif_tx_wake_all_queues(priv->dev);
}
+ kfree(cmd);
done:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret;
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 76caebaa4397..4cb234349fbf 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -1314,6 +1314,7 @@ static void if_sdio_remove(struct sdio_func *func)
kfree(packet);
}
+ kfree(card);
lbs_deb_leave(LBS_DEB_SDIO);
}
@@ -1325,6 +1326,11 @@ static int if_sdio_suspend(struct device *dev)
mmc_pm_flag_t flags = sdio_get_host_pm_caps(func);
+ /* If we're powered off anyway, just let the mmc layer remove the
+ * card. */
+ if (!lbs_iface_active(card->priv))
+ return -ENOSYS;
+
dev_info(dev, "%s: suspend: PM flags = 0x%x\n",
sdio_func_id(func), flags);
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 55a77e41170a..27980778d992 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -10,6 +10,7 @@
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/usb.h>
+#include <linux/olpc-ec.h>
#ifdef CONFIG_OLPC
#include <asm/olpc.h>
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 58048189bd24..fe1ea43c5149 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -571,7 +571,10 @@ static int lbs_thread(void *data)
netdev_info(dev, "Timeout submitting command 0x%04x\n",
le16_to_cpu(cmdnode->cmdbuf->command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
- if (priv->reset_card)
+
+ /* Reset card, but only when it isn't in the process
+ * of being shutdown anyway. */
+ if (!dev->dismantle && priv->reset_card)
priv->reset_card(priv);
}
priv->cmd_timed_out = 0;
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index c68adec3cc8b..565527aee0ea 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -170,7 +170,20 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv,
cmd_code = le16_to_cpu(host_cmd->command);
cmd_size = le16_to_cpu(host_cmd->size);
- skb_trim(cmd_node->cmd_skb, cmd_size);
+ /* Adjust skb length */
+ if (cmd_node->cmd_skb->len > cmd_size)
+ /*
+ * cmd_size is less than sizeof(struct host_cmd_ds_command).
+ * Trim off the unused portion.
+ */
+ skb_trim(cmd_node->cmd_skb, cmd_size);
+ else if (cmd_node->cmd_skb->len < cmd_size)
+ /*
+ * cmd_size is larger than sizeof(struct host_cmd_ds_command)
+ * because we have appended custom IE TLV. Increase skb length
+ * accordingly.
+ */
+ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len);
do_gettimeofday(&tstamp);
dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d,"
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 7f207b6e9552..effb044a8a9d 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -42,7 +42,7 @@ MODULE_FIRMWARE("isl3887usb");
* whenever you add a new device.
*/
-static struct usb_device_id p54u_table[] __devinitdata = {
+static struct usb_device_id p54u_table[] = {
/* Version 1 devices (pci chip + net2280) */
{USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
{USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 241162e8111d..7a4ae9ee1c63 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1803,6 +1803,7 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev,
struct cfg80211_pmksa *pmksa,
int max_pmkids)
{
+ struct ndis_80211_pmkid *new_pmkids;
int i, err, newlen;
unsigned int count;
@@ -1833,11 +1834,12 @@ static struct ndis_80211_pmkid *update_pmkid(struct usbnet *usbdev,
/* add new pmkid */
newlen = sizeof(*pmkids) + (count + 1) * sizeof(pmkids->bssid_info[0]);
- pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
- if (!pmkids) {
+ new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
+ if (!new_pmkids) {
err = -ENOMEM;
goto error;
}
+ pmkids = new_pmkids;
pmkids->length = cpu_to_le32(newlen);
pmkids->bssid_info_count = cpu_to_le32(count + 1);
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 8b9dbd76a252..64328af496f5 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1611,6 +1611,7 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1624,6 +1625,14 @@ static int rt2400pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+ rt2x00_set_field32(&reg, GPIOCSR_BIT8, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2400pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h
index d3a4a68cc439..7564ae992b73 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.h
+++ b/drivers/net/wireless/rt2x00/rt2400pci.h
@@ -670,6 +670,7 @@
#define GPIOCSR_BIT5 FIELD32(0x00000020)
#define GPIOCSR_BIT6 FIELD32(0x00000040)
#define GPIOCSR_BIT7 FIELD32(0x00000080)
+#define GPIOCSR_BIT8 FIELD32(0x00000100)
/*
* BBPPCSR: BBP Pin control register.
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index d2cf8a4bc8b5..3de0406735f6 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1929,6 +1929,7 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1942,6 +1943,14 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIOCSR, &reg);
+ rt2x00_set_field32(&reg, GPIOCSR_DIR0, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIOCSR, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2500pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index 3aae36bb0a9e..89fee311d8fd 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -283,7 +283,7 @@ static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev)
u16 reg;
rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
- return rt2x00_get_field32(reg, MAC_CSR19_BIT7);
+ return rt2x00_get_field16(reg, MAC_CSR19_BIT7);
}
#ifdef CONFIG_RT2X00_LIB_LEDS
@@ -1768,6 +1768,7 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u16 reg;
/*
* Allocate eeprom data.
@@ -1781,6 +1782,14 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2500usb_register_read(rt2x00dev, MAC_CSR19, &reg);
+ rt2x00_set_field16(&reg, MAC_CSR19_BIT8, 0);
+ rt2500usb_register_write(rt2x00dev, MAC_CSR19, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2500usb_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h
index b493306a7eed..196bd5103e4f 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.h
+++ b/drivers/net/wireless/rt2x00/rt2500usb.h
@@ -189,14 +189,15 @@
* MAC_CSR19: GPIO control register.
*/
#define MAC_CSR19 0x0426
-#define MAC_CSR19_BIT0 FIELD32(0x0001)
-#define MAC_CSR19_BIT1 FIELD32(0x0002)
-#define MAC_CSR19_BIT2 FIELD32(0x0004)
-#define MAC_CSR19_BIT3 FIELD32(0x0008)
-#define MAC_CSR19_BIT4 FIELD32(0x0010)
-#define MAC_CSR19_BIT5 FIELD32(0x0020)
-#define MAC_CSR19_BIT6 FIELD32(0x0040)
-#define MAC_CSR19_BIT7 FIELD32(0x0080)
+#define MAC_CSR19_BIT0 FIELD16(0x0001)
+#define MAC_CSR19_BIT1 FIELD16(0x0002)
+#define MAC_CSR19_BIT2 FIELD16(0x0004)
+#define MAC_CSR19_BIT3 FIELD16(0x0008)
+#define MAC_CSR19_BIT4 FIELD16(0x0010)
+#define MAC_CSR19_BIT5 FIELD16(0x0020)
+#define MAC_CSR19_BIT6 FIELD16(0x0040)
+#define MAC_CSR19_BIT7 FIELD16(0x0080)
+#define MAC_CSR19_BIT8 FIELD16(0x0100)
/*
* MAC_CSR20: LED control register.
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 88455b1b9fe0..b93516d832fb 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -221,6 +221,67 @@ static void rt2800_rf_write(struct rt2x00_dev *rt2x00dev,
mutex_unlock(&rt2x00dev->csr_mutex);
}
+static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ int i, count;
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+ if (rt2x00_get_field32(reg, WLAN_EN))
+ return 0;
+
+ rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
+ rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
+ rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
+ rt2x00_set_field32(&reg, WLAN_EN, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+
+ udelay(REGISTER_BUSY_DELAY);
+
+ count = 0;
+ do {
+ /*
+ * Check PLL_LD & XTAL_RDY.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
+ if (rt2x00_get_field32(reg, PLL_LD) &&
+ rt2x00_get_field32(reg, XTAL_RDY))
+ break;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ if (i >= REGISTER_BUSY_COUNT) {
+
+ if (count >= 10)
+ return -EIO;
+
+ rt2800_register_write(rt2x00dev, 0x58, 0x018);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x418);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x618);
+ udelay(REGISTER_BUSY_DELAY);
+ count++;
+ } else {
+ count = 0;
+ }
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
+ rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
+ rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
+ rt2x00_set_field32(&reg, WLAN_RESET, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2x00_set_field32(&reg, WLAN_RESET, 0);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
+ } while (count != 0);
+
+ return 0;
+}
+
void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token,
const u8 arg0, const u8 arg1)
@@ -400,6 +461,13 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
{
unsigned int i;
u32 reg;
+ int retval;
+
+ if (rt2x00_rt(rt2x00dev, RT3290)) {
+ retval = rt2800_enable_wlan_rt3290(rt2x00dev);
+ if (retval)
+ return -EBUSY;
+ }
/*
* If driver doesn't wake up firmware here,
@@ -4021,6 +4089,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
msleep(1);
rt2800_register_read(rt2x00dev, LDO_CFG0, &reg);
+ rt2x00_set_field32(&reg, LDO_CFG0_LDO_CORE_VLEVEL, 0);
rt2x00_set_field32(&reg, LDO_CFG0_BGSEL, 1);
rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
}
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 235376e9cb04..4765bbd654cd 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -980,69 +980,10 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
return rt2800_validate_eeprom(rt2x00dev);
}
-static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- int i, count;
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
- if (rt2x00_get_field32(reg, WLAN_EN))
- return 0;
-
- rt2x00_set_field32(&reg, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
- rt2x00_set_field32(&reg, FRC_WL_ANT_SET, 1);
- rt2x00_set_field32(&reg, WLAN_CLK_EN, 0);
- rt2x00_set_field32(&reg, WLAN_EN, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-
- udelay(REGISTER_BUSY_DELAY);
-
- count = 0;
- do {
- /*
- * Check PLL_LD & XTAL_RDY.
- */
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800_register_read(rt2x00dev, CMB_CTRL, &reg);
- if (rt2x00_get_field32(reg, PLL_LD) &&
- rt2x00_get_field32(reg, XTAL_RDY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- if (i >= REGISTER_BUSY_COUNT) {
-
- if (count >= 10)
- return -EIO;
-
- rt2800_register_write(rt2x00dev, 0x58, 0x018);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x418);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x618);
- udelay(REGISTER_BUSY_DELAY);
- count++;
- } else {
- count = 0;
- }
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, &reg);
- rt2x00_set_field32(&reg, PCIE_APP0_CLK_REQ, 0);
- rt2x00_set_field32(&reg, WLAN_CLK_EN, 1);
- rt2x00_set_field32(&reg, WLAN_RESET, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2x00_set_field32(&reg, WLAN_RESET, 0);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
- } while (count != 0);
-
- return 0;
-}
static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -1056,6 +997,14 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+ rt2x00pci_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2800_probe_hw_mode(rt2x00dev);
@@ -1063,17 +1012,6 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
- * In probe phase call rt2800_enable_wlan_rt3290 to enable wlan
- * clk for rt3290. That avoid the MCU fail in start phase.
- */
- if (rt2x00_rt(rt2x00dev, RT3290)) {
- retval = rt2800_enable_wlan_rt3290(rt2x00dev);
-
- if (retval)
- return retval;
- }
-
- /*
* This device has multiple filters for control frames
* and has a separate filter for PS Poll frames.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 6cf336595e25..6b4226b71618 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -667,8 +667,16 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
skb_pull(entry->skb, RXINFO_DESC_SIZE);
/*
- * FIXME: we need to check for rx_pkt_len validity
+ * Check for rx_pkt_len validity. Return if invalid, leaving
+ * rxdesc->size zeroed out by the upper level.
*/
+ if (unlikely(rx_pkt_len == 0 ||
+ rx_pkt_len > entry->queue->data_size)) {
+ ERROR(entry->queue->rt2x00dev,
+ "Bad frame size %d, forcing to 0\n", rx_pkt_len);
+ return;
+ }
+
rxd = (__le32 *)(entry->skb->data + rx_pkt_len);
/*
@@ -736,6 +744,7 @@ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -749,6 +758,14 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, &reg);
+ rt2x00_set_field32(&reg, GPIO_CTRL_CFG_GPIOD_BIT2, 1);
+ rt2x00usb_register_write(rt2x00dev, GPIO_CTRL_CFG, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt2800_probe_hw_mode(rt2x00dev);
@@ -1157,6 +1174,8 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x1690, 0x0744) },
{ USB_DEVICE(0x1690, 0x0761) },
{ USB_DEVICE(0x1690, 0x0764) },
+ /* ASUS */
+ { USB_DEVICE(0x0b05, 0x179d) },
/* Cisco */
{ USB_DEVICE(0x167b, 0x4001) },
/* EnGenius */
@@ -1222,7 +1241,6 @@ static struct usb_device_id rt2800usb_device_table[] = {
{ USB_DEVICE(0x0b05, 0x1760) },
{ USB_DEVICE(0x0b05, 0x1761) },
{ USB_DEVICE(0x0b05, 0x1790) },
- { USB_DEVICE(0x0b05, 0x179d) },
/* AzureWave */
{ USB_DEVICE(0x13d3, 0x3262) },
{ USB_DEVICE(0x13d3, 0x3284) },
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index a6b88bd4a1a5..3f07e36f462b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -629,7 +629,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)
*/
if (unlikely(rxdesc.size == 0 ||
rxdesc.size > entry->queue->data_size)) {
- WARNING(rt2x00dev, "Wrong frame size %d max %d.\n",
+ ERROR(rt2x00dev, "Wrong frame size %d max %d.\n",
rxdesc.size, entry->queue->data_size);
dev_kfree_skb(entry->skb);
goto renew_skb;
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index f32259686b45..b8ec96163922 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2243,8 +2243,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)
{
- struct ieee80211_conf conf = { .flags = 0 };
- struct rt2x00lib_conf libconf = { .conf = &conf };
+ struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf };
rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
}
@@ -2833,6 +2832,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Disable power saving.
@@ -2851,6 +2851,14 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00pci_register_read(rt2x00dev, MAC_CSR13, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR13_BIT13, 1);
+ rt2x00pci_register_write(rt2x00dev, MAC_CSR13, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt61pci_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h
index e3cd6db76b0e..8f3da5a56766 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.h
+++ b/drivers/net/wireless/rt2x00/rt61pci.h
@@ -372,6 +372,7 @@ struct hw_pairwise_ta_entry {
#define MAC_CSR13_BIT10 FIELD32(0x00000400)
#define MAC_CSR13_BIT11 FIELD32(0x00000800)
#define MAC_CSR13_BIT12 FIELD32(0x00001000)
+#define MAC_CSR13_BIT13 FIELD32(0x00002000)
/*
* MAC_CSR14: LED control register.
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index ba6e434b859d..248436c13ce0 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -2177,6 +2177,7 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ u32 reg;
/*
* Allocate eeprom data.
@@ -2190,6 +2191,14 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return retval;
/*
+ * Enable rfkill polling by setting GPIO direction of the
+ * rfkill switch GPIO pin correctly.
+ */
+ rt2x00usb_register_read(rt2x00dev, MAC_CSR13, &reg);
+ rt2x00_set_field32(&reg, MAC_CSR13_BIT15, 0);
+ rt2x00usb_register_write(rt2x00dev, MAC_CSR13, reg);
+
+ /*
* Initialize hw specifications.
*/
retval = rt73usb_probe_hw_mode(rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h
index 9f6b470414d3..df1cc116b83b 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.h
+++ b/drivers/net/wireless/rt2x00/rt73usb.h
@@ -282,6 +282,9 @@ struct hw_pairwise_ta_entry {
#define MAC_CSR13_BIT10 FIELD32(0x00000400)
#define MAC_CSR13_BIT11 FIELD32(0x00000800)
#define MAC_CSR13_BIT12 FIELD32(0x00001000)
+#define MAC_CSR13_BIT13 FIELD32(0x00002000)
+#define MAC_CSR13_BIT14 FIELD32(0x00004000)
+#define MAC_CSR13_BIT15 FIELD32(0x00008000)
/*
* MAC_CSR14: LED control register.
diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
index 71a30b026089..533024095c43 100644
--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
@@ -44,7 +44,7 @@ MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
MODULE_LICENSE("GPL");
-static struct usb_device_id rtl8187_table[] __devinitdata = {
+static struct usb_device_id rtl8187_table[] = {
/* Asus */
{USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187},
/* Belkin */
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 04c3aef8a4f6..2925094b2d91 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -117,6 +117,7 @@
#define CHIP_VER_B BIT(4)
#define CHIP_92C_BITMASK BIT(0)
+#define CHIP_UNKNOWN BIT(7)
#define CHIP_92C_1T2R 0x03
#define CHIP_92C 0x01
#define CHIP_88C 0x00
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index bd0da7ef290b..dd4bb0950a57 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -994,8 +994,16 @@ static enum version_8192c _rtl92ce_read_chip_version(struct ieee80211_hw *hw)
version = (value32 & TYPE_ID) ? VERSION_A_CHIP_92C :
VERSION_A_CHIP_88C;
} else {
- version = (value32 & TYPE_ID) ? VERSION_B_CHIP_92C :
- VERSION_B_CHIP_88C;
+ version = (enum version_8192c) (CHIP_VER_B |
+ ((value32 & TYPE_ID) ? CHIP_92C_BITMASK : 0) |
+ ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0));
+ if ((!IS_CHIP_VENDOR_UMC(version)) && (value32 &
+ CHIP_VER_RTL_MASK)) {
+ version = (enum version_8192c)(version |
+ ((((value32 & CHIP_VER_RTL_MASK) == BIT(12))
+ ? CHIP_VENDOR_UMC_B_CUT : CHIP_UNKNOWN) |
+ CHIP_VENDOR_UMC));
+ }
}
switch (version) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index 3aa927f8b9b9..7d8f96405f42 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -162,10 +162,12 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
/* request fw */
if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
- !IS_92C_SERIAL(rtlhal->version))
+ !IS_92C_SERIAL(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
- else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+ } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+ pr_info("****** This B_CUT device may not work with kernels 3.6 and earlier\n");
+ }
rtlpriv->max_fw_size = 0x4000;
pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 30899901aef5..650f79a1f2bd 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -57,8 +57,7 @@
static const struct ethtool_ops xennet_ethtool_ops;
struct netfront_cb {
- struct page *page;
- unsigned offset;
+ int pull_to;
};
#define NETFRONT_SKB_CB(skb) ((struct netfront_cb *)((skb)->cb))
@@ -867,15 +866,9 @@ static int handle_incoming_queue(struct net_device *dev,
struct sk_buff *skb;
while ((skb = __skb_dequeue(rxq)) != NULL) {
- struct page *page = NETFRONT_SKB_CB(skb)->page;
- void *vaddr = page_address(page);
- unsigned offset = NETFRONT_SKB_CB(skb)->offset;
-
- memcpy(skb->data, vaddr + offset,
- skb_headlen(skb));
+ int pull_to = NETFRONT_SKB_CB(skb)->pull_to;
- if (page != skb_frag_page(&skb_shinfo(skb)->frags[0]))
- __free_page(page);
+ __pskb_pull_tail(skb, pull_to - skb_headlen(skb));
/* Ethernet work: Delayed to here as it peeks the header. */
skb->protocol = eth_type_trans(skb, dev);
@@ -913,7 +906,6 @@ static int xennet_poll(struct napi_struct *napi, int budget)
struct sk_buff_head errq;
struct sk_buff_head tmpq;
unsigned long flags;
- unsigned int len;
int err;
spin_lock(&np->rx_lock);
@@ -955,24 +947,13 @@ err:
}
}
- NETFRONT_SKB_CB(skb)->page =
- skb_frag_page(&skb_shinfo(skb)->frags[0]);
- NETFRONT_SKB_CB(skb)->offset = rx->offset;
-
- len = rx->status;
- if (len > RX_COPY_THRESHOLD)
- len = RX_COPY_THRESHOLD;
- skb_put(skb, len);
+ NETFRONT_SKB_CB(skb)->pull_to = rx->status;
+ if (NETFRONT_SKB_CB(skb)->pull_to > RX_COPY_THRESHOLD)
+ NETFRONT_SKB_CB(skb)->pull_to = RX_COPY_THRESHOLD;
- if (rx->status > len) {
- skb_shinfo(skb)->frags[0].page_offset =
- rx->offset + len;
- skb_frag_size_set(&skb_shinfo(skb)->frags[0], rx->status - len);
- skb->data_len = rx->status - len;
- } else {
- __skb_fill_page_desc(skb, 0, NULL, 0, 0);
- skb_shinfo(skb)->nr_frags = 0;
- }
+ skb_shinfo(skb)->frags[0].page_offset = rx->offset;
+ skb_frag_size_set(&skb_shinfo(skb)->frags[0], rx->status);
+ skb->data_len = rx->status;
i = xennet_fill_frags(np, skb, &tmpq);
@@ -999,7 +980,7 @@ err:
* receive throughout using the standard receive
* buffer size was cut by 25%(!!!).
*/
- skb->truesize += skb->data_len - (RX_COPY_THRESHOLD - len);
+ skb->truesize += skb->data_len - RX_COPY_THRESHOLD;
skb->len += skb->data_len;
if (rx->flags & XEN_NETRXF_csum_blank)
diff --git a/drivers/of/base.c b/drivers/of/base.c
index c181b94abc36..d4a1c9a043e1 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -364,6 +364,33 @@ struct device_node *of_get_next_child(const struct device_node *node,
EXPORT_SYMBOL(of_get_next_child);
/**
+ * of_get_next_available_child - Find the next available child node
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * This function is like of_get_next_child(), except that it
+ * automatically skips any disabled nodes (i.e. status = "disabled").
+ */
+struct device_node *of_get_next_available_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ struct device_node *next;
+
+ read_lock(&devtree_lock);
+ next = prev ? prev->sibling : node->child;
+ for (; next; next = next->sibling) {
+ if (!of_device_is_available(next))
+ continue;
+ if (of_node_get(next))
+ break;
+ }
+ of_node_put(prev);
+ read_unlock(&devtree_lock);
+ return next;
+}
+EXPORT_SYMBOL(of_get_next_available_child);
+
+/**
* of_find_node_by_path - Find a node matching a full OF path
* @path: The full path to match
*
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index fbf7b26c7c8a..c5792d622dc4 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -266,8 +266,8 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
}
if (!error)
- dev_printk(KERN_INFO, &dev->dev,
- "power state changed by ACPI to D%d\n", state);
+ dev_info(&dev->dev, "power state changed by ACPI to %s\n",
+ pci_power_name(state));
return error;
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 185be3703343..d6fd6b6d9d4b 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -280,8 +280,12 @@ static long local_pci_probe(void *_ddi)
{
struct drv_dev_and_id *ddi = _ddi;
struct device *dev = &ddi->dev->dev;
+ struct device *parent = dev->parent;
int rc;
+ /* The parent bridge must be in active state when probing */
+ if (parent)
+ pm_runtime_get_sync(parent);
/* Unbound PCI devices are always set to disabled and suspended.
* During probe, the device is set to enabled and active and the
* usage count is incremented. If the driver supports runtime PM,
@@ -298,6 +302,8 @@ static long local_pci_probe(void *_ddi)
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
}
+ if (parent)
+ pm_runtime_put(parent);
return rc;
}
@@ -959,6 +965,13 @@ static int pci_pm_poweroff_noirq(struct device *dev)
if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev);
+ /*
+ * The reason for doing this here is the same as for the analogous code
+ * in pci_pm_suspend_noirq().
+ */
+ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
+ pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
return 0;
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 6869009c7393..02d107b15281 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -458,6 +458,40 @@ boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
}
struct device_attribute vga_attr = __ATTR_RO(boot_vga);
+static void
+pci_config_pm_runtime_get(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ if (parent)
+ pm_runtime_get_sync(parent);
+ pm_runtime_get_noresume(dev);
+ /*
+ * pdev->current_state is set to PCI_D3cold during suspending,
+ * so wait until suspending completes
+ */
+ pm_runtime_barrier(dev);
+ /*
+ * Only need to resume devices in D3cold, because config
+ * registers are still accessible for devices suspended but
+ * not in D3cold.
+ */
+ if (pdev->current_state == PCI_D3cold)
+ pm_runtime_resume(dev);
+}
+
+static void
+pci_config_pm_runtime_put(struct pci_dev *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device *parent = dev->parent;
+
+ pm_runtime_put(dev);
+ if (parent)
+ pm_runtime_put_sync(parent);
+}
+
static ssize_t
pci_read_config(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
@@ -484,6 +518,8 @@ pci_read_config(struct file *filp, struct kobject *kobj,
size = count;
}
+ pci_config_pm_runtime_get(dev);
+
if ((off & 1) && size) {
u8 val;
pci_user_read_config_byte(dev, off, &val);
@@ -529,6 +565,8 @@ pci_read_config(struct file *filp, struct kobject *kobj,
--size;
}
+ pci_config_pm_runtime_put(dev);
+
return count;
}
@@ -549,6 +587,8 @@ pci_write_config(struct file* filp, struct kobject *kobj,
count = size;
}
+ pci_config_pm_runtime_get(dev);
+
if ((off & 1) && size) {
pci_user_write_config_byte(dev, off, data[off - init_off]);
off++;
@@ -587,6 +627,8 @@ pci_write_config(struct file* filp, struct kobject *kobj,
--size;
}
+ pci_config_pm_runtime_put(dev);
+
return count;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index f3ea977a5b1b..ab4bf5a4c2f1 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1941,6 +1941,7 @@ void pci_pm_init(struct pci_dev *dev)
dev->pm_cap = pm;
dev->d3_delay = PCI_PM_D3_WAIT;
dev->d3cold_delay = PCI_PM_D3COLD_WAIT;
+ dev->d3cold_allowed = true;
dev->d1_support = false;
dev->d2_support = false;
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 3a7eefcb270a..e76b44777dbf 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -140,9 +140,17 @@ static int pcie_port_runtime_resume(struct device *dev)
{
return 0;
}
+
+static int pcie_port_runtime_idle(struct device *dev)
+{
+ /* Delay for a short while to prevent too frequent suspend/resume */
+ pm_schedule_suspend(dev, 10);
+ return -EBUSY;
+}
#else
#define pcie_port_runtime_suspend NULL
#define pcie_port_runtime_resume NULL
+#define pcie_port_runtime_idle NULL
#endif
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
@@ -155,6 +163,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.resume_noirq = pcie_port_resume_noirq,
.runtime_suspend = pcie_port_runtime_suspend,
.runtime_resume = pcie_port_runtime_resume,
+ .runtime_idle = pcie_port_runtime_idle,
};
#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
@@ -200,6 +209,11 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
return status;
pci_save_state(dev);
+ /*
+ * D3cold may not work properly on some PCIe port, so disable
+ * it by default.
+ */
+ dev->d3cold_allowed = false;
if (!pci_match_id(port_runtime_pm_black_list, dev))
pm_runtime_put_noidle(&dev->dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 6c143b4497ca..9f8a6b79a8ec 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -144,15 +144,13 @@ static inline unsigned long decode_bar(struct pci_dev *dev, u32 bar)
case PCI_BASE_ADDRESS_MEM_TYPE_32:
break;
case PCI_BASE_ADDRESS_MEM_TYPE_1M:
- dev_info(&dev->dev, "1M mem BAR treated as 32-bit BAR\n");
+ /* 1M mem BAR treated as 32-bit BAR */
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
flags |= IORESOURCE_MEM_64;
break;
default:
- dev_warn(&dev->dev,
- "mem unknown type %x treated as 32-bit BAR\n",
- mem_type);
+ /* mem unknown type treated as 32-bit BAR */
break;
}
return flags;
@@ -173,9 +171,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
u32 l, sz, mask;
u16 orig_cmd;
struct pci_bus_region region;
+ bool bar_too_big = false, bar_disabled = false;
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
+ /* No printks while decoding is disabled! */
if (!dev->mmio_always_on) {
pci_read_config_word(dev, PCI_COMMAND, &orig_cmd);
pci_write_config_word(dev, PCI_COMMAND,
@@ -240,8 +240,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
goto fail;
if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
- dev_err(&dev->dev, "reg %x: can't handle 64-bit BAR\n",
- pos);
+ bar_too_big = true;
goto fail;
}
@@ -252,12 +251,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
region.start = 0;
region.end = sz64;
pcibios_bus_to_resource(dev, res, &region);
+ bar_disabled = true;
} else {
region.start = l64;
region.end = l64 + sz64;
pcibios_bus_to_resource(dev, res, &region);
- dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n",
- pos, res);
}
} else {
sz = pci_size(l, sz, mask);
@@ -268,18 +266,23 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
region.start = l;
region.end = l + sz;
pcibios_bus_to_resource(dev, res, &region);
-
- dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res);
}
- out:
+ goto out;
+
+
+fail:
+ res->flags = 0;
+out:
if (!dev->mmio_always_on)
pci_write_config_word(dev, PCI_COMMAND, orig_cmd);
+ if (bar_too_big)
+ dev_err(&dev->dev, "reg %x: can't handle 64-bit BAR\n", pos);
+ if (res->flags && !bar_disabled)
+ dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res);
+
return (res->flags & IORESOURCE_MEM_64) ? 1 : 0;
- fail:
- res->flags = 0;
- goto out;
}
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index fb7f3bebdc69..dc5c126e398a 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -657,11 +657,7 @@ static struct pinctrl *pinctrl_get_locked(struct device *dev)
if (p != NULL)
return ERR_PTR(-EBUSY);
- p = create_pinctrl(dev);
- if (IS_ERR(p))
- return p;
-
- return p;
+ return create_pinctrl(dev);
}
/**
@@ -738,11 +734,8 @@ static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p,
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
state = create_state(p, name);
- if (IS_ERR(state))
- return state;
- } else {
- return ERR_PTR(-ENODEV);
- }
+ } else
+ state = ERR_PTR(-ENODEV);
}
return state;
diff --git a/drivers/pinctrl/pinctrl-imx23.c b/drivers/pinctrl/pinctrl-imx23.c
index 75d3eff94296..3674d877ed7c 100644
--- a/drivers/pinctrl/pinctrl-imx23.c
+++ b/drivers/pinctrl/pinctrl-imx23.c
@@ -292,7 +292,7 @@ static int __init imx23_pinctrl_init(void)
{
return platform_driver_register(&imx23_pinctrl_driver);
}
-arch_initcall(imx23_pinctrl_init);
+postcore_initcall(imx23_pinctrl_init);
static void __exit imx23_pinctrl_exit(void)
{
diff --git a/drivers/pinctrl/pinctrl-imx28.c b/drivers/pinctrl/pinctrl-imx28.c
index b973026811a2..0f5b2122b1ba 100644
--- a/drivers/pinctrl/pinctrl-imx28.c
+++ b/drivers/pinctrl/pinctrl-imx28.c
@@ -408,7 +408,7 @@ static int __init imx28_pinctrl_init(void)
{
return platform_driver_register(&imx28_pinctrl_driver);
}
-arch_initcall(imx28_pinctrl_init);
+postcore_initcall(imx28_pinctrl_init);
static void __exit imx28_pinctrl_exit(void)
{
diff --git a/drivers/pinctrl/pinctrl-imx51.c b/drivers/pinctrl/pinctrl-imx51.c
index 689b3c88dd2e..9fd02162a3c2 100644
--- a/drivers/pinctrl/pinctrl-imx51.c
+++ b/drivers/pinctrl/pinctrl-imx51.c
@@ -974,7 +974,7 @@ static struct imx_pin_reg imx51_pin_regs[] = {
IMX_PIN_REG(MX51_PAD_EIM_DA13, NO_PAD, 0x050, 0, 0x000, 0), /* MX51_PAD_EIM_DA13__EIM_DA13 */
IMX_PIN_REG(MX51_PAD_EIM_DA14, NO_PAD, 0x054, 0, 0x000, 0), /* MX51_PAD_EIM_DA14__EIM_DA14 */
IMX_PIN_REG(MX51_PAD_EIM_DA15, NO_PAD, 0x058, 0, 0x000, 0), /* MX51_PAD_EIM_DA15__EIM_DA15 */
- IMX_PIN_REG(MX51_PAD_SD2_CMD, NO_PAD, 0x3b4, 2, 0x91c, 3), /* MX51_PAD_SD2_CMD__CSPI_MOSI */
+ IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 2, 0x91c, 3), /* MX51_PAD_SD2_CMD__CSPI_MOSI */
IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 1, 0x9b0, 2), /* MX51_PAD_SD2_CMD__I2C1_SCL */
IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 0, 0x000, 0), /* MX51_PAD_SD2_CMD__SD2_CMD */
IMX_PIN_REG(MX51_PAD_SD2_CLK, 0x7c0, 0x3b8, 2, 0x914, 3), /* MX51_PAD_SD2_CLK__CSPI_SCLK */
diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c
index 6f99769c6733..a39fb7a6fc51 100644
--- a/drivers/pinctrl/pinctrl-nomadik-db8500.c
+++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c
@@ -505,6 +505,8 @@ static const unsigned kp_b_1_pins[] = { DB8500_PIN_F3, DB8500_PIN_F1,
DB8500_PIN_J3, DB8500_PIN_H2, DB8500_PIN_J2, DB8500_PIN_H1,
DB8500_PIN_F4, DB8500_PIN_E3, DB8500_PIN_E4, DB8500_PIN_D2,
DB8500_PIN_C1, DB8500_PIN_D3, DB8500_PIN_C2, DB8500_PIN_D5 };
+static const unsigned kp_b_2_pins[] = { DB8500_PIN_F3, DB8500_PIN_F1,
+ DB8500_PIN_G3, DB8500_PIN_G2, DB8500_PIN_F4, DB8500_PIN_E3};
static const unsigned sm_b_1_pins[] = { DB8500_PIN_C6, DB8500_PIN_B3,
DB8500_PIN_C4, DB8500_PIN_E6, DB8500_PIN_A3, DB8500_PIN_B6,
DB8500_PIN_D6, DB8500_PIN_B7, DB8500_PIN_D7, DB8500_PIN_D8,
@@ -662,6 +664,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = {
DB8500_PIN_GROUP(spi3_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(msp1txrx_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(kp_b_1, NMK_GPIO_ALT_B),
+ DB8500_PIN_GROUP(kp_b_2, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(sm_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(smcs0_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(smcs1_b_1, NMK_GPIO_ALT_B),
@@ -751,7 +754,7 @@ DB8500_FUNC_GROUPS(msp1, "msp1txrx_a_1", "msp1_a_1", "msp1txrx_b_1");
DB8500_FUNC_GROUPS(lcdb, "lcdb_a_1");
DB8500_FUNC_GROUPS(lcd, "lcdvsi0_a_1", "lcdvsi1_a_1", "lcd_d0_d7_a_1",
"lcd_d8_d11_a_1", "lcd_d12_d23_a_1", "lcd_b_1");
-DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_c_1", "kp_oc1_1");
+DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_b_2", "kp_c_1", "kp_oc1_1");
DB8500_FUNC_GROUPS(mc2, "mc2_a_1", "mc2rstn_c_1");
DB8500_FUNC_GROUPS(ssp1, "ssp1_a_1");
DB8500_FUNC_GROUPS(ssp0, "ssp0_a_1");
@@ -766,7 +769,7 @@ DB8500_FUNC_GROUPS(ipgpio, "ipgpio0_a_1", "ipgpio1_a_1", "ipgpio7_b_1",
DB8500_FUNC_GROUPS(msp2, "msp2sck_a_1", "msp2_a_1");
DB8500_FUNC_GROUPS(mc4, "mc4_a_1", "mc4rstn_c_1");
DB8500_FUNC_GROUPS(mc1, "mc1_a_1", "mc1dir_a_1");
-DB8500_FUNC_GROUPS(hsi, "hsir1_a_1", "hsit1_a_1", "hsit_a_2");
+DB8500_FUNC_GROUPS(hsi, "hsir_a_1", "hsit_a_1", "hsit_a_2");
DB8500_FUNC_GROUPS(clkout, "clkout_a_1", "clkout_a_2", "clkout_c_1");
DB8500_FUNC_GROUPS(usb, "usb_a_1");
DB8500_FUNC_GROUPS(trig, "trig_b_1");
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c
index 53b0d49a7a1c..3dde6537adb8 100644
--- a/drivers/pinctrl/pinctrl-nomadik.c
+++ b/drivers/pinctrl/pinctrl-nomadik.c
@@ -1292,7 +1292,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
NOMADIK_GPIO_TO_IRQ(pdata->first_gpio),
0, &nmk_gpio_irq_simple_ops, nmk_chip);
if (!nmk_chip->domain) {
- pr_err("%s: Failed to create irqdomain\n", np->full_name);
+ dev_err(&dev->dev, "failed to create irqdomain\n");
ret = -ENOSYS;
goto out;
}
@@ -1731,7 +1731,6 @@ static int __devinit nmk_pinctrl_probe(struct platform_device *pdev)
for (i = 0; i < npct->soc->gpio_num_ranges; i++) {
if (!nmk_gpio_chips[i]) {
dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i);
- devm_kfree(&pdev->dev, npct);
return -EPROBE_DEFER;
}
npct->soc->gpio_ranges[i].gc = &nmk_gpio_chips[i]->chip;
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 2aae8a8978e9..7fca6ce5952b 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -1217,7 +1217,6 @@ out_no_rsc_remap:
iounmap(spmx->gpio_virtbase);
out_no_gpio_remap:
platform_set_drvdata(pdev, NULL);
- devm_kfree(&pdev->dev, spmx);
return ret;
}
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index a7ad8c112d91..309f5b9a70ec 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -1121,10 +1121,8 @@ static int __devinit u300_pmx_probe(struct platform_device *pdev)
upmx->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto out_no_resource;
- }
+ if (!res)
+ return -ENOENT;
upmx->phybase = res->start;
upmx->physize = resource_size(res);
@@ -1165,8 +1163,6 @@ out_no_remap:
platform_set_drvdata(pdev, NULL);
out_no_memregion:
release_mem_region(upmx->phybase, upmx->physize);
-out_no_resource:
- devm_kfree(&pdev->dev, upmx);
return ret;
}
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 782953ae4c03..b17c16ce54ad 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_OLPC) += olpc/
diff --git a/drivers/platform/olpc/Makefile b/drivers/platform/olpc/Makefile
new file mode 100644
index 000000000000..dc8b26bc7209
--- /dev/null
+++ b/drivers/platform/olpc/Makefile
@@ -0,0 +1,4 @@
+#
+# OLPC XO platform-specific drivers
+#
+obj-$(CONFIG_OLPC) += olpc-ec.o
diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c
new file mode 100644
index 000000000000..0f9f8596b300
--- /dev/null
+++ b/drivers/platform/olpc/olpc-ec.c
@@ -0,0 +1,336 @@
+/*
+ * Generic driver for the OLPC Embedded Controller.
+ *
+ * Copyright (C) 2011-2012 One Laptop per Child Foundation.
+ *
+ * Licensed under the GPL v2 or later.
+ */
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/olpc-ec.h>
+#include <asm/olpc.h>
+
+struct ec_cmd_desc {
+ u8 cmd;
+ u8 *inbuf, *outbuf;
+ size_t inlen, outlen;
+
+ int err;
+ struct completion finished;
+ struct list_head node;
+
+ void *priv;
+};
+
+struct olpc_ec_priv {
+ struct olpc_ec_driver *drv;
+ struct work_struct worker;
+ struct mutex cmd_lock;
+
+ /* Pending EC commands */
+ struct list_head cmd_q;
+ spinlock_t cmd_q_lock;
+
+ struct dentry *dbgfs_dir;
+
+ /*
+ * Running an EC command while suspending means we don't always finish
+ * the command before the machine suspends. This means that the EC
+ * is expecting the command protocol to finish, but we after a period
+ * of time (while the OS is asleep) the EC times out and restarts its
+ * idle loop. Meanwhile, the OS wakes up, thinks it's still in the
+ * middle of the command protocol, starts throwing random things at
+ * the EC... and everyone's uphappy.
+ */
+ bool suspended;
+};
+
+static struct olpc_ec_driver *ec_driver;
+static struct olpc_ec_priv *ec_priv;
+static void *ec_cb_arg;
+
+void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg)
+{
+ ec_driver = drv;
+ ec_cb_arg = arg;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_driver_register);
+
+static void olpc_ec_worker(struct work_struct *w)
+{
+ struct olpc_ec_priv *ec = container_of(w, struct olpc_ec_priv, worker);
+ struct ec_cmd_desc *desc = NULL;
+ unsigned long flags;
+
+ /* Grab the first pending command from the queue */
+ spin_lock_irqsave(&ec->cmd_q_lock, flags);
+ if (!list_empty(&ec->cmd_q)) {
+ desc = list_first_entry(&ec->cmd_q, struct ec_cmd_desc, node);
+ list_del(&desc->node);
+ }
+ spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
+
+ /* Do we actually have anything to do? */
+ if (!desc)
+ return;
+
+ /* Protect the EC hw with a mutex; only run one cmd at a time */
+ mutex_lock(&ec->cmd_lock);
+ desc->err = ec_driver->ec_cmd(desc->cmd, desc->inbuf, desc->inlen,
+ desc->outbuf, desc->outlen, ec_cb_arg);
+ mutex_unlock(&ec->cmd_lock);
+
+ /* Finished, wake up olpc_ec_cmd() */
+ complete(&desc->finished);
+
+ /* Run the worker thread again in case there are more cmds pending */
+ schedule_work(&ec->worker);
+}
+
+/*
+ * Throw a cmd descripter onto the list. We now have SMP OLPC machines, so
+ * locking is pretty critical.
+ */
+static void queue_ec_descriptor(struct ec_cmd_desc *desc,
+ struct olpc_ec_priv *ec)
+{
+ unsigned long flags;
+
+ INIT_LIST_HEAD(&desc->node);
+
+ spin_lock_irqsave(&ec->cmd_q_lock, flags);
+ list_add_tail(&desc->node, &ec->cmd_q);
+ spin_unlock_irqrestore(&ec->cmd_q_lock, flags);
+
+ schedule_work(&ec->worker);
+}
+
+int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen)
+{
+ struct olpc_ec_priv *ec = ec_priv;
+ struct ec_cmd_desc desc;
+
+ /* Ensure a driver and ec hook have been registered */
+ if (WARN_ON(!ec_driver || !ec_driver->ec_cmd))
+ return -ENODEV;
+
+ if (!ec)
+ return -ENOMEM;
+
+ /* Suspending in the middle of a command hoses things really badly */
+ if (WARN_ON(ec->suspended))
+ return -EBUSY;
+
+ might_sleep();
+
+ desc.cmd = cmd;
+ desc.inbuf = inbuf;
+ desc.outbuf = outbuf;
+ desc.inlen = inlen;
+ desc.outlen = outlen;
+ desc.err = 0;
+ init_completion(&desc.finished);
+
+ queue_ec_descriptor(&desc, ec);
+
+ /* Timeouts must be handled in the platform-specific EC hook */
+ wait_for_completion(&desc.finished);
+
+ /* The worker thread dequeues the cmd; no need to do anything here */
+ return desc.err;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_cmd);
+
+#ifdef CONFIG_DEBUG_FS
+
+/*
+ * debugfs support for "generic commands", to allow sending
+ * arbitrary EC commands from userspace.
+ */
+
+#define EC_MAX_CMD_ARGS (5 + 1) /* cmd byte + 5 args */
+#define EC_MAX_CMD_REPLY (8)
+
+static DEFINE_MUTEX(ec_dbgfs_lock);
+static unsigned char ec_dbgfs_resp[EC_MAX_CMD_REPLY];
+static unsigned int ec_dbgfs_resp_bytes;
+
+static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ int i, m;
+ unsigned char ec_cmd[EC_MAX_CMD_ARGS];
+ unsigned int ec_cmd_int[EC_MAX_CMD_ARGS];
+ char cmdbuf[64];
+ int ec_cmd_bytes;
+
+ mutex_lock(&ec_dbgfs_lock);
+
+ size = simple_write_to_buffer(cmdbuf, sizeof(cmdbuf), ppos, buf, size);
+
+ m = sscanf(cmdbuf, "%x:%u %x %x %x %x %x", &ec_cmd_int[0],
+ &ec_dbgfs_resp_bytes, &ec_cmd_int[1], &ec_cmd_int[2],
+ &ec_cmd_int[3], &ec_cmd_int[4], &ec_cmd_int[5]);
+ if (m < 2 || ec_dbgfs_resp_bytes > EC_MAX_CMD_REPLY) {
+ /* reset to prevent overflow on read */
+ ec_dbgfs_resp_bytes = 0;
+
+ pr_debug("olpc-ec: bad ec cmd: cmd:response-count [arg1 [arg2 ...]]\n");
+ size = -EINVAL;
+ goto out;
+ }
+
+ /* convert scanf'd ints to char */
+ ec_cmd_bytes = m - 2;
+ for (i = 0; i <= ec_cmd_bytes; i++)
+ ec_cmd[i] = ec_cmd_int[i];
+
+ pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %02x %02x %02x %02x %02x, want %d returns\n",
+ ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2],
+ ec_cmd[3], ec_cmd[4], ec_cmd[5], ec_dbgfs_resp_bytes);
+
+ olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
+ ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes);
+
+ pr_debug("olpc-ec: response %02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
+ ec_dbgfs_resp[0], ec_dbgfs_resp[1], ec_dbgfs_resp[2],
+ ec_dbgfs_resp[3], ec_dbgfs_resp[4], ec_dbgfs_resp[5],
+ ec_dbgfs_resp[6], ec_dbgfs_resp[7],
+ ec_dbgfs_resp_bytes);
+
+out:
+ mutex_unlock(&ec_dbgfs_lock);
+ return size;
+}
+
+static ssize_t ec_dbgfs_cmd_read(struct file *file, char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ unsigned int i, r;
+ char *rp;
+ char respbuf[64];
+
+ mutex_lock(&ec_dbgfs_lock);
+ rp = respbuf;
+ rp += sprintf(rp, "%02x", ec_dbgfs_resp[0]);
+ for (i = 1; i < ec_dbgfs_resp_bytes; i++)
+ rp += sprintf(rp, ", %02x", ec_dbgfs_resp[i]);
+ mutex_unlock(&ec_dbgfs_lock);
+ rp += sprintf(rp, "\n");
+
+ r = rp - respbuf;
+ return simple_read_from_buffer(buf, size, ppos, respbuf, r);
+}
+
+static const struct file_operations ec_dbgfs_ops = {
+ .write = ec_dbgfs_cmd_write,
+ .read = ec_dbgfs_cmd_read,
+};
+
+static struct dentry *olpc_ec_setup_debugfs(void)
+{
+ struct dentry *dbgfs_dir;
+
+ dbgfs_dir = debugfs_create_dir("olpc-ec", NULL);
+ if (IS_ERR_OR_NULL(dbgfs_dir))
+ return NULL;
+
+ debugfs_create_file("cmd", 0600, dbgfs_dir, NULL, &ec_dbgfs_ops);
+
+ return dbgfs_dir;
+}
+
+#else
+
+static struct dentry *olpc_ec_setup_debugfs(void)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int olpc_ec_probe(struct platform_device *pdev)
+{
+ struct olpc_ec_priv *ec;
+ int err;
+
+ if (!ec_driver)
+ return -ENODEV;
+
+ ec = kzalloc(sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ ec->drv = ec_driver;
+ INIT_WORK(&ec->worker, olpc_ec_worker);
+ mutex_init(&ec->cmd_lock);
+
+ INIT_LIST_HEAD(&ec->cmd_q);
+ spin_lock_init(&ec->cmd_q_lock);
+
+ ec_priv = ec;
+ platform_set_drvdata(pdev, ec);
+
+ err = ec_driver->probe ? ec_driver->probe(pdev) : 0;
+ if (err) {
+ ec_priv = NULL;
+ kfree(ec);
+ } else {
+ ec->dbgfs_dir = olpc_ec_setup_debugfs();
+ }
+
+ return err;
+}
+
+static int olpc_ec_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
+ int err = 0;
+
+ if (ec_driver->suspend)
+ err = ec_driver->suspend(pdev);
+ if (!err)
+ ec->suspended = true;
+
+ return err;
+}
+
+static int olpc_ec_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct olpc_ec_priv *ec = platform_get_drvdata(pdev);
+
+ ec->suspended = false;
+ return ec_driver->resume ? ec_driver->resume(pdev) : 0;
+}
+
+static const struct dev_pm_ops olpc_ec_pm_ops = {
+ .suspend_late = olpc_ec_suspend,
+ .resume_early = olpc_ec_resume,
+};
+
+static struct platform_driver olpc_ec_plat_driver = {
+ .probe = olpc_ec_probe,
+ .driver = {
+ .name = "olpc-ec",
+ .pm = &olpc_ec_pm_ops,
+ },
+};
+
+static int __init olpc_ec_init_module(void)
+{
+ return platform_driver_register(&olpc_ec_plat_driver);
+}
+
+module_init(olpc_ec_init_module);
+
+MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2a262f5c5c0c..c86bae828c28 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -289,6 +289,7 @@ config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI
depends on RFKILL && INPUT
+ depends on SERIO_I8042
select INPUT_SPARSEKMAP
help
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
@@ -758,8 +759,11 @@ config SAMSUNG_Q10
config APPLE_GMUX
tristate "Apple Gmux Driver"
+ depends on ACPI
depends on PNP
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
+ depends on ACPI_VIDEO=n || ACPI_VIDEO
---help---
This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 3782e1cd3697..934d861a3235 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -2196,10 +2196,8 @@ static int __init acer_wmi_init(void)
interface->capability &= ~ACER_CAP_BRIGHTNESS;
pr_info("Brightness must be controlled by acpi video driver\n");
} else {
-#ifdef CONFIG_ACPI_VIDEO
pr_info("Disabling ACPI video driver\n");
acpi_video_unregister();
-#endif
}
if (wmi_has_guid(WMID_GUID3)) {
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 905fa01ac8df..db8f63841b42 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -2,6 +2,7 @@
* Gmux driver for Apple laptops
*
* Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
+ * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
*
* 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
@@ -18,16 +19,30 @@
#include <linux/pnp.h>
#include <linux/apple_bl.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vga_switcheroo.h>
#include <acpi/video.h>
#include <asm/io.h>
struct apple_gmux_data {
unsigned long iostart;
unsigned long iolen;
+ bool indexed;
+ struct mutex index_lock;
struct backlight_device *bdev;
+
+ /* switcheroo data */
+ acpi_handle dhandle;
+ int gpe;
+ enum vga_switcheroo_client_id resume_client_id;
+ enum vga_switcheroo_state power_state;
+ struct completion powerchange_done;
};
+static struct apple_gmux_data *apple_gmux_data;
+
/*
* gmux port offsets. Many of these are not yet used, but may be in the
* future, and it's useful to have them documented here anyhow.
@@ -45,6 +60,9 @@ struct apple_gmux_data {
#define GMUX_PORT_DISCRETE_POWER 0x50
#define GMUX_PORT_MAX_BRIGHTNESS 0x70
#define GMUX_PORT_BRIGHTNESS 0x74
+#define GMUX_PORT_VALUE 0xc2
+#define GMUX_PORT_READ 0xd0
+#define GMUX_PORT_WRITE 0xd4
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
@@ -59,22 +77,174 @@ struct apple_gmux_data {
#define GMUX_BRIGHTNESS_MASK 0x00ffffff
#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
-static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
{
return inb(gmux_data->iostart + port);
}
-static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
+static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
u8 val)
{
outb(val, gmux_data->iostart + port);
}
-static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
{
return inl(gmux_data->iostart + port);
}
+static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, gmux_data->iostart + port + i);
+ }
+}
+
+static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && (gwr & 0x01)) {
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ return !!i;
+}
+
+static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && !(gwr & 0x01)) {
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ if (gwr & 0x01)
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+
+ return !!i;
+}
+
+static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ u8 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ gmux_index_wait_ready(gmux_data);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_complete(gmux_data);
+ val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
+ u8 val)
+{
+ mutex_lock(&gmux_data->index_lock);
+ outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ u32 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ gmux_index_wait_ready(gmux_data);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_complete(gmux_data);
+ val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ mutex_lock(&gmux_data->index_lock);
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
+ }
+
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read8(gmux_data, port);
+ else
+ return gmux_pio_read8(gmux_data, port);
+}
+
+static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write8(gmux_data, port, val);
+ else
+ gmux_pio_write8(gmux_data, port, val);
+}
+
+static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read32(gmux_data, port);
+ else
+ return gmux_pio_read32(gmux_data, port);
+}
+
+static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write32(gmux_data, port, val);
+ else
+ gmux_pio_write32(gmux_data, port, val);
+}
+
+static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
+{
+ u16 val;
+
+ outb(0xaa, gmux_data->iostart + 0xcc);
+ outb(0x55, gmux_data->iostart + 0xcd);
+ outb(0x00, gmux_data->iostart + 0xce);
+
+ val = inb(gmux_data->iostart + 0xcc) |
+ (inb(gmux_data->iostart + 0xcd) << 8);
+
+ if (val == 0x55aa)
+ return true;
+
+ return false;
+}
+
static int gmux_get_brightness(struct backlight_device *bd)
{
struct apple_gmux_data *gmux_data = bl_get_data(bd);
@@ -90,16 +260,7 @@ static int gmux_update_status(struct backlight_device *bd)
if (bd->props.state & BL_CORE_SUSPENDED)
return 0;
- /*
- * Older gmux versions require writing out lower bytes first then
- * setting the upper byte to 0 to flush the values. Newer versions
- * accept a single u32 write, but the old method also works, so we
- * just use the old method for all gmux versions.
- */
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
+ gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
return 0;
}
@@ -110,6 +271,146 @@ static const struct backlight_ops gmux_bl_ops = {
.update_status = gmux_update_status,
};
+static int gmux_switchto(enum vga_switcheroo_client_id id)
+{
+ if (id == VGA_SWITCHEROO_IGD) {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
+ } else {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
+ }
+
+ return 0;
+}
+
+static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
+ enum vga_switcheroo_state state)
+{
+ INIT_COMPLETION(gmux_data->powerchange_done);
+
+ if (state == VGA_SWITCHEROO_ON) {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
+ pr_debug("Discrete card powered up\n");
+ } else {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
+ pr_debug("Discrete card powered down\n");
+ }
+
+ gmux_data->power_state = state;
+
+ if (gmux_data->gpe >= 0 &&
+ !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
+ msecs_to_jiffies(200)))
+ pr_warn("Timeout waiting for gmux switch to complete\n");
+
+ return 0;
+}
+
+static int gmux_set_power_state(enum vga_switcheroo_client_id id,
+ enum vga_switcheroo_state state)
+{
+ if (id == VGA_SWITCHEROO_IGD)
+ return 0;
+
+ return gmux_set_discrete_state(apple_gmux_data, state);
+}
+
+static int gmux_get_client_id(struct pci_dev *pdev)
+{
+ /*
+ * Early Macbook Pros with switchable graphics use nvidia
+ * integrated graphics. Hardcode that the 9400M is integrated.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ return VGA_SWITCHEROO_IGD;
+ else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
+ pdev->device == 0x0863)
+ return VGA_SWITCHEROO_IGD;
+ else
+ return VGA_SWITCHEROO_DIS;
+}
+
+static enum vga_switcheroo_client_id
+gmux_active_client(struct apple_gmux_data *gmux_data)
+{
+ if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
+ return VGA_SWITCHEROO_IGD;
+
+ return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler gmux_handler = {
+ .switchto = gmux_switchto,
+ .power_state = gmux_set_power_state,
+ .get_client_id = gmux_get_client_id,
+};
+
+static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_DISABLE);
+}
+
+static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_ENABLE);
+}
+
+static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
+{
+ return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
+}
+
+static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
+{
+ u8 status;
+
+ /* to clear interrupts write back current status */
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
+}
+
+static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
+{
+ u8 status;
+ struct pnp_dev *pnp = (struct pnp_dev *)context;
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ pr_debug("Notify handler called: status %d\n", status);
+
+ gmux_clear_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data);
+
+ if (status & GMUX_INTERRUPT_STATUS_POWER)
+ complete(&gmux_data->powerchange_done);
+}
+
+static int gmux_suspend(struct pnp_dev *pnp, pm_message_t state)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ gmux_data->resume_client_id = gmux_active_client(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ return 0;
+}
+
+static int gmux_resume(struct pnp_dev *pnp)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ gmux_enable_interrupts(gmux_data);
+ gmux_switchto(gmux_data->resume_client_id);
+ if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
+ gmux_set_discrete_state(gmux_data, gmux_data->power_state);
+ return 0;
+}
+
static int __devinit gmux_probe(struct pnp_dev *pnp,
const struct pnp_device_id *id)
{
@@ -119,6 +420,11 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
struct backlight_device *bdev;
u8 ver_major, ver_minor, ver_release;
int ret = -ENXIO;
+ acpi_status status;
+ unsigned long long gpe;
+
+ if (apple_gmux_data)
+ return -EBUSY;
gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
if (!gmux_data)
@@ -147,21 +453,32 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
}
/*
- * On some machines the gmux is in ACPI even thought the machine
- * doesn't really have a gmux. Check for invalid version information
- * to detect this.
+ * Invalid version information may indicate either that the gmux
+ * device isn't present or that it's a new one that uses indexed
+ * io
*/
+
ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
- pr_info("gmux device not present\n");
- ret = -ENODEV;
- goto err_release;
+ if (gmux_is_indexed(gmux_data)) {
+ u32 version;
+ mutex_init(&gmux_data->index_lock);
+ gmux_data->indexed = true;
+ version = gmux_read32(gmux_data,
+ GMUX_PORT_VERSION_MAJOR);
+ ver_major = (version >> 24) & 0xff;
+ ver_minor = (version >> 16) & 0xff;
+ ver_release = (version >> 8) & 0xff;
+ } else {
+ pr_info("gmux device not present\n");
+ ret = -ENODEV;
+ goto err_release;
+ }
}
-
- pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
- ver_release);
+ pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
+ ver_release, (gmux_data->indexed ? "indexed" : "classic"));
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
@@ -194,13 +511,65 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
* Disable the other backlight choices.
*/
acpi_video_dmi_promote_vendor();
-#ifdef CONFIG_ACPI_VIDEO
acpi_video_unregister();
-#endif
apple_bl_unregister();
+ gmux_data->power_state = VGA_SWITCHEROO_ON;
+
+ gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev);
+ if (!gmux_data->dhandle) {
+ pr_err("Cannot find acpi handle for pnp device %s\n",
+ dev_name(&pnp->dev));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
+ if (ACPI_SUCCESS(status)) {
+ gmux_data->gpe = (int)gpe;
+
+ status = acpi_install_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler, pnp);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Install notify handler failed: %s\n",
+ acpi_format_exception(status));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_enable_gpe(NULL, gmux_data->gpe);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Cannot enable gpe: %s\n",
+ acpi_format_exception(status));
+ goto err_enable_gpe;
+ }
+ } else {
+ pr_warn("No GPE found for gmux\n");
+ gmux_data->gpe = -1;
+ }
+
+ if (vga_switcheroo_register_handler(&gmux_handler)) {
+ ret = -ENODEV;
+ goto err_register_handler;
+ }
+
+ init_completion(&gmux_data->powerchange_done);
+ apple_gmux_data = gmux_data;
+ gmux_enable_interrupts(gmux_data);
+
return 0;
+err_register_handler:
+ if (gmux_data->gpe >= 0)
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+err_enable_gpe:
+ if (gmux_data->gpe >= 0)
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+err_notify:
+ backlight_device_unregister(bdev);
err_release:
release_region(gmux_data->iostart, gmux_data->iolen);
err_free:
@@ -212,14 +581,23 @@ static void __devexit gmux_remove(struct pnp_dev *pnp)
{
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ vga_switcheroo_unregister_handler();
+ gmux_disable_interrupts(gmux_data);
+ if (gmux_data->gpe >= 0) {
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+ }
+
backlight_device_unregister(gmux_data->bdev);
+
release_region(gmux_data->iostart, gmux_data->iolen);
+ apple_gmux_data = NULL;
kfree(gmux_data);
acpi_video_dmi_demote_vendor();
-#ifdef CONFIG_ACPI_VIDEO
acpi_video_register();
-#endif
apple_bl_register();
}
@@ -233,6 +611,8 @@ static struct pnp_driver gmux_pnp_driver = {
.probe = gmux_probe,
.remove = __devexit_p(gmux_remove),
.id_table = gmux_device_ids,
+ .suspend = gmux_suspend,
+ .resume = gmux_resume
};
static int __init apple_gmux_init(void)
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index e38f91be0b10..4b568df56643 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -85,7 +85,7 @@ static char *wled_type = "unknown";
static char *bled_type = "unknown";
module_param(wled_type, charp, 0444);
-MODULE_PARM_DESC(wlan_status, "Set the wled type on boot "
+MODULE_PARM_DESC(wled_type, "Set the wled type on boot "
"(unknown, led or rfkill). "
"default is unknown");
@@ -863,9 +863,9 @@ static ssize_t show_infos(struct device *dev,
* The significance of others is yet to be found.
* If we don't find the method, we assume the device are present.
*/
- rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);
+ rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
if (!ACPI_FAILURE(rv))
- len += sprintf(page + len, "HRWS value : %#x\n",
+ len += sprintf(page + len, "HWRS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
@@ -1751,9 +1751,9 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
* The significance of others is yet to be found.
*/
status =
- acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result);
+ acpi_evaluate_integer(asus->handle, "HWRS", NULL, &hwrs_result);
if (!ACPI_FAILURE(status))
- pr_notice(" HRWS returned %x", (int)hwrs_result);
+ pr_notice(" HWRS returned %x", (int)hwrs_result);
if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
asus->have_rsts = true;
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index c7a36f6b0580..c0e9ff489b24 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -47,9 +47,7 @@
#include <linux/thermal.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
-#ifdef CONFIG_ACPI_VIDEO
#include <acpi/video.h>
-#endif
#include "asus-wmi.h"
@@ -101,6 +99,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
#define ASUS_WMI_DEVID_CWAP 0x00010003
#define ASUS_WMI_DEVID_WLAN 0x00010011
+#define ASUS_WMI_DEVID_WLAN_LED 0x00010012
#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
#define ASUS_WMI_DEVID_GPS 0x00010015
#define ASUS_WMI_DEVID_WIMAX 0x00010017
@@ -731,8 +730,21 @@ static int asus_rfkill_set(void *data, bool blocked)
{
struct asus_rfkill *priv = data;
u32 ctrl_param = !blocked;
+ u32 dev_id = priv->dev_id;
- return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL);
+ /*
+ * If the user bit is set, BIOS can't set and record the wlan status,
+ * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
+ * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
+ * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
+ * while setting the wlan status through WMI.
+ * This is also the behavior that windows app will do.
+ */
+ if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
+ priv->asus->driver->wlan_ctrl_by_user)
+ dev_id = ASUS_WMI_DEVID_WLAN_LED;
+
+ return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
}
static void asus_rfkill_query(struct rfkill *rfkill, void *data)
@@ -1653,6 +1665,7 @@ static int asus_wmi_add(struct platform_device *pdev)
struct asus_wmi *asus;
acpi_status status;
int err;
+ u32 result;
asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
if (!asus)
@@ -1689,10 +1702,8 @@ static int asus_wmi_add(struct platform_device *pdev)
if (asus->driver->quirks->wmi_backlight_power)
acpi_video_dmi_promote_vendor();
if (!acpi_video_backlight_support()) {
-#ifdef CONFIG_ACPI_VIDEO
pr_info("Disabling ACPI video driver\n");
acpi_video_unregister();
-#endif
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
goto fail_backlight;
@@ -1711,6 +1722,10 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_debugfs;
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
+ if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
+ asus->driver->wlan_ctrl_by_user = 1;
+
return 0;
fail_debugfs:
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 9c1da8b81bea..4c9bd38bb0a2 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -46,6 +46,7 @@ struct quirk_entry {
struct asus_wmi_driver {
int brightness;
int panel_power;
+ int wlan_ctrl_by_user;
const char *name;
struct module *owner;
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 2ca7dd1ab3e4..c87ff16873f9 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -350,6 +350,7 @@ static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
inputdev->close = cmpc_accel_close_v4;
}
+#ifdef CONFIG_PM_SLEEP
static int cmpc_accel_suspend_v4(struct device *dev)
{
struct input_dev *inputdev;
@@ -384,6 +385,7 @@ static int cmpc_accel_resume_v4(struct device *dev)
return 0;
}
+#endif
static int cmpc_accel_add_v4(struct acpi_device *acpi)
{
@@ -723,8 +725,10 @@ static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
if (event == 0x81) {
- if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
}
}
@@ -737,8 +741,10 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev)
set_bit(SW_TABLET_MODE, inputdev->swbit);
acpi = to_acpi_device(inputdev->dev.parent);
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
}
static int cmpc_tablet_add(struct acpi_device *acpi)
@@ -752,15 +758,19 @@ static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
return cmpc_remove_acpi_notify_device(acpi);
}
+#ifdef CONFIG_PM_SLEEP
static int cmpc_tablet_resume(struct device *dev)
{
struct input_dev *inputdev = dev_get_drvdata(dev);
unsigned long long val = 0;
- if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 4e96e8c0b60f..927c33af67ec 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -211,7 +211,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5420",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -220,7 +220,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -229,7 +229,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 5720",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -238,7 +238,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7420",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -247,7 +247,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -256,7 +256,7 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
.ident = "Dell Inspiron 7720",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
},
.driver_data = &quirk_dell_vostro_v130,
},
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index dab91b48d22c..5ca264179f4e 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -610,12 +610,12 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
if (!bus) {
pr_warn("Unable to find PCI bus 1?\n");
- goto out_unlock;
+ goto out_put_dev;
}
if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
pr_err("Unable to read PCI config space?\n");
- goto out_unlock;
+ goto out_put_dev;
}
absent = (l == 0xffffffff);
@@ -627,7 +627,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
absent ? "absent" : "present");
pr_warn("skipped wireless hotplug as probably "
"inappropriate for this model\n");
- goto out_unlock;
+ goto out_put_dev;
}
if (!blocked) {
@@ -635,7 +635,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
if (dev) {
/* Device already present */
pci_dev_put(dev);
- goto out_unlock;
+ goto out_put_dev;
}
dev = pci_scan_single_device(bus, 0);
if (dev) {
@@ -650,6 +650,8 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
pci_dev_put(dev);
}
}
+out_put_dev:
+ pci_dev_put(port);
}
out_unlock:
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
index d2e41735a47b..7acae3f85f3b 100644
--- a/drivers/platform/x86/fujitsu-tablet.c
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -440,11 +440,13 @@ static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_fujitsu_resume(struct device *dev)
{
fujitsu_reset();
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index d9ab6f64dcec..777c7e3dda51 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -305,10 +305,12 @@ static int hdaps_probe(struct platform_device *dev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int hdaps_resume(struct device *dev)
{
return hdaps_device_init();
}
+#endif
static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index f4d91154ad67..6b9af989632b 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -352,7 +352,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type)
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_suspend(struct device *dev)
{
/* make sure the device is off when we suspend */
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 17f6dfd8dbfb..dae7abe1d711 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -36,6 +36,7 @@
#include <linux/fb.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/i8042.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -63,8 +64,11 @@ enum {
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
- VPCCMD_R_RF = 0x23,
+ VPCCMD_W_FAN,
+ VPCCMD_R_RF,
VPCCMD_W_RF,
+ VPCCMD_R_FAN = 0x2B,
+ VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33,
};
@@ -356,14 +360,46 @@ static ssize_t store_ideapad_cam(struct device *dev,
return -EINVAL;
ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
if (ret < 0)
- return ret;
+ return -EIO;
return count;
}
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+static ssize_t show_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long result;
+
+ if (read_ec_data(ideapad_handle, VPCCMD_R_FAN, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%lu\n", result);
+}
+
+static ssize_t store_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, state;
+
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", &state) != 1)
+ return -EINVAL;
+ if (state < 0 || state > 4 || state == 3)
+ return -EINVAL;
+ ret = write_ec_cmd(ideapad_handle, VPCCMD_W_FAN, state);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
+
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -377,7 +413,10 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
if (attr == &dev_attr_camera_power.attr)
supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
- else
+ else if (attr == &dev_attr_fan_mode.attr) {
+ unsigned long value;
+ supported = !read_ec_data(ideapad_handle, VPCCMD_R_FAN, &value);
+ } else
supported = true;
return supported ? attr->mode : 0;
@@ -518,9 +557,15 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
*/
static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 7, { KEY_CAMERA } },
+ { KE_KEY, 11, { KEY_F16 } },
{ KE_KEY, 13, { KEY_WLAN } },
{ KE_KEY, 16, { KEY_PROG1 } },
{ KE_KEY, 17, { KEY_PROG2 } },
+ { KE_KEY, 64, { KEY_PROG3 } },
+ { KE_KEY, 65, { KEY_PROG4 } },
+ { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
+ { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
{ KE_END, 0 },
};
@@ -587,6 +632,28 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
ideapad_input_report(priv, 16);
}
+static void ideapad_check_special_buttons(struct ideapad_private *priv)
+{
+ unsigned long bit, value;
+
+ read_ec_data(ideapad_handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
+
+ for (bit = 0; bit < 16; bit++) {
+ if (test_bit(bit, &value)) {
+ switch (bit) {
+ case 6:
+ /* Thermal Management button */
+ ideapad_input_report(priv, 65);
+ break;
+ case 1:
+ /* OneKey Theater button */
+ ideapad_input_report(priv, 64);
+ break;
+ }
+ }
+ }
+}
+
/*
* backlight
*/
@@ -691,6 +758,24 @@ static const struct acpi_device_id ideapad_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
+static void ideapad_sync_touchpad_state(struct acpi_device *adevice)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ unsigned long value;
+
+ /* Without reading from EC touchpad LED doesn't switch state */
+ if (!read_ec_data(adevice->handle, VPCCMD_R_TOUCHPAD, &value)) {
+ /* Some IdeaPads don't really turn off touchpad - they only
+ * switch the LED state. We (de)activate KBC AUX port to turn
+ * touchpad off and on. We send KEY_TOUCHPAD_OFF and
+ * KEY_TOUCHPAD_ON to not to get out of sync with LED */
+ unsigned char param;
+ i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
+ I8042_CMD_AUX_DISABLE);
+ ideapad_input_report(priv, value ? 67 : 66);
+ }
+}
+
static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
{
int ret, i;
@@ -727,6 +812,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
priv->rfk[i] = NULL;
}
ideapad_sync_rfk_state(priv);
+ ideapad_sync_touchpad_state(adevice);
if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv);
@@ -785,9 +871,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
ideapad_sync_rfk_state(priv);
break;
case 13:
+ case 11:
+ case 7:
case 6:
ideapad_input_report(priv, vpc_bit);
break;
+ case 5:
+ ideapad_sync_touchpad_state(adevice);
+ break;
case 4:
ideapad_backlight_notify_brightness(priv);
break;
@@ -797,6 +888,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
case 2:
ideapad_backlight_notify_power(priv);
break;
+ case 0:
+ ideapad_check_special_buttons(priv);
+ break;
default:
pr_info("Unknown event: %lu\n", vpc_bit);
}
@@ -804,6 +898,15 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
}
}
+static int ideapad_acpi_resume(struct device *device)
+{
+ ideapad_sync_rfk_state(ideapad_priv);
+ ideapad_sync_touchpad_state(to_acpi_device(device));
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
+
static struct acpi_driver ideapad_acpi_driver = {
.name = "ideapad_acpi",
.class = "IdeaPad",
@@ -811,6 +914,7 @@ static struct acpi_driver ideapad_acpi_driver = {
.ops.add = ideapad_acpi_add,
.ops.remove = ideapad_acpi_remove,
.ops.notify = ideapad_acpi_notify,
+ .drv.pm = &ideapad_pm,
.owner = THIS_MODULE,
};
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index f64441844317..2111dbb7e1e3 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -85,7 +85,9 @@
#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
+#ifdef CONFIG_PM_SLEEP
static int msi_laptop_resume(struct device *device);
+#endif
static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
@@ -753,6 +755,7 @@ err_bluetooth:
return retval;
}
+#ifdef CONFIG_PM_SLEEP
static int msi_laptop_resume(struct device *device)
{
u8 data;
@@ -773,6 +776,7 @@ static int msi_laptop_resume(struct device *device)
return 0;
}
+#endif
static int __init msi_laptop_input_setup(void)
{
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 24480074bcf0..8e8caa767d6a 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -188,7 +188,9 @@ static const struct acpi_device_id pcc_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_pcc_hotkey_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
static struct acpi_driver acpi_pcc_driver = {
@@ -540,6 +542,7 @@ static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
/* kernel module interface */
+#ifdef CONFIG_PM_SLEEP
static int acpi_pcc_hotkey_resume(struct device *dev)
{
struct pcc_acpi *pcc;
@@ -556,6 +559,7 @@ static int acpi_pcc_hotkey_resume(struct device *dev)
return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
}
+#endif
static int acpi_pcc_hotkey_add(struct acpi_device *device)
{
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index c1ca7bcebb66..dd90d15f5210 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -26,9 +26,7 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
-#ifdef CONFIG_ACPI_VIDEO
#include <acpi/video.h>
-#endif
/*
* This driver is needed because a number of Samsung laptops do not hook
@@ -1558,9 +1556,7 @@ static int __init samsung_init(void)
samsung->handle_backlight = false;
} else if (samsung->quirks->broken_acpi_video) {
pr_info("Disabling ACPI video driver\n");
-#ifdef CONFIG_ACPI_VIDEO
acpi_video_unregister();
-#endif
}
#endif
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 9363969ad07a..daaddec68def 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -140,7 +140,10 @@ MODULE_PARM_DESC(kbd_backlight_timeout,
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
"(default: 0)");
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_kbd_backlight_resume(void);
+static void sony_nc_thermal_resume(void);
+#endif
static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
@@ -151,7 +154,6 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd);
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
-static void sony_nc_thermal_resume(void);
static int sony_nc_lid_resume_setup(struct platform_device *pd);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
@@ -1431,6 +1433,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_nc_handles_cleanup(pd);
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_function_resume(void)
{
unsigned int i, result, bitmask, arg;
@@ -1508,6 +1511,7 @@ static int sony_nc_resume(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume);
@@ -1872,6 +1876,7 @@ static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
}
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_kbd_backlight_resume(void)
{
int ignore = 0;
@@ -1888,6 +1893,7 @@ static void sony_nc_kbd_backlight_resume(void)
(kbdbl_ctl->base + 0x200) |
(kbdbl_ctl->timeout << 0x10), &ignore);
}
+#endif
struct battery_care_control {
struct device_attribute attrs[2];
@@ -2210,6 +2216,7 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
}
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_thermal_resume(void)
{
unsigned int status = sony_nc_thermal_mode_get();
@@ -2217,6 +2224,7 @@ static void sony_nc_thermal_resume(void)
if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode);
}
+#endif
/* resume on LID open */
struct snc_lid_resume_control {
@@ -4287,6 +4295,7 @@ err_free_resources:
return result;
}
+#ifdef CONFIG_PM_SLEEP
static int sony_pic_suspend(struct device *dev)
{
if (sony_pic_disable(to_acpi_device(dev)))
@@ -4300,6 +4309,7 @@ static int sony_pic_resume(struct device *dev)
spic_dev.cur_ioport, spic_dev.cur_irq);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e7f73287636c..52daaa816e53 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -545,7 +545,7 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
*/
static int acpi_evalf(acpi_handle handle,
- void *res, char *method, char *fmt, ...)
+ int *res, char *method, char *fmt, ...)
{
char *fmt0 = fmt;
struct acpi_object_list params;
@@ -606,7 +606,7 @@ static int acpi_evalf(acpi_handle handle,
success = (status == AE_OK &&
out_obj.type == ACPI_TYPE_INTEGER);
if (success && res)
- *(int *)res = out_obj.integer.value;
+ *res = out_obj.integer.value;
break;
case 'v': /* void */
success = status == AE_OK;
@@ -922,6 +922,7 @@ static struct input_dev *tpacpi_inputdev;
static struct mutex tpacpi_inputdev_send_mutex;
static LIST_HEAD(tpacpi_all_drivers);
+#ifdef CONFIG_PM_SLEEP
static int tpacpi_suspend_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -949,6 +950,7 @@ static int tpacpi_resume_handler(struct device *dev)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(tpacpi_pm,
tpacpi_suspend_handler, tpacpi_resume_handler);
@@ -7384,17 +7386,18 @@ static int fan_get_status(u8 *status)
* Add TPACPI_FAN_RD_ACPI_FANS ? */
switch (fan_status_access_mode) {
- case TPACPI_FAN_RD_ACPI_GFAN:
+ case TPACPI_FAN_RD_ACPI_GFAN: {
/* 570, 600e/x, 770e, 770x */
+ int res;
- if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+ if (unlikely(!acpi_evalf(gfan_handle, &res, NULL, "d")))
return -EIO;
if (likely(status))
- *status = s & 0x07;
+ *status = res & 0x07;
break;
-
+ }
case TPACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */
if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
@@ -8662,6 +8665,13 @@ static int __must_check __init get_thinkpad_model_data(
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
+ } else {
+ s = dmi_get_system_info(DMI_BIOS_VENDOR);
+ if (s && !(strnicmp(s, "Lenovo", 6))) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
+ }
}
s = dmi_get_system_info(DMI_PRODUCT_NAME);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index c13ba5bac93f..5f1256d5e933 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1296,6 +1296,7 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
}
}
+#ifdef CONFIG_PM_SLEEP
static int toshiba_acpi_suspend(struct device *device)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
@@ -1317,6 +1318,7 @@ static int toshiba_acpi_resume(struct device *device)
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
toshiba_acpi_suspend, toshiba_acpi_resume);
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
index 715a43cb5e3c..5e5d6317d690 100644
--- a/drivers/platform/x86/toshiba_bluetooth.c
+++ b/drivers/platform/x86/toshiba_bluetooth.c
@@ -41,7 +41,9 @@ static const struct acpi_device_id bt_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, bt_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int toshiba_bt_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
static struct acpi_driver toshiba_bt_rfkill_driver = {
@@ -90,10 +92,12 @@ static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
toshiba_bluetooth_enable(device->handle);
}
+#ifdef CONFIG_PM_SLEEP
static int toshiba_bt_resume(struct device *dev)
{
return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
}
+#endif
static int toshiba_bt_rfkill_add(struct acpi_device *device)
{
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
index b57ad8641480..1da13ed34b04 100644
--- a/drivers/platform/x86/xo1-rfkill.c
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -12,8 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
-
-#include <asm/olpc.h>
+#include <linux/olpc-ec.h>
static bool card_blocked;
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c
index 849c07c13bf6..38ba39d7ca7d 100644
--- a/drivers/platform/x86/xo15-ebook.c
+++ b/drivers/platform/x86/xo15-ebook.c
@@ -77,10 +77,12 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event)
}
}
+#ifdef CONFIG_PM_SLEEP
static int ebook_switch_resume(struct device *dev)
{
return ebook_send_state(to_acpi_device(dev));
}
+#endif
static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume);
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
index 55b10b813353..a89a41acf9c5 100644
--- a/drivers/power/olpc_battery.c
+++ b/drivers/power/olpc_battery.c
@@ -17,6 +17,7 @@
#include <linux/power_supply.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
+#include <linux/olpc-ec.h>
#include <asm/olpc.h>
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
index 6a1ca735e935..7312f2651647 100644
--- a/drivers/power/pda_power.c
+++ b/drivers/power/pda_power.c
@@ -24,11 +24,7 @@
static inline unsigned int get_irq_flags(struct resource *res)
{
- unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED;
-
- flags |= res->flags & IRQF_TRIGGER_MASK;
-
- return flags;
+ return IRQF_SHARED | (res->flags & IRQF_TRIGGER_MASK);
}
static struct device *dev;
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 8fc3808d7a3e..90c5c7357a50 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -1,12 +1,31 @@
menuconfig PWM
- bool "PWM Support"
+ bool "Pulse-Width Modulation (PWM) Support"
depends on !MACH_JZ4740 && !PUV3_PWM
help
- This enables PWM support through the generic PWM framework.
- You only need to enable this, if you also want to enable
- one or more of the PWM drivers below.
-
- If unsure, say N.
+ Generic Pulse-Width Modulation (PWM) support.
+
+ In Pulse-Width Modulation, a variation of the width of pulses
+ in a rectangular pulse signal is used as a means to alter the
+ average power of the signal. Applications include efficient
+ power delivery and voltage regulation. In computer systems,
+ PWMs are commonly used to control fans or the brightness of
+ display backlights.
+
+ This framework provides a generic interface to PWM devices
+ within the Linux kernel. On the driver side it provides an API
+ to register and unregister a PWM chip, an abstraction of a PWM
+ controller, that supports one or more PWM devices. Client
+ drivers can request PWM devices and use the generic framework
+ to configure as well as enable and disable them.
+
+ This generic framework replaces the legacy PWM framework which
+ allows only a single driver implementing the required API. Not
+ all legacy implementations have been ported to the framework
+ yet. The framework provides an API that is backward compatible
+ with the legacy framework so that existing client drivers
+ continue to work as expected.
+
+ If unsure, say no.
if PWM
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index ecb76909e946..c6e05078d3ad 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -129,8 +129,8 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
return 0;
}
-static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
- const struct of_phandle_args *args)
+static struct pwm_device *
+of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
@@ -149,7 +149,7 @@ static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
return pwm;
}
-void of_pwmchip_add(struct pwm_chip *chip)
+static void of_pwmchip_add(struct pwm_chip *chip)
{
if (!chip->dev || !chip->dev->of_node)
return;
@@ -162,7 +162,7 @@ void of_pwmchip_add(struct pwm_chip *chip)
of_node_get(chip->dev->of_node);
}
-void of_pwmchip_remove(struct pwm_chip *chip)
+static void of_pwmchip_remove(struct pwm_chip *chip)
{
if (chip->dev && chip->dev->of_node)
of_node_put(chip->dev->of_node);
@@ -527,7 +527,7 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
struct pwm_device *pwm_get(struct device *dev, const char *con_id)
{
struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
- const char *dev_id = dev ? dev_name(dev): NULL;
+ const char *dev_id = dev ? dev_name(dev) : NULL;
struct pwm_chip *chip = NULL;
unsigned int index = 0;
unsigned int best = 0;
@@ -609,7 +609,7 @@ void pwm_put(struct pwm_device *pwm)
mutex_lock(&pwm_lock);
if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
- pr_warning("PWM device already freed\n");
+ pr_warn("PWM device already freed\n");
goto out;
}
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index d10386528c9c..e5187c0ade9f 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -225,6 +225,7 @@ static int s3c_pwm_probe(struct platform_device *pdev)
/* calculate base of control bits in TCON */
s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+ s3c->chip.dev = &pdev->dev;
s3c->chip.ops = &s3c_pwm_ops;
s3c->chip.base = -1;
s3c->chip.npwm = 1;
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 02ce18d5e49a..057465e0553c 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -187,10 +187,8 @@ static int tegra_pwm_probe(struct platform_device *pdev)
}
pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pwm->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() region\n");
+ if (!pwm->mmio_base)
return -EADDRNOTAVAIL;
- }
platform_set_drvdata(pdev, pwm);
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
index 3c2ad284ee3e..4b6688909fee 100644
--- a/drivers/pwm/pwm-tiecap.c
+++ b/drivers/pwm/pwm-tiecap.c
@@ -100,6 +100,13 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
writel(period_cycles, pc->mmio_base + CAP3);
}
+ if (!test_bit(PWMF_ENABLED, &pwm->flags)) {
+ reg_val = readw(pc->mmio_base + ECCTL2);
+ /* Disable APWM mode to put APWM output Low */
+ reg_val &= ~ECCTL2_APWM_MODE;
+ writew(reg_val, pc->mmio_base + ECCTL2);
+ }
+
pm_runtime_put_sync(pc->chip.dev);
return 0;
}
@@ -192,10 +199,8 @@ static int __devinit ecap_pwm_probe(struct platform_device *pdev)
}
pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pc->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ if (!pc->mmio_base)
return -EADDRNOTAVAIL;
- }
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 010d232cb0c8..b1996bcd5b78 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -104,6 +104,7 @@ struct ehrpwm_pwm_chip {
struct pwm_chip chip;
unsigned int clk_rate;
void __iomem *mmio_base;
+ unsigned long period_cycles[NUM_PWM_CHANNEL];
};
static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip)
@@ -210,6 +211,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
unsigned long long c;
unsigned long period_cycles, duty_cycles;
unsigned short ps_divval, tb_divval;
+ int i;
if (period_ns < 0 || duty_ns < 0 || period_ns > NSEC_PER_SEC)
return -ERANGE;
@@ -229,6 +231,28 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
duty_cycles = (unsigned long)c;
}
+ /*
+ * Period values should be same for multiple PWM channels as IP uses
+ * same period register for multiple channels.
+ */
+ for (i = 0; i < NUM_PWM_CHANNEL; i++) {
+ if (pc->period_cycles[i] &&
+ (pc->period_cycles[i] != period_cycles)) {
+ /*
+ * Allow channel to reconfigure period if no other
+ * channels being configured.
+ */
+ if (i == pwm->hwpwm)
+ continue;
+
+ dev_err(chip->dev, "Period value conflicts with channel %d\n",
+ i);
+ return -EINVAL;
+ }
+ }
+
+ pc->period_cycles[pwm->hwpwm] = period_cycles;
+
/* Configure clock prescaler to support Low frequency PWM wave */
if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval,
&tb_divval)) {
@@ -320,10 +344,15 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
+ struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip);
+
if (test_bit(PWMF_ENABLED, &pwm->flags)) {
dev_warn(chip->dev, "Removing PWM device without disabling\n");
pm_runtime_put_sync(chip->dev);
}
+
+ /* set period value to zero on free */
+ pc->period_cycles[pwm->hwpwm] = 0;
}
static const struct pwm_ops ehrpwm_pwm_ops = {
@@ -371,10 +400,8 @@ static int __devinit ehrpwm_pwm_probe(struct platform_device *pdev)
}
pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pc->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ if (!pc->mmio_base)
return -EADDRNOTAVAIL;
- }
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
index 548021439f0c..ad14389b7144 100644
--- a/drivers/pwm/pwm-vt8500.c
+++ b/drivers/pwm/pwm-vt8500.c
@@ -41,7 +41,7 @@ static inline void pwm_busy_wait(void __iomem *reg, u8 bitmask)
cpu_relax();
if (unlikely(!loops))
- pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+ pr_warn("Waiting for status bits 0x%x to clear timed out\n",
bitmask);
}
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 722246cf20ab..5d44252b7342 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -435,6 +435,9 @@ static void tsi721_db_dpc(struct work_struct *work)
" info %4.4x\n", DBELL_SID(idb.bytes),
DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
}
+
+ wr_ptr = ioread32(priv->regs +
+ TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
}
iowrite32(rd_ptr & (IDB_QSIZE - 1),
@@ -445,6 +448,10 @@ static void tsi721_db_dpc(struct work_struct *work)
regval |= TSI721_SR_CHINT_IDBQRCV;
iowrite32(regval,
priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+
+ wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
+ if (wr_ptr != rd_ptr)
+ schedule_work(&priv->idb_work);
}
/**
@@ -2212,7 +2219,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct tsi721_device *priv;
- int i, cap;
+ int cap;
int err;
u32 regval;
@@ -2232,12 +2239,15 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
priv->pdev = pdev;
#ifdef DEBUG
+ {
+ int i;
for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
dev_dbg(&pdev->dev, "res[%d] @ 0x%llx (0x%lx, 0x%lx)\n",
i, (unsigned long long)pci_resource_start(pdev, i),
(unsigned long)pci_resource_len(pdev, i),
pci_resource_flags(pdev, i));
}
+ }
#endif
/*
* Verify BAR configuration
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index 182b553059c9..c151fd5d8c97 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -486,6 +486,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = {
.id = AB3100_BUCK,
.ops = &regulator_ops_variable_sleepable,
.n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ .volt_table = ldo_e_buck_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 1000,
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index e9c2085f9dfb..ce0fe72a428e 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -64,14 +64,15 @@ static int anatop_set_voltage_sel(struct regulator_dev *reg, unsigned selector)
static int anatop_get_voltage_sel(struct regulator_dev *reg)
{
struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
- u32 val;
+ u32 val, mask;
if (!anatop_reg->control_reg)
return -ENOTSUPP;
val = anatop_read_reg(anatop_reg->mfd, anatop_reg->control_reg);
- val = (val & ((1 << anatop_reg->vol_bit_width) - 1)) >>
+ mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
anatop_reg->vol_bit_shift;
+ val = (val & mask) >> anatop_reg->vol_bit_shift;
return val - anatop_reg->min_bit_val;
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f092588a078c..48385318175a 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -3217,7 +3217,7 @@ regulator_register(const struct regulator_desc *regulator_desc,
dev_set_drvdata(&rdev->dev, rdev);
- if (config->ena_gpio) {
+ if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
ret = gpio_request_one(config->ena_gpio,
GPIOF_DIR_OUT | config->ena_gpio_flags,
rdev_get_name(rdev));
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
index 34b67bee9323..8b5944f2d7d1 100644
--- a/drivers/regulator/gpio-regulator.c
+++ b/drivers/regulator/gpio-regulator.c
@@ -57,16 +57,17 @@ static int gpio_regulator_get_value(struct regulator_dev *dev)
return -EINVAL;
}
-static int gpio_regulator_set_value(struct regulator_dev *dev,
- int min, int max, unsigned *selector)
+static int gpio_regulator_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
int ptr, target = 0, state, best_val = INT_MAX;
for (ptr = 0; ptr < data->nr_states; ptr++)
if (data->states[ptr].value < best_val &&
- data->states[ptr].value >= min &&
- data->states[ptr].value <= max) {
+ data->states[ptr].value >= min_uV &&
+ data->states[ptr].value <= max_uV) {
target = data->states[ptr].gpios;
best_val = data->states[ptr].value;
if (selector)
@@ -85,13 +86,6 @@ static int gpio_regulator_set_value(struct regulator_dev *dev,
return 0;
}
-static int gpio_regulator_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV,
- unsigned *selector)
-{
- return gpio_regulator_set_value(dev, min_uV, max_uV, selector);
-}
-
static int gpio_regulator_list_voltage(struct regulator_dev *dev,
unsigned selector)
{
@@ -106,7 +100,27 @@ static int gpio_regulator_list_voltage(struct regulator_dev *dev,
static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
int min_uA, int max_uA)
{
- return gpio_regulator_set_value(dev, min_uA, max_uA, NULL);
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+ int ptr, target = 0, state, best_val = 0;
+
+ for (ptr = 0; ptr < data->nr_states; ptr++)
+ if (data->states[ptr].value > best_val &&
+ data->states[ptr].value >= min_uA &&
+ data->states[ptr].value <= max_uA) {
+ target = data->states[ptr].gpios;
+ best_val = data->states[ptr].value;
+ }
+
+ if (best_val == 0)
+ return -EINVAL;
+
+ for (ptr = 0; ptr < data->nr_gpios; ptr++) {
+ state = (target & (1 << ptr)) >> ptr;
+ gpio_set_value(data->gpios[ptr].gpio, state);
+ }
+ data->state = target;
+
+ return 0;
}
static struct regulator_ops gpio_regulator_voltage_ops = {
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 17d19fbbc490..46c7e88f8381 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -486,9 +486,12 @@ static int palmas_map_voltage_ldo(struct regulator_dev *rdev,
{
int ret, voltage;
- ret = ((min_uV - 900000) / 50000) + 1;
- if (ret < 0)
- return ret;
+ if (min_uV == 0)
+ return 0;
+
+ if (min_uV < 900000)
+ min_uV = 900000;
+ ret = DIV_ROUND_UP(min_uV - 900000, 50000) + 1;
/* Map back into a voltage to verify we're still in bounds */
voltage = palmas_list_voltage_ldo(rdev, ret);
@@ -586,7 +589,7 @@ static int palmas_ldo_init(struct palmas *palmas, int id,
addr = palmas_regs_info[id].ctrl_addr;
- ret = palmas_smps_read(palmas, addr, &reg);
+ ret = palmas_ldo_read(palmas, addr, &reg);
if (ret)
return ret;
@@ -596,7 +599,7 @@ static int palmas_ldo_init(struct palmas *palmas, int id,
if (reg_init->mode_sleep)
reg |= PALMAS_LDO1_CTRL_MODE_SLEEP;
- ret = palmas_smps_write(palmas, addr, reg);
+ ret = palmas_ldo_write(palmas, addr, reg);
if (ret)
return ret;
@@ -630,7 +633,7 @@ static __devinit int palmas_probe(struct platform_device *pdev)
ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, &reg);
if (ret)
- goto err_unregister_regulator;
+ return ret;
if (reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN)
pmic->smps123 = 1;
@@ -676,7 +679,9 @@ static __devinit int palmas_probe(struct platform_device *pdev)
case PALMAS_REG_SMPS10:
pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES;
pmic->desc[id].ops = &palmas_ops_smps10;
- pmic->desc[id].vsel_reg = PALMAS_SMPS10_CTRL;
+ pmic->desc[id].vsel_reg =
+ PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
pmic->desc[id].vsel_mask = SMPS10_VSEL;
pmic->desc[id].enable_reg =
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
@@ -778,8 +783,10 @@ static __devinit int palmas_probe(struct platform_device *pdev)
reg_init = pdata->reg_init[id];
if (reg_init) {
ret = palmas_ldo_init(palmas, id, reg_init);
- if (ret)
+ if (ret) {
+ regulator_unregister(pmic->rdev[id]);
goto err_unregister_regulator;
+ }
}
}
}
diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c
index 6caa222af77a..ab00cab905b7 100644
--- a/drivers/regulator/tps65217-regulator.c
+++ b/drivers/regulator/tps65217-regulator.c
@@ -22,6 +22,7 @@
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/mfd/tps65217.h>
@@ -281,37 +282,130 @@ static const struct regulator_desc regulators[] = {
NULL),
};
+#ifdef CONFIG_OF
+static struct of_regulator_match reg_matches[] = {
+ { .name = "dcdc1", .driver_data = (void *)TPS65217_DCDC_1 },
+ { .name = "dcdc2", .driver_data = (void *)TPS65217_DCDC_2 },
+ { .name = "dcdc3", .driver_data = (void *)TPS65217_DCDC_3 },
+ { .name = "ldo1", .driver_data = (void *)TPS65217_LDO_1 },
+ { .name = "ldo2", .driver_data = (void *)TPS65217_LDO_2 },
+ { .name = "ldo3", .driver_data = (void *)TPS65217_LDO_3 },
+ { .name = "ldo4", .driver_data = (void *)TPS65217_LDO_4 },
+};
+
+static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
+{
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct device_node *node = tps->dev->of_node;
+ struct tps65217_board *pdata;
+ struct device_node *regs;
+ int i, count;
+
+ regs = of_find_node_by_name(node, "regulators");
+ if (!regs)
+ return NULL;
+
+ count = of_regulator_match(pdev->dev.parent, regs,
+ reg_matches, TPS65217_NUM_REGULATOR);
+ of_node_put(regs);
+ if ((count < 0) || (count > TPS65217_NUM_REGULATOR))
+ return NULL;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ if (!reg_matches[i].init_data || !reg_matches[i].of_node)
+ continue;
+
+ pdata->tps65217_init_data[i] = reg_matches[i].init_data;
+ pdata->of_node[i] = reg_matches[i].of_node;
+ }
+
+ return pdata;
+}
+#else
+static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev)
+{
+ return NULL;
+}
+#endif
+
static int __devinit tps65217_regulator_probe(struct platform_device *pdev)
{
+ struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+ struct tps65217_board *pdata = dev_get_platdata(tps->dev);
+ struct regulator_init_data *reg_data;
struct regulator_dev *rdev;
- struct tps65217 *tps;
- struct tps_info *info = &tps65217_pmic_regs[pdev->id];
struct regulator_config config = { };
+ int i, ret;
- /* Already set by core driver */
- tps = dev_to_tps65217(pdev->dev.parent);
- tps->info[pdev->id] = info;
+ if (tps->dev->of_node)
+ pdata = tps65217_parse_dt(pdev);
- config.dev = &pdev->dev;
- config.of_node = pdev->dev.of_node;
- config.init_data = pdev->dev.platform_data;
- config.driver_data = tps;
+ if (!pdata) {
+ dev_err(&pdev->dev, "Platform data not found\n");
+ return -EINVAL;
+ }
- rdev = regulator_register(&regulators[pdev->id], &config);
- if (IS_ERR(rdev))
- return PTR_ERR(rdev);
+ if (tps65217_chip_id(tps) != TPS65217) {
+ dev_err(&pdev->dev, "Invalid tps chip version\n");
+ return -ENODEV;
+ }
- platform_set_drvdata(pdev, rdev);
+ platform_set_drvdata(pdev, tps);
+ for (i = 0; i < TPS65217_NUM_REGULATOR; i++) {
+
+ reg_data = pdata->tps65217_init_data[i];
+
+ /*
+ * Regulator API handles empty constraints but not NULL
+ * constraints
+ */
+ if (!reg_data)
+ continue;
+
+ /* Register the regulators */
+ tps->info[i] = &tps65217_pmic_regs[i];
+
+ config.dev = tps->dev;
+ config.init_data = reg_data;
+ config.driver_data = tps;
+ config.regmap = tps->regmap;
+ if (tps->dev->of_node)
+ config.of_node = pdata->of_node[i];
+
+ rdev = regulator_register(&regulators[i], &config);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "failed to register %s regulator\n",
+ pdev->name);
+ ret = PTR_ERR(rdev);
+ goto err_unregister_regulator;
+ }
+
+ /* Save regulator for cleanup */
+ tps->rdev[i] = rdev;
+ }
return 0;
+
+err_unregister_regulator:
+ while (--i >= 0)
+ regulator_unregister(tps->rdev[i]);
+
+ return ret;
}
static int __devexit tps65217_regulator_remove(struct platform_device *pdev)
{
- struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct tps65217 *tps = platform_get_drvdata(pdev);
+ unsigned int i;
+
+ for (i = 0; i < TPS65217_NUM_REGULATOR; i++)
+ regulator_unregister(tps->rdev[i]);
platform_set_drvdata(pdev, NULL);
- regulator_unregister(rdev);
return 0;
}
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index e6da90ab5153..19241fc30050 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -240,14 +240,16 @@ static struct tps6586x_regulator tps6586x_regulator[] = {
TPS6586X_LDO(LDO_9, "vinldo9", ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7),
TPS6586X_LDO(LDO_RTC, NULL, ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7),
TPS6586X_LDO(LDO_1, "vinldo01", dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1),
- TPS6586X_LDO(SM_2, "sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
+ TPS6586X_LDO(SM_2, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
TPS6586X_DVM(LDO_2, "vinldo23", dvm, LDO2BV1, 0, 5, ENA, 3,
ENB, 3, VCC2, 6),
TPS6586X_DVM(LDO_4, "vinldo4", ldo4, LDO4V1, 0, 5, ENC, 3,
END, 3, VCC1, 6),
- TPS6586X_DVM(SM_0, "sm0", dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2),
- TPS6586X_DVM(SM_1, "sm1", dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0),
+ TPS6586X_DVM(SM_0, "vin-sm0", dvm, SM0V1, 0, 5, ENA, 1,
+ ENB, 1, VCC1, 2),
+ TPS6586X_DVM(SM_1, "vin-sm1", dvm, SM1V1, 0, 5, ENA, 0,
+ ENB, 0, VCC1, 0),
};
/*
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 242fe90dc565..77a71a5c17c3 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -1037,7 +1037,7 @@ TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300);
TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300);
TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300);
TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300);
-TWL4030_FIXED_LDO(VINTANA2, 0x3f, 1500, 11, 100, 0x08);
+TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08);
TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08);
TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08);
TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08);
@@ -1048,7 +1048,6 @@ TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0);
TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0);
TWL6030_FIXED_LDO(V1V8, 0x16, 1800, 0);
TWL6030_FIXED_LDO(V2V1, 0x1c, 2100, 0);
-TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0);
TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34);
TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10);
TWL6025_ADJUSTABLE_SMPS(VIO, 0x16);
@@ -1117,7 +1116,7 @@ static const struct of_device_id twl_of_match[] __devinitconst = {
TWL6025_OF_MATCH("ti,twl6025-ldo6", LDO6),
TWL6025_OF_MATCH("ti,twl6025-ldoln", LDOLN),
TWL6025_OF_MATCH("ti,twl6025-ldousb", LDOUSB),
- TWLFIXED_OF_MATCH("ti,twl4030-vintana2", VINTANA2),
+ TWLFIXED_OF_MATCH("ti,twl4030-vintana1", VINTANA1),
TWLFIXED_OF_MATCH("ti,twl4030-vintdig", VINTDIG),
TWLFIXED_OF_MATCH("ti,twl4030-vusb1v5", VUSB1V5),
TWLFIXED_OF_MATCH("ti,twl4030-vusb1v8", VUSB1V8),
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 590cfafc7c17..1859f71372e2 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -1008,8 +1008,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
return 0;
free_coherent:
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
- vrp->bufs_dma);
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
+ bufs_va, vrp->bufs_dma);
vqs_del:
vdev->config->del_vqs(vrp->vdev);
free_vrp:
@@ -1043,7 +1043,7 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
vdev->config->del_vqs(vrp->vdev);
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
vrp->rbufs, vrp->bufs_dma);
kfree(vrp);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index eb415bd76494..9592b936b71b 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -582,6 +582,7 @@ enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer)
void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
{
+ pm_stay_awake(rtc->dev.parent);
schedule_work(&rtc->irqwork);
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
@@ -844,6 +845,7 @@ void rtc_timer_do_work(struct work_struct *work)
mutex_lock(&rtc->ops_lock);
again:
+ pm_relax(rtc->dev.parent);
__rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
while ((next = timerqueue_getnext(&rtc->timerqueue))) {
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
index a2f956d90de0..6367984e0565 100644
--- a/drivers/rtc/rtc-88pm80x.c
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -314,8 +314,8 @@ static int __devinit pm80x_rtc_probe(struct platform_device *pdev)
info->rtc_dev = rtc_device_register("88pm80x-rtc", &pdev->dev,
&pm80x_rtc_ops, THIS_MODULE);
- ret = PTR_ERR(info->rtc_dev);
if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
goto out_rtc;
}
@@ -339,7 +339,6 @@ static int __devinit pm80x_rtc_probe(struct platform_device *pdev)
out_rtc:
pm80x_free_irq(chip, info->irq, info);
out:
- devm_kfree(&pdev->dev, info);
return ret;
}
@@ -349,7 +348,6 @@ static int __devexit pm80x_rtc_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
rtc_device_unregister(info->rtc_dev);
pm80x_free_irq(info->chip, info->irq, info);
- devm_kfree(&pdev->dev, info);
return 0;
}
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 831868904e02..1dd61f402b04 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -58,6 +58,7 @@ struct sam9_rtc {
struct rtc_device *rtcdev;
u32 imr;
void __iomem *gpbr;
+ int irq;
};
#define rtt_readl(rtc, field) \
@@ -292,7 +293,7 @@ static int __devinit at91_rtc_probe(struct platform_device *pdev)
{
struct resource *r, *r_gpbr;
struct sam9_rtc *rtc;
- int ret;
+ int ret, irq;
u32 mr;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -302,10 +303,18 @@ static int __devinit at91_rtc_probe(struct platform_device *pdev)
return -ENODEV;
}
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resource\n");
+ return irq;
+ }
+
rtc = kzalloc(sizeof *rtc, GFP_KERNEL);
if (!rtc)
return -ENOMEM;
+ rtc->irq = irq;
+
/* platform setup code should have handled this; sigh */
if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, 1);
@@ -345,11 +354,10 @@ static int __devinit at91_rtc_probe(struct platform_device *pdev)
}
/* register irq handler after we know what name we'll use */
- ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
- IRQF_SHARED,
+ ret = request_irq(rtc->irq, at91_rtc_interrupt, IRQF_SHARED,
dev_name(&rtc->rtcdev->dev), rtc);
if (ret) {
- dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS);
+ dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq);
rtc_device_unregister(rtc->rtcdev);
goto fail_register;
}
@@ -386,7 +394,7 @@ static int __devexit at91_rtc_remove(struct platform_device *pdev)
/* disable all interrupts */
rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
- free_irq(AT91_ID_SYS, rtc);
+ free_irq(rtc->irq, rtc);
rtc_device_unregister(rtc->rtcdev);
@@ -423,7 +431,7 @@ static int at91_rtc_suspend(struct platform_device *pdev,
rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
if (rtc->imr) {
if (device_may_wakeup(&pdev->dev) && (mr & AT91_RTT_ALMIEN)) {
- enable_irq_wake(AT91_ID_SYS);
+ enable_irq_wake(rtc->irq);
/* don't let RTTINC cause wakeups */
if (mr & AT91_RTT_RTTINCIEN)
rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
@@ -441,7 +449,7 @@ static int at91_rtc_resume(struct platform_device *pdev)
if (rtc->imr) {
if (device_may_wakeup(&pdev->dev))
- disable_irq_wake(AT91_ID_SYS);
+ disable_irq_wake(rtc->irq);
mr = rtt_readl(rtc, MR);
rtt_writel(rtc, MR, mr | rtc->imr);
}
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 132333d75408..4267789ca995 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -568,7 +568,6 @@ static irqreturn_t cmos_interrupt(int irq, void *p)
hpet_mask_rtc_irq_bit(RTC_AIE);
CMOS_READ(RTC_INTR_FLAGS);
- pm_wakeup_event(cmos_rtc.dev, 0);
}
spin_unlock(&rtc_lock);
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index 836118795c0b..13e4df63974f 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -43,6 +43,7 @@
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
+#include <linux/sysfs.h>
#define DRV_VERSION "0.6"
@@ -292,6 +293,7 @@ static int __devinit pcf2123_probe(struct spi_device *spi)
pdata->rtc = rtc;
for (i = 0; i < 16; i++) {
+ sysfs_attr_init(&pdata->regs[i].attr.attr);
sprintf(pdata->regs[i].name, "%1x", i);
pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
pdata->regs[i].attr.attr.name = pdata->regs[i].name;
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 77074ccd2850..fd5c7af04ae5 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -122,9 +122,12 @@ rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
if (!pdata->rtc_24h) {
- tm->tm_hour %= 12;
- if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM)
+ if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) {
+ tm->tm_hour -= 20;
+ tm->tm_hour %= 12;
tm->tm_hour += 12;
+ } else
+ tm->tm_hour %= 12;
}
tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index c5d06fe83bba..9277d945bf48 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -495,6 +495,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
if (ret < 0)
goto out1;
+ /* ensure interrupts are disabled, bootloaders can be strange */
+ ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "unable to disable interrupt\n");
+
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
index 59c6245e0421..ea5c6f857ca5 100644
--- a/drivers/rtc/rtc-wm831x.c
+++ b/drivers/rtc/rtc-wm831x.c
@@ -24,7 +24,7 @@
#include <linux/mfd/wm831x/core.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
-
+#include <linux/random.h>
/*
* R16416 (0x4020) - RTC Write Counter
@@ -96,6 +96,26 @@ struct wm831x_rtc {
unsigned int alarm_enabled:1;
};
+static void wm831x_rtc_add_randomness(struct wm831x *wm831x)
+{
+ int ret;
+ u16 reg;
+
+ /*
+ * The write counter contains a pseudo-random number which is
+ * regenerated every time we set the RTC so it should be a
+ * useful per-system source of entropy.
+ */
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
+ if (ret >= 0) {
+ reg = ret;
+ add_device_randomness(&reg, sizeof(reg));
+ } else {
+ dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
+ ret);
+ }
+}
+
/*
* Read current time and date in RTC
*/
@@ -431,6 +451,8 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
alm_irq, ret);
}
+ wm831x_rtc_add_randomness(wm831x);
+
return 0;
err:
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 15370a2c5ff0..0595c763dafd 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -534,11 +534,11 @@ static void dasd_change_state(struct dasd_device *device)
if (rc)
device->target = device->state;
- if (device->state == device->target)
- wake_up(&dasd_init_waitq);
-
/* let user-space know that the device status changed */
kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
+
+ if (device->state == device->target)
+ wake_up(&dasd_init_waitq);
}
/*
@@ -2157,6 +2157,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(device))) {
cqr->status = DASD_CQR_FAILED;
+ cqr->intrc = -EAGAIN;
continue;
}
/* Don't try to start requests if device is stopped */
@@ -3270,6 +3271,16 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
dasd_schedule_device_bh(device);
}
if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) {
+ if (!(device->path_data.opm & eventlpm) &&
+ !(device->path_data.tbvpm & eventlpm)) {
+ /*
+ * we can not establish a pathgroup on an
+ * unavailable path, so trigger a path
+ * verification first
+ */
+ device->path_data.tbvpm |= eventlpm;
+ dasd_schedule_device_bh(device);
+ }
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Pathgroup re-established\n");
if (device->discipline->kick_validate)
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 157defe5e069..6b556995bb33 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -384,6 +384,29 @@ static void _remove_device_from_lcu(struct alias_lcu *lcu,
group->next = NULL;
};
+static int
+suborder_not_supported(struct dasd_ccw_req *cqr)
+{
+ char *sense;
+ char reason;
+ char msg_format;
+ char msg_no;
+
+ sense = dasd_get_sense(&cqr->irb);
+ if (!sense)
+ return 0;
+
+ reason = sense[0];
+ msg_format = (sense[7] & 0xF0);
+ msg_no = (sense[7] & 0x0F);
+
+ /* command reject, Format 0 MSG 4 - invalid parameter */
+ if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04))
+ return 1;
+
+ return 0;
+}
+
static int read_unit_address_configuration(struct dasd_device *device,
struct alias_lcu *lcu)
{
@@ -435,6 +458,8 @@ static int read_unit_address_configuration(struct dasd_device *device,
do {
rc = dasd_sleep_on(cqr);
+ if (rc && suborder_not_supported(cqr))
+ return -EOPNOTSUPP;
} while (rc && (cqr->retries > 0));
if (rc) {
spin_lock_irqsave(&lcu->lock, flags);
@@ -521,7 +546,7 @@ static void lcu_update_work(struct work_struct *work)
* processing the data
*/
spin_lock_irqsave(&lcu->lock, flags);
- if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+ if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
" alias data in lcu (rc = %d), retry later", rc);
schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 40a826a7295f..c48c72abbefc 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1507,7 +1507,8 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
* call might change behaviour of DASD devices.
*/
static int
-dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
+dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
+ unsigned long flags)
{
struct dasd_ccw_req *cqr;
int rc;
@@ -1516,10 +1517,19 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
if (IS_ERR(cqr))
return PTR_ERR(cqr);
+ /*
+ * set flags e.g. turn on failfast, to prevent blocking
+ * the calling function should handle failed requests
+ */
+ cqr->flags |= flags;
+
rc = dasd_sleep_on(cqr);
if (!rc)
/* trigger CIO to reprobe devices */
css_schedule_reprobe();
+ else if (cqr->intrc == -EAGAIN)
+ rc = -EAGAIN;
+
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1527,7 +1537,8 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
/*
* Valide storage server of current device.
*/
-static void dasd_eckd_validate_server(struct dasd_device *device)
+static int dasd_eckd_validate_server(struct dasd_device *device,
+ unsigned long flags)
{
int rc;
struct dasd_eckd_private *private;
@@ -1536,17 +1547,18 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
if (private->uid.type == UA_BASE_PAV_ALIAS ||
private->uid.type == UA_HYPER_PAV_ALIAS)
- return;
+ return 0;
if (dasd_nopav || MACHINE_IS_VM)
enable_pav = 0;
else
enable_pav = 1;
- rc = dasd_eckd_psf_ssc(device, enable_pav);
+ rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
"returned rc=%d", private->uid.ssid, rc);
+ return rc;
}
/*
@@ -1556,7 +1568,13 @@ static void dasd_eckd_do_validate_server(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device,
kick_validate);
- dasd_eckd_validate_server(device);
+ if (dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST)
+ == -EAGAIN) {
+ /* schedule worker again if failed */
+ schedule_work(&device->kick_validate);
+ return;
+ }
+
dasd_put_device(device);
}
@@ -1685,7 +1703,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err2;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, 0);
/* device may report different configuration data after LCU setup */
rc = dasd_eckd_read_conf(device);
@@ -3804,7 +3822,7 @@ dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
case BIODASDSYMMIO:
return dasd_symm_io(device, argp);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
@@ -4153,7 +4171,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
rc = dasd_alias_make_device_known_to_lcu(device);
if (rc)
return rc;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST);
/* RE-Read Configuration Data */
rc = dasd_eckd_read_conf(device);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index cceae70279f6..654c6921a6d4 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -498,12 +498,9 @@ int dasd_ioctl(struct block_device *bdev, fmode_t mode,
break;
default:
/* if the discipline has an ioctl method try it. */
- if (base->discipline->ioctl) {
+ rc = -ENOTTY;
+ if (base->discipline->ioctl)
rc = base->discipline->ioctl(block, cmd, argp);
- if (rc == -ENOIOCTLCMD)
- rc = -EINVAL;
- } else
- rc = -EINVAL;
}
dasd_put_device(base);
return rc;
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 6a6f76bf6e3d..b1032931a1c4 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -242,11 +242,13 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
switch (sdias_evbuf.event_status) {
case EVSTATE_ALL_STORED:
TRACE("all stored\n");
+ break;
case EVSTATE_PART_STORED:
TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
break;
case EVSTATE_NO_DATA:
TRACE("no data\n");
+ /* fall through */
default:
pr_err("Error from SCLP while copying hsa. "
"Event status = %x\n",
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index ed25c8740a9c..fc916f5d7314 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1426,6 +1426,8 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
return IO_SCH_REPROBE;
if (cdev->online)
return IO_SCH_VERIFY;
+ if (cdev->private->state == DEV_STATE_NOT_OPER)
+ return IO_SCH_UNREG_ATTACH;
return IO_SCH_NOP;
}
@@ -1519,11 +1521,14 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
+ spin_lock_irqsave(sch->lock, flags);
if (cdev->private->flags.resuming) {
/* Device will be handled later. */
rc = 0;
- goto out;
+ goto out_unlock;
}
+ sch_set_cdev(sch, NULL);
+ spin_unlock_irqrestore(sch->lock, flags);
/* Unregister ccw device. */
ccw_device_unregister(cdev);
break;
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 33d6630529de..91eec60252ee 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -1264,6 +1264,9 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
int rc = 0;
u64 mask64;
+ memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1));
+ memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2));
+
bnx2i_adjust_qp_size(hba);
iscsi_init.flags =
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 796482badf13..2b4261cb7742 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1315,8 +1315,9 @@ static void complete_scsi_command(struct CommandList *cp)
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "cp %p has "
- "protocol error \n", cp);
+ "protocol error\n", cp);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index dc27598785e5..ed38454228c6 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -4066,7 +4066,6 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
spin_lock_init(&instance->cmd_pool_lock);
spin_lock_init(&instance->hba_lock);
spin_lock_init(&instance->completion_lock);
- spin_lock_init(&poll_aen_lock);
mutex_init(&instance->aen_mutex);
mutex_init(&instance->reset_mutex);
@@ -5392,6 +5391,8 @@ static int __init megasas_init(void)
printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION,
MEGASAS_EXT_VERSION);
+ spin_lock_init(&poll_aen_lock);
+
support_poll_for_event = 2;
support_device_change = 1;
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 9d46fcbe7755..9d5a56c4b332 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -1209,6 +1209,13 @@ _base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
u16 message_control;
+ /* Check whether controller SAS2008 B0 controller,
+ if it is SAS2008 B0 controller use IO-APIC instead of MSIX */
+ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
+ ioc->pdev->revision == 0x01) {
+ return -EINVAL;
+ }
+
base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
if (!base) {
dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
@@ -2424,10 +2431,13 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
}
/* command line tunables for max controller queue depth */
- if (max_queue_depth != -1)
- max_request_credit = (max_queue_depth < facts->RequestCredit)
- ? max_queue_depth : facts->RequestCredit;
- else
+ if (max_queue_depth != -1 && max_queue_depth != 0) {
+ max_request_credit = min_t(u16, max_queue_depth +
+ ioc->hi_priority_depth + ioc->internal_depth,
+ facts->RequestCredit);
+ if (max_request_credit > MAX_HBA_QUEUE_DEPTH)
+ max_request_credit = MAX_HBA_QUEUE_DEPTH;
+ } else
max_request_credit = min_t(u16, facts->RequestCredit,
MAX_HBA_QUEUE_DEPTH);
@@ -2502,7 +2512,7 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
/* set the scsi host can_queue depth
* with some internal commands that could be outstanding
*/
- ioc->shost->can_queue = ioc->scsiio_depth - (2);
+ ioc->shost->can_queue = ioc->scsiio_depth;
dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host: "
"can_queue depth (%d)\n", ioc->name, ioc->shost->can_queue));
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 4a6381c87253..de2337f255a7 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -42,6 +42,8 @@
#include <trace/events/scsi.h>
+static void scsi_eh_done(struct scsi_cmnd *scmd);
+
#define SENSE_TIMEOUT (10*HZ)
/*
@@ -241,6 +243,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (! scsi_command_normalize_sense(scmd, &sshdr))
return FAILED; /* no valid sense data */
+ if (scmd->cmnd[0] == TEST_UNIT_READY && scmd->scsi_done != scsi_eh_done)
+ /*
+ * nasty: for mid-layer issued TURs, we need to return the
+ * actual sense data without any recovery attempt. For eh
+ * issued ones, we need to try to recover and interpret
+ */
+ return SUCCESS;
+
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index ffd77739ae3e..faa790fba134 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -776,7 +776,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
}
if (req->cmd_type == REQ_TYPE_BLOCK_PC) { /* SG_IO ioctl from block level */
- req->errors = result;
if (result) {
if (sense_valid && req->sense) {
/*
@@ -792,6 +791,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
if (!sense_deferred)
error = __scsi_error_from_host_byte(cmd, result);
}
+ /*
+ * __scsi_error_from_host_byte may have reset the host_byte
+ */
+ req->errors = cmd->result;
req->resid_len = scsi_get_resid(cmd);
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 56a93794c470..d947ffc20ceb 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -764,6 +764,16 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
sdev->model = (char *) (sdev->inquiry + 16);
sdev->rev = (char *) (sdev->inquiry + 32);
+ if (strncmp(sdev->vendor, "ATA ", 8) == 0) {
+ /*
+ * sata emulation layer device. This is a hack to work around
+ * the SATL power management specifications which state that
+ * when the SATL detects the device has gone into standby
+ * mode, it shall respond with NOT READY.
+ */
+ sdev->allow_restart = 1;
+ }
+
if (*bflags & BLIST_ISROM) {
sdev->type = TYPE_ROM;
sdev->removable = 1;
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 2d1e68db9b3f..e894ca7b54c0 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -4146,45 +4146,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
static void
fc_bsg_remove(struct request_queue *q)
{
- struct request *req; /* block request */
- int counts; /* totals for request_list count and starved */
-
if (q) {
- /* Stop taking in new requests */
- spin_lock_irq(q->queue_lock);
- blk_stop_queue(q);
-
- /* drain all requests in the queue */
- while (1) {
- /* need the lock to fetch a request
- * this may fetch the same reqeust as the previous pass
- */
- req = blk_fetch_request(q);
- /* save requests in use and starved */
- counts = q->rq.count[0] + q->rq.count[1] +
- q->rq.starved[0] + q->rq.starved[1];
- spin_unlock_irq(q->queue_lock);
- /* any requests still outstanding? */
- if (counts == 0)
- break;
-
- /* This may be the same req as the previous iteration,
- * always send the blk_end_request_all after a prefetch.
- * It is not okay to not end the request because the
- * prefetch started the request.
- */
- if (req) {
- /* return -ENXIO to indicate that this queue is
- * going away
- */
- req->errors = -ENXIO;
- blk_end_request_all(req, -ENXIO);
- }
-
- msleep(200); /* allow bsg to possibly finish */
- spin_lock_irq(q->queue_lock);
- }
-
bsg_unregister_queue(q);
blk_cleanup_queue(q);
}
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 09809d06eccb..fa1dfaa83e32 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -575,7 +575,7 @@ static int iscsi_remove_host(struct transport_container *tc,
struct iscsi_cls_host *ihost = shost->shost_data;
if (ihost->bsg_q) {
- bsg_remove_queue(ihost->bsg_q);
+ bsg_unregister_queue(ihost->bsg_q);
blk_cleanup_queue(ihost->bsg_q);
}
return 0;
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index c7030fbee79c..3e79a2f00042 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -331,7 +331,7 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
int i;
for_each_sg(table->sgl, sg_elem, table->nents, i)
- sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
+ sg[idx++] = *sg_elem;
*p_idx = idx;
}
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig
index c88cbccc62b0..a305731742a9 100644
--- a/drivers/sh/intc/Kconfig
+++ b/drivers/sh/intc/Kconfig
@@ -1,3 +1,7 @@
+config SH_INTC
+ def_bool y
+ select IRQ_DOMAIN
+
comment "Interrupt controller options"
config INTC_USERIMASK
diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile
index 44f006d09471..54ec2a0643df 100644
--- a/drivers/sh/intc/Makefile
+++ b/drivers/sh/intc/Makefile
@@ -1,4 +1,4 @@
-obj-y := access.o chip.o core.o handle.o virq.o
+obj-y := access.o chip.o core.o handle.o irqdomain.o virq.o
obj-$(CONFIG_INTC_BALANCING) += balancing.o
obj-$(CONFIG_INTC_USERIMASK) += userimask.o
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 7e562ccb6997..8f32a1323a79 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -25,6 +25,7 @@
#include <linux/stat.h>
#include <linux/interrupt.h>
#include <linux/sh_intc.h>
+#include <linux/irqdomain.h>
#include <linux/device.h>
#include <linux/syscore_ops.h>
#include <linux/list.h>
@@ -310,6 +311,8 @@ int __init register_intc_controller(struct intc_desc *desc)
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
+ intc_irq_domain_init(d, hw);
+
/* register the vectors one by one */
for (i = 0; i < hw->nr_vectors; i++) {
struct intc_vect *vect = hw->vectors + i;
@@ -319,10 +322,18 @@ int __init register_intc_controller(struct intc_desc *desc)
if (!vect->enum_id)
continue;
- res = irq_alloc_desc_at(irq, numa_node_id());
- if (res != irq && res != -EEXIST) {
- pr_err("can't get irq_desc for %d\n", irq);
- continue;
+ res = irq_create_identity_mapping(d->domain, irq);
+ if (unlikely(res)) {
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain, irq, irq);
+ if (unlikely(res)) {
+ pr_err("domain association failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n", irq);
+ continue;
+ }
}
intc_irq_xlate_set(irq, vect->enum_id, d);
@@ -340,10 +351,21 @@ int __init register_intc_controller(struct intc_desc *desc)
* IRQ support, each vector still needs to have
* its own backing irq_desc.
*/
- res = irq_alloc_desc_at(irq2, numa_node_id());
- if (res != irq2 && res != -EEXIST) {
- pr_err("can't get irq_desc for %d\n", irq2);
- continue;
+ res = irq_create_identity_mapping(d->domain, irq2);
+ if (unlikely(res)) {
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain,
+ irq2, irq2);
+ if (unlikely(res)) {
+ pr_err("domain association "
+ "failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n",
+ irq);
+ continue;
+ }
}
vect2->enum_id = 0;
diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h
index f034a979a16f..7dff08e2a071 100644
--- a/drivers/sh/intc/internals.h
+++ b/drivers/sh/intc/internals.h
@@ -1,5 +1,6 @@
#include <linux/sh_intc.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/types.h>
@@ -66,6 +67,7 @@ struct intc_desc_int {
unsigned int nr_sense;
struct intc_window *window;
unsigned int nr_windows;
+ struct irq_domain *domain;
struct irq_chip chip;
bool skip_suspend;
};
@@ -187,6 +189,9 @@ unsigned long intc_get_ack_handle(unsigned int irq);
void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d,
intc_enum enum_id, int enable);
+/* irqdomain.c */
+void intc_irq_domain_init(struct intc_desc_int *d, struct intc_hw_desc *hw);
+
/* virq.c */
void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d);
void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d);
diff --git a/drivers/sh/intc/irqdomain.c b/drivers/sh/intc/irqdomain.c
new file mode 100644
index 000000000000..3968f1c3c5c3
--- /dev/null
+++ b/drivers/sh/intc/irqdomain.c
@@ -0,0 +1,68 @@
+/*
+ * IRQ domain support for SH INTC subsystem
+ *
+ * Copyright (C) 2012 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/irqdomain.h>
+#include <linux/sh_intc.h>
+#include <linux/export.h>
+#include "internals.h"
+
+/**
+ * intc_irq_domain_evt_xlate() - Generic xlate for vectored IRQs.
+ *
+ * This takes care of exception vector to hwirq translation through
+ * by way of evt2irq() translation.
+ *
+ * Note: For platforms that use a flat vector space without INTEVT this
+ * basically just mimics irq_domain_xlate_onecell() by way of a nopped
+ * out evt2irq() implementation.
+ */
+static int intc_evt_xlate(struct irq_domain *d, struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 1))
+ return -EINVAL;
+
+ *out_hwirq = evt2irq(intspec[0]);
+ *out_type = IRQ_TYPE_NONE;
+
+ return 0;
+}
+
+static const struct irq_domain_ops intc_evt_ops = {
+ .xlate = intc_evt_xlate,
+};
+
+void __init intc_irq_domain_init(struct intc_desc_int *d,
+ struct intc_hw_desc *hw)
+{
+ unsigned int irq_base, irq_end;
+
+ /*
+ * Quick linear revmap check
+ */
+ irq_base = evt2irq(hw->vectors[0].vect);
+ irq_end = evt2irq(hw->vectors[hw->nr_vectors - 1].vect);
+
+ /*
+ * Linear domains have a hard-wired assertion that IRQs start at
+ * 0 in order to make some performance optimizations. Lamely
+ * restrict the linear case to these conditions here, taking the
+ * tree penalty for linear cases with non-zero hwirq bases.
+ */
+ if (irq_base == 0 && irq_end == (irq_base + hw->nr_vectors - 1))
+ d->domain = irq_domain_add_linear(NULL, hw->nr_vectors,
+ &intc_evt_ops, NULL);
+ else
+ d->domain = irq_domain_add_tree(NULL, &intc_evt_ops, NULL);
+
+ BUG_ON(!d->domain);
+}
diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c
index 0802b6c0d653..0646bf6e7889 100644
--- a/drivers/sh/pfc/pinctrl.c
+++ b/drivers/sh/pfc/pinctrl.c
@@ -208,10 +208,13 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
break;
case PINMUX_TYPE_GPIO:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_OUTPUT:
break;
default:
pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type);
- return -ENOTSUPP;
+ ret = -ENOTSUPP;
+ goto err;
}
ret = 0;
@@ -276,7 +279,6 @@ static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long config)
{
struct sh_pfc_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev);
- struct sh_pfc *pfc = pmx->pfc;
/* Validate the new type */
if (config >= PINMUX_FLAG_TYPE)
@@ -326,20 +328,6 @@ static struct pinctrl_desc sh_pfc_pinctrl_desc = {
.confops = &sh_pfc_pinconf_ops,
};
-int sh_pfc_register_pinctrl(struct sh_pfc *pfc)
-{
- sh_pfc_pmx = kzalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL);
- if (unlikely(!sh_pfc_pmx))
- return -ENOMEM;
-
- spin_lock_init(&sh_pfc_pmx->lock);
-
- sh_pfc_pmx->pfc = pfc;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(sh_pfc_register_pinctrl);
-
static inline void __devinit sh_pfc_map_one_gpio(struct sh_pfc *pfc,
struct sh_pfc_pinctrl *pmx,
struct pinmux_gpio *gpio,
@@ -481,7 +469,6 @@ static int __devexit sh_pfc_pinctrl_remove(struct platform_device *pdev)
{
struct sh_pfc_pinctrl *pmx = platform_get_drvdata(pdev);
- pinctrl_remove_gpio_range(pmx->pctl, &sh_pfc_gpio_range);
pinctrl_unregister(pmx->pctl);
platform_set_drvdata(pdev, NULL);
@@ -507,7 +494,7 @@ static struct platform_device sh_pfc_pinctrl_device = {
.id = -1,
};
-static int __init sh_pfc_pinctrl_init(void)
+static int sh_pfc_pinctrl_init(void)
{
int rc;
@@ -521,10 +508,22 @@ static int __init sh_pfc_pinctrl_init(void)
return rc;
}
+int sh_pfc_register_pinctrl(struct sh_pfc *pfc)
+{
+ sh_pfc_pmx = kzalloc(sizeof(struct sh_pfc_pinctrl), GFP_KERNEL);
+ if (unlikely(!sh_pfc_pmx))
+ return -ENOMEM;
+
+ spin_lock_init(&sh_pfc_pmx->lock);
+
+ sh_pfc_pmx->pfc = pfc;
+
+ return sh_pfc_pinctrl_init();
+}
+EXPORT_SYMBOL_GPL(sh_pfc_register_pinctrl);
+
static void __exit sh_pfc_pinctrl_exit(void)
{
platform_driver_unregister(&sh_pfc_pinctrl_driver);
}
-
-subsys_initcall(sh_pfc_pinctrl_init);
module_exit(sh_pfc_pinctrl_exit);
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 6e25ef1bce91..a9f4049c6769 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -47,6 +47,8 @@ struct bcm63xx_spi {
/* Platform data */
u32 speed_hz;
unsigned fifo_size;
+ unsigned int msg_type_shift;
+ unsigned int msg_ctl_width;
/* Data buffers */
const unsigned char *tx_ptr;
@@ -221,13 +223,20 @@ static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
if (t->rx_buf && t->tx_buf)
- msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
+ msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
else if (t->rx_buf)
- msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
+ msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
else if (t->tx_buf)
- msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
-
- bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
+ msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
+
+ switch (bs->msg_ctl_width) {
+ case 8:
+ bcm_spi_writeb(bs, msg_ctl, SPI_MSG_CTL);
+ break;
+ case 16:
+ bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
+ break;
+ }
/* Issue the transfer */
cmd = SPI_CMD_START_IMMEDIATE;
@@ -406,9 +415,21 @@ static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
master->transfer_one_message = bcm63xx_spi_transfer_one;
master->mode_bits = MODEBITS;
bs->speed_hz = pdata->speed_hz;
+ bs->msg_type_shift = pdata->msg_type_shift;
+ bs->msg_ctl_width = pdata->msg_ctl_width;
bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA));
+ switch (bs->msg_ctl_width) {
+ case 8:
+ case 16:
+ break;
+ default:
+ dev_err(dev, "unsupported MSG_CTL width: %d\n",
+ bs->msg_ctl_width);
+ goto out_clk_disable;
+ }
+
/* Initialize hardware */
clk_enable(bs->clk);
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
@@ -438,7 +459,7 @@ out:
static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
{
- struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
spi_unregister_master(master);
@@ -452,6 +473,8 @@ static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, 0);
+ spi_master_put(master);
+
return 0;
}
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index b2d4b9e4e010..764bfee75920 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -533,7 +533,6 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
iounmap(mcfqspi->iobase);
release_mem_region(res->start, resource_size(res));
spi_unregister_master(master);
- spi_master_put(master);
return 0;
}
@@ -541,7 +540,7 @@ static int __devexit mcfqspi_remove(struct platform_device *pdev)
#ifdef CONFIG_PM_SLEEP
static int mcfqspi_suspend(struct device *dev)
{
- struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_suspend(master);
@@ -553,7 +552,7 @@ static int mcfqspi_suspend(struct device *dev)
static int mcfqspi_resume(struct device *dev)
{
- struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_resume(master);
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 7d46b15e1520..b2fb141da375 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -28,6 +28,8 @@
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -39,7 +41,6 @@
#include <linux/spi/spi.h>
-#include <plat/dma.h>
#include <plat/clock.h>
#include <plat/mcspi.h>
@@ -93,8 +94,8 @@
/* We have 2 DMA channels per CS, one for RX and one for TX */
struct omap2_mcspi_dma {
- int dma_tx_channel;
- int dma_rx_channel;
+ struct dma_chan *dma_tx;
+ struct dma_chan *dma_rx;
int dma_tx_sync_dev;
int dma_rx_sync_dev;
@@ -300,20 +301,46 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
return 0;
}
+static void omap2_mcspi_rx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_rx_completion);
+
+ /* We must disable the DMA RX request */
+ omap2_mcspi_set_dma_req(spi, 1, 0);
+}
+
+static void omap2_mcspi_tx_callback(void *data)
+{
+ struct spi_device *spi = data;
+ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master);
+ struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select];
+
+ complete(&mcspi_dma->dma_tx_completion);
+
+ /* We must disable the DMA TX request */
+ omap2_mcspi_set_dma_req(spi, 0, 0);
+}
+
static unsigned
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
{
struct omap2_mcspi *mcspi;
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi_dma *mcspi_dma;
- unsigned int count, c;
- unsigned long base, tx_reg, rx_reg;
- int word_len, data_type, element_count;
+ unsigned int count;
+ int word_len, element_count;
int elements = 0;
u32 l;
u8 * rx;
const u8 * tx;
void __iomem *chstat_reg;
+ struct dma_slave_config cfg;
+ enum dma_slave_buswidth width;
+ unsigned es;
mcspi = spi_master_get_devdata(spi->master);
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
@@ -321,68 +348,92 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
+ if (cs->word_len <= 8) {
+ width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ es = 1;
+ } else if (cs->word_len <= 16) {
+ width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ es = 2;
+ } else {
+ width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ es = 4;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
+ cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0;
+ cfg.src_addr_width = width;
+ cfg.dst_addr_width = width;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
+
+ if (xfer->tx_buf && mcspi_dma->dma_tx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+
+ dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->tx_dma;
+ sg_dma_len(&sg) = xfer->len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
+ DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_tx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
+ if (xfer->rx_buf && mcspi_dma->dma_rx) {
+ struct dma_async_tx_descriptor *tx;
+ struct scatterlist sg;
+ size_t len = xfer->len - es;
+
+ dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
+
+ if (l & OMAP2_MCSPI_CHCONF_TURBO)
+ len -= es;
+
+ sg_init_table(&sg, 1);
+ sg_dma_address(&sg) = xfer->rx_dma;
+ sg_dma_len(&sg) = len;
+
+ tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
+ DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (tx) {
+ tx->callback = omap2_mcspi_rx_callback;
+ tx->callback_param = spi;
+ dmaengine_submit(tx);
+ } else {
+ /* FIXME: fall back to PIO? */
+ }
+ }
+
count = xfer->len;
- c = count;
word_len = cs->word_len;
- base = cs->phys;
- tx_reg = base + OMAP2_MCSPI_TX0;
- rx_reg = base + OMAP2_MCSPI_RX0;
rx = xfer->rx_buf;
tx = xfer->tx_buf;
if (word_len <= 8) {
- data_type = OMAP_DMA_DATA_TYPE_S8;
element_count = count;
} else if (word_len <= 16) {
- data_type = OMAP_DMA_DATA_TYPE_S16;
element_count = count >> 1;
} else /* word_len <= 32 */ {
- data_type = OMAP_DMA_DATA_TYPE_S32;
element_count = count >> 2;
}
if (tx != NULL) {
- omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
- data_type, element_count, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_tx_sync_dev, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- tx_reg, 0, 0);
-
- omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->tx_dma, 0, 0);
- }
-
- if (rx != NULL) {
- elements = element_count - 1;
- if (l & OMAP2_MCSPI_CHCONF_TURBO)
- elements--;
-
- omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
- data_type, elements, 1,
- OMAP_DMA_SYNC_ELEMENT,
- mcspi_dma->dma_rx_sync_dev, 1);
-
- omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_CONSTANT,
- rx_reg, 0, 0);
-
- omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0,
- OMAP_DMA_AMODE_POST_INC,
- xfer->rx_dma, 0, 0);
- }
-
- if (tx != NULL) {
- omap_start_dma(mcspi_dma->dma_tx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
}
if (rx != NULL) {
- omap_start_dma(mcspi_dma->dma_rx_channel);
+ dma_async_issue_pending(mcspi_dma->dma_rx);
omap2_mcspi_set_dma_req(spi, 1, 1);
}
@@ -408,7 +459,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
DMA_FROM_DEVICE);
omap2_mcspi_set_enable(spi, 0);
+ elements = element_count - 1;
+
if (l & OMAP2_MCSPI_CHCONF_TURBO) {
+ elements--;
if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
& OMAP2_MCSPI_CHSTAT_RXS)) {
@@ -725,64 +779,38 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,
return 0;
}
-static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_rx_completion);
-
- /* We must disable the DMA RX request */
- omap2_mcspi_set_dma_req(spi, 1, 0);
-}
-
-static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
-{
- struct spi_device *spi = data;
- struct omap2_mcspi *mcspi;
- struct omap2_mcspi_dma *mcspi_dma;
-
- mcspi = spi_master_get_devdata(spi->master);
- mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
-
- complete(&mcspi_dma->dma_tx_completion);
-
- /* We must disable the DMA TX request */
- omap2_mcspi_set_dma_req(spi, 0, 0);
-}
-
static int omap2_mcspi_request_dma(struct spi_device *spi)
{
struct spi_master *master = spi->master;
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
+ dma_cap_mask_t mask;
+ unsigned sig;
mcspi = spi_master_get_devdata(master);
mcspi_dma = mcspi->dma_channels + spi->chip_select;
- if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
- omap2_mcspi_dma_rx_callback, spi,
- &mcspi_dma->dma_rx_channel)) {
- dev_err(&spi->dev, "no RX DMA channel for McSPI\n");
+ init_completion(&mcspi_dma->dma_rx_completion);
+ init_completion(&mcspi_dma->dma_tx_completion);
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ sig = mcspi_dma->dma_rx_sync_dev;
+ mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_rx) {
+ dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");
return -EAGAIN;
}
- if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
- omap2_mcspi_dma_tx_callback, spi,
- &mcspi_dma->dma_tx_channel)) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
- dev_err(&spi->dev, "no TX DMA channel for McSPI\n");
+ sig = mcspi_dma->dma_tx_sync_dev;
+ mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
+ if (!mcspi_dma->dma_tx) {
+ dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n");
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
return -EAGAIN;
}
- init_completion(&mcspi_dma->dma_rx_completion);
- init_completion(&mcspi_dma->dma_tx_completion);
-
return 0;
}
@@ -814,8 +842,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)
list_add_tail(&cs->node, &ctx->cs);
}
- if (mcspi_dma->dma_rx_channel == -1
- || mcspi_dma->dma_tx_channel == -1) {
+ if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {
ret = omap2_mcspi_request_dma(spi);
if (ret < 0)
return ret;
@@ -850,13 +877,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
if (spi->chip_select < spi->master->num_chipselect) {
mcspi_dma = &mcspi->dma_channels[spi->chip_select];
- if (mcspi_dma->dma_rx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_rx_channel);
- mcspi_dma->dma_rx_channel = -1;
+ if (mcspi_dma->dma_rx) {
+ dma_release_channel(mcspi_dma->dma_rx);
+ mcspi_dma->dma_rx = NULL;
}
- if (mcspi_dma->dma_tx_channel != -1) {
- omap_free_dma(mcspi_dma->dma_tx_channel);
- mcspi_dma->dma_tx_channel = -1;
+ if (mcspi_dma->dma_tx) {
+ dma_release_channel(mcspi_dma->dma_tx);
+ mcspi_dma->dma_tx = NULL;
}
}
}
@@ -1176,7 +1203,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_rx_channel = -1;
mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
sprintf(dma_ch_name, "tx%d", i);
dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
@@ -1187,7 +1213,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
break;
}
- mcspi->dma_channels[i].dma_tx_channel = -1;
mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
}
@@ -1203,18 +1228,16 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
status = spi_register_master(master);
if (status < 0)
- goto err_spi_register;
+ goto disable_pm;
return status;
-err_spi_register:
- spi_master_put(master);
disable_pm:
pm_runtime_disable(&pdev->dev);
dma_chnl_free:
kfree(mcspi->dma_channels);
free_master:
- kfree(master);
+ spi_master_put(master);
platform_set_drvdata(pdev, NULL);
return status;
}
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index aab518ec2bbc..6abbe23c39b4 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -2053,7 +2053,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
- pm_runtime_enable(dev);
pm_runtime_resume(dev);
pl022->clk = clk_get(&adev->dev, NULL);
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 646a7657fe62..d1c8441f638c 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -826,7 +826,7 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
struct spi_device *spi)
{
struct s3c64xx_spi_csinfo *cs;
- struct device_node *slave_np, *data_np;
+ struct device_node *slave_np, *data_np = NULL;
u32 fb_delay = 0;
slave_np = spi->dev.of_node;
@@ -1479,40 +1479,40 @@ static const struct dev_pm_ops s3c64xx_spi_pm = {
s3c64xx_spi_runtime_resume, NULL)
};
-struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
+static struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
.fifo_lvl_mask = { 0x7f },
.rx_lvl_offset = 13,
.tx_st_done = 21,
.high_speed = true,
};
-struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
+static struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
.fifo_lvl_mask = { 0x7f, 0x7F },
.rx_lvl_offset = 13,
.tx_st_done = 21,
};
-struct s3c64xx_spi_port_config s5p64x0_spi_port_config = {
+static struct s3c64xx_spi_port_config s5p64x0_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
};
-struct s3c64xx_spi_port_config s5pc100_spi_port_config = {
+static struct s3c64xx_spi_port_config s5pc100_spi_port_config = {
.fifo_lvl_mask = { 0x7f, 0x7F },
.rx_lvl_offset = 13,
.tx_st_done = 21,
.high_speed = true,
};
-struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
+static struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
.high_speed = true,
};
-struct s3c64xx_spi_port_config exynos4_spi_port_config = {
+static struct s3c64xx_spi_port_config exynos4_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h
index d0cafd637199..f2ffd963f1c3 100644
--- a/drivers/staging/android/android_alarm.h
+++ b/drivers/staging/android/android_alarm.h
@@ -51,10 +51,12 @@ enum android_alarm_return_flags {
#define ANDROID_ALARM_WAIT _IO('a', 1)
#define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size)
+#define ALARM_IOR(c, type, size) _IOR('a', (c) | ((type) << 4), size)
+
/* Set alarm */
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
#define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
-#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
+#define ANDROID_ALARM_GET_TIME(type) ALARM_IOR(4, type, struct timespec)
#define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec)
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
#define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
diff --git a/drivers/staging/bcm/Misc.c b/drivers/staging/bcm/Misc.c
index 9a60d4cd2184..f545716c666d 100644
--- a/drivers/staging/bcm/Misc.c
+++ b/drivers/staging/bcm/Misc.c
@@ -157,12 +157,7 @@ static int create_worker_threads(struct bcm_mini_adapter *psAdapter)
static struct file *open_firmware_file(struct bcm_mini_adapter *Adapter, const char *path)
{
- struct file *flp = NULL;
- mm_segment_t oldfs;
- oldfs = get_fs();
- set_fs(get_ds());
- flp = filp_open(path, O_RDONLY, S_IRWXU);
- set_fs(oldfs);
+ struct file *flp = filp_open(path, O_RDONLY, S_IRWXU);
if (IS_ERR(flp)) {
pr_err(DRV_NAME "Unable To Open File %s, err %ld", path, PTR_ERR(flp));
flp = NULL;
@@ -183,14 +178,12 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
{
int errorno = 0;
struct file *flp = NULL;
- mm_segment_t oldfs;
struct timeval tv = {0};
flp = open_firmware_file(Adapter, path);
if (!flp) {
- errorno = -ENOENT;
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Unable to Open %s\n", path);
- goto exit_download;
+ return -ENOENT;
}
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Opened file is = %s and length =0x%lx to be downloaded at =0x%x", path, (unsigned long)flp->f_dentry->d_inode->i_size, loc);
do_gettimeofday(&tv);
@@ -201,10 +194,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
errorno = -EIO;
goto exit_download;
}
- oldfs = get_fs();
- set_fs(get_ds());
vfs_llseek(flp, 0, 0);
- set_fs(oldfs);
if (Adapter->bcm_file_readback_from_chip(Adapter->pvInterfaceAdapter, flp, loc)) {
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Failed to read back firmware!");
errorno = -EIO;
@@ -212,12 +202,7 @@ static int BcmFileDownload(struct bcm_mini_adapter *Adapter, const char *path, u
}
exit_download:
- oldfs = get_fs();
- set_fs(get_ds());
- if (flp && !(IS_ERR(flp)))
- filp_close(flp, current->files);
- set_fs(oldfs);
-
+ filp_close(flp, NULL);
return errorno;
}
@@ -1056,10 +1041,8 @@ OUT:
static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
{
struct file *flp = NULL;
- mm_segment_t oldfs = {0};
char *buff;
int len = 0;
- loff_t pos = 0;
buff = kmalloc(BUFFER_1K, GFP_KERNEL);
if (!buff)
@@ -1079,20 +1062,16 @@ static int bcm_parse_target_params(struct bcm_mini_adapter *Adapter)
Adapter->pstargetparams = NULL;
return -ENOENT;
}
- oldfs = get_fs();
- set_fs(get_ds());
- len = vfs_read(flp, (void __user __force *)buff, BUFFER_1K, &pos);
- set_fs(oldfs);
+ len = kernel_read(flp, 0, buff, BUFFER_1K);
+ filp_close(flp, NULL);
if (len != sizeof(STARGETPARAMS)) {
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_INITEXIT, MP_INIT, DBG_LVL_ALL, "Mismatch in Target Param Structure!\n");
kfree(buff);
kfree(Adapter->pstargetparams);
Adapter->pstargetparams = NULL;
- filp_close(flp, current->files);
return -ENOENT;
}
- filp_close(flp, current->files);
/* Check for autolink in config params */
/*
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index c0fdb00783ed..2359151af7e1 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -168,7 +168,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->board_ptr = comedi_recognize(driv, it->board_name);
if (dev->board_ptr)
break;
- } else if (strcmp(driv->driver_name, it->board_name))
+ } else if (strcmp(driv->driver_name, it->board_name) == 0)
break;
module_put(driv->module);
}
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
index 31986608eaf1..6b4d0d68e637 100644
--- a/drivers/staging/comedi/drivers/adv_pci1710.c
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -1349,9 +1349,6 @@ static struct pci_dev *pci1710_find_pci_dev(struct comedi_device *dev,
}
if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
continue;
- if (pci_is_enabled(pcidev))
- continue;
-
if (strcmp(this_board->name, DRV_NAME) == 0) {
for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
if (pcidev->device == boardtypes[i].device_id) {
diff --git a/drivers/staging/comedi/drivers/adv_pci1723.c b/drivers/staging/comedi/drivers/adv_pci1723.c
index da5ee69d2c9d..dfde0f6328dd 100644
--- a/drivers/staging/comedi/drivers/adv_pci1723.c
+++ b/drivers/staging/comedi/drivers/adv_pci1723.c
@@ -301,8 +301,6 @@ static struct pci_dev *pci1723_find_pci_dev(struct comedi_device *dev,
}
if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
continue;
- if (pci_is_enabled(pcidev))
- continue;
return pcidev;
}
dev_err(dev->class_dev,
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
index 97f06dc8e48d..2d4cb7f638b2 100644
--- a/drivers/staging/comedi/drivers/adv_pci_dio.c
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -1064,8 +1064,6 @@ static struct pci_dev *pci_dio_find_pci_dev(struct comedi_device *dev,
slot != PCI_SLOT(pcidev->devfn))
continue;
}
- if (pci_is_enabled(pcidev))
- continue;
for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
if (boardtypes[i].vendor_id != pcidev->vendor)
continue;
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
index 6c81e377262c..cc8931fde839 100644
--- a/drivers/staging/comedi/drivers/amplc_dio200.c
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -1412,6 +1412,13 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in dio200_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via dio200_attach()
+ * has been removed.
+ */
+ pci_dev_get(pci_dev);
return dio200_pci_common_attach(dev, pci_dev);
}
diff --git a/drivers/staging/comedi/drivers/amplc_pc236.c b/drivers/staging/comedi/drivers/amplc_pc236.c
index aabba9886b7d..f50287903038 100644
--- a/drivers/staging/comedi/drivers/amplc_pc236.c
+++ b/drivers/staging/comedi/drivers/amplc_pc236.c
@@ -565,6 +565,13 @@ static int __devinit pc236_attach_pci(struct comedi_device *dev,
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in pc236_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via pc236_attach()
+ * has been removed.
+ */
+ pci_dev_get(pci_dev);
return pc236_pci_common_attach(dev, pci_dev);
}
diff --git a/drivers/staging/comedi/drivers/amplc_pc263.c b/drivers/staging/comedi/drivers/amplc_pc263.c
index 40ec1ffebba6..8191c4e28e0a 100644
--- a/drivers/staging/comedi/drivers/amplc_pc263.c
+++ b/drivers/staging/comedi/drivers/amplc_pc263.c
@@ -298,6 +298,13 @@ static int __devinit pc263_attach_pci(struct comedi_device *dev,
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in pc263_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via pc263_attach()
+ * has been removed.
+ */
+ pci_dev_get(pci_dev);
return pc263_pci_common_attach(dev, pci_dev);
}
diff --git a/drivers/staging/comedi/drivers/amplc_pci224.c b/drivers/staging/comedi/drivers/amplc_pci224.c
index 4e17f13e57f6..8bf109e7bb05 100644
--- a/drivers/staging/comedi/drivers/amplc_pci224.c
+++ b/drivers/staging/comedi/drivers/amplc_pci224.c
@@ -1503,6 +1503,13 @@ pci224_attach_pci(struct comedi_device *dev, struct pci_dev *pci_dev)
DRIVER_NAME ": BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in pci224_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via pci224_attach()
+ * has been removed.
+ */
+ pci_dev_get(pci_dev);
return pci224_attach_common(dev, pci_dev, NULL);
}
diff --git a/drivers/staging/comedi/drivers/amplc_pci230.c b/drivers/staging/comedi/drivers/amplc_pci230.c
index 1b67d0c61fa7..66e74bd12267 100644
--- a/drivers/staging/comedi/drivers/amplc_pci230.c
+++ b/drivers/staging/comedi/drivers/amplc_pci230.c
@@ -2925,6 +2925,13 @@ static int __devinit pci230_attach_pci(struct comedi_device *dev,
"amplc_pci230: BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in pci230_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via pci230_attach()
+ * has been removed.
+ */
+ pci_dev_get(pci_dev);
return pci230_attach_common(dev, pci_dev);
}
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
index ef28385c1482..cad559a1a730 100644
--- a/drivers/staging/comedi/drivers/daqboard2000.c
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -718,7 +718,8 @@ static struct pci_dev *daqboard2000_find_pci_dev(struct comedi_device *dev,
continue;
}
if (pcidev->vendor != PCI_VENDOR_ID_IOTECH ||
- pcidev->device != 0x0409)
+ pcidev->device != 0x0409 ||
+ pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
continue;
for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
@@ -739,6 +740,7 @@ static int daqboard2000_attach(struct comedi_device *dev,
{
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
void *aux_data;
unsigned int aux_len;
int result;
@@ -758,11 +760,12 @@ static int daqboard2000_attach(struct comedi_device *dev,
"failed to enable PCI device and request regions\n");
return -EIO;
}
- dev->iobase = pci_resource_start(pcidev, 2);
+ dev->iobase = 1; /* the "detach" needs this */
- devpriv->plx =
- ioremap(pci_resource_start(pcidev, 0), DAQBOARD2000_PLX_SIZE);
- devpriv->daq = ioremap(dev->iobase, DAQBOARD2000_DAQ_SIZE);
+ pci_base = pci_resource_start(pcidev, 0);
+ devpriv->plx = ioremap(pci_base, DAQBOARD2000_PLX_SIZE);
+ pci_base = pci_resource_start(pcidev, 2);
+ devpriv->daq = ioremap(pci_base, DAQBOARD2000_DAQ_SIZE);
if (!devpriv->plx || !devpriv->daq)
return -ENOMEM;
@@ -799,8 +802,6 @@ static int daqboard2000_attach(struct comedi_device *dev,
printk("Interrupt after is: %x\n", interrupt);
*/
- dev->iobase = (unsigned long)devpriv->daq;
-
dev->board_name = this_board->name;
s = dev->subdevices + 0;
@@ -824,7 +825,7 @@ static int daqboard2000_attach(struct comedi_device *dev,
s = dev->subdevices + 2;
result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
- (unsigned long)(dev->iobase + 0x40));
+ (unsigned long)(devpriv->daq + 0x40));
out:
return result;
diff --git a/drivers/staging/comedi/drivers/das08.c b/drivers/staging/comedi/drivers/das08.c
index 874e02e47668..67a914a10b55 100644
--- a/drivers/staging/comedi/drivers/das08.c
+++ b/drivers/staging/comedi/drivers/das08.c
@@ -378,7 +378,7 @@ das08jr_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
int chan;
lsb = data[0] & 0xff;
- msb = (data[0] >> 8) & 0xf;
+ msb = (data[0] >> 8) & 0xff;
chan = CR_CHAN(insn->chanspec);
@@ -623,7 +623,7 @@ static const struct das08_board_struct das08_boards[] = {
.ai = das08_ai_rinsn,
.ai_nbits = 16,
.ai_pg = das08_pg_none,
- .ai_encoding = das08_encode12,
+ .ai_encoding = das08_encode16,
.ao = das08jr_ao_winsn,
.ao_nbits = 16,
.di = das08jr_di_rbits,
@@ -922,6 +922,13 @@ das08_attach_pci(struct comedi_device *dev, struct pci_dev *pdev)
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
+ /*
+ * Need to 'get' the PCI device to match the 'put' in das08_detach().
+ * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
+ * support for manual attachment of PCI devices via das08_attach()
+ * has been removed.
+ */
+ pci_dev_get(pdev);
return das08_pci_attach_common(dev, pdev);
}
diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c
index a6fe6c9be87e..3476cda0fff0 100644
--- a/drivers/staging/comedi/drivers/dt3000.c
+++ b/drivers/staging/comedi/drivers/dt3000.c
@@ -804,6 +804,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
int ret = 0;
dev_dbg(dev->class_dev, "dt3000:\n");
@@ -820,9 +821,10 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
ret = comedi_pci_enable(pcidev, "dt3000");
if (ret < 0)
return ret;
+ dev->iobase = 1; /* the "detach" needs this */
- dev->iobase = pci_resource_start(pcidev, 0);
- devpriv->io_addr = ioremap(dev->iobase, DT3000_SIZE);
+ pci_base = pci_resource_start(pcidev, 0);
+ devpriv->io_addr = ioremap(pci_base, DT3000_SIZE);
if (!devpriv->io_addr)
return -ENOMEM;
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c
index 112fdc3e9c69..5aa8be1e7b92 100644
--- a/drivers/staging/comedi/drivers/rtd520.c
+++ b/drivers/staging/comedi/drivers/rtd520.c
@@ -1619,9 +1619,8 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
struct rtdPrivate *devpriv;
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
int ret;
- resource_size_t physLas1; /* data area */
- resource_size_t physLcfg; /* PLX9080 */
#ifdef USE_DMA
int index;
#endif
@@ -1655,20 +1654,15 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
return ret;
}
-
- /*
- * Initialize base addresses
- */
- /* Get the physical address from PCI config */
- dev->iobase = pci_resource_start(pcidev, LAS0_PCIINDEX);
- physLas1 = pci_resource_start(pcidev, LAS1_PCIINDEX);
- physLcfg = pci_resource_start(pcidev, LCFG_PCIINDEX);
- /* Now have the kernel map this into memory */
- /* ASSUME page aligned */
- devpriv->las0 = ioremap_nocache(dev->iobase, LAS0_PCISIZE);
- devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
- devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
-
+ dev->iobase = 1; /* the "detach" needs this */
+
+ /* Initialize the base addresses */
+ pci_base = pci_resource_start(pcidev, LAS0_PCIINDEX);
+ devpriv->las0 = ioremap_nocache(pci_base, LAS0_PCISIZE);
+ pci_base = pci_resource_start(pcidev, LAS1_PCIINDEX);
+ devpriv->las1 = ioremap_nocache(pci_base, LAS1_PCISIZE);
+ pci_base = pci_resource_start(pcidev, LCFG_PCIINDEX);
+ devpriv->lcfg = ioremap_nocache(pci_base, LCFG_PCISIZE);
if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
return -ENOMEM;
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index 848c7ec06976..11ee83681da7 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -102,6 +102,7 @@ sampling rate. If you sample two channels you get 4kHz and so on.
#define BULK_TIMEOUT 1000
/* constants for "firmware" upload and download */
+#define FIRMWARE "usbdux_firmware.bin"
#define USBDUXSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -2791,7 +2792,7 @@ static int usbdux_usb_probe(struct usb_interface *uinterf,
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbdux_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxsub + index,
@@ -2850,3 +2851,4 @@ module_comedi_usb_driver(usbdux_driver, usbdux_usb_driver);
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index d9911588c10a..8eb41257c6ce 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -57,6 +57,7 @@
/*
* constants for "firmware" upload and download
*/
+#define FIRMWARE "usbduxfast_firmware.bin"
#define USBDUXFASTSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -1706,7 +1707,7 @@ static int usbduxfast_usb_probe(struct usb_interface *uinterf,
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbduxfast_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxfastsub + index,
@@ -1774,3 +1775,4 @@ module_comedi_usb_driver(usbduxfast_driver, usbduxfast_usb_driver);
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c
index 543e604791e2..f54ab8c2fcfd 100644
--- a/drivers/staging/comedi/drivers/usbduxsigma.c
+++ b/drivers/staging/comedi/drivers/usbduxsigma.c
@@ -63,6 +63,7 @@ Status: testing
#define BULK_TIMEOUT 1000
/* constants for "firmware" upload and download */
+#define FIRMWARE "usbduxsigma_firmware.bin"
#define USBDUXSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -2780,7 +2781,7 @@ static int usbduxsigma_usb_probe(struct usb_interface *uinterf,
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbduxsigma_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxsub + index,
@@ -2845,3 +2846,4 @@ module_comedi_usb_driver(usbduxsigma_driver, usbduxsigma_usb_driver);
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("Stirling/ITL USB-DUX SIGMA -- Bernd.Porr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/csr/Kconfig b/drivers/staging/csr/Kconfig
index cee8d48d2af9..ad2a1096e920 100644
--- a/drivers/staging/csr/Kconfig
+++ b/drivers/staging/csr/Kconfig
@@ -1,6 +1,6 @@
config CSR_WIFI
tristate "CSR wireless driver"
- depends on MMC && CFG80211_WEXT
+ depends on MMC && CFG80211_WEXT && INET
select WIRELESS_EXT
select WEXT_PRIV
help
diff --git a/drivers/staging/gdm72xx/sdio_boot.c b/drivers/staging/gdm72xx/sdio_boot.c
index 760efee23d4a..65624bca8b3a 100644
--- a/drivers/staging/gdm72xx/sdio_boot.c
+++ b/drivers/staging/gdm72xx/sdio_boot.c
@@ -66,9 +66,8 @@ static int download_image(struct sdio_func *func, char *img_name)
return -ENOENT;
}
- if (filp->f_dentry)
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
printk(KERN_ERR "Invalid file type: %s\n", img_name);
ret = -EINVAL;
goto out;
@@ -123,7 +122,7 @@ static int download_image(struct sdio_func *func, char *img_name)
pno++;
}
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
return ret;
}
diff --git a/drivers/staging/gdm72xx/usb_boot.c b/drivers/staging/gdm72xx/usb_boot.c
index fef290c38db6..e3dbd5a552ca 100644
--- a/drivers/staging/gdm72xx/usb_boot.c
+++ b/drivers/staging/gdm72xx/usb_boot.c
@@ -173,14 +173,12 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0);
if (IS_ERR(filp)) {
printk(KERN_ERR "Can't find %s.\n", img_name);
- set_fs(fs);
ret = PTR_ERR(filp);
goto restore_fs;
}
- if (filp->f_dentry)
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
printk(KERN_ERR "Invalid file type: %s\n", img_name);
ret = -EINVAL;
goto out;
@@ -262,7 +260,7 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
ret = -EINVAL;
}
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
restore_fs:
set_fs(fs);
@@ -322,13 +320,11 @@ static int em_download_image(struct usb_device *usbdev, char *path,
goto restore_fs;
}
- if (filp->f_dentry) {
- inode = filp->f_dentry->d_inode;
- if (!inode || !S_ISREG(inode->i_mode)) {
- printk(KERN_ERR "Invalid file type: %s\n", path);
- ret = -EINVAL;
- goto out;
- }
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISREG(inode->i_mode)) {
+ printk(KERN_ERR "Invalid file type: %s\n", path);
+ ret = -EINVAL;
+ goto out;
}
buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
@@ -364,7 +360,7 @@ static int em_download_image(struct usb_device *usbdev, char *path,
goto out;
out:
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
restore_fs:
set_fs(fs);
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index 18d108fd967a..f3da59063ed2 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -121,8 +121,10 @@ static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev,
if (rx_array == NULL)
return -ENOMEM;
ret = lis3l02dq_read_all(indio_dev, rx_array);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(rx_array);
return ret;
+ }
for (i = 0; i < scan_count; i++)
data[i] = combine_8_to_16(rx_array[i*4+1],
rx_array[i*4+3]);
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 22c3923d55eb..19a064d649e3 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -647,6 +647,8 @@ static ssize_t ad7192_write_frequency(struct device *dev,
ret = strict_strtoul(buf, 10, &lval);
if (ret)
return ret;
+ if (lval == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
if (iio_buffer_enabled(indio_dev)) {
@@ -754,7 +756,7 @@ static ssize_t ad7192_set(struct device *dev,
else
st->mode &= ~AD7192_MODE_ACX;
- ad7192_write_reg(st, AD7192_REG_GPOCON, 3, st->mode);
+ ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode);
break;
default:
ret = -EINVAL;
@@ -798,6 +800,11 @@ static const struct attribute_group ad7195_attribute_group = {
.attrs = ad7195_attributes,
};
+static unsigned int ad7192_get_temp_scale(bool unipolar)
+{
+ return unipolar ? 2815 * 2 : 2815;
+}
+
static int ad7192_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
@@ -824,19 +831,6 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
*val = (smpl >> chan->scan_type.shift) &
((1 << (chan->scan_type.realbits)) - 1);
- switch (chan->type) {
- case IIO_VOLTAGE:
- if (!unipolar)
- *val -= (1 << (chan->scan_type.realbits - 1));
- break;
- case IIO_TEMP:
- *val -= 0x800000;
- *val /= 2815; /* temp Kelvin */
- *val -= 273; /* temp Celsius */
- break;
- default:
- return -EINVAL;
- }
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -848,11 +842,21 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT_PLUS_NANO;
case IIO_TEMP:
- *val = 1000;
- return IIO_VAL_INT;
+ *val = 0;
+ *val2 = 1000000000 / ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP)
+ *val -= 273 * ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT;
}
return -EINVAL;
@@ -890,7 +894,7 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
}
ret = 0;
}
-
+ break;
default:
ret = -EINVAL;
}
@@ -942,20 +946,22 @@ static const struct iio_info ad7195_info = {
.channel = _chan, \
.channel2 = _chan2, \
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
#define AD7192_CHAN(_chan, _address, _si) \
{ .type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
#define AD7192_CHAN_TEMP(_chan, _address, _si) \
{ .type = IIO_TEMP, \
@@ -965,7 +971,7 @@ static const struct iio_info ad7195_info = {
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
static struct iio_chan_spec ad7192_channels[] = {
AD7192_CHAN_DIFF(1, 2, NULL, AD7192_CH_AIN1P_AIN2M, 0),
diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c
index fd1d855ff57a..506016f01593 100644
--- a/drivers/staging/iio/adc/ad7298_ring.c
+++ b/drivers/staging/iio/adc/ad7298_ring.c
@@ -76,7 +76,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7298_state *st = iio_priv(indio_dev);
struct iio_buffer *ring = indio_dev->buffer;
- s64 time_ns;
+ s64 time_ns = 0;
__u16 buf[16];
int b_sent, i;
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
index 1ece2ac8de56..19ee49c95de4 100644
--- a/drivers/staging/iio/adc/ad7780.c
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -131,9 +131,10 @@ static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
.indexed = 1,
.channel = 0,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_type = {
- .sign = 's',
+ .sign = 'u',
.realbits = 24,
.storagebits = 32,
.shift = 8,
@@ -146,9 +147,10 @@ static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
.indexed = 1,
.channel = 0,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_type = {
- .sign = 's',
+ .sign = 'u',
.realbits = 20,
.storagebits = 32,
.shift = 12,
diff --git a/drivers/staging/iio/adc/ad7793.c b/drivers/staging/iio/adc/ad7793.c
index 76fdd7145fc5..112e2b7b5bc4 100644
--- a/drivers/staging/iio/adc/ad7793.c
+++ b/drivers/staging/iio/adc/ad7793.c
@@ -563,8 +563,9 @@ static ssize_t ad7793_show_scale_available(struct device *dev,
return len;
}
-static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, in-in_scale_available,
- S_IRUGO, ad7793_show_scale_available, NULL, 0);
+static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available,
+ in_voltage-voltage_scale_available, S_IRUGO,
+ ad7793_show_scale_available, NULL, 0);
static struct attribute *ad7793_attributes[] = {
&iio_dev_attr_sampling_frequency.dev_attr.attr,
@@ -604,9 +605,6 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
*val = (smpl >> chan->scan_type.shift) &
((1 << (chan->scan_type.realbits)) - 1);
- if (!unipolar)
- *val -= (1 << (chan->scan_type.realbits - 1));
-
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -620,25 +618,38 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_NANO;
} else {
/* 1170mV / 2^23 * 6 */
- scale_uv = (1170ULL * 100000000ULL * 6ULL)
- >> (chan->scan_type.realbits -
- (unipolar ? 0 : 1));
+ scale_uv = (1170ULL * 100000000ULL * 6ULL);
}
break;
case IIO_TEMP:
- /* Always uses unity gain and internal ref */
- scale_uv = (2500ULL * 100000000ULL)
- >> (chan->scan_type.realbits -
- (unipolar ? 0 : 1));
+ /* 1170mV / 0.81 mV/C / 2^23 */
+ scale_uv = 1444444444444ULL;
break;
default:
return -EINVAL;
}
- *val2 = do_div(scale_uv, 100000000) * 10;
- *val = scale_uv;
-
+ scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1));
+ *val = 0;
+ *val2 = scale_uv;
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP) {
+ unsigned long long offset;
+ unsigned int shift;
+
+ shift = chan->scan_type.realbits - (unipolar ? 0 : 1);
+ offset = 273ULL << shift;
+ do_div(offset, 1444);
+ *val -= offset;
+ }
+ return IIO_VAL_INT;
}
return -EINVAL;
}
@@ -676,7 +687,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
}
ret = 0;
}
-
+ break;
default:
ret = -EINVAL;
}
@@ -720,9 +731,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 0,
.address = AD7793_CH_AIN1P_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 0,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[1] = {
.type = IIO_VOLTAGE,
@@ -732,9 +744,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 1,
.address = AD7793_CH_AIN2P_AIN2M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 1,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[2] = {
.type = IIO_VOLTAGE,
@@ -744,9 +757,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 2,
.address = AD7793_CH_AIN3P_AIN3M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 2,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[3] = {
.type = IIO_VOLTAGE,
@@ -757,9 +771,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 2,
.address = AD7793_CH_AIN1M_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 3,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[4] = {
.type = IIO_TEMP,
@@ -769,7 +784,7 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
.scan_index = 4,
- .scan_type = IIO_ST('s', 24, 32, 0),
+ .scan_type = IIO_ST('u', 24, 32, 0),
},
.channel[5] = {
.type = IIO_VOLTAGE,
@@ -778,9 +793,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel = 4,
.address = AD7793_CH_AVDD_MONITOR,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 5,
- .scan_type = IIO_ST('s', 24, 32, 0),
+ .scan_type = IIO_ST('u', 24, 32, 0),
},
.channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
},
@@ -793,9 +809,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 0,
.address = AD7793_CH_AIN1P_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 0,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[1] = {
.type = IIO_VOLTAGE,
@@ -805,9 +822,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 1,
.address = AD7793_CH_AIN2P_AIN2M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 1,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[2] = {
.type = IIO_VOLTAGE,
@@ -817,9 +835,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 2,
.address = AD7793_CH_AIN3P_AIN3M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 2,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[3] = {
.type = IIO_VOLTAGE,
@@ -830,9 +849,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel2 = 2,
.address = AD7793_CH_AIN1M_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 3,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[4] = {
.type = IIO_TEMP,
@@ -842,7 +862,7 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
.scan_index = 4,
- .scan_type = IIO_ST('s', 16, 32, 0),
+ .scan_type = IIO_ST('u', 16, 32, 0),
},
.channel[5] = {
.type = IIO_VOLTAGE,
@@ -851,9 +871,10 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
.channel = 4,
.address = AD7793_CH_AVDD_MONITOR,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 5,
- .scan_type = IIO_ST('s', 16, 32, 0),
+ .scan_type = IIO_ST('u', 16, 32, 0),
},
.channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
},
@@ -901,7 +922,7 @@ static int __devinit ad7793_probe(struct spi_device *spi)
else if (voltage_uv)
st->int_vref_mv = voltage_uv / 1000;
else
- st->int_vref_mv = 2500; /* Build-in ref */
+ st->int_vref_mv = 1170; /* Build-in ref */
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c
index 93aa431287ac..eb8e9d69efd3 100644
--- a/drivers/staging/iio/gyro/adis16260_core.c
+++ b/drivers/staging/iio/gyro/adis16260_core.c
@@ -195,6 +195,8 @@ static ssize_t adis16260_write_frequency(struct device *dev,
ret = strict_strtol(buf, 10, &val);
if (ret)
return ret;
+ if (val == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
if (spi_get_device_id(st->us)) {
diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c
index 1f4c17779b5a..a618327e06ed 100644
--- a/drivers/staging/iio/imu/adis16400_core.c
+++ b/drivers/staging/iio/imu/adis16400_core.c
@@ -234,6 +234,8 @@ static ssize_t adis16400_write_frequency(struct device *dev,
ret = strict_strtol(buf, 10, &val);
if (ret)
return ret;
+ if (val == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c
index f04ece7fbc2f..3ccff189f258 100644
--- a/drivers/staging/iio/meter/ade7753.c
+++ b/drivers/staging/iio/meter/ade7753.c
@@ -425,6 +425,8 @@ static ssize_t ade7753_write_frequency(struct device *dev,
ret = strict_strtol(buf, 10, &val);
if (ret)
return ret;
+ if (val == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c
index 6cee28a5e877..abb1e9c8d094 100644
--- a/drivers/staging/iio/meter/ade7754.c
+++ b/drivers/staging/iio/meter/ade7754.c
@@ -445,6 +445,8 @@ static ssize_t ade7754_write_frequency(struct device *dev,
ret = strict_strtol(buf, 10, &val);
if (ret)
return ret;
+ if (val == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c
index b3f7e0fa9612..eb0a2a98f388 100644
--- a/drivers/staging/iio/meter/ade7759.c
+++ b/drivers/staging/iio/meter/ade7759.c
@@ -385,6 +385,8 @@ static ssize_t ade7759_write_frequency(struct device *dev,
ret = strict_strtol(buf, 10, &val);
if (ret)
return ret;
+ if (val == 0)
+ return -EINVAL;
mutex_lock(&indio_dev->mlock);
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 4f4b7d6281a7..427218b8b10f 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -25,8 +25,6 @@ source "drivers/staging/media/cxd2099/Kconfig"
source "drivers/staging/media/dt3155v4l/Kconfig"
-source "drivers/staging/media/easycap/Kconfig"
-
source "drivers/staging/media/go7007/Kconfig"
source "drivers/staging/media/solo6x10/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index c69124cdb0d3..aec6eb963940 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,6 +1,5 @@
obj-$(CONFIG_DVB_AS102) += as102/
obj-$(CONFIG_DVB_CXD2099) += cxd2099/
-obj-$(CONFIG_EASYCAP) += easycap/
obj-$(CONFIG_LIRC_STAGING) += lirc/
obj-$(CONFIG_SOLO6X10) += solo6x10/
obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/
diff --git a/drivers/staging/media/as102/Makefile b/drivers/staging/media/as102/Makefile
index 1bca43e847c7..d8dfb757f1e2 100644
--- a/drivers/staging/media/as102/Makefile
+++ b/drivers/staging/media/as102/Makefile
@@ -3,4 +3,4 @@ dvb-as102-objs := as102_drv.o as102_fw.o as10x_cmd.o as10x_cmd_stream.o \
obj-$(CONFIG_DVB_AS102) += dvb-as102.o
-EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb-core
diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile
index 64cfc77be357..b2905e65057c 100644
--- a/drivers/staging/media/cxd2099/Makefile
+++ b/drivers/staging/media/cxd2099/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_DVB_CXD2099) += cxd2099.o
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/dvb/frontends/
-ccflags-y += -Idrivers/media/common/tuners/
+ccflags-y += -Idrivers/media/dvb-core/
+ccflags-y += -Idrivers/media/dvb-frontends/
+ccflags-y += -Idrivers/media/tuners/
diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c
index 1c04185bcfd7..0ff19724992f 100644
--- a/drivers/staging/media/cxd2099/cxd2099.c
+++ b/drivers/staging/media/cxd2099/cxd2099.c
@@ -683,27 +683,26 @@ struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg,
void *priv,
struct i2c_adapter *i2c)
{
- struct cxd *ci = 0;
+ struct cxd *ci;
u8 val;
if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) {
printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr);
- return 0;
+ return NULL;
}
- ci = kmalloc(sizeof(struct cxd), GFP_KERNEL);
+ ci = kzalloc(sizeof(struct cxd), GFP_KERNEL);
if (!ci)
- return 0;
- memset(ci, 0, sizeof(*ci));
+ return NULL;
mutex_init(&ci->lock);
- memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg));
+ ci->cfg = *cfg;
ci->i2c = i2c;
ci->lastaddress = 0xff;
ci->clk_reg_b = 0x4a;
ci->clk_reg_f = 0x1b;
- memcpy(&ci->en, &en_templ, sizeof(en_templ));
+ ci->en = en_templ;
ci->en.data = ci;
init(ci);
printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr);
diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c
index ebe5a27c06f5..2e7b711c8501 100644
--- a/drivers/staging/media/dt3155v4l/dt3155v4l.c
+++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c
@@ -381,6 +381,8 @@ dt3155_open(struct file *filp)
int ret = 0;
struct dt3155_priv *pd = video_drvdata(filp);
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
if (!pd->users) {
pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL);
if (!pd->q) {
@@ -411,6 +413,7 @@ err_request_irq:
kfree(pd->q);
pd->q = NULL;
err_alloc_queue:
+ mutex_unlock(&pd->mux);
return ret;
}
@@ -419,6 +422,7 @@ dt3155_release(struct file *filp)
{
struct dt3155_priv *pd = video_drvdata(filp);
+ mutex_lock(&pd->mux);
pd->users--;
BUG_ON(pd->users < 0);
if (!pd->users) {
@@ -429,6 +433,7 @@ dt3155_release(struct file *filp)
kfree(pd->q);
pd->q = NULL;
}
+ mutex_unlock(&pd->mux);
return 0;
}
@@ -436,24 +441,38 @@ static ssize_t
dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff)
{
struct dt3155_priv *pd = video_drvdata(filp);
+ ssize_t res;
- return vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
+ res = vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK);
+ mutex_unlock(&pd->mux);
+ return res;
}
static unsigned int
dt3155_poll(struct file *filp, struct poll_table_struct *polltbl)
{
struct dt3155_priv *pd = video_drvdata(filp);
+ unsigned int res;
- return vb2_poll(pd->q, filp, polltbl);
+ mutex_lock(&pd->mux);
+ res = vb2_poll(pd->q, filp, polltbl);
+ mutex_unlock(&pd->mux);
+ return res;
}
static int
dt3155_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct dt3155_priv *pd = video_drvdata(filp);
+ int res;
- return vb2_mmap(pd->q, vma);
+ if (mutex_lock_interruptible(&pd->mux))
+ return -ERESTARTSYS;
+ res = vb2_mmap(pd->q, vma);
+ mutex_unlock(&pd->mux);
+ return res;
}
static const struct v4l2_file_operations dt3155_fops = {
@@ -898,10 +917,6 @@ dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id)
INIT_LIST_HEAD(&pd->dmaq);
mutex_init(&pd->mux);
pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */
- /* Locking in file operations other than ioctl should be done
- by the driver, not the V4L2 core.
- This driver needs auditing so that this flag can be removed. */
- set_bit(V4L2_FL_LOCK_ALL_FOPS, &pd->vdev->flags);
spin_lock_init(&pd->lock);
pd->csr2 = csr2_init;
pd->config = config_init;
diff --git a/drivers/staging/media/easycap/Kconfig b/drivers/staging/media/easycap/Kconfig
deleted file mode 100644
index a425a6f9cdca..000000000000
--- a/drivers/staging/media/easycap/Kconfig
+++ /dev/null
@@ -1,30 +0,0 @@
-config EASYCAP
- tristate "EasyCAP USB ID 05e1:0408 support"
- depends on USB && VIDEO_DEV && SND
- select SND_PCM
-
- ---help---
- This is an integrated audio/video driver for EasyCAP cards with
- USB ID 05e1:0408. It supports two hardware variants:
-
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
- having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
-
- * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
- 1, 2, 3, 4 and an unlabelled input cable for a microphone.
-
- To compile this driver as a module, choose M here: the
- module will be called easycap
-
-config EASYCAP_DEBUG
- bool "Enable EasyCAP driver debugging"
- depends on EASYCAP
-
- ---help---
- This option enables debug printouts
-
- To enable debug, pass the debug level to the debug module
- parameter:
-
- modprobe easycap debug=[0..9]
-
diff --git a/drivers/staging/media/easycap/Makefile b/drivers/staging/media/easycap/Makefile
deleted file mode 100644
index a34e75f59c18..000000000000
--- a/drivers/staging/media/easycap/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-easycap-objs := easycap_main.o
-easycap-objs += easycap_low.o
-easycap-objs += easycap_ioctl.o
-easycap-objs += easycap_settings.o
-easycap-objs += easycap_testcard.o
-easycap-objs += easycap_sound.o
-obj-$(CONFIG_EASYCAP) += easycap.o
-
-ccflags-y := -Wall
-
diff --git a/drivers/staging/media/easycap/README b/drivers/staging/media/easycap/README
deleted file mode 100644
index 796b032384bd..000000000000
--- a/drivers/staging/media/easycap/README
+++ /dev/null
@@ -1,141 +0,0 @@
-
- ***********************************************************
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 *
- * and *
- * EasyCAP002 4-Channel USB 2.0 DVR *
- ***********************************************************
- Mike Thomas <rmthomas@sciolus.org>
-
-
-
-SUPPORTED HARDWARE
-------------------
-
-This driver is intended for use with hardware having USB ID 05e1:0408.
-Two kinds of EasyCAP have this USB ID, namely:
-
- * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
- having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
-
- * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
- 1, 2, 3, 4 and an unlabelled input cable for a microphone.
-
-
-BUILD OPTIONS AND DEPENDENCIES
-------------------------------
-
-Unless EASYCAP_DEBUG is defined during compilation it will not be possible
-to select a debug level at the time of module installation.
-
-
-KNOWN RUNTIME ISSUES
---------------------
-
-(1) Intentionally, this driver will not stream material which is unambiguously
-identified by the hardware as copy-protected. Normal video output will be
-present for about a minute but will then freeze when this situation arises.
-
-(2) The controls for luminance, contrast, saturation, hue and volume may not
-always work properly.
-
-(3) Reduced-resolution S-Video seems to suffer from moire artefacts.
-
-
-INPUT NUMBERING
----------------
-
-For the EasyCAP with S-VIDEO input cable the driver regards a request for
-inputs numbered 0 or 1 as referring to CVBS and a request for input
-numbered 5 as referring to S-VIDEO.
-
-For the EasyCAP with four CVBS inputs the driver expects to be asked for
-any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is
-interpreted as input 1.
-
-
-MODULE PARAMETERS
------------------
-
-Three module parameters are defined:
-
-debug the easycap module is configured at diagnostic level n (0 to 9)
-gain audio gain level n (0 to 31, default is 16)
-bars whether to display testcard bars when incoming video signal is lost
- 0 => no, 1 => yes (default)
-
-
-SUPPORTED TV STANDARDS AND RESOLUTIONS
---------------------------------------
-
-The following TV standards are natively supported by the hardware and are
-usable as (for example) the "norm=" parameter in the mplayer command:
-
- PAL_BGHIN, NTSC_N_443,
- PAL_Nc, NTSC_N,
- SECAM, NTSC_M, NTSC_M_JP,
- PAL_60, NTSC_443,
- PAL_M.
-
-In addition, the driver offers "custom" pseudo-standards with a framerate
-which is 20% of the usual framerate. These pseudo-standards are named:
-
- PAL_BGHIN_SLOW, NTSC_N_443_SLOW,
- PAL_Nc_SLOW, NTSC_N_SLOW,
- SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW,
- PAL_60_SLOW, NTSC_443_SLOW,
- PAL_M_SLOW.
-
-
-The available picture sizes are:
-
- at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240;
- at 30 frames per second: 720x480, 640x480, 360x240, 320x240.
-
-
-WHAT'S TESTED AND WHAT'S NOT
-----------------------------
-
-This driver is known to work with mplayer, mencoder, tvtime, zoneminder,
-xawtv, gstreamer and sufficiently recent versions of vlc. An interface
-to ffmpeg is implemented, but serious audio-video synchronization problems
-remain.
-
-The driver is designed to support all the TV standards accepted by the
-hardware, but as yet it has actually been tested on only a few of these.
-
-I have been unable to test and calibrate the S-video input myself because I
-do not possess any equipment with S-video output.
-
-
-UDEV RULES
-----------
-
-In order that the special files /dev/easycap0 and /dev/easysnd1 are created
-with conveniently relaxed permissions when the EasyCAP is plugged in, a file
-is preferably to be provided in directory /etc/udev/rules.d with content:
-
-ACTION!="add|change", GOTO="easycap_rules_end"
-ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \
- MODE="0666", OWNER="root", GROUP="root"
-LABEL="easycap_rules_end"
-
-
-MODPROBE CONFIGURATION
-----------------------
-
-The easycap module is in competition with the module snd-usb-audio for the
-EasyCAP's audio channel, and its installation can be aided by providing a
-file in directory /etc/modprobe.d with content:
-
-options easycap gain=16 bars=1
-install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap
-
-
-ACKNOWLEGEMENTS AND REFERENCES
-------------------------------
-This driver makes use of information contained in the Syntek Semicon DC-1125
-Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/
-by Nicolas Vivien. Particularly useful has been a patch to the latter driver
-provided by Ivor Hewitt in January 2009. The NTSC implementation is taken
-from the work of Ben Trask.
-
diff --git a/drivers/staging/media/easycap/easycap.h b/drivers/staging/media/easycap/easycap.h
deleted file mode 100644
index a007e7442be8..000000000000
--- a/drivers/staging/media/easycap/easycap.h
+++ /dev/null
@@ -1,567 +0,0 @@
-/*****************************************************************************
-* *
-* easycap.h *
-* *
-*****************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THE FOLLOWING PARAMETERS ARE UNDEFINED:
- *
- * EASYCAP_DEBUG
- *
- * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER
- * OPTIONS.
- */
-/*---------------------------------------------------------------------------*/
-
-#ifndef __EASYCAP_H__
-#define __EASYCAP_H__
-
-/*---------------------------------------------------------------------------*/
-/*
- * THESE ARE NORMALLY DEFINED
- */
-/*---------------------------------------------------------------------------*/
-#define PATIENCE 500
-#define PERSEVERE
-/*---------------------------------------------------------------------------*/
-/*
- * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED:
- */
-/*---------------------------------------------------------------------------*/
-#undef EASYCAP_TESTCARD
-/*---------------------------------------------------------------------------*/
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/usb.h>
-#include <linux/uaccess.h>
-
-#include <linux/i2c.h>
-#include <linux/workqueue.h>
-#include <linux/poll.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/types.h>
-
-#include <linux/vmalloc.h>
-#include <linux/sound.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/initval.h>
-#include <sound/control.h>
-#include <media/v4l2-dev.h>
-#include <media/v4l2-device.h>
-#include <linux/videodev2.h>
-#include <linux/soundcard.h>
-
-/*---------------------------------------------------------------------------*/
-/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd
- *
- * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60
- * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO.
- *
- * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002
- * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4.
- */
-/*---------------------------------------------------------------------------*/
-#define USB_EASYCAP_VENDOR_ID 0x05e1
-#define USB_EASYCAP_PRODUCT_ID 0x0408
-
-#define EASYCAP_DRIVER_VERSION "0.9.01"
-#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60"
-
-#define DONGLE_MANY 8
-#define INPUT_MANY 6
-/*---------------------------------------------------------------------------*/
-/*
- * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE
- */
-/*---------------------------------------------------------------------------*/
-#define SAA_0A_DEFAULT 0x7F
-#define SAA_0B_DEFAULT 0x3F
-#define SAA_0C_DEFAULT 0x2F
-#define SAA_0D_DEFAULT 0x00
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO STREAMING PARAMETERS:
- * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT
- * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize.
- */
-/*---------------------------------------------------------------------------*/
-#define VIDEO_ISOC_BUFFER_MANY 16
-#define VIDEO_ISOC_ORDER 3
-#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER)
-#define USB_2_0_MAXPACKETSIZE 3072
-#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE)
-#error video_isoc_buffer[.] will not be big enough
-#endif
-#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY
-#define VIDEO_LOST_TOLERATE 50
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO BUFFERS
- */
-/*---------------------------------------------------------------------------*/
-#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE)
-#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE)
-#define FIELD_BUFFER_MANY 4
-#define FRAME_BUFFER_MANY 6
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO STREAMING PARAMETERS
- */
-/*---------------------------------------------------------------------------*/
-#define AUDIO_ISOC_BUFFER_MANY 16
-#define AUDIO_ISOC_ORDER 1
-#define AUDIO_ISOC_FRAMESPERDESC 32
-#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER)
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO BUFFERS
- */
-/*---------------------------------------------------------------------------*/
-#define AUDIO_FRAGMENT_MANY 32
-#define PAGES_PER_AUDIO_FRAGMENT 4
-/*---------------------------------------------------------------------------*/
-/*
- * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND,
- * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND.
- * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT
- * ONLY MUST THE PARAMETER
- * STANDARD_MANY
- * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE
- * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS
- * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN
- * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-#define PAL_BGHIN 0
-#define PAL_Nc 2
-#define SECAM 4
-#define NTSC_N 6
-#define NTSC_N_443 8
-#define NTSC_M 1
-#define NTSC_443 3
-#define NTSC_M_JP 5
-#define PAL_60 7
-#define PAL_M 9
-#define PAL_BGHIN_SLOW 10
-#define PAL_Nc_SLOW 12
-#define SECAM_SLOW 14
-#define NTSC_N_SLOW 16
-#define NTSC_N_443_SLOW 18
-#define NTSC_M_SLOW 11
-#define NTSC_443_SLOW 13
-#define NTSC_M_JP_SLOW 15
-#define PAL_60_SLOW 17
-#define PAL_M_SLOW 19
-#define STANDARD_MANY 20
-/*---------------------------------------------------------------------------*/
-/*
- * ENUMS
- */
-/*---------------------------------------------------------------------------*/
-enum {
- AT_720x576,
- AT_704x576,
- AT_640x480,
- AT_720x480,
- AT_360x288,
- AT_320x240,
- AT_360x240,
- RESOLUTION_MANY
-};
-enum {
- FMT_UYVY,
- FMT_YUY2,
- FMT_RGB24,
- FMT_RGB32,
- FMT_BGR24,
- FMT_BGR32,
- PIXELFORMAT_MANY
-};
-enum {
- FIELD_NONE,
- FIELD_INTERLACED,
- INTERLACE_MANY
-};
-#define SETTINGS_MANY (STANDARD_MANY * \
- RESOLUTION_MANY * \
- 2 * \
- PIXELFORMAT_MANY * \
- INTERLACE_MANY)
-/*---------------------------------------------------------------------------*/
-/*
- * STRUCTURE DEFINITIONS
- */
-/*---------------------------------------------------------------------------*/
-struct easycap_dongle {
- struct easycap *peasycap;
- struct mutex mutex_video;
- struct mutex mutex_audio;
-};
-/*---------------------------------------------------------------------------*/
-struct data_buffer {
- struct list_head list_head;
- void *pgo;
- void *pto;
- u16 kount;
- u16 input;
-};
-/*---------------------------------------------------------------------------*/
-struct data_urb {
- struct list_head list_head;
- struct urb *purb;
- int isbuf;
- int length;
-};
-/*---------------------------------------------------------------------------*/
-struct easycap_standard {
- u16 mask;
-struct v4l2_standard v4l2_standard;
-};
-struct easycap_format {
- u16 mask;
- char name[128];
-struct v4l2_format v4l2_format;
-};
-struct inputset {
- int input;
- int input_ok;
- int standard_offset;
- int standard_offset_ok;
- int format_offset;
- int format_offset_ok;
- int brightness;
- int brightness_ok;
- int contrast;
- int contrast_ok;
- int saturation;
- int saturation_ok;
- int hue;
- int hue_ok;
-};
-/*---------------------------------------------------------------------------*/
-/*
- * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256
- * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9
- * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9
- */
-/*---------------------------------------------------------------------------*/
-struct easycap {
- int isdongle;
- int minor;
-
- struct video_device video_device;
- struct v4l2_device v4l2_device;
-
- int status;
- unsigned int audio_pages_per_fragment;
- unsigned int audio_bytes_per_fragment;
- unsigned int audio_buffer_page_many;
-
-#define UPSAMPLE
-#ifdef UPSAMPLE
- s16 oldaudio;
-#endif /*UPSAMPLE*/
-
- int ilk;
- bool microphone;
-
- struct usb_device *pusb_device;
- struct usb_interface *pusb_interface;
-
- struct kref kref;
-
- int queued[FRAME_BUFFER_MANY];
- int done[FRAME_BUFFER_MANY];
-
- wait_queue_head_t wq_video;
- wait_queue_head_t wq_audio;
- wait_queue_head_t wq_trigger;
-
- int input;
- int polled;
- int standard_offset;
- int format_offset;
- struct inputset inputset[INPUT_MANY];
-
- bool ntsc;
- int fps;
- int usec;
- int tolerate;
- int skip;
- int skipped;
- int lost[INPUT_MANY];
- int merit[180];
-
- int video_interface;
- int video_altsetting_on;
- int video_altsetting_off;
- int video_endpointnumber;
- int video_isoc_maxframesize;
- int video_isoc_buffer_size;
- int video_isoc_framesperdesc;
-
- int video_isoc_streaming;
- int video_isoc_sequence;
- int video_idle;
- int video_eof;
- int video_junk;
-
- struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY];
- struct data_buffer field_buffer[FIELD_BUFFER_MANY]
- [(FIELD_BUFFER_SIZE/PAGE_SIZE)];
- struct data_buffer frame_buffer[FRAME_BUFFER_MANY]
- [(FRAME_BUFFER_SIZE/PAGE_SIZE)];
-
- struct list_head urb_video_head;
- struct list_head *purb_video_head;
-
- u8 cache[8];
- u8 *pcache;
- int video_mt;
- int audio_mt;
- u32 isequence;
-
- int vma_many;
-/*---------------------------------------------------------------------------*/
-/*
- * BUFFER INDICATORS
- */
-/*---------------------------------------------------------------------------*/
- int field_fill; /* Field buffer being filled by easycap_complete(). */
- /* Bumped only by easycap_complete(). */
- int field_page; /* Page of field buffer page being filled by */
- /* easycap_complete(). */
- int field_read; /* Field buffer to be read by field2frame(). */
- /* Bumped only by easycap_complete(). */
- int frame_fill; /* Frame buffer being filled by field2frame(). */
- /* Bumped only by easycap_dqbuf() when */
- /* field2frame() has created a complete frame. */
- int frame_read; /* Frame buffer offered to user by DQBUF. */
- /* Set only by easycap_dqbuf() to trail frame_fill.*/
- int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */
-/*---------------------------------------------------------------------------*/
-/*
- * IMAGE PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
- u32 pixelformat;
- int width;
- int height;
- int bytesperpixel;
- bool byteswaporder;
- bool decimatepixel;
- bool offerfields;
- int frame_buffer_used;
- int frame_buffer_many;
- int videofieldamount;
-
- int brightness;
- int contrast;
- int saturation;
- int hue;
-
- int allocation_video_urb;
- int allocation_video_page;
- int allocation_video_struct;
- int registered_video;
-/*---------------------------------------------------------------------------*/
-/*
- * ALSA
- */
-/*---------------------------------------------------------------------------*/
- struct snd_pcm_hardware alsa_hardware;
- struct snd_card *psnd_card;
- struct snd_pcm *psnd_pcm;
- struct snd_pcm_substream *psubstream;
- int dma_fill;
- int dma_next;
- int dma_read;
-/*---------------------------------------------------------------------------*/
-/*
- * SOUND PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
- int audio_interface;
- int audio_altsetting_on;
- int audio_altsetting_off;
- int audio_endpointnumber;
- int audio_isoc_maxframesize;
- int audio_isoc_buffer_size;
- int audio_isoc_framesperdesc;
-
- int audio_isoc_streaming;
- int audio_idle;
- int audio_eof;
- int volume;
- int mute;
- s8 gain;
-
- struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY];
-
- struct list_head urb_audio_head;
- struct list_head *purb_audio_head;
-/*---------------------------------------------------------------------------*/
-/*
- * BUFFER INDICATORS
- */
-/*---------------------------------------------------------------------------*/
- int audio_fill; /* Audio buffer being filled by easycap_complete(). */
- /* Bumped only by easycap_complete(). */
- int audio_read; /* Audio buffer page being read by easycap_read(). */
- /* Set by easycap_read() to trail audio_fill by */
- /* one fragment. */
-/*---------------------------------------------------------------------------*/
-/*
- * SOUND PROPERTIES
- */
-/*---------------------------------------------------------------------------*/
- int allocation_audio_urb;
- int allocation_audio_page;
- int allocation_audio_struct;
- int registered_audio;
-
- long long int audio_sample;
- long long int audio_niveau;
- long long int audio_square;
-
- struct data_buffer audio_buffer[];
-};
-/*---------------------------------------------------------------------------*/
-/*
- * VIDEO FUNCTION PROTOTYPES
- */
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-int easycap_newinput(struct easycap *, int);
-void easycap_testcard(struct easycap *, int);
-int easycap_isdongle(struct easycap *);
-
-long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long);
-
-int easycap_video_dqbuf(struct easycap *, int);
-int easycap_video_submit_urbs(struct easycap *);
-int easycap_video_kill_urbs(struct easycap *);
-int easycap_video_fillin_formats(void);
-
-int adjust_standard(struct easycap *, v4l2_std_id);
-int adjust_format(struct easycap *, u32, u32, u32, int, bool);
-int adjust_brightness(struct easycap *, int);
-int adjust_contrast(struct easycap *, int);
-int adjust_saturation(struct easycap *, int);
-int adjust_hue(struct easycap *, int);
-/*---------------------------------------------------------------------------*/
-/*
- * AUDIO FUNCTION PROTOTYPES
- */
-/*---------------------------------------------------------------------------*/
-int easycap_alsa_probe(struct easycap *);
-int easycap_audio_kill_urbs(struct easycap *);
-void easycap_alsa_complete(struct urb *);
-/*---------------------------------------------------------------------------*/
-/*
- * LOW-LEVEL FUNCTION PROTOTYPES
- */
-/*---------------------------------------------------------------------------*/
-int easycap_audio_gainset(struct usb_device *, s8);
-int easycap_audio_setup(struct easycap *);
-
-int easycap_wakeup_device(struct usb_device *);
-
-int setup_stk(struct usb_device *, bool);
-int setup_saa(struct usb_device *, bool);
-int ready_saa(struct usb_device *);
-int merit_saa(struct usb_device *);
-int check_vt(struct usb_device *);
-int select_input(struct usb_device *, int, int);
-int set_resolution(struct usb_device *, u16, u16, u16, u16);
-
-int read_saa(struct usb_device *, u16);
-int write_saa(struct usb_device *, u16, u16);
-int start_100(struct usb_device *);
-int stop_100(struct usb_device *);
-/*---------------------------------------------------------------------------*/
-
-
-/*---------------------------------------------------------------------------*/
-/*
- * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH
- * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE
- * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE
- * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
-const char *strerror(int err);
-
-#define SAY(format, args...) do { \
- printk(KERN_DEBUG "easycap:: %s: " \
- format, __func__, ##args); \
-} while (0)
-#define SAM(format, args...) do { \
- printk(KERN_DEBUG "easycap::%i%s: " \
- format, peasycap->isdongle, __func__, ##args);\
-} while (0)
-
-#ifdef CONFIG_EASYCAP_DEBUG
-extern int easycap_debug;
-#define JOT(n, format, args...) do { \
- if (n <= easycap_debug) { \
- printk(KERN_DEBUG "easycap:: %s: " \
- format, __func__, ##args);\
- } \
-} while (0)
-#define JOM(n, format, args...) do { \
- if (n <= easycap_debug) { \
- printk(KERN_DEBUG "easycap::%i%s: " \
- format, peasycap->isdongle, __func__, ##args);\
- } \
-} while (0)
-
-#else
-#define JOT(n, format, args...) do {} while (0)
-#define JOM(n, format, args...) do {} while (0)
-#endif /* CONFIG_EASYCAP_DEBUG */
-
-/*---------------------------------------------------------------------------*/
-
-/*---------------------------------------------------------------------------*/
-/* globals
- */
-/*---------------------------------------------------------------------------*/
-
-extern bool easycap_readback;
-extern const struct easycap_standard easycap_standard[];
-extern struct easycap_format easycap_format[];
-extern struct v4l2_queryctrl easycap_control[];
-extern struct easycap_dongle easycapdc60_dongle[];
-
-#endif /* !__EASYCAP_H__ */
diff --git a/drivers/staging/media/easycap/easycap_ioctl.c b/drivers/staging/media/easycap/easycap_ioctl.c
deleted file mode 100644
index 3cee3cd986d2..000000000000
--- a/drivers/staging/media/easycap/easycap_ioctl.c
+++ /dev/null
@@ -1,2443 +0,0 @@
-/******************************************************************************
-* *
-* easycap_ioctl.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-#include <linux/version.h>
-
-/*--------------------------------------------------------------------------*/
-/*
- * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE
- * FOLLOWING:
- * peasycap->standard_offset
- * peasycap->inputset[peasycap->input].standard_offset
- * peasycap->fps
- * peasycap->usec
- * peasycap->tolerate
- * peasycap->skip
- */
-/*---------------------------------------------------------------------------*/
-int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id)
-{
- struct easycap_standard const *peasycap_standard;
- u16 reg, set;
- int ir, rc, need, k;
- unsigned int itwas, isnow;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (std_id == peasycap_standard->v4l2_standard.id)
- break;
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (std_id & peasycap_standard->v4l2_standard.id)
- break;
- peasycap_standard++;
- }
- }
- if (0xFFFF == peasycap_standard->mask) {
- SAM("ERROR: 0x%08X=std_id: standard not found\n",
- (unsigned int)std_id);
- return -EINVAL;
- }
- SAM("selected standard: %s\n",
- &(peasycap_standard->v4l2_standard.name[0]));
- if (peasycap->standard_offset == peasycap_standard - easycap_standard) {
- SAM("requested standard already in effect\n");
- return 0;
- }
- peasycap->standard_offset = peasycap_standard - easycap_standard;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].standard_offset_ok) {
- peasycap->inputset[k].standard_offset =
- peasycap->standard_offset;
- }
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].standard_offset =
- peasycap->standard_offset;
- peasycap->inputset[peasycap->input].standard_offset_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
- peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator /
- peasycap_standard->v4l2_standard.frameperiod.numerator;
- switch (peasycap->fps) {
- case 6:
- case 30: {
- peasycap->ntsc = true;
- break;
- }
- case 5:
- case 25: {
- peasycap->ntsc = false;
- break;
- }
- default: {
- SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps);
- return -ENOENT;
- }
- }
- JOM(8, "%i frames-per-second\n", peasycap->fps);
- if (0x8000 & peasycap_standard->mask) {
- peasycap->skip = 5;
- peasycap->usec = 1000000 / (2 * (5 * peasycap->fps));
- peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps));
- } else {
- peasycap->skip = 0;
- peasycap->usec = 1000000 / (2 * peasycap->fps);
- peasycap->tolerate = 1000 * (25 / peasycap->fps);
- }
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- easycap_video_kill_urbs(peasycap);
- } else
- resubmit = false;
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 44, TABLE 42
- */
-/*--------------------------------------------------------------------------*/
- need = 0;
- itwas = 0;
- reg = 0x00;
- set = 0x00;
- switch (peasycap_standard->mask & 0x000F) {
- case NTSC_M_JP: {
- reg = 0x0A;
- set = 0x95;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register "
- "0x%02X to 0x%02X for JP standard\n", reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
-
- reg = 0x0B;
- set = 0x48;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X "
- "for JP standard\n", reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
-/*--------------------------------------------------------------------------*/
-/*
- * NOTE: NO break HERE: RUN ON TO NEXT CASE
- */
-/*--------------------------------------------------------------------------*/
- }
- case NTSC_M:
- case PAL_BGHIN: {
- reg = 0x0E;
- set = 0x01;
- need = 1;
- break;
- }
- case NTSC_N_443:
- case PAL_60: {
- reg = 0x0E;
- set = 0x11;
- need = 1;
- break;
- }
- case NTSC_443:
- case PAL_Nc: {
- reg = 0x0E;
- set = 0x21;
- need = 1;
- break;
- }
- case NTSC_N:
- case PAL_M: {
- reg = 0x0E;
- set = 0x31;
- need = 1;
- break;
- }
- case SECAM: {
- reg = 0x0E;
- set = 0x51;
- need = 1;
- break;
- }
- default:
- break;
- }
-/*--------------------------------------------------------------------------*/
- if (need) {
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X\n", reg);
- else
- itwas = (unsigned int)ir;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (0 != write_saa(peasycap->pusb_device, reg, set)) {
- SAM("ERROR: failed to set SAA register "
- "0x%02X to 0x%02X for table 42\n", reg, set);
- } else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 41
- */
-/*--------------------------------------------------------------------------*/
- reg = 0x08;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X "
- "so cannot reset\n", reg);
- else {
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = itwas | 0x40 ;
- else
- set = itwas & ~0x40 ;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
- reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 51, TABLE 57
- */
-/*---------------------------------------------------------------------------*/
- reg = 0x40;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X "
- "so cannot reset\n", reg);
- else {
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = itwas | 0x80 ;
- else
- set = itwas & ~0x80 ;
- rc = write_saa(peasycap->pusb_device, reg, set);
- if (rc)
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
- reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * SAA7113H DATASHEET PAGE 53, TABLE 66
- */
-/*--------------------------------------------------------------------------*/
- reg = 0x5A;
- ir = read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg);
- itwas = (unsigned int)ir;
- if (peasycap_standard->mask & 0x0001)
- set = 0x0A ;
- else
- set = 0x07 ;
- if (0 != write_saa(peasycap->pusb_device, reg, set))
- SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
- reg, set);
- else {
- isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
- if (0 > ir)
- JOM(8, "SAA register 0x%02X changed "
- "to 0x%02X\n", reg, isnow);
- else
- JOM(8, "SAA register 0x%02X changed "
- "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
- }
- if (resubmit)
- easycap_video_submit_urbs(peasycap);
- return 0;
-}
-/*****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES
- * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED.
- *
- * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN
- * THIS ROUTINE UPDATES THE FOLLOWING:
- * peasycap->format_offset
- * peasycap->inputset[peasycap->input].format_offset
- * peasycap->pixelformat
- * peasycap->height
- * peasycap->width
- * peasycap->bytesperpixel
- * peasycap->byteswaporder
- * peasycap->decimatepixel
- * peasycap->frame_buffer_used
- * peasycap->videofieldamount
- * peasycap->offerfields
- *
- * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[]
- * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER.
- * ERRORS RETURN A NEGATIVE NUMBER.
- */
-/*--------------------------------------------------------------------------*/
-int adjust_format(struct easycap *peasycap,
- u32 width, u32 height, u32 pixelformat, int field, bool try)
-{
- struct easycap_format *peasycap_format, *peasycap_best_format;
- u16 mask;
- struct usb_device *p;
- int miss, multiplier, best, k;
- char bf[5], fo[32], *pc;
- u32 uc;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (0 > peasycap->standard_offset) {
- JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset);
- return -EBUSY;
- }
- p = peasycap->pusb_device;
- if (!p) {
- SAM("ERROR: peaycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- pc = &bf[0];
- uc = pixelformat;
- memcpy((void *)pc, (void *)(&uc), 4);
- bf[4] = 0;
- mask = 0xFF & easycap_standard[peasycap->standard_offset].mask;
- SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n",
- width, height, pc, pixelformat, field, mask);
- switch (field) {
- case V4L2_FIELD_ANY: {
- strcpy(&fo[0], "V4L2_FIELD_ANY ");
- break;
- }
- case V4L2_FIELD_NONE: {
- strcpy(&fo[0], "V4L2_FIELD_NONE");
- break;
- }
- case V4L2_FIELD_TOP: {
- strcpy(&fo[0], "V4L2_FIELD_TOP");
- break;
- }
- case V4L2_FIELD_BOTTOM: {
- strcpy(&fo[0], "V4L2_FIELD_BOTTOM");
- break;
- }
- case V4L2_FIELD_INTERLACED: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED");
- break;
- }
- case V4L2_FIELD_SEQ_TB: {
- strcpy(&fo[0], "V4L2_FIELD_SEQ_TB");
- break;
- }
- case V4L2_FIELD_SEQ_BT: {
- strcpy(&fo[0], "V4L2_FIELD_SEQ_BT");
- break;
- }
- case V4L2_FIELD_ALTERNATE: {
- strcpy(&fo[0], "V4L2_FIELD_ALTERNATE");
- break;
- }
- case V4L2_FIELD_INTERLACED_TB: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB");
- break;
- }
- case V4L2_FIELD_INTERLACED_BT: {
- strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT");
- break;
- }
- default: {
- strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN ");
- break;
- }
- }
- SAM("sought: %s\n", &fo[0]);
- if (V4L2_FIELD_ANY == field) {
- field = V4L2_FIELD_NONE;
- SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n");
- }
- peasycap_best_format = NULL;
- peasycap_format = &easycap_format[0];
- while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
- JOM(16, ".> %i %i 0x%08X %ix%i\n",
- peasycap_format->mask & 0x01,
- peasycap_format->v4l2_format.fmt.pix.field,
- peasycap_format->v4l2_format.fmt.pix.pixelformat,
- peasycap_format->v4l2_format.fmt.pix.width,
- peasycap_format->v4l2_format.fmt.pix.height);
-
- if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
- (peasycap_format->v4l2_format.fmt.pix.field == field) &&
- (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) &&
- (peasycap_format->v4l2_format.fmt.pix.width == width) &&
- (peasycap_format->v4l2_format.fmt.pix.height == height)) {
-
- peasycap_best_format = peasycap_format;
- break;
- }
- peasycap_format++;
- }
- if (0 == peasycap_format->v4l2_format.fmt.pix.width) {
- SAM("cannot do: %ix%i with standard mask 0x%02X\n",
- width, height, mask);
- peasycap_format = &easycap_format[0];
- best = -1;
- while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
- if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
- (peasycap_format->v4l2_format.fmt.pix.field == field) &&
- (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) {
-
- miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width);
- if ((best > miss) || (best < 0)) {
- best = miss;
- peasycap_best_format = peasycap_format;
- if (!miss)
- break;
- }
- }
- peasycap_format++;
- }
- if (-1 == best) {
- SAM("cannot do %ix... with standard mask 0x%02X\n",
- width, mask);
- SAM("cannot do ...x%i with standard mask 0x%02X\n",
- height, mask);
- SAM(" %ix%i unmatched\n", width, height);
- return peasycap->format_offset;
- }
- }
- if (!peasycap_best_format) {
- SAM("MISTAKE: peasycap_best_format is NULL");
- return -EINVAL;
- }
- peasycap_format = peasycap_best_format;
-
-/*...........................................................................*/
- if (try)
- return peasycap_best_format - easycap_format;
-/*...........................................................................*/
-
- if (false != try) {
- SAM("MISTAKE: true==try where is should be false\n");
- return -EINVAL;
- }
- SAM("actioning: %ix%i %s\n",
- peasycap_format->v4l2_format.fmt.pix.width,
- peasycap_format->v4l2_format.fmt.pix.height,
- &peasycap_format->name[0]);
- peasycap->height = peasycap_format->v4l2_format.fmt.pix.height;
- peasycap->width = peasycap_format->v4l2_format.fmt.pix.width;
- peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat;
- peasycap->format_offset = peasycap_format - easycap_format;
-
-
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].format_offset_ok) {
- peasycap->inputset[k].format_offset =
- peasycap->format_offset;
- }
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].format_offset =
- peasycap->format_offset;
- peasycap->inputset[peasycap->input].format_offset_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
-
-
- peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ;
- if (0x0100 & peasycap_format->mask)
- peasycap->byteswaporder = true;
- else
- peasycap->byteswaporder = false;
- if (0x0200 & peasycap_format->mask)
- peasycap->skip = 5;
- else
- peasycap->skip = 0;
- if (0x0800 & peasycap_format->mask)
- peasycap->decimatepixel = true;
- else
- peasycap->decimatepixel = false;
- if (0x1000 & peasycap_format->mask)
- peasycap->offerfields = true;
- else
- peasycap->offerfields = false;
- if (peasycap->decimatepixel)
- multiplier = 2;
- else
- multiplier = 1;
- peasycap->videofieldamount =
- multiplier * peasycap->width * multiplier * peasycap->height;
- peasycap->frame_buffer_used =
- peasycap->bytesperpixel * peasycap->width * peasycap->height;
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- easycap_video_kill_urbs(peasycap);
- } else
- resubmit = false;
-/*---------------------------------------------------------------------------*/
-/*
- * PAL
- */
-/*---------------------------------------------------------------------------*/
- if (0 == (0x01 & peasycap_format->mask)) {
- if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (576 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (288 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (576 == peasycap_format->v4l2_format.fmt.pix.height)) {
- if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else {
- SAM("MISTAKE: bad format, cannot set resolution\n");
- return -EINVAL;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * NTSC
- */
-/*---------------------------------------------------------------------------*/
- } else {
- if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
- ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
- (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
- if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) {
- SAM("ERROR: set_resolution() failed\n");
- return -EINVAL;
- }
- } else {
- SAM("MISTAKE: bad format, cannot set resolution\n");
- return -EINVAL;
- }
- }
-/*---------------------------------------------------------------------------*/
- if (resubmit)
- easycap_video_submit_urbs(peasycap);
-
- return peasycap_best_format - easycap_format;
-}
-/*****************************************************************************/
-int adjust_brightness(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->brightness) &&
- (easycap_control[i1].maximum >= peasycap->brightness)) {
- if (peasycap->brightness == value) {
- SAM("unchanged brightness at 0x%02X\n",
- value);
- return 0;
- }
- }
- peasycap->brightness = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].brightness_ok)
- peasycap->inputset[k].brightness =
- peasycap->brightness;
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].brightness =
- peasycap->brightness;
- peasycap->inputset[peasycap->input].brightness_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
- mood = 0x00FF & (unsigned int)peasycap->brightness;
- if (write_saa(peasycap->pusb_device, 0x0A, mood)) {
- SAM("WARNING: failed to adjust brightness "
- "to 0x%02X\n", mood);
- return -ENOENT;
- }
- SAM("adjusting brightness to 0x%02X\n", mood);
- return 0;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust brightness: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_contrast(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_CONTRAST == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
-
- if ((easycap_control[i1].minimum <= peasycap->contrast) &&
- (easycap_control[i1].maximum >= peasycap->contrast)) {
- if (peasycap->contrast == value) {
- SAM("unchanged contrast at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->contrast = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].contrast_ok)
- peasycap->inputset[k].contrast = peasycap->contrast;
- }
-
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].contrast =
- peasycap->contrast;
- peasycap->inputset[peasycap->input].contrast_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
-
- mood = 0x00FF & (unsigned int) (peasycap->contrast - 128);
- if (write_saa(peasycap->pusb_device, 0x0B, mood)) {
- SAM("WARNING: failed to adjust contrast to "
- "0x%02X\n", mood);
- return -ENOENT;
- }
- SAM("adjusting contrast to 0x%02X\n", mood);
- return 0;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust contrast: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_saturation(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_SATURATION == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
-
- if ((easycap_control[i1].minimum <= peasycap->saturation) &&
- (easycap_control[i1].maximum >= peasycap->saturation)) {
- if (peasycap->saturation == value) {
- SAM("unchanged saturation at 0x%02X\n",
- value);
- return 0;
- }
- }
- peasycap->saturation = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].saturation_ok)
- peasycap->inputset[k].saturation =
- peasycap->saturation;
- }
- if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
- peasycap->inputset[peasycap->input].saturation =
- peasycap->saturation;
- peasycap->inputset[peasycap->input].saturation_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
- mood = 0x00FF & (unsigned int) (peasycap->saturation - 128);
- if (write_saa(peasycap->pusb_device, 0x0C, mood)) {
- SAM("WARNING: failed to adjust saturation to "
- "0x%02X\n", mood);
- return -ENOENT;
- }
- SAM("adjusting saturation to 0x%02X\n", mood);
- return 0;
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust saturation: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-int adjust_hue(struct easycap *peasycap, int value)
-{
- unsigned int mood;
- int i1, i2, k;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_HUE == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->hue) &&
- (easycap_control[i1].maximum >= peasycap->hue)) {
- if (peasycap->hue == value) {
- SAM("unchanged hue at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->hue = value;
- for (k = 0; k < INPUT_MANY; k++) {
- if (!peasycap->inputset[k].hue_ok)
- peasycap->inputset[k].hue = peasycap->hue;
- }
- if (0 <= peasycap->input && INPUT_MANY > peasycap->input) {
- peasycap->inputset[peasycap->input].hue = peasycap->hue;
- peasycap->inputset[peasycap->input].hue_ok = 1;
- } else
- JOM(8, "%i=peasycap->input\n", peasycap->input);
- i2 = peasycap->hue - 128;
- mood = 0x00FF & ((int) i2);
- if (write_saa(peasycap->pusb_device, 0x0D, mood)) {
- SAM("WARNING: failed to adjust hue to 0x%02X\n", mood);
- return -ENOENT;
- }
- SAM("adjusting hue to 0x%02X\n", mood);
- return 0;
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust hue: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-static int adjust_volume(struct easycap *peasycap, int value)
-{
- s8 mood;
- int i1;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) {
- if ((easycap_control[i1].minimum > value) ||
- (easycap_control[i1].maximum < value))
- value = easycap_control[i1].default_value;
-
- if ((easycap_control[i1].minimum <= peasycap->volume) &&
- (easycap_control[i1].maximum >= peasycap->volume)) {
- if (peasycap->volume == value) {
- SAM("unchanged volume at 0x%02X\n", value);
- return 0;
- }
- }
- peasycap->volume = value;
- mood = (16 > peasycap->volume) ? 16 :
- ((31 < peasycap->volume) ? 31 :
- (s8) peasycap->volume);
- if (!easycap_audio_gainset(peasycap->pusb_device, mood)) {
- SAM("WARNING: failed to adjust volume to "
- "0x%2X\n", mood);
- return -ENOENT;
- }
- SAM("adjusting volume to 0x%02X\n", mood);
- return 0;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust volume: control not found\n");
- return -ENOENT;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE:
- * usb_set_interface(peasycap->pusb_device,
- * peasycap->audio_interface,
- * peasycap->audio_altsetting_off);
- * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS
- * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT
- * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-static int adjust_mute(struct easycap *peasycap, int value)
-{
- int i1;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) {
- peasycap->mute = value;
- switch (peasycap->mute) {
- case 1: {
- peasycap->audio_idle = 1;
- SAM("adjusting mute: %i=peasycap->audio_idle\n",
- peasycap->audio_idle);
- return 0;
- }
- default: {
- peasycap->audio_idle = 0;
- SAM("adjusting mute: %i=peasycap->audio_idle\n",
- peasycap->audio_idle);
- return 0;
- }
- }
- break;
- }
- i1++;
- }
- SAM("WARNING: failed to adjust mute: control not found\n");
- return -ENOENT;
-}
-/*---------------------------------------------------------------------------*/
-long easycap_unlocked_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct easycap *peasycap;
- struct usb_device *p;
- int kd;
-
- if (!file) {
- SAY("ERROR: file is NULL\n");
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -1;
- }
- p = peasycap->pusb_device;
- if (!p) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- kd = easycap_isdongle(peasycap);
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot lock "
- "easycapdc60_dongle[%i].mutex_video\n", kd);
- return -ERESTARTSYS;
- }
- JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd);
-/*---------------------------------------------------------------------------*/
-/*
- * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
- * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
- * IF NECESSARY, BAIL OUT.
- */
-/*---------------------------------------------------------------------------*/
- if (kd != easycap_isdongle(peasycap))
- return -ERESTARTSYS;
- if (!file) {
- SAY("ERROR: file is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- } else {
-/*---------------------------------------------------------------------------*/
-/*
- * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
- * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
- */
-/*---------------------------------------------------------------------------*/
- return -ERESTARTSYS;
- }
-/*---------------------------------------------------------------------------*/
- switch (cmd) {
- case VIDIOC_QUERYCAP: {
- struct v4l2_capability v4l2_capability;
- char version[16], *p1, *p2;
- int i, rc, k[3];
- long lng;
-
- JOM(8, "VIDIOC_QUERYCAP\n");
-
- if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
- SAM("ERROR: bad driver version string\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- strcpy(&version[0], EASYCAP_DRIVER_VERSION);
- for (i = 0; i < 3; i++)
- k[i] = 0;
- p2 = &version[0];
- i = 0;
- while (*p2) {
- p1 = p2;
- while (*p2 && ('.' != *p2))
- p2++;
- if (*p2)
- *p2++ = 0;
- if (3 > i) {
- rc = (int) strict_strtol(p1, 10, &lng);
- if (rc) {
- SAM("ERROR: %i=strict_strtol(%s,.,,)\n",
- rc, p1);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- k[i] = (int)lng;
- }
- i++;
- }
-
- memset(&v4l2_capability, 0, sizeof(struct v4l2_capability));
- strlcpy(&v4l2_capability.driver[0],
- "easycap", sizeof(v4l2_capability.driver));
-
- v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_AUDIO |
- V4L2_CAP_READWRITE;
-
- v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]);
- JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]);
-
- strlcpy(&v4l2_capability.card[0],
- "EasyCAP DC60", sizeof(v4l2_capability.card));
-
- if (usb_make_path(peasycap->pusb_device,
- &v4l2_capability.bus_info[0],
- sizeof(v4l2_capability.bus_info)) < 0) {
-
- strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info",
- sizeof(v4l2_capability.bus_info));
- JOM(8, "%s=v4l2_capability.bus_info\n",
- &v4l2_capability.bus_info[0]);
- }
- if (copy_to_user((void __user *)arg, &v4l2_capability,
- sizeof(struct v4l2_capability))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMINPUT: {
- struct v4l2_input v4l2_input;
- u32 index;
-
- JOM(8, "VIDIOC_ENUMINPUT\n");
-
- if (copy_from_user(&v4l2_input, (void __user *)arg,
- sizeof(struct v4l2_input))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_input.index;
- memset(&v4l2_input, 0, sizeof(struct v4l2_input));
-
- switch (index) {
- case 0: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS0");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL |
- V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 1: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS1");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 2: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS2");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 3: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS3");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 4: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "CVBS4");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- case 5: {
- v4l2_input.index = index;
- strcpy(&v4l2_input.name[0], "S-VIDEO");
- v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
- v4l2_input.audioset = 0x01;
- v4l2_input.tuner = 0;
- v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
- V4L2_STD_NTSC ;
- v4l2_input.status = 0;
- JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts inputs\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
-
- if (copy_to_user((void __user *)arg, &v4l2_input,
- sizeof(struct v4l2_input))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_INPUT: {
- u32 index;
-
- JOM(8, "VIDIOC_G_INPUT\n");
- index = (u32)peasycap->input;
- JOM(8, "user is told: %i\n", index);
- if (copy_to_user((void __user *)arg, &index, sizeof(u32))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_INPUT:
- {
- u32 index;
- int rc;
-
- JOM(8, "VIDIOC_S_INPUT\n");
-
- if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "user requests input %i\n", index);
-
- if ((int)index == peasycap->input) {
- SAM("requested input already in effect\n");
- break;
- }
-
- if ((0 > index) || (INPUT_MANY <= index)) {
- JOM(8, "ERROR: bad requested input: %i\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- rc = easycap_newinput(peasycap, (int)index);
- if (0 == rc) {
- JOM(8, "newinput(.,%i) OK\n", (int)index);
- } else {
- SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMAUDIO: {
- JOM(8, "VIDIOC_ENUMAUDIO\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUMAUDOUT: {
- struct v4l2_audioout v4l2_audioout;
-
- JOM(8, "VIDIOC_ENUMAUDOUT\n");
-
- if (copy_from_user(&v4l2_audioout, (void __user *)arg,
- sizeof(struct v4l2_audioout))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (0 != v4l2_audioout.index) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
- v4l2_audioout.index = 0;
- strcpy(&v4l2_audioout.name[0], "Soundtrack");
-
- if (copy_to_user((void __user *)arg, &v4l2_audioout,
- sizeof(struct v4l2_audioout))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYCTRL: {
- int i1;
- struct v4l2_queryctrl v4l2_queryctrl;
-
- JOM(8, "VIDIOC_QUERYCTRL\n");
-
- if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg,
- sizeof(struct v4l2_queryctrl))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- i1 = 0;
- while (0xFFFFFFFF != easycap_control[i1].id) {
- if (easycap_control[i1].id == v4l2_queryctrl.id) {
- JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]"
- ".name\n", &easycap_control[i1].name[0], i1);
- memcpy(&v4l2_queryctrl, &easycap_control[i1],
- sizeof(struct v4l2_queryctrl));
- break;
- }
- i1++;
- }
- if (0xFFFFFFFF == easycap_control[i1].id) {
- JOM(8, "%i=index: exhausts controls\n", i1);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (copy_to_user((void __user *)arg, &v4l2_queryctrl,
- sizeof(struct v4l2_queryctrl))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYMENU: {
- JOM(8, "VIDIOC_QUERYMENU unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_CTRL: {
- struct v4l2_control *pv4l2_control;
-
- JOM(8, "VIDIOC_G_CTRL\n");
- pv4l2_control = memdup_user((void __user *)arg,
- sizeof(struct v4l2_control));
- if (IS_ERR(pv4l2_control)) {
- SAM("ERROR: copy from user failed\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return PTR_ERR(pv4l2_control);
- }
-
- switch (pv4l2_control->id) {
- case V4L2_CID_BRIGHTNESS: {
- pv4l2_control->value = peasycap->brightness;
- JOM(8, "user enquires brightness: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_CONTRAST: {
- pv4l2_control->value = peasycap->contrast;
- JOM(8, "user enquires contrast: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_SATURATION: {
- pv4l2_control->value = peasycap->saturation;
- JOM(8, "user enquires saturation: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_HUE: {
- pv4l2_control->value = peasycap->hue;
- JOM(8, "user enquires hue: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_AUDIO_VOLUME: {
- pv4l2_control->value = peasycap->volume;
- JOM(8, "user enquires volume: %i\n", pv4l2_control->value);
- break;
- }
- case V4L2_CID_AUDIO_MUTE: {
- if (1 == peasycap->mute)
- pv4l2_control->value = true;
- else
- pv4l2_control->value = false;
- JOM(8, "user enquires mute: %i\n", pv4l2_control->value);
- break;
- }
- default: {
- SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
- pv4l2_control->id);
- kfree(pv4l2_control);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, pv4l2_control,
- sizeof(struct v4l2_control))) {
- kfree(pv4l2_control);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_control);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_CTRL: {
- struct v4l2_control v4l2_control;
-
- JOM(8, "VIDIOC_S_CTRL\n");
-
- if (0 != copy_from_user(&v4l2_control, (void __user *)arg,
- sizeof(struct v4l2_control))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- switch (v4l2_control.id) {
- case V4L2_CID_BRIGHTNESS: {
- JOM(8, "user requests brightness %i\n", v4l2_control.value);
- if (0 != adjust_brightness(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_CONTRAST: {
- JOM(8, "user requests contrast %i\n", v4l2_control.value);
- if (0 != adjust_contrast(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_SATURATION: {
- JOM(8, "user requests saturation %i\n", v4l2_control.value);
- if (0 != adjust_saturation(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_HUE: {
- JOM(8, "user requests hue %i\n", v4l2_control.value);
- if (0 != adjust_hue(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_AUDIO_VOLUME: {
- JOM(8, "user requests volume %i\n", v4l2_control.value);
- if (0 != adjust_volume(peasycap, v4l2_control.value))
- ;
- break;
- }
- case V4L2_CID_AUDIO_MUTE: {
- int mute;
-
- JOM(8, "user requests mute %i\n", v4l2_control.value);
- if (v4l2_control.value)
- mute = 1;
- else
- mute = 0;
-
- if (0 != adjust_mute(peasycap, mute))
- SAM("WARNING: failed to adjust mute to %i\n", mute);
- break;
- }
- default: {
- SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
- v4l2_control.id);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_EXT_CTRLS: {
- JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FMT: {
- u32 index;
- struct v4l2_fmtdesc v4l2_fmtdesc;
-
- JOM(8, "VIDIOC_ENUM_FMT\n");
-
- if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg,
- sizeof(struct v4l2_fmtdesc))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_fmtdesc.index;
- memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
-
- v4l2_fmtdesc.index = index;
- v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- switch (index) {
- case 0: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "uyvy");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 1: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "yuy2");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 2: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "rgb24");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 3: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "rgb32");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 4: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "bgr24");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- case 5: {
- v4l2_fmtdesc.flags = 0;
- strcpy(&v4l2_fmtdesc.description[0], "bgr32");
- v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32;
- JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts formats\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_fmtdesc,
- sizeof(struct v4l2_fmtdesc))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE
- * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
- */
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FRAMESIZES: {
- u32 index;
- struct v4l2_frmsizeenum v4l2_frmsizeenum;
-
- JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
-
- if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg,
- sizeof(struct v4l2_frmsizeenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_frmsizeenum.index;
-
- v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE;
-
- if (peasycap->ntsc) {
- switch (index) {
- case 0: {
- v4l2_frmsizeenum.discrete.width = 640;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 1: {
- v4l2_frmsizeenum.discrete.width = 320;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 2: {
- v4l2_frmsizeenum.discrete.width = 720;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 3: {
- v4l2_frmsizeenum.discrete.width = 360;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts framesizes\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- } else {
- switch (index) {
- case 0: {
- v4l2_frmsizeenum.discrete.width = 640;
- v4l2_frmsizeenum.discrete.height = 480;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 1: {
- v4l2_frmsizeenum.discrete.width = 320;
- v4l2_frmsizeenum.discrete.height = 240;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 2: {
- v4l2_frmsizeenum.discrete.width = 704;
- v4l2_frmsizeenum.discrete.height = 576;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 3: {
- v4l2_frmsizeenum.discrete.width = 720;
- v4l2_frmsizeenum.discrete.height = 576;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- case 4: {
- v4l2_frmsizeenum.discrete.width = 360;
- v4l2_frmsizeenum.discrete.height = 288;
- JOM(8, "%i=index: %ix%i\n", index,
- (int)(v4l2_frmsizeenum.
- discrete.width),
- (int)(v4l2_frmsizeenum.
- discrete.height));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts framesizes\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum,
- sizeof(struct v4l2_frmsizeenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE
- * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
- */
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_ENUM_FRAMEINTERVALS: {
- u32 index;
- int denominator;
- struct v4l2_frmivalenum v4l2_frmivalenum;
-
- JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n");
-
- if (peasycap->fps)
- denominator = peasycap->fps;
- else {
- if (peasycap->ntsc)
- denominator = 30;
- else
- denominator = 25;
- }
-
- if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg,
- sizeof(struct v4l2_frmivalenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- index = v4l2_frmivalenum.index;
-
- v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE;
-
- switch (index) {
- case 0: {
- v4l2_frmivalenum.discrete.numerator = 1;
- v4l2_frmivalenum.discrete.denominator = denominator;
- JOM(8, "%i=index: %i/%i\n", index,
- (int)(v4l2_frmivalenum.discrete.numerator),
- (int)(v4l2_frmivalenum.discrete.denominator));
- break;
- }
- case 1: {
- v4l2_frmivalenum.discrete.numerator = 1;
- v4l2_frmivalenum.discrete.denominator = denominator/5;
- JOM(8, "%i=index: %i/%i\n", index,
- (int)(v4l2_frmivalenum.discrete.numerator),
- (int)(v4l2_frmivalenum.discrete.denominator));
- break;
- }
- default: {
- JOM(8, "%i=index: exhausts frameintervals\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- }
- if (copy_to_user((void __user *)arg, &v4l2_frmivalenum,
- sizeof(struct v4l2_frmivalenum))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_FMT: {
- struct v4l2_format *pv4l2_format;
- struct v4l2_pix_format *pv4l2_pix_format;
-
- JOM(8, "VIDIOC_G_FMT\n");
- pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
- if (!pv4l2_format) {
- SAM("ERROR: out of memory\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOMEM;
- }
- pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
- if (!pv4l2_pix_format) {
- SAM("ERROR: out of memory\n");
- kfree(pv4l2_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOMEM;
- }
- if (0 != copy_from_user(pv4l2_format, (void __user *)arg,
- sizeof(struct v4l2_format))) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
- pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- memcpy(&pv4l2_format->fmt.pix,
- &easycap_format[peasycap->format_offset]
- .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format));
- JOM(8, "user is told: %s\n",
- &easycap_format[peasycap->format_offset].name[0]);
-
- if (copy_to_user((void __user *)arg, pv4l2_format,
- sizeof(struct v4l2_format))) {
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_format);
- kfree(pv4l2_pix_format);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT: {
- struct v4l2_format v4l2_format;
- struct v4l2_pix_format v4l2_pix_format;
- bool try;
- int best_format;
-
- if (VIDIOC_TRY_FMT == cmd) {
- JOM(8, "VIDIOC_TRY_FMT\n");
- try = true;
- } else {
- JOM(8, "VIDIOC_S_FMT\n");
- try = false;
- }
-
- if (0 != copy_from_user(&v4l2_format, (void __user *)arg,
- sizeof(struct v4l2_format))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- best_format = adjust_format(peasycap,
- v4l2_format.fmt.pix.width,
- v4l2_format.fmt.pix.height,
- v4l2_format.fmt.pix.pixelformat,
- v4l2_format.fmt.pix.field,
- try);
- if (0 > best_format) {
- if (-EBUSY == best_format) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EBUSY;
- }
- JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOENT;
- }
-/*...........................................................................*/
- memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
- v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-
- memcpy(&(v4l2_format.fmt.pix),
- &(easycap_format[best_format].v4l2_format.fmt.pix),
- sizeof(v4l2_pix_format));
- JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
-
- if (copy_to_user((void __user *)arg, &v4l2_format,
- sizeof(struct v4l2_format))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_CROPCAP: {
- struct v4l2_cropcap v4l2_cropcap;
-
- JOM(8, "VIDIOC_CROPCAP\n");
-
- if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg,
- sizeof(struct v4l2_cropcap))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
-
- memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap));
- v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_cropcap.bounds.left = 0;
- v4l2_cropcap.bounds.top = 0;
- v4l2_cropcap.bounds.width = peasycap->width;
- v4l2_cropcap.bounds.height = peasycap->height;
- v4l2_cropcap.defrect.left = 0;
- v4l2_cropcap.defrect.top = 0;
- v4l2_cropcap.defrect.width = peasycap->width;
- v4l2_cropcap.defrect.height = peasycap->height;
- v4l2_cropcap.pixelaspect.numerator = 1;
- v4l2_cropcap.pixelaspect.denominator = 1;
-
- JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
-
- if (copy_to_user((void __user *)arg, &v4l2_cropcap,
- sizeof(struct v4l2_cropcap))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_CROP:
- case VIDIOC_S_CROP: {
- JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYSTD: {
- JOM(8, "VIDIOC_QUERYSTD: "
- "EasyCAP is incapable of detecting standard\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- break;
- }
- /*-------------------------------------------------------------------*/
- /*
- * THE MANIPULATIONS INVOLVING last0,last1,last2,last3
- * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE
- * A BUG IN 64-BIT mplayer.
- * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer.
- */
- /*------------------------------------------------------------------*/
- case VIDIOC_ENUMSTD: {
- int last0 = -1, last1 = -1, last2 = -1, last3 = -1;
- struct v4l2_standard v4l2_standard;
- u32 index;
- struct easycap_standard const *peasycap_standard;
-
- JOM(8, "VIDIOC_ENUMSTD\n");
-
- if (0 != copy_from_user(&v4l2_standard, (void __user *)arg,
- sizeof(struct v4l2_standard))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- index = v4l2_standard.index;
-
- last3 = last2;
- last2 = last1;
- last1 = last0;
- last0 = index;
- if ((index == last3) && (index == last2) &&
- (index == last1) && (index == last0)) {
- index++;
- last3 = last2;
- last2 = last1;
- last1 = last0;
- last0 = index;
- }
-
- memset(&v4l2_standard, 0, sizeof(struct v4l2_standard));
-
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if ((int)(peasycap_standard - &easycap_standard[0]) == index)
- break;
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- JOM(8, "%i=index: exhausts standards\n", index);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- JOM(8, "%i=index: %s\n", index,
- &(peasycap_standard->v4l2_standard.name[0]));
- memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard),
- sizeof(struct v4l2_standard));
-
- v4l2_standard.index = index;
-
- if (copy_to_user((void __user *)arg, &v4l2_standard,
- sizeof(struct v4l2_standard))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_STD: {
- v4l2_std_id std_id;
- struct easycap_standard const *peasycap_standard;
-
- JOM(8, "VIDIOC_G_STD\n");
-
- if (0 > peasycap->standard_offset) {
- JOM(8, "%i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EBUSY;
- }
-
- if (0 != copy_from_user(&std_id, (void __user *)arg,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- peasycap_standard = &easycap_standard[peasycap->standard_offset];
- std_id = peasycap_standard->v4l2_standard.id;
-
- JOM(8, "user is told: %s\n",
- &peasycap_standard->v4l2_standard.name[0]);
-
- if (copy_to_user((void __user *)arg, &std_id,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_STD: {
- v4l2_std_id std_id;
- int rc;
-
- JOM(8, "VIDIOC_S_STD\n");
-
- if (0 != copy_from_user(&std_id, (void __user *)arg,
- sizeof(v4l2_std_id))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "User requests standard: 0x%08X%08X\n",
- (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32),
- (int)(std_id & ((v4l2_std_id)0xFFFFFFFF)));
-
- rc = adjust_standard(peasycap, std_id);
- if (0 > rc) {
- JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOENT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_REQBUFS: {
- int nbuffers;
- struct v4l2_requestbuffers v4l2_requestbuffers;
-
- JOM(8, "VIDIOC_REQBUFS\n");
-
- if (0 != copy_from_user(&v4l2_requestbuffers,
- (void __user *)arg,
- sizeof(struct v4l2_requestbuffers))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- nbuffers = v4l2_requestbuffers.count;
- JOM(8, " User requests %i buffers ...\n", nbuffers);
- if (nbuffers < 2)
- nbuffers = 2;
- if (nbuffers > FRAME_BUFFER_MANY)
- nbuffers = FRAME_BUFFER_MANY;
- if (v4l2_requestbuffers.count == nbuffers) {
- JOM(8, " ... agree to %i buffers\n",
- nbuffers);
- } else {
- JOM(8, " ... insist on %i buffers\n",
- nbuffers);
- v4l2_requestbuffers.count = nbuffers;
- }
- peasycap->frame_buffer_many = nbuffers;
-
- if (copy_to_user((void __user *)arg, &v4l2_requestbuffers,
- sizeof(struct v4l2_requestbuffers))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QUERYBUF: {
- u32 index;
- struct v4l2_buffer v4l2_buffer;
-
- JOM(8, "VIDIOC_QUERYBUF\n");
-
- if (peasycap->video_eof) {
- JOM(8, "returning -EIO because %i=video_eof\n",
- peasycap->video_eof);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
-
- if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- index = v4l2_buffer.index;
- if (index < 0 || index >= peasycap->frame_buffer_many)
- return -EINVAL;
- memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
- v4l2_buffer.index = index;
- v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buffer.bytesused = peasycap->frame_buffer_used;
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED |
- peasycap->done[index] |
- peasycap->queued[index];
- v4l2_buffer.field = V4L2_FIELD_NONE;
- v4l2_buffer.memory = V4L2_MEMORY_MMAP;
- v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE;
- v4l2_buffer.length = FRAME_BUFFER_SIZE;
-
- JOM(16, " %10i=index\n", v4l2_buffer.index);
- JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
- JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
- JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
- JOM(16, " %10i=field\n", v4l2_buffer.field);
- JOM(16, " %10li=timestamp.tv_usec\n",
- (long)v4l2_buffer.timestamp.tv_usec);
- JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
- JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
- JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
- JOM(16, " %10i=length\n", v4l2_buffer.length);
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_QBUF: {
- struct v4l2_buffer v4l2_buffer;
-
- JOM(8, "VIDIOC_QBUF\n");
-
- if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- if (v4l2_buffer.index < 0 ||
- v4l2_buffer.index >= peasycap->frame_buffer_many) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
-
- peasycap->done[v4l2_buffer.index] = 0;
- peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- JOM(8, "..... user queueing frame buffer %i\n",
- (int)v4l2_buffer.index);
-
- peasycap->frame_lock = 0;
-
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_DQBUF:
- {
- struct timeval timeval, timeval2;
- int i, j;
- struct v4l2_buffer v4l2_buffer;
- int rcdq;
- u16 input;
-
- JOM(8, "VIDIOC_DQBUF\n");
-
- if ((peasycap->video_idle) || (peasycap->video_eof)) {
- JOM(8, "returning -EIO because "
- "%i=video_idle %i=video_eof\n",
- peasycap->video_idle, peasycap->video_eof);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
-
- if (copy_from_user(&v4l2_buffer, (void __user *)arg,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-
- if (peasycap->offerfields) {
- /*---------------------------------------------------*/
- /*
- * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST
- * V4L2_FIELD_BOTTOM
- */
- /*---------------------------------------------------*/
- if (V4L2_FIELD_TOP == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_TOP\n");
- else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_BOTTOM\n");
- else if (V4L2_FIELD_ANY == v4l2_buffer.field)
- JOM(8, "user wants V4L2_FIELD_ANY\n");
- else
- JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n",
- v4l2_buffer.field);
- }
-
- if (!peasycap->video_isoc_streaming) {
- JOM(16, "returning -EIO because video urbs not streaming\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- /*-------------------------------------------------------------------*/
- /*
- * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(),
- * AS DETERMINED BY FINDING
- * THE FLAG peasycap->polled SET, THERE MUST BE
- * NO FURTHER WAIT HERE. IN THIS
- * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read
- */
- /*-------------------------------------------------------------------*/
-
- if (!peasycap->polled) {
- do {
- rcdq = easycap_video_dqbuf(peasycap, 0);
- if (-EIO == rcdq) {
- JOM(8, "returning -EIO because "
- "dqbuf() returned -EIO\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- } while (0 != rcdq);
- } else {
- if (peasycap->video_eof) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EIO;
- }
- }
- if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
- JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n",
- peasycap->done[peasycap->frame_read]);
- }
- peasycap->polled = 0;
-
- if (!(peasycap->isequence % 10)) {
- for (i = 0; i < 179; i++)
- peasycap->merit[i] = peasycap->merit[i+1];
- peasycap->merit[179] = merit_saa(peasycap->pusb_device);
- j = 0;
- for (i = 0; i < 180; i++)
- j += peasycap->merit[i];
- if (90 < j) {
- SAM("easycap driver shutting down "
- "on condition blue\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- }
- }
-
- v4l2_buffer.index = peasycap->frame_read;
- v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buffer.bytesused = peasycap->frame_buffer_used;
- v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
- if (peasycap->offerfields)
- v4l2_buffer.field = V4L2_FIELD_BOTTOM;
- else
- v4l2_buffer.field = V4L2_FIELD_NONE;
- do_gettimeofday(&timeval);
- timeval2 = timeval;
-
- v4l2_buffer.timestamp = timeval2;
- v4l2_buffer.sequence = peasycap->isequence++;
- v4l2_buffer.memory = V4L2_MEMORY_MMAP;
- v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE;
- v4l2_buffer.length = FRAME_BUFFER_SIZE;
-
- JOM(16, " %10i=index\n", v4l2_buffer.index);
- JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
- JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
- JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
- JOM(16, " %10i=field\n", v4l2_buffer.field);
- JOM(16, " %10li=timestamp.tv_sec\n",
- (long)v4l2_buffer.timestamp.tv_sec);
- JOM(16, " %10li=timestamp.tv_usec\n",
- (long)v4l2_buffer.timestamp.tv_usec);
- JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
- JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
- JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
- JOM(16, " %10i=length\n", v4l2_buffer.length);
-
- if (copy_to_user((void __user *)arg, &v4l2_buffer,
- sizeof(struct v4l2_buffer))) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- input = peasycap->frame_buffer[peasycap->frame_read][0].input;
- if (0x08 & input) {
- JOM(8, "user is offered frame buffer %i, input %i\n",
- peasycap->frame_read, (0x07 & input));
- } else {
- JOM(8, "user is offered frame buffer %i\n",
- peasycap->frame_read);
- }
- peasycap->frame_lock = 1;
- JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill);
- if (peasycap->frame_read == peasycap->frame_fill) {
- if (peasycap->frame_lock) {
- JOM(8, "WORRY: filling frame buffer "
- "while offered to user\n");
- }
- }
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_STREAMON: {
- int i;
-
- JOM(8, "VIDIOC_STREAMON\n");
-
- peasycap->isequence = 0;
- for (i = 0; i < 180; i++)
- peasycap->merit[i] = 0;
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- easycap_video_submit_urbs(peasycap);
- peasycap->video_idle = 0;
- peasycap->audio_idle = 0;
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_STREAMOFF: {
- JOM(8, "VIDIOC_STREAMOFF\n");
-
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
-
- peasycap->video_idle = 1;
- peasycap->audio_idle = 1;
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND
- * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
- JOM(8, "calling wake_up on wq_video and wq_audio\n");
- wake_up_interruptible(&(peasycap->wq_video));
- if (peasycap->psubstream)
- snd_pcm_period_elapsed(peasycap->psubstream);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_PARM: {
- struct v4l2_streamparm *pv4l2_streamparm;
-
- JOM(8, "VIDIOC_G_PARM\n");
- pv4l2_streamparm = memdup_user((void __user *)arg,
- sizeof(struct v4l2_streamparm));
- if (IS_ERR(pv4l2_streamparm)) {
- SAM("ERROR: copy from user failed\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return PTR_ERR(pv4l2_streamparm);
- }
-
- if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- kfree(pv4l2_streamparm);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- pv4l2_streamparm->parm.capture.capability = 0;
- pv4l2_streamparm->parm.capture.capturemode = 0;
- pv4l2_streamparm->parm.capture.timeperframe.numerator = 1;
-
- if (peasycap->fps) {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = peasycap->fps;
- } else {
- if (peasycap->ntsc) {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = 30;
- } else {
- pv4l2_streamparm->parm.capture.timeperframe.
- denominator = 25;
- }
- }
-
- pv4l2_streamparm->parm.capture.readbuffers =
- peasycap->frame_buffer_many;
- pv4l2_streamparm->parm.capture.extendedmode = 0;
- if (copy_to_user((void __user *)arg,
- pv4l2_streamparm,
- sizeof(struct v4l2_streamparm))) {
- kfree(pv4l2_streamparm);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EFAULT;
- }
- kfree(pv4l2_streamparm);
- break;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_PARM: {
- JOM(8, "VIDIOC_S_PARM unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_AUDIO: {
- JOM(8, "VIDIOC_G_AUDIO unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_AUDIO: {
- JOM(8, "VIDIOC_S_AUDIO unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_S_TUNER: {
- JOM(8, "VIDIOC_S_TUNER unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_FBUF:
- case VIDIOC_S_FBUF:
- case VIDIOC_OVERLAY: {
- JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- case VIDIOC_G_TUNER: {
- JOM(8, "VIDIOC_G_TUNER unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
- case VIDIOC_G_FREQUENCY:
- case VIDIOC_S_FREQUENCY: {
- JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -EINVAL;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- default: {
- JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ENOIOCTLCMD;
- }
- }
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd);
- return 0;
-}
-/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c
deleted file mode 100644
index 0380babed22c..000000000000
--- a/drivers/staging/media/easycap/easycap_low.c
+++ /dev/null
@@ -1,968 +0,0 @@
-/*****************************************************************************
-* *
-* *
-* easycap_low.c *
-* *
-* *
-*****************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-/*
- * ACKNOWLEGEMENTS AND REFERENCES
- * ------------------------------
- * This driver makes use of register information contained in the Syntek
- * Semicon DC-1125 driver hosted at
- * http://sourceforge.net/projects/syntekdriver/.
- * Particularly useful has been a patch to the latter driver provided by
- * Ivor Hewitt in January 2009. The NTSC implementation is taken from the
- * work of Ben Trask.
-*/
-/****************************************************************************/
-
-#include "easycap.h"
-
-
-#define GET(X, Y, Z) do { \
- int __rc; \
- *(Z) = (u16)0; \
- __rc = regget(X, Y, Z, sizeof(u8)); \
- if (0 > __rc) { \
- JOT(8, ":-(%i\n", __LINE__); return __rc; \
- } \
-} while (0)
-
-#define SET(X, Y, Z) do { \
- int __rc; \
- __rc = regset(X, Y, Z); \
- if (0 > __rc) { \
- JOT(8, ":-(%i\n", __LINE__); return __rc; \
- } \
-} while (0)
-
-/*--------------------------------------------------------------------------*/
-static const struct stk1160config {
- u16 reg;
- u16 set;
-} stk1160configPAL[] = {
- {0x000, 0x0098},
- {0x002, 0x0093},
-
- {0x001, 0x0003},
- {0x003, 0x0080},
- {0x00D, 0x0000},
- {0x00F, 0x0002},
- {0x018, 0x0010},
- {0x019, 0x0000},
- {0x01A, 0x0014},
- {0x01B, 0x000E},
- {0x01C, 0x0046},
-
- {0x100, 0x0033},
- {0x103, 0x0000},
- {0x104, 0x0000},
- {0x105, 0x0000},
- {0x106, 0x0000},
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * RESOLUTION 640x480
-*/
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {0x110, 0x0008},
- {0x111, 0x0000},
- {0x112, 0x0020},
- {0x113, 0x0000},
- {0x114, 0x0508},
- {0x115, 0x0005},
- {0x116, 0x0110},
- {0x117, 0x0001},
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- {0x202, 0x000F},
- {0x203, 0x004A},
- {0x2FF, 0x0000},
-
- {0xFFF, 0xFFFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct stk1160config stk1160configNTSC[] = {
- {0x000, 0x0098},
- {0x002, 0x0093},
-
- {0x001, 0x0003},
- {0x003, 0x0080},
- {0x00D, 0x0000},
- {0x00F, 0x0002},
- {0x018, 0x0010},
- {0x019, 0x0000},
- {0x01A, 0x0014},
- {0x01B, 0x000E},
- {0x01C, 0x0046},
-
- {0x100, 0x0033},
- {0x103, 0x0000},
- {0x104, 0x0000},
- {0x105, 0x0000},
- {0x106, 0x0000},
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-/*
- * RESOLUTION 640x480
-*/
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {0x110, 0x0008},
- {0x111, 0x0000},
- {0x112, 0x0003},
- {0x113, 0x0000},
- {0x114, 0x0508},
- {0x115, 0x0005},
- {0x116, 0x00F3},
- {0x117, 0x0000},
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- {0x202, 0x000F},
- {0x203, 0x004A},
- {0x2FF, 0x0000},
-
- {0xFFF, 0xFFFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct saa7113config {
- u8 reg;
- u8 set;
-} saa7113configPAL[] = {
- {0x01, 0x08},
- {0x02, 0x80},
- {0x03, 0x33},
- {0x04, 0x00},
- {0x05, 0x00},
- {0x06, 0xE9},
- {0x07, 0x0D},
- {0x08, 0x38},
- {0x09, 0x00},
- {0x0A, SAA_0A_DEFAULT},
- {0x0B, SAA_0B_DEFAULT},
- {0x0C, SAA_0C_DEFAULT},
- {0x0D, SAA_0D_DEFAULT},
- {0x0E, 0x01},
- {0x0F, 0x36},
- {0x10, 0x00},
- {0x11, 0x0C},
- {0x12, 0xE7},
- {0x13, 0x00},
- {0x15, 0x00},
- {0x16, 0x00},
- {0x40, 0x02},
- {0x41, 0xFF},
- {0x42, 0xFF},
- {0x43, 0xFF},
- {0x44, 0xFF},
- {0x45, 0xFF},
- {0x46, 0xFF},
- {0x47, 0xFF},
- {0x48, 0xFF},
- {0x49, 0xFF},
- {0x4A, 0xFF},
- {0x4B, 0xFF},
- {0x4C, 0xFF},
- {0x4D, 0xFF},
- {0x4E, 0xFF},
- {0x4F, 0xFF},
- {0x50, 0xFF},
- {0x51, 0xFF},
- {0x52, 0xFF},
- {0x53, 0xFF},
- {0x54, 0xFF},
- {0x55, 0xFF},
- {0x56, 0xFF},
- {0x57, 0xFF},
- {0x58, 0x40},
- {0x59, 0x54},
- {0x5A, 0x07},
- {0x5B, 0x83},
-
- {0xFF, 0xFF}
-};
-/*--------------------------------------------------------------------------*/
-static const struct saa7113config saa7113configNTSC[] = {
- {0x01, 0x08},
- {0x02, 0x80},
- {0x03, 0x33},
- {0x04, 0x00},
- {0x05, 0x00},
- {0x06, 0xE9},
- {0x07, 0x0D},
- {0x08, 0x78},
- {0x09, 0x00},
- {0x0A, SAA_0A_DEFAULT},
- {0x0B, SAA_0B_DEFAULT},
- {0x0C, SAA_0C_DEFAULT},
- {0x0D, SAA_0D_DEFAULT},
- {0x0E, 0x01},
- {0x0F, 0x36},
- {0x10, 0x00},
- {0x11, 0x0C},
- {0x12, 0xE7},
- {0x13, 0x00},
- {0x15, 0x00},
- {0x16, 0x00},
- {0x40, 0x82},
- {0x41, 0xFF},
- {0x42, 0xFF},
- {0x43, 0xFF},
- {0x44, 0xFF},
- {0x45, 0xFF},
- {0x46, 0xFF},
- {0x47, 0xFF},
- {0x48, 0xFF},
- {0x49, 0xFF},
- {0x4A, 0xFF},
- {0x4B, 0xFF},
- {0x4C, 0xFF},
- {0x4D, 0xFF},
- {0x4E, 0xFF},
- {0x4F, 0xFF},
- {0x50, 0xFF},
- {0x51, 0xFF},
- {0x52, 0xFF},
- {0x53, 0xFF},
- {0x54, 0xFF},
- {0x55, 0xFF},
- {0x56, 0xFF},
- {0x57, 0xFF},
- {0x58, 0x40},
- {0x59, 0x54},
- {0x5A, 0x0A},
- {0x5B, 0x83},
-
- {0xFF, 0xFF}
-};
-
-static int regget(struct usb_device *pusb_device,
- u16 index, void *reg, int reg_size)
-{
- int rc;
-
- if (!pusb_device)
- return -ENODEV;
-
- rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0),
- 0x00,
- (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
- 0x00,
- index, reg, reg_size, 50000);
-
- return rc;
-}
-
-static int regset(struct usb_device *pusb_device, u16 index, u16 value)
-{
- int rc;
-
- if (!pusb_device)
- return -ENODEV;
-
- rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- 0x01,
- (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
- value, index, NULL, 0, 500);
-
- if (rc < 0)
- return rc;
-
- if (easycap_readback) {
- u16 igot = 0;
- rc = regget(pusb_device, index, &igot, sizeof(igot));
- igot = 0xFF & igot;
- switch (index) {
- case 0x000:
- case 0x500:
- case 0x502:
- case 0x503:
- case 0x504:
- case 0x506:
- case 0x507:
- break;
-
- case 0x204:
- case 0x205:
- case 0x350:
- case 0x351:
- if (igot)
- JOT(8, "unexpected 0x%02X "
- "for STK register 0x%03X\n",
- igot, index);
- break;
-
- default:
- if ((0xFF & value) != igot)
- JOT(8, "unexpected 0x%02X != 0x%02X "
- "for STK register 0x%03X\n",
- igot, value, index);
- break;
- }
- }
-
- return rc;
-}
-/*--------------------------------------------------------------------------*/
-/*
- * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS
-*/
-/*--------------------------------------------------------------------------*/
-static int wait_i2c(struct usb_device *p)
-{
- u16 get0;
- u8 igot;
- const int max = 2;
- int k;
-
- if (!p)
- return -ENODEV;
-
- for (k = 0; k < max; k++) {
- GET(p, 0x0201, &igot); get0 = igot;
- switch (get0) {
- case 0x04:
- case 0x01:
- return 0;
- case 0x00:
- msleep(20);
- continue;
- default:
- return get0 - 1;
- }
- }
- return -1;
-}
-
-/****************************************************************************/
-int write_saa(struct usb_device *p, u16 reg0, u16 set0)
-{
- if (!p)
- return -ENODEV;
- SET(p, 0x200, 0x00);
- SET(p, 0x204, reg0);
- SET(p, 0x205, set0);
- SET(p, 0x200, 0x01);
- return wait_i2c(p);
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
- * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
- * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET
- * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET
- * REGISTER 504: TARGET ADDRESS ON VT1612A
- */
-/*--------------------------------------------------------------------------*/
-static int write_vt(struct usb_device *p, u16 reg0, u16 set0)
-{
- u8 igot;
- u16 got502, got503;
- u16 set502, set503;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x0504, reg0);
- SET(p, 0x0500, 0x008B);
-
- GET(p, 0x0502, &igot); got502 = (0xFF & igot);
- GET(p, 0x0503, &igot); got503 = (0xFF & igot);
-
- JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n",
- reg0, set0, ((got503 << 8) | got502));
-
- set502 = (0x00FF & set0);
- set503 = ((0xFF00 & set0) >> 8);
-
- SET(p, 0x0504, reg0);
- SET(p, 0x0502, set502);
- SET(p, 0x0503, set503);
- SET(p, 0x0500, 0x008C);
-
- return 0;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
- * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
- * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET
- * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET
- * REGISTER 504: TARGET ADDRESS ON VT1612A
- */
-/*--------------------------------------------------------------------------*/
-static int read_vt(struct usb_device *p, u16 reg0)
-{
- u8 igot;
- u16 got502, got503;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x0504, reg0);
- SET(p, 0x0500, 0x008B);
-
- GET(p, 0x0502, &igot); got502 = (0xFF & igot);
- GET(p, 0x0503, &igot); got503 = (0xFF & igot);
-
- JOT(16, "read_vt(., 0x%04X): has 0x%04X\n",
- reg0, ((got503 << 8) | got502));
-
- return (got503 << 8) | got502;
-}
-/****************************************************************************/
-/*--------------------------------------------------------------------------*/
-/*
- * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO.
- */
-/*--------------------------------------------------------------------------*/
-static int write_300(struct usb_device *p)
-{
- if (!p)
- return -ENODEV;
- SET(p, 0x300, 0x0012);
- SET(p, 0x350, 0x002D);
- SET(p, 0x351, 0x0001);
- SET(p, 0x352, 0x0000);
- SET(p, 0x353, 0x0000);
- SET(p, 0x300, 0x0080);
- return 0;
-}
-/****************************************************************************/
-/****************************************************************************/
-int setup_stk(struct usb_device *p, bool ntsc)
-{
- int i;
- const struct stk1160config *cfg;
- if (!p)
- return -ENODEV;
- cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
- for (i = 0; cfg[i].reg != 0xFFF; i++)
- SET(p, cfg[i].reg, cfg[i].set);
-
- write_300(p);
-
- return 0;
-}
-/****************************************************************************/
-int setup_saa(struct usb_device *p, bool ntsc)
-{
- int i, rc;
- const struct saa7113config *cfg;
- if (!p)
- return -ENODEV;
- cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
- for (i = 0; cfg[i].reg != 0xFF; i++) {
- rc = write_saa(p, cfg[i].reg, cfg[i].set);
- if (rc)
- dev_err(&p->dev,
- "Failed to set SAA register %d", cfg[i].reg);
- }
- return 0;
-}
-/****************************************************************************/
-int merit_saa(struct usb_device *p)
-{
- int rc;
-
- if (!p)
- return -ENODEV;
- rc = read_saa(p, 0x1F);
- return ((0 > rc) || (0x02 & rc)) ? 1 : 0;
-}
-/****************************************************************************/
-int ready_saa(struct usb_device *p)
-{
- int j, rc, rate;
- const int max = 5, marktime = PATIENCE/5;
-/*--------------------------------------------------------------------------*/
-/*
- * RETURNS 0 FOR INTERLACED 50 Hz
- * 1 FOR NON-INTERLACED 50 Hz
- * 2 FOR INTERLACED 60 Hz
- * 3 FOR NON-INTERLACED 60 Hz
-*/
-/*--------------------------------------------------------------------------*/
- if (!p)
- return -ENODEV;
- j = 0;
- while (max > j) {
- rc = read_saa(p, 0x1F);
- if (0 <= rc) {
- if (0 == (0x40 & rc))
- break;
- if (1 == (0x01 & rc))
- break;
- }
- msleep(marktime);
- j++;
- }
-
- if (max == j)
- return -1;
-
- if (0x20 & rc) {
- rate = 2;
- JOT(8, "hardware detects 60 Hz\n");
- } else {
- rate = 0;
- JOT(8, "hardware detects 50 Hz\n");
- }
- if (0x80 & rc)
- JOT(8, "hardware detects interlacing\n");
- else {
- rate++;
- JOT(8, "hardware detects no interlacing\n");
- }
- return 0;
-}
-/****************************************************************************/
-int read_saa(struct usb_device *p, u16 reg0)
-{
- u8 igot;
-
- if (!p)
- return -ENODEV;
- SET(p, 0x208, reg0);
- SET(p, 0x200, 0x20);
- if (0 != wait_i2c(p))
- return -1;
- igot = 0;
- GET(p, 0x0209, &igot);
- return igot;
-}
-/****************************************************************************/
-static int read_stk(struct usb_device *p, u32 reg0)
-{
- u8 igot;
-
- if (!p)
- return -ENODEV;
- igot = 0;
- GET(p, reg0, &igot);
- return igot;
-}
-int select_input(struct usb_device *p, int input, int mode)
-{
- int ir;
-
- if (!p)
- return -ENODEV;
- stop_100(p);
- switch (input) {
- case 0:
- case 1: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- SET(p, 0x0000, 0x0098);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 2: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- SET(p, 0x0000, 0x0090);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 3: {
- if (0 != write_saa(p, 0x02, 0x80))
- SAY("ERROR: failed to set SAA register 0x02 "
- " for input %i\n", input);
-
- SET(p, 0x0000, 0x0088);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 4: {
- if (0 != write_saa(p, 0x02, 0x80)) {
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
- }
- SET(p, 0x0000, 0x0080);
- SET(p, 0x0002, 0x0078);
- break;
- }
- case 5: {
- if (9 != mode)
- mode = 7;
- switch (mode) {
- case 7: {
- if (0 != write_saa(p, 0x02, 0x87))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x05, 0xFF))
- SAY("ERROR: failed to set SAA register 0x05 "
- "for input %i\n", input);
-
- break;
- }
- case 9: {
- if (0 != write_saa(p, 0x02, 0x89))
- SAY("ERROR: failed to set SAA register 0x02 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x05, 0x00))
- SAY("ERROR: failed to set SAA register 0x05 "
- "for input %i\n", input);
-
- break;
- }
- default:
- SAY("MISTAKE: bad mode: %i\n", mode);
- return -1;
- }
-
- if (0 != write_saa(p, 0x04, 0x00))
- SAY("ERROR: failed to set SAA register 0x04 "
- "for input %i\n", input);
-
- if (0 != write_saa(p, 0x09, 0x80))
- SAY("ERROR: failed to set SAA register 0x09 "
- "for input %i\n", input);
-
- SET(p, 0x0002, 0x0093);
- break;
- }
- default:
- SAY("ERROR: bad input: %i\n", input);
- return -1;
- }
-
- ir = read_stk(p, 0x00);
- JOT(8, "STK register 0x00 has 0x%02X\n", ir);
- ir = read_saa(p, 0x02);
- JOT(8, "SAA register 0x02 has 0x%02X\n", ir);
-
- start_100(p);
-
- return 0;
-}
-/****************************************************************************/
-int set_resolution(struct usb_device *p,
- u16 set0, u16 set1, u16 set2, u16 set3)
-{
- u16 u0x0111, u0x0113, u0x0115, u0x0117;
-
- if (!p)
- return -ENODEV;
- u0x0111 = ((0xFF00 & set0) >> 8);
- u0x0113 = ((0xFF00 & set1) >> 8);
- u0x0115 = ((0xFF00 & set2) >> 8);
- u0x0117 = ((0xFF00 & set3) >> 8);
-
- SET(p, 0x0110, (0x00FF & set0));
- SET(p, 0x0111, u0x0111);
- SET(p, 0x0112, (0x00FF & set1));
- SET(p, 0x0113, u0x0113);
- SET(p, 0x0114, (0x00FF & set2));
- SET(p, 0x0115, u0x0115);
- SET(p, 0x0116, (0x00FF & set3));
- SET(p, 0x0117, u0x0117);
-
- return 0;
-}
-/****************************************************************************/
-int start_100(struct usb_device *p)
-{
- u16 get116, get117, get0;
- u8 igot116, igot117, igot;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0116, &igot116);
- get116 = igot116;
- GET(p, 0x0117, &igot117);
- get117 = igot117;
- SET(p, 0x0116, 0x0000);
- SET(p, 0x0117, 0x0000);
-
- GET(p, 0x0100, &igot);
- get0 = igot;
- SET(p, 0x0100, (0x80 | get0));
-
- SET(p, 0x0116, get116);
- SET(p, 0x0117, get117);
-
- return 0;
-}
-/****************************************************************************/
-int stop_100(struct usb_device *p)
-{
- u16 get0;
- u8 igot;
-
- if (!p)
- return -ENODEV;
- GET(p, 0x0100, &igot);
- get0 = igot;
- SET(p, 0x0100, (0x7F & get0));
- return 0;
-}
-/****************************************************************************/
-/****************************************************************************/
-/*****************************************************************************/
-int easycap_wakeup_device(struct usb_device *pusb_device)
-{
- if (!pusb_device)
- return -ENODEV;
-
- return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- USB_REQ_SET_FEATURE,
- USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- USB_DEVICE_REMOTE_WAKEUP,
- 0, NULL, 0, 50000);
-}
-/*****************************************************************************/
-int easycap_audio_setup(struct easycap *peasycap)
-{
- struct usb_device *pusb_device;
- u8 buffer[1];
- int rc, id1, id2;
-/*---------------------------------------------------------------------------*/
-/*
- * IMPORTANT:
- * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
- * CAUSES MUTING IF THE VALUE 0x0100 IS SENT.
- * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT.
- */
-/*---------------------------------------------------------------------------*/
- const u8 request = 0x01;
- const u8 requesttype = USB_DIR_OUT |
- USB_TYPE_CLASS |
- USB_RECIP_INTERFACE;
- const u16 value_unmute = 0x0200;
- const u16 index = 0x0301;
- const u16 length = 1;
-
- if (!peasycap)
- return -EFAULT;
-
- pusb_device = peasycap->pusb_device;
- if (!pusb_device)
- return -ENODEV;
-
- JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n",
- requesttype, request,
- (0x00FF & value_unmute),
- (0xFF00 & value_unmute) >> 8,
- (0x00FF & index),
- (0xFF00 & index) >> 8,
- (0x00FF & length),
- (0xFF00 & length) >> 8);
-
- buffer[0] = 0x01;
-
- rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
- request, requesttype, value_unmute,
- index, &buffer[0], length, 50000);
-
- JOT(8, "0x%02X=buffer\n", buffer[0]);
- if (rc != (int)length) {
- switch (rc) {
- case -EPIPE:
- SAY("usb_control_msg returned -EPIPE\n");
- break;
- default:
- SAY("ERROR: usb_control_msg returned %i\n", rc);
- break;
- }
- }
-/*--------------------------------------------------------------------------*/
-/*
- * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ???
- * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ???
- * FOR THE CVBS+S-VIDEO HARDWARE:
- * SETTING VALUE TO 0x0000 GIVES QUIET SOUND.
- * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
- * FOR THE FOUR-CVBS HARDWARE:
- * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT.
- * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ???
- * FOR THE CVBS-S-VIDEO HARDWARE:
- * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND.
- * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
- */
-/*--------------------------------------------------------------------------*/
- SET(pusb_device, 0x0500, 0x0094);
- SET(pusb_device, 0x0500, 0x008C);
- SET(pusb_device, 0x0506, 0x0001);
- SET(pusb_device, 0x0507, 0x0000);
- id1 = read_vt(pusb_device, 0x007C);
- id2 = read_vt(pusb_device, 0x007E);
- SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2);
-/*---------------------------------------------------------------------------*/
-/*
- * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN.
-*/
-/*---------------------------------------------------------------------------*/
- if (easycap_audio_gainset(pusb_device, peasycap->gain))
- SAY("ERROR: audio_gainset() failed\n");
- check_vt(pusb_device);
- return 0;
-}
-/*****************************************************************************/
-int check_vt(struct usb_device *pusb_device)
-{
- int igot;
-
- if (!pusb_device)
- return -ENODEV;
- igot = read_vt(pusb_device, 0x0002);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x02\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x02);
-
- igot = read_vt(pusb_device, 0x000E);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x0E\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x0E);
-
- igot = read_vt(pusb_device, 0x0010);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x10\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x10);
-
- igot = read_vt(pusb_device, 0x0012);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x12\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x12);
-
- igot = read_vt(pusb_device, 0x0014);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x14\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x14);
-
- igot = read_vt(pusb_device, 0x0016);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x16\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x16);
-
- igot = read_vt(pusb_device, 0x0018);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x18\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x18);
-
- igot = read_vt(pusb_device, 0x001C);
- if (0 > igot)
- SAY("ERROR: failed to read VT1612A register 0x1C\n");
- if (0x8000 & igot)
- SAY("register 0x%02X muted\n", 0x1C);
-
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY:
- * audio_gainset(pusb_device, 0x000F);
- *
- * loud dB register 0x10 dB register 0x1C dB total
- * 0 -34.5 0 -34.5
- * .. .... . ....
- * 15 10.5 0 10.5
- * 16 12.0 0 12.0
- * 17 12.0 1.5 13.5
- * .. .... .... ....
- * 31 12.0 22.5 34.5
-*/
-/*---------------------------------------------------------------------------*/
-int easycap_audio_gainset(struct usb_device *pusb_device, s8 loud)
-{
- int igot;
- u8 tmp;
- u16 mute;
-
- if (!pusb_device)
- return -ENODEV;
- if (0 > loud)
- loud = 0;
- if (31 < loud)
- loud = 31;
-
- write_vt(pusb_device, 0x0002, 0x8000);
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x000E);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x0E\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- if (16 > loud)
- tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1));
- else
- tmp = 0;
-
- JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp);
- write_vt(pusb_device, 0x000E, (mute | tmp));
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x0010);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x10\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n",
- mute | tmp | (tmp << 8));
- write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8)));
-/*---------------------------------------------------------------------------*/
- igot = read_vt(pusb_device, 0x001C);
- if (0 > igot) {
- SAY("ERROR: failed to read VT1612A register 0x1C\n");
- mute = 0x0000;
- } else
- mute = 0x8000 & ((unsigned int)igot);
- mute = 0;
-
- if (16 <= loud)
- tmp = 0x000F & (u8)(loud - 16);
- else
- tmp = 0;
-
- JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n",
- mute | tmp | (tmp << 8));
- write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8)));
- write_vt(pusb_device, 0x001A, 0x0404);
- write_vt(pusb_device, 0x0002, 0x0000);
- return 0;
-}
-/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
deleted file mode 100644
index 8269c77dbf7d..000000000000
--- a/drivers/staging/media/easycap/easycap_main.c
+++ /dev/null
@@ -1,4239 +0,0 @@
-/******************************************************************************
-* *
-* easycap_main.c *
-* *
-* Video driver for EasyCAP USB2.0 Video Capture Device DC60 *
-* *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-#include <linux/usb/audio.h>
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
-MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
-MODULE_VERSION(EASYCAP_DRIVER_VERSION);
-
-#ifdef CONFIG_EASYCAP_DEBUG
-int easycap_debug;
-module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9");
-#endif /* CONFIG_EASYCAP_DEBUG */
-
-bool easycap_readback;
-module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(readback, "read back written registers: (default false)");
-
-static int easycap_bars = 1;
-module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(bars,
- "Testcard bars on input signal failure: 0=>no, 1=>yes(default)");
-
-static int easycap_gain = 16;
-module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31");
-
-static bool easycap_ntsc;
-module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)");
-
-
-
-struct easycap_dongle easycapdc60_dongle[DONGLE_MANY];
-static struct mutex mutex_dongle;
-static void easycap_complete(struct urb *purb);
-static int reset(struct easycap *peasycap);
-static int field2frame(struct easycap *peasycap);
-static int redaub(struct easycap *peasycap,
- void *pad, void *pex, int much, int more,
- u8 mask, u8 margin, bool isuy);
-
-const char *strerror(int err)
-{
-#define ERRNOSTR(_e) case _e: return # _e
- switch (err) {
- case 0: return "OK";
- ERRNOSTR(ENOMEM);
- ERRNOSTR(ENODEV);
- ERRNOSTR(ENXIO);
- ERRNOSTR(EINVAL);
- ERRNOSTR(EAGAIN);
- ERRNOSTR(EFBIG);
- ERRNOSTR(EPIPE);
- ERRNOSTR(EMSGSIZE);
- ERRNOSTR(ENOSPC);
- ERRNOSTR(EINPROGRESS);
- ERRNOSTR(ENOSR);
- ERRNOSTR(EOVERFLOW);
- ERRNOSTR(EPROTO);
- ERRNOSTR(EILSEQ);
- ERRNOSTR(ETIMEDOUT);
- ERRNOSTR(EOPNOTSUPP);
- ERRNOSTR(EPFNOSUPPORT);
- ERRNOSTR(EAFNOSUPPORT);
- ERRNOSTR(EADDRINUSE);
- ERRNOSTR(EADDRNOTAVAIL);
- ERRNOSTR(ENOBUFS);
- ERRNOSTR(EISCONN);
- ERRNOSTR(ENOTCONN);
- ERRNOSTR(ESHUTDOWN);
- ERRNOSTR(ENOENT);
- ERRNOSTR(ECONNRESET);
- ERRNOSTR(ETIME);
- ERRNOSTR(ECOMM);
- ERRNOSTR(EREMOTEIO);
- ERRNOSTR(EXDEV);
- ERRNOSTR(EPERM);
- default: return "unknown";
- }
-
-#undef ERRNOSTR
-}
-
-/****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap
-*/
-/*---------------------------------------------------------------------------*/
-int easycap_isdongle(struct easycap *peasycap)
-{
- int k;
- if (!peasycap)
- return -2;
- for (k = 0; k < DONGLE_MANY; k++) {
- if (easycapdc60_dongle[k].peasycap == peasycap) {
- peasycap->isdongle = k;
- return k;
- }
- }
- return -1;
-}
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-static int easycap_open(struct inode *inode, struct file *file)
-{
- struct video_device *pvideo_device;
- struct easycap *peasycap;
- int rc;
-
- JOT(4, "\n");
- SAY("==========OPEN=========\n");
-
- pvideo_device = video_devdata(file);
- if (!pvideo_device) {
- SAY("ERROR: pvideo_device is NULL.\n");
- return -EFAULT;
- }
- peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
-
- JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device);
-
- file->private_data = peasycap;
- rc = easycap_wakeup_device(peasycap->pusb_device);
- if (rc) {
- SAM("ERROR: wakeup_device() rc = %i\n", rc);
- if (-ENODEV == rc)
- SAM("ERROR: wakeup_device() returned -ENODEV\n");
- else
- SAM("ERROR: wakeup_device() rc = %i\n", rc);
- return rc;
- }
- JOM(8, "wakeup_device() OK\n");
- peasycap->input = 0;
- rc = reset(peasycap);
- if (rc) {
- SAM("ERROR: reset() rc = %i\n", rc);
- return -EFAULT;
- }
- return 0;
-}
-
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * RESET THE HARDWARE TO ITS REFERENCE STATE.
- *
- * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS
- * A BAD VIDEO FRAME SIZE.
-*/
-/*---------------------------------------------------------------------------*/
-static int reset(struct easycap *peasycap)
-{
- struct easycap_standard const *peasycap_standard;
- int fmtidx, input, rate;
- bool ntsc, other;
- int rc;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- input = peasycap->input;
-
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
- * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR
- * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
- * A SWITCH BETWEEN PAL AND NTSC.
- *
- * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO
- * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON.
-*/
-/*---------------------------------------------------------------------------*/
- other = false;
- JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc);
-
- rate = ready_saa(peasycap->pusb_device);
- if (rate < 0) {
- JOM(8, "not ready to capture after %i ms ...\n", PATIENCE);
- ntsc = !peasycap->ntsc;
- JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL");
- rc = setup_stk(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_stk() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = setup_saa(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_saa() rc = %i\n", rc);
- return -EFAULT;
- }
-
- rate = ready_saa(peasycap->pusb_device);
- if (rate < 0) {
- JOM(8, "not ready to capture after %i ms\n", PATIENCE);
- JOM(8, "... saa register 0x1F has 0x%02X\n",
- read_saa(peasycap->pusb_device, 0x1F));
- ntsc = peasycap->ntsc;
- } else {
- JOM(8, "... success at second try: %i=rate\n", rate);
- ntsc = (0 < (rate/2)) ? true : false ;
- other = true;
- }
- } else {
- JOM(8, "... success at first try: %i=rate\n", rate);
- ntsc = (0 < rate/2) ? true : false ;
- }
- JOM(8, "ntsc=%d\n", ntsc);
-/*---------------------------------------------------------------------------*/
-
- rc = setup_stk(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_stk() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = setup_saa(peasycap->pusb_device, ntsc);
- if (rc) {
- SAM("ERROR: setup_saa() rc = %i\n", rc);
- return -EFAULT;
- }
-
- memset(peasycap->merit, 0, sizeof(peasycap->merit));
-
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
-/*---------------------------------------------------------------------------*/
-/*
- * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC.
- *
- * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY.
-*/
-/*---------------------------------------------------------------------------*/
- peasycap->input = -8192;
- peasycap->standard_offset = -8192;
- fmtidx = ntsc ? NTSC_M : PAL_BGHIN;
- if (other) {
- peasycap_standard = &easycap_standard[0];
- while (0xFFFF != peasycap_standard->mask) {
- if (fmtidx == peasycap_standard->v4l2_standard.index) {
- peasycap->inputset[input].standard_offset =
- peasycap_standard - easycap_standard;
- break;
- }
- peasycap_standard++;
- }
- if (0xFFFF == peasycap_standard->mask) {
- SAM("ERROR: standard not found\n");
- return -EINVAL;
- }
- JOM(8, "%i=peasycap->inputset[%i].standard_offset\n",
- peasycap->inputset[input].standard_offset, input);
- }
- peasycap->format_offset = -8192;
- peasycap->brightness = -8192;
- peasycap->contrast = -8192;
- peasycap->saturation = -8192;
- peasycap->hue = -8192;
-
- rc = easycap_newinput(peasycap, input);
-
- if (rc) {
- SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input);
- return -EFAULT;
- }
- JOM(4, "restored input, standard and format\n");
-
- JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc);
-
- if (0 > peasycap->input) {
- SAM("MISTAKE: %i=peasycap->input\n", peasycap->input);
- return -ENOENT;
- }
- if (0 > peasycap->standard_offset) {
- SAM("MISTAKE: %i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- return -ENOENT;
- }
- if (0 > peasycap->format_offset) {
- SAM("MISTAKE: %i=peasycap->format_offset\n",
- peasycap->format_offset);
- return -ENOENT;
- }
- if (0 > peasycap->brightness) {
- SAM("MISTAKE: %i=peasycap->brightness\n",
- peasycap->brightness);
- return -ENOENT;
- }
- if (0 > peasycap->contrast) {
- SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast);
- return -ENOENT;
- }
- if (0 > peasycap->saturation) {
- SAM("MISTAKE: %i=peasycap->saturation\n",
- peasycap->saturation);
- return -ENOENT;
- }
- if (0 > peasycap->hue) {
- SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue);
- return -ENOENT;
- }
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING.
- * OTHERWISE:
- * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR
- * _read AND _fill POINTERS.
- * SELECT THE NEW INPUT.
- * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE
- * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input].
- * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS.
- *
- * NOTE:
- * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL,
- * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE.
-*/
-/*---------------------------------------------------------------------------*/
-int easycap_newinput(struct easycap *peasycap, int input)
-{
- int rc, k, m, mood, off;
- int inputnow, video_idlenow, audio_idlenow;
- bool resubmit;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- JOM(8, "%i=input sought\n", input);
-
- if (0 > input && INPUT_MANY <= input)
- return -ENOENT;
- inputnow = peasycap->input;
- if (input == inputnow)
- return 0;
-/*---------------------------------------------------------------------------*/
-/*
- * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS
- * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE.
- * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE
- * ROUTINE.
-*/
-/*---------------------------------------------------------------------------*/
- video_idlenow = peasycap->video_idle;
- audio_idlenow = peasycap->audio_idle;
-
- peasycap->video_idle = 1;
- peasycap->audio_idle = 1;
- if (peasycap->video_isoc_streaming) {
- resubmit = true;
- easycap_video_kill_urbs(peasycap);
- } else {
- resubmit = false;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->video_interface,
- peasycap->video_altsetting_off);
- if (rc) {
- SAM("ERROR: usb_set_interface() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = stop_100(peasycap->pusb_device);
- if (rc) {
- SAM("ERROR: stop_100() rc = %i\n", rc);
- return -EFAULT;
- }
- for (k = 0; k < FIELD_BUFFER_MANY; k++) {
- for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++)
- memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE);
- }
- for (k = 0; k < FRAME_BUFFER_MANY; k++) {
- for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++)
- memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE);
- }
- peasycap->field_page = 0;
- peasycap->field_read = 0;
- peasycap->field_fill = 0;
-
- peasycap->frame_read = 0;
- peasycap->frame_fill = 0;
- for (k = 0; k < peasycap->input; k++) {
- (peasycap->frame_fill)++;
- if (peasycap->frame_buffer_many <= peasycap->frame_fill)
- peasycap->frame_fill = 0;
- }
- peasycap->input = input;
- select_input(peasycap->pusb_device, peasycap->input, 9);
-/*---------------------------------------------------------------------------*/
- if (input == peasycap->inputset[input].input) {
- off = peasycap->inputset[input].standard_offset;
- if (off != peasycap->standard_offset) {
- rc = adjust_standard(peasycap,
- easycap_standard[off].v4l2_standard.id);
- if (rc) {
- SAM("ERROR: adjust_standard() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->standard_offset\n",
- peasycap->standard_offset);
- } else {
- JOM(8, "%i=peasycap->standard_offset unchanged\n",
- peasycap->standard_offset);
- }
- off = peasycap->inputset[input].format_offset;
- if (off != peasycap->format_offset) {
- struct v4l2_pix_format *pix =
- &easycap_format[off].v4l2_format.fmt.pix;
- rc = adjust_format(peasycap,
- pix->width, pix->height,
- pix->pixelformat, pix->field, false);
- if (0 > rc) {
- SAM("ERROR: adjust_format() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->format_offset\n",
- peasycap->format_offset);
- } else {
- JOM(8, "%i=peasycap->format_offset unchanged\n",
- peasycap->format_offset);
- }
- mood = peasycap->inputset[input].brightness;
- if (mood != peasycap->brightness) {
- rc = adjust_brightness(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_brightness rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->brightness\n",
- peasycap->brightness);
- }
- mood = peasycap->inputset[input].contrast;
- if (mood != peasycap->contrast) {
- rc = adjust_contrast(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_contrast rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->contrast\n", peasycap->contrast);
- }
- mood = peasycap->inputset[input].saturation;
- if (mood != peasycap->saturation) {
- rc = adjust_saturation(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_saturation rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->saturation\n",
- peasycap->saturation);
- }
- mood = peasycap->inputset[input].hue;
- if (mood != peasycap->hue) {
- rc = adjust_hue(peasycap, mood);
- if (rc) {
- SAM("ERROR: adjust_hue rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(8, "%i=peasycap->hue\n", peasycap->hue);
- }
- } else {
- SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input);
- return -ENOENT;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->video_interface,
- peasycap->video_altsetting_on);
- if (rc) {
- SAM("ERROR: usb_set_interface() rc = %i\n", rc);
- return -EFAULT;
- }
- rc = start_100(peasycap->pusb_device);
- if (rc) {
- SAM("ERROR: start_100() rc = %i\n", rc);
- return -EFAULT;
- }
- if (resubmit)
- easycap_video_submit_urbs(peasycap);
-
- peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1;
- peasycap->video_idle = video_idlenow;
- peasycap->audio_idle = audio_idlenow;
- peasycap->video_junk = 0;
-
- return 0;
-}
-/*****************************************************************************/
-int easycap_video_submit_urbs(struct easycap *peasycap)
-{
- struct data_urb *pdata_urb;
- struct urb *purb;
- struct list_head *plist_head;
- int j, isbad, nospc, m, rc;
- int isbuf;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- if (!peasycap->purb_video_head) {
- SAY("ERROR: peasycap->urb_video_head uninitialized\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- if (!peasycap->video_isoc_streaming) {
- JOM(4, "submission of all video urbs\n");
- isbad = 0; nospc = 0; m = 0;
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- purb = pdata_urb->purb;
- isbuf = pdata_urb->isbuf;
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe =
- usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->video_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer =
- peasycap->video_isoc_buffer[isbuf].pgo;
- purb->transfer_buffer_length =
- peasycap->video_isoc_buffer_size;
- purb->complete = easycap_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets =
- peasycap->video_isoc_framesperdesc;
-
- for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j]. offset =
- j * peasycap->video_isoc_maxframesize;
- purb->iso_frame_desc[j]. length =
- peasycap->video_isoc_maxframesize;
- }
-
- rc = usb_submit_urb(purb, GFP_KERNEL);
- if (rc) {
- isbad++;
- SAM("ERROR: usb_submit_urb() failed "
- "for urb with rc:-%s\n",
- strerror(rc));
- if (rc == -ENOSPC)
- nospc++;
- } else {
- m++;
- }
- } else {
- isbad++;
- }
- }
- if (nospc) {
- SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
- SAM("..... possibly inadequate USB bandwidth\n");
- peasycap->video_eof = 1;
- }
-
- if (isbad)
- easycap_video_kill_urbs(peasycap);
- else
- peasycap->video_isoc_streaming = 1;
- } else {
- JOM(4, "already streaming video urbs\n");
- }
- return 0;
-}
-/*****************************************************************************/
-int easycap_audio_kill_urbs(struct easycap *peasycap)
-{
- int m;
- struct list_head *plist_head;
- struct data_urb *pdata_urb;
-
- if (!peasycap->audio_isoc_streaming)
- return 0;
-
- if (!peasycap->purb_audio_head) {
- SAM("ERROR: peasycap->purb_audio_head is NULL\n");
- return -EFAULT;
- }
-
- peasycap->audio_isoc_streaming = 0;
- m = 0;
- list_for_each(plist_head, peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
-
- JOM(4, "%i audio urbs killed\n", m);
-
- return 0;
-}
-int easycap_video_kill_urbs(struct easycap *peasycap)
-{
- int m;
- struct list_head *plist_head;
- struct data_urb *pdata_urb;
-
- if (!peasycap->video_isoc_streaming)
- return 0;
-
- if (!peasycap->purb_video_head) {
- SAM("ERROR: peasycap->purb_video_head is NULL\n");
- return -EFAULT;
- }
-
- peasycap->video_isoc_streaming = 0;
- JOM(4, "killing video urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_video_head)) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_kill_urb(pdata_urb->purb);
- m++;
- }
- }
- JOM(4, "%i video urbs killed\n", m);
-
- return 0;
-}
-/****************************************************************************/
-/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
-/*--------------------------------------------------------------------------*/
-static int easycap_open_noinode(struct file *file)
-{
- return easycap_open(NULL, file);
-}
-
-static int videodev_release(struct video_device *pvideo_device)
-{
- struct easycap *peasycap;
-
- peasycap = video_get_drvdata(pvideo_device);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- SAY("ending unsuccessfully\n");
- return -EFAULT;
- }
- if (easycap_video_kill_urbs(peasycap)) {
- SAM("ERROR: easycap_video_kill_urbs() failed\n");
- return -EFAULT;
- }
- JOM(4, "ending successfully\n");
- return 0;
-}
-
-/*****************************************************************************/
-static unsigned int easycap_poll(struct file *file, poll_table *wait)
-{
- struct easycap *peasycap;
- int rc, kd;
-
- JOT(8, "\n");
-
- if (NULL == ((poll_table *)wait))
- JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
- if (!file) {
- SAY("ERROR: file pointer is NULL\n");
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
- kd = easycap_isdongle(peasycap);
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd);
- return -ERESTARTSYS;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- /*
- * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
- * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
- * IF NECESSARY, BAIL OUT.
- */
- if (kd != easycap_isdongle(peasycap)) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!file) {
- SAY("ERROR: file is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- peasycap = file->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return -ERESTARTSYS;
- }
- } else
- /*
- * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
- * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
- * HAVE FAILED. BAIL OUT.
- */
- return -ERESTARTSYS;
-/*---------------------------------------------------------------------------*/
- rc = easycap_video_dqbuf(peasycap, 0);
- peasycap->polled = 1;
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- if (rc)
- return POLLERR;
-
- return POLLIN | POLLRDNORM;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING.
- */
-/*---------------------------------------------------------------------------*/
-int easycap_video_dqbuf(struct easycap *peasycap, int mode)
-{
- int input, ifield, miss, rc;
-
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAY("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
- ifield = 0;
- JOM(8, "%i=ifield\n", ifield);
-/*---------------------------------------------------------------------------*/
-/*
- * CHECK FOR LOST INPUT SIGNAL.
- *
- * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
- * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
- * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
- * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS:
- *
- * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
- * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
- * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK
- * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS
-*/
-/*---------------------------------------------------------------------------*/
- input = peasycap->input;
- if (0 <= input && INPUT_MANY > input) {
- rc = read_saa(peasycap->pusb_device, 0x1F);
- if (0 <= rc) {
- if (rc & 0x40)
- peasycap->lost[input] += 1;
- else
- peasycap->lost[input] -= 2;
-
- if (0 > peasycap->lost[input])
- peasycap->lost[input] = 0;
- else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input])
- peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM)
- */
-/*---------------------------------------------------------------------------*/
- miss = 0;
- while ((peasycap->field_read == peasycap->field_fill) ||
- (0 != (0xFF00 & peasycap->field_buffer
- [peasycap->field_read][0].kount)) ||
- (ifield != (0x00FF & peasycap->field_buffer
- [peasycap->field_read][0].kount))) {
- if (mode)
- return -EAGAIN;
-
- JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n",
- peasycap->field_read, peasycap->field_fill);
-
- if (0 != (wait_event_interruptible(peasycap->wq_video,
- (peasycap->video_idle || peasycap->video_eof ||
- ((peasycap->field_read != peasycap->field_fill) &&
- (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
- (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
- SAM("aborted by signal\n");
- return -EIO;
- }
- if (peasycap->video_idle) {
- JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
- peasycap->video_idle);
- return -EAGAIN;
- }
- if (peasycap->video_eof) {
- JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
- #if defined(PERSEVERE)
- if (1 == peasycap->status) {
- JOM(8, "persevering ...\n");
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- if (0 != reset(peasycap)) {
- JOM(8, " ... failed returning -EIO\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- easycap_video_kill_urbs(peasycap);
- return -EIO;
- }
- peasycap->status = 0;
- JOM(8, " ... OK returning -EAGAIN\n");
- return -EAGAIN;
- }
- #endif /*PERSEVERE*/
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- easycap_video_kill_urbs(peasycap);
- JOM(8, "returning -EIO\n");
- return -EIO;
- }
- miss++;
- }
- JOM(8, "first awakening on wq_video after %i waits\n", miss);
-
- rc = field2frame(peasycap);
- if (rc)
- SAM("ERROR: field2frame() rc = %i\n", rc);
-/*---------------------------------------------------------------------------*/
-/*
- * WAIT FOR THE OTHER FIELD
- */
-/*---------------------------------------------------------------------------*/
- if (ifield)
- ifield = 0;
- else
- ifield = 1;
- miss = 0;
- while ((peasycap->field_read == peasycap->field_fill) ||
- (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) ||
- (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) {
- if (mode)
- return -EAGAIN;
-
- JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n",
- peasycap->field_read, peasycap->field_fill);
- if (0 != (wait_event_interruptible(peasycap->wq_video,
- (peasycap->video_idle || peasycap->video_eof ||
- ((peasycap->field_read != peasycap->field_fill) &&
- (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
- (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
- SAM("aborted by signal\n");
- return -EIO;
- }
- if (peasycap->video_idle) {
- JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
- peasycap->video_idle);
- return -EAGAIN;
- }
- if (peasycap->video_eof) {
- JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
-#if defined(PERSEVERE)
- if (1 == peasycap->status) {
- JOM(8, "persevering ...\n");
- peasycap->video_eof = 0;
- peasycap->audio_eof = 0;
- if (0 != reset(peasycap)) {
- JOM(8, " ... failed returning -EIO\n");
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- easycap_video_kill_urbs(peasycap);
- return -EIO;
- }
- peasycap->status = 0;
- JOM(8, " ... OK ... returning -EAGAIN\n");
- return -EAGAIN;
- }
-#endif /*PERSEVERE*/
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- easycap_video_kill_urbs(peasycap);
- JOM(8, "returning -EIO\n");
- return -EIO;
- }
- miss++;
- }
- JOM(8, "second awakening on wq_video after %i waits\n", miss);
-
- rc = field2frame(peasycap);
- if (rc)
- SAM("ERROR: field2frame() rc = %i\n", rc);
-/*---------------------------------------------------------------------------*/
-/*
- * WASTE THIS FRAME
-*/
-/*---------------------------------------------------------------------------*/
- if (peasycap->skip) {
- peasycap->skipped++;
- if (peasycap->skip != peasycap->skipped)
- return peasycap->skip - peasycap->skipped;
- else
- peasycap->skipped = 0;
- }
-/*---------------------------------------------------------------------------*/
- peasycap->frame_read = peasycap->frame_fill;
- peasycap->queued[peasycap->frame_read] = 0;
- peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE;
-
- peasycap->frame_fill++;
- if (peasycap->frame_buffer_many <= peasycap->frame_fill)
- peasycap->frame_fill = 0;
-
- if (0x01 & easycap_standard[peasycap->standard_offset].mask)
- peasycap->frame_buffer[peasycap->frame_read][0].kount =
- V4L2_FIELD_TOP;
- else
- peasycap->frame_buffer[peasycap->frame_read][0].kount =
- V4L2_FIELD_BOTTOM;
-
-
- JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
- JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill);
-
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479
- * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478
- *
- * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH
- * odd==false IS TRANSFERRED TO THE FRAME BUFFER.
- *
- */
-/*---------------------------------------------------------------------------*/
-static int field2frame(struct easycap *peasycap)
-{
-
- void *pex, *pad;
- int kex, kad, mex, mad, rex, rad, rad2;
- int c2, c3, w2, w3, cz, wz;
- int rc, bytesperpixel, multiplier;
- int much, more, over, rump, caches, input;
- u8 mask, margin;
- bool odd, isuy, decimatepixel, badinput;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- badinput = false;
- input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input;
-
- JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> "
- "frame buffer %i\n",
- peasycap->field_buffer[peasycap->field_read][0].kount,
- peasycap->field_buffer[peasycap->field_read][0].input,
- peasycap->field_read, peasycap->frame_fill);
- JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel);
-
-/*---------------------------------------------------------------------------*/
-/*
- * REJECT OR CLEAN BAD FIELDS
- */
-/*---------------------------------------------------------------------------*/
- if (peasycap->field_read == peasycap->field_fill) {
- SAM("ERROR: on entry, still filling field buffer %i\n",
- peasycap->field_read);
- return 0;
- }
-#ifdef EASYCAP_TESTCARD
- easycap_testcard(peasycap, peasycap->field_read);
-#else
- if (0 <= input && INPUT_MANY > input) {
- if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input])
- easycap_testcard(peasycap, peasycap->field_read);
- }
-#endif /*EASYCAP_TESTCARD*/
-/*---------------------------------------------------------------------------*/
-
- bytesperpixel = peasycap->bytesperpixel;
- decimatepixel = peasycap->decimatepixel;
-
- if ((2 != bytesperpixel) &&
- (3 != bytesperpixel) &&
- (4 != bytesperpixel)) {
- SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
- return -EFAULT;
- }
- if (decimatepixel)
- multiplier = 2;
- else
- multiplier = 1;
-
- w2 = 2 * multiplier * (peasycap->width);
- w3 = bytesperpixel * multiplier * (peasycap->width);
- wz = multiplier * (peasycap->height) *
- multiplier * (peasycap->width);
-
- kex = peasycap->field_read; mex = 0;
- kad = peasycap->frame_fill; mad = 0;
-
- pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE;
- pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE;
- odd = !!(peasycap->field_buffer[kex][0].kount);
-
- if (odd && (!decimatepixel)) {
- JOM(8, "initial skipping %4i bytes p.%4i\n",
- w3/multiplier, mad);
- pad += (w3 / multiplier); rad -= (w3 / multiplier);
- }
- isuy = true;
- mask = 0; rump = 0; caches = 0;
-
- cz = 0;
- while (cz < wz) {
- /*
- * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION:
- * READ w2 BYTES FROM FIELD BUFFER,
- * WRITE w3 BYTES TO FRAME BUFFER
- */
- if (!decimatepixel) {
- over = w2;
- do {
- much = over; more = 0;
- margin = 0; mask = 0x00;
- if (rex < much)
- much = rex;
- rump = 0;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
-
- more = (bytesperpixel *
- much) / 2;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (1 < bytesperpixel) {
- if (rad * 2 < much * bytesperpixel) {
- /*
- * INJUDICIOUS ALTERATION OF
- * THIS STATEMENT BLOCK WILL
- * CAUSE BREAKAGE. BEWARE.
- */
- rad2 = rad + bytesperpixel - 1;
- much = ((((2 * rad2)/bytesperpixel)/2) * 2);
- rump = ((bytesperpixel * much) / 2) - rad;
- more = rad;
- }
- mask = (u8)rump;
- margin = 0;
- if (much == rex) {
- mask |= 0x04;
- if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
- margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
- else
- mask |= 0x08;
- }
- } else {
- SAM("MISTAKE: %i=bytesperpixel\n",
- bytesperpixel);
- return -EFAULT;
- }
- if (rump)
- caches++;
- if (badinput) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- }
- rc = redaub(peasycap, pad, pex, much, more,
- mask, margin, isuy);
- if (0 > rc) {
- SAM("ERROR: redaub() failed\n");
- return -EFAULT;
- }
- if (much % 4)
- isuy = !isuy;
-
- over -= much; cz += much;
- pex += much; rex -= much;
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input))
- badinput = true;
- }
- pad += more;
- rad -= more;
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer[kad][mad].pgo;
- rad = PAGE_SIZE;
- if (rump) {
- pad += rump;
- rad -= rump;
- }
- }
- } while (over);
-/*---------------------------------------------------------------------------*/
-/*
- * SKIP w3 BYTES IN TARGET FRAME BUFFER,
- * UNLESS IT IS THE LAST LINE OF AN ODD FRAME
- */
-/*---------------------------------------------------------------------------*/
- if (!odd || (cz != wz)) {
- over = w3;
- do {
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer
- [kad][mad].pgo;
- rad = PAGE_SIZE;
- }
- more = over;
- if (rad < more)
- more = rad;
- over -= more;
- pad += more;
- rad -= more;
- } while (over);
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION:
- * ONLY IF false==odd,
- * READ w2 BYTES FROM FIELD BUFFER,
- * WRITE w3 / 2 BYTES TO FRAME BUFFER
- */
-/*---------------------------------------------------------------------------*/
- } else if (!odd) {
- over = w2;
- do {
- much = over; more = 0; margin = 0; mask = 0x00;
- if (rex < much)
- much = rex;
- rump = 0;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
-
- more = (bytesperpixel * much) / 4;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (1 < bytesperpixel) {
- if (rad * 4 < much * bytesperpixel) {
- /*
- * INJUDICIOUS ALTERATION OF
- * THIS STATEMENT BLOCK
- * WILL CAUSE BREAKAGE.
- * BEWARE.
- */
- rad2 = rad + bytesperpixel - 1;
- much = ((((2 * rad2) / bytesperpixel) / 2) * 4);
- rump = ((bytesperpixel * much) / 4) - rad;
- more = rad;
- }
- mask = (u8)rump;
- margin = 0;
- if (much == rex) {
- mask |= 0x04;
- if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
- margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
- else
- mask |= 0x08;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- } else {
- SAM("MISTAKE: %i=bytesperpixel\n",
- bytesperpixel);
- return -EFAULT;
- }
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- if (rump)
- caches++;
-
- if (badinput) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- }
- rc = redaub(peasycap, pad, pex, much, more,
- mask, margin, isuy);
- if (0 > rc) {
- SAM("ERROR: redaub() failed\n");
- return -EFAULT;
- }
- over -= much; cz += much;
- pex += much; rex -= much;
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input !=
- (0x08|peasycap->input))
- badinput = true;
- }
- pad += more;
- rad -= more;
- if (!rad) {
- mad++;
- pad = peasycap->frame_buffer[kad][mad].pgo;
- rad = PAGE_SIZE;
- if (rump) {
- pad += rump;
- rad -= rump;
- }
- }
- } while (over);
-/*---------------------------------------------------------------------------*/
-/*
- * OTHERWISE JUST
- * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM
- */
-/*---------------------------------------------------------------------------*/
- } else {
- over = w2;
- do {
- if (!rex) {
- mex++;
- pex = peasycap->field_buffer[kex][mex].pgo;
- rex = PAGE_SIZE;
- if (peasycap->field_buffer[kex][mex].input !=
- (0x08|peasycap->input)) {
- JOM(8, "ERROR: 0x%02X=->field_buffer"
- "[%i][%i].input, "
- "0x%02X=(0x08|->input)\n",
- peasycap->field_buffer
- [kex][mex].input, kex, mex,
- (0x08|peasycap->input));
- badinput = true;
- }
- }
- much = over;
- if (rex < much)
- much = rex;
- over -= much;
- cz += much;
- pex += much;
- rex -= much;
- } while (over);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * SANITY CHECKS
- */
-/*---------------------------------------------------------------------------*/
- c2 = (mex + 1)*PAGE_SIZE - rex;
- if (cz != c2)
- SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz);
- c3 = (mad + 1)*PAGE_SIZE - rad;
-
- if (!decimatepixel) {
- if (bytesperpixel * cz != c3)
- SAM("ERROR: discrepancy %i in bytes written\n",
- c3 - (bytesperpixel * cz));
- } else {
- if (!odd) {
- if (bytesperpixel *
- cz != (4 * c3))
- SAM("ERROR: discrepancy %i in bytes written\n",
- (2*c3)-(bytesperpixel * cz));
- } else {
- if (0 != c3)
- SAM("ERROR: discrepancy %i "
- "in bytes written\n", c3);
- }
- }
- if (rump)
- SAM("WORRY: undischarged cache at end of line in frame buffer\n");
-
- JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3);
- JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad);
-
- if (odd)
- JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad);
-
- if (peasycap->field_read == peasycap->field_fill)
- SAM("WARNING: on exit, filling field buffer %i\n",
- peasycap->field_read);
-
- if (caches)
- JOM(8, "%i=caches\n", caches);
- return 0;
-}
-/*---------------------------------------------------------------------------*/
-/*
- * DECIMATION AND COLOURSPACE CONVERSION.
- *
- * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE
- * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE.
- * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST
- * ALSO ENSURE THAT much IS EVEN.
- *
- * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN
- * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION.
- *
- * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS:
- * 0x03 & mask = number of bytes to be written to cache instead of to
- * frame buffer
- * 0x04 & mask => use argument margin to set the chrominance for last pixel
- * 0x08 & mask => do not set the chrominance for last pixel
- *
- * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601.
- *
- * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID
- * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO
- * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
-static int redaub(struct easycap *peasycap,
- void *pad, void *pex, int much, int more,
- u8 mask, u8 margin, bool isuy)
-{
- static s32 ay[256], bu[256], rv[256], gu[256], gv[256];
- u8 *pcache;
- u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr;
- int bytesperpixel;
- bool byteswaporder, decimatepixel, last;
- int j, rump;
- s32 tmp;
-
- if (much % 2) {
- SAM("MISTAKE: much is odd\n");
- return -EFAULT;
- }
- bytesperpixel = peasycap->bytesperpixel;
- byteswaporder = peasycap->byteswaporder;
- decimatepixel = peasycap->decimatepixel;
-
-/*---------------------------------------------------------------------------*/
- if (!bu[255]) {
- for (j = 0; j < 112; j++) {
- tmp = (0xFF00 & (453 * j)) >> 8;
- bu[j + 128] = tmp; bu[127 - j] = -tmp;
- tmp = (0xFF00 & (359 * j)) >> 8;
- rv[j + 128] = tmp; rv[127 - j] = -tmp;
- tmp = (0xFF00 & (88 * j)) >> 8;
- gu[j + 128] = tmp; gu[127 - j] = -tmp;
- tmp = (0xFF00 & (183 * j)) >> 8;
- gv[j + 128] = tmp; gv[127 - j] = -tmp;
- }
- for (j = 0; j < 16; j++) {
- bu[j] = bu[16]; rv[j] = rv[16];
- gu[j] = gu[16]; gv[j] = gv[16];
- }
- for (j = 240; j < 256; j++) {
- bu[j] = bu[239]; rv[j] = rv[239];
- gu[j] = gu[239]; gv[j] = gv[239];
- }
- for (j = 16; j < 236; j++)
- ay[j] = j;
- for (j = 0; j < 16; j++)
- ay[j] = ay[16];
- for (j = 236; j < 256; j++)
- ay[j] = ay[235];
- JOM(8, "lookup tables are prepared\n");
- }
- pcache = peasycap->pcache;
- if (!pcache)
- pcache = &peasycap->cache[0];
-/*---------------------------------------------------------------------------*/
-/*
- * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER
- */
-/*---------------------------------------------------------------------------*/
- if (!pcache) {
- SAM("MISTAKE: pcache is NULL\n");
- return -EFAULT;
- }
-
- if (pcache != &peasycap->cache[0])
- JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0]));
- p2 = &peasycap->cache[0];
- p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]);
- while (p2 < pcache) {
- *p3++ = *p2; p2++;
- }
- pcache = &peasycap->cache[0];
- if (p3 != pad) {
- SAM("MISTAKE: pointer misalignment\n");
- return -EFAULT;
- }
-/*---------------------------------------------------------------------------*/
- rump = (int)(0x03 & mask);
- u = 0; v = 0;
- p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false;
- p2++;
-
- if (isuy)
- u = *(p2 - 1);
- else
- v = *(p2 - 1);
-
- if (rump)
- JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump);
-
-/*---------------------------------------------------------------------------*/
- switch (bytesperpixel) {
- case 2: {
- if (!decimatepixel) {
- memcpy(pad, pex, (size_t)much);
- if (!byteswaporder) {
- /* UYVY */
- return 0;
- } else {
- /* YUYV */
- p3 = (u8 *)pad; pz = p3 + much;
- while (pz > p3) {
- c = *p3;
- *p3 = *(p3 + 1);
- *(p3 + 1) = c;
- p3 += 2;
- }
- return 0;
- }
- } else {
- if (!byteswaporder) {
- /* UYVY DECIMATED */
- p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
- while (pz > p2) {
- *p3 = *p2;
- *(p3 + 1) = *(p2 + 1);
- *(p3 + 2) = *(p2 + 2);
- *(p3 + 3) = *(p2 + 3);
- p3 += 4; p2 += 8;
- }
- return 0;
- } else {
- /* YUYV DECIMATED */
- p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
- while (pz > p2) {
- *p3 = *(p2 + 1);
- *(p3 + 1) = *p2;
- *(p3 + 2) = *(p2 + 3);
- *(p3 + 3) = *(p2 + 2);
- p3 += 4; p2 += 8;
- }
- return 0;
- }
- }
- break;
- }
- case 3:
- {
- if (!decimatepixel) {
- if (!byteswaporder) {
- /* RGB */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- return 0;
- } else {
- /* BGR */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- }
- else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- }
- return 0;
- } else {
- if (!byteswaporder) {
- /* RGB DECIMATED */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else {
- isuy = true;
- }
- p2 += 2;
- }
- return 0;
- } else {
- /* BGR DECIMATED */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- }
- isuy = false;
- p3 += bytesperpixel;
- }
- else
- isuy = true;
- p2 += 2;
- }
- return 0;
- }
- }
- break;
- }
- case 4:
- {
- if (!decimatepixel) {
- if (!byteswaporder) {
- /* RGBA */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *(p3 + 3) = 0;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- return 0;
- } else {
- /*
- * BGRA
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *pcache++ = 0;
- break;
- }
- default:
- SAM("MISTAKE: %i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *(p3 + 3) = 0;
- }
- p2 += 2;
- if (isuy)
- isuy = false;
- else
- isuy = true;
- p3 += bytesperpixel;
- }
- }
- return 0;
- } else {
- if (!byteswaporder) {
- /*
- * RGBA DECIMATED
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
-
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = r;
- *pcache++ = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = r;
- *(p3 + 1) = g;
- *pcache++ = b;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel -
- rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = r;
- *(p3 + 1) = g;
- *(p3 + 2) = b;
- *(p3 + 3) = 0;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else
- isuy = true;
- p2 += 2;
- }
- return 0;
- } else {
- /*
- * BGRA DECIMATED
- */
- while (pz > p2) {
- if (pr <= (p3 + bytesperpixel))
- last = true;
- else
- last = false;
- y = *p2;
- if (last && (0x0C & mask)) {
- if (0x04 & mask) {
- if (isuy)
- v = margin;
- else
- u = margin;
- } else
- if (0x08 & mask)
- ;
- } else {
- if (isuy)
- v = *(p2 + 1);
- else
- u = *(p2 + 1);
- }
-
- if (isuy) {
- tmp = ay[(int)y] + rv[(int)v];
- r = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] - gu[(int)u] -
- gv[(int)v];
- g = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
- tmp = ay[(int)y] + bu[(int)u];
- b = (255 < tmp) ? 255 : ((0 > tmp) ?
- 0 : (u8)tmp);
-
- if (last && rump) {
- pcache = &peasycap->cache[0];
- switch (bytesperpixel - rump) {
- case 1: {
- *p3 = b;
- *pcache++ = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 2: {
- *p3 = b;
- *(p3 + 1) = g;
- *pcache++ = r;
- *pcache++ = 0;
- break;
- }
- case 3: {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *pcache++ = 0;
- break;
- }
- default: {
- SAM("MISTAKE: "
- "%i=rump\n",
- bytesperpixel - rump);
- return -EFAULT;
- }
- }
- } else {
- *p3 = b;
- *(p3 + 1) = g;
- *(p3 + 2) = r;
- *(p3 + 3) = 0;
- }
- isuy = false;
- p3 += bytesperpixel;
- } else
- isuy = true;
- p2 += 2;
- }
- return 0;
- }
- }
- break;
- }
- default: {
- SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
- return -EFAULT;
- }
- }
- return 0;
-}
-/*****************************************************************************/
-/*
- * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434
- */
-/*****************************************************************************/
-static void easycap_vma_open(struct vm_area_struct *pvma)
-{
- struct easycap *peasycap;
-
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- peasycap->vma_many++;
- JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
- return;
-}
-/*****************************************************************************/
-static void easycap_vma_close(struct vm_area_struct *pvma)
-{
- struct easycap *peasycap;
-
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- peasycap->vma_many--;
- JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
- return;
-}
-/*****************************************************************************/
-static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf)
-{
- int k, m, retcode;
- void *pbuf;
- struct page *page;
- struct easycap *peasycap;
-
- retcode = VM_FAULT_NOPAGE;
-
- if (!pvma) {
- SAY("pvma is NULL\n");
- return retcode;
- }
- if (!pvmf) {
- SAY("pvmf is NULL\n");
- return retcode;
- }
-
- k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE);
- m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE);
-
- if (!m)
- JOT(4, "%4i=k, %4i=m\n", k, m);
- else
- JOT(16, "%4i=k, %4i=m\n", k, m);
-
- if ((0 > k) || (FRAME_BUFFER_MANY <= k)) {
- SAY("ERROR: buffer index %i out of range\n", k);
- return retcode;
- }
- if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) {
- SAY("ERROR: page number %i out of range\n", m);
- return retcode;
- }
- peasycap = pvma->vm_private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return retcode;
- }
-/*---------------------------------------------------------------------------*/
- pbuf = peasycap->frame_buffer[k][m].pgo;
- if (!pbuf) {
- SAM("ERROR: pbuf is NULL\n");
- return retcode;
- }
- page = virt_to_page(pbuf);
- if (!page) {
- SAM("ERROR: page is NULL\n");
- return retcode;
- }
- get_page(page);
-/*---------------------------------------------------------------------------*/
- if (!page) {
- SAM("ERROR: page is NULL after get_page(page)\n");
- } else {
- pvmf->page = page;
- retcode = VM_FAULT_MINOR;
- }
- return retcode;
-}
-
-static const struct vm_operations_struct easycap_vm_ops = {
- .open = easycap_vma_open,
- .close = easycap_vma_close,
- .fault = easycap_vma_fault,
-};
-
-static int easycap_mmap(struct file *file, struct vm_area_struct *pvma)
-{
- JOT(8, "\n");
-
- pvma->vm_ops = &easycap_vm_ops;
- pvma->vm_flags |= VM_RESERVED;
- if (file)
- pvma->vm_private_data = file->private_data;
- easycap_vma_open(pvma);
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS
- * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
- * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO.
- *
- * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP.
- *
- * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE
- * STORED IN THE TWO-BYTE STATUS PARAMETER
- * peasycap->field_buffer[peasycap->field_fill][0].kount
- * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER.
- *
- * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H
- * CHIP.
- *
- * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE:
- * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS
- * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA
- * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA
- * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS
- * 0 != (kount & 0x0400) => RESERVED
- * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED
- * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY?
- */
-/*---------------------------------------------------------------------------*/
-static void easycap_complete(struct urb *purb)
-{
- struct easycap *peasycap;
- struct data_buffer *pfield_buffer;
- char errbuf[16];
- int i, more, much, leap, rc, last;
- int videofieldamount;
- unsigned int override, bad;
- int framestatus, framelength, frameactual, frameoffset;
- u8 *pu;
-
- if (!purb) {
- SAY("ERROR: easycap_complete(): purb is NULL\n");
- return;
- }
- peasycap = purb->context;
- if (!peasycap) {
- SAY("ERROR: easycap_complete(): peasycap is NULL\n");
- return;
- }
- if (peasycap->video_eof)
- return;
- for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++)
- if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo)
- break;
- JOM(16, "%2i=urb\n", i);
- last = peasycap->video_isoc_sequence;
- if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) ||
- (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) {
- JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n",
- last, i);
- }
- peasycap->video_isoc_sequence = i;
-
- if (peasycap->video_idle) {
- JOM(16, "%i=video_idle %i=video_isoc_streaming\n",
- peasycap->video_idle, peasycap->video_isoc_streaming);
- if (peasycap->video_isoc_streaming) {
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- SAM("%s:%d ENOMEM\n", strerror(rc), rc);
- if (-ENODEV != rc)
- SAM("ERROR: while %i=video_idle, "
- "usb_submit_urb() "
- "failed with rc:\n",
- peasycap->video_idle);
- }
- }
- return;
- }
- override = 0;
-/*---------------------------------------------------------------------------*/
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (purb->status) {
- if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
- JOM(8, "urb status -ESHUTDOWN or -ENOENT\n");
- return;
- }
-
- (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ;
- SAM("ERROR: bad urb status -%s: %d\n",
- strerror(purb->status), purb->status);
-/*---------------------------------------------------------------------------*/
- } else {
- for (i = 0; i < purb->number_of_packets; i++) {
- if (0 != purb->iso_frame_desc[i].status) {
- (peasycap->field_buffer
- [peasycap->field_fill][0].kount) |= 0x8000 ;
- /* FIXME: 1. missing '-' check boundaries */
- strcpy(&errbuf[0],
- strerror(purb->iso_frame_desc[i].status));
- }
- framestatus = purb->iso_frame_desc[i].status;
- framelength = purb->iso_frame_desc[i].length;
- frameactual = purb->iso_frame_desc[i].actual_length;
- frameoffset = purb->iso_frame_desc[i].offset;
-
- JOM(16, "frame[%2i]:"
- "%4i=status "
- "%4i=actual "
- "%4i=length "
- "%5i=offset\n",
- i, framestatus, frameactual, framelength, frameoffset);
- if (!purb->iso_frame_desc[i].status) {
- more = purb->iso_frame_desc[i].actual_length;
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- videofieldamount = (peasycap->field_page *
- PAGE_SIZE) +
- (int)(pfield_buffer->pto - pfield_buffer->pgo);
- if (4 == more)
- peasycap->video_mt++;
- if (4 < more) {
- if (peasycap->video_mt) {
- JOM(8, "%4i empty video urb frames\n",
- peasycap->video_mt);
- peasycap->video_mt = 0;
- }
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
- peasycap->field_page) {
- SAM("ERROR: bad peasycap->field_page\n");
- return;
- }
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- pu = (u8 *)(purb->transfer_buffer +
- purb->iso_frame_desc[i].offset);
- if (0x80 & *pu)
- leap = 8;
- else
- leap = 4;
-/*--------------------------------------------------------------------------*/
-/*
- * EIGHT-BYTE END-OF-VIDEOFIELD MARKER.
- * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY,
- * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD.
- *
- * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER
- * BYTE OF
- * peasycap->field_buffer[peasycap->field_fill][0].kount
- * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS
- * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA
- * NOTHING IS OFFERED TO dqbuf().
- *
- * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT
- * RESTS WITH dqbuf().
- */
-/*---------------------------------------------------------------------------*/
- if ((8 == more) || override) {
- if (videofieldamount >
- peasycap->videofieldamount) {
- if (2 == videofieldamount -
- peasycap->
- videofieldamount) {
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x0100;
- peasycap->video_junk += (1 +
- VIDEO_JUNK_TOLERATE);
- } else
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x4000;
- } else if (videofieldamount <
- peasycap->
- videofieldamount) {
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount) |= 0x2000;
- }
- bad = 0xFF00 & peasycap->field_buffer
- [peasycap->field_fill]
- [0].kount;
- if (!bad) {
- (peasycap->video_junk)--;
- if (-VIDEO_JUNK_TOLERATE >
- peasycap->video_junk)
- peasycap->video_junk =
- -VIDEO_JUNK_TOLERATE;
- peasycap->field_read =
- (peasycap->
- field_fill)++;
- if (FIELD_BUFFER_MANY <=
- peasycap->
- field_fill)
- peasycap->
- field_fill = 0;
- peasycap->field_page = 0;
- pfield_buffer = &peasycap->
- field_buffer
- [peasycap->
- field_fill]
- [peasycap->
- field_page];
- pfield_buffer->pto =
- pfield_buffer->pgo;
- JOM(8, "bumped to: %i="
- "peasycap->"
- "field_fill %i="
- "parity\n",
- peasycap->field_fill,
- 0x00FF &
- pfield_buffer->kount);
- JOM(8, "field buffer %i has "
- "%i bytes fit to be "
- "read\n",
- peasycap->field_read,
- videofieldamount);
- JOM(8, "wakeup call to "
- "wq_video, "
- "%i=field_read "
- "%i=field_fill "
- "%i=parity\n",
- peasycap->field_read,
- peasycap->field_fill,
- 0x00FF & peasycap->
- field_buffer
- [peasycap->
- field_read][0].kount);
- wake_up_interruptible
- (&(peasycap->
- wq_video));
- } else {
- peasycap->video_junk++;
- if (bad & 0x0010)
- peasycap->video_junk +=
- (1 + VIDEO_JUNK_TOLERATE/2);
- JOM(8, "field buffer %i had %i "
- "bytes, now discarded: "
- "0x%04X\n",
- peasycap->field_fill,
- videofieldamount,
- (0xFF00 &
- peasycap->field_buffer
- [peasycap->field_fill][0].
- kount));
- (peasycap->field_fill)++;
-
- if (FIELD_BUFFER_MANY <=
- peasycap->field_fill)
- peasycap->field_fill = 0;
- peasycap->field_page = 0;
- pfield_buffer =
- &peasycap->field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- pfield_buffer->pto =
- pfield_buffer->pgo;
-
- JOM(8, "bumped to: %i=peasycap->"
- "field_fill %i=parity\n",
- peasycap->field_fill,
- 0x00FF & pfield_buffer->kount);
- }
- if (8 == more) {
- JOM(8, "end-of-field: received "
- "parity byte 0x%02X\n",
- (0xFF & *pu));
- if (0x40 & *pu)
- pfield_buffer->kount = 0x0000;
- else
- pfield_buffer->kount = 0x0001;
- pfield_buffer->input = 0x08 |
- (0x07 & peasycap->input);
- JOM(8, "end-of-field: 0x%02X=kount\n",
- 0xFF & pfield_buffer->kount);
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER
- */
-/*---------------------------------------------------------------------------*/
- pu += leap;
- more -= leap;
-
- if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
- SAM("ERROR: bad peasycap->field_fill\n");
- return;
- }
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) {
- SAM("ERROR: bad peasycap->field_page\n");
- return;
- }
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill][peasycap->field_page];
- while (more) {
- pfield_buffer = &peasycap->field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- if (PAGE_SIZE < (pfield_buffer->pto -
- pfield_buffer->pgo)) {
- SAM("ERROR: bad pfield_buffer->pto\n");
- return;
- }
- if (PAGE_SIZE == (pfield_buffer->pto -
- pfield_buffer->pgo)) {
- (peasycap->field_page)++;
- if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
- peasycap->field_page) {
- JOM(16, "wrapping peasycap->"
- "field_page\n");
- peasycap->field_page = 0;
- }
- pfield_buffer = &peasycap->
- field_buffer
- [peasycap->field_fill]
- [peasycap->field_page];
- pfield_buffer->pto = pfield_buffer->pgo;
- pfield_buffer->input = 0x08 |
- (0x07 & peasycap->input);
- if ((peasycap->field_buffer[peasycap->
- field_fill][0]).
- input !=
- pfield_buffer->input)
- (peasycap->field_buffer
- [peasycap->field_fill]
- [0]).kount |= 0x1000;
- }
-
- much = PAGE_SIZE -
- (int)(pfield_buffer->pto -
- pfield_buffer->pgo);
-
- if (much > more)
- much = more;
- memcpy(pfield_buffer->pto, pu, much);
- pu += much;
- (pfield_buffer->pto) += much;
- more -= much;
- }
- }
- }
- }
- }
-/*---------------------------------------------------------------------------*/
-/*
- * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS.
- *
- * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION
- * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
- */
-/*---------------------------------------------------------------------------*/
- if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) {
- SAM("easycap driver shutting down on condition green\n");
- peasycap->status = 1;
- peasycap->video_eof = 1;
- peasycap->video_junk = 0;
- wake_up_interruptible(&peasycap->wq_video);
-#if !defined(PERSEVERE)
- peasycap->audio_eof = 1;
- wake_up_interruptible(&peasycap->wq_audio);
-#endif /*PERSEVERE*/
- return;
- }
- if (peasycap->video_isoc_streaming) {
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- SAM("%s: %d\n", strerror(rc), rc);
- if (-ENODEV != rc)
- SAM("ERROR: while %i=video_idle, "
- "usb_submit_urb() "
- "failed with rc:\n",
- peasycap->video_idle);
- }
- }
- return;
-}
-
-static struct easycap *alloc_easycap(u8 bInterfaceNumber)
-{
- struct easycap *peasycap;
- int i;
-
- peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
- if (!peasycap) {
- SAY("ERROR: Could not allocate peasycap\n");
- return NULL;
- }
-
- if (mutex_lock_interruptible(&mutex_dongle)) {
- SAY("ERROR: cannot lock mutex_dongle\n");
- kfree(peasycap);
- return NULL;
- }
-
- /* Find a free dongle in easycapdc60_dongle array */
- for (i = 0; i < DONGLE_MANY; i++) {
-
- if ((!easycapdc60_dongle[i].peasycap) &&
- (!mutex_is_locked(&easycapdc60_dongle[i].mutex_video)) &&
- (!mutex_is_locked(&easycapdc60_dongle[i].mutex_audio))) {
-
- easycapdc60_dongle[i].peasycap = peasycap;
- peasycap->isdongle = i;
- JOM(8, "intf[%i]: peasycap-->easycap"
- "_dongle[%i].peasycap\n",
- bInterfaceNumber, i);
- break;
- }
- }
-
- mutex_unlock(&mutex_dongle);
-
- if (i >= DONGLE_MANY) {
- SAM("ERROR: too many dongles\n");
- kfree(peasycap);
- return NULL;
- }
-
- return peasycap;
-}
-
-static void free_easycap(struct easycap *peasycap)
-{
- int allocation_video_urb;
- int allocation_video_page;
- int allocation_video_struct;
- int allocation_audio_urb;
- int allocation_audio_page;
- int allocation_audio_struct;
- int registered_video, registered_audio;
- int kd;
-
- JOM(4, "freeing easycap structure.\n");
- allocation_video_urb = peasycap->allocation_video_urb;
- allocation_video_page = peasycap->allocation_video_page;
- allocation_video_struct = peasycap->allocation_video_struct;
- registered_video = peasycap->registered_video;
- allocation_audio_urb = peasycap->allocation_audio_urb;
- allocation_audio_page = peasycap->allocation_audio_page;
- allocation_audio_struct = peasycap->allocation_audio_struct;
- registered_audio = peasycap->registered_audio;
-
- kd = easycap_isdongle(peasycap);
- if (0 <= kd && DONGLE_MANY > kd) {
- if (mutex_lock_interruptible(&mutex_dongle)) {
- SAY("ERROR: cannot down mutex_dongle\n");
- } else {
- JOM(4, "locked mutex_dongle\n");
- easycapdc60_dongle[kd].peasycap = NULL;
- mutex_unlock(&mutex_dongle);
- JOM(4, "unlocked mutex_dongle\n");
- JOT(4, " null-->dongle[%i].peasycap\n", kd);
- allocation_video_struct -= sizeof(struct easycap);
- }
- } else {
- SAY("ERROR: cannot purge dongle[].peasycap");
- }
-
- /* Free device structure */
- kfree(peasycap);
-
- SAY("%8i=video urbs after all deletions\n", allocation_video_urb);
- SAY("%8i=video pages after all deletions\n", allocation_video_page);
- SAY("%8i=video structs after all deletions\n", allocation_video_struct);
- SAY("%8i=video devices after all deletions\n", registered_video);
- SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb);
- SAY("%8i=audio pages after all deletions\n", allocation_audio_page);
- SAY("%8i=audio structs after all deletions\n", allocation_audio_struct);
- SAY("%8i=audio devices after all deletions\n", registered_audio);
-}
-
-/*
- * FIXME: Identify the appropriate pointer peasycap for interfaces
- * 1 and 2. The address of peasycap->pusb_device is reluctantly used
- * for this purpose.
- */
-static struct easycap *get_easycap(struct usb_device *usbdev,
- u8 bInterfaceNumber)
-{
- int i;
- struct easycap *peasycap;
-
- for (i = 0; i < DONGLE_MANY; i++) {
- if (easycapdc60_dongle[i].peasycap->pusb_device == usbdev) {
- peasycap = easycapdc60_dongle[i].peasycap;
- JOT(8, "intf[%i]: dongle[%i].peasycap\n",
- bInterfaceNumber, i);
- break;
- }
- }
- if (i >= DONGLE_MANY) {
- SAY("ERROR: peasycap is unknown when probing interface %i\n",
- bInterfaceNumber);
- return NULL;
- }
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL when probing interface %i\n",
- bInterfaceNumber);
- return NULL;
- }
-
- return peasycap;
-}
-
-static void init_easycap(struct easycap *peasycap,
- struct usb_device *usbdev,
- struct usb_interface *intf,
- u8 bInterfaceNumber)
-{
- /* Save usb_device and usb_interface */
- peasycap->pusb_device = usbdev;
- peasycap->pusb_interface = intf;
-
- peasycap->minor = -1;
- kref_init(&peasycap->kref);
- JOM(8, "intf[%i]: after kref_init(..._video) "
- "%i=peasycap->kref.refcount.counter\n",
- bInterfaceNumber, peasycap->kref.refcount.counter);
-
- /* module params */
- peasycap->gain = (s8)clamp(easycap_gain, 0, 31);
-
- init_waitqueue_head(&peasycap->wq_video);
- init_waitqueue_head(&peasycap->wq_audio);
- init_waitqueue_head(&peasycap->wq_trigger);
-
- peasycap->allocation_video_struct = sizeof(struct easycap);
-
- peasycap->microphone = false;
-
- peasycap->video_interface = -1;
- peasycap->video_altsetting_on = -1;
- peasycap->video_altsetting_off = -1;
- peasycap->video_endpointnumber = -1;
- peasycap->video_isoc_maxframesize = -1;
- peasycap->video_isoc_buffer_size = -1;
-
- peasycap->audio_interface = -1;
- peasycap->audio_altsetting_on = -1;
- peasycap->audio_altsetting_off = -1;
- peasycap->audio_endpointnumber = -1;
- peasycap->audio_isoc_maxframesize = -1;
- peasycap->audio_isoc_buffer_size = -1;
-
- peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
-
- peasycap->ntsc = easycap_ntsc;
- JOM(8, "defaulting initially to %s\n",
- easycap_ntsc ? "NTSC" : "PAL");
-}
-
-static int populate_inputset(struct easycap *peasycap)
-{
- struct inputset *inputset;
- struct easycap_format *peasycap_format;
- struct v4l2_pix_format *pix;
- int m, i, k, mask, fmtidx;
- s32 value;
-
- inputset = peasycap->inputset;
-
- fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN;
-
- m = 0;
- mask = 0;
- for (i = 0; easycap_standard[i].mask != 0xffff; i++) {
- if (fmtidx == easycap_standard[i].v4l2_standard.index) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].standard_offset = i;
- mask = easycap_standard[i].mask;
- }
- }
-
- if (m != 1) {
- SAM("ERROR: inputset->standard_offset unpopulated, %i=m\n", m);
- return -ENOENT;
- }
-
- peasycap_format = &easycap_format[0];
- m = 0;
- for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) {
- pix = &peasycap_format->v4l2_format.fmt.pix;
- if (((peasycap_format->mask & 0x0F) == (mask & 0x0F))
- && pix->field == V4L2_FIELD_NONE
- && pix->pixelformat == V4L2_PIX_FMT_UYVY
- && pix->width == 640 && pix->height == 480) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].format_offset = i;
- break;
- }
- peasycap_format++;
- }
- if (m != 1) {
- SAM("ERROR: inputset[]->format_offset unpopulated\n");
- return -ENOENT;
- }
-
- m = 0;
- for (i = 0; easycap_control[i].id != 0xffffffff; i++) {
- value = easycap_control[i].default_value;
- if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].brightness = value;
- } else if (V4L2_CID_CONTRAST == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].contrast = value;
- } else if (V4L2_CID_SATURATION == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].saturation = value;
- } else if (V4L2_CID_HUE == easycap_control[i].id) {
- m++;
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].hue = value;
- }
- }
-
- if (m != 4) {
- SAM("ERROR: inputset[]->brightness underpopulated\n");
- return -ENOENT;
- }
-
- for (k = 0; k < INPUT_MANY; k++)
- inputset[k].input = k;
- JOM(4, "populated inputset[]\n");
-
- return 0;
-}
-
-static int alloc_framebuffers(struct easycap *peasycap)
-{
- int i, j;
- void *pbuf;
-
- JOM(4, "allocating %i frame buffers of size %li\n",
- FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
- JOM(4, ".... each scattered over %li pages\n",
- FRAME_BUFFER_SIZE/PAGE_SIZE);
-
- for (i = 0; i < FRAME_BUFFER_MANY; i++) {
- for (j = 0; j < FRAME_BUFFER_SIZE/PAGE_SIZE; j++) {
- if (peasycap->frame_buffer[i][j].pgo)
- SAM("attempting to reallocate framebuffers\n");
- else {
- pbuf = (void *)__get_free_page(GFP_KERNEL);
- if (!pbuf) {
- SAM("ERROR: Could not allocate "
- "framebuffer %i page %i\n", i, j);
- return -ENOMEM;
- }
- peasycap->allocation_video_page += 1;
- peasycap->frame_buffer[i][j].pgo = pbuf;
- }
- peasycap->frame_buffer[i][j].pto =
- peasycap->frame_buffer[i][j].pgo;
- }
- }
-
- peasycap->frame_fill = 0;
- peasycap->frame_read = 0;
- JOM(4, "allocation of frame buffers done: %i pages\n", i*j);
-
- return 0;
-}
-
-static void free_framebuffers(struct easycap *peasycap)
-{
- int k, m, gone;
-
- JOM(4, "freeing video frame buffers.\n");
- gone = 0;
- for (k = 0; k < FRAME_BUFFER_MANY; k++) {
- for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->frame_buffer[k][m].pgo) {
- free_page((unsigned long)
- peasycap->frame_buffer[k][m].pgo);
- peasycap->frame_buffer[k][m].pgo = NULL;
- peasycap->allocation_video_page -= 1;
- gone++;
- }
- }
- }
- JOM(4, "video frame buffers freed: %i pages\n", gone);
-}
-
-static int alloc_fieldbuffers(struct easycap *peasycap)
-{
- int i, j;
- void *pbuf;
-
- JOM(4, "allocating %i field buffers of size %li\n",
- FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
- JOM(4, ".... each scattered over %li pages\n",
- FIELD_BUFFER_SIZE/PAGE_SIZE);
-
- for (i = 0; i < FIELD_BUFFER_MANY; i++) {
- for (j = 0; j < FIELD_BUFFER_SIZE/PAGE_SIZE; j++) {
- if (peasycap->field_buffer[i][j].pgo) {
- SAM("ERROR: attempting to reallocate "
- "fieldbuffers\n");
- } else {
- pbuf = (void *) __get_free_page(GFP_KERNEL);
- if (!pbuf) {
- SAM("ERROR: Could not allocate "
- "fieldbuffer %i page %i\n", i, j);
- return -ENOMEM;
- }
- peasycap->allocation_video_page += 1;
- peasycap->field_buffer[i][j].pgo = pbuf;
- }
- peasycap->field_buffer[i][j].pto =
- peasycap->field_buffer[i][j].pgo;
- }
- /* TODO: Hardcoded 0x0200 meaning? */
- peasycap->field_buffer[i][0].kount = 0x0200;
- }
- peasycap->field_fill = 0;
- peasycap->field_page = 0;
- peasycap->field_read = 0;
- JOM(4, "allocation of field buffers done: %i pages\n", i*j);
-
- return 0;
-}
-
-static void free_fieldbuffers(struct easycap *peasycap)
-{
- int k, m, gone;
-
- JOM(4, "freeing video field buffers.\n");
- gone = 0;
- for (k = 0; k < FIELD_BUFFER_MANY; k++) {
- for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
- if (peasycap->field_buffer[k][m].pgo) {
- free_page((unsigned long)
- peasycap->field_buffer[k][m].pgo);
- peasycap->field_buffer[k][m].pgo = NULL;
- peasycap->allocation_video_page -= 1;
- gone++;
- }
- }
- }
- JOM(4, "video field buffers freed: %i pages\n", gone);
-}
-
-static int alloc_isocbuffers(struct easycap *peasycap)
-{
- int i;
- void *pbuf;
-
- JOM(4, "allocating %i isoc video buffers of size %i\n",
- VIDEO_ISOC_BUFFER_MANY,
- peasycap->video_isoc_buffer_size);
- JOM(4, ".... each occupying contiguous memory pages\n");
-
- for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) {
- pbuf = (void *)__get_free_pages(GFP_KERNEL,
- VIDEO_ISOC_ORDER);
- if (!pbuf) {
- SAM("ERROR: Could not allocate isoc "
- "video buffer %i\n", i);
- return -ENOMEM;
- }
- peasycap->allocation_video_page += BIT(VIDEO_ISOC_ORDER);
-
- peasycap->video_isoc_buffer[i].pgo = pbuf;
- peasycap->video_isoc_buffer[i].pto =
- pbuf + peasycap->video_isoc_buffer_size;
- peasycap->video_isoc_buffer[i].kount = i;
- }
- JOM(4, "allocation of isoc video buffers done: %i pages\n",
- i * (0x01 << VIDEO_ISOC_ORDER));
- return 0;
-}
-
-static void free_isocbuffers(struct easycap *peasycap)
-{
- int k, m;
-
- JOM(4, "freeing video isoc buffers.\n");
- m = 0;
- for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
- if (peasycap->video_isoc_buffer[k].pgo) {
- free_pages((unsigned long)
- peasycap->video_isoc_buffer[k].pgo,
- VIDEO_ISOC_ORDER);
- peasycap->video_isoc_buffer[k].pgo = NULL;
- peasycap->allocation_video_page -=
- BIT(VIDEO_ISOC_ORDER);
- m++;
- }
- }
- JOM(4, "isoc video buffers freed: %i pages\n",
- m * (0x01 << VIDEO_ISOC_ORDER));
-}
-
-static int create_video_urbs(struct easycap *peasycap)
-{
- struct urb *purb;
- struct data_urb *pdata_urb;
- int i, j;
-
- JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
- JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, "using %i=peasycap->video_isoc_maxframesize\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, "using %i=peasycap->video_isoc_buffer_sizen",
- peasycap->video_isoc_buffer_size);
-
- for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) {
- purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc,
- GFP_KERNEL);
- if (!purb) {
- SAM("ERROR: usb_alloc_urb returned NULL for buffer "
- "%i\n", i);
- return -ENOMEM;
- }
-
- peasycap->allocation_video_urb += 1;
- pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
- if (!pdata_urb) {
- usb_free_urb(purb);
- SAM("ERROR: Could not allocate struct data_urb.\n");
- return -ENOMEM;
- }
-
- peasycap->allocation_video_struct +=
- sizeof(struct data_urb);
-
- pdata_urb->purb = purb;
- pdata_urb->isbuf = i;
- pdata_urb->length = 0;
- list_add_tail(&(pdata_urb->list_head),
- peasycap->purb_video_head);
-
- if (!i) {
- JOM(4, "initializing video urbs thus:\n");
- JOM(4, " purb->interval = 1;\n");
- JOM(4, " purb->dev = peasycap->pusb_device;\n");
- JOM(4, " purb->pipe = usb_rcvisocpipe"
- "(peasycap->pusb_device,%i);\n",
- peasycap->video_endpointnumber);
- JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
- JOM(4, " purb->transfer_buffer = peasycap->"
- "video_isoc_buffer[.].pgo;\n");
- JOM(4, " purb->transfer_buffer_length = %i;\n",
- peasycap->video_isoc_buffer_size);
- JOM(4, " purb->complete = easycap_complete;\n");
- JOM(4, " purb->context = peasycap;\n");
- JOM(4, " purb->start_frame = 0;\n");
- JOM(4, " purb->number_of_packets = %i;\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, " for (j = 0; j < %i; j++)\n",
- peasycap->video_isoc_framesperdesc);
- JOM(4, " {\n");
- JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
- peasycap->video_isoc_maxframesize);
- JOM(4, " }\n");
- }
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->video_endpointnumber);
-
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->video_isoc_buffer[i].pgo;
- purb->transfer_buffer_length =
- peasycap->video_isoc_buffer_size;
-
- purb->complete = easycap_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->video_isoc_framesperdesc;
-
- for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset =
- j * peasycap->video_isoc_maxframesize;
- purb->iso_frame_desc[j].length =
- peasycap->video_isoc_maxframesize;
- }
- }
- JOM(4, "allocation of %i struct urb done.\n", i);
- return 0;
-}
-
-static void free_video_urbs(struct easycap *peasycap)
-{
- struct list_head *plist_head, *plist_next;
- struct data_urb *pdata_urb;
- int m;
-
- if (peasycap->purb_video_head) {
- m = 0;
- list_for_each(plist_head, peasycap->purb_video_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_free_urb(pdata_urb->purb);
- pdata_urb->purb = NULL;
- peasycap->allocation_video_urb--;
- m++;
- }
- }
-
- JOM(4, "%i video urbs freed\n", m);
- JOM(4, "freeing video data_urb structures.\n");
- m = 0;
- list_for_each_safe(plist_head, plist_next,
- peasycap->purb_video_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- peasycap->allocation_video_struct -=
- sizeof(struct data_urb);
- kfree(pdata_urb);
- m++;
- }
- }
- JOM(4, "%i video data_urb structures freed\n", m);
- JOM(4, "setting peasycap->purb_video_head=NULL\n");
- peasycap->purb_video_head = NULL;
- }
-}
-
-static int alloc_audio_buffers(struct easycap *peasycap)
-{
- void *pbuf;
- int k;
-
- JOM(4, "allocating %i isoc audio buffers of size %i\n",
- AUDIO_ISOC_BUFFER_MANY,
- peasycap->audio_isoc_buffer_size);
- JOM(4, ".... each occupying contiguous memory pages\n");
-
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- pbuf = (void *)__get_free_pages(GFP_KERNEL, AUDIO_ISOC_ORDER);
- if (!pbuf) {
- SAM("ERROR: Could not allocate isoc audio buffer %i\n",
- k);
- return -ENOMEM;
- }
- peasycap->allocation_audio_page += BIT(AUDIO_ISOC_ORDER);
-
- peasycap->audio_isoc_buffer[k].pgo = pbuf;
- peasycap->audio_isoc_buffer[k].pto =
- pbuf + peasycap->audio_isoc_buffer_size;
- peasycap->audio_isoc_buffer[k].kount = k;
- }
-
- JOM(4, "allocation of isoc audio buffers done.\n");
- return 0;
-}
-
-static void free_audio_buffers(struct easycap *peasycap)
-{
- int k, m;
-
- JOM(4, "freeing audio isoc buffers.\n");
- m = 0;
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- if (peasycap->audio_isoc_buffer[k].pgo) {
- free_pages((unsigned long)
- (peasycap->audio_isoc_buffer[k].pgo),
- AUDIO_ISOC_ORDER);
- peasycap->audio_isoc_buffer[k].pgo = NULL;
- peasycap->allocation_audio_page -=
- BIT(AUDIO_ISOC_ORDER);
- m++;
- }
- }
- JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n",
- m * (0x01 << AUDIO_ISOC_ORDER));
-}
-
-static int create_audio_urbs(struct easycap *peasycap)
-{
- struct urb *purb;
- struct data_urb *pdata_urb;
- int k, j;
-
- JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
- JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n",
- peasycap->audio_isoc_buffer_size);
-
- for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
- purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc,
- GFP_KERNEL);
- if (!purb) {
- SAM("ERROR: usb_alloc_urb returned NULL for buffer "
- "%i\n", k);
- return -ENOMEM;
- }
- peasycap->allocation_audio_urb += 1 ;
- pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
- if (!pdata_urb) {
- usb_free_urb(purb);
- SAM("ERROR: Could not allocate struct data_urb.\n");
- return -ENOMEM;
- }
- peasycap->allocation_audio_struct +=
- sizeof(struct data_urb);
-
- pdata_urb->purb = purb;
- pdata_urb->isbuf = k;
- pdata_urb->length = 0;
- list_add_tail(&(pdata_urb->list_head),
- peasycap->purb_audio_head);
-
- if (!k) {
- JOM(4, "initializing audio urbs thus:\n");
- JOM(4, " purb->interval = 1;\n");
- JOM(4, " purb->dev = peasycap->pusb_device;\n");
- JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->"
- "pusb_device,%i);\n",
- peasycap->audio_endpointnumber);
- JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
- JOM(4, " purb->transfer_buffer = "
- "peasycap->audio_isoc_buffer[.].pgo;\n");
- JOM(4, " purb->transfer_buffer_length = %i;\n",
- peasycap->audio_isoc_buffer_size);
- JOM(4, " purb->complete = easycap_alsa_complete;\n");
- JOM(4, " purb->context = peasycap;\n");
- JOM(4, " purb->start_frame = 0;\n");
- JOM(4, " purb->number_of_packets = %i;\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, " for (j = 0; j < %i; j++)\n",
- peasycap->audio_isoc_framesperdesc);
- JOM(4, " {\n");
- JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
- peasycap->audio_isoc_maxframesize);
- JOM(4, " }\n");
- }
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->audio_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo;
- purb->transfer_buffer_length =
- peasycap->audio_isoc_buffer_size;
- purb->complete = easycap_alsa_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
- for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset =
- j * peasycap->audio_isoc_maxframesize;
- purb->iso_frame_desc[j].length =
- peasycap->audio_isoc_maxframesize;
- }
- }
- JOM(4, "allocation of %i struct urb done.\n", k);
- return 0;
-}
-
-static void free_audio_urbs(struct easycap *peasycap)
-{
- struct list_head *plist_head, *plist_next;
- struct data_urb *pdata_urb;
- int m;
-
- if (peasycap->purb_audio_head) {
- JOM(4, "freeing audio urbs\n");
- m = 0;
- list_for_each(plist_head, (peasycap->purb_audio_head)) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- usb_free_urb(pdata_urb->purb);
- pdata_urb->purb = NULL;
- peasycap->allocation_audio_urb--;
- m++;
- }
- }
- JOM(4, "%i audio urbs freed\n", m);
- JOM(4, "freeing audio data_urb structures.\n");
- m = 0;
- list_for_each_safe(plist_head, plist_next,
- peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head,
- struct data_urb, list_head);
- if (pdata_urb) {
- peasycap->allocation_audio_struct -=
- sizeof(struct data_urb);
- kfree(pdata_urb);
- m++;
- }
- }
- JOM(4, "%i audio data_urb structures freed\n", m);
- JOM(4, "setting peasycap->purb_audio_head=NULL\n");
- peasycap->purb_audio_head = NULL;
- }
-}
-
-static void config_easycap(struct easycap *peasycap,
- u8 bInterfaceNumber,
- u8 bInterfaceClass,
- u8 bInterfaceSubClass)
-{
- if ((USB_CLASS_VIDEO == bInterfaceClass) ||
- (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
- if (-1 == peasycap->video_interface) {
- peasycap->video_interface = bInterfaceNumber;
- JOM(4, "setting peasycap->video_interface=%i\n",
- peasycap->video_interface);
- } else {
- if (peasycap->video_interface != bInterfaceNumber) {
- SAM("ERROR: attempting to reset "
- "peasycap->video_interface\n");
- SAM("...... continuing with "
- "%i=peasycap->video_interface\n",
- peasycap->video_interface);
- }
- }
- } else if ((USB_CLASS_AUDIO == bInterfaceClass) &&
- (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) {
- if (-1 == peasycap->audio_interface) {
- peasycap->audio_interface = bInterfaceNumber;
- JOM(4, "setting peasycap->audio_interface=%i\n",
- peasycap->audio_interface);
- } else {
- if (peasycap->audio_interface != bInterfaceNumber) {
- SAM("ERROR: attempting to reset "
- "peasycap->audio_interface\n");
- SAM("...... continuing with "
- "%i=peasycap->audio_interface\n",
- peasycap->audio_interface);
- }
- }
- }
-}
-
-/*
- * This function is called from within easycap_usb_disconnect() and is
- * protected by semaphores set and cleared by easycap_usb_disconnect().
- * By this stage the device has already been physically unplugged,
- * so peasycap->pusb_device is no longer valid.
- */
-static void easycap_delete(struct kref *pkref)
-{
- struct easycap *peasycap;
-
- peasycap = container_of(pkref, struct easycap, kref);
- if (!peasycap) {
- SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
- return;
- }
-
- /* Free video urbs */
- free_video_urbs(peasycap);
-
- /* Free video isoc buffers */
- free_isocbuffers(peasycap);
-
- /* Free video field buffers */
- free_fieldbuffers(peasycap);
-
- /* Free video frame buffers */
- free_framebuffers(peasycap);
-
- /* Free audio urbs */
- free_audio_urbs(peasycap);
-
- /* Free audio isoc buffers */
- free_audio_buffers(peasycap);
-
- free_easycap(peasycap);
-
- JOT(4, "ending.\n");
-}
-
-static const struct v4l2_file_operations v4l2_fops = {
- .owner = THIS_MODULE,
- .open = easycap_open_noinode,
- .unlocked_ioctl = easycap_unlocked_ioctl,
- .poll = easycap_poll,
- .mmap = easycap_mmap,
-};
-
-static int easycap_register_video(struct easycap *peasycap)
-{
- /*
- * FIXME: This is believed to be harmless,
- * but may well be unnecessary or wrong.
- */
- peasycap->video_device.v4l2_dev = NULL;
-
- strcpy(&peasycap->video_device.name[0], "easycapdc60");
- peasycap->video_device.fops = &v4l2_fops;
- peasycap->video_device.minor = -1;
- peasycap->video_device.release = (void *)(&videodev_release);
-
- video_set_drvdata(&(peasycap->video_device), (void *)peasycap);
-
- if (0 != (video_register_device(&(peasycap->video_device),
- VFL_TYPE_GRABBER, -1))) {
- videodev_release(&(peasycap->video_device));
- return -ENODEV;
- }
-
- peasycap->registered_video++;
-
- SAM("registered with videodev: %i=minor\n",
- peasycap->video_device.minor);
- peasycap->minor = peasycap->video_device.minor;
-
- return 0;
-}
-
-/*
- * When the device is plugged, this function is called three times,
- * one for each interface.
- */
-static int easycap_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct usb_device *usbdev;
- struct usb_host_interface *alt;
- struct usb_endpoint_descriptor *ep;
- struct usb_interface_descriptor *interface;
- struct easycap *peasycap;
- int i, j, rc;
- u8 bInterfaceNumber;
- u8 bInterfaceClass;
- u8 bInterfaceSubClass;
- int okalt[8], isokalt;
- int okepn[8];
- int okmps[8];
- int maxpacketsize;
-
- usbdev = interface_to_usbdev(intf);
-
- alt = usb_altnum_to_altsetting(intf, 0);
- if (!alt) {
- SAY("ERROR: usb_host_interface not found\n");
- return -EFAULT;
- }
-
- interface = &alt->desc;
- if (!interface) {
- SAY("ERROR: intf_descriptor is NULL\n");
- return -EFAULT;
- }
-
- /* Get properties of probed interface */
- bInterfaceNumber = interface->bInterfaceNumber;
- bInterfaceClass = interface->bInterfaceClass;
- bInterfaceSubClass = interface->bInterfaceSubClass;
-
- JOT(4, "intf[%i]: num_altsetting=%i\n",
- bInterfaceNumber, intf->num_altsetting);
- JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n",
- bInterfaceNumber,
- (long int)(intf->cur_altsetting - intf->altsetting));
- JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n",
- bInterfaceNumber, bInterfaceClass, bInterfaceSubClass);
-
- /*
- * A new struct easycap is always allocated when interface 0 is probed.
- * It is not possible here to free any existing struct easycap.
- * This should have been done by easycap_delete() when the device was
- * physically unplugged.
- * The allocated struct easycap is saved for later usage when
- * interfaces 1 and 2 are probed.
- */
- if (0 == bInterfaceNumber) {
- /*
- * Alloc structure and save it in a free slot in
- * easycapdc60_dongle array
- */
- peasycap = alloc_easycap(bInterfaceNumber);
- if (!peasycap)
- return -ENOMEM;
-
- /* Perform basic struct initialization */
- init_easycap(peasycap, usbdev, intf, bInterfaceNumber);
-
- /* Dynamically fill in the available formats */
- rc = easycap_video_fillin_formats();
- if (0 > rc) {
- SAM("ERROR: fillin_formats() rc = %i\n", rc);
- return -EFAULT;
- }
- JOM(4, "%i formats available\n", rc);
-
- /* Populate easycap.inputset[] */
- rc = populate_inputset(peasycap);
- if (rc < 0)
- return rc;
- JOM(4, "finished initialization\n");
- } else {
- peasycap = get_easycap(usbdev, bInterfaceNumber);
- if (!peasycap)
- return -ENODEV;
- }
-
- config_easycap(peasycap, bInterfaceNumber,
- bInterfaceClass,
- bInterfaceSubClass);
-
- /*
- * Investigate all altsettings. This is done in detail
- * because USB device 05e1:0408 has disparate incarnations.
- */
- isokalt = 0;
- for (i = 0; i < intf->num_altsetting; i++) {
- alt = usb_altnum_to_altsetting(intf, i);
- if (!alt) {
- SAM("ERROR: alt is NULL\n");
- return -EFAULT;
- }
- interface = &alt->desc;
- if (!interface) {
- SAM("ERROR: intf_descriptor is NULL\n");
- return -EFAULT;
- }
-
- if (0 == interface->bNumEndpoints)
- JOM(4, "intf[%i]alt[%i] has no endpoints\n",
- bInterfaceNumber, i);
- for (j = 0; j < interface->bNumEndpoints; j++) {
- ep = &alt->endpoint[j].desc;
- if (!ep) {
- SAM("ERROR: ep is NULL.\n");
- SAM("...... skipping\n");
- continue;
- }
-
- if (!usb_endpoint_is_isoc_in(ep)) {
- JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n",
- bInterfaceNumber,
- i, j, ep->bmAttributes);
- if (usb_endpoint_dir_out(ep)) {
- SAM("ERROR: OUT endpoint unexpected\n");
- SAM("...... continuing\n");
- }
- continue;
- }
- switch (bInterfaceClass) {
- case USB_CLASS_VIDEO:
- case USB_CLASS_VENDOR_SPEC: {
- if (ep->wMaxPacketSize) {
- if (8 > isokalt) {
- okalt[isokalt] = i;
- JOM(4,
- "%i=okalt[%i]\n",
- okalt[isokalt],
- isokalt);
- okepn[isokalt] =
- ep->
- bEndpointAddress &
- 0x0F;
- JOM(4,
- "%i=okepn[%i]\n",
- okepn[isokalt],
- isokalt);
- okmps[isokalt] =
- le16_to_cpu(ep->
- wMaxPacketSize);
- JOM(4,
- "%i=okmps[%i]\n",
- okmps[isokalt],
- isokalt);
- isokalt++;
- }
- } else {
- if (-1 == peasycap->
- video_altsetting_off) {
- peasycap->
- video_altsetting_off =
- i;
- JOM(4, "%i=video_"
- "altsetting_off "
- "<====\n",
- peasycap->
- video_altsetting_off);
- } else {
- SAM("ERROR: peasycap"
- "->video_altsetting_"
- "off already set\n");
- SAM("...... "
- "continuing with "
- "%i=peasycap->video_"
- "altsetting_off\n",
- peasycap->
- video_altsetting_off);
- }
- }
- break;
- }
- case USB_CLASS_AUDIO: {
- if (bInterfaceSubClass !=
- USB_SUBCLASS_AUDIOSTREAMING)
- break;
- if (!peasycap) {
- SAM("MISTAKE: "
- "peasycap is NULL\n");
- return -EFAULT;
- }
- if (ep->wMaxPacketSize) {
- if (8 > isokalt) {
- okalt[isokalt] = i ;
- JOM(4,
- "%i=okalt[%i]\n",
- okalt[isokalt],
- isokalt);
- okepn[isokalt] =
- ep->
- bEndpointAddress &
- 0x0F;
- JOM(4,
- "%i=okepn[%i]\n",
- okepn[isokalt],
- isokalt);
- okmps[isokalt] =
- le16_to_cpu(ep->
- wMaxPacketSize);
- JOM(4,
- "%i=okmps[%i]\n",
- okmps[isokalt],
- isokalt);
- isokalt++;
- }
- } else {
- if (-1 == peasycap->
- audio_altsetting_off) {
- peasycap->
- audio_altsetting_off =
- i;
- JOM(4, "%i=audio_"
- "altsetting_off "
- "<====\n",
- peasycap->
- audio_altsetting_off);
- } else {
- SAM("ERROR: peasycap"
- "->audio_altsetting_"
- "off already set\n");
- SAM("...... "
- "continuing with "
- "%i=peasycap->"
- "audio_altsetting_"
- "off\n",
- peasycap->
- audio_altsetting_off);
- }
- }
- break;
- }
- default:
- break;
- }
- if (0 == ep->wMaxPacketSize) {
- JOM(4, "intf[%i]alt[%i]end[%i] "
- "has zero packet size\n",
- bInterfaceNumber, i, j);
- }
- }
- }
-
- /* Perform initialization of the probed interface */
- JOM(4, "initialization begins for interface %i\n",
- interface->bInterfaceNumber);
- switch (bInterfaceNumber) {
- /* 0: Video interface */
- case 0: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!isokalt) {
- SAM("ERROR: no viable video_altsetting_on\n");
- return -ENOENT;
- }
- peasycap->video_altsetting_on = okalt[isokalt - 1];
- JOM(4, "%i=video_altsetting_on <====\n",
- peasycap->video_altsetting_on);
-
- /* Decide video streaming parameters */
- peasycap->video_endpointnumber = okepn[isokalt - 1];
- JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber);
- maxpacketsize = okmps[isokalt - 1];
-
- peasycap->video_isoc_maxframesize =
- min(maxpacketsize, USB_2_0_MAXPACKETSIZE);
- if (0 >= peasycap->video_isoc_maxframesize) {
- SAM("ERROR: bad video_isoc_maxframesize\n");
- SAM(" possibly because port is USB 1.1\n");
- return -ENOENT;
- }
- JOM(4, "%i=video_isoc_maxframesize\n",
- peasycap->video_isoc_maxframesize);
-
- peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC;
- JOM(4, "%i=video_isoc_framesperdesc\n",
- peasycap->video_isoc_framesperdesc);
- if (0 >= peasycap->video_isoc_framesperdesc) {
- SAM("ERROR: bad video_isoc_framesperdesc\n");
- return -ENOENT;
- }
- peasycap->video_isoc_buffer_size =
- peasycap->video_isoc_maxframesize *
- peasycap->video_isoc_framesperdesc;
- JOM(4, "%i=video_isoc_buffer_size\n",
- peasycap->video_isoc_buffer_size);
- if ((PAGE_SIZE << VIDEO_ISOC_ORDER) <
- peasycap->video_isoc_buffer_size) {
- SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_interface) {
- SAM("MISTAKE: video_interface is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_altsetting_on) {
- SAM("MISTAKE: video_altsetting_on is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_altsetting_off) {
- SAM("MISTAKE: video_interface_off is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_endpointnumber) {
- SAM("MISTAKE: video_endpointnumber is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_isoc_maxframesize) {
- SAM("MISTAKE: video_isoc_maxframesize is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->video_isoc_buffer_size) {
- SAM("MISTAKE: video_isoc_buffer_size is unset\n");
- return -EFAULT;
- }
-
- /*
- * Allocate memory for video buffers.
- * Lists must be initialized first.
- */
- INIT_LIST_HEAD(&(peasycap->urb_video_head));
- peasycap->purb_video_head = &(peasycap->urb_video_head);
-
- rc = alloc_framebuffers(peasycap);
- if (rc < 0)
- return rc;
-
- rc = alloc_fieldbuffers(peasycap);
- if (rc < 0)
- return rc;
-
- rc = alloc_isocbuffers(peasycap);
- if (rc < 0)
- return rc;
-
- /* Allocate and initialize video urbs */
- rc = create_video_urbs(peasycap);
- if (rc < 0)
- return rc;
-
- /* Save pointer peasycap in this interface */
- usb_set_intfdata(intf, peasycap);
-
- /*
- * It is essential to initialize the hardware before,
- * rather than after, the device is registered,
- * because some udev rules triggers easycap_open()
- * immediately after registration, causing a clash.
- */
- rc = reset(peasycap);
- if (rc) {
- SAM("ERROR: reset() rc = %i\n", rc);
- return -EFAULT;
- }
-
- /* The video device can now be registered */
- if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) {
- SAM("v4l2_device_register() failed\n");
- return -ENODEV;
- }
- JOM(4, "registered device instance: %s\n",
- peasycap->v4l2_device.name);
-
- rc = easycap_register_video(peasycap);
- if (rc < 0) {
- dev_err(&intf->dev,
- "Not able to register with videodev\n");
- return -ENODEV;
- }
- break;
- }
- /* 1: Audio control */
- case 1: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
- /* Save pointer peasycap in this interface */
- usb_set_intfdata(intf, peasycap);
- JOM(4, "no initialization required for interface %i\n",
- interface->bInterfaceNumber);
- break;
- }
- /* 2: Audio streaming */
- case 2: {
- if (!peasycap) {
- SAM("MISTAKE: peasycap is NULL\n");
- return -EFAULT;
- }
- if (!isokalt) {
- SAM("ERROR: no viable audio_altsetting_on\n");
- return -ENOENT;
- }
- peasycap->audio_altsetting_on = okalt[isokalt - 1];
- JOM(4, "%i=audio_altsetting_on <====\n",
- peasycap->audio_altsetting_on);
-
- peasycap->audio_endpointnumber = okepn[isokalt - 1];
- JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber);
-
- peasycap->audio_isoc_maxframesize = okmps[isokalt - 1];
- JOM(4, "%i=audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- if (0 >= peasycap->audio_isoc_maxframesize) {
- SAM("ERROR: bad audio_isoc_maxframesize\n");
- return -ENOENT;
- }
- if (9 == peasycap->audio_isoc_maxframesize) {
- peasycap->ilk |= 0x02;
- SAM("audio hardware is microphone\n");
- peasycap->microphone = true;
- peasycap->audio_pages_per_fragment =
- PAGES_PER_AUDIO_FRAGMENT;
- } else if (256 == peasycap->audio_isoc_maxframesize) {
- peasycap->ilk &= ~0x02;
- SAM("audio hardware is AC'97\n");
- peasycap->microphone = false;
- peasycap->audio_pages_per_fragment =
- PAGES_PER_AUDIO_FRAGMENT;
- } else {
- SAM("hardware is unidentified:\n");
- SAM("%i=audio_isoc_maxframesize\n",
- peasycap->audio_isoc_maxframesize);
- return -ENOENT;
- }
-
- peasycap->audio_bytes_per_fragment =
- peasycap->audio_pages_per_fragment * PAGE_SIZE;
- peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY *
- peasycap->audio_pages_per_fragment);
-
- JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY);
- JOM(4, "%6i=audio_pages_per_fragment\n",
- peasycap->audio_pages_per_fragment);
- JOM(4, "%6i=audio_bytes_per_fragment\n",
- peasycap->audio_bytes_per_fragment);
- JOM(4, "%6i=audio_buffer_page_many\n",
- peasycap->audio_buffer_page_many);
-
- peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC;
-
- JOM(4, "%i=audio_isoc_framesperdesc\n",
- peasycap->audio_isoc_framesperdesc);
- if (0 >= peasycap->audio_isoc_framesperdesc) {
- SAM("ERROR: bad audio_isoc_framesperdesc\n");
- return -ENOENT;
- }
-
- peasycap->audio_isoc_buffer_size =
- peasycap->audio_isoc_maxframesize *
- peasycap->audio_isoc_framesperdesc;
- JOM(4, "%i=audio_isoc_buffer_size\n",
- peasycap->audio_isoc_buffer_size);
- if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) {
- SAM("MISTAKE: audio_isoc_buffer_size bigger "
- "than %li=AUDIO_ISOC_BUFFER_SIZE\n",
- AUDIO_ISOC_BUFFER_SIZE);
- return -EFAULT;
- }
- if (-1 == peasycap->audio_interface) {
- SAM("MISTAKE: audio_interface is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_altsetting_on) {
- SAM("MISTAKE: audio_altsetting_on is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_altsetting_off) {
- SAM("MISTAKE: audio_interface_off is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_endpointnumber) {
- SAM("MISTAKE: audio_endpointnumber is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_isoc_maxframesize) {
- SAM("MISTAKE: audio_isoc_maxframesize is unset\n");
- return -EFAULT;
- }
- if (-1 == peasycap->audio_isoc_buffer_size) {
- SAM("MISTAKE: audio_isoc_buffer_size is unset\n");
- return -EFAULT;
- }
-
- /*
- * Allocate memory for audio buffers.
- * Lists must be initialized first.
- */
- INIT_LIST_HEAD(&(peasycap->urb_audio_head));
- peasycap->purb_audio_head = &(peasycap->urb_audio_head);
-
- alloc_audio_buffers(peasycap);
- if (rc < 0)
- return rc;
-
- /* Allocate and initialize urbs */
- rc = create_audio_urbs(peasycap);
- if (rc < 0)
- return rc;
-
- /* Save pointer peasycap in this interface */
- usb_set_intfdata(intf, peasycap);
-
- /* The audio device can now be registered */
- JOM(4, "initializing ALSA card\n");
-
- rc = easycap_alsa_probe(peasycap);
- if (rc) {
- dev_err(&intf->dev, "easycap_alsa_probe() rc = %i\n",
- rc);
- return -ENODEV;
- }
-
-
- JOM(8, "kref_get() with %i=kref.refcount.counter\n",
- peasycap->kref.refcount.counter);
- kref_get(&peasycap->kref);
- peasycap->registered_audio++;
- break;
- }
- /* Interfaces other than 0,1,2 are unexpected */
- default:
- JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
- return -EINVAL;
- }
- SAM("ends successfully for interface %i\n", bInterfaceNumber);
- return 0;
-}
-
-/*
- * When this function is called the device has already been
- * physically unplugged.
- * Hence, peasycap->pusb_device is no longer valid.
- * This function affects alsa.
- */
-static void easycap_usb_disconnect(struct usb_interface *pusb_interface)
-{
- struct usb_host_interface *pusb_host_interface;
- struct usb_interface_descriptor *pusb_interface_descriptor;
- struct easycap *peasycap;
- int minor, kd;
- u8 bInterfaceNumber;
-
- JOT(4, "\n");
-
- pusb_host_interface = pusb_interface->cur_altsetting;
- if (!pusb_host_interface) {
- JOT(4, "ERROR: pusb_host_interface is NULL\n");
- return;
- }
- pusb_interface_descriptor = &(pusb_host_interface->desc);
- if (!pusb_interface_descriptor) {
- JOT(4, "ERROR: pusb_interface_descriptor is NULL\n");
- return;
- }
- bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber;
- minor = pusb_interface->minor;
- JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor);
-
- /* There is nothing to do for Interface Number 1 */
- if (1 == bInterfaceNumber)
- return;
-
- peasycap = usb_get_intfdata(pusb_interface);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
-
- /* If the waitqueues are not cleared a deadlock is possible */
- peasycap->video_eof = 1;
- peasycap->audio_eof = 1;
- wake_up_interruptible(&(peasycap->wq_video));
- wake_up_interruptible(&(peasycap->wq_audio));
-
- switch (bInterfaceNumber) {
- case 0:
- easycap_video_kill_urbs(peasycap);
- break;
- case 2:
- easycap_audio_kill_urbs(peasycap);
- break;
- default:
- break;
- }
-
- /*
- * Deregister
- * This procedure will block until easycap_poll(),
- * video and audio ioctl are all unlocked.
- * If this is not done an oops can occur when an easycap
- * is unplugged while the urbs are running.
- */
- kd = easycap_isdongle(peasycap);
- switch (bInterfaceNumber) {
- case 0: {
- if (0 <= kd && DONGLE_MANY > kd) {
- wake_up_interruptible(&peasycap->wq_video);
- JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
- mutex_video)) {
- SAY("ERROR: "
- "cannot lock dongle[%i].mutex_video\n", kd);
- return;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- } else {
- SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
- }
- if (!peasycap->v4l2_device.name[0]) {
- SAM("ERROR: peasycap->v4l2_device.name is empty\n");
- if (0 <= kd && DONGLE_MANY > kd)
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- return;
- }
- v4l2_device_disconnect(&peasycap->v4l2_device);
- JOM(4, "v4l2_device_disconnect() OK\n");
- v4l2_device_unregister(&peasycap->v4l2_device);
- JOM(4, "v4l2_device_unregister() OK\n");
-
- video_unregister_device(&peasycap->video_device);
- JOM(4, "intf[%i]: video_unregister_device() minor=%i\n",
- bInterfaceNumber, minor);
- peasycap->registered_video--;
-
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
- }
- break;
- }
- case 2: {
- if (0 <= kd && DONGLE_MANY > kd) {
- wake_up_interruptible(&peasycap->wq_audio);
- JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
- mutex_audio)) {
- SAY("ERROR: "
- "cannot lock dongle[%i].mutex_audio\n", kd);
- return;
- }
- JOM(4, "locked dongle[%i].mutex_audio\n", kd);
- } else
- SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
- if (0 != snd_card_free(peasycap->psnd_card)) {
- SAY("ERROR: snd_card_free() failed\n");
- } else {
- peasycap->psnd_card = NULL;
- (peasycap->registered_audio)--;
- }
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
- JOM(4, "unlocked dongle[%i].mutex_audio\n", kd);
- }
- break;
- }
- default:
- break;
- }
-
- /*
- * If no remaining references to peasycap,
- * call easycap_delete.
- * (Also when alsa has been in use)
- */
- if (!peasycap->kref.refcount.counter) {
- SAM("ERROR: peasycap->kref.refcount.counter is zero "
- "so cannot call kref_put()\n");
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- if (0 <= kd && DONGLE_MANY > kd) {
- JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
- SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd);
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- JOM(4, "locked dongle[%i].mutex_video\n", kd);
- JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
- if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) {
- SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd);
- mutex_unlock(&(easycapdc60_dongle[kd].mutex_video));
- JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
- SAM("ending unsuccessfully: may cause memory leak\n");
- return;
- }
- JOM(4, "locked dongle[%i].mutex_audio\n", kd);
- }
- JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n",
- bInterfaceNumber, (int)peasycap->kref.refcount.counter);
- kref_put(&peasycap->kref, easycap_delete);
- JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
- if (0 <= kd && DONGLE_MANY > kd) {
- mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio));
- JOT(4, "unlocked dongle[%i].mutex_audio\n", kd);
- mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
- JOT(4, "unlocked dongle[%i].mutex_video\n", kd);
- }
- JOM(4, "ends\n");
- return;
-}
-
-/* Devices supported by this driver */
-static struct usb_device_id easycap_usb_device_id_table[] = {
- {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)},
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table);
-static struct usb_driver easycap_usb_driver = {
- .name = "easycap",
- .id_table = easycap_usb_device_id_table,
- .probe = easycap_usb_probe,
- .disconnect = easycap_usb_disconnect,
-};
-
-static int __init easycap_module_init(void)
-{
- int k, rc;
-
- printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n");
-
- JOT(4, "begins. %i=debug %i=bars %i=gain\n",
- easycap_debug, easycap_bars, easycap_gain);
-
- mutex_init(&mutex_dongle);
- for (k = 0; k < DONGLE_MANY; k++) {
- easycapdc60_dongle[k].peasycap = NULL;
- mutex_init(&easycapdc60_dongle[k].mutex_video);
- mutex_init(&easycapdc60_dongle[k].mutex_audio);
- }
- rc = usb_register(&easycap_usb_driver);
- if (rc)
- printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc);
-
- return rc;
-}
-
-static void __exit easycap_module_exit(void)
-{
- usb_deregister(&easycap_usb_driver);
-}
-
-module_init(easycap_module_init);
-module_exit(easycap_module_exit);
diff --git a/drivers/staging/media/easycap/easycap_settings.c b/drivers/staging/media/easycap/easycap_settings.c
deleted file mode 100644
index 3f5f5b3e5a35..000000000000
--- a/drivers/staging/media/easycap/easycap_settings.c
+++ /dev/null
@@ -1,696 +0,0 @@
-/******************************************************************************
-* *
-* easycap_settings.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*---------------------------------------------------------------------------*/
-/*
- * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
- * 0 => 25 fps
- * 1 => 30 fps
- *
- * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
- * 0 => full framerate
- * 1 => 20% framerate
- */
-/*---------------------------------------------------------------------------*/
-const struct easycap_standard easycap_standard[] = {
- {
- .mask = 0x00FF & PAL_BGHIN ,
- .v4l2_standard = {
- .index = PAL_BGHIN,
- .id = (V4L2_STD_PAL_B |
- V4L2_STD_PAL_G | V4L2_STD_PAL_H |
- V4L2_STD_PAL_I | V4L2_STD_PAL_N),
- .name = "PAL_BGHIN",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_N_443 ,
- .v4l2_standard = {
- .index = NTSC_N_443,
- .id = V4L2_STD_UNKNOWN,
- .name = "NTSC_N_443",
- .frameperiod = {1, 25},
- .framelines = 480,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_Nc ,
- .v4l2_standard = {
- .index = PAL_Nc,
- .id = V4L2_STD_PAL_Nc,
- .name = "PAL_Nc",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_N ,
- .v4l2_standard = {
- .index = NTSC_N,
- .id = V4L2_STD_UNKNOWN,
- .name = "NTSC_N",
- .frameperiod = {1, 25},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & SECAM ,
- .v4l2_standard = {
- .index = SECAM,
- .id = V4L2_STD_SECAM,
- .name = "SECAM",
- .frameperiod = {1, 25},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_M ,
- .v4l2_standard = {
- .index = NTSC_M,
- .id = V4L2_STD_NTSC_M,
- .name = "NTSC_M",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_M_JP ,
- .v4l2_standard = {
- .index = NTSC_M_JP,
- .id = V4L2_STD_NTSC_M_JP,
- .name = "NTSC_M_JP",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_60 ,
- .v4l2_standard = {
- .index = PAL_60,
- .id = V4L2_STD_PAL_60,
- .name = "PAL_60",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & NTSC_443 ,
- .v4l2_standard = {
- .index = NTSC_443,
- .id = V4L2_STD_NTSC_443,
- .name = "NTSC_443",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x00FF & PAL_M ,
- .v4l2_standard = {
- .index = PAL_M,
- .id = V4L2_STD_PAL_M,
- .name = "PAL_M",
- .frameperiod = {1, 30},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW),
- .v4l2_standard = {
- .index = PAL_BGHIN_SLOW,
- .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G |
- V4L2_STD_PAL_H |
- V4L2_STD_PAL_I | V4L2_STD_PAL_N |
- (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_BGHIN_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW),
- .v4l2_standard = {
- .index = NTSC_N_443_SLOW,
- .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)),
- .name = "NTSC_N_443_SLOW",
- .frameperiod = {1, 5},
- .framelines = 480,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW),
- .v4l2_standard = {
- .index = PAL_Nc_SLOW,
- .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_Nc_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_N_SLOW),
- .v4l2_standard = {
- .index = NTSC_N_SLOW,
- .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)),
- .name = "NTSC_N_SLOW",
- .frameperiod = {1, 5},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & SECAM_SLOW),
- .v4l2_standard = {
- .index = SECAM_SLOW,
- .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)),
- .name = "SECAM_SLOW",
- .frameperiod = {1, 5},
- .framelines = 625,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_M_SLOW),
- .v4l2_standard = {
- .index = NTSC_M_SLOW,
- .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_M_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW),
- .v4l2_standard = {
- .index = NTSC_M_JP_SLOW,
- .id = (V4L2_STD_NTSC_M_JP |
- (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_M_JP_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_60_SLOW),
- .v4l2_standard = {
- .index = PAL_60_SLOW,
- .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_60_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & NTSC_443_SLOW),
- .v4l2_standard = {
- .index = NTSC_443_SLOW,
- .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)),
- .name = "NTSC_443_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0x8000 | (0x00FF & PAL_M_SLOW),
- .v4l2_standard = {
- .index = PAL_M_SLOW,
- .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)),
- .name = "PAL_M_SLOW",
- .frameperiod = {1, 6},
- .framelines = 525,
- .reserved = {0, 0, 0, 0}
- }
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .mask = 0xFFFF
- }
-};
-/*---------------------------------------------------------------------------*/
-/*
- * THE 16-BIT easycap_format.mask HAS MEANING:
- * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS
- * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS
- * BITS 5-7: NUMBER OF BYTES PER PIXEL
- * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED
- * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS
- * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED
- * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS
- * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED
- * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS
- * IT FOLLOWS THAT:
- * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5)
- * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask))
- *
- * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask))
- *
- * offerfields IS true IF (0 != (0x1000 & easycap_format.mask))
- */
-/*---------------------------------------------------------------------------*/
-
-struct easycap_format easycap_format[1 + SETTINGS_MANY];
-
-int easycap_video_fillin_formats(void)
-{
- const char *name1, *name2, *name3, *name4;
- struct v4l2_format *fmt;
- int i, j, k, m, n;
- u32 width, height, pixelformat, bytesperline, sizeimage;
- u16 mask1, mask2, mask3, mask4;
- enum v4l2_field field;
- enum v4l2_colorspace colorspace;
-
- for (i = 0, n = 0; i < STANDARD_MANY; i++) {
- mask1 = 0x0000;
- switch (i) {
- case PAL_BGHIN: {
- mask1 = 0x1F & PAL_BGHIN;
- name1 = "PAL_BGHIN";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case SECAM: {
- mask1 = 0x1F & SECAM;
- name1 = "SECAM";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_Nc: {
- mask1 = 0x1F & PAL_Nc;
- name1 = "PAL_Nc";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_60: {
- mask1 = 0x1F & PAL_60;
- name1 = "PAL_60";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_M: {
- mask1 = 0x1F & PAL_M;
- name1 = "PAL_M";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case NTSC_M: {
- mask1 = 0x1F & NTSC_M;
- name1 = "NTSC_M";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_443: {
- mask1 = 0x1F & NTSC_443;
- name1 = "NTSC_443";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_M_JP: {
- mask1 = 0x1F & NTSC_M_JP;
- name1 = "NTSC_M_JP";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N: {
- mask1 = 0x1F & NTSC_M;
- name1 = "NTSC_N";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_443: {
- mask1 = 0x1F & NTSC_N_443;
- name1 = "NTSC_N_443";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case PAL_BGHIN_SLOW: {
- mask1 = 0x001F & PAL_BGHIN_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_BGHIN_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case SECAM_SLOW: {
- mask1 = 0x001F & SECAM_SLOW;
- mask1 |= 0x0200;
- name1 = "SECAM_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_Nc_SLOW: {
- mask1 = 0x001F & PAL_Nc_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_Nc_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_60_SLOW: {
- mask1 = 0x001F & PAL_60_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_60_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case PAL_M_SLOW: {
- mask1 = 0x001F & PAL_M_SLOW;
- mask1 |= 0x0200;
- name1 = "PAL_M_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
- break;
- }
- case NTSC_M_SLOW: {
- mask1 = 0x001F & NTSC_M_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_M_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_443_SLOW: {
- mask1 = 0x001F & NTSC_443_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_443_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_M_JP_SLOW: {
- mask1 = 0x001F & NTSC_M_JP_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_M_JP_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_SLOW: {
- mask1 = 0x001F & NTSC_N_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_N_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- case NTSC_N_443_SLOW: {
- mask1 = 0x001F & NTSC_N_443_SLOW;
- mask1 |= 0x0200;
- name1 = "NTSC_N_443_SLOW";
- colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
- break;
- }
- default:
- return -1;
- }
-
- for (j = 0; j < RESOLUTION_MANY; j++) {
- mask2 = 0x0000;
- switch (j) {
- case AT_720x576: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_720x576";
- width = 720;
- height = 576;
- break;
- }
- case AT_704x576: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_704x576";
- width = 704;
- height = 576;
- break;
- }
- case AT_640x480: {
- name2 = "_AT_640x480";
- width = 640;
- height = 480;
- break;
- }
- case AT_720x480: {
- if (!(0x1 & mask1))
- continue;
- name2 = "_AT_720x480";
- width = 720;
- height = 480;
- break;
- }
- case AT_360x288: {
- if (0x1 & mask1)
- continue;
- name2 = "_AT_360x288";
- width = 360;
- height = 288;
- mask2 = 0x0800;
- break;
- }
- case AT_320x240: {
- name2 = "_AT_320x240";
- width = 320;
- height = 240;
- mask2 = 0x0800;
- break;
- }
- case AT_360x240: {
- if (!(0x1 & mask1))
- continue;
- name2 = "_AT_360x240";
- width = 360;
- height = 240;
- mask2 = 0x0800;
- break;
- }
- default:
- return -2;
- }
-
- for (k = 0; k < PIXELFORMAT_MANY; k++) {
- mask3 = 0x0000;
- switch (k) {
- case FMT_UYVY: {
- name3 = __stringify(FMT_UYVY);
- pixelformat = V4L2_PIX_FMT_UYVY;
- mask3 |= (0x02 << 5);
- break;
- }
- case FMT_YUY2: {
- name3 = __stringify(FMT_YUY2);
- pixelformat = V4L2_PIX_FMT_YUYV;
- mask3 |= (0x02 << 5);
- mask3 |= 0x0100;
- break;
- }
- case FMT_RGB24: {
- name3 = __stringify(FMT_RGB24);
- pixelformat = V4L2_PIX_FMT_RGB24;
- mask3 |= (0x03 << 5);
- break;
- }
- case FMT_RGB32: {
- name3 = __stringify(FMT_RGB32);
- pixelformat = V4L2_PIX_FMT_RGB32;
- mask3 |= (0x04 << 5);
- break;
- }
- case FMT_BGR24: {
- name3 = __stringify(FMT_BGR24);
- pixelformat = V4L2_PIX_FMT_BGR24;
- mask3 |= (0x03 << 5);
- mask3 |= 0x0100;
- break;
- }
- case FMT_BGR32: {
- name3 = __stringify(FMT_BGR32);
- pixelformat = V4L2_PIX_FMT_BGR32;
- mask3 |= (0x04 << 5);
- mask3 |= 0x0100;
- break;
- }
- default:
- return -3;
- }
- bytesperline = width * ((mask3 & 0x00E0) >> 5);
- sizeimage = bytesperline * height;
-
- for (m = 0; m < INTERLACE_MANY; m++) {
- mask4 = 0x0000;
- switch (m) {
- case FIELD_NONE: {
- name4 = "-n";
- field = V4L2_FIELD_NONE;
- break;
- }
- case FIELD_INTERLACED: {
- name4 = "-i";
- mask4 |= 0x1000;
- field = V4L2_FIELD_INTERLACED;
- break;
- }
- default:
- return -4;
- }
- if (SETTINGS_MANY <= n)
- return -5;
-
- strcpy(easycap_format[n].name, name1);
- strcat(easycap_format[n].name, name2);
- strcat(easycap_format[n].name, "_");
- strcat(easycap_format[n].name, name3);
- strcat(easycap_format[n].name, name4);
- easycap_format[n].mask =
- mask1 | mask2 | mask3 | mask4;
- fmt = &easycap_format[n].v4l2_format;
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- fmt->fmt.pix.width = width;
- fmt->fmt.pix.height = height;
- fmt->fmt.pix.pixelformat = pixelformat;
- fmt->fmt.pix.field = field;
- fmt->fmt.pix.bytesperline = bytesperline;
- fmt->fmt.pix.sizeimage = sizeimage;
- fmt->fmt.pix.colorspace = colorspace;
- fmt->fmt.pix.priv = 0;
- n++;
- }
- }
- }
- }
- if ((1 + SETTINGS_MANY) <= n)
- return -6;
- easycap_format[n].mask = 0xFFFF;
- return n;
-}
-/*---------------------------------------------------------------------------*/
-struct v4l2_queryctrl easycap_control[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0A_DEFAULT,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0B_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0C_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = SAA_0D_DEFAULT + 128,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_AUDIO_VOLUME,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Volume",
- .minimum = 0,
- .maximum = 31,
- .step = 1,
- .default_value = 16,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mute",
- .default_value = true,
- .flags = 0,
- .reserved = {0, 0}
- },
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- {
- .id = 0xFFFFFFFF
- }
-};
-/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_sound.c b/drivers/staging/media/easycap/easycap_sound.c
deleted file mode 100644
index 8c8bcae8ded8..000000000000
--- a/drivers/staging/media/easycap/easycap_sound.c
+++ /dev/null
@@ -1,750 +0,0 @@
-/******************************************************************************
-* *
-* easycap_sound.c *
-* *
-* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 *
-* *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*--------------------------------------------------------------------------*/
-/*
- * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
- */
-/*--------------------------------------------------------------------------*/
-static const struct snd_pcm_hardware alsa_hardware = {
- .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP_VALID,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
- .rate_min = 32000,
- .rate_max = 48000,
- .channels_min = 2,
- .channels_max = 2,
- .buffer_bytes_max = PAGE_SIZE *
- PAGES_PER_AUDIO_FRAGMENT *
- AUDIO_FRAGMENT_MANY,
- .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT,
- .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2,
- .periods_min = AUDIO_FRAGMENT_MANY,
- .periods_max = AUDIO_FRAGMENT_MANY * 2,
-};
-
-
-/*---------------------------------------------------------------------------*/
-/*
- * SUBMIT ALL AUDIO URBS.
- */
-/*---------------------------------------------------------------------------*/
-static int easycap_audio_submit_urbs(struct easycap *peasycap)
-{
- struct data_urb *pdata_urb;
- struct urb *purb;
- struct list_head *plist_head;
- int j, isbad, nospc, m, rc;
- int isbuf;
-
- if (!peasycap->purb_audio_head) {
- SAM("ERROR: peasycap->urb_audio_head uninitialized\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -EFAULT;
- }
-
- if (peasycap->audio_isoc_streaming) {
- JOM(4, "already streaming audio urbs\n");
- return 0;
- }
-
- JOM(4, "initial submission of all audio urbs\n");
- rc = usb_set_interface(peasycap->pusb_device,
- peasycap->audio_interface,
- peasycap->audio_altsetting_on);
- JOM(8, "usb_set_interface(.,%i,%i) returned %i\n",
- peasycap->audio_interface,
- peasycap->audio_altsetting_on, rc);
-
- isbad = 0;
- nospc = 0;
- m = 0;
- list_for_each(plist_head, peasycap->purb_audio_head) {
- pdata_urb = list_entry(plist_head, struct data_urb, list_head);
- if (pdata_urb && pdata_urb->purb) {
- purb = pdata_urb->purb;
- isbuf = pdata_urb->isbuf;
-
- purb->interval = 1;
- purb->dev = peasycap->pusb_device;
- purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
- peasycap->audio_endpointnumber);
- purb->transfer_flags = URB_ISO_ASAP;
- purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo;
- purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size;
- purb->complete = easycap_alsa_complete;
- purb->context = peasycap;
- purb->start_frame = 0;
- purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
- for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
- purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize;
- purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize;
- }
-
- rc = usb_submit_urb(purb, GFP_KERNEL);
- if (rc) {
- isbad++;
- SAM("ERROR: usb_submit_urb() failed"
- " for urb with rc: -%s: %d\n",
- strerror(rc), rc);
- } else {
- m++;
- }
- } else {
- isbad++;
- }
- }
- if (nospc) {
- SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
- SAM("..... possibly inadequate USB bandwidth\n");
- peasycap->audio_eof = 1;
- }
-
- if (isbad)
- easycap_audio_kill_urbs(peasycap);
- else
- peasycap->audio_isoc_streaming = m;
-
- return 0;
-}
-/*---------------------------------------------------------------------------*/
-/*
- * COMMON AUDIO INITIALIZATION
- */
-/*---------------------------------------------------------------------------*/
-static int easycap_sound_setup(struct easycap *peasycap)
-{
- int rc;
-
- JOM(4, "starting initialization\n");
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL.\n");
- return -EFAULT;
- }
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device is NULL\n");
- return -ENODEV;
- }
- JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device);
-
- rc = easycap_audio_setup(peasycap);
- JOM(8, "audio_setup() returned %i\n", rc);
-
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device has become NULL\n");
- return -ENODEV;
- }
-/*---------------------------------------------------------------------------*/
- if (!peasycap->pusb_device) {
- SAM("ERROR: peasycap->pusb_device has become NULL\n");
- return -ENODEV;
- }
- rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface,
- peasycap->audio_altsetting_on);
- JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface,
- peasycap->audio_altsetting_on, rc);
-
- rc = easycap_wakeup_device(peasycap->pusb_device);
- JOM(8, "wakeup_device() returned %i\n", rc);
-
- peasycap->audio_eof = 0;
- peasycap->audio_idle = 0;
-
- easycap_audio_submit_urbs(peasycap);
-
- JOM(4, "finished initialization\n");
- return 0;
-}
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER
- * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
- * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
- */
-/*---------------------------------------------------------------------------*/
-void easycap_alsa_complete(struct urb *purb)
-{
- struct easycap *peasycap;
- struct snd_pcm_substream *pss;
- struct snd_pcm_runtime *prt;
- int dma_bytes, fragment_bytes;
- int isfragment;
- u8 *p1, *p2;
- s16 tmp;
- int i, j, more, much, rc;
-#ifdef UPSAMPLE
- int k;
- s16 oldaudio, newaudio, delta;
-#endif /*UPSAMPLE*/
-
- JOT(16, "\n");
-
- if (!purb) {
- SAY("ERROR: purb is NULL\n");
- return;
- }
- peasycap = purb->context;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- much = 0;
- if (peasycap->audio_idle) {
- JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
- peasycap->audio_idle, peasycap->audio_isoc_streaming);
- if (peasycap->audio_isoc_streaming)
- goto resubmit;
- }
-/*---------------------------------------------------------------------------*/
- pss = peasycap->psubstream;
- if (!pss)
- goto resubmit;
- prt = pss->runtime;
- if (!prt)
- goto resubmit;
- dma_bytes = (int)prt->dma_bytes;
- if (0 == dma_bytes)
- goto resubmit;
- fragment_bytes = 4 * ((int)prt->period_size);
- if (0 == fragment_bytes)
- goto resubmit;
-/* -------------------------------------------------------------------------*/
- if (purb->status) {
- if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
- JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
- return;
- }
- SAM("ERROR: non-zero urb status: -%s: %d\n",
- strerror(purb->status), purb->status);
- goto resubmit;
- }
-/*---------------------------------------------------------------------------*/
-/*
- * PROCEED HERE WHEN NO ERROR
- */
-/*---------------------------------------------------------------------------*/
-
-#ifdef UPSAMPLE
- oldaudio = peasycap->oldaudio;
-#endif /*UPSAMPLE*/
-
- for (i = 0; i < purb->number_of_packets; i++) {
- if (purb->iso_frame_desc[i].status < 0) {
- SAM("-%s: %d\n",
- strerror(purb->iso_frame_desc[i].status),
- purb->iso_frame_desc[i].status);
- }
- if (purb->iso_frame_desc[i].status) {
- JOM(12, "discarding audio samples because "
- "%i=purb->iso_frame_desc[i].status\n",
- purb->iso_frame_desc[i].status);
- continue;
- }
- more = purb->iso_frame_desc[i].actual_length;
- if (more == 0) {
- peasycap->audio_mt++;
- continue;
- }
- if (0 > more) {
- SAM("MISTAKE: more is negative\n");
- return;
- }
-
- if (peasycap->audio_mt) {
- JOM(12, "%4i empty audio urb frames\n",
- peasycap->audio_mt);
- peasycap->audio_mt = 0;
- }
-
- p1 = (u8 *)(purb->transfer_buffer +
- purb->iso_frame_desc[i].offset);
-
- /*
- * COPY more BYTES FROM ISOC BUFFER
- * TO THE DMA BUFFER, CONVERTING
- * 8-BIT MONO TO 16-BIT SIGNED
- * LITTLE-ENDIAN SAMPLES IF NECESSARY
- */
- while (more) {
- much = dma_bytes - peasycap->dma_fill;
- if (0 > much) {
- SAM("MISTAKE: much is negative\n");
- return;
- }
- if (0 == much) {
- peasycap->dma_fill = 0;
- peasycap->dma_next = fragment_bytes;
- JOM(8, "wrapped dma buffer\n");
- }
- if (!peasycap->microphone) {
- if (much > more)
- much = more;
- memcpy(prt->dma_area + peasycap->dma_fill,
- p1, much);
- p1 += much;
- more -= much;
- } else {
-#ifdef UPSAMPLE
- if (much % 16)
- JOM(8, "MISTAKE? much"
- " is not divisible by 16\n");
- if (much > (16 * more))
- much = 16 * more;
- p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
-
- for (j = 0; j < (much / 16); j++) {
- newaudio = ((int) *p1) - 128;
- newaudio = 128 * newaudio;
-
- delta = (newaudio - oldaudio) / 4;
- tmp = oldaudio + delta;
-
- for (k = 0; k < 4; k++) {
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p2 += 2;
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p2 += 2;
- tmp += delta;
- }
- p1++;
- more--;
- oldaudio = tmp;
- }
-#else /*!UPSAMPLE*/
- if (much > (2 * more))
- much = 2 * more;
- p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
-
- for (j = 0; j < (much / 2); j++) {
- tmp = ((int) *p1) - 128;
- tmp = 128 * tmp;
- *p2 = (0x00FF & tmp);
- *(p2 + 1) = (0xFF00 & tmp) >> 8;
- p1++;
- p2 += 2;
- more--;
- }
-#endif /*UPSAMPLE*/
- }
- peasycap->dma_fill += much;
- if (peasycap->dma_fill >= peasycap->dma_next) {
- isfragment = peasycap->dma_fill / fragment_bytes;
- if (0 > isfragment) {
- SAM("MISTAKE: isfragment is negative\n");
- return;
- }
- peasycap->dma_read = (isfragment - 1) * fragment_bytes;
- peasycap->dma_next = (isfragment + 1) * fragment_bytes;
- if (dma_bytes < peasycap->dma_next)
- peasycap->dma_next = fragment_bytes;
-
- if (0 <= peasycap->dma_read) {
- JOM(8, "snd_pcm_period_elapsed(), %i="
- "isfragment\n", isfragment);
- snd_pcm_period_elapsed(pss);
- }
- }
- }
-
-#ifdef UPSAMPLE
- peasycap->oldaudio = oldaudio;
-#endif /*UPSAMPLE*/
-
- }
-/*---------------------------------------------------------------------------*/
-/*
- * RESUBMIT THIS URB
- */
-/*---------------------------------------------------------------------------*/
-resubmit:
- if (peasycap->audio_isoc_streaming == 0)
- return;
-
- rc = usb_submit_urb(purb, GFP_ATOMIC);
- if (rc) {
- if ((-ENODEV != rc) && (-ENOENT != rc)) {
- SAM("ERROR: while %i=audio_idle, usb_submit_urb failed "
- "with rc: -%s :%d\n",
- peasycap->audio_idle, strerror(rc), rc);
- }
- if (0 < peasycap->audio_isoc_streaming)
- peasycap->audio_isoc_streaming--;
- }
- return;
-}
-/*****************************************************************************/
-static int easycap_alsa_open(struct snd_pcm_substream *pss)
-{
- struct snd_pcm *psnd_pcm;
- struct snd_card *psnd_card;
- struct easycap *peasycap;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- psnd_pcm = pss->pcm;
- if (!psnd_pcm) {
- SAY("ERROR: psnd_pcm is NULL\n");
- return -EFAULT;
- }
- psnd_card = psnd_pcm->card;
- if (!psnd_card) {
- SAY("ERROR: psnd_card is NULL\n");
- return -EFAULT;
- }
-
- peasycap = psnd_card->private_data;
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if (peasycap->psnd_card != psnd_card) {
- SAM("ERROR: bad peasycap->psnd_card\n");
- return -EFAULT;
- }
- if (peasycap->psubstream) {
- SAM("ERROR: bad peasycap->psubstream\n");
- return -EFAULT;
- }
- pss->private_data = peasycap;
- peasycap->psubstream = pss;
- pss->runtime->hw = peasycap->alsa_hardware;
- pss->runtime->private_data = peasycap;
- pss->private_data = peasycap;
-
- if (0 != easycap_sound_setup(peasycap)) {
- JOM(4, "ending unsuccessfully\n");
- return -EFAULT;
- }
- JOM(4, "ending successfully\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_close(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- pss->private_data = NULL;
- peasycap->psubstream = NULL;
- JOT(4, "ending successfully\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz)
-{
- struct snd_pcm_runtime *prt;
- JOT(4, "\n");
-
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- if (!prt) {
- SAY("ERROR: substream.runtime is NULL\n");
- return -EFAULT;
- }
- if (prt->dma_area) {
- if (prt->dma_bytes > sz)
- return 0;
- vfree(prt->dma_area);
- }
- prt->dma_area = vmalloc(sz);
- if (!prt->dma_area)
- return -ENOMEM;
- prt->dma_bytes = sz;
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_hw_params(struct snd_pcm_substream *pss,
- struct snd_pcm_hw_params *phw)
-{
- int rc;
-
- JOT(4, "%i\n", (params_buffer_bytes(phw)));
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw));
- if (rc)
- return rc;
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_hw_free(struct snd_pcm_substream *pss)
-{
- struct snd_pcm_runtime *prt;
- JOT(4, "\n");
-
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- if (!prt) {
- SAY("ERROR: substream.runtime is NULL\n");
- return -EFAULT;
- }
- if (prt->dma_area) {
- JOT(8, "prt->dma_area = %p\n", prt->dma_area);
- vfree(prt->dma_area);
- prt->dma_area = NULL;
- } else
- JOT(8, "dma_area already freed\n");
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_prepare(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
- struct snd_pcm_runtime *prt;
-
- JOT(4, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- prt = pss->runtime;
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
-
- JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate);
- JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size);
- JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods);
- JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size);
- JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes);
- JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary);
- JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step);
- JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits);
- JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits);
- JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align);
- JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base);
- JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n",
- pss->runtime->hw_ptr_interrupt);
-
- if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) {
- SAY("MISTAKE: unexpected ALSA parameters\n");
- return -ENOENT;
- }
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_ack(struct snd_pcm_substream *pss)
-{
- return 0;
-}
-/*****************************************************************************/
-static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd)
-{
- struct easycap *peasycap;
-
- JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START,
- SNDRV_PCM_TRIGGER_STOP);
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START: {
- peasycap->audio_idle = 0;
- break;
- }
- case SNDRV_PCM_TRIGGER_STOP: {
- peasycap->audio_idle = 1;
- break;
- }
- default:
- return -EINVAL;
- }
- return 0;
-}
-/*****************************************************************************/
-static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss)
-{
- struct easycap *peasycap;
- snd_pcm_uframes_t offset;
-
- JOT(16, "\n");
- if (!pss) {
- SAY("ERROR: pss is NULL\n");
- return -EFAULT;
- }
- peasycap = snd_pcm_substream_chip(pss);
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -EFAULT;
- }
- if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) {
- JOM(8, "returning -EIO because "
- "%i=audio_idle %i=audio_eof\n",
- peasycap->audio_idle, peasycap->audio_eof);
- return -EIO;
- }
-/*---------------------------------------------------------------------------*/
- if (0 > peasycap->dma_read) {
- JOM(8, "returning -EBUSY\n");
- return -EBUSY;
- }
- offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4;
- JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base);
- JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n",
- (int)pss->runtime->hw_ptr_interrupt);
- JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n",
- (int)offset, peasycap->dma_read, peasycap->dma_next);
- return offset;
-}
-/*****************************************************************************/
-static struct page *
-easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset)
-{
- return vmalloc_to_page(pss->runtime->dma_area + offset);
-}
-/*****************************************************************************/
-
-static struct snd_pcm_ops easycap_alsa_pcm_ops = {
- .open = easycap_alsa_open,
- .close = easycap_alsa_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = easycap_alsa_hw_params,
- .hw_free = easycap_alsa_hw_free,
- .prepare = easycap_alsa_prepare,
- .ack = easycap_alsa_ack,
- .trigger = easycap_alsa_trigger,
- .pointer = easycap_alsa_pointer,
- .page = easycap_alsa_page,
-};
-
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
-/*
- * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS
- * MEANS MODULE easycap. BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
-int easycap_alsa_probe(struct easycap *peasycap)
-{
- int rc;
- struct snd_card *psnd_card;
- struct snd_pcm *psnd_pcm;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return -ENODEV;
- }
- if (0 > peasycap->minor) {
- SAY("ERROR: no minor\n");
- return -ENODEV;
- }
-
- peasycap->alsa_hardware = alsa_hardware;
- if (peasycap->microphone) {
- peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000;
- peasycap->alsa_hardware.rate_min = 32000;
- peasycap->alsa_hardware.rate_max = 32000;
- } else {
- peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000;
- peasycap->alsa_hardware.rate_min = 48000;
- peasycap->alsa_hardware.rate_max = 48000;
- }
-
- if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa",
- THIS_MODULE, 0, &psnd_card)) {
- SAY("ERROR: Cannot do ALSA snd_card_create()\n");
- return -EFAULT;
- }
-
- sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor);
- strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION);
- strcpy(&psnd_card->shortname[0], "easycap_alsa");
- sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]);
-
- psnd_card->dev = &peasycap->pusb_device->dev;
- psnd_card->private_data = peasycap;
- peasycap->psnd_card = psnd_card;
-
- rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm);
- if (rc) {
- SAM("ERROR: Cannot do ALSA snd_pcm_new()\n");
- snd_card_free(psnd_card);
- return -EFAULT;
- }
-
- snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE,
- &easycap_alsa_pcm_ops);
- psnd_pcm->info_flags = 0;
- strcpy(&psnd_pcm->name[0], &psnd_card->id[0]);
- psnd_pcm->private_data = peasycap;
- peasycap->psnd_pcm = psnd_pcm;
- peasycap->psubstream = NULL;
-
- rc = snd_card_register(psnd_card);
- if (rc) {
- SAM("ERROR: Cannot do ALSA snd_card_register()\n");
- snd_card_free(psnd_card);
- return -EFAULT;
- }
-
- SAM("registered %s\n", &psnd_card->id[0]);
- return 0;
-}
-
diff --git a/drivers/staging/media/easycap/easycap_testcard.c b/drivers/staging/media/easycap/easycap_testcard.c
deleted file mode 100644
index 0f71470ace39..000000000000
--- a/drivers/staging/media/easycap/easycap_testcard.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/******************************************************************************
-* *
-* easycap_testcard.c *
-* *
-******************************************************************************/
-/*
- *
- * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
- *
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
-*/
-/*****************************************************************************/
-
-#include "easycap.h"
-
-/*****************************************************************************/
-#define TESTCARD_BYTESPERLINE (2 * 720)
-void
-easycap_testcard(struct easycap *peasycap, int field)
-{
- int total;
- int y, u, v, r, g, b;
- unsigned char uyvy[4];
- int i1, line, k, m, n, more, much, barwidth, barheight;
- unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
- struct data_buffer *pfield_buffer;
-
- if (!peasycap) {
- SAY("ERROR: peasycap is NULL\n");
- return;
- }
- JOM(8, "%i=field\n", field);
- switch (peasycap->width) {
- case 720:
- case 360: {
- barwidth = (2 * 720) / 8;
- break;
- }
- case 704:
- case 352: {
- barwidth = (2 * 704) / 8;
- break;
- }
- case 640:
- case 320: {
- barwidth = (2 * 640) / 8;
- break;
- }
- default: {
- SAM("ERROR: cannot set barwidth\n");
- return;
- }
- }
- if (TESTCARD_BYTESPERLINE < barwidth) {
- SAM("ERROR: barwidth is too large\n");
- return;
- }
- switch (peasycap->height) {
- case 576:
- case 288: {
- barheight = 576;
- break;
- }
- case 480:
- case 240: {
- barheight = 480;
- break;
- }
- default: {
- SAM("ERROR: cannot set barheight\n");
- return;
- }
- }
- total = 0;
- k = field;
- m = 0;
- n = 0;
-
- for (line = 0; line < (barheight / 2); line++) {
- for (i1 = 0; i1 < 8; i1++) {
- r = (i1 * 256)/8;
- g = (i1 * 256)/8;
- b = (i1 * 256)/8;
-
- y = 299*r/1000 + 587*g/1000 + 114*b/1000 ;
- u = -147*r/1000 - 289*g/1000 + 436*b/1000 ;
- u = u + 128;
- v = 615*r/1000 - 515*g/1000 - 100*b/1000 ;
- v = v + 128;
-
- uyvy[0] = 0xFF & u ;
- uyvy[1] = 0xFF & y ;
- uyvy[2] = 0xFF & v ;
- uyvy[3] = 0xFF & y ;
-
- p1 = &bfbar[0];
- while (p1 < &bfbar[barwidth]) {
- *p1++ = uyvy[0] ;
- *p1++ = uyvy[1] ;
- *p1++ = uyvy[2] ;
- *p1++ = uyvy[3] ;
- total += 4;
- }
-
- p1 = &bfbar[0];
- more = barwidth;
-
- while (more) {
- if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) {
- SAM("ERROR: bad m reached\n");
- return;
- }
- if (PAGE_SIZE < n) {
- SAM("ERROR: bad n reached\n");
- return;
- }
-
- if (0 > more) {
- SAM("ERROR: internal fault\n");
- return;
- }
-
- much = PAGE_SIZE - n;
- if (much > more)
- much = more;
- pfield_buffer = &peasycap->field_buffer[k][m];
- p2 = pfield_buffer->pgo + n;
- memcpy(p2, p1, much);
-
- p1 += much;
- n += much;
- more -= much;
- if (PAGE_SIZE == n) {
- m++;
- n = 0;
- }
- }
- }
- }
- return;
-}
diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile
index 6ee837c56706..3fdbef5306a0 100644
--- a/drivers/staging/media/go7007/Makefile
+++ b/drivers/staging/media/go7007/Makefile
@@ -24,7 +24,7 @@ s2250-y := s2250-board.o
#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3
# S2250 needs cypress ezusb loader from dvb-usb
-ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/dvb/dvb-usb
+ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/usb/dvb-usb
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/dvb/dvb-core
+ccflags-y += -Idrivers/media/dvb-frontends
+ccflags-y += -Idrivers/media/dvb-core
diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c
index c184ad30fbd8..980371b02749 100644
--- a/drivers/staging/media/go7007/go7007-v4l2.c
+++ b/drivers/staging/media/go7007/go7007-v4l2.c
@@ -1372,7 +1372,7 @@ static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
/* FIXME: vidioc_s_crop is not really implemented!!!
*/
-static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+static int vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop)
{
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1392,7 +1392,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
}
static int vidioc_s_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *params)
+ const struct v4l2_jpegcompression *params)
{
if (params->quality != 50 ||
params->jpeg_markers != (V4L2_JPEG_MARKER_DHT |
diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig
index 526ec0fc2f04..e60a59fc252b 100644
--- a/drivers/staging/media/lirc/Kconfig
+++ b/drivers/staging/media/lirc/Kconfig
@@ -63,12 +63,6 @@ config LIRC_SIR
help
Driver for the SIR IrDA port
-config LIRC_TTUSBIR
- tristate "Technotrend USB IR Receiver"
- depends on LIRC && USB
- help
- Driver for the Technotrend USB IR Receiver
-
config LIRC_ZILOG
tristate "Zilog/Hauppauge IR Transmitter"
depends on LIRC && I2C
diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile
index d76b0fa2af53..b90fcabddab6 100644
--- a/drivers/staging/media/lirc/Makefile
+++ b/drivers/staging/media/lirc/Makefile
@@ -10,5 +10,4 @@ obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
-obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
diff --git a/drivers/staging/media/lirc/lirc_ene0100.h b/drivers/staging/media/lirc/lirc_ene0100.h
deleted file mode 100644
index 06bebd6acc46..000000000000
--- a/drivers/staging/media/lirc/lirc_ene0100.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
- *
- * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
- */
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-/* hardware address */
-#define ENE_STATUS 0 /* hardware status - unused */
-#define ENE_ADDR_HI 1 /* hi byte of register address */
-#define ENE_ADDR_LO 2 /* low byte of register address */
-#define ENE_IO 3 /* read/write window */
-#define ENE_MAX_IO 4
-
-/* 8 bytes of samples, divided in 2 halfs*/
-#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
-#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */
-#define ENE_SAMPLE_VALUE_MASK 0x7F
-#define ENE_SAMPLE_OVERFLOW 0x7F
-#define ENE_SAMPLES_SIZE 4
-
-/* fan input sample buffer */
-#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
- /* each sample of normal buffer */
-
-#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
- /* if set, says that sample is pulse */
-#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
-
-/* first firmware register */
-#define ENE_FW1 0xF8F8
-#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */
-#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */
-#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */
-#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */
-
-/* second firmware register */
-#define ENE_FW2 0xF8F9
-#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */
-#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */
-#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */
- /* learning input */
-#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */
-#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */
-
-/* fan as input settings - only if learning capable */
-#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
-#define ENE_FAN_AS_IN1_EN 0xCD
-#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
-#define ENE_FAN_AS_IN2_EN 0x03
-#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
-
-/* IRQ registers block (for revision B) */
-#define ENEB_IRQ 0xFD09 /* IRQ number */
-#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
-#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
-#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */
-
-/* IRQ registers block (for revision C,D) */
-#define ENEC_IRQ 0xFE9B /* new irq settings register */
-#define ENEC_IRQ_MASK 0x0F /* irq number mask */
-#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */
-#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */
-
-/* CIR block settings */
-#define ENE_CIR_CONF1 0xFEC0
-#define ENE_CIR_CONF1_ADC_ON 0x7 /* receiver on gpio40 enabled */
-#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */
-#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
-#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */
-
-#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
-#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */
-#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */
-
-#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
-#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */
-
-
-/* transmitter - not implemented yet */
-/* KB3926C and higher */
-/* transmission is very similar to receiving, a byte is written to */
-/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
-/* sample period is fixed*/
-
-
-/* transmitter ports */
-#define ENE_TX_PORT1 0xFC01 /* this enables one or both */
-#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */
-#define ENE_TX_PORT2 0xFC08
-#define ENE_TX_PORT2_EN (1 << 1)
-
-#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */
-#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */
-#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
-#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */
-
-
-#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */
-#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */
-#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */
-
-/* Hardware versions */
-#define ENE_HW_VERSION 0xFF00 /* hardware revision */
-#define ENE_HW_UNK 0xFF1D
-#define ENE_HW_UNK_CLR (1 << 2)
-#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
-#define ENE_HW_VER_MINOR 0xFF1F
-#define ENE_HW_VER_OLD 0xFD00
-
-#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
-
-#define ENE_DRIVER_NAME "enecir"
-#define ENE_MAXGAP 250000 /* this is amount of time we wait
- before turning the sampler, chosen
- arbitry */
-
-#define space(len) (-(len)) /* add a space */
-
-/* software defines */
-#define ENE_IRQ_RX 1
-#define ENE_IRQ_TX 2
-
-#define ENE_HW_B 1 /* 3926B */
-#define ENE_HW_C 2 /* 3926C */
-#define ENE_HW_D 3 /* 3926D */
-
-#define ene_printk(level, text, ...) \
- printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
-
-struct ene_device {
- struct pnp_dev *pnp_dev;
- struct lirc_driver *lirc_driver;
-
- /* hw settings */
- unsigned long hw_io;
- int irq;
-
- int hw_revision; /* hardware revision */
- int hw_learning_and_tx_capable; /* learning capable */
- int hw_gpio40_learning; /* gpio40 is learning */
- int hw_fan_as_normal_input; /* fan input is used as regular input */
-
- /* device data */
- int idle;
- int fan_input_inuse;
-
- int sample;
- int in_use;
-
- struct timeval gap_start;
-};
diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c
index 7a2501776679..939a801c23e4 100644
--- a/drivers/staging/media/lirc/lirc_igorplugusb.c
+++ b/drivers/staging/media/lirc/lirc_igorplugusb.c
@@ -325,8 +325,8 @@ static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf)
if (ret < DEVICE_HEADERLEN)
return -ENODATA;
- dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
- ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
+ dprintk(DRIVER_NAME ": Got %d bytes. Header: %*ph\n",
+ ret, 3, ir->buf_in);
do_gettimeofday(&now);
timediff = now.tv_sec - ir->last_time.tv_sec;
diff --git a/drivers/staging/media/lirc/lirc_ttusbir.c b/drivers/staging/media/lirc/lirc_ttusbir.c
deleted file mode 100644
index 3bb865c02173..000000000000
--- a/drivers/staging/media/lirc/lirc_ttusbir.c
+++ /dev/null
@@ -1,376 +0,0 @@
-/*
- * lirc_ttusbir.c
- *
- * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
- *
- * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
- *
- * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
- * The receiver delivers the IR signal as raw sampled true/false data in
- * isochronous USB packets each of size 128 byte.
- * Currently the driver reduces the sampling rate by factor of 8 as this
- * is still more than enough to decode RC-5 - others should be analyzed.
- * But the driver does not rely on RC-5 it should be able to decode every
- * IR signal that is not too fast.
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/usb.h>
-
-#include <media/lirc.h>
-#include <media/lirc_dev.h>
-
-MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
-MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
-MODULE_LICENSE("GPL");
-
-/* #define DEBUG */
-#ifdef DEBUG
-#define DPRINTK printk
-#else
-#define DPRINTK(_x_, a...)
-#endif
-
-/* function declarations */
-static int probe(struct usb_interface *intf, const struct usb_device_id *id);
-static void disconnect(struct usb_interface *intf);
-static void urb_complete(struct urb *urb);
-static int set_use_inc(void *data);
-static void set_use_dec(void *data);
-
-static int num_urbs = 2;
-module_param(num_urbs, int, S_IRUGO);
-MODULE_PARM_DESC(num_urbs,
- "Number of URBs in queue. Try to increase to 4 in case "
- "of problems (default: 2; minimum: 2)");
-
-/* table of devices that work with this driver */
-static struct usb_device_id device_id_table[] = {
- /* TechnoTrend USB IR Receiver */
- { USB_DEVICE(0x0B48, 0x2003) },
- /* Terminating entry */
- { }
-};
-MODULE_DEVICE_TABLE(usb, device_id_table);
-
-/* USB driver definition */
-static struct usb_driver usb_driver = {
- .name = "TTUSBIR",
- .id_table = &(device_id_table[0]),
- .probe = probe,
- .disconnect = disconnect,
-};
-
-/* USB device definition */
-struct ttusbir_device {
- struct usb_driver *usb_driver;
- struct usb_device *udev;
- struct usb_interface *interf;
- struct usb_class_driver class_driver;
- unsigned int ifnum; /* Interface number to use */
- unsigned int alt_setting; /* alternate setting to use */
- unsigned int endpoint; /* Endpoint to use */
- struct urb **urb; /* num_urb URB pointers*/
- char **buffer; /* 128 byte buffer for each URB */
- struct lirc_buffer rbuf; /* Buffer towards LIRC */
- struct lirc_driver driver;
- int minor;
- int last_pulse; /* remembers if last received byte was pulse or space */
- int last_num; /* remembers how many last bytes appeared */
- int opened;
-};
-
-/*** LIRC specific functions ***/
-static int set_use_inc(void *data)
-{
- int i, retval;
- struct ttusbir_device *ttusbir = data;
-
- DPRINTK("Sending first URBs\n");
- /* @TODO Do I need to check if I am already opened */
- ttusbir->opened = 1;
-
- for (i = 0; i < num_urbs; i++) {
- retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
- if (retval) {
- dev_err(&ttusbir->interf->dev,
- "%s: usb_submit_urb failed on urb %d\n",
- __func__, i);
- return retval;
- }
- }
- return 0;
-}
-
-static void set_use_dec(void *data)
-{
- struct ttusbir_device *ttusbir = data;
-
- DPRINTK("Device closed\n");
-
- ttusbir->opened = 0;
-}
-
-/*** USB specific functions ***/
-
-/*
- * This mapping table is used to do a very simple filtering of the
- * input signal.
- * For a value with at least 4 bits set it returns 0xFF otherwise
- * 0x00. For faster IR signals this can not be used. But for RC-5 we
- * still have about 14 samples per pulse/space, i.e. we sample with 14
- * times higher frequency than the signal frequency
- */
-const unsigned char map_table[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
-
-static void urb_complete(struct urb *urb)
-{
- struct ttusbir_device *ttusbir;
- unsigned char *buf;
- int i;
- int l;
-
- ttusbir = urb->context;
-
- if (!ttusbir->opened)
- return;
-
- buf = (unsigned char *)urb->transfer_buffer;
-
- for (i = 0; i < 128; i++) {
- /* Here we do the filtering and some kind of down sampling */
- buf[i] = ~map_table[buf[i]];
- if (ttusbir->last_pulse == buf[i]) {
- if (ttusbir->last_num < PULSE_MASK/63)
- ttusbir->last_num++;
- /*
- * else we are in a idle period and do not need to
- * increment any longer
- */
- } else {
- l = ttusbir->last_num * 62; /* about 62 = us/byte */
- if (ttusbir->last_pulse) /* pulse or space? */
- l |= PULSE_BIT;
- if (!lirc_buffer_full(&ttusbir->rbuf)) {
- lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
- wake_up_interruptible(&ttusbir->rbuf.wait_poll);
- }
- ttusbir->last_num = 0;
- ttusbir->last_pulse = buf[i];
- }
- }
- usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
-}
-
-/*
- * Called whenever the USB subsystem thinks we could be the right driver
- * to handle this device
- */
-static int probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- int alt_set, endp;
- int found = 0;
- int i, j;
- int struct_size;
- struct usb_host_interface *host_interf;
- struct usb_interface_descriptor *interf_desc;
- struct usb_host_endpoint *host_endpoint;
- struct ttusbir_device *ttusbir;
-
- DPRINTK("Module ttusbir probe\n");
-
- /* To reduce memory fragmentation we use only one allocation */
- struct_size = sizeof(struct ttusbir_device) +
- (sizeof(struct urb *) * num_urbs) +
- (sizeof(char *) * num_urbs) +
- (num_urbs * 128);
- ttusbir = kzalloc(struct_size, GFP_KERNEL);
- if (!ttusbir)
- return -ENOMEM;
-
- ttusbir->urb = (struct urb **)((char *)ttusbir +
- sizeof(struct ttusbir_device));
- ttusbir->buffer = (char **)((char *)ttusbir->urb +
- (sizeof(struct urb *) * num_urbs));
- for (i = 0; i < num_urbs; i++)
- ttusbir->buffer[i] = (char *)ttusbir->buffer +
- (sizeof(char *)*num_urbs) + (i * 128);
-
- ttusbir->usb_driver = &usb_driver;
- ttusbir->alt_setting = -1;
- /* @TODO check if error can be returned */
- ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
- ttusbir->interf = intf;
- ttusbir->last_pulse = 0x00;
- ttusbir->last_num = 0;
-
- /*
- * Now look for interface setting we can handle
- * We are searching for the alt setting where end point
- * 0x82 has max packet size 16
- */
- for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
- host_interf = &intf->altsetting[alt_set];
- interf_desc = &host_interf->desc;
- for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
- host_endpoint = &host_interf->endpoint[endp];
- if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
- (host_endpoint->desc.wMaxPacketSize == 0x10)) {
- ttusbir->alt_setting = alt_set;
- ttusbir->endpoint = endp;
- found = 1;
- break;
- }
- }
- }
- if (ttusbir->alt_setting != -1)
- DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
- else {
- dev_err(&intf->dev, "Could not find alternate setting\n");
- kfree(ttusbir);
- return -EINVAL;
- }
-
- /* OK lets setup this interface setting */
- usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
-
- /* Store device info in interface structure */
- usb_set_intfdata(intf, ttusbir);
-
- /* Register as a LIRC driver */
- if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
- dev_err(&intf->dev, "Could not get memory for LIRC data buffer\n");
- usb_set_intfdata(intf, NULL);
- kfree(ttusbir);
- return -ENOMEM;
- }
- strcpy(ttusbir->driver.name, "TTUSBIR");
- ttusbir->driver.minor = -1;
- ttusbir->driver.code_length = 1;
- ttusbir->driver.sample_rate = 0;
- ttusbir->driver.data = ttusbir;
- ttusbir->driver.add_to_buf = NULL;
- ttusbir->driver.rbuf = &ttusbir->rbuf;
- ttusbir->driver.set_use_inc = set_use_inc;
- ttusbir->driver.set_use_dec = set_use_dec;
- ttusbir->driver.dev = &intf->dev;
- ttusbir->driver.owner = THIS_MODULE;
- ttusbir->driver.features = LIRC_CAN_REC_MODE2;
- ttusbir->minor = lirc_register_driver(&ttusbir->driver);
- if (ttusbir->minor < 0) {
- dev_err(&intf->dev, "Error registering as LIRC driver\n");
- usb_set_intfdata(intf, NULL);
- lirc_buffer_free(&ttusbir->rbuf);
- kfree(ttusbir);
- return -EIO;
- }
-
- /* Allocate and setup the URB that we will use to talk to the device */
- for (i = 0; i < num_urbs; i++) {
- ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
- if (!ttusbir->urb[i]) {
- dev_err(&intf->dev, "Could not allocate memory for the URB\n");
- for (j = i - 1; j >= 0; j--)
- kfree(ttusbir->urb[j]);
- lirc_buffer_free(&ttusbir->rbuf);
- lirc_unregister_driver(ttusbir->minor);
- kfree(ttusbir);
- usb_set_intfdata(intf, NULL);
- return -ENOMEM;
- }
- ttusbir->urb[i]->dev = ttusbir->udev;
- ttusbir->urb[i]->context = ttusbir;
- ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
- ttusbir->endpoint);
- ttusbir->urb[i]->interval = 1;
- ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
- ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
- ttusbir->urb[i]->complete = urb_complete;
- ttusbir->urb[i]->number_of_packets = 8;
- ttusbir->urb[i]->transfer_buffer_length = 128;
- for (j = 0; j < 8; j++) {
- ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
- ttusbir->urb[i]->iso_frame_desc[j].length = 16;
- }
- }
- return 0;
-}
-
-/**
- * Called when the driver is unloaded or the device is unplugged
- */
-static void disconnect(struct usb_interface *intf)
-{
- int i;
- struct ttusbir_device *ttusbir;
-
- DPRINTK("Module ttusbir disconnect\n");
-
- ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
- lirc_unregister_driver(ttusbir->minor);
- DPRINTK("unregistered\n");
-
- for (i = 0; i < num_urbs; i++) {
- usb_kill_urb(ttusbir->urb[i]);
- usb_free_urb(ttusbir->urb[i]);
- }
- DPRINTK("URBs killed\n");
- lirc_buffer_free(&ttusbir->rbuf);
- kfree(ttusbir);
-}
-
-module_usb_driver(usb_driver);
diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c
index 76ea4a8f2c75..11d5338b4f2f 100644
--- a/drivers/staging/media/lirc/lirc_zilog.c
+++ b/drivers/staging/media/lirc/lirc_zilog.c
@@ -658,8 +658,7 @@ static int send_data_block(struct IR_tx *tx, unsigned char *data_block)
buf[0] = (unsigned char)(i + 1);
for (j = 0; j < tosend; ++j)
buf[1 + j] = data_block[i + j];
- dprintk("%02x %02x %02x %02x %02x",
- buf[0], buf[1], buf[2], buf[3], buf[4]);
+ dprintk("%*ph", 5, buf);
ret = i2c_master_send(tx->c, buf, tosend + 1);
if (ret != tosend + 1) {
zilog_error("i2c_master_send failed with %d\n", ret);
diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
index 695ea35f75b0..d0a7e408efe9 100644
--- a/drivers/staging/nvec/nvec.c
+++ b/drivers/staging/nvec/nvec.c
@@ -837,7 +837,7 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
}
ret = mfd_add_devices(nvec->dev, -1, nvec_devices,
- ARRAY_SIZE(nvec_devices), base, 0);
+ ARRAY_SIZE(nvec_devices), base, 0, NULL);
if (ret)
dev_err(nvec->dev, "error adding subdevices\n");
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c
index 992275c0d87c..2c4bd746715a 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon.c
@@ -27,6 +27,7 @@
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/reboot.h>
+#include <linux/olpc-ec.h>
#include <asm/tsc.h>
#include <asm/olpc.h>
diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c
index 5e2856c0e0bb..55e9c8655850 100644
--- a/drivers/staging/omapdrm/omap_connector.c
+++ b/drivers/staging/omapdrm/omap_connector.c
@@ -48,13 +48,20 @@ static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
mode->vsync_end = mode->vsync_start + timings->vsw;
mode->vtotal = mode->vsync_end + timings->vbp;
- /* note: whether or not it is interlaced, +/- h/vsync, etc,
- * which should be set in the mode flags, is not exposed in
- * the omap_video_timings struct.. but hdmi driver tracks
- * those separately so all we have to have to set the mode
- * is the way to recover these timings values, and the
- * omap_dss_driver would do the rest.
- */
+ mode->flags = 0;
+
+ if (timings->interlace)
+ mode->flags |= DRM_MODE_FLAG_INTERLACE;
+
+ if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+ if (timings->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH)
+ mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ mode->flags |= DRM_MODE_FLAG_NVSYNC;
}
static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
@@ -71,6 +78,22 @@ static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
timings->vfp = mode->vsync_start - mode->vdisplay;
timings->vsw = mode->vsync_end - mode->vsync_start;
timings->vbp = mode->vtotal - mode->vsync_end;
+
+ timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ else
+ timings->hsync_level = OMAPDSS_SIG_ACTIVE_LOW;
+
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ timings->vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ else
+ timings->vsync_level = OMAPDSS_SIG_ACTIVE_LOW;
+
+ timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
}
static void omap_connector_dpms(struct drm_connector *connector, int mode)
@@ -187,7 +210,7 @@ static int omap_connector_get_modes(struct drm_connector *connector)
}
} else {
struct drm_display_mode *mode = drm_mode_create(dev);
- struct omap_video_timings timings;
+ struct omap_video_timings timings = {0};
dssdrv->get_timings(dssdev, &timings);
@@ -291,7 +314,7 @@ void omap_connector_mode_set(struct drm_connector *connector,
struct omap_connector *omap_connector = to_omap_connector(connector);
struct omap_dss_device *dssdev = omap_connector->dssdev;
struct omap_dss_driver *dssdrv = dssdev->driver;
- struct omap_video_timings timings;
+ struct omap_video_timings timings = {0};
copy_timings_drm_to_omap(&timings, mode);
diff --git a/drivers/staging/ozwpan/ozcdev.c b/drivers/staging/ozwpan/ozcdev.c
index d98321945802..758ce0a8d82e 100644
--- a/drivers/staging/ozwpan/ozcdev.c
+++ b/drivers/staging/ozwpan/ozcdev.c
@@ -8,6 +8,7 @@
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include "ozconfig.h"
@@ -213,7 +214,7 @@ static int oz_set_active_pd(u8 *addr)
if (old_pd)
oz_pd_put(old_pd);
} else {
- if (!memcmp(addr, "\0\0\0\0\0\0", sizeof(addr))) {
+ if (is_zero_ether_addr(addr)) {
spin_lock_bh(&g_cdev.lock);
pd = g_cdev.active_pd;
g_cdev.active_pd = 0;
diff --git a/drivers/staging/rtl8712/recv_linux.c b/drivers/staging/rtl8712/recv_linux.c
index 0e26d5f6cf2d..495ee1205e02 100644
--- a/drivers/staging/rtl8712/recv_linux.c
+++ b/drivers/staging/rtl8712/recv_linux.c
@@ -117,13 +117,8 @@ void r8712_recv_indicatepkt(struct _adapter *padapter,
if (skb == NULL)
goto _recv_indicatepkt_drop;
skb->data = precv_frame->u.hdr.rx_data;
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
- skb->tail = (sk_buff_data_t)(precv_frame->u.hdr.rx_tail -
- precv_frame->u.hdr.rx_head);
-#else
- skb->tail = (sk_buff_data_t)precv_frame->u.hdr.rx_tail;
-#endif
skb->len = precv_frame->u.hdr.len;
+ skb_set_tail_pointer(skb, skb->len);
if ((pattrib->tcpchk_valid == 1) && (pattrib->tcp_chkrpt == 1))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
diff --git a/drivers/staging/vt6656/dpc.c b/drivers/staging/vt6656/dpc.c
index e4bdf2a2b582..3aa895ec6507 100644
--- a/drivers/staging/vt6656/dpc.c
+++ b/drivers/staging/vt6656/dpc.c
@@ -200,7 +200,7 @@ s_vProcessRxMACHeader (
} else if (!compare_ether_addr(pbyRxBuffer, &pDevice->abySNAP_RFC1042[0])) {
cbHeaderSize += 6;
pwType = (PWORD) (pbyRxBufferAddr + cbHeaderSize);
- if ((*pwType == cpu_to_le16(ETH_P_IPX)) ||
+ if ((*pwType == cpu_to_be16(ETH_P_IPX)) ||
(*pwType == cpu_to_le16(0xF380))) {
cbHeaderSize -= 8;
pwType = (PWORD) (pbyRxBufferAddr + cbHeaderSize);
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index b06fd5b723fa..d536756549e6 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -189,7 +189,7 @@ DEVICE_PARAM(b80211hEnable, "802.11h mode");
// Static vars definitions
//
-static struct usb_device_id vt6656_table[] __devinitdata = {
+static struct usb_device_id vt6656_table[] = {
{USB_DEVICE(VNT_USB_VENDOR_ID, VNT_USB_PRODUCT_ID)},
{}
};
diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
index bb464527fc1b..b6e04e7b629b 100644
--- a/drivers/staging/vt6656/rxtx.c
+++ b/drivers/staging/vt6656/rxtx.c
@@ -1699,7 +1699,7 @@ s_bPacketToWirelessUsb(
// 802.1H
if (ntohs(psEthHeader->wType) > ETH_DATA_LEN) {
if (pDevice->dwDiagRefCount == 0) {
- if ((psEthHeader->wType == cpu_to_le16(ETH_P_IPX)) ||
+ if ((psEthHeader->wType == cpu_to_be16(ETH_P_IPX)) ||
(psEthHeader->wType == cpu_to_le16(0xF380))) {
memcpy((PBYTE) (pbyPayloadHead),
abySNAP_Bridgetunnel, 6);
@@ -2838,10 +2838,10 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
Packet_Type = skb->data[ETH_HLEN+1];
Descriptor_type = skb->data[ETH_HLEN+1+1+2];
Key_info = (skb->data[ETH_HLEN+1+1+2+1] << 8)|(skb->data[ETH_HLEN+1+1+2+2]);
- if (pDevice->sTxEthHeader.wType == cpu_to_le16(ETH_P_PAE)) {
- /* 802.1x OR eapol-key challenge frame transfer */
- if (((Protocol_Version == 1) || (Protocol_Version == 2)) &&
- (Packet_Type == 3)) {
+ if (pDevice->sTxEthHeader.wType == cpu_to_be16(ETH_P_PAE)) {
+ /* 802.1x OR eapol-key challenge frame transfer */
+ if (((Protocol_Version == 1) || (Protocol_Version == 2)) &&
+ (Packet_Type == 3)) {
bTxeapol_key = TRUE;
if(!(Key_info & BIT3) && //WPA or RSN group-key challenge
(Key_info & BIT8) && (Key_info & BIT9)) { //send 2/2 key
@@ -2987,19 +2987,19 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
}
}
- if (pDevice->sTxEthHeader.wType == cpu_to_le16(ETH_P_PAE)) {
- if (pDevice->byBBType != BB_TYPE_11A) {
- pDevice->wCurrentRate = RATE_1M;
- pDevice->byACKRate = RATE_1M;
- pDevice->byTopCCKBasicRate = RATE_1M;
- pDevice->byTopOFDMBasicRate = RATE_6M;
- } else {
- pDevice->wCurrentRate = RATE_6M;
- pDevice->byACKRate = RATE_6M;
- pDevice->byTopCCKBasicRate = RATE_1M;
- pDevice->byTopOFDMBasicRate = RATE_6M;
- }
- }
+ if (pDevice->sTxEthHeader.wType == cpu_to_be16(ETH_P_PAE)) {
+ if (pDevice->byBBType != BB_TYPE_11A) {
+ pDevice->wCurrentRate = RATE_1M;
+ pDevice->byACKRate = RATE_1M;
+ pDevice->byTopCCKBasicRate = RATE_1M;
+ pDevice->byTopOFDMBasicRate = RATE_6M;
+ } else {
+ pDevice->wCurrentRate = RATE_6M;
+ pDevice->byACKRate = RATE_6M;
+ pDevice->byTopCCKBasicRate = RATE_1M;
+ pDevice->byTopOFDMBasicRate = RATE_6M;
+ }
+ }
DBG_PRT(MSG_LEVEL_DEBUG,
KERN_INFO "dma_tx: pDevice->wCurrentRate = %d\n",
@@ -3015,7 +3015,7 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
if (bNeedEncryption == TRUE) {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"ntohs Pkt Type=%04x\n", ntohs(pDevice->sTxEthHeader.wType));
- if ((pDevice->sTxEthHeader.wType) == cpu_to_le16(ETH_P_PAE)) {
+ if ((pDevice->sTxEthHeader.wType) == cpu_to_be16(ETH_P_PAE)) {
bNeedEncryption = FALSE;
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Pkt Type=%04x\n", (pDevice->sTxEthHeader.wType));
if ((pMgmt->eCurrMode == WMAC_MODE_ESS_STA) && (pMgmt->eCurrState == WMAC_STATE_ASSOC)) {
diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c
index ef360547ecec..0ca857ac473e 100644
--- a/drivers/staging/winbond/wbusb.c
+++ b/drivers/staging/winbond/wbusb.c
@@ -25,7 +25,7 @@ MODULE_DESCRIPTION("IS89C35 802.11bg WLAN USB Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
-static const struct usb_device_id wb35_table[] __devinitconst = {
+static const struct usb_device_id wb35_table[] = {
{ USB_DEVICE(0x0416, 0x0035) },
{ USB_DEVICE(0x18E8, 0x6201) },
{ USB_DEVICE(0x18E8, 0x6206) },
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
index fabff4d650ef..0970127344e6 100644
--- a/drivers/staging/wlan-ng/cfg80211.c
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -327,9 +327,9 @@ int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
return result;
}
-int prism2_scan(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_scan_request *request)
+int prism2_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
{
+ struct net_device *dev = request->wdev->netdev;
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
wlandevice_t *wlandev = dev->ml_priv;
struct p80211msg_dot11req_scan msg1;
diff --git a/drivers/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c
index c214977b4ab4..52b43b7b83d7 100644
--- a/drivers/staging/zcache/zcache-main.c
+++ b/drivers/staging/zcache/zcache-main.c
@@ -1251,13 +1251,12 @@ static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
void *pampd, struct tmem_pool *pool,
struct tmem_oid *oid, uint32_t index)
{
- int ret = 0;
-
BUG_ON(!is_ephemeral(pool));
- zbud_decompress((struct page *)(data), pampd);
+ if (zbud_decompress((struct page *)(data), pampd) < 0)
+ return -EINVAL;
zbud_free_and_delist((struct zbud_hdr *)pampd);
atomic_dec(&zcache_curr_eph_pampd_count);
- return ret;
+ return 0;
}
/*
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 0694d9b1bce6..6aba4395e8d8 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -221,6 +221,7 @@ static int iscsi_login_zero_tsih_s1(
{
struct iscsi_session *sess = NULL;
struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
+ int ret;
sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);
if (!sess) {
@@ -257,9 +258,17 @@ static int iscsi_login_zero_tsih_s1(
return -ENOMEM;
}
spin_lock(&sess_idr_lock);
- idr_get_new(&sess_idr, NULL, &sess->session_index);
+ ret = idr_get_new(&sess_idr, NULL, &sess->session_index);
spin_unlock(&sess_idr_lock);
+ if (ret < 0) {
+ pr_err("idr_get_new() for sess_idr failed\n");
+ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
+ ISCSI_LOGIN_STATUS_NO_RESOURCES);
+ kfree(sess);
+ return -ENOMEM;
+ }
+
sess->creation_time = get_jiffies_64();
spin_lock_init(&sess->session_stats_lock);
/*
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 91799973081a..41641ba54828 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -218,6 +218,13 @@ int target_emulate_set_target_port_groups(struct se_cmd *cmd)
cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
return -EINVAL;
}
+ if (cmd->data_length < 4) {
+ pr_warn("SET TARGET PORT GROUPS parameter list length %u too"
+ " small\n", cmd->data_length);
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+ return -EINVAL;
+ }
+
buf = transport_kmap_data_sg(cmd);
/*
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index cf2c66f3c116..9fc9a6006ca0 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -669,6 +669,13 @@ int target_report_luns(struct se_cmd *se_cmd)
unsigned char *buf;
u32 lun_count = 0, offset = 8, i;
+ if (se_cmd->data_length < 16) {
+ pr_warn("REPORT LUNS allocation length %u too small\n",
+ se_cmd->data_length);
+ se_cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+ return -EINVAL;
+ }
+
buf = transport_kmap_data_sg(se_cmd);
if (!buf)
return -ENOMEM;
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
index 9e2100551c78..cbb5aaf3e567 100644
--- a/drivers/target/target_core_file.c
+++ b/drivers/target/target_core_file.c
@@ -109,46 +109,29 @@ static struct se_device *fd_create_virtdevice(
struct se_subsystem_dev *se_dev,
void *p)
{
- char *dev_p = NULL;
struct se_device *dev;
struct se_dev_limits dev_limits;
struct queue_limits *limits;
struct fd_dev *fd_dev = p;
struct fd_host *fd_host = hba->hba_ptr;
- mm_segment_t old_fs;
struct file *file;
struct inode *inode = NULL;
int dev_flags = 0, flags, ret = -EINVAL;
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
- old_fs = get_fs();
- set_fs(get_ds());
- dev_p = getname(fd_dev->fd_dev_name);
- set_fs(old_fs);
-
- if (IS_ERR(dev_p)) {
- pr_err("getname(%s) failed: %lu\n",
- fd_dev->fd_dev_name, IS_ERR(dev_p));
- ret = PTR_ERR(dev_p);
- goto fail;
- }
/*
* Use O_DSYNC by default instead of O_SYNC to forgo syncing
* of pure timestamp updates.
*/
flags = O_RDWR | O_CREAT | O_LARGEFILE | O_DSYNC;
- file = filp_open(dev_p, flags, 0600);
+ file = filp_open(fd_dev->fd_dev_name, flags, 0600);
if (IS_ERR(file)) {
- pr_err("filp_open(%s) failed\n", dev_p);
+ pr_err("filp_open(%s) failed\n", fd_dev->fd_dev_name);
ret = PTR_ERR(file);
goto fail;
}
- if (!file || !file->f_dentry) {
- pr_err("filp_open(%s) failed\n", dev_p);
- goto fail;
- }
fd_dev->fd_file = file;
/*
* If using a block backend with this struct file, we extract
@@ -212,14 +195,12 @@ static struct se_device *fd_create_virtdevice(
" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
fd_dev->fd_dev_name, fd_dev->fd_dev_size);
- putname(dev_p);
return dev;
fail:
if (fd_dev->fd_file) {
filp_close(fd_dev->fd_file, NULL);
fd_dev->fd_file = NULL;
}
- putname(dev_p);
return ERR_PTR(ret);
}
@@ -452,14 +433,11 @@ static ssize_t fd_set_configfs_dev_params(
token = match_token(ptr, tokens, args);
switch (token) {
case Opt_fd_dev_name:
- arg_p = match_strdup(&args[0]);
- if (!arg_p) {
- ret = -ENOMEM;
+ if (match_strlcpy(fd_dev->fd_dev_name, &args[0],
+ FD_MAX_DEV_NAME) == 0) {
+ ret = -EINVAL;
break;
}
- snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
- "%s", arg_p);
- kfree(arg_p);
pr_debug("FILEIO: Referencing Path: %s\n",
fd_dev->fd_dev_name);
fd_dev->fbd_flags |= FBDF_HAS_PATH;
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 76db75e836ed..9ba495477fd2 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -325,17 +325,30 @@ static int iblock_execute_unmap(struct se_cmd *cmd)
struct iblock_dev *ibd = dev->dev_ptr;
unsigned char *buf, *ptr = NULL;
sector_t lba;
- int size = cmd->data_length;
+ int size;
u32 range;
int ret = 0;
int dl, bd_dl;
+ if (cmd->data_length < 8) {
+ pr_warn("UNMAP parameter list length %u too small\n",
+ cmd->data_length);
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+ return -EINVAL;
+ }
+
buf = transport_kmap_data_sg(cmd);
dl = get_unaligned_be16(&buf[0]);
bd_dl = get_unaligned_be16(&buf[2]);
- size = min(size - 8, bd_dl);
+ size = cmd->data_length - 8;
+ if (bd_dl > size)
+ pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
+ cmd->data_length, bd_dl);
+ else
+ size = bd_dl;
+
if (size / 16 > dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count) {
cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
ret = -EINVAL;
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 1e946502c378..956c84c6b666 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -1540,6 +1540,14 @@ static int core_scsi3_decode_spec_i_port(
tidh_new->dest_local_nexus = 1;
list_add_tail(&tidh_new->dest_list, &tid_dest_list);
+ if (cmd->data_length < 28) {
+ pr_warn("SPC-PR: Received PR OUT parameter list"
+ " length too small: %u\n", cmd->data_length);
+ cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+ ret = -EINVAL;
+ goto out;
+ }
+
buf = transport_kmap_data_sg(cmd);
/*
* For a PERSISTENT RESERVE OUT specify initiator ports payload,
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 6e32ff6f2fa0..9d7ce3daa262 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -667,24 +667,32 @@ static void pscsi_free_device(void *p)
kfree(pdv);
}
-static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
+static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg,
+ unsigned char *sense_buffer)
{
struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr;
struct scsi_device *sd = pdv->pdv_sd;
int result;
struct pscsi_plugin_task *pt = cmd->priv;
- unsigned char *cdb = &pt->pscsi_cdb[0];
+ unsigned char *cdb;
+ /*
+ * Special case for REPORT_LUNs handling where pscsi_plugin_task has
+ * not been allocated because TCM is handling the emulation directly.
+ */
+ if (!pt)
+ return;
+ cdb = &pt->pscsi_cdb[0];
result = pt->pscsi_result;
/*
* Hack to make sure that Write-Protect modepage is set if R/O mode is
* forced.
*/
+ if (!cmd->se_deve || !cmd->data_length)
+ goto after_mode_sense;
+
if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&
(status_byte(result) << 1) == SAM_STAT_GOOD) {
- if (!cmd->se_deve)
- goto after_mode_sense;
-
if (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) {
unsigned char *buf = transport_kmap_data_sg(cmd);
@@ -701,7 +709,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)
}
after_mode_sense:
- if (sd->type != TYPE_TAPE)
+ if (sd->type != TYPE_TAPE || !cmd->data_length)
goto after_mode_select;
/*
@@ -743,10 +751,10 @@ after_mode_sense:
}
after_mode_select:
- if (status_byte(result) & CHECK_CONDITION)
- return 1;
-
- return 0;
+ if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) {
+ memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER);
+ cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
+ }
}
enum {
@@ -1177,13 +1185,6 @@ fail:
return -ENOMEM;
}
-static unsigned char *pscsi_get_sense_buffer(struct se_cmd *cmd)
-{
- struct pscsi_plugin_task *pt = cmd->priv;
-
- return pt->pscsi_sense;
-}
-
/* pscsi_get_device_rev():
*
*
@@ -1266,7 +1267,6 @@ static struct se_subsystem_api pscsi_template = {
.check_configfs_dev_params = pscsi_check_configfs_dev_params,
.set_configfs_dev_params = pscsi_set_configfs_dev_params,
.show_configfs_dev_params = pscsi_show_configfs_dev_params,
- .get_sense_buffer = pscsi_get_sense_buffer,
.get_device_rev = pscsi_get_device_rev,
.get_device_type = pscsi_get_device_type,
.get_blocks = pscsi_get_blocks,
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index 4c861de538c9..388a922c8f6d 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -877,9 +877,11 @@ static int spc_emulate_modesense(struct se_cmd *cmd)
static int spc_emulate_request_sense(struct se_cmd *cmd)
{
unsigned char *cdb = cmd->t_task_cdb;
- unsigned char *buf;
+ unsigned char *rbuf;
u8 ua_asc = 0, ua_ascq = 0;
- int err = 0;
+ unsigned char buf[SE_SENSE_BUF];
+
+ memset(buf, 0, SE_SENSE_BUF);
if (cdb[1] & 0x01) {
pr_err("REQUEST_SENSE description emulation not"
@@ -888,20 +890,21 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
return -ENOSYS;
}
- buf = transport_kmap_data_sg(cmd);
-
- if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
+ rbuf = transport_kmap_data_sg(cmd);
+ if (cmd->scsi_sense_reason != 0) {
+ /*
+ * Out of memory. We will fail with CHECK CONDITION, so
+ * we must not clear the unit attention condition.
+ */
+ target_complete_cmd(cmd, CHECK_CONDITION);
+ return 0;
+ } else if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
/*
* CURRENT ERROR, UNIT ATTENTION
*/
buf[0] = 0x70;
buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
- if (cmd->data_length < 18) {
- buf[7] = 0x00;
- err = -EINVAL;
- goto end;
- }
/*
* The Additional Sense Code (ASC) from the UNIT ATTENTION
*/
@@ -915,11 +918,6 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
buf[0] = 0x70;
buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE;
- if (cmd->data_length < 18) {
- buf[7] = 0x00;
- err = -EINVAL;
- goto end;
- }
/*
* NO ADDITIONAL SENSE INFORMATION
*/
@@ -927,8 +925,11 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)
buf[7] = 0x0A;
}
-end:
- transport_kunmap_data_sg(cmd);
+ if (rbuf) {
+ memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
+ transport_kunmap_data_sg(cmd);
+ }
+
target_complete_cmd(cmd, GOOD);
return 0;
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 0eaae23d12b5..269f54488397 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -567,6 +567,34 @@ static void target_complete_failure_work(struct work_struct *work)
transport_generic_request_failure(cmd);
}
+/*
+ * Used when asking transport to copy Sense Data from the underlying
+ * Linux/SCSI struct scsi_cmnd
+ */
+static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd)
+{
+ unsigned char *buffer = cmd->sense_buffer;
+ struct se_device *dev = cmd->se_dev;
+ u32 offset = 0;
+
+ WARN_ON(!cmd->se_lun);
+
+ if (!dev)
+ return NULL;
+
+ if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION)
+ return NULL;
+
+ offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER);
+
+ /* Automatically padded */
+ cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset;
+
+ pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n",
+ dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status);
+ return &buffer[offset];
+}
+
void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
{
struct se_device *dev = cmd->se_dev;
@@ -580,11 +608,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
cmd->transport_state &= ~CMD_T_BUSY;
if (dev && dev->transport->transport_complete) {
- if (dev->transport->transport_complete(cmd,
- cmd->t_data_sg) != 0) {
- cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE;
+ dev->transport->transport_complete(cmd,
+ cmd->t_data_sg,
+ transport_get_sense_buffer(cmd));
+ if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)
success = 1;
- }
}
/*
@@ -1165,8 +1193,6 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
" 0x%02x\n", cmd->se_tfo->get_fabric_name(),
cmd->data_length, size, cmd->t_task_cdb[0]);
- cmd->cmd_spdtl = size;
-
if (cmd->data_direction == DMA_TO_DEVICE) {
pr_err("Rejecting underflow/overflow"
" WRITE data\n");
@@ -1183,15 +1209,20 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
/* Returns CHECK_CONDITION + INVALID_CDB_FIELD */
goto out_invalid_cdb_field;
}
-
+ /*
+ * For the overflow case keep the existing fabric provided
+ * ->data_length. Otherwise for the underflow case, reset
+ * ->data_length to the smaller SCSI expected data transfer
+ * length.
+ */
if (size > cmd->data_length) {
cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;
cmd->residual_count = (size - cmd->data_length);
} else {
cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
cmd->residual_count = (cmd->data_length - size);
+ cmd->data_length = size;
}
- cmd->data_length = size;
}
return 0;
@@ -1818,61 +1849,6 @@ execute:
EXPORT_SYMBOL(target_execute_cmd);
/*
- * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd
- */
-static int transport_get_sense_data(struct se_cmd *cmd)
-{
- unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL;
- struct se_device *dev = cmd->se_dev;
- unsigned long flags;
- u32 offset = 0;
-
- WARN_ON(!cmd->se_lun);
-
- if (!dev)
- return 0;
-
- spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- return 0;
- }
-
- if (!(cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
- goto out;
-
- if (!dev->transport->get_sense_buffer) {
- pr_err("dev->transport->get_sense_buffer is NULL\n");
- goto out;
- }
-
- sense_buffer = dev->transport->get_sense_buffer(cmd);
- if (!sense_buffer) {
- pr_err("ITT 0x%08x cmd %p: Unable to locate"
- " sense buffer for task with sense\n",
- cmd->se_tfo->get_task_tag(cmd), cmd);
- goto out;
- }
-
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
- offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER);
-
- memcpy(&buffer[offset], sense_buffer, TRANSPORT_SENSE_BUFFER);
-
- /* Automatically padded */
- cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset;
-
- pr_debug("HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x and sense\n",
- dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status);
- return 0;
-
-out:
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- return -1;
-}
-
-/*
* Process all commands up to the last received ORDERED task attribute which
* requires another blocking boundary
*/
@@ -1987,7 +1963,7 @@ static void transport_handle_queue_full(
static void target_complete_ok_work(struct work_struct *work)
{
struct se_cmd *cmd = container_of(work, struct se_cmd, work);
- int reason = 0, ret;
+ int ret;
/*
* Check if we need to move delayed/dormant tasks from cmds on the
@@ -2004,23 +1980,19 @@ static void target_complete_ok_work(struct work_struct *work)
schedule_work(&cmd->se_dev->qf_work_queue);
/*
- * Check if we need to retrieve a sense buffer from
+ * Check if we need to send a sense buffer from
* the struct se_cmd in question.
*/
if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) {
- if (transport_get_sense_data(cmd) < 0)
- reason = TCM_NON_EXISTENT_LUN;
-
- if (cmd->scsi_status) {
- ret = transport_send_check_condition_and_sense(
- cmd, reason, 1);
- if (ret == -EAGAIN || ret == -ENOMEM)
- goto queue_full;
+ WARN_ON(!cmd->scsi_status);
+ ret = transport_send_check_condition_and_sense(
+ cmd, 0, 1);
+ if (ret == -EAGAIN || ret == -ENOMEM)
+ goto queue_full;
- transport_lun_remove_cmd(cmd);
- transport_cmd_check_stop_to_fabric(cmd);
- return;
- }
+ transport_lun_remove_cmd(cmd);
+ transport_cmd_check_stop_to_fabric(cmd);
+ return;
}
/*
* Check for a callback, used by amongst other things
@@ -2218,7 +2190,6 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
struct page **pages;
int i;
- BUG_ON(!sg);
/*
* We need to take into account a possible offset here for fabrics like
* tcm_loop who may be using a contig buffer from the SCSI midlayer for
@@ -2226,13 +2197,17 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
*/
if (!cmd->t_data_nents)
return NULL;
- else if (cmd->t_data_nents == 1)
+
+ BUG_ON(!sg);
+ if (cmd->t_data_nents == 1)
return kmap(sg_page(sg)) + sg->offset;
/* >1 page. use vmap */
pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL);
- if (!pages)
+ if (!pages) {
+ cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
return NULL;
+ }
/* convert sg[] to pages[] */
for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) {
@@ -2241,8 +2216,10 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)
cmd->t_data_vmap = vmap(pages, cmd->t_data_nents, VM_MAP, PAGE_KERNEL);
kfree(pages);
- if (!cmd->t_data_vmap)
+ if (!cmd->t_data_vmap) {
+ cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
return NULL;
+ }
return cmd->t_data_vmap + cmd->t_data_sg[0].offset;
}
@@ -2294,9 +2271,9 @@ transport_generic_get_mem(struct se_cmd *cmd)
return 0;
out:
- while (i >= 0) {
- __free_page(sg_page(&cmd->t_data_sg[i]));
+ while (i > 0) {
i--;
+ __free_page(sg_page(&cmd->t_data_sg[i]));
}
kfree(cmd->t_data_sg);
cmd->t_data_sg = NULL;
@@ -2323,21 +2300,19 @@ int transport_generic_new_cmd(struct se_cmd *cmd)
if (ret < 0)
goto out_fail;
}
-
- /* Workaround for handling zero-length control CDBs */
- if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->data_length) {
+ /*
+ * If this command doesn't have any payload and we don't have to call
+ * into the fabric for data transfers, go ahead and complete it right
+ * away.
+ */
+ if (!cmd->data_length &&
+ cmd->t_task_cdb[0] != REQUEST_SENSE &&
+ cmd->se_dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {
spin_lock_irq(&cmd->t_state_lock);
cmd->t_state = TRANSPORT_COMPLETE;
cmd->transport_state |= CMD_T_ACTIVE;
spin_unlock_irq(&cmd->t_state_lock);
- if (cmd->t_task_cdb[0] == REQUEST_SENSE) {
- u8 ua_asc = 0, ua_ascq = 0;
-
- core_scsi3_ua_clear_for_request_sense(cmd,
- &ua_asc, &ua_ascq);
- }
-
INIT_WORK(&cmd->work, target_complete_ok_work);
queue_work(target_completion_wq, &cmd->work);
return 0;
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index c5eb3c33c3db..eea69358ced3 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -131,6 +131,7 @@ extern struct list_head ft_lport_list;
extern struct mutex ft_lport_lock;
extern struct fc4_prov ft_prov;
extern struct target_fabric_configfs *ft_configfs;
+extern unsigned int ft_debug_logging;
/*
* Fabric methods.
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index b9cb5006177e..823e6922249d 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -48,7 +48,7 @@
/*
* Dump cmd state for debugging.
*/
-void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+static void _ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
{
struct fc_exch *ep;
struct fc_seq *sp;
@@ -80,6 +80,12 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
}
}
+void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+{
+ if (unlikely(ft_debug_logging))
+ _ft_dump_cmd(cmd, caller);
+}
+
static void ft_free_cmd(struct ft_cmd *cmd)
{
struct fc_frame *fp;
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index 87901fa74dd7..3c9e5b57caab 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -456,7 +456,9 @@ static void ft_prlo(struct fc_rport_priv *rdata)
struct ft_tport *tport;
mutex_lock(&ft_lport_lock);
- tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
+ tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
+ lockdep_is_held(&ft_lport_lock));
+
if (!tport) {
mutex_unlock(&ft_lport_lock);
return;
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442c1f81..4720b4ba096a 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -160,10 +160,12 @@ config SERIAL_KS8695_CONSOLE
config SERIAL_CLPS711X
tristate "CLPS711X serial port support"
- depends on ARM && ARCH_CLPS711X
+ depends on ARCH_CLPS711X
select SERIAL_CORE
+ default y
help
- ::: To be written :::
+ This enables the driver for the on-chip UARTs of the Cirrus
+ Logic EP711x/EP721x/EP731x processors.
config SERIAL_CLPS711X_CONSOLE
bool "Support for console on CLPS711X serial port"
@@ -173,9 +175,7 @@ config SERIAL_CLPS711X_CONSOLE
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttyCL1". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
+ "console=ttyCL1".
config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 144cd3987d4c..3ad079ffd049 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -1331,7 +1331,7 @@ static const struct spi_device_id ifx_id_table[] = {
MODULE_DEVICE_TABLE(spi, ifx_id_table);
/* spi operations */
-static const struct spi_driver ifx_spi_driver = {
+static struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
.pm = &ifx_spi_pm,
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index d5c689d6217e..e309e8b0aaba 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -132,6 +132,7 @@
#define UCR4_OREN (1<<1) /* Receiver overrun interrupt enable */
#define UCR4_DREN (1<<0) /* Recv data ready interrupt enable */
#define UFCR_RXTL_SHF 0 /* Receiver trigger level shift */
+#define UFCR_DCEDTE (1<<6) /* DCE/DTE mode select */
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
@@ -667,22 +668,11 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
{
unsigned int val;
- unsigned int ufcr_rfdiv;
-
- /* set receiver / transmitter trigger level.
- * RFDIV is set such way to satisfy requested uartclk value
- */
- val = TXTL << 10 | RXTL;
- ufcr_rfdiv = (clk_get_rate(sport->clk_per) + sport->port.uartclk / 2)
- / sport->port.uartclk;
-
- if(!ufcr_rfdiv)
- ufcr_rfdiv = 1;
-
- val |= UFCR_RFDIV_REG(ufcr_rfdiv);
+ /* set receiver / transmitter trigger level */
+ val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
+ val |= TXTL << UFCR_TXTL_SHF | RXTL;
writel(val, sport->port.membase + UFCR);
-
return 0;
}
@@ -754,6 +744,7 @@ static int imx_startup(struct uart_port *port)
}
}
+ spin_lock_irqsave(&sport->port.lock, flags);
/*
* Finally, clear and enable interrupts
*/
@@ -807,7 +798,6 @@ static int imx_startup(struct uart_port *port)
/*
* Enable modem status interrupts
*/
- spin_lock_irqsave(&sport->port.lock,flags);
imx_enable_ms(&sport->port);
spin_unlock_irqrestore(&sport->port.lock,flags);
@@ -837,10 +827,13 @@ static void imx_shutdown(struct uart_port *port)
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ unsigned long flags;
+ spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
if (USE_IRDA(sport)) {
struct imxuart_platform_data *pdata;
@@ -869,12 +862,14 @@ static void imx_shutdown(struct uart_port *port)
* Disable all interrupts, port and break condition.
*/
+ spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR1);
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
if (USE_IRDA(sport))
temp &= ~(UCR1_IREN);
writel(temp, sport->port.membase + UCR1);
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
static void
@@ -1217,6 +1212,9 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
struct imx_port *sport = imx_ports[co->index];
struct imx_port_ucrs old_ucr;
unsigned int ucr1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
/*
* First, save UCR1/2/3 and then disable interrupts
@@ -1242,6 +1240,8 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+ spin_unlock_irqrestore(&sport->port.lock, flags);
}
/*
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 2e341b81ff89..3a667eed63d6 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -73,6 +73,7 @@
#define AUART_CTRL0_CLKGATE (1 << 30)
#define AUART_CTRL2_CTSEN (1 << 15)
+#define AUART_CTRL2_RTSEN (1 << 14)
#define AUART_CTRL2_RTS (1 << 11)
#define AUART_CTRL2_RXE (1 << 9)
#define AUART_CTRL2_TXE (1 << 8)
@@ -259,9 +260,12 @@ static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
u32 ctrl = readl(u->membase + AUART_CTRL2);
- ctrl &= ~AUART_CTRL2_RTS;
- if (mctrl & TIOCM_RTS)
- ctrl |= AUART_CTRL2_RTS;
+ ctrl &= ~AUART_CTRL2_RTSEN;
+ if (mctrl & TIOCM_RTS) {
+ if (u->state->port.flags & ASYNC_CTS_FLOW)
+ ctrl |= AUART_CTRL2_RTSEN;
+ }
+
s->ctrl = mctrl;
writel(ctrl, u->membase + AUART_CTRL2);
}
@@ -359,9 +363,9 @@ static void mxs_auart_settermios(struct uart_port *u,
/* figure out the hardware flow control settings */
if (cflag & CRTSCTS)
- ctrl2 |= AUART_CTRL2_CTSEN;
+ ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN;
else
- ctrl2 &= ~AUART_CTRL2_CTSEN;
+ ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN);
/* set baud rate */
baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index 654755a990df..333c8d012b0e 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1348,10 +1348,16 @@ static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser)
static int pmz_poll_get_char(struct uart_port *port)
{
struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+ int tries = 2;
- while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0)
- udelay(5);
- return read_zsdata(uap);
+ while (tries) {
+ if ((read_zsreg(uap, R0) & Rx_CH_AV) != 0)
+ return read_zsdata(uap);
+ if (tries--)
+ udelay(5);
+ }
+
+ return NO_POLL_CHAR;
}
static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index d4d8c9453cd8..9be296cf7295 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -25,6 +25,7 @@
#include <linux/module.h>
#include <linux/errno.h>
+#include <linux/sh_dma.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
@@ -1410,8 +1411,8 @@ static void work_fn_rx(struct work_struct *work)
/* Handle incomplete DMA receive */
struct tty_struct *tty = port->state->port.tty;
struct dma_chan *chan = s->chan_rx;
- struct sh_desc *sh_desc = container_of(desc, struct sh_desc,
- async_tx);
+ struct shdma_desc *sh_desc = container_of(desc,
+ struct shdma_desc, async_tx);
unsigned long flags;
int count;
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 6cd414341d5e..6579ffdd8e9b 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -216,8 +216,7 @@ static int ulite_startup(struct uart_port *port)
{
int ret;
- ret = request_irq(port->irq, ulite_isr,
- IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+ ret = request_irq(port->irq, ulite_isr, IRQF_SHARED, "uartlite", port);
if (ret)
return ret;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index a7773a3e02b1..7065df6036ca 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -13,7 +13,7 @@ config USB_ARCH_HAS_OHCI
default y if PXA3xx
default y if ARCH_EP93XX
default y if ARCH_AT91
- default y if ARCH_PNX4008 && I2C
+ default y if ARCH_PNX4008
default y if MFD_TC6393XB
default y if ARCH_W90X900
default y if ARCH_DAVINCI_DA8XX
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 8337fb5d988d..47e499c9c0b6 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -1,9 +1,9 @@
config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller"
- depends on USB
+ depends on USB || USB_GADGET
help
- Say Y here if your system has a dual role high speed USB
- controller based on ChipIdea silicon IP. Currently, only the
+ Say Y here if your system has a dual role high speed USB
+ controller based on ChipIdea silicon IP. Currently, only the
peripheral mode is supported.
When compiled dynamically, the module will be called ci-hdrc.ko.
@@ -12,7 +12,7 @@ if USB_CHIPIDEA
config USB_CHIPIDEA_UDC
bool "ChipIdea device controller"
- depends on USB_GADGET
+ depends on USB_GADGET=y || USB_GADGET=USB_CHIPIDEA
select USB_GADGET_DUALSPEED
help
Say Y here to enable device controller functionality of the
@@ -20,6 +20,7 @@ config USB_CHIPIDEA_UDC
config USB_CHIPIDEA_HOST
bool "ChipIdea host controller"
+ depends on USB=y || USB=USB_CHIPIDEA
select USB_EHCI_ROOT_HUB_TT
help
Say Y here to enable host controller functionality of the
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index c7a032a4f0c5..d214448b677e 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -78,8 +78,7 @@ static inline int ep_to_bit(struct ci13xxx *ci, int n)
}
/**
- * hw_device_state: enables/disables interrupts & starts/stops device (execute
- * without interruption)
+ * hw_device_state: enables/disables interrupts (execute without interruption)
* @dma: 0 => disable, !0 => enable and set dma engine
*
* This function returns an error code
@@ -91,9 +90,7 @@ static int hw_device_state(struct ci13xxx *ci, u32 dma)
/* interrupt, error, port change, reset, sleep/suspend */
hw_write(ci, OP_USBINTR, ~0,
USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
- hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
} else {
- hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
hw_write(ci, OP_USBINTR, ~0, 0);
}
return 0;
@@ -774,10 +771,7 @@ __acquires(mEp->lock)
{
struct ci13xxx_req *mReq, *mReqTemp;
struct ci13xxx_ep *mEpTemp = mEp;
- int uninitialized_var(retval);
-
- if (list_empty(&mEp->qh.queue))
- return -EINVAL;
+ int retval = 0;
list_for_each_entry_safe(mReq, mReqTemp, &mEp->qh.queue,
queue) {
@@ -1420,6 +1414,21 @@ static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
return -ENOTSUPP;
}
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int ci13xxx_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct ci13xxx *ci = container_of(_gadget, struct ci13xxx, gadget);
+
+ if (is_on)
+ hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+ else
+ hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+
+ return 0;
+}
+
static int ci13xxx_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int ci13xxx_stop(struct usb_gadget *gadget,
@@ -1432,6 +1441,7 @@ static int ci13xxx_stop(struct usb_gadget *gadget,
static const struct usb_gadget_ops usb_gadget_ops = {
.vbus_session = ci13xxx_vbus_session,
.wakeup = ci13xxx_wakeup,
+ .pullup = ci13xxx_pullup,
.vbus_draw = ci13xxx_vbus_draw,
.udc_start = ci13xxx_start,
.udc_stop = ci13xxx_stop,
@@ -1455,7 +1465,12 @@ static int init_eps(struct ci13xxx *ci)
mEp->ep.name = mEp->name;
mEp->ep.ops = &usb_ep_ops;
- mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
+ /*
+ * for ep0: maxP defined in desc, for other
+ * eps, maxP is set by epautoconfig() called
+ * by gadget layer
+ */
+ mEp->ep.maxpacket = (unsigned short)~0;
INIT_LIST_HEAD(&mEp->qh.queue);
mEp->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL,
@@ -1475,6 +1490,7 @@ static int init_eps(struct ci13xxx *ci)
else
ci->ep0in = mEp;
+ mEp->ep.maxpacket = CTRL_PAYLOAD_MAX;
continue;
}
@@ -1484,6 +1500,17 @@ static int init_eps(struct ci13xxx *ci)
return retval;
}
+static void destroy_eps(struct ci13xxx *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->hw_ep_max; i++) {
+ struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i];
+
+ dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma);
+ }
+}
+
/**
* ci13xxx_start: register a gadget driver
* @gadget: our gadget
@@ -1691,7 +1718,7 @@ static int udc_start(struct ci13xxx *ci)
if (ci->platdata->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
if (ci->transceiver == NULL) {
retval = -ENODEV;
- goto free_pools;
+ goto destroy_eps;
}
}
@@ -1729,7 +1756,7 @@ static int udc_start(struct ci13xxx *ci)
remove_trans:
if (!IS_ERR_OR_NULL(ci->transceiver)) {
- otg_set_peripheral(ci->transceiver->otg, &ci->gadget);
+ otg_set_peripheral(ci->transceiver->otg, NULL);
if (ci->global_phy)
usb_put_phy(ci->transceiver);
}
@@ -1742,6 +1769,8 @@ unreg_device:
put_transceiver:
if (!IS_ERR_OR_NULL(ci->transceiver) && ci->global_phy)
usb_put_phy(ci->transceiver);
+destroy_eps:
+ destroy_eps(ci);
free_pools:
dma_pool_destroy(ci->td_pool);
free_qh_pool:
@@ -1756,18 +1785,12 @@ free_qh_pool:
*/
static void udc_stop(struct ci13xxx *ci)
{
- int i;
-
if (ci == NULL)
return;
usb_del_gadget_udc(&ci->gadget);
- for (i = 0; i < ci->hw_ep_max; i++) {
- struct ci13xxx_ep *mEp = &ci->ci13xxx_ep[i];
-
- dma_pool_free(ci->qh_pool, mEp->qh.ptr, mEp->qh.dma);
- }
+ destroy_eps(ci);
dma_pool_destroy(ci->td_pool);
dma_pool_destroy(ci->qh_pool);
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 56d6bf668488..f763ed7ba91e 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1104,7 +1104,8 @@ skip_normal_probe:
}
- if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
+ if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+ control_interface->cur_altsetting->desc.bNumEndpoints == 0)
return -EINVAL;
epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 65a55abb791f..5f0cb417b736 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -109,12 +109,14 @@ static struct usb_driver wdm_driver;
/* return intfdata if we own the interface, else look up intf in the list */
static struct wdm_device *wdm_find_device(struct usb_interface *intf)
{
- struct wdm_device *desc = NULL;
+ struct wdm_device *desc;
spin_lock(&wdm_device_list_lock);
list_for_each_entry(desc, &wdm_device_list, device_list)
if (desc->intf == intf)
- break;
+ goto found;
+ desc = NULL;
+found:
spin_unlock(&wdm_device_list_lock);
return desc;
@@ -122,12 +124,14 @@ static struct wdm_device *wdm_find_device(struct usb_interface *intf)
static struct wdm_device *wdm_find_device_by_minor(int minor)
{
- struct wdm_device *desc = NULL;
+ struct wdm_device *desc;
spin_lock(&wdm_device_list_lock);
list_for_each_entry(desc, &wdm_device_list, device_list)
if (desc->intf->minor == minor)
- break;
+ goto found;
+ desc = NULL;
+found:
spin_unlock(&wdm_device_list_lock);
return desc;
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index d95696584762..3440812b4a84 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
- if (!bus->root_hub)
+ if (!bus_to_hcd(bus)->rh_registered)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc84106ac057..75ba2091f9b4 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
- }
- mutex_unlock(&usb_bus_list_lock);
-
- if (retval == 0) {
+ } else {
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock);
@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
}
+ mutex_unlock(&usb_bus_list_lock);
return retval;
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 821126eb8176..128a804c42f4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -25,6 +25,7 @@
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/freezer.h>
+#include <linux/random.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
@@ -2181,6 +2182,14 @@ int usb_new_device(struct usb_device *udev)
/* Tell the world! */
announce_device(udev);
+ if (udev->serial)
+ add_device_randomness(udev->serial, strlen(udev->serial));
+ if (udev->product)
+ add_device_randomness(udev->product, strlen(udev->product));
+ if (udev->manufacturer)
+ add_device_randomness(udev->manufacturer,
+ strlen(udev->manufacturer));
+
device_enable_async_suspend(&udev->dev);
/*
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f15501f4c585..e77a8e8eaa23 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -71,6 +71,10 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x04b4, 0x0526), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* Microchip Joss Optical infrared touchboard device */
+ { USB_DEVICE(0x04d8, 0x000c), .driver_info =
+ USB_QUIRK_CONFIG_INTF_STRINGS },
+
/* Samsung Android phone modem - ID conflict with SPH-I500 */
{ USB_DEVICE(0x04e8, 0x6601), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index c34452a7304f..a68ff53124dc 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -436,16 +436,21 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
dev_err(dev, "missing IRQ\n");
return -ENODEV;
}
- dwc->xhci_resources[1] = *res;
+ dwc->xhci_resources[1].start = res->start;
+ dwc->xhci_resources[1].end = res->end;
+ dwc->xhci_resources[1].flags = res->flags;
+ dwc->xhci_resources[1].name = res->name;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "missing memory resource\n");
return -ENODEV;
}
- dwc->xhci_resources[0] = *res;
+ dwc->xhci_resources[0].start = res->start;
dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
/*
* Request memory region but exclude xHCI regs,
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 9b94886b66e5..e4d5ca86b9da 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -720,7 +720,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
transferred = min_t(u32, ur->length,
transfer_size - length);
memcpy(ur->buf, dwc->ep0_bounce, transferred);
- dwc->ep0_bounced = false;
} else {
transferred = ur->length - length;
}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 58fdfad96b4d..c2813c2b005a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -263,8 +263,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- usb_gadget_unmap_request(&dwc->gadget, &req->request,
- req->direction);
+ if (dwc->ep0_bounced && dep->number == 0)
+ dwc->ep0_bounced = false;
+ else
+ usb_gadget_unmap_request(&dwc->gadget, &req->request,
+ req->direction);
dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n",
req, dep->name, req->request.actual,
@@ -1026,6 +1029,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
if (list_empty(&dep->request_list)) {
dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
dep->name);
+ dep->flags |= DWC3_EP_PENDING_REQUEST;
return;
}
@@ -1089,6 +1093,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
if (dep->flags & DWC3_EP_PENDING_REQUEST) {
int ret;
+ /*
+ * If xfernotready is already elapsed and it is a case
+ * of isoc transfer, then issue END TRANSFER, so that
+ * you can receive xfernotready again and can have
+ * notion of current microframe.
+ */
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
+ dwc3_stop_active_transfer(dwc, dep->number);
+ return 0;
+ }
+
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
if (ret && ret != -EBUSY) {
struct dwc3 *dwc = dep->dwc;
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index ee0ebacf8227..89dcf155d57e 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -450,7 +450,7 @@ static int dbgp_ehci_startup(void)
writel(FLAG_CF, &ehci_regs->configured_flag);
/* Wait until the controller is no longer halted */
- loop = 10;
+ loop = 1000;
do {
status = readl(&ehci_regs->status);
if (!(status & STS_HALT))
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index c9e66dfb02e6..1e35963bd4ed 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -475,8 +475,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
unsigned long flags;
if (!_ep || !ep
- || !desc || ep->ep.desc
- || _ep->name == ep0name
+ || !desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT
|| (maxpacket = usb_endpoint_maxp(desc)) == 0
|| maxpacket > ep->maxpacket) {
@@ -530,7 +529,6 @@ ok:
tmp |= AT91_UDP_EPEDS;
__raw_writel(tmp, ep->creg);
- ep->ep.desc = desc;
ep->ep.maxpacket = maxpacket;
/*
@@ -1635,7 +1633,6 @@ static int at91_start(struct usb_gadget *gadget,
udc->driver = driver;
udc->gadget.dev.driver = &driver->driver;
udc->gadget.dev.of_node = udc->pdev->dev.of_node;
- dev_set_drvdata(&udc->gadget.dev, &driver->driver);
udc->enabled = 1;
udc->selfpowered = 1;
@@ -1656,7 +1653,6 @@ static int at91_stop(struct usb_gadget *gadget,
spin_unlock_irqrestore(&udc->lock, flags);
udc->gadget.dev.driver = NULL;
- dev_set_drvdata(&udc->gadget.dev, NULL);
udc->driver = NULL;
DBG("unbound from %s\n", driver->driver.name);
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index b799106027ad..afdbb1cbf5d9 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1916,6 +1916,27 @@ done:
return retval;
}
+/* usb 3.0 root hub device descriptor */
+struct {
+ struct usb_bos_descriptor bos;
+ struct usb_ss_cap_descriptor ss_cap;
+} __packed usb3_bos_desc = {
+
+ .bos = {
+ .bLength = USB_DT_BOS_SIZE,
+ .bDescriptorType = USB_DT_BOS,
+ .wTotalLength = cpu_to_le16(sizeof(usb3_bos_desc)),
+ .bNumDeviceCaps = 1,
+ },
+ .ss_cap = {
+ .bLength = USB_DT_USB_SS_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_SS_CAP_TYPE,
+ .wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION),
+ .bFunctionalitySupport = ilog2(USB_5GBPS_OPERATION),
+ },
+};
+
static inline void
ss_hub_descriptor(struct usb_hub_descriptor *desc)
{
@@ -2006,6 +2027,18 @@ static int dummy_hub_control(
else
hub_descriptor((struct usb_hub_descriptor *) buf);
break;
+
+ case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+ if (hcd->speed != HCD_USB3)
+ goto error;
+
+ if ((wValue >> 8) != USB_DT_BOS)
+ goto error;
+
+ memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc));
+ retval = sizeof(usb3_bos_desc);
+ break;
+
case GetHubStatus:
*(__le32 *) buf = cpu_to_le32(0);
break;
@@ -2503,10 +2536,8 @@ static int dummy_hcd_probe(struct platform_device *pdev)
hs_hcd->has_tt = 1;
retval = usb_add_hcd(hs_hcd, 0, 0);
- if (retval != 0) {
- usb_put_hcd(hs_hcd);
- return retval;
- }
+ if (retval)
+ goto put_usb2_hcd;
if (mod_data.is_super_speed) {
ss_hcd = usb_create_shared_hcd(&dummy_hcd, &pdev->dev,
@@ -2525,6 +2556,8 @@ static int dummy_hcd_probe(struct platform_device *pdev)
put_usb3_hcd:
usb_put_hcd(ss_hcd);
dealloc_usb2_hcd:
+ usb_remove_hcd(hs_hcd);
+put_usb2_hcd:
usb_put_hcd(hs_hcd);
the_controller.hs_hcd = the_controller.ss_hcd = NULL;
return retval;
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 8adc79d1b402..829aba75a6df 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -34,11 +34,15 @@
/* Debugging ****************************************************************/
#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
# define pr_vdebug pr_debug
+#endif /* pr_vdebug */
# define ffs_dump_mem(prefix, ptr, len) \
print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
#else
+#ifndef pr_vdebug
# define pr_vdebug(...) do { } while (0)
+#endif /* pr_vdebug */
# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
#endif /* VERBOSE_DEBUG */
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index 965a6293206a..8ee9268fe253 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -301,7 +301,7 @@ pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
struct page *page;
int err;
- page = alloc_page(gfp_flags);
+ page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 3d28fb976c78..9fd7886cfa9a 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1836,7 +1836,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* init to known state, then setup irqs */
udc_reset(dev);
udc_reinit (dev);
- if (request_irq(pdev->irq, goku_irq, IRQF_SHARED/*|IRQF_SAMPLE_RANDOM*/,
+ if (request_irq(pdev->irq, goku_irq, IRQF_SHARED,
driver_name, dev) != 0) {
DBG(dev, "request interrupt %d failed\n", pdev->irq);
retval = -EBUSY;
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 53c093b941e5..907ad3ecb341 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -2201,19 +2201,15 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
#ifdef CONFIG_ARCH_LUBBOCK
if (machine_is_lubbock()) {
- retval = request_irq(LUBBOCK_USB_DISC_IRQ,
- lubbock_vbus_irq,
- IRQF_SAMPLE_RANDOM,
- driver_name, dev);
+ retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq,
+ 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
goto err_irq_lub;
}
- retval = request_irq(LUBBOCK_USB_IRQ,
- lubbock_vbus_irq,
- IRQF_SAMPLE_RANDOM,
- driver_name, dev);
+ retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq,
+ 0, driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_IRQ, retval);
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index b13e0bb5f5b8..0bb617e1dda2 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -3599,6 +3599,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
if (hsotg->num_of_eps == 0) {
dev_err(dev, "wrong number of EPs (zero)\n");
+ ret = -EINVAL;
goto err_supplies;
}
@@ -3606,6 +3607,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!eps) {
dev_err(dev, "cannot get memory\n");
+ ret = -ENOMEM;
goto err_supplies;
}
@@ -3622,6 +3624,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!hsotg->ctrl_req) {
dev_err(dev, "failed to allocate ctrl req\n");
+ ret = -ENOMEM;
goto err_ep_mem;
}
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index ae8b18869b8c..8d9bcd8207c8 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -656,9 +656,8 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (!(filp->f_mode & FMODE_WRITE))
ro = 1;
- if (filp->f_path.dentry)
- inode = filp->f_path.dentry->d_inode;
- if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ inode = filp->f_path.dentry->d_inode;
+ if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
LINFO(curlun, "invalid file type: %s\n", filename);
goto out;
}
@@ -667,7 +666,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
* If we can't read the file, it's no good.
* If we can't write the file, use it read-only.
*/
- if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) {
+ if (!(filp->f_op->read || filp->f_op->aio_read)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
}
@@ -712,7 +711,6 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (fsg_lun_is_open(curlun))
fsg_lun_close(curlun);
- get_file(filp);
curlun->blksize = blksize;
curlun->blkbits = blkbits;
curlun->ro = ro;
@@ -720,10 +718,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
curlun->file_length = size;
curlun->num_sectors = num_sectors;
LDBG(curlun, "open backing file: %s\n", filename);
- rc = 0;
+ return 0;
out:
- filp_close(filp, current->files);
+ fput(filp);
return rc;
}
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 90e82e288eb9..0e5230926154 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -669,6 +669,8 @@ static int eth_stop(struct net_device *net)
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
struct gether *link = dev->port_usb;
+ const struct usb_endpoint_descriptor *in;
+ const struct usb_endpoint_descriptor *out;
if (link->close)
link->close(link);
@@ -682,10 +684,14 @@ static int eth_stop(struct net_device *net)
* their own pace; the network stack can handle old packets.
* For the moment we leave this here, since it works.
*/
+ in = link->in_ep->desc;
+ out = link->out_ep->desc;
usb_ep_disable(link->in_ep);
usb_ep_disable(link->out_ep);
if (netif_carrier_ok(net)) {
DBG(dev, "host still using in/out endpoints\n");
+ link->in_ep->desc = in;
+ link->out_ep->desc = out;
usb_ep_enable(link->in_ep);
usb_ep_enable(link->out_ep);
}
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 5b3f5fffea92..da6d479ff9a6 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -132,11 +132,15 @@ static unsigned n_ports;
#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
#define pr_vdebug(fmt, arg...) \
pr_debug(fmt, ##arg)
+#endif /* pr_vdebug */
#else
+#ifndef pr_vdebig
#define pr_vdebug(fmt, arg...) \
({ if (0) pr_debug(fmt, ##arg); })
+#endif /* pr_vdebug */
#endif
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/u_uac1.c
index af9898982059..e0c5e88e03ed 100644
--- a/drivers/usb/gadget/u_uac1.c
+++ b/drivers/usb/gadget/u_uac1.c
@@ -275,17 +275,17 @@ static int gaudio_close_snd_dev(struct gaudio *gau)
/* Close control device */
snd = &gau->control;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM playback device and setup substream */
snd = &gau->playback;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
/* Close PCM capture device and setup substream */
snd = &gau->capture;
if (snd->filp)
- filp_close(snd->filp, current->files);
+ filp_close(snd->filp, NULL);
return 0;
}
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index bb55eb4a7d48..d7fe287d0678 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -56,15 +56,6 @@
#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
-/* Errata i693 */
-static struct clk *utmi_p1_fck;
-static struct clk *utmi_p2_fck;
-static struct clk *xclk60mhsp1_ck;
-static struct clk *xclk60mhsp2_ck;
-static struct clk *usbhost_p1_fck;
-static struct clk *usbhost_p2_fck;
-static struct clk *init_60m_fclk;
-
/*-------------------------------------------------------------------------*/
static const struct hc_driver ehci_omap_hc_driver;
@@ -80,40 +71,6 @@ static inline u32 ehci_read(void __iomem *base, u32 reg)
return __raw_readl(base + reg);
}
-/* Erratum i693 workaround sequence */
-static void omap_ehci_erratum_i693(struct ehci_hcd *ehci)
-{
- int ret = 0;
-
- /* Switch to the internal 60 MHz clock */
- ret = clk_set_parent(utmi_p1_fck, init_60m_fclk);
- if (ret != 0)
- ehci_err(ehci, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
-
- ret = clk_set_parent(utmi_p2_fck, init_60m_fclk);
- if (ret != 0)
- ehci_err(ehci, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
-
- clk_enable(usbhost_p1_fck);
- clk_enable(usbhost_p2_fck);
-
- /* Wait 1ms and switch back to the external clock */
- mdelay(1);
- ret = clk_set_parent(utmi_p1_fck, xclk60mhsp1_ck);
- if (ret != 0)
- ehci_err(ehci, "xclk60mhsp1_ck set parent"
- "failed error:%d\n", ret);
-
- ret = clk_set_parent(utmi_p2_fck, xclk60mhsp2_ck);
- if (ret != 0)
- ehci_err(ehci, "xclk60mhsp2_ck set parent"
- "failed error:%d\n", ret);
-
- clk_disable(usbhost_p1_fck);
- clk_disable(usbhost_p2_fck);
-}
static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
{
@@ -195,50 +152,6 @@ static int omap_ehci_init(struct usb_hcd *hcd)
return rc;
}
-static int omap_ehci_hub_control(
- struct usb_hcd *hcd,
- u16 typeReq,
- u16 wValue,
- u16 wIndex,
- char *buf,
- u16 wLength
-)
-{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- u32 __iomem *status_reg = &ehci->regs->port_status[
- (wIndex & 0xff) - 1];
- u32 temp;
- unsigned long flags;
- int retval = 0;
-
- spin_lock_irqsave(&ehci->lock, flags);
-
- if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
- temp = ehci_readl(ehci, status_reg);
- if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
- retval = -EPIPE;
- goto done;
- }
-
- temp &= ~PORT_WKCONN_E;
- temp |= PORT_WKDISC_E | PORT_WKOC_E;
- ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
-
- omap_ehci_erratum_i693(ehci);
-
- set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
- goto done;
- }
-
- spin_unlock_irqrestore(&ehci->lock, flags);
-
- /* Handle the hub control events here */
- return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
-done:
- spin_unlock_irqrestore(&ehci->lock, flags);
- return retval;
-}
-
static void disable_put_regulator(
struct ehci_hcd_omap_platform_data *pdata)
{
@@ -351,79 +264,9 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
goto err_pm_runtime;
}
- /* get clocks */
- utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
- if (IS_ERR(utmi_p1_fck)) {
- ret = PTR_ERR(utmi_p1_fck);
- dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_add_hcd;
- }
-
- xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
- if (IS_ERR(xclk60mhsp1_ck)) {
- ret = PTR_ERR(xclk60mhsp1_ck);
- dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_utmi_p1_fck;
- }
-
- utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
- if (IS_ERR(utmi_p2_fck)) {
- ret = PTR_ERR(utmi_p2_fck);
- dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_xclk60mhsp1_ck;
- }
-
- xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
- if (IS_ERR(xclk60mhsp2_ck)) {
- ret = PTR_ERR(xclk60mhsp2_ck);
- dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_utmi_p2_fck;
- }
-
- usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
- if (IS_ERR(usbhost_p1_fck)) {
- ret = PTR_ERR(usbhost_p1_fck);
- dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
- goto err_xclk60mhsp2_ck;
- }
-
- usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
- if (IS_ERR(usbhost_p2_fck)) {
- ret = PTR_ERR(usbhost_p2_fck);
- dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
- goto err_usbhost_p1_fck;
- }
-
- init_60m_fclk = clk_get(dev, "init_60m_fclk");
- if (IS_ERR(init_60m_fclk)) {
- ret = PTR_ERR(init_60m_fclk);
- dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_usbhost_p2_fck;
- }
return 0;
-err_usbhost_p2_fck:
- clk_put(usbhost_p2_fck);
-
-err_usbhost_p1_fck:
- clk_put(usbhost_p1_fck);
-
-err_xclk60mhsp2_ck:
- clk_put(xclk60mhsp2_ck);
-
-err_utmi_p2_fck:
- clk_put(utmi_p2_fck);
-
-err_xclk60mhsp1_ck:
- clk_put(xclk60mhsp1_ck);
-
-err_utmi_p1_fck:
- clk_put(utmi_p1_fck);
-
-err_add_hcd:
- usb_remove_hcd(hcd);
-
err_pm_runtime:
disable_put_regulator(pdata);
pm_runtime_put_sync(dev);
@@ -454,14 +297,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev)
iounmap(hcd->regs);
usb_put_hcd(hcd);
- clk_put(utmi_p1_fck);
- clk_put(utmi_p2_fck);
- clk_put(xclk60mhsp1_ck);
- clk_put(xclk60mhsp2_ck);
- clk_put(usbhost_p1_fck);
- clk_put(usbhost_p2_fck);
- clk_put(init_60m_fclk);
-
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -532,7 +367,7 @@ static const struct hc_driver ehci_omap_hc_driver = {
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
- .hub_control = omap_ehci_hub_control,
+ .hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 9bc39ca460c8..4b66374bdc8e 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -128,9 +128,17 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
else {
qtd = list_entry (qh->qtd_list.next,
struct ehci_qtd, qtd_list);
- /* first qtd may already be partially processed */
- if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
+ /*
+ * first qtd may already be partially processed.
+ * If we come here during unlink, the QH overlay region
+ * might have reference to the just unlinked qtd. The
+ * qtd is updated in qh_completions(). Update the QH
+ * overlay here.
+ */
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current) {
+ qh->hw->hw_qtd_next = qtd->hw_next;
qtd = NULL;
+ }
}
if (qtd)
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
index 58c96bd50d22..0c9e43cfaff5 100644
--- a/drivers/usb/host/ehci-sead3.c
+++ b/drivers/usb/host/ehci-sead3.c
@@ -40,7 +40,7 @@ static int ehci_sead3_setup(struct usb_hcd *hcd)
ehci->need_io_watchdog = 0;
/* Set burst length to 16 words. */
- ehci_writel(ehci, 0x1010, &ehci->regs->reserved[1]);
+ ehci_writel(ehci, 0x1010, &ehci->regs->reserved1[1]);
return ret;
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 950e95efa381..26dedb30ad0b 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -799,11 +799,12 @@ static int tegra_ehci_remove(struct platform_device *pdev)
#endif
usb_remove_hcd(hcd);
- usb_put_hcd(hcd);
tegra_usb_phy_close(tegra->phy);
iounmap(hcd->regs);
+ usb_put_hcd(hcd);
+
clk_disable_unprepare(tegra->clk);
clk_put(tegra->clk);
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 2ed112d3e159..256326322cfd 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -543,12 +543,12 @@ static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
short_ok ? "" : "not_",
PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ /* save the data underrun error code for later and
+ * proceed with the status stage
+ */
+ urb->actual_length += PTD_GET_COUNT(ptd);
if (usb_pipecontrol(urb->pipe)) {
ep->nextpid = USB_PID_ACK;
- /* save the data underrun error code for later and
- * proceed with the status stage
- */
- urb->actual_length += PTD_GET_COUNT(ptd);
BUG_ON(urb->actual_length > urb->transfer_buffer_length);
if (urb->status == -EINPROGRESS)
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index a665b3eaa746..0bf72f943b00 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -467,7 +467,8 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
at91_for_each_port(port) {
- if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+ if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
+ gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
gpio = pdata->overcurrent_pin[port];
break;
}
@@ -570,6 +571,16 @@ static int __devinit ohci_hcd_at91_drv_probe(struct platform_device *pdev)
if (pdata) {
at91_for_each_port(i) {
+ /*
+ * do not configure PIO if not in relation with
+ * real USB port on board
+ */
+ if (i >= pdata->ports) {
+ pdata->vbus_pin[i] = -EINVAL;
+ pdata->overcurrent_pin[i] = -EINVAL;
+ break;
+ }
+
if (!gpio_is_valid(pdata->vbus_pin[i]))
continue;
gpio = pdata->vbus_pin[i];
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index e7d75d295988..f8b2d91851f7 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -403,8 +403,6 @@ err0:
static inline void
usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-
usb_remove_hcd(hcd);
if (!IS_ERR_OR_NULL(hcd->phy)) {
(void) otg_set_host(hcd->phy->otg, 0);
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index df0828cb2aa3..966d1484ee79 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -75,7 +75,9 @@
#define NB_PIF0_PWRDOWN_1 0x01100013
#define USB_INTEL_XUSB2PR 0xD0
+#define USB_INTEL_USB2PRM 0xD4
#define USB_INTEL_USB3_PSSEN 0xD8
+#define USB_INTEL_USB3PRM 0xDC
static struct amd_chipset_info {
struct pci_dev *nb_dev;
@@ -772,10 +774,18 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
return;
}
- ports_available = 0xffffffff;
+ /* Read USB3PRM, the USB 3.0 Port Routing Mask Register
+ * Indicate the ports that can be changed from OS.
+ */
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB3PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable ports to enable SuperSpeed: 0x%x\n",
+ ports_available);
+
/* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
- * Register, to turn on SuperSpeed terminations for all
- * available ports.
+ * Register, to turn on SuperSpeed terminations for the
+ * switchable ports.
*/
pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
cpu_to_le32(ports_available));
@@ -785,7 +795,16 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
"under xHCI: 0x%x\n", ports_available);
- ports_available = 0xffffffff;
+ /* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
+ * Indicate the USB 2.0 ports to be controlled by the xHCI host.
+ */
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_USB2PRM,
+ &ports_available);
+
+ dev_dbg(&xhci_pdev->dev, "Configurable USB 2.0 ports to hand over to xCHI: 0x%x\n",
+ ports_available);
+
/* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to
* switch the USB 2.0 power and data lines over to the xHCI
* host.
@@ -800,6 +819,13 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
}
EXPORT_SYMBOL_GPL(usb_enable_xhci_ports);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+{
+ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, 0x0);
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, 0x0);
+}
+EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+
/**
* PCI Quirks for xHCI.
*
@@ -815,12 +841,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
void __iomem *op_reg_base;
u32 val;
int timeout;
+ int len = pci_resource_len(pdev, 0);
if (!mmio_resource_enabled(pdev, 0))
return;
- base = ioremap_nocache(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0));
+ base = ioremap_nocache(pci_resource_start(pdev, 0), len);
if (base == NULL)
return;
@@ -830,9 +856,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
*/
ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
do {
+ if ((ext_cap_offset + sizeof(val)) > len) {
+ /* We're reading garbage from the controller */
+ dev_warn(&pdev->dev,
+ "xHCI controller failing to respond");
+ return;
+ }
+
if (!ext_cap_offset)
/* We've reached the end of the extended capabilities */
goto hc_init;
+
val = readl(base + ext_cap_offset);
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
break;
@@ -863,9 +897,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
/* Disable any BIOS SMIs and clear all SMI events*/
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
+hc_init:
if (usb_is_intel_switchable_xhci(pdev))
usb_enable_xhci_ports(pdev);
-hc_init:
+
op_reg_base = base + XHCI_HC_LENGTH(readl(base));
/* Wait for the host controller to be ready before writing any
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index b1002a8ef96f..7f69a39163ce 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -10,10 +10,12 @@ void usb_amd_quirk_pll_disable(void);
void usb_amd_quirk_pll_enable(void);
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev);
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
#else
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_amd_dev_put(void) {}
+static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
#endif /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 74bfc868b7ad..d5eb357aa5c4 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -493,11 +493,48 @@ static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
* when this bit is set.
*/
pls |= USB_PORT_STAT_CONNECTION;
+ } else {
+ /*
+ * If CAS bit isn't set but the Port is already at
+ * Compliance Mode, fake a connection so the USB core
+ * notices the Compliance state and resets the port.
+ * This resolves an issue generated by the SN65LVPE502CP
+ * in which sometimes the port enters compliance mode
+ * caused by a delay on the host-device negotiation.
+ */
+ if (pls == USB_SS_PORT_LS_COMP_MOD)
+ pls |= USB_PORT_STAT_CONNECTION;
}
+
/* update status field */
*status |= pls;
}
+/*
+ * Function for Compliance Mode Quirk.
+ *
+ * This Function verifies if all xhc USB3 ports have entered U0, if so,
+ * the compliance mode timer is deleted. A port won't enter
+ * compliance mode if it has previously entered U0.
+ */
+void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
+{
+ u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
+ bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
+
+ if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
+ return;
+
+ if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
+ xhci->port_status_u0 |= 1 << wIndex;
+ if (xhci->port_status_u0 == all_ports_seen_u0) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n");
+ xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n");
+ }
+ }
+}
+
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
@@ -651,6 +688,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* Update Port Link State for super speed ports*/
if (hcd->speed == HCD_USB3) {
xhci_hub_report_link_state(&status, temp);
+ /*
+ * Verify if all USB3 Ports Have entered U0 already.
+ * Delete Compliance Mode Timer if so.
+ */
+ xhci_del_comp_mod_timer(xhci, temp, wIndex);
}
if (bus_state->port_c_suspend & (1 << wIndex))
status |= 1 << USB_PORT_FEAT_C_SUSPEND;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 18b231b0c5d3..9bfd4ca1153c 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -94,11 +94,21 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
xhci->limit_active_eps = 64;
xhci->quirks |= XHCI_SW_BW_CHECKING;
+ /*
+ * PPT desktop boards DH77EB and DH77DF will power back on after
+ * a few seconds of being shutdown. The fix for this is to
+ * switch the ports from xHCI to EHCI on shutdown. We can't use
+ * DMI information to find those particular boards (since each
+ * vendor will change the board name), so we have to key off all
+ * PPT chipsets.
+ */
+ xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 689bc18b051d..df90fe51b4aa 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -118,7 +118,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_hcd;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 8275645889da..643c2f3f3e73 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -145,29 +145,37 @@ static void next_trb(struct xhci_hcd *xhci,
*/
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
- union xhci_trb *next;
unsigned long long addr;
ring->deq_updates++;
- /* If this is not event ring, there is one more usable TRB */
+ /*
+ * If this is not event ring, and the dequeue pointer
+ * is not on a link TRB, there is one more usable TRB
+ */
if (ring->type != TYPE_EVENT &&
!last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
ring->num_trbs_free++;
- next = ++(ring->dequeue);
- /* Update the dequeue pointer further if that was a link TRB or we're at
- * the end of an event ring segment (which doesn't have link TRBS)
- */
- while (last_trb(xhci, ring, ring->deq_seg, next)) {
- if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci,
- ring, ring->deq_seg, next)) {
- ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ do {
+ /*
+ * Update the dequeue pointer further if that was a link TRB or
+ * we're at the end of an event ring segment (which doesn't have
+ * link TRBS)
+ */
+ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
+ if (ring->type == TYPE_EVENT &&
+ last_trb_on_last_seg(xhci, ring,
+ ring->deq_seg, ring->dequeue)) {
+ ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ }
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ } else {
+ ring->dequeue++;
}
- ring->deq_seg = ring->deq_seg->next;
- ring->dequeue = ring->deq_seg->trbs;
- next = ring->dequeue;
- }
+ } while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue));
+
addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
}
@@ -2073,8 +2081,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
trb_comp_code = COMP_SHORT_TX;
else
- xhci_warn(xhci, "WARN Successful completion on short TX: "
- "needs XHCI_TRUST_TX_LENGTH quirk?\n");
+ xhci_warn_ratelimited(xhci,
+ "WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk?\n");
case COMP_SHORT_TX:
break;
case COMP_STOP:
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 7648b2d4b268..6ece0ed288d4 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/dmi.h>
#include "xhci.h"
@@ -166,7 +167,7 @@ int xhci_reset(struct xhci_hcd *xhci)
xhci_writel(xhci, command, &xhci->op_regs->command);
ret = handshake(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, 250 * 1000);
+ CMD_RESET, 0, 10 * 1000 * 1000);
if (ret)
return ret;
@@ -175,7 +176,8 @@ int xhci_reset(struct xhci_hcd *xhci)
* xHCI cannot write to any doorbells or operational registers other
* than status until the "Controller Not Ready" flag is cleared.
*/
- ret = handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
+ ret = handshake(xhci, &xhci->op_regs->status,
+ STS_CNR, 0, 10 * 1000 * 1000);
for (i = 0; i < 2; ++i) {
xhci->bus_state[i].port_c_suspend = 0;
@@ -397,6 +399,95 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
#endif
+static void compliance_mode_recovery(unsigned long arg)
+{
+ struct xhci_hcd *xhci;
+ struct usb_hcd *hcd;
+ u32 temp;
+ int i;
+
+ xhci = (struct xhci_hcd *)arg;
+
+ for (i = 0; i < xhci->num_usb3_ports; i++) {
+ temp = xhci_readl(xhci, xhci->usb3_ports[i]);
+ if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
+ /*
+ * Compliance Mode Detected. Letting USB Core
+ * handle the Warm Reset
+ */
+ xhci_dbg(xhci, "Compliance Mode Detected->Port %d!\n",
+ i + 1);
+ xhci_dbg(xhci, "Attempting Recovery routine!\n");
+ hcd = xhci->shared_hcd;
+
+ if (hcd->state == HC_STATE_SUSPENDED)
+ usb_hcd_resume_root_hub(hcd);
+
+ usb_hcd_poll_rh_status(hcd);
+ }
+ }
+
+ if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1))
+ mod_timer(&xhci->comp_mode_recovery_timer,
+ jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+}
+
+/*
+ * Quirk to work around issue generated by the SN65LVPE502CP USB3.0 re-driver
+ * that causes ports behind that hardware to enter compliance mode sometimes.
+ * The quirk creates a timer that polls every 2 seconds the link state of
+ * each host controller's port and recovers it by issuing a Warm reset
+ * if Compliance mode is detected, otherwise the port will become "dead" (no
+ * device connections or disconnections will be detected anymore). Becasue no
+ * status event is generated when entering compliance mode (per xhci spec),
+ * this quirk is needed on systems that have the failing hardware installed.
+ */
+static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci)
+{
+ xhci->port_status_u0 = 0;
+ init_timer(&xhci->comp_mode_recovery_timer);
+
+ xhci->comp_mode_recovery_timer.data = (unsigned long) xhci;
+ xhci->comp_mode_recovery_timer.function = compliance_mode_recovery;
+ xhci->comp_mode_recovery_timer.expires = jiffies +
+ msecs_to_jiffies(COMP_MODE_RCVRY_MSECS);
+
+ set_timer_slack(&xhci->comp_mode_recovery_timer,
+ msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
+ add_timer(&xhci->comp_mode_recovery_timer);
+ xhci_dbg(xhci, "Compliance Mode Recovery Timer Initialized.\n");
+}
+
+/*
+ * This function identifies the systems that have installed the SN65LVPE502CP
+ * USB3.0 re-driver and that need the Compliance Mode Quirk.
+ * Systems:
+ * Vendor: Hewlett-Packard -> System Models: Z420, Z620 and Z820
+ */
+static bool compliance_mode_recovery_timer_quirk_check(void)
+{
+ const char *dmi_product_name, *dmi_sys_vendor;
+
+ dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
+ dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+
+ if (!(strstr(dmi_sys_vendor, "Hewlett-Packard")))
+ return false;
+
+ if (strstr(dmi_product_name, "Z420") ||
+ strstr(dmi_product_name, "Z620") ||
+ strstr(dmi_product_name, "Z820"))
+ return true;
+
+ return false;
+}
+
+static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
+{
+ return (xhci->port_status_u0 == ((1 << xhci->num_usb3_ports)-1));
+}
+
+
/*
* Initialize memory for HCD and xHC (one-time init).
*
@@ -420,6 +511,12 @@ int xhci_init(struct usb_hcd *hcd)
retval = xhci_mem_init(xhci, GFP_KERNEL);
xhci_dbg(xhci, "Finished xhci_init\n");
+ /* Initializing Compliance Mode Recovery Data If Needed */
+ if (compliance_mode_recovery_timer_quirk_check()) {
+ xhci->quirks |= XHCI_COMP_MODE_QUIRK;
+ compliance_mode_recovery_timer_init(xhci);
+ }
+
return retval;
}
@@ -628,6 +725,11 @@ void xhci_stop(struct usb_hcd *hcd)
del_timer_sync(&xhci->event_ring_timer);
#endif
+ /* Deleting Compliance Mode Recovery Timer */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ (!(xhci_all_ports_seen_u0(xhci))))
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+
if (xhci->quirks & XHCI_AMD_PLL_FIX)
usb_amd_dev_put();
@@ -658,6 +760,9 @@ void xhci_shutdown(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
+ usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
+
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
spin_unlock_irq(&xhci->lock);
@@ -802,6 +907,16 @@ int xhci_suspend(struct xhci_hcd *xhci)
}
spin_unlock_irq(&xhci->lock);
+ /*
+ * Deleting Compliance Mode Recovery Timer because the xHCI Host
+ * is about to be suspended.
+ */
+ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
+ (!(xhci_all_ports_seen_u0(xhci)))) {
+ del_timer_sync(&xhci->comp_mode_recovery_timer);
+ xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted!\n");
+ }
+
/* step 5: remove core well power */
/* synchronize irq when using MSI-X */
xhci_msix_sync_irqs(xhci);
@@ -934,6 +1049,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
usb_hcd_resume_root_hub(hcd);
usb_hcd_resume_root_hub(xhci->shared_hcd);
}
+
+ /*
+ * If system is subject to the Quirk, Compliance Mode Timer needs to
+ * be re-initialized Always after a system resume. Ports are subject
+ * to suffer the Compliance Mode issue again. It doesn't matter if
+ * ports have entered previously to U0 before system's suspension.
+ */
+ if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
+ compliance_mode_recovery_timer_init(xhci);
+
return retval;
}
#endif /* CONFIG_PM */
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 55c0785810c9..1a05908c6673 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1494,6 +1494,8 @@ struct xhci_hcd {
#define XHCI_TRUST_TX_LENGTH (1 << 10)
#define XHCI_LPM_SUPPORT (1 << 11)
#define XHCI_INTEL_HOST (1 << 12)
+#define XHCI_SPURIOUS_REBOOT (1 << 13)
+#define XHCI_COMP_MODE_QUIRK (1 << 14)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1510,6 +1512,11 @@ struct xhci_hcd {
unsigned sw_lpm_support:1;
/* support xHCI 1.0 spec USB2 hardware LPM */
unsigned hw_lpm_support:1;
+ /* Compliance Mode Recovery Data */
+ struct timer_list comp_mode_recovery_timer;
+ u32 port_status_u0;
+/* Compliance Mode Timer Triggered every 2 seconds */
+#define COMP_MODE_RCVRY_MSECS 2000
};
/* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -1537,6 +1544,8 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
#define xhci_warn(xhci, fmt, args...) \
dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#define xhci_warn_ratelimited(xhci, fmt, args...) \
+ dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
/* TODO: copied from ehci.h - can be refactored? */
/* xHCI spec says all registers are little endian */
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
index ff08015b230c..ae794b90766b 100644
--- a/drivers/usb/misc/emi62.c
+++ b/drivers/usb/misc/emi62.c
@@ -232,7 +232,7 @@ wraperr:
return err;
}
-static const struct usb_device_id id_table[] __devinitconst = {
+static const struct usb_device_id id_table[] = {
{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index ef0c3f9f0947..6259f0d99324 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -8,7 +8,7 @@ config USB_MUSB_HDRC
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
depends on USB && USB_GADGET
select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
- select NOP_USB_XCEIV if (SOC_OMAPTI81XX || SOC_OMAPAM33XX)
+ select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
@@ -57,7 +57,7 @@ config USB_MUSB_AM35X
config USB_MUSB_DSPS
tristate "TI DSPS platforms"
- depends on SOC_OMAPTI81XX || SOC_OMAPAM33XX
+ depends on SOC_TI81XX || SOC_AM33XX
config USB_MUSB_BLACKFIN
tristate "Blackfin"
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 217808d9fbe1..494772fc9e23 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -479,9 +479,9 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
ret = -ENODEV;
goto err0;
}
- strcpy((u8 *)res->name, "mc");
res->parent = NULL;
resources[1] = *res;
+ resources[1].name = "mc";
/* allocate the child platform device */
musb = platform_device_alloc("musb-hdrc", -1);
@@ -566,27 +566,28 @@ static int __devinit dsps_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, glue);
- /* create the child platform device for first instances of musb */
- ret = dsps_create_musb_pdev(glue, 0);
- if (ret != 0) {
- dev_err(&pdev->dev, "failed to create child pdev\n");
- goto err2;
- }
-
/* enable the usbss clocks */
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
+ goto err2;
+ }
+
+ /* create the child platform device for first instances of musb */
+ ret = dsps_create_musb_pdev(glue, 0);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to create child pdev\n");
goto err3;
}
return 0;
err3:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
err2:
+ pm_runtime_disable(&pdev->dev);
kfree(glue->wrp);
err1:
kfree(glue);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 4bb717d0bd41..1ae378d5fc6f 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2049,7 +2049,7 @@ static int musb_urb_enqueue(
* we only have work to do in the former case.
*/
spin_lock_irqsave(&musb->lock, flags);
- if (hep->hcpriv) {
+ if (hep->hcpriv || !next_urb(qh)) {
/* some concurrent activity submitted another urb to hep...
* odd, rare, error prone, but legal.
*/
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index 57a608584e16..c1be687e00ec 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -388,7 +388,7 @@ dma_controller_create(struct musb *musb, void __iomem *base)
struct platform_device *pdev = to_platform_device(dev);
int irq = platform_get_irq_byname(pdev, "dma");
- if (irq == 0) {
+ if (irq <= 0) {
dev_err(dev, "No DMA interrupt line!\n");
return NULL;
}
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 1a1bd9cf40c5..341625442377 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -1215,7 +1215,7 @@ static int __devinit tusb_probe(struct platform_device *pdev)
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err1;
+ goto err2;
}
return 0;
diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c
index 575fc815c932..7a88667742b6 100644
--- a/drivers/usb/otg/isp1301_omap.c
+++ b/drivers/usb/otg/isp1301_omap.c
@@ -1576,7 +1576,6 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
isp->irq_type = IRQF_TRIGGER_FALLING;
}
- isp->irq_type |= IRQF_SAMPLE_RANDOM;
status = request_irq(i2c->irq, isp1301_irq,
isp->irq_type, DRIVER_NAME, isp);
if (status < 0) {
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 8c9bb1ad3069..681da06170c2 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -603,12 +603,12 @@ static int usbhsc_resume(struct device *dev)
struct usbhs_priv *priv = dev_get_drvdata(dev);
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
- usbhs_platform_call(priv, phy_reset, pdev);
-
if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
usbhsc_power_ctrl(priv, 1);
- usbhsc_hotplug(priv);
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ usbhsc_drvcllbck_notify_hotplug(pdev);
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index ecd173032fd4..143c4e9e1be4 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -818,7 +818,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_is_dcp(pipe))
goto usbhsf_pio_prepare_push;
- if (len % 4) /* 32bit alignment */
+ if (len & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_push;
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
@@ -905,7 +905,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
/* use PIO if packet is less than pio_dma_border */
len = usbhsf_fifo_rcv_len(priv, fifo);
len = min(pkt->length - pkt->actual, len);
- if (len % 4) /* 32bit alignment */
+ if (len & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_pop_unselect;
if (len < usbhs_get_dparam(priv, pio_dma_border))
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 1834cf50888c..9b69a1323294 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1266,6 +1266,12 @@ static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
return ret;
}
+static int usbhsh_bus_nop(struct usb_hcd *hcd)
+{
+ /* nothing to do */
+ return 0;
+}
+
static struct hc_driver usbhsh_driver = {
.description = usbhsh_hcd_name,
.hcd_priv_size = sizeof(struct usbhsh_hpriv),
@@ -1290,6 +1296,8 @@ static struct hc_driver usbhsh_driver = {
*/
.hub_status_data = usbhsh_hub_status_data,
.hub_control = usbhsh_hub_control,
+ .bus_suspend = usbhsh_bus_nop,
+ .bus_resume = usbhsh_bus_nop,
};
/*
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index f398d1e34474..c15f2e7cefc7 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -61,18 +61,23 @@ static int usb_serial_device_probe(struct device *dev)
goto exit;
}
+ /* make sure suspend/resume doesn't race against port_probe */
+ retval = usb_autopm_get_interface(port->serial->interface);
+ if (retval)
+ goto exit;
+
driver = port->serial->type;
if (driver->port_probe) {
retval = driver->port_probe(port);
if (retval)
- goto exit;
+ goto exit_with_autopm;
}
retval = device_create_file(dev, &dev_attr_port_number);
if (retval) {
if (driver->port_remove)
retval = driver->port_remove(port);
- goto exit;
+ goto exit_with_autopm;
}
minor = port->number;
@@ -81,6 +86,8 @@ static int usb_serial_device_probe(struct device *dev)
"%s converter now attached to ttyUSB%d\n",
driver->description, minor);
+exit_with_autopm:
+ usb_autopm_put_interface(port->serial->interface);
exit:
return retval;
}
@@ -96,6 +103,9 @@ static int usb_serial_device_remove(struct device *dev)
if (!port)
return -ENODEV;
+ /* make sure suspend/resume doesn't race against port_remove */
+ usb_autopm_get_interface(port->serial->interface);
+
device_remove_file(&port->dev, &dev_attr_port_number);
driver = port->serial->type;
@@ -107,6 +117,7 @@ static int usb_serial_device_remove(struct device *dev)
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
driver->description, minor);
+ usb_autopm_put_interface(port->serial->interface);
return retval;
}
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index bc912e5a3beb..f906b3aec217 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -704,6 +704,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_NZR_SEM_USB_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) },
{ USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) },
@@ -804,13 +805,33 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
- { USB_DEVICE(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID) },
+ { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID,
+ USB_CLASS_VENDOR_SPEC,
+ USB_SUBCLASS_VENDOR_SPEC, 0x00) },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
+ { USB_DEVICE(FTDI_VID, PI_C865_PID) },
+ { USB_DEVICE(FTDI_VID, PI_C857_PID) },
+ { USB_DEVICE(PI_VID, PI_C866_PID) },
+ { USB_DEVICE(PI_VID, PI_C663_PID) },
+ { USB_DEVICE(PI_VID, PI_C725_PID) },
+ { USB_DEVICE(PI_VID, PI_E517_PID) },
+ { USB_DEVICE(PI_VID, PI_C863_PID) },
{ USB_DEVICE(PI_VID, PI_E861_PID) },
+ { USB_DEVICE(PI_VID, PI_C867_PID) },
+ { USB_DEVICE(PI_VID, PI_E609_PID) },
+ { USB_DEVICE(PI_VID, PI_E709_PID) },
+ { USB_DEVICE(PI_VID, PI_100F_PID) },
+ { USB_DEVICE(PI_VID, PI_1011_PID) },
+ { USB_DEVICE(PI_VID, PI_1012_PID) },
+ { USB_DEVICE(PI_VID, PI_1013_PID) },
+ { USB_DEVICE(PI_VID, PI_1014_PID) },
+ { USB_DEVICE(PI_VID, PI_1015_PID) },
+ { USB_DEVICE(PI_VID, PI_1016_PID) },
+ { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
{ USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 5661c7e2d415..41fe5826100c 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -75,6 +75,9 @@
#define FTDI_OPENDCC_GATEWAY_PID 0xBFDB
#define FTDI_OPENDCC_GBM_PID 0xBFDC
+/* NZR SEM 16+ USB (http://www.nzr.de) */
+#define FTDI_NZR_SEM_USB_PID 0xC1E0 /* NZR SEM-LOG16+ */
+
/*
* RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com)
*/
@@ -539,7 +542,10 @@
/*
* Microchip Technology, Inc.
*
- * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are also used by:
+ * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
+ * used by single function CDC ACM class based firmware demo
+ * applications. The VID/PID has also been used in firmware
+ * emulating FTDI serial chips by:
* Hornby Elite - Digital Command Control Console
* http://www.hornby.com/hornby-dcc/controllers/
*/
@@ -791,8 +797,34 @@
* Physik Instrumente
* http://www.physikinstrumente.com/en/products/
*/
+/* These two devices use the VID of FTDI */
+#define PI_C865_PID 0xe0a0 /* PI C-865 Piezomotor Controller */
+#define PI_C857_PID 0xe0a1 /* PI Encoder Trigger Box */
+
#define PI_VID 0x1a72 /* Vendor ID */
-#define PI_E861_PID 0x1008 /* E-861 piezo controller USB connection */
+#define PI_C866_PID 0x1000 /* PI C-866 Piezomotor Controller */
+#define PI_C663_PID 0x1001 /* PI C-663 Mercury-Step */
+#define PI_C725_PID 0x1002 /* PI C-725 Piezomotor Controller */
+#define PI_E517_PID 0x1005 /* PI E-517 Digital Piezo Controller Operation Module */
+#define PI_C863_PID 0x1007 /* PI C-863 */
+#define PI_E861_PID 0x1008 /* PI E-861 Piezomotor Controller */
+#define PI_C867_PID 0x1009 /* PI C-867 Piezomotor Controller */
+#define PI_E609_PID 0x100D /* PI E-609 Digital Piezo Controller */
+#define PI_E709_PID 0x100E /* PI E-709 Digital Piezo Controller */
+#define PI_100F_PID 0x100F /* PI Digital Piezo Controller */
+#define PI_1011_PID 0x1011 /* PI Digital Piezo Controller */
+#define PI_1012_PID 0x1012 /* PI Motion Controller */
+#define PI_1013_PID 0x1013 /* PI Motion Controller */
+#define PI_1014_PID 0x1014 /* PI Device */
+#define PI_1015_PID 0x1015 /* PI Device */
+#define PI_1016_PID 0x1016 /* PI Digital Servo Module */
+
+/*
+ * Kondo Kagaku Co.Ltd.
+ * http://www.kondo-robot.com/EN
+ */
+#define KONDO_VID 0x165c
+#define KONDO_USB_SERIAL_PID 0x0002
/*
* Bayer Ascensia Contour blood glucose meter USB-converter cable.
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 5811d34b6c6b..2cb30c535839 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -227,7 +227,6 @@ static void ipw_release(struct usb_serial *serial)
{
struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
- usb_wwan_release(serial);
usb_set_serial_data(serial, NULL);
kfree(data);
}
@@ -309,12 +308,12 @@ static struct usb_serial_driver ipw_device = {
.description = "IPWireless converter",
.id_table = id_table,
.num_ports = 1,
- .disconnect = usb_wwan_disconnect,
.open = ipw_open,
.close = ipw_close,
.probe = ipw_probe,
.attach = usb_wwan_startup,
.release = ipw_release,
+ .port_remove = usb_wwan_port_remove,
.dtr_rts = ipw_dtr_rts,
.write = usb_wwan_write,
};
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 57eca2448424..2f6da1e89bfa 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -82,8 +82,7 @@
* Defines used for sending commands to port
*/
-#define WAIT_FOR_EVER (HZ * 0) /* timeout urb is wait for ever */
-#define MOS_WDR_TIMEOUT (HZ * 5) /* default urb timeout */
+#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */
#define MOS_PORT1 0x0200
#define MOS_PORT2 0x0300
@@ -1232,9 +1231,12 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
return 0;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
- for (i = 0; i < NUM_URBS; ++i)
- if (mos7840_port->busy[i])
- chars += URB_TRANSFER_BUFFER_SIZE;
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->busy[i]) {
+ struct urb *urb = mos7840_port->write_urb_pool[i];
+ chars += urb->transfer_buffer_length;
+ }
+ }
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
dbg("%s - returns %d", __func__, chars);
return chars;
@@ -1344,7 +1346,7 @@ static void mos7840_close(struct usb_serial_port *port)
static void mos7840_block_until_chase_response(struct tty_struct *tty,
struct moschip_port *mos7840_port)
{
- int timeout = 1 * HZ;
+ int timeout = msecs_to_jiffies(1000);
int wait = 10;
int count;
@@ -2672,7 +2674,7 @@ static int mos7840_startup(struct usb_serial *serial)
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
+ (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, MOS_WDR_TIMEOUT);
return 0;
error:
for (/* nothing */; i >= 0; i--) {
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 08ff9b862049..5ce88d1bc6f1 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -80,85 +80,9 @@ static void option_instat_callback(struct urb *urb);
#define OPTION_PRODUCT_GTM380_MODEM 0x7201
#define HUAWEI_VENDOR_ID 0x12D1
-#define HUAWEI_PRODUCT_E600 0x1001
-#define HUAWEI_PRODUCT_E220 0x1003
-#define HUAWEI_PRODUCT_E220BIS 0x1004
-#define HUAWEI_PRODUCT_E1401 0x1401
-#define HUAWEI_PRODUCT_E1402 0x1402
-#define HUAWEI_PRODUCT_E1403 0x1403
-#define HUAWEI_PRODUCT_E1404 0x1404
-#define HUAWEI_PRODUCT_E1405 0x1405
-#define HUAWEI_PRODUCT_E1406 0x1406
-#define HUAWEI_PRODUCT_E1407 0x1407
-#define HUAWEI_PRODUCT_E1408 0x1408
-#define HUAWEI_PRODUCT_E1409 0x1409
-#define HUAWEI_PRODUCT_E140A 0x140A
-#define HUAWEI_PRODUCT_E140B 0x140B
-#define HUAWEI_PRODUCT_E140C 0x140C
-#define HUAWEI_PRODUCT_E140D 0x140D
-#define HUAWEI_PRODUCT_E140E 0x140E
-#define HUAWEI_PRODUCT_E140F 0x140F
-#define HUAWEI_PRODUCT_E1410 0x1410
-#define HUAWEI_PRODUCT_E1411 0x1411
-#define HUAWEI_PRODUCT_E1412 0x1412
-#define HUAWEI_PRODUCT_E1413 0x1413
-#define HUAWEI_PRODUCT_E1414 0x1414
-#define HUAWEI_PRODUCT_E1415 0x1415
-#define HUAWEI_PRODUCT_E1416 0x1416
-#define HUAWEI_PRODUCT_E1417 0x1417
-#define HUAWEI_PRODUCT_E1418 0x1418
-#define HUAWEI_PRODUCT_E1419 0x1419
-#define HUAWEI_PRODUCT_E141A 0x141A
-#define HUAWEI_PRODUCT_E141B 0x141B
-#define HUAWEI_PRODUCT_E141C 0x141C
-#define HUAWEI_PRODUCT_E141D 0x141D
-#define HUAWEI_PRODUCT_E141E 0x141E
-#define HUAWEI_PRODUCT_E141F 0x141F
-#define HUAWEI_PRODUCT_E1420 0x1420
-#define HUAWEI_PRODUCT_E1421 0x1421
-#define HUAWEI_PRODUCT_E1422 0x1422
-#define HUAWEI_PRODUCT_E1423 0x1423
-#define HUAWEI_PRODUCT_E1424 0x1424
-#define HUAWEI_PRODUCT_E1425 0x1425
-#define HUAWEI_PRODUCT_E1426 0x1426
-#define HUAWEI_PRODUCT_E1427 0x1427
-#define HUAWEI_PRODUCT_E1428 0x1428
-#define HUAWEI_PRODUCT_E1429 0x1429
-#define HUAWEI_PRODUCT_E142A 0x142A
-#define HUAWEI_PRODUCT_E142B 0x142B
-#define HUAWEI_PRODUCT_E142C 0x142C
-#define HUAWEI_PRODUCT_E142D 0x142D
-#define HUAWEI_PRODUCT_E142E 0x142E
-#define HUAWEI_PRODUCT_E142F 0x142F
-#define HUAWEI_PRODUCT_E1430 0x1430
-#define HUAWEI_PRODUCT_E1431 0x1431
-#define HUAWEI_PRODUCT_E1432 0x1432
-#define HUAWEI_PRODUCT_E1433 0x1433
-#define HUAWEI_PRODUCT_E1434 0x1434
-#define HUAWEI_PRODUCT_E1435 0x1435
-#define HUAWEI_PRODUCT_E1436 0x1436
-#define HUAWEI_PRODUCT_E1437 0x1437
-#define HUAWEI_PRODUCT_E1438 0x1438
-#define HUAWEI_PRODUCT_E1439 0x1439
-#define HUAWEI_PRODUCT_E143A 0x143A
-#define HUAWEI_PRODUCT_E143B 0x143B
-#define HUAWEI_PRODUCT_E143C 0x143C
-#define HUAWEI_PRODUCT_E143D 0x143D
-#define HUAWEI_PRODUCT_E143E 0x143E
-#define HUAWEI_PRODUCT_E143F 0x143F
#define HUAWEI_PRODUCT_K4505 0x1464
#define HUAWEI_PRODUCT_K3765 0x1465
-#define HUAWEI_PRODUCT_E14AC 0x14AC
-#define HUAWEI_PRODUCT_K3806 0x14AE
#define HUAWEI_PRODUCT_K4605 0x14C6
-#define HUAWEI_PRODUCT_K5005 0x14C8
-#define HUAWEI_PRODUCT_K3770 0x14C9
-#define HUAWEI_PRODUCT_K3771 0x14CA
-#define HUAWEI_PRODUCT_K4510 0x14CB
-#define HUAWEI_PRODUCT_K4511 0x14CC
-#define HUAWEI_PRODUCT_ETS1220 0x1803
-#define HUAWEI_PRODUCT_E353 0x1506
-#define HUAWEI_PRODUCT_E173S 0x1C05
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
@@ -615,104 +539,123 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1402, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1404, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1407, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1413, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1414, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1415, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1416, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1420, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1421, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1422, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1423, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1424, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1425, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1426, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1427, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1428, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1429, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1430, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1431, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1432, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1433, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1434, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1435, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1436, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1437, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1438, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1439, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x33) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x02) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x03) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x10) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x12) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x13) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x01) }, /* E398 3G Modem */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x02) }, /* E398 3G PC UI Interface */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x03) }, /* E398 3G Application Interface */
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+
+
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) },
@@ -1147,6 +1090,10 @@ static const struct usb_device_id option_ids[] = {
.driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) },
+
{ USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) },
{ USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) },
{ USB_DEVICE(ALINK_VENDOR_ID, DLINK_PRODUCT_DWM_652_U5) }, /* Yes, ALINK_VENDOR_ID */
@@ -1297,8 +1244,8 @@ static struct usb_serial_driver option_1port_device = {
.tiocmset = usb_wwan_tiocmset,
.ioctl = usb_wwan_ioctl,
.attach = usb_wwan_startup,
- .disconnect = usb_wwan_disconnect,
.release = option_release,
+ .port_remove = usb_wwan_port_remove,
.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
@@ -1414,8 +1361,6 @@ static void option_release(struct usb_serial *serial)
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct option_private *priv = intfdata->private;
- usb_wwan_release(serial);
-
kfree(priv);
kfree(intfdata);
}
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 8d103019d6aa..bfd50779f0c9 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -199,43 +199,49 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
/* default to enabling interface */
altsetting = 0;
- switch (ifnum) {
- /* Composite mode; don't bind to the QMI/net interface as that
- * gets handled by other drivers.
- */
+ /* Composite mode; don't bind to the QMI/net interface as that
+ * gets handled by other drivers.
+ */
+
+ if (is_gobi1k) {
/* Gobi 1K USB layout:
* 0: serial port (doesn't respond)
* 1: serial port (doesn't respond)
* 2: AT-capable modem port
* 3: QMI/net
- *
- * Gobi 2K+ USB layout:
+ */
+ if (ifnum == 2)
+ dev_dbg(dev, "Modem port found\n");
+ else
+ altsetting = -1;
+ } else {
+ /* Gobi 2K+ USB layout:
* 0: QMI/net
* 1: DM/DIAG (use libqcdm from ModemManager for communication)
* 2: AT-capable modem port
* 3: NMEA
*/
-
- case 1:
- if (is_gobi1k)
+ switch (ifnum) {
+ case 0:
+ /* Don't claim the QMI/net interface */
altsetting = -1;
- else
+ break;
+ case 1:
dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
- break;
- case 2:
- dev_dbg(dev, "Modem port found\n");
- break;
- case 3:
- if (is_gobi1k)
- altsetting = -1;
- else
+ break;
+ case 2:
+ dev_dbg(dev, "Modem port found\n");
+ break;
+ case 3:
/*
* NMEA (serial line 9600 8N1)
* # echo "\$GPS_START" > /dev/ttyUSBx
* # echo "\$GPS_STOP" > /dev/ttyUSBx
*/
dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
+ break;
+ }
}
done:
@@ -262,8 +268,7 @@ static void qc_release(struct usb_serial *serial)
{
struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
- /* Call usb_wwan release & free the private data allocated in qcprobe */
- usb_wwan_release(serial);
+ /* Free the private data allocated in qcprobe */
usb_set_serial_data(serial, NULL);
kfree(priv);
}
@@ -283,8 +288,8 @@ static struct usb_serial_driver qcdevice = {
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.attach = usb_wwan_startup,
- .disconnect = usb_wwan_disconnect,
.release = qc_release,
+ .port_remove = usb_wwan_port_remove,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index c47b6ec03063..1f034d2397c6 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -9,8 +9,7 @@ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
extern void usb_wwan_close(struct usb_serial_port *port);
extern int usb_wwan_startup(struct usb_serial *serial);
-extern void usb_wwan_disconnect(struct usb_serial *serial);
-extern void usb_wwan_release(struct usb_serial *serial);
+extern int usb_wwan_port_remove(struct usb_serial_port *port);
extern int usb_wwan_write_room(struct tty_struct *tty);
extern void usb_wwan_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index f35971dff4a5..6855d5ed0331 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -565,62 +565,52 @@ bail_out_error:
}
EXPORT_SYMBOL(usb_wwan_startup);
-static void stop_read_write_urbs(struct usb_serial *serial)
+int usb_wwan_port_remove(struct usb_serial_port *port)
{
- int i, j;
- struct usb_serial_port *port;
+ int i;
struct usb_wwan_port_private *portdata;
- /* Stop reading/writing urbs */
- for (i = 0; i < serial->num_ports; ++i) {
- port = serial->port[i];
- portdata = usb_get_serial_port_data(port);
- for (j = 0; j < N_IN_URB; j++)
- usb_kill_urb(portdata->in_urbs[j]);
- for (j = 0; j < N_OUT_URB; j++)
- usb_kill_urb(portdata->out_urbs[j]);
+ portdata = usb_get_serial_port_data(port);
+ usb_set_serial_port_data(port, NULL);
+
+ /* Stop reading/writing urbs and free them */
+ for (i = 0; i < N_IN_URB; i++) {
+ usb_kill_urb(portdata->in_urbs[i]);
+ usb_free_urb(portdata->in_urbs[i]);
+ free_page((unsigned long)portdata->in_buffer[i]);
+ }
+ for (i = 0; i < N_OUT_URB; i++) {
+ usb_kill_urb(portdata->out_urbs[i]);
+ usb_free_urb(portdata->out_urbs[i]);
+ kfree(portdata->out_buffer[i]);
}
-}
-void usb_wwan_disconnect(struct usb_serial *serial)
-{
- stop_read_write_urbs(serial);
+ /* Now free port private data */
+ kfree(portdata);
+ return 0;
}
-EXPORT_SYMBOL(usb_wwan_disconnect);
+EXPORT_SYMBOL(usb_wwan_port_remove);
-void usb_wwan_release(struct usb_serial *serial)
+#ifdef CONFIG_PM
+static void stop_read_write_urbs(struct usb_serial *serial)
{
int i, j;
struct usb_serial_port *port;
struct usb_wwan_port_private *portdata;
- /* Now free them */
+ /* Stop reading/writing urbs */
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
-
- for (j = 0; j < N_IN_URB; j++) {
- usb_free_urb(portdata->in_urbs[j]);
- free_page((unsigned long)
- portdata->in_buffer[j]);
- portdata->in_urbs[j] = NULL;
- }
- for (j = 0; j < N_OUT_URB; j++) {
- usb_free_urb(portdata->out_urbs[j]);
- kfree(portdata->out_buffer[j]);
- portdata->out_urbs[j] = NULL;
- }
- }
-
- /* Now free per port private data */
- for (i = 0; i < serial->num_ports; i++) {
- port = serial->port[i];
- kfree(usb_get_serial_port_data(port));
+ if (!portdata)
+ continue;
+ for (j = 0; j < N_IN_URB; j++)
+ usb_kill_urb(portdata->in_urbs[j]);
+ for (j = 0; j < N_OUT_URB; j++)
+ usb_kill_urb(portdata->out_urbs[j]);
}
}
-EXPORT_SYMBOL(usb_wwan_release);
-#ifdef CONFIG_PM
int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
struct usb_wwan_intf_private *intfdata = serial->private;
@@ -712,7 +702,7 @@ int usb_wwan_resume(struct usb_serial *serial)
/* skip closed ports */
spin_lock_irq(&intfdata->susp_lock);
- if (!portdata->opened) {
+ if (!portdata || !portdata->opened) {
spin_unlock_irq(&intfdata->susp_lock);
continue;
}
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
new file mode 100644
index 000000000000..7cd5dec0abd1
--- /dev/null
+++ b/drivers/vfio/Kconfig
@@ -0,0 +1,16 @@
+config VFIO_IOMMU_TYPE1
+ tristate
+ depends on VFIO
+ default n
+
+menuconfig VFIO
+ tristate "VFIO Non-Privileged userspace driver framework"
+ depends on IOMMU_API
+ select VFIO_IOMMU_TYPE1 if X86
+ help
+ VFIO provides a framework for secure userspace device drivers.
+ See Documentation/vfio.txt for more details.
+
+ If you don't know what to do here, say N.
+
+source "drivers/vfio/pci/Kconfig"
diff --git a/drivers/vfio/Makefile b/drivers/vfio/Makefile
new file mode 100644
index 000000000000..2398d4a0e38b
--- /dev/null
+++ b/drivers/vfio/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_VFIO) += vfio.o
+obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
+obj-$(CONFIG_VFIO_PCI) += pci/
diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig
new file mode 100644
index 000000000000..5980758563eb
--- /dev/null
+++ b/drivers/vfio/pci/Kconfig
@@ -0,0 +1,8 @@
+config VFIO_PCI
+ tristate "VFIO support for PCI devices"
+ depends on VFIO && PCI && EVENTFD
+ help
+ Support for the PCI VFIO bus driver. This is required to make
+ use of PCI drivers using the VFIO framework.
+
+ If you don't know what to do here, say N.
diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile
new file mode 100644
index 000000000000..131079255fd9
--- /dev/null
+++ b/drivers/vfio/pci/Makefile
@@ -0,0 +1,4 @@
+
+vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
+
+obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
new file mode 100644
index 000000000000..6968b7232232
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/eventfd.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "VFIO PCI - User Level meta-driver"
+
+static bool nointxmask;
+module_param_named(nointxmask, nointxmask, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(nointxmask,
+ "Disable support for PCI 2.3 style INTx masking. If this resolves problems for specific devices, report lspci -vvvxxx to linux-pci@vger.kernel.org so the device can be fixed automatically via the broken_intx_masking flag.");
+
+static int vfio_pci_enable(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+ u16 cmd;
+ u8 msix_pos;
+
+ vdev->reset_works = (pci_reset_function(pdev) == 0);
+ pci_save_state(pdev);
+ vdev->pci_saved_state = pci_store_saved_state(pdev);
+ if (!vdev->pci_saved_state)
+ pr_debug("%s: Couldn't store %s saved state\n",
+ __func__, dev_name(&pdev->dev));
+
+ ret = vfio_config_init(vdev);
+ if (ret)
+ goto out;
+
+ if (likely(!nointxmask))
+ vdev->pci_2_3 = pci_intx_mask_supported(pdev);
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (vdev->pci_2_3 && (cmd & PCI_COMMAND_INTX_DISABLE)) {
+ cmd &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+ }
+
+ msix_pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
+ if (msix_pos) {
+ u16 flags;
+ u32 table;
+
+ pci_read_config_word(pdev, msix_pos + PCI_MSIX_FLAGS, &flags);
+ pci_read_config_dword(pdev, msix_pos + PCI_MSIX_TABLE, &table);
+
+ vdev->msix_bar = table & PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix_offset = table & ~PCI_MSIX_FLAGS_BIRMASK;
+ vdev->msix_size = ((flags & PCI_MSIX_FLAGS_QSIZE) + 1) * 16;
+ } else
+ vdev->msix_bar = 0xFF;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out;
+
+ return ret;
+
+out:
+ kfree(vdev->pci_saved_state);
+ vdev->pci_saved_state = NULL;
+ vfio_config_free(vdev);
+ return ret;
+}
+
+static void vfio_pci_disable(struct vfio_pci_device *vdev)
+{
+ int bar;
+
+ pci_disable_device(vdev->pdev);
+
+ vfio_pci_set_irqs_ioctl(vdev, VFIO_IRQ_SET_DATA_NONE |
+ VFIO_IRQ_SET_ACTION_TRIGGER,
+ vdev->irq_type, 0, 0, NULL);
+
+ vdev->virq_disabled = false;
+
+ vfio_config_free(vdev);
+
+ pci_reset_function(vdev->pdev);
+
+ if (pci_load_and_free_saved_state(vdev->pdev,
+ &vdev->pci_saved_state) == 0)
+ pci_restore_state(vdev->pdev);
+ else
+ pr_info("%s: Couldn't reload %s saved state\n",
+ __func__, dev_name(&vdev->pdev->dev));
+
+ for (bar = PCI_STD_RESOURCES; bar <= PCI_STD_RESOURCE_END; bar++) {
+ if (!vdev->barmap[bar])
+ continue;
+ pci_iounmap(vdev->pdev, vdev->barmap[bar]);
+ pci_release_selected_regions(vdev->pdev, 1 << bar);
+ vdev->barmap[bar] = NULL;
+ }
+}
+
+static void vfio_pci_release(void *device_data)
+{
+ struct vfio_pci_device *vdev = device_data;
+
+ if (atomic_dec_and_test(&vdev->refcnt))
+ vfio_pci_disable(vdev);
+
+ module_put(THIS_MODULE);
+}
+
+static int vfio_pci_open(void *device_data)
+{
+ struct vfio_pci_device *vdev = device_data;
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ if (atomic_inc_return(&vdev->refcnt) == 1) {
+ int ret = vfio_pci_enable(vdev);
+ if (ret) {
+ module_put(THIS_MODULE);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int vfio_pci_get_irq_count(struct vfio_pci_device *vdev, int irq_type)
+{
+ if (irq_type == VFIO_PCI_INTX_IRQ_INDEX) {
+ u8 pin;
+ pci_read_config_byte(vdev->pdev, PCI_INTERRUPT_PIN, &pin);
+ if (pin)
+ return 1;
+
+ } else if (irq_type == VFIO_PCI_MSI_IRQ_INDEX) {
+ u8 pos;
+ u16 flags;
+
+ pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSI);
+ if (pos) {
+ pci_read_config_word(vdev->pdev,
+ pos + PCI_MSI_FLAGS, &flags);
+
+ return 1 << (flags & PCI_MSI_FLAGS_QMASK);
+ }
+ } else if (irq_type == VFIO_PCI_MSIX_IRQ_INDEX) {
+ u8 pos;
+ u16 flags;
+
+ pos = pci_find_capability(vdev->pdev, PCI_CAP_ID_MSIX);
+ if (pos) {
+ pci_read_config_word(vdev->pdev,
+ pos + PCI_MSIX_FLAGS, &flags);
+
+ return (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
+ }
+ }
+
+ return 0;
+}
+
+static long vfio_pci_ioctl(void *device_data,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_pci_device *vdev = device_data;
+ unsigned long minsz;
+
+ if (cmd == VFIO_DEVICE_GET_INFO) {
+ struct vfio_device_info info;
+
+ minsz = offsetofend(struct vfio_device_info, num_irqs);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = VFIO_DEVICE_FLAGS_PCI;
+
+ if (vdev->reset_works)
+ info.flags |= VFIO_DEVICE_FLAGS_RESET;
+
+ info.num_regions = VFIO_PCI_NUM_REGIONS;
+ info.num_irqs = VFIO_PCI_NUM_IRQS;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
+ struct pci_dev *pdev = vdev->pdev;
+ struct vfio_region_info info;
+
+ minsz = offsetofend(struct vfio_region_info, offset);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ switch (info.index) {
+ case VFIO_PCI_CONFIG_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = pdev->cfg_size;
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+ case VFIO_PCI_BAR0_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.size = pci_resource_len(pdev, info.index);
+ if (!info.size) {
+ info.flags = 0;
+ break;
+ }
+
+ info.flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ if (pci_resource_flags(pdev, info.index) &
+ IORESOURCE_MEM && info.size >= PAGE_SIZE)
+ info.flags |= VFIO_REGION_INFO_FLAG_MMAP;
+ break;
+ case VFIO_PCI_ROM_REGION_INDEX:
+ {
+ void __iomem *io;
+ size_t size;
+
+ info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
+ info.flags = 0;
+
+ /* Report the BAR size, not the ROM size */
+ info.size = pci_resource_len(pdev, info.index);
+ if (!info.size)
+ break;
+
+ /* Is it really there? */
+ io = pci_map_rom(pdev, &size);
+ if (!io || !size) {
+ info.size = 0;
+ break;
+ }
+ pci_unmap_rom(pdev, io);
+
+ info.flags = VFIO_REGION_INFO_FLAG_READ;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
+ struct vfio_irq_info info;
+
+ minsz = offsetofend(struct vfio_irq_info, count);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz || info.index >= VFIO_PCI_NUM_IRQS)
+ return -EINVAL;
+
+ info.flags = VFIO_IRQ_INFO_EVENTFD;
+
+ info.count = vfio_pci_get_irq_count(vdev, info.index);
+
+ if (info.index == VFIO_PCI_INTX_IRQ_INDEX)
+ info.flags |= (VFIO_IRQ_INFO_MASKABLE |
+ VFIO_IRQ_INFO_AUTOMASKED);
+ else
+ info.flags |= VFIO_IRQ_INFO_NORESIZE;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_DEVICE_SET_IRQS) {
+ struct vfio_irq_set hdr;
+ u8 *data = NULL;
+ int ret = 0;
+
+ minsz = offsetofend(struct vfio_irq_set, count);
+
+ if (copy_from_user(&hdr, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
+ VFIO_IRQ_SET_ACTION_TYPE_MASK))
+ return -EINVAL;
+
+ if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
+ size_t size;
+
+ if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
+ size = sizeof(uint8_t);
+ else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
+ size = sizeof(int32_t);
+ else
+ return -EINVAL;
+
+ if (hdr.argsz - minsz < hdr.count * size ||
+ hdr.count > vfio_pci_get_irq_count(vdev, hdr.index))
+ return -EINVAL;
+
+ data = kmalloc(hdr.count * size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, (void __user *)(arg + minsz),
+ hdr.count * size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ }
+
+ mutex_lock(&vdev->igate);
+
+ ret = vfio_pci_set_irqs_ioctl(vdev, hdr.flags, hdr.index,
+ hdr.start, hdr.count, data);
+
+ mutex_unlock(&vdev->igate);
+ kfree(data);
+
+ return ret;
+
+ } else if (cmd == VFIO_DEVICE_RESET)
+ return vdev->reset_works ?
+ pci_reset_function(vdev->pdev) : -EINVAL;
+
+ return -ENOTTY;
+}
+
+static ssize_t vfio_pci_read(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+
+ if (index >= VFIO_PCI_NUM_REGIONS)
+ return -EINVAL;
+
+ if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+ return vfio_pci_config_readwrite(vdev, buf, count, ppos, false);
+ else if (index == VFIO_PCI_ROM_REGION_INDEX)
+ return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+ return vfio_pci_io_readwrite(vdev, buf, count, ppos, false);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM)
+ return vfio_pci_mem_readwrite(vdev, buf, count, ppos, false);
+
+ return -EINVAL;
+}
+
+static ssize_t vfio_pci_write(void *device_data, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned int index = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+
+ if (index >= VFIO_PCI_NUM_REGIONS)
+ return -EINVAL;
+
+ if (index == VFIO_PCI_CONFIG_REGION_INDEX)
+ return vfio_pci_config_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ else if (index == VFIO_PCI_ROM_REGION_INDEX)
+ return -EINVAL;
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_IO)
+ return vfio_pci_io_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ else if (pci_resource_flags(pdev, index) & IORESOURCE_MEM) {
+ return vfio_pci_mem_readwrite(vdev, (char __user *)buf,
+ count, ppos, true);
+ }
+
+ return -EINVAL;
+}
+
+static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
+{
+ struct vfio_pci_device *vdev = device_data;
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned int index;
+ u64 phys_len, req_len, pgoff, req_start, phys;
+ int ret;
+
+ index = vma->vm_pgoff >> (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT);
+
+ if (vma->vm_end < vma->vm_start)
+ return -EINVAL;
+ if ((vma->vm_flags & VM_SHARED) == 0)
+ return -EINVAL;
+ if (index >= VFIO_PCI_ROM_REGION_INDEX)
+ return -EINVAL;
+ if (!(pci_resource_flags(pdev, index) & IORESOURCE_MEM))
+ return -EINVAL;
+
+ phys_len = pci_resource_len(pdev, index);
+ req_len = vma->vm_end - vma->vm_start;
+ pgoff = vma->vm_pgoff &
+ ((1U << (VFIO_PCI_OFFSET_SHIFT - PAGE_SHIFT)) - 1);
+ req_start = pgoff << PAGE_SHIFT;
+
+ if (phys_len < PAGE_SIZE || req_start + req_len > phys_len)
+ return -EINVAL;
+
+ if (index == vdev->msix_bar) {
+ /*
+ * Disallow mmaps overlapping the MSI-X table; users don't
+ * get to touch this directly. We could find somewhere
+ * else to map the overlap, but page granularity is only
+ * a recommendation, not a requirement, so the user needs
+ * to know which bits are real. Requiring them to mmap
+ * around the table makes that clear.
+ */
+
+ /* If neither entirely above nor below, then it overlaps */
+ if (!(req_start >= vdev->msix_offset + vdev->msix_size ||
+ req_start + req_len <= vdev->msix_offset))
+ return -EINVAL;
+ }
+
+ /*
+ * Even though we don't make use of the barmap for the mmap,
+ * we need to request the region and the barmap tracks that.
+ */
+ if (!vdev->barmap[index]) {
+ ret = pci_request_selected_regions(pdev,
+ 1 << index, "vfio-pci");
+ if (ret)
+ return ret;
+
+ vdev->barmap[index] = pci_iomap(pdev, index, 0);
+ }
+
+ vma->vm_private_data = vdev;
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ phys = (pci_resource_start(pdev, index) >> PAGE_SHIFT) + pgoff;
+
+ return remap_pfn_range(vma, vma->vm_start, phys,
+ req_len, vma->vm_page_prot);
+}
+
+static const struct vfio_device_ops vfio_pci_ops = {
+ .name = "vfio-pci",
+ .open = vfio_pci_open,
+ .release = vfio_pci_release,
+ .ioctl = vfio_pci_ioctl,
+ .read = vfio_pci_read,
+ .write = vfio_pci_write,
+ .mmap = vfio_pci_mmap,
+};
+
+static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ u8 type;
+ struct vfio_pci_device *vdev;
+ struct iommu_group *group;
+ int ret;
+
+ pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type);
+ if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL)
+ return -EINVAL;
+
+ group = iommu_group_get(&pdev->dev);
+ if (!group)
+ return -EINVAL;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ iommu_group_put(group);
+ return -ENOMEM;
+ }
+
+ vdev->pdev = pdev;
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ mutex_init(&vdev->igate);
+ spin_lock_init(&vdev->irqlock);
+ atomic_set(&vdev->refcnt, 0);
+
+ ret = vfio_add_group_dev(&pdev->dev, &vfio_pci_ops, vdev);
+ if (ret) {
+ iommu_group_put(group);
+ kfree(vdev);
+ }
+
+ return ret;
+}
+
+static void vfio_pci_remove(struct pci_dev *pdev)
+{
+ struct vfio_pci_device *vdev;
+
+ vdev = vfio_del_group_dev(&pdev->dev);
+ if (!vdev)
+ return;
+
+ iommu_group_put(pdev->dev.iommu_group);
+ kfree(vdev);
+}
+
+static struct pci_driver vfio_pci_driver = {
+ .name = "vfio-pci",
+ .id_table = NULL, /* only dynamic ids */
+ .probe = vfio_pci_probe,
+ .remove = vfio_pci_remove,
+};
+
+static void __exit vfio_pci_cleanup(void)
+{
+ pci_unregister_driver(&vfio_pci_driver);
+ vfio_pci_virqfd_exit();
+ vfio_pci_uninit_perm_bits();
+}
+
+static int __init vfio_pci_init(void)
+{
+ int ret;
+
+ /* Allocate shared config space permision data used by all devices */
+ ret = vfio_pci_init_perm_bits();
+ if (ret)
+ return ret;
+
+ /* Start the virqfd cleanup handler */
+ ret = vfio_pci_virqfd_init();
+ if (ret)
+ goto out_virqfd;
+
+ /* Register and scan for devices */
+ ret = pci_register_driver(&vfio_pci_driver);
+ if (ret)
+ goto out_driver;
+
+ return 0;
+
+out_virqfd:
+ vfio_pci_virqfd_exit();
+out_driver:
+ vfio_pci_uninit_perm_bits();
+ return ret;
+}
+
+module_init(vfio_pci_init);
+module_exit(vfio_pci_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
new file mode 100644
index 000000000000..8b8f7d11e102
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -0,0 +1,1540 @@
+/*
+ * VFIO PCI config space virtualization
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+/*
+ * This code handles reading and writing of PCI configuration registers.
+ * This is hairy because we want to allow a lot of flexibility to the
+ * user driver, but cannot trust it with all of the config fields.
+ * Tables determine which fields can be read and written, as well as
+ * which fields are 'virtualized' - special actions and translations to
+ * make it appear to the user that he has control, when in fact things
+ * must be negotiated with the underlying OS.
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+
+#include "vfio_pci_private.h"
+
+#define PCI_CFG_SPACE_SIZE 256
+
+/* Useful "pseudo" capabilities */
+#define PCI_CAP_ID_BASIC 0
+#define PCI_CAP_ID_INVALID 0xFF
+
+#define is_bar(offset) \
+ ((offset >= PCI_BASE_ADDRESS_0 && offset < PCI_BASE_ADDRESS_5 + 4) || \
+ (offset >= PCI_ROM_ADDRESS && offset < PCI_ROM_ADDRESS + 4))
+
+/*
+ * Lengths of PCI Config Capabilities
+ * 0: Removed from the user visible capability list
+ * FF: Variable length
+ */
+static u8 pci_cap_length[] = {
+ [PCI_CAP_ID_BASIC] = PCI_STD_HEADER_SIZEOF, /* pci config header */
+ [PCI_CAP_ID_PM] = PCI_PM_SIZEOF,
+ [PCI_CAP_ID_AGP] = PCI_AGP_SIZEOF,
+ [PCI_CAP_ID_VPD] = PCI_CAP_VPD_SIZEOF,
+ [PCI_CAP_ID_SLOTID] = 0, /* bridge - don't care */
+ [PCI_CAP_ID_MSI] = 0xFF, /* 10, 14, 20, or 24 */
+ [PCI_CAP_ID_CHSWP] = 0, /* cpci - not yet */
+ [PCI_CAP_ID_PCIX] = 0xFF, /* 8 or 24 */
+ [PCI_CAP_ID_HT] = 0xFF, /* hypertransport */
+ [PCI_CAP_ID_VNDR] = 0xFF, /* variable */
+ [PCI_CAP_ID_DBG] = 0, /* debug - don't care */
+ [PCI_CAP_ID_CCRC] = 0, /* cpci - not yet */
+ [PCI_CAP_ID_SHPC] = 0, /* hotswap - not yet */
+ [PCI_CAP_ID_SSVID] = 0, /* bridge - don't care */
+ [PCI_CAP_ID_AGP3] = 0, /* AGP8x - not yet */
+ [PCI_CAP_ID_SECDEV] = 0, /* secure device not yet */
+ [PCI_CAP_ID_EXP] = 0xFF, /* 20 or 44 */
+ [PCI_CAP_ID_MSIX] = PCI_CAP_MSIX_SIZEOF,
+ [PCI_CAP_ID_SATA] = 0xFF,
+ [PCI_CAP_ID_AF] = PCI_CAP_AF_SIZEOF,
+};
+
+/*
+ * Lengths of PCIe/PCI-X Extended Config Capabilities
+ * 0: Removed or masked from the user visible capabilty list
+ * FF: Variable length
+ */
+static u16 pci_ext_cap_length[] = {
+ [PCI_EXT_CAP_ID_ERR] = PCI_ERR_ROOT_COMMAND,
+ [PCI_EXT_CAP_ID_VC] = 0xFF,
+ [PCI_EXT_CAP_ID_DSN] = PCI_EXT_CAP_DSN_SIZEOF,
+ [PCI_EXT_CAP_ID_PWR] = PCI_EXT_CAP_PWR_SIZEOF,
+ [PCI_EXT_CAP_ID_RCLD] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_RCILC] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_RCEC] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_MFVC] = 0xFF,
+ [PCI_EXT_CAP_ID_VC9] = 0xFF, /* same as CAP_ID_VC */
+ [PCI_EXT_CAP_ID_RCRB] = 0, /* root only - don't care */
+ [PCI_EXT_CAP_ID_VNDR] = 0xFF,
+ [PCI_EXT_CAP_ID_CAC] = 0, /* obsolete */
+ [PCI_EXT_CAP_ID_ACS] = 0xFF,
+ [PCI_EXT_CAP_ID_ARI] = PCI_EXT_CAP_ARI_SIZEOF,
+ [PCI_EXT_CAP_ID_ATS] = PCI_EXT_CAP_ATS_SIZEOF,
+ [PCI_EXT_CAP_ID_SRIOV] = PCI_EXT_CAP_SRIOV_SIZEOF,
+ [PCI_EXT_CAP_ID_MRIOV] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_MCAST] = PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF,
+ [PCI_EXT_CAP_ID_PRI] = PCI_EXT_CAP_PRI_SIZEOF,
+ [PCI_EXT_CAP_ID_AMD_XXX] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_REBAR] = 0xFF,
+ [PCI_EXT_CAP_ID_DPA] = 0xFF,
+ [PCI_EXT_CAP_ID_TPH] = 0xFF,
+ [PCI_EXT_CAP_ID_LTR] = PCI_EXT_CAP_LTR_SIZEOF,
+ [PCI_EXT_CAP_ID_SECPCI] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_PMUX] = 0, /* not yet */
+ [PCI_EXT_CAP_ID_PASID] = 0, /* not yet */
+};
+
+/*
+ * Read/Write Permission Bits - one bit for each bit in capability
+ * Any field can be read if it exists, but what is read depends on
+ * whether the field is 'virtualized', or just pass thru to the
+ * hardware. Any virtualized field is also virtualized for writes.
+ * Writes are only permitted if they have a 1 bit here.
+ */
+struct perm_bits {
+ u8 *virt; /* read/write virtual data, not hw */
+ u8 *write; /* writeable bits */
+ int (*readfn)(struct vfio_pci_device *vdev, int pos, int count,
+ struct perm_bits *perm, int offset, __le32 *val);
+ int (*writefn)(struct vfio_pci_device *vdev, int pos, int count,
+ struct perm_bits *perm, int offset, __le32 val);
+};
+
+#define NO_VIRT 0
+#define ALL_VIRT 0xFFFFFFFFU
+#define NO_WRITE 0
+#define ALL_WRITE 0xFFFFFFFFU
+
+static int vfio_user_config_read(struct pci_dev *pdev, int offset,
+ __le32 *val, int count)
+{
+ int ret = -EINVAL;
+ u32 tmp_val = 0;
+
+ switch (count) {
+ case 1:
+ {
+ u8 tmp;
+ ret = pci_user_read_config_byte(pdev, offset, &tmp);
+ tmp_val = tmp;
+ break;
+ }
+ case 2:
+ {
+ u16 tmp;
+ ret = pci_user_read_config_word(pdev, offset, &tmp);
+ tmp_val = tmp;
+ break;
+ }
+ case 4:
+ ret = pci_user_read_config_dword(pdev, offset, &tmp_val);
+ break;
+ }
+
+ *val = cpu_to_le32(tmp_val);
+
+ return pcibios_err_to_errno(ret);
+}
+
+static int vfio_user_config_write(struct pci_dev *pdev, int offset,
+ __le32 val, int count)
+{
+ int ret = -EINVAL;
+ u32 tmp_val = le32_to_cpu(val);
+
+ switch (count) {
+ case 1:
+ ret = pci_user_write_config_byte(pdev, offset, tmp_val);
+ break;
+ case 2:
+ ret = pci_user_write_config_word(pdev, offset, tmp_val);
+ break;
+ case 4:
+ ret = pci_user_write_config_dword(pdev, offset, tmp_val);
+ break;
+ }
+
+ return pcibios_err_to_errno(ret);
+}
+
+static int vfio_default_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ __le32 virt = 0;
+
+ memcpy(val, vdev->vconfig + pos, count);
+
+ memcpy(&virt, perm->virt + offset, count);
+
+ /* Any non-virtualized bits? */
+ if (cpu_to_le32(~0U >> (32 - (count * 8))) != virt) {
+ struct pci_dev *pdev = vdev->pdev;
+ __le32 phys_val = 0;
+ int ret;
+
+ ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+ if (ret)
+ return ret;
+
+ *val = (phys_val & ~virt) | (*val & virt);
+ }
+
+ return count;
+}
+
+static int vfio_default_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ __le32 virt = 0, write = 0;
+
+ memcpy(&write, perm->write + offset, count);
+
+ if (!write)
+ return count; /* drop, no writable bits */
+
+ memcpy(&virt, perm->virt + offset, count);
+
+ /* Virtualized and writable bits go to vconfig */
+ if (write & virt) {
+ __le32 virt_val = 0;
+
+ memcpy(&virt_val, vdev->vconfig + pos, count);
+
+ virt_val &= ~(write & virt);
+ virt_val |= (val & (write & virt));
+
+ memcpy(vdev->vconfig + pos, &virt_val, count);
+ }
+
+ /* Non-virtualzed and writable bits go to hardware */
+ if (write & ~virt) {
+ struct pci_dev *pdev = vdev->pdev;
+ __le32 phys_val = 0;
+ int ret;
+
+ ret = vfio_user_config_read(pdev, pos, &phys_val, count);
+ if (ret)
+ return ret;
+
+ phys_val &= ~(write & ~virt);
+ phys_val |= (val & (write & ~virt));
+
+ ret = vfio_user_config_write(pdev, pos, phys_val, count);
+ if (ret)
+ return ret;
+ }
+
+ return count;
+}
+
+/* Allow direct read from hardware, except for capability next pointer */
+static int vfio_direct_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ int ret;
+
+ ret = vfio_user_config_read(vdev->pdev, pos, val, count);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (pos >= PCI_CFG_SPACE_SIZE) { /* Extended cap header mangling */
+ if (offset < 4)
+ memcpy(val, vdev->vconfig + pos, count);
+ } else if (pos >= PCI_STD_HEADER_SIZEOF) { /* Std cap mangling */
+ if (offset == PCI_CAP_LIST_ID && count > 1)
+ memcpy(val, vdev->vconfig + pos,
+ min(PCI_CAP_FLAGS, count));
+ else if (offset == PCI_CAP_LIST_NEXT)
+ memcpy(val, vdev->vconfig + pos, 1);
+ }
+
+ return count;
+}
+
+static int vfio_direct_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ int ret;
+
+ ret = vfio_user_config_write(vdev->pdev, pos, val, count);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+/* Default all regions to read-only, no-virtualization */
+static struct perm_bits cap_perms[PCI_CAP_ID_MAX + 1] = {
+ [0 ... PCI_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+static struct perm_bits ecap_perms[PCI_EXT_CAP_ID_MAX + 1] = {
+ [0 ... PCI_EXT_CAP_ID_MAX] = { .readfn = vfio_direct_config_read }
+};
+
+static void free_perm_bits(struct perm_bits *perm)
+{
+ kfree(perm->virt);
+ kfree(perm->write);
+ perm->virt = NULL;
+ perm->write = NULL;
+}
+
+static int alloc_perm_bits(struct perm_bits *perm, int size)
+{
+ /*
+ * Round up all permission bits to the next dword, this lets us
+ * ignore whether a read/write exceeds the defined capability
+ * structure. We can do this because:
+ * - Standard config space is already dword aligned
+ * - Capabilities are all dword alinged (bits 0:1 of next reserved)
+ * - Express capabilities defined as dword aligned
+ */
+ size = round_up(size, 4);
+
+ /*
+ * Zero state is
+ * - All Readable, None Writeable, None Virtualized
+ */
+ perm->virt = kzalloc(size, GFP_KERNEL);
+ perm->write = kzalloc(size, GFP_KERNEL);
+ if (!perm->virt || !perm->write) {
+ free_perm_bits(perm);
+ return -ENOMEM;
+ }
+
+ perm->readfn = vfio_default_config_read;
+ perm->writefn = vfio_default_config_write;
+
+ return 0;
+}
+
+/*
+ * Helper functions for filling in permission tables
+ */
+static inline void p_setb(struct perm_bits *p, int off, u8 virt, u8 write)
+{
+ p->virt[off] = virt;
+ p->write[off] = write;
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setw(struct perm_bits *p, int off, u16 virt, u16 write)
+{
+ *(__le16 *)(&p->virt[off]) = cpu_to_le16(virt);
+ *(__le16 *)(&p->write[off]) = cpu_to_le16(write);
+}
+
+/* Handle endian-ness - pci and tables are little-endian */
+static inline void p_setd(struct perm_bits *p, int off, u32 virt, u32 write)
+{
+ *(__le32 *)(&p->virt[off]) = cpu_to_le32(virt);
+ *(__le32 *)(&p->write[off]) = cpu_to_le32(write);
+}
+
+/*
+ * Restore the *real* BARs after we detect a FLR or backdoor reset.
+ * (backdoor = some device specific technique that we didn't catch)
+ */
+static void vfio_bar_restore(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u32 *rbar = vdev->rbar;
+ int i;
+
+ if (pdev->is_virtfn)
+ return;
+
+ pr_info("%s: %s reset recovery - restoring bars\n",
+ __func__, dev_name(&pdev->dev));
+
+ for (i = PCI_BASE_ADDRESS_0; i <= PCI_BASE_ADDRESS_5; i += 4, rbar++)
+ pci_user_write_config_dword(pdev, i, *rbar);
+
+ pci_user_write_config_dword(pdev, PCI_ROM_ADDRESS, *rbar);
+}
+
+static __le32 vfio_generate_bar_flags(struct pci_dev *pdev, int bar)
+{
+ unsigned long flags = pci_resource_flags(pdev, bar);
+ u32 val;
+
+ if (flags & IORESOURCE_IO)
+ return cpu_to_le32(PCI_BASE_ADDRESS_SPACE_IO);
+
+ val = PCI_BASE_ADDRESS_SPACE_MEMORY;
+
+ if (flags & IORESOURCE_PREFETCH)
+ val |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ if (flags & IORESOURCE_MEM_64)
+ val |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+ return cpu_to_le32(val);
+}
+
+/*
+ * Pretend we're hardware and tweak the values of the *virtual* PCI BARs
+ * to reflect the hardware capabilities. This implements BAR sizing.
+ */
+static void vfio_bar_fixup(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int i;
+ __le32 *bar;
+ u64 mask;
+
+ bar = (__le32 *)&vdev->vconfig[PCI_BASE_ADDRESS_0];
+
+ for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++, bar++) {
+ if (!pci_resource_start(pdev, i)) {
+ *bar = 0; /* Unmapped by host = unimplemented to user */
+ continue;
+ }
+
+ mask = ~(pci_resource_len(pdev, i) - 1);
+
+ *bar &= cpu_to_le32((u32)mask);
+ *bar |= vfio_generate_bar_flags(pdev, i);
+
+ if (*bar & cpu_to_le32(PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+ bar++;
+ *bar &= cpu_to_le32((u32)(mask >> 32));
+ i++;
+ }
+ }
+
+ bar = (__le32 *)&vdev->vconfig[PCI_ROM_ADDRESS];
+
+ /*
+ * NB. we expose the actual BAR size here, regardless of whether
+ * we can read it. When we report the REGION_INFO for the ROM
+ * we report what PCI tells us is the actual ROM size.
+ */
+ if (pci_resource_start(pdev, PCI_ROM_RESOURCE)) {
+ mask = ~(pci_resource_len(pdev, PCI_ROM_RESOURCE) - 1);
+ mask |= PCI_ROM_ADDRESS_ENABLE;
+ *bar &= cpu_to_le32((u32)mask);
+ } else
+ *bar = 0;
+
+ vdev->bardirty = false;
+}
+
+static int vfio_basic_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ if (is_bar(offset)) /* pos == offset for basic config */
+ vfio_bar_fixup(vdev);
+
+ count = vfio_default_config_read(vdev, pos, count, perm, offset, val);
+
+ /* Mask in virtual memory enable for SR-IOV devices */
+ if (offset == PCI_COMMAND && vdev->pdev->is_virtfn) {
+ u16 cmd = le16_to_cpu(*(__le16 *)&vdev->vconfig[PCI_COMMAND]);
+ u32 tmp_val = le32_to_cpu(*val);
+
+ tmp_val |= cmd & PCI_COMMAND_MEMORY;
+ *val = cpu_to_le32(tmp_val);
+ }
+
+ return count;
+}
+
+static int vfio_basic_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ __le16 *virt_cmd;
+ u16 new_cmd = 0;
+ int ret;
+
+ virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND];
+
+ if (offset == PCI_COMMAND) {
+ bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io;
+ u16 phys_cmd;
+
+ ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd);
+ if (ret)
+ return ret;
+
+ new_cmd = le32_to_cpu(val);
+
+ phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY);
+ virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY);
+ new_mem = !!(new_cmd & PCI_COMMAND_MEMORY);
+
+ phys_io = !!(phys_cmd & PCI_COMMAND_IO);
+ virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO);
+ new_io = !!(new_cmd & PCI_COMMAND_IO);
+
+ /*
+ * If the user is writing mem/io enable (new_mem/io) and we
+ * think it's already enabled (virt_mem/io), but the hardware
+ * shows it disabled (phys_mem/io, then the device has
+ * undergone some kind of backdoor reset and needs to be
+ * restored before we allow it to enable the bars.
+ * SR-IOV devices will trigger this, but we catch them later
+ */
+ if ((new_mem && virt_mem && !phys_mem) ||
+ (new_io && virt_io && !phys_io))
+ vfio_bar_restore(vdev);
+ }
+
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ if (count < 0)
+ return count;
+
+ /*
+ * Save current memory/io enable bits in vconfig to allow for
+ * the test above next time.
+ */
+ if (offset == PCI_COMMAND) {
+ u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+
+ *virt_cmd &= cpu_to_le16(~mask);
+ *virt_cmd |= cpu_to_le16(new_cmd & mask);
+ }
+
+ /* Emulate INTx disable */
+ if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) {
+ bool virt_intx_disable;
+
+ virt_intx_disable = !!(le16_to_cpu(*virt_cmd) &
+ PCI_COMMAND_INTX_DISABLE);
+
+ if (virt_intx_disable && !vdev->virq_disabled) {
+ vdev->virq_disabled = true;
+ vfio_pci_intx_mask(vdev);
+ } else if (!virt_intx_disable && vdev->virq_disabled) {
+ vdev->virq_disabled = false;
+ vfio_pci_intx_unmask(vdev);
+ }
+ }
+
+ if (is_bar(offset))
+ vdev->bardirty = true;
+
+ return count;
+}
+
+/* Permissions for the Basic PCI Header */
+static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, PCI_STD_HEADER_SIZEOF))
+ return -ENOMEM;
+
+ perm->readfn = vfio_basic_config_read;
+ perm->writefn = vfio_basic_config_write;
+
+ /* Virtualized for SR-IOV functions, which just have FFFF */
+ p_setw(perm, PCI_VENDOR_ID, (u16)ALL_VIRT, NO_WRITE);
+ p_setw(perm, PCI_DEVICE_ID, (u16)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Virtualize INTx disable, we use it internally for interrupt
+ * control and can emulate it for non-PCI 2.3 devices.
+ */
+ p_setw(perm, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE, (u16)ALL_WRITE);
+
+ /* Virtualize capability list, we might want to skip/disable */
+ p_setw(perm, PCI_STATUS, PCI_STATUS_CAP_LIST, NO_WRITE);
+
+ /* No harm to write */
+ p_setb(perm, PCI_CACHE_LINE_SIZE, NO_VIRT, (u8)ALL_WRITE);
+ p_setb(perm, PCI_LATENCY_TIMER, NO_VIRT, (u8)ALL_WRITE);
+ p_setb(perm, PCI_BIST, NO_VIRT, (u8)ALL_WRITE);
+
+ /* Virtualize all bars, can't touch the real ones */
+ p_setd(perm, PCI_BASE_ADDRESS_0, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_1, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_2, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_3, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_4, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_BASE_ADDRESS_5, ALL_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_ROM_ADDRESS, ALL_VIRT, ALL_WRITE);
+
+ /* Allow us to adjust capability chain */
+ p_setb(perm, PCI_CAPABILITY_LIST, (u8)ALL_VIRT, NO_WRITE);
+
+ /* Sometimes used by sw, just virtualize */
+ p_setb(perm, PCI_INTERRUPT_LINE, (u8)ALL_VIRT, (u8)ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for the Power Management capability */
+static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM]))
+ return -ENOMEM;
+
+ /*
+ * We always virtualize the next field so we can remove
+ * capabilities from the chain if we want to.
+ */
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Power management is defined *per function*,
+ * so we let the user write this
+ */
+ p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for PCI-X capability */
+static int __init init_pci_cap_pcix_perm(struct perm_bits *perm)
+{
+ /* Alloc 24, but only 8 are used in v0 */
+ if (alloc_perm_bits(perm, PCI_CAP_PCIX_SIZEOF_V2))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ p_setw(perm, PCI_X_CMD, NO_VIRT, (u16)ALL_WRITE);
+ p_setd(perm, PCI_X_ECC_CSR, NO_VIRT, ALL_WRITE);
+ return 0;
+}
+
+/* Permissions for PCI Express capability */
+static int __init init_pci_cap_exp_perm(struct perm_bits *perm)
+{
+ /* Alloc larger of two possible sizes */
+ if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * Allow writes to device control fields (includes FLR!)
+ * but not to devctl_phantom which could confuse IOMMU
+ * or to the ARI bit in devctl2 which is set at probe time
+ */
+ p_setw(perm, PCI_EXP_DEVCTL, NO_VIRT, ~PCI_EXP_DEVCTL_PHANTOM);
+ p_setw(perm, PCI_EXP_DEVCTL2, NO_VIRT, ~PCI_EXP_DEVCTL2_ARI);
+ return 0;
+}
+
+/* Permissions for Advanced Function capability */
+static int __init init_pci_cap_af_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_AF]))
+ return -ENOMEM;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+ p_setb(perm, PCI_AF_CTRL, NO_VIRT, PCI_AF_CTRL_FLR);
+ return 0;
+}
+
+/* Permissions for Advanced Error Reporting extended capability */
+static int __init init_pci_ext_cap_err_perm(struct perm_bits *perm)
+{
+ u32 mask;
+
+ if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_ERR]))
+ return -ENOMEM;
+
+ /*
+ * Virtualize the first dword of all express capabilities
+ * because it includes the next pointer. This lets us later
+ * remove capabilities from the chain if we need to.
+ */
+ p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+ /* Writable bits mask */
+ mask = PCI_ERR_UNC_TRAIN | /* Training */
+ PCI_ERR_UNC_DLP | /* Data Link Protocol */
+ PCI_ERR_UNC_SURPDN | /* Surprise Down */
+ PCI_ERR_UNC_POISON_TLP | /* Poisoned TLP */
+ PCI_ERR_UNC_FCP | /* Flow Control Protocol */
+ PCI_ERR_UNC_COMP_TIME | /* Completion Timeout */
+ PCI_ERR_UNC_COMP_ABORT | /* Completer Abort */
+ PCI_ERR_UNC_UNX_COMP | /* Unexpected Completion */
+ PCI_ERR_UNC_RX_OVER | /* Receiver Overflow */
+ PCI_ERR_UNC_MALF_TLP | /* Malformed TLP */
+ PCI_ERR_UNC_ECRC | /* ECRC Error Status */
+ PCI_ERR_UNC_UNSUP | /* Unsupported Request */
+ PCI_ERR_UNC_ACSV | /* ACS Violation */
+ PCI_ERR_UNC_INTN | /* internal error */
+ PCI_ERR_UNC_MCBTLP | /* MC blocked TLP */
+ PCI_ERR_UNC_ATOMEG | /* Atomic egress blocked */
+ PCI_ERR_UNC_TLPPRE; /* TLP prefix blocked */
+ p_setd(perm, PCI_ERR_UNCOR_STATUS, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_UNCOR_MASK, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_UNCOR_SEVER, NO_VIRT, mask);
+
+ mask = PCI_ERR_COR_RCVR | /* Receiver Error Status */
+ PCI_ERR_COR_BAD_TLP | /* Bad TLP Status */
+ PCI_ERR_COR_BAD_DLLP | /* Bad DLLP Status */
+ PCI_ERR_COR_REP_ROLL | /* REPLAY_NUM Rollover */
+ PCI_ERR_COR_REP_TIMER | /* Replay Timer Timeout */
+ PCI_ERR_COR_ADV_NFAT | /* Advisory Non-Fatal */
+ PCI_ERR_COR_INTERNAL | /* Corrected Internal */
+ PCI_ERR_COR_LOG_OVER; /* Header Log Overflow */
+ p_setd(perm, PCI_ERR_COR_STATUS, NO_VIRT, mask);
+ p_setd(perm, PCI_ERR_COR_MASK, NO_VIRT, mask);
+
+ mask = PCI_ERR_CAP_ECRC_GENE | /* ECRC Generation Enable */
+ PCI_ERR_CAP_ECRC_CHKE; /* ECRC Check Enable */
+ p_setd(perm, PCI_ERR_CAP, NO_VIRT, mask);
+ return 0;
+}
+
+/* Permissions for Power Budgeting extended capability */
+static int __init init_pci_ext_cap_pwr_perm(struct perm_bits *perm)
+{
+ if (alloc_perm_bits(perm, pci_ext_cap_length[PCI_EXT_CAP_ID_PWR]))
+ return -ENOMEM;
+
+ p_setd(perm, 0, ALL_VIRT, NO_WRITE);
+
+ /* Writing the data selector is OK, the info is still read-only */
+ p_setb(perm, PCI_PWR_DATA, NO_VIRT, (u8)ALL_WRITE);
+ return 0;
+}
+
+/*
+ * Initialize the shared permission tables
+ */
+void vfio_pci_uninit_perm_bits(void)
+{
+ free_perm_bits(&cap_perms[PCI_CAP_ID_BASIC]);
+
+ free_perm_bits(&cap_perms[PCI_CAP_ID_PM]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_PCIX]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_EXP]);
+ free_perm_bits(&cap_perms[PCI_CAP_ID_AF]);
+
+ free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+ free_perm_bits(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+}
+
+int __init vfio_pci_init_perm_bits(void)
+{
+ int ret;
+
+ /* Basic config space */
+ ret = init_pci_cap_basic_perm(&cap_perms[PCI_CAP_ID_BASIC]);
+
+ /* Capabilities */
+ ret |= init_pci_cap_pm_perm(&cap_perms[PCI_CAP_ID_PM]);
+ cap_perms[PCI_CAP_ID_VPD].writefn = vfio_direct_config_write;
+ ret |= init_pci_cap_pcix_perm(&cap_perms[PCI_CAP_ID_PCIX]);
+ cap_perms[PCI_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+ ret |= init_pci_cap_exp_perm(&cap_perms[PCI_CAP_ID_EXP]);
+ ret |= init_pci_cap_af_perm(&cap_perms[PCI_CAP_ID_AF]);
+
+ /* Extended capabilities */
+ ret |= init_pci_ext_cap_err_perm(&ecap_perms[PCI_EXT_CAP_ID_ERR]);
+ ret |= init_pci_ext_cap_pwr_perm(&ecap_perms[PCI_EXT_CAP_ID_PWR]);
+ ecap_perms[PCI_EXT_CAP_ID_VNDR].writefn = vfio_direct_config_write;
+
+ if (ret)
+ vfio_pci_uninit_perm_bits();
+
+ return ret;
+}
+
+static int vfio_find_cap_start(struct vfio_pci_device *vdev, int pos)
+{
+ u8 cap;
+ int base = (pos >= PCI_CFG_SPACE_SIZE) ? PCI_CFG_SPACE_SIZE :
+ PCI_STD_HEADER_SIZEOF;
+ base /= 4;
+ pos /= 4;
+
+ cap = vdev->pci_config_map[pos];
+
+ if (cap == PCI_CAP_ID_BASIC)
+ return 0;
+
+ /* XXX Can we have to abutting capabilities of the same type? */
+ while (pos - 1 >= base && vdev->pci_config_map[pos - 1] == cap)
+ pos--;
+
+ return pos * 4;
+}
+
+static int vfio_msi_config_read(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 *val)
+{
+ /* Update max available queue size from msi_qmax */
+ if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+ __le16 *flags;
+ int start;
+
+ start = vfio_find_cap_start(vdev, pos);
+
+ flags = (__le16 *)&vdev->vconfig[start];
+
+ *flags &= cpu_to_le16(~PCI_MSI_FLAGS_QMASK);
+ *flags |= cpu_to_le16(vdev->msi_qmax << 1);
+ }
+
+ return vfio_default_config_read(vdev, pos, count, perm, offset, val);
+}
+
+static int vfio_msi_config_write(struct vfio_pci_device *vdev, int pos,
+ int count, struct perm_bits *perm,
+ int offset, __le32 val)
+{
+ count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
+ if (count < 0)
+ return count;
+
+ /* Fixup and write configured queue size and enable to hardware */
+ if (offset <= PCI_MSI_FLAGS && offset + count >= PCI_MSI_FLAGS) {
+ __le16 *pflags;
+ u16 flags;
+ int start, ret;
+
+ start = vfio_find_cap_start(vdev, pos);
+
+ pflags = (__le16 *)&vdev->vconfig[start + PCI_MSI_FLAGS];
+
+ flags = le16_to_cpu(*pflags);
+
+ /* MSI is enabled via ioctl */
+ if (!is_msi(vdev))
+ flags &= ~PCI_MSI_FLAGS_ENABLE;
+
+ /* Check queue size */
+ if ((flags & PCI_MSI_FLAGS_QSIZE) >> 4 > vdev->msi_qmax) {
+ flags &= ~PCI_MSI_FLAGS_QSIZE;
+ flags |= vdev->msi_qmax << 4;
+ }
+
+ /* Write back to virt and to hardware */
+ *pflags = cpu_to_le16(flags);
+ ret = pci_user_write_config_word(vdev->pdev,
+ start + PCI_MSI_FLAGS,
+ flags);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+ }
+
+ return count;
+}
+
+/*
+ * MSI determination is per-device, so this routine gets used beyond
+ * initialization time. Don't add __init
+ */
+static int init_pci_cap_msi_perm(struct perm_bits *perm, int len, u16 flags)
+{
+ if (alloc_perm_bits(perm, len))
+ return -ENOMEM;
+
+ perm->readfn = vfio_msi_config_read;
+ perm->writefn = vfio_msi_config_write;
+
+ p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
+
+ /*
+ * The upper byte of the control register is reserved,
+ * just setup the lower byte.
+ */
+ p_setb(perm, PCI_MSI_FLAGS, (u8)ALL_VIRT, (u8)ALL_WRITE);
+ p_setd(perm, PCI_MSI_ADDRESS_LO, ALL_VIRT, ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_64BIT) {
+ p_setd(perm, PCI_MSI_ADDRESS_HI, ALL_VIRT, ALL_WRITE);
+ p_setw(perm, PCI_MSI_DATA_64, (u16)ALL_VIRT, (u16)ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ p_setd(perm, PCI_MSI_MASK_64, NO_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_MSI_PENDING_64, NO_VIRT, ALL_WRITE);
+ }
+ } else {
+ p_setw(perm, PCI_MSI_DATA_32, (u16)ALL_VIRT, (u16)ALL_WRITE);
+ if (flags & PCI_MSI_FLAGS_MASKBIT) {
+ p_setd(perm, PCI_MSI_MASK_32, NO_VIRT, ALL_WRITE);
+ p_setd(perm, PCI_MSI_PENDING_32, NO_VIRT, ALL_WRITE);
+ }
+ }
+ return 0;
+}
+
+/* Determine MSI CAP field length; initialize msi_perms on 1st call per vdev */
+static int vfio_msi_cap_len(struct vfio_pci_device *vdev, u8 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int len, ret;
+ u16 flags;
+
+ ret = pci_read_config_word(pdev, pos + PCI_MSI_FLAGS, &flags);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ len = 10; /* Minimum size */
+ if (flags & PCI_MSI_FLAGS_64BIT)
+ len += 4;
+ if (flags & PCI_MSI_FLAGS_MASKBIT)
+ len += 10;
+
+ if (vdev->msi_perm)
+ return len;
+
+ vdev->msi_perm = kmalloc(sizeof(struct perm_bits), GFP_KERNEL);
+ if (!vdev->msi_perm)
+ return -ENOMEM;
+
+ ret = init_pci_cap_msi_perm(vdev->msi_perm, len, flags);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+/* Determine extended capability length for VC (2 & 9) and MFVC */
+static int vfio_vc_cap_len(struct vfio_pci_device *vdev, u16 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u32 tmp;
+ int ret, evcc, phases, vc_arb;
+ int len = PCI_CAP_VC_BASE_SIZEOF;
+
+ ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG1, &tmp);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ evcc = tmp & PCI_VC_REG1_EVCC; /* extended vc count */
+ ret = pci_read_config_dword(pdev, pos + PCI_VC_PORT_REG2, &tmp);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (tmp & PCI_VC_REG2_128_PHASE)
+ phases = 128;
+ else if (tmp & PCI_VC_REG2_64_PHASE)
+ phases = 64;
+ else if (tmp & PCI_VC_REG2_32_PHASE)
+ phases = 32;
+ else
+ phases = 0;
+
+ vc_arb = phases * 4;
+
+ /*
+ * Port arbitration tables are root & switch only;
+ * function arbitration tables are function 0 only.
+ * In either case, we'll never let user write them so
+ * we don't care how big they are
+ */
+ len += (1 + evcc) * PCI_CAP_VC_PER_VC_SIZEOF;
+ if (vc_arb) {
+ len = round_up(len, 16);
+ len += vc_arb / 8;
+ }
+ return len;
+}
+
+static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u16 word;
+ u8 byte;
+ int ret;
+
+ switch (cap) {
+ case PCI_CAP_ID_MSI:
+ return vfio_msi_cap_len(vdev, pos);
+ case PCI_CAP_ID_PCIX:
+ ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (PCI_X_CMD_VERSION(word)) {
+ vdev->extended_caps = true;
+ return PCI_CAP_PCIX_SIZEOF_V2;
+ } else
+ return PCI_CAP_PCIX_SIZEOF_V0;
+ case PCI_CAP_ID_VNDR:
+ /* length follows next field */
+ ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return byte;
+ case PCI_CAP_ID_EXP:
+ /* length based on version */
+ ret = pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &word);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if ((word & PCI_EXP_FLAGS_VERS) == 1)
+ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1;
+ else {
+ vdev->extended_caps = true;
+ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2;
+ }
+ case PCI_CAP_ID_HT:
+ ret = pci_read_config_byte(pdev, pos + 3, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return (byte & HT_3BIT_CAP_MASK) ?
+ HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG;
+ case PCI_CAP_ID_SATA:
+ ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_SATA_REGS_MASK;
+ if (byte == PCI_SATA_REGS_INLINE)
+ return PCI_SATA_SIZEOF_LONG;
+ else
+ return PCI_SATA_SIZEOF_SHORT;
+ default:
+ pr_warn("%s: %s unknown length for pci cap 0x%x@0x%x\n",
+ dev_name(&pdev->dev), __func__, cap, pos);
+ }
+
+ return 0;
+}
+
+static int vfio_ext_cap_len(struct vfio_pci_device *vdev, u16 ecap, u16 epos)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 byte;
+ u32 dword;
+ int ret;
+
+ switch (ecap) {
+ case PCI_EXT_CAP_ID_VNDR:
+ ret = pci_read_config_dword(pdev, epos + PCI_VSEC_HDR, &dword);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ return dword >> PCI_VSEC_HDR_LEN_SHIFT;
+ case PCI_EXT_CAP_ID_VC:
+ case PCI_EXT_CAP_ID_VC9:
+ case PCI_EXT_CAP_ID_MFVC:
+ return vfio_vc_cap_len(vdev, epos);
+ case PCI_EXT_CAP_ID_ACS:
+ ret = pci_read_config_byte(pdev, epos + PCI_ACS_CAP, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if (byte & PCI_ACS_EC) {
+ int bits;
+
+ ret = pci_read_config_byte(pdev,
+ epos + PCI_ACS_EGRESS_BITS,
+ &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ bits = byte ? round_up(byte, 32) : 256;
+ return 8 + (bits / 8);
+ }
+ return 8;
+
+ case PCI_EXT_CAP_ID_REBAR:
+ ret = pci_read_config_byte(pdev, epos + PCI_REBAR_CTRL, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_REBAR_CTRL_NBAR_MASK;
+ byte >>= PCI_REBAR_CTRL_NBAR_SHIFT;
+
+ return 4 + (byte * 8);
+ case PCI_EXT_CAP_ID_DPA:
+ ret = pci_read_config_byte(pdev, epos + PCI_DPA_CAP, &byte);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ byte &= PCI_DPA_CAP_SUBSTATE_MASK;
+ byte = round_up(byte + 1, 4);
+ return PCI_DPA_BASE_SIZEOF + byte;
+ case PCI_EXT_CAP_ID_TPH:
+ ret = pci_read_config_dword(pdev, epos + PCI_TPH_CAP, &dword);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ if ((dword & PCI_TPH_CAP_LOC_MASK) == PCI_TPH_LOC_CAP) {
+ int sts;
+
+ sts = byte & PCI_TPH_CAP_ST_MASK;
+ sts >>= PCI_TPH_CAP_ST_SHIFT;
+ return PCI_TPH_BASE_SIZEOF + round_up(sts * 2, 4);
+ }
+ return PCI_TPH_BASE_SIZEOF;
+ default:
+ pr_warn("%s: %s unknown length for pci ecap 0x%x@0x%x\n",
+ dev_name(&pdev->dev), __func__, ecap, epos);
+ }
+
+ return 0;
+}
+
+static int vfio_fill_vconfig_bytes(struct vfio_pci_device *vdev,
+ int offset, int size)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret = 0;
+
+ /*
+ * We try to read physical config space in the largest chunks
+ * we can, assuming that all of the fields support dword access.
+ * pci_save_state() makes this same assumption and seems to do ok.
+ */
+ while (size) {
+ int filled;
+
+ if (size >= 4 && !(offset % 4)) {
+ __le32 *dwordp = (__le32 *)&vdev->vconfig[offset];
+ u32 dword;
+
+ ret = pci_read_config_dword(pdev, offset, &dword);
+ if (ret)
+ return ret;
+ *dwordp = cpu_to_le32(dword);
+ filled = 4;
+ } else if (size >= 2 && !(offset % 2)) {
+ __le16 *wordp = (__le16 *)&vdev->vconfig[offset];
+ u16 word;
+
+ ret = pci_read_config_word(pdev, offset, &word);
+ if (ret)
+ return ret;
+ *wordp = cpu_to_le16(word);
+ filled = 2;
+ } else {
+ u8 *byte = &vdev->vconfig[offset];
+ ret = pci_read_config_byte(pdev, offset, byte);
+ if (ret)
+ return ret;
+ filled = 1;
+ }
+
+ offset += filled;
+ size -= filled;
+ }
+
+ return ret;
+}
+
+static int vfio_cap_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map = vdev->pci_config_map;
+ u16 status;
+ u8 pos, *prev, cap;
+ int loops, ret, caps = 0;
+
+ /* Any capabilities? */
+ ret = pci_read_config_word(pdev, PCI_STATUS, &status);
+ if (ret)
+ return ret;
+
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0; /* Done */
+
+ ret = pci_read_config_byte(pdev, PCI_CAPABILITY_LIST, &pos);
+ if (ret)
+ return ret;
+
+ /* Mark the previous position in case we want to skip a capability */
+ prev = &vdev->vconfig[PCI_CAPABILITY_LIST];
+
+ /* We can bound our loop, capabilities are dword aligned */
+ loops = (PCI_CFG_SPACE_SIZE - PCI_STD_HEADER_SIZEOF) / PCI_CAP_SIZEOF;
+ while (pos && loops--) {
+ u8 next;
+ int i, len = 0;
+
+ ret = pci_read_config_byte(pdev, pos, &cap);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_byte(pdev,
+ pos + PCI_CAP_LIST_NEXT, &next);
+ if (ret)
+ return ret;
+
+ if (cap <= PCI_CAP_ID_MAX) {
+ len = pci_cap_length[cap];
+ if (len == 0xFF) { /* Variable length */
+ len = vfio_cap_len(vdev, cap, pos);
+ if (len < 0)
+ return len;
+ }
+ }
+
+ if (!len) {
+ pr_info("%s: %s hiding cap 0x%x\n",
+ __func__, dev_name(&pdev->dev), cap);
+ *prev = next;
+ pos = next;
+ continue;
+ }
+
+ /* Sanity check, do we overlap other capabilities? */
+ for (i = 0; i < len; i += 4) {
+ if (likely(map[(pos + i) / 4] == PCI_CAP_ID_INVALID))
+ continue;
+
+ pr_warn("%s: %s pci config conflict @0x%x, was cap 0x%x now cap 0x%x\n",
+ __func__, dev_name(&pdev->dev),
+ pos + i, map[pos + i], cap);
+ }
+
+ memset(map + (pos / 4), cap, len / 4);
+ ret = vfio_fill_vconfig_bytes(vdev, pos, len);
+ if (ret)
+ return ret;
+
+ prev = &vdev->vconfig[pos + PCI_CAP_LIST_NEXT];
+ pos = next;
+ caps++;
+ }
+
+ /* If we didn't fill any capabilities, clear the status flag */
+ if (!caps) {
+ __le16 *vstatus = (__le16 *)&vdev->vconfig[PCI_STATUS];
+ *vstatus &= ~cpu_to_le16(PCI_STATUS_CAP_LIST);
+ }
+
+ return 0;
+}
+
+static int vfio_ecap_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map = vdev->pci_config_map;
+ u16 epos;
+ __le32 *prev = NULL;
+ int loops, ret, ecaps = 0;
+
+ if (!vdev->extended_caps)
+ return 0;
+
+ epos = PCI_CFG_SPACE_SIZE;
+
+ loops = (pdev->cfg_size - PCI_CFG_SPACE_SIZE) / PCI_CAP_SIZEOF;
+
+ while (loops-- && epos >= PCI_CFG_SPACE_SIZE) {
+ u32 header;
+ u16 ecap;
+ int i, len = 0;
+ bool hidden = false;
+
+ ret = pci_read_config_dword(pdev, epos, &header);
+ if (ret)
+ return ret;
+
+ ecap = PCI_EXT_CAP_ID(header);
+
+ if (ecap <= PCI_EXT_CAP_ID_MAX) {
+ len = pci_ext_cap_length[ecap];
+ if (len == 0xFF) {
+ len = vfio_ext_cap_len(vdev, ecap, epos);
+ if (len < 0)
+ return ret;
+ }
+ }
+
+ if (!len) {
+ pr_info("%s: %s hiding ecap 0x%x@0x%x\n",
+ __func__, dev_name(&pdev->dev), ecap, epos);
+
+ /* If not the first in the chain, we can skip over it */
+ if (prev) {
+ u32 val = epos = PCI_EXT_CAP_NEXT(header);
+ *prev &= cpu_to_le32(~(0xffcU << 20));
+ *prev |= cpu_to_le32(val << 20);
+ continue;
+ }
+
+ /*
+ * Otherwise, fill in a placeholder, the direct
+ * readfn will virtualize this automatically
+ */
+ len = PCI_CAP_SIZEOF;
+ hidden = true;
+ }
+
+ for (i = 0; i < len; i += 4) {
+ if (likely(map[(epos + i) / 4] == PCI_CAP_ID_INVALID))
+ continue;
+
+ pr_warn("%s: %s pci config conflict @0x%x, was ecap 0x%x now ecap 0x%x\n",
+ __func__, dev_name(&pdev->dev),
+ epos + i, map[epos + i], ecap);
+ }
+
+ /*
+ * Even though ecap is 2 bytes, we're currently a long way
+ * from exceeding 1 byte capabilities. If we ever make it
+ * up to 0xFF we'll need to up this to a two-byte, byte map.
+ */
+ BUILD_BUG_ON(PCI_EXT_CAP_ID_MAX >= PCI_CAP_ID_INVALID);
+
+ memset(map + (epos / 4), ecap, len / 4);
+ ret = vfio_fill_vconfig_bytes(vdev, epos, len);
+ if (ret)
+ return ret;
+
+ /*
+ * If we're just using this capability to anchor the list,
+ * hide the real ID. Only count real ecaps. XXX PCI spec
+ * indicates to use cap id = 0, version = 0, next = 0 if
+ * ecaps are absent, hope users check all the way to next.
+ */
+ if (hidden)
+ *(__le32 *)&vdev->vconfig[epos] &=
+ cpu_to_le32((0xffcU << 20));
+ else
+ ecaps++;
+
+ prev = (__le32 *)&vdev->vconfig[epos];
+ epos = PCI_EXT_CAP_NEXT(header);
+ }
+
+ if (!ecaps)
+ *(u32 *)&vdev->vconfig[PCI_CFG_SPACE_SIZE] = 0;
+
+ return 0;
+}
+
+/*
+ * For each device we allocate a pci_config_map that indicates the
+ * capability occupying each dword and thus the struct perm_bits we
+ * use for read and write. We also allocate a virtualized config
+ * space which tracks reads and writes to bits that we emulate for
+ * the user. Initial values filled from device.
+ *
+ * Using shared stuct perm_bits between all vfio-pci devices saves
+ * us from allocating cfg_size buffers for virt and write for every
+ * device. We could remove vconfig and allocate individual buffers
+ * for each area requring emulated bits, but the array of pointers
+ * would be comparable in size (at least for standard config space).
+ */
+int vfio_config_init(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ u8 *map, *vconfig;
+ int ret;
+
+ /*
+ * Config space, caps and ecaps are all dword aligned, so we can
+ * use one byte per dword to record the type.
+ */
+ map = kmalloc(pdev->cfg_size / 4, GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ vconfig = kmalloc(pdev->cfg_size, GFP_KERNEL);
+ if (!vconfig) {
+ kfree(map);
+ return -ENOMEM;
+ }
+
+ vdev->pci_config_map = map;
+ vdev->vconfig = vconfig;
+
+ memset(map, PCI_CAP_ID_BASIC, PCI_STD_HEADER_SIZEOF / 4);
+ memset(map + (PCI_STD_HEADER_SIZEOF / 4), PCI_CAP_ID_INVALID,
+ (pdev->cfg_size - PCI_STD_HEADER_SIZEOF) / 4);
+
+ ret = vfio_fill_vconfig_bytes(vdev, 0, PCI_STD_HEADER_SIZEOF);
+ if (ret)
+ goto out;
+
+ vdev->bardirty = true;
+
+ /*
+ * XXX can we just pci_load_saved_state/pci_restore_state?
+ * may need to rebuild vconfig after that
+ */
+
+ /* For restore after reset */
+ vdev->rbar[0] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_0]);
+ vdev->rbar[1] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_1]);
+ vdev->rbar[2] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_2]);
+ vdev->rbar[3] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_3]);
+ vdev->rbar[4] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_4]);
+ vdev->rbar[5] = le32_to_cpu(*(__le32 *)&vconfig[PCI_BASE_ADDRESS_5]);
+ vdev->rbar[6] = le32_to_cpu(*(__le32 *)&vconfig[PCI_ROM_ADDRESS]);
+
+ if (pdev->is_virtfn) {
+ *(__le16 *)&vconfig[PCI_VENDOR_ID] = cpu_to_le16(pdev->vendor);
+ *(__le16 *)&vconfig[PCI_DEVICE_ID] = cpu_to_le16(pdev->device);
+ }
+
+ ret = vfio_cap_init(vdev);
+ if (ret)
+ goto out;
+
+ ret = vfio_ecap_init(vdev);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ kfree(map);
+ vdev->pci_config_map = NULL;
+ kfree(vconfig);
+ vdev->vconfig = NULL;
+ return pcibios_err_to_errno(ret);
+}
+
+void vfio_config_free(struct vfio_pci_device *vdev)
+{
+ kfree(vdev->vconfig);
+ vdev->vconfig = NULL;
+ kfree(vdev->pci_config_map);
+ vdev->pci_config_map = NULL;
+ kfree(vdev->msi_perm);
+ vdev->msi_perm = NULL;
+}
+
+static ssize_t vfio_config_do_rw(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ struct perm_bits *perm;
+ __le32 val = 0;
+ int cap_start = 0, offset;
+ u8 cap_id;
+ ssize_t ret = count;
+
+ if (*ppos < 0 || *ppos + count > pdev->cfg_size)
+ return -EFAULT;
+
+ /*
+ * gcc can't seem to figure out we're a static function, only called
+ * with count of 1/2/4 and hits copy_from_user_overflow without this.
+ */
+ if (count > sizeof(val))
+ return -EINVAL;
+
+ cap_id = vdev->pci_config_map[*ppos / 4];
+
+ if (cap_id == PCI_CAP_ID_INVALID) {
+ if (iswrite)
+ return ret; /* drop */
+
+ /*
+ * Per PCI spec 3.0, section 6.1, reads from reserved and
+ * unimplemented registers return 0
+ */
+ if (copy_to_user(buf, &val, count))
+ return -EFAULT;
+
+ return ret;
+ }
+
+ /*
+ * All capabilities are minimum 4 bytes and aligned on dword
+ * boundaries. Since we don't support unaligned accesses, we're
+ * only ever accessing a single capability.
+ */
+ if (*ppos >= PCI_CFG_SPACE_SIZE) {
+ WARN_ON(cap_id > PCI_EXT_CAP_ID_MAX);
+
+ perm = &ecap_perms[cap_id];
+ cap_start = vfio_find_cap_start(vdev, *ppos);
+
+ } else {
+ WARN_ON(cap_id > PCI_CAP_ID_MAX);
+
+ perm = &cap_perms[cap_id];
+
+ if (cap_id == PCI_CAP_ID_MSI)
+ perm = vdev->msi_perm;
+
+ if (cap_id > PCI_CAP_ID_BASIC)
+ cap_start = vfio_find_cap_start(vdev, *ppos);
+ }
+
+ WARN_ON(!cap_start && cap_id != PCI_CAP_ID_BASIC);
+ WARN_ON(cap_start > *ppos);
+
+ offset = *ppos - cap_start;
+
+ if (iswrite) {
+ if (!perm->writefn)
+ return ret;
+
+ if (copy_from_user(&val, buf, count))
+ return -EFAULT;
+
+ ret = perm->writefn(vdev, *ppos, count, perm, offset, val);
+ } else {
+ if (perm->readfn) {
+ ret = perm->readfn(vdev, *ppos, count,
+ perm, offset, &val);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (copy_to_user(buf, &val, count))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite)
+{
+ size_t done = 0;
+ int ret = 0;
+ loff_t pos = *ppos;
+
+ pos &= VFIO_PCI_OFFSET_MASK;
+
+ /*
+ * We want to both keep the access size the caller users as well as
+ * support reading large chunks of config space in a single call.
+ * PCI doesn't support unaligned accesses, so we can safely break
+ * those apart.
+ */
+ while (count) {
+ if (count >= 4 && !(pos % 4))
+ ret = vfio_config_do_rw(vdev, buf, 4, &pos, iswrite);
+ else if (count >= 2 && !(pos % 2))
+ ret = vfio_config_do_rw(vdev, buf, 2, &pos, iswrite);
+ else
+ ret = vfio_config_do_rw(vdev, buf, 1, &pos, iswrite);
+
+ if (ret < 0)
+ return ret;
+
+ count -= ret;
+ done += ret;
+ buf += ret;
+ pos += ret;
+ }
+
+ *ppos += done;
+
+ return done;
+}
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
new file mode 100644
index 000000000000..d8dedc7d3910
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -0,0 +1,776 @@
+/*
+ * VFIO PCI interrupt handling
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/eventfd.h>
+#include <linux/pci.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include "vfio_pci_private.h"
+
+/*
+ * IRQfd - generic
+ */
+struct virqfd {
+ struct vfio_pci_device *vdev;
+ struct eventfd_ctx *eventfd;
+ int (*handler)(struct vfio_pci_device *, void *);
+ void (*thread)(struct vfio_pci_device *, void *);
+ void *data;
+ struct work_struct inject;
+ wait_queue_t wait;
+ poll_table pt;
+ struct work_struct shutdown;
+ struct virqfd **pvirqfd;
+};
+
+static struct workqueue_struct *vfio_irqfd_cleanup_wq;
+
+int __init vfio_pci_virqfd_init(void)
+{
+ vfio_irqfd_cleanup_wq =
+ create_singlethread_workqueue("vfio-irqfd-cleanup");
+ if (!vfio_irqfd_cleanup_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void vfio_pci_virqfd_exit(void)
+{
+ destroy_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+static void virqfd_deactivate(struct virqfd *virqfd)
+{
+ queue_work(vfio_irqfd_cleanup_wq, &virqfd->shutdown);
+}
+
+static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ struct virqfd *virqfd = container_of(wait, struct virqfd, wait);
+ unsigned long flags = (unsigned long)key;
+
+ if (flags & POLLIN) {
+ /* An event has been signaled, call function */
+ if ((!virqfd->handler ||
+ virqfd->handler(virqfd->vdev, virqfd->data)) &&
+ virqfd->thread)
+ schedule_work(&virqfd->inject);
+ }
+
+ if (flags & POLLHUP) {
+ unsigned long flags;
+ spin_lock_irqsave(&virqfd->vdev->irqlock, flags);
+
+ /*
+ * The eventfd is closing, if the virqfd has not yet been
+ * queued for release, as determined by testing whether the
+ * vdev pointer to it is still valid, queue it now. As
+ * with kvm irqfds, we know we won't race against the virqfd
+ * going away because we hold wqh->lock to get here.
+ */
+ if (*(virqfd->pvirqfd) == virqfd) {
+ *(virqfd->pvirqfd) = NULL;
+ virqfd_deactivate(virqfd);
+ }
+
+ spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags);
+ }
+
+ return 0;
+}
+
+static void virqfd_ptable_queue_proc(struct file *file,
+ wait_queue_head_t *wqh, poll_table *pt)
+{
+ struct virqfd *virqfd = container_of(pt, struct virqfd, pt);
+ add_wait_queue(wqh, &virqfd->wait);
+}
+
+static void virqfd_shutdown(struct work_struct *work)
+{
+ struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
+ u64 cnt;
+
+ eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
+ flush_work(&virqfd->inject);
+ eventfd_ctx_put(virqfd->eventfd);
+
+ kfree(virqfd);
+}
+
+static void virqfd_inject(struct work_struct *work)
+{
+ struct virqfd *virqfd = container_of(work, struct virqfd, inject);
+ if (virqfd->thread)
+ virqfd->thread(virqfd->vdev, virqfd->data);
+}
+
+static int virqfd_enable(struct vfio_pci_device *vdev,
+ int (*handler)(struct vfio_pci_device *, void *),
+ void (*thread)(struct vfio_pci_device *, void *),
+ void *data, struct virqfd **pvirqfd, int fd)
+{
+ struct file *file = NULL;
+ struct eventfd_ctx *ctx = NULL;
+ struct virqfd *virqfd;
+ int ret = 0;
+ unsigned int events;
+
+ virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
+ if (!virqfd)
+ return -ENOMEM;
+
+ virqfd->pvirqfd = pvirqfd;
+ virqfd->vdev = vdev;
+ virqfd->handler = handler;
+ virqfd->thread = thread;
+ virqfd->data = data;
+
+ INIT_WORK(&virqfd->shutdown, virqfd_shutdown);
+ INIT_WORK(&virqfd->inject, virqfd_inject);
+
+ file = eventfd_fget(fd);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto fail;
+ }
+
+ ctx = eventfd_ctx_fileget(file);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto fail;
+ }
+
+ virqfd->eventfd = ctx;
+
+ /*
+ * virqfds can be released by closing the eventfd or directly
+ * through ioctl. These are both done through a workqueue, so
+ * we update the pointer to the virqfd under lock to avoid
+ * pushing multiple jobs to release the same virqfd.
+ */
+ spin_lock_irq(&vdev->irqlock);
+
+ if (*pvirqfd) {
+ spin_unlock_irq(&vdev->irqlock);
+ ret = -EBUSY;
+ goto fail;
+ }
+ *pvirqfd = virqfd;
+
+ spin_unlock_irq(&vdev->irqlock);
+
+ /*
+ * Install our own custom wake-up handling so we are notified via
+ * a callback whenever someone signals the underlying eventfd.
+ */
+ init_waitqueue_func_entry(&virqfd->wait, virqfd_wakeup);
+ init_poll_funcptr(&virqfd->pt, virqfd_ptable_queue_proc);
+
+ events = file->f_op->poll(file, &virqfd->pt);
+
+ /*
+ * Check if there was an event already pending on the eventfd
+ * before we registered and trigger it as if we didn't miss it.
+ */
+ if (events & POLLIN) {
+ if ((!handler || handler(vdev, data)) && thread)
+ schedule_work(&virqfd->inject);
+ }
+
+ /*
+ * Do not drop the file until the irqfd is fully initialized,
+ * otherwise we might race against the POLLHUP.
+ */
+ fput(file);
+
+ return 0;
+
+fail:
+ if (ctx && !IS_ERR(ctx))
+ eventfd_ctx_put(ctx);
+
+ if (file && !IS_ERR(file))
+ fput(file);
+
+ kfree(virqfd);
+
+ return ret;
+}
+
+static void virqfd_disable(struct vfio_pci_device *vdev,
+ struct virqfd **pvirqfd)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ if (*pvirqfd) {
+ virqfd_deactivate(*pvirqfd);
+ *pvirqfd = NULL;
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ /*
+ * Block until we know all outstanding shutdown jobs have completed.
+ * Even if we don't queue the job, flush the wq to be sure it's
+ * been released.
+ */
+ flush_workqueue(vfio_irqfd_cleanup_wq);
+}
+
+/*
+ * INTx
+ */
+static void vfio_send_intx_eventfd(struct vfio_pci_device *vdev, void *unused)
+{
+ if (likely(is_intx(vdev) && !vdev->virq_disabled))
+ eventfd_signal(vdev->ctx[0].trigger, 1);
+}
+
+void vfio_pci_intx_mask(struct vfio_pci_device *vdev)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ /*
+ * Masking can come from interrupt, ioctl, or config space
+ * via INTx disable. The latter means this can get called
+ * even when not using intx delivery. In this case, just
+ * try to have the physical bit follow the virtual bit.
+ */
+ if (unlikely(!is_intx(vdev))) {
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 0);
+ } else if (!vdev->ctx[0].masked) {
+ /*
+ * Can't use check_and_mask here because we always want to
+ * mask, not just when something is pending.
+ */
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 0);
+ else
+ disable_irq_nosync(pdev->irq);
+
+ vdev->ctx[0].masked = true;
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+}
+
+/*
+ * If this is triggered by an eventfd, we can't call eventfd_signal
+ * or else we'll deadlock on the eventfd wait queue. Return >0 when
+ * a signal is necessary, which can then be handled via a work queue
+ * or directly depending on the caller.
+ */
+int vfio_pci_intx_unmask_handler(struct vfio_pci_device *vdev, void *unused)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ /*
+ * Unmasking comes from ioctl or config, so again, have the
+ * physical bit follow the virtual even when not using INTx.
+ */
+ if (unlikely(!is_intx(vdev))) {
+ if (vdev->pci_2_3)
+ pci_intx(pdev, 1);
+ } else if (vdev->ctx[0].masked && !vdev->virq_disabled) {
+ /*
+ * A pending interrupt here would immediately trigger,
+ * but we can avoid that overhead by just re-sending
+ * the interrupt to the user.
+ */
+ if (vdev->pci_2_3) {
+ if (!pci_check_and_unmask_intx(pdev))
+ ret = 1;
+ } else
+ enable_irq(pdev->irq);
+
+ vdev->ctx[0].masked = (ret > 0);
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ return ret;
+}
+
+void vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
+{
+ if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0)
+ vfio_send_intx_eventfd(vdev, NULL);
+}
+
+static irqreturn_t vfio_intx_handler(int irq, void *dev_id)
+{
+ struct vfio_pci_device *vdev = dev_id;
+ unsigned long flags;
+ int ret = IRQ_NONE;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ if (!vdev->pci_2_3) {
+ disable_irq_nosync(vdev->pdev->irq);
+ vdev->ctx[0].masked = true;
+ ret = IRQ_HANDLED;
+ } else if (!vdev->ctx[0].masked && /* may be shared */
+ pci_check_and_mask_intx(vdev->pdev)) {
+ vdev->ctx[0].masked = true;
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ if (ret == IRQ_HANDLED)
+ vfio_send_intx_eventfd(vdev, NULL);
+
+ return ret;
+}
+
+static int vfio_intx_enable(struct vfio_pci_device *vdev)
+{
+ if (!is_irq_none(vdev))
+ return -EINVAL;
+
+ if (!vdev->pdev->irq)
+ return -ENODEV;
+
+ vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ if (!vdev->ctx)
+ return -ENOMEM;
+
+ vdev->num_ctx = 1;
+ vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX;
+
+ return 0;
+}
+
+static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ unsigned long irqflags = IRQF_SHARED;
+ struct eventfd_ctx *trigger;
+ unsigned long flags;
+ int ret;
+
+ if (vdev->ctx[0].trigger) {
+ free_irq(pdev->irq, vdev);
+ kfree(vdev->ctx[0].name);
+ eventfd_ctx_put(vdev->ctx[0].trigger);
+ vdev->ctx[0].trigger = NULL;
+ }
+
+ if (fd < 0) /* Disable only */
+ return 0;
+
+ vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)",
+ pci_name(pdev));
+ if (!vdev->ctx[0].name)
+ return -ENOMEM;
+
+ trigger = eventfd_ctx_fdget(fd);
+ if (IS_ERR(trigger)) {
+ kfree(vdev->ctx[0].name);
+ return PTR_ERR(trigger);
+ }
+
+ if (!vdev->pci_2_3)
+ irqflags = 0;
+
+ ret = request_irq(pdev->irq, vfio_intx_handler,
+ irqflags, vdev->ctx[0].name, vdev);
+ if (ret) {
+ kfree(vdev->ctx[0].name);
+ eventfd_ctx_put(trigger);
+ return ret;
+ }
+
+ vdev->ctx[0].trigger = trigger;
+
+ /*
+ * INTx disable will stick across the new irq setup,
+ * disable_irq won't.
+ */
+ spin_lock_irqsave(&vdev->irqlock, flags);
+ if (!vdev->pci_2_3 && (vdev->ctx[0].masked || vdev->virq_disabled))
+ disable_irq_nosync(pdev->irq);
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
+
+ return 0;
+}
+
+static void vfio_intx_disable(struct vfio_pci_device *vdev)
+{
+ vfio_intx_set_signal(vdev, -1);
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
+ virqfd_disable(vdev, &vdev->ctx[0].mask);
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ vdev->num_ctx = 0;
+ kfree(vdev->ctx);
+}
+
+/*
+ * MSI/MSI-X
+ */
+static irqreturn_t vfio_msihandler(int irq, void *arg)
+{
+ struct eventfd_ctx *trigger = arg;
+
+ eventfd_signal(trigger, 1);
+ return IRQ_HANDLED;
+}
+
+static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int ret;
+
+ if (!is_irq_none(vdev))
+ return -EINVAL;
+
+ vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ if (!vdev->ctx)
+ return -ENOMEM;
+
+ if (msix) {
+ int i;
+
+ vdev->msix = kzalloc(nvec * sizeof(struct msix_entry),
+ GFP_KERNEL);
+ if (!vdev->msix) {
+ kfree(vdev->ctx);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < nvec; i++)
+ vdev->msix[i].entry = i;
+
+ ret = pci_enable_msix(pdev, vdev->msix, nvec);
+ if (ret) {
+ kfree(vdev->msix);
+ kfree(vdev->ctx);
+ return ret;
+ }
+ } else {
+ ret = pci_enable_msi_block(pdev, nvec);
+ if (ret) {
+ kfree(vdev->ctx);
+ return ret;
+ }
+ }
+
+ vdev->num_ctx = nvec;
+ vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX :
+ VFIO_PCI_MSI_IRQ_INDEX;
+
+ if (!msix) {
+ /*
+ * Compute the virtual hardware field for max msi vectors -
+ * it is the log base 2 of the number of vectors.
+ */
+ vdev->msi_qmax = fls(nvec * 2 - 1) - 1;
+ }
+
+ return 0;
+}
+
+static int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
+ int vector, int fd, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int irq = msix ? vdev->msix[vector].vector : pdev->irq + vector;
+ char *name = msix ? "vfio-msix" : "vfio-msi";
+ struct eventfd_ctx *trigger;
+ int ret;
+
+ if (vector >= vdev->num_ctx)
+ return -EINVAL;
+
+ if (vdev->ctx[vector].trigger) {
+ free_irq(irq, vdev->ctx[vector].trigger);
+ kfree(vdev->ctx[vector].name);
+ eventfd_ctx_put(vdev->ctx[vector].trigger);
+ vdev->ctx[vector].trigger = NULL;
+ }
+
+ if (fd < 0)
+ return 0;
+
+ vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "%s[%d](%s)",
+ name, vector, pci_name(pdev));
+ if (!vdev->ctx[vector].name)
+ return -ENOMEM;
+
+ trigger = eventfd_ctx_fdget(fd);
+ if (IS_ERR(trigger)) {
+ kfree(vdev->ctx[vector].name);
+ return PTR_ERR(trigger);
+ }
+
+ ret = request_irq(irq, vfio_msihandler, 0,
+ vdev->ctx[vector].name, trigger);
+ if (ret) {
+ kfree(vdev->ctx[vector].name);
+ eventfd_ctx_put(trigger);
+ return ret;
+ }
+
+ vdev->ctx[vector].trigger = trigger;
+
+ return 0;
+}
+
+static int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
+ unsigned count, int32_t *fds, bool msix)
+{
+ int i, j, ret = 0;
+
+ if (start + count > vdev->num_ctx)
+ return -EINVAL;
+
+ for (i = 0, j = start; i < count && !ret; i++, j++) {
+ int fd = fds ? fds[i] : -1;
+ ret = vfio_msi_set_vector_signal(vdev, j, fd, msix);
+ }
+
+ if (ret) {
+ for (--j; j >= start; j--)
+ vfio_msi_set_vector_signal(vdev, j, -1, msix);
+ }
+
+ return ret;
+}
+
+static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ int i;
+
+ vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
+
+ for (i = 0; i < vdev->num_ctx; i++) {
+ virqfd_disable(vdev, &vdev->ctx[i].unmask);
+ virqfd_disable(vdev, &vdev->ctx[i].mask);
+ }
+
+ if (msix) {
+ pci_disable_msix(vdev->pdev);
+ kfree(vdev->msix);
+ } else
+ pci_disable_msi(pdev);
+
+ vdev->irq_type = VFIO_PCI_NUM_IRQS;
+ vdev->num_ctx = 0;
+ kfree(vdev->ctx);
+}
+
+/*
+ * IOCTL support
+ */
+static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (!is_intx(vdev) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_pci_intx_unmask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t unmask = *(uint8_t *)data;
+ if (unmask)
+ vfio_pci_intx_unmask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t fd = *(int32_t *)data;
+ if (fd >= 0)
+ return virqfd_enable(vdev, vfio_pci_intx_unmask_handler,
+ vfio_send_intx_eventfd, NULL,
+ &vdev->ctx[0].unmask, fd);
+
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
+ }
+
+ return 0;
+}
+
+static int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (!is_intx(vdev) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_pci_intx_mask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t mask = *(uint8_t *)data;
+ if (mask)
+ vfio_pci_intx_mask(vdev);
+ } else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ return -ENOTTY; /* XXX implement me */
+ }
+
+ return 0;
+}
+
+static int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+ vfio_intx_disable(vdev);
+ return 0;
+ }
+
+ if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1)
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t fd = *(int32_t *)data;
+ int ret;
+
+ if (is_intx(vdev))
+ return vfio_intx_set_signal(vdev, fd);
+
+ ret = vfio_intx_enable(vdev);
+ if (ret)
+ return ret;
+
+ ret = vfio_intx_set_signal(vdev, fd);
+ if (ret)
+ vfio_intx_disable(vdev);
+
+ return ret;
+ }
+
+ if (!is_intx(vdev))
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ vfio_send_intx_eventfd(vdev, NULL);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t trigger = *(uint8_t *)data;
+ if (trigger)
+ vfio_send_intx_eventfd(vdev, NULL);
+ }
+ return 0;
+}
+
+static int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
+ unsigned index, unsigned start,
+ unsigned count, uint32_t flags, void *data)
+{
+ int i;
+ bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false;
+
+ if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
+ vfio_msi_disable(vdev, msix);
+ return 0;
+ }
+
+ if (!(irq_is(vdev, index) || is_irq_none(vdev)))
+ return -EINVAL;
+
+ if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
+ int32_t *fds = data;
+ int ret;
+
+ if (vdev->irq_type == index)
+ return vfio_msi_set_block(vdev, start, count,
+ fds, msix);
+
+ ret = vfio_msi_enable(vdev, start + count, msix);
+ if (ret)
+ return ret;
+
+ ret = vfio_msi_set_block(vdev, start, count, fds, msix);
+ if (ret)
+ vfio_msi_disable(vdev, msix);
+
+ return ret;
+ }
+
+ if (!irq_is(vdev, index) || start + count > vdev->num_ctx)
+ return -EINVAL;
+
+ for (i = start; i < start + count; i++) {
+ if (!vdev->ctx[i].trigger)
+ continue;
+ if (flags & VFIO_IRQ_SET_DATA_NONE) {
+ eventfd_signal(vdev->ctx[i].trigger, 1);
+ } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
+ uint8_t *bools = data;
+ if (bools[i - start])
+ eventfd_signal(vdev->ctx[i].trigger, 1);
+ }
+ }
+ return 0;
+}
+
+int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
+ unsigned index, unsigned start, unsigned count,
+ void *data)
+{
+ int (*func)(struct vfio_pci_device *vdev, unsigned index,
+ unsigned start, unsigned count, uint32_t flags,
+ void *data) = NULL;
+
+ switch (index) {
+ case VFIO_PCI_INTX_IRQ_INDEX:
+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+ case VFIO_IRQ_SET_ACTION_MASK:
+ func = vfio_pci_set_intx_mask;
+ break;
+ case VFIO_IRQ_SET_ACTION_UNMASK:
+ func = vfio_pci_set_intx_unmask;
+ break;
+ case VFIO_IRQ_SET_ACTION_TRIGGER:
+ func = vfio_pci_set_intx_trigger;
+ break;
+ }
+ break;
+ case VFIO_PCI_MSI_IRQ_INDEX:
+ case VFIO_PCI_MSIX_IRQ_INDEX:
+ switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
+ case VFIO_IRQ_SET_ACTION_MASK:
+ case VFIO_IRQ_SET_ACTION_UNMASK:
+ /* XXX Need masking support exported */
+ break;
+ case VFIO_IRQ_SET_ACTION_TRIGGER:
+ func = vfio_pci_set_msi_trigger;
+ break;
+ }
+ break;
+ }
+
+ if (!func)
+ return -ENOTTY;
+
+ return func(vdev, index, start, count, flags, data);
+}
diff --git a/drivers/vfio/pci/vfio_pci_private.h b/drivers/vfio/pci/vfio_pci_private.h
new file mode 100644
index 000000000000..611827cba8cd
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_private.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/mutex.h>
+#include <linux/pci.h>
+
+#ifndef VFIO_PCI_PRIVATE_H
+#define VFIO_PCI_PRIVATE_H
+
+#define VFIO_PCI_OFFSET_SHIFT 40
+
+#define VFIO_PCI_OFFSET_TO_INDEX(off) (off >> VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
+#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
+
+struct vfio_pci_irq_ctx {
+ struct eventfd_ctx *trigger;
+ struct virqfd *unmask;
+ struct virqfd *mask;
+ char *name;
+ bool masked;
+};
+
+struct vfio_pci_device {
+ struct pci_dev *pdev;
+ void __iomem *barmap[PCI_STD_RESOURCE_END + 1];
+ u8 *pci_config_map;
+ u8 *vconfig;
+ struct perm_bits *msi_perm;
+ spinlock_t irqlock;
+ struct mutex igate;
+ struct msix_entry *msix;
+ struct vfio_pci_irq_ctx *ctx;
+ int num_ctx;
+ int irq_type;
+ u8 msi_qmax;
+ u8 msix_bar;
+ u16 msix_size;
+ u32 msix_offset;
+ u32 rbar[7];
+ bool pci_2_3;
+ bool virq_disabled;
+ bool reset_works;
+ bool extended_caps;
+ bool bardirty;
+ struct pci_saved_state *pci_saved_state;
+ atomic_t refcnt;
+};
+
+#define is_intx(vdev) (vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX)
+#define is_msi(vdev) (vdev->irq_type == VFIO_PCI_MSI_IRQ_INDEX)
+#define is_msix(vdev) (vdev->irq_type == VFIO_PCI_MSIX_IRQ_INDEX)
+#define is_irq_none(vdev) (!(is_intx(vdev) || is_msi(vdev) || is_msix(vdev)))
+#define irq_is(vdev, type) (vdev->irq_type == type)
+
+extern void vfio_pci_intx_mask(struct vfio_pci_device *vdev);
+extern void vfio_pci_intx_unmask(struct vfio_pci_device *vdev);
+
+extern int vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev,
+ uint32_t flags, unsigned index,
+ unsigned start, unsigned count, void *data);
+
+extern ssize_t vfio_pci_config_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+extern ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev,
+ char __user *buf, size_t count,
+ loff_t *ppos, bool iswrite);
+
+extern int vfio_pci_init_perm_bits(void);
+extern void vfio_pci_uninit_perm_bits(void);
+
+extern int vfio_pci_virqfd_init(void);
+extern void vfio_pci_virqfd_exit(void);
+
+extern int vfio_config_init(struct vfio_pci_device *vdev);
+extern void vfio_config_free(struct vfio_pci_device *vdev);
+#endif /* VFIO_PCI_PRIVATE_H */
diff --git a/drivers/vfio/pci/vfio_pci_rdwr.c b/drivers/vfio/pci/vfio_pci_rdwr.c
new file mode 100644
index 000000000000..4362d9e7baa3
--- /dev/null
+++ b/drivers/vfio/pci/vfio_pci_rdwr.c
@@ -0,0 +1,269 @@
+/*
+ * VFIO PCI I/O Port & MMIO access
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "vfio_pci_private.h"
+
+/* I/O Port BAR access */
+ssize_t vfio_pci_io_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+ int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ void __iomem *io;
+ size_t done = 0;
+
+ if (!pci_resource_start(pdev, bar))
+ return -EINVAL;
+
+ if (pos + count > pci_resource_len(pdev, bar))
+ return -EINVAL;
+
+ if (!vdev->barmap[bar]) {
+ int ret;
+
+ ret = pci_request_selected_regions(pdev, 1 << bar, "vfio");
+ if (ret)
+ return ret;
+
+ vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+ if (!vdev->barmap[bar]) {
+ pci_release_selected_regions(pdev, 1 << bar);
+ return -EINVAL;
+ }
+ }
+
+ io = vdev->barmap[bar];
+
+ while (count) {
+ int filled;
+
+ if (count >= 3 && !(pos % 4)) {
+ __le32 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 4))
+ return -EFAULT;
+
+ iowrite32(le32_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le32(ioread32(io + pos));
+
+ if (copy_to_user(buf, &val, 4))
+ return -EFAULT;
+ }
+
+ filled = 4;
+
+ } else if ((pos % 2) == 0 && count >= 2) {
+ __le16 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 2))
+ return -EFAULT;
+
+ iowrite16(le16_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le16(ioread16(io + pos));
+
+ if (copy_to_user(buf, &val, 2))
+ return -EFAULT;
+ }
+
+ filled = 2;
+ } else {
+ u8 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 1))
+ return -EFAULT;
+
+ iowrite8(val, io + pos);
+ } else {
+ val = ioread8(io + pos);
+
+ if (copy_to_user(buf, &val, 1))
+ return -EFAULT;
+ }
+
+ filled = 1;
+ }
+
+ count -= filled;
+ done += filled;
+ buf += filled;
+ pos += filled;
+ }
+
+ *ppos += done;
+
+ return done;
+}
+
+/*
+ * MMIO BAR access
+ * We handle two excluded ranges here as well, if the user tries to read
+ * the ROM beyond what PCI tells us is available or the MSI-X table region,
+ * we return 0xFF and writes are dropped.
+ */
+ssize_t vfio_pci_mem_readwrite(struct vfio_pci_device *vdev, char __user *buf,
+ size_t count, loff_t *ppos, bool iswrite)
+{
+ struct pci_dev *pdev = vdev->pdev;
+ loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
+ int bar = VFIO_PCI_OFFSET_TO_INDEX(*ppos);
+ void __iomem *io;
+ resource_size_t end;
+ size_t done = 0;
+ size_t x_start = 0, x_end = 0; /* excluded range */
+
+ if (!pci_resource_start(pdev, bar))
+ return -EINVAL;
+
+ end = pci_resource_len(pdev, bar);
+
+ if (pos > end)
+ return -EINVAL;
+
+ if (pos == end)
+ return 0;
+
+ if (pos + count > end)
+ count = end - pos;
+
+ if (bar == PCI_ROM_RESOURCE) {
+ io = pci_map_rom(pdev, &x_start);
+ x_end = end;
+ } else {
+ if (!vdev->barmap[bar]) {
+ int ret;
+
+ ret = pci_request_selected_regions(pdev, 1 << bar,
+ "vfio");
+ if (ret)
+ return ret;
+
+ vdev->barmap[bar] = pci_iomap(pdev, bar, 0);
+
+ if (!vdev->barmap[bar]) {
+ pci_release_selected_regions(pdev, 1 << bar);
+ return -EINVAL;
+ }
+ }
+
+ io = vdev->barmap[bar];
+
+ if (bar == vdev->msix_bar) {
+ x_start = vdev->msix_offset;
+ x_end = vdev->msix_offset + vdev->msix_size;
+ }
+ }
+
+ if (!io)
+ return -EINVAL;
+
+ while (count) {
+ size_t fillable, filled;
+
+ if (pos < x_start)
+ fillable = x_start - pos;
+ else if (pos >= x_end)
+ fillable = end - pos;
+ else
+ fillable = 0;
+
+ if (fillable >= 4 && !(pos % 4) && (count >= 4)) {
+ __le32 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 4))
+ goto out;
+
+ iowrite32(le32_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le32(ioread32(io + pos));
+
+ if (copy_to_user(buf, &val, 4))
+ goto out;
+ }
+
+ filled = 4;
+ } else if (fillable >= 2 && !(pos % 2) && (count >= 2)) {
+ __le16 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 2))
+ goto out;
+
+ iowrite16(le16_to_cpu(val), io + pos);
+ } else {
+ val = cpu_to_le16(ioread16(io + pos));
+
+ if (copy_to_user(buf, &val, 2))
+ goto out;
+ }
+
+ filled = 2;
+ } else if (fillable) {
+ u8 val;
+
+ if (iswrite) {
+ if (copy_from_user(&val, buf, 1))
+ goto out;
+
+ iowrite8(val, io + pos);
+ } else {
+ val = ioread8(io + pos);
+
+ if (copy_to_user(buf, &val, 1))
+ goto out;
+ }
+
+ filled = 1;
+ } else {
+ /* Drop writes, fill reads with FF */
+ if (!iswrite) {
+ char val = 0xFF;
+ size_t i;
+
+ for (i = 0; i < x_end - pos; i++) {
+ if (put_user(val, buf + i))
+ goto out;
+ }
+ }
+
+ filled = x_end - pos;
+ }
+
+ count -= filled;
+ done += filled;
+ buf += filled;
+ pos += filled;
+ }
+
+ *ppos += done;
+
+out:
+ if (bar == PCI_ROM_RESOURCE)
+ pci_unmap_rom(pdev, io);
+
+ return count ? -EFAULT : done;
+}
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
new file mode 100644
index 000000000000..17830c9c7cc6
--- /dev/null
+++ b/drivers/vfio/vfio.c
@@ -0,0 +1,1415 @@
+/*
+ * VFIO core
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ */
+
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
+#include <linux/fs.h>
+#include <linux/idr.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/wait.h>
+
+#define DRIVER_VERSION "0.3"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "VFIO - User Level meta-driver"
+
+static struct vfio {
+ struct class *class;
+ struct list_head iommu_drivers_list;
+ struct mutex iommu_drivers_lock;
+ struct list_head group_list;
+ struct idr group_idr;
+ struct mutex group_lock;
+ struct cdev group_cdev;
+ struct device *dev;
+ dev_t devt;
+ struct cdev cdev;
+ wait_queue_head_t release_q;
+} vfio;
+
+struct vfio_iommu_driver {
+ const struct vfio_iommu_driver_ops *ops;
+ struct list_head vfio_next;
+};
+
+struct vfio_container {
+ struct kref kref;
+ struct list_head group_list;
+ struct mutex group_lock;
+ struct vfio_iommu_driver *iommu_driver;
+ void *iommu_data;
+};
+
+struct vfio_group {
+ struct kref kref;
+ int minor;
+ atomic_t container_users;
+ struct iommu_group *iommu_group;
+ struct vfio_container *container;
+ struct list_head device_list;
+ struct mutex device_lock;
+ struct device *dev;
+ struct notifier_block nb;
+ struct list_head vfio_next;
+ struct list_head container_next;
+};
+
+struct vfio_device {
+ struct kref kref;
+ struct device *dev;
+ const struct vfio_device_ops *ops;
+ struct vfio_group *group;
+ struct list_head group_next;
+ void *device_data;
+};
+
+/**
+ * IOMMU driver registration
+ */
+int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+ struct vfio_iommu_driver *driver, *tmp;
+
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
+ if (!driver)
+ return -ENOMEM;
+
+ driver->ops = ops;
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+
+ /* Check for duplicates */
+ list_for_each_entry(tmp, &vfio.iommu_drivers_list, vfio_next) {
+ if (tmp->ops == ops) {
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ kfree(driver);
+ return -EINVAL;
+ }
+ }
+
+ list_add(&driver->vfio_next, &vfio.iommu_drivers_list);
+
+ mutex_unlock(&vfio.iommu_drivers_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_register_iommu_driver);
+
+void vfio_unregister_iommu_driver(const struct vfio_iommu_driver_ops *ops)
+{
+ struct vfio_iommu_driver *driver;
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+ if (driver->ops == ops) {
+ list_del(&driver->vfio_next);
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ kfree(driver);
+ return;
+ }
+ }
+ mutex_unlock(&vfio.iommu_drivers_lock);
+}
+EXPORT_SYMBOL_GPL(vfio_unregister_iommu_driver);
+
+/**
+ * Group minor allocation/free - both called with vfio.group_lock held
+ */
+static int vfio_alloc_group_minor(struct vfio_group *group)
+{
+ int ret, minor;
+
+again:
+ if (unlikely(idr_pre_get(&vfio.group_idr, GFP_KERNEL) == 0))
+ return -ENOMEM;
+
+ /* index 0 is used by /dev/vfio/vfio */
+ ret = idr_get_new_above(&vfio.group_idr, group, 1, &minor);
+ if (ret == -EAGAIN)
+ goto again;
+ if (ret || minor > MINORMASK) {
+ if (minor > MINORMASK)
+ idr_remove(&vfio.group_idr, minor);
+ return -ENOSPC;
+ }
+
+ return minor;
+}
+
+static void vfio_free_group_minor(int minor)
+{
+ idr_remove(&vfio.group_idr, minor);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+ unsigned long action, void *data);
+static void vfio_group_get(struct vfio_group *group);
+
+/**
+ * Container objects - containers are created when /dev/vfio/vfio is
+ * opened, but their lifecycle extends until the last user is done, so
+ * it's freed via kref. Must support container/group/device being
+ * closed in any order.
+ */
+static void vfio_container_get(struct vfio_container *container)
+{
+ kref_get(&container->kref);
+}
+
+static void vfio_container_release(struct kref *kref)
+{
+ struct vfio_container *container;
+ container = container_of(kref, struct vfio_container, kref);
+
+ kfree(container);
+}
+
+static void vfio_container_put(struct vfio_container *container)
+{
+ kref_put(&container->kref, vfio_container_release);
+}
+
+/**
+ * Group objects - create, release, get, put, search
+ */
+static struct vfio_group *vfio_create_group(struct iommu_group *iommu_group)
+{
+ struct vfio_group *group, *tmp;
+ struct device *dev;
+ int ret, minor;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&group->kref);
+ INIT_LIST_HEAD(&group->device_list);
+ mutex_init(&group->device_lock);
+ atomic_set(&group->container_users, 0);
+ group->iommu_group = iommu_group;
+
+ group->nb.notifier_call = vfio_iommu_group_notifier;
+
+ /*
+ * blocking notifiers acquire a rwsem around registering and hold
+ * it around callback. Therefore, need to register outside of
+ * vfio.group_lock to avoid A-B/B-A contention. Our callback won't
+ * do anything unless it can find the group in vfio.group_list, so
+ * no harm in registering early.
+ */
+ ret = iommu_group_register_notifier(iommu_group, &group->nb);
+ if (ret) {
+ kfree(group);
+ return ERR_PTR(ret);
+ }
+
+ mutex_lock(&vfio.group_lock);
+
+ minor = vfio_alloc_group_minor(group);
+ if (minor < 0) {
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return ERR_PTR(minor);
+ }
+
+ /* Did we race creating this group? */
+ list_for_each_entry(tmp, &vfio.group_list, vfio_next) {
+ if (tmp->iommu_group == iommu_group) {
+ vfio_group_get(tmp);
+ vfio_free_group_minor(minor);
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return tmp;
+ }
+ }
+
+ dev = device_create(vfio.class, NULL, MKDEV(MAJOR(vfio.devt), minor),
+ group, "%d", iommu_group_id(iommu_group));
+ if (IS_ERR(dev)) {
+ vfio_free_group_minor(minor);
+ mutex_unlock(&vfio.group_lock);
+ kfree(group);
+ return (struct vfio_group *)dev; /* ERR_PTR */
+ }
+
+ group->minor = minor;
+ group->dev = dev;
+
+ list_add(&group->vfio_next, &vfio.group_list);
+
+ mutex_unlock(&vfio.group_lock);
+
+ return group;
+}
+
+/* called with vfio.group_lock held */
+static void vfio_group_release(struct kref *kref)
+{
+ struct vfio_group *group = container_of(kref, struct vfio_group, kref);
+
+ WARN_ON(!list_empty(&group->device_list));
+
+ device_destroy(vfio.class, MKDEV(MAJOR(vfio.devt), group->minor));
+ list_del(&group->vfio_next);
+ vfio_free_group_minor(group->minor);
+
+ mutex_unlock(&vfio.group_lock);
+
+ /*
+ * Unregister outside of lock. A spurious callback is harmless now
+ * that the group is no longer in vfio.group_list.
+ */
+ iommu_group_unregister_notifier(group->iommu_group, &group->nb);
+
+ kfree(group);
+}
+
+static void vfio_group_put(struct vfio_group *group)
+{
+ kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock);
+}
+
+/* Assume group_lock or group reference is held */
+static void vfio_group_get(struct vfio_group *group)
+{
+ kref_get(&group->kref);
+}
+
+/*
+ * Not really a try as we will sleep for mutex, but we need to make
+ * sure the group pointer is valid under lock and get a reference.
+ */
+static struct vfio_group *vfio_group_try_get(struct vfio_group *group)
+{
+ struct vfio_group *target = group;
+
+ mutex_lock(&vfio.group_lock);
+ list_for_each_entry(group, &vfio.group_list, vfio_next) {
+ if (group == target) {
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+ return group;
+ }
+ }
+ mutex_unlock(&vfio.group_lock);
+
+ return NULL;
+}
+
+static
+struct vfio_group *vfio_group_get_from_iommu(struct iommu_group *iommu_group)
+{
+ struct vfio_group *group;
+
+ mutex_lock(&vfio.group_lock);
+ list_for_each_entry(group, &vfio.group_list, vfio_next) {
+ if (group->iommu_group == iommu_group) {
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+ return group;
+ }
+ }
+ mutex_unlock(&vfio.group_lock);
+
+ return NULL;
+}
+
+static struct vfio_group *vfio_group_get_from_minor(int minor)
+{
+ struct vfio_group *group;
+
+ mutex_lock(&vfio.group_lock);
+ group = idr_find(&vfio.group_idr, minor);
+ if (!group) {
+ mutex_unlock(&vfio.group_lock);
+ return NULL;
+ }
+ vfio_group_get(group);
+ mutex_unlock(&vfio.group_lock);
+
+ return group;
+}
+
+/**
+ * Device objects - create, release, get, put, search
+ */
+static
+struct vfio_device *vfio_group_create_device(struct vfio_group *group,
+ struct device *dev,
+ const struct vfio_device_ops *ops,
+ void *device_data)
+{
+ struct vfio_device *device;
+ int ret;
+
+ device = kzalloc(sizeof(*device), GFP_KERNEL);
+ if (!device)
+ return ERR_PTR(-ENOMEM);
+
+ kref_init(&device->kref);
+ device->dev = dev;
+ device->group = group;
+ device->ops = ops;
+ device->device_data = device_data;
+
+ ret = dev_set_drvdata(dev, device);
+ if (ret) {
+ kfree(device);
+ return ERR_PTR(ret);
+ }
+
+ /* No need to get group_lock, caller has group reference */
+ vfio_group_get(group);
+
+ mutex_lock(&group->device_lock);
+ list_add(&device->group_next, &group->device_list);
+ mutex_unlock(&group->device_lock);
+
+ return device;
+}
+
+static void vfio_device_release(struct kref *kref)
+{
+ struct vfio_device *device = container_of(kref,
+ struct vfio_device, kref);
+ struct vfio_group *group = device->group;
+
+ list_del(&device->group_next);
+ mutex_unlock(&group->device_lock);
+
+ dev_set_drvdata(device->dev, NULL);
+
+ kfree(device);
+
+ /* vfio_del_group_dev may be waiting for this device */
+ wake_up(&vfio.release_q);
+}
+
+/* Device reference always implies a group reference */
+static void vfio_device_put(struct vfio_device *device)
+{
+ struct vfio_group *group = device->group;
+ kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock);
+ vfio_group_put(group);
+}
+
+static void vfio_device_get(struct vfio_device *device)
+{
+ vfio_group_get(device->group);
+ kref_get(&device->kref);
+}
+
+static struct vfio_device *vfio_group_get_device(struct vfio_group *group,
+ struct device *dev)
+{
+ struct vfio_device *device;
+
+ mutex_lock(&group->device_lock);
+ list_for_each_entry(device, &group->device_list, group_next) {
+ if (device->dev == dev) {
+ vfio_device_get(device);
+ mutex_unlock(&group->device_lock);
+ return device;
+ }
+ }
+ mutex_unlock(&group->device_lock);
+ return NULL;
+}
+
+/*
+ * Whitelist some drivers that we know are safe (no dma) or just sit on
+ * a device. It's not always practical to leave a device within a group
+ * driverless as it could get re-bound to something unsafe.
+ */
+static const char * const vfio_driver_whitelist[] = { "pci-stub" };
+
+static bool vfio_whitelisted_driver(struct device_driver *drv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vfio_driver_whitelist); i++) {
+ if (!strcmp(drv->name, vfio_driver_whitelist[i]))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * A vfio group is viable for use by userspace if all devices are either
+ * driver-less or bound to a vfio or whitelisted driver. We test the
+ * latter by the existence of a struct vfio_device matching the dev.
+ */
+static int vfio_dev_viable(struct device *dev, void *data)
+{
+ struct vfio_group *group = data;
+ struct vfio_device *device;
+
+ if (!dev->driver || vfio_whitelisted_driver(dev->driver))
+ return 0;
+
+ device = vfio_group_get_device(group, dev);
+ if (device) {
+ vfio_device_put(device);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * Async device support
+ */
+static int vfio_group_nb_add_dev(struct vfio_group *group, struct device *dev)
+{
+ struct vfio_device *device;
+
+ /* Do we already know about it? We shouldn't */
+ device = vfio_group_get_device(group, dev);
+ if (WARN_ON_ONCE(device)) {
+ vfio_device_put(device);
+ return 0;
+ }
+
+ /* Nothing to do for idle groups */
+ if (!atomic_read(&group->container_users))
+ return 0;
+
+ /* TODO Prevent device auto probing */
+ WARN("Device %s added to live group %d!\n", dev_name(dev),
+ iommu_group_id(group->iommu_group));
+
+ return 0;
+}
+
+static int vfio_group_nb_del_dev(struct vfio_group *group, struct device *dev)
+{
+ struct vfio_device *device;
+
+ /*
+ * Expect to fall out here. If a device was in use, it would
+ * have been bound to a vfio sub-driver, which would have blocked
+ * in .remove at vfio_del_group_dev. Sanity check that we no
+ * longer track the device, so it's safe to remove.
+ */
+ device = vfio_group_get_device(group, dev);
+ if (likely(!device))
+ return 0;
+
+ WARN("Device %s removed from live group %d!\n", dev_name(dev),
+ iommu_group_id(group->iommu_group));
+
+ vfio_device_put(device);
+ return 0;
+}
+
+static int vfio_group_nb_verify(struct vfio_group *group, struct device *dev)
+{
+ /* We don't care what happens when the group isn't in use */
+ if (!atomic_read(&group->container_users))
+ return 0;
+
+ return vfio_dev_viable(dev, group);
+}
+
+static int vfio_iommu_group_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct vfio_group *group = container_of(nb, struct vfio_group, nb);
+ struct device *dev = data;
+
+ /*
+ * Need to go through a group_lock lookup to get a reference or
+ * we risk racing a group being removed. Leave a WARN_ON for
+ * debuging, but if the group no longer exists, a spurious notify
+ * is harmless.
+ */
+ group = vfio_group_try_get(group);
+ if (WARN_ON(!group))
+ return NOTIFY_OK;
+
+ switch (action) {
+ case IOMMU_GROUP_NOTIFY_ADD_DEVICE:
+ vfio_group_nb_add_dev(group, dev);
+ break;
+ case IOMMU_GROUP_NOTIFY_DEL_DEVICE:
+ vfio_group_nb_del_dev(group, dev);
+ break;
+ case IOMMU_GROUP_NOTIFY_BIND_DRIVER:
+ pr_debug("%s: Device %s, group %d binding to driver\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group));
+ break;
+ case IOMMU_GROUP_NOTIFY_BOUND_DRIVER:
+ pr_debug("%s: Device %s, group %d bound to driver %s\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group), dev->driver->name);
+ BUG_ON(vfio_group_nb_verify(group, dev));
+ break;
+ case IOMMU_GROUP_NOTIFY_UNBIND_DRIVER:
+ pr_debug("%s: Device %s, group %d unbinding from driver %s\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group), dev->driver->name);
+ break;
+ case IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER:
+ pr_debug("%s: Device %s, group %d unbound from driver\n",
+ __func__, dev_name(dev),
+ iommu_group_id(group->iommu_group));
+ /*
+ * XXX An unbound device in a live group is ok, but we'd
+ * really like to avoid the above BUG_ON by preventing other
+ * drivers from binding to it. Once that occurs, we have to
+ * stop the system to maintain isolation. At a minimum, we'd
+ * want a toggle to disable driver auto probe for this device.
+ */
+ break;
+ }
+
+ vfio_group_put(group);
+ return NOTIFY_OK;
+}
+
+/**
+ * VFIO driver API
+ */
+int vfio_add_group_dev(struct device *dev,
+ const struct vfio_device_ops *ops, void *device_data)
+{
+ struct iommu_group *iommu_group;
+ struct vfio_group *group;
+ struct vfio_device *device;
+
+ iommu_group = iommu_group_get(dev);
+ if (!iommu_group)
+ return -EINVAL;
+
+ group = vfio_group_get_from_iommu(iommu_group);
+ if (!group) {
+ group = vfio_create_group(iommu_group);
+ if (IS_ERR(group)) {
+ iommu_group_put(iommu_group);
+ return PTR_ERR(group);
+ }
+ }
+
+ device = vfio_group_get_device(group, dev);
+ if (device) {
+ WARN(1, "Device %s already exists on group %d\n",
+ dev_name(dev), iommu_group_id(iommu_group));
+ vfio_device_put(device);
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return -EBUSY;
+ }
+
+ device = vfio_group_create_device(group, dev, ops, device_data);
+ if (IS_ERR(device)) {
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return PTR_ERR(device);
+ }
+
+ /*
+ * Added device holds reference to iommu_group and vfio_device
+ * (which in turn holds reference to vfio_group). Drop extra
+ * group reference used while acquiring device.
+ */
+ vfio_group_put(group);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vfio_add_group_dev);
+
+/* Test whether a struct device is present in our tracking */
+static bool vfio_dev_present(struct device *dev)
+{
+ struct iommu_group *iommu_group;
+ struct vfio_group *group;
+ struct vfio_device *device;
+
+ iommu_group = iommu_group_get(dev);
+ if (!iommu_group)
+ return false;
+
+ group = vfio_group_get_from_iommu(iommu_group);
+ if (!group) {
+ iommu_group_put(iommu_group);
+ return false;
+ }
+
+ device = vfio_group_get_device(group, dev);
+ if (!device) {
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return false;
+ }
+
+ vfio_device_put(device);
+ vfio_group_put(group);
+ iommu_group_put(iommu_group);
+ return true;
+}
+
+/*
+ * Decrement the device reference count and wait for the device to be
+ * removed. Open file descriptors for the device... */
+void *vfio_del_group_dev(struct device *dev)
+{
+ struct vfio_device *device = dev_get_drvdata(dev);
+ struct vfio_group *group = device->group;
+ struct iommu_group *iommu_group = group->iommu_group;
+ void *device_data = device->device_data;
+
+ vfio_device_put(device);
+
+ /* TODO send a signal to encourage this to be released */
+ wait_event(vfio.release_q, !vfio_dev_present(dev));
+
+ iommu_group_put(iommu_group);
+
+ return device_data;
+}
+EXPORT_SYMBOL_GPL(vfio_del_group_dev);
+
+/**
+ * VFIO base fd, /dev/vfio/vfio
+ */
+static long vfio_ioctl_check_extension(struct vfio_container *container,
+ unsigned long arg)
+{
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+ long ret = 0;
+
+ switch (arg) {
+ /* No base extensions yet */
+ default:
+ /*
+ * If no driver is set, poll all registered drivers for
+ * extensions and return the first positive result. If
+ * a driver is already set, further queries will be passed
+ * only to that driver.
+ */
+ if (!driver) {
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list,
+ vfio_next) {
+ if (!try_module_get(driver->ops->owner))
+ continue;
+
+ ret = driver->ops->ioctl(NULL,
+ VFIO_CHECK_EXTENSION,
+ arg);
+ module_put(driver->ops->owner);
+ if (ret > 0)
+ break;
+ }
+ mutex_unlock(&vfio.iommu_drivers_lock);
+ } else
+ ret = driver->ops->ioctl(container->iommu_data,
+ VFIO_CHECK_EXTENSION, arg);
+ }
+
+ return ret;
+}
+
+/* hold container->group_lock */
+static int __vfio_container_attach_groups(struct vfio_container *container,
+ struct vfio_iommu_driver *driver,
+ void *data)
+{
+ struct vfio_group *group;
+ int ret = -ENODEV;
+
+ list_for_each_entry(group, &container->group_list, container_next) {
+ ret = driver->ops->attach_group(data, group->iommu_group);
+ if (ret)
+ goto unwind;
+ }
+
+ return ret;
+
+unwind:
+ list_for_each_entry_continue_reverse(group, &container->group_list,
+ container_next) {
+ driver->ops->detach_group(data, group->iommu_group);
+ }
+
+ return ret;
+}
+
+static long vfio_ioctl_set_iommu(struct vfio_container *container,
+ unsigned long arg)
+{
+ struct vfio_iommu_driver *driver;
+ long ret = -ENODEV;
+
+ mutex_lock(&container->group_lock);
+
+ /*
+ * The container is designed to be an unprivileged interface while
+ * the group can be assigned to specific users. Therefore, only by
+ * adding a group to a container does the user get the privilege of
+ * enabling the iommu, which may allocate finite resources. There
+ * is no unset_iommu, but by removing all the groups from a container,
+ * the container is deprivileged and returns to an unset state.
+ */
+ if (list_empty(&container->group_list) || container->iommu_driver) {
+ mutex_unlock(&container->group_lock);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vfio.iommu_drivers_lock);
+ list_for_each_entry(driver, &vfio.iommu_drivers_list, vfio_next) {
+ void *data;
+
+ if (!try_module_get(driver->ops->owner))
+ continue;
+
+ /*
+ * The arg magic for SET_IOMMU is the same as CHECK_EXTENSION,
+ * so test which iommu driver reported support for this
+ * extension and call open on them. We also pass them the
+ * magic, allowing a single driver to support multiple
+ * interfaces if they'd like.
+ */
+ if (driver->ops->ioctl(NULL, VFIO_CHECK_EXTENSION, arg) <= 0) {
+ module_put(driver->ops->owner);
+ continue;
+ }
+
+ /* module reference holds the driver we're working on */
+ mutex_unlock(&vfio.iommu_drivers_lock);
+
+ data = driver->ops->open(arg);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ module_put(driver->ops->owner);
+ goto skip_drivers_unlock;
+ }
+
+ ret = __vfio_container_attach_groups(container, driver, data);
+ if (!ret) {
+ container->iommu_driver = driver;
+ container->iommu_data = data;
+ } else {
+ driver->ops->release(data);
+ module_put(driver->ops->owner);
+ }
+
+ goto skip_drivers_unlock;
+ }
+
+ mutex_unlock(&vfio.iommu_drivers_lock);
+skip_drivers_unlock:
+ mutex_unlock(&container->group_lock);
+
+ return ret;
+}
+
+static long vfio_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver;
+ void *data;
+ long ret = -EINVAL;
+
+ if (!container)
+ return ret;
+
+ driver = container->iommu_driver;
+ data = container->iommu_data;
+
+ switch (cmd) {
+ case VFIO_GET_API_VERSION:
+ ret = VFIO_API_VERSION;
+ break;
+ case VFIO_CHECK_EXTENSION:
+ ret = vfio_ioctl_check_extension(container, arg);
+ break;
+ case VFIO_SET_IOMMU:
+ ret = vfio_ioctl_set_iommu(container, arg);
+ break;
+ default:
+ if (driver) /* passthrough all unrecognized ioctls */
+ ret = driver->ops->ioctl(data, cmd, arg);
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_fops_open(struct inode *inode, struct file *filep)
+{
+ struct vfio_container *container;
+
+ container = kzalloc(sizeof(*container), GFP_KERNEL);
+ if (!container)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&container->group_list);
+ mutex_init(&container->group_lock);
+ kref_init(&container->kref);
+
+ filep->private_data = container;
+
+ return 0;
+}
+
+static int vfio_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_container *container = filep->private_data;
+
+ filep->private_data = NULL;
+
+ vfio_container_put(container);
+
+ return 0;
+}
+
+/*
+ * Once an iommu driver is set, we optionally pass read/write/mmap
+ * on to the driver, allowing management interfaces beyond ioctl.
+ */
+static ssize_t vfio_fops_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->read))
+ return -EINVAL;
+
+ return driver->ops->read(container->iommu_data, buf, count, ppos);
+}
+
+static ssize_t vfio_fops_write(struct file *filep, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->write))
+ return -EINVAL;
+
+ return driver->ops->write(container->iommu_data, buf, count, ppos);
+}
+
+static int vfio_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vfio_container *container = filep->private_data;
+ struct vfio_iommu_driver *driver = container->iommu_driver;
+
+ if (unlikely(!driver || !driver->ops->mmap))
+ return -EINVAL;
+
+ return driver->ops->mmap(container->iommu_data, vma);
+}
+
+static const struct file_operations vfio_fops = {
+ .owner = THIS_MODULE,
+ .open = vfio_fops_open,
+ .release = vfio_fops_release,
+ .read = vfio_fops_read,
+ .write = vfio_fops_write,
+ .unlocked_ioctl = vfio_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_fops_compat_ioctl,
+#endif
+ .mmap = vfio_fops_mmap,
+};
+
+/**
+ * VFIO Group fd, /dev/vfio/$GROUP
+ */
+static void __vfio_group_unset_container(struct vfio_group *group)
+{
+ struct vfio_container *container = group->container;
+ struct vfio_iommu_driver *driver;
+
+ mutex_lock(&container->group_lock);
+
+ driver = container->iommu_driver;
+ if (driver)
+ driver->ops->detach_group(container->iommu_data,
+ group->iommu_group);
+
+ group->container = NULL;
+ list_del(&group->container_next);
+
+ /* Detaching the last group deprivileges a container, remove iommu */
+ if (driver && list_empty(&container->group_list)) {
+ driver->ops->release(container->iommu_data);
+ module_put(driver->ops->owner);
+ container->iommu_driver = NULL;
+ container->iommu_data = NULL;
+ }
+
+ mutex_unlock(&container->group_lock);
+
+ vfio_container_put(container);
+}
+
+/*
+ * VFIO_GROUP_UNSET_CONTAINER should fail if there are other users or
+ * if there was no container to unset. Since the ioctl is called on
+ * the group, we know that still exists, therefore the only valid
+ * transition here is 1->0.
+ */
+static int vfio_group_unset_container(struct vfio_group *group)
+{
+ int users = atomic_cmpxchg(&group->container_users, 1, 0);
+
+ if (!users)
+ return -EINVAL;
+ if (users != 1)
+ return -EBUSY;
+
+ __vfio_group_unset_container(group);
+
+ return 0;
+}
+
+/*
+ * When removing container users, anything that removes the last user
+ * implicitly removes the group from the container. That is, if the
+ * group file descriptor is closed, as well as any device file descriptors,
+ * the group is free.
+ */
+static void vfio_group_try_dissolve_container(struct vfio_group *group)
+{
+ if (0 == atomic_dec_if_positive(&group->container_users))
+ __vfio_group_unset_container(group);
+}
+
+static int vfio_group_set_container(struct vfio_group *group, int container_fd)
+{
+ struct file *filep;
+ struct vfio_container *container;
+ struct vfio_iommu_driver *driver;
+ int ret = 0;
+
+ if (atomic_read(&group->container_users))
+ return -EINVAL;
+
+ filep = fget(container_fd);
+ if (!filep)
+ return -EBADF;
+
+ /* Sanity check, is this really our fd? */
+ if (filep->f_op != &vfio_fops) {
+ fput(filep);
+ return -EINVAL;
+ }
+
+ container = filep->private_data;
+ WARN_ON(!container); /* fget ensures we don't race vfio_release */
+
+ mutex_lock(&container->group_lock);
+
+ driver = container->iommu_driver;
+ if (driver) {
+ ret = driver->ops->attach_group(container->iommu_data,
+ group->iommu_group);
+ if (ret)
+ goto unlock_out;
+ }
+
+ group->container = container;
+ list_add(&group->container_next, &container->group_list);
+
+ /* Get a reference on the container and mark a user within the group */
+ vfio_container_get(container);
+ atomic_inc(&group->container_users);
+
+unlock_out:
+ mutex_unlock(&container->group_lock);
+ fput(filep);
+
+ return ret;
+}
+
+static bool vfio_group_viable(struct vfio_group *group)
+{
+ return (iommu_group_for_each_dev(group->iommu_group,
+ group, vfio_dev_viable) == 0);
+}
+
+static const struct file_operations vfio_device_fops;
+
+static int vfio_group_get_device_fd(struct vfio_group *group, char *buf)
+{
+ struct vfio_device *device;
+ struct file *filep;
+ int ret = -ENODEV;
+
+ if (0 == atomic_read(&group->container_users) ||
+ !group->container->iommu_driver || !vfio_group_viable(group))
+ return -EINVAL;
+
+ mutex_lock(&group->device_lock);
+ list_for_each_entry(device, &group->device_list, group_next) {
+ if (strcmp(dev_name(device->dev), buf))
+ continue;
+
+ ret = device->ops->open(device->device_data);
+ if (ret)
+ break;
+ /*
+ * We can't use anon_inode_getfd() because we need to modify
+ * the f_mode flags directly to allow more than just ioctls
+ */
+ ret = get_unused_fd();
+ if (ret < 0) {
+ device->ops->release(device->device_data);
+ break;
+ }
+
+ filep = anon_inode_getfile("[vfio-device]", &vfio_device_fops,
+ device, O_RDWR);
+ if (IS_ERR(filep)) {
+ put_unused_fd(ret);
+ ret = PTR_ERR(filep);
+ device->ops->release(device->device_data);
+ break;
+ }
+
+ /*
+ * TODO: add an anon_inode interface to do this.
+ * Appears to be missing by lack of need rather than
+ * explicitly prevented. Now there's need.
+ */
+ filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+
+ vfio_device_get(device);
+ atomic_inc(&group->container_users);
+
+ fd_install(ret, filep);
+ break;
+ }
+ mutex_unlock(&group->device_lock);
+
+ return ret;
+}
+
+static long vfio_group_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_group *group = filep->private_data;
+ long ret = -ENOTTY;
+
+ switch (cmd) {
+ case VFIO_GROUP_GET_STATUS:
+ {
+ struct vfio_group_status status;
+ unsigned long minsz;
+
+ minsz = offsetofend(struct vfio_group_status, flags);
+
+ if (copy_from_user(&status, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (status.argsz < minsz)
+ return -EINVAL;
+
+ status.flags = 0;
+
+ if (vfio_group_viable(group))
+ status.flags |= VFIO_GROUP_FLAGS_VIABLE;
+
+ if (group->container)
+ status.flags |= VFIO_GROUP_FLAGS_CONTAINER_SET;
+
+ if (copy_to_user((void __user *)arg, &status, minsz))
+ return -EFAULT;
+
+ ret = 0;
+ break;
+ }
+ case VFIO_GROUP_SET_CONTAINER:
+ {
+ int fd;
+
+ if (get_user(fd, (int __user *)arg))
+ return -EFAULT;
+
+ if (fd < 0)
+ return -EINVAL;
+
+ ret = vfio_group_set_container(group, fd);
+ break;
+ }
+ case VFIO_GROUP_UNSET_CONTAINER:
+ ret = vfio_group_unset_container(group);
+ break;
+ case VFIO_GROUP_GET_DEVICE_FD:
+ {
+ char *buf;
+
+ buf = strndup_user((const char __user *)arg, PAGE_SIZE);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = vfio_group_get_device_fd(group, buf);
+ kfree(buf);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_group_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_group_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static int vfio_group_fops_open(struct inode *inode, struct file *filep)
+{
+ struct vfio_group *group;
+
+ group = vfio_group_get_from_minor(iminor(inode));
+ if (!group)
+ return -ENODEV;
+
+ if (group->container) {
+ vfio_group_put(group);
+ return -EBUSY;
+ }
+
+ filep->private_data = group;
+
+ return 0;
+}
+
+static int vfio_group_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_group *group = filep->private_data;
+
+ filep->private_data = NULL;
+
+ vfio_group_try_dissolve_container(group);
+
+ vfio_group_put(group);
+
+ return 0;
+}
+
+static const struct file_operations vfio_group_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = vfio_group_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_group_fops_compat_ioctl,
+#endif
+ .open = vfio_group_fops_open,
+ .release = vfio_group_fops_release,
+};
+
+/**
+ * VFIO Device fd
+ */
+static int vfio_device_fops_release(struct inode *inode, struct file *filep)
+{
+ struct vfio_device *device = filep->private_data;
+
+ device->ops->release(device->device_data);
+
+ vfio_group_try_dissolve_container(device->group);
+
+ vfio_device_put(device);
+
+ return 0;
+}
+
+static long vfio_device_fops_unl_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->ioctl))
+ return -EINVAL;
+
+ return device->ops->ioctl(device->device_data, cmd, arg);
+}
+
+static ssize_t vfio_device_fops_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->read))
+ return -EINVAL;
+
+ return device->ops->read(device->device_data, buf, count, ppos);
+}
+
+static ssize_t vfio_device_fops_write(struct file *filep,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->write))
+ return -EINVAL;
+
+ return device->ops->write(device->device_data, buf, count, ppos);
+}
+
+static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma)
+{
+ struct vfio_device *device = filep->private_data;
+
+ if (unlikely(!device->ops->mmap))
+ return -EINVAL;
+
+ return device->ops->mmap(device->device_data, vma);
+}
+
+#ifdef CONFIG_COMPAT
+static long vfio_device_fops_compat_ioctl(struct file *filep,
+ unsigned int cmd, unsigned long arg)
+{
+ arg = (unsigned long)compat_ptr(arg);
+ return vfio_device_fops_unl_ioctl(filep, cmd, arg);
+}
+#endif /* CONFIG_COMPAT */
+
+static const struct file_operations vfio_device_fops = {
+ .owner = THIS_MODULE,
+ .release = vfio_device_fops_release,
+ .read = vfio_device_fops_read,
+ .write = vfio_device_fops_write,
+ .unlocked_ioctl = vfio_device_fops_unl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vfio_device_fops_compat_ioctl,
+#endif
+ .mmap = vfio_device_fops_mmap,
+};
+
+/**
+ * Module/class support
+ */
+static char *vfio_devnode(struct device *dev, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "vfio/%s", dev_name(dev));
+}
+
+static int __init vfio_init(void)
+{
+ int ret;
+
+ idr_init(&vfio.group_idr);
+ mutex_init(&vfio.group_lock);
+ mutex_init(&vfio.iommu_drivers_lock);
+ INIT_LIST_HEAD(&vfio.group_list);
+ INIT_LIST_HEAD(&vfio.iommu_drivers_list);
+ init_waitqueue_head(&vfio.release_q);
+
+ vfio.class = class_create(THIS_MODULE, "vfio");
+ if (IS_ERR(vfio.class)) {
+ ret = PTR_ERR(vfio.class);
+ goto err_class;
+ }
+
+ vfio.class->devnode = vfio_devnode;
+
+ ret = alloc_chrdev_region(&vfio.devt, 0, MINORMASK, "vfio");
+ if (ret)
+ goto err_base_chrdev;
+
+ cdev_init(&vfio.cdev, &vfio_fops);
+ ret = cdev_add(&vfio.cdev, vfio.devt, 1);
+ if (ret)
+ goto err_base_cdev;
+
+ vfio.dev = device_create(vfio.class, NULL, vfio.devt, NULL, "vfio");
+ if (IS_ERR(vfio.dev)) {
+ ret = PTR_ERR(vfio.dev);
+ goto err_base_dev;
+ }
+
+ /* /dev/vfio/$GROUP */
+ cdev_init(&vfio.group_cdev, &vfio_group_fops);
+ ret = cdev_add(&vfio.group_cdev,
+ MKDEV(MAJOR(vfio.devt), 1), MINORMASK - 1);
+ if (ret)
+ goto err_groups_cdev;
+
+ pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+ /*
+ * Attempt to load known iommu-drivers. This gives us a working
+ * environment without the user needing to explicitly load iommu
+ * drivers.
+ */
+ request_module_nowait("vfio_iommu_type1");
+
+ return 0;
+
+err_groups_cdev:
+ device_destroy(vfio.class, vfio.devt);
+err_base_dev:
+ cdev_del(&vfio.cdev);
+err_base_cdev:
+ unregister_chrdev_region(vfio.devt, MINORMASK);
+err_base_chrdev:
+ class_destroy(vfio.class);
+ vfio.class = NULL;
+err_class:
+ return ret;
+}
+
+static void __exit vfio_cleanup(void)
+{
+ WARN_ON(!list_empty(&vfio.group_list));
+
+ idr_destroy(&vfio.group_idr);
+ cdev_del(&vfio.group_cdev);
+ device_destroy(vfio.class, vfio.devt);
+ cdev_del(&vfio.cdev);
+ unregister_chrdev_region(vfio.devt, MINORMASK);
+ class_destroy(vfio.class);
+ vfio.class = NULL;
+}
+
+module_init(vfio_init);
+module_exit(vfio_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
new file mode 100644
index 000000000000..6f3fbc48a6c7
--- /dev/null
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -0,0 +1,753 @@
+/*
+ * VFIO: IOMMU DMA mapping support for Type1 IOMMU
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ *
+ * Derived from original vfio:
+ * Copyright 2010 Cisco Systems, Inc. All rights reserved.
+ * Author: Tom Lyon, pugs@cisco.com
+ *
+ * We arbitrarily define a Type1 IOMMU as one matching the below code.
+ * It could be called the x86 IOMMU as it's designed for AMD-Vi & Intel
+ * VT-d, but that makes it harder to re-use as theoretically anyone
+ * implementing a similar IOMMU could make use of this. We expect the
+ * IOMMU to support the IOMMU API and have few to no restrictions around
+ * the IOVA range that can be mapped. The Type1 IOMMU is currently
+ * optimized for relatively static mappings of a userspace process with
+ * userpsace pages pinned into memory. We also assume devices and IOMMU
+ * domains are PCI based as the IOMMU API is still centered around a
+ * device/bus interface rather than a group interface.
+ */
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pci.h> /* pci_bus_type */
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vfio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_VERSION "0.2"
+#define DRIVER_AUTHOR "Alex Williamson <alex.williamson@redhat.com>"
+#define DRIVER_DESC "Type1 IOMMU driver for VFIO"
+
+static bool allow_unsafe_interrupts;
+module_param_named(allow_unsafe_interrupts,
+ allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(allow_unsafe_interrupts,
+ "Enable VFIO IOMMU support for on platforms without interrupt remapping support.");
+
+struct vfio_iommu {
+ struct iommu_domain *domain;
+ struct mutex lock;
+ struct list_head dma_list;
+ struct list_head group_list;
+ bool cache;
+};
+
+struct vfio_dma {
+ struct list_head next;
+ dma_addr_t iova; /* Device address */
+ unsigned long vaddr; /* Process virtual addr */
+ long npage; /* Number of pages */
+ int prot; /* IOMMU_READ/WRITE */
+};
+
+struct vfio_group {
+ struct iommu_group *iommu_group;
+ struct list_head next;
+};
+
+/*
+ * This code handles mapping and unmapping of user data buffers
+ * into DMA'ble space using the IOMMU
+ */
+
+#define NPAGE_TO_SIZE(npage) ((size_t)(npage) << PAGE_SHIFT)
+
+struct vwork {
+ struct mm_struct *mm;
+ long npage;
+ struct work_struct work;
+};
+
+/* delayed decrement/increment for locked_vm */
+static void vfio_lock_acct_bg(struct work_struct *work)
+{
+ struct vwork *vwork = container_of(work, struct vwork, work);
+ struct mm_struct *mm;
+
+ mm = vwork->mm;
+ down_write(&mm->mmap_sem);
+ mm->locked_vm += vwork->npage;
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ kfree(vwork);
+}
+
+static void vfio_lock_acct(long npage)
+{
+ struct vwork *vwork;
+ struct mm_struct *mm;
+
+ if (!current->mm)
+ return; /* process exited */
+
+ if (down_write_trylock(&current->mm->mmap_sem)) {
+ current->mm->locked_vm += npage;
+ up_write(&current->mm->mmap_sem);
+ return;
+ }
+
+ /*
+ * Couldn't get mmap_sem lock, so must setup to update
+ * mm->locked_vm later. If locked_vm were atomic, we
+ * wouldn't need this silliness
+ */
+ vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
+ if (!vwork)
+ return;
+ mm = get_task_mm(current);
+ if (!mm) {
+ kfree(vwork);
+ return;
+ }
+ INIT_WORK(&vwork->work, vfio_lock_acct_bg);
+ vwork->mm = mm;
+ vwork->npage = npage;
+ schedule_work(&vwork->work);
+}
+
+/*
+ * Some mappings aren't backed by a struct page, for example an mmap'd
+ * MMIO range for our own or another device. These use a different
+ * pfn conversion and shouldn't be tracked as locked pages.
+ */
+static bool is_invalid_reserved_pfn(unsigned long pfn)
+{
+ if (pfn_valid(pfn)) {
+ bool reserved;
+ struct page *tail = pfn_to_page(pfn);
+ struct page *head = compound_trans_head(tail);
+ reserved = !!(PageReserved(head));
+ if (head != tail) {
+ /*
+ * "head" is not a dangling pointer
+ * (compound_trans_head takes care of that)
+ * but the hugepage may have been split
+ * from under us (and we may not hold a
+ * reference count on the head page so it can
+ * be reused before we run PageReferenced), so
+ * we've to check PageTail before returning
+ * what we just read.
+ */
+ smp_rmb();
+ if (PageTail(tail))
+ return reserved;
+ }
+ return PageReserved(tail);
+ }
+
+ return true;
+}
+
+static int put_pfn(unsigned long pfn, int prot)
+{
+ if (!is_invalid_reserved_pfn(pfn)) {
+ struct page *page = pfn_to_page(pfn);
+ if (prot & IOMMU_WRITE)
+ SetPageDirty(page);
+ put_page(page);
+ return 1;
+ }
+ return 0;
+}
+
+/* Unmap DMA region */
+static long __vfio_dma_do_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+ long npage, int prot)
+{
+ long i, unlocked = 0;
+
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE) {
+ unsigned long pfn;
+
+ pfn = iommu_iova_to_phys(iommu->domain, iova) >> PAGE_SHIFT;
+ if (pfn) {
+ iommu_unmap(iommu->domain, iova, PAGE_SIZE);
+ unlocked += put_pfn(pfn, prot);
+ }
+ }
+ return unlocked;
+}
+
+static void vfio_dma_unmap(struct vfio_iommu *iommu, dma_addr_t iova,
+ long npage, int prot)
+{
+ long unlocked;
+
+ unlocked = __vfio_dma_do_unmap(iommu, iova, npage, prot);
+ vfio_lock_acct(-unlocked);
+}
+
+static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn)
+{
+ struct page *page[1];
+ struct vm_area_struct *vma;
+ int ret = -EFAULT;
+
+ if (get_user_pages_fast(vaddr, 1, !!(prot & IOMMU_WRITE), page) == 1) {
+ *pfn = page_to_pfn(page[0]);
+ return 0;
+ }
+
+ down_read(&current->mm->mmap_sem);
+
+ vma = find_vma_intersection(current->mm, vaddr, vaddr + 1);
+
+ if (vma && vma->vm_flags & VM_PFNMAP) {
+ *pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ if (is_invalid_reserved_pfn(*pfn))
+ ret = 0;
+ }
+
+ up_read(&current->mm->mmap_sem);
+
+ return ret;
+}
+
+/* Map DMA region */
+static int __vfio_dma_map(struct vfio_iommu *iommu, dma_addr_t iova,
+ unsigned long vaddr, long npage, int prot)
+{
+ dma_addr_t start = iova;
+ long i, locked = 0;
+ int ret;
+
+ /* Verify that pages are not already mapped */
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE)
+ if (iommu_iova_to_phys(iommu->domain, iova))
+ return -EBUSY;
+
+ iova = start;
+
+ if (iommu->cache)
+ prot |= IOMMU_CACHE;
+
+ /*
+ * XXX We break mappings into pages and use get_user_pages_fast to
+ * pin the pages in memory. It's been suggested that mlock might
+ * provide a more efficient mechanism, but nothing prevents the
+ * user from munlocking the pages, which could then allow the user
+ * access to random host memory. We also have no guarantee from the
+ * IOMMU API that the iommu driver can unmap sub-pages of previous
+ * mappings. This means we might lose an entire range if a single
+ * page within it is unmapped. Single page mappings are inefficient,
+ * but provide the most flexibility for now.
+ */
+ for (i = 0; i < npage; i++, iova += PAGE_SIZE, vaddr += PAGE_SIZE) {
+ unsigned long pfn = 0;
+
+ ret = vaddr_get_pfn(vaddr, prot, &pfn);
+ if (ret) {
+ __vfio_dma_do_unmap(iommu, start, i, prot);
+ return ret;
+ }
+
+ /*
+ * Only add actual locked pages to accounting
+ * XXX We're effectively marking a page locked for every
+ * IOVA page even though it's possible the user could be
+ * backing multiple IOVAs with the same vaddr. This over-
+ * penalizes the user process, but we currently have no
+ * easy way to do this properly.
+ */
+ if (!is_invalid_reserved_pfn(pfn))
+ locked++;
+
+ ret = iommu_map(iommu->domain, iova,
+ (phys_addr_t)pfn << PAGE_SHIFT,
+ PAGE_SIZE, prot);
+ if (ret) {
+ /* Back out mappings on error */
+ put_pfn(pfn, prot);
+ __vfio_dma_do_unmap(iommu, start, i, prot);
+ return ret;
+ }
+ }
+ vfio_lock_acct(locked);
+ return 0;
+}
+
+static inline bool ranges_overlap(dma_addr_t start1, size_t size1,
+ dma_addr_t start2, size_t size2)
+{
+ if (start1 < start2)
+ return (start2 - start1 < size1);
+ else if (start2 < start1)
+ return (start1 - start2 < size2);
+ return (size1 > 0 && size2 > 0);
+}
+
+static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
+ dma_addr_t start, size_t size)
+{
+ struct vfio_dma *dma;
+
+ list_for_each_entry(dma, &iommu->dma_list, next) {
+ if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+ start, size))
+ return dma;
+ }
+ return NULL;
+}
+
+static long vfio_remove_dma_overlap(struct vfio_iommu *iommu, dma_addr_t start,
+ size_t size, struct vfio_dma *dma)
+{
+ struct vfio_dma *split;
+ long npage_lo, npage_hi;
+
+ /* Existing dma region is completely covered, unmap all */
+ if (start <= dma->iova &&
+ start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+ vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+ list_del(&dma->next);
+ npage_lo = dma->npage;
+ kfree(dma);
+ return npage_lo;
+ }
+
+ /* Overlap low address of existing range */
+ if (start <= dma->iova) {
+ size_t overlap;
+
+ overlap = start + size - dma->iova;
+ npage_lo = overlap >> PAGE_SHIFT;
+
+ vfio_dma_unmap(iommu, dma->iova, npage_lo, dma->prot);
+ dma->iova += overlap;
+ dma->vaddr += overlap;
+ dma->npage -= npage_lo;
+ return npage_lo;
+ }
+
+ /* Overlap high address of existing range */
+ if (start + size >= dma->iova + NPAGE_TO_SIZE(dma->npage)) {
+ size_t overlap;
+
+ overlap = dma->iova + NPAGE_TO_SIZE(dma->npage) - start;
+ npage_hi = overlap >> PAGE_SHIFT;
+
+ vfio_dma_unmap(iommu, start, npage_hi, dma->prot);
+ dma->npage -= npage_hi;
+ return npage_hi;
+ }
+
+ /* Split existing */
+ npage_lo = (start - dma->iova) >> PAGE_SHIFT;
+ npage_hi = dma->npage - (size >> PAGE_SHIFT) - npage_lo;
+
+ split = kzalloc(sizeof *split, GFP_KERNEL);
+ if (!split)
+ return -ENOMEM;
+
+ vfio_dma_unmap(iommu, start, size >> PAGE_SHIFT, dma->prot);
+
+ dma->npage = npage_lo;
+
+ split->npage = npage_hi;
+ split->iova = start + size;
+ split->vaddr = dma->vaddr + NPAGE_TO_SIZE(npage_lo) + size;
+ split->prot = dma->prot;
+ list_add(&split->next, &iommu->dma_list);
+ return size >> PAGE_SHIFT;
+}
+
+static int vfio_dma_do_unmap(struct vfio_iommu *iommu,
+ struct vfio_iommu_type1_dma_unmap *unmap)
+{
+ long ret = 0, npage = unmap->size >> PAGE_SHIFT;
+ struct vfio_dma *dma, *tmp;
+ uint64_t mask;
+
+ mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+ if (unmap->iova & mask)
+ return -EINVAL;
+ if (unmap->size & mask)
+ return -EINVAL;
+
+ /* XXX We still break these down into PAGE_SIZE */
+ WARN_ON(mask & PAGE_MASK);
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry_safe(dma, tmp, &iommu->dma_list, next) {
+ if (ranges_overlap(dma->iova, NPAGE_TO_SIZE(dma->npage),
+ unmap->iova, unmap->size)) {
+ ret = vfio_remove_dma_overlap(iommu, unmap->iova,
+ unmap->size, dma);
+ if (ret > 0)
+ npage -= ret;
+ if (ret < 0 || npage == 0)
+ break;
+ }
+ }
+ mutex_unlock(&iommu->lock);
+ return ret > 0 ? 0 : (int)ret;
+}
+
+static int vfio_dma_do_map(struct vfio_iommu *iommu,
+ struct vfio_iommu_type1_dma_map *map)
+{
+ struct vfio_dma *dma, *pdma = NULL;
+ dma_addr_t iova = map->iova;
+ unsigned long locked, lock_limit, vaddr = map->vaddr;
+ size_t size = map->size;
+ int ret = 0, prot = 0;
+ uint64_t mask;
+ long npage;
+
+ mask = ((uint64_t)1 << __ffs(iommu->domain->ops->pgsize_bitmap)) - 1;
+
+ /* READ/WRITE from device perspective */
+ if (map->flags & VFIO_DMA_MAP_FLAG_WRITE)
+ prot |= IOMMU_WRITE;
+ if (map->flags & VFIO_DMA_MAP_FLAG_READ)
+ prot |= IOMMU_READ;
+
+ if (!prot)
+ return -EINVAL; /* No READ/WRITE? */
+
+ if (vaddr & mask)
+ return -EINVAL;
+ if (iova & mask)
+ return -EINVAL;
+ if (size & mask)
+ return -EINVAL;
+
+ /* XXX We still break these down into PAGE_SIZE */
+ WARN_ON(mask & PAGE_MASK);
+
+ /* Don't allow IOVA wrap */
+ if (iova + size && iova + size < iova)
+ return -EINVAL;
+
+ /* Don't allow virtual address wrap */
+ if (vaddr + size && vaddr + size < vaddr)
+ return -EINVAL;
+
+ npage = size >> PAGE_SHIFT;
+ if (!npage)
+ return -EINVAL;
+
+ mutex_lock(&iommu->lock);
+
+ if (vfio_find_dma(iommu, iova, size)) {
+ ret = -EBUSY;
+ goto out_lock;
+ }
+
+ /* account for locked pages */
+ locked = current->mm->locked_vm + npage;
+ lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+ if (locked > lock_limit && !capable(CAP_IPC_LOCK)) {
+ pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
+ __func__, rlimit(RLIMIT_MEMLOCK));
+ ret = -ENOMEM;
+ goto out_lock;
+ }
+
+ ret = __vfio_dma_map(iommu, iova, vaddr, npage, prot);
+ if (ret)
+ goto out_lock;
+
+ /* Check if we abut a region below - nothing below 0 */
+ if (iova) {
+ dma = vfio_find_dma(iommu, iova - 1, 1);
+ if (dma && dma->prot == prot &&
+ dma->vaddr + NPAGE_TO_SIZE(dma->npage) == vaddr) {
+
+ dma->npage += npage;
+ iova = dma->iova;
+ vaddr = dma->vaddr;
+ npage = dma->npage;
+ size = NPAGE_TO_SIZE(npage);
+
+ pdma = dma;
+ }
+ }
+
+ /* Check if we abut a region above - nothing above ~0 + 1 */
+ if (iova + size) {
+ dma = vfio_find_dma(iommu, iova + size, 1);
+ if (dma && dma->prot == prot &&
+ dma->vaddr == vaddr + size) {
+
+ dma->npage += npage;
+ dma->iova = iova;
+ dma->vaddr = vaddr;
+
+ /*
+ * If merged above and below, remove previously
+ * merged entry. New entry covers it.
+ */
+ if (pdma) {
+ list_del(&pdma->next);
+ kfree(pdma);
+ }
+ pdma = dma;
+ }
+ }
+
+ /* Isolated, new region */
+ if (!pdma) {
+ dma = kzalloc(sizeof *dma, GFP_KERNEL);
+ if (!dma) {
+ ret = -ENOMEM;
+ vfio_dma_unmap(iommu, iova, npage, prot);
+ goto out_lock;
+ }
+
+ dma->npage = npage;
+ dma->iova = iova;
+ dma->vaddr = vaddr;
+ dma->prot = prot;
+ list_add(&dma->next, &iommu->dma_list);
+ }
+
+out_lock:
+ mutex_unlock(&iommu->lock);
+ return ret;
+}
+
+static int vfio_iommu_type1_attach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group, *tmp;
+ int ret;
+
+ group = kzalloc(sizeof(*group), GFP_KERNEL);
+ if (!group)
+ return -ENOMEM;
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry(tmp, &iommu->group_list, next) {
+ if (tmp->iommu_group == iommu_group) {
+ mutex_unlock(&iommu->lock);
+ kfree(group);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * TODO: Domain have capabilities that might change as we add
+ * groups (see iommu->cache, currently never set). Check for
+ * them and potentially disallow groups to be attached when it
+ * would change capabilities (ugh).
+ */
+ ret = iommu_attach_group(iommu->domain, iommu_group);
+ if (ret) {
+ mutex_unlock(&iommu->lock);
+ kfree(group);
+ return ret;
+ }
+
+ group->iommu_group = iommu_group;
+ list_add(&group->next, &iommu->group_list);
+
+ mutex_unlock(&iommu->lock);
+
+ return 0;
+}
+
+static void vfio_iommu_type1_detach_group(void *iommu_data,
+ struct iommu_group *iommu_group)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group;
+
+ mutex_lock(&iommu->lock);
+
+ list_for_each_entry(group, &iommu->group_list, next) {
+ if (group->iommu_group == iommu_group) {
+ iommu_detach_group(iommu->domain, iommu_group);
+ list_del(&group->next);
+ kfree(group);
+ break;
+ }
+ }
+
+ mutex_unlock(&iommu->lock);
+}
+
+static void *vfio_iommu_type1_open(unsigned long arg)
+{
+ struct vfio_iommu *iommu;
+
+ if (arg != VFIO_TYPE1_IOMMU)
+ return ERR_PTR(-EINVAL);
+
+ iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
+ if (!iommu)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&iommu->group_list);
+ INIT_LIST_HEAD(&iommu->dma_list);
+ mutex_init(&iommu->lock);
+
+ /*
+ * Wish we didn't have to know about bus_type here.
+ */
+ iommu->domain = iommu_domain_alloc(&pci_bus_type);
+ if (!iommu->domain) {
+ kfree(iommu);
+ return ERR_PTR(-EIO);
+ }
+
+ /*
+ * Wish we could specify required capabilities rather than create
+ * a domain, see what comes out and hope it doesn't change along
+ * the way. Fortunately we know interrupt remapping is global for
+ * our iommus.
+ */
+ if (!allow_unsafe_interrupts &&
+ !iommu_domain_has_cap(iommu->domain, IOMMU_CAP_INTR_REMAP)) {
+ pr_warn("%s: No interrupt remapping support. Use the module param \"allow_unsafe_interrupts\" to enable VFIO IOMMU support on this platform\n",
+ __func__);
+ iommu_domain_free(iommu->domain);
+ kfree(iommu);
+ return ERR_PTR(-EPERM);
+ }
+
+ return iommu;
+}
+
+static void vfio_iommu_type1_release(void *iommu_data)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ struct vfio_group *group, *group_tmp;
+ struct vfio_dma *dma, *dma_tmp;
+
+ list_for_each_entry_safe(group, group_tmp, &iommu->group_list, next) {
+ iommu_detach_group(iommu->domain, group->iommu_group);
+ list_del(&group->next);
+ kfree(group);
+ }
+
+ list_for_each_entry_safe(dma, dma_tmp, &iommu->dma_list, next) {
+ vfio_dma_unmap(iommu, dma->iova, dma->npage, dma->prot);
+ list_del(&dma->next);
+ kfree(dma);
+ }
+
+ iommu_domain_free(iommu->domain);
+ iommu->domain = NULL;
+ kfree(iommu);
+}
+
+static long vfio_iommu_type1_ioctl(void *iommu_data,
+ unsigned int cmd, unsigned long arg)
+{
+ struct vfio_iommu *iommu = iommu_data;
+ unsigned long minsz;
+
+ if (cmd == VFIO_CHECK_EXTENSION) {
+ switch (arg) {
+ case VFIO_TYPE1_IOMMU:
+ return 1;
+ default:
+ return 0;
+ }
+ } else if (cmd == VFIO_IOMMU_GET_INFO) {
+ struct vfio_iommu_type1_info info;
+
+ minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes);
+
+ if (copy_from_user(&info, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ info.flags = 0;
+
+ info.iova_pgsizes = iommu->domain->ops->pgsize_bitmap;
+
+ return copy_to_user((void __user *)arg, &info, minsz);
+
+ } else if (cmd == VFIO_IOMMU_MAP_DMA) {
+ struct vfio_iommu_type1_dma_map map;
+ uint32_t mask = VFIO_DMA_MAP_FLAG_READ |
+ VFIO_DMA_MAP_FLAG_WRITE;
+
+ minsz = offsetofend(struct vfio_iommu_type1_dma_map, size);
+
+ if (copy_from_user(&map, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (map.argsz < minsz || map.flags & ~mask)
+ return -EINVAL;
+
+ return vfio_dma_do_map(iommu, &map);
+
+ } else if (cmd == VFIO_IOMMU_UNMAP_DMA) {
+ struct vfio_iommu_type1_dma_unmap unmap;
+
+ minsz = offsetofend(struct vfio_iommu_type1_dma_unmap, size);
+
+ if (copy_from_user(&unmap, (void __user *)arg, minsz))
+ return -EFAULT;
+
+ if (unmap.argsz < minsz || unmap.flags)
+ return -EINVAL;
+
+ return vfio_dma_do_unmap(iommu, &unmap);
+ }
+
+ return -ENOTTY;
+}
+
+static const struct vfio_iommu_driver_ops vfio_iommu_driver_ops_type1 = {
+ .name = "vfio-iommu-type1",
+ .owner = THIS_MODULE,
+ .open = vfio_iommu_type1_open,
+ .release = vfio_iommu_type1_release,
+ .ioctl = vfio_iommu_type1_ioctl,
+ .attach_group = vfio_iommu_type1_attach_group,
+ .detach_group = vfio_iommu_type1_detach_group,
+};
+
+static int __init vfio_iommu_type1_init(void)
+{
+ if (!iommu_present(&pci_bus_type))
+ return -ENODEV;
+
+ return vfio_register_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+static void __exit vfio_iommu_type1_cleanup(void)
+{
+ vfio_unregister_iommu_driver(&vfio_iommu_driver_ops_type1);
+}
+
+module_init(vfio_iommu_type1_init);
+module_exit(vfio_iommu_type1_cleanup);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index e4e2fd1b5107..202bba6c997c 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -9,3 +9,6 @@ config VHOST_NET
To compile this driver as a module, choose M here: the module will
be called vhost_net.
+if STAGING
+source "drivers/vhost/Kconfig.tcm"
+endif
diff --git a/drivers/vhost/Kconfig.tcm b/drivers/vhost/Kconfig.tcm
new file mode 100644
index 000000000000..a9c6f76e3208
--- /dev/null
+++ b/drivers/vhost/Kconfig.tcm
@@ -0,0 +1,6 @@
+config TCM_VHOST
+ tristate "TCM_VHOST fabric module (EXPERIMENTAL)"
+ depends on TARGET_CORE && EVENTFD && EXPERIMENTAL && m
+ default n
+ ---help---
+ Say M here to enable the TCM_VHOST fabric module for use with virtio-scsi guests
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
index 72dd02050bb9..a27b053bc9ab 100644
--- a/drivers/vhost/Makefile
+++ b/drivers/vhost/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_VHOST_NET) += vhost_net.o
vhost_net-y := vhost.o net.o
+
+obj-$(CONFIG_TCM_VHOST) += tcm_vhost.o
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c
new file mode 100644
index 000000000000..ed8e2e6c8df2
--- /dev/null
+++ b/drivers/vhost/tcm_vhost.c
@@ -0,0 +1,1649 @@
+/*******************************************************************************
+ * Vhost kernel TCM fabric driver for virtio SCSI initiators
+ *
+ * (C) Copyright 2010-2012 RisingTide Systems LLC.
+ * (C) Copyright 2010-2012 IBM Corp.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Authors: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/compat.h>
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h> /* TODO vhost.h currently depends on this */
+#include <linux/virtio_scsi.h>
+
+#include "vhost.c"
+#include "vhost.h"
+#include "tcm_vhost.h"
+
+enum {
+ VHOST_SCSI_VQ_CTL = 0,
+ VHOST_SCSI_VQ_EVT = 1,
+ VHOST_SCSI_VQ_IO = 2,
+};
+
+struct vhost_scsi {
+ struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */
+ struct vhost_dev dev;
+ struct vhost_virtqueue vqs[3];
+
+ struct vhost_work vs_completion_work; /* cmd completion work item */
+ struct list_head vs_completion_list; /* cmd completion queue */
+ spinlock_t vs_completion_lock; /* protects s_completion_list */
+};
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *tcm_vhost_fabric_configfs;
+
+static struct workqueue_struct *tcm_vhost_workqueue;
+
+/* Global spinlock to protect tcm_vhost TPG list for vhost IOCTL access */
+static DEFINE_MUTEX(tcm_vhost_mutex);
+static LIST_HEAD(tcm_vhost_list);
+
+static int tcm_vhost_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int tcm_vhost_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static char *tcm_vhost_get_fabric_name(void)
+{
+ return "vhost";
+}
+
+static u8 tcm_vhost_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_fabric_proto_ident(se_tpg);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_fabric_proto_ident(se_tpg);
+}
+
+static char *tcm_vhost_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+static u16 tcm_vhost_get_tag(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+static u32 tcm_vhost_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+}
+
+static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+}
+
+static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_FCP:
+ return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+}
+
+static struct se_node_acl *tcm_vhost_alloc_fabric_acl(
+ struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct tcm_vhost_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to alocate struct tcm_vhost_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+static void tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct tcm_vhost_nacl *nacl = container_of(se_nacl,
+ struct tcm_vhost_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+static u32 tcm_vhost_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static int tcm_vhost_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void tcm_vhost_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+static u32 tcm_vhost_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static int tcm_vhost_write_pending(struct se_cmd *se_cmd)
+{
+ /* Go ahead and process the write immediately */
+ target_execute_cmd(se_cmd);
+ return 0;
+}
+
+static int tcm_vhost_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void tcm_vhost_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+static u32 tcm_vhost_get_task_tag(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int tcm_vhost_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd)
+{
+ struct vhost_scsi *vs = tv_cmd->tvc_vhost;
+
+ spin_lock_bh(&vs->vs_completion_lock);
+ list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list);
+ spin_unlock_bh(&vs->vs_completion_lock);
+
+ vhost_work_queue(&vs->dev, &vs->vs_completion_work);
+}
+
+static int tcm_vhost_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd, tvc_se_cmd);
+ vhost_scsi_complete_cmd(tv_cmd);
+ return 0;
+}
+
+static int tcm_vhost_queue_status(struct se_cmd *se_cmd)
+{
+ struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd, tvc_se_cmd);
+ vhost_scsi_complete_cmd(tv_cmd);
+ return 0;
+}
+
+static int tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static u16 tcm_vhost_set_fabric_sense_len(struct se_cmd *se_cmd,
+ u32 sense_length)
+{
+ return 0;
+}
+
+static u16 tcm_vhost_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
+{
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+
+ /* TODO locking against target/backend threads? */
+ transport_generic_free_cmd(se_cmd, 1);
+
+ if (tv_cmd->tvc_sgl_count) {
+ u32 i;
+ for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&tv_cmd->tvc_sgl[i]));
+
+ kfree(tv_cmd->tvc_sgl);
+ }
+
+ kfree(tv_cmd);
+}
+
+/* Dequeue a command from the completion list */
+static struct tcm_vhost_cmd *vhost_scsi_get_cmd_from_completion(
+ struct vhost_scsi *vs)
+{
+ struct tcm_vhost_cmd *tv_cmd = NULL;
+
+ spin_lock_bh(&vs->vs_completion_lock);
+ if (list_empty(&vs->vs_completion_list)) {
+ spin_unlock_bh(&vs->vs_completion_lock);
+ return NULL;
+ }
+
+ list_for_each_entry(tv_cmd, &vs->vs_completion_list,
+ tvc_completion_list) {
+ list_del(&tv_cmd->tvc_completion_list);
+ break;
+ }
+ spin_unlock_bh(&vs->vs_completion_lock);
+ return tv_cmd;
+}
+
+/* Fill in status and signal that we are done processing this command
+ *
+ * This is scheduled in the vhost work queue so we are called with the owner
+ * process mm and can access the vring.
+ */
+static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
+{
+ struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
+ vs_completion_work);
+ struct tcm_vhost_cmd *tv_cmd;
+
+ while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs))) {
+ struct virtio_scsi_cmd_resp v_rsp;
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ int ret;
+
+ pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__,
+ tv_cmd, se_cmd->residual_count, se_cmd->scsi_status);
+
+ memset(&v_rsp, 0, sizeof(v_rsp));
+ v_rsp.resid = se_cmd->residual_count;
+ /* TODO is status_qualifier field needed? */
+ v_rsp.status = se_cmd->scsi_status;
+ v_rsp.sense_len = se_cmd->scsi_sense_length;
+ memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf,
+ v_rsp.sense_len);
+ ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp));
+ if (likely(ret == 0))
+ vhost_add_used(&vs->vqs[2], tv_cmd->tvc_vq_desc, 0);
+ else
+ pr_err("Faulted on virtio_scsi_cmd_resp\n");
+
+ vhost_scsi_free_cmd(tv_cmd);
+ }
+
+ vhost_signal(&vs->dev, &vs->vqs[2]);
+}
+
+static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd(
+ struct tcm_vhost_tpg *tv_tpg,
+ struct virtio_scsi_cmd_req *v_req,
+ u32 exp_data_len,
+ int data_direction)
+{
+ struct tcm_vhost_cmd *tv_cmd;
+ struct tcm_vhost_nexus *tv_nexus;
+ struct se_portal_group *se_tpg = &tv_tpg->se_tpg;
+ struct se_session *se_sess;
+ struct se_cmd *se_cmd;
+ int sam_task_attr;
+
+ tv_nexus = tv_tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Unable to locate active struct tcm_vhost_nexus\n");
+ return ERR_PTR(-EIO);
+ }
+ se_sess = tv_nexus->tvn_se_sess;
+
+ tv_cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC);
+ if (!tv_cmd) {
+ pr_err("Unable to allocate struct tcm_vhost_cmd\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&tv_cmd->tvc_completion_list);
+ tv_cmd->tvc_tag = v_req->tag;
+
+ se_cmd = &tv_cmd->tvc_se_cmd;
+ /*
+ * Locate the SAM Task Attr from virtio_scsi_cmd_req
+ */
+ sam_task_attr = v_req->task_attr;
+ /*
+ * Initialize struct se_cmd descriptor from TCM infrastructure
+ */
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, exp_data_len,
+ data_direction, sam_task_attr,
+ &tv_cmd->tvc_sense_buf[0]);
+
+#if 0 /* FIXME: vhost_scsi_allocate_cmd() BIDI operation */
+ if (bidi)
+ se_cmd->se_cmd_flags |= SCF_BIDI;
+#endif
+ return tv_cmd;
+}
+
+/*
+ * Map a user memory range into a scatterlist
+ *
+ * Returns the number of scatterlist entries used or -errno on error.
+ */
+static int vhost_scsi_map_to_sgl(struct scatterlist *sgl,
+ unsigned int sgl_count, void __user *ptr, size_t len, int write)
+{
+ struct scatterlist *sg = sgl;
+ unsigned int npages = 0;
+ int ret;
+
+ while (len > 0) {
+ struct page *page;
+ unsigned int offset = (uintptr_t)ptr & ~PAGE_MASK;
+ unsigned int nbytes = min_t(unsigned int,
+ PAGE_SIZE - offset, len);
+
+ if (npages == sgl_count) {
+ ret = -ENOBUFS;
+ goto err;
+ }
+
+ ret = get_user_pages_fast((unsigned long)ptr, 1, write, &page);
+ BUG_ON(ret == 0); /* we should either get our page or fail */
+ if (ret < 0)
+ goto err;
+
+ sg_set_page(sg, page, nbytes, offset);
+ ptr += nbytes;
+ len -= nbytes;
+ sg++;
+ npages++;
+ }
+ return npages;
+
+err:
+ /* Put pages that we hold */
+ for (sg = sgl; sg != &sgl[npages]; sg++)
+ put_page(sg_page(sg));
+ return ret;
+}
+
+static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd,
+ struct iovec *iov, unsigned int niov, int write)
+{
+ int ret;
+ unsigned int i;
+ u32 sgl_count;
+ struct scatterlist *sg;
+
+ /*
+ * Find out how long sglist needs to be
+ */
+ sgl_count = 0;
+ for (i = 0; i < niov; i++) {
+ sgl_count += (((uintptr_t)iov[i].iov_base + iov[i].iov_len +
+ PAGE_SIZE - 1) >> PAGE_SHIFT) -
+ ((uintptr_t)iov[i].iov_base >> PAGE_SHIFT);
+ }
+ /* TODO overflow checking */
+
+ sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC);
+ if (!sg)
+ return -ENOMEM;
+ pr_debug("%s sg %p sgl_count %u is_err %d\n", __func__,
+ sg, sgl_count, !sg);
+ sg_init_table(sg, sgl_count);
+
+ tv_cmd->tvc_sgl = sg;
+ tv_cmd->tvc_sgl_count = sgl_count;
+
+ pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count);
+ for (i = 0; i < niov; i++) {
+ ret = vhost_scsi_map_to_sgl(sg, sgl_count, iov[i].iov_base,
+ iov[i].iov_len, write);
+ if (ret < 0) {
+ for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&tv_cmd->tvc_sgl[i]));
+ kfree(tv_cmd->tvc_sgl);
+ tv_cmd->tvc_sgl = NULL;
+ tv_cmd->tvc_sgl_count = 0;
+ return ret;
+ }
+
+ sg += ret;
+ sgl_count -= ret;
+ }
+ return 0;
+}
+
+static void tcm_vhost_submission_work(struct work_struct *work)
+{
+ struct tcm_vhost_cmd *tv_cmd =
+ container_of(work, struct tcm_vhost_cmd, work);
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
+ int rc, sg_no_bidi = 0;
+ /*
+ * Locate the struct se_lun pointer based on v_req->lun, and
+ * attach it to struct se_cmd
+ */
+ rc = transport_lookup_cmd_lun(&tv_cmd->tvc_se_cmd, tv_cmd->tvc_lun);
+ if (rc < 0) {
+ pr_err("Failed to look up lun: %d\n", tv_cmd->tvc_lun);
+ transport_send_check_condition_and_sense(&tv_cmd->tvc_se_cmd,
+ tv_cmd->tvc_se_cmd.scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ rc = target_setup_cmd_from_cdb(se_cmd, tv_cmd->tvc_cdb);
+ if (rc == -ENOMEM) {
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ } else if (rc < 0) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ tcm_vhost_queue_status(se_cmd);
+ else
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ if (tv_cmd->tvc_sgl_count) {
+ sg_ptr = tv_cmd->tvc_sgl;
+ /*
+ * For BIDI commands, pass in the extra READ buffer
+ * to transport_generic_map_mem_to_cmd() below..
+ */
+/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
+#if 0
+ if (se_cmd->se_cmd_flags & SCF_BIDI) {
+ sg_bidi_ptr = NULL;
+ sg_no_bidi = 0;
+ }
+#endif
+ } else {
+ sg_ptr = NULL;
+ }
+
+ rc = transport_generic_map_mem_to_cmd(se_cmd, sg_ptr,
+ tv_cmd->tvc_sgl_count, sg_bidi_ptr,
+ sg_no_bidi);
+ if (rc < 0) {
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+ transport_handle_cdb_direct(se_cmd);
+}
+
+static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
+{
+ struct vhost_virtqueue *vq = &vs->vqs[2];
+ struct virtio_scsi_cmd_req v_req;
+ struct tcm_vhost_tpg *tv_tpg;
+ struct tcm_vhost_cmd *tv_cmd;
+ u32 exp_data_len, data_first, data_num, data_direction;
+ unsigned out, in, i;
+ int head, ret;
+
+ /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */
+ tv_tpg = vs->vs_tpg;
+ if (unlikely(!tv_tpg)) {
+ pr_err("%s endpoint not set\n", __func__);
+ return;
+ }
+
+ mutex_lock(&vq->mutex);
+ vhost_disable_notify(&vs->dev, vq);
+
+ for (;;) {
+ head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
+ ARRAY_SIZE(vq->iov), &out, &in,
+ NULL, NULL);
+ pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
+ head, out, in);
+ /* On error, stop handling until the next kick. */
+ if (unlikely(head < 0))
+ break;
+ /* Nothing new? Wait for eventfd to tell us they refilled. */
+ if (head == vq->num) {
+ if (unlikely(vhost_enable_notify(&vs->dev, vq))) {
+ vhost_disable_notify(&vs->dev, vq);
+ continue;
+ }
+ break;
+ }
+
+/* FIXME: BIDI operation */
+ if (out == 1 && in == 1) {
+ data_direction = DMA_NONE;
+ data_first = 0;
+ data_num = 0;
+ } else if (out == 1 && in > 1) {
+ data_direction = DMA_FROM_DEVICE;
+ data_first = out + 1;
+ data_num = in - 1;
+ } else if (out > 1 && in == 1) {
+ data_direction = DMA_TO_DEVICE;
+ data_first = 1;
+ data_num = out - 1;
+ } else {
+ vq_err(vq, "Invalid buffer layout out: %u in: %u\n",
+ out, in);
+ break;
+ }
+
+ /*
+ * Check for a sane resp buffer so we can report errors to
+ * the guest.
+ */
+ if (unlikely(vq->iov[out].iov_len !=
+ sizeof(struct virtio_scsi_cmd_resp))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+ " bytes\n", vq->iov[out].iov_len);
+ break;
+ }
+
+ if (unlikely(vq->iov[0].iov_len != sizeof(v_req))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_req, got %zu"
+ " bytes\n", vq->iov[0].iov_len);
+ break;
+ }
+ pr_debug("Calling __copy_from_user: vq->iov[0].iov_base: %p,"
+ " len: %zu\n", vq->iov[0].iov_base, sizeof(v_req));
+ ret = __copy_from_user(&v_req, vq->iov[0].iov_base,
+ sizeof(v_req));
+ if (unlikely(ret)) {
+ vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
+ break;
+ }
+
+ exp_data_len = 0;
+ for (i = 0; i < data_num; i++)
+ exp_data_len += vq->iov[data_first + i].iov_len;
+
+ tv_cmd = vhost_scsi_allocate_cmd(tv_tpg, &v_req,
+ exp_data_len, data_direction);
+ if (IS_ERR(tv_cmd)) {
+ vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n",
+ PTR_ERR(tv_cmd));
+ break;
+ }
+ pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
+ ": %d\n", tv_cmd, exp_data_len, data_direction);
+
+ tv_cmd->tvc_vhost = vs;
+
+ if (unlikely(vq->iov[out].iov_len !=
+ sizeof(struct virtio_scsi_cmd_resp))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+ " bytes, out: %d, in: %d\n",
+ vq->iov[out].iov_len, out, in);
+ break;
+ }
+
+ tv_cmd->tvc_resp = vq->iov[out].iov_base;
+
+ /*
+ * Copy in the recieved CDB descriptor into tv_cmd->tvc_cdb
+ * that will be used by tcm_vhost_new_cmd_map() and down into
+ * target_setup_cmd_from_cdb()
+ */
+ memcpy(tv_cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
+ /*
+ * Check that the recieved CDB size does not exceeded our
+ * hardcoded max for tcm_vhost
+ */
+ /* TODO what if cdb was too small for varlen cdb header? */
+ if (unlikely(scsi_command_size(tv_cmd->tvc_cdb) >
+ TCM_VHOST_MAX_CDB_SIZE)) {
+ vq_err(vq, "Received SCSI CDB with command_size: %d that"
+ " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
+ scsi_command_size(tv_cmd->tvc_cdb),
+ TCM_VHOST_MAX_CDB_SIZE);
+ break; /* TODO */
+ }
+ tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+
+ pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
+ tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun);
+
+ if (data_direction != DMA_NONE) {
+ ret = vhost_scsi_map_iov_to_sgl(tv_cmd,
+ &vq->iov[data_first], data_num,
+ data_direction == DMA_TO_DEVICE);
+ if (unlikely(ret)) {
+ vq_err(vq, "Failed to map iov to sgl\n");
+ break; /* TODO */
+ }
+ }
+
+ /*
+ * Save the descriptor from vhost_get_vq_desc() to be used to
+ * complete the virtio-scsi request in TCM callback context via
+ * tcm_vhost_queue_data_in() and tcm_vhost_queue_status()
+ */
+ tv_cmd->tvc_vq_desc = head;
+ /*
+ * Dispatch tv_cmd descriptor for cmwq execution in process
+ * context provided by tcm_vhost_workqueue. This also ensures
+ * tv_cmd is executed on the same kworker CPU as this vhost
+ * thread to gain positive L2 cache locality effects..
+ */
+ INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work);
+ queue_work(tcm_vhost_workqueue, &tv_cmd->work);
+ }
+
+ mutex_unlock(&vq->mutex);
+}
+
+static void vhost_scsi_ctl_handle_kick(struct vhost_work *work)
+{
+ pr_debug("%s: The handling func for control queue.\n", __func__);
+}
+
+static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
+{
+ pr_debug("%s: The handling func for event queue.\n", __func__);
+}
+
+static void vhost_scsi_handle_kick(struct vhost_work *work)
+{
+ struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
+ poll.work);
+ struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
+
+ vhost_scsi_handle_vq(vs);
+}
+
+/*
+ * Called from vhost_scsi_ioctl() context to walk the list of available
+ * tcm_vhost_tpg with an active struct tcm_vhost_nexus
+ */
+static int vhost_scsi_set_endpoint(
+ struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
+{
+ struct tcm_vhost_tport *tv_tport;
+ struct tcm_vhost_tpg *tv_tpg;
+ int index;
+
+ mutex_lock(&vs->dev.mutex);
+ /* Verify that ring has been setup correctly. */
+ for (index = 0; index < vs->dev.nvqs; ++index) {
+ /* Verify that ring has been setup correctly. */
+ if (!vhost_vq_access_ok(&vs->vqs[index])) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EFAULT;
+ }
+ }
+ mutex_unlock(&vs->dev.mutex);
+
+ mutex_lock(&tcm_vhost_mutex);
+ list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) {
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ if (!tv_tpg->tpg_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ continue;
+ }
+ if (tv_tpg->tv_tpg_vhost_count != 0) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ continue;
+ }
+ tv_tport = tv_tpg->tport;
+
+ if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) &&
+ (tv_tpg->tport_tpgt == t->vhost_tpgt)) {
+ tv_tpg->tv_tpg_vhost_count++;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tcm_vhost_mutex);
+
+ mutex_lock(&vs->dev.mutex);
+ if (vs->vs_tpg) {
+ mutex_unlock(&vs->dev.mutex);
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_vhost_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return -EEXIST;
+ }
+
+ vs->vs_tpg = tv_tpg;
+ smp_mb__after_atomic_inc();
+ mutex_unlock(&vs->dev.mutex);
+ return 0;
+ }
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ }
+ mutex_unlock(&tcm_vhost_mutex);
+ return -EINVAL;
+}
+
+static int vhost_scsi_clear_endpoint(
+ struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
+{
+ struct tcm_vhost_tport *tv_tport;
+ struct tcm_vhost_tpg *tv_tpg;
+ int index, ret;
+
+ mutex_lock(&vs->dev.mutex);
+ /* Verify that ring has been setup correctly. */
+ for (index = 0; index < vs->dev.nvqs; ++index) {
+ if (!vhost_vq_access_ok(&vs->vqs[index])) {
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+
+ if (!vs->vs_tpg) {
+ ret = -ENODEV;
+ goto err;
+ }
+ tv_tpg = vs->vs_tpg;
+ tv_tport = tv_tpg->tport;
+
+ if (strcmp(tv_tport->tport_name, t->vhost_wwpn) ||
+ (tv_tpg->tport_tpgt != t->vhost_tpgt)) {
+ pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu"
+ " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n",
+ tv_tport->tport_name, tv_tpg->tport_tpgt,
+ t->vhost_wwpn, t->vhost_tpgt);
+ ret = -EINVAL;
+ goto err;
+ }
+ tv_tpg->tv_tpg_vhost_count--;
+ vs->vs_tpg = NULL;
+ mutex_unlock(&vs->dev.mutex);
+
+ return 0;
+
+err:
+ mutex_unlock(&vs->dev.mutex);
+ return ret;
+}
+
+static int vhost_scsi_open(struct inode *inode, struct file *f)
+{
+ struct vhost_scsi *s;
+ int r;
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work);
+ INIT_LIST_HEAD(&s->vs_completion_list);
+ spin_lock_init(&s->vs_completion_lock);
+
+ s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick;
+ r = vhost_dev_init(&s->dev, s->vqs, 3);
+ if (r < 0) {
+ kfree(s);
+ return r;
+ }
+
+ f->private_data = s;
+ return 0;
+}
+
+static int vhost_scsi_release(struct inode *inode, struct file *f)
+{
+ struct vhost_scsi *s = f->private_data;
+
+ if (s->vs_tpg && s->vs_tpg->tport) {
+ struct vhost_scsi_target backend;
+
+ memcpy(backend.vhost_wwpn, s->vs_tpg->tport->tport_name,
+ sizeof(backend.vhost_wwpn));
+ backend.vhost_tpgt = s->vs_tpg->tport_tpgt;
+ vhost_scsi_clear_endpoint(s, &backend);
+ }
+
+ vhost_dev_cleanup(&s->dev, false);
+ kfree(s);
+ return 0;
+}
+
+static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index)
+{
+ vhost_poll_flush(&vs->dev.vqs[index].poll);
+}
+
+static void vhost_scsi_flush(struct vhost_scsi *vs)
+{
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO);
+}
+
+static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
+{
+ if (features & ~VHOST_FEATURES)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&vs->dev.mutex);
+ if ((features & (1 << VHOST_F_LOG_ALL)) &&
+ !vhost_log_access_ok(&vs->dev)) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EFAULT;
+ }
+ vs->dev.acked_features = features;
+ smp_wmb();
+ vhost_scsi_flush(vs);
+ mutex_unlock(&vs->dev.mutex);
+ return 0;
+}
+
+static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ struct vhost_scsi *vs = f->private_data;
+ struct vhost_scsi_target backend;
+ void __user *argp = (void __user *)arg;
+ u64 __user *featurep = argp;
+ u64 features;
+ int r, abi_version = VHOST_SCSI_ABI_VERSION;
+
+ switch (ioctl) {
+ case VHOST_SCSI_SET_ENDPOINT:
+ if (copy_from_user(&backend, argp, sizeof backend))
+ return -EFAULT;
+ if (backend.reserved != 0)
+ return -EOPNOTSUPP;
+
+ return vhost_scsi_set_endpoint(vs, &backend);
+ case VHOST_SCSI_CLEAR_ENDPOINT:
+ if (copy_from_user(&backend, argp, sizeof backend))
+ return -EFAULT;
+ if (backend.reserved != 0)
+ return -EOPNOTSUPP;
+
+ return vhost_scsi_clear_endpoint(vs, &backend);
+ case VHOST_SCSI_GET_ABI_VERSION:
+ if (copy_to_user(argp, &abi_version, sizeof abi_version))
+ return -EFAULT;
+ return 0;
+ case VHOST_GET_FEATURES:
+ features = VHOST_FEATURES;
+ if (copy_to_user(featurep, &features, sizeof features))
+ return -EFAULT;
+ return 0;
+ case VHOST_SET_FEATURES:
+ if (copy_from_user(&features, featurep, sizeof features))
+ return -EFAULT;
+ return vhost_scsi_set_features(vs, features);
+ default:
+ mutex_lock(&vs->dev.mutex);
+ r = vhost_dev_ioctl(&vs->dev, ioctl, arg);
+ mutex_unlock(&vs->dev.mutex);
+ return r;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations vhost_scsi_fops = {
+ .owner = THIS_MODULE,
+ .release = vhost_scsi_release,
+ .unlocked_ioctl = vhost_scsi_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhost_scsi_compat_ioctl,
+#endif
+ .open = vhost_scsi_open,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice vhost_scsi_misc = {
+ MISC_DYNAMIC_MINOR,
+ "vhost-scsi",
+ &vhost_scsi_fops,
+};
+
+static int __init vhost_scsi_register(void)
+{
+ return misc_register(&vhost_scsi_misc);
+}
+
+static int vhost_scsi_deregister(void)
+{
+ return misc_deregister(&vhost_scsi_misc);
+}
+
+static char *tcm_vhost_dump_proto_id(struct tcm_vhost_tport *tport)
+{
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return "SAS";
+ case SCSI_PROTOCOL_FCP:
+ return "FCP";
+ case SCSI_PROTOCOL_ISCSI:
+ return "iSCSI";
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
+ struct se_lun *lun)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count++;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+
+ return 0;
+}
+
+static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+}
+
+static struct se_node_acl *tcm_vhost_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct tcm_vhost_nacl *nacl;
+ u64 wwpn = 0;
+ u32 nexus_depth;
+
+ /* tcm_vhost_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL); */
+ se_nacl_new = tcm_vhost_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ nexus_depth = 1;
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ tcm_vhost_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+ /*
+ * Locate our struct tcm_vhost_nacl and set the FC Nport WWPN
+ */
+ nacl = container_of(se_nacl, struct tcm_vhost_nacl, se_node_acl);
+ nacl->iport_wwpn = wwpn;
+
+ return se_nacl;
+}
+
+static void tcm_vhost_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct tcm_vhost_nacl *nacl = container_of(se_acl,
+ struct tcm_vhost_nacl, se_node_acl);
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg,
+ const char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_vhost_nexus *tv_nexus;
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ if (tv_tpg->tpg_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_debug("tv_tpg->tpg_nexus already exists\n");
+ return -EEXIST;
+ }
+ se_tpg = &tv_tpg->se_tpg;
+
+ tv_nexus = kzalloc(sizeof(struct tcm_vhost_nexus), GFP_KERNEL);
+ if (!tv_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_err("Unable to allocate struct tcm_vhost_nexus\n");
+ return -ENOMEM;
+ }
+ /*
+ * Initialize the struct se_session pointer
+ */
+ tv_nexus->tvn_se_sess = transport_init_session();
+ if (IS_ERR(tv_nexus->tvn_se_sess)) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ kfree(tv_nexus);
+ return -ENOMEM;
+ }
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_vhost struct se_portal_group with
+ * the SCSI Initiator port name of the passed configfs group 'name'.
+ */
+ tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, (unsigned char *)name);
+ if (!tv_nexus->tvn_se_sess->se_node_acl) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_debug("core_tpg_check_initiator_node_acl() failed"
+ " for %s\n", name);
+ transport_free_session(tv_nexus->tvn_se_sess);
+ kfree(tv_nexus);
+ return -ENOMEM;
+ }
+ /*
+ * Now register the TCM vhost virtual I_T Nexus as active with the
+ * call to __transport_register_session()
+ */
+ __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ tv_nexus->tvn_se_sess, tv_nexus);
+ tv_tpg->tpg_nexus = tv_nexus;
+
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return 0;
+}
+
+static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_vhost_nexus *tv_nexus;
+
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+
+ se_sess = tv_nexus->tvn_se_sess;
+ if (!se_sess) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+
+ if (tpg->tv_tpg_port_count != 0) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
+ " active TPG port count: %d\n",
+ tpg->tv_tpg_port_count);
+ return -EBUSY;
+ }
+
+ if (tpg->tv_tpg_vhost_count != 0) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
+ " active TPG vhost count: %d\n",
+ tpg->tv_tpg_vhost_count);
+ return -EBUSY;
+ }
+
+ pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_vhost_dump_proto_id(tpg->tport),
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated vhost Target Port
+ */
+ transport_deregister_session(tv_nexus->tvn_se_sess);
+ tpg->tpg_nexus = NULL;
+ mutex_unlock(&tpg->tv_tpg_mutex);
+
+ kfree(tv_nexus);
+ return 0;
+}
+
+static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_nexus *tv_nexus;
+ ssize_t ret;
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_nexus = tv_tpg->tpg_nexus;
+ if (!tv_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+
+ return ret;
+}
+
+static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport_wwn = tv_tpg->tport;
+ unsigned char i_port[TCM_VHOST_NAMELEN], *ptr, *port_ptr;
+ int ret;
+ /*
+ * Shutdown the active I_T nexus if 'NULL' is passed..
+ */
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_vhost_drop_nexus(tv_tpg);
+ return (!ret) ? count : ret;
+ }
+ /*
+ * Otherwise make sure the passed virtual Initiator port WWN matches
+ * the fabric protocol_id set in tcm_vhost_make_tport(), and call
+ * tcm_vhost_make_nexus().
+ */
+ if (strlen(page) >= TCM_VHOST_NAMELEN) {
+ pr_err("Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, TCM_VHOST_NAMELEN);
+ return -EINVAL;
+ }
+ snprintf(&i_port[0], TCM_VHOST_NAMELEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) {
+ pr_err("Passed SAS Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "fc.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) {
+ pr_err("Passed FCP Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[3]; /* Skip over "fc." */
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "iqn.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) {
+ pr_err("Passed iSCSI Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ pr_err("Unable to locate prefix for emulated Initiator Port:"
+ " %s\n", i_port);
+ return -EINVAL;
+ /*
+ * Clear any trailing newline for the NAA WWN
+ */
+check_newline:
+ if (i_port[strlen(i_port)-1] == '\n')
+ i_port[strlen(i_port)-1] = '\0';
+
+ ret = tcm_vhost_make_nexus(tv_tpg, port_ptr);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_vhost, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_vhost_tpg_attrs[] = {
+ &tcm_vhost_tpg_nexus.attr,
+ NULL,
+};
+
+static struct se_portal_group *tcm_vhost_make_tpg(struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_vhost_tport *tport = container_of(wwn,
+ struct tcm_vhost_tport, tport_wwn);
+
+ struct tcm_vhost_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ tpg = kzalloc(sizeof(struct tcm_vhost_tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct tcm_vhost_tpg");
+ return ERR_PTR(-ENOMEM);
+ }
+ mutex_init(&tpg->tv_tpg_mutex);
+ INIT_LIST_HEAD(&tpg->tv_tpg_list);
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+
+ ret = core_tpg_register(&tcm_vhost_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ kfree(tpg);
+ return NULL;
+ }
+ mutex_lock(&tcm_vhost_mutex);
+ list_add_tail(&tpg->tv_tpg_list, &tcm_vhost_list);
+ mutex_unlock(&tcm_vhost_mutex);
+
+ return &tpg->se_tpg;
+}
+
+static void tcm_vhost_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tcm_vhost_mutex);
+ list_del(&tpg->tv_tpg_list);
+ mutex_unlock(&tcm_vhost_mutex);
+ /*
+ * Release the virtual I_T Nexus for this vhost TPG
+ */
+ tcm_vhost_drop_nexus(tpg);
+ /*
+ * Deregister the se_tpg from TCM..
+ */
+ core_tpg_deregister(se_tpg);
+ kfree(tpg);
+}
+
+static struct se_wwn *tcm_vhost_make_tport(struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_vhost_tport *tport;
+ char *ptr;
+ u64 wwpn = 0;
+ int off = 0;
+
+ /* if (tcm_vhost_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL); */
+
+ tport = kzalloc(sizeof(struct tcm_vhost_tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct tcm_vhost_tport");
+ return ERR_PTR(-ENOMEM);
+ }
+ tport->tport_wwpn = wwpn;
+ /*
+ * Determine the emulated Protocol Identifier and Target Port Name
+ * based on the incoming configfs directory name.
+ */
+ ptr = strstr(name, "naa.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_SAS;
+ goto check_len;
+ }
+ ptr = strstr(name, "fc.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_FCP;
+ off = 3; /* Skip over "fc." */
+ goto check_len;
+ }
+ ptr = strstr(name, "iqn.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_ISCSI;
+ goto check_len;
+ }
+
+ pr_err("Unable to locate prefix for emulated Target Port:"
+ " %s\n", name);
+ kfree(tport);
+ return ERR_PTR(-EINVAL);
+
+check_len:
+ if (strlen(name) >= TCM_VHOST_NAMELEN) {
+ pr_err("Emulated %s Address: %s, exceeds"
+ " max: %d\n", name, tcm_vhost_dump_proto_id(tport),
+ TCM_VHOST_NAMELEN);
+ kfree(tport);
+ return ERR_PTR(-EINVAL);
+ }
+ snprintf(&tport->tport_name[0], TCM_VHOST_NAMELEN, "%s", &name[off]);
+
+ pr_debug("TCM_VHost_ConfigFS: Allocated emulated Target"
+ " %s Address: %s\n", tcm_vhost_dump_proto_id(tport), name);
+
+ return &tport->tport_wwn;
+}
+
+static void tcm_vhost_drop_tport(struct se_wwn *wwn)
+{
+ struct tcm_vhost_tport *tport = container_of(wwn,
+ struct tcm_vhost_tport, tport_wwn);
+
+ pr_debug("TCM_VHost_ConfigFS: Deallocating emulated Target"
+ " %s Address: %s\n", tcm_vhost_dump_proto_id(tport),
+ tport->tport_name);
+
+ kfree(tport);
+}
+
+static ssize_t tcm_vhost_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM_VHOST fabric module %s on %s/%s"
+ "on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname,
+ utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(tcm_vhost, version);
+
+static struct configfs_attribute *tcm_vhost_wwn_attrs[] = {
+ &tcm_vhost_wwn_version.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops tcm_vhost_ops = {
+ .get_fabric_name = tcm_vhost_get_fabric_name,
+ .get_fabric_proto_ident = tcm_vhost_get_fabric_proto_ident,
+ .tpg_get_wwn = tcm_vhost_get_fabric_wwn,
+ .tpg_get_tag = tcm_vhost_get_tag,
+ .tpg_get_default_depth = tcm_vhost_get_default_depth,
+ .tpg_get_pr_transport_id = tcm_vhost_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = tcm_vhost_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = tcm_vhost_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = tcm_vhost_check_true,
+ .tpg_check_demo_mode_cache = tcm_vhost_check_true,
+ .tpg_check_demo_mode_write_protect = tcm_vhost_check_false,
+ .tpg_check_prod_mode_write_protect = tcm_vhost_check_false,
+ .tpg_alloc_fabric_acl = tcm_vhost_alloc_fabric_acl,
+ .tpg_release_fabric_acl = tcm_vhost_release_fabric_acl,
+ .tpg_get_inst_index = tcm_vhost_tpg_get_inst_index,
+ .release_cmd = tcm_vhost_release_cmd,
+ .shutdown_session = tcm_vhost_shutdown_session,
+ .close_session = tcm_vhost_close_session,
+ .sess_get_index = tcm_vhost_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = tcm_vhost_write_pending,
+ .write_pending_status = tcm_vhost_write_pending_status,
+ .set_default_node_attributes = tcm_vhost_set_default_node_attrs,
+ .get_task_tag = tcm_vhost_get_task_tag,
+ .get_cmd_state = tcm_vhost_get_cmd_state,
+ .queue_data_in = tcm_vhost_queue_data_in,
+ .queue_status = tcm_vhost_queue_status,
+ .queue_tm_rsp = tcm_vhost_queue_tm_rsp,
+ .get_fabric_sense_len = tcm_vhost_get_fabric_sense_len,
+ .set_fabric_sense_len = tcm_vhost_set_fabric_sense_len,
+ /*
+ * Setup callers for generic logic in target_core_fabric_configfs.c
+ */
+ .fabric_make_wwn = tcm_vhost_make_tport,
+ .fabric_drop_wwn = tcm_vhost_drop_tport,
+ .fabric_make_tpg = tcm_vhost_make_tpg,
+ .fabric_drop_tpg = tcm_vhost_drop_tpg,
+ .fabric_post_link = tcm_vhost_port_link,
+ .fabric_pre_unlink = tcm_vhost_port_unlink,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = tcm_vhost_make_nodeacl,
+ .fabric_drop_nodeacl = tcm_vhost_drop_nodeacl,
+};
+
+static int tcm_vhost_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ pr_debug("TCM_VHOST fabric module %s on %s/%s"
+ " on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname,
+ utsname()->machine);
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "vhost");
+ if (IS_ERR(fabric)) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return PTR_ERR(fabric);
+ }
+ /*
+ * Setup fabric->tf_ops from our local tcm_vhost_ops
+ */
+ fabric->tf_ops = tcm_vhost_ops;
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_vhost_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_vhost_tpg_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+ /*
+ * Register the fabric for use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed"
+ " for TCM_VHOST\n");
+ return ret;
+ }
+ /*
+ * Setup our local pointer to *fabric
+ */
+ tcm_vhost_fabric_configfs = fabric;
+ pr_debug("TCM_VHOST[0] - Set fabric -> tcm_vhost_fabric_configfs\n");
+ return 0;
+};
+
+static void tcm_vhost_deregister_configfs(void)
+{
+ if (!tcm_vhost_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(tcm_vhost_fabric_configfs);
+ tcm_vhost_fabric_configfs = NULL;
+ pr_debug("TCM_VHOST[0] - Cleared tcm_vhost_fabric_configfs\n");
+};
+
+static int __init tcm_vhost_init(void)
+{
+ int ret = -ENOMEM;
+ /*
+ * Use our own dedicated workqueue for submitting I/O into
+ * target core to avoid contention within system_wq.
+ */
+ tcm_vhost_workqueue = alloc_workqueue("tcm_vhost", 0, 0);
+ if (!tcm_vhost_workqueue)
+ goto out;
+
+ ret = vhost_scsi_register();
+ if (ret < 0)
+ goto out_destroy_workqueue;
+
+ ret = tcm_vhost_register_configfs();
+ if (ret < 0)
+ goto out_vhost_scsi_deregister;
+
+ return 0;
+
+out_vhost_scsi_deregister:
+ vhost_scsi_deregister();
+out_destroy_workqueue:
+ destroy_workqueue(tcm_vhost_workqueue);
+out:
+ return ret;
+};
+
+static void tcm_vhost_exit(void)
+{
+ tcm_vhost_deregister_configfs();
+ vhost_scsi_deregister();
+ destroy_workqueue(tcm_vhost_workqueue);
+};
+
+MODULE_DESCRIPTION("TCM_VHOST series fabric driver");
+MODULE_LICENSE("GPL");
+module_init(tcm_vhost_init);
+module_exit(tcm_vhost_exit);
diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h
new file mode 100644
index 000000000000..d9e93557d669
--- /dev/null
+++ b/drivers/vhost/tcm_vhost.h
@@ -0,0 +1,103 @@
+#define TCM_VHOST_VERSION "v0.1"
+#define TCM_VHOST_NAMELEN 256
+#define TCM_VHOST_MAX_CDB_SIZE 32
+
+struct tcm_vhost_cmd {
+ /* Descriptor from vhost_get_vq_desc() for virt_queue segment */
+ int tvc_vq_desc;
+ /* The Tag from include/linux/virtio_scsi.h:struct virtio_scsi_cmd_req */
+ u64 tvc_tag;
+ /* The number of scatterlists associated with this cmd */
+ u32 tvc_sgl_count;
+ /* Saved unpacked SCSI LUN for tcm_vhost_submission_work() */
+ u32 tvc_lun;
+ /* Pointer to the SGL formatted memory from virtio-scsi */
+ struct scatterlist *tvc_sgl;
+ /* Pointer to response */
+ struct virtio_scsi_cmd_resp __user *tvc_resp;
+ /* Pointer to vhost_scsi for our device */
+ struct vhost_scsi *tvc_vhost;
+ /* The TCM I/O descriptor that is accessed via container_of() */
+ struct se_cmd tvc_se_cmd;
+ /* work item used for cmwq dispatch to tcm_vhost_submission_work() */
+ struct work_struct work;
+ /* Copy of the incoming SCSI command descriptor block (CDB) */
+ unsigned char tvc_cdb[TCM_VHOST_MAX_CDB_SIZE];
+ /* Sense buffer that will be mapped into outgoing status */
+ unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER];
+ /* Completed commands list, serviced from vhost worker thread */
+ struct list_head tvc_completion_list;
+};
+
+struct tcm_vhost_nexus {
+ /* Pointer to TCM session for I_T Nexus */
+ struct se_session *tvn_se_sess;
+};
+
+struct tcm_vhost_nacl {
+ /* Binary World Wide unique Port Name for Vhost Initiator port */
+ u64 iport_wwpn;
+ /* ASCII formatted WWPN for Sas Initiator port */
+ char iport_name[TCM_VHOST_NAMELEN];
+ /* Returned by tcm_vhost_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct tcm_vhost_tpg {
+ /* Vhost port target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Used to track number of TPG Port/Lun Links wrt to explict I_T Nexus shutdown */
+ int tv_tpg_port_count;
+ /* Used for vhost_scsi device reference to tpg_nexus, protected by tv_tpg_mutex */
+ int tv_tpg_vhost_count;
+ /* list for tcm_vhost_list */
+ struct list_head tv_tpg_list;
+ /* Used to protect access for tpg_nexus */
+ struct mutex tv_tpg_mutex;
+ /* Pointer to the TCM VHost I_T Nexus for this TPG endpoint */
+ struct tcm_vhost_nexus *tpg_nexus;
+ /* Pointer back to tcm_vhost_tport */
+ struct tcm_vhost_tport *tport;
+ /* Returned by tcm_vhost_make_tpg() */
+ struct se_portal_group se_tpg;
+};
+
+struct tcm_vhost_tport {
+ /* SCSI protocol the tport is providing */
+ u8 tport_proto_id;
+ /* Binary World Wide unique Port Name for Vhost Target port */
+ u64 tport_wwpn;
+ /* ASCII formatted WWPN for Vhost Target port */
+ char tport_name[TCM_VHOST_NAMELEN];
+ /* Returned by tcm_vhost_make_tport() */
+ struct se_wwn tport_wwn;
+};
+
+/*
+ * As per request from MST, keep TCM_VHOST related ioctl defines out of
+ * linux/vhost.h (user-space) for now..
+ */
+
+#include <linux/vhost.h>
+
+/*
+ * Used by QEMU userspace to ensure a consistent vhost-scsi ABI.
+ *
+ * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate +
+ * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage
+ */
+
+#define VHOST_SCSI_ABI_VERSION 0
+
+struct vhost_scsi_target {
+ int abi_version;
+ char vhost_wwpn[TRANSPORT_IQN_LEN];
+ unsigned short vhost_tpgt;
+ unsigned short reserved;
+};
+
+/* VHOST_SCSI specific defines */
+#define VHOST_SCSI_SET_ENDPOINT _IOW(VHOST_VIRTIO, 0x40, struct vhost_scsi_target)
+#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
+/* Changing this breaks userspace. */
+#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int)
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index b0b2ac335347..747442d2c0f6 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -90,7 +90,8 @@
#undef DEBUG
#ifdef DEBUG
-#define DBG(fmt, args...) printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
+#define DBG(fmt, args...) \
+ printk(KERN_DEBUG "aty128fb: %s " fmt, __func__, ##args);
#else
#define DBG(fmt, args...)
#endif
@@ -449,8 +450,9 @@ static int aty128_decode_var(struct fb_var_screeninfo *var,
struct aty128fb_par *par);
#if 0
static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
- void __iomem *bios);
-static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
+ void __iomem *bios);
+static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev,
+ const struct aty128fb_par *par);
#endif
static void aty128_timings(struct aty128fb_par *par);
static void aty128_init_engine(struct aty128fb_par *par);
@@ -779,7 +781,8 @@ static u32 depth_to_dst(u32 depth)
#ifndef __sparc__
-static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
+static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par,
+ struct pci_dev *dev)
{
u16 dptr;
u8 rom_type;
@@ -811,13 +814,14 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
/* Look for the PCI data to check the ROM type */
dptr = BIOS_IN16(0x18);
- /* Check the PCI data signature. If it's wrong, we still assume a normal x86 ROM
- * for now, until I've verified this works everywhere. The goal here is more
- * to phase out Open Firmware images.
+ /* Check the PCI data signature. If it's wrong, we still assume a normal
+ * x86 ROM for now, until I've verified this works everywhere.
+ * The goal here is more to phase out Open Firmware images.
*
- * Currently, we only look at the first PCI data, we could iteratre and deal with
- * them all, and we should use fb_bios_start relative to start of image and not
- * relative start of ROM, but so far, I never found a dual-image ATI card
+ * Currently, we only look at the first PCI data, we could iteratre and
+ * deal with them all, and we should use fb_bios_start relative to start
+ * of image and not relative start of ROM, but so far, I never found a
+ * dual-image ATI card.
*
* typedef struct {
* u32 signature; + 0x00
@@ -852,7 +856,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
printk(KERN_INFO "aty128fb: Found HP PA-RISC ROM Image\n");
goto failed;
default:
- printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n", rom_type);
+ printk(KERN_INFO "aty128fb: Found unknown type %d ROM Image\n",
+ rom_type);
goto failed;
}
anyway:
@@ -863,7 +868,8 @@ static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, s
return NULL;
}
-static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
+static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
+ unsigned char __iomem *bios)
{
unsigned int bios_hdr;
unsigned int bios_pll;
@@ -1247,10 +1253,13 @@ static int aty128_crtc_to_var(const struct aty128_crtc *crtc,
static void aty128_set_crt_enable(struct aty128fb_par *par, int on)
{
if (on) {
- aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
- aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) |
+ CRT_CRTC_ON);
+ aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE2_SNOOP_EN));
} else
- aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+ aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) &
+ ~CRT_CRTC_ON);
}
static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
@@ -1281,7 +1290,8 @@ static void aty128_set_lcd_enable(struct aty128fb_par *par, int on)
}
}
-static void aty128_set_pll(struct aty128_pll *pll, const struct aty128fb_par *par)
+static void aty128_set_pll(struct aty128_pll *pll,
+ const struct aty128fb_par *par)
{
u32 div3;
@@ -1366,7 +1376,8 @@ static int aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
}
-static int aty128_pll_to_var(const struct aty128_pll *pll, struct fb_var_screeninfo *var)
+static int aty128_pll_to_var(const struct aty128_pll *pll,
+ struct fb_var_screeninfo *var)
{
var->pixclock = 100000000 / pll->vclk;
@@ -1512,7 +1523,8 @@ static int aty128fb_set_par(struct fb_info *info)
* encode/decode the User Defined Part of the Display
*/
-static int aty128_decode_var(struct fb_var_screeninfo *var, struct aty128fb_par *par)
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+ struct aty128fb_par *par)
{
int err;
struct aty128_crtc crtc;
@@ -1559,7 +1571,8 @@ static int aty128_encode_var(struct fb_var_screeninfo *var,
}
-static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int aty128fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct aty128fb_par par;
int err;
@@ -1575,7 +1588,8 @@ static int aty128fb_check_var(struct fb_var_screeninfo *var, struct fb_info *inf
/*
* Pan or Wrap the Display
*/
-static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb)
+static int aty128fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fb)
{
struct aty128fb_par *par = fb->par;
u32 xoffset, yoffset;
@@ -1594,7 +1608,8 @@ static int aty128fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *f
par->crtc.xoffset = xoffset;
par->crtc.yoffset = yoffset;
- offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+ offset = ((yoffset * par->crtc.vxres + xoffset) * (par->crtc.bpp >> 3))
+ & ~7;
if (par->crtc.bpp == 24)
offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
@@ -1620,11 +1635,13 @@ static void aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
* do mirroring
*/
- aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) |
+ DAC_PALETTE_ACCESS_CNTL);
aty_st_8(PALETTE_INDEX, regno);
aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
#endif
- aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+ aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) &
+ ~DAC_PALETTE_ACCESS_CNTL);
}
aty_st_8(PALETTE_INDEX, regno);
@@ -1753,7 +1770,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
aty_st_le32(LVDS_GEN_CNTL, reg);
}
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
- reg |= (aty128_bl_get_level_brightness(par, level) << LVDS_BL_MOD_LEVEL_SHIFT);
+ reg |= (aty128_bl_get_level_brightness(par, level) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_ON | LVDS_EN;
reg &= ~LVDS_DISPLAY_DIS;
@@ -1764,7 +1782,8 @@ static int aty128_bl_update_status(struct backlight_device *bd)
#endif
} else {
reg &= ~LVDS_BL_MOD_LEVEL_MASK;
- reg |= (aty128_bl_get_level_brightness(par, 0) << LVDS_BL_MOD_LEVEL_SHIFT);
+ reg |= (aty128_bl_get_level_brightness(par, 0) <<
+ LVDS_BL_MOD_LEVEL_SHIFT);
#ifdef BACKLIGHT_LVDS_OFF
reg |= LVDS_DISPLAY_DIS;
aty_st_le32(LVDS_GEN_CNTL, reg);
@@ -1869,7 +1888,8 @@ static void aty128_early_resume(void *data)
}
#endif /* CONFIG_PPC_PMAC */
-static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_init(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct aty128fb_par *par = info->par;
@@ -1887,7 +1907,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
/* range check to make sure */
if (ent->driver_data < ARRAY_SIZE(r128_family))
- strlcat(video_card, r128_family[ent->driver_data], sizeof(video_card));
+ strlcat(video_card, r128_family[ent->driver_data],
+ sizeof(video_card));
printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
@@ -1911,11 +1932,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
/* Indicate sleep capability */
if (par->chip_gen == rage_M3) {
pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
-#if 0 /* Disable the early video resume hack for now as it's causing problems, among
- * others we now rely on the PCI core restoring the config space for us, which
- * isn't the case with that hack, and that code path causes various things to
- * be called with interrupts off while they shouldn't. I'm leaving the code in
- * as it can be useful for debugging purposes
+#if 0 /* Disable the early video resume hack for now as it's causing problems,
+ * among others we now rely on the PCI core restoring the config space
+ * for us, which isn't the case with that hack, and that code path causes
+ * various things to be called with interrupts off while they shouldn't.
+ * I'm leaving the code in as it can be useful for debugging purposes
*/
pmac_set_early_video_resume(aty128_early_resume, par);
#endif
@@ -1953,11 +1974,11 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
default_vmode = VMODE_1152_768_60;
if (default_cmode > 16)
- default_cmode = CMODE_32;
+ default_cmode = CMODE_32;
else if (default_cmode > 8)
- default_cmode = CMODE_16;
+ default_cmode = CMODE_16;
else
- default_cmode = CMODE_8;
+ default_cmode = CMODE_8;
if (mac_vmode_to_var(default_vmode, default_cmode, &var))
var = default_var;
@@ -2018,7 +2039,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
#ifdef CONFIG_PCI
/* register a card ++ajoshi */
-static int __devinit aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
{
unsigned long fb_addr, reg_addr;
struct aty128fb_par *par;
@@ -2318,39 +2340,39 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
u_int width, u_int height,
struct fb_info_aty128 *par)
{
- u32 save_dp_datatype, save_dp_cntl, dstval;
-
- if (!width || !height)
- return;
-
- dstval = depth_to_dst(par->current_par.crtc.depth);
- if (dstval == DST_24BPP) {
- srcx *= 3;
- dstx *= 3;
- width *= 3;
- } else if (dstval == -EINVAL) {
- printk("aty128fb: invalid depth or RGBA\n");
- return;
- }
-
- wait_for_fifo(2, par);
- save_dp_datatype = aty_ld_le32(DP_DATATYPE);
- save_dp_cntl = aty_ld_le32(DP_CNTL);
-
- wait_for_fifo(6, par);
- aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
- aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
- aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
- aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
-
- aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
- aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
-
- par->blitter_may_be_busy = 1;
-
- wait_for_fifo(2, par);
- aty_st_le32(DP_DATATYPE, save_dp_datatype);
- aty_st_le32(DP_CNTL, save_dp_cntl);
+ u32 save_dp_datatype, save_dp_cntl, dstval;
+
+ if (!width || !height)
+ return;
+
+ dstval = depth_to_dst(par->current_par.crtc.depth);
+ if (dstval == DST_24BPP) {
+ srcx *= 3;
+ dstx *= 3;
+ width *= 3;
+ } else if (dstval == -EINVAL) {
+ printk("aty128fb: invalid depth or RGBA\n");
+ return;
+ }
+
+ wait_for_fifo(2, par);
+ save_dp_datatype = aty_ld_le32(DP_DATATYPE);
+ save_dp_cntl = aty_ld_le32(DP_CNTL);
+
+ wait_for_fifo(6, par);
+ aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
+ aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
+ aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
+
+ aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
+ aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
+
+ par->blitter_may_be_busy = 1;
+
+ wait_for_fifo(2, par);
+ aty_st_le32(DP_DATATYPE, save_dp_datatype);
+ aty_st_le32(DP_CNTL, save_dp_cntl);
}
@@ -2358,17 +2380,17 @@ static inline void aty128_rectcopy(int srcx, int srcy, int dstx, int dsty,
* Text mode accelerated functions
*/
-static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy, int dx,
- int height, int width)
+static void fbcon_aty128_bmove(struct display *p, int sy, int sx, int dy,
+ int dx, int height, int width)
{
- sx *= fontwidth(p);
- sy *= fontheight(p);
- dx *= fontwidth(p);
- dy *= fontheight(p);
- width *= fontwidth(p);
- height *= fontheight(p);
-
- aty128_rectcopy(sx, sy, dx, dy, width, height,
+ sx *= fontwidth(p);
+ sy *= fontheight(p);
+ dx *= fontwidth(p);
+ dy *= fontheight(p);
+ width *= fontwidth(p);
+ height *= fontheight(p);
+
+ aty128_rectcopy(sx, sy, dx, dy, width, height,
(struct fb_info_aty128 *)p->fb_info);
}
#endif /* 0 */
diff --git a/drivers/video/auo_k190x.c b/drivers/video/auo_k190x.c
index 77da6a2f43dc..c03ecdd31e4c 100644
--- a/drivers/video/auo_k190x.c
+++ b/drivers/video/auo_k190x.c
@@ -987,7 +987,6 @@ err_regfb:
fb_dealloc_cmap(&info->cmap);
err_cmap:
fb_deferred_io_cleanup(info);
- kfree(info->fbdefio);
err_defio:
vfree((void *)info->screen_base);
err_irq:
@@ -1022,7 +1021,6 @@ int __devexit auok190x_common_remove(struct platform_device *pdev)
fb_dealloc_cmap(&info->cmap);
fb_deferred_io_cleanup(info);
- kfree(info->fbdefio);
vfree((void *)info->screen_base);
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index 28b1a834906b..61b182bf32a2 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -162,7 +162,7 @@ static void bit_putcs(struct vc_data *vc, struct fb_info *info,
image.depth = 1;
if (attribute) {
- buf = kmalloc(cellsize, GFP_KERNEL);
+ buf = kmalloc(cellsize, GFP_ATOMIC);
if (!buf)
return;
}
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 2e471c22abf5..fdefa8fd72c4 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -372,8 +372,15 @@ static void fb_flashcursor(struct work_struct *work)
struct vc_data *vc = NULL;
int c;
int mode;
+ int ret;
+
+ /* FIXME: we should sort out the unbind locking instead */
+ /* instead we just fail to flash the cursor if we can't get
+ * the lock instead of blocking fbcon deinit */
+ ret = console_trylock();
+ if (ret == 0)
+ return;
- console_lock();
if (ops && ops->currcon != -1)
vc = vc_cons[ops->currcon].d;
@@ -442,7 +449,7 @@ static int __init fb_console_setup(char *this_opt)
while ((options = strsep(&this_opt, ",")) != NULL) {
if (!strncmp(options, "font:", 5))
- strcpy(fontname, options + 5);
+ strlcpy(fontname, options + 5, sizeof(fontname));
if (!strncmp(options, "scrollback:", 11)) {
options += 11;
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 47118c75a4c0..7ae9d53f2bf1 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -30,7 +30,10 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/console.h>
+#include <linux/spinlock.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/lcm.h>
#include <video/da8xx-fb.h>
#include <asm/div64.h>
@@ -160,6 +163,13 @@ struct da8xx_fb_par {
wait_queue_head_t vsync_wait;
int vsync_flag;
int vsync_timeout;
+ spinlock_t lock_for_chan_update;
+
+ /*
+ * LCDC has 2 ping pong DMA channels, channel 0
+ * and channel 1.
+ */
+ unsigned int which_dma_channel_done;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
unsigned int lcd_fck_rate;
@@ -260,10 +270,18 @@ static inline void lcd_enable_raster(void)
{
u32 reg;
+ /* Put LCDC in reset for several cycles */
+ if (lcd_revision == LCD_VERSION_2)
+ /* Write 1 to reset LCDC */
+ lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
+ mdelay(1);
+
/* Bring LCDC out of reset */
if (lcd_revision == LCD_VERSION_2)
lcdc_write(0, LCD_CLK_RESET_REG);
+ mdelay(1);
+ /* Above reset sequence doesnot reset register context */
reg = lcdc_read(LCD_RASTER_CTRL_REG);
if (!(reg & LCD_RASTER_ENABLE))
lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
@@ -277,10 +295,6 @@ static inline void lcd_disable_raster(void)
reg = lcdc_read(LCD_RASTER_CTRL_REG);
if (reg & LCD_RASTER_ENABLE)
lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
-
- if (lcd_revision == LCD_VERSION_2)
- /* Write 1 to reset LCDC */
- lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
}
static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
@@ -344,8 +358,8 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
lcd_enable_raster();
}
-/* Configure the Burst Size of DMA */
-static int lcd_cfg_dma(int burst_size)
+/* Configure the Burst Size and fifo threhold of DMA */
+static int lcd_cfg_dma(int burst_size, int fifo_th)
{
u32 reg;
@@ -369,6 +383,9 @@ static int lcd_cfg_dma(int burst_size)
default:
return -EINVAL;
}
+
+ reg |= (fifo_th << 8);
+
lcdc_write(reg, LCD_DMA_CTRL_REG);
return 0;
@@ -670,8 +687,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) &
~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
- /* Configure the DMA burst size. */
- ret = lcd_cfg_dma(cfg->dma_burst_sz);
+ /* Configure the DMA burst size and fifo threshold. */
+ ret = lcd_cfg_dma(cfg->dma_burst_sz, cfg->fifo_th);
if (ret < 0)
return ret;
@@ -715,7 +732,6 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
{
struct da8xx_fb_par *par = arg;
u32 stat = lcdc_read(LCD_MASKED_STAT_REG);
- u32 reg_int;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
lcd_disable_raster();
@@ -732,10 +748,8 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
lcdc_write(stat, LCD_MASKED_STAT_REG);
- /* Disable PL completion inerrupt */
- reg_int = lcdc_read(LCD_INT_ENABLE_CLR_REG) |
- (LCD_V2_PL_INT_ENA);
- lcdc_write(reg_int, LCD_INT_ENABLE_CLR_REG);
+ /* Disable PL completion interrupt */
+ lcdc_write(LCD_V2_PL_INT_ENA, LCD_INT_ENABLE_CLR_REG);
/* Setup and start data loading mode */
lcd_blit(LOAD_DATA, par);
@@ -743,6 +757,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
lcdc_write(stat, LCD_MASKED_STAT_REG);
if (stat & LCD_END_OF_FRAME0) {
+ par->which_dma_channel_done = 0;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
@@ -752,6 +767,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
}
if (stat & LCD_END_OF_FRAME1) {
+ par->which_dma_channel_done = 1;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
@@ -798,6 +814,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
lcdc_write(stat, LCD_STAT_REG);
if (stat & LCD_END_OF_FRAME0) {
+ par->which_dma_channel_done = 0;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
lcdc_write(par->dma_end,
@@ -807,6 +824,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
}
if (stat & LCD_END_OF_FRAME1) {
+ par->which_dma_channel_done = 1;
lcdc_write(par->dma_start,
LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
lcdc_write(par->dma_end,
@@ -1021,11 +1039,14 @@ static int cfb_blank(int blank, struct fb_info *info)
par->blank = blank;
switch (blank) {
case FB_BLANK_UNBLANK:
+ lcd_enable_raster();
+
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
-
- lcd_enable_raster();
break;
+ case FB_BLANK_NORMAL:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
if (par->panel_power_ctrl)
par->panel_power_ctrl(0);
@@ -1052,6 +1073,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
struct fb_fix_screeninfo *fix = &fbi->fix;
unsigned int end;
unsigned int start;
+ unsigned long irq_flags;
if (var->xoffset != fbi->var.xoffset ||
var->yoffset != fbi->var.yoffset) {
@@ -1069,6 +1091,21 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
end = start + fbi->var.yres * fix->line_length - 1;
par->dma_start = start;
par->dma_end = end;
+ spin_lock_irqsave(&par->lock_for_chan_update,
+ irq_flags);
+ if (par->which_dma_channel_done == 0) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+ } else if (par->which_dma_channel_done == 1) {
+ lcdc_write(par->dma_start,
+ LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+ lcdc_write(par->dma_end,
+ LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+ }
+ spin_unlock_irqrestore(&par->lock_for_chan_update,
+ irq_flags);
}
}
@@ -1114,6 +1151,7 @@ static int __devinit fb_probe(struct platform_device *device)
struct da8xx_fb_par *par;
resource_size_t len;
int ret, i;
+ unsigned long ulcm;
if (fb_pdata == NULL) {
dev_err(&device->dev, "Can not get platform data\n");
@@ -1209,7 +1247,8 @@ static int __devinit fb_probe(struct platform_device *device)
/* allocate frame buffer */
par->vram_size = lcdc_info->width * lcdc_info->height * lcd_cfg->bpp;
- par->vram_size = PAGE_ALIGN(par->vram_size/8);
+ ulcm = lcm((lcdc_info->width * lcd_cfg->bpp)/8, PAGE_SIZE);
+ par->vram_size = roundup(par->vram_size/8, ulcm);
par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
par->vram_virt = dma_alloc_coherent(NULL,
@@ -1296,6 +1335,8 @@ static int __devinit fb_probe(struct platform_device *device)
/* initialize the vsync wait queue */
init_waitqueue_head(&par->vsync_wait);
par->vsync_timeout = HZ / 5;
+ par->which_dma_channel_done = -1;
+ spin_lock_init(&par->lock_for_chan_update);
/* Register the Frame Buffer */
if (register_framebuffer(da8xx_fb_info) < 0) {
@@ -1382,11 +1423,12 @@ static int fb_resume(struct platform_device *dev)
struct da8xx_fb_par *par = info->par;
console_lock();
+ clk_enable(par->lcdc_clk);
+ lcd_enable_raster();
+
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
- clk_enable(par->lcdc_clk);
- lcd_enable_raster();
fb_set_suspend(info, 0);
console_unlock();
diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c
index a268cbf1cbea..68b9b511ce80 100644
--- a/drivers/video/epson1355fb.c
+++ b/drivers/video/epson1355fb.c
@@ -477,11 +477,11 @@ static __init unsigned int get_fb_size(struct fb_info *info)
return size;
}
-static int epson1355_width_tab[2][4] __initdata =
+static int epson1355_width_tab[2][4] __devinitdata =
{ {4, 8, 16, -1}, {9, 12, 16, -1} };
-static int epson1355_bpp_tab[8] __initdata = { 1, 2, 4, 8, 15, 16 };
+static int epson1355_bpp_tab[8] __devinitdata = { 1, 2, 4, 8, 15, 16 };
-static void __init fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
+static void __devinit fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
{
struct fb_var_screeninfo *var = &info->var;
struct fb_fix_screeninfo *fix = &info->fix;
@@ -601,7 +601,7 @@ static int epson1355fb_remove(struct platform_device *dev)
return 0;
}
-int __devinit epson1355fb_probe(struct platform_device *dev)
+static int __devinit epson1355fb_probe(struct platform_device *dev)
{
struct epson1355_par *default_par;
struct fb_info *info;
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c
index a36b2d28280e..c6c016a506ce 100644
--- a/drivers/video/exynos/exynos_dp_core.c
+++ b/drivers/video/exynos/exynos_dp_core.c
@@ -47,7 +47,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
exynos_dp_init_hpd(dp);
- udelay(200);
+ usleep_range(200, 210);
while (exynos_dp_get_plug_in_status(dp) != 0) {
timeout_loop++;
@@ -55,7 +55,7 @@ static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
dev_err(dp->dev, "failed to get hpd plug status\n");
return -ETIMEDOUT;
}
- udelay(10);
+ usleep_range(10, 11);
}
return 0;
@@ -304,7 +304,7 @@ static void exynos_dp_link_start(struct exynos_dp_device *dp)
buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
exynos_dp_write_bytes_to_dpcd(dp,
- DPCD_ADDR_TRAINING_PATTERN_SET,
+ DPCD_ADDR_TRAINING_LANE0_SET,
lane_count, buf);
}
@@ -336,7 +336,7 @@ static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count)
u8 lane_status;
lane_align = link_status[2];
- if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0)
+ if ((lane_align & DPCD_INTERLANE_ALIGN_DONE) == 0)
return -EINVAL;
for (lane = 0; lane < lane_count; lane++) {
@@ -407,6 +407,9 @@ static unsigned int exynos_dp_get_lane_link_training(
case 3:
reg = exynos_dp_get_lane3_link_training(dp);
break;
+ default:
+ WARN_ON(1);
+ return 0;
}
return reg;
@@ -483,7 +486,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
u8 pre_emphasis;
u8 training_lane;
- udelay(100);
+ usleep_range(100, 101);
exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
6, link_status);
@@ -501,7 +504,7 @@ static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
buf[0] = DPCD_SCRAMBLING_DISABLED |
DPCD_TRAINING_PATTERN_2;
exynos_dp_write_byte_to_dpcd(dp,
- DPCD_ADDR_TRAINING_LANE0_SET,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
buf[0]);
for (lane = 0; lane < lane_count; lane++) {
@@ -568,7 +571,7 @@ static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
u8 adjust_request[2];
- udelay(400);
+ usleep_range(400, 401);
exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
6, link_status);
@@ -736,7 +739,7 @@ static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
if (retval == 0)
break;
- udelay(100);
+ usleep_range(100, 110);
}
return retval;
@@ -770,7 +773,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
return -ETIMEDOUT;
}
- udelay(1);
+ usleep_range(1, 2);
}
/* Set to use the register calculated M/N video */
@@ -804,7 +807,7 @@ static int exynos_dp_config_video(struct exynos_dp_device *dp,
return -ETIMEDOUT;
}
- mdelay(1);
+ usleep_range(1000, 1001);
}
if (retval != 0)
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h
index 1e0f998e0c9f..8526e548c385 100644
--- a/drivers/video/exynos/exynos_dp_core.h
+++ b/drivers/video/exynos/exynos_dp_core.h
@@ -85,10 +85,6 @@ void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
-void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
-void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
-void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
-void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
enum pattern_set pattern);
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c
index bcb0e3ae1e9d..2db5b9aa250a 100644
--- a/drivers/video/exynos/exynos_dp_reg.c
+++ b/drivers/video/exynos/exynos_dp_reg.c
@@ -122,7 +122,7 @@ void exynos_dp_reset(struct exynos_dp_device *dp)
LS_CLK_DOMAIN_FUNC_EN_N;
writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
- udelay(20);
+ usleep_range(20, 30);
exynos_dp_lane_swap(dp, 0);
@@ -988,7 +988,7 @@ void exynos_dp_reset_macro(struct exynos_dp_device *dp)
writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
/* 10 us is the minimum reset time. */
- udelay(10);
+ usleep_range(10, 20);
reg &= ~MACRO_RST;
writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
index 9908e75ae761..4bc2b8a5dd8b 100644
--- a/drivers/video/exynos/exynos_mipi_dsi.c
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -154,7 +154,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
if (client_drv && client_drv->power_on)
client_drv->power_on(client_dev, 1);
- exynos_mipi_regulator_disable(dsim);
+ exynos_mipi_regulator_enable(dsim);
/* enable MIPI-DSI PHY. */
if (dsim->pd->phy_enable)
diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h
deleted file mode 100644
index 1f1b270484b0..000000000000
--- a/drivers/video/exynos/s6e8ax0.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* linux/drivers/video/backlight/s6e8ax0.h
- *
- * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions.
- *
- * Copyright (c) 2011 Samsung Electronics
- *
- * Inki Dae, <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.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.
-*/
-
-#ifndef _S6E8AX0_H
-#define _S6E8AX0_H
-
-extern void s6e8ax0_init(void);
-
-#endif
-
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 1ddeb11659d4..64cda560c488 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -104,6 +104,8 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
deferred framebuffer IO. then if userspace touches a page
again, we repeat the same scheme */
+ file_update_time(vma->vm_file);
+
/* protect against the workqueue changing the page list */
mutex_lock(&fbdefio->lock);
diff --git a/drivers/video/fb_draw.h b/drivers/video/fb_draw.h
index 04c01faaf772..624ee115f129 100644
--- a/drivers/video/fb_draw.h
+++ b/drivers/video/fb_draw.h
@@ -3,6 +3,7 @@
#include <asm/types.h>
#include <linux/fb.h>
+#include <linux/bug.h>
/*
* Compose two values, using a bitmask as decision value
@@ -41,7 +42,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 32:
return 0x0000000100000001ul*pixel;
default:
- panic("pixel_to_pat(): unsupported pixelformat\n");
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
}
}
#else
@@ -66,7 +68,8 @@ pixel_to_pat( u32 bpp, u32 pixel)
case 32:
return 0x00000001ul*pixel;
default:
- panic("pixel_to_pat(): unsupported pixelformat\n");
+ WARN(1, "pixel_to_pat(): unsupported pixelformat %d\n", bpp);
+ return 0;
}
}
#endif
diff --git a/drivers/video/grvga.c b/drivers/video/grvga.c
index da066c210923..5245f9a71892 100644
--- a/drivers/video/grvga.c
+++ b/drivers/video/grvga.c
@@ -354,7 +354,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
*/
if (fb_get_options("grvga", &options)) {
retval = -ENODEV;
- goto err;
+ goto free_fb;
}
if (!options || !*options)
@@ -370,7 +370,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (grvga_parse_custom(this_opt, &info->var) < 0) {
dev_err(&dev->dev, "Failed to parse custom mode (%s).\n", this_opt);
retval = -EINVAL;
- goto err1;
+ goto free_fb;
}
} else if (!strncmp(this_opt, "addr", 4))
grvga_fix_addr = simple_strtoul(this_opt + 5, NULL, 16);
@@ -387,10 +387,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | FBINFO_HWACCEL_YPAN;
info->fix.smem_len = grvga_mem_size;
- if (!request_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
+ if (!devm_request_mem_region(&dev->dev, dev->resource[0].start,
+ resource_size(&dev->resource[0]), "grlib-svgactrl regs")) {
dev_err(&dev->dev, "registers already mapped\n");
retval = -EBUSY;
- goto err;
+ goto free_fb;
}
par->regs = of_ioremap(&dev->resource[0], 0,
@@ -400,14 +401,14 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (!par->regs) {
dev_err(&dev->dev, "failed to map registers\n");
retval = -ENOMEM;
- goto err1;
+ goto free_fb;
}
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0) {
dev_err(&dev->dev, "failed to allocate mem with fb_alloc_cmap\n");
retval = -ENOMEM;
- goto err2;
+ goto unmap_regs;
}
if (mode_opt) {
@@ -415,7 +416,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
grvga_modedb, sizeof(grvga_modedb), &grvga_modedb[0], 8);
if (!retval || retval == 4) {
retval = -EINVAL;
- goto err3;
+ goto dealloc_cmap;
}
}
@@ -427,10 +428,11 @@ static int __devinit grvga_probe(struct platform_device *dev)
physical_start = grvga_fix_addr;
- if (!request_mem_region(physical_start, grvga_mem_size, dev->name)) {
+ if (!devm_request_mem_region(&dev->dev, physical_start,
+ grvga_mem_size, dev->name)) {
dev_err(&dev->dev, "failed to request memory region\n");
retval = -ENOMEM;
- goto err3;
+ goto dealloc_cmap;
}
virtual_start = (unsigned long) ioremap(physical_start, grvga_mem_size);
@@ -438,7 +440,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
if (!virtual_start) {
dev_err(&dev->dev, "error mapping framebuffer memory\n");
retval = -ENOMEM;
- goto err4;
+ goto dealloc_cmap;
}
} else { /* Allocate frambuffer memory */
@@ -451,7 +453,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
"unable to allocate framebuffer memory (%lu bytes)\n",
grvga_mem_size);
retval = -ENOMEM;
- goto err3;
+ goto dealloc_cmap;
}
physical_start = dma_map_single(&dev->dev, (void *)virtual_start, grvga_mem_size, DMA_TO_DEVICE);
@@ -484,7 +486,7 @@ static int __devinit grvga_probe(struct platform_device *dev)
retval = register_framebuffer(info);
if (retval < 0) {
dev_err(&dev->dev, "failed to register framebuffer\n");
- goto err4;
+ goto free_mem;
}
__raw_writel(physical_start, &par->regs->fb_pos);
@@ -493,21 +495,18 @@ static int __devinit grvga_probe(struct platform_device *dev)
return 0;
-err4:
+free_mem:
dev_set_drvdata(&dev->dev, NULL);
- if (grvga_fix_addr) {
- release_mem_region(physical_start, grvga_mem_size);
+ if (grvga_fix_addr)
iounmap((void *)virtual_start);
- } else
+ else
kfree((void *)virtual_start);
-err3:
+dealloc_cmap:
fb_dealloc_cmap(&info->cmap);
-err2:
+unmap_regs:
of_iounmap(&dev->resource[0], par->regs,
resource_size(&dev->resource[0]));
-err1:
- release_mem_region(dev->resource[0].start, resource_size(&dev->resource[0]));
-err:
+free_fb:
framebuffer_release(info);
return retval;
@@ -524,12 +523,10 @@ static int __devexit grvga_remove(struct platform_device *device)
of_iounmap(&device->resource[0], par->regs,
resource_size(&device->resource[0]));
- release_mem_region(device->resource[0].start, resource_size(&device->resource[0]));
- if (!par->fb_alloced) {
- release_mem_region(info->fix.smem_start, info->fix.smem_len);
+ if (!par->fb_alloced)
iounmap(info->screen_base);
- } else
+ else
kfree((void *)info->screen_base);
framebuffer_release(info);
diff --git a/drivers/video/mb862xx/mb862xxfbdrv.c b/drivers/video/mb862xx/mb862xxfbdrv.c
index 00ce1f34b496..57d940be5f3d 100644
--- a/drivers/video/mb862xx/mb862xxfbdrv.c
+++ b/drivers/video/mb862xx/mb862xxfbdrv.c
@@ -328,6 +328,8 @@ static int mb862xxfb_ioctl(struct fb_info *fbi, unsigned int cmd,
case MB862XX_L1_SET_CFG:
if (copy_from_user(l1_cfg, argp, sizeof(*l1_cfg)))
return -EFAULT;
+ if (l1_cfg->dh == 0 || l1_cfg->dw == 0)
+ return -EINVAL;
if ((l1_cfg->sw >= l1_cfg->dw) && (l1_cfg->sh >= l1_cfg->dh)) {
/* downscaling */
outreg(cap, GC_CAP_CSC,
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c
index eec0d7b748eb..c89f8a8d36d2 100644
--- a/drivers/video/mx3fb.c
+++ b/drivers/video/mx3fb.c
@@ -269,7 +269,7 @@ struct mx3fb_info {
dma_cookie_t cookie;
struct scatterlist sg[2];
- u32 sync; /* preserve var->sync flags */
+ struct fb_var_screeninfo cur_var; /* current var info */
};
static void mx3fb_dma_done(void *);
@@ -698,9 +698,29 @@ static void mx3fb_dma_done(void *arg)
complete(&mx3_fbi->flip_cmpl);
}
+static bool mx3fb_must_set_par(struct fb_info *fbi)
+{
+ struct mx3fb_info *mx3_fbi = fbi->par;
+ struct fb_var_screeninfo old_var = mx3_fbi->cur_var;
+ struct fb_var_screeninfo new_var = fbi->var;
+
+ if ((fbi->var.activate & FB_ACTIVATE_FORCE) &&
+ (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
+ return true;
+
+ /*
+ * Ignore xoffset and yoffset update,
+ * because pan display handles this case.
+ */
+ old_var.xoffset = new_var.xoffset;
+ old_var.yoffset = new_var.yoffset;
+
+ return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo));
+}
+
static int __set_par(struct fb_info *fbi, bool lock)
{
- u32 mem_len;
+ u32 mem_len, cur_xoffset, cur_yoffset;
struct ipu_di_signal_cfg sig_cfg;
enum ipu_panel mode = IPU_PANEL_TFT;
struct mx3fb_info *mx3_fbi = fbi->par;
@@ -780,8 +800,25 @@ static int __set_par(struct fb_info *fbi, bool lock)
video->out_height = fbi->var.yres;
video->out_stride = fbi->var.xres_virtual;
- if (mx3_fbi->blank == FB_BLANK_UNBLANK)
+ if (mx3_fbi->blank == FB_BLANK_UNBLANK) {
sdc_enable_channel(mx3_fbi);
+ /*
+ * sg[0] points to fb smem_start address
+ * and is actually active in controller.
+ */
+ mx3_fbi->cur_var.xoffset = 0;
+ mx3_fbi->cur_var.yoffset = 0;
+ }
+
+ /*
+ * Preserve xoffset and yoffest in case they are
+ * inactive in controller as fb is blanked.
+ */
+ cur_xoffset = mx3_fbi->cur_var.xoffset;
+ cur_yoffset = mx3_fbi->cur_var.yoffset;
+ mx3_fbi->cur_var = fbi->var;
+ mx3_fbi->cur_var.xoffset = cur_xoffset;
+ mx3_fbi->cur_var.yoffset = cur_yoffset;
return 0;
}
@@ -802,7 +839,7 @@ static int mx3fb_set_par(struct fb_info *fbi)
mutex_lock(&mx3_fbi->mutex);
- ret = __set_par(fbi, true);
+ ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0;
mutex_unlock(&mx3_fbi->mutex);
@@ -901,8 +938,8 @@ static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
var->grayscale = 0;
/* Preserve sync flags */
- var->sync |= mx3_fbi->sync;
- mx3_fbi->sync |= var->sync;
+ var->sync |= mx3_fbi->cur_var.sync;
+ mx3_fbi->cur_var.sync |= var->sync;
return 0;
}
@@ -1043,8 +1080,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
return -EINVAL;
}
- if (fbi->var.xoffset == var->xoffset &&
- fbi->var.yoffset == var->yoffset)
+ if (mx3_fbi->cur_var.xoffset == var->xoffset &&
+ mx3_fbi->cur_var.yoffset == var->yoffset)
return 0; /* No change, do nothing */
y_bottom = var->yoffset;
@@ -1127,6 +1164,8 @@ static int mx3fb_pan_display(struct fb_var_screeninfo *var,
else
fbi->var.vmode &= ~FB_VMODE_YWRAP;
+ mx3_fbi->cur_var = fbi->var;
+
mutex_unlock(&mx3_fbi->mutex);
dev_dbg(fbi->device, "Update complete\n");
diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c
index ad741c3d1ae1..eaeed4340e04 100644
--- a/drivers/video/omap2/displays/panel-acx565akm.c
+++ b/drivers/video/omap2/displays/panel-acx565akm.c
@@ -487,6 +487,13 @@ static struct omap_video_timings acx_panel_timings = {
.vfp = 3,
.vsw = 3,
.vbp = 4,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int acx_panel_probe(struct omap_dss_device *dssdev)
@@ -498,8 +505,7 @@ static int acx_panel_probe(struct omap_dss_device *dssdev)
struct backlight_properties props;
dev_dbg(&dssdev->dev, "%s\n", __func__);
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
+
/* FIXME AC bias ? */
dssdev->panel.timings = acx_panel_timings;
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index e42f9dc22123..bc5af2500eb9 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -40,12 +40,6 @@
struct panel_config {
struct omap_video_timings timings;
- int acbi; /* ac-bias pin transitions per interrupt */
- /* Unit: line clocks */
- int acb; /* ac-bias pin frequency */
-
- enum omap_panel_config config;
-
int power_on_delay;
int power_off_delay;
@@ -73,11 +67,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 11,
.vfp = 3,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_lq",
@@ -98,11 +94,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x28,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 50,
.power_off_delay = 100,
.name = "sharp_ls",
@@ -123,12 +121,13 @@ static struct panel_config generic_dpi_panels[] = {
.vfp = 4,
.vsw = 2,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
- OMAP_DSS_LCD_ONOFF,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "toppoly_tdo35s",
@@ -149,11 +148,13 @@ static struct panel_config generic_dpi_panels[] = {
.vfp = 4,
.vsw = 10,
.vbp = 12 - 10,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "samsung_lte430wq_f0c",
@@ -174,11 +175,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 4,
.vbp = 11,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "seiko_70wvw1tz3",
@@ -199,11 +202,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 2,
.vbp = 2,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "powertip_ph480272t",
@@ -224,11 +229,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 3,
.vfp = 12,
.vbp = 25,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x28,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "innolux_at070tn83",
@@ -249,9 +256,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 2,
.vbp = 7,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.name = "nec_nl2432dr22-11b",
},
@@ -270,9 +281,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
- },
- .config = OMAP_DSS_LCD_TFT,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "h4",
},
@@ -291,10 +306,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 2,
.vbp = 2,
- },
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "apollon",
},
/* FocalTech ETM070003DH6 */
@@ -312,9 +330,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 3,
.vfp = 13,
.vbp = 29,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS,
.name = "focaltech_etm070003dh6",
},
@@ -333,11 +355,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 23,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .acbi = 0x0,
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.power_on_delay = 0,
.power_off_delay = 0,
.name = "microtips_umsh_8173md",
@@ -358,9 +382,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 4,
.vbp = 2,
- },
- .config = OMAP_DSS_LCD_TFT,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "ortustech_com43h4m10xtc",
},
@@ -379,11 +407,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 10,
.vfp = 12,
.vbp = 23,
- },
- .acb = 0x0,
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ },
.name = "innolux_at080tn52",
},
@@ -401,8 +431,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 1,
.vfp = 26,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT,
.name = "mitsubishi_aa084sb01",
},
/* EDT ET0500G0DH6 */
@@ -419,8 +454,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 35,
.vbp = 10,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT,
.name = "edt_et0500g0dh6",
},
@@ -439,9 +479,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 10,
.vbp = 33,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pd050vl1",
},
@@ -460,9 +504,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 2,
.vfp = 10,
.vbp = 33,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pm070wl4",
},
@@ -481,9 +529,13 @@ static struct panel_config generic_dpi_panels[] = {
.vsw = 4,
.vfp = 1,
.vbp = 23,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
},
- .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC,
.name = "primeview_pd104slf",
},
};
@@ -573,10 +625,7 @@ static int generic_dpi_panel_probe(struct omap_dss_device *dssdev)
if (!panel_config)
return -EINVAL;
- dssdev->panel.config = panel_config->config;
dssdev->panel.timings = panel_config->timings;
- dssdev->panel.acb = panel_config->acb;
- dssdev->panel.acbi = panel_config->acbi;
drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
if (!drv_data)
diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
index 0841cc2b3f77..802807798846 100644
--- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
+++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
@@ -40,6 +40,12 @@ static struct omap_video_timings lb035q02_timings = {
.vsw = 2,
.vfp = 4,
.vbp = 18,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int lb035q02_panel_power_on(struct omap_dss_device *dssdev)
@@ -82,8 +88,6 @@ static int lb035q02_panel_probe(struct omap_dss_device *dssdev)
struct lb035q02_data *ld;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
dssdev->panel.timings = lb035q02_timings;
ld = kzalloc(sizeof(*ld), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-n8x0.c b/drivers/video/omap2/displays/panel-n8x0.c
index 4a34cdc1371b..e6c115373c00 100644
--- a/drivers/video/omap2/displays/panel-n8x0.c
+++ b/drivers/video/omap2/displays/panel-n8x0.c
@@ -473,7 +473,6 @@ static int n8x0_panel_probe(struct omap_dss_device *dssdev)
mutex_init(&ddata->lock);
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings.x_res = 800;
dssdev->panel.timings.y_res = 480;
dssdev->ctrl.pixel_size = 16;
diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
index 8b38b39213f4..b122b0f31c43 100644
--- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
+++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
@@ -76,6 +76,12 @@ static struct omap_video_timings nec_8048_panel_timings = {
.vfp = 3,
.vsw = 1,
.vbp = 4,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
};
static int nec_8048_bl_update_status(struct backlight_device *bl)
@@ -116,9 +122,6 @@ static int nec_8048_panel_probe(struct omap_dss_device *dssdev)
struct backlight_properties props;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_RF |
- OMAP_DSS_LCD_ONOFF;
dssdev->panel.timings = nec_8048_panel_timings;
necd = kzalloc(sizeof(*necd), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-picodlp.c b/drivers/video/omap2/displays/panel-picodlp.c
index 98ebdaddab5a..2d35bd388860 100644
--- a/drivers/video/omap2/displays/panel-picodlp.c
+++ b/drivers/video/omap2/displays/panel-picodlp.c
@@ -69,6 +69,12 @@ static struct omap_video_timings pico_ls_timings = {
.vsw = 2,
.vfp = 3,
.vbp = 14,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
static inline struct picodlp_panel_data
@@ -414,9 +420,6 @@ static int picodlp_panel_probe(struct omap_dss_device *dssdev)
struct i2c_client *picodlp_i2c_client;
int r = 0, picodlp_adapter_id;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_ONOFF |
- OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IVS;
- dssdev->panel.acb = 0x0;
dssdev->panel.timings = pico_ls_timings;
picod = kzalloc(sizeof(struct picodlp_data), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
index ba38b3ad17d6..bd86ba9ccf76 100644
--- a/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
+++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c
@@ -44,6 +44,12 @@ static struct omap_video_timings sharp_ls_timings = {
.vsw = 1,
.vfp = 1,
.vbp = 1,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int sharp_ls_bl_update_status(struct backlight_device *bl)
@@ -86,9 +92,6 @@ static int sharp_ls_panel_probe(struct omap_dss_device *dssdev)
struct sharp_data *sd;
int r;
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
- OMAP_DSS_LCD_IHS;
- dssdev->panel.acb = 0x28;
dssdev->panel.timings = sharp_ls_timings;
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 901576eb5a84..3f5acc7771da 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -882,7 +882,6 @@ static int taal_probe(struct omap_dss_device *dssdev)
goto err;
}
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
dssdev->panel.timings = panel_config->timings;
dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
diff --git a/drivers/video/omap2/displays/panel-tfp410.c b/drivers/video/omap2/displays/panel-tfp410.c
index bff306e041ca..40cc0cfa5d17 100644
--- a/drivers/video/omap2/displays/panel-tfp410.c
+++ b/drivers/video/omap2/displays/panel-tfp410.c
@@ -39,6 +39,12 @@ static const struct omap_video_timings tfp410_default_timings = {
.vfp = 3,
.vsw = 4,
.vbp = 7,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
struct panel_drv_data {
@@ -95,7 +101,6 @@ static int tfp410_probe(struct omap_dss_device *dssdev)
return -ENOMEM;
dssdev->panel.timings = tfp410_default_timings;
- dssdev->panel.config = OMAP_DSS_LCD_TFT;
ddata->dssdev = dssdev;
mutex_init(&ddata->lock);
diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index 4b6448b3c31f..fa7baa650ae0 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -267,6 +267,12 @@ static const struct omap_video_timings tpo_td043_timings = {
.vsw = 1,
.vfp = 39,
.vbp = 34,
+
+ .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
+ .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+ .de_level = OMAPDSS_SIG_ACTIVE_HIGH,
+ .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
};
static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
@@ -423,8 +429,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
return -ENODEV;
}
- dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IHS |
- OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IPC;
dssdev->panel.timings = tpo_td043_timings;
dssdev->ctrl.pixel_size = 24;
diff --git a/drivers/video/omap2/dss/Kconfig b/drivers/video/omap2/dss/Kconfig
index 43324e5ed25f..b337a8469fd8 100644
--- a/drivers/video/omap2/dss/Kconfig
+++ b/drivers/video/omap2/dss/Kconfig
@@ -52,7 +52,7 @@ config OMAP2_DSS_RFBI
DBI is a bus between the host processor and a peripheral,
such as a display or a framebuffer chip.
- See http://www.mipi.org/ for DBI spesifications.
+ See http://www.mipi.org/ for DBI specifications.
config OMAP2_DSS_VENC
bool "VENC support"
@@ -92,7 +92,7 @@ config OMAP2_DSS_DSI
DSI is a high speed half-duplex serial interface between the host
processor and a peripheral, such as a display or a framebuffer chip.
- See http://www.mipi.org/ for DSI spesifications.
+ See http://www.mipi.org/ for DSI specifications.
config OMAP2_DSS_MIN_FCK_PER_PCK
int "Minimum FCK/PCK ratio (for scaling)"
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index ab22cc224f3e..0fefc68372b9 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -104,6 +104,7 @@ struct mgr_priv_data {
bool shadow_extra_info_dirty;
struct omap_video_timings timings;
+ struct dss_lcd_mgr_config lcd_config;
};
static struct {
@@ -137,6 +138,7 @@ static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
void dss_apply_init(void)
{
const int num_ovls = dss_feat_get_num_ovls();
+ struct mgr_priv_data *mp;
int i;
spin_lock_init(&data_lock);
@@ -168,16 +170,35 @@ void dss_apply_init(void)
op->user_info = op->info;
}
+
+ /*
+ * Initialize some of the lcd_config fields for TV manager, this lets
+ * us prevent checking if the manager is LCD or TV at some places
+ */
+ mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT];
+
+ mp->lcd_config.video_port_width = 24;
+ mp->lcd_config.clock_info.lck_div = 1;
+ mp->lcd_config.clock_info.pck_div = 1;
}
+/*
+ * A LCD manager's stallmode decides whether it is in manual or auto update. TV
+ * manager is always auto update, stallmode field for TV manager is false by
+ * default
+ */
static bool ovl_manual_update(struct omap_overlay *ovl)
{
- return ovl->manager->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ struct mgr_priv_data *mp = get_mgr_priv(ovl->manager);
+
+ return mp->lcd_config.stallmode;
}
static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
- return mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE;
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ return mp->lcd_config.stallmode;
}
static int dss_check_settings_low(struct omap_overlay_manager *mgr,
@@ -214,7 +235,7 @@ static int dss_check_settings_low(struct omap_overlay_manager *mgr,
ois[ovl->id] = oi;
}
- return dss_mgr_check(mgr, mi, &mp->timings, ois);
+ return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois);
}
/*
@@ -537,7 +558,7 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct omap_overlay_info *oi;
- bool ilace, replication;
+ bool replication;
struct mgr_priv_data *mp;
int r;
@@ -550,11 +571,9 @@ static void dss_ovl_write_regs(struct omap_overlay *ovl)
mp = get_mgr_priv(ovl->manager);
- replication = dss_use_replication(ovl->manager->device, oi->color_mode);
-
- ilace = ovl->manager->device->type == OMAP_DISPLAY_TYPE_VENC;
+ replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode);
- r = dispc_ovl_setup(ovl->id, oi, ilace, replication, &mp->timings);
+ r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings);
if (r) {
/*
* We can't do much here, as this function can be called from
@@ -635,6 +654,24 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
dispc_mgr_set_timings(mgr->id, &mp->timings);
+ /* lcd_config parameters */
+ if (dss_mgr_is_lcd(mgr->id)) {
+ dispc_mgr_set_io_pad_mode(mp->lcd_config.io_pad_mode);
+
+ dispc_mgr_enable_stallmode(mgr->id, mp->lcd_config.stallmode);
+ dispc_mgr_enable_fifohandcheck(mgr->id,
+ mp->lcd_config.fifohandcheck);
+
+ dispc_mgr_set_clock_div(mgr->id, &mp->lcd_config.clock_info);
+
+ dispc_mgr_set_tft_data_lines(mgr->id,
+ mp->lcd_config.video_port_width);
+
+ dispc_lcd_enable_signal_polarity(mp->lcd_config.lcden_sig_polarity);
+
+ dispc_mgr_set_lcd_type_tft(mgr->id);
+ }
+
mp->extra_info_dirty = false;
if (mp->updating)
mp->shadow_extra_info_dirty = true;
@@ -1294,6 +1331,44 @@ void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
mutex_unlock(&apply_lock);
}
+static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ mp->lcd_config = *config;
+ mp->extra_info_dirty = true;
+}
+
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ unsigned long flags;
+ struct mgr_priv_data *mp = get_mgr_priv(mgr);
+
+ mutex_lock(&apply_lock);
+
+ if (mp->enabled) {
+ DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n",
+ mgr->name);
+ goto out;
+ }
+
+ spin_lock_irqsave(&data_lock, flags);
+
+ dss_apply_mgr_lcd_config(mgr, config);
+
+ dss_write_regs();
+ dss_set_go_bits();
+
+ spin_unlock_irqrestore(&data_lock, flags);
+
+ wait_pending_extra_info_updates();
+
+out:
+ mutex_unlock(&apply_lock);
+}
+
int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 397d4eee11bb..5b289c5f695b 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -119,6 +119,97 @@ enum omap_color_component {
DISPC_COLOR_COMPONENT_UV = 1 << 1,
};
+enum mgr_reg_fields {
+ DISPC_MGR_FLD_ENABLE,
+ DISPC_MGR_FLD_STNTFT,
+ DISPC_MGR_FLD_GO,
+ DISPC_MGR_FLD_TFTDATALINES,
+ DISPC_MGR_FLD_STALLMODE,
+ DISPC_MGR_FLD_TCKENABLE,
+ DISPC_MGR_FLD_TCKSELECTION,
+ DISPC_MGR_FLD_CPR,
+ DISPC_MGR_FLD_FIFOHANDCHECK,
+ /* used to maintain a count of the above fields */
+ DISPC_MGR_FLD_NUM,
+};
+
+static const struct {
+ const char *name;
+ u32 vsync_irq;
+ u32 framedone_irq;
+ u32 sync_lost_irq;
+ struct reg_field reg_desc[DISPC_MGR_FLD_NUM];
+} mgr_desc[] = {
+ [OMAP_DSS_CHANNEL_LCD] = {
+ .name = "LCD",
+ .vsync_irq = DISPC_IRQ_VSYNC,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_DIGIT] = {
+ .name = "DIGIT",
+ .vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
+ .framedone_irq = 0,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL, 1, 1 },
+ [DISPC_MGR_FLD_STNTFT] = { },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL, 6, 6 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { },
+ [DISPC_MGR_FLD_STALLMODE] = { },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG, 12, 12 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG, 13, 13 },
+ [DISPC_MGR_FLD_CPR] = { },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_LCD2] = {
+ .name = "LCD2",
+ .vsync_irq = DISPC_IRQ_VSYNC2,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE2,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST2,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL2, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL2, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL2, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL2, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL2, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG2, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG2, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG2, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2, 16, 16 },
+ },
+ },
+ [OMAP_DSS_CHANNEL_LCD3] = {
+ .name = "LCD3",
+ .vsync_irq = DISPC_IRQ_VSYNC3,
+ .framedone_irq = DISPC_IRQ_FRAMEDONE3,
+ .sync_lost_irq = DISPC_IRQ_SYNC_LOST3,
+ .reg_desc = {
+ [DISPC_MGR_FLD_ENABLE] = { DISPC_CONTROL3, 0, 0 },
+ [DISPC_MGR_FLD_STNTFT] = { DISPC_CONTROL3, 3, 3 },
+ [DISPC_MGR_FLD_GO] = { DISPC_CONTROL3, 5, 5 },
+ [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3, 9, 8 },
+ [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 },
+ [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3, 10, 10 },
+ [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3, 11, 11 },
+ [DISPC_MGR_FLD_CPR] = { DISPC_CONFIG3, 15, 15 },
+ [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3, 16, 16 },
+ },
+ },
+};
+
static void _omap_dispc_set_irqs(void);
static inline void dispc_write_reg(const u16 idx, u32 val)
@@ -131,6 +222,18 @@ static inline u32 dispc_read_reg(const u16 idx)
return __raw_readl(dispc.base + idx);
}
+static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
+{
+ const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+ return REG_GET(rfld.reg, rfld.high, rfld.low);
+}
+
+static void mgr_fld_write(enum omap_channel channel,
+ enum mgr_reg_fields regfld, int val) {
+ const struct reg_field rfld = mgr_desc[channel].reg_desc[regfld];
+ REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);
+}
+
#define SR(reg) \
dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
#define RR(reg) \
@@ -153,6 +256,10 @@ static void dispc_save_context(void)
SR(CONTROL2);
SR(CONFIG2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ SR(CONTROL3);
+ SR(CONFIG3);
+ }
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
SR(DEFAULT_COLOR(i));
@@ -266,6 +373,8 @@ static void dispc_restore_context(void)
RR(GLOBAL_ALPHA);
if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONFIG2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ RR(CONFIG3);
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
RR(DEFAULT_COLOR(i));
@@ -351,6 +460,8 @@ static void dispc_restore_context(void)
RR(CONTROL);
if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONTROL2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ RR(CONTROL3);
/* clear spurious SYNC_LOST_DIGIT interrupts */
dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
@@ -387,101 +498,41 @@ void dispc_runtime_put(void)
WARN_ON(r < 0 && r != -ENOSYS);
}
-static inline bool dispc_mgr_is_lcd(enum omap_channel channel)
-{
- if (channel == OMAP_DSS_CHANNEL_LCD ||
- channel == OMAP_DSS_CHANNEL_LCD2)
- return true;
- else
- return false;
-}
-
u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
{
- switch (channel) {
- case OMAP_DSS_CHANNEL_LCD:
- return DISPC_IRQ_VSYNC;
- case OMAP_DSS_CHANNEL_LCD2:
- return DISPC_IRQ_VSYNC2;
- case OMAP_DSS_CHANNEL_DIGIT:
- return DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN;
- default:
- BUG();
- return 0;
- }
+ return mgr_desc[channel].vsync_irq;
}
u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
{
- switch (channel) {
- case OMAP_DSS_CHANNEL_LCD:
- return DISPC_IRQ_FRAMEDONE;
- case OMAP_DSS_CHANNEL_LCD2:
- return DISPC_IRQ_FRAMEDONE2;
- case OMAP_DSS_CHANNEL_DIGIT:
- return 0;
- default:
- BUG();
- return 0;
- }
+ return mgr_desc[channel].framedone_irq;
}
bool dispc_mgr_go_busy(enum omap_channel channel)
{
- int bit;
-
- if (dispc_mgr_is_lcd(channel))
- bit = 5; /* GOLCD */
- else
- bit = 6; /* GODIGIT */
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- return REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- return REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
}
void dispc_mgr_go(enum omap_channel channel)
{
- int bit;
bool enable_bit, go_bit;
- if (dispc_mgr_is_lcd(channel))
- bit = 0; /* LCDENABLE */
- else
- bit = 1; /* DIGITALENABLE */
-
/* if the channel is not enabled, we don't need GO */
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- enable_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- enable_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ enable_bit = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE) == 1;
if (!enable_bit)
return;
- if (dispc_mgr_is_lcd(channel))
- bit = 5; /* GOLCD */
- else
- bit = 6; /* GODIGIT */
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- go_bit = REG_GET(DISPC_CONTROL2, bit, bit) == 1;
- else
- go_bit = REG_GET(DISPC_CONTROL, bit, bit) == 1;
+ go_bit = mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
if (go_bit) {
DSSERR("GO bit not down for channel %d\n", channel);
return;
}
- DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" :
- (channel == OMAP_DSS_CHANNEL_LCD2 ? "LCD2" : "DIGIT"));
+ DSSDBG("GO %s\n", mgr_desc[channel].name);
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, 1, bit, bit);
- else
- REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit);
+ mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
}
static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
@@ -832,6 +883,15 @@ void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
chan = 0;
chan2 = 1;
break;
+ case OMAP_DSS_CHANNEL_LCD3:
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ chan = 0;
+ chan2 = 2;
+ } else {
+ BUG();
+ return;
+ }
+ break;
default:
BUG();
return;
@@ -867,7 +927,14 @@ static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
- if (dss_has_feature(FEAT_MGR_LCD2)) {
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ if (FLD_GET(val, 31, 30) == 0)
+ channel = FLD_GET(val, shift, shift);
+ else if (FLD_GET(val, 31, 30) == 1)
+ channel = OMAP_DSS_CHANNEL_LCD2;
+ else
+ channel = OMAP_DSS_CHANNEL_LCD3;
+ } else if (dss_has_feature(FEAT_MGR_LCD2)) {
if (FLD_GET(val, 31, 30) == 0)
channel = FLD_GET(val, shift, shift);
else
@@ -922,16 +989,10 @@ void dispc_enable_gamma_table(bool enable)
static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
{
- u16 reg;
-
- if (channel == OMAP_DSS_CHANNEL_LCD)
- reg = DISPC_CONFIG;
- else if (channel == OMAP_DSS_CHANNEL_LCD2)
- reg = DISPC_CONFIG2;
- else
+ if (channel == OMAP_DSS_CHANNEL_DIGIT)
return;
- REG_FLD_MOD(reg, enable, 15, 15);
+ mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
}
static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
@@ -939,7 +1000,7 @@ static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
{
u32 coef_r, coef_g, coef_b;
- if (!dispc_mgr_is_lcd(channel))
+ if (!dss_mgr_is_lcd(channel))
return;
coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
@@ -1798,7 +1859,7 @@ static int check_horiz_timing_omap3(enum omap_channel channel,
nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
pclk = dispc_mgr_pclk_rate(channel);
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
lclk = dispc_mgr_lclk_rate(channel);
else
lclk = dispc_fclk_rate();
@@ -2086,8 +2147,7 @@ static int dispc_ovl_calc_scaling(enum omap_plane plane,
}
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
- bool ilace, bool replication,
- const struct omap_video_timings *mgr_timings)
+ bool replication, const struct omap_video_timings *mgr_timings)
{
struct omap_overlay *ovl = omap_dss_get_overlay(plane);
bool five_taps = true;
@@ -2103,6 +2163,7 @@ int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
u16 out_width, out_height;
enum omap_channel channel;
int x_predecim = 1, y_predecim = 1;
+ bool ilace = mgr_timings->interlace;
channel = dispc_ovl_get_channel_out(plane);
@@ -2254,14 +2315,9 @@ static void dispc_disable_isr(void *data, u32 mask)
static void _enable_lcd_out(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2) {
- REG_FLD_MOD(DISPC_CONTROL2, enable ? 1 : 0, 0, 0);
- /* flush posted write */
- dispc_read_reg(DISPC_CONTROL2);
- } else {
- REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0);
- dispc_read_reg(DISPC_CONTROL);
- }
+ mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
+ /* flush posted write */
+ mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
@@ -2274,12 +2330,9 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
/* When we disable LCD output, we need to wait until frame is done.
* Otherwise the DSS is still working, and turning off the clocks
* prevents DSS from going to OFF mode */
- is_on = channel == OMAP_DSS_CHANNEL_LCD2 ?
- REG_GET(DISPC_CONTROL2, 0, 0) :
- REG_GET(DISPC_CONTROL, 0, 0);
+ is_on = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
- irq = channel == OMAP_DSS_CHANNEL_LCD2 ? DISPC_IRQ_FRAMEDONE2 :
- DISPC_IRQ_FRAMEDONE;
+ irq = mgr_desc[channel].framedone_irq;
if (!enable && is_on) {
init_completion(&frame_done_completion);
@@ -2384,21 +2437,12 @@ static void dispc_mgr_enable_digit_out(bool enable)
bool dispc_mgr_is_enabled(enum omap_channel channel)
{
- if (channel == OMAP_DSS_CHANNEL_LCD)
- return !!REG_GET(DISPC_CONTROL, 0, 0);
- else if (channel == OMAP_DSS_CHANNEL_DIGIT)
- return !!REG_GET(DISPC_CONTROL, 1, 1);
- else if (channel == OMAP_DSS_CHANNEL_LCD2)
- return !!REG_GET(DISPC_CONTROL2, 0, 0);
- else {
- BUG();
- return false;
- }
+ return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
}
void dispc_mgr_enable(enum omap_channel channel, bool enable)
{
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
dispc_mgr_enable_lcd_out(channel, enable);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_enable_digit_out(enable);
@@ -2432,36 +2476,13 @@ void dispc_pck_free_enable(bool enable)
void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONFIG2, enable ? 1 : 0, 16, 16);
- else
- REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16);
+ mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
}
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
- enum omap_lcd_display_type type)
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
{
- int mode;
-
- switch (type) {
- case OMAP_DSS_LCD_DISPLAY_STN:
- mode = 0;
- break;
-
- case OMAP_DSS_LCD_DISPLAY_TFT:
- mode = 1;
- break;
-
- default:
- BUG();
- return;
- }
-
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, mode, 3, 3);
- else
- REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3);
+ mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
}
void dispc_set_loadmode(enum omap_dss_load_mode mode)
@@ -2479,24 +2500,14 @@ static void dispc_mgr_set_trans_key(enum omap_channel ch,
enum omap_dss_trans_key_type type,
u32 trans_key)
{
- if (ch == OMAP_DSS_CHANNEL_LCD)
- REG_FLD_MOD(DISPC_CONFIG, type, 11, 11);
- else if (ch == OMAP_DSS_CHANNEL_DIGIT)
- REG_FLD_MOD(DISPC_CONFIG, type, 13, 13);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, type, 11, 11);
+ mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type);
dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
}
static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
{
- if (ch == OMAP_DSS_CHANNEL_LCD)
- REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10);
- else if (ch == OMAP_DSS_CHANNEL_DIGIT)
- REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12);
- else /* OMAP_DSS_CHANNEL_LCD2 */
- REG_FLD_MOD(DISPC_CONFIG2, enable, 10, 10);
+ mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable);
}
static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
@@ -2547,10 +2558,7 @@ void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
return;
}
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, code, 9, 8);
- else
- REG_FLD_MOD(DISPC_CONTROL, code, 9, 8);
+ mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
}
void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
@@ -2584,10 +2592,7 @@ void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
{
- if (channel == OMAP_DSS_CHANNEL_LCD2)
- REG_FLD_MOD(DISPC_CONTROL2, enable, 11, 11);
- else
- REG_FLD_MOD(DISPC_CONTROL, enable, 11, 11);
+ mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
}
static bool _dispc_mgr_size_ok(u16 width, u16 height)
@@ -2627,7 +2632,7 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
timings_ok = _dispc_mgr_size_ok(timings->x_res, timings->y_res);
- if (dispc_mgr_is_lcd(channel))
+ if (dss_mgr_is_lcd(channel))
timings_ok = timings_ok && _dispc_lcd_timings_ok(timings->hsw,
timings->hfp, timings->hbp,
timings->vsw, timings->vfp,
@@ -2637,9 +2642,16 @@ bool dispc_mgr_timings_ok(enum omap_channel channel,
}
static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
- int hfp, int hbp, int vsw, int vfp, int vbp)
+ int hfp, int hbp, int vsw, int vfp, int vbp,
+ enum omap_dss_signal_level vsync_level,
+ enum omap_dss_signal_level hsync_level,
+ enum omap_dss_signal_edge data_pclk_edge,
+ enum omap_dss_signal_level de_level,
+ enum omap_dss_signal_edge sync_pclk_edge)
+
{
- u32 timing_h, timing_v;
+ u32 timing_h, timing_v, l;
+ bool onoff, rf, ipc;
if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) {
timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) |
@@ -2657,6 +2669,44 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
+
+ switch (data_pclk_edge) {
+ case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+ ipc = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+ ipc = true;
+ break;
+ case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+ default:
+ BUG();
+ }
+
+ switch (sync_pclk_edge) {
+ case OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES:
+ onoff = false;
+ rf = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
+ onoff = true;
+ rf = false;
+ break;
+ case OMAPDSS_DRIVE_SIG_RISING_EDGE:
+ onoff = true;
+ rf = true;
+ break;
+ default:
+ BUG();
+ };
+
+ l = dispc_read_reg(DISPC_POL_FREQ(channel));
+ l |= FLD_VAL(onoff, 17, 17);
+ l |= FLD_VAL(rf, 16, 16);
+ l |= FLD_VAL(de_level, 15, 15);
+ l |= FLD_VAL(ipc, 14, 14);
+ l |= FLD_VAL(hsync_level, 13, 13);
+ l |= FLD_VAL(vsync_level, 12, 12);
+ dispc_write_reg(DISPC_POL_FREQ(channel), l);
}
/* change name to mode? */
@@ -2674,9 +2724,10 @@ void dispc_mgr_set_timings(enum omap_channel channel,
return;
}
- if (dispc_mgr_is_lcd(channel)) {
+ if (dss_mgr_is_lcd(channel)) {
_dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw,
- t.vfp, t.vbp);
+ t.vfp, t.vbp, t.vsync_level, t.hsync_level,
+ t.data_pclk_edge, t.de_level, t.sync_pclk_edge);
xtot = t.x_res + t.hfp + t.hsw + t.hbp;
ytot = t.y_res + t.vfp + t.vsw + t.vbp;
@@ -2687,14 +2738,13 @@ void dispc_mgr_set_timings(enum omap_channel channel,
DSSDBG("pck %u\n", timings->pixel_clock);
DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp);
+ DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
+ t.vsync_level, t.hsync_level, t.data_pclk_edge,
+ t.de_level, t.sync_pclk_edge);
DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
} else {
- enum dss_hdmi_venc_clk_source_select source;
-
- source = dss_get_hdmi_venc_clk_source();
-
- if (source == DSS_VENC_TV_CLK)
+ if (t.interlace == true)
t.y_res /= 2;
}
@@ -2780,7 +2830,7 @@ unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
{
unsigned long r;
- if (dispc_mgr_is_lcd(channel)) {
+ if (dss_mgr_is_lcd(channel)) {
int pcd;
u32 l;
@@ -2821,12 +2871,32 @@ unsigned long dispc_core_clk_rate(void)
return fclk / lcd;
}
-void dispc_dump_clocks(struct seq_file *s)
+static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
{
int lcd, pcd;
+ enum omap_dss_clk_source lcd_clk_src;
+
+ seq_printf(s, "- %s -\n", mgr_desc[channel].name);
+
+ lcd_clk_src = dss_get_lcd_clk_source(channel);
+
+ seq_printf(s, "%s clk source = %s (%s)\n", mgr_desc[channel].name,
+ dss_get_generic_clk_source_name(lcd_clk_src),
+ dss_feat_get_clk_source_name(lcd_clk_src));
+
+ dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd);
+
+ seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
+ dispc_mgr_lclk_rate(channel), lcd);
+ seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
+ dispc_mgr_pclk_rate(channel), pcd);
+}
+
+void dispc_dump_clocks(struct seq_file *s)
+{
+ int lcd;
u32 l;
enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
- enum omap_dss_clk_source lcd_clk_src;
if (dispc_runtime_get())
return;
@@ -2847,36 +2917,13 @@ void dispc_dump_clocks(struct seq_file *s)
seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
(dispc_fclk_rate()/lcd), lcd);
}
- seq_printf(s, "- LCD1 -\n");
-
- lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD);
-
- seq_printf(s, "lcd1_clk source = %s (%s)\n",
- dss_get_generic_clk_source_name(lcd_clk_src),
- dss_feat_get_clk_source_name(lcd_clk_src));
-
- dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD, &lcd, &pcd);
-
- seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD), lcd);
- seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD), pcd);
- if (dss_has_feature(FEAT_MGR_LCD2)) {
- seq_printf(s, "- LCD2 -\n");
-
- lcd_clk_src = dss_get_lcd_clk_source(OMAP_DSS_CHANNEL_LCD2);
- seq_printf(s, "lcd2_clk source = %s (%s)\n",
- dss_get_generic_clk_source_name(lcd_clk_src),
- dss_feat_get_clk_source_name(lcd_clk_src));
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD);
- dispc_mgr_get_lcd_divisor(OMAP_DSS_CHANNEL_LCD2, &lcd, &pcd);
-
- seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
- dispc_mgr_lclk_rate(OMAP_DSS_CHANNEL_LCD2), lcd);
- seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
- dispc_mgr_pclk_rate(OMAP_DSS_CHANNEL_LCD2), pcd);
- }
+ if (dss_has_feature(FEAT_MGR_LCD2))
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3);
dispc_runtime_put();
}
@@ -2929,6 +2976,12 @@ void dispc_dump_irqs(struct seq_file *s)
PIS(ACBIAS_COUNT_STAT2);
PIS(SYNC_LOST2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ PIS(FRAMEDONE3);
+ PIS(VSYNC3);
+ PIS(ACBIAS_COUNT_STAT3);
+ PIS(SYNC_LOST3);
+ }
#undef PIS
}
#endif
@@ -2940,6 +2993,7 @@ static void dispc_dump_regs(struct seq_file *s)
[OMAP_DSS_CHANNEL_LCD] = "LCD",
[OMAP_DSS_CHANNEL_DIGIT] = "TV",
[OMAP_DSS_CHANNEL_LCD2] = "LCD2",
+ [OMAP_DSS_CHANNEL_LCD3] = "LCD3",
};
const char *ovl_names[] = {
[OMAP_DSS_GFX] = "GFX",
@@ -2972,6 +3026,10 @@ static void dispc_dump_regs(struct seq_file *s)
DUMPREG(DISPC_CONTROL2);
DUMPREG(DISPC_CONFIG2);
}
+ if (dss_has_feature(FEAT_MGR_LCD3)) {
+ DUMPREG(DISPC_CONTROL3);
+ DUMPREG(DISPC_CONFIG3);
+ }
#undef DUMPREG
@@ -3093,41 +3151,8 @@ static void dispc_dump_regs(struct seq_file *s)
#undef DUMPREG
}
-static void _dispc_mgr_set_pol_freq(enum omap_channel channel, bool onoff,
- bool rf, bool ieo, bool ipc, bool ihs, bool ivs, u8 acbi,
- u8 acb)
-{
- u32 l = 0;
-
- DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n",
- onoff, rf, ieo, ipc, ihs, ivs, acbi, acb);
-
- l |= FLD_VAL(onoff, 17, 17);
- l |= FLD_VAL(rf, 16, 16);
- l |= FLD_VAL(ieo, 15, 15);
- l |= FLD_VAL(ipc, 14, 14);
- l |= FLD_VAL(ihs, 13, 13);
- l |= FLD_VAL(ivs, 12, 12);
- l |= FLD_VAL(acbi, 11, 8);
- l |= FLD_VAL(acb, 7, 0);
-
- dispc_write_reg(DISPC_POL_FREQ(channel), l);
-}
-
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
- enum omap_panel_config config, u8 acbi, u8 acb)
-{
- _dispc_mgr_set_pol_freq(channel, (config & OMAP_DSS_LCD_ONOFF) != 0,
- (config & OMAP_DSS_LCD_RF) != 0,
- (config & OMAP_DSS_LCD_IEO) != 0,
- (config & OMAP_DSS_LCD_IPC) != 0,
- (config & OMAP_DSS_LCD_IHS) != 0,
- (config & OMAP_DSS_LCD_IVS) != 0,
- acbi, acb);
-}
-
/* with fck as input clock rate, find dispc dividers that produce req_pck */
-void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+void dispc_find_clk_divs(unsigned long req_pck, unsigned long fck,
struct dispc_clock_info *cinfo)
{
u16 pcd_min, pcd_max;
@@ -3138,9 +3163,6 @@ void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
pcd_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD);
pcd_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD);
- if (!is_tft)
- pcd_min = 3;
-
best_pck = 0;
best_ld = 0;
best_pd = 0;
@@ -3192,15 +3214,13 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
return 0;
}
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo)
{
DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
-
- return 0;
}
int dispc_mgr_get_clock_div(enum omap_channel channel,
@@ -3354,6 +3374,8 @@ static void print_irq_status(u32 status)
PIS(SYNC_LOST_DIGIT);
if (dss_has_feature(FEAT_MGR_LCD2))
PIS(SYNC_LOST2);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ PIS(SYNC_LOST3);
#undef PIS
printk("\n");
@@ -3450,12 +3472,6 @@ static void dispc_error_worker(struct work_struct *work)
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
- static const unsigned sync_lost_bits[] = {
- DISPC_IRQ_SYNC_LOST,
- DISPC_IRQ_SYNC_LOST_DIGIT,
- DISPC_IRQ_SYNC_LOST2,
- };
-
spin_lock_irqsave(&dispc.irq_lock, flags);
errors = dispc.error_irqs;
dispc.error_irqs = 0;
@@ -3484,7 +3500,7 @@ static void dispc_error_worker(struct work_struct *work)
unsigned bit;
mgr = omap_dss_get_overlay_manager(i);
- bit = sync_lost_bits[i];
+ bit = mgr_desc[i].sync_lost_irq;
if (bit & errors) {
struct omap_dss_device *dssdev = mgr->device;
@@ -3603,6 +3619,8 @@ static void _omap_dispc_initialize_irq(void)
dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
if (dss_feat_get_num_ovls() > 3)
dispc.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index f278080e1063..92d8a9be86fc 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -36,6 +36,8 @@
#define DISPC_CONTROL2 0x0238
#define DISPC_CONFIG2 0x0620
#define DISPC_DIVISOR 0x0804
+#define DISPC_CONTROL3 0x0848
+#define DISPC_CONFIG3 0x084C
/* DISPC overlay registers */
#define DISPC_OVL_BA0(n) (DISPC_OVL_BASE(n) + \
@@ -118,6 +120,8 @@ static inline u16 DISPC_DEFAULT_COLOR(enum omap_channel channel)
return 0x0050;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03AC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0814;
default:
BUG();
return 0;
@@ -133,6 +137,8 @@ static inline u16 DISPC_TRANS_COLOR(enum omap_channel channel)
return 0x0058;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B0;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0818;
default:
BUG();
return 0;
@@ -149,6 +155,8 @@ static inline u16 DISPC_TIMING_H(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0400;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0840;
default:
BUG();
return 0;
@@ -165,6 +173,8 @@ static inline u16 DISPC_TIMING_V(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0404;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0844;
default:
BUG();
return 0;
@@ -181,6 +191,8 @@ static inline u16 DISPC_POL_FREQ(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x0408;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x083C;
default:
BUG();
return 0;
@@ -197,6 +209,8 @@ static inline u16 DISPC_DIVISORo(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x040C;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0838;
default:
BUG();
return 0;
@@ -213,6 +227,8 @@ static inline u16 DISPC_SIZE_MGR(enum omap_channel channel)
return 0x0078;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03CC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0834;
default:
BUG();
return 0;
@@ -229,6 +245,8 @@ static inline u16 DISPC_DATA_CYCLE1(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C0;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0828;
default:
BUG();
return 0;
@@ -245,6 +263,8 @@ static inline u16 DISPC_DATA_CYCLE2(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C4;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x082C;
default:
BUG();
return 0;
@@ -261,6 +281,8 @@ static inline u16 DISPC_DATA_CYCLE3(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03C8;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0830;
default:
BUG();
return 0;
@@ -277,6 +299,8 @@ static inline u16 DISPC_CPR_COEF_R(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03BC;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0824;
default:
BUG();
return 0;
@@ -293,6 +317,8 @@ static inline u16 DISPC_CPR_COEF_G(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B8;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x0820;
default:
BUG();
return 0;
@@ -309,6 +335,8 @@ static inline u16 DISPC_CPR_COEF_B(enum omap_channel channel)
return 0;
case OMAP_DSS_CHANNEL_LCD2:
return 0x03B4;
+ case OMAP_DSS_CHANNEL_LCD3:
+ return 0x081C;
default:
BUG();
return 0;
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 249010630370..5bd957e85505 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -116,7 +116,7 @@ static ssize_t display_timings_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct omap_dss_device *dssdev = to_dss_device(dev);
- struct omap_video_timings t;
+ struct omap_video_timings t = dssdev->panel.timings;
int r, found;
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
@@ -316,44 +316,6 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_default_get_timings);
-/* Checks if replication logic should be used. Only use for active matrix,
- * when overlay is in RGB12U or RGB16 mode, and LCD interface is
- * 18bpp or 24bpp */
-bool dss_use_replication(struct omap_dss_device *dssdev,
- enum omap_color_mode mode)
-{
- int bpp;
-
- if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
- return false;
-
- if (dssdev->type == OMAP_DISPLAY_TYPE_DPI &&
- (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0)
- return false;
-
- switch (dssdev->type) {
- case OMAP_DISPLAY_TYPE_DPI:
- bpp = dssdev->phy.dpi.data_lines;
- break;
- case OMAP_DISPLAY_TYPE_HDMI:
- case OMAP_DISPLAY_TYPE_VENC:
- case OMAP_DISPLAY_TYPE_SDI:
- bpp = 24;
- break;
- case OMAP_DISPLAY_TYPE_DBI:
- bpp = dssdev->ctrl.pixel_size;
- break;
- case OMAP_DISPLAY_TYPE_DSI:
- bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
- break;
- default:
- BUG();
- return false;
- }
-
- return bpp > 16;
-}
-
void dss_init_device(struct platform_device *pdev,
struct omap_dss_device *dssdev)
{
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
index 8c2056c9537b..3266be23fc0d 100644
--- a/drivers/video/omap2/dss/dpi.c
+++ b/drivers/video/omap2/dss/dpi.c
@@ -38,6 +38,8 @@
static struct {
struct regulator *vdds_dsi_reg;
struct platform_device *dsidev;
+
+ struct dss_lcd_mgr_config mgr_config;
} dpi;
static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk)
@@ -64,7 +66,7 @@ static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
return false;
}
-static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
@@ -72,8 +74,8 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
struct dispc_clock_info dispc_cinfo;
int r;
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft, pck_req,
- &dsi_cinfo, &dispc_cinfo);
+ r = dsi_pll_calc_clock_div_pck(dpi.dsidev, pck_req, &dsi_cinfo,
+ &dispc_cinfo);
if (r)
return r;
@@ -83,11 +85,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
dss_select_dispc_clk_source(dssdev->clocks.dispc.dispc_fclk_src);
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r) {
- dss_select_dispc_clk_source(OMAP_DSS_CLK_SRC_FCK);
- return r;
- }
+ dpi.mgr_config.clock_info = dispc_cinfo;
*fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
*lck_div = dispc_cinfo.lck_div;
@@ -96,7 +94,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev, bool is_tft,
return 0;
}
-static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
+static int dpi_set_dispc_clk(struct omap_dss_device *dssdev,
unsigned long pck_req, unsigned long *fck, int *lck_div,
int *pck_div)
{
@@ -104,7 +102,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
struct dispc_clock_info dispc_cinfo;
int r;
- r = dss_calc_clock_div(is_tft, pck_req, &dss_cinfo, &dispc_cinfo);
+ r = dss_calc_clock_div(pck_req, &dss_cinfo, &dispc_cinfo);
if (r)
return r;
@@ -112,9 +110,7 @@ static int dpi_set_dispc_clk(struct omap_dss_device *dssdev, bool is_tft,
if (r)
return r;
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r)
- return r;
+ dpi.mgr_config.clock_info = dispc_cinfo;
*fck = dss_cinfo.fck;
*lck_div = dispc_cinfo.lck_div;
@@ -129,20 +125,14 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
int lck_div = 0, pck_div = 0;
unsigned long fck = 0;
unsigned long pck;
- bool is_tft;
int r = 0;
- dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
- dssdev->panel.acbi, dssdev->panel.acb);
-
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
if (dpi_use_dsi_pll(dssdev))
- r = dpi_set_dsi_clk(dssdev, is_tft, t->pixel_clock * 1000,
- &fck, &lck_div, &pck_div);
+ r = dpi_set_dsi_clk(dssdev, t->pixel_clock * 1000, &fck,
+ &lck_div, &pck_div);
else
- r = dpi_set_dispc_clk(dssdev, is_tft, t->pixel_clock * 1000,
- &fck, &lck_div, &pck_div);
+ r = dpi_set_dispc_clk(dssdev, t->pixel_clock * 1000, &fck,
+ &lck_div, &pck_div);
if (r)
return r;
@@ -161,19 +151,18 @@ static int dpi_set_mode(struct omap_dss_device *dssdev)
return 0;
}
-static void dpi_basic_init(struct omap_dss_device *dssdev)
+static void dpi_config_lcd_manager(struct omap_dss_device *dssdev)
{
- bool is_tft;
+ dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+ dpi.mgr_config.stallmode = false;
+ dpi.mgr_config.fifohandcheck = false;
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
+ dpi.mgr_config.video_port_width = dssdev->phy.dpi.data_lines;
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+ dpi.mgr_config.lcden_sig_polarity = 0;
- dispc_mgr_set_lcd_display_type(dssdev->manager->id, is_tft ?
- OMAP_DSS_LCD_DISPLAY_TFT : OMAP_DSS_LCD_DISPLAY_STN);
- dispc_mgr_set_tft_data_lines(dssdev->manager->id,
- dssdev->phy.dpi.data_lines);
+ dss_mgr_set_lcd_config(dssdev->manager, &dpi.mgr_config);
}
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
@@ -206,8 +195,6 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_get_dispc;
- dpi_basic_init(dssdev);
-
if (dpi_use_dsi_pll(dssdev)) {
r = dsi_runtime_get(dpi.dsidev);
if (r)
@@ -222,6 +209,8 @@ int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_set_mode;
+ dpi_config_lcd_manager(dssdev);
+
mdelay(2);
r = dss_mgr_enable(dssdev->manager);
@@ -292,7 +281,6 @@ EXPORT_SYMBOL(dpi_set_timings);
int dpi_check_timings(struct omap_dss_device *dssdev,
struct omap_video_timings *timings)
{
- bool is_tft;
int r;
int lck_div, pck_div;
unsigned long fck;
@@ -305,11 +293,9 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
if (timings->pixel_clock == 0)
return -EINVAL;
- is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0;
-
if (dpi_use_dsi_pll(dssdev)) {
struct dsi_clock_info dsi_cinfo;
- r = dsi_pll_calc_clock_div_pck(dpi.dsidev, is_tft,
+ r = dsi_pll_calc_clock_div_pck(dpi.dsidev,
timings->pixel_clock * 1000,
&dsi_cinfo, &dispc_cinfo);
@@ -319,7 +305,7 @@ int dpi_check_timings(struct omap_dss_device *dssdev,
fck = dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
} else {
struct dss_clock_info dss_cinfo;
- r = dss_calc_clock_div(is_tft, timings->pixel_clock * 1000,
+ r = dss_calc_clock_div(timings->pixel_clock * 1000,
&dss_cinfo, &dispc_cinfo);
if (r)
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 14ce8cc079e3..b07e8864f82f 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -331,6 +331,8 @@ struct dsi_data {
unsigned num_lanes_used;
unsigned scp_clk_refcount;
+
+ struct dss_lcd_mgr_config mgr_config;
};
struct dsi_packet_sent_handler_data {
@@ -1085,9 +1087,9 @@ static inline void dsi_enable_pll_clock(struct platform_device *dsidev,
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
if (enable)
- clk_enable(dsi->sys_clk);
+ clk_prepare_enable(dsi->sys_clk);
else
- clk_disable(dsi->sys_clk);
+ clk_disable_unprepare(dsi->sys_clk);
if (enable && dsi->pll_locked) {
if (wait_for_bit_change(dsidev, DSI_PLL_STATUS, 1, 1) != 1)
@@ -1316,7 +1318,7 @@ static int dsi_calc_clock_rates(struct platform_device *dsidev,
return 0;
}
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
unsigned long req_pck, struct dsi_clock_info *dsi_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
@@ -1335,8 +1337,8 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
dsi->cache_cinfo.clkin == dss_sys_clk) {
DSSDBG("DSI clock info found from cache\n");
*dsi_cinfo = dsi->cache_cinfo;
- dispc_find_clk_divs(is_tft, req_pck,
- dsi_cinfo->dsi_pll_hsdiv_dispc_clk, dispc_cinfo);
+ dispc_find_clk_divs(req_pck, dsi_cinfo->dsi_pll_hsdiv_dispc_clk,
+ dispc_cinfo);
return 0;
}
@@ -1402,7 +1404,7 @@ retry:
match = 1;
- dispc_find_clk_divs(is_tft, req_pck,
+ dispc_find_clk_divs(req_pck,
cur.dsi_pll_hsdiv_dispc_clk,
&cur_dispc);
@@ -3631,17 +3633,14 @@ static void dsi_config_vp_num_line_buffers(struct omap_dss_device *dssdev)
static void dsi_config_vp_sync_events(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- int de_pol = dssdev->panel.dsi_vm_data.vp_de_pol;
- int hsync_pol = dssdev->panel.dsi_vm_data.vp_hsync_pol;
- int vsync_pol = dssdev->panel.dsi_vm_data.vp_vsync_pol;
bool vsync_end = dssdev->panel.dsi_vm_data.vp_vsync_end;
bool hsync_end = dssdev->panel.dsi_vm_data.vp_hsync_end;
u32 r;
r = dsi_read_reg(dsidev, DSI_CTRL);
- r = FLD_MOD(r, de_pol, 9, 9); /* VP_DE_POL */
- r = FLD_MOD(r, hsync_pol, 10, 10); /* VP_HSYNC_POL */
- r = FLD_MOD(r, vsync_pol, 11, 11); /* VP_VSYNC_POL */
+ r = FLD_MOD(r, 1, 9, 9); /* VP_DE_POL */
+ r = FLD_MOD(r, 1, 10, 10); /* VP_HSYNC_POL */
+ r = FLD_MOD(r, 1, 11, 11); /* VP_VSYNC_POL */
r = FLD_MOD(r, 1, 15, 15); /* VP_VSYNC_START */
r = FLD_MOD(r, vsync_end, 16, 16); /* VP_VSYNC_END */
r = FLD_MOD(r, 1, 17, 17); /* VP_HSYNC_START */
@@ -4340,52 +4339,101 @@ EXPORT_SYMBOL(omap_dsi_update);
/* Display funcs */
+static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
+{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct dispc_clock_info dispc_cinfo;
+ int r;
+ unsigned long long fck;
+
+ fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
+
+ dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
+ dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
+
+ r = dispc_calc_clock_rates(fck, &dispc_cinfo);
+ if (r) {
+ DSSERR("Failed to calc dispc clocks\n");
+ return r;
+ }
+
+ dsi->mgr_config.clock_info = dispc_cinfo;
+
+ return 0;
+}
+
static int dsi_display_init_dispc(struct omap_dss_device *dssdev)
{
+ struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
+ struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
+ struct omap_video_timings timings;
int r;
+ u32 irq = 0;
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
u16 dw, dh;
- u32 irq;
- struct omap_video_timings timings = {
- .hsw = 1,
- .hfp = 1,
- .hbp = 1,
- .vsw = 1,
- .vfp = 0,
- .vbp = 0,
- };
dssdev->driver->get_resolution(dssdev, &dw, &dh);
+
timings.x_res = dw;
timings.y_res = dh;
+ timings.hsw = 1;
+ timings.hfp = 1;
+ timings.hbp = 1;
+ timings.vsw = 1;
+ timings.vfp = 0;
+ timings.vbp = 0;
- irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
- DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+ irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
r = omap_dispc_register_isr(dsi_framedone_irq_callback,
(void *) dssdev, irq);
if (r) {
DSSERR("can't get FRAMEDONE irq\n");
- return r;
+ goto err;
}
- dispc_mgr_enable_stallmode(dssdev->manager->id, true);
- dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 1);
-
- dss_mgr_set_timings(dssdev->manager, &timings);
+ dsi->mgr_config.stallmode = true;
+ dsi->mgr_config.fifohandcheck = true;
} else {
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
- dispc_mgr_enable_fifohandcheck(dssdev->manager->id, 0);
+ timings = dssdev->panel.timings;
- dss_mgr_set_timings(dssdev->manager, &dssdev->panel.timings);
+ dsi->mgr_config.stallmode = false;
+ dsi->mgr_config.fifohandcheck = false;
}
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
- dispc_mgr_set_tft_data_lines(dssdev->manager->id,
- dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt));
+ /*
+ * override interlace, logic level and edge related parameters in
+ * omap_video_timings with default values
+ */
+ timings.interlace = false;
+ timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+
+ dss_mgr_set_timings(dssdev->manager, &timings);
+
+ r = dsi_configure_dispc_clocks(dssdev);
+ if (r)
+ goto err1;
+
+ dsi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+ dsi->mgr_config.video_port_width =
+ dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
+ dsi->mgr_config.lcden_sig_polarity = 0;
+
+ dss_mgr_set_lcd_config(dssdev->manager, &dsi->mgr_config);
+
return 0;
+err1:
+ if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE)
+ omap_dispc_unregister_isr(dsi_framedone_irq_callback,
+ (void *) dssdev, irq);
+err:
+ return r;
}
static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
@@ -4393,8 +4441,7 @@ static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev)
if (dssdev->panel.dsi_mode == OMAP_DSS_DSI_CMD_MODE) {
u32 irq;
- irq = dssdev->manager->id == OMAP_DSS_CHANNEL_LCD ?
- DISPC_IRQ_FRAMEDONE : DISPC_IRQ_FRAMEDONE2;
+ irq = dispc_mgr_get_framedone_irq(dssdev->manager->id);
omap_dispc_unregister_isr(dsi_framedone_irq_callback,
(void *) dssdev, irq);
@@ -4426,33 +4473,6 @@ static int dsi_configure_dsi_clocks(struct omap_dss_device *dssdev)
return 0;
}
-static int dsi_configure_dispc_clocks(struct omap_dss_device *dssdev)
-{
- struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
- struct dispc_clock_info dispc_cinfo;
- int r;
- unsigned long long fck;
-
- fck = dsi_get_pll_hsdiv_dispc_rate(dsidev);
-
- dispc_cinfo.lck_div = dssdev->clocks.dispc.channel.lck_div;
- dispc_cinfo.pck_div = dssdev->clocks.dispc.channel.pck_div;
-
- r = dispc_calc_clock_rates(fck, &dispc_cinfo);
- if (r) {
- DSSERR("Failed to calc dispc clocks\n");
- return r;
- }
-
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r) {
- DSSERR("Failed to set dispc clocks\n");
- return r;
- }
-
- return 0;
-}
-
static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4474,10 +4494,6 @@ static int dsi_display_init_dsi(struct omap_dss_device *dssdev)
DSSDBG("PLL OK\n");
- r = dsi_configure_dispc_clocks(dssdev);
- if (r)
- goto err2;
-
r = dsi_cio_init(dssdev);
if (r)
goto err2;
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index d2b57197b292..04b4586113e3 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -388,7 +388,8 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
dsi_wait_pll_hsdiv_dispc_active(dsidev);
break;
case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
- BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2);
+ BUG_ON(channel != OMAP_DSS_CHANNEL_LCD2 &&
+ channel != OMAP_DSS_CHANNEL_LCD3);
b = 1;
dsidev = dsi_get_dsidev_from_id(1);
dsi_wait_pll_hsdiv_dispc_active(dsidev);
@@ -398,10 +399,12 @@ void dss_select_lcd_clk_source(enum omap_channel channel,
return;
}
- pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 12;
+ pos = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 12 : 19);
REG_FLD_MOD(DSS_CONTROL, b, pos, pos); /* LCDx_CLK_SWITCH */
- ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+ ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
dss.lcd_clk_source[ix] = clk_src;
}
@@ -418,7 +421,8 @@ enum omap_dss_clk_source dss_get_dsi_clk_source(int dsi_module)
enum omap_dss_clk_source dss_get_lcd_clk_source(enum omap_channel channel)
{
if (dss_has_feature(FEAT_LCD_CLK_SRC)) {
- int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 : 1;
+ int ix = channel == OMAP_DSS_CHANNEL_LCD ? 0 :
+ (channel == OMAP_DSS_CHANNEL_LCD2 ? 1 : 2);
return dss.lcd_clk_source[ix];
} else {
/* LCD_CLK source is the same as DISPC_FCLK source for
@@ -502,8 +506,7 @@ unsigned long dss_get_dpll4_rate(void)
return 0;
}
-int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
- struct dss_clock_info *dss_cinfo,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
unsigned long prate;
@@ -551,7 +554,7 @@ retry:
fck = clk_get_rate(dss.dss_clk);
fck_div = 1;
- dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+ dispc_find_clk_divs(req_pck, fck, &cur_dispc);
match = 1;
best_dss.fck = fck;
@@ -581,7 +584,7 @@ retry:
match = 1;
- dispc_find_clk_divs(is_tft, req_pck, fck, &cur_dispc);
+ dispc_find_clk_divs(req_pck, fck, &cur_dispc);
if (abs(cur_dispc.pck - req_pck) <
abs(best_dispc.pck - req_pck)) {
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index dd1092ceaeef..f67afe76f217 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -152,6 +152,25 @@ struct dsi_clock_info {
u16 lp_clk_div;
};
+struct reg_field {
+ u16 reg;
+ u8 high;
+ u8 low;
+};
+
+struct dss_lcd_mgr_config {
+ enum dss_io_pad_mode io_pad_mode;
+
+ bool stallmode;
+ bool fifohandcheck;
+
+ struct dispc_clock_info clock_info;
+
+ int video_port_width;
+
+ int lcden_sig_polarity;
+};
+
struct seq_file;
struct platform_device;
@@ -188,6 +207,8 @@ int dss_mgr_set_device(struct omap_overlay_manager *mgr,
int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
struct omap_video_timings *timings);
+void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config);
const struct omap_video_timings *dss_mgr_get_timings(struct omap_overlay_manager *mgr);
bool dss_ovl_is_enabled(struct omap_overlay *ovl);
@@ -210,8 +231,6 @@ void dss_init_device(struct platform_device *pdev,
struct omap_dss_device *dssdev);
void dss_uninit_device(struct platform_device *pdev,
struct omap_dss_device *dssdev);
-bool dss_use_replication(struct omap_dss_device *dssdev,
- enum omap_color_mode mode);
/* manager */
int dss_init_overlay_managers(struct platform_device *pdev);
@@ -223,8 +242,18 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
+ const struct dss_lcd_mgr_config *config,
struct omap_overlay_info **overlay_infos);
+static inline bool dss_mgr_is_lcd(enum omap_channel id)
+{
+ if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
+ id == OMAP_DSS_CHANNEL_LCD3)
+ return true;
+ else
+ return false;
+}
+
/* overlay */
void dss_init_overlays(struct platform_device *pdev);
void dss_uninit_overlays(struct platform_device *pdev);
@@ -234,6 +263,8 @@ int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info);
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings);
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+ enum omap_color_mode mode);
/* DSS */
int dss_init_platform_driver(void) __init;
@@ -268,8 +299,7 @@ unsigned long dss_get_dpll4_rate(void);
int dss_calc_clock_rates(struct dss_clock_info *cinfo);
int dss_set_clock_div(struct dss_clock_info *cinfo);
int dss_get_clock_div(struct dss_clock_info *cinfo);
-int dss_calc_clock_div(bool is_tft, unsigned long req_pck,
- struct dss_clock_info *dss_cinfo,
+int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
struct dispc_clock_info *dispc_cinfo);
/* SDI */
@@ -296,7 +326,7 @@ u8 dsi_get_pixel_size(enum omap_dss_dsi_pixel_format fmt);
unsigned long dsi_get_pll_hsdiv_dispc_rate(struct platform_device *dsidev);
int dsi_pll_set_clock_div(struct platform_device *dsidev,
struct dsi_clock_info *cinfo);
-int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
+int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
unsigned long req_pck, struct dsi_clock_info *cinfo,
struct dispc_clock_info *dispc_cinfo);
int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
@@ -330,7 +360,7 @@ static inline int dsi_pll_set_clock_div(struct platform_device *dsidev,
return -ENODEV;
}
static inline int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev,
- bool is_tft, unsigned long req_pck,
+ unsigned long req_pck,
struct dsi_clock_info *dsi_cinfo,
struct dispc_clock_info *dispc_cinfo)
{
@@ -387,7 +417,7 @@ void dispc_set_loadmode(enum omap_dss_load_mode mode);
bool dispc_mgr_timings_ok(enum omap_channel channel,
const struct omap_video_timings *timings);
unsigned long dispc_fclk_rate(void);
-void dispc_find_clk_divs(bool is_tft, unsigned long req_pck, unsigned long fck,
+void dispc_find_clk_divs(unsigned long req_pck, unsigned long fck,
struct dispc_clock_info *cinfo);
int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
struct dispc_clock_info *cinfo);
@@ -398,8 +428,7 @@ void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
bool manual_update);
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
- bool ilace, bool replication,
- const struct omap_video_timings *mgr_timings);
+ bool replication, const struct omap_video_timings *mgr_timings);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
void dispc_ovl_set_channel_out(enum omap_plane plane,
enum omap_channel channel);
@@ -415,16 +444,13 @@ bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_mgr_set_lcd_display_type(enum omap_channel channel,
- enum omap_lcd_display_type type);
+void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
void dispc_mgr_set_timings(enum omap_channel channel,
struct omap_video_timings *timings);
-void dispc_mgr_set_pol_freq(enum omap_channel channel,
- enum omap_panel_config config, u8 acbi, u8 acb);
unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
unsigned long dispc_core_clk_rate(void);
-int dispc_mgr_set_clock_div(enum omap_channel channel,
+void dispc_mgr_set_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
int dispc_mgr_get_clock_div(enum omap_channel channel,
struct dispc_clock_info *cinfo);
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index bdf469f080e7..996ffcbfed58 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -24,9 +24,9 @@
#include "ti_hdmi.h"
#endif
-#define MAX_DSS_MANAGERS 3
+#define MAX_DSS_MANAGERS 4
#define MAX_DSS_OVERLAYS 4
-#define MAX_DSS_LCD_MANAGERS 2
+#define MAX_DSS_LCD_MANAGERS 3
#define MAX_NUM_DSI 2
/* DSS has feature id */
@@ -36,6 +36,7 @@ enum dss_feat_id {
FEAT_PCKFREEENABLE,
FEAT_FUNCGATED,
FEAT_MGR_LCD2,
+ FEAT_MGR_LCD3,
FEAT_LINEBUFFERSPLIT,
FEAT_ROWREPEATENABLE,
FEAT_RESIZECONF,
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 26a2430a7028..060216fdc578 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -78,43 +78,214 @@ static struct {
*/
static const struct hdmi_config cea_timings[] = {
-{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} },
-{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} },
-{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} },
-{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} },
-{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} },
-{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} },
-{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} },
-{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} },
-{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} },
-{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} },
-{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} },
-{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} },
+ {
+ { 640, 480, 25200, 96, 16, 48, 2, 10, 33,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 1, HDMI_HDMI },
+ },
+ {
+ { 720, 480, 27027, 62, 16, 60, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 2, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 4, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 88, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 5, HDMI_HDMI },
+ },
+ {
+ { 1440, 240, 27027, 124, 38, 114, 3, 4, 15,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 6, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 88, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 16, HDMI_HDMI },
+ },
+ {
+ { 720, 576, 27000, 64, 12, 68, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 17, HDMI_HDMI },
+ },
+ {
+ { 1280, 720, 74250, 40, 440, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 19, HDMI_HDMI },
+ },
+ {
+ { 1920, 540, 74250, 44, 528, 148, 5, 2, 15,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ true, },
+ { 20, HDMI_HDMI },
+ },
+ {
+ { 1440, 288, 27000, 126, 24, 138, 3, 2, 19,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ true, },
+ { 21, HDMI_HDMI },
+ },
+ {
+ { 1440, 576, 54000, 128, 24, 136, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 29, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 528, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 31, HDMI_HDMI },
+ },
+ {
+ { 1920, 1080, 74250, 44, 638, 148, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 32, HDMI_HDMI },
+ },
+ {
+ { 2880, 480, 108108, 248, 64, 240, 6, 9, 30,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 35, HDMI_HDMI },
+ },
+ {
+ { 2880, 576, 108000, 256, 48, 272, 5, 5, 39,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 37, HDMI_HDMI },
+ },
};
+
static const struct hdmi_config vesa_timings[] = {
/* VESA From Here */
-{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} },
-{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} },
-{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} },
-{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} },
-{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} },
-{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} },
-{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} },
-{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} },
-{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} },
-{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} },
-{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} },
-{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} },
-{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} },
-{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} },
-{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} },
-{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} },
-{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} },
-{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} },
-{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} }
+ {
+ { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 4, HDMI_DVI },
+ },
+ {
+ { 800, 600, 40000, 128, 40, 88, 4, 1, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 9, HDMI_DVI },
+ },
+ {
+ { 848, 480, 33750, 112, 16, 112, 8, 6, 23,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0xE, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 79500, 128, 64, 192, 7, 3, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x17, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 83500, 128, 72, 200, 6, 3, 22,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x1C, HDMI_DVI },
+ },
+ {
+ { 1360, 768, 85500, 112, 64, 256, 6, 3, 18,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x27, HDMI_DVI },
+ },
+ {
+ { 1280, 960, 108000, 112, 96, 312, 3, 1, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x20, HDMI_DVI },
+ },
+ {
+ { 1280, 1024, 108000, 112, 48, 248, 3, 1, 38,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x23, HDMI_DVI },
+ },
+ {
+ { 1024, 768, 65000, 136, 24, 160, 6, 3, 29,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x10, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 121750, 144, 88, 232, 4, 3, 32,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2A, HDMI_DVI },
+ },
+ {
+ { 1440, 900, 106500, 152, 80, 232, 6, 3, 25,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x2F, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 146250, 176 , 104, 280, 6, 3, 30,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_LOW,
+ false, },
+ { 0x3A, HDMI_DVI },
+ },
+ {
+ { 1366, 768, 85500, 143, 70, 213, 3, 3, 24,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x51, HDMI_DVI },
+ },
+ {
+ { 1920, 1080, 148500, 44, 148, 80, 5, 4, 36,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x52, HDMI_DVI },
+ },
+ {
+ { 1280, 768, 68250, 32, 48, 80, 7, 3, 12,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x16, HDMI_DVI },
+ },
+ {
+ { 1400, 1050, 101000, 32, 48, 80, 4, 3, 23,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x29, HDMI_DVI },
+ },
+ {
+ { 1680, 1050, 119000, 32, 48, 80, 6, 3, 21,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x39, HDMI_DVI },
+ },
+ {
+ { 1280, 800, 79500, 32, 48, 80, 6, 3, 14,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x1B, HDMI_DVI },
+ },
+ {
+ { 1280, 720, 74250, 40, 110, 220, 5, 5, 20,
+ OMAPDSS_SIG_ACTIVE_HIGH, OMAPDSS_SIG_ACTIVE_HIGH,
+ false, },
+ { 0x55, HDMI_DVI },
+ },
};
static int hdmi_runtime_get(void)
@@ -179,7 +350,7 @@ static const struct hdmi_config *hdmi_get_timings(void)
}
static bool hdmi_timings_compare(struct omap_video_timings *timing1,
- const struct hdmi_video_timings *timing2)
+ const struct omap_video_timings *timing2)
{
int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
@@ -758,6 +929,7 @@ static int __init omapdss_hdmihw_probe(struct platform_device *pdev)
hdmi.ip_data.core_av_offset = HDMI_CORE_AV;
hdmi.ip_data.pll_offset = HDMI_PLLCTRL;
hdmi.ip_data.phy_offset = HDMI_PHY;
+ mutex_init(&hdmi.ip_data.lock);
hdmi_panel_init();
@@ -785,7 +957,7 @@ static int __exit omapdss_hdmihw_remove(struct platform_device *pdev)
static int hdmi_runtime_suspend(struct device *dev)
{
- clk_disable(hdmi.sys_clk);
+ clk_disable_unprepare(hdmi.sys_clk);
dispc_runtime_put();
@@ -800,7 +972,7 @@ static int hdmi_runtime_resume(struct device *dev)
if (r < 0)
return r;
- clk_enable(hdmi.sys_clk);
+ clk_prepare_enable(hdmi.sys_clk);
return 0;
}
diff --git a/drivers/video/omap2/dss/hdmi_panel.c b/drivers/video/omap2/dss/hdmi_panel.c
index 1179e3c4b1c7..e10844faadf9 100644
--- a/drivers/video/omap2/dss/hdmi_panel.c
+++ b/drivers/video/omap2/dss/hdmi_panel.c
@@ -43,10 +43,11 @@ static int hdmi_panel_probe(struct omap_dss_device *dssdev)
{
DSSDBG("ENTER hdmi_panel_probe\n");
- dssdev->panel.config = OMAP_DSS_LCD_TFT |
- OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS;
-
- dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31};
+ dssdev->panel.timings = (struct omap_video_timings)
+ { 640, 480, 25175, 96, 16, 48, 2, 11, 31,
+ OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_LOW,
+ false,
+ };
DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
dssdev->panel.timings.x_res,
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index 0cbcde4c688a..53710fadc82d 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -500,16 +500,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
if (r)
return r;
- if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
+ if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC)
irq = DISPC_IRQ_EVSYNC_ODD;
- } else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI) {
+ else if (mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
irq = DISPC_IRQ_EVSYNC_EVEN;
- } else {
- if (mgr->id == OMAP_DSS_CHANNEL_LCD)
- irq = DISPC_IRQ_VSYNC;
- else
- irq = DISPC_IRQ_VSYNC2;
- }
+ else
+ irq = dispc_mgr_get_vsync_irq(mgr->id);
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
@@ -545,6 +541,10 @@ int dss_init_overlay_managers(struct platform_device *pdev)
mgr->name = "lcd2";
mgr->id = OMAP_DSS_CHANNEL_LCD2;
break;
+ case 3:
+ mgr->name = "lcd3";
+ mgr->id = OMAP_DSS_CHANNEL_LCD3;
+ break;
}
mgr->set_device = &dss_mgr_set_device;
@@ -665,9 +665,40 @@ int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
return 0;
}
+static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
+ const struct dss_lcd_mgr_config *config)
+{
+ struct dispc_clock_info cinfo = config->clock_info;
+ int dl = config->video_port_width;
+ bool stallmode = config->stallmode;
+ bool fifohandcheck = config->fifohandcheck;
+
+ if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
+ return -EINVAL;
+
+ if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
+ return -EINVAL;
+
+ if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
+ return -EINVAL;
+
+ /* fifohandcheck should be used only with stallmode */
+ if (stallmode == false && fifohandcheck == true)
+ return -EINVAL;
+
+ /*
+ * io pad mode can be only checked by using dssdev connected to the
+ * manager. Ignore checking these for now, add checks when manager
+ * is capable of holding information related to the connected interface
+ */
+
+ return 0;
+}
+
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
+ const struct dss_lcd_mgr_config *lcd_config,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl;
@@ -683,6 +714,10 @@ int dss_mgr_check(struct omap_overlay_manager *mgr,
if (r)
return r;
+ r = dss_mgr_check_lcd_config(mgr, lcd_config);
+ if (r)
+ return r;
+
list_for_each_entry(ovl, &mgr->overlays, list) {
struct omap_overlay_info *oi;
int r;
diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c
index b0ba60f88dd2..952c6fad9a81 100644
--- a/drivers/video/omap2/dss/overlay.c
+++ b/drivers/video/omap2/dss/overlay.c
@@ -528,14 +528,24 @@ void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
struct omap_overlay_manager *lcd_mgr;
struct omap_overlay_manager *tv_mgr;
struct omap_overlay_manager *lcd2_mgr = NULL;
+ struct omap_overlay_manager *lcd3_mgr = NULL;
struct omap_overlay_manager *mgr = NULL;
- lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
- tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
+ lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD);
+ tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_DIGIT);
+ if (dss_has_feature(FEAT_MGR_LCD3))
+ lcd3_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD3);
if (dss_has_feature(FEAT_MGR_LCD2))
- lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2);
-
- if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
+ lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_CHANNEL_LCD2);
+
+ if (dssdev->channel == OMAP_DSS_CHANNEL_LCD3) {
+ if (!lcd3_mgr->device || force) {
+ if (lcd3_mgr->device)
+ lcd3_mgr->unset_device(lcd3_mgr);
+ lcd3_mgr->set_device(lcd3_mgr, dssdev);
+ mgr = lcd3_mgr;
+ }
+ } else if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) {
if (!lcd2_mgr->device || force) {
if (lcd2_mgr->device)
lcd2_mgr->unset_device(lcd2_mgr);
@@ -677,3 +687,16 @@ int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
return 0;
}
+
+/*
+ * Checks if replication logic should be used. Only use when overlay is in
+ * RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp
+ */
+bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
+ enum omap_color_mode mode)
+{
+ if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
+ return false;
+
+ return config.video_port_width > 16;
+}
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index 7985fa12b9b4..7c087424b634 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -300,10 +300,11 @@ void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width,
}
EXPORT_SYMBOL(omap_rfbi_write_pixels);
-static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
+static int rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
u16 height, void (*callback)(void *data), void *data)
{
u32 l;
+ int r;
struct omap_video_timings timings = {
.hsw = 1,
.hfp = 1,
@@ -322,7 +323,9 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
dss_mgr_set_timings(dssdev->manager, &timings);
- dispc_mgr_enable(dssdev->manager->id, true);
+ r = dss_mgr_enable(dssdev->manager);
+ if (r)
+ return r;
rfbi.framedone_callback = callback;
rfbi.framedone_callback_data = data;
@@ -335,6 +338,8 @@ static void rfbi_transfer_area(struct omap_dss_device *dssdev, u16 width,
l = FLD_MOD(l, 1, 4, 4); /* ITE */
rfbi_write_reg(RFBI_CONTROL, l);
+
+ return 0;
}
static void framedone_callback(void *data, u32 mask)
@@ -814,8 +819,11 @@ int omap_rfbi_update(struct omap_dss_device *dssdev,
u16 x, u16 y, u16 w, u16 h,
void (*callback)(void *), void *data)
{
- rfbi_transfer_area(dssdev, w, h, callback, data);
- return 0;
+ int r;
+
+ r = rfbi_transfer_area(dssdev, w, h, callback, data);
+
+ return r;
}
EXPORT_SYMBOL(omap_rfbi_update);
@@ -859,6 +867,22 @@ static void rfbi_dump_regs(struct seq_file *s)
#undef DUMPREG
}
+static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev)
+{
+ struct dss_lcd_mgr_config mgr_config;
+
+ mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI;
+
+ mgr_config.stallmode = true;
+ /* Do we need fifohandcheck for RFBI? */
+ mgr_config.fifohandcheck = false;
+
+ mgr_config.video_port_width = dssdev->ctrl.pixel_size;
+ mgr_config.lcden_sig_polarity = 0;
+
+ dss_mgr_set_lcd_config(dssdev->manager, &mgr_config);
+}
+
int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
{
int r;
@@ -885,13 +909,7 @@ int omapdss_rfbi_display_enable(struct omap_dss_device *dssdev)
goto err1;
}
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
-
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_RFBI);
- dispc_mgr_enable_stallmode(dssdev->manager->id, true);
-
- dispc_mgr_set_tft_data_lines(dssdev->manager->id, dssdev->ctrl.pixel_size);
+ rfbi_config_lcd_manager(dssdev);
rfbi_configure(dssdev->phy.rfbi.channel,
dssdev->ctrl.pixel_size,
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c
index 3a43dc2a9b46..f43bfe17b3b6 100644
--- a/drivers/video/omap2/dss/sdi.c
+++ b/drivers/video/omap2/dss/sdi.c
@@ -32,19 +32,21 @@
static struct {
bool update_enabled;
struct regulator *vdds_sdi_reg;
-} sdi;
-static void sdi_basic_init(struct omap_dss_device *dssdev)
+ struct dss_lcd_mgr_config mgr_config;
+} sdi;
+static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
{
- dispc_mgr_set_io_pad_mode(DSS_IO_PAD_MODE_BYPASS);
- dispc_mgr_enable_stallmode(dssdev->manager->id, false);
+ sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
+
+ sdi.mgr_config.stallmode = false;
+ sdi.mgr_config.fifohandcheck = false;
- dispc_mgr_set_lcd_display_type(dssdev->manager->id,
- OMAP_DSS_LCD_DISPLAY_TFT);
+ sdi.mgr_config.video_port_width = 24;
+ sdi.mgr_config.lcden_sig_polarity = 1;
- dispc_mgr_set_tft_data_lines(dssdev->manager->id, 24);
- dispc_lcd_enable_signal_polarity(1);
+ dss_mgr_set_lcd_config(dssdev->manager, &sdi.mgr_config);
}
int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
@@ -52,8 +54,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
struct omap_video_timings *t = &dssdev->panel.timings;
struct dss_clock_info dss_cinfo;
struct dispc_clock_info dispc_cinfo;
- u16 lck_div, pck_div;
- unsigned long fck;
unsigned long pck;
int r;
@@ -76,24 +76,17 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_get_dispc;
- sdi_basic_init(dssdev);
-
/* 15.5.9.1.2 */
- dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF;
-
- dispc_mgr_set_pol_freq(dssdev->manager->id, dssdev->panel.config,
- dssdev->panel.acbi, dssdev->panel.acb);
+ dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
- r = dss_calc_clock_div(1, t->pixel_clock * 1000,
- &dss_cinfo, &dispc_cinfo);
+ r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo);
if (r)
goto err_calc_clock_div;
- fck = dss_cinfo.fck;
- lck_div = dispc_cinfo.lck_div;
- pck_div = dispc_cinfo.pck_div;
+ sdi.mgr_config.clock_info = dispc_cinfo;
- pck = fck / lck_div / pck_div / 1000;
+ pck = dss_cinfo.fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000;
if (pck != t->pixel_clock) {
DSSWARN("Could not find exact pixel clock. Requested %d kHz, "
@@ -110,9 +103,21 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
if (r)
goto err_set_dss_clock_div;
- r = dispc_mgr_set_clock_div(dssdev->manager->id, &dispc_cinfo);
- if (r)
- goto err_set_dispc_clock_div;
+ sdi_config_lcd_manager(dssdev);
+
+ /*
+ * LCLK and PCLK divisors are located in shadow registers, and we
+ * normally write them to DISPC registers when enabling the output.
+ * However, SDI uses pck-free as source clock for its PLL, and pck-free
+ * is affected by the divisors. And as we need the PLL before enabling
+ * the output, we need to write the divisors early.
+ *
+ * It seems just writing to the DISPC register is enough, and we don't
+ * need to care about the shadow register mechanism for pck-free. The
+ * exact reason for this is unknown.
+ */
+ dispc_mgr_set_clock_div(dssdev->manager->id,
+ &sdi.mgr_config.clock_info);
dss_sdi_init(dssdev->phy.sdi.datapairs);
r = dss_sdi_enable();
@@ -129,7 +134,6 @@ int omapdss_sdi_display_enable(struct omap_dss_device *dssdev)
err_mgr_enable:
dss_sdi_disable();
err_sdi_enable:
-err_set_dispc_clock_div:
err_set_dss_clock_div:
err_calc_clock_div:
dispc_runtime_put();
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index e734cb444bc7..b046c208cb97 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -42,30 +42,13 @@ enum hdmi_clk_refsel {
HDMI_REFSEL_SYSCLK = 3
};
-/* HDMI timing structure */
-struct hdmi_video_timings {
- u16 x_res;
- u16 y_res;
- /* Unit: KHz */
- u32 pixel_clock;
- u16 hsw;
- u16 hfp;
- u16 hbp;
- u16 vsw;
- u16 vfp;
- u16 vbp;
- bool vsync_pol;
- bool hsync_pol;
- bool interlace;
-};
-
struct hdmi_cm {
int code;
int mode;
};
struct hdmi_config {
- struct hdmi_video_timings timings;
+ struct omap_video_timings timings;
struct hdmi_cm cm;
};
@@ -177,7 +160,7 @@ struct hdmi_ip_data {
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
int hpd_gpio;
- bool phy_tx_enabled;
+ struct mutex lock;
};
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index 4dae1b291079..c23b85a20cdc 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -157,6 +157,10 @@ static int hdmi_pll_init(struct hdmi_ip_data *ip_data)
/* PHY_PWR_CMD */
static int hdmi_set_phy_pwr(struct hdmi_ip_data *ip_data, enum hdmi_phy_pwr val)
{
+ /* Return if already the state */
+ if (REG_GET(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, 5, 4) == val)
+ return 0;
+
/* Command for power control of HDMI PHY */
REG_FLD_MOD(hdmi_wp_base(ip_data), HDMI_WP_PWR_CTRL, val, 7, 6);
@@ -231,21 +235,13 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
{
- unsigned long flags;
bool hpd;
int r;
- /* this should be in ti_hdmi_4xxx_ip private data */
- static DEFINE_SPINLOCK(phy_tx_lock);
- spin_lock_irqsave(&phy_tx_lock, flags);
+ mutex_lock(&ip_data->lock);
hpd = gpio_get_value(ip_data->hpd_gpio);
- if (hpd == ip_data->phy_tx_enabled) {
- spin_unlock_irqrestore(&phy_tx_lock, flags);
- return 0;
- }
-
if (hpd)
r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
else
@@ -257,9 +253,8 @@ static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
goto err;
}
- ip_data->phy_tx_enabled = hpd;
err:
- spin_unlock_irqrestore(&phy_tx_lock, flags);
+ mutex_unlock(&ip_data->lock);
return r;
}
@@ -327,7 +322,6 @@ void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
- ip_data->phy_tx_enabled = false;
}
static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
@@ -747,11 +741,15 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
{
u32 r;
+ bool vsync_pol, hsync_pol;
pr_debug("Enter hdmi_wp_video_config_interface\n");
+ vsync_pol = ip_data->cfg.timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+ hsync_pol = ip_data->cfg.timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH;
+
r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7);
- r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6);
+ r = FLD_MOD(r, vsync_pol, 7, 7);
+ r = FLD_MOD(r, hsync_pol, 6, 6);
r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 3907c8b6ecbc..3a220877461a 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -272,6 +272,8 @@ const struct omap_video_timings omap_dss_pal_timings = {
.vsw = 5,
.vfp = 5,
.vbp = 41,
+
+ .interlace = true,
};
EXPORT_SYMBOL(omap_dss_pal_timings);
@@ -285,6 +287,8 @@ const struct omap_video_timings omap_dss_ntsc_timings = {
.vsw = 6,
.vfp = 6,
.vbp = 31,
+
+ .interlace = true,
};
EXPORT_SYMBOL(omap_dss_ntsc_timings);
@@ -930,7 +934,7 @@ static int __exit omap_venchw_remove(struct platform_device *pdev)
static int venc_runtime_suspend(struct device *dev)
{
if (venc.tv_dac_clk)
- clk_disable(venc.tv_dac_clk);
+ clk_disable_unprepare(venc.tv_dac_clk);
dispc_runtime_put();
@@ -946,7 +950,7 @@ static int venc_runtime_resume(struct device *dev)
return r;
if (venc.tv_dac_clk)
- clk_enable(venc.tv_dac_clk);
+ clk_prepare_enable(venc.tv_dac_clk);
return 0;
}
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 3450ea0966c9..fc671d3d8004 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -733,6 +733,12 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->lower_margin = timings.vfp;
var->hsync_len = timings.hsw;
var->vsync_len = timings.vsw;
+ var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+ FB_SYNC_HOR_HIGH_ACT : 0;
+ var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?
+ FB_SYNC_VERT_HIGH_ACT : 0;
+ var->vmode = timings.interlace ?
+ FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
} else {
var->pixclock = 0;
var->left_margin = 0;
@@ -741,12 +747,10 @@ int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
var->lower_margin = 0;
var->hsync_len = 0;
var->vsync_len = 0;
+ var->sync = 0;
+ var->vmode = FB_VMODE_NONINTERLACED;
}
- /* TODO: get these from panel->config */
- var->vmode = FB_VMODE_NONINTERLACED;
- var->sync = 0;
-
return 0;
}
@@ -1188,7 +1192,7 @@ static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,
break;
if (regno < 16) {
- u16 pal;
+ u32 pal;
pal = ((red >> (16 - var->red.length)) <<
var->red.offset) |
((green >> (16 - var->green.length)) <<
@@ -1993,6 +1997,7 @@ static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)
}
static int omapfb_mode_to_timings(const char *mode_str,
+ struct omap_dss_device *display,
struct omap_video_timings *timings, u8 *bpp)
{
struct fb_info *fbi;
@@ -2046,6 +2051,14 @@ static int omapfb_mode_to_timings(const char *mode_str,
goto err;
}
+ if (display->driver->get_timings) {
+ display->driver->get_timings(display, timings);
+ } else {
+ timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+ }
+
timings->pixel_clock = PICOS2KHZ(var->pixclock);
timings->hbp = var->left_margin;
timings->hfp = var->right_margin;
@@ -2055,6 +2068,13 @@ static int omapfb_mode_to_timings(const char *mode_str,
timings->vsw = var->vsync_len;
timings->x_res = var->xres;
timings->y_res = var->yres;
+ timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ timings->interlace = var->vmode & FB_VMODE_INTERLACED;
switch (var->bits_per_pixel) {
case 16:
@@ -2085,7 +2105,7 @@ static int omapfb_set_def_mode(struct omapfb2_device *fbdev,
struct omap_video_timings timings, temp_timings;
struct omapfb_display_data *d;
- r = omapfb_mode_to_timings(mode_str, &timings, &bpp);
+ r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp);
if (r)
return r;
@@ -2178,8 +2198,17 @@ static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)
}
static void fb_videomode_to_omap_timings(struct fb_videomode *m,
+ struct omap_dss_device *display,
struct omap_video_timings *t)
{
+ if (display->driver->get_timings) {
+ display->driver->get_timings(display, t);
+ } else {
+ t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
+ t->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
+ t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+ }
+
t->x_res = m->xres;
t->y_res = m->yres;
t->pixel_clock = PICOS2KHZ(m->pixclock);
@@ -2189,6 +2218,13 @@ static void fb_videomode_to_omap_timings(struct fb_videomode *m,
t->vsw = m->vsync_len;
t->vfp = m->lower_margin;
t->vbp = m->upper_margin;
+ t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ?
+ OMAPDSS_SIG_ACTIVE_HIGH :
+ OMAPDSS_SIG_ACTIVE_LOW;
+ t->interlace = m->vmode & FB_VMODE_INTERLACED;
}
static int omapfb_find_best_mode(struct omap_dss_device *display,
@@ -2231,7 +2267,7 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
if (m->xres == 2880 || m->xres == 1440)
continue;
- fb_videomode_to_omap_timings(m, &t);
+ fb_videomode_to_omap_timings(m, display, &t);
r = display->driver->check_timings(display, &t);
if (r == 0 && best_xres < m->xres) {
@@ -2245,7 +2281,8 @@ static int omapfb_find_best_mode(struct omap_dss_device *display,
goto err2;
}
- fb_videomode_to_omap_timings(&specs->modedb[best_idx], timings);
+ fb_videomode_to_omap_timings(&specs->modedb[best_idx], display,
+ timings);
r = 0;
diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c
index 2c80246b18b8..1d007366b917 100644
--- a/drivers/video/s3fb.c
+++ b/drivers/video/s3fb.c
@@ -84,7 +84,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
"S3 Virge/VX", "S3 Virge/DX", "S3 Virge/GX",
"S3 Virge/GX2", "S3 Virge/GX2+", "",
"S3 Trio3D/1X", "S3 Trio3D/2X", "S3 Trio3D/2X",
- "S3 Trio3D"};
+ "S3 Trio3D", "S3 Virge/MX"};
#define CHIP_UNKNOWN 0x00
#define CHIP_732_TRIO32 0x01
@@ -105,6 +105,7 @@ static const char * const s3_names[] = {"S3 Unknown", "S3 Trio32", "S3 Trio64",
#define CHIP_362_TRIO3D_2X 0x11
#define CHIP_368_TRIO3D_2X 0x12
#define CHIP_365_TRIO3D 0x13
+#define CHIP_260_VIRGE_MX 0x14
#define CHIP_XXX_TRIO 0x80
#define CHIP_XXX_TRIO64V2_DXGX 0x81
@@ -280,7 +281,8 @@ static int __devinit s3fb_setup_ddc_bus(struct fb_info *info)
*/
/* vga_wseq(par->state.vgabase, 0x08, 0x06); - not needed, already unlocked */
if (par->chip == CHIP_357_VIRGE_GX2 ||
- par->chip == CHIP_359_VIRGE_GX2P)
+ par->chip == CHIP_359_VIRGE_GX2P ||
+ par->chip == CHIP_260_VIRGE_MX)
svga_wseq_mask(par->state.vgabase, 0x0d, 0x01, 0x03);
else
svga_wseq_mask(par->state.vgabase, 0x0d, 0x00, 0x03);
@@ -487,7 +489,8 @@ static void s3_set_pixclock(struct fb_info *info, u32 pixclock)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X) {
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX) {
vga_wseq(par->state.vgabase, 0x12, (n - 2) | ((r & 3) << 6)); /* n and two bits of r */
vga_wseq(par->state.vgabase, 0x29, r >> 2); /* remaining highest bit of r */
} else
@@ -690,7 +693,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X) {
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX) {
vga_wcrt(par->state.vgabase, 0x54, 0x18); /* M parameter */
vga_wcrt(par->state.vgabase, 0x60, 0xff); /* N parameter */
vga_wcrt(par->state.vgabase, 0x61, 0xff); /* L parameter */
@@ -739,7 +743,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_368_TRIO3D_2X ||
par->chip == CHIP_365_TRIO3D ||
par->chip == CHIP_375_VIRGE_DX ||
- par->chip == CHIP_385_VIRGE_GX) {
+ par->chip == CHIP_385_VIRGE_GX ||
+ par->chip == CHIP_260_VIRGE_MX) {
dbytes = info->var.xres * ((bpp+7)/8);
vga_wcrt(par->state.vgabase, 0x91, (dbytes + 7) / 8);
vga_wcrt(par->state.vgabase, 0x90, (((dbytes + 7) / 8) >> 8) | 0x80);
@@ -751,7 +756,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X)
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX)
vga_wcrt(par->state.vgabase, 0x34, 0x00);
else /* enable Data Transfer Position Control (DTPC) */
vga_wcrt(par->state.vgabase, 0x34, 0x10);
@@ -807,7 +813,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip == CHIP_359_VIRGE_GX2P ||
par->chip == CHIP_360_TRIO3D_1X ||
par->chip == CHIP_362_TRIO3D_2X ||
- par->chip == CHIP_368_TRIO3D_2X)
+ par->chip == CHIP_368_TRIO3D_2X ||
+ par->chip == CHIP_260_VIRGE_MX)
svga_wcrt_mask(par->state.vgabase, 0x67, 0x00, 0xF0);
else {
svga_wcrt_mask(par->state.vgabase, 0x67, 0x10, 0xF0);
@@ -837,7 +844,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X)
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX)
hmul = 2;
}
break;
@@ -864,7 +872,8 @@ static int s3fb_set_par(struct fb_info *info)
par->chip != CHIP_359_VIRGE_GX2P &&
par->chip != CHIP_360_TRIO3D_1X &&
par->chip != CHIP_362_TRIO3D_2X &&
- par->chip != CHIP_368_TRIO3D_2X)
+ par->chip != CHIP_368_TRIO3D_2X &&
+ par->chip != CHIP_260_VIRGE_MX)
hmul = 2;
}
break;
@@ -1208,7 +1217,8 @@ static int __devinit s3_pci_probe(struct pci_dev *dev, const struct pci_device_i
break;
}
} else if (par->chip == CHIP_357_VIRGE_GX2 ||
- par->chip == CHIP_359_VIRGE_GX2P) {
+ par->chip == CHIP_359_VIRGE_GX2P ||
+ par->chip == CHIP_260_VIRGE_MX) {
switch ((regval & 0xC0) >> 6) {
case 1: /* 4MB */
info->screen_size = 4 << 20;
@@ -1515,6 +1525,7 @@ static struct pci_device_id s3_devices[] __devinitdata = {
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A12), .driver_data = CHIP_359_VIRGE_GX2P},
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8A13), .driver_data = CHIP_36X_TRIO3D_1X_2X},
{PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8904), .driver_data = CHIP_365_TRIO3D},
+ {PCI_DEVICE(PCI_VENDOR_ID_S3, 0x8C01), .driver_data = CHIP_260_VIRGE_MX},
{0, 0, 0, 0, 0, 0, 0}
};
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 4c6b84488561..3951fdae5f68 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -127,8 +127,7 @@ static void sh_mipi_shutdown(struct platform_device *pdev)
sh_mipi_dsi_enable(mipi, false);
}
-static int __init sh_mipi_setup(struct sh_mipi *mipi,
- struct sh_mipi_dsi_info *pdata)
+static int sh_mipi_setup(struct sh_mipi *mipi, struct sh_mipi_dsi_info *pdata)
{
void __iomem *base = mipi->base;
struct sh_mobile_lcdc_chan_cfg *ch = pdata->lcd_chan;
@@ -551,7 +550,7 @@ efindslot:
return ret;
}
-static int __exit sh_mipi_remove(struct platform_device *pdev)
+static int __devexit sh_mipi_remove(struct platform_device *pdev)
{
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
@@ -592,7 +591,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
}
static struct platform_driver sh_mipi_driver = {
- .remove = __exit_p(sh_mipi_remove),
+ .remove = __devexit_p(sh_mipi_remove),
.shutdown = sh_mipi_shutdown,
.driver = {
.name = "sh-mipi-dsi",
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index e672698bd820..699487c287b2 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -12,6 +12,7 @@
#include <linux/backlight.h>
#include <linux/clk.h>
#include <linux/console.h>
+#include <linux/ctype.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/gpio.h>
@@ -32,12 +33,176 @@
#include "sh_mobile_lcdcfb.h"
+/* ----------------------------------------------------------------------------
+ * Overlay register definitions
+ */
+
+#define LDBCR 0xb00
+#define LDBCR_UPC(n) (1 << ((n) + 16))
+#define LDBCR_UPF(n) (1 << ((n) + 8))
+#define LDBCR_UPD(n) (1 << ((n) + 0))
+#define LDBnBSIFR(n) (0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN (1 << 31)
+#define LDBBSIFR_VS (1 << 29)
+#define LDBBSIFR_BRSEL (1 << 28)
+#define LDBBSIFR_MX (1 << 27)
+#define LDBBSIFR_MY (1 << 26)
+#define LDBBSIFR_CV3 (3 << 24)
+#define LDBBSIFR_CV2 (2 << 24)
+#define LDBBSIFR_CV1 (1 << 24)
+#define LDBBSIFR_CV0 (0 << 24)
+#define LDBBSIFR_CV_MASK (3 << 24)
+#define LDBBSIFR_LAY_MASK (0xff << 16)
+#define LDBBSIFR_LAY_SHIFT 16
+#define LDBBSIFR_ROP3_MASK (0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT 16
+#define LDBBSIFR_AL_PL8 (3 << 14)
+#define LDBBSIFR_AL_PL1 (2 << 14)
+#define LDBBSIFR_AL_PK (1 << 14)
+#define LDBBSIFR_AL_1 (0 << 14)
+#define LDBBSIFR_AL_MASK (3 << 14)
+#define LDBBSIFR_SWPL (1 << 10)
+#define LDBBSIFR_SWPW (1 << 9)
+#define LDBBSIFR_SWPB (1 << 8)
+#define LDBBSIFR_RY (1 << 7)
+#define LDBBSIFR_CHRR_420 (2 << 0)
+#define LDBBSIFR_CHRR_422 (1 << 0)
+#define LDBBSIFR_CHRR_444 (0 << 0)
+#define LDBBSIFR_RPKF_ARGB32 (0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16 (0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24 (0x0b << 0)
+#define LDBBSIFR_RPKF_MASK (0x1f << 0)
+#define LDBnBSSZR(n) (0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK (0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT 16
+#define LDBBSSZR_BHSS_MASK (0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT 0
+#define LDBnBLOCR(n) (0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK (0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT 16
+#define LDBBLOCR_CHLC_MASK (0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT 0
+#define LDBnBSMWR(n) (0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK (0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT 16
+#define LDBBSMWR_BSMW_MASK (0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT 0
+#define LDBnBSAYR(n) (0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK (0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT 24
+#define LDBBSAYR_FG1R_MASK (0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT 16
+#define LDBBSAYR_FG1G_MASK (0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT 8
+#define LDBBSAYR_FG1B_MASK (0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT 0
+#define LDBnBSACR(n) (0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK (0xff << 24)
+#define LDBBSACR_FG2A_SHIFT 24
+#define LDBBSACR_FG2R_MASK (0xff << 16)
+#define LDBBSACR_FG2R_SHIFT 16
+#define LDBBSACR_FG2G_MASK (0xff << 8)
+#define LDBBSACR_FG2G_SHIFT 8
+#define LDBBSACR_FG2B_MASK (0xff << 0)
+#define LDBBSACR_FG2B_SHIFT 0
+#define LDBnBSAAR(n) (0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK (0xff << 24)
+#define LDBBSAAR_AP_SHIFT 24
+#define LDBBSAAR_R_MASK (0xff << 16)
+#define LDBBSAAR_R_SHIFT 16
+#define LDBBSAAR_GY_MASK (0xff << 8)
+#define LDBBSAAR_GY_SHIFT 8
+#define LDBBSAAR_B_MASK (0xff << 0)
+#define LDBBSAAR_B_SHIFT 0
+#define LDBnBPPCR(n) (0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK (0xff << 24)
+#define LDBBPPCR_AP_SHIFT 24
+#define LDBBPPCR_R_MASK (0xff << 16)
+#define LDBBPPCR_R_SHIFT 16
+#define LDBBPPCR_GY_MASK (0xff << 8)
+#define LDBBPPCR_GY_SHIFT 8
+#define LDBBPPCR_B_MASK (0xff << 0)
+#define LDBBPPCR_B_SHIFT 0
+#define LDBnBBGCL(n) (0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK (0xff << 24)
+#define LDBBBGCL_BGA_SHIFT 24
+#define LDBBBGCL_BGR_MASK (0xff << 16)
+#define LDBBBGCL_BGR_SHIFT 16
+#define LDBBBGCL_BGG_MASK (0xff << 8)
+#define LDBBBGCL_BGG_SHIFT 8
+#define LDBBBGCL_BGB_MASK (0xff << 0)
+#define LDBBBGCL_BGB_SHIFT 0
+
#define SIDE_B_OFFSET 0x1000
#define MIRROR_OFFSET 0x2000
#define MAX_XRES 1920
#define MAX_YRES 1080
+enum sh_mobile_lcdc_overlay_mode {
+ LCDC_OVERLAY_BLEND,
+ LCDC_OVERLAY_ROP3,
+};
+
+/*
+ * struct sh_mobile_lcdc_overlay - LCDC display overlay
+ *
+ * @channel: LCDC channel this overlay belongs to
+ * @cfg: Overlay configuration
+ * @info: Frame buffer device
+ * @index: Overlay index (0-3)
+ * @base: Overlay registers base address
+ * @enabled: True if the overlay is enabled
+ * @mode: Overlay blending mode (alpha blend or ROP3)
+ * @alpha: Global alpha blending value (0-255, for alpha blending mode)
+ * @rop3: Raster operation (for ROP3 mode)
+ * @fb_mem: Frame buffer virtual memory address
+ * @fb_size: Frame buffer size in bytes
+ * @dma_handle: Frame buffer DMA address
+ * @base_addr_y: Overlay base address (RGB or luma component)
+ * @base_addr_c: Overlay base address (chroma component)
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
+ * @format: Current pixelf format
+ * @xres: Horizontal visible resolution
+ * @xres_virtual: Horizontal total resolution
+ * @yres: Vertical visible resolution
+ * @yres_virtual: Vertical total resolution
+ * @pitch: Overlay line pitch
+ * @pos_x: Horizontal overlay position
+ * @pos_y: Vertical overlay position
+ */
+struct sh_mobile_lcdc_overlay {
+ struct sh_mobile_lcdc_chan *channel;
+
+ const struct sh_mobile_lcdc_overlay_cfg *cfg;
+ struct fb_info *info;
+
+ unsigned int index;
+ unsigned long base;
+
+ bool enabled;
+ enum sh_mobile_lcdc_overlay_mode mode;
+ unsigned int alpha;
+ unsigned int rop3;
+
+ void *fb_mem;
+ unsigned long fb_size;
+
+ dma_addr_t dma_handle;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long pan_y_offset;
+
+ const struct sh_mobile_lcdc_format_info *format;
+ unsigned int xres;
+ unsigned int xres_virtual;
+ unsigned int yres;
+ unsigned int yres_virtual;
+ unsigned int pitch;
+ int pos_x;
+ int pos_y;
+};
+
struct sh_mobile_lcdc_priv {
void __iomem *base;
int irq;
@@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv {
struct device *dev;
struct clk *dot_clk;
unsigned long lddckr;
+
struct sh_mobile_lcdc_chan ch[2];
+ struct sh_mobile_lcdc_overlay overlays[4];
+
struct notifier_block notifier;
int started;
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
@@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
}
+static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl,
+ int reg, unsigned long data)
+{
+ iowrite32(data, ovl->channel->lcdc->base + reg);
+ iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET);
+}
+
static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
unsigned long reg_offs, unsigned long data)
{
@@ -384,8 +559,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
return true;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var,
- struct fb_info *info);
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
enum sh_mobile_lcdc_entity_event event,
@@ -439,7 +614,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
fb_videomode_to_var(&var, mode);
var.bits_per_pixel = info->var.bits_per_pixel;
var.grayscale = info->var.grayscale;
- ret = sh_mobile_check_var(&var, info);
+ ret = sh_mobile_lcdc_check_var(&var, info);
break;
}
@@ -585,7 +760,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
{
unsigned long ldintr;
int ret;
@@ -685,8 +860,98 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
lcdc_write_chan(ch, LDHAJR, tmp);
}
+static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ u32 format = 0;
+
+ if (!ovl->enabled) {
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0);
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+ return;
+ }
+
+ ovl->base_addr_y = ovl->dma_handle;
+ ovl->base_addr_c = ovl->dma_handle
+ + ovl->xres_virtual * ovl->yres_virtual;
+
+ switch (ovl->mode) {
+ case LCDC_OVERLAY_BLEND:
+ format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT);
+ break;
+
+ case LCDC_OVERLAY_ROP3:
+ format = LDBBSIFR_EN | LDBBSIFR_BRSEL
+ | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT);
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV24:
+ format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ default:
+ format |= LDBBSIFR_SWPL;
+ break;
+ }
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_RGB565:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+ break;
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV42:
+ format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+ break;
+ }
+
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+ lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format);
+
+ lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index),
+ (ovl->yres << LDBBSSZR_BVSS_SHIFT) |
+ (ovl->xres << LDBBSSZR_BHSS_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index),
+ (ovl->pos_y << LDBBLOCR_CVLC_SHIFT) |
+ (ovl->pos_x << LDBBLOCR_CHLC_SHIFT));
+ lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index),
+ ovl->pitch << LDBBSMWR_BSMW_SHIFT);
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+}
+
/*
- * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * __sh_mobile_lcdc_start - Configure and start the LCDC
* @priv: LCDC device
*
* Configure all enabled channels and start the LCDC device. All external
@@ -839,27 +1104,25 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Compute frame buffer base address and pitch for each channel. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
int pixelformat;
- void *meram;
+ void *cache;
ch = &priv->ch[k];
if (!ch->enabled)
continue;
ch->base_addr_y = ch->dma_handle;
- ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
+ ch->base_addr_c = ch->dma_handle
+ + ch->xres_virtual * ch->yres_virtual;
ch->line_size = ch->pitch;
/* Enable MERAM if possible. */
- if (mdev == NULL || mdev->ops == NULL ||
- ch->cfg->meram_cfg == NULL)
+ if (mdev == NULL || ch->cfg->meram_cfg == NULL)
continue;
- /* we need to de-init configured ICBs before we can
- * re-initialize them.
- */
- if (ch->meram) {
- mdev->ops->meram_unregister(mdev, ch->meram);
- ch->meram = NULL;
+ /* Free the allocated MERAM cache. */
+ if (ch->cache) {
+ sh_mobile_meram_cache_free(mdev, ch->cache);
+ ch->cache = NULL;
}
switch (ch->format->fourcc) {
@@ -881,17 +1144,22 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
break;
}
- meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+ cache = sh_mobile_meram_cache_alloc(mdev, ch->cfg->meram_cfg,
ch->pitch, ch->yres, pixelformat,
&ch->line_size);
- if (!IS_ERR(meram)) {
- mdev->ops->meram_update(mdev, meram,
+ if (!IS_ERR(cache)) {
+ sh_mobile_meram_cache_update(mdev, cache,
ch->base_addr_y, ch->base_addr_c,
&ch->base_addr_y, &ch->base_addr_c);
- ch->meram = meram;
+ ch->cache = cache;
}
}
+ for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k];
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
/* Start the LCDC. */
__sh_mobile_lcdc_start(priv);
@@ -953,12 +1221,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_display_off(ch);
- /* disable the meram */
- if (ch->meram) {
- struct sh_mobile_meram_info *mdev;
- mdev = priv->meram_dev;
- mdev->ops->meram_unregister(mdev, ch->meram);
- ch->meram = 0;
+ /* Free the MERAM cache. */
+ if (ch->cache) {
+ sh_mobile_meram_cache_free(priv->meram_dev, ch->cache);
+ ch->cache = 0;
}
}
@@ -975,8 +1241,511 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_clk_off(priv);
}
+static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+ return -EINVAL;
+
+ /* Make sure the virtual resolution is at least as big as the visible
+ * resolution.
+ */
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (sh_mobile_format_is_fourcc(var)) {
+ const struct sh_mobile_lcdc_format_info *format;
+
+ format = sh_mobile_format_info(var->grayscale);
+ if (format == NULL)
+ return -EINVAL;
+ var->bits_per_pixel = format->bpp;
+
+ /* Default to RGB and JPEG color-spaces for RGB and YUV formats
+ * respectively.
+ */
+ if (!format->yuv)
+ var->colorspace = V4L2_COLORSPACE_SRGB;
+ else if (var->colorspace != V4L2_COLORSPACE_REC709)
+ var->colorspace = V4L2_COLORSPACE_JPEG;
+ } else {
+ if (var->bits_per_pixel <= 16) { /* RGB 565 */
+ var->bits_per_pixel = 16;
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
+ var->bits_per_pixel = 24;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
+ var->bits_per_pixel = 32;
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ } else
+ return -EINVAL;
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+ }
+
+ /* Make sure we don't exceed our allocated memory. */
+ if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
+ info->fix.smem_len)
+ return -EINVAL;
+
+ return 0;
+}
+
/* -----------------------------------------------------------------------------
- * Frame buffer operations
+ * Frame buffer operations - Overlays
+ */
+
+static ssize_t
+overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha);
+}
+
+static ssize_t
+overlay_alpha_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int alpha;
+ char *endp;
+
+ alpha = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (alpha > 255)
+ return -EINVAL;
+
+ if (ovl->alpha != alpha) {
+ ovl->alpha = alpha;
+
+ if (ovl->mode == LCDC_OVERLAY_BLEND && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode);
+}
+
+static ssize_t
+overlay_mode_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int mode;
+ char *endp;
+
+ mode = simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3)
+ return -EINVAL;
+
+ if (ovl->mode != mode) {
+ ovl->mode = mode;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_position_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y);
+}
+
+static ssize_t
+overlay_position_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ char *endp;
+ int pos_x;
+ int pos_y;
+
+ pos_x = simple_strtol(buf, &endp, 10);
+ if (*endp != ',')
+ return -EINVAL;
+
+ pos_y = simple_strtol(endp + 1, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) {
+ ovl->pos_x = pos_x;
+ ovl->pos_y = pos_y;
+
+ if (ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static ssize_t
+overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3);
+}
+
+static ssize_t
+overlay_rop3_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned int rop3;
+ char *endp;
+
+ rop3 = !!simple_strtoul(buf, &endp, 10);
+ if (isspace(*endp))
+ endp++;
+
+ if (endp - buf != count)
+ return -EINVAL;
+
+ if (rop3 > 255)
+ return -EINVAL;
+
+ if (ovl->rop3 != rop3) {
+ ovl->rop3 = rop3;
+
+ if (ovl->mode == LCDC_OVERLAY_ROP3 && ovl->enabled)
+ sh_mobile_lcdc_overlay_setup(ovl);
+ }
+
+ return count;
+}
+
+static const struct device_attribute overlay_sysfs_attrs[] = {
+ __ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
+ overlay_alpha_show, overlay_alpha_store),
+ __ATTR(ovl_mode, S_IRUGO|S_IWUSR,
+ overlay_mode_show, overlay_mode_store),
+ __ATTR(ovl_position, S_IRUGO|S_IWUSR,
+ overlay_position_show, overlay_position_store),
+ __ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
+ overlay_rop3_show, overlay_rop3_store),
+};
+
+static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix = {
+ .id = "SH Mobile LCDC",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .accel = FB_ACCEL_NONE,
+ .xpanstep = 1,
+ .ypanstep = 1,
+ .ywrapstep = 0,
+ .capabilities = FB_CAP_FOURCC,
+};
+
+static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+ unsigned long base_addr_y;
+ unsigned long base_addr_c;
+ unsigned long y_offset;
+ unsigned long c_offset;
+
+ if (!ovl->format->yuv) {
+ y_offset = (var->yoffset * ovl->xres_virtual + var->xoffset)
+ * ovl->format->bpp / 8;
+ c_offset = 0;
+ } else {
+ unsigned int xsub = ovl->format->bpp < 24 ? 2 : 1;
+ unsigned int ysub = ovl->format->bpp < 16 ? 2 : 1;
+
+ y_offset = var->yoffset * ovl->xres_virtual + var->xoffset;
+ c_offset = var->yoffset / ysub * ovl->xres_virtual * 2 / xsub
+ + var->xoffset * 2 / xsub;
+ }
+
+ /* If the Y offset hasn't changed, the C offset hasn't either. There's
+ * nothing to do in that case.
+ */
+ if (y_offset == ovl->pan_y_offset)
+ return 0;
+
+ /* Set the source address for the next refresh */
+ base_addr_y = ovl->dma_handle + y_offset;
+ base_addr_c = ovl->dma_handle + ovl->xres_virtual * ovl->yres_virtual
+ + c_offset;
+
+ ovl->base_addr_y = base_addr_y;
+ ovl->base_addr_c = base_addr_c;
+ ovl->pan_y_offset = y_offset;
+
+ lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+ lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+ lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+ lcdc_write(ovl->channel->lcdc, LDBCR,
+ LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+
+ return 0;
+}
+
+static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ return sh_mobile_lcdc_wait_for_vsync(ovl->channel);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ return __sh_mobile_lcdc_check_var(var, info);
+}
+
+static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ ovl->format =
+ sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+
+ ovl->xres = info->var.xres;
+ ovl->xres_virtual = info->var.xres_virtual;
+ ovl->yres = info->var.yres;
+ ovl->yres_virtual = info->var.yres_virtual;
+
+ if (ovl->format->yuv)
+ ovl->pitch = info->var.xres_virtual;
+ else
+ ovl->pitch = info->var.xres_virtual * ovl->format->bpp / 8;
+
+ sh_mobile_lcdc_overlay_setup(ovl);
+
+ info->fix.line_length = ovl->pitch;
+
+ if (sh_mobile_format_is_fourcc(&info->var)) {
+ info->fix.type = FB_TYPE_FOURCC;
+ info->fix.visual = FB_VISUAL_FOURCC;
+ } else {
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ }
+
+ return 0;
+}
+
+/* Overlay blanking. Disable the overlay when blanked. */
+static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
+{
+ struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+ ovl->enabled = !blank;
+ sh_mobile_lcdc_overlay_setup(ovl);
+
+ /* Prevent the backlight from receiving a blanking event by returning
+ * a non-zero value.
+ */
+ return 1;
+}
+
+static struct fb_ops sh_mobile_lcdc_overlay_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+ .fb_blank = sh_mobile_lcdc_overlay_blank,
+ .fb_pan_display = sh_mobile_lcdc_overlay_pan,
+ .fb_ioctl = sh_mobile_lcdc_overlay_ioctl,
+ .fb_check_var = sh_mobile_lcdc_overlay_check_var,
+ .fb_set_par = sh_mobile_lcdc_overlay_set_par,
+};
+
+static void
+sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info == NULL || info->dev == NULL)
+ return;
+
+ unregister_framebuffer(ovl->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
+ struct fb_info *info = ovl->info;
+ unsigned int i;
+ int ret;
+
+ if (info == NULL)
+ return 0;
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return ret;
+
+ dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n",
+ dev_name(lcdc->dev), ovl->index, info->var.xres,
+ info->var.yres, info->var.bits_per_pixel);
+
+ for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
+ ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct fb_info *info = ovl->info;
+
+ if (info == NULL || info->device == NULL)
+ return;
+
+ framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
+{
+ struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc;
+ struct fb_var_screeninfo *var;
+ struct fb_info *info;
+
+ /* Allocate and initialize the frame buffer device. */
+ info = framebuffer_alloc(0, priv->dev);
+ if (info == NULL) {
+ dev_err(priv->dev, "unable to allocate fb_info\n");
+ return -ENOMEM;
+ }
+
+ ovl->info = info;
+
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &sh_mobile_lcdc_overlay_ops;
+ info->device = priv->dev;
+ info->screen_base = ovl->fb_mem;
+ info->par = ovl;
+
+ /* Initialize fixed screen information. Restrict pan to 2 lines steps
+ * for NV12 and NV21.
+ */
+ info->fix = sh_mobile_lcdc_overlay_fix;
+ snprintf(info->fix.id, sizeof(info->fix.id),
+ "SH Mobile LCDC Overlay %u", ovl->index);
+ info->fix.smem_start = ovl->dma_handle;
+ info->fix.smem_len = ovl->fb_size;
+ info->fix.line_length = ovl->pitch;
+
+ if (ovl->format->yuv)
+ info->fix.visual = FB_VISUAL_FOURCC;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ switch (ovl->format->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ info->fix.ypanstep = 2;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ info->fix.xpanstep = 2;
+ }
+
+ /* Initialize variable screen information. */
+ var = &info->var;
+ memset(var, 0, sizeof(*var));
+ var->xres = ovl->xres;
+ var->yres = ovl->yres;
+ var->xres_virtual = ovl->xres_virtual;
+ var->yres_virtual = ovl->yres_virtual;
+ var->activate = FB_ACTIVATE_NOW;
+
+ /* Use the legacy API by default for RGB formats, and the FOURCC API
+ * for YUV formats.
+ */
+ if (!ovl->format->yuv)
+ var->bits_per_pixel = ovl->format->bpp;
+ else
+ var->grayscale = ovl->format->fourcc;
+
+ return sh_mobile_lcdc_overlay_check_var(var, info);
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations - main frame buffer
*/
static int sh_mobile_lcdc_setcolreg(u_int regno,
@@ -1003,12 +1772,12 @@ static int sh_mobile_lcdc_setcolreg(u_int regno,
return 0;
}
-static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
+static const struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.id = "SH Mobile LCDC",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE,
- .xpanstep = 0,
+ .xpanstep = 1,
.ypanstep = 1,
.ywrapstep = 0,
.capabilities = FB_CAP_FOURCC,
@@ -1035,78 +1804,74 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
sh_mobile_lcdc_deferred_io_touch(info);
}
-static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info)
+static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *priv = ch->lcdc;
unsigned long ldrcntr;
- unsigned long new_pan_offset;
unsigned long base_addr_y, base_addr_c;
+ unsigned long y_offset;
unsigned long c_offset;
- if (!ch->format->yuv)
- new_pan_offset = var->yoffset * ch->pitch
- + var->xoffset * (ch->format->bpp / 8);
- else
- new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
+ if (!ch->format->yuv) {
+ y_offset = (var->yoffset * ch->xres_virtual + var->xoffset)
+ * ch->format->bpp / 8;
+ c_offset = 0;
+ } else {
+ unsigned int xsub = ch->format->bpp < 24 ? 2 : 1;
+ unsigned int ysub = ch->format->bpp < 16 ? 2 : 1;
- if (new_pan_offset == ch->pan_offset)
- return 0; /* No change, do nothing */
+ y_offset = var->yoffset * ch->xres_virtual + var->xoffset;
+ c_offset = var->yoffset / ysub * ch->xres_virtual * 2 / xsub
+ + var->xoffset * 2 / xsub;
+ }
- ldrcntr = lcdc_read(priv, _LDRCNTR);
+ /* If the Y offset hasn't changed, the C offset hasn't either. There's
+ * nothing to do in that case.
+ */
+ if (y_offset == ch->pan_y_offset)
+ return 0;
/* Set the source address for the next refresh */
- base_addr_y = ch->dma_handle + new_pan_offset;
- if (ch->format->yuv) {
- /* Set y offset */
- c_offset = var->yoffset * ch->pitch
- * (ch->format->bpp - 8) / 8;
- base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
- + c_offset;
- /* Set x offset */
- if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
- base_addr_c += 2 * var->xoffset;
- else
- base_addr_c += var->xoffset;
- }
+ base_addr_y = ch->dma_handle + y_offset;
+ base_addr_c = ch->dma_handle + ch->xres_virtual * ch->yres_virtual
+ + c_offset;
- if (ch->meram) {
- struct sh_mobile_meram_info *mdev;
-
- mdev = priv->meram_dev;
- mdev->ops->meram_update(mdev, ch->meram,
- base_addr_y, base_addr_c,
- &base_addr_y, &base_addr_c);
- }
+ if (ch->cache)
+ sh_mobile_meram_cache_update(priv->meram_dev, ch->cache,
+ base_addr_y, base_addr_c,
+ &base_addr_y, &base_addr_c);
ch->base_addr_y = base_addr_y;
ch->base_addr_c = base_addr_c;
+ ch->pan_y_offset = y_offset;
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
if (ch->format->yuv)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
+ ldrcntr = lcdc_read(priv, _LDRCNTR);
if (lcdc_chan_is_sublcd(ch))
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
else
lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
- ch->pan_offset = new_pan_offset;
sh_mobile_lcdc_deferred_io_touch(info);
return 0;
}
-static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
- unsigned long arg)
+static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
{
+ struct sh_mobile_lcdc_chan *ch = info->par;
int retval;
switch (cmd) {
case FBIO_WAITFORVSYNC:
- retval = sh_mobile_wait_for_vsync(info->par);
+ retval = sh_mobile_lcdc_wait_for_vsync(ch);
break;
default:
@@ -1158,7 +1923,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
* Locking: both .fb_release() and .fb_open() are called with info->lock held if
* user == 1, or with console sem held, if user == 0.
*/
-static int sh_mobile_release(struct fb_info *info, int user)
+static int sh_mobile_lcdc_release(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1179,7 +1944,7 @@ static int sh_mobile_release(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_open(struct fb_info *info, int user)
+static int sh_mobile_lcdc_open(struct fb_info *info, int user)
{
struct sh_mobile_lcdc_chan *ch = info->par;
@@ -1192,7 +1957,8 @@ static int sh_mobile_open(struct fb_info *info, int user)
return 0;
}
-static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
struct sh_mobile_lcdc_priv *p = ch->lcdc;
@@ -1200,9 +1966,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
unsigned int best_xres = 0;
unsigned int best_yres = 0;
unsigned int i;
-
- if (var->xres > MAX_XRES || var->yres > MAX_YRES)
- return -EINVAL;
+ int ret;
/* If board code provides us with a list of available modes, make sure
* we use one of them. Find the mode closest to the requested one. The
@@ -1237,73 +2001,9 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
var->yres = best_yres;
}
- /* Make sure the virtual resolution is at least as big as the visible
- * resolution.
- */
- if (var->xres_virtual < var->xres)
- var->xres_virtual = var->xres;
- if (var->yres_virtual < var->yres)
- var->yres_virtual = var->yres;
-
- if (sh_mobile_format_is_fourcc(var)) {
- const struct sh_mobile_lcdc_format_info *format;
-
- format = sh_mobile_format_info(var->grayscale);
- if (format == NULL)
- return -EINVAL;
- var->bits_per_pixel = format->bpp;
-
- /* Default to RGB and JPEG color-spaces for RGB and YUV formats
- * respectively.
- */
- if (!format->yuv)
- var->colorspace = V4L2_COLORSPACE_SRGB;
- else if (var->colorspace != V4L2_COLORSPACE_REC709)
- var->colorspace = V4L2_COLORSPACE_JPEG;
- } else {
- if (var->bits_per_pixel <= 16) { /* RGB 565 */
- var->bits_per_pixel = 16;
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 24) { /* RGB 888 */
- var->bits_per_pixel = 24;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 0;
- var->transp.length = 0;
- } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */
- var->bits_per_pixel = 32;
- var->red.offset = 16;
- var->red.length = 8;
- var->green.offset = 8;
- var->green.length = 8;
- var->blue.offset = 0;
- var->blue.length = 8;
- var->transp.offset = 24;
- var->transp.length = 8;
- } else
- return -EINVAL;
-
- var->red.msb_right = 0;
- var->green.msb_right = 0;
- var->blue.msb_right = 0;
- var->transp.msb_right = 0;
- }
-
- /* Make sure we don't exceed our allocated memory. */
- if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
- info->fix.smem_len)
- return -EINVAL;
+ ret = __sh_mobile_lcdc_check_var(var, info);
+ if (ret < 0)
+ return ret;
/* only accept the forced_fourcc for dual channel configurations */
if (p->forced_fourcc &&
@@ -1313,7 +2013,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
return 0;
}
-static int sh_mobile_set_par(struct fb_info *info)
+static int sh_mobile_lcdc_set_par(struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
int ret;
@@ -1329,9 +2029,9 @@ static int sh_mobile_set_par(struct fb_info *info)
ch->yres_virtual = info->var.yres_virtual;
if (ch->format->yuv)
- ch->pitch = info->var.xres;
+ ch->pitch = info->var.xres_virtual;
else
- ch->pitch = info->var.xres * ch->format->bpp / 8;
+ ch->pitch = info->var.xres_virtual * ch->format->bpp / 8;
ret = sh_mobile_lcdc_start(ch->lcdc);
if (ret < 0)
@@ -1383,8 +2083,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
* mode will reenable the clocks and update the screen in time,
* so it does not need this. */
if (!info->fbdefio) {
- sh_mobile_wait_for_vsync(ch);
- sh_mobile_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
+ sh_mobile_lcdc_wait_for_vsync(ch);
}
sh_mobile_lcdc_clk_off(p);
}
@@ -1402,12 +2102,12 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_copyarea = sh_mobile_lcdc_copyarea,
.fb_imageblit = sh_mobile_lcdc_imageblit,
.fb_blank = sh_mobile_lcdc_blank,
- .fb_pan_display = sh_mobile_fb_pan_display,
- .fb_ioctl = sh_mobile_ioctl,
- .fb_open = sh_mobile_open,
- .fb_release = sh_mobile_release,
- .fb_check_var = sh_mobile_check_var,
- .fb_set_par = sh_mobile_set_par,
+ .fb_pan_display = sh_mobile_lcdc_pan,
+ .fb_ioctl = sh_mobile_lcdc_ioctl,
+ .fb_open = sh_mobile_lcdc_open,
+ .fb_release = sh_mobile_lcdc_release,
+ .fb_check_var = sh_mobile_lcdc_check_var,
+ .fb_set_par = sh_mobile_lcdc_set_par,
};
static void
@@ -1514,19 +2214,24 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
else
info->fix.visual = FB_VISUAL_TRUECOLOR;
- if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
- ch->format->fourcc == V4L2_PIX_FMT_NV21)
+ switch (ch->format->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
info->fix.ypanstep = 2;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ info->fix.xpanstep = 2;
+ }
/* Initialize variable screen information using the first mode as
- * default. The default Y virtual resolution is twice the panel size to
- * allow for double-buffering.
+ * default.
*/
var = &info->var;
fb_videomode_to_var(var, mode);
var->width = ch->cfg->panel_cfg.width;
var->height = ch->cfg->panel_cfg.height;
- var->yres_virtual = var->yres * 2;
+ var->xres_virtual = ch->xres_virtual;
+ var->yres_virtual = ch->yres_virtual;
var->activate = FB_ACTIVATE_NOW;
/* Use the legacy API by default for RGB formats, and the FOURCC API
@@ -1537,7 +2242,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
else
var->grayscale = ch->format->fourcc;
- ret = sh_mobile_check_var(var, info);
+ ret = sh_mobile_lcdc_check_var(var, info);
if (ret)
return ret;
@@ -1712,15 +2417,27 @@ static const struct fb_videomode default_720p __devinitconst = {
static int sh_mobile_lcdc_remove(struct platform_device *pdev)
{
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- int i;
+ unsigned int i;
fb_unregister_client(&priv->notifier);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+ sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
sh_mobile_lcdc_stop(priv);
+ for (i = 0; i < ARRAY_SIZE(priv->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ sh_mobile_lcdc_overlay_fb_cleanup(ovl);
+
+ if (ovl->fb_mem)
+ dma_free_coherent(&pdev->dev, ovl->fb_size,
+ ovl->fb_mem, ovl->dma_handle);
+ }
+
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
@@ -1737,8 +2454,11 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
}
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
- if (priv->ch[i].bl)
- sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
+ struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
+
+ if (ch->bl)
+ sh_mobile_lcdc_bl_remove(ch->bl);
+ mutex_destroy(&ch->open_lock);
}
if (priv->dot_clk) {
@@ -1796,6 +2516,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
}
static int __devinit
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
+ struct sh_mobile_lcdc_overlay *ovl)
+{
+ const struct sh_mobile_lcdc_format_info *format;
+ int ret;
+
+ if (ovl->cfg->fourcc == 0)
+ return 0;
+
+ /* Validate the format. */
+ format = sh_mobile_format_info(ovl->cfg->fourcc);
+ if (format == NULL) {
+ dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+ return -EINVAL;
+ }
+
+ ovl->enabled = false;
+ ovl->mode = LCDC_OVERLAY_BLEND;
+ ovl->alpha = 255;
+ ovl->rop3 = 0;
+ ovl->pos_x = 0;
+ ovl->pos_y = 0;
+
+ /* The default Y virtual resolution is twice the panel size to allow for
+ * double-buffering.
+ */
+ ovl->format = format;
+ ovl->xres = ovl->cfg->max_xres;
+ ovl->xres_virtual = ovl->xres;
+ ovl->yres = ovl->cfg->max_yres;
+ ovl->yres_virtual = ovl->yres * 2;
+
+ if (!format->yuv)
+ ovl->pitch = ovl->xres_virtual * format->bpp / 8;
+ else
+ ovl->pitch = ovl->xres_virtual;
+
+ /* Allocate frame buffer memory. */
+ ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
+ * format->bpp / 8 * 2;
+ ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
+ &ovl->dma_handle, GFP_KERNEL);
+ if (!ovl->fb_mem) {
+ dev_err(priv->dev, "unable to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = sh_mobile_lcdc_overlay_fb_init(ovl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int __devinit
sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
struct sh_mobile_lcdc_chan *ch)
{
@@ -1854,7 +2629,9 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
num_modes = cfg->num_modes;
}
- /* Use the first mode as default. */
+ /* Use the first mode as default. The default Y virtual resolution is
+ * twice the panel size to allow for double-buffering.
+ */
ch->format = format;
ch->xres = mode->xres;
ch->xres_virtual = mode->xres;
@@ -1863,10 +2640,10 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
if (!format->yuv) {
ch->colorspace = V4L2_COLORSPACE_SRGB;
- ch->pitch = ch->xres * format->bpp / 8;
+ ch->pitch = ch->xres_virtual * format->bpp / 8;
} else {
ch->colorspace = V4L2_COLORSPACE_REC709;
- ch->pitch = ch->xres;
+ ch->pitch = ch->xres_virtual;
}
ch->display.width = cfg->panel_cfg.width;
@@ -1952,7 +2729,6 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
}
init_waitqueue_head(&ch->frame_end_wait);
init_completion(&ch->vsync_completion);
- ch->pan_offset = 0;
/* probe the backlight is there is one defined */
if (ch->cfg->bl_info.max_brightness)
@@ -2003,6 +2779,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ ovl->cfg = &pdata->overlays[i];
+ ovl->channel = &priv->ch[0];
+
+ error = sh_mobile_lcdc_overlay_init(priv, ovl);
+ if (error)
+ goto err1;
+ }
+
error = sh_mobile_lcdc_start(priv);
if (error) {
dev_err(&pdev->dev, "unable to start hardware\n");
@@ -2017,6 +2804,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
+ for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+ struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+ error = sh_mobile_lcdc_overlay_fb_register(ovl);
+ if (error)
+ goto err1;
+ }
+
/* Failure ignored */
priv->notifier.notifier_call = sh_mobile_lcdc_notify;
fb_register_client(&priv->notifier);
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 5c3bddd2cb72..0f92f6544b94 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -47,6 +47,7 @@ struct sh_mobile_lcdc_entity {
/*
* struct sh_mobile_lcdc_chan - LCDC display channel
*
+ * @pan_y_offset: Panning linear offset in bytes (luma component)
* @base_addr_y: Frame buffer viewport base address (luma component)
* @base_addr_c: Frame buffer viewport base address (chroma component)
* @pitch: Frame buffer line pitch
@@ -59,7 +60,7 @@ struct sh_mobile_lcdc_chan {
unsigned long *reg_offs;
unsigned long ldmt1r_value;
unsigned long enabled; /* ME and SE in LDCNT2R */
- void *meram;
+ void *cache;
struct mutex open_lock; /* protects the use counter */
int use_count;
@@ -68,7 +69,7 @@ struct sh_mobile_lcdc_chan {
unsigned long fb_size;
dma_addr_t dma_handle;
- unsigned long pan_offset;
+ unsigned long pan_y_offset;
unsigned long frame_end;
wait_queue_head_t frame_end_wait;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 82ba830bf95d..7a0ba8bb3fbe 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -11,6 +11,7 @@
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/export.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -194,13 +195,28 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
}
/* -----------------------------------------------------------------------------
- * Allocation
+ * MERAM allocation and free
+ */
+
+static unsigned long meram_alloc(struct sh_mobile_meram_priv *priv, size_t size)
+{
+ return gen_pool_alloc(priv->pool, size);
+}
+
+static void meram_free(struct sh_mobile_meram_priv *priv, unsigned long mem,
+ size_t size)
+{
+ gen_pool_free(priv->pool, mem, size);
+}
+
+/* -----------------------------------------------------------------------------
+ * LCDC cache planes allocation, init, cleanup and free
*/
/* Allocate ICBs and MERAM for a plane. */
-static int __meram_alloc(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- size_t size)
+static int meram_plane_alloc(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ size_t size)
{
unsigned long mem;
unsigned long idx;
@@ -215,7 +231,7 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
return -ENOMEM;
plane->marker = &priv->icbs[idx];
- mem = gen_pool_alloc(priv->pool, size * 1024);
+ mem = meram_alloc(priv, size * 1024);
if (mem == 0)
return -ENOMEM;
@@ -229,11 +245,11 @@ static int __meram_alloc(struct sh_mobile_meram_priv *priv,
}
/* Free ICBs and MERAM for a plane. */
-static void __meram_free(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
{
- gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
- plane->marker->size * 1024);
+ meram_free(priv, priv->meram + plane->marker->offset,
+ plane->marker->size * 1024);
__clear_bit(plane->marker->index, &priv->used_icb);
__clear_bit(plane->cache->index, &priv->used_icb);
@@ -248,62 +264,6 @@ static int is_nvcolor(int cspace)
return 0;
}
-/* Allocate memory for the ICBs and mark them as used. */
-static struct sh_mobile_meram_fb_cache *
-meram_alloc(struct sh_mobile_meram_priv *priv,
- const struct sh_mobile_meram_cfg *cfg,
- int pixelformat)
-{
- struct sh_mobile_meram_fb_cache *cache;
- unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
- int ret;
-
- if (cfg->icb[0].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- if (nplanes == 2 && cfg->icb[1].meram_size == 0)
- return ERR_PTR(-EINVAL);
-
- cache = kzalloc(sizeof(*cache), GFP_KERNEL);
- if (cache == NULL)
- return ERR_PTR(-ENOMEM);
-
- cache->nplanes = nplanes;
-
- ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
- if (ret < 0)
- goto error;
-
- cache->planes[0].marker->current_reg = 1;
- cache->planes[0].marker->pixelformat = pixelformat;
-
- if (cache->nplanes == 1)
- return cache;
-
- ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
- if (ret < 0) {
- __meram_free(priv, &cache->planes[0]);
- goto error;
- }
-
- return cache;
-
-error:
- kfree(cache);
- return ERR_PTR(-ENOMEM);
-}
-
-/* Unmark the specified ICB as used. */
-static void meram_free(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_cache *cache)
-{
- __meram_free(priv, &cache->planes[0]);
- if (cache->nplanes == 2)
- __meram_free(priv, &cache->planes[1]);
-
- kfree(cache);
-}
-
/* Set the next address to fetch. */
static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
struct sh_mobile_meram_fb_cache *cache,
@@ -355,10 +315,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
/* Initialize MERAM. */
-static int meram_init(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane,
- unsigned int xres, unsigned int yres,
- unsigned int *out_pitch)
+static int meram_plane_init(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ unsigned int xres, unsigned int yres,
+ unsigned int *out_pitch)
{
struct sh_mobile_meram_icb *marker = plane->marker;
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
@@ -427,8 +387,8 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
return 0;
}
-static void meram_deinit(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_fb_plane *plane)
+static void meram_plane_cleanup(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
{
/* disable ICB */
meram_write_icb(priv->base, plane->cache->index, MExxCTL,
@@ -441,20 +401,82 @@ static void meram_deinit(struct sh_mobile_meram_priv *priv,
}
/* -----------------------------------------------------------------------------
- * Registration/unregistration
+ * MERAM operations
*/
-static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned int *pitch)
+unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *pdata,
+ size_t size)
+{
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+
+ return meram_alloc(priv, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_alloc);
+
+void sh_mobile_meram_free(struct sh_mobile_meram_info *pdata, unsigned long mem,
+ size_t size)
+{
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+
+ meram_free(priv, mem, size);
+}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_free);
+
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_cache_alloc(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_cfg *cfg,
+ int pixelformat)
+{
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+ struct sh_mobile_meram_fb_cache *cache;
+ int ret;
+
+ cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+ if (cache == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ cache->nplanes = nplanes;
+
+ ret = meram_plane_alloc(priv, &cache->planes[0],
+ cfg->icb[0].meram_size);
+ if (ret < 0)
+ goto error;
+
+ cache->planes[0].marker->current_reg = 1;
+ cache->planes[0].marker->pixelformat = pixelformat;
+
+ if (cache->nplanes == 1)
+ return cache;
+
+ ret = meram_plane_alloc(priv, &cache->planes[1],
+ cfg->icb[1].meram_size);
+ if (ret < 0) {
+ meram_plane_free(priv, &cache->planes[0]);
+ goto error;
+ }
+
+ return cache;
+
+error:
+ kfree(cache);
+ return ERR_PTR(-ENOMEM);
+}
+
+void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *pdata,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat, unsigned int *pitch)
{
struct sh_mobile_meram_fb_cache *cache;
struct sh_mobile_meram_priv *priv = pdata->priv;
struct platform_device *pdev = pdata->pdev;
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
unsigned int out_pitch;
+ if (priv == NULL)
+ return ERR_PTR(-ENODEV);
+
if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
pixelformat != SH_MOBILE_MERAM_PF_RGB)
@@ -469,10 +491,16 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
return ERR_PTR(-EINVAL);
}
+ if (cfg->icb[0].meram_size == 0)
+ return ERR_PTR(-EINVAL);
+
+ if (nplanes == 2 && cfg->icb[1].meram_size == 0)
+ return ERR_PTR(-EINVAL);
+
mutex_lock(&priv->lock);
/* We now register the ICBs and allocate the MERAM regions. */
- cache = meram_alloc(priv, cfg, pixelformat);
+ cache = meram_cache_alloc(priv, cfg, pixelformat);
if (IS_ERR(cache)) {
dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
PTR_ERR(cache));
@@ -480,42 +508,50 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
}
/* initialize MERAM */
- meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
+ meram_plane_init(priv, &cache->planes[0], xres, yres, &out_pitch);
*pitch = out_pitch;
if (pixelformat == SH_MOBILE_MERAM_PF_NV)
- meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
- &out_pitch);
+ meram_plane_init(priv, &cache->planes[1],
+ xres, (yres + 1) / 2, &out_pitch);
else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
- meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
- &out_pitch);
+ meram_plane_init(priv, &cache->planes[1],
+ 2 * xres, (yres + 1) / 2, &out_pitch);
err:
mutex_unlock(&priv->lock);
return cache;
}
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_alloc);
-static void
-sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
+void
+sh_mobile_meram_cache_free(struct sh_mobile_meram_info *pdata, void *data)
{
struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
- /* deinit & free */
- meram_deinit(priv, &cache->planes[0]);
- if (cache->nplanes == 2)
- meram_deinit(priv, &cache->planes[1]);
+ /* Cleanup and free. */
+ meram_plane_cleanup(priv, &cache->planes[0]);
+ meram_plane_free(priv, &cache->planes[0]);
+
+ if (cache->nplanes == 2) {
+ meram_plane_cleanup(priv, &cache->planes[1]);
+ meram_plane_free(priv, &cache->planes[1]);
+ }
- meram_free(priv, cache);
+ kfree(cache);
mutex_unlock(&priv->lock);
}
-
-static void
-sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
- unsigned long base_addr_y, unsigned long base_addr_c,
- unsigned long *icb_addr_y, unsigned long *icb_addr_c)
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_free);
+
+void
+sh_mobile_meram_cache_update(struct sh_mobile_meram_info *pdata, void *data,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c)
{
struct sh_mobile_meram_fb_cache *cache = data;
struct sh_mobile_meram_priv *priv = pdata->priv;
@@ -527,13 +563,7 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
mutex_unlock(&priv->lock);
}
-
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
- .module = THIS_MODULE,
- .meram_register = sh_mobile_meram_register,
- .meram_unregister = sh_mobile_meram_unregister,
- .meram_update = sh_mobile_meram_update,
-};
+EXPORT_SYMBOL_GPL(sh_mobile_meram_cache_update);
/* -----------------------------------------------------------------------------
* Power management
@@ -624,7 +654,6 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
for (i = 0; i < MERAM_ICB_NUM; ++i)
priv->icbs[i].index = i;
- pdata->ops = &sh_mobile_meram_ops;
pdata->priv = priv;
pdata->pdev = pdev;
diff --git a/drivers/video/smscufx.c b/drivers/video/smscufx.c
index 26f864289498..5533a32c6ca1 100644
--- a/drivers/video/smscufx.c
+++ b/drivers/video/smscufx.c
@@ -904,7 +904,7 @@ static ssize_t ufx_ops_write(struct fb_info *info, const char __user *buf,
result = fb_sys_write(info, buf, count, ppos);
if (result > 0) {
- int start = max((int)(offset / info->fix.line_length) - 1, 0);
+ int start = max((int)(offset / info->fix.line_length), 0);
int lines = min((u32)((result / info->fix.line_length) + 1),
(u32)info->var.yres);
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index 90a2e30272ad..2f6b2b835f88 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -1567,6 +1567,18 @@ static void w100_suspend(u32 mode)
val = readl(remapped_regs + mmPLL_CNTL);
val |= 0x00000004; /* bit2=1 */
writel(val, remapped_regs + mmPLL_CNTL);
+
+ writel(0x00000000, remapped_regs + mmLCDD_CNTL1);
+ writel(0x00000000, remapped_regs + mmLCDD_CNTL2);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL1);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL2);
+ writel(0x00000000, remapped_regs + mmGENLCD_CNTL3);
+
+ val = readl(remapped_regs + mmMEM_EXT_CNTL);
+ val |= 0xF0000000;
+ val &= ~(0x00000001);
+ writel(val, remapped_regs + mmMEM_EXT_CNTL);
+
writel(0x0000001d, remapped_regs + mmPWRMGT_CNTL);
}
}
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index d90062b211f8..92d08e7fcba2 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -91,6 +91,11 @@ static struct w1_family w1_therm_family_DS28EA00 = {
.fops = &w1_therm_fops,
};
+static struct w1_family w1_therm_family_DS1825 = {
+ .fid = W1_THERM_DS1825,
+ .fops = &w1_therm_fops,
+};
+
struct w1_therm_family_converter
{
u8 broken;
@@ -120,6 +125,10 @@ static struct w1_therm_family_converter w1_therm_families[] = {
.f = &w1_therm_family_DS28EA00,
.convert = w1_DS18B20_convert_temp
},
+ {
+ .f = &w1_therm_family_DS1825,
+ .convert = w1_DS18B20_convert_temp
+ }
};
static inline int w1_DS18B20_convert_temp(u8 rom[9])
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index b00ada44a89b..a1f0ce151d53 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -39,6 +39,7 @@
#define W1_EEPROM_DS2431 0x2D
#define W1_FAMILY_DS2760 0x30
#define W1_FAMILY_DS2780 0x32
+#define W1_THERM_DS1825 0x3B
#define W1_FAMILY_DS2781 0x3D
#define W1_THERM_DS28EA00 0x42
diff --git a/drivers/watchdog/booke_wdt.c b/drivers/watchdog/booke_wdt.c
index 3fe82d0e8caa..5b06d31ab6a9 100644
--- a/drivers/watchdog/booke_wdt.c
+++ b/drivers/watchdog/booke_wdt.c
@@ -166,18 +166,17 @@ static long booke_wdt_ioctl(struct file *file,
switch (cmd) {
case WDIOC_GETSUPPORT:
- if (copy_to_user((void *)arg, &ident, sizeof(ident)))
- return -EFAULT;
+ return copy_to_user(p, &ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
return put_user(0, p);
case WDIOC_GETBOOTSTATUS:
/* XXX: something is clearing TSR */
tmp = mfspr(SPRN_TSR) & TSR_WRS(3);
/* returns CARDRESET if last reset was caused by the WDT */
- return (tmp ? WDIOF_CARDRESET : 0);
+ return put_user((tmp ? WDIOF_CARDRESET : 0), p);
case WDIOC_SETOPTIONS:
if (get_user(tmp, p))
- return -EINVAL;
+ return -EFAULT;
if (tmp == WDIOS_ENABLECARD) {
booke_wdt_ping();
break;
diff --git a/drivers/watchdog/da9052_wdt.c b/drivers/watchdog/da9052_wdt.c
index 3f75129eb0a9..f7abbaeebcaf 100644
--- a/drivers/watchdog/da9052_wdt.c
+++ b/drivers/watchdog/da9052_wdt.c
@@ -21,7 +21,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
-#include <linux/delay.h>
#include <linux/mfd/da9052/reg.h>
#include <linux/mfd/da9052/da9052.h>
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 1eff743ec497..ae60406ea8a1 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -814,6 +814,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
+ /* Make sure that timer is disabled until /dev/watchdog is opened */
+ hpwdt_stop();
+
/* Make sure that we have a valid soft_margin */
if (hpwdt_change_timer(soft_margin))
hpwdt_change_timer(DEFAULT_MARGIN);
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
index a73bea4aa1ba..c20f96b579d9 100644
--- a/drivers/watchdog/orion_wdt.c
+++ b/drivers/watchdog/orion_wdt.c
@@ -24,6 +24,7 @@
#include <linux/spinlock.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <mach/bridge-regs.h>
/*
@@ -192,6 +193,12 @@ static void orion_wdt_shutdown(struct platform_device *pdev)
orion_wdt_stop(&orion_wdt);
}
+static const struct of_device_id orion_wdt_of_match_table[] __devinitdata = {
+ { .compatible = "marvell,orion-wdt", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
+
static struct platform_driver orion_wdt_driver = {
.probe = orion_wdt_probe,
.remove = __devexit_p(orion_wdt_remove),
@@ -199,6 +206,7 @@ static struct platform_driver orion_wdt_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "orion_wdt",
+ .of_match_table = of_match_ptr(orion_wdt_of_match_table),
},
};
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 6aa46a90ff02..3796434991fa 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -128,11 +128,12 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
void watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
- int devno = wdd->cdev.dev;
+ int devno;
if (wdd == NULL)
return;
+ devno = wdd->cdev.dev;
ret = watchdog_dev_unregister(wdd);
if (ret)
pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1ffd03bf8e10..7f1241608489 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -314,8 +314,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
}
}
- err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
- pages, true);
+ err = gnttab_unmap_refs(map->unmap_ops + offset,
+ use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset,
+ pages);
if (err)
return err;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 0bfc1ef11259..006726688baf 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -870,7 +870,8 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
EXPORT_SYMBOL_GPL(gnttab_map_refs);
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
- struct page **pages, unsigned int count, bool clear_pte)
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
{
int i, ret;
bool lazy = false;
@@ -888,7 +889,8 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
}
for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i], clear_pte);
+ ret = m2p_remove_override(pages[i], kmap_ops ?
+ &kmap_ops[i] : NULL);
if (ret)
return ret;
}
diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c
index d4c50d63acbc..97ca359ae2bd 100644
--- a/drivers/xen/platform-pci.c
+++ b/drivers/xen/platform-pci.c
@@ -101,19 +101,6 @@ static int platform_pci_resume(struct pci_dev *pdev)
return 0;
}
-static void __devinit prepare_shared_info(void)
-{
-#ifdef CONFIG_KEXEC
- unsigned long addr;
- struct shared_info *hvm_shared_info;
-
- addr = alloc_xen_mmio(PAGE_SIZE);
- hvm_shared_info = ioremap(addr, PAGE_SIZE);
- memset(hvm_shared_info, 0, PAGE_SIZE);
- xen_hvm_prepare_kexec(hvm_shared_info, addr >> PAGE_SHIFT);
-#endif
-}
-
static int __devinit platform_pci_init(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -151,8 +138,6 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
platform_mmio = mmio_addr;
platform_mmiolen = mmio_len;
- prepare_shared_info();
-
if (!xen_have_vector_callback) {
ret = xen_allocate_irq(pdev);
if (ret) {
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 1afb4fba11b4..4d519488d304 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -232,7 +232,7 @@ xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
return ret;
if (hwdev && hwdev->coherent_dma_mask)
- dma_mask = hwdev->coherent_dma_mask;
+ dma_mask = dma_alloc_coherent_mask(hwdev, flags);
phys = virt_to_phys(ret);
dev_addr = xen_phys_to_bus(phys);
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 097e536e8672..03342728bf23 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -353,16 +353,16 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
if (err)
goto config_release;
- dev_dbg(&dev->dev, "reseting (FLR, D3, etc) the device\n");
- __pci_reset_function_locked(dev);
-
/* We need the device active to save the state. */
dev_dbg(&dev->dev, "save state of device\n");
pci_save_state(dev);
dev_data->pci_saved_state = pci_store_saved_state(dev);
if (!dev_data->pci_saved_state)
dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
-
+ else {
+ dev_dbg(&dev->dev, "reseting (FLR, D3, etc) the device\n");
+ __pci_reset_function_locked(dev);
+ }
/* Now disable the device (this also ensures some private device
* data is setup before we export)
*/
diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
index 181fa8158a8b..858c9714b2f3 100644
--- a/drivers/zorro/zorro.c
+++ b/drivers/zorro/zorro.c
@@ -37,7 +37,6 @@ struct zorro_dev zorro_autocon[ZORRO_NUM_AUTO];
*/
struct zorro_bus {
- struct list_head devices; /* list of devices on this bus */
struct device dev;
};
@@ -136,7 +135,6 @@ static int __init amiga_zorro_probe(struct platform_device *pdev)
if (!bus)
return -ENOMEM;
- INIT_LIST_HEAD(&bus->devices);
bus->dev.parent = &pdev->dev;
dev_set_name(&bus->dev, "zorro");
error = device_register(&bus->dev);
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c
index fc06fd27065e..dd6f7ee1e312 100644
--- a/fs/9p/vfs_file.c
+++ b/fs/9p/vfs_file.c
@@ -610,6 +610,9 @@ v9fs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
p9_debug(P9_DEBUG_VFS, "page %p fid %lx\n",
page, (unsigned long)filp->private_data);
+ /* Update file times before taking page lock */
+ file_update_time(filp);
+
v9inode = V9FS_I(inode);
/* make sure the cache has finished storing the page */
v9fs_fscache_wait_on_page_write(inode, page);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 1feb68ecef95..842d00048a65 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -94,25 +94,21 @@ static struct dentry *get_next_positive_subdir(struct dentry *prev,
{
struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb);
struct list_head *next;
- struct dentry *p, *q;
+ struct dentry *q;
spin_lock(&sbi->lookup_lock);
+ spin_lock(&root->d_lock);
- if (prev == NULL) {
- spin_lock(&root->d_lock);
+ if (prev)
+ next = prev->d_u.d_child.next;
+ else {
prev = dget_dlock(root);
next = prev->d_subdirs.next;
- p = prev;
- goto start;
}
- p = prev;
- spin_lock(&p->d_lock);
-again:
- next = p->d_u.d_child.next;
-start:
+cont:
if (next == &root->d_subdirs) {
- spin_unlock(&p->d_lock);
+ spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev);
return NULL;
@@ -121,16 +117,15 @@ start:
q = list_entry(next, struct dentry, d_u.d_child);
spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
- /* Negative dentry - try next */
- if (!simple_positive(q)) {
- spin_unlock(&p->d_lock);
- lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_);
- p = q;
- goto again;
+ /* Already gone or negative dentry (under construction) - try next */
+ if (q->d_count == 0 || !simple_positive(q)) {
+ spin_unlock(&q->d_lock);
+ next = q->d_u.d_child.next;
+ goto cont;
}
dget_dlock(q);
spin_unlock(&q->d_lock);
- spin_unlock(&p->d_lock);
+ spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev);
@@ -404,11 +399,6 @@ struct dentry *autofs4_expire_indirect(struct super_block *sb,
DPRINTK("checking mountpoint %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
- /* Path walk currently on this dentry? */
- ino_count = atomic_read(&ino->count) + 2;
- if (dentry->d_count > ino_count)
- goto next;
-
/* Can we umount this guy */
if (autofs4_mount_busy(mnt, dentry))
goto next;
diff --git a/fs/bio.c b/fs/bio.c
index 73922abba832..71072ab99128 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -73,7 +73,7 @@ static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)
{
unsigned int sz = sizeof(struct bio) + extra_size;
struct kmem_cache *slab = NULL;
- struct bio_slab *bslab;
+ struct bio_slab *bslab, *new_bio_slabs;
unsigned int i, entry = -1;
mutex_lock(&bio_slab_lock);
@@ -97,11 +97,12 @@ static struct kmem_cache *bio_find_or_create_slab(unsigned int extra_size)
if (bio_slab_nr == bio_slab_max && entry == -1) {
bio_slab_max <<= 1;
- bio_slabs = krealloc(bio_slabs,
- bio_slab_max * sizeof(struct bio_slab),
- GFP_KERNEL);
- if (!bio_slabs)
+ new_bio_slabs = krealloc(bio_slabs,
+ bio_slab_max * sizeof(struct bio_slab),
+ GFP_KERNEL);
+ if (!new_bio_slabs)
goto out_unlock;
+ bio_slabs = new_bio_slabs;
}
if (entry == -1)
entry = bio_slab_nr++;
@@ -1312,7 +1313,7 @@ EXPORT_SYMBOL(bio_copy_kern);
* Note that this code is very hard to test under normal circumstances because
* direct-io pins the pages with get_user_pages(). This makes
* is_page_cache_freeable return false, and the VM will not clean the pages.
- * But other code (eg, pdflush) could clean the pages if they are mapped
+ * But other code (eg, flusher threads) could clean the pages if they are mapped
* pagecache.
*
* Simply disabling the call to bio_set_pages_dirty() is a good way to test the
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1e519195d45b..38e721b35d45 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1578,10 +1578,12 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct file *file = iocb->ki_filp;
+ struct blk_plug plug;
ssize_t ret;
BUG_ON(iocb->ki_pos != pos);
+ blk_start_plug(&plug);
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
if (ret > 0 || ret == -EIOCBQUEUED) {
ssize_t err;
@@ -1590,6 +1592,7 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (err < 0 && ret > 0)
ret = err;
}
+ blk_finish_plug(&plug);
return ret;
}
EXPORT_SYMBOL_GPL(blkdev_aio_write);
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index a256f3b2a845..ff6475f409d6 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -1438,10 +1438,10 @@ int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
ret = extent_from_logical(fs_info, logical, path,
&found_key);
btrfs_release_path(path);
- if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK)
- ret = -EINVAL;
if (ret < 0)
return ret;
+ if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK)
+ return -EINVAL;
extent_item_pos = logical - found_key.objectid;
ret = iterate_extent_inodes(fs_info, found_key.objectid,
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 86eff48dab78..43d1c5a3a030 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -818,6 +818,7 @@ static void free_workspace(int type, struct list_head *workspace)
btrfs_compress_op[idx]->free_workspace(workspace);
atomic_dec(alloc_workspace);
wake:
+ smp_mb();
if (waitqueue_active(workspace_wait))
wake_up(workspace_wait);
}
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 9d7621f271ff..6d183f60d63a 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -421,12 +421,6 @@ void btrfs_put_tree_mod_seq(struct btrfs_fs_info *fs_info,
spin_unlock(&fs_info->tree_mod_seq_lock);
/*
- * we removed the lowest blocker from the blocker list, so there may be
- * more processible delayed refs.
- */
- wake_up(&fs_info->tree_mod_seq_wait);
-
- /*
* anything that's lower than the lowest existing (read: blocked)
* sequence number can be removed from the tree.
*/
@@ -631,6 +625,9 @@ __tree_mod_log_free_eb(struct btrfs_fs_info *fs_info, struct extent_buffer *eb)
u32 nritems;
int ret;
+ if (btrfs_header_level(eb) == 0)
+ return;
+
nritems = btrfs_header_nritems(eb);
for (i = nritems - 1; i >= 0; i--) {
ret = tree_mod_log_insert_key_locked(fs_info, eb, i,
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 4bab807227ad..0d195b507660 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1252,7 +1252,6 @@ struct btrfs_fs_info {
atomic_t tree_mod_seq;
struct list_head tree_mod_seq_list;
struct seq_list tree_mod_seq_elem;
- wait_queue_head_t tree_mod_seq_wait;
/* this protects tree_mod_log */
rwlock_t tree_mod_log_lock;
@@ -3192,7 +3191,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
int btrfs_lookup_bio_sums(struct btrfs_root *root, struct inode *inode,
struct bio *bio, u32 *dst);
int btrfs_lookup_bio_sums_dio(struct btrfs_root *root, struct inode *inode,
- struct bio *bio, u64 logical_offset, u32 *dst);
+ struct bio *bio, u64 logical_offset);
int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 objectid, u64 pos,
diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c
index 335605c8ceab..07d5eeb1e6f1 100644
--- a/fs/btrfs/delayed-inode.c
+++ b/fs/btrfs/delayed-inode.c
@@ -512,8 +512,8 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item)
rb_erase(&delayed_item->rb_node, root);
delayed_item->delayed_node->count--;
- atomic_dec(&delayed_root->items);
- if (atomic_read(&delayed_root->items) < BTRFS_DELAYED_BACKGROUND &&
+ if (atomic_dec_return(&delayed_root->items) <
+ BTRFS_DELAYED_BACKGROUND &&
waitqueue_active(&delayed_root->wait))
wake_up(&delayed_root->wait);
}
@@ -1028,9 +1028,10 @@ do_again:
btrfs_release_delayed_item(prev);
ret = 0;
btrfs_release_path(path);
- if (curr)
+ if (curr) {
+ mutex_unlock(&node->mutex);
goto do_again;
- else
+ } else
goto delete_fail;
}
@@ -1055,8 +1056,7 @@ static void btrfs_release_delayed_inode(struct btrfs_delayed_node *delayed_node)
delayed_node->count--;
delayed_root = delayed_node->root->fs_info->delayed_root;
- atomic_dec(&delayed_root->items);
- if (atomic_read(&delayed_root->items) <
+ if (atomic_dec_return(&delayed_root->items) <
BTRFS_DELAYED_BACKGROUND &&
waitqueue_active(&delayed_root->wait))
wake_up(&delayed_root->wait);
diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c
index da7419ed01bb..ae9411773397 100644
--- a/fs/btrfs/delayed-ref.c
+++ b/fs/btrfs/delayed-ref.c
@@ -38,17 +38,14 @@
static int comp_tree_refs(struct btrfs_delayed_tree_ref *ref2,
struct btrfs_delayed_tree_ref *ref1)
{
- if (ref1->node.type == BTRFS_TREE_BLOCK_REF_KEY) {
- if (ref1->root < ref2->root)
- return -1;
- if (ref1->root > ref2->root)
- return 1;
- } else {
- if (ref1->parent < ref2->parent)
- return -1;
- if (ref1->parent > ref2->parent)
- return 1;
- }
+ if (ref1->root < ref2->root)
+ return -1;
+ if (ref1->root > ref2->root)
+ return 1;
+ if (ref1->parent < ref2->parent)
+ return -1;
+ if (ref1->parent > ref2->parent)
+ return 1;
return 0;
}
@@ -85,7 +82,8 @@ static int comp_data_refs(struct btrfs_delayed_data_ref *ref2,
* type of the delayed backrefs and content of delayed backrefs.
*/
static int comp_entry(struct btrfs_delayed_ref_node *ref2,
- struct btrfs_delayed_ref_node *ref1)
+ struct btrfs_delayed_ref_node *ref1,
+ bool compare_seq)
{
if (ref1->bytenr < ref2->bytenr)
return -1;
@@ -102,10 +100,12 @@ static int comp_entry(struct btrfs_delayed_ref_node *ref2,
if (ref1->type > ref2->type)
return 1;
/* merging of sequenced refs is not allowed */
- if (ref1->seq < ref2->seq)
- return -1;
- if (ref1->seq > ref2->seq)
- return 1;
+ if (compare_seq) {
+ if (ref1->seq < ref2->seq)
+ return -1;
+ if (ref1->seq > ref2->seq)
+ return 1;
+ }
if (ref1->type == BTRFS_TREE_BLOCK_REF_KEY ||
ref1->type == BTRFS_SHARED_BLOCK_REF_KEY) {
return comp_tree_refs(btrfs_delayed_node_to_tree_ref(ref2),
@@ -139,7 +139,7 @@ static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
entry = rb_entry(parent_node, struct btrfs_delayed_ref_node,
rb_node);
- cmp = comp_entry(entry, ins);
+ cmp = comp_entry(entry, ins, 1);
if (cmp < 0)
p = &(*p)->rb_left;
else if (cmp > 0)
@@ -233,6 +233,114 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans,
return 0;
}
+static void inline drop_delayed_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_delayed_ref_root *delayed_refs,
+ struct btrfs_delayed_ref_node *ref)
+{
+ rb_erase(&ref->rb_node, &delayed_refs->root);
+ ref->in_tree = 0;
+ btrfs_put_delayed_ref(ref);
+ delayed_refs->num_entries--;
+ if (trans->delayed_ref_updates)
+ trans->delayed_ref_updates--;
+}
+
+static int merge_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_delayed_ref_root *delayed_refs,
+ struct btrfs_delayed_ref_node *ref, u64 seq)
+{
+ struct rb_node *node;
+ int merged = 0;
+ int mod = 0;
+ int done = 0;
+
+ node = rb_prev(&ref->rb_node);
+ while (node) {
+ struct btrfs_delayed_ref_node *next;
+
+ next = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
+ node = rb_prev(node);
+ if (next->bytenr != ref->bytenr)
+ break;
+ if (seq && next->seq >= seq)
+ break;
+ if (comp_entry(ref, next, 0))
+ continue;
+
+ if (ref->action == next->action) {
+ mod = next->ref_mod;
+ } else {
+ if (ref->ref_mod < next->ref_mod) {
+ struct btrfs_delayed_ref_node *tmp;
+
+ tmp = ref;
+ ref = next;
+ next = tmp;
+ done = 1;
+ }
+ mod = -next->ref_mod;
+ }
+
+ merged++;
+ drop_delayed_ref(trans, delayed_refs, next);
+ ref->ref_mod += mod;
+ if (ref->ref_mod == 0) {
+ drop_delayed_ref(trans, delayed_refs, ref);
+ break;
+ } else {
+ /*
+ * You can't have multiples of the same ref on a tree
+ * block.
+ */
+ WARN_ON(ref->type == BTRFS_TREE_BLOCK_REF_KEY ||
+ ref->type == BTRFS_SHARED_BLOCK_REF_KEY);
+ }
+
+ if (done)
+ break;
+ node = rb_prev(&ref->rb_node);
+ }
+
+ return merged;
+}
+
+void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_delayed_ref_root *delayed_refs,
+ struct btrfs_delayed_ref_head *head)
+{
+ struct rb_node *node;
+ u64 seq = 0;
+
+ spin_lock(&fs_info->tree_mod_seq_lock);
+ if (!list_empty(&fs_info->tree_mod_seq_list)) {
+ struct seq_list *elem;
+
+ elem = list_first_entry(&fs_info->tree_mod_seq_list,
+ struct seq_list, list);
+ seq = elem->seq;
+ }
+ spin_unlock(&fs_info->tree_mod_seq_lock);
+
+ node = rb_prev(&head->node.rb_node);
+ while (node) {
+ struct btrfs_delayed_ref_node *ref;
+
+ ref = rb_entry(node, struct btrfs_delayed_ref_node,
+ rb_node);
+ if (ref->bytenr != head->node.bytenr)
+ break;
+
+ /* We can't merge refs that are outside of our seq count */
+ if (seq && ref->seq >= seq)
+ break;
+ if (merge_ref(trans, delayed_refs, ref, seq))
+ node = rb_prev(&head->node.rb_node);
+ else
+ node = rb_prev(node);
+ }
+}
+
int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info,
struct btrfs_delayed_ref_root *delayed_refs,
u64 seq)
@@ -336,18 +444,11 @@ update_existing_ref(struct btrfs_trans_handle *trans,
* every changing the extent allocation tree.
*/
existing->ref_mod--;
- if (existing->ref_mod == 0) {
- rb_erase(&existing->rb_node,
- &delayed_refs->root);
- existing->in_tree = 0;
- btrfs_put_delayed_ref(existing);
- delayed_refs->num_entries--;
- if (trans->delayed_ref_updates)
- trans->delayed_ref_updates--;
- } else {
+ if (existing->ref_mod == 0)
+ drop_delayed_ref(trans, delayed_refs, existing);
+ else
WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
- }
} else {
WARN_ON(existing->type == BTRFS_TREE_BLOCK_REF_KEY ||
existing->type == BTRFS_SHARED_BLOCK_REF_KEY);
@@ -662,9 +763,6 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info,
add_delayed_tree_ref(fs_info, trans, &ref->node, bytenr,
num_bytes, parent, ref_root, level, action,
for_cow);
- if (!need_ref_seq(for_cow, ref_root) &&
- waitqueue_active(&fs_info->tree_mod_seq_wait))
- wake_up(&fs_info->tree_mod_seq_wait);
spin_unlock(&delayed_refs->lock);
if (need_ref_seq(for_cow, ref_root))
btrfs_qgroup_record_ref(trans, &ref->node, extent_op);
@@ -713,9 +811,6 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info,
add_delayed_data_ref(fs_info, trans, &ref->node, bytenr,
num_bytes, parent, ref_root, owner, offset,
action, for_cow);
- if (!need_ref_seq(for_cow, ref_root) &&
- waitqueue_active(&fs_info->tree_mod_seq_wait))
- wake_up(&fs_info->tree_mod_seq_wait);
spin_unlock(&delayed_refs->lock);
if (need_ref_seq(for_cow, ref_root))
btrfs_qgroup_record_ref(trans, &ref->node, extent_op);
@@ -744,8 +839,6 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
num_bytes, BTRFS_UPDATE_DELAYED_HEAD,
extent_op->is_data);
- if (waitqueue_active(&fs_info->tree_mod_seq_wait))
- wake_up(&fs_info->tree_mod_seq_wait);
spin_unlock(&delayed_refs->lock);
return 0;
}
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h
index 0d7c90c366b6..ab5300595847 100644
--- a/fs/btrfs/delayed-ref.h
+++ b/fs/btrfs/delayed-ref.h
@@ -167,6 +167,10 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info,
struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes,
struct btrfs_delayed_extent_op *extent_op);
+void btrfs_merge_delayed_refs(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info,
+ struct btrfs_delayed_ref_root *delayed_refs,
+ struct btrfs_delayed_ref_head *head);
struct btrfs_delayed_ref_head *
btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index fadeba6a5db9..22e98e04c2ea 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -377,9 +377,13 @@ static int btree_read_extent_buffer_pages(struct btrfs_root *root,
ret = read_extent_buffer_pages(io_tree, eb, start,
WAIT_COMPLETE,
btree_get_extent, mirror_num);
- if (!ret && !verify_parent_transid(io_tree, eb,
+ if (!ret) {
+ if (!verify_parent_transid(io_tree, eb,
parent_transid, 0))
- break;
+ break;
+ else
+ ret = -EIO;
+ }
/*
* This buffer's crc is fine, but its contents are corrupted, so
@@ -754,9 +758,7 @@ static void run_one_async_done(struct btrfs_work *work)
limit = btrfs_async_submit_limit(fs_info);
limit = limit * 2 / 3;
- atomic_dec(&fs_info->nr_async_submits);
-
- if (atomic_read(&fs_info->nr_async_submits) < limit &&
+ if (atomic_dec_return(&fs_info->nr_async_submits) < limit &&
waitqueue_active(&fs_info->async_submit_wait))
wake_up(&fs_info->async_submit_wait);
@@ -1614,8 +1616,6 @@ static int cleaner_kthread(void *arg)
struct btrfs_root *root = arg;
do {
- vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
-
if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
mutex_trylock(&root->fs_info->cleaner_mutex)) {
btrfs_run_delayed_iputs(root);
@@ -1647,7 +1647,6 @@ static int transaction_kthread(void *arg)
do {
cannot_commit = false;
delay = HZ * 30;
- vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
mutex_lock(&root->fs_info->transaction_kthread_mutex);
spin_lock(&root->fs_info->trans_lock);
@@ -2035,8 +2034,6 @@ int open_ctree(struct super_block *sb,
fs_info->free_chunk_space = 0;
fs_info->tree_mod_log = RB_ROOT;
- init_waitqueue_head(&fs_info->tree_mod_seq_wait);
-
/* readahead state */
INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_WAIT);
spin_lock_init(&fs_info->reada_lock);
@@ -2531,8 +2528,7 @@ retry_root_backup:
goto fail_trans_kthread;
/* do not make disk changes in broken FS */
- if (btrfs_super_log_root(disk_super) != 0 &&
- !(fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)) {
+ if (btrfs_super_log_root(disk_super) != 0) {
u64 bytenr = btrfs_super_log_root(disk_super);
if (fs_devices->rw_devices == 0) {
@@ -3192,30 +3188,14 @@ int close_ctree(struct btrfs_root *root)
/* clear out the rbtree of defraggable inodes */
btrfs_run_defrag_inodes(fs_info);
- /*
- * Here come 2 situations when btrfs is broken to flip readonly:
- *
- * 1. when btrfs flips readonly somewhere else before
- * btrfs_commit_super, sb->s_flags has MS_RDONLY flag,
- * and btrfs will skip to write sb directly to keep
- * ERROR state on disk.
- *
- * 2. when btrfs flips readonly just in btrfs_commit_super,
- * and in such case, btrfs cannot write sb via btrfs_commit_super,
- * and since fs_state has been set BTRFS_SUPER_FLAG_ERROR flag,
- * btrfs will cleanup all FS resources first and write sb then.
- */
if (!(fs_info->sb->s_flags & MS_RDONLY)) {
ret = btrfs_commit_super(root);
if (ret)
printk(KERN_ERR "btrfs: commit super ret %d\n", ret);
}
- if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
- ret = btrfs_error_commit_super(root);
- if (ret)
- printk(KERN_ERR "btrfs: commit super ret %d\n", ret);
- }
+ if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
+ btrfs_error_commit_super(root);
btrfs_put_block_group_cache(fs_info);
@@ -3437,18 +3417,11 @@ static int btrfs_check_super_valid(struct btrfs_fs_info *fs_info,
if (read_only)
return 0;
- if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
- printk(KERN_WARNING "warning: mount fs with errors, "
- "running btrfsck is recommended\n");
- }
-
return 0;
}
-int btrfs_error_commit_super(struct btrfs_root *root)
+void btrfs_error_commit_super(struct btrfs_root *root)
{
- int ret;
-
mutex_lock(&root->fs_info->cleaner_mutex);
btrfs_run_delayed_iputs(root);
mutex_unlock(&root->fs_info->cleaner_mutex);
@@ -3458,10 +3431,6 @@ int btrfs_error_commit_super(struct btrfs_root *root)
/* cleanup FS via transaction */
btrfs_cleanup_transaction(root);
-
- ret = write_ctree_super(NULL, root, 0);
-
- return ret;
}
static void btrfs_destroy_ordered_operations(struct btrfs_root *root)
@@ -3785,14 +3754,17 @@ int btrfs_cleanup_transaction(struct btrfs_root *root)
/* FIXME: cleanup wait for commit */
t->in_commit = 1;
t->blocked = 1;
+ smp_mb();
if (waitqueue_active(&root->fs_info->transaction_blocked_wait))
wake_up(&root->fs_info->transaction_blocked_wait);
t->blocked = 0;
+ smp_mb();
if (waitqueue_active(&root->fs_info->transaction_wait))
wake_up(&root->fs_info->transaction_wait);
t->commit_done = 1;
+ smp_mb();
if (waitqueue_active(&t->commit_wait))
wake_up(&t->commit_wait);
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 95e147eea239..c5b00a735fef 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -54,7 +54,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int max_mirrors);
struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
int btrfs_commit_super(struct btrfs_root *root);
-int btrfs_error_commit_super(struct btrfs_root *root);
+void btrfs_error_commit_super(struct btrfs_root *root);
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
u64 bytenr, u32 blocksize);
struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 4e1b153b7c47..ba58024d40d3 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2252,6 +2252,16 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
}
/*
+ * We need to try and merge add/drops of the same ref since we
+ * can run into issues with relocate dropping the implicit ref
+ * and then it being added back again before the drop can
+ * finish. If we merged anything we need to re-loop so we can
+ * get a good ref.
+ */
+ btrfs_merge_delayed_refs(trans, fs_info, delayed_refs,
+ locked_ref);
+
+ /*
* locked_ref is the head node, so we have to go one
* node back for any delayed ref updates
*/
@@ -2318,12 +2328,23 @@ static noinline int run_clustered_refs(struct btrfs_trans_handle *trans,
ref->in_tree = 0;
rb_erase(&ref->rb_node, &delayed_refs->root);
delayed_refs->num_entries--;
- /*
- * we modified num_entries, but as we're currently running
- * delayed refs, skip
- * wake_up(&delayed_refs->seq_wait);
- * here.
- */
+ if (locked_ref) {
+ /*
+ * when we play the delayed ref, also correct the
+ * ref_mod on head
+ */
+ switch (ref->action) {
+ case BTRFS_ADD_DELAYED_REF:
+ case BTRFS_ADD_DELAYED_EXTENT:
+ locked_ref->node.ref_mod -= ref->ref_mod;
+ break;
+ case BTRFS_DROP_DELAYED_REF:
+ locked_ref->node.ref_mod += ref->ref_mod;
+ break;
+ default:
+ WARN_ON(1);
+ }
+ }
spin_unlock(&delayed_refs->lock);
ret = run_one_delayed_ref(trans, root, ref, extent_op,
@@ -2350,22 +2371,6 @@ next:
return count;
}
-static void wait_for_more_refs(struct btrfs_fs_info *fs_info,
- struct btrfs_delayed_ref_root *delayed_refs,
- unsigned long num_refs,
- struct list_head *first_seq)
-{
- spin_unlock(&delayed_refs->lock);
- pr_debug("waiting for more refs (num %ld, first %p)\n",
- num_refs, first_seq);
- wait_event(fs_info->tree_mod_seq_wait,
- num_refs != delayed_refs->num_entries ||
- fs_info->tree_mod_seq_list.next != first_seq);
- pr_debug("done waiting for more refs (num %ld, first %p)\n",
- delayed_refs->num_entries, fs_info->tree_mod_seq_list.next);
- spin_lock(&delayed_refs->lock);
-}
-
#ifdef SCRAMBLE_DELAYED_REFS
/*
* Normally delayed refs get processed in ascending bytenr order. This
@@ -2460,13 +2465,11 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_delayed_ref_node *ref;
struct list_head cluster;
- struct list_head *first_seq = NULL;
int ret;
u64 delayed_start;
int run_all = count == (unsigned long)-1;
int run_most = 0;
- unsigned long num_refs = 0;
- int consider_waiting;
+ int loops;
/* We'll clean this up in btrfs_cleanup_transaction */
if (trans->aborted)
@@ -2484,7 +2487,7 @@ int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
delayed_refs = &trans->transaction->delayed_refs;
INIT_LIST_HEAD(&cluster);
again:
- consider_waiting = 0;
+ loops = 0;
spin_lock(&delayed_refs->lock);
#ifdef SCRAMBLE_DELAYED_REFS
@@ -2512,31 +2515,6 @@ again:
if (ret)
break;
- if (delayed_start >= delayed_refs->run_delayed_start) {
- if (consider_waiting == 0) {
- /*
- * btrfs_find_ref_cluster looped. let's do one
- * more cycle. if we don't run any delayed ref
- * during that cycle (because we can't because
- * all of them are blocked) and if the number of
- * refs doesn't change, we avoid busy waiting.
- */
- consider_waiting = 1;
- num_refs = delayed_refs->num_entries;
- first_seq = root->fs_info->tree_mod_seq_list.next;
- } else {
- wait_for_more_refs(root->fs_info, delayed_refs,
- num_refs, first_seq);
- /*
- * after waiting, things have changed. we
- * dropped the lock and someone else might have
- * run some refs, built new clusters and so on.
- * therefore, we restart staleness detection.
- */
- consider_waiting = 0;
- }
- }
-
ret = run_clustered_refs(trans, root, &cluster);
if (ret < 0) {
spin_unlock(&delayed_refs->lock);
@@ -2549,9 +2527,26 @@ again:
if (count == 0)
break;
- if (ret || delayed_refs->run_delayed_start == 0) {
+ if (delayed_start >= delayed_refs->run_delayed_start) {
+ if (loops == 0) {
+ /*
+ * btrfs_find_ref_cluster looped. let's do one
+ * more cycle. if we don't run any delayed ref
+ * during that cycle (because we can't because
+ * all of them are blocked), bail out.
+ */
+ loops = 1;
+ } else {
+ /*
+ * no runnable refs left, stop trying
+ */
+ BUG_ON(run_all);
+ break;
+ }
+ }
+ if (ret) {
/* refs were run, let's reset staleness detection */
- consider_waiting = 0;
+ loops = 0;
}
}
@@ -3007,17 +3002,16 @@ again:
}
spin_unlock(&block_group->lock);
- num_pages = (int)div64_u64(block_group->key.offset, 1024 * 1024 * 1024);
+ /*
+ * Try to preallocate enough space based on how big the block group is.
+ * Keep in mind this has to include any pinned space which could end up
+ * taking up quite a bit since it's not folded into the other space
+ * cache.
+ */
+ num_pages = (int)div64_u64(block_group->key.offset, 256 * 1024 * 1024);
if (!num_pages)
num_pages = 1;
- /*
- * Just to make absolutely sure we have enough space, we're going to
- * preallocate 12 pages worth of space for each block group. In
- * practice we ought to use at most 8, but we need extra space so we can
- * add our header and have a terminator between the extents and the
- * bitmaps.
- */
num_pages *= 16;
num_pages *= PAGE_CACHE_SIZE;
@@ -4571,8 +4565,10 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
if (root->fs_info->quota_enabled) {
ret = btrfs_qgroup_reserve(root, num_bytes +
nr_extents * root->leafsize);
- if (ret)
+ if (ret) {
+ mutex_unlock(&BTRFS_I(inode)->delalloc_mutex);
return ret;
+ }
}
ret = reserve_metadata_bytes(root, block_rsv, to_reserve, flush);
@@ -5294,9 +5290,6 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans,
rb_erase(&head->node.rb_node, &delayed_refs->root);
delayed_refs->num_entries--;
- smp_mb();
- if (waitqueue_active(&root->fs_info->tree_mod_seq_wait))
- wake_up(&root->fs_info->tree_mod_seq_wait);
/*
* we don't take a ref on the node because we're removing it from the
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 45c81bb4ac82..4c878476bb91 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2330,23 +2330,10 @@ static void end_bio_extent_readpage(struct bio *bio, int err)
if (uptodate && tree->ops && tree->ops->readpage_end_io_hook) {
ret = tree->ops->readpage_end_io_hook(page, start, end,
state, mirror);
- if (ret) {
- /* no IO indicated but software detected errors
- * in the block, either checksum errors or
- * issues with the contents */
- struct btrfs_root *root =
- BTRFS_I(page->mapping->host)->root;
- struct btrfs_device *device;
-
+ if (ret)
uptodate = 0;
- device = btrfs_find_device_for_logical(
- root, start, mirror);
- if (device)
- btrfs_dev_stat_inc_and_print(device,
- BTRFS_DEV_STAT_CORRUPTION_ERRS);
- } else {
+ else
clean_io_failure(start, page);
- }
}
if (!uptodate && tree->ops && tree->ops->readpage_io_failed_hook) {
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index b45b9de0c21d..857d93cd01dc 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -272,9 +272,9 @@ int btrfs_lookup_bio_sums(struct btrfs_root *root, struct inode *inode,
}
int btrfs_lookup_bio_sums_dio(struct btrfs_root *root, struct inode *inode,
- struct bio *bio, u64 offset, u32 *dst)
+ struct bio *bio, u64 offset)
{
- return __btrfs_lookup_bio_sums(root, inode, bio, offset, dst, 1);
+ return __btrfs_lookup_bio_sums(root, inode, bio, offset, NULL, 1);
}
int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 9aa01ec2138d..5caf285c6e4d 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1379,7 +1379,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
ssize_t err = 0;
size_t count, ocount;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+ sb_start_write(inode->i_sb);
mutex_lock(&inode->i_mutex);
@@ -1469,6 +1469,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
num_written = err;
}
out:
+ sb_end_write(inode->i_sb);
current->backing_dev_info = NULL;
return num_written ? num_written : err;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 48bdfd2591c2..ec154f954646 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -324,7 +324,8 @@ static noinline int add_async_extent(struct async_cow *cow,
* If this code finds it can't get good compression, it puts an
* entry onto the work queue to write the uncompressed bytes. This
* makes sure that both compressed inodes and uncompressed inodes
- * are written in the same order that pdflush sent them down.
+ * are written in the same order that the flusher thread sent them
+ * down.
*/
static noinline int compress_file_range(struct inode *inode,
struct page *locked_page,
@@ -1007,9 +1008,7 @@ static noinline void async_cow_submit(struct btrfs_work *work)
nr_pages = (async_cow->end - async_cow->start + PAGE_CACHE_SIZE) >>
PAGE_CACHE_SHIFT;
- atomic_sub(nr_pages, &root->fs_info->async_delalloc_pages);
-
- if (atomic_read(&root->fs_info->async_delalloc_pages) <
+ if (atomic_sub_return(nr_pages, &root->fs_info->async_delalloc_pages) <
5 * 1024 * 1024 &&
waitqueue_active(&root->fs_info->async_submit_wait))
wake_up(&root->fs_info->async_submit_wait);
@@ -1884,8 +1883,11 @@ static int btrfs_finish_ordered_io(struct btrfs_ordered_extent *ordered_extent)
trans = btrfs_join_transaction_nolock(root);
else
trans = btrfs_join_transaction(root);
- if (IS_ERR(trans))
- return PTR_ERR(trans);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ trans = NULL;
+ goto out;
+ }
trans->block_rsv = &root->fs_info->delalloc_block_rsv;
ret = btrfs_update_inode_fallback(trans, root, inode);
if (ret) /* -ENOMEM or corruption */
@@ -3173,7 +3175,7 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans,
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
inode_inc_iversion(dir);
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
- ret = btrfs_update_inode(trans, root, dir);
+ ret = btrfs_update_inode_fallback(trans, root, dir);
if (ret)
btrfs_abort_transaction(trans, root, ret);
out:
@@ -5773,18 +5775,112 @@ out:
return ret;
}
+static int lock_extent_direct(struct inode *inode, u64 lockstart, u64 lockend,
+ struct extent_state **cached_state, int writing)
+{
+ struct btrfs_ordered_extent *ordered;
+ int ret = 0;
+
+ while (1) {
+ lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+ 0, cached_state);
+ /*
+ * We're concerned with the entire range that we're going to be
+ * doing DIO to, so we need to make sure theres no ordered
+ * extents in this range.
+ */
+ ordered = btrfs_lookup_ordered_range(inode, lockstart,
+ lockend - lockstart + 1);
+
+ /*
+ * We need to make sure there are no buffered pages in this
+ * range either, we could have raced between the invalidate in
+ * generic_file_direct_write and locking the extent. The
+ * invalidate needs to happen so that reads after a write do not
+ * get stale data.
+ */
+ if (!ordered && (!writing ||
+ !test_range_bit(&BTRFS_I(inode)->io_tree,
+ lockstart, lockend, EXTENT_UPTODATE, 0,
+ *cached_state)))
+ break;
+
+ unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+ cached_state, GFP_NOFS);
+
+ if (ordered) {
+ btrfs_start_ordered_extent(inode, ordered, 1);
+ btrfs_put_ordered_extent(ordered);
+ } else {
+ /* Screw you mmap */
+ ret = filemap_write_and_wait_range(inode->i_mapping,
+ lockstart,
+ lockend);
+ if (ret)
+ break;
+
+ /*
+ * If we found a page that couldn't be invalidated just
+ * fall back to buffered.
+ */
+ ret = invalidate_inode_pages2_range(inode->i_mapping,
+ lockstart >> PAGE_CACHE_SHIFT,
+ lockend >> PAGE_CACHE_SHIFT);
+ if (ret)
+ break;
+ }
+
+ cond_resched();
+ }
+
+ return ret;
+}
+
static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
struct extent_map *em;
struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct extent_state *cached_state = NULL;
u64 start = iblock << inode->i_blkbits;
+ u64 lockstart, lockend;
u64 len = bh_result->b_size;
struct btrfs_trans_handle *trans;
+ int unlock_bits = EXTENT_LOCKED;
+ int ret;
+
+ if (create) {
+ ret = btrfs_delalloc_reserve_space(inode, len);
+ if (ret)
+ return ret;
+ unlock_bits |= EXTENT_DELALLOC | EXTENT_DIRTY;
+ } else {
+ len = min_t(u64, len, root->sectorsize);
+ }
+
+ lockstart = start;
+ lockend = start + len - 1;
+
+ /*
+ * If this errors out it's because we couldn't invalidate pagecache for
+ * this range and we need to fallback to buffered.
+ */
+ if (lock_extent_direct(inode, lockstart, lockend, &cached_state, create))
+ return -ENOTBLK;
+
+ if (create) {
+ ret = set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
+ lockend, EXTENT_DELALLOC, NULL,
+ &cached_state, GFP_NOFS);
+ if (ret)
+ goto unlock_err;
+ }
em = btrfs_get_extent(inode, NULL, 0, start, len, 0);
- if (IS_ERR(em))
- return PTR_ERR(em);
+ if (IS_ERR(em)) {
+ ret = PTR_ERR(em);
+ goto unlock_err;
+ }
/*
* Ok for INLINE and COMPRESSED extents we need to fallback on buffered
@@ -5803,17 +5899,16 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags) ||
em->block_start == EXTENT_MAP_INLINE) {
free_extent_map(em);
- return -ENOTBLK;
+ ret = -ENOTBLK;
+ goto unlock_err;
}
/* Just a good old fashioned hole, return */
if (!create && (em->block_start == EXTENT_MAP_HOLE ||
test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
free_extent_map(em);
- /* DIO will do one hole at a time, so just unlock a sector */
- unlock_extent(&BTRFS_I(inode)->io_tree, start,
- start + root->sectorsize - 1);
- return 0;
+ ret = 0;
+ goto unlock_err;
}
/*
@@ -5826,8 +5921,9 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
*
*/
if (!create) {
- len = em->len - (start - em->start);
- goto map;
+ len = min(len, em->len - (start - em->start));
+ lockstart = start + len;
+ goto unlock;
}
if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags) ||
@@ -5859,7 +5955,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
btrfs_end_transaction(trans, root);
if (ret) {
free_extent_map(em);
- return ret;
+ goto unlock_err;
}
goto unlock;
}
@@ -5872,14 +5968,12 @@ must_cow:
*/
len = bh_result->b_size;
em = btrfs_new_extent_direct(inode, em, start, len);
- if (IS_ERR(em))
- return PTR_ERR(em);
+ if (IS_ERR(em)) {
+ ret = PTR_ERR(em);
+ goto unlock_err;
+ }
len = min(len, em->len - (start - em->start));
unlock:
- clear_extent_bit(&BTRFS_I(inode)->io_tree, start, start + len - 1,
- EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DIRTY, 1,
- 0, NULL, GFP_NOFS);
-map:
bh_result->b_blocknr = (em->block_start + (start - em->start)) >>
inode->i_blkbits;
bh_result->b_size = len;
@@ -5897,9 +5991,44 @@ map:
i_size_write(inode, start + len);
}
+ /*
+ * In the case of write we need to clear and unlock the entire range,
+ * in the case of read we need to unlock only the end area that we
+ * aren't using if there is any left over space.
+ */
+ if (lockstart < lockend) {
+ if (create && len < lockend - lockstart) {
+ clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
+ lockstart + len - 1, unlock_bits, 1, 0,
+ &cached_state, GFP_NOFS);
+ /*
+ * Beside unlock, we also need to cleanup reserved space
+ * for the left range by attaching EXTENT_DO_ACCOUNTING.
+ */
+ clear_extent_bit(&BTRFS_I(inode)->io_tree,
+ lockstart + len, lockend,
+ unlock_bits | EXTENT_DO_ACCOUNTING,
+ 1, 0, NULL, GFP_NOFS);
+ } else {
+ clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
+ lockend, unlock_bits, 1, 0,
+ &cached_state, GFP_NOFS);
+ }
+ } else {
+ free_extent_state(cached_state);
+ }
+
free_extent_map(em);
return 0;
+
+unlock_err:
+ if (create)
+ unlock_bits |= EXTENT_DO_ACCOUNTING;
+
+ clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
+ unlock_bits, 1, 0, &cached_state, GFP_NOFS);
+ return ret;
}
struct btrfs_dio_private {
@@ -5907,7 +6036,6 @@ struct btrfs_dio_private {
u64 logical_offset;
u64 disk_bytenr;
u64 bytes;
- u32 *csums;
void *private;
/* number of bios pending for this dio */
@@ -5927,7 +6055,6 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
struct inode *inode = dip->inode;
struct btrfs_root *root = BTRFS_I(inode)->root;
u64 start;
- u32 *private = dip->csums;
start = dip->logical_offset;
do {
@@ -5935,8 +6062,12 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
struct page *page = bvec->bv_page;
char *kaddr;
u32 csum = ~(u32)0;
+ u64 private = ~(u32)0;
unsigned long flags;
+ if (get_state_private(&BTRFS_I(inode)->io_tree,
+ start, &private))
+ goto failed;
local_irq_save(flags);
kaddr = kmap_atomic(page);
csum = btrfs_csum_data(root, kaddr + bvec->bv_offset,
@@ -5946,18 +6077,18 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
local_irq_restore(flags);
flush_dcache_page(bvec->bv_page);
- if (csum != *private) {
+ if (csum != private) {
+failed:
printk(KERN_ERR "btrfs csum failed ino %llu off"
" %llu csum %u private %u\n",
(unsigned long long)btrfs_ino(inode),
(unsigned long long)start,
- csum, *private);
+ csum, (unsigned)private);
err = -EIO;
}
}
start += bvec->bv_len;
- private++;
bvec++;
} while (bvec <= bvec_end);
@@ -5965,7 +6096,6 @@ static void btrfs_endio_direct_read(struct bio *bio, int err)
dip->logical_offset + dip->bytes - 1);
bio->bi_private = dip->private;
- kfree(dip->csums);
kfree(dip);
/* If we had a csum failure make sure to clear the uptodate flag */
@@ -6071,7 +6201,7 @@ static struct bio *btrfs_dio_bio_alloc(struct block_device *bdev,
static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
int rw, u64 file_offset, int skip_sum,
- u32 *csums, int async_submit)
+ int async_submit)
{
int write = rw & REQ_WRITE;
struct btrfs_root *root = BTRFS_I(inode)->root;
@@ -6104,8 +6234,7 @@ static inline int __btrfs_submit_dio_bio(struct bio *bio, struct inode *inode,
if (ret)
goto err;
} else if (!skip_sum) {
- ret = btrfs_lookup_bio_sums_dio(root, inode, bio,
- file_offset, csums);
+ ret = btrfs_lookup_bio_sums_dio(root, inode, bio, file_offset);
if (ret)
goto err;
}
@@ -6131,10 +6260,8 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
u64 submit_len = 0;
u64 map_length;
int nr_pages = 0;
- u32 *csums = dip->csums;
int ret = 0;
int async_submit = 0;
- int write = rw & REQ_WRITE;
map_length = orig_bio->bi_size;
ret = btrfs_map_block(map_tree, READ, start_sector << 9,
@@ -6170,16 +6297,13 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
atomic_inc(&dip->pending_bios);
ret = __btrfs_submit_dio_bio(bio, inode, rw,
file_offset, skip_sum,
- csums, async_submit);
+ async_submit);
if (ret) {
bio_put(bio);
atomic_dec(&dip->pending_bios);
goto out_err;
}
- /* Write's use the ordered csums */
- if (!write && !skip_sum)
- csums = csums + nr_pages;
start_sector += submit_len >> 9;
file_offset += submit_len;
@@ -6209,7 +6333,7 @@ static int btrfs_submit_direct_hook(int rw, struct btrfs_dio_private *dip,
submit:
ret = __btrfs_submit_dio_bio(bio, inode, rw, file_offset, skip_sum,
- csums, async_submit);
+ async_submit);
if (!ret)
return 0;
@@ -6245,17 +6369,6 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode,
ret = -ENOMEM;
goto free_ordered;
}
- dip->csums = NULL;
-
- /* Write's use the ordered csum stuff, so we don't need dip->csums */
- if (!write && !skip_sum) {
- dip->csums = kmalloc(sizeof(u32) * bio->bi_vcnt, GFP_NOFS);
- if (!dip->csums) {
- kfree(dip);
- ret = -ENOMEM;
- goto free_ordered;
- }
- }
dip->private = bio->bi_private;
dip->inode = inode;
@@ -6340,132 +6453,22 @@ static ssize_t check_direct_IO(struct btrfs_root *root, int rw, struct kiocb *io
out:
return retval;
}
+
static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
const struct iovec *iov, loff_t offset,
unsigned long nr_segs)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
- struct btrfs_ordered_extent *ordered;
- struct extent_state *cached_state = NULL;
- u64 lockstart, lockend;
- ssize_t ret;
- int writing = rw & WRITE;
- int write_bits = 0;
- size_t count = iov_length(iov, nr_segs);
if (check_direct_IO(BTRFS_I(inode)->root, rw, iocb, iov,
- offset, nr_segs)) {
+ offset, nr_segs))
return 0;
- }
- lockstart = offset;
- lockend = offset + count - 1;
-
- if (writing) {
- ret = btrfs_delalloc_reserve_space(inode, count);
- if (ret)
- goto out;
- }
-
- while (1) {
- lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend,
- 0, &cached_state);
- /*
- * We're concerned with the entire range that we're going to be
- * doing DIO to, so we need to make sure theres no ordered
- * extents in this range.
- */
- ordered = btrfs_lookup_ordered_range(inode, lockstart,
- lockend - lockstart + 1);
-
- /*
- * We need to make sure there are no buffered pages in this
- * range either, we could have raced between the invalidate in
- * generic_file_direct_write and locking the extent. The
- * invalidate needs to happen so that reads after a write do not
- * get stale data.
- */
- if (!ordered && (!writing ||
- !test_range_bit(&BTRFS_I(inode)->io_tree,
- lockstart, lockend, EXTENT_UPTODATE, 0,
- cached_state)))
- break;
-
- unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
- &cached_state, GFP_NOFS);
-
- if (ordered) {
- btrfs_start_ordered_extent(inode, ordered, 1);
- btrfs_put_ordered_extent(ordered);
- } else {
- /* Screw you mmap */
- ret = filemap_write_and_wait_range(file->f_mapping,
- lockstart,
- lockend);
- if (ret)
- goto out;
-
- /*
- * If we found a page that couldn't be invalidated just
- * fall back to buffered.
- */
- ret = invalidate_inode_pages2_range(file->f_mapping,
- lockstart >> PAGE_CACHE_SHIFT,
- lockend >> PAGE_CACHE_SHIFT);
- if (ret) {
- if (ret == -EBUSY)
- ret = 0;
- goto out;
- }
- }
-
- cond_resched();
- }
-
- /*
- * we don't use btrfs_set_extent_delalloc because we don't want
- * the dirty or uptodate bits
- */
- if (writing) {
- write_bits = EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING;
- ret = set_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
- EXTENT_DELALLOC, NULL, &cached_state,
- GFP_NOFS);
- if (ret) {
- clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart,
- lockend, EXTENT_LOCKED | write_bits,
- 1, 0, &cached_state, GFP_NOFS);
- goto out;
- }
- }
-
- free_extent_state(cached_state);
- cached_state = NULL;
-
- ret = __blockdev_direct_IO(rw, iocb, inode,
+ return __blockdev_direct_IO(rw, iocb, inode,
BTRFS_I(inode)->root->fs_info->fs_devices->latest_bdev,
iov, offset, nr_segs, btrfs_get_blocks_direct, NULL,
btrfs_submit_direct, 0);
-
- if (ret < 0 && ret != -EIOCBQUEUED) {
- clear_extent_bit(&BTRFS_I(inode)->io_tree, offset,
- offset + iov_length(iov, nr_segs) - 1,
- EXTENT_LOCKED | write_bits, 1, 0,
- &cached_state, GFP_NOFS);
- } else if (ret >= 0 && ret < iov_length(iov, nr_segs)) {
- /*
- * We're falling back to buffered, unlock the section we didn't
- * do IO on.
- */
- clear_extent_bit(&BTRFS_I(inode)->io_tree, offset + ret,
- offset + iov_length(iov, nr_segs) - 1,
- EXTENT_LOCKED | write_bits, 1, 0,
- &cached_state, GFP_NOFS);
- }
-out:
- free_extent_state(cached_state);
- return ret;
}
static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
@@ -6629,6 +6632,7 @@ int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
u64 page_start;
u64 page_end;
+ sb_start_pagefault(inode->i_sb);
ret = btrfs_delalloc_reserve_space(inode, PAGE_CACHE_SIZE);
if (!ret) {
ret = file_update_time(vma->vm_file);
@@ -6718,12 +6722,15 @@ again:
unlock_extent_cached(io_tree, page_start, page_end, &cached_state, GFP_NOFS);
out_unlock:
- if (!ret)
+ if (!ret) {
+ sb_end_pagefault(inode->i_sb);
return VM_FAULT_LOCKED;
+ }
unlock_page(page);
out:
btrfs_delalloc_release_space(inode, PAGE_CACHE_SIZE);
out_noreserve:
+ sb_end_pagefault(inode->i_sb);
return ret;
}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 43f0012016e3..9df50fa8a078 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -195,6 +195,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
if (!inode_owner_or_capable(inode))
return -EACCES;
+ ret = mnt_want_write_file(file);
+ if (ret)
+ return ret;
+
mutex_lock(&inode->i_mutex);
ip_oldflags = ip->flags;
@@ -209,10 +213,6 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
}
}
- ret = mnt_want_write_file(file);
- if (ret)
- goto out_unlock;
-
if (flags & FS_SYNC_FL)
ip->flags |= BTRFS_INODE_SYNC;
else
@@ -275,9 +275,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode->i_flags = i_oldflags;
}
- mnt_drop_write_file(file);
out_unlock:
mutex_unlock(&inode->i_mutex);
+ mnt_drop_write_file(file);
return ret;
}
@@ -424,7 +424,7 @@ static noinline int create_subvol(struct btrfs_root *root,
uuid_le_gen(&new_uuid);
memcpy(root_item.uuid, new_uuid.b, BTRFS_UUID_SIZE);
root_item.otime.sec = cpu_to_le64(cur_time.tv_sec);
- root_item.otime.nsec = cpu_to_le64(cur_time.tv_nsec);
+ root_item.otime.nsec = cpu_to_le32(cur_time.tv_nsec);
root_item.ctime = root_item.otime;
btrfs_set_root_ctransid(&root_item, trans->transid);
btrfs_set_root_otransid(&root_item, trans->transid);
diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c
index a44eff074805..2a1762c66041 100644
--- a/fs/btrfs/locking.c
+++ b/fs/btrfs/locking.c
@@ -67,7 +67,7 @@ void btrfs_clear_lock_blocking_rw(struct extent_buffer *eb, int rw)
{
if (eb->lock_nested) {
read_lock(&eb->lock);
- if (&eb->lock_nested && current->pid == eb->lock_owner) {
+ if (eb->lock_nested && current->pid == eb->lock_owner) {
read_unlock(&eb->lock);
return;
}
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 643335a4fe3c..051c7fe551dd 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -596,7 +596,7 @@ void btrfs_start_ordered_extent(struct inode *inode,
/*
* pages in the range can be dirty, clean or writeback. We
* start IO on any dirty ones so the wait doesn't stall waiting
- * for pdflush to find them
+ * for the flusher thread to find them
*/
if (!test_bit(BTRFS_ORDERED_DIRECT, &entry->flags))
filemap_fdatawrite_range(inode->i_mapping, start, end);
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index bc424ae5a81a..b65015581744 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1364,8 +1364,10 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans,
spin_lock(&fs_info->qgroup_lock);
dstgroup = add_qgroup_rb(fs_info, objectid);
- if (!dstgroup)
+ if (IS_ERR(dstgroup)) {
+ ret = PTR_ERR(dstgroup);
goto unlock;
+ }
if (srcid) {
srcgroup = find_qgroup_rb(fs_info, srcid);
diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c
index 6bb465cca20f..10d8e4d88071 100644
--- a/fs/btrfs/root-tree.c
+++ b/fs/btrfs/root-tree.c
@@ -544,8 +544,8 @@ void btrfs_update_root_times(struct btrfs_trans_handle *trans,
struct timespec ct = CURRENT_TIME;
spin_lock(&root->root_times_lock);
- item->ctransid = trans->transid;
+ item->ctransid = cpu_to_le64(trans->transid);
item->ctime.sec = cpu_to_le64(ct.tv_sec);
- item->ctime.nsec = cpu_to_le64(ct.tv_nsec);
+ item->ctime.nsec = cpu_to_le32(ct.tv_nsec);
spin_unlock(&root->root_times_lock);
}
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 8c6e61d6eed5..83d6f9f9c220 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -100,10 +100,6 @@ static void __save_error_info(struct btrfs_fs_info *fs_info)
fs_info->fs_state = BTRFS_SUPER_FLAG_ERROR;
}
-/* NOTE:
- * We move write_super stuff at umount in order to avoid deadlock
- * for umount hold all lock.
- */
static void save_error_info(struct btrfs_fs_info *fs_info)
{
__save_error_info(fs_info);
@@ -842,7 +838,6 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
struct btrfs_trans_handle *trans;
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
struct btrfs_root *root = fs_info->tree_root;
- int ret;
trace_btrfs_sync_fs(wait);
@@ -853,11 +848,17 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
btrfs_wait_ordered_extents(root, 0, 0);
- trans = btrfs_start_transaction(root, 0);
+ spin_lock(&fs_info->trans_lock);
+ if (!fs_info->running_transaction) {
+ spin_unlock(&fs_info->trans_lock);
+ return 0;
+ }
+ spin_unlock(&fs_info->trans_lock);
+
+ trans = btrfs_join_transaction(root);
if (IS_ERR(trans))
return PTR_ERR(trans);
- ret = btrfs_commit_transaction(trans, root);
- return ret;
+ return btrfs_commit_transaction(trans, root);
}
static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
@@ -1534,6 +1535,8 @@ static int btrfs_show_devname(struct seq_file *m, struct dentry *root)
while (cur_devices) {
head = &cur_devices->devices;
list_for_each_entry(dev, head, dev_list) {
+ if (dev->missing)
+ continue;
if (!first_dev || dev->devid < first_dev->devid)
first_dev = dev;
}
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 7ac7cdcc294e..27c26004e050 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -335,6 +335,8 @@ again:
if (!h)
return ERR_PTR(-ENOMEM);
+ sb_start_intwrite(root->fs_info->sb);
+
if (may_wait_transaction(root, type))
wait_current_trans(root);
@@ -345,6 +347,7 @@ again:
} while (ret == -EBUSY);
if (ret < 0) {
+ sb_end_intwrite(root->fs_info->sb);
kmem_cache_free(btrfs_trans_handle_cachep, h);
return ERR_PTR(ret);
}
@@ -548,6 +551,8 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
btrfs_trans_release_metadata(trans, root);
trans->block_rsv = NULL;
+ sb_end_intwrite(root->fs_info->sb);
+
if (lock && !atomic_read(&root->fs_info->open_ioctl_trans) &&
should_end_transaction(trans, root)) {
trans->transaction->blocked = 1;
@@ -1026,6 +1031,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
btrfs_i_size_write(parent_inode, parent_inode->i_size +
dentry->d_name.len * 2);
+ parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
ret = btrfs_update_inode(trans, parent_root, parent_inode);
if (ret)
goto abort_trans_dput;
@@ -1061,7 +1067,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
memcpy(new_root_item->parent_uuid, root->root_item.uuid,
BTRFS_UUID_SIZE);
new_root_item->otime.sec = cpu_to_le64(cur_time.tv_sec);
- new_root_item->otime.nsec = cpu_to_le64(cur_time.tv_nsec);
+ new_root_item->otime.nsec = cpu_to_le32(cur_time.tv_nsec);
btrfs_set_root_otransid(new_root_item, trans->transid);
memset(&new_root_item->stime, 0, sizeof(new_root_item->stime));
memset(&new_root_item->rtime, 0, sizeof(new_root_item->rtime));
@@ -1578,6 +1584,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
put_transaction(cur_trans);
put_transaction(cur_trans);
+ sb_end_intwrite(root->fs_info->sb);
+
trace_btrfs_transaction_commit(root);
btrfs_scrub_continue(root);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index b8708f994e67..88b969aeeb71 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -227,9 +227,8 @@ loop_lock:
cur = pending;
pending = pending->bi_next;
cur->bi_next = NULL;
- atomic_dec(&fs_info->nr_async_bios);
- if (atomic_read(&fs_info->nr_async_bios) < limit &&
+ if (atomic_dec_return(&fs_info->nr_async_bios) < limit &&
waitqueue_active(&fs_info->async_submit_wait))
wake_up(&fs_info->async_submit_wait);
@@ -569,9 +568,11 @@ static int __btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
memcpy(new_device, device, sizeof(*new_device));
/* Safe because we are under uuid_mutex */
- name = rcu_string_strdup(device->name->str, GFP_NOFS);
- BUG_ON(device->name && !name); /* -ENOMEM */
- rcu_assign_pointer(new_device->name, name);
+ if (device->name) {
+ name = rcu_string_strdup(device->name->str, GFP_NOFS);
+ BUG_ON(device->name && !name); /* -ENOMEM */
+ rcu_assign_pointer(new_device->name, name);
+ }
new_device->bdev = NULL;
new_device->writeable = 0;
new_device->in_fs_metadata = 0;
@@ -1744,10 +1745,6 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path)
device->fs_devices = root->fs_info->fs_devices;
- /*
- * we don't want write_supers to jump in here with our device
- * half setup
- */
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices);
list_add(&device->dev_alloc_list,
@@ -4609,28 +4606,6 @@ int btrfs_read_sys_array(struct btrfs_root *root)
return ret;
}
-struct btrfs_device *btrfs_find_device_for_logical(struct btrfs_root *root,
- u64 logical, int mirror_num)
-{
- struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree;
- int ret;
- u64 map_length = 0;
- struct btrfs_bio *bbio = NULL;
- struct btrfs_device *device;
-
- BUG_ON(mirror_num == 0);
- ret = btrfs_map_block(map_tree, WRITE, logical, &map_length, &bbio,
- mirror_num);
- if (ret) {
- BUG_ON(bbio != NULL);
- return NULL;
- }
- BUG_ON(mirror_num != bbio->mirror_num);
- device = bbio->stripes[mirror_num - 1].dev;
- kfree(bbio);
- return device;
-}
-
int btrfs_read_chunk_tree(struct btrfs_root *root)
{
struct btrfs_path *path;
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index 5479325987b3..53c06af92e8d 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -289,8 +289,6 @@ int btrfs_cancel_balance(struct btrfs_fs_info *fs_info);
int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes,
u64 *start, u64 *max_avail);
-struct btrfs_device *btrfs_find_device_for_logical(struct btrfs_root *root,
- u64 logical, int mirror_num);
void btrfs_dev_stat_print_on_error(struct btrfs_device *device);
void btrfs_dev_stat_inc_and_print(struct btrfs_device *dev, int index);
int btrfs_get_dev_stats(struct btrfs_root *root,
diff --git a/fs/buffer.c b/fs/buffer.c
index c7062c896d7c..58e2e7b77372 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -914,7 +914,7 @@ link_dev_buffers(struct page *page, struct buffer_head *head)
/*
* Initialise the state of a blockdev page's buffers.
*/
-static void
+static sector_t
init_page_buffers(struct page *page, struct block_device *bdev,
sector_t block, int size)
{
@@ -936,33 +936,41 @@ init_page_buffers(struct page *page, struct block_device *bdev,
block++;
bh = bh->b_this_page;
} while (bh != head);
+
+ /*
+ * Caller needs to validate requested block against end of device.
+ */
+ return end_block;
}
/*
* Create the page-cache page that contains the requested block.
*
- * This is user purely for blockdev mappings.
+ * This is used purely for blockdev mappings.
*/
-static struct page *
+static int
grow_dev_page(struct block_device *bdev, sector_t block,
- pgoff_t index, int size)
+ pgoff_t index, int size, int sizebits)
{
struct inode *inode = bdev->bd_inode;
struct page *page;
struct buffer_head *bh;
+ sector_t end_block;
+ int ret = 0; /* Will call free_more_memory() */
page = find_or_create_page(inode->i_mapping, index,
(mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS)|__GFP_MOVABLE);
if (!page)
- return NULL;
+ return ret;
BUG_ON(!PageLocked(page));
if (page_has_buffers(page)) {
bh = page_buffers(page);
if (bh->b_size == size) {
- init_page_buffers(page, bdev, block, size);
- return page;
+ end_block = init_page_buffers(page, bdev,
+ index << sizebits, size);
+ goto done;
}
if (!try_to_free_buffers(page))
goto failed;
@@ -982,14 +990,14 @@ grow_dev_page(struct block_device *bdev, sector_t block,
*/
spin_lock(&inode->i_mapping->private_lock);
link_dev_buffers(page, bh);
- init_page_buffers(page, bdev, block, size);
+ end_block = init_page_buffers(page, bdev, index << sizebits, size);
spin_unlock(&inode->i_mapping->private_lock);
- return page;
-
+done:
+ ret = (block < end_block) ? 1 : -ENXIO;
failed:
unlock_page(page);
page_cache_release(page);
- return NULL;
+ return ret;
}
/*
@@ -999,7 +1007,6 @@ failed:
static int
grow_buffers(struct block_device *bdev, sector_t block, int size)
{
- struct page *page;
pgoff_t index;
int sizebits;
@@ -1023,22 +1030,14 @@ grow_buffers(struct block_device *bdev, sector_t block, int size)
bdevname(bdev, b));
return -EIO;
}
- block = index << sizebits;
+
/* Create a page with the proper size buffers.. */
- page = grow_dev_page(bdev, block, index, size);
- if (!page)
- return 0;
- unlock_page(page);
- page_cache_release(page);
- return 1;
+ return grow_dev_page(bdev, block, index, size, sizebits);
}
static struct buffer_head *
__getblk_slow(struct block_device *bdev, sector_t block, int size)
{
- int ret;
- struct buffer_head *bh;
-
/* Size must be multiple of hard sectorsize */
if (unlikely(size & (bdev_logical_block_size(bdev)-1) ||
(size < 512 || size > PAGE_SIZE))) {
@@ -1051,21 +1050,20 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size)
return NULL;
}
-retry:
- bh = __find_get_block(bdev, block, size);
- if (bh)
- return bh;
+ for (;;) {
+ struct buffer_head *bh;
+ int ret;
- ret = grow_buffers(bdev, block, size);
- if (ret == 0) {
- free_more_memory();
- goto retry;
- } else if (ret > 0) {
bh = __find_get_block(bdev, block, size);
if (bh)
return bh;
+
+ ret = grow_buffers(bdev, block, size);
+ if (ret < 0)
+ return NULL;
+ if (ret == 0)
+ free_more_memory();
}
- return NULL;
}
/*
@@ -1321,10 +1319,6 @@ EXPORT_SYMBOL(__find_get_block);
* which corresponds to the passed block_device, block and size. The
* returned buffer has its reference count incremented.
*
- * __getblk() cannot fail - it just keeps trying. If you pass it an
- * illegal block number, __getblk() will happily return a buffer_head
- * which represents the non-existent block. Very weird.
- *
* __getblk() will lock up the machine if grow_dev_page's try_to_free_buffers()
* attempt is failing. FIXME, perhaps?
*/
@@ -2306,8 +2300,8 @@ EXPORT_SYMBOL(block_commit_write);
* beyond EOF, then the page is guaranteed safe against truncation until we
* unlock the page.
*
- * Direct callers of this function should call vfs_check_frozen() so that page
- * fault does not busyloop until the fs is thawed.
+ * Direct callers of this function should protect against filesystem freezing
+ * using sb_start_write() - sb_end_write() functions.
*/
int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
get_block_t get_block)
@@ -2318,6 +2312,12 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
loff_t size;
int ret;
+ /*
+ * Update file times before taking page lock. We may end up failing the
+ * fault so this update may be superfluous but who really cares...
+ */
+ file_update_time(vma->vm_file);
+
lock_page(page);
size = i_size_read(inode);
if ((page->mapping != inode->i_mapping) ||
@@ -2339,18 +2339,7 @@ int __block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
if (unlikely(ret < 0))
goto out_unlock;
- /*
- * Freezing in progress? We check after the page is marked dirty and
- * with page lock held so if the test here fails, we are sure freezing
- * code will wait during syncing until the page fault is done - at that
- * point page will be dirty and unlocked so freezing code will write it
- * and writeprotect it again.
- */
set_page_dirty(page);
- if (inode->i_sb->s_frozen != SB_UNFROZEN) {
- ret = -EAGAIN;
- goto out_unlock;
- }
wait_on_page_writeback(page);
return 0;
out_unlock:
@@ -2365,12 +2354,9 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
int ret;
struct super_block *sb = vma->vm_file->f_path.dentry->d_inode->i_sb;
- /*
- * This check is racy but catches the common case. The check in
- * __block_page_mkwrite() is reliable.
- */
- vfs_check_frozen(sb, SB_FREEZE_WRITE);
+ sb_start_pagefault(sb);
ret = __block_page_mkwrite(vma, vmf, get_block);
+ sb_end_pagefault(sb);
return block_page_mkwrite_return(ret);
}
EXPORT_SYMBOL(block_page_mkwrite);
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 8b67304e4b80..452e71a1b753 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1184,6 +1184,9 @@ static int ceph_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
loff_t size, len;
int ret;
+ /* Update time before taking page lock */
+ file_update_time(vma->vm_file);
+
size = i_size_read(inode);
if (off + PAGE_CACHE_SIZE <= size)
len = PAGE_CACHE_SIZE;
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index fb962efdacee..6d59006bfa27 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -201,6 +201,7 @@ int ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
int err = -ENOMEM;
dout("ceph_fs_debugfs_init\n");
+ BUG_ON(!fsc->client->debugfs_dir);
fsc->debugfs_congestion_kb =
debugfs_create_file("writeback_congestion_kb",
0600,
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index f391f1e75414..e5b77319c97b 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -633,44 +633,6 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
return dentry;
}
-int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
- struct file *file, unsigned flags, umode_t mode,
- int *opened)
-{
- int err;
- struct dentry *res = NULL;
-
- if (!(flags & O_CREAT)) {
- if (dentry->d_name.len > NAME_MAX)
- return -ENAMETOOLONG;
-
- err = ceph_init_dentry(dentry);
- if (err < 0)
- return err;
-
- return ceph_lookup_open(dir, dentry, file, flags, mode, opened);
- }
-
- if (d_unhashed(dentry)) {
- res = ceph_lookup(dir, dentry, 0);
- if (IS_ERR(res))
- return PTR_ERR(res);
-
- if (res)
- dentry = res;
- }
-
- /* We don't deal with positive dentries here */
- if (dentry->d_inode)
- return finish_no_open(file, res);
-
- *opened |= FILE_CREATED;
- err = ceph_lookup_open(dir, dentry, file, flags, mode, opened);
- dput(res);
-
- return err;
-}
-
/*
* If we do a create but get no trace back from the MDS, follow up with
* a lookup (the VFS expects us to link up the provided dentry).
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 1b81d6c31878..ecebbc09bfc7 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -4,6 +4,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/file.h>
+#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/writeback.h>
@@ -106,9 +107,6 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
}
/*
- * If the filp already has private_data, that means the file was
- * already opened by intent during lookup, and we do nothing.
- *
* If we already have the requisite capabilities, we can satisfy
* the open request locally (no need to request new caps from the
* MDS). We do, however, need to inform the MDS (asynchronously)
@@ -207,24 +205,29 @@ out:
/*
- * Do a lookup + open with a single request.
- *
- * If this succeeds, but some subsequent check in the vfs
- * may_open() fails, the struct *file gets cleaned up (i.e.
- * ceph_release gets called). So fear not!
+ * Do a lookup + open with a single request. If we get a non-existent
+ * file or symlink, return 1 so the VFS can retry.
*/
-int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
+int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
struct file *file, unsigned flags, umode_t mode,
int *opened)
{
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
struct ceph_mds_client *mdsc = fsc->mdsc;
struct ceph_mds_request *req;
- struct dentry *ret;
+ struct dentry *dn;
int err;
- dout("ceph_lookup_open dentry %p '%.*s' flags %d mode 0%o\n",
- dentry, dentry->d_name.len, dentry->d_name.name, flags, mode);
+ dout("atomic_open %p dentry %p '%.*s' %s flags %d mode 0%o\n",
+ dir, dentry, dentry->d_name.len, dentry->d_name.name,
+ d_unhashed(dentry) ? "unhashed" : "hashed", flags, mode);
+
+ if (dentry->d_name.len > NAME_MAX)
+ return -ENAMETOOLONG;
+
+ err = ceph_init_dentry(dentry);
+ if (err < 0)
+ return err;
/* do the open */
req = prepare_open_request(dir->i_sb, flags, mode);
@@ -241,22 +244,31 @@ int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
(flags & (O_CREAT|O_TRUNC)) ? dir : NULL,
req);
err = ceph_handle_snapdir(req, dentry, err);
- if (err)
- goto out;
- if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
+ if (err == 0 && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry)
err = ceph_handle_notrace_create(dir, dentry);
- if (err)
- goto out;
- err = finish_open(file, req->r_dentry, ceph_open, opened);
-out:
- ret = ceph_finish_lookup(req, dentry, err);
- ceph_mdsc_put_request(req);
- dout("ceph_lookup_open result=%p\n", ret);
- if (IS_ERR(ret))
- return PTR_ERR(ret);
+ if (d_unhashed(dentry)) {
+ dn = ceph_finish_lookup(req, dentry, err);
+ if (IS_ERR(dn))
+ err = PTR_ERR(dn);
+ } else {
+ /* we were given a hashed negative dentry */
+ dn = NULL;
+ }
+ if (err)
+ goto out_err;
+ if (dn || dentry->d_inode == NULL || S_ISLNK(dentry->d_inode->i_mode)) {
+ /* make vfs retry on splice, ENOENT, or symlink */
+ dout("atomic_open finish_no_open on dn %p\n", dn);
+ err = finish_no_open(file, dn);
+ } else {
+ dout("atomic_open finish_open on dn %p\n", dn);
+ err = finish_open(file, dentry, ceph_open, opened);
+ }
- dput(ret);
+out_err:
+ ceph_mdsc_put_request(req);
+ dout("atomic_open result=%d\n", err);
return err;
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 9fff9f3b17e4..4b5762ef7c2b 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -992,11 +992,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
if (rinfo->head->is_dentry) {
struct inode *dir = req->r_locked_dir;
- err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag,
- session, req->r_request_started, -1,
- &req->r_caps_reservation);
- if (err < 0)
- return err;
+ if (dir) {
+ err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag,
+ session, req->r_request_started, -1,
+ &req->r_caps_reservation);
+ if (err < 0)
+ return err;
+ } else {
+ WARN_ON_ONCE(1);
+ }
}
/*
@@ -1004,6 +1008,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req,
* will have trouble splicing in the virtual snapdir later
*/
if (rinfo->head->is_dentry && !req->r_aborted &&
+ req->r_locked_dir &&
(rinfo->head->is_target || strncmp(req->r_dentry->d_name.name,
fsc->mount_options->snapdir_name,
req->r_dentry->d_name.len))) {
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index 8e3fb69fbe62..1396ceb46797 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -42,7 +42,8 @@ static long __validate_layout(struct ceph_mds_client *mdsc,
/* validate striping parameters */
if ((l->object_size & ~PAGE_MASK) ||
(l->stripe_unit & ~PAGE_MASK) ||
- ((unsigned)l->object_size % (unsigned)l->stripe_unit))
+ (l->stripe_unit != 0 &&
+ ((unsigned)l->object_size % (unsigned)l->stripe_unit)))
return -EINVAL;
/* make sure it's a valid data pool */
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index ebc95cc652be..66ebe720e40d 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -806,9 +806,9 @@ extern int ceph_copy_from_page_vector(struct page **pages,
loff_t off, size_t len);
extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags);
extern int ceph_open(struct inode *inode, struct file *file);
-extern int ceph_lookup_open(struct inode *dir, struct dentry *dentry,
- struct file *od, unsigned flags,
- umode_t mode, int *opened);
+extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
+ struct file *file, unsigned flags, umode_t mode,
+ int *opened);
extern int ceph_release(struct inode *inode, struct file *filp);
/* dir.c */
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 7dab9c04ad52..53cf2aabce87 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -328,7 +328,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
}
ctoUTF16_out:
- return i;
+ return j;
}
#ifdef CONFIG_CIFS_SMB2
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 497da5ce704c..977dc0e85ccb 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -246,6 +246,16 @@ struct smb_version_operations {
bool (*can_echo)(struct TCP_Server_Info *);
/* send echo request */
int (*echo)(struct TCP_Server_Info *);
+ /* create directory */
+ int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
+ /* set info on created directory */
+ void (*mkdir_setinfo)(struct inode *, const char *,
+ struct cifs_sb_info *, struct cifs_tcon *,
+ const unsigned int);
+ /* remove directory */
+ int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
};
struct smb_version_values {
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index cf7fb185103c..f1bbf8305d3a 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -289,18 +289,15 @@ extern int CIFSSMBUnixSetFileInfo(const unsigned int xid,
u16 fid, u32 pid_of_opener);
extern int CIFSSMBUnixSetPathInfo(const unsigned int xid,
- struct cifs_tcon *tcon, char *file_name,
+ struct cifs_tcon *tcon, const char *file_name,
const struct cifs_unix_set_info_args *args,
const struct nls_table *nls_codepage,
- int remap_special_chars);
+ int remap);
extern int CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon,
- const char *newName,
- const struct nls_table *nls_codepage,
- int remap_special_chars);
+ const char *name, struct cifs_sb_info *cifs_sb);
extern int CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon,
- const char *name, const struct nls_table *nls_codepage,
- int remap_special_chars);
+ const char *name, struct cifs_sb_info *cifs_sb);
extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, __u16 type,
const struct nls_table *nls_codepage,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index cabc7a01f5df..f0cf934ba877 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -948,15 +948,15 @@ DelFileRetry:
}
int
-CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon,
- const char *dirName, const struct nls_table *nls_codepage,
- int remap)
+CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
{
DELETE_DIRECTORY_REQ *pSMB = NULL;
DELETE_DIRECTORY_RSP *pSMBr = NULL;
int rc = 0;
int bytes_returned;
int name_len;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
cFYI(1, "In CIFSSMBRmDir");
RmDirRetry:
@@ -966,14 +966,15 @@ RmDirRetry:
return rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
- name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, dirName,
- PATH_MAX, nls_codepage, remap);
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
name_len++; /* trailing null */
name_len *= 2;
} else { /* BB improve check for buffer overruns BB */
- name_len = strnlen(dirName, PATH_MAX);
+ name_len = strnlen(name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->DirName, dirName, name_len);
+ strncpy(pSMB->DirName, name, name_len);
}
pSMB->BufferFormat = 0x04;
@@ -992,14 +993,15 @@ RmDirRetry:
}
int
-CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon,
- const char *name, const struct nls_table *nls_codepage, int remap)
+CIFSSMBMkDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
{
int rc = 0;
CREATE_DIRECTORY_REQ *pSMB = NULL;
CREATE_DIRECTORY_RSP *pSMBr = NULL;
int bytes_returned;
int name_len;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
cFYI(1, "In CIFSSMBMkDir");
MkDirRetry:
@@ -1010,7 +1012,8 @@ MkDirRetry:
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
- PATH_MAX, nls_codepage, remap);
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
name_len++; /* trailing null */
name_len *= 2;
} else { /* BB improve check for buffer overruns BB */
@@ -1573,9 +1576,14 @@ cifs_readv_callback(struct mid_q_entry *mid)
/* result already set, check signature */
if (server->sec_mode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
- if (cifs_verify_signature(rdata->iov, rdata->nr_iov,
- server, mid->sequence_number + 1))
- cERROR(1, "Unexpected SMB signature");
+ int rc = 0;
+
+ rc = cifs_verify_signature(rdata->iov, rdata->nr_iov,
+ server,
+ mid->sequence_number + 1);
+ if (rc)
+ cERROR(1, "SMB signature verification returned "
+ "error = %d", rc);
}
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->bytes);
@@ -5943,7 +5951,7 @@ CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
int
CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
- char *fileName,
+ const char *file_name,
const struct cifs_unix_set_info_args *args,
const struct nls_table *nls_codepage, int remap)
{
@@ -5964,14 +5972,14 @@ setPermsRetry:
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
- cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
PATH_MAX, nls_codepage, remap);
name_len++; /* trailing null */
name_len *= 2;
} else { /* BB improve the check for buffer overruns BB */
- name_len = strnlen(fileName, PATH_MAX);
+ name_len = strnlen(file_name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->FileName, fileName, name_len);
+ strncpy(pSMB->FileName, file_name, name_len);
}
params = 6 + name_len;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index cbe709ad6663..781025be48bc 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -356,19 +356,12 @@ cifs_create_get_file_info:
cifs_create_set_dentry:
if (rc != 0) {
cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
+ CIFSSMBClose(xid, tcon, *fileHandle);
goto out;
}
d_drop(direntry);
d_add(direntry, newinode);
- /* ENOENT for create? How weird... */
- rc = -ENOENT;
- if (!newinode) {
- CIFSSMBClose(xid, tcon, *fileHandle);
- goto out;
- }
- rc = 0;
-
out:
kfree(buf);
kfree(full_path);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 9154192b0683..71e9ad9f5961 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -917,7 +917,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
if (!buf) {
mutex_unlock(&cinode->lock_mutex);
free_xid(xid);
- return rc;
+ return -ENOMEM;
}
for (i = 0; i < 2; i++) {
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 35cb6a374a45..cb79c7edecb0 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -124,10 +124,10 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
{
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- unsigned long oldtime = cifs_i->time;
cifs_revalidate_cache(inode, fattr);
+ spin_lock(&inode->i_lock);
inode->i_atime = fattr->cf_atime;
inode->i_mtime = fattr->cf_mtime;
inode->i_ctime = fattr->cf_ctime;
@@ -148,9 +148,6 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
else
cifs_i->time = jiffies;
- cFYI(1, "inode 0x%p old_time=%ld new_time=%ld", inode,
- oldtime, cifs_i->time);
-
cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING;
cifs_i->server_eof = fattr->cf_eof;
@@ -158,7 +155,6 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
* Can't safely change the file size here if the client is writing to
* it due to potential races.
*/
- spin_lock(&inode->i_lock);
if (is_size_safe_to_change(cifs_i, fattr->cf_eof)) {
i_size_write(inode, fattr->cf_eof);
@@ -859,12 +855,14 @@ struct inode *cifs_root_iget(struct super_block *sb)
if (rc && tcon->ipc) {
cFYI(1, "ipc connection - fake read inode");
+ spin_lock(&inode->i_lock);
inode->i_mode |= S_IFDIR;
set_nlink(inode, 2);
inode->i_op = &cifs_ipc_inode_ops;
inode->i_fop = &simple_dir_operations;
inode->i_uid = cifs_sb->mnt_uid;
inode->i_gid = cifs_sb->mnt_gid;
+ spin_unlock(&inode->i_lock);
} else if (rc) {
iget_failed(inode);
inode = ERR_PTR(rc);
@@ -1110,6 +1108,15 @@ undo_setattr:
goto out_close;
}
+/* copied from fs/nfs/dir.c with small changes */
+static void
+cifs_drop_nlink(struct inode *inode)
+{
+ spin_lock(&inode->i_lock);
+ if (inode->i_nlink > 0)
+ drop_nlink(inode);
+ spin_unlock(&inode->i_lock);
+}
/*
* If dentry->d_inode is null (usually meaning the cached dentry
@@ -1166,13 +1173,13 @@ retry_std_delete:
psx_del_no_retry:
if (!rc) {
if (inode)
- drop_nlink(inode);
+ cifs_drop_nlink(inode);
} else if (rc == -ENOENT) {
d_drop(dentry);
} else if (rc == -ETXTBSY) {
rc = cifs_rename_pending_delete(full_path, dentry, xid);
if (rc == 0)
- drop_nlink(inode);
+ cifs_drop_nlink(inode);
} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
if (attrs == NULL) {
@@ -1219,16 +1226,154 @@ unlink_out:
return rc;
}
+static int
+cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
+ const char *full_path, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid)
+{
+ int rc = 0;
+ struct inode *newinode = NULL;
+
+ if (tcon->unix_ext)
+ rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ xid);
+ else
+ rc = cifs_get_inode_info(&newinode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+ if (rc)
+ return rc;
+
+ d_instantiate(dentry, newinode);
+ /*
+ * setting nlink not necessary except in cases where we failed to get it
+ * from the server or was set bogus
+ */
+ spin_lock(&dentry->d_inode->i_lock);
+ if ((dentry->d_inode) && (dentry->d_inode->i_nlink < 2))
+ set_nlink(dentry->d_inode, 2);
+ spin_unlock(&dentry->d_inode->i_lock);
+ mode &= ~current_umask();
+ /* must turn on setgid bit if parent dir has it */
+ if (inode->i_mode & S_ISGID)
+ mode |= S_ISGID;
+
+ if (tcon->unix_ext) {
+ struct cifs_unix_set_info_args args = {
+ .mode = mode,
+ .ctime = NO_CHANGE_64,
+ .atime = NO_CHANGE_64,
+ .mtime = NO_CHANGE_64,
+ .device = 0,
+ };
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ args.uid = (__u64)current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ args.gid = (__u64)inode->i_gid;
+ else
+ args.gid = (__u64)current_fsgid();
+ } else {
+ args.uid = NO_CHANGE_64;
+ args.gid = NO_CHANGE_64;
+ }
+ CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ } else {
+ struct TCP_Server_Info *server = tcon->ses->server;
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
+ (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
+ server->ops->mkdir_setinfo(newinode, full_path, cifs_sb,
+ tcon, xid);
+ if (dentry->d_inode) {
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ dentry->d_inode->i_mode = (mode | S_IFDIR);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ dentry->d_inode->i_uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ dentry->d_inode->i_gid = inode->i_gid;
+ else
+ dentry->d_inode->i_gid =
+ current_fsgid();
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+cifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode,
+ const char *full_path, struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid)
+{
+ int rc = 0;
+ u32 oplock = 0;
+ FILE_UNIX_BASIC_INFO *info = NULL;
+ struct inode *newinode = NULL;
+ struct cifs_fattr fattr;
+
+ info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
+ if (info == NULL) {
+ rc = -ENOMEM;
+ goto posix_mkdir_out;
+ }
+
+ mode &= ~current_umask();
+ rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode,
+ NULL /* netfid */, info, &oplock, full_path,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == -EOPNOTSUPP)
+ goto posix_mkdir_out;
+ else if (rc) {
+ cFYI(1, "posix mkdir returned 0x%x", rc);
+ d_drop(dentry);
+ goto posix_mkdir_out;
+ }
+
+ if (info->Type == cpu_to_le32(-1))
+ /* no return info, go query for it */
+ goto posix_mkdir_get_info;
+ /*
+ * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if
+ * need to set uid/gid.
+ */
+
+ cifs_unix_basic_to_fattr(&fattr, info, cifs_sb);
+ cifs_fill_uniqueid(inode->i_sb, &fattr);
+ newinode = cifs_iget(inode->i_sb, &fattr);
+ if (!newinode)
+ goto posix_mkdir_get_info;
+
+ d_instantiate(dentry, newinode);
+
+#ifdef CONFIG_CIFS_DEBUG2
+ cFYI(1, "instantiated dentry %p %s to inode %p", dentry,
+ dentry->d_name.name, newinode);
+
+ if (newinode->i_nlink != 2)
+ cFYI(1, "unexpected number of links %d", newinode->i_nlink);
+#endif
+
+posix_mkdir_out:
+ kfree(info);
+ return rc;
+posix_mkdir_get_info:
+ rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon,
+ xid);
+ goto posix_mkdir_out;
+}
+
int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode)
{
- int rc = 0, tmprc;
+ int rc = 0;
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
- char *full_path = NULL;
- struct inode *newinode = NULL;
- struct cifs_fattr fattr;
+ struct TCP_Server_Info *server;
+ char *full_path;
cFYI(1, "In cifs_mkdir, mode = 0x%hx inode = 0x%p", mode, inode);
@@ -1248,145 +1393,29 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode)
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
- u32 oplock = 0;
- FILE_UNIX_BASIC_INFO *pInfo =
- kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
- if (pInfo == NULL) {
- rc = -ENOMEM;
+ rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb,
+ tcon, xid);
+ if (rc != -EOPNOTSUPP)
goto mkdir_out;
- }
-
- mode &= ~current_umask();
- rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT,
- mode, NULL /* netfid */, pInfo, &oplock,
- full_path, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (rc == -EOPNOTSUPP) {
- kfree(pInfo);
- goto mkdir_retry_old;
- } else if (rc) {
- cFYI(1, "posix mkdir returned 0x%x", rc);
- d_drop(direntry);
- } else {
- if (pInfo->Type == cpu_to_le32(-1)) {
- /* no return info, go query for it */
- kfree(pInfo);
- goto mkdir_get_info;
- }
-/*BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if need
- to set uid/gid */
-
- cifs_unix_basic_to_fattr(&fattr, pInfo, cifs_sb);
- cifs_fill_uniqueid(inode->i_sb, &fattr);
- newinode = cifs_iget(inode->i_sb, &fattr);
- if (!newinode) {
- kfree(pInfo);
- goto mkdir_get_info;
- }
-
- d_instantiate(direntry, newinode);
+ }
-#ifdef CONFIG_CIFS_DEBUG2
- cFYI(1, "instantiated dentry %p %s to inode %p",
- direntry, direntry->d_name.name, newinode);
+ server = tcon->ses->server;
- if (newinode->i_nlink != 2)
- cFYI(1, "unexpected number of links %d",
- newinode->i_nlink);
-#endif
- }
- kfree(pInfo);
+ if (!server->ops->mkdir) {
+ rc = -ENOSYS;
goto mkdir_out;
}
-mkdir_retry_old:
+
/* BB add setting the equivalent of mode via CreateX w/ACLs */
- rc = CIFSSMBMkDir(xid, tcon, full_path, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = server->ops->mkdir(xid, tcon, full_path, cifs_sb);
if (rc) {
cFYI(1, "cifs_mkdir returned 0x%x", rc);
d_drop(direntry);
- } else {
-mkdir_get_info:
- if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path,
- inode->i_sb, xid);
- else
- rc = cifs_get_inode_info(&newinode, full_path, NULL,
- inode->i_sb, xid, NULL);
-
- d_instantiate(direntry, newinode);
- /* setting nlink not necessary except in cases where we
- * failed to get it from the server or was set bogus */
- if ((direntry->d_inode) && (direntry->d_inode->i_nlink < 2))
- set_nlink(direntry->d_inode, 2);
-
- mode &= ~current_umask();
- /* must turn on setgid bit if parent dir has it */
- if (inode->i_mode & S_ISGID)
- mode |= S_ISGID;
-
- if (tcon->unix_ext) {
- struct cifs_unix_set_info_args args = {
- .mode = mode,
- .ctime = NO_CHANGE_64,
- .atime = NO_CHANGE_64,
- .mtime = NO_CHANGE_64,
- .device = 0,
- };
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- args.uid = (__u64)current_fsuid();
- if (inode->i_mode & S_ISGID)
- args.gid = (__u64)inode->i_gid;
- else
- args.gid = (__u64)current_fsgid();
- } else {
- args.uid = NO_CHANGE_64;
- args.gid = NO_CHANGE_64;
- }
- CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- } else {
- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
- (mode & S_IWUGO) == 0) {
- FILE_BASIC_INFO pInfo;
- struct cifsInodeInfo *cifsInode;
- u32 dosattrs;
-
- memset(&pInfo, 0, sizeof(pInfo));
- cifsInode = CIFS_I(newinode);
- dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
- pInfo.Attributes = cpu_to_le32(dosattrs);
- tmprc = CIFSSMBSetPathInfo(xid, tcon,
- full_path, &pInfo,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (tmprc == 0)
- cifsInode->cifsAttrs = dosattrs;
- }
- if (direntry->d_inode) {
- if (cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_DYNPERM)
- direntry->d_inode->i_mode =
- (mode | S_IFDIR);
-
- if (cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_SET_UID) {
- direntry->d_inode->i_uid =
- current_fsuid();
- if (inode->i_mode & S_ISGID)
- direntry->d_inode->i_gid =
- inode->i_gid;
- else
- direntry->d_inode->i_gid =
- current_fsgid();
- }
- }
- }
+ goto mkdir_out;
}
+
+ rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon,
+ xid);
mkdir_out:
/*
* Force revalidate to get parent dir info when needed since cached
@@ -1405,7 +1434,8 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
unsigned int xid;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
- struct cifs_tcon *pTcon;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
char *full_path = NULL;
struct cifsInodeInfo *cifsInode;
@@ -1425,10 +1455,16 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
rc = PTR_ERR(tlink);
goto rmdir_exit;
}
- pTcon = tlink_tcon(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->rmdir) {
+ rc = -ENOSYS;
+ cifs_put_tlink(tlink);
+ goto rmdir_exit;
+ }
- rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
cifs_put_tlink(tlink);
if (!rc) {
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index 09e4b3ae4564..e6ce3b112875 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -433,7 +433,9 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
if (old_file->d_inode) {
cifsInode = CIFS_I(old_file->d_inode);
if (rc == 0) {
+ spin_lock(&old_file->d_inode->i_lock);
inc_nlink(old_file->d_inode);
+ spin_unlock(&old_file->d_inode->i_lock);
/* BB should we make this contingent on superblock flag NOATIME? */
/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
/* parent dir timestamps will update from srv
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index c40356d24c5c..3129ac74b819 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -586,6 +586,27 @@ cifs_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
#endif
}
+static void
+cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+{
+ FILE_BASIC_INFO info;
+ struct cifsInodeInfo *cifsInode;
+ u32 dosattrs;
+ int rc;
+
+ memset(&info, 0, sizeof(info));
+ cifsInode = CIFS_I(inode);
+ dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
+ info.Attributes = cpu_to_le32(dosattrs);
+ rc = CIFSSMBSetPathInfo(xid, tcon, full_path, &info, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0)
+ cifsInode->cifsAttrs = dosattrs;
+}
+
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
@@ -620,6 +641,9 @@ struct smb_version_operations smb1_operations = {
.get_srv_inum = cifs_get_srv_inum,
.build_path_to_root = cifs_build_path_to_root,
.echo = CIFSSMBEcho,
+ .mkdir = CIFSSMBMkDir,
+ .mkdir_setinfo = cifs_mkdir_setinfo,
+ .rmdir = CIFSSMBRmDir,
};
struct smb_version_values smb1_values = {
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 1ba5c405315c..2aa5cb08c526 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -122,3 +122,42 @@ out:
kfree(smb2_data);
return rc;
}
+
+int
+smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0,
+ CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR);
+}
+
+void
+smb2_mkdir_setinfo(struct inode *inode, const char *name,
+ struct cifs_sb_info *cifs_sb, struct cifs_tcon *tcon,
+ const unsigned int xid)
+{
+ FILE_BASIC_INFO data;
+ struct cifsInodeInfo *cifs_i;
+ u32 dosattrs;
+ int tmprc;
+
+ memset(&data, 0, sizeof(data));
+ cifs_i = CIFS_I(inode);
+ dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
+ data.Attributes = cpu_to_le32(dosattrs);
+ tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name,
+ FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0,
+ CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO);
+ if (tmprc == 0)
+ cifs_i->cifsAttrs = dosattrs;
+}
+
+int
+smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ 0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
+ NULL, SMB2_OP_DELETE);
+}
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index a4ff5d547554..e4d3b9964167 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -52,7 +52,8 @@ check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
cERROR(1, "Bad protocol string signature header %x",
*(unsigned int *) hdr->ProtocolId);
if (mid != hdr->MessageId)
- cERROR(1, "Mids do not match");
+ cERROR(1, "Mids do not match: %llu and %llu", mid,
+ hdr->MessageId);
}
cERROR(1, "Bad SMB detected. The Mid=%llu", hdr->MessageId);
return 1;
@@ -107,7 +108,7 @@ smb2_check_message(char *buf, unsigned int length)
* ie Validate the wct via smb2_struct_sizes table above
*/
- if (length < 2 + sizeof(struct smb2_hdr)) {
+ if (length < sizeof(struct smb2_pdu)) {
if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
pdu->StructureSize2 = 0;
/*
@@ -121,15 +122,15 @@ smb2_check_message(char *buf, unsigned int length)
return 1;
}
if (len > CIFSMaxBufSize + MAX_SMB2_HDR_SIZE - 4) {
- cERROR(1, "SMB length greater than maximum, mid=%lld", mid);
+ cERROR(1, "SMB length greater than maximum, mid=%llu", mid);
return 1;
}
if (check_smb2_hdr(hdr, mid))
return 1;
- if (hdr->StructureSize != SMB2_HEADER_SIZE) {
- cERROR(1, "Illegal structure size %d",
+ if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
+ cERROR(1, "Illegal structure size %u",
le16_to_cpu(hdr->StructureSize));
return 1;
}
@@ -161,8 +162,9 @@ smb2_check_message(char *buf, unsigned int length)
if (4 + len != clc_len) {
cFYI(1, "Calculated size %u length %u mismatch mid %llu",
clc_len, 4 + len, mid);
- if (clc_len == 4 + len + 1) /* BB FIXME (fix samba) */
- return 0; /* BB workaround Samba 3 bug SessSetup rsp */
+ /* server can return one byte more */
+ if (clc_len == 4 + len + 1)
+ return 0;
return 1;
}
return 0;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 410cf925ea26..826209bf3684 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -318,6 +318,9 @@ struct smb_version_operations smb21_operations = {
.query_path_info = smb2_query_path_info,
.get_srv_inum = smb2_get_srv_inum,
.build_path_to_root = smb2_build_path_to_root,
+ .mkdir = smb2_mkdir,
+ .mkdir_setinfo = smb2_mkdir_setinfo,
+ .rmdir = smb2_rmdir,
};
struct smb_version_values smb21_values = {
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index f37a1b41b402..15dc8eea8273 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -87,10 +87,6 @@
#define SMB2_PROTO_NUMBER __constant_cpu_to_le32(0x424d53fe)
-#define SMB2_HEADER_SIZE __constant_le16_to_cpu(64)
-
-#define SMB2_ERROR_STRUCTURE_SIZE2 __constant_le16_to_cpu(9)
-
/*
* SMB2 Header Definition
*
@@ -99,6 +95,9 @@
* "PDU" : "Protocol Data Unit" (ie a network "frame")
*
*/
+
+#define SMB2_HEADER_STRUCTURE_SIZE __constant_cpu_to_le16(64)
+
struct smb2_hdr {
__be32 smb2_buf_length; /* big endian on wire */
/* length is only two or three bytes - with
@@ -140,6 +139,9 @@ struct smb2_pdu {
* command code name for the struct. Note that structures must be packed.
*
*/
+
+#define SMB2_ERROR_STRUCTURE_SIZE2 __constant_cpu_to_le16(9)
+
struct smb2_err_rsp {
struct smb2_hdr hdr;
__le16 StructureSize;
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 902bbe2b5ad3..bfaa7b148afd 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -52,6 +52,14 @@ extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path, FILE_ALL_INFO *data,
bool *adjust_tz);
+extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
+ struct cifs_sb_info *cifs_sb,
+ struct cifs_tcon *tcon, const unsigned int xid);
+extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+
/*
* SMB2 Worker functions - most of protocol specific implementation details
* are contained within these calls.
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 83867ef348df..d9b639b95fa8 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -503,13 +503,16 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
/* convert the length into a more usable form */
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
struct kvec iov;
+ int rc = 0;
iov.iov_base = mid->resp_buf;
iov.iov_len = len;
/* FIXME: add code to kill session */
- if (cifs_verify_signature(&iov, 1, server,
- mid->sequence_number + 1) != 0)
- cERROR(1, "Unexpected SMB signature");
+ rc = cifs_verify_signature(&iov, 1, server,
+ mid->sequence_number + 1);
+ if (rc)
+ cERROR(1, "SMB signature verification returned error = "
+ "%d", rc);
}
/* BB special case reconnect tid and uid here? */
diff --git a/fs/compat.c b/fs/compat.c
index 6161255fac45..1bdb350ea5d3 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1155,11 +1155,14 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec,
struct file *file;
int fput_needed;
ssize_t ret;
+ loff_t pos;
file = fget_light(fd, &fput_needed);
if (!file)
return -EBADF;
- ret = compat_readv(file, vec, vlen, &file->f_pos);
+ pos = file->f_pos;
+ ret = compat_readv(file, vec, vlen, &pos);
+ file->f_pos = pos;
fput_light(file, fput_needed);
return ret;
}
@@ -1221,11 +1224,14 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec,
struct file *file;
int fput_needed;
ssize_t ret;
+ loff_t pos;
file = fget_light(fd, &fput_needed);
if (!file)
return -EBADF;
- ret = compat_writev(file, vec, vlen, &file->f_pos);
+ pos = file->f_pos;
+ ret = compat_writev(file, vec, vlen, &pos);
+ file->f_pos = pos;
fput_light(file, fput_needed);
return ret;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 8086636bf796..693f95bf1cae 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -389,7 +389,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
* Inform try_to_ascend() that we are no longer attached to the
* dentry tree
*/
- dentry->d_flags |= DCACHE_DISCONNECTED;
+ dentry->d_flags |= DCACHE_DENTRY_KILLED;
if (parent)
spin_unlock(&parent->d_lock);
dentry_iput(dentry);
@@ -1048,7 +1048,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
* or deletion
*/
if (new != old->d_parent ||
- (old->d_flags & DCACHE_DISCONNECTED) ||
+ (old->d_flags & DCACHE_DENTRY_KILLED) ||
(!locked && read_seqretry(&rename_lock, seq))) {
spin_unlock(&new->d_lock);
new = NULL;
@@ -1134,6 +1134,8 @@ positive:
return 1;
rename_retry:
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
@@ -1141,7 +1143,7 @@ rename_retry:
EXPORT_SYMBOL(have_submounts);
/*
- * Search the dentry child list for the specified parent,
+ * Search the dentry child list of the specified parent,
* and move any unused dentries to the end of the unused
* list for prune_dcache(). We descend to the next level
* whenever the d_subdirs list is non-empty and continue
@@ -1236,6 +1238,8 @@ out:
rename_retry:
if (found)
return found;
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
@@ -3035,6 +3039,8 @@ resume:
return;
rename_retry:
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 2340f6978d6e..c5ca6ae5a30c 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -526,73 +526,51 @@ struct array_data {
u32 elements;
};
-static int u32_array_open(struct inode *inode, struct file *file)
-{
- file->private_data = NULL;
- return nonseekable_open(inode, file);
-}
-
-static size_t format_array(char *buf, size_t bufsize, const char *fmt,
- u32 *array, u32 array_size)
+static size_t u32_format_array(char *buf, size_t bufsize,
+ u32 *array, int array_size)
{
size_t ret = 0;
- u32 i;
- for (i = 0; i < array_size; i++) {
+ while (--array_size >= 0) {
size_t len;
+ char term = array_size ? ' ' : '\n';
- len = snprintf(buf, bufsize, fmt, array[i]);
- len++; /* ' ' or '\n' */
+ len = snprintf(buf, bufsize, "%u%c", *array++, term);
ret += len;
- if (buf) {
- buf += len;
- bufsize -= len;
- buf[-1] = (i == array_size-1) ? '\n' : ' ';
- }
+ buf += len;
+ bufsize -= len;
}
-
- ret++; /* \0 */
- if (buf)
- *buf = '\0';
-
return ret;
}
-static char *format_array_alloc(const char *fmt, u32 *array,
- u32 array_size)
+static int u32_array_open(struct inode *inode, struct file *file)
{
- size_t len = format_array(NULL, 0, fmt, array, array_size);
- char *ret;
-
- ret = kmalloc(len, GFP_KERNEL);
- if (ret == NULL)
- return NULL;
+ struct array_data *data = inode->i_private;
+ int size, elements = data->elements;
+ char *buf;
+
+ /*
+ * Max size:
+ * - 10 digits + ' '/'\n' = 11 bytes per number
+ * - terminating NUL character
+ */
+ size = elements*11;
+ buf = kmalloc(size+1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ buf[size] = 0;
+
+ file->private_data = buf;
+ u32_format_array(buf, size, data->array, data->elements);
- format_array(ret, len, fmt, array, array_size);
- return ret;
+ return nonseekable_open(inode, file);
}
static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
loff_t *ppos)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- struct array_data *data = inode->i_private;
- size_t size;
-
- if (*ppos == 0) {
- if (file->private_data) {
- kfree(file->private_data);
- file->private_data = NULL;
- }
-
- file->private_data = format_array_alloc("%u", data->array,
- data->elements);
- }
-
- size = 0;
- if (file->private_data)
- size = strlen(file->private_data);
+ size_t size = strlen(file->private_data);
return simple_read_from_buffer(buf, len, ppos,
file->private_data, size);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 1faf4cb56f39..f86c720dba0e 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -1062,6 +1062,7 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
unsigned long user_addr;
size_t bytes;
struct buffer_head map_bh = { 0, };
+ struct blk_plug plug;
if (rw & WRITE)
rw = WRITE_ODIRECT;
@@ -1177,6 +1178,8 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
PAGE_SIZE - user_addr / PAGE_SIZE);
}
+ blk_start_plug(&plug);
+
for (seg = 0; seg < nr_segs; seg++) {
user_addr = (unsigned long)iov[seg].iov_base;
sdio.size += bytes = iov[seg].iov_len;
@@ -1235,6 +1238,8 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
if (sdio.bio)
dio_bio_submit(dio, &sdio);
+ blk_finish_plug(&plug);
+
/*
* It is possible that, we return short IO due to end of file.
* In that case, we need to release all the pages we got hold on.
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 989e034f02bd..cfb4b9fed520 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -385,8 +385,6 @@ struct ecryptfs_msg_ctx {
struct mutex mux;
};
-struct ecryptfs_daemon;
-
struct ecryptfs_daemon {
#define ECRYPTFS_DAEMON_IN_READ 0x00000001
#define ECRYPTFS_DAEMON_IN_POLL 0x00000002
@@ -394,10 +392,7 @@ struct ecryptfs_daemon {
#define ECRYPTFS_DAEMON_MISCDEV_OPEN 0x00000008
u32 flags;
u32 num_queued_msg_ctx;
- struct pid *pid;
- uid_t euid;
- struct user_namespace *user_ns;
- struct task_struct *task;
+ struct file *file;
struct mutex mux;
struct list_head msg_ctx_out_queue;
wait_queue_head_t wait;
@@ -554,6 +549,8 @@ extern struct kmem_cache *ecryptfs_key_tfm_cache;
struct inode *ecryptfs_get_inode(struct inode *lower_inode,
struct super_block *sb);
void ecryptfs_i_size_init(const char *page_virt, struct inode *inode);
+int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
+ struct inode *ecryptfs_inode);
int ecryptfs_decode_and_decrypt_filename(char **decrypted_name,
size_t *decrypted_name_size,
struct dentry *ecryptfs_dentry,
@@ -607,13 +604,8 @@ int
ecryptfs_setxattr(struct dentry *dentry, const char *name, const void *value,
size_t size, int flags);
int ecryptfs_read_xattr_region(char *page_virt, struct inode *ecryptfs_inode);
-int ecryptfs_process_helo(uid_t euid, struct user_namespace *user_ns,
- struct pid *pid);
-int ecryptfs_process_quit(uid_t euid, struct user_namespace *user_ns,
- struct pid *pid);
-int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
- struct user_namespace *user_ns, struct pid *pid,
- u32 seq);
+int ecryptfs_process_response(struct ecryptfs_daemon *daemon,
+ struct ecryptfs_message *msg, u32 seq);
int ecryptfs_send_message(char *data, int data_len,
struct ecryptfs_msg_ctx **msg_ctx);
int ecryptfs_wait_for_response(struct ecryptfs_msg_ctx *msg_ctx,
@@ -658,8 +650,7 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs,
struct inode *ecryptfs_inode);
struct page *ecryptfs_get_locked_page(struct inode *inode, loff_t index);
int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon);
-int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon, uid_t euid,
- struct user_namespace *user_ns);
+int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon);
int ecryptfs_parse_packet_length(unsigned char *data, size_t *size,
size_t *length_size);
int ecryptfs_write_packet_length(char *dest, size_t size,
@@ -671,8 +662,7 @@ int ecryptfs_send_miscdev(char *data, size_t data_size,
u16 msg_flags, struct ecryptfs_daemon *daemon);
void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx);
int
-ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
- struct user_namespace *user_ns, struct pid *pid);
+ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, struct file *file);
int ecryptfs_init_kthread(void);
void ecryptfs_destroy_kthread(void);
int ecryptfs_privileged_open(struct file **lower_file,
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index 2b17f2f9b121..d45ba4568128 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -138,29 +138,50 @@ out:
return rc;
}
-static void ecryptfs_vma_close(struct vm_area_struct *vma)
-{
- filemap_write_and_wait(vma->vm_file->f_mapping);
-}
-
-static const struct vm_operations_struct ecryptfs_file_vm_ops = {
- .close = ecryptfs_vma_close,
- .fault = filemap_fault,
-};
+struct kmem_cache *ecryptfs_file_info_cache;
-static int ecryptfs_file_mmap(struct file *file, struct vm_area_struct *vma)
+static int read_or_initialize_metadata(struct dentry *dentry)
{
+ struct inode *inode = dentry->d_inode;
+ struct ecryptfs_mount_crypt_stat *mount_crypt_stat;
+ struct ecryptfs_crypt_stat *crypt_stat;
int rc;
- rc = generic_file_mmap(file, vma);
+ crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
+ mount_crypt_stat = &ecryptfs_superblock_to_private(
+ inode->i_sb)->mount_crypt_stat;
+ mutex_lock(&crypt_stat->cs_mutex);
+
+ if (crypt_stat->flags & ECRYPTFS_POLICY_APPLIED &&
+ crypt_stat->flags & ECRYPTFS_KEY_VALID) {
+ rc = 0;
+ goto out;
+ }
+
+ rc = ecryptfs_read_metadata(dentry);
if (!rc)
- vma->vm_ops = &ecryptfs_file_vm_ops;
+ goto out;
+
+ if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED) {
+ crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
+ | ECRYPTFS_ENCRYPTED);
+ rc = 0;
+ goto out;
+ }
+
+ if (!(mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED) &&
+ !i_size_read(ecryptfs_inode_to_lower(inode))) {
+ rc = ecryptfs_initialize_file(dentry, inode);
+ if (!rc)
+ goto out;
+ }
+ rc = -EIO;
+out:
+ mutex_unlock(&crypt_stat->cs_mutex);
return rc;
}
-struct kmem_cache *ecryptfs_file_info_cache;
-
/**
* ecryptfs_open
* @inode: inode speciying file to open
@@ -236,32 +257,9 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
rc = 0;
goto out;
}
- mutex_lock(&crypt_stat->cs_mutex);
- if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)
- || !(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
- rc = ecryptfs_read_metadata(ecryptfs_dentry);
- if (rc) {
- ecryptfs_printk(KERN_DEBUG,
- "Valid headers not found\n");
- if (!(mount_crypt_stat->flags
- & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)) {
- rc = -EIO;
- printk(KERN_WARNING "Either the lower file "
- "is not in a valid eCryptfs format, "
- "or the key could not be retrieved. "
- "Plaintext passthrough mode is not "
- "enabled; returning -EIO\n");
- mutex_unlock(&crypt_stat->cs_mutex);
- goto out_put;
- }
- rc = 0;
- crypt_stat->flags &= ~(ECRYPTFS_I_SIZE_INITIALIZED
- | ECRYPTFS_ENCRYPTED);
- mutex_unlock(&crypt_stat->cs_mutex);
- goto out;
- }
- }
- mutex_unlock(&crypt_stat->cs_mutex);
+ rc = read_or_initialize_metadata(ecryptfs_dentry);
+ if (rc)
+ goto out_put;
ecryptfs_printk(KERN_DEBUG, "inode w/ addr = [0x%p], i_ino = "
"[0x%.16lx] size: [0x%.16llx]\n", inode, inode->i_ino,
(unsigned long long)i_size_read(inode));
@@ -277,8 +275,14 @@ out:
static int ecryptfs_flush(struct file *file, fl_owner_t td)
{
- return file->f_mode & FMODE_WRITE
- ? filemap_write_and_wait(file->f_mapping) : 0;
+ struct file *lower_file = ecryptfs_file_to_lower(file);
+
+ if (lower_file->f_op && lower_file->f_op->flush) {
+ filemap_write_and_wait(file->f_mapping);
+ return lower_file->f_op->flush(lower_file, td);
+ }
+
+ return 0;
}
static int ecryptfs_release(struct inode *inode, struct file *file)
@@ -292,15 +296,7 @@ static int ecryptfs_release(struct inode *inode, struct file *file)
static int
ecryptfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
- int rc = 0;
-
- rc = generic_file_fsync(file, start, end, datasync);
- if (rc)
- goto out;
- rc = vfs_fsync_range(ecryptfs_file_to_lower(file), start, end,
- datasync);
-out:
- return rc;
+ return vfs_fsync(ecryptfs_file_to_lower(file), datasync);
}
static int ecryptfs_fasync(int fd, struct file *file, int flag)
@@ -369,7 +365,7 @@ const struct file_operations ecryptfs_main_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl = ecryptfs_compat_ioctl,
#endif
- .mmap = ecryptfs_file_mmap,
+ .mmap = generic_file_mmap,
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.release = ecryptfs_release,
diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
index ffa2be57804d..cc7709e7c508 100644
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -143,6 +143,31 @@ static int ecryptfs_interpose(struct dentry *lower_dentry,
return 0;
}
+static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
+ struct inode *inode)
+{
+ struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
+ struct dentry *lower_dir_dentry;
+ int rc;
+
+ dget(lower_dentry);
+ lower_dir_dentry = lock_parent(lower_dentry);
+ rc = vfs_unlink(lower_dir_inode, lower_dentry);
+ if (rc) {
+ printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
+ goto out_unlock;
+ }
+ fsstack_copy_attr_times(dir, lower_dir_inode);
+ set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
+ inode->i_ctime = dir->i_ctime;
+ d_drop(dentry);
+out_unlock:
+ unlock_dir(lower_dir_dentry);
+ dput(lower_dentry);
+ return rc;
+}
+
/**
* ecryptfs_do_create
* @directory_inode: inode of the new file's dentry's parent in ecryptfs
@@ -182,8 +207,10 @@ ecryptfs_do_create(struct inode *directory_inode,
}
inode = __ecryptfs_get_inode(lower_dentry->d_inode,
directory_inode->i_sb);
- if (IS_ERR(inode))
+ if (IS_ERR(inode)) {
+ vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
goto out_lock;
+ }
fsstack_copy_attr_times(directory_inode, lower_dir_dentry->d_inode);
fsstack_copy_inode_size(directory_inode, lower_dir_dentry->d_inode);
out_lock:
@@ -200,8 +227,8 @@ out:
*
* Returns zero on success
*/
-static int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
- struct inode *ecryptfs_inode)
+int ecryptfs_initialize_file(struct dentry *ecryptfs_dentry,
+ struct inode *ecryptfs_inode)
{
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
@@ -264,7 +291,9 @@ ecryptfs_create(struct inode *directory_inode, struct dentry *ecryptfs_dentry,
* that this on disk file is prepared to be an ecryptfs file */
rc = ecryptfs_initialize_file(ecryptfs_dentry, ecryptfs_inode);
if (rc) {
- drop_nlink(ecryptfs_inode);
+ ecryptfs_do_unlink(directory_inode, ecryptfs_dentry,
+ ecryptfs_inode);
+ make_bad_inode(ecryptfs_inode);
unlock_new_inode(ecryptfs_inode);
iput(ecryptfs_inode);
goto out;
@@ -318,21 +347,20 @@ static int ecryptfs_lookup_interpose(struct dentry *dentry,
struct vfsmount *lower_mnt;
int rc = 0;
- lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
- fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode);
- BUG_ON(!lower_dentry->d_count);
-
dentry_info = kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL);
- ecryptfs_set_dentry_private(dentry, dentry_info);
if (!dentry_info) {
printk(KERN_ERR "%s: Out of memory whilst attempting "
"to allocate ecryptfs_dentry_info struct\n",
__func__);
dput(lower_dentry);
- mntput(lower_mnt);
- d_drop(dentry);
return -ENOMEM;
}
+
+ lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt(dentry->d_parent));
+ fsstack_copy_attr_atime(dir_inode, lower_dentry->d_parent->d_inode);
+ BUG_ON(!lower_dentry->d_count);
+
+ ecryptfs_set_dentry_private(dentry, dentry_info);
ecryptfs_set_dentry_lower(dentry, lower_dentry);
ecryptfs_set_dentry_lower_mnt(dentry, lower_mnt);
@@ -381,12 +409,6 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
struct dentry *lower_dir_dentry, *lower_dentry;
int rc = 0;
- if ((ecryptfs_dentry->d_name.len == 1
- && !strcmp(ecryptfs_dentry->d_name.name, "."))
- || (ecryptfs_dentry->d_name.len == 2
- && !strcmp(ecryptfs_dentry->d_name.name, ".."))) {
- goto out_d_drop;
- }
lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent);
mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name,
@@ -397,8 +419,8 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
rc = PTR_ERR(lower_dentry);
ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
"[%d] on lower_dentry = [%s]\n", __func__, rc,
- encrypted_and_encoded_name);
- goto out_d_drop;
+ ecryptfs_dentry->d_name.name);
+ goto out;
}
if (lower_dentry->d_inode)
goto interpose;
@@ -415,7 +437,7 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
if (rc) {
printk(KERN_ERR "%s: Error attempting to encrypt and encode "
"filename; rc = [%d]\n", __func__, rc);
- goto out_d_drop;
+ goto out;
}
mutex_lock(&lower_dir_dentry->d_inode->i_mutex);
lower_dentry = lookup_one_len(encrypted_and_encoded_name,
@@ -427,14 +449,11 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode,
ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned "
"[%d] on lower_dentry = [%s]\n", __func__, rc,
encrypted_and_encoded_name);
- goto out_d_drop;
+ goto out;
}
interpose:
rc = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry,
ecryptfs_dir_inode);
- goto out;
-out_d_drop:
- d_drop(ecryptfs_dentry);
out:
kfree(encrypted_and_encoded_name);
return ERR_PTR(rc);
@@ -476,27 +495,7 @@ out_lock:
static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry)
{
- int rc = 0;
- struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
- struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
- struct dentry *lower_dir_dentry;
-
- dget(lower_dentry);
- lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_unlink(lower_dir_inode, lower_dentry);
- if (rc) {
- printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
- goto out_unlock;
- }
- fsstack_copy_attr_times(dir, lower_dir_inode);
- set_nlink(dentry->d_inode,
- ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink);
- dentry->d_inode->i_ctime = dir->i_ctime;
- d_drop(dentry);
-out_unlock:
- unlock_dir(lower_dir_dentry);
- dput(lower_dentry);
- return rc;
+ return ecryptfs_do_unlink(dir, dentry, dentry->d_inode);
}
static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry,
@@ -620,6 +619,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct dentry *lower_old_dir_dentry;
struct dentry *lower_new_dir_dentry;
struct dentry *trap = NULL;
+ struct inode *target_inode;
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
@@ -627,6 +627,7 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
dget(lower_new_dentry);
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
+ target_inode = new_dentry->d_inode;
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
/* source should not be ancestor of target */
if (trap == lower_old_dentry) {
@@ -642,6 +643,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry);
if (rc)
goto out_lock;
+ if (target_inode)
+ fsstack_copy_attr_all(target_inode,
+ ecryptfs_inode_to_lower(target_inode));
fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);
if (new_dir != old_dir)
fsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);
@@ -971,12 +975,6 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
goto out;
}
- if (S_ISREG(inode->i_mode)) {
- rc = filemap_write_and_wait(inode->i_mapping);
- if (rc)
- goto out;
- fsstack_copy_attr_all(inode, lower_inode);
- }
memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = ecryptfs_file_to_lower(ia->ia_file);
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
index 1c0b3b6b75c6..9b627c15010a 100644
--- a/fs/ecryptfs/main.c
+++ b/fs/ecryptfs/main.c
@@ -162,6 +162,7 @@ void ecryptfs_put_lower_file(struct inode *inode)
inode_info = ecryptfs_inode_to_private(inode);
if (atomic_dec_and_mutex_lock(&inode_info->lower_file_count,
&inode_info->lower_file_mutex)) {
+ filemap_write_and_wait(inode->i_mapping);
fput(inode_info->lower_file);
inode_info->lower_file = NULL;
mutex_unlock(&inode_info->lower_file_mutex);
@@ -279,6 +280,7 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
char *fnek_src;
char *cipher_key_bytes_src;
char *fn_cipher_key_bytes_src;
+ u8 cipher_code;
*check_ruid = 0;
@@ -420,6 +422,18 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options,
&& !fn_cipher_key_bytes_set)
mount_crypt_stat->global_default_fn_cipher_key_bytes =
mount_crypt_stat->global_default_cipher_key_size;
+
+ cipher_code = ecryptfs_code_for_cipher_string(
+ mount_crypt_stat->global_default_cipher_name,
+ mount_crypt_stat->global_default_cipher_key_size);
+ if (!cipher_code) {
+ ecryptfs_printk(KERN_ERR,
+ "eCryptfs doesn't support cipher: %s",
+ mount_crypt_stat->global_default_cipher_name);
+ rc = -EINVAL;
+ goto out;
+ }
+
mutex_lock(&key_tfm_list_mutex);
if (!ecryptfs_tfm_exists(mount_crypt_stat->global_default_cipher_name,
NULL)) {
@@ -540,6 +554,15 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
}
ecryptfs_set_superblock_lower(s, path.dentry->d_sb);
+
+ /**
+ * Set the POSIX ACL flag based on whether they're enabled in the lower
+ * mount. Force a read-only eCryptfs mount if the lower mount is ro.
+ * Allow a ro eCryptfs mount even when the lower mount is rw.
+ */
+ s->s_flags = flags & ~MS_POSIXACL;
+ s->s_flags |= path.dentry->d_sb->s_flags & (MS_RDONLY | MS_POSIXACL);
+
s->s_maxbytes = path.dentry->d_sb->s_maxbytes;
s->s_blocksize = path.dentry->d_sb->s_blocksize;
s->s_magic = ECRYPTFS_SUPER_MAGIC;
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index a750f957b145..b29bb8bfa8d9 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -32,8 +32,8 @@ static struct mutex ecryptfs_msg_ctx_lists_mux;
static struct hlist_head *ecryptfs_daemon_hash;
struct mutex ecryptfs_daemon_hash_mux;
static int ecryptfs_hash_bits;
-#define ecryptfs_uid_hash(uid) \
- hash_long((unsigned long)uid, ecryptfs_hash_bits)
+#define ecryptfs_current_euid_hash(uid) \
+ hash_long((unsigned long)current_euid(), ecryptfs_hash_bits)
static u32 ecryptfs_msg_counter;
static struct ecryptfs_msg_ctx *ecryptfs_msg_ctx_arr;
@@ -105,26 +105,24 @@ void ecryptfs_msg_ctx_alloc_to_free(struct ecryptfs_msg_ctx *msg_ctx)
/**
* ecryptfs_find_daemon_by_euid
- * @euid: The effective user id which maps to the desired daemon id
- * @user_ns: The namespace in which @euid applies
* @daemon: If return value is zero, points to the desired daemon pointer
*
* Must be called with ecryptfs_daemon_hash_mux held.
*
- * Search the hash list for the given user id.
+ * Search the hash list for the current effective user id.
*
* Returns zero if the user id exists in the list; non-zero otherwise.
*/
-int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon, uid_t euid,
- struct user_namespace *user_ns)
+int ecryptfs_find_daemon_by_euid(struct ecryptfs_daemon **daemon)
{
struct hlist_node *elem;
int rc;
hlist_for_each_entry(*daemon, elem,
- &ecryptfs_daemon_hash[ecryptfs_uid_hash(euid)],
- euid_chain) {
- if ((*daemon)->euid == euid && (*daemon)->user_ns == user_ns) {
+ &ecryptfs_daemon_hash[ecryptfs_current_euid_hash()],
+ euid_chain) {
+ if ((*daemon)->file->f_cred->euid == current_euid() &&
+ (*daemon)->file->f_cred->user_ns == current_user_ns()) {
rc = 0;
goto out;
}
@@ -137,9 +135,7 @@ out:
/**
* ecryptfs_spawn_daemon - Create and initialize a new daemon struct
* @daemon: Pointer to set to newly allocated daemon struct
- * @euid: Effective user id for the daemon
- * @user_ns: The namespace in which @euid applies
- * @pid: Process id for the daemon
+ * @file: File used when opening /dev/ecryptfs
*
* Must be called ceremoniously while in possession of
* ecryptfs_sacred_daemon_hash_mux
@@ -147,8 +143,7 @@ out:
* Returns zero on success; non-zero otherwise
*/
int
-ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
- struct user_namespace *user_ns, struct pid *pid)
+ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, struct file *file)
{
int rc = 0;
@@ -159,16 +154,13 @@ ecryptfs_spawn_daemon(struct ecryptfs_daemon **daemon, uid_t euid,
"GFP_KERNEL memory\n", __func__, sizeof(**daemon));
goto out;
}
- (*daemon)->euid = euid;
- (*daemon)->user_ns = get_user_ns(user_ns);
- (*daemon)->pid = get_pid(pid);
- (*daemon)->task = current;
+ (*daemon)->file = file;
mutex_init(&(*daemon)->mux);
INIT_LIST_HEAD(&(*daemon)->msg_ctx_out_queue);
init_waitqueue_head(&(*daemon)->wait);
(*daemon)->num_queued_msg_ctx = 0;
hlist_add_head(&(*daemon)->euid_chain,
- &ecryptfs_daemon_hash[ecryptfs_uid_hash(euid)]);
+ &ecryptfs_daemon_hash[ecryptfs_current_euid_hash()]);
out:
return rc;
}
@@ -188,9 +180,6 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
if ((daemon->flags & ECRYPTFS_DAEMON_IN_READ)
|| (daemon->flags & ECRYPTFS_DAEMON_IN_POLL)) {
rc = -EBUSY;
- printk(KERN_WARNING "%s: Attempt to destroy daemon with pid "
- "[0x%p], but it is in the midst of a read or a poll\n",
- __func__, daemon->pid);
mutex_unlock(&daemon->mux);
goto out;
}
@@ -203,12 +192,6 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
}
hlist_del(&daemon->euid_chain);
- if (daemon->task)
- wake_up_process(daemon->task);
- if (daemon->pid)
- put_pid(daemon->pid);
- if (daemon->user_ns)
- put_user_ns(daemon->user_ns);
mutex_unlock(&daemon->mux);
kzfree(daemon);
out:
@@ -216,42 +199,9 @@ out:
}
/**
- * ecryptfs_process_quit
- * @euid: The user ID owner of the message
- * @user_ns: The namespace in which @euid applies
- * @pid: The process ID for the userspace program that sent the
- * message
- *
- * Deletes the corresponding daemon for the given euid and pid, if
- * it is the registered that is requesting the deletion. Returns zero
- * after deleting the desired daemon; non-zero otherwise.
- */
-int ecryptfs_process_quit(uid_t euid, struct user_namespace *user_ns,
- struct pid *pid)
-{
- struct ecryptfs_daemon *daemon;
- int rc;
-
- mutex_lock(&ecryptfs_daemon_hash_mux);
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, user_ns);
- if (rc || !daemon) {
- rc = -EINVAL;
- printk(KERN_ERR "Received request from user [%d] to "
- "unregister unrecognized daemon [0x%p]\n", euid, pid);
- goto out_unlock;
- }
- rc = ecryptfs_exorcise_daemon(daemon);
-out_unlock:
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- return rc;
-}
-
-/**
* ecryptfs_process_reponse
* @msg: The ecryptfs message received; the caller should sanity check
* msg->data_len and free the memory
- * @pid: The process ID of the userspace application that sent the
- * message
* @seq: The sequence number of the message; must match the sequence
* number for the existing message context waiting for this
* response
@@ -270,16 +220,11 @@ out_unlock:
*
* Returns zero on success; non-zero otherwise
*/
-int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
- struct user_namespace *user_ns, struct pid *pid,
- u32 seq)
+int ecryptfs_process_response(struct ecryptfs_daemon *daemon,
+ struct ecryptfs_message *msg, u32 seq)
{
- struct ecryptfs_daemon *uninitialized_var(daemon);
struct ecryptfs_msg_ctx *msg_ctx;
size_t msg_size;
- struct nsproxy *nsproxy;
- struct user_namespace *tsk_user_ns;
- uid_t ctx_euid;
int rc;
if (msg->index >= ecryptfs_message_buf_len) {
@@ -292,51 +237,6 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
}
msg_ctx = &ecryptfs_msg_ctx_arr[msg->index];
mutex_lock(&msg_ctx->mux);
- mutex_lock(&ecryptfs_daemon_hash_mux);
- rcu_read_lock();
- nsproxy = task_nsproxy(msg_ctx->task);
- if (nsproxy == NULL) {
- rc = -EBADMSG;
- printk(KERN_ERR "%s: Receiving process is a zombie. Dropping "
- "message.\n", __func__);
- rcu_read_unlock();
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- goto wake_up;
- }
- tsk_user_ns = __task_cred(msg_ctx->task)->user_ns;
- ctx_euid = task_euid(msg_ctx->task);
- rc = ecryptfs_find_daemon_by_euid(&daemon, ctx_euid, tsk_user_ns);
- rcu_read_unlock();
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- if (rc) {
- rc = -EBADMSG;
- printk(KERN_WARNING "%s: User [%d] received a "
- "message response from process [0x%p] but does "
- "not have a registered daemon\n", __func__,
- ctx_euid, pid);
- goto wake_up;
- }
- if (ctx_euid != euid) {
- rc = -EBADMSG;
- printk(KERN_WARNING "%s: Received message from user "
- "[%d]; expected message from user [%d]\n", __func__,
- euid, ctx_euid);
- goto unlock;
- }
- if (tsk_user_ns != user_ns) {
- rc = -EBADMSG;
- printk(KERN_WARNING "%s: Received message from user_ns "
- "[0x%p]; expected message from user_ns [0x%p]\n",
- __func__, user_ns, tsk_user_ns);
- goto unlock;
- }
- if (daemon->pid != pid) {
- rc = -EBADMSG;
- printk(KERN_ERR "%s: User [%d] sent a message response "
- "from an unrecognized process [0x%p]\n",
- __func__, ctx_euid, pid);
- goto unlock;
- }
if (msg_ctx->state != ECRYPTFS_MSG_CTX_STATE_PENDING) {
rc = -EINVAL;
printk(KERN_WARNING "%s: Desired context element is not "
@@ -359,9 +259,8 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid,
}
memcpy(msg_ctx->msg, msg, msg_size);
msg_ctx->state = ECRYPTFS_MSG_CTX_STATE_DONE;
- rc = 0;
-wake_up:
wake_up_process(msg_ctx->task);
+ rc = 0;
unlock:
mutex_unlock(&msg_ctx->mux);
out:
@@ -383,14 +282,11 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
struct ecryptfs_msg_ctx **msg_ctx)
{
struct ecryptfs_daemon *daemon;
- uid_t euid = current_euid();
int rc;
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
+ rc = ecryptfs_find_daemon_by_euid(&daemon);
if (rc || !daemon) {
rc = -ENOTCONN;
- printk(KERN_ERR "%s: User [%d] does not have a daemon "
- "registered\n", __func__, euid);
goto out;
}
mutex_lock(&ecryptfs_msg_ctx_lists_mux);
diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c
index c0038f6566d4..412e6eda25f8 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -33,7 +33,7 @@ static atomic_t ecryptfs_num_miscdev_opens;
/**
* ecryptfs_miscdev_poll
- * @file: dev file (ignored)
+ * @file: dev file
* @pt: dev poll table (ignored)
*
* Returns the poll mask
@@ -41,20 +41,10 @@ static atomic_t ecryptfs_num_miscdev_opens;
static unsigned int
ecryptfs_miscdev_poll(struct file *file, poll_table *pt)
{
- struct ecryptfs_daemon *daemon;
+ struct ecryptfs_daemon *daemon = file->private_data;
unsigned int mask = 0;
- uid_t euid = current_euid();
- int rc;
- mutex_lock(&ecryptfs_daemon_hash_mux);
- /* TODO: Just use file->private_data? */
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
- if (rc || !daemon) {
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- return -EINVAL;
- }
mutex_lock(&daemon->mux);
- mutex_unlock(&ecryptfs_daemon_hash_mux);
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
printk(KERN_WARNING "%s: Attempt to poll on zombified "
"daemon\n", __func__);
@@ -79,7 +69,7 @@ out_unlock_daemon:
/**
* ecryptfs_miscdev_open
* @inode: inode of miscdev handle (ignored)
- * @file: file for miscdev handle (ignored)
+ * @file: file for miscdev handle
*
* Returns zero on success; non-zero otherwise
*/
@@ -87,7 +77,6 @@ static int
ecryptfs_miscdev_open(struct inode *inode, struct file *file)
{
struct ecryptfs_daemon *daemon = NULL;
- uid_t euid = current_euid();
int rc;
mutex_lock(&ecryptfs_daemon_hash_mux);
@@ -98,30 +87,20 @@ ecryptfs_miscdev_open(struct inode *inode, struct file *file)
"count; rc = [%d]\n", __func__, rc);
goto out_unlock_daemon_list;
}
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
- if (rc || !daemon) {
- rc = ecryptfs_spawn_daemon(&daemon, euid, current_user_ns(),
- task_pid(current));
- if (rc) {
- printk(KERN_ERR "%s: Error attempting to spawn daemon; "
- "rc = [%d]\n", __func__, rc);
- goto out_module_put_unlock_daemon_list;
- }
- }
- mutex_lock(&daemon->mux);
- if (daemon->pid != task_pid(current)) {
+ rc = ecryptfs_find_daemon_by_euid(&daemon);
+ if (!rc) {
rc = -EINVAL;
- printk(KERN_ERR "%s: pid [0x%p] has registered with euid [%d], "
- "but pid [0x%p] has attempted to open the handle "
- "instead\n", __func__, daemon->pid, daemon->euid,
- task_pid(current));
- goto out_unlock_daemon;
+ goto out_unlock_daemon_list;
+ }
+ rc = ecryptfs_spawn_daemon(&daemon, file);
+ if (rc) {
+ printk(KERN_ERR "%s: Error attempting to spawn daemon; "
+ "rc = [%d]\n", __func__, rc);
+ goto out_module_put_unlock_daemon_list;
}
+ mutex_lock(&daemon->mux);
if (daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN) {
rc = -EBUSY;
- printk(KERN_ERR "%s: Miscellaneous device handle may only be "
- "opened once per daemon; pid [0x%p] already has this "
- "handle open\n", __func__, daemon->pid);
goto out_unlock_daemon;
}
daemon->flags |= ECRYPTFS_DAEMON_MISCDEV_OPEN;
@@ -140,7 +119,7 @@ out_unlock_daemon_list:
/**
* ecryptfs_miscdev_release
* @inode: inode of fs/ecryptfs/euid handle (ignored)
- * @file: file for fs/ecryptfs/euid handle (ignored)
+ * @file: file for fs/ecryptfs/euid handle
*
* This keeps the daemon registered until the daemon sends another
* ioctl to fs/ecryptfs/ctl or until the kernel module unregisters.
@@ -150,20 +129,18 @@ out_unlock_daemon_list:
static int
ecryptfs_miscdev_release(struct inode *inode, struct file *file)
{
- struct ecryptfs_daemon *daemon = NULL;
- uid_t euid = current_euid();
+ struct ecryptfs_daemon *daemon = file->private_data;
int rc;
- mutex_lock(&ecryptfs_daemon_hash_mux);
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
- if (rc || !daemon)
- daemon = file->private_data;
mutex_lock(&daemon->mux);
BUG_ON(!(daemon->flags & ECRYPTFS_DAEMON_MISCDEV_OPEN));
daemon->flags &= ~ECRYPTFS_DAEMON_MISCDEV_OPEN;
atomic_dec(&ecryptfs_num_miscdev_opens);
mutex_unlock(&daemon->mux);
+
+ mutex_lock(&ecryptfs_daemon_hash_mux);
rc = ecryptfs_exorcise_daemon(daemon);
+ mutex_unlock(&ecryptfs_daemon_hash_mux);
if (rc) {
printk(KERN_CRIT "%s: Fatal error whilst attempting to "
"shut down daemon; rc = [%d]. Please report this "
@@ -171,7 +148,6 @@ ecryptfs_miscdev_release(struct inode *inode, struct file *file)
BUG();
}
module_put(THIS_MODULE);
- mutex_unlock(&ecryptfs_daemon_hash_mux);
return rc;
}
@@ -248,7 +224,7 @@ int ecryptfs_send_miscdev(char *data, size_t data_size,
/**
* ecryptfs_miscdev_read - format and send message from queue
- * @file: fs/ecryptfs/euid miscdevfs handle (ignored)
+ * @file: miscdevfs handle
* @buf: User buffer into which to copy the next message on the daemon queue
* @count: Amount of space available in @buf
* @ppos: Offset in file (ignored)
@@ -262,43 +238,27 @@ static ssize_t
ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
- struct ecryptfs_daemon *daemon;
+ struct ecryptfs_daemon *daemon = file->private_data;
struct ecryptfs_msg_ctx *msg_ctx;
size_t packet_length_size;
char packet_length[ECRYPTFS_MAX_PKT_LEN_SIZE];
size_t i;
size_t total_length;
- uid_t euid = current_euid();
int rc;
- mutex_lock(&ecryptfs_daemon_hash_mux);
- /* TODO: Just use file->private_data? */
- rc = ecryptfs_find_daemon_by_euid(&daemon, euid, current_user_ns());
- if (rc || !daemon) {
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- return -EINVAL;
- }
mutex_lock(&daemon->mux);
- if (task_pid(current) != daemon->pid) {
- mutex_unlock(&daemon->mux);
- mutex_unlock(&ecryptfs_daemon_hash_mux);
- return -EPERM;
- }
if (daemon->flags & ECRYPTFS_DAEMON_ZOMBIE) {
rc = 0;
- mutex_unlock(&ecryptfs_daemon_hash_mux);
printk(KERN_WARNING "%s: Attempt to read from zombified "
"daemon\n", __func__);
goto out_unlock_daemon;
}
if (daemon->flags & ECRYPTFS_DAEMON_IN_READ) {
rc = 0;
- mutex_unlock(&ecryptfs_daemon_hash_mux);
goto out_unlock_daemon;
}
/* This daemon will not go away so long as this flag is set */
daemon->flags |= ECRYPTFS_DAEMON_IN_READ;
- mutex_unlock(&ecryptfs_daemon_hash_mux);
check_list:
if (list_empty(&daemon->msg_ctx_out_queue)) {
mutex_unlock(&daemon->mux);
@@ -382,16 +342,12 @@ out_unlock_daemon:
* ecryptfs_miscdev_response - miscdevess response to message previously sent to daemon
* @data: Bytes comprising struct ecryptfs_message
* @data_size: sizeof(struct ecryptfs_message) + data len
- * @euid: Effective user id of miscdevess sending the miscdev response
- * @user_ns: The namespace in which @euid applies
- * @pid: Miscdevess id of miscdevess sending the miscdev response
* @seq: Sequence number for miscdev response packet
*
* Returns zero on success; non-zero otherwise
*/
-static int ecryptfs_miscdev_response(char *data, size_t data_size,
- uid_t euid, struct user_namespace *user_ns,
- struct pid *pid, u32 seq)
+static int ecryptfs_miscdev_response(struct ecryptfs_daemon *daemon, char *data,
+ size_t data_size, u32 seq)
{
struct ecryptfs_message *msg = (struct ecryptfs_message *)data;
int rc;
@@ -403,7 +359,7 @@ static int ecryptfs_miscdev_response(char *data, size_t data_size,
rc = -EINVAL;
goto out;
}
- rc = ecryptfs_process_response(msg, euid, user_ns, pid, seq);
+ rc = ecryptfs_process_response(daemon, msg, seq);
if (rc)
printk(KERN_ERR
"Error processing response message; rc = [%d]\n", rc);
@@ -413,7 +369,7 @@ out:
/**
* ecryptfs_miscdev_write - handle write to daemon miscdev handle
- * @file: File for misc dev handle (ignored)
+ * @file: File for misc dev handle
* @buf: Buffer containing user data
* @count: Amount of data in @buf
* @ppos: Pointer to offset in file (ignored)
@@ -428,7 +384,6 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
u32 seq;
size_t packet_size, packet_size_length;
char *data;
- uid_t euid = current_euid();
unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
ssize_t rc;
@@ -488,10 +443,9 @@ memdup:
}
memcpy(&counter_nbo, &data[PKT_CTR_OFFSET], PKT_CTR_SIZE);
seq = be32_to_cpu(counter_nbo);
- rc = ecryptfs_miscdev_response(
+ rc = ecryptfs_miscdev_response(file->private_data,
&data[PKT_LEN_OFFSET + packet_size_length],
- packet_size, euid, current_user_ns(),
- task_pid(current), seq);
+ packet_size, seq);
if (rc) {
printk(KERN_WARNING "%s: Failed to deliver miscdev "
"response to requesting operation; rc = [%zd]\n",
diff --git a/fs/ecryptfs/mmap.c b/fs/ecryptfs/mmap.c
index a46b3a8fee1e..bd1d57f98f74 100644
--- a/fs/ecryptfs/mmap.c
+++ b/fs/ecryptfs/mmap.c
@@ -66,18 +66,6 @@ static int ecryptfs_writepage(struct page *page, struct writeback_control *wbc)
{
int rc;
- /*
- * Refuse to write the page out if we are called from reclaim context
- * since our writepage() path may potentially allocate memory when
- * calling into the lower fs vfs_write() which may in turn invoke
- * us again.
- */
- if (current->flags & PF_MEMALLOC) {
- redirty_page_for_writepage(wbc, page);
- rc = 0;
- goto out;
- }
-
rc = ecryptfs_encrypt_page(page);
if (rc) {
ecryptfs_printk(KERN_WARNING, "Error encrypting "
@@ -498,7 +486,6 @@ static int ecryptfs_write_end(struct file *file,
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
int rc;
- int need_unlock_page = 1;
ecryptfs_printk(KERN_DEBUG, "Calling fill_zeros_to_end_of_page"
"(page w/ index = [0x%.16lx], to = [%d])\n", index, to);
@@ -519,26 +506,26 @@ static int ecryptfs_write_end(struct file *file,
"zeros in page with index = [0x%.16lx]\n", index);
goto out;
}
- set_page_dirty(page);
- unlock_page(page);
- need_unlock_page = 0;
+ rc = ecryptfs_encrypt_page(page);
+ if (rc) {
+ ecryptfs_printk(KERN_WARNING, "Error encrypting page (upper "
+ "index [0x%.16lx])\n", index);
+ goto out;
+ }
if (pos + copied > i_size_read(ecryptfs_inode)) {
i_size_write(ecryptfs_inode, pos + copied);
ecryptfs_printk(KERN_DEBUG, "Expanded file size to "
"[0x%.16llx]\n",
(unsigned long long)i_size_read(ecryptfs_inode));
- balance_dirty_pages_ratelimited(mapping);
- rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode);
- if (rc) {
- printk(KERN_ERR "Error writing inode size to metadata; "
- "rc = [%d]\n", rc);
- goto out;
- }
}
- rc = copied;
+ rc = ecryptfs_write_inode_size_to_metadata(ecryptfs_inode);
+ if (rc)
+ printk(KERN_ERR "Error writing inode size to metadata; "
+ "rc = [%d]\n", rc);
+ else
+ rc = copied;
out:
- if (need_unlock_page)
- unlock_page(page);
+ unlock_page(page);
page_cache_release(page);
return rc;
}
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 1c8b55670804..eedec84c1809 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1654,8 +1654,8 @@ SYSCALL_DEFINE1(epoll_create1, int, flags)
error = PTR_ERR(file);
goto out_free_fd;
}
- fd_install(fd, file);
ep->file = file;
+ fd_install(fd, file);
return fd;
out_free_fd:
diff --git a/fs/exec.c b/fs/exec.c
index 3684353ebd5f..574cf4de4ec3 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2069,25 +2069,18 @@ static void wait_for_dump_helpers(struct file *file)
*/
static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
{
- struct file *rp, *wp;
+ struct file *files[2];
struct fdtable *fdt;
struct coredump_params *cp = (struct coredump_params *)info->data;
struct files_struct *cf = current->files;
+ int err = create_pipe_files(files, 0);
+ if (err)
+ return err;
- wp = create_write_pipe(0);
- if (IS_ERR(wp))
- return PTR_ERR(wp);
-
- rp = create_read_pipe(wp, 0);
- if (IS_ERR(rp)) {
- free_write_pipe(wp);
- return PTR_ERR(rp);
- }
-
- cp->file = wp;
+ cp->file = files[1];
sys_close(0);
- fd_install(0, rp);
+ fd_install(0, files[0]);
spin_lock(&cf->file_lock);
fdt = files_fdtable(cf);
__set_open_fd(0, fdt);
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index 5badb0c039de..1562c27a2fab 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -37,15 +37,12 @@
#define EXOFS_DBGMSG2(M...) do {} while (0)
-enum {MAX_PAGES_KMALLOC = PAGE_SIZE / sizeof(struct page *), };
-
unsigned exofs_max_io_pages(struct ore_layout *layout,
unsigned expected_pages)
{
- unsigned pages = min_t(unsigned, expected_pages, MAX_PAGES_KMALLOC);
+ unsigned pages = min_t(unsigned, expected_pages,
+ layout->max_io_length / PAGE_SIZE);
- /* TODO: easily support bio chaining */
- pages = min_t(unsigned, pages, layout->max_io_length / PAGE_SIZE);
return pages;
}
@@ -101,7 +98,8 @@ static void _pcol_reset(struct page_collect *pcol)
* it might not end here. don't be left with nothing
*/
if (!pcol->expected_pages)
- pcol->expected_pages = MAX_PAGES_KMALLOC;
+ pcol->expected_pages =
+ exofs_max_io_pages(&pcol->sbi->layout, ~0);
}
static int pcol_try_alloc(struct page_collect *pcol)
@@ -389,6 +387,8 @@ static int readpage_strip(void *data, struct page *page)
size_t len;
int ret;
+ BUG_ON(!PageLocked(page));
+
/* FIXME: Just for debugging, will be removed */
if (PageUptodate(page))
EXOFS_ERR("PageUptodate(0x%lx, 0x%lx)\n", pcol->inode->i_ino,
@@ -572,8 +572,16 @@ static struct page *__r4w_get_page(void *priv, u64 offset, bool *uptodate)
if (!pcol->that_locked_page ||
(pcol->that_locked_page->index != index)) {
- struct page *page = find_get_page(pcol->inode->i_mapping, index);
+ struct page *page;
+ loff_t i_size = i_size_read(pcol->inode);
+
+ if (offset >= i_size) {
+ *uptodate = true;
+ EXOFS_DBGMSG("offset >= i_size index=0x%lx\n", index);
+ return ZERO_PAGE(0);
+ }
+ page = find_get_page(pcol->inode->i_mapping, index);
if (!page) {
page = find_or_create_page(pcol->inode->i_mapping,
index, GFP_NOFS);
@@ -602,12 +610,13 @@ static void __r4w_put_page(void *priv, struct page *page)
{
struct page_collect *pcol = priv;
- if (pcol->that_locked_page != page) {
+ if ((pcol->that_locked_page != page) && (ZERO_PAGE(0) != page)) {
EXOFS_DBGMSG("index=0x%lx\n", page->index);
page_cache_release(page);
return;
}
- EXOFS_DBGMSG("that_locked_page index=0x%lx\n", page->index);
+ EXOFS_DBGMSG("that_locked_page index=0x%lx\n",
+ ZERO_PAGE(0) == page ? -1 : page->index);
}
static const struct _ore_r4w_op _r4w_op = {
diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c
index 24a49d47e935..1585db1aa365 100644
--- a/fs/exofs/ore.c
+++ b/fs/exofs/ore.c
@@ -837,11 +837,11 @@ static int _write_mirror(struct ore_io_state *ios, int cur_comp)
bio->bi_rw |= REQ_WRITE;
}
- osd_req_write(or, _ios_obj(ios, dev), per_dev->offset,
- bio, per_dev->length);
+ osd_req_write(or, _ios_obj(ios, cur_comp),
+ per_dev->offset, bio, per_dev->length);
ORE_DBGMSG("write(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
_LLU(per_dev->offset),
_LLU(per_dev->length), dev);
} else if (ios->kern_buff) {
@@ -853,20 +853,20 @@ static int _write_mirror(struct ore_io_state *ios, int cur_comp)
(ios->si.unit_off + ios->length >
ios->layout->stripe_unit));
- ret = osd_req_write_kern(or, _ios_obj(ios, per_dev->dev),
+ ret = osd_req_write_kern(or, _ios_obj(ios, cur_comp),
per_dev->offset,
ios->kern_buff, ios->length);
if (unlikely(ret))
goto out;
ORE_DBGMSG2("write_kern(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
_LLU(per_dev->offset),
_LLU(ios->length), per_dev->dev);
} else {
- osd_req_set_attributes(or, _ios_obj(ios, dev));
+ osd_req_set_attributes(or, _ios_obj(ios, cur_comp));
ORE_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
ios->out_attr_len, dev);
}
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index 433783624d10..dde41a75c7c8 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -400,8 +400,6 @@ static int exofs_sync_fs(struct super_block *sb, int wait)
ret = ore_write(ios);
if (unlikely(ret))
EXOFS_ERR("%s: ore_write failed.\n", __func__);
- else
- sb->s_dirt = 0;
unlock_super(sb);
@@ -412,14 +410,6 @@ out:
return ret;
}
-static void exofs_write_super(struct super_block *sb)
-{
- if (!(sb->s_flags & MS_RDONLY))
- exofs_sync_fs(sb, 1);
- else
- sb->s_dirt = 0;
-}
-
static void _exofs_print_device(const char *msg, const char *dev_path,
struct osd_dev *od, u64 pid)
{
@@ -952,7 +942,6 @@ static const struct super_operations exofs_sops = {
.write_inode = exofs_write_inode,
.evict_inode = exofs_evict_inode,
.put_super = exofs_put_super,
- .write_super = exofs_write_super,
.sync_fs = exofs_sync_fs,
.statfs = exofs_statfs,
};
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 264d315f6c47..6363ac66fafa 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -79,6 +79,7 @@ void ext2_evict_inode(struct inode * inode)
truncate_inode_pages(&inode->i_data, 0);
if (want_delete) {
+ sb_start_intwrite(inode->i_sb);
/* set dtime */
EXT2_I(inode)->i_dtime = get_seconds();
mark_inode_dirty(inode);
@@ -98,8 +99,10 @@ void ext2_evict_inode(struct inode * inode)
if (unlikely(rsv))
kfree(rsv);
- if (want_delete)
+ if (want_delete) {
ext2_free_inode(inode);
+ sb_end_intwrite(inode->i_sb);
+ }
}
typedef struct {
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 9f311d27b16f..af74d9e27b71 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -42,6 +42,8 @@ static void ext2_sync_super(struct super_block *sb,
static int ext2_remount (struct super_block * sb, int * flags, char * data);
static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf);
static int ext2_sync_fs(struct super_block *sb, int wait);
+static int ext2_freeze(struct super_block *sb);
+static int ext2_unfreeze(struct super_block *sb);
void ext2_error(struct super_block *sb, const char *function,
const char *fmt, ...)
@@ -305,6 +307,8 @@ static const struct super_operations ext2_sops = {
.evict_inode = ext2_evict_inode,
.put_super = ext2_put_super,
.sync_fs = ext2_sync_fs,
+ .freeze_fs = ext2_freeze,
+ .unfreeze_fs = ext2_unfreeze,
.statfs = ext2_statfs,
.remount_fs = ext2_remount,
.show_options = ext2_show_options,
@@ -1200,6 +1204,35 @@ static int ext2_sync_fs(struct super_block *sb, int wait)
return 0;
}
+static int ext2_freeze(struct super_block *sb)
+{
+ struct ext2_sb_info *sbi = EXT2_SB(sb);
+
+ /*
+ * Open but unlinked files present? Keep EXT2_VALID_FS flag cleared
+ * because we have unattached inodes and thus filesystem is not fully
+ * consistent.
+ */
+ if (atomic_long_read(&sb->s_remove_count)) {
+ ext2_sync_fs(sb, 1);
+ return 0;
+ }
+ /* Set EXT2_FS_VALID flag */
+ spin_lock(&sbi->s_lock);
+ sbi->s_es->s_state = cpu_to_le16(sbi->s_mount_state);
+ spin_unlock(&sbi->s_lock);
+ ext2_sync_super(sb, sbi->s_es, 1);
+
+ return 0;
+}
+
+static int ext2_unfreeze(struct super_block *sb)
+{
+ /* Just write sb to clear EXT2_VALID_FS flag */
+ ext2_write_super(sb);
+
+ return 0;
+}
void ext2_write_super(struct super_block *sb)
{
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 9a4a5c48b1c9..ff574b4e345e 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -3072,6 +3072,8 @@ static int ext3_do_update_inode(handle_t *handle,
struct ext3_inode_info *ei = EXT3_I(inode);
struct buffer_head *bh = iloc->bh;
int err = 0, rc, block;
+ int need_datasync = 0;
+ __le32 disksize;
uid_t i_uid;
gid_t i_gid;
@@ -3113,7 +3115,11 @@ again:
raw_inode->i_gid_high = 0;
}
raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
- raw_inode->i_size = cpu_to_le32(ei->i_disksize);
+ disksize = cpu_to_le32(ei->i_disksize);
+ if (disksize != raw_inode->i_size) {
+ need_datasync = 1;
+ raw_inode->i_size = disksize;
+ }
raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec);
raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec);
raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec);
@@ -3129,8 +3135,11 @@ again:
if (!S_ISREG(inode->i_mode)) {
raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
} else {
- raw_inode->i_size_high =
- cpu_to_le32(ei->i_disksize >> 32);
+ disksize = cpu_to_le32(ei->i_disksize >> 32);
+ if (disksize != raw_inode->i_size_high) {
+ raw_inode->i_size_high = disksize;
+ need_datasync = 1;
+ }
if (ei->i_disksize > 0x7fffffffULL) {
struct super_block *sb = inode->i_sb;
if (!EXT3_HAS_RO_COMPAT_FEATURE(sb,
@@ -3183,6 +3192,8 @@ again:
ext3_clear_inode_state(inode, EXT3_STATE_NEW);
atomic_set(&ei->i_sync_tid, handle->h_transaction->t_tid);
+ if (need_datasync)
+ atomic_set(&ei->i_datasync_tid, handle->h_transaction->t_tid);
out_brelse:
brelse (bh);
ext3_std_error(inode->i_sb, err);
@@ -3459,14 +3470,6 @@ ext3_reserve_inode_write(handle_t *handle, struct inode *inode,
* inode out, but prune_icache isn't a user-visible syncing function.
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
* we start and wait on commits.
- *
- * Is this efficient/effective? Well, we're being nice to the system
- * by cleaning up our inodes proactively so they can be reaped
- * without I/O. But we are potentially leaving up to five seconds'
- * worth of inodes floating about which prune_icache wants us to
- * write out. One way to fix that would be to get prune_icache()
- * to do a write_super() to free up some memory. It has the desired
- * effect.
*/
int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode)
{
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc5b0d5..8c892e93d8e7 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -64,11 +64,6 @@ static int ext3_freeze(struct super_block *sb);
/*
* Wrappers for journal_start/end.
- *
- * The only special thing we need to do here is to make sure that all
- * journal_end calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
*/
handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks)
{
@@ -90,12 +85,6 @@ handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks)
return journal_start(journal, nblocks);
}
-/*
- * The only special thing we need to do here is to make sure that all
- * journal_stop calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
- */
int __ext3_journal_stop(const char *where, handle_t *handle)
{
struct super_block *sb;
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index d23b31ca9d7a..1b5089067d01 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -280,14 +280,18 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
return desc;
}
-static int ext4_valid_block_bitmap(struct super_block *sb,
- struct ext4_group_desc *desc,
- unsigned int block_group,
- struct buffer_head *bh)
+/*
+ * Return the block number which was discovered to be invalid, or 0 if
+ * the block bitmap is valid.
+ */
+static ext4_fsblk_t ext4_valid_block_bitmap(struct super_block *sb,
+ struct ext4_group_desc *desc,
+ unsigned int block_group,
+ struct buffer_head *bh)
{
ext4_grpblk_t offset;
ext4_grpblk_t next_zero_bit;
- ext4_fsblk_t bitmap_blk;
+ ext4_fsblk_t blk;
ext4_fsblk_t group_first_block;
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
@@ -297,37 +301,33 @@ static int ext4_valid_block_bitmap(struct super_block *sb,
* or it has to also read the block group where the bitmaps
* are located to verify they are set.
*/
- return 1;
+ return 0;
}
group_first_block = ext4_group_first_block_no(sb, block_group);
/* check whether block bitmap block number is set */
- bitmap_blk = ext4_block_bitmap(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_block_bitmap(sb, desc);
+ offset = blk - group_first_block;
if (!ext4_test_bit(offset, bh->b_data))
/* bad block bitmap */
- goto err_out;
+ return blk;
/* check whether the inode bitmap block number is set */
- bitmap_blk = ext4_inode_bitmap(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_inode_bitmap(sb, desc);
+ offset = blk - group_first_block;
if (!ext4_test_bit(offset, bh->b_data))
/* bad block bitmap */
- goto err_out;
+ return blk;
/* check whether the inode table block number is set */
- bitmap_blk = ext4_inode_table(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_inode_table(sb, desc);
+ offset = blk - group_first_block;
next_zero_bit = ext4_find_next_zero_bit(bh->b_data,
offset + EXT4_SB(sb)->s_itb_per_group,
offset);
- if (next_zero_bit >= offset + EXT4_SB(sb)->s_itb_per_group)
- /* good bitmap for inode tables */
- return 1;
-
-err_out:
- ext4_error(sb, "Invalid block bitmap - block_group = %d, block = %llu",
- block_group, bitmap_blk);
+ if (next_zero_bit < offset + EXT4_SB(sb)->s_itb_per_group)
+ /* bad bitmap for inode tables */
+ return blk;
return 0;
}
@@ -336,14 +336,26 @@ void ext4_validate_block_bitmap(struct super_block *sb,
unsigned int block_group,
struct buffer_head *bh)
{
+ ext4_fsblk_t blk;
+
if (buffer_verified(bh))
return;
ext4_lock_group(sb, block_group);
- if (ext4_valid_block_bitmap(sb, desc, block_group, bh) &&
- ext4_block_bitmap_csum_verify(sb, block_group, desc, bh,
- EXT4_BLOCKS_PER_GROUP(sb) / 8))
- set_buffer_verified(bh);
+ blk = ext4_valid_block_bitmap(sb, desc, block_group, bh);
+ if (unlikely(blk != 0)) {
+ ext4_unlock_group(sb, block_group);
+ ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
+ block_group, blk);
+ return;
+ }
+ if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
+ desc, bh, EXT4_BLOCKS_PER_GROUP(sb) / 8))) {
+ ext4_unlock_group(sb, block_group);
+ ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
+ return;
+ }
+ set_buffer_verified(bh);
ext4_unlock_group(sb, block_group);
}
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index f8716eab9995..5c2d1813ebe9 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -79,7 +79,6 @@ int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group,
if (provided == calculated)
return 1;
- ext4_error(sb, "Bad block bitmap checksum: block_group = %u", group);
return 0;
}
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index cd0c7ed06772..aabbb3f53683 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2662,6 +2662,7 @@ cont:
}
path[0].p_depth = depth;
path[0].p_hdr = ext_inode_hdr(inode);
+ i = 0;
if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
err = -EIO;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 89b59cb7f9b8..dff171c3a123 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -233,6 +233,11 @@ void ext4_evict_inode(struct inode *inode)
if (is_bad_inode(inode))
goto no_delete;
+ /*
+ * Protect us against freezing - iput() caller didn't have to have any
+ * protection against it
+ */
+ sb_start_intwrite(inode->i_sb);
handle = ext4_journal_start(inode, ext4_blocks_for_truncate(inode)+3);
if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle));
@@ -242,6 +247,7 @@ void ext4_evict_inode(struct inode *inode)
* cleaned up.
*/
ext4_orphan_del(NULL, inode);
+ sb_end_intwrite(inode->i_sb);
goto no_delete;
}
@@ -273,6 +279,7 @@ void ext4_evict_inode(struct inode *inode)
stop_handle:
ext4_journal_stop(handle);
ext4_orphan_del(NULL, inode);
+ sb_end_intwrite(inode->i_sb);
goto no_delete;
}
}
@@ -301,6 +308,7 @@ void ext4_evict_inode(struct inode *inode)
else
ext4_free_inode(handle, inode);
ext4_journal_stop(handle);
+ sb_end_intwrite(inode->i_sb);
return;
no_delete:
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
@@ -1962,7 +1970,7 @@ static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate);
* This function can get called via...
* - ext4_da_writepages after taking page lock (have journal handle)
* - journal_submit_inode_data_buffers (no journal handle)
- * - shrink_page_list via pdflush (no journal handle)
+ * - shrink_page_list via the kswapd/direct reclaim (no journal handle)
* - grab_page_cache when doing write_begin (have journal handle)
*
* We don't do any block allocation in this function. If we have page with
@@ -4581,14 +4589,6 @@ static int ext4_expand_extra_isize(struct inode *inode,
* inode out, but prune_icache isn't a user-visible syncing function.
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
* we start and wait on commits.
- *
- * Is this efficient/effective? Well, we're being nice to the system
- * by cleaning up our inodes proactively so they can be reaped
- * without I/O. But we are potentially leaving up to five seconds'
- * worth of inodes floating about which prune_icache wants us to
- * write out. One way to fix that would be to get prune_icache()
- * to do a write_super() to free up some memory. It has the desired
- * effect.
*/
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
{
@@ -4779,11 +4779,7 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
get_block_t *get_block;
int retries = 0;
- /*
- * This check is racy but catches the common case. We rely on
- * __block_page_mkwrite() to do a reliable check.
- */
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+ sb_start_pagefault(inode->i_sb);
/* Delalloc case is easy... */
if (test_opt(inode->i_sb, DELALLOC) &&
!ext4_should_journal_data(inode) &&
@@ -4851,5 +4847,6 @@ retry_alloc:
out_ret:
ret = block_page_mkwrite_return(ret);
out:
+ sb_end_pagefault(inode->i_sb);
return ret;
}
diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c
index f99a1311e847..fe7c63f4717e 100644
--- a/fs/ext4/mmp.c
+++ b/fs/ext4/mmp.c
@@ -44,6 +44,11 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
{
struct mmp_struct *mmp = (struct mmp_struct *)(bh->b_data);
+ /*
+ * We protect against freezing so that we don't create dirty buffers
+ * on frozen filesystem.
+ */
+ sb_start_write(sb);
ext4_mmp_csum_set(sb, mmp);
mark_buffer_dirty(bh);
lock_buffer(bh);
@@ -51,6 +56,7 @@ static int write_mmp_block(struct super_block *sb, struct buffer_head *bh)
get_bh(bh);
submit_bh(WRITE_SYNC, bh);
wait_on_buffer(bh);
+ sb_end_write(sb);
if (unlikely(!buffer_uptodate(bh)))
return 1;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2d51cd9af225..c6e0cb3d1f4a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -326,38 +326,17 @@ static void ext4_put_nojournal(handle_t *handle)
/*
* Wrappers for jbd2_journal_start/end.
- *
- * The only special thing we need to do here is to make sure that all
- * journal_end calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
- *
- * To avoid j_barrier hold in userspace when a user calls freeze(),
- * ext4 prevents a new handle from being started by s_frozen, which
- * is in an upper layer.
*/
handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
{
journal_t *journal;
- handle_t *handle;
trace_ext4_journal_start(sb, nblocks, _RET_IP_);
if (sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS);
+ WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
journal = EXT4_SB(sb)->s_journal;
- handle = ext4_journal_current_handle();
-
- /*
- * If a handle has been started, it should be allowed to
- * finish, otherwise deadlock could happen between freeze
- * and others(e.g. truncate) due to the restart of the
- * journal handle if the filesystem is forzen and active
- * handles are not stopped.
- */
- if (!handle)
- vfs_check_frozen(sb, SB_FREEZE_TRANS);
-
if (!journal)
return ext4_get_nojournal();
/*
@@ -372,12 +351,6 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
return jbd2_journal_start(journal, nblocks);
}
-/*
- * The only special thing we need to do here is to make sure that all
- * jbd2_journal_stop calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
- */
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
{
struct super_block *sb;
@@ -975,6 +948,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
ei->i_reserved_meta_blocks = 0;
ei->i_allocated_meta_blocks = 0;
ei->i_da_metadata_calc_len = 0;
+ ei->i_da_metadata_calc_last_lblock = 0;
spin_lock_init(&(ei->i_block_reservation_lock));
#ifdef CONFIG_QUOTA
ei->i_reserved_quota = 0;
@@ -2747,6 +2721,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
sb = elr->lr_super;
ngroups = EXT4_SB(sb)->s_groups_count;
+ sb_start_write(sb);
for (group = elr->lr_next_group; group < ngroups; group++) {
gdp = ext4_get_group_desc(sb, group, NULL);
if (!gdp) {
@@ -2773,6 +2748,7 @@ static int ext4_run_li_request(struct ext4_li_request *elr)
elr->lr_next_sched = jiffies + elr->lr_timeout;
elr->lr_next_group = group + 1;
}
+ sb_end_write(sb);
return ret;
}
@@ -3133,6 +3109,10 @@ static int count_overhead(struct super_block *sb, ext4_group_t grp,
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
int s, j, count = 0;
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC))
+ return (ext4_bg_has_super(sb, grp) + ext4_bg_num_gdb(sb, grp) +
+ sbi->s_itb_per_group + 2);
+
first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
(grp * EXT4_BLOCKS_PER_GROUP(sb));
last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
@@ -4444,6 +4424,7 @@ static void ext4_clear_journal_err(struct super_block *sb,
ext4_commit_super(sb, 1);
jbd2_journal_clear_err(journal);
+ jbd2_journal_update_sb_errno(journal);
}
}
@@ -4460,10 +4441,8 @@ int ext4_force_commit(struct super_block *sb)
return 0;
journal = EXT4_SB(sb)->s_journal;
- if (journal) {
- vfs_check_frozen(sb, SB_FREEZE_TRANS);
+ if (journal)
ret = ext4_journal_force_commit(journal);
- }
return ret;
}
@@ -4493,9 +4472,8 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
* gives us a chance to flush the journal completely and mark the fs clean.
*
* Note that only this function cannot bring a filesystem to be in a clean
- * state independently, because ext4 prevents a new handle from being started
- * by @sb->s_frozen, which stays in an upper layer. It thus needs help from
- * the upper layer.
+ * state independently. It relies on upper layer to stop all data & metadata
+ * modifications.
*/
static int ext4_freeze(struct super_block *sb)
{
@@ -4522,7 +4500,7 @@ static int ext4_freeze(struct super_block *sb)
EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);
error = ext4_commit_super(sb, 1);
out:
- /* we rely on s_frozen to stop further updates */
+ /* we rely on upper layer to stop further updates */
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
return error;
}
diff --git a/fs/fat/file.c b/fs/fat/file.c
index a71fe3715ee8..e007b8bd8e5e 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -43,10 +43,10 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
if (err)
goto out;
- mutex_lock(&inode->i_mutex);
err = mnt_want_write_file(file);
if (err)
- goto out_unlock_inode;
+ goto out;
+ mutex_lock(&inode->i_mutex);
/*
* ATTR_VOLUME and ATTR_DIR cannot be changed; this also
@@ -73,14 +73,14 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
/* The root directory has no attributes */
if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
err = -EINVAL;
- goto out_drop_write;
+ goto out_unlock_inode;
}
if (sbi->options.sys_immutable &&
((attr | oldattr) & ATTR_SYS) &&
!capable(CAP_LINUX_IMMUTABLE)) {
err = -EPERM;
- goto out_drop_write;
+ goto out_unlock_inode;
}
/*
@@ -90,12 +90,12 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
*/
err = security_inode_setattr(file->f_path.dentry, &ia);
if (err)
- goto out_drop_write;
+ goto out_unlock_inode;
/* This MUST be done before doing anything irreversible... */
err = fat_setattr(file->f_path.dentry, &ia);
if (err)
- goto out_drop_write;
+ goto out_unlock_inode;
fsnotify_change(file->f_path.dentry, ia.ia_valid);
if (sbi->options.sys_immutable) {
@@ -107,10 +107,9 @@ static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
fat_save_attrs(inode, attr);
mark_inode_dirty(inode);
-out_drop_write:
- mnt_drop_write_file(file);
out_unlock_inode:
mutex_unlock(&inode->i_mutex);
+ mnt_drop_write_file(file);
out:
return err;
}
diff --git a/fs/file_table.c b/fs/file_table.c
index b3fc4d67a26b..701985e4ccda 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -43,7 +43,7 @@ static struct kmem_cache *filp_cachep __read_mostly;
static struct percpu_counter nr_files __cacheline_aligned_in_smp;
-static inline void file_free_rcu(struct rcu_head *head)
+static void file_free_rcu(struct rcu_head *head)
{
struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
@@ -217,7 +217,7 @@ static void drop_file_write_access(struct file *file)
return;
if (file_check_writeable(file) != 0)
return;
- mnt_drop_write(mnt);
+ __mnt_drop_write(mnt);
file_release_write(file);
}
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 50d0b78130a1..be3efc4f64f4 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -52,11 +52,6 @@ struct wb_writeback_work {
struct completion *done; /* set if the caller waits */
};
-/*
- * We don't actually have pdflush, but this one is exported though /proc...
- */
-int nr_pdflush_threads;
-
/**
* writeback_in_progress - determine whether there is writeback in progress
* @bdi: the device's backing_dev_info structure.
diff --git a/fs/fuse/control.c b/fs/fuse/control.c
index 03ff5b1eba93..75a20c092dd4 100644
--- a/fs/fuse/control.c
+++ b/fs/fuse/control.c
@@ -117,7 +117,7 @@ static ssize_t fuse_conn_max_background_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
- unsigned val;
+ unsigned uninitialized_var(val);
ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
@@ -154,7 +154,7 @@ static ssize_t fuse_conn_congestion_threshold_write(struct file *file,
const char __user *buf,
size_t count, loff_t *ppos)
{
- unsigned val;
+ unsigned uninitialized_var(val);
ssize_t ret;
ret = fuse_conn_limit_write(file, buf, count, ppos, &val,
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index 3426521f3205..ee8d55042298 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -396,7 +396,7 @@ err_device:
err_region:
unregister_chrdev_region(devt, 1);
err:
- fc->conn_error = 1;
+ fuse_conn_kill(fc);
goto out;
}
@@ -532,8 +532,6 @@ static int cuse_channel_release(struct inode *inode, struct file *file)
cdev_del(cc->cdev);
}
- /* kill connection and shutdown channel */
- fuse_conn_kill(&cc->fc);
rc = fuse_dev_release(inode, file); /* puts the base reference */
return rc;
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 7df2b5e8fbe1..f4246cfc8d87 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1576,6 +1576,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
req->pages[req->num_pages] = page;
req->num_pages++;
+ offset = 0;
num -= this_num;
total_len += this_num;
index++;
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 8964cf3999b2..324bc0850534 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -383,6 +383,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
struct fuse_entry_out outentry;
struct fuse_file *ff;
+ /* Userspace expects S_IFREG in create mode */
+ BUG_ON((mode & S_IFMT) != S_IFREG);
+
forget = fuse_alloc_forget();
err = -ENOMEM;
if (!forget)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index b321a688cde7..aba15f1b7ad2 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -703,13 +703,16 @@ static ssize_t fuse_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct inode *inode = iocb->ki_filp->f_mapping->host;
+ struct fuse_conn *fc = get_fuse_conn(inode);
- if (pos + iov_length(iov, nr_segs) > i_size_read(inode)) {
+ /*
+ * In auto invalidate mode, always update attributes on read.
+ * Otherwise, only update if we attempt to read past EOF (to ensure
+ * i_size is up to date).
+ */
+ if (fc->auto_inval_data ||
+ (pos + iov_length(iov, nr_segs) > i_size_read(inode))) {
int err;
- /*
- * If trying to read past EOF, make sure the i_size
- * attribute is up-to-date.
- */
err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL);
if (err)
return err;
@@ -944,9 +947,8 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
return err;
count = ocount;
-
+ sb_start_write(inode->i_sb);
mutex_lock(&inode->i_mutex);
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
/* We can write back this queue in page reclaim */
current->backing_dev_info = mapping->backing_dev_info;
@@ -1004,6 +1006,7 @@ static ssize_t fuse_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
out:
current->backing_dev_info = NULL;
mutex_unlock(&inode->i_mutex);
+ sb_end_write(inode->i_sb);
return written ? written : err;
}
@@ -1700,7 +1703,7 @@ static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count)
size_t n;
u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT;
- for (n = 0; n < count; n++) {
+ for (n = 0; n < count; n++, iov++) {
if (iov->iov_len > (size_t) max)
return -ENOMEM;
max -= iov->iov_len;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 771fb6322c07..e24dd74e3068 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -484,6 +484,9 @@ struct fuse_conn {
/** Is fallocate not implemented by fs? */
unsigned no_fallocate:1;
+ /** Use enhanced/automatic page cache invalidation. */
+ unsigned auto_inval_data:1;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 1cd61652018c..fca222dabe3c 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -197,6 +197,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
loff_t oldsize;
+ struct timespec old_mtime;
spin_lock(&fc->lock);
if (attr_version != 0 && fi->attr_version > attr_version) {
@@ -204,15 +205,35 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
return;
}
+ old_mtime = inode->i_mtime;
fuse_change_attributes_common(inode, attr, attr_valid);
oldsize = inode->i_size;
i_size_write(inode, attr->size);
spin_unlock(&fc->lock);
- if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
- truncate_pagecache(inode, oldsize, attr->size);
- invalidate_inode_pages2(inode->i_mapping);
+ if (S_ISREG(inode->i_mode)) {
+ bool inval = false;
+
+ if (oldsize != attr->size) {
+ truncate_pagecache(inode, oldsize, attr->size);
+ inval = true;
+ } else if (fc->auto_inval_data) {
+ struct timespec new_mtime = {
+ .tv_sec = attr->mtime,
+ .tv_nsec = attr->mtimensec,
+ };
+
+ /*
+ * Auto inval mode also checks and invalidates if mtime
+ * has changed.
+ */
+ if (!timespec_equal(&old_mtime, &new_mtime))
+ inval = true;
+ }
+
+ if (inval)
+ invalidate_inode_pages2(inode->i_mapping);
}
}
@@ -346,11 +367,6 @@ void fuse_conn_kill(struct fuse_conn *fc)
wake_up_all(&fc->waitq);
wake_up_all(&fc->blocked_waitq);
wake_up_all(&fc->reserved_req_waitq);
- mutex_lock(&fuse_mutex);
- list_del(&fc->entry);
- fuse_ctl_remove_conn(fc);
- mutex_unlock(&fuse_mutex);
- fuse_bdi_destroy(fc);
}
EXPORT_SYMBOL_GPL(fuse_conn_kill);
@@ -359,7 +375,14 @@ static void fuse_put_super(struct super_block *sb)
struct fuse_conn *fc = get_fuse_conn_super(sb);
fuse_send_destroy(fc);
+
fuse_conn_kill(fc);
+ mutex_lock(&fuse_mutex);
+ list_del(&fc->entry);
+ fuse_ctl_remove_conn(fc);
+ mutex_unlock(&fuse_mutex);
+ fuse_bdi_destroy(fc);
+
fuse_conn_put(fc);
}
@@ -834,6 +857,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->big_writes = 1;
if (arg->flags & FUSE_DONT_MASK)
fc->dont_mask = 1;
+ if (arg->flags & FUSE_AUTO_INVAL_DATA)
+ fc->auto_inval_data = 1;
} else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1;
@@ -859,7 +884,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
- FUSE_FLOCK_LOCKS;
+ FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
+ FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 9aa6af13823c..382000ffac1f 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -323,6 +323,29 @@ static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
/**
+ * gfs2_size_hint - Give a hint to the size of a write request
+ * @file: The struct file
+ * @offset: The file offset of the write
+ * @size: The length of the write
+ *
+ * When we are about to do a write, this function records the total
+ * write size in order to provide a suitable hint to the lower layers
+ * about how many blocks will be required.
+ *
+ */
+
+static void gfs2_size_hint(struct file *filep, loff_t offset, size_t size)
+{
+ struct inode *inode = filep->f_dentry->d_inode;
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
+ struct gfs2_inode *ip = GFS2_I(inode);
+ size_t blks = (size + sdp->sd_sb.sb_bsize - 1) >> sdp->sd_sb.sb_bsize_shift;
+ int hint = min_t(size_t, INT_MAX, blks);
+
+ atomic_set(&ip->i_res->rs_sizehint, hint);
+}
+
+/**
* gfs2_allocate_page_backing - Use bmap to allocate blocks
* @page: The (locked) page to allocate backing for
*
@@ -373,18 +396,16 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
loff_t size;
int ret;
- /* Wait if fs is frozen. This is racy so we check again later on
- * and retry if the fs has been frozen after the page lock has
- * been acquired
- */
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+ sb_start_pagefault(inode->i_sb);
+
+ /* Update file times before taking page lock */
+ file_update_time(vma->vm_file);
ret = gfs2_rs_alloc(ip);
if (ret)
return ret;
- atomic_set(&ip->i_res->rs_sizehint,
- PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift);
+ gfs2_size_hint(vma->vm_file, pos, PAGE_CACHE_SIZE);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
@@ -462,14 +483,9 @@ out:
gfs2_holder_uninit(&gh);
if (ret == 0) {
set_page_dirty(page);
- /* This check must be post dropping of transaction lock */
- if (inode->i_sb->s_frozen == SB_UNFROZEN) {
- wait_on_page_writeback(page);
- } else {
- ret = -EAGAIN;
- unlock_page(page);
- }
+ wait_on_page_writeback(page);
}
+ sb_end_pagefault(inode->i_sb);
return block_page_mkwrite_return(ret);
}
@@ -669,7 +685,8 @@ static ssize_t gfs2_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (ret)
return ret;
- atomic_set(&ip->i_res->rs_sizehint, writesize >> sdp->sd_sb.sb_bsize_shift);
+ gfs2_size_hint(file, pos, writesize);
+
if (file->f_flags & O_APPEND) {
struct gfs2_holder gh;
@@ -795,7 +812,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
if (unlikely(error))
goto out_uninit;
- atomic_set(&ip->i_res->rs_sizehint, len >> sdp->sd_sb.sb_bsize_shift);
+ gfs2_size_hint(file, offset, len);
while (len > 0) {
if (len < bytes)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 4ce22e547308..753af3d86bbc 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1722,7 +1722,9 @@ static int gfs2_setxattr(struct dentry *dentry, const char *name,
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
- ret = generic_setxattr(dentry, name, data, size, flags);
+ ret = gfs2_rs_alloc(ip);
+ if (ret == 0)
+ ret = generic_setxattr(dentry, name, data, size, flags);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
@@ -1757,7 +1759,9 @@ static int gfs2_removexattr(struct dentry *dentry, const char *name)
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
- ret = generic_removexattr(dentry, name);
+ ret = gfs2_rs_alloc(ip);
+ if (ret == 0)
+ ret = generic_removexattr(dentry, name);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 3a56c8d94de0..22255d96b27e 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -52,7 +52,7 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb
/*
* If it's a fully non-blocking write attempt and we cannot
* lock the buffer then redirty the page. Note that this can
- * potentially cause a busy-wait loop from pdflush and kswapd
+ * potentially cause a busy-wait loop from flusher thread and kswapd
* activity, but those code paths have their own higher-level
* throttling.
*/
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index 4d34887a601d..c9ed814eeb6f 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -1961,7 +1961,7 @@ static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
* @dinode: 1 if this block is a dinode block, otherwise data block
* @nblocks: desired extent length
*
- * Lay claim to previously allocated block reservation blocks.
+ * Lay claim to previously reserved blocks.
* Returns: Starting block number of the blocks claimed.
* Sets *nblocks to the actual extent length allocated.
*/
@@ -1970,19 +1970,17 @@ static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
{
struct gfs2_blkreserv *rs = ip->i_res;
struct gfs2_rgrpd *rgd = rs->rs_rgd;
- struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_bitmap *bi;
u64 start_block = gfs2_rs_startblk(rs);
const unsigned int elen = *nblocks;
- /*BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));*/
- gfs2_assert_withdraw(sdp, rgd);
- /*BUG_ON(!gfs2_glock_is_locked_by_me(rgd->rd_gl));*/
bi = rs->rs_bi;
gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
for (*nblocks = 0; *nblocks < elen && rs->rs_free; (*nblocks)++) {
- /* Make sure the bitmap hasn't changed */
+ if (gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
+ bi->bi_len, rs->rs_biblk) != GFS2_BLKST_FREE)
+ break;
gfs2_setbit(rgd, bi->bi_clone, bi, rs->rs_biblk,
dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
rs->rs_biblk++;
@@ -1991,20 +1989,12 @@ static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
BUG_ON(!rgd->rd_reserved);
rgd->rd_reserved--;
dinode = false;
- trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
}
- if (!rs->rs_free) {
- struct gfs2_rgrpd *rgd = ip->i_res->rs_rgd;
-
+ trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
+ if (!rs->rs_free || *nblocks != elen)
gfs2_rs_deltree(rs);
- /* -nblocks because we haven't returned to do the math yet.
- I'm doing the math backwards to prevent negative numbers,
- but think of it as:
- if (unclaimed_blocks(rgd) - *nblocks >= RGRP_RSRV_MINBLKS */
- if (unclaimed_blocks(rgd) >= RGRP_RSRV_MINBLKS + *nblocks)
- rg_mblk_search(rgd, ip);
- }
+
return start_block;
}
@@ -2037,34 +2027,34 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
if (ip->i_res->rs_requested == 0)
return -ECANCELED;
- /* Check if we have a multi-block reservation, and if so, claim the
- next free block from it. */
+ /* If we have a reservation, claim blocks from it. */
if (gfs2_rs_active(ip->i_res)) {
BUG_ON(!ip->i_res->rs_free);
rgd = ip->i_res->rs_rgd;
block = claim_reserved_blks(ip, dinode, nblocks);
- } else {
- rgd = ip->i_rgd;
+ if (*nblocks)
+ goto found_blocks;
+ }
- if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
- goal = ip->i_goal - rgd->rd_data0;
- else
- goal = rgd->rd_last_alloc;
-
- blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
-
- /* Since all blocks are reserved in advance, this shouldn't
- happen */
- if (blk == BFITNOENT) {
- printk(KERN_WARNING "BFITNOENT, nblocks=%u\n",
- *nblocks);
- printk(KERN_WARNING "FULL=%d\n",
- test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
- goto rgrp_error;
- }
+ rgd = ip->i_rgd;
- block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
+ if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
+ goal = ip->i_goal - rgd->rd_data0;
+ else
+ goal = rgd->rd_last_alloc;
+
+ blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
+
+ /* Since all blocks are reserved in advance, this shouldn't happen */
+ if (blk == BFITNOENT) {
+ printk(KERN_WARNING "BFITNOENT, nblocks=%u\n", *nblocks);
+ printk(KERN_WARNING "FULL=%d\n",
+ test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
+ goto rgrp_error;
}
+
+ block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
+found_blocks:
ndata = *nblocks;
if (dinode)
ndata--;
diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c
index ad3e2fb763d7..adbd27875ef9 100644
--- a/fs/gfs2/trans.c
+++ b/fs/gfs2/trans.c
@@ -50,6 +50,7 @@ int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks,
if (revokes)
tr->tr_reserved += gfs2_struct2blk(sdp, revokes,
sizeof(u64));
+ sb_start_intwrite(sdp->sd_vfs);
gfs2_holder_init(sdp->sd_trans_gl, LM_ST_SHARED, 0, &tr->tr_t_gh);
error = gfs2_glock_nq(&tr->tr_t_gh);
@@ -68,6 +69,7 @@ fail_gunlock:
gfs2_glock_dq(&tr->tr_t_gh);
fail_holder_uninit:
+ sb_end_intwrite(sdp->sd_vfs);
gfs2_holder_uninit(&tr->tr_t_gh);
kfree(tr);
@@ -116,6 +118,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
gfs2_holder_uninit(&tr->tr_t_gh);
kfree(tr);
}
+ sb_end_intwrite(sdp->sd_vfs);
return;
}
@@ -136,6 +139,7 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
gfs2_log_flush(sdp, NULL);
+ sb_end_intwrite(sdp->sd_vfs);
}
/**
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index 5fd51a5833ff..b7ec224910c5 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -236,10 +236,10 @@ out:
* hfs_mdb_commit()
*
* Description:
- * This updates the MDB on disk (look also at hfs_write_super()).
+ * This updates the MDB on disk.
* It does not check, if the superblock has been modified, or
* if the filesystem has been mounted read-only. It is mainly
- * called by hfs_write_super() and hfs_btree_extend().
+ * called by hfs_sync_fs() and flush_mdb().
* Input Variable(s):
* struct hfs_mdb *mdb: Pointer to the hfs MDB
* int backup;
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index e13e9bdb0bf5..8349a899912e 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -416,8 +416,8 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff)
else
v_offset = 0;
- __unmap_hugepage_range(vma,
- vma->vm_start + v_offset, vma->vm_end, NULL);
+ unmap_hugepage_range(vma, vma->vm_start + v_offset,
+ vma->vm_end, NULL);
}
}
diff --git a/fs/inode.c b/fs/inode.c
index 3cc504320467..ac8d904b3f16 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1542,9 +1542,11 @@ void touch_atime(struct path *path)
if (timespec_equal(&inode->i_atime, &now))
return;
- if (mnt_want_write(mnt))
+ if (!sb_start_write_trylock(inode->i_sb))
return;
+ if (__mnt_want_write(mnt))
+ goto skip_update;
/*
* File systems can error out when updating inodes if they need to
* allocate new space to modify an inode (such is the case for
@@ -1555,7 +1557,9 @@ void touch_atime(struct path *path)
* of the fs read only, e.g. subvolumes in Btrfs.
*/
update_time(inode, &now, S_ATIME);
- mnt_drop_write(mnt);
+ __mnt_drop_write(mnt);
+skip_update:
+ sb_end_write(inode->i_sb);
}
EXPORT_SYMBOL(touch_atime);
@@ -1662,11 +1666,11 @@ int file_update_time(struct file *file)
return 0;
/* Finally allowed to write? Takes lock. */
- if (mnt_want_write_file(file))
+ if (__mnt_want_write_file(file))
return 0;
ret = update_time(inode, &now, sync_it);
- mnt_drop_write_file(file);
+ __mnt_drop_write_file(file);
return ret;
}
diff --git a/fs/internal.h b/fs/internal.h
index a6fd56c68b11..371bcc4b1697 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -61,6 +61,10 @@ extern void __init mnt_init(void);
extern struct lglock vfsmount_lock;
+extern int __mnt_want_write(struct vfsmount *);
+extern int __mnt_want_write_file(struct file *);
+extern void __mnt_drop_write(struct vfsmount *);
+extern void __mnt_drop_write_file(struct file *);
/*
* fs_struct.c
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index 425c2f2cf170..a2862339323b 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -534,8 +534,8 @@ int journal_start_commit(journal_t *journal, tid_t *ptid)
ret = 1;
} else if (journal->j_committing_transaction) {
/*
- * If ext3_write_super() recently started a commit, then we
- * have to wait for completion of that transaction
+ * If commit has been started, then we have to wait for
+ * completion of that transaction.
*/
if (ptid)
*ptid = journal->j_committing_transaction->t_tid;
@@ -1113,6 +1113,11 @@ static void mark_journal_empty(journal_t *journal)
BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex));
spin_lock(&journal->j_state_lock);
+ /* Is it already empty? */
+ if (sb->s_start == 0) {
+ spin_unlock(&journal->j_state_lock);
+ return;
+ }
jbd_debug(1, "JBD: Marking journal as empty (seq %d)\n",
journal->j_tail_sequence);
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index e9a3c4c85594..e149b99a7ffb 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -612,8 +612,8 @@ int jbd2_journal_start_commit(journal_t *journal, tid_t *ptid)
ret = 1;
} else if (journal->j_committing_transaction) {
/*
- * If ext3_write_super() recently started a commit, then we
- * have to wait for completion of that transaction
+ * If commit has been started, then we have to wait for
+ * completion of that transaction.
*/
if (ptid)
*ptid = journal->j_committing_transaction->t_tid;
@@ -1377,7 +1377,7 @@ static void jbd2_mark_journal_empty(journal_t *journal)
* Update a journal's errno. Write updated superblock to disk waiting for IO
* to complete.
*/
-static void jbd2_journal_update_sb_errno(journal_t *journal)
+void jbd2_journal_update_sb_errno(journal_t *journal)
{
journal_superblock_t *sb = journal->j_superblock;
@@ -1390,6 +1390,7 @@ static void jbd2_journal_update_sb_errno(journal_t *journal)
jbd2_write_superblock(journal, WRITE_SYNC);
}
+EXPORT_SYMBOL(jbd2_journal_update_sb_errno);
/*
* Read the superblock for a given journal, performing initial
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
index 8392cb85bd54..05d29124c6ab 100644
--- a/fs/lockd/clntproc.c
+++ b/fs/lockd/clntproc.c
@@ -156,12 +156,16 @@ int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl)
struct nlm_rqst *call;
int status;
- nlm_get_host(host);
call = nlm_alloc_call(host);
if (call == NULL)
return -ENOMEM;
nlmclnt_locks_init_private(fl, host);
+ if (!fl->fl_u.nfs_fl.owner) {
+ /* lockowner allocation has failed */
+ nlmclnt_release_call(call);
+ return -ENOMEM;
+ }
/* Set up the argument struct */
nlmclnt_setlockargs(call, fl);
@@ -185,9 +189,6 @@ EXPORT_SYMBOL_GPL(nlmclnt_proc);
/*
* Allocate an NLM RPC call struct
- *
- * Note: the caller must hold a reference to host. In case of failure,
- * this reference will be released.
*/
struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
{
@@ -199,7 +200,7 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
atomic_set(&call->a_count, 1);
locks_init_lock(&call->a_args.lock.fl);
locks_init_lock(&call->a_res.lock.fl);
- call->a_host = host;
+ call->a_host = nlm_get_host(host);
return call;
}
if (signalled())
@@ -207,7 +208,6 @@ struct nlm_rqst *nlm_alloc_call(struct nlm_host *host)
printk("nlm_alloc_call: failed, waiting for memory\n");
schedule_timeout_interruptible(5*HZ);
}
- nlmclnt_release_host(host);
return NULL;
}
@@ -750,7 +750,7 @@ static int nlmclnt_cancel(struct nlm_host *host, int block, struct file_lock *fl
dprintk("lockd: blocking lock attempt was interrupted by a signal.\n"
" Attempting to cancel lock.\n");
- req = nlm_alloc_call(nlm_get_host(host));
+ req = nlm_alloc_call(host);
if (!req)
return -ENOMEM;
req->a_flags = RPC_TASK_ASYNC;
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
index 4a43d253c045..b147d1ae71fd 100644
--- a/fs/lockd/svc4proc.c
+++ b/fs/lockd/svc4proc.c
@@ -257,6 +257,7 @@ static __be32 nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
return rpc_system_err;
call = nlm_alloc_call(host);
+ nlmsvc_release_host(host);
if (call == NULL)
return rpc_system_err;
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index afe4488c33d8..8d80c990dffd 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -219,7 +219,6 @@ nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_host *host,
struct nlm_block *block;
struct nlm_rqst *call = NULL;
- nlm_get_host(host);
call = nlm_alloc_call(host);
if (call == NULL)
return NULL;
@@ -290,7 +289,6 @@ static void nlmsvc_free_block(struct kref *kref)
dprintk("lockd: freeing block %p...\n", block);
/* Remove block from file's list of blocks */
- mutex_lock(&file->f_mutex);
list_del_init(&block->b_flist);
mutex_unlock(&file->f_mutex);
@@ -304,7 +302,7 @@ static void nlmsvc_free_block(struct kref *kref)
static void nlmsvc_release_block(struct nlm_block *block)
{
if (block != NULL)
- kref_put(&block->b_count, nlmsvc_free_block);
+ kref_put_mutex(&block->b_count, nlmsvc_free_block, &block->b_file->f_mutex);
}
/*
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
index de8f2caa2235..3009a365e082 100644
--- a/fs/lockd/svcproc.c
+++ b/fs/lockd/svcproc.c
@@ -297,6 +297,7 @@ static __be32 nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_args
return rpc_system_err;
call = nlm_alloc_call(host);
+ nlmsvc_release_host(host);
if (call == NULL)
return rpc_system_err;
diff --git a/fs/locks.c b/fs/locks.c
index cdcf219a7391..7e81bfc75164 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -200,11 +200,7 @@ void locks_release_private(struct file_lock *fl)
fl->fl_ops->fl_release_private(fl);
fl->fl_ops = NULL;
}
- if (fl->fl_lmops) {
- if (fl->fl_lmops->lm_release_private)
- fl->fl_lmops->lm_release_private(fl);
- fl->fl_lmops = NULL;
- }
+ fl->fl_lmops = NULL;
}
EXPORT_SYMBOL_GPL(locks_release_private);
diff --git a/fs/logfs/dev_bdev.c b/fs/logfs/dev_bdev.c
index df0de27c2733..e784a217b500 100644
--- a/fs/logfs/dev_bdev.c
+++ b/fs/logfs/dev_bdev.c
@@ -26,6 +26,7 @@ static int sync_request(struct page *page, struct block_device *bdev, int rw)
struct completion complete;
bio_init(&bio);
+ bio.bi_max_vecs = 1;
bio.bi_io_vec = &bio_vec;
bio_vec.bv_page = page;
bio_vec.bv_len = PAGE_SIZE;
@@ -95,12 +96,11 @@ static int __bdev_writeseg(struct super_block *sb, u64 ofs, pgoff_t index,
struct address_space *mapping = super->s_mapping_inode->i_mapping;
struct bio *bio;
struct page *page;
- struct request_queue *q = bdev_get_queue(sb->s_bdev);
- unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+ unsigned int max_pages;
int i;
- if (max_pages > BIO_MAX_PAGES)
- max_pages = BIO_MAX_PAGES;
+ max_pages = min(nr_pages, (size_t) bio_get_nr_vecs(super->s_bdev));
+
bio = bio_alloc(GFP_NOFS, max_pages);
BUG_ON(!bio);
@@ -190,12 +190,11 @@ static int do_erase(struct super_block *sb, u64 ofs, pgoff_t index,
{
struct logfs_super *super = logfs_super(sb);
struct bio *bio;
- struct request_queue *q = bdev_get_queue(sb->s_bdev);
- unsigned int max_pages = queue_max_hw_sectors(q) >> (PAGE_SHIFT - 9);
+ unsigned int max_pages;
int i;
- if (max_pages > BIO_MAX_PAGES)
- max_pages = BIO_MAX_PAGES;
+ max_pages = min(nr_pages, (size_t) bio_get_nr_vecs(super->s_bdev));
+
bio = bio_alloc(GFP_NOFS, max_pages);
BUG_ON(!bio);
diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c
index a422f42238b2..6984562738d3 100644
--- a/fs/logfs/inode.c
+++ b/fs/logfs/inode.c
@@ -156,10 +156,26 @@ static void __logfs_destroy_inode(struct inode *inode)
call_rcu(&inode->i_rcu, logfs_i_callback);
}
+static void __logfs_destroy_meta_inode(struct inode *inode)
+{
+ struct logfs_inode *li = logfs_inode(inode);
+ BUG_ON(li->li_block);
+ call_rcu(&inode->i_rcu, logfs_i_callback);
+}
+
static void logfs_destroy_inode(struct inode *inode)
{
struct logfs_inode *li = logfs_inode(inode);
+ if (inode->i_ino < LOGFS_RESERVED_INOS) {
+ /*
+ * The reserved inodes are never destroyed unless we are in
+ * unmont path.
+ */
+ __logfs_destroy_meta_inode(inode);
+ return;
+ }
+
BUG_ON(list_empty(&li->li_freeing_list));
spin_lock(&logfs_inode_lock);
li->li_refcount--;
@@ -373,8 +389,8 @@ static void logfs_put_super(struct super_block *sb)
{
struct logfs_super *super = logfs_super(sb);
/* kill the meta-inodes */
- iput(super->s_master_inode);
iput(super->s_segfile_inode);
+ iput(super->s_master_inode);
iput(super->s_mapping_inode);
}
diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c
index 1e1c369df22b..2a09b8d73989 100644
--- a/fs/logfs/journal.c
+++ b/fs/logfs/journal.c
@@ -565,7 +565,7 @@ static void write_wbuf(struct super_block *sb, struct logfs_area *area,
index = ofs >> PAGE_SHIFT;
page_ofs = ofs & (PAGE_SIZE - 1);
- page = find_lock_page(mapping, index);
+ page = find_or_create_page(mapping, index, GFP_NOFS);
BUG_ON(!page);
memcpy(wbuf, page_address(page) + page_ofs, super->s_writesize);
unlock_page(page);
diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c
index f1cb512c5019..5be0abef603d 100644
--- a/fs/logfs/readwrite.c
+++ b/fs/logfs/readwrite.c
@@ -2189,7 +2189,6 @@ void logfs_evict_inode(struct inode *inode)
return;
}
- BUG_ON(inode->i_ino < LOGFS_RESERVED_INOS);
page = inode_to_page(inode);
BUG_ON(!page); /* FIXME: Use emergency page */
logfs_put_write_page(page);
diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c
index e28d090c98d6..038da0991794 100644
--- a/fs/logfs/segment.c
+++ b/fs/logfs/segment.c
@@ -886,7 +886,7 @@ static struct logfs_area *alloc_area(struct super_block *sb)
static void map_invalidatepage(struct page *page, unsigned long l)
{
- BUG();
+ return;
}
static int map_releasepage(struct page *page, gfp_t g)
diff --git a/fs/namei.c b/fs/namei.c
index 2ccc35c4dc24..dd1ed1b8e98e 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -352,6 +352,7 @@ int __inode_permission(struct inode *inode, int mask)
/**
* sb_permission - Check superblock-level permissions
* @sb: Superblock of inode to check permission on
+ * @inode: Inode to check permission on
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
*
* Separate out file-system wide checks from inode-specific permission checks.
@@ -650,6 +651,122 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
path_put(link);
}
+int sysctl_protected_symlinks __read_mostly = 1;
+int sysctl_protected_hardlinks __read_mostly = 1;
+
+/**
+ * may_follow_link - Check symlink following for unsafe situations
+ * @link: The path of the symlink
+ * @nd: nameidata pathwalk data
+ *
+ * In the case of the sysctl_protected_symlinks sysctl being enabled,
+ * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
+ * in a sticky world-writable directory. This is to protect privileged
+ * processes from failing races against path names that may change out
+ * from under them by way of other users creating malicious symlinks.
+ * It will permit symlinks to be followed only when outside a sticky
+ * world-writable directory, or when the uid of the symlink and follower
+ * match, or when the directory owner matches the symlink's owner.
+ *
+ * Returns 0 if following the symlink is allowed, -ve on error.
+ */
+static inline int may_follow_link(struct path *link, struct nameidata *nd)
+{
+ const struct inode *inode;
+ const struct inode *parent;
+
+ if (!sysctl_protected_symlinks)
+ return 0;
+
+ /* Allowed if owner and follower match. */
+ inode = link->dentry->d_inode;
+ if (current_cred()->fsuid == inode->i_uid)
+ return 0;
+
+ /* Allowed if parent directory not sticky and world-writable. */
+ parent = nd->path.dentry->d_inode;
+ if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
+ return 0;
+
+ /* Allowed if parent directory and link owner match. */
+ if (parent->i_uid == inode->i_uid)
+ return 0;
+
+ path_put_conditional(link, nd);
+ path_put(&nd->path);
+ audit_log_link_denied("follow_link", link);
+ return -EACCES;
+}
+
+/**
+ * safe_hardlink_source - Check for safe hardlink conditions
+ * @inode: the source inode to hardlink from
+ *
+ * Return false if at least one of the following conditions:
+ * - inode is not a regular file
+ * - inode is setuid
+ * - inode is setgid and group-exec
+ * - access failure for read and write
+ *
+ * Otherwise returns true.
+ */
+static bool safe_hardlink_source(struct inode *inode)
+{
+ umode_t mode = inode->i_mode;
+
+ /* Special files should not get pinned to the filesystem. */
+ if (!S_ISREG(mode))
+ return false;
+
+ /* Setuid files should not get pinned to the filesystem. */
+ if (mode & S_ISUID)
+ return false;
+
+ /* Executable setgid files should not get pinned to the filesystem. */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+ return false;
+
+ /* Hardlinking to unreadable or unwritable sources is dangerous. */
+ if (inode_permission(inode, MAY_READ | MAY_WRITE))
+ return false;
+
+ return true;
+}
+
+/**
+ * may_linkat - Check permissions for creating a hardlink
+ * @link: the source to hardlink from
+ *
+ * Block hardlink when all of:
+ * - sysctl_protected_hardlinks enabled
+ * - fsuid does not match inode
+ * - hardlink source is unsafe (see safe_hardlink_source() above)
+ * - not CAP_FOWNER
+ *
+ * Returns 0 if successful, -ve on error.
+ */
+static int may_linkat(struct path *link)
+{
+ const struct cred *cred;
+ struct inode *inode;
+
+ if (!sysctl_protected_hardlinks)
+ return 0;
+
+ cred = current_cred();
+ inode = link->dentry->d_inode;
+
+ /* Source inode owner (or CAP_FOWNER) can hardlink all they like,
+ * otherwise, it must be a safe source.
+ */
+ if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) ||
+ capable(CAP_FOWNER))
+ return 0;
+
+ audit_log_link_denied("linkat", link);
+ return -EPERM;
+}
+
static __always_inline int
follow_link(struct path *link, struct nameidata *nd, void **p)
{
@@ -1818,6 +1935,9 @@ static int path_lookupat(int dfd, const char *name,
while (err > 0) {
void *cookie;
struct path link = path;
+ err = may_follow_link(&link, nd);
+ if (unlikely(err))
+ break;
nd->flags |= LOOKUP_PARENT;
err = follow_link(&link, nd, &cookie);
if (err)
@@ -2277,7 +2397,7 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
static int atomic_open(struct nameidata *nd, struct dentry *dentry,
struct path *path, struct file *file,
const struct open_flags *op,
- bool *want_write, bool need_lookup,
+ bool got_write, bool need_lookup,
int *opened)
{
struct inode *dir = nd->path.dentry->d_inode;
@@ -2296,11 +2416,11 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
goto out;
}
- mode = op->mode & S_IALLUGO;
+ mode = op->mode;
if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
mode &= ~current_umask();
- if (open_flag & O_EXCL) {
+ if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
open_flag &= ~O_TRUNC;
*opened |= FILE_CREATED;
}
@@ -2314,12 +2434,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
* Another problem is returing the "right" error value (e.g. for an
* O_EXCL open we want to return EEXIST not EROFS).
*/
- if ((open_flag & (O_CREAT | O_TRUNC)) ||
- (open_flag & O_ACCMODE) != O_RDONLY) {
- error = mnt_want_write(nd->path.mnt);
- if (!error) {
- *want_write = true;
- } else if (!(open_flag & O_CREAT)) {
+ if (((open_flag & (O_CREAT | O_TRUNC)) ||
+ (open_flag & O_ACCMODE) != O_RDONLY) && unlikely(!got_write)) {
+ if (!(open_flag & O_CREAT)) {
/*
* No O_CREATE -> atomicity not a requirement -> fall
* back to lookup + open
@@ -2327,17 +2444,17 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
goto no_open;
} else if (open_flag & (O_EXCL | O_TRUNC)) {
/* Fall back and fail with the right error */
- create_error = error;
+ create_error = -EROFS;
goto no_open;
} else {
/* No side effects, safe to clear O_CREAT */
- create_error = error;
+ create_error = -EROFS;
open_flag &= ~O_CREAT;
}
}
if (open_flag & O_CREAT) {
- error = may_o_create(&nd->path, dentry, op->mode);
+ error = may_o_create(&nd->path, dentry, mode);
if (error) {
create_error = error;
if (open_flag & O_EXCL)
@@ -2374,6 +2491,10 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
dput(dentry);
dentry = file->f_path.dentry;
}
+ if (create_error && dentry->d_inode == NULL) {
+ error = create_error;
+ goto out;
+ }
goto looked_up;
}
@@ -2438,7 +2559,7 @@ looked_up:
static int lookup_open(struct nameidata *nd, struct path *path,
struct file *file,
const struct open_flags *op,
- bool *want_write, int *opened)
+ bool got_write, int *opened)
{
struct dentry *dir = nd->path.dentry;
struct inode *dir_inode = dir->d_inode;
@@ -2456,7 +2577,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
goto out_no_open;
if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {
- return atomic_open(nd, dentry, path, file, op, want_write,
+ return atomic_open(nd, dentry, path, file, op, got_write,
need_lookup, opened);
}
@@ -2480,10 +2601,10 @@ static int lookup_open(struct nameidata *nd, struct path *path,
* a permanent write count is taken through
* the 'struct file' in finish_open().
*/
- error = mnt_want_write(nd->path.mnt);
- if (error)
+ if (!got_write) {
+ error = -EROFS;
goto out_dput;
- *want_write = true;
+ }
*opened |= FILE_CREATED;
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
@@ -2513,7 +2634,7 @@ static int do_last(struct nameidata *nd, struct path *path,
struct dentry *dir = nd->path.dentry;
int open_flag = op->open_flag;
bool will_truncate = (open_flag & O_TRUNC) != 0;
- bool want_write = false;
+ bool got_write = false;
int acc_mode = op->acc_mode;
struct inode *inode;
bool symlink_ok = false;
@@ -2582,8 +2703,18 @@ static int do_last(struct nameidata *nd, struct path *path,
}
retry_lookup:
+ if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
+ error = mnt_want_write(nd->path.mnt);
+ if (!error)
+ got_write = true;
+ /*
+ * do _not_ fail yet - we might not need that or fail with
+ * a different error; let lookup_open() decide; we'll be
+ * dropping this one anyway.
+ */
+ }
mutex_lock(&dir->d_inode->i_mutex);
- error = lookup_open(nd, path, file, op, &want_write, opened);
+ error = lookup_open(nd, path, file, op, got_write, opened);
mutex_unlock(&dir->d_inode->i_mutex);
if (error <= 0) {
@@ -2608,22 +2739,23 @@ retry_lookup:
}
/*
- * It already exists.
+ * create/update audit record if it already exists.
*/
- audit_inode(pathname, path->dentry);
+ if (path->dentry->d_inode)
+ audit_inode(pathname, path->dentry);
/*
* If atomic_open() acquired write access it is dropped now due to
* possible mount and symlink following (this might be optimized away if
* necessary...)
*/
- if (want_write) {
+ if (got_write) {
mnt_drop_write(nd->path.mnt);
- want_write = false;
+ got_write = false;
}
error = -EEXIST;
- if (open_flag & O_EXCL)
+ if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
goto exit_dput;
error = follow_managed(path, nd->flags);
@@ -2684,7 +2816,7 @@ finish_open:
error = mnt_want_write(nd->path.mnt);
if (error)
goto out;
- want_write = true;
+ got_write = true;
}
finish_open_created:
error = may_open(&nd->path, acc_mode, open_flag);
@@ -2711,7 +2843,7 @@ opened:
goto exit_fput;
}
out:
- if (want_write)
+ if (got_write)
mnt_drop_write(nd->path.mnt);
path_put(&save_parent);
terminate_walk(nd);
@@ -2735,9 +2867,9 @@ stale_open:
nd->inode = dir->d_inode;
save_parent.mnt = NULL;
save_parent.dentry = NULL;
- if (want_write) {
+ if (got_write) {
mnt_drop_write(nd->path.mnt);
- want_write = false;
+ got_write = false;
}
retried = true;
goto retry_lookup;
@@ -2777,6 +2909,9 @@ static struct file *path_openat(int dfd, const char *pathname,
error = -ELOOP;
break;
}
+ error = may_follow_link(&link, nd);
+ if (unlikely(error))
+ break;
nd->flags |= LOOKUP_PARENT;
nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
error = follow_link(&link, nd, &cookie);
@@ -2846,6 +2981,7 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
{
struct dentry *dentry = ERR_PTR(-EEXIST);
struct nameidata nd;
+ int err2;
int error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd);
if (error)
return ERR_PTR(error);
@@ -2859,16 +2995,19 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
nd.flags &= ~LOOKUP_PARENT;
nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;
+ /* don't fail immediately if it's r/o, at least try to report other errors */
+ err2 = mnt_want_write(nd.path.mnt);
/*
* Do the final lookup.
*/
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
if (IS_ERR(dentry))
- goto fail;
+ goto unlock;
+ error = -EEXIST;
if (dentry->d_inode)
- goto eexist;
+ goto fail;
/*
* Special case - lookup gave negative, but... we had foo/bar/
* From the vfs_mknod() POV we just have a negative dentry -
@@ -2876,23 +3015,37 @@ struct dentry *kern_path_create(int dfd, const char *pathname, struct path *path
* been asking for (non-existent) directory. -ENOENT for you.
*/
if (unlikely(!is_dir && nd.last.name[nd.last.len])) {
- dput(dentry);
- dentry = ERR_PTR(-ENOENT);
+ error = -ENOENT;
+ goto fail;
+ }
+ if (unlikely(err2)) {
+ error = err2;
goto fail;
}
*path = nd.path;
return dentry;
-eexist:
- dput(dentry);
- dentry = ERR_PTR(-EEXIST);
fail:
+ dput(dentry);
+ dentry = ERR_PTR(error);
+unlock:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ if (!err2)
+ mnt_drop_write(nd.path.mnt);
out:
path_put(&nd.path);
return dentry;
}
EXPORT_SYMBOL(kern_path_create);
+void done_path_create(struct path *path, struct dentry *dentry)
+{
+ dput(dentry);
+ mutex_unlock(&path->dentry->d_inode->i_mutex);
+ mnt_drop_write(path->mnt);
+ path_put(path);
+}
+EXPORT_SYMBOL(done_path_create);
+
struct dentry *user_path_create(int dfd, const char __user *pathname, struct path *path, int is_dir)
{
char *tmp = getname(pathname);
@@ -2956,8 +3109,9 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
struct path path;
int error;
- if (S_ISDIR(mode))
- return -EPERM;
+ error = may_mknod(mode);
+ if (error)
+ return error;
dentry = user_path_create(dfd, filename, &path, 0);
if (IS_ERR(dentry))
@@ -2965,15 +3119,9 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
if (!IS_POSIXACL(path.dentry->d_inode))
mode &= ~current_umask();
- error = may_mknod(mode);
- if (error)
- goto out_dput;
- error = mnt_want_write(path.mnt);
- if (error)
- goto out_dput;
error = security_path_mknod(&path, dentry, mode, dev);
if (error)
- goto out_drop_write;
+ goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
error = vfs_create(path.dentry->d_inode,dentry,mode,true);
@@ -2986,13 +3134,8 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, umode_t, mode,
error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
break;
}
-out_drop_write:
- mnt_drop_write(path.mnt);
-out_dput:
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
-
+out:
+ done_path_create(&path, dentry);
return error;
}
@@ -3038,19 +3181,10 @@ SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
if (!IS_POSIXACL(path.dentry->d_inode))
mode &= ~current_umask();
- error = mnt_want_write(path.mnt);
- if (error)
- goto out_dput;
error = security_path_mkdir(&path, dentry, mode);
- if (error)
- goto out_drop_write;
- error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
-out_drop_write:
- mnt_drop_write(path.mnt);
-out_dput:
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ if (!error)
+ error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ done_path_create(&path, dentry);
return error;
}
@@ -3144,6 +3278,9 @@ static long do_rmdir(int dfd, const char __user *pathname)
}
nd.flags &= ~LOOKUP_PARENT;
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto exit1;
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
@@ -3154,19 +3291,15 @@ static long do_rmdir(int dfd, const char __user *pathname)
error = -ENOENT;
goto exit3;
}
- error = mnt_want_write(nd.path.mnt);
- if (error)
- goto exit3;
error = security_path_rmdir(&nd.path, dentry);
if (error)
- goto exit4;
+ goto exit3;
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
-exit4:
- mnt_drop_write(nd.path.mnt);
exit3:
dput(dentry);
exit2:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ mnt_drop_write(nd.path.mnt);
exit1:
path_put(&nd.path);
putname(name);
@@ -3233,6 +3366,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
goto exit1;
nd.flags &= ~LOOKUP_PARENT;
+ error = mnt_want_write(nd.path.mnt);
+ if (error)
+ goto exit1;
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
@@ -3245,21 +3381,17 @@ static long do_unlinkat(int dfd, const char __user *pathname)
if (!inode)
goto slashes;
ihold(inode);
- error = mnt_want_write(nd.path.mnt);
- if (error)
- goto exit2;
error = security_path_unlink(&nd.path, dentry);
if (error)
- goto exit3;
+ goto exit2;
error = vfs_unlink(nd.path.dentry->d_inode, dentry);
-exit3:
- mnt_drop_write(nd.path.mnt);
- exit2:
+exit2:
dput(dentry);
}
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
if (inode)
iput(inode); /* truncate the inode here */
+ mnt_drop_write(nd.path.mnt);
exit1:
path_put(&nd.path);
putname(name);
@@ -3324,19 +3456,10 @@ SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
if (IS_ERR(dentry))
goto out_putname;
- error = mnt_want_write(path.mnt);
- if (error)
- goto out_dput;
error = security_path_symlink(&path, dentry, from);
- if (error)
- goto out_drop_write;
- error = vfs_symlink(path.dentry->d_inode, dentry, from);
-out_drop_write:
- mnt_drop_write(path.mnt);
-out_dput:
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
+ if (!error)
+ error = vfs_symlink(path.dentry->d_inode, dentry, from);
+ done_path_create(&path, dentry);
out_putname:
putname(from);
return error;
@@ -3436,19 +3559,15 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
error = -EXDEV;
if (old_path.mnt != new_path.mnt)
goto out_dput;
- error = mnt_want_write(new_path.mnt);
- if (error)
+ error = may_linkat(&old_path);
+ if (unlikely(error))
goto out_dput;
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
- goto out_drop_write;
+ goto out_dput;
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
-out_drop_write:
- mnt_drop_write(new_path.mnt);
out_dput:
- dput(new_dentry);
- mutex_unlock(&new_path.dentry->d_inode->i_mutex);
- path_put(&new_path);
+ done_path_create(&new_path, new_dentry);
out:
path_put(&old_path);
@@ -3644,6 +3763,10 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
if (newnd.last_type != LAST_NORM)
goto exit2;
+ error = mnt_want_write(oldnd.path.mnt);
+ if (error)
+ goto exit2;
+
oldnd.flags &= ~LOOKUP_PARENT;
newnd.flags &= ~LOOKUP_PARENT;
newnd.flags |= LOOKUP_RENAME_TARGET;
@@ -3679,23 +3802,19 @@ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
if (new_dentry == trap)
goto exit5;
- error = mnt_want_write(oldnd.path.mnt);
- if (error)
- goto exit5;
error = security_path_rename(&oldnd.path, old_dentry,
&newnd.path, new_dentry);
if (error)
- goto exit6;
+ goto exit5;
error = vfs_rename(old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
-exit6:
- mnt_drop_write(oldnd.path.mnt);
exit5:
dput(new_dentry);
exit4:
dput(old_dentry);
exit3:
unlock_rename(new_dir, old_dir);
+ mnt_drop_write(oldnd.path.mnt);
exit2:
path_put(&newnd.path);
putname(to);
diff --git a/fs/namespace.c b/fs/namespace.c
index c53d3381b0d0..7bdf7907413f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -283,24 +283,22 @@ static int mnt_is_readonly(struct vfsmount *mnt)
}
/*
- * Most r/o checks on a fs are for operations that take
- * discrete amounts of time, like a write() or unlink().
- * We must keep track of when those operations start
- * (for permission checks) and when they end, so that
- * we can determine when writes are able to occur to
- * a filesystem.
+ * Most r/o & frozen checks on a fs are for operations that take discrete
+ * amounts of time, like a write() or unlink(). We must keep track of when
+ * those operations start (for permission checks) and when they end, so that we
+ * can determine when writes are able to occur to a filesystem.
*/
/**
- * mnt_want_write - get write access to a mount
+ * __mnt_want_write - get write access to a mount without freeze protection
* @m: the mount on which to take a write
*
- * This tells the low-level filesystem that a write is
- * about to be performed to it, and makes sure that
- * writes are allowed before returning success. When
- * the write operation is finished, mnt_drop_write()
- * must be called. This is effectively a refcount.
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mnt it read-write) before
+ * returning success. This operation does not protect against filesystem being
+ * frozen. When the write operation is finished, __mnt_drop_write() must be
+ * called. This is effectively a refcount.
*/
-int mnt_want_write(struct vfsmount *m)
+int __mnt_want_write(struct vfsmount *m)
{
struct mount *mnt = real_mount(m);
int ret = 0;
@@ -326,6 +324,27 @@ int mnt_want_write(struct vfsmount *m)
ret = -EROFS;
}
preempt_enable();
+
+ return ret;
+}
+
+/**
+ * mnt_want_write - get write access to a mount
+ * @m: the mount on which to take a write
+ *
+ * This tells the low-level filesystem that a write is about to be performed to
+ * it, and makes sure that writes are allowed (mount is read-write, filesystem
+ * is not frozen) before returning success. When the write operation is
+ * finished, mnt_drop_write() must be called. This is effectively a refcount.
+ */
+int mnt_want_write(struct vfsmount *m)
+{
+ int ret;
+
+ sb_start_write(m->mnt_sb);
+ ret = __mnt_want_write(m);
+ if (ret)
+ sb_end_write(m->mnt_sb);
return ret;
}
EXPORT_SYMBOL_GPL(mnt_want_write);
@@ -355,38 +374,76 @@ int mnt_clone_write(struct vfsmount *mnt)
EXPORT_SYMBOL_GPL(mnt_clone_write);
/**
- * mnt_want_write_file - get write access to a file's mount
+ * __mnt_want_write_file - get write access to a file's mount
* @file: the file who's mount on which to take a write
*
- * This is like mnt_want_write, but it takes a file and can
+ * This is like __mnt_want_write, but it takes a file and can
* do some optimisations if the file is open for write already
*/
-int mnt_want_write_file(struct file *file)
+int __mnt_want_write_file(struct file *file)
{
struct inode *inode = file->f_dentry->d_inode;
+
if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
- return mnt_want_write(file->f_path.mnt);
+ return __mnt_want_write(file->f_path.mnt);
else
return mnt_clone_write(file->f_path.mnt);
}
+
+/**
+ * mnt_want_write_file - get write access to a file's mount
+ * @file: the file who's mount on which to take a write
+ *
+ * This is like mnt_want_write, but it takes a file and can
+ * do some optimisations if the file is open for write already
+ */
+int mnt_want_write_file(struct file *file)
+{
+ int ret;
+
+ sb_start_write(file->f_path.mnt->mnt_sb);
+ ret = __mnt_want_write_file(file);
+ if (ret)
+ sb_end_write(file->f_path.mnt->mnt_sb);
+ return ret;
+}
EXPORT_SYMBOL_GPL(mnt_want_write_file);
/**
- * mnt_drop_write - give up write access to a mount
+ * __mnt_drop_write - give up write access to a mount
* @mnt: the mount on which to give up write access
*
* Tells the low-level filesystem that we are done
* performing writes to it. Must be matched with
- * mnt_want_write() call above.
+ * __mnt_want_write() call above.
*/
-void mnt_drop_write(struct vfsmount *mnt)
+void __mnt_drop_write(struct vfsmount *mnt)
{
preempt_disable();
mnt_dec_writers(real_mount(mnt));
preempt_enable();
}
+
+/**
+ * mnt_drop_write - give up write access to a mount
+ * @mnt: the mount on which to give up write access
+ *
+ * Tells the low-level filesystem that we are done performing writes to it and
+ * also allows filesystem to be frozen again. Must be matched with
+ * mnt_want_write() call above.
+ */
+void mnt_drop_write(struct vfsmount *mnt)
+{
+ __mnt_drop_write(mnt);
+ sb_end_write(mnt->mnt_sb);
+}
EXPORT_SYMBOL_GPL(mnt_drop_write);
+void __mnt_drop_write_file(struct file *file)
+{
+ __mnt_drop_write(file->f_path.mnt);
+}
+
void mnt_drop_write_file(struct file *file)
{
mnt_drop_write(file->f_path.mnt);
@@ -1829,8 +1886,14 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
return err;
err = -EINVAL;
- if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(real_mount(path->mnt)))
- goto unlock;
+ if (unlikely(!check_mnt(real_mount(path->mnt)))) {
+ /* that's acceptable only for automounts done in private ns */
+ if (!(mnt_flags & MNT_SHRINKABLE))
+ goto unlock;
+ /* ... and for those we'd better have mountpoint still alive */
+ if (!real_mount(path->mnt)->mnt_ns)
+ goto unlock;
+ }
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index 195c1ea6151a..db7ad719628a 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -86,6 +86,14 @@ config NFS_V4
If unsure, say Y.
+config NFS_SWAP
+ bool "Provide swap over NFS support"
+ default n
+ depends on NFS_FS
+ select SUNRPC_SWAP
+ help
+ This option enables swapon to work on files located on NFS mounts.
+
config NFS_V4_1
bool "NFS client support for NFSv4.1 (EXPERIMENTAL)"
depends on NFS_V4 && EXPERIMENTAL
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 8bf3a3f6925a..b7db60897f91 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -12,19 +12,19 @@ nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
-obj-$(CONFIG_NFS_V2) += nfs2.o
-nfs2-y := nfs2super.o proc.o nfs2xdr.o
+obj-$(CONFIG_NFS_V2) += nfsv2.o
+nfsv2-y := nfs2super.o proc.o nfs2xdr.o
-obj-$(CONFIG_NFS_V3) += nfs3.o
-nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
-nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
+obj-$(CONFIG_NFS_V3) += nfsv3.o
+nfsv3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
+nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
-obj-$(CONFIG_NFS_V4) += nfs4.o
-nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
+obj-$(CONFIG_NFS_V4) += nfsv4.o
+nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o nfs4getroot.o nfs4client.o
-nfs4-$(CONFIG_SYSCTL) += nfs4sysctl.o
-nfs4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
+nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
+nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 9fc0d9dfc91b..99694442b93f 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -105,7 +105,7 @@ struct nfs_subversion *get_nfs_version(unsigned int version)
if (IS_ERR(nfs)) {
mutex_lock(&nfs_version_mutex);
- request_module("nfs%d", version);
+ request_module("nfsv%d", version);
nfs = find_nfs_version(version);
mutex_unlock(&nfs_version_mutex);
}
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index b7b4f80968b5..1ba385b7c90d 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -115,17 +115,28 @@ static inline int put_dreq(struct nfs_direct_req *dreq)
* @nr_segs: size of iovec array
*
* The presence of this routine in the address space ops vector means
- * the NFS client supports direct I/O. However, we shunt off direct
- * read and write requests before the VFS gets them, so this method
- * should never be called.
+ * the NFS client supports direct I/O. However, for most direct IO, we
+ * shunt off direct read and write requests before the VFS gets them,
+ * so this method is only ever called for swap.
*/
ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t pos, unsigned long nr_segs)
{
+#ifndef CONFIG_NFS_SWAP
dprintk("NFS: nfs_direct_IO (%s) off/no(%Ld/%lu) EINVAL\n",
iocb->ki_filp->f_path.dentry->d_name.name,
(long long) pos, nr_segs);
return -EINVAL;
+#else
+ VM_BUG_ON(iocb->ki_left != PAGE_SIZE);
+ VM_BUG_ON(iocb->ki_nbytes != PAGE_SIZE);
+
+ if (rw == READ || rw == KERNEL_READ)
+ return nfs_file_direct_read(iocb, iov, nr_segs, pos,
+ rw == READ ? true : false);
+ return nfs_file_direct_write(iocb, iov, nr_segs, pos,
+ rw == WRITE ? true : false);
+#endif /* CONFIG_NFS_SWAP */
}
static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
@@ -303,7 +314,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_read_completion_ops = {
*/
static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *desc,
const struct iovec *iov,
- loff_t pos)
+ loff_t pos, bool uio)
{
struct nfs_direct_req *dreq = desc->pg_dreq;
struct nfs_open_context *ctx = dreq->ctx;
@@ -331,12 +342,20 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
GFP_KERNEL);
if (!pagevec)
break;
- down_read(&current->mm->mmap_sem);
- result = get_user_pages(current, current->mm, user_addr,
+ if (uio) {
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm, user_addr,
npages, 1, 0, pagevec, NULL);
- up_read(&current->mm->mmap_sem);
- if (result < 0)
- break;
+ up_read(&current->mm->mmap_sem);
+ if (result < 0)
+ break;
+ } else {
+ WARN_ON(npages != 1);
+ result = get_kernel_page(user_addr, 1, pagevec);
+ if (WARN_ON(result != 1))
+ break;
+ }
+
if ((unsigned)result < npages) {
bytes = result * PAGE_SIZE;
if (bytes <= pgbase) {
@@ -386,7 +405,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_pageio_descriptor *de
static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
const struct iovec *iov,
unsigned long nr_segs,
- loff_t pos)
+ loff_t pos, bool uio)
{
struct nfs_pageio_descriptor desc;
ssize_t result = -EINVAL;
@@ -400,7 +419,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg];
- result = nfs_direct_read_schedule_segment(&desc, vec, pos);
+ result = nfs_direct_read_schedule_segment(&desc, vec, pos, uio);
if (result < 0)
break;
requested_bytes += result;
@@ -426,7 +445,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
}
static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+ unsigned long nr_segs, loff_t pos, bool uio)
{
ssize_t result = -ENOMEM;
struct inode *inode = iocb->ki_filp->f_mapping->host;
@@ -444,7 +463,7 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov,
if (!is_sync_kiocb(iocb))
dreq->iocb = iocb;
- result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos);
+ result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos, uio);
if (!result)
result = nfs_direct_wait(dreq);
NFS_I(inode)->read_io += result;
@@ -610,7 +629,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
*/
static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *desc,
const struct iovec *iov,
- loff_t pos)
+ loff_t pos, bool uio)
{
struct nfs_direct_req *dreq = desc->pg_dreq;
struct nfs_open_context *ctx = dreq->ctx;
@@ -638,12 +657,19 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_pageio_descriptor *d
if (!pagevec)
break;
- down_read(&current->mm->mmap_sem);
- result = get_user_pages(current, current->mm, user_addr,
- npages, 0, 0, pagevec, NULL);
- up_read(&current->mm->mmap_sem);
- if (result < 0)
- break;
+ if (uio) {
+ down_read(&current->mm->mmap_sem);
+ result = get_user_pages(current, current->mm, user_addr,
+ npages, 0, 0, pagevec, NULL);
+ up_read(&current->mm->mmap_sem);
+ if (result < 0)
+ break;
+ } else {
+ WARN_ON(npages != 1);
+ result = get_kernel_page(user_addr, 0, pagevec);
+ if (WARN_ON(result != 1))
+ break;
+ }
if ((unsigned)result < npages) {
bytes = result * PAGE_SIZE;
@@ -774,7 +800,7 @@ static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = {
static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
const struct iovec *iov,
unsigned long nr_segs,
- loff_t pos)
+ loff_t pos, bool uio)
{
struct nfs_pageio_descriptor desc;
struct inode *inode = dreq->inode;
@@ -790,7 +816,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
for (seg = 0; seg < nr_segs; seg++) {
const struct iovec *vec = &iov[seg];
- result = nfs_direct_write_schedule_segment(&desc, vec, pos);
+ result = nfs_direct_write_schedule_segment(&desc, vec, pos, uio);
if (result < 0)
break;
requested_bytes += result;
@@ -818,7 +844,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos,
- size_t count)
+ size_t count, bool uio)
{
ssize_t result = -ENOMEM;
struct inode *inode = iocb->ki_filp->f_mapping->host;
@@ -836,7 +862,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov,
if (!is_sync_kiocb(iocb))
dreq->iocb = iocb;
- result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos);
+ result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, uio);
if (!result)
result = nfs_direct_wait(dreq);
out_release:
@@ -867,7 +893,7 @@ out:
* cache.
*/
ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+ unsigned long nr_segs, loff_t pos, bool uio)
{
ssize_t retval = -EINVAL;
struct file *file = iocb->ki_filp;
@@ -892,7 +918,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, const struct iovec *iov,
task_io_account_read(count);
- retval = nfs_direct_read(iocb, iov, nr_segs, pos);
+ retval = nfs_direct_read(iocb, iov, nr_segs, pos, uio);
if (retval > 0)
iocb->ki_pos = pos + retval;
@@ -923,7 +949,7 @@ out:
* is no atomic O_APPEND write facility in the NFS protocol.
*/
ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+ unsigned long nr_segs, loff_t pos, bool uio)
{
ssize_t retval = -EINVAL;
struct file *file = iocb->ki_filp;
@@ -955,7 +981,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
task_io_account_write(count);
- retval = nfs_direct_write(iocb, iov, nr_segs, pos, count);
+ retval = nfs_direct_write(iocb, iov, nr_segs, pos, count, uio);
if (retval > 0) {
struct inode *inode = mapping->host;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index b039a17ee941..6a7fcab7ecb3 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -180,7 +180,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
ssize_t result;
if (iocb->ki_filp->f_flags & O_DIRECT)
- return nfs_file_direct_read(iocb, iov, nr_segs, pos);
+ return nfs_file_direct_read(iocb, iov, nr_segs, pos, true);
dprintk("NFS: read(%s/%s, %lu@%lu)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
@@ -287,10 +287,12 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
struct inode *inode = file->f_path.dentry->d_inode;
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (ret != 0)
+ goto out;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
mutex_unlock(&inode->i_mutex);
-
+out:
return ret;
}
@@ -439,7 +441,7 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset)
if (offset != 0)
return;
/* Cancel any unstarted writes on this page */
- nfs_wb_page_cancel(page->mapping->host, page);
+ nfs_wb_page_cancel(page_file_mapping(page)->host, page);
nfs_fscache_invalidate_page(page, page->mapping->host);
}
@@ -484,7 +486,7 @@ static int nfs_release_page(struct page *page, gfp_t gfp)
*/
static int nfs_launder_page(struct page *page)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_inode *nfsi = NFS_I(inode);
dfprintk(PAGECACHE, "NFS: launder_page(%ld, %llu)\n",
@@ -494,6 +496,20 @@ static int nfs_launder_page(struct page *page)
return nfs_wb_page(inode, page);
}
+#ifdef CONFIG_NFS_SWAP
+static int nfs_swap_activate(struct swap_info_struct *sis, struct file *file,
+ sector_t *span)
+{
+ *span = sis->pages;
+ return xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 1);
+}
+
+static void nfs_swap_deactivate(struct file *file)
+{
+ xs_swapper(NFS_CLIENT(file->f_mapping->host)->cl_xprt, 0);
+}
+#endif
+
const struct address_space_operations nfs_file_aops = {
.readpage = nfs_readpage,
.readpages = nfs_readpages,
@@ -508,6 +524,10 @@ const struct address_space_operations nfs_file_aops = {
.migratepage = nfs_migrate_page,
.launder_page = nfs_launder_page,
.error_remove_page = generic_error_remove_page,
+#ifdef CONFIG_NFS_SWAP
+ .swap_activate = nfs_swap_activate,
+ .swap_deactivate = nfs_swap_deactivate,
+#endif
};
/*
@@ -533,7 +553,7 @@ static int nfs_vm_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
nfs_fscache_wait_on_page_write(NFS_I(dentry->d_inode), page);
lock_page(page);
- mapping = page->mapping;
+ mapping = page_file_mapping(page);
if (mapping != dentry->d_inode->i_mapping)
goto out_unlock;
@@ -582,7 +602,7 @@ ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
size_t count = iov_length(iov, nr_segs);
if (iocb->ki_filp->f_flags & O_DIRECT)
- return nfs_file_direct_write(iocb, iov, nr_segs, pos);
+ return nfs_file_direct_write(iocb, iov, nr_segs, pos, true);
dprintk("NFS: write(%s/%s, %lu@%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index b701358c39c3..a850079467d8 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -61,6 +61,12 @@ struct idmap {
struct mutex idmap_mutex;
};
+struct idmap_legacy_upcalldata {
+ struct rpc_pipe_msg pipe_msg;
+ struct idmap_msg idmap_msg;
+ struct idmap *idmap;
+};
+
/**
* nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
* @fattr: fully initialised struct nfs_fattr
@@ -324,6 +330,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
name, namelen, type, data,
data_size, idmap);
+ idmap->idmap_key_cons = NULL;
mutex_unlock(&idmap->idmap_mutex);
}
return ret;
@@ -380,11 +387,13 @@ static const match_table_t nfs_idmap_tokens = {
static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *);
static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
size_t);
+static void idmap_release_pipe(struct inode *);
static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
static const struct rpc_pipe_ops idmap_upcall_ops = {
.upcall = rpc_pipe_generic_upcall,
.downcall = idmap_pipe_downcall,
+ .release_pipe = idmap_release_pipe,
.destroy_msg = idmap_pipe_destroy_msg,
};
@@ -616,7 +625,8 @@ void nfs_idmap_quit(void)
nfs_idmap_quit_keyring();
}
-static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im,
+static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
+ struct idmap_msg *im,
struct rpc_pipe_msg *msg)
{
substring_t substr;
@@ -659,6 +669,7 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
const char *op,
void *aux)
{
+ struct idmap_legacy_upcalldata *data;
struct rpc_pipe_msg *msg;
struct idmap_msg *im;
struct idmap *idmap = (struct idmap *)aux;
@@ -666,15 +677,15 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
int ret = -ENOMEM;
/* msg and im are freed in idmap_pipe_destroy_msg */
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- goto out0;
-
- im = kmalloc(sizeof(*im), GFP_KERNEL);
- if (!im)
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
goto out1;
- ret = nfs_idmap_prepare_message(key->description, im, msg);
+ msg = &data->pipe_msg;
+ im = &data->idmap_msg;
+ data->idmap = idmap;
+
+ ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
if (ret < 0)
goto out2;
@@ -683,15 +694,15 @@ static int nfs_idmap_legacy_upcall(struct key_construction *cons,
ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
if (ret < 0)
- goto out2;
+ goto out3;
return ret;
+out3:
+ idmap->idmap_key_cons = NULL;
out2:
- kfree(im);
+ kfree(data);
out1:
- kfree(msg);
-out0:
complete_request_key(cons, ret);
return ret;
}
@@ -749,9 +760,8 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
}
if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
- ret = mlen;
- complete_request_key(cons, -ENOKEY);
- goto out_incomplete;
+ ret = -ENOKEY;
+ goto out;
}
namelen_in = strnlen(im.im_name, IDMAP_NAMESZ);
@@ -768,16 +778,32 @@ idmap_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
out:
complete_request_key(cons, ret);
-out_incomplete:
return ret;
}
static void
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
+ struct idmap_legacy_upcalldata *data = container_of(msg,
+ struct idmap_legacy_upcalldata,
+ pipe_msg);
+ struct idmap *idmap = data->idmap;
+ struct key_construction *cons;
+ if (msg->errno) {
+ cons = ACCESS_ONCE(idmap->idmap_key_cons);
+ idmap->idmap_key_cons = NULL;
+ complete_request_key(cons, msg->errno);
+ }
/* Free memory allocated in nfs_idmap_legacy_upcall() */
- kfree(msg->data);
- kfree(msg);
+ kfree(data);
+}
+
+static void
+idmap_release_pipe(struct inode *inode)
+{
+ struct rpc_inode *rpci = RPC_I(inode);
+ struct idmap *idmap = (struct idmap *)rpci->private;
+ idmap->idmap_key_cons = NULL;
}
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 2ed6138f32ad..9b47610338f5 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -154,7 +154,7 @@ static void nfs_zap_caches_locked(struct inode *inode)
nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
nfsi->attrtimeo_timestamp = jiffies;
- memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))
nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
else
@@ -897,6 +897,10 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
struct nfs_inode *nfsi = NFS_I(inode);
int ret = 0;
+ /* swapfiles are not supposed to be shared. */
+ if (IS_SWAPFILE(inode))
+ goto out;
+
if (nfs_mapping_need_revalidate_inode(inode)) {
ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
if (ret < 0)
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 8865538b26b6..31fdb03225cd 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -554,13 +554,14 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize)
static inline
unsigned int nfs_page_length(struct page *page)
{
- loff_t i_size = i_size_read(page->mapping->host);
+ loff_t i_size = i_size_read(page_file_mapping(page)->host);
if (i_size > 0) {
+ pgoff_t page_index = page_file_index(page);
pgoff_t end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
- if (page->index < end_index)
+ if (page_index < end_index)
return PAGE_CACHE_SIZE;
- if (page->index == end_index)
+ if (page_index == end_index)
return ((i_size - 1) & ~PAGE_CACHE_MASK) + 1;
}
return 0;
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 0952c791df36..69322096c325 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -69,7 +69,7 @@ do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle,
nfs_fattr_init(info->fattr);
status = rpc_call_sync(client, &msg, 0);
dprintk("%s: reply fsinfo: %d\n", __func__, status);
- if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
+ if (status == 0 && !(info->fattr->valid & NFS_ATTR_FATTR)) {
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
msg.rpc_resp = info->fattr;
status = rpc_call_sync(client, &msg, 0);
@@ -643,7 +643,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page **pages, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
- __be32 *verf = NFS_COOKIEVERF(dir);
+ __be32 *verf = NFS_I(dir)->cookieverf;
struct nfs3_readdirargs arg = {
.fh = NFS_FH(dir),
.cookie = cookie,
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 3b950dd81e81..da0618aeeadb 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -205,6 +205,9 @@ extern const struct dentry_operations nfs4_dentry_operations;
int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t, int *);
+/* super.c */
+extern struct file_system_type nfs4_fs_type;
+
/* nfs4namespace.c */
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index cbcdfaf32505..24eb663f8ed5 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -74,7 +74,7 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
return clp;
error:
- kfree(clp);
+ nfs_free_client(clp);
return ERR_PTR(err);
}
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c
index acb65e7887f8..eb5eb8eef4d3 100644
--- a/fs/nfs/nfs4file.c
+++ b/fs/nfs/nfs4file.c
@@ -96,13 +96,15 @@ nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
struct inode *inode = file->f_path.dentry->d_inode;
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (ret != 0)
+ goto out;
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
if (!ret && !datasync)
/* application has asked for meta-data sync */
ret = pnfs_layoutcommit_inode(inode, true);
mutex_unlock(&inode->i_mutex);
-
+out:
return ret;
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a99a8d948721..1e50326d00dd 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3215,11 +3215,11 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
dentry->d_parent->d_name.name,
dentry->d_name.name,
(unsigned long long)cookie);
- nfs4_setup_readdir(cookie, NFS_COOKIEVERF(dir), dentry, &args);
+ nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args);
res.pgbase = args.pgbase;
status = nfs4_call_sync(NFS_SERVER(dir)->client, NFS_SERVER(dir), &msg, &args.seq_args, &res.seq_res, 0);
if (status >= 0) {
- memcpy(NFS_COOKIEVERF(dir), res.verifier.data, NFS4_VERIFIER_SIZE);
+ memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE);
status += args.pgbase;
}
@@ -3653,11 +3653,11 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
&& (server->acl_bitmask & ACL4_SUPPORT_DENY_ACL);
}
-/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_CACHE_SIZE, and that
- * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_CACHE_SIZE) bytes on
+/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
+ * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
* the stack.
*/
-#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
+#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
static int buf_to_pages_noslab(const void *buf, size_t buflen,
struct page **pages, unsigned int *pgbase)
@@ -3668,7 +3668,7 @@ static int buf_to_pages_noslab(const void *buf, size_t buflen,
spages = pages;
do {
- len = min_t(size_t, PAGE_CACHE_SIZE, buflen);
+ len = min_t(size_t, PAGE_SIZE, buflen);
newpage = alloc_page(GFP_KERNEL);
if (newpage == NULL)
@@ -3737,9 +3737,10 @@ out:
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
{
struct nfs4_cached_acl *acl;
+ size_t buflen = sizeof(*acl) + acl_len;
- if (pages && acl_len <= PAGE_SIZE) {
- acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
+ if (buflen <= PAGE_SIZE) {
+ acl = kmalloc(buflen, GFP_KERNEL);
if (acl == NULL)
goto out;
acl->cached = 1;
@@ -3781,17 +3782,15 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args,
.rpc_resp = &res,
};
- int ret = -ENOMEM, npages, i;
- size_t acl_len = 0;
+ unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
+ int ret = -ENOMEM, i;
- npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
/* As long as we're doing a round trip to the server anyway,
* let's be prepared for a page of acl data. */
if (npages == 0)
npages = 1;
-
- /* Add an extra page to handle the bitmap returned */
- npages++;
+ if (npages > ARRAY_SIZE(pages))
+ return -ERANGE;
for (i = 0; i < npages; i++) {
pages[i] = alloc_page(GFP_KERNEL);
@@ -3807,11 +3806,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
args.acl_len = npages * PAGE_SIZE;
args.acl_pgbase = 0;
- /* Let decode_getfacl know not to fail if the ACL data is larger than
- * the page we send as a guess */
- if (buf == NULL)
- res.acl_flags |= NFS4_ACL_LEN_REQUEST;
-
dprintk("%s buf %p buflen %zu npages %d args.acl_len %zu\n",
__func__, buf, buflen, npages, args.acl_len);
ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
@@ -3819,20 +3813,19 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
if (ret)
goto out_free;
- acl_len = res.acl_len - res.acl_data_offset;
- if (acl_len > args.acl_len)
- nfs4_write_cached_acl(inode, NULL, 0, acl_len);
- else
- nfs4_write_cached_acl(inode, pages, res.acl_data_offset,
- acl_len);
- if (buf) {
+ /* Handle the case where the passed-in buffer is too short */
+ if (res.acl_flags & NFS4_ACL_TRUNC) {
+ /* Did the user only issue a request for the acl length? */
+ if (buf == NULL)
+ goto out_ok;
ret = -ERANGE;
- if (acl_len > buflen)
- goto out_free;
- _copy_from_pages(buf, pages, res.acl_data_offset,
- acl_len);
+ goto out_free;
}
- ret = acl_len;
+ nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
+ if (buf)
+ _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
+out_ok:
+ ret = res.acl_len;
out_free:
for (i = 0; i < npages; i++)
if (pages[i])
@@ -3890,10 +3883,13 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
.rpc_argp = &arg,
.rpc_resp = &res,
};
+ unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
int ret, i;
if (!nfs4_server_supports_acls(server))
return -EOPNOTSUPP;
+ if (npages > ARRAY_SIZE(pages))
+ return -ERANGE;
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
if (i < 0)
return i;
@@ -6223,11 +6219,58 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata)
dprintk("<-- %s\n", __func__);
}
+static size_t max_response_pages(struct nfs_server *server)
+{
+ u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
+ return nfs_page_array_len(0, max_resp_sz);
+}
+
+static void nfs4_free_pages(struct page **pages, size_t size)
+{
+ int i;
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < size; i++) {
+ if (!pages[i])
+ break;
+ __free_page(pages[i]);
+ }
+ kfree(pages);
+}
+
+static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags)
+{
+ struct page **pages;
+ int i;
+
+ pages = kcalloc(size, sizeof(struct page *), gfp_flags);
+ if (!pages) {
+ dprintk("%s: can't alloc array of %zu pages\n", __func__, size);
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ pages[i] = alloc_page(gfp_flags);
+ if (!pages[i]) {
+ dprintk("%s: failed to allocate page\n", __func__);
+ nfs4_free_pages(pages, size);
+ return NULL;
+ }
+ }
+
+ return pages;
+}
+
static void nfs4_layoutget_release(void *calldata)
{
struct nfs4_layoutget *lgp = calldata;
+ struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+ size_t max_pages = max_response_pages(server);
dprintk("--> %s\n", __func__);
+ nfs4_free_pages(lgp->args.layout.pages, max_pages);
put_nfs_open_context(lgp->args.ctx);
kfree(calldata);
dprintk("<-- %s\n", __func__);
@@ -6239,9 +6282,10 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = {
.rpc_release = nfs4_layoutget_release,
};
-int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
+void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
{
struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+ size_t max_pages = max_response_pages(server);
struct rpc_task *task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
@@ -6259,12 +6303,19 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
dprintk("--> %s\n", __func__);
+ lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
+ if (!lgp->args.layout.pages) {
+ nfs4_layoutget_release(lgp);
+ return;
+ }
+ lgp->args.layout.pglen = max_pages * PAGE_SIZE;
+
lgp->res.layoutp = &lgp->args.layout;
lgp->res.seq_res.sr_slot = NULL;
nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0);
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
- return PTR_ERR(task);
+ return;
status = nfs4_wait_for_completion_rpc_task(task);
if (status == 0)
status = task->tk_status;
@@ -6272,7 +6323,7 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
status = pnfs_layout_process(lgp);
rpc_put_task(task);
dprintk("<-- %s status=%d\n", __func__, status);
- return status;
+ return;
}
static void
@@ -6304,12 +6355,8 @@ static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
return;
}
spin_lock(&lo->plh_inode->i_lock);
- if (task->tk_status == 0) {
- if (lrp->res.lrs_present) {
- pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
- } else
- BUG_ON(!list_empty(&lo->plh_segs));
- }
+ if (task->tk_status == 0 && lrp->res.lrs_present)
+ pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
lo->plh_block_lgets--;
spin_unlock(&lo->plh_inode->i_lock);
dprintk("<-- %s\n", __func__);
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 12a31a9dbcdd..bd61221ad2c5 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -23,14 +23,6 @@ static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
-static struct file_system_type nfs4_fs_type = {
- .owner = THIS_MODULE,
- .name = "nfs4",
- .mount = nfs_fs_mount,
- .kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
@@ -344,14 +336,8 @@ static int __init init_nfs_v4(void)
if (err)
goto out1;
- err = register_filesystem(&nfs4_fs_type);
- if (err < 0)
- goto out2;
-
register_nfs_version(&nfs_v4);
return 0;
-out2:
- nfs4_unregister_sysctl();
out1:
nfs_idmap_quit();
out:
@@ -361,7 +347,6 @@ out:
static void __exit exit_nfs_v4(void)
{
unregister_nfs_version(&nfs_v4);
- unregister_filesystem(&nfs4_fs_type);
nfs4_unregister_sysctl();
nfs_idmap_quit();
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index ca13483edd60..8dba6bd48557 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5045,22 +5045,19 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
struct nfs_getaclres *res)
{
unsigned int savep;
- __be32 *bm_p;
uint32_t attrlen,
bitmap[3] = {0};
int status;
- size_t page_len = xdr->buf->page_len;
+ unsigned int pg_offset;
res->acl_len = 0;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto out;
- bm_p = xdr->p;
- res->acl_data_offset = be32_to_cpup(bm_p) + 2;
- res->acl_data_offset <<= 2;
- /* Check if the acl data starts beyond the allocated buffer */
- if (res->acl_data_offset > page_len)
- return -ERANGE;
+ xdr_enter_page(xdr, xdr->buf->page_len);
+
+ /* Calculate the offset of the page data */
+ pg_offset = xdr->buf->head[0].iov_len;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto out;
@@ -5074,23 +5071,16 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
* are stored with the acl data to handle the problem of
* variable length bitmaps.*/
- xdr->p = bm_p;
-
- /* We ignore &savep and don't do consistency checks on
- * the attr length. Let userspace figure it out.... */
- attrlen += res->acl_data_offset;
- if (attrlen > page_len) {
- if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
- /* getxattr interface called with a NULL buf */
- res->acl_len = attrlen;
- goto out;
- }
- dprintk("NFS: acl reply: attrlen %u > page_len %zu\n",
- attrlen, page_len);
- return -EINVAL;
- }
- xdr_read_pages(xdr, attrlen);
+ res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
res->acl_len = attrlen;
+
+ /* Check for receive buffer overflow */
+ if (res->acl_len > (xdr->nwords << 2) ||
+ res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
+ res->acl_flags |= NFS4_ACL_TRUNC;
+ dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
+ attrlen, xdr->nwords << 2);
+ }
} else
status = -EOPNOTSUPP;
@@ -6235,7 +6225,8 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_open(xdr, res);
if (status)
goto out;
- if (decode_getfh(xdr, &res->fh) != 0)
+ status = decode_getfh(xdr, &res->fh);
+ if (status)
goto out;
decode_getfattr(xdr, res->f_attr, res->server);
out:
diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c
index f50d3e8d6f22..ea6d111b03e9 100644
--- a/fs/nfs/objlayout/objio_osd.c
+++ b/fs/nfs/objlayout/objio_osd.c
@@ -570,17 +570,66 @@ static bool objio_pg_test(struct nfs_pageio_descriptor *pgio,
return false;
return pgio->pg_count + req->wb_bytes <=
- OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+ (unsigned long)pgio->pg_layout_private;
+}
+
+void objio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+ pnfs_generic_pg_init_read(pgio, req);
+ if (unlikely(pgio->pg_lseg == NULL))
+ return; /* Not pNFS */
+
+ pgio->pg_layout_private = (void *)
+ OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+}
+
+static bool aligned_on_raid_stripe(u64 offset, struct ore_layout *layout,
+ unsigned long *stripe_end)
+{
+ u32 stripe_off;
+ unsigned stripe_size;
+
+ if (layout->raid_algorithm == PNFS_OSD_RAID_0)
+ return true;
+
+ stripe_size = layout->stripe_unit *
+ (layout->group_width - layout->parity);
+
+ div_u64_rem(offset, stripe_size, &stripe_off);
+ if (!stripe_off)
+ return true;
+
+ *stripe_end = stripe_size - stripe_off;
+ return false;
+}
+
+void objio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+ unsigned long stripe_end = 0;
+
+ pnfs_generic_pg_init_write(pgio, req);
+ if (unlikely(pgio->pg_lseg == NULL))
+ return; /* Not pNFS */
+
+ if (req->wb_offset ||
+ !aligned_on_raid_stripe(req->wb_index * PAGE_SIZE,
+ &OBJIO_LSEG(pgio->pg_lseg)->layout,
+ &stripe_end)) {
+ pgio->pg_layout_private = (void *)stripe_end;
+ } else {
+ pgio->pg_layout_private = (void *)
+ OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+ }
}
static const struct nfs_pageio_ops objio_pg_read_ops = {
- .pg_init = pnfs_generic_pg_init_read,
+ .pg_init = objio_init_read,
.pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_readpages,
};
static const struct nfs_pageio_ops objio_pg_write_ops = {
- .pg_init = pnfs_generic_pg_init_write,
+ .pg_init = objio_init_write,
.pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_writepages,
};
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 1e7d8879dae6..311a79681e2b 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -49,6 +49,7 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
hdr->io_start = req_offset(hdr->req);
hdr->good_bytes = desc->pg_count;
hdr->dreq = desc->pg_dreq;
+ hdr->layout_private = desc->pg_layout_private;
hdr->release = release;
hdr->completion_ops = desc->pg_completion_ops;
if (hdr->completion_ops->init_hdr)
@@ -71,7 +72,7 @@ void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos)
static inline struct nfs_page *
nfs_page_alloc(void)
{
- struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_KERNEL);
+ struct nfs_page *p = kmem_cache_zalloc(nfs_page_cachep, GFP_NOIO);
if (p)
INIT_LIST_HEAD(&p->wb_list);
return p;
@@ -118,7 +119,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode,
* long write-back delay. This will be adjusted in
* update_nfs_request below if the region is not locked. */
req->wb_page = page;
- req->wb_index = page->index;
+ req->wb_index = page_file_index(page);
page_cache_get(page);
req->wb_offset = offset;
req->wb_pgbase = offset;
@@ -268,6 +269,7 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
desc->pg_error = 0;
desc->pg_lseg = NULL;
desc->pg_dreq = NULL;
+ desc->pg_layout_private = NULL;
}
EXPORT_SYMBOL_GPL(nfs_pageio_init);
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 76875bfcf19c..2e00feacd4be 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -583,9 +583,6 @@ send_layoutget(struct pnfs_layout_hdr *lo,
struct nfs_server *server = NFS_SERVER(ino);
struct nfs4_layoutget *lgp;
struct pnfs_layout_segment *lseg = NULL;
- struct page **pages = NULL;
- int i;
- u32 max_resp_sz, max_pages;
dprintk("--> %s\n", __func__);
@@ -594,20 +591,6 @@ send_layoutget(struct pnfs_layout_hdr *lo,
if (lgp == NULL)
return NULL;
- /* allocate pages for xdr post processing */
- max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
- max_pages = nfs_page_array_len(0, max_resp_sz);
-
- pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
- if (!pages)
- goto out_err_free;
-
- for (i = 0; i < max_pages; i++) {
- pages[i] = alloc_page(gfp_flags);
- if (!pages[i])
- goto out_err_free;
- }
-
lgp->args.minlength = PAGE_CACHE_SIZE;
if (lgp->args.minlength > range->length)
lgp->args.minlength = range->length;
@@ -616,39 +599,19 @@ send_layoutget(struct pnfs_layout_hdr *lo,
lgp->args.type = server->pnfs_curr_ld->id;
lgp->args.inode = ino;
lgp->args.ctx = get_nfs_open_context(ctx);
- lgp->args.layout.pages = pages;
- lgp->args.layout.pglen = max_pages * PAGE_SIZE;
lgp->lsegpp = &lseg;
lgp->gfp_flags = gfp_flags;
/* Synchronously retrieve layout information from server and
* store in lseg.
*/
- nfs4_proc_layoutget(lgp);
+ nfs4_proc_layoutget(lgp, gfp_flags);
if (!lseg) {
/* remember that LAYOUTGET failed and suspend trying */
set_bit(lo_fail_bit(range->iomode), &lo->plh_flags);
}
- /* free xdr pages */
- for (i = 0; i < max_pages; i++)
- __free_page(pages[i]);
- kfree(pages);
-
return lseg;
-
-out_err_free:
- /* free any allocated xdr pages, lgp as it's not used */
- if (pages) {
- for (i = 0; i < max_pages; i++) {
- if (!pages[i])
- break;
- __free_page(pages[i]);
- }
- kfree(pages);
- }
- kfree(lgp);
- return NULL;
}
/*
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 2c6c80503ba4..745aa1b39e7c 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -172,7 +172,7 @@ extern int nfs4_proc_getdevicelist(struct nfs_server *server,
struct pnfs_devicelist *devlist);
extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
struct pnfs_device *dev);
-extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp);
+extern void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags);
extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
/* pnfs.c */
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 6935e401ad76..b6bdb18e892c 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -527,11 +527,11 @@ static const struct rpc_call_ops nfs_read_common_ops = {
int nfs_readpage(struct file *file, struct page *page)
{
struct nfs_open_context *ctx;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
int error;
dprintk("NFS: nfs_readpage (%p %ld@%lu)\n",
- page, PAGE_CACHE_SIZE, page->index);
+ page, PAGE_CACHE_SIZE, page_file_index(page));
nfs_inc_stats(inode, NFSIOS_VFSREADPAGE);
nfs_add_stats(inode, NFSIOS_READPAGES, 1);
@@ -585,7 +585,7 @@ static int
readpage_async_filler(void *data, struct page *page)
{
struct nfs_readdesc *desc = (struct nfs_readdesc *)data;
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *new;
unsigned int len;
int error;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index ac6a3c55dce4..b8eda700584b 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -319,6 +319,34 @@ EXPORT_SYMBOL_GPL(nfs_sops);
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
static int nfs4_validate_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name);
+
+struct file_system_type nfs4_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "nfs4",
+ .mount = nfs_fs_mount,
+ .kill_sb = nfs_kill_super,
+ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+EXPORT_SYMBOL_GPL(nfs4_fs_type);
+
+static int __init register_nfs4_fs(void)
+{
+ return register_filesystem(&nfs4_fs_type);
+}
+
+static void unregister_nfs4_fs(void)
+{
+ unregister_filesystem(&nfs4_fs_type);
+}
+#else
+static int __init register_nfs4_fs(void)
+{
+ return 0;
+}
+
+static void unregister_nfs4_fs(void)
+{
+}
#endif
static struct shrinker acl_shrinker = {
@@ -337,12 +365,18 @@ int __init register_nfs_fs(void)
if (ret < 0)
goto error_0;
- ret = nfs_register_sysctl();
+ ret = register_nfs4_fs();
if (ret < 0)
goto error_1;
+
+ ret = nfs_register_sysctl();
+ if (ret < 0)
+ goto error_2;
register_shrinker(&acl_shrinker);
return 0;
+error_2:
+ unregister_nfs4_fs();
error_1:
unregister_filesystem(&nfs_fs_type);
error_0:
@@ -356,6 +390,7 @@ void __exit unregister_nfs_fs(void)
{
unregister_shrinker(&acl_shrinker);
nfs_unregister_sysctl();
+ unregister_nfs4_fs();
unregister_filesystem(&nfs_fs_type);
}
@@ -1832,6 +1867,7 @@ static int nfs23_validate_mount_data(void *options,
memcpy(sap, &data->addr, sizeof(data->addr));
args->nfs_server.addrlen = sizeof(data->addr);
+ args->nfs_server.port = ntohs(data->addr.sin_port);
if (!nfs_verify_server_address(sap))
goto out_no_address;
@@ -2529,6 +2565,7 @@ static int nfs4_validate_mount_data(void *options,
return -EFAULT;
if (!nfs_verify_server_address(sap))
goto out_no_address;
+ args->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
if (data->auth_flavourlen) {
if (data->auth_flavourlen > 1)
@@ -2645,4 +2682,6 @@ MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
module_param(send_implementation_id, ushort, 0644);
MODULE_PARM_DESC(send_implementation_id,
"Send implementation ID with NFSv4.1 exchange_id");
+MODULE_ALIAS("nfs4");
+
#endif /* CONFIG_NFS_V4 */
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index e4a2ad2059bd..e3b55372726c 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -52,7 +52,7 @@ static mempool_t *nfs_commit_mempool;
struct nfs_commit_data *nfs_commitdata_alloc(void)
{
- struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
+ struct nfs_commit_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOIO);
if (p) {
memset(p, 0, sizeof(*p));
@@ -70,7 +70,7 @@ EXPORT_SYMBOL_GPL(nfs_commit_free);
struct nfs_write_header *nfs_writehdr_alloc(void)
{
- struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
+ struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOIO);
if (p) {
struct nfs_pgio_header *hdr = &p->header;
@@ -142,25 +142,38 @@ static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error)
set_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
}
-static struct nfs_page *nfs_page_find_request_locked(struct page *page)
+static struct nfs_page *
+nfs_page_find_request_locked(struct nfs_inode *nfsi, struct page *page)
{
struct nfs_page *req = NULL;
- if (PagePrivate(page)) {
+ if (PagePrivate(page))
req = (struct nfs_page *)page_private(page);
- if (req != NULL)
- kref_get(&req->wb_kref);
+ else if (unlikely(PageSwapCache(page))) {
+ struct nfs_page *freq, *t;
+
+ /* Linearly search the commit list for the correct req */
+ list_for_each_entry_safe(freq, t, &nfsi->commit_info.list, wb_list) {
+ if (freq->wb_page == page) {
+ req = freq;
+ break;
+ }
+ }
}
+
+ if (req)
+ kref_get(&req->wb_kref);
+
return req;
}
static struct nfs_page *nfs_page_find_request(struct page *page)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *req = NULL;
spin_lock(&inode->i_lock);
- req = nfs_page_find_request_locked(page);
+ req = nfs_page_find_request_locked(NFS_I(inode), page);
spin_unlock(&inode->i_lock);
return req;
}
@@ -168,16 +181,16 @@ static struct nfs_page *nfs_page_find_request(struct page *page)
/* Adjust the file length if we're writing beyond the end */
static void nfs_grow_file(struct page *page, unsigned int offset, unsigned int count)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
loff_t end, i_size;
pgoff_t end_index;
spin_lock(&inode->i_lock);
i_size = i_size_read(inode);
end_index = (i_size - 1) >> PAGE_CACHE_SHIFT;
- if (i_size > 0 && page->index < end_index)
+ if (i_size > 0 && page_file_index(page) < end_index)
goto out;
- end = ((loff_t)page->index << PAGE_CACHE_SHIFT) + ((loff_t)offset+count);
+ end = page_file_offset(page) + ((loff_t)offset+count);
if (i_size >= end)
goto out;
i_size_write(inode, end);
@@ -190,7 +203,7 @@ out:
static void nfs_set_pageerror(struct page *page)
{
SetPageError(page);
- nfs_zap_mapping(page->mapping->host, page->mapping);
+ nfs_zap_mapping(page_file_mapping(page)->host, page_file_mapping(page));
}
/* We can set the PG_uptodate flag if we see that a write request
@@ -231,7 +244,7 @@ static int nfs_set_page_writeback(struct page *page)
int ret = test_set_page_writeback(page);
if (!ret) {
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_server *nfss = NFS_SERVER(inode);
if (atomic_long_inc_return(&nfss->writeback) >
@@ -245,7 +258,7 @@ static int nfs_set_page_writeback(struct page *page)
static void nfs_end_page_writeback(struct page *page)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_server *nfss = NFS_SERVER(inode);
end_page_writeback(page);
@@ -255,13 +268,13 @@ static void nfs_end_page_writeback(struct page *page)
static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *req;
int ret;
spin_lock(&inode->i_lock);
for (;;) {
- req = nfs_page_find_request_locked(page);
+ req = nfs_page_find_request_locked(NFS_I(inode), page);
if (req == NULL)
break;
if (nfs_lock_request(req))
@@ -316,13 +329,13 @@ out:
static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
int ret;
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE);
nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1);
- nfs_pageio_cond_complete(pgio, page->index);
+ nfs_pageio_cond_complete(pgio, page_file_index(page));
ret = nfs_page_async_flush(pgio, page, wbc->sync_mode == WB_SYNC_NONE);
if (ret == -EAGAIN) {
redirty_page_for_writepage(wbc, page);
@@ -339,7 +352,7 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
struct nfs_pageio_descriptor pgio;
int err;
- NFS_PROTO(page->mapping->host)->write_pageio_init(&pgio,
+ NFS_PROTO(page_file_mapping(page)->host)->write_pageio_init(&pgio,
page->mapping->host,
wb_priority(wbc),
&nfs_async_write_completion_ops);
@@ -416,9 +429,15 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
spin_lock(&inode->i_lock);
if (!nfsi->npages && NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
inode->i_version++;
- set_bit(PG_MAPPED, &req->wb_flags);
- SetPagePrivate(req->wb_page);
- set_page_private(req->wb_page, (unsigned long)req);
+ /*
+ * Swap-space should not get truncated. Hence no need to plug the race
+ * with invalidate/truncate.
+ */
+ if (likely(!PageSwapCache(req->wb_page))) {
+ set_bit(PG_MAPPED, &req->wb_flags);
+ SetPagePrivate(req->wb_page);
+ set_page_private(req->wb_page, (unsigned long)req);
+ }
nfsi->npages++;
kref_get(&req->wb_kref);
spin_unlock(&inode->i_lock);
@@ -435,9 +454,11 @@ static void nfs_inode_remove_request(struct nfs_page *req)
BUG_ON (!NFS_WBACK_BUSY(req));
spin_lock(&inode->i_lock);
- set_page_private(req->wb_page, 0);
- ClearPagePrivate(req->wb_page);
- clear_bit(PG_MAPPED, &req->wb_flags);
+ if (likely(!PageSwapCache(req->wb_page))) {
+ set_page_private(req->wb_page, 0);
+ ClearPagePrivate(req->wb_page);
+ clear_bit(PG_MAPPED, &req->wb_flags);
+ }
nfsi->npages--;
spin_unlock(&inode->i_lock);
nfs_release_request(req);
@@ -474,7 +495,7 @@ nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst,
spin_unlock(cinfo->lock);
if (!cinfo->dreq) {
inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
- inc_bdi_stat(req->wb_page->mapping->backing_dev_info,
+ inc_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
BDI_RECLAIMABLE);
__mark_inode_dirty(req->wb_context->dentry->d_inode,
I_DIRTY_DATASYNC);
@@ -541,7 +562,7 @@ static void
nfs_clear_page_commit(struct page *page)
{
dec_zone_page_state(page, NR_UNSTABLE_NFS);
- dec_bdi_stat(page->mapping->backing_dev_info, BDI_RECLAIMABLE);
+ dec_bdi_stat(page_file_mapping(page)->backing_dev_info, BDI_RECLAIMABLE);
}
static void
@@ -733,7 +754,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
spin_lock(&inode->i_lock);
for (;;) {
- req = nfs_page_find_request_locked(page);
+ req = nfs_page_find_request_locked(NFS_I(inode), page);
if (req == NULL)
goto out_unlock;
@@ -792,7 +813,7 @@ out_err:
static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
struct page *page, unsigned int offset, unsigned int bytes)
{
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
struct nfs_page *req;
req = nfs_try_to_update_request(inode, page, offset, bytes);
@@ -845,7 +866,7 @@ int nfs_flush_incompatible(struct file *file, struct page *page)
nfs_release_request(req);
if (!do_flush)
return 0;
- status = nfs_wb_page(page->mapping->host, page);
+ status = nfs_wb_page(page_file_mapping(page)->host, page);
} while (status == 0);
return status;
}
@@ -875,7 +896,7 @@ int nfs_updatepage(struct file *file, struct page *page,
unsigned int offset, unsigned int count)
{
struct nfs_open_context *ctx = nfs_file_open_context(file);
- struct inode *inode = page->mapping->host;
+ struct inode *inode = page_file_mapping(page)->host;
int status = 0;
nfs_inc_stats(inode, NFSIOS_VFSUPDATEPAGE);
@@ -883,7 +904,7 @@ int nfs_updatepage(struct file *file, struct page *page,
dprintk("NFS: nfs_updatepage(%s/%s %d@%lld)\n",
file->f_path.dentry->d_parent->d_name.name,
file->f_path.dentry->d_name.name, count,
- (long long)(page_offset(page) + offset));
+ (long long)(page_file_offset(page) + offset));
/* If we're not using byte range locks, and we know the page
* is up to date, it may be more efficient to extend the write
@@ -1474,7 +1495,7 @@ void nfs_retry_commit(struct list_head *page_list,
nfs_mark_request_commit(req, lseg, cinfo);
if (!cinfo->dreq) {
dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS);
- dec_bdi_stat(req->wb_page->mapping->backing_dev_info,
+ dec_bdi_stat(page_file_mapping(req->wb_page)->backing_dev_info,
BDI_RECLAIMABLE);
}
nfs_unlock_and_release_request(req);
@@ -1731,7 +1752,7 @@ int nfs_wb_page_cancel(struct inode *inode, struct page *page)
*/
int nfs_wb_page(struct inode *inode, struct page *page)
{
- loff_t range_start = page_offset(page);
+ loff_t range_start = page_file_offset(page);
loff_t range_end = range_start + (loff_t)(PAGE_CACHE_SIZE - 1);
struct writeback_control wbc = {
.sync_mode = WB_SYNC_ALL,
@@ -1793,19 +1814,19 @@ int __init nfs_init_writepagecache(void)
nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
nfs_wdata_cachep);
if (nfs_wdata_mempool == NULL)
- return -ENOMEM;
+ goto out_destroy_write_cache;
nfs_cdata_cachep = kmem_cache_create("nfs_commit_data",
sizeof(struct nfs_commit_data),
0, SLAB_HWCACHE_ALIGN,
NULL);
if (nfs_cdata_cachep == NULL)
- return -ENOMEM;
+ goto out_destroy_write_mempool;
nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
nfs_wdata_cachep);
if (nfs_commit_mempool == NULL)
- return -ENOMEM;
+ goto out_destroy_commit_cache;
/*
* NFS congestion size, scale with available memory.
@@ -1828,11 +1849,20 @@ int __init nfs_init_writepagecache(void)
nfs_congestion_kb = 256*1024;
return 0;
+
+out_destroy_commit_cache:
+ kmem_cache_destroy(nfs_cdata_cachep);
+out_destroy_write_mempool:
+ mempool_destroy(nfs_wdata_mempool);
+out_destroy_write_cache:
+ kmem_cache_destroy(nfs_wdata_cachep);
+ return -ENOMEM;
}
void nfs_destroy_writepagecache(void)
{
mempool_destroy(nfs_commit_mempool);
+ kmem_cache_destroy(nfs_cdata_cachep);
mempool_destroy(nfs_wdata_mempool);
kmem_cache_destroy(nfs_wdata_cachep);
}
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index cbaf4f8bb7b7..4c7bd35b1876 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -651,12 +651,12 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
if (clp->cl_minorversion == 0) {
if (!clp->cl_cred.cr_principal &&
- (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
+ (clp->cl_cred.cr_flavor >= RPC_AUTH_GSS_KRB5))
return -EINVAL;
args.client_name = clp->cl_cred.cr_principal;
args.prognumber = conn->cb_prog,
args.protocol = XPRT_TRANSPORT_TCP;
- args.authflavor = clp->cl_flavor;
+ args.authflavor = clp->cl_cred.cr_flavor;
clp->cl_cb_ident = conn->cb_ident;
} else {
if (!conn->cb_xprt)
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index 5ff0b7b9fc08..43295d45cc2b 100644
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -154,6 +154,10 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
if (status < 0)
return;
+ status = mnt_want_write_file(rec_file);
+ if (status)
+ return;
+
dir = rec_file->f_path.dentry;
/* lock the parent */
mutex_lock(&dir->d_inode->i_mutex);
@@ -173,11 +177,7 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
* as well be forgiving and just succeed silently.
*/
goto out_put;
- status = mnt_want_write_file(rec_file);
- if (status)
- goto out_put;
status = vfs_mkdir(dir->d_inode, dentry, S_IRWXU);
- mnt_drop_write_file(rec_file);
out_put:
dput(dentry);
out_unlock:
@@ -189,6 +189,7 @@ out_unlock:
" (err %d); please check that %s exists"
" and is writeable", status,
user_recovery_dirname);
+ mnt_drop_write_file(rec_file);
nfs4_reset_creds(original_cred);
}
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index cc793005a87c..032af381b3aa 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -635,6 +635,7 @@ fh_put(struct svc_fh *fhp)
fhp->fh_post_saved = 0;
#endif
}
+ fh_drop_write(fhp);
if (exp) {
exp_put(exp);
fhp->fh_export = NULL;
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index e15dc45fc5ec..aad6d457b9e8 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -196,6 +196,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct dentry *dchild;
int type, mode;
__be32 nfserr;
+ int hosterr;
dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size);
dprintk("nfsd: CREATE %s %.*s\n",
@@ -214,6 +215,12 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = nfserr_exist;
if (isdotent(argp->name, argp->len))
goto done;
+ hosterr = fh_want_write(dirfhp);
+ if (hosterr) {
+ nfserr = nfserrno(hosterr);
+ goto done;
+ }
+
fh_lock_nested(dirfhp, I_MUTEX_PARENT);
dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
if (IS_ERR(dchild)) {
@@ -330,7 +337,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
out_unlock:
/* We don't really need to unlock, as fh_put does it. */
fh_unlock(dirfhp);
-
+ fh_drop_write(dirfhp);
done:
fh_put(dirfhp);
return nfsd_return_dirop(nfserr, resp);
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index e6173147f982..22bd0a66c356 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -231,7 +231,6 @@ struct nfs4_client {
nfs4_verifier cl_verifier; /* generated by client */
time_t cl_time; /* time of last lease renewal */
struct sockaddr_storage cl_addr; /* client ipaddress */
- u32 cl_flavor; /* setclientid pseudoflavor */
struct svc_cred cl_cred; /* setclientid principal */
clientid_t cl_clientid; /* generated by server */
nfs4_verifier cl_confirm; /* generated by server */
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 702f64e820c3..a9269f142cc4 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1284,6 +1284,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
* If it has, the parent directory should already be locked.
*/
if (!resfhp->fh_dentry) {
+ host_err = fh_want_write(fhp);
+ if (host_err)
+ goto out_nfserr;
+
/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
fh_lock_nested(fhp, I_MUTEX_PARENT);
dchild = lookup_one_len(fname, dentry, flen);
@@ -1327,14 +1331,11 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
goto out;
}
- host_err = fh_want_write(fhp);
- if (host_err)
- goto out_nfserr;
-
/*
* Get the dir op function pointer.
*/
err = 0;
+ host_err = 0;
switch (type) {
case S_IFREG:
host_err = vfs_create(dirp, dchild, iap->ia_mode, true);
@@ -1351,10 +1352,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
break;
}
- if (host_err < 0) {
- fh_drop_write(fhp);
+ if (host_err < 0)
goto out_nfserr;
- }
err = nfsd_create_setattr(rqstp, resfhp, iap);
@@ -1366,7 +1365,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err2 = nfserrno(commit_metadata(fhp));
if (err2)
err = err2;
- fh_drop_write(fhp);
/*
* Update the file handle to get the new inode info.
*/
@@ -1425,6 +1423,11 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfserr_notdir;
if (!dirp->i_op->lookup)
goto out;
+
+ host_err = fh_want_write(fhp);
+ if (host_err)
+ goto out_nfserr;
+
fh_lock_nested(fhp, I_MUTEX_PARENT);
/*
@@ -1457,9 +1460,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
v_atime = verifier[1]&0x7fffffff;
}
- host_err = fh_want_write(fhp);
- if (host_err)
- goto out_nfserr;
if (dchild->d_inode) {
err = 0;
@@ -1530,7 +1530,6 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (!err)
err = nfserrno(commit_metadata(fhp));
- fh_drop_write(fhp);
/*
* Update the filehandle to get the new inode info.
*/
@@ -1541,6 +1540,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
fh_unlock(fhp);
if (dchild && !IS_ERR(dchild))
dput(dchild);
+ fh_drop_write(fhp);
return err;
out_nfserr:
@@ -1621,6 +1621,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
if (err)
goto out;
+
+ host_err = fh_want_write(fhp);
+ if (host_err)
+ goto out_nfserr;
+
fh_lock(fhp);
dentry = fhp->fh_dentry;
dnew = lookup_one_len(fname, dentry, flen);
@@ -1628,10 +1633,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (IS_ERR(dnew))
goto out_nfserr;
- host_err = fh_want_write(fhp);
- if (host_err)
- goto out_nfserr;
-
if (unlikely(path[plen] != 0)) {
char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
if (path_alloced == NULL)
@@ -1691,6 +1692,12 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
if (isdotent(name, len))
goto out;
+ host_err = fh_want_write(tfhp);
+ if (host_err) {
+ err = nfserrno(host_err);
+ goto out;
+ }
+
fh_lock_nested(ffhp, I_MUTEX_PARENT);
ddir = ffhp->fh_dentry;
dirp = ddir->d_inode;
@@ -1702,18 +1709,13 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
dold = tfhp->fh_dentry;
- host_err = fh_want_write(tfhp);
- if (host_err) {
- err = nfserrno(host_err);
- goto out_dput;
- }
err = nfserr_noent;
if (!dold->d_inode)
- goto out_drop_write;
+ goto out_dput;
host_err = nfsd_break_lease(dold->d_inode);
if (host_err) {
err = nfserrno(host_err);
- goto out_drop_write;
+ goto out_dput;
}
host_err = vfs_link(dold, dirp, dnew);
if (!host_err) {
@@ -1726,12 +1728,11 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
else
err = nfserrno(host_err);
}
-out_drop_write:
- fh_drop_write(tfhp);
out_dput:
dput(dnew);
out_unlock:
fh_unlock(ffhp);
+ fh_drop_write(tfhp);
out:
return err;
@@ -1774,6 +1775,12 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
goto out;
+ host_err = fh_want_write(ffhp);
+ if (host_err) {
+ err = nfserrno(host_err);
+ goto out;
+ }
+
/* cannot use fh_lock as we need deadlock protective ordering
* so do it by hand */
trap = lock_rename(tdentry, fdentry);
@@ -1804,17 +1811,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
host_err = -EXDEV;
if (ffhp->fh_export->ex_path.mnt != tfhp->fh_export->ex_path.mnt)
goto out_dput_new;
- host_err = fh_want_write(ffhp);
- if (host_err)
- goto out_dput_new;
host_err = nfsd_break_lease(odentry->d_inode);
if (host_err)
- goto out_drop_write;
+ goto out_dput_new;
if (ndentry->d_inode) {
host_err = nfsd_break_lease(ndentry->d_inode);
if (host_err)
- goto out_drop_write;
+ goto out_dput_new;
}
host_err = vfs_rename(fdir, odentry, tdir, ndentry);
if (!host_err) {
@@ -1822,8 +1826,6 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
if (!host_err)
host_err = commit_metadata(ffhp);
}
-out_drop_write:
- fh_drop_write(ffhp);
out_dput_new:
dput(ndentry);
out_dput_old:
@@ -1839,6 +1841,7 @@ out_drop_write:
fill_post_wcc(tfhp);
unlock_rename(tdentry, fdentry);
ffhp->fh_locked = tfhp->fh_locked = 0;
+ fh_drop_write(ffhp);
out:
return err;
@@ -1864,6 +1867,10 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (err)
goto out;
+ host_err = fh_want_write(fhp);
+ if (host_err)
+ goto out_nfserr;
+
fh_lock_nested(fhp, I_MUTEX_PARENT);
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
@@ -1882,21 +1889,15 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
if (!type)
type = rdentry->d_inode->i_mode & S_IFMT;
- host_err = fh_want_write(fhp);
- if (host_err)
- goto out_put;
-
host_err = nfsd_break_lease(rdentry->d_inode);
if (host_err)
- goto out_drop_write;
+ goto out_put;
if (type != S_IFDIR)
host_err = vfs_unlink(dirp, rdentry);
else
host_err = vfs_rmdir(dirp, rdentry);
if (!host_err)
host_err = commit_metadata(fhp);
-out_drop_write:
- fh_drop_write(fhp);
out_put:
dput(rdentry);
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index ec0611b2b738..359594c393d2 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -110,12 +110,19 @@ int nfsd_set_posix_acl(struct svc_fh *, int, struct posix_acl *);
static inline int fh_want_write(struct svc_fh *fh)
{
- return mnt_want_write(fh->fh_export->ex_path.mnt);
+ int ret = mnt_want_write(fh->fh_export->ex_path.mnt);
+
+ if (!ret)
+ fh->fh_want_write = 1;
+ return ret;
}
static inline void fh_drop_write(struct svc_fh *fh)
{
- mnt_drop_write(fh->fh_export->ex_path.mnt);
+ if (fh->fh_want_write) {
+ fh->fh_want_write = 0;
+ mnt_drop_write(fh->fh_export->ex_path.mnt);
+ }
}
#endif /* LINUX_NFSD_VFS_H */
diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c
index 62cebc8e1a1f..a4d56ac02e6c 100644
--- a/fs/nilfs2/file.c
+++ b/fs/nilfs2/file.c
@@ -69,16 +69,18 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
struct page *page = vmf->page;
struct inode *inode = vma->vm_file->f_dentry->d_inode;
struct nilfs_transaction_info ti;
- int ret;
+ int ret = 0;
if (unlikely(nilfs_near_disk_full(inode->i_sb->s_fs_info)))
return VM_FAULT_SIGBUS; /* -ENOSPC */
+ sb_start_pagefault(inode->i_sb);
lock_page(page);
if (page->mapping != inode->i_mapping ||
page_offset(page) >= i_size_read(inode) || !PageUptodate(page)) {
unlock_page(page);
- return VM_FAULT_NOPAGE; /* make the VM retry the fault */
+ ret = -EFAULT; /* make the VM retry the fault */
+ goto out;
}
/*
@@ -112,19 +114,21 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
ret = nilfs_transaction_begin(inode->i_sb, &ti, 1);
/* never returns -ENOMEM, but may return -ENOSPC */
if (unlikely(ret))
- return VM_FAULT_SIGBUS;
+ goto out;
- ret = block_page_mkwrite(vma, vmf, nilfs_get_block);
- if (ret != VM_FAULT_LOCKED) {
+ ret = __block_page_mkwrite(vma, vmf, nilfs_get_block);
+ if (ret) {
nilfs_transaction_abort(inode->i_sb);
- return ret;
+ goto out;
}
nilfs_set_file_dirty(inode, 1 << (PAGE_SHIFT - inode->i_blkbits));
nilfs_transaction_commit(inode->i_sb);
mapped:
wait_on_page_writeback(page);
- return VM_FAULT_LOCKED;
+ out:
+ sb_end_pagefault(inode->i_sb);
+ return block_page_mkwrite_return(ret);
}
static const struct vm_operations_struct nilfs_file_vm_ops = {
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 0b6387c67e6c..fdb180769485 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -660,8 +660,6 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
goto out_free;
}
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
ret = nilfs_ioctl_move_blocks(inode->i_sb, &argv[0], kbufs[0]);
if (ret < 0)
printk(KERN_ERR "NILFS: GC failed during preparation: "
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 88e11fb346b6..a5752a589932 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -189,7 +189,7 @@ int nilfs_transaction_begin(struct super_block *sb,
if (ret > 0)
return 0;
- vfs_check_frozen(sb, SB_FREEZE_WRITE);
+ sb_start_intwrite(sb);
nilfs = sb->s_fs_info;
down_read(&nilfs->ns_segctor_sem);
@@ -205,6 +205,7 @@ int nilfs_transaction_begin(struct super_block *sb,
current->journal_info = ti->ti_save;
if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
kmem_cache_free(nilfs_transaction_cachep, ti);
+ sb_end_intwrite(sb);
return ret;
}
@@ -246,6 +247,7 @@ int nilfs_transaction_commit(struct super_block *sb)
err = nilfs_construct_segment(sb);
if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
kmem_cache_free(nilfs_transaction_cachep, ti);
+ sb_end_intwrite(sb);
return err;
}
@@ -264,6 +266,7 @@ void nilfs_transaction_abort(struct super_block *sb)
current->journal_info = ti->ti_save;
if (ti->ti_flags & NILFS_TI_DYNAMIC_ALLOC)
kmem_cache_free(nilfs_transaction_cachep, ti);
+ sb_end_intwrite(sb);
}
void nilfs_relax_pressure_in_lock(struct super_block *sb)
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 6522cac6057c..6a10812711c1 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -676,17 +676,13 @@ static const struct super_operations nilfs_sops = {
.alloc_inode = nilfs_alloc_inode,
.destroy_inode = nilfs_destroy_inode,
.dirty_inode = nilfs_dirty_inode,
- /* .write_inode = nilfs_write_inode, */
- /* .drop_inode = nilfs_drop_inode, */
.evict_inode = nilfs_evict_inode,
.put_super = nilfs_put_super,
- /* .write_super = nilfs_write_super, */
.sync_fs = nilfs_sync_fs,
.freeze_fs = nilfs_freeze,
.unfreeze_fs = nilfs_unfreeze,
.statfs = nilfs_statfs,
.remount_fs = nilfs_remount,
- /* .umount_begin */
.show_options = nilfs_show_options
};
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 6eee4177807b..be1267a34cea 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -107,8 +107,6 @@ struct the_nilfs {
* used for
* - loading the latest checkpoint exclusively.
* - allocating a new full segment.
- * - protecting s_dirt in the super_block struct
- * (see nilfs_write_super) and the following fields.
*/
struct buffer_head *ns_sbh[2];
struct nilfs_super_block *ns_sbp[2];
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 7389d2d5e51d..1ecf46448f85 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -2084,7 +2084,6 @@ static ssize_t ntfs_file_aio_write_nolock(struct kiocb *iocb,
if (err)
return err;
pos = *ppos;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
/* We can write back this queue in page reclaim. */
current->backing_dev_info = mapping->backing_dev_info;
written = 0;
@@ -2119,6 +2118,7 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
BUG_ON(iocb->ki_pos != pos);
+ sb_start_write(inode->i_sb);
mutex_lock(&inode->i_mutex);
ret = ntfs_file_aio_write_nolock(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
@@ -2127,6 +2127,7 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (err < 0)
ret = err;
}
+ sb_end_write(inode->i_sb);
return ret;
}
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 7602783d7f41..46a1f6d75104 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1971,6 +1971,7 @@ int ocfs2_change_file_space(struct file *file, unsigned int cmd,
{
struct inode *inode = file->f_path.dentry->d_inode;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ int ret;
if ((cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) &&
!ocfs2_writes_unwritten_extents(osb))
@@ -1985,7 +1986,12 @@ int ocfs2_change_file_space(struct file *file, unsigned int cmd,
if (!(file->f_mode & FMODE_WRITE))
return -EBADF;
- return __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
+ ret = mnt_want_write_file(file);
+ if (ret)
+ return ret;
+ ret = __ocfs2_change_file_space(file, inode, file->f_pos, cmd, sr, 0);
+ mnt_drop_write_file(file);
+ return ret;
}
static long ocfs2_fallocate(struct file *file, int mode, loff_t offset,
@@ -2261,7 +2267,7 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
if (iocb->ki_left == 0)
return 0;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
+ sb_start_write(inode->i_sb);
appending = file->f_flags & O_APPEND ? 1 : 0;
direct_io = file->f_flags & O_DIRECT ? 1 : 0;
@@ -2436,6 +2442,7 @@ out_sems:
ocfs2_iocb_clear_sem_locked(iocb);
mutex_unlock(&inode->i_mutex);
+ sb_end_write(inode->i_sb);
if (written)
ret = written;
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index d96f7f81d8dd..f20edcbfe700 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -928,7 +928,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (get_user(new_clusters, (int __user *)arg))
return -EFAULT;
- return ocfs2_group_extend(inode, new_clusters);
+ status = mnt_want_write_file(filp);
+ if (status)
+ return status;
+ status = ocfs2_group_extend(inode, new_clusters);
+ mnt_drop_write_file(filp);
+ return status;
case OCFS2_IOC_GROUP_ADD:
case OCFS2_IOC_GROUP_ADD64:
if (!capable(CAP_SYS_RESOURCE))
@@ -937,7 +942,12 @@ long ocfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (copy_from_user(&input, (int __user *) arg, sizeof(input)))
return -EFAULT;
- return ocfs2_group_add(inode, &input);
+ status = mnt_want_write_file(filp);
+ if (status)
+ return status;
+ status = ocfs2_group_add(inode, &input);
+ mnt_drop_write_file(filp);
+ return status;
case OCFS2_IOC_REFLINK:
if (copy_from_user(&args, argp, sizeof(args)))
return -EFAULT;
diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c
index 0a42ae96dca7..2dd36af79e26 100644
--- a/fs/ocfs2/journal.c
+++ b/fs/ocfs2/journal.c
@@ -355,11 +355,14 @@ handle_t *ocfs2_start_trans(struct ocfs2_super *osb, int max_buffs)
if (journal_current_handle())
return jbd2_journal_start(journal, max_buffs);
+ sb_start_intwrite(osb->sb);
+
down_read(&osb->journal->j_trans_barrier);
handle = jbd2_journal_start(journal, max_buffs);
if (IS_ERR(handle)) {
up_read(&osb->journal->j_trans_barrier);
+ sb_end_intwrite(osb->sb);
mlog_errno(PTR_ERR(handle));
@@ -388,8 +391,10 @@ int ocfs2_commit_trans(struct ocfs2_super *osb,
if (ret < 0)
mlog_errno(ret);
- if (!nested)
+ if (!nested) {
up_read(&journal->j_trans_barrier);
+ sb_end_intwrite(osb->sb);
+ }
return ret;
}
diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c
index 9cd41083e991..d150372fd81d 100644
--- a/fs/ocfs2/mmap.c
+++ b/fs/ocfs2/mmap.c
@@ -136,6 +136,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
sigset_t oldset;
int ret;
+ sb_start_pagefault(inode->i_sb);
ocfs2_block_signals(&oldset);
/*
@@ -165,6 +166,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
out:
ocfs2_unblock_signals(&oldset);
+ sb_end_pagefault(inode->i_sb);
return ret;
}
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index 9f32d7cbb7a3..30a055049e16 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -4466,20 +4466,11 @@ int ocfs2_reflink_ioctl(struct inode *inode,
goto out_dput;
}
- error = mnt_want_write(new_path.mnt);
- if (error) {
- mlog_errno(error);
- goto out_dput;
- }
-
error = ocfs2_vfs_reflink(old_path.dentry,
new_path.dentry->d_inode,
new_dentry, preserve);
- mnt_drop_write(new_path.mnt);
out_dput:
- dput(new_dentry);
- mutex_unlock(&new_path.dentry->d_inode->i_mutex);
- path_put(&new_path);
+ done_path_create(&new_path, new_dentry);
out:
path_put(&old_path);
diff --git a/fs/open.c b/fs/open.c
index 1e914b397e12..e1f2cdb91a4d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -164,11 +164,13 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
if (IS_APPEND(inode))
goto out_putf;
+ sb_start_write(inode->i_sb);
error = locks_verify_truncate(inode, file, length);
if (!error)
error = security_path_truncate(&file->f_path);
if (!error)
error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+ sb_end_write(inode->i_sb);
out_putf:
fput(file);
out:
@@ -266,7 +268,10 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
if (!file->f_op->fallocate)
return -EOPNOTSUPP;
- return file->f_op->fallocate(file, mode, offset, len);
+ sb_start_write(inode->i_sb);
+ ret = file->f_op->fallocate(file, mode, offset, len);
+ sb_end_write(inode->i_sb);
+ return ret;
}
SYSCALL_DEFINE(fallocate)(int fd, int mode, loff_t offset, loff_t len)
@@ -620,7 +625,7 @@ static inline int __get_file_write_access(struct inode *inode,
/*
* Balanced in __fput()
*/
- error = mnt_want_write(mnt);
+ error = __mnt_want_write(mnt);
if (error)
put_write_access(inode);
}
@@ -654,6 +659,7 @@ static int do_dentry_open(struct file *f,
if (unlikely(f->f_flags & O_PATH))
f->f_mode = FMODE_PATH;
+ path_get(&f->f_path);
inode = f->f_path.dentry->d_inode;
if (f->f_mode & FMODE_WRITE) {
error = __get_file_write_access(inode, f->f_path.mnt);
@@ -711,7 +717,7 @@ cleanup_all:
* here, so just reset the state.
*/
file_reset_write(f);
- mnt_drop_write(f->f_path.mnt);
+ __mnt_drop_write(f->f_path.mnt);
}
}
cleanup_file:
@@ -739,9 +745,7 @@ int finish_open(struct file *file, struct dentry *dentry,
int error;
BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
- mntget(file->f_path.mnt);
- file->f_path.dentry = dget(dentry);
-
+ file->f_path.dentry = dentry;
error = do_dentry_open(file, open, current_cred());
if (!error)
*opened |= FILE_OPENED;
@@ -784,7 +788,6 @@ struct file *dentry_open(const struct path *path, int flags,
f->f_flags = flags;
f->f_path = *path;
- path_get(&f->f_path);
error = do_dentry_open(f, NULL, cred);
if (!error) {
error = open_check_o_direct(f);
@@ -849,9 +852,10 @@ static inline int build_open_flags(int flags, umode_t mode, struct open_flags *o
int lookup_flags = 0;
int acc_mode;
- if (!(flags & O_CREAT))
- mode = 0;
- op->mode = mode;
+ if (flags & O_CREAT)
+ op->mode = (mode & S_IALLUGO) | S_IFREG;
+ else
+ op->mode = 0;
/* Must never be set by userspace */
flags &= ~FMODE_NONOTIFY;
diff --git a/fs/pipe.c b/fs/pipe.c
index 95cbd6b227e6..8d85d7068c1e 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1016,18 +1016,16 @@ fail_inode:
return NULL;
}
-struct file *create_write_pipe(int flags)
+int create_pipe_files(struct file **res, int flags)
{
int err;
- struct inode *inode;
+ struct inode *inode = get_pipe_inode();
struct file *f;
struct path path;
- struct qstr name = { .name = "" };
+ static struct qstr name = { .name = "" };
- err = -ENFILE;
- inode = get_pipe_inode();
if (!inode)
- goto err;
+ return -ENFILE;
err = -ENOMEM;
path.dentry = d_alloc_pseudo(pipe_mnt->mnt_sb, &name);
@@ -1041,62 +1039,43 @@ struct file *create_write_pipe(int flags)
f = alloc_file(&path, FMODE_WRITE, &write_pipefifo_fops);
if (!f)
goto err_dentry;
- f->f_mapping = inode->i_mapping;
f->f_flags = O_WRONLY | (flags & (O_NONBLOCK | O_DIRECT));
- f->f_version = 0;
- return f;
+ res[0] = alloc_file(&path, FMODE_READ, &read_pipefifo_fops);
+ if (!res[0])
+ goto err_file;
+
+ path_get(&path);
+ res[0]->f_flags = O_RDONLY | (flags & O_NONBLOCK);
+ res[1] = f;
+ return 0;
- err_dentry:
+err_file:
+ put_filp(f);
+err_dentry:
free_pipe_info(inode);
path_put(&path);
- return ERR_PTR(err);
+ return err;
- err_inode:
+err_inode:
free_pipe_info(inode);
iput(inode);
- err:
- return ERR_PTR(err);
-}
-
-void free_write_pipe(struct file *f)
-{
- free_pipe_info(f->f_dentry->d_inode);
- path_put(&f->f_path);
- put_filp(f);
-}
-
-struct file *create_read_pipe(struct file *wrf, int flags)
-{
- /* Grab pipe from the writer */
- struct file *f = alloc_file(&wrf->f_path, FMODE_READ,
- &read_pipefifo_fops);
- if (!f)
- return ERR_PTR(-ENFILE);
-
- path_get(&wrf->f_path);
- f->f_flags = O_RDONLY | (flags & O_NONBLOCK);
-
- return f;
+ return err;
}
int do_pipe_flags(int *fd, int flags)
{
- struct file *fw, *fr;
+ struct file *files[2];
int error;
int fdw, fdr;
if (flags & ~(O_CLOEXEC | O_NONBLOCK | O_DIRECT))
return -EINVAL;
- fw = create_write_pipe(flags);
- if (IS_ERR(fw))
- return PTR_ERR(fw);
- fr = create_read_pipe(fw, flags);
- error = PTR_ERR(fr);
- if (IS_ERR(fr))
- goto err_write_pipe;
+ error = create_pipe_files(files, flags);
+ if (error)
+ return error;
error = get_unused_fd_flags(flags);
if (error < 0)
@@ -1109,8 +1088,8 @@ int do_pipe_flags(int *fd, int flags)
fdw = error;
audit_fd_pair(fdr, fdw);
- fd_install(fdr, fr);
- fd_install(fdw, fw);
+ fd_install(fdr, files[0]);
+ fd_install(fdw, files[1]);
fd[0] = fdr;
fd[1] = fdw;
@@ -1119,10 +1098,8 @@ int do_pipe_flags(int *fd, int flags)
err_fdr:
put_unused_fd(fdr);
err_read_pipe:
- path_put(&fr->f_path);
- put_filp(fr);
- err_write_pipe:
- free_write_pipe(fw);
+ fput(files[0]);
+ fput(files[1]);
return error;
}
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index dfafeb2b05a0..eb7cc91b7258 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -462,9 +462,6 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
err = ERR_PTR(-ENOMEM);
inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
- if (h)
- sysctl_head_finish(h);
-
if (!inode)
goto out;
@@ -473,6 +470,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
d_add(dentry, inode);
out:
+ if (h)
+ sysctl_head_finish(h);
sysctl_head_finish(head);
return err;
}
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 36a29b753c79..c495a3055e2a 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1589,10 +1589,10 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
goto out;
}
- down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
warn[cnt].w_type = QUOTA_NL_NOWARN;
+ down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
spin_lock(&dq_data_lock);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (!dquots[cnt])
diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c
index 4c0c7d163d15..a98b7740a0fc 100644
--- a/fs/reiserfs/bitmap.c
+++ b/fs/reiserfs/bitmap.c
@@ -1334,9 +1334,7 @@ struct buffer_head *reiserfs_read_bitmap_block(struct super_block *sb,
else if (bitmap == 0)
block = (REISERFS_DISK_OFFSET_IN_BYTES >> sb->s_blocksize_bits) + 1;
- reiserfs_write_unlock(sb);
bh = sb_bread(sb, block);
- reiserfs_write_lock(sb);
if (bh == NULL)
reiserfs_warning(sb, "sh-2029: %s: bitmap block (#%u) "
"reading failed", __func__, block);
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index a6d4268fb6c1..855da58db145 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -76,10 +76,10 @@ void reiserfs_evict_inode(struct inode *inode)
;
}
out:
+ reiserfs_write_unlock_once(inode->i_sb, depth);
clear_inode(inode); /* note this must go after the journal_end to prevent deadlock */
dquot_drop(inode);
inode->i_blocks = 0;
- reiserfs_write_unlock_once(inode->i_sb, depth);
return;
no_delete:
diff --git a/fs/splice.c b/fs/splice.c
index 7bf08fa22ec9..41514dd89462 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -996,6 +996,8 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
};
ssize_t ret;
+ sb_start_write(inode->i_sb);
+
pipe_lock(pipe);
splice_from_pipe_begin(&sd);
@@ -1034,6 +1036,7 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
*ppos += ret;
balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
}
+ sb_end_write(inode->i_sb);
return ret;
}
diff --git a/fs/stat.c b/fs/stat.c
index b6ff11825fc8..40780229a032 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -58,7 +58,7 @@ EXPORT_SYMBOL(vfs_getattr);
int vfs_fstat(unsigned int fd, struct kstat *stat)
{
int fput_needed;
- struct file *f = fget_light(fd, &fput_needed);
+ struct file *f = fget_raw_light(fd, &fput_needed);
int error = -EBADF;
if (f) {
diff --git a/fs/super.c b/fs/super.c
index 4c5d82f56ec4..0902cfa6a12e 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -33,12 +33,19 @@
#include <linux/rculist_bl.h>
#include <linux/cleancache.h>
#include <linux/fsnotify.h>
+#include <linux/lockdep.h>
#include "internal.h"
LIST_HEAD(super_blocks);
DEFINE_SPINLOCK(sb_lock);
+static char *sb_writers_name[SB_FREEZE_LEVELS] = {
+ "sb_writers",
+ "sb_pagefaults",
+ "sb_internal",
+};
+
/*
* One thing we have to be careful of with a per-sb shrinker is that we don't
* drop the last active reference to the superblock from within the shrinker.
@@ -62,7 +69,7 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
return -1;
if (!grab_super_passive(sb))
- return !sc->nr_to_scan ? 0 : -1;
+ return -1;
if (sb->s_op && sb->s_op->nr_cached_objects)
fs_objects = sb->s_op->nr_cached_objects(sb);
@@ -102,6 +109,35 @@ static int prune_super(struct shrinker *shrink, struct shrink_control *sc)
return total_objects;
}
+static int init_sb_writers(struct super_block *s, struct file_system_type *type)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < SB_FREEZE_LEVELS; i++) {
+ err = percpu_counter_init(&s->s_writers.counter[i], 0);
+ if (err < 0)
+ goto err_out;
+ lockdep_init_map(&s->s_writers.lock_map[i], sb_writers_name[i],
+ &type->s_writers_key[i], 0);
+ }
+ init_waitqueue_head(&s->s_writers.wait);
+ init_waitqueue_head(&s->s_writers.wait_unfrozen);
+ return 0;
+err_out:
+ while (--i >= 0)
+ percpu_counter_destroy(&s->s_writers.counter[i]);
+ return err;
+}
+
+static void destroy_sb_writers(struct super_block *s)
+{
+ int i;
+
+ for (i = 0; i < SB_FREEZE_LEVELS; i++)
+ percpu_counter_destroy(&s->s_writers.counter[i]);
+}
+
/**
* alloc_super - create new superblock
* @type: filesystem type superblock should belong to
@@ -117,18 +153,19 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
if (s) {
if (security_sb_alloc(s)) {
+ /*
+ * We cannot call security_sb_free() without
+ * security_sb_alloc() succeeding. So bail out manually
+ */
kfree(s);
s = NULL;
goto out;
}
#ifdef CONFIG_SMP
s->s_files = alloc_percpu(struct list_head);
- if (!s->s_files) {
- security_sb_free(s);
- kfree(s);
- s = NULL;
- goto out;
- } else {
+ if (!s->s_files)
+ goto err_out;
+ else {
int i;
for_each_possible_cpu(i)
@@ -137,6 +174,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
#else
INIT_LIST_HEAD(&s->s_files);
#endif
+ if (init_sb_writers(s, type))
+ goto err_out;
s->s_flags = flags;
s->s_bdi = &default_backing_dev_info;
INIT_HLIST_NODE(&s->s_instances);
@@ -178,7 +217,6 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
mutex_init(&s->s_dquot.dqio_mutex);
mutex_init(&s->s_dquot.dqonoff_mutex);
init_rwsem(&s->s_dquot.dqptr_sem);
- init_waitqueue_head(&s->s_wait_unfrozen);
s->s_maxbytes = MAX_NON_LFS;
s->s_op = &default_op;
s->s_time_gran = 1000000000;
@@ -190,6 +228,16 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)
}
out:
return s;
+err_out:
+ security_sb_free(s);
+#ifdef CONFIG_SMP
+ if (s->s_files)
+ free_percpu(s->s_files);
+#endif
+ destroy_sb_writers(s);
+ kfree(s);
+ s = NULL;
+ goto out;
}
/**
@@ -203,6 +251,7 @@ static inline void destroy_super(struct super_block *s)
#ifdef CONFIG_SMP
free_percpu(s->s_files);
#endif
+ destroy_sb_writers(s);
security_sb_free(s);
WARN_ON(!list_empty(&s->s_mounts));
kfree(s->s_subtype);
@@ -488,46 +537,6 @@ void drop_super(struct super_block *sb)
EXPORT_SYMBOL(drop_super);
/**
- * sync_supers - helper for periodic superblock writeback
- *
- * Call the write_super method if present on all dirty superblocks in
- * the system. This is for the periodic writeback used by most older
- * filesystems. For data integrity superblock writeback use
- * sync_filesystems() instead.
- *
- * Note: check the dirty flag before waiting, so we don't
- * hold up the sync while mounting a device. (The newly
- * mounted device won't need syncing.)
- */
-void sync_supers(void)
-{
- struct super_block *sb, *p = NULL;
-
- spin_lock(&sb_lock);
- list_for_each_entry(sb, &super_blocks, s_list) {
- if (hlist_unhashed(&sb->s_instances))
- continue;
- if (sb->s_op->write_super && sb->s_dirt) {
- sb->s_count++;
- spin_unlock(&sb_lock);
-
- down_read(&sb->s_umount);
- if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN))
- sb->s_op->write_super(sb);
- up_read(&sb->s_umount);
-
- spin_lock(&sb_lock);
- if (p)
- __put_super(p);
- p = sb;
- }
- }
- if (p)
- __put_super(p);
- spin_unlock(&sb_lock);
-}
-
-/**
* iterate_supers - call function for all active superblocks
* @f: function to call
* @arg: argument to pass to it
@@ -651,10 +660,11 @@ struct super_block *get_super_thawed(struct block_device *bdev)
{
while (1) {
struct super_block *s = get_super(bdev);
- if (!s || s->s_frozen == SB_UNFROZEN)
+ if (!s || s->s_writers.frozen == SB_UNFROZEN)
return s;
up_read(&s->s_umount);
- vfs_check_frozen(s, SB_FREEZE_WRITE);
+ wait_event(s->s_writers.wait_unfrozen,
+ s->s_writers.frozen == SB_UNFROZEN);
put_super(s);
}
}
@@ -732,7 +742,7 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
int retval;
int remount_ro;
- if (sb->s_frozen != SB_UNFROZEN)
+ if (sb->s_writers.frozen != SB_UNFROZEN)
return -EBUSY;
#ifdef CONFIG_BLOCK
@@ -1163,6 +1173,120 @@ out:
return ERR_PTR(error);
}
+/*
+ * This is an internal function, please use sb_end_{write,pagefault,intwrite}
+ * instead.
+ */
+void __sb_end_write(struct super_block *sb, int level)
+{
+ percpu_counter_dec(&sb->s_writers.counter[level-1]);
+ /*
+ * Make sure s_writers are updated before we wake up waiters in
+ * freeze_super().
+ */
+ smp_mb();
+ if (waitqueue_active(&sb->s_writers.wait))
+ wake_up(&sb->s_writers.wait);
+ rwsem_release(&sb->s_writers.lock_map[level-1], 1, _RET_IP_);
+}
+EXPORT_SYMBOL(__sb_end_write);
+
+#ifdef CONFIG_LOCKDEP
+/*
+ * We want lockdep to tell us about possible deadlocks with freezing but
+ * it's it bit tricky to properly instrument it. Getting a freeze protection
+ * works as getting a read lock but there are subtle problems. XFS for example
+ * gets freeze protection on internal level twice in some cases, which is OK
+ * only because we already hold a freeze protection also on higher level. Due
+ * to these cases we have to tell lockdep we are doing trylock when we
+ * already hold a freeze protection for a higher freeze level.
+ */
+static void acquire_freeze_lock(struct super_block *sb, int level, bool trylock,
+ unsigned long ip)
+{
+ int i;
+
+ if (!trylock) {
+ for (i = 0; i < level - 1; i++)
+ if (lock_is_held(&sb->s_writers.lock_map[i])) {
+ trylock = true;
+ break;
+ }
+ }
+ rwsem_acquire_read(&sb->s_writers.lock_map[level-1], 0, trylock, ip);
+}
+#endif
+
+/*
+ * This is an internal function, please use sb_start_{write,pagefault,intwrite}
+ * instead.
+ */
+int __sb_start_write(struct super_block *sb, int level, bool wait)
+{
+retry:
+ if (unlikely(sb->s_writers.frozen >= level)) {
+ if (!wait)
+ return 0;
+ wait_event(sb->s_writers.wait_unfrozen,
+ sb->s_writers.frozen < level);
+ }
+
+#ifdef CONFIG_LOCKDEP
+ acquire_freeze_lock(sb, level, !wait, _RET_IP_);
+#endif
+ percpu_counter_inc(&sb->s_writers.counter[level-1]);
+ /*
+ * Make sure counter is updated before we check for frozen.
+ * freeze_super() first sets frozen and then checks the counter.
+ */
+ smp_mb();
+ if (unlikely(sb->s_writers.frozen >= level)) {
+ __sb_end_write(sb, level);
+ goto retry;
+ }
+ return 1;
+}
+EXPORT_SYMBOL(__sb_start_write);
+
+/**
+ * sb_wait_write - wait until all writers to given file system finish
+ * @sb: the super for which we wait
+ * @level: type of writers we wait for (normal vs page fault)
+ *
+ * This function waits until there are no writers of given type to given file
+ * system. Caller of this function should make sure there can be no new writers
+ * of type @level before calling this function. Otherwise this function can
+ * livelock.
+ */
+static void sb_wait_write(struct super_block *sb, int level)
+{
+ s64 writers;
+
+ /*
+ * We just cycle-through lockdep here so that it does not complain
+ * about returning with lock to userspace
+ */
+ rwsem_acquire(&sb->s_writers.lock_map[level-1], 0, 0, _THIS_IP_);
+ rwsem_release(&sb->s_writers.lock_map[level-1], 1, _THIS_IP_);
+
+ do {
+ DEFINE_WAIT(wait);
+
+ /*
+ * We use a barrier in prepare_to_wait() to separate setting
+ * of frozen and checking of the counter
+ */
+ prepare_to_wait(&sb->s_writers.wait, &wait,
+ TASK_UNINTERRUPTIBLE);
+
+ writers = percpu_counter_sum(&sb->s_writers.counter[level-1]);
+ if (writers)
+ schedule();
+
+ finish_wait(&sb->s_writers.wait, &wait);
+ } while (writers);
+}
+
/**
* freeze_super - lock the filesystem and force it into a consistent state
* @sb: the super to lock
@@ -1170,6 +1294,31 @@ out:
* Syncs the super to make sure the filesystem is consistent and calls the fs's
* freeze_fs. Subsequent calls to this without first thawing the fs will return
* -EBUSY.
+ *
+ * During this function, sb->s_writers.frozen goes through these values:
+ *
+ * SB_UNFROZEN: File system is normal, all writes progress as usual.
+ *
+ * SB_FREEZE_WRITE: The file system is in the process of being frozen. New
+ * writes should be blocked, though page faults are still allowed. We wait for
+ * all writes to complete and then proceed to the next stage.
+ *
+ * SB_FREEZE_PAGEFAULT: Freezing continues. Now also page faults are blocked
+ * but internal fs threads can still modify the filesystem (although they
+ * should not dirty new pages or inodes), writeback can run etc. After waiting
+ * for all running page faults we sync the filesystem which will clean all
+ * dirty pages and inodes (no new dirty pages or inodes can be created when
+ * sync is running).
+ *
+ * SB_FREEZE_FS: The file system is frozen. Now all internal sources of fs
+ * modification are blocked (e.g. XFS preallocation truncation on inode
+ * reclaim). This is usually implemented by blocking new transactions for
+ * filesystems that have them and need this additional guard. After all
+ * internal writers are finished we call ->freeze_fs() to finish filesystem
+ * freezing. Then we transition to SB_FREEZE_COMPLETE state. This state is
+ * mostly auxiliary for filesystems to verify they do not modify frozen fs.
+ *
+ * sb->s_writers.frozen is protected by sb->s_umount.
*/
int freeze_super(struct super_block *sb)
{
@@ -1177,7 +1326,7 @@ int freeze_super(struct super_block *sb)
atomic_inc(&sb->s_active);
down_write(&sb->s_umount);
- if (sb->s_frozen) {
+ if (sb->s_writers.frozen != SB_UNFROZEN) {
deactivate_locked_super(sb);
return -EBUSY;
}
@@ -1188,33 +1337,53 @@ int freeze_super(struct super_block *sb)
}
if (sb->s_flags & MS_RDONLY) {
- sb->s_frozen = SB_FREEZE_TRANS;
- smp_wmb();
+ /* Nothing to do really... */
+ sb->s_writers.frozen = SB_FREEZE_COMPLETE;
up_write(&sb->s_umount);
return 0;
}
- sb->s_frozen = SB_FREEZE_WRITE;
+ /* From now on, no new normal writers can start */
+ sb->s_writers.frozen = SB_FREEZE_WRITE;
smp_wmb();
+ /* Release s_umount to preserve sb_start_write -> s_umount ordering */
+ up_write(&sb->s_umount);
+
+ sb_wait_write(sb, SB_FREEZE_WRITE);
+
+ /* Now we go and block page faults... */
+ down_write(&sb->s_umount);
+ sb->s_writers.frozen = SB_FREEZE_PAGEFAULT;
+ smp_wmb();
+
+ sb_wait_write(sb, SB_FREEZE_PAGEFAULT);
+
+ /* All writers are done so after syncing there won't be dirty data */
sync_filesystem(sb);
- sb->s_frozen = SB_FREEZE_TRANS;
+ /* Now wait for internal filesystem counter */
+ sb->s_writers.frozen = SB_FREEZE_FS;
smp_wmb();
+ sb_wait_write(sb, SB_FREEZE_FS);
- sync_blockdev(sb->s_bdev);
if (sb->s_op->freeze_fs) {
ret = sb->s_op->freeze_fs(sb);
if (ret) {
printk(KERN_ERR
"VFS:Filesystem freeze failed\n");
- sb->s_frozen = SB_UNFROZEN;
+ sb->s_writers.frozen = SB_UNFROZEN;
smp_wmb();
- wake_up(&sb->s_wait_unfrozen);
+ wake_up(&sb->s_writers.wait_unfrozen);
deactivate_locked_super(sb);
return ret;
}
}
+ /*
+ * This is just for debugging purposes so that fs can warn if it
+ * sees write activity when frozen is set to SB_FREEZE_COMPLETE.
+ */
+ sb->s_writers.frozen = SB_FREEZE_COMPLETE;
up_write(&sb->s_umount);
return 0;
}
@@ -1231,7 +1400,7 @@ int thaw_super(struct super_block *sb)
int error;
down_write(&sb->s_umount);
- if (sb->s_frozen == SB_UNFROZEN) {
+ if (sb->s_writers.frozen == SB_UNFROZEN) {
up_write(&sb->s_umount);
return -EINVAL;
}
@@ -1244,16 +1413,15 @@ int thaw_super(struct super_block *sb)
if (error) {
printk(KERN_ERR
"VFS:Filesystem thaw failed\n");
- sb->s_frozen = SB_FREEZE_TRANS;
up_write(&sb->s_umount);
return error;
}
}
out:
- sb->s_frozen = SB_UNFROZEN;
+ sb->s_writers.frozen = SB_UNFROZEN;
smp_wmb();
- wake_up(&sb->s_wait_unfrozen);
+ wake_up(&sb->s_writers.wait_unfrozen);
deactivate_locked_super(sb);
return 0;
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index a4759833d62d..614b2b544880 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -228,6 +228,8 @@ static int bin_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
ret = 0;
if (bb->vm_ops->page_mkwrite)
ret = bb->vm_ops->page_mkwrite(vma, vmf);
+ else
+ file_update_time(file);
sysfs_put_active(attr_sd);
return ret;
diff --git a/fs/ubifs/debug.h b/fs/ubifs/debug.h
index 8b8cc4e945f4..760de723dadb 100644
--- a/fs/ubifs/debug.h
+++ b/fs/ubifs/debug.h
@@ -167,7 +167,7 @@ struct ubifs_global_debug_info {
#define ubifs_dbg_msg(type, fmt, ...) \
pr_debug("UBIFS DBG " type ": " fmt "\n", ##__VA_ARGS__)
-#define DBG_KEY_BUF_LEN 32
+#define DBG_KEY_BUF_LEN 48
#define ubifs_dbg_msg_key(type, key, fmt, ...) do { \
char __tmp_key_buf[DBG_KEY_BUF_LEN]; \
pr_debug("UBIFS DBG " type ": " fmt "%s\n", ##__VA_ARGS__, \
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 35389ca2d267..7bd6e72afd11 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -37,11 +37,11 @@
*
* A thing to keep in mind: inode @i_mutex is locked in most VFS operations we
* implement. However, this is not true for 'ubifs_writepage()', which may be
- * called with @i_mutex unlocked. For example, when pdflush is doing background
- * write-back, it calls 'ubifs_writepage()' with unlocked @i_mutex. At "normal"
- * work-paths the @i_mutex is locked in 'ubifs_writepage()', e.g. in the
- * "sys_write -> alloc_pages -> direct reclaim path". So, in 'ubifs_writepage()'
- * we are only guaranteed that the page is locked.
+ * called with @i_mutex unlocked. For example, when flusher thread is doing
+ * background write-back, it calls 'ubifs_writepage()' with unlocked @i_mutex.
+ * At "normal" work-paths the @i_mutex is locked in 'ubifs_writepage()', e.g.
+ * in the "sys_write -> alloc_pages -> direct reclaim path". So, in
+ * 'ubifs_writepage()' we are only guaranteed that the page is locked.
*
* Similarly, @i_mutex is not always locked in 'ubifs_readpage()', e.g., the
* read-ahead path does not lock it ("sys_read -> generic_file_aio_read ->
diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c
index ce33b2beb151..8640920766ed 100644
--- a/fs/ubifs/lpt.c
+++ b/fs/ubifs/lpt.c
@@ -1749,7 +1749,10 @@ int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr)
return 0;
out_err:
- ubifs_lpt_free(c, 0);
+ if (wr)
+ ubifs_lpt_free(c, 1);
+ if (rd)
+ ubifs_lpt_free(c, 0);
return err;
}
diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c
index c30d976b4be8..edeec499c048 100644
--- a/fs/ubifs/recovery.c
+++ b/fs/ubifs/recovery.c
@@ -788,7 +788,7 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum,
corrupted_rescan:
/* Re-scan the corrupted data with verbose messages */
- ubifs_err("corruptio %d", ret);
+ ubifs_err("corruption %d", ret);
ubifs_scan_a_node(c, buf, len, lnum, offs, 1);
corrupted:
ubifs_scanned_corruption(c, lnum, offs, buf);
diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c
index eba46d4a7619..94d78fc5d4e0 100644
--- a/fs/ubifs/replay.c
+++ b/fs/ubifs/replay.c
@@ -1026,7 +1026,6 @@ int ubifs_replay_journal(struct ubifs_info *c)
c->replaying = 1;
lnum = c->ltail_lnum = c->lhead_lnum;
- lnum = UBIFS_LOG_LNUM;
do {
err = replay_log_leb(c, lnum, 0, c->sbuf);
if (err == 1)
@@ -1035,7 +1034,7 @@ int ubifs_replay_journal(struct ubifs_info *c)
if (err)
goto out;
lnum = ubifs_next_log_lnum(c, lnum);
- } while (lnum != UBIFS_LOG_LNUM);
+ } while (lnum != c->ltail_lnum);
err = replay_buds(c);
if (err)
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 1c766c39c038..71a197f0f93d 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -303,7 +303,7 @@ static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
mutex_lock(&ui->ui_mutex);
/*
* Due to races between write-back forced by budgeting
- * (see 'sync_some_inodes()') and pdflush write-back, the inode may
+ * (see 'sync_some_inodes()') and background write-back, the inode may
* have already been synchronized, do not do this again. This might
* also happen if it was synchronized in an VFS operation, e.g.
* 'ubifs_link()'.
@@ -1157,9 +1157,6 @@ static int check_free_space(struct ubifs_info *c)
*
* This function mounts UBIFS file system. Returns zero in case of success and
* a negative error code in case of failure.
- *
- * Note, the function does not de-allocate resources it it fails half way
- * through, and the caller has to do this instead.
*/
static int mount_ubifs(struct ubifs_info *c)
{
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 7f3f7ba3df6e..d1c6093fd3d3 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -39,20 +39,24 @@
#include "udf_i.h"
#include "udf_sb.h"
-static int udf_adinicb_readpage(struct file *file, struct page *page)
+static void __udf_adinicb_readpage(struct page *page)
{
struct inode *inode = page->mapping->host;
char *kaddr;
struct udf_inode_info *iinfo = UDF_I(inode);
- BUG_ON(!PageLocked(page));
-
kaddr = kmap(page);
- memset(kaddr, 0, PAGE_CACHE_SIZE);
memcpy(kaddr, iinfo->i_ext.i_data + iinfo->i_lenEAttr, inode->i_size);
+ memset(kaddr + inode->i_size, 0, PAGE_CACHE_SIZE - inode->i_size);
flush_dcache_page(page);
SetPageUptodate(page);
kunmap(page);
+}
+
+static int udf_adinicb_readpage(struct file *file, struct page *page)
+{
+ BUG_ON(!PageLocked(page));
+ __udf_adinicb_readpage(page);
unlock_page(page);
return 0;
@@ -77,6 +81,25 @@ static int udf_adinicb_writepage(struct page *page,
return 0;
}
+static int udf_adinicb_write_begin(struct file *file,
+ struct address_space *mapping, loff_t pos,
+ unsigned len, unsigned flags, struct page **pagep,
+ void **fsdata)
+{
+ struct page *page;
+
+ if (WARN_ON_ONCE(pos >= PAGE_CACHE_SIZE))
+ return -EIO;
+ page = grab_cache_page_write_begin(mapping, 0, flags);
+ if (!page)
+ return -ENOMEM;
+ *pagep = page;
+
+ if (!PageUptodate(page) && len != PAGE_CACHE_SIZE)
+ __udf_adinicb_readpage(page);
+ return 0;
+}
+
static int udf_adinicb_write_end(struct file *file,
struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied,
@@ -98,8 +121,8 @@ static int udf_adinicb_write_end(struct file *file,
const struct address_space_operations udf_adinicb_aops = {
.readpage = udf_adinicb_readpage,
.writepage = udf_adinicb_writepage,
- .write_begin = simple_write_begin,
- .write_end = udf_adinicb_write_end,
+ .write_begin = udf_adinicb_write_begin,
+ .write_end = udf_adinicb_write_end,
};
static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index fafaad795cd6..aa233469b3c1 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -1124,14 +1124,17 @@ int udf_setsize(struct inode *inode, loff_t newsize)
if (err)
return err;
down_write(&iinfo->i_data_sem);
- } else
+ } else {
iinfo->i_lenAlloc = newsize;
+ goto set_size;
+ }
}
err = udf_extend_file(inode, newsize);
if (err) {
up_write(&iinfo->i_data_sem);
return err;
}
+set_size:
truncate_setsize(inode, newsize);
up_write(&iinfo->i_data_sem);
} else {
diff --git a/fs/udf/super.c b/fs/udf/super.c
index dcbf98722afc..18fc038a438d 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -1344,6 +1344,7 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
udf_err(sb, "error loading logical volume descriptor: "
"Partition table too long (%u > %lu)\n", table_len,
sb->s_blocksize - sizeof(*lvd));
+ ret = 1;
goto out_bh;
}
@@ -1388,8 +1389,10 @@ static int udf_load_logicalvol(struct super_block *sb, sector_t block,
UDF_ID_SPARABLE,
strlen(UDF_ID_SPARABLE))) {
if (udf_load_sparable_map(sb, map,
- (struct sparablePartitionMap *)gpm) < 0)
+ (struct sparablePartitionMap *)gpm) < 0) {
+ ret = 1;
goto out_bh;
+ }
} else if (!strncmp(upm2->partIdent.ident,
UDF_ID_METADATA,
strlen(UDF_ID_METADATA))) {
@@ -2000,6 +2003,8 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent)
if (!silent)
pr_notice("Rescanning with blocksize %d\n",
UDF_DEFAULT_BLOCKSIZE);
+ brelse(sbi->s_lvid_bh);
+ sbi->s_lvid_bh = NULL;
uopt.blocksize = UDF_DEFAULT_BLOCKSIZE;
ret = udf_load_vrs(sb, &uopt, silent, &fileset);
}
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 15052ff916ec..e562dd43f41f 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -124,6 +124,12 @@ xfs_setfilesize_trans_alloc(
ioend->io_append_trans = tp;
/*
+ * We will pass freeze protection with a transaction. So tell lockdep
+ * we released it.
+ */
+ rwsem_release(&ioend->io_inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+ 1, _THIS_IP_);
+ /*
* We hand off the transaction to the completion thread now, so
* clear the flag here.
*/
@@ -199,6 +205,15 @@ xfs_end_io(
struct xfs_inode *ip = XFS_I(ioend->io_inode);
int error = 0;
+ if (ioend->io_append_trans) {
+ /*
+ * We've got freeze protection passed with the transaction.
+ * Tell lockdep about it.
+ */
+ rwsem_acquire_read(
+ &ioend->io_inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+ 0, 1, _THIS_IP_);
+ }
if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
ioend->io_error = -EIO;
goto done;
@@ -1425,6 +1440,9 @@ out_trans_cancel:
if (ioend->io_append_trans) {
current_set_flags_nested(&ioend->io_append_trans->t_pflags,
PF_FSTRANS);
+ rwsem_acquire_read(
+ &inode->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
+ 0, 1, _THIS_IP_);
xfs_trans_cancel(ioend->io_append_trans, 0);
}
out_destroy_ioend:
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index d7a9dd735e1e..933b7930b863 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -96,6 +96,7 @@ xfs_buf_lru_add(
atomic_inc(&bp->b_hold);
list_add_tail(&bp->b_lru, &btp->bt_lru);
btp->bt_lru_nr++;
+ bp->b_lru_flags &= ~_XBF_LRU_DISPOSE;
}
spin_unlock(&btp->bt_lru_lock);
}
@@ -154,7 +155,8 @@ xfs_buf_stale(
struct xfs_buftarg *btp = bp->b_target;
spin_lock(&btp->bt_lru_lock);
- if (!list_empty(&bp->b_lru)) {
+ if (!list_empty(&bp->b_lru) &&
+ !(bp->b_lru_flags & _XBF_LRU_DISPOSE)) {
list_del_init(&bp->b_lru);
btp->bt_lru_nr--;
atomic_dec(&bp->b_hold);
@@ -1501,6 +1503,7 @@ xfs_buftarg_shrink(
*/
list_move(&bp->b_lru, &dispose);
btp->bt_lru_nr--;
+ bp->b_lru_flags |= _XBF_LRU_DISPOSE;
}
spin_unlock(&btp->bt_lru_lock);
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index d03b73b9604e..7c0b6a0a1557 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -38,27 +38,28 @@ typedef enum {
XBRW_ZERO = 3, /* Zero target memory */
} xfs_buf_rw_t;
-#define XBF_READ (1 << 0) /* buffer intended for reading from device */
-#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */
-#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
-#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */
-#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */
-#define XBF_STALE (1 << 6) /* buffer has been staled, do not find it */
+#define XBF_READ (1 << 0) /* buffer intended for reading from device */
+#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */
+#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
+#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */
+#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */
+#define XBF_STALE (1 << 6) /* buffer has been staled, do not find it */
/* I/O hints for the BIO layer */
-#define XBF_SYNCIO (1 << 10)/* treat this buffer as synchronous I/O */
-#define XBF_FUA (1 << 11)/* force cache write through mode */
-#define XBF_FLUSH (1 << 12)/* flush the disk cache before a write */
+#define XBF_SYNCIO (1 << 10)/* treat this buffer as synchronous I/O */
+#define XBF_FUA (1 << 11)/* force cache write through mode */
+#define XBF_FLUSH (1 << 12)/* flush the disk cache before a write */
/* flags used only as arguments to access routines */
-#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */
-#define XBF_UNMAPPED (1 << 17)/* do not map the buffer */
+#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */
+#define XBF_UNMAPPED (1 << 17)/* do not map the buffer */
/* flags used only internally */
-#define _XBF_PAGES (1 << 20)/* backed by refcounted pages */
-#define _XBF_KMEM (1 << 21)/* backed by heap memory */
-#define _XBF_DELWRI_Q (1 << 22)/* buffer on a delwri queue */
-#define _XBF_COMPOUND (1 << 23)/* compound buffer */
+#define _XBF_PAGES (1 << 20)/* backed by refcounted pages */
+#define _XBF_KMEM (1 << 21)/* backed by heap memory */
+#define _XBF_DELWRI_Q (1 << 22)/* buffer on a delwri queue */
+#define _XBF_COMPOUND (1 << 23)/* compound buffer */
+#define _XBF_LRU_DISPOSE (1 << 24)/* buffer being discarded */
typedef unsigned int xfs_buf_flags_t;
@@ -72,12 +73,13 @@ typedef unsigned int xfs_buf_flags_t;
{ XBF_SYNCIO, "SYNCIO" }, \
{ XBF_FUA, "FUA" }, \
{ XBF_FLUSH, "FLUSH" }, \
- { XBF_TRYLOCK, "TRYLOCK" }, /* should never be set */\
+ { XBF_TRYLOCK, "TRYLOCK" }, /* should never be set */\
{ XBF_UNMAPPED, "UNMAPPED" }, /* ditto */\
{ _XBF_PAGES, "PAGES" }, \
{ _XBF_KMEM, "KMEM" }, \
{ _XBF_DELWRI_Q, "DELWRI_Q" }, \
- { _XBF_COMPOUND, "COMPOUND" }
+ { _XBF_COMPOUND, "COMPOUND" }, \
+ { _XBF_LRU_DISPOSE, "LRU_DISPOSE" }
typedef struct xfs_buftarg {
dev_t bt_dev;
@@ -124,7 +126,12 @@ typedef struct xfs_buf {
xfs_buf_flags_t b_flags; /* status flags */
struct semaphore b_sema; /* semaphore for lockables */
+ /*
+ * concurrent access to b_lru and b_lru_flags are protected by
+ * bt_lru_lock and not by b_sema
+ */
struct list_head b_lru; /* lru list */
+ xfs_buf_flags_t b_lru_flags; /* internal lru status flags */
wait_queue_head_t b_waiters; /* unpin waiters */
struct list_head b_list;
struct xfs_perag *b_pag; /* contains rbtree root */
diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c
index f9c3fe304a17..69cf4fcde03e 100644
--- a/fs/xfs/xfs_discard.c
+++ b/fs/xfs/xfs_discard.c
@@ -179,12 +179,14 @@ xfs_ioc_trim(
* used by the fstrim application. In the end it really doesn't
* matter as trimming blocks is an advisory interface.
*/
+ if (range.start >= XFS_FSB_TO_B(mp, mp->m_sb.sb_dblocks) ||
+ range.minlen > XFS_FSB_TO_B(mp, XFS_ALLOC_AG_MAX_USABLE(mp)))
+ return -XFS_ERROR(EINVAL);
+
start = BTOBB(range.start);
end = start + BTOBBT(range.len) - 1;
minlen = BTOBB(max_t(u64, granularity, range.minlen));
- if (XFS_BB_TO_FSB(mp, start) >= mp->m_sb.sb_dblocks)
- return -XFS_ERROR(EINVAL);
if (end > XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) - 1)
end = XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)- 1;
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index c4559c6e6f2c..56afcdb2377d 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -770,10 +770,12 @@ xfs_file_aio_write(
if (ocount == 0)
return 0;
- xfs_wait_for_freeze(ip->i_mount, SB_FREEZE_WRITE);
+ sb_start_write(inode->i_sb);
- if (XFS_FORCED_SHUTDOWN(ip->i_mount))
- return -EIO;
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+ ret = -EIO;
+ goto out;
+ }
if (unlikely(file->f_flags & O_DIRECT))
ret = xfs_file_dio_aio_write(iocb, iovp, nr_segs, pos, ocount);
@@ -792,6 +794,8 @@ xfs_file_aio_write(
ret = err;
}
+out:
+ sb_end_write(inode->i_sb);
return ret;
}
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
index 21e37b55f7e5..5aceb3f8ecd6 100644
--- a/fs/xfs/xfs_ialloc.c
+++ b/fs/xfs/xfs_ialloc.c
@@ -962,23 +962,22 @@ xfs_dialloc(
if (!pag->pagi_freecount && !okalloc)
goto nextag;
+ /*
+ * Then read in the AGI buffer and recheck with the AGI buffer
+ * lock held.
+ */
error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
if (error)
goto out_error;
- /*
- * Once the AGI has been read in we have to recheck
- * pagi_freecount with the AGI buffer lock held.
- */
if (pag->pagi_freecount) {
xfs_perag_put(pag);
goto out_alloc;
}
- if (!okalloc) {
- xfs_trans_brelse(tp, agbp);
- goto nextag;
- }
+ if (!okalloc)
+ goto nextag_relse_buffer;
+
error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced);
if (error) {
@@ -1007,6 +1006,8 @@ xfs_dialloc(
return 0;
}
+nextag_relse_buffer:
+ xfs_trans_brelse(tp, agbp);
nextag:
xfs_perag_put(pag);
if (++agno == mp->m_sb.sb_agcount)
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 1f1535d25a9b..0e0232c3b6d9 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -364,9 +364,15 @@ xfs_fssetdm_by_handle(
if (copy_from_user(&dmhreq, arg, sizeof(xfs_fsop_setdm_handlereq_t)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(parfilp);
+ if (error)
+ return error;
+
dentry = xfs_handlereq_to_dentry(parfilp, &dmhreq.hreq);
- if (IS_ERR(dentry))
+ if (IS_ERR(dentry)) {
+ mnt_drop_write_file(parfilp);
return PTR_ERR(dentry);
+ }
if (IS_IMMUTABLE(dentry->d_inode) || IS_APPEND(dentry->d_inode)) {
error = -XFS_ERROR(EPERM);
@@ -382,6 +388,7 @@ xfs_fssetdm_by_handle(
fsd.fsd_dmstate);
out:
+ mnt_drop_write_file(parfilp);
dput(dentry);
return error;
}
@@ -634,7 +641,11 @@ xfs_ioc_space(
if (ioflags & IO_INVIS)
attr_flags |= XFS_ATTR_DMI;
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_change_file_space(ip, cmd, bf, filp->f_pos, attr_flags);
+ mnt_drop_write_file(filp);
return -error;
}
@@ -1163,6 +1174,7 @@ xfs_ioc_fssetxattr(
{
struct fsxattr fa;
unsigned int mask;
+ int error;
if (copy_from_user(&fa, arg, sizeof(fa)))
return -EFAULT;
@@ -1171,7 +1183,12 @@ xfs_ioc_fssetxattr(
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
mask |= FSX_NONBLOCK;
- return -xfs_ioctl_setattr(ip, &fa, mask);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
+ error = xfs_ioctl_setattr(ip, &fa, mask);
+ mnt_drop_write_file(filp);
+ return -error;
}
STATIC int
@@ -1196,6 +1213,7 @@ xfs_ioc_setxflags(
struct fsxattr fa;
unsigned int flags;
unsigned int mask;
+ int error;
if (copy_from_user(&flags, arg, sizeof(flags)))
return -EFAULT;
@@ -1210,7 +1228,12 @@ xfs_ioc_setxflags(
mask |= FSX_NONBLOCK;
fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
- return -xfs_ioctl_setattr(ip, &fa, mask);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
+ error = xfs_ioctl_setattr(ip, &fa, mask);
+ mnt_drop_write_file(filp);
+ return -error;
}
STATIC int
@@ -1385,8 +1408,13 @@ xfs_file_ioctl(
if (copy_from_user(&dmi, arg, sizeof(dmi)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
+
error = xfs_set_dmattrs(ip, dmi.fsd_dmevmask,
dmi.fsd_dmstate);
+ mnt_drop_write_file(filp);
return -error;
}
@@ -1434,7 +1462,11 @@ xfs_file_ioctl(
if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_swapext(&sxp);
+ mnt_drop_write_file(filp);
return -error;
}
@@ -1463,9 +1495,14 @@ xfs_file_ioctl(
if (copy_from_user(&inout, arg, sizeof(inout)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
+
/* input parameter is passed in resblks field of structure */
in = inout.resblks;
error = xfs_reserve_blocks(mp, &in, &inout);
+ mnt_drop_write_file(filp);
if (error)
return -error;
@@ -1496,7 +1533,11 @@ xfs_file_ioctl(
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_growfs_data(mp, &in);
+ mnt_drop_write_file(filp);
return -error;
}
@@ -1506,7 +1547,11 @@ xfs_file_ioctl(
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_growfs_log(mp, &in);
+ mnt_drop_write_file(filp);
return -error;
}
@@ -1516,7 +1561,11 @@ xfs_file_ioctl(
if (copy_from_user(&in, arg, sizeof(in)))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_growfs_rt(mp, &in);
+ mnt_drop_write_file(filp);
return -error;
}
diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c
index c4f2da0d2bf5..1244274a5674 100644
--- a/fs/xfs/xfs_ioctl32.c
+++ b/fs/xfs/xfs_ioctl32.c
@@ -600,7 +600,11 @@ xfs_file_compat_ioctl(
if (xfs_compat_growfs_data_copyin(&in, arg))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_growfs_data(mp, &in);
+ mnt_drop_write_file(filp);
return -error;
}
case XFS_IOC_FSGROWFSRT_32: {
@@ -608,7 +612,11 @@ xfs_file_compat_ioctl(
if (xfs_compat_growfs_rt_copyin(&in, arg))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_growfs_rt(mp, &in);
+ mnt_drop_write_file(filp);
return -error;
}
#endif
@@ -627,7 +635,11 @@ xfs_file_compat_ioctl(
offsetof(struct xfs_swapext, sx_stat)) ||
xfs_ioctl32_bstat_copyin(&sxp.sx_stat, &sxu->sx_stat))
return -XFS_ERROR(EFAULT);
+ error = mnt_want_write_file(filp);
+ if (error)
+ return error;
error = xfs_swapext(&sxp);
+ mnt_drop_write_file(filp);
return -error;
}
case XFS_IOC_FSBULKSTAT_32:
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 915edf6639f0..973dff6ad935 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -680,9 +680,9 @@ xfs_iomap_write_unwritten(
* the same inode that we complete here and might deadlock
* on the iolock.
*/
- xfs_wait_for_freeze(mp, SB_FREEZE_TRANS);
+ sb_start_intwrite(mp->m_super);
tp = _xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE, KM_NOFS);
- tp->t_flags |= XFS_TRANS_RESERVE;
+ tp->t_flags |= XFS_TRANS_RESERVE | XFS_TRANS_FREEZE_PROT;
error = xfs_trans_reserve(tp, resblks,
XFS_WRITE_LOG_RES(mp), 0,
XFS_TRANS_PERM_LOG_RES,
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 711ca51ca3d7..29c2f83d4147 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1551,7 +1551,7 @@ xfs_unmountfs(
int
xfs_fs_writable(xfs_mount_t *mp)
{
- return !(xfs_test_for_freeze(mp) || XFS_FORCED_SHUTDOWN(mp) ||
+ return !(mp->m_super->s_writers.frozen || XFS_FORCED_SHUTDOWN(mp) ||
(mp->m_flags & XFS_MOUNT_RDONLY));
}
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 8724336a9a08..05a05a7b6119 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -311,9 +311,6 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
#define SHUTDOWN_REMOTE_REQ 0x0010 /* shutdown came from remote cell */
#define SHUTDOWN_DEVICE_REQ 0x0020 /* failed all paths to the device */
-#define xfs_test_for_freeze(mp) ((mp)->m_super->s_frozen)
-#define xfs_wait_for_freeze(mp,l) vfs_check_frozen((mp)->m_super, (l))
-
/*
* Flags for xfs_mountfs
*/
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 92d4331cd4f1..ca28a4ba4b54 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -857,7 +857,7 @@ xfs_rtbuf_get(
xfs_buf_t *bp; /* block buffer, result */
xfs_inode_t *ip; /* bitmap or summary inode */
xfs_bmbt_irec_t map;
- int nmap;
+ int nmap = 1;
int error; /* error value */
ip = issum ? mp->m_rsumip : mp->m_rbmip;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bdaf4cb9f4a2..19e2380fb867 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -919,6 +919,7 @@ xfs_fs_put_super(
struct xfs_mount *mp = XFS_M(sb);
xfs_filestream_unmount(mp);
+ cancel_delayed_work_sync(&mp->m_sync_work);
xfs_unmountfs(mp);
xfs_syncd_stop(mp);
xfs_freesb(mp);
diff --git a/fs/xfs/xfs_sync.c b/fs/xfs/xfs_sync.c
index 97304f10e78a..96548176db80 100644
--- a/fs/xfs/xfs_sync.c
+++ b/fs/xfs/xfs_sync.c
@@ -403,7 +403,7 @@ xfs_sync_worker(
if (!(mp->m_super->s_flags & MS_ACTIVE) &&
!(mp->m_flags & XFS_MOUNT_RDONLY)) {
/* dgc: errors ignored here */
- if (mp->m_super->s_frozen == SB_UNFROZEN &&
+ if (mp->m_super->s_writers.frozen == SB_UNFROZEN &&
xfs_log_need_covered(mp))
error = xfs_fs_log_dummy(mp);
else
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index fdf324508c5e..06ed520a767f 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -576,8 +576,12 @@ xfs_trans_alloc(
xfs_mount_t *mp,
uint type)
{
- xfs_wait_for_freeze(mp, SB_FREEZE_TRANS);
- return _xfs_trans_alloc(mp, type, KM_SLEEP);
+ xfs_trans_t *tp;
+
+ sb_start_intwrite(mp->m_super);
+ tp = _xfs_trans_alloc(mp, type, KM_SLEEP);
+ tp->t_flags |= XFS_TRANS_FREEZE_PROT;
+ return tp;
}
xfs_trans_t *
@@ -588,6 +592,7 @@ _xfs_trans_alloc(
{
xfs_trans_t *tp;
+ WARN_ON(mp->m_super->s_writers.frozen == SB_FREEZE_COMPLETE);
atomic_inc(&mp->m_active_trans);
tp = kmem_zone_zalloc(xfs_trans_zone, memflags);
@@ -611,6 +616,8 @@ xfs_trans_free(
xfs_extent_busy_clear(tp->t_mountp, &tp->t_busy, false);
atomic_dec(&tp->t_mountp->m_active_trans);
+ if (tp->t_flags & XFS_TRANS_FREEZE_PROT)
+ sb_end_intwrite(tp->t_mountp->m_super);
xfs_trans_free_dqinfo(tp);
kmem_zone_free(xfs_trans_zone, tp);
}
@@ -643,7 +650,11 @@ xfs_trans_dup(
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(tp->t_ticket != NULL);
- ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE);
+ ntp->t_flags = XFS_TRANS_PERM_LOG_RES |
+ (tp->t_flags & XFS_TRANS_RESERVE) |
+ (tp->t_flags & XFS_TRANS_FREEZE_PROT);
+ /* We gave our writer reference to the new transaction */
+ tp->t_flags &= ~XFS_TRANS_FREEZE_PROT;
ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
tp->t_blk_res = tp->t_blk_res_used;
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index bc2afd52a0b7..db056544cbb5 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -179,6 +179,8 @@ struct xfs_log_item_desc {
#define XFS_TRANS_SYNC 0x08 /* make commit synchronous */
#define XFS_TRANS_DQ_DIRTY 0x10 /* at least one dquot in trx dirty */
#define XFS_TRANS_RESERVE 0x20 /* OK to use reserved data blocks */
+#define XFS_TRANS_FREEZE_PROT 0x40 /* Transaction has elevated writer
+ count in superblock */
/*
* Values for call flags parameter.
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 2c744c7a5b3d..26a92fc28a59 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -491,11 +491,11 @@ acpi_get_sleep_type_data(u8 sleep_state, u8 * slp_typ_a, u8 * slp_typ_b);
acpi_status acpi_enter_sleep_state_prep(u8 sleep_state);
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags);
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state);
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void))
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
acpi_status acpi_leave_sleep_state(u8 sleep_state);
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
index 3af87de6a68c..3d00bd5bd7e3 100644
--- a/include/acpi/actypes.h
+++ b/include/acpi/actypes.h
@@ -803,7 +803,7 @@ typedef u8 acpi_adr_space_type;
/* Sleep function dispatch */
-typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state, u8 flags);
+typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state);
struct acpi_sleep_functions {
ACPI_SLEEP_FUNCTION legacy_function;
diff --git a/include/asm-generic/mutex-xchg.h b/include/asm-generic/mutex-xchg.h
index 580a6d35c700..c04e0db8a2d6 100644
--- a/include/asm-generic/mutex-xchg.h
+++ b/include/asm-generic/mutex-xchg.h
@@ -26,7 +26,13 @@ static inline void
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 0) != 1))
- fail_fn(count);
+ /*
+ * We failed to acquire the lock, so mark it contended
+ * to ensure that any waiting tasks are woken up by the
+ * unlock slow path.
+ */
+ if (likely(atomic_xchg(count, -1) != 1))
+ fail_fn(count);
}
/**
@@ -43,7 +49,8 @@ static inline int
__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 0) != 1))
- return fail_fn(count);
+ if (likely(atomic_xchg(count, -1) != 1))
+ return fail_fn(count);
return 0;
}
diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h
index 991ef01cd77e..3748ec92dcbc 100644
--- a/include/asm-generic/unistd.h
+++ b/include/asm-generic/unistd.h
@@ -691,9 +691,11 @@ __SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \
#define __NR_process_vm_writev 271
__SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \
compat_sys_process_vm_writev)
+#define __NR_kcmp 272
+__SYSCALL(__NR_kcmp, sys_kcmp)
#undef __NR_syscalls
-#define __NR_syscalls 272
+#define __NR_syscalls 273
/*
* All syscalls below here should go away really,
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index a1a0386e0160..bfacf0d5a225 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -118,7 +118,8 @@ enum drm_mode_status {
.hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \
.htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \
.vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
- .vscan = (vs), .flags = (f), .vrefresh = 0
+ .vscan = (vs), .flags = (f), .vrefresh = 0, \
+ .base.type = DRM_MODE_OBJECT_MODE
#define CRTC_INTERLACE_HALVE_V 0x1 /* halve V values for interlacing */
@@ -166,8 +167,6 @@ struct drm_display_mode {
int crtc_vsync_start;
int crtc_vsync_end;
int crtc_vtotal;
- int crtc_hadjusted;
- int crtc_vadjusted;
/* Driver private mode info */
int private_size;
diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h
index bdf0152cbbe9..f4621184a9b4 100644
--- a/include/drm/drm_fourcc.h
+++ b/include/drm/drm_fourcc.h
@@ -107,8 +107,7 @@
#define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */
#define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */
-/* 2 non contiguous plane YCbCr */
-#define DRM_FORMAT_NV12M fourcc_code('N', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane */
+/* special NV12 tiled format */
#define DRM_FORMAT_NV12MT fourcc_code('T', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane 64x32 macroblocks */
/*
@@ -131,7 +130,4 @@
#define DRM_FORMAT_YUV444 fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
#define DRM_FORMAT_YVU444 fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
-/* 3 non contiguous plane YCbCr */
-#define DRM_FORMAT_YUV420M fourcc_code('Y', 'M', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
-
#endif /* DRM_FOURCC_H */
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index 5581980b14f6..3d6301b6ec16 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -359,8 +359,9 @@ struct drm_mode_mode_cmd {
struct drm_mode_modeinfo mode;
};
-#define DRM_MODE_CURSOR_BO (1<<0)
-#define DRM_MODE_CURSOR_MOVE (1<<1)
+#define DRM_MODE_CURSOR_BO 0x01
+#define DRM_MODE_CURSOR_MOVE 0x02
+#define DRM_MODE_CURSOR_FLAGS 0x03
/*
* depending on the value in flags different members are used.
diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h
index 7ff5c99b1638..c78bb997e2c6 100644
--- a/include/drm/drm_pciids.h
+++ b/include/drm/drm_pciids.h
@@ -213,9 +213,12 @@
{0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
index 58056865b8e9..dc3a8cd7db8a 100644
--- a/include/drm/radeon_drm.h
+++ b/include/drm/radeon_drm.h
@@ -964,6 +964,8 @@ struct drm_radeon_cs {
#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f
/* max pipes - needed for compute shaders */
#define RADEON_INFO_MAX_PIPES 0x10
+/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */
+#define RADEON_INFO_TIMESTAMP 0x11
struct drm_radeon_info {
uint32_t request;
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index d9a754474878..e53840d941d4 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -387,10 +387,12 @@ header-y += utsname.h
header-y += uuid.h
header-y += uvcvideo.h
header-y += v4l2-common.h
+header-y += v4l2-controls.h
header-y += v4l2-dv-timings.h
header-y += v4l2-mediabus.h
header-y += v4l2-subdev.h
header-y += veth.h
+header-y += vfio.h
header-y += vhost.h
header-y += videodev2.h
header-y += virtio_9p.h
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 3ad510b25283..4f2a76224509 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -96,7 +96,7 @@ void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
void acpi_numa_slit_init (struct acpi_table_slit *slit);
void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa);
void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa);
-void acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
+int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
void acpi_numa_arch_fixup(void);
#ifdef CONFIG_ACPI_HOTPLUG_CPU
diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h
index 02549017212a..2a5f64a11b77 100644
--- a/include/linux/amba/pl08x.h
+++ b/include/linux/amba/pl08x.h
@@ -21,8 +21,9 @@
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
-struct pl08x_lli;
struct pl08x_driver_data;
+struct pl08x_phy_chan;
+struct pl08x_txd;
/* Bitmasks for selecting AHB ports for DMA transfers */
enum {
@@ -46,170 +47,29 @@ enum {
* devices with static assignments
* @muxval: a number usually used to poke into some mux regiser to
* mux in the signal to this channel
- * @cctl_opt: default options for the channel control register
+ * @cctl_memcpy: options for the channel control register for memcpy
+ * *** not used for slave channels ***
* @addr: source/target address in physical memory for this DMA channel,
* can be the address of a FIFO register for burst requests for example.
* This can be left undefined if the PrimeCell API is used for configuring
* this.
- * @circular_buffer: whether the buffer passed in is circular and
- * shall simply be looped round round (like a record baby round
- * round round round)
* @single: the device connected to this channel will request single DMA
* transfers, not bursts. (Bursts are default.)
* @periph_buses: the device connected to this channel is accessible via
* these buses (use PL08X_AHB1 | PL08X_AHB2).
*/
struct pl08x_channel_data {
- char *bus_id;
+ const char *bus_id;
int min_signal;
int max_signal;
u32 muxval;
- u32 cctl;
+ u32 cctl_memcpy;
dma_addr_t addr;
- bool circular_buffer;
bool single;
u8 periph_buses;
};
/**
- * Struct pl08x_bus_data - information of source or destination
- * busses for a transfer
- * @addr: current address
- * @maxwidth: the maximum width of a transfer on this bus
- * @buswidth: the width of this bus in bytes: 1, 2 or 4
- */
-struct pl08x_bus_data {
- dma_addr_t addr;
- u8 maxwidth;
- u8 buswidth;
-};
-
-/**
- * struct pl08x_phy_chan - holder for the physical channels
- * @id: physical index to this channel
- * @lock: a lock to use when altering an instance of this struct
- * @signal: the physical signal (aka channel) serving this physical channel
- * right now
- * @serving: the virtual channel currently being served by this physical
- * channel
- * @locked: channel unavailable for the system, e.g. dedicated to secure
- * world
- */
-struct pl08x_phy_chan {
- unsigned int id;
- void __iomem *base;
- spinlock_t lock;
- int signal;
- struct pl08x_dma_chan *serving;
- bool locked;
-};
-
-/**
- * struct pl08x_sg - structure containing data per sg
- * @src_addr: src address of sg
- * @dst_addr: dst address of sg
- * @len: transfer len in bytes
- * @node: node for txd's dsg_list
- */
-struct pl08x_sg {
- dma_addr_t src_addr;
- dma_addr_t dst_addr;
- size_t len;
- struct list_head node;
-};
-
-/**
- * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor
- * @tx: async tx descriptor
- * @node: node for txd list for channels
- * @dsg_list: list of children sg's
- * @direction: direction of transfer
- * @llis_bus: DMA memory address (physical) start for the LLIs
- * @llis_va: virtual memory address start for the LLIs
- * @cctl: control reg values for current txd
- * @ccfg: config reg values for current txd
- */
-struct pl08x_txd {
- struct dma_async_tx_descriptor tx;
- struct list_head node;
- struct list_head dsg_list;
- enum dma_transfer_direction direction;
- dma_addr_t llis_bus;
- struct pl08x_lli *llis_va;
- /* Default cctl value for LLIs */
- u32 cctl;
- /*
- * Settings to be put into the physical channel when we
- * trigger this txd. Other registers are in llis_va[0].
- */
- u32 ccfg;
-};
-
-/**
- * struct pl08x_dma_chan_state - holds the PL08x specific virtual channel
- * states
- * @PL08X_CHAN_IDLE: the channel is idle
- * @PL08X_CHAN_RUNNING: the channel has allocated a physical transport
- * channel and is running a transfer on it
- * @PL08X_CHAN_PAUSED: the channel has allocated a physical transport
- * channel, but the transfer is currently paused
- * @PL08X_CHAN_WAITING: the channel is waiting for a physical transport
- * channel to become available (only pertains to memcpy channels)
- */
-enum pl08x_dma_chan_state {
- PL08X_CHAN_IDLE,
- PL08X_CHAN_RUNNING,
- PL08X_CHAN_PAUSED,
- PL08X_CHAN_WAITING,
-};
-
-/**
- * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel
- * @chan: wrappped abstract channel
- * @phychan: the physical channel utilized by this channel, if there is one
- * @phychan_hold: if non-zero, hold on to the physical channel even if we
- * have no pending entries
- * @tasklet: tasklet scheduled by the IRQ to handle actual work etc
- * @name: name of channel
- * @cd: channel platform data
- * @runtime_addr: address for RX/TX according to the runtime config
- * @runtime_direction: current direction of this channel according to
- * runtime config
- * @pend_list: queued transactions pending on this channel
- * @at: active transaction on this channel
- * @lock: a lock for this channel data
- * @host: a pointer to the host (internal use)
- * @state: whether the channel is idle, paused, running etc
- * @slave: whether this channel is a device (slave) or for memcpy
- * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave
- * channels. Fill with 'true' if peripheral should be flow controller. Direction
- * will be selected at Runtime.
- * @waiting: a TX descriptor on this channel which is waiting for a physical
- * channel to become available
- */
-struct pl08x_dma_chan {
- struct dma_chan chan;
- struct pl08x_phy_chan *phychan;
- int phychan_hold;
- struct tasklet_struct tasklet;
- char *name;
- const struct pl08x_channel_data *cd;
- dma_addr_t src_addr;
- dma_addr_t dst_addr;
- u32 src_cctl;
- u32 dst_cctl;
- enum dma_transfer_direction runtime_direction;
- struct list_head pend_list;
- struct pl08x_txd *at;
- spinlock_t lock;
- struct pl08x_driver_data *host;
- enum pl08x_dma_chan_state state;
- bool slave;
- bool device_fc;
- struct pl08x_txd *waiting;
-};
-
-/**
* struct pl08x_platform_data - the platform configuration for the PL08x
* PrimeCells.
* @slave_channels: the channels defined for the different devices on the
@@ -229,8 +89,8 @@ struct pl08x_platform_data {
const struct pl08x_channel_data *slave_channels;
unsigned int num_slave_channels;
struct pl08x_channel_data memcpy_channel;
- int (*get_signal)(struct pl08x_dma_chan *);
- void (*put_signal)(struct pl08x_dma_chan *);
+ int (*get_signal)(const struct pl08x_channel_data *);
+ void (*put_signal)(const struct pl08x_channel_data *, int);
u8 lli_buses;
u8 mem_buses;
};
diff --git a/include/linux/atmel-ssc.h b/include/linux/atmel-ssc.h
index 06023393fba9..4eb31752e2b7 100644
--- a/include/linux/atmel-ssc.h
+++ b/include/linux/atmel-ssc.h
@@ -3,6 +3,7 @@
#include <linux/platform_device.h>
#include <linux/list.h>
+#include <linux/io.h>
struct ssc_device {
struct list_head list;
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 22f292a917a3..36abf2aa7e68 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -130,6 +130,7 @@
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
#define AUDIT_ANOM_ABEND 1701 /* Process ended abnormally */
+#define AUDIT_ANOM_LINK 1702 /* Suspicious use of file links */
#define AUDIT_INTEGRITY_DATA 1800 /* Data integrity verification */
#define AUDIT_INTEGRITY_METADATA 1801 /* Metadata integrity verification */
#define AUDIT_INTEGRITY_STATUS 1802 /* Integrity enable status */
@@ -687,6 +688,8 @@ extern void audit_log_d_path(struct audit_buffer *ab,
const struct path *path);
extern void audit_log_key(struct audit_buffer *ab,
char *key);
+extern void audit_log_link_denied(const char *operation,
+ struct path *link);
extern void audit_log_lost(const char *message);
#ifdef CONFIG_SECURITY
extern void audit_log_secctx(struct audit_buffer *ab, u32 secid);
@@ -716,6 +719,7 @@ extern int audit_enabled;
#define audit_log_untrustedstring(a,s) do { ; } while (0)
#define audit_log_d_path(b, p, d) do { ; } while (0)
#define audit_log_key(b, k) do { ; } while (0)
+#define audit_log_link_denied(o, l) do { ; } while (0)
#define audit_log_secctx(b,s) do { ; } while (0)
#define audit_enabled 0
#endif
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 489de625cd25..2a9a9abc9126 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -17,6 +17,7 @@
#include <linux/timer.h>
#include <linux/writeback.h>
#include <linux/atomic.h>
+#include <linux/sysctl.h>
struct page;
struct device;
@@ -123,7 +124,6 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
void bdi_start_background_writeback(struct backing_dev_info *bdi);
int bdi_writeback_thread(void *data);
int bdi_has_dirty_io(struct backing_dev_info *bdi);
-void bdi_arm_supers_timer(void);
void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi);
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2);
@@ -304,6 +304,8 @@ void clear_bdi_congested(struct backing_dev_info *bdi, int sync);
void set_bdi_congested(struct backing_dev_info *bdi, int sync);
long congestion_wait(int sync, long timeout);
long wait_iff_congested(struct zone *zone, int sync, long timeout);
+int pdflush_proc_obsolete(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
static inline bool bdi_cap_writeback_dirty(struct backing_dev_info *bdi)
{
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 3c80885fa829..d323a4b4143c 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -89,6 +89,12 @@
#define BCMA_CC_CHIPST_4313_OTP_PRESENT 2
#define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2
#define BCMA_CC_CHIPST_4331_OTP_PRESENT 4
+#define BCMA_CC_CHIPST_43228_ILP_DIV_EN 0x00000001
+#define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002
+#define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004
+#define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008
+#define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010
+#define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020
#define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */
#define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */
#define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 0edb65dd8edd..7b7ac9ccec7a 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -160,6 +160,7 @@ enum rq_flag_bits {
__REQ_FLUSH_SEQ, /* request for flush sequence */
__REQ_IO_STAT, /* account I/O stat */
__REQ_MIXED_MERGE, /* merge of different types, fail separately */
+ __REQ_KERNEL, /* direct IO to kernel pages */
__REQ_NR_BITS, /* stops here */
};
@@ -201,5 +202,6 @@ enum rq_flag_bits {
#define REQ_IO_STAT (1 << __REQ_IO_STAT)
#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
#define REQ_SECURE (1 << __REQ_SECURE)
+#define REQ_KERNEL (1 << __REQ_KERNEL)
#endif /* __LINUX_BLK_TYPES_H */
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 07954b05b86c..4a2ab7c85393 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -46,16 +46,23 @@ struct blkcg_gq;
struct request;
typedef void (rq_end_io_fn)(struct request *, int);
+#define BLK_RL_SYNCFULL (1U << 0)
+#define BLK_RL_ASYNCFULL (1U << 1)
+
struct request_list {
+ struct request_queue *q; /* the queue this rl belongs to */
+#ifdef CONFIG_BLK_CGROUP
+ struct blkcg_gq *blkg; /* blkg this request pool belongs to */
+#endif
/*
* count[], starved[], and wait[] are indexed by
* BLK_RW_SYNC/BLK_RW_ASYNC
*/
- int count[2];
- int starved[2];
- int elvpriv;
- mempool_t *rq_pool;
- wait_queue_head_t wait[2];
+ int count[2];
+ int starved[2];
+ mempool_t *rq_pool;
+ wait_queue_head_t wait[2];
+ unsigned int flags;
};
/*
@@ -138,6 +145,7 @@ struct request {
struct hd_struct *part;
unsigned long start_time;
#ifdef CONFIG_BLK_CGROUP
+ struct request_list *rl; /* rl this rq is alloced from */
unsigned long long start_time_ns;
unsigned long long io_start_time_ns; /* when passed to hardware */
#endif
@@ -282,11 +290,16 @@ struct request_queue {
struct list_head queue_head;
struct request *last_merge;
struct elevator_queue *elevator;
+ int nr_rqs[2]; /* # allocated [a]sync rqs */
+ int nr_rqs_elvpriv; /* # allocated rqs w/ elvpriv */
/*
- * the queue request freelist, one for reads and one for writes
+ * If blkcg is not used, @q->root_rl serves all requests. If blkcg
+ * is used, root blkg allocates from @q->root_rl and all other
+ * blkgs from their own blkg->rl. Which one to use should be
+ * determined using bio_request_list().
*/
- struct request_list rq;
+ struct request_list root_rl;
request_fn_proc *request_fn;
make_request_fn *make_request_fn;
@@ -561,27 +574,25 @@ static inline bool rq_is_sync(struct request *rq)
return rw_is_sync(rq->cmd_flags);
}
-static inline int blk_queue_full(struct request_queue *q, int sync)
+static inline bool blk_rl_full(struct request_list *rl, bool sync)
{
- if (sync)
- return test_bit(QUEUE_FLAG_SYNCFULL, &q->queue_flags);
- return test_bit(QUEUE_FLAG_ASYNCFULL, &q->queue_flags);
+ unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+ return rl->flags & flag;
}
-static inline void blk_set_queue_full(struct request_queue *q, int sync)
+static inline void blk_set_rl_full(struct request_list *rl, bool sync)
{
- if (sync)
- queue_flag_set(QUEUE_FLAG_SYNCFULL, q);
- else
- queue_flag_set(QUEUE_FLAG_ASYNCFULL, q);
+ unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+ rl->flags |= flag;
}
-static inline void blk_clear_queue_full(struct request_queue *q, int sync)
+static inline void blk_clear_rl_full(struct request_list *rl, bool sync)
{
- if (sync)
- queue_flag_clear(QUEUE_FLAG_SYNCFULL, q);
- else
- queue_flag_clear(QUEUE_FLAG_ASYNCFULL, q);
+ unsigned int flag = sync ? BLK_RL_SYNCFULL : BLK_RL_ASYNCFULL;
+
+ rl->flags &= ~flag;
}
@@ -590,7 +601,7 @@ static inline void blk_clear_queue_full(struct request_queue *q, int sync)
* it already be started by driver.
*/
#define RQ_NOMERGE_FLAGS \
- (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA)
+ (REQ_NOMERGE | REQ_STARTED | REQ_SOFTBARRIER | REQ_FLUSH | REQ_FUA | REQ_DISCARD)
#define rq_mergeable(rq) \
(!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && \
(((rq)->cmd_flags & REQ_DISCARD) || \
@@ -883,6 +894,8 @@ extern void blk_queue_flush_queueable(struct request_queue *q, bool queueable);
extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
+extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
+ struct scatterlist *sglist);
extern void blk_dump_rq_flags(struct request *, char *);
extern long nr_blockdev_pages(void);
@@ -911,11 +924,15 @@ struct blk_plug {
};
#define BLK_MAX_REQUEST_COUNT 16
+struct blk_plug_cb;
+typedef void (*blk_plug_cb_fn)(struct blk_plug_cb *, bool);
struct blk_plug_cb {
struct list_head list;
- void (*callback)(struct blk_plug_cb *);
+ blk_plug_cb_fn callback;
+ void *data;
};
-
+extern struct blk_plug_cb *blk_check_plugged(blk_plug_cb_fn unplug,
+ void *data, int size);
extern void blk_start_plug(struct blk_plug *);
extern void blk_finish_plug(struct blk_plug *);
extern void blk_flush_plug_list(struct blk_plug *, bool);
@@ -1124,6 +1141,16 @@ static inline int queue_limit_discard_alignment(struct queue_limits *lim, sector
& (lim->discard_granularity - 1);
}
+static inline int bdev_discard_alignment(struct block_device *bdev)
+{
+ struct request_queue *q = bdev_get_queue(bdev);
+
+ if (bdev != bdev->bd_contains)
+ return bdev->bd_part->discard_alignment;
+
+ return q->limits.discard_alignment;
+}
+
static inline unsigned int queue_discard_zeroes_data(struct request_queue *q)
{
if (q->limits.max_discard_sectors && q->limits.discard_zeroes_data == 1)
diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h
index faf8a45af210..a8519446c111 100644
--- a/include/linux/blkpg.h
+++ b/include/linux/blkpg.h
@@ -40,6 +40,7 @@ struct blkpg_ioctl_arg {
/* The subfunctions (for the op field) */
#define BLKPG_ADD_PARTITION 1
#define BLKPG_DEL_PARTITION 2
+#define BLKPG_RESIZE_PARTITION 3
/* Sizes of name fields. Unused at present. */
#define BLKPG_DEVNAMELTH 64
diff --git a/include/linux/bsg-lib.h b/include/linux/bsg-lib.h
index f55ab8cdc106..4d0fb3df2f4a 100644
--- a/include/linux/bsg-lib.h
+++ b/include/linux/bsg-lib.h
@@ -67,7 +67,6 @@ void bsg_job_done(struct bsg_job *job, int result,
int bsg_setup_queue(struct device *dev, struct request_queue *q, char *name,
bsg_job_fn *job_fn, int dd_job_size);
void bsg_request_fn(struct request_queue *q);
-void bsg_remove_queue(struct request_queue *q);
void bsg_goose_queue(struct request_queue *q);
#endif
diff --git a/include/linux/can.h b/include/linux/can.h
index 018055efc034..e52958d7c2d1 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -74,20 +74,21 @@ struct can_frame {
/*
* defined bits for canfd_frame.flags
*
- * As the default for CAN FD should be to support the high data rate in the
- * payload section of the frame (HDR) and to support up to 64 byte in the
- * data section (EDL) the bits are only set in the non-default case.
- * Btw. as long as there's no real implementation for CAN FD network driver
- * these bits are only preliminary.
+ * The use of struct canfd_frame implies the Extended Data Length (EDL) bit to
+ * be set in the CAN frame bitstream on the wire. The EDL bit switch turns
+ * the CAN controllers bitstream processor into the CAN FD mode which creates
+ * two new options within the CAN FD frame specification:
*
- * RX: NOHDR/NOEDL - info about received CAN FD frame
- * ESI - bit from originating CAN controller
- * TX: NOHDR/NOEDL - control per-frame settings if supported by CAN controller
- * ESI - bit is set by local CAN controller
+ * Bit Rate Switch - to indicate a second bitrate is/was used for the payload
+ * Error State Indicator - represents the error state of the transmitting node
+ *
+ * As the CANFD_ESI bit is internally generated by the transmitting CAN
+ * controller only the CANFD_BRS bit is relevant for real CAN controllers when
+ * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make
+ * sense for virtual CAN interfaces to test applications with echoed frames.
*/
-#define CANFD_NOHDR 0x01 /* frame without high data rate */
-#define CANFD_NOEDL 0x02 /* frame without extended data length */
-#define CANFD_ESI 0x04 /* error state indicator */
+#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */
+#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */
/**
* struct canfd_frame - CAN flexible data rate frame structure
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 0bd390ce98b2..dfae957398c3 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -31,7 +31,7 @@ SUBSYS(cpuacct)
/* */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
SUBSYS(mem_cgroup)
#endif
@@ -72,3 +72,9 @@ SUBSYS(net_prio)
#endif
/* */
+
+#ifdef CONFIG_CGROUP_HUGETLB
+SUBSYS(hugetlb)
+#endif
+
+/* */
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index 51a90b7f2d60..ef658147e4e8 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -22,7 +22,7 @@ extern int sysctl_extfrag_handler(struct ctl_table *table, int write,
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
- bool sync);
+ bool sync, bool *contended);
extern int compact_pgdat(pg_data_t *pgdat, int order);
extern unsigned long compaction_suitable(struct zone *zone, int order);
@@ -58,13 +58,13 @@ static inline bool compaction_deferred(struct zone *zone, int order)
if (++zone->compact_considered > defer_limit)
zone->compact_considered = defer_limit;
- return zone->compact_considered < (1UL << zone->compact_defer_shift);
+ return zone->compact_considered < defer_limit;
}
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
return COMPACT_CONTINUE;
}
@@ -85,7 +85,7 @@ static inline void defer_compaction(struct zone *zone, int order)
static inline bool compaction_deferred(struct zone *zone, int order)
{
- return 1;
+ return true;
}
#endif /* CONFIG_COMPACTION */
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 2f4079175afb..934bc34d5f99 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -49,6 +49,13 @@
#endif
#endif
+#if __GNUC_MINOR__ >= 6
+/*
+ * Tell the optimizer that something else uses this function or variable.
+ */
+#define __visible __attribute__((externally_visible))
+#endif
+
#if __GNUC_MINOR__ > 0
#define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
#endif
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 923d093c9cea..f430e4162f41 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -278,6 +278,10 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
# define __section(S) __attribute__ ((__section__(#S)))
#endif
+#ifndef __visible
+#define __visible
+#endif
+
/* Are two types/vars the same type (ignoring qualifiers)? */
#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 040b13b5c14a..279b1eaa8b73 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -194,6 +194,10 @@ static inline int cpuidle_play_dead(void) {return -ENODEV; }
#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a);
+#else
+static inline void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a)
+{
+}
#endif
/******************************
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index caa34e50537e..59200795482e 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -206,6 +206,8 @@ struct dentry_operations {
#define DCACHE_MANAGED_DENTRY \
(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
+#define DCACHE_DENTRY_KILLED 0x100000
+
extern seqlock_t rename_lock;
static inline int dname_external(struct dentry *dentry)
diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h
index f50d4058c5fb..c12d452cb40d 100644
--- a/include/linux/dvb/frontend.h
+++ b/include/linux/dvb/frontend.h
@@ -62,6 +62,7 @@ typedef enum fe_caps {
FE_CAN_8VSB = 0x200000,
FE_CAN_16VSB = 0x400000,
FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */
+ FE_CAN_MULTISTREAM = 0x4000000, /* frontend supports multistream filtering */
FE_CAN_TURBO_FEC = 0x8000000, /* frontend supports "turbo fec modulation" */
FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */
FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */
@@ -121,16 +122,27 @@ typedef enum fe_sec_mini_cmd {
} fe_sec_mini_cmd_t;
+/**
+ * enum fe_status - enumerates the possible frontend status
+ * @FE_HAS_SIGNAL: found something above the noise level
+ * @FE_HAS_CARRIER: found a DVB signal
+ * @FE_HAS_VITERBI: FEC is stable
+ * @FE_HAS_SYNC: found sync bytes
+ * @FE_HAS_LOCK: everything's working
+ * @FE_TIMEDOUT: no lock within the last ~2 seconds
+ * @FE_REINIT: frontend was reinitialized, application is recommended
+ * to reset DiSEqC, tone and parameters
+ */
+
typedef enum fe_status {
- FE_HAS_SIGNAL = 0x01, /* found something above the noise level */
- FE_HAS_CARRIER = 0x02, /* found a DVB signal */
- FE_HAS_VITERBI = 0x04, /* FEC is stable */
- FE_HAS_SYNC = 0x08, /* found sync bytes */
- FE_HAS_LOCK = 0x10, /* everything's working... */
- FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */
- FE_REINIT = 0x40 /* frontend was reinitialized, */
-} fe_status_t; /* application is recommended to reset */
- /* DiSEqC, tone and parameters */
+ FE_HAS_SIGNAL = 0x01,
+ FE_HAS_CARRIER = 0x02,
+ FE_HAS_VITERBI = 0x04,
+ FE_HAS_SYNC = 0x08,
+ FE_HAS_LOCK = 0x10,
+ FE_TIMEDOUT = 0x20,
+ FE_REINIT = 0x40,
+} fe_status_t;
typedef enum fe_spectral_inversion {
INVERSION_OFF,
@@ -152,6 +164,7 @@ typedef enum fe_code_rate {
FEC_AUTO,
FEC_3_5,
FEC_9_10,
+ FEC_2_5,
} fe_code_rate_t;
@@ -169,6 +182,7 @@ typedef enum fe_modulation {
APSK_16,
APSK_32,
DQPSK,
+ QAM_4_NR,
} fe_modulation_t;
typedef enum fe_transmit_mode {
@@ -179,6 +193,8 @@ typedef enum fe_transmit_mode {
TRANSMISSION_MODE_1K,
TRANSMISSION_MODE_16K,
TRANSMISSION_MODE_32K,
+ TRANSMISSION_MODE_C1,
+ TRANSMISSION_MODE_C3780,
} fe_transmit_mode_t;
#if defined(__DVB_CORE__) || !defined (__KERNEL__)
@@ -202,6 +218,9 @@ typedef enum fe_guard_interval {
GUARD_INTERVAL_1_128,
GUARD_INTERVAL_19_128,
GUARD_INTERVAL_19_256,
+ GUARD_INTERVAL_PN420,
+ GUARD_INTERVAL_PN595,
+ GUARD_INTERVAL_PN945,
} fe_guard_interval_t;
@@ -213,6 +232,12 @@ typedef enum fe_hierarchy {
HIERARCHY_AUTO
} fe_hierarchy_t;
+enum fe_interleaving {
+ INTERLEAVING_NONE,
+ INTERLEAVING_AUTO,
+ INTERLEAVING_240,
+ INTERLEAVING_720,
+};
#if defined(__DVB_CORE__) || !defined (__KERNEL__)
struct dvb_qpsk_parameters {
@@ -314,9 +339,9 @@ struct dvb_frontend_event {
#define DTV_ISDBT_LAYER_ENABLED 41
-#define DTV_ISDBS_TS_ID 42
-
-#define DTV_DVBT2_PLP_ID 43
+#define DTV_STREAM_ID 42
+#define DTV_ISDBS_TS_ID_LEGACY DTV_STREAM_ID
+#define DTV_DVBT2_PLP_ID_LEGACY 43
#define DTV_ENUM_DELSYS 44
@@ -337,7 +362,10 @@ struct dvb_frontend_event {
#define DTV_ATSCMH_SCCC_CODE_MODE_C 58
#define DTV_ATSCMH_SCCC_CODE_MODE_D 59
-#define DTV_MAX_COMMAND DTV_ATSCMH_SCCC_CODE_MODE_D
+#define DTV_INTERLEAVING 60
+#define DTV_LNA 61
+
+#define DTV_MAX_COMMAND DTV_LNA
typedef enum fe_pilot {
PILOT_ON,
@@ -366,7 +394,7 @@ typedef enum fe_delivery_system {
SYS_ISDBC,
SYS_ATSC,
SYS_ATSCMH,
- SYS_DMBTH,
+ SYS_DTMB,
SYS_CMMB,
SYS_DAB,
SYS_DVBT2,
@@ -374,8 +402,9 @@ typedef enum fe_delivery_system {
SYS_DVBC_ANNEX_C,
} fe_delivery_system_t;
-
+/* backward compatibility */
#define SYS_DVBC_ANNEX_AC SYS_DVBC_ANNEX_A
+#define SYS_DMBTH SYS_DTMB /* DMB-TH is legacy name, use DTMB instead */
/* ATSC-MH */
@@ -409,6 +438,8 @@ enum atscmh_rs_code_mode {
ATSCMH_RSCODE_RES = 3,
};
+#define NO_STREAM_ID_FILTER (~0U)
+#define LNA_AUTO (~0U)
struct dtv_cmds_h {
char *name; /* A display name for debugging purposes */
diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h
index 43d9e8d462d4..20e5eac2ffd3 100644
--- a/include/linux/dvb/version.h
+++ b/include/linux/dvb/version.h
@@ -24,6 +24,6 @@
#define _DVBVERSION_H_
#define DVB_API_VERSION 5
-#define DVB_API_VERSION_MINOR 6
+#define DVB_API_VERSION_MINOR 8
#endif /*_DVBVERSION_H_*/
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 103adc6d7e3a..ec45ccd8708a 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -503,6 +503,8 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
+extern unsigned long efi_get_time(void);
+extern int efi_set_rtc_mmss(unsigned long nowtime);
extern void efi_reserve_boot_services(void);
extern struct efi_memory_map memmap;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b178f9e91e23..aa110476a95b 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -165,6 +165,8 @@ struct inodes_stat_t {
#define READ 0
#define WRITE RW_MASK
#define READA RWA_MASK
+#define KERNEL_READ (READ|REQ_KERNEL)
+#define KERNEL_WRITE (WRITE|REQ_KERNEL)
#define READ_SYNC (READ | REQ_SYNC)
#define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE)
@@ -412,6 +414,7 @@ struct inodes_stat_t {
#include <linux/shrinker.h>
#include <linux/migrate_mode.h>
#include <linux/uidgid.h>
+#include <linux/lockdep.h>
#include <asm/byteorder.h>
@@ -427,6 +430,7 @@ struct kstatfs;
struct vm_area_struct;
struct vfsmount;
struct cred;
+struct swap_info_struct;
extern void __init inode_init(void);
extern void __init inode_init_early(void);
@@ -437,6 +441,8 @@ extern unsigned long get_max_files(void);
extern int sysctl_nr_open;
extern struct inodes_stat_t inodes_stat;
extern int leases_enable, lease_break_time;
+extern int sysctl_protected_symlinks;
+extern int sysctl_protected_hardlinks;
struct buffer_head;
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
@@ -636,6 +642,11 @@ struct address_space_operations {
int (*is_partially_uptodate) (struct page *, read_descriptor_t *,
unsigned long);
int (*error_remove_page)(struct address_space *, struct page *);
+
+ /* swapfile support */
+ int (*swap_activate)(struct swap_info_struct *sis, struct file *file,
+ sector_t *span);
+ void (*swap_deactivate)(struct file *file);
};
extern const struct address_space_operations empty_aops;
@@ -1154,7 +1165,6 @@ struct lock_manager_operations {
int (*lm_compare_owner)(struct file_lock *, struct file_lock *);
void (*lm_notify)(struct file_lock *); /* unblock callback */
int (*lm_grant)(struct file_lock *, struct file_lock *, int);
- void (*lm_release_private)(struct file_lock *);
void (*lm_break)(struct file_lock *);
int (*lm_change)(struct file_lock **, int);
};
@@ -1438,6 +1448,8 @@ extern void f_delown(struct file *filp);
extern pid_t f_getown(struct file *filp);
extern int send_sigurg(struct fown_struct *fown);
+struct mm_struct;
+
/*
* Umount options
*/
@@ -1451,10 +1463,34 @@ extern int send_sigurg(struct fown_struct *fown);
extern struct list_head super_blocks;
extern spinlock_t sb_lock;
+/* Possible states of 'frozen' field */
+enum {
+ SB_UNFROZEN = 0, /* FS is unfrozen */
+ SB_FREEZE_WRITE = 1, /* Writes, dir ops, ioctls frozen */
+ SB_FREEZE_PAGEFAULT = 2, /* Page faults stopped as well */
+ SB_FREEZE_FS = 3, /* For internal FS use (e.g. to stop
+ * internal threads if needed) */
+ SB_FREEZE_COMPLETE = 4, /* ->freeze_fs finished successfully */
+};
+
+#define SB_FREEZE_LEVELS (SB_FREEZE_COMPLETE - 1)
+
+struct sb_writers {
+ /* Counters for counting writers at each level */
+ struct percpu_counter counter[SB_FREEZE_LEVELS];
+ wait_queue_head_t wait; /* queue for waiting for
+ writers / faults to finish */
+ int frozen; /* Is sb frozen? */
+ wait_queue_head_t wait_unfrozen; /* queue for waiting for
+ sb to be thawed */
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct lockdep_map lock_map[SB_FREEZE_LEVELS];
+#endif
+};
+
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
- unsigned char s_dirt;
unsigned char s_blocksize_bits;
unsigned long s_blocksize;
loff_t s_maxbytes; /* Max file size */
@@ -1498,8 +1534,7 @@ struct super_block {
struct hlist_node s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
- int s_frozen;
- wait_queue_head_t s_wait_unfrozen;
+ struct sb_writers s_writers;
char s_id[32]; /* Informational name */
u8 s_uuid[16]; /* UUID */
@@ -1554,14 +1589,117 @@ extern struct timespec current_fs_time(struct super_block *sb);
/*
* Snapshotting support.
*/
-enum {
- SB_UNFROZEN = 0,
- SB_FREEZE_WRITE = 1,
- SB_FREEZE_TRANS = 2,
-};
-#define vfs_check_frozen(sb, level) \
- wait_event((sb)->s_wait_unfrozen, ((sb)->s_frozen < (level)))
+void __sb_end_write(struct super_block *sb, int level);
+int __sb_start_write(struct super_block *sb, int level, bool wait);
+
+/**
+ * sb_end_write - drop write access to a superblock
+ * @sb: the super we wrote to
+ *
+ * Decrement number of writers to the filesystem. Wake up possible waiters
+ * wanting to freeze the filesystem.
+ */
+static inline void sb_end_write(struct super_block *sb)
+{
+ __sb_end_write(sb, SB_FREEZE_WRITE);
+}
+
+/**
+ * sb_end_pagefault - drop write access to a superblock from a page fault
+ * @sb: the super we wrote to
+ *
+ * Decrement number of processes handling write page fault to the filesystem.
+ * Wake up possible waiters wanting to freeze the filesystem.
+ */
+static inline void sb_end_pagefault(struct super_block *sb)
+{
+ __sb_end_write(sb, SB_FREEZE_PAGEFAULT);
+}
+
+/**
+ * sb_end_intwrite - drop write access to a superblock for internal fs purposes
+ * @sb: the super we wrote to
+ *
+ * Decrement fs-internal number of writers to the filesystem. Wake up possible
+ * waiters wanting to freeze the filesystem.
+ */
+static inline void sb_end_intwrite(struct super_block *sb)
+{
+ __sb_end_write(sb, SB_FREEZE_FS);
+}
+
+/**
+ * sb_start_write - get write access to a superblock
+ * @sb: the super we write to
+ *
+ * When a process wants to write data or metadata to a file system (i.e. dirty
+ * a page or an inode), it should embed the operation in a sb_start_write() -
+ * sb_end_write() pair to get exclusion against file system freezing. This
+ * function increments number of writers preventing freezing. If the file
+ * system is already frozen, the function waits until the file system is
+ * thawed.
+ *
+ * Since freeze protection behaves as a lock, users have to preserve
+ * ordering of freeze protection and other filesystem locks. Generally,
+ * freeze protection should be the outermost lock. In particular, we have:
+ *
+ * sb_start_write
+ * -> i_mutex (write path, truncate, directory ops, ...)
+ * -> s_umount (freeze_super, thaw_super)
+ */
+static inline void sb_start_write(struct super_block *sb)
+{
+ __sb_start_write(sb, SB_FREEZE_WRITE, true);
+}
+
+static inline int sb_start_write_trylock(struct super_block *sb)
+{
+ return __sb_start_write(sb, SB_FREEZE_WRITE, false);
+}
+
+/**
+ * sb_start_pagefault - get write access to a superblock from a page fault
+ * @sb: the super we write to
+ *
+ * When a process starts handling write page fault, it should embed the
+ * operation into sb_start_pagefault() - sb_end_pagefault() pair to get
+ * exclusion against file system freezing. This is needed since the page fault
+ * is going to dirty a page. This function increments number of running page
+ * faults preventing freezing. If the file system is already frozen, the
+ * function waits until the file system is thawed.
+ *
+ * Since page fault freeze protection behaves as a lock, users have to preserve
+ * ordering of freeze protection and other filesystem locks. It is advised to
+ * put sb_start_pagefault() close to mmap_sem in lock ordering. Page fault
+ * handling code implies lock dependency:
+ *
+ * mmap_sem
+ * -> sb_start_pagefault
+ */
+static inline void sb_start_pagefault(struct super_block *sb)
+{
+ __sb_start_write(sb, SB_FREEZE_PAGEFAULT, true);
+}
+
+/*
+ * sb_start_intwrite - get write access to a superblock for internal fs purposes
+ * @sb: the super we write to
+ *
+ * This is the third level of protection against filesystem freezing. It is
+ * free for use by a filesystem. The only requirement is that it must rank
+ * below sb_start_pagefault.
+ *
+ * For example filesystem can call sb_start_intwrite() when starting a
+ * transaction which somewhat eases handling of freezing for internal sources
+ * of filesystem changes (internal fs threads, discarding preallocation on file
+ * close, etc.).
+ */
+static inline void sb_start_intwrite(struct super_block *sb)
+{
+ __sb_start_write(sb, SB_FREEZE_FS, true);
+}
+
extern bool inode_owner_or_capable(const struct inode *inode);
@@ -1722,7 +1860,6 @@ struct super_operations {
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -1885,6 +2022,7 @@ struct file_system_type {
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key s_vfs_rename_key;
+ struct lock_class_key s_writers_key[SB_FREEZE_LEVELS];
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
@@ -2257,7 +2395,6 @@ extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end,
int datasync);
extern int vfs_fsync(struct file *file, int datasync);
extern int generic_write_sync(struct file *file, loff_t pos, loff_t count);
-extern void sync_supers(void);
extern void emergency_sync(void);
extern void emergency_remount(void);
#ifdef CONFIG_BLOCK
@@ -2327,9 +2464,6 @@ static inline void i_readcount_inc(struct inode *inode)
}
#endif
extern int do_pipe_flags(int *, int);
-extern struct file *create_read_pipe(struct file *f, int flags);
-extern struct file *create_write_pipe(int flags);
-extern void free_write_pipe(struct file *);
extern int kernel_read(struct file *, loff_t, char *, unsigned long);
extern struct file * open_exec(const char *);
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index af961d6f7ab1..642928cf57b4 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -306,9 +306,10 @@ extern void *perf_trace_buf_prepare(int size, unsigned short type,
static inline void
perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr,
- u64 count, struct pt_regs *regs, void *head)
+ u64 count, struct pt_regs *regs, void *head,
+ struct task_struct *task)
{
- perf_tp_event(addr, count, raw_data, size, regs, head, rctx);
+ perf_tp_event(addr, count, raw_data, size, regs, head, rctx, task);
}
#endif
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 9303348965fb..d8c713e148e3 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -57,6 +57,9 @@
*
* 7.19
* - add FUSE_FALLOCATE
+ *
+ * 7.20
+ * - add FUSE_AUTO_INVAL_DATA
*/
#ifndef _LINUX_FUSE_H
@@ -88,7 +91,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 19
+#define FUSE_KERNEL_MINOR_VERSION 20
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -163,10 +166,19 @@ struct fuse_file_lock {
/**
* INIT request/reply flags
*
+ * FUSE_ASYNC_READ: asynchronous read requests
* FUSE_POSIX_LOCKS: remote locking for POSIX file locks
+ * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported)
+ * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem
* FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_SPLICE_WRITE: kernel supports splice write on the device
+ * FUSE_SPLICE_MOVE: kernel supports splice move on the device
+ * FUSE_SPLICE_READ: kernel supports splice read on the device
* FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
+ * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories
+ * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -175,7 +187,12 @@ struct fuse_file_lock {
#define FUSE_EXPORT_SUPPORT (1 << 4)
#define FUSE_BIG_WRITES (1 << 5)
#define FUSE_DONT_MASK (1 << 6)
+#define FUSE_SPLICE_WRITE (1 << 7)
+#define FUSE_SPLICE_MOVE (1 << 8)
+#define FUSE_SPLICE_READ (1 << 9)
#define FUSE_FLOCK_LOCKS (1 << 10)
+#define FUSE_HAS_IOCTL_DIR (1 << 11)
+#define FUSE_AUTO_INVAL_DATA (1 << 12)
/**
* CUSE INIT request/reply flags
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ae0aaa9d42fa..4f440b3e89fe 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -97,7 +97,13 @@ struct partition_meta_info {
struct hd_struct {
sector_t start_sect;
+ /*
+ * nr_sects is protected by sequence counter. One might extend a
+ * partition while IO is happening to it and update of nr_sects
+ * can be non-atomic on 32bit machines with 64bit sector_t.
+ */
sector_t nr_sects;
+ seqcount_t nr_sects_seq;
sector_t alignment_offset;
unsigned int discard_alignment;
struct device __dev;
@@ -647,6 +653,57 @@ static inline void hd_struct_put(struct hd_struct *part)
__delete_partition(part);
}
+/*
+ * Any access of part->nr_sects which is not protected by partition
+ * bd_mutex or gendisk bdev bd_mutex, should be done using this
+ * accessor function.
+ *
+ * Code written along the lines of i_size_read() and i_size_write().
+ * CONFIG_PREEMPT case optimizes the case of UP kernel with preemption
+ * on.
+ */
+static inline sector_t part_nr_sects_read(struct hd_struct *part)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP)
+ sector_t nr_sects;
+ unsigned seq;
+ do {
+ seq = read_seqcount_begin(&part->nr_sects_seq);
+ nr_sects = part->nr_sects;
+ } while (read_seqcount_retry(&part->nr_sects_seq, seq));
+ return nr_sects;
+#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT)
+ sector_t nr_sects;
+
+ preempt_disable();
+ nr_sects = part->nr_sects;
+ preempt_enable();
+ return nr_sects;
+#else
+ return part->nr_sects;
+#endif
+}
+
+/*
+ * Should be called with mutex lock held (typically bd_mutex) of partition
+ * to provide mutual exlusion among writers otherwise seqcount might be
+ * left in wrong state leaving the readers spinning infinitely.
+ */
+static inline void part_nr_sects_write(struct hd_struct *part, sector_t size)
+{
+#if BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_SMP)
+ write_seqcount_begin(&part->nr_sects_seq);
+ part->nr_sects = size;
+ write_seqcount_end(&part->nr_sects_seq);
+#elif BITS_PER_LONG==32 && defined(CONFIG_LBDAF) && defined(CONFIG_PREEMPT)
+ preempt_disable();
+ part->nr_sects = size;
+ preempt_enable();
+#else
+ part->nr_sects = size;
+#endif
+}
+
#else /* CONFIG_BLOCK */
static inline void printk_all_partitions(void) { }
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 1e49be49d324..4883f393f50a 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -23,6 +23,7 @@ struct vm_area_struct;
#define ___GFP_REPEAT 0x400u
#define ___GFP_NOFAIL 0x800u
#define ___GFP_NORETRY 0x1000u
+#define ___GFP_MEMALLOC 0x2000u
#define ___GFP_COMP 0x4000u
#define ___GFP_ZERO 0x8000u
#define ___GFP_NOMEMALLOC 0x10000u
@@ -76,9 +77,14 @@ struct vm_area_struct;
#define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* See above */
#define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* See above */
#define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* See above */
+#define __GFP_MEMALLOC ((__force gfp_t)___GFP_MEMALLOC)/* Allow access to emergency reserves */
#define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* Add compound page metadata */
#define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* Return zeroed page on success */
-#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves */
+#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* Don't use emergency reserves.
+ * This takes precedence over the
+ * __GFP_MEMALLOC flag if both are
+ * set
+ */
#define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL) /* Enforce hardwall cpuset memory allocs */
#define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE)/* No fallback, no policies */
#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */
@@ -129,7 +135,7 @@ struct vm_area_struct;
/* Control page allocator reclaim behavior */
#define GFP_RECLAIM_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS|\
__GFP_NOWARN|__GFP_REPEAT|__GFP_NOFAIL|\
- __GFP_NORETRY|__GFP_NOMEMALLOC)
+ __GFP_NORETRY|__GFP_MEMALLOC|__GFP_NOMEMALLOC)
/* Control slab gfp mask during early boot */
#define GFP_BOOT_MASK (__GFP_BITS_MASK & ~(__GFP_WAIT|__GFP_IO|__GFP_FS))
@@ -379,6 +385,9 @@ void drain_local_pages(void *dummy);
*/
extern gfp_t gfp_allowed_mask;
+/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */
+bool gfp_pfmemalloc_allowed(gfp_t gfp_mask);
+
extern void pm_restrict_gfp_mask(void);
extern void pm_restore_gfp_mask(void);
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index bb7f30971858..305f23cd7cff 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -22,7 +22,7 @@
*
* - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024)
* - bit 26 is the NMI_MASK
- * - bit 28 is the PREEMPT_ACTIVE flag
+ * - bit 27 is the PREEMPT_ACTIVE flag
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 774fa47b3b5b..ef788b5b4a35 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -39,10 +39,17 @@ extern unsigned long totalhigh_pages;
void kmap_flush_unused(void);
+struct page *kmap_to_page(void *addr);
+
#else /* CONFIG_HIGHMEM */
static inline unsigned int nr_free_highpages(void) { return 0; }
+static inline struct page *kmap_to_page(void *addr)
+{
+ return virt_to_page(addr);
+}
+
#define totalhigh_pages 0UL
#ifndef ARCH_HAS_KMAP
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index d5d6bbe2259e..225164842ab6 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -4,9 +4,11 @@
#include <linux/mm_types.h>
#include <linux/fs.h>
#include <linux/hugetlb_inline.h>
+#include <linux/cgroup.h>
struct ctl_table;
struct user_struct;
+struct mmu_gather;
#ifdef CONFIG_HUGETLB_PAGE
@@ -20,6 +22,11 @@ struct hugepage_subpool {
long max_hpages, used_hpages;
};
+extern spinlock_t hugetlb_lock;
+extern int hugetlb_max_hstate __read_mostly;
+#define for_each_hstate(h) \
+ for ((h) = hstates; (h) < &hstates[hugetlb_max_hstate]; (h)++)
+
struct hugepage_subpool *hugepage_new_subpool(long nr_blocks);
void hugepage_put_subpool(struct hugepage_subpool *spool);
@@ -40,9 +47,14 @@ int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *,
struct page **, struct vm_area_struct **,
unsigned long *, int *, int, unsigned int flags);
void unmap_hugepage_range(struct vm_area_struct *,
- unsigned long, unsigned long, struct page *);
-void __unmap_hugepage_range(struct vm_area_struct *,
- unsigned long, unsigned long, struct page *);
+ unsigned long, unsigned long, struct page *);
+void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+ struct vm_area_struct *vma,
+ unsigned long start, unsigned long end,
+ struct page *ref_page);
+void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end,
+ struct page *ref_page);
int hugetlb_prefault(struct address_space *, struct vm_area_struct *);
void hugetlb_report_meminfo(struct seq_file *);
int hugetlb_report_node_meminfo(int, char *);
@@ -98,7 +110,6 @@ static inline unsigned long hugetlb_total_pages(void)
#define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL)
#define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; })
#define hugetlb_prefault(mapping, vma) ({ BUG(); 0; })
-#define unmap_hugepage_range(vma, start, end, page) BUG()
static inline void hugetlb_report_meminfo(struct seq_file *m)
{
}
@@ -112,13 +123,31 @@ static inline void hugetlb_report_meminfo(struct seq_file *m)
#define hugetlb_free_pgd_range(tlb, addr, end, floor, ceiling) ({BUG(); 0; })
#define hugetlb_fault(mm, vma, addr, flags) ({ BUG(); 0; })
#define huge_pte_offset(mm, address) 0
-#define dequeue_hwpoisoned_huge_page(page) 0
+static inline int dequeue_hwpoisoned_huge_page(struct page *page)
+{
+ return 0;
+}
+
static inline void copy_huge_page(struct page *dst, struct page *src)
{
}
#define hugetlb_change_protection(vma, address, end, newprot)
+static inline void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, struct page *ref_page)
+{
+ BUG();
+}
+
+static inline void __unmap_hugepage_range(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, struct page *ref_page)
+{
+ BUG();
+}
+
#endif /* !CONFIG_HUGETLB_PAGE */
#define HUGETLB_ANON_FILE "anon_hugepage"
@@ -199,10 +228,15 @@ struct hstate {
unsigned long resv_huge_pages;
unsigned long surplus_huge_pages;
unsigned long nr_overcommit_huge_pages;
+ struct list_head hugepage_activelist;
struct list_head hugepage_freelists[MAX_NUMNODES];
unsigned int nr_huge_pages_node[MAX_NUMNODES];
unsigned int free_huge_pages_node[MAX_NUMNODES];
unsigned int surplus_huge_pages_node[MAX_NUMNODES];
+#ifdef CONFIG_CGROUP_HUGETLB
+ /* cgroup control files */
+ struct cftype cgroup_files[5];
+#endif
char name[HSTATE_NAME_LEN];
};
@@ -302,6 +336,11 @@ static inline unsigned hstate_index_to_shift(unsigned index)
return hstates[index].order + PAGE_SHIFT;
}
+static inline int hstate_index(struct hstate *h)
+{
+ return h - hstates;
+}
+
#else
struct hstate {};
#define alloc_huge_page_node(h, nid) NULL
@@ -320,6 +359,7 @@ static inline unsigned int pages_per_huge_page(struct hstate *h)
return 1;
}
#define hstate_index_to_shift(index) 0
+#define hstate_index(h) 0
#endif
#endif /* _LINUX_HUGETLB_H */
diff --git a/include/linux/hugetlb_cgroup.h b/include/linux/hugetlb_cgroup.h
new file mode 100644
index 000000000000..d73878c694b3
--- /dev/null
+++ b/include/linux/hugetlb_cgroup.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright IBM Corporation, 2012
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef _LINUX_HUGETLB_CGROUP_H
+#define _LINUX_HUGETLB_CGROUP_H
+
+#include <linux/res_counter.h>
+
+struct hugetlb_cgroup;
+/*
+ * Minimum page order trackable by hugetlb cgroup.
+ * At least 3 pages are necessary for all the tracking information.
+ */
+#define HUGETLB_CGROUP_MIN_ORDER 2
+
+#ifdef CONFIG_CGROUP_HUGETLB
+
+static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page)
+{
+ VM_BUG_ON(!PageHuge(page));
+
+ if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER)
+ return NULL;
+ return (struct hugetlb_cgroup *)page[2].lru.next;
+}
+
+static inline
+int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg)
+{
+ VM_BUG_ON(!PageHuge(page));
+
+ if (compound_order(page) < HUGETLB_CGROUP_MIN_ORDER)
+ return -1;
+ page[2].lru.next = (void *)h_cg;
+ return 0;
+}
+
+static inline bool hugetlb_cgroup_disabled(void)
+{
+ if (hugetlb_subsys.disabled)
+ return true;
+ return false;
+}
+
+extern int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup **ptr);
+extern void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg,
+ struct page *page);
+extern void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
+ struct page *page);
+extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg);
+extern int hugetlb_cgroup_file_init(int idx) __init;
+extern void hugetlb_cgroup_migrate(struct page *oldhpage,
+ struct page *newhpage);
+
+#else
+static inline struct hugetlb_cgroup *hugetlb_cgroup_from_page(struct page *page)
+{
+ return NULL;
+}
+
+static inline
+int set_hugetlb_cgroup(struct page *page, struct hugetlb_cgroup *h_cg)
+{
+ return 0;
+}
+
+static inline bool hugetlb_cgroup_disabled(void)
+{
+ return true;
+}
+
+static inline int
+hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup **ptr)
+{
+ return 0;
+}
+
+static inline void
+hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg,
+ struct page *page)
+{
+ return;
+}
+
+static inline void
+hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages, struct page *page)
+{
+ return;
+}
+
+static inline void
+hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg)
+{
+ return;
+}
+
+static inline int __init hugetlb_cgroup_file_init(int idx)
+{
+ return 0;
+}
+
+static inline void hugetlb_cgroup_migrate(struct page *oldhpage,
+ struct page *newhpage)
+{
+ return;
+}
+
+#endif /* CONFIG_MEM_RES_CTLR_HUGETLB */
+#endif
diff --git a/include/linux/i2c-pnx.h b/include/linux/i2c-pnx.h
index 1bc74afe7a35..49ed17fdf055 100644
--- a/include/linux/i2c-pnx.h
+++ b/include/linux/i2c-pnx.h
@@ -22,6 +22,7 @@ struct i2c_pnx_mif {
struct timer_list timer; /* Timeout */
u8 * buf; /* Data buffer */
int len; /* Length of data buffer */
+ int order; /* RX Bytes to order via TX */
};
struct i2c_pnx_algo_data {
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 6960fc1841a7..aa2e167e1ef4 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -96,21 +96,6 @@ static inline void team_netpoll_send_skb(struct team_port *port,
}
#endif
-static inline int team_dev_queue_xmit(struct team *team, struct team_port *port,
- struct sk_buff *skb)
-{
- BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
- sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
- skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping);
-
- skb->dev = port->dev;
- if (unlikely(netpoll_tx_running(port->dev))) {
- team_netpoll_send_skb(port, skb);
- return 0;
- }
- return dev_queue_xmit(skb);
-}
-
struct team_mode_ops {
int (*init)(struct team *team);
void (*exit)(struct team *team);
@@ -200,6 +185,21 @@ struct team {
long mode_priv[TEAM_MODE_PRIV_LONGS];
};
+static inline int team_dev_queue_xmit(struct team *team, struct team_port *port,
+ struct sk_buff *skb)
+{
+ BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
+ sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
+ skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping);
+
+ skb->dev = port->dev;
+ if (unlikely(netpoll_tx_running(team->dev))) {
+ team_netpoll_send_skb(port, skb);
+ return 0;
+ }
+ return dev_queue_xmit(skb);
+}
+
static inline struct hlist_head *team_port_index_hash(struct team *team,
int port_index)
{
diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h
index b76b4a87065e..be91f344d5fc 100644
--- a/include/linux/iio/frequency/adf4350.h
+++ b/include/linux/iio/frequency/adf4350.h
@@ -87,6 +87,8 @@
#define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */
#define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */
#define ADF4350_MAX_MODULUS 4095
+#define ADF4350_MAX_R_CNT 1023
+
/**
* struct adf4350_platform_data - platform specific information
diff --git a/include/linux/input/eeti_ts.h b/include/linux/input/eeti_ts.h
index f875b316249d..16625d799b6f 100644
--- a/include/linux/input/eeti_ts.h
+++ b/include/linux/input/eeti_ts.h
@@ -2,6 +2,7 @@
#define LINUX_INPUT_EETI_TS_H
struct eeti_ts_platform_data {
+ int irq_gpio;
unsigned int irq_active_high;
};
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index e68a8e53bb59..c5f856a040b9 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -42,7 +42,6 @@
*
* IRQF_DISABLED - keep irqs disabled when calling the action handler.
* DEPRECATED. This flag is a NOOP and scheduled to be removed
- * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator
* IRQF_SHARED - allow sharing the irq among several devices
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt
@@ -61,7 +60,6 @@
* resume time.
*/
#define IRQF_DISABLED 0x00000020
-#define IRQF_SAMPLE_RANDOM 0x00000040
#define IRQF_SHARED 0x00000080
#define IRQF_PROBE_SHARED 0x00000100
#define __IRQF_TIMER 0x00000200
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 54d6d690073c..f3b99e1c1042 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -20,6 +20,7 @@
#define __LINUX_IOMMU_H
#include <linux/errno.h>
+#include <linux/types.h>
#define IOMMU_READ (1)
#define IOMMU_WRITE (2)
@@ -30,6 +31,7 @@ struct iommu_group;
struct bus_type;
struct device;
struct iommu_domain;
+struct notifier_block;
/* iommu fault flags */
#define IOMMU_FAULT_READ 0x0
@@ -254,72 +256,78 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain,
{
}
-int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline int iommu_attach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
{
return -ENODEV;
}
-void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline void iommu_detach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
{
}
-struct iommu_group *iommu_group_alloc(void)
+static inline struct iommu_group *iommu_group_alloc(void)
{
return ERR_PTR(-ENODEV);
}
-void *iommu_group_get_iommudata(struct iommu_group *group)
+static inline void *iommu_group_get_iommudata(struct iommu_group *group)
{
return NULL;
}
-void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,
- void (*release)(void *iommu_data))
+static inline void iommu_group_set_iommudata(struct iommu_group *group,
+ void *iommu_data,
+ void (*release)(void *iommu_data))
{
}
-int iommu_group_set_name(struct iommu_group *group, const char *name)
+static inline int iommu_group_set_name(struct iommu_group *group,
+ const char *name)
{
return -ENODEV;
}
-int iommu_group_add_device(struct iommu_group *group, struct device *dev)
+static inline int iommu_group_add_device(struct iommu_group *group,
+ struct device *dev)
{
return -ENODEV;
}
-void iommu_group_remove_device(struct device *dev)
+static inline void iommu_group_remove_device(struct device *dev)
{
}
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
- int (*fn)(struct device *, void *))
+static inline int iommu_group_for_each_dev(struct iommu_group *group,
+ void *data,
+ int (*fn)(struct device *, void *))
{
return -ENODEV;
}
-struct iommu_group *iommu_group_get(struct device *dev)
+static inline struct iommu_group *iommu_group_get(struct device *dev)
{
return NULL;
}
-void iommu_group_put(struct iommu_group *group)
+static inline void iommu_group_put(struct iommu_group *group)
{
}
-int iommu_group_register_notifier(struct iommu_group *group,
- struct notifier_block *nb)
+static inline int iommu_group_register_notifier(struct iommu_group *group,
+ struct notifier_block *nb)
{
return -ENODEV;
}
-int iommu_group_unregister_notifier(struct iommu_group *group,
- struct notifier_block *nb)
+static inline int iommu_group_unregister_notifier(struct iommu_group *group,
+ struct notifier_block *nb)
{
return 0;
}
-int iommu_group_id(struct iommu_group *group)
+static inline int iommu_group_id(struct iommu_group *group)
{
return -ENODEV;
}
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 379e433e15e0..879db26ec401 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -369,6 +369,7 @@ struct ipv6_pinfo {
__u8 rcv_tclass;
__u32 dst_cookie;
+ __u32 rx_dst_cookie;
struct ipv6_mc_socklist __rcu *ipv6_mc_list;
struct ipv6_ac_socklist *ipv6_ac_list;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 553fb66da130..216b0ba109d7 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -349,6 +349,7 @@ enum {
IRQCHIP_MASK_ON_SUSPEND = (1 << 2),
IRQCHIP_ONOFFLINE_ENABLED = (1 << 3),
IRQCHIP_SKIP_SET_WAKE = (1 << 4),
+ IRQCHIP_ONESHOT_SAFE = (1 << 5),
};
/* This include will go away once we isolated irq_desc usage to core code */
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index f1e2527006bd..9a323d12de1c 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -39,7 +39,6 @@ struct module;
*/
struct irq_desc {
struct irq_data irq_data;
- struct timer_rand_state *timer_rand_state;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 5abb533eb8eb..0d5b17bf5e51 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -112,6 +112,11 @@ struct irq_domain {
};
#ifdef CONFIG_IRQ_DOMAIN
+struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
+ unsigned int size,
+ unsigned int first_irq,
+ const struct irq_domain_ops *ops,
+ void *host_data);
struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
unsigned int size,
unsigned int first_irq,
@@ -144,16 +149,31 @@ static inline struct irq_domain *irq_domain_add_legacy_isa(
extern void irq_domain_remove(struct irq_domain *host);
+extern int irq_domain_associate_many(struct irq_domain *domain,
+ unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count);
+static inline int irq_domain_associate(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ return irq_domain_associate_many(domain, irq, hwirq, 1);
+}
+
extern unsigned int irq_create_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern void irq_dispose_mapping(unsigned int virq);
extern unsigned int irq_find_mapping(struct irq_domain *host,
irq_hw_number_t hwirq);
extern unsigned int irq_create_direct_mapping(struct irq_domain *host);
-extern void irq_radix_revmap_insert(struct irq_domain *host, unsigned int virq,
- irq_hw_number_t hwirq);
-extern unsigned int irq_radix_revmap_lookup(struct irq_domain *host,
- irq_hw_number_t hwirq);
+extern int irq_create_strict_mappings(struct irq_domain *domain,
+ unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count);
+
+static inline int irq_create_identity_mapping(struct irq_domain *host,
+ irq_hw_number_t hwirq)
+{
+ return irq_create_strict_mappings(host, hwirq, hwirq, 1);
+}
+
extern unsigned int irq_linear_revmap(struct irq_domain *host,
irq_hw_number_t hwirq);
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index f334c7fab967..3efc43f3f162 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -1125,6 +1125,7 @@ extern int jbd2_journal_destroy (journal_t *);
extern int jbd2_journal_recover (journal_t *journal);
extern int jbd2_journal_wipe (journal_t *, int);
extern int jbd2_journal_skip_recovery (journal_t *);
+extern void jbd2_journal_update_sb_errno(journal_t *);
extern void jbd2_journal_update_sb_log_tail (journal_t *, tid_t,
unsigned long, int);
extern void __jbd2_journal_abort_hard (journal_t *);
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 265e2c3cbd1c..82680541576d 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -39,9 +39,6 @@
# error Invalid value of HZ.
#endif
-/* LATCH is used in the interval timer and ftape setup. */
-#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
-
/* Suppose we want to divide two numbers NOM and DEN: NOM/DEN, then we can
* improve accuracy by shifting LSH bits, hence calculating:
* (NOM << LSH) / DEN
@@ -54,18 +51,30 @@
#define SH_DIV(NOM,DEN,LSH) ( (((NOM) / (DEN)) << (LSH)) \
+ ((((NOM) % (DEN)) << (LSH)) + (DEN) / 2) / (DEN))
-/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
-#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8))
+#ifdef CLOCK_TICK_RATE
+/* LATCH is used in the interval timer and ftape setup. */
+# define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
+
+/*
+ * HZ is the requested value. However the CLOCK_TICK_RATE may not allow
+ * for exactly HZ. So SHIFTED_HZ is high res HZ ("<< 8" is for accuracy)
+ */
+# define SHIFTED_HZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8))
+#else
+# define SHIFTED_HZ (HZ << 8)
+#endif
-/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */
-#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
+/* TICK_NSEC is the time between ticks in nsec assuming SHIFTED_HZ */
+#define TICK_NSEC (SH_DIV(1000000UL * 1000, SHIFTED_HZ, 8))
/* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */
#define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ)
-/* TICK_USEC_TO_NSEC is the time between ticks in nsec assuming real ACTHZ and */
-/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */
-#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8))
+/*
+ * TICK_USEC_TO_NSEC is the time between ticks in nsec assuming SHIFTED_HZ and
+ * a value TUSEC for TICK_USEC (can be set bij adjtimex)
+ */
+#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV(TUSEC * USER_HZ * 1000, SHIFTED_HZ, 8))
/* some arch's have a small-data section that can be accessed register-relative
* but that can only take up to, say, 4-byte variables. jiffies being part of
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index 064725854db8..42d9e863a313 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -75,8 +75,6 @@ extern const char *kdb_diemsg;
#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */
#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */
#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */
-#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when
- * kdb is off */
#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available,
* kdb is disabled */
#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 604382143bcf..2451f1f7a1d9 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -82,10 +82,18 @@
__x - (__x % (y)); \
} \
)
+
+/*
+ * Divide positive or negative dividend by positive divisor and round
+ * to closest integer. Result is undefined for negative divisors.
+ */
#define DIV_ROUND_CLOSEST(x, divisor)( \
{ \
- typeof(divisor) __divisor = divisor; \
- (((x) + ((__divisor) / 2)) / (__divisor)); \
+ typeof(x) __x = x; \
+ typeof(divisor) __d = divisor; \
+ (((typeof(x))-1) > 0 || (__x) > 0) ? \
+ (((__x) + ((__d) / 2)) / (__d)) : \
+ (((__x) - ((__d) / 2)) / (__d)); \
} \
)
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index fc615a97e2d3..1e57449395b1 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -224,7 +224,7 @@ static inline int kobject_uevent_env(struct kobject *kobj,
static inline __printf(2, 3)
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
-{ return 0; }
+{ return -ENOMEM; }
static inline int kobject_action_type(const char *buf, size_t count,
enum kobject_action *type)
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 9c07dcebded7..65af6887872f 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -18,6 +18,7 @@
#include <linux/bug.h>
#include <linux/atomic.h>
#include <linux/kernel.h>
+#include <linux/mutex.h>
struct kref {
atomic_t refcount;
@@ -93,4 +94,21 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)
{
return kref_sub(kref, 1, release);
}
+
+static inline int kref_put_mutex(struct kref *kref,
+ void (*release)(struct kref *kref),
+ struct mutex *lock)
+{
+ WARN_ON(release == NULL);
+ if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
+ mutex_lock(lock);
+ if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
+ mutex_unlock(lock);
+ return 0;
+ }
+ release(kref);
+ return 1;
+ }
+ return 0;
+}
#endif /* _KREF_H_ */
diff --git a/include/linux/ktime.h b/include/linux/ktime.h
index 603bec2913b0..06177ba10a16 100644
--- a/include/linux/ktime.h
+++ b/include/linux/ktime.h
@@ -58,13 +58,6 @@ union ktime {
typedef union ktime ktime_t; /* Kill this */
-#define KTIME_MAX ((s64)~((u64)1 << 63))
-#if (BITS_PER_LONG == 64)
-# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
-#else
-# define KTIME_SEC_MAX LONG_MAX
-#endif
-
/*
* ktime_t definitions when using the 64-bit scalar representation:
*/
diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h
index d0752eca9b44..9d96d5d4dfed 100644
--- a/include/linux/mISDNhw.h
+++ b/include/linux/mISDNhw.h
@@ -183,7 +183,7 @@ extern int mISDN_initbchannel(struct bchannel *, unsigned short,
unsigned short);
extern int mISDN_freedchannel(struct dchannel *);
extern void mISDN_clear_bchannel(struct bchannel *);
-extern int mISDN_freebchannel(struct bchannel *);
+extern void mISDN_freebchannel(struct bchannel *);
extern int mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *);
extern void queue_ch_frame(struct mISDNchannel *, u_int,
int, struct sk_buff *);
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 83e7ba90d6e5..8d9489fdab2e 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -38,7 +38,7 @@ struct mem_cgroup_reclaim_cookie {
unsigned int generation;
};
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
/*
* All "charge" functions with gfp_mask should use GFP_KERNEL or
* (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't
@@ -72,8 +72,6 @@ extern void mem_cgroup_uncharge_end(void);
extern void mem_cgroup_uncharge_page(struct page *page);
extern void mem_cgroup_uncharge_cache_page(struct page *page);
-extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
- int order);
bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
struct mem_cgroup *memcg);
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg);
@@ -100,9 +98,9 @@ int mm_match_cgroup(const struct mm_struct *mm, const struct mem_cgroup *cgroup)
extern struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg);
-extern int
-mem_cgroup_prepare_migration(struct page *page,
- struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask);
+extern void
+mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
+ struct mem_cgroup **memcgp);
extern void mem_cgroup_end_migration(struct mem_cgroup *memcg,
struct page *oldpage, struct page *newpage, bool migration_ok);
@@ -124,7 +122,7 @@ extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
extern void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
extern int do_swap_account;
#endif
@@ -182,7 +180,6 @@ static inline void mem_cgroup_dec_page_stat(struct page *page,
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
gfp_t gfp_mask,
unsigned long *total_scanned);
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg);
void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
@@ -193,7 +190,7 @@ void mem_cgroup_split_huge_fixup(struct page *head);
bool mem_cgroup_bad_page_check(struct page *page);
void mem_cgroup_print_bad_page(struct page *page);
#endif
-#else /* CONFIG_CGROUP_MEM_RES_CTLR */
+#else /* CONFIG_MEMCG */
struct mem_cgroup;
static inline int mem_cgroup_newpage_charge(struct page *page,
@@ -279,11 +276,10 @@ static inline struct cgroup_subsys_state
return NULL;
}
-static inline int
+static inline void
mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
- struct mem_cgroup **memcgp, gfp_t gfp_mask)
+ struct mem_cgroup **memcgp)
{
- return 0;
}
static inline void mem_cgroup_end_migration(struct mem_cgroup *memcg,
@@ -366,12 +362,6 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
return 0;
}
-static inline
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
-{
- return 0;
-}
-
static inline void mem_cgroup_split_huge_fixup(struct page *head)
{
}
@@ -384,9 +374,9 @@ static inline void mem_cgroup_replace_page_cache(struct page *oldpage,
struct page *newpage)
{
}
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR */
+#endif /* CONFIG_MEMCG */
-#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
+#if !defined(CONFIG_MEMCG) || !defined(CONFIG_DEBUG_VM)
static inline bool
mem_cgroup_bad_page_check(struct page *page)
{
@@ -406,7 +396,7 @@ enum {
};
struct sock;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
void sock_update_memcg(struct sock *sk);
void sock_release_memcg(struct sock *sk);
#else
@@ -416,6 +406,6 @@ static inline void sock_update_memcg(struct sock *sk)
static inline void sock_release_memcg(struct sock *sk)
{
}
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
+#endif /* CONFIG_MEMCG_KMEM */
#endif /* _LINUX_MEMCONTROL_H */
diff --git a/include/linux/memory.h b/include/linux/memory.h
index 1ac7f6e405f9..ff9a9f8e0ed9 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -19,7 +19,7 @@
#include <linux/compiler.h>
#include <linux/mutex.h>
-#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
+#define MIN_MEMORY_BLOCK_SIZE (1UL << SECTION_SIZE_BITS)
struct memory_block {
unsigned long start_section_nr;
diff --git a/include/linux/mempool.h b/include/linux/mempool.h
index 7c08052e3321..39ed62ab5b8a 100644
--- a/include/linux/mempool.h
+++ b/include/linux/mempool.h
@@ -26,7 +26,8 @@ typedef struct mempool_s {
extern mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data);
extern mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
- mempool_free_t *free_fn, void *pool_data, int nid);
+ mempool_free_t *free_fn, void *pool_data,
+ gfp_t gfp_mask, int nid);
extern int mempool_resize(mempool_t *pool, int new_min_nr, gfp_t gfp_mask);
extern void mempool_destroy(mempool_t *pool);
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h
index 3a8435a8058f..cebe97ee98b8 100644
--- a/include/linux/mfd/core.h
+++ b/include/linux/mfd/core.h
@@ -16,6 +16,8 @@
#include <linux/platform_device.h>
+struct irq_domain;
+
/*
* This struct describes the MFD part ("cell").
* After registration the copy of this structure will become the platform data
@@ -98,7 +100,7 @@ static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev)
extern int mfd_add_devices(struct device *parent, int id,
struct mfd_cell *cells, int n_devs,
struct resource *mem_base,
- int irq_base);
+ int irq_base, struct irq_domain *irq_domain);
extern void mfd_remove_devices(struct device *parent);
diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h
index 40c372165f3e..32a1b5cfeba1 100644
--- a/include/linux/mfd/ezx-pcap.h
+++ b/include/linux/mfd/ezx-pcap.h
@@ -16,6 +16,7 @@ struct pcap_subdev {
struct pcap_platform_data {
unsigned int irq_base;
unsigned int config;
+ int gpio;
void (*init) (void *); /* board specific init */
int num_subdevs;
struct pcap_subdev *subdevs;
diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h
index 12c06870829a..7cd83d826ed8 100644
--- a/include/linux/mfd/tps65217.h
+++ b/include/linux/mfd/tps65217.h
@@ -22,6 +22,9 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+/* TPS chip id list */
+#define TPS65217 0xF0
+
/* I2C ID for TPS65217 part */
#define TPS65217_I2C_ID 0x24
@@ -248,13 +251,11 @@ struct tps_info {
struct tps65217 {
struct device *dev;
struct tps65217_board *pdata;
+ unsigned int id;
struct regulator_desc desc[TPS65217_NUM_REGULATOR];
struct regulator_dev *rdev[TPS65217_NUM_REGULATOR];
struct tps_info *info[TPS65217_NUM_REGULATOR];
struct regmap *regmap;
-
- /* Client devices */
- struct platform_device *regulator_pdev[TPS65217_NUM_REGULATOR];
};
static inline struct tps65217 *dev_to_tps65217(struct device *dev)
@@ -262,6 +263,11 @@ static inline struct tps65217 *dev_to_tps65217(struct device *dev)
return dev_get_drvdata(dev);
}
+static inline int tps65217_chip_id(struct tps65217 *tps65217)
+{
+ return tps65217->id;
+}
+
int tps65217_reg_read(struct tps65217 *tps, unsigned int reg,
unsigned int *val);
int tps65217_reg_write(struct tps65217 *tps, unsigned int reg,
diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h
index 61f0905bdc48..de201203bc7c 100644
--- a/include/linux/micrel_phy.h
+++ b/include/linux/micrel_phy.h
@@ -1,3 +1,15 @@
+/*
+ * include/linux/micrel_phy.h
+ *
+ * Micrel PHY IDs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the 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 _MICREL_PHY_H
#define _MICREL_PHY_H
@@ -5,10 +17,11 @@
#define PHY_ID_KSZ9021 0x00221610
#define PHY_ID_KS8737 0x00221720
-#define PHY_ID_KS8041 0x00221510
-#define PHY_ID_KS8051 0x00221550
+#define PHY_ID_KSZ8021 0x00221555
+#define PHY_ID_KSZ8041 0x00221510
+#define PHY_ID_KSZ8051 0x00221550
/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */
-#define PHY_ID_KS8001 0x0022161A
+#define PHY_ID_KSZ8001 0x0022161A
/* struct phy_device dev_flags definitions */
#define MICREL_PHY_50MHZ_CLK 0x00000001
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index 855c337b20c3..ce7e6671968b 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -15,7 +15,7 @@ extern int migrate_page(struct address_space *,
extern int migrate_pages(struct list_head *l, new_page_t x,
unsigned long private, bool offlining,
enum migrate_mode mode);
-extern int migrate_huge_pages(struct list_head *l, new_page_t x,
+extern int migrate_huge_page(struct page *, new_page_t x,
unsigned long private, bool offlining,
enum migrate_mode mode);
@@ -36,7 +36,7 @@ static inline void putback_lru_pages(struct list_head *l) {}
static inline int migrate_pages(struct list_head *l, new_page_t x,
unsigned long private, bool offlining,
enum migrate_mode mode) { return -ENOSYS; }
-static inline int migrate_huge_pages(struct list_head *l, new_page_t x,
+static inline int migrate_huge_page(struct page *page, new_page_t x,
unsigned long private, bool offlining,
enum migrate_mode mode) { return -ENOSYS; }
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h
index bd6c9fcdf2dd..6e1b0f973a03 100644
--- a/include/linux/mlx4/device.h
+++ b/include/linux/mlx4/device.h
@@ -796,6 +796,19 @@ enum mlx4_net_trans_rule_id {
MLX4_NET_TRANS_RULE_NUM, /* should be last */
};
+extern const u16 __sw_id_hw[];
+
+static inline int map_hw_to_sw_id(u16 header_id)
+{
+
+ int i;
+ for (i = 0; i < MLX4_NET_TRANS_RULE_NUM; i++) {
+ if (header_id == __sw_id_hw[i])
+ return i;
+ }
+ return -EINVAL;
+}
+
enum mlx4_net_trans_promisc_mode {
MLX4_FS_PROMISC_NONE = 0,
MLX4_FS_PROMISC_UPLINK,
diff --git a/include/linux/mm.h b/include/linux/mm.h
index f9f279cf5b1b..311be906b57d 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -805,6 +805,17 @@ static inline void *page_rmapping(struct page *page)
return (void *)((unsigned long)page->mapping & ~PAGE_MAPPING_FLAGS);
}
+extern struct address_space *__page_file_mapping(struct page *);
+
+static inline
+struct address_space *page_file_mapping(struct page *page)
+{
+ if (unlikely(PageSwapCache(page)))
+ return __page_file_mapping(page);
+
+ return page->mapping;
+}
+
static inline int PageAnon(struct page *page)
{
return ((unsigned long)page->mapping & PAGE_MAPPING_ANON) != 0;
@@ -821,6 +832,20 @@ static inline pgoff_t page_index(struct page *page)
return page->index;
}
+extern pgoff_t __page_file_index(struct page *page);
+
+/*
+ * Return the file index of the page. Regular pagecache pages use ->index
+ * whereas swapcache pages use swp_offset(->private)
+ */
+static inline pgoff_t page_file_index(struct page *page)
+{
+ if (unlikely(PageSwapCache(page)))
+ return __page_file_index(page);
+
+ return page->index;
+}
+
/*
* Return true if this page is mapped into pagetables.
*/
@@ -994,6 +1019,10 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
struct page **pages, struct vm_area_struct **vmas);
int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages);
+struct kvec;
+int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
+ struct page **pages);
+int get_kernel_page(unsigned long start, int write, struct page **pages);
struct page *get_dump_page(unsigned long addr);
extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
@@ -1331,6 +1360,7 @@ void warn_alloc_failed(gfp_t gfp_mask, int order, const char *fmt, ...);
extern void setup_per_cpu_pageset(void);
extern void zone_pcp_update(struct zone *zone);
+extern void zone_pcp_reset(struct zone *zone);
/* nommu.c */
extern atomic_long_t mmap_pages_allocated;
@@ -1411,6 +1441,7 @@ extern void truncate_inode_pages_range(struct address_space *,
/* generic vm_area_ops exported for stackable file systems */
extern int filemap_fault(struct vm_area_struct *, struct vm_fault *);
+extern int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
/* mm/page-writeback.c */
int write_one_page(struct page *page, int wait);
@@ -1528,6 +1559,7 @@ void vm_stat_account(struct mm_struct *, unsigned long, struct file *, long);
static inline void vm_stat_account(struct mm_struct *mm,
unsigned long flags, struct file *file, long pages)
{
+ mm->total_vm += pages;
}
#endif /* CONFIG_PROC_FS */
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 074eb98fe15d..bf7867200b95 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -54,6 +54,15 @@ struct page {
union {
pgoff_t index; /* Our offset within mapping. */
void *freelist; /* slub/slob first free object */
+ bool pfmemalloc; /* If set by the page allocator,
+ * ALLOC_NO_WATERMARKS was set
+ * and the low watermark was not
+ * met implying that the system
+ * is under some pressure. The
+ * caller should try ensure
+ * this page is only used to
+ * free other pages.
+ */
};
union {
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 111aca5e97f3..4b27f9f503e4 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -239,6 +239,7 @@ struct mmc_card {
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
+#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
/* byte mode */
unsigned int poweroff_notify_state; /* eMMC4.5 notify feature */
#define MMC_NO_POWER_NOTIFICATION 0
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 458988bd55a1..2daa54f55db7 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -201,7 +201,7 @@ struct zone_reclaim_stat {
struct lruvec {
struct list_head lists[NR_LRU_LISTS];
struct zone_reclaim_stat reclaim_stat;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
struct zone *zone;
#endif
};
@@ -209,7 +209,6 @@ struct lruvec {
/* Mask used at gathering information at once (see memcontrol.c) */
#define LRU_ALL_FILE (BIT(LRU_INACTIVE_FILE) | BIT(LRU_ACTIVE_FILE))
#define LRU_ALL_ANON (BIT(LRU_INACTIVE_ANON) | BIT(LRU_ACTIVE_ANON))
-#define LRU_ALL_EVICTABLE (LRU_ALL_FILE | LRU_ALL_ANON)
#define LRU_ALL ((1 << NR_LRU_LISTS) - 1)
/* Isolate clean file */
@@ -369,6 +368,10 @@ struct zone {
*/
spinlock_t lock;
int all_unreclaimable; /* All pages pinned */
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+ /* pfn where the last incremental compaction isolated free pages */
+ unsigned long compact_cached_free_pfn;
+#endif
#ifdef CONFIG_MEMORY_HOTPLUG
/* see spanned/present_pages for more description */
seqlock_t span_seqlock;
@@ -475,6 +478,14 @@ struct zone {
* rarely used fields:
*/
const char *name;
+#ifdef CONFIG_MEMORY_ISOLATION
+ /*
+ * the number of MIGRATE_ISOLATE *pageblock*.
+ * We need this for free page counting. Look at zone_watermark_ok_safe.
+ * It's protected by zone->lock
+ */
+ int nr_pageblock_isolate;
+#endif
} ____cacheline_internodealigned_in_smp;
typedef enum {
@@ -671,7 +682,7 @@ typedef struct pglist_data {
int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
struct page *node_mem_map;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
struct page_cgroup *node_page_cgroup;
#endif
#endif
@@ -694,6 +705,7 @@ typedef struct pglist_data {
range, including holes */
int node_id;
wait_queue_head_t kswapd_wait;
+ wait_queue_head_t pfmemalloc_wait;
struct task_struct *kswapd; /* Protected by lock_memory_hotplug() */
int kswapd_max_order;
enum zone_type classzone_idx;
@@ -718,7 +730,7 @@ typedef struct pglist_data {
#include <linux/memory_hotplug.h>
extern struct mutex zonelists_mutex;
-void build_all_zonelists(void *data);
+void build_all_zonelists(pg_data_t *pgdat, struct zone *zone);
void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx);
bool zone_watermark_ok(struct zone *z, int order, unsigned long mark,
int classzone_idx, int alloc_flags);
@@ -736,7 +748,7 @@ extern void lruvec_init(struct lruvec *lruvec, struct zone *zone);
static inline struct zone *lruvec_zone(struct lruvec *lruvec)
{
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
return lruvec->zone;
#else
return container_of(lruvec, struct zone, lruvec);
@@ -773,7 +785,7 @@ extern int movable_zone;
static inline int zone_movable_is_highmem(void)
{
-#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE)
+#if defined(CONFIG_HIGHMEM) && defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
return movable_zone == ZONE_HIGHMEM;
#else
return 0;
@@ -1052,7 +1064,7 @@ struct mem_section {
/* See declaration of similar field in struct zone */
unsigned long *pageblock_flags;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
/*
* If !SPARSEMEM, pgdat doesn't have page_cgroup pointer. We use
* section. (see memcontrol.h/page_cgroup.h about this.)
diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h
index 51bf8ada6dc0..49258e0ed1c6 100644
--- a/include/linux/mv643xx_eth.h
+++ b/include/linux/mv643xx_eth.h
@@ -15,6 +15,8 @@
#define MV643XX_ETH_SIZE_REG_4 0x2224
#define MV643XX_ETH_BASE_ADDR_ENABLE_REG 0x2290
+#define MV643XX_TX_CSUM_DEFAULT_LIMIT 0
+
struct mv643xx_eth_shared_platform_data {
struct mbus_dram_target_info *dram;
struct platform_device *shared_smi;
diff --git a/include/linux/namei.h b/include/linux/namei.h
index d2ef8b34b967..4bf19d8174ed 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -67,6 +67,7 @@ extern int kern_path(const char *, unsigned, struct path *);
extern struct dentry *kern_path_create(int, const char *, struct path *, int);
extern struct dentry *user_path_create(int, const char __user *, struct path *, int);
+extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *);
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eb06e58bed0b..59dc05f38247 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -953,7 +953,8 @@ struct net_device_ops {
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
- struct netpoll_info *info);
+ struct netpoll_info *info,
+ gfp_t gfp);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
int (*ndo_set_vf_mac)(struct net_device *dev,
@@ -1300,6 +1301,8 @@ struct net_device {
/* for setting kernel sock attribute on TCP connection setup */
#define GSO_MAX_SIZE 65536
unsigned int gso_max_size;
+#define GSO_MAX_SEGS 65535
+ u16 gso_max_segs;
#ifdef CONFIG_DCB
/* Data Center Bridging netlink ops */
@@ -1519,6 +1522,8 @@ struct packet_type {
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb);
+ bool (*id_match)(struct packet_type *ptype,
+ struct sock *sk);
void *af_packet_priv;
struct list_head list;
};
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index 0dfc8b7210a3..89f2a627f3f0 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -164,7 +164,7 @@ extern int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr
unsigned int dataoff, unsigned int datalen,
const char *name,
unsigned int *matchoff, unsigned int *matchlen,
- union nf_inet_addr *addr);
+ union nf_inet_addr *addr, bool delim);
extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
unsigned int off, unsigned int datalen,
const char *name,
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 28f5389c924b..66d5379c305e 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -23,6 +23,7 @@ struct netpoll {
u8 remote_mac[ETH_ALEN];
struct list_head rx; /* rx_np list element */
+ struct rcu_head rcu;
};
struct netpoll_info {
@@ -38,28 +39,40 @@ struct netpoll_info {
struct delayed_work tx_work;
struct netpoll *netpoll;
+ struct rcu_head rcu;
};
void netpoll_send_udp(struct netpoll *np, const char *msg, int len);
void netpoll_print_options(struct netpoll *np);
int netpoll_parse_options(struct netpoll *np, char *opt);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev);
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp);
int netpoll_setup(struct netpoll *np);
int netpoll_trap(void);
void netpoll_set_trap(int trap);
void __netpoll_cleanup(struct netpoll *np);
+void __netpoll_free_rcu(struct netpoll *np);
void netpoll_cleanup(struct netpoll *np);
-int __netpoll_rx(struct sk_buff *skb);
+int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo);
void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
struct net_device *dev);
static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
{
+ unsigned long flags;
+ local_irq_save(flags);
netpoll_send_skb_on_dev(np, skb, np->dev);
+ local_irq_restore(flags);
}
#ifdef CONFIG_NETPOLL
+static inline bool netpoll_rx_on(struct sk_buff *skb)
+{
+ struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo);
+
+ return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags);
+}
+
static inline bool netpoll_rx(struct sk_buff *skb)
{
struct netpoll_info *npinfo;
@@ -67,14 +80,14 @@ static inline bool netpoll_rx(struct sk_buff *skb)
bool ret = false;
local_irq_save(flags);
- npinfo = rcu_dereference_bh(skb->dev->npinfo);
- if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags))
+ if (!netpoll_rx_on(skb))
goto out;
+ npinfo = rcu_dereference_bh(skb->dev->npinfo);
spin_lock(&npinfo->rx_lock);
/* check rx_flags again with the lock held */
- if (npinfo->rx_flags && __netpoll_rx(skb))
+ if (npinfo->rx_flags && __netpoll_rx(skb, npinfo))
ret = true;
spin_unlock(&npinfo->rx_lock);
@@ -83,13 +96,6 @@ out:
return ret;
}
-static inline int netpoll_rx_on(struct sk_buff *skb)
-{
- struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo);
-
- return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags);
-}
-
static inline int netpoll_receive_skb(struct sk_buff *skb)
{
if (!list_empty(&skb->dev->napi_list))
@@ -119,7 +125,7 @@ static inline void netpoll_poll_unlock(void *have)
}
}
-static inline int netpoll_tx_running(struct net_device *dev)
+static inline bool netpoll_tx_running(struct net_device *dev)
{
return irqs_disabled();
}
@@ -127,11 +133,11 @@ static inline int netpoll_tx_running(struct net_device *dev)
#else
static inline bool netpoll_rx(struct sk_buff *skb)
{
- return 0;
+ return false;
}
-static inline int netpoll_rx_on(struct sk_buff *skb)
+static inline bool netpoll_rx_on(struct sk_buff *skb)
{
- return 0;
+ return false;
}
static inline int netpoll_receive_skb(struct sk_buff *skb)
{
@@ -147,9 +153,9 @@ static inline void netpoll_poll_unlock(void *have)
static inline void netpoll_netdev_init(struct net_device *dev)
{
}
-static inline int netpoll_tx_running(struct net_device *dev)
+static inline bool netpoll_tx_running(struct net_device *dev)
{
- return 0;
+ return false;
}
#endif
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 2889877318bc..4b03f56e280e 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -265,11 +265,6 @@ static inline const struct nfs_rpc_ops *NFS_PROTO(const struct inode *inode)
return NFS_SERVER(inode)->nfs_client->rpc_ops;
}
-static inline __be32 *NFS_COOKIEVERF(const struct inode *inode)
-{
- return NFS_I(inode)->cookieverf;
-}
-
static inline unsigned NFS_MINATTRTIMEO(const struct inode *inode)
{
struct nfs_server *nfss = NFS_SERVER(inode);
@@ -473,10 +468,10 @@ extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t,
unsigned long);
extern ssize_t nfs_file_direct_read(struct kiocb *iocb,
const struct iovec *iov, unsigned long nr_segs,
- loff_t pos);
+ loff_t pos, bool uio);
extern ssize_t nfs_file_direct_write(struct kiocb *iocb,
const struct iovec *iov, unsigned long nr_segs,
- loff_t pos);
+ loff_t pos, bool uio);
/*
* linux/fs/nfs/dir.c
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 880805774f9f..92ce5783b707 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -69,6 +69,7 @@ struct nfs_pageio_descriptor {
const struct nfs_pgio_completion_ops *pg_completion_ops;
struct pnfs_layout_segment *pg_lseg;
struct nfs_direct_req *pg_dreq;
+ void *pg_layout_private;
};
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 00485e084394..be9cf3c7e79e 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -652,7 +652,7 @@ struct nfs_getaclargs {
};
/* getxattr ACL interface flags */
-#define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */
+#define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */
struct nfs_getaclres {
size_t acl_len;
size_t acl_data_offset;
@@ -1248,6 +1248,7 @@ struct nfs_pgio_header {
void (*release) (struct nfs_pgio_header *hdr);
const struct nfs_pgio_completion_ops *completion_ops;
struct nfs_direct_req *dreq;
+ void *layout_private;
spinlock_t lock;
/* fields protected by lock */
int pnfs_error;
diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h
index ce4743a26015..fa63048fecff 100644
--- a/include/linux/nfsd/nfsfh.h
+++ b/include/linux/nfsd/nfsfh.h
@@ -143,6 +143,7 @@ typedef struct svc_fh {
int fh_maxsize; /* max size for fh_handle */
unsigned char fh_locked; /* inode locked by us */
+ unsigned char fh_want_write; /* remount protection taken */
#ifdef CONFIG_NFSD_V3
unsigned char fh_post_saved; /* post-op attrs saved */
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 9490a00529f4..c25cccaa555a 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -35,8 +35,10 @@ struct nvme_bar {
__u64 acq; /* Admin CQ Base Address */
};
+#define NVME_CAP_MQES(cap) ((cap) & 0xffff)
#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff)
#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf)
+#define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf)
enum {
NVME_CC_ENABLE = 1 << 0,
diff --git a/include/linux/of.h b/include/linux/of.h
index 42c2a58328c1..1b1163225f3b 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -21,6 +21,7 @@
#include <linux/kref.h>
#include <linux/mod_devicetable.h>
#include <linux/spinlock.h>
+#include <linux/topology.h>
#include <asm/byteorder.h>
#include <asm/errno.h>
@@ -158,11 +159,6 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
#define OF_BAD_ADDR ((u64)-1)
-#ifndef of_node_to_nid
-static inline int of_node_to_nid(struct device_node *np) { return -1; }
-#define of_node_to_nid of_node_to_nid
-#endif
-
static inline const char* of_node_full_name(struct device_node *np)
{
return np ? np->full_name : "<no-node>";
@@ -194,10 +190,17 @@ extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node);
extern struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev);
+extern struct device_node *of_get_next_available_child(
+ const struct device_node *node, struct device_node *prev);
+
#define for_each_child_of_node(parent, child) \
for (child = of_get_next_child(parent, NULL); child != NULL; \
child = of_get_next_child(parent, child))
+#define for_each_available_child_of_node(parent, child) \
+ for (child = of_get_next_available_child(parent, NULL); child != NULL; \
+ child = of_get_next_available_child(parent, child))
+
static inline int of_get_child_count(const struct device_node *np)
{
struct device_node *child;
@@ -427,6 +430,15 @@ static inline int of_machine_is_compatible(const char *compat)
while (0)
#endif /* CONFIG_OF */
+#ifndef of_node_to_nid
+static inline int of_node_to_nid(struct device_node *np)
+{
+ return numa_node_id();
+}
+
+#define of_node_to_nid of_node_to_nid
+#endif
+
/**
* of_property_read_bool - Findfrom a property
* @np: device node from which the property value is to be read.
diff --git a/include/linux/olpc-ec.h b/include/linux/olpc-ec.h
new file mode 100644
index 000000000000..5bb6e760aa61
--- /dev/null
+++ b/include/linux/olpc-ec.h
@@ -0,0 +1,41 @@
+#ifndef _LINUX_OLPC_EC_H
+#define _LINUX_OLPC_EC_H
+
+/* XO-1 EC commands */
+#define EC_FIRMWARE_REV 0x08
+#define EC_WRITE_SCI_MASK 0x1b
+#define EC_WAKE_UP_WLAN 0x24
+#define EC_WLAN_LEAVE_RESET 0x25
+#define EC_READ_EB_MODE 0x2a
+#define EC_SET_SCI_INHIBIT 0x32
+#define EC_SET_SCI_INHIBIT_RELEASE 0x34
+#define EC_WLAN_ENTER_RESET 0x35
+#define EC_WRITE_EXT_SCI_MASK 0x38
+#define EC_SCI_QUERY 0x84
+#define EC_EXT_SCI_QUERY 0x85
+
+struct platform_device;
+
+struct olpc_ec_driver {
+ int (*probe)(struct platform_device *);
+ int (*suspend)(struct platform_device *);
+ int (*resume)(struct platform_device *);
+
+ int (*ec_cmd)(u8, u8 *, size_t, u8 *, size_t, void *);
+};
+
+#ifdef CONFIG_OLPC
+
+extern void olpc_ec_driver_register(struct olpc_ec_driver *drv, void *arg);
+
+extern int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
+ size_t outlen);
+
+#else
+
+static inline int olpc_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
+ size_t outlen) { return -ENODEV; }
+
+#endif /* CONFIG_OLPC */
+
+#endif /* _LINUX_OLPC_EC_H */
diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h
new file mode 100644
index 000000000000..eb475a8ea25b
--- /dev/null
+++ b/include/linux/omap-dma.h
@@ -0,0 +1,22 @@
+/*
+ * OMAP DMA Engine support
+ *
+ * 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.
+ */
+#ifndef __LINUX_OMAP_DMA_H
+#define __LINUX_OMAP_DMA_H
+
+struct dma_chan;
+
+#if defined(CONFIG_DMA_OMAP) || defined(CONFIG_DMA_OMAP_MODULE)
+bool omap_dma_filter_fn(struct dma_chan *, void *);
+#else
+static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d)
+{
+ return false;
+}
+#endif
+
+#endif
diff --git a/include/linux/omap3isp.h b/include/linux/omap3isp.h
index c73a34c3434d..c090cf9249bb 100644
--- a/include/linux/omap3isp.h
+++ b/include/linux/omap3isp.h
@@ -28,6 +28,7 @@
#define OMAP3_ISP_USER_H
#include <linux/types.h>
+#include <linux/videodev2.h>
/*
* Private IOCTLs
@@ -427,7 +428,7 @@ struct omap3isp_ccdc_update_config {
#define OMAP3ISP_PREV_COLOR_CONV (1 << 8)
#define OMAP3ISP_PREV_YC_LIMIT (1 << 9)
#define OMAP3ISP_PREV_DEFECT_COR (1 << 10)
-#define OMAP3ISP_PREV_GAMMABYPASS (1 << 11)
+/* Bit 11 was OMAP3ISP_PREV_GAMMABYPASS, now merged with OMAP3ISP_PREV_GAMMA */
#define OMAP3ISP_PREV_DRK_FRM_CAPTURE (1 << 12)
#define OMAP3ISP_PREV_DRK_FRM_SUBTRACT (1 << 13)
#define OMAP3ISP_PREV_LENS_SHADING (1 << 14)
@@ -436,6 +437,7 @@ struct omap3isp_ccdc_update_config {
#define OMAP3ISP_PREV_NF_TBL_SIZE 64
#define OMAP3ISP_PREV_CFA_TBL_SIZE 576
+#define OMAP3ISP_PREV_CFA_BLK_SIZE (OMAP3ISP_PREV_CFA_TBL_SIZE / 4)
#define OMAP3ISP_PREV_GAMMA_TBL_SIZE 1024
#define OMAP3ISP_PREV_YENH_TBL_SIZE 128
@@ -477,7 +479,7 @@ struct omap3isp_prev_cfa {
enum omap3isp_cfa_fmt format;
__u8 gradthrs_vert;
__u8 gradthrs_horz;
- __u32 table[OMAP3ISP_PREV_CFA_TBL_SIZE];
+ __u32 table[4][OMAP3ISP_PREV_CFA_BLK_SIZE];
};
/**
diff --git a/include/linux/oom.h b/include/linux/oom.h
index e4c29bc72e70..49a3031fda50 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -40,15 +40,36 @@ enum oom_constraint {
CONSTRAINT_MEMCG,
};
+enum oom_scan_t {
+ OOM_SCAN_OK, /* scan thread and find its badness */
+ OOM_SCAN_CONTINUE, /* do not consider thread for oom kill */
+ OOM_SCAN_ABORT, /* abort the iteration and return */
+ OOM_SCAN_SELECT, /* always select this thread first */
+};
+
extern void compare_swap_oom_score_adj(int old_val, int new_val);
extern int test_set_oom_score_adj(int new_val);
extern unsigned long oom_badness(struct task_struct *p,
struct mem_cgroup *memcg, const nodemask_t *nodemask,
unsigned long totalpages);
+extern void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+ unsigned int points, unsigned long totalpages,
+ struct mem_cgroup *memcg, nodemask_t *nodemask,
+ const char *message);
+
extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags);
+extern void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
+ int order, const nodemask_t *nodemask);
+
+extern enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
+ unsigned long totalpages, const nodemask_t *nodemask,
+ bool force_kill);
+extern void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+ int order);
+
extern void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
int order, nodemask_t *mask, bool force_kill);
extern int register_oom_notifier(struct notifier_block *nb);
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index c88d2a9451af..b5d13841604e 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/bug.h>
+#include <linux/mmdebug.h>
#ifndef __GENERATING_BOUNDS_H
#include <linux/mm_types.h>
#include <generated/bounds.h>
@@ -453,6 +454,34 @@ static inline int PageTransTail(struct page *page)
}
#endif
+/*
+ * If network-based swap is enabled, sl*b must keep track of whether pages
+ * were allocated from pfmemalloc reserves.
+ */
+static inline int PageSlabPfmemalloc(struct page *page)
+{
+ VM_BUG_ON(!PageSlab(page));
+ return PageActive(page);
+}
+
+static inline void SetPageSlabPfmemalloc(struct page *page)
+{
+ VM_BUG_ON(!PageSlab(page));
+ SetPageActive(page);
+}
+
+static inline void __ClearPageSlabPfmemalloc(struct page *page)
+{
+ VM_BUG_ON(!PageSlab(page));
+ __ClearPageActive(page);
+}
+
+static inline void ClearPageSlabPfmemalloc(struct page *page)
+{
+ VM_BUG_ON(!PageSlab(page));
+ ClearPageActive(page);
+}
+
#ifdef CONFIG_MMU
#define __PG_MLOCKED (1 << PG_mlocked)
#else
diff --git a/include/linux/page-isolation.h b/include/linux/page-isolation.h
index 3bdcab30ca41..105077aa7685 100644
--- a/include/linux/page-isolation.h
+++ b/include/linux/page-isolation.h
@@ -1,6 +1,11 @@
#ifndef __LINUX_PAGEISOLATION_H
#define __LINUX_PAGEISOLATION_H
+
+bool has_unmovable_pages(struct zone *zone, struct page *page, int count);
+void set_pageblock_migratetype(struct page *page, int migratetype);
+int move_freepages_block(struct zone *zone, struct page *page,
+ int migratetype);
/*
* Changes migrate type in [start_pfn, end_pfn) to be MIGRATE_ISOLATE.
* If specified range includes migrate types other than MOVABLE or CMA,
@@ -10,7 +15,7 @@
* free all pages in the range. test_page_isolated() can be used for
* test it.
*/
-extern int
+int
start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
unsigned migratetype);
@@ -18,7 +23,7 @@ start_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
* Changes MIGRATE_ISOLATE to MIGRATE_MOVABLE.
* target range is [start_pfn, end_pfn)
*/
-extern int
+int
undo_isolate_page_range(unsigned long start_pfn, unsigned long end_pfn,
unsigned migratetype);
@@ -30,8 +35,8 @@ int test_pages_isolated(unsigned long start_pfn, unsigned long end_pfn);
/*
* Internal functions. Changes pageblock's migrate type.
*/
-extern int set_migratetype_isolate(struct page *page);
-extern void unset_migratetype_isolate(struct page *page, unsigned migratetype);
+int set_migratetype_isolate(struct page *page);
+void unset_migratetype_isolate(struct page *page, unsigned migratetype);
#endif
diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h
index a88cdba27809..777a524716db 100644
--- a/include/linux/page_cgroup.h
+++ b/include/linux/page_cgroup.h
@@ -12,7 +12,7 @@ enum {
#ifndef __GENERATING_BOUNDS_H
#include <generated/bounds.h>
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
#include <linux/bit_spinlock.h>
/*
@@ -82,7 +82,7 @@ static inline void unlock_page_cgroup(struct page_cgroup *pc)
bit_spin_unlock(PCG_LOCK, &pc->flags);
}
-#else /* CONFIG_CGROUP_MEM_RES_CTLR */
+#else /* CONFIG_MEMCG */
struct page_cgroup;
static inline void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat)
@@ -102,11 +102,11 @@ static inline void __init page_cgroup_init_flatmem(void)
{
}
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR */
+#endif /* CONFIG_MEMCG */
#include <linux/swap.h>
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
unsigned short old, unsigned short new);
extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id);
@@ -138,7 +138,7 @@ static inline void swap_cgroup_swapoff(int type)
return;
}
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_SWAP */
+#endif /* CONFIG_MEMCG_SWAP */
#endif /* !__GENERATING_BOUNDS_H */
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 7cfad3bbb0cc..e42c762f0dc7 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -286,6 +286,11 @@ static inline loff_t page_offset(struct page *page)
return ((loff_t)page->index) << PAGE_CACHE_SHIFT;
}
+static inline loff_t page_file_offset(struct page *page)
+{
+ return ((loff_t)page_file_index(page)) << PAGE_CACHE_SHIFT;
+}
+
extern pgoff_t linear_hugepage_index(struct vm_area_struct *vma,
unsigned long address);
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index fc3526077348..6b4565c440c8 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2149,7 +2149,7 @@
#define PCI_DEVICE_ID_TIGON3_5704S 0x16a8
#define PCI_DEVICE_ID_NX2_57800_VF 0x16a9
#define PCI_DEVICE_ID_NX2_5706S 0x16aa
-#define PCI_DEVICE_ID_NX2_57840_MF 0x16ab
+#define PCI_DEVICE_ID_NX2_57840_MF 0x16a4
#define PCI_DEVICE_ID_NX2_5708S 0x16ac
#define PCI_DEVICE_ID_NX2_57840_VF 0x16ad
#define PCI_DEVICE_ID_NX2_57810_MF 0x16ae
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 76c5c8b724a7..bdb41612bfec 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -274,6 +274,8 @@ struct perf_event_attr {
__u64 branch_sample_type; /* enum branch_sample_type */
};
+#define perf_flags(attr) (*(&(attr)->read_format + 1))
+
/*
* Ioctls that can be done on a perf event fd:
*/
@@ -926,7 +928,7 @@ struct perf_event {
struct hw_perf_event hw;
struct perf_event_context *ctx;
- struct file *filp;
+ atomic_long_t refcount;
/*
* These accumulate total time (in nanoseconds) that children
@@ -1272,7 +1274,8 @@ static inline bool perf_paranoid_kernel(void)
extern void perf_event_init(void);
extern void perf_tp_event(u64 addr, u64 count, void *record,
int entry_size, struct pt_regs *regs,
- struct hlist_head *head, int rctx);
+ struct hlist_head *head, int rctx,
+ struct task_struct *task);
extern void perf_bp_event(struct perf_event *event, void *data);
#ifndef perf_misc_flags
@@ -1295,6 +1298,7 @@ extern int perf_swevent_get_recursion_context(void);
extern void perf_swevent_put_recursion_context(int rctx);
extern void perf_event_enable(struct perf_event *event);
extern void perf_event_disable(struct perf_event *event);
+extern int __perf_event_disable(void *info);
extern void perf_event_task_tick(void);
#else
static inline void
@@ -1333,6 +1337,7 @@ static inline int perf_swevent_get_recursion_context(void) { return -1; }
static inline void perf_swevent_put_recursion_context(int rctx) { }
static inline void perf_event_enable(struct perf_event *event) { }
static inline void perf_event_disable(struct perf_event *event) { }
+static inline int __perf_event_disable(void *info) { return -1; }
static inline void perf_event_task_tick(void) { }
#endif
diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h
index 6dd96fb45482..e9b7f4350844 100644
--- a/include/linux/pinctrl/consumer.h
+++ b/include/linux/pinctrl/consumer.h
@@ -20,6 +20,7 @@
/* This struct is private to the core and should be regarded as a cookie */
struct pinctrl;
struct pinctrl_state;
+struct device;
#ifdef CONFIG_PINCTRL
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index e11d1c0fc60f..ad1a427b5267 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -160,4 +160,6 @@ void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
long pipe_fcntl(struct file *, unsigned int, unsigned long arg);
struct pipe_inode_info *get_pipe_info(struct file *file);
+int create_pipe_files(struct file **, int);
+
#endif
diff --git a/include/linux/random.h b/include/linux/random.h
index 8f74538c96db..ac621ce886ca 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -48,13 +48,13 @@ struct rnd_state {
#ifdef __KERNEL__
-extern void rand_initialize_irq(int irq);
-
+extern void add_device_randomness(const void *, unsigned int);
extern void add_input_randomness(unsigned int type, unsigned int code,
unsigned int value);
-extern void add_interrupt_randomness(int irq);
+extern void add_interrupt_randomness(int irq, int irq_flags);
extern void get_random_bytes(void *buf, int nbytes);
+extern void get_random_bytes_arch(void *buf, int nbytes);
void generate_random_uuid(unsigned char uuid_out[16]);
#ifndef MODULE
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 68dcffaa62a0..23bddac4bad8 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -334,14 +334,6 @@ static inline void lockup_detector_init(void)
}
#endif
-#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND)
-void lockup_detector_bootcpu_resume(void);
-#else
-static inline void lockup_detector_bootcpu_resume(void)
-{
-}
-#endif
-
#ifdef CONFIG_DETECT_HUNG_TASK
extern unsigned int sysctl_hung_task_panic;
extern unsigned long sysctl_hung_task_check_count;
@@ -962,7 +954,6 @@ struct sched_domain {
unsigned int smt_gain;
int flags; /* See SD_* */
int level;
- int idle_buddy; /* cpu assigned to select_idle_sibling() */
/* Runtime fields. */
unsigned long last_balance; /* init to jiffies. units in jiffies */
@@ -1584,7 +1575,7 @@ struct task_struct {
/* bitmask and counter of trace recursion */
unsigned long trace_recursion;
#endif /* CONFIG_TRACING */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR /* memcg uses this to do batch job */
+#ifdef CONFIG_MEMCG /* memcg uses this to do batch job */
struct memcg_batch_info {
int do_batch; /* incremented when batch uncharge started */
struct mem_cgroup *memcg; /* target memcg of uncharge */
@@ -1894,6 +1885,13 @@ static inline void rcu_copy_process(struct task_struct *p)
#endif
+static inline void tsk_restore_flags(struct task_struct *task,
+ unsigned long orig_flags, unsigned long flags)
+{
+ task->flags &= ~flags;
+ task->flags |= orig_flags & flags;
+}
+
#ifdef CONFIG_SMP
extern void do_set_cpus_allowed(struct task_struct *p,
const struct cpumask *new_mask);
diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73cdbbef..d143b8e01954 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -118,6 +118,7 @@ void reset_security_ops(void);
extern unsigned long mmap_min_addr;
extern unsigned long dac_mmap_min_addr;
#else
+#define mmap_min_addr 0UL
#define dac_mmap_min_addr 0UL
#endif
@@ -1242,8 +1243,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* Check that the @parent process has sufficient permission to trace the
* current process before allowing the current process to present itself
* to the @parent process for tracing.
- * The parent process will still have to undergo the ptrace_access_check
- * checks before it is allowed to trace this one.
* @parent contains the task_struct structure for debugger process.
* Return 0 if permission is granted.
* @capget:
diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h
index 93f9821554b6..a3728bf66f0e 100644
--- a/include/linux/shdma-base.h
+++ b/include/linux/shdma-base.h
@@ -50,6 +50,7 @@ struct shdma_desc {
struct list_head node;
struct dma_async_tx_descriptor async_tx;
enum dma_transfer_direction direction;
+ size_t partial;
dma_cookie_t cookie;
int chunks;
int mark;
@@ -98,6 +99,7 @@ struct shdma_ops {
void (*start_xfer)(struct shdma_chan *, struct shdma_desc *);
struct shdma_desc *(*embedded_desc)(void *, int);
bool (*chan_irq)(struct shdma_chan *, int);
+ size_t (*get_partial)(struct shdma_chan *, struct shdma_desc *);
};
struct shdma_dev {
diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h
index 07ceb97d53fa..ac6b8ee07825 100644
--- a/include/linux/shrinker.h
+++ b/include/linux/shrinker.h
@@ -20,7 +20,6 @@ struct shrink_control {
* 'nr_to_scan' entries and attempt to free them up. It should return
* the number of objects which remain in the cache. If it returns -1, it means
* it cannot do any scanning at this time (eg. there is a risk of deadlock).
- * The callback must not return -1 if nr_to_scan is zero.
*
* The 'gfpmask' refers to the allocation we are currently trying to
* fulfil.
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index d205c4be7f5b..7632c87da2c9 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -462,6 +462,7 @@ struct sk_buff {
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
+ __u8 pfmemalloc:1;
__u8 ooo_okay:1;
__u8 l4_rxhash:1;
__u8 wifi_acked_valid:1;
@@ -502,6 +503,15 @@ struct sk_buff {
#include <linux/slab.h>
+#define SKB_ALLOC_FCLONE 0x01
+#define SKB_ALLOC_RX 0x02
+
+/* Returns true if the skb was allocated from PFMEMALLOC reserves */
+static inline bool skb_pfmemalloc(const struct sk_buff *skb)
+{
+ return unlikely(skb->pfmemalloc);
+}
+
/*
* skb might have a dst pointer attached, refcounted or not.
* _skb_refdst low order bit is set if refcount was _not_ taken
@@ -565,7 +575,7 @@ extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
bool *fragstolen, int *delta_truesize);
extern struct sk_buff *__alloc_skb(unsigned int size,
- gfp_t priority, int fclone, int node);
+ gfp_t priority, int flags, int node);
extern struct sk_buff *build_skb(void *data, unsigned int frag_size);
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
@@ -576,7 +586,7 @@ static inline struct sk_buff *alloc_skb(unsigned int size,
static inline struct sk_buff *alloc_skb_fclone(unsigned int size,
gfp_t priority)
{
- return __alloc_skb(size, priority, 1, NUMA_NO_NODE);
+ return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE);
}
extern void skb_recycle(struct sk_buff *skb);
@@ -1237,6 +1247,17 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ /*
+ * Propagate page->pfmemalloc to the skb if we can. The problem is
+ * that not all callers have unique ownership of the page. If
+ * pfmemalloc is set, we check the mapping as a mapping implies
+ * page->index is set (index and pfmemalloc share space).
+ * If it's a valid mapping, we cannot use page->pfmemalloc but we
+ * do not lose pfmemalloc information as the pages would not be
+ * allocated using __GFP_MEMALLOC.
+ */
+ if (page->pfmemalloc && !page->mapping)
+ skb->pfmemalloc = true;
frag->page.p = page;
frag->page_offset = off;
skb_frag_size_set(frag, size);
@@ -1753,6 +1774,61 @@ static inline struct sk_buff *netdev_alloc_skb_ip_align(struct net_device *dev,
return __netdev_alloc_skb_ip_align(dev, length, GFP_ATOMIC);
}
+/*
+ * __skb_alloc_page - allocate pages for ps-rx on a skb and preserve pfmemalloc data
+ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
+ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
+ * @order: size of the allocation
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+*/
+static inline struct page *__skb_alloc_pages(gfp_t gfp_mask,
+ struct sk_buff *skb,
+ unsigned int order)
+{
+ struct page *page;
+
+ gfp_mask |= __GFP_COLD;
+
+ if (!(gfp_mask & __GFP_NOMEMALLOC))
+ gfp_mask |= __GFP_MEMALLOC;
+
+ page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
+ if (skb && page && page->pfmemalloc)
+ skb->pfmemalloc = true;
+
+ return page;
+}
+
+/**
+ * __skb_alloc_page - allocate a page for ps-rx for a given skb and preserve pfmemalloc data
+ * @gfp_mask: alloc_pages_node mask. Set __GFP_NOMEMALLOC if not for network packet RX
+ * @skb: skb to set pfmemalloc on if __GFP_MEMALLOC is used
+ *
+ * Allocate a new page.
+ *
+ * %NULL is returned if there is no free memory.
+ */
+static inline struct page *__skb_alloc_page(gfp_t gfp_mask,
+ struct sk_buff *skb)
+{
+ return __skb_alloc_pages(gfp_mask, skb, 0);
+}
+
+/**
+ * skb_propagate_pfmemalloc - Propagate pfmemalloc if skb is allocated after RX page
+ * @page: The page that was allocated from skb_alloc_page
+ * @skb: The skb that may need pfmemalloc set
+ */
+static inline void skb_propagate_pfmemalloc(struct page *page,
+ struct sk_buff *skb)
+{
+ if (page && page->pfmemalloc)
+ skb->pfmemalloc = true;
+}
+
/**
* skb_frag_page - retrieve the page refered to by a paged fragment
* @frag: the paged fragment
diff --git a/include/linux/string.h b/include/linux/string.h
index ffe0442e18d2..b9178812d9df 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -144,8 +144,8 @@ static inline bool strstarts(const char *str, const char *prefix)
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
-#endif
extern size_t memweight(const void *ptr, size_t bytes);
+#endif /* __KERNEL__ */
#endif /* _LINUX_STRING_H_ */
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 77d278defa70..bf8c49ff7530 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -114,6 +114,7 @@ struct rpc_xprt_ops {
void (*set_buffer_size)(struct rpc_xprt *xprt, size_t sndsize, size_t rcvsize);
int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task);
+ void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task);
void (*rpcbind)(struct rpc_task *task);
void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
void (*connect)(struct rpc_task *task);
@@ -174,6 +175,8 @@ struct rpc_xprt {
unsigned long state; /* transport state */
unsigned char shutdown : 1, /* being shut down */
resvport : 1; /* use a reserved port */
+ unsigned int swapper; /* we're swapping over this
+ transport */
unsigned int bind_index; /* bind function index */
/*
@@ -279,6 +282,8 @@ void xprt_connect(struct rpc_task *task);
void xprt_reserve(struct rpc_task *task);
int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task);
int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task);
+void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
+void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task);
int xprt_prepare_transmit(struct rpc_task *task);
void xprt_transmit(struct rpc_task *task);
void xprt_end_transmit(struct rpc_task *task);
@@ -316,6 +321,7 @@ void xprt_release_rqst_cong(struct rpc_task *task);
void xprt_disconnect_done(struct rpc_xprt *xprt);
void xprt_force_disconnect(struct rpc_xprt *xprt);
void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
+int xs_swapper(struct rpc_xprt *xprt, int enable);
/*
* Reserved bit positions in xprt->state
diff --git a/include/linux/swap.h b/include/linux/swap.h
index c84ec68eaec9..388e70601413 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -151,6 +151,7 @@ enum {
SWP_SOLIDSTATE = (1 << 4), /* blkdev seeks are cheap */
SWP_CONTINUED = (1 << 5), /* swap_map has count continuation */
SWP_BLKDEV = (1 << 6), /* its a block device */
+ SWP_FILE = (1 << 7), /* set after swap_activate success */
/* add others here before... */
SWP_SCANNING = (1 << 8), /* refcount in scan_swap_map */
};
@@ -301,7 +302,7 @@ static inline void scan_unevictable_unregister_node(struct node *node)
extern int kswapd_run(int nid);
extern void kswapd_stop(int nid);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
extern int mem_cgroup_swappiness(struct mem_cgroup *mem);
#else
static inline int mem_cgroup_swappiness(struct mem_cgroup *mem)
@@ -309,7 +310,7 @@ static inline int mem_cgroup_swappiness(struct mem_cgroup *mem)
return vm_swappiness;
}
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
extern void mem_cgroup_uncharge_swap(swp_entry_t ent);
#else
static inline void mem_cgroup_uncharge_swap(swp_entry_t ent)
@@ -320,8 +321,14 @@ static inline void mem_cgroup_uncharge_swap(swp_entry_t ent)
/* linux/mm/page_io.c */
extern int swap_readpage(struct page *);
extern int swap_writepage(struct page *page, struct writeback_control *wbc);
+extern int swap_set_page_dirty(struct page *page);
extern void end_swap_bio_read(struct bio *bio, int err);
+int add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
+ unsigned long nr_pages, sector_t start_block);
+int generic_swapfile_activate(struct swap_info_struct *, struct file *,
+ sector_t *);
+
/* linux/mm/swap_state.c */
extern struct address_space swapper_space;
#define total_swapcache_pages swapper_space.nrpages
@@ -356,11 +363,12 @@ extern unsigned int count_swap_pages(int, int);
extern sector_t map_swap_page(struct page *, struct block_device **);
extern sector_t swapdev_block(int, pgoff_t);
extern int page_swapcount(struct page *);
+extern struct swap_info_struct *page_swap_info(struct page *);
extern int reuse_swap_page(struct page *);
extern int try_to_free_swap(struct page *);
struct backing_dev_info;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
extern void
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout);
#else
diff --git a/include/linux/time.h b/include/linux/time.h
index c81c5e40fcb5..b51e664c83e7 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -107,11 +107,36 @@ static inline struct timespec timespec_sub(struct timespec lhs,
return ts_delta;
}
+#define KTIME_MAX ((s64)~((u64)1 << 63))
+#if (BITS_PER_LONG == 64)
+# define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC)
+#else
+# define KTIME_SEC_MAX LONG_MAX
+#endif
+
/*
* Returns true if the timespec is norm, false if denorm:
*/
-#define timespec_valid(ts) \
- (((ts)->tv_sec >= 0) && (((unsigned long) (ts)->tv_nsec) < NSEC_PER_SEC))
+static inline bool timespec_valid(const struct timespec *ts)
+{
+ /* Dates before 1970 are bogus */
+ if (ts->tv_sec < 0)
+ return false;
+ /* Can't have more nanoseconds then a second */
+ if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
+ return false;
+ return true;
+}
+
+static inline bool timespec_valid_strict(const struct timespec *ts)
+{
+ if (!timespec_valid(ts))
+ return false;
+ /* Disallow values that could overflow ktime_t */
+ if ((unsigned long long)ts->tv_sec >= KTIME_SEC_MAX)
+ return false;
+ return true;
+}
extern void read_persistent_clock(struct timespec *ts);
extern void read_boot_clock(struct timespec *ts);
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 99bc88b1fc02..7c5ceb20e03a 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -232,7 +232,7 @@ struct timex {
* estimated error = NTP dispersion.
*/
extern unsigned long tick_usec; /* USER_HZ period (usec) */
-extern unsigned long tick_nsec; /* ACTHZ period (nsec) */
+extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */
extern void ntp_init(void);
extern void ntp_clear(void);
diff --git a/include/linux/topology.h b/include/linux/topology.h
index e91cd43394df..fec12d667211 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -164,6 +164,7 @@ int arch_update_cpu_topology(void);
| 0*SD_SHARE_CPUPOWER \
| 0*SD_SHARE_PKG_RESOURCES \
| 0*SD_SERIALIZE \
+ | 1*SD_PREFER_SIBLING \
, \
.last_balance = jiffies, \
.balance_interval = 1, \
diff --git a/include/linux/v4l2-common.h b/include/linux/v4l2-common.h
index 0fa8b64c3cdb..4f0667e010dd 100644
--- a/include/linux/v4l2-common.h
+++ b/include/linux/v4l2-common.h
@@ -53,10 +53,10 @@
/* Backward compatibility target definitions --- to be removed. */
#define V4L2_SEL_TGT_CROP_ACTIVE V4L2_SEL_TGT_CROP
#define V4L2_SEL_TGT_COMPOSE_ACTIVE V4L2_SEL_TGT_COMPOSE
-#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL \
- V4L2_SEL_TGT_CROP
-#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL \
- V4L2_SEL_TGT_COMPOSE
+#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL V4L2_SEL_TGT_CROP
+#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL V4L2_SEL_TGT_COMPOSE
+#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS V4L2_SEL_TGT_CROP_BOUNDS
+#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS V4L2_SEL_TGT_COMPOSE_BOUNDS
/* Selection flags */
#define V4L2_SEL_FLAG_GE (1 << 0)
diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h
new file mode 100644
index 000000000000..421d24c7f686
--- /dev/null
+++ b/include/linux/v4l2-controls.h
@@ -0,0 +1,761 @@
+/*
+ * Video for Linux Two controls header file
+ *
+ * Copyright (C) 1999-2012 the contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Alternatively you can redistribute this file under the terms of the
+ * BSD license as stated below:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of its contributors may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The contents of this header was split off from videodev2.h. All control
+ * definitions should be added to this header, which is included by
+ * videodev2.h.
+ */
+
+#ifndef __LINUX_V4L2_CONTROLS_H
+#define __LINUX_V4L2_CONTROLS_H
+
+/* Control classes */
+#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
+#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */
+#define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */
+#define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */
+#define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */
+#define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */
+#define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */
+#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */
+#define V4L2_CTRL_CLASS_DV 0x00a00000 /* Digital Video controls */
+
+/* User-class control IDs */
+
+#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900)
+#define V4L2_CID_USER_BASE V4L2_CID_BASE
+#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1)
+#define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0)
+#define V4L2_CID_CONTRAST (V4L2_CID_BASE+1)
+#define V4L2_CID_SATURATION (V4L2_CID_BASE+2)
+#define V4L2_CID_HUE (V4L2_CID_BASE+3)
+#define V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5)
+#define V4L2_CID_AUDIO_BALANCE (V4L2_CID_BASE+6)
+#define V4L2_CID_AUDIO_BASS (V4L2_CID_BASE+7)
+#define V4L2_CID_AUDIO_TREBLE (V4L2_CID_BASE+8)
+#define V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9)
+#define V4L2_CID_AUDIO_LOUDNESS (V4L2_CID_BASE+10)
+#define V4L2_CID_BLACK_LEVEL (V4L2_CID_BASE+11) /* Deprecated */
+#define V4L2_CID_AUTO_WHITE_BALANCE (V4L2_CID_BASE+12)
+#define V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13)
+#define V4L2_CID_RED_BALANCE (V4L2_CID_BASE+14)
+#define V4L2_CID_BLUE_BALANCE (V4L2_CID_BASE+15)
+#define V4L2_CID_GAMMA (V4L2_CID_BASE+16)
+#define V4L2_CID_WHITENESS (V4L2_CID_GAMMA) /* Deprecated */
+#define V4L2_CID_EXPOSURE (V4L2_CID_BASE+17)
+#define V4L2_CID_AUTOGAIN (V4L2_CID_BASE+18)
+#define V4L2_CID_GAIN (V4L2_CID_BASE+19)
+#define V4L2_CID_HFLIP (V4L2_CID_BASE+20)
+#define V4L2_CID_VFLIP (V4L2_CID_BASE+21)
+
+/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */
+#define V4L2_CID_HCENTER (V4L2_CID_BASE+22)
+#define V4L2_CID_VCENTER (V4L2_CID_BASE+23)
+
+#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24)
+enum v4l2_power_line_frequency {
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ = 1,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ = 2,
+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO = 3,
+};
+#define V4L2_CID_HUE_AUTO (V4L2_CID_BASE+25)
+#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE+26)
+#define V4L2_CID_SHARPNESS (V4L2_CID_BASE+27)
+#define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_BASE+28)
+#define V4L2_CID_CHROMA_AGC (V4L2_CID_BASE+29)
+#define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30)
+#define V4L2_CID_COLORFX (V4L2_CID_BASE+31)
+enum v4l2_colorfx {
+ V4L2_COLORFX_NONE = 0,
+ V4L2_COLORFX_BW = 1,
+ V4L2_COLORFX_SEPIA = 2,
+ V4L2_COLORFX_NEGATIVE = 3,
+ V4L2_COLORFX_EMBOSS = 4,
+ V4L2_COLORFX_SKETCH = 5,
+ V4L2_COLORFX_SKY_BLUE = 6,
+ V4L2_COLORFX_GRASS_GREEN = 7,
+ V4L2_COLORFX_SKIN_WHITEN = 8,
+ V4L2_COLORFX_VIVID = 9,
+ V4L2_COLORFX_AQUA = 10,
+ V4L2_COLORFX_ART_FREEZE = 11,
+ V4L2_COLORFX_SILHOUETTE = 12,
+ V4L2_COLORFX_SOLARIZATION = 13,
+ V4L2_COLORFX_ANTIQUE = 14,
+ V4L2_COLORFX_SET_CBCR = 15,
+};
+#define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32)
+#define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33)
+
+#define V4L2_CID_ROTATE (V4L2_CID_BASE+34)
+#define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35)
+
+#define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36)
+
+#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37)
+#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38)
+
+#define V4L2_CID_MIN_BUFFERS_FOR_CAPTURE (V4L2_CID_BASE+39)
+#define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (V4L2_CID_BASE+40)
+
+#define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41)
+#define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42)
+
+/* last CID + 1 */
+#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43)
+
+
+/* MPEG-class control IDs */
+
+#define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900)
+#define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1)
+
+/* MPEG streams, specific to multiplexed streams */
+#define V4L2_CID_MPEG_STREAM_TYPE (V4L2_CID_MPEG_BASE+0)
+enum v4l2_mpeg_stream_type {
+ V4L2_MPEG_STREAM_TYPE_MPEG2_PS = 0, /* MPEG-2 program stream */
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS = 1, /* MPEG-2 transport stream */
+ V4L2_MPEG_STREAM_TYPE_MPEG1_SS = 2, /* MPEG-1 system stream */
+ V4L2_MPEG_STREAM_TYPE_MPEG2_DVD = 3, /* MPEG-2 DVD-compatible stream */
+ V4L2_MPEG_STREAM_TYPE_MPEG1_VCD = 4, /* MPEG-1 VCD-compatible stream */
+ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */
+};
+#define V4L2_CID_MPEG_STREAM_PID_PMT (V4L2_CID_MPEG_BASE+1)
+#define V4L2_CID_MPEG_STREAM_PID_AUDIO (V4L2_CID_MPEG_BASE+2)
+#define V4L2_CID_MPEG_STREAM_PID_VIDEO (V4L2_CID_MPEG_BASE+3)
+#define V4L2_CID_MPEG_STREAM_PID_PCR (V4L2_CID_MPEG_BASE+4)
+#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (V4L2_CID_MPEG_BASE+5)
+#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (V4L2_CID_MPEG_BASE+6)
+#define V4L2_CID_MPEG_STREAM_VBI_FMT (V4L2_CID_MPEG_BASE+7)
+enum v4l2_mpeg_stream_vbi_fmt {
+ V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV format */
+};
+
+/* MPEG audio controls specific to multiplexed streams */
+#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ (V4L2_CID_MPEG_BASE+100)
+enum v4l2_mpeg_audio_sampling_freq {
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1,
+ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2,
+};
+#define V4L2_CID_MPEG_AUDIO_ENCODING (V4L2_CID_MPEG_BASE+101)
+enum v4l2_mpeg_audio_encoding {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
+ V4L2_MPEG_AUDIO_ENCODING_AAC = 3,
+ V4L2_MPEG_AUDIO_ENCODING_AC3 = 4,
+};
+#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102)
+enum v4l2_mpeg_audio_l1_bitrate {
+ V4L2_MPEG_AUDIO_L1_BITRATE_32K = 0,
+ V4L2_MPEG_AUDIO_L1_BITRATE_64K = 1,
+ V4L2_MPEG_AUDIO_L1_BITRATE_96K = 2,
+ V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3,
+ V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4,
+ V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5,
+ V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6,
+ V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7,
+ V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8,
+ V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9,
+ V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10,
+ V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11,
+ V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12,
+ V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_L2_BITRATE (V4L2_CID_MPEG_BASE+103)
+enum v4l2_mpeg_audio_l2_bitrate {
+ V4L2_MPEG_AUDIO_L2_BITRATE_32K = 0,
+ V4L2_MPEG_AUDIO_L2_BITRATE_48K = 1,
+ V4L2_MPEG_AUDIO_L2_BITRATE_56K = 2,
+ V4L2_MPEG_AUDIO_L2_BITRATE_64K = 3,
+ V4L2_MPEG_AUDIO_L2_BITRATE_80K = 4,
+ V4L2_MPEG_AUDIO_L2_BITRATE_96K = 5,
+ V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6,
+ V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7,
+ V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8,
+ V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9,
+ V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10,
+ V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11,
+ V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_L3_BITRATE (V4L2_CID_MPEG_BASE+104)
+enum v4l2_mpeg_audio_l3_bitrate {
+ V4L2_MPEG_AUDIO_L3_BITRATE_32K = 0,
+ V4L2_MPEG_AUDIO_L3_BITRATE_40K = 1,
+ V4L2_MPEG_AUDIO_L3_BITRATE_48K = 2,
+ V4L2_MPEG_AUDIO_L3_BITRATE_56K = 3,
+ V4L2_MPEG_AUDIO_L3_BITRATE_64K = 4,
+ V4L2_MPEG_AUDIO_L3_BITRATE_80K = 5,
+ V4L2_MPEG_AUDIO_L3_BITRATE_96K = 6,
+ V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7,
+ V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8,
+ V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9,
+ V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10,
+ V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11,
+ V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12,
+ V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13,
+};
+#define V4L2_CID_MPEG_AUDIO_MODE (V4L2_CID_MPEG_BASE+105)
+enum v4l2_mpeg_audio_mode {
+ V4L2_MPEG_AUDIO_MODE_STEREO = 0,
+ V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1,
+ V4L2_MPEG_AUDIO_MODE_DUAL = 2,
+ V4L2_MPEG_AUDIO_MODE_MONO = 3,
+};
+#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION (V4L2_CID_MPEG_BASE+106)
+enum v4l2_mpeg_audio_mode_extension {
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4 = 0,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8 = 1,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2,
+ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3,
+};
+#define V4L2_CID_MPEG_AUDIO_EMPHASIS (V4L2_CID_MPEG_BASE+107)
+enum v4l2_mpeg_audio_emphasis {
+ V4L2_MPEG_AUDIO_EMPHASIS_NONE = 0,
+ V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1,
+ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17 = 2,
+};
+#define V4L2_CID_MPEG_AUDIO_CRC (V4L2_CID_MPEG_BASE+108)
+enum v4l2_mpeg_audio_crc {
+ V4L2_MPEG_AUDIO_CRC_NONE = 0,
+ V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
+};
+#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109)
+#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110)
+#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111)
+enum v4l2_mpeg_audio_ac3_bitrate {
+ V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_48K = 2,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_56K = 3,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_64K = 4,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_80K = 5,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_96K = 6,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
+};
+#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_MPEG_BASE+112)
+enum v4l2_mpeg_audio_dec_playback {
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO = 0,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO = 1,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT = 2,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT = 3,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO = 4,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO = 5,
+};
+#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_MPEG_BASE+113)
+
+/* MPEG video controls specific to multiplexed streams */
+#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200)
+enum v4l2_mpeg_video_encoding {
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
+};
+#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201)
+enum v4l2_mpeg_video_aspect {
+ V4L2_MPEG_VIDEO_ASPECT_1x1 = 0,
+ V4L2_MPEG_VIDEO_ASPECT_4x3 = 1,
+ V4L2_MPEG_VIDEO_ASPECT_16x9 = 2,
+ V4L2_MPEG_VIDEO_ASPECT_221x100 = 3,
+};
+#define V4L2_CID_MPEG_VIDEO_B_FRAMES (V4L2_CID_MPEG_BASE+202)
+#define V4L2_CID_MPEG_VIDEO_GOP_SIZE (V4L2_CID_MPEG_BASE+203)
+#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (V4L2_CID_MPEG_BASE+204)
+#define V4L2_CID_MPEG_VIDEO_PULLDOWN (V4L2_CID_MPEG_BASE+205)
+#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_MPEG_BASE+206)
+enum v4l2_mpeg_video_bitrate_mode {
+ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1,
+};
+#define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207)
+#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208)
+#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
+#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210)
+#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211)
+#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (V4L2_CID_MPEG_BASE+212)
+#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (V4L2_CID_MPEG_BASE+213)
+#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (V4L2_CID_MPEG_BASE+214)
+#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (V4L2_CID_MPEG_BASE+215)
+#define V4L2_CID_MPEG_VIDEO_HEADER_MODE (V4L2_CID_MPEG_BASE+216)
+enum v4l2_mpeg_video_header_mode {
+ V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE = 0,
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME = 1,
+
+};
+#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_MPEG_BASE+217)
+#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (V4L2_CID_MPEG_BASE+218)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (V4L2_CID_MPEG_BASE+219)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (V4L2_CID_MPEG_BASE+220)
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_MPEG_BASE+221)
+enum v4l2_mpeg_video_multi_slice_mode {
+ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0,
+ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1,
+ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2,
+};
+#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222)
+#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223)
+#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224)
+
+#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300)
+#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301)
+#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302)
+#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP (V4L2_CID_MPEG_BASE+303)
+#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP (V4L2_CID_MPEG_BASE+304)
+#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (V4L2_CID_MPEG_BASE+350)
+#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (V4L2_CID_MPEG_BASE+351)
+#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (V4L2_CID_MPEG_BASE+352)
+#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP (V4L2_CID_MPEG_BASE+353)
+#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP (V4L2_CID_MPEG_BASE+354)
+#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (V4L2_CID_MPEG_BASE+355)
+#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (V4L2_CID_MPEG_BASE+356)
+#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE (V4L2_CID_MPEG_BASE+357)
+enum v4l2_mpeg_video_h264_entropy_mode {
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC = 0,
+ V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC = 1,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (V4L2_CID_MPEG_BASE+358)
+#define V4L2_CID_MPEG_VIDEO_H264_LEVEL (V4L2_CID_MPEG_BASE+359)
+enum v4l2_mpeg_video_h264_level {
+ V4L2_MPEG_VIDEO_H264_LEVEL_1_0 = 0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_1B = 1,
+ V4L2_MPEG_VIDEO_H264_LEVEL_1_1 = 2,
+ V4L2_MPEG_VIDEO_H264_LEVEL_1_2 = 3,
+ V4L2_MPEG_VIDEO_H264_LEVEL_1_3 = 4,
+ V4L2_MPEG_VIDEO_H264_LEVEL_2_0 = 5,
+ V4L2_MPEG_VIDEO_H264_LEVEL_2_1 = 6,
+ V4L2_MPEG_VIDEO_H264_LEVEL_2_2 = 7,
+ V4L2_MPEG_VIDEO_H264_LEVEL_3_0 = 8,
+ V4L2_MPEG_VIDEO_H264_LEVEL_3_1 = 9,
+ V4L2_MPEG_VIDEO_H264_LEVEL_3_2 = 10,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_0 = 11,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_1 = 12,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_2 = 13,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_0 = 14,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360)
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361)
+#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE+362)
+enum v4l2_mpeg_video_h264_loop_filter_mode {
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED = 0,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED = 1,
+ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_PROFILE (V4L2_CID_MPEG_BASE+363)
+enum v4l2_mpeg_video_h264_profile {
+ V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE = 0,
+ V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE = 1,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MAIN = 2,
+ V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED = 3,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH = 4,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10 = 5,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422 = 6,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE = 7,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA = 8,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA = 9,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA = 10,
+ V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA = 11,
+ V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE = 12,
+ V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH = 13,
+ V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA = 14,
+ V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH = 15,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH = 16,
+};
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (V4L2_CID_MPEG_BASE+366)
+#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC (V4L2_CID_MPEG_BASE+367)
+enum v4l2_mpeg_video_h264_vui_sar_idc {
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED = 0,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 = 1,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 = 2,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 = 3,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 = 4,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 = 5,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 = 6,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 = 7,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 = 8,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 = 9,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 = 10,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 = 11,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 = 12,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 = 13,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 = 14,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 = 15,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 = 16,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED = 17,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (V4L2_CID_MPEG_BASE+403)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (V4L2_CID_MPEG_BASE+404)
+#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL (V4L2_CID_MPEG_BASE+405)
+enum v4l2_mpeg_video_mpeg4_level {
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 = 0,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B = 1,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 = 2,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 = 3,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 = 4,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B = 5,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 = 6,
+ V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 = 7,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE (V4L2_CID_MPEG_BASE+406)
+enum v4l2_mpeg_video_mpeg4_profile {
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE = 0,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE = 1,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE = 2,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE = 3,
+ V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY = 4,
+};
+#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407)
+
+/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
+#define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0)
+enum v4l2_mpeg_cx2341x_video_spatial_filter_mode {
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0,
+ V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+1)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+2)
+enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type {
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF = 0,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT = 2,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE = 3,
+ V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+3)
+enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type {
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF = 0,
+ V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+4)
+enum v4l2_mpeg_cx2341x_video_temporal_filter_mode {
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0,
+ V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO = 1,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+5)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+6)
+enum v4l2_mpeg_cx2341x_video_median_filter_type {
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF = 0,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR = 1,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT = 2,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3,
+ V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG = 4,
+};
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+7)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9)
+#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10)
+#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11)
+
+/* MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */
+#define V4L2_CID_MPEG_MFC51_BASE (V4L2_CTRL_CLASS_MPEG | 0x1100)
+
+#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_MPEG_MFC51_BASE+0)
+#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_MPEG_MFC51_BASE+1)
+#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE (V4L2_CID_MPEG_MFC51_BASE+2)
+enum v4l2_mpeg_mfc51_video_frame_skip_mode {
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED = 0,
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT = 1,
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT = 2,
+};
+#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE (V4L2_CID_MPEG_MFC51_BASE+3)
+enum v4l2_mpeg_mfc51_video_force_frame_type {
+ V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED = 0,
+ V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME = 1,
+ V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED = 2,
+};
+#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING (V4L2_CID_MPEG_MFC51_BASE+4)
+#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (V4L2_CID_MPEG_MFC51_BASE+5)
+#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (V4L2_CID_MPEG_MFC51_BASE+6)
+#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (V4L2_CID_MPEG_MFC51_BASE+7)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (V4L2_CID_MPEG_MFC51_BASE+50)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (V4L2_CID_MPEG_MFC51_BASE+51)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (V4L2_CID_MPEG_MFC51_BASE+52)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_MPEG_MFC51_BASE+53)
+#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_MPEG_MFC51_BASE+54)
+
+
+/* Camera class control IDs */
+
+#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
+#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
+
+#define V4L2_CID_EXPOSURE_AUTO (V4L2_CID_CAMERA_CLASS_BASE+1)
+enum v4l2_exposure_auto_type {
+ V4L2_EXPOSURE_AUTO = 0,
+ V4L2_EXPOSURE_MANUAL = 1,
+ V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
+ V4L2_EXPOSURE_APERTURE_PRIORITY = 3
+};
+#define V4L2_CID_EXPOSURE_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+2)
+#define V4L2_CID_EXPOSURE_AUTO_PRIORITY (V4L2_CID_CAMERA_CLASS_BASE+3)
+
+#define V4L2_CID_PAN_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+4)
+#define V4L2_CID_TILT_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+5)
+#define V4L2_CID_PAN_RESET (V4L2_CID_CAMERA_CLASS_BASE+6)
+#define V4L2_CID_TILT_RESET (V4L2_CID_CAMERA_CLASS_BASE+7)
+
+#define V4L2_CID_PAN_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+8)
+#define V4L2_CID_TILT_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+9)
+
+#define V4L2_CID_FOCUS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+10)
+#define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+11)
+#define V4L2_CID_FOCUS_AUTO (V4L2_CID_CAMERA_CLASS_BASE+12)
+
+#define V4L2_CID_ZOOM_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+13)
+#define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14)
+#define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15)
+
+#define V4L2_CID_PRIVACY (V4L2_CID_CAMERA_CLASS_BASE+16)
+
+#define V4L2_CID_IRIS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+17)
+#define V4L2_CID_IRIS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+18)
+
+#define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19)
+
+#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20)
+enum v4l2_auto_n_preset_white_balance {
+ V4L2_WHITE_BALANCE_MANUAL = 0,
+ V4L2_WHITE_BALANCE_AUTO = 1,
+ V4L2_WHITE_BALANCE_INCANDESCENT = 2,
+ V4L2_WHITE_BALANCE_FLUORESCENT = 3,
+ V4L2_WHITE_BALANCE_FLUORESCENT_H = 4,
+ V4L2_WHITE_BALANCE_HORIZON = 5,
+ V4L2_WHITE_BALANCE_DAYLIGHT = 6,
+ V4L2_WHITE_BALANCE_FLASH = 7,
+ V4L2_WHITE_BALANCE_CLOUDY = 8,
+ V4L2_WHITE_BALANCE_SHADE = 9,
+};
+
+#define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21)
+#define V4L2_CID_IMAGE_STABILIZATION (V4L2_CID_CAMERA_CLASS_BASE+22)
+
+#define V4L2_CID_ISO_SENSITIVITY (V4L2_CID_CAMERA_CLASS_BASE+23)
+#define V4L2_CID_ISO_SENSITIVITY_AUTO (V4L2_CID_CAMERA_CLASS_BASE+24)
+enum v4l2_iso_sensitivity_auto_type {
+ V4L2_ISO_SENSITIVITY_MANUAL = 0,
+ V4L2_ISO_SENSITIVITY_AUTO = 1,
+};
+
+#define V4L2_CID_EXPOSURE_METERING (V4L2_CID_CAMERA_CLASS_BASE+25)
+enum v4l2_exposure_metering {
+ V4L2_EXPOSURE_METERING_AVERAGE = 0,
+ V4L2_EXPOSURE_METERING_CENTER_WEIGHTED = 1,
+ V4L2_EXPOSURE_METERING_SPOT = 2,
+};
+
+#define V4L2_CID_SCENE_MODE (V4L2_CID_CAMERA_CLASS_BASE+26)
+enum v4l2_scene_mode {
+ V4L2_SCENE_MODE_NONE = 0,
+ V4L2_SCENE_MODE_BACKLIGHT = 1,
+ V4L2_SCENE_MODE_BEACH_SNOW = 2,
+ V4L2_SCENE_MODE_CANDLE_LIGHT = 3,
+ V4L2_SCENE_MODE_DAWN_DUSK = 4,
+ V4L2_SCENE_MODE_FALL_COLORS = 5,
+ V4L2_SCENE_MODE_FIREWORKS = 6,
+ V4L2_SCENE_MODE_LANDSCAPE = 7,
+ V4L2_SCENE_MODE_NIGHT = 8,
+ V4L2_SCENE_MODE_PARTY_INDOOR = 9,
+ V4L2_SCENE_MODE_PORTRAIT = 10,
+ V4L2_SCENE_MODE_SPORTS = 11,
+ V4L2_SCENE_MODE_SUNSET = 12,
+ V4L2_SCENE_MODE_TEXT = 13,
+};
+
+#define V4L2_CID_3A_LOCK (V4L2_CID_CAMERA_CLASS_BASE+27)
+#define V4L2_LOCK_EXPOSURE (1 << 0)
+#define V4L2_LOCK_WHITE_BALANCE (1 << 1)
+#define V4L2_LOCK_FOCUS (1 << 2)
+
+#define V4L2_CID_AUTO_FOCUS_START (V4L2_CID_CAMERA_CLASS_BASE+28)
+#define V4L2_CID_AUTO_FOCUS_STOP (V4L2_CID_CAMERA_CLASS_BASE+29)
+#define V4L2_CID_AUTO_FOCUS_STATUS (V4L2_CID_CAMERA_CLASS_BASE+30)
+#define V4L2_AUTO_FOCUS_STATUS_IDLE (0 << 0)
+#define V4L2_AUTO_FOCUS_STATUS_BUSY (1 << 0)
+#define V4L2_AUTO_FOCUS_STATUS_REACHED (1 << 1)
+#define V4L2_AUTO_FOCUS_STATUS_FAILED (1 << 2)
+
+#define V4L2_CID_AUTO_FOCUS_RANGE (V4L2_CID_CAMERA_CLASS_BASE+31)
+enum v4l2_auto_focus_range {
+ V4L2_AUTO_FOCUS_RANGE_AUTO = 0,
+ V4L2_AUTO_FOCUS_RANGE_NORMAL = 1,
+ V4L2_AUTO_FOCUS_RANGE_MACRO = 2,
+ V4L2_AUTO_FOCUS_RANGE_INFINITY = 3,
+};
+
+
+/* FM Modulator class control IDs */
+
+#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
+#define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1)
+
+#define V4L2_CID_RDS_TX_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 1)
+#define V4L2_CID_RDS_TX_PI (V4L2_CID_FM_TX_CLASS_BASE + 2)
+#define V4L2_CID_RDS_TX_PTY (V4L2_CID_FM_TX_CLASS_BASE + 3)
+#define V4L2_CID_RDS_TX_PS_NAME (V4L2_CID_FM_TX_CLASS_BASE + 5)
+#define V4L2_CID_RDS_TX_RADIO_TEXT (V4L2_CID_FM_TX_CLASS_BASE + 6)
+
+#define V4L2_CID_AUDIO_LIMITER_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 64)
+#define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 65)
+#define V4L2_CID_AUDIO_LIMITER_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 66)
+
+#define V4L2_CID_AUDIO_COMPRESSION_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 80)
+#define V4L2_CID_AUDIO_COMPRESSION_GAIN (V4L2_CID_FM_TX_CLASS_BASE + 81)
+#define V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (V4L2_CID_FM_TX_CLASS_BASE + 82)
+#define V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (V4L2_CID_FM_TX_CLASS_BASE + 83)
+#define V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 84)
+
+#define V4L2_CID_PILOT_TONE_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 96)
+#define V4L2_CID_PILOT_TONE_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 97)
+#define V4L2_CID_PILOT_TONE_FREQUENCY (V4L2_CID_FM_TX_CLASS_BASE + 98)
+
+#define V4L2_CID_TUNE_PREEMPHASIS (V4L2_CID_FM_TX_CLASS_BASE + 112)
+enum v4l2_preemphasis {
+ V4L2_PREEMPHASIS_DISABLED = 0,
+ V4L2_PREEMPHASIS_50_uS = 1,
+ V4L2_PREEMPHASIS_75_uS = 2,
+};
+#define V4L2_CID_TUNE_POWER_LEVEL (V4L2_CID_FM_TX_CLASS_BASE + 113)
+#define V4L2_CID_TUNE_ANTENNA_CAPACITOR (V4L2_CID_FM_TX_CLASS_BASE + 114)
+
+
+/* Flash and privacy (indicator) light controls */
+
+#define V4L2_CID_FLASH_CLASS_BASE (V4L2_CTRL_CLASS_FLASH | 0x900)
+#define V4L2_CID_FLASH_CLASS (V4L2_CTRL_CLASS_FLASH | 1)
+
+#define V4L2_CID_FLASH_LED_MODE (V4L2_CID_FLASH_CLASS_BASE + 1)
+enum v4l2_flash_led_mode {
+ V4L2_FLASH_LED_MODE_NONE,
+ V4L2_FLASH_LED_MODE_FLASH,
+ V4L2_FLASH_LED_MODE_TORCH,
+};
+
+#define V4L2_CID_FLASH_STROBE_SOURCE (V4L2_CID_FLASH_CLASS_BASE + 2)
+enum v4l2_flash_strobe_source {
+ V4L2_FLASH_STROBE_SOURCE_SOFTWARE,
+ V4L2_FLASH_STROBE_SOURCE_EXTERNAL,
+};
+
+#define V4L2_CID_FLASH_STROBE (V4L2_CID_FLASH_CLASS_BASE + 3)
+#define V4L2_CID_FLASH_STROBE_STOP (V4L2_CID_FLASH_CLASS_BASE + 4)
+#define V4L2_CID_FLASH_STROBE_STATUS (V4L2_CID_FLASH_CLASS_BASE + 5)
+
+#define V4L2_CID_FLASH_TIMEOUT (V4L2_CID_FLASH_CLASS_BASE + 6)
+#define V4L2_CID_FLASH_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 7)
+#define V4L2_CID_FLASH_TORCH_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 8)
+#define V4L2_CID_FLASH_INDICATOR_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 9)
+
+#define V4L2_CID_FLASH_FAULT (V4L2_CID_FLASH_CLASS_BASE + 10)
+#define V4L2_FLASH_FAULT_OVER_VOLTAGE (1 << 0)
+#define V4L2_FLASH_FAULT_TIMEOUT (1 << 1)
+#define V4L2_FLASH_FAULT_OVER_TEMPERATURE (1 << 2)
+#define V4L2_FLASH_FAULT_SHORT_CIRCUIT (1 << 3)
+#define V4L2_FLASH_FAULT_OVER_CURRENT (1 << 4)
+#define V4L2_FLASH_FAULT_INDICATOR (1 << 5)
+
+#define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11)
+#define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12)
+
+
+/* JPEG-class control IDs */
+
+#define V4L2_CID_JPEG_CLASS_BASE (V4L2_CTRL_CLASS_JPEG | 0x900)
+#define V4L2_CID_JPEG_CLASS (V4L2_CTRL_CLASS_JPEG | 1)
+
+#define V4L2_CID_JPEG_CHROMA_SUBSAMPLING (V4L2_CID_JPEG_CLASS_BASE + 1)
+enum v4l2_jpeg_chroma_subsampling {
+ V4L2_JPEG_CHROMA_SUBSAMPLING_444 = 0,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422 = 1,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_420 = 2,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_411 = 3,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_410 = 4,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY = 5,
+};
+#define V4L2_CID_JPEG_RESTART_INTERVAL (V4L2_CID_JPEG_CLASS_BASE + 2)
+#define V4L2_CID_JPEG_COMPRESSION_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 3)
+
+#define V4L2_CID_JPEG_ACTIVE_MARKER (V4L2_CID_JPEG_CLASS_BASE + 4)
+#define V4L2_JPEG_ACTIVE_MARKER_APP0 (1 << 0)
+#define V4L2_JPEG_ACTIVE_MARKER_APP1 (1 << 1)
+#define V4L2_JPEG_ACTIVE_MARKER_COM (1 << 16)
+#define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17)
+#define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18)
+
+/* Image source controls */
+#define V4L2_CID_IMAGE_SOURCE_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_SOURCE | 0x900)
+#define V4L2_CID_IMAGE_SOURCE_CLASS (V4L2_CTRL_CLASS_IMAGE_SOURCE | 1)
+
+#define V4L2_CID_VBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 1)
+#define V4L2_CID_HBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2)
+#define V4L2_CID_ANALOGUE_GAIN (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3)
+
+
+/* Image processing controls */
+
+#define V4L2_CID_IMAGE_PROC_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_PROC | 0x900)
+#define V4L2_CID_IMAGE_PROC_CLASS (V4L2_CTRL_CLASS_IMAGE_PROC | 1)
+
+#define V4L2_CID_LINK_FREQ (V4L2_CID_IMAGE_PROC_CLASS_BASE + 1)
+#define V4L2_CID_PIXEL_RATE (V4L2_CID_IMAGE_PROC_CLASS_BASE + 2)
+
+#endif
diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h
index 8c57ee9872bb..a33c4daadce3 100644
--- a/include/linux/v4l2-subdev.h
+++ b/include/linux/v4l2-subdev.h
@@ -148,6 +148,14 @@ struct v4l2_subdev_selection {
__u32 reserved[8];
};
+struct v4l2_subdev_edid {
+ __u32 pad;
+ __u32 start_block;
+ __u32 blocks;
+ __u32 reserved[5];
+ __u8 __user *edid;
+};
+
#define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format)
#define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format)
#define VIDIOC_SUBDEV_G_FRAME_INTERVAL \
@@ -166,5 +174,7 @@ struct v4l2_subdev_selection {
_IOWR('V', 61, struct v4l2_subdev_selection)
#define VIDIOC_SUBDEV_S_SELECTION \
_IOWR('V', 62, struct v4l2_subdev_selection)
+#define VIDIOC_SUBDEV_G_EDID _IOWR('V', 40, struct v4l2_subdev_edid)
+#define VIDIOC_SUBDEV_S_EDID _IOWR('V', 41, struct v4l2_subdev_edid)
#endif
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
new file mode 100644
index 000000000000..0a4f180a11d8
--- /dev/null
+++ b/include/linux/vfio.h
@@ -0,0 +1,445 @@
+/*
+ * VFIO API definition
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ * Author: Alex Williamson <alex.williamson@redhat.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.
+ */
+#ifndef VFIO_H
+#define VFIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VFIO_API_VERSION 0
+
+#ifdef __KERNEL__ /* Internal VFIO-core/bus driver API */
+
+#include <linux/iommu.h>
+#include <linux/mm.h>
+
+/**
+ * struct vfio_device_ops - VFIO bus driver device callbacks
+ *
+ * @open: Called when userspace creates new file descriptor for device
+ * @release: Called when userspace releases file descriptor for device
+ * @read: Perform read(2) on device file descriptor
+ * @write: Perform write(2) on device file descriptor
+ * @ioctl: Perform ioctl(2) on device file descriptor, supporting VFIO_DEVICE_*
+ * operations documented below
+ * @mmap: Perform mmap(2) on a region of the device file descriptor
+ */
+struct vfio_device_ops {
+ char *name;
+ int (*open)(void *device_data);
+ void (*release)(void *device_data);
+ ssize_t (*read)(void *device_data, char __user *buf,
+ size_t count, loff_t *ppos);
+ ssize_t (*write)(void *device_data, const char __user *buf,
+ size_t count, loff_t *size);
+ long (*ioctl)(void *device_data, unsigned int cmd,
+ unsigned long arg);
+ int (*mmap)(void *device_data, struct vm_area_struct *vma);
+};
+
+extern int vfio_add_group_dev(struct device *dev,
+ const struct vfio_device_ops *ops,
+ void *device_data);
+
+extern void *vfio_del_group_dev(struct device *dev);
+
+/**
+ * struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
+ */
+struct vfio_iommu_driver_ops {
+ char *name;
+ struct module *owner;
+ void *(*open)(unsigned long arg);
+ void (*release)(void *iommu_data);
+ ssize_t (*read)(void *iommu_data, char __user *buf,
+ size_t count, loff_t *ppos);
+ ssize_t (*write)(void *iommu_data, const char __user *buf,
+ size_t count, loff_t *size);
+ long (*ioctl)(void *iommu_data, unsigned int cmd,
+ unsigned long arg);
+ int (*mmap)(void *iommu_data, struct vm_area_struct *vma);
+ int (*attach_group)(void *iommu_data,
+ struct iommu_group *group);
+ void (*detach_group)(void *iommu_data,
+ struct iommu_group *group);
+
+};
+
+extern int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops);
+
+extern void vfio_unregister_iommu_driver(
+ const struct vfio_iommu_driver_ops *ops);
+
+/**
+ * offsetofend(TYPE, MEMBER)
+ *
+ * @TYPE: The type of the structure
+ * @MEMBER: The member within the structure to get the end offset of
+ *
+ * Simple helper macro for dealing with variable sized structures passed
+ * from user space. This allows us to easily determine if the provided
+ * structure is sized to include various fields.
+ */
+#define offsetofend(TYPE, MEMBER) ({ \
+ TYPE tmp; \
+ offsetof(TYPE, MEMBER) + sizeof(tmp.MEMBER); }) \
+
+#endif /* __KERNEL__ */
+
+/* Kernel & User level defines for VFIO IOCTLs. */
+
+/* Extensions */
+
+#define VFIO_TYPE1_IOMMU 1
+
+/*
+ * The IOCTL interface is designed for extensibility by embedding the
+ * structure length (argsz) and flags into structures passed between
+ * kernel and userspace. We therefore use the _IO() macro for these
+ * defines to avoid implicitly embedding a size into the ioctl request.
+ * As structure fields are added, argsz will increase to match and flag
+ * bits will be defined to indicate additional fields with valid data.
+ * It's *always* the caller's responsibility to indicate the size of
+ * the structure passed by setting argsz appropriately.
+ */
+
+#define VFIO_TYPE (';')
+#define VFIO_BASE 100
+
+/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */
+
+/**
+ * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0)
+ *
+ * Report the version of the VFIO API. This allows us to bump the entire
+ * API version should we later need to add or change features in incompatible
+ * ways.
+ * Return: VFIO_API_VERSION
+ * Availability: Always
+ */
+#define VFIO_GET_API_VERSION _IO(VFIO_TYPE, VFIO_BASE + 0)
+
+/**
+ * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32)
+ *
+ * Check whether an extension is supported.
+ * Return: 0 if not supported, 1 (or some other positive integer) if supported.
+ * Availability: Always
+ */
+#define VFIO_CHECK_EXTENSION _IO(VFIO_TYPE, VFIO_BASE + 1)
+
+/**
+ * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32)
+ *
+ * Set the iommu to the given type. The type must be supported by an
+ * iommu driver as verified by calling CHECK_EXTENSION using the same
+ * type. A group must be set to this file descriptor before this
+ * ioctl is available. The IOMMU interfaces enabled by this call are
+ * specific to the value set.
+ * Return: 0 on success, -errno on failure
+ * Availability: When VFIO group attached
+ */
+#define VFIO_SET_IOMMU _IO(VFIO_TYPE, VFIO_BASE + 2)
+
+/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */
+
+/**
+ * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3,
+ * struct vfio_group_status)
+ *
+ * Retrieve information about the group. Fills in provided
+ * struct vfio_group_info. Caller sets argsz.
+ * Return: 0 on succes, -errno on failure.
+ * Availability: Always
+ */
+struct vfio_group_status {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_GROUP_FLAGS_VIABLE (1 << 0)
+#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1)
+};
+#define VFIO_GROUP_GET_STATUS _IO(VFIO_TYPE, VFIO_BASE + 3)
+
+/**
+ * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32)
+ *
+ * Set the container for the VFIO group to the open VFIO file
+ * descriptor provided. Groups may only belong to a single
+ * container. Containers may, at their discretion, support multiple
+ * groups. Only when a container is set are all of the interfaces
+ * of the VFIO file descriptor and the VFIO group file descriptor
+ * available to the user.
+ * Return: 0 on success, -errno on failure.
+ * Availability: Always
+ */
+#define VFIO_GROUP_SET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 4)
+
+/**
+ * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5)
+ *
+ * Remove the group from the attached container. This is the
+ * opposite of the SET_CONTAINER call and returns the group to
+ * an initial state. All device file descriptors must be released
+ * prior to calling this interface. When removing the last group
+ * from a container, the IOMMU will be disabled and all state lost,
+ * effectively also returning the VFIO file descriptor to an initial
+ * state.
+ * Return: 0 on success, -errno on failure.
+ * Availability: When attached to container
+ */
+#define VFIO_GROUP_UNSET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 5)
+
+/**
+ * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char)
+ *
+ * Return a new file descriptor for the device object described by
+ * the provided string. The string should match a device listed in
+ * the devices subdirectory of the IOMMU group sysfs entry. The
+ * group containing the device must already be added to this context.
+ * Return: new file descriptor on success, -errno on failure.
+ * Availability: When attached to container
+ */
+#define VFIO_GROUP_GET_DEVICE_FD _IO(VFIO_TYPE, VFIO_BASE + 6)
+
+/* --------------- IOCTLs for DEVICE file descriptors --------------- */
+
+/**
+ * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7,
+ * struct vfio_device_info)
+ *
+ * Retrieve information about the device. Fills in provided
+ * struct vfio_device_info. Caller sets argsz.
+ * Return: 0 on success, -errno on failure.
+ */
+struct vfio_device_info {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */
+#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */
+ __u32 num_regions; /* Max region index + 1 */
+ __u32 num_irqs; /* Max IRQ index + 1 */
+};
+#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7)
+
+/**
+ * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8,
+ * struct vfio_region_info)
+ *
+ * Retrieve information about a device region. Caller provides
+ * struct vfio_region_info with index value set. Caller sets argsz.
+ * Implementation of region mapping is bus driver specific. This is
+ * intended to describe MMIO, I/O port, as well as bus specific
+ * regions (ex. PCI config space). Zero sized regions may be used
+ * to describe unimplemented regions (ex. unimplemented PCI BARs).
+ * Return: 0 on success, -errno on failure.
+ */
+struct vfio_region_info {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */
+#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */
+#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */
+ __u32 index; /* Region index */
+ __u32 resv; /* Reserved for alignment */
+ __u64 size; /* Region size (bytes) */
+ __u64 offset; /* Region offset from start of device fd */
+};
+#define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8)
+
+/**
+ * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
+ * struct vfio_irq_info)
+ *
+ * Retrieve information about a device IRQ. Caller provides
+ * struct vfio_irq_info with index value set. Caller sets argsz.
+ * Implementation of IRQ mapping is bus driver specific. Indexes
+ * using multiple IRQs are primarily intended to support MSI-like
+ * interrupt blocks. Zero count irq blocks may be used to describe
+ * unimplemented interrupt types.
+ *
+ * The EVENTFD flag indicates the interrupt index supports eventfd based
+ * signaling.
+ *
+ * The MASKABLE flags indicates the index supports MASK and UNMASK
+ * actions described below.
+ *
+ * AUTOMASKED indicates that after signaling, the interrupt line is
+ * automatically masked by VFIO and the user needs to unmask the line
+ * to receive new interrupts. This is primarily intended to distinguish
+ * level triggered interrupts.
+ *
+ * The NORESIZE flag indicates that the interrupt lines within the index
+ * are setup as a set and new subindexes cannot be enabled without first
+ * disabling the entire index. This is used for interrupts like PCI MSI
+ * and MSI-X where the driver may only use a subset of the available
+ * indexes, but VFIO needs to enable a specific number of vectors
+ * upfront. In the case of MSI-X, where the user can enable MSI-X and
+ * then add and unmask vectors, it's up to userspace to make the decision
+ * whether to allocate the maximum supported number of vectors or tear
+ * down setup and incrementally increase the vectors as each is enabled.
+ */
+struct vfio_irq_info {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_IRQ_INFO_EVENTFD (1 << 0)
+#define VFIO_IRQ_INFO_MASKABLE (1 << 1)
+#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2)
+#define VFIO_IRQ_INFO_NORESIZE (1 << 3)
+ __u32 index; /* IRQ index */
+ __u32 count; /* Number of IRQs within this index */
+};
+#define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9)
+
+/**
+ * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set)
+ *
+ * Set signaling, masking, and unmasking of interrupts. Caller provides
+ * struct vfio_irq_set with all fields set. 'start' and 'count' indicate
+ * the range of subindexes being specified.
+ *
+ * The DATA flags specify the type of data provided. If DATA_NONE, the
+ * operation performs the specified action immediately on the specified
+ * interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]:
+ * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1.
+ *
+ * DATA_BOOL allows sparse support for the same on arrays of interrupts.
+ * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]):
+ * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3,
+ * data = {1,0,1}
+ *
+ * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd.
+ * A value of -1 can be used to either de-assign interrupts if already
+ * assigned or skip un-assigned interrupts. For example, to set an eventfd
+ * to be trigger for interrupts [0,0] and [0,2]:
+ * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3,
+ * data = {fd1, -1, fd2}
+ * If index [0,1] is previously set, two count = 1 ioctls calls would be
+ * required to set [0,0] and [0,2] without changing [0,1].
+ *
+ * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used
+ * with ACTION_TRIGGER to perform kernel level interrupt loopback testing
+ * from userspace (ie. simulate hardware triggering).
+ *
+ * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER
+ * enables the interrupt index for the device. Individual subindex interrupts
+ * can be disabled using the -1 value for DATA_EVENTFD or the index can be
+ * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0.
+ *
+ * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while
+ * ACTION_TRIGGER specifies kernel->user signaling.
+ */
+struct vfio_irq_set {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */
+#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */
+#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */
+#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */
+#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */
+#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */
+ __u32 index;
+ __u32 start;
+ __u32 count;
+ __u8 data[];
+};
+#define VFIO_DEVICE_SET_IRQS _IO(VFIO_TYPE, VFIO_BASE + 10)
+
+#define VFIO_IRQ_SET_DATA_TYPE_MASK (VFIO_IRQ_SET_DATA_NONE | \
+ VFIO_IRQ_SET_DATA_BOOL | \
+ VFIO_IRQ_SET_DATA_EVENTFD)
+#define VFIO_IRQ_SET_ACTION_TYPE_MASK (VFIO_IRQ_SET_ACTION_MASK | \
+ VFIO_IRQ_SET_ACTION_UNMASK | \
+ VFIO_IRQ_SET_ACTION_TRIGGER)
+/**
+ * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11)
+ *
+ * Reset a device.
+ */
+#define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11)
+
+/*
+ * The VFIO-PCI bus driver makes use of the following fixed region and
+ * IRQ index mapping. Unimplemented regions return a size of zero.
+ * Unimplemented IRQ types return a count of zero.
+ */
+
+enum {
+ VFIO_PCI_BAR0_REGION_INDEX,
+ VFIO_PCI_BAR1_REGION_INDEX,
+ VFIO_PCI_BAR2_REGION_INDEX,
+ VFIO_PCI_BAR3_REGION_INDEX,
+ VFIO_PCI_BAR4_REGION_INDEX,
+ VFIO_PCI_BAR5_REGION_INDEX,
+ VFIO_PCI_ROM_REGION_INDEX,
+ VFIO_PCI_CONFIG_REGION_INDEX,
+ VFIO_PCI_NUM_REGIONS
+};
+
+enum {
+ VFIO_PCI_INTX_IRQ_INDEX,
+ VFIO_PCI_MSI_IRQ_INDEX,
+ VFIO_PCI_MSIX_IRQ_INDEX,
+ VFIO_PCI_NUM_IRQS
+};
+
+/* -------- API for Type1 VFIO IOMMU -------- */
+
+/**
+ * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info)
+ *
+ * Retrieve information about the IOMMU object. Fills in provided
+ * struct vfio_iommu_info. Caller sets argsz.
+ *
+ * XXX Should we do these by CHECK_EXTENSION too?
+ */
+struct vfio_iommu_type1_info {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */
+ __u64 iova_pgsizes; /* Bitmap of supported page sizes */
+};
+
+#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12)
+
+/**
+ * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map)
+ *
+ * Map process virtual addresses to IO virtual addresses using the
+ * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required.
+ */
+struct vfio_iommu_type1_dma_map {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */
+#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */
+ __u64 vaddr; /* Process virtual address */
+ __u64 iova; /* IO virtual address */
+ __u64 size; /* Size of mapping (bytes) */
+};
+
+#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13)
+
+/**
+ * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap)
+ *
+ * Unmap IO virtual addresses using the provided struct vfio_dma_unmap.
+ * Caller sets argsz.
+ */
+struct vfio_iommu_type1_dma_unmap {
+ __u32 argsz;
+ __u32 flags;
+ __u64 iova; /* IO virtual address */
+ __u64 size; /* Size of mapping (bytes) */
+};
+
+#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
+
+#endif /* VFIO_H */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 7a147c8299ab..61395ef85a00 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1,7 +1,7 @@
/*
* Video for Linux Two header file
*
- * Copyright (C) 1999-2007 the contributors
+ * Copyright (C) 1999-2012 the contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -65,6 +65,7 @@
#include <linux/ioctl.h>
#include <linux/types.h>
#include <linux/v4l2-common.h>
+#include <linux/v4l2-controls.h>
/*
* Common stuff for both V4L1 and V4L2
@@ -161,6 +162,7 @@ enum v4l2_buf_type {
#endif
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE = 9,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE = 10,
+ /* Deprecated, do not use */
V4L2_BUF_TYPE_PRIVATE = 0x80,
};
@@ -368,6 +370,7 @@ struct v4l2_pix_format {
/* three non contiguous planes - Y, Cb, Cr */
#define V4L2_PIX_FMT_YUV420M v4l2_fourcc('Y', 'M', '1', '2') /* 12 YUV420 planar */
+#define V4L2_PIX_FMT_YVU420M v4l2_fourcc('Y', 'M', '2', '1') /* 12 YVU420 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.. */
@@ -1188,7 +1191,8 @@ struct v4l2_input {
/* capabilities flags */
#define V4L2_IN_CAP_PRESETS 0x00000001 /* Supports S_DV_PRESET */
-#define V4L2_IN_CAP_CUSTOM_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_IN_CAP_DV_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_IN_CAP_CUSTOM_TIMINGS V4L2_IN_CAP_DV_TIMINGS /* For compatibility */
#define V4L2_IN_CAP_STD 0x00000004 /* Supports S_STD */
/*
@@ -1211,7 +1215,8 @@ struct v4l2_output {
/* capabilities flags */
#define V4L2_OUT_CAP_PRESETS 0x00000001 /* Supports S_DV_PRESET */
-#define V4L2_OUT_CAP_CUSTOM_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_OUT_CAP_DV_TIMINGS 0x00000002 /* Supports S_DV_TIMINGS */
+#define V4L2_OUT_CAP_CUSTOM_TIMINGS V4L2_OUT_CAP_DV_TIMINGS /* For compatibility */
#define V4L2_OUT_CAP_STD 0x00000004 /* Supports S_STD */
/*
@@ -1241,16 +1246,6 @@ struct v4l2_ext_controls {
struct v4l2_ext_control *controls;
};
-/* Values for ctrl_class field */
-#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
-#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */
-#define V4L2_CTRL_CLASS_CAMERA 0x009a0000 /* Camera class controls */
-#define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */
-#define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */
-#define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */
-#define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */
-#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */
-
#define V4L2_CTRL_ID_MASK (0x0fffffff)
#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL)
#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000)
@@ -1306,692 +1301,31 @@ struct v4l2_querymenu {
/* User-class control IDs defined by V4L2 */
#define V4L2_CID_MAX_CTRLS 1024
-#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900)
-#define V4L2_CID_USER_BASE V4L2_CID_BASE
/* IDs reserved for driver specific controls */
#define V4L2_CID_PRIVATE_BASE 0x08000000
-#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1)
-#define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0)
-#define V4L2_CID_CONTRAST (V4L2_CID_BASE+1)
-#define V4L2_CID_SATURATION (V4L2_CID_BASE+2)
-#define V4L2_CID_HUE (V4L2_CID_BASE+3)
-#define V4L2_CID_AUDIO_VOLUME (V4L2_CID_BASE+5)
-#define V4L2_CID_AUDIO_BALANCE (V4L2_CID_BASE+6)
-#define V4L2_CID_AUDIO_BASS (V4L2_CID_BASE+7)
-#define V4L2_CID_AUDIO_TREBLE (V4L2_CID_BASE+8)
-#define V4L2_CID_AUDIO_MUTE (V4L2_CID_BASE+9)
-#define V4L2_CID_AUDIO_LOUDNESS (V4L2_CID_BASE+10)
-#define V4L2_CID_BLACK_LEVEL (V4L2_CID_BASE+11) /* Deprecated */
-#define V4L2_CID_AUTO_WHITE_BALANCE (V4L2_CID_BASE+12)
-#define V4L2_CID_DO_WHITE_BALANCE (V4L2_CID_BASE+13)
-#define V4L2_CID_RED_BALANCE (V4L2_CID_BASE+14)
-#define V4L2_CID_BLUE_BALANCE (V4L2_CID_BASE+15)
-#define V4L2_CID_GAMMA (V4L2_CID_BASE+16)
-#define V4L2_CID_WHITENESS (V4L2_CID_GAMMA) /* Deprecated */
-#define V4L2_CID_EXPOSURE (V4L2_CID_BASE+17)
-#define V4L2_CID_AUTOGAIN (V4L2_CID_BASE+18)
-#define V4L2_CID_GAIN (V4L2_CID_BASE+19)
-#define V4L2_CID_HFLIP (V4L2_CID_BASE+20)
-#define V4L2_CID_VFLIP (V4L2_CID_BASE+21)
-
-/* Deprecated; use V4L2_CID_PAN_RESET and V4L2_CID_TILT_RESET */
-#define V4L2_CID_HCENTER (V4L2_CID_BASE+22)
-#define V4L2_CID_VCENTER (V4L2_CID_BASE+23)
-
-#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24)
-enum v4l2_power_line_frequency {
- V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0,
- V4L2_CID_POWER_LINE_FREQUENCY_50HZ = 1,
- V4L2_CID_POWER_LINE_FREQUENCY_60HZ = 2,
- V4L2_CID_POWER_LINE_FREQUENCY_AUTO = 3,
-};
-#define V4L2_CID_HUE_AUTO (V4L2_CID_BASE+25)
-#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE+26)
-#define V4L2_CID_SHARPNESS (V4L2_CID_BASE+27)
-#define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_BASE+28)
-#define V4L2_CID_CHROMA_AGC (V4L2_CID_BASE+29)
-#define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30)
-#define V4L2_CID_COLORFX (V4L2_CID_BASE+31)
-enum v4l2_colorfx {
- V4L2_COLORFX_NONE = 0,
- V4L2_COLORFX_BW = 1,
- V4L2_COLORFX_SEPIA = 2,
- V4L2_COLORFX_NEGATIVE = 3,
- V4L2_COLORFX_EMBOSS = 4,
- V4L2_COLORFX_SKETCH = 5,
- V4L2_COLORFX_SKY_BLUE = 6,
- V4L2_COLORFX_GRASS_GREEN = 7,
- V4L2_COLORFX_SKIN_WHITEN = 8,
- V4L2_COLORFX_VIVID = 9,
- V4L2_COLORFX_AQUA = 10,
- V4L2_COLORFX_ART_FREEZE = 11,
- V4L2_COLORFX_SILHOUETTE = 12,
- V4L2_COLORFX_SOLARIZATION = 13,
- V4L2_COLORFX_ANTIQUE = 14,
- V4L2_COLORFX_SET_CBCR = 15,
-};
-#define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32)
-#define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33)
-
-#define V4L2_CID_ROTATE (V4L2_CID_BASE+34)
-#define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35)
-
-#define V4L2_CID_CHROMA_GAIN (V4L2_CID_BASE+36)
-
-#define V4L2_CID_ILLUMINATORS_1 (V4L2_CID_BASE+37)
-#define V4L2_CID_ILLUMINATORS_2 (V4L2_CID_BASE+38)
-
-#define V4L2_CID_MIN_BUFFERS_FOR_CAPTURE (V4L2_CID_BASE+39)
-#define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (V4L2_CID_BASE+40)
-
-#define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41)
-#define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42)
-
-/* last CID + 1 */
-#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43)
-
-/* MPEG-class control IDs defined by V4L2 */
-#define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900)
-#define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1)
-
-/* MPEG streams, specific to multiplexed streams */
-#define V4L2_CID_MPEG_STREAM_TYPE (V4L2_CID_MPEG_BASE+0)
-enum v4l2_mpeg_stream_type {
- V4L2_MPEG_STREAM_TYPE_MPEG2_PS = 0, /* MPEG-2 program stream */
- V4L2_MPEG_STREAM_TYPE_MPEG2_TS = 1, /* MPEG-2 transport stream */
- V4L2_MPEG_STREAM_TYPE_MPEG1_SS = 2, /* MPEG-1 system stream */
- V4L2_MPEG_STREAM_TYPE_MPEG2_DVD = 3, /* MPEG-2 DVD-compatible stream */
- V4L2_MPEG_STREAM_TYPE_MPEG1_VCD = 4, /* MPEG-1 VCD-compatible stream */
- V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */
-};
-#define V4L2_CID_MPEG_STREAM_PID_PMT (V4L2_CID_MPEG_BASE+1)
-#define V4L2_CID_MPEG_STREAM_PID_AUDIO (V4L2_CID_MPEG_BASE+2)
-#define V4L2_CID_MPEG_STREAM_PID_VIDEO (V4L2_CID_MPEG_BASE+3)
-#define V4L2_CID_MPEG_STREAM_PID_PCR (V4L2_CID_MPEG_BASE+4)
-#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (V4L2_CID_MPEG_BASE+5)
-#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (V4L2_CID_MPEG_BASE+6)
-#define V4L2_CID_MPEG_STREAM_VBI_FMT (V4L2_CID_MPEG_BASE+7)
-enum v4l2_mpeg_stream_vbi_fmt {
- V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */
- V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV format */
-};
-
-/* MPEG audio controls specific to multiplexed streams */
-#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ (V4L2_CID_MPEG_BASE+100)
-enum v4l2_mpeg_audio_sampling_freq {
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0,
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1,
- V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2,
-};
-#define V4L2_CID_MPEG_AUDIO_ENCODING (V4L2_CID_MPEG_BASE+101)
-enum v4l2_mpeg_audio_encoding {
- V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1,
- V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2,
- V4L2_MPEG_AUDIO_ENCODING_AAC = 3,
- V4L2_MPEG_AUDIO_ENCODING_AC3 = 4,
-};
-#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102)
-enum v4l2_mpeg_audio_l1_bitrate {
- V4L2_MPEG_AUDIO_L1_BITRATE_32K = 0,
- V4L2_MPEG_AUDIO_L1_BITRATE_64K = 1,
- V4L2_MPEG_AUDIO_L1_BITRATE_96K = 2,
- V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3,
- V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4,
- V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5,
- V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6,
- V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7,
- V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8,
- V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9,
- V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10,
- V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11,
- V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12,
- V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_L2_BITRATE (V4L2_CID_MPEG_BASE+103)
-enum v4l2_mpeg_audio_l2_bitrate {
- V4L2_MPEG_AUDIO_L2_BITRATE_32K = 0,
- V4L2_MPEG_AUDIO_L2_BITRATE_48K = 1,
- V4L2_MPEG_AUDIO_L2_BITRATE_56K = 2,
- V4L2_MPEG_AUDIO_L2_BITRATE_64K = 3,
- V4L2_MPEG_AUDIO_L2_BITRATE_80K = 4,
- V4L2_MPEG_AUDIO_L2_BITRATE_96K = 5,
- V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6,
- V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7,
- V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8,
- V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9,
- V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10,
- V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11,
- V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12,
- V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_L3_BITRATE (V4L2_CID_MPEG_BASE+104)
-enum v4l2_mpeg_audio_l3_bitrate {
- V4L2_MPEG_AUDIO_L3_BITRATE_32K = 0,
- V4L2_MPEG_AUDIO_L3_BITRATE_40K = 1,
- V4L2_MPEG_AUDIO_L3_BITRATE_48K = 2,
- V4L2_MPEG_AUDIO_L3_BITRATE_56K = 3,
- V4L2_MPEG_AUDIO_L3_BITRATE_64K = 4,
- V4L2_MPEG_AUDIO_L3_BITRATE_80K = 5,
- V4L2_MPEG_AUDIO_L3_BITRATE_96K = 6,
- V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7,
- V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8,
- V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9,
- V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10,
- V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11,
- V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12,
- V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13,
-};
-#define V4L2_CID_MPEG_AUDIO_MODE (V4L2_CID_MPEG_BASE+105)
-enum v4l2_mpeg_audio_mode {
- V4L2_MPEG_AUDIO_MODE_STEREO = 0,
- V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1,
- V4L2_MPEG_AUDIO_MODE_DUAL = 2,
- V4L2_MPEG_AUDIO_MODE_MONO = 3,
-};
-#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION (V4L2_CID_MPEG_BASE+106)
-enum v4l2_mpeg_audio_mode_extension {
- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4 = 0,
- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8 = 1,
- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2,
- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3,
-};
-#define V4L2_CID_MPEG_AUDIO_EMPHASIS (V4L2_CID_MPEG_BASE+107)
-enum v4l2_mpeg_audio_emphasis {
- V4L2_MPEG_AUDIO_EMPHASIS_NONE = 0,
- V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1,
- V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17 = 2,
-};
-#define V4L2_CID_MPEG_AUDIO_CRC (V4L2_CID_MPEG_BASE+108)
-enum v4l2_mpeg_audio_crc {
- V4L2_MPEG_AUDIO_CRC_NONE = 0,
- V4L2_MPEG_AUDIO_CRC_CRC16 = 1,
-};
-#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109)
-#define V4L2_CID_MPEG_AUDIO_AAC_BITRATE (V4L2_CID_MPEG_BASE+110)
-#define V4L2_CID_MPEG_AUDIO_AC3_BITRATE (V4L2_CID_MPEG_BASE+111)
-enum v4l2_mpeg_audio_ac3_bitrate {
- V4L2_MPEG_AUDIO_AC3_BITRATE_32K = 0,
- V4L2_MPEG_AUDIO_AC3_BITRATE_40K = 1,
- V4L2_MPEG_AUDIO_AC3_BITRATE_48K = 2,
- V4L2_MPEG_AUDIO_AC3_BITRATE_56K = 3,
- V4L2_MPEG_AUDIO_AC3_BITRATE_64K = 4,
- V4L2_MPEG_AUDIO_AC3_BITRATE_80K = 5,
- V4L2_MPEG_AUDIO_AC3_BITRATE_96K = 6,
- V4L2_MPEG_AUDIO_AC3_BITRATE_112K = 7,
- V4L2_MPEG_AUDIO_AC3_BITRATE_128K = 8,
- V4L2_MPEG_AUDIO_AC3_BITRATE_160K = 9,
- V4L2_MPEG_AUDIO_AC3_BITRATE_192K = 10,
- V4L2_MPEG_AUDIO_AC3_BITRATE_224K = 11,
- V4L2_MPEG_AUDIO_AC3_BITRATE_256K = 12,
- V4L2_MPEG_AUDIO_AC3_BITRATE_320K = 13,
- V4L2_MPEG_AUDIO_AC3_BITRATE_384K = 14,
- V4L2_MPEG_AUDIO_AC3_BITRATE_448K = 15,
- V4L2_MPEG_AUDIO_AC3_BITRATE_512K = 16,
- V4L2_MPEG_AUDIO_AC3_BITRATE_576K = 17,
- V4L2_MPEG_AUDIO_AC3_BITRATE_640K = 18,
-};
-#define V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK (V4L2_CID_MPEG_BASE+112)
-enum v4l2_mpeg_audio_dec_playback {
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO = 0,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO = 1,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT = 2,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_RIGHT = 3,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_MONO = 4,
- V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO = 5,
-};
-#define V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK (V4L2_CID_MPEG_BASE+113)
-
-/* MPEG video controls specific to multiplexed streams */
-#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200)
-enum v4l2_mpeg_video_encoding {
- V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC = 2,
-};
-#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201)
-enum v4l2_mpeg_video_aspect {
- V4L2_MPEG_VIDEO_ASPECT_1x1 = 0,
- V4L2_MPEG_VIDEO_ASPECT_4x3 = 1,
- V4L2_MPEG_VIDEO_ASPECT_16x9 = 2,
- V4L2_MPEG_VIDEO_ASPECT_221x100 = 3,
-};
-#define V4L2_CID_MPEG_VIDEO_B_FRAMES (V4L2_CID_MPEG_BASE+202)
-#define V4L2_CID_MPEG_VIDEO_GOP_SIZE (V4L2_CID_MPEG_BASE+203)
-#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (V4L2_CID_MPEG_BASE+204)
-#define V4L2_CID_MPEG_VIDEO_PULLDOWN (V4L2_CID_MPEG_BASE+205)
-#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_MPEG_BASE+206)
-enum v4l2_mpeg_video_bitrate_mode {
- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0,
- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1,
-};
-#define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207)
-#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208)
-#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209)
-#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210)
-#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211)
-#define V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE (V4L2_CID_MPEG_BASE+212)
-#define V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER (V4L2_CID_MPEG_BASE+213)
-#define V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB (V4L2_CID_MPEG_BASE+214)
-#define V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE (V4L2_CID_MPEG_BASE+215)
-#define V4L2_CID_MPEG_VIDEO_HEADER_MODE (V4L2_CID_MPEG_BASE+216)
-enum v4l2_mpeg_video_header_mode {
- V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE = 0,
- V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME = 1,
-
-};
-#define V4L2_CID_MPEG_VIDEO_MAX_REF_PIC (V4L2_CID_MPEG_BASE+217)
-#define V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE (V4L2_CID_MPEG_BASE+218)
-#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES (V4L2_CID_MPEG_BASE+219)
-#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB (V4L2_CID_MPEG_BASE+220)
-#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE (V4L2_CID_MPEG_BASE+221)
-enum v4l2_mpeg_video_multi_slice_mode {
- V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE = 0,
- V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB = 1,
- V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES = 2,
-};
-#define V4L2_CID_MPEG_VIDEO_VBV_SIZE (V4L2_CID_MPEG_BASE+222)
-#define V4L2_CID_MPEG_VIDEO_DEC_PTS (V4L2_CID_MPEG_BASE+223)
-#define V4L2_CID_MPEG_VIDEO_DEC_FRAME (V4L2_CID_MPEG_BASE+224)
-
-#define V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP (V4L2_CID_MPEG_BASE+300)
-#define V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP (V4L2_CID_MPEG_BASE+301)
-#define V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP (V4L2_CID_MPEG_BASE+302)
-#define V4L2_CID_MPEG_VIDEO_H263_MIN_QP (V4L2_CID_MPEG_BASE+303)
-#define V4L2_CID_MPEG_VIDEO_H263_MAX_QP (V4L2_CID_MPEG_BASE+304)
-#define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP (V4L2_CID_MPEG_BASE+350)
-#define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP (V4L2_CID_MPEG_BASE+351)
-#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP (V4L2_CID_MPEG_BASE+352)
-#define V4L2_CID_MPEG_VIDEO_H264_MIN_QP (V4L2_CID_MPEG_BASE+353)
-#define V4L2_CID_MPEG_VIDEO_H264_MAX_QP (V4L2_CID_MPEG_BASE+354)
-#define V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM (V4L2_CID_MPEG_BASE+355)
-#define V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE (V4L2_CID_MPEG_BASE+356)
-#define V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE (V4L2_CID_MPEG_BASE+357)
-enum v4l2_mpeg_video_h264_entropy_mode {
- V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC = 0,
- V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC = 1,
-};
-#define V4L2_CID_MPEG_VIDEO_H264_I_PERIOD (V4L2_CID_MPEG_BASE+358)
-#define V4L2_CID_MPEG_VIDEO_H264_LEVEL (V4L2_CID_MPEG_BASE+359)
-enum v4l2_mpeg_video_h264_level {
- V4L2_MPEG_VIDEO_H264_LEVEL_1_0 = 0,
- V4L2_MPEG_VIDEO_H264_LEVEL_1B = 1,
- V4L2_MPEG_VIDEO_H264_LEVEL_1_1 = 2,
- V4L2_MPEG_VIDEO_H264_LEVEL_1_2 = 3,
- V4L2_MPEG_VIDEO_H264_LEVEL_1_3 = 4,
- V4L2_MPEG_VIDEO_H264_LEVEL_2_0 = 5,
- V4L2_MPEG_VIDEO_H264_LEVEL_2_1 = 6,
- V4L2_MPEG_VIDEO_H264_LEVEL_2_2 = 7,
- V4L2_MPEG_VIDEO_H264_LEVEL_3_0 = 8,
- V4L2_MPEG_VIDEO_H264_LEVEL_3_1 = 9,
- V4L2_MPEG_VIDEO_H264_LEVEL_3_2 = 10,
- V4L2_MPEG_VIDEO_H264_LEVEL_4_0 = 11,
- V4L2_MPEG_VIDEO_H264_LEVEL_4_1 = 12,
- V4L2_MPEG_VIDEO_H264_LEVEL_4_2 = 13,
- V4L2_MPEG_VIDEO_H264_LEVEL_5_0 = 14,
- V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15,
-};
-#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA (V4L2_CID_MPEG_BASE+360)
-#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA (V4L2_CID_MPEG_BASE+361)
-#define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE (V4L2_CID_MPEG_BASE+362)
-enum v4l2_mpeg_video_h264_loop_filter_mode {
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED = 0,
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED = 1,
- V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY = 2,
-};
-#define V4L2_CID_MPEG_VIDEO_H264_PROFILE (V4L2_CID_MPEG_BASE+363)
-enum v4l2_mpeg_video_h264_profile {
- V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE = 0,
- V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE = 1,
- V4L2_MPEG_VIDEO_H264_PROFILE_MAIN = 2,
- V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED = 3,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH = 4,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10 = 5,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422 = 6,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE = 7,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA = 8,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA = 9,
- V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA = 10,
- V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA = 11,
- V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE = 12,
- V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH = 13,
- V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA = 14,
- V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH = 15,
- V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH = 16,
-};
-#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364)
-#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365)
-#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE (V4L2_CID_MPEG_BASE+366)
-#define V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC (V4L2_CID_MPEG_BASE+367)
-enum v4l2_mpeg_video_h264_vui_sar_idc {
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_UNSPECIFIED = 0,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1 = 1,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_12x11 = 2,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_10x11 = 3,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_16x11 = 4,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_40x33 = 5,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_24x11 = 6,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_20x11 = 7,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_32x11 = 8,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_80x33 = 9,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_18x11 = 10,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_15x11 = 11,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_64x33 = 12,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_160x99 = 13,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_4x3 = 14,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_3x2 = 15,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_2x1 = 16,
- V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED = 17,
-};
-#define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP (V4L2_CID_MPEG_BASE+400)
-#define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP (V4L2_CID_MPEG_BASE+401)
-#define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP (V4L2_CID_MPEG_BASE+402)
-#define V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP (V4L2_CID_MPEG_BASE+403)
-#define V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP (V4L2_CID_MPEG_BASE+404)
-#define V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL (V4L2_CID_MPEG_BASE+405)
-enum v4l2_mpeg_video_mpeg4_level {
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_0 = 0,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_0B = 1,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_1 = 2,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_2 = 3,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_3 = 4,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_3B = 5,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_4 = 6,
- V4L2_MPEG_VIDEO_MPEG4_LEVEL_5 = 7,
-};
-#define V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE (V4L2_CID_MPEG_BASE+406)
-enum v4l2_mpeg_video_mpeg4_profile {
- V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE = 0,
- V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_SIMPLE = 1,
- V4L2_MPEG_VIDEO_MPEG4_PROFILE_CORE = 2,
- V4L2_MPEG_VIDEO_MPEG4_PROFILE_SIMPLE_SCALABLE = 3,
- V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY = 4,
-};
-#define V4L2_CID_MPEG_VIDEO_MPEG4_QPEL (V4L2_CID_MPEG_BASE+407)
-
-/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
-#define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0)
-enum v4l2_mpeg_cx2341x_video_spatial_filter_mode {
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0,
- V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+1)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+2)
-enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type {
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF = 0,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT = 2,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE = 3,
- V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+3)
-enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type {
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF = 0,
- V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+4)
-enum v4l2_mpeg_cx2341x_video_temporal_filter_mode {
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0,
- V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO = 1,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+5)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+6)
-enum v4l2_mpeg_cx2341x_video_median_filter_type {
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF = 0,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR = 1,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT = 2,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3,
- V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG = 4,
-};
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+7)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9)
-#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10)
-#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11)
-
-/* MPEG-class control IDs specific to the Samsung MFC 5.1 driver as defined by V4L2 */
-#define V4L2_CID_MPEG_MFC51_BASE (V4L2_CTRL_CLASS_MPEG | 0x1100)
-
-#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY (V4L2_CID_MPEG_MFC51_BASE+0)
-#define V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY_ENABLE (V4L2_CID_MPEG_MFC51_BASE+1)
-#define V4L2_CID_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE (V4L2_CID_MPEG_MFC51_BASE+2)
-enum v4l2_mpeg_mfc51_video_frame_skip_mode {
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_DISABLED = 0,
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT = 1,
- V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT = 2,
-};
-#define V4L2_CID_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE (V4L2_CID_MPEG_MFC51_BASE+3)
-enum v4l2_mpeg_mfc51_video_force_frame_type {
- V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_DISABLED = 0,
- V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_I_FRAME = 1,
- V4L2_MPEG_MFC51_VIDEO_FORCE_FRAME_TYPE_NOT_CODED = 2,
-};
-#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING (V4L2_CID_MPEG_MFC51_BASE+4)
-#define V4L2_CID_MPEG_MFC51_VIDEO_PADDING_YUV (V4L2_CID_MPEG_MFC51_BASE+5)
-#define V4L2_CID_MPEG_MFC51_VIDEO_RC_FIXED_TARGET_BIT (V4L2_CID_MPEG_MFC51_BASE+6)
-#define V4L2_CID_MPEG_MFC51_VIDEO_RC_REACTION_COEFF (V4L2_CID_MPEG_MFC51_BASE+7)
-#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_ACTIVITY (V4L2_CID_MPEG_MFC51_BASE+50)
-#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_DARK (V4L2_CID_MPEG_MFC51_BASE+51)
-#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_SMOOTH (V4L2_CID_MPEG_MFC51_BASE+52)
-#define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC (V4L2_CID_MPEG_MFC51_BASE+53)
-#define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P (V4L2_CID_MPEG_MFC51_BASE+54)
-
-/* Camera class control IDs */
-#define V4L2_CID_CAMERA_CLASS_BASE (V4L2_CTRL_CLASS_CAMERA | 0x900)
-#define V4L2_CID_CAMERA_CLASS (V4L2_CTRL_CLASS_CAMERA | 1)
-
-#define V4L2_CID_EXPOSURE_AUTO (V4L2_CID_CAMERA_CLASS_BASE+1)
-enum v4l2_exposure_auto_type {
- V4L2_EXPOSURE_AUTO = 0,
- V4L2_EXPOSURE_MANUAL = 1,
- V4L2_EXPOSURE_SHUTTER_PRIORITY = 2,
- V4L2_EXPOSURE_APERTURE_PRIORITY = 3
-};
-#define V4L2_CID_EXPOSURE_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+2)
-#define V4L2_CID_EXPOSURE_AUTO_PRIORITY (V4L2_CID_CAMERA_CLASS_BASE+3)
-
-#define V4L2_CID_PAN_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+4)
-#define V4L2_CID_TILT_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+5)
-#define V4L2_CID_PAN_RESET (V4L2_CID_CAMERA_CLASS_BASE+6)
-#define V4L2_CID_TILT_RESET (V4L2_CID_CAMERA_CLASS_BASE+7)
-
-#define V4L2_CID_PAN_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+8)
-#define V4L2_CID_TILT_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+9)
-
-#define V4L2_CID_FOCUS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+10)
-#define V4L2_CID_FOCUS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+11)
-#define V4L2_CID_FOCUS_AUTO (V4L2_CID_CAMERA_CLASS_BASE+12)
-
-#define V4L2_CID_ZOOM_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+13)
-#define V4L2_CID_ZOOM_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+14)
-#define V4L2_CID_ZOOM_CONTINUOUS (V4L2_CID_CAMERA_CLASS_BASE+15)
-
-#define V4L2_CID_PRIVACY (V4L2_CID_CAMERA_CLASS_BASE+16)
-
-#define V4L2_CID_IRIS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+17)
-#define V4L2_CID_IRIS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+18)
-
-#define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19)
-
-#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20)
-enum v4l2_auto_n_preset_white_balance {
- V4L2_WHITE_BALANCE_MANUAL = 0,
- V4L2_WHITE_BALANCE_AUTO = 1,
- V4L2_WHITE_BALANCE_INCANDESCENT = 2,
- V4L2_WHITE_BALANCE_FLUORESCENT = 3,
- V4L2_WHITE_BALANCE_FLUORESCENT_H = 4,
- V4L2_WHITE_BALANCE_HORIZON = 5,
- V4L2_WHITE_BALANCE_DAYLIGHT = 6,
- V4L2_WHITE_BALANCE_FLASH = 7,
- V4L2_WHITE_BALANCE_CLOUDY = 8,
- V4L2_WHITE_BALANCE_SHADE = 9,
-};
-
-#define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21)
-#define V4L2_CID_IMAGE_STABILIZATION (V4L2_CID_CAMERA_CLASS_BASE+22)
-
-#define V4L2_CID_ISO_SENSITIVITY (V4L2_CID_CAMERA_CLASS_BASE+23)
-#define V4L2_CID_ISO_SENSITIVITY_AUTO (V4L2_CID_CAMERA_CLASS_BASE+24)
-enum v4l2_iso_sensitivity_auto_type {
- V4L2_ISO_SENSITIVITY_MANUAL = 0,
- V4L2_ISO_SENSITIVITY_AUTO = 1,
-};
-
-#define V4L2_CID_EXPOSURE_METERING (V4L2_CID_CAMERA_CLASS_BASE+25)
-enum v4l2_exposure_metering {
- V4L2_EXPOSURE_METERING_AVERAGE = 0,
- V4L2_EXPOSURE_METERING_CENTER_WEIGHTED = 1,
- V4L2_EXPOSURE_METERING_SPOT = 2,
-};
-
-#define V4L2_CID_SCENE_MODE (V4L2_CID_CAMERA_CLASS_BASE+26)
-enum v4l2_scene_mode {
- V4L2_SCENE_MODE_NONE = 0,
- V4L2_SCENE_MODE_BACKLIGHT = 1,
- V4L2_SCENE_MODE_BEACH_SNOW = 2,
- V4L2_SCENE_MODE_CANDLE_LIGHT = 3,
- V4L2_SCENE_MODE_DAWN_DUSK = 4,
- V4L2_SCENE_MODE_FALL_COLORS = 5,
- V4L2_SCENE_MODE_FIREWORKS = 6,
- V4L2_SCENE_MODE_LANDSCAPE = 7,
- V4L2_SCENE_MODE_NIGHT = 8,
- V4L2_SCENE_MODE_PARTY_INDOOR = 9,
- V4L2_SCENE_MODE_PORTRAIT = 10,
- V4L2_SCENE_MODE_SPORTS = 11,
- V4L2_SCENE_MODE_SUNSET = 12,
- V4L2_SCENE_MODE_TEXT = 13,
-};
-
-#define V4L2_CID_3A_LOCK (V4L2_CID_CAMERA_CLASS_BASE+27)
-#define V4L2_LOCK_EXPOSURE (1 << 0)
-#define V4L2_LOCK_WHITE_BALANCE (1 << 1)
-#define V4L2_LOCK_FOCUS (1 << 2)
-
-#define V4L2_CID_AUTO_FOCUS_START (V4L2_CID_CAMERA_CLASS_BASE+28)
-#define V4L2_CID_AUTO_FOCUS_STOP (V4L2_CID_CAMERA_CLASS_BASE+29)
-#define V4L2_CID_AUTO_FOCUS_STATUS (V4L2_CID_CAMERA_CLASS_BASE+30)
-#define V4L2_AUTO_FOCUS_STATUS_IDLE (0 << 0)
-#define V4L2_AUTO_FOCUS_STATUS_BUSY (1 << 0)
-#define V4L2_AUTO_FOCUS_STATUS_REACHED (1 << 1)
-#define V4L2_AUTO_FOCUS_STATUS_FAILED (1 << 2)
-
-#define V4L2_CID_AUTO_FOCUS_RANGE (V4L2_CID_CAMERA_CLASS_BASE+31)
-enum v4l2_auto_focus_range {
- V4L2_AUTO_FOCUS_RANGE_AUTO = 0,
- V4L2_AUTO_FOCUS_RANGE_NORMAL = 1,
- V4L2_AUTO_FOCUS_RANGE_MACRO = 2,
- V4L2_AUTO_FOCUS_RANGE_INFINITY = 3,
-};
-
-/* FM Modulator class control IDs */
-#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
-#define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1)
-
-#define V4L2_CID_RDS_TX_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 1)
-#define V4L2_CID_RDS_TX_PI (V4L2_CID_FM_TX_CLASS_BASE + 2)
-#define V4L2_CID_RDS_TX_PTY (V4L2_CID_FM_TX_CLASS_BASE + 3)
-#define V4L2_CID_RDS_TX_PS_NAME (V4L2_CID_FM_TX_CLASS_BASE + 5)
-#define V4L2_CID_RDS_TX_RADIO_TEXT (V4L2_CID_FM_TX_CLASS_BASE + 6)
-
-#define V4L2_CID_AUDIO_LIMITER_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 64)
-#define V4L2_CID_AUDIO_LIMITER_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 65)
-#define V4L2_CID_AUDIO_LIMITER_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 66)
-
-#define V4L2_CID_AUDIO_COMPRESSION_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 80)
-#define V4L2_CID_AUDIO_COMPRESSION_GAIN (V4L2_CID_FM_TX_CLASS_BASE + 81)
-#define V4L2_CID_AUDIO_COMPRESSION_THRESHOLD (V4L2_CID_FM_TX_CLASS_BASE + 82)
-#define V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME (V4L2_CID_FM_TX_CLASS_BASE + 83)
-#define V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME (V4L2_CID_FM_TX_CLASS_BASE + 84)
-
-#define V4L2_CID_PILOT_TONE_ENABLED (V4L2_CID_FM_TX_CLASS_BASE + 96)
-#define V4L2_CID_PILOT_TONE_DEVIATION (V4L2_CID_FM_TX_CLASS_BASE + 97)
-#define V4L2_CID_PILOT_TONE_FREQUENCY (V4L2_CID_FM_TX_CLASS_BASE + 98)
-
-#define V4L2_CID_TUNE_PREEMPHASIS (V4L2_CID_FM_TX_CLASS_BASE + 112)
-enum v4l2_preemphasis {
- V4L2_PREEMPHASIS_DISABLED = 0,
- V4L2_PREEMPHASIS_50_uS = 1,
- V4L2_PREEMPHASIS_75_uS = 2,
-};
-#define V4L2_CID_TUNE_POWER_LEVEL (V4L2_CID_FM_TX_CLASS_BASE + 113)
-#define V4L2_CID_TUNE_ANTENNA_CAPACITOR (V4L2_CID_FM_TX_CLASS_BASE + 114)
-
-/* Flash and privacy (indicator) light controls */
-#define V4L2_CID_FLASH_CLASS_BASE (V4L2_CTRL_CLASS_FLASH | 0x900)
-#define V4L2_CID_FLASH_CLASS (V4L2_CTRL_CLASS_FLASH | 1)
-
-#define V4L2_CID_FLASH_LED_MODE (V4L2_CID_FLASH_CLASS_BASE + 1)
-enum v4l2_flash_led_mode {
- V4L2_FLASH_LED_MODE_NONE,
- V4L2_FLASH_LED_MODE_FLASH,
- V4L2_FLASH_LED_MODE_TORCH,
-};
-
-#define V4L2_CID_FLASH_STROBE_SOURCE (V4L2_CID_FLASH_CLASS_BASE + 2)
-enum v4l2_flash_strobe_source {
- V4L2_FLASH_STROBE_SOURCE_SOFTWARE,
- V4L2_FLASH_STROBE_SOURCE_EXTERNAL,
-};
-
-#define V4L2_CID_FLASH_STROBE (V4L2_CID_FLASH_CLASS_BASE + 3)
-#define V4L2_CID_FLASH_STROBE_STOP (V4L2_CID_FLASH_CLASS_BASE + 4)
-#define V4L2_CID_FLASH_STROBE_STATUS (V4L2_CID_FLASH_CLASS_BASE + 5)
-
-#define V4L2_CID_FLASH_TIMEOUT (V4L2_CID_FLASH_CLASS_BASE + 6)
-#define V4L2_CID_FLASH_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 7)
-#define V4L2_CID_FLASH_TORCH_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 8)
-#define V4L2_CID_FLASH_INDICATOR_INTENSITY (V4L2_CID_FLASH_CLASS_BASE + 9)
-
-#define V4L2_CID_FLASH_FAULT (V4L2_CID_FLASH_CLASS_BASE + 10)
-#define V4L2_FLASH_FAULT_OVER_VOLTAGE (1 << 0)
-#define V4L2_FLASH_FAULT_TIMEOUT (1 << 1)
-#define V4L2_FLASH_FAULT_OVER_TEMPERATURE (1 << 2)
-#define V4L2_FLASH_FAULT_SHORT_CIRCUIT (1 << 3)
-#define V4L2_FLASH_FAULT_OVER_CURRENT (1 << 4)
-#define V4L2_FLASH_FAULT_INDICATOR (1 << 5)
-
-#define V4L2_CID_FLASH_CHARGE (V4L2_CID_FLASH_CLASS_BASE + 11)
-#define V4L2_CID_FLASH_READY (V4L2_CID_FLASH_CLASS_BASE + 12)
-
-/* JPEG-class control IDs defined by V4L2 */
-#define V4L2_CID_JPEG_CLASS_BASE (V4L2_CTRL_CLASS_JPEG | 0x900)
-#define V4L2_CID_JPEG_CLASS (V4L2_CTRL_CLASS_JPEG | 1)
-
-#define V4L2_CID_JPEG_CHROMA_SUBSAMPLING (V4L2_CID_JPEG_CLASS_BASE + 1)
-enum v4l2_jpeg_chroma_subsampling {
- V4L2_JPEG_CHROMA_SUBSAMPLING_444 = 0,
- V4L2_JPEG_CHROMA_SUBSAMPLING_422 = 1,
- V4L2_JPEG_CHROMA_SUBSAMPLING_420 = 2,
- V4L2_JPEG_CHROMA_SUBSAMPLING_411 = 3,
- V4L2_JPEG_CHROMA_SUBSAMPLING_410 = 4,
- V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY = 5,
-};
-#define V4L2_CID_JPEG_RESTART_INTERVAL (V4L2_CID_JPEG_CLASS_BASE + 2)
-#define V4L2_CID_JPEG_COMPRESSION_QUALITY (V4L2_CID_JPEG_CLASS_BASE + 3)
-
-#define V4L2_CID_JPEG_ACTIVE_MARKER (V4L2_CID_JPEG_CLASS_BASE + 4)
-#define V4L2_JPEG_ACTIVE_MARKER_APP0 (1 << 0)
-#define V4L2_JPEG_ACTIVE_MARKER_APP1 (1 << 1)
-#define V4L2_JPEG_ACTIVE_MARKER_COM (1 << 16)
-#define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17)
-#define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18)
-
-/* Image source controls */
-#define V4L2_CID_IMAGE_SOURCE_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_SOURCE | 0x900)
-#define V4L2_CID_IMAGE_SOURCE_CLASS (V4L2_CTRL_CLASS_IMAGE_SOURCE | 1)
-
-#define V4L2_CID_VBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 1)
-#define V4L2_CID_HBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2)
-#define V4L2_CID_ANALOGUE_GAIN (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3)
-
-/* Image processing controls */
-#define V4L2_CID_IMAGE_PROC_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_PROC | 0x900)
-#define V4L2_CID_IMAGE_PROC_CLASS (V4L2_CTRL_CLASS_IMAGE_PROC | 1)
-
-#define V4L2_CID_LINK_FREQ (V4L2_CID_IMAGE_PROC_CLASS_BASE + 1)
-#define V4L2_CID_PIXEL_RATE (V4L2_CID_IMAGE_PROC_CLASS_BASE + 2)
+
+/* DV-class control IDs defined by V4L2 */
+#define V4L2_CID_DV_CLASS_BASE (V4L2_CTRL_CLASS_DV | 0x900)
+#define V4L2_CID_DV_CLASS (V4L2_CTRL_CLASS_DV | 1)
+
+#define V4L2_CID_DV_TX_HOTPLUG (V4L2_CID_DV_CLASS_BASE + 1)
+#define V4L2_CID_DV_TX_RXSENSE (V4L2_CID_DV_CLASS_BASE + 2)
+#define V4L2_CID_DV_TX_EDID_PRESENT (V4L2_CID_DV_CLASS_BASE + 3)
+#define V4L2_CID_DV_TX_MODE (V4L2_CID_DV_CLASS_BASE + 4)
+enum v4l2_dv_tx_mode {
+ V4L2_DV_TX_MODE_DVI_D = 0,
+ V4L2_DV_TX_MODE_HDMI = 1,
+};
+#define V4L2_CID_DV_TX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 5)
+enum v4l2_dv_rgb_range {
+ V4L2_DV_RGB_RANGE_AUTO = 0,
+ V4L2_DV_RGB_RANGE_LIMITED = 1,
+ V4L2_DV_RGB_RANGE_FULL = 2,
+};
+
+#define V4L2_CID_DV_RX_POWER_PRESENT (V4L2_CID_DV_CLASS_BASE + 100)
+#define V4L2_CID_DV_RX_RGB_RANGE (V4L2_CID_DV_CLASS_BASE + 101)
/*
* T U N I N G
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 06f8e3858251..57f7b1091511 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -30,6 +30,7 @@ enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT,
FOR_ALL_ZONES(PGSTEAL_DIRECT),
FOR_ALL_ZONES(PGSCAN_KSWAPD),
FOR_ALL_ZONES(PGSCAN_DIRECT),
+ PGSCAN_DIRECT_THROTTLE,
#ifdef CONFIG_NUMA
PGSCAN_ZONE_RECLAIM_FAILED,
#endif
diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h
index 65efb92da996..ad2cfd53dadc 100644
--- a/include/linux/vmstat.h
+++ b/include/linux/vmstat.h
@@ -179,11 +179,6 @@ extern void zone_statistics(struct zone *, struct zone *, gfp_t gfp);
#define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d)
#define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d))
-static inline void zap_zone_vm_stats(struct zone *zone)
-{
- memset(zone->vm_stat, 0, sizeof(zone->vm_stat));
-}
-
extern void inc_zone_state(struct zone *, enum zone_stat_item);
#ifdef CONFIG_SMP
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 6d0a0fcd80e7..50c3e8fa06a8 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -104,7 +104,6 @@ static inline void wait_on_inode(struct inode *inode)
wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE);
}
-
/*
* mm/page-writeback.c
*/
@@ -189,9 +188,4 @@ void tag_pages_for_writeback(struct address_space *mapping,
void account_page_redirty(struct page *page);
-/* pdflush.c */
-extern int nr_pdflush_threads; /* Global so it can be exported to sysctl
- read-only. */
-
-
#endif /* WRITEBACK_H */
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 22e61fdf75a2..28e493b5b94c 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -84,6 +84,8 @@ struct xfrm_replay_state {
__u32 bitmap;
};
+#define XFRMA_REPLAY_ESN_MAX 4096
+
struct xfrm_replay_state_esn {
unsigned int bmp_len;
__u32 oseq;
diff --git a/include/media/ad9389b.h b/include/media/ad9389b.h
new file mode 100644
index 000000000000..5ba9af869b8b
--- /dev/null
+++ b/include/media/ad9389b.h
@@ -0,0 +1,49 @@
+/*
+ * Analog Devices AD9389B/AD9889B video encoder driver header
+ *
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef AD9389B_H
+#define AD9389B_H
+
+enum ad9389b_tmds_pll_gear {
+ AD9389B_TMDS_PLL_GEAR_AUTOMATIC,
+ AD9389B_TMDS_PLL_GEAR_SEMI_AUTOMATIC,
+};
+
+/* Platform dependent definitions */
+struct ad9389b_platform_data {
+ enum ad9389b_tmds_pll_gear tmds_pll_gear ;
+ /* Differential Data/Clock Output Drive Strength (reg. 0xa2/0xa3) */
+ u8 diff_data_drive_strength;
+ u8 diff_clk_drive_strength;
+};
+
+/* notify events */
+#define AD9389B_MONITOR_DETECT 0
+#define AD9389B_EDID_DETECT 1
+
+struct ad9389b_monitor_detect {
+ int present;
+};
+
+struct ad9389b_edid_detect {
+ int present;
+ int segment;
+};
+
+#endif
diff --git a/include/media/adv7604.h b/include/media/adv7604.h
new file mode 100644
index 000000000000..171b957db743
--- /dev/null
+++ b/include/media/adv7604.h
@@ -0,0 +1,153 @@
+/*
+ * adv7604 - Analog Devices ADV7604 video decoder driver
+ *
+ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef _ADV7604_
+#define _ADV7604_
+
+/* Analog input muxing modes (AFE register 0x02, [2:0]) */
+enum adv7604_ain_sel {
+ ADV7604_AIN1_2_3_NC_SYNC_1_2 = 0,
+ ADV7604_AIN4_5_6_NC_SYNC_2_1 = 1,
+ ADV7604_AIN7_8_9_NC_SYNC_3_1 = 2,
+ ADV7604_AIN10_11_12_NC_SYNC_4_1 = 3,
+ ADV7604_AIN9_4_5_6_SYNC_2_1 = 4,
+};
+
+/* Bus rotation and reordering (IO register 0x04, [7:5]) */
+enum adv7604_op_ch_sel {
+ ADV7604_OP_CH_SEL_GBR = 0,
+ ADV7604_OP_CH_SEL_GRB = 1,
+ ADV7604_OP_CH_SEL_BGR = 2,
+ ADV7604_OP_CH_SEL_RGB = 3,
+ ADV7604_OP_CH_SEL_BRG = 4,
+ ADV7604_OP_CH_SEL_RBG = 5,
+};
+
+/* Primary mode (IO register 0x01, [3:0]) */
+enum adv7604_prim_mode {
+ ADV7604_PRIM_MODE_COMP = 1,
+ ADV7604_PRIM_MODE_RGB = 2,
+ ADV7604_PRIM_MODE_HDMI_COMP = 5,
+ ADV7604_PRIM_MODE_HDMI_GR = 6,
+};
+
+/* Input Color Space (IO register 0x02, [7:4]) */
+enum adv7604_inp_color_space {
+ ADV7604_INP_COLOR_SPACE_LIM_RGB = 0,
+ ADV7604_INP_COLOR_SPACE_FULL_RGB = 1,
+ ADV7604_INP_COLOR_SPACE_LIM_YCbCr_601 = 2,
+ ADV7604_INP_COLOR_SPACE_LIM_YCbCr_709 = 3,
+ ADV7604_INP_COLOR_SPACE_XVYCC_601 = 4,
+ ADV7604_INP_COLOR_SPACE_XVYCC_709 = 5,
+ ADV7604_INP_COLOR_SPACE_FULL_YCbCr_601 = 6,
+ ADV7604_INP_COLOR_SPACE_FULL_YCbCr_709 = 7,
+ ADV7604_INP_COLOR_SPACE_AUTO = 0xf,
+};
+
+/* Select output format (IO register 0x03, [7:0]) */
+enum adv7604_op_format_sel {
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_8 = 0x00,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_10 = 0x01,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE0 = 0x02,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE1 = 0x06,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_12_MODE2 = 0x0a,
+ ADV7604_OP_FORMAT_SEL_DDR_422_8 = 0x20,
+ ADV7604_OP_FORMAT_SEL_DDR_422_10 = 0x21,
+ ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE0 = 0x22,
+ ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE1 = 0x23,
+ ADV7604_OP_FORMAT_SEL_DDR_422_12_MODE2 = 0x24,
+ ADV7604_OP_FORMAT_SEL_SDR_444_24 = 0x40,
+ ADV7604_OP_FORMAT_SEL_SDR_444_30 = 0x41,
+ ADV7604_OP_FORMAT_SEL_SDR_444_36_MODE0 = 0x42,
+ ADV7604_OP_FORMAT_SEL_DDR_444_24 = 0x60,
+ ADV7604_OP_FORMAT_SEL_DDR_444_30 = 0x61,
+ ADV7604_OP_FORMAT_SEL_DDR_444_36 = 0x62,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_16 = 0x80,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_20 = 0x81,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE0 = 0x82,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE1 = 0x86,
+ ADV7604_OP_FORMAT_SEL_SDR_ITU656_24_MODE2 = 0x8a,
+};
+
+/* Platform dependent definition */
+struct adv7604_platform_data {
+ /* connector - HDMI or DVI? */
+ unsigned connector_hdmi:1;
+
+ /* DIS_PWRDNB: 1 if the PWRDNB pin is unused and unconnected */
+ unsigned disable_pwrdnb:1;
+
+ /* DIS_CABLE_DET_RST: 1 if the 5V pins are unused and unconnected */
+ unsigned disable_cable_det_rst:1;
+
+ /* Analog input muxing mode */
+ enum adv7604_ain_sel ain_sel;
+
+ /* Bus rotation and reordering */
+ enum adv7604_op_ch_sel op_ch_sel;
+
+ /* Primary mode */
+ enum adv7604_prim_mode prim_mode;
+
+ /* Select output format */
+ enum adv7604_op_format_sel op_format_sel;
+
+ /* IO register 0x02 */
+ unsigned alt_gamma:1;
+ unsigned op_656_range:1;
+ unsigned rgb_out:1;
+ unsigned alt_data_sat:1;
+
+ /* IO register 0x05 */
+ unsigned blank_data:1;
+ unsigned insert_av_codes:1;
+ unsigned replicate_av_codes:1;
+ unsigned invert_cbcr:1;
+
+ /* IO register 0x30 */
+ unsigned output_bus_lsb_to_msb:1;
+
+ /* Free run */
+ unsigned hdmi_free_run_mode;
+
+ /* i2c addresses: 0 == use default */
+ u8 i2c_avlink;
+ u8 i2c_cec;
+ u8 i2c_infoframe;
+ u8 i2c_esdp;
+ u8 i2c_dpp;
+ u8 i2c_afe;
+ u8 i2c_repeater;
+ u8 i2c_edid;
+ u8 i2c_hdmi;
+ u8 i2c_test;
+ u8 i2c_cp;
+ u8 i2c_vdp;
+};
+
+#define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000)
+#define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001)
+#define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002)
+
+/* notify events */
+#define ADV7604_HOTPLUG 1
+#define ADV7604_FMT_CHANGE 2
+
+#endif
diff --git a/include/media/ir-rx51.h b/include/media/ir-rx51.h
new file mode 100644
index 000000000000..104aa892f31b
--- /dev/null
+++ b/include/media/ir-rx51.h
@@ -0,0 +1,10 @@
+#ifndef _LIRC_RX51_H
+#define _LIRC_RX51_H
+
+struct lirc_rx51_platform_data {
+ int pwm_timer;
+
+ int(*set_max_mpu_wakeup_lat)(struct device *dev, long t);
+};
+
+#endif
diff --git a/include/media/mt9v032.h b/include/media/mt9v032.h
index 5e27f9be6b95..78fd39eac219 100644
--- a/include/media/mt9v032.h
+++ b/include/media/mt9v032.h
@@ -7,6 +7,9 @@ struct mt9v032_platform_data {
unsigned int clk_pol:1;
void (*set_clock)(struct v4l2_subdev *subdev, unsigned int rate);
+
+ const s64 *link_freqs;
+ s64 link_def_freq;
};
#endif
diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h
index 4d94be5226af..95842696857f 100644
--- a/include/media/omap3isp.h
+++ b/include/media/omap3isp.h
@@ -42,12 +42,6 @@ enum isp_interface_type {
};
enum {
- ISP_BRIDGE_DISABLE = 0,
- ISP_BRIDGE_LITTLE_ENDIAN = 2,
- ISP_BRIDGE_BIG_ENDIAN = 3,
-};
-
-enum {
ISP_LANE_SHIFT_0 = 0,
ISP_LANE_SHIFT_2 = 1,
ISP_LANE_SHIFT_4 = 2,
@@ -67,17 +61,15 @@ enum {
* 0 - Active high, 1 - Active low
* @vs_pol: Vertical synchronization polarity
* 0 - Active high, 1 - Active low
- * @bridge: CCDC Bridge input control
- * ISP_BRIDGE_DISABLE - Disable
- * ISP_BRIDGE_LITTLE_ENDIAN - Little endian
- * ISP_BRIDGE_BIG_ENDIAN - Big endian
+ * @data_pol: Data polarity
+ * 0 - Normal, 1 - One's complement
*/
struct isp_parallel_platform_data {
unsigned int data_lane_shift:2;
unsigned int clk_pol:1;
unsigned int hs_pol:1;
unsigned int vs_pol:1;
- unsigned int bridge:2;
+ unsigned int data_pol:1;
};
enum {
diff --git a/include/media/s5k4ecgx.h b/include/media/s5k4ecgx.h
new file mode 100644
index 000000000000..90c1be792ffe
--- /dev/null
+++ b/include/media/s5k4ecgx.h
@@ -0,0 +1,37 @@
+/*
+ * S5K4ECGX image sensor header file
+ *
+ * Copyright (C) 2012, Linaro
+ * Copyright (C) 2012, Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the 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 S5K4ECGX_H
+#define S5K4ECGX_H
+
+/**
+ * struct s5k4ecgx_gpio - data structure describing a GPIO
+ * @gpio : GPIO number
+ * @level: indicates active state of the @gpio
+ */
+struct s5k4ecgx_gpio {
+ int gpio;
+ int level;
+};
+
+/**
+ * struct ss5k4ecgx_platform_data- s5k4ecgx driver platform data
+ * @gpio_reset: GPIO driving RESET pin
+ * @gpio_stby : GPIO driving STBY pin
+ */
+
+struct s5k4ecgx_platform_data {
+ struct s5k4ecgx_gpio gpio_reset;
+ struct s5k4ecgx_gpio gpio_stby;
+};
+
+#endif /* S5K4ECGX_H */
diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h
index 8587aaf73646..09421a611d73 100644
--- a/include/media/s5p_fimc.h
+++ b/include/media/s5p_fimc.h
@@ -12,6 +12,8 @@
#ifndef S5P_FIMC_H_
#define S5P_FIMC_H_
+#include <media/media-entity.h>
+
enum cam_bus_type {
FIMC_ITU_601 = 1,
FIMC_ITU_656,
@@ -80,4 +82,20 @@ struct fimc_pipeline {
struct media_pipeline *m_pipeline;
};
+/*
+ * Media pipeline operations to be called from within the fimc(-lite)
+ * video node when it is the last entity of the pipeline. Implemented
+ * by corresponding media device driver.
+ */
+struct fimc_pipeline_ops {
+ int (*open)(struct fimc_pipeline *p, struct media_entity *me,
+ bool resume);
+ int (*close)(struct fimc_pipeline *p);
+ int (*set_stream)(struct fimc_pipeline *p, bool state);
+};
+
+#define fimc_pipeline_call(f, op, p, args...) \
+ (!(f) ? -ENODEV : (((f)->pipeline_ops && (f)->pipeline_ops->op) ? \
+ (f)->pipeline_ops->op((p), ##args) : -ENOIOCTLCMD))
+
#endif /* S5P_FIMC_H_ */
diff --git a/include/media/saa7146.h b/include/media/saa7146.h
index 773e527deabe..96058a5a4acc 100644
--- a/include/media/saa7146.h
+++ b/include/media/saa7146.h
@@ -117,8 +117,6 @@ struct saa7146_dev
{
struct module *module;
- struct list_head item;
-
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
@@ -166,8 +164,6 @@ static inline struct saa7146_dev *to_saa7146_dev(struct v4l2_device *v4l2_dev)
int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate);
/* from saa7146_core.c */
-extern struct list_head saa7146_devices;
-extern struct mutex saa7146_devices_lock;
int saa7146_register_extension(struct saa7146_extension*);
int saa7146_unregister_extension(struct saa7146_extension*);
struct saa7146_format* saa7146_format_by_fourcc(struct saa7146_dev *dev, int fourcc);
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
index d865dcf9879f..6442edc2a151 100644
--- a/include/media/soc_camera.h
+++ b/include/media/soc_camera.h
@@ -85,12 +85,14 @@ struct soc_camera_host_ops {
void (*put_formats)(struct soc_camera_device *);
int (*cropcap)(struct soc_camera_device *, struct v4l2_cropcap *);
int (*get_crop)(struct soc_camera_device *, struct v4l2_crop *);
- int (*set_crop)(struct soc_camera_device *, struct v4l2_crop *);
+ int (*set_crop)(struct soc_camera_device *, const struct v4l2_crop *);
+ int (*get_selection)(struct soc_camera_device *, struct v4l2_selection *);
+ int (*set_selection)(struct soc_camera_device *, struct v4l2_selection *);
/*
* The difference to .set_crop() is, that .set_livecrop is not allowed
* to change the output sizes
*/
- int (*set_livecrop)(struct soc_camera_device *, struct v4l2_crop *);
+ int (*set_livecrop)(struct soc_camera_device *, const struct v4l2_crop *);
int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
void (*init_videobuf)(struct videobuf_queue *,
@@ -254,6 +256,16 @@ unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl,
const struct v4l2_mbus_config *cfg);
+int soc_camera_power_on(struct device *dev, struct soc_camera_link *icl);
+int soc_camera_power_off(struct device *dev, struct soc_camera_link *icl);
+
+static inline int soc_camera_set_power(struct device *dev,
+ struct soc_camera_link *icl, bool on)
+{
+ return on ? soc_camera_power_on(dev, icl)
+ : soc_camera_power_off(dev, icl);
+}
+
/* This is only temporary here - until v4l2-subdev begins to link to video_device */
#include <linux/i2c.h>
static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client)
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 58f914a40b20..4ee125bae719 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -183,6 +183,9 @@ enum {
/* module adv7393: just ident 7393 */
V4L2_IDENT_ADV7393 = 7393,
+ /* module adv7604: just ident 7604 */
+ V4L2_IDENT_ADV7604 = 7604,
+
/* module saa7706h: just ident 7706 */
V4L2_IDENT_SAA7706H = 7706,
@@ -212,6 +215,9 @@ enum {
V4L2_IDENT_CX23888_AV = 8881, /* Integrated A/V decoder */
V4L2_IDENT_CX23888_IR = 8882, /* Integrated infrared controller */
+ /* module ad9389b: just ident 9389 */
+ V4L2_IDENT_AD9389B = 9389,
+
/* module tda9840: just ident 9840 */
V4L2_IDENT_TDA9840 = 9840,
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index a298ec49ddc4..1a0b2db4c5d3 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -133,7 +133,7 @@ struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,
struct i2c_adapter *adapter, struct i2c_board_info *info,
const unsigned short *probe_addrs);
-/* Initialize an v4l2_subdev with data from an i2c_client struct */
+/* Initialize a v4l2_subdev with data from an i2c_client struct */
void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
const struct v4l2_subdev_ops *ops);
/* Return i2c client address of v4l2_subdev. */
@@ -166,7 +166,7 @@ struct spi_device;
struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
struct spi_master *master, struct spi_board_info *info);
-/* Initialize an v4l2_subdev with data from an spi_device struct */
+/* Initialize a v4l2_subdev with data from an spi_device struct */
void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
const struct v4l2_subdev_ops *ops);
#endif
@@ -212,4 +212,17 @@ const struct v4l2_frmsize_discrete *v4l2_find_nearest_format(
const struct v4l2_discrete_probe *probe,
s32 width, s32 height);
+bool v4l_match_dv_timings(const struct v4l2_dv_timings *t1,
+ const struct v4l2_dv_timings *t2,
+ unsigned pclock_delta);
+
+bool v4l2_detect_cvt(unsigned frame_height, unsigned hfreq, unsigned vsync,
+ u32 polarities, struct v4l2_dv_timings *fmt);
+
+bool v4l2_detect_gtf(unsigned frame_height, unsigned hfreq, unsigned vsync,
+ u32 polarities, struct v4l2_fract aspect,
+ struct v4l2_dv_timings *fmt);
+
+struct v4l2_fract v4l2_calc_aspect_ratio(u8 hor_landscape, u8 vert_portrait);
+
#endif /* V4L2_COMMON_H_ */
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 776605f1cbe2..801adb466bd2 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -384,14 +384,28 @@ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
* @hdl: The control handler.
* @add: The control handler whose controls you want to add to
* the @hdl control handler.
+ * @filter: This function will filter which controls should be added.
*
- * Does nothing if either of the two is a NULL pointer.
+ * Does nothing if either of the two handlers is a NULL pointer.
+ * If @filter is NULL, then all controls are added. Otherwise only those
+ * controls for which @filter returns true will be added.
* In case of an error @hdl->error will be set to the error code (if it
* wasn't set already).
*/
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
- struct v4l2_ctrl_handler *add);
+ struct v4l2_ctrl_handler *add,
+ bool (*filter)(const struct v4l2_ctrl *ctrl));
+/** v4l2_ctrl_radio_filter() - Standard filter for radio controls.
+ * @ctrl: The control that is filtered.
+ *
+ * This will return true for any controls that are valid for radio device
+ * nodes. Those are all of the V4L2_CID_AUDIO_* user controls and all FM
+ * transmitter class controls.
+ *
+ * This function is to be used with v4l2_ctrl_add_handler().
+ */
+bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl);
/** v4l2_ctrl_cluster() - Mark all controls in the cluster as belonging to that cluster.
* @ncontrols: The number of controls in this cluster.
@@ -511,6 +525,29 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
*/
int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
+/** v4l2_ctrl_g_ctrl_int64() - Helper function to get a 64-bit control's value from within a driver.
+ * @ctrl: The control.
+ *
+ * This returns the control's value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for 64-bit integer type controls only.
+ */
+s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl);
+
+/** v4l2_ctrl_s_ctrl_int64() - Helper function to set a 64-bit control's value from within a driver.
+ * @ctrl: The control.
+ * @val: The new value.
+ *
+ * This set the control's new value safely by going through the control
+ * framework. This function will lock the control's handler, so it cannot be
+ * used from within the &v4l2_ctrl_ops functions.
+ *
+ * This function is for 64-bit integer type controls only.
+ */
+int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val);
+
/* Internal helper functions that deal with control events. */
extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops;
void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new);
@@ -523,7 +560,7 @@ int v4l2_ctrl_log_status(struct file *file, void *fh);
/* Can be used as a vidioc_subscribe_event function that just subscribes
control events. */
int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
+ const struct v4l2_event_subscription *sub);
/* Can be used as a poll function that just polls for control events. */
unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait);
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 5c416cdc88d5..95d1c91770f4 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -26,6 +26,12 @@
#define VFL_TYPE_SUBDEV 3
#define VFL_TYPE_MAX 4
+/* Is this a receiver, transmitter or mem-to-mem? */
+/* Ignored for VFL_TYPE_SUBDEV. */
+#define VFL_DIR_RX 0
+#define VFL_DIR_TX 1
+#define VFL_DIR_M2M 2
+
struct v4l2_ioctl_callbacks;
struct video_device;
struct v4l2_device;
@@ -39,9 +45,6 @@ struct v4l2_ctrl_handler;
#define V4L2_FL_USES_V4L2_FH (1)
/* Use the prio field of v4l2_fh for core priority checking */
#define V4L2_FL_USE_FH_PRIO (2)
-/* If ioctl core locking is in use, then apply that also to all
- file operations. Don't use this flag in new drivers! */
-#define V4L2_FL_LOCK_ALL_FOPS (3)
/* Priority helper functions */
@@ -108,7 +111,8 @@ struct video_device
/* device info */
char name[32];
- int vfl_type;
+ int vfl_type; /* device type */
+ int vfl_dir; /* receiver, transmitter or m2m */
/* 'minor' is set to -1 if the registration failed */
int minor;
u16 num;
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
index 2885a810a128..e7c5d170a9cd 100644
--- a/include/media/v4l2-event.h
+++ b/include/media/v4l2-event.h
@@ -124,10 +124,10 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev);
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev);
int v4l2_event_pending(struct v4l2_fh *fh);
int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub, unsigned elems,
+ const struct v4l2_event_subscription *sub, unsigned elems,
const struct v4l2_subscribed_event_ops *ops);
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
+ const struct v4l2_event_subscription *sub);
void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
#endif /* V4L2_EVENT_H */
diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
index e614c9c15e56..e48b571ca37d 100644
--- a/include/media/v4l2-ioctl.h
+++ b/include/media/v4l2-ioctl.h
@@ -40,8 +40,6 @@ struct v4l2_ioctl_ops {
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);
/* VIDIOC_G_FMT handlers */
int (*vidioc_g_fmt_vid_cap) (struct file *file, void *fh,
@@ -64,8 +62,6 @@ struct v4l2_ioctl_ops {
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);
/* VIDIOC_S_FMT handlers */
int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh,
@@ -88,8 +84,6 @@ struct v4l2_ioctl_ops {
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);
/* VIDIOC_TRY_FMT handlers */
int (*vidioc_try_fmt_vid_cap) (struct file *file, void *fh,
@@ -112,8 +106,6 @@ struct v4l2_ioctl_ops {
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);
/* Buffer handlers */
int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b);
@@ -128,7 +120,7 @@ struct v4l2_ioctl_ops {
int (*vidioc_g_fbuf) (struct file *file, void *fh,
struct v4l2_framebuffer *a);
int (*vidioc_s_fbuf) (struct file *file, void *fh,
- struct v4l2_framebuffer *a);
+ const struct v4l2_framebuffer *a);
/* Stream on/off */
int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i);
@@ -175,7 +167,7 @@ struct v4l2_ioctl_ops {
int (*vidioc_g_audio) (struct file *file, void *fh,
struct v4l2_audio *a);
int (*vidioc_s_audio) (struct file *file, void *fh,
- struct v4l2_audio *a);
+ const struct v4l2_audio *a);
/* Audio out ioctls */
int (*vidioc_enumaudout) (struct file *file, void *fh,
@@ -183,18 +175,18 @@ struct v4l2_ioctl_ops {
int (*vidioc_g_audout) (struct file *file, void *fh,
struct v4l2_audioout *a);
int (*vidioc_s_audout) (struct file *file, void *fh,
- struct v4l2_audioout *a);
+ const struct v4l2_audioout *a);
int (*vidioc_g_modulator) (struct file *file, void *fh,
struct v4l2_modulator *a);
int (*vidioc_s_modulator) (struct file *file, void *fh,
- struct v4l2_modulator *a);
+ const struct v4l2_modulator *a);
/* Crop ioctls */
int (*vidioc_cropcap) (struct file *file, void *fh,
struct v4l2_cropcap *a);
int (*vidioc_g_crop) (struct file *file, void *fh,
struct v4l2_crop *a);
int (*vidioc_s_crop) (struct file *file, void *fh,
- struct v4l2_crop *a);
+ const struct v4l2_crop *a);
int (*vidioc_g_selection) (struct file *file, void *fh,
struct v4l2_selection *s);
int (*vidioc_s_selection) (struct file *file, void *fh,
@@ -203,7 +195,7 @@ struct v4l2_ioctl_ops {
int (*vidioc_g_jpegcomp) (struct file *file, void *fh,
struct v4l2_jpegcompression *a);
int (*vidioc_s_jpegcomp) (struct file *file, void *fh,
- struct v4l2_jpegcompression *a);
+ const struct v4l2_jpegcompression *a);
int (*vidioc_g_enc_index) (struct file *file, void *fh,
struct v4l2_enc_idx *a);
int (*vidioc_encoder_cmd) (struct file *file, void *fh,
@@ -241,7 +233,7 @@ struct v4l2_ioctl_ops {
int (*vidioc_log_status) (struct file *file, void *fh);
int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh,
- struct v4l2_hw_freq_seek *a);
+ const struct v4l2_hw_freq_seek *a);
/* Debugging ioctls */
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -281,9 +273,9 @@ struct v4l2_ioctl_ops {
struct v4l2_dv_timings_cap *cap);
int (*vidioc_subscribe_event) (struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
+ const struct v4l2_event_subscription *sub);
int (*vidioc_unsubscribe_event)(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub);
+ const struct v4l2_event_subscription *sub);
/* For other private ioctls */
long (*vidioc_default) (struct file *file, void *fh,
diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h
index 16ac4733e80d..131cc4a53675 100644
--- a/include/media/v4l2-mem2mem.h
+++ b/include/media/v4l2-mem2mem.h
@@ -140,7 +140,7 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb);
static inline
unsigned int v4l2_m2m_num_src_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
{
- return m2m_ctx->cap_q_ctx.num_rdy;
+ return m2m_ctx->out_q_ctx.num_rdy;
}
/**
@@ -150,7 +150,7 @@ unsigned int v4l2_m2m_num_src_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
static inline
unsigned int v4l2_m2m_num_dst_bufs_ready(struct v4l2_m2m_ctx *m2m_ctx)
{
- return m2m_ctx->out_q_ctx.num_rdy;
+ return m2m_ctx->cap_q_ctx.num_rdy;
}
void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index c35a3545e273..2ecd7377153b 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -120,7 +120,7 @@ struct v4l2_subdev_io_pin_config {
each pin being configured. This function could be called at times
other than just subdevice initialization.
- init: initialize the sensor registors to some sort of reasonable default
+ init: initialize the sensor registers to some sort of reasonable default
values. Do not use for new drivers and should be removed in existing
drivers.
@@ -194,7 +194,7 @@ struct v4l2_subdev_tuner_ops {
int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt);
int (*g_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
- int (*s_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm);
+ int (*s_modulator)(struct v4l2_subdev *sd, const struct v4l2_modulator *vm);
int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type);
int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config);
};
@@ -286,7 +286,7 @@ struct v4l2_subdev_video_ops {
int (*s_stream)(struct v4l2_subdev *sd, int enable);
int (*cropcap)(struct v4l2_subdev *sd, struct v4l2_cropcap *cc);
int (*g_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
- int (*s_crop)(struct v4l2_subdev *sd, struct v4l2_crop *crop);
+ int (*s_crop)(struct v4l2_subdev *sd, const struct v4l2_crop *crop);
int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
int (*g_frame_interval)(struct v4l2_subdev *sd,
@@ -476,6 +476,8 @@ struct v4l2_subdev_pad_ops {
struct v4l2_subdev_selection *sel);
int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel);
+ int (*get_edid)(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid);
+ int (*set_edid)(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid);
#ifdef CONFIG_MEDIA_CONTROLLER
int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link,
struct v4l2_subdev_format *source_fmt,
diff --git a/include/media/videobuf-dvb.h b/include/media/videobuf-dvb.h
index bf365721d6b0..d63965a1faaf 100644
--- a/include/media/videobuf-dvb.h
+++ b/include/media/videobuf-dvb.h
@@ -45,9 +45,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f,
void *adapter_priv,
struct device *device,
short *adapter_nr,
- int mfe_shared,
- int (*fe_ioctl_override)(struct dvb_frontend *,
- unsigned int, void *, unsigned int));
+ int mfe_shared);
void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f);
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 8dd9b6cc296b..e04252a9fea6 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -324,7 +324,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create);
int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b);
-int vb2_queue_init(struct vb2_queue *q);
+int __must_check vb2_queue_init(struct vb2_queue *q);
void vb2_queue_release(struct vb2_queue *q);
diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h
index ca356a734920..8b27927b2a55 100644
--- a/include/net/bluetooth/smp.h
+++ b/include/net/bluetooth/smp.h
@@ -136,7 +136,7 @@ struct smp_chan {
};
/* SMP Commands */
-int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 493fa0c79005..3d254e10ff30 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -96,6 +96,7 @@ enum ieee80211_band {
* is not permitted.
* @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
* is not permitted.
+ * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel.
*/
enum ieee80211_channel_flags {
IEEE80211_CHAN_DISABLED = 1<<0,
@@ -104,6 +105,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_RADAR = 1<<3,
IEEE80211_CHAN_NO_HT40PLUS = 1<<4,
IEEE80211_CHAN_NO_HT40MINUS = 1<<5,
+ IEEE80211_CHAN_NO_OFDM = 1<<6,
};
#define IEEE80211_CHAN_NO_HT40 \
diff --git a/include/net/codel.h b/include/net/codel.h
index 550debfc2403..389cf621161d 100644
--- a/include/net/codel.h
+++ b/include/net/codel.h
@@ -305,6 +305,8 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch,
}
}
} else if (drop) {
+ u32 delta;
+
if (params->ecn && INET_ECN_set_ce(skb)) {
stats->ecn_mark++;
} else {
@@ -320,9 +322,11 @@ static struct sk_buff *codel_dequeue(struct Qdisc *sch,
* assume that the drop rate that controlled the queue on the
* last cycle is a good starting point to control it now.
*/
- if (codel_time_before(now - vars->drop_next,
+ delta = vars->count - vars->lastcount;
+ if (delta > 1 &&
+ codel_time_before(now - vars->drop_next,
16 * params->interval)) {
- vars->count = (vars->count - vars->lastcount) | 1;
+ vars->count = delta;
/* we dont care if rec_inv_sqrt approximation
* is not very precise :
* Next Newton steps will correct it quadratically.
diff --git a/include/net/dst.h b/include/net/dst.h
index baf597890064..621e3513ef5e 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -110,7 +110,7 @@ struct dst_entry {
};
extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
-extern const u32 dst_default_metrics[RTAX_MAX];
+extern const u32 dst_default_metrics[];
#define DST_METRICS_READ_ONLY 0x1UL
#define __DST_METRICS_PTR(Y) \
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 5ee66f517b4f..ba1d3615acbb 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -39,6 +39,7 @@ struct inet_connection_sock_af_ops {
int (*queue_xmit)(struct sk_buff *skb, struct flowi *fl);
void (*send_check)(struct sock *sk, struct sk_buff *skb);
int (*rebuild_header)(struct sock *sk);
+ void (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb);
int (*conn_request)(struct sock *sk, struct sk_buff *skb);
struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 83b567fe1941..613cfa401672 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -249,13 +249,4 @@ static inline __u8 inet_sk_flowi_flags(const struct sock *sk)
return flags;
}
-static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
-{
- struct dst_entry *dst = skb_dst(skb);
-
- dst_hold(dst);
- sk->sk_rx_dst = dst;
- inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
-}
-
#endif /* _INET_SOCK_H */
diff --git a/include/net/ip.h b/include/net/ip.h
index bd5e444a19ce..5a5d84d3d2c6 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -120,7 +120,7 @@ extern struct sk_buff *__ip_make_skb(struct sock *sk,
struct flowi4 *fl4,
struct sk_buff_head *queue,
struct inet_cork *cork);
-extern int ip_send_skb(struct sk_buff *skb);
+extern int ip_send_skb(struct net *net, struct sk_buff *skb);
extern int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
extern void ip_flush_pending_frames(struct sock *sk);
extern struct sk_buff *ip_make_skb(struct sock *sk,
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 0fedbd8d747a..9fc7114159e8 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -111,9 +111,8 @@ struct rt6_info {
struct inet6_dev *rt6i_idev;
unsigned long _rt6i_peer;
-#ifdef CONFIG_XFRM
- u32 rt6i_flow_cache_genid;
-#endif
+ u32 rt6i_genid;
+
/* more non-fragment space at head required */
unsigned short rt6i_nfheader_len;
diff --git a/include/net/llc.h b/include/net/llc.h
index 226c846cab08..f2d0fc570527 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -133,7 +133,7 @@ extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb,
extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_station_init(void);
+extern void llc_station_init(void);
extern void llc_station_exit(void);
#ifdef CONFIG_PROC_FS
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index ae1cd6c9ba52..fd87963a0ea5 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -102,6 +102,7 @@ struct net {
#endif
struct netns_ipvs *ipvs;
struct sock *diag_nlsk;
+ atomic_t rt_genid;
};
@@ -300,5 +301,14 @@ static inline void unregister_net_sysctl_table(struct ctl_table_header *header)
}
#endif
+static inline int rt_genid(struct net *net)
+{
+ return atomic_read(&net->rt_genid);
+}
+
+static inline void rt_genid_bump(struct net *net)
+{
+ atomic_inc(&net->rt_genid);
+}
#endif /* __NET_NET_NAMESPACE_H */
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index e1ce1048fe5f..4a045cda9c60 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -18,6 +18,7 @@ struct nf_conntrack_ecache {
u16 ctmask; /* bitmask of ct events to be delivered */
u16 expmask; /* bitmask of expect events to be delivered */
u32 pid; /* netlink pid of destroyer */
+ struct timer_list timeout;
};
static inline struct nf_conntrack_ecache *
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 1474dd65c66f..eb24dbccd81e 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -65,7 +65,6 @@ struct netns_ipv4 {
unsigned int sysctl_ping_group_range[2];
long sysctl_tcp_mem[3];
- atomic_t rt_genid;
atomic_t dev_addr_genid;
#ifdef CONFIG_IP_MROUTE
diff --git a/include/net/route.h b/include/net/route.h
index 776a27f1ab78..da22243d2760 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -108,7 +108,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct;
struct in_device;
extern int ip_rt_init(void);
-extern void rt_cache_flush(struct net *net, int how);
+extern void rt_cache_flush(struct net *net);
extern void rt_flush_dev(struct net_device *dev);
extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
diff --git a/include/net/scm.h b/include/net/scm.h
index 079d7887dac1..7dc0854f0b38 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -70,9 +70,11 @@ static __inline__ void scm_destroy(struct scm_cookie *scm)
}
static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm)
+ struct scm_cookie *scm, bool forcecreds)
{
memset(scm, 0, sizeof(*scm));
+ if (forcecreds)
+ scm_set_cred(scm, task_tgid(current), current_cred());
unix_get_peersec_dgram(sock, scm);
if (msg->msg_controllen <= 0)
return 0;
diff --git a/include/net/sock.h b/include/net/sock.h
index e067f8c18f88..adb7da20b5a1 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -218,6 +218,7 @@ struct cg_proto;
* @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
* @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
* @sk_gso_max_size: Maximum GSO segment size to build
+ * @sk_gso_max_segs: Maximum number of GSO segments
* @sk_lingertime: %SO_LINGER l_linger setting
* @sk_backlog: always used with the per-socket spinlock held
* @sk_callback_lock: used with the callbacks in the end of this struct
@@ -338,6 +339,7 @@ struct sock {
netdev_features_t sk_route_nocaps;
int sk_gso_type;
unsigned int sk_gso_max_size;
+ u16 sk_gso_max_segs;
int sk_rcvlowat;
unsigned long sk_lingertime;
struct sk_buff_head sk_error_queue;
@@ -621,6 +623,7 @@ enum sock_flags {
SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */
SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
+ SOCK_MEMALLOC, /* VM depends on this socket for swapping */
SOCK_TIMESTAMPING_TX_HARDWARE, /* %SOF_TIMESTAMPING_TX_HARDWARE */
SOCK_TIMESTAMPING_TX_SOFTWARE, /* %SOF_TIMESTAMPING_TX_SOFTWARE */
SOCK_TIMESTAMPING_RX_HARDWARE, /* %SOF_TIMESTAMPING_RX_HARDWARE */
@@ -658,6 +661,26 @@ static inline bool sock_flag(const struct sock *sk, enum sock_flags flag)
return test_bit(flag, &sk->sk_flags);
}
+#ifdef CONFIG_NET
+extern struct static_key memalloc_socks;
+static inline int sk_memalloc_socks(void)
+{
+ return static_key_false(&memalloc_socks);
+}
+#else
+
+static inline int sk_memalloc_socks(void)
+{
+ return 0;
+}
+
+#endif
+
+static inline gfp_t sk_gfp_atomic(struct sock *sk, gfp_t gfp_mask)
+{
+ return GFP_ATOMIC | (sk->sk_allocation & __GFP_MEMALLOC);
+}
+
static inline void sk_acceptq_removed(struct sock *sk)
{
sk->sk_ack_backlog--;
@@ -733,8 +756,13 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s
return 0;
}
+extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+
static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
+ if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+ return __sk_backlog_rcv(sk, skb);
+
return sk->sk_backlog_rcv(sk, skb);
}
@@ -798,6 +826,8 @@ extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p);
extern void sk_stream_wait_close(struct sock *sk, long timeo_p);
extern int sk_stream_error(struct sock *sk, int flags, int err);
extern void sk_stream_kill_queues(struct sock *sk);
+extern void sk_set_memalloc(struct sock *sk);
+extern void sk_clear_memalloc(struct sock *sk);
extern int sk_wait_data(struct sock *sk, long *timeo);
@@ -913,7 +943,7 @@ struct proto {
#ifdef SOCK_REFCNT_DEBUG
atomic_t socks;
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
/*
* cgroup specific init/deinit functions. Called once for all
* protocols that implement it, from cgroups populate function.
@@ -994,7 +1024,7 @@ inline void sk_refcnt_debug_release(const struct sock *sk)
#define sk_refcnt_debug_release(sk) do { } while (0)
#endif /* SOCK_REFCNT_DEBUG */
-#if defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) && defined(CONFIG_NET)
+#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_NET)
extern struct static_key memcg_socket_limit_enabled;
static inline struct cg_proto *parent_cg_proto(struct proto *proto,
struct cg_proto *cg_proto)
@@ -1301,12 +1331,14 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size)
__sk_mem_schedule(sk, size, SK_MEM_SEND);
}
-static inline bool sk_rmem_schedule(struct sock *sk, int size)
+static inline bool
+sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, int size)
{
if (!sk_has_account(sk))
return true;
- return size <= sk->sk_forward_alloc ||
- __sk_mem_schedule(sk, size, SK_MEM_RECV);
+ return size<= sk->sk_forward_alloc ||
+ __sk_mem_schedule(sk, size, SK_MEM_RECV) ||
+ skb_pfmemalloc(skb);
}
static inline void sk_mem_reclaim(struct sock *sk)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e19124b84cd2..1f000ffe7075 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -464,6 +464,7 @@ extern int tcp_disconnect(struct sock *sk, int flags);
void tcp_connect_init(struct sock *sk);
void tcp_finish_connect(struct sock *sk, struct sk_buff *skb);
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size);
+void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);
/* From syncookies.c */
extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index d9509eb29b80..639dd1316d37 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -213,6 +213,9 @@ struct xfrm_state {
struct xfrm_lifetime_cur curlft;
struct tasklet_hrtimer mtimer;
+ /* used to fix curlft->add_time when changing date */
+ long saved_tmo;
+
/* Last used time */
unsigned long lastused;
@@ -238,6 +241,7 @@ static inline struct net *xs_net(struct xfrm_state *x)
/* xflags - make enum if more show up */
#define XFRM_TIME_DEFER 1
+#define XFRM_SOFT_EXPIRE 2
enum {
XFRM_STATE_VOID,
@@ -269,6 +273,9 @@ struct xfrm_replay {
int (*check)(struct xfrm_state *x,
struct sk_buff *skb,
__be32 net_seq);
+ int (*recheck)(struct xfrm_state *x,
+ struct sk_buff *skb,
+ __be32 net_seq);
void (*notify)(struct xfrm_state *x, int event);
int (*overflow)(struct xfrm_state *x, struct sk_buff *skb);
};
@@ -288,6 +295,8 @@ struct xfrm_policy_afinfo {
struct flowi *fl,
int reverse);
int (*get_tos)(const struct flowi *fl);
+ void (*init_dst)(struct net *net,
+ struct xfrm_dst *dst);
int (*init_path)(struct xfrm_dst *path,
struct dst_entry *dst,
int nfheader_len);
diff --git a/include/sound/es1688.h b/include/sound/es1688.h
index 3ec7ecbe2502..f752dd33dfaf 100644
--- a/include/sound/es1688.h
+++ b/include/sound/es1688.h
@@ -29,6 +29,7 @@
#define ES1688_HW_AUTO 0x0000
#define ES1688_HW_688 0x0001
#define ES1688_HW_1688 0x0002
+#define ES1688_HW_UNDEF 0x0003
struct snd_es1688 {
unsigned long port; /* port of ESS chip */
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index c75c0d1a85e2..cdca2ab1e711 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1075,7 +1075,8 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
const char *snd_pcm_format_name(snd_pcm_format_t format);
/**
- * Get a string naming the direction of a stream
+ * snd_pcm_stream_str - Get a string naming the direction of a stream
+ * @substream: the pcm substream instance
*/
static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream)
{
diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h
index fe8590cac5c2..098c4de44945 100644
--- a/include/sound/tea575x-tuner.h
+++ b/include/sound/tea575x-tuner.h
@@ -28,6 +28,7 @@
#include <media/v4l2-device.h>
#define TEA575X_FMIF 10700
+#define TEA575X_AMIF 450
#define TEA575X_DATA (1 << 0)
#define TEA575X_CLK (1 << 1)
@@ -52,12 +53,14 @@ struct snd_tea575x {
struct video_device vd; /* video device */
int radio_nr; /* radio_nr */
bool tea5759; /* 5759 chip is present */
+ bool has_am; /* Device can tune to AM freqs */
bool cannot_read_data; /* Device cannot read the data pin */
bool cannot_mute; /* Device cannot mute */
bool mute; /* Device is muted? */
bool stereo; /* receiving stereo */
bool tuned; /* tuned to a station */
unsigned int val; /* hw value */
+ u32 band; /* 0: FM, 1: FM-Japan, 2: AM */
u32 freq; /* frequency */
struct mutex mutex;
struct snd_tea575x_ops *ops;
@@ -70,5 +73,6 @@ struct snd_tea575x {
int snd_tea575x_init(struct snd_tea575x *tea, struct module *owner);
void snd_tea575x_exit(struct snd_tea575x *tea);
+void snd_tea575x_set_freq(struct snd_tea575x *tea);
#endif /* __SOUND_TEA575X_TUNER_H */
diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h
index f1405d335a96..941c84bf1065 100644
--- a/include/target/target_core_backend.h
+++ b/include/target/target_core_backend.h
@@ -23,7 +23,9 @@ struct se_subsystem_api {
struct se_device *(*create_virtdevice)(struct se_hba *,
struct se_subsystem_dev *, void *);
void (*free_device)(void *);
- int (*transport_complete)(struct se_cmd *cmd, struct scatterlist *);
+ void (*transport_complete)(struct se_cmd *cmd,
+ struct scatterlist *,
+ unsigned char *);
int (*parse_cdb)(struct se_cmd *cmd);
ssize_t (*check_configfs_dev_params)(struct se_hba *,
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 128ce46fa48a..5be89373ceac 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -121,6 +121,7 @@
#define SE_INQUIRY_BUF 512
#define SE_MODE_PAGE_BUF 512
+#define SE_SENSE_BUF 96
/* struct se_hba->hba_flags */
enum hba_flags_table {
@@ -503,8 +504,6 @@ struct se_cmd {
u32 se_ordered_id;
/* Total size in bytes associated with command */
u32 data_length;
- /* SCSI Presented Data Transfer Length */
- u32 cmd_spdtl;
u32 residual_count;
u32 orig_fe_lun;
/* Persistent Reservation key */
diff --git a/include/trace/events/gfpflags.h b/include/trace/events/gfpflags.h
index 9fe3a36646e9..d6fd8e5b14b7 100644
--- a/include/trace/events/gfpflags.h
+++ b/include/trace/events/gfpflags.h
@@ -30,6 +30,7 @@
{(unsigned long)__GFP_COMP, "GFP_COMP"}, \
{(unsigned long)__GFP_ZERO, "GFP_ZERO"}, \
{(unsigned long)__GFP_NOMEMALLOC, "GFP_NOMEMALLOC"}, \
+ {(unsigned long)__GFP_MEMALLOC, "GFP_MEMALLOC"}, \
{(unsigned long)__GFP_HARDWALL, "GFP_HARDWALL"}, \
{(unsigned long)__GFP_THISNODE, "GFP_THISNODE"}, \
{(unsigned long)__GFP_RECLAIMABLE, "GFP_RECLAIMABLE"}, \
diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h
index 5f889f16b0c8..08fa27244da7 100644
--- a/include/trace/events/kmem.h
+++ b/include/trace/events/kmem.h
@@ -214,7 +214,7 @@ TRACE_EVENT(mm_page_alloc,
TP_printk("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s",
__entry->page,
- page_to_pfn(__entry->page),
+ __entry->page ? page_to_pfn(__entry->page) : 0,
__entry->order,
__entry->migratetype,
show_gfp_flags(__entry->gfp_flags))
@@ -240,7 +240,7 @@ DECLARE_EVENT_CLASS(mm_page,
TP_printk("page=%p pfn=%lu order=%u migratetype=%d percpu_refill=%d",
__entry->page,
- page_to_pfn(__entry->page),
+ __entry->page ? page_to_pfn(__entry->page) : 0,
__entry->order,
__entry->migratetype,
__entry->order == 0)
diff --git a/include/trace/events/random.h b/include/trace/events/random.h
new file mode 100644
index 000000000000..422df19de732
--- /dev/null
+++ b/include/trace/events/random.h
@@ -0,0 +1,134 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM random
+
+#if !defined(_TRACE_RANDOM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_RANDOM_H
+
+#include <linux/writeback.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(random__mix_pool_bytes,
+ TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+ TP_ARGS(pool_name, bytes, IP),
+
+ TP_STRUCT__entry(
+ __field( const char *, pool_name )
+ __field( int, bytes )
+ __field(unsigned long, IP )
+ ),
+
+ TP_fast_assign(
+ __entry->pool_name = pool_name;
+ __entry->bytes = bytes;
+ __entry->IP = IP;
+ ),
+
+ TP_printk("%s pool: bytes %d caller %pF",
+ __entry->pool_name, __entry->bytes, (void *)__entry->IP)
+);
+
+DEFINE_EVENT(random__mix_pool_bytes, mix_pool_bytes,
+ TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+ TP_ARGS(pool_name, bytes, IP)
+);
+
+DEFINE_EVENT(random__mix_pool_bytes, mix_pool_bytes_nolock,
+ TP_PROTO(const char *pool_name, int bytes, unsigned long IP),
+
+ TP_ARGS(pool_name, bytes, IP)
+);
+
+TRACE_EVENT(credit_entropy_bits,
+ TP_PROTO(const char *pool_name, int bits, int entropy_count,
+ int entropy_total, unsigned long IP),
+
+ TP_ARGS(pool_name, bits, entropy_count, entropy_total, IP),
+
+ TP_STRUCT__entry(
+ __field( const char *, pool_name )
+ __field( int, bits )
+ __field( int, entropy_count )
+ __field( int, entropy_total )
+ __field(unsigned long, IP )
+ ),
+
+ TP_fast_assign(
+ __entry->pool_name = pool_name;
+ __entry->bits = bits;
+ __entry->entropy_count = entropy_count;
+ __entry->entropy_total = entropy_total;
+ __entry->IP = IP;
+ ),
+
+ TP_printk("%s pool: bits %d entropy_count %d entropy_total %d "
+ "caller %pF", __entry->pool_name, __entry->bits,
+ __entry->entropy_count, __entry->entropy_total,
+ (void *)__entry->IP)
+);
+
+TRACE_EVENT(get_random_bytes,
+ TP_PROTO(int nbytes, unsigned long IP),
+
+ TP_ARGS(nbytes, IP),
+
+ TP_STRUCT__entry(
+ __field( int, nbytes )
+ __field(unsigned long, IP )
+ ),
+
+ TP_fast_assign(
+ __entry->nbytes = nbytes;
+ __entry->IP = IP;
+ ),
+
+ TP_printk("nbytes %d caller %pF", __entry->nbytes, (void *)__entry->IP)
+);
+
+DECLARE_EVENT_CLASS(random__extract_entropy,
+ TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+ unsigned long IP),
+
+ TP_ARGS(pool_name, nbytes, entropy_count, IP),
+
+ TP_STRUCT__entry(
+ __field( const char *, pool_name )
+ __field( int, nbytes )
+ __field( int, entropy_count )
+ __field(unsigned long, IP )
+ ),
+
+ TP_fast_assign(
+ __entry->pool_name = pool_name;
+ __entry->nbytes = nbytes;
+ __entry->entropy_count = entropy_count;
+ __entry->IP = IP;
+ ),
+
+ TP_printk("%s pool: nbytes %d entropy_count %d caller %pF",
+ __entry->pool_name, __entry->nbytes, __entry->entropy_count,
+ (void *)__entry->IP)
+);
+
+
+DEFINE_EVENT(random__extract_entropy, extract_entropy,
+ TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+ unsigned long IP),
+
+ TP_ARGS(pool_name, nbytes, entropy_count, IP)
+);
+
+DEFINE_EVENT(random__extract_entropy, extract_entropy_user,
+ TP_PROTO(const char *pool_name, int nbytes, int entropy_count,
+ unsigned long IP),
+
+ TP_ARGS(pool_name, nbytes, entropy_count, IP)
+);
+
+
+
+#endif /* _TRACE_RANDOM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index ea7a2035456d..5a8671e8a67f 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -73,6 +73,9 @@ DECLARE_EVENT_CLASS(sched_wakeup_template,
__entry->prio = p->prio;
__entry->success = success;
__entry->target_cpu = task_cpu(p);
+ )
+ TP_perf_assign(
+ __perf_task(p);
),
TP_printk("comm=%s pid=%d prio=%d success=%d target_cpu=%03d",
@@ -325,6 +328,7 @@ DECLARE_EVENT_CLASS(sched_stat_template,
)
TP_perf_assign(
__perf_count(delay);
+ __perf_task(tsk);
),
TP_printk("comm=%s pid=%d delay=%Lu [ns]",
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index c6bc2faaf261..a763888a36f9 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -712,6 +712,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call
#undef __perf_count
#define __perf_count(c) __count = (c)
+#undef __perf_task
+#define __perf_task(t) __task = (t)
+
#undef TP_perf_assign
#define TP_perf_assign(args...) args
@@ -725,6 +728,7 @@ perf_trace_##call(void *__data, proto) \
struct ftrace_raw_##call *entry; \
struct pt_regs __regs; \
u64 __addr = 0, __count = 1; \
+ struct task_struct *__task = NULL; \
struct hlist_head *head; \
int __entry_size; \
int __data_size; \
@@ -752,7 +756,7 @@ perf_trace_##call(void *__data, proto) \
\
head = this_cpu_ptr(event_call->perf_events); \
perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \
- __count, &__regs, head); \
+ __count, &__regs, head, __task); \
}
/*
diff --git a/include/video/da8xx-fb.h b/include/video/da8xx-fb.h
index 89d43b3d4cb9..5a0e4f9efb53 100644
--- a/include/video/da8xx-fb.h
+++ b/include/video/da8xx-fb.h
@@ -82,6 +82,9 @@ struct lcd_ctrl_config {
/* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */
unsigned char raster_order;
+
+ /* DMA FIFO threshold */
+ int fifo_th;
};
struct lcd_sync_arg {
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index c8e59b4a3364..a6267a2d292b 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -48,6 +48,10 @@
#define DISPC_IRQ_FRAMEDONEWB (1 << 23)
#define DISPC_IRQ_FRAMEDONETV (1 << 24)
#define DISPC_IRQ_WBBUFFEROVERFLOW (1 << 25)
+#define DISPC_IRQ_FRAMEDONE3 (1 << 26)
+#define DISPC_IRQ_VSYNC3 (1 << 27)
+#define DISPC_IRQ_ACBIAS_COUNT_STAT3 (1 << 28)
+#define DISPC_IRQ_SYNC_LOST3 (1 << 29)
struct omap_dss_device;
struct omap_overlay_manager;
@@ -75,6 +79,7 @@ enum omap_channel {
OMAP_DSS_CHANNEL_LCD = 0,
OMAP_DSS_CHANNEL_DIGIT = 1,
OMAP_DSS_CHANNEL_LCD2 = 2,
+ OMAP_DSS_CHANNEL_LCD3 = 3,
};
enum omap_color_mode {
@@ -99,11 +104,6 @@ enum omap_color_mode {
OMAP_DSS_COLOR_XRGB16_1555 = 1 << 18, /* xRGB16 - 1555 */
};
-enum omap_lcd_display_type {
- OMAP_DSS_LCD_DISPLAY_STN,
- OMAP_DSS_LCD_DISPLAY_TFT,
-};
-
enum omap_dss_load_mode {
OMAP_DSS_LOAD_CLUT_AND_FRAME = 0,
OMAP_DSS_LOAD_CLUT_ONLY = 1,
@@ -121,15 +121,15 @@ enum omap_rfbi_te_mode {
OMAP_DSS_RFBI_TE_MODE_2 = 2,
};
-enum omap_panel_config {
- OMAP_DSS_LCD_IVS = 1<<0,
- OMAP_DSS_LCD_IHS = 1<<1,
- OMAP_DSS_LCD_IPC = 1<<2,
- OMAP_DSS_LCD_IEO = 1<<3,
- OMAP_DSS_LCD_RF = 1<<4,
- OMAP_DSS_LCD_ONOFF = 1<<5,
+enum omap_dss_signal_level {
+ OMAPDSS_SIG_ACTIVE_HIGH = 0,
+ OMAPDSS_SIG_ACTIVE_LOW = 1,
+};
- OMAP_DSS_LCD_TFT = 1<<20,
+enum omap_dss_signal_edge {
+ OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+ OMAPDSS_DRIVE_SIG_RISING_EDGE,
+ OMAPDSS_DRIVE_SIG_FALLING_EDGE,
};
enum omap_dss_venc_type {
@@ -167,13 +167,6 @@ enum omap_dss_audio_state {
OMAP_DSS_AUDIO_PLAYING,
};
-/* XXX perhaps this should be removed */
-enum omap_dss_overlay_managers {
- OMAP_DSS_OVL_MGR_LCD,
- OMAP_DSS_OVL_MGR_TV,
- OMAP_DSS_OVL_MGR_LCD2,
-};
-
enum omap_dss_rotation_type {
OMAP_DSS_ROT_DMA = 1 << 0,
OMAP_DSS_ROT_VRFB = 1 << 1,
@@ -268,9 +261,6 @@ struct omap_dss_dsi_videomode_data {
int hfp_blanking_mode;
/* Video port sync events */
- int vp_de_pol;
- int vp_hsync_pol;
- int vp_vsync_pol;
bool vp_vsync_end;
bool vp_hsync_end;
@@ -346,6 +336,19 @@ struct omap_video_timings {
u16 vfp; /* Vertical front porch */
/* Unit: line clocks */
u16 vbp; /* Vertical back porch */
+
+ /* Vsync logic level */
+ enum omap_dss_signal_level vsync_level;
+ /* Hsync logic level */
+ enum omap_dss_signal_level hsync_level;
+ /* Interlaced or Progressive timings */
+ bool interlace;
+ /* Pixel clock edge to drive LCD data */
+ enum omap_dss_signal_edge data_pclk_edge;
+ /* Data enable logic level */
+ enum omap_dss_signal_level de_level;
+ /* Pixel clock edges to drive HSYNC and VSYNC signals */
+ enum omap_dss_signal_edge sync_pclk_edge;
};
#ifdef CONFIG_OMAP2_DSS_VENC
@@ -559,8 +562,6 @@ struct omap_dss_device {
/* Unit: line clocks */
int acb; /* ac-bias pin frequency */
- enum omap_panel_config config;
-
enum omap_dss_dsi_pixel_format dsi_pix_fmt;
enum omap_dss_dsi_mode dsi_mode;
struct omap_dss_dsi_videomode_data dsi_vm_data;
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 7571b27a0ba1..ff43ffc1aab2 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info {
int (*get_brightness)(void);
};
+struct sh_mobile_lcdc_overlay_cfg {
+ int fourcc;
+ unsigned int max_xres;
+ unsigned int max_yres;
+};
+
struct sh_mobile_lcdc_chan_cfg {
int chan;
int fourcc;
@@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg {
struct sh_mobile_lcdc_info {
int clock_source;
struct sh_mobile_lcdc_chan_cfg ch[2];
+ struct sh_mobile_lcdc_overlay_cfg overlays[4];
struct sh_mobile_meram_info *meram_dev;
};
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 29b2fd3b147e..062e6e7f955c 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -15,7 +15,6 @@ enum {
struct sh_mobile_meram_priv;
-struct sh_mobile_meram_ops;
/*
* struct sh_mobile_meram_info - MERAM platform data
@@ -24,7 +23,6 @@ struct sh_mobile_meram_ops;
struct sh_mobile_meram_info {
int addr_mode;
u32 reserved_icbs;
- struct sh_mobile_meram_ops *ops;
struct sh_mobile_meram_priv *priv;
struct platform_device *pdev;
};
@@ -38,26 +36,59 @@ struct sh_mobile_meram_cfg {
struct sh_mobile_meram_icb_cfg icb[2];
};
-struct module;
-struct sh_mobile_meram_ops {
- struct module *module;
- /* register usage of meram */
- void *(*meram_register)(struct sh_mobile_meram_info *meram_dev,
- const struct sh_mobile_meram_cfg *cfg,
- unsigned int xres, unsigned int yres,
- unsigned int pixelformat,
- unsigned int *pitch);
-
- /* unregister usage of meram */
- void (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
- void *data);
-
- /* update meram settings */
- void (*meram_update)(struct sh_mobile_meram_info *meram_dev, void *data,
+#if defined(CONFIG_FB_SH_MOBILE_MERAM) || \
+ defined(CONFIG_FB_SH_MOBILE_MERAM_MODULE)
+unsigned long sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev,
+ size_t size);
+void sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
+ unsigned long mem, size_t size);
+void *sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat,
+ unsigned int *pitch);
+void sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data);
+void sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c,
+ unsigned long *icb_addr_y,
+ unsigned long *icb_addr_c);
+#else
+static inline unsigned long
+sh_mobile_meram_alloc(struct sh_mobile_meram_info *meram_dev, size_t size)
+{
+ return 0;
+}
+
+static inline void
+sh_mobile_meram_free(struct sh_mobile_meram_info *meram_dev,
+ unsigned long mem, size_t size)
+{
+}
+
+static inline void *
+sh_mobile_meram_cache_alloc(struct sh_mobile_meram_info *dev,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat,
+ unsigned int *pitch)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline void
+sh_mobile_meram_cache_free(struct sh_mobile_meram_info *dev, void *data)
+{
+}
+
+static inline void
+sh_mobile_meram_cache_update(struct sh_mobile_meram_info *dev, void *data,
unsigned long base_addr_y,
unsigned long base_addr_c,
unsigned long *icb_addr_y,
- unsigned long *icb_addr_c);
-};
+ unsigned long *icb_addr_c)
+{
+}
+#endif
#endif /* __VIDEO_SH_MOBILE_MERAM_H__ */
diff --git a/include/xen/events.h b/include/xen/events.h
index 9c641deb65d2..04399b28e821 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -58,8 +58,6 @@ void notify_remote_via_irq(int irq);
void xen_irq_resume(void);
-void xen_hvm_prepare_kexec(struct shared_info *sip, unsigned long pfn);
-
/* Clear an irq's pending state, in preparation for polling on it */
void xen_clear_irq_pending(int irq);
void xen_set_irq_pending(int irq);
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h
index 11e27c3af3cb..f19fff8650e9 100644
--- a/include/xen/grant_table.h
+++ b/include/xen/grant_table.h
@@ -187,6 +187,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
struct gnttab_map_grant_ref *kmap_ops,
struct page **pages, unsigned int count);
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
- struct page **pages, unsigned int count, bool clear_pte);
+ struct gnttab_map_grant_ref *kunmap_ops,
+ struct page **pages, unsigned int count);
#endif /* __ASM_GNTTAB_H__ */
diff --git a/init/Kconfig b/init/Kconfig
index b3f55f15e107..af6c7f8ba019 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -686,7 +686,7 @@ config RESOURCE_COUNTERS
This option enables controller independent resource accounting
infrastructure that works with cgroups.
-config CGROUP_MEM_RES_CTLR
+config MEMCG
bool "Memory Resource Controller for Control Groups"
depends on RESOURCE_COUNTERS
select MM_OWNER
@@ -709,9 +709,9 @@ config CGROUP_MEM_RES_CTLR
This config option also selects MM_OWNER config option, which
could in turn add some fork/exit overhead.
-config CGROUP_MEM_RES_CTLR_SWAP
+config MEMCG_SWAP
bool "Memory Resource Controller Swap Extension"
- depends on CGROUP_MEM_RES_CTLR && SWAP
+ depends on MEMCG && SWAP
help
Add swap management feature to memory resource controller. When you
enable this, you can limit mem+swap usage per cgroup. In other words,
@@ -726,9 +726,9 @@ config CGROUP_MEM_RES_CTLR_SWAP
if boot option "swapaccount=0" is set, swap will not be accounted.
Now, memory usage of swap_cgroup is 2 bytes per entry. If swap page
size is 4096bytes, 512k per 1Gbytes of swap.
-config CGROUP_MEM_RES_CTLR_SWAP_ENABLED
+config MEMCG_SWAP_ENABLED
bool "Memory Resource Controller Swap Extension enabled by default"
- depends on CGROUP_MEM_RES_CTLR_SWAP
+ depends on MEMCG_SWAP
default y
help
Memory Resource Controller Swap Extension comes with its price in
@@ -739,9 +739,9 @@ config CGROUP_MEM_RES_CTLR_SWAP_ENABLED
For those who want to have the feature enabled by default should
select this option (if, for some reason, they need to disable it
then swapaccount=0 does the trick).
-config CGROUP_MEM_RES_CTLR_KMEM
+config MEMCG_KMEM
bool "Memory Resource Controller Kernel Memory accounting (EXPERIMENTAL)"
- depends on CGROUP_MEM_RES_CTLR && EXPERIMENTAL
+ depends on MEMCG && EXPERIMENTAL
default n
help
The Kernel Memory extension for Memory Resource Controller can limit
@@ -751,6 +751,21 @@ config CGROUP_MEM_RES_CTLR_KMEM
the kmem extension can use it to guarantee that no group of processes
will ever exhaust kernel resources alone.
+config CGROUP_HUGETLB
+ bool "HugeTLB Resource Controller for Control Groups"
+ depends on RESOURCE_COUNTERS && HUGETLB_PAGE && EXPERIMENTAL
+ default n
+ help
+ Provides a cgroup Resource Controller for HugeTLB pages.
+ When you enable this, you can put a per cgroup limit on HugeTLB usage.
+ The limit is enforced during page fault. Since HugeTLB doesn't
+ support page reclaim, enforcing the limit at page fault time implies
+ that, the application will get SIGBUS signal if it tries to access
+ HugeTLB pages beyond its limit. This requires the application to know
+ beforehand how much HugeTLB pages it would require for its use. The
+ control group is tracked in the third page lru pointer. This means
+ that we cannot use the controller with huge page less than 3 pages.
+
config CGROUP_PERF
bool "Enable perf_event per-cpu per-container group (cgroup) monitoring"
depends on PERF_EVENTS && CGROUPS
diff --git a/init/main.c b/init/main.c
index 95316a1b4a76..b28673087ac0 100644
--- a/init/main.c
+++ b/init/main.c
@@ -461,10 +461,6 @@ static void __init mm_init(void)
percpu_init_late();
pgtable_cache_init();
vmalloc_init();
-#ifdef CONFIG_X86
- if (efi_enabled)
- efi_enter_virtual_mode();
-#endif
}
asmlinkage void __init start_kernel(void)
@@ -506,7 +502,7 @@ asmlinkage void __init start_kernel(void)
setup_per_cpu_areas();
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
- build_all_zonelists(NULL);
+ build_all_zonelists(NULL, NULL);
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);
@@ -606,6 +602,10 @@ asmlinkage void __init start_kernel(void)
calibrate_delay();
pidmap_init();
anon_vma_init();
+#ifdef CONFIG_X86
+ if (efi_enabled)
+ efi_enter_virtual_mode();
+#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index f8e54f5b9080..9a08acc9e649 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -726,7 +726,6 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
struct mq_attr *attr)
{
const struct cred *cred = current_cred();
- struct file *result;
int ret;
if (attr) {
@@ -748,21 +747,11 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
}
mode &= ~current_umask();
- ret = mnt_want_write(path->mnt);
- if (ret)
- return ERR_PTR(ret);
ret = vfs_create(dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
- if (!ret)
- result = dentry_open(path, oflag, cred);
- else
- result = ERR_PTR(ret);
- /*
- * dentry_open() took a persistent mnt_want_write(),
- * so we can now drop this one.
- */
- mnt_drop_write(path->mnt);
- return result;
+ if (ret)
+ return ERR_PTR(ret);
+ return dentry_open(path, oflag, cred);
}
/* Opens existing queue */
@@ -788,7 +777,9 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
struct mq_attr attr;
int fd, error;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
- struct dentry *root = ipc_ns->mq_mnt->mnt_root;
+ struct vfsmount *mnt = ipc_ns->mq_mnt;
+ struct dentry *root = mnt->mnt_root;
+ int ro;
if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
return -EFAULT;
@@ -802,6 +793,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
if (fd < 0)
goto out_putname;
+ ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&root->d_inode->i_mutex);
path.dentry = lookup_one_len(name, root, strlen(name));
@@ -809,7 +801,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
error = PTR_ERR(path.dentry);
goto out_putfd;
}
- path.mnt = mntget(ipc_ns->mq_mnt);
+ path.mnt = mntget(mnt);
if (oflag & O_CREAT) {
if (path.dentry->d_inode) { /* entry already exists */
@@ -820,6 +812,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
}
filp = do_open(&path, oflag);
} else {
+ if (ro) {
+ error = ro;
+ goto out;
+ }
filp = do_create(ipc_ns, root->d_inode,
&path, oflag, mode,
u_attr ? &attr : NULL);
@@ -845,6 +841,7 @@ out_putfd:
fd = error;
}
mutex_unlock(&root->d_inode->i_mutex);
+ mnt_drop_write(mnt);
out_putname:
putname(name);
return fd;
@@ -857,40 +854,38 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
struct dentry *dentry;
struct inode *inode = NULL;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
+ struct vfsmount *mnt = ipc_ns->mq_mnt;
name = getname(u_name);
if (IS_ERR(name))
return PTR_ERR(name);
- mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex,
- I_MUTEX_PARENT);
- dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
+ err = mnt_want_write(mnt);
+ if (err)
+ goto out_name;
+ mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
+ dentry = lookup_one_len(name, mnt->mnt_root, strlen(name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
goto out_unlock;
}
- if (!dentry->d_inode) {
- err = -ENOENT;
- goto out_err;
- }
-
inode = dentry->d_inode;
- if (inode)
+ if (!inode) {
+ err = -ENOENT;
+ } else {
ihold(inode);
- err = mnt_want_write(ipc_ns->mq_mnt);
- if (err)
- goto out_err;
- err = vfs_unlink(dentry->d_parent->d_inode, dentry);
- mnt_drop_write(ipc_ns->mq_mnt);
-out_err:
+ err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+ }
dput(dentry);
out_unlock:
- mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
- putname(name);
+ mutex_unlock(&mnt->mnt_root->d_inode->i_mutex);
if (inode)
iput(inode);
+ mnt_drop_write(mnt);
+out_name:
+ putname(name);
return err;
}
diff --git a/kernel/audit.c b/kernel/audit.c
index 4a3f28d2ca65..ea3b7b6191c7 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1456,6 +1456,27 @@ void audit_log_key(struct audit_buffer *ab, char *key)
}
/**
+ * audit_log_link_denied - report a link restriction denial
+ * @operation: specific link opreation
+ * @link: the path that triggered the restriction
+ */
+void audit_log_link_denied(const char *operation, struct path *link)
+{
+ struct audit_buffer *ab;
+
+ ab = audit_log_start(current->audit_context, GFP_KERNEL,
+ AUDIT_ANOM_LINK);
+ audit_log_format(ab, "op=%s action=denied", operation);
+ audit_log_format(ab, " pid=%d comm=", current->pid);
+ audit_log_untrustedstring(ab, current->comm);
+ audit_log_d_path(ab, " path=", link);
+ audit_log_format(ab, " dev=");
+ audit_log_untrustedstring(ab, link->dentry->d_inode->i_sb->s_id);
+ audit_log_format(ab, " ino=%lu", link->dentry->d_inode->i_ino);
+ audit_log_end(ab);
+}
+
+/**
* audit_log_end - end one audit record
* @ab: the audit_buffer
*
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 3a5ca582ba1e..ed206fd88cca 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -250,7 +250,6 @@ static void untag_chunk(struct node *p)
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
fsnotify_destroy_mark(entry);
- fsnotify_put_mark(entry);
goto out;
}
@@ -259,7 +258,7 @@ static void untag_chunk(struct node *p)
fsnotify_duplicate_mark(&new->mark, entry);
if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) {
- free_chunk(new);
+ fsnotify_put_mark(&new->mark);
goto Fallback;
}
@@ -293,7 +292,7 @@ static void untag_chunk(struct node *p)
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
fsnotify_destroy_mark(entry);
- fsnotify_put_mark(entry);
+ fsnotify_put_mark(&new->mark); /* drop initial reference */
goto out;
Fallback:
@@ -322,7 +321,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree)
entry = &chunk->mark;
if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) {
- free_chunk(chunk);
+ fsnotify_put_mark(entry);
return -ENOSPC;
}
@@ -347,6 +346,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree)
insert_hash(chunk);
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
+ fsnotify_put_mark(entry); /* drop initial reference */
return 0;
}
@@ -396,7 +396,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
fsnotify_duplicate_mark(chunk_entry, old_entry);
if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) {
spin_unlock(&old_entry->lock);
- free_chunk(chunk);
+ fsnotify_put_mark(chunk_entry);
fsnotify_put_mark(old_entry);
return -ENOSPC;
}
@@ -444,8 +444,8 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
spin_unlock(&chunk_entry->lock);
spin_unlock(&old_entry->lock);
fsnotify_destroy_mark(old_entry);
+ fsnotify_put_mark(chunk_entry); /* drop initial reference */
fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */
- fsnotify_put_mark(old_entry); /* and kill it */
return 0;
}
@@ -916,7 +916,12 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify
struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
evict_chunk(chunk);
- fsnotify_put_mark(entry);
+
+ /*
+ * We are guaranteed to have at least one reference to the mark from
+ * either the inode or the caller of fsnotify_destroy_mark().
+ */
+ BUG_ON(atomic_read(&entry->refcnt) < 1);
}
static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode,
diff --git a/kernel/cpu.c b/kernel/cpu.c
index a4eb5227a19e..14d32588cccd 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -416,7 +416,7 @@ int __cpuinit cpu_up(unsigned int cpu)
if (pgdat->node_zonelists->_zonerefs->zone == NULL) {
mutex_lock(&zonelists_mutex);
- build_all_zonelists(NULL);
+ build_all_zonelists(NULL, NULL);
mutex_unlock(&zonelists_mutex);
}
#endif
diff --git a/kernel/debug/kdb/kdb_debugger.c b/kernel/debug/kdb/kdb_debugger.c
index 8b68ce78ff17..be7b33b73d30 100644
--- a/kernel/debug/kdb/kdb_debugger.c
+++ b/kernel/debug/kdb/kdb_debugger.c
@@ -12,6 +12,7 @@
#include <linux/kdb.h>
#include <linux/kdebug.h>
#include <linux/export.h>
+#include <linux/hardirq.h>
#include "kdb_private.h"
#include "../debug_core.h"
@@ -52,6 +53,9 @@ int kdb_stub(struct kgdb_state *ks)
if (atomic_read(&kgdb_setting_breakpoint))
reason = KDB_REASON_KEYBOARD;
+ if (in_nmi())
+ reason = KDB_REASON_NMI;
+
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) {
if ((bp->bp_enabled) && (bp->bp_addr == addr)) {
reason = KDB_REASON_BREAK;
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index bb9520f0f6ff..0a69d2adc4f3 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -715,9 +715,6 @@ kdb_printit:
/* check for having reached the LINES number of printed lines */
if (kdb_nextline == linecount) {
char buf1[16] = "";
-#if defined(CONFIG_SMP)
- char buf2[32];
-#endif
/* Watch out for recursion here. Any routine that calls
* kdb_printf will come back through here. And kdb_read
@@ -732,14 +729,6 @@ kdb_printit:
if (moreprompt == NULL)
moreprompt = "more> ";
-#if defined(CONFIG_SMP)
- if (strchr(moreprompt, '%')) {
- sprintf(buf2, moreprompt, get_cpu());
- put_cpu();
- moreprompt = buf2;
- }
-#endif
-
kdb_input_flush();
c = console_drivers;
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 1f91413edb87..31df1706b9a9 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -139,11 +139,10 @@ static const int __nkdb_err = sizeof(kdbmsgs) / sizeof(kdbmsg_t);
static char *__env[] = {
#if defined(CONFIG_SMP)
"PROMPT=[%d]kdb> ",
- "MOREPROMPT=[%d]more> ",
#else
"PROMPT=kdb> ",
- "MOREPROMPT=more> ",
#endif
+ "MOREPROMPT=more> ",
"RADIX=16",
"MDCOUNT=8", /* lines of md output */
KDB_PLATFORM_ENV,
@@ -1236,18 +1235,6 @@ static int kdb_local(kdb_reason_t reason, int error, struct pt_regs *regs,
*cmdbuf = '\0';
*(cmd_hist[cmd_head]) = '\0';
- if (KDB_FLAG(ONLY_DO_DUMP)) {
- /* kdb is off but a catastrophic error requires a dump.
- * Take the dump and reboot.
- * Turn on logging so the kdb output appears in the log
- * buffer in the dump.
- */
- const char *setargs[] = { "set", "LOGGING", "1" };
- kdb_set(2, setargs);
- kdb_reboot(0, NULL);
- /*NOTREACHED*/
- }
-
do_full_getstr:
#if defined(CONFIG_SMP)
snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"),
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index 6581a040f399..98d4597f43d6 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -153,7 +153,8 @@ put_callchain_entry(int rctx)
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
}
-struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
+struct perf_callchain_entry *
+perf_callchain(struct perf_event *event, struct pt_regs *regs)
{
int rctx;
struct perf_callchain_entry *entry;
@@ -178,6 +179,12 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
}
if (regs) {
+ /*
+ * Disallow cross-task user callchains.
+ */
+ if (event->ctx->task && event->ctx->task != current)
+ goto exit_put;
+
perf_callchain_store(entry, PERF_CONTEXT_USER);
perf_callchain_user(entry, regs);
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index f1cf0edeb39a..7fee567153f0 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1253,7 +1253,7 @@ retry:
/*
* Cross CPU call to disable a performance event
*/
-static int __perf_event_disable(void *info)
+int __perf_event_disable(void *info)
{
struct perf_event *event = info;
struct perf_event_context *ctx = event->ctx;
@@ -2935,12 +2935,12 @@ EXPORT_SYMBOL_GPL(perf_event_release_kernel);
/*
* Called when the last reference to the file is gone.
*/
-static int perf_release(struct inode *inode, struct file *file)
+static void put_event(struct perf_event *event)
{
- struct perf_event *event = file->private_data;
struct task_struct *owner;
- file->private_data = NULL;
+ if (!atomic_long_dec_and_test(&event->refcount))
+ return;
rcu_read_lock();
owner = ACCESS_ONCE(event->owner);
@@ -2975,7 +2975,13 @@ static int perf_release(struct inode *inode, struct file *file)
put_task_struct(owner);
}
- return perf_event_release_kernel(event);
+ perf_event_release_kernel(event);
+}
+
+static int perf_release(struct inode *inode, struct file *file)
+{
+ put_event(file->private_data);
+ return 0;
}
u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running)
@@ -3227,7 +3233,7 @@ unlock:
static const struct file_operations perf_fops;
-static struct perf_event *perf_fget_light(int fd, int *fput_needed)
+static struct file *perf_fget_light(int fd, int *fput_needed)
{
struct file *file;
@@ -3241,7 +3247,7 @@ static struct perf_event *perf_fget_light(int fd, int *fput_needed)
return ERR_PTR(-EBADF);
}
- return file->private_data;
+ return file;
}
static int perf_event_set_output(struct perf_event *event,
@@ -3273,19 +3279,21 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case PERF_EVENT_IOC_SET_OUTPUT:
{
+ struct file *output_file = NULL;
struct perf_event *output_event = NULL;
int fput_needed = 0;
int ret;
if (arg != -1) {
- output_event = perf_fget_light(arg, &fput_needed);
- if (IS_ERR(output_event))
- return PTR_ERR(output_event);
+ output_file = perf_fget_light(arg, &fput_needed);
+ if (IS_ERR(output_file))
+ return PTR_ERR(output_file);
+ output_event = output_file->private_data;
}
ret = perf_event_set_output(event, output_event);
if (output_event)
- fput_light(output_event->filp, fput_needed);
+ fput_light(output_file, fput_needed);
return ret;
}
@@ -4039,7 +4047,7 @@ void perf_prepare_sample(struct perf_event_header *header,
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
int size = 1;
- data->callchain = perf_callchain(regs);
+ data->callchain = perf_callchain(event, regs);
if (data->callchain)
size += data->callchain->nr;
@@ -5209,7 +5217,8 @@ static int perf_tp_event_match(struct perf_event *event,
}
void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
- struct pt_regs *regs, struct hlist_head *head, int rctx)
+ struct pt_regs *regs, struct hlist_head *head, int rctx,
+ struct task_struct *task)
{
struct perf_sample_data data;
struct perf_event *event;
@@ -5228,6 +5237,31 @@ void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
perf_swevent_event(event, count, &data, regs);
}
+ /*
+ * If we got specified a target task, also iterate its context and
+ * deliver this event there too.
+ */
+ if (task && task != current) {
+ struct perf_event_context *ctx;
+ struct trace_entry *entry = record;
+
+ rcu_read_lock();
+ ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]);
+ if (!ctx)
+ goto unlock;
+
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (event->attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+ if (event->attr.config != entry->type)
+ continue;
+ if (perf_tp_event_match(event, &data, regs))
+ perf_swevent_event(event, count, &data, regs);
+ }
+unlock:
+ rcu_read_unlock();
+ }
+
perf_swevent_put_recursion_context(rctx);
}
EXPORT_SYMBOL_GPL(perf_tp_event);
@@ -5924,6 +5958,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
mutex_init(&event->mmap_mutex);
+ atomic_long_set(&event->refcount, 1);
event->cpu = cpu;
event->attr = *attr;
event->group_leader = group_leader;
@@ -6234,12 +6269,12 @@ SYSCALL_DEFINE5(perf_event_open,
return event_fd;
if (group_fd != -1) {
- group_leader = perf_fget_light(group_fd, &fput_needed);
- if (IS_ERR(group_leader)) {
- err = PTR_ERR(group_leader);
+ group_file = perf_fget_light(group_fd, &fput_needed);
+ if (IS_ERR(group_file)) {
+ err = PTR_ERR(group_file);
goto err_fd;
}
- group_file = group_leader->filp;
+ group_leader = group_file->private_data;
if (flags & PERF_FLAG_FD_OUTPUT)
output_event = group_leader;
if (flags & PERF_FLAG_FD_NO_GROUP)
@@ -6376,7 +6411,6 @@ SYSCALL_DEFINE5(perf_event_open,
put_ctx(gctx);
}
- event->filp = event_file;
WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex);
@@ -6470,7 +6504,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
goto err_free;
}
- event->filp = NULL;
WARN_ON_ONCE(ctx->parent_ctx);
mutex_lock(&ctx->mutex);
perf_install_in_context(ctx, event, cpu);
@@ -6552,7 +6585,7 @@ static void sync_child_event(struct perf_event *child_event,
* Release the parent event, if this was the last
* reference to it.
*/
- fput(parent_event->filp);
+ put_event(parent_event);
}
static void
@@ -6628,9 +6661,8 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
*
* __perf_event_exit_task()
* sync_child_event()
- * fput(parent_event->filp)
- * perf_release()
- * mutex_lock(&ctx->mutex)
+ * put_event()
+ * mutex_lock(&ctx->mutex)
*
* But since its the parent context it won't be the same instance.
*/
@@ -6698,7 +6730,7 @@ static void perf_free_event(struct perf_event *event,
list_del_init(&event->child_list);
mutex_unlock(&parent->child_mutex);
- fput(parent->filp);
+ put_event(parent);
perf_group_detach(event);
list_del_event(event, ctx);
@@ -6778,6 +6810,12 @@ inherit_event(struct perf_event *parent_event,
NULL, NULL);
if (IS_ERR(child_event))
return child_event;
+
+ if (!atomic_long_inc_not_zero(&parent_event->refcount)) {
+ free_event(child_event);
+ return NULL;
+ }
+
get_ctx(child_ctx);
/*
@@ -6819,14 +6857,6 @@ inherit_event(struct perf_event *parent_event,
raw_spin_unlock_irqrestore(&child_ctx->lock, flags);
/*
- * Get a reference to the parent filp - we will fput it
- * when the child event exits. This is safe to do because
- * we are in the parent and we know that the filp still
- * exists and has a nonzero count:
- */
- atomic_long_inc(&parent_event->filp->f_count);
-
- /*
* Link this into the parent event's child list
*/
WARN_ON_ONCE(parent_event->ctx->parent_ctx);
diff --git a/kernel/events/hw_breakpoint.c b/kernel/events/hw_breakpoint.c
index bb38c4d3ee12..9a7b487c6fe2 100644
--- a/kernel/events/hw_breakpoint.c
+++ b/kernel/events/hw_breakpoint.c
@@ -453,7 +453,16 @@ int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *att
int old_type = bp->attr.bp_type;
int err = 0;
- perf_event_disable(bp);
+ /*
+ * modify_user_hw_breakpoint can be invoked with IRQs disabled and hence it
+ * will not be possible to raise IPIs that invoke __perf_event_disable.
+ * So call the function directly after making sure we are targeting the
+ * current task.
+ */
+ if (irqs_disabled() && bp->ctx && bp->ctx->task == current)
+ __perf_event_disable(bp);
+ else
+ perf_event_disable(bp);
bp->attr.bp_addr = attr->bp_addr;
bp->attr.bp_type = attr->bp_type;
diff --git a/kernel/events/internal.h b/kernel/events/internal.h
index b0b107f90afc..a096c19f2c2a 100644
--- a/kernel/events/internal.h
+++ b/kernel/events/internal.h
@@ -101,7 +101,8 @@ __output_copy(struct perf_output_handle *handle,
}
/* Callchain handling */
-extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
+extern struct perf_callchain_entry *
+perf_callchain(struct perf_event *event, struct pt_regs *regs);
extern int get_callchain_buffers(void);
extern void put_callchain_buffers(void);
diff --git a/kernel/fork.c b/kernel/fork.c
index 8efac1fe56bc..2c8857e12855 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -381,10 +381,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
struct file *file;
if (mpnt->vm_flags & VM_DONTCOPY) {
- long pages = vma_pages(mpnt);
- mm->total_vm -= pages;
vm_stat_account(mm, mpnt->vm_flags, mpnt->vm_file,
- -pages);
+ -vma_pages(mpnt));
continue;
}
charge = 0;
@@ -457,8 +455,8 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
if (retval)
goto out;
- if (file && uprobe_mmap(tmp))
- goto out;
+ if (file)
+ uprobe_mmap(tmp);
}
/* a new mm has just been created */
arch_dup_mmap(oldmm, mm);
@@ -1308,7 +1306,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
#ifdef CONFIG_DEBUG_MUTEXES
p->blocked_on = NULL; /* not blocked yet */
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
p->memcg_batch.do_batch = 0;
p->memcg_batch.memcg = NULL;
#endif
diff --git a/kernel/futex.c b/kernel/futex.c
index e2b0fb9a0b3b..3717e7b306e0 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -2231,11 +2231,11 @@ int handle_early_requeue_pi_wakeup(struct futex_hash_bucket *hb,
* @uaddr2: the pi futex we will take prior to returning to user-space
*
* The caller will wait on uaddr and will be requeued by futex_requeue() to
- * uaddr2 which must be PI aware. Normal wakeup will wake on uaddr2 and
- * complete the acquisition of the rt_mutex prior to returning to userspace.
- * This ensures the rt_mutex maintains an owner when it has waiters; without
- * one, the pi logic wouldn't know which task to boost/deboost, if there was a
- * need to.
+ * uaddr2 which must be PI aware and unique from uaddr. Normal wakeup will wake
+ * on uaddr2 and complete the acquisition of the rt_mutex prior to returning to
+ * userspace. This ensures the rt_mutex maintains an owner when it has waiters;
+ * without one, the pi logic would not know which task to boost/deboost, if
+ * there was a need to.
*
* We call schedule in futex_wait_queue_me() when we enqueue and return there
* via the following:
@@ -2272,6 +2272,9 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
struct futex_q q = futex_q_init;
int res, ret;
+ if (uaddr == uaddr2)
+ return -EINVAL;
+
if (!bitset)
return -EINVAL;
@@ -2343,7 +2346,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
* signal. futex_unlock_pi() will not destroy the lock_ptr nor
* the pi_state.
*/
- WARN_ON(!&q.pi_state);
+ WARN_ON(!q.pi_state);
pi_mutex = &q.pi_state->pi_mutex;
ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter, 1);
debug_rt_mutex_free_waiter(&rt_waiter);
@@ -2370,7 +2373,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
* fault, unlock the rt_mutex and return the fault to userspace.
*/
if (ret == -EFAULT) {
- if (rt_mutex_owner(pi_mutex) == current)
+ if (pi_mutex && rt_mutex_owner(pi_mutex) == current)
rt_mutex_unlock(pi_mutex);
} else if (ret == -EINTR) {
/*
diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c
index bdb180325551..131ca176b497 100644
--- a/kernel/irq/handle.c
+++ b/kernel/irq/handle.c
@@ -133,7 +133,7 @@ irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
irqreturn_t retval = IRQ_NONE;
- unsigned int random = 0, irq = desc->irq_data.irq;
+ unsigned int flags = 0, irq = desc->irq_data.irq;
do {
irqreturn_t res;
@@ -161,7 +161,7 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
/* Fall through to add to randomness */
case IRQ_HANDLED:
- random |= action->flags;
+ flags |= action->flags;
break;
default:
@@ -172,8 +172,7 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
action = action->next;
} while (action);
- if (random & IRQF_SAMPLE_RANDOM)
- add_interrupt_randomness(irq);
+ add_interrupt_randomness(irq, flags);
if (!noirqdebug)
note_interrupt(irq, desc, retval);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 38c5eb839c92..49a77727db42 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -10,6 +10,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/topology.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/smp.h>
@@ -45,7 +46,8 @@ static struct irq_domain *irq_domain_alloc(struct device_node *of_node,
{
struct irq_domain *domain;
- domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ domain = kzalloc_node(sizeof(*domain), GFP_KERNEL,
+ of_node_to_nid(of_node));
if (WARN_ON(!domain))
return NULL;
@@ -138,6 +140,36 @@ static unsigned int irq_domain_legacy_revmap(struct irq_domain *domain,
}
/**
+ * irq_domain_add_simple() - Allocate and register a simple irq_domain.
+ * @of_node: pointer to interrupt controller's device tree node.
+ * @size: total number of irqs in mapping
+ * @first_irq: first number of irq block assigned to the domain
+ * @ops: map/unmap domain callbacks
+ * @host_data: Controller private data pointer
+ *
+ * Allocates a legacy irq_domain if irq_base is positive or a linear
+ * domain otherwise.
+ *
+ * This is intended to implement the expected behaviour for most
+ * interrupt controllers which is that a linear mapping should
+ * normally be used unless the system requires a legacy mapping in
+ * order to support supplying interrupt numbers during non-DT
+ * registration of devices.
+ */
+struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
+ unsigned int size,
+ unsigned int first_irq,
+ const struct irq_domain_ops *ops,
+ void *host_data)
+{
+ if (first_irq > 0)
+ return irq_domain_add_legacy(of_node, size, first_irq, 0,
+ ops, host_data);
+ else
+ return irq_domain_add_linear(of_node, size, ops, host_data);
+}
+
+/**
* irq_domain_add_legacy() - Allocate and register a legacy revmap irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
* @size: total number of irqs in legacy mapping
@@ -203,7 +235,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
* one can then use irq_create_mapping() to
* explicitly change them
*/
- ops->map(domain, irq, hwirq);
+ if (ops->map)
+ ops->map(domain, irq, hwirq);
/* Clear norequest flags */
irq_clear_status_flags(irq, IRQ_NOREQUEST);
@@ -215,7 +248,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
/**
- * irq_domain_add_linear() - Allocate and register a legacy revmap irq_domain.
+ * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain.
* @of_node: pointer to interrupt controller's device tree node.
* @size: Number of interrupts in the domain.
* @ops: map/unmap domain callbacks
@@ -229,7 +262,8 @@ struct irq_domain *irq_domain_add_linear(struct device_node *of_node,
struct irq_domain *domain;
unsigned int *revmap;
- revmap = kzalloc(sizeof(*revmap) * size, GFP_KERNEL);
+ revmap = kzalloc_node(sizeof(*revmap) * size, GFP_KERNEL,
+ of_node_to_nid(of_node));
if (WARN_ON(!revmap))
return NULL;
@@ -330,24 +364,112 @@ void irq_set_default_host(struct irq_domain *domain)
}
EXPORT_SYMBOL_GPL(irq_set_default_host);
-static int irq_setup_virq(struct irq_domain *domain, unsigned int virq,
- irq_hw_number_t hwirq)
+static void irq_domain_disassociate_many(struct irq_domain *domain,
+ unsigned int irq_base, int count)
{
- struct irq_data *irq_data = irq_get_irq_data(virq);
+ /*
+ * disassociate in reverse order;
+ * not strictly necessary, but nice for unwinding
+ */
+ while (count--) {
+ int irq = irq_base + count;
+ struct irq_data *irq_data = irq_get_irq_data(irq);
+ irq_hw_number_t hwirq = irq_data->hwirq;
+
+ if (WARN_ON(!irq_data || irq_data->domain != domain))
+ continue;
+
+ irq_set_status_flags(irq, IRQ_NOREQUEST);
+
+ /* remove chip and handler */
+ irq_set_chip_and_handler(irq, NULL, NULL);
+
+ /* Make sure it's completed */
+ synchronize_irq(irq);
+
+ /* Tell the PIC about it */
+ if (domain->ops->unmap)
+ domain->ops->unmap(domain, irq);
+ smp_mb();
- irq_data->hwirq = hwirq;
- irq_data->domain = domain;
- if (domain->ops->map(domain, virq, hwirq)) {
- pr_debug("irq-%i==>hwirq-0x%lx mapping failed\n", virq, hwirq);
irq_data->domain = NULL;
irq_data->hwirq = 0;
- return -1;
+
+ /* Clear reverse map */
+ switch(domain->revmap_type) {
+ case IRQ_DOMAIN_MAP_LINEAR:
+ if (hwirq < domain->revmap_data.linear.size)
+ domain->revmap_data.linear.revmap[hwirq] = 0;
+ break;
+ case IRQ_DOMAIN_MAP_TREE:
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_delete(&domain->revmap_data.tree, hwirq);
+ mutex_unlock(&revmap_trees_mutex);
+ break;
+ }
}
+}
+
+int irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count)
+{
+ unsigned int virq = irq_base;
+ irq_hw_number_t hwirq = hwirq_base;
+ int i, ret;
+
+ pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
+ of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+
+ for (i = 0; i < count; i++) {
+ struct irq_data *irq_data = irq_get_irq_data(virq + i);
+
+ if (WARN(!irq_data, "error: irq_desc not allocated; "
+ "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
+ return -EINVAL;
+ if (WARN(irq_data->domain, "error: irq_desc already associated; "
+ "irq=%i hwirq=0x%x\n", virq + i, (int)hwirq + i))
+ return -EINVAL;
+ };
+
+ for (i = 0; i < count; i++, virq++, hwirq++) {
+ struct irq_data *irq_data = irq_get_irq_data(virq);
+
+ irq_data->hwirq = hwirq;
+ irq_data->domain = domain;
+ if (domain->ops->map) {
+ ret = domain->ops->map(domain, virq, hwirq);
+ if (ret != 0) {
+ pr_err("irq-%i==>hwirq-0x%lx mapping failed: %d\n",
+ virq, hwirq, ret);
+ WARN_ON(1);
+ irq_data->domain = NULL;
+ irq_data->hwirq = 0;
+ goto err_unmap;
+ }
+ }
- irq_clear_status_flags(virq, IRQ_NOREQUEST);
+ switch (domain->revmap_type) {
+ case IRQ_DOMAIN_MAP_LINEAR:
+ if (hwirq < domain->revmap_data.linear.size)
+ domain->revmap_data.linear.revmap[hwirq] = virq;
+ break;
+ case IRQ_DOMAIN_MAP_TREE:
+ mutex_lock(&revmap_trees_mutex);
+ radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
+ mutex_unlock(&revmap_trees_mutex);
+ break;
+ }
+
+ irq_clear_status_flags(virq, IRQ_NOREQUEST);
+ }
return 0;
+
+ err_unmap:
+ irq_domain_disassociate_many(domain, irq_base, i);
+ return -EINVAL;
}
+EXPORT_SYMBOL_GPL(irq_domain_associate_many);
/**
* irq_create_direct_mapping() - Allocate an irq for direct mapping
@@ -364,10 +486,10 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
if (domain == NULL)
domain = irq_default_domain;
- BUG_ON(domain == NULL);
- WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP);
+ if (WARN_ON(!domain || domain->revmap_type != IRQ_DOMAIN_MAP_NOMAP))
+ return 0;
- virq = irq_alloc_desc_from(1, 0);
+ virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
if (!virq) {
pr_debug("create_direct virq allocation failed\n");
return 0;
@@ -380,7 +502,7 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
}
pr_debug("create_direct obtained virq %d\n", virq);
- if (irq_setup_virq(domain, virq, virq)) {
+ if (irq_domain_associate(domain, virq, virq)) {
irq_free_desc(virq);
return 0;
}
@@ -433,17 +555,16 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
hint = hwirq % nr_irqs;
if (hint == 0)
hint++;
- virq = irq_alloc_desc_from(hint, 0);
+ virq = irq_alloc_desc_from(hint, of_node_to_nid(domain->of_node));
if (virq <= 0)
- virq = irq_alloc_desc_from(1, 0);
+ virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
if (virq <= 0) {
pr_debug("-> virq allocation failed\n");
return 0;
}
- if (irq_setup_virq(domain, virq, hwirq)) {
- if (domain->revmap_type != IRQ_DOMAIN_MAP_LEGACY)
- irq_free_desc(virq);
+ if (irq_domain_associate(domain, virq, hwirq)) {
+ irq_free_desc(virq);
return 0;
}
@@ -454,6 +575,44 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
+/**
+ * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
+ * @domain: domain owning the interrupt range
+ * @irq_base: beginning of linux IRQ range
+ * @hwirq_base: beginning of hardware IRQ range
+ * @count: Number of interrupts to map
+ *
+ * This routine is used for allocating and mapping a range of hardware
+ * irqs to linux irqs where the linux irq numbers are at pre-defined
+ * locations. For use by controllers that already have static mappings
+ * to insert in to the domain.
+ *
+ * Non-linear users can use irq_create_identity_mapping() for IRQ-at-a-time
+ * domain insertion.
+ *
+ * 0 is returned upon success, while any failure to establish a static
+ * mapping is treated as an error.
+ */
+int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
+ irq_hw_number_t hwirq_base, int count)
+{
+ int ret;
+
+ ret = irq_alloc_descs(irq_base, irq_base, count,
+ of_node_to_nid(domain->of_node));
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = irq_domain_associate_many(domain, irq_base, hwirq_base, count);
+ if (unlikely(ret < 0)) {
+ irq_free_descs(irq_base, count);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
+
unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec, unsigned int intsize)
{
@@ -511,7 +670,6 @@ void irq_dispose_mapping(unsigned int virq)
{
struct irq_data *irq_data = irq_get_irq_data(virq);
struct irq_domain *domain;
- irq_hw_number_t hwirq;
if (!virq || !irq_data)
return;
@@ -524,33 +682,7 @@ void irq_dispose_mapping(unsigned int virq)
if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
return;
- irq_set_status_flags(virq, IRQ_NOREQUEST);
-
- /* remove chip and handler */
- irq_set_chip_and_handler(virq, NULL, NULL);
-
- /* Make sure it's completed */
- synchronize_irq(virq);
-
- /* Tell the PIC about it */
- if (domain->ops->unmap)
- domain->ops->unmap(domain, virq);
- smp_mb();
-
- /* Clear reverse map */
- hwirq = irq_data->hwirq;
- switch(domain->revmap_type) {
- case IRQ_DOMAIN_MAP_LINEAR:
- if (hwirq < domain->revmap_data.linear.size)
- domain->revmap_data.linear.revmap[hwirq] = 0;
- break;
- case IRQ_DOMAIN_MAP_TREE:
- mutex_lock(&revmap_trees_mutex);
- radix_tree_delete(&domain->revmap_data.tree, hwirq);
- mutex_unlock(&revmap_trees_mutex);
- break;
- }
-
+ irq_domain_disassociate_many(domain, virq, 1);
irq_free_desc(virq);
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
@@ -559,16 +691,11 @@ EXPORT_SYMBOL_GPL(irq_dispose_mapping);
* irq_find_mapping() - Find a linux irq from an hw irq number.
* @domain: domain owning this hardware interrupt
* @hwirq: hardware irq number in that domain space
- *
- * This is a slow path, for use by generic code. It's expected that an
- * irq controller implementation directly calls the appropriate low level
- * mapping function.
*/
unsigned int irq_find_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
- unsigned int i;
- unsigned int hint = hwirq % nr_irqs;
+ struct irq_data *data;
/* Look for default domain if nececssary */
if (domain == NULL)
@@ -576,115 +703,47 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
if (domain == NULL)
return 0;
- /* legacy -> bail early */
- if (domain->revmap_type == IRQ_DOMAIN_MAP_LEGACY)
+ switch (domain->revmap_type) {
+ case IRQ_DOMAIN_MAP_LEGACY:
return irq_domain_legacy_revmap(domain, hwirq);
-
- /* Slow path does a linear search of the map */
- if (hint == 0)
- hint = 1;
- i = hint;
- do {
- struct irq_data *data = irq_get_irq_data(i);
+ case IRQ_DOMAIN_MAP_LINEAR:
+ return irq_linear_revmap(domain, hwirq);
+ case IRQ_DOMAIN_MAP_TREE:
+ rcu_read_lock();
+ data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
+ rcu_read_unlock();
+ if (data)
+ return data->irq;
+ break;
+ case IRQ_DOMAIN_MAP_NOMAP:
+ data = irq_get_irq_data(hwirq);
if (data && (data->domain == domain) && (data->hwirq == hwirq))
- return i;
- i++;
- if (i >= nr_irqs)
- i = 1;
- } while(i != hint);
+ return hwirq;
+ break;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(irq_find_mapping);
/**
- * irq_radix_revmap_lookup() - Find a linux irq from a hw irq number.
- * @domain: domain owning this hardware interrupt
- * @hwirq: hardware irq number in that domain space
- *
- * This is a fast path, for use by irq controller code that uses radix tree
- * revmaps
- */
-unsigned int irq_radix_revmap_lookup(struct irq_domain *domain,
- irq_hw_number_t hwirq)
-{
- struct irq_data *irq_data;
-
- if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
- return irq_find_mapping(domain, hwirq);
-
- /*
- * Freeing an irq can delete nodes along the path to
- * do the lookup via call_rcu.
- */
- rcu_read_lock();
- irq_data = radix_tree_lookup(&domain->revmap_data.tree, hwirq);
- rcu_read_unlock();
-
- /*
- * If found in radix tree, then fine.
- * Else fallback to linear lookup - this should not happen in practice
- * as it means that we failed to insert the node in the radix tree.
- */
- return irq_data ? irq_data->irq : irq_find_mapping(domain, hwirq);
-}
-EXPORT_SYMBOL_GPL(irq_radix_revmap_lookup);
-
-/**
- * irq_radix_revmap_insert() - Insert a hw irq to linux irq number mapping.
- * @domain: domain owning this hardware interrupt
- * @virq: linux irq number
- * @hwirq: hardware irq number in that domain space
- *
- * This is for use by irq controllers that use a radix tree reverse
- * mapping for fast lookup.
- */
-void irq_radix_revmap_insert(struct irq_domain *domain, unsigned int virq,
- irq_hw_number_t hwirq)
-{
- struct irq_data *irq_data = irq_get_irq_data(virq);
-
- if (WARN_ON(domain->revmap_type != IRQ_DOMAIN_MAP_TREE))
- return;
-
- if (virq) {
- mutex_lock(&revmap_trees_mutex);
- radix_tree_insert(&domain->revmap_data.tree, hwirq, irq_data);
- mutex_unlock(&revmap_trees_mutex);
- }
-}
-EXPORT_SYMBOL_GPL(irq_radix_revmap_insert);
-
-/**
* irq_linear_revmap() - Find a linux irq from a hw irq number.
* @domain: domain owning this hardware interrupt
* @hwirq: hardware irq number in that domain space
*
- * This is a fast path, for use by irq controller code that uses linear
- * revmaps. It does fallback to the slow path if the revmap doesn't exist
- * yet and will create the revmap entry with appropriate locking
+ * This is a fast path that can be called directly by irq controller code to
+ * save a handful of instructions.
*/
unsigned int irq_linear_revmap(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
- unsigned int *revmap;
-
- if (WARN_ON_ONCE(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR))
- return irq_find_mapping(domain, hwirq);
+ BUG_ON(domain->revmap_type != IRQ_DOMAIN_MAP_LINEAR);
- /* Check revmap bounds */
- if (unlikely(hwirq >= domain->revmap_data.linear.size))
- return irq_find_mapping(domain, hwirq);
-
- /* Check if revmap was allocated */
- revmap = domain->revmap_data.linear.revmap;
- if (unlikely(revmap == NULL))
- return irq_find_mapping(domain, hwirq);
-
- /* Fill up revmap with slow path if no mapping found */
- if (unlikely(!revmap[hwirq]))
- revmap[hwirq] = irq_find_mapping(domain, hwirq);
+ /* Check revmap bounds; complain if exceeded */
+ if (WARN_ON(hwirq >= domain->revmap_data.linear.size))
+ return 0;
- return revmap[hwirq];
+ return domain->revmap_data.linear.revmap[hwirq];
}
EXPORT_SYMBOL_GPL(irq_linear_revmap);
@@ -761,12 +820,6 @@ static int __init irq_debugfs_init(void)
__initcall(irq_debugfs_init);
#endif /* CONFIG_IRQ_DOMAIN_DEBUG */
-static int irq_domain_simple_map(struct irq_domain *d, unsigned int irq,
- irq_hw_number_t hwirq)
-{
- return 0;
-}
-
/**
* irq_domain_xlate_onecell() - Generic xlate for direct one cell bindings
*
@@ -829,7 +882,6 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d,
EXPORT_SYMBOL_GPL(irq_domain_xlate_onetwocell);
const struct irq_domain_ops irq_domain_simple_ops = {
- .map = irq_domain_simple_map,
.xlate = irq_domain_xlate_onetwocell,
};
EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 814c9ef6bba1..4c69326aa773 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -893,22 +893,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
return -ENOSYS;
if (!try_module_get(desc->owner))
return -ENODEV;
- /*
- * Some drivers like serial.c use request_irq() heavily,
- * so we have to be careful not to interfere with a
- * running system.
- */
- if (new->flags & IRQF_SAMPLE_RANDOM) {
- /*
- * This function might sleep, we want to call it first,
- * outside of the atomic block.
- * Yes, this might clear the entropy pool if the wrong
- * driver is attempted to be loaded, without actually
- * installing a new handler, but is this really a problem,
- * only the sysadmin is able to do this.
- */
- rand_initialize_irq(irq);
- }
/*
* Check whether the interrupt nests into another interrupt
@@ -960,6 +944,18 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
}
/*
+ * Drivers are often written to work w/o knowledge about the
+ * underlying irq chip implementation, so a request for a
+ * threaded irq without a primary hard irq context handler
+ * requires the ONESHOT flag to be set. Some irq chips like
+ * MSI based interrupts are per se one shot safe. Check the
+ * chip flags, so we can avoid the unmask dance at the end of
+ * the threaded handler for those.
+ */
+ if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
+ new->flags &= ~IRQF_ONESHOT;
+
+ /*
* The following block of code has to be executed atomically
*/
raw_spin_lock_irqsave(&desc->lock, flags);
@@ -1033,7 +1029,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
*/
new->thread_mask = 1 << ffz(thread_mask);
- } else if (new->handler == irq_default_primary_handler) {
+ } else if (new->handler == irq_default_primary_handler &&
+ !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
/*
* The interrupt was requested with handler = NULL, so
* we use the default primary handler for it. But it
@@ -1354,7 +1351,6 @@ EXPORT_SYMBOL(free_irq);
* Flags:
*
* IRQF_SHARED Interrupt is shared
- * IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
* IRQF_TRIGGER_* Specify active edge(s) or level
*
*/
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index b3c7fd554250..6144bab8fd8e 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -232,15 +232,19 @@ static int pid_ns_ctl_handler(struct ctl_table *table, int write,
*/
tmp.data = &current->nsproxy->pid_ns->last_pid;
- return proc_dointvec(&tmp, write, buffer, lenp, ppos);
+ return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
}
+extern int pid_max;
+static int zero = 0;
static struct ctl_table pid_ns_ctl_table[] = {
{
.procname = "ns_last_pid",
.maxlen = sizeof(int),
.mode = 0666, /* permissions are checked in the handler */
.proc_handler = pid_ns_ctl_handler,
+ .extra1 = &zero,
+ .extra2 = &pid_max,
},
{ }
};
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 1da39ea248fd..c8b7446b27df 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -178,9 +178,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
- /* Kick the lockup detector */
- lockup_detector_bootcpu_resume();
-
Enable_cpus:
enable_nonboot_cpus();
diff --git a/kernel/printk.c b/kernel/printk.c
index 6a76ab9d4476..66a2ea37b576 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1034,6 +1034,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
struct log *msg = log_from_idx(idx);
len += msg_print_text(msg, prev, true, NULL, 0);
+ prev = msg->flags;
idx = log_next(idx);
seq++;
}
@@ -1046,6 +1047,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
struct log *msg = log_from_idx(idx);
len -= msg_print_text(msg, prev, true, NULL, 0);
+ prev = msg->flags;
idx = log_next(idx);
seq++;
}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d325c4b2dcbb..649c9f876cb1 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3142,6 +3142,20 @@ void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
# define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs)
#endif
+static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total)
+{
+ u64 temp = (__force u64) rtime;
+
+ temp *= (__force u64) utime;
+
+ if (sizeof(cputime_t) == 4)
+ temp = div_u64(temp, (__force u32) total);
+ else
+ temp = div64_u64(temp, (__force u64) total);
+
+ return (__force cputime_t) temp;
+}
+
void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
{
cputime_t rtime, utime = p->utime, total = utime + p->stime;
@@ -3151,13 +3165,9 @@ void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
*/
rtime = nsecs_to_cputime(p->se.sum_exec_runtime);
- if (total) {
- u64 temp = (__force u64) rtime;
-
- temp *= (__force u64) utime;
- do_div(temp, (__force u32) total);
- utime = (__force cputime_t) temp;
- } else
+ if (total)
+ utime = scale_utime(utime, rtime, total);
+ else
utime = rtime;
/*
@@ -3184,13 +3194,9 @@ void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
total = cputime.utime + cputime.stime;
rtime = nsecs_to_cputime(cputime.sum_exec_runtime);
- if (total) {
- u64 temp = (__force u64) rtime;
-
- temp *= (__force u64) cputime.utime;
- do_div(temp, (__force u32) total);
- utime = (__force cputime_t) temp;
- } else
+ if (total)
+ utime = scale_utime(cputime.utime, rtime, total);
+ else
utime = rtime;
sig->prev_utime = max(sig->prev_utime, utime);
@@ -4340,9 +4346,7 @@ recheck:
*/
if (unlikely(policy == p->policy && (!rt_policy(policy) ||
param->sched_priority == p->rt_priority))) {
-
- __task_rq_unlock(rq);
- raw_spin_unlock_irqrestore(&p->pi_lock, flags);
+ task_rq_unlock(rq, p, &flags);
return 0;
}
@@ -5300,27 +5304,17 @@ void idle_task_exit(void)
}
/*
- * While a dead CPU has no uninterruptible tasks queued at this point,
- * it might still have a nonzero ->nr_uninterruptible counter, because
- * for performance reasons the counter is not stricly tracking tasks to
- * their home CPUs. So we just add the counter to another CPU's counter,
- * to keep the global sum constant after CPU-down:
- */
-static void migrate_nr_uninterruptible(struct rq *rq_src)
-{
- struct rq *rq_dest = cpu_rq(cpumask_any(cpu_active_mask));
-
- rq_dest->nr_uninterruptible += rq_src->nr_uninterruptible;
- rq_src->nr_uninterruptible = 0;
-}
-
-/*
- * remove the tasks which were accounted by rq from calc_load_tasks.
+ * Since this CPU is going 'away' for a while, fold any nr_active delta
+ * we might have. Assumes we're called after migrate_tasks() so that the
+ * nr_active count is stable.
+ *
+ * Also see the comment "Global load-average calculations".
*/
-static void calc_global_load_remove(struct rq *rq)
+static void calc_load_migrate(struct rq *rq)
{
- atomic_long_sub(rq->calc_load_active, &calc_load_tasks);
- rq->calc_load_active = 0;
+ long delta = calc_load_fold_active(rq);
+ if (delta)
+ atomic_long_add(delta, &calc_load_tasks);
}
/*
@@ -5348,9 +5342,6 @@ static void migrate_tasks(unsigned int dead_cpu)
*/
rq->stop = NULL;
- /* Ensure any throttled groups are reachable by pick_next_task */
- unthrottle_offline_cfs_rqs(rq);
-
for ( ; ; ) {
/*
* There's this thread running, bail when that's the only
@@ -5614,8 +5605,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
BUG_ON(rq->nr_running != 1); /* the migration thread */
raw_spin_unlock_irqrestore(&rq->lock, flags);
- migrate_nr_uninterruptible(rq);
- calc_global_load_remove(rq);
+ calc_load_migrate(rq);
break;
#endif
}
@@ -6024,11 +6014,6 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu)
* SD_SHARE_PKG_RESOURCE set (Last Level Cache Domain) for this
* allows us to avoid some pointer chasing select_idle_sibling().
*
- * Iterate domains and sched_groups downward, assigning CPUs to be
- * select_idle_sibling() hw buddy. Cross-wiring hw makes bouncing
- * due to random perturbation self canceling, ie sw buddies pull
- * their counterpart to their CPU's hw counterpart.
- *
* Also keep a unique ID per domain (we use the first cpu number in
* the cpumask of the domain), this allows us to quickly tell if
* two cpus are in the same cache domain, see cpus_share_cache().
@@ -6042,40 +6027,8 @@ static void update_top_cache_domain(int cpu)
int id = cpu;
sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
- if (sd) {
- struct sched_domain *tmp = sd;
- struct sched_group *sg, *prev;
- bool right;
-
- /*
- * Traverse to first CPU in group, and count hops
- * to cpu from there, switching direction on each
- * hop, never ever pointing the last CPU rightward.
- */
- do {
- id = cpumask_first(sched_domain_span(tmp));
- prev = sg = tmp->groups;
- right = 1;
-
- while (cpumask_first(sched_group_cpus(sg)) != id)
- sg = sg->next;
-
- while (!cpumask_test_cpu(cpu, sched_group_cpus(sg))) {
- prev = sg;
- sg = sg->next;
- right = !right;
- }
-
- /* A CPU went down, never point back to domain start. */
- if (right && cpumask_first(sched_group_cpus(sg->next)) == id)
- right = false;
-
- sg = right ? sg->next : prev;
- tmp->idle_buddy = cpumask_first(sched_group_cpus(sg));
- } while ((tmp = tmp->child));
-
+ if (sd)
id = cpumask_first(sched_domain_span(sd));
- }
rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
per_cpu(sd_llc_id, cpu) = id;
@@ -7248,6 +7201,7 @@ int in_sched_functions(unsigned long addr)
#ifdef CONFIG_CGROUP_SCHED
struct task_group root_task_group;
+LIST_HEAD(task_groups);
#endif
DECLARE_PER_CPU(cpumask_var_t, load_balance_tmpmask);
diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index d72586fdf660..23aa789c53ee 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -65,8 +65,8 @@ static int convert_prio(int prio)
int cpupri_find(struct cpupri *cp, struct task_struct *p,
struct cpumask *lowest_mask)
{
- int idx = 0;
- int task_pri = convert_prio(p->prio);
+ int idx = 0;
+ int task_pri = convert_prio(p->prio);
if (task_pri >= MAX_RT_PRIO)
return 0;
@@ -137,9 +137,9 @@ int cpupri_find(struct cpupri *cp, struct task_struct *p,
*/
void cpupri_set(struct cpupri *cp, int cpu, int newpri)
{
- int *currpri = &cp->cpu_to_pri[cpu];
- int oldpri = *currpri;
- int do_mb = 0;
+ int *currpri = &cp->cpu_to_pri[cpu];
+ int oldpri = *currpri;
+ int do_mb = 0;
newpri = convert_prio(newpri);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 22321db64952..96e2b18b6283 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -2052,7 +2052,7 @@ static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
hrtimer_cancel(&cfs_b->slack_timer);
}
-void unthrottle_offline_cfs_rqs(struct rq *rq)
+static void unthrottle_offline_cfs_rqs(struct rq *rq)
{
struct cfs_rq *cfs_rq;
@@ -2106,7 +2106,7 @@ static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg)
return NULL;
}
static inline void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b) {}
-void unthrottle_offline_cfs_rqs(struct rq *rq) {}
+static inline void unthrottle_offline_cfs_rqs(struct rq *rq) {}
#endif /* CONFIG_CFS_BANDWIDTH */
@@ -2637,6 +2637,8 @@ static int select_idle_sibling(struct task_struct *p, int target)
int cpu = smp_processor_id();
int prev_cpu = task_cpu(p);
struct sched_domain *sd;
+ struct sched_group *sg;
+ int i;
/*
* If the task is going to be woken-up on this cpu and if it is
@@ -2653,17 +2655,29 @@ static int select_idle_sibling(struct task_struct *p, int target)
return prev_cpu;
/*
- * Otherwise, check assigned siblings to find an elegible idle cpu.
+ * Otherwise, iterate the domains and find an elegible idle cpu.
*/
sd = rcu_dereference(per_cpu(sd_llc, target));
-
for_each_lower_domain(sd) {
- if (!cpumask_test_cpu(sd->idle_buddy, tsk_cpus_allowed(p)))
- continue;
- if (idle_cpu(sd->idle_buddy))
- return sd->idle_buddy;
- }
+ sg = sd->groups;
+ do {
+ if (!cpumask_intersects(sched_group_cpus(sg),
+ tsk_cpus_allowed(p)))
+ goto next;
+ for_each_cpu(i, sched_group_cpus(sg)) {
+ if (!idle_cpu(i))
+ goto next;
+ }
+
+ target = cpumask_first_and(sched_group_cpus(sg),
+ tsk_cpus_allowed(p));
+ goto done;
+next:
+ sg = sg->next;
+ } while (sg != sd->groups);
+ }
+done:
return target;
}
@@ -3069,6 +3083,9 @@ struct lb_env {
int new_dst_cpu;
enum cpu_idle_type idle;
long imbalance;
+ /* The set of CPUs under consideration for load-balancing */
+ struct cpumask *cpus;
+
unsigned int flags;
unsigned int loop;
@@ -3384,6 +3401,14 @@ static int tg_load_down(struct task_group *tg, void *data)
static void update_h_load(long cpu)
{
+ struct rq *rq = cpu_rq(cpu);
+ unsigned long now = jiffies;
+
+ if (rq->h_load_throttle == now)
+ return;
+
+ rq->h_load_throttle = now;
+
rcu_read_lock();
walk_tg_tree(tg_load_down, tg_nop, (void *)cpu);
rcu_read_unlock();
@@ -3647,14 +3672,12 @@ fix_small_capacity(struct sched_domain *sd, struct sched_group *group)
* @group: sched_group whose statistics are to be updated.
* @load_idx: Load index of sched_domain of this_cpu for load calc.
* @local_group: Does group contain this_cpu.
- * @cpus: Set of cpus considered for load balancing.
* @balance: Should we balance.
* @sgs: variable to hold the statistics for this group.
*/
static inline void update_sg_lb_stats(struct lb_env *env,
struct sched_group *group, int load_idx,
- int local_group, const struct cpumask *cpus,
- int *balance, struct sg_lb_stats *sgs)
+ int local_group, int *balance, struct sg_lb_stats *sgs)
{
unsigned long nr_running, max_nr_running, min_nr_running;
unsigned long load, max_cpu_load, min_cpu_load;
@@ -3671,7 +3694,7 @@ static inline void update_sg_lb_stats(struct lb_env *env,
max_nr_running = 0;
min_nr_running = ~0UL;
- for_each_cpu_and(i, sched_group_cpus(group), cpus) {
+ for_each_cpu_and(i, sched_group_cpus(group), env->cpus) {
struct rq *rq = cpu_rq(i);
nr_running = rq->nr_running;
@@ -3795,13 +3818,11 @@ static bool update_sd_pick_busiest(struct lb_env *env,
/**
* update_sd_lb_stats - Update sched_domain's statistics for load balancing.
* @env: The load balancing environment.
- * @cpus: Set of cpus considered for load balancing.
* @balance: Should we balance.
* @sds: variable to hold the statistics for this sched_domain.
*/
static inline void update_sd_lb_stats(struct lb_env *env,
- const struct cpumask *cpus,
- int *balance, struct sd_lb_stats *sds)
+ int *balance, struct sd_lb_stats *sds)
{
struct sched_domain *child = env->sd->child;
struct sched_group *sg = env->sd->groups;
@@ -3818,8 +3839,7 @@ static inline void update_sd_lb_stats(struct lb_env *env,
local_group = cpumask_test_cpu(env->dst_cpu, sched_group_cpus(sg));
memset(&sgs, 0, sizeof(sgs));
- update_sg_lb_stats(env, sg, load_idx, local_group,
- cpus, balance, &sgs);
+ update_sg_lb_stats(env, sg, load_idx, local_group, balance, &sgs);
if (local_group && !(*balance))
return;
@@ -4055,7 +4075,6 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
* to restore balance.
*
* @env: The load balancing environment.
- * @cpus: The set of CPUs under consideration for load-balancing.
* @balance: Pointer to a variable indicating if this_cpu
* is the appropriate cpu to perform load balancing at this_level.
*
@@ -4065,7 +4084,7 @@ static inline void calculate_imbalance(struct lb_env *env, struct sd_lb_stats *s
* put to idle by rebalancing its tasks onto our group.
*/
static struct sched_group *
-find_busiest_group(struct lb_env *env, const struct cpumask *cpus, int *balance)
+find_busiest_group(struct lb_env *env, int *balance)
{
struct sd_lb_stats sds;
@@ -4075,7 +4094,7 @@ find_busiest_group(struct lb_env *env, const struct cpumask *cpus, int *balance)
* Compute the various statistics relavent for load balancing at
* this level.
*/
- update_sd_lb_stats(env, cpus, balance, &sds);
+ update_sd_lb_stats(env, balance, &sds);
/*
* this_cpu is not the appropriate cpu to perform load balancing at
@@ -4155,8 +4174,7 @@ ret:
* find_busiest_queue - find the busiest runqueue among the cpus in group.
*/
static struct rq *find_busiest_queue(struct lb_env *env,
- struct sched_group *group,
- const struct cpumask *cpus)
+ struct sched_group *group)
{
struct rq *busiest = NULL, *rq;
unsigned long max_load = 0;
@@ -4171,7 +4189,7 @@ static struct rq *find_busiest_queue(struct lb_env *env,
if (!capacity)
capacity = fix_small_capacity(env->sd, group);
- if (!cpumask_test_cpu(i, cpus))
+ if (!cpumask_test_cpu(i, env->cpus))
continue;
rq = cpu_rq(i);
@@ -4252,6 +4270,7 @@ static int load_balance(int this_cpu, struct rq *this_rq,
.dst_grpmask = sched_group_cpus(sd->groups),
.idle = idle,
.loop_break = sched_nr_migrate_break,
+ .cpus = cpus,
};
cpumask_copy(cpus, cpu_active_mask);
@@ -4260,7 +4279,7 @@ static int load_balance(int this_cpu, struct rq *this_rq,
schedstat_inc(sd, lb_count[idle]);
redo:
- group = find_busiest_group(&env, cpus, balance);
+ group = find_busiest_group(&env, balance);
if (*balance == 0)
goto out_balanced;
@@ -4270,7 +4289,7 @@ redo:
goto out_balanced;
}
- busiest = find_busiest_queue(&env, group, cpus);
+ busiest = find_busiest_queue(&env, group);
if (!busiest) {
schedstat_inc(sd, lb_nobusyq[idle]);
goto out_balanced;
@@ -4294,11 +4313,10 @@ redo:
env.src_rq = busiest;
env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running);
+ update_h_load(env.src_cpu);
more_balance:
local_irq_save(flags);
double_rq_lock(this_rq, busiest);
- if (!env.loop)
- update_h_load(env.src_cpu);
/*
* cur_ld_moved - load moved in current iteration
@@ -4950,6 +4968,9 @@ static void rq_online_fair(struct rq *rq)
static void rq_offline_fair(struct rq *rq)
{
update_sysctl();
+
+ /* Ensure any throttled groups are reachable by pick_next_task */
+ unthrottle_offline_cfs_rqs(rq);
}
#endif /* CONFIG_SMP */
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 573e1ca01102..e0b7ba9c040f 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -691,6 +691,7 @@ balanced:
* runtime - in which case borrowing doesn't make sense.
*/
rt_rq->rt_runtime = RUNTIME_INF;
+ rt_rq->rt_throttled = 0;
raw_spin_unlock(&rt_rq->rt_runtime_lock);
raw_spin_unlock(&rt_b->rt_runtime_lock);
}
@@ -788,6 +789,19 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)
const struct cpumask *span;
span = sched_rt_period_mask();
+#ifdef CONFIG_RT_GROUP_SCHED
+ /*
+ * FIXME: isolated CPUs should really leave the root task group,
+ * whether they are isolcpus or were isolated via cpusets, lest
+ * the timer run on a CPU which does not service all runqueues,
+ * potentially leaving other CPUs indefinitely throttled. If
+ * isolation is really required, the user will turn the throttle
+ * off to kill the perturbations it causes anyway. Meanwhile,
+ * this maintains functionality for boot and/or troubleshooting.
+ */
+ if (rt_b == &root_task_group.rt_bandwidth)
+ span = cpu_online_mask;
+#endif
for_each_cpu(i, span) {
int enqueue = 0;
struct rt_rq *rt_rq = sched_rt_period_rt_rq(rt_b, i);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index c35a1a7dd4d6..0848fa36c383 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -80,7 +80,7 @@ extern struct mutex sched_domains_mutex;
struct cfs_rq;
struct rt_rq;
-static LIST_HEAD(task_groups);
+extern struct list_head task_groups;
struct cfs_bandwidth {
#ifdef CONFIG_CFS_BANDWIDTH
@@ -374,7 +374,11 @@ struct rq {
#ifdef CONFIG_FAIR_GROUP_SCHED
/* list of leaf cfs_rq on this cpu: */
struct list_head leaf_cfs_rq_list;
-#endif
+#ifdef CONFIG_SMP
+ unsigned long h_load_throttle;
+#endif /* CONFIG_SMP */
+#endif /* CONFIG_FAIR_GROUP_SCHED */
+
#ifdef CONFIG_RT_GROUP_SCHED
struct list_head leaf_rt_rq_list;
#endif
@@ -1140,7 +1144,6 @@ extern void print_rt_stats(struct seq_file *m, int cpu);
extern void init_cfs_rq(struct cfs_rq *cfs_rq);
extern void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq);
-extern void unthrottle_offline_cfs_rqs(struct rq *rq);
extern void account_cfs_bandwidth_used(int enabled, int was_enabled);
diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
index 7b386e86fd23..da5eb5bed84a 100644
--- a/kernel/sched/stop_task.c
+++ b/kernel/sched/stop_task.c
@@ -27,8 +27,10 @@ static struct task_struct *pick_next_task_stop(struct rq *rq)
{
struct task_struct *stop = rq->stop;
- if (stop && stop->on_rq)
+ if (stop && stop->on_rq) {
+ stop->se.exec_start = rq->clock_task;
return stop;
+ }
return NULL;
}
@@ -52,6 +54,21 @@ static void yield_task_stop(struct rq *rq)
static void put_prev_task_stop(struct rq *rq, struct task_struct *prev)
{
+ struct task_struct *curr = rq->curr;
+ u64 delta_exec;
+
+ delta_exec = rq->clock_task - curr->se.exec_start;
+ if (unlikely((s64)delta_exec < 0))
+ delta_exec = 0;
+
+ schedstat_set(curr->se.statistics.exec_max,
+ max(curr->se.statistics.exec_max, delta_exec));
+
+ curr->se.sum_exec_runtime += delta_exec;
+ account_group_exec_runtime(curr, delta_exec);
+
+ curr->se.exec_start = rq->clock_task;
+ cpuacct_charge(curr, delta_exec);
}
static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued)
@@ -60,6 +77,9 @@ static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued)
static void set_curr_task_stop(struct rq *rq)
{
+ struct task_struct *stop = rq->stop;
+
+ stop->se.exec_start = rq->clock_task;
}
static void switched_to_stop(struct rq *rq, struct task_struct *p)
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 671f9594e368..b73e681df09e 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -210,6 +210,14 @@ asmlinkage void __do_softirq(void)
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
+ unsigned long old_flags = current->flags;
+
+ /*
+ * Mask out PF_MEMALLOC s current task context is borrowed for the
+ * softirq. A softirq handled such as network RX might set PF_MEMALLOC
+ * again if the socket is related to swap
+ */
+ current->flags &= ~PF_MEMALLOC;
pending = local_softirq_pending();
account_system_vtime(current);
@@ -265,6 +273,7 @@ restart:
account_system_vtime(current);
__local_bh_enable(SOFTIRQ_OFFSET);
+ tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}
#ifndef __ARCH_HAS_DO_SOFTIRQ
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 97186b99b0e4..87174ef59161 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1101,11 +1101,9 @@ static struct ctl_table vm_table[] = {
.extra1 = &zero,
},
{
- .procname = "nr_pdflush_threads",
- .data = &nr_pdflush_threads,
- .maxlen = sizeof nr_pdflush_threads,
- .mode = 0444 /* read-only*/,
- .proc_handler = proc_dointvec,
+ .procname = "nr_pdflush_threads",
+ .mode = 0444 /* read-only */,
+ .proc_handler = pdflush_proc_obsolete,
},
{
.procname = "swappiness",
@@ -1500,6 +1498,24 @@ static struct ctl_table fs_table[] = {
#endif
#endif
{
+ .procname = "protected_symlinks",
+ .data = &sysctl_protected_symlinks,
+ .maxlen = sizeof(int),
+ .mode = 0600,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ {
+ .procname = "protected_hardlinks",
+ .data = &sysctl_protected_hardlinks,
+ .maxlen = sizeof(int),
+ .mode = 0600,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ {
.procname = "suid_dumpable",
.data = &suid_dumpable,
.maxlen = sizeof(int),
diff --git a/kernel/sysctl_binary.c b/kernel/sysctl_binary.c
index a650694883a1..65bdcf198d4e 100644
--- a/kernel/sysctl_binary.c
+++ b/kernel/sysctl_binary.c
@@ -147,7 +147,7 @@ static const struct bin_table bin_vm_table[] = {
{ CTL_INT, VM_DIRTY_RATIO, "dirty_ratio" },
/* VM_DIRTY_WB_CS "dirty_writeback_centisecs" no longer used */
/* VM_DIRTY_EXPIRE_CS "dirty_expire_centisecs" no longer used */
- { CTL_INT, VM_NR_PDFLUSH_THREADS, "nr_pdflush_threads" },
+ /* VM_NR_PDFLUSH_THREADS "nr_pdflush_threads" no longer used */
{ CTL_INT, VM_OVERCOMMIT_RATIO, "overcommit_ratio" },
/* VM_PAGEBUF unused */
/* VM_HUGETLB_PAGES "nr_hugepages" no longer used */
diff --git a/kernel/task_work.c b/kernel/task_work.c
index 91d4e1742a0c..d320d44903bd 100644
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -75,6 +75,7 @@ void task_work_run(void)
p = q->next;
q->func(q);
q = p;
+ cond_resched();
}
}
}
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index a470154e0408..46da0537c10b 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -37,7 +37,7 @@
* requested HZ value. It is also not recommended
* for "tick-less" systems.
*/
-#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ))
+#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/SHIFTED_HZ))
/* Since jiffies uses a simple NSEC_PER_JIFFY multiplier
* conversion, the .shift value could be zero. However
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index b7fbadc5c973..24174b4d669b 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -28,7 +28,7 @@ DEFINE_SPINLOCK(ntp_lock);
/* USER_HZ period (usecs): */
unsigned long tick_usec = TICK_USEC;
-/* ACTHZ period (nsecs): */
+/* SHIFTED_HZ period (nsecs): */
unsigned long tick_nsec;
static u64 tick_length;
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 024540f97f74..3a9e5d5c1091 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -573,6 +573,7 @@ static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
tick_do_update_jiffies64(now);
update_cpu_load_nohz();
+ calc_load_exit_idle();
touch_softlockup_watchdog();
/*
* Cancel the scheduled timer and restore the tick
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index f045cc50832d..d3b91e75cecd 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -65,14 +65,14 @@ struct timekeeper {
* used instead.
*/
struct timespec wall_to_monotonic;
- /* time spent in suspend */
- struct timespec total_sleep_time;
- /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
- struct timespec raw_time;
/* Offset clock monotonic -> clock realtime */
ktime_t offs_real;
+ /* time spent in suspend */
+ struct timespec total_sleep_time;
/* Offset clock monotonic -> clock boottime */
ktime_t offs_boot;
+ /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
+ struct timespec raw_time;
/* Seqlock for all timekeeper values */
seqlock_t lock;
};
@@ -108,13 +108,39 @@ static struct timespec tk_xtime(struct timekeeper *tk)
static void tk_set_xtime(struct timekeeper *tk, const struct timespec *ts)
{
tk->xtime_sec = ts->tv_sec;
- tk->xtime_nsec = ts->tv_nsec << tk->shift;
+ tk->xtime_nsec = (u64)ts->tv_nsec << tk->shift;
}
static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts)
{
tk->xtime_sec += ts->tv_sec;
- tk->xtime_nsec += ts->tv_nsec << tk->shift;
+ tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift;
+ tk_normalize_xtime(tk);
+}
+
+static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm)
+{
+ struct timespec tmp;
+
+ /*
+ * Verify consistency of: offset_real = -wall_to_monotonic
+ * before modifying anything
+ */
+ set_normalized_timespec(&tmp, -tk->wall_to_monotonic.tv_sec,
+ -tk->wall_to_monotonic.tv_nsec);
+ WARN_ON_ONCE(tk->offs_real.tv64 != timespec_to_ktime(tmp).tv64);
+ tk->wall_to_monotonic = wtm;
+ set_normalized_timespec(&tmp, -wtm.tv_sec, -wtm.tv_nsec);
+ tk->offs_real = timespec_to_ktime(tmp);
+}
+
+static void tk_set_sleep_time(struct timekeeper *tk, struct timespec t)
+{
+ /* Verify consistency before modifying */
+ WARN_ON_ONCE(tk->offs_boot.tv64 != timespec_to_ktime(tk->total_sleep_time).tv64);
+
+ tk->total_sleep_time = t;
+ tk->offs_boot = timespec_to_ktime(t);
}
/**
@@ -217,14 +243,6 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
return nsec + arch_gettimeoffset();
}
-static void update_rt_offset(struct timekeeper *tk)
-{
- struct timespec tmp, *wtm = &tk->wall_to_monotonic;
-
- set_normalized_timespec(&tmp, -wtm->tv_sec, -wtm->tv_nsec);
- tk->offs_real = timespec_to_ktime(tmp);
-}
-
/* must hold write on timekeeper.lock */
static void timekeeping_update(struct timekeeper *tk, bool clearntp)
{
@@ -234,12 +252,10 @@ static void timekeeping_update(struct timekeeper *tk, bool clearntp)
tk->ntp_error = 0;
ntp_clear();
}
- update_rt_offset(tk);
xt = tk_xtime(tk);
update_vsyscall(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult);
}
-
/**
* timekeeping_forward_now - update clock to the current time
*
@@ -261,7 +277,7 @@ static void timekeeping_forward_now(struct timekeeper *tk)
tk->xtime_nsec += cycle_delta * tk->mult;
/* If arch requires, add in gettimeoffset() */
- tk->xtime_nsec += arch_gettimeoffset() << tk->shift;
+ tk->xtime_nsec += (u64)arch_gettimeoffset() << tk->shift;
tk_normalize_xtime(tk);
@@ -277,38 +293,39 @@ static void timekeeping_forward_now(struct timekeeper *tk)
*/
void getnstimeofday(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs = 0;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
+ ts->tv_sec = tk->xtime_sec;
+ nsecs = timekeeping_get_ns(tk);
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
+ ts->tv_nsec = 0;
timespec_add_ns(ts, nsecs);
}
EXPORT_SYMBOL(getnstimeofday);
ktime_t ktime_get(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned int seq;
s64 secs, nsecs;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- secs = timekeeper.xtime_sec +
- timekeeper.wall_to_monotonic.tv_sec;
- nsecs = timekeeping_get_ns(&timekeeper) +
- timekeeper.wall_to_monotonic.tv_nsec;
+ seq = read_seqbegin(&tk->lock);
+ secs = tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
+ nsecs = timekeeping_get_ns(tk) + tk->wall_to_monotonic.tv_nsec;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
/*
* Use ktime_set/ktime_add_ns to create a proper ktime on
* 32-bit architectures without CONFIG_KTIME_SCALAR.
@@ -327,21 +344,24 @@ EXPORT_SYMBOL_GPL(ktime_get);
*/
void ktime_get_ts(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec tomono;
+ s64 nsec;
unsigned int seq;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
- tomono = timekeeper.wall_to_monotonic;
+ seq = read_seqbegin(&tk->lock);
+ ts->tv_sec = tk->xtime_sec;
+ nsec = timekeeping_get_ns(tk);
+ tomono = tk->wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
- set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
- ts->tv_nsec + tomono.tv_nsec);
+ ts->tv_sec += tomono.tv_sec;
+ ts->tv_nsec = 0;
+ timespec_add_ns(ts, nsec + tomono.tv_nsec);
}
EXPORT_SYMBOL_GPL(ktime_get_ts);
@@ -358,22 +378,23 @@ EXPORT_SYMBOL_GPL(ktime_get_ts);
*/
void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs_raw, nsecs_real;
WARN_ON_ONCE(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- *ts_raw = timekeeper.raw_time;
- ts_real->tv_sec = timekeeper.xtime_sec;
+ *ts_raw = tk->raw_time;
+ ts_real->tv_sec = tk->xtime_sec;
ts_real->tv_nsec = 0;
- nsecs_raw = timekeeping_get_ns_raw(&timekeeper);
- nsecs_real = timekeeping_get_ns(&timekeeper);
+ nsecs_raw = timekeeping_get_ns_raw(tk);
+ nsecs_real = timekeeping_get_ns(tk);
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
timespec_add_ns(ts_raw, nsecs_raw);
timespec_add_ns(ts_real, nsecs_real);
@@ -406,28 +427,28 @@ EXPORT_SYMBOL(do_gettimeofday);
*/
int do_settimeofday(const struct timespec *tv)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec ts_delta, xt;
unsigned long flags;
- if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
+ if (!timespec_valid_strict(tv))
return -EINVAL;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
- xt = tk_xtime(&timekeeper);
+ xt = tk_xtime(tk);
ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
- timekeeper.wall_to_monotonic =
- timespec_sub(timekeeper.wall_to_monotonic, ts_delta);
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));
- tk_set_xtime(&timekeeper, tv);
+ tk_set_xtime(tk, tv);
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
@@ -436,7 +457,6 @@ int do_settimeofday(const struct timespec *tv)
}
EXPORT_SYMBOL(do_settimeofday);
-
/**
* timekeeping_inject_offset - Adds or subtracts from the current time.
* @tv: pointer to the timespec variable containing the offset
@@ -445,28 +465,37 @@ EXPORT_SYMBOL(do_settimeofday);
*/
int timekeeping_inject_offset(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
+ struct timespec tmp;
+ int ret = 0;
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
+ /* Make sure the proposed value is valid */
+ tmp = timespec_add(tk_xtime(tk), *ts);
+ if (!timespec_valid_strict(&tmp)) {
+ ret = -EINVAL;
+ goto error;
+ }
- tk_xtime_add(&timekeeper, ts);
- timekeeper.wall_to_monotonic =
- timespec_sub(timekeeper.wall_to_monotonic, *ts);
+ tk_xtime_add(tk, ts);
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts));
- timekeeping_update(&timekeeper, true);
+error: /* even if we error out, we forwarded the time, so call update */
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
- return 0;
+ return ret;
}
EXPORT_SYMBOL(timekeeping_inject_offset);
@@ -477,23 +506,24 @@ EXPORT_SYMBOL(timekeeping_inject_offset);
*/
static int change_clocksource(void *data)
{
+ struct timekeeper *tk = &timekeeper;
struct clocksource *new, *old;
unsigned long flags;
new = (struct clocksource *) data;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
if (!new->enable || new->enable(new) == 0) {
- old = timekeeper.clock;
- tk_setup_internals(&timekeeper, new);
+ old = tk->clock;
+ tk_setup_internals(tk, new);
if (old->disable)
old->disable(old);
}
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
return 0;
}
@@ -507,7 +537,9 @@ static int change_clocksource(void *data)
*/
void timekeeping_notify(struct clocksource *clock)
{
- if (timekeeper.clock == clock)
+ struct timekeeper *tk = &timekeeper;
+
+ if (tk->clock == clock)
return;
stop_machine(change_clocksource, clock, NULL);
tick_clock_notify();
@@ -536,35 +568,36 @@ EXPORT_SYMBOL_GPL(ktime_get_real);
*/
void getrawmonotonic(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs;
do {
- seq = read_seqbegin(&timekeeper.lock);
- nsecs = timekeeping_get_ns_raw(&timekeeper);
- *ts = timekeeper.raw_time;
+ seq = read_seqbegin(&tk->lock);
+ nsecs = timekeeping_get_ns_raw(tk);
+ *ts = tk->raw_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
timespec_add_ns(ts, nsecs);
}
EXPORT_SYMBOL(getrawmonotonic);
-
/**
* timekeeping_valid_for_hres - Check if timekeeping is suitable for hres
*/
int timekeeping_valid_for_hres(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
int ret;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ret = timekeeper.clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
+ ret = tk->clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
return ret;
}
@@ -574,15 +607,16 @@ int timekeeping_valid_for_hres(void)
*/
u64 timekeeping_max_deferment(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
u64 ret;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ret = timekeeper.clock->max_idle_ns;
+ ret = tk->clock->max_idle_ns;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
return ret;
}
@@ -622,46 +656,56 @@ void __attribute__((weak)) read_boot_clock(struct timespec *ts)
*/
void __init timekeeping_init(void)
{
+ struct timekeeper *tk = &timekeeper;
struct clocksource *clock;
unsigned long flags;
- struct timespec now, boot;
+ struct timespec now, boot, tmp;
read_persistent_clock(&now);
+ if (!timespec_valid_strict(&now)) {
+ pr_warn("WARNING: Persistent clock returned invalid value!\n"
+ " Check your CMOS/BIOS settings.\n");
+ now.tv_sec = 0;
+ now.tv_nsec = 0;
+ }
+
read_boot_clock(&boot);
+ if (!timespec_valid_strict(&boot)) {
+ pr_warn("WARNING: Boot clock returned invalid value!\n"
+ " Check your CMOS/BIOS settings.\n");
+ boot.tv_sec = 0;
+ boot.tv_nsec = 0;
+ }
- seqlock_init(&timekeeper.lock);
+ seqlock_init(&tk->lock);
ntp_init();
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
clock = clocksource_default_clock();
if (clock->enable)
clock->enable(clock);
- tk_setup_internals(&timekeeper, clock);
+ tk_setup_internals(tk, clock);
- tk_set_xtime(&timekeeper, &now);
- timekeeper.raw_time.tv_sec = 0;
- timekeeper.raw_time.tv_nsec = 0;
+ tk_set_xtime(tk, &now);
+ tk->raw_time.tv_sec = 0;
+ tk->raw_time.tv_nsec = 0;
if (boot.tv_sec == 0 && boot.tv_nsec == 0)
- boot = tk_xtime(&timekeeper);
-
- set_normalized_timespec(&timekeeper.wall_to_monotonic,
- -boot.tv_sec, -boot.tv_nsec);
- update_rt_offset(&timekeeper);
- timekeeper.total_sleep_time.tv_sec = 0;
- timekeeper.total_sleep_time.tv_nsec = 0;
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ boot = tk_xtime(tk);
+
+ set_normalized_timespec(&tmp, -boot.tv_sec, -boot.tv_nsec);
+ tk_set_wall_to_mono(tk, tmp);
+
+ tmp.tv_sec = 0;
+ tmp.tv_nsec = 0;
+ tk_set_sleep_time(tk, tmp);
+
+ write_sequnlock_irqrestore(&tk->lock, flags);
}
/* time in seconds when suspend began */
static struct timespec timekeeping_suspend_time;
-static void update_sleep_time(struct timespec t)
-{
- timekeeper.total_sleep_time = t;
- timekeeper.offs_boot = timespec_to_ktime(t);
-}
-
/**
* __timekeeping_inject_sleeptime - Internal function to add sleep interval
* @delta: pointer to a timespec delta value
@@ -672,18 +716,16 @@ static void update_sleep_time(struct timespec t)
static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
struct timespec *delta)
{
- if (!timespec_valid(delta)) {
+ if (!timespec_valid_strict(delta)) {
printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid "
"sleep delta value!\n");
return;
}
-
tk_xtime_add(tk, delta);
- tk->wall_to_monotonic = timespec_sub(tk->wall_to_monotonic, *delta);
- update_sleep_time(timespec_add(tk->total_sleep_time, *delta));
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta));
+ tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta));
}
-
/**
* timekeeping_inject_sleeptime - Adds suspend interval to timeekeeping values
* @delta: pointer to a timespec delta value
@@ -696,6 +738,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
*/
void timekeeping_inject_sleeptime(struct timespec *delta)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec ts;
@@ -704,21 +747,20 @@ void timekeeping_inject_sleeptime(struct timespec *delta)
if (!(ts.tv_sec == 0 && ts.tv_nsec == 0))
return;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
- __timekeeping_inject_sleeptime(&timekeeper, delta);
+ __timekeeping_inject_sleeptime(tk, delta);
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
}
-
/**
* timekeeping_resume - Resumes the generic timekeeping subsystem.
*
@@ -728,6 +770,7 @@ void timekeeping_inject_sleeptime(struct timespec *delta)
*/
static void timekeeping_resume(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec ts;
@@ -735,18 +778,18 @@ static void timekeeping_resume(void)
clocksource_resume();
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
ts = timespec_sub(ts, timekeeping_suspend_time);
- __timekeeping_inject_sleeptime(&timekeeper, &ts);
+ __timekeeping_inject_sleeptime(tk, &ts);
}
/* re-base the last cycle value */
- timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock);
- timekeeper.ntp_error = 0;
+ tk->clock->cycle_last = tk->clock->read(tk->clock);
+ tk->ntp_error = 0;
timekeeping_suspended = 0;
- timekeeping_update(&timekeeper, false);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ timekeeping_update(tk, false);
+ write_sequnlock_irqrestore(&tk->lock, flags);
touch_softlockup_watchdog();
@@ -758,14 +801,15 @@ static void timekeeping_resume(void)
static int timekeeping_suspend(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec delta, delta_delta;
static struct timespec old_delta;
read_persistent_clock(&timekeeping_suspend_time);
- write_seqlock_irqsave(&timekeeper.lock, flags);
- timekeeping_forward_now(&timekeeper);
+ write_seqlock_irqsave(&tk->lock, flags);
+ timekeeping_forward_now(tk);
timekeeping_suspended = 1;
/*
@@ -774,7 +818,7 @@ static int timekeeping_suspend(void)
* try to compensate so the difference in system time
* and persistent_clock time stays close to constant.
*/
- delta = timespec_sub(tk_xtime(&timekeeper), timekeeping_suspend_time);
+ delta = timespec_sub(tk_xtime(tk), timekeeping_suspend_time);
delta_delta = timespec_sub(delta, old_delta);
if (abs(delta_delta.tv_sec) >= 2) {
/*
@@ -787,7 +831,7 @@ static int timekeeping_suspend(void)
timekeeping_suspend_time =
timespec_add(timekeeping_suspend_time, delta_delta);
}
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
clocksource_suspend();
@@ -898,27 +942,29 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
* the error. This causes the likely below to be unlikely.
*
* The proper fix is to avoid rounding up by using
- * the high precision timekeeper.xtime_nsec instead of
+ * the high precision tk->xtime_nsec instead of
* xtime.tv_nsec everywhere. Fixing this will take some
* time.
*/
if (likely(error <= interval))
adj = 1;
else
- adj = timekeeping_bigadjust(tk, error, &interval,
- &offset);
- } else if (error < -interval) {
- /* See comment above, this is just switched for the negative */
- error >>= 2;
- if (likely(error >= -interval)) {
- adj = -1;
- interval = -interval;
- offset = -offset;
- } else
- adj = timekeeping_bigadjust(tk, error, &interval,
- &offset);
- } else
- return;
+ adj = timekeeping_bigadjust(tk, error, &interval, &offset);
+ } else {
+ if (error < -interval) {
+ /* See comment above, this is just switched for the negative */
+ error >>= 2;
+ if (likely(error >= -interval)) {
+ adj = -1;
+ interval = -interval;
+ offset = -offset;
+ } else {
+ adj = timekeeping_bigadjust(tk, error, &interval, &offset);
+ }
+ } else {
+ goto out_adjust;
+ }
+ }
if (unlikely(tk->clock->maxadj &&
(tk->mult + adj > tk->clock->mult + tk->clock->maxadj))) {
@@ -981,6 +1027,7 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
tk->xtime_nsec -= offset;
tk->ntp_error -= (interval - offset) << tk->ntp_error_shift;
+out_adjust:
/*
* It may be possible that when we entered this function, xtime_nsec
* was very small. Further, if we're slightly speeding the clocksource
@@ -1003,7 +1050,6 @@ static void timekeeping_adjust(struct timekeeper *tk, s64 offset)
}
-
/**
* accumulate_nsecs_to_secs - Accumulates nsecs into secs
*
@@ -1024,15 +1070,21 @@ static inline void accumulate_nsecs_to_secs(struct timekeeper *tk)
/* Figure out if its a leap sec and apply if needed */
leap = second_overflow(tk->xtime_sec);
- tk->xtime_sec += leap;
- tk->wall_to_monotonic.tv_sec -= leap;
- if (leap)
- clock_was_set_delayed();
+ if (unlikely(leap)) {
+ struct timespec ts;
+
+ tk->xtime_sec += leap;
+
+ ts.tv_sec = leap;
+ ts.tv_nsec = 0;
+ tk_set_wall_to_mono(tk,
+ timespec_sub(tk->wall_to_monotonic, ts));
+ clock_was_set_delayed();
+ }
}
}
-
/**
* logarithmic_accumulation - shifted accumulation of cycles
*
@@ -1076,7 +1128,6 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
return offset;
}
-
/**
* update_wall_time - Uses the current clocksource to increment the wall time
*
@@ -1084,25 +1135,30 @@ static cycle_t logarithmic_accumulation(struct timekeeper *tk, cycle_t offset,
static void update_wall_time(void)
{
struct clocksource *clock;
+ struct timekeeper *tk = &timekeeper;
cycle_t offset;
int shift = 0, maxshift;
unsigned long flags;
s64 remainder;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
/* Make sure we're fully resumed: */
if (unlikely(timekeeping_suspended))
goto out;
- clock = timekeeper.clock;
+ clock = tk->clock;
#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
- offset = timekeeper.cycle_interval;
+ offset = tk->cycle_interval;
#else
offset = (clock->read(clock) - clock->cycle_last) & clock->mask;
#endif
+ /* Check if there's really nothing to do */
+ if (offset < tk->cycle_interval)
+ goto out;
+
/*
* With NO_HZ we may have to accumulate many cycle_intervals
* (think "ticks") worth of time at once. To do this efficiently,
@@ -1111,19 +1167,19 @@ static void update_wall_time(void)
* chunk in one go, and then try to consume the next smaller
* doubled multiple.
*/
- shift = ilog2(offset) - ilog2(timekeeper.cycle_interval);
+ shift = ilog2(offset) - ilog2(tk->cycle_interval);
shift = max(0, shift);
/* Bound shift to one less than what overflows tick_length */
maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
shift = min(shift, maxshift);
- while (offset >= timekeeper.cycle_interval) {
- offset = logarithmic_accumulation(&timekeeper, offset, shift);
- if(offset < timekeeper.cycle_interval<<shift)
+ while (offset >= tk->cycle_interval) {
+ offset = logarithmic_accumulation(tk, offset, shift);
+ if (offset < tk->cycle_interval<<shift)
shift--;
}
/* correct the clock when NTP error is too big */
- timekeeping_adjust(&timekeeper, offset);
+ timekeeping_adjust(tk, offset);
/*
@@ -1135,21 +1191,21 @@ static void update_wall_time(void)
* the vsyscall implementations are converted to use xtime_nsec
* (shifted nanoseconds), this can be killed.
*/
- remainder = timekeeper.xtime_nsec & ((1 << timekeeper.shift) - 1);
- timekeeper.xtime_nsec -= remainder;
- timekeeper.xtime_nsec += 1 << timekeeper.shift;
- timekeeper.ntp_error += remainder << timekeeper.ntp_error_shift;
+ remainder = tk->xtime_nsec & ((1ULL << tk->shift) - 1);
+ tk->xtime_nsec -= remainder;
+ tk->xtime_nsec += 1ULL << tk->shift;
+ tk->ntp_error += remainder << tk->ntp_error_shift;
/*
* Finally, make sure that after the rounding
* xtime_nsec isn't larger than NSEC_PER_SEC
*/
- accumulate_nsecs_to_secs(&timekeeper);
+ accumulate_nsecs_to_secs(tk);
- timekeeping_update(&timekeeper, false);
+ timekeeping_update(tk, false);
out:
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
}
@@ -1166,18 +1222,18 @@ out:
*/
void getboottime(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec boottime = {
- .tv_sec = timekeeper.wall_to_monotonic.tv_sec +
- timekeeper.total_sleep_time.tv_sec,
- .tv_nsec = timekeeper.wall_to_monotonic.tv_nsec +
- timekeeper.total_sleep_time.tv_nsec
+ .tv_sec = tk->wall_to_monotonic.tv_sec +
+ tk->total_sleep_time.tv_sec,
+ .tv_nsec = tk->wall_to_monotonic.tv_nsec +
+ tk->total_sleep_time.tv_nsec
};
set_normalized_timespec(ts, -boottime.tv_sec, -boottime.tv_nsec);
}
EXPORT_SYMBOL_GPL(getboottime);
-
/**
* get_monotonic_boottime - Returns monotonic time since boot
* @ts: pointer to the timespec to be set
@@ -1189,22 +1245,25 @@ EXPORT_SYMBOL_GPL(getboottime);
*/
void get_monotonic_boottime(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec tomono, sleep;
+ s64 nsec;
unsigned int seq;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
- tomono = timekeeper.wall_to_monotonic;
- sleep = timekeeper.total_sleep_time;
+ seq = read_seqbegin(&tk->lock);
+ ts->tv_sec = tk->xtime_sec;
+ nsec = timekeeping_get_ns(tk);
+ tomono = tk->wall_to_monotonic;
+ sleep = tk->total_sleep_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
- set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
- ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec);
+ ts->tv_sec += tomono.tv_sec + sleep.tv_sec;
+ ts->tv_nsec = 0;
+ timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec);
}
EXPORT_SYMBOL_GPL(get_monotonic_boottime);
@@ -1231,31 +1290,38 @@ EXPORT_SYMBOL_GPL(ktime_get_boottime);
*/
void monotonic_to_bootbased(struct timespec *ts)
{
- *ts = timespec_add(*ts, timekeeper.total_sleep_time);
+ struct timekeeper *tk = &timekeeper;
+
+ *ts = timespec_add(*ts, tk->total_sleep_time);
}
EXPORT_SYMBOL_GPL(monotonic_to_bootbased);
unsigned long get_seconds(void)
{
- return timekeeper.xtime_sec;
+ struct timekeeper *tk = &timekeeper;
+
+ return tk->xtime_sec;
}
EXPORT_SYMBOL(get_seconds);
struct timespec __current_kernel_time(void)
{
- return tk_xtime(&timekeeper);
+ struct timekeeper *tk = &timekeeper;
+
+ return tk_xtime(tk);
}
struct timespec current_kernel_time(void)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec now;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- now = tk_xtime(&timekeeper);
- } while (read_seqretry(&timekeeper.lock, seq));
+ now = tk_xtime(tk);
+ } while (read_seqretry(&tk->lock, seq));
return now;
}
@@ -1263,15 +1329,16 @@ EXPORT_SYMBOL(current_kernel_time);
struct timespec get_monotonic_coarse(void)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec now, mono;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- now = tk_xtime(&timekeeper);
- mono = timekeeper.wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ now = tk_xtime(tk);
+ mono = tk->wall_to_monotonic;
+ } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(&now, now.tv_sec + mono.tv_sec,
now.tv_nsec + mono.tv_nsec);
@@ -1300,14 +1367,15 @@ void do_timer(unsigned long ticks)
void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
struct timespec *wtom, struct timespec *sleep)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
- *xtim = tk_xtime(&timekeeper);
- *wtom = timekeeper.wall_to_monotonic;
- *sleep = timekeeper.total_sleep_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ seq = read_seqbegin(&tk->lock);
+ *xtim = tk_xtime(tk);
+ *wtom = tk->wall_to_monotonic;
+ *sleep = tk->total_sleep_time;
+ } while (read_seqretry(&tk->lock, seq));
}
#ifdef CONFIG_HIGH_RES_TIMERS
@@ -1321,19 +1389,20 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
*/
ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot)
{
+ struct timekeeper *tk = &timekeeper;
ktime_t now;
unsigned int seq;
u64 secs, nsecs;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- secs = timekeeper.xtime_sec;
- nsecs = timekeeping_get_ns(&timekeeper);
+ secs = tk->xtime_sec;
+ nsecs = timekeeping_get_ns(tk);
- *offs_real = timekeeper.offs_real;
- *offs_boot = timekeeper.offs_boot;
- } while (read_seqretry(&timekeeper.lock, seq));
+ *offs_real = tk->offs_real;
+ *offs_boot = tk->offs_boot;
+ } while (read_seqretry(&tk->lock, seq));
now = ktime_add_ns(ktime_set(secs, 0), nsecs);
now = ktime_sub(now, *offs_real);
@@ -1346,19 +1415,19 @@ ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot)
*/
ktime_t ktime_get_monotonic_offset(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
struct timespec wtom;
do {
- seq = read_seqbegin(&timekeeper.lock);
- wtom = timekeeper.wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ seq = read_seqbegin(&tk->lock);
+ wtom = tk->wall_to_monotonic;
+ } while (read_seqretry(&tk->lock, seq));
return timespec_to_ktime(wtom);
}
EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset);
-
/**
* xtime_update() - advances the timekeeping infrastructure
* @ticks: number of ticks, that have elapsed since the last call.
diff --git a/kernel/timer.c b/kernel/timer.c
index a61c09374eba..8c5e7b908c68 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1407,13 +1407,6 @@ SYSCALL_DEFINE1(alarm, unsigned int, seconds)
#endif
-#ifndef __alpha__
-
-/*
- * The Alpha uses getxpid, getxuid, and getxgid instead. Maybe this
- * should be moved into arch/i386 instead?
- */
-
/**
* sys_getpid - return the thread group id of the current process
*
@@ -1469,8 +1462,6 @@ SYSCALL_DEFINE0(getegid)
return from_kgid_munged(current_user_ns(), current_egid());
}
-#endif
-
static void process_timeout(unsigned long __data)
{
wake_up_process((struct task_struct *)__data);
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index fee3752ae8f6..8a6d2ee2086c 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -281,7 +281,7 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip)
head = this_cpu_ptr(event_function.perf_events);
perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0,
- 1, &regs, head);
+ 1, &regs, head, NULL);
#undef ENTRY_SIZE
}
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index b31d3d5699fe..1a2117043bb1 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1002,7 +1002,8 @@ static __kprobes void kprobe_perf_func(struct kprobe *kp,
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx,
+ entry->ip, 1, regs, head, NULL);
}
/* Kretprobe profile handler */
@@ -1033,7 +1034,8 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx,
+ entry->ret_ip, 1, regs, head, NULL);
}
#endif /* CONFIG_PERF_EVENTS */
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 96fc73369099..6b245f64c8dd 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -506,6 +506,8 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
int size;
syscall_nr = syscall_get_nr(current, regs);
+ if (syscall_nr < 0)
+ return;
if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
return;
@@ -532,7 +534,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
(unsigned long *)&rec->args);
head = this_cpu_ptr(sys_data->enter_event->perf_events);
- perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
+ perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
}
int perf_sysenter_enable(struct ftrace_event_call *call)
@@ -580,6 +582,8 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
int size;
syscall_nr = syscall_get_nr(current, regs);
+ if (syscall_nr < 0)
+ return;
if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
return;
@@ -608,7 +612,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
rec->ret = syscall_get_return_value(current, regs);
head = this_cpu_ptr(sys_data->exit_event->perf_events);
- perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
+ perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
}
int perf_sysexit_enable(struct ftrace_event_call *call)
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 2b36ac68549e..03003cd7dd96 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -670,7 +670,7 @@ static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs)
call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL);
out:
preempt_enable();
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 69add8a9da68..4b1dfba70f7c 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -575,7 +575,7 @@ out:
/*
* Create/destroy watchdog threads as CPUs come and go:
*/
-static int
+static int __cpuinit
cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
@@ -610,27 +610,10 @@ cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
return NOTIFY_OK;
}
-static struct notifier_block cpu_nfb = {
+static struct notifier_block __cpuinitdata cpu_nfb = {
.notifier_call = cpu_callback
};
-#ifdef CONFIG_SUSPEND
-/*
- * On exit from suspend we force an offline->online transition on the boot CPU
- * so that the PMU state that was lost while in suspended state gets set up
- * properly for the boot CPU. This information is required for restarting the
- * NMI watchdog.
- */
-void lockup_detector_bootcpu_resume(void)
-{
- void *cpu = (void *)(long)smp_processor_id();
-
- cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu);
- cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu);
- cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu);
-}
-#endif
-
void __init lockup_detector_init(void)
{
void *cpu = (void *)(long)smp_processor_id();
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 692d97628a10..3c5a79e2134c 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -66,6 +66,7 @@ enum {
/* pool flags */
POOL_MANAGE_WORKERS = 1 << 0, /* need to manage workers */
+ POOL_MANAGING_WORKERS = 1 << 1, /* managing workers */
/* worker flags */
WORKER_STARTED = 1 << 0, /* started */
@@ -652,7 +653,7 @@ static bool need_to_manage_workers(struct worker_pool *pool)
/* Do we have too many workers and should some go away? */
static bool too_many_workers(struct worker_pool *pool)
{
- bool managing = mutex_is_locked(&pool->manager_mutex);
+ bool managing = pool->flags & POOL_MANAGING_WORKERS;
int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
int nr_busy = pool->nr_workers - nr_idle;
@@ -1326,6 +1327,15 @@ static void idle_worker_rebind(struct worker *worker)
/* we did our part, wait for rebind_workers() to finish up */
wait_event(gcwq->rebind_hold, !(worker->flags & WORKER_REBIND));
+
+ /*
+ * rebind_workers() shouldn't finish until all workers passed the
+ * above WORKER_REBIND wait. Tell it when done.
+ */
+ spin_lock_irq(&worker->pool->gcwq->lock);
+ if (!--worker->idle_rebind->cnt)
+ complete(&worker->idle_rebind->done);
+ spin_unlock_irq(&worker->pool->gcwq->lock);
}
/*
@@ -1339,8 +1349,16 @@ static void busy_worker_rebind_fn(struct work_struct *work)
struct worker *worker = container_of(work, struct worker, rebind_work);
struct global_cwq *gcwq = worker->pool->gcwq;
- if (worker_maybe_bind_and_lock(worker))
- worker_clr_flags(worker, WORKER_REBIND);
+ worker_maybe_bind_and_lock(worker);
+
+ /*
+ * %WORKER_REBIND must be cleared even if the above binding failed;
+ * otherwise, we may confuse the next CPU_UP cycle or oops / get
+ * stuck by calling idle_worker_rebind() prematurely. If CPU went
+ * down again inbetween, %WORKER_UNBOUND would be set, so clearing
+ * %WORKER_REBIND is always safe.
+ */
+ worker_clr_flags(worker, WORKER_REBIND);
spin_unlock_irq(&gcwq->lock);
}
@@ -1396,12 +1414,15 @@ retry:
/* set REBIND and kick idle ones, we'll wait for these later */
for_each_worker_pool(pool, gcwq) {
list_for_each_entry(worker, &pool->idle_list, entry) {
+ unsigned long worker_flags = worker->flags;
+
if (worker->flags & WORKER_REBIND)
continue;
- /* morph UNBOUND to REBIND */
- worker->flags &= ~WORKER_UNBOUND;
- worker->flags |= WORKER_REBIND;
+ /* morph UNBOUND to REBIND atomically */
+ worker_flags &= ~WORKER_UNBOUND;
+ worker_flags |= WORKER_REBIND;
+ ACCESS_ONCE(worker->flags) = worker_flags;
idle_rebind.cnt++;
worker->idle_rebind = &idle_rebind;
@@ -1419,25 +1440,15 @@ retry:
goto retry;
}
- /*
- * All idle workers are rebound and waiting for %WORKER_REBIND to
- * be cleared inside idle_worker_rebind(). Clear and release.
- * Clearing %WORKER_REBIND from this foreign context is safe
- * because these workers are still guaranteed to be idle.
- */
- for_each_worker_pool(pool, gcwq)
- list_for_each_entry(worker, &pool->idle_list, entry)
- worker->flags &= ~WORKER_REBIND;
-
- wake_up_all(&gcwq->rebind_hold);
-
- /* rebind busy workers */
+ /* all idle workers are rebound, rebind busy workers */
for_each_busy_worker(worker, i, pos, gcwq) {
struct work_struct *rebind_work = &worker->rebind_work;
+ unsigned long worker_flags = worker->flags;
- /* morph UNBOUND to REBIND */
- worker->flags &= ~WORKER_UNBOUND;
- worker->flags |= WORKER_REBIND;
+ /* morph UNBOUND to REBIND atomically */
+ worker_flags &= ~WORKER_UNBOUND;
+ worker_flags |= WORKER_REBIND;
+ ACCESS_ONCE(worker->flags) = worker_flags;
if (test_and_set_bit(WORK_STRUCT_PENDING_BIT,
work_data_bits(rebind_work)))
@@ -1449,6 +1460,34 @@ retry:
worker->scheduled.next,
work_color_to_flags(WORK_NO_COLOR));
}
+
+ /*
+ * All idle workers are rebound and waiting for %WORKER_REBIND to
+ * be cleared inside idle_worker_rebind(). Clear and release.
+ * Clearing %WORKER_REBIND from this foreign context is safe
+ * because these workers are still guaranteed to be idle.
+ *
+ * We need to make sure all idle workers passed WORKER_REBIND wait
+ * in idle_worker_rebind() before returning; otherwise, workers can
+ * get stuck at the wait if hotplug cycle repeats.
+ */
+ idle_rebind.cnt = 1;
+ INIT_COMPLETION(idle_rebind.done);
+
+ for_each_worker_pool(pool, gcwq) {
+ list_for_each_entry(worker, &pool->idle_list, entry) {
+ worker->flags &= ~WORKER_REBIND;
+ idle_rebind.cnt++;
+ }
+ }
+
+ wake_up_all(&gcwq->rebind_hold);
+
+ if (--idle_rebind.cnt) {
+ spin_unlock_irq(&gcwq->lock);
+ wait_for_completion(&idle_rebind.done);
+ spin_lock_irq(&gcwq->lock);
+ }
}
static struct worker *alloc_worker(void)
@@ -1794,9 +1833,45 @@ static bool manage_workers(struct worker *worker)
struct worker_pool *pool = worker->pool;
bool ret = false;
- if (!mutex_trylock(&pool->manager_mutex))
+ if (pool->flags & POOL_MANAGING_WORKERS)
return ret;
+ pool->flags |= POOL_MANAGING_WORKERS;
+
+ /*
+ * To simplify both worker management and CPU hotplug, hold off
+ * management while hotplug is in progress. CPU hotplug path can't
+ * grab %POOL_MANAGING_WORKERS to achieve this because that can
+ * lead to idle worker depletion (all become busy thinking someone
+ * else is managing) which in turn can result in deadlock under
+ * extreme circumstances. Use @pool->manager_mutex to synchronize
+ * manager against CPU hotplug.
+ *
+ * manager_mutex would always be free unless CPU hotplug is in
+ * progress. trylock first without dropping @gcwq->lock.
+ */
+ if (unlikely(!mutex_trylock(&pool->manager_mutex))) {
+ spin_unlock_irq(&pool->gcwq->lock);
+ mutex_lock(&pool->manager_mutex);
+ /*
+ * CPU hotplug could have happened while we were waiting
+ * for manager_mutex. Hotplug itself can't handle us
+ * because manager isn't either on idle or busy list, and
+ * @gcwq's state and ours could have deviated.
+ *
+ * As hotplug is now excluded via manager_mutex, we can
+ * simply try to bind. It will succeed or fail depending
+ * on @gcwq's current state. Try it and adjust
+ * %WORKER_UNBOUND accordingly.
+ */
+ if (worker_maybe_bind_and_lock(worker))
+ worker->flags &= ~WORKER_UNBOUND;
+ else
+ worker->flags |= WORKER_UNBOUND;
+
+ ret = true;
+ }
+
pool->flags &= ~POOL_MANAGE_WORKERS;
/*
@@ -1806,6 +1881,7 @@ static bool manage_workers(struct worker *worker)
ret |= maybe_destroy_workers(pool);
ret |= maybe_create_worker(pool);
+ pool->flags &= ~POOL_MANAGING_WORKERS;
mutex_unlock(&pool->manager_mutex);
return ret;
}
@@ -3500,18 +3576,17 @@ static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
#ifdef CONFIG_SMP
struct work_for_cpu {
- struct completion completion;
+ struct work_struct work;
long (*fn)(void *);
void *arg;
long ret;
};
-static int do_work_for_cpu(void *_wfc)
+static void work_for_cpu_fn(struct work_struct *work)
{
- struct work_for_cpu *wfc = _wfc;
+ struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);
+
wfc->ret = wfc->fn(wfc->arg);
- complete(&wfc->completion);
- return 0;
}
/**
@@ -3526,19 +3601,11 @@ static int do_work_for_cpu(void *_wfc)
*/
long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg)
{
- struct task_struct *sub_thread;
- struct work_for_cpu wfc = {
- .completion = COMPLETION_INITIALIZER_ONSTACK(wfc.completion),
- .fn = fn,
- .arg = arg,
- };
+ struct work_for_cpu wfc = { .fn = fn, .arg = arg };
- sub_thread = kthread_create(do_work_for_cpu, &wfc, "work_for_cpu");
- if (IS_ERR(sub_thread))
- return PTR_ERR(sub_thread);
- kthread_bind(sub_thread, cpu);
- wake_up_process(sub_thread);
- wait_for_completion(&wfc.completion);
+ INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn);
+ schedule_work_on(cpu, &wfc.work);
+ flush_work(&wfc.work);
return wfc.ret;
}
EXPORT_SYMBOL_GPL(work_on_cpu);
diff --git a/lib/digsig.c b/lib/digsig.c
index 286d558033e2..8c0e62975c88 100644
--- a/lib/digsig.c
+++ b/lib/digsig.c
@@ -163,9 +163,11 @@ static int digsig_verify_rsa(struct key *key,
memcpy(out1 + head, p, l);
err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
+ if (err)
+ goto err;
- if (!err && len == hlen)
- err = memcmp(out2, h, hlen);
+ if (len != hlen || memcmp(out2, h, hlen))
+ err = -EINVAL;
err:
mpi_free(in);
diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c
index c785554f9523..ebf3bac460b0 100644
--- a/lib/flex_proportions.c
+++ b/lib/flex_proportions.c
@@ -62,7 +62,7 @@ void fprop_global_destroy(struct fprop_global *p)
*/
bool fprop_new_period(struct fprop_global *p, int periods)
{
- u64 events;
+ s64 events;
unsigned long flags;
local_irq_save(flags);
diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c
index f8a3f1a829b8..ba6085d9c741 100644
--- a/lib/percpu_counter.c
+++ b/lib/percpu_counter.c
@@ -12,7 +12,7 @@
#ifdef CONFIG_HOTPLUG_CPU
static LIST_HEAD(percpu_counters);
-static DEFINE_MUTEX(percpu_counters_lock);
+static DEFINE_SPINLOCK(percpu_counters_lock);
#endif
#ifdef CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER
@@ -123,9 +123,9 @@ int __percpu_counter_init(struct percpu_counter *fbc, s64 amount,
#ifdef CONFIG_HOTPLUG_CPU
INIT_LIST_HEAD(&fbc->list);
- mutex_lock(&percpu_counters_lock);
+ spin_lock(&percpu_counters_lock);
list_add(&fbc->list, &percpu_counters);
- mutex_unlock(&percpu_counters_lock);
+ spin_unlock(&percpu_counters_lock);
#endif
return 0;
}
@@ -139,9 +139,9 @@ void percpu_counter_destroy(struct percpu_counter *fbc)
debug_percpu_counter_deactivate(fbc);
#ifdef CONFIG_HOTPLUG_CPU
- mutex_lock(&percpu_counters_lock);
+ spin_lock(&percpu_counters_lock);
list_del(&fbc->list);
- mutex_unlock(&percpu_counters_lock);
+ spin_unlock(&percpu_counters_lock);
#endif
free_percpu(fbc->counters);
fbc->counters = NULL;
@@ -170,7 +170,7 @@ static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb,
return NOTIFY_OK;
cpu = (unsigned long)hcpu;
- mutex_lock(&percpu_counters_lock);
+ spin_lock(&percpu_counters_lock);
list_for_each_entry(fbc, &percpu_counters, list) {
s32 *pcount;
unsigned long flags;
@@ -181,7 +181,7 @@ static int __cpuinit percpu_counter_hotcpu_callback(struct notifier_block *nb,
*pcount = 0;
raw_spin_unlock_irqrestore(&fbc->lock, flags);
}
- mutex_unlock(&percpu_counters_lock);
+ spin_unlock(&percpu_counters_lock);
#endif
return NOTIFY_OK;
}
diff --git a/mm/Kconfig b/mm/Kconfig
index 82fed4eb2b6f..d5c8019c6627 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -140,9 +140,13 @@ config ARCH_DISCARD_MEMBLOCK
config NO_BOOTMEM
boolean
+config MEMORY_ISOLATION
+ boolean
+
# eventually, we can have this option just 'select SPARSEMEM'
config MEMORY_HOTPLUG
bool "Allow for memory hot-add"
+ select MEMORY_ISOLATION
depends on SPARSEMEM || X86_64_ACPI_NUMA
depends on HOTPLUG && ARCH_ENABLE_MEMORY_HOTPLUG
depends on (IA64 || X86 || PPC_BOOK3S_64 || SUPERH || S390)
@@ -272,6 +276,7 @@ config MEMORY_FAILURE
depends on MMU
depends on ARCH_SUPPORTS_MEMORY_FAILURE
bool "Enable recovery from hardware memory errors"
+ select MEMORY_ISOLATION
help
Enables code to recover from some memory failures on systems
with MCA recovery. This allows a system to continue running
diff --git a/mm/Makefile b/mm/Makefile
index 8e81fe263c94..92753e2d82da 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -15,8 +15,8 @@ obj-y := filemap.o mempool.o oom_kill.o fadvise.o \
maccess.o page_alloc.o page-writeback.o \
readahead.o swap.o truncate.o vmscan.o shmem.o \
prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \
- page_isolation.o mm_init.o mmu_context.o percpu.o \
- compaction.o slab_common.o $(mmu-y)
+ mm_init.o mmu_context.o percpu.o slab_common.o \
+ compaction.o $(mmu-y)
obj-y += init-mm.o
@@ -49,9 +49,11 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o
obj-$(CONFIG_MIGRATION) += migrate.o
obj-$(CONFIG_QUICKLIST) += quicklist.o
obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o
-obj-$(CONFIG_CGROUP_MEM_RES_CTLR) += memcontrol.o page_cgroup.o
+obj-$(CONFIG_MEMCG) += memcontrol.o page_cgroup.o
+obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o
obj-$(CONFIG_MEMORY_FAILURE) += memory-failure.o
obj-$(CONFIG_HWPOISON_INJECT) += hwpoison-inject.o
obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
obj-$(CONFIG_CLEANCACHE) += cleancache.o
+obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 3387aea11209..b41823cc05e6 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -39,12 +39,6 @@ DEFINE_SPINLOCK(bdi_lock);
LIST_HEAD(bdi_list);
LIST_HEAD(bdi_pending_list);
-static struct task_struct *sync_supers_tsk;
-static struct timer_list sync_supers_timer;
-
-static int bdi_sync_supers(void *);
-static void sync_supers_timer_fn(unsigned long);
-
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2)
{
if (wb1 < wb2) {
@@ -250,12 +244,6 @@ static int __init default_bdi_init(void)
{
int err;
- sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers");
- BUG_ON(IS_ERR(sync_supers_tsk));
-
- setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0);
- bdi_arm_supers_timer();
-
err = bdi_init(&default_backing_dev_info);
if (!err)
bdi_register(&default_backing_dev_info, NULL, "default");
@@ -270,46 +258,6 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi)
return wb_has_dirty_io(&bdi->wb);
}
-/*
- * kupdated() used to do this. We cannot do it from the bdi_forker_thread()
- * or we risk deadlocking on ->s_umount. The longer term solution would be
- * to implement sync_supers_bdi() or similar and simply do it from the
- * bdi writeback thread individually.
- */
-static int bdi_sync_supers(void *unused)
-{
- set_user_nice(current, 0);
-
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
-
- /*
- * Do this periodically, like kupdated() did before.
- */
- sync_supers();
- }
-
- return 0;
-}
-
-void bdi_arm_supers_timer(void)
-{
- unsigned long next;
-
- if (!dirty_writeback_interval)
- return;
-
- next = msecs_to_jiffies(dirty_writeback_interval * 10) + jiffies;
- mod_timer(&sync_supers_timer, round_jiffies_up(next));
-}
-
-static void sync_supers_timer_fn(unsigned long unused)
-{
- wake_up_process(sync_supers_tsk);
- bdi_arm_supers_timer();
-}
-
static void wakeup_timer_fn(unsigned long data)
{
struct backing_dev_info *bdi = (struct backing_dev_info *)data;
@@ -886,3 +834,23 @@ out:
return ret;
}
EXPORT_SYMBOL(wait_iff_congested);
+
+int pdflush_proc_obsolete(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ char kbuf[] = "0\n";
+
+ if (*ppos) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (copy_to_user(buffer, kbuf, sizeof(kbuf)))
+ return -EFAULT;
+ printk_once(KERN_WARNING "%s exported in /proc is scheduled for removal\n",
+ table->procname);
+
+ *lenp = 2;
+ *ppos += *lenp;
+ return 2;
+}
diff --git a/mm/compaction.c b/mm/compaction.c
index 2f42d9528539..7fcd3a52e68d 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -51,6 +51,47 @@ static inline bool migrate_async_suitable(int migratetype)
}
/*
+ * Compaction requires the taking of some coarse locks that are potentially
+ * very heavily contended. Check if the process needs to be scheduled or
+ * if the lock is contended. For async compaction, back out in the event
+ * if contention is severe. For sync compaction, schedule.
+ *
+ * Returns true if the lock is held.
+ * Returns false if the lock is released and compaction should abort
+ */
+static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
+ bool locked, struct compact_control *cc)
+{
+ if (need_resched() || spin_is_contended(lock)) {
+ if (locked) {
+ spin_unlock_irqrestore(lock, *flags);
+ locked = false;
+ }
+
+ /* async aborts if taking too long or contended */
+ if (!cc->sync) {
+ if (cc->contended)
+ *cc->contended = true;
+ return false;
+ }
+
+ cond_resched();
+ if (fatal_signal_pending(current))
+ return false;
+ }
+
+ if (!locked)
+ spin_lock_irqsave(lock, *flags);
+ return true;
+}
+
+static inline bool compact_trylock_irqsave(spinlock_t *lock,
+ unsigned long *flags, struct compact_control *cc)
+{
+ return compact_checklock_irqsave(lock, flags, false, cc);
+}
+
+/*
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
* pages inside of the pageblock (even though it may still end up isolating
@@ -173,7 +214,7 @@ isolate_freepages_range(unsigned long start_pfn, unsigned long end_pfn)
}
/* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
+static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
{
struct page *page;
unsigned int count[2] = { 0, };
@@ -181,8 +222,14 @@ static void acct_isolated(struct zone *zone, struct compact_control *cc)
list_for_each_entry(page, &cc->migratepages, lru)
count[!!page_is_file_cache(page)]++;
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ /* If locked we can use the interrupt unsafe versions */
+ if (locked) {
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ } else {
+ mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ }
}
/* Similar to reclaim, but different enough that they don't share logic */
@@ -228,6 +275,8 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
struct list_head *migratelist = &cc->migratepages;
isolate_mode_t mode = 0;
struct lruvec *lruvec;
+ unsigned long flags;
+ bool locked;
/*
* Ensure that there are not too many pages isolated from the LRU
@@ -247,25 +296,22 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
/* Time to isolate some pages for migration */
cond_resched();
- spin_lock_irq(&zone->lru_lock);
+ spin_lock_irqsave(&zone->lru_lock, flags);
+ locked = true;
for (; low_pfn < end_pfn; low_pfn++) {
struct page *page;
- bool locked = true;
/* give a chance to irqs before checking need_resched() */
if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
- spin_unlock_irq(&zone->lru_lock);
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
locked = false;
}
- if (need_resched() || spin_is_contended(&zone->lru_lock)) {
- if (locked)
- spin_unlock_irq(&zone->lru_lock);
- cond_resched();
- spin_lock_irq(&zone->lru_lock);
- if (fatal_signal_pending(current))
- break;
- } else if (!locked)
- spin_lock_irq(&zone->lru_lock);
+
+ /* Check if it is ok to still hold the lock */
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
+ locked, cc);
+ if (!locked)
+ break;
/*
* migrate_pfn does not necessarily start aligned to a
@@ -349,9 +395,10 @@ isolate_migratepages_range(struct zone *zone, struct compact_control *cc,
}
}
- acct_isolated(zone, cc);
+ acct_isolated(zone, locked, cc);
- spin_unlock_irq(&zone->lru_lock);
+ if (locked)
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
@@ -384,6 +431,20 @@ static bool suitable_migration_target(struct page *page)
}
/*
+ * Returns the start pfn of the last page block in a zone. This is the starting
+ * point for full compaction of a zone. Compaction searches for free pages from
+ * the end of each zone, while isolate_freepages_block scans forward inside each
+ * page block.
+ */
+static unsigned long start_free_pfn(struct zone *zone)
+{
+ unsigned long free_pfn;
+ free_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ free_pfn &= ~(pageblock_nr_pages-1);
+ return free_pfn;
+}
+
+/*
* Based on information in the current compact_control, find blocks
* suitable for isolating free pages from and then isolate them.
*/
@@ -447,7 +508,16 @@ static void isolate_freepages(struct zone *zone,
* are disabled
*/
isolated = 0;
- spin_lock_irqsave(&zone->lock, flags);
+
+ /*
+ * The zone lock must be held to isolate freepages. This
+ * unfortunately this is a very coarse lock and can be
+ * heavily contended if there are parallel allocations
+ * or parallel compactions. For async compaction do not
+ * spin on the lock
+ */
+ if (!compact_trylock_irqsave(&zone->lock, &flags, cc))
+ break;
if (suitable_migration_target(page)) {
end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
isolated = isolate_freepages_block(pfn, end_pfn,
@@ -461,8 +531,19 @@ static void isolate_freepages(struct zone *zone,
* looking for free pages, the search will restart here as
* page migration may have returned some pages to the allocator
*/
- if (isolated)
+ if (isolated) {
high_pfn = max(high_pfn, pfn);
+
+ /*
+ * If the free scanner has wrapped, update
+ * compact_cached_free_pfn to point to the highest
+ * pageblock with free pages. This reduces excessive
+ * scanning of full pageblocks near the end of the
+ * zone
+ */
+ if (cc->order > 0 && cc->wrapped)
+ zone->compact_cached_free_pfn = high_pfn;
+ }
}
/* split_free_page does not map the pages */
@@ -470,6 +551,11 @@ static void isolate_freepages(struct zone *zone,
cc->free_pfn = high_pfn;
cc->nr_freepages = nr_freepages;
+
+ /* If compact_cached_free_pfn is reset then set it now */
+ if (cc->order > 0 && !cc->wrapped &&
+ zone->compact_cached_free_pfn == start_free_pfn(zone))
+ zone->compact_cached_free_pfn = high_pfn;
}
/*
@@ -565,8 +651,26 @@ static int compact_finished(struct zone *zone,
if (fatal_signal_pending(current))
return COMPACT_PARTIAL;
- /* Compaction run completes if the migrate and free scanner meet */
- if (cc->free_pfn <= cc->migrate_pfn)
+ /*
+ * A full (order == -1) compaction run starts at the beginning and
+ * end of a zone; it completes when the migrate and free scanner meet.
+ * A partial (order > 0) compaction can start with the free scanner
+ * at a random point in the zone, and may have to restart.
+ */
+ if (cc->free_pfn <= cc->migrate_pfn) {
+ if (cc->order > 0 && !cc->wrapped) {
+ /* We started partway through; restart at the end. */
+ unsigned long free_pfn = start_free_pfn(zone);
+ zone->compact_cached_free_pfn = free_pfn;
+ cc->free_pfn = free_pfn;
+ cc->wrapped = 1;
+ return COMPACT_CONTINUE;
+ }
+ return COMPACT_COMPLETE;
+ }
+
+ /* We wrapped around and ended up where we started. */
+ if (cc->wrapped && cc->free_pfn <= cc->start_free_pfn)
return COMPACT_COMPLETE;
/*
@@ -664,8 +768,15 @@ static int compact_zone(struct zone *zone, struct compact_control *cc)
/* Setup to move all movable pages to the end of the zone */
cc->migrate_pfn = zone->zone_start_pfn;
- cc->free_pfn = cc->migrate_pfn + zone->spanned_pages;
- cc->free_pfn &= ~(pageblock_nr_pages-1);
+
+ if (cc->order > 0) {
+ /* Incremental compaction. Start where the last one stopped. */
+ cc->free_pfn = zone->compact_cached_free_pfn;
+ cc->start_free_pfn = cc->free_pfn;
+ } else {
+ /* Order == -1 starts at the end of the zone. */
+ cc->free_pfn = start_free_pfn(zone);
+ }
migrate_prep_local();
@@ -718,7 +829,7 @@ out:
static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask,
- bool sync)
+ bool sync, bool *contended)
{
struct compact_control cc = {
.nr_freepages = 0,
@@ -727,6 +838,7 @@ static unsigned long compact_zone_order(struct zone *zone,
.migratetype = allocflags_to_migratetype(gfp_mask),
.zone = zone,
.sync = sync,
+ .contended = contended,
};
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
@@ -748,7 +860,7 @@ int sysctl_extfrag_threshold = 500;
*/
unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
int may_enter_fs = gfp_mask & __GFP_FS;
@@ -772,7 +884,8 @@ unsigned long try_to_compact_pages(struct zonelist *zonelist,
nodemask) {
int status;
- status = compact_zone_order(zone, order, gfp_mask, sync);
+ status = compact_zone_order(zone, order, gfp_mask, sync,
+ contended);
rc = max(status, rc);
/* If a normal allocation would succeed, stop compacting */
@@ -808,7 +921,7 @@ static int __compact_pgdat(pg_data_t *pgdat, struct compact_control *cc)
if (cc->order > 0) {
int ok = zone_watermark_ok(zone, cc->order,
low_wmark_pages(zone), 0, 0);
- if (ok && cc->order > zone->compact_order_failed)
+ if (ok && cc->order >= zone->compact_order_failed)
zone->compact_order_failed = cc->order + 1;
/* Currently async compaction is never deferred. */
else if (!ok && cc->sync)
diff --git a/mm/fadvise.c b/mm/fadvise.c
index 469491e0af79..9b75a045dbf4 100644
--- a/mm/fadvise.c
+++ b/mm/fadvise.c
@@ -93,11 +93,6 @@ SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
spin_unlock(&file->f_lock);
break;
case POSIX_FADV_WILLNEED:
- if (!mapping->a_ops->readpage) {
- ret = -EINVAL;
- break;
- }
-
/* First and last PARTIAL page! */
start_index = offset >> PAGE_CACHE_SHIFT;
end_index = endbyte >> PAGE_CACHE_SHIFT;
@@ -106,12 +101,13 @@ SYSCALL_DEFINE(fadvise64_64)(int fd, loff_t offset, loff_t len, int advice)
nrpages = end_index - start_index + 1;
if (!nrpages)
nrpages = ~0UL;
-
- ret = force_page_cache_readahead(mapping, file,
- start_index,
- nrpages);
- if (ret > 0)
- ret = 0;
+
+ /*
+ * Ignore return value because fadvise() shall return
+ * success even if filesystem can't retrieve a hint,
+ */
+ force_page_cache_readahead(mapping, file, start_index,
+ nrpages);
break;
case POSIX_FADV_NOREUSE:
break;
diff --git a/mm/filemap.c b/mm/filemap.c
index a4a5260b0279..384344575c37 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1412,12 +1412,8 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
retval = filemap_write_and_wait_range(mapping, pos,
pos + iov_length(iov, nr_segs) - 1);
if (!retval) {
- struct blk_plug plug;
-
- blk_start_plug(&plug);
retval = mapping->a_ops->direct_IO(READ, iocb,
iov, pos, nr_segs);
- blk_finish_plug(&plug);
}
if (retval > 0) {
*ppos = pos + retval;
@@ -1712,8 +1708,35 @@ page_not_uptodate:
}
EXPORT_SYMBOL(filemap_fault);
+int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct page *page = vmf->page;
+ struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
+ int ret = VM_FAULT_LOCKED;
+
+ sb_start_pagefault(inode->i_sb);
+ file_update_time(vma->vm_file);
+ lock_page(page);
+ if (page->mapping != inode->i_mapping) {
+ unlock_page(page);
+ ret = VM_FAULT_NOPAGE;
+ goto out;
+ }
+ /*
+ * We mark the page dirty already here so that when freeze is in
+ * progress, we are guaranteed that writeback during freezing will
+ * see the dirty page and writeprotect it again.
+ */
+ set_page_dirty(page);
+out:
+ sb_end_pagefault(inode->i_sb);
+ return ret;
+}
+EXPORT_SYMBOL(filemap_page_mkwrite);
+
const struct vm_operations_struct generic_file_vm_ops = {
.fault = filemap_fault,
+ .page_mkwrite = filemap_page_mkwrite,
};
/* This is used for a general mmap of a disk file */
@@ -2407,8 +2430,6 @@ ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
count = ocount;
pos = *ppos;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
/* We can write back this queue in page reclaim */
current->backing_dev_info = mapping->backing_dev_info;
written = 0;
@@ -2502,13 +2523,12 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
- struct blk_plug plug;
ssize_t ret;
BUG_ON(iocb->ki_pos != pos);
+ sb_start_write(inode->i_sb);
mutex_lock(&inode->i_mutex);
- blk_start_plug(&plug);
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
@@ -2519,7 +2539,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
if (err < 0 && ret > 0)
ret = err;
}
- blk_finish_plug(&plug);
+ sb_end_write(inode->i_sb);
return ret;
}
EXPORT_SYMBOL(generic_file_aio_write);
diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c
index 213ca1f53409..13e013b1270c 100644
--- a/mm/filemap_xip.c
+++ b/mm/filemap_xip.c
@@ -304,6 +304,7 @@ out:
static const struct vm_operations_struct xip_file_vm_ops = {
.fault = xip_file_fault,
+ .page_mkwrite = filemap_page_mkwrite,
};
int xip_file_mmap(struct file * file, struct vm_area_struct * vma)
@@ -401,6 +402,8 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
loff_t pos;
ssize_t ret;
+ sb_start_write(inode->i_sb);
+
mutex_lock(&inode->i_mutex);
if (!access_ok(VERIFY_READ, buf, len)) {
@@ -411,8 +414,6 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
pos = *ppos;
count = len;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
-
/* We can write back this queue in page reclaim */
current->backing_dev_info = mapping->backing_dev_info;
@@ -436,6 +437,7 @@ xip_file_write(struct file *filp, const char __user *buf, size_t len,
current->backing_dev_info = NULL;
out_up:
mutex_unlock(&inode->i_mutex);
+ sb_end_write(inode->i_sb);
return ret;
}
EXPORT_SYMBOL_GPL(xip_file_write);
diff --git a/mm/highmem.c b/mm/highmem.c
index 57d82c6250c3..d517cd16a6eb 100644
--- a/mm/highmem.c
+++ b/mm/highmem.c
@@ -94,6 +94,18 @@ static DECLARE_WAIT_QUEUE_HEAD(pkmap_map_wait);
do { spin_unlock(&kmap_lock); (void)(flags); } while (0)
#endif
+struct page *kmap_to_page(void *vaddr)
+{
+ unsigned long addr = (unsigned long)vaddr;
+
+ if (addr >= PKMAP_ADDR(0) && addr <= PKMAP_ADDR(LAST_PKMAP)) {
+ int i = (addr - PKMAP_ADDR(0)) >> PAGE_SHIFT;
+ return pte_page(pkmap_page_table[i]);
+ }
+
+ return virt_to_page(addr);
+}
+
static void flush_all_zero_pkmaps(void)
{
int i;
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 57c4b9309015..141dbb695097 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1811,7 +1811,6 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page,
src_page = pte_page(pteval);
copy_user_highpage(page, src_page, address, vma);
VM_BUG_ON(page_mapcount(src_page) != 1);
- VM_BUG_ON(page_count(src_page) != 2);
release_pte_page(src_page);
/*
* ptl mostly unnecessary, but preempt has to
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index e198831276a3..bc727122dd44 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -24,17 +24,20 @@
#include <asm/page.h>
#include <asm/pgtable.h>
-#include <linux/io.h>
+#include <asm/tlb.h>
+#include <linux/io.h>
#include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
#include <linux/node.h>
+#include <linux/hugetlb_cgroup.h>
#include "internal.h"
const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL;
static gfp_t htlb_alloc_mask = GFP_HIGHUSER;
unsigned long hugepages_treat_as_movable;
-static int max_hstate;
+int hugetlb_max_hstate __read_mostly;
unsigned int default_hstate_idx;
struct hstate hstates[HUGE_MAX_HSTATE];
@@ -45,13 +48,10 @@ static struct hstate * __initdata parsed_hstate;
static unsigned long __initdata default_hstate_max_huge_pages;
static unsigned long __initdata default_hstate_size;
-#define for_each_hstate(h) \
- for ((h) = hstates; (h) < &hstates[max_hstate]; (h)++)
-
/*
* Protects updates to hugepage_freelists, nr_huge_pages, and free_huge_pages
*/
-static DEFINE_SPINLOCK(hugetlb_lock);
+DEFINE_SPINLOCK(hugetlb_lock);
static inline void unlock_or_release_subpool(struct hugepage_subpool *spool)
{
@@ -509,7 +509,7 @@ void copy_huge_page(struct page *dst, struct page *src)
static void enqueue_huge_page(struct hstate *h, struct page *page)
{
int nid = page_to_nid(page);
- list_add(&page->lru, &h->hugepage_freelists[nid]);
+ list_move(&page->lru, &h->hugepage_freelists[nid]);
h->free_huge_pages++;
h->free_huge_pages_node[nid]++;
}
@@ -521,7 +521,7 @@ static struct page *dequeue_huge_page_node(struct hstate *h, int nid)
if (list_empty(&h->hugepage_freelists[nid]))
return NULL;
page = list_entry(h->hugepage_freelists[nid].next, struct page, lru);
- list_del(&page->lru);
+ list_move(&page->lru, &h->hugepage_activelist);
set_page_refcounted(page);
h->free_huge_pages--;
h->free_huge_pages_node[nid]--;
@@ -593,6 +593,7 @@ static void update_and_free_page(struct hstate *h, struct page *page)
1 << PG_active | 1 << PG_reserved |
1 << PG_private | 1 << PG_writeback);
}
+ VM_BUG_ON(hugetlb_cgroup_from_page(page));
set_compound_page_dtor(page, NULL);
set_page_refcounted(page);
arch_release_hugepage(page);
@@ -625,10 +626,13 @@ static void free_huge_page(struct page *page)
page->mapping = NULL;
BUG_ON(page_count(page));
BUG_ON(page_mapcount(page));
- INIT_LIST_HEAD(&page->lru);
spin_lock(&hugetlb_lock);
+ hugetlb_cgroup_uncharge_page(hstate_index(h),
+ pages_per_huge_page(h), page);
if (h->surplus_huge_pages_node[nid] && huge_page_order(h) < MAX_ORDER) {
+ /* remove the page from active list */
+ list_del(&page->lru);
update_and_free_page(h, page);
h->surplus_huge_pages--;
h->surplus_huge_pages_node[nid]--;
@@ -641,8 +645,10 @@ static void free_huge_page(struct page *page)
static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
{
+ INIT_LIST_HEAD(&page->lru);
set_compound_page_dtor(page, free_huge_page);
spin_lock(&hugetlb_lock);
+ set_hugetlb_cgroup(page, NULL);
h->nr_huge_pages++;
h->nr_huge_pages_node[nid]++;
spin_unlock(&hugetlb_lock);
@@ -889,8 +895,10 @@ static struct page *alloc_buddy_huge_page(struct hstate *h, int nid)
spin_lock(&hugetlb_lock);
if (page) {
+ INIT_LIST_HEAD(&page->lru);
r_nid = page_to_nid(page);
set_compound_page_dtor(page, free_huge_page);
+ set_hugetlb_cgroup(page, NULL);
/*
* We incremented the global counters already
*/
@@ -993,7 +1001,6 @@ retry:
list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
if ((--needed) < 0)
break;
- list_del(&page->lru);
/*
* This page is now managed by the hugetlb allocator and has
* no users -- drop the buddy allocator's reference.
@@ -1008,7 +1015,6 @@ free:
/* Free unnecessary surplus pages to the buddy allocator */
if (!list_empty(&surplus_list)) {
list_for_each_entry_safe(page, tmp, &surplus_list, lru) {
- list_del(&page->lru);
put_page(page);
}
}
@@ -1112,7 +1118,10 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
struct hstate *h = hstate_vma(vma);
struct page *page;
long chg;
+ int ret, idx;
+ struct hugetlb_cgroup *h_cg;
+ idx = hstate_index(h);
/*
* Processes that did not create the mapping will have no
* reserves and will not have accounted against subpool
@@ -1123,27 +1132,43 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
*/
chg = vma_needs_reservation(h, vma, addr);
if (chg < 0)
- return ERR_PTR(-VM_FAULT_OOM);
+ return ERR_PTR(-ENOMEM);
if (chg)
if (hugepage_subpool_get_pages(spool, chg))
- return ERR_PTR(-VM_FAULT_SIGBUS);
+ return ERR_PTR(-ENOSPC);
+ ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg);
+ if (ret) {
+ hugepage_subpool_put_pages(spool, chg);
+ return ERR_PTR(-ENOSPC);
+ }
spin_lock(&hugetlb_lock);
page = dequeue_huge_page_vma(h, vma, addr, avoid_reserve);
- spin_unlock(&hugetlb_lock);
-
- if (!page) {
+ if (page) {
+ /* update page cgroup details */
+ hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h),
+ h_cg, page);
+ spin_unlock(&hugetlb_lock);
+ } else {
+ spin_unlock(&hugetlb_lock);
page = alloc_buddy_huge_page(h, NUMA_NO_NODE);
if (!page) {
+ hugetlb_cgroup_uncharge_cgroup(idx,
+ pages_per_huge_page(h),
+ h_cg);
hugepage_subpool_put_pages(spool, chg);
- return ERR_PTR(-VM_FAULT_SIGBUS);
+ return ERR_PTR(-ENOSPC);
}
+ spin_lock(&hugetlb_lock);
+ hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h),
+ h_cg, page);
+ list_move(&page->lru, &h->hugepage_activelist);
+ spin_unlock(&hugetlb_lock);
}
set_page_private(page, (unsigned long)spool);
vma_commit_reservation(h, vma, addr);
-
return page;
}
@@ -1646,7 +1671,7 @@ static int hugetlb_sysfs_add_hstate(struct hstate *h, struct kobject *parent,
struct attribute_group *hstate_attr_group)
{
int retval;
- int hi = h - hstates;
+ int hi = hstate_index(h);
hstate_kobjs[hi] = kobject_create_and_add(h->name, parent);
if (!hstate_kobjs[hi])
@@ -1741,11 +1766,13 @@ void hugetlb_unregister_node(struct node *node)
if (!nhs->hugepages_kobj)
return; /* no hstate attributes */
- for_each_hstate(h)
- if (nhs->hstate_kobjs[h - hstates]) {
- kobject_put(nhs->hstate_kobjs[h - hstates]);
- nhs->hstate_kobjs[h - hstates] = NULL;
+ for_each_hstate(h) {
+ int idx = hstate_index(h);
+ if (nhs->hstate_kobjs[idx]) {
+ kobject_put(nhs->hstate_kobjs[idx]);
+ nhs->hstate_kobjs[idx] = NULL;
}
+ }
kobject_put(nhs->hugepages_kobj);
nhs->hugepages_kobj = NULL;
@@ -1848,7 +1875,7 @@ static void __exit hugetlb_exit(void)
hugetlb_unregister_all_nodes();
for_each_hstate(h) {
- kobject_put(hstate_kobjs[h - hstates]);
+ kobject_put(hstate_kobjs[hstate_index(h)]);
}
kobject_put(hugepages_kobj);
@@ -1869,7 +1896,7 @@ static int __init hugetlb_init(void)
if (!size_to_hstate(default_hstate_size))
hugetlb_add_hstate(HUGETLB_PAGE_ORDER);
}
- default_hstate_idx = size_to_hstate(default_hstate_size) - hstates;
+ default_hstate_idx = hstate_index(size_to_hstate(default_hstate_size));
if (default_hstate_max_huge_pages)
default_hstate.max_huge_pages = default_hstate_max_huge_pages;
@@ -1897,19 +1924,27 @@ void __init hugetlb_add_hstate(unsigned order)
printk(KERN_WARNING "hugepagesz= specified twice, ignoring\n");
return;
}
- BUG_ON(max_hstate >= HUGE_MAX_HSTATE);
+ BUG_ON(hugetlb_max_hstate >= HUGE_MAX_HSTATE);
BUG_ON(order == 0);
- h = &hstates[max_hstate++];
+ h = &hstates[hugetlb_max_hstate++];
h->order = order;
h->mask = ~((1ULL << (order + PAGE_SHIFT)) - 1);
h->nr_huge_pages = 0;
h->free_huge_pages = 0;
for (i = 0; i < MAX_NUMNODES; ++i)
INIT_LIST_HEAD(&h->hugepage_freelists[i]);
+ INIT_LIST_HEAD(&h->hugepage_activelist);
h->next_nid_to_alloc = first_node(node_states[N_HIGH_MEMORY]);
h->next_nid_to_free = first_node(node_states[N_HIGH_MEMORY]);
snprintf(h->name, HSTATE_NAME_LEN, "hugepages-%lukB",
huge_page_size(h)/1024);
+ /*
+ * Add cgroup control files only if the huge page consists
+ * of more than two normal pages. This is because we use
+ * page[2].lru.next for storing cgoup details.
+ */
+ if (order >= HUGETLB_CGROUP_MIN_ORDER)
+ hugetlb_cgroup_file_init(hugetlb_max_hstate - 1);
parsed_hstate = h;
}
@@ -1920,10 +1955,10 @@ static int __init hugetlb_nrpages_setup(char *s)
static unsigned long *last_mhp;
/*
- * !max_hstate means we haven't parsed a hugepagesz= parameter yet,
+ * !hugetlb_max_hstate means we haven't parsed a hugepagesz= parameter yet,
* so this hugepages= parameter goes to the "default hstate".
*/
- if (!max_hstate)
+ if (!hugetlb_max_hstate)
mhp = &default_hstate_max_huge_pages;
else
mhp = &parsed_hstate->max_huge_pages;
@@ -1942,7 +1977,7 @@ static int __init hugetlb_nrpages_setup(char *s)
* But we need to allocate >= MAX_ORDER hstates here early to still
* use the bootmem allocator.
*/
- if (max_hstate && parsed_hstate->order >= MAX_ORDER)
+ if (hugetlb_max_hstate && parsed_hstate->order >= MAX_ORDER)
hugetlb_hstate_alloc_pages(parsed_hstate);
last_mhp = mhp;
@@ -2308,30 +2343,26 @@ static int is_hugetlb_entry_hwpoisoned(pte_t pte)
return 0;
}
-void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
- unsigned long end, struct page *ref_page)
+void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,
+ unsigned long start, unsigned long end,
+ struct page *ref_page)
{
+ int force_flush = 0;
struct mm_struct *mm = vma->vm_mm;
unsigned long address;
pte_t *ptep;
pte_t pte;
struct page *page;
- struct page *tmp;
struct hstate *h = hstate_vma(vma);
unsigned long sz = huge_page_size(h);
- /*
- * A page gathering list, protected by per file i_mmap_mutex. The
- * lock is used to avoid list corruption from multiple unmapping
- * of the same page since we are using page->lru.
- */
- LIST_HEAD(page_list);
-
WARN_ON(!is_vm_hugetlb_page(vma));
BUG_ON(start & ~huge_page_mask(h));
BUG_ON(end & ~huge_page_mask(h));
+ tlb_start_vma(tlb, vma);
mmu_notifier_invalidate_range_start(mm, start, end);
+again:
spin_lock(&mm->page_table_lock);
for (address = start; address < end; address += sz) {
ptep = huge_pte_offset(mm, address);
@@ -2370,30 +2401,64 @@ void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
}
pte = huge_ptep_get_and_clear(mm, address, ptep);
+ tlb_remove_tlb_entry(tlb, ptep, address);
if (pte_dirty(pte))
set_page_dirty(page);
- list_add(&page->lru, &page_list);
+ page_remove_rmap(page);
+ force_flush = !__tlb_remove_page(tlb, page);
+ if (force_flush)
+ break;
/* Bail out after unmapping reference page if supplied */
if (ref_page)
break;
}
- flush_tlb_range(vma, start, end);
spin_unlock(&mm->page_table_lock);
- mmu_notifier_invalidate_range_end(mm, start, end);
- list_for_each_entry_safe(page, tmp, &page_list, lru) {
- page_remove_rmap(page);
- list_del(&page->lru);
- put_page(page);
+ /*
+ * mmu_gather ran out of room to batch pages, we break out of
+ * the PTE lock to avoid doing the potential expensive TLB invalidate
+ * and page-free while holding it.
+ */
+ if (force_flush) {
+ force_flush = 0;
+ tlb_flush_mmu(tlb);
+ if (address < end && !ref_page)
+ goto again;
}
+ mmu_notifier_invalidate_range_end(mm, start, end);
+ tlb_end_vma(tlb, vma);
+}
+
+void __unmap_hugepage_range_final(struct mmu_gather *tlb,
+ struct vm_area_struct *vma, unsigned long start,
+ unsigned long end, struct page *ref_page)
+{
+ __unmap_hugepage_range(tlb, vma, start, end, ref_page);
+
+ /*
+ * Clear this flag so that x86's huge_pmd_share page_table_shareable
+ * test will fail on a vma being torn down, and not grab a page table
+ * on its way out. We're lucky that the flag has such an appropriate
+ * name, and can in fact be safely cleared here. We could clear it
+ * before the __unmap_hugepage_range above, but all that's necessary
+ * is to clear it before releasing the i_mmap_mutex. This works
+ * because in the context this is called, the VMA is about to be
+ * destroyed and the i_mmap_mutex is held.
+ */
+ vma->vm_flags &= ~VM_MAYSHARE;
}
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end, struct page *ref_page)
{
- mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex);
- __unmap_hugepage_range(vma, start, end, ref_page);
- mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
+ struct mm_struct *mm;
+ struct mmu_gather tlb;
+
+ mm = vma->vm_mm;
+
+ tlb_gather_mmu(&tlb, mm, 0);
+ __unmap_hugepage_range(&tlb, vma, start, end, ref_page);
+ tlb_finish_mmu(&tlb, start, end);
}
/*
@@ -2438,9 +2503,8 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma,
* from the time of fork. This would look like data corruption
*/
if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER))
- __unmap_hugepage_range(iter_vma,
- address, address + huge_page_size(h),
- page);
+ unmap_hugepage_range(iter_vma, address,
+ address + huge_page_size(h), page);
}
mutex_unlock(&mapping->i_mmap_mutex);
@@ -2496,6 +2560,7 @@ retry_avoidcopy:
new_page = alloc_huge_page(vma, address, outside_reserve);
if (IS_ERR(new_page)) {
+ long err = PTR_ERR(new_page);
page_cache_release(old_page);
/*
@@ -2524,7 +2589,10 @@ retry_avoidcopy:
/* Caller expects lock to be held */
spin_lock(&mm->page_table_lock);
- return -PTR_ERR(new_page);
+ if (err == -ENOMEM)
+ return VM_FAULT_OOM;
+ else
+ return VM_FAULT_SIGBUS;
}
/*
@@ -2642,7 +2710,11 @@ retry:
goto out;
page = alloc_huge_page(vma, address, 0);
if (IS_ERR(page)) {
- ret = -PTR_ERR(page);
+ ret = PTR_ERR(page);
+ if (ret == -ENOMEM)
+ ret = VM_FAULT_OOM;
+ else
+ ret = VM_FAULT_SIGBUS;
goto out;
}
clear_huge_page(page, address, pages_per_huge_page(h));
@@ -2679,7 +2751,7 @@ retry:
*/
if (unlikely(PageHWPoison(page))) {
ret = VM_FAULT_HWPOISON |
- VM_FAULT_SET_HINDEX(h - hstates);
+ VM_FAULT_SET_HINDEX(hstate_index(h));
goto backout_unlocked;
}
}
@@ -2752,7 +2824,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
return 0;
} else if (unlikely(is_hugetlb_entry_hwpoisoned(entry)))
return VM_FAULT_HWPOISON_LARGE |
- VM_FAULT_SET_HINDEX(h - hstates);
+ VM_FAULT_SET_HINDEX(hstate_index(h));
}
ptep = huge_pte_alloc(mm, address, huge_page_size(h));
@@ -2959,9 +3031,14 @@ void hugetlb_change_protection(struct vm_area_struct *vma,
}
}
spin_unlock(&mm->page_table_lock);
- mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
-
+ /*
+ * Must flush TLB before releasing i_mmap_mutex: x86's huge_pmd_unshare
+ * may have cleared our pud entry and done put_page on the page table:
+ * once we release i_mmap_mutex, another task can do the final put_page
+ * and that page table be reused and filled with junk.
+ */
flush_tlb_range(vma, start, end);
+ mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
}
int hugetlb_reserve_pages(struct inode *inode,
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
new file mode 100644
index 000000000000..a3f358fb8a0c
--- /dev/null
+++ b/mm/hugetlb_cgroup.c
@@ -0,0 +1,418 @@
+/*
+ *
+ * Copyright IBM Corporation, 2012
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/cgroup.h>
+#include <linux/slab.h>
+#include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
+
+struct hugetlb_cgroup {
+ struct cgroup_subsys_state css;
+ /*
+ * the counter to account for hugepages from hugetlb.
+ */
+ struct res_counter hugepage[HUGE_MAX_HSTATE];
+};
+
+#define MEMFILE_PRIVATE(x, val) (((x) << 16) | (val))
+#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff)
+#define MEMFILE_ATTR(val) ((val) & 0xffff)
+
+struct cgroup_subsys hugetlb_subsys __read_mostly;
+static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
+{
+ return container_of(s, struct hugetlb_cgroup, css);
+}
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_cgroup(struct cgroup *cgroup)
+{
+ return hugetlb_cgroup_from_css(cgroup_subsys_state(cgroup,
+ hugetlb_subsys_id));
+}
+
+static inline
+struct hugetlb_cgroup *hugetlb_cgroup_from_task(struct task_struct *task)
+{
+ return hugetlb_cgroup_from_css(task_subsys_state(task,
+ hugetlb_subsys_id));
+}
+
+static inline bool hugetlb_cgroup_is_root(struct hugetlb_cgroup *h_cg)
+{
+ return (h_cg == root_h_cgroup);
+}
+
+static inline struct hugetlb_cgroup *parent_hugetlb_cgroup(struct cgroup *cg)
+{
+ if (!cg->parent)
+ return NULL;
+ return hugetlb_cgroup_from_cgroup(cg->parent);
+}
+
+static inline bool hugetlb_cgroup_have_usage(struct cgroup *cg)
+{
+ int idx;
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cg);
+
+ for (idx = 0; idx < hugetlb_max_hstate; idx++) {
+ if ((res_counter_read_u64(&h_cg->hugepage[idx], RES_USAGE)) > 0)
+ return true;
+ }
+ return false;
+}
+
+static struct cgroup_subsys_state *hugetlb_cgroup_create(struct cgroup *cgroup)
+{
+ int idx;
+ struct cgroup *parent_cgroup;
+ struct hugetlb_cgroup *h_cgroup, *parent_h_cgroup;
+
+ h_cgroup = kzalloc(sizeof(*h_cgroup), GFP_KERNEL);
+ if (!h_cgroup)
+ return ERR_PTR(-ENOMEM);
+
+ parent_cgroup = cgroup->parent;
+ if (parent_cgroup) {
+ parent_h_cgroup = hugetlb_cgroup_from_cgroup(parent_cgroup);
+ for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
+ res_counter_init(&h_cgroup->hugepage[idx],
+ &parent_h_cgroup->hugepage[idx]);
+ } else {
+ root_h_cgroup = h_cgroup;
+ for (idx = 0; idx < HUGE_MAX_HSTATE; idx++)
+ res_counter_init(&h_cgroup->hugepage[idx], NULL);
+ }
+ return &h_cgroup->css;
+}
+
+static void hugetlb_cgroup_destroy(struct cgroup *cgroup)
+{
+ struct hugetlb_cgroup *h_cgroup;
+
+ h_cgroup = hugetlb_cgroup_from_cgroup(cgroup);
+ kfree(h_cgroup);
+}
+
+
+/*
+ * Should be called with hugetlb_lock held.
+ * Since we are holding hugetlb_lock, pages cannot get moved from
+ * active list or uncharged from the cgroup, So no need to get
+ * page reference and test for page active here. This function
+ * cannot fail.
+ */
+static void hugetlb_cgroup_move_parent(int idx, struct cgroup *cgroup,
+ struct page *page)
+{
+ int csize;
+ struct res_counter *counter;
+ struct res_counter *fail_res;
+ struct hugetlb_cgroup *page_hcg;
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+ struct hugetlb_cgroup *parent = parent_hugetlb_cgroup(cgroup);
+
+ page_hcg = hugetlb_cgroup_from_page(page);
+ /*
+ * We can have pages in active list without any cgroup
+ * ie, hugepage with less than 3 pages. We can safely
+ * ignore those pages.
+ */
+ if (!page_hcg || page_hcg != h_cg)
+ goto out;
+
+ csize = PAGE_SIZE << compound_order(page);
+ if (!parent) {
+ parent = root_h_cgroup;
+ /* root has no limit */
+ res_counter_charge_nofail(&parent->hugepage[idx],
+ csize, &fail_res);
+ }
+ counter = &h_cg->hugepage[idx];
+ res_counter_uncharge_until(counter, counter->parent, csize);
+
+ set_hugetlb_cgroup(page, parent);
+out:
+ return;
+}
+
+/*
+ * Force the hugetlb cgroup to empty the hugetlb resources by moving them to
+ * the parent cgroup.
+ */
+static int hugetlb_cgroup_pre_destroy(struct cgroup *cgroup)
+{
+ struct hstate *h;
+ struct page *page;
+ int ret = 0, idx = 0;
+
+ do {
+ if (cgroup_task_count(cgroup) ||
+ !list_empty(&cgroup->children)) {
+ ret = -EBUSY;
+ goto out;
+ }
+ for_each_hstate(h) {
+ spin_lock(&hugetlb_lock);
+ list_for_each_entry(page, &h->hugepage_activelist, lru)
+ hugetlb_cgroup_move_parent(idx, cgroup, page);
+
+ spin_unlock(&hugetlb_lock);
+ idx++;
+ }
+ cond_resched();
+ } while (hugetlb_cgroup_have_usage(cgroup));
+out:
+ return ret;
+}
+
+int hugetlb_cgroup_charge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup **ptr)
+{
+ int ret = 0;
+ struct res_counter *fail_res;
+ struct hugetlb_cgroup *h_cg = NULL;
+ unsigned long csize = nr_pages * PAGE_SIZE;
+
+ if (hugetlb_cgroup_disabled())
+ goto done;
+ /*
+ * We don't charge any cgroup if the compound page have less
+ * than 3 pages.
+ */
+ if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
+ goto done;
+again:
+ rcu_read_lock();
+ h_cg = hugetlb_cgroup_from_task(current);
+ if (!css_tryget(&h_cg->css)) {
+ rcu_read_unlock();
+ goto again;
+ }
+ rcu_read_unlock();
+
+ ret = res_counter_charge(&h_cg->hugepage[idx], csize, &fail_res);
+ css_put(&h_cg->css);
+done:
+ *ptr = h_cg;
+ return ret;
+}
+
+/* Should be called with hugetlb_lock held */
+void hugetlb_cgroup_commit_charge(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg,
+ struct page *page)
+{
+ if (hugetlb_cgroup_disabled() || !h_cg)
+ return;
+
+ set_hugetlb_cgroup(page, h_cg);
+ return;
+}
+
+/*
+ * Should be called with hugetlb_lock held
+ */
+void hugetlb_cgroup_uncharge_page(int idx, unsigned long nr_pages,
+ struct page *page)
+{
+ struct hugetlb_cgroup *h_cg;
+ unsigned long csize = nr_pages * PAGE_SIZE;
+
+ if (hugetlb_cgroup_disabled())
+ return;
+ VM_BUG_ON(!spin_is_locked(&hugetlb_lock));
+ h_cg = hugetlb_cgroup_from_page(page);
+ if (unlikely(!h_cg))
+ return;
+ set_hugetlb_cgroup(page, NULL);
+ res_counter_uncharge(&h_cg->hugepage[idx], csize);
+ return;
+}
+
+void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
+ struct hugetlb_cgroup *h_cg)
+{
+ unsigned long csize = nr_pages * PAGE_SIZE;
+
+ if (hugetlb_cgroup_disabled() || !h_cg)
+ return;
+
+ if (huge_page_order(&hstates[idx]) < HUGETLB_CGROUP_MIN_ORDER)
+ return;
+
+ res_counter_uncharge(&h_cg->hugepage[idx], csize);
+ return;
+}
+
+static ssize_t hugetlb_cgroup_read(struct cgroup *cgroup, struct cftype *cft,
+ struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ u64 val;
+ char str[64];
+ int idx, name, len;
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+ idx = MEMFILE_IDX(cft->private);
+ name = MEMFILE_ATTR(cft->private);
+
+ val = res_counter_read_u64(&h_cg->hugepage[idx], name);
+ len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val);
+ return simple_read_from_buffer(buf, nbytes, ppos, str, len);
+}
+
+static int hugetlb_cgroup_write(struct cgroup *cgroup, struct cftype *cft,
+ const char *buffer)
+{
+ int idx, name, ret;
+ unsigned long long val;
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+ idx = MEMFILE_IDX(cft->private);
+ name = MEMFILE_ATTR(cft->private);
+
+ switch (name) {
+ case RES_LIMIT:
+ if (hugetlb_cgroup_is_root(h_cg)) {
+ /* Can't set limit on root */
+ ret = -EINVAL;
+ break;
+ }
+ /* This function does all necessary parse...reuse it */
+ ret = res_counter_memparse_write_strategy(buffer, &val);
+ if (ret)
+ break;
+ ret = res_counter_set_limit(&h_cg->hugepage[idx], val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int hugetlb_cgroup_reset(struct cgroup *cgroup, unsigned int event)
+{
+ int idx, name, ret = 0;
+ struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_cgroup(cgroup);
+
+ idx = MEMFILE_IDX(event);
+ name = MEMFILE_ATTR(event);
+
+ switch (name) {
+ case RES_MAX_USAGE:
+ res_counter_reset_max(&h_cg->hugepage[idx]);
+ break;
+ case RES_FAILCNT:
+ res_counter_reset_failcnt(&h_cg->hugepage[idx]);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static char *mem_fmt(char *buf, int size, unsigned long hsize)
+{
+ if (hsize >= (1UL << 30))
+ snprintf(buf, size, "%luGB", hsize >> 30);
+ else if (hsize >= (1UL << 20))
+ snprintf(buf, size, "%luMB", hsize >> 20);
+ else
+ snprintf(buf, size, "%luKB", hsize >> 10);
+ return buf;
+}
+
+int __init hugetlb_cgroup_file_init(int idx)
+{
+ char buf[32];
+ struct cftype *cft;
+ struct hstate *h = &hstates[idx];
+
+ /* format the size */
+ mem_fmt(buf, 32, huge_page_size(h));
+
+ /* Add the limit file */
+ cft = &h->cgroup_files[0];
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.limit_in_bytes", buf);
+ cft->private = MEMFILE_PRIVATE(idx, RES_LIMIT);
+ cft->read = hugetlb_cgroup_read;
+ cft->write_string = hugetlb_cgroup_write;
+
+ /* Add the usage file */
+ cft = &h->cgroup_files[1];
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf);
+ cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);
+ cft->read = hugetlb_cgroup_read;
+
+ /* Add the MAX usage file */
+ cft = &h->cgroup_files[2];
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf);
+ cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE);
+ cft->trigger = hugetlb_cgroup_reset;
+ cft->read = hugetlb_cgroup_read;
+
+ /* Add the failcntfile */
+ cft = &h->cgroup_files[3];
+ snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);
+ cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT);
+ cft->trigger = hugetlb_cgroup_reset;
+ cft->read = hugetlb_cgroup_read;
+
+ /* NULL terminate the last cft */
+ cft = &h->cgroup_files[4];
+ memset(cft, 0, sizeof(*cft));
+
+ WARN_ON(cgroup_add_cftypes(&hugetlb_subsys, h->cgroup_files));
+
+ return 0;
+}
+
+/*
+ * hugetlb_lock will make sure a parallel cgroup rmdir won't happen
+ * when we migrate hugepages
+ */
+void hugetlb_cgroup_migrate(struct page *oldhpage, struct page *newhpage)
+{
+ struct hugetlb_cgroup *h_cg;
+ struct hstate *h = page_hstate(oldhpage);
+
+ if (hugetlb_cgroup_disabled())
+ return;
+
+ VM_BUG_ON(!PageHuge(oldhpage));
+ spin_lock(&hugetlb_lock);
+ h_cg = hugetlb_cgroup_from_page(oldhpage);
+ set_hugetlb_cgroup(oldhpage, NULL);
+
+ /* move the h_cg details to new cgroup */
+ set_hugetlb_cgroup(newhpage, h_cg);
+ list_move(&newhpage->lru, &h->hugepage_activelist);
+ spin_unlock(&hugetlb_lock);
+ return;
+}
+
+struct cgroup_subsys hugetlb_subsys = {
+ .name = "hugetlb",
+ .create = hugetlb_cgroup_create,
+ .pre_destroy = hugetlb_cgroup_pre_destroy,
+ .destroy = hugetlb_cgroup_destroy,
+ .subsys_id = hugetlb_subsys_id,
+};
diff --git a/mm/hwpoison-inject.c b/mm/hwpoison-inject.c
index cc448bb983ba..3a61efc518d5 100644
--- a/mm/hwpoison-inject.c
+++ b/mm/hwpoison-inject.c
@@ -123,7 +123,7 @@ static int pfn_inject_init(void)
if (!dentry)
goto fail;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
dentry = debugfs_create_u64("corrupt-filter-memcg", 0600,
hwpoison_dir, &hwpoison_filter_memcg);
if (!dentry)
diff --git a/mm/internal.h b/mm/internal.h
index 2ba87fbfb75b..b8c91b342e24 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -118,12 +118,19 @@ struct compact_control {
unsigned long nr_freepages; /* Number of isolated free pages */
unsigned long nr_migratepages; /* Number of pages to migrate */
unsigned long free_pfn; /* isolate_freepages search base */
+ unsigned long start_free_pfn; /* where we started the search */
unsigned long migrate_pfn; /* isolate_migratepages search base */
bool sync; /* Synchronous migration */
+ bool wrapped; /* Order > 0 compactions are
+ incremental, once free_pfn
+ and migrate_pfn meet, we restart
+ from the top of the zone;
+ remember we wrapped around. */
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
+ bool *contended; /* True if a lock was contended */
};
unsigned long
@@ -347,3 +354,5 @@ extern u32 hwpoison_filter_enable;
extern unsigned long vm_mmap_pgoff(struct file *, unsigned long,
unsigned long, unsigned long,
unsigned long, unsigned long);
+
+extern void set_pageblock_order(void);
diff --git a/mm/memblock.c b/mm/memblock.c
index 5cc6731b00cc..82aa349d2f7a 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -222,13 +222,13 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
/* Try to find some space for it.
*
* WARNING: We assume that either slab_is_available() and we use it or
- * we use MEMBLOCK for allocations. That means that this is unsafe to use
- * when bootmem is currently active (unless bootmem itself is implemented
- * on top of MEMBLOCK which isn't the case yet)
+ * we use MEMBLOCK for allocations. That means that this is unsafe to
+ * use when bootmem is currently active (unless bootmem itself is
+ * implemented on top of MEMBLOCK which isn't the case yet)
*
* This should however not be an issue for now, as we currently only
- * call into MEMBLOCK while it's still active, or much later when slab is
- * active for memory hotplug operations
+ * call into MEMBLOCK while it's still active, or much later when slab
+ * is active for memory hotplug operations
*/
if (use_slab) {
new_array = kmalloc(new_size, GFP_KERNEL);
@@ -243,10 +243,10 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
new_alloc_size, PAGE_SIZE);
if (!addr && new_area_size)
addr = memblock_find_in_range(0,
- min(new_area_start, memblock.current_limit),
- new_alloc_size, PAGE_SIZE);
+ min(new_area_start, memblock.current_limit),
+ new_alloc_size, PAGE_SIZE);
- new_array = addr ? __va(addr) : 0;
+ new_array = addr ? __va(addr) : NULL;
}
if (!addr) {
pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",
@@ -254,12 +254,14 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
return -1;
}
- memblock_dbg("memblock: %s array is doubled to %ld at [%#010llx-%#010llx]",
- memblock_type_name(type), type->max * 2, (u64)addr, (u64)addr + new_size - 1);
+ memblock_dbg("memblock: %s is doubled to %ld at [%#010llx-%#010llx]",
+ memblock_type_name(type), type->max * 2, (u64)addr,
+ (u64)addr + new_size - 1);
- /* Found space, we now need to move the array over before
- * we add the reserved region since it may be our reserved
- * array itself that is full.
+ /*
+ * Found space, we now need to move the array over before we add the
+ * reserved region since it may be our reserved array itself that is
+ * full.
*/
memcpy(new_array, type->regions, old_size);
memset(new_array + type->max, 0, old_size);
@@ -267,17 +269,16 @@ static int __init_memblock memblock_double_array(struct memblock_type *type,
type->regions = new_array;
type->max <<= 1;
- /* Free old array. We needn't free it if the array is the
- * static one
- */
+ /* Free old array. We needn't free it if the array is the static one */
if (*in_slab)
kfree(old_array);
else if (old_array != memblock_memory_init_regions &&
old_array != memblock_reserved_init_regions)
memblock_free(__pa(old_array), old_alloc_size);
- /* Reserve the new array if that comes from the memblock.
- * Otherwise, we needn't do it
+ /*
+ * Reserve the new array if that comes from the memblock. Otherwise, we
+ * needn't do it
*/
if (!use_slab)
BUG_ON(memblock_reserve(addr, new_alloc_size));
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index f72b5e52451a..795e525afaba 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -61,12 +61,12 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly;
#define MEM_CGROUP_RECLAIM_RETRIES 5
static struct mem_cgroup *root_mem_cgroup __read_mostly;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
/* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */
int do_swap_account __read_mostly;
/* for remember boot option*/
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP_ENABLED
+#ifdef CONFIG_MEMCG_SWAP_ENABLED
static int really_do_swap_account __initdata = 1;
#else
static int really_do_swap_account __initdata = 0;
@@ -87,7 +87,7 @@ enum mem_cgroup_stat_index {
MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */
MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */
MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */
- MEM_CGROUP_STAT_SWAPOUT, /* # of pages, swapped out */
+ MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */
MEM_CGROUP_STAT_NSTATS,
};
@@ -378,9 +378,7 @@ static bool move_file(void)
enum charge_type {
MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
- MEM_CGROUP_CHARGE_TYPE_MAPPED,
- MEM_CGROUP_CHARGE_TYPE_SHMEM, /* used by page migration of shmem */
- MEM_CGROUP_CHARGE_TYPE_FORCE, /* used by force_empty */
+ MEM_CGROUP_CHARGE_TYPE_ANON,
MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
MEM_CGROUP_CHARGE_TYPE_DROP, /* a page was unused swap cache */
NR_CHARGE_TYPE,
@@ -407,8 +405,14 @@ enum charge_type {
static void mem_cgroup_get(struct mem_cgroup *memcg);
static void mem_cgroup_put(struct mem_cgroup *memcg);
+static inline
+struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *s)
+{
+ return container_of(s, struct mem_cgroup, css);
+}
+
/* Writing them here to avoid exposing memcg's inner layout */
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
#include <net/sock.h>
#include <net/ip.h>
@@ -467,9 +471,9 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
}
EXPORT_SYMBOL(tcp_proto_cgroup);
#endif /* CONFIG_INET */
-#endif /* CONFIG_CGROUP_MEM_RES_CTLR_KMEM */
+#endif /* CONFIG_MEMCG_KMEM */
-#if defined(CONFIG_INET) && defined(CONFIG_CGROUP_MEM_RES_CTLR_KMEM)
+#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM)
static void disarm_sock_keys(struct mem_cgroup *memcg)
{
if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto))
@@ -703,7 +707,7 @@ static void mem_cgroup_swap_statistics(struct mem_cgroup *memcg,
bool charge)
{
int val = (charge) ? 1 : -1;
- this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_SWAPOUT], val);
+ this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_SWAP], val);
}
static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
@@ -864,9 +868,8 @@ static void memcg_check_events(struct mem_cgroup *memcg, struct page *page)
struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont)
{
- return container_of(cgroup_subsys_state(cont,
- mem_cgroup_subsys_id), struct mem_cgroup,
- css);
+ return mem_cgroup_from_css(
+ cgroup_subsys_state(cont, mem_cgroup_subsys_id));
}
struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
@@ -879,8 +882,7 @@ struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
if (unlikely(!p))
return NULL;
- return container_of(task_subsys_state(p, mem_cgroup_subsys_id),
- struct mem_cgroup, css);
+ return mem_cgroup_from_css(task_subsys_state(p, mem_cgroup_subsys_id));
}
struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm)
@@ -966,8 +968,7 @@ struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
css = css_get_next(&mem_cgroup_subsys, id + 1, &root->css, &id);
if (css) {
if (css == &root->css || css_tryget(css))
- memcg = container_of(css,
- struct mem_cgroup, css);
+ memcg = mem_cgroup_from_css(css);
} else
id = 0;
rcu_read_unlock();
@@ -1454,7 +1455,7 @@ static int mem_cgroup_count_children(struct mem_cgroup *memcg)
/*
* Return the memory (and swap, if configured) limit for a memcg.
*/
-u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
+static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
{
u64 limit;
u64 memsw;
@@ -1470,6 +1471,73 @@ u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
return min(limit, memsw);
}
+void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+ int order)
+{
+ struct mem_cgroup *iter;
+ unsigned long chosen_points = 0;
+ unsigned long totalpages;
+ unsigned int points = 0;
+ struct task_struct *chosen = NULL;
+
+ /*
+ * If current has a pending SIGKILL, then automatically select it. The
+ * goal is to allow it to allocate so that it may quickly exit and free
+ * its memory.
+ */
+ if (fatal_signal_pending(current)) {
+ set_thread_flag(TIF_MEMDIE);
+ return;
+ }
+
+ check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
+ totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
+ for_each_mem_cgroup_tree(iter, memcg) {
+ struct cgroup *cgroup = iter->css.cgroup;
+ struct cgroup_iter it;
+ struct task_struct *task;
+
+ cgroup_iter_start(cgroup, &it);
+ while ((task = cgroup_iter_next(cgroup, &it))) {
+ switch (oom_scan_process_thread(task, totalpages, NULL,
+ false)) {
+ case OOM_SCAN_SELECT:
+ if (chosen)
+ put_task_struct(chosen);
+ chosen = task;
+ chosen_points = ULONG_MAX;
+ get_task_struct(chosen);
+ /* fall through */
+ case OOM_SCAN_CONTINUE:
+ continue;
+ case OOM_SCAN_ABORT:
+ cgroup_iter_end(cgroup, &it);
+ mem_cgroup_iter_break(memcg, iter);
+ if (chosen)
+ put_task_struct(chosen);
+ return;
+ case OOM_SCAN_OK:
+ break;
+ };
+ points = oom_badness(task, memcg, NULL, totalpages);
+ if (points > chosen_points) {
+ if (chosen)
+ put_task_struct(chosen);
+ chosen = task;
+ chosen_points = points;
+ get_task_struct(chosen);
+ }
+ }
+ cgroup_iter_end(cgroup, &it);
+ }
+
+ if (!chosen)
+ return;
+ points = chosen_points * 1000 / totalpages;
+ oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg,
+ NULL, "Memory cgroup out of memory");
+}
+
static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg,
gfp_t gfp_mask,
unsigned long flags)
@@ -1899,7 +1967,7 @@ again:
return;
/*
* If this memory cgroup is not under account moving, we don't
- * need to take move_lock_page_cgroup(). Because we already hold
+ * need to take move_lock_mem_cgroup(). Because we already hold
* rcu_read_lock(), any calls to move_account will be delayed until
* rcu_read_unlock() if mem_cgroup_stolen() == true.
*/
@@ -1921,7 +1989,7 @@ void __mem_cgroup_end_update_page_stat(struct page *page, unsigned long *flags)
/*
* It's guaranteed that pc->mem_cgroup never changes while
* lock is held because a routine modifies pc->mem_cgroup
- * should take move_lock_page_cgroup().
+ * should take move_lock_mem_cgroup().
*/
move_unlock_mem_cgroup(pc->mem_cgroup, flags);
}
@@ -2268,7 +2336,7 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
* We always charge the cgroup the mm_struct belongs to.
* The mm_struct's mem_cgroup changes on task migration if the
* thread group leader migrates. It's possible that mm is not
- * set, if so charge the init_mm (happens for pagecache usage).
+ * set, if so charge the root memcg (happens for pagecache usage).
*/
if (!*ptr && !mm)
*ptr = root_mem_cgroup;
@@ -2429,7 +2497,7 @@ static struct mem_cgroup *mem_cgroup_lookup(unsigned short id)
css = css_lookup(&mem_cgroup_subsys, id);
if (!css)
return NULL;
- return container_of(css, struct mem_cgroup, css);
+ return mem_cgroup_from_css(css);
}
struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
@@ -2473,11 +2541,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
bool anon;
lock_page_cgroup(pc);
- if (unlikely(PageCgroupUsed(pc))) {
- unlock_page_cgroup(pc);
- __mem_cgroup_cancel_charge(memcg, nr_pages);
- return;
- }
+ VM_BUG_ON(PageCgroupUsed(pc));
/*
* we don't need page_cgroup_lock about tail pages, becase they are not
* accessed by any other context at this point.
@@ -2519,7 +2583,7 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
spin_unlock_irq(&zone->lru_lock);
}
- if (ctype == MEM_CGROUP_CHARGE_TYPE_MAPPED)
+ if (ctype == MEM_CGROUP_CHARGE_TYPE_ANON)
anon = true;
else
anon = false;
@@ -2644,8 +2708,7 @@ out:
static int mem_cgroup_move_parent(struct page *page,
struct page_cgroup *pc,
- struct mem_cgroup *child,
- gfp_t gfp_mask)
+ struct mem_cgroup *child)
{
struct mem_cgroup *parent;
unsigned int nr_pages;
@@ -2728,38 +2791,7 @@ int mem_cgroup_newpage_charge(struct page *page,
VM_BUG_ON(page->mapping && !PageAnon(page));
VM_BUG_ON(!mm);
return mem_cgroup_charge_common(page, mm, gfp_mask,
- MEM_CGROUP_CHARGE_TYPE_MAPPED);
-}
-
-static void
-__mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr,
- enum charge_type ctype);
-
-int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
- gfp_t gfp_mask)
-{
- struct mem_cgroup *memcg = NULL;
- enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
- int ret;
-
- if (mem_cgroup_disabled())
- return 0;
- if (PageCompound(page))
- return 0;
-
- if (unlikely(!mm))
- mm = &init_mm;
- if (!page_is_file_cache(page))
- type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
-
- if (!PageSwapCache(page))
- ret = mem_cgroup_charge_common(page, mm, gfp_mask, type);
- else { /* page is swapcache/shmem */
- ret = mem_cgroup_try_charge_swapin(mm, page, gfp_mask, &memcg);
- if (!ret)
- __mem_cgroup_commit_charge_swapin(page, memcg, type);
- }
- return ret;
+ MEM_CGROUP_CHARGE_TYPE_ANON);
}
/*
@@ -2768,27 +2800,26 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
* struct page_cgroup is acquired. This refcnt will be consumed by
* "commit()" or removed by "cancel()"
*/
-int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
- struct page *page,
- gfp_t mask, struct mem_cgroup **memcgp)
+static int __mem_cgroup_try_charge_swapin(struct mm_struct *mm,
+ struct page *page,
+ gfp_t mask,
+ struct mem_cgroup **memcgp)
{
struct mem_cgroup *memcg;
+ struct page_cgroup *pc;
int ret;
- *memcgp = NULL;
-
- if (mem_cgroup_disabled())
- return 0;
-
- if (!do_swap_account)
- goto charge_cur_mm;
+ pc = lookup_page_cgroup(page);
/*
- * A racing thread's fault, or swapoff, may have already updated
- * the pte, and even removed page from swap cache: in those cases
- * do_swap_page()'s pte_same() test will fail; but there's also a
- * KSM case which does need to charge the page.
+ * Every swap fault against a single page tries to charge the
+ * page, bail as early as possible. shmem_unuse() encounters
+ * already charged pages, too. The USED bit is protected by
+ * the page lock, which serializes swap cache removal, which
+ * in turn serializes uncharging.
*/
- if (!PageSwapCache(page))
+ if (PageCgroupUsed(pc))
+ return 0;
+ if (!do_swap_account)
goto charge_cur_mm;
memcg = try_get_mem_cgroup_from_page(page);
if (!memcg)
@@ -2800,14 +2831,44 @@ int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
ret = 0;
return ret;
charge_cur_mm:
- if (unlikely(!mm))
- mm = &init_mm;
ret = __mem_cgroup_try_charge(mm, mask, 1, memcgp, true);
if (ret == -EINTR)
ret = 0;
return ret;
}
+int mem_cgroup_try_charge_swapin(struct mm_struct *mm, struct page *page,
+ gfp_t gfp_mask, struct mem_cgroup **memcgp)
+{
+ *memcgp = NULL;
+ if (mem_cgroup_disabled())
+ return 0;
+ /*
+ * A racing thread's fault, or swapoff, may have already
+ * updated the pte, and even removed page from swap cache: in
+ * those cases unuse_pte()'s pte_same() test will fail; but
+ * there's also a KSM case which does need to charge the page.
+ */
+ if (!PageSwapCache(page)) {
+ int ret;
+
+ ret = __mem_cgroup_try_charge(mm, gfp_mask, 1, memcgp, true);
+ if (ret == -EINTR)
+ ret = 0;
+ return ret;
+ }
+ return __mem_cgroup_try_charge_swapin(mm, page, gfp_mask, memcgp);
+}
+
+void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
+{
+ if (mem_cgroup_disabled())
+ return;
+ if (!memcg)
+ return;
+ __mem_cgroup_cancel_charge(memcg, 1);
+}
+
static void
__mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
enum charge_type ctype)
@@ -2842,16 +2903,30 @@ void mem_cgroup_commit_charge_swapin(struct page *page,
struct mem_cgroup *memcg)
{
__mem_cgroup_commit_charge_swapin(page, memcg,
- MEM_CGROUP_CHARGE_TYPE_MAPPED);
+ MEM_CGROUP_CHARGE_TYPE_ANON);
}
-void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
+int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
+ gfp_t gfp_mask)
{
+ struct mem_cgroup *memcg = NULL;
+ enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
+ int ret;
+
if (mem_cgroup_disabled())
- return;
- if (!memcg)
- return;
- __mem_cgroup_cancel_charge(memcg, 1);
+ return 0;
+ if (PageCompound(page))
+ return 0;
+
+ if (!PageSwapCache(page))
+ ret = mem_cgroup_charge_common(page, mm, gfp_mask, type);
+ else { /* page is swapcache/shmem */
+ ret = __mem_cgroup_try_charge_swapin(mm, page,
+ gfp_mask, &memcg);
+ if (!ret)
+ __mem_cgroup_commit_charge_swapin(page, memcg, type);
+ }
+ return ret;
}
static void mem_cgroup_do_uncharge(struct mem_cgroup *memcg,
@@ -2911,7 +2986,8 @@ direct_uncharge:
* uncharge if !page_mapped(page)
*/
static struct mem_cgroup *
-__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
+__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
+ bool end_migration)
{
struct mem_cgroup *memcg = NULL;
unsigned int nr_pages = 1;
@@ -2921,8 +2997,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
if (mem_cgroup_disabled())
return NULL;
- if (PageSwapCache(page))
- return NULL;
+ VM_BUG_ON(PageSwapCache(page));
if (PageTransHuge(page)) {
nr_pages <<= compound_order(page);
@@ -2945,7 +3020,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
anon = PageAnon(page);
switch (ctype) {
- case MEM_CGROUP_CHARGE_TYPE_MAPPED:
+ case MEM_CGROUP_CHARGE_TYPE_ANON:
/*
* Generally PageAnon tells if it's the anon statistics to be
* updated; but sometimes e.g. mem_cgroup_uncharge_page() is
@@ -2955,7 +3030,16 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
/* fallthrough */
case MEM_CGROUP_CHARGE_TYPE_DROP:
/* See mem_cgroup_prepare_migration() */
- if (page_mapped(page) || PageCgroupMigration(pc))
+ if (page_mapped(page))
+ goto unlock_out;
+ /*
+ * Pages under migration may not be uncharged. But
+ * end_migration() /must/ be the one uncharging the
+ * unused post-migration page and so it has to call
+ * here with the migration bit still set. See the
+ * res_counter handling below.
+ */
+ if (!end_migration && PageCgroupMigration(pc))
goto unlock_out;
break;
case MEM_CGROUP_CHARGE_TYPE_SWAPOUT:
@@ -2989,7 +3073,12 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
mem_cgroup_swap_statistics(memcg, true);
mem_cgroup_get(memcg);
}
- if (!mem_cgroup_is_root(memcg))
+ /*
+ * Migration does not charge the res_counter for the
+ * replacement page, so leave it alone when phasing out the
+ * page that is unused after the migration.
+ */
+ if (!end_migration && !mem_cgroup_is_root(memcg))
mem_cgroup_do_uncharge(memcg, nr_pages, ctype);
return memcg;
@@ -3005,14 +3094,16 @@ void mem_cgroup_uncharge_page(struct page *page)
if (page_mapped(page))
return;
VM_BUG_ON(page->mapping && !PageAnon(page));
- __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_MAPPED);
+ if (PageSwapCache(page))
+ return;
+ __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false);
}
void mem_cgroup_uncharge_cache_page(struct page *page)
{
VM_BUG_ON(page_mapped(page));
VM_BUG_ON(page->mapping);
- __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE);
+ __mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE, false);
}
/*
@@ -3076,7 +3167,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
if (!swapout) /* this was a swap cache but the swap is unused ! */
ctype = MEM_CGROUP_CHARGE_TYPE_DROP;
- memcg = __mem_cgroup_uncharge_common(page, ctype);
+ memcg = __mem_cgroup_uncharge_common(page, ctype, false);
/*
* record memcg information, if swapout && memcg != NULL,
@@ -3087,7 +3178,7 @@ mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
}
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
/*
* called from swap_entry_free(). remove record in swap_cgroup and
* uncharge "memsw" account.
@@ -3166,19 +3257,18 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
* Before starting migration, account PAGE_SIZE to mem_cgroup that the old
* page belongs to.
*/
-int mem_cgroup_prepare_migration(struct page *page,
- struct page *newpage, struct mem_cgroup **memcgp, gfp_t gfp_mask)
+void mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
+ struct mem_cgroup **memcgp)
{
struct mem_cgroup *memcg = NULL;
struct page_cgroup *pc;
enum charge_type ctype;
- int ret = 0;
*memcgp = NULL;
VM_BUG_ON(PageTransHuge(page));
if (mem_cgroup_disabled())
- return 0;
+ return;
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc);
@@ -3223,24 +3313,9 @@ int mem_cgroup_prepare_migration(struct page *page,
* we return here.
*/
if (!memcg)
- return 0;
+ return;
*memcgp = memcg;
- ret = __mem_cgroup_try_charge(NULL, gfp_mask, 1, memcgp, false);
- css_put(&memcg->css);/* drop extra refcnt */
- if (ret) {
- if (PageAnon(page)) {
- lock_page_cgroup(pc);
- ClearPageCgroupMigration(pc);
- unlock_page_cgroup(pc);
- /*
- * The old page may be fully unmapped while we kept it.
- */
- mem_cgroup_uncharge_page(page);
- }
- /* we'll need to revisit this error code (we have -EINTR) */
- return -ENOMEM;
- }
/*
* We charge new page before it's used/mapped. So, even if unlock_page()
* is called before end_migration, we can catch all events on this new
@@ -3248,13 +3323,15 @@ int mem_cgroup_prepare_migration(struct page *page,
* mapcount will be finally 0 and we call uncharge in end_migration().
*/
if (PageAnon(page))
- ctype = MEM_CGROUP_CHARGE_TYPE_MAPPED;
- else if (page_is_file_cache(page))
- ctype = MEM_CGROUP_CHARGE_TYPE_CACHE;
+ ctype = MEM_CGROUP_CHARGE_TYPE_ANON;
else
- ctype = MEM_CGROUP_CHARGE_TYPE_SHMEM;
+ ctype = MEM_CGROUP_CHARGE_TYPE_CACHE;
+ /*
+ * The page is committed to the memcg, but it's not actually
+ * charged to the res_counter since we plan on replacing the
+ * old one and only one page is going to be left afterwards.
+ */
__mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false);
- return ret;
}
/* remove redundant charge if migration failed*/
@@ -3276,6 +3353,12 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
used = newpage;
unused = oldpage;
}
+ anon = PageAnon(used);
+ __mem_cgroup_uncharge_common(unused,
+ anon ? MEM_CGROUP_CHARGE_TYPE_ANON
+ : MEM_CGROUP_CHARGE_TYPE_CACHE,
+ true);
+ css_put(&memcg->css);
/*
* We disallowed uncharge of pages under migration because mapcount
* of the page goes down to zero, temporarly.
@@ -3285,10 +3368,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
lock_page_cgroup(pc);
ClearPageCgroupMigration(pc);
unlock_page_cgroup(pc);
- anon = PageAnon(used);
- __mem_cgroup_uncharge_common(unused,
- anon ? MEM_CGROUP_CHARGE_TYPE_MAPPED
- : MEM_CGROUP_CHARGE_TYPE_CACHE);
/*
* If a page is a file cache, radix-tree replacement is very atomic
@@ -3340,10 +3419,6 @@ void mem_cgroup_replace_page_cache(struct page *oldpage,
*/
if (!memcg)
return;
-
- if (PageSwapBacked(oldpage))
- type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
-
/*
* Even if newpage->mapping was NULL before starting replacement,
* the newpage may be on LRU(or pagevec for LRU) already. We lock
@@ -3418,7 +3493,7 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
/*
* Rather than hide all in some function, I do this in
* open coded manner. You see what this really does.
- * We have to guarantee memcg->res.limit < memcg->memsw.limit.
+ * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
*/
mutex_lock(&set_limit_mutex);
memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
@@ -3479,7 +3554,7 @@ static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
/*
* Rather than hide all in some function, I do this in
* open coded manner. You see what this really does.
- * We have to guarantee memcg->res.limit < memcg->memsw.limit.
+ * We have to guarantee memcg->res.limit <= memcg->memsw.limit.
*/
mutex_lock(&set_limit_mutex);
memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
@@ -3611,10 +3686,12 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
}
/*
- * This routine traverse page_cgroup in given list and drop them all.
- * *And* this routine doesn't reclaim page itself, just removes page_cgroup.
+ * Traverse a specified page_cgroup list and try to drop them all. This doesn't
+ * reclaim the pages page themselves - it just removes the page_cgroups.
+ * Returns true if some page_cgroups were not freed, indicating that the caller
+ * must retry this operation.
*/
-static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
+static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
int node, int zid, enum lru_list lru)
{
struct mem_cgroup_per_zone *mz;
@@ -3622,7 +3699,6 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
struct list_head *list;
struct page *busy;
struct zone *zone;
- int ret = 0;
zone = &NODE_DATA(node)->node_zones[zid];
mz = mem_cgroup_zoneinfo(memcg, node, zid);
@@ -3636,7 +3712,6 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
struct page_cgroup *pc;
struct page *page;
- ret = 0;
spin_lock_irqsave(&zone->lru_lock, flags);
if (list_empty(list)) {
spin_unlock_irqrestore(&zone->lru_lock, flags);
@@ -3653,21 +3728,14 @@ static int mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
pc = lookup_page_cgroup(page);
- ret = mem_cgroup_move_parent(page, pc, memcg, GFP_KERNEL);
- if (ret == -ENOMEM || ret == -EINTR)
- break;
-
- if (ret == -EBUSY || ret == -EINVAL) {
+ if (mem_cgroup_move_parent(page, pc, memcg)) {
/* found lock contention or "pc" is obsolete. */
busy = page;
cond_resched();
} else
busy = NULL;
}
-
- if (!ret && !list_empty(list))
- return -EBUSY;
- return ret;
+ return !list_empty(list);
}
/*
@@ -3692,9 +3760,6 @@ move_account:
ret = -EBUSY;
if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
goto out;
- ret = -EINTR;
- if (signal_pending(current))
- goto out;
/* This is for making all *used* pages to be on LRU. */
lru_add_drain_all();
drain_all_stock_sync(memcg);
@@ -3715,9 +3780,6 @@ move_account:
}
mem_cgroup_end_move(memcg);
memcg_oom_recover(memcg);
- /* it seems parent cgroup doesn't have enough mem */
- if (ret == -ENOMEM)
- goto try_to_free;
cond_resched();
/* "ret" should also be checked to ensure all lists are empty. */
} while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret);
@@ -3779,6 +3841,10 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
parent_memcg = mem_cgroup_from_cont(parent);
cgroup_lock();
+
+ if (memcg->use_hierarchy == val)
+ goto out;
+
/*
* If parent's use_hierarchy is set, we can't make any modifications
* in the child subtrees. If it is unset, then the change can
@@ -3795,6 +3861,8 @@ static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
retval = -EBUSY;
} else
retval = -EINVAL;
+
+out:
cgroup_unlock();
return retval;
@@ -3831,7 +3899,7 @@ static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_RSS);
if (swap)
- val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_SWAPOUT);
+ val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_SWAP);
return val << PAGE_SHIFT;
}
@@ -4015,7 +4083,7 @@ static int mem_cgroup_move_charge_write(struct cgroup *cgrp,
#endif
#ifdef CONFIG_NUMA
-static int mem_control_numa_stat_show(struct cgroup *cont, struct cftype *cft,
+static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
struct seq_file *m)
{
int nid;
@@ -4074,7 +4142,7 @@ static inline void mem_cgroup_lru_names_not_uptodate(void)
BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS);
}
-static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
+static int memcg_stat_show(struct cgroup *cont, struct cftype *cft,
struct seq_file *m)
{
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
@@ -4082,7 +4150,7 @@ static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
unsigned int i;
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
- if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
+ if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
continue;
seq_printf(m, "%s %ld\n", mem_cgroup_stat_names[i],
mem_cgroup_read_stat(memcg, i) * PAGE_SIZE);
@@ -4109,7 +4177,7 @@ static int mem_control_stat_show(struct cgroup *cont, struct cftype *cft,
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
long long val = 0;
- if (i == MEM_CGROUP_STAT_SWAPOUT && !do_swap_account)
+ if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
continue;
for_each_mem_cgroup_tree(mi, memcg)
val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE;
@@ -4533,7 +4601,7 @@ static int mem_cgroup_oom_control_write(struct cgroup *cgrp,
return 0;
}
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
{
return mem_cgroup_sockets_init(memcg, ss);
@@ -4588,7 +4656,7 @@ static struct cftype mem_cgroup_files[] = {
},
{
.name = "stat",
- .read_seq_string = mem_control_stat_show,
+ .read_seq_string = memcg_stat_show,
},
{
.name = "force_empty",
@@ -4620,10 +4688,10 @@ static struct cftype mem_cgroup_files[] = {
#ifdef CONFIG_NUMA
{
.name = "numa_stat",
- .read_seq_string = mem_control_numa_stat_show,
+ .read_seq_string = memcg_numa_stat_show,
},
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
{
.name = "memsw.usage_in_bytes",
.private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
@@ -4810,7 +4878,7 @@ struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
}
EXPORT_SYMBOL(parent_mem_cgroup);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
static void __init enable_swap_cgroup(void)
{
if (!mem_cgroup_disabled() && really_do_swap_account)
@@ -5541,7 +5609,7 @@ struct cgroup_subsys mem_cgroup_subsys = {
.__DEPRECATED_clear_css_refs = true,
};
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
static int __init enable_swap_account(char *s)
{
/* consider enabled if no parameter or 1 is given */
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 6de0d613bbe6..a6e2141a6610 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -128,7 +128,7 @@ static int hwpoison_filter_flags(struct page *p)
* can only guarantee that the page either belongs to the memcg tasks, or is
* a freed page.
*/
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
u64 hwpoison_filter_memcg;
EXPORT_SYMBOL_GPL(hwpoison_filter_memcg);
static int hwpoison_filter_task(struct page *p)
@@ -1416,7 +1416,6 @@ static int soft_offline_huge_page(struct page *page, int flags)
int ret;
unsigned long pfn = page_to_pfn(page);
struct page *hpage = compound_head(page);
- LIST_HEAD(pagelist);
ret = get_any_page(page, pfn, flags);
if (ret < 0)
@@ -1431,24 +1430,18 @@ static int soft_offline_huge_page(struct page *page, int flags)
}
/* Keep page count to indicate a given hugepage is isolated. */
-
- list_add(&hpage->lru, &pagelist);
- ret = migrate_huge_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, false,
+ ret = migrate_huge_page(hpage, new_page, MPOL_MF_MOVE_ALL, false,
MIGRATE_SYNC);
+ put_page(hpage);
if (ret) {
- struct page *page1, *page2;
- list_for_each_entry_safe(page1, page2, &pagelist, lru)
- put_page(page1);
-
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
pfn, ret, page->flags);
- if (ret > 0)
- ret = -EIO;
return ret;
}
done:
if (!PageHWPoison(hpage))
- atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages);
+ atomic_long_add(1 << compound_trans_order(hpage),
+ &mce_bad_pages);
set_page_hwpoison_huge_page(hpage);
dequeue_hwpoisoned_huge_page(hpage);
/* keep elevated page count for bad page */
diff --git a/mm/memory.c b/mm/memory.c
index 91f69459d3e8..57361708d1a5 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1343,8 +1343,11 @@ static void unmap_single_vma(struct mmu_gather *tlb,
* Since no pte has actually been setup, it is
* safe to do nothing in this case.
*/
- if (vma->vm_file)
- unmap_hugepage_range(vma, start, end, NULL);
+ if (vma->vm_file) {
+ mutex_lock(&vma->vm_file->f_mapping->i_mmap_mutex);
+ __unmap_hugepage_range_final(tlb, vma, start, end, NULL);
+ mutex_unlock(&vma->vm_file->f_mapping->i_mmap_mutex);
+ }
} else
unmap_page_range(tlb, vma, start, end, details);
}
@@ -2647,6 +2650,9 @@ reuse:
if (!page_mkwrite) {
wait_on_page_locked(dirty_page);
set_page_dirty_balance(dirty_page, page_mkwrite);
+ /* file_update_time outside page_lock */
+ if (vma->vm_file)
+ file_update_time(vma->vm_file);
}
put_page(dirty_page);
if (page_mkwrite) {
@@ -2664,10 +2670,6 @@ reuse:
}
}
- /* file_update_time outside page_lock */
- if (vma->vm_file)
- file_update_time(vma->vm_file);
-
return ret;
}
@@ -3336,12 +3338,13 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
if (dirty_page) {
struct address_space *mapping = page->mapping;
+ int dirtied = 0;
if (set_page_dirty(dirty_page))
- page_mkwrite = 1;
+ dirtied = 1;
unlock_page(dirty_page);
put_page(dirty_page);
- if (page_mkwrite && mapping) {
+ if ((dirtied || page_mkwrite) && mapping) {
/*
* Some device drivers do not set page.mapping but still
* dirty their pages
@@ -3350,7 +3353,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
}
/* file_update_time outside page_lock */
- if (vma->vm_file)
+ if (vma->vm_file && !page_mkwrite)
file_update_time(vma->vm_file);
} else {
unlock_page(vmf.page);
@@ -3938,7 +3941,7 @@ void print_vma_addr(char *prefix, unsigned long ip)
free_page((unsigned long)buf);
}
}
- up_read(&current->mm->mmap_sem);
+ up_read(&mm->mmap_sem);
}
#ifdef CONFIG_PROVE_LOCKING
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 427bb291dd0f..6a5b90d0cfd7 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -126,9 +126,6 @@ static void register_page_bootmem_info_section(unsigned long start_pfn)
struct mem_section *ms;
struct page *page, *memmap;
- if (!pfn_valid(start_pfn))
- return;
-
section_nr = pfn_to_section_nr(start_pfn);
ms = __nr_to_section(section_nr);
@@ -187,9 +184,16 @@ void register_page_bootmem_info_node(struct pglist_data *pgdat)
end_pfn = pfn + pgdat->node_spanned_pages;
/* register_section info */
- for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
- register_page_bootmem_info_section(pfn);
-
+ for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+ /*
+ * Some platforms can assign the same pfn to multiple nodes - on
+ * node0 as well as nodeN. To avoid registering a pfn against
+ * multiple nodes we check that this pfn does not already
+ * reside in some other node.
+ */
+ if (pfn_valid(pfn) && (pfn_to_nid(pfn) == node))
+ register_page_bootmem_info_section(pfn);
+ }
}
#endif /* !CONFIG_SPARSEMEM_VMEMMAP */
@@ -512,19 +516,20 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages)
zone->present_pages += onlined_pages;
zone->zone_pgdat->node_present_pages += onlined_pages;
- if (need_zonelists_rebuild)
- build_all_zonelists(zone);
- else
- zone_pcp_update(zone);
+ if (onlined_pages) {
+ node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
+ if (need_zonelists_rebuild)
+ build_all_zonelists(NULL, zone);
+ else
+ zone_pcp_update(zone);
+ }
mutex_unlock(&zonelists_mutex);
init_per_zone_wmark_min();
- if (onlined_pages) {
+ if (onlined_pages)
kswapd_run(zone_to_nid(zone));
- node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
- }
vm_total_pages = nr_free_pagecache_pages();
@@ -562,7 +567,7 @@ static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
* to access not-initialized zonelist, build here.
*/
mutex_lock(&zonelists_mutex);
- build_all_zonelists(NULL);
+ build_all_zonelists(pgdat, NULL);
mutex_unlock(&zonelists_mutex);
return pgdat;
@@ -965,6 +970,9 @@ repeat:
init_per_zone_wmark_min();
+ if (!populated_zone(zone))
+ zone_pcp_reset(zone);
+
if (!node_present_pages(node)) {
node_clear_state(node, N_HIGH_MEMORY);
kswapd_stop(node);
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index bd92431d4c49..4ada3be6e252 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2562,7 +2562,7 @@ int mpol_to_str(char *buffer, int maxlen, struct mempolicy *pol, int no_context)
break;
default:
- BUG();
+ return -EINVAL;
}
l = strlen(policy_modes[mode]);
diff --git a/mm/mempool.c b/mm/mempool.c
index d9049811f352..54990476c049 100644
--- a/mm/mempool.c
+++ b/mm/mempool.c
@@ -63,19 +63,21 @@ EXPORT_SYMBOL(mempool_destroy);
mempool_t *mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
mempool_free_t *free_fn, void *pool_data)
{
- return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,-1);
+ return mempool_create_node(min_nr,alloc_fn,free_fn, pool_data,
+ GFP_KERNEL, NUMA_NO_NODE);
}
EXPORT_SYMBOL(mempool_create);
mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
- mempool_free_t *free_fn, void *pool_data, int node_id)
+ mempool_free_t *free_fn, void *pool_data,
+ gfp_t gfp_mask, int node_id)
{
mempool_t *pool;
- pool = kmalloc_node(sizeof(*pool), GFP_KERNEL | __GFP_ZERO, node_id);
+ pool = kmalloc_node(sizeof(*pool), gfp_mask | __GFP_ZERO, node_id);
if (!pool)
return NULL;
pool->elements = kmalloc_node(min_nr * sizeof(void *),
- GFP_KERNEL, node_id);
+ gfp_mask, node_id);
if (!pool->elements) {
kfree(pool);
return NULL;
@@ -93,7 +95,7 @@ mempool_t *mempool_create_node(int min_nr, mempool_alloc_t *alloc_fn,
while (pool->curr_nr < pool->min_nr) {
void *element;
- element = pool->alloc(GFP_KERNEL, pool->pool_data);
+ element = pool->alloc(gfp_mask, pool->pool_data);
if (unlikely(!element)) {
mempool_destroy(pool);
return NULL;
diff --git a/mm/migrate.c b/mm/migrate.c
index be26d5cbe56b..77ed2d773705 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -33,6 +33,7 @@
#include <linux/memcontrol.h>
#include <linux/syscalls.h>
#include <linux/hugetlb.h>
+#include <linux/hugetlb_cgroup.h>
#include <linux/gfp.h>
#include <asm/tlbflush.h>
@@ -682,7 +683,6 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
{
int rc = -EAGAIN;
int remap_swapcache = 1;
- int charge = 0;
struct mem_cgroup *mem;
struct anon_vma *anon_vma = NULL;
@@ -724,12 +724,7 @@ static int __unmap_and_move(struct page *page, struct page *newpage,
}
/* charge against new page */
- charge = mem_cgroup_prepare_migration(page, newpage, &mem, GFP_KERNEL);
- if (charge == -ENOMEM) {
- rc = -ENOMEM;
- goto unlock;
- }
- BUG_ON(charge);
+ mem_cgroup_prepare_migration(page, newpage, &mem);
if (PageWriteback(page)) {
/*
@@ -819,8 +814,7 @@ skip_unmap:
put_anon_vma(anon_vma);
uncharge:
- if (!charge)
- mem_cgroup_end_migration(mem, page, newpage, rc == 0);
+ mem_cgroup_end_migration(mem, page, newpage, rc == 0);
unlock:
unlock_page(page);
out:
@@ -931,16 +925,13 @@ static int unmap_and_move_huge_page(new_page_t get_new_page,
if (anon_vma)
put_anon_vma(anon_vma);
- unlock_page(hpage);
-out:
- if (rc != -EAGAIN) {
- list_del(&hpage->lru);
- put_page(hpage);
- }
+ if (!rc)
+ hugetlb_cgroup_migrate(hpage, new_hpage);
+ unlock_page(hpage);
+out:
put_page(new_hpage);
-
if (result) {
if (rc)
*result = rc;
@@ -1016,48 +1007,32 @@ out:
return nr_failed + retry;
}
-int migrate_huge_pages(struct list_head *from,
- new_page_t get_new_page, unsigned long private, bool offlining,
- enum migrate_mode mode)
+int migrate_huge_page(struct page *hpage, new_page_t get_new_page,
+ unsigned long private, bool offlining,
+ enum migrate_mode mode)
{
- int retry = 1;
- int nr_failed = 0;
- int pass = 0;
- struct page *page;
- struct page *page2;
- int rc;
-
- for (pass = 0; pass < 10 && retry; pass++) {
- retry = 0;
-
- list_for_each_entry_safe(page, page2, from, lru) {
+ int pass, rc;
+
+ for (pass = 0; pass < 10; pass++) {
+ rc = unmap_and_move_huge_page(get_new_page,
+ private, hpage, pass > 2, offlining,
+ mode);
+ switch (rc) {
+ case -ENOMEM:
+ goto out;
+ case -EAGAIN:
+ /* try again */
cond_resched();
-
- rc = unmap_and_move_huge_page(get_new_page,
- private, page, pass > 2, offlining,
- mode);
-
- switch(rc) {
- case -ENOMEM:
- goto out;
- case -EAGAIN:
- retry++;
- break;
- case 0:
- break;
- default:
- /* Permanent failure */
- nr_failed++;
- break;
- }
+ break;
+ case 0:
+ goto out;
+ default:
+ rc = -EIO;
+ goto out;
}
}
- rc = 0;
out:
- if (rc)
- return rc;
-
- return nr_failed + retry;
+ return rc;
}
#ifdef CONFIG_NUMA
diff --git a/mm/mmap.c b/mm/mmap.c
index 4fe2697339ed..ae18a48e7e4e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -943,6 +943,8 @@ void vm_stat_account(struct mm_struct *mm, unsigned long flags,
const unsigned long stack_flags
= VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN);
+ mm->total_vm += pages;
+
if (file) {
mm->shared_vm += pages;
if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC)
@@ -1347,7 +1349,6 @@ munmap_back:
out:
perf_event_mmap(vma);
- mm->total_vm += len >> PAGE_SHIFT;
vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);
if (vm_flags & VM_LOCKED) {
if (!mlock_vma_pages_range(vma, addr, addr + len))
@@ -1355,9 +1356,8 @@ out:
} else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK))
make_pages_present(addr, addr + len);
- if (file && uprobe_mmap(vma))
- /* matching probes but cannot insert */
- goto unmap_and_free_vma;
+ if (file)
+ uprobe_mmap(vma);
return addr;
@@ -1707,7 +1707,6 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
return -ENOMEM;
/* Ok, everything looks good - let it rip */
- mm->total_vm += grow;
if (vma->vm_flags & VM_LOCKED)
mm->locked_vm += grow;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow);
@@ -1889,7 +1888,6 @@ static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma)
if (vma->vm_flags & VM_ACCOUNT)
nr_accounted += nrpages;
- mm->total_vm -= nrpages;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages);
vma = remove_vma(vma);
} while (vma);
@@ -2310,7 +2308,7 @@ void exit_mmap(struct mm_struct *mm)
}
vm_unacct_memory(nr_accounted);
- BUG_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
+ WARN_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
}
/* Insert vm structure into process list sorted by address
diff --git a/mm/mmu_notifier.c b/mm/mmu_notifier.c
index 9a611d3a1848..862b60822d9f 100644
--- a/mm/mmu_notifier.c
+++ b/mm/mmu_notifier.c
@@ -33,6 +33,24 @@
void __mmu_notifier_release(struct mm_struct *mm)
{
struct mmu_notifier *mn;
+ struct hlist_node *n;
+
+ /*
+ * RCU here will block mmu_notifier_unregister until
+ * ->release returns.
+ */
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(mn, n, &mm->mmu_notifier_mm->list, hlist)
+ /*
+ * if ->release runs before mmu_notifier_unregister it
+ * must be handled as it's the only way for the driver
+ * to flush all existing sptes and stop the driver
+ * from establishing any more sptes before all the
+ * pages in the mm are freed.
+ */
+ if (mn->ops->release)
+ mn->ops->release(mn, mm);
+ rcu_read_unlock();
spin_lock(&mm->mmu_notifier_mm->lock);
while (unlikely(!hlist_empty(&mm->mmu_notifier_mm->list))) {
@@ -46,23 +64,6 @@ void __mmu_notifier_release(struct mm_struct *mm)
* mmu_notifier_unregister to return.
*/
hlist_del_init_rcu(&mn->hlist);
- /*
- * RCU here will block mmu_notifier_unregister until
- * ->release returns.
- */
- rcu_read_lock();
- spin_unlock(&mm->mmu_notifier_mm->lock);
- /*
- * if ->release runs before mmu_notifier_unregister it
- * must be handled as it's the only way for the driver
- * to flush all existing sptes and stop the driver
- * from establishing any more sptes before all the
- * pages in the mm are freed.
- */
- if (mn->ops->release)
- mn->ops->release(mn, mm);
- rcu_read_unlock();
- spin_lock(&mm->mmu_notifier_mm->lock);
}
spin_unlock(&mm->mmu_notifier_mm->lock);
@@ -284,16 +285,13 @@ void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm)
{
BUG_ON(atomic_read(&mm->mm_count) <= 0);
- spin_lock(&mm->mmu_notifier_mm->lock);
if (!hlist_unhashed(&mn->hlist)) {
- hlist_del_rcu(&mn->hlist);
-
/*
* RCU here will force exit_mmap to wait ->release to finish
* before freeing the pages.
*/
rcu_read_lock();
- spin_unlock(&mm->mmu_notifier_mm->lock);
+
/*
* exit_mmap will block in mmu_notifier_release to
* guarantee ->release is called before freeing the
@@ -302,8 +300,11 @@ void mmu_notifier_unregister(struct mmu_notifier *mn, struct mm_struct *mm)
if (mn->ops->release)
mn->ops->release(mn, mm);
rcu_read_unlock();
- } else
+
+ spin_lock(&mm->mmu_notifier_mm->lock);
+ hlist_del_rcu(&mn->hlist);
spin_unlock(&mm->mmu_notifier_mm->lock);
+ }
/*
* Wait any running method to finish, of course including
diff --git a/mm/mmzone.c b/mm/mmzone.c
index 6830eab5bf09..3cef80f6ac79 100644
--- a/mm/mmzone.c
+++ b/mm/mmzone.c
@@ -96,7 +96,7 @@ void lruvec_init(struct lruvec *lruvec, struct zone *zone)
for_each_lru(lru)
INIT_LIST_HEAD(&lruvec->lists[lru]);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
lruvec->zone = zone;
#endif
}
diff --git a/mm/mremap.c b/mm/mremap.c
index 21fed202ddad..cc06d0e48d05 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -260,7 +260,6 @@ static unsigned long move_vma(struct vm_area_struct *vma,
* If this were a serious issue, we'd add a flag to do_munmap().
*/
hiwater_vm = mm->hiwater_vm;
- mm->total_vm += new_len >> PAGE_SHIFT;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, new_len>>PAGE_SHIFT);
if (do_munmap(mm, old_addr, old_len) < 0) {
@@ -497,7 +496,6 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
goto out;
}
- mm->total_vm += pages;
vm_stat_account(mm, vma->vm_flags, vma->vm_file, pages);
if (vma->vm_flags & VM_LOCKED) {
mm->locked_vm += pages;
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index ac300c99baf6..198600861638 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -288,76 +288,93 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist,
}
#endif
+enum oom_scan_t oom_scan_process_thread(struct task_struct *task,
+ unsigned long totalpages, const nodemask_t *nodemask,
+ bool force_kill)
+{
+ if (task->exit_state)
+ return OOM_SCAN_CONTINUE;
+ if (oom_unkillable_task(task, NULL, nodemask))
+ return OOM_SCAN_CONTINUE;
+
+ /*
+ * This task already has access to memory reserves and is being killed.
+ * Don't allow any other task to have access to the reserves.
+ */
+ if (test_tsk_thread_flag(task, TIF_MEMDIE)) {
+ if (unlikely(frozen(task)))
+ __thaw_task(task);
+ if (!force_kill)
+ return OOM_SCAN_ABORT;
+ }
+ if (!task->mm)
+ return OOM_SCAN_CONTINUE;
+
+ if (task->flags & PF_EXITING) {
+ /*
+ * If task is current and is in the process of releasing memory,
+ * allow the "kill" to set TIF_MEMDIE, which will allow it to
+ * access memory reserves. Otherwise, it may stall forever.
+ *
+ * The iteration isn't broken here, however, in case other
+ * threads are found to have already been oom killed.
+ */
+ if (task == current)
+ return OOM_SCAN_SELECT;
+ else if (!force_kill) {
+ /*
+ * If this task is not being ptraced on exit, then wait
+ * for it to finish before killing some other task
+ * unnecessarily.
+ */
+ if (!(task->group_leader->ptrace & PT_TRACE_EXIT))
+ return OOM_SCAN_ABORT;
+ }
+ }
+ return OOM_SCAN_OK;
+}
+
/*
* Simple selection loop. We chose the process with the highest
- * number of 'points'. We expect the caller will lock the tasklist.
+ * number of 'points'.
*
* (not docbooked, we don't want this one cluttering up the manual)
*/
static struct task_struct *select_bad_process(unsigned int *ppoints,
- unsigned long totalpages, struct mem_cgroup *memcg,
- const nodemask_t *nodemask, bool force_kill)
+ unsigned long totalpages, const nodemask_t *nodemask,
+ bool force_kill)
{
struct task_struct *g, *p;
struct task_struct *chosen = NULL;
unsigned long chosen_points = 0;
+ rcu_read_lock();
do_each_thread(g, p) {
unsigned int points;
- if (p->exit_state)
- continue;
- if (oom_unkillable_task(p, memcg, nodemask))
- continue;
-
- /*
- * This task already has access to memory reserves and is
- * being killed. Don't allow any other task access to the
- * memory reserve.
- *
- * Note: this may have a chance of deadlock if it gets
- * blocked waiting for another task which itself is waiting
- * for memory. Is there a better alternative?
- */
- if (test_tsk_thread_flag(p, TIF_MEMDIE)) {
- if (unlikely(frozen(p)))
- __thaw_task(p);
- if (!force_kill)
- return ERR_PTR(-1UL);
- }
- if (!p->mm)
+ switch (oom_scan_process_thread(p, totalpages, nodemask,
+ force_kill)) {
+ case OOM_SCAN_SELECT:
+ chosen = p;
+ chosen_points = ULONG_MAX;
+ /* fall through */
+ case OOM_SCAN_CONTINUE:
continue;
-
- if (p->flags & PF_EXITING) {
- /*
- * If p is the current task and is in the process of
- * releasing memory, we allow the "kill" to set
- * TIF_MEMDIE, which will allow it to gain access to
- * memory reserves. Otherwise, it may stall forever.
- *
- * The loop isn't broken here, however, in case other
- * threads are found to have already been oom killed.
- */
- if (p == current) {
- chosen = p;
- chosen_points = ULONG_MAX;
- } else if (!force_kill) {
- /*
- * If this task is not being ptraced on exit,
- * then wait for it to finish before killing
- * some other task unnecessarily.
- */
- if (!(p->group_leader->ptrace & PT_TRACE_EXIT))
- return ERR_PTR(-1UL);
- }
- }
-
- points = oom_badness(p, memcg, nodemask, totalpages);
+ case OOM_SCAN_ABORT:
+ rcu_read_unlock();
+ return ERR_PTR(-1UL);
+ case OOM_SCAN_OK:
+ break;
+ };
+ points = oom_badness(p, NULL, nodemask, totalpages);
if (points > chosen_points) {
chosen = p;
chosen_points = points;
}
} while_each_thread(g, p);
+ if (chosen)
+ get_task_struct(chosen);
+ rcu_read_unlock();
*ppoints = chosen_points * 1000 / totalpages;
return chosen;
@@ -371,17 +388,16 @@ static struct task_struct *select_bad_process(unsigned int *ppoints,
* Dumps the current memory state of all eligible tasks. Tasks not in the same
* memcg, not in the same cpuset, or bound to a disjoint set of mempolicy nodes
* are not shown.
- * State information includes task's pid, uid, tgid, vm size, rss, cpu, oom_adj
- * value, oom_score_adj value, and name.
- *
- * Call with tasklist_lock read-locked.
+ * State information includes task's pid, uid, tgid, vm size, rss, nr_ptes,
+ * swapents, oom_score_adj value, and name.
*/
static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemask)
{
struct task_struct *p;
struct task_struct *task;
- pr_info("[ pid ] uid tgid total_vm rss cpu oom_adj oom_score_adj name\n");
+ pr_info("[ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name\n");
+ rcu_read_lock();
for_each_process(p) {
if (oom_unkillable_task(p, memcg, nodemask))
continue;
@@ -396,13 +412,15 @@ static void dump_tasks(const struct mem_cgroup *memcg, const nodemask_t *nodemas
continue;
}
- pr_info("[%5d] %5d %5d %8lu %8lu %3u %3d %5d %s\n",
+ pr_info("[%5d] %5d %5d %8lu %8lu %7lu %8lu %5d %s\n",
task->pid, from_kuid(&init_user_ns, task_uid(task)),
task->tgid, task->mm->total_vm, get_mm_rss(task->mm),
- task_cpu(task), task->signal->oom_adj,
+ task->mm->nr_ptes,
+ get_mm_counter(task->mm, MM_SWAPENTS),
task->signal->oom_score_adj, task->comm);
task_unlock(task);
}
+ rcu_read_unlock();
}
static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
@@ -423,10 +441,14 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order,
}
#define K(x) ((x) << (PAGE_SHIFT-10))
-static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
- unsigned int points, unsigned long totalpages,
- struct mem_cgroup *memcg, nodemask_t *nodemask,
- const char *message)
+/*
+ * Must be called while holding a reference to p, which will be released upon
+ * returning.
+ */
+void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
+ unsigned int points, unsigned long totalpages,
+ struct mem_cgroup *memcg, nodemask_t *nodemask,
+ const char *message)
{
struct task_struct *victim = p;
struct task_struct *child;
@@ -442,6 +464,7 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
*/
if (p->flags & PF_EXITING) {
set_tsk_thread_flag(p, TIF_MEMDIE);
+ put_task_struct(p);
return;
}
@@ -459,6 +482,7 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
* parent. This attempts to lose the minimal amount of work done while
* still freeing memory.
*/
+ read_lock(&tasklist_lock);
do {
list_for_each_entry(child, &t->children, sibling) {
unsigned int child_points;
@@ -471,15 +495,26 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
child_points = oom_badness(child, memcg, nodemask,
totalpages);
if (child_points > victim_points) {
+ put_task_struct(victim);
victim = child;
victim_points = child_points;
+ get_task_struct(victim);
}
}
} while_each_thread(p, t);
+ read_unlock(&tasklist_lock);
- victim = find_lock_task_mm(victim);
- if (!victim)
+ rcu_read_lock();
+ p = find_lock_task_mm(victim);
+ if (!p) {
+ rcu_read_unlock();
+ put_task_struct(victim);
return;
+ } else if (victim != p) {
+ get_task_struct(p);
+ put_task_struct(victim);
+ victim = p;
+ }
/* mm cannot safely be dereferenced after task_unlock(victim) */
mm = victim->mm;
@@ -510,17 +545,19 @@ static void oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order,
task_unlock(p);
do_send_sig_info(SIGKILL, SEND_SIG_FORCED, p, true);
}
+ rcu_read_unlock();
set_tsk_thread_flag(victim, TIF_MEMDIE);
do_send_sig_info(SIGKILL, SEND_SIG_FORCED, victim, true);
+ put_task_struct(victim);
}
#undef K
/*
* Determines whether the kernel must panic because of the panic_on_oom sysctl.
*/
-static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
- int order, const nodemask_t *nodemask)
+void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
+ int order, const nodemask_t *nodemask)
{
if (likely(!sysctl_panic_on_oom))
return;
@@ -533,42 +570,11 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask,
if (constraint != CONSTRAINT_NONE)
return;
}
- read_lock(&tasklist_lock);
dump_header(NULL, gfp_mask, order, NULL, nodemask);
- read_unlock(&tasklist_lock);
panic("Out of memory: %s panic_on_oom is enabled\n",
sysctl_panic_on_oom == 2 ? "compulsory" : "system-wide");
}
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
-void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
- int order)
-{
- unsigned long limit;
- unsigned int points = 0;
- struct task_struct *p;
-
- /*
- * If current has a pending SIGKILL, then automatically select it. The
- * goal is to allow it to allocate so that it may quickly exit and free
- * its memory.
- */
- if (fatal_signal_pending(current)) {
- set_thread_flag(TIF_MEMDIE);
- return;
- }
-
- check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
- limit = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
- read_lock(&tasklist_lock);
- p = select_bad_process(&points, limit, memcg, NULL, false);
- if (p && PTR_ERR(p) != -1UL)
- oom_kill_process(p, gfp_mask, order, points, limit, memcg, NULL,
- "Memory cgroup out of memory");
- read_unlock(&tasklist_lock);
-}
-#endif
-
static BLOCKING_NOTIFIER_HEAD(oom_notify_list);
int register_oom_notifier(struct notifier_block *nb)
@@ -690,7 +696,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
struct task_struct *p;
unsigned long totalpages;
unsigned long freed = 0;
- unsigned int points;
+ unsigned int uninitialized_var(points);
enum oom_constraint constraint = CONSTRAINT_NONE;
int killed = 0;
@@ -718,22 +724,20 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
mpol_mask = (constraint == CONSTRAINT_MEMORY_POLICY) ? nodemask : NULL;
check_panic_on_oom(constraint, gfp_mask, order, mpol_mask);
- read_lock(&tasklist_lock);
- if (sysctl_oom_kill_allocating_task &&
+ if (sysctl_oom_kill_allocating_task && current->mm &&
!oom_unkillable_task(current, NULL, nodemask) &&
- current->mm) {
+ current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {
+ get_task_struct(current);
oom_kill_process(current, gfp_mask, order, 0, totalpages, NULL,
nodemask,
"Out of memory (oom_kill_allocating_task)");
goto out;
}
- p = select_bad_process(&points, totalpages, NULL, mpol_mask,
- force_kill);
+ p = select_bad_process(&points, totalpages, mpol_mask, force_kill);
/* Found nothing?!?! Either we hang forever, or we panic. */
if (!p) {
dump_header(NULL, gfp_mask, order, NULL, mpol_mask);
- read_unlock(&tasklist_lock);
panic("Out of memory and no killable processes...\n");
}
if (PTR_ERR(p) != -1UL) {
@@ -742,14 +746,12 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask,
killed = 1;
}
out:
- read_unlock(&tasklist_lock);
-
/*
- * Give "p" a good chance of killing itself before we
- * retry to allocate memory unless "p" is current
+ * Give the killed threads a good chance of exiting before trying to
+ * allocate memory again.
*/
- if (killed && !test_thread_flag(TIF_MEMDIE))
- schedule_timeout_uninterruptible(1);
+ if (killed)
+ schedule_timeout_killable(1);
}
/*
@@ -764,6 +766,5 @@ void pagefault_out_of_memory(void)
out_of_memory(NULL, 0, 0, NULL, false);
clear_system_oom();
}
- if (!test_thread_flag(TIF_MEMDIE))
- schedule_timeout_uninterruptible(1);
+ schedule_timeout_killable(1);
}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index e5363f34e025..5ad5ce23c1e0 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -1532,7 +1532,6 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
{
proc_dointvec(table, write, buffer, length, ppos);
- bdi_arm_supers_timer();
return 0;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 4a4f9219683f..c13ea7538891 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -51,7 +51,6 @@
#include <linux/page_cgroup.h>
#include <linux/debugobjects.h>
#include <linux/kmemleak.h>
-#include <linux/memory.h>
#include <linux/compaction.h>
#include <trace/events/kmem.h>
#include <linux/ftrace_event.h>
@@ -219,7 +218,12 @@ EXPORT_SYMBOL(nr_online_nodes);
int page_group_by_mobility_disabled __read_mostly;
-static void set_pageblock_migratetype(struct page *page, int migratetype)
+/*
+ * NOTE:
+ * Don't use set_pageblock_migratetype(page, MIGRATE_ISOLATE) directly.
+ * Instead, use {un}set_pageblock_isolate.
+ */
+void set_pageblock_migratetype(struct page *page, int migratetype)
{
if (unlikely(page_group_by_mobility_disabled))
@@ -580,7 +584,7 @@ static inline void __free_one_page(struct page *page,
combined_idx = buddy_idx & page_idx;
higher_page = page + (combined_idx - page_idx);
buddy_idx = __find_buddy_index(combined_idx, order + 1);
- higher_buddy = page + (buddy_idx - combined_idx);
+ higher_buddy = higher_page + (buddy_idx - combined_idx);
if (page_is_buddy(higher_page, higher_buddy, order + 1)) {
list_add_tail(&page->lru,
&zone->free_area[order].free_list[migratetype]);
@@ -954,7 +958,7 @@ static int move_freepages(struct zone *zone,
return pages_moved;
}
-static int move_freepages_block(struct zone *zone, struct page *page,
+int move_freepages_block(struct zone *zone, struct page *page,
int migratetype)
{
unsigned long start_pfn, end_pfn;
@@ -1158,8 +1162,10 @@ void drain_zone_pages(struct zone *zone, struct per_cpu_pages *pcp)
to_drain = pcp->batch;
else
to_drain = pcp->count;
- free_pcppages_bulk(zone, to_drain, pcp);
- pcp->count -= to_drain;
+ if (to_drain > 0) {
+ free_pcppages_bulk(zone, to_drain, pcp);
+ pcp->count -= to_drain;
+ }
local_irq_restore(flags);
}
#endif
@@ -1529,16 +1535,16 @@ static int __init setup_fail_page_alloc(char *str)
}
__setup("fail_page_alloc=", setup_fail_page_alloc);
-static int should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
+static bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
{
if (order < fail_page_alloc.min_order)
- return 0;
+ return false;
if (gfp_mask & __GFP_NOFAIL)
- return 0;
+ return false;
if (fail_page_alloc.ignore_gfp_highmem && (gfp_mask & __GFP_HIGHMEM))
- return 0;
+ return false;
if (fail_page_alloc.ignore_gfp_wait && (gfp_mask & __GFP_WAIT))
- return 0;
+ return false;
return should_fail(&fail_page_alloc.attr, 1 << order);
}
@@ -1578,9 +1584,9 @@ late_initcall(fail_page_alloc_debugfs);
#else /* CONFIG_FAIL_PAGE_ALLOC */
-static inline int should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
+static inline bool should_fail_alloc_page(gfp_t gfp_mask, unsigned int order)
{
- return 0;
+ return false;
}
#endif /* CONFIG_FAIL_PAGE_ALLOC */
@@ -1594,6 +1600,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
{
/* free_pages my go negative - that's OK */
long min = mark;
+ long lowmem_reserve = z->lowmem_reserve[classzone_idx];
int o;
free_pages -= (1 << order) - 1;
@@ -1602,7 +1609,7 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
if (alloc_flags & ALLOC_HARDER)
min -= min / 4;
- if (free_pages <= min + z->lowmem_reserve[classzone_idx])
+ if (free_pages <= min + lowmem_reserve)
return false;
for (o = 0; o < order; o++) {
/* At the next order, this order's pages become unavailable */
@@ -1617,6 +1624,20 @@ static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
return true;
}
+#ifdef CONFIG_MEMORY_ISOLATION
+static inline unsigned long nr_zone_isolate_freepages(struct zone *zone)
+{
+ if (unlikely(zone->nr_pageblock_isolate))
+ return zone->nr_pageblock_isolate * pageblock_nr_pages;
+ return 0;
+}
+#else
+static inline unsigned long nr_zone_isolate_freepages(struct zone *zone)
+{
+ return 0;
+}
+#endif
+
bool zone_watermark_ok(struct zone *z, int order, unsigned long mark,
int classzone_idx, int alloc_flags)
{
@@ -1632,6 +1653,14 @@ bool zone_watermark_ok_safe(struct zone *z, int order, unsigned long mark,
if (z->percpu_drift_mark && free_pages < z->percpu_drift_mark)
free_pages = zone_page_state_snapshot(z, NR_FREE_PAGES);
+ /*
+ * If the zone has MIGRATE_ISOLATE type free pages, we should consider
+ * it. nr_zone_isolate_freepages is never accurate so kswapd might not
+ * sleep although it could do so. But this is more desirable for memory
+ * hotplug than sleeping which can cause a livelock in the direct
+ * reclaim path.
+ */
+ free_pages -= nr_zone_isolate_freepages(z);
return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags,
free_pages);
}
@@ -1899,6 +1928,17 @@ this_zone_full:
zlc_active = 0;
goto zonelist_scan;
}
+
+ if (page)
+ /*
+ * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was
+ * necessary to allocate the page. The expectation is
+ * that the caller is taking steps that will free more
+ * memory. The caller should avoid the page being used
+ * for !PFMEMALLOC purposes.
+ */
+ page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS);
+
return page;
}
@@ -2062,7 +2102,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
struct page *page;
@@ -2077,7 +2117,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
current->flags |= PF_MEMALLOC;
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
- nodemask, sync_migration);
+ nodemask, sync_migration,
+ contended_compaction);
current->flags &= ~PF_MEMALLOC;
if (*did_some_progress != COMPACT_SKIPPED) {
@@ -2087,8 +2128,8 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
page = get_page_from_freelist(gfp_mask, nodemask,
order, zonelist, high_zoneidx,
- alloc_flags, preferred_zone,
- migratetype);
+ alloc_flags & ~ALLOC_NO_WATERMARKS,
+ preferred_zone, migratetype);
if (page) {
preferred_zone->compact_considered = 0;
preferred_zone->compact_defer_shift = 0;
@@ -2123,7 +2164,7 @@ __alloc_pages_direct_compact(gfp_t gfp_mask, unsigned int order,
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
return NULL;
@@ -2180,8 +2221,8 @@ __alloc_pages_direct_reclaim(gfp_t gfp_mask, unsigned int order,
retry:
page = get_page_from_freelist(gfp_mask, nodemask, order,
zonelist, high_zoneidx,
- alloc_flags, preferred_zone,
- migratetype);
+ alloc_flags & ~ALLOC_NO_WATERMARKS,
+ preferred_zone, migratetype);
/*
* If an allocation failed after direct reclaim, it could be because
@@ -2265,15 +2306,24 @@ gfp_to_alloc_flags(gfp_t gfp_mask)
alloc_flags |= ALLOC_HARDER;
if (likely(!(gfp_mask & __GFP_NOMEMALLOC))) {
- if (!in_interrupt() &&
- ((current->flags & PF_MEMALLOC) ||
- unlikely(test_thread_flag(TIF_MEMDIE))))
+ if (gfp_mask & __GFP_MEMALLOC)
+ alloc_flags |= ALLOC_NO_WATERMARKS;
+ else if (in_serving_softirq() && (current->flags & PF_MEMALLOC))
+ alloc_flags |= ALLOC_NO_WATERMARKS;
+ else if (!in_interrupt() &&
+ ((current->flags & PF_MEMALLOC) ||
+ unlikely(test_thread_flag(TIF_MEMDIE))))
alloc_flags |= ALLOC_NO_WATERMARKS;
}
return alloc_flags;
}
+bool gfp_pfmemalloc_allowed(gfp_t gfp_mask)
+{
+ return !!(gfp_to_alloc_flags(gfp_mask) & ALLOC_NO_WATERMARKS);
+}
+
static inline struct page *
__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
struct zonelist *zonelist, enum zone_type high_zoneidx,
@@ -2287,6 +2337,7 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
unsigned long did_some_progress;
bool sync_migration = false;
bool deferred_compaction = false;
+ bool contended_compaction = false;
/*
* In the slowpath, we sanity check order to avoid ever trying to
@@ -2340,11 +2391,19 @@ rebalance:
/* Allocate without watermarks if the context allows */
if (alloc_flags & ALLOC_NO_WATERMARKS) {
+ /*
+ * Ignore mempolicies if ALLOC_NO_WATERMARKS on the grounds
+ * the allocation is high priority and these type of
+ * allocations are system rather than user orientated
+ */
+ zonelist = node_zonelist(numa_node_id(), gfp_mask);
+
page = __alloc_pages_high_priority(gfp_mask, order,
zonelist, high_zoneidx, nodemask,
preferred_zone, migratetype);
- if (page)
+ if (page) {
goto got_pg;
+ }
}
/* Atomic allocations - we can't balance anything */
@@ -2368,6 +2427,7 @@ rebalance:
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2377,10 +2437,11 @@ rebalance:
/*
* If compaction is deferred for high-order allocations, it is because
* sync compaction recently failed. In this is the case and the caller
- * has requested the system not be heavily disrupted, fail the
- * allocation now instead of entering direct reclaim
+ * requested a movable allocation that does not heavily disrupt the
+ * system then fail the allocation instead of entering direct reclaim.
*/
- if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
+ if ((deferred_compaction || contended_compaction) &&
+ (gfp_mask & __GFP_NO_KSWAPD))
goto nopage;
/* Try direct reclaim and then allocating */
@@ -2451,6 +2512,7 @@ rebalance:
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2463,8 +2525,8 @@ nopage:
got_pg:
if (kmemcheck_enabled)
kmemcheck_pagealloc_alloc(page, order, gfp_mask);
- return page;
+ return page;
}
/*
@@ -3030,7 +3092,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write,
user_zonelist_order = oldval;
} else if (oldval != user_zonelist_order) {
mutex_lock(&zonelists_mutex);
- build_all_zonelists(NULL);
+ build_all_zonelists(NULL, NULL);
mutex_unlock(&zonelists_mutex);
}
}
@@ -3409,14 +3471,21 @@ static void setup_zone_pageset(struct zone *zone);
DEFINE_MUTEX(zonelists_mutex);
/* return values int ....just for stop_machine() */
-static __init_refok int __build_all_zonelists(void *data)
+static int __build_all_zonelists(void *data)
{
int nid;
int cpu;
+ pg_data_t *self = data;
#ifdef CONFIG_NUMA
memset(node_load, 0, sizeof(node_load));
#endif
+
+ if (self && !node_online(self->node_id)) {
+ build_zonelists(self);
+ build_zonelist_cache(self);
+ }
+
for_each_online_node(nid) {
pg_data_t *pgdat = NODE_DATA(nid);
@@ -3461,7 +3530,7 @@ static __init_refok int __build_all_zonelists(void *data)
* Called with zonelists_mutex held always
* unless system_state == SYSTEM_BOOTING.
*/
-void __ref build_all_zonelists(void *data)
+void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone)
{
set_zonelist_order();
@@ -3473,10 +3542,10 @@ void __ref build_all_zonelists(void *data)
/* we have to stop all cpus to guarantee there is no user
of zonelist */
#ifdef CONFIG_MEMORY_HOTPLUG
- if (data)
- setup_zone_pageset((struct zone *)data);
+ if (zone)
+ setup_zone_pageset(zone);
#endif
- stop_machine(__build_all_zonelists, NULL, NULL);
+ stop_machine(__build_all_zonelists, pgdat, NULL);
/* cpuset refresh routine should be here */
}
vm_total_pages = nr_free_pagecache_pages();
@@ -3746,7 +3815,7 @@ static void __meminit zone_init_free_lists(struct zone *zone)
memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)
#endif
-static int zone_batchsize(struct zone *zone)
+static int __meminit zone_batchsize(struct zone *zone)
{
#ifdef CONFIG_MMU
int batch;
@@ -3828,7 +3897,7 @@ static void setup_pagelist_highmark(struct per_cpu_pageset *p,
pcp->batch = PAGE_SHIFT * 8;
}
-static void setup_zone_pageset(struct zone *zone)
+static void __meminit setup_zone_pageset(struct zone *zone)
{
int cpu;
@@ -3901,32 +3970,6 @@ int zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages)
return 0;
}
-static int __zone_pcp_update(void *data)
-{
- struct zone *zone = data;
- int cpu;
- unsigned long batch = zone_batchsize(zone), flags;
-
- for_each_possible_cpu(cpu) {
- struct per_cpu_pageset *pset;
- struct per_cpu_pages *pcp;
-
- pset = per_cpu_ptr(zone->pageset, cpu);
- pcp = &pset->pcp;
-
- local_irq_save(flags);
- free_pcppages_bulk(zone, pcp->count, pcp);
- setup_pageset(pset, batch);
- local_irq_restore(flags);
- }
- return 0;
-}
-
-void zone_pcp_update(struct zone *zone)
-{
- stop_machine(__zone_pcp_update, zone, NULL);
-}
-
static __meminit void zone_pcp_init(struct zone *zone)
{
/*
@@ -3942,7 +3985,7 @@ static __meminit void zone_pcp_init(struct zone *zone)
zone_batchsize(zone));
}
-__meminit int init_currently_empty_zone(struct zone *zone,
+int __meminit init_currently_empty_zone(struct zone *zone,
unsigned long zone_start_pfn,
unsigned long size,
enum memmap_context context)
@@ -4301,7 +4344,7 @@ static inline void setup_usemap(struct pglist_data *pgdat,
#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
/* Initialise the number of pages represented by NR_PAGEBLOCK_BITS */
-static inline void __init set_pageblock_order(void)
+void __init set_pageblock_order(void)
{
unsigned int order;
@@ -4329,7 +4372,7 @@ static inline void __init set_pageblock_order(void)
* include/linux/pageblock-flags.h for the values of pageblock_order based on
* the kernel config
*/
-static inline void set_pageblock_order(void)
+void __init set_pageblock_order(void)
{
}
@@ -4340,6 +4383,8 @@ static inline void set_pageblock_order(void)
* - mark all pages reserved
* - mark all memory queues empty
* - clear the memory bitmaps
+ *
+ * NOTE: pgdat should get zeroed by caller.
*/
static void __paginginit free_area_init_core(struct pglist_data *pgdat,
unsigned long *zones_size, unsigned long *zholes_size)
@@ -4350,9 +4395,8 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
int ret;
pgdat_resize_init(pgdat);
- pgdat->nr_zones = 0;
init_waitqueue_head(&pgdat->kswapd_wait);
- pgdat->kswapd_max_order = 0;
+ init_waitqueue_head(&pgdat->pfmemalloc_wait);
pgdat_page_cgroup_init(pgdat);
for (j = 0; j < MAX_NR_ZONES; j++) {
@@ -4394,6 +4438,11 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
zone->spanned_pages = size;
zone->present_pages = realsize;
+#if defined CONFIG_COMPACTION || defined CONFIG_CMA
+ zone->compact_cached_free_pfn = zone->zone_start_pfn +
+ zone->spanned_pages;
+ zone->compact_cached_free_pfn &= ~(pageblock_nr_pages-1);
+#endif
#ifdef CONFIG_NUMA
zone->node = nid;
zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)
@@ -4408,8 +4457,6 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
zone_pcp_init(zone);
lruvec_init(&zone->lruvec, zone);
- zap_zone_vm_stats(zone);
- zone->flags = 0;
if (!size)
continue;
@@ -4469,6 +4516,9 @@ void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
{
pg_data_t *pgdat = NODE_DATA(nid);
+ /* pg_data_t should be reset to zero when it's allocated */
+ WARN_ON(pgdat->nr_zones || pgdat->classzone_idx);
+
pgdat->node_id = nid;
pgdat->node_start_pfn = node_start_pfn;
calculate_node_totalpages(pgdat, zones_size, zholes_size);
@@ -4750,7 +4800,7 @@ out:
}
/* Any regular memory on that node ? */
-static void check_for_regular_memory(pg_data_t *pgdat)
+static void __init check_for_regular_memory(pg_data_t *pgdat)
{
#ifdef CONFIG_HIGHMEM
enum zone_type zone_type;
@@ -5468,26 +5518,27 @@ void set_pageblock_flags_group(struct page *page, unsigned long flags,
}
/*
- * This is designed as sub function...plz see page_isolation.c also.
- * set/clear page block's type to be ISOLATE.
- * page allocater never alloc memory from ISOLATE block.
+ * This function checks whether pageblock includes unmovable pages or not.
+ * If @count is not zero, it is okay to include less @count unmovable pages
+ *
+ * PageLRU check wihtout isolation or lru_lock could race so that
+ * MIGRATE_MOVABLE block might include unmovable pages. It means you can't
+ * expect this function should be exact.
*/
-
-static int
-__count_immobile_pages(struct zone *zone, struct page *page, int count)
+bool has_unmovable_pages(struct zone *zone, struct page *page, int count)
{
unsigned long pfn, iter, found;
int mt;
/*
* For avoiding noise data, lru_add_drain_all() should be called
- * If ZONE_MOVABLE, the zone never contains immobile pages
+ * If ZONE_MOVABLE, the zone never contains unmovable pages
*/
if (zone_idx(zone) == ZONE_MOVABLE)
- return true;
+ return false;
mt = get_pageblock_migratetype(page);
if (mt == MIGRATE_MOVABLE || is_migrate_cma(mt))
- return true;
+ return false;
pfn = page_to_pfn(page);
for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) {
@@ -5497,11 +5548,18 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count)
continue;
page = pfn_to_page(check);
- if (!page_count(page)) {
+ /*
+ * We can't use page_count without pin a page
+ * because another CPU can free compound page.
+ * This check already skips compound tails of THP
+ * because their page->_count is zero at all time.
+ */
+ if (!atomic_read(&page->_count)) {
if (PageBuddy(page))
iter += (1 << page_order(page)) - 1;
continue;
}
+
if (!PageLRU(page))
found++;
/*
@@ -5518,9 +5576,9 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count)
* page at boot.
*/
if (found > count)
- return false;
+ return true;
}
- return true;
+ return false;
}
bool is_pageblock_removable_nolock(struct page *page)
@@ -5544,77 +5602,7 @@ bool is_pageblock_removable_nolock(struct page *page)
zone->zone_start_pfn + zone->spanned_pages <= pfn)
return false;
- return __count_immobile_pages(zone, page, 0);
-}
-
-int set_migratetype_isolate(struct page *page)
-{
- struct zone *zone;
- unsigned long flags, pfn;
- struct memory_isolate_notify arg;
- int notifier_ret;
- int ret = -EBUSY;
-
- zone = page_zone(page);
-
- spin_lock_irqsave(&zone->lock, flags);
-
- pfn = page_to_pfn(page);
- arg.start_pfn = pfn;
- arg.nr_pages = pageblock_nr_pages;
- arg.pages_found = 0;
-
- /*
- * It may be possible to isolate a pageblock even if the
- * migratetype is not MIGRATE_MOVABLE. The memory isolation
- * notifier chain is used by balloon drivers to return the
- * number of pages in a range that are held by the balloon
- * driver to shrink memory. If all the pages are accounted for
- * by balloons, are free, or on the LRU, isolation can continue.
- * Later, for example, when memory hotplug notifier runs, these
- * pages reported as "can be isolated" should be isolated(freed)
- * by the balloon driver through the memory notifier chain.
- */
- notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
- notifier_ret = notifier_to_errno(notifier_ret);
- if (notifier_ret)
- goto out;
- /*
- * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
- * We just check MOVABLE pages.
- */
- if (__count_immobile_pages(zone, page, arg.pages_found))
- ret = 0;
-
- /*
- * immobile means "not-on-lru" paes. If immobile is larger than
- * removable-by-driver pages reported by notifier, we'll fail.
- */
-
-out:
- if (!ret) {
- set_pageblock_migratetype(page, MIGRATE_ISOLATE);
- move_freepages_block(zone, page, MIGRATE_ISOLATE);
- }
-
- spin_unlock_irqrestore(&zone->lock, flags);
- if (!ret)
- drain_all_pages();
- return ret;
-}
-
-void unset_migratetype_isolate(struct page *page, unsigned migratetype)
-{
- struct zone *zone;
- unsigned long flags;
- zone = page_zone(page);
- spin_lock_irqsave(&zone->lock, flags);
- if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
- goto out;
- set_pageblock_migratetype(page, migratetype);
- move_freepages_block(zone, page, migratetype);
-out:
- spin_unlock_irqrestore(&zone->lock, flags);
+ return !has_unmovable_pages(zone, page, 0);
}
#ifdef CONFIG_CMA
@@ -5869,7 +5857,49 @@ void free_contig_range(unsigned long pfn, unsigned nr_pages)
}
#endif
+#ifdef CONFIG_MEMORY_HOTPLUG
+static int __meminit __zone_pcp_update(void *data)
+{
+ struct zone *zone = data;
+ int cpu;
+ unsigned long batch = zone_batchsize(zone), flags;
+
+ for_each_possible_cpu(cpu) {
+ struct per_cpu_pageset *pset;
+ struct per_cpu_pages *pcp;
+
+ pset = per_cpu_ptr(zone->pageset, cpu);
+ pcp = &pset->pcp;
+
+ local_irq_save(flags);
+ if (pcp->count > 0)
+ free_pcppages_bulk(zone, pcp->count, pcp);
+ setup_pageset(pset, batch);
+ local_irq_restore(flags);
+ }
+ return 0;
+}
+
+void __meminit zone_pcp_update(struct zone *zone)
+{
+ stop_machine(__zone_pcp_update, zone, NULL);
+}
+#endif
+
#ifdef CONFIG_MEMORY_HOTREMOVE
+void zone_pcp_reset(struct zone *zone)
+{
+ unsigned long flags;
+
+ /* avoid races with drain_pages() */
+ local_irq_save(flags);
+ if (zone->pageset != &boot_pageset) {
+ free_percpu(zone->pageset);
+ zone->pageset = &boot_pageset;
+ }
+ local_irq_restore(flags);
+}
+
/*
* All pages in the range must be isolated before calling this.
*/
diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c
index eb750f851395..5ddad0c6daa6 100644
--- a/mm/page_cgroup.c
+++ b/mm/page_cgroup.c
@@ -317,7 +317,7 @@ void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat)
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
+#ifdef CONFIG_MEMCG_SWAP
static DEFINE_MUTEX(swap_cgroup_mutex);
struct swap_cgroup_ctrl {
diff --git a/mm/page_io.c b/mm/page_io.c
index 34f02923744c..78eee32ee486 100644
--- a/mm/page_io.c
+++ b/mm/page_io.c
@@ -17,6 +17,7 @@
#include <linux/swap.h>
#include <linux/bio.h>
#include <linux/swapops.h>
+#include <linux/buffer_head.h>
#include <linux/writeback.h>
#include <linux/frontswap.h>
#include <asm/pgtable.h>
@@ -86,6 +87,98 @@ void end_swap_bio_read(struct bio *bio, int err)
bio_put(bio);
}
+int generic_swapfile_activate(struct swap_info_struct *sis,
+ struct file *swap_file,
+ sector_t *span)
+{
+ struct address_space *mapping = swap_file->f_mapping;
+ struct inode *inode = mapping->host;
+ unsigned blocks_per_page;
+ unsigned long page_no;
+ unsigned blkbits;
+ sector_t probe_block;
+ sector_t last_block;
+ sector_t lowest_block = -1;
+ sector_t highest_block = 0;
+ int nr_extents = 0;
+ int ret;
+
+ blkbits = inode->i_blkbits;
+ blocks_per_page = PAGE_SIZE >> blkbits;
+
+ /*
+ * Map all the blocks into the extent list. This code doesn't try
+ * to be very smart.
+ */
+ probe_block = 0;
+ page_no = 0;
+ last_block = i_size_read(inode) >> blkbits;
+ while ((probe_block + blocks_per_page) <= last_block &&
+ page_no < sis->max) {
+ unsigned block_in_page;
+ sector_t first_block;
+
+ first_block = bmap(inode, probe_block);
+ if (first_block == 0)
+ goto bad_bmap;
+
+ /*
+ * It must be PAGE_SIZE aligned on-disk
+ */
+ if (first_block & (blocks_per_page - 1)) {
+ probe_block++;
+ goto reprobe;
+ }
+
+ for (block_in_page = 1; block_in_page < blocks_per_page;
+ block_in_page++) {
+ sector_t block;
+
+ block = bmap(inode, probe_block + block_in_page);
+ if (block == 0)
+ goto bad_bmap;
+ if (block != first_block + block_in_page) {
+ /* Discontiguity */
+ probe_block++;
+ goto reprobe;
+ }
+ }
+
+ first_block >>= (PAGE_SHIFT - blkbits);
+ if (page_no) { /* exclude the header page */
+ if (first_block < lowest_block)
+ lowest_block = first_block;
+ if (first_block > highest_block)
+ highest_block = first_block;
+ }
+
+ /*
+ * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks
+ */
+ ret = add_swap_extent(sis, page_no, 1, first_block);
+ if (ret < 0)
+ goto out;
+ nr_extents += ret;
+ page_no++;
+ probe_block += blocks_per_page;
+reprobe:
+ continue;
+ }
+ ret = nr_extents;
+ *span = 1 + highest_block - lowest_block;
+ if (page_no == 0)
+ page_no = 1; /* force Empty message */
+ sis->max = page_no;
+ sis->pages = page_no - 1;
+ sis->highest_bit = page_no - 1;
+out:
+ return ret;
+bad_bmap:
+ printk(KERN_ERR "swapon: swapfile has holes\n");
+ ret = -EINVAL;
+ goto out;
+}
+
/*
* We may have stale swap cache pages in memory: notice
* them here and get rid of the unnecessary final write.
@@ -94,6 +187,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
{
struct bio *bio;
int ret = 0, rw = WRITE;
+ struct swap_info_struct *sis = page_swap_info(page);
if (try_to_free_swap(page)) {
unlock_page(page);
@@ -105,6 +199,33 @@ int swap_writepage(struct page *page, struct writeback_control *wbc)
end_page_writeback(page);
goto out;
}
+
+ if (sis->flags & SWP_FILE) {
+ struct kiocb kiocb;
+ struct file *swap_file = sis->swap_file;
+ struct address_space *mapping = swap_file->f_mapping;
+ struct iovec iov = {
+ .iov_base = kmap(page),
+ .iov_len = PAGE_SIZE,
+ };
+
+ init_sync_kiocb(&kiocb, swap_file);
+ kiocb.ki_pos = page_file_offset(page);
+ kiocb.ki_left = PAGE_SIZE;
+ kiocb.ki_nbytes = PAGE_SIZE;
+
+ unlock_page(page);
+ ret = mapping->a_ops->direct_IO(KERNEL_WRITE,
+ &kiocb, &iov,
+ kiocb.ki_pos, 1);
+ kunmap(page);
+ if (ret == PAGE_SIZE) {
+ count_vm_event(PSWPOUT);
+ ret = 0;
+ }
+ return ret;
+ }
+
bio = get_swap_bio(GFP_NOIO, page, end_swap_bio_write);
if (bio == NULL) {
set_page_dirty(page);
@@ -126,6 +247,7 @@ int swap_readpage(struct page *page)
{
struct bio *bio;
int ret = 0;
+ struct swap_info_struct *sis = page_swap_info(page);
VM_BUG_ON(!PageLocked(page));
VM_BUG_ON(PageUptodate(page));
@@ -134,6 +256,17 @@ int swap_readpage(struct page *page)
unlock_page(page);
goto out;
}
+
+ if (sis->flags & SWP_FILE) {
+ struct file *swap_file = sis->swap_file;
+ struct address_space *mapping = swap_file->f_mapping;
+
+ ret = mapping->a_ops->readpage(swap_file, page);
+ if (!ret)
+ count_vm_event(PSWPIN);
+ return ret;
+ }
+
bio = get_swap_bio(GFP_KERNEL, page, end_swap_bio_read);
if (bio == NULL) {
unlock_page(page);
@@ -145,3 +278,15 @@ int swap_readpage(struct page *page)
out:
return ret;
}
+
+int swap_set_page_dirty(struct page *page)
+{
+ struct swap_info_struct *sis = page_swap_info(page);
+
+ if (sis->flags & SWP_FILE) {
+ struct address_space *mapping = sis->swap_file->f_mapping;
+ return mapping->a_ops->set_page_dirty(page);
+ } else {
+ return __set_page_dirty_no_writeback(page);
+ }
+}
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index c9f04774f2b8..247d1f175739 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -5,8 +5,101 @@
#include <linux/mm.h>
#include <linux/page-isolation.h>
#include <linux/pageblock-flags.h>
+#include <linux/memory.h>
#include "internal.h"
+/* called while holding zone->lock */
+static void set_pageblock_isolate(struct page *page)
+{
+ if (get_pageblock_migratetype(page) == MIGRATE_ISOLATE)
+ return;
+
+ set_pageblock_migratetype(page, MIGRATE_ISOLATE);
+ page_zone(page)->nr_pageblock_isolate++;
+}
+
+/* called while holding zone->lock */
+static void restore_pageblock_isolate(struct page *page, int migratetype)
+{
+ struct zone *zone = page_zone(page);
+ if (WARN_ON(get_pageblock_migratetype(page) != MIGRATE_ISOLATE))
+ return;
+
+ BUG_ON(zone->nr_pageblock_isolate <= 0);
+ set_pageblock_migratetype(page, migratetype);
+ zone->nr_pageblock_isolate--;
+}
+
+int set_migratetype_isolate(struct page *page)
+{
+ struct zone *zone;
+ unsigned long flags, pfn;
+ struct memory_isolate_notify arg;
+ int notifier_ret;
+ int ret = -EBUSY;
+
+ zone = page_zone(page);
+
+ spin_lock_irqsave(&zone->lock, flags);
+
+ pfn = page_to_pfn(page);
+ arg.start_pfn = pfn;
+ arg.nr_pages = pageblock_nr_pages;
+ arg.pages_found = 0;
+
+ /*
+ * It may be possible to isolate a pageblock even if the
+ * migratetype is not MIGRATE_MOVABLE. The memory isolation
+ * notifier chain is used by balloon drivers to return the
+ * number of pages in a range that are held by the balloon
+ * driver to shrink memory. If all the pages are accounted for
+ * by balloons, are free, or on the LRU, isolation can continue.
+ * Later, for example, when memory hotplug notifier runs, these
+ * pages reported as "can be isolated" should be isolated(freed)
+ * by the balloon driver through the memory notifier chain.
+ */
+ notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
+ notifier_ret = notifier_to_errno(notifier_ret);
+ if (notifier_ret)
+ goto out;
+ /*
+ * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
+ * We just check MOVABLE pages.
+ */
+ if (!has_unmovable_pages(zone, page, arg.pages_found))
+ ret = 0;
+
+ /*
+ * immobile means "not-on-lru" paes. If immobile is larger than
+ * removable-by-driver pages reported by notifier, we'll fail.
+ */
+
+out:
+ if (!ret) {
+ set_pageblock_isolate(page);
+ move_freepages_block(zone, page, MIGRATE_ISOLATE);
+ }
+
+ spin_unlock_irqrestore(&zone->lock, flags);
+ if (!ret)
+ drain_all_pages();
+ return ret;
+}
+
+void unset_migratetype_isolate(struct page *page, unsigned migratetype)
+{
+ struct zone *zone;
+ unsigned long flags;
+ zone = page_zone(page);
+ spin_lock_irqsave(&zone->lock, flags);
+ if (get_pageblock_migratetype(page) != MIGRATE_ISOLATE)
+ goto out;
+ move_freepages_block(zone, page, migratetype);
+ restore_pageblock_isolate(page, migratetype);
+out:
+ spin_unlock_irqrestore(&zone->lock, flags);
+}
+
static inline struct page *
__first_valid_page(unsigned long pfn, unsigned long nr_pages)
{
diff --git a/mm/shmem.c b/mm/shmem.c
index c15b998e5a86..d4e184e2a38e 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -929,7 +929,8 @@ static struct page *shmem_swapin(swp_entry_t swap, gfp_t gfp,
/* Create a pseudo vma that just contains the policy */
pvma.vm_start = 0;
- pvma.vm_pgoff = index;
+ /* Bias interleave by inode number to distribute better across nodes */
+ pvma.vm_pgoff = index + info->vfs_inode.i_ino;
pvma.vm_ops = NULL;
pvma.vm_policy = spol;
return swapin_readahead(swap, gfp, &pvma, 0);
@@ -942,7 +943,8 @@ static struct page *shmem_alloc_page(gfp_t gfp,
/* Create a pseudo vma that just contains the policy */
pvma.vm_start = 0;
- pvma.vm_pgoff = index;
+ /* Bias interleave by inode number to distribute better across nodes */
+ pvma.vm_pgoff = index + info->vfs_inode.i_ino;
pvma.vm_ops = NULL;
pvma.vm_policy = mpol_shared_policy_lookup(&info->policy, index);
diff --git a/mm/slab.c b/mm/slab.c
index 1fcf3ac94b6c..c6854759bcf1 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -118,12 +118,16 @@
#include <linux/memory.h>
#include <linux/prefetch.h>
+#include <net/sock.h>
+
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/page.h>
#include <trace/events/kmem.h>
+#include "internal.h"
+
/*
* DEBUG - 1 for kmem_cache_create() to honour; SLAB_RED_ZONE & SLAB_POISON.
* 0 for faster, smaller code (especially in the critical paths).
@@ -152,6 +156,12 @@
#define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN
#endif
+/*
+ * true if a page was allocated from pfmemalloc reserves for network-based
+ * swap
+ */
+static bool pfmemalloc_active __read_mostly;
+
/* Legal flag mask for kmem_cache_create(). */
#if DEBUG
# define CREATE_MASK (SLAB_RED_ZONE | \
@@ -257,9 +267,30 @@ struct array_cache {
* Must have this definition in here for the proper
* alignment of array_cache. Also simplifies accessing
* the entries.
+ *
+ * Entries should not be directly dereferenced as
+ * entries belonging to slabs marked pfmemalloc will
+ * have the lower bits set SLAB_OBJ_PFMEMALLOC
*/
};
+#define SLAB_OBJ_PFMEMALLOC 1
+static inline bool is_obj_pfmemalloc(void *objp)
+{
+ return (unsigned long)objp & SLAB_OBJ_PFMEMALLOC;
+}
+
+static inline void set_obj_pfmemalloc(void **objp)
+{
+ *objp = (void *)((unsigned long)*objp | SLAB_OBJ_PFMEMALLOC);
+ return;
+}
+
+static inline void clear_obj_pfmemalloc(void **objp)
+{
+ *objp = (void *)((unsigned long)*objp & ~SLAB_OBJ_PFMEMALLOC);
+}
+
/*
* bootstrap: The caches do not work without cpuarrays anymore, but the
* cpuarrays are allocated from the generic caches...
@@ -900,6 +931,124 @@ static struct array_cache *alloc_arraycache(int node, int entries,
return nc;
}
+static inline bool is_slab_pfmemalloc(struct slab *slabp)
+{
+ struct page *page = virt_to_page(slabp->s_mem);
+
+ return PageSlabPfmemalloc(page);
+}
+
+/* Clears pfmemalloc_active if no slabs have pfmalloc set */
+static void recheck_pfmemalloc_active(struct kmem_cache *cachep,
+ struct array_cache *ac)
+{
+ struct kmem_list3 *l3 = cachep->nodelists[numa_mem_id()];
+ struct slab *slabp;
+ unsigned long flags;
+
+ if (!pfmemalloc_active)
+ return;
+
+ spin_lock_irqsave(&l3->list_lock, flags);
+ list_for_each_entry(slabp, &l3->slabs_full, list)
+ if (is_slab_pfmemalloc(slabp))
+ goto out;
+
+ list_for_each_entry(slabp, &l3->slabs_partial, list)
+ if (is_slab_pfmemalloc(slabp))
+ goto out;
+
+ list_for_each_entry(slabp, &l3->slabs_free, list)
+ if (is_slab_pfmemalloc(slabp))
+ goto out;
+
+ pfmemalloc_active = false;
+out:
+ spin_unlock_irqrestore(&l3->list_lock, flags);
+}
+
+static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
+ gfp_t flags, bool force_refill)
+{
+ int i;
+ void *objp = ac->entry[--ac->avail];
+
+ /* Ensure the caller is allowed to use objects from PFMEMALLOC slab */
+ if (unlikely(is_obj_pfmemalloc(objp))) {
+ struct kmem_list3 *l3;
+
+ if (gfp_pfmemalloc_allowed(flags)) {
+ clear_obj_pfmemalloc(&objp);
+ return objp;
+ }
+
+ /* The caller cannot use PFMEMALLOC objects, find another one */
+ for (i = 0; i < ac->avail; i++) {
+ /* If a !PFMEMALLOC object is found, swap them */
+ if (!is_obj_pfmemalloc(ac->entry[i])) {
+ objp = ac->entry[i];
+ ac->entry[i] = ac->entry[ac->avail];
+ ac->entry[ac->avail] = objp;
+ return objp;
+ }
+ }
+
+ /*
+ * If there are empty slabs on the slabs_free list and we are
+ * being forced to refill the cache, mark this one !pfmemalloc.
+ */
+ l3 = cachep->nodelists[numa_mem_id()];
+ if (!list_empty(&l3->slabs_free) && force_refill) {
+ struct slab *slabp = virt_to_slab(objp);
+ ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem));
+ clear_obj_pfmemalloc(&objp);
+ recheck_pfmemalloc_active(cachep, ac);
+ return objp;
+ }
+
+ /* No !PFMEMALLOC objects available */
+ ac->avail++;
+ objp = NULL;
+ }
+
+ return objp;
+}
+
+static inline void *ac_get_obj(struct kmem_cache *cachep,
+ struct array_cache *ac, gfp_t flags, bool force_refill)
+{
+ void *objp;
+
+ if (unlikely(sk_memalloc_socks()))
+ objp = __ac_get_obj(cachep, ac, flags, force_refill);
+ else
+ objp = ac->entry[--ac->avail];
+
+ return objp;
+}
+
+static void *__ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
+ void *objp)
+{
+ if (unlikely(pfmemalloc_active)) {
+ /* Some pfmemalloc slabs exist, check if this is one */
+ struct page *page = virt_to_head_page(objp);
+ if (PageSlabPfmemalloc(page))
+ set_obj_pfmemalloc(&objp);
+ }
+
+ return objp;
+}
+
+static inline void ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
+ void *objp)
+{
+ if (unlikely(sk_memalloc_socks()))
+ objp = __ac_put_obj(cachep, ac, objp);
+
+ ac->entry[ac->avail++] = objp;
+}
+
/*
* Transfer objects in one arraycache to another.
* Locking must be handled by the caller.
@@ -1076,7 +1225,7 @@ static inline int cache_free_alien(struct kmem_cache *cachep, void *objp)
STATS_INC_ACOVERFLOW(cachep);
__drain_alien_cache(cachep, alien, nodeid);
}
- alien->entry[alien->avail++] = objp;
+ ac_put_obj(cachep, alien, objp);
spin_unlock(&alien->lock);
} else {
spin_lock(&(cachep->nodelists[nodeid])->list_lock);
@@ -1759,6 +1908,10 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
return NULL;
}
+ /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */
+ if (unlikely(page->pfmemalloc))
+ pfmemalloc_active = true;
+
nr_pages = (1 << cachep->gfporder);
if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
add_zone_page_state(page_zone(page),
@@ -1766,9 +1919,13 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
else
add_zone_page_state(page_zone(page),
NR_SLAB_UNRECLAIMABLE, nr_pages);
- for (i = 0; i < nr_pages; i++)
+ for (i = 0; i < nr_pages; i++) {
__SetPageSlab(page + i);
+ if (page->pfmemalloc)
+ SetPageSlabPfmemalloc(page + i);
+ }
+
if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) {
kmemcheck_alloc_shadow(page, cachep->gfporder, flags, nodeid);
@@ -1800,6 +1957,7 @@ static void kmem_freepages(struct kmem_cache *cachep, void *addr)
NR_SLAB_UNRECLAIMABLE, nr_freed);
while (i--) {
BUG_ON(!PageSlab(page));
+ __ClearPageSlabPfmemalloc(page);
__ClearPageSlab(page);
page++;
}
@@ -3015,16 +3173,19 @@ bad:
#define check_slabp(x,y) do { } while(0)
#endif
-static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
+static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags,
+ bool force_refill)
{
int batchcount;
struct kmem_list3 *l3;
struct array_cache *ac;
int node;
-retry:
check_irq_off();
node = numa_mem_id();
+ if (unlikely(force_refill))
+ goto force_grow;
+retry:
ac = cpu_cache_get(cachep);
batchcount = ac->batchcount;
if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
@@ -3074,8 +3235,8 @@ retry:
STATS_INC_ACTIVE(cachep);
STATS_SET_HIGH(cachep);
- ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,
- node);
+ ac_put_obj(cachep, ac, slab_get_obj(cachep, slabp,
+ node));
}
check_slabp(cachep, slabp);
@@ -3094,18 +3255,23 @@ alloc_done:
if (unlikely(!ac->avail)) {
int x;
+force_grow:
x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
/* cache_grow can reenable interrupts, then ac could change. */
ac = cpu_cache_get(cachep);
- if (!x && ac->avail == 0) /* no objects in sight? abort */
+ node = numa_mem_id();
+
+ /* no objects in sight? abort */
+ if (!x && (ac->avail == 0 || force_refill))
return NULL;
if (!ac->avail) /* objects refilled by interrupt? */
goto retry;
}
ac->touched = 1;
- return ac->entry[--ac->avail];
+
+ return ac_get_obj(cachep, ac, flags, force_refill);
}
static inline void cache_alloc_debugcheck_before(struct kmem_cache *cachep,
@@ -3187,23 +3353,35 @@ static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{
void *objp;
struct array_cache *ac;
+ bool force_refill = false;
check_irq_off();
ac = cpu_cache_get(cachep);
if (likely(ac->avail)) {
- STATS_INC_ALLOCHIT(cachep);
ac->touched = 1;
- objp = ac->entry[--ac->avail];
- } else {
- STATS_INC_ALLOCMISS(cachep);
- objp = cache_alloc_refill(cachep, flags);
+ objp = ac_get_obj(cachep, ac, flags, false);
+
/*
- * the 'ac' may be updated by cache_alloc_refill(),
- * and kmemleak_erase() requires its correct value.
+ * Allow for the possibility all avail objects are not allowed
+ * by the current flags
*/
- ac = cpu_cache_get(cachep);
+ if (objp) {
+ STATS_INC_ALLOCHIT(cachep);
+ goto out;
+ }
+ force_refill = true;
}
+
+ STATS_INC_ALLOCMISS(cachep);
+ objp = cache_alloc_refill(cachep, flags, force_refill);
+ /*
+ * the 'ac' may be updated by cache_alloc_refill(),
+ * and kmemleak_erase() requires its correct value.
+ */
+ ac = cpu_cache_get(cachep);
+
+out:
/*
* To avoid a false negative, if an object that is in one of the
* per-CPU caches is leaked, we need to make sure kmemleak doesn't
@@ -3525,9 +3703,12 @@ static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
struct kmem_list3 *l3;
for (i = 0; i < nr_objects; i++) {
- void *objp = objpp[i];
+ void *objp;
struct slab *slabp;
+ clear_obj_pfmemalloc(&objpp[i]);
+ objp = objpp[i];
+
slabp = virt_to_slab(objp);
l3 = cachep->nodelists[node];
list_del(&slabp->list);
@@ -3645,7 +3826,7 @@ static inline void __cache_free(struct kmem_cache *cachep, void *objp,
cache_flusharray(cachep, ac);
}
- ac->entry[ac->avail++] = objp;
+ ac_put_obj(cachep, ac, objp);
}
/**
diff --git a/mm/slub.c b/mm/slub.c
index e517d435e5dc..2fdd96f9e998 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -34,6 +34,8 @@
#include <trace/events/kmem.h>
+#include "internal.h"
+
/*
* Lock order:
* 1. slab_mutex (Global Mutex)
@@ -1354,6 +1356,8 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
inc_slabs_node(s, page_to_nid(page), page->objects);
page->slab = s;
__SetPageSlab(page);
+ if (page->pfmemalloc)
+ SetPageSlabPfmemalloc(page);
start = page_address(page);
@@ -1397,6 +1401,7 @@ static void __free_slab(struct kmem_cache *s, struct page *page)
NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE,
-pages);
+ __ClearPageSlabPfmemalloc(page);
__ClearPageSlab(page);
reset_page_mapcount(page);
if (current->reclaim_state)
@@ -1519,12 +1524,13 @@ static inline void *acquire_slab(struct kmem_cache *s,
}
static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
+static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags);
/*
* Try to allocate a partial slab from a specific node.
*/
-static void *get_partial_node(struct kmem_cache *s,
- struct kmem_cache_node *n, struct kmem_cache_cpu *c)
+static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
+ struct kmem_cache_cpu *c, gfp_t flags)
{
struct page *page, *page2;
void *object = NULL;
@@ -1540,9 +1546,13 @@ static void *get_partial_node(struct kmem_cache *s,
spin_lock(&n->list_lock);
list_for_each_entry_safe(page, page2, &n->partial, lru) {
- void *t = acquire_slab(s, n, page, object == NULL);
+ void *t;
int available;
+ if (!pfmemalloc_match(page, flags))
+ continue;
+
+ t = acquire_slab(s, n, page, object == NULL);
if (!t)
break;
@@ -1609,7 +1619,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
n->nr_partial > s->min_partial) {
- object = get_partial_node(s, n, c);
+ object = get_partial_node(s, n, c, flags);
if (object) {
/*
* Return the object even if
@@ -1638,7 +1648,7 @@ static void *get_partial(struct kmem_cache *s, gfp_t flags, int node,
void *object;
int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;
- object = get_partial_node(s, get_node(s, searchnode), c);
+ object = get_partial_node(s, get_node(s, searchnode), c, flags);
if (object || node != NUMA_NO_NODE)
return object;
@@ -2126,6 +2136,14 @@ static inline void *new_slab_objects(struct kmem_cache *s, gfp_t flags,
return freelist;
}
+static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags)
+{
+ if (unlikely(PageSlabPfmemalloc(page)))
+ return gfp_pfmemalloc_allowed(gfpflags);
+
+ return true;
+}
+
/*
* Check the page->freelist of a page and either transfer the freelist to the per cpu freelist
* or deactivate the page.
@@ -2206,6 +2224,18 @@ redo:
goto new_slab;
}
+ /*
+ * By rights, we should be searching for a slab page that was
+ * PFMEMALLOC but right now, we are losing the pfmemalloc
+ * information when the page leaves the per-cpu allocator
+ */
+ if (unlikely(!pfmemalloc_match(page, gfpflags))) {
+ deactivate_slab(s, page, c->freelist);
+ c->page = NULL;
+ c->freelist = NULL;
+ goto new_slab;
+ }
+
/* must check again c->freelist in case of cpu migration or IRQ */
freelist = c->freelist;
if (freelist)
@@ -2256,11 +2286,11 @@ new_slab:
}
page = c->page;
- if (likely(!kmem_cache_debug(s)))
+ if (likely(!kmem_cache_debug(s) && pfmemalloc_match(page, gfpflags)))
goto load_freelist;
/* Only entered in the debug case */
- if (!alloc_debug_processing(s, page, freelist, addr))
+ if (kmem_cache_debug(s) && !alloc_debug_processing(s, page, freelist, addr))
goto new_slab; /* Slab failed checks. Next slab needed */
deactivate_slab(s, page, get_freepointer(s, freelist));
@@ -2313,7 +2343,6 @@ redo:
object = c->freelist;
page = c->page;
if (unlikely(!object || !node_match(page, node)))
-
object = __slab_alloc(s, gfpflags, node, addr, c);
else {
diff --git a/mm/sparse.c b/mm/sparse.c
index c7bb952400c8..fac95f2888f2 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -65,21 +65,18 @@ static struct mem_section noinline __init_refok *sparse_index_alloc(int nid)
if (slab_is_available()) {
if (node_state(nid, N_HIGH_MEMORY))
- section = kmalloc_node(array_size, GFP_KERNEL, nid);
+ section = kzalloc_node(array_size, GFP_KERNEL, nid);
else
- section = kmalloc(array_size, GFP_KERNEL);
- } else
+ section = kzalloc(array_size, GFP_KERNEL);
+ } else {
section = alloc_bootmem_node(NODE_DATA(nid), array_size);
-
- if (section)
- memset(section, 0, array_size);
+ }
return section;
}
static int __meminit sparse_index_init(unsigned long section_nr, int nid)
{
- static DEFINE_SPINLOCK(index_init_lock);
unsigned long root = SECTION_NR_TO_ROOT(section_nr);
struct mem_section *section;
int ret = 0;
@@ -90,20 +87,9 @@ static int __meminit sparse_index_init(unsigned long section_nr, int nid)
section = sparse_index_alloc(nid);
if (!section)
return -ENOMEM;
- /*
- * This lock keeps two different sections from
- * reallocating for the same index
- */
- spin_lock(&index_init_lock);
-
- if (mem_section[root]) {
- ret = -EEXIST;
- goto out;
- }
mem_section[root] = section;
-out:
- spin_unlock(&index_init_lock);
+
return ret;
}
#else /* !SPARSEMEM_EXTREME */
@@ -132,6 +118,8 @@ int __section_nr(struct mem_section* ms)
break;
}
+ VM_BUG_ON(root_nr == NR_SECTION_ROOTS);
+
return (root_nr * SECTIONS_PER_ROOT) + (ms - root);
}
@@ -493,6 +481,9 @@ void __init sparse_init(void)
struct page **map_map;
#endif
+ /* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */
+ set_pageblock_order();
+
/*
* map is using big page (aka 2M in x86 64 bit)
* usemap is less one page (aka 24 bytes)
diff --git a/mm/swap.c b/mm/swap.c
index 4e7e2ec67078..77825883298f 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -236,6 +236,58 @@ void put_pages_list(struct list_head *pages)
}
EXPORT_SYMBOL(put_pages_list);
+/*
+ * get_kernel_pages() - pin kernel pages in memory
+ * @kiov: An array of struct kvec structures
+ * @nr_segs: number of segments to pin
+ * @write: pinning for read/write, currently ignored
+ * @pages: array that receives pointers to the pages pinned.
+ * Should be at least nr_segs long.
+ *
+ * Returns number of pages pinned. This may be fewer than the number
+ * requested. If nr_pages is 0 or negative, returns 0. If no pages
+ * were pinned, returns -errno. Each page returned must be released
+ * with a put_page() call when it is finished with.
+ */
+int get_kernel_pages(const struct kvec *kiov, int nr_segs, int write,
+ struct page **pages)
+{
+ int seg;
+
+ for (seg = 0; seg < nr_segs; seg++) {
+ if (WARN_ON(kiov[seg].iov_len != PAGE_SIZE))
+ return seg;
+
+ pages[seg] = kmap_to_page(kiov[seg].iov_base);
+ page_cache_get(pages[seg]);
+ }
+
+ return seg;
+}
+EXPORT_SYMBOL_GPL(get_kernel_pages);
+
+/*
+ * get_kernel_page() - pin a kernel page in memory
+ * @start: starting kernel address
+ * @write: pinning for read/write, currently ignored
+ * @pages: array that receives pointer to the page pinned.
+ * Must be at least nr_segs long.
+ *
+ * Returns 1 if page is pinned. If the page was not pinned, returns
+ * -errno. The page returned must be released with a put_page() call
+ * when it is finished with.
+ */
+int get_kernel_page(unsigned long start, int write, struct page **pages)
+{
+ const struct kvec kiov = {
+ .iov_base = (void *)start,
+ .iov_len = PAGE_SIZE
+ };
+
+ return get_kernel_pages(&kiov, 1, write, pages);
+}
+EXPORT_SYMBOL_GPL(get_kernel_page);
+
static void pagevec_lru_move_fn(struct pagevec *pvec,
void (*move_fn)(struct page *page, struct lruvec *lruvec, void *arg),
void *arg)
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 4c5ff7f284d9..0cb36fb1f61c 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/backing-dev.h>
+#include <linux/blkdev.h>
#include <linux/pagevec.h>
#include <linux/migrate.h>
#include <linux/page_cgroup.h>
@@ -26,7 +27,7 @@
*/
static const struct address_space_operations swap_aops = {
.writepage = swap_writepage,
- .set_page_dirty = __set_page_dirty_no_writeback,
+ .set_page_dirty = swap_set_page_dirty,
.migratepage = migrate_page,
};
@@ -376,6 +377,7 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
unsigned long offset = swp_offset(entry);
unsigned long start_offset, end_offset;
unsigned long mask = (1UL << page_cluster) - 1;
+ struct blk_plug plug;
/* Read a page_cluster sized and aligned cluster around offset. */
start_offset = offset & ~mask;
@@ -383,6 +385,7 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
if (!start_offset) /* First page is swap header. */
start_offset++;
+ blk_start_plug(&plug);
for (offset = start_offset; offset <= end_offset ; offset++) {
/* Ok, do the async read-ahead now */
page = read_swap_cache_async(swp_entry(swp_type(entry), offset),
@@ -391,6 +394,8 @@ struct page *swapin_readahead(swp_entry_t entry, gfp_t gfp_mask,
continue;
page_cache_release(page);
}
+ blk_finish_plug(&plug);
+
lru_add_drain(); /* Push any new pages onto the LRU now */
return read_swap_cache_async(entry, gfp_mask, vma, addr);
}
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 71373d03fcee..14e254c768fc 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -33,6 +33,7 @@
#include <linux/oom.h>
#include <linux/frontswap.h>
#include <linux/swapfile.h>
+#include <linux/export.h>
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
@@ -548,7 +549,6 @@ static unsigned char swap_entry_free(struct swap_info_struct *p,
/* free if no reference */
if (!usage) {
- struct gendisk *disk = p->bdev->bd_disk;
if (offset < p->lowest_bit)
p->lowest_bit = offset;
if (offset > p->highest_bit)
@@ -559,9 +559,12 @@ static unsigned char swap_entry_free(struct swap_info_struct *p,
nr_swap_pages++;
p->inuse_pages--;
frontswap_invalidate_page(p->type, offset);
- if ((p->flags & SWP_BLKDEV) &&
- disk->fops->swap_slot_free_notify)
- disk->fops->swap_slot_free_notify(p->bdev, offset);
+ if (p->flags & SWP_BLKDEV) {
+ struct gendisk *disk = p->bdev->bd_disk;
+ if (disk->fops->swap_slot_free_notify)
+ disk->fops->swap_slot_free_notify(p->bdev,
+ offset);
+ }
}
return usage;
@@ -832,8 +835,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd,
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) {
- if (ret > 0)
- mem_cgroup_cancel_charge_swapin(memcg);
+ mem_cgroup_cancel_charge_swapin(memcg);
ret = 0;
goto out;
}
@@ -1328,6 +1330,14 @@ static void destroy_swap_extents(struct swap_info_struct *sis)
list_del(&se->list);
kfree(se);
}
+
+ if (sis->flags & SWP_FILE) {
+ struct file *swap_file = sis->swap_file;
+ struct address_space *mapping = swap_file->f_mapping;
+
+ sis->flags &= ~SWP_FILE;
+ mapping->a_ops->swap_deactivate(swap_file);
+ }
}
/*
@@ -1336,7 +1346,7 @@ static void destroy_swap_extents(struct swap_info_struct *sis)
*
* This function rather assumes that it is called in ascending page order.
*/
-static int
+int
add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
unsigned long nr_pages, sector_t start_block)
{
@@ -1409,98 +1419,28 @@ add_swap_extent(struct swap_info_struct *sis, unsigned long start_page,
*/
static int setup_swap_extents(struct swap_info_struct *sis, sector_t *span)
{
- struct inode *inode;
- unsigned blocks_per_page;
- unsigned long page_no;
- unsigned blkbits;
- sector_t probe_block;
- sector_t last_block;
- sector_t lowest_block = -1;
- sector_t highest_block = 0;
- int nr_extents = 0;
+ struct file *swap_file = sis->swap_file;
+ struct address_space *mapping = swap_file->f_mapping;
+ struct inode *inode = mapping->host;
int ret;
- inode = sis->swap_file->f_mapping->host;
if (S_ISBLK(inode->i_mode)) {
ret = add_swap_extent(sis, 0, sis->max, 0);
*span = sis->pages;
- goto out;
+ return ret;
}
- blkbits = inode->i_blkbits;
- blocks_per_page = PAGE_SIZE >> blkbits;
-
- /*
- * Map all the blocks into the extent list. This code doesn't try
- * to be very smart.
- */
- probe_block = 0;
- page_no = 0;
- last_block = i_size_read(inode) >> blkbits;
- while ((probe_block + blocks_per_page) <= last_block &&
- page_no < sis->max) {
- unsigned block_in_page;
- sector_t first_block;
-
- first_block = bmap(inode, probe_block);
- if (first_block == 0)
- goto bad_bmap;
-
- /*
- * It must be PAGE_SIZE aligned on-disk
- */
- if (first_block & (blocks_per_page - 1)) {
- probe_block++;
- goto reprobe;
- }
-
- for (block_in_page = 1; block_in_page < blocks_per_page;
- block_in_page++) {
- sector_t block;
-
- block = bmap(inode, probe_block + block_in_page);
- if (block == 0)
- goto bad_bmap;
- if (block != first_block + block_in_page) {
- /* Discontiguity */
- probe_block++;
- goto reprobe;
- }
- }
-
- first_block >>= (PAGE_SHIFT - blkbits);
- if (page_no) { /* exclude the header page */
- if (first_block < lowest_block)
- lowest_block = first_block;
- if (first_block > highest_block)
- highest_block = first_block;
+ if (mapping->a_ops->swap_activate) {
+ ret = mapping->a_ops->swap_activate(sis, swap_file, span);
+ if (!ret) {
+ sis->flags |= SWP_FILE;
+ ret = add_swap_extent(sis, 0, sis->max, 0);
+ *span = sis->pages;
}
+ return ret;
+ }
- /*
- * We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks
- */
- ret = add_swap_extent(sis, page_no, 1, first_block);
- if (ret < 0)
- goto out;
- nr_extents += ret;
- page_no++;
- probe_block += blocks_per_page;
-reprobe:
- continue;
- }
- ret = nr_extents;
- *span = 1 + highest_block - lowest_block;
- if (page_no == 0)
- page_no = 1; /* force Empty message */
- sis->max = page_no;
- sis->pages = page_no - 1;
- sis->highest_bit = page_no - 1;
-out:
- return ret;
-bad_bmap:
- printk(KERN_ERR "swapon: swapfile has holes\n");
- ret = -EINVAL;
- goto out;
+ return generic_swapfile_activate(sis, swap_file, span);
}
static void enable_swap_info(struct swap_info_struct *p, int prio,
@@ -2285,6 +2225,31 @@ int swapcache_prepare(swp_entry_t entry)
return __swap_duplicate(entry, SWAP_HAS_CACHE);
}
+struct swap_info_struct *page_swap_info(struct page *page)
+{
+ swp_entry_t swap = { .val = page_private(page) };
+ BUG_ON(!PageSwapCache(page));
+ return swap_info[swp_type(swap)];
+}
+
+/*
+ * out-of-line __page_file_ methods to avoid include hell.
+ */
+struct address_space *__page_file_mapping(struct page *page)
+{
+ VM_BUG_ON(!PageSwapCache(page));
+ return page_swap_info(page)->swap_file->f_mapping;
+}
+EXPORT_SYMBOL_GPL(__page_file_mapping);
+
+pgoff_t __page_file_index(struct page *page)
+{
+ swp_entry_t swap = { .val = page_private(page) };
+ VM_BUG_ON(!PageSwapCache(page));
+ return swp_offset(swap);
+}
+EXPORT_SYMBOL_GPL(__page_file_index);
+
/*
* add_swap_count_continuation - called when a swap count is duplicated
* beyond SWAP_MAP_MAX, it allocates a new page and links that to the entry's
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index e03f4c7307a5..2bb90b1d241c 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -413,11 +413,11 @@ nocache:
if (addr + size - 1 < addr)
goto overflow;
- n = rb_next(&first->rb_node);
- if (n)
- first = rb_entry(n, struct vmap_area, rb_node);
- else
+ if (list_is_last(&first->list, &vmap_area_list))
goto found;
+
+ first = list_entry(first->list.next,
+ struct vmap_area, list);
}
found:
@@ -904,6 +904,14 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask)
BUG_ON(size & ~PAGE_MASK);
BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
+ if (WARN_ON(size == 0)) {
+ /*
+ * Allocating 0 bytes isn't what caller wants since
+ * get_order(0) returns funny result. Just warn and terminate
+ * early.
+ */
+ return NULL;
+ }
order = get_order(size);
again:
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 347b3ff2a478..99b434b674c0 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -133,7 +133,7 @@ long vm_total_pages; /* The total number of pages which the VM controls */
static LIST_HEAD(shrinker_list);
static DECLARE_RWSEM(shrinker_rwsem);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
static bool global_reclaim(struct scan_control *sc)
{
return !sc->target_mem_cgroup;
@@ -687,6 +687,7 @@ static unsigned long shrink_page_list(struct list_head *page_list,
cond_resched();
+ mem_cgroup_uncharge_start();
while (!list_empty(page_list)) {
enum page_references references;
struct address_space *mapping;
@@ -720,9 +721,41 @@ static unsigned long shrink_page_list(struct list_head *page_list,
(PageSwapCache(page) && (sc->gfp_mask & __GFP_IO));
if (PageWriteback(page)) {
- nr_writeback++;
- unlock_page(page);
- goto keep;
+ /*
+ * memcg doesn't have any dirty pages throttling so we
+ * could easily OOM just because too many pages are in
+ * writeback and there is nothing else to reclaim.
+ *
+ * Check __GFP_IO, certainly because a loop driver
+ * thread might enter reclaim, and deadlock if it waits
+ * on a page for which it is needed to do the write
+ * (loop masks off __GFP_IO|__GFP_FS for this reason);
+ * but more thought would probably show more reasons.
+ *
+ * Don't require __GFP_FS, since we're not going into
+ * the FS, just waiting on its writeback completion.
+ * Worryingly, ext4 gfs2 and xfs allocate pages with
+ * grab_cache_page_write_begin(,,AOP_FLAG_NOFS), so
+ * testing may_enter_fs here is liable to OOM on them.
+ */
+ if (global_reclaim(sc) ||
+ !PageReclaim(page) || !(sc->gfp_mask & __GFP_IO)) {
+ /*
+ * This is slightly racy - end_page_writeback()
+ * might have just cleared PageReclaim, then
+ * setting PageReclaim here end up interpreted
+ * as PageReadahead - but that does not matter
+ * enough to care. What we do want is for this
+ * page to have PageReclaim set next time memcg
+ * reclaim reaches the tests above, so it will
+ * then wait_on_page_writeback() to avoid OOM;
+ * and it's also appropriate in global reclaim.
+ */
+ SetPageReclaim(page);
+ nr_writeback++;
+ goto keep_locked;
+ }
+ wait_on_page_writeback(page);
}
references = page_check_references(page, sc);
@@ -921,6 +954,7 @@ keep:
list_splice(&ret_pages, page_list);
count_vm_events(PGACTIVATE, pgactivate);
+ mem_cgroup_uncharge_end();
*ret_nr_dirty += nr_dirty;
*ret_nr_writeback += nr_writeback;
return nr_reclaimed;
@@ -2112,6 +2146,83 @@ out:
return 0;
}
+static bool pfmemalloc_watermark_ok(pg_data_t *pgdat)
+{
+ struct zone *zone;
+ unsigned long pfmemalloc_reserve = 0;
+ unsigned long free_pages = 0;
+ int i;
+ bool wmark_ok;
+
+ for (i = 0; i <= ZONE_NORMAL; i++) {
+ zone = &pgdat->node_zones[i];
+ pfmemalloc_reserve += min_wmark_pages(zone);
+ free_pages += zone_page_state(zone, NR_FREE_PAGES);
+ }
+
+ wmark_ok = free_pages > pfmemalloc_reserve / 2;
+
+ /* kswapd must be awake if processes are being throttled */
+ if (!wmark_ok && waitqueue_active(&pgdat->kswapd_wait)) {
+ pgdat->classzone_idx = min(pgdat->classzone_idx,
+ (enum zone_type)ZONE_NORMAL);
+ wake_up_interruptible(&pgdat->kswapd_wait);
+ }
+
+ return wmark_ok;
+}
+
+/*
+ * Throttle direct reclaimers if backing storage is backed by the network
+ * and the PFMEMALLOC reserve for the preferred node is getting dangerously
+ * depleted. kswapd will continue to make progress and wake the processes
+ * when the low watermark is reached
+ */
+static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist,
+ nodemask_t *nodemask)
+{
+ struct zone *zone;
+ int high_zoneidx = gfp_zone(gfp_mask);
+ pg_data_t *pgdat;
+
+ /*
+ * Kernel threads should not be throttled as they may be indirectly
+ * responsible for cleaning pages necessary for reclaim to make forward
+ * progress. kjournald for example may enter direct reclaim while
+ * committing a transaction where throttling it could forcing other
+ * processes to block on log_wait_commit().
+ */
+ if (current->flags & PF_KTHREAD)
+ return;
+
+ /* Check if the pfmemalloc reserves are ok */
+ first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone);
+ pgdat = zone->zone_pgdat;
+ if (pfmemalloc_watermark_ok(pgdat))
+ return;
+
+ /* Account for the throttling */
+ count_vm_event(PGSCAN_DIRECT_THROTTLE);
+
+ /*
+ * If the caller cannot enter the filesystem, it's possible that it
+ * is due to the caller holding an FS lock or performing a journal
+ * transaction in the case of a filesystem like ext[3|4]. In this case,
+ * it is not safe to block on pfmemalloc_wait as kswapd could be
+ * blocked waiting on the same lock. Instead, throttle for up to a
+ * second before continuing.
+ */
+ if (!(gfp_mask & __GFP_FS)) {
+ wait_event_interruptible_timeout(pgdat->pfmemalloc_wait,
+ pfmemalloc_watermark_ok(pgdat), HZ);
+ return;
+ }
+
+ /* Throttle until kswapd wakes the process */
+ wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
+ pfmemalloc_watermark_ok(pgdat));
+}
+
unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
gfp_t gfp_mask, nodemask_t *nodemask)
{
@@ -2131,6 +2242,15 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
.gfp_mask = sc.gfp_mask,
};
+ throttle_direct_reclaim(gfp_mask, zonelist, nodemask);
+
+ /*
+ * Do not enter reclaim if fatal signal is pending. 1 is returned so
+ * that the page allocator does not consider triggering OOM
+ */
+ if (fatal_signal_pending(current))
+ return 1;
+
trace_mm_vmscan_direct_reclaim_begin(order,
sc.may_writepage,
gfp_mask);
@@ -2142,7 +2262,7 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
return nr_reclaimed;
}
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR
+#ifdef CONFIG_MEMCG
unsigned long mem_cgroup_shrink_node_zone(struct mem_cgroup *memcg,
gfp_t gfp_mask, bool noswap,
@@ -2275,8 +2395,13 @@ static bool pgdat_balanced(pg_data_t *pgdat, unsigned long balanced_pages,
return balanced_pages >= (present_pages >> 2);
}
-/* is kswapd sleeping prematurely? */
-static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
+/*
+ * Prepare kswapd for sleeping. This verifies that there are no processes
+ * waiting in throttle_direct_reclaim() and that watermarks have been met.
+ *
+ * Returns true if kswapd is ready to sleep
+ */
+static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, long remaining,
int classzone_idx)
{
int i;
@@ -2285,7 +2410,21 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
/* If a direct reclaimer woke kswapd within HZ/10, it's premature */
if (remaining)
- return true;
+ return false;
+
+ /*
+ * There is a potential race between when kswapd checks its watermarks
+ * and a process gets throttled. There is also a potential race if
+ * processes get throttled, kswapd wakes, a large process exits therby
+ * balancing the zones that causes kswapd to miss a wakeup. If kswapd
+ * is going to sleep, no process should be sleeping on pfmemalloc_wait
+ * so wake them now if necessary. If necessary, processes will wake
+ * kswapd and get throttled again
+ */
+ if (waitqueue_active(&pgdat->pfmemalloc_wait)) {
+ wake_up(&pgdat->pfmemalloc_wait);
+ return false;
+ }
/* Check the watermark levels */
for (i = 0; i <= classzone_idx; i++) {
@@ -2318,9 +2457,9 @@ static bool sleeping_prematurely(pg_data_t *pgdat, int order, long remaining,
* must be balanced
*/
if (order)
- return !pgdat_balanced(pgdat, balanced, classzone_idx);
+ return pgdat_balanced(pgdat, balanced, classzone_idx);
else
- return !all_zones_ok;
+ return all_zones_ok;
}
/*
@@ -2546,6 +2685,16 @@ loop_again:
}
}
+
+ /*
+ * If the low watermark is met there is no need for processes
+ * to be throttled on pfmemalloc_wait as they should not be
+ * able to safely make forward progress. Wake them
+ */
+ if (waitqueue_active(&pgdat->pfmemalloc_wait) &&
+ pfmemalloc_watermark_ok(pgdat))
+ wake_up(&pgdat->pfmemalloc_wait);
+
if (all_zones_ok || (order && pgdat_balanced(pgdat, balanced, *classzone_idx)))
break; /* kswapd: all done */
/*
@@ -2647,7 +2796,7 @@ out:
}
/*
- * Return the order we were reclaiming at so sleeping_prematurely()
+ * Return the order we were reclaiming at so prepare_kswapd_sleep()
* makes a decision on the order we were last reclaiming at. However,
* if another caller entered the allocator slow path while kswapd
* was awake, order will remain at the higher level
@@ -2667,7 +2816,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
/* Try to sleep for a short interval */
- if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) {
+ if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
remaining = schedule_timeout(HZ/10);
finish_wait(&pgdat->kswapd_wait, &wait);
prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
@@ -2677,7 +2826,7 @@ static void kswapd_try_to_sleep(pg_data_t *pgdat, int order, int classzone_idx)
* After a short sleep, check if it was a premature sleep. If not, then
* go fully to sleep until explicitly woken up.
*/
- if (!sleeping_prematurely(pgdat, order, remaining, classzone_idx)) {
+ if (prepare_kswapd_sleep(pgdat, order, remaining, classzone_idx)) {
trace_mm_vmscan_kswapd_sleep(pgdat->node_id);
/*
@@ -2953,6 +3102,7 @@ int kswapd_run(int nid)
/* failure at boot is fatal */
BUG_ON(system_state == SYSTEM_BOOTING);
printk("Failed to start kswapd on node %d\n",nid);
+ pgdat->kswapd = NULL;
ret = -1;
}
return ret;
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 1bbbbd9776ad..df7a6748231d 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -745,6 +745,7 @@ const char * const vmstat_text[] = {
TEXTS_FOR_ZONES("pgsteal_direct")
TEXTS_FOR_ZONES("pgscan_kswapd")
TEXTS_FOR_ZONES("pgscan_direct")
+ "pgscan_direct_throttle",
#ifdef CONFIG_NUMA
"zone_reclaim_failed",
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 73a2a83ee2da..402442402af7 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -137,9 +137,21 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
return rc;
}
+static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ if (vlan->netpoll)
+ netpoll_send_skb(vlan->netpoll, skb);
+#else
+ BUG();
+#endif
+ return NETDEV_TX_OK;
+}
+
static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
unsigned int len;
int ret;
@@ -150,29 +162,30 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
* OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
*/
if (veth->h_vlan_proto != htons(ETH_P_8021Q) ||
- vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR) {
+ vlan->flags & VLAN_FLAG_REORDER_HDR) {
u16 vlan_tci;
- vlan_tci = vlan_dev_priv(dev)->vlan_id;
+ vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
}
- skb->dev = vlan_dev_priv(dev)->real_dev;
+ skb->dev = vlan->real_dev;
len = skb->len;
- if (netpoll_tx_running(dev))
- return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
+ if (unlikely(netpoll_tx_running(dev)))
+ return vlan_netpoll_send_skb(vlan, skb);
+
ret = dev_queue_xmit(skb);
if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
struct vlan_pcpu_stats *stats;
- stats = this_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats);
+ stats = this_cpu_ptr(vlan->vlan_pcpu_stats);
u64_stats_update_begin(&stats->syncp);
stats->tx_packets++;
stats->tx_bytes += len;
u64_stats_update_end(&stats->syncp);
} else {
- this_cpu_inc(vlan_dev_priv(dev)->vlan_pcpu_stats->tx_dropped);
+ this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);
}
return ret;
@@ -669,25 +682,26 @@ static void vlan_dev_poll_controller(struct net_device *dev)
return;
}
-static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo,
+ gfp_t gfp)
{
- struct vlan_dev_priv *info = vlan_dev_priv(dev);
- struct net_device *real_dev = info->real_dev;
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
+ struct net_device *real_dev = vlan->real_dev;
struct netpoll *netpoll;
int err = 0;
- netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+ netpoll = kzalloc(sizeof(*netpoll), gfp);
err = -ENOMEM;
if (!netpoll)
goto out;
- err = __netpoll_setup(netpoll, real_dev);
+ err = __netpoll_setup(netpoll, real_dev, gfp);
if (err) {
kfree(netpoll);
goto out;
}
- info->netpoll = netpoll;
+ vlan->netpoll = netpoll;
out:
return err;
@@ -695,19 +709,15 @@ out:
static void vlan_dev_netpoll_cleanup(struct net_device *dev)
{
- struct vlan_dev_priv *info = vlan_dev_priv(dev);
- struct netpoll *netpoll = info->netpoll;
+ struct vlan_dev_priv *vlan= vlan_dev_priv(dev);
+ struct netpoll *netpoll = vlan->netpoll;
if (!netpoll)
return;
- info->netpoll = NULL;
-
- /* Wait for transmitting packets to finish before freeing. */
- synchronize_rcu_bh();
+ vlan->netpoll = NULL;
- __netpoll_cleanup(netpoll);
- kfree(netpoll);
+ __netpoll_free_rcu(netpoll);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
diff --git a/net/atm/common.c b/net/atm/common.c
index b4b44dbed645..0c0ad930a632 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -812,6 +812,7 @@ int vcc_getsockopt(struct socket *sock, int level, int optname,
if (!vcc->dev || !test_bit(ATM_VF_ADDR, &vcc->flags))
return -ENOTCONN;
+ memset(&pvc, 0, sizeof(pvc));
pvc.sap_family = AF_ATMPVC;
pvc.sap_addr.itf = vcc->dev->number;
pvc.sap_addr.vpi = vcc->vpi;
diff --git a/net/atm/pvc.c b/net/atm/pvc.c
index 3a734919c36c..ae0324021407 100644
--- a/net/atm/pvc.c
+++ b/net/atm/pvc.c
@@ -95,6 +95,7 @@ static int pvc_getname(struct socket *sock, struct sockaddr *sockaddr,
return -ENOTCONN;
*sockaddr_len = sizeof(struct sockaddr_atmpvc);
addr = (struct sockaddr_atmpvc *)sockaddr;
+ memset(addr, 0, sizeof(*addr));
addr->sap_family = AF_ATMPVC;
addr->sap_addr.itf = vcc->dev->number;
addr->sap_addr.vpi = vcc->vpi;
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index e877af8bdd1e..469daabd90c7 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -642,7 +642,8 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
struct batadv_neigh_node *router = NULL;
struct batadv_orig_node *orig_node_tmp;
struct hlist_node *node;
- uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;
+ int if_num;
+ uint8_t sum_orig, sum_neigh;
uint8_t *neigh_addr;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -727,17 +728,17 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
if (router && (neigh_node->tq_avg == router->tq_avg)) {
orig_node_tmp = router->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
- bcast_own_sum_orig =
- orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+ if_num = router->if_incoming->if_num;
+ sum_orig = orig_node_tmp->bcast_own_sum[if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
orig_node_tmp = neigh_node->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
- bcast_own_sum_neigh =
- orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+ if_num = neigh_node->if_incoming->if_num;
+ sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
- if (bcast_own_sum_orig >= bcast_own_sum_neigh)
+ if (sum_orig >= sum_neigh)
goto update_tt;
}
diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h
index a081ce1c0514..cebaae7e148b 100644
--- a/net/batman-adv/bitarray.h
+++ b/net/batman-adv/bitarray.h
@@ -20,8 +20,8 @@
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
#define _NET_BATMAN_ADV_BITARRAY_H_
-/* returns true if the corresponding bit in the given seq_bits indicates true
- * and curr_seqno is within range of last_seqno
+/* Returns 1 if the corresponding bit in the given seq_bits indicates true
+ * and curr_seqno is within range of last_seqno. Otherwise returns 0.
*/
static inline int batadv_test_bit(const unsigned long *seq_bits,
uint32_t last_seqno, uint32_t curr_seqno)
@@ -32,7 +32,7 @@ static inline int batadv_test_bit(const unsigned long *seq_bits,
if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE)
return 0;
else
- return test_bit(diff, seq_bits);
+ return test_bit(diff, seq_bits) != 0;
}
/* turn corresponding bit on, so we can remember that we got the packet */
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index b421cc49d2cd..fc866f2e4528 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -200,11 +200,11 @@ void batadv_gw_election(struct batadv_priv *bat_priv)
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
goto out;
- if (!batadv_atomic_dec_not_zero(&bat_priv->gw_reselect))
- goto out;
-
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+ if (!batadv_atomic_dec_not_zero(&bat_priv->gw_reselect) && curr_gw)
+ goto out;
+
next_gw = batadv_gw_get_best_gw_node(bat_priv);
if (curr_gw == next_gw)
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 109ea2aae96c..21c53577c8d6 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -100,18 +100,21 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
{
struct batadv_priv *bat_priv = netdev_priv(dev);
struct sockaddr *addr = p;
+ uint8_t old_addr[ETH_ALEN];
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
+ memcpy(old_addr, dev->dev_addr, ETH_ALEN);
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
/* only modify transtable if it has been initialized before */
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
- batadv_tt_local_remove(bat_priv, dev->dev_addr,
+ batadv_tt_local_remove(bat_priv, old_addr,
"mac address changed", false);
batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX);
}
- memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
dev->addr_assign_type &= ~NET_ADDR_RANDOM;
return 0;
}
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index a438f4b582fc..99dd8f75b3ff 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -197,6 +197,7 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv,
del:
list_del(&entry->list);
kfree(entry);
+ kfree(tt_change_node);
event_removed = true;
goto unlock;
}
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 5e5f5b410e0b..1eaacf10d19d 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -58,7 +58,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case BNEPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -84,7 +84,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case BNEPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 311668d14571..32dc83dcb6b2 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -72,7 +72,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case CMTPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -97,7 +97,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case CMTPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 5ad7da217474..3c094e78dde9 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -29,6 +29,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/smp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -619,6 +620,9 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("hcon %p", conn);
+ if (conn->type == LE_LINK)
+ return smp_conn_security(conn, sec_level);
+
/* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d4de5db18d5a..0b997c8f9655 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -734,6 +734,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
cancel_work_sync(&hdev->le_scan);
+ cancel_delayed_work(&hdev->power_off);
+
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 41ff978a33f9..715d7e33fba0 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1365,6 +1365,9 @@ static bool hci_resolve_next_name(struct hci_dev *hdev)
return false;
e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED);
+ if (!e)
+ return false;
+
if (hci_resolve_name(hdev, e) == 0) {
e->name_state = NAME_PENDING;
return true;
@@ -1393,12 +1396,20 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
return;
e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING);
- if (e) {
+ /* If the device was not found in a list of found devices names of which
+ * are pending. there is no need to continue resolving a next name as it
+ * will be done upon receiving another Remote Name Request Complete
+ * Event */
+ if (!e)
+ return;
+
+ list_del(&e->list);
+ if (name) {
e->name_state = NAME_KNOWN;
- list_del(&e->list);
- if (name)
- mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
- e->data.rssi, name, name_len);
+ mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
+ e->data.rssi, name, name_len);
+ } else {
+ e->name_state = NAME_NOT_KNOWN;
}
if (hci_resolve_next_name(hdev))
@@ -1762,7 +1773,12 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (conn->type == ACL_LINK) {
conn->state = BT_CONFIG;
hci_conn_hold(conn);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+
+ if (!conn->out && !hci_conn_ssp_enabled(conn) &&
+ !hci_find_link_key(hdev, &ev->bdaddr))
+ conn->disc_timeout = HCI_PAIRING_TIMEOUT;
+ else
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
} else
conn->state = BT_CONNECTED;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index a7f04de03d79..d5ace1eda3ed 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -490,7 +490,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
switch (cmd) {
case HCISETRAW:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return -EPERM;
@@ -510,12 +510,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_sock_blacklist_add(hdev, (void __user *) arg);
case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_sock_blacklist_del(hdev, (void __user *) arg);
default:
@@ -546,22 +546,22 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCIDEVUP:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_open(arg);
case HCIDEVDOWN:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_close(arg);
case HCIDEVRESET:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_reset(arg);
case HCIDEVRESTAT:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_reset_stat(arg);
case HCISETSCAN:
@@ -573,7 +573,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCISETACLMTU:
case HCISETSCOMTU:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_cmd(cmd, argp);
case HCIINQUIRY:
@@ -694,6 +694,7 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
*addr_len = sizeof(*haddr);
haddr->hci_family = AF_BLUETOOTH;
haddr->hci_dev = hdev->id;
+ haddr->hci_channel= 0;
release_sock(sk);
return 0;
@@ -1009,6 +1010,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
{
struct hci_filter *f = &hci_pi(sk)->filter;
+ memset(&uf, 0, sizeof(uf));
uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index 18b3f6892a36..b24fb3bd8625 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -56,7 +56,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case HIDPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -91,7 +91,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case HIDPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a8964db04bfb..38c00f142203 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1008,7 +1008,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
if (!conn)
return;
- if (chan->mode == L2CAP_MODE_ERTM) {
+ if (chan->mode == L2CAP_MODE_ERTM && chan->state == BT_CONNECTED) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
@@ -1181,6 +1181,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
sk = chan->sk;
hci_conn_hold(conn->hcon);
+ conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
@@ -1198,14 +1199,15 @@ clean:
static void l2cap_conn_ready(struct l2cap_conn *conn)
{
struct l2cap_chan *chan;
+ struct hci_conn *hcon = conn->hcon;
BT_DBG("conn %p", conn);
- if (!conn->hcon->out && conn->hcon->type == LE_LINK)
+ if (!hcon->out && hcon->type == LE_LINK)
l2cap_le_conn_ready(conn);
- if (conn->hcon->out && conn->hcon->type == LE_LINK)
- smp_conn_security(conn, conn->hcon->pending_sec_level);
+ if (hcon->out && hcon->type == LE_LINK)
+ smp_conn_security(hcon, hcon->pending_sec_level);
mutex_lock(&conn->chan_lock);
@@ -1218,8 +1220,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
continue;
}
- if (conn->hcon->type == LE_LINK) {
- if (smp_conn_security(conn, chan->sec_level))
+ if (hcon->type == LE_LINK) {
+ if (smp_conn_security(hcon, chan->sec_level))
l2cap_chan_ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index a4bb27e8427e..34bbe1c5e389 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -245,6 +245,7 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
BT_DBG("sock %p, sk %p", sock, sk);
+ memset(la, 0, sizeof(struct sockaddr_l2));
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
@@ -615,7 +616,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
break;
}
- if (smp_conn_security(conn, sec.level))
+ if (smp_conn_security(conn->hcon, sec.level))
break;
sk->sk_state = BT_CONFIG;
chan->state = BT_CONFIG;
@@ -1174,7 +1175,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p
chan = l2cap_chan_create();
if (!chan) {
- l2cap_sock_kill(sk);
+ sk_free(sk);
return NULL;
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ad6613d17ca6..eba022de3c20 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2875,6 +2875,22 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
if (scan)
hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ u8 ssp = 1;
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
+ }
+
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ struct hci_cp_write_le_host_supported cp;
+
+ cp.le = 1;
+ cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
+ }
+
update_class(hdev);
update_name(hdev, hdev->dev_name);
update_eir(hdev);
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 7e1e59645c05..1a17850d093c 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -528,6 +528,7 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
BT_DBG("sock %p, sk %p", sock, sk);
+ memset(sa, 0, sizeof(*sa));
sa->rc_family = AF_BLUETOOTH;
sa->rc_channel = rfcomm_pi(sk)->channel;
if (peer)
@@ -822,6 +823,7 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
}
sec.level = rfcomm_pi(sk)->sec_level;
+ sec.key_size = 0;
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index cb960773c002..56f182393c4c 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -456,7 +456,7 @@ static int rfcomm_get_dev_list(void __user *arg)
size = sizeof(*dl) + dev_num * sizeof(*di);
- dl = kmalloc(size, GFP_KERNEL);
+ dl = kzalloc(size, GFP_KERNEL);
if (!dl)
return -ENOMEM;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 40bbe25dcff7..3589e21edb09 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -131,6 +131,15 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
sco_sock_clear_timer(sk);
sco_chan_del(sk, err);
bh_unlock_sock(sk);
+
+ sco_conn_lock(conn);
+ conn->sk = NULL;
+ sco_pi(sk)->conn = NULL;
+ sco_conn_unlock(conn);
+
+ if (conn->hcon)
+ hci_conn_put(conn->hcon);
+
sco_sock_kill(sk);
}
@@ -821,16 +830,6 @@ static void sco_chan_del(struct sock *sk, int err)
BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
- if (conn) {
- sco_conn_lock(conn);
- conn->sk = NULL;
- sco_pi(sk)->conn = NULL;
- sco_conn_unlock(conn);
-
- if (conn->hcon)
- hci_conn_put(conn->hcon);
- }
-
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 16ef0dc85a0a..8c225ef349cd 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -267,10 +267,10 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
hcon->dst_type, reason);
- if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) {
- cancel_delayed_work_sync(&conn->security_timer);
+ cancel_delayed_work_sync(&conn->security_timer);
+
+ if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
smp_chan_destroy(conn);
- }
}
#define JUST_WORKS 0x00
@@ -579,8 +579,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
smp = smp_chan_create(conn);
+ else
+ smp = conn->smp_chan;
- smp = conn->smp_chan;
+ if (!smp)
+ return SMP_UNSPECIFIED;
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
@@ -757,9 +760,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
-int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
+int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
- struct hci_conn *hcon = conn->hcon;
+ struct l2cap_conn *conn = hcon->l2cap_data;
struct smp_chan *smp = conn->smp_chan;
__u8 authreq;
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 333484537600..070e8a68cfc6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -31,9 +31,11 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
struct net_bridge_mdb_entry *mdst;
struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
+ rcu_read_lock();
#ifdef CONFIG_BRIDGE_NETFILTER
if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) {
br_nf_pre_routing_finish_bridge_slow(skb);
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
#endif
@@ -48,7 +50,6 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
- rcu_read_lock();
if (is_broadcast_ether_addr(dest))
br_flood_deliver(br, skb);
else if (is_multicast_ether_addr(dest)) {
@@ -206,24 +207,23 @@ static void br_poll_controller(struct net_device *br_dev)
static void br_netpoll_cleanup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
- struct net_bridge_port *p, *n;
+ struct net_bridge_port *p;
- list_for_each_entry_safe(p, n, &br->port_list, list) {
+ list_for_each_entry(p, &br->port_list, list)
br_netpoll_disable(p);
- }
}
-static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
+static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni,
+ gfp_t gfp)
{
struct net_bridge *br = netdev_priv(dev);
- struct net_bridge_port *p, *n;
+ struct net_bridge_port *p;
int err = 0;
- list_for_each_entry_safe(p, n, &br->port_list, list) {
+ list_for_each_entry(p, &br->port_list, list) {
if (!p->dev)
continue;
-
- err = br_netpoll_enable(p);
+ err = br_netpoll_enable(p, gfp);
if (err)
goto fail;
}
@@ -236,17 +236,17 @@ fail:
goto out;
}
-int br_netpoll_enable(struct net_bridge_port *p)
+int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
{
struct netpoll *np;
int err = 0;
- np = kzalloc(sizeof(*p->np), GFP_KERNEL);
+ np = kzalloc(sizeof(*p->np), gfp);
err = -ENOMEM;
if (!np)
goto out;
- err = __netpoll_setup(np, p->dev);
+ err = __netpoll_setup(np, p->dev, gfp);
if (err) {
kfree(np);
goto out;
@@ -267,11 +267,7 @@ void br_netpoll_disable(struct net_bridge_port *p)
p->np = NULL;
- /* Wait for transmitting packets to finish before freeing. */
- synchronize_rcu_bh();
-
- __netpoll_cleanup(np);
- kfree(np);
+ __netpoll_free_rcu(np);
}
#endif
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index e9466d412707..02015a505d2a 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -65,7 +65,7 @@ static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
{
skb->dev = to->dev;
- if (unlikely(netpoll_tx_running(to->dev))) {
+ if (unlikely(netpoll_tx_running(to->br->dev))) {
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
kfree_skb(skb);
else {
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index e1144e1617be..1c8fdc3558cd 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -361,7 +361,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
if (err)
goto err2;
- if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
+ if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL))))
goto err3;
err = netdev_set_master(dev, br->dev);
@@ -427,6 +427,10 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
if (!p || p->br != br)
return -EINVAL;
+ /* Since more than one interface can be attached to a bridge,
+ * there still maybe an alternate path for netconsole to use;
+ * therefore there is no reason for a NETDEV_RELEASE event.
+ */
del_nbp(p);
spin_lock_bh(&br->lock);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index a768b2408edf..f507d2af9646 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -316,7 +316,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
netpoll_send_skb(np, skb);
}
-extern int br_netpoll_enable(struct net_bridge_port *p);
+extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp);
extern void br_netpoll_disable(struct net_bridge_port *p);
#else
static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br)
@@ -329,7 +329,7 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
{
}
-static inline int br_netpoll_enable(struct net_bridge_port *p)
+static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
{
return 0;
}
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index f88ee537fb2b..92de5e5f9db2 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -80,7 +80,7 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
unsigned int bitmask;
spin_lock_bh(&ebt_log_lock);
- printk("<%c>%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
+ printk(KERN_SOH "%c%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
'0' + loginfo->u.log.level, prefix,
in ? in->name : "", out ? out->name : "",
eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 78f1cdad5b33..095259f83902 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -141,7 +141,7 @@ static int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
err = sk_filter(sk, skb);
if (err)
return err;
- if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) {
+ if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) {
set_rx_flow_off(cf_sk);
net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n");
caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
index dd485f6128e8..ba217e90765e 100644
--- a/net/caif/cfsrvl.c
+++ b/net/caif/cfsrvl.c
@@ -211,9 +211,10 @@ void caif_client_register_refcnt(struct cflayer *adapt_layer,
void (*put)(struct cflayer *lyr))
{
struct cfsrvl *service;
- service = container_of(adapt_layer->dn, struct cfsrvl, layer);
- WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL);
+ if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
+ return;
+ service = container_of(adapt_layer->dn, struct cfsrvl, layer);
service->hold = hold;
service->put = put;
}
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index 69771c04ba8f..e597733affb8 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -94,6 +94,10 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
/* check the version of IP */
ip_version = skb_header_pointer(skb, 0, 1, &buf);
+ if (!ip_version) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
switch (*ip_version >> 4) {
case 4:
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 69e38db28e5f..a8020293f342 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -84,7 +84,6 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid)
return -1;
}
} else {
- pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid);
memcpy(&client->fsid, fsid, sizeof(*fsid));
}
return 0;
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index b780cb7947dd..9da7fdd3cd8a 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -466,6 +466,7 @@ void ceph_key_destroy(struct key *key) {
struct ceph_crypto_key *ckey = key->payload.data;
ceph_crypto_key_destroy(ckey);
+ kfree(ckey);
}
struct key_type key_type_ceph = {
diff --git a/net/ceph/crypto.h b/net/ceph/crypto.h
index 1919d1550d75..3572dc518bc9 100644
--- a/net/ceph/crypto.h
+++ b/net/ceph/crypto.h
@@ -16,7 +16,8 @@ struct ceph_crypto_key {
static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
{
- kfree(key->key);
+ if (key)
+ kfree(key->key);
}
extern int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
diff --git a/net/ceph/debugfs.c b/net/ceph/debugfs.c
index 54b531a01121..38b5dc1823d4 100644
--- a/net/ceph/debugfs.c
+++ b/net/ceph/debugfs.c
@@ -189,6 +189,9 @@ int ceph_debugfs_client_init(struct ceph_client *client)
snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid,
client->monc.auth->global_id);
+ dout("ceph_debugfs_client_init %p %s\n", client, name);
+
+ BUG_ON(client->debugfs_dir);
client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir);
if (!client->debugfs_dir)
goto out;
@@ -234,6 +237,7 @@ out:
void ceph_debugfs_client_cleanup(struct ceph_client *client)
{
+ dout("ceph_debugfs_client_cleanup %p\n", client);
debugfs_remove(client->debugfs_osdmap);
debugfs_remove(client->debugfs_monmap);
debugfs_remove(client->osdc.debugfs_file);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index b9796750034a..159aa8bef9e7 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -915,7 +915,6 @@ static int prepare_write_connect(struct ceph_connection *con)
con->out_connect.authorizer_len = auth ?
cpu_to_le32(auth->authorizer_buf_len) : 0;
- con_out_kvec_reset(con);
con_out_kvec_add(con, sizeof (con->out_connect),
&con->out_connect);
if (auth && auth->authorizer_buf_len)
@@ -1074,16 +1073,13 @@ static int write_partial_msg_pages(struct ceph_connection *con)
BUG_ON(kaddr == NULL);
base = kaddr + con->out_msg_pos.page_pos + bio_offset;
crc = crc32c(crc, base, len);
+ kunmap(page);
msg->footer.data_crc = cpu_to_le32(crc);
con->out_msg_pos.did_page_crc = true;
}
ret = ceph_tcp_sendpage(con->sock, page,
con->out_msg_pos.page_pos + bio_offset,
len, 1);
-
- if (do_datacrc)
- kunmap(page);
-
if (ret <= 0)
goto out;
@@ -1557,6 +1553,7 @@ static int process_connect(struct ceph_connection *con)
return -1;
}
con->auth_retry = 1;
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1577,6 +1574,7 @@ static int process_connect(struct ceph_connection *con)
ENTITY_NAME(con->peer_name),
ceph_pr_addr(&con->peer_addr.in_addr));
reset_connection(con);
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1601,6 +1599,7 @@ static int process_connect(struct ceph_connection *con)
le32_to_cpu(con->out_connect.connect_seq),
le32_to_cpu(con->in_reply.connect_seq));
con->connect_seq = le32_to_cpu(con->in_reply.connect_seq);
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1617,6 +1616,7 @@ static int process_connect(struct ceph_connection *con)
le32_to_cpu(con->in_reply.global_seq));
get_global_seq(con->msgr,
le32_to_cpu(con->in_reply.global_seq));
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -2135,7 +2135,11 @@ more:
BUG_ON(con->state != CON_STATE_CONNECTING);
con->state = CON_STATE_NEGOTIATING;
- /* Banner is good, exchange connection info */
+ /*
+ * Received banner is good, exchange connection info.
+ * Do not reset out_kvec, as sending our banner raced
+ * with receiving peer banner after connect completed.
+ */
ret = prepare_write_connect(con);
if (ret < 0)
goto out;
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index 105d533b55f3..900ea0f043fc 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -311,6 +311,17 @@ int ceph_monc_open_session(struct ceph_mon_client *monc)
EXPORT_SYMBOL(ceph_monc_open_session);
/*
+ * We require the fsid and global_id in order to initialize our
+ * debugfs dir.
+ */
+static bool have_debugfs_info(struct ceph_mon_client *monc)
+{
+ dout("have_debugfs_info fsid %d globalid %lld\n",
+ (int)monc->client->have_fsid, monc->auth->global_id);
+ return monc->client->have_fsid && monc->auth->global_id > 0;
+}
+
+/*
* The monitor responds with mount ack indicate mount success. The
* included client ticket allows the client to talk to MDSs and OSDs.
*/
@@ -320,9 +331,12 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc,
struct ceph_client *client = monc->client;
struct ceph_monmap *monmap = NULL, *old = monc->monmap;
void *p, *end;
+ int had_debugfs_info, init_debugfs = 0;
mutex_lock(&monc->mutex);
+ had_debugfs_info = have_debugfs_info(monc);
+
dout("handle_monmap\n");
p = msg->front.iov_base;
end = p + msg->front.iov_len;
@@ -344,12 +358,22 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc,
if (!client->have_fsid) {
client->have_fsid = true;
+ if (!had_debugfs_info && have_debugfs_info(monc)) {
+ pr_info("client%lld fsid %pU\n",
+ ceph_client_id(monc->client),
+ &monc->client->fsid);
+ init_debugfs = 1;
+ }
mutex_unlock(&monc->mutex);
- /*
- * do debugfs initialization without mutex to avoid
- * creating a locking dependency
- */
- ceph_debugfs_client_init(client);
+
+ if (init_debugfs) {
+ /*
+ * do debugfs initialization without mutex to avoid
+ * creating a locking dependency
+ */
+ ceph_debugfs_client_init(monc->client);
+ }
+
goto out_unlocked;
}
out:
@@ -865,8 +889,10 @@ static void handle_auth_reply(struct ceph_mon_client *monc,
{
int ret;
int was_auth = 0;
+ int had_debugfs_info, init_debugfs = 0;
mutex_lock(&monc->mutex);
+ had_debugfs_info = have_debugfs_info(monc);
if (monc->auth->ops)
was_auth = monc->auth->ops->is_authenticated(monc->auth);
monc->pending_auth = 0;
@@ -889,7 +915,22 @@ static void handle_auth_reply(struct ceph_mon_client *monc,
__send_subscribe(monc);
__resend_generic_request(monc);
}
+
+ if (!had_debugfs_info && have_debugfs_info(monc)) {
+ pr_info("client%lld fsid %pU\n",
+ ceph_client_id(monc->client),
+ &monc->client->fsid);
+ init_debugfs = 1;
+ }
mutex_unlock(&monc->mutex);
+
+ if (init_debugfs) {
+ /*
+ * do debugfs initialization without mutex to avoid
+ * creating a locking dependency
+ */
+ ceph_debugfs_client_init(monc->client);
+ }
}
static int __validate_auth(struct ceph_mon_client *monc)
diff --git a/net/core/dev.c b/net/core/dev.c
index 0ebaea16632f..89e33a5d4d93 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1055,6 +1055,8 @@ rollback:
*/
int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
{
+ char *new_ifalias;
+
ASSERT_RTNL();
if (len >= IFALIASZ)
@@ -1068,9 +1070,10 @@ int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
return 0;
}
- dev->ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
- if (!dev->ifalias)
+ new_ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
+ if (!new_ifalias)
return -ENOMEM;
+ dev->ifalias = new_ifalias;
strlcpy(dev->ifalias, alias, len+1);
return len;
@@ -1172,6 +1175,7 @@ static int __dev_open(struct net_device *dev)
net_dmaengine_get();
dev_set_rx_mode(dev);
dev_activate(dev);
+ add_device_randomness(dev->dev_addr, dev->addr_len);
}
return ret;
@@ -1638,6 +1642,19 @@ static inline int deliver_skb(struct sk_buff *skb,
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
+static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb)
+{
+ if (ptype->af_packet_priv == NULL)
+ return false;
+
+ if (ptype->id_match)
+ return ptype->id_match(ptype, skb->sk);
+ else if ((struct sock *)ptype->af_packet_priv == skb->sk)
+ return true;
+
+ return false;
+}
+
/*
* Support routine. Sends outgoing frames to any network
* taps currently in use.
@@ -1655,8 +1672,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
* they originated from - MvS (miquels@drinkel.ow.org)
*/
if ((ptype->dev == dev || !ptype->dev) &&
- (ptype->af_packet_priv == NULL ||
- (struct sock *)ptype->af_packet_priv != skb->sk)) {
+ (!skb_loop_sk(ptype, skb))) {
if (pt_prev) {
deliver_skb(skb2, pt_prev, skb->dev);
pt_prev = ptype;
@@ -2118,7 +2134,8 @@ static bool can_checksum_protocol(netdev_features_t features, __be16 protocol)
static netdev_features_t harmonize_features(struct sk_buff *skb,
__be16 protocol, netdev_features_t features)
{
- if (!can_checksum_protocol(features, protocol)) {
+ if (skb->ip_summed != CHECKSUM_NONE &&
+ !can_checksum_protocol(features, protocol)) {
features &= ~NETIF_F_ALL_CSUM;
features &= ~NETIF_F_SG;
} else if (illegal_highdma(skb->dev, skb)) {
@@ -2133,6 +2150,9 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
__be16 protocol = skb->protocol;
netdev_features_t features = skb->dev->features;
+ if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
+ features &= ~NETIF_F_GSO_MASK;
+
if (protocol == htons(ETH_P_8021Q)) {
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
protocol = veh->h_vlan_encapsulated_proto;
@@ -2628,15 +2648,16 @@ void __skb_get_rxhash(struct sk_buff *skb)
if (!skb_flow_dissect(skb, &keys))
return;
- if (keys.ports) {
- if ((__force u16)keys.port16[1] < (__force u16)keys.port16[0])
- swap(keys.port16[0], keys.port16[1]);
+ if (keys.ports)
skb->l4_rxhash = 1;
- }
/* get a consistent hash (same value on both flow directions) */
- if ((__force u32)keys.dst < (__force u32)keys.src)
+ if (((__force u32)keys.dst < (__force u32)keys.src) ||
+ (((__force u32)keys.dst == (__force u32)keys.src) &&
+ ((__force u16)keys.port16[1] < (__force u16)keys.port16[0]))) {
swap(keys.dst, keys.src);
+ swap(keys.port16[0], keys.port16[1]);
+ }
hash = jhash_3words((__force u32)keys.dst,
(__force u32)keys.src,
@@ -3155,6 +3176,23 @@ void netdev_rx_handler_unregister(struct net_device *dev)
}
EXPORT_SYMBOL_GPL(netdev_rx_handler_unregister);
+/*
+ * Limit the use of PFMEMALLOC reserves to those protocols that implement
+ * the special handling of PFMEMALLOC skbs.
+ */
+static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
+{
+ switch (skb->protocol) {
+ case __constant_htons(ETH_P_ARP):
+ case __constant_htons(ETH_P_IP):
+ case __constant_htons(ETH_P_IPV6):
+ case __constant_htons(ETH_P_8021Q):
+ return true;
+ default:
+ return false;
+ }
+}
+
static int __netif_receive_skb(struct sk_buff *skb)
{
struct packet_type *ptype, *pt_prev;
@@ -3164,14 +3202,27 @@ static int __netif_receive_skb(struct sk_buff *skb)
bool deliver_exact = false;
int ret = NET_RX_DROP;
__be16 type;
+ unsigned long pflags = current->flags;
net_timestamp_check(!netdev_tstamp_prequeue, skb);
trace_netif_receive_skb(skb);
+ /*
+ * PFMEMALLOC skbs are special, they should
+ * - be delivered to SOCK_MEMALLOC sockets only
+ * - stay away from userspace
+ * - have bounded memory usage
+ *
+ * Use PF_MEMALLOC as this saves us from propagating the allocation
+ * context down to all allocation sites.
+ */
+ if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+ current->flags |= PF_MEMALLOC;
+
/* if we've gotten here through NAPI, check netpoll */
if (netpoll_receive_skb(skb))
- return NET_RX_DROP;
+ goto out;
orig_dev = skb->dev;
@@ -3191,7 +3242,7 @@ another_round:
if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
skb = vlan_untag(skb);
if (unlikely(!skb))
- goto out;
+ goto unlock;
}
#ifdef CONFIG_NET_CLS_ACT
@@ -3201,6 +3252,9 @@ another_round:
}
#endif
+ if (sk_memalloc_socks() && skb_pfmemalloc(skb))
+ goto skip_taps;
+
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (!ptype->dev || ptype->dev == skb->dev) {
if (pt_prev)
@@ -3209,13 +3263,18 @@ another_round:
}
}
+skip_taps:
#ifdef CONFIG_NET_CLS_ACT
skb = handle_ing(skb, &pt_prev, &ret, orig_dev);
if (!skb)
- goto out;
+ goto unlock;
ncls:
#endif
+ if (sk_memalloc_socks() && skb_pfmemalloc(skb)
+ && !skb_pfmemalloc_protocol(skb))
+ goto drop;
+
rx_handler = rcu_dereference(skb->dev->rx_handler);
if (vlan_tx_tag_present(skb)) {
if (pt_prev) {
@@ -3225,7 +3284,7 @@ ncls:
if (vlan_do_receive(&skb, !rx_handler))
goto another_round;
else if (unlikely(!skb))
- goto out;
+ goto unlock;
}
if (rx_handler) {
@@ -3235,7 +3294,7 @@ ncls:
}
switch (rx_handler(&skb)) {
case RX_HANDLER_CONSUMED:
- goto out;
+ goto unlock;
case RX_HANDLER_ANOTHER:
goto another_round;
case RX_HANDLER_EXACT:
@@ -3264,10 +3323,11 @@ ncls:
if (pt_prev) {
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
- ret = -ENOMEM;
+ goto drop;
else
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
} else {
+drop:
atomic_long_inc(&skb->dev->rx_dropped);
kfree_skb(skb);
/* Jamal, now you will not able to escape explaining
@@ -3276,8 +3336,10 @@ ncls:
ret = NET_RX_DROP;
}
-out:
+unlock:
rcu_read_unlock();
+out:
+ tsk_restore_flags(current, pflags, PF_MEMALLOC);
return ret;
}
@@ -4801,6 +4863,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
err = ops->ndo_set_mac_address(dev, sa);
if (!err)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+ add_device_randomness(dev->dev_addr, dev->addr_len);
return err;
}
EXPORT_SYMBOL(dev_set_mac_address);
@@ -5579,6 +5642,7 @@ int register_netdevice(struct net_device *dev)
dev_init_scheduler(dev);
dev_hold(dev);
list_netdevice(dev);
+ add_device_randomness(dev->dev_addr, dev->addr_len);
/* Notify protocols, that a new device appeared. */
ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
@@ -5682,6 +5746,7 @@ EXPORT_SYMBOL(netdev_refcnt_read);
/**
* netdev_wait_allrefs - wait until all references are gone.
+ * @dev: target net_device
*
* This is called when unregistering network devices.
*
@@ -5942,6 +6007,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
dev_net_set(dev, &init_net);
dev->gso_max_size = GSO_MAX_SIZE;
+ dev->gso_max_segs = GSO_MAX_SEGS;
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
diff --git a/net/core/dst.c b/net/core/dst.c
index 069d51d29414..56d63612e1e4 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -149,7 +149,15 @@ int dst_discard(struct sk_buff *skb)
}
EXPORT_SYMBOL(dst_discard);
-const u32 dst_default_metrics[RTAX_MAX];
+const u32 dst_default_metrics[RTAX_MAX + 1] = {
+ /* This initializer is needed to force linker to place this variable
+ * into const section. Otherwise it might end into bss section.
+ * We really want to avoid false sharing on this variable, and catch
+ * any writes on it.
+ */
+ [RTAX_MAX] = 0xdeadbeef,
+};
+
void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
int initial_ref, int initial_obsolete, unsigned short flags)
diff --git a/net/core/filter.c b/net/core/filter.c
index d4ce2dc712e3..907efd27ec77 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -83,6 +83,14 @@ int sk_filter(struct sock *sk, struct sk_buff *skb)
int err;
struct sk_filter *filter;
+ /*
+ * If the skb was allocated from pfmemalloc reserves, only
+ * allow SOCK_MEMALLOC sockets to use it as this socket is
+ * helping free memory
+ */
+ if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC))
+ return -ENOMEM;
+
err = security_sock_rcv_skb(sk, skb);
if (err)
return err;
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index b4c90e42b443..e4ba3e70c174 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -26,6 +26,7 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/if_vlan.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <asm/unaligned.h>
@@ -54,7 +55,7 @@ static atomic_t trapped;
MAX_UDP_CHUNK)
static void zap_completion_queue(void);
-static void arp_reply(struct sk_buff *skb);
+static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo);
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);
@@ -170,7 +171,8 @@ static void poll_napi(struct net_device *dev)
list_for_each_entry(napi, &dev->napi_list, dev_list) {
if (napi->poll_owner != smp_processor_id() &&
spin_trylock(&napi->poll_lock)) {
- budget = poll_one_napi(dev->npinfo, napi, budget);
+ budget = poll_one_napi(rcu_dereference_bh(dev->npinfo),
+ napi, budget);
spin_unlock(&napi->poll_lock);
if (!budget)
@@ -185,13 +187,14 @@ static void service_arp_queue(struct netpoll_info *npi)
struct sk_buff *skb;
while ((skb = skb_dequeue(&npi->arp_tx)))
- arp_reply(skb);
+ netpoll_arp_reply(skb, npi);
}
}
static void netpoll_poll_dev(struct net_device *dev)
{
const struct net_device_ops *ops;
+ struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo);
if (!dev || !netif_running(dev))
return;
@@ -206,17 +209,18 @@ static void netpoll_poll_dev(struct net_device *dev)
poll_napi(dev);
if (dev->flags & IFF_SLAVE) {
- if (dev->npinfo) {
+ if (ni) {
struct net_device *bond_dev = dev->master;
struct sk_buff *skb;
- while ((skb = skb_dequeue(&dev->npinfo->arp_tx))) {
+ struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo);
+ while ((skb = skb_dequeue(&ni->arp_tx))) {
skb->dev = bond_dev;
- skb_queue_tail(&bond_dev->npinfo->arp_tx, skb);
+ skb_queue_tail(&bond_ni->arp_tx, skb);
}
}
}
- service_arp_queue(dev->npinfo);
+ service_arp_queue(ni);
zap_completion_queue();
}
@@ -302,6 +306,7 @@ static int netpoll_owner_active(struct net_device *dev)
return 0;
}
+/* call with IRQ disabled */
void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
struct net_device *dev)
{
@@ -309,8 +314,11 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
unsigned long tries;
const struct net_device_ops *ops = dev->netdev_ops;
/* It is up to the caller to keep npinfo alive. */
- struct netpoll_info *npinfo = np->dev->npinfo;
+ struct netpoll_info *npinfo;
+
+ WARN_ON_ONCE(!irqs_disabled());
+ npinfo = rcu_dereference_bh(np->dev->npinfo);
if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) {
__kfree_skb(skb);
return;
@@ -319,16 +327,22 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
/* don't get messages out of order, and no recursion */
if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) {
struct netdev_queue *txq;
- unsigned long flags;
txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
- local_irq_save(flags);
/* try until next clock tick */
for (tries = jiffies_to_usecs(1)/USEC_PER_POLL;
tries > 0; --tries) {
if (__netif_tx_trylock(txq)) {
if (!netif_xmit_stopped(txq)) {
+ if (vlan_tx_tag_present(skb) &&
+ !(netif_skb_features(skb) & NETIF_F_HW_VLAN_TX)) {
+ skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+ if (unlikely(!skb))
+ break;
+ skb->vlan_tci = 0;
+ }
+
status = ops->ndo_start_xmit(skb, dev);
if (status == NETDEV_TX_OK)
txq_trans_update(txq);
@@ -347,10 +361,9 @@ void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
}
WARN_ONCE(!irqs_disabled(),
- "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n",
+ "netpoll_send_skb_on_dev(): %s enabled interrupts in poll (%pF)\n",
dev->name, ops->ndo_start_xmit);
- local_irq_restore(flags);
}
if (status != NETDEV_TX_OK) {
@@ -423,9 +436,8 @@ void netpoll_send_udp(struct netpoll *np, const char *msg, int len)
}
EXPORT_SYMBOL(netpoll_send_udp);
-static void arp_reply(struct sk_buff *skb)
+static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
{
- struct netpoll_info *npinfo = skb->dev->npinfo;
struct arphdr *arp;
unsigned char *arp_ptr;
int size, type = ARPOP_REPLY, ptype = ETH_P_ARP;
@@ -543,13 +555,12 @@ static void arp_reply(struct sk_buff *skb)
spin_unlock_irqrestore(&npinfo->rx_lock, flags);
}
-int __netpoll_rx(struct sk_buff *skb)
+int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
{
int proto, len, ulen;
int hits = 0;
const struct iphdr *iph;
struct udphdr *uh;
- struct netpoll_info *npinfo = skb->dev->npinfo;
struct netpoll *np, *tmp;
if (list_empty(&npinfo->rx_np))
@@ -565,6 +576,12 @@ int __netpoll_rx(struct sk_buff *skb)
return 1;
}
+ if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
+ skb = vlan_untag(skb);
+ if (unlikely(!skb))
+ goto out;
+ }
+
proto = ntohs(eth_hdr(skb)->h_proto);
if (proto != ETH_P_IP)
goto out;
@@ -715,7 +732,7 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
}
EXPORT_SYMBOL(netpoll_parse_options);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
{
struct netpoll_info *npinfo;
const struct net_device_ops *ops;
@@ -734,7 +751,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
}
if (!ndev->npinfo) {
- npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL);
+ npinfo = kmalloc(sizeof(*npinfo), gfp);
if (!npinfo) {
err = -ENOMEM;
goto out;
@@ -752,7 +769,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
ops = np->dev->netdev_ops;
if (ops->ndo_netpoll_setup) {
- err = ops->ndo_netpoll_setup(ndev, npinfo);
+ err = ops->ndo_netpoll_setup(ndev, npinfo, gfp);
if (err)
goto free_npinfo;
}
@@ -857,7 +874,7 @@ int netpoll_setup(struct netpoll *np)
refill_skbs();
rtnl_lock();
- err = __netpoll_setup(np, ndev);
+ err = __netpoll_setup(np, ndev, GFP_KERNEL);
rtnl_unlock();
if (err)
@@ -878,6 +895,24 @@ static int __init netpoll_init(void)
}
core_initcall(netpoll_init);
+static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
+{
+ struct netpoll_info *npinfo =
+ container_of(rcu_head, struct netpoll_info, rcu);
+
+ skb_queue_purge(&npinfo->arp_tx);
+ skb_queue_purge(&npinfo->txq);
+
+ /* we can't call cancel_delayed_work_sync here, as we are in softirq */
+ cancel_delayed_work(&npinfo->tx_work);
+
+ /* clean after last, unfinished work */
+ __skb_queue_purge(&npinfo->txq);
+ /* now cancel it again */
+ cancel_delayed_work(&npinfo->tx_work);
+ kfree(npinfo);
+}
+
void __netpoll_cleanup(struct netpoll *np)
{
struct netpoll_info *npinfo;
@@ -903,20 +938,24 @@ void __netpoll_cleanup(struct netpoll *np)
ops->ndo_netpoll_cleanup(np->dev);
RCU_INIT_POINTER(np->dev->npinfo, NULL);
+ call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info);
+ }
+}
+EXPORT_SYMBOL_GPL(__netpoll_cleanup);
- /* avoid racing with NAPI reading npinfo */
- synchronize_rcu_bh();
+static void rcu_cleanup_netpoll(struct rcu_head *rcu_head)
+{
+ struct netpoll *np = container_of(rcu_head, struct netpoll, rcu);
- skb_queue_purge(&npinfo->arp_tx);
- skb_queue_purge(&npinfo->txq);
- cancel_delayed_work_sync(&npinfo->tx_work);
+ __netpoll_cleanup(np);
+ kfree(np);
+}
- /* clean after last, unfinished work */
- __skb_queue_purge(&npinfo->txq);
- kfree(npinfo);
- }
+void __netpoll_free_rcu(struct netpoll *np)
+{
+ call_rcu_bh(&np->rcu, rcu_cleanup_netpoll);
}
-EXPORT_SYMBOL_GPL(__netpoll_cleanup);
+EXPORT_SYMBOL_GPL(__netpoll_free_rcu);
void netpoll_cleanup(struct netpoll *np)
{
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index ed0c0431fcd8..c75e3f9d060f 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -101,12 +101,10 @@ static int write_update_netdev_table(struct net_device *dev)
u32 max_len;
struct netprio_map *map;
- rtnl_lock();
max_len = atomic_read(&max_prioidx) + 1;
map = rtnl_dereference(dev->priomap);
if (!map || map->priomap_len < max_len)
ret = extend_netdev_table(dev, max_len);
- rtnl_unlock();
return ret;
}
@@ -256,17 +254,17 @@ static int write_priomap(struct cgroup *cgrp, struct cftype *cft,
if (!dev)
goto out_free_devname;
+ rtnl_lock();
ret = write_update_netdev_table(dev);
if (ret < 0)
goto out_put_dev;
- rcu_read_lock();
- map = rcu_dereference(dev->priomap);
+ map = rtnl_dereference(dev->priomap);
if (map)
map->priomap[prioidx] = priority;
- rcu_read_unlock();
out_put_dev:
+ rtnl_unlock();
dev_put(dev);
out_free_devname:
@@ -277,12 +275,6 @@ out_free_devname:
void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
{
struct task_struct *p;
- char *tmp = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL);
-
- if (!tmp) {
- pr_warn("Unable to attach cgrp due to alloc failure!\n");
- return;
- }
cgroup_taskset_for_each(p, cgrp, tset) {
unsigned int fd;
@@ -296,32 +288,24 @@ void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
continue;
}
- rcu_read_lock();
+ spin_lock(&files->file_lock);
fdt = files_fdtable(files);
for (fd = 0; fd < fdt->max_fds; fd++) {
- char *path;
struct file *file;
struct socket *sock;
- unsigned long s;
- int rv, err = 0;
+ int err;
file = fcheck_files(files, fd);
if (!file)
continue;
- path = d_path(&file->f_path, tmp, PAGE_SIZE);
- rv = sscanf(path, "socket:[%lu]", &s);
- if (rv <= 0)
- continue;
-
sock = sock_from_file(file, &err);
- if (!err)
+ if (sock)
sock_update_netprioidx(sock->sk, p);
}
- rcu_read_unlock();
+ spin_unlock(&files->file_lock);
task_unlock(p);
}
- kfree(tmp);
}
static struct cftype ss_files[] = {
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index cce9e53528b1..148e73d2c451 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -2721,7 +2721,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
/* Eth + IPh + UDPh + mpls */
datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
pkt_dev->pkt_overhead;
- if (datalen < sizeof(struct pktgen_hdr))
+ if (datalen < 0 || datalen < sizeof(struct pktgen_hdr))
datalen = sizeof(struct pktgen_hdr);
udph->source = htons(pkt_dev->cur_udp_src);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 5ff949dc954f..2c5a0a06c4ce 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1381,6 +1381,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
goto errout;
send_addr_notify = 1;
modified = 1;
+ add_device_randomness(dev->dev_addr, dev->addr_len);
}
if (tb[IFLA_MTU]) {
diff --git a/net/core/scm.c b/net/core/scm.c
index 8f6ccfd68ef4..040cebeed45b 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -265,6 +265,7 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;
i++, cmfptr++)
{
+ struct socket *sock;
int new_fd;
err = security_file_receive(fp[i]);
if (err)
@@ -281,6 +282,9 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
}
/* Bump the usage count and install the file. */
get_file(fp[i]);
+ sock = sock_from_file(fp[i], &err);
+ if (sock)
+ sock_update_netprioidx(sock->sk, current);
fd_install(new_fd, fp[i]);
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 368f65c15e4f..e33ebae519c8 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -145,6 +145,43 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
BUG();
}
+
+/*
+ * kmalloc_reserve is a wrapper around kmalloc_node_track_caller that tells
+ * the caller if emergency pfmemalloc reserves are being used. If it is and
+ * the socket is later found to be SOCK_MEMALLOC then PFMEMALLOC reserves
+ * may be used. Otherwise, the packet data may be discarded until enough
+ * memory is free
+ */
+#define kmalloc_reserve(size, gfp, node, pfmemalloc) \
+ __kmalloc_reserve(size, gfp, node, _RET_IP_, pfmemalloc)
+void *__kmalloc_reserve(size_t size, gfp_t flags, int node, unsigned long ip,
+ bool *pfmemalloc)
+{
+ void *obj;
+ bool ret_pfmemalloc = false;
+
+ /*
+ * Try a regular allocation, when that fails and we're not entitled
+ * to the reserves, fail.
+ */
+ obj = kmalloc_node_track_caller(size,
+ flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
+ node);
+ if (obj || !(gfp_pfmemalloc_allowed(flags)))
+ goto out;
+
+ /* Try again but now we are using pfmemalloc reserves */
+ ret_pfmemalloc = true;
+ obj = kmalloc_node_track_caller(size, flags, node);
+
+out:
+ if (pfmemalloc)
+ *pfmemalloc = ret_pfmemalloc;
+
+ return obj;
+}
+
/* Allocate a new skbuff. We do this ourselves so we can fill in a few
* 'private' fields and also do memory statistics to find all the
* [BEEP] leaks.
@@ -155,8 +192,10 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
* __alloc_skb - allocate a network buffer
* @size: size to allocate
* @gfp_mask: allocation mask
- * @fclone: allocate from fclone cache instead of head cache
- * and allocate a cloned (child) skb
+ * @flags: If SKB_ALLOC_FCLONE is set, allocate from fclone cache
+ * instead of head cache and allocate a cloned (child) skb.
+ * If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for
+ * allocations in case the data is required for writeback
* @node: numa node to allocate memory on
*
* Allocate a new &sk_buff. The returned buffer has no headroom and a
@@ -167,14 +206,19 @@ static void skb_under_panic(struct sk_buff *skb, int sz, void *here)
* %GFP_ATOMIC.
*/
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
- int fclone, int node)
+ int flags, int node)
{
struct kmem_cache *cache;
struct skb_shared_info *shinfo;
struct sk_buff *skb;
u8 *data;
+ bool pfmemalloc;
- cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
+ cache = (flags & SKB_ALLOC_FCLONE)
+ ? skbuff_fclone_cache : skbuff_head_cache;
+
+ if (sk_memalloc_socks() && (flags & SKB_ALLOC_RX))
+ gfp_mask |= __GFP_MEMALLOC;
/* Get the HEAD */
skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
@@ -189,7 +233,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
*/
size = SKB_DATA_ALIGN(size);
size += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
- data = kmalloc_node_track_caller(size, gfp_mask, node);
+ data = kmalloc_reserve(size, gfp_mask, node, &pfmemalloc);
if (!data)
goto nodata;
/* kmalloc(size) might give us more room than requested.
@@ -207,6 +251,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
memset(skb, 0, offsetof(struct sk_buff, tail));
/* Account for allocated memory : skb + skb->head */
skb->truesize = SKB_TRUESIZE(size);
+ skb->pfmemalloc = pfmemalloc;
atomic_set(&skb->users, 1);
skb->head = data;
skb->data = data;
@@ -222,7 +267,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
atomic_set(&shinfo->dataref, 1);
kmemcheck_annotate_variable(shinfo->destructor_arg);
- if (fclone) {
+ if (flags & SKB_ALLOC_FCLONE) {
struct sk_buff *child = skb + 1;
atomic_t *fclone_ref = (atomic_t *) (child + 1);
@@ -232,6 +277,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
atomic_set(fclone_ref, 1);
child->fclone = SKB_FCLONE_UNAVAILABLE;
+ child->pfmemalloc = pfmemalloc;
}
out:
return skb;
@@ -302,14 +348,7 @@ static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache);
#define NETDEV_PAGECNT_BIAS (PAGE_SIZE / SMP_CACHE_BYTES)
-/**
- * netdev_alloc_frag - allocate a page fragment
- * @fragsz: fragment size
- *
- * Allocates a frag from a page for receive buffer.
- * Uses GFP_ATOMIC allocations.
- */
-void *netdev_alloc_frag(unsigned int fragsz)
+static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
{
struct netdev_alloc_cache *nc;
void *data = NULL;
@@ -319,7 +358,7 @@ void *netdev_alloc_frag(unsigned int fragsz)
nc = &__get_cpu_var(netdev_alloc_cache);
if (unlikely(!nc->page)) {
refill:
- nc->page = alloc_page(GFP_ATOMIC | __GFP_COLD);
+ nc->page = alloc_page(gfp_mask);
if (unlikely(!nc->page))
goto end;
recycle:
@@ -343,6 +382,18 @@ end:
local_irq_restore(flags);
return data;
}
+
+/**
+ * netdev_alloc_frag - allocate a page fragment
+ * @fragsz: fragment size
+ *
+ * Allocates a frag from a page for receive buffer.
+ * Uses GFP_ATOMIC allocations.
+ */
+void *netdev_alloc_frag(unsigned int fragsz)
+{
+ return __netdev_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD);
+}
EXPORT_SYMBOL(netdev_alloc_frag);
/**
@@ -366,7 +417,12 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) {
- void *data = netdev_alloc_frag(fragsz);
+ void *data;
+
+ if (sk_memalloc_socks())
+ gfp_mask |= __GFP_MEMALLOC;
+
+ data = __netdev_alloc_frag(fragsz, gfp_mask);
if (likely(data)) {
skb = build_skb(data, fragsz);
@@ -374,7 +430,8 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
put_page(virt_to_head_page(data));
}
} else {
- skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask, 0, NUMA_NO_NODE);
+ skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask,
+ SKB_ALLOC_RX, NUMA_NO_NODE);
}
if (likely(skb)) {
skb_reserve(skb, NET_SKB_PAD);
@@ -656,6 +713,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
#if IS_ENABLED(CONFIG_IP_VS)
new->ipvs_property = old->ipvs_property;
#endif
+ new->pfmemalloc = old->pfmemalloc;
new->protocol = old->protocol;
new->mark = old->mark;
new->skb_iif = old->skb_iif;
@@ -814,6 +872,9 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
n->fclone = SKB_FCLONE_CLONE;
atomic_inc(fclone_ref);
} else {
+ if (skb_pfmemalloc(skb))
+ gfp_mask |= __GFP_MEMALLOC;
+
n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
if (!n)
return NULL;
@@ -850,6 +911,13 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type;
}
+static inline int skb_alloc_rx_flag(const struct sk_buff *skb)
+{
+ if (skb_pfmemalloc(skb))
+ return SKB_ALLOC_RX;
+ return 0;
+}
+
/**
* skb_copy - create private copy of an sk_buff
* @skb: buffer to copy
@@ -871,7 +939,8 @@ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
{
int headerlen = skb_headroom(skb);
unsigned int size = skb_end_offset(skb) + skb->data_len;
- struct sk_buff *n = alloc_skb(size, gfp_mask);
+ struct sk_buff *n = __alloc_skb(size, gfp_mask,
+ skb_alloc_rx_flag(skb), NUMA_NO_NODE);
if (!n)
return NULL;
@@ -906,7 +975,8 @@ EXPORT_SYMBOL(skb_copy);
struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask)
{
unsigned int size = skb_headlen(skb) + headroom;
- struct sk_buff *n = alloc_skb(size, gfp_mask);
+ struct sk_buff *n = __alloc_skb(size, gfp_mask,
+ skb_alloc_rx_flag(skb), NUMA_NO_NODE);
if (!n)
goto out;
@@ -979,8 +1049,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
size = SKB_DATA_ALIGN(size);
- data = kmalloc(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
- gfp_mask);
+ if (skb_pfmemalloc(skb))
+ gfp_mask |= __GFP_MEMALLOC;
+ data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
+ gfp_mask, NUMA_NO_NODE, NULL);
if (!data)
goto nodata;
size = SKB_WITH_OVERHEAD(ksize(data));
@@ -1092,8 +1164,9 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
/*
* Allocate the copy buffer
*/
- struct sk_buff *n = alloc_skb(newheadroom + skb->len + newtailroom,
- gfp_mask);
+ struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom,
+ gfp_mask, skb_alloc_rx_flag(skb),
+ NUMA_NO_NODE);
int oldheadroom = skb_headroom(skb);
int head_copy_len, head_copy_off;
int off;
@@ -2775,8 +2848,9 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
skb_release_head_state(nskb);
__skb_push(nskb, doffset);
} else {
- nskb = alloc_skb(hsize + doffset + headroom,
- GFP_ATOMIC);
+ nskb = __alloc_skb(hsize + doffset + headroom,
+ GFP_ATOMIC, skb_alloc_rx_flag(skb),
+ NUMA_NO_NODE);
if (unlikely(!nskb))
goto err;
@@ -3428,7 +3502,9 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
if (!skb_cloned(from))
skb_shinfo(from)->nr_frags = 0;
- /* if the skb is cloned this does nothing since we set nr_frags to 0 */
+ /* if the skb is not cloned this does nothing
+ * since we set nr_frags to 0.
+ */
for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
skb_frag_ref(from, i);
diff --git a/net/core/sock.c b/net/core/sock.c
index 2676a88f533e..a6000fbad294 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -142,7 +142,7 @@
static DEFINE_MUTEX(proto_list_mutex);
static LIST_HEAD(proto_list);
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
int mem_cgroup_sockets_init(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
{
struct proto *proto;
@@ -271,6 +271,61 @@ __u32 sysctl_rmem_default __read_mostly = SK_RMEM_MAX;
int sysctl_optmem_max __read_mostly = sizeof(unsigned long)*(2*UIO_MAXIOV+512);
EXPORT_SYMBOL(sysctl_optmem_max);
+struct static_key memalloc_socks = STATIC_KEY_INIT_FALSE;
+EXPORT_SYMBOL_GPL(memalloc_socks);
+
+/**
+ * sk_set_memalloc - sets %SOCK_MEMALLOC
+ * @sk: socket to set it on
+ *
+ * Set %SOCK_MEMALLOC on a socket for access to emergency reserves.
+ * It's the responsibility of the admin to adjust min_free_kbytes
+ * to meet the requirements
+ */
+void sk_set_memalloc(struct sock *sk)
+{
+ sock_set_flag(sk, SOCK_MEMALLOC);
+ sk->sk_allocation |= __GFP_MEMALLOC;
+ static_key_slow_inc(&memalloc_socks);
+}
+EXPORT_SYMBOL_GPL(sk_set_memalloc);
+
+void sk_clear_memalloc(struct sock *sk)
+{
+ sock_reset_flag(sk, SOCK_MEMALLOC);
+ sk->sk_allocation &= ~__GFP_MEMALLOC;
+ static_key_slow_dec(&memalloc_socks);
+
+ /*
+ * SOCK_MEMALLOC is allowed to ignore rmem limits to ensure forward
+ * progress of swapping. However, if SOCK_MEMALLOC is cleared while
+ * it has rmem allocations there is a risk that the user of the
+ * socket cannot make forward progress due to exceeding the rmem
+ * limits. By rights, sk_clear_memalloc() should only be called
+ * on sockets being torn down but warn and reset the accounting if
+ * that assumption breaks.
+ */
+ if (WARN_ON(sk->sk_forward_alloc))
+ sk_mem_reclaim(sk);
+}
+EXPORT_SYMBOL_GPL(sk_clear_memalloc);
+
+int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ int ret;
+ unsigned long pflags = current->flags;
+
+ /* these should have been dropped before queueing */
+ BUG_ON(!sock_flag(sk, SOCK_MEMALLOC));
+
+ current->flags |= PF_MEMALLOC;
+ ret = sk->sk_backlog_rcv(sk, skb);
+ tsk_restore_flags(current, pflags, PF_MEMALLOC);
+
+ return ret;
+}
+EXPORT_SYMBOL(__sk_backlog_rcv);
+
#if defined(CONFIG_CGROUPS)
#if !defined(CONFIG_NET_CLS_CGROUP)
int net_cls_subsys_id = -1;
@@ -353,7 +408,7 @@ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (err)
return err;
- if (!sk_rmem_schedule(sk, skb->truesize)) {
+ if (!sk_rmem_schedule(sk, skb, skb->truesize)) {
atomic_inc(&sk->sk_drops);
return -ENOBUFS;
}
@@ -636,7 +691,8 @@ set_rcvbuf:
case SO_KEEPALIVE:
#ifdef CONFIG_INET
- if (sk->sk_protocol == IPPROTO_TCP)
+ if (sk->sk_protocol == IPPROTO_TCP &&
+ sk->sk_type == SOCK_STREAM)
tcp_set_keepalive(sk, valbool);
#endif
sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
@@ -1403,6 +1459,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
} else {
sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
sk->sk_gso_max_size = dst->dev->gso_max_size;
+ sk->sk_gso_max_segs = dst->dev->gso_max_segs;
}
}
}
@@ -1467,7 +1524,14 @@ EXPORT_SYMBOL(sock_rfree);
void sock_edemux(struct sk_buff *skb)
{
- sock_put(skb->sk);
+ struct sock *sk = skb->sk;
+
+#ifdef CONFIG_INET
+ if (sk->sk_state == TCP_TIME_WAIT)
+ inet_twsk_put(inet_twsk(sk));
+ else
+#endif
+ sock_put(sk);
}
EXPORT_SYMBOL(sock_edemux);
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index 75c3582a7678..fb85d371a8de 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -246,7 +246,7 @@ static inline int ccid_hc_rx_getsockopt(struct ccid *ccid, struct sock *sk,
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_ops->ccid_hc_rx_getsockopt != NULL)
+ if (ccid != NULL && ccid->ccid_ops->ccid_hc_rx_getsockopt != NULL)
rc = ccid->ccid_ops->ccid_hc_rx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
@@ -257,7 +257,7 @@ static inline int ccid_hc_tx_getsockopt(struct ccid *ccid, struct sock *sk,
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_ops->ccid_hc_tx_getsockopt != NULL)
+ if (ccid != NULL && ccid->ccid_ops->ccid_hc_tx_getsockopt != NULL)
rc = ccid->ccid_ops->ccid_hc_tx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index d65e98798eca..119c04317d48 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -535,6 +535,7 @@ static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
case DCCP_SOCKOPT_CCID_TX_INFO:
if (len < sizeof(tfrc))
return -EINVAL;
+ memset(&tfrc, 0, sizeof(tfrc));
tfrc.tfrctx_x = hc->tx_x;
tfrc.tfrctx_x_recv = hc->tx_x_recv;
tfrc.tfrctx_x_calc = hc->tx_x_calc;
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index ae2ccf2890e4..15ca63ec604e 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
-obj-$(CONFIG_CGROUP_MEM_RES_CTLR_KMEM) += tcp_memcontrol.o
+obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 77e87aff419a..47800459e4cb 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1225,7 +1225,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&arp_tbl, dev);
- rt_cache_flush(dev_net(dev), 0);
+ rt_cache_flush(dev_net(dev));
break;
default:
break;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 44bf82e3aef7..e12fad773852 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -725,7 +725,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
break;
case SIOCSIFFLAGS:
- ret = -EACCES;
+ ret = -EPERM;
if (!capable(CAP_NET_ADMIN))
goto out;
break;
@@ -733,7 +733,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCSIFBRDADDR: /* Set the broadcast address */
case SIOCSIFDSTADDR: /* Set the destination address */
case SIOCSIFNETMASK: /* Set the netmask for the interface */
- ret = -EACCES;
+ ret = -EPERM;
if (!capable(CAP_NET_ADMIN))
goto out;
ret = -EINVAL;
@@ -1503,7 +1503,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
if ((new_value == 0) && (old_value != 0))
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
}
return ret;
@@ -1537,7 +1537,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
dev_disable_lro(idev->dev);
}
rtnl_unlock();
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
}
}
@@ -1554,7 +1554,7 @@ static int ipv4_doint_and_flush(ctl_table *ctl, int write,
struct net *net = ctl->extra2;
if (write && *valp != val)
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
return ret;
}
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index c43ae3fba792..8e2b475da9fa 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -148,7 +148,7 @@ static void fib_flush(struct net *net)
}
if (flushed)
- rt_cache_flush(net, -1);
+ rt_cache_flush(net);
}
/*
@@ -999,11 +999,11 @@ static void nl_fib_lookup_exit(struct net *net)
net->ipv4.fibnl = NULL;
}
-static void fib_disable_ip(struct net_device *dev, int force, int delay)
+static void fib_disable_ip(struct net_device *dev, int force)
{
if (fib_sync_down_dev(dev, force))
fib_flush(dev_net(dev));
- rt_cache_flush(dev_net(dev), delay);
+ rt_cache_flush(dev_net(dev));
arp_ifdown(dev);
}
@@ -1020,7 +1020,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
fib_sync_up(dev);
#endif
atomic_inc(&net->ipv4.dev_addr_genid);
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_DOWN:
fib_del_ifaddr(ifa, NULL);
@@ -1029,9 +1029,9 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
/* Last address was deleted from this interface.
* Disable IP.
*/
- fib_disable_ip(dev, 1, 0);
+ fib_disable_ip(dev, 1);
} else {
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
}
break;
}
@@ -1045,7 +1045,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
struct net *net = dev_net(dev);
if (event == NETDEV_UNREGISTER) {
- fib_disable_ip(dev, 2, -1);
+ fib_disable_ip(dev, 2);
rt_flush_dev(dev);
return NOTIFY_DONE;
}
@@ -1062,14 +1062,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
fib_sync_up(dev);
#endif
atomic_inc(&net->ipv4.dev_addr_genid);
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_DOWN:
- fib_disable_ip(dev, 0, 0);
+ fib_disable_ip(dev, 0);
break;
case NETDEV_CHANGEMTU:
case NETDEV_CHANGE:
- rt_cache_flush(dev_net(dev), 0);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_UNREGISTER_BATCH:
break;
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index a83d74e498d2..274309d3aded 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -259,7 +259,7 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)
static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
{
- rt_cache_flush(ops->fro_net, -1);
+ rt_cache_flush(ops->fro_net);
}
static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index f0cdb30921c0..d1b93595b4a7 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -367,7 +367,7 @@ static void __leaf_free_rcu(struct rcu_head *head)
static inline void free_leaf(struct leaf *l)
{
- call_rcu_bh(&l->rcu, __leaf_free_rcu);
+ call_rcu(&l->rcu, __leaf_free_rcu);
}
static inline void free_leaf_info(struct leaf_info *leaf)
@@ -1286,7 +1286,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
fib_release_info(fi_drop);
if (state & FA_S_ACCESSED)
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
@@ -1333,7 +1333,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
list_add_tail_rcu(&new_fa->fa_list,
(fa ? &fa->fa_list : fa_head));
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
&cfg->fc_nlinfo, 0);
succeeded:
@@ -1708,7 +1708,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
trie_leaf_remove(t, l);
if (fa->fa_state & FA_S_ACCESSED)
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
fib_release_info(fa->fa_info);
alias_free_mem_rcu(fa);
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index db0cf17c00f7..7f75f21d7b83 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -404,12 +404,15 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
{
const struct inet_request_sock *ireq = inet_rsk(req);
struct inet_sock *newinet = inet_sk(newsk);
- struct ip_options_rcu *opt = ireq->opt;
+ struct ip_options_rcu *opt;
struct net *net = sock_net(sk);
struct flowi4 *fl4;
struct rtable *rt;
fl4 = &newinet->cork.fl.u.ip4;
+
+ rcu_read_lock();
+ opt = rcu_dereference(newinet->inet_opt);
flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
sk->sk_protocol, inet_sk_flowi_flags(sk),
@@ -421,11 +424,13 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk,
goto no_route;
if (opt && opt->opt.is_strictroute && rt->rt_gateway)
goto route_err;
+ rcu_read_unlock();
return &rt->dst;
route_err:
ip_rt_put(rt);
no_route:
+ rcu_read_unlock();
IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
return NULL;
}
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index e1e0a4e8fd34..c7527f6b9ad9 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -510,7 +510,10 @@ relookup:
secure_ipv6_id(daddr->addr.a6));
p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
p->rate_tokens = 0;
- p->rate_last = 0;
+ /* 60*HZ is arbitrary, but chosen enough high so that the first
+ * calculation of tokens is at its maximum.
+ */
+ p->rate_last = jiffies - 60*HZ;
INIT_LIST_HEAD(&p->gc_list);
/* Link the node. */
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index ba39a52d18c1..c196d749daf2 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -197,7 +197,7 @@ static inline int ip_finish_output2(struct sk_buff *skb)
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
- if (neigh) {
+ if (!IS_ERR(neigh)) {
int res = dst_neigh_output(dst, neigh, skb);
rcu_read_unlock_bh();
@@ -1338,10 +1338,10 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
iph->ihl = 5;
iph->tos = inet->tos;
iph->frag_off = df;
- ip_select_ident(iph, &rt->dst, sk);
iph->ttl = ttl;
iph->protocol = sk->sk_protocol;
ip_copy_addrs(iph, fl4);
+ ip_select_ident(iph, &rt->dst, sk);
if (opt) {
iph->ihl += opt->optlen>>2;
@@ -1366,9 +1366,8 @@ out:
return skb;
}
-int ip_send_skb(struct sk_buff *skb)
+int ip_send_skb(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
int err;
err = ip_local_out(skb);
@@ -1391,7 +1390,7 @@ int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4)
return 0;
/* Netfilter gets whole the not fragmented skb. */
- return ip_send_skb(skb);
+ return ip_send_skb(sock_net(sk), skb);
}
/*
@@ -1536,6 +1535,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr,
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE;
+ skb_orphan(nskb);
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 8eec8f4a0536..ebdf06f938bf 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -124,6 +124,8 @@ static DEFINE_SPINLOCK(mfc_unres_lock);
static struct kmem_cache *mrt_cachep __read_mostly;
static struct mr_table *ipmr_new_table(struct net *net, u32 id);
+static void ipmr_free_table(struct mr_table *mrt);
+
static int ip_mr_forward(struct net *net, struct mr_table *mrt,
struct sk_buff *skb, struct mfc_cache *cache,
int local);
@@ -131,6 +133,7 @@ static int ipmr_cache_report(struct mr_table *mrt,
struct sk_buff *pkt, vifi_t vifi, int assert);
static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
struct mfc_cache *c, struct rtmsg *rtm);
+static void mroute_clean_tables(struct mr_table *mrt);
static void ipmr_expire_process(unsigned long arg);
#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
@@ -271,7 +274,7 @@ static void __net_exit ipmr_rules_exit(struct net *net)
list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
list_del(&mrt->list);
- kfree(mrt);
+ ipmr_free_table(mrt);
}
fib_rules_unregister(net->ipv4.mr_rules_ops);
}
@@ -299,7 +302,7 @@ static int __net_init ipmr_rules_init(struct net *net)
static void __net_exit ipmr_rules_exit(struct net *net)
{
- kfree(net->ipv4.mrt);
+ ipmr_free_table(net->ipv4.mrt);
}
#endif
@@ -336,6 +339,13 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
return mrt;
}
+static void ipmr_free_table(struct mr_table *mrt)
+{
+ del_timer_sync(&mrt->ipmr_expire_timer);
+ mroute_clean_tables(mrt);
+ kfree(mrt);
+}
+
/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index ea4a23813d26..9c87cde28ff8 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -148,7 +148,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
hdr, NULL, &matchoff, &matchlen,
&addr, &port) > 0) {
- unsigned int matchend, poff, plen, buflen, n;
+ unsigned int olen, matchend, poff, plen, buflen, n;
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
/* We're only interested in headers related to this
@@ -163,17 +163,18 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
goto next;
}
+ olen = *datalen;
if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
&addr, port))
return NF_DROP;
- matchend = matchoff + matchlen;
+ matchend = matchoff + matchlen + *datalen - olen;
/* The maddr= parameter (RFC 2361) specifies where to send
* the reply. */
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
"maddr=", &poff, &plen,
- &addr) > 0 &&
+ &addr, true) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
buflen = sprintf(buffer, "%pI4",
@@ -187,7 +188,7 @@ static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
* from which the server received the request. */
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
"received=", &poff, &plen,
- &addr) > 0 &&
+ &addr, false) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
buflen = sprintf(buffer, "%pI4",
@@ -501,7 +502,10 @@ static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff,
ret = nf_ct_expect_related(rtcp_exp);
if (ret == 0)
break;
- else if (ret != -EBUSY) {
+ else if (ret == -EBUSY) {
+ nf_ct_unexpect_related(rtp_exp);
+ continue;
+ } else if (ret < 0) {
nf_ct_unexpect_related(rtp_exp);
port = 0;
break;
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index ff0f071969ea..d23c6571ba1c 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -131,18 +131,20 @@ found:
* 0 - deliver
* 1 - block
*/
-static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+static int icmp_filter(const struct sock *sk, const struct sk_buff *skb)
{
- int type;
+ struct icmphdr _hdr;
+ const struct icmphdr *hdr;
- if (!pskb_may_pull(skb, sizeof(struct icmphdr)))
+ hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (!hdr)
return 1;
- type = icmp_hdr(skb)->type;
- if (type < 32) {
+ if (hdr->type < 32) {
__u32 data = raw_sk(sk)->filter.data;
- return ((1 << type) & data) != 0;
+ return ((1U << hdr->type) & data) != 0;
}
/* Do not block unknown ICMP types */
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index c035251beb07..fd9af60397b5 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -70,7 +70,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <linux/bootmem.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
@@ -80,7 +79,6 @@
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
-#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
@@ -88,11 +86,9 @@
#include <linux/mroute.h>
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
-#include <linux/jhash.h>
#include <linux/rcupdate.h>
#include <linux/times.h>
#include <linux/slab.h>
-#include <linux/prefetch.h>
#include <net/dst.h>
#include <net/net_namespace.h>
#include <net/protocol.h>
@@ -206,11 +202,6 @@ EXPORT_SYMBOL(ip_tos2prio);
static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat);
#define RT_CACHE_STAT_INC(field) __this_cpu_inc(rt_cache_stat.field)
-static inline int rt_genid(struct net *net)
-{
- return atomic_read(&net->ipv4.rt_genid);
-}
-
#ifdef CONFIG_PROC_FS
static void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
{
@@ -451,27 +442,9 @@ static inline bool rt_is_expired(const struct rtable *rth)
return rth->rt_genid != rt_genid(dev_net(rth->dst.dev));
}
-/*
- * Perturbation of rt_genid by a small quantity [1..256]
- * Using 8 bits of shuffling ensure we can call rt_cache_invalidate()
- * many times (2^24) without giving recent rt_genid.
- * Jenkins hash is strong enough that litle changes of rt_genid are OK.
- */
-static void rt_cache_invalidate(struct net *net)
-{
- unsigned char shuffle;
-
- get_random_bytes(&shuffle, sizeof(shuffle));
- atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
-}
-
-/*
- * delay < 0 : invalidate cache (fast : entries will be deleted later)
- * delay >= 0 : invalidate & flush cache (can be long)
- */
-void rt_cache_flush(struct net *net, int delay)
+void rt_cache_flush(struct net *net)
{
- rt_cache_invalidate(net);
+ rt_genid_bump(net);
}
static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
@@ -938,12 +911,14 @@ static u32 __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
if (mtu < ip_rt_min_pmtu)
mtu = ip_rt_min_pmtu;
+ rcu_read_lock();
if (fib_lookup(dev_net(rt->dst.dev), fl4, &res) == 0) {
struct fib_nh *nh = &FIB_RES_NH(res);
update_or_create_fnhe(nh, fl4->daddr, 0, mtu,
jiffies + ip_rt_mtu_expires);
}
+ rcu_read_unlock();
return mtu;
}
@@ -960,7 +935,7 @@ static void ip_rt_update_pmtu(struct dst_entry *dst, struct sock *sk,
dst->obsolete = DST_OBSOLETE_KILL;
} else {
rt->rt_pmtu = mtu;
- dst_set_expires(&rt->dst, ip_rt_mtu_expires);
+ rt->dst.expires = max(1UL, jiffies + ip_rt_mtu_expires);
}
}
@@ -1267,7 +1242,7 @@ static void ipv4_dst_destroy(struct dst_entry *dst)
{
struct rtable *rt = (struct rtable *) dst;
- if (dst->flags & DST_NOCACHE) {
+ if (!list_empty(&rt->rt_uncached)) {
spin_lock_bh(&rt_uncached_lock);
list_del(&rt->rt_uncached);
spin_unlock_bh(&rt_uncached_lock);
@@ -2032,7 +2007,6 @@ struct rtable *__ip_route_output_key(struct net *net, struct flowi4 *fl4)
}
dev_out = net->loopback_dev;
fl4->flowi4_oif = dev_out->ifindex;
- res.fi = NULL;
flags |= RTCF_LOCAL;
goto make_route;
}
@@ -2348,7 +2322,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb)
void ip_rt_multicast_event(struct in_device *in_dev)
{
- rt_cache_flush(dev_net(in_dev->dev), 0);
+ rt_cache_flush(dev_net(in_dev->dev));
}
#ifdef CONFIG_SYSCTL
@@ -2357,16 +2331,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
size_t *lenp, loff_t *ppos)
{
if (write) {
- int flush_delay;
- ctl_table ctl;
- struct net *net;
-
- memcpy(&ctl, __ctl, sizeof(ctl));
- ctl.data = &flush_delay;
- proc_dointvec(&ctl, write, buffer, lenp, ppos);
-
- net = (struct net *)__ctl->extra1;
- rt_cache_flush(net, flush_delay);
+ rt_cache_flush((struct net *)__ctl->extra1);
return 0;
}
@@ -2536,8 +2501,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = {
static __net_init int rt_genid_init(struct net *net)
{
- get_random_bytes(&net->ipv4.rt_genid,
- sizeof(net->ipv4.rt_genid));
+ atomic_set(&net->rt_genid, 0);
get_random_bytes(&net->ipv4.dev_addr_genid,
sizeof(net->ipv4.dev_addr_genid));
return 0;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 4b6487a68279..1b5ce96707a3 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -184,7 +184,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
int ret;
unsigned long vec[3];
struct net *net = current->nsproxy->net_ns;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
struct mem_cgroup *memcg;
#endif
@@ -203,7 +203,7 @@ static int ipv4_tcp_mem(ctl_table *ctl, int write,
if (ret)
return ret;
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
rcu_read_lock();
memcg = mem_cgroup_from_task(current);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index e7e6eeae49c0..5f6419341821 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -811,7 +811,9 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
old_size_goal + mss_now > xmit_size_goal)) {
xmit_size_goal = old_size_goal;
} else {
- tp->xmit_size_goal_segs = xmit_size_goal / mss_now;
+ tp->xmit_size_goal_segs =
+ min_t(u16, xmit_size_goal / mss_now,
+ sk->sk_gso_max_segs);
xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
}
}
@@ -1760,8 +1762,14 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
#ifdef CONFIG_NET_DMA
- if (tp->ucopy.dma_chan)
- dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+ if (tp->ucopy.dma_chan) {
+ if (tp->rcv_wnd == 0 &&
+ !skb_queue_empty(&sk->sk_async_wait_queue)) {
+ tcp_service_net_dma(sk, true);
+ tcp_cleanup_rbuf(sk, copied);
+ } else
+ dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+ }
#endif
if (copied >= target) {
/* Do not sleep, just process backlog. */
@@ -2323,10 +2331,17 @@ static int tcp_repair_options_est(struct tcp_sock *tp,
tp->rx_opt.mss_clamp = opt.opt_val;
break;
case TCPOPT_WINDOW:
- if (opt.opt_val > 14)
- return -EFBIG;
+ {
+ u16 snd_wscale = opt.opt_val & 0xFFFF;
+ u16 rcv_wscale = opt.opt_val >> 16;
+
+ if (snd_wscale > 14 || rcv_wscale > 14)
+ return -EFBIG;
- tp->rx_opt.snd_wscale = opt.opt_val;
+ tp->rx_opt.snd_wscale = snd_wscale;
+ tp->rx_opt.rcv_wscale = rcv_wscale;
+ tp->rx_opt.wscale_ok = 1;
+ }
break;
case TCPOPT_SACK_PERM:
if (opt.opt_val != 0)
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 4d4db16e336e..1432cdb0644c 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -291,7 +291,8 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight)
left = tp->snd_cwnd - in_flight;
if (sk_can_gso(sk) &&
left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
- left * tp->mss_cache < sk->sk_gso_max_size)
+ left * tp->mss_cache < sk->sk_gso_max_size &&
+ left < sk->sk_gso_max_segs)
return true;
return left <= tcp_max_tso_deferred_mss(tp);
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 9be30b039ae3..d377f4854cb8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2926,13 +2926,14 @@ static void tcp_enter_recovery(struct sock *sk, bool ece_ack)
* tcp_xmit_retransmit_queue().
*/
static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
- int newly_acked_sacked, bool is_dupack,
+ int prior_sacked, bool is_dupack,
int flag)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
int do_lost = is_dupack || ((flag & FLAG_DATA_SACKED) &&
(tcp_fackets_out(tp) > tp->reordering));
+ int newly_acked_sacked = 0;
int fast_rexmit = 0;
if (WARN_ON(!tp->packets_out && tp->sacked_out))
@@ -2992,6 +2993,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
tcp_add_reno_sack(sk);
} else
do_lost = tcp_try_undo_partial(sk, pkts_acked);
+ newly_acked_sacked = pkts_acked + tp->sacked_out - prior_sacked;
break;
case TCP_CA_Loss:
if (flag & FLAG_DATA_ACKED)
@@ -3013,6 +3015,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked,
if (is_dupack)
tcp_add_reno_sack(sk);
}
+ newly_acked_sacked = pkts_acked + tp->sacked_out - prior_sacked;
if (icsk->icsk_ca_state <= TCP_CA_Disorder)
tcp_try_undo_dsack(sk);
@@ -3590,7 +3593,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
int prior_packets;
int prior_sacked = tp->sacked_out;
int pkts_acked = 0;
- int newly_acked_sacked = 0;
bool frto_cwnd = false;
/* If the ack is older than previous acks
@@ -3666,8 +3668,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una);
pkts_acked = prior_packets - tp->packets_out;
- newly_acked_sacked = (prior_packets - prior_sacked) -
- (tp->packets_out - tp->sacked_out);
if (tp->frto_counter)
frto_cwnd = tcp_process_frto(sk, flag);
@@ -3681,7 +3681,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
tcp_may_raise_cwnd(sk, flag))
tcp_cong_avoid(sk, ack, prior_in_flight);
is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP));
- tcp_fastretrans_alert(sk, pkts_acked, newly_acked_sacked,
+ tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
is_dupack, flag);
} else {
if ((flag & FLAG_DATA_ACKED) && !frto_cwnd)
@@ -3698,7 +3698,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
no_queue:
/* If data was DSACKed, see if we can undo a cwnd reduction. */
if (flag & FLAG_DSACKING_ACK)
- tcp_fastretrans_alert(sk, pkts_acked, newly_acked_sacked,
+ tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
is_dupack, flag);
/* If this ack opens up a zero window, clear backoff. It was
* being used to time the probes, and is probably far higher than
@@ -3718,8 +3718,7 @@ old_ack:
*/
if (TCP_SKB_CB(skb)->sacked) {
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una);
- newly_acked_sacked = tp->sacked_out - prior_sacked;
- tcp_fastretrans_alert(sk, pkts_acked, newly_acked_sacked,
+ tcp_fastretrans_alert(sk, pkts_acked, prior_sacked,
is_dupack, flag);
}
@@ -4351,19 +4350,20 @@ static void tcp_ofo_queue(struct sock *sk)
static bool tcp_prune_ofo_queue(struct sock *sk);
static int tcp_prune_queue(struct sock *sk);
-static int tcp_try_rmem_schedule(struct sock *sk, unsigned int size)
+static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb,
+ unsigned int size)
{
if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
- !sk_rmem_schedule(sk, size)) {
+ !sk_rmem_schedule(sk, skb, size)) {
if (tcp_prune_queue(sk) < 0)
return -1;
- if (!sk_rmem_schedule(sk, size)) {
+ if (!sk_rmem_schedule(sk, skb, size)) {
if (!tcp_prune_ofo_queue(sk))
return -1;
- if (!sk_rmem_schedule(sk, size))
+ if (!sk_rmem_schedule(sk, skb, size))
return -1;
}
}
@@ -4418,7 +4418,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
TCP_ECN_check_ce(tp, skb);
- if (unlikely(tcp_try_rmem_schedule(sk, skb->truesize))) {
+ if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPOFODROP);
__kfree_skb(skb);
return;
@@ -4552,17 +4552,17 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
{
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
struct tcphdr *th;
bool fragstolen;
- if (tcp_try_rmem_schedule(sk, size + sizeof(*th)))
- goto err;
-
skb = alloc_skb(size + sizeof(*th), sk->sk_allocation);
if (!skb)
goto err;
+ if (tcp_try_rmem_schedule(sk, skb, size + sizeof(*th)))
+ goto err_free;
+
th = (struct tcphdr *)skb_put(skb, sizeof(*th));
skb_reset_transport_header(skb);
memset(th, 0, sizeof(*th));
@@ -4633,7 +4633,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
if (eaten <= 0) {
queue_and_out:
if (eaten < 0 &&
- tcp_try_rmem_schedule(sk, skb->truesize))
+ tcp_try_rmem_schedule(sk, skb, skb->truesize))
goto drop;
eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
@@ -4661,7 +4661,7 @@ queue_and_out:
if (eaten > 0)
kfree_skb_partial(skb, fragstolen);
- else if (!sock_flag(sk, SOCK_DEAD))
+ if (!sock_flag(sk, SOCK_DEAD))
sk->sk_data_ready(sk, 0);
return;
}
@@ -5391,6 +5391,8 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
{
struct tcp_sock *tp = tcp_sk(sk);
+ if (unlikely(sk->sk_rx_dst == NULL))
+ inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
/*
* Header prediction.
* The code loosely follows the one in the famous
@@ -5554,8 +5556,7 @@ no_ack:
#endif
if (eaten)
kfree_skb_partial(skb, fragstolen);
- else
- sk->sk_data_ready(sk, 0);
+ sk->sk_data_ready(sk, 0);
return 0;
}
}
@@ -5604,7 +5605,7 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)
tcp_set_state(sk, TCP_ESTABLISHED);
if (skb != NULL) {
- inet_sk_rx_dst_set(sk, skb);
+ icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
security_inet_conn_established(sk, skb);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 7f91e5ac8277..00a748d14062 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -417,10 +417,12 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
tp->mtu_info = info;
- if (!sock_owned_by_user(sk))
+ if (!sock_owned_by_user(sk)) {
tcp_v4_mtu_reduced(sk);
- else
- set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags);
+ } else {
+ if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags))
+ sock_hold(sk);
+ }
goto out;
}
@@ -1462,6 +1464,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
goto exit_nonewsk;
newsk->sk_gso_type = SKB_GSO_TCPV4;
+ inet_sk_rx_dst_set(newsk, skb);
newtp = tcp_sk(newsk);
newinet = inet_sk(newsk);
@@ -1627,9 +1630,6 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
sk->sk_rx_dst = NULL;
}
}
- if (unlikely(sk->sk_rx_dst == NULL))
- inet_sk_rx_dst_set(sk, skb);
-
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
rsk = sk;
goto reset;
@@ -1872,10 +1872,21 @@ static struct timewait_sock_ops tcp_timewait_sock_ops = {
.twsk_destructor= tcp_twsk_destructor,
};
+void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ dst_hold(dst);
+ sk->sk_rx_dst = dst;
+ inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+}
+EXPORT_SYMBOL(inet_sk_rx_dst_set);
+
const struct inet_connection_sock_af_ops ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
+ .sk_rx_dst_set = inet_sk_rx_dst_set,
.conn_request = tcp_v4_conn_request,
.syn_recv_sock = tcp_v4_syn_recv_sock,
.net_header_len = sizeof(struct iphdr),
@@ -2633,7 +2644,7 @@ struct proto tcp_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
.init_cgroup = tcp_init_cgroup,
.destroy_cgroup = tcp_destroy_cgroup,
.proto_cgroup = tcp_proto_cgroup,
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index 2288a6399e1e..0abe67bb4d3a 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -731,6 +731,18 @@ static int __net_init tcp_net_metrics_init(struct net *net)
static void __net_exit tcp_net_metrics_exit(struct net *net)
{
+ unsigned int i;
+
+ for (i = 0; i < (1U << net->ipv4.tcp_metrics_hash_log) ; i++) {
+ struct tcp_metrics_block *tm, *next;
+
+ tm = rcu_dereference_protected(net->ipv4.tcp_metrics_hash[i].chain, 1);
+ while (tm) {
+ next = rcu_dereference_protected(tm->tcpm_next, 1);
+ kfree(tm);
+ tm = next;
+ }
+ }
kfree(net->ipv4.tcp_metrics_hash);
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 232a90c3ec86..6ff7f10dce9d 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -387,8 +387,6 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
struct tcp_sock *oldtp = tcp_sk(sk);
struct tcp_cookie_values *oldcvp = oldtp->cookie_values;
- inet_sk_rx_dst_set(newsk, skb);
-
/* TCP Cookie Transactions require space for the cookie pair,
* as it differs for each connection. There is no need to
* copy any s_data_payload stored at the original socket.
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 33cd065cfbd8..d04632673a9e 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -910,14 +910,18 @@ void tcp_release_cb(struct sock *sk)
if (flags & (1UL << TCP_TSQ_DEFERRED))
tcp_tsq_handler(sk);
- if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED))
+ if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED)) {
tcp_write_timer_handler(sk);
-
- if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED))
+ __sock_put(sk);
+ }
+ if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED)) {
tcp_delack_timer_handler(sk);
-
- if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED))
+ __sock_put(sk);
+ }
+ if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED)) {
sk->sk_prot->mtu_reduced(sk);
+ __sock_put(sk);
+ }
}
EXPORT_SYMBOL(tcp_release_cb);
@@ -940,7 +944,7 @@ void __init tcp_tasklet_init(void)
* We cant xmit new skbs from this context, as we might already
* hold qdisc lock.
*/
-void tcp_wfree(struct sk_buff *skb)
+static void tcp_wfree(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct tcp_sock *tp = tcp_sk(sk);
@@ -1522,21 +1526,21 @@ static void tcp_cwnd_validate(struct sock *sk)
* when we would be allowed to send the split-due-to-Nagle skb fully.
*/
static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb,
- unsigned int mss_now, unsigned int cwnd)
+ unsigned int mss_now, unsigned int max_segs)
{
const struct tcp_sock *tp = tcp_sk(sk);
- u32 needed, window, cwnd_len;
+ u32 needed, window, max_len;
window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
- cwnd_len = mss_now * cwnd;
+ max_len = mss_now * max_segs;
- if (likely(cwnd_len <= window && skb != tcp_write_queue_tail(sk)))
- return cwnd_len;
+ if (likely(max_len <= window && skb != tcp_write_queue_tail(sk)))
+ return max_len;
needed = min(skb->len, window);
- if (cwnd_len <= needed)
- return cwnd_len;
+ if (max_len <= needed)
+ return max_len;
return needed - needed % mss_now;
}
@@ -1765,7 +1769,8 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb)
limit = min(send_win, cong_win);
/* If a full-sized TSO skb can be sent, do it. */
- if (limit >= sk->sk_gso_max_size)
+ if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
+ sk->sk_gso_max_segs * tp->mss_cache))
goto send_now;
/* Middle in queue won't get any more data, full sendable already? */
@@ -1999,7 +2004,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
limit = mss_now;
if (tso_segs > 1 && !tcp_urg_mode(tp))
limit = tcp_mss_split_point(sk, skb, mss_now,
- cwnd_quota);
+ min_t(unsigned int,
+ cwnd_quota,
+ sk->sk_gso_max_segs));
if (skb->len > limit &&
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
@@ -2045,7 +2052,8 @@ void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
if (unlikely(sk->sk_state == TCP_CLOSE))
return;
- if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
+ if (tcp_write_xmit(sk, cur_mss, nonagle, 0,
+ sk_gfp_atomic(sk, GFP_ATOMIC)))
tcp_check_probe_timer(sk);
}
@@ -2666,7 +2674,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
if (cvp != NULL && cvp->s_data_constant && cvp->s_data_desired)
s_data_desired = cvp->s_data_desired;
- skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired, GFP_ATOMIC);
+ skb = alloc_skb(MAX_TCP_HEADER + 15 + s_data_desired,
+ sk_gfp_atomic(sk, GFP_ATOMIC));
if (unlikely(!skb)) {
dst_release(dst);
return NULL;
@@ -3064,7 +3073,7 @@ void tcp_send_ack(struct sock *sk)
* tcp_transmit_skb() will set the ownership to this
* sock.
*/
- buff = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
+ buff = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
if (buff == NULL) {
inet_csk_schedule_ack(sk);
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
@@ -3079,7 +3088,7 @@ void tcp_send_ack(struct sock *sk)
/* Send it off, this clears delayed acks for us. */
TCP_SKB_CB(buff)->when = tcp_time_stamp;
- tcp_transmit_skb(sk, buff, 0, GFP_ATOMIC);
+ tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
}
/* This routine sends a packet with an out of date sequence
@@ -3099,7 +3108,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent)
struct sk_buff *skb;
/* We don't queue it, tcp_transmit_skb() sets ownership. */
- skb = alloc_skb(MAX_TCP_HEADER, GFP_ATOMIC);
+ skb = alloc_skb(MAX_TCP_HEADER, sk_gfp_atomic(sk, GFP_ATOMIC));
if (skb == NULL)
return -1;
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 6df36ad55a38..b774a03bd1dc 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -252,7 +252,8 @@ static void tcp_delack_timer(unsigned long data)
inet_csk(sk)->icsk_ack.blocked = 1;
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
/* deleguate our work to tcp_release_cb() */
- set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags);
+ if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
+ sock_hold(sk);
}
bh_unlock_sock(sk);
sock_put(sk);
@@ -481,7 +482,8 @@ static void tcp_write_timer(unsigned long data)
tcp_write_timer_handler(sk);
} else {
/* deleguate our work to tcp_release_cb() */
- set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags);
+ if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
+ sock_hold(sk);
}
bh_unlock_sock(sk);
sock_put(sk);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index b4c3582a991f..2814f66dac64 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -758,7 +758,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4)
uh->check = CSUM_MANGLED_0;
send:
- err = ip_send_skb(skb);
+ err = ip_send_skb(sock_net(sk), skb);
if (err) {
if (err == -ENOBUFS && !inet->recverr) {
UDP_INC_STATS_USER(sock_net(sk),
@@ -1226,6 +1226,11 @@ try_again:
if (unlikely(err)) {
trace_kfree_skb(skb, udp_recvmsg);
+ if (!peeked) {
+ atomic_inc(&sk->sk_drops);
+ UDP_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_INERRORS, is_udplite);
+ }
goto out_free;
}
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 79181819a24f..6bc85f7c31e3 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -494,8 +494,7 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
struct net_device *dev;
struct inet6_dev *idev;
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
+ for_each_netdev(net, dev) {
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.forwarding) ^ (!newf);
@@ -504,7 +503,6 @@ static void addrconf_forward_change(struct net *net, __s32 newf)
dev_forward_change(idev);
}
}
- rcu_read_unlock();
}
static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 6dc7fd353ef5..282f3723ee19 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -167,8 +167,6 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
struct esp_data *esp = x->data;
/* skb is pure payload to encrypt */
- err = -ENOMEM;
-
aead = esp->aead;
alen = crypto_aead_authsize(aead);
@@ -203,8 +201,10 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
}
tmp = esp_alloc_tmp(aead, nfrags + sglists, seqhilen);
- if (!tmp)
+ if (!tmp) {
+ err = -ENOMEM;
goto error;
+ }
seqhi = esp_tmp_seqhi(tmp);
iv = esp_tmp_iv(aead, tmp, seqhilen);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 0251a6005be8..c4f934176cab 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -175,33 +175,12 @@ void __inet6_csk_dst_store(struct sock *sk, struct dst_entry *dst,
const struct in6_addr *saddr)
{
__ip6_dst_store(sk, dst, daddr, saddr);
-
-#ifdef CONFIG_XFRM
- {
- struct rt6_info *rt = (struct rt6_info *)dst;
- rt->rt6i_flow_cache_genid = atomic_read(&flow_cache_genid);
- }
-#endif
}
static inline
struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie)
{
- struct dst_entry *dst;
-
- dst = __sk_dst_check(sk, cookie);
-
-#ifdef CONFIG_XFRM
- if (dst) {
- struct rt6_info *rt = (struct rt6_info *)dst;
- if (rt->rt6i_flow_cache_genid != atomic_read(&flow_cache_genid)) {
- __sk_dst_reset(sk);
- dst = NULL;
- }
- }
-#endif
-
- return dst;
+ return __sk_dst_check(sk, cookie);
}
static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 13690d650c3e..286acfc21250 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -819,6 +819,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
offsetof(struct rt6_info, rt6i_src),
allow_create, replace_required);
+ if (IS_ERR(sn)) {
+ err = PTR_ERR(sn);
+ sn = NULL;
+ }
if (!sn) {
/* If it is failed, discard just allocated
root, and then (in st_failure) stale node
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 5b087c31d87b..0f9bdc5ee9f3 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -86,28 +86,30 @@ static int mip6_mh_len(int type)
static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
{
- struct ip6_mh *mh;
+ struct ip6_mh _hdr;
+ const struct ip6_mh *mh;
- if (!pskb_may_pull(skb, (skb_transport_offset(skb)) + 8) ||
- !pskb_may_pull(skb, (skb_transport_offset(skb) +
- ((skb_transport_header(skb)[1] + 1) << 3))))
+ mh = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (!mh)
return -1;
- mh = (struct ip6_mh *)skb_transport_header(skb);
+ if (((mh->ip6mh_hdrlen + 1) << 3) > skb->len)
+ return -1;
if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
- mip6_param_prob(skb, 0, ((&mh->ip6mh_hdrlen) -
- skb_network_header(skb)));
+ mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) +
+ skb_network_header_len(skb));
return -1;
}
if (mh->ip6mh_proto != IPPROTO_NONE) {
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
mh->ip6mh_proto);
- mip6_param_prob(skb, 0, ((&mh->ip6mh_proto) -
- skb_network_header(skb)));
+ mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) +
+ skb_network_header_len(skb));
return -1;
}
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index da2e92d05c15..745a32042950 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -307,10 +307,10 @@ static int __net_init ipv6_proc_init_net(struct net *net)
goto proc_dev_snmp6_fail;
return 0;
+proc_dev_snmp6_fail:
+ proc_net_remove(net, "snmp6");
proc_snmp6_fail:
proc_net_remove(net, "sockstat6");
-proc_dev_snmp6_fail:
- proc_net_remove(net, "dev_snmp6");
return -ENOMEM;
}
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ef0579d5bca6..4a5f78b50495 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -107,21 +107,20 @@ found:
* 0 - deliver
* 1 - block
*/
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb)
{
- struct icmp6hdr *icmph;
- struct raw6_sock *rp = raw6_sk(sk);
-
- if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
- __u32 *data = &rp->filter.data[0];
- int bit_nr;
+ struct icmp6hdr *_hdr;
+ const struct icmp6hdr *hdr;
- icmph = (struct icmp6hdr *) skb->data;
- bit_nr = icmph->icmp6_type;
+ hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (hdr) {
+ const __u32 *data = &raw6_sk(sk)->filter.data[0];
+ unsigned int type = hdr->icmp6_type;
- return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0;
+ return (data[type >> 5] & (1U << (type & 31))) != 0;
}
- return 0;
+ return 1;
}
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 8e80fd279100..854e4018d205 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -226,7 +226,7 @@ static struct rt6_info ip6_null_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -ENETUNREACH,
.input = ip6_pkt_discard,
.output = ip6_pkt_discard_out,
@@ -246,7 +246,7 @@ static struct rt6_info ip6_prohibit_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -EACCES,
.input = ip6_pkt_prohibit,
.output = ip6_pkt_prohibit_out,
@@ -261,7 +261,7 @@ static struct rt6_info ip6_blk_hole_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -EINVAL,
.input = dst_discard,
.output = dst_discard,
@@ -281,13 +281,14 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
struct fib6_table *table)
{
struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
- 0, DST_OBSOLETE_NONE, flags);
+ 0, DST_OBSOLETE_FORCE_CHK, flags);
if (rt) {
struct dst_entry *dst = &rt->dst;
memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
+ rt->rt6i_genid = rt_genid(net);
}
return rt;
}
@@ -1031,6 +1032,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
rt = (struct rt6_info *) dst;
+ /* All IPV6 dsts are created with ->obsolete set to the value
+ * DST_OBSOLETE_FORCE_CHK which forces validation calls down
+ * into this function always.
+ */
+ if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
+ return NULL;
+
if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
if (rt->rt6i_peer_genid != rt6_peer_genid()) {
if (!rt6_has_peer(rt))
@@ -1397,8 +1405,6 @@ int ip6_route_add(struct fib6_config *cfg)
goto out;
}
- rt->dst.obsolete = -1;
-
if (cfg->fc_flags & RTF_EXPIRES)
rt6_set_expires(rt, jiffies +
clock_t_to_jiffies(cfg->fc_expires));
@@ -2080,7 +2086,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
rt->rt6i_idev = idev;
- rt->dst.obsolete = -1;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
if (anycast)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 221224e72507..acd32e3f1b68 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -94,6 +94,18 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
}
#endif
+static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+ const struct rt6_info *rt = (const struct rt6_info *)dst;
+
+ dst_hold(dst);
+ sk->sk_rx_dst = dst;
+ inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+ if (rt->rt6i_node)
+ inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum;
+}
+
static void tcp_v6_hash(struct sock *sk)
{
if (sk->sk_state != TCP_CLOSE) {
@@ -391,8 +403,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
tp->mtu_info = ntohl(info);
if (!sock_owned_by_user(sk))
tcp_v6_mtu_reduced(sk);
- else
- set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags);
+ else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED,
+ &tp->tsq_flags))
+ sock_hold(sk);
goto out;
}
@@ -1270,6 +1283,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->sk_gso_type = SKB_GSO_TCPV6;
__ip6_dst_store(newsk, dst, NULL, NULL);
+ inet6_sk_rx_dst_set(newsk, skb);
newtcp6sk = (struct tcp6_sock *)newsk;
inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
@@ -1299,7 +1313,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
/* Clone pktoptions received with SYN */
newnp->pktoptions = NULL;
if (treq->pktopts != NULL) {
- newnp->pktoptions = skb_clone(treq->pktopts, GFP_ATOMIC);
+ newnp->pktoptions = skb_clone(treq->pktopts,
+ sk_gfp_atomic(sk, GFP_ATOMIC));
consume_skb(treq->pktopts);
treq->pktopts = NULL;
if (newnp->pktoptions)
@@ -1349,7 +1364,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
* across. Shucks.
*/
tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr,
- AF_INET6, key->key, key->keylen, GFP_ATOMIC);
+ AF_INET6, key->key, key->keylen,
+ sk_gfp_atomic(sk, GFP_ATOMIC));
}
#endif
@@ -1442,10 +1458,20 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
--ANK (980728)
*/
if (np->rxopt.all)
- opt_skb = skb_clone(skb, GFP_ATOMIC);
+ opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+ struct dst_entry *dst = sk->sk_rx_dst;
+
sock_rps_save_rxhash(sk, skb);
+ if (dst) {
+ if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
+ dst->ops->check(dst, np->rx_dst_cookie) == NULL) {
+ dst_release(dst);
+ sk->sk_rx_dst = NULL;
+ }
+ }
+
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))
goto reset;
if (opt_skb)
@@ -1703,9 +1729,9 @@ static void tcp_v6_early_demux(struct sk_buff *skb)
struct dst_entry *dst = sk->sk_rx_dst;
struct inet_sock *icsk = inet_sk(sk);
if (dst)
- dst = dst_check(dst, 0);
+ dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
if (dst &&
- icsk->rx_dst_ifindex == inet6_iif(skb))
+ icsk->rx_dst_ifindex == skb->skb_iif)
skb_dst_set_noref(skb, dst);
}
}
@@ -1721,6 +1747,7 @@ static const struct inet_connection_sock_af_ops ipv6_specific = {
.queue_xmit = inet6_csk_xmit,
.send_check = tcp_v6_send_check,
.rebuild_header = inet6_sk_rebuild_header,
+ .sk_rx_dst_set = inet6_sk_rx_dst_set,
.conn_request = tcp_v6_conn_request,
.syn_recv_sock = tcp_v6_syn_recv_sock,
.net_header_len = sizeof(struct ipv6hdr),
@@ -1752,6 +1779,7 @@ static const struct inet_connection_sock_af_ops ipv6_mapped = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
+ .sk_rx_dst_set = inet_sk_rx_dst_set,
.conn_request = tcp_v6_conn_request,
.syn_recv_sock = tcp_v6_syn_recv_sock,
.net_header_len = sizeof(struct iphdr),
@@ -2015,7 +2043,7 @@ struct proto tcpv6_prot = {
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
-#ifdef CONFIG_CGROUP_MEM_RES_CTLR_KMEM
+#ifdef CONFIG_MEMCG_KMEM
.proto_cgroup = tcp_proto_cgroup,
#endif
};
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 99d0077b56b8..07e2bfef6845 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -394,6 +394,17 @@ try_again:
}
if (unlikely(err)) {
trace_kfree_skb(skb, udpv6_recvmsg);
+ if (!peeked) {
+ atomic_inc(&sk->sk_drops);
+ if (is_udp4)
+ UDP_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_INERRORS,
+ is_udplite);
+ else
+ UDP6_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_INERRORS,
+ is_udplite);
+ }
goto out_free;
}
if (!peeked) {
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index ef39812107b1..f8c4c08ffb60 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -73,6 +73,13 @@ static int xfrm6_get_tos(const struct flowi *fl)
return 0;
}
+static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst)
+{
+ struct rt6_info *rt = (struct rt6_info *)xdst;
+
+ rt6_init_peer(rt, net->ipv6.peers);
+}
+
static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
int nfheader_len)
{
@@ -286,6 +293,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
.get_saddr = xfrm6_get_saddr,
.decode_session = _decode_session6,
.get_tos = xfrm6_get_tos,
+ .init_dst = xfrm6_init_dst,
.init_path = xfrm6_init_path,
.fill_dst = xfrm6_fill_dst,
.blackhole_route = ip6_blackhole_route,
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 393355d37b47..1a9f3723c13c 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1347,11 +1347,10 @@ static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
/* Remove from tunnel list */
spin_lock_bh(&pn->l2tp_tunnel_list_lock);
list_del_rcu(&tunnel->list);
+ kfree_rcu(tunnel, rcu);
spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
- synchronize_rcu();
atomic_dec(&l2tp_tunnel_count);
- kfree(tunnel);
}
/* Create a socket for the tunnel, if one isn't set up by
@@ -1502,6 +1501,8 @@ out:
return err;
}
+static struct lock_class_key l2tp_socket_class;
+
int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
{
struct l2tp_tunnel *tunnel = NULL;
@@ -1606,6 +1607,8 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
tunnel->old_sk_destruct = sk->sk_destruct;
sk->sk_destruct = &l2tp_tunnel_destruct;
tunnel->sock = sk;
+ lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock");
+
sk->sk_allocation = GFP_ATOMIC;
/* Add tunnel to our list */
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index a38ec6cdeee1..56d583e083a7 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -163,6 +163,7 @@ struct l2tp_tunnel_cfg {
struct l2tp_tunnel {
int magic; /* Should be L2TP_TUNNEL_MAGIC */
+ struct rcu_head rcu;
rwlock_t hlist_lock; /* protect session_hlist */
struct hlist_head session_hlist[L2TP_HASH_SIZE];
/* hashed list of sessions,
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index f9ee74deeac2..3bfb34aaee29 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -153,7 +153,7 @@ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb,
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, skb->data, length);
}
- if (!pskb_may_pull(skb, sizeof(ETH_HLEN)))
+ if (!pskb_may_pull(skb, ETH_HLEN))
goto error;
secpath_reset(skb);
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 35e1e4bde587..927547171bc7 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -410,6 +410,7 @@ static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
lsa->l2tp_family = AF_INET6;
lsa->l2tp_flowinfo = 0;
lsa->l2tp_scope_id = 0;
+ lsa->l2tp_unused = 0;
if (peer) {
if (!lsk->peer_conn_id)
return -ENOTCONN;
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index d71cd9229a47..6f936358d664 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -80,8 +80,8 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&l2tp_nl_family, 0, L2TP_CMD_NOOP);
- if (IS_ERR(hdr)) {
- ret = PTR_ERR(hdr);
+ if (!hdr) {
+ ret = -EMSGSIZE;
goto err_out;
}
@@ -250,8 +250,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags,
L2TP_CMD_TUNNEL_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
@@ -617,8 +617,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags
sk = tunnel->sock;
hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index f6fe4d400502..c2190005a114 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -969,14 +969,13 @@ static int llc_ui_getname(struct socket *sock, struct sockaddr *uaddr,
struct sockaddr_llc sllc;
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
- int rc = 0;
+ int rc = -EBADF;
memset(&sllc, 0, sizeof(sllc));
lock_sock(sk);
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
*uaddrlen = sizeof(sllc);
- memset(uaddr, 0, *uaddrlen);
if (peer) {
rc = -ENOTCONN;
if (sk->sk_state != TCP_ESTABLISHED)
@@ -1206,7 +1205,7 @@ static int __init llc2_init(void)
rc = llc_proc_init();
if (rc != 0) {
printk(llc_proc_err_msg);
- goto out_unregister_llc_proto;
+ goto out_station;
}
rc = llc_sysctl_init();
if (rc) {
@@ -1226,7 +1225,8 @@ out_sysctl:
llc_sysctl_exit();
out_proc:
llc_proc_exit();
-out_unregister_llc_proto:
+out_station:
+ llc_station_exit();
proto_unregister(&llc_proto);
goto out;
}
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index e32cab44ea95..dd3e83328ad5 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -42,6 +42,7 @@ static void (*llc_type_handlers[2])(struct llc_sap *sap,
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
struct sk_buff *skb))
{
+ smp_wmb(); /* ensure initialisation is complete before it's called */
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = handler;
}
@@ -50,11 +51,19 @@ void llc_remove_pack(int type)
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = NULL;
+ synchronize_net();
}
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
{
+ /* Ensure initialisation is complete before it's called */
+ if (handler)
+ smp_wmb();
+
llc_station_handler = handler;
+
+ if (!handler)
+ synchronize_net();
}
/**
@@ -150,6 +159,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
int dest;
int (*rcv)(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
+ void (*sta_handler)(struct sk_buff *skb);
+ void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
if (!net_eq(dev_net(dev), &init_net))
goto drop;
@@ -182,7 +193,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
*/
rcv = rcu_dereference(sap->rcv_func);
dest = llc_pdu_type(skb);
- if (unlikely(!dest || !llc_type_handlers[dest - 1])) {
+ sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL;
+ if (unlikely(!sap_handler)) {
if (rcv)
rcv(skb, dev, pt, orig_dev);
else
@@ -193,7 +205,7 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
if (cskb)
rcv(cskb, dev, pt, orig_dev);
}
- llc_type_handlers[dest - 1](sap, skb);
+ sap_handler(sap, skb);
}
llc_sap_put(sap);
out:
@@ -202,9 +214,10 @@ drop:
kfree_skb(skb);
goto out;
handle_station:
- if (!llc_station_handler)
+ sta_handler = ACCESS_ONCE(llc_station_handler);
+ if (!sta_handler)
goto drop;
- llc_station_handler(skb);
+ sta_handler(skb);
goto out;
}
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 39a8d8924b9c..b2f2bac2c2a2 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -268,7 +268,7 @@ static int llc_station_ac_send_null_dsap_xid_c(struct sk_buff *skb)
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -293,7 +293,7 @@ static int llc_station_ac_send_xid_r(struct sk_buff *skb)
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -322,7 +322,7 @@ static int llc_station_ac_send_test_r(struct sk_buff *skb)
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -687,12 +687,8 @@ static void llc_station_rcv(struct sk_buff *skb)
llc_station_state_process(skb);
}
-int __init llc_station_init(void)
+void __init llc_station_init(void)
{
- int rc = -ENOBUFS;
- struct sk_buff *skb;
- struct llc_station_state_ev *ev;
-
skb_queue_head_init(&llc_main_station.mac_pdu_q);
skb_queue_head_init(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
@@ -700,23 +696,12 @@ int __init llc_station_init(void)
(unsigned long)&llc_main_station);
llc_main_station.ack_timer.expires = jiffies +
sysctl_llc_station_ack_timeout;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (!skb)
- goto out;
- rc = 0;
- llc_set_station_handler(llc_station_rcv);
- ev = llc_station_ev(skb);
- memset(ev, 0, sizeof(*ev));
llc_main_station.maximum_retry = 1;
- llc_main_station.state = LLC_STATION_STATE_DOWN;
- ev->type = LLC_STATION_EV_TYPE_SIMPLE;
- ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
- rc = llc_station_next_state(skb);
-out:
- return rc;
+ llc_main_station.state = LLC_STATION_STATE_UP;
+ llc_set_station_handler(llc_station_rcv);
}
-void __exit llc_station_exit(void)
+void llc_station_exit(void)
{
llc_set_station_handler(NULL);
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index d41974aacf51..a58c0b649ba1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1378,6 +1378,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
else
memset(next_hop, 0, ETH_ALEN);
+ memset(pinfo, 0, sizeof(*pinfo));
+
pinfo->generation = mesh_paths_generation;
pinfo->filled = MPATH_INFO_FRAME_QLEN |
@@ -1396,7 +1398,6 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
pinfo->discovery_timeout =
jiffies_to_msecs(mpath->discovery_timeout);
pinfo->discovery_retries = mpath->discovery_retries;
- pinfo->flags = 0;
if (mpath->flags & MESH_PATH_ACTIVE)
pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
if (mpath->flags & MESH_PATH_RESOLVING)
@@ -1405,10 +1406,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
if (mpath->flags & MESH_PATH_FIXED)
pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
- if (mpath->flags & MESH_PATH_RESOLVING)
- pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
-
- pinfo->flags = mpath->flags;
+ if (mpath->flags & MESH_PATH_RESOLVED)
+ pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
}
static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6fac18c0423f..85572353a7e3 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -622,6 +622,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
+ del_timer_sync(&sdata->u.mesh.mesh_path_timer);
/*
* If the timer fired while we waited for it, it will have
* requeued the work. Now the work will be running again
@@ -634,6 +635,8 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local);
+
+ sdata->u.mesh.timers_running = 0;
}
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cef0c9e79aba..f76b83341cf9 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1430,6 +1430,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
del_timer_sync(&sdata->u.mgd.timer);
del_timer_sync(&sdata->u.mgd.chswitch_timer);
+
+ sdata->u.mgd.timers_running = 0;
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -3246,6 +3248,8 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
goto out_unlock;
err_clear:
+ memset(ifmgd->bssid, 0, ETH_ALEN);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->auth_data = NULL;
err_free:
kfree(auth_data);
@@ -3437,6 +3441,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
err = 0;
goto out;
err_clear:
+ memset(ifmgd->bssid, 0, ETH_ALEN);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
ifmgd->assoc_data = NULL;
err_free:
kfree(assoc_data);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index bcaee5d12839..839dd9737989 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -299,7 +299,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
- local->scan_sdata = NULL;
+ rcu_assign_pointer(local->scan_sdata, NULL);
local->scanning = 0;
local->scan_channel = NULL;
@@ -984,7 +984,6 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
kfree(local->sched_scan_ies.ie[i]);
drv_sched_scan_stop(local, sdata);
- rcu_assign_pointer(local->sched_scan_sdata, NULL);
}
out:
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index acf712ffb5e6..c5e8c9c31f76 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1811,37 +1811,31 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
- int is_mesh_mcast = 1;
- const u8 *mesh_da;
+ /* DS -> MBSS (802.11-2012 13.11.3.3).
+ * For unicast with unknown forwarding information,
+ * destination might be in the MBSS or if that fails
+ * forwarded to another mesh gate. In either case
+ * resolution will be handled in ieee80211_xmit(), so
+ * leave the original DA. This also works for mcast */
+ const u8 *mesh_da = skb->data;
+
+ if (mppath)
+ mesh_da = mppath->mpp;
+ else if (mpath)
+ mesh_da = mpath->dst;
+ rcu_read_unlock();
- if (is_multicast_ether_addr(skb->data))
- /* DA TA mSA AE:SA */
- mesh_da = skb->data;
- else {
- static const u8 bcast[ETH_ALEN] =
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- if (mppath) {
- /* RA TA mDA mSA AE:DA SA */
- mesh_da = mppath->mpp;
- is_mesh_mcast = 0;
- } else if (mpath) {
- mesh_da = mpath->dst;
- is_mesh_mcast = 0;
- } else {
- /* DA TA mSA AE:SA */
- mesh_da = bcast;
- }
- }
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
mesh_da, sdata->vif.addr);
- rcu_read_unlock();
- if (is_mesh_mcast)
+ if (is_multicast_ether_addr(mesh_da))
+ /* DA TA mSA AE:SA */
meshhdrlen =
ieee80211_new_mesh_header(&mesh_hdr,
sdata,
skb->data + ETH_ALEN,
NULL);
else
+ /* RA TA mDA mSA AE:DA SA */
meshhdrlen =
ieee80211_new_mesh_header(&mesh_hdr,
sdata,
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 84444dda194b..f51013c07b9f 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -1171,8 +1171,10 @@ ip_vs_add_service(struct net *net, struct ip_vs_service_user_kern *u,
goto out_err;
}
svc->stats.cpustats = alloc_percpu(struct ip_vs_cpu_stats);
- if (!svc->stats.cpustats)
+ if (!svc->stats.cpustats) {
+ ret = -ENOMEM;
goto out_err;
+ }
/* I'm the first user of the service */
atomic_set(&svc->usecnt, 0);
@@ -2759,6 +2761,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
struct ip_vs_timeout_user t;
+ memset(&t, 0, sizeof(t));
__ip_vs_get_timeouts(net, &t);
if (copy_to_user(user, &t, sizeof(t)) != 0)
ret = -EFAULT;
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index cf4875565d67..2ceec64b19f9 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -249,12 +249,15 @@ static void death_by_event(unsigned long ul_conntrack)
{
struct nf_conn *ct = (void *)ul_conntrack;
struct net *net = nf_ct_net(ct);
+ struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
+
+ BUG_ON(ecache == NULL);
if (nf_conntrack_event(IPCT_DESTROY, ct) < 0) {
/* bad luck, let's retry again */
- ct->timeout.expires = jiffies +
+ ecache->timeout.expires = jiffies +
(random32() % net->ct.sysctl_events_retry_timeout);
- add_timer(&ct->timeout);
+ add_timer(&ecache->timeout);
return;
}
/* we've got the event delivered, now it's dying */
@@ -268,6 +271,9 @@ static void death_by_event(unsigned long ul_conntrack)
void nf_ct_insert_dying_list(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
+ struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
+
+ BUG_ON(ecache == NULL);
/* add this conntrack to the dying list */
spin_lock_bh(&nf_conntrack_lock);
@@ -275,10 +281,10 @@ void nf_ct_insert_dying_list(struct nf_conn *ct)
&net->ct.dying);
spin_unlock_bh(&nf_conntrack_lock);
/* set a new timer to retry event delivery */
- setup_timer(&ct->timeout, death_by_event, (unsigned long)ct);
- ct->timeout.expires = jiffies +
+ setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct);
+ ecache->timeout.expires = jiffies +
(random32() % net->ct.sysctl_events_retry_timeout);
- add_timer(&ct->timeout);
+ add_timer(&ecache->timeout);
}
EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list);
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 45cf602a76bc..527651a53a45 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -361,23 +361,6 @@ static void evict_oldest_expect(struct nf_conn *master,
}
}
-static inline int refresh_timer(struct nf_conntrack_expect *i)
-{
- struct nf_conn_help *master_help = nfct_help(i->master);
- const struct nf_conntrack_expect_policy *p;
-
- if (!del_timer(&i->timeout))
- return 0;
-
- p = &rcu_dereference_protected(
- master_help->helper,
- lockdep_is_held(&nf_conntrack_lock)
- )->expect_policy[i->class];
- i->timeout.expires = jiffies + p->timeout * HZ;
- add_timer(&i->timeout);
- return 1;
-}
-
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
{
const struct nf_conntrack_expect_policy *p;
@@ -386,7 +369,7 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
struct nf_conn_help *master_help = nfct_help(master);
struct nf_conntrack_helper *helper;
struct net *net = nf_ct_exp_net(expect);
- struct hlist_node *n;
+ struct hlist_node *n, *next;
unsigned int h;
int ret = 1;
@@ -395,12 +378,12 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
goto out;
}
h = nf_ct_expect_dst_hash(&expect->tuple);
- hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
+ hlist_for_each_entry_safe(i, n, next, &net->ct.expect_hash[h], hnode) {
if (expect_matches(i, expect)) {
- /* Refresh timer: if it's dying, ignore.. */
- if (refresh_timer(i)) {
- ret = 0;
- goto out;
+ if (del_timer(&i->timeout)) {
+ nf_ct_unlink_expect(i);
+ nf_ct_expect_put(i);
+ break;
}
} else if (expect_clash(i, expect)) {
ret = -EBUSY;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 14f67a2cbcb5..9807f3278fcb 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1896,10 +1896,15 @@ static int
ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
{
struct nlattr *cda[CTA_MAX+1];
+ int ret;
nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
- return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+ spin_lock_bh(&nf_conntrack_lock);
+ ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+ spin_unlock_bh(&nf_conntrack_lock);
+
+ return ret;
}
static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
@@ -2785,7 +2790,8 @@ static int __init ctnetlink_init(void)
goto err_unreg_subsys;
}
- if (register_pernet_subsys(&ctnetlink_net_ops)) {
+ ret = register_pernet_subsys(&ctnetlink_net_ops);
+ if (ret < 0) {
pr_err("ctnetlink_init: cannot register pernet operations\n");
goto err_unreg_exp_subsys;
}
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index a5ac11ebef33..e046b3756aab 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -158,21 +158,18 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
* sCL -> sSS
*/
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2 */
-/*synack*/ { sIV, sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIG, sSR },
+/*synack*/ { sIV, sIV, sSR, sIV, sIV, sIV, sIV, sIV, sIV, sSR },
/*
* sNO -> sIV Too late and no reason to do anything
* sSS -> sIV Client can't send SYN and then SYN/ACK
* sS2 -> sSR SYN/ACK sent to SYN2 in simultaneous open
- * sSR -> sIG
- * sES -> sIG Error: SYNs in window outside the SYN_SENT state
- * are errors. Receiver will reply with RST
- * and close the connection.
- * Or we are not in sync and hold a dead connection.
- * sFW -> sIG
- * sCW -> sIG
- * sLA -> sIG
- * sTW -> sIG
- * sCL -> sIG
+ * sSR -> sSR Late retransmitted SYN/ACK in simultaneous open
+ * sES -> sIV Invalid SYN/ACK packets sent by the client
+ * sFW -> sIV
+ * sCW -> sIV
+ * sLA -> sIV
+ * sTW -> sIV
+ * sCL -> sIV
*/
/* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2 */
/*fin*/ { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
@@ -633,15 +630,9 @@ static bool tcp_in_window(const struct nf_conn *ct,
ack = sack = receiver->td_end;
}
- if (seq == end
- && (!tcph->rst
- || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
+ if (tcph->rst && seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)
/*
- * Packets contains no data: we assume it is valid
- * and check the ack value only.
- * However RST segments are always validated by their
- * SEQ number, except when seq == 0 (reset sent answering
- * SYN.
+ * RST sent answering SYN.
*/
seq = end = sender->td_end;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 758a1bacc126..5c0a112aeee6 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -183,12 +183,12 @@ static int media_len(const struct nf_conn *ct, const char *dptr,
return len + digits_len(ct, dptr, limit, shift);
}
-static int parse_addr(const struct nf_conn *ct, const char *cp,
- const char **endp, union nf_inet_addr *addr,
- const char *limit)
+static int sip_parse_addr(const struct nf_conn *ct, const char *cp,
+ const char **endp, union nf_inet_addr *addr,
+ const char *limit, bool delim)
{
const char *end;
- int ret = 0;
+ int ret;
if (!ct)
return 0;
@@ -197,16 +197,28 @@ static int parse_addr(const struct nf_conn *ct, const char *cp,
switch (nf_ct_l3num(ct)) {
case AF_INET:
ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
+ if (ret == 0)
+ return 0;
break;
case AF_INET6:
+ if (cp < limit && *cp == '[')
+ cp++;
+ else if (delim)
+ return 0;
+
ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
+ if (ret == 0)
+ return 0;
+
+ if (end < limit && *end == ']')
+ end++;
+ else if (delim)
+ return 0;
break;
default:
BUG();
}
- if (ret == 0 || end == cp)
- return 0;
if (endp)
*endp = end;
return 1;
@@ -219,7 +231,7 @@ static int epaddr_len(const struct nf_conn *ct, const char *dptr,
union nf_inet_addr addr;
const char *aux = dptr;
- if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
+ if (!sip_parse_addr(ct, dptr, &dptr, &addr, limit, true)) {
pr_debug("ip: %s parse failed.!\n", dptr);
return 0;
}
@@ -296,7 +308,7 @@ int ct_sip_parse_request(const struct nf_conn *ct,
return 0;
dptr += shift;
- if (!parse_addr(ct, dptr, &end, addr, limit))
+ if (!sip_parse_addr(ct, dptr, &end, addr, limit, true))
return -1;
if (end < limit && *end == ':') {
end++;
@@ -550,7 +562,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
if (ret == 0)
return ret;
- if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
+ if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true))
return -1;
if (*c == ':') {
c++;
@@ -599,7 +611,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
unsigned int dataoff, unsigned int datalen,
const char *name,
unsigned int *matchoff, unsigned int *matchlen,
- union nf_inet_addr *addr)
+ union nf_inet_addr *addr, bool delim)
{
const char *limit = dptr + datalen;
const char *start, *end;
@@ -613,7 +625,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
return 0;
start += strlen(name);
- if (!parse_addr(ct, start, &end, addr, limit))
+ if (!sip_parse_addr(ct, start, &end, addr, limit, delim))
return 0;
*matchoff = start - dptr;
*matchlen = end - start;
@@ -675,6 +687,47 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr,
return 1;
}
+static int sdp_parse_addr(const struct nf_conn *ct, const char *cp,
+ const char **endp, union nf_inet_addr *addr,
+ const char *limit)
+{
+ const char *end;
+ int ret;
+
+ memset(addr, 0, sizeof(*addr));
+ switch (nf_ct_l3num(ct)) {
+ case AF_INET:
+ ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
+ break;
+ case AF_INET6:
+ ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
+ break;
+ default:
+ BUG();
+ }
+
+ if (ret == 0)
+ return 0;
+ if (endp)
+ *endp = end;
+ return 1;
+}
+
+/* skip ip address. returns its length. */
+static int sdp_addr_len(const struct nf_conn *ct, const char *dptr,
+ const char *limit, int *shift)
+{
+ union nf_inet_addr addr;
+ const char *aux = dptr;
+
+ if (!sdp_parse_addr(ct, dptr, &dptr, &addr, limit)) {
+ pr_debug("ip: %s parse failed.!\n", dptr);
+ return 0;
+ }
+
+ return dptr - aux;
+}
+
/* SDP header parsing: a SDP session description contains an ordered set of
* headers, starting with a section containing general session parameters,
* optionally followed by multiple media descriptions.
@@ -686,10 +739,10 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr,
*/
static const struct sip_header ct_sdp_hdrs[] = {
[SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
- [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
- [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
- [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
- [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
+ [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len),
+ [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len),
+ [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len),
+ [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len),
[SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len),
};
@@ -775,8 +828,8 @@ static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr,
if (ret <= 0)
return ret;
- if (!parse_addr(ct, dptr + *matchoff, NULL, addr,
- dptr + *matchoff + *matchlen))
+ if (!sdp_parse_addr(ct, dptr + *matchoff, NULL, addr,
+ dptr + *matchoff + *matchlen))
return -1;
return 1;
}
@@ -1515,7 +1568,6 @@ static int sip_help_udp(struct sk_buff *skb, unsigned int protoff,
}
static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly;
-static char sip_names[MAX_PORTS][4][sizeof("sip-65535")] __read_mostly;
static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
[SIP_EXPECT_SIGNALLING] = {
@@ -1585,9 +1637,9 @@ static int __init nf_conntrack_sip_init(void)
sip[i][j].me = THIS_MODULE;
if (ports[i] == SIP_PORT)
- sprintf(sip_names[i][j], "sip");
+ sprintf(sip[i][j].name, "sip");
else
- sprintf(sip_names[i][j], "sip-%u", i);
+ sprintf(sip[i][j].name, "sip-%u", i);
pr_debug("port #%u: %u\n", i, ports[i]);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 169ab59ed9d4..5cfb5bedb2b8 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -381,6 +381,7 @@ __build_packet_message(struct nfulnl_instance *inst,
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
sk_buff_data_t old_tail = inst->skb->tail;
+ struct sock *sk;
nlh = nlmsg_put(inst->skb, 0, 0,
NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET,
@@ -480,7 +481,7 @@ __build_packet_message(struct nfulnl_instance *inst,
}
if (indev && skb_mac_header_was_set(skb)) {
- if (nla_put_be32(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)) ||
+ if (nla_put_be16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)) ||
nla_put_be16(inst->skb, NFULA_HWLEN,
htons(skb->dev->hard_header_len)) ||
nla_put(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len,
@@ -499,18 +500,19 @@ __build_packet_message(struct nfulnl_instance *inst,
}
/* UID */
- if (skb->sk) {
- read_lock_bh(&skb->sk->sk_callback_lock);
- if (skb->sk->sk_socket && skb->sk->sk_socket->file) {
- struct file *file = skb->sk->sk_socket->file;
+ sk = skb->sk;
+ if (sk && sk->sk_state != TCP_TIME_WAIT) {
+ read_lock_bh(&sk->sk_callback_lock);
+ if (sk->sk_socket && sk->sk_socket->file) {
+ struct file *file = sk->sk_socket->file;
__be32 uid = htonl(file->f_cred->fsuid);
__be32 gid = htonl(file->f_cred->fsgid);
- read_unlock_bh(&skb->sk->sk_callback_lock);
+ read_unlock_bh(&sk->sk_callback_lock);
if (nla_put_be32(inst->skb, NFULA_UID, uid) ||
nla_put_be32(inst->skb, NFULA_GID, gid))
goto nla_put_failure;
} else
- read_unlock_bh(&skb->sk->sk_callback_lock);
+ read_unlock_bh(&sk->sk_callback_lock);
}
/* local sequence number */
@@ -996,8 +998,10 @@ static int __init nfnetlink_log_init(void)
#ifdef CONFIG_PROC_FS
if (!proc_create("nfnetlink_log", 0440,
- proc_net_netfilter, &nful_file_ops))
+ proc_net_netfilter, &nful_file_ops)) {
+ status = -ENOMEM;
goto cleanup_logger;
+ }
#endif
return status;
diff --git a/net/netfilter/xt_LOG.c b/net/netfilter/xt_LOG.c
index ff5f75fddb15..91e9af4d1f42 100644
--- a/net/netfilter/xt_LOG.c
+++ b/net/netfilter/xt_LOG.c
@@ -145,6 +145,19 @@ static int dump_tcp_header(struct sbuff *m, const struct sk_buff *skb,
return 0;
}
+static void dump_sk_uid_gid(struct sbuff *m, struct sock *sk)
+{
+ if (!sk || sk->sk_state == TCP_TIME_WAIT)
+ return;
+
+ read_lock_bh(&sk->sk_callback_lock);
+ if (sk->sk_socket && sk->sk_socket->file)
+ sb_add(m, "UID=%u GID=%u ",
+ sk->sk_socket->file->f_cred->fsuid,
+ sk->sk_socket->file->f_cred->fsgid);
+ read_unlock_bh(&sk->sk_callback_lock);
+}
+
/* One level of recursion won't kill us */
static void dump_ipv4_packet(struct sbuff *m,
const struct nf_loginfo *info,
@@ -361,14 +374,8 @@ static void dump_ipv4_packet(struct sbuff *m,
}
/* Max length: 15 "UID=4294967295 " */
- if ((logflags & XT_LOG_UID) && !iphoff && skb->sk) {
- read_lock_bh(&skb->sk->sk_callback_lock);
- if (skb->sk->sk_socket && skb->sk->sk_socket->file)
- sb_add(m, "UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_cred->fsuid,
- skb->sk->sk_socket->file->f_cred->fsgid);
- read_unlock_bh(&skb->sk->sk_callback_lock);
- }
+ if ((logflags & XT_LOG_UID) && !iphoff)
+ dump_sk_uid_gid(m, skb->sk);
/* Max length: 16 "MARK=0xFFFFFFFF " */
if (!iphoff && skb->mark)
@@ -436,8 +443,8 @@ log_packet_common(struct sbuff *m,
const struct nf_loginfo *loginfo,
const char *prefix)
{
- sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level,
- prefix,
+ sb_add(m, KERN_SOH "%c%sIN=%s OUT=%s ",
+ '0' + loginfo->u.log.level, prefix,
in ? in->name : "",
out ? out->name : "");
#ifdef CONFIG_BRIDGE_NETFILTER
@@ -717,14 +724,8 @@ static void dump_ipv6_packet(struct sbuff *m,
}
/* Max length: 15 "UID=4294967295 " */
- if ((logflags & XT_LOG_UID) && recurse && skb->sk) {
- read_lock_bh(&skb->sk->sk_callback_lock);
- if (skb->sk->sk_socket && skb->sk->sk_socket->file)
- sb_add(m, "UID=%u GID=%u ",
- skb->sk->sk_socket->file->f_cred->fsuid,
- skb->sk->sk_socket->file->f_cred->fsgid);
- read_unlock_bh(&skb->sk->sk_callback_lock);
- }
+ if ((logflags & XT_LOG_UID) && recurse)
+ dump_sk_uid_gid(m, skb->sk);
/* Max length: 16 "MARK=0xFFFFFFFF " */
if (!recurse && skb->mark)
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c
index 5c22ce8ab309..a4c1e4528cac 100644
--- a/net/netfilter/xt_limit.c
+++ b/net/netfilter/xt_limit.c
@@ -117,11 +117,11 @@ static int limit_mt_check(const struct xt_mtchk_param *par)
/* For SMP, we only want to use one set of state. */
r->master = priv;
+ /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
+ 128. */
+ priv->prev = jiffies;
+ priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
if (r->cost == 0) {
- /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
- 128. */
- priv->prev = jiffies;
- priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
r->credit_cap = priv->credit; /* Credits full. */
r->cost = user2credits(r->avg);
}
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 5463969da45b..527023823b5c 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1362,7 +1362,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (NULL == siocb->scm)
siocb->scm = &scm;
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, true);
if (err < 0)
return err;
@@ -1373,7 +1373,8 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
dst_pid = addr->nl_pid;
dst_group = ffs(addr->nl_groups);
err = -EPERM;
- if (dst_group && !netlink_capable(sock, NL_NONROOT_SEND))
+ if ((dst_group || dst_pid) &&
+ !netlink_capable(sock, NL_NONROOT_SEND))
goto out;
} else {
dst_pid = nlk->dst_pid;
@@ -2147,6 +2148,7 @@ static void __init netlink_add_usersock_entry(void)
rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners);
nl_table[NETLINK_USERSOCK].module = THIS_MODULE;
nl_table[NETLINK_USERSOCK].registered = 1;
+ nl_table[NETLINK_USERSOCK].nl_nonroot = NL_NONROOT_SEND;
netlink_table_ungrab();
}
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 06592d8b4a2b..7261eb81974f 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -601,7 +601,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (!capable(CAP_NET_BIND_SERVICE)) {
dev_put(dev);
release_sock(sk);
- return -EACCES;
+ return -EPERM;
}
nr->user_addr = addr->fsa_digipeater[0];
nr->source_addr = addr->fsa_ax25.sax25_call;
@@ -1169,7 +1169,12 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ er = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ if (er < 0) {
+ skb_free_datagram(sk, skb);
+ release_sock(sk);
+ return er;
+ }
if (sax != NULL) {
sax->sax25_family = AF_NETROM;
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index f3f96badf5aa..954405ceae9e 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -45,7 +45,7 @@ static int make_writable(struct sk_buff *skb, int write_len)
return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
}
-/* remove VLAN header from packet and update csum accrodingly. */
+/* remove VLAN header from packet and update csum accordingly. */
static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
{
struct vlan_hdr *vhdr;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index d8277d29e710..cf58cedad083 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -425,10 +425,10 @@ static int validate_sample(const struct nlattr *attr,
static int validate_tp_port(const struct sw_flow_key *flow_key)
{
if (flow_key->eth.type == htons(ETH_P_IP)) {
- if (flow_key->ipv4.tp.src && flow_key->ipv4.tp.dst)
+ if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst)
return 0;
} else if (flow_key->eth.type == htons(ETH_P_IPV6)) {
- if (flow_key->ipv6.tp.src && flow_key->ipv6.tp.dst)
+ if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst)
return 0;
}
@@ -460,7 +460,7 @@ static int validate_set(const struct nlattr *a,
if (flow_key->eth.type != htons(ETH_P_IP))
return -EINVAL;
- if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst)
+ if (!flow_key->ip.proto)
return -EINVAL;
ipv4_key = nla_data(ovs_key);
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 9b75617ca4e0..c30df1a10c67 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -145,15 +145,17 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
* OVS_KEY_ATTR_PRIORITY 4 -- 4 8
* OVS_KEY_ATTR_IN_PORT 4 -- 4 8
* OVS_KEY_ATTR_ETHERNET 12 -- 4 16
+ * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype)
* OVS_KEY_ATTR_8021Q 4 -- 4 8
- * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8
+ * OVS_KEY_ATTR_ENCAP 0 -- 4 4 (VLAN encapsulation)
+ * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (inner VLAN ethertype)
* OVS_KEY_ATTR_IPV6 40 -- 4 44
* OVS_KEY_ATTR_ICMPV6 2 2 4 8
* OVS_KEY_ATTR_ND 28 -- 4 32
* -------------------------------------------------
- * total 132
+ * total 144
*/
-#define FLOW_BUFSIZE 132
+#define FLOW_BUFSIZE 144
int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index ceaca7c134a0..c5c9e2a54218 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1079,7 +1079,7 @@ static void *packet_current_rx_frame(struct packet_sock *po,
default:
WARN(1, "TPACKET version not supported\n");
BUG();
- return 0;
+ return NULL;
}
}
@@ -1273,6 +1273,14 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po)
spin_unlock(&f->lock);
}
+static bool match_fanout_group(struct packet_type *ptype, struct sock * sk)
+{
+ if (ptype->af_packet_priv == (void*)((struct packet_sock *)sk)->fanout)
+ return true;
+
+ return false;
+}
+
static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{
struct packet_sock *po = pkt_sk(sk);
@@ -1325,6 +1333,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
match->prot_hook.dev = po->prot_hook.dev;
match->prot_hook.func = packet_rcv_fanout;
match->prot_hook.af_packet_priv = match;
+ match->prot_hook.id_match = match_fanout_group;
dev_add_pack(&match->prot_hook);
list_add(&match->list, &fanout_list);
}
@@ -1936,7 +1945,6 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
if (likely(po->tx_ring.pg_vec)) {
ph = skb_shinfo(skb)->destructor_arg;
- BUG_ON(__packet_get_status(po, ph) != TP_STATUS_SENDING);
BUG_ON(atomic_read(&po->tx_ring.pending) == 0);
atomic_dec(&po->tx_ring.pending);
__packet_set_status(po, ph, TP_STATUS_AVAILABLE);
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index f10fb8256442..05d60859d8e3 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -67,6 +67,9 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
struct tcf_common *pc;
int ret = 0;
int err;
+#ifdef CONFIG_GACT_PROB
+ struct tc_gact_p *p_parm = NULL;
+#endif
if (nla == NULL)
return -EINVAL;
@@ -82,6 +85,12 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
#ifndef CONFIG_GACT_PROB
if (tb[TCA_GACT_PROB] != NULL)
return -EOPNOTSUPP;
+#else
+ if (tb[TCA_GACT_PROB]) {
+ p_parm = nla_data(tb[TCA_GACT_PROB]);
+ if (p_parm->ptype >= MAX_RAND)
+ return -EINVAL;
+ }
#endif
pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info);
@@ -103,8 +112,7 @@ static int tcf_gact_init(struct nlattr *nla, struct nlattr *est,
spin_lock_bh(&gact->tcf_lock);
gact->tcf_action = parm->action;
#ifdef CONFIG_GACT_PROB
- if (tb[TCA_GACT_PROB] != NULL) {
- struct tc_gact_p *p_parm = nla_data(tb[TCA_GACT_PROB]);
+ if (p_parm) {
gact->tcfg_paction = p_parm->paction;
gact->tcfg_pval = p_parm->pval;
gact->tcfg_ptype = p_parm->ptype;
@@ -133,7 +141,7 @@ static int tcf_gact(struct sk_buff *skb, const struct tc_action *a,
spin_lock(&gact->tcf_lock);
#ifdef CONFIG_GACT_PROB
- if (gact->tcfg_ptype && gact_rand[gact->tcfg_ptype] != NULL)
+ if (gact->tcfg_ptype)
action = gact_rand[gact->tcfg_ptype](gact);
else
action = gact->tcf_action;
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 60e281ad0f07..58fb3c7aab9e 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -185,7 +185,12 @@ err3:
err2:
kfree(tname);
err1:
- kfree(pc);
+ if (ret == ACT_P_CREATED) {
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
+ }
return err;
}
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index fe81cc18e9e0..9c0fd0c78814 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -200,13 +200,12 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a,
out:
if (err) {
m->tcf_qstats.overlimits++;
- /* should we be asking for packet to be dropped?
- * may make sense for redirect case only
- */
- retval = TC_ACT_SHOT;
- } else {
+ if (m->tcfm_eaction != TCA_EGRESS_MIRROR)
+ retval = TC_ACT_SHOT;
+ else
+ retval = m->tcf_action;
+ } else
retval = m->tcf_action;
- }
spin_unlock(&m->tcf_lock);
return retval;
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 26aa2f6ce257..45c53ab067a6 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -74,7 +74,10 @@ static int tcf_pedit_init(struct nlattr *nla, struct nlattr *est,
p = to_pedit(pc);
keys = kmalloc(ksize, GFP_KERNEL);
if (keys == NULL) {
- kfree(pc);
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
return -ENOMEM;
}
ret = ACT_P_CREATED;
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 3922f2a2821b..3714f60f0b3c 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -131,7 +131,10 @@ static int tcf_simp_init(struct nlattr *nla, struct nlattr *est,
d = to_defact(pc);
ret = alloc_defdata(d, defdata);
if (ret < 0) {
- kfree(pc);
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
return ret;
}
d->tcf_action = parm->action;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 6aabd77d1cfd..564b9fc8efd3 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -250,10 +250,11 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
else if ((cl = defmap[res.classid & TC_PRIO_MAX]) == NULL)
cl = defmap[TC_PRIO_BESTEFFORT];
- if (cl == NULL || cl->level >= head->level)
+ if (cl == NULL)
goto fallback;
}
-
+ if (cl->level >= head->level)
+ goto fallback;
#ifdef CONFIG_NET_CLS_ACT
switch (result) {
case TC_ACT_QUEUED:
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 9fc1c62ec80e..4e606fcb2534 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -191,7 +191,6 @@ static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
if (list_empty(&flow->flowchain)) {
list_add_tail(&flow->flowchain, &q->new_flows);
- codel_vars_init(&flow->cvars);
q->new_flow_count++;
flow->deficit = q->quantum;
flow->dropped = 0;
@@ -418,6 +417,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt)
struct fq_codel_flow *flow = q->flows + i;
INIT_LIST_HEAD(&flow->flowchain);
+ codel_vars_init(&flow->cvars);
}
}
if (sch->limit >= 1)
diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c
index e901583e4ea5..d42234c0f13b 100644
--- a/net/sched/sch_gred.c
+++ b/net/sched/sch_gred.c
@@ -102,9 +102,8 @@ static inline int gred_wred_mode_check(struct Qdisc *sch)
if (q == NULL)
continue;
- for (n = 0; n < table->DPs; n++)
- if (table->tab[n] && table->tab[n] != q &&
- table->tab[n]->prio == q->prio)
+ for (n = i + 1; n < table->DPs; n++)
+ if (table->tab[n] && table->tab[n]->prio == q->prio)
return 1;
}
@@ -137,6 +136,7 @@ static inline void gred_store_wred_set(struct gred_sched *table,
struct gred_sched_data *q)
{
table->wred_set.qavg = q->vars.qavg;
+ table->wred_set.qidlestart = q->vars.qidlestart;
}
static inline int gred_use_ecn(struct gred_sched *t)
@@ -176,7 +176,7 @@ static int gred_enqueue(struct sk_buff *skb, struct Qdisc *sch)
skb->tc_index = (skb->tc_index & ~GRED_VQ_MASK) | dp;
}
- /* sum up all the qaves of prios <= to ours to get the new qave */
+ /* sum up all the qaves of prios < ours to get the new qave */
if (!gred_wred_mode(t) && gred_rio_mode(t)) {
int i;
@@ -260,16 +260,18 @@ static struct sk_buff *gred_dequeue(struct Qdisc *sch)
} else {
q->backlog -= qdisc_pkt_len(skb);
- if (!q->backlog && !gred_wred_mode(t))
- red_start_of_idle_period(&q->vars);
+ if (gred_wred_mode(t)) {
+ if (!sch->qstats.backlog)
+ red_start_of_idle_period(&t->wred_set);
+ } else {
+ if (!q->backlog)
+ red_start_of_idle_period(&q->vars);
+ }
}
return skb;
}
- if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
- red_start_of_idle_period(&t->wred_set);
-
return NULL;
}
@@ -291,19 +293,20 @@ static unsigned int gred_drop(struct Qdisc *sch)
q->backlog -= len;
q->stats.other++;
- if (!q->backlog && !gred_wred_mode(t))
- red_start_of_idle_period(&q->vars);
+ if (gred_wred_mode(t)) {
+ if (!sch->qstats.backlog)
+ red_start_of_idle_period(&t->wred_set);
+ } else {
+ if (!q->backlog)
+ red_start_of_idle_period(&q->vars);
+ }
}
qdisc_drop(skb, sch);
return len;
}
- if (gred_wred_mode(t) && !red_is_idling(&t->wred_set))
- red_start_of_idle_period(&t->wred_set);
-
return 0;
-
}
static void gred_reset(struct Qdisc *sch)
@@ -535,6 +538,7 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
for (i = 0; i < MAX_DPs; i++) {
struct gred_sched_data *q = table->tab[i];
struct tc_gred_qopt opt;
+ unsigned long qavg;
memset(&opt, 0, sizeof(opt));
@@ -566,7 +570,9 @@ static int gred_dump(struct Qdisc *sch, struct sk_buff *skb)
if (gred_wred_mode(table))
gred_load_wred_set(table, q);
- opt.qave = red_calc_qavg(&q->parms, &q->vars, q->vars.qavg);
+ qavg = red_calc_qavg(&q->parms, &q->vars,
+ q->vars.qavg >> q->parms.Wlog);
+ opt.qave = qavg >> q->parms.Wlog;
append_opt:
if (nla_append(skb, sizeof(opt), &opt) < 0)
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 9af01f3df18c..211a21217045 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -203,6 +203,34 @@ out:
return index;
}
+/* Length of the next packet (0 if the queue is empty). */
+static unsigned int qdisc_peek_len(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+
+ skb = sch->ops->peek(sch);
+ return skb ? qdisc_pkt_len(skb) : 0;
+}
+
+static void qfq_deactivate_class(struct qfq_sched *, struct qfq_class *);
+static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
+ unsigned int len);
+
+static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl,
+ u32 lmax, u32 inv_w, int delta_w)
+{
+ int i;
+
+ /* update qfq-specific data */
+ cl->lmax = lmax;
+ cl->inv_w = inv_w;
+ i = qfq_calc_index(cl->inv_w, cl->lmax);
+
+ cl->grp = &q->groups[i];
+
+ q->wsum += delta_w;
+}
+
static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
struct nlattr **tca, unsigned long *arg)
{
@@ -250,6 +278,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
lmax = 1UL << QFQ_MTU_SHIFT;
if (cl != NULL) {
+ bool need_reactivation = false;
+
if (tca[TCA_RATE]) {
err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
qdisc_root_sleeping_lock(sch),
@@ -258,12 +288,29 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
return err;
}
- if (inv_w != cl->inv_w) {
- sch_tree_lock(sch);
- q->wsum += delta_w;
- cl->inv_w = inv_w;
- sch_tree_unlock(sch);
+ if (lmax == cl->lmax && inv_w == cl->inv_w)
+ return 0; /* nothing to update */
+
+ i = qfq_calc_index(inv_w, lmax);
+ sch_tree_lock(sch);
+ if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) {
+ /*
+ * shift cl->F back, to not charge the
+ * class for the not-yet-served head
+ * packet
+ */
+ cl->F = cl->S;
+ /* remove class from its slot in the old group */
+ qfq_deactivate_class(q, cl);
+ need_reactivation = true;
}
+
+ qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
+
+ if (need_reactivation) /* activate in new group */
+ qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc));
+ sch_tree_unlock(sch);
+
return 0;
}
@@ -273,11 +320,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
cl->refcnt = 1;
cl->common.classid = classid;
- cl->lmax = lmax;
- cl->inv_w = inv_w;
- i = qfq_calc_index(cl->inv_w, cl->lmax);
- cl->grp = &q->groups[i];
+ qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
cl->qdisc = qdisc_create_dflt(sch->dev_queue,
&pfifo_qdisc_ops, classid);
@@ -294,7 +338,6 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
return err;
}
}
- q->wsum += weight;
sch_tree_lock(sch);
qdisc_class_hash_insert(&q->clhash, &cl->common);
@@ -711,15 +754,6 @@ static void qfq_update_eligible(struct qfq_sched *q, u64 old_V)
}
}
-/* What is length of next packet in queue (0 if queue is empty) */
-static unsigned int qdisc_peek_len(struct Qdisc *sch)
-{
- struct sk_buff *skb;
-
- skb = sch->ops->peek(sch);
- return skb ? qdisc_pkt_len(skb) : 0;
-}
-
/*
* Updates the class, returns true if also the group needs to be updated.
*/
@@ -831,7 +865,10 @@ static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
if (mask) {
struct qfq_group *next = qfq_ffs(q, mask);
if (qfq_gt(roundedF, next->F)) {
- cl->S = next->F;
+ if (qfq_gt(limit, next->F))
+ cl->S = next->F;
+ else /* preserve timestamp correctness */
+ cl->S = limit;
return;
}
}
@@ -843,11 +880,8 @@ static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
- struct qfq_group *grp;
struct qfq_class *cl;
int err;
- u64 roundedS;
- int s;
cl = qfq_classify(skb, sch, &err);
if (cl == NULL) {
@@ -876,11 +910,25 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return err;
/* If reach this point, queue q was idle */
- grp = cl->grp;
+ qfq_activate_class(q, cl, qdisc_pkt_len(skb));
+
+ return err;
+}
+
+/*
+ * Handle class switch from idle to backlogged.
+ */
+static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
+ unsigned int pkt_len)
+{
+ struct qfq_group *grp = cl->grp;
+ u64 roundedS;
+ int s;
+
qfq_update_start(q, cl);
/* compute new finish time and rounded start. */
- cl->F = cl->S + (u64)qdisc_pkt_len(skb) * cl->inv_w;
+ cl->F = cl->S + (u64)pkt_len * cl->inv_w;
roundedS = qfq_round_down(cl->S, grp->slot_shift);
/*
@@ -917,8 +965,6 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
skip_update:
qfq_slot_insert(grp, cl, roundedS);
-
- return err;
}
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 838e18b4d7ea..be50aa234dcd 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -364,6 +364,25 @@ finish:
return retval;
}
+static void sctp_packet_release_owner(struct sk_buff *skb)
+{
+ sk_free(skb->sk);
+}
+
+static void sctp_packet_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+ skb_orphan(skb);
+ skb->sk = sk;
+ skb->destructor = sctp_packet_release_owner;
+
+ /*
+ * The data chunks have already been accounted for in sctp_sendmsg(),
+ * therefore only reserve a single byte to keep socket around until
+ * the packet has been transmitted.
+ */
+ atomic_inc(&sk->sk_wmem_alloc);
+}
+
/* All packets are sent to the network through this function from
* sctp_outq_tail().
*
@@ -405,7 +424,7 @@ int sctp_packet_transmit(struct sctp_packet *packet)
/* Set the owning socket so that we know where to get the
* destination IP address.
*/
- skb_set_owner_w(nskb, sk);
+ sctp_packet_set_owner_w(nskb, sk);
if (!sctp_transport_dst_check(tp)) {
sctp_transport_route(tp, NULL, sctp_sk(sk));
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 33d894776192..10c018a5b9fe 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -702,7 +702,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
if (rx_count >= asoc->base.sk->sk_rcvbuf) {
if ((asoc->base.sk->sk_userlocks & SOCK_RCVBUF_LOCK) ||
- (!sk_rmem_schedule(asoc->base.sk, chunk->skb->truesize)))
+ (!sk_rmem_schedule(asoc->base.sk, chunk->skb,
+ chunk->skb->truesize)))
goto fail;
}
diff --git a/net/socket.c b/net/socket.c
index dfe5b66c97e0..edc3c4af9085 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2604,7 +2604,7 @@ static int do_siocgstamp(struct net *net, struct socket *sock,
err = sock_do_ioctl(net, sock, cmd, (unsigned long)&ktv);
set_fs(old_fs);
if (!err)
- err = compat_put_timeval(up, &ktv);
+ err = compat_put_timeval(&ktv, up);
return err;
}
@@ -2620,7 +2620,7 @@ static int do_siocgstampns(struct net *net, struct socket *sock,
err = sock_do_ioctl(net, sock, cmd, (unsigned long)&kts);
set_fs(old_fs);
if (!err)
- err = compat_put_timespec(up, &kts);
+ err = compat_put_timespec(&kts, up);
return err;
}
@@ -2657,6 +2657,7 @@ static int dev_ifconf(struct net *net, struct compat_ifconf __user *uifc32)
if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
return -EFAULT;
+ memset(&ifc, 0, sizeof(ifc));
if (ifc32.ifcbuf == 0) {
ifc32.ifc_len = 0;
ifc.ifc_len = 0;
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index 9fe8857d8d59..03d03e37a7d5 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -21,6 +21,11 @@ config SUNRPC_XPRT_RDMA
If unsure, say N.
+config SUNRPC_SWAP
+ bool
+ depends on SUNRPC
+ select NETVM
+
config RPCSEC_GSS_KRB5
tristate "Secure RPC: Kerberos V mechanism"
depends on SUNRPC && CRYPTO
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index b05df36692ff..fa48c60aef23 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -717,6 +717,15 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
atomic_inc(&clnt->cl_count);
if (clnt->cl_softrtry)
task->tk_flags |= RPC_TASK_SOFT;
+ if (sk_memalloc_socks()) {
+ struct rpc_xprt *xprt;
+
+ rcu_read_lock();
+ xprt = rcu_dereference(clnt->cl_xprt);
+ if (xprt->swapper)
+ task->tk_flags |= RPC_TASK_SWAPPER;
+ rcu_read_unlock();
+ }
/* Add to the client's list of all tasks */
spin_lock(&clnt->cl_lock);
list_add_tail(&task->tk_task, &clnt->cl_tasks);
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 1f19aa15f89b..128494ec9a64 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -815,7 +815,10 @@ static void rpc_async_schedule(struct work_struct *work)
void *rpc_malloc(struct rpc_task *task, size_t size)
{
struct rpc_buffer *buf;
- gfp_t gfp = RPC_IS_SWAPPER(task) ? GFP_ATOMIC : GFP_NOWAIT;
+ gfp_t gfp = GFP_NOWAIT;
+
+ if (RPC_IS_SWAPPER(task))
+ gfp |= __GFP_MEMALLOC;
size += sizeof(struct rpc_buffer);
if (size <= RPC_BUFFER_MAXSIZE)
@@ -889,7 +892,7 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
static struct rpc_task *
rpc_alloc_task(void)
{
- return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS);
+ return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOIO);
}
/*
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 88f2bf671960..bac973a31367 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -316,7 +316,6 @@ static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt)
*/
void svc_xprt_enqueue(struct svc_xprt *xprt)
{
- struct svc_serv *serv = xprt->xpt_server;
struct svc_pool *pool;
struct svc_rqst *rqstp;
int cpu;
@@ -362,8 +361,6 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
rqstp, rqstp->rq_xprt);
rqstp->rq_xprt = xprt;
svc_xprt_get(xprt);
- rqstp->rq_reserved = serv->sv_max_mesg;
- atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
pool->sp_stats.threads_woken++;
wake_up(&rqstp->rq_wait);
} else {
@@ -640,8 +637,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
if (xprt) {
rqstp->rq_xprt = xprt;
svc_xprt_get(xprt);
- rqstp->rq_reserved = serv->sv_max_mesg;
- atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
/* As there is a shortage of threads and this request
* had to be queued, don't allow the thread to wait so
@@ -738,6 +733,8 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
else
len = xprt->xpt_ops->xpo_recvfrom(rqstp);
dprintk("svc: got len=%d\n", len);
+ rqstp->rq_reserved = serv->sv_max_mesg;
+ atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
}
svc_xprt_received(xprt);
@@ -794,7 +791,8 @@ int svc_send(struct svc_rqst *rqstp)
/* Grab mutex to serialize outgoing data. */
mutex_lock(&xprt->xpt_mutex);
- if (test_bit(XPT_DEAD, &xprt->xpt_flags))
+ if (test_bit(XPT_DEAD, &xprt->xpt_flags)
+ || test_bit(XPT_CLOSE, &xprt->xpt_flags))
len = -ENOTCONN;
else
len = xprt->xpt_ops->xpo_sendto(rqstp);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 18bc130255a7..998aa8c1807c 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1129,9 +1129,9 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
if (len >= 0)
svsk->sk_tcplen += len;
if (len != want) {
+ svc_tcp_save_pages(svsk, rqstp);
if (len < 0 && len != -EAGAIN)
goto err_other;
- svc_tcp_save_pages(svsk, rqstp);
dprintk("svc: incomplete TCP record (%d of %d)\n",
svsk->sk_tcplen, svsk->sk_reclen);
goto err_noclose;
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index a5a402a7d21f..5d7f61d7559c 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -969,11 +969,11 @@ static bool xprt_dynamic_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
return false;
}
-static void xprt_alloc_slot(struct rpc_task *task)
+void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
{
- struct rpc_xprt *xprt = task->tk_xprt;
struct rpc_rqst *req;
+ spin_lock(&xprt->reserve_lock);
if (!list_empty(&xprt->free)) {
req = list_entry(xprt->free.next, struct rpc_rqst, rq_list);
list_del(&req->rq_list);
@@ -994,12 +994,29 @@ static void xprt_alloc_slot(struct rpc_task *task)
default:
task->tk_status = -EAGAIN;
}
+ spin_unlock(&xprt->reserve_lock);
return;
out_init_req:
task->tk_status = 0;
task->tk_rqstp = req;
xprt_request_init(task, xprt);
+ spin_unlock(&xprt->reserve_lock);
+}
+EXPORT_SYMBOL_GPL(xprt_alloc_slot);
+
+void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+ /* Note: grabbing the xprt_lock_write() ensures that we throttle
+ * new slot allocation if the transport is congested (i.e. when
+ * reconnecting a stream transport or when out of socket write
+ * buffer space).
+ */
+ if (xprt_lock_write(xprt, task)) {
+ xprt_alloc_slot(xprt, task);
+ xprt_release_write(xprt, task);
+ }
}
+EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot);
static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
{
@@ -1083,20 +1100,9 @@ void xprt_reserve(struct rpc_task *task)
if (task->tk_rqstp != NULL)
return;
- /* Note: grabbing the xprt_lock_write() here is not strictly needed,
- * but ensures that we throttle new slot allocation if the transport
- * is congested (e.g. if reconnecting or if we're out of socket
- * write buffer space).
- */
task->tk_timeout = 0;
task->tk_status = -EAGAIN;
- if (!xprt_lock_write(xprt, task))
- return;
-
- spin_lock(&xprt->reserve_lock);
- xprt_alloc_slot(task);
- spin_unlock(&xprt->reserve_lock);
- xprt_release_write(xprt, task);
+ xprt->ops->alloc_slot(xprt, task);
}
static inline __be32 xprt_alloc_xid(struct rpc_xprt *xprt)
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 06cdbff79e4a..5d9202dc7cb1 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -713,6 +713,7 @@ static void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
static struct rpc_xprt_ops xprt_rdma_procs = {
.reserve_xprt = xprt_rdma_reserve_xprt,
.release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */
+ .alloc_slot = xprt_alloc_slot,
.release_request = xprt_release_rqst_cong, /* ditto */
.set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */
.rpcbind = rpcb_getport_async, /* sunrpc/rpcb_clnt.c */
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 926679459e71..a35b8e52e551 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1930,6 +1930,45 @@ out:
current->flags &= ~PF_FSTRANS;
}
+#ifdef CONFIG_SUNRPC_SWAP
+static void xs_set_memalloc(struct rpc_xprt *xprt)
+{
+ struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
+ xprt);
+
+ if (xprt->swapper)
+ sk_set_memalloc(transport->inet);
+}
+
+/**
+ * xs_swapper - Tag this transport as being used for swap.
+ * @xprt: transport to tag
+ * @enable: enable/disable
+ *
+ */
+int xs_swapper(struct rpc_xprt *xprt, int enable)
+{
+ struct sock_xprt *transport = container_of(xprt, struct sock_xprt,
+ xprt);
+ int err = 0;
+
+ if (enable) {
+ xprt->swapper++;
+ xs_set_memalloc(xprt);
+ } else if (xprt->swapper) {
+ xprt->swapper--;
+ sk_clear_memalloc(transport->inet);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(xs_swapper);
+#else
+static void xs_set_memalloc(struct rpc_xprt *xprt)
+{
+}
+#endif
+
static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
{
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
@@ -1954,6 +1993,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
transport->sock = sock;
transport->inet = sk;
+ xs_set_memalloc(xprt);
+
write_unlock_bh(&sk->sk_callback_lock);
}
xs_udp_do_set_buffer_size(xprt);
@@ -2081,6 +2122,8 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
if (!xprt_bound(xprt))
goto out;
+ xs_set_memalloc(xprt);
+
/* Tell the socket layer to start connecting... */
xprt->stat.connect_count++;
xprt->stat.connect_start = jiffies;
@@ -2430,6 +2473,7 @@ static void bc_destroy(struct rpc_xprt *xprt)
static struct rpc_xprt_ops xs_local_ops = {
.reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt,
+ .alloc_slot = xprt_alloc_slot,
.rpcbind = xs_local_rpcbind,
.set_port = xs_local_set_port,
.connect = xs_connect,
@@ -2446,6 +2490,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
.set_buffer_size = xs_udp_set_buffer_size,
.reserve_xprt = xprt_reserve_xprt_cong,
.release_xprt = xprt_release_xprt_cong,
+ .alloc_slot = xprt_alloc_slot,
.rpcbind = rpcb_getport_async,
.set_port = xs_set_port,
.connect = xs_connect,
@@ -2463,6 +2508,7 @@ static struct rpc_xprt_ops xs_udp_ops = {
static struct rpc_xprt_ops xs_tcp_ops = {
.reserve_xprt = xprt_reserve_xprt,
.release_xprt = xs_tcp_release_xprt,
+ .alloc_slot = xprt_lock_and_alloc_slot,
.rpcbind = rpcb_getport_async,
.set_port = xs_set_port,
.connect = xs_connect,
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 79981d97bc9c..c5ee4ff61364 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -823,6 +823,34 @@ fail:
return NULL;
}
+static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
+{
+ struct dentry *dentry;
+ struct path path;
+ int err = 0;
+ /*
+ * Get the parent directory, calculate the hash for last
+ * component.
+ */
+ dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ return err;
+
+ /*
+ * All right, let's create it.
+ */
+ err = security_path_mknod(&path, dentry, mode, 0);
+ if (!err) {
+ err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+ if (!err) {
+ res->mnt = mntget(path.mnt);
+ res->dentry = dget(dentry);
+ }
+ }
+ done_path_create(&path, dentry);
+ return err;
+}
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
@@ -831,8 +859,6 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct unix_sock *u = unix_sk(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)uaddr;
char *sun_path = sunaddr->sun_path;
- struct dentry *dentry = NULL;
- struct path path;
int err;
unsigned int hash;
struct unix_address *addr;
@@ -869,43 +895,23 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
atomic_set(&addr->refcnt, 1);
if (sun_path[0]) {
- umode_t mode;
- err = 0;
- /*
- * Get the parent directory, calculate the hash for last
- * component.
- */
- dentry = kern_path_create(AT_FDCWD, sun_path, &path, 0);
- err = PTR_ERR(dentry);
- if (IS_ERR(dentry))
- goto out_mknod_parent;
-
- /*
- * All right, let's create it.
- */
- mode = S_IFSOCK |
+ struct path path;
+ umode_t mode = S_IFSOCK |
(SOCK_INODE(sock)->i_mode & ~current_umask());
- err = mnt_want_write(path.mnt);
- if (err)
- goto out_mknod_dput;
- err = security_path_mknod(&path, dentry, mode, 0);
- if (err)
- goto out_mknod_drop_write;
- err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
-out_mknod_drop_write:
- mnt_drop_write(path.mnt);
- if (err)
- goto out_mknod_dput;
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- dput(path.dentry);
- path.dentry = dentry;
-
+ err = unix_mknod(sun_path, mode, &path);
+ if (err) {
+ if (err == -EEXIST)
+ err = -EADDRINUSE;
+ unix_release_addr(addr);
+ goto out_up;
+ }
addr->hash = UNIX_HASH_SIZE;
- }
-
- spin_lock(&unix_table_lock);
-
- if (!sun_path[0]) {
+ hash = path.dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1);
+ spin_lock(&unix_table_lock);
+ u->path = path;
+ list = &unix_socket_table[hash];
+ } else {
+ spin_lock(&unix_table_lock);
err = -EADDRINUSE;
if (__unix_find_socket_byname(net, sunaddr, addr_len,
sk->sk_type, hash)) {
@@ -914,9 +920,6 @@ out_mknod_drop_write:
}
list = &unix_socket_table[addr->hash];
- } else {
- list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)];
- u->path = path;
}
err = 0;
@@ -930,16 +933,6 @@ out_up:
mutex_unlock(&u->readlock);
out:
return err;
-
-out_mknod_dput:
- dput(dentry);
- mutex_unlock(&path.dentry->d_inode->i_mutex);
- path_put(&path);
-out_mknod_parent:
- if (err == -EEXIST)
- err = -EADDRINUSE;
- unix_release_addr(addr);
- goto out_up;
}
static void unix_state_double_lock(struct sock *sk1, struct sock *sk2)
@@ -1457,7 +1450,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
@@ -1626,7 +1619,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 31b40cc4a9c3..dcd64d5b07aa 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -952,6 +952,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
*/
synchronize_rcu();
INIT_LIST_HEAD(&wdev->list);
+ /*
+ * Ensure that all events have been processed and
+ * freed.
+ */
+ cfg80211_process_wdev_events(wdev);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 5206c6844fd7..bc7430b54771 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -426,6 +426,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
struct net_device *dev, enum nl80211_iftype ntype,
u32 *flags, struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
+void cfg80211_process_wdev_events(struct wireless_dev *wdev);
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 97026f3b215a..1e37dbf00cb3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -5633,8 +5633,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
sizeof(connect.ht_capa_mask));
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
- if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
+ kfree(connkeys);
return -EINVAL;
+ }
memcpy(&connect.ht_capa,
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
sizeof(connect.ht_capa));
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2303ee73b50a..72d170ca3406 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work)
struct reg_regdb_search_request *request;
const struct ieee80211_regdomain *curdom, *regdom;
int i, r;
+ bool set_reg = false;
+
+ mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_regdb_search_mutex);
while (!list_empty(&reg_regdb_search_list)) {
@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work)
r = reg_copy_regd(&regdom, curdom);
if (r)
break;
- mutex_lock(&cfg80211_mutex);
- set_regdom(regdom);
- mutex_unlock(&cfg80211_mutex);
+ set_reg = true;
break;
}
}
@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work)
kfree(request);
}
mutex_unlock(&reg_regdb_search_mutex);
+
+ if (set_reg)
+ set_regdom(regdom);
+
+ mutex_unlock(&cfg80211_mutex);
}
static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
@@ -680,6 +686,8 @@ static u32 map_regdom_flags(u32 rd_flags)
channel_flags |= IEEE80211_CHAN_NO_IBSS;
if (rd_flags & NL80211_RRF_DFS)
channel_flags |= IEEE80211_CHAN_RADAR;
+ if (rd_flags & NL80211_RRF_NO_OFDM)
+ channel_flags |= IEEE80211_CHAN_NO_OFDM;
return channel_flags;
}
@@ -901,7 +909,21 @@ static void handle_channel(struct wiphy *wiphy,
chan->max_antenna_gain = min(chan->orig_mag,
(int) MBI_TO_DBI(power_rule->max_antenna_gain));
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
- chan->max_power = min(chan->max_power, chan->max_reg_power);
+ if (chan->orig_mpwr) {
+ /*
+ * Devices that have their own custom regulatory domain
+ * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
+ * passed country IE power settings.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
+ wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ chan->max_power = chan->max_reg_power;
+ else
+ chan->max_power = min(chan->orig_mpwr,
+ chan->max_reg_power);
+ } else
+ chan->max_power = chan->max_reg_power;
}
static void handle_band(struct wiphy *wiphy,
@@ -1885,6 +1907,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy)
chan->flags = chan->orig_flags;
chan->max_antenna_gain = chan->orig_mag;
chan->max_power = chan->orig_mpwr;
+ chan->beacon_found = false;
}
}
}
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 26f8cd30f712..994e2f0cc7a8 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -735,7 +735,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
wdev->connect_keys = NULL;
}
-static void cfg80211_process_wdev_events(struct wireless_dev *wdev)
+void cfg80211_process_wdev_events(struct wireless_dev *wdev)
{
struct cfg80211_event *ev;
unsigned long flags;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 54a0dc2e2f8d..ab2bb42fe094 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -212,7 +212,7 @@ resume:
/* only the first xfrm gets the encap type */
encap_type = 0;
- if (async && x->repl->check(x, skb, seq)) {
+ if (async && x->repl->recheck(x, skb, seq)) {
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
goto drop_unlock;
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index c5a5165a5927..387848e90078 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -585,6 +585,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
xfrm_pol_hold(policy);
net->xfrm.policy_count[dir]++;
atomic_inc(&flow_cache_genid);
+ rt_genid_bump(net);
if (delpol)
__xfrm_policy_unlink(delpol, dir);
policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
@@ -1357,6 +1358,8 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family)
memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
xdst->flo.ops = &xfrm_bundle_fc_ops;
+ if (afinfo->init_dst)
+ afinfo->init_dst(net, xdst);
} else
xdst = ERR_PTR(-ENOBUFS);
@@ -1761,7 +1764,7 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
if (!afinfo) {
dst_release(dst_orig);
- ret = ERR_PTR(-EINVAL);
+ return ERR_PTR(-EINVAL);
} else {
ret = afinfo->blackhole_route(net, dst_orig);
}
diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c
index 2f6d11d04a2b..3efb07d3eb27 100644
--- a/net/xfrm/xfrm_replay.c
+++ b/net/xfrm/xfrm_replay.c
@@ -420,6 +420,18 @@ err:
return -EINVAL;
}
+static int xfrm_replay_recheck_esn(struct xfrm_state *x,
+ struct sk_buff *skb, __be32 net_seq)
+{
+ if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi !=
+ htonl(xfrm_replay_seqhi(x, net_seq)))) {
+ x->stats.replay_window++;
+ return -EINVAL;
+ }
+
+ return xfrm_replay_check_esn(x, skb, net_seq);
+}
+
static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
{
unsigned int bitnr, nr, i;
@@ -479,6 +491,7 @@ static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
static struct xfrm_replay xfrm_replay_legacy = {
.advance = xfrm_replay_advance,
.check = xfrm_replay_check,
+ .recheck = xfrm_replay_check,
.notify = xfrm_replay_notify,
.overflow = xfrm_replay_overflow,
};
@@ -486,6 +499,7 @@ static struct xfrm_replay xfrm_replay_legacy = {
static struct xfrm_replay xfrm_replay_bmp = {
.advance = xfrm_replay_advance_bmp,
.check = xfrm_replay_check_bmp,
+ .recheck = xfrm_replay_check_bmp,
.notify = xfrm_replay_notify_bmp,
.overflow = xfrm_replay_overflow_bmp,
};
@@ -493,6 +507,7 @@ static struct xfrm_replay xfrm_replay_bmp = {
static struct xfrm_replay xfrm_replay_esn = {
.advance = xfrm_replay_advance_esn,
.check = xfrm_replay_check_esn,
+ .recheck = xfrm_replay_recheck_esn,
.notify = xfrm_replay_notify_bmp,
.overflow = xfrm_replay_overflow_esn,
};
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5b228f97d4b3..210be48d8ae3 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -415,8 +415,17 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
if (x->lft.hard_add_expires_seconds) {
long tmo = x->lft.hard_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
- goto expired;
+ if (tmo <= 0) {
+ if (x->xflags & XFRM_SOFT_EXPIRE) {
+ /* enter hard expire without soft expire first?!
+ * setting a new date could trigger this.
+ * workarbound: fix x->curflt.add_time by below:
+ */
+ x->curlft.add_time = now - x->saved_tmo - 1;
+ tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
+ } else
+ goto expired;
+ }
if (tmo < next)
next = tmo;
}
@@ -433,10 +442,14 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer * me)
if (x->lft.soft_add_expires_seconds) {
long tmo = x->lft.soft_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
+ if (tmo <= 0) {
warn = 1;
- else if (tmo < next)
+ x->xflags &= ~XFRM_SOFT_EXPIRE;
+ } else if (tmo < next) {
next = tmo;
+ x->xflags |= XFRM_SOFT_EXPIRE;
+ x->saved_tmo = tmo;
+ }
}
if (x->lft.soft_use_expires_seconds) {
long tmo = x->lft.soft_use_expires_seconds +
@@ -1981,8 +1994,10 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay)
goto error;
x->outer_mode = xfrm_get_mode(x->props.mode, family);
- if (x->outer_mode == NULL)
+ if (x->outer_mode == NULL) {
+ err = -EPROTONOSUPPORT;
goto error;
+ }
if (init_replay) {
err = xfrm_init_replay(x);
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index e75d8e47f35c..289f4bf18ff0 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -123,9 +123,21 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+ struct xfrm_replay_state_esn *rs;
- if ((p->flags & XFRM_STATE_ESN) && !rt)
- return -EINVAL;
+ if (p->flags & XFRM_STATE_ESN) {
+ if (!rt)
+ return -EINVAL;
+
+ rs = nla_data(rt);
+
+ if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
+ return -EINVAL;
+
+ if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
+ nla_len(rt) != sizeof(*rs))
+ return -EINVAL;
+ }
if (!rt)
return 0;
@@ -370,14 +382,15 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
struct nlattr *rp)
{
struct xfrm_replay_state_esn *up;
+ int ulen;
if (!replay_esn || !rp)
return 0;
up = nla_data(rp);
+ ulen = xfrm_replay_state_esn_len(up);
- if (xfrm_replay_state_esn_len(replay_esn) !=
- xfrm_replay_state_esn_len(up))
+ if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
return -EINVAL;
return 0;
@@ -388,22 +401,28 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
struct nlattr *rta)
{
struct xfrm_replay_state_esn *p, *pp, *up;
+ int klen, ulen;
if (!rta)
return 0;
up = nla_data(rta);
+ klen = xfrm_replay_state_esn_len(up);
+ ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
- p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+ p = kzalloc(klen, GFP_KERNEL);
if (!p)
return -ENOMEM;
- pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+ pp = kzalloc(klen, GFP_KERNEL);
if (!pp) {
kfree(p);
return -ENOMEM;
}
+ memcpy(p, up, ulen);
+ memcpy(pp, up, ulen);
+
*replay_esn = p;
*preplay_esn = pp;
@@ -442,10 +461,11 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
* somehow made shareable and move it to xfrm_state.c - JHS
*
*/
-static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
+static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
+ int update_esn)
{
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
- struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
+ struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
@@ -555,7 +575,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
goto error;
/* override default values from above */
- xfrm_update_ae_params(x, attrs);
+ xfrm_update_ae_params(x, attrs, 0);
return x;
@@ -689,6 +709,7 @@ out:
static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
{
+ memset(p, 0, sizeof(*p));
memcpy(&p->id, &x->id, sizeof(p->id));
memcpy(&p->sel, &x->sel, sizeof(p->sel));
memcpy(&p->lft, &x->lft, sizeof(p->lft));
@@ -742,7 +763,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
return -EMSGSIZE;
algo = nla_data(nla);
- strcpy(algo->alg_name, auth->alg_name);
+ strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8);
algo->alg_key_len = auth->alg_key_len;
@@ -878,6 +899,7 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
{
struct xfrm_dump_info info;
struct sk_buff *skb;
+ int err;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!skb)
@@ -888,9 +910,10 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
info.nlmsg_seq = seq;
info.nlmsg_flags = 0;
- if (dump_one_state(x, 0, &info)) {
+ err = dump_one_state(x, 0, &info);
+ if (err) {
kfree_skb(skb);
- return NULL;
+ return ERR_PTR(err);
}
return skb;
@@ -1317,6 +1340,7 @@ static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy
static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
{
+ memset(p, 0, sizeof(*p));
memcpy(&p->sel, &xp->selector, sizeof(p->sel));
memcpy(&p->lft, &xp->lft, sizeof(p->lft));
memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
@@ -1421,6 +1445,7 @@ static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
struct xfrm_user_tmpl *up = &vec[i];
struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
+ memset(up, 0, sizeof(*up));
memcpy(&up->id, &kp->id, sizeof(up->id));
up->family = kp->encap_family;
memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr));
@@ -1546,6 +1571,7 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
{
struct xfrm_dump_info info;
struct sk_buff *skb;
+ int err;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
@@ -1556,9 +1582,10 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
info.nlmsg_seq = seq;
info.nlmsg_flags = 0;
- if (dump_one_policy(xp, dir, 0, &info) < 0) {
+ err = dump_one_policy(xp, dir, 0, &info);
+ if (err) {
kfree_skb(skb);
- return NULL;
+ return ERR_PTR(err);
}
return skb;
@@ -1822,7 +1849,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
spin_lock_bh(&x->lock);
- xfrm_update_ae_params(x, attrs);
+ xfrm_update_ae_params(x, attrs, 1);
spin_unlock_bh(&x->lock);
c.event = nlh->nlmsg_type;
diff --git a/scripts/Makefile.fwinst b/scripts/Makefile.fwinst
index 6bf8e87f1dcf..4d908d16c035 100644
--- a/scripts/Makefile.fwinst
+++ b/scripts/Makefile.fwinst
@@ -27,7 +27,7 @@ endif
installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw))
installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all))
-installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/.
+installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./
# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 913d6bdfdda3..ca05ba217f5f 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3016,7 +3016,8 @@ sub process {
$herectx .= raw_line($linenr, $n) . "\n";
}
- if (($stmts =~ tr/;/;/) == 1) {
+ if (($stmts =~ tr/;/;/) == 1 &&
+ $stmts !~ /^\s*(if|while|for|switch)\b/) {
WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
"Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
}
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index d24810fc6af6..fd8fa9aa7c4e 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -200,7 +200,7 @@ EOF
syscall_list() {
grep '^[0-9]' "$1" | sort -n | (
while read nr abi name entry ; do
- echo <<EOF
+ cat <<EOF
#if !defined(__NR_${name}) && !defined(__IGNORE_${name})
#warning syscall ${name} not implemented
#endif
diff --git a/scripts/decodecode b/scripts/decodecode
index 18ba881c3415..4f8248d5a11f 100755
--- a/scripts/decodecode
+++ b/scripts/decodecode
@@ -89,7 +89,7 @@ echo $code >> $T.s
disas $T
cat $T.dis >> $T.aa
-faultline=`cat $T.dis | head -1 | cut -d":" -f2`
+faultline=`cat $T.dis | head -1 | cut -d":" -f2-`
faultline=`echo "$faultline" | sed -e 's/\[/\\\[/g; s/\]/\\\]/g'`
cat $T.oo | sed -e "s/\($faultline\)/\*\1 <-- trapping instruction/g"
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 9b0c0b8b4ab4..8fd107a3fac4 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -1786,6 +1786,7 @@ sub dump_function($$) {
$prototype =~ s/__init +//;
$prototype =~ s/__init_or_module +//;
$prototype =~ s/__must_check +//;
+ $prototype =~ s/__weak +//;
$prototype =~ s/^#\s*define\s+//; #ak added
$prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//;
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index 4629038c9e5a..b3d907eb93a9 100644
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -74,8 +74,13 @@ kallsyms()
info KSYM ${2}
local kallsymopt;
+ if [ -n "${CONFIG_SYMBOL_PREFIX}" ]; then
+ kallsymopt="${kallsymopt} \
+ --symbol-prefix=${CONFIG_SYMBOL_PREFIX}"
+ fi
+
if [ -n "${CONFIG_KALLSYMS_ALL}" ]; then
- kallsymopt=--all-symbols
+ kallsymopt="${kallsymopt} --all-symbols"
fi
local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \
@@ -211,7 +216,7 @@ if [ -n "${CONFIG_KALLSYMS}" ]; then
if ! cmp -s System.map .tmp_System.map; then
echo >&2 Inconsistent kallsyms data
- echo >&2 echo Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
+ echo >&2 Try "make KALLSYMS_EXTRA_PASS=1" as a workaround
cleanup
exit 1
fi
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 68d82daed257..4d3fab47e643 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -274,7 +274,7 @@ static struct avc_node *avc_alloc_node(void)
{
struct avc_node *node;
- node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC);
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC);
if (!node)
goto out;
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index c220f314709c..65f67cb0aefb 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -51,6 +51,7 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
static inline void selinux_xfrm_notify_policyload(void)
{
atomic_inc(&flow_cache_genid);
+ rt_genid_bump(&init_net);
}
#else
static inline int selinux_xfrm_enabled(void)
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 83554ee8a587..0cc99a3ea42d 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -279,12 +279,46 @@ static int yama_ptrace_access_check(struct task_struct *child,
}
if (rc) {
- char name[sizeof(current->comm)];
printk_ratelimited(KERN_NOTICE
"ptrace of pid %d was attempted by: %s (pid %d)\n",
- child->pid,
- get_task_comm(name, current),
- current->pid);
+ child->pid, current->comm, current->pid);
+ }
+
+ return rc;
+}
+
+/**
+ * yama_ptrace_traceme - validate PTRACE_TRACEME calls
+ * @parent: task that will become the ptracer of the current task
+ *
+ * Returns 0 if following the ptrace is allowed, -ve on error.
+ */
+static int yama_ptrace_traceme(struct task_struct *parent)
+{
+ int rc;
+
+ /* If standard caps disallows it, so does Yama. We should
+ * only tighten restrictions further.
+ */
+ rc = cap_ptrace_traceme(parent);
+ if (rc)
+ return rc;
+
+ /* Only disallow PTRACE_TRACEME on more aggressive settings. */
+ switch (ptrace_scope) {
+ case YAMA_SCOPE_CAPABILITY:
+ if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE))
+ rc = -EPERM;
+ break;
+ case YAMA_SCOPE_NO_ATTACH:
+ rc = -EPERM;
+ break;
+ }
+
+ if (rc) {
+ printk_ratelimited(KERN_NOTICE
+ "ptraceme of pid %d was attempted by: %s (pid %d)\n",
+ current->pid, parent->comm, parent->pid);
}
return rc;
@@ -294,6 +328,7 @@ static struct security_operations yama_ops = {
.name = "yama",
.ptrace_access_check = yama_ptrace_access_check,
+ .ptrace_traceme = yama_ptrace_traceme,
.task_prctl = yama_task_prctl,
.task_free = yama_task_free,
};
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 0d7b25e81643..4e1fda75c1c9 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -106,7 +106,7 @@ static struct pxa2xx_pcm_client pxa2xx_ac97_pcm_client = {
.prepare = pxa2xx_ac97_pcm_prepare,
};
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int pxa2xx_ac97_do_suspend(struct snd_card *card)
{
@@ -243,7 +243,7 @@ static struct platform_driver pxa2xx_ac97_driver = {
.driver = {
.name = "pxa2xx-ac97",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.pm = &pxa2xx_ac97_pm_ops,
#endif
},
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
index eb4ceb71123e..277ebce23a45 100644
--- a/sound/atmel/abdac.c
+++ b/sound/atmel/abdac.c
@@ -452,6 +452,7 @@ static int __devinit atmel_abdac_probe(struct platform_device *pdev)
dac->regs = ioremap(regs->start, resource_size(regs));
if (!dac->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto out_free_card;
}
@@ -534,7 +535,7 @@ out_put_pclk:
return retval;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atmel_abdac_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index bf47025bdf45..9052aff37f64 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -278,14 +278,9 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
if (retval < 0)
return retval;
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
+ if (cpu_is_at32ap7000() && retval == 1)
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
@@ -980,6 +975,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
if (!chip->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto err_ioremap;
}
@@ -1134,7 +1130,7 @@ err_snd_card_new:
return retval;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atmel_ac97c_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index ec2118d0e27a..eb60cb8dbb8a 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -80,14 +80,12 @@ static int snd_compr_open(struct inode *inode, struct file *f)
int maj = imajor(inode);
int ret;
- if (f->f_flags & O_WRONLY)
+ if ((f->f_flags & O_ACCMODE) == O_WRONLY)
dirn = SND_COMPRESS_PLAYBACK;
- else if (f->f_flags & O_RDONLY)
+ else if ((f->f_flags & O_ACCMODE) == O_RDONLY)
dirn = SND_COMPRESS_CAPTURE;
- else {
- pr_err("invalid direction\n");
+ else
return -EINVAL;
- }
if (maj == snd_major)
compr = snd_lookup_minor_data(iminor(inode),
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 4e7ec2b49873..d0f00356fc11 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -101,7 +101,7 @@ void *snd_malloc_sgbuf_pages(struct device *device,
if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
chunk, &tmpb) < 0) {
if (!sgbuf->pages)
- return NULL;
+ goto _failed;
if (!res_size)
goto _failed;
size = sgbuf->pages * PAGE_SIZE;
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 1128b35b2b05..5a34355e78e8 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1176,7 +1176,7 @@ static int __devexit loopback_remove(struct platform_device *devptr)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int loopback_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index f7d3bfc6bca8..54bb6644a598 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1064,7 +1064,7 @@ static int __devexit snd_dummy_remove(struct platform_device *devptr)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_dummy_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
index 1cff331a228e..4608c2ca43f8 100644
--- a/sound/drivers/mpu401/mpu401_uart.c
+++ b/sound/drivers/mpu401/mpu401_uart.c
@@ -554,6 +554,7 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
spin_lock_init(&mpu->output_lock);
spin_lock_init(&mpu->timer_lock);
mpu->hardware = hardware;
+ mpu->irq = -1;
if (! (info_flags & MPU401_INFO_INTEGRATED)) {
int res_size = hardware == MPU401_HW_PC98II ? 4 : 2;
mpu->res = request_region(port, res_size, "MPU401 UART");
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index 6ca59fc6dcb9..ef171295f6d4 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -199,7 +199,7 @@ static void pcsp_stop_beep(struct snd_pcsp *chip)
pcspkr_stop_sound();
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int pcsp_suspend(struct device *dev)
{
struct snd_pcsp *chip = dev_get_drvdata(dev);
@@ -212,7 +212,7 @@ static SIMPLE_DEV_PM_OPS(pcsp_pm, pcsp_suspend, NULL);
#define PCSP_PM_OPS &pcsp_pm
#else
#define PCSP_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static void pcsp_shutdown(struct platform_device *dev)
{
diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
index d14edb7d6484..3c6c1e3226f3 100644
--- a/sound/i2c/other/tea575x-tuner.c
+++ b/sound/i2c/other/tea575x-tuner.c
@@ -37,9 +37,6 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
MODULE_LICENSE("GPL");
-#define FREQ_LO ((tea->tea5759 ? 760 : 875) * 1600U)
-#define FREQ_HI ((tea->tea5759 ? 910 : 1080) * 1600U)
-
/*
* definitions
*/
@@ -50,8 +47,8 @@ MODULE_LICENSE("GPL");
#define TEA575X_BIT_BAND_MASK (3<<20)
#define TEA575X_BIT_BAND_FM (0<<20)
#define TEA575X_BIT_BAND_MW (1<<20)
-#define TEA575X_BIT_BAND_LW (1<<21)
-#define TEA575X_BIT_BAND_SW (1<<22)
+#define TEA575X_BIT_BAND_LW (2<<20)
+#define TEA575X_BIT_BAND_SW (3<<20)
#define TEA575X_BIT_PORT_0 (1<<19) /* user bit */
#define TEA575X_BIT_PORT_1 (1<<18) /* user bit */
#define TEA575X_BIT_SEARCH_MASK (3<<16) /* search level */
@@ -62,6 +59,37 @@ MODULE_LICENSE("GPL");
#define TEA575X_BIT_DUMMY (1<<15) /* buffer */
#define TEA575X_BIT_FREQ_MASK 0x7fff
+enum { BAND_FM, BAND_FM_JAPAN, BAND_AM };
+
+static const struct v4l2_frequency_band bands[] = {
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 87500 * 16,
+ .rangehigh = 108000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 0,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 76000 * 16,
+ .rangehigh = 91000 * 16,
+ .modulation = V4L2_BAND_MODULATION_FM,
+ },
+ {
+ .type = V4L2_TUNER_RADIO,
+ .index = 1,
+ .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
+ .rangelow = 530 * 16,
+ .rangehigh = 1710 * 16,
+ .modulation = V4L2_BAND_MODULATION_AM,
+ },
+};
+
/*
* lowlevel part
*/
@@ -133,16 +161,29 @@ static u32 snd_tea575x_val_to_freq(struct snd_tea575x *tea, u32 val)
if (freq == 0)
return freq;
- /* freq *= 12.5 */
- freq *= 125;
- freq /= 10;
- /* crystal fixup */
- if (tea->tea5759)
- freq += TEA575X_FMIF;
- else
+ switch (tea->band) {
+ case BAND_FM:
+ /* freq *= 12.5 */
+ freq *= 125;
+ freq /= 10;
+ /* crystal fixup */
freq -= TEA575X_FMIF;
+ break;
+ case BAND_FM_JAPAN:
+ /* freq *= 12.5 */
+ freq *= 125;
+ freq /= 10;
+ /* crystal fixup */
+ freq += TEA575X_FMIF;
+ break;
+ case BAND_AM:
+ /* crystal fixup */
+ freq -= TEA575X_AMIF;
+ break;
+ }
- return clamp(freq * 16, FREQ_LO, FREQ_HI); /* from kHz */
+ return clamp(freq * 16, bands[tea->band].rangelow,
+ bands[tea->band].rangehigh); /* from kHz */
}
static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
@@ -150,21 +191,37 @@ static u32 snd_tea575x_get_freq(struct snd_tea575x *tea)
return snd_tea575x_val_to_freq(tea, snd_tea575x_read(tea));
}
-static void snd_tea575x_set_freq(struct snd_tea575x *tea)
+void snd_tea575x_set_freq(struct snd_tea575x *tea)
{
- u32 freq = tea->freq;
+ u32 freq = tea->freq / 16; /* to kHz */
+ u32 band = 0;
- freq /= 16; /* to kHz */
- /* crystal fixup */
- if (tea->tea5759)
- freq -= TEA575X_FMIF;
- else
+ switch (tea->band) {
+ case BAND_FM:
+ band = TEA575X_BIT_BAND_FM;
+ /* crystal fixup */
freq += TEA575X_FMIF;
- /* freq /= 12.5 */
- freq *= 10;
- freq /= 125;
+ /* freq /= 12.5 */
+ freq *= 10;
+ freq /= 125;
+ break;
+ case BAND_FM_JAPAN:
+ band = TEA575X_BIT_BAND_FM;
+ /* crystal fixup */
+ freq -= TEA575X_FMIF;
+ /* freq /= 12.5 */
+ freq *= 10;
+ freq /= 125;
+ break;
+ case BAND_AM:
+ band = TEA575X_BIT_BAND_MW;
+ /* crystal fixup */
+ freq += TEA575X_AMIF;
+ break;
+ }
- tea->val &= ~TEA575X_BIT_FREQ_MASK;
+ tea->val &= ~(TEA575X_BIT_FREQ_MASK | TEA575X_BIT_BAND_MASK);
+ tea->val |= band;
tea->val |= freq & TEA575X_BIT_FREQ_MASK;
snd_tea575x_write(tea, tea->val);
tea->freq = snd_tea575x_val_to_freq(tea, tea->val);
@@ -190,23 +247,57 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
+static int vidioc_enum_freq_bands(struct file *file, void *priv,
+ struct v4l2_frequency_band *band)
+{
+ struct snd_tea575x *tea = video_drvdata(file);
+ int index;
+
+ if (band->tuner != 0)
+ return -EINVAL;
+
+ switch (band->index) {
+ case 0:
+ if (tea->tea5759)
+ index = BAND_FM_JAPAN;
+ else
+ index = BAND_FM;
+ break;
+ case 1:
+ if (tea->has_am) {
+ index = BAND_AM;
+ break;
+ }
+ /* Fall through */
+ default:
+ return -EINVAL;
+ }
+
+ *band = bands[index];
+ if (!tea->cannot_read_data)
+ band->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
+
+ return 0;
+}
+
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct snd_tea575x *tea = video_drvdata(file);
+ struct v4l2_frequency_band band_fm = { 0, };
if (v->index > 0)
return -EINVAL;
snd_tea575x_read(tea);
+ vidioc_enum_freq_bands(file, priv, &band_fm);
- strcpy(v->name, "FM");
+ memset(v, 0, sizeof(*v));
+ strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name));
v->type = V4L2_TUNER_RADIO;
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- if (!tea->cannot_read_data)
- v->capability |= V4L2_TUNER_CAP_HWSEEK_BOUNDED;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
+ v->capability = band_fm.capability;
+ v->rangelow = tea->has_am ? bands[BAND_AM].rangelow : band_fm.rangelow;
+ v->rangehigh = band_fm.rangehigh;
v->rxsubchans = tea->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
v->audmode = (tea->val & TEA575X_BIT_MONO) ?
V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
@@ -218,13 +309,17 @@ static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
struct snd_tea575x *tea = video_drvdata(file);
+ u32 orig_val = tea->val;
if (v->index)
return -EINVAL;
tea->val &= ~TEA575X_BIT_MONO;
if (v->audmode == V4L2_TUNER_MODE_MONO)
tea->val |= TEA575X_BIT_MONO;
- snd_tea575x_write(tea, tea->val);
+ /* Only apply changes if currently tuning FM */
+ if (tea->band != BAND_AM && tea->val != orig_val)
+ snd_tea575x_set_freq(tea);
+
return 0;
}
@@ -248,24 +343,56 @@ static int vidioc_s_frequency(struct file *file, void *priv,
if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
- tea->val &= ~TEA575X_BIT_SEARCH;
- tea->freq = clamp(f->frequency, FREQ_LO, FREQ_HI);
+ if (tea->has_am && f->frequency < (20000 * 16))
+ tea->band = BAND_AM;
+ else if (tea->tea5759)
+ tea->band = BAND_FM_JAPAN;
+ else
+ tea->band = BAND_FM;
+
+ tea->freq = clamp(f->frequency, bands[tea->band].rangelow,
+ bands[tea->band].rangehigh);
snd_tea575x_set_freq(tea);
return 0;
}
static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
- struct v4l2_hw_freq_seek *a)
+ const struct v4l2_hw_freq_seek *a)
{
struct snd_tea575x *tea = video_drvdata(file);
unsigned long timeout;
- int i;
+ int i, spacing;
if (tea->cannot_read_data)
return -ENOTTY;
if (a->tuner || a->wrap_around)
return -EINVAL;
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+
+ if (a->rangelow || a->rangehigh) {
+ for (i = 0; i < ARRAY_SIZE(bands); i++) {
+ if ((i == BAND_FM && tea->tea5759) ||
+ (i == BAND_FM_JAPAN && !tea->tea5759) ||
+ (i == BAND_AM && !tea->has_am))
+ continue;
+ if (bands[i].rangelow == a->rangelow &&
+ bands[i].rangehigh == a->rangehigh)
+ break;
+ }
+ if (i == ARRAY_SIZE(bands))
+ return -EINVAL; /* No matching band found */
+ if (i != tea->band) {
+ tea->band = i;
+ tea->freq = clamp(tea->freq, bands[i].rangelow,
+ bands[i].rangehigh);
+ snd_tea575x_set_freq(tea);
+ }
+ }
+
+ spacing = (tea->band == BAND_AM) ? 5 : 50; /* kHz */
+
/* clear the frequency, HW will fill it in */
tea->val &= ~TEA575X_BIT_FREQ_MASK;
tea->val |= TEA575X_BIT_SEARCH;
@@ -297,10 +424,10 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh,
if (freq == 0) /* shouldn't happen */
break;
/*
- * if we moved by less than 50 kHz, or in the wrong
- * direction, continue seeking
+ * if we moved by less than the spacing, or in the
+ * wrong direction, continue seeking
*/
- if (abs(tea->freq - freq) < 16 * 50 ||
+ if (abs(tea->freq - freq) < 16 * spacing ||
(a->seek_upward && freq < tea->freq) ||
(!a->seek_upward && freq > tea->freq)) {
snd_tea575x_write(tea, tea->val);
@@ -344,6 +471,7 @@ static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+ .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -446,3 +574,4 @@ module_exit(alsa_tea575x_module_exit)
EXPORT_SYMBOL(snd_tea575x_init);
EXPORT_SYMBOL(snd_tea575x_exit);
+EXPORT_SYMBOL(snd_tea575x_set_freq);
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index 2d67c78c9f4b..f7cdaf51512d 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -233,7 +233,7 @@ static int __devinit snd_card_als100_probe(int dev,
irq[dev], dma8[dev], dma16[dev]);
}
- if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+ if ((error = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0) {
snd_card_free(card);
return error;
}
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index 1d47be8170b5..b3b4f15e45ba 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -612,10 +612,10 @@ static int snd_es1688_capture_close(struct snd_pcm_substream *substream)
static int snd_es1688_free(struct snd_es1688 *chip)
{
- if (chip->res_port) {
+ if (chip->hardware != ES1688_HW_UNDEF)
snd_es1688_init(chip, 0);
+ if (chip->res_port)
release_and_free_resource(chip->res_port);
- }
if (chip->irq >= 0)
free_irq(chip->irq, (void *) chip);
if (chip->dma8 >= 0) {
@@ -657,19 +657,27 @@ int snd_es1688_create(struct snd_card *card,
return -ENOMEM;
chip->irq = -1;
chip->dma8 = -1;
+ chip->hardware = ES1688_HW_UNDEF;
- if ((chip->res_port = request_region(port + 4, 12, "ES1688")) == NULL) {
+ chip->res_port = request_region(port + 4, 12, "ES1688");
+ if (chip->res_port == NULL) {
snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
- return -EBUSY;
+ err = -EBUSY;
+ goto exit;
}
- if (request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip)) {
+
+ err = request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip);
+ if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
- return -EBUSY;
+ goto exit;
}
+
chip->irq = irq;
- if (request_dma(dma8, "ES1688")) {
+ err = request_dma(dma8, "ES1688");
+
+ if (err < 0) {
snd_printk(KERN_ERR "es1688: can't grab DMA8 %d\n", dma8);
- return -EBUSY;
+ goto exit;
}
chip->dma8 = dma8;
@@ -685,14 +693,18 @@ int snd_es1688_create(struct snd_card *card,
err = snd_es1688_probe(chip);
if (err < 0)
- return err;
+ goto exit;
err = snd_es1688_init(chip, 1);
if (err < 0)
- return err;
+ goto exit;
/* Register device */
- return snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+exit:
+ if (err)
+ snd_es1688_free(chip);
+ return err;
}
static struct snd_pcm_ops snd_es1688_playback_ops = {
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
index 733b014ec7d1..b2b3c014221a 100644
--- a/sound/oss/sb_audio.c
+++ b/sound/oss/sb_audio.c
@@ -575,13 +575,15 @@ static int jazz16_audio_set_speed(int dev, int speed)
if (speed > 0)
{
int tmp;
- int s = speed * devc->channels;
+ int s;
if (speed < 5000)
speed = 5000;
if (speed > 44100)
speed = 44100;
+ s = speed * devc->channels;
+
devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
tmp = 256 - devc->tconst;
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index f75f5ffdfdfb..a71d1c14a0f6 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -94,7 +94,7 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
codec_index != CS46XX_SECONDARY_CODEC_INDEX))
- return -EINVAL;
+ return 0xffff;
chip->active_ctrl(chip, 1);
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 8e40262d4117..2f6e9c762d3f 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1725,8 +1725,10 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
atc_connect_resources(atc);
atc->timer = ct_timer_new(atc);
- if (!atc->timer)
+ if (!atc->timer) {
+ err = -ENOMEM;
goto error1;
+ }
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
if (err < 0)
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 4f502a2bdc3c..0a436626182b 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -326,7 +326,10 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
unsigned long ofs = idx << PAGE_SHIFT;
dma_addr_t addr;
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ if (ofs >= runtime->dma_bytes)
+ addr = emu->silent_page.addr;
+ else
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
printk(KERN_ERR "emu: failure page = %d\n", idx);
mutex_unlock(&hdr->block_mutex);
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 647218d69f68..4f7d2dfcef7b 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -332,13 +332,12 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
+ snd_printd(" inputs:\n");
for (i = 0; i < cfg->num_inputs; i++) {
- snd_printd(" %s=0x%x",
+ snd_printd(" %s=0x%x\n",
hda_get_autocfg_input_label(codec, cfg, i),
cfg->inputs[i].pin);
}
- snd_printd("\n");
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 0bc2315b181d..0849aac449f2 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -231,16 +231,22 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return query_amp_caps(codec, get_amp_nid(kcontrol),
+ get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
/* get/put callbacks for beep mute mixer switches */
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
- if (beep) {
+ if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
ucontrol->value.integer.value[0] =
- ucontrol->value.integer.value[1] =
- beep->enabled;
+ ucontrol->value.integer.value[1] = beep->enabled;
return 0;
}
return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
@@ -252,9 +258,20 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
- if (beep)
- snd_hda_enable_beep_device(codec,
- *ucontrol->value.integer.value);
+ if (beep) {
+ u8 chs = get_amp_channels(kcontrol);
+ int enable = 0;
+ long *valp = ucontrol->value.integer.value;
+ if (chs & 1) {
+ enable |= *valp;
+ valp++;
+ }
+ if (chs & 2)
+ enable |= *valp;
+ snd_hda_enable_beep_device(codec, enable);
+ }
+ if (!ctl_has_mute(kcontrol))
+ return 0;
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 88a9c20eb7a2..1c65cc5e3a31 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1209,6 +1209,9 @@ static void snd_hda_codec_free(struct hda_codec *codec)
kfree(codec);
}
+static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec,
+ hda_nid_t fg, unsigned int power_state);
+
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
unsigned int power_state);
@@ -1317,6 +1320,10 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
AC_VERB_GET_SUBSYSTEM_ID, 0);
}
+ codec->epss = snd_hda_codec_get_supported_ps(codec,
+ codec->afg ? codec->afg : codec->mfg,
+ AC_PWRST_EPSS);
+
/* power-up all before initialization */
hda_set_power_state(codec,
codec->afg ? codec->afg : codec->mfg,
@@ -1386,6 +1393,44 @@ int snd_hda_codec_configure(struct hda_codec *codec)
}
EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+ struct hda_cvt_setup *p, hda_nid_t nid,
+ u32 stream_tag, int channel_id)
+{
+ unsigned int oldval, newval;
+
+ if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
+ oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ newval = (stream_tag << 4) | channel_id;
+ if (oldval != newval)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ newval);
+ p->stream_tag = stream_tag;
+ p->channel_id = channel_id;
+ }
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+ hda_nid_t nid, int format)
+{
+ unsigned int oldval;
+
+ if (p->format_id != format) {
+ oldval = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ if (oldval != format) {
+ msleep(1);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ }
+ p->format_id = format;
+ }
+}
+
/**
* snd_hda_codec_setup_stream - set up the codec for streaming
* @codec: the CODEC to set up
@@ -1400,7 +1445,6 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
{
struct hda_codec *c;
struct hda_cvt_setup *p;
- unsigned int oldval, newval;
int type;
int i;
@@ -1413,29 +1457,13 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
p = get_hda_cvt_setup(codec, nid);
if (!p)
return;
- /* update the stream-id if changed */
- if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- p->stream_tag = stream_tag;
- p->channel_id = channel_id;
- }
- /* update the format-id if changed */
- if (p->format_id != format) {
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT, 0);
- if (oldval != format) {
- msleep(1);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
- p->format_id = format;
- }
+
+ if (codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+ update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+ if (!codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+
p->active = 1;
p->dirty = 0;
@@ -2325,6 +2353,7 @@ int snd_hda_codec_reset(struct hda_codec *codec)
}
if (codec->patch_ops.free)
codec->patch_ops.free(codec);
+ memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
snd_hda_jack_tbl_clear(codec);
codec->proc_widget_hook = NULL;
codec->spec = NULL;
@@ -2340,7 +2369,6 @@ int snd_hda_codec_reset(struct hda_codec *codec)
codec->num_pcms = 0;
codec->pcm_info = NULL;
codec->preset = NULL;
- memset(&codec->patch_ops, 0, sizeof(codec->patch_ops));
codec->slave_dig_outs = NULL;
codec->spdif_status_reset = 0;
module_put(codec->owner);
@@ -3497,7 +3525,7 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg
{
int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE);
- if (sup < 0)
+ if (sup == -1)
return false;
if (sup & power_state)
return true;
@@ -3522,8 +3550,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
/* this delay seems necessary to avoid click noise at power-down */
if (power_state == AC_PWRST_D3) {
/* transition time less than 10ms for power down */
- bool epss = snd_hda_codec_get_supported_ps(codec, fg, AC_PWRST_EPSS);
- msleep(epss ? 10 : 100);
+ msleep(codec->epss ? 10 : 100);
}
/* repeat power states setting at most 10 times*/
@@ -4433,6 +4460,8 @@ static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down)
* then there is no need to go through power up here.
*/
if (codec->power_on) {
+ if (codec->power_transition < 0)
+ codec->power_transition = 0;
spin_unlock(&codec->power_lock);
return;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c422d330ca54..e5a7e19a8071 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -861,6 +861,8 @@ struct hda_codec {
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
+ unsigned int pcm_format_first:1; /* PCM format must be set first */
+ unsigned int epss:1; /* supporting EPSS? */
#ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */
int power_transition; /* power-state in transition */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c8aced182fd1..c4763c52eaf6 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -151,6 +151,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, CPT},"
"{Intel, PPT},"
"{Intel, LPT},"
+ "{Intel, LPT_LP},"
"{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
@@ -2700,6 +2701,8 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = {
SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
+ SND_PCI_QUIRK(0x1043, 0x1ac3, "ASUS X53S", POS_FIX_POSBUF),
+ SND_PCI_QUIRK(0x1043, 0x1b43, "ASUS K53E", POS_FIX_POSBUF),
SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
SND_PCI_QUIRK(0x10de, 0xcb89, "Macbook Pro 7,1", POS_FIX_LPIB),
SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
@@ -3270,6 +3273,14 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0c0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 7e46258fc700..6894ec66258c 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -412,7 +412,7 @@ static void print_digital_conv(struct snd_info_buffer *buffer,
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
- snd_iprintf(buffer, " Copyright");
+ snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index d0d3540e39e7..49750a96d649 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -246,7 +246,7 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
}
- if (dac)
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, dac, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
}
@@ -261,7 +261,7 @@ static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
- if (adc)
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP))
snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
@@ -275,6 +275,10 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_MUTE) == 0) {
+ snd_printdd("Skipping '%s %s Switch' (no mute on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
@@ -286,6 +290,10 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) {
+ snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
@@ -464,50 +472,17 @@ exit:
}
/*
- * PCM stuffs
+ * PCM callbacks
*/
-static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
+static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
- unsigned int oldval, newval;
-
- if (!nid)
- return;
-
- snd_printdd("ca0132_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
-
- /* update the format-id if changed */
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT,
- 0);
- if (oldval != format) {
- msleep(20);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
-
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- }
-}
-
-static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
}
-/*
- * PCM callbacks
- */
static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -515,10 +490,8 @@ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
-
- return 0;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -526,92 +499,45 @@ static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dacs[0]);
-
- return 0;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
/*
* Digital out
*/
-static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
-
- return 0;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
}
-static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dig_out);
-
- return 0;
-}
-
-/*
- * Analog capture
- */
-static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
-
- return 0;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
-static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
-
- return 0;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
}
-/*
- * Digital capture
- */
-static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
-
- return 0;
-}
-
-static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dig_in);
-
- return 0;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
/*
@@ -621,6 +547,7 @@ static struct hda_pcm_stream ca0132_pcm_analog_playback = {
.channels_min = 2,
.channels_max = 2,
.ops = {
+ .open = ca0132_playback_pcm_open,
.prepare = ca0132_playback_pcm_prepare,
.cleanup = ca0132_playback_pcm_cleanup
},
@@ -630,10 +557,6 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .ops = {
- .prepare = ca0132_capture_pcm_prepare,
- .cleanup = ca0132_capture_pcm_cleanup
- },
};
static struct hda_pcm_stream ca0132_pcm_digital_playback = {
@@ -641,6 +564,8 @@ static struct hda_pcm_stream ca0132_pcm_digital_playback = {
.channels_min = 2,
.channels_max = 2,
.ops = {
+ .open = ca0132_dig_playback_pcm_open,
+ .close = ca0132_dig_playback_pcm_close,
.prepare = ca0132_dig_playback_pcm_prepare,
.cleanup = ca0132_dig_playback_pcm_cleanup
},
@@ -650,10 +575,6 @@ static struct hda_pcm_stream ca0132_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_capture_pcm_prepare,
- .cleanup = ca0132_dig_capture_pcm_cleanup
- },
};
static int ca0132_build_pcms(struct hda_codec *codec)
@@ -928,18 +849,16 @@ static int ca0132_build_controls(struct hda_codec *codec)
spec->dig_out);
if (err < 0)
return err;
- err = add_out_volume(codec, spec->dig_out, "IEC958");
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
if (err < 0)
return err;
+ /* spec->multiout.share_spdif = 1; */
}
if (spec->dig_in) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
if (err < 0)
return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- if (err < 0)
- return err;
}
return 0;
}
@@ -961,6 +880,9 @@ static void ca0132_config(struct hda_codec *codec)
struct ca0132_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
/* line-outs */
cfg->line_outs = 1;
cfg->line_out_pins[0] = 0x0b; /* front */
@@ -988,14 +910,24 @@ static void ca0132_config(struct hda_codec *codec)
/* Mic-in */
spec->input_pins[0] = 0x12;
- spec->input_labels[0] = "Mic-In";
+ spec->input_labels[0] = "Mic";
spec->adcs[0] = 0x07;
/* Line-In */
spec->input_pins[1] = 0x11;
- spec->input_labels[1] = "Line-In";
+ spec->input_labels[1] = "Line";
spec->adcs[1] = 0x08;
spec->num_inputs = 2;
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
}
static void ca0132_init_chip(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 14361184ae1e..5e22a8f43d2e 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -2967,12 +2967,10 @@ static const char * const cxt5066_models[CXT5066_MODELS] = {
};
static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO),
SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD),
@@ -2988,14 +2986,10 @@ static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO),
- SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
- SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO),
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 641408dc28c0..8f23374fa642 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1165,14 +1165,20 @@ static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
+static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
struct hdmi_spec *spec = codec->spec;
int cvt_idx, pin_idx;
struct hdmi_spec_per_cvt *per_cvt;
struct hdmi_spec_per_pin *per_pin;
int pinctl;
- snd_hda_codec_cleanup_stream(codec, hinfo->nid);
-
if (hinfo->nid) {
cvt_idx = cvt_nid_to_cvt_index(spec, hinfo->nid);
if (snd_BUG_ON(cvt_idx < 0))
@@ -1195,12 +1201,12 @@ static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
pinctl & ~PIN_OUT);
snd_hda_spdif_ctls_unassign(codec, pin_idx);
}
-
return 0;
}
static const struct hda_pcm_ops generic_ops = {
.open = hdmi_pcm_open,
+ .close = hdmi_pcm_close,
.prepare = generic_hdmi_playback_pcm_prepare,
.cleanup = generic_hdmi_playback_pcm_cleanup,
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f141395dfee6..4f81dd44c837 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -203,6 +203,7 @@ struct alc_spec {
unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */
unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */
unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */
+ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
/* auto-mute control */
int automute_mode;
@@ -4323,7 +4324,8 @@ static int alc_parse_auto_config(struct hda_codec *codec,
return 0; /* can't find valid BIOS pin config */
}
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
+ if (!spec->no_primary_hp &&
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
cfg->line_outs <= cfg->hp_outs) {
/* use HP as primary out */
cfg->speaker_outs = cfg->line_outs;
@@ -5050,6 +5052,7 @@ enum {
ALC889_FIXUP_MBP_VREF,
ALC889_FIXUP_IMAC91_VREF,
ALC882_FIXUP_INV_DMIC,
+ ALC882_FIXUP_NO_PRIMARY_HP,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@@ -5171,6 +5174,17 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
spec->keep_vref_in_automute = 1;
}
+/* Don't take HP output as primary
+ * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05
+ */
+static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
+ const struct alc_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ if (action == ALC_FIXUP_ACT_PRE_PROBE)
+ spec->no_primary_hp = 1;
+}
+
static const struct alc_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
.type = ALC_FIXUP_PINS,
@@ -5357,6 +5371,10 @@ static const struct alc_fixup alc882_fixups[] = {
.type = ALC_FIXUP_FUNC,
.v.func = alc_fixup_inv_dmic_0x12,
},
+ [ALC882_FIXUP_NO_PRIMARY_HP] = {
+ .type = ALC_FIXUP_FUNC,
+ .v.func = alc882_fixup_no_primary_hp,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -5391,6 +5409,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC),
SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601),
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
+ SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
/* All Apple entries are in codec SSIDs */
SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
@@ -5432,6 +5451,7 @@ static const struct alc_model_fixup alc882_fixup_models[] = {
{.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"},
{.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"},
{.id = ALC882_FIXUP_INV_DMIC, .name = "inv-dmic"},
+ {.id = ALC882_FIXUP_NO_PRIMARY_HP, .name = "no-primary-hp"},
{}
};
@@ -6079,6 +6099,8 @@ static const struct alc_fixup alc269_fixups[] = {
[ALC269_FIXUP_PCM_44K] = {
.type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
},
[ALC269_FIXUP_STEREO_DMIC] = {
.type = ALC_FIXUP_FUNC,
@@ -6186,9 +6208,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
#if 0
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index a1596a3b171c..3d4722f0a1ca 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -101,6 +101,8 @@ enum {
STAC_92HD83XXX_HP_cNB11_INTQUAD,
STAC_HP_DV7_4000,
STAC_HP_ZEPHYR,
+ STAC_92HD83XXX_HP_LED,
+ STAC_92HD83XXX_HP_INV_LED,
STAC_92HD83XXX_MODELS
};
@@ -1073,7 +1075,7 @@ static struct snd_kcontrol_new stac_smux_mixer = {
static const char * const slave_pfxs[] = {
"Front", "Surround", "Center", "LFE", "Side",
- "Headphone", "Speaker", "IEC958",
+ "Headphone", "Speaker", "IEC958", "PCM",
NULL
};
@@ -1675,6 +1677,8 @@ static const char * const stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
[STAC_92HD83XXX_HP_cNB11_INTQUAD] = "hp_cNB11_intquad",
[STAC_HP_DV7_4000] = "hp-dv7-4000",
[STAC_HP_ZEPHYR] = "hp-zephyr",
+ [STAC_92HD83XXX_HP_LED] = "hp-led",
+ [STAC_92HD83XXX_HP_INV_LED] = "hp-inv-led",
};
static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1729,6 +1733,8 @@ static const struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
"HP", STAC_92HD83XXX_HP_cNB11_INTQUAD),
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3561,
"HP", STAC_HP_ZEPHYR),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3660,
+ "HP Mini", STAC_92HD83XXX_HP_LED),
{} /* terminator */
};
@@ -4266,7 +4272,8 @@ static int stac92xx_init(struct hda_codec *codec)
unsigned int gpio;
int i;
- snd_hda_sequence_write(codec, spec->init);
+ if (spec->init)
+ snd_hda_sequence_write(codec, spec->init);
/* power down adcs initially */
if (spec->powerdown_adcs)
@@ -4414,7 +4421,12 @@ static int stac92xx_init(struct hda_codec *codec)
snd_hda_jack_report_sync(codec);
/* sync mute LED */
- snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ if (spec->gpio_led) {
+ if (spec->vmaster_mute.hook)
+ snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+ else /* the very first init call doesn't have vmaster yet */
+ stac92xx_update_led_status(codec, false);
+ }
/* sync the power-map */
if (spec->num_pwrs)
@@ -4531,6 +4543,9 @@ static void stac92xx_line_out_detect(struct hda_codec *codec,
struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
+ if (cfg->speaker_outs == 0)
+ return;
+
for (i = 0; i < cfg->line_outs; i++) {
if (presence)
break;
@@ -5507,6 +5522,7 @@ static void stac92hd8x_fill_auto_spec(struct hda_codec *codec)
static int patch_stac92hd83xxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
+ int default_polarity = -1; /* no default cfg */
int err;
spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -5518,6 +5534,7 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
snd_hda_codec_set_pincfg(codec, 0xf, 0x2181205e);
}
+ codec->epss = 0; /* longer delay needed for D3 */
codec->no_trigger_sense = 1;
codec->spec = spec;
@@ -5555,9 +5572,15 @@ again:
case STAC_HP_ZEPHYR:
spec->init = stac92hd83xxx_hp_zephyr_init;
break;
+ case STAC_92HD83XXX_HP_LED:
+ default_polarity = 0;
+ break;
+ case STAC_92HD83XXX_HP_INV_LED:
+ default_polarity = 1;
+ break;
}
- if (find_mute_led_cfg(codec, -1/*no default cfg*/))
+ if (find_mute_led_cfg(codec, default_polarity))
snd_printd("mute LED gpio %d polarity %d\n",
spec->gpio_led,
spec->gpio_led_polarity);
@@ -5730,7 +5753,6 @@ again:
/* fallthru */
case 0x111d76b4: /* 6 Port without Analog Mixer */
case 0x111d76b5:
- spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
@@ -5755,7 +5777,6 @@ again:
spec->stream_delay = 40; /* 40 milliseconds */
/* disable VSW */
- spec->init = stac92hd71bxx_core_init;
unmute_init++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
@@ -5770,7 +5791,6 @@ again:
/* fallthru */
default:
- spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
@@ -5778,6 +5798,9 @@ again:
break;
}
+ if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
+ spec->init = stac92hd71bxx_core_init;
+
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 90645560ed39..430771776915 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1752,6 +1752,14 @@ static int via_suspend(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
vt1708_stop_hp_work(spec);
+
+ if (spec->codec_type == VT1802) {
+ /* Fix pop noise on headphones */
+ int i;
+ for (i = 0; i < spec->autocfg.hp_outs; i++)
+ snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0);
+ }
+
return 0;
}
#endif
@@ -3226,7 +3234,7 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int imux_is_smixer;
- unsigned int parm;
+ unsigned int parm, parm2;
/* MUX6 (1eh) = stereo mixer */
imux_is_smixer =
snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
@@ -3249,7 +3257,7 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
parm = AC_PWRST_D3;
set_pin_power_state(codec, 0x27, &parm);
update_power_state(codec, 0x1a, parm);
- update_power_state(codec, 0xb, parm);
+ parm2 = parm; /* for pin 0x0b */
/* PW2 (26h), AOW2 (ah) */
parm = AC_PWRST_D3;
@@ -3264,6 +3272,9 @@ static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
if (!spec->hp_independent_mode) /* check for redirected HP */
set_pin_power_state(codec, 0x28, &parm);
update_power_state(codec, 0x8, parm);
+ if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
+ parm = parm2;
+ update_power_state(codec, 0xb, parm);
/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c
index 764cc93dbca4..075d5aa1fee0 100644
--- a/sound/pci/ice1712/prodigy_hifi.c
+++ b/sound/pci/ice1712/prodigy_hifi.c
@@ -297,6 +297,7 @@ static int ak4396_dac_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem
}
static const DECLARE_TLV_DB_SCALE(db_scale_wm_dac, -12700, 100, 1);
+static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
{
@@ -307,7 +308,7 @@ static struct snd_kcontrol_new prodigy_hd2_controls[] __devinitdata = {
.info = ak4396_dac_vol_info,
.get = ak4396_dac_vol_get,
.put = ak4396_dac_vol_put,
- .tlv = { .p = db_scale_wm_dac },
+ .tlv = { .p = ak4396_db_scale },
},
};
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index d1ab43706735..5579b08bb35b 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -851,6 +851,8 @@ static int __devinit lx_pcm_create(struct lx6464es *chip)
/* hardcoded device name & channel count */
err = snd_pcm_new(chip->card, (char *)card_name, 0,
1, 1, &pcm);
+ if (err < 0)
+ return err;
pcm->private_data = chip;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index b8ac8710f47f..b12308b5ba2a 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6585,7 +6585,7 @@ static int __devinit snd_hdspm_create(struct snd_card *card,
snd_printk(KERN_ERR "HDSPM: "
"unable to kmalloc Mixer memory of %d Bytes\n",
(int)sizeof(struct hdspm_mixer));
- return err;
+ return -ENOMEM;
}
hdspm->port_names_in = NULL;
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 512434efcc31..805ab6e9a78f 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1377,8 +1377,9 @@ static int __devinit sis_chip_create(struct snd_card *card,
if (rc)
goto error_out_cleanup;
- if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
- sis)) {
+ rc = request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
+ sis);
+ if (rc) {
dev_err(&pci->dev, "unable to allocate irq %d\n", sis->irq);
goto error_out_cleanup;
}
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index f5ceb6f282de..210cafe04890 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -143,7 +143,7 @@ static int __devexit snd_pmac_remove(struct platform_device *devptr)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_pmac_driver_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 1aa52eff526a..9b18b5243a56 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -1040,6 +1040,7 @@ static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
GFP_KERNEL);
if (!the_card.null_buffer_start_vaddr) {
pr_info("%s: nullbuffer alloc failed\n", __func__);
+ ret = -ENOMEM;
goto clean_preallocate;
}
pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__,
diff --git a/sound/soc/blackfin/bf6xx-sport.c b/sound/soc/blackfin/bf6xx-sport.c
index 318c5ba5360f..dfb744381c42 100644
--- a/sound/soc/blackfin/bf6xx-sport.c
+++ b/sound/soc/blackfin/bf6xx-sport.c
@@ -413,7 +413,14 @@ EXPORT_SYMBOL(sport_create);
void sport_delete(struct sport_device *sport)
{
+ if (sport->tx_desc)
+ dma_free_coherent(NULL, sport->tx_desc_size,
+ sport->tx_desc, 0);
+ if (sport->rx_desc)
+ dma_free_coherent(NULL, sport->rx_desc_size,
+ sport->rx_desc, 0);
sport_free_resource(sport);
+ kfree(sport);
}
EXPORT_SYMBOL(sport_delete);
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 3c795921c5f6..23b40186f9b8 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2406,6 +2406,10 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
/* Setup AB8500 according to board-settings */
pdata = (struct ab8500_platform_data *)dev_get_platdata(dev->parent);
+
+ /* Inform SoC Core that we have our own I/O arrangements. */
+ codec->control_data = (void *)true;
+
status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);
if (status < 0) {
pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 8c39dddd7d00..11b1b714b8b5 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -186,6 +186,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 5c9cacaf2d52..1cf7a32d1b21 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -426,7 +426,7 @@ static const int arizona_44k1_bclk_rates[] = {
940800,
1411200,
1881600,
- 2882400,
+ 2822400,
3763200,
5644800,
7526400,
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 6276e352125f..115a40301810 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -581,6 +581,8 @@ static int mc13783_probe(struct snd_soc_codec *codec)
{
struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ codec->control_data = priv->mc13xxx;
+
mc13xxx_lock(priv->mc13xxx);
/* these are the reset values */
@@ -657,7 +659,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
.id = MC13783_ID_STEREO_DAC,
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = MC13783_FORMATS,
@@ -668,7 +670,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
.id = MC13783_ID_STEREO_CODEC,
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = MC13783_RATES_RECORD,
.formats = MC13783_FORMATS,
@@ -690,14 +692,14 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
.id = MC13783_ID_SYNC,
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = MC13783_FORMATS,
},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = MC13783_RATES_RECORD,
.formats = MC13783_FORMATS,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 8af6a5245b18..df2f99d1d428 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -239,6 +239,7 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
+ {"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@@ -1357,8 +1358,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
if (ret)
goto err;
- snd_soc_dapm_new_widgets(&codec->dapm);
-
return 0;
err:
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 982e437799a8..33c0f3d39c87 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -340,6 +340,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 3fd5b29dc933..a3acb7a85f6a 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -702,7 +702,7 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_config wm2000_regmap = {
- .reg_bits = 8,
+ .reg_bits = 16,
.val_bits = 8,
.max_register = WM2000_REG_IF_CTL,
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 6537f16d383e..e33d327396ad 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -128,13 +128,9 @@ SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
-ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
-ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
-SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
- ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA),
ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
@@ -236,8 +232,6 @@ ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
@@ -349,10 +343,6 @@ SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
NULL, 0),
SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
NULL, 0),
-SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
- NULL, 0),
-SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
- NULL, 0),
SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
NULL, 0),
@@ -466,8 +456,6 @@ ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
-ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
-ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
@@ -553,8 +541,6 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "EQ4", "EQ4" }, \
{ name, "DRC1L", "DRC1L" }, \
{ name, "DRC1R", "DRC1R" }, \
- { name, "DRC2L", "DRC2L" }, \
- { name, "DRC2R", "DRC2R" }, \
{ name, "LHPF1", "LHPF1" }, \
{ name, "LHPF2", "LHPF2" }, \
{ name, "LHPF3", "LHPF3" }, \
@@ -639,6 +625,15 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -675,8 +670,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
- ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
- ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 8033f7065189..01ebbcc5c6a4 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -681,6 +681,18 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
+ { "IN4L PGA", NULL, "IN4L" },
+ { "IN4R PGA", NULL, "IN4R" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 0013afe48e66..dc4262eea4b7 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -100,7 +100,7 @@ static const struct reg_default wm8904_reg_defaults[] = {
{ 14, 0x0000 }, /* R14 - Power Management 2 */
{ 15, 0x0000 }, /* R15 - Power Management 3 */
{ 18, 0x0000 }, /* R18 - Power Management 6 */
- { 19, 0x945E }, /* R20 - Clock Rates 0 */
+ { 20, 0x945E }, /* R20 - Clock Rates 0 */
{ 21, 0x0C05 }, /* R21 - Clock Rates 1 */
{ 22, 0x0006 }, /* R22 - Clock Rates 2 */
{ 24, 0x0050 }, /* R24 - Audio Interface 0 */
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index eaf65863ec21..ce6720073798 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2501,6 +2501,9 @@ static int wm8962_set_bias_level(struct snd_soc_codec *codec,
/* VMID 2*250k */
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x100);
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ msleep(100);
break;
case SND_SOC_BIAS_OFF:
@@ -3730,21 +3733,6 @@ static int wm8962_runtime_resume(struct device *dev)
regcache_sync(wm8962->regmap);
- regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
- WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
- WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
-
- /* Bias enable at 2*50k for ramp */
- regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
- WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA,
- WM8962_BIAS_ENA | 0x180);
-
- msleep(5);
-
- /* VMID back to 2x250k for standby */
- regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
- WM8962_VMID_SEL_MASK, 0x100);
-
return 0;
}
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index bb62f4b3d563..6c9eeca85b95 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -2649,7 +2649,7 @@ static int wm8994_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- bclk_rate = params_rate(params) * 2;
+ bclk_rate = params_rate(params) * 4;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
bclk_rate *= 16;
@@ -3253,10 +3253,13 @@ static void wm8994_mic_work(struct work_struct *work)
int ret;
int report;
+ pm_runtime_get_sync(dev);
+
ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, &reg);
if (ret < 0) {
dev_err(dev, "Failed to read microphone status: %d\n",
ret);
+ pm_runtime_put(dev);
return;
}
@@ -3299,6 +3302,8 @@ static void wm8994_mic_work(struct work_struct *work)
snd_soc_jack_report(priv->micdet[1].jack, report,
SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ pm_runtime_put(dev);
}
static irqreturn_t wm8994_mic_irq(int irq, void *data)
@@ -3421,12 +3426,15 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
int reg;
bool present;
+ pm_runtime_get_sync(codec->dev);
+
mutex_lock(&wm8994->accdet_lock);
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
if (reg < 0) {
dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
mutex_unlock(&wm8994->accdet_lock);
+ pm_runtime_put(codec->dev);
return IRQ_NONE;
}
@@ -3491,6 +3499,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
SND_JACK_MECHANICAL | SND_JACK_HEADSET |
wm8994->btn_mask);
+ pm_runtime_put(codec->dev);
return IRQ_HANDLED;
}
@@ -3602,6 +3611,8 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
+ pm_runtime_get_sync(codec->dev);
+
/* We may occasionally read a detection without an impedence
* range being provided - if that happens loop again.
*/
@@ -3612,6 +3623,7 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
dev_err(codec->dev,
"Failed to read mic detect status: %d\n",
reg);
+ pm_runtime_put(codec->dev);
return IRQ_NONE;
}
@@ -3639,6 +3651,7 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
dev_warn(codec->dev, "Accessory detection with no callback\n");
out:
+ pm_runtime_put(codec->dev);
return IRQ_HANDLED;
}
@@ -4025,6 +4038,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
break;
case WM8958:
if (wm8994->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_intercon,
+ ARRAY_SIZE(wm8994_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
ARRAY_SIZE(wm8994_revd_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 099e6ec32125..c6d2076a796b 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -148,7 +148,7 @@ SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
-SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
@@ -272,7 +272,7 @@ SOC_DAPM_ENUM("Route", wm9712_enum[9]);
/* Mic select */
static const struct snd_kcontrol_new wm9712_mic_src_controls =
-SOC_DAPM_ENUM("Route", wm9712_enum[7]);
+SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
/* diff select */
static const struct snd_kcontrol_new wm9712_diff_sel_controls =
@@ -291,7 +291,9 @@ SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
&wm9712_capture_selectl_controls),
SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
&wm9712_capture_selectr_controls),
-SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
+SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
&wm9712_mic_src_controls),
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
&wm9712_diff_sel_controls),
@@ -319,6 +321,7 @@ SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
@@ -379,6 +382,18 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
{"Mic PGA", NULL, "MIC1"},
{"Mic PGA", NULL, "MIC2"},
+ /* microphones */
+ {"Differential Mic", NULL, "MIC1"},
+ {"Differential Mic", NULL, "MIC2"},
+ {"Left Mic Select Source", "Mic 1", "MIC1"},
+ {"Left Mic Select Source", "Mic 2", "MIC2"},
+ {"Left Mic Select Source", "Stereo", "MIC1"},
+ {"Left Mic Select Source", "Differential", "Differential Mic"},
+ {"Right Mic Select Source", "Mic 1", "MIC1"},
+ {"Right Mic Select Source", "Mic 2", "MIC2"},
+ {"Right Mic Select Source", "Stereo", "MIC2"},
+ {"Right Mic Select Source", "Differential", "Differential Mic"},
+
/* left capture selector */
{"Left Capture Select", "Mic", "MIC1"},
{"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
@@ -619,6 +634,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 3eb19fb71d17..d0b8a3287a85 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1196,6 +1196,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
if (wm9713 == NULL)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
+ codec->control_data = wm9713; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 95441bfc8190..ce5e5cd254dd 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -380,14 +380,20 @@ static void mcasp_start_tx(struct davinci_audio_dev *dev)
static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt) /* enable FIFO */
+ if (dev->txnumevt) { /* enable FIFO */
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ FIFO_ENABLE);
mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
FIFO_ENABLE);
+ }
mcasp_start_tx(dev);
} else {
- if (dev->rxnumevt) /* enable FIFO */
+ if (dev->rxnumevt) { /* enable FIFO */
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ FIFO_ENABLE);
mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
FIFO_ENABLE);
+ }
mcasp_start_rx(dev);
}
}
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index fb21b17f17f5..199408ec4261 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -94,7 +94,7 @@ static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "audmux internal port setup failed\n");
return ret;
}
- imx_audmux_v2_configure_port(ext_port,
+ ret = imx_audmux_v2_configure_port(ext_port,
IMX_AUDMUX_V2_PTCR_SYN,
IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
if (ret) {
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 28dd76c7cb1c..81d7728cf67f 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -380,13 +380,14 @@ static int imx_ssi_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver imx_ssi_dai = {
.probe = imx_ssi_dai_probe,
.playback = {
- .channels_min = 1,
+ /* The SSI does not support monaural audio. */
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index 99a997f19bb9..b6fa77678d97 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -10,7 +10,7 @@ menuconfig SND_MXS_SOC
if SND_MXS_SOC
config SND_SOC_MXS_SGTL5000
- tristate "SoC Audio support for i.MX boards with sgtl5000"
+ tristate "SoC Audio support for MXS boards with sgtl5000"
depends on I2C
select SND_SOC_SGTL5000
help
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index aba71bfa33b1..b3030718c228 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -394,9 +394,14 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+ struct mxs_saif *master_saif;
u32 scr, stat;
int ret;
+ master_saif = mxs_saif_get_master(saif);
+ if (!master_saif)
+ return -EINVAL;
+
/* mclk should already be set */
if (!saif->mclk && saif->mclk_in_use) {
dev_err(cpu_dai->dev, "set mclk first\n");
@@ -420,6 +425,25 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ /* prepare clk in hw_param, enable in trigger */
+ clk_prepare(saif->clk);
+ if (saif != master_saif) {
+ /*
+ * Set an initial clock rate for the saif internal logic to work
+ * properly. This is important when working in EXTMASTER mode
+ * that uses the other saif's BITCLK&LRCLK but it still needs a
+ * basic clock which should be fast enough for the internal
+ * logic.
+ */
+ clk_enable(saif->clk);
+ ret = clk_set_rate(saif->clk, 24000000);
+ clk_disable(saif->clk);
+ if (ret)
+ return ret;
+
+ clk_prepare(master_saif->clk);
+ }
+
scr = __raw_readl(saif->base + SAIF_CTRL);
scr &= ~BM_SAIF_CTRL_WORD_LENGTH;
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
index 009533ab8d18..df65f98211ec 100644
--- a/sound/soc/omap/am3517evm.c
+++ b/sound/soc/omap/am3517evm.c
@@ -59,7 +59,7 @@ static int am3517evm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
SND_SOC_CLOCK_IN);
if (ret < 0) {
printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 34835e8a9160..d33c48baaf71 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -745,7 +745,7 @@ int omap_mcbsp_6pin_src_mux(struct omap_mcbsp *mcbsp, u8 mux)
{
const char *signal, *src;
- if (mcbsp->pdata->mux_signal)
+ if (!mcbsp->pdata->mux_signal)
return -EINVAL;
switch (mux) {
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 1046083e90a0..acdd3ef14e08 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -820,3 +820,4 @@ module_platform_driver(asoc_mcbsp_driver);
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-mcbsp");
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 5a649da9122a..f0feb06615f8 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -441,3 +441,4 @@ module_platform_driver(omap_pcm_driver);
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP PCM DMA module");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-pcm-audio");
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index f3ebc38c10fe..b70964ea448c 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -34,9 +34,7 @@ static const struct snd_pcm_hardware dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
+ SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
@@ -248,15 +246,11 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->state |= ST_RUNNING;
prtd->params->ops->trigger(prtd->params->ch);
break;
case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING;
prtd->params->ops->stop(prtd->params->ch);
break;
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index b7b2a1f91425..89b064650f14 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -20,7 +20,7 @@
#include <sound/pcm_params.h>
#include <plat/audio.h>
-#include <plat/dma.h>
+#include <mach/dma.h>
#include "dma.h"
#include "pcm.h"
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index f219b2f7ee68..c501af6d8dbe 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -826,7 +826,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
}
if (!rtd->cpu_dai) {
- dev_dbg(card->dev, "CPU DAI %s not registered\n",
+ dev_err(card->dev, "CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
return -EPROBE_DEFER;
}
@@ -857,14 +857,14 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
}
if (!rtd->codec_dai) {
- dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+ dev_err(card->dev, "CODEC DAI %s not registered\n",
dai_link->codec_dai_name);
return -EPROBE_DEFER;
}
}
if (!rtd->codec) {
- dev_dbg(card->dev, "CODEC %s not registered\n",
+ dev_err(card->dev, "CODEC %s not registered\n",
dai_link->codec_name);
return -EPROBE_DEFER;
}
@@ -888,7 +888,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
rtd->platform = platform;
}
if (!rtd->platform) {
- dev_dbg(card->dev, "platform %s not registered\n",
+ dev_err(card->dev, "platform %s not registered\n",
dai_link->platform_name);
return -EPROBE_DEFER;
}
@@ -1096,7 +1096,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
}
/* If the driver didn't set I/O up try regmap */
- if (!codec->control_data)
+ if (!codec->write && dev_get_regmap(codec->dev, NULL))
snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
if (driver->controls)
@@ -1481,6 +1481,8 @@ static int soc_check_aux_dev(struct snd_soc_card *card, int num)
return 0;
}
+ dev_err(card->dev, "%s not registered\n", aux_dev->codec_name);
+
return -EPROBE_DEFER;
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index dd7c49fafd75..f90139b5f50d 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -291,8 +291,11 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
if (dapm->codec->driver->set_bias_level)
ret = dapm->codec->driver->set_bias_level(dapm->codec,
level);
- } else
+ else
+ dapm->bias_level = level;
+ } else if (!card || dapm != &card->dapm) {
dapm->bias_level = level;
+ }
if (ret != 0)
goto out;
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 7f8b3b7428bb..0c172938b82a 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -103,7 +103,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
}
/* Report before the DAPM sync to help users updating micbias status */
- blocking_notifier_call_chain(&jack->notifier, status, jack);
+ blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
snd_soc_dapm_sync(dapm);
diff --git a/sound/soc/spear/spear_pcm.c b/sound/soc/spear/spear_pcm.c
index 97c2cac8e92c..8c7f23729446 100644
--- a/sound/soc/spear/spear_pcm.c
+++ b/sound/soc/spear/spear_pcm.c
@@ -138,7 +138,7 @@ static void spear_pcm_free(struct snd_pcm *pcm)
continue;
buf = &substream->dma_buffer;
- if (!buf && !buf->area)
+ if (!buf || !buf->area)
continue;
dma_free_writecombine(pcm->card->dev, buf->bytes,
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index d684df294c0c..76cb1b363b71 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -89,7 +89,6 @@ static struct snd_soc_jack_gpio tegra_alc5632_hp_jack_gpio = {
.name = "Headset detection",
.report = SND_JACK_HEADSET,
.debounce_time = 150,
- .invert = 1,
};
static const struct snd_soc_dapm_widget tegra_alc5632_dapm_widgets[] = {
@@ -177,7 +176,7 @@ static __devinit int tegra_alc5632_probe(struct platform_device *pdev)
}
alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
- if (alc5632->gpio_hp_det == -ENODEV)
+ if (alc5632->gpio_hp_det == -EPROBE_DEFER)
return -EPROBE_DEFER;
ret = snd_soc_of_parse_card_name(card, "nvidia,model");
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 5658bcec1931..8d6900c1ee47 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -334,11 +334,11 @@ static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slave_config.dst_addr = dmap->addr;
- slave_config.src_maxburst = 0;
+ slave_config.dst_maxburst = 4;
} else {
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
slave_config.src_addr = dmap->addr;
- slave_config.dst_maxburst = 0;
+ slave_config.src_maxburst = 4;
}
slave_config.slave_id = dmap->req_sel;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 0c5bb33d258e..d4f14e492341 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -284,27 +284,27 @@ static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
} else if (np) {
pdata->gpio_spkr_en = of_get_named_gpio(np,
"nvidia,spkr-en-gpios", 0);
- if (pdata->gpio_spkr_en == -ENODEV)
+ if (pdata->gpio_spkr_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_hp_mute = of_get_named_gpio(np,
"nvidia,hp-mute-gpios", 0);
- if (pdata->gpio_hp_mute == -ENODEV)
+ if (pdata->gpio_hp_mute == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_hp_det = of_get_named_gpio(np,
"nvidia,hp-det-gpios", 0);
- if (pdata->gpio_hp_det == -ENODEV)
+ if (pdata->gpio_hp_det == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_int_mic_en = of_get_named_gpio(np,
"nvidia,int-mic-en-gpios", 0);
- if (pdata->gpio_int_mic_en == -ENODEV)
+ if (pdata->gpio_int_mic_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_ext_mic_en = of_get_named_gpio(np,
"nvidia,ext-mic-en-gpios", 0);
- if (pdata->gpio_ext_mic_en == -ENODEV)
+ if (pdata->gpio_ext_mic_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 62ac0285bfaf..057e28ef770e 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -21,7 +21,7 @@
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index ee14d2dac2f5..eb85113d472a 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -19,7 +19,7 @@
#include <linux/slab.h>
#include <mach/hardware.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#include <sound/soc.h>
@@ -663,7 +663,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
struct ux500_msp **msp_p,
struct msp_i2s_platform_data *platform_data)
{
- int ret = 0;
struct resource *res = NULL;
struct i2s_controller *i2s_cont;
struct ux500_msp *msp;
@@ -685,15 +684,14 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
if (res == NULL) {
dev_err(&pdev->dev, "%s: ERROR: Unable to get resource!\n",
__func__);
- ret = -ENOMEM;
- goto err_res;
+ return -ENOMEM;
}
- msp->registers = ioremap(res->start, (res->end - res->start + 1));
+ msp->registers = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (msp->registers == NULL) {
dev_err(&pdev->dev, "%s: ERROR: ioremap failed!\n", __func__);
- ret = -ENOMEM;
- goto err_res;
+ return -ENOMEM;
}
msp->msp_state = MSP_STATE_IDLE;
@@ -705,7 +703,7 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
dev_err(&pdev->dev,
"%s: ERROR: Failed to allocate I2S-controller!\n",
__func__);
- goto err_i2s_cont;
+ return -ENOMEM;
}
i2s_cont->dev.parent = &pdev->dev;
i2s_cont->data = (void *)msp;
@@ -716,14 +714,6 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
msp->i2s_cont = i2s_cont;
return 0;
-
-err_i2s_cont:
- iounmap(msp->registers);
-
-err_res:
- devm_kfree(&pdev->dev, msp);
-
- return ret;
}
void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
@@ -732,11 +722,6 @@ void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
dev_dbg(msp->dev, "%s: Enter (id = %d).\n", __func__, msp->id);
device_unregister(&msp->i2s_cont->dev);
- devm_kfree(&pdev->dev, msp->i2s_cont);
-
- iounmap(msp->registers);
-
- devm_kfree(&pdev->dev, msp);
}
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index 7f71b4a0d4bc..2d9136da9865 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -17,7 +17,7 @@
#include <linux/platform_device.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#define MSP_INPUT_FREQ_APB 48000000
diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c
index 7e96249536b4..37711a5d0d6b 100644
--- a/sound/sound_firmware.c
+++ b/sound/sound_firmware.c
@@ -23,14 +23,14 @@ static int do_mod_firmware_load(const char *fn, char **fp)
if (l <= 0 || l > 131072)
{
printk(KERN_INFO "Invalid firmware '%s'\n", fn);
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
return 0;
}
dp = vmalloc(l);
if (dp == NULL)
{
printk(KERN_INFO "Out of memory loading '%s'.\n", fn);
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
return 0;
}
pos = 0;
@@ -38,10 +38,10 @@ static int do_mod_firmware_load(const char *fn, char **fp)
{
printk(KERN_INFO "Failed to read '%s'.\n", fn);
vfree(dp);
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
return 0;
}
- filp_close(filp, current->files);
+ filp_close(filp, NULL);
*fp = dp;
return (int) l;
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index d5b5c3388e28..4a469f0cb6d4 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -553,7 +553,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
struct snd_usb_audio *chip)
{
struct snd_card *card;
- struct list_head *p;
+ struct list_head *p, *n;
if (chip == (void *)-1L)
return;
@@ -570,7 +570,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
snd_usb_stream_disconnect(p);
}
/* release the endpoint resources */
- list_for_each(p, &chip->ep_list) {
+ list_for_each_safe(p, n, &chip->ep_list) {
snd_usb_endpoint_free(p);
}
/* release the midi resources */
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 379baad3d5ad..5e634a2eb282 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -111,7 +111,8 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
return 0;
/* If a clock source can't tell us whether it's valid, we assume it is */
- if (!uac2_control_is_readable(cs_desc->bmControls, UAC2_CS_CONTROL_CLOCK_VALID))
+ if (!uac2_control_is_readable(cs_desc->bmControls,
+ UAC2_CS_CONTROL_CLOCK_VALID - 1))
return 1;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 0f647d22cb4a..060dccb9ec75 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -141,7 +141,7 @@ int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
*
* For implicit feedback, next_packet_size() is unused.
*/
-static int next_packet_size(struct snd_usb_endpoint *ep)
+int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep)
{
unsigned long flags;
int ret;
@@ -177,15 +177,6 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
ep->retire_data_urb(ep->data_subs, urb);
}
-static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
- struct snd_urb_ctx *ctx)
-{
- int i;
-
- for (i = 0; i < ctx->packets; ++i)
- ctx->packet_size[i] = next_packet_size(ep);
-}
-
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
@@ -206,7 +197,13 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
/* no data provider, so send silence */
unsigned int offs = 0;
for (i = 0; i < ctx->packets; ++i) {
- int counts = ctx->packet_size[i];
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
urb->iso_frame_desc[i].offset = offs * ep->stride;
urb->iso_frame_desc[i].length = counts * ep->stride;
offs += counts;
@@ -370,7 +367,6 @@ static void snd_complete_urb(struct urb *urb)
goto exit_clear;
}
- prepare_outbound_urb_sizes(ep, ctx);
prepare_outbound_urb(ep, ctx);
} else {
retire_inbound_urb(ep, ctx);
@@ -799,7 +795,9 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
- * @ep: the endpoint to start
+ * @ep: the endpoint to start
+ * @can_sleep: flag indicating whether the operation is executed in
+ * non-atomic context
*
* A call to this function will increment the use count of the endpoint.
* In case it is not already running, the URBs for this endpoint will be
@@ -809,7 +807,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
*
* Returns an error if the URB submission failed, 0 in all other cases.
*/
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep)
{
int err;
unsigned int i;
@@ -822,8 +820,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
return 0;
/* just to be sure */
- deactivate_urbs(ep, 0, 1);
- wait_clear_urbs(ep);
+ deactivate_urbs(ep, 0, can_sleep);
+ if (can_sleep)
+ wait_clear_urbs(ep);
ep->active_mask = 0;
ep->unlink_mask = 0;
@@ -854,7 +853,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
goto __error;
if (usb_pipeout(ep->pipe)) {
- prepare_outbound_urb_sizes(ep, urb->context);
prepare_outbound_urb(ep, urb->context);
} else {
prepare_inbound_urb(ep, urb->context);
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index ee2723fb174f..cbbbdf226d66 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -13,7 +13,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
struct audioformat *fmt,
struct snd_usb_endpoint *sync_ep);
-int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
int force, int can_sleep, int wait);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
@@ -21,6 +21,7 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free(struct list_head *head);
int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep);
void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
struct snd_usb_endpoint *sender,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index a1298f379428..f782ce19bf5a 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -212,7 +212,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
}
}
-static int start_endpoints(struct snd_usb_substream *subs)
+static int start_endpoints(struct snd_usb_substream *subs, int can_sleep)
{
int err;
@@ -225,7 +225,7 @@ static int start_endpoints(struct snd_usb_substream *subs)
snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep);
ep->data_subs = subs;
- err = snd_usb_endpoint_start(ep);
+ err = snd_usb_endpoint_start(ep, can_sleep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
return err;
@@ -236,10 +236,25 @@ static int start_endpoints(struct snd_usb_substream *subs)
!test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
struct snd_usb_endpoint *ep = subs->sync_endpoint;
+ if (subs->data_endpoint->iface != subs->sync_endpoint->iface ||
+ subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) {
+ err = usb_set_interface(subs->dev,
+ subs->sync_endpoint->iface,
+ subs->sync_endpoint->alt_idx);
+ if (err < 0) {
+ snd_printk(KERN_ERR
+ "%d:%d:%d: cannot set interface (%d)\n",
+ subs->dev->devnum,
+ subs->sync_endpoint->iface,
+ subs->sync_endpoint->alt_idx, err);
+ return -EIO;
+ }
+ }
+
snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep);
ep->sync_slave = subs->data_endpoint;
- err = snd_usb_endpoint_start(ep);
+ err = snd_usb_endpoint_start(ep, can_sleep);
if (err < 0) {
clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
return err;
@@ -547,7 +562,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
- return start_endpoints(subs);
+ return start_endpoints(subs, 1);
return 0;
}
@@ -1029,6 +1044,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
struct urb *urb)
{
struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+ struct snd_usb_endpoint *ep = subs->data_endpoint;
struct snd_urb_ctx *ctx = urb->context;
unsigned int counts, frames, bytes;
int i, stride, period_elapsed = 0;
@@ -1040,7 +1056,11 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
urb->number_of_packets = 0;
spin_lock_irqsave(&subs->lock, flags);
for (i = 0; i < ctx->packets; i++) {
- counts = ctx->packet_size[i];
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
/* set up descriptor */
urb->iso_frame_desc[i].offset = frames * stride;
urb->iso_frame_desc[i].length = counts * stride;
@@ -1091,7 +1111,16 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
subs->hwptr_done += bytes;
if (subs->hwptr_done >= runtime->buffer_size * stride)
subs->hwptr_done -= runtime->buffer_size * stride;
+
+ /* update delay with exact number of samples queued */
+ runtime->delay = subs->last_delay;
runtime->delay += frames;
+ subs->last_delay = runtime->delay;
+
+ /* realign last_frame_number */
+ subs->last_frame_number = usb_get_current_frame_number(subs->dev);
+ subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
+
spin_unlock_irqrestore(&subs->lock, flags);
urb->transfer_buffer_length = bytes;
if (period_elapsed)
@@ -1109,12 +1138,32 @@ static void retire_playback_urb(struct snd_usb_substream *subs,
struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
int stride = runtime->frame_bits >> 3;
int processed = urb->transfer_buffer_length / stride;
+ int est_delay;
+
+ /* ignore the delay accounting when procssed=0 is given, i.e.
+ * silent payloads are procssed before handling the actual data
+ */
+ if (!processed)
+ return;
spin_lock_irqsave(&subs->lock, flags);
- if (processed > runtime->delay)
- runtime->delay = 0;
+ est_delay = snd_usb_pcm_delay(subs, runtime->rate);
+ /* update delay with exact number of samples played */
+ if (processed > subs->last_delay)
+ subs->last_delay = 0;
else
- runtime->delay -= processed;
+ subs->last_delay -= processed;
+ runtime->delay = subs->last_delay;
+
+ /*
+ * Report when delay estimate is off by more than 2ms.
+ * The error should be lower than 2ms since the estimate relies
+ * on two reads of a counter updated every ms.
+ */
+ if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
+ snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
+ est_delay, subs->last_delay);
+
spin_unlock_irqrestore(&subs->lock, flags);
}
@@ -1172,7 +1221,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
- err = start_endpoints(subs);
+ err = start_endpoints(subs, 0);
if (err < 0)
return err;
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 77f124fe57ad..35655c3a7b7a 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -319,6 +319,8 @@ LIB_H += $(ARCH_INCLUDE)
LIB_H += util/cgroup.h
LIB_H += $(TRACE_EVENT_DIR)event-parse.h
LIB_H += util/target.h
+LIB_H += util/rblist.h
+LIB_H += util/intlist.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -383,6 +385,8 @@ LIB_OBJS += $(OUTPUT)util/xyarray.o
LIB_OBJS += $(OUTPUT)util/cpumap.o
LIB_OBJS += $(OUTPUT)util/cgroup.o
LIB_OBJS += $(OUTPUT)util/target.o
+LIB_OBJS += $(OUTPUT)util/rblist.o
+LIB_OBJS += $(OUTPUT)util/intlist.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
@@ -983,7 +987,8 @@ clean:
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
$(MAKE) -C Documentation/ clean
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
- $(RM) $(OUTPUT)util/*-{bison,flex}*
+ $(RM) $(OUTPUT)util/*-bison*
+ $(RM) $(OUTPUT)util/*-flex*
$(python-clean)
.PHONY: all install clean strip $(LIBTRACEEVENT)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index f5a6452931e6..4db6e1ba54e3 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -313,7 +313,7 @@ try_again:
}
}
- perf_session__update_sample_type(session);
+ perf_session__set_id_hdr_size(session);
}
static int process_buildids(struct perf_record *rec)
@@ -844,8 +844,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
struct perf_record *rec = &record;
char errbuf[BUFSIZ];
- perf_header__set_cmdline(argc, argv);
-
evsel_list = perf_evlist__new(NULL, NULL);
if (evsel_list == NULL)
return -ENOMEM;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 69b1c1185159..7c88a243b5db 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -249,8 +249,9 @@ static int process_read_event(struct perf_tool *tool,
static int perf_report__setup_sample_type(struct perf_report *rep)
{
struct perf_session *self = rep->session;
+ u64 sample_type = perf_evlist__sample_type(self->evlist);
- if (!self->fd_pipe && !(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
+ if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
ui__error("Selected --sort parent, but no "
"callchain data. Did you call "
@@ -274,7 +275,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
if (sort__branch_mode == 1) {
if (!self->fd_pipe &&
- !(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
+ !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
ui__error("Selected -b but no branch data. "
"Did you call perf record without -b?\n");
return -1;
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index d909eb74a0eb..1d592f5cbea9 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -478,7 +478,6 @@ static int test__basic_mmap(void)
unsigned int nr_events[nsyscalls],
expected_nr_events[nsyscalls], i, j;
struct perf_evsel *evsels[nsyscalls], *evsel;
- int sample_size = __perf_evsel__sample_size(attr.sample_type);
for (i = 0; i < nsyscalls; ++i) {
char name[64];
@@ -563,8 +562,7 @@ static int test__basic_mmap(void)
goto out_munmap;
}
- err = perf_event__parse_sample(event, attr.sample_type, sample_size,
- false, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample, false);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;
@@ -661,12 +659,12 @@ static int test__PERF_RECORD(void)
const char *cmd = "sleep";
const char *argv[] = { cmd, "1", NULL, };
char *bname;
- u64 sample_type, prev_time = 0;
+ u64 prev_time = 0;
bool found_cmd_mmap = false,
found_libc_mmap = false,
found_vdso_mmap = false,
found_ld_mmap = false;
- int err = -1, errs = 0, i, wakeups = 0, sample_size;
+ int err = -1, errs = 0, i, wakeups = 0;
u32 cpu;
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
@@ -757,13 +755,6 @@ static int test__PERF_RECORD(void)
}
/*
- * We'll need these two to parse the PERF_SAMPLE_* fields in each
- * event.
- */
- sample_type = perf_evlist__sample_type(evlist);
- sample_size = __perf_evsel__sample_size(sample_type);
-
- /*
* Now that all is properly set up, enable the events, they will
* count just on workload.pid, which will start...
*/
@@ -788,9 +779,7 @@ static int test__PERF_RECORD(void)
if (type < PERF_RECORD_MAX)
nr_events[type]++;
- err = perf_event__parse_sample(event, sample_type,
- sample_size, true,
- &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample, false);
if (err < 0) {
if (verbose)
perf_event__fprintf(event, stderr);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 35e86c6df713..68cd61ef6ac5 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -38,6 +38,7 @@
#include "util/cpumap.h"
#include "util/xyarray.h"
#include "util/sort.h"
+#include "util/intlist.h"
#include "util/debug.h"
@@ -706,8 +707,16 @@ static void perf_event__process_sample(struct perf_tool *tool,
int err;
if (!machine && perf_guest) {
- pr_err("Can't find guest [%d]'s kernel information\n",
- event->ip.pid);
+ static struct intlist *seen;
+
+ if (!seen)
+ seen = intlist__new();
+
+ if (!intlist__has_entry(seen, event->ip.pid)) {
+ pr_err("Can't find guest [%d]'s kernel information\n",
+ event->ip.pid);
+ intlist__add(seen, event->ip.pid);
+ }
return;
}
@@ -811,7 +820,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
int ret;
while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
- ret = perf_session__parse_sample(session, event, &sample);
+ ret = perf_evlist__parse_sample(top->evlist, event, &sample, false);
if (ret) {
pr_err("Can't parse sample, err = %d\n", ret);
continue;
@@ -943,8 +952,10 @@ try_again:
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support:
*/
- if (attr->type == PERF_TYPE_HARDWARE &&
- attr->config == PERF_COUNT_HW_CPU_CYCLES) {
+ if ((err == ENOENT || err == ENXIO) &&
+ (attr->type == PERF_TYPE_HARDWARE) &&
+ (attr->config == PERF_COUNT_HW_CPU_CYCLES)) {
+
if (verbose)
ui__warning("Cycles event not supported,\n"
"trying to fall back to cpu-clock-ticks\n");
@@ -1032,7 +1043,7 @@ static int __cmd_top(struct perf_top *top)
&top->session->host_machine);
perf_top__start_counters(top);
top->session->evlist = top->evlist;
- perf_session__update_sample_type(top->session);
+ perf_session__set_id_hdr_size(top->session);
/* Wait for a minimal set of events before starting the snapshot */
poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 1b197280c621..d84870b06426 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -197,9 +197,6 @@ int perf_event__preprocess_sample(const union perf_event *self,
const char *perf_event__name(unsigned int id);
-int perf_event__parse_sample(const union perf_event *event, u64 type,
- int sample_size, bool sample_id_all,
- struct perf_sample *sample, bool swapped);
int perf_event__synthesize_sample(union perf_event *event, u64 type,
const struct perf_sample *sample,
bool swapped);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 3edfd3483816..9b38681add9e 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -881,3 +881,10 @@ int perf_evlist__start_workload(struct perf_evlist *evlist)
return 0;
}
+
+int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+ struct perf_sample *sample, bool swapped)
+{
+ struct perf_evsel *e = list_entry(evlist->entries.next, struct perf_evsel, node);
+ return perf_evsel__parse_sample(e, event, sample, swapped);
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 40d4d3cdced0..528c1acd9298 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -122,6 +122,9 @@ u64 perf_evlist__sample_type(const struct perf_evlist *evlist);
bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist);
u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist);
+int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+ struct perf_sample *sample, bool swapped);
+
bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist);
bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index e81771364867..2eaae140def2 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -20,7 +20,7 @@
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
-int __perf_evsel__sample_size(u64 sample_type)
+static int __perf_evsel__sample_size(u64 sample_type)
{
u64 mask = sample_type & PERF_SAMPLE_MASK;
int size = 0;
@@ -53,6 +53,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node);
hists__init(&evsel->hists);
+ evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
}
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
@@ -728,10 +729,10 @@ static bool sample_overlap(const union perf_event *event,
return false;
}
-int perf_event__parse_sample(const union perf_event *event, u64 type,
- int sample_size, bool sample_id_all,
+int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
struct perf_sample *data, bool swapped)
{
+ u64 type = evsel->attr.sample_type;
const u64 *array;
/*
@@ -746,14 +747,14 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
data->period = 1;
if (event->header.type != PERF_RECORD_SAMPLE) {
- if (!sample_id_all)
+ if (!evsel->attr.sample_id_all)
return 0;
return perf_event__parse_id_sample(event, type, data, swapped);
}
array = event->sample.array;
- if (sample_size + sizeof(event->header) > event->header.size)
+ if (evsel->sample_size + sizeof(event->header) > event->header.size)
return -EFAULT;
if (type & PERF_SAMPLE_IP) {
@@ -895,7 +896,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
u.val32[1] = sample->tid;
if (swapped) {
/*
- * Inverse of what is done in perf_event__parse_sample
+ * Inverse of what is done in perf_evsel__parse_sample
*/
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
@@ -930,7 +931,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
u.val32[0] = sample->cpu;
if (swapped) {
/*
- * Inverse of what is done in perf_event__parse_sample
+ * Inverse of what is done in perf_evsel__parse_sample
*/
u.val32[0] = bswap_32(u.val32[0]);
u.val64 = bswap_64(u.val64);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 67cc5033d192..b559929983bb 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -65,6 +65,7 @@ struct perf_evsel {
void *func;
void *data;
} handler;
+ unsigned int sample_size;
bool supported;
};
@@ -177,13 +178,8 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
return __perf_evsel__read(evsel, ncpus, nthreads, true);
}
-int __perf_evsel__sample_size(u64 sample_type);
-
-static inline int perf_evsel__sample_size(struct perf_evsel *evsel)
-{
- return __perf_evsel__sample_size(evsel->attr.sample_type);
-}
-
void hists__init(struct hists *hists);
+int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
+ struct perf_sample *sample, bool swapped);
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 3a6d20443330..74ea3c2f8138 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -174,6 +174,15 @@ perf_header__set_cmdline(int argc, const char **argv)
{
int i;
+ /*
+ * If header_argv has already been set, do not override it.
+ * This allows a command to set the cmdline, parse args and
+ * then call another builtin function that implements a
+ * command -- e.g, cmd_kvm calling cmd_record.
+ */
+ if (header_argv)
+ return 0;
+
header_argc = (u32)argc;
/* do not include NULL termination */
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c
new file mode 100644
index 000000000000..fd530dced9cb
--- /dev/null
+++ b/tools/perf/util/intlist.c
@@ -0,0 +1,101 @@
+/*
+ * Based on intlist.c by:
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+
+#include "intlist.h"
+
+static struct rb_node *intlist__node_new(struct rblist *rblist __used,
+ const void *entry)
+{
+ int i = (int)((long)entry);
+ struct rb_node *rc = NULL;
+ struct int_node *node = malloc(sizeof(*node));
+
+ if (node != NULL) {
+ node->i = i;
+ rc = &node->rb_node;
+ }
+
+ return rc;
+}
+
+static void int_node__delete(struct int_node *ilist)
+{
+ free(ilist);
+}
+
+static void intlist__node_delete(struct rblist *rblist __used,
+ struct rb_node *rb_node)
+{
+ struct int_node *node = container_of(rb_node, struct int_node, rb_node);
+
+ int_node__delete(node);
+}
+
+static int intlist__node_cmp(struct rb_node *rb_node, const void *entry)
+{
+ int i = (int)((long)entry);
+ struct int_node *node = container_of(rb_node, struct int_node, rb_node);
+
+ return node->i - i;
+}
+
+int intlist__add(struct intlist *ilist, int i)
+{
+ return rblist__add_node(&ilist->rblist, (void *)((long)i));
+}
+
+void intlist__remove(struct intlist *ilist __used, struct int_node *node)
+{
+ int_node__delete(node);
+}
+
+struct int_node *intlist__find(struct intlist *ilist, int i)
+{
+ struct int_node *node = NULL;
+ struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
+
+ if (rb_node)
+ node = container_of(rb_node, struct int_node, rb_node);
+
+ return node;
+}
+
+struct intlist *intlist__new(void)
+{
+ struct intlist *ilist = malloc(sizeof(*ilist));
+
+ if (ilist != NULL) {
+ rblist__init(&ilist->rblist);
+ ilist->rblist.node_cmp = intlist__node_cmp;
+ ilist->rblist.node_new = intlist__node_new;
+ ilist->rblist.node_delete = intlist__node_delete;
+ }
+
+ return ilist;
+}
+
+void intlist__delete(struct intlist *ilist)
+{
+ if (ilist != NULL)
+ rblist__delete(&ilist->rblist);
+}
+
+struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx)
+{
+ struct int_node *node = NULL;
+ struct rb_node *rb_node;
+
+ rb_node = rblist__entry(&ilist->rblist, idx);
+ if (rb_node)
+ node = container_of(rb_node, struct int_node, rb_node);
+
+ return node;
+}
diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h
new file mode 100644
index 000000000000..6d63ab90db50
--- /dev/null
+++ b/tools/perf/util/intlist.h
@@ -0,0 +1,75 @@
+#ifndef __PERF_INTLIST_H
+#define __PERF_INTLIST_H
+
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+#include "rblist.h"
+
+struct int_node {
+ struct rb_node rb_node;
+ int i;
+};
+
+struct intlist {
+ struct rblist rblist;
+};
+
+struct intlist *intlist__new(void);
+void intlist__delete(struct intlist *ilist);
+
+void intlist__remove(struct intlist *ilist, struct int_node *in);
+int intlist__add(struct intlist *ilist, int i);
+
+struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx);
+struct int_node *intlist__find(struct intlist *ilist, int i);
+
+static inline bool intlist__has_entry(struct intlist *ilist, int i)
+{
+ return intlist__find(ilist, i) != NULL;
+}
+
+static inline bool intlist__empty(const struct intlist *ilist)
+{
+ return rblist__empty(&ilist->rblist);
+}
+
+static inline unsigned int intlist__nr_entries(const struct intlist *ilist)
+{
+ return rblist__nr_entries(&ilist->rblist);
+}
+
+/* For intlist iteration */
+static inline struct int_node *intlist__first(struct intlist *ilist)
+{
+ struct rb_node *rn = rb_first(&ilist->rblist.entries);
+ return rn ? rb_entry(rn, struct int_node, rb_node) : NULL;
+}
+static inline struct int_node *intlist__next(struct int_node *in)
+{
+ struct rb_node *rn;
+ if (!in)
+ return NULL;
+ rn = rb_next(&in->rb_node);
+ return rn ? rb_entry(rn, struct int_node, rb_node) : NULL;
+}
+
+/**
+ * intlist_for_each - iterate over a intlist
+ * @pos: the &struct int_node to use as a loop cursor.
+ * @ilist: the &struct intlist for loop.
+ */
+#define intlist__for_each(pos, ilist) \
+ for (pos = intlist__first(ilist); pos; pos = intlist__next(pos))
+
+/**
+ * intlist_for_each_safe - iterate over a intlist safe against removal of
+ * int_node
+ * @pos: the &struct int_node to use as a loop cursor.
+ * @n: another &struct int_node to use as temporary storage.
+ * @ilist: the &struct intlist for loop.
+ */
+#define intlist__for_each_safe(pos, n, ilist) \
+ for (pos = intlist__first(ilist), n = intlist__next(pos); pos;\
+ pos = n, n = intlist__next(n))
+#endif /* __PERF_INTLIST_H */
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c
index 1b997d2b89ce..127d648cc548 100644
--- a/tools/perf/util/parse-events-test.c
+++ b/tools/perf/util/parse-events-test.c
@@ -13,6 +13,9 @@ do { \
} \
} while (0)
+#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
+
static int test__checkevent_tracepoint(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = list_entry(evlist->entries.next,
@@ -21,8 +24,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
- evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
return 0;
}
@@ -37,8 +39,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong type",
PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU)
- == evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period",
1 == evsel->attr.sample_period);
}
@@ -428,8 +429,7 @@ static int test__checkevent_list(struct perf_evlist *evlist)
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
- evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 99d02aa57dbf..594f8fad5ecd 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -1,6 +1,7 @@
#include "util.h"
#include "parse-options.h"
#include "cache.h"
+#include "header.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
@@ -413,6 +414,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
{
struct parse_opt_ctx_t ctx;
+ perf_header__set_cmdline(argc, argv);
+
parse_options_start(&ctx, argc, argv, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 2884e67ee625..213362850abd 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -10,10 +10,12 @@ util/ctype.c
util/evlist.c
util/evsel.c
util/cpumap.c
+util/hweight.c
util/thread_map.c
util/util.c
util/xyarray.c
util/cgroup.c
util/debugfs.c
+util/rblist.c
util/strlist.c
../../lib/rbtree.c
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index e03b58a48424..0688bfb6d280 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -797,17 +797,13 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
event = perf_evlist__mmap_read(evlist, cpu);
if (event != NULL) {
- struct perf_evsel *first;
PyObject *pyevent = pyrf_event__new(event);
struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
if (pyevent == NULL)
return PyErr_NoMemory();
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
- err = perf_event__parse_sample(event, first->attr.sample_type,
- perf_evsel__sample_size(first),
- sample_id_all, &pevent->sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false);
if (err)
return PyErr_Format(PyExc_OSError,
"perf: can't parse sample, err=%d", err);
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c
new file mode 100644
index 000000000000..0171fb611004
--- /dev/null
+++ b/tools/perf/util/rblist.c
@@ -0,0 +1,107 @@
+/*
+ * Based on strlist.c by:
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "rblist.h"
+
+int rblist__add_node(struct rblist *rblist, const void *new_entry)
+{
+ struct rb_node **p = &rblist->entries.rb_node;
+ struct rb_node *parent = NULL, *new_node;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+
+ rc = rblist->node_cmp(parent, new_entry);
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ new_node = rblist->node_new(rblist, new_entry);
+ if (new_node == NULL)
+ return -ENOMEM;
+
+ rb_link_node(new_node, parent, p);
+ rb_insert_color(new_node, &rblist->entries);
+ ++rblist->nr_entries;
+
+ return 0;
+}
+
+void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)
+{
+ rb_erase(rb_node, &rblist->entries);
+ rblist->node_delete(rblist, rb_node);
+}
+
+struct rb_node *rblist__find(struct rblist *rblist, const void *entry)
+{
+ struct rb_node **p = &rblist->entries.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+
+ rc = rblist->node_cmp(parent, entry);
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return parent;
+ }
+
+ return NULL;
+}
+
+void rblist__init(struct rblist *rblist)
+{
+ if (rblist != NULL) {
+ rblist->entries = RB_ROOT;
+ rblist->nr_entries = 0;
+ }
+
+ return;
+}
+
+void rblist__delete(struct rblist *rblist)
+{
+ if (rblist != NULL) {
+ struct rb_node *pos, *next = rb_first(&rblist->entries);
+
+ while (next) {
+ pos = next;
+ next = rb_next(pos);
+ rb_erase(pos, &rblist->entries);
+ rblist->node_delete(rblist, pos);
+ }
+ free(rblist);
+ }
+}
+
+struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx)
+{
+ struct rb_node *node;
+
+ for (node = rb_first(&rblist->entries); node; node = rb_next(node)) {
+ if (!idx--)
+ return node;
+ }
+
+ return NULL;
+}
diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h
new file mode 100644
index 000000000000..6d0cae5ae83d
--- /dev/null
+++ b/tools/perf/util/rblist.h
@@ -0,0 +1,47 @@
+#ifndef __PERF_RBLIST_H
+#define __PERF_RBLIST_H
+
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+/*
+ * create node structs of the form:
+ * struct my_node {
+ * struct rb_node rb_node;
+ * ... my data ...
+ * };
+ *
+ * create list structs of the form:
+ * struct mylist {
+ * struct rblist rblist;
+ * ... my data ...
+ * };
+ */
+
+struct rblist {
+ struct rb_root entries;
+ unsigned int nr_entries;
+
+ int (*node_cmp)(struct rb_node *rbn, const void *entry);
+ struct rb_node *(*node_new)(struct rblist *rlist, const void *new_entry);
+ void (*node_delete)(struct rblist *rblist, struct rb_node *rb_node);
+};
+
+void rblist__init(struct rblist *rblist);
+void rblist__delete(struct rblist *rblist);
+int rblist__add_node(struct rblist *rblist, const void *new_entry);
+void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);
+struct rb_node *rblist__find(struct rblist *rblist, const void *entry);
+struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx);
+
+static inline bool rblist__empty(const struct rblist *rblist)
+{
+ return rblist->nr_entries == 0;
+}
+
+static inline unsigned int rblist__nr_entries(const struct rblist *rblist)
+{
+ return rblist->nr_entries;
+}
+
+#endif /* __PERF_RBLIST_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 8e4f0755d2aa..2437fb0b463a 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -80,14 +80,12 @@ out_close:
return -1;
}
-void perf_session__update_sample_type(struct perf_session *self)
+void perf_session__set_id_hdr_size(struct perf_session *session)
{
- self->sample_type = perf_evlist__sample_type(self->evlist);
- self->sample_size = __perf_evsel__sample_size(self->sample_type);
- self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
- self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
- self->host_machine.id_hdr_size = self->id_hdr_size;
- machines__set_id_hdr_size(&self->machines, self->id_hdr_size);
+ u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist);
+
+ session->host_machine.id_hdr_size = id_hdr_size;
+ machines__set_id_hdr_size(&session->machines, id_hdr_size);
}
int perf_session__create_kernel_maps(struct perf_session *self)
@@ -147,7 +145,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0)
goto out_delete;
- perf_session__update_sample_type(self);
+ perf_session__set_id_hdr_size(self);
} else if (mode == O_WRONLY) {
/*
* In O_RDONLY mode this will be performed when reading the
@@ -158,7 +156,7 @@ struct perf_session *perf_session__new(const char *filename, int mode,
}
if (tool && tool->ordering_requires_timestamps &&
- tool->ordered_samples && !self->sample_id_all) {
+ tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
tool->ordered_samples = false;
}
@@ -673,7 +671,8 @@ static void flush_sample_queue(struct perf_session *s,
if (iter->timestamp > limit)
break;
- ret = perf_session__parse_sample(s, iter->event, &sample);
+ ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
+ s->header.needs_swap);
if (ret)
pr_err("Can't parse sample, err = %d\n", ret);
else
@@ -865,16 +864,18 @@ static void perf_session__print_tstamp(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample)
{
+ u64 sample_type = perf_evlist__sample_type(session->evlist);
+
if (event->header.type != PERF_RECORD_SAMPLE &&
- !session->sample_id_all) {
+ !perf_evlist__sample_id_all(session->evlist)) {
fputs("-1 -1 ", stdout);
return;
}
- if ((session->sample_type & PERF_SAMPLE_CPU))
+ if ((sample_type & PERF_SAMPLE_CPU))
printf("%u ", sample->cpu);
- if (session->sample_type & PERF_SAMPLE_TIME)
+ if (sample_type & PERF_SAMPLE_TIME)
printf("%" PRIu64 " ", sample->time);
}
@@ -899,6 +900,8 @@ static void dump_event(struct perf_session *session, union perf_event *event,
static void dump_sample(struct perf_session *session, union perf_event *event,
struct perf_sample *sample)
{
+ u64 sample_type;
+
if (!dump_trace)
return;
@@ -906,10 +909,12 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
event->header.misc, sample->pid, sample->tid, sample->ip,
sample->period, sample->addr);
- if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
+ sample_type = perf_evlist__sample_type(session->evlist);
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample);
- if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK)
branch_stack__printf(sample);
}
@@ -1006,7 +1011,7 @@ static int perf_session__preprocess_sample(struct perf_session *session,
union perf_event *event, struct perf_sample *sample)
{
if (event->header.type != PERF_RECORD_SAMPLE ||
- !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
+ !(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN))
return 0;
if (!ip_callchain__valid(sample->callchain, event)) {
@@ -1030,7 +1035,7 @@ static int perf_session__process_user_event(struct perf_session *session, union
case PERF_RECORD_HEADER_ATTR:
err = tool->attr(event, &session->evlist);
if (err == 0)
- perf_session__update_sample_type(session);
+ perf_session__set_id_hdr_size(session);
return err;
case PERF_RECORD_HEADER_EVENT_TYPE:
return tool->event_type(tool, event);
@@ -1065,7 +1070,7 @@ static int perf_session__process_event(struct perf_session *session,
int ret;
if (session->header.needs_swap)
- event_swap(event, session->sample_id_all);
+ event_swap(event, perf_evlist__sample_id_all(session->evlist));
if (event->header.type >= PERF_RECORD_HEADER_MAX)
return -EINVAL;
@@ -1078,7 +1083,8 @@ static int perf_session__process_event(struct perf_session *session,
/*
* For all kernel events we get the sample data
*/
- ret = perf_session__parse_sample(session, event, &sample);
+ ret = perf_evlist__parse_sample(session->evlist, event, &sample,
+ session->header.needs_swap);
if (ret)
return ret;
@@ -1389,9 +1395,9 @@ int perf_session__process_events(struct perf_session *self,
return err;
}
-bool perf_session__has_traces(struct perf_session *self, const char *msg)
+bool perf_session__has_traces(struct perf_session *session, const char *msg)
{
- if (!(self->sample_type & PERF_SAMPLE_RAW)) {
+ if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) {
pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
return false;
}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 7c435bde6eb0..1f7ec87db7d7 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -41,13 +41,9 @@ struct perf_session {
* perf.data file.
*/
struct hists hists;
- u64 sample_type;
- int sample_size;
int fd;
bool fd_pipe;
bool repipe;
- bool sample_id_all;
- u16 id_hdr_size;
int cwdlen;
char *cwd;
struct ordered_samples ordered_samples;
@@ -86,7 +82,7 @@ void perf_event__attr_swap(struct perf_event_attr *attr);
int perf_session__create_kernel_maps(struct perf_session *self);
-void perf_session__update_sample_type(struct perf_session *self);
+void perf_session__set_id_hdr_size(struct perf_session *session);
void perf_session__remove_thread(struct perf_session *self, struct thread *th);
static inline
@@ -130,24 +126,6 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
-static inline int perf_session__parse_sample(struct perf_session *session,
- const union perf_event *event,
- struct perf_sample *sample)
-{
- return perf_event__parse_sample(event, session->sample_type,
- session->sample_size,
- session->sample_id_all, sample,
- session->header.needs_swap);
-}
-
-static inline int perf_session__synthesize_sample(struct perf_session *session,
- union perf_event *event,
- const struct perf_sample *sample)
-{
- return perf_event__synthesize_sample(event, session->sample_type,
- sample, session->header.needs_swap);
-}
-
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type);
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 6783a2043555..95856ff3dda4 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -10,23 +10,28 @@
#include <stdlib.h>
#include <string.h>
-static struct str_node *str_node__new(const char *s, bool dupstr)
+static
+struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
{
- struct str_node *self = malloc(sizeof(*self));
+ const char *s = entry;
+ struct rb_node *rc = NULL;
+ struct strlist *strlist = container_of(rblist, struct strlist, rblist);
+ struct str_node *snode = malloc(sizeof(*snode));
- if (self != NULL) {
- if (dupstr) {
+ if (snode != NULL) {
+ if (strlist->dupstr) {
s = strdup(s);
if (s == NULL)
goto out_delete;
}
- self->s = s;
+ snode->s = s;
+ rc = &snode->rb_node;
}
- return self;
+ return rc;
out_delete:
- free(self);
+ free(snode);
return NULL;
}
@@ -37,36 +42,26 @@ static void str_node__delete(struct str_node *self, bool dupstr)
free(self);
}
-int strlist__add(struct strlist *self, const char *new_entry)
+static
+void strlist__node_delete(struct rblist *rblist, struct rb_node *rb_node)
{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
- struct str_node *sn;
-
- while (*p != NULL) {
- int rc;
-
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, new_entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return -EEXIST;
- }
+ struct strlist *slist = container_of(rblist, struct strlist, rblist);
+ struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
- sn = str_node__new(new_entry, self->dupstr);
- if (sn == NULL)
- return -ENOMEM;
+ str_node__delete(snode, slist->dupstr);
+}
- rb_link_node(&sn->rb_node, parent, p);
- rb_insert_color(&sn->rb_node, &self->entries);
- ++self->nr_entries;
+static int strlist__node_cmp(struct rb_node *rb_node, const void *entry)
+{
+ const char *str = entry;
+ struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
+
+ return strcmp(snode->s, str);
+}
- return 0;
+int strlist__add(struct strlist *self, const char *new_entry)
+{
+ return rblist__add_node(&self->rblist, new_entry);
}
int strlist__load(struct strlist *self, const char *filename)
@@ -96,34 +91,20 @@ out:
return err;
}
-void strlist__remove(struct strlist *self, struct str_node *sn)
+void strlist__remove(struct strlist *slist, struct str_node *snode)
{
- rb_erase(&sn->rb_node, &self->entries);
- str_node__delete(sn, self->dupstr);
+ str_node__delete(snode, slist->dupstr);
}
-struct str_node *strlist__find(struct strlist *self, const char *entry)
+struct str_node *strlist__find(struct strlist *slist, const char *entry)
{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
-
- while (*p != NULL) {
- struct str_node *sn;
- int rc;
-
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return sn;
- }
+ struct str_node *snode = NULL;
+ struct rb_node *rb_node = rblist__find(&slist->rblist, entry);
- return NULL;
+ if (rb_node)
+ snode = container_of(rb_node, struct str_node, rb_node);
+
+ return snode;
}
static int strlist__parse_list_entry(struct strlist *self, const char *s)
@@ -156,9 +137,12 @@ struct strlist *strlist__new(bool dupstr, const char *slist)
struct strlist *self = malloc(sizeof(*self));
if (self != NULL) {
- self->entries = RB_ROOT;
+ rblist__init(&self->rblist);
+ self->rblist.node_cmp = strlist__node_cmp;
+ self->rblist.node_new = strlist__node_new;
+ self->rblist.node_delete = strlist__node_delete;
+
self->dupstr = dupstr;
- self->nr_entries = 0;
if (slist && strlist__parse_list(self, slist) != 0)
goto out_error;
}
@@ -171,30 +155,18 @@ out_error:
void strlist__delete(struct strlist *self)
{
- if (self != NULL) {
- struct str_node *pos;
- struct rb_node *next = rb_first(&self->entries);
-
- while (next) {
- pos = rb_entry(next, struct str_node, rb_node);
- next = rb_next(&pos->rb_node);
- strlist__remove(self, pos);
- }
- self->entries = RB_ROOT;
- free(self);
- }
+ if (self != NULL)
+ rblist__delete(&self->rblist);
}
-struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
+struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx)
{
- struct rb_node *nd;
+ struct str_node *snode = NULL;
+ struct rb_node *rb_node;
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
+ rb_node = rblist__entry(&slist->rblist, idx);
+ if (rb_node)
+ snode = container_of(rb_node, struct str_node, rb_node);
- if (!idx--)
- return pos;
- }
-
- return NULL;
+ return snode;
}
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 3ba839007d2c..dd9f922ec67c 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -4,14 +4,15 @@
#include <linux/rbtree.h>
#include <stdbool.h>
+#include "rblist.h"
+
struct str_node {
struct rb_node rb_node;
const char *s;
};
struct strlist {
- struct rb_root entries;
- unsigned int nr_entries;
+ struct rblist rblist;
bool dupstr;
};
@@ -32,18 +33,18 @@ static inline bool strlist__has_entry(struct strlist *self, const char *entry)
static inline bool strlist__empty(const struct strlist *self)
{
- return self->nr_entries == 0;
+ return rblist__empty(&self->rblist);
}
static inline unsigned int strlist__nr_entries(const struct strlist *self)
{
- return self->nr_entries;
+ return rblist__nr_entries(&self->rblist);
}
/* For strlist iteration */
static inline struct str_node *strlist__first(struct strlist *self)
{
- struct rb_node *rn = rb_first(&self->entries);
+ struct rb_node *rn = rb_first(&self->rblist.entries);
return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
}
static inline struct str_node *strlist__next(struct str_node *sn)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index fdad4eeeb429..8b63b678e127 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -64,7 +64,7 @@ static enum dso_binary_type binary_type_symtab[] = {
DSO_BINARY_TYPE__NOT_FOUND,
};
-#define DSO_BINARY_TYPE__SYMTAB_CNT sizeof(binary_type_symtab)
+#define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab)
static enum dso_binary_type binary_type_data[] = {
DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -72,7 +72,7 @@ static enum dso_binary_type binary_type_data[] = {
DSO_BINARY_TYPE__NOT_FOUND,
};
-#define DSO_BINARY_TYPE__DATA_CNT sizeof(binary_type_data)
+#define DSO_BINARY_TYPE__DATA_CNT ARRAY_SIZE(binary_type_data)
int dso__name_len(const struct dso *dso)
{
@@ -2875,6 +2875,7 @@ int machines__create_guest_kernel_maps(struct rb_root *machines)
int i, items = 0;
char path[PATH_MAX];
pid_t pid;
+ char *endp;
if (symbol_conf.default_guest_vmlinux_name ||
symbol_conf.default_guest_modules ||
@@ -2891,7 +2892,14 @@ int machines__create_guest_kernel_maps(struct rb_root *machines)
/* Filter out . and .. */
continue;
}
- pid = atoi(namelist[i]->d_name);
+ pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10);
+ if ((*endp != '\0') ||
+ (endp == namelist[i]->d_name) ||
+ (errno == ERANGE)) {
+ pr_debug("invalid directory (%s). Skipping.\n",
+ namelist[i]->d_name);
+ continue;
+ }
sprintf(path, "%s/%s/proc/kallsyms",
symbol_conf.guestmount,
namelist[i]->d_name);
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index 3f59c496e64c..051eaa68095e 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -110,7 +110,7 @@ int perf_target__strerror(struct perf_target *target, int errnum,
int idx;
const char *msg;
- BUG_ON(buflen > 0);
+ BUG_ON(buflen == 0);
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
diff --git a/tools/testing/fault-injection/failcmd.sh b/tools/testing/fault-injection/failcmd.sh
index 1776e924b202..78a9ed7fecdb 100644
--- a/tools/testing/fault-injection/failcmd.sh
+++ b/tools/testing/fault-injection/failcmd.sh
@@ -206,7 +206,7 @@ while true; do
esac
done
-[ -z "$@" ] && exit 0
+[ -z "$1" ] && exit 0
echo $oom_kill_allocating_task > /proc/sys/vm/oom_kill_allocating_task
echo $task_filter > $FAULTATTR/task-filter
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 246852397e30..d617f69131d7 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1976,9 +1976,10 @@ static long kvm_vcpu_compat_ioctl(struct file *filp,
if (copy_from_user(&csigset, sigmask_arg->sigset,
sizeof csigset))
goto out;
- }
- sigset_from_compat(&sigset, &csigset);
- r = kvm_vcpu_ioctl_set_sigmask(vcpu, &sigset);
+ sigset_from_compat(&sigset, &csigset);
+ r = kvm_vcpu_ioctl_set_sigmask(vcpu, &sigset);
+ } else
+ r = kvm_vcpu_ioctl_set_sigmask(vcpu, NULL);
break;
}
default: